blob: ba759a21af899e004b2a6e8dda27e2f30386bdb4 [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;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070025import org.osgi.service.component.annotations.Activate;
26import org.osgi.service.component.annotations.Component;
27import org.osgi.service.component.annotations.Deactivate;
28import org.osgi.service.component.annotations.Modified;
29import org.osgi.service.component.annotations.Reference;
30import org.osgi.service.component.annotations.ReferenceCardinality;
Yi Tseng51301292017-07-28 13:02:59 -070031import org.onlab.packet.BasePacket;
Kalhee Kim45fede42017-09-05 19:05:06 +000032import org.onlab.packet.DHCP6;
33import org.onlab.packet.IPv6;
34import org.onlab.packet.Ethernet;
35import org.onlab.packet.Ip6Address;
Yi Tseng51301292017-07-28 13:02:59 -070036import org.onlab.packet.IpAddress;
Kalhee Kim45fede42017-09-05 19:05:06 +000037import org.onlab.packet.IpPrefix;
Yi Tseng51301292017-07-28 13:02:59 -070038import org.onlab.packet.MacAddress;
Yi Tseng525ff402017-10-23 19:39:39 -070039import org.onlab.packet.TpPort;
Kalhee Kim45fede42017-09-05 19:05:06 +000040import org.onlab.packet.UDP;
Yi Tseng51301292017-07-28 13:02:59 -070041import org.onlab.packet.VlanId;
Lior Assoulinea21c0ca2018-01-28 16:18:48 -080042import org.onlab.packet.dhcp.Dhcp6ClientDataOption;
43import org.onlab.packet.dhcp.Dhcp6LeaseQueryOption;
Kalhee Kim45fede42017-09-05 19:05:06 +000044import org.onlab.packet.dhcp.Dhcp6RelayOption;
45import org.onlab.packet.dhcp.Dhcp6InterfaceIdOption;
Lior Assoulinea21c0ca2018-01-28 16:18:48 -080046import org.onlab.packet.dhcp.Dhcp6Option;
Kalhee Kim45fede42017-09-05 19:05:06 +000047import org.onlab.packet.dhcp.Dhcp6IaNaOption;
48import org.onlab.packet.dhcp.Dhcp6IaTaOption;
49import org.onlab.packet.dhcp.Dhcp6IaPdOption;
50import org.onlab.packet.dhcp.Dhcp6IaAddressOption;
51import org.onlab.packet.dhcp.Dhcp6IaPrefixOption;
Kalhee Kimea4b6c22017-11-09 14:38:37 +000052import org.onlab.packet.dhcp.Dhcp6ClientIdOption;
53import org.onlab.packet.dhcp.Dhcp6Duid;
Kalhee Kim495c9b22017-11-07 16:32:09 +000054import org.onlab.packet.DHCP6.MsgType;
Kalhee Kim45fede42017-09-05 19:05:06 +000055import org.onlab.util.HexString;
Taras Lemkin96a0d342018-03-26 14:52:58 +000056import org.onlab.util.Tools;
57import org.onosproject.cfg.ComponentConfigService;
Yi Tseng525ff402017-10-23 19:39:39 -070058import org.onosproject.core.ApplicationId;
59import org.onosproject.core.CoreService;
Yi Tseng51301292017-07-28 13:02:59 -070060import org.onosproject.dhcprelay.api.DhcpHandler;
Yi Tseng919b2df2017-09-07 16:22:51 -070061import org.onosproject.dhcprelay.api.DhcpServerInfo;
Yi Tseng525ff402017-10-23 19:39:39 -070062import org.onosproject.dhcprelay.config.IgnoreDhcpConfig;
Kalhee Kim45fede42017-09-05 19:05:06 +000063import org.onosproject.dhcprelay.store.DhcpRelayStore;
Kalhee Kimea4b6c22017-11-09 14:38:37 +000064import org.onosproject.dhcprelay.store.DhcpRecord;
Kalhee Kimba366062017-11-07 16:32:09 +000065import org.onosproject.dhcprelay.store.DhcpFpmPrefixStore;
Kalhee Kimd94ceea2017-11-29 19:03:02 +000066import org.onosproject.dhcprelay.store.DhcpRelayCounters;
67import org.onosproject.dhcprelay.store.DhcpRelayCountersStore;
Yi Tseng525ff402017-10-23 19:39:39 -070068import org.onosproject.net.Device;
69import org.onosproject.net.DeviceId;
Kalhee Kimba366062017-11-07 16:32:09 +000070import org.onosproject.routing.fpm.api.FpmRecord;
Yi Tseng525ff402017-10-23 19:39:39 -070071import org.onosproject.net.behaviour.Pipeliner;
72import org.onosproject.net.device.DeviceService;
73import org.onosproject.net.flow.DefaultTrafficSelector;
74import org.onosproject.net.flow.TrafficSelector;
75import org.onosproject.net.flowobjective.DefaultForwardingObjective;
76import org.onosproject.net.flowobjective.FlowObjectiveService;
77import org.onosproject.net.flowobjective.ForwardingObjective;
78import org.onosproject.net.flowobjective.Objective;
79import org.onosproject.net.flowobjective.ObjectiveContext;
80import org.onosproject.net.flowobjective.ObjectiveError;
Yi Tsengaa417a62017-09-08 17:22:51 -070081import org.onosproject.net.host.HostProvider;
82import org.onosproject.net.host.HostProviderRegistry;
83import org.onosproject.net.host.HostProviderService;
Kalhee Kim45fede42017-09-05 19:05:06 +000084import org.onosproject.net.host.HostService;
85import org.onosproject.net.host.DefaultHostDescription;
86import org.onosproject.net.host.HostDescription;
Kalhee Kim45fede42017-09-05 19:05:06 +000087import org.onosproject.net.host.HostListener;
88import org.onosproject.net.host.HostEvent;
89import org.onosproject.net.intf.Interface;
90import org.onosproject.net.intf.InterfaceService;
Yi Tseng525ff402017-10-23 19:39:39 -070091import org.onosproject.net.packet.PacketPriority;
Yi Tsengaa417a62017-09-08 17:22:51 -070092import org.onosproject.net.provider.ProviderId;
Kalhee Kim45fede42017-09-05 19:05:06 +000093import org.onosproject.routeservice.Route;
94import org.onosproject.routeservice.RouteStore;
Yi Tseng483ac6f2017-08-02 15:03:31 -070095import org.onosproject.dhcprelay.config.DhcpServerConfig;
Yi Tseng51301292017-07-28 13:02:59 -070096import org.onosproject.net.ConnectPoint;
Kalhee Kim45fede42017-09-05 19:05:06 +000097import org.onosproject.net.Host;
98import org.onosproject.net.HostId;
99import org.onosproject.net.HostLocation;
100import org.onosproject.net.packet.DefaultOutboundPacket;
101import org.onosproject.net.packet.OutboundPacket;
Yi Tseng51301292017-07-28 13:02:59 -0700102import org.onosproject.net.packet.PacketContext;
Kalhee Kim45fede42017-09-05 19:05:06 +0000103import org.onosproject.net.packet.PacketService;
Taras Lemkin96a0d342018-03-26 14:52:58 +0000104import org.osgi.service.component.ComponentContext;
Kalhee Kim45fede42017-09-05 19:05:06 +0000105import org.slf4j.Logger;
106import org.slf4j.LoggerFactory;
107import org.onosproject.net.flow.DefaultTrafficTreatment;
108import org.onosproject.net.flow.TrafficTreatment;
Kalhee Kim45fede42017-09-05 19:05:06 +0000109import java.nio.ByteBuffer;
Taras Lemkin96a0d342018-03-26 14:52:58 +0000110import java.util.ArrayList;
Yi Tseng483ac6f2017-08-02 15:03:31 -0700111import java.util.Collection;
Taras Lemkin96a0d342018-03-26 14:52:58 +0000112import java.util.Dictionary;
113import java.util.List;
Yi Tseng51301292017-07-28 13:02:59 -0700114import java.util.Optional;
Kalhee Kim45fede42017-09-05 19:05:06 +0000115import java.util.Set;
Charles Chan909cff82018-03-05 13:14:02 -0800116import java.util.concurrent.CopyOnWriteArrayList;
Jordan Halterman6328db72018-04-10 13:34:50 -0400117import java.util.concurrent.Executor;
Yi Tseng525ff402017-10-23 19:39:39 -0700118import java.util.concurrent.atomic.AtomicInteger;
Kalhee Kim45fede42017-09-05 19:05:06 +0000119import static com.google.common.base.Preconditions.checkNotNull;
120import static com.google.common.base.Preconditions.checkState;
Jordan Halterman6328db72018-04-10 13:34:50 -0400121import static java.util.concurrent.Executors.newSingleThreadExecutor;
122import static org.onlab.util.Tools.groupedThreads;
Yi Tseng525ff402017-10-23 19:39:39 -0700123import static org.onosproject.net.flowobjective.Objective.Operation.ADD;
124import static org.onosproject.net.flowobjective.Objective.Operation.REMOVE;
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000125import java.util.concurrent.Semaphore;
Yi Tseng51301292017-07-28 13:02:59 -0700126
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700127@Component(service = { DhcpHandler.class, HostProvider.class })
128//@Property(name = "version", value = "6")
Yi Tsengaa417a62017-09-08 17:22:51 -0700129public class Dhcp6HandlerImpl implements DhcpHandler, HostProvider {
Charles Chand988c282017-09-12 17:09:32 -0700130 public static final String DHCP_V6_RELAY_APP = "org.onosproject.Dhcp6HandlerImpl";
Saurav Das7c6dec12017-09-13 14:35:56 -0700131 public static final ProviderId PROVIDER_ID = new ProviderId("dhcp6", DHCP_V6_RELAY_APP);
Yi Tseng525ff402017-10-23 19:39:39 -0700132 private static final int IGNORE_CONTROL_PRIORITY = PacketPriority.CONTROL.priorityValue() + 1000;
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000133 private String gCount = "global";
Taras Lemkin96a0d342018-03-26 14:52:58 +0000134 private static final String LQ_ROUTE_PROPERTY_NAME = "learnRouteFromLeasequery";
Yi Tseng525ff402017-10-23 19:39:39 -0700135 private static final TrafficSelector CLIENT_SERVER_SELECTOR = DefaultTrafficSelector.builder()
136 .matchEthType(Ethernet.TYPE_IPV6)
137 .matchIPProtocol(IPv6.PROTOCOL_UDP)
138 .matchIPv6Src(IpPrefix.IPV6_LINK_LOCAL_PREFIX)
139 .matchIPv6Dst(Ip6Address.ALL_DHCP_RELAY_AGENTS_AND_SERVERS.toIpPrefix())
140 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_V6_CLIENT_PORT))
141 .matchUdpDst(TpPort.tpPort(UDP.DHCP_V6_SERVER_PORT))
142 .build();
143 private static final TrafficSelector SERVER_RELAY_SELECTOR = DefaultTrafficSelector.builder()
144 .matchEthType(Ethernet.TYPE_IPV6)
145 .matchIPProtocol(IPv6.PROTOCOL_UDP)
146 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_V6_SERVER_PORT))
147 .matchUdpDst(TpPort.tpPort(UDP.DHCP_V6_SERVER_PORT))
148 .build();
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800149 // lease query reply is from server to client (no relay in between) - so we need to
150 // catch that scenario also ..
151 private static final TrafficSelector LEASE_QUERY_RESPONSE_SELECTOR = DefaultTrafficSelector.builder()
152 .matchEthType(Ethernet.TYPE_IPV6)
153 .matchIPProtocol(IPv6.PROTOCOL_UDP)
154 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_V6_SERVER_PORT))
155 .matchUdpDst(TpPort.tpPort(UDP.DHCP_V6_CLIENT_PORT))
156 .build();
Yi Tseng525ff402017-10-23 19:39:39 -0700157 static final Set<TrafficSelector> DHCP_SELECTORS = ImmutableSet.of(
158 CLIENT_SERVER_SELECTOR,
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800159 SERVER_RELAY_SELECTOR,
160 LEASE_QUERY_RESPONSE_SELECTOR
Yi Tseng525ff402017-10-23 19:39:39 -0700161 );
Kalhee Kim45fede42017-09-05 19:05:06 +0000162 private static Logger log = LoggerFactory.getLogger(Dhcp6HandlerImpl.class);
Yi Tseng51301292017-07-28 13:02:59 -0700163
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700164 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Kalhee Kim45fede42017-09-05 19:05:06 +0000165 protected DhcpRelayStore dhcpRelayStore;
Yi Tseng51301292017-07-28 13:02:59 -0700166
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700167 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000168 protected DhcpRelayCountersStore dhcpRelayCountersStore;
169
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700170 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Kalhee Kim45fede42017-09-05 19:05:06 +0000171 protected PacketService packetService;
172
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700173 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Kalhee Kim45fede42017-09-05 19:05:06 +0000174 protected RouteStore routeStore;
175
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700176 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Kalhee Kim45fede42017-09-05 19:05:06 +0000177 protected InterfaceService interfaceService;
178
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700179 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Kalhee Kim45fede42017-09-05 19:05:06 +0000180 protected HostService hostService;
181
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700182 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Yi Tsengaa417a62017-09-08 17:22:51 -0700183 protected HostProviderRegistry providerRegistry;
Kalhee Kim45fede42017-09-05 19:05:06 +0000184
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700185 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Yi Tseng525ff402017-10-23 19:39:39 -0700186 protected CoreService coreService;
187
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700188 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Kalhee Kimba366062017-11-07 16:32:09 +0000189 protected DhcpFpmPrefixStore dhcpFpmPrefixStore;
190
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700191 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Yi Tseng525ff402017-10-23 19:39:39 -0700192 protected DeviceService deviceService;
193
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700194 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Yi Tseng525ff402017-10-23 19:39:39 -0700195 protected FlowObjectiveService flowObjectiveService;
196
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700197 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Taras Lemkin96a0d342018-03-26 14:52:58 +0000198 protected ComponentConfigService cfgService;
199
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700200 //@Property(name = Dhcp6HandlerImpl.LQ_ROUTE_PROPERTY_NAME, boolValue = false,
201 // label = "Enable learning routing information from LQ")
Taras Lemkin96a0d342018-03-26 14:52:58 +0000202 private Boolean learnRouteFromLeasequery = Boolean.TRUE;
203
Yi Tsengaa417a62017-09-08 17:22:51 -0700204 protected HostProviderService providerService;
Yi Tseng525ff402017-10-23 19:39:39 -0700205 protected ApplicationId appId;
Charles Chan70fdd492018-03-07 17:36:06 -0800206 protected Multimap<DeviceId, VlanId> ignoredVlans = Multimaps.synchronizedMultimap(HashMultimap.create());
Yi Tseng525ff402017-10-23 19:39:39 -0700207 private InternalHostListener hostListener = new InternalHostListener();
Kalhee Kimba366062017-11-07 16:32:09 +0000208 private Boolean dhcpFpmEnabled = false;
Charles Chan909cff82018-03-05 13:14:02 -0800209 private List<DhcpServerInfo> defaultServerInfoList = new CopyOnWriteArrayList<>();
210 private List<DhcpServerInfo> indirectServerInfoList = new CopyOnWriteArrayList<>();
Jordan Halterman6328db72018-04-10 13:34:50 -0400211
212 private Executor hostEventExecutor = newSingleThreadExecutor(
213 groupedThreads("dhcp6-event-host", "%d", log));
214
Kalhee Kim495c9b22017-11-07 16:32:09 +0000215 private class IpAddressInfo {
216 Ip6Address ip6Address;
217 long prefTime;
218 }
219 private class PdPrefixInfo {
220 IpPrefix pdPrefix;
221 long prefTime;
222 }
223 protected int dhcp6PollInterval = 24 * 3600; // 24 hr period
224
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000225 // max 1 thread
226 static Semaphore recordSemaphore = new Semaphore(1);
Kalhee Kim45fede42017-09-05 19:05:06 +0000227
228 // CLIENT message types
229 public static final Set<Byte> MSG_TYPE_FROM_CLIENT =
230 ImmutableSet.of(DHCP6.MsgType.SOLICIT.value(),
231 DHCP6.MsgType.REQUEST.value(),
232 DHCP6.MsgType.REBIND.value(),
233 DHCP6.MsgType.RENEW.value(),
234 DHCP6.MsgType.RELEASE.value(),
235 DHCP6.MsgType.DECLINE.value(),
236 DHCP6.MsgType.CONFIRM.value(),
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800237 DHCP6.MsgType.RELAY_FORW.value(),
238 DHCP6.MsgType.LEASEQUERY.value());
Kalhee Kim45fede42017-09-05 19:05:06 +0000239 // SERVER message types
240 public static final Set<Byte> MSG_TYPE_FROM_SERVER =
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800241 ImmutableSet.of(DHCP6.MsgType.RELAY_REPL.value(),
242 DHCP6.MsgType.LEASEQUERY_REPLY.value());
Kalhee Kim45fede42017-09-05 19:05:06 +0000243
244 @Activate
Taras Lemkin96a0d342018-03-26 14:52:58 +0000245 protected void activate(ComponentContext context) {
246 cfgService.registerProperties(getClass());
247 modified(context);
Yi Tseng525ff402017-10-23 19:39:39 -0700248 appId = coreService.registerApplication(DHCP_V6_RELAY_APP);
Yi Tsengaa417a62017-09-08 17:22:51 -0700249 providerService = providerRegistry.register(this);
Kalhee Kim45fede42017-09-05 19:05:06 +0000250 hostService.addListener(hostListener);
Yi Tseng51301292017-07-28 13:02:59 -0700251 }
252
Kalhee Kim45fede42017-09-05 19:05:06 +0000253 @Deactivate
254 protected void deactivate() {
Taras Lemkin96a0d342018-03-26 14:52:58 +0000255 cfgService.unregisterProperties(getClass(), false);
Yi Tsengaa417a62017-09-08 17:22:51 -0700256 providerRegistry.unregister(this);
Kalhee Kim45fede42017-09-05 19:05:06 +0000257 hostService.removeListener(hostListener);
Yi Tseng919b2df2017-09-07 16:22:51 -0700258 defaultServerInfoList.forEach(this::stopMonitoringIps);
Charles Chanfc1c22e2018-07-19 09:52:01 -0700259 defaultServerInfoList.forEach(info -> info.getDhcpServerIp6().ifPresent(this::cancelDhcpPacket));
Yi Tseng919b2df2017-09-07 16:22:51 -0700260 defaultServerInfoList.clear();
261 indirectServerInfoList.forEach(this::stopMonitoringIps);
Charles Chanfc1c22e2018-07-19 09:52:01 -0700262 indirectServerInfoList.forEach(info -> info.getDhcpServerIp6().ifPresent(this::cancelDhcpPacket));
Yi Tseng919b2df2017-09-07 16:22:51 -0700263 indirectServerInfoList.clear();
264 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000265
Taras Lemkin96a0d342018-03-26 14:52:58 +0000266 @Modified
267 protected void modified(ComponentContext context) {
268 Dictionary<?, ?> properties = context.getProperties();
269 Boolean flag;
270 flag = Tools.isPropertyEnabled(properties, Dhcp6HandlerImpl.LQ_ROUTE_PROPERTY_NAME);
271 if (flag != null) {
272 learnRouteFromLeasequery = flag;
273 log.info("Learning routes from DHCP leasequery is {}",
274 learnRouteFromLeasequery ? "enabled" : "disabled");
275 }
276 }
277
Yi Tseng919b2df2017-09-07 16:22:51 -0700278 private void stopMonitoringIps(DhcpServerInfo serverInfo) {
279 serverInfo.getDhcpGatewayIp6().ifPresent(gatewayIp -> {
280 hostService.stopMonitoringIp(gatewayIp);
281 });
282 serverInfo.getDhcpServerIp6().ifPresent(serverIp -> {
283 hostService.stopMonitoringIp(serverIp);
284 });
Yi Tseng51301292017-07-28 13:02:59 -0700285 }
286
Yi Tseng51301292017-07-28 13:02:59 -0700287 @Override
Yi Tseng919b2df2017-09-07 16:22:51 -0700288 public List<DhcpServerInfo> getDefaultDhcpServerInfoList() {
289 return defaultServerInfoList;
Yi Tseng51301292017-07-28 13:02:59 -0700290 }
291
292 @Override
Yi Tseng919b2df2017-09-07 16:22:51 -0700293 public List<DhcpServerInfo> getIndirectDhcpServerInfoList() {
294 return indirectServerInfoList;
Yi Tseng51301292017-07-28 13:02:59 -0700295 }
296
297 @Override
Yi Tseng525ff402017-10-23 19:39:39 -0700298 public void updateIgnoreVlanConfig(IgnoreDhcpConfig config) {
299 if (config == null) {
300 ignoredVlans.forEach(((deviceId, vlanId) -> {
301 processIgnoreVlanRule(deviceId, vlanId, REMOVE);
302 }));
303 return;
304 }
305 config.ignoredVlans().forEach((deviceId, vlanId) -> {
306 if (ignoredVlans.get(deviceId).contains(vlanId)) {
307 // don't need to process if it already ignored
308 return;
309 }
310 processIgnoreVlanRule(deviceId, vlanId, ADD);
311 });
Yi Tseng525ff402017-10-23 19:39:39 -0700312 ignoredVlans.forEach((deviceId, vlanId) -> {
313 if (!config.ignoredVlans().get(deviceId).contains(vlanId)) {
314 // not contains in new config, remove it
315 processIgnoreVlanRule(deviceId, vlanId, REMOVE);
316 }
317 });
318 }
319
320 @Override
Saurav Dasb805f1a2017-12-13 16:19:35 -0800321 public void removeIgnoreVlanState(IgnoreDhcpConfig config) {
322 if (config == null) {
323 ignoredVlans.clear();
324 return;
325 }
326 config.ignoredVlans().forEach((deviceId, vlanId) -> {
327 ignoredVlans.remove(deviceId, vlanId);
328 });
329 }
330
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800331 public DhcpRecord getDhcpRelayRecordFor(Ip6Address clientAddress) {
332
Taras Lemkin96a0d342018-03-26 14:52:58 +0000333 Collection<DhcpRecord> records = dhcpRelayStore.getDhcpRecords();
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800334 DhcpRecord dr = null;
335 for (DhcpRecord e:records) {
336 if (e.ip6Address().isPresent()) {
337 if (e.ip6Address().get().equals(clientAddress)) {
338 dr = e;
339 break;
340 }
341 }
342 }
343 return dr;
344 }
345
346 public MacAddress findNextHopMacForIp6FromRelayStore(Ip6Address clientAddress,
347 MacAddress clientMacAddress, VlanId vlanID) {
348
349 DhcpRecord dr = getDhcpRelayRecordFor(clientAddress);
350
351 if (dr != null) {
352 Optional<MacAddress> nextHopTempMac = dr.nextHopTemp();
353 if (nextHopTempMac.isPresent()) {
354 log.info("findNextHopForIp6FromRelayStore " + clientAddress + " got mac " + nextHopTempMac.toString());
355 return nextHopTempMac.get();
356 }
357 } else {
358 log.warn("findNextHopMacForIp6FromRelayStore could NOT find next hop for " + clientAddress);
359 return null;
360 }
361 return null;
362 }
363
364 public Ip6Address findNextHopIp6FromRelayStore(Ip6Address clientAddress) {
365
366 DhcpRecord dr = getDhcpRelayRecordFor(clientAddress);
367 if (dr != null) {
368 Optional<MacAddress> nextHopMac = dr.nextHop();
369 if (nextHopMac.isPresent()) {
370 // find the local ip6 from the host store
371 HostId gwHostId = HostId.hostId(nextHopMac.get(), dr.vlanId());
372 Host gwHost = hostService.getHost(gwHostId);
373 if (gwHost == null) {
374 log.warn("Can't find next hop host ID {}", gwHostId);
375 return null;
376 }
377 Ip6Address nextHopIp = gwHost.ipAddresses()
378 .stream()
379 .filter(IpAddress::isIp6)
380 .filter(IpAddress::isLinkLocal)
381 .map(IpAddress::getIp6Address)
382 .findFirst()
383 .orElse(null);
384
385 log.info("findNextHopIp6FromRelayStore " + clientAddress + " got mac " +
386 nextHopMac.toString() + " ip6 " + nextHopIp);
387 return nextHopIp;
388 }
389 } else {
390 log.warn("findNextHopIp6FromRelayStore could NOT find next hop for " + clientAddress);
391 return null;
392 }
393 return null;
394 }
395
396 private void setPotentialNextHopForIp6InRelayStore(Ip6Address clientAddress,
397 VlanId vlanId, MacAddress nh) {
398 DhcpRecord dr = getDhcpRelayRecordFor(clientAddress);
399 if (dr != null) {
400 dr.nextHopTemp(nh);
401 log.debug("LQ6 potential NH mac " + nh.toString() + " UPDATED in RelayRecord client " + clientAddress);
402 } else {
403 log.warn("LQ6 potential NH mac" + nh.toString() +
404 " NOT FOUND in RelayRecord for client - LQ rejected" + clientAddress);
405 }
406 }
407
408 public void handleLeaseQuery6ReplyMsg(PacketContext context, DHCP6 dhcp6Payload) {
409 ConnectPoint inPort = context.inPacket().receivedFrom();
410 log.info("Got LQV6-REPLY on port {}", inPort);
411 List<Dhcp6Option> lopt = dhcp6Payload.getOptions();
412 log.info("Options list: {}", lopt);
413 // find out if this lease is known is
414 Dhcp6ClientDataOption clientDataOption = dhcp6Payload.getOptions()
415 .stream()
416 .filter(opt -> opt instanceof Dhcp6ClientDataOption)
417 .map(pld -> (Dhcp6ClientDataOption) pld)
418 .findFirst()
419 .orElse(null);
420
421 if (clientDataOption == null) {
422 log.warn("clientDataOption option is not present, " +
423 "lease is UNKNOWN - not adding any new route...");
424 } else {
425 Dhcp6IaAddressOption aiAddressOption = clientDataOption.getOptions()
426 .stream()
427 .filter(opt -> opt instanceof Dhcp6IaAddressOption)
428 .map(pld -> (Dhcp6IaAddressOption) pld)
429 .findFirst()
430 .orElse(null);
431
432 Dhcp6ClientIdOption clientIdOption = clientDataOption.getOptions()
433 .stream()
434 .filter(opt -> opt instanceof Dhcp6ClientIdOption)
435 .map(pld -> (Dhcp6ClientIdOption) pld)
436 .findFirst()
437 .orElse(null);
438
439 if (aiAddressOption == null) {
440 log.warn("clientDataOption from DHCP server does not " +
441 "contains Dhcp6IaAddressOption for the client - giving up...");
442 } else {
443 Ip6Address clientAddress = aiAddressOption.getIp6Address();
444 MacAddress clientMacAddress = MacAddress.valueOf(clientIdOption.getDuid().getLinkLayerAddress());
445 Ethernet packet = context.inPacket().parsed();
446 VlanId vlanId = VlanId.vlanId(packet.getVlanID());
447 MacAddress potentialNextHopMac =
448 findNextHopMacForIp6FromRelayStore(clientAddress, clientMacAddress, vlanId);
449
450 if (potentialNextHopMac == null) {
451 log.warn("Can't find next hop host mac for client {} mac:{}/{}",
452 clientAddress, clientMacAddress, vlanId);
453 return;
454 } else {
455 log.info("Next hop mac for {}/{}/{} is {}", clientAddress,
456 clientMacAddress, vlanId, potentialNextHopMac.toString());
457 }
458 // search the next hop in the hosts store
459 HostId gwHostId = HostId.hostId(potentialNextHopMac, vlanId);
460 Host gwHost = hostService.getHost(gwHostId);
461 if (gwHost == null) {
462 log.warn("Can't find next hop host ID {}", gwHostId);
463 return;
464 }
465 Ip6Address nextHopIp = gwHost.ipAddresses()
466 .stream()
467 .filter(IpAddress::isIp6)
468 .filter(IpAddress::isLinkLocal)
469 .map(IpAddress::getIp6Address)
470 .findFirst()
471 .orElse(null);
472 if (nextHopIp == null) {
473 log.warn("Can't find IP6 address of next hop {}", gwHost);
474 return;
475 }
476 log.info("client " + clientAddress + " is known !");
Kalhee Kim54a97c92018-04-02 21:23:46 +0000477 Route routeForIP6 = new Route(Route.Source.DHCP, clientAddress.toIpPrefix(), nextHopIp);
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800478 log.debug("updating route of Client for indirectly connected.");
479 log.debug("client ip: " + clientAddress + ", next hop ip6: " + nextHopIp);
480 routeStore.updateRoute(routeForIP6);
481 }
482 }
483 }
484
Saurav Dasb805f1a2017-12-13 16:19:35 -0800485 @Override
Kalhee Kim45fede42017-09-05 19:05:06 +0000486 public void processDhcpPacket(PacketContext context, BasePacket payload) {
487 checkNotNull(payload, "DHCP6 payload can't be null");
488 checkState(payload instanceof DHCP6, "Payload is not a DHCP6");
489 DHCP6 dhcp6Payload = (DHCP6) payload;
490 Ethernet receivedPacket = context.inPacket().parsed();
491
492 if (!configured()) {
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800493 log.warn("Missing DHCP6 relay server config. " +
494 "Abort packet processing dhcp6 payload {}", dhcp6Payload);
Kalhee Kim45fede42017-09-05 19:05:06 +0000495 return;
496 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000497 byte msgTypeVal = dhcp6Payload.getMsgType();
498 MsgType msgType = DHCP6.MsgType.getType(msgTypeVal);
499 log.debug("msgType is {}", msgType);
Kalhee Kim45fede42017-09-05 19:05:06 +0000500
Kalhee Kim45fede42017-09-05 19:05:06 +0000501 ConnectPoint inPort = context.inPacket().receivedFrom();
Jonathan Hart8ca2bc02017-11-30 18:23:42 -0800502
Kalhee Kim495c9b22017-11-07 16:32:09 +0000503 if (inPort == null) {
Taras Lemkin96a0d342018-03-26 14:52:58 +0000504 log.warn("incoming ConnectPoint is null");
Kalhee Kim495c9b22017-11-07 16:32:09 +0000505 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000506 Set<Interface> receivingInterfaces = interfaceService.getInterfacesByPort(inPort);
507 //ignore the packets if dhcp client interface is not configured on onos.
508 if (receivingInterfaces.isEmpty()) {
509 log.warn("Virtual interface is not configured on {}", inPort);
510 return;
511 }
512
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800513 if (msgTypeVal == DHCP6.MsgType.LEASEQUERY.value()) {
Taras Lemkin96a0d342018-03-26 14:52:58 +0000514 List<InternalPacket> ethernetClientPackets =
515 learnRouteFromLeasequery ?
516 processLQ6PacketFromClient(context, receivedPacket, receivingInterfaces, dhcp6Payload) :
517 processDhcp6ForwardOnly(context, receivedPacket, receivingInterfaces, dhcp6Payload);
518 for (InternalPacket internalPacket : ethernetClientPackets) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000519 forwardPacket(internalPacket);
Kalhee Kim45fede42017-09-05 19:05:06 +0000520 }
Taras Lemkin96a0d342018-03-26 14:52:58 +0000521 } else if (msgTypeVal == DHCP6.MsgType.LEASEQUERY_REPLY.value() && learnRouteFromLeasequery) {
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800522 IPv6 clientIpv6 = (IPv6) receivedPacket.getPayload();
523 UDP clientUdp = (UDP) clientIpv6.getPayload();
524 DHCP6 clientDhcp6 = (DHCP6) clientUdp.getPayload();
Taras Lemkin96a0d342018-03-26 14:52:58 +0000525 Interface serverInterface = Dhcp6HandlerUtil.directlyConnected(clientDhcp6) ?
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800526 getServerInterface() : getIndirectServerInterface();
Kalhee Kim45fede42017-09-05 19:05:06 +0000527 InternalPacket ethernetPacketReply =
Taras Lemkin96a0d342018-03-26 14:52:58 +0000528 Dhcp6HandlerUtil.processLQ6PacketFromServer(
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800529 defaultServerInfoList, indirectServerInfoList,
530 serverInterface, interfaceService,
531 hostService,
532 context, receivedPacket, receivingInterfaces);
Kalhee Kim45fede42017-09-05 19:05:06 +0000533 if (ethernetPacketReply != null) {
534 forwardPacket(ethernetPacketReply);
535 }
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800536 handleLeaseQuery6ReplyMsg(context, dhcp6Payload);
Taras Lemkin96a0d342018-03-26 14:52:58 +0000537 } else if (MSG_TYPE_FROM_CLIENT.contains(msgTypeVal)) {
538 List<InternalPacket> ethernetClientPacket =
539 processDhcp6PacketFromClient(context, receivedPacket, receivingInterfaces);
540 for (InternalPacket internalPacket : ethernetClientPacket) {
541 forwardPacket(internalPacket);
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800542 }
Taras Lemkin96a0d342018-03-26 14:52:58 +0000543 } else if (MSG_TYPE_FROM_SERVER.contains(msgTypeVal)) {
544 log.debug("calling processDhcp6PacketFromServer with RELAY_REPL {}, {}", receivedPacket, dhcp6Payload);
545 InternalPacket ethernetPacketReply =
546 processDhcp6PacketFromServer(context, receivedPacket, receivingInterfaces);
547 if (ethernetPacketReply != null) {
548 forwardPacket(ethernetPacketReply);
549 }
550 } else {
551 log.warn("Not so fast, packet type {} not supported yet", msgTypeVal);
Kalhee Kim45fede42017-09-05 19:05:06 +0000552 }
553 }
554
Kalhee Kim45fede42017-09-05 19:05:06 +0000555 /**
556 * Checks if this app has been configured.
557 *
558 * @return true if all information we need have been initialized
559 */
560 public boolean configured() {
Yi Tseng919b2df2017-09-07 16:22:51 -0700561 return !defaultServerInfoList.isEmpty();
Kalhee Kim45fede42017-09-05 19:05:06 +0000562 }
563
Yi Tsengaa417a62017-09-08 17:22:51 -0700564 @Override
565 public ProviderId id() {
Charles Chand988c282017-09-12 17:09:32 -0700566 return PROVIDER_ID;
Yi Tsengaa417a62017-09-08 17:22:51 -0700567 }
568
569 @Override
570 public void triggerProbe(Host host) {
571 // Do nothing here
572 }
573
Kalhee Kim45fede42017-09-05 19:05:06 +0000574 //forward the packet to ConnectPoint where the DHCP server is attached.
575 private void forwardPacket(InternalPacket packet) {
576 //send Packetout to dhcp server connectpoint.
Taras Lemkin96a0d342018-03-26 14:52:58 +0000577 if (packet.getDestLocation() != null) {
Kalhee Kim45fede42017-09-05 19:05:06 +0000578 TrafficTreatment t = DefaultTrafficTreatment.builder()
Taras Lemkin96a0d342018-03-26 14:52:58 +0000579 .setOutput(packet.getDestLocation().port()).build();
Kalhee Kim45fede42017-09-05 19:05:06 +0000580 OutboundPacket o = new DefaultOutboundPacket(
Taras Lemkin96a0d342018-03-26 14:52:58 +0000581 packet.getDestLocation().deviceId(), t, ByteBuffer.wrap(packet.getPacket().serialize()));
Kalhee Kim45fede42017-09-05 19:05:06 +0000582 packetService.emit(o);
Taras Lemkin96a0d342018-03-26 14:52:58 +0000583 if (log.isTraceEnabled()) {
584 IPv6 ip6 = (IPv6) packet.getPacket().getPayload();
585 UDP udp = (UDP) ip6.getPayload();
586 DHCP6 dhcp6 = (DHCP6) udp.getPayload();
587 log.trace("Relaying packet to destination {} eth: {} dhcp: {}",
588 packet.getDestLocation(), packet.getPacket(), dhcp6);
589 }
590
591 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000592 }
593
Kalhee Kim45fede42017-09-05 19:05:06 +0000594 /**
595 * extract from dhcp6 packet client ipv6 address of given by dhcp server.
596 *
597 * @param dhcp6 the dhcp6 packet
Kalhee Kim495c9b22017-11-07 16:32:09 +0000598 * @return IpAddressInfo IpAddressInfo given by dhcp server, or null if not exists
Kalhee Kim45fede42017-09-05 19:05:06 +0000599 */
Kalhee Kim495c9b22017-11-07 16:32:09 +0000600 private IpAddressInfo extractIpAddress(DHCP6 dhcp6) {
601 IpAddressInfo ipInfo = new IpAddressInfo();
Kalhee Kim45fede42017-09-05 19:05:06 +0000602
603 log.debug("extractIpAddress enters dhcp6 {}.", dhcp6);
604 // Extract IPv6 address from IA NA ot IA TA option
605 Optional<Dhcp6IaNaOption> iaNaOption = dhcp6.getOptions()
606 .stream()
607 .filter(opt -> opt instanceof Dhcp6IaNaOption)
608 .map(opt -> (Dhcp6IaNaOption) opt)
609 .findFirst();
610 Optional<Dhcp6IaTaOption> iaTaOption = dhcp6.getOptions()
611 .stream()
612 .filter(opt -> opt instanceof Dhcp6IaTaOption)
613 .map(opt -> (Dhcp6IaTaOption) opt)
614 .findFirst();
615 Optional<Dhcp6IaAddressOption> iaAddressOption;
616 if (iaNaOption.isPresent()) {
617 log.debug("Found IPv6 address from iaNaOption {}", iaNaOption);
618
619 iaAddressOption = iaNaOption.get().getOptions().stream()
620 .filter(opt -> opt instanceof Dhcp6IaAddressOption)
621 .map(opt -> (Dhcp6IaAddressOption) opt)
622 .findFirst();
623 } else if (iaTaOption.isPresent()) {
624 log.debug("Found IPv6 address from iaTaOption {}", iaTaOption);
625
626 iaAddressOption = iaTaOption.get().getOptions().stream()
627 .filter(opt -> opt instanceof Dhcp6IaAddressOption)
628 .map(opt -> (Dhcp6IaAddressOption) opt)
629 .findFirst();
630 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000631 log.info("No IPv6 address found from iaTaOption {}", iaTaOption);
Kalhee Kim45fede42017-09-05 19:05:06 +0000632 iaAddressOption = Optional.empty();
633 }
634 if (iaAddressOption.isPresent()) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000635 ipInfo.ip6Address = iaAddressOption.get().getIp6Address();
636 ipInfo.prefTime = iaAddressOption.get().getPreferredLifetime();
Kalhee Kim45fede42017-09-05 19:05:06 +0000637 log.debug("Found IPv6 address from iaAddressOption {}", iaAddressOption);
Kalhee Kim45fede42017-09-05 19:05:06 +0000638 } else {
639 log.debug("Can't find IPv6 address from DHCPv6 {}", dhcp6);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000640 return null;
Kalhee Kim45fede42017-09-05 19:05:06 +0000641 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000642 return ipInfo;
Kalhee Kim45fede42017-09-05 19:05:06 +0000643 }
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800644
Kalhee Kim45fede42017-09-05 19:05:06 +0000645 /**
646 * extract from dhcp6 packet Prefix prefix provided by dhcp server.
647 *
648 * @param dhcp6 the dhcp6 payload
649 * @return IpPrefix Prefix Delegation prefix, or null if not exists.
650 */
Kalhee Kim495c9b22017-11-07 16:32:09 +0000651 private PdPrefixInfo extractPrefix(DHCP6 dhcp6) {
652 log.debug("extractPrefix enters {}", dhcp6);
Kalhee Kim45fede42017-09-05 19:05:06 +0000653
654 // extract prefix
Kalhee Kim495c9b22017-11-07 16:32:09 +0000655 PdPrefixInfo pdPrefixInfo = new PdPrefixInfo();
Kalhee Kim45fede42017-09-05 19:05:06 +0000656
657 Ip6Address prefixAddress = null;
658
659 // Extract IPv6 prefix from IA PD option
660 Optional<Dhcp6IaPdOption> iaPdOption = dhcp6.getOptions()
661 .stream()
662 .filter(opt -> opt instanceof Dhcp6IaPdOption)
663 .map(opt -> (Dhcp6IaPdOption) opt)
664 .findFirst();
665
666 Optional<Dhcp6IaPrefixOption> iaPrefixOption;
667 if (iaPdOption.isPresent()) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000668 log.debug("IA_PD option found {}", iaPdOption);
Kalhee Kim45fede42017-09-05 19:05:06 +0000669
670 iaPrefixOption = iaPdOption.get().getOptions().stream()
671 .filter(opt -> opt instanceof Dhcp6IaPrefixOption)
672 .map(opt -> (Dhcp6IaPrefixOption) opt)
673 .findFirst();
674 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000675 log.debug("IA_PD option NOT found");
Kalhee Kim45fede42017-09-05 19:05:06 +0000676
677 iaPrefixOption = Optional.empty();
678 }
679 if (iaPrefixOption.isPresent()) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000680 log.debug("IAPrefix Option within IA_PD option found {}", iaPrefixOption);
Kalhee Kim45fede42017-09-05 19:05:06 +0000681
682 prefixAddress = iaPrefixOption.get().getIp6Prefix();
683 int prefixLen = (int) iaPrefixOption.get().getPrefixLength();
Kalhee Kim495c9b22017-11-07 16:32:09 +0000684 log.debug("Prefix length is {} bits", prefixLen);
685 pdPrefixInfo.pdPrefix = IpPrefix.valueOf(prefixAddress, prefixLen);
686 pdPrefixInfo.prefTime = iaPrefixOption.get().getPreferredLifetime();
Kalhee Kim45fede42017-09-05 19:05:06 +0000687 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000688 log.debug("Can't find IPv6 prefix from DHCPv6 {}", dhcp6);
689 return null;
Kalhee Kim45fede42017-09-05 19:05:06 +0000690 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000691 return pdPrefixInfo;
Kalhee Kim45fede42017-09-05 19:05:06 +0000692 }
693
694 /**
Kalhee Kim495c9b22017-11-07 16:32:09 +0000695 * remove host or route and update dhcp relay record attributes.
Kalhee Kim45fede42017-09-05 19:05:06 +0000696 *
697 * @param directConnFlag flag to show that packet is from directly connected client
Kalhee Kim495c9b22017-11-07 16:32:09 +0000698 * @param location client side connect point
Kalhee Kim45fede42017-09-05 19:05:06 +0000699 * @param dhcp6Packet the dhcp6 payload
700 * @param clientPacket client's ethernet packet
701 * @param clientIpv6 client's Ipv6 packet
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +0000702 * @param clientInterface client interfaces
Kalhee Kim45fede42017-09-05 19:05:06 +0000703 */
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000704 private void removeHostOrRoute(boolean directConnFlag, ConnectPoint location,
705 DHCP6 dhcp6Packet,
Kalhee Kim45fede42017-09-05 19:05:06 +0000706 Ethernet clientPacket, IPv6 clientIpv6,
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +0000707 Interface clientInterface) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000708 log.debug("removeHostOrRoute enters {}", dhcp6Packet);
Kalhee Kim121ba922017-11-01 17:56:44 +0000709 VlanId vlanId = clientInterface.vlan();
Kalhee Kim495c9b22017-11-07 16:32:09 +0000710 MacAddress srcMac = clientPacket.getSourceMAC(); // could be gw or host
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000711 MacAddress leafClientMac;
712 byte leafMsgType;
Kalhee Kim495c9b22017-11-07 16:32:09 +0000713 log.debug("client mac {} client vlan {}", HexString.toHexString(srcMac.toBytes(), ":"), vlanId);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000714
Charles Chan24a96ff2018-08-13 10:32:03 -0700715 Dhcp6ClientIdOption clientIdOption = Dhcp6HandlerUtil.extractClientId(directConnFlag, dhcp6Packet);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000716 if (clientIdOption != null) {
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000717 if ((clientIdOption.getDuid().getDuidType() == Dhcp6Duid.DuidType.DUID_LLT) ||
718 (clientIdOption.getDuid().getDuidType() == Dhcp6Duid.DuidType.DUID_LL)) {
719 leafClientMac = MacAddress.valueOf(clientIdOption.getDuid().getLinkLayerAddress());
720 } else {
721 log.warn("Link-Layer Address not supported in CLIENTID option. No DhcpRelay Record created.");
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000722 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_LINKLOCAL_FAIL);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000723 return;
724 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000725 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000726 log.warn("CLIENTID option NOT found. Don't create DhcpRelay Record.");
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000727 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_CLIENTID_FAIL);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000728 return;
729 }
730
Kalhee Kim495c9b22017-11-07 16:32:09 +0000731 HostId leafHostId = HostId.hostId(leafClientMac, vlanId);
732 DhcpRecord record = dhcpRelayStore.getDhcpRecord(leafHostId).orElse(null);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000733 if (record == null) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000734 record = new DhcpRecord(leafHostId);
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000735 } else {
736 record = record.clone();
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000737 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000738
Taras Lemkin96a0d342018-03-26 14:52:58 +0000739 Boolean isMsgRelease = Dhcp6HandlerUtil.isDhcp6Release(dhcp6Packet);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000740 IpAddressInfo ipInfo;
741 PdPrefixInfo pdInfo = null;
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000742 if (directConnFlag) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000743 // Add to host store if it is connected to network directly
744 ipInfo = extractIpAddress(dhcp6Packet);
745 if (ipInfo != null) {
746 if (isMsgRelease) {
747 HostId hostId = HostId.hostId(srcMac, vlanId);
748 log.debug("remove Host {} ip for directly connected.", hostId.toString());
749 providerService.removeIpFromHost(hostId, ipInfo.ip6Address);
Kalhee Kim45fede42017-09-05 19:05:06 +0000750 }
751 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000752 log.debug("ipAddress not found. Do not remove Host {} for directly connected.",
753 HostId.hostId(srcMac, vlanId).toString());
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000754 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000755 leafMsgType = dhcp6Packet.getMsgType();
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000756 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000757 // Remove from route store if it is not connected to network directly
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000758 // pick out the first link-local ip address
Kalhee Kim495c9b22017-11-07 16:32:09 +0000759 IpAddress nextHopIp = getFirstIpByHost(directConnFlag, srcMac, vlanId);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000760 if (nextHopIp == null) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000761 log.warn("Can't find link-local IP address of gateway mac {} vlanId {}", srcMac, vlanId);
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000762 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_LINKLOCAL_GW);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000763 return;
764 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000765
Taras Lemkin96a0d342018-03-26 14:52:58 +0000766 DHCP6 leafDhcp = Dhcp6HandlerUtil.getDhcp6Leaf(dhcp6Packet);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000767 ipInfo = extractIpAddress(leafDhcp);
768 if (ipInfo == null) {
769 log.debug("ip is null");
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000770 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000771 if (isMsgRelease) {
Kalhee Kim54a97c92018-04-02 21:23:46 +0000772 Route routeForIP = new Route(Route.Source.DHCP, ipInfo.ip6Address.toIpPrefix(), nextHopIp);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000773 log.debug("removing route of 128 address for indirectly connected.");
774 log.debug("128 ip {}, nexthop {}",
775 HexString.toHexString(ipInfo.ip6Address.toOctets(), ":"),
776 HexString.toHexString(nextHopIp.toOctets(), ":"));
777 routeStore.removeRoute(routeForIP);
Kalhee Kim45fede42017-09-05 19:05:06 +0000778 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000779 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000780
Kalhee Kim495c9b22017-11-07 16:32:09 +0000781 pdInfo = extractPrefix(leafDhcp);
782 if (pdInfo == null) {
783 log.debug("ipPrefix is null ");
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000784 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000785 if (isMsgRelease) {
Kalhee Kim54a97c92018-04-02 21:23:46 +0000786 Route routeForPrefix = new Route(Route.Source.DHCP, pdInfo.pdPrefix, nextHopIp);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000787 log.debug("removing route of PD for indirectly connected.");
788 log.debug("pd ip {}, nexthop {}",
789 HexString.toHexString(pdInfo.pdPrefix.address().toOctets(), ":"),
790 HexString.toHexString(nextHopIp.toOctets(), ":"));
791
792 routeStore.removeRoute(routeForPrefix);
Kalhee Kimba366062017-11-07 16:32:09 +0000793 if (this.dhcpFpmEnabled) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000794 dhcpFpmPrefixStore.removeFpmRecord(pdInfo.pdPrefix);
Kalhee Kimba366062017-11-07 16:32:09 +0000795 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000796 }
797 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000798 leafMsgType = leafDhcp.getMsgType();
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000799 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000800
Kalhee Kim495c9b22017-11-07 16:32:09 +0000801 if (isMsgRelease) {
802 log.debug("DHCP6 RELEASE msg.");
803 if (record != null) {
804 if (ipInfo != null) {
805 log.debug("DhcpRelay Record ip6Address is set to null.");
806 record.ip6Address(null);
807 }
808 if (pdInfo != null) {
809 log.debug("DhcpRelay Record pdPrefix is set to null.");
810 }
811
812 if (!record.ip6Address().isPresent() && !record.pdPrefix().isPresent()) {
813 log.warn("IP6 address and IP6 PD both are null. Remove record.");
814 // do not remove a record. Let timer task handler it.
815 //dhcpRelayStore.removeDhcpRecord(HostId.hostId(leafClientMac, vlanId));
816 }
817 }
818 }
819
Ray Milkeyffe1a332018-01-24 10:41:14 -0800820 if (record != null) {
Taras Lemkin96a0d342018-03-26 14:52:58 +0000821 record.getV6Counters().incrementCounter(Dhcp6HandlerUtil.getMsgTypeStr(leafMsgType));
Ray Milkeyffe1a332018-01-24 10:41:14 -0800822 record.addLocation(new HostLocation(location, System.currentTimeMillis()));
823 record.ip6Status(DHCP6.MsgType.getType(leafMsgType));
824 record.setDirectlyConnected(directConnFlag);
825 if (!directConnFlag) {
826 // Update gateway mac address if the host is not directly connected
827 record.nextHop(srcMac);
828 }
829 record.updateLastSeen();
Kalhee Kim495c9b22017-11-07 16:32:09 +0000830 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000831 dhcpRelayStore.updateDhcpRecord(leafHostId, record);
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000832 // TODO Use AtomicInteger for the counters
833 try {
834 recordSemaphore.acquire();
835 try {
Taras Lemkin96a0d342018-03-26 14:52:58 +0000836 dhcpRelayCountersStore.incrementCounter(gCount, Dhcp6HandlerUtil.getMsgTypeStr(leafMsgType));
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000837 } finally {
838 // calling release() after a successful acquire()
839 recordSemaphore.release();
840 }
841 } catch (InterruptedException e) {
Ray Milkeyba547f92018-02-01 15:22:31 -0800842 Thread.currentThread().interrupt();
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000843 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000844 }
845
846 /**
847 * add host or route and update dhcp relay record.
848 *
849 * @param directConnFlag flag to show that packet is from directly connected client
850 * @param location client side connect point
851 * @param dhcp6Relay the dhcp6 payload
852 * @param embeddedDhcp6 the dhcp6 payload within relay
853 * @param srcMac client gw/host macAddress
854 * @param clientInterface client interface
855 */
856 private void addHostOrRoute(boolean directConnFlag, ConnectPoint location, DHCP6 dhcp6Relay,
857 DHCP6 embeddedDhcp6, MacAddress srcMac, Interface clientInterface) {
858 log.debug("addHostOrRoute entered.");
859 VlanId vlanId = clientInterface.vlan();
Taras Lemkin96a0d342018-03-26 14:52:58 +0000860 Boolean isMsgReply = Dhcp6HandlerUtil.isDhcp6Reply(dhcp6Relay);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000861 MacAddress leafClientMac;
862 Byte leafMsgType;
863
Charles Chan24a96ff2018-08-13 10:32:03 -0700864 Dhcp6ClientIdOption clientIdOption = Dhcp6HandlerUtil.extractClientId(directConnFlag, embeddedDhcp6);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000865 if (clientIdOption != null) {
866 log.debug("CLIENTID option found {}", clientIdOption);
867 if ((clientIdOption.getDuid().getDuidType() == Dhcp6Duid.DuidType.DUID_LLT) ||
868 (clientIdOption.getDuid().getDuidType() == Dhcp6Duid.DuidType.DUID_LL)) {
869 leafClientMac = MacAddress.valueOf(clientIdOption.getDuid().getLinkLayerAddress());
870 } else {
871 log.warn("Link-Layer Address not supported in CLIENTID option. No DhcpRelay Record created.");
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000872 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_LINKLOCAL_FAIL);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000873 return;
874 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000875 } else {
876 log.warn("CLIENTID option NOT found. No DhcpRelay Record created.");
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000877 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_CLIENTID_FAIL);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000878 return;
879 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000880 HostId leafHostId = HostId.hostId(leafClientMac, vlanId);
881 DhcpRecord record = dhcpRelayStore.getDhcpRecord(leafHostId).orElse(null);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000882 if (record == null) {
883 record = new DhcpRecord(HostId.hostId(leafClientMac, vlanId));
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000884 } else {
885 record = record.clone();
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000886 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000887
888 IpAddressInfo ipInfo;
889 PdPrefixInfo pdInfo = null;
890 if (directConnFlag) {
891 // Add to host store if it connect to network directly
892 ipInfo = extractIpAddress(embeddedDhcp6);
893 if (ipInfo != null) {
894 if (isMsgReply) {
895 Set<IpAddress> ips = Sets.newHashSet(ipInfo.ip6Address);
896 HostId hostId = HostId.hostId(srcMac, vlanId);
897 Host host = hostService.getHost(hostId);
898 HostLocation hostLocation = new HostLocation(clientInterface.connectPoint(),
899 System.currentTimeMillis());
900 Set<HostLocation> hostLocations = Sets.newHashSet(hostLocation);
901 if (host != null) {
902 // Dual homing support:
903 // if host exists, use old locations and new location
904 hostLocations.addAll(host.locations());
905 }
906 HostDescription desc = new DefaultHostDescription(srcMac, vlanId, hostLocations, ips,
907 false);
908 log.debug("adding Host for directly connected.");
909 log.debug("client mac {} client vlan {} hostlocation {}",
910 HexString.toHexString(srcMac.toBytes(), ":"), vlanId, hostLocation.toString());
911 // Replace the ip when dhcp server give the host new ip address
912 providerService.hostDetected(hostId, desc, false);
913 }
914 } else {
915 log.warn("ipAddress not found. Do not add Host {} for directly connected.",
916 HostId.hostId(srcMac, vlanId).toString());
917 }
918 leafMsgType = embeddedDhcp6.getMsgType();
919 } else {
920 // Add to route store if it does not connect to network directly
921 // pick out the first link-local ip address
922 IpAddress nextHopIp = getFirstIpByHost(directConnFlag, srcMac, vlanId);
923 if (nextHopIp == null) {
924 log.warn("Can't find link-local IP address of gateway mac {} vlanId {}", srcMac, vlanId);
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000925 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_LINKLOCAL_GW);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000926 return;
927 }
928
Taras Lemkin96a0d342018-03-26 14:52:58 +0000929 DHCP6 leafDhcp = Dhcp6HandlerUtil.getDhcp6Leaf(embeddedDhcp6);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000930 ipInfo = extractIpAddress(leafDhcp);
931 if (ipInfo == null) {
932 log.debug("ip is null");
933 } else {
934 if (isMsgReply) {
Kalhee Kim54a97c92018-04-02 21:23:46 +0000935 Route routeForIP = new Route(Route.Source.DHCP, ipInfo.ip6Address.toIpPrefix(), nextHopIp);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000936 log.debug("adding Route of 128 address for indirectly connected.");
Daniel Ginsburg83b76452018-06-09 01:43:59 +0300937 routeStore.replaceRoute(routeForIP);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000938 }
939 }
940
941 pdInfo = extractPrefix(leafDhcp);
942 if (pdInfo == null) {
943 log.debug("ipPrefix is null ");
944 } else {
945 if (isMsgReply) {
Kalhee Kim54a97c92018-04-02 21:23:46 +0000946 Route routeForPrefix = new Route(Route.Source.DHCP, pdInfo.pdPrefix, nextHopIp);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000947 log.debug("adding Route of PD for indirectly connected.");
Daniel Ginsburg83b76452018-06-09 01:43:59 +0300948 routeStore.replaceRoute(routeForPrefix);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000949 if (this.dhcpFpmEnabled) {
950 FpmRecord fpmRecord = new FpmRecord(pdInfo.pdPrefix, nextHopIp, FpmRecord.Type.DHCP_RELAY);
951 dhcpFpmPrefixStore.addFpmRecord(pdInfo.pdPrefix, fpmRecord);
952 }
953 }
954 }
955 leafMsgType = leafDhcp.getMsgType();
956 }
957 if (leafMsgType == DHCP6.MsgType.RELEASE.value() ||
958 (leafMsgType == DHCP6.MsgType.REPLY.value()) && ipInfo == null) {
959 log.warn("DHCP6 RELEASE/REPLY(null ip) from Server. MsgType {}", leafMsgType);
960 //return;
961 }
962
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000963 record.addLocation(new HostLocation(location, System.currentTimeMillis()));
Kalhee Kim2f07c602017-11-15 18:57:53 +0000964
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000965 if (leafMsgType == DHCP6.MsgType.REPLY.value()) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000966 if (ipInfo != null) {
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000967 log.debug("IP6 address is being stored into dhcp-relay store.");
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800968 log.debug("Client IP6 address {}", HexString.toHexString(ipInfo.ip6Address.toOctets(), ":"));
Kalhee Kim495c9b22017-11-07 16:32:09 +0000969 record.ip6Address(ipInfo.ip6Address);
Kalhee Kim2f07c602017-11-15 18:57:53 +0000970 record.updateAddrPrefTime(ipInfo.prefTime);
971 record.updateLastIp6Update();
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800972 } else {
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000973 log.debug("IP6 address is not returned from server. Maybe only PD is returned.");
974 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000975 if (pdInfo != null) {
976 log.debug("IP6 PD address {}",
977 HexString.toHexString(pdInfo.pdPrefix.address().toOctets(), ":"));
Kalhee Kim2f07c602017-11-15 18:57:53 +0000978 record.pdPrefix(pdInfo.pdPrefix);
979 record.updatePdPrefTime(pdInfo.prefTime);
980 record.updateLastPdUpdate();
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000981 } else {
982 log.debug("IP6 PD address is not returned from server. Maybe only IPAddress is returned.");
983 }
984 }
Kalhee Kim2f07c602017-11-15 18:57:53 +0000985
Taras Lemkin96a0d342018-03-26 14:52:58 +0000986 record.getV6Counters().incrementCounter(Dhcp6HandlerUtil.getMsgTypeStr(leafMsgType));
Kalhee Kim495c9b22017-11-07 16:32:09 +0000987 record.ip6Status(DHCP6.MsgType.getType(leafMsgType));
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000988 record.setDirectlyConnected(directConnFlag);
989 record.updateLastSeen();
Kalhee Kim495c9b22017-11-07 16:32:09 +0000990 dhcpRelayStore.updateDhcpRecord(leafHostId, record);
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000991 // TODO Use AtomicInteger for the counters
992 try {
993 recordSemaphore.acquire();
994 try {
Taras Lemkin96a0d342018-03-26 14:52:58 +0000995 dhcpRelayCountersStore.incrementCounter(gCount, Dhcp6HandlerUtil.getMsgTypeStr(leafMsgType));
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000996 } finally {
997 // calling release() after a successful acquire()
998 recordSemaphore.release();
999 }
1000 } catch (InterruptedException e) {
Ray Milkeyba547f92018-02-01 15:22:31 -08001001 Thread.currentThread().interrupt();
Kalhee Kimd94ceea2017-11-29 19:03:02 +00001002 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001003 }
1004
Taras Lemkin96a0d342018-03-26 14:52:58 +00001005 private List<InternalPacket> processDhcp6ForwardOnly(PacketContext context,
1006 Ethernet clientPacket,
1007 Set<Interface> clientInterfaces,
1008 DHCP6 dhcpPacket) {
1009 ConnectPoint inPort = context.inPacket().receivedFrom();
1010 log.trace("Got DHCPv6 on port {}", inPort);
1011 boolean directConnFlag = Dhcp6HandlerUtil.directlyConnected(dhcpPacket);
1012
1013 List<InternalPacket> internalPackets = new ArrayList<>();
1014 List<DhcpServerInfo> serverInfoList = findValidServerInfo(directConnFlag);
1015
1016 for (DhcpServerInfo dhcpServer : serverInfoList) {
Charles Chan24a96ff2018-08-13 10:32:03 -07001017 Interface serverInterface = getServerInterface(dhcpServer);
1018 if (serverInterface == null) {
1019 log.warn("Can't get server interface, ignore");
1020 continue;
1021 }
1022
Taras Lemkin96a0d342018-03-26 14:52:58 +00001023 Ethernet newPacket = Dhcp6HandlerUtil.buildDhcp6PacketFromClient(context,
Charles Chan24a96ff2018-08-13 10:32:03 -07001024 clientPacket, clientInterfaces, dhcpServer, serverInterface);
Taras Lemkin96a0d342018-03-26 14:52:58 +00001025 log.trace("Built packet for server {} : {}", dhcpServer, newPacket);
1026 internalPackets.add(InternalPacket.internalPacket(newPacket,
1027 dhcpServer.getDhcpServerConnectPoint().get()));
1028 }
1029
1030 return internalPackets;
1031 }
1032
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001033 private List<InternalPacket> processLQ6PacketFromClient(PacketContext context,
1034 Ethernet clientPacket,
1035 Set<Interface> clientInterfaces,
1036 DHCP6 dhcp6Payload) {
1037 ConnectPoint inPort = context.inPacket().receivedFrom();
1038 log.info("Got LQ-REQUEST V6 on port {}", inPort);
1039 List<Dhcp6Option> lopt = dhcp6Payload.getOptions();
1040 log.info("Options list: {}", lopt);
1041 Dhcp6LeaseQueryOption lqoption = dhcp6Payload.getOptions()
1042 .stream()
1043 .filter(opt -> opt instanceof Dhcp6LeaseQueryOption)
1044 .map(pld -> (Dhcp6LeaseQueryOption) pld)
1045 .findFirst()
1046 .orElse(null);
1047
1048 if (lqoption == null) {
1049 // Can't find dhcp payload
1050 log.warn("Can't find dhcp6 lease query message - aborting");
1051 return null;
1052 } else {
1053 log.info("dhcp6 lqv6 options found: {}", lqoption);
1054 }
1055 log.warn("LQv6 for " + lqoption.linkAddress.toString() + " comes from " + inPort.toString());
1056 Ethernet packet = context.inPacket().parsed();
1057 Ip6Address clientAddress = lqoption.linkAddress;
1058 IPv6 ipv6Packet = (IPv6) packet.getPayload();
1059 Ip6Address nextHopIp = findNextHopIp6FromRelayStore(clientAddress);
1060
1061 // 1. only if there is a route to remove - remove it
1062 if (nextHopIp != null) {
Kalhee Kim54a97c92018-04-02 21:23:46 +00001063 Route routeForIP6 = new Route(Route.Source.DHCP, clientAddress.toIpPrefix(), nextHopIp);
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001064 log.debug("Removing route of Client " + clientAddress +
1065 " for indirectly connected - next hop ip6 " + nextHopIp);
1066 routeStore.removeRoute(routeForIP6);
1067 }
1068
1069 // 2. note the potential NH this packet came from in case it's a known lease
1070 // this NH will then be used to build the route
1071 MacAddress potentialNH = packet.getSourceMAC();
1072 VlanId vlanId = VlanId.vlanId(packet.getVlanID());
1073 setPotentialNextHopForIp6InRelayStore(clientAddress, vlanId, potentialNH);
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001074 // 3. route this LQ6 to all relevant servers
1075 IPv6 clientIpv6 = (IPv6) clientPacket.getPayload();
1076 UDP clientUdp = (UDP) clientIpv6.getPayload();
1077 DHCP6 clientDhcp6 = (DHCP6) clientUdp.getPayload();
1078
Taras Lemkin96a0d342018-03-26 14:52:58 +00001079 boolean directConnFlag = Dhcp6HandlerUtil.directlyConnected(clientDhcp6);
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001080 List<InternalPacket> internalPackets = new ArrayList<>();
1081 List<DhcpServerInfo> serverInfoList = findValidServerInfo(directConnFlag);
1082 List<DhcpServerInfo> copyServerInfoList = new ArrayList<DhcpServerInfo>(serverInfoList);
1083
1084 for (DhcpServerInfo serverInfo : copyServerInfoList) {
Taras Lemkin96a0d342018-03-26 14:52:58 +00001085 if (!Dhcp6HandlerUtil.checkDhcpServerConnPt(directConnFlag, serverInfo)) {
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001086 log.warn("Can't get server connect point, ignore");
1087 continue;
1088 }
1089 DhcpServerInfo newServerInfo = getHostInfoForServerInfo(serverInfo, serverInfoList);
1090 if (newServerInfo == null) {
1091 log.warn("Can't get server interface with host info resolved, ignore");
1092 continue;
1093 }
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001094 Interface serverInterface = getServerInterface(newServerInfo);
1095 if (serverInterface == null) {
1096 log.warn("Can't get server interface, ignore");
1097 continue;
1098 }
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001099 Ethernet etherRouted = (Ethernet) clientPacket.clone();
1100 MacAddress macFacingServer = serverInterface.mac();
1101 if (macFacingServer == null) {
1102 log.warn("No MAC address for server Interface {}", serverInterface);
1103 return null;
1104 }
1105 etherRouted.setSourceMACAddress(macFacingServer);
1106 etherRouted.setDestinationMACAddress(newServerInfo.getDhcpConnectMac().get());
1107 InternalPacket internalPacket =
Taras Lemkin96a0d342018-03-26 14:52:58 +00001108 InternalPacket.internalPacket(etherRouted,
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001109 serverInfo.getDhcpServerConnectPoint().get());
1110 internalPackets.add(internalPacket);
1111 log.debug("Sending LQ to DHCP server {}", newServerInfo.getDhcpServerIp6());
1112 }
1113 log.debug("num of client packets to send is{}", internalPackets.size());
1114
1115 return internalPackets;
1116 }
1117
Kalhee Kim45fede42017-09-05 19:05:06 +00001118 /**
Kalhee Kim495c9b22017-11-07 16:32:09 +00001119 * build the DHCP6 solicit/request packet with gatewayip.
Kalhee Kim45fede42017-09-05 19:05:06 +00001120 *
1121 * @param context packet context
1122 * @param clientPacket client ethernet packet
1123 * @param clientInterfaces set of client side interfaces
1124 */
Kalhee Kim495c9b22017-11-07 16:32:09 +00001125 private List<InternalPacket> processDhcp6PacketFromClient(PacketContext context,
1126 Ethernet clientPacket,
1127 Set<Interface> clientInterfaces) {
Yi Tseng3bd57ac2017-11-29 14:39:18 -08001128 ConnectPoint receivedFrom = context.inPacket().receivedFrom();
Taras Lemkin96a0d342018-03-26 14:52:58 +00001129 Ip6Address relayAgentIp = Dhcp6HandlerUtil.getRelayAgentIPv6Address(clientInterfaces);
Yi Tseng3bd57ac2017-11-29 14:39:18 -08001130 MacAddress relayAgentMac = clientInterfaces.iterator().next().mac();
1131 if (relayAgentIp == null || relayAgentMac == null) {
1132 log.warn("Missing DHCP relay agent interface Ipv6 addr config for "
Kalhee Kim495c9b22017-11-07 16:32:09 +00001133 + "packet from client on port: {}. Aborting packet processing",
1134 clientInterfaces.iterator().next().connectPoint());
Kalhee Kimd94ceea2017-11-29 19:03:02 +00001135 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_CLIENT_INTF_MAC);
Charles Chan239492c2018-01-22 13:27:28 -08001136 return Lists.newArrayList();
Kalhee Kim121ba922017-11-01 17:56:44 +00001137 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001138
Kalhee Kim495c9b22017-11-07 16:32:09 +00001139 IPv6 clientIpv6 = (IPv6) clientPacket.getPayload();
1140 UDP clientUdp = (UDP) clientIpv6.getPayload();
1141 DHCP6 clientDhcp6 = (DHCP6) clientUdp.getPayload();
1142
Taras Lemkin96a0d342018-03-26 14:52:58 +00001143 boolean directConnFlag = Dhcp6HandlerUtil.directlyConnected(clientDhcp6);
Charles Chana990ce92017-10-30 10:22:50 -07001144
Kalhee Kim121ba922017-11-01 17:56:44 +00001145 ConnectPoint clientConnectionPoint = context.inPacket().receivedFrom();
1146 VlanId vlanIdInUse = VlanId.vlanId(clientPacket.getVlanID());
1147 Interface clientInterface = interfaceService.getInterfacesByPort(clientConnectionPoint)
Taras Lemkin96a0d342018-03-26 14:52:58 +00001148 .stream().filter(iface -> Dhcp6HandlerUtil.interfaceContainsVlan(iface, vlanIdInUse))
Kalhee Kim495c9b22017-11-07 16:32:09 +00001149 .findFirst()
1150 .orElse(null);
Kalhee Kim45fede42017-09-05 19:05:06 +00001151
Kalhee Kim495c9b22017-11-07 16:32:09 +00001152 List<InternalPacket> internalPackets = new ArrayList<>();
1153 List<DhcpServerInfo> serverInfoList = findValidServerInfo(directConnFlag);
1154 List<DhcpServerInfo> copyServerInfoList = new ArrayList<DhcpServerInfo>(serverInfoList);
Kalhee Kim45b24182017-10-18 18:30:23 +00001155
Kalhee Kim495c9b22017-11-07 16:32:09 +00001156 for (DhcpServerInfo serverInfo : copyServerInfoList) {
Taras Lemkin96a0d342018-03-26 14:52:58 +00001157 if (!Dhcp6HandlerUtil.checkDhcpServerConnPt(directConnFlag, serverInfo)) {
Kalhee Kim495c9b22017-11-07 16:32:09 +00001158 log.warn("Can't get server connect point, ignore");
1159 continue;
1160 }
1161 DhcpServerInfo newServerInfo = getHostInfoForServerInfo(serverInfo, serverInfoList);
1162 if (newServerInfo == null) {
1163 log.warn("Can't get server interface with host info resolved, ignore");
1164 continue;
1165 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001166
Kalhee Kim495c9b22017-11-07 16:32:09 +00001167 Interface serverInterface = getServerInterface(newServerInfo);
1168 if (serverInterface == null) {
1169 log.warn("Can't get server interface, ignore");
1170 continue;
1171 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +00001172
Taras Lemkin96a0d342018-03-26 14:52:58 +00001173 Ethernet etherReply = Dhcp6HandlerUtil.buildDhcp6PacketFromClient(context, clientPacket,
Kalhee Kim495c9b22017-11-07 16:32:09 +00001174 clientInterfaces, newServerInfo, serverInterface);
1175 removeHostOrRoute(directConnFlag, clientConnectionPoint, clientDhcp6, clientPacket,
1176 clientIpv6, clientInterface);
Kalhee Kimea4b6c22017-11-09 14:38:37 +00001177
Taras Lemkin96a0d342018-03-26 14:52:58 +00001178 InternalPacket internalPacket = InternalPacket.internalPacket(etherReply,
Kalhee Kim495c9b22017-11-07 16:32:09 +00001179 serverInfo.getDhcpServerConnectPoint().get());
1180 internalPackets.add(internalPacket);
1181 }
1182 log.debug("num of client packets to send is{}", internalPackets.size());
Kalhee Kimea4b6c22017-11-09 14:38:37 +00001183
Kalhee Kim495c9b22017-11-07 16:32:09 +00001184 return internalPackets;
1185 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001186
1187 /**
Kalhee Kim45fede42017-09-05 19:05:06 +00001188 * process the DHCP6 relay-reply packet from dhcp server.
1189 *
1190 * @param context packet context
1191 * @param receivedPacket server ethernet packet
1192 * @param recevingInterfaces set of server side interfaces
Kalhee Kim495c9b22017-11-07 16:32:09 +00001193 * @return internalPacket toward client
Kalhee Kim45fede42017-09-05 19:05:06 +00001194 */
1195 private InternalPacket processDhcp6PacketFromServer(PacketContext context,
1196 Ethernet receivedPacket, Set<Interface> recevingInterfaces) {
Kalhee Kim45fede42017-09-05 19:05:06 +00001197 // get dhcp6 header.
Ray Milkeyf0c47612017-09-28 11:29:38 -07001198 Ethernet etherReply = receivedPacket.duplicate();
Kalhee Kim45fede42017-09-05 19:05:06 +00001199 IPv6 ipv6Packet = (IPv6) etherReply.getPayload();
1200 UDP udpPacket = (UDP) ipv6Packet.getPayload();
1201 DHCP6 dhcp6Relay = (DHCP6) udpPacket.getPayload();
Taras Lemkin96a0d342018-03-26 14:52:58 +00001202 Boolean directConnFlag = Dhcp6HandlerUtil.directlyConnected(dhcp6Relay);
Kalhee Kim45fede42017-09-05 19:05:06 +00001203
Kalhee Kim495c9b22017-11-07 16:32:09 +00001204 DHCP6 embeddedDhcp6 = dhcp6Relay.getOptions().stream()
1205 .filter(opt -> opt instanceof Dhcp6RelayOption)
1206 .map(BasePacket::getPayload)
1207 .map(pld -> (DHCP6) pld)
1208 .findFirst()
1209 .orElse(null);
Kalhee Kimd21029f2017-09-26 20:21:53 +00001210 ConnectPoint inPort = context.inPacket().receivedFrom();
Kalhee Kim495c9b22017-11-07 16:32:09 +00001211 DhcpServerInfo foundServerInfo = findServerInfoFromServer(directConnFlag, inPort);
Kalhee Kimd21029f2017-09-26 20:21:53 +00001212
Kalhee Kim495c9b22017-11-07 16:32:09 +00001213 if (foundServerInfo == null) {
1214 log.warn("Cannot find server info");
Kalhee Kimd94ceea2017-11-29 19:03:02 +00001215 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_SERVER_INFO);
Kalhee Kimd21029f2017-09-26 20:21:53 +00001216 return null;
Kalhee Kim495c9b22017-11-07 16:32:09 +00001217 } else {
Taras Lemkin96a0d342018-03-26 14:52:58 +00001218 if (Dhcp6HandlerUtil.isServerIpEmpty(foundServerInfo)) {
Kalhee Kim495c9b22017-11-07 16:32:09 +00001219 log.warn("Cannot find server info's ipaddress");
Kalhee Kimd94ceea2017-11-29 19:03:02 +00001220 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_SERVER_IP6ADDR);
Kalhee Kim495c9b22017-11-07 16:32:09 +00001221 return null;
1222 }
Kalhee Kimd21029f2017-09-26 20:21:53 +00001223 }
1224
Kalhee Kim45fede42017-09-05 19:05:06 +00001225 Dhcp6InterfaceIdOption interfaceIdOption = dhcp6Relay.getOptions().stream()
1226 .filter(opt -> opt instanceof Dhcp6InterfaceIdOption)
1227 .map(opt -> (Dhcp6InterfaceIdOption) opt)
1228 .findFirst()
1229 .orElse(null);
Kalhee Kim45fede42017-09-05 19:05:06 +00001230 if (interfaceIdOption == null) {
1231 log.warn("Interface Id option is not present, abort packet...");
Kalhee Kimd94ceea2017-11-29 19:03:02 +00001232 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.OPTION_MISSING_FAIL);
Kalhee Kim45fede42017-09-05 19:05:06 +00001233 return null;
1234 }
1235
1236 MacAddress peerMac = interfaceIdOption.getMacAddress();
1237 String clientConnectionPointStr = new String(interfaceIdOption.getInPort());
Kalhee Kim45fede42017-09-05 19:05:06 +00001238 ConnectPoint clientConnectionPoint = ConnectPoint.deviceConnectPoint(clientConnectionPointStr);
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +00001239 VlanId vlanIdInUse = VlanId.vlanId(interfaceIdOption.getVlanId());
1240 Interface clientInterface = interfaceService.getInterfacesByPort(clientConnectionPoint)
Taras Lemkin96a0d342018-03-26 14:52:58 +00001241 .stream().filter(iface -> Dhcp6HandlerUtil.interfaceContainsVlan(iface, vlanIdInUse))
Kalhee Kim495c9b22017-11-07 16:32:09 +00001242 .findFirst().orElse(null);
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +00001243 if (clientInterface == null) {
1244 log.warn("Cannot get client interface for from packet, abort... vlan {}", vlanIdInUse.toString());
Kalhee Kimd94ceea2017-11-29 19:03:02 +00001245 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_MATCHING_INTF);
Kalhee Kim45fede42017-09-05 19:05:06 +00001246 return null;
1247 }
Kalhee Kim44fbda12018-04-03 21:08:18 +00001248 etherReply.setVlanID(vlanIdInUse.toShort());
1249
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +00001250 MacAddress relayAgentMac = clientInterface.mac();
Kalhee Kim45fede42017-09-05 19:05:06 +00001251 if (relayAgentMac == null) {
Yi Tseng68ef26b2017-12-18 17:10:00 -08001252 log.warn("Can not get client interface mac, abort packet..");
Kalhee Kimd94ceea2017-11-29 19:03:02 +00001253 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_CLIENT_INTF_MAC);
Kalhee Kim45fede42017-09-05 19:05:06 +00001254 return null;
1255 }
1256 etherReply.setSourceMACAddress(relayAgentMac);
1257
1258 // find destMac
Yi Tseng68ef26b2017-12-18 17:10:00 -08001259 MacAddress clientMac;
Yi Tsengaa417a62017-09-08 17:22:51 -07001260 Ip6Address peerAddress = Ip6Address.valueOf(dhcp6Relay.getPeerAddress());
1261 Set<Host> clients = hostService.getHostsByIp(peerAddress);
Kalhee Kim45fede42017-09-05 19:05:06 +00001262 if (clients.isEmpty()) {
Yi Tseng68ef26b2017-12-18 17:10:00 -08001263 log.trace("There's no host found for this address {}",
Kalhee Kim45fede42017-09-05 19:05:06 +00001264 HexString.toHexString(dhcp6Relay.getPeerAddress(), ":"));
Yi Tseng68ef26b2017-12-18 17:10:00 -08001265 log.trace("Let's look up interfaceId {}", HexString.toHexString(peerMac.toBytes(), ":"));
Kalhee Kim45fede42017-09-05 19:05:06 +00001266 clientMac = peerMac;
1267 } else {
1268 clientMac = clients.iterator().next().mac();
1269 if (clientMac == null) {
1270 log.warn("No client mac address found, abort packet...");
Kalhee Kimd94ceea2017-11-29 19:03:02 +00001271 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_CLIENT_INTF_MAC);
Kalhee Kim45fede42017-09-05 19:05:06 +00001272 return null;
1273 }
Yi Tseng68ef26b2017-12-18 17:10:00 -08001274 log.trace("Client mac address found from getHostByIp");
Kalhee Kim45fede42017-09-05 19:05:06 +00001275 }
1276 etherReply.setDestinationMACAddress(clientMac);
Kalhee Kim45fede42017-09-05 19:05:06 +00001277 // ip header
1278 ipv6Packet.setSourceAddress(dhcp6Relay.getLinkAddress());
1279 ipv6Packet.setDestinationAddress(dhcp6Relay.getPeerAddress());
1280 // udp header
1281 udpPacket.setSourcePort(UDP.DHCP_V6_SERVER_PORT);
1282 if (directConnFlag) {
1283 udpPacket.setDestinationPort(UDP.DHCP_V6_CLIENT_PORT);
1284 } else {
1285 udpPacket.setDestinationPort(UDP.DHCP_V6_SERVER_PORT);
1286 }
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001287
Kalhee Kim45fede42017-09-05 19:05:06 +00001288
Taras Lemkin96a0d342018-03-26 14:52:58 +00001289 boolean hostOrRouteAllowed = learnRouteFromLeasequery ||
1290 Dhcp6HandlerUtil.getDhcp6LeafMessageType(dhcp6Relay) != MsgType.LEASEQUERY_REPLY;
1291 log.debug("Can add host or route: {}", hostOrRouteAllowed);
1292
1293 if (hostOrRouteAllowed) {
1294 // add host or route
1295 addHostOrRoute(directConnFlag, clientConnectionPoint, dhcp6Relay, embeddedDhcp6,
1296 clientMac, clientInterface);
1297 }
1298
Kalhee Kim45fede42017-09-05 19:05:06 +00001299 udpPacket.setPayload(embeddedDhcp6);
1300 udpPacket.resetChecksum();
1301 ipv6Packet.setPayload(udpPacket);
1302 etherReply.setPayload(ipv6Packet);
Taras Lemkin96a0d342018-03-26 14:52:58 +00001303 return InternalPacket.internalPacket(etherReply, clientConnectionPoint);
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001304 }
Yi Tseng483ac6f2017-08-02 15:03:31 -07001305
1306 @Override
Kalhee Kimba366062017-11-07 16:32:09 +00001307 public void setDhcpFpmEnabled(Boolean enabled) {
1308 dhcpFpmEnabled = enabled;
1309 }
1310
1311 @Override
Yi Tseng483ac6f2017-08-02 15:03:31 -07001312 public void setDefaultDhcpServerConfigs(Collection<DhcpServerConfig> configs) {
Kalhee Kim495c9b22017-11-07 16:32:09 +00001313 log.debug("setDefaultDhcpServerConfigs is called.");
Yi Tseng919b2df2017-09-07 16:22:51 -07001314 setDhcpServerConfigs(configs, defaultServerInfoList);
Yi Tseng919b2df2017-09-07 16:22:51 -07001315 }
1316
1317 @Override
1318 public void setIndirectDhcpServerConfigs(Collection<DhcpServerConfig> configs) {
Kalhee Kim495c9b22017-11-07 16:32:09 +00001319 log.debug("setIndirectDhcpServerConfigs is called.");
Yi Tseng919b2df2017-09-07 16:22:51 -07001320 setDhcpServerConfigs(configs, indirectServerInfoList);
Yi Tseng919b2df2017-09-07 16:22:51 -07001321 }
1322
1323 public void setDhcpServerConfigs(Collection<DhcpServerConfig> configs, List<DhcpServerInfo> serverInfoList) {
Kalhee Kim495c9b22017-11-07 16:32:09 +00001324 log.debug("config size {}.", configs.size());
1325
Kalhee Kim45fede42017-09-05 19:05:06 +00001326 if (configs.size() == 0) {
1327 // no config to update
1328 return;
1329 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001330 // TODO: currently we pick up first DHCP server config.
1331 // Will use other server configs in the future for HA.
Kalhee Kim495c9b22017-11-07 16:32:09 +00001332 Boolean isConfigValid = false;
1333 for (DhcpServerConfig serverConfig : configs) {
1334 if (serverConfig.getDhcpServerIp6().isPresent()) {
1335 isConfigValid = true;
1336 break;
1337 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001338 }
Kalhee Kim495c9b22017-11-07 16:32:09 +00001339 if (!isConfigValid) {
1340 log.warn("No IP V6 server address found.");
1341 return; // No IP V6 address found
1342 }
1343 for (DhcpServerInfo oldServerInfo : serverInfoList) {
Yi Tseng919b2df2017-09-07 16:22:51 -07001344 // stop monitoring gateway or server
1345 oldServerInfo.getDhcpGatewayIp6().ifPresent(gatewayIp -> {
1346 hostService.stopMonitoringIp(gatewayIp);
1347 });
1348 oldServerInfo.getDhcpServerIp6().ifPresent(serverIp -> {
1349 hostService.stopMonitoringIp(serverIp);
Yi Tseng525ff402017-10-23 19:39:39 -07001350 cancelDhcpPacket(serverIp);
Yi Tseng919b2df2017-09-07 16:22:51 -07001351 });
1352 }
Kalhee Kim495c9b22017-11-07 16:32:09 +00001353 serverInfoList.clear();
1354 for (DhcpServerConfig serverConfig : configs) {
1355 // Create new server info according to the config
1356 DhcpServerInfo newServerInfo = new DhcpServerInfo(serverConfig,
1357 DhcpServerInfo.Version.DHCP_V6);
1358 checkState(newServerInfo.getDhcpServerConnectPoint().isPresent(),
1359 "Connect point not exists");
1360 checkState(newServerInfo.getDhcpServerIp6().isPresent(),
1361 "IP of DHCP server not exists");
Yi Tseng919b2df2017-09-07 16:22:51 -07001362
Kalhee Kim495c9b22017-11-07 16:32:09 +00001363 log.debug("DHCP server connect point: {}", newServerInfo.getDhcpServerConnectPoint().orElse(null));
1364 log.debug("DHCP server IP: {}", newServerInfo.getDhcpServerIp6().orElse(null));
Yi Tseng919b2df2017-09-07 16:22:51 -07001365
Kalhee Kim495c9b22017-11-07 16:32:09 +00001366 Ip6Address serverIp = newServerInfo.getDhcpServerIp6().get();
1367 Ip6Address ipToProbe;
1368 if (newServerInfo.getDhcpGatewayIp6().isPresent()) {
1369 ipToProbe = newServerInfo.getDhcpGatewayIp6().get();
1370 } else {
1371 ipToProbe = newServerInfo.getDhcpServerIp6().orElse(null);
1372 }
1373 String hostToProbe = newServerInfo.getDhcpGatewayIp6()
1374 .map(ip -> "gateway").orElse("server");
Yi Tseng919b2df2017-09-07 16:22:51 -07001375
Kalhee Kim495c9b22017-11-07 16:32:09 +00001376 log.warn("Probing to resolve {} IP {}", hostToProbe, ipToProbe);
1377 hostService.startMonitoringIp(ipToProbe);
1378
1379 Set<Host> hosts = hostService.getHostsByIp(ipToProbe);
1380 if (!hosts.isEmpty()) {
1381 Host host = hosts.iterator().next();
1382 newServerInfo.setDhcpConnectVlan(host.vlan());
1383 newServerInfo.setDhcpConnectMac(host.mac());
1384 log.warn("Host found host {}", host);
1385
1386 } else {
1387 log.warn("No host found host ip {}", ipToProbe);
1388 }
1389 // Add new server info
1390 synchronized (this) {
1391 serverInfoList.add(newServerInfo);
1392 }
1393 if (!hosts.isEmpty()) {
1394 requestDhcpPacket(serverIp);
1395 }
Yi Tseng919b2df2017-09-07 16:22:51 -07001396 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001397 }
1398
1399 class InternalHostListener implements HostListener {
1400 @Override
1401 public void event(HostEvent event) {
1402 switch (event.type()) {
1403 case HOST_ADDED:
1404 case HOST_UPDATED:
Charles Chan24a96ff2018-08-13 10:32:03 -07001405 case HOST_MOVED:
Jordan Halterman6328db72018-04-10 13:34:50 -04001406 log.trace("Scheduled host event {}", event);
1407 hostEventExecutor.execute(() -> hostUpdated(event.subject()));
Kalhee Kim45fede42017-09-05 19:05:06 +00001408 break;
1409 case HOST_REMOVED:
Jordan Halterman6328db72018-04-10 13:34:50 -04001410 log.trace("Scheduled host event {}", event);
1411 hostEventExecutor.execute(() -> hostRemoved(event.subject()));
Kalhee Kim45fede42017-09-05 19:05:06 +00001412 break;
Kalhee Kim45fede42017-09-05 19:05:06 +00001413 default:
1414 break;
1415 }
1416 }
1417 }
1418
1419 /**
Kalhee Kim45fede42017-09-05 19:05:06 +00001420 * Handle host updated.
1421 * If the host is DHCP server or gateway, update connect mac and vlan.
1422 *
1423 * @param host the host
1424 */
1425 private void hostUpdated(Host host) {
Yi Tseng525ff402017-10-23 19:39:39 -07001426 hostUpdated(host, defaultServerInfoList);
1427 hostUpdated(host, indirectServerInfoList);
Kalhee Kim45fede42017-09-05 19:05:06 +00001428 }
1429
Yi Tseng525ff402017-10-23 19:39:39 -07001430 private void hostUpdated(Host host, List<DhcpServerInfo> serverInfoList) {
jjosep004c05f55ab2018-08-21 09:01:10 -04001431 serverInfoList.stream().forEach(serverInfo -> {
Yi Tseng525ff402017-10-23 19:39:39 -07001432 Ip6Address serverIp = serverInfo.getDhcpServerIp6().orElse(null);
jjosep004c05f55ab2018-08-21 09:01:10 -04001433 Ip6Address targetIp = serverInfo.getDhcpGatewayIp6().orElse(null);
Yi Tseng525ff402017-10-23 19:39:39 -07001434
1435 if (targetIp == null) {
1436 targetIp = serverIp;
1437 }
Yi Tseng525ff402017-10-23 19:39:39 -07001438 if (targetIp != null) {
1439 if (host.ipAddresses().contains(targetIp)) {
1440 serverInfo.setDhcpConnectMac(host.mac());
1441 serverInfo.setDhcpConnectVlan(host.vlan());
1442 requestDhcpPacket(serverIp);
1443 }
1444 }
jjosep004c05f55ab2018-08-21 09:01:10 -04001445 });
Yi Tseng525ff402017-10-23 19:39:39 -07001446 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001447 /**
1448 * Handle host removed.
1449 * If the host is DHCP server or gateway, unset connect mac and vlan.
1450 *
1451 * @param host the host
1452 */
1453 private void hostRemoved(Host host) {
Yi Tseng525ff402017-10-23 19:39:39 -07001454 hostRemoved(host, defaultServerInfoList);
1455 hostRemoved(host, indirectServerInfoList);
Yi Tseng919b2df2017-09-07 16:22:51 -07001456 }
1457
Yi Tseng525ff402017-10-23 19:39:39 -07001458 private void hostRemoved(Host host, List<DhcpServerInfo> serverInfoList) {
jjosep004c05f55ab2018-08-21 09:01:10 -04001459 serverInfoList.stream().forEach(serverInfo -> {
Yi Tseng525ff402017-10-23 19:39:39 -07001460 Ip6Address serverIp = serverInfo.getDhcpServerIp6().orElse(null);
jjosep004c05f55ab2018-08-21 09:01:10 -04001461 Ip6Address targetIp = serverInfo.getDhcpGatewayIp6().orElse(null);
Yi Tseng525ff402017-10-23 19:39:39 -07001462
1463 if (targetIp == null) {
1464 targetIp = serverIp;
1465 }
Yi Tseng525ff402017-10-23 19:39:39 -07001466 if (targetIp != null) {
1467 if (host.ipAddresses().contains(targetIp)) {
1468 serverInfo.setDhcpConnectVlan(null);
1469 serverInfo.setDhcpConnectMac(null);
1470 cancelDhcpPacket(serverIp);
1471 }
1472 }
jjosep004c05f55ab2018-08-21 09:01:10 -04001473 });
Yi Tseng525ff402017-10-23 19:39:39 -07001474 }
1475
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001476 /**
1477 * Gets Interface facing to the server for default host.
1478 *
1479 * @return the Interface facing to the server; null if not found
1480 */
1481 private Interface getServerInterface() {
1482 DhcpServerInfo serverInfo;
1483 ConnectPoint dhcpServerConnectPoint;
1484 VlanId dhcpConnectVlan;
1485
1486 if (!defaultServerInfoList.isEmpty()) {
1487 serverInfo = defaultServerInfoList.get(0);
1488 dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1489 dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1490 } else {
1491 return null;
1492 }
1493 if (dhcpServerConnectPoint == null || dhcpConnectVlan == null) {
1494 log.info("Default DHCP server {} not resolve yet", serverInfo.getDhcpGatewayIp6());
1495 return null;
1496 }
1497 return interfaceService.getInterfacesByPort(dhcpServerConnectPoint)
1498 .stream()
Taras Lemkin96a0d342018-03-26 14:52:58 +00001499 .filter(iface -> Dhcp6HandlerUtil.interfaceContainsVlan(iface, dhcpConnectVlan))
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001500 .findFirst()
1501 .orElse(null);
1502 }
1503
1504 /**
1505 * Gets Interface facing to the server for indirect hosts.
1506 * Use default server Interface if indirect server not configured.
1507 *
1508 * @return the Interface facing to the server; null if not found
1509 */
1510 private Interface getIndirectServerInterface() {
1511 DhcpServerInfo serverInfo;
1512
1513 ConnectPoint indirectDhcpServerConnectPoint;
1514 VlanId indirectDhcpConnectVlan;
1515
1516 if (!indirectServerInfoList.isEmpty()) {
1517 serverInfo = indirectServerInfoList.get(0);
1518 indirectDhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1519 indirectDhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1520 } else {
1521 return getServerInterface();
1522 }
1523 if (indirectDhcpServerConnectPoint == null || indirectDhcpConnectVlan == null) {
1524 log.info("Indirect DHCP server {} not resolve yet", serverInfo.getDhcpGatewayIp6());
1525 return null;
1526 }
1527 return interfaceService.getInterfacesByPort(indirectDhcpServerConnectPoint)
1528 .stream()
Taras Lemkin96a0d342018-03-26 14:52:58 +00001529 .filter(iface -> Dhcp6HandlerUtil.interfaceContainsVlan(iface, indirectDhcpConnectVlan))
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001530 .findFirst()
1531 .orElse(null);
1532 }
1533
Kalhee Kim45b24182017-10-18 18:30:23 +00001534 /**
Kalhee Kim495c9b22017-11-07 16:32:09 +00001535 * Checks if serverInfo's host info (mac and vlan) is filled in; if not, fills in.
1536 *
1537 * @param serverInfo server information
1538 * @return newServerInfo if host info can be either found or filled in.
1539 */
1540 private DhcpServerInfo getHostInfoForServerInfo(DhcpServerInfo serverInfo, List<DhcpServerInfo> sererInfoList) {
1541 DhcpServerInfo newServerInfo = null;
1542 MacAddress dhcpServerConnectMac = serverInfo.getDhcpConnectMac().orElse(null);
1543 VlanId dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1544 ConnectPoint dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1545
1546 if (dhcpServerConnectMac != null && dhcpConnectVlan != null) {
1547 newServerInfo = serverInfo;
Charles Chan750c9992018-02-26 10:44:49 -08001548 log.debug("DHCP server {} host info found. ConnectPt{} Mac {} vlan {}", serverInfo.getDhcpServerIp6(),
Kalhee Kim495c9b22017-11-07 16:32:09 +00001549 dhcpServerConnectPoint, dhcpServerConnectMac, dhcpConnectVlan);
1550 } else {
1551 log.warn("DHCP server {} not resolve yet connectPt {} mac {} vlan {}", serverInfo.getDhcpServerIp6(),
1552 dhcpServerConnectPoint, dhcpServerConnectMac, dhcpConnectVlan);
1553
1554 Ip6Address ipToProbe;
1555 if (serverInfo.getDhcpGatewayIp6().isPresent()) {
1556 ipToProbe = serverInfo.getDhcpGatewayIp6().get();
1557 } else {
1558 ipToProbe = serverInfo.getDhcpServerIp6().orElse(null);
1559 }
1560 String hostToProbe = serverInfo.getDhcpGatewayIp6()
1561 .map(ip -> "gateway").orElse("server");
1562
1563 log.info("Dynamically probing to resolve {} IP {}", hostToProbe, ipToProbe);
1564 hostService.startMonitoringIp(ipToProbe);
1565
1566 Set<Host> hosts = hostService.getHostsByIp(ipToProbe);
1567 if (!hosts.isEmpty()) {
1568 int serverInfoIndex = sererInfoList.indexOf(serverInfo);
1569 Host host = hosts.iterator().next();
1570 serverInfo.setDhcpConnectVlan(host.vlan());
1571 serverInfo.setDhcpConnectMac(host.mac());
1572 // replace the serverInfo in the list
1573 sererInfoList.set(serverInfoIndex, serverInfo);
1574 newServerInfo = serverInfo;
1575 log.warn("Dynamically host found host {}", host);
1576 } else {
1577 log.warn("No host found host ip {} dynamically", ipToProbe);
1578 }
1579 }
1580 return newServerInfo;
1581 }
1582
1583 /**
Kalhee Kim45b24182017-10-18 18:30:23 +00001584 * Gets Interface facing to the server for default host.
1585 *
Kalhee Kim495c9b22017-11-07 16:32:09 +00001586 * @param serverInfo server information
Kalhee Kim45b24182017-10-18 18:30:23 +00001587 * @return the Interface facing to the server; null if not found
1588 */
Kalhee Kim495c9b22017-11-07 16:32:09 +00001589 private Interface getServerInterface(DhcpServerInfo serverInfo) {
1590 Interface serverInterface = null;
Yi Tseng25bfe372017-11-03 16:27:32 -07001591
Kalhee Kim495c9b22017-11-07 16:32:09 +00001592 ConnectPoint dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1593 VlanId dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1594
1595 if (dhcpServerConnectPoint != null && dhcpConnectVlan != null) {
1596 serverInterface = interfaceService.getInterfacesByPort(dhcpServerConnectPoint)
1597 .stream()
Taras Lemkin96a0d342018-03-26 14:52:58 +00001598 .filter(iface -> Dhcp6HandlerUtil.interfaceContainsVlan(iface, dhcpConnectVlan))
Kalhee Kim495c9b22017-11-07 16:32:09 +00001599 .findFirst()
1600 .orElse(null);
Yi Tseng25bfe372017-11-03 16:27:32 -07001601 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +00001602 log.warn("DHCP server {} not resolve yet connectPoint {} vlan {}", serverInfo.getDhcpServerIp6(),
1603 dhcpServerConnectPoint, dhcpConnectVlan);
Kalhee Kim45b24182017-10-18 18:30:23 +00001604 }
Kalhee Kim495c9b22017-11-07 16:32:09 +00001605
1606 return serverInterface;
Kalhee Kim45b24182017-10-18 18:30:23 +00001607 }
1608
Yi Tseng525ff402017-10-23 19:39:39 -07001609 private void requestDhcpPacket(Ip6Address serverIp) {
1610 requestServerDhcpPacket(serverIp);
1611 requestClientDhcpPacket(serverIp);
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001612 requestServerLQPacket(serverIp);
Yi Tseng525ff402017-10-23 19:39:39 -07001613 }
1614
1615 private void cancelDhcpPacket(Ip6Address serverIp) {
1616 cancelServerDhcpPacket(serverIp);
1617 cancelClientDhcpPacket(serverIp);
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001618 cancelServerLQPacket(serverIp);
Yi Tseng525ff402017-10-23 19:39:39 -07001619 }
1620
1621 private void cancelServerDhcpPacket(Ip6Address serverIp) {
1622 TrafficSelector serverSelector =
1623 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1624 .matchIPv6Src(serverIp.toIpPrefix())
1625 .build();
1626 packetService.cancelPackets(serverSelector,
1627 PacketPriority.CONTROL,
1628 appId);
1629 }
1630
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001631 private void cancelServerLQPacket(Ip6Address serverIp) {
1632 TrafficSelector serverSelector =
1633 DefaultTrafficSelector.builder(LEASE_QUERY_RESPONSE_SELECTOR)
1634 .matchIPv6Src(serverIp.toIpPrefix())
1635 .build();
1636 packetService.cancelPackets(serverSelector,
1637 PacketPriority.CONTROL,
1638 appId);
1639 }
1640
Yi Tseng525ff402017-10-23 19:39:39 -07001641 private void requestServerDhcpPacket(Ip6Address serverIp) {
1642 TrafficSelector serverSelector =
1643 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1644 .matchIPv6Src(serverIp.toIpPrefix())
1645 .build();
1646 packetService.requestPackets(serverSelector,
1647 PacketPriority.CONTROL,
1648 appId);
1649 }
1650
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001651 private void requestServerLQPacket(Ip6Address serverIp) {
1652 TrafficSelector serverSelector =
1653 DefaultTrafficSelector.builder(LEASE_QUERY_RESPONSE_SELECTOR)
1654 .matchIPv6Src(serverIp.toIpPrefix())
1655 .build();
1656 packetService.requestPackets(serverSelector,
1657 PacketPriority.CONTROL,
1658 appId);
1659 }
1660
Yi Tseng525ff402017-10-23 19:39:39 -07001661 private void cancelClientDhcpPacket(Ip6Address serverIp) {
1662 // Packet comes from relay
1663 TrafficSelector indirectClientSelector =
1664 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1665 .matchIPv6Dst(serverIp.toIpPrefix())
1666 .build();
1667 packetService.cancelPackets(indirectClientSelector,
1668 PacketPriority.CONTROL,
1669 appId);
Yi Tseng41dde702017-11-02 15:21:10 -07001670 indirectClientSelector =
1671 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1672 .matchIPv6Dst(Ip6Address.ALL_DHCP_RELAY_AGENTS_AND_SERVERS.toIpPrefix())
1673 .build();
1674 packetService.cancelPackets(indirectClientSelector,
1675 PacketPriority.CONTROL,
1676 appId);
1677 indirectClientSelector =
1678 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1679 .matchIPv6Dst(Ip6Address.ALL_DHCP_SERVERS.toIpPrefix())
1680 .build();
1681 packetService.cancelPackets(indirectClientSelector,
1682 PacketPriority.CONTROL,
1683 appId);
Yi Tseng525ff402017-10-23 19:39:39 -07001684
1685 // Packet comes from client
1686 packetService.cancelPackets(CLIENT_SERVER_SELECTOR,
1687 PacketPriority.CONTROL,
1688 appId);
1689 }
1690
1691 private void requestClientDhcpPacket(Ip6Address serverIp) {
1692 // Packet comes from relay
1693 TrafficSelector indirectClientSelector =
1694 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1695 .matchIPv6Dst(serverIp.toIpPrefix())
1696 .build();
1697 packetService.requestPackets(indirectClientSelector,
1698 PacketPriority.CONTROL,
1699 appId);
Yi Tseng41dde702017-11-02 15:21:10 -07001700 indirectClientSelector =
1701 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1702 .matchIPv6Dst(Ip6Address.ALL_DHCP_RELAY_AGENTS_AND_SERVERS.toIpPrefix())
1703 .build();
1704 packetService.requestPackets(indirectClientSelector,
1705 PacketPriority.CONTROL,
1706 appId);
1707 indirectClientSelector =
1708 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1709 .matchIPv6Dst(Ip6Address.ALL_DHCP_SERVERS.toIpPrefix())
1710 .build();
1711 packetService.requestPackets(indirectClientSelector,
1712 PacketPriority.CONTROL,
1713 appId);
Yi Tseng525ff402017-10-23 19:39:39 -07001714
1715 // Packet comes from client
1716 packetService.requestPackets(CLIENT_SERVER_SELECTOR,
1717 PacketPriority.CONTROL,
1718 appId);
1719 }
1720
1721 /**
1722 * Process the ignore rules.
1723 *
1724 * @param deviceId the device id
1725 * @param vlanId the vlan to be ignored
1726 * @param op the operation, ADD to install; REMOVE to uninstall rules
1727 */
1728 private void processIgnoreVlanRule(DeviceId deviceId, VlanId vlanId, Objective.Operation op) {
Yi Tseng525ff402017-10-23 19:39:39 -07001729 AtomicInteger installedCount = new AtomicInteger(DHCP_SELECTORS.size());
1730 DHCP_SELECTORS.forEach(trafficSelector -> {
1731 TrafficSelector selector = DefaultTrafficSelector.builder(trafficSelector)
1732 .matchVlanId(vlanId)
1733 .build();
1734
1735 ForwardingObjective.Builder builder = DefaultForwardingObjective.builder()
1736 .withFlag(ForwardingObjective.Flag.VERSATILE)
1737 .withSelector(selector)
1738 .withPriority(IGNORE_CONTROL_PRIORITY)
Yi Tsengdbabeed2017-11-29 10:49:20 -08001739 .withTreatment(DefaultTrafficTreatment.emptyTreatment())
Yi Tseng525ff402017-10-23 19:39:39 -07001740 .fromApp(appId);
1741
1742
1743 ObjectiveContext objectiveContext = new ObjectiveContext() {
1744 @Override
1745 public void onSuccess(Objective objective) {
1746 log.info("Ignore rule {} (Vlan id {}, device {}, selector {})",
1747 op, vlanId, deviceId, selector);
1748 int countDown = installedCount.decrementAndGet();
1749 if (countDown != 0) {
1750 return;
1751 }
1752 switch (op) {
1753 case ADD:
1754 ignoredVlans.put(deviceId, vlanId);
1755 break;
1756 case REMOVE:
1757 ignoredVlans.remove(deviceId, vlanId);
1758 break;
1759 default:
1760 log.warn("Unsupported objective operation {}", op);
1761 break;
1762 }
1763 }
1764
1765 @Override
1766 public void onError(Objective objective, ObjectiveError error) {
1767 log.warn("Can't {} ignore rule (vlan id {}, selector {}, device {}) due to {}",
1768 op, vlanId, selector, deviceId, error);
1769 }
1770 };
1771
1772 ForwardingObjective fwd;
1773 switch (op) {
1774 case ADD:
1775 fwd = builder.add(objectiveContext);
1776 break;
1777 case REMOVE:
1778 fwd = builder.remove(objectiveContext);
1779 break;
1780 default:
1781 log.warn("Unsupported objective operation {}", op);
1782 return;
1783 }
1784
1785 Device device = deviceService.getDevice(deviceId);
1786 if (device == null || !device.is(Pipeliner.class)) {
1787 log.warn("Device {} is not available now, wait until device is available", deviceId);
1788 return;
1789 }
1790 flowObjectiveService.apply(deviceId, fwd);
1791 });
1792 }
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001793
Kalhee Kim121ba922017-11-01 17:56:44 +00001794 /**
1795 * Find first ipaddress for a given Host info i.e. mac and vlan.
1796 *
1797 * @param clientMac client mac
1798 * @param vlanId packet's vlan
1799 * @return next-hop link-local ipaddress for a given host
1800 */
Kalhee Kim495c9b22017-11-07 16:32:09 +00001801 private IpAddress getFirstIpByHost(Boolean directConnFlag, MacAddress clientMac, VlanId vlanId) {
Kalhee Kim121ba922017-11-01 17:56:44 +00001802 IpAddress nextHopIp;
1803 // pick out the first link-local ip address
1804 HostId gwHostId = HostId.hostId(clientMac, vlanId);
1805 Host gwHost = hostService.getHost(gwHostId);
1806 if (gwHost == null) {
1807 log.warn("Can't find gateway host for hostId {}", gwHostId);
1808 return null;
1809 }
Kalhee Kim495c9b22017-11-07 16:32:09 +00001810 if (directConnFlag) {
1811 nextHopIp = gwHost.ipAddresses()
1812 .stream()
1813 .filter(IpAddress::isIp6)
1814 .map(IpAddress::getIp6Address)
1815 .findFirst()
1816 .orElse(null);
1817 } else {
1818 nextHopIp = gwHost.ipAddresses()
1819 .stream()
1820 .filter(IpAddress::isIp6)
1821 .filter(ip6 -> ip6.isLinkLocal())
1822 .map(IpAddress::getIp6Address)
1823 .findFirst()
1824 .orElse(null);
1825 }
Kalhee Kim121ba922017-11-01 17:56:44 +00001826 return nextHopIp;
1827 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +00001828
Kalhee Kim495c9b22017-11-07 16:32:09 +00001829 private List<DhcpServerInfo> findValidServerInfo(boolean directConnFlag) {
1830 List<DhcpServerInfo> validServerInfo;
1831
1832 if (directConnFlag || indirectServerInfoList.isEmpty()) {
1833 validServerInfo = new ArrayList<DhcpServerInfo>(defaultServerInfoList);
1834 } else {
1835 validServerInfo = new ArrayList<DhcpServerInfo>(indirectServerInfoList);
1836 }
1837 return validServerInfo;
1838 }
1839
1840 private DhcpServerInfo findServerInfoFromServer(boolean directConnFlag, ConnectPoint inPort) {
1841 List<DhcpServerInfo> validServerInfoList = findValidServerInfo(directConnFlag);
1842 DhcpServerInfo foundServerInfo = null;
1843 for (DhcpServerInfo serverInfo : validServerInfoList) {
1844 if (inPort.equals(serverInfo.getDhcpServerConnectPoint().get())) {
1845 foundServerInfo = serverInfo;
Charles Chan750c9992018-02-26 10:44:49 -08001846 log.debug("ServerInfo found for Rcv port {} Server Connect Point {} for {}",
Kalhee Kim495c9b22017-11-07 16:32:09 +00001847 inPort, serverInfo.getDhcpServerConnectPoint(), directConnFlag ? "direct" : "indirect");
1848 break;
1849 } else {
1850 log.warn("Rcv port {} not the same as Server Connect Point {} for {}",
1851 inPort, serverInfo.getDhcpServerConnectPoint(), directConnFlag ? "direct" : "indirect");
1852 }
1853 }
1854 return foundServerInfo;
1855 }
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001856
Kalhee Kim495c9b22017-11-07 16:32:09 +00001857 /**
1858 * Set the dhcp6 lease expiry poll interval value.
1859 *
1860 * @param val poll interval value in seconds
1861 */
Kalhee Kim2f07c602017-11-15 18:57:53 +00001862 @Override
Kalhee Kim495c9b22017-11-07 16:32:09 +00001863 public void setDhcp6PollInterval(int val) {
1864 dhcp6PollInterval = val;
1865 }
1866
Kalhee Kim2f07c602017-11-15 18:57:53 +00001867 /**
1868 * get the dhcp6 lease expiry poll interval value.
1869 * This is a private function
1870 * @return poll interval value in seconds
1871 */
1872 private int getDhcp6PollInterval() {
1873 return dhcp6PollInterval;
1874 }
1875
1876 /**
1877 * Find lease-expired ipaddresses and pd prefixes.
1878 * Removing host/route/fpm entries.
1879 */
1880 public void timeTick() {
1881 long currentTime = System.currentTimeMillis();
1882 Collection<DhcpRecord> records = dhcpRelayStore.getDhcpRecords();
1883
1884 log.debug("timeTick called currenttime {} records num {} ", currentTime, records.size());
1885
1886 records.forEach(record -> {
1887 boolean addrOrPdRemoved = false;
1888 DHCP6.MsgType ip6Status = record.ip6Status().orElse(null);
1889 if (ip6Status == null) {
1890 log.debug("record is not valid v6 record.");
1891 return;
1892 }
1893
1894 if ((currentTime - record.getLastIp6Update()) >
1895 ((record.addrPrefTime() + getDhcp6PollInterval() / 2) * 1000)) {
1896 // remove ipaddress from host/route table
1897 IpAddress ip = record.ip6Address().orElse(null);
1898 if (ip != null) {
1899 if (record.directlyConnected()) {
1900 providerService.removeIpFromHost(HostId.hostId(record.macAddress(),
1901 record.vlanId()), ip);
1902 } else {
1903 MacAddress gwMac = record.nextHop().orElse(null);
1904 if (gwMac == null) {
1905 log.warn("Can't find gateway mac address from record {} for ip6Addr", record);
1906 return;
1907 }
1908 IpAddress nextHopIp = getFirstIpByHost(record.directlyConnected(),
1909 gwMac,
1910 record.vlanId());
Kalhee Kim54a97c92018-04-02 21:23:46 +00001911 Route route = new Route(Route.Source.DHCP, ip.toIpPrefix(), nextHopIp);
Kalhee Kim2f07c602017-11-15 18:57:53 +00001912 routeStore.removeRoute(route);
1913 }
1914 record.updateAddrPrefTime(0);
1915 record.ip6Address(null);
1916 addrOrPdRemoved = true;
1917 dhcpRelayStore.updateDhcpRecord(HostId.hostId(record.macAddress(),
1918 record.vlanId()), record);
1919 log.warn("IP6 address is set to null. delta {} lastUpdate {} addrPrefTime {}",
1920 (currentTime - record.getLastIp6Update()), record.getLastIp6Update(),
1921 record.addrPrefTime());
1922 }
1923 }
1924 if ((currentTime - record.getLastPdUpdate()) >
1925 ((record.pdPrefTime() + getDhcp6PollInterval() / 2) * 1000)) {
1926 // remove PD from route/fpm table
1927 IpPrefix pdIpPrefix = record.pdPrefix().orElse(null);
1928 if (pdIpPrefix != null) {
1929 if (record.directlyConnected()) {
1930 providerService.removeIpFromHost(HostId.hostId(record.macAddress(), record.vlanId()),
1931 pdIpPrefix.address().getIp6Address());
1932 } else {
1933 MacAddress gwMac = record.nextHop().orElse(null);
1934 if (gwMac == null) {
1935 log.warn("Can't find gateway mac address from record {} for PD prefix", record);
1936 return;
1937 }
1938 IpAddress nextHopIp = getFirstIpByHost(record.directlyConnected(),
1939 gwMac,
1940 record.vlanId());
Kalhee Kim54a97c92018-04-02 21:23:46 +00001941 Route route = new Route(Route.Source.DHCP, pdIpPrefix, nextHopIp);
Kalhee Kim2f07c602017-11-15 18:57:53 +00001942 routeStore.removeRoute(route);
1943 if (this.dhcpFpmEnabled) {
1944 dhcpFpmPrefixStore.removeFpmRecord(pdIpPrefix);
1945 }
1946 }
1947 record.updatePdPrefTime(0);
1948 record.pdPrefix(null);
1949 addrOrPdRemoved = true;
1950 dhcpRelayStore.updateDhcpRecord(HostId.hostId(record.macAddress(),
1951 record.vlanId()), record);
1952 log.warn("PD prefix is set to null.delta {} pdPrefTime {}",
1953 (currentTime - record.getLastPdUpdate()), record.pdPrefTime());
1954 }
1955 }
1956 if (addrOrPdRemoved &&
1957 !record.ip6Address().isPresent() && !record.pdPrefix().isPresent()) {
1958 log.warn("ip6Status {} IP6 address and IP6 PD both are null. Remove record.", ip6Status);
1959 dhcpRelayStore.removeDhcpRecord(HostId.hostId(record.macAddress(), record.vlanId()));
1960 }
1961 }
1962 );
1963 }
1964}