blob: 09a118c6108d7d5bcb94378e00316a2259f35b00 [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;
Kalhee Kim45fede42017-09-05 19:05:06 +000022import org.apache.felix.scr.annotations.Activate;
23import org.apache.felix.scr.annotations.Deactivate;
Kalhee Kim45fede42017-09-05 19:05:06 +000024import com.google.common.collect.Sets;
25import com.google.common.collect.ImmutableSet;
Yi Tseng51301292017-07-28 13:02:59 -070026import org.apache.felix.scr.annotations.Component;
27import org.apache.felix.scr.annotations.Property;
Kalhee Kim45fede42017-09-05 19:05:06 +000028import org.apache.felix.scr.annotations.Reference;
29import org.apache.felix.scr.annotations.ReferenceCardinality;
Yi Tseng51301292017-07-28 13:02:59 -070030import org.apache.felix.scr.annotations.Service;
31import 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;
Kalhee Kim45fede42017-09-05 19:05:06 +000042import org.onlab.packet.dhcp.Dhcp6RelayOption;
43import org.onlab.packet.dhcp.Dhcp6InterfaceIdOption;
Kalhee Kim45fede42017-09-05 19:05:06 +000044import org.onlab.packet.dhcp.Dhcp6IaNaOption;
45import org.onlab.packet.dhcp.Dhcp6IaTaOption;
46import org.onlab.packet.dhcp.Dhcp6IaPdOption;
47import org.onlab.packet.dhcp.Dhcp6IaAddressOption;
48import org.onlab.packet.dhcp.Dhcp6IaPrefixOption;
Kalhee Kimea4b6c22017-11-09 14:38:37 +000049import org.onlab.packet.dhcp.Dhcp6ClientIdOption;
50import org.onlab.packet.dhcp.Dhcp6Duid;
Kalhee Kim495c9b22017-11-07 16:32:09 +000051import org.onlab.packet.DHCP6.MsgType;
Kalhee Kim45fede42017-09-05 19:05:06 +000052import org.onlab.util.HexString;
Yi Tseng525ff402017-10-23 19:39:39 -070053import org.onosproject.core.ApplicationId;
54import org.onosproject.core.CoreService;
Yi Tseng51301292017-07-28 13:02:59 -070055import org.onosproject.dhcprelay.api.DhcpHandler;
Yi Tseng919b2df2017-09-07 16:22:51 -070056import org.onosproject.dhcprelay.api.DhcpServerInfo;
Yi Tseng525ff402017-10-23 19:39:39 -070057import org.onosproject.dhcprelay.config.IgnoreDhcpConfig;
Kalhee Kim45fede42017-09-05 19:05:06 +000058import org.onosproject.dhcprelay.store.DhcpRelayStore;
Kalhee Kimea4b6c22017-11-09 14:38:37 +000059import org.onosproject.dhcprelay.store.DhcpRecord;
Kalhee Kimba366062017-11-07 16:32:09 +000060import org.onosproject.dhcprelay.store.DhcpFpmPrefixStore;
Kalhee Kimd94ceea2017-11-29 19:03:02 +000061import org.onosproject.dhcprelay.store.DhcpRelayCounters;
62import org.onosproject.dhcprelay.store.DhcpRelayCountersStore;
Yi Tseng525ff402017-10-23 19:39:39 -070063import org.onosproject.net.Device;
64import org.onosproject.net.DeviceId;
Kalhee Kimba366062017-11-07 16:32:09 +000065import org.onosproject.routing.fpm.api.FpmRecord;
Yi Tseng525ff402017-10-23 19:39:39 -070066import org.onosproject.net.behaviour.Pipeliner;
67import org.onosproject.net.device.DeviceService;
68import org.onosproject.net.flow.DefaultTrafficSelector;
69import org.onosproject.net.flow.TrafficSelector;
70import org.onosproject.net.flowobjective.DefaultForwardingObjective;
71import org.onosproject.net.flowobjective.FlowObjectiveService;
72import org.onosproject.net.flowobjective.ForwardingObjective;
73import org.onosproject.net.flowobjective.Objective;
74import org.onosproject.net.flowobjective.ObjectiveContext;
75import org.onosproject.net.flowobjective.ObjectiveError;
Yi Tsengaa417a62017-09-08 17:22:51 -070076import org.onosproject.net.host.HostProvider;
77import org.onosproject.net.host.HostProviderRegistry;
78import org.onosproject.net.host.HostProviderService;
Kalhee Kim45fede42017-09-05 19:05:06 +000079import org.onosproject.net.host.HostService;
80import org.onosproject.net.host.DefaultHostDescription;
81import org.onosproject.net.host.HostDescription;
82import org.onosproject.net.host.InterfaceIpAddress;
83import org.onosproject.net.host.HostListener;
84import org.onosproject.net.host.HostEvent;
85import org.onosproject.net.intf.Interface;
86import org.onosproject.net.intf.InterfaceService;
Yi Tseng525ff402017-10-23 19:39:39 -070087import org.onosproject.net.packet.PacketPriority;
Yi Tsengaa417a62017-09-08 17:22:51 -070088import org.onosproject.net.provider.ProviderId;
Kalhee Kim45fede42017-09-05 19:05:06 +000089import org.onosproject.routeservice.Route;
90import org.onosproject.routeservice.RouteStore;
Yi Tseng483ac6f2017-08-02 15:03:31 -070091import org.onosproject.dhcprelay.config.DhcpServerConfig;
Yi Tseng51301292017-07-28 13:02:59 -070092import org.onosproject.net.ConnectPoint;
Kalhee Kim45fede42017-09-05 19:05:06 +000093import org.onosproject.net.Host;
94import org.onosproject.net.HostId;
95import org.onosproject.net.HostLocation;
96import org.onosproject.net.packet.DefaultOutboundPacket;
97import org.onosproject.net.packet.OutboundPacket;
Yi Tseng51301292017-07-28 13:02:59 -070098import org.onosproject.net.packet.PacketContext;
Kalhee Kim45fede42017-09-05 19:05:06 +000099import org.onosproject.net.packet.PacketService;
100import org.slf4j.Logger;
101import org.slf4j.LoggerFactory;
102import org.onosproject.net.flow.DefaultTrafficTreatment;
103import org.onosproject.net.flow.TrafficTreatment;
Kalhee Kim495c9b22017-11-07 16:32:09 +0000104import org.onosproject.dhcprelay.Dhcp6HandlerUtil.InternalPacket;
Kalhee Kim45fede42017-09-05 19:05:06 +0000105
Kalhee Kim2f07c602017-11-15 18:57:53 +0000106
Kalhee Kim45fede42017-09-05 19:05:06 +0000107import java.nio.ByteBuffer;
108import java.util.List;
Yi Tseng483ac6f2017-08-02 15:03:31 -0700109import java.util.Collection;
Yi Tseng51301292017-07-28 13:02:59 -0700110import java.util.Optional;
Kalhee Kim45fede42017-09-05 19:05:06 +0000111import java.util.Set;
112import java.util.ArrayList;
Yi Tseng525ff402017-10-23 19:39:39 -0700113import java.util.concurrent.atomic.AtomicInteger;
Kalhee Kim45fede42017-09-05 19:05:06 +0000114import static com.google.common.base.Preconditions.checkNotNull;
115import static com.google.common.base.Preconditions.checkState;
Yi Tseng525ff402017-10-23 19:39:39 -0700116import static org.onosproject.net.flowobjective.Objective.Operation.ADD;
117import static org.onosproject.net.flowobjective.Objective.Operation.REMOVE;
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000118import java.util.concurrent.Semaphore;
Yi Tseng51301292017-07-28 13:02:59 -0700119
120@Component
121@Service
122@Property(name = "version", value = "6")
Yi Tsengaa417a62017-09-08 17:22:51 -0700123public class Dhcp6HandlerImpl implements DhcpHandler, HostProvider {
Charles Chand988c282017-09-12 17:09:32 -0700124 public static final String DHCP_V6_RELAY_APP = "org.onosproject.Dhcp6HandlerImpl";
Saurav Das7c6dec12017-09-13 14:35:56 -0700125 public static final ProviderId PROVIDER_ID = new ProviderId("dhcp6", DHCP_V6_RELAY_APP);
Yi Tseng525ff402017-10-23 19:39:39 -0700126 private static final int IGNORE_CONTROL_PRIORITY = PacketPriority.CONTROL.priorityValue() + 1000;
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000127 private String gCount = "global";
Yi Tseng525ff402017-10-23 19:39:39 -0700128 private static final TrafficSelector CLIENT_SERVER_SELECTOR = DefaultTrafficSelector.builder()
129 .matchEthType(Ethernet.TYPE_IPV6)
130 .matchIPProtocol(IPv6.PROTOCOL_UDP)
131 .matchIPv6Src(IpPrefix.IPV6_LINK_LOCAL_PREFIX)
132 .matchIPv6Dst(Ip6Address.ALL_DHCP_RELAY_AGENTS_AND_SERVERS.toIpPrefix())
133 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_V6_CLIENT_PORT))
134 .matchUdpDst(TpPort.tpPort(UDP.DHCP_V6_SERVER_PORT))
135 .build();
136 private static final TrafficSelector SERVER_RELAY_SELECTOR = DefaultTrafficSelector.builder()
137 .matchEthType(Ethernet.TYPE_IPV6)
138 .matchIPProtocol(IPv6.PROTOCOL_UDP)
139 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_V6_SERVER_PORT))
140 .matchUdpDst(TpPort.tpPort(UDP.DHCP_V6_SERVER_PORT))
141 .build();
142 static final Set<TrafficSelector> DHCP_SELECTORS = ImmutableSet.of(
143 CLIENT_SERVER_SELECTOR,
144 SERVER_RELAY_SELECTOR
145 );
Kalhee Kim45fede42017-09-05 19:05:06 +0000146 private static Logger log = LoggerFactory.getLogger(Dhcp6HandlerImpl.class);
Yi Tseng51301292017-07-28 13:02:59 -0700147
Kalhee Kim45fede42017-09-05 19:05:06 +0000148 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
149 protected DhcpRelayStore dhcpRelayStore;
Yi Tseng51301292017-07-28 13:02:59 -0700150
Kalhee Kim45fede42017-09-05 19:05:06 +0000151 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000152 protected DhcpRelayCountersStore dhcpRelayCountersStore;
153
154 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Kalhee Kim45fede42017-09-05 19:05:06 +0000155 protected PacketService packetService;
156
157 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Kalhee Kim45fede42017-09-05 19:05:06 +0000158 protected RouteStore routeStore;
159
160 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
161 protected InterfaceService interfaceService;
162
163 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
164 protected HostService hostService;
165
Yi Tsengaa417a62017-09-08 17:22:51 -0700166 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
167 protected HostProviderRegistry providerRegistry;
Kalhee Kim45fede42017-09-05 19:05:06 +0000168
Yi Tseng525ff402017-10-23 19:39:39 -0700169 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
170 protected CoreService coreService;
171
172 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Kalhee Kimba366062017-11-07 16:32:09 +0000173 protected DhcpFpmPrefixStore dhcpFpmPrefixStore;
174
175 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Yi Tseng525ff402017-10-23 19:39:39 -0700176 protected DeviceService deviceService;
177
178 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
179 protected FlowObjectiveService flowObjectiveService;
180
Yi Tsengaa417a62017-09-08 17:22:51 -0700181 protected HostProviderService providerService;
Yi Tseng525ff402017-10-23 19:39:39 -0700182 protected ApplicationId appId;
183 protected Multimap<DeviceId, VlanId> ignoredVlans = HashMultimap.create();
184 private InternalHostListener hostListener = new InternalHostListener();
185
Kalhee Kimba366062017-11-07 16:32:09 +0000186 private Boolean dhcpFpmEnabled = false;
187
Kalhee Kim495c9b22017-11-07 16:32:09 +0000188 private Dhcp6HandlerUtil dhcp6HandlerUtil = new Dhcp6HandlerUtil();
189
Yi Tseng919b2df2017-09-07 16:22:51 -0700190 private List<DhcpServerInfo> defaultServerInfoList = Lists.newArrayList();
191 private List<DhcpServerInfo> indirectServerInfoList = Lists.newArrayList();
Kalhee Kim495c9b22017-11-07 16:32:09 +0000192 private class IpAddressInfo {
193 Ip6Address ip6Address;
194 long prefTime;
195 }
196 private class PdPrefixInfo {
197 IpPrefix pdPrefix;
198 long prefTime;
199 }
200 protected int dhcp6PollInterval = 24 * 3600; // 24 hr period
201
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000202 // max 1 thread
203 static Semaphore recordSemaphore = new Semaphore(1);
Kalhee Kim45fede42017-09-05 19:05:06 +0000204
205 // CLIENT message types
206 public static final Set<Byte> MSG_TYPE_FROM_CLIENT =
207 ImmutableSet.of(DHCP6.MsgType.SOLICIT.value(),
208 DHCP6.MsgType.REQUEST.value(),
209 DHCP6.MsgType.REBIND.value(),
210 DHCP6.MsgType.RENEW.value(),
211 DHCP6.MsgType.RELEASE.value(),
212 DHCP6.MsgType.DECLINE.value(),
213 DHCP6.MsgType.CONFIRM.value(),
214 DHCP6.MsgType.RELAY_FORW.value());
215 // SERVER message types
216 public static final Set<Byte> MSG_TYPE_FROM_SERVER =
217 ImmutableSet.of(DHCP6.MsgType.RELAY_REPL.value());
218
219 @Activate
220 protected void activate() {
Yi Tseng525ff402017-10-23 19:39:39 -0700221 appId = coreService.registerApplication(DHCP_V6_RELAY_APP);
Yi Tsengaa417a62017-09-08 17:22:51 -0700222 providerService = providerRegistry.register(this);
Kalhee Kim45fede42017-09-05 19:05:06 +0000223 hostService.addListener(hostListener);
Yi Tseng51301292017-07-28 13:02:59 -0700224 }
225
Kalhee Kim45fede42017-09-05 19:05:06 +0000226 @Deactivate
227 protected void deactivate() {
Yi Tsengaa417a62017-09-08 17:22:51 -0700228 providerRegistry.unregister(this);
Kalhee Kim45fede42017-09-05 19:05:06 +0000229 hostService.removeListener(hostListener);
Yi Tseng919b2df2017-09-07 16:22:51 -0700230 defaultServerInfoList.forEach(this::stopMonitoringIps);
231 defaultServerInfoList.clear();
232 indirectServerInfoList.forEach(this::stopMonitoringIps);
233 indirectServerInfoList.clear();
234 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000235
Yi Tseng919b2df2017-09-07 16:22:51 -0700236 private void stopMonitoringIps(DhcpServerInfo serverInfo) {
237 serverInfo.getDhcpGatewayIp6().ifPresent(gatewayIp -> {
238 hostService.stopMonitoringIp(gatewayIp);
239 });
240 serverInfo.getDhcpServerIp6().ifPresent(serverIp -> {
241 hostService.stopMonitoringIp(serverIp);
242 });
Yi Tseng51301292017-07-28 13:02:59 -0700243 }
244
Yi Tseng51301292017-07-28 13:02:59 -0700245 @Override
Yi Tseng919b2df2017-09-07 16:22:51 -0700246 public List<DhcpServerInfo> getDefaultDhcpServerInfoList() {
247 return defaultServerInfoList;
Yi Tseng51301292017-07-28 13:02:59 -0700248 }
249
250 @Override
Yi Tseng919b2df2017-09-07 16:22:51 -0700251 public List<DhcpServerInfo> getIndirectDhcpServerInfoList() {
252 return indirectServerInfoList;
Yi Tseng51301292017-07-28 13:02:59 -0700253 }
254
255 @Override
Yi Tseng525ff402017-10-23 19:39:39 -0700256 public void updateIgnoreVlanConfig(IgnoreDhcpConfig config) {
257 if (config == null) {
258 ignoredVlans.forEach(((deviceId, vlanId) -> {
259 processIgnoreVlanRule(deviceId, vlanId, REMOVE);
260 }));
261 return;
262 }
263 config.ignoredVlans().forEach((deviceId, vlanId) -> {
264 if (ignoredVlans.get(deviceId).contains(vlanId)) {
265 // don't need to process if it already ignored
266 return;
267 }
268 processIgnoreVlanRule(deviceId, vlanId, ADD);
269 });
270
271 ignoredVlans.forEach((deviceId, vlanId) -> {
272 if (!config.ignoredVlans().get(deviceId).contains(vlanId)) {
273 // not contains in new config, remove it
274 processIgnoreVlanRule(deviceId, vlanId, REMOVE);
275 }
276 });
277 }
278
279 @Override
Saurav Dasb805f1a2017-12-13 16:19:35 -0800280 public void removeIgnoreVlanState(IgnoreDhcpConfig config) {
281 if (config == null) {
282 ignoredVlans.clear();
283 return;
284 }
285 config.ignoredVlans().forEach((deviceId, vlanId) -> {
286 ignoredVlans.remove(deviceId, vlanId);
287 });
288 }
289
290 @Override
Kalhee Kim45fede42017-09-05 19:05:06 +0000291 public void processDhcpPacket(PacketContext context, BasePacket payload) {
292 checkNotNull(payload, "DHCP6 payload can't be null");
293 checkState(payload instanceof DHCP6, "Payload is not a DHCP6");
294 DHCP6 dhcp6Payload = (DHCP6) payload;
295 Ethernet receivedPacket = context.inPacket().parsed();
296
297 if (!configured()) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000298 log.warn("Missing DHCP6 relay server config. Abort packet processing dhcp6 payload {}", dhcp6Payload);
Kalhee Kim45fede42017-09-05 19:05:06 +0000299 return;
300 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000301 byte msgTypeVal = dhcp6Payload.getMsgType();
302 MsgType msgType = DHCP6.MsgType.getType(msgTypeVal);
303 log.debug("msgType is {}", msgType);
Kalhee Kim45fede42017-09-05 19:05:06 +0000304
Kalhee Kim45fede42017-09-05 19:05:06 +0000305 ConnectPoint inPort = context.inPacket().receivedFrom();
Jonathan Hart8ca2bc02017-11-30 18:23:42 -0800306
Kalhee Kim495c9b22017-11-07 16:32:09 +0000307 if (inPort == null) {
308 log.warn("incomming ConnectPoint is null");
309 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000310 Set<Interface> receivingInterfaces = interfaceService.getInterfacesByPort(inPort);
311 //ignore the packets if dhcp client interface is not configured on onos.
312 if (receivingInterfaces.isEmpty()) {
313 log.warn("Virtual interface is not configured on {}", inPort);
314 return;
315 }
316
Kalhee Kim495c9b22017-11-07 16:32:09 +0000317 if (MSG_TYPE_FROM_CLIENT.contains(msgTypeVal)) {
Kalhee Kim45fede42017-09-05 19:05:06 +0000318
Kalhee Kim495c9b22017-11-07 16:32:09 +0000319 List<InternalPacket> ethernetClientPacket =
Kalhee Kim45fede42017-09-05 19:05:06 +0000320 processDhcp6PacketFromClient(context, receivedPacket, receivingInterfaces);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000321 for (InternalPacket internalPacket : ethernetClientPacket) {
322 forwardPacket(internalPacket);
Kalhee Kim45fede42017-09-05 19:05:06 +0000323 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000324 } else if (MSG_TYPE_FROM_SERVER.contains(msgTypeVal)) {
325 log.debug("calling processDhcp6PacketFromServer with RELAY_REPL {}", msgTypeVal);
Kalhee Kim45fede42017-09-05 19:05:06 +0000326 InternalPacket ethernetPacketReply =
327 processDhcp6PacketFromServer(context, receivedPacket, receivingInterfaces);
328 if (ethernetPacketReply != null) {
329 forwardPacket(ethernetPacketReply);
330 }
331 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000332 log.warn("Not so fast, packet type {} not supported yet", msgTypeVal);
Kalhee Kim45fede42017-09-05 19:05:06 +0000333 }
334 }
335
Kalhee Kim45fede42017-09-05 19:05:06 +0000336 /**
337 * Checks if this app has been configured.
338 *
339 * @return true if all information we need have been initialized
340 */
341 public boolean configured() {
Yi Tseng919b2df2017-09-07 16:22:51 -0700342 return !defaultServerInfoList.isEmpty();
Kalhee Kim45fede42017-09-05 19:05:06 +0000343 }
344
Yi Tsengaa417a62017-09-08 17:22:51 -0700345 @Override
346 public ProviderId id() {
Charles Chand988c282017-09-12 17:09:32 -0700347 return PROVIDER_ID;
Yi Tsengaa417a62017-09-08 17:22:51 -0700348 }
349
350 @Override
351 public void triggerProbe(Host host) {
352 // Do nothing here
353 }
354
Kalhee Kim495c9b22017-11-07 16:32:09 +0000355
Kalhee Kim45fede42017-09-05 19:05:06 +0000356
357 //forward the packet to ConnectPoint where the DHCP server is attached.
358 private void forwardPacket(InternalPacket packet) {
359 //send Packetout to dhcp server connectpoint.
360 if (packet.destLocation != null) {
361 TrafficTreatment t = DefaultTrafficTreatment.builder()
362 .setOutput(packet.destLocation.port()).build();
363 OutboundPacket o = new DefaultOutboundPacket(
364 packet.destLocation.deviceId(), t, ByteBuffer.wrap(packet.packet.serialize()));
365 if (log.isTraceEnabled()) {
366 log.trace("Relaying packet to destination {}", packet.destLocation);
367 }
368 packetService.emit(o);
369 } // if
370 }
371
Kalhee Kim45fede42017-09-05 19:05:06 +0000372
373
Kalhee Kim45fede42017-09-05 19:05:06 +0000374
375 /**
376 * extract from dhcp6 packet client ipv6 address of given by dhcp server.
377 *
378 * @param dhcp6 the dhcp6 packet
Kalhee Kim495c9b22017-11-07 16:32:09 +0000379 * @return IpAddressInfo IpAddressInfo given by dhcp server, or null if not exists
Kalhee Kim45fede42017-09-05 19:05:06 +0000380 */
Kalhee Kim495c9b22017-11-07 16:32:09 +0000381 private IpAddressInfo extractIpAddress(DHCP6 dhcp6) {
382 IpAddressInfo ipInfo = new IpAddressInfo();
Kalhee Kim45fede42017-09-05 19:05:06 +0000383
384 log.debug("extractIpAddress enters dhcp6 {}.", dhcp6);
385 // Extract IPv6 address from IA NA ot IA TA option
386 Optional<Dhcp6IaNaOption> iaNaOption = dhcp6.getOptions()
387 .stream()
388 .filter(opt -> opt instanceof Dhcp6IaNaOption)
389 .map(opt -> (Dhcp6IaNaOption) opt)
390 .findFirst();
391 Optional<Dhcp6IaTaOption> iaTaOption = dhcp6.getOptions()
392 .stream()
393 .filter(opt -> opt instanceof Dhcp6IaTaOption)
394 .map(opt -> (Dhcp6IaTaOption) opt)
395 .findFirst();
396 Optional<Dhcp6IaAddressOption> iaAddressOption;
397 if (iaNaOption.isPresent()) {
398 log.debug("Found IPv6 address from iaNaOption {}", iaNaOption);
399
400 iaAddressOption = iaNaOption.get().getOptions().stream()
401 .filter(opt -> opt instanceof Dhcp6IaAddressOption)
402 .map(opt -> (Dhcp6IaAddressOption) opt)
403 .findFirst();
404 } else if (iaTaOption.isPresent()) {
405 log.debug("Found IPv6 address from iaTaOption {}", iaTaOption);
406
407 iaAddressOption = iaTaOption.get().getOptions().stream()
408 .filter(opt -> opt instanceof Dhcp6IaAddressOption)
409 .map(opt -> (Dhcp6IaAddressOption) opt)
410 .findFirst();
411 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000412 log.info("No IPv6 address found from iaTaOption {}", iaTaOption);
Kalhee Kim45fede42017-09-05 19:05:06 +0000413 iaAddressOption = Optional.empty();
414 }
415 if (iaAddressOption.isPresent()) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000416 ipInfo.ip6Address = iaAddressOption.get().getIp6Address();
417 ipInfo.prefTime = iaAddressOption.get().getPreferredLifetime();
Kalhee Kim45fede42017-09-05 19:05:06 +0000418 log.debug("Found IPv6 address from iaAddressOption {}", iaAddressOption);
Kalhee Kim45fede42017-09-05 19:05:06 +0000419 } else {
420 log.debug("Can't find IPv6 address from DHCPv6 {}", dhcp6);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000421 return null;
Kalhee Kim45fede42017-09-05 19:05:06 +0000422 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000423 return ipInfo;
Kalhee Kim45fede42017-09-05 19:05:06 +0000424 }
425 /**
426 * extract from dhcp6 packet Prefix prefix provided by dhcp server.
427 *
428 * @param dhcp6 the dhcp6 payload
429 * @return IpPrefix Prefix Delegation prefix, or null if not exists.
430 */
Kalhee Kim495c9b22017-11-07 16:32:09 +0000431 private PdPrefixInfo extractPrefix(DHCP6 dhcp6) {
432 log.debug("extractPrefix enters {}", dhcp6);
Kalhee Kim45fede42017-09-05 19:05:06 +0000433
434 // extract prefix
Kalhee Kim495c9b22017-11-07 16:32:09 +0000435 PdPrefixInfo pdPrefixInfo = new PdPrefixInfo();
Kalhee Kim45fede42017-09-05 19:05:06 +0000436
437 Ip6Address prefixAddress = null;
438
439 // Extract IPv6 prefix from IA PD option
440 Optional<Dhcp6IaPdOption> iaPdOption = dhcp6.getOptions()
441 .stream()
442 .filter(opt -> opt instanceof Dhcp6IaPdOption)
443 .map(opt -> (Dhcp6IaPdOption) opt)
444 .findFirst();
445
446 Optional<Dhcp6IaPrefixOption> iaPrefixOption;
447 if (iaPdOption.isPresent()) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000448 log.debug("IA_PD option found {}", iaPdOption);
Kalhee Kim45fede42017-09-05 19:05:06 +0000449
450 iaPrefixOption = iaPdOption.get().getOptions().stream()
451 .filter(opt -> opt instanceof Dhcp6IaPrefixOption)
452 .map(opt -> (Dhcp6IaPrefixOption) opt)
453 .findFirst();
454 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000455 log.debug("IA_PD option NOT found");
Kalhee Kim45fede42017-09-05 19:05:06 +0000456
457 iaPrefixOption = Optional.empty();
458 }
459 if (iaPrefixOption.isPresent()) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000460 log.debug("IAPrefix Option within IA_PD option found {}", iaPrefixOption);
Kalhee Kim45fede42017-09-05 19:05:06 +0000461
462 prefixAddress = iaPrefixOption.get().getIp6Prefix();
463 int prefixLen = (int) iaPrefixOption.get().getPrefixLength();
Kalhee Kim495c9b22017-11-07 16:32:09 +0000464 log.debug("Prefix length is {} bits", prefixLen);
465 pdPrefixInfo.pdPrefix = IpPrefix.valueOf(prefixAddress, prefixLen);
466 pdPrefixInfo.prefTime = iaPrefixOption.get().getPreferredLifetime();
Kalhee Kim45fede42017-09-05 19:05:06 +0000467 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000468 log.debug("Can't find IPv6 prefix from DHCPv6 {}", dhcp6);
469 return null;
Kalhee Kim45fede42017-09-05 19:05:06 +0000470 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000471 return pdPrefixInfo;
Kalhee Kim45fede42017-09-05 19:05:06 +0000472 }
473
474 /**
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000475 * extract from dhcp6 packet ClientIdOption.
476 *
477 * @param directConnFlag directly connected host
478 * @param dhcp6Payload the dhcp6 payload
479 * @return Dhcp6ClientIdOption clientIdOption, or null if not exists.
480 */
481 private Dhcp6ClientIdOption extractClinedId(Boolean directConnFlag, DHCP6 dhcp6Payload) {
482 Dhcp6ClientIdOption clientIdOption;
483
484 if (directConnFlag) {
485 clientIdOption = dhcp6Payload.getOptions()
486 .stream()
487 .filter(opt -> opt instanceof Dhcp6ClientIdOption)
488 .map(opt -> (Dhcp6ClientIdOption) opt)
489 .findFirst()
490 .orElse(null);
491 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000492 DHCP6 leafDhcp = dhcp6HandlerUtil.getDhcp6Leaf(dhcp6Payload);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000493 clientIdOption = leafDhcp.getOptions()
494 .stream()
495 .filter(opt -> opt instanceof Dhcp6ClientIdOption)
496 .map(opt -> (Dhcp6ClientIdOption) opt)
497 .findFirst()
498 .orElse(null);
499 }
500
501 return clientIdOption;
502 }
503 /**
Kalhee Kim495c9b22017-11-07 16:32:09 +0000504 * remove host or route and update dhcp relay record attributes.
Kalhee Kim45fede42017-09-05 19:05:06 +0000505 *
506 * @param directConnFlag flag to show that packet is from directly connected client
Kalhee Kim495c9b22017-11-07 16:32:09 +0000507 * @param location client side connect point
Kalhee Kim45fede42017-09-05 19:05:06 +0000508 * @param dhcp6Packet the dhcp6 payload
509 * @param clientPacket client's ethernet packet
510 * @param clientIpv6 client's Ipv6 packet
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +0000511 * @param clientInterface client interfaces
Kalhee Kim45fede42017-09-05 19:05:06 +0000512 */
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000513 private void removeHostOrRoute(boolean directConnFlag, ConnectPoint location,
514 DHCP6 dhcp6Packet,
Kalhee Kim45fede42017-09-05 19:05:06 +0000515 Ethernet clientPacket, IPv6 clientIpv6,
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +0000516 Interface clientInterface) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000517 log.debug("removeHostOrRoute enters {}", dhcp6Packet);
Kalhee Kim121ba922017-11-01 17:56:44 +0000518 VlanId vlanId = clientInterface.vlan();
Kalhee Kim495c9b22017-11-07 16:32:09 +0000519 MacAddress srcMac = clientPacket.getSourceMAC(); // could be gw or host
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000520 MacAddress leafClientMac;
521 byte leafMsgType;
Kalhee Kim495c9b22017-11-07 16:32:09 +0000522 log.debug("client mac {} client vlan {}", HexString.toHexString(srcMac.toBytes(), ":"), vlanId);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000523
524 Dhcp6ClientIdOption clientIdOption = extractClinedId(directConnFlag, dhcp6Packet);
525 if (clientIdOption != null) {
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000526 if ((clientIdOption.getDuid().getDuidType() == Dhcp6Duid.DuidType.DUID_LLT) ||
527 (clientIdOption.getDuid().getDuidType() == Dhcp6Duid.DuidType.DUID_LL)) {
528 leafClientMac = MacAddress.valueOf(clientIdOption.getDuid().getLinkLayerAddress());
529 } else {
530 log.warn("Link-Layer Address not supported in CLIENTID option. No DhcpRelay Record created.");
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000531 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_LINKLOCAL_FAIL);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000532 return;
533 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000534 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000535 log.warn("CLIENTID option NOT found. Don't create DhcpRelay Record.");
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000536 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_CLIENTID_FAIL);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000537 return;
538 }
539
Kalhee Kim495c9b22017-11-07 16:32:09 +0000540 HostId leafHostId = HostId.hostId(leafClientMac, vlanId);
541 DhcpRecord record = dhcpRelayStore.getDhcpRecord(leafHostId).orElse(null);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000542 if (record == null) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000543 record = new DhcpRecord(leafHostId);
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000544 } else {
545 record = record.clone();
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000546 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000547
Kalhee Kim495c9b22017-11-07 16:32:09 +0000548 Boolean isMsgRelease = dhcp6HandlerUtil.isDhcp6Release(dhcp6Packet);
549 IpAddressInfo ipInfo;
550 PdPrefixInfo pdInfo = null;
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000551 if (directConnFlag) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000552 // Add to host store if it is connected to network directly
553 ipInfo = extractIpAddress(dhcp6Packet);
554 if (ipInfo != null) {
555 if (isMsgRelease) {
556 HostId hostId = HostId.hostId(srcMac, vlanId);
557 log.debug("remove Host {} ip for directly connected.", hostId.toString());
558 providerService.removeIpFromHost(hostId, ipInfo.ip6Address);
Kalhee Kim45fede42017-09-05 19:05:06 +0000559 }
560 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000561 log.debug("ipAddress not found. Do not remove Host {} for directly connected.",
562 HostId.hostId(srcMac, vlanId).toString());
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000563 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000564 leafMsgType = dhcp6Packet.getMsgType();
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000565 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000566 // Remove from route store if it is not connected to network directly
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000567 // pick out the first link-local ip address
Kalhee Kim495c9b22017-11-07 16:32:09 +0000568 IpAddress nextHopIp = getFirstIpByHost(directConnFlag, srcMac, vlanId);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000569 if (nextHopIp == null) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000570 log.warn("Can't find link-local IP address of gateway mac {} vlanId {}", srcMac, vlanId);
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000571 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_LINKLOCAL_GW);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000572 return;
573 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000574
Kalhee Kim495c9b22017-11-07 16:32:09 +0000575 DHCP6 leafDhcp = dhcp6HandlerUtil.getDhcp6Leaf(dhcp6Packet);
576 ipInfo = extractIpAddress(leafDhcp);
577 if (ipInfo == null) {
578 log.debug("ip is null");
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000579 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000580 if (isMsgRelease) {
581 Route routeForIP = new Route(Route.Source.STATIC, ipInfo.ip6Address.toIpPrefix(), nextHopIp);
582 log.debug("removing route of 128 address for indirectly connected.");
583 log.debug("128 ip {}, nexthop {}",
584 HexString.toHexString(ipInfo.ip6Address.toOctets(), ":"),
585 HexString.toHexString(nextHopIp.toOctets(), ":"));
586 routeStore.removeRoute(routeForIP);
Kalhee Kim45fede42017-09-05 19:05:06 +0000587 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000588 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000589
Kalhee Kim495c9b22017-11-07 16:32:09 +0000590 pdInfo = extractPrefix(leafDhcp);
591 if (pdInfo == null) {
592 log.debug("ipPrefix is null ");
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000593 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000594 if (isMsgRelease) {
595 Route routeForPrefix = new Route(Route.Source.STATIC, pdInfo.pdPrefix, nextHopIp);
596 log.debug("removing route of PD for indirectly connected.");
597 log.debug("pd ip {}, nexthop {}",
598 HexString.toHexString(pdInfo.pdPrefix.address().toOctets(), ":"),
599 HexString.toHexString(nextHopIp.toOctets(), ":"));
600
601 routeStore.removeRoute(routeForPrefix);
Kalhee Kimba366062017-11-07 16:32:09 +0000602 if (this.dhcpFpmEnabled) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000603 dhcpFpmPrefixStore.removeFpmRecord(pdInfo.pdPrefix);
Kalhee Kimba366062017-11-07 16:32:09 +0000604 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000605 }
606 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000607 leafMsgType = leafDhcp.getMsgType();
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000608 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000609
Kalhee Kim495c9b22017-11-07 16:32:09 +0000610 if (isMsgRelease) {
611 log.debug("DHCP6 RELEASE msg.");
612 if (record != null) {
613 if (ipInfo != null) {
614 log.debug("DhcpRelay Record ip6Address is set to null.");
615 record.ip6Address(null);
616 }
617 if (pdInfo != null) {
618 log.debug("DhcpRelay Record pdPrefix is set to null.");
619 }
620
621 if (!record.ip6Address().isPresent() && !record.pdPrefix().isPresent()) {
622 log.warn("IP6 address and IP6 PD both are null. Remove record.");
623 // do not remove a record. Let timer task handler it.
624 //dhcpRelayStore.removeDhcpRecord(HostId.hostId(leafClientMac, vlanId));
625 }
626 }
627 }
628
Ray Milkeyffe1a332018-01-24 10:41:14 -0800629 if (record != null) {
630 record.getV6Counters().incrementCounter(dhcp6HandlerUtil.getMsgTypeStr(leafMsgType));
631 record.addLocation(new HostLocation(location, System.currentTimeMillis()));
632 record.ip6Status(DHCP6.MsgType.getType(leafMsgType));
633 record.setDirectlyConnected(directConnFlag);
634 if (!directConnFlag) {
635 // Update gateway mac address if the host is not directly connected
636 record.nextHop(srcMac);
637 }
638 record.updateLastSeen();
Kalhee Kim495c9b22017-11-07 16:32:09 +0000639 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000640 dhcpRelayStore.updateDhcpRecord(leafHostId, record);
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000641 // TODO Use AtomicInteger for the counters
642 try {
643 recordSemaphore.acquire();
644 try {
645 dhcpRelayCountersStore.incrementCounter(gCount, dhcp6HandlerUtil.getMsgTypeStr(leafMsgType));
646 } finally {
647 // calling release() after a successful acquire()
648 recordSemaphore.release();
649 }
650 } catch (InterruptedException e) {
Ray Milkeyba547f92018-02-01 15:22:31 -0800651 Thread.currentThread().interrupt();
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000652 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000653 }
654
655 /**
656 * add host or route and update dhcp relay record.
657 *
658 * @param directConnFlag flag to show that packet is from directly connected client
659 * @param location client side connect point
660 * @param dhcp6Relay the dhcp6 payload
661 * @param embeddedDhcp6 the dhcp6 payload within relay
662 * @param srcMac client gw/host macAddress
663 * @param clientInterface client interface
664 */
665 private void addHostOrRoute(boolean directConnFlag, ConnectPoint location, DHCP6 dhcp6Relay,
666 DHCP6 embeddedDhcp6, MacAddress srcMac, Interface clientInterface) {
667 log.debug("addHostOrRoute entered.");
668 VlanId vlanId = clientInterface.vlan();
669 Boolean isMsgReply = dhcp6HandlerUtil.isDhcp6Reply(dhcp6Relay);
670 MacAddress leafClientMac;
671 Byte leafMsgType;
672
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000673 Dhcp6ClientIdOption clientIdOption = extractClinedId(directConnFlag, embeddedDhcp6);
674 if (clientIdOption != null) {
675 log.debug("CLIENTID option found {}", clientIdOption);
676 if ((clientIdOption.getDuid().getDuidType() == Dhcp6Duid.DuidType.DUID_LLT) ||
677 (clientIdOption.getDuid().getDuidType() == Dhcp6Duid.DuidType.DUID_LL)) {
678 leafClientMac = MacAddress.valueOf(clientIdOption.getDuid().getLinkLayerAddress());
679 } else {
680 log.warn("Link-Layer Address not supported in CLIENTID option. No DhcpRelay Record created.");
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000681 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_LINKLOCAL_FAIL);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000682 return;
683 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000684 } else {
685 log.warn("CLIENTID option NOT found. No DhcpRelay Record created.");
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000686 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_CLIENTID_FAIL);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000687 return;
688 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000689 HostId leafHostId = HostId.hostId(leafClientMac, vlanId);
690 DhcpRecord record = dhcpRelayStore.getDhcpRecord(leafHostId).orElse(null);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000691 if (record == null) {
692 record = new DhcpRecord(HostId.hostId(leafClientMac, vlanId));
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000693 } else {
694 record = record.clone();
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000695 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000696
697 IpAddressInfo ipInfo;
698 PdPrefixInfo pdInfo = null;
699 if (directConnFlag) {
700 // Add to host store if it connect to network directly
701 ipInfo = extractIpAddress(embeddedDhcp6);
702 if (ipInfo != null) {
703 if (isMsgReply) {
704 Set<IpAddress> ips = Sets.newHashSet(ipInfo.ip6Address);
705 HostId hostId = HostId.hostId(srcMac, vlanId);
706 Host host = hostService.getHost(hostId);
707 HostLocation hostLocation = new HostLocation(clientInterface.connectPoint(),
708 System.currentTimeMillis());
709 Set<HostLocation> hostLocations = Sets.newHashSet(hostLocation);
710 if (host != null) {
711 // Dual homing support:
712 // if host exists, use old locations and new location
713 hostLocations.addAll(host.locations());
714 }
715 HostDescription desc = new DefaultHostDescription(srcMac, vlanId, hostLocations, ips,
716 false);
717 log.debug("adding Host for directly connected.");
718 log.debug("client mac {} client vlan {} hostlocation {}",
719 HexString.toHexString(srcMac.toBytes(), ":"), vlanId, hostLocation.toString());
720 // Replace the ip when dhcp server give the host new ip address
721 providerService.hostDetected(hostId, desc, false);
722 }
723 } else {
724 log.warn("ipAddress not found. Do not add Host {} for directly connected.",
725 HostId.hostId(srcMac, vlanId).toString());
726 }
727 leafMsgType = embeddedDhcp6.getMsgType();
728 } else {
729 // Add to route store if it does not connect to network directly
730 // pick out the first link-local ip address
731 IpAddress nextHopIp = getFirstIpByHost(directConnFlag, srcMac, vlanId);
732 if (nextHopIp == null) {
733 log.warn("Can't find link-local IP address of gateway mac {} vlanId {}", srcMac, vlanId);
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000734 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_LINKLOCAL_GW);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000735 return;
736 }
737
738 DHCP6 leafDhcp = dhcp6HandlerUtil.getDhcp6Leaf(embeddedDhcp6);
739 ipInfo = extractIpAddress(leafDhcp);
740 if (ipInfo == null) {
741 log.debug("ip is null");
742 } else {
743 if (isMsgReply) {
744 Route routeForIP = new Route(Route.Source.STATIC, ipInfo.ip6Address.toIpPrefix(), nextHopIp);
745 log.debug("adding Route of 128 address for indirectly connected.");
746 routeStore.updateRoute(routeForIP);
747 }
748 }
749
750 pdInfo = extractPrefix(leafDhcp);
751 if (pdInfo == null) {
752 log.debug("ipPrefix is null ");
753 } else {
754 if (isMsgReply) {
755 Route routeForPrefix = new Route(Route.Source.STATIC, pdInfo.pdPrefix, nextHopIp);
756 log.debug("adding Route of PD for indirectly connected.");
757 routeStore.updateRoute(routeForPrefix);
758 if (this.dhcpFpmEnabled) {
759 FpmRecord fpmRecord = new FpmRecord(pdInfo.pdPrefix, nextHopIp, FpmRecord.Type.DHCP_RELAY);
760 dhcpFpmPrefixStore.addFpmRecord(pdInfo.pdPrefix, fpmRecord);
761 }
762 }
763 }
764 leafMsgType = leafDhcp.getMsgType();
765 }
766 if (leafMsgType == DHCP6.MsgType.RELEASE.value() ||
767 (leafMsgType == DHCP6.MsgType.REPLY.value()) && ipInfo == null) {
768 log.warn("DHCP6 RELEASE/REPLY(null ip) from Server. MsgType {}", leafMsgType);
769 //return;
770 }
771
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000772 record.addLocation(new HostLocation(location, System.currentTimeMillis()));
Kalhee Kim2f07c602017-11-15 18:57:53 +0000773
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000774 if (leafMsgType == DHCP6.MsgType.REPLY.value()) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000775 if (ipInfo != null) {
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000776 log.debug("IP6 address is being stored into dhcp-relay store.");
Kalhee Kim495c9b22017-11-07 16:32:09 +0000777 log.debug("IP6 address {}", HexString.toHexString(ipInfo.ip6Address.toOctets(), ":"));
778 record.ip6Address(ipInfo.ip6Address);
Kalhee Kim2f07c602017-11-15 18:57:53 +0000779 record.updateAddrPrefTime(ipInfo.prefTime);
780 record.updateLastIp6Update();
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000781 } else {
782 log.debug("IP6 address is not returned from server. Maybe only PD is returned.");
783 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000784 if (pdInfo != null) {
785 log.debug("IP6 PD address {}",
786 HexString.toHexString(pdInfo.pdPrefix.address().toOctets(), ":"));
Kalhee Kim2f07c602017-11-15 18:57:53 +0000787 record.pdPrefix(pdInfo.pdPrefix);
788 record.updatePdPrefTime(pdInfo.prefTime);
789 record.updateLastPdUpdate();
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000790 } else {
791 log.debug("IP6 PD address is not returned from server. Maybe only IPAddress is returned.");
792 }
793 }
Kalhee Kim2f07c602017-11-15 18:57:53 +0000794
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000795 record.getV6Counters().incrementCounter(dhcp6HandlerUtil.getMsgTypeStr(leafMsgType));
Kalhee Kim495c9b22017-11-07 16:32:09 +0000796 record.ip6Status(DHCP6.MsgType.getType(leafMsgType));
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000797 record.setDirectlyConnected(directConnFlag);
798 record.updateLastSeen();
Kalhee Kim495c9b22017-11-07 16:32:09 +0000799 dhcpRelayStore.updateDhcpRecord(leafHostId, record);
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000800 // TODO Use AtomicInteger for the counters
801 try {
802 recordSemaphore.acquire();
803 try {
804 dhcpRelayCountersStore.incrementCounter(gCount, dhcp6HandlerUtil.getMsgTypeStr(leafMsgType));
805 } finally {
806 // calling release() after a successful acquire()
807 recordSemaphore.release();
808 }
809 } catch (InterruptedException e) {
Ray Milkeyba547f92018-02-01 15:22:31 -0800810 Thread.currentThread().interrupt();
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000811 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000812 }
813
814 /**
Kalhee Kim495c9b22017-11-07 16:32:09 +0000815 * build the DHCP6 solicit/request packet with gatewayip.
Kalhee Kim45fede42017-09-05 19:05:06 +0000816 *
817 * @param context packet context
818 * @param clientPacket client ethernet packet
819 * @param clientInterfaces set of client side interfaces
820 */
Kalhee Kim495c9b22017-11-07 16:32:09 +0000821 private List<InternalPacket> processDhcp6PacketFromClient(PacketContext context,
822 Ethernet clientPacket,
823 Set<Interface> clientInterfaces) {
Yi Tseng3bd57ac2017-11-29 14:39:18 -0800824 ConnectPoint receivedFrom = context.inPacket().receivedFrom();
825 DeviceId receivedFromDevice = receivedFrom.deviceId();
Kalhee Kim495c9b22017-11-07 16:32:09 +0000826 Ip6Address relayAgentIp = dhcp6HandlerUtil.getRelayAgentIPv6Address(clientInterfaces);
Yi Tseng3bd57ac2017-11-29 14:39:18 -0800827 MacAddress relayAgentMac = clientInterfaces.iterator().next().mac();
828 if (relayAgentIp == null || relayAgentMac == null) {
829 log.warn("Missing DHCP relay agent interface Ipv6 addr config for "
Kalhee Kim495c9b22017-11-07 16:32:09 +0000830 + "packet from client on port: {}. Aborting packet processing",
831 clientInterfaces.iterator().next().connectPoint());
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000832 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_CLIENT_INTF_MAC);
Charles Chan239492c2018-01-22 13:27:28 -0800833 return Lists.newArrayList();
Kalhee Kim121ba922017-11-01 17:56:44 +0000834 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000835
Kalhee Kim495c9b22017-11-07 16:32:09 +0000836 IPv6 clientIpv6 = (IPv6) clientPacket.getPayload();
837 UDP clientUdp = (UDP) clientIpv6.getPayload();
838 DHCP6 clientDhcp6 = (DHCP6) clientUdp.getPayload();
839
840 boolean directConnFlag = dhcp6HandlerUtil.directlyConnected(clientDhcp6);
Charles Chana990ce92017-10-30 10:22:50 -0700841
Kalhee Kim121ba922017-11-01 17:56:44 +0000842 ConnectPoint clientConnectionPoint = context.inPacket().receivedFrom();
843 VlanId vlanIdInUse = VlanId.vlanId(clientPacket.getVlanID());
844 Interface clientInterface = interfaceService.getInterfacesByPort(clientConnectionPoint)
Kalhee Kim495c9b22017-11-07 16:32:09 +0000845 .stream().filter(iface -> dhcp6HandlerUtil.interfaceContainsVlan(iface, vlanIdInUse))
846 .findFirst()
847 .orElse(null);
Kalhee Kim45fede42017-09-05 19:05:06 +0000848
Kalhee Kim495c9b22017-11-07 16:32:09 +0000849 List<InternalPacket> internalPackets = new ArrayList<>();
850 List<DhcpServerInfo> serverInfoList = findValidServerInfo(directConnFlag);
851 List<DhcpServerInfo> copyServerInfoList = new ArrayList<DhcpServerInfo>(serverInfoList);
Kalhee Kim45b24182017-10-18 18:30:23 +0000852
Kalhee Kim495c9b22017-11-07 16:32:09 +0000853 for (DhcpServerInfo serverInfo : copyServerInfoList) {
854 if (!dhcp6HandlerUtil.checkDhcpServerConnPt(directConnFlag, serverInfo)) {
855 log.warn("Can't get server connect point, ignore");
856 continue;
857 }
858 DhcpServerInfo newServerInfo = getHostInfoForServerInfo(serverInfo, serverInfoList);
859 if (newServerInfo == null) {
860 log.warn("Can't get server interface with host info resolved, ignore");
861 continue;
862 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000863
Kalhee Kim495c9b22017-11-07 16:32:09 +0000864 Interface serverInterface = getServerInterface(newServerInfo);
865 if (serverInterface == null) {
866 log.warn("Can't get server interface, ignore");
867 continue;
868 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000869
Kalhee Kim495c9b22017-11-07 16:32:09 +0000870 Ethernet etherReply = dhcp6HandlerUtil.buildDhcp6PacketFromClient(context, clientPacket,
871 clientInterfaces, newServerInfo, serverInterface);
872 removeHostOrRoute(directConnFlag, clientConnectionPoint, clientDhcp6, clientPacket,
873 clientIpv6, clientInterface);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000874
Kalhee Kim495c9b22017-11-07 16:32:09 +0000875 InternalPacket internalPacket = new Dhcp6HandlerUtil().new InternalPacket(etherReply,
876 serverInfo.getDhcpServerConnectPoint().get());
877 internalPackets.add(internalPacket);
878 }
879 log.debug("num of client packets to send is{}", internalPackets.size());
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000880
Kalhee Kim495c9b22017-11-07 16:32:09 +0000881 return internalPackets;
882 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000883
884 /**
Kalhee Kim45fede42017-09-05 19:05:06 +0000885 * process the DHCP6 relay-reply packet from dhcp server.
886 *
887 * @param context packet context
888 * @param receivedPacket server ethernet packet
889 * @param recevingInterfaces set of server side interfaces
Kalhee Kim495c9b22017-11-07 16:32:09 +0000890 * @return internalPacket toward client
Kalhee Kim45fede42017-09-05 19:05:06 +0000891 */
892 private InternalPacket processDhcp6PacketFromServer(PacketContext context,
893 Ethernet receivedPacket, Set<Interface> recevingInterfaces) {
Kalhee Kim45fede42017-09-05 19:05:06 +0000894 // get dhcp6 header.
Ray Milkeyf0c47612017-09-28 11:29:38 -0700895 Ethernet etherReply = receivedPacket.duplicate();
Kalhee Kim45fede42017-09-05 19:05:06 +0000896 IPv6 ipv6Packet = (IPv6) etherReply.getPayload();
897 UDP udpPacket = (UDP) ipv6Packet.getPayload();
898 DHCP6 dhcp6Relay = (DHCP6) udpPacket.getPayload();
Kalhee Kim495c9b22017-11-07 16:32:09 +0000899 Boolean directConnFlag = dhcp6HandlerUtil.directlyConnected(dhcp6Relay);
Kalhee Kim45fede42017-09-05 19:05:06 +0000900
Kalhee Kim495c9b22017-11-07 16:32:09 +0000901 DHCP6 embeddedDhcp6 = dhcp6Relay.getOptions().stream()
902 .filter(opt -> opt instanceof Dhcp6RelayOption)
903 .map(BasePacket::getPayload)
904 .map(pld -> (DHCP6) pld)
905 .findFirst()
906 .orElse(null);
Kalhee Kimd21029f2017-09-26 20:21:53 +0000907 ConnectPoint inPort = context.inPacket().receivedFrom();
Kalhee Kim495c9b22017-11-07 16:32:09 +0000908 DhcpServerInfo foundServerInfo = findServerInfoFromServer(directConnFlag, inPort);
Kalhee Kimd21029f2017-09-26 20:21:53 +0000909
Kalhee Kim495c9b22017-11-07 16:32:09 +0000910 if (foundServerInfo == null) {
911 log.warn("Cannot find server info");
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000912 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_SERVER_INFO);
Kalhee Kimd21029f2017-09-26 20:21:53 +0000913 return null;
Kalhee Kim495c9b22017-11-07 16:32:09 +0000914 } else {
915 if (dhcp6HandlerUtil.isServerIpEmpty(foundServerInfo)) {
916 log.warn("Cannot find server info's ipaddress");
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000917 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_SERVER_IP6ADDR);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000918 return null;
919 }
Kalhee Kimd21029f2017-09-26 20:21:53 +0000920 }
921
Kalhee Kim45fede42017-09-05 19:05:06 +0000922 Dhcp6InterfaceIdOption interfaceIdOption = dhcp6Relay.getOptions().stream()
923 .filter(opt -> opt instanceof Dhcp6InterfaceIdOption)
924 .map(opt -> (Dhcp6InterfaceIdOption) opt)
925 .findFirst()
926 .orElse(null);
Kalhee Kim45fede42017-09-05 19:05:06 +0000927 if (interfaceIdOption == null) {
928 log.warn("Interface Id option is not present, abort packet...");
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000929 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.OPTION_MISSING_FAIL);
Kalhee Kim45fede42017-09-05 19:05:06 +0000930 return null;
931 }
932
933 MacAddress peerMac = interfaceIdOption.getMacAddress();
934 String clientConnectionPointStr = new String(interfaceIdOption.getInPort());
Kalhee Kim45fede42017-09-05 19:05:06 +0000935 ConnectPoint clientConnectionPoint = ConnectPoint.deviceConnectPoint(clientConnectionPointStr);
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +0000936 VlanId vlanIdInUse = VlanId.vlanId(interfaceIdOption.getVlanId());
937 Interface clientInterface = interfaceService.getInterfacesByPort(clientConnectionPoint)
Kalhee Kim495c9b22017-11-07 16:32:09 +0000938 .stream().filter(iface -> dhcp6HandlerUtil.interfaceContainsVlan(iface, vlanIdInUse))
939 .findFirst().orElse(null);
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +0000940 if (clientInterface == null) {
941 log.warn("Cannot get client interface for from packet, abort... vlan {}", vlanIdInUse.toString());
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000942 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_MATCHING_INTF);
Kalhee Kim45fede42017-09-05 19:05:06 +0000943 return null;
944 }
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +0000945 MacAddress relayAgentMac = clientInterface.mac();
Kalhee Kim45fede42017-09-05 19:05:06 +0000946 if (relayAgentMac == null) {
Yi Tseng68ef26b2017-12-18 17:10:00 -0800947 log.warn("Can not get client interface mac, abort packet..");
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000948 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_CLIENT_INTF_MAC);
Kalhee Kim45fede42017-09-05 19:05:06 +0000949 return null;
950 }
951 etherReply.setSourceMACAddress(relayAgentMac);
952
953 // find destMac
Yi Tseng68ef26b2017-12-18 17:10:00 -0800954 MacAddress clientMac;
Yi Tsengaa417a62017-09-08 17:22:51 -0700955 Ip6Address peerAddress = Ip6Address.valueOf(dhcp6Relay.getPeerAddress());
956 Set<Host> clients = hostService.getHostsByIp(peerAddress);
Kalhee Kim45fede42017-09-05 19:05:06 +0000957 if (clients.isEmpty()) {
Yi Tseng68ef26b2017-12-18 17:10:00 -0800958 log.trace("There's no host found for this address {}",
Kalhee Kim45fede42017-09-05 19:05:06 +0000959 HexString.toHexString(dhcp6Relay.getPeerAddress(), ":"));
Yi Tseng68ef26b2017-12-18 17:10:00 -0800960 log.trace("Let's look up interfaceId {}", HexString.toHexString(peerMac.toBytes(), ":"));
Kalhee Kim45fede42017-09-05 19:05:06 +0000961 clientMac = peerMac;
962 } else {
963 clientMac = clients.iterator().next().mac();
964 if (clientMac == null) {
965 log.warn("No client mac address found, abort packet...");
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000966 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_CLIENT_INTF_MAC);
Kalhee Kim45fede42017-09-05 19:05:06 +0000967 return null;
968 }
Yi Tseng68ef26b2017-12-18 17:10:00 -0800969 log.trace("Client mac address found from getHostByIp");
Kalhee Kim45fede42017-09-05 19:05:06 +0000970 }
971 etherReply.setDestinationMACAddress(clientMac);
Kalhee Kim45fede42017-09-05 19:05:06 +0000972 // ip header
973 ipv6Packet.setSourceAddress(dhcp6Relay.getLinkAddress());
974 ipv6Packet.setDestinationAddress(dhcp6Relay.getPeerAddress());
975 // udp header
976 udpPacket.setSourcePort(UDP.DHCP_V6_SERVER_PORT);
977 if (directConnFlag) {
978 udpPacket.setDestinationPort(UDP.DHCP_V6_CLIENT_PORT);
979 } else {
980 udpPacket.setDestinationPort(UDP.DHCP_V6_SERVER_PORT);
981 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000982 // add host or route
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000983 addHostOrRoute(directConnFlag, clientConnectionPoint, dhcp6Relay, embeddedDhcp6, clientMac, clientInterface);
Kalhee Kim45fede42017-09-05 19:05:06 +0000984
985 udpPacket.setPayload(embeddedDhcp6);
986 udpPacket.resetChecksum();
987 ipv6Packet.setPayload(udpPacket);
988 etherReply.setPayload(ipv6Packet);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000989 return new Dhcp6HandlerUtil().new InternalPacket(etherReply, clientConnectionPoint);
Kalhee Kim45fede42017-09-05 19:05:06 +0000990 }
991
Yi Tseng483ac6f2017-08-02 15:03:31 -0700992
993 @Override
Kalhee Kimba366062017-11-07 16:32:09 +0000994 public void setDhcpFpmEnabled(Boolean enabled) {
995 dhcpFpmEnabled = enabled;
996 }
997
998 @Override
Yi Tseng483ac6f2017-08-02 15:03:31 -0700999 public void setDefaultDhcpServerConfigs(Collection<DhcpServerConfig> configs) {
Kalhee Kim495c9b22017-11-07 16:32:09 +00001000 log.debug("setDefaultDhcpServerConfigs is called.");
Yi Tseng919b2df2017-09-07 16:22:51 -07001001 setDhcpServerConfigs(configs, defaultServerInfoList);
Yi Tseng919b2df2017-09-07 16:22:51 -07001002 }
1003
1004 @Override
1005 public void setIndirectDhcpServerConfigs(Collection<DhcpServerConfig> configs) {
Kalhee Kim495c9b22017-11-07 16:32:09 +00001006 log.debug("setIndirectDhcpServerConfigs is called.");
Yi Tseng919b2df2017-09-07 16:22:51 -07001007 setDhcpServerConfigs(configs, indirectServerInfoList);
Yi Tseng919b2df2017-09-07 16:22:51 -07001008 }
1009
1010 public void setDhcpServerConfigs(Collection<DhcpServerConfig> configs, List<DhcpServerInfo> serverInfoList) {
Kalhee Kim495c9b22017-11-07 16:32:09 +00001011 log.debug("config size {}.", configs.size());
1012
Kalhee Kim45fede42017-09-05 19:05:06 +00001013 if (configs.size() == 0) {
1014 // no config to update
1015 return;
1016 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001017 // TODO: currently we pick up first DHCP server config.
1018 // Will use other server configs in the future for HA.
Kalhee Kim495c9b22017-11-07 16:32:09 +00001019 Boolean isConfigValid = false;
1020 for (DhcpServerConfig serverConfig : configs) {
1021 if (serverConfig.getDhcpServerIp6().isPresent()) {
1022 isConfigValid = true;
1023 break;
1024 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001025 }
Kalhee Kim495c9b22017-11-07 16:32:09 +00001026 if (!isConfigValid) {
1027 log.warn("No IP V6 server address found.");
1028 return; // No IP V6 address found
1029 }
1030 for (DhcpServerInfo oldServerInfo : serverInfoList) {
Yi Tseng919b2df2017-09-07 16:22:51 -07001031 // stop monitoring gateway or server
1032 oldServerInfo.getDhcpGatewayIp6().ifPresent(gatewayIp -> {
1033 hostService.stopMonitoringIp(gatewayIp);
1034 });
1035 oldServerInfo.getDhcpServerIp6().ifPresent(serverIp -> {
1036 hostService.stopMonitoringIp(serverIp);
Yi Tseng525ff402017-10-23 19:39:39 -07001037 cancelDhcpPacket(serverIp);
Yi Tseng919b2df2017-09-07 16:22:51 -07001038 });
1039 }
Kalhee Kim495c9b22017-11-07 16:32:09 +00001040 serverInfoList.clear();
1041 for (DhcpServerConfig serverConfig : configs) {
1042 // Create new server info according to the config
1043 DhcpServerInfo newServerInfo = new DhcpServerInfo(serverConfig,
1044 DhcpServerInfo.Version.DHCP_V6);
1045 checkState(newServerInfo.getDhcpServerConnectPoint().isPresent(),
1046 "Connect point not exists");
1047 checkState(newServerInfo.getDhcpServerIp6().isPresent(),
1048 "IP of DHCP server not exists");
Yi Tseng919b2df2017-09-07 16:22:51 -07001049
Kalhee Kim495c9b22017-11-07 16:32:09 +00001050 log.debug("DHCP server connect point: {}", newServerInfo.getDhcpServerConnectPoint().orElse(null));
1051 log.debug("DHCP server IP: {}", newServerInfo.getDhcpServerIp6().orElse(null));
Yi Tseng919b2df2017-09-07 16:22:51 -07001052
Kalhee Kim495c9b22017-11-07 16:32:09 +00001053 Ip6Address serverIp = newServerInfo.getDhcpServerIp6().get();
1054 Ip6Address ipToProbe;
1055 if (newServerInfo.getDhcpGatewayIp6().isPresent()) {
1056 ipToProbe = newServerInfo.getDhcpGatewayIp6().get();
1057 } else {
1058 ipToProbe = newServerInfo.getDhcpServerIp6().orElse(null);
1059 }
1060 String hostToProbe = newServerInfo.getDhcpGatewayIp6()
1061 .map(ip -> "gateway").orElse("server");
Yi Tseng919b2df2017-09-07 16:22:51 -07001062
Kalhee Kim495c9b22017-11-07 16:32:09 +00001063 log.warn("Probing to resolve {} IP {}", hostToProbe, ipToProbe);
1064 hostService.startMonitoringIp(ipToProbe);
1065
1066 Set<Host> hosts = hostService.getHostsByIp(ipToProbe);
1067 if (!hosts.isEmpty()) {
1068 Host host = hosts.iterator().next();
1069 newServerInfo.setDhcpConnectVlan(host.vlan());
1070 newServerInfo.setDhcpConnectMac(host.mac());
1071 log.warn("Host found host {}", host);
1072
1073 } else {
1074 log.warn("No host found host ip {}", ipToProbe);
1075 }
1076 // Add new server info
1077 synchronized (this) {
1078 serverInfoList.add(newServerInfo);
1079 }
1080 if (!hosts.isEmpty()) {
1081 requestDhcpPacket(serverIp);
1082 }
Yi Tseng919b2df2017-09-07 16:22:51 -07001083 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001084 }
1085
1086 class InternalHostListener implements HostListener {
1087 @Override
1088 public void event(HostEvent event) {
1089 switch (event.type()) {
1090 case HOST_ADDED:
1091 case HOST_UPDATED:
1092 hostUpdated(event.subject());
1093 break;
1094 case HOST_REMOVED:
1095 hostRemoved(event.subject());
1096 break;
Kalhee Kim45fede42017-09-05 19:05:06 +00001097 default:
1098 break;
1099 }
1100 }
1101 }
1102
1103 /**
Kalhee Kim45fede42017-09-05 19:05:06 +00001104 * Handle host updated.
1105 * If the host is DHCP server or gateway, update connect mac and vlan.
1106 *
1107 * @param host the host
1108 */
1109 private void hostUpdated(Host host) {
Yi Tseng525ff402017-10-23 19:39:39 -07001110 hostUpdated(host, defaultServerInfoList);
1111 hostUpdated(host, indirectServerInfoList);
Kalhee Kim45fede42017-09-05 19:05:06 +00001112 }
1113
Yi Tseng525ff402017-10-23 19:39:39 -07001114 private void hostUpdated(Host host, List<DhcpServerInfo> serverInfoList) {
1115 DhcpServerInfo serverInfo;
1116 Ip6Address targetIp;
1117 if (!serverInfoList.isEmpty()) {
1118 serverInfo = serverInfoList.get(0);
1119 Ip6Address serverIp = serverInfo.getDhcpServerIp6().orElse(null);
1120 targetIp = serverInfo.getDhcpGatewayIp6().orElse(null);
1121
1122 if (targetIp == null) {
1123 targetIp = serverIp;
1124 }
Yi Tseng525ff402017-10-23 19:39:39 -07001125 if (targetIp != null) {
1126 if (host.ipAddresses().contains(targetIp)) {
1127 serverInfo.setDhcpConnectMac(host.mac());
1128 serverInfo.setDhcpConnectVlan(host.vlan());
1129 requestDhcpPacket(serverIp);
1130 }
1131 }
1132 }
1133 }
1134
Kalhee Kim45fede42017-09-05 19:05:06 +00001135 /**
1136 * Handle host removed.
1137 * If the host is DHCP server or gateway, unset connect mac and vlan.
1138 *
1139 * @param host the host
1140 */
1141 private void hostRemoved(Host host) {
Yi Tseng525ff402017-10-23 19:39:39 -07001142 hostRemoved(host, defaultServerInfoList);
1143 hostRemoved(host, indirectServerInfoList);
Yi Tseng919b2df2017-09-07 16:22:51 -07001144 }
1145
Yi Tseng525ff402017-10-23 19:39:39 -07001146 private void hostRemoved(Host host, List<DhcpServerInfo> serverInfoList) {
1147 DhcpServerInfo serverInfo;
1148 Ip6Address targetIp;
1149
1150 if (!serverInfoList.isEmpty()) {
1151 serverInfo = serverInfoList.get(0);
1152 Ip6Address serverIp = serverInfo.getDhcpServerIp6().orElse(null);
1153 targetIp = serverInfo.getDhcpGatewayIp6().orElse(null);
1154
1155 if (targetIp == null) {
1156 targetIp = serverIp;
1157 }
Yi Tseng525ff402017-10-23 19:39:39 -07001158 if (targetIp != null) {
1159 if (host.ipAddresses().contains(targetIp)) {
1160 serverInfo.setDhcpConnectVlan(null);
1161 serverInfo.setDhcpConnectMac(null);
1162 cancelDhcpPacket(serverIp);
1163 }
1164 }
1165 }
1166 }
1167
Kalhee Kim45b24182017-10-18 18:30:23 +00001168 /**
1169 * Returns the first interface ip from interface.
1170 *
1171 * @param iface interface of one connect point
1172 * @return the first interface IP; null if not exists an IP address in
1173 * these interfaces
1174 */
1175 private Ip6Address getFirstIpFromInterface(Interface iface) {
1176 checkNotNull(iface, "Interface can't be null");
1177 return iface.ipAddressesList().stream()
1178 .map(InterfaceIpAddress::ipAddress)
1179 .filter(IpAddress::isIp6)
1180 .map(IpAddress::getIp6Address)
1181 .findFirst()
1182 .orElse(null);
1183 }
Kalhee Kim45b24182017-10-18 18:30:23 +00001184 /**
Kalhee Kim495c9b22017-11-07 16:32:09 +00001185 * Checks if serverInfo's host info (mac and vlan) is filled in; if not, fills in.
1186 *
1187 * @param serverInfo server information
1188 * @return newServerInfo if host info can be either found or filled in.
1189 */
1190 private DhcpServerInfo getHostInfoForServerInfo(DhcpServerInfo serverInfo, List<DhcpServerInfo> sererInfoList) {
1191 DhcpServerInfo newServerInfo = null;
1192 MacAddress dhcpServerConnectMac = serverInfo.getDhcpConnectMac().orElse(null);
1193 VlanId dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1194 ConnectPoint dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1195
1196 if (dhcpServerConnectMac != null && dhcpConnectVlan != null) {
1197 newServerInfo = serverInfo;
1198 log.info("DHCP server {} host info found. ConnectPt{} Mac {} vlan {}", serverInfo.getDhcpServerIp6(),
1199 dhcpServerConnectPoint, dhcpServerConnectMac, dhcpConnectVlan);
1200 } else {
1201 log.warn("DHCP server {} not resolve yet connectPt {} mac {} vlan {}", serverInfo.getDhcpServerIp6(),
1202 dhcpServerConnectPoint, dhcpServerConnectMac, dhcpConnectVlan);
1203
1204 Ip6Address ipToProbe;
1205 if (serverInfo.getDhcpGatewayIp6().isPresent()) {
1206 ipToProbe = serverInfo.getDhcpGatewayIp6().get();
1207 } else {
1208 ipToProbe = serverInfo.getDhcpServerIp6().orElse(null);
1209 }
1210 String hostToProbe = serverInfo.getDhcpGatewayIp6()
1211 .map(ip -> "gateway").orElse("server");
1212
1213 log.info("Dynamically probing to resolve {} IP {}", hostToProbe, ipToProbe);
1214 hostService.startMonitoringIp(ipToProbe);
1215
1216 Set<Host> hosts = hostService.getHostsByIp(ipToProbe);
1217 if (!hosts.isEmpty()) {
1218 int serverInfoIndex = sererInfoList.indexOf(serverInfo);
1219 Host host = hosts.iterator().next();
1220 serverInfo.setDhcpConnectVlan(host.vlan());
1221 serverInfo.setDhcpConnectMac(host.mac());
1222 // replace the serverInfo in the list
1223 sererInfoList.set(serverInfoIndex, serverInfo);
1224 newServerInfo = serverInfo;
1225 log.warn("Dynamically host found host {}", host);
1226 } else {
1227 log.warn("No host found host ip {} dynamically", ipToProbe);
1228 }
1229 }
1230 return newServerInfo;
1231 }
1232
1233 /**
Kalhee Kim45b24182017-10-18 18:30:23 +00001234 * Gets Interface facing to the server for default host.
1235 *
Kalhee Kim495c9b22017-11-07 16:32:09 +00001236 * @param serverInfo server information
Kalhee Kim45b24182017-10-18 18:30:23 +00001237 * @return the Interface facing to the server; null if not found
1238 */
Kalhee Kim495c9b22017-11-07 16:32:09 +00001239 private Interface getServerInterface(DhcpServerInfo serverInfo) {
1240 Interface serverInterface = null;
Yi Tseng25bfe372017-11-03 16:27:32 -07001241
Kalhee Kim495c9b22017-11-07 16:32:09 +00001242 ConnectPoint dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1243 VlanId dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1244
1245 if (dhcpServerConnectPoint != null && dhcpConnectVlan != null) {
1246 serverInterface = interfaceService.getInterfacesByPort(dhcpServerConnectPoint)
1247 .stream()
1248 .filter(iface -> dhcp6HandlerUtil.interfaceContainsVlan(iface, dhcpConnectVlan))
1249 .findFirst()
1250 .orElse(null);
Yi Tseng25bfe372017-11-03 16:27:32 -07001251 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +00001252 log.warn("DHCP server {} not resolve yet connectPoint {} vlan {}", serverInfo.getDhcpServerIp6(),
1253 dhcpServerConnectPoint, dhcpConnectVlan);
Kalhee Kim45b24182017-10-18 18:30:23 +00001254 }
Kalhee Kim495c9b22017-11-07 16:32:09 +00001255
1256 return serverInterface;
Kalhee Kim45b24182017-10-18 18:30:23 +00001257 }
1258
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +00001259
Yi Tseng525ff402017-10-23 19:39:39 -07001260 private void requestDhcpPacket(Ip6Address serverIp) {
1261 requestServerDhcpPacket(serverIp);
1262 requestClientDhcpPacket(serverIp);
1263 }
1264
1265 private void cancelDhcpPacket(Ip6Address serverIp) {
1266 cancelServerDhcpPacket(serverIp);
1267 cancelClientDhcpPacket(serverIp);
1268 }
1269
1270 private void cancelServerDhcpPacket(Ip6Address serverIp) {
1271 TrafficSelector serverSelector =
1272 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1273 .matchIPv6Src(serverIp.toIpPrefix())
1274 .build();
1275 packetService.cancelPackets(serverSelector,
1276 PacketPriority.CONTROL,
1277 appId);
1278 }
1279
1280 private void requestServerDhcpPacket(Ip6Address serverIp) {
1281 TrafficSelector serverSelector =
1282 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1283 .matchIPv6Src(serverIp.toIpPrefix())
1284 .build();
1285 packetService.requestPackets(serverSelector,
1286 PacketPriority.CONTROL,
1287 appId);
1288 }
1289
1290 private void cancelClientDhcpPacket(Ip6Address serverIp) {
1291 // Packet comes from relay
1292 TrafficSelector indirectClientSelector =
1293 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1294 .matchIPv6Dst(serverIp.toIpPrefix())
1295 .build();
1296 packetService.cancelPackets(indirectClientSelector,
1297 PacketPriority.CONTROL,
1298 appId);
Yi Tseng41dde702017-11-02 15:21:10 -07001299 indirectClientSelector =
1300 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1301 .matchIPv6Dst(Ip6Address.ALL_DHCP_RELAY_AGENTS_AND_SERVERS.toIpPrefix())
1302 .build();
1303 packetService.cancelPackets(indirectClientSelector,
1304 PacketPriority.CONTROL,
1305 appId);
1306 indirectClientSelector =
1307 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1308 .matchIPv6Dst(Ip6Address.ALL_DHCP_SERVERS.toIpPrefix())
1309 .build();
1310 packetService.cancelPackets(indirectClientSelector,
1311 PacketPriority.CONTROL,
1312 appId);
Yi Tseng525ff402017-10-23 19:39:39 -07001313
1314 // Packet comes from client
1315 packetService.cancelPackets(CLIENT_SERVER_SELECTOR,
1316 PacketPriority.CONTROL,
1317 appId);
1318 }
1319
1320 private void requestClientDhcpPacket(Ip6Address serverIp) {
1321 // Packet comes from relay
1322 TrafficSelector indirectClientSelector =
1323 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1324 .matchIPv6Dst(serverIp.toIpPrefix())
1325 .build();
1326 packetService.requestPackets(indirectClientSelector,
1327 PacketPriority.CONTROL,
1328 appId);
Yi Tseng41dde702017-11-02 15:21:10 -07001329 indirectClientSelector =
1330 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1331 .matchIPv6Dst(Ip6Address.ALL_DHCP_RELAY_AGENTS_AND_SERVERS.toIpPrefix())
1332 .build();
1333 packetService.requestPackets(indirectClientSelector,
1334 PacketPriority.CONTROL,
1335 appId);
1336 indirectClientSelector =
1337 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1338 .matchIPv6Dst(Ip6Address.ALL_DHCP_SERVERS.toIpPrefix())
1339 .build();
1340 packetService.requestPackets(indirectClientSelector,
1341 PacketPriority.CONTROL,
1342 appId);
Yi Tseng525ff402017-10-23 19:39:39 -07001343
1344 // Packet comes from client
1345 packetService.requestPackets(CLIENT_SERVER_SELECTOR,
1346 PacketPriority.CONTROL,
1347 appId);
1348 }
1349
1350 /**
1351 * Process the ignore rules.
1352 *
1353 * @param deviceId the device id
1354 * @param vlanId the vlan to be ignored
1355 * @param op the operation, ADD to install; REMOVE to uninstall rules
1356 */
1357 private void processIgnoreVlanRule(DeviceId deviceId, VlanId vlanId, Objective.Operation op) {
Yi Tseng525ff402017-10-23 19:39:39 -07001358 AtomicInteger installedCount = new AtomicInteger(DHCP_SELECTORS.size());
1359 DHCP_SELECTORS.forEach(trafficSelector -> {
1360 TrafficSelector selector = DefaultTrafficSelector.builder(trafficSelector)
1361 .matchVlanId(vlanId)
1362 .build();
1363
1364 ForwardingObjective.Builder builder = DefaultForwardingObjective.builder()
1365 .withFlag(ForwardingObjective.Flag.VERSATILE)
1366 .withSelector(selector)
1367 .withPriority(IGNORE_CONTROL_PRIORITY)
Yi Tsengdbabeed2017-11-29 10:49:20 -08001368 .withTreatment(DefaultTrafficTreatment.emptyTreatment())
Yi Tseng525ff402017-10-23 19:39:39 -07001369 .fromApp(appId);
1370
1371
1372 ObjectiveContext objectiveContext = new ObjectiveContext() {
1373 @Override
1374 public void onSuccess(Objective objective) {
1375 log.info("Ignore rule {} (Vlan id {}, device {}, selector {})",
1376 op, vlanId, deviceId, selector);
1377 int countDown = installedCount.decrementAndGet();
1378 if (countDown != 0) {
1379 return;
1380 }
1381 switch (op) {
1382 case ADD:
1383 ignoredVlans.put(deviceId, vlanId);
1384 break;
1385 case REMOVE:
1386 ignoredVlans.remove(deviceId, vlanId);
1387 break;
1388 default:
1389 log.warn("Unsupported objective operation {}", op);
1390 break;
1391 }
1392 }
1393
1394 @Override
1395 public void onError(Objective objective, ObjectiveError error) {
1396 log.warn("Can't {} ignore rule (vlan id {}, selector {}, device {}) due to {}",
1397 op, vlanId, selector, deviceId, error);
1398 }
1399 };
1400
1401 ForwardingObjective fwd;
1402 switch (op) {
1403 case ADD:
1404 fwd = builder.add(objectiveContext);
1405 break;
1406 case REMOVE:
1407 fwd = builder.remove(objectiveContext);
1408 break;
1409 default:
1410 log.warn("Unsupported objective operation {}", op);
1411 return;
1412 }
1413
1414 Device device = deviceService.getDevice(deviceId);
1415 if (device == null || !device.is(Pipeliner.class)) {
1416 log.warn("Device {} is not available now, wait until device is available", deviceId);
1417 return;
1418 }
1419 flowObjectiveService.apply(deviceId, fwd);
1420 });
1421 }
Kalhee Kim121ba922017-11-01 17:56:44 +00001422 /**
1423 * Find first ipaddress for a given Host info i.e. mac and vlan.
1424 *
1425 * @param clientMac client mac
1426 * @param vlanId packet's vlan
1427 * @return next-hop link-local ipaddress for a given host
1428 */
Kalhee Kim495c9b22017-11-07 16:32:09 +00001429 private IpAddress getFirstIpByHost(Boolean directConnFlag, MacAddress clientMac, VlanId vlanId) {
Kalhee Kim121ba922017-11-01 17:56:44 +00001430 IpAddress nextHopIp;
1431 // pick out the first link-local ip address
1432 HostId gwHostId = HostId.hostId(clientMac, vlanId);
1433 Host gwHost = hostService.getHost(gwHostId);
1434 if (gwHost == null) {
1435 log.warn("Can't find gateway host for hostId {}", gwHostId);
1436 return null;
1437 }
Kalhee Kim495c9b22017-11-07 16:32:09 +00001438 if (directConnFlag) {
1439 nextHopIp = gwHost.ipAddresses()
1440 .stream()
1441 .filter(IpAddress::isIp6)
1442 .map(IpAddress::getIp6Address)
1443 .findFirst()
1444 .orElse(null);
1445 } else {
1446 nextHopIp = gwHost.ipAddresses()
1447 .stream()
1448 .filter(IpAddress::isIp6)
1449 .filter(ip6 -> ip6.isLinkLocal())
1450 .map(IpAddress::getIp6Address)
1451 .findFirst()
1452 .orElse(null);
1453 }
Kalhee Kim121ba922017-11-01 17:56:44 +00001454 return nextHopIp;
1455 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +00001456
Kalhee Kim495c9b22017-11-07 16:32:09 +00001457 private List<DhcpServerInfo> findValidServerInfo(boolean directConnFlag) {
1458 List<DhcpServerInfo> validServerInfo;
1459
1460 if (directConnFlag || indirectServerInfoList.isEmpty()) {
1461 validServerInfo = new ArrayList<DhcpServerInfo>(defaultServerInfoList);
1462 } else {
1463 validServerInfo = new ArrayList<DhcpServerInfo>(indirectServerInfoList);
1464 }
1465 return validServerInfo;
1466 }
1467
1468 private DhcpServerInfo findServerInfoFromServer(boolean directConnFlag, ConnectPoint inPort) {
1469 List<DhcpServerInfo> validServerInfoList = findValidServerInfo(directConnFlag);
1470 DhcpServerInfo foundServerInfo = null;
1471 for (DhcpServerInfo serverInfo : validServerInfoList) {
1472 if (inPort.equals(serverInfo.getDhcpServerConnectPoint().get())) {
1473 foundServerInfo = serverInfo;
1474 log.warn("ServerInfo found for Rcv port {} Server Connect Point {} for {}",
1475 inPort, serverInfo.getDhcpServerConnectPoint(), directConnFlag ? "direct" : "indirect");
1476 break;
1477 } else {
1478 log.warn("Rcv port {} not the same as Server Connect Point {} for {}",
1479 inPort, serverInfo.getDhcpServerConnectPoint(), directConnFlag ? "direct" : "indirect");
1480 }
1481 }
1482 return foundServerInfo;
1483 }
1484 /**
1485 * Set the dhcp6 lease expiry poll interval value.
1486 *
1487 * @param val poll interval value in seconds
1488 */
Kalhee Kim2f07c602017-11-15 18:57:53 +00001489 @Override
Kalhee Kim495c9b22017-11-07 16:32:09 +00001490 public void setDhcp6PollInterval(int val) {
1491 dhcp6PollInterval = val;
1492 }
1493
Kalhee Kim2f07c602017-11-15 18:57:53 +00001494 /**
1495 * get the dhcp6 lease expiry poll interval value.
1496 * This is a private function
1497 * @return poll interval value in seconds
1498 */
1499 private int getDhcp6PollInterval() {
1500 return dhcp6PollInterval;
1501 }
1502
1503 /**
1504 * Find lease-expired ipaddresses and pd prefixes.
1505 * Removing host/route/fpm entries.
1506 */
1507 public void timeTick() {
1508 long currentTime = System.currentTimeMillis();
1509 Collection<DhcpRecord> records = dhcpRelayStore.getDhcpRecords();
1510
1511 log.debug("timeTick called currenttime {} records num {} ", currentTime, records.size());
1512
1513 records.forEach(record -> {
1514 boolean addrOrPdRemoved = false;
1515 DHCP6.MsgType ip6Status = record.ip6Status().orElse(null);
1516 if (ip6Status == null) {
1517 log.debug("record is not valid v6 record.");
1518 return;
1519 }
1520
1521 if ((currentTime - record.getLastIp6Update()) >
1522 ((record.addrPrefTime() + getDhcp6PollInterval() / 2) * 1000)) {
1523 // remove ipaddress from host/route table
1524 IpAddress ip = record.ip6Address().orElse(null);
1525 if (ip != null) {
1526 if (record.directlyConnected()) {
1527 providerService.removeIpFromHost(HostId.hostId(record.macAddress(),
1528 record.vlanId()), ip);
1529 } else {
1530 MacAddress gwMac = record.nextHop().orElse(null);
1531 if (gwMac == null) {
1532 log.warn("Can't find gateway mac address from record {} for ip6Addr", record);
1533 return;
1534 }
1535 IpAddress nextHopIp = getFirstIpByHost(record.directlyConnected(),
1536 gwMac,
1537 record.vlanId());
1538 Route route = new Route(Route.Source.STATIC, ip.toIpPrefix(), nextHopIp);
1539 routeStore.removeRoute(route);
1540 }
1541 record.updateAddrPrefTime(0);
1542 record.ip6Address(null);
1543 addrOrPdRemoved = true;
1544 dhcpRelayStore.updateDhcpRecord(HostId.hostId(record.macAddress(),
1545 record.vlanId()), record);
1546 log.warn("IP6 address is set to null. delta {} lastUpdate {} addrPrefTime {}",
1547 (currentTime - record.getLastIp6Update()), record.getLastIp6Update(),
1548 record.addrPrefTime());
1549 }
1550 }
1551 if ((currentTime - record.getLastPdUpdate()) >
1552 ((record.pdPrefTime() + getDhcp6PollInterval() / 2) * 1000)) {
1553 // remove PD from route/fpm table
1554 IpPrefix pdIpPrefix = record.pdPrefix().orElse(null);
1555 if (pdIpPrefix != null) {
1556 if (record.directlyConnected()) {
1557 providerService.removeIpFromHost(HostId.hostId(record.macAddress(), record.vlanId()),
1558 pdIpPrefix.address().getIp6Address());
1559 } else {
1560 MacAddress gwMac = record.nextHop().orElse(null);
1561 if (gwMac == null) {
1562 log.warn("Can't find gateway mac address from record {} for PD prefix", record);
1563 return;
1564 }
1565 IpAddress nextHopIp = getFirstIpByHost(record.directlyConnected(),
1566 gwMac,
1567 record.vlanId());
1568 Route route = new Route(Route.Source.STATIC, pdIpPrefix, nextHopIp);
1569 routeStore.removeRoute(route);
1570 if (this.dhcpFpmEnabled) {
1571 dhcpFpmPrefixStore.removeFpmRecord(pdIpPrefix);
1572 }
1573 }
1574 record.updatePdPrefTime(0);
1575 record.pdPrefix(null);
1576 addrOrPdRemoved = true;
1577 dhcpRelayStore.updateDhcpRecord(HostId.hostId(record.macAddress(),
1578 record.vlanId()), record);
1579 log.warn("PD prefix is set to null.delta {} pdPrefTime {}",
1580 (currentTime - record.getLastPdUpdate()), record.pdPrefTime());
1581 }
1582 }
1583 if (addrOrPdRemoved &&
1584 !record.ip6Address().isPresent() && !record.pdPrefix().isPresent()) {
1585 log.warn("ip6Status {} IP6 address and IP6 PD both are null. Remove record.", ip6Status);
1586 dhcpRelayStore.removeDhcpRecord(HostId.hostId(record.macAddress(), record.vlanId()));
1587 }
1588 }
1589 );
1590 }
1591}