blob: e36daf9caa407d5a157e1723aeaaae5bc931563c [file] [log] [blame]
Yi Tseng51301292017-07-28 13:02:59 -07001/*
2 * Copyright 2017-present Open Networking Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 */
Yi Tseng51301292017-07-28 13:02:59 -070017package org.onosproject.dhcprelay;
18
Yi Tseng525ff402017-10-23 19:39:39 -070019import com.google.common.collect.HashMultimap;
Yi Tseng919b2df2017-09-07 16:22:51 -070020import com.google.common.collect.Lists;
Yi Tseng525ff402017-10-23 19:39:39 -070021import com.google.common.collect.Multimap;
Charles Chan70fdd492018-03-07 17:36:06 -080022import com.google.common.collect.Multimaps;
Kalhee Kim45fede42017-09-05 19:05:06 +000023import com.google.common.collect.Sets;
24import com.google.common.collect.ImmutableSet;
Taras Lemkin96a0d342018-03-26 14:52:58 +000025import org.apache.felix.scr.annotations.Activate;
Yi Tseng51301292017-07-28 13:02:59 -070026import org.apache.felix.scr.annotations.Component;
Taras Lemkin96a0d342018-03-26 14:52:58 +000027import org.apache.felix.scr.annotations.Deactivate;
28import org.apache.felix.scr.annotations.Modified;
Yi Tseng51301292017-07-28 13:02:59 -070029import org.apache.felix.scr.annotations.Property;
Kalhee Kim45fede42017-09-05 19:05:06 +000030import org.apache.felix.scr.annotations.Reference;
31import org.apache.felix.scr.annotations.ReferenceCardinality;
Yi Tseng51301292017-07-28 13:02:59 -070032import org.apache.felix.scr.annotations.Service;
33import org.onlab.packet.BasePacket;
Kalhee Kim45fede42017-09-05 19:05:06 +000034import org.onlab.packet.DHCP6;
35import org.onlab.packet.IPv6;
36import org.onlab.packet.Ethernet;
37import org.onlab.packet.Ip6Address;
Yi Tseng51301292017-07-28 13:02:59 -070038import org.onlab.packet.IpAddress;
Kalhee Kim45fede42017-09-05 19:05:06 +000039import org.onlab.packet.IpPrefix;
Yi Tseng51301292017-07-28 13:02:59 -070040import org.onlab.packet.MacAddress;
Yi Tseng525ff402017-10-23 19:39:39 -070041import org.onlab.packet.TpPort;
Kalhee Kim45fede42017-09-05 19:05:06 +000042import org.onlab.packet.UDP;
Yi Tseng51301292017-07-28 13:02:59 -070043import org.onlab.packet.VlanId;
Lior Assoulinea21c0ca2018-01-28 16:18:48 -080044import org.onlab.packet.dhcp.Dhcp6ClientDataOption;
45import org.onlab.packet.dhcp.Dhcp6LeaseQueryOption;
Kalhee Kim45fede42017-09-05 19:05:06 +000046import org.onlab.packet.dhcp.Dhcp6RelayOption;
47import org.onlab.packet.dhcp.Dhcp6InterfaceIdOption;
Lior Assoulinea21c0ca2018-01-28 16:18:48 -080048import org.onlab.packet.dhcp.Dhcp6Option;
Kalhee Kim45fede42017-09-05 19:05:06 +000049import org.onlab.packet.dhcp.Dhcp6IaNaOption;
50import org.onlab.packet.dhcp.Dhcp6IaTaOption;
51import org.onlab.packet.dhcp.Dhcp6IaPdOption;
52import org.onlab.packet.dhcp.Dhcp6IaAddressOption;
53import org.onlab.packet.dhcp.Dhcp6IaPrefixOption;
Kalhee Kimea4b6c22017-11-09 14:38:37 +000054import org.onlab.packet.dhcp.Dhcp6ClientIdOption;
55import org.onlab.packet.dhcp.Dhcp6Duid;
Kalhee Kim495c9b22017-11-07 16:32:09 +000056import org.onlab.packet.DHCP6.MsgType;
Kalhee Kim45fede42017-09-05 19:05:06 +000057import org.onlab.util.HexString;
Taras Lemkin96a0d342018-03-26 14:52:58 +000058import org.onlab.util.Tools;
59import org.onosproject.cfg.ComponentConfigService;
Yi Tseng525ff402017-10-23 19:39:39 -070060import org.onosproject.core.ApplicationId;
61import org.onosproject.core.CoreService;
Yi Tseng51301292017-07-28 13:02:59 -070062import org.onosproject.dhcprelay.api.DhcpHandler;
Yi Tseng919b2df2017-09-07 16:22:51 -070063import org.onosproject.dhcprelay.api.DhcpServerInfo;
Yi Tseng525ff402017-10-23 19:39:39 -070064import org.onosproject.dhcprelay.config.IgnoreDhcpConfig;
Kalhee Kim45fede42017-09-05 19:05:06 +000065import org.onosproject.dhcprelay.store.DhcpRelayStore;
Kalhee Kimea4b6c22017-11-09 14:38:37 +000066import org.onosproject.dhcprelay.store.DhcpRecord;
Kalhee Kimba366062017-11-07 16:32:09 +000067import org.onosproject.dhcprelay.store.DhcpFpmPrefixStore;
Kalhee Kimd94ceea2017-11-29 19:03:02 +000068import org.onosproject.dhcprelay.store.DhcpRelayCounters;
69import org.onosproject.dhcprelay.store.DhcpRelayCountersStore;
Yi Tseng525ff402017-10-23 19:39:39 -070070import org.onosproject.net.Device;
71import org.onosproject.net.DeviceId;
Kalhee Kimba366062017-11-07 16:32:09 +000072import org.onosproject.routing.fpm.api.FpmRecord;
Yi Tseng525ff402017-10-23 19:39:39 -070073import org.onosproject.net.behaviour.Pipeliner;
74import org.onosproject.net.device.DeviceService;
75import org.onosproject.net.flow.DefaultTrafficSelector;
76import org.onosproject.net.flow.TrafficSelector;
77import org.onosproject.net.flowobjective.DefaultForwardingObjective;
78import org.onosproject.net.flowobjective.FlowObjectiveService;
79import org.onosproject.net.flowobjective.ForwardingObjective;
80import org.onosproject.net.flowobjective.Objective;
81import org.onosproject.net.flowobjective.ObjectiveContext;
82import org.onosproject.net.flowobjective.ObjectiveError;
Yi Tsengaa417a62017-09-08 17:22:51 -070083import org.onosproject.net.host.HostProvider;
84import org.onosproject.net.host.HostProviderRegistry;
85import org.onosproject.net.host.HostProviderService;
Kalhee Kim45fede42017-09-05 19:05:06 +000086import org.onosproject.net.host.HostService;
87import org.onosproject.net.host.DefaultHostDescription;
88import org.onosproject.net.host.HostDescription;
Kalhee Kim45fede42017-09-05 19:05:06 +000089import org.onosproject.net.host.HostListener;
90import org.onosproject.net.host.HostEvent;
91import org.onosproject.net.intf.Interface;
92import org.onosproject.net.intf.InterfaceService;
Yi Tseng525ff402017-10-23 19:39:39 -070093import org.onosproject.net.packet.PacketPriority;
Yi Tsengaa417a62017-09-08 17:22:51 -070094import org.onosproject.net.provider.ProviderId;
Kalhee Kim45fede42017-09-05 19:05:06 +000095import org.onosproject.routeservice.Route;
96import org.onosproject.routeservice.RouteStore;
Yi Tseng483ac6f2017-08-02 15:03:31 -070097import org.onosproject.dhcprelay.config.DhcpServerConfig;
Yi Tseng51301292017-07-28 13:02:59 -070098import org.onosproject.net.ConnectPoint;
Kalhee Kim45fede42017-09-05 19:05:06 +000099import org.onosproject.net.Host;
100import org.onosproject.net.HostId;
101import org.onosproject.net.HostLocation;
102import org.onosproject.net.packet.DefaultOutboundPacket;
103import org.onosproject.net.packet.OutboundPacket;
Yi Tseng51301292017-07-28 13:02:59 -0700104import org.onosproject.net.packet.PacketContext;
Kalhee Kim45fede42017-09-05 19:05:06 +0000105import org.onosproject.net.packet.PacketService;
Taras Lemkin96a0d342018-03-26 14:52:58 +0000106import org.osgi.service.component.ComponentContext;
Kalhee Kim45fede42017-09-05 19:05:06 +0000107import org.slf4j.Logger;
108import org.slf4j.LoggerFactory;
109import org.onosproject.net.flow.DefaultTrafficTreatment;
110import org.onosproject.net.flow.TrafficTreatment;
Kalhee Kim45fede42017-09-05 19:05:06 +0000111import java.nio.ByteBuffer;
Taras Lemkin96a0d342018-03-26 14:52:58 +0000112import java.util.ArrayList;
Yi Tseng483ac6f2017-08-02 15:03:31 -0700113import java.util.Collection;
Taras Lemkin96a0d342018-03-26 14:52:58 +0000114import java.util.Dictionary;
115import java.util.List;
Yi Tseng51301292017-07-28 13:02:59 -0700116import java.util.Optional;
Kalhee Kim45fede42017-09-05 19:05:06 +0000117import java.util.Set;
Charles Chan909cff82018-03-05 13:14:02 -0800118import java.util.concurrent.CopyOnWriteArrayList;
Jordan Halterman6328db72018-04-10 13:34:50 -0400119import java.util.concurrent.Executor;
Yi Tseng525ff402017-10-23 19:39:39 -0700120import java.util.concurrent.atomic.AtomicInteger;
Kalhee Kim45fede42017-09-05 19:05:06 +0000121import static com.google.common.base.Preconditions.checkNotNull;
122import static com.google.common.base.Preconditions.checkState;
Jordan Halterman6328db72018-04-10 13:34:50 -0400123import static java.util.concurrent.Executors.newSingleThreadExecutor;
124import static org.onlab.util.Tools.groupedThreads;
Yi Tseng525ff402017-10-23 19:39:39 -0700125import static org.onosproject.net.flowobjective.Objective.Operation.ADD;
126import static org.onosproject.net.flowobjective.Objective.Operation.REMOVE;
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000127import java.util.concurrent.Semaphore;
Yi Tseng51301292017-07-28 13:02:59 -0700128
129@Component
130@Service
131@Property(name = "version", value = "6")
Yi Tsengaa417a62017-09-08 17:22:51 -0700132public class Dhcp6HandlerImpl implements DhcpHandler, HostProvider {
Charles Chand988c282017-09-12 17:09:32 -0700133 public static final String DHCP_V6_RELAY_APP = "org.onosproject.Dhcp6HandlerImpl";
Saurav Das7c6dec12017-09-13 14:35:56 -0700134 public static final ProviderId PROVIDER_ID = new ProviderId("dhcp6", DHCP_V6_RELAY_APP);
Yi Tseng525ff402017-10-23 19:39:39 -0700135 private static final int IGNORE_CONTROL_PRIORITY = PacketPriority.CONTROL.priorityValue() + 1000;
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000136 private String gCount = "global";
Taras Lemkin96a0d342018-03-26 14:52:58 +0000137 private static final String LQ_ROUTE_PROPERTY_NAME = "learnRouteFromLeasequery";
Yi Tseng525ff402017-10-23 19:39:39 -0700138 private static final TrafficSelector CLIENT_SERVER_SELECTOR = DefaultTrafficSelector.builder()
139 .matchEthType(Ethernet.TYPE_IPV6)
140 .matchIPProtocol(IPv6.PROTOCOL_UDP)
141 .matchIPv6Src(IpPrefix.IPV6_LINK_LOCAL_PREFIX)
142 .matchIPv6Dst(Ip6Address.ALL_DHCP_RELAY_AGENTS_AND_SERVERS.toIpPrefix())
143 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_V6_CLIENT_PORT))
144 .matchUdpDst(TpPort.tpPort(UDP.DHCP_V6_SERVER_PORT))
145 .build();
146 private static final TrafficSelector SERVER_RELAY_SELECTOR = DefaultTrafficSelector.builder()
147 .matchEthType(Ethernet.TYPE_IPV6)
148 .matchIPProtocol(IPv6.PROTOCOL_UDP)
149 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_V6_SERVER_PORT))
150 .matchUdpDst(TpPort.tpPort(UDP.DHCP_V6_SERVER_PORT))
151 .build();
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800152 // lease query reply is from server to client (no relay in between) - so we need to
153 // catch that scenario also ..
154 private static final TrafficSelector LEASE_QUERY_RESPONSE_SELECTOR = DefaultTrafficSelector.builder()
155 .matchEthType(Ethernet.TYPE_IPV6)
156 .matchIPProtocol(IPv6.PROTOCOL_UDP)
157 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_V6_SERVER_PORT))
158 .matchUdpDst(TpPort.tpPort(UDP.DHCP_V6_CLIENT_PORT))
159 .build();
Yi Tseng525ff402017-10-23 19:39:39 -0700160 static final Set<TrafficSelector> DHCP_SELECTORS = ImmutableSet.of(
161 CLIENT_SERVER_SELECTOR,
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800162 SERVER_RELAY_SELECTOR,
163 LEASE_QUERY_RESPONSE_SELECTOR
Yi Tseng525ff402017-10-23 19:39:39 -0700164 );
Kalhee Kim45fede42017-09-05 19:05:06 +0000165 private static Logger log = LoggerFactory.getLogger(Dhcp6HandlerImpl.class);
Yi Tseng51301292017-07-28 13:02:59 -0700166
Kalhee Kim45fede42017-09-05 19:05:06 +0000167 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
168 protected DhcpRelayStore dhcpRelayStore;
Yi Tseng51301292017-07-28 13:02:59 -0700169
Kalhee Kim45fede42017-09-05 19:05:06 +0000170 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000171 protected DhcpRelayCountersStore dhcpRelayCountersStore;
172
173 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Kalhee Kim45fede42017-09-05 19:05:06 +0000174 protected PacketService packetService;
175
176 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Kalhee Kim45fede42017-09-05 19:05:06 +0000177 protected RouteStore routeStore;
178
179 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
180 protected InterfaceService interfaceService;
181
182 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
183 protected HostService hostService;
184
Yi Tsengaa417a62017-09-08 17:22:51 -0700185 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
186 protected HostProviderRegistry providerRegistry;
Kalhee Kim45fede42017-09-05 19:05:06 +0000187
Yi Tseng525ff402017-10-23 19:39:39 -0700188 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
189 protected CoreService coreService;
190
191 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Kalhee Kimba366062017-11-07 16:32:09 +0000192 protected DhcpFpmPrefixStore dhcpFpmPrefixStore;
193
194 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Yi Tseng525ff402017-10-23 19:39:39 -0700195 protected DeviceService deviceService;
196
197 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
198 protected FlowObjectiveService flowObjectiveService;
199
Taras Lemkin96a0d342018-03-26 14:52:58 +0000200 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
201 protected ComponentConfigService cfgService;
202
203 @Property(name = Dhcp6HandlerImpl.LQ_ROUTE_PROPERTY_NAME, boolValue = false,
204 label = "Enable learning routing information from LQ")
205 private Boolean learnRouteFromLeasequery = Boolean.TRUE;
206
Yi Tsengaa417a62017-09-08 17:22:51 -0700207 protected HostProviderService providerService;
Yi Tseng525ff402017-10-23 19:39:39 -0700208 protected ApplicationId appId;
Charles Chan70fdd492018-03-07 17:36:06 -0800209 protected Multimap<DeviceId, VlanId> ignoredVlans = Multimaps.synchronizedMultimap(HashMultimap.create());
Yi Tseng525ff402017-10-23 19:39:39 -0700210 private InternalHostListener hostListener = new InternalHostListener();
Kalhee Kimba366062017-11-07 16:32:09 +0000211 private Boolean dhcpFpmEnabled = false;
Charles Chan909cff82018-03-05 13:14:02 -0800212 private List<DhcpServerInfo> defaultServerInfoList = new CopyOnWriteArrayList<>();
213 private List<DhcpServerInfo> indirectServerInfoList = new CopyOnWriteArrayList<>();
Jordan Halterman6328db72018-04-10 13:34:50 -0400214
215 private Executor hostEventExecutor = newSingleThreadExecutor(
216 groupedThreads("dhcp6-event-host", "%d", log));
217
Kalhee Kim495c9b22017-11-07 16:32:09 +0000218 private class IpAddressInfo {
219 Ip6Address ip6Address;
220 long prefTime;
221 }
222 private class PdPrefixInfo {
223 IpPrefix pdPrefix;
224 long prefTime;
225 }
226 protected int dhcp6PollInterval = 24 * 3600; // 24 hr period
227
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000228 // max 1 thread
229 static Semaphore recordSemaphore = new Semaphore(1);
Kalhee Kim45fede42017-09-05 19:05:06 +0000230
231 // CLIENT message types
232 public static final Set<Byte> MSG_TYPE_FROM_CLIENT =
233 ImmutableSet.of(DHCP6.MsgType.SOLICIT.value(),
234 DHCP6.MsgType.REQUEST.value(),
235 DHCP6.MsgType.REBIND.value(),
236 DHCP6.MsgType.RENEW.value(),
237 DHCP6.MsgType.RELEASE.value(),
238 DHCP6.MsgType.DECLINE.value(),
239 DHCP6.MsgType.CONFIRM.value(),
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800240 DHCP6.MsgType.RELAY_FORW.value(),
241 DHCP6.MsgType.LEASEQUERY.value());
Kalhee Kim45fede42017-09-05 19:05:06 +0000242 // SERVER message types
243 public static final Set<Byte> MSG_TYPE_FROM_SERVER =
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800244 ImmutableSet.of(DHCP6.MsgType.RELAY_REPL.value(),
245 DHCP6.MsgType.LEASEQUERY_REPLY.value());
Kalhee Kim45fede42017-09-05 19:05:06 +0000246
247 @Activate
Taras Lemkin96a0d342018-03-26 14:52:58 +0000248 protected void activate(ComponentContext context) {
249 cfgService.registerProperties(getClass());
250 modified(context);
Yi Tseng525ff402017-10-23 19:39:39 -0700251 appId = coreService.registerApplication(DHCP_V6_RELAY_APP);
Yi Tsengaa417a62017-09-08 17:22:51 -0700252 providerService = providerRegistry.register(this);
Kalhee Kim45fede42017-09-05 19:05:06 +0000253 hostService.addListener(hostListener);
Yi Tseng51301292017-07-28 13:02:59 -0700254 }
255
Kalhee Kim45fede42017-09-05 19:05:06 +0000256 @Deactivate
257 protected void deactivate() {
Taras Lemkin96a0d342018-03-26 14:52:58 +0000258 cfgService.unregisterProperties(getClass(), false);
Yi Tsengaa417a62017-09-08 17:22:51 -0700259 providerRegistry.unregister(this);
Kalhee Kim45fede42017-09-05 19:05:06 +0000260 hostService.removeListener(hostListener);
Yi Tseng919b2df2017-09-07 16:22:51 -0700261 defaultServerInfoList.forEach(this::stopMonitoringIps);
Charles Chanfc1c22e2018-07-19 09:52:01 -0700262 defaultServerInfoList.forEach(info -> info.getDhcpServerIp6().ifPresent(this::cancelDhcpPacket));
Yi Tseng919b2df2017-09-07 16:22:51 -0700263 defaultServerInfoList.clear();
264 indirectServerInfoList.forEach(this::stopMonitoringIps);
Charles Chanfc1c22e2018-07-19 09:52:01 -0700265 indirectServerInfoList.forEach(info -> info.getDhcpServerIp6().ifPresent(this::cancelDhcpPacket));
Yi Tseng919b2df2017-09-07 16:22:51 -0700266 indirectServerInfoList.clear();
267 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000268
Taras Lemkin96a0d342018-03-26 14:52:58 +0000269 @Modified
270 protected void modified(ComponentContext context) {
271 Dictionary<?, ?> properties = context.getProperties();
272 Boolean flag;
273 flag = Tools.isPropertyEnabled(properties, Dhcp6HandlerImpl.LQ_ROUTE_PROPERTY_NAME);
274 if (flag != null) {
275 learnRouteFromLeasequery = flag;
276 log.info("Learning routes from DHCP leasequery is {}",
277 learnRouteFromLeasequery ? "enabled" : "disabled");
278 }
279 }
280
Yi Tseng919b2df2017-09-07 16:22:51 -0700281 private void stopMonitoringIps(DhcpServerInfo serverInfo) {
282 serverInfo.getDhcpGatewayIp6().ifPresent(gatewayIp -> {
283 hostService.stopMonitoringIp(gatewayIp);
284 });
285 serverInfo.getDhcpServerIp6().ifPresent(serverIp -> {
286 hostService.stopMonitoringIp(serverIp);
287 });
Yi Tseng51301292017-07-28 13:02:59 -0700288 }
289
Yi Tseng51301292017-07-28 13:02:59 -0700290 @Override
Yi Tseng919b2df2017-09-07 16:22:51 -0700291 public List<DhcpServerInfo> getDefaultDhcpServerInfoList() {
292 return defaultServerInfoList;
Yi Tseng51301292017-07-28 13:02:59 -0700293 }
294
295 @Override
Yi Tseng919b2df2017-09-07 16:22:51 -0700296 public List<DhcpServerInfo> getIndirectDhcpServerInfoList() {
297 return indirectServerInfoList;
Yi Tseng51301292017-07-28 13:02:59 -0700298 }
299
300 @Override
Yi Tseng525ff402017-10-23 19:39:39 -0700301 public void updateIgnoreVlanConfig(IgnoreDhcpConfig config) {
302 if (config == null) {
303 ignoredVlans.forEach(((deviceId, vlanId) -> {
304 processIgnoreVlanRule(deviceId, vlanId, REMOVE);
305 }));
306 return;
307 }
308 config.ignoredVlans().forEach((deviceId, vlanId) -> {
309 if (ignoredVlans.get(deviceId).contains(vlanId)) {
310 // don't need to process if it already ignored
311 return;
312 }
313 processIgnoreVlanRule(deviceId, vlanId, ADD);
314 });
Yi Tseng525ff402017-10-23 19:39:39 -0700315 ignoredVlans.forEach((deviceId, vlanId) -> {
316 if (!config.ignoredVlans().get(deviceId).contains(vlanId)) {
317 // not contains in new config, remove it
318 processIgnoreVlanRule(deviceId, vlanId, REMOVE);
319 }
320 });
321 }
322
323 @Override
Saurav Dasb805f1a2017-12-13 16:19:35 -0800324 public void removeIgnoreVlanState(IgnoreDhcpConfig config) {
325 if (config == null) {
326 ignoredVlans.clear();
327 return;
328 }
329 config.ignoredVlans().forEach((deviceId, vlanId) -> {
330 ignoredVlans.remove(deviceId, vlanId);
331 });
332 }
333
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800334 public DhcpRecord getDhcpRelayRecordFor(Ip6Address clientAddress) {
335
Taras Lemkin96a0d342018-03-26 14:52:58 +0000336 Collection<DhcpRecord> records = dhcpRelayStore.getDhcpRecords();
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800337 DhcpRecord dr = null;
338 for (DhcpRecord e:records) {
339 if (e.ip6Address().isPresent()) {
340 if (e.ip6Address().get().equals(clientAddress)) {
341 dr = e;
342 break;
343 }
344 }
345 }
346 return dr;
347 }
348
349 public MacAddress findNextHopMacForIp6FromRelayStore(Ip6Address clientAddress,
350 MacAddress clientMacAddress, VlanId vlanID) {
351
352 DhcpRecord dr = getDhcpRelayRecordFor(clientAddress);
353
354 if (dr != null) {
355 Optional<MacAddress> nextHopTempMac = dr.nextHopTemp();
356 if (nextHopTempMac.isPresent()) {
357 log.info("findNextHopForIp6FromRelayStore " + clientAddress + " got mac " + nextHopTempMac.toString());
358 return nextHopTempMac.get();
359 }
360 } else {
361 log.warn("findNextHopMacForIp6FromRelayStore could NOT find next hop for " + clientAddress);
362 return null;
363 }
364 return null;
365 }
366
367 public Ip6Address findNextHopIp6FromRelayStore(Ip6Address clientAddress) {
368
369 DhcpRecord dr = getDhcpRelayRecordFor(clientAddress);
370 if (dr != null) {
371 Optional<MacAddress> nextHopMac = dr.nextHop();
372 if (nextHopMac.isPresent()) {
373 // find the local ip6 from the host store
374 HostId gwHostId = HostId.hostId(nextHopMac.get(), dr.vlanId());
375 Host gwHost = hostService.getHost(gwHostId);
376 if (gwHost == null) {
377 log.warn("Can't find next hop host ID {}", gwHostId);
378 return null;
379 }
380 Ip6Address nextHopIp = gwHost.ipAddresses()
381 .stream()
382 .filter(IpAddress::isIp6)
383 .filter(IpAddress::isLinkLocal)
384 .map(IpAddress::getIp6Address)
385 .findFirst()
386 .orElse(null);
387
388 log.info("findNextHopIp6FromRelayStore " + clientAddress + " got mac " +
389 nextHopMac.toString() + " ip6 " + nextHopIp);
390 return nextHopIp;
391 }
392 } else {
393 log.warn("findNextHopIp6FromRelayStore could NOT find next hop for " + clientAddress);
394 return null;
395 }
396 return null;
397 }
398
399 private void setPotentialNextHopForIp6InRelayStore(Ip6Address clientAddress,
400 VlanId vlanId, MacAddress nh) {
401 DhcpRecord dr = getDhcpRelayRecordFor(clientAddress);
402 if (dr != null) {
403 dr.nextHopTemp(nh);
404 log.debug("LQ6 potential NH mac " + nh.toString() + " UPDATED in RelayRecord client " + clientAddress);
405 } else {
406 log.warn("LQ6 potential NH mac" + nh.toString() +
407 " NOT FOUND in RelayRecord for client - LQ rejected" + clientAddress);
408 }
409 }
410
411 public void handleLeaseQuery6ReplyMsg(PacketContext context, DHCP6 dhcp6Payload) {
412 ConnectPoint inPort = context.inPacket().receivedFrom();
413 log.info("Got LQV6-REPLY on port {}", inPort);
414 List<Dhcp6Option> lopt = dhcp6Payload.getOptions();
415 log.info("Options list: {}", lopt);
416 // find out if this lease is known is
417 Dhcp6ClientDataOption clientDataOption = dhcp6Payload.getOptions()
418 .stream()
419 .filter(opt -> opt instanceof Dhcp6ClientDataOption)
420 .map(pld -> (Dhcp6ClientDataOption) pld)
421 .findFirst()
422 .orElse(null);
423
424 if (clientDataOption == null) {
425 log.warn("clientDataOption option is not present, " +
426 "lease is UNKNOWN - not adding any new route...");
427 } else {
428 Dhcp6IaAddressOption aiAddressOption = clientDataOption.getOptions()
429 .stream()
430 .filter(opt -> opt instanceof Dhcp6IaAddressOption)
431 .map(pld -> (Dhcp6IaAddressOption) pld)
432 .findFirst()
433 .orElse(null);
434
435 Dhcp6ClientIdOption clientIdOption = clientDataOption.getOptions()
436 .stream()
437 .filter(opt -> opt instanceof Dhcp6ClientIdOption)
438 .map(pld -> (Dhcp6ClientIdOption) pld)
439 .findFirst()
440 .orElse(null);
441
442 if (aiAddressOption == null) {
443 log.warn("clientDataOption from DHCP server does not " +
444 "contains Dhcp6IaAddressOption for the client - giving up...");
445 } else {
446 Ip6Address clientAddress = aiAddressOption.getIp6Address();
447 MacAddress clientMacAddress = MacAddress.valueOf(clientIdOption.getDuid().getLinkLayerAddress());
448 Ethernet packet = context.inPacket().parsed();
449 VlanId vlanId = VlanId.vlanId(packet.getVlanID());
450 MacAddress potentialNextHopMac =
451 findNextHopMacForIp6FromRelayStore(clientAddress, clientMacAddress, vlanId);
452
453 if (potentialNextHopMac == null) {
454 log.warn("Can't find next hop host mac for client {} mac:{}/{}",
455 clientAddress, clientMacAddress, vlanId);
456 return;
457 } else {
458 log.info("Next hop mac for {}/{}/{} is {}", clientAddress,
459 clientMacAddress, vlanId, potentialNextHopMac.toString());
460 }
461 // search the next hop in the hosts store
462 HostId gwHostId = HostId.hostId(potentialNextHopMac, vlanId);
463 Host gwHost = hostService.getHost(gwHostId);
464 if (gwHost == null) {
465 log.warn("Can't find next hop host ID {}", gwHostId);
466 return;
467 }
468 Ip6Address nextHopIp = gwHost.ipAddresses()
469 .stream()
470 .filter(IpAddress::isIp6)
471 .filter(IpAddress::isLinkLocal)
472 .map(IpAddress::getIp6Address)
473 .findFirst()
474 .orElse(null);
475 if (nextHopIp == null) {
476 log.warn("Can't find IP6 address of next hop {}", gwHost);
477 return;
478 }
479 log.info("client " + clientAddress + " is known !");
Kalhee Kim54a97c92018-04-02 21:23:46 +0000480 Route routeForIP6 = new Route(Route.Source.DHCP, clientAddress.toIpPrefix(), nextHopIp);
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800481 log.debug("updating route of Client for indirectly connected.");
482 log.debug("client ip: " + clientAddress + ", next hop ip6: " + nextHopIp);
483 routeStore.updateRoute(routeForIP6);
484 }
485 }
486 }
487
Saurav Dasb805f1a2017-12-13 16:19:35 -0800488 @Override
Kalhee Kim45fede42017-09-05 19:05:06 +0000489 public void processDhcpPacket(PacketContext context, BasePacket payload) {
490 checkNotNull(payload, "DHCP6 payload can't be null");
491 checkState(payload instanceof DHCP6, "Payload is not a DHCP6");
492 DHCP6 dhcp6Payload = (DHCP6) payload;
493 Ethernet receivedPacket = context.inPacket().parsed();
494
495 if (!configured()) {
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800496 log.warn("Missing DHCP6 relay server config. " +
497 "Abort packet processing dhcp6 payload {}", dhcp6Payload);
Kalhee Kim45fede42017-09-05 19:05:06 +0000498 return;
499 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000500 byte msgTypeVal = dhcp6Payload.getMsgType();
501 MsgType msgType = DHCP6.MsgType.getType(msgTypeVal);
502 log.debug("msgType is {}", msgType);
Kalhee Kim45fede42017-09-05 19:05:06 +0000503
Kalhee Kim45fede42017-09-05 19:05:06 +0000504 ConnectPoint inPort = context.inPacket().receivedFrom();
Jonathan Hart8ca2bc02017-11-30 18:23:42 -0800505
Kalhee Kim495c9b22017-11-07 16:32:09 +0000506 if (inPort == null) {
Taras Lemkin96a0d342018-03-26 14:52:58 +0000507 log.warn("incoming ConnectPoint is null");
Kalhee Kim495c9b22017-11-07 16:32:09 +0000508 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000509 Set<Interface> receivingInterfaces = interfaceService.getInterfacesByPort(inPort);
510 //ignore the packets if dhcp client interface is not configured on onos.
511 if (receivingInterfaces.isEmpty()) {
512 log.warn("Virtual interface is not configured on {}", inPort);
513 return;
514 }
515
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800516 if (msgTypeVal == DHCP6.MsgType.LEASEQUERY.value()) {
Taras Lemkin96a0d342018-03-26 14:52:58 +0000517 List<InternalPacket> ethernetClientPackets =
518 learnRouteFromLeasequery ?
519 processLQ6PacketFromClient(context, receivedPacket, receivingInterfaces, dhcp6Payload) :
520 processDhcp6ForwardOnly(context, receivedPacket, receivingInterfaces, dhcp6Payload);
521 for (InternalPacket internalPacket : ethernetClientPackets) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000522 forwardPacket(internalPacket);
Kalhee Kim45fede42017-09-05 19:05:06 +0000523 }
Taras Lemkin96a0d342018-03-26 14:52:58 +0000524 } else if (msgTypeVal == DHCP6.MsgType.LEASEQUERY_REPLY.value() && learnRouteFromLeasequery) {
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800525 IPv6 clientIpv6 = (IPv6) receivedPacket.getPayload();
526 UDP clientUdp = (UDP) clientIpv6.getPayload();
527 DHCP6 clientDhcp6 = (DHCP6) clientUdp.getPayload();
Taras Lemkin96a0d342018-03-26 14:52:58 +0000528 Interface serverInterface = Dhcp6HandlerUtil.directlyConnected(clientDhcp6) ?
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800529 getServerInterface() : getIndirectServerInterface();
Kalhee Kim45fede42017-09-05 19:05:06 +0000530 InternalPacket ethernetPacketReply =
Taras Lemkin96a0d342018-03-26 14:52:58 +0000531 Dhcp6HandlerUtil.processLQ6PacketFromServer(
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800532 defaultServerInfoList, indirectServerInfoList,
533 serverInterface, interfaceService,
534 hostService,
535 context, receivedPacket, receivingInterfaces);
Kalhee Kim45fede42017-09-05 19:05:06 +0000536 if (ethernetPacketReply != null) {
537 forwardPacket(ethernetPacketReply);
538 }
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800539 handleLeaseQuery6ReplyMsg(context, dhcp6Payload);
Taras Lemkin96a0d342018-03-26 14:52:58 +0000540 } else if (MSG_TYPE_FROM_CLIENT.contains(msgTypeVal)) {
541 List<InternalPacket> ethernetClientPacket =
542 processDhcp6PacketFromClient(context, receivedPacket, receivingInterfaces);
543 for (InternalPacket internalPacket : ethernetClientPacket) {
544 forwardPacket(internalPacket);
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800545 }
Taras Lemkin96a0d342018-03-26 14:52:58 +0000546 } else if (MSG_TYPE_FROM_SERVER.contains(msgTypeVal)) {
547 log.debug("calling processDhcp6PacketFromServer with RELAY_REPL {}, {}", receivedPacket, dhcp6Payload);
548 InternalPacket ethernetPacketReply =
549 processDhcp6PacketFromServer(context, receivedPacket, receivingInterfaces);
550 if (ethernetPacketReply != null) {
551 forwardPacket(ethernetPacketReply);
552 }
553 } else {
554 log.warn("Not so fast, packet type {} not supported yet", msgTypeVal);
Kalhee Kim45fede42017-09-05 19:05:06 +0000555 }
556 }
557
Kalhee Kim45fede42017-09-05 19:05:06 +0000558 /**
559 * Checks if this app has been configured.
560 *
561 * @return true if all information we need have been initialized
562 */
563 public boolean configured() {
Yi Tseng919b2df2017-09-07 16:22:51 -0700564 return !defaultServerInfoList.isEmpty();
Kalhee Kim45fede42017-09-05 19:05:06 +0000565 }
566
Yi Tsengaa417a62017-09-08 17:22:51 -0700567 @Override
568 public ProviderId id() {
Charles Chand988c282017-09-12 17:09:32 -0700569 return PROVIDER_ID;
Yi Tsengaa417a62017-09-08 17:22:51 -0700570 }
571
572 @Override
573 public void triggerProbe(Host host) {
574 // Do nothing here
575 }
576
Kalhee Kim45fede42017-09-05 19:05:06 +0000577 //forward the packet to ConnectPoint where the DHCP server is attached.
578 private void forwardPacket(InternalPacket packet) {
579 //send Packetout to dhcp server connectpoint.
Taras Lemkin96a0d342018-03-26 14:52:58 +0000580 if (packet.getDestLocation() != null) {
Kalhee Kim45fede42017-09-05 19:05:06 +0000581 TrafficTreatment t = DefaultTrafficTreatment.builder()
Taras Lemkin96a0d342018-03-26 14:52:58 +0000582 .setOutput(packet.getDestLocation().port()).build();
Kalhee Kim45fede42017-09-05 19:05:06 +0000583 OutboundPacket o = new DefaultOutboundPacket(
Taras Lemkin96a0d342018-03-26 14:52:58 +0000584 packet.getDestLocation().deviceId(), t, ByteBuffer.wrap(packet.getPacket().serialize()));
Kalhee Kim45fede42017-09-05 19:05:06 +0000585 packetService.emit(o);
Taras Lemkin96a0d342018-03-26 14:52:58 +0000586 if (log.isTraceEnabled()) {
587 IPv6 ip6 = (IPv6) packet.getPacket().getPayload();
588 UDP udp = (UDP) ip6.getPayload();
589 DHCP6 dhcp6 = (DHCP6) udp.getPayload();
590 log.trace("Relaying packet to destination {} eth: {} dhcp: {}",
591 packet.getDestLocation(), packet.getPacket(), dhcp6);
592 }
593
594 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000595 }
596
Kalhee Kim45fede42017-09-05 19:05:06 +0000597 /**
598 * extract from dhcp6 packet client ipv6 address of given by dhcp server.
599 *
600 * @param dhcp6 the dhcp6 packet
Kalhee Kim495c9b22017-11-07 16:32:09 +0000601 * @return IpAddressInfo IpAddressInfo given by dhcp server, or null if not exists
Kalhee Kim45fede42017-09-05 19:05:06 +0000602 */
Kalhee Kim495c9b22017-11-07 16:32:09 +0000603 private IpAddressInfo extractIpAddress(DHCP6 dhcp6) {
604 IpAddressInfo ipInfo = new IpAddressInfo();
Kalhee Kim45fede42017-09-05 19:05:06 +0000605
606 log.debug("extractIpAddress enters dhcp6 {}.", dhcp6);
607 // Extract IPv6 address from IA NA ot IA TA option
608 Optional<Dhcp6IaNaOption> iaNaOption = dhcp6.getOptions()
609 .stream()
610 .filter(opt -> opt instanceof Dhcp6IaNaOption)
611 .map(opt -> (Dhcp6IaNaOption) opt)
612 .findFirst();
613 Optional<Dhcp6IaTaOption> iaTaOption = dhcp6.getOptions()
614 .stream()
615 .filter(opt -> opt instanceof Dhcp6IaTaOption)
616 .map(opt -> (Dhcp6IaTaOption) opt)
617 .findFirst();
618 Optional<Dhcp6IaAddressOption> iaAddressOption;
619 if (iaNaOption.isPresent()) {
620 log.debug("Found IPv6 address from iaNaOption {}", iaNaOption);
621
622 iaAddressOption = iaNaOption.get().getOptions().stream()
623 .filter(opt -> opt instanceof Dhcp6IaAddressOption)
624 .map(opt -> (Dhcp6IaAddressOption) opt)
625 .findFirst();
626 } else if (iaTaOption.isPresent()) {
627 log.debug("Found IPv6 address from iaTaOption {}", iaTaOption);
628
629 iaAddressOption = iaTaOption.get().getOptions().stream()
630 .filter(opt -> opt instanceof Dhcp6IaAddressOption)
631 .map(opt -> (Dhcp6IaAddressOption) opt)
632 .findFirst();
633 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000634 log.info("No IPv6 address found from iaTaOption {}", iaTaOption);
Kalhee Kim45fede42017-09-05 19:05:06 +0000635 iaAddressOption = Optional.empty();
636 }
637 if (iaAddressOption.isPresent()) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000638 ipInfo.ip6Address = iaAddressOption.get().getIp6Address();
639 ipInfo.prefTime = iaAddressOption.get().getPreferredLifetime();
Kalhee Kim45fede42017-09-05 19:05:06 +0000640 log.debug("Found IPv6 address from iaAddressOption {}", iaAddressOption);
Kalhee Kim45fede42017-09-05 19:05:06 +0000641 } else {
642 log.debug("Can't find IPv6 address from DHCPv6 {}", dhcp6);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000643 return null;
Kalhee Kim45fede42017-09-05 19:05:06 +0000644 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000645 return ipInfo;
Kalhee Kim45fede42017-09-05 19:05:06 +0000646 }
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800647
Kalhee Kim45fede42017-09-05 19:05:06 +0000648 /**
649 * extract from dhcp6 packet Prefix prefix provided by dhcp server.
650 *
651 * @param dhcp6 the dhcp6 payload
652 * @return IpPrefix Prefix Delegation prefix, or null if not exists.
653 */
Kalhee Kim495c9b22017-11-07 16:32:09 +0000654 private PdPrefixInfo extractPrefix(DHCP6 dhcp6) {
655 log.debug("extractPrefix enters {}", dhcp6);
Kalhee Kim45fede42017-09-05 19:05:06 +0000656
657 // extract prefix
Kalhee Kim495c9b22017-11-07 16:32:09 +0000658 PdPrefixInfo pdPrefixInfo = new PdPrefixInfo();
Kalhee Kim45fede42017-09-05 19:05:06 +0000659
660 Ip6Address prefixAddress = null;
661
662 // Extract IPv6 prefix from IA PD option
663 Optional<Dhcp6IaPdOption> iaPdOption = dhcp6.getOptions()
664 .stream()
665 .filter(opt -> opt instanceof Dhcp6IaPdOption)
666 .map(opt -> (Dhcp6IaPdOption) opt)
667 .findFirst();
668
669 Optional<Dhcp6IaPrefixOption> iaPrefixOption;
670 if (iaPdOption.isPresent()) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000671 log.debug("IA_PD option found {}", iaPdOption);
Kalhee Kim45fede42017-09-05 19:05:06 +0000672
673 iaPrefixOption = iaPdOption.get().getOptions().stream()
674 .filter(opt -> opt instanceof Dhcp6IaPrefixOption)
675 .map(opt -> (Dhcp6IaPrefixOption) opt)
676 .findFirst();
677 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000678 log.debug("IA_PD option NOT found");
Kalhee Kim45fede42017-09-05 19:05:06 +0000679
680 iaPrefixOption = Optional.empty();
681 }
682 if (iaPrefixOption.isPresent()) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000683 log.debug("IAPrefix Option within IA_PD option found {}", iaPrefixOption);
Kalhee Kim45fede42017-09-05 19:05:06 +0000684
685 prefixAddress = iaPrefixOption.get().getIp6Prefix();
686 int prefixLen = (int) iaPrefixOption.get().getPrefixLength();
Kalhee Kim495c9b22017-11-07 16:32:09 +0000687 log.debug("Prefix length is {} bits", prefixLen);
688 pdPrefixInfo.pdPrefix = IpPrefix.valueOf(prefixAddress, prefixLen);
689 pdPrefixInfo.prefTime = iaPrefixOption.get().getPreferredLifetime();
Kalhee Kim45fede42017-09-05 19:05:06 +0000690 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000691 log.debug("Can't find IPv6 prefix from DHCPv6 {}", dhcp6);
692 return null;
Kalhee Kim45fede42017-09-05 19:05:06 +0000693 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000694 return pdPrefixInfo;
Kalhee Kim45fede42017-09-05 19:05:06 +0000695 }
696
697 /**
Kalhee Kim495c9b22017-11-07 16:32:09 +0000698 * remove host or route and update dhcp relay record attributes.
Kalhee Kim45fede42017-09-05 19:05:06 +0000699 *
700 * @param directConnFlag flag to show that packet is from directly connected client
Kalhee Kim495c9b22017-11-07 16:32:09 +0000701 * @param location client side connect point
Kalhee Kim45fede42017-09-05 19:05:06 +0000702 * @param dhcp6Packet the dhcp6 payload
703 * @param clientPacket client's ethernet packet
704 * @param clientIpv6 client's Ipv6 packet
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +0000705 * @param clientInterface client interfaces
Kalhee Kim45fede42017-09-05 19:05:06 +0000706 */
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000707 private void removeHostOrRoute(boolean directConnFlag, ConnectPoint location,
708 DHCP6 dhcp6Packet,
Kalhee Kim45fede42017-09-05 19:05:06 +0000709 Ethernet clientPacket, IPv6 clientIpv6,
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +0000710 Interface clientInterface) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000711 log.debug("removeHostOrRoute enters {}", dhcp6Packet);
Kalhee Kim121ba922017-11-01 17:56:44 +0000712 VlanId vlanId = clientInterface.vlan();
Kalhee Kim495c9b22017-11-07 16:32:09 +0000713 MacAddress srcMac = clientPacket.getSourceMAC(); // could be gw or host
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000714 MacAddress leafClientMac;
715 byte leafMsgType;
Kalhee Kim495c9b22017-11-07 16:32:09 +0000716 log.debug("client mac {} client vlan {}", HexString.toHexString(srcMac.toBytes(), ":"), vlanId);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000717
Charles Chan24a96ff2018-08-13 10:32:03 -0700718 Dhcp6ClientIdOption clientIdOption = Dhcp6HandlerUtil.extractClientId(directConnFlag, dhcp6Packet);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000719 if (clientIdOption != null) {
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000720 if ((clientIdOption.getDuid().getDuidType() == Dhcp6Duid.DuidType.DUID_LLT) ||
721 (clientIdOption.getDuid().getDuidType() == Dhcp6Duid.DuidType.DUID_LL)) {
722 leafClientMac = MacAddress.valueOf(clientIdOption.getDuid().getLinkLayerAddress());
723 } else {
724 log.warn("Link-Layer Address not supported in CLIENTID option. No DhcpRelay Record created.");
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000725 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_LINKLOCAL_FAIL);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000726 return;
727 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000728 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000729 log.warn("CLIENTID option NOT found. Don't create DhcpRelay Record.");
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000730 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_CLIENTID_FAIL);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000731 return;
732 }
733
Kalhee Kim495c9b22017-11-07 16:32:09 +0000734 HostId leafHostId = HostId.hostId(leafClientMac, vlanId);
735 DhcpRecord record = dhcpRelayStore.getDhcpRecord(leafHostId).orElse(null);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000736 if (record == null) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000737 record = new DhcpRecord(leafHostId);
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000738 } else {
739 record = record.clone();
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000740 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000741
Taras Lemkin96a0d342018-03-26 14:52:58 +0000742 Boolean isMsgRelease = Dhcp6HandlerUtil.isDhcp6Release(dhcp6Packet);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000743 IpAddressInfo ipInfo;
744 PdPrefixInfo pdInfo = null;
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000745 if (directConnFlag) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000746 // Add to host store if it is connected to network directly
747 ipInfo = extractIpAddress(dhcp6Packet);
748 if (ipInfo != null) {
749 if (isMsgRelease) {
750 HostId hostId = HostId.hostId(srcMac, vlanId);
751 log.debug("remove Host {} ip for directly connected.", hostId.toString());
752 providerService.removeIpFromHost(hostId, ipInfo.ip6Address);
Kalhee Kim45fede42017-09-05 19:05:06 +0000753 }
754 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000755 log.debug("ipAddress not found. Do not remove Host {} for directly connected.",
756 HostId.hostId(srcMac, vlanId).toString());
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000757 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000758 leafMsgType = dhcp6Packet.getMsgType();
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000759 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000760 // Remove from route store if it is not connected to network directly
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000761 // pick out the first link-local ip address
Kalhee Kim495c9b22017-11-07 16:32:09 +0000762 IpAddress nextHopIp = getFirstIpByHost(directConnFlag, srcMac, vlanId);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000763 if (nextHopIp == null) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000764 log.warn("Can't find link-local IP address of gateway mac {} vlanId {}", srcMac, vlanId);
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000765 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_LINKLOCAL_GW);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000766 return;
767 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000768
Taras Lemkin96a0d342018-03-26 14:52:58 +0000769 DHCP6 leafDhcp = Dhcp6HandlerUtil.getDhcp6Leaf(dhcp6Packet);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000770 ipInfo = extractIpAddress(leafDhcp);
771 if (ipInfo == null) {
772 log.debug("ip is null");
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000773 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000774 if (isMsgRelease) {
Kalhee Kim54a97c92018-04-02 21:23:46 +0000775 Route routeForIP = new Route(Route.Source.DHCP, ipInfo.ip6Address.toIpPrefix(), nextHopIp);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000776 log.debug("removing route of 128 address for indirectly connected.");
777 log.debug("128 ip {}, nexthop {}",
778 HexString.toHexString(ipInfo.ip6Address.toOctets(), ":"),
779 HexString.toHexString(nextHopIp.toOctets(), ":"));
780 routeStore.removeRoute(routeForIP);
Kalhee Kim45fede42017-09-05 19:05:06 +0000781 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000782 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000783
Kalhee Kim495c9b22017-11-07 16:32:09 +0000784 pdInfo = extractPrefix(leafDhcp);
785 if (pdInfo == null) {
786 log.debug("ipPrefix is null ");
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000787 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000788 if (isMsgRelease) {
Kalhee Kim54a97c92018-04-02 21:23:46 +0000789 Route routeForPrefix = new Route(Route.Source.DHCP, pdInfo.pdPrefix, nextHopIp);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000790 log.debug("removing route of PD for indirectly connected.");
791 log.debug("pd ip {}, nexthop {}",
792 HexString.toHexString(pdInfo.pdPrefix.address().toOctets(), ":"),
793 HexString.toHexString(nextHopIp.toOctets(), ":"));
794
795 routeStore.removeRoute(routeForPrefix);
Kalhee Kimba366062017-11-07 16:32:09 +0000796 if (this.dhcpFpmEnabled) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000797 dhcpFpmPrefixStore.removeFpmRecord(pdInfo.pdPrefix);
Kalhee Kimba366062017-11-07 16:32:09 +0000798 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000799 }
800 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000801 leafMsgType = leafDhcp.getMsgType();
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000802 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000803
Kalhee Kim495c9b22017-11-07 16:32:09 +0000804 if (isMsgRelease) {
805 log.debug("DHCP6 RELEASE msg.");
806 if (record != null) {
807 if (ipInfo != null) {
808 log.debug("DhcpRelay Record ip6Address is set to null.");
809 record.ip6Address(null);
810 }
811 if (pdInfo != null) {
812 log.debug("DhcpRelay Record pdPrefix is set to null.");
813 }
814
815 if (!record.ip6Address().isPresent() && !record.pdPrefix().isPresent()) {
816 log.warn("IP6 address and IP6 PD both are null. Remove record.");
817 // do not remove a record. Let timer task handler it.
818 //dhcpRelayStore.removeDhcpRecord(HostId.hostId(leafClientMac, vlanId));
819 }
820 }
821 }
822
Ray Milkeyffe1a332018-01-24 10:41:14 -0800823 if (record != null) {
Taras Lemkin96a0d342018-03-26 14:52:58 +0000824 record.getV6Counters().incrementCounter(Dhcp6HandlerUtil.getMsgTypeStr(leafMsgType));
Ray Milkeyffe1a332018-01-24 10:41:14 -0800825 record.addLocation(new HostLocation(location, System.currentTimeMillis()));
826 record.ip6Status(DHCP6.MsgType.getType(leafMsgType));
827 record.setDirectlyConnected(directConnFlag);
828 if (!directConnFlag) {
829 // Update gateway mac address if the host is not directly connected
830 record.nextHop(srcMac);
831 }
832 record.updateLastSeen();
Kalhee Kim495c9b22017-11-07 16:32:09 +0000833 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000834 dhcpRelayStore.updateDhcpRecord(leafHostId, record);
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000835 // TODO Use AtomicInteger for the counters
836 try {
837 recordSemaphore.acquire();
838 try {
Taras Lemkin96a0d342018-03-26 14:52:58 +0000839 dhcpRelayCountersStore.incrementCounter(gCount, Dhcp6HandlerUtil.getMsgTypeStr(leafMsgType));
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000840 } finally {
841 // calling release() after a successful acquire()
842 recordSemaphore.release();
843 }
844 } catch (InterruptedException e) {
Ray Milkeyba547f92018-02-01 15:22:31 -0800845 Thread.currentThread().interrupt();
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000846 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000847 }
848
849 /**
850 * add host or route and update dhcp relay record.
851 *
852 * @param directConnFlag flag to show that packet is from directly connected client
853 * @param location client side connect point
854 * @param dhcp6Relay the dhcp6 payload
855 * @param embeddedDhcp6 the dhcp6 payload within relay
856 * @param srcMac client gw/host macAddress
857 * @param clientInterface client interface
858 */
859 private void addHostOrRoute(boolean directConnFlag, ConnectPoint location, DHCP6 dhcp6Relay,
860 DHCP6 embeddedDhcp6, MacAddress srcMac, Interface clientInterface) {
861 log.debug("addHostOrRoute entered.");
862 VlanId vlanId = clientInterface.vlan();
Taras Lemkin96a0d342018-03-26 14:52:58 +0000863 Boolean isMsgReply = Dhcp6HandlerUtil.isDhcp6Reply(dhcp6Relay);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000864 MacAddress leafClientMac;
865 Byte leafMsgType;
866
Charles Chan24a96ff2018-08-13 10:32:03 -0700867 Dhcp6ClientIdOption clientIdOption = Dhcp6HandlerUtil.extractClientId(directConnFlag, embeddedDhcp6);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000868 if (clientIdOption != null) {
869 log.debug("CLIENTID option found {}", clientIdOption);
870 if ((clientIdOption.getDuid().getDuidType() == Dhcp6Duid.DuidType.DUID_LLT) ||
871 (clientIdOption.getDuid().getDuidType() == Dhcp6Duid.DuidType.DUID_LL)) {
872 leafClientMac = MacAddress.valueOf(clientIdOption.getDuid().getLinkLayerAddress());
873 } else {
874 log.warn("Link-Layer Address not supported in CLIENTID option. No DhcpRelay Record created.");
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000875 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_LINKLOCAL_FAIL);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000876 return;
877 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000878 } else {
879 log.warn("CLIENTID option NOT found. No DhcpRelay Record created.");
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000880 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_CLIENTID_FAIL);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000881 return;
882 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000883 HostId leafHostId = HostId.hostId(leafClientMac, vlanId);
884 DhcpRecord record = dhcpRelayStore.getDhcpRecord(leafHostId).orElse(null);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000885 if (record == null) {
886 record = new DhcpRecord(HostId.hostId(leafClientMac, vlanId));
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000887 } else {
888 record = record.clone();
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000889 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000890
891 IpAddressInfo ipInfo;
892 PdPrefixInfo pdInfo = null;
893 if (directConnFlag) {
894 // Add to host store if it connect to network directly
895 ipInfo = extractIpAddress(embeddedDhcp6);
896 if (ipInfo != null) {
897 if (isMsgReply) {
898 Set<IpAddress> ips = Sets.newHashSet(ipInfo.ip6Address);
899 HostId hostId = HostId.hostId(srcMac, vlanId);
900 Host host = hostService.getHost(hostId);
901 HostLocation hostLocation = new HostLocation(clientInterface.connectPoint(),
902 System.currentTimeMillis());
903 Set<HostLocation> hostLocations = Sets.newHashSet(hostLocation);
904 if (host != null) {
905 // Dual homing support:
906 // if host exists, use old locations and new location
907 hostLocations.addAll(host.locations());
908 }
909 HostDescription desc = new DefaultHostDescription(srcMac, vlanId, hostLocations, ips,
910 false);
911 log.debug("adding Host for directly connected.");
912 log.debug("client mac {} client vlan {} hostlocation {}",
913 HexString.toHexString(srcMac.toBytes(), ":"), vlanId, hostLocation.toString());
914 // Replace the ip when dhcp server give the host new ip address
915 providerService.hostDetected(hostId, desc, false);
916 }
917 } else {
918 log.warn("ipAddress not found. Do not add Host {} for directly connected.",
919 HostId.hostId(srcMac, vlanId).toString());
920 }
921 leafMsgType = embeddedDhcp6.getMsgType();
922 } else {
923 // Add to route store if it does not connect to network directly
924 // pick out the first link-local ip address
925 IpAddress nextHopIp = getFirstIpByHost(directConnFlag, srcMac, vlanId);
926 if (nextHopIp == null) {
927 log.warn("Can't find link-local IP address of gateway mac {} vlanId {}", srcMac, vlanId);
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000928 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_LINKLOCAL_GW);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000929 return;
930 }
931
Taras Lemkin96a0d342018-03-26 14:52:58 +0000932 DHCP6 leafDhcp = Dhcp6HandlerUtil.getDhcp6Leaf(embeddedDhcp6);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000933 ipInfo = extractIpAddress(leafDhcp);
934 if (ipInfo == null) {
935 log.debug("ip is null");
936 } else {
937 if (isMsgReply) {
Kalhee Kim54a97c92018-04-02 21:23:46 +0000938 Route routeForIP = new Route(Route.Source.DHCP, ipInfo.ip6Address.toIpPrefix(), nextHopIp);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000939 log.debug("adding Route of 128 address for indirectly connected.");
Daniel Ginsburg83b76452018-06-09 01:43:59 +0300940 routeStore.replaceRoute(routeForIP);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000941 }
942 }
943
944 pdInfo = extractPrefix(leafDhcp);
945 if (pdInfo == null) {
946 log.debug("ipPrefix is null ");
947 } else {
948 if (isMsgReply) {
Kalhee Kim54a97c92018-04-02 21:23:46 +0000949 Route routeForPrefix = new Route(Route.Source.DHCP, pdInfo.pdPrefix, nextHopIp);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000950 log.debug("adding Route of PD for indirectly connected.");
Daniel Ginsburg83b76452018-06-09 01:43:59 +0300951 routeStore.replaceRoute(routeForPrefix);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000952 if (this.dhcpFpmEnabled) {
953 FpmRecord fpmRecord = new FpmRecord(pdInfo.pdPrefix, nextHopIp, FpmRecord.Type.DHCP_RELAY);
954 dhcpFpmPrefixStore.addFpmRecord(pdInfo.pdPrefix, fpmRecord);
955 }
956 }
957 }
958 leafMsgType = leafDhcp.getMsgType();
959 }
960 if (leafMsgType == DHCP6.MsgType.RELEASE.value() ||
961 (leafMsgType == DHCP6.MsgType.REPLY.value()) && ipInfo == null) {
962 log.warn("DHCP6 RELEASE/REPLY(null ip) from Server. MsgType {}", leafMsgType);
963 //return;
964 }
965
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000966 record.addLocation(new HostLocation(location, System.currentTimeMillis()));
Kalhee Kim2f07c602017-11-15 18:57:53 +0000967
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000968 if (leafMsgType == DHCP6.MsgType.REPLY.value()) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000969 if (ipInfo != null) {
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000970 log.debug("IP6 address is being stored into dhcp-relay store.");
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800971 log.debug("Client IP6 address {}", HexString.toHexString(ipInfo.ip6Address.toOctets(), ":"));
Kalhee Kim495c9b22017-11-07 16:32:09 +0000972 record.ip6Address(ipInfo.ip6Address);
Kalhee Kim2f07c602017-11-15 18:57:53 +0000973 record.updateAddrPrefTime(ipInfo.prefTime);
974 record.updateLastIp6Update();
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800975 } else {
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000976 log.debug("IP6 address is not returned from server. Maybe only PD is returned.");
977 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000978 if (pdInfo != null) {
979 log.debug("IP6 PD address {}",
980 HexString.toHexString(pdInfo.pdPrefix.address().toOctets(), ":"));
Kalhee Kim2f07c602017-11-15 18:57:53 +0000981 record.pdPrefix(pdInfo.pdPrefix);
982 record.updatePdPrefTime(pdInfo.prefTime);
983 record.updateLastPdUpdate();
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000984 } else {
985 log.debug("IP6 PD address is not returned from server. Maybe only IPAddress is returned.");
986 }
987 }
Kalhee Kim2f07c602017-11-15 18:57:53 +0000988
Taras Lemkin96a0d342018-03-26 14:52:58 +0000989 record.getV6Counters().incrementCounter(Dhcp6HandlerUtil.getMsgTypeStr(leafMsgType));
Kalhee Kim495c9b22017-11-07 16:32:09 +0000990 record.ip6Status(DHCP6.MsgType.getType(leafMsgType));
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000991 record.setDirectlyConnected(directConnFlag);
992 record.updateLastSeen();
Kalhee Kim495c9b22017-11-07 16:32:09 +0000993 dhcpRelayStore.updateDhcpRecord(leafHostId, record);
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000994 // TODO Use AtomicInteger for the counters
995 try {
996 recordSemaphore.acquire();
997 try {
Taras Lemkin96a0d342018-03-26 14:52:58 +0000998 dhcpRelayCountersStore.incrementCounter(gCount, Dhcp6HandlerUtil.getMsgTypeStr(leafMsgType));
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000999 } finally {
1000 // calling release() after a successful acquire()
1001 recordSemaphore.release();
1002 }
1003 } catch (InterruptedException e) {
Ray Milkeyba547f92018-02-01 15:22:31 -08001004 Thread.currentThread().interrupt();
Kalhee Kimd94ceea2017-11-29 19:03:02 +00001005 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001006 }
1007
Taras Lemkin96a0d342018-03-26 14:52:58 +00001008 private List<InternalPacket> processDhcp6ForwardOnly(PacketContext context,
1009 Ethernet clientPacket,
1010 Set<Interface> clientInterfaces,
1011 DHCP6 dhcpPacket) {
1012 ConnectPoint inPort = context.inPacket().receivedFrom();
1013 log.trace("Got DHCPv6 on port {}", inPort);
1014 boolean directConnFlag = Dhcp6HandlerUtil.directlyConnected(dhcpPacket);
1015
1016 List<InternalPacket> internalPackets = new ArrayList<>();
1017 List<DhcpServerInfo> serverInfoList = findValidServerInfo(directConnFlag);
1018
1019 for (DhcpServerInfo dhcpServer : serverInfoList) {
Charles Chan24a96ff2018-08-13 10:32:03 -07001020 Interface serverInterface = getServerInterface(dhcpServer);
1021 if (serverInterface == null) {
1022 log.warn("Can't get server interface, ignore");
1023 continue;
1024 }
1025
Taras Lemkin96a0d342018-03-26 14:52:58 +00001026 Ethernet newPacket = Dhcp6HandlerUtil.buildDhcp6PacketFromClient(context,
Charles Chan24a96ff2018-08-13 10:32:03 -07001027 clientPacket, clientInterfaces, dhcpServer, serverInterface);
Taras Lemkin96a0d342018-03-26 14:52:58 +00001028 log.trace("Built packet for server {} : {}", dhcpServer, newPacket);
1029 internalPackets.add(InternalPacket.internalPacket(newPacket,
1030 dhcpServer.getDhcpServerConnectPoint().get()));
1031 }
1032
1033 return internalPackets;
1034 }
1035
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001036 private List<InternalPacket> processLQ6PacketFromClient(PacketContext context,
1037 Ethernet clientPacket,
1038 Set<Interface> clientInterfaces,
1039 DHCP6 dhcp6Payload) {
1040 ConnectPoint inPort = context.inPacket().receivedFrom();
1041 log.info("Got LQ-REQUEST V6 on port {}", inPort);
1042 List<Dhcp6Option> lopt = dhcp6Payload.getOptions();
1043 log.info("Options list: {}", lopt);
1044 Dhcp6LeaseQueryOption lqoption = dhcp6Payload.getOptions()
1045 .stream()
1046 .filter(opt -> opt instanceof Dhcp6LeaseQueryOption)
1047 .map(pld -> (Dhcp6LeaseQueryOption) pld)
1048 .findFirst()
1049 .orElse(null);
1050
1051 if (lqoption == null) {
1052 // Can't find dhcp payload
1053 log.warn("Can't find dhcp6 lease query message - aborting");
1054 return null;
1055 } else {
1056 log.info("dhcp6 lqv6 options found: {}", lqoption);
1057 }
1058 log.warn("LQv6 for " + lqoption.linkAddress.toString() + " comes from " + inPort.toString());
1059 Ethernet packet = context.inPacket().parsed();
1060 Ip6Address clientAddress = lqoption.linkAddress;
1061 IPv6 ipv6Packet = (IPv6) packet.getPayload();
1062 Ip6Address nextHopIp = findNextHopIp6FromRelayStore(clientAddress);
1063
1064 // 1. only if there is a route to remove - remove it
1065 if (nextHopIp != null) {
Kalhee Kim54a97c92018-04-02 21:23:46 +00001066 Route routeForIP6 = new Route(Route.Source.DHCP, clientAddress.toIpPrefix(), nextHopIp);
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001067 log.debug("Removing route of Client " + clientAddress +
1068 " for indirectly connected - next hop ip6 " + nextHopIp);
1069 routeStore.removeRoute(routeForIP6);
1070 }
1071
1072 // 2. note the potential NH this packet came from in case it's a known lease
1073 // this NH will then be used to build the route
1074 MacAddress potentialNH = packet.getSourceMAC();
1075 VlanId vlanId = VlanId.vlanId(packet.getVlanID());
1076 setPotentialNextHopForIp6InRelayStore(clientAddress, vlanId, potentialNH);
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001077 // 3. route this LQ6 to all relevant servers
1078 IPv6 clientIpv6 = (IPv6) clientPacket.getPayload();
1079 UDP clientUdp = (UDP) clientIpv6.getPayload();
1080 DHCP6 clientDhcp6 = (DHCP6) clientUdp.getPayload();
1081
Taras Lemkin96a0d342018-03-26 14:52:58 +00001082 boolean directConnFlag = Dhcp6HandlerUtil.directlyConnected(clientDhcp6);
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001083 List<InternalPacket> internalPackets = new ArrayList<>();
1084 List<DhcpServerInfo> serverInfoList = findValidServerInfo(directConnFlag);
1085 List<DhcpServerInfo> copyServerInfoList = new ArrayList<DhcpServerInfo>(serverInfoList);
1086
1087 for (DhcpServerInfo serverInfo : copyServerInfoList) {
Taras Lemkin96a0d342018-03-26 14:52:58 +00001088 if (!Dhcp6HandlerUtil.checkDhcpServerConnPt(directConnFlag, serverInfo)) {
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001089 log.warn("Can't get server connect point, ignore");
1090 continue;
1091 }
1092 DhcpServerInfo newServerInfo = getHostInfoForServerInfo(serverInfo, serverInfoList);
1093 if (newServerInfo == null) {
1094 log.warn("Can't get server interface with host info resolved, ignore");
1095 continue;
1096 }
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001097 Interface serverInterface = getServerInterface(newServerInfo);
1098 if (serverInterface == null) {
1099 log.warn("Can't get server interface, ignore");
1100 continue;
1101 }
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001102 Ethernet etherRouted = (Ethernet) clientPacket.clone();
1103 MacAddress macFacingServer = serverInterface.mac();
1104 if (macFacingServer == null) {
1105 log.warn("No MAC address for server Interface {}", serverInterface);
1106 return null;
1107 }
1108 etherRouted.setSourceMACAddress(macFacingServer);
1109 etherRouted.setDestinationMACAddress(newServerInfo.getDhcpConnectMac().get());
1110 InternalPacket internalPacket =
Taras Lemkin96a0d342018-03-26 14:52:58 +00001111 InternalPacket.internalPacket(etherRouted,
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001112 serverInfo.getDhcpServerConnectPoint().get());
1113 internalPackets.add(internalPacket);
1114 log.debug("Sending LQ to DHCP server {}", newServerInfo.getDhcpServerIp6());
1115 }
1116 log.debug("num of client packets to send is{}", internalPackets.size());
1117
1118 return internalPackets;
1119 }
1120
Kalhee Kim45fede42017-09-05 19:05:06 +00001121 /**
Kalhee Kim495c9b22017-11-07 16:32:09 +00001122 * build the DHCP6 solicit/request packet with gatewayip.
Kalhee Kim45fede42017-09-05 19:05:06 +00001123 *
1124 * @param context packet context
1125 * @param clientPacket client ethernet packet
1126 * @param clientInterfaces set of client side interfaces
1127 */
Kalhee Kim495c9b22017-11-07 16:32:09 +00001128 private List<InternalPacket> processDhcp6PacketFromClient(PacketContext context,
1129 Ethernet clientPacket,
1130 Set<Interface> clientInterfaces) {
Yi Tseng3bd57ac2017-11-29 14:39:18 -08001131 ConnectPoint receivedFrom = context.inPacket().receivedFrom();
Taras Lemkin96a0d342018-03-26 14:52:58 +00001132 Ip6Address relayAgentIp = Dhcp6HandlerUtil.getRelayAgentIPv6Address(clientInterfaces);
Yi Tseng3bd57ac2017-11-29 14:39:18 -08001133 MacAddress relayAgentMac = clientInterfaces.iterator().next().mac();
1134 if (relayAgentIp == null || relayAgentMac == null) {
1135 log.warn("Missing DHCP relay agent interface Ipv6 addr config for "
Kalhee Kim495c9b22017-11-07 16:32:09 +00001136 + "packet from client on port: {}. Aborting packet processing",
1137 clientInterfaces.iterator().next().connectPoint());
Kalhee Kimd94ceea2017-11-29 19:03:02 +00001138 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_CLIENT_INTF_MAC);
Charles Chan239492c2018-01-22 13:27:28 -08001139 return Lists.newArrayList();
Kalhee Kim121ba922017-11-01 17:56:44 +00001140 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001141
Kalhee Kim495c9b22017-11-07 16:32:09 +00001142 IPv6 clientIpv6 = (IPv6) clientPacket.getPayload();
1143 UDP clientUdp = (UDP) clientIpv6.getPayload();
1144 DHCP6 clientDhcp6 = (DHCP6) clientUdp.getPayload();
1145
Taras Lemkin96a0d342018-03-26 14:52:58 +00001146 boolean directConnFlag = Dhcp6HandlerUtil.directlyConnected(clientDhcp6);
Charles Chana990ce92017-10-30 10:22:50 -07001147
Kalhee Kim121ba922017-11-01 17:56:44 +00001148 ConnectPoint clientConnectionPoint = context.inPacket().receivedFrom();
1149 VlanId vlanIdInUse = VlanId.vlanId(clientPacket.getVlanID());
1150 Interface clientInterface = interfaceService.getInterfacesByPort(clientConnectionPoint)
Taras Lemkin96a0d342018-03-26 14:52:58 +00001151 .stream().filter(iface -> Dhcp6HandlerUtil.interfaceContainsVlan(iface, vlanIdInUse))
Kalhee Kim495c9b22017-11-07 16:32:09 +00001152 .findFirst()
1153 .orElse(null);
Kalhee Kim45fede42017-09-05 19:05:06 +00001154
Kalhee Kim495c9b22017-11-07 16:32:09 +00001155 List<InternalPacket> internalPackets = new ArrayList<>();
1156 List<DhcpServerInfo> serverInfoList = findValidServerInfo(directConnFlag);
1157 List<DhcpServerInfo> copyServerInfoList = new ArrayList<DhcpServerInfo>(serverInfoList);
Kalhee Kim45b24182017-10-18 18:30:23 +00001158
Kalhee Kim495c9b22017-11-07 16:32:09 +00001159 for (DhcpServerInfo serverInfo : copyServerInfoList) {
Taras Lemkin96a0d342018-03-26 14:52:58 +00001160 if (!Dhcp6HandlerUtil.checkDhcpServerConnPt(directConnFlag, serverInfo)) {
Kalhee Kim495c9b22017-11-07 16:32:09 +00001161 log.warn("Can't get server connect point, ignore");
1162 continue;
1163 }
1164 DhcpServerInfo newServerInfo = getHostInfoForServerInfo(serverInfo, serverInfoList);
1165 if (newServerInfo == null) {
1166 log.warn("Can't get server interface with host info resolved, ignore");
1167 continue;
1168 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001169
Kalhee Kim495c9b22017-11-07 16:32:09 +00001170 Interface serverInterface = getServerInterface(newServerInfo);
1171 if (serverInterface == null) {
1172 log.warn("Can't get server interface, ignore");
1173 continue;
1174 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +00001175
Taras Lemkin96a0d342018-03-26 14:52:58 +00001176 Ethernet etherReply = Dhcp6HandlerUtil.buildDhcp6PacketFromClient(context, clientPacket,
Kalhee Kim495c9b22017-11-07 16:32:09 +00001177 clientInterfaces, newServerInfo, serverInterface);
1178 removeHostOrRoute(directConnFlag, clientConnectionPoint, clientDhcp6, clientPacket,
1179 clientIpv6, clientInterface);
Kalhee Kimea4b6c22017-11-09 14:38:37 +00001180
Taras Lemkin96a0d342018-03-26 14:52:58 +00001181 InternalPacket internalPacket = InternalPacket.internalPacket(etherReply,
Kalhee Kim495c9b22017-11-07 16:32:09 +00001182 serverInfo.getDhcpServerConnectPoint().get());
1183 internalPackets.add(internalPacket);
1184 }
1185 log.debug("num of client packets to send is{}", internalPackets.size());
Kalhee Kimea4b6c22017-11-09 14:38:37 +00001186
Kalhee Kim495c9b22017-11-07 16:32:09 +00001187 return internalPackets;
1188 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001189
1190 /**
Kalhee Kim45fede42017-09-05 19:05:06 +00001191 * process the DHCP6 relay-reply packet from dhcp server.
1192 *
1193 * @param context packet context
1194 * @param receivedPacket server ethernet packet
1195 * @param recevingInterfaces set of server side interfaces
Kalhee Kim495c9b22017-11-07 16:32:09 +00001196 * @return internalPacket toward client
Kalhee Kim45fede42017-09-05 19:05:06 +00001197 */
1198 private InternalPacket processDhcp6PacketFromServer(PacketContext context,
1199 Ethernet receivedPacket, Set<Interface> recevingInterfaces) {
Kalhee Kim45fede42017-09-05 19:05:06 +00001200 // get dhcp6 header.
Ray Milkeyf0c47612017-09-28 11:29:38 -07001201 Ethernet etherReply = receivedPacket.duplicate();
Kalhee Kim45fede42017-09-05 19:05:06 +00001202 IPv6 ipv6Packet = (IPv6) etherReply.getPayload();
1203 UDP udpPacket = (UDP) ipv6Packet.getPayload();
1204 DHCP6 dhcp6Relay = (DHCP6) udpPacket.getPayload();
Taras Lemkin96a0d342018-03-26 14:52:58 +00001205 Boolean directConnFlag = Dhcp6HandlerUtil.directlyConnected(dhcp6Relay);
Kalhee Kim45fede42017-09-05 19:05:06 +00001206
Kalhee Kim495c9b22017-11-07 16:32:09 +00001207 DHCP6 embeddedDhcp6 = dhcp6Relay.getOptions().stream()
1208 .filter(opt -> opt instanceof Dhcp6RelayOption)
1209 .map(BasePacket::getPayload)
1210 .map(pld -> (DHCP6) pld)
1211 .findFirst()
1212 .orElse(null);
Kalhee Kimd21029f2017-09-26 20:21:53 +00001213 ConnectPoint inPort = context.inPacket().receivedFrom();
Kalhee Kim495c9b22017-11-07 16:32:09 +00001214 DhcpServerInfo foundServerInfo = findServerInfoFromServer(directConnFlag, inPort);
Kalhee Kimd21029f2017-09-26 20:21:53 +00001215
Kalhee Kim495c9b22017-11-07 16:32:09 +00001216 if (foundServerInfo == null) {
1217 log.warn("Cannot find server info");
Kalhee Kimd94ceea2017-11-29 19:03:02 +00001218 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_SERVER_INFO);
Kalhee Kimd21029f2017-09-26 20:21:53 +00001219 return null;
Kalhee Kim495c9b22017-11-07 16:32:09 +00001220 } else {
Taras Lemkin96a0d342018-03-26 14:52:58 +00001221 if (Dhcp6HandlerUtil.isServerIpEmpty(foundServerInfo)) {
Kalhee Kim495c9b22017-11-07 16:32:09 +00001222 log.warn("Cannot find server info's ipaddress");
Kalhee Kimd94ceea2017-11-29 19:03:02 +00001223 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_SERVER_IP6ADDR);
Kalhee Kim495c9b22017-11-07 16:32:09 +00001224 return null;
1225 }
Kalhee Kimd21029f2017-09-26 20:21:53 +00001226 }
1227
Kalhee Kim45fede42017-09-05 19:05:06 +00001228 Dhcp6InterfaceIdOption interfaceIdOption = dhcp6Relay.getOptions().stream()
1229 .filter(opt -> opt instanceof Dhcp6InterfaceIdOption)
1230 .map(opt -> (Dhcp6InterfaceIdOption) opt)
1231 .findFirst()
1232 .orElse(null);
Kalhee Kim45fede42017-09-05 19:05:06 +00001233 if (interfaceIdOption == null) {
1234 log.warn("Interface Id option is not present, abort packet...");
Kalhee Kimd94ceea2017-11-29 19:03:02 +00001235 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.OPTION_MISSING_FAIL);
Kalhee Kim45fede42017-09-05 19:05:06 +00001236 return null;
1237 }
1238
1239 MacAddress peerMac = interfaceIdOption.getMacAddress();
1240 String clientConnectionPointStr = new String(interfaceIdOption.getInPort());
Kalhee Kim45fede42017-09-05 19:05:06 +00001241 ConnectPoint clientConnectionPoint = ConnectPoint.deviceConnectPoint(clientConnectionPointStr);
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +00001242 VlanId vlanIdInUse = VlanId.vlanId(interfaceIdOption.getVlanId());
1243 Interface clientInterface = interfaceService.getInterfacesByPort(clientConnectionPoint)
Taras Lemkin96a0d342018-03-26 14:52:58 +00001244 .stream().filter(iface -> Dhcp6HandlerUtil.interfaceContainsVlan(iface, vlanIdInUse))
Kalhee Kim495c9b22017-11-07 16:32:09 +00001245 .findFirst().orElse(null);
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +00001246 if (clientInterface == null) {
1247 log.warn("Cannot get client interface for from packet, abort... vlan {}", vlanIdInUse.toString());
Kalhee Kimd94ceea2017-11-29 19:03:02 +00001248 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_MATCHING_INTF);
Kalhee Kim45fede42017-09-05 19:05:06 +00001249 return null;
1250 }
Kalhee Kim44fbda12018-04-03 21:08:18 +00001251 etherReply.setVlanID(vlanIdInUse.toShort());
1252
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +00001253 MacAddress relayAgentMac = clientInterface.mac();
Kalhee Kim45fede42017-09-05 19:05:06 +00001254 if (relayAgentMac == null) {
Yi Tseng68ef26b2017-12-18 17:10:00 -08001255 log.warn("Can not get client interface mac, abort packet..");
Kalhee Kimd94ceea2017-11-29 19:03:02 +00001256 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_CLIENT_INTF_MAC);
Kalhee Kim45fede42017-09-05 19:05:06 +00001257 return null;
1258 }
1259 etherReply.setSourceMACAddress(relayAgentMac);
1260
1261 // find destMac
Yi Tseng68ef26b2017-12-18 17:10:00 -08001262 MacAddress clientMac;
Yi Tsengaa417a62017-09-08 17:22:51 -07001263 Ip6Address peerAddress = Ip6Address.valueOf(dhcp6Relay.getPeerAddress());
1264 Set<Host> clients = hostService.getHostsByIp(peerAddress);
Kalhee Kim45fede42017-09-05 19:05:06 +00001265 if (clients.isEmpty()) {
Yi Tseng68ef26b2017-12-18 17:10:00 -08001266 log.trace("There's no host found for this address {}",
Kalhee Kim45fede42017-09-05 19:05:06 +00001267 HexString.toHexString(dhcp6Relay.getPeerAddress(), ":"));
Yi Tseng68ef26b2017-12-18 17:10:00 -08001268 log.trace("Let's look up interfaceId {}", HexString.toHexString(peerMac.toBytes(), ":"));
Kalhee Kim45fede42017-09-05 19:05:06 +00001269 clientMac = peerMac;
1270 } else {
1271 clientMac = clients.iterator().next().mac();
1272 if (clientMac == null) {
1273 log.warn("No client mac address found, abort packet...");
Kalhee Kimd94ceea2017-11-29 19:03:02 +00001274 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_CLIENT_INTF_MAC);
Kalhee Kim45fede42017-09-05 19:05:06 +00001275 return null;
1276 }
Yi Tseng68ef26b2017-12-18 17:10:00 -08001277 log.trace("Client mac address found from getHostByIp");
Kalhee Kim45fede42017-09-05 19:05:06 +00001278 }
1279 etherReply.setDestinationMACAddress(clientMac);
Kalhee Kim45fede42017-09-05 19:05:06 +00001280 // ip header
1281 ipv6Packet.setSourceAddress(dhcp6Relay.getLinkAddress());
1282 ipv6Packet.setDestinationAddress(dhcp6Relay.getPeerAddress());
1283 // udp header
1284 udpPacket.setSourcePort(UDP.DHCP_V6_SERVER_PORT);
1285 if (directConnFlag) {
1286 udpPacket.setDestinationPort(UDP.DHCP_V6_CLIENT_PORT);
1287 } else {
1288 udpPacket.setDestinationPort(UDP.DHCP_V6_SERVER_PORT);
1289 }
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001290
Kalhee Kim45fede42017-09-05 19:05:06 +00001291
Taras Lemkin96a0d342018-03-26 14:52:58 +00001292 boolean hostOrRouteAllowed = learnRouteFromLeasequery ||
1293 Dhcp6HandlerUtil.getDhcp6LeafMessageType(dhcp6Relay) != MsgType.LEASEQUERY_REPLY;
1294 log.debug("Can add host or route: {}", hostOrRouteAllowed);
1295
1296 if (hostOrRouteAllowed) {
1297 // add host or route
1298 addHostOrRoute(directConnFlag, clientConnectionPoint, dhcp6Relay, embeddedDhcp6,
1299 clientMac, clientInterface);
1300 }
1301
Kalhee Kim45fede42017-09-05 19:05:06 +00001302 udpPacket.setPayload(embeddedDhcp6);
1303 udpPacket.resetChecksum();
1304 ipv6Packet.setPayload(udpPacket);
1305 etherReply.setPayload(ipv6Packet);
Taras Lemkin96a0d342018-03-26 14:52:58 +00001306 return InternalPacket.internalPacket(etherReply, clientConnectionPoint);
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001307 }
Yi Tseng483ac6f2017-08-02 15:03:31 -07001308
1309 @Override
Kalhee Kimba366062017-11-07 16:32:09 +00001310 public void setDhcpFpmEnabled(Boolean enabled) {
1311 dhcpFpmEnabled = enabled;
1312 }
1313
1314 @Override
Yi Tseng483ac6f2017-08-02 15:03:31 -07001315 public void setDefaultDhcpServerConfigs(Collection<DhcpServerConfig> configs) {
Kalhee Kim495c9b22017-11-07 16:32:09 +00001316 log.debug("setDefaultDhcpServerConfigs is called.");
Yi Tseng919b2df2017-09-07 16:22:51 -07001317 setDhcpServerConfigs(configs, defaultServerInfoList);
Yi Tseng919b2df2017-09-07 16:22:51 -07001318 }
1319
1320 @Override
1321 public void setIndirectDhcpServerConfigs(Collection<DhcpServerConfig> configs) {
Kalhee Kim495c9b22017-11-07 16:32:09 +00001322 log.debug("setIndirectDhcpServerConfigs is called.");
Yi Tseng919b2df2017-09-07 16:22:51 -07001323 setDhcpServerConfigs(configs, indirectServerInfoList);
Yi Tseng919b2df2017-09-07 16:22:51 -07001324 }
1325
1326 public void setDhcpServerConfigs(Collection<DhcpServerConfig> configs, List<DhcpServerInfo> serverInfoList) {
Kalhee Kim495c9b22017-11-07 16:32:09 +00001327 log.debug("config size {}.", configs.size());
1328
Kalhee Kim45fede42017-09-05 19:05:06 +00001329 if (configs.size() == 0) {
1330 // no config to update
1331 return;
1332 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001333 // TODO: currently we pick up first DHCP server config.
1334 // Will use other server configs in the future for HA.
Kalhee Kim495c9b22017-11-07 16:32:09 +00001335 Boolean isConfigValid = false;
1336 for (DhcpServerConfig serverConfig : configs) {
1337 if (serverConfig.getDhcpServerIp6().isPresent()) {
1338 isConfigValid = true;
1339 break;
1340 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001341 }
Kalhee Kim495c9b22017-11-07 16:32:09 +00001342 if (!isConfigValid) {
1343 log.warn("No IP V6 server address found.");
1344 return; // No IP V6 address found
1345 }
1346 for (DhcpServerInfo oldServerInfo : serverInfoList) {
Yi Tseng919b2df2017-09-07 16:22:51 -07001347 // stop monitoring gateway or server
1348 oldServerInfo.getDhcpGatewayIp6().ifPresent(gatewayIp -> {
1349 hostService.stopMonitoringIp(gatewayIp);
1350 });
1351 oldServerInfo.getDhcpServerIp6().ifPresent(serverIp -> {
1352 hostService.stopMonitoringIp(serverIp);
Yi Tseng525ff402017-10-23 19:39:39 -07001353 cancelDhcpPacket(serverIp);
Yi Tseng919b2df2017-09-07 16:22:51 -07001354 });
1355 }
Kalhee Kim495c9b22017-11-07 16:32:09 +00001356 serverInfoList.clear();
1357 for (DhcpServerConfig serverConfig : configs) {
1358 // Create new server info according to the config
1359 DhcpServerInfo newServerInfo = new DhcpServerInfo(serverConfig,
1360 DhcpServerInfo.Version.DHCP_V6);
1361 checkState(newServerInfo.getDhcpServerConnectPoint().isPresent(),
1362 "Connect point not exists");
1363 checkState(newServerInfo.getDhcpServerIp6().isPresent(),
1364 "IP of DHCP server not exists");
Yi Tseng919b2df2017-09-07 16:22:51 -07001365
Kalhee Kim495c9b22017-11-07 16:32:09 +00001366 log.debug("DHCP server connect point: {}", newServerInfo.getDhcpServerConnectPoint().orElse(null));
1367 log.debug("DHCP server IP: {}", newServerInfo.getDhcpServerIp6().orElse(null));
Yi Tseng919b2df2017-09-07 16:22:51 -07001368
Kalhee Kim495c9b22017-11-07 16:32:09 +00001369 Ip6Address serverIp = newServerInfo.getDhcpServerIp6().get();
1370 Ip6Address ipToProbe;
1371 if (newServerInfo.getDhcpGatewayIp6().isPresent()) {
1372 ipToProbe = newServerInfo.getDhcpGatewayIp6().get();
1373 } else {
1374 ipToProbe = newServerInfo.getDhcpServerIp6().orElse(null);
1375 }
1376 String hostToProbe = newServerInfo.getDhcpGatewayIp6()
1377 .map(ip -> "gateway").orElse("server");
Yi Tseng919b2df2017-09-07 16:22:51 -07001378
Kalhee Kim495c9b22017-11-07 16:32:09 +00001379 log.warn("Probing to resolve {} IP {}", hostToProbe, ipToProbe);
1380 hostService.startMonitoringIp(ipToProbe);
1381
1382 Set<Host> hosts = hostService.getHostsByIp(ipToProbe);
1383 if (!hosts.isEmpty()) {
1384 Host host = hosts.iterator().next();
1385 newServerInfo.setDhcpConnectVlan(host.vlan());
1386 newServerInfo.setDhcpConnectMac(host.mac());
1387 log.warn("Host found host {}", host);
1388
1389 } else {
1390 log.warn("No host found host ip {}", ipToProbe);
1391 }
1392 // Add new server info
1393 synchronized (this) {
1394 serverInfoList.add(newServerInfo);
1395 }
1396 if (!hosts.isEmpty()) {
1397 requestDhcpPacket(serverIp);
1398 }
Yi Tseng919b2df2017-09-07 16:22:51 -07001399 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001400 }
1401
1402 class InternalHostListener implements HostListener {
1403 @Override
1404 public void event(HostEvent event) {
1405 switch (event.type()) {
1406 case HOST_ADDED:
1407 case HOST_UPDATED:
Charles Chan24a96ff2018-08-13 10:32:03 -07001408 case HOST_MOVED:
Jordan Halterman6328db72018-04-10 13:34:50 -04001409 log.trace("Scheduled host event {}", event);
1410 hostEventExecutor.execute(() -> hostUpdated(event.subject()));
Kalhee Kim45fede42017-09-05 19:05:06 +00001411 break;
1412 case HOST_REMOVED:
Jordan Halterman6328db72018-04-10 13:34:50 -04001413 log.trace("Scheduled host event {}", event);
1414 hostEventExecutor.execute(() -> hostRemoved(event.subject()));
Kalhee Kim45fede42017-09-05 19:05:06 +00001415 break;
Kalhee Kim45fede42017-09-05 19:05:06 +00001416 default:
1417 break;
1418 }
1419 }
1420 }
1421
1422 /**
Kalhee Kim45fede42017-09-05 19:05:06 +00001423 * Handle host updated.
1424 * If the host is DHCP server or gateway, update connect mac and vlan.
1425 *
1426 * @param host the host
1427 */
1428 private void hostUpdated(Host host) {
Yi Tseng525ff402017-10-23 19:39:39 -07001429 hostUpdated(host, defaultServerInfoList);
1430 hostUpdated(host, indirectServerInfoList);
Kalhee Kim45fede42017-09-05 19:05:06 +00001431 }
1432
Yi Tseng525ff402017-10-23 19:39:39 -07001433 private void hostUpdated(Host host, List<DhcpServerInfo> serverInfoList) {
jjosep004c05f55ab2018-08-21 09:01:10 -04001434 serverInfoList.stream().forEach(serverInfo -> {
Yi Tseng525ff402017-10-23 19:39:39 -07001435 Ip6Address serverIp = serverInfo.getDhcpServerIp6().orElse(null);
jjosep004c05f55ab2018-08-21 09:01:10 -04001436 Ip6Address targetIp = serverInfo.getDhcpGatewayIp6().orElse(null);
Yi Tseng525ff402017-10-23 19:39:39 -07001437
1438 if (targetIp == null) {
1439 targetIp = serverIp;
1440 }
Yi Tseng525ff402017-10-23 19:39:39 -07001441 if (targetIp != null) {
1442 if (host.ipAddresses().contains(targetIp)) {
1443 serverInfo.setDhcpConnectMac(host.mac());
1444 serverInfo.setDhcpConnectVlan(host.vlan());
1445 requestDhcpPacket(serverIp);
1446 }
1447 }
jjosep004c05f55ab2018-08-21 09:01:10 -04001448 });
Yi Tseng525ff402017-10-23 19:39:39 -07001449 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001450 /**
1451 * Handle host removed.
1452 * If the host is DHCP server or gateway, unset connect mac and vlan.
1453 *
1454 * @param host the host
1455 */
1456 private void hostRemoved(Host host) {
Yi Tseng525ff402017-10-23 19:39:39 -07001457 hostRemoved(host, defaultServerInfoList);
1458 hostRemoved(host, indirectServerInfoList);
Yi Tseng919b2df2017-09-07 16:22:51 -07001459 }
1460
Yi Tseng525ff402017-10-23 19:39:39 -07001461 private void hostRemoved(Host host, List<DhcpServerInfo> serverInfoList) {
jjosep004c05f55ab2018-08-21 09:01:10 -04001462 serverInfoList.stream().forEach(serverInfo -> {
Yi Tseng525ff402017-10-23 19:39:39 -07001463 Ip6Address serverIp = serverInfo.getDhcpServerIp6().orElse(null);
jjosep004c05f55ab2018-08-21 09:01:10 -04001464 Ip6Address targetIp = serverInfo.getDhcpGatewayIp6().orElse(null);
Yi Tseng525ff402017-10-23 19:39:39 -07001465
1466 if (targetIp == null) {
1467 targetIp = serverIp;
1468 }
Yi Tseng525ff402017-10-23 19:39:39 -07001469 if (targetIp != null) {
1470 if (host.ipAddresses().contains(targetIp)) {
1471 serverInfo.setDhcpConnectVlan(null);
1472 serverInfo.setDhcpConnectMac(null);
1473 cancelDhcpPacket(serverIp);
1474 }
1475 }
jjosep004c05f55ab2018-08-21 09:01:10 -04001476 });
Yi Tseng525ff402017-10-23 19:39:39 -07001477 }
1478
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001479 /**
1480 * Gets Interface facing to the server for default host.
1481 *
1482 * @return the Interface facing to the server; null if not found
1483 */
1484 private Interface getServerInterface() {
1485 DhcpServerInfo serverInfo;
1486 ConnectPoint dhcpServerConnectPoint;
1487 VlanId dhcpConnectVlan;
1488
1489 if (!defaultServerInfoList.isEmpty()) {
1490 serverInfo = defaultServerInfoList.get(0);
1491 dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1492 dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1493 } else {
1494 return null;
1495 }
1496 if (dhcpServerConnectPoint == null || dhcpConnectVlan == null) {
1497 log.info("Default DHCP server {} not resolve yet", serverInfo.getDhcpGatewayIp6());
1498 return null;
1499 }
1500 return interfaceService.getInterfacesByPort(dhcpServerConnectPoint)
1501 .stream()
Taras Lemkin96a0d342018-03-26 14:52:58 +00001502 .filter(iface -> Dhcp6HandlerUtil.interfaceContainsVlan(iface, dhcpConnectVlan))
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001503 .findFirst()
1504 .orElse(null);
1505 }
1506
1507 /**
1508 * Gets Interface facing to the server for indirect hosts.
1509 * Use default server Interface if indirect server not configured.
1510 *
1511 * @return the Interface facing to the server; null if not found
1512 */
1513 private Interface getIndirectServerInterface() {
1514 DhcpServerInfo serverInfo;
1515
1516 ConnectPoint indirectDhcpServerConnectPoint;
1517 VlanId indirectDhcpConnectVlan;
1518
1519 if (!indirectServerInfoList.isEmpty()) {
1520 serverInfo = indirectServerInfoList.get(0);
1521 indirectDhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1522 indirectDhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1523 } else {
1524 return getServerInterface();
1525 }
1526 if (indirectDhcpServerConnectPoint == null || indirectDhcpConnectVlan == null) {
1527 log.info("Indirect DHCP server {} not resolve yet", serverInfo.getDhcpGatewayIp6());
1528 return null;
1529 }
1530 return interfaceService.getInterfacesByPort(indirectDhcpServerConnectPoint)
1531 .stream()
Taras Lemkin96a0d342018-03-26 14:52:58 +00001532 .filter(iface -> Dhcp6HandlerUtil.interfaceContainsVlan(iface, indirectDhcpConnectVlan))
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001533 .findFirst()
1534 .orElse(null);
1535 }
1536
Kalhee Kim45b24182017-10-18 18:30:23 +00001537 /**
Kalhee Kim495c9b22017-11-07 16:32:09 +00001538 * Checks if serverInfo's host info (mac and vlan) is filled in; if not, fills in.
1539 *
1540 * @param serverInfo server information
1541 * @return newServerInfo if host info can be either found or filled in.
1542 */
1543 private DhcpServerInfo getHostInfoForServerInfo(DhcpServerInfo serverInfo, List<DhcpServerInfo> sererInfoList) {
1544 DhcpServerInfo newServerInfo = null;
1545 MacAddress dhcpServerConnectMac = serverInfo.getDhcpConnectMac().orElse(null);
1546 VlanId dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1547 ConnectPoint dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1548
1549 if (dhcpServerConnectMac != null && dhcpConnectVlan != null) {
1550 newServerInfo = serverInfo;
Charles Chan750c9992018-02-26 10:44:49 -08001551 log.debug("DHCP server {} host info found. ConnectPt{} Mac {} vlan {}", serverInfo.getDhcpServerIp6(),
Kalhee Kim495c9b22017-11-07 16:32:09 +00001552 dhcpServerConnectPoint, dhcpServerConnectMac, dhcpConnectVlan);
1553 } else {
1554 log.warn("DHCP server {} not resolve yet connectPt {} mac {} vlan {}", serverInfo.getDhcpServerIp6(),
1555 dhcpServerConnectPoint, dhcpServerConnectMac, dhcpConnectVlan);
1556
1557 Ip6Address ipToProbe;
1558 if (serverInfo.getDhcpGatewayIp6().isPresent()) {
1559 ipToProbe = serverInfo.getDhcpGatewayIp6().get();
1560 } else {
1561 ipToProbe = serverInfo.getDhcpServerIp6().orElse(null);
1562 }
1563 String hostToProbe = serverInfo.getDhcpGatewayIp6()
1564 .map(ip -> "gateway").orElse("server");
1565
1566 log.info("Dynamically probing to resolve {} IP {}", hostToProbe, ipToProbe);
1567 hostService.startMonitoringIp(ipToProbe);
1568
1569 Set<Host> hosts = hostService.getHostsByIp(ipToProbe);
1570 if (!hosts.isEmpty()) {
1571 int serverInfoIndex = sererInfoList.indexOf(serverInfo);
1572 Host host = hosts.iterator().next();
1573 serverInfo.setDhcpConnectVlan(host.vlan());
1574 serverInfo.setDhcpConnectMac(host.mac());
1575 // replace the serverInfo in the list
1576 sererInfoList.set(serverInfoIndex, serverInfo);
1577 newServerInfo = serverInfo;
1578 log.warn("Dynamically host found host {}", host);
1579 } else {
1580 log.warn("No host found host ip {} dynamically", ipToProbe);
1581 }
1582 }
1583 return newServerInfo;
1584 }
1585
1586 /**
Kalhee Kim45b24182017-10-18 18:30:23 +00001587 * Gets Interface facing to the server for default host.
1588 *
Kalhee Kim495c9b22017-11-07 16:32:09 +00001589 * @param serverInfo server information
Kalhee Kim45b24182017-10-18 18:30:23 +00001590 * @return the Interface facing to the server; null if not found
1591 */
Kalhee Kim495c9b22017-11-07 16:32:09 +00001592 private Interface getServerInterface(DhcpServerInfo serverInfo) {
1593 Interface serverInterface = null;
Yi Tseng25bfe372017-11-03 16:27:32 -07001594
Kalhee Kim495c9b22017-11-07 16:32:09 +00001595 ConnectPoint dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1596 VlanId dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1597
1598 if (dhcpServerConnectPoint != null && dhcpConnectVlan != null) {
1599 serverInterface = interfaceService.getInterfacesByPort(dhcpServerConnectPoint)
1600 .stream()
Taras Lemkin96a0d342018-03-26 14:52:58 +00001601 .filter(iface -> Dhcp6HandlerUtil.interfaceContainsVlan(iface, dhcpConnectVlan))
Kalhee Kim495c9b22017-11-07 16:32:09 +00001602 .findFirst()
1603 .orElse(null);
Yi Tseng25bfe372017-11-03 16:27:32 -07001604 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +00001605 log.warn("DHCP server {} not resolve yet connectPoint {} vlan {}", serverInfo.getDhcpServerIp6(),
1606 dhcpServerConnectPoint, dhcpConnectVlan);
Kalhee Kim45b24182017-10-18 18:30:23 +00001607 }
Kalhee Kim495c9b22017-11-07 16:32:09 +00001608
1609 return serverInterface;
Kalhee Kim45b24182017-10-18 18:30:23 +00001610 }
1611
Yi Tseng525ff402017-10-23 19:39:39 -07001612 private void requestDhcpPacket(Ip6Address serverIp) {
1613 requestServerDhcpPacket(serverIp);
1614 requestClientDhcpPacket(serverIp);
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001615 requestServerLQPacket(serverIp);
Yi Tseng525ff402017-10-23 19:39:39 -07001616 }
1617
1618 private void cancelDhcpPacket(Ip6Address serverIp) {
1619 cancelServerDhcpPacket(serverIp);
1620 cancelClientDhcpPacket(serverIp);
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001621 cancelServerLQPacket(serverIp);
Yi Tseng525ff402017-10-23 19:39:39 -07001622 }
1623
1624 private void cancelServerDhcpPacket(Ip6Address serverIp) {
1625 TrafficSelector serverSelector =
1626 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1627 .matchIPv6Src(serverIp.toIpPrefix())
1628 .build();
1629 packetService.cancelPackets(serverSelector,
1630 PacketPriority.CONTROL,
1631 appId);
1632 }
1633
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001634 private void cancelServerLQPacket(Ip6Address serverIp) {
1635 TrafficSelector serverSelector =
1636 DefaultTrafficSelector.builder(LEASE_QUERY_RESPONSE_SELECTOR)
1637 .matchIPv6Src(serverIp.toIpPrefix())
1638 .build();
1639 packetService.cancelPackets(serverSelector,
1640 PacketPriority.CONTROL,
1641 appId);
1642 }
1643
Yi Tseng525ff402017-10-23 19:39:39 -07001644 private void requestServerDhcpPacket(Ip6Address serverIp) {
1645 TrafficSelector serverSelector =
1646 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1647 .matchIPv6Src(serverIp.toIpPrefix())
1648 .build();
1649 packetService.requestPackets(serverSelector,
1650 PacketPriority.CONTROL,
1651 appId);
1652 }
1653
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001654 private void requestServerLQPacket(Ip6Address serverIp) {
1655 TrafficSelector serverSelector =
1656 DefaultTrafficSelector.builder(LEASE_QUERY_RESPONSE_SELECTOR)
1657 .matchIPv6Src(serverIp.toIpPrefix())
1658 .build();
1659 packetService.requestPackets(serverSelector,
1660 PacketPriority.CONTROL,
1661 appId);
1662 }
1663
Yi Tseng525ff402017-10-23 19:39:39 -07001664 private void cancelClientDhcpPacket(Ip6Address serverIp) {
1665 // Packet comes from relay
1666 TrafficSelector indirectClientSelector =
1667 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1668 .matchIPv6Dst(serverIp.toIpPrefix())
1669 .build();
1670 packetService.cancelPackets(indirectClientSelector,
1671 PacketPriority.CONTROL,
1672 appId);
Yi Tseng41dde702017-11-02 15:21:10 -07001673 indirectClientSelector =
1674 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1675 .matchIPv6Dst(Ip6Address.ALL_DHCP_RELAY_AGENTS_AND_SERVERS.toIpPrefix())
1676 .build();
1677 packetService.cancelPackets(indirectClientSelector,
1678 PacketPriority.CONTROL,
1679 appId);
1680 indirectClientSelector =
1681 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1682 .matchIPv6Dst(Ip6Address.ALL_DHCP_SERVERS.toIpPrefix())
1683 .build();
1684 packetService.cancelPackets(indirectClientSelector,
1685 PacketPriority.CONTROL,
1686 appId);
Yi Tseng525ff402017-10-23 19:39:39 -07001687
1688 // Packet comes from client
1689 packetService.cancelPackets(CLIENT_SERVER_SELECTOR,
1690 PacketPriority.CONTROL,
1691 appId);
1692 }
1693
1694 private void requestClientDhcpPacket(Ip6Address serverIp) {
1695 // Packet comes from relay
1696 TrafficSelector indirectClientSelector =
1697 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1698 .matchIPv6Dst(serverIp.toIpPrefix())
1699 .build();
1700 packetService.requestPackets(indirectClientSelector,
1701 PacketPriority.CONTROL,
1702 appId);
Yi Tseng41dde702017-11-02 15:21:10 -07001703 indirectClientSelector =
1704 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1705 .matchIPv6Dst(Ip6Address.ALL_DHCP_RELAY_AGENTS_AND_SERVERS.toIpPrefix())
1706 .build();
1707 packetService.requestPackets(indirectClientSelector,
1708 PacketPriority.CONTROL,
1709 appId);
1710 indirectClientSelector =
1711 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1712 .matchIPv6Dst(Ip6Address.ALL_DHCP_SERVERS.toIpPrefix())
1713 .build();
1714 packetService.requestPackets(indirectClientSelector,
1715 PacketPriority.CONTROL,
1716 appId);
Yi Tseng525ff402017-10-23 19:39:39 -07001717
1718 // Packet comes from client
1719 packetService.requestPackets(CLIENT_SERVER_SELECTOR,
1720 PacketPriority.CONTROL,
1721 appId);
1722 }
1723
1724 /**
1725 * Process the ignore rules.
1726 *
1727 * @param deviceId the device id
1728 * @param vlanId the vlan to be ignored
1729 * @param op the operation, ADD to install; REMOVE to uninstall rules
1730 */
1731 private void processIgnoreVlanRule(DeviceId deviceId, VlanId vlanId, Objective.Operation op) {
Yi Tseng525ff402017-10-23 19:39:39 -07001732 AtomicInteger installedCount = new AtomicInteger(DHCP_SELECTORS.size());
1733 DHCP_SELECTORS.forEach(trafficSelector -> {
1734 TrafficSelector selector = DefaultTrafficSelector.builder(trafficSelector)
1735 .matchVlanId(vlanId)
1736 .build();
1737
1738 ForwardingObjective.Builder builder = DefaultForwardingObjective.builder()
1739 .withFlag(ForwardingObjective.Flag.VERSATILE)
1740 .withSelector(selector)
1741 .withPriority(IGNORE_CONTROL_PRIORITY)
Yi Tsengdbabeed2017-11-29 10:49:20 -08001742 .withTreatment(DefaultTrafficTreatment.emptyTreatment())
Yi Tseng525ff402017-10-23 19:39:39 -07001743 .fromApp(appId);
1744
1745
1746 ObjectiveContext objectiveContext = new ObjectiveContext() {
1747 @Override
1748 public void onSuccess(Objective objective) {
1749 log.info("Ignore rule {} (Vlan id {}, device {}, selector {})",
1750 op, vlanId, deviceId, selector);
1751 int countDown = installedCount.decrementAndGet();
1752 if (countDown != 0) {
1753 return;
1754 }
1755 switch (op) {
1756 case ADD:
1757 ignoredVlans.put(deviceId, vlanId);
1758 break;
1759 case REMOVE:
1760 ignoredVlans.remove(deviceId, vlanId);
1761 break;
1762 default:
1763 log.warn("Unsupported objective operation {}", op);
1764 break;
1765 }
1766 }
1767
1768 @Override
1769 public void onError(Objective objective, ObjectiveError error) {
1770 log.warn("Can't {} ignore rule (vlan id {}, selector {}, device {}) due to {}",
1771 op, vlanId, selector, deviceId, error);
1772 }
1773 };
1774
1775 ForwardingObjective fwd;
1776 switch (op) {
1777 case ADD:
1778 fwd = builder.add(objectiveContext);
1779 break;
1780 case REMOVE:
1781 fwd = builder.remove(objectiveContext);
1782 break;
1783 default:
1784 log.warn("Unsupported objective operation {}", op);
1785 return;
1786 }
1787
1788 Device device = deviceService.getDevice(deviceId);
1789 if (device == null || !device.is(Pipeliner.class)) {
1790 log.warn("Device {} is not available now, wait until device is available", deviceId);
1791 return;
1792 }
1793 flowObjectiveService.apply(deviceId, fwd);
1794 });
1795 }
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001796
Kalhee Kim121ba922017-11-01 17:56:44 +00001797 /**
1798 * Find first ipaddress for a given Host info i.e. mac and vlan.
1799 *
1800 * @param clientMac client mac
1801 * @param vlanId packet's vlan
1802 * @return next-hop link-local ipaddress for a given host
1803 */
Kalhee Kim495c9b22017-11-07 16:32:09 +00001804 private IpAddress getFirstIpByHost(Boolean directConnFlag, MacAddress clientMac, VlanId vlanId) {
Kalhee Kim121ba922017-11-01 17:56:44 +00001805 IpAddress nextHopIp;
1806 // pick out the first link-local ip address
1807 HostId gwHostId = HostId.hostId(clientMac, vlanId);
1808 Host gwHost = hostService.getHost(gwHostId);
1809 if (gwHost == null) {
1810 log.warn("Can't find gateway host for hostId {}", gwHostId);
1811 return null;
1812 }
Kalhee Kim495c9b22017-11-07 16:32:09 +00001813 if (directConnFlag) {
1814 nextHopIp = gwHost.ipAddresses()
1815 .stream()
1816 .filter(IpAddress::isIp6)
1817 .map(IpAddress::getIp6Address)
1818 .findFirst()
1819 .orElse(null);
1820 } else {
1821 nextHopIp = gwHost.ipAddresses()
1822 .stream()
1823 .filter(IpAddress::isIp6)
1824 .filter(ip6 -> ip6.isLinkLocal())
1825 .map(IpAddress::getIp6Address)
1826 .findFirst()
1827 .orElse(null);
1828 }
Kalhee Kim121ba922017-11-01 17:56:44 +00001829 return nextHopIp;
1830 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +00001831
Kalhee Kim495c9b22017-11-07 16:32:09 +00001832 private List<DhcpServerInfo> findValidServerInfo(boolean directConnFlag) {
1833 List<DhcpServerInfo> validServerInfo;
1834
1835 if (directConnFlag || indirectServerInfoList.isEmpty()) {
1836 validServerInfo = new ArrayList<DhcpServerInfo>(defaultServerInfoList);
1837 } else {
1838 validServerInfo = new ArrayList<DhcpServerInfo>(indirectServerInfoList);
1839 }
1840 return validServerInfo;
1841 }
1842
1843 private DhcpServerInfo findServerInfoFromServer(boolean directConnFlag, ConnectPoint inPort) {
1844 List<DhcpServerInfo> validServerInfoList = findValidServerInfo(directConnFlag);
1845 DhcpServerInfo foundServerInfo = null;
1846 for (DhcpServerInfo serverInfo : validServerInfoList) {
1847 if (inPort.equals(serverInfo.getDhcpServerConnectPoint().get())) {
1848 foundServerInfo = serverInfo;
Charles Chan750c9992018-02-26 10:44:49 -08001849 log.debug("ServerInfo found for Rcv port {} Server Connect Point {} for {}",
Kalhee Kim495c9b22017-11-07 16:32:09 +00001850 inPort, serverInfo.getDhcpServerConnectPoint(), directConnFlag ? "direct" : "indirect");
1851 break;
1852 } else {
1853 log.warn("Rcv port {} not the same as Server Connect Point {} for {}",
1854 inPort, serverInfo.getDhcpServerConnectPoint(), directConnFlag ? "direct" : "indirect");
1855 }
1856 }
1857 return foundServerInfo;
1858 }
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001859
Kalhee Kim495c9b22017-11-07 16:32:09 +00001860 /**
1861 * Set the dhcp6 lease expiry poll interval value.
1862 *
1863 * @param val poll interval value in seconds
1864 */
Kalhee Kim2f07c602017-11-15 18:57:53 +00001865 @Override
Kalhee Kim495c9b22017-11-07 16:32:09 +00001866 public void setDhcp6PollInterval(int val) {
1867 dhcp6PollInterval = val;
1868 }
1869
Kalhee Kim2f07c602017-11-15 18:57:53 +00001870 /**
1871 * get the dhcp6 lease expiry poll interval value.
1872 * This is a private function
1873 * @return poll interval value in seconds
1874 */
1875 private int getDhcp6PollInterval() {
1876 return dhcp6PollInterval;
1877 }
1878
1879 /**
1880 * Find lease-expired ipaddresses and pd prefixes.
1881 * Removing host/route/fpm entries.
1882 */
1883 public void timeTick() {
1884 long currentTime = System.currentTimeMillis();
1885 Collection<DhcpRecord> records = dhcpRelayStore.getDhcpRecords();
1886
1887 log.debug("timeTick called currenttime {} records num {} ", currentTime, records.size());
1888
1889 records.forEach(record -> {
1890 boolean addrOrPdRemoved = false;
1891 DHCP6.MsgType ip6Status = record.ip6Status().orElse(null);
1892 if (ip6Status == null) {
1893 log.debug("record is not valid v6 record.");
1894 return;
1895 }
1896
1897 if ((currentTime - record.getLastIp6Update()) >
1898 ((record.addrPrefTime() + getDhcp6PollInterval() / 2) * 1000)) {
1899 // remove ipaddress from host/route table
1900 IpAddress ip = record.ip6Address().orElse(null);
1901 if (ip != null) {
1902 if (record.directlyConnected()) {
1903 providerService.removeIpFromHost(HostId.hostId(record.macAddress(),
1904 record.vlanId()), ip);
1905 } else {
1906 MacAddress gwMac = record.nextHop().orElse(null);
1907 if (gwMac == null) {
1908 log.warn("Can't find gateway mac address from record {} for ip6Addr", record);
1909 return;
1910 }
1911 IpAddress nextHopIp = getFirstIpByHost(record.directlyConnected(),
1912 gwMac,
1913 record.vlanId());
Kalhee Kim54a97c92018-04-02 21:23:46 +00001914 Route route = new Route(Route.Source.DHCP, ip.toIpPrefix(), nextHopIp);
Kalhee Kim2f07c602017-11-15 18:57:53 +00001915 routeStore.removeRoute(route);
1916 }
1917 record.updateAddrPrefTime(0);
1918 record.ip6Address(null);
1919 addrOrPdRemoved = true;
1920 dhcpRelayStore.updateDhcpRecord(HostId.hostId(record.macAddress(),
1921 record.vlanId()), record);
1922 log.warn("IP6 address is set to null. delta {} lastUpdate {} addrPrefTime {}",
1923 (currentTime - record.getLastIp6Update()), record.getLastIp6Update(),
1924 record.addrPrefTime());
1925 }
1926 }
1927 if ((currentTime - record.getLastPdUpdate()) >
1928 ((record.pdPrefTime() + getDhcp6PollInterval() / 2) * 1000)) {
1929 // remove PD from route/fpm table
1930 IpPrefix pdIpPrefix = record.pdPrefix().orElse(null);
1931 if (pdIpPrefix != null) {
1932 if (record.directlyConnected()) {
1933 providerService.removeIpFromHost(HostId.hostId(record.macAddress(), record.vlanId()),
1934 pdIpPrefix.address().getIp6Address());
1935 } else {
1936 MacAddress gwMac = record.nextHop().orElse(null);
1937 if (gwMac == null) {
1938 log.warn("Can't find gateway mac address from record {} for PD prefix", record);
1939 return;
1940 }
1941 IpAddress nextHopIp = getFirstIpByHost(record.directlyConnected(),
1942 gwMac,
1943 record.vlanId());
Kalhee Kim54a97c92018-04-02 21:23:46 +00001944 Route route = new Route(Route.Source.DHCP, pdIpPrefix, nextHopIp);
Kalhee Kim2f07c602017-11-15 18:57:53 +00001945 routeStore.removeRoute(route);
1946 if (this.dhcpFpmEnabled) {
1947 dhcpFpmPrefixStore.removeFpmRecord(pdIpPrefix);
1948 }
1949 }
1950 record.updatePdPrefTime(0);
1951 record.pdPrefix(null);
1952 addrOrPdRemoved = true;
1953 dhcpRelayStore.updateDhcpRecord(HostId.hostId(record.macAddress(),
1954 record.vlanId()), record);
1955 log.warn("PD prefix is set to null.delta {} pdPrefTime {}",
1956 (currentTime - record.getLastPdUpdate()), record.pdPrefTime());
1957 }
1958 }
1959 if (addrOrPdRemoved &&
1960 !record.ip6Address().isPresent() && !record.pdPrefix().isPresent()) {
1961 log.warn("ip6Status {} IP6 address and IP6 PD both are null. Remove record.", ip6Status);
1962 dhcpRelayStore.removeDhcpRecord(HostId.hostId(record.macAddress(), record.vlanId()));
1963 }
1964 }
1965 );
1966 }
1967}