blob: a30d685aac28e9afe4f688108df7aee366b69e2a [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;
Ray Milkey687c00c2018-10-31 10:18:41 -0700123import static org.onosproject.dhcprelay.OsgiPropertyConstants.LEARN_ROUTE_FROM_LEASE_QUERY;
124import static org.onosproject.dhcprelay.OsgiPropertyConstants.LEARN_ROUTE_FROM_LEASE_QUERY_DEFAULT;
Yi Tseng525ff402017-10-23 19:39:39 -0700125import static org.onosproject.net.flowobjective.Objective.Operation.ADD;
126import static org.onosproject.net.flowobjective.Objective.Operation.REMOVE;
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000127import java.util.concurrent.Semaphore;
Yi Tseng51301292017-07-28 13:02:59 -0700128
Ray Milkey687c00c2018-10-31 10:18:41 -0700129@Component(
130 service = { DhcpHandler.class, HostProvider.class },
131 property = {
132 "version:Integer=6",
133 LEARN_ROUTE_FROM_LEASE_QUERY + ":Boolean=" + LEARN_ROUTE_FROM_LEASE_QUERY_DEFAULT
134 }
135)
136
Yi Tsengaa417a62017-09-08 17:22:51 -0700137public class Dhcp6HandlerImpl implements DhcpHandler, HostProvider {
Charles Chand988c282017-09-12 17:09:32 -0700138 public static final String DHCP_V6_RELAY_APP = "org.onosproject.Dhcp6HandlerImpl";
Saurav Das7c6dec12017-09-13 14:35:56 -0700139 public static final ProviderId PROVIDER_ID = new ProviderId("dhcp6", DHCP_V6_RELAY_APP);
Yi Tseng525ff402017-10-23 19:39:39 -0700140 private static final int IGNORE_CONTROL_PRIORITY = PacketPriority.CONTROL.priorityValue() + 1000;
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000141 private String gCount = "global";
Yi Tseng525ff402017-10-23 19:39:39 -0700142 private static final TrafficSelector CLIENT_SERVER_SELECTOR = DefaultTrafficSelector.builder()
143 .matchEthType(Ethernet.TYPE_IPV6)
144 .matchIPProtocol(IPv6.PROTOCOL_UDP)
145 .matchIPv6Src(IpPrefix.IPV6_LINK_LOCAL_PREFIX)
146 .matchIPv6Dst(Ip6Address.ALL_DHCP_RELAY_AGENTS_AND_SERVERS.toIpPrefix())
147 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_V6_CLIENT_PORT))
148 .matchUdpDst(TpPort.tpPort(UDP.DHCP_V6_SERVER_PORT))
149 .build();
150 private static final TrafficSelector SERVER_RELAY_SELECTOR = DefaultTrafficSelector.builder()
151 .matchEthType(Ethernet.TYPE_IPV6)
152 .matchIPProtocol(IPv6.PROTOCOL_UDP)
153 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_V6_SERVER_PORT))
154 .matchUdpDst(TpPort.tpPort(UDP.DHCP_V6_SERVER_PORT))
155 .build();
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800156 // lease query reply is from server to client (no relay in between) - so we need to
157 // catch that scenario also ..
158 private static final TrafficSelector LEASE_QUERY_RESPONSE_SELECTOR = DefaultTrafficSelector.builder()
159 .matchEthType(Ethernet.TYPE_IPV6)
160 .matchIPProtocol(IPv6.PROTOCOL_UDP)
161 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_V6_SERVER_PORT))
162 .matchUdpDst(TpPort.tpPort(UDP.DHCP_V6_CLIENT_PORT))
163 .build();
Yi Tseng525ff402017-10-23 19:39:39 -0700164 static final Set<TrafficSelector> DHCP_SELECTORS = ImmutableSet.of(
165 CLIENT_SERVER_SELECTOR,
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800166 SERVER_RELAY_SELECTOR,
167 LEASE_QUERY_RESPONSE_SELECTOR
Yi Tseng525ff402017-10-23 19:39:39 -0700168 );
Kalhee Kim45fede42017-09-05 19:05:06 +0000169 private static Logger log = LoggerFactory.getLogger(Dhcp6HandlerImpl.class);
Yi Tseng51301292017-07-28 13:02:59 -0700170
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700171 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Kalhee Kim45fede42017-09-05 19:05:06 +0000172 protected DhcpRelayStore dhcpRelayStore;
Yi Tseng51301292017-07-28 13:02:59 -0700173
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700174 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000175 protected DhcpRelayCountersStore dhcpRelayCountersStore;
176
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700177 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Kalhee Kim45fede42017-09-05 19:05:06 +0000178 protected PacketService packetService;
179
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700180 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Kalhee Kim45fede42017-09-05 19:05:06 +0000181 protected RouteStore routeStore;
182
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700183 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Kalhee Kim45fede42017-09-05 19:05:06 +0000184 protected InterfaceService interfaceService;
185
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700186 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Kalhee Kim45fede42017-09-05 19:05:06 +0000187 protected HostService hostService;
188
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700189 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Yi Tsengaa417a62017-09-08 17:22:51 -0700190 protected HostProviderRegistry providerRegistry;
Kalhee Kim45fede42017-09-05 19:05:06 +0000191
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700192 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Yi Tseng525ff402017-10-23 19:39:39 -0700193 protected CoreService coreService;
194
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700195 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Kalhee Kimba366062017-11-07 16:32:09 +0000196 protected DhcpFpmPrefixStore dhcpFpmPrefixStore;
197
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700198 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Yi Tseng525ff402017-10-23 19:39:39 -0700199 protected DeviceService deviceService;
200
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700201 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Yi Tseng525ff402017-10-23 19:39:39 -0700202 protected FlowObjectiveService flowObjectiveService;
203
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700204 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Taras Lemkin96a0d342018-03-26 14:52:58 +0000205 protected ComponentConfigService cfgService;
206
Ray Milkey687c00c2018-10-31 10:18:41 -0700207 /** Enable learning routing information from LQ. */
208 private Boolean learnRouteFromLeasequery = LEARN_ROUTE_FROM_LEASE_QUERY_DEFAULT;
Taras Lemkin96a0d342018-03-26 14:52:58 +0000209
Yi Tsengaa417a62017-09-08 17:22:51 -0700210 protected HostProviderService providerService;
Yi Tseng525ff402017-10-23 19:39:39 -0700211 protected ApplicationId appId;
Charles Chan70fdd492018-03-07 17:36:06 -0800212 protected Multimap<DeviceId, VlanId> ignoredVlans = Multimaps.synchronizedMultimap(HashMultimap.create());
Yi Tseng525ff402017-10-23 19:39:39 -0700213 private InternalHostListener hostListener = new InternalHostListener();
Kalhee Kimba366062017-11-07 16:32:09 +0000214 private Boolean dhcpFpmEnabled = false;
Charles Chan909cff82018-03-05 13:14:02 -0800215 private List<DhcpServerInfo> defaultServerInfoList = new CopyOnWriteArrayList<>();
216 private List<DhcpServerInfo> indirectServerInfoList = new CopyOnWriteArrayList<>();
Jordan Halterman6328db72018-04-10 13:34:50 -0400217
218 private Executor hostEventExecutor = newSingleThreadExecutor(
219 groupedThreads("dhcp6-event-host", "%d", log));
220
Kalhee Kim495c9b22017-11-07 16:32:09 +0000221 private class IpAddressInfo {
222 Ip6Address ip6Address;
223 long prefTime;
224 }
225 private class PdPrefixInfo {
226 IpPrefix pdPrefix;
227 long prefTime;
228 }
229 protected int dhcp6PollInterval = 24 * 3600; // 24 hr period
230
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000231 // max 1 thread
232 static Semaphore recordSemaphore = new Semaphore(1);
Kalhee Kim45fede42017-09-05 19:05:06 +0000233
234 // CLIENT message types
235 public static final Set<Byte> MSG_TYPE_FROM_CLIENT =
236 ImmutableSet.of(DHCP6.MsgType.SOLICIT.value(),
237 DHCP6.MsgType.REQUEST.value(),
238 DHCP6.MsgType.REBIND.value(),
239 DHCP6.MsgType.RENEW.value(),
240 DHCP6.MsgType.RELEASE.value(),
241 DHCP6.MsgType.DECLINE.value(),
242 DHCP6.MsgType.CONFIRM.value(),
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800243 DHCP6.MsgType.RELAY_FORW.value(),
244 DHCP6.MsgType.LEASEQUERY.value());
Kalhee Kim45fede42017-09-05 19:05:06 +0000245 // SERVER message types
246 public static final Set<Byte> MSG_TYPE_FROM_SERVER =
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800247 ImmutableSet.of(DHCP6.MsgType.RELAY_REPL.value(),
248 DHCP6.MsgType.LEASEQUERY_REPLY.value());
Kalhee Kim45fede42017-09-05 19:05:06 +0000249
250 @Activate
Taras Lemkin96a0d342018-03-26 14:52:58 +0000251 protected void activate(ComponentContext context) {
252 cfgService.registerProperties(getClass());
253 modified(context);
Yi Tseng525ff402017-10-23 19:39:39 -0700254 appId = coreService.registerApplication(DHCP_V6_RELAY_APP);
Yi Tsengaa417a62017-09-08 17:22:51 -0700255 providerService = providerRegistry.register(this);
Kalhee Kim45fede42017-09-05 19:05:06 +0000256 hostService.addListener(hostListener);
Yi Tseng51301292017-07-28 13:02:59 -0700257 }
258
Kalhee Kim45fede42017-09-05 19:05:06 +0000259 @Deactivate
260 protected void deactivate() {
Taras Lemkin96a0d342018-03-26 14:52:58 +0000261 cfgService.unregisterProperties(getClass(), false);
Yi Tsengaa417a62017-09-08 17:22:51 -0700262 providerRegistry.unregister(this);
Kalhee Kim45fede42017-09-05 19:05:06 +0000263 hostService.removeListener(hostListener);
Yi Tseng919b2df2017-09-07 16:22:51 -0700264 defaultServerInfoList.forEach(this::stopMonitoringIps);
Charles Chanfc1c22e2018-07-19 09:52:01 -0700265 defaultServerInfoList.forEach(info -> info.getDhcpServerIp6().ifPresent(this::cancelDhcpPacket));
Yi Tseng919b2df2017-09-07 16:22:51 -0700266 defaultServerInfoList.clear();
267 indirectServerInfoList.forEach(this::stopMonitoringIps);
Charles Chanfc1c22e2018-07-19 09:52:01 -0700268 indirectServerInfoList.forEach(info -> info.getDhcpServerIp6().ifPresent(this::cancelDhcpPacket));
Yi Tseng919b2df2017-09-07 16:22:51 -0700269 indirectServerInfoList.clear();
270 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000271
Taras Lemkin96a0d342018-03-26 14:52:58 +0000272 @Modified
273 protected void modified(ComponentContext context) {
274 Dictionary<?, ?> properties = context.getProperties();
275 Boolean flag;
Ray Milkey687c00c2018-10-31 10:18:41 -0700276 flag = Tools.isPropertyEnabled(properties, LEARN_ROUTE_FROM_LEASE_QUERY);
Taras Lemkin96a0d342018-03-26 14:52:58 +0000277 if (flag != null) {
278 learnRouteFromLeasequery = flag;
279 log.info("Learning routes from DHCP leasequery is {}",
280 learnRouteFromLeasequery ? "enabled" : "disabled");
281 }
282 }
283
Yi Tseng919b2df2017-09-07 16:22:51 -0700284 private void stopMonitoringIps(DhcpServerInfo serverInfo) {
285 serverInfo.getDhcpGatewayIp6().ifPresent(gatewayIp -> {
286 hostService.stopMonitoringIp(gatewayIp);
287 });
288 serverInfo.getDhcpServerIp6().ifPresent(serverIp -> {
289 hostService.stopMonitoringIp(serverIp);
290 });
Yi Tseng51301292017-07-28 13:02:59 -0700291 }
292
Yi Tseng51301292017-07-28 13:02:59 -0700293 @Override
Yi Tseng919b2df2017-09-07 16:22:51 -0700294 public List<DhcpServerInfo> getDefaultDhcpServerInfoList() {
295 return defaultServerInfoList;
Yi Tseng51301292017-07-28 13:02:59 -0700296 }
297
298 @Override
Yi Tseng919b2df2017-09-07 16:22:51 -0700299 public List<DhcpServerInfo> getIndirectDhcpServerInfoList() {
300 return indirectServerInfoList;
Yi Tseng51301292017-07-28 13:02:59 -0700301 }
302
303 @Override
Yi Tseng525ff402017-10-23 19:39:39 -0700304 public void updateIgnoreVlanConfig(IgnoreDhcpConfig config) {
305 if (config == null) {
306 ignoredVlans.forEach(((deviceId, vlanId) -> {
307 processIgnoreVlanRule(deviceId, vlanId, REMOVE);
308 }));
309 return;
310 }
311 config.ignoredVlans().forEach((deviceId, vlanId) -> {
312 if (ignoredVlans.get(deviceId).contains(vlanId)) {
313 // don't need to process if it already ignored
314 return;
315 }
316 processIgnoreVlanRule(deviceId, vlanId, ADD);
317 });
Yi Tseng525ff402017-10-23 19:39:39 -0700318 ignoredVlans.forEach((deviceId, vlanId) -> {
319 if (!config.ignoredVlans().get(deviceId).contains(vlanId)) {
320 // not contains in new config, remove it
321 processIgnoreVlanRule(deviceId, vlanId, REMOVE);
322 }
323 });
324 }
325
326 @Override
Saurav Dasb805f1a2017-12-13 16:19:35 -0800327 public void removeIgnoreVlanState(IgnoreDhcpConfig config) {
328 if (config == null) {
329 ignoredVlans.clear();
330 return;
331 }
332 config.ignoredVlans().forEach((deviceId, vlanId) -> {
333 ignoredVlans.remove(deviceId, vlanId);
334 });
335 }
336
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800337 public DhcpRecord getDhcpRelayRecordFor(Ip6Address clientAddress) {
338
Taras Lemkin96a0d342018-03-26 14:52:58 +0000339 Collection<DhcpRecord> records = dhcpRelayStore.getDhcpRecords();
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800340 DhcpRecord dr = null;
341 for (DhcpRecord e:records) {
342 if (e.ip6Address().isPresent()) {
343 if (e.ip6Address().get().equals(clientAddress)) {
344 dr = e;
345 break;
346 }
347 }
348 }
349 return dr;
350 }
351
352 public MacAddress findNextHopMacForIp6FromRelayStore(Ip6Address clientAddress,
353 MacAddress clientMacAddress, VlanId vlanID) {
354
355 DhcpRecord dr = getDhcpRelayRecordFor(clientAddress);
356
357 if (dr != null) {
358 Optional<MacAddress> nextHopTempMac = dr.nextHopTemp();
359 if (nextHopTempMac.isPresent()) {
360 log.info("findNextHopForIp6FromRelayStore " + clientAddress + " got mac " + nextHopTempMac.toString());
361 return nextHopTempMac.get();
362 }
363 } else {
364 log.warn("findNextHopMacForIp6FromRelayStore could NOT find next hop for " + clientAddress);
365 return null;
366 }
367 return null;
368 }
369
370 public Ip6Address findNextHopIp6FromRelayStore(Ip6Address clientAddress) {
371
372 DhcpRecord dr = getDhcpRelayRecordFor(clientAddress);
373 if (dr != null) {
374 Optional<MacAddress> nextHopMac = dr.nextHop();
375 if (nextHopMac.isPresent()) {
376 // find the local ip6 from the host store
377 HostId gwHostId = HostId.hostId(nextHopMac.get(), dr.vlanId());
378 Host gwHost = hostService.getHost(gwHostId);
379 if (gwHost == null) {
380 log.warn("Can't find next hop host ID {}", gwHostId);
381 return null;
382 }
383 Ip6Address nextHopIp = gwHost.ipAddresses()
384 .stream()
385 .filter(IpAddress::isIp6)
386 .filter(IpAddress::isLinkLocal)
387 .map(IpAddress::getIp6Address)
388 .findFirst()
389 .orElse(null);
390
391 log.info("findNextHopIp6FromRelayStore " + clientAddress + " got mac " +
392 nextHopMac.toString() + " ip6 " + nextHopIp);
393 return nextHopIp;
394 }
395 } else {
396 log.warn("findNextHopIp6FromRelayStore could NOT find next hop for " + clientAddress);
397 return null;
398 }
399 return null;
400 }
401
402 private void setPotentialNextHopForIp6InRelayStore(Ip6Address clientAddress,
403 VlanId vlanId, MacAddress nh) {
404 DhcpRecord dr = getDhcpRelayRecordFor(clientAddress);
405 if (dr != null) {
406 dr.nextHopTemp(nh);
407 log.debug("LQ6 potential NH mac " + nh.toString() + " UPDATED in RelayRecord client " + clientAddress);
408 } else {
409 log.warn("LQ6 potential NH mac" + nh.toString() +
410 " NOT FOUND in RelayRecord for client - LQ rejected" + clientAddress);
411 }
412 }
413
414 public void handleLeaseQuery6ReplyMsg(PacketContext context, DHCP6 dhcp6Payload) {
415 ConnectPoint inPort = context.inPacket().receivedFrom();
416 log.info("Got LQV6-REPLY on port {}", inPort);
417 List<Dhcp6Option> lopt = dhcp6Payload.getOptions();
418 log.info("Options list: {}", lopt);
419 // find out if this lease is known is
420 Dhcp6ClientDataOption clientDataOption = dhcp6Payload.getOptions()
421 .stream()
422 .filter(opt -> opt instanceof Dhcp6ClientDataOption)
423 .map(pld -> (Dhcp6ClientDataOption) pld)
424 .findFirst()
425 .orElse(null);
426
427 if (clientDataOption == null) {
428 log.warn("clientDataOption option is not present, " +
429 "lease is UNKNOWN - not adding any new route...");
430 } else {
431 Dhcp6IaAddressOption aiAddressOption = clientDataOption.getOptions()
432 .stream()
433 .filter(opt -> opt instanceof Dhcp6IaAddressOption)
434 .map(pld -> (Dhcp6IaAddressOption) pld)
435 .findFirst()
436 .orElse(null);
437
438 Dhcp6ClientIdOption clientIdOption = clientDataOption.getOptions()
439 .stream()
440 .filter(opt -> opt instanceof Dhcp6ClientIdOption)
441 .map(pld -> (Dhcp6ClientIdOption) pld)
442 .findFirst()
443 .orElse(null);
444
445 if (aiAddressOption == null) {
446 log.warn("clientDataOption from DHCP server does not " +
447 "contains Dhcp6IaAddressOption for the client - giving up...");
448 } else {
449 Ip6Address clientAddress = aiAddressOption.getIp6Address();
450 MacAddress clientMacAddress = MacAddress.valueOf(clientIdOption.getDuid().getLinkLayerAddress());
451 Ethernet packet = context.inPacket().parsed();
452 VlanId vlanId = VlanId.vlanId(packet.getVlanID());
453 MacAddress potentialNextHopMac =
454 findNextHopMacForIp6FromRelayStore(clientAddress, clientMacAddress, vlanId);
455
456 if (potentialNextHopMac == null) {
457 log.warn("Can't find next hop host mac for client {} mac:{}/{}",
458 clientAddress, clientMacAddress, vlanId);
459 return;
460 } else {
461 log.info("Next hop mac for {}/{}/{} is {}", clientAddress,
462 clientMacAddress, vlanId, potentialNextHopMac.toString());
463 }
464 // search the next hop in the hosts store
465 HostId gwHostId = HostId.hostId(potentialNextHopMac, vlanId);
466 Host gwHost = hostService.getHost(gwHostId);
467 if (gwHost == null) {
468 log.warn("Can't find next hop host ID {}", gwHostId);
469 return;
470 }
471 Ip6Address nextHopIp = gwHost.ipAddresses()
472 .stream()
473 .filter(IpAddress::isIp6)
474 .filter(IpAddress::isLinkLocal)
475 .map(IpAddress::getIp6Address)
476 .findFirst()
477 .orElse(null);
478 if (nextHopIp == null) {
479 log.warn("Can't find IP6 address of next hop {}", gwHost);
480 return;
481 }
482 log.info("client " + clientAddress + " is known !");
Kalhee Kim54a97c92018-04-02 21:23:46 +0000483 Route routeForIP6 = new Route(Route.Source.DHCP, clientAddress.toIpPrefix(), nextHopIp);
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800484 log.debug("updating route of Client for indirectly connected.");
485 log.debug("client ip: " + clientAddress + ", next hop ip6: " + nextHopIp);
486 routeStore.updateRoute(routeForIP6);
487 }
488 }
489 }
490
Saurav Dasb805f1a2017-12-13 16:19:35 -0800491 @Override
Kalhee Kim45fede42017-09-05 19:05:06 +0000492 public void processDhcpPacket(PacketContext context, BasePacket payload) {
493 checkNotNull(payload, "DHCP6 payload can't be null");
494 checkState(payload instanceof DHCP6, "Payload is not a DHCP6");
495 DHCP6 dhcp6Payload = (DHCP6) payload;
496 Ethernet receivedPacket = context.inPacket().parsed();
497
498 if (!configured()) {
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800499 log.warn("Missing DHCP6 relay server config. " +
500 "Abort packet processing dhcp6 payload {}", dhcp6Payload);
Kalhee Kim45fede42017-09-05 19:05:06 +0000501 return;
502 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000503 byte msgTypeVal = dhcp6Payload.getMsgType();
504 MsgType msgType = DHCP6.MsgType.getType(msgTypeVal);
505 log.debug("msgType is {}", msgType);
Kalhee Kim45fede42017-09-05 19:05:06 +0000506
Kalhee Kim45fede42017-09-05 19:05:06 +0000507 ConnectPoint inPort = context.inPacket().receivedFrom();
Jonathan Hart8ca2bc02017-11-30 18:23:42 -0800508
Kalhee Kim495c9b22017-11-07 16:32:09 +0000509 if (inPort == null) {
Taras Lemkin96a0d342018-03-26 14:52:58 +0000510 log.warn("incoming ConnectPoint is null");
Kalhee Kim495c9b22017-11-07 16:32:09 +0000511 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000512 Set<Interface> receivingInterfaces = interfaceService.getInterfacesByPort(inPort);
513 //ignore the packets if dhcp client interface is not configured on onos.
514 if (receivingInterfaces.isEmpty()) {
515 log.warn("Virtual interface is not configured on {}", inPort);
516 return;
517 }
518
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800519 if (msgTypeVal == DHCP6.MsgType.LEASEQUERY.value()) {
Taras Lemkin96a0d342018-03-26 14:52:58 +0000520 List<InternalPacket> ethernetClientPackets =
521 learnRouteFromLeasequery ?
522 processLQ6PacketFromClient(context, receivedPacket, receivingInterfaces, dhcp6Payload) :
523 processDhcp6ForwardOnly(context, receivedPacket, receivingInterfaces, dhcp6Payload);
524 for (InternalPacket internalPacket : ethernetClientPackets) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000525 forwardPacket(internalPacket);
Kalhee Kim45fede42017-09-05 19:05:06 +0000526 }
Taras Lemkin96a0d342018-03-26 14:52:58 +0000527 } else if (msgTypeVal == DHCP6.MsgType.LEASEQUERY_REPLY.value() && learnRouteFromLeasequery) {
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800528 IPv6 clientIpv6 = (IPv6) receivedPacket.getPayload();
529 UDP clientUdp = (UDP) clientIpv6.getPayload();
530 DHCP6 clientDhcp6 = (DHCP6) clientUdp.getPayload();
Taras Lemkin96a0d342018-03-26 14:52:58 +0000531 Interface serverInterface = Dhcp6HandlerUtil.directlyConnected(clientDhcp6) ?
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800532 getServerInterface() : getIndirectServerInterface();
Kalhee Kim45fede42017-09-05 19:05:06 +0000533 InternalPacket ethernetPacketReply =
Taras Lemkin96a0d342018-03-26 14:52:58 +0000534 Dhcp6HandlerUtil.processLQ6PacketFromServer(
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800535 defaultServerInfoList, indirectServerInfoList,
536 serverInterface, interfaceService,
537 hostService,
538 context, receivedPacket, receivingInterfaces);
Kalhee Kim45fede42017-09-05 19:05:06 +0000539 if (ethernetPacketReply != null) {
540 forwardPacket(ethernetPacketReply);
541 }
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800542 handleLeaseQuery6ReplyMsg(context, dhcp6Payload);
Taras Lemkin96a0d342018-03-26 14:52:58 +0000543 } else if (MSG_TYPE_FROM_CLIENT.contains(msgTypeVal)) {
544 List<InternalPacket> ethernetClientPacket =
545 processDhcp6PacketFromClient(context, receivedPacket, receivingInterfaces);
546 for (InternalPacket internalPacket : ethernetClientPacket) {
547 forwardPacket(internalPacket);
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800548 }
Taras Lemkin96a0d342018-03-26 14:52:58 +0000549 } else if (MSG_TYPE_FROM_SERVER.contains(msgTypeVal)) {
550 log.debug("calling processDhcp6PacketFromServer with RELAY_REPL {}, {}", receivedPacket, dhcp6Payload);
551 InternalPacket ethernetPacketReply =
552 processDhcp6PacketFromServer(context, receivedPacket, receivingInterfaces);
553 if (ethernetPacketReply != null) {
554 forwardPacket(ethernetPacketReply);
555 }
556 } else {
557 log.warn("Not so fast, packet type {} not supported yet", msgTypeVal);
Kalhee Kim45fede42017-09-05 19:05:06 +0000558 }
559 }
560
Kalhee Kim45fede42017-09-05 19:05:06 +0000561 /**
562 * Checks if this app has been configured.
563 *
564 * @return true if all information we need have been initialized
565 */
566 public boolean configured() {
Yi Tseng919b2df2017-09-07 16:22:51 -0700567 return !defaultServerInfoList.isEmpty();
Kalhee Kim45fede42017-09-05 19:05:06 +0000568 }
569
Yi Tsengaa417a62017-09-08 17:22:51 -0700570 @Override
571 public ProviderId id() {
Charles Chand988c282017-09-12 17:09:32 -0700572 return PROVIDER_ID;
Yi Tsengaa417a62017-09-08 17:22:51 -0700573 }
574
575 @Override
576 public void triggerProbe(Host host) {
577 // Do nothing here
578 }
579
Kalhee Kim45fede42017-09-05 19:05:06 +0000580 //forward the packet to ConnectPoint where the DHCP server is attached.
581 private void forwardPacket(InternalPacket packet) {
582 //send Packetout to dhcp server connectpoint.
Taras Lemkin96a0d342018-03-26 14:52:58 +0000583 if (packet.getDestLocation() != null) {
Kalhee Kim45fede42017-09-05 19:05:06 +0000584 TrafficTreatment t = DefaultTrafficTreatment.builder()
Taras Lemkin96a0d342018-03-26 14:52:58 +0000585 .setOutput(packet.getDestLocation().port()).build();
Kalhee Kim45fede42017-09-05 19:05:06 +0000586 OutboundPacket o = new DefaultOutboundPacket(
Taras Lemkin96a0d342018-03-26 14:52:58 +0000587 packet.getDestLocation().deviceId(), t, ByteBuffer.wrap(packet.getPacket().serialize()));
Kalhee Kim45fede42017-09-05 19:05:06 +0000588 packetService.emit(o);
Taras Lemkin96a0d342018-03-26 14:52:58 +0000589 if (log.isTraceEnabled()) {
590 IPv6 ip6 = (IPv6) packet.getPacket().getPayload();
591 UDP udp = (UDP) ip6.getPayload();
592 DHCP6 dhcp6 = (DHCP6) udp.getPayload();
593 log.trace("Relaying packet to destination {} eth: {} dhcp: {}",
594 packet.getDestLocation(), packet.getPacket(), dhcp6);
595 }
596
597 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000598 }
599
Kalhee Kim45fede42017-09-05 19:05:06 +0000600 /**
601 * extract from dhcp6 packet client ipv6 address of given by dhcp server.
602 *
603 * @param dhcp6 the dhcp6 packet
Kalhee Kim495c9b22017-11-07 16:32:09 +0000604 * @return IpAddressInfo IpAddressInfo given by dhcp server, or null if not exists
Kalhee Kim45fede42017-09-05 19:05:06 +0000605 */
Kalhee Kim495c9b22017-11-07 16:32:09 +0000606 private IpAddressInfo extractIpAddress(DHCP6 dhcp6) {
607 IpAddressInfo ipInfo = new IpAddressInfo();
Kalhee Kim45fede42017-09-05 19:05:06 +0000608
609 log.debug("extractIpAddress enters dhcp6 {}.", dhcp6);
610 // Extract IPv6 address from IA NA ot IA TA option
611 Optional<Dhcp6IaNaOption> iaNaOption = dhcp6.getOptions()
612 .stream()
613 .filter(opt -> opt instanceof Dhcp6IaNaOption)
614 .map(opt -> (Dhcp6IaNaOption) opt)
615 .findFirst();
616 Optional<Dhcp6IaTaOption> iaTaOption = dhcp6.getOptions()
617 .stream()
618 .filter(opt -> opt instanceof Dhcp6IaTaOption)
619 .map(opt -> (Dhcp6IaTaOption) opt)
620 .findFirst();
621 Optional<Dhcp6IaAddressOption> iaAddressOption;
622 if (iaNaOption.isPresent()) {
623 log.debug("Found IPv6 address from iaNaOption {}", iaNaOption);
624
625 iaAddressOption = iaNaOption.get().getOptions().stream()
626 .filter(opt -> opt instanceof Dhcp6IaAddressOption)
627 .map(opt -> (Dhcp6IaAddressOption) opt)
628 .findFirst();
629 } else if (iaTaOption.isPresent()) {
630 log.debug("Found IPv6 address from iaTaOption {}", iaTaOption);
631
632 iaAddressOption = iaTaOption.get().getOptions().stream()
633 .filter(opt -> opt instanceof Dhcp6IaAddressOption)
634 .map(opt -> (Dhcp6IaAddressOption) opt)
635 .findFirst();
636 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000637 log.info("No IPv6 address found from iaTaOption {}", iaTaOption);
Kalhee Kim45fede42017-09-05 19:05:06 +0000638 iaAddressOption = Optional.empty();
639 }
640 if (iaAddressOption.isPresent()) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000641 ipInfo.ip6Address = iaAddressOption.get().getIp6Address();
642 ipInfo.prefTime = iaAddressOption.get().getPreferredLifetime();
Kalhee Kim45fede42017-09-05 19:05:06 +0000643 log.debug("Found IPv6 address from iaAddressOption {}", iaAddressOption);
Kalhee Kim45fede42017-09-05 19:05:06 +0000644 } else {
645 log.debug("Can't find IPv6 address from DHCPv6 {}", dhcp6);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000646 return null;
Kalhee Kim45fede42017-09-05 19:05:06 +0000647 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000648 return ipInfo;
Kalhee Kim45fede42017-09-05 19:05:06 +0000649 }
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800650
Kalhee Kim45fede42017-09-05 19:05:06 +0000651 /**
652 * extract from dhcp6 packet Prefix prefix provided by dhcp server.
653 *
654 * @param dhcp6 the dhcp6 payload
655 * @return IpPrefix Prefix Delegation prefix, or null if not exists.
656 */
Kalhee Kim495c9b22017-11-07 16:32:09 +0000657 private PdPrefixInfo extractPrefix(DHCP6 dhcp6) {
658 log.debug("extractPrefix enters {}", dhcp6);
Kalhee Kim45fede42017-09-05 19:05:06 +0000659
660 // extract prefix
Kalhee Kim495c9b22017-11-07 16:32:09 +0000661 PdPrefixInfo pdPrefixInfo = new PdPrefixInfo();
Kalhee Kim45fede42017-09-05 19:05:06 +0000662
663 Ip6Address prefixAddress = null;
664
665 // Extract IPv6 prefix from IA PD option
666 Optional<Dhcp6IaPdOption> iaPdOption = dhcp6.getOptions()
667 .stream()
668 .filter(opt -> opt instanceof Dhcp6IaPdOption)
669 .map(opt -> (Dhcp6IaPdOption) opt)
670 .findFirst();
671
672 Optional<Dhcp6IaPrefixOption> iaPrefixOption;
673 if (iaPdOption.isPresent()) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000674 log.debug("IA_PD option found {}", iaPdOption);
Kalhee Kim45fede42017-09-05 19:05:06 +0000675
676 iaPrefixOption = iaPdOption.get().getOptions().stream()
677 .filter(opt -> opt instanceof Dhcp6IaPrefixOption)
678 .map(opt -> (Dhcp6IaPrefixOption) opt)
679 .findFirst();
680 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000681 log.debug("IA_PD option NOT found");
Kalhee Kim45fede42017-09-05 19:05:06 +0000682
683 iaPrefixOption = Optional.empty();
684 }
685 if (iaPrefixOption.isPresent()) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000686 log.debug("IAPrefix Option within IA_PD option found {}", iaPrefixOption);
Kalhee Kim45fede42017-09-05 19:05:06 +0000687
688 prefixAddress = iaPrefixOption.get().getIp6Prefix();
689 int prefixLen = (int) iaPrefixOption.get().getPrefixLength();
Kalhee Kim495c9b22017-11-07 16:32:09 +0000690 log.debug("Prefix length is {} bits", prefixLen);
691 pdPrefixInfo.pdPrefix = IpPrefix.valueOf(prefixAddress, prefixLen);
692 pdPrefixInfo.prefTime = iaPrefixOption.get().getPreferredLifetime();
Kalhee Kim45fede42017-09-05 19:05:06 +0000693 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000694 log.debug("Can't find IPv6 prefix from DHCPv6 {}", dhcp6);
695 return null;
Kalhee Kim45fede42017-09-05 19:05:06 +0000696 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000697 return pdPrefixInfo;
Kalhee Kim45fede42017-09-05 19:05:06 +0000698 }
699
700 /**
Kalhee Kim495c9b22017-11-07 16:32:09 +0000701 * remove host or route and update dhcp relay record attributes.
Kalhee Kim45fede42017-09-05 19:05:06 +0000702 *
703 * @param directConnFlag flag to show that packet is from directly connected client
Kalhee Kim495c9b22017-11-07 16:32:09 +0000704 * @param location client side connect point
Kalhee Kim45fede42017-09-05 19:05:06 +0000705 * @param dhcp6Packet the dhcp6 payload
706 * @param clientPacket client's ethernet packet
707 * @param clientIpv6 client's Ipv6 packet
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +0000708 * @param clientInterface client interfaces
Kalhee Kim45fede42017-09-05 19:05:06 +0000709 */
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000710 private void removeHostOrRoute(boolean directConnFlag, ConnectPoint location,
711 DHCP6 dhcp6Packet,
Kalhee Kim45fede42017-09-05 19:05:06 +0000712 Ethernet clientPacket, IPv6 clientIpv6,
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +0000713 Interface clientInterface) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000714 log.debug("removeHostOrRoute enters {}", dhcp6Packet);
Kalhee Kim121ba922017-11-01 17:56:44 +0000715 VlanId vlanId = clientInterface.vlan();
Kalhee Kim495c9b22017-11-07 16:32:09 +0000716 MacAddress srcMac = clientPacket.getSourceMAC(); // could be gw or host
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000717 MacAddress leafClientMac;
718 byte leafMsgType;
Kalhee Kim495c9b22017-11-07 16:32:09 +0000719 log.debug("client mac {} client vlan {}", HexString.toHexString(srcMac.toBytes(), ":"), vlanId);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000720
Charles Chan24a96ff2018-08-13 10:32:03 -0700721 Dhcp6ClientIdOption clientIdOption = Dhcp6HandlerUtil.extractClientId(directConnFlag, dhcp6Packet);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000722 if (clientIdOption != null) {
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000723 if ((clientIdOption.getDuid().getDuidType() == Dhcp6Duid.DuidType.DUID_LLT) ||
724 (clientIdOption.getDuid().getDuidType() == Dhcp6Duid.DuidType.DUID_LL)) {
725 leafClientMac = MacAddress.valueOf(clientIdOption.getDuid().getLinkLayerAddress());
726 } else {
727 log.warn("Link-Layer Address not supported in CLIENTID option. No DhcpRelay Record created.");
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000728 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_LINKLOCAL_FAIL);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000729 return;
730 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000731 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000732 log.warn("CLIENTID option NOT found. Don't create DhcpRelay Record.");
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000733 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_CLIENTID_FAIL);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000734 return;
735 }
736
Kalhee Kim495c9b22017-11-07 16:32:09 +0000737 HostId leafHostId = HostId.hostId(leafClientMac, vlanId);
738 DhcpRecord record = dhcpRelayStore.getDhcpRecord(leafHostId).orElse(null);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000739 if (record == null) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000740 record = new DhcpRecord(leafHostId);
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000741 } else {
742 record = record.clone();
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000743 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000744
Taras Lemkin96a0d342018-03-26 14:52:58 +0000745 Boolean isMsgRelease = Dhcp6HandlerUtil.isDhcp6Release(dhcp6Packet);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000746 IpAddressInfo ipInfo;
747 PdPrefixInfo pdInfo = null;
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000748 if (directConnFlag) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000749 // Add to host store if it is connected to network directly
750 ipInfo = extractIpAddress(dhcp6Packet);
751 if (ipInfo != null) {
752 if (isMsgRelease) {
753 HostId hostId = HostId.hostId(srcMac, vlanId);
754 log.debug("remove Host {} ip for directly connected.", hostId.toString());
755 providerService.removeIpFromHost(hostId, ipInfo.ip6Address);
Kalhee Kim45fede42017-09-05 19:05:06 +0000756 }
757 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000758 log.debug("ipAddress not found. Do not remove Host {} for directly connected.",
759 HostId.hostId(srcMac, vlanId).toString());
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000760 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000761 leafMsgType = dhcp6Packet.getMsgType();
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000762 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000763 // Remove from route store if it is not connected to network directly
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000764 // pick out the first link-local ip address
Kalhee Kim495c9b22017-11-07 16:32:09 +0000765 IpAddress nextHopIp = getFirstIpByHost(directConnFlag, srcMac, vlanId);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000766 if (nextHopIp == null) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000767 log.warn("Can't find link-local IP address of gateway mac {} vlanId {}", srcMac, vlanId);
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000768 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_LINKLOCAL_GW);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000769 return;
770 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000771
Taras Lemkin96a0d342018-03-26 14:52:58 +0000772 DHCP6 leafDhcp = Dhcp6HandlerUtil.getDhcp6Leaf(dhcp6Packet);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000773 ipInfo = extractIpAddress(leafDhcp);
774 if (ipInfo == null) {
775 log.debug("ip is null");
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000776 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000777 if (isMsgRelease) {
Kalhee Kim54a97c92018-04-02 21:23:46 +0000778 Route routeForIP = new Route(Route.Source.DHCP, ipInfo.ip6Address.toIpPrefix(), nextHopIp);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000779 log.debug("removing route of 128 address for indirectly connected.");
780 log.debug("128 ip {}, nexthop {}",
781 HexString.toHexString(ipInfo.ip6Address.toOctets(), ":"),
782 HexString.toHexString(nextHopIp.toOctets(), ":"));
783 routeStore.removeRoute(routeForIP);
Kalhee Kim45fede42017-09-05 19:05:06 +0000784 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000785 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000786
Kalhee Kim495c9b22017-11-07 16:32:09 +0000787 pdInfo = extractPrefix(leafDhcp);
788 if (pdInfo == null) {
789 log.debug("ipPrefix is null ");
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000790 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000791 if (isMsgRelease) {
Kalhee Kim54a97c92018-04-02 21:23:46 +0000792 Route routeForPrefix = new Route(Route.Source.DHCP, pdInfo.pdPrefix, nextHopIp);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000793 log.debug("removing route of PD for indirectly connected.");
794 log.debug("pd ip {}, nexthop {}",
795 HexString.toHexString(pdInfo.pdPrefix.address().toOctets(), ":"),
796 HexString.toHexString(nextHopIp.toOctets(), ":"));
797
798 routeStore.removeRoute(routeForPrefix);
Kalhee Kimba366062017-11-07 16:32:09 +0000799 if (this.dhcpFpmEnabled) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000800 dhcpFpmPrefixStore.removeFpmRecord(pdInfo.pdPrefix);
Kalhee Kimba366062017-11-07 16:32:09 +0000801 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000802 }
803 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000804 leafMsgType = leafDhcp.getMsgType();
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000805 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000806
Kalhee Kim495c9b22017-11-07 16:32:09 +0000807 if (isMsgRelease) {
808 log.debug("DHCP6 RELEASE msg.");
809 if (record != null) {
810 if (ipInfo != null) {
811 log.debug("DhcpRelay Record ip6Address is set to null.");
812 record.ip6Address(null);
813 }
814 if (pdInfo != null) {
815 log.debug("DhcpRelay Record pdPrefix is set to null.");
816 }
817
818 if (!record.ip6Address().isPresent() && !record.pdPrefix().isPresent()) {
819 log.warn("IP6 address and IP6 PD both are null. Remove record.");
820 // do not remove a record. Let timer task handler it.
821 //dhcpRelayStore.removeDhcpRecord(HostId.hostId(leafClientMac, vlanId));
822 }
823 }
824 }
825
Ray Milkeyffe1a332018-01-24 10:41:14 -0800826 if (record != null) {
Taras Lemkin96a0d342018-03-26 14:52:58 +0000827 record.getV6Counters().incrementCounter(Dhcp6HandlerUtil.getMsgTypeStr(leafMsgType));
Ray Milkeyffe1a332018-01-24 10:41:14 -0800828 record.addLocation(new HostLocation(location, System.currentTimeMillis()));
829 record.ip6Status(DHCP6.MsgType.getType(leafMsgType));
830 record.setDirectlyConnected(directConnFlag);
831 if (!directConnFlag) {
832 // Update gateway mac address if the host is not directly connected
833 record.nextHop(srcMac);
834 }
835 record.updateLastSeen();
Kalhee Kim495c9b22017-11-07 16:32:09 +0000836 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000837 dhcpRelayStore.updateDhcpRecord(leafHostId, record);
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000838 // TODO Use AtomicInteger for the counters
839 try {
840 recordSemaphore.acquire();
841 try {
Taras Lemkin96a0d342018-03-26 14:52:58 +0000842 dhcpRelayCountersStore.incrementCounter(gCount, Dhcp6HandlerUtil.getMsgTypeStr(leafMsgType));
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000843 } finally {
844 // calling release() after a successful acquire()
845 recordSemaphore.release();
846 }
847 } catch (InterruptedException e) {
Ray Milkeyba547f92018-02-01 15:22:31 -0800848 Thread.currentThread().interrupt();
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000849 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000850 }
851
852 /**
853 * add host or route and update dhcp relay record.
854 *
855 * @param directConnFlag flag to show that packet is from directly connected client
856 * @param location client side connect point
857 * @param dhcp6Relay the dhcp6 payload
858 * @param embeddedDhcp6 the dhcp6 payload within relay
859 * @param srcMac client gw/host macAddress
860 * @param clientInterface client interface
861 */
862 private void addHostOrRoute(boolean directConnFlag, ConnectPoint location, DHCP6 dhcp6Relay,
863 DHCP6 embeddedDhcp6, MacAddress srcMac, Interface clientInterface) {
864 log.debug("addHostOrRoute entered.");
865 VlanId vlanId = clientInterface.vlan();
Taras Lemkin96a0d342018-03-26 14:52:58 +0000866 Boolean isMsgReply = Dhcp6HandlerUtil.isDhcp6Reply(dhcp6Relay);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000867 MacAddress leafClientMac;
868 Byte leafMsgType;
869
Charles Chan24a96ff2018-08-13 10:32:03 -0700870 Dhcp6ClientIdOption clientIdOption = Dhcp6HandlerUtil.extractClientId(directConnFlag, embeddedDhcp6);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000871 if (clientIdOption != null) {
872 log.debug("CLIENTID option found {}", clientIdOption);
873 if ((clientIdOption.getDuid().getDuidType() == Dhcp6Duid.DuidType.DUID_LLT) ||
874 (clientIdOption.getDuid().getDuidType() == Dhcp6Duid.DuidType.DUID_LL)) {
875 leafClientMac = MacAddress.valueOf(clientIdOption.getDuid().getLinkLayerAddress());
876 } else {
877 log.warn("Link-Layer Address not supported in CLIENTID option. No DhcpRelay Record created.");
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000878 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_LINKLOCAL_FAIL);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000879 return;
880 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000881 } else {
882 log.warn("CLIENTID option NOT found. No DhcpRelay Record created.");
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000883 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_CLIENTID_FAIL);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000884 return;
885 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000886 HostId leafHostId = HostId.hostId(leafClientMac, vlanId);
887 DhcpRecord record = dhcpRelayStore.getDhcpRecord(leafHostId).orElse(null);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000888 if (record == null) {
889 record = new DhcpRecord(HostId.hostId(leafClientMac, vlanId));
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000890 } else {
891 record = record.clone();
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000892 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000893
894 IpAddressInfo ipInfo;
895 PdPrefixInfo pdInfo = null;
896 if (directConnFlag) {
897 // Add to host store if it connect to network directly
898 ipInfo = extractIpAddress(embeddedDhcp6);
899 if (ipInfo != null) {
900 if (isMsgReply) {
901 Set<IpAddress> ips = Sets.newHashSet(ipInfo.ip6Address);
902 HostId hostId = HostId.hostId(srcMac, vlanId);
903 Host host = hostService.getHost(hostId);
904 HostLocation hostLocation = new HostLocation(clientInterface.connectPoint(),
905 System.currentTimeMillis());
906 Set<HostLocation> hostLocations = Sets.newHashSet(hostLocation);
907 if (host != null) {
908 // Dual homing support:
909 // if host exists, use old locations and new location
910 hostLocations.addAll(host.locations());
911 }
912 HostDescription desc = new DefaultHostDescription(srcMac, vlanId, hostLocations, ips,
913 false);
914 log.debug("adding Host for directly connected.");
915 log.debug("client mac {} client vlan {} hostlocation {}",
916 HexString.toHexString(srcMac.toBytes(), ":"), vlanId, hostLocation.toString());
917 // Replace the ip when dhcp server give the host new ip address
918 providerService.hostDetected(hostId, desc, false);
919 }
920 } else {
921 log.warn("ipAddress not found. Do not add Host {} for directly connected.",
922 HostId.hostId(srcMac, vlanId).toString());
923 }
924 leafMsgType = embeddedDhcp6.getMsgType();
925 } else {
926 // Add to route store if it does not connect to network directly
927 // pick out the first link-local ip address
928 IpAddress nextHopIp = getFirstIpByHost(directConnFlag, srcMac, vlanId);
929 if (nextHopIp == null) {
930 log.warn("Can't find link-local IP address of gateway mac {} vlanId {}", srcMac, vlanId);
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000931 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_LINKLOCAL_GW);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000932 return;
933 }
934
Taras Lemkin96a0d342018-03-26 14:52:58 +0000935 DHCP6 leafDhcp = Dhcp6HandlerUtil.getDhcp6Leaf(embeddedDhcp6);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000936 ipInfo = extractIpAddress(leafDhcp);
937 if (ipInfo == null) {
938 log.debug("ip is null");
939 } else {
940 if (isMsgReply) {
Kalhee Kim54a97c92018-04-02 21:23:46 +0000941 Route routeForIP = new Route(Route.Source.DHCP, ipInfo.ip6Address.toIpPrefix(), nextHopIp);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000942 log.debug("adding Route of 128 address for indirectly connected.");
Daniel Ginsburg83b76452018-06-09 01:43:59 +0300943 routeStore.replaceRoute(routeForIP);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000944 }
945 }
946
947 pdInfo = extractPrefix(leafDhcp);
948 if (pdInfo == null) {
949 log.debug("ipPrefix is null ");
950 } else {
951 if (isMsgReply) {
Kalhee Kim54a97c92018-04-02 21:23:46 +0000952 Route routeForPrefix = new Route(Route.Source.DHCP, pdInfo.pdPrefix, nextHopIp);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000953 log.debug("adding Route of PD for indirectly connected.");
Daniel Ginsburg83b76452018-06-09 01:43:59 +0300954 routeStore.replaceRoute(routeForPrefix);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000955 if (this.dhcpFpmEnabled) {
956 FpmRecord fpmRecord = new FpmRecord(pdInfo.pdPrefix, nextHopIp, FpmRecord.Type.DHCP_RELAY);
957 dhcpFpmPrefixStore.addFpmRecord(pdInfo.pdPrefix, fpmRecord);
958 }
959 }
960 }
961 leafMsgType = leafDhcp.getMsgType();
962 }
963 if (leafMsgType == DHCP6.MsgType.RELEASE.value() ||
964 (leafMsgType == DHCP6.MsgType.REPLY.value()) && ipInfo == null) {
965 log.warn("DHCP6 RELEASE/REPLY(null ip) from Server. MsgType {}", leafMsgType);
966 //return;
967 }
968
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000969 record.addLocation(new HostLocation(location, System.currentTimeMillis()));
Kalhee Kim2f07c602017-11-15 18:57:53 +0000970
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000971 if (leafMsgType == DHCP6.MsgType.REPLY.value()) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000972 if (ipInfo != null) {
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000973 log.debug("IP6 address is being stored into dhcp-relay store.");
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800974 log.debug("Client IP6 address {}", HexString.toHexString(ipInfo.ip6Address.toOctets(), ":"));
Kalhee Kim495c9b22017-11-07 16:32:09 +0000975 record.ip6Address(ipInfo.ip6Address);
Kalhee Kim2f07c602017-11-15 18:57:53 +0000976 record.updateAddrPrefTime(ipInfo.prefTime);
977 record.updateLastIp6Update();
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800978 } else {
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000979 log.debug("IP6 address is not returned from server. Maybe only PD is returned.");
980 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000981 if (pdInfo != null) {
982 log.debug("IP6 PD address {}",
983 HexString.toHexString(pdInfo.pdPrefix.address().toOctets(), ":"));
Kalhee Kim2f07c602017-11-15 18:57:53 +0000984 record.pdPrefix(pdInfo.pdPrefix);
985 record.updatePdPrefTime(pdInfo.prefTime);
986 record.updateLastPdUpdate();
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000987 } else {
988 log.debug("IP6 PD address is not returned from server. Maybe only IPAddress is returned.");
989 }
990 }
Kalhee Kim2f07c602017-11-15 18:57:53 +0000991
Taras Lemkin96a0d342018-03-26 14:52:58 +0000992 record.getV6Counters().incrementCounter(Dhcp6HandlerUtil.getMsgTypeStr(leafMsgType));
Kalhee Kim495c9b22017-11-07 16:32:09 +0000993 record.ip6Status(DHCP6.MsgType.getType(leafMsgType));
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000994 record.setDirectlyConnected(directConnFlag);
995 record.updateLastSeen();
Kalhee Kim495c9b22017-11-07 16:32:09 +0000996 dhcpRelayStore.updateDhcpRecord(leafHostId, record);
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000997 // TODO Use AtomicInteger for the counters
998 try {
999 recordSemaphore.acquire();
1000 try {
Taras Lemkin96a0d342018-03-26 14:52:58 +00001001 dhcpRelayCountersStore.incrementCounter(gCount, Dhcp6HandlerUtil.getMsgTypeStr(leafMsgType));
Kalhee Kimd94ceea2017-11-29 19:03:02 +00001002 } finally {
1003 // calling release() after a successful acquire()
1004 recordSemaphore.release();
1005 }
1006 } catch (InterruptedException e) {
Ray Milkeyba547f92018-02-01 15:22:31 -08001007 Thread.currentThread().interrupt();
Kalhee Kimd94ceea2017-11-29 19:03:02 +00001008 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001009 }
1010
Taras Lemkin96a0d342018-03-26 14:52:58 +00001011 private List<InternalPacket> processDhcp6ForwardOnly(PacketContext context,
1012 Ethernet clientPacket,
1013 Set<Interface> clientInterfaces,
1014 DHCP6 dhcpPacket) {
1015 ConnectPoint inPort = context.inPacket().receivedFrom();
1016 log.trace("Got DHCPv6 on port {}", inPort);
1017 boolean directConnFlag = Dhcp6HandlerUtil.directlyConnected(dhcpPacket);
1018
1019 List<InternalPacket> internalPackets = new ArrayList<>();
1020 List<DhcpServerInfo> serverInfoList = findValidServerInfo(directConnFlag);
1021
1022 for (DhcpServerInfo dhcpServer : serverInfoList) {
Charles Chan24a96ff2018-08-13 10:32:03 -07001023 Interface serverInterface = getServerInterface(dhcpServer);
1024 if (serverInterface == null) {
1025 log.warn("Can't get server interface, ignore");
1026 continue;
1027 }
1028
Taras Lemkin96a0d342018-03-26 14:52:58 +00001029 Ethernet newPacket = Dhcp6HandlerUtil.buildDhcp6PacketFromClient(context,
Charles Chan24a96ff2018-08-13 10:32:03 -07001030 clientPacket, clientInterfaces, dhcpServer, serverInterface);
Taras Lemkin96a0d342018-03-26 14:52:58 +00001031 log.trace("Built packet for server {} : {}", dhcpServer, newPacket);
1032 internalPackets.add(InternalPacket.internalPacket(newPacket,
1033 dhcpServer.getDhcpServerConnectPoint().get()));
1034 }
1035
1036 return internalPackets;
1037 }
1038
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001039 private List<InternalPacket> processLQ6PacketFromClient(PacketContext context,
1040 Ethernet clientPacket,
1041 Set<Interface> clientInterfaces,
1042 DHCP6 dhcp6Payload) {
1043 ConnectPoint inPort = context.inPacket().receivedFrom();
1044 log.info("Got LQ-REQUEST V6 on port {}", inPort);
1045 List<Dhcp6Option> lopt = dhcp6Payload.getOptions();
1046 log.info("Options list: {}", lopt);
1047 Dhcp6LeaseQueryOption lqoption = dhcp6Payload.getOptions()
1048 .stream()
1049 .filter(opt -> opt instanceof Dhcp6LeaseQueryOption)
1050 .map(pld -> (Dhcp6LeaseQueryOption) pld)
1051 .findFirst()
1052 .orElse(null);
1053
1054 if (lqoption == null) {
1055 // Can't find dhcp payload
1056 log.warn("Can't find dhcp6 lease query message - aborting");
1057 return null;
1058 } else {
1059 log.info("dhcp6 lqv6 options found: {}", lqoption);
1060 }
1061 log.warn("LQv6 for " + lqoption.linkAddress.toString() + " comes from " + inPort.toString());
1062 Ethernet packet = context.inPacket().parsed();
1063 Ip6Address clientAddress = lqoption.linkAddress;
1064 IPv6 ipv6Packet = (IPv6) packet.getPayload();
1065 Ip6Address nextHopIp = findNextHopIp6FromRelayStore(clientAddress);
1066
1067 // 1. only if there is a route to remove - remove it
1068 if (nextHopIp != null) {
Kalhee Kim54a97c92018-04-02 21:23:46 +00001069 Route routeForIP6 = new Route(Route.Source.DHCP, clientAddress.toIpPrefix(), nextHopIp);
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001070 log.debug("Removing route of Client " + clientAddress +
1071 " for indirectly connected - next hop ip6 " + nextHopIp);
1072 routeStore.removeRoute(routeForIP6);
1073 }
1074
1075 // 2. note the potential NH this packet came from in case it's a known lease
1076 // this NH will then be used to build the route
1077 MacAddress potentialNH = packet.getSourceMAC();
1078 VlanId vlanId = VlanId.vlanId(packet.getVlanID());
1079 setPotentialNextHopForIp6InRelayStore(clientAddress, vlanId, potentialNH);
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001080 // 3. route this LQ6 to all relevant servers
1081 IPv6 clientIpv6 = (IPv6) clientPacket.getPayload();
1082 UDP clientUdp = (UDP) clientIpv6.getPayload();
1083 DHCP6 clientDhcp6 = (DHCP6) clientUdp.getPayload();
1084
Taras Lemkin96a0d342018-03-26 14:52:58 +00001085 boolean directConnFlag = Dhcp6HandlerUtil.directlyConnected(clientDhcp6);
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001086 List<InternalPacket> internalPackets = new ArrayList<>();
1087 List<DhcpServerInfo> serverInfoList = findValidServerInfo(directConnFlag);
1088 List<DhcpServerInfo> copyServerInfoList = new ArrayList<DhcpServerInfo>(serverInfoList);
1089
1090 for (DhcpServerInfo serverInfo : copyServerInfoList) {
Taras Lemkin96a0d342018-03-26 14:52:58 +00001091 if (!Dhcp6HandlerUtil.checkDhcpServerConnPt(directConnFlag, serverInfo)) {
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001092 log.warn("Can't get server connect point, ignore");
1093 continue;
1094 }
1095 DhcpServerInfo newServerInfo = getHostInfoForServerInfo(serverInfo, serverInfoList);
1096 if (newServerInfo == null) {
1097 log.warn("Can't get server interface with host info resolved, ignore");
1098 continue;
1099 }
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001100 Interface serverInterface = getServerInterface(newServerInfo);
1101 if (serverInterface == null) {
1102 log.warn("Can't get server interface, ignore");
1103 continue;
1104 }
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001105 Ethernet etherRouted = (Ethernet) clientPacket.clone();
1106 MacAddress macFacingServer = serverInterface.mac();
1107 if (macFacingServer == null) {
1108 log.warn("No MAC address for server Interface {}", serverInterface);
1109 return null;
1110 }
1111 etherRouted.setSourceMACAddress(macFacingServer);
1112 etherRouted.setDestinationMACAddress(newServerInfo.getDhcpConnectMac().get());
1113 InternalPacket internalPacket =
Taras Lemkin96a0d342018-03-26 14:52:58 +00001114 InternalPacket.internalPacket(etherRouted,
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001115 serverInfo.getDhcpServerConnectPoint().get());
1116 internalPackets.add(internalPacket);
1117 log.debug("Sending LQ to DHCP server {}", newServerInfo.getDhcpServerIp6());
1118 }
1119 log.debug("num of client packets to send is{}", internalPackets.size());
1120
1121 return internalPackets;
1122 }
1123
Kalhee Kim45fede42017-09-05 19:05:06 +00001124 /**
Kalhee Kim495c9b22017-11-07 16:32:09 +00001125 * build the DHCP6 solicit/request packet with gatewayip.
Kalhee Kim45fede42017-09-05 19:05:06 +00001126 *
1127 * @param context packet context
1128 * @param clientPacket client ethernet packet
1129 * @param clientInterfaces set of client side interfaces
1130 */
Kalhee Kim495c9b22017-11-07 16:32:09 +00001131 private List<InternalPacket> processDhcp6PacketFromClient(PacketContext context,
1132 Ethernet clientPacket,
1133 Set<Interface> clientInterfaces) {
Yi Tseng3bd57ac2017-11-29 14:39:18 -08001134 ConnectPoint receivedFrom = context.inPacket().receivedFrom();
Taras Lemkin96a0d342018-03-26 14:52:58 +00001135 Ip6Address relayAgentIp = Dhcp6HandlerUtil.getRelayAgentIPv6Address(clientInterfaces);
Yi Tseng3bd57ac2017-11-29 14:39:18 -08001136 MacAddress relayAgentMac = clientInterfaces.iterator().next().mac();
1137 if (relayAgentIp == null || relayAgentMac == null) {
1138 log.warn("Missing DHCP relay agent interface Ipv6 addr config for "
Kalhee Kim495c9b22017-11-07 16:32:09 +00001139 + "packet from client on port: {}. Aborting packet processing",
1140 clientInterfaces.iterator().next().connectPoint());
Kalhee Kimd94ceea2017-11-29 19:03:02 +00001141 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_CLIENT_INTF_MAC);
Charles Chan239492c2018-01-22 13:27:28 -08001142 return Lists.newArrayList();
Kalhee Kim121ba922017-11-01 17:56:44 +00001143 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001144
Kalhee Kim495c9b22017-11-07 16:32:09 +00001145 IPv6 clientIpv6 = (IPv6) clientPacket.getPayload();
1146 UDP clientUdp = (UDP) clientIpv6.getPayload();
1147 DHCP6 clientDhcp6 = (DHCP6) clientUdp.getPayload();
1148
Taras Lemkin96a0d342018-03-26 14:52:58 +00001149 boolean directConnFlag = Dhcp6HandlerUtil.directlyConnected(clientDhcp6);
Charles Chana990ce92017-10-30 10:22:50 -07001150
Kalhee Kim121ba922017-11-01 17:56:44 +00001151 ConnectPoint clientConnectionPoint = context.inPacket().receivedFrom();
1152 VlanId vlanIdInUse = VlanId.vlanId(clientPacket.getVlanID());
1153 Interface clientInterface = interfaceService.getInterfacesByPort(clientConnectionPoint)
Taras Lemkin96a0d342018-03-26 14:52:58 +00001154 .stream().filter(iface -> Dhcp6HandlerUtil.interfaceContainsVlan(iface, vlanIdInUse))
Kalhee Kim495c9b22017-11-07 16:32:09 +00001155 .findFirst()
1156 .orElse(null);
Kalhee Kim45fede42017-09-05 19:05:06 +00001157
Kalhee Kim495c9b22017-11-07 16:32:09 +00001158 List<InternalPacket> internalPackets = new ArrayList<>();
1159 List<DhcpServerInfo> serverInfoList = findValidServerInfo(directConnFlag);
1160 List<DhcpServerInfo> copyServerInfoList = new ArrayList<DhcpServerInfo>(serverInfoList);
Kalhee Kim45b24182017-10-18 18:30:23 +00001161
Kalhee Kim495c9b22017-11-07 16:32:09 +00001162 for (DhcpServerInfo serverInfo : copyServerInfoList) {
Taras Lemkin96a0d342018-03-26 14:52:58 +00001163 if (!Dhcp6HandlerUtil.checkDhcpServerConnPt(directConnFlag, serverInfo)) {
Kalhee Kim495c9b22017-11-07 16:32:09 +00001164 log.warn("Can't get server connect point, ignore");
1165 continue;
1166 }
1167 DhcpServerInfo newServerInfo = getHostInfoForServerInfo(serverInfo, serverInfoList);
1168 if (newServerInfo == null) {
1169 log.warn("Can't get server interface with host info resolved, ignore");
1170 continue;
1171 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001172
Kalhee Kim495c9b22017-11-07 16:32:09 +00001173 Interface serverInterface = getServerInterface(newServerInfo);
1174 if (serverInterface == null) {
1175 log.warn("Can't get server interface, ignore");
1176 continue;
1177 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +00001178
Taras Lemkin96a0d342018-03-26 14:52:58 +00001179 Ethernet etherReply = Dhcp6HandlerUtil.buildDhcp6PacketFromClient(context, clientPacket,
Kalhee Kim495c9b22017-11-07 16:32:09 +00001180 clientInterfaces, newServerInfo, serverInterface);
1181 removeHostOrRoute(directConnFlag, clientConnectionPoint, clientDhcp6, clientPacket,
1182 clientIpv6, clientInterface);
Kalhee Kimea4b6c22017-11-09 14:38:37 +00001183
Taras Lemkin96a0d342018-03-26 14:52:58 +00001184 InternalPacket internalPacket = InternalPacket.internalPacket(etherReply,
Kalhee Kim495c9b22017-11-07 16:32:09 +00001185 serverInfo.getDhcpServerConnectPoint().get());
1186 internalPackets.add(internalPacket);
1187 }
1188 log.debug("num of client packets to send is{}", internalPackets.size());
Kalhee Kimea4b6c22017-11-09 14:38:37 +00001189
Kalhee Kim495c9b22017-11-07 16:32:09 +00001190 return internalPackets;
1191 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001192
1193 /**
Kalhee Kim45fede42017-09-05 19:05:06 +00001194 * process the DHCP6 relay-reply packet from dhcp server.
1195 *
1196 * @param context packet context
1197 * @param receivedPacket server ethernet packet
1198 * @param recevingInterfaces set of server side interfaces
Kalhee Kim495c9b22017-11-07 16:32:09 +00001199 * @return internalPacket toward client
Kalhee Kim45fede42017-09-05 19:05:06 +00001200 */
1201 private InternalPacket processDhcp6PacketFromServer(PacketContext context,
1202 Ethernet receivedPacket, Set<Interface> recevingInterfaces) {
Kalhee Kim45fede42017-09-05 19:05:06 +00001203 // get dhcp6 header.
Ray Milkeyf0c47612017-09-28 11:29:38 -07001204 Ethernet etherReply = receivedPacket.duplicate();
Kalhee Kim45fede42017-09-05 19:05:06 +00001205 IPv6 ipv6Packet = (IPv6) etherReply.getPayload();
1206 UDP udpPacket = (UDP) ipv6Packet.getPayload();
1207 DHCP6 dhcp6Relay = (DHCP6) udpPacket.getPayload();
Taras Lemkin96a0d342018-03-26 14:52:58 +00001208 Boolean directConnFlag = Dhcp6HandlerUtil.directlyConnected(dhcp6Relay);
Kalhee Kim45fede42017-09-05 19:05:06 +00001209
Kalhee Kim495c9b22017-11-07 16:32:09 +00001210 DHCP6 embeddedDhcp6 = dhcp6Relay.getOptions().stream()
1211 .filter(opt -> opt instanceof Dhcp6RelayOption)
1212 .map(BasePacket::getPayload)
1213 .map(pld -> (DHCP6) pld)
1214 .findFirst()
1215 .orElse(null);
Kalhee Kimd21029f2017-09-26 20:21:53 +00001216 ConnectPoint inPort = context.inPacket().receivedFrom();
Kalhee Kim495c9b22017-11-07 16:32:09 +00001217 DhcpServerInfo foundServerInfo = findServerInfoFromServer(directConnFlag, inPort);
Kalhee Kimd21029f2017-09-26 20:21:53 +00001218
Kalhee Kim495c9b22017-11-07 16:32:09 +00001219 if (foundServerInfo == null) {
jayakumarthazhathed5f05d2018-09-25 15:56:51 -04001220 log.warn("Cannot find server info for {} server, inPort {}",
1221 directConnFlag ? "direct" : "indirect", inPort);
Kalhee Kimd94ceea2017-11-29 19:03:02 +00001222 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_SERVER_INFO);
Kalhee Kimd21029f2017-09-26 20:21:53 +00001223 return null;
Kalhee Kim495c9b22017-11-07 16:32:09 +00001224 } else {
Taras Lemkin96a0d342018-03-26 14:52:58 +00001225 if (Dhcp6HandlerUtil.isServerIpEmpty(foundServerInfo)) {
Kalhee Kim495c9b22017-11-07 16:32:09 +00001226 log.warn("Cannot find server info's ipaddress");
Kalhee Kimd94ceea2017-11-29 19:03:02 +00001227 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_SERVER_IP6ADDR);
Kalhee Kim495c9b22017-11-07 16:32:09 +00001228 return null;
1229 }
Kalhee Kimd21029f2017-09-26 20:21:53 +00001230 }
1231
Kalhee Kim45fede42017-09-05 19:05:06 +00001232 Dhcp6InterfaceIdOption interfaceIdOption = dhcp6Relay.getOptions().stream()
1233 .filter(opt -> opt instanceof Dhcp6InterfaceIdOption)
1234 .map(opt -> (Dhcp6InterfaceIdOption) opt)
1235 .findFirst()
1236 .orElse(null);
Kalhee Kim45fede42017-09-05 19:05:06 +00001237 if (interfaceIdOption == null) {
1238 log.warn("Interface Id option is not present, abort packet...");
Kalhee Kimd94ceea2017-11-29 19:03:02 +00001239 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.OPTION_MISSING_FAIL);
Kalhee Kim45fede42017-09-05 19:05:06 +00001240 return null;
1241 }
1242
1243 MacAddress peerMac = interfaceIdOption.getMacAddress();
1244 String clientConnectionPointStr = new String(interfaceIdOption.getInPort());
Kalhee Kim45fede42017-09-05 19:05:06 +00001245 ConnectPoint clientConnectionPoint = ConnectPoint.deviceConnectPoint(clientConnectionPointStr);
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +00001246 VlanId vlanIdInUse = VlanId.vlanId(interfaceIdOption.getVlanId());
1247 Interface clientInterface = interfaceService.getInterfacesByPort(clientConnectionPoint)
Taras Lemkin96a0d342018-03-26 14:52:58 +00001248 .stream().filter(iface -> Dhcp6HandlerUtil.interfaceContainsVlan(iface, vlanIdInUse))
Kalhee Kim495c9b22017-11-07 16:32:09 +00001249 .findFirst().orElse(null);
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +00001250 if (clientInterface == null) {
1251 log.warn("Cannot get client interface for from packet, abort... vlan {}", vlanIdInUse.toString());
Kalhee Kimd94ceea2017-11-29 19:03:02 +00001252 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_MATCHING_INTF);
Kalhee Kim45fede42017-09-05 19:05:06 +00001253 return null;
1254 }
Kalhee Kim44fbda12018-04-03 21:08:18 +00001255 etherReply.setVlanID(vlanIdInUse.toShort());
1256
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +00001257 MacAddress relayAgentMac = clientInterface.mac();
Kalhee Kim45fede42017-09-05 19:05:06 +00001258 if (relayAgentMac == null) {
Yi Tseng68ef26b2017-12-18 17:10:00 -08001259 log.warn("Can not get client interface mac, abort packet..");
Kalhee Kimd94ceea2017-11-29 19:03:02 +00001260 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_CLIENT_INTF_MAC);
Kalhee Kim45fede42017-09-05 19:05:06 +00001261 return null;
1262 }
1263 etherReply.setSourceMACAddress(relayAgentMac);
1264
1265 // find destMac
Yi Tseng68ef26b2017-12-18 17:10:00 -08001266 MacAddress clientMac;
Yi Tsengaa417a62017-09-08 17:22:51 -07001267 Ip6Address peerAddress = Ip6Address.valueOf(dhcp6Relay.getPeerAddress());
1268 Set<Host> clients = hostService.getHostsByIp(peerAddress);
Kalhee Kim45fede42017-09-05 19:05:06 +00001269 if (clients.isEmpty()) {
Yi Tseng68ef26b2017-12-18 17:10:00 -08001270 log.trace("There's no host found for this address {}",
Kalhee Kim45fede42017-09-05 19:05:06 +00001271 HexString.toHexString(dhcp6Relay.getPeerAddress(), ":"));
Yi Tseng68ef26b2017-12-18 17:10:00 -08001272 log.trace("Let's look up interfaceId {}", HexString.toHexString(peerMac.toBytes(), ":"));
Kalhee Kim45fede42017-09-05 19:05:06 +00001273 clientMac = peerMac;
1274 } else {
1275 clientMac = clients.iterator().next().mac();
1276 if (clientMac == null) {
1277 log.warn("No client mac address found, abort packet...");
Kalhee Kimd94ceea2017-11-29 19:03:02 +00001278 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_CLIENT_INTF_MAC);
Kalhee Kim45fede42017-09-05 19:05:06 +00001279 return null;
1280 }
Yi Tseng68ef26b2017-12-18 17:10:00 -08001281 log.trace("Client mac address found from getHostByIp");
Kalhee Kim45fede42017-09-05 19:05:06 +00001282 }
1283 etherReply.setDestinationMACAddress(clientMac);
Kalhee Kim45fede42017-09-05 19:05:06 +00001284 // ip header
1285 ipv6Packet.setSourceAddress(dhcp6Relay.getLinkAddress());
1286 ipv6Packet.setDestinationAddress(dhcp6Relay.getPeerAddress());
1287 // udp header
1288 udpPacket.setSourcePort(UDP.DHCP_V6_SERVER_PORT);
1289 if (directConnFlag) {
1290 udpPacket.setDestinationPort(UDP.DHCP_V6_CLIENT_PORT);
1291 } else {
1292 udpPacket.setDestinationPort(UDP.DHCP_V6_SERVER_PORT);
1293 }
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001294
Kalhee Kim45fede42017-09-05 19:05:06 +00001295
Taras Lemkin96a0d342018-03-26 14:52:58 +00001296 boolean hostOrRouteAllowed = learnRouteFromLeasequery ||
1297 Dhcp6HandlerUtil.getDhcp6LeafMessageType(dhcp6Relay) != MsgType.LEASEQUERY_REPLY;
1298 log.debug("Can add host or route: {}", hostOrRouteAllowed);
1299
1300 if (hostOrRouteAllowed) {
1301 // add host or route
1302 addHostOrRoute(directConnFlag, clientConnectionPoint, dhcp6Relay, embeddedDhcp6,
1303 clientMac, clientInterface);
1304 }
1305
Kalhee Kim45fede42017-09-05 19:05:06 +00001306 udpPacket.setPayload(embeddedDhcp6);
1307 udpPacket.resetChecksum();
1308 ipv6Packet.setPayload(udpPacket);
1309 etherReply.setPayload(ipv6Packet);
Taras Lemkin96a0d342018-03-26 14:52:58 +00001310 return InternalPacket.internalPacket(etherReply, clientConnectionPoint);
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001311 }
Yi Tseng483ac6f2017-08-02 15:03:31 -07001312
1313 @Override
Kalhee Kimba366062017-11-07 16:32:09 +00001314 public void setDhcpFpmEnabled(Boolean enabled) {
1315 dhcpFpmEnabled = enabled;
1316 }
1317
1318 @Override
Yi Tseng483ac6f2017-08-02 15:03:31 -07001319 public void setDefaultDhcpServerConfigs(Collection<DhcpServerConfig> configs) {
Kalhee Kim495c9b22017-11-07 16:32:09 +00001320 log.debug("setDefaultDhcpServerConfigs is called.");
Yi Tseng919b2df2017-09-07 16:22:51 -07001321 setDhcpServerConfigs(configs, defaultServerInfoList);
Yi Tseng919b2df2017-09-07 16:22:51 -07001322 }
1323
1324 @Override
1325 public void setIndirectDhcpServerConfigs(Collection<DhcpServerConfig> configs) {
Kalhee Kim495c9b22017-11-07 16:32:09 +00001326 log.debug("setIndirectDhcpServerConfigs is called.");
Yi Tseng919b2df2017-09-07 16:22:51 -07001327 setDhcpServerConfigs(configs, indirectServerInfoList);
Yi Tseng919b2df2017-09-07 16:22:51 -07001328 }
1329
1330 public void setDhcpServerConfigs(Collection<DhcpServerConfig> configs, List<DhcpServerInfo> serverInfoList) {
Kalhee Kim495c9b22017-11-07 16:32:09 +00001331 log.debug("config size {}.", configs.size());
1332
Kalhee Kim45fede42017-09-05 19:05:06 +00001333 if (configs.size() == 0) {
1334 // no config to update
1335 return;
1336 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001337 // TODO: currently we pick up first DHCP server config.
1338 // Will use other server configs in the future for HA.
Kalhee Kim495c9b22017-11-07 16:32:09 +00001339 Boolean isConfigValid = false;
1340 for (DhcpServerConfig serverConfig : configs) {
1341 if (serverConfig.getDhcpServerIp6().isPresent()) {
1342 isConfigValid = true;
1343 break;
1344 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001345 }
Kalhee Kim495c9b22017-11-07 16:32:09 +00001346 if (!isConfigValid) {
1347 log.warn("No IP V6 server address found.");
1348 return; // No IP V6 address found
1349 }
1350 for (DhcpServerInfo oldServerInfo : serverInfoList) {
Yi Tseng919b2df2017-09-07 16:22:51 -07001351 // stop monitoring gateway or server
1352 oldServerInfo.getDhcpGatewayIp6().ifPresent(gatewayIp -> {
1353 hostService.stopMonitoringIp(gatewayIp);
1354 });
1355 oldServerInfo.getDhcpServerIp6().ifPresent(serverIp -> {
1356 hostService.stopMonitoringIp(serverIp);
Yi Tseng525ff402017-10-23 19:39:39 -07001357 cancelDhcpPacket(serverIp);
Yi Tseng919b2df2017-09-07 16:22:51 -07001358 });
1359 }
Kalhee Kim495c9b22017-11-07 16:32:09 +00001360 serverInfoList.clear();
1361 for (DhcpServerConfig serverConfig : configs) {
1362 // Create new server info according to the config
1363 DhcpServerInfo newServerInfo = new DhcpServerInfo(serverConfig,
1364 DhcpServerInfo.Version.DHCP_V6);
1365 checkState(newServerInfo.getDhcpServerConnectPoint().isPresent(),
1366 "Connect point not exists");
1367 checkState(newServerInfo.getDhcpServerIp6().isPresent(),
1368 "IP of DHCP server not exists");
Yi Tseng919b2df2017-09-07 16:22:51 -07001369
Kalhee Kim495c9b22017-11-07 16:32:09 +00001370 log.debug("DHCP server connect point: {}", newServerInfo.getDhcpServerConnectPoint().orElse(null));
1371 log.debug("DHCP server IP: {}", newServerInfo.getDhcpServerIp6().orElse(null));
Yi Tseng919b2df2017-09-07 16:22:51 -07001372
Kalhee Kim495c9b22017-11-07 16:32:09 +00001373 Ip6Address serverIp = newServerInfo.getDhcpServerIp6().get();
1374 Ip6Address ipToProbe;
1375 if (newServerInfo.getDhcpGatewayIp6().isPresent()) {
1376 ipToProbe = newServerInfo.getDhcpGatewayIp6().get();
1377 } else {
1378 ipToProbe = newServerInfo.getDhcpServerIp6().orElse(null);
1379 }
1380 String hostToProbe = newServerInfo.getDhcpGatewayIp6()
1381 .map(ip -> "gateway").orElse("server");
Yi Tseng919b2df2017-09-07 16:22:51 -07001382
Kalhee Kim495c9b22017-11-07 16:32:09 +00001383 log.warn("Probing to resolve {} IP {}", hostToProbe, ipToProbe);
1384 hostService.startMonitoringIp(ipToProbe);
1385
1386 Set<Host> hosts = hostService.getHostsByIp(ipToProbe);
1387 if (!hosts.isEmpty()) {
1388 Host host = hosts.iterator().next();
1389 newServerInfo.setDhcpConnectVlan(host.vlan());
1390 newServerInfo.setDhcpConnectMac(host.mac());
1391 log.warn("Host found host {}", host);
1392
1393 } else {
1394 log.warn("No host found host ip {}", ipToProbe);
1395 }
1396 // Add new server info
1397 synchronized (this) {
1398 serverInfoList.add(newServerInfo);
1399 }
1400 if (!hosts.isEmpty()) {
1401 requestDhcpPacket(serverIp);
1402 }
Yi Tseng919b2df2017-09-07 16:22:51 -07001403 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001404 }
1405
1406 class InternalHostListener implements HostListener {
1407 @Override
1408 public void event(HostEvent event) {
1409 switch (event.type()) {
1410 case HOST_ADDED:
1411 case HOST_UPDATED:
Charles Chan24a96ff2018-08-13 10:32:03 -07001412 case HOST_MOVED:
Jordan Halterman6328db72018-04-10 13:34:50 -04001413 log.trace("Scheduled host event {}", event);
1414 hostEventExecutor.execute(() -> hostUpdated(event.subject()));
Kalhee Kim45fede42017-09-05 19:05:06 +00001415 break;
1416 case HOST_REMOVED:
Jordan Halterman6328db72018-04-10 13:34:50 -04001417 log.trace("Scheduled host event {}", event);
1418 hostEventExecutor.execute(() -> hostRemoved(event.subject()));
Kalhee Kim45fede42017-09-05 19:05:06 +00001419 break;
Kalhee Kim45fede42017-09-05 19:05:06 +00001420 default:
1421 break;
1422 }
1423 }
1424 }
1425
1426 /**
Kalhee Kim45fede42017-09-05 19:05:06 +00001427 * Handle host updated.
1428 * If the host is DHCP server or gateway, update connect mac and vlan.
1429 *
1430 * @param host the host
1431 */
1432 private void hostUpdated(Host host) {
Yi Tseng525ff402017-10-23 19:39:39 -07001433 hostUpdated(host, defaultServerInfoList);
1434 hostUpdated(host, indirectServerInfoList);
Kalhee Kim45fede42017-09-05 19:05:06 +00001435 }
1436
Yi Tseng525ff402017-10-23 19:39:39 -07001437 private void hostUpdated(Host host, List<DhcpServerInfo> serverInfoList) {
jjosep004c05f55ab2018-08-21 09:01:10 -04001438 serverInfoList.stream().forEach(serverInfo -> {
Yi Tseng525ff402017-10-23 19:39:39 -07001439 Ip6Address serverIp = serverInfo.getDhcpServerIp6().orElse(null);
jjosep004c05f55ab2018-08-21 09:01:10 -04001440 Ip6Address targetIp = serverInfo.getDhcpGatewayIp6().orElse(null);
Yi Tseng525ff402017-10-23 19:39:39 -07001441
1442 if (targetIp == null) {
1443 targetIp = serverIp;
1444 }
Yi Tseng525ff402017-10-23 19:39:39 -07001445 if (targetIp != null) {
1446 if (host.ipAddresses().contains(targetIp)) {
1447 serverInfo.setDhcpConnectMac(host.mac());
1448 serverInfo.setDhcpConnectVlan(host.vlan());
1449 requestDhcpPacket(serverIp);
1450 }
1451 }
jjosep004c05f55ab2018-08-21 09:01:10 -04001452 });
Yi Tseng525ff402017-10-23 19:39:39 -07001453 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001454 /**
1455 * Handle host removed.
1456 * If the host is DHCP server or gateway, unset connect mac and vlan.
1457 *
1458 * @param host the host
1459 */
1460 private void hostRemoved(Host host) {
Yi Tseng525ff402017-10-23 19:39:39 -07001461 hostRemoved(host, defaultServerInfoList);
1462 hostRemoved(host, indirectServerInfoList);
Yi Tseng919b2df2017-09-07 16:22:51 -07001463 }
1464
Yi Tseng525ff402017-10-23 19:39:39 -07001465 private void hostRemoved(Host host, List<DhcpServerInfo> serverInfoList) {
jjosep004c05f55ab2018-08-21 09:01:10 -04001466 serverInfoList.stream().forEach(serverInfo -> {
Yi Tseng525ff402017-10-23 19:39:39 -07001467 Ip6Address serverIp = serverInfo.getDhcpServerIp6().orElse(null);
jjosep004c05f55ab2018-08-21 09:01:10 -04001468 Ip6Address targetIp = serverInfo.getDhcpGatewayIp6().orElse(null);
Yi Tseng525ff402017-10-23 19:39:39 -07001469
1470 if (targetIp == null) {
1471 targetIp = serverIp;
1472 }
Yi Tseng525ff402017-10-23 19:39:39 -07001473 if (targetIp != null) {
1474 if (host.ipAddresses().contains(targetIp)) {
1475 serverInfo.setDhcpConnectVlan(null);
1476 serverInfo.setDhcpConnectMac(null);
1477 cancelDhcpPacket(serverIp);
1478 }
1479 }
jjosep004c05f55ab2018-08-21 09:01:10 -04001480 });
Yi Tseng525ff402017-10-23 19:39:39 -07001481 }
1482
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001483 /**
1484 * Gets Interface facing to the server for default host.
1485 *
1486 * @return the Interface facing to the server; null if not found
1487 */
1488 private Interface getServerInterface() {
1489 DhcpServerInfo serverInfo;
1490 ConnectPoint dhcpServerConnectPoint;
1491 VlanId dhcpConnectVlan;
1492
1493 if (!defaultServerInfoList.isEmpty()) {
1494 serverInfo = defaultServerInfoList.get(0);
1495 dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1496 dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1497 } else {
1498 return null;
1499 }
1500 if (dhcpServerConnectPoint == null || dhcpConnectVlan == null) {
1501 log.info("Default DHCP server {} not resolve yet", serverInfo.getDhcpGatewayIp6());
1502 return null;
1503 }
1504 return interfaceService.getInterfacesByPort(dhcpServerConnectPoint)
1505 .stream()
Taras Lemkin96a0d342018-03-26 14:52:58 +00001506 .filter(iface -> Dhcp6HandlerUtil.interfaceContainsVlan(iface, dhcpConnectVlan))
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001507 .findFirst()
1508 .orElse(null);
1509 }
1510
1511 /**
1512 * Gets Interface facing to the server for indirect hosts.
1513 * Use default server Interface if indirect server not configured.
1514 *
1515 * @return the Interface facing to the server; null if not found
1516 */
1517 private Interface getIndirectServerInterface() {
1518 DhcpServerInfo serverInfo;
1519
1520 ConnectPoint indirectDhcpServerConnectPoint;
1521 VlanId indirectDhcpConnectVlan;
1522
1523 if (!indirectServerInfoList.isEmpty()) {
1524 serverInfo = indirectServerInfoList.get(0);
1525 indirectDhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1526 indirectDhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1527 } else {
1528 return getServerInterface();
1529 }
1530 if (indirectDhcpServerConnectPoint == null || indirectDhcpConnectVlan == null) {
1531 log.info("Indirect DHCP server {} not resolve yet", serverInfo.getDhcpGatewayIp6());
1532 return null;
1533 }
1534 return interfaceService.getInterfacesByPort(indirectDhcpServerConnectPoint)
1535 .stream()
Taras Lemkin96a0d342018-03-26 14:52:58 +00001536 .filter(iface -> Dhcp6HandlerUtil.interfaceContainsVlan(iface, indirectDhcpConnectVlan))
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001537 .findFirst()
1538 .orElse(null);
1539 }
1540
Kalhee Kim45b24182017-10-18 18:30:23 +00001541 /**
Kalhee Kim495c9b22017-11-07 16:32:09 +00001542 * Checks if serverInfo's host info (mac and vlan) is filled in; if not, fills in.
1543 *
1544 * @param serverInfo server information
1545 * @return newServerInfo if host info can be either found or filled in.
1546 */
1547 private DhcpServerInfo getHostInfoForServerInfo(DhcpServerInfo serverInfo, List<DhcpServerInfo> sererInfoList) {
1548 DhcpServerInfo newServerInfo = null;
1549 MacAddress dhcpServerConnectMac = serverInfo.getDhcpConnectMac().orElse(null);
1550 VlanId dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1551 ConnectPoint dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1552
1553 if (dhcpServerConnectMac != null && dhcpConnectVlan != null) {
1554 newServerInfo = serverInfo;
Charles Chan750c9992018-02-26 10:44:49 -08001555 log.debug("DHCP server {} host info found. ConnectPt{} Mac {} vlan {}", serverInfo.getDhcpServerIp6(),
Kalhee Kim495c9b22017-11-07 16:32:09 +00001556 dhcpServerConnectPoint, dhcpServerConnectMac, dhcpConnectVlan);
1557 } else {
1558 log.warn("DHCP server {} not resolve yet connectPt {} mac {} vlan {}", serverInfo.getDhcpServerIp6(),
1559 dhcpServerConnectPoint, dhcpServerConnectMac, dhcpConnectVlan);
1560
1561 Ip6Address ipToProbe;
1562 if (serverInfo.getDhcpGatewayIp6().isPresent()) {
1563 ipToProbe = serverInfo.getDhcpGatewayIp6().get();
1564 } else {
1565 ipToProbe = serverInfo.getDhcpServerIp6().orElse(null);
1566 }
1567 String hostToProbe = serverInfo.getDhcpGatewayIp6()
1568 .map(ip -> "gateway").orElse("server");
1569
1570 log.info("Dynamically probing to resolve {} IP {}", hostToProbe, ipToProbe);
1571 hostService.startMonitoringIp(ipToProbe);
1572
1573 Set<Host> hosts = hostService.getHostsByIp(ipToProbe);
1574 if (!hosts.isEmpty()) {
1575 int serverInfoIndex = sererInfoList.indexOf(serverInfo);
1576 Host host = hosts.iterator().next();
1577 serverInfo.setDhcpConnectVlan(host.vlan());
1578 serverInfo.setDhcpConnectMac(host.mac());
1579 // replace the serverInfo in the list
1580 sererInfoList.set(serverInfoIndex, serverInfo);
1581 newServerInfo = serverInfo;
1582 log.warn("Dynamically host found host {}", host);
1583 } else {
1584 log.warn("No host found host ip {} dynamically", ipToProbe);
1585 }
1586 }
1587 return newServerInfo;
1588 }
1589
1590 /**
Kalhee Kim45b24182017-10-18 18:30:23 +00001591 * Gets Interface facing to the server for default host.
1592 *
Kalhee Kim495c9b22017-11-07 16:32:09 +00001593 * @param serverInfo server information
Kalhee Kim45b24182017-10-18 18:30:23 +00001594 * @return the Interface facing to the server; null if not found
1595 */
Kalhee Kim495c9b22017-11-07 16:32:09 +00001596 private Interface getServerInterface(DhcpServerInfo serverInfo) {
1597 Interface serverInterface = null;
Yi Tseng25bfe372017-11-03 16:27:32 -07001598
Kalhee Kim495c9b22017-11-07 16:32:09 +00001599 ConnectPoint dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1600 VlanId dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1601
1602 if (dhcpServerConnectPoint != null && dhcpConnectVlan != null) {
1603 serverInterface = interfaceService.getInterfacesByPort(dhcpServerConnectPoint)
1604 .stream()
Taras Lemkin96a0d342018-03-26 14:52:58 +00001605 .filter(iface -> Dhcp6HandlerUtil.interfaceContainsVlan(iface, dhcpConnectVlan))
Kalhee Kim495c9b22017-11-07 16:32:09 +00001606 .findFirst()
1607 .orElse(null);
Yi Tseng25bfe372017-11-03 16:27:32 -07001608 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +00001609 log.warn("DHCP server {} not resolve yet connectPoint {} vlan {}", serverInfo.getDhcpServerIp6(),
1610 dhcpServerConnectPoint, dhcpConnectVlan);
Kalhee Kim45b24182017-10-18 18:30:23 +00001611 }
Kalhee Kim495c9b22017-11-07 16:32:09 +00001612
1613 return serverInterface;
Kalhee Kim45b24182017-10-18 18:30:23 +00001614 }
1615
Yi Tseng525ff402017-10-23 19:39:39 -07001616 private void requestDhcpPacket(Ip6Address serverIp) {
1617 requestServerDhcpPacket(serverIp);
1618 requestClientDhcpPacket(serverIp);
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001619 requestServerLQPacket(serverIp);
Yi Tseng525ff402017-10-23 19:39:39 -07001620 }
1621
1622 private void cancelDhcpPacket(Ip6Address serverIp) {
1623 cancelServerDhcpPacket(serverIp);
1624 cancelClientDhcpPacket(serverIp);
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001625 cancelServerLQPacket(serverIp);
Yi Tseng525ff402017-10-23 19:39:39 -07001626 }
1627
1628 private void cancelServerDhcpPacket(Ip6Address serverIp) {
1629 TrafficSelector serverSelector =
1630 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1631 .matchIPv6Src(serverIp.toIpPrefix())
1632 .build();
1633 packetService.cancelPackets(serverSelector,
1634 PacketPriority.CONTROL,
1635 appId);
1636 }
1637
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001638 private void cancelServerLQPacket(Ip6Address serverIp) {
1639 TrafficSelector serverSelector =
1640 DefaultTrafficSelector.builder(LEASE_QUERY_RESPONSE_SELECTOR)
1641 .matchIPv6Src(serverIp.toIpPrefix())
1642 .build();
1643 packetService.cancelPackets(serverSelector,
1644 PacketPriority.CONTROL,
1645 appId);
1646 }
1647
Yi Tseng525ff402017-10-23 19:39:39 -07001648 private void requestServerDhcpPacket(Ip6Address serverIp) {
1649 TrafficSelector serverSelector =
1650 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1651 .matchIPv6Src(serverIp.toIpPrefix())
1652 .build();
1653 packetService.requestPackets(serverSelector,
1654 PacketPriority.CONTROL,
1655 appId);
1656 }
1657
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001658 private void requestServerLQPacket(Ip6Address serverIp) {
1659 TrafficSelector serverSelector =
1660 DefaultTrafficSelector.builder(LEASE_QUERY_RESPONSE_SELECTOR)
1661 .matchIPv6Src(serverIp.toIpPrefix())
1662 .build();
1663 packetService.requestPackets(serverSelector,
1664 PacketPriority.CONTROL,
1665 appId);
1666 }
1667
Yi Tseng525ff402017-10-23 19:39:39 -07001668 private void cancelClientDhcpPacket(Ip6Address serverIp) {
1669 // Packet comes from relay
1670 TrafficSelector indirectClientSelector =
1671 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1672 .matchIPv6Dst(serverIp.toIpPrefix())
1673 .build();
1674 packetService.cancelPackets(indirectClientSelector,
1675 PacketPriority.CONTROL,
1676 appId);
Yi Tseng41dde702017-11-02 15:21:10 -07001677 indirectClientSelector =
1678 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1679 .matchIPv6Dst(Ip6Address.ALL_DHCP_RELAY_AGENTS_AND_SERVERS.toIpPrefix())
1680 .build();
1681 packetService.cancelPackets(indirectClientSelector,
1682 PacketPriority.CONTROL,
1683 appId);
1684 indirectClientSelector =
1685 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1686 .matchIPv6Dst(Ip6Address.ALL_DHCP_SERVERS.toIpPrefix())
1687 .build();
1688 packetService.cancelPackets(indirectClientSelector,
1689 PacketPriority.CONTROL,
1690 appId);
Yi Tseng525ff402017-10-23 19:39:39 -07001691
1692 // Packet comes from client
1693 packetService.cancelPackets(CLIENT_SERVER_SELECTOR,
1694 PacketPriority.CONTROL,
1695 appId);
1696 }
1697
1698 private void requestClientDhcpPacket(Ip6Address serverIp) {
1699 // Packet comes from relay
1700 TrafficSelector indirectClientSelector =
1701 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1702 .matchIPv6Dst(serverIp.toIpPrefix())
1703 .build();
1704 packetService.requestPackets(indirectClientSelector,
1705 PacketPriority.CONTROL,
1706 appId);
Yi Tseng41dde702017-11-02 15:21:10 -07001707 indirectClientSelector =
1708 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1709 .matchIPv6Dst(Ip6Address.ALL_DHCP_RELAY_AGENTS_AND_SERVERS.toIpPrefix())
1710 .build();
1711 packetService.requestPackets(indirectClientSelector,
1712 PacketPriority.CONTROL,
1713 appId);
1714 indirectClientSelector =
1715 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1716 .matchIPv6Dst(Ip6Address.ALL_DHCP_SERVERS.toIpPrefix())
1717 .build();
1718 packetService.requestPackets(indirectClientSelector,
1719 PacketPriority.CONTROL,
1720 appId);
Yi Tseng525ff402017-10-23 19:39:39 -07001721
1722 // Packet comes from client
1723 packetService.requestPackets(CLIENT_SERVER_SELECTOR,
1724 PacketPriority.CONTROL,
1725 appId);
1726 }
1727
1728 /**
1729 * Process the ignore rules.
1730 *
1731 * @param deviceId the device id
1732 * @param vlanId the vlan to be ignored
1733 * @param op the operation, ADD to install; REMOVE to uninstall rules
1734 */
1735 private void processIgnoreVlanRule(DeviceId deviceId, VlanId vlanId, Objective.Operation op) {
Yi Tseng525ff402017-10-23 19:39:39 -07001736 AtomicInteger installedCount = new AtomicInteger(DHCP_SELECTORS.size());
1737 DHCP_SELECTORS.forEach(trafficSelector -> {
1738 TrafficSelector selector = DefaultTrafficSelector.builder(trafficSelector)
1739 .matchVlanId(vlanId)
1740 .build();
1741
1742 ForwardingObjective.Builder builder = DefaultForwardingObjective.builder()
1743 .withFlag(ForwardingObjective.Flag.VERSATILE)
1744 .withSelector(selector)
1745 .withPriority(IGNORE_CONTROL_PRIORITY)
Yi Tsengdbabeed2017-11-29 10:49:20 -08001746 .withTreatment(DefaultTrafficTreatment.emptyTreatment())
Yi Tseng525ff402017-10-23 19:39:39 -07001747 .fromApp(appId);
1748
1749
1750 ObjectiveContext objectiveContext = new ObjectiveContext() {
1751 @Override
1752 public void onSuccess(Objective objective) {
1753 log.info("Ignore rule {} (Vlan id {}, device {}, selector {})",
1754 op, vlanId, deviceId, selector);
1755 int countDown = installedCount.decrementAndGet();
1756 if (countDown != 0) {
1757 return;
1758 }
1759 switch (op) {
1760 case ADD:
1761 ignoredVlans.put(deviceId, vlanId);
1762 break;
1763 case REMOVE:
1764 ignoredVlans.remove(deviceId, vlanId);
1765 break;
1766 default:
1767 log.warn("Unsupported objective operation {}", op);
1768 break;
1769 }
1770 }
1771
1772 @Override
1773 public void onError(Objective objective, ObjectiveError error) {
1774 log.warn("Can't {} ignore rule (vlan id {}, selector {}, device {}) due to {}",
1775 op, vlanId, selector, deviceId, error);
1776 }
1777 };
1778
1779 ForwardingObjective fwd;
1780 switch (op) {
1781 case ADD:
1782 fwd = builder.add(objectiveContext);
1783 break;
1784 case REMOVE:
1785 fwd = builder.remove(objectiveContext);
1786 break;
1787 default:
1788 log.warn("Unsupported objective operation {}", op);
1789 return;
1790 }
1791
1792 Device device = deviceService.getDevice(deviceId);
1793 if (device == null || !device.is(Pipeliner.class)) {
1794 log.warn("Device {} is not available now, wait until device is available", deviceId);
1795 return;
1796 }
1797 flowObjectiveService.apply(deviceId, fwd);
1798 });
1799 }
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001800
Kalhee Kim121ba922017-11-01 17:56:44 +00001801 /**
1802 * Find first ipaddress for a given Host info i.e. mac and vlan.
1803 *
1804 * @param clientMac client mac
1805 * @param vlanId packet's vlan
1806 * @return next-hop link-local ipaddress for a given host
1807 */
Kalhee Kim495c9b22017-11-07 16:32:09 +00001808 private IpAddress getFirstIpByHost(Boolean directConnFlag, MacAddress clientMac, VlanId vlanId) {
Kalhee Kim121ba922017-11-01 17:56:44 +00001809 IpAddress nextHopIp;
1810 // pick out the first link-local ip address
1811 HostId gwHostId = HostId.hostId(clientMac, vlanId);
1812 Host gwHost = hostService.getHost(gwHostId);
1813 if (gwHost == null) {
1814 log.warn("Can't find gateway host for hostId {}", gwHostId);
1815 return null;
1816 }
Kalhee Kim495c9b22017-11-07 16:32:09 +00001817 if (directConnFlag) {
1818 nextHopIp = gwHost.ipAddresses()
1819 .stream()
1820 .filter(IpAddress::isIp6)
1821 .map(IpAddress::getIp6Address)
1822 .findFirst()
1823 .orElse(null);
1824 } else {
1825 nextHopIp = gwHost.ipAddresses()
1826 .stream()
1827 .filter(IpAddress::isIp6)
1828 .filter(ip6 -> ip6.isLinkLocal())
1829 .map(IpAddress::getIp6Address)
1830 .findFirst()
1831 .orElse(null);
1832 }
Kalhee Kim121ba922017-11-01 17:56:44 +00001833 return nextHopIp;
1834 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +00001835
Kalhee Kim495c9b22017-11-07 16:32:09 +00001836 private List<DhcpServerInfo> findValidServerInfo(boolean directConnFlag) {
1837 List<DhcpServerInfo> validServerInfo;
1838
1839 if (directConnFlag || indirectServerInfoList.isEmpty()) {
1840 validServerInfo = new ArrayList<DhcpServerInfo>(defaultServerInfoList);
1841 } else {
1842 validServerInfo = new ArrayList<DhcpServerInfo>(indirectServerInfoList);
1843 }
1844 return validServerInfo;
1845 }
1846
1847 private DhcpServerInfo findServerInfoFromServer(boolean directConnFlag, ConnectPoint inPort) {
1848 List<DhcpServerInfo> validServerInfoList = findValidServerInfo(directConnFlag);
1849 DhcpServerInfo foundServerInfo = null;
1850 for (DhcpServerInfo serverInfo : validServerInfoList) {
1851 if (inPort.equals(serverInfo.getDhcpServerConnectPoint().get())) {
1852 foundServerInfo = serverInfo;
Charles Chan750c9992018-02-26 10:44:49 -08001853 log.debug("ServerInfo found for Rcv port {} Server Connect Point {} for {}",
Kalhee Kim495c9b22017-11-07 16:32:09 +00001854 inPort, serverInfo.getDhcpServerConnectPoint(), directConnFlag ? "direct" : "indirect");
1855 break;
Kalhee Kim495c9b22017-11-07 16:32:09 +00001856 }
1857 }
1858 return foundServerInfo;
1859 }
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001860
Kalhee Kim495c9b22017-11-07 16:32:09 +00001861 /**
1862 * Set the dhcp6 lease expiry poll interval value.
1863 *
1864 * @param val poll interval value in seconds
1865 */
Kalhee Kim2f07c602017-11-15 18:57:53 +00001866 @Override
Kalhee Kim495c9b22017-11-07 16:32:09 +00001867 public void setDhcp6PollInterval(int val) {
1868 dhcp6PollInterval = val;
1869 }
1870
Kalhee Kim2f07c602017-11-15 18:57:53 +00001871 /**
1872 * get the dhcp6 lease expiry poll interval value.
1873 * This is a private function
1874 * @return poll interval value in seconds
1875 */
1876 private int getDhcp6PollInterval() {
1877 return dhcp6PollInterval;
1878 }
1879
1880 /**
1881 * Find lease-expired ipaddresses and pd prefixes.
1882 * Removing host/route/fpm entries.
1883 */
1884 public void timeTick() {
1885 long currentTime = System.currentTimeMillis();
1886 Collection<DhcpRecord> records = dhcpRelayStore.getDhcpRecords();
1887
1888 log.debug("timeTick called currenttime {} records num {} ", currentTime, records.size());
1889
1890 records.forEach(record -> {
1891 boolean addrOrPdRemoved = false;
1892 DHCP6.MsgType ip6Status = record.ip6Status().orElse(null);
1893 if (ip6Status == null) {
1894 log.debug("record is not valid v6 record.");
1895 return;
1896 }
1897
1898 if ((currentTime - record.getLastIp6Update()) >
1899 ((record.addrPrefTime() + getDhcp6PollInterval() / 2) * 1000)) {
1900 // remove ipaddress from host/route table
1901 IpAddress ip = record.ip6Address().orElse(null);
1902 if (ip != null) {
1903 if (record.directlyConnected()) {
1904 providerService.removeIpFromHost(HostId.hostId(record.macAddress(),
1905 record.vlanId()), ip);
1906 } else {
1907 MacAddress gwMac = record.nextHop().orElse(null);
1908 if (gwMac == null) {
1909 log.warn("Can't find gateway mac address from record {} for ip6Addr", record);
1910 return;
1911 }
1912 IpAddress nextHopIp = getFirstIpByHost(record.directlyConnected(),
1913 gwMac,
1914 record.vlanId());
Kalhee Kim54a97c92018-04-02 21:23:46 +00001915 Route route = new Route(Route.Source.DHCP, ip.toIpPrefix(), nextHopIp);
Kalhee Kim2f07c602017-11-15 18:57:53 +00001916 routeStore.removeRoute(route);
1917 }
1918 record.updateAddrPrefTime(0);
1919 record.ip6Address(null);
1920 addrOrPdRemoved = true;
1921 dhcpRelayStore.updateDhcpRecord(HostId.hostId(record.macAddress(),
1922 record.vlanId()), record);
1923 log.warn("IP6 address is set to null. delta {} lastUpdate {} addrPrefTime {}",
1924 (currentTime - record.getLastIp6Update()), record.getLastIp6Update(),
1925 record.addrPrefTime());
1926 }
1927 }
1928 if ((currentTime - record.getLastPdUpdate()) >
1929 ((record.pdPrefTime() + getDhcp6PollInterval() / 2) * 1000)) {
1930 // remove PD from route/fpm table
1931 IpPrefix pdIpPrefix = record.pdPrefix().orElse(null);
1932 if (pdIpPrefix != null) {
1933 if (record.directlyConnected()) {
1934 providerService.removeIpFromHost(HostId.hostId(record.macAddress(), record.vlanId()),
1935 pdIpPrefix.address().getIp6Address());
1936 } else {
1937 MacAddress gwMac = record.nextHop().orElse(null);
1938 if (gwMac == null) {
1939 log.warn("Can't find gateway mac address from record {} for PD prefix", record);
1940 return;
1941 }
1942 IpAddress nextHopIp = getFirstIpByHost(record.directlyConnected(),
1943 gwMac,
1944 record.vlanId());
Kalhee Kim54a97c92018-04-02 21:23:46 +00001945 Route route = new Route(Route.Source.DHCP, pdIpPrefix, nextHopIp);
Kalhee Kim2f07c602017-11-15 18:57:53 +00001946 routeStore.removeRoute(route);
1947 if (this.dhcpFpmEnabled) {
1948 dhcpFpmPrefixStore.removeFpmRecord(pdIpPrefix);
1949 }
1950 }
1951 record.updatePdPrefTime(0);
1952 record.pdPrefix(null);
1953 addrOrPdRemoved = true;
1954 dhcpRelayStore.updateDhcpRecord(HostId.hostId(record.macAddress(),
1955 record.vlanId()), record);
1956 log.warn("PD prefix is set to null.delta {} pdPrefTime {}",
1957 (currentTime - record.getLastPdUpdate()), record.pdPrefTime());
1958 }
1959 }
1960 if (addrOrPdRemoved &&
1961 !record.ip6Address().isPresent() && !record.pdPrefix().isPresent()) {
1962 log.warn("ip6Status {} IP6 address and IP6 PD both are null. Remove record.", ip6Status);
1963 dhcpRelayStore.removeDhcpRecord(HostId.hostId(record.macAddress(), record.vlanId()));
1964 }
1965 }
1966 );
1967 }
1968}