blob: 1472082644645455819415090d0648d84f49ab1f [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 */
17
18package org.onosproject.dhcprelay;
19
Yi Tseng525ff402017-10-23 19:39:39 -070020import com.google.common.collect.HashMultimap;
Yi Tseng919b2df2017-09-07 16:22:51 -070021import com.google.common.collect.Lists;
Yi Tseng525ff402017-10-23 19:39:39 -070022import com.google.common.collect.Multimap;
Kalhee Kim45fede42017-09-05 19:05:06 +000023import org.apache.felix.scr.annotations.Activate;
24import org.apache.felix.scr.annotations.Deactivate;
Kalhee Kim45fede42017-09-05 19:05:06 +000025import com.google.common.collect.Sets;
26import com.google.common.collect.ImmutableSet;
Yi Tseng51301292017-07-28 13:02:59 -070027import org.apache.felix.scr.annotations.Component;
28import org.apache.felix.scr.annotations.Property;
Kalhee Kim45fede42017-09-05 19:05:06 +000029import org.apache.felix.scr.annotations.Reference;
30import org.apache.felix.scr.annotations.ReferenceCardinality;
Yi Tseng51301292017-07-28 13:02:59 -070031import org.apache.felix.scr.annotations.Service;
32import org.onlab.packet.BasePacket;
Kalhee Kim45fede42017-09-05 19:05:06 +000033import org.onlab.packet.DHCP6;
34import org.onlab.packet.IPv6;
35import org.onlab.packet.Ethernet;
36import org.onlab.packet.Ip6Address;
Yi Tseng51301292017-07-28 13:02:59 -070037import org.onlab.packet.IpAddress;
Kalhee Kim45fede42017-09-05 19:05:06 +000038import org.onlab.packet.IpPrefix;
Yi Tseng51301292017-07-28 13:02:59 -070039import org.onlab.packet.MacAddress;
Yi Tseng525ff402017-10-23 19:39:39 -070040import org.onlab.packet.TpPort;
Kalhee Kim45fede42017-09-05 19:05:06 +000041import org.onlab.packet.UDP;
Yi Tseng51301292017-07-28 13:02:59 -070042import org.onlab.packet.VlanId;
Kalhee Kim45fede42017-09-05 19:05:06 +000043import org.onlab.packet.dhcp.Dhcp6RelayOption;
44import org.onlab.packet.dhcp.Dhcp6InterfaceIdOption;
45import org.onlab.packet.dhcp.Dhcp6Option;
46import org.onlab.packet.dhcp.Dhcp6IaNaOption;
47import org.onlab.packet.dhcp.Dhcp6IaTaOption;
48import org.onlab.packet.dhcp.Dhcp6IaPdOption;
49import org.onlab.packet.dhcp.Dhcp6IaAddressOption;
50import org.onlab.packet.dhcp.Dhcp6IaPrefixOption;
Kalhee Kimea4b6c22017-11-09 14:38:37 +000051import org.onlab.packet.dhcp.Dhcp6ClientIdOption;
52import org.onlab.packet.dhcp.Dhcp6Duid;
Kalhee Kim45fede42017-09-05 19:05:06 +000053import org.onlab.util.HexString;
Yi Tseng525ff402017-10-23 19:39:39 -070054import org.onosproject.core.ApplicationId;
55import org.onosproject.core.CoreService;
Yi Tseng51301292017-07-28 13:02:59 -070056import org.onosproject.dhcprelay.api.DhcpHandler;
Yi Tseng919b2df2017-09-07 16:22:51 -070057import org.onosproject.dhcprelay.api.DhcpServerInfo;
Yi Tseng525ff402017-10-23 19:39:39 -070058import org.onosproject.dhcprelay.config.IgnoreDhcpConfig;
Kalhee Kim45fede42017-09-05 19:05:06 +000059import org.onosproject.dhcprelay.store.DhcpRelayStore;
Kalhee Kimea4b6c22017-11-09 14:38:37 +000060import org.onosproject.dhcprelay.store.DhcpRecord;
Kalhee Kimba366062017-11-07 16:32:09 +000061import org.onosproject.dhcprelay.store.DhcpFpmPrefixStore;
Yi Tseng525ff402017-10-23 19:39:39 -070062import org.onosproject.net.Device;
63import org.onosproject.net.DeviceId;
Kalhee Kimba366062017-11-07 16:32:09 +000064import org.onosproject.routing.fpm.api.FpmRecord;
Yi Tseng525ff402017-10-23 19:39:39 -070065import org.onosproject.net.behaviour.Pipeliner;
66import org.onosproject.net.device.DeviceService;
67import org.onosproject.net.flow.DefaultTrafficSelector;
68import org.onosproject.net.flow.TrafficSelector;
69import org.onosproject.net.flowobjective.DefaultForwardingObjective;
70import org.onosproject.net.flowobjective.FlowObjectiveService;
71import org.onosproject.net.flowobjective.ForwardingObjective;
72import org.onosproject.net.flowobjective.Objective;
73import org.onosproject.net.flowobjective.ObjectiveContext;
74import org.onosproject.net.flowobjective.ObjectiveError;
Yi Tsengaa417a62017-09-08 17:22:51 -070075import org.onosproject.net.host.HostProvider;
76import org.onosproject.net.host.HostProviderRegistry;
77import org.onosproject.net.host.HostProviderService;
Kalhee Kim45fede42017-09-05 19:05:06 +000078import org.onosproject.net.host.HostService;
79import org.onosproject.net.host.DefaultHostDescription;
80import org.onosproject.net.host.HostDescription;
81import org.onosproject.net.host.InterfaceIpAddress;
82import org.onosproject.net.host.HostListener;
83import org.onosproject.net.host.HostEvent;
84import org.onosproject.net.intf.Interface;
85import org.onosproject.net.intf.InterfaceService;
Yi Tseng525ff402017-10-23 19:39:39 -070086import org.onosproject.net.packet.PacketPriority;
Yi Tsengaa417a62017-09-08 17:22:51 -070087import org.onosproject.net.provider.ProviderId;
Kalhee Kim45fede42017-09-05 19:05:06 +000088import org.onosproject.routeservice.Route;
89import org.onosproject.routeservice.RouteStore;
Yi Tseng483ac6f2017-08-02 15:03:31 -070090import org.onosproject.dhcprelay.config.DhcpServerConfig;
Yi Tseng51301292017-07-28 13:02:59 -070091import org.onosproject.net.ConnectPoint;
Kalhee Kim45fede42017-09-05 19:05:06 +000092import org.onosproject.net.Host;
93import org.onosproject.net.HostId;
94import org.onosproject.net.HostLocation;
95import org.onosproject.net.packet.DefaultOutboundPacket;
96import org.onosproject.net.packet.OutboundPacket;
Yi Tseng51301292017-07-28 13:02:59 -070097import org.onosproject.net.packet.PacketContext;
Kalhee Kim45fede42017-09-05 19:05:06 +000098import org.onosproject.net.packet.PacketService;
99import org.slf4j.Logger;
100import org.slf4j.LoggerFactory;
101import org.onosproject.net.flow.DefaultTrafficTreatment;
102import org.onosproject.net.flow.TrafficTreatment;
Yi Tseng51301292017-07-28 13:02:59 -0700103
Kalhee Kim45fede42017-09-05 19:05:06 +0000104
105import java.nio.ByteBuffer;
106import java.util.List;
Yi Tseng483ac6f2017-08-02 15:03:31 -0700107import java.util.Collection;
Yi Tseng51301292017-07-28 13:02:59 -0700108import java.util.Optional;
Kalhee Kim45fede42017-09-05 19:05:06 +0000109import java.util.Set;
110import java.util.ArrayList;
Yi Tseng525ff402017-10-23 19:39:39 -0700111import java.util.concurrent.atomic.AtomicInteger;
Kalhee Kim45fede42017-09-05 19:05:06 +0000112
113
114import 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;
Yi Tseng51301292017-07-28 13:02:59 -0700118
119@Component
120@Service
121@Property(name = "version", value = "6")
Yi Tsengaa417a62017-09-08 17:22:51 -0700122public class Dhcp6HandlerImpl implements DhcpHandler, HostProvider {
Charles Chand988c282017-09-12 17:09:32 -0700123 public static final String DHCP_V6_RELAY_APP = "org.onosproject.Dhcp6HandlerImpl";
Saurav Das7c6dec12017-09-13 14:35:56 -0700124 public static final ProviderId PROVIDER_ID = new ProviderId("dhcp6", DHCP_V6_RELAY_APP);
Yi Tseng525ff402017-10-23 19:39:39 -0700125 private static final int IGNORE_CONTROL_PRIORITY = PacketPriority.CONTROL.priorityValue() + 1000;
126
127 private static final TrafficSelector CLIENT_SERVER_SELECTOR = DefaultTrafficSelector.builder()
128 .matchEthType(Ethernet.TYPE_IPV6)
129 .matchIPProtocol(IPv6.PROTOCOL_UDP)
130 .matchIPv6Src(IpPrefix.IPV6_LINK_LOCAL_PREFIX)
131 .matchIPv6Dst(Ip6Address.ALL_DHCP_RELAY_AGENTS_AND_SERVERS.toIpPrefix())
132 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_V6_CLIENT_PORT))
133 .matchUdpDst(TpPort.tpPort(UDP.DHCP_V6_SERVER_PORT))
134 .build();
135 private static final TrafficSelector SERVER_RELAY_SELECTOR = DefaultTrafficSelector.builder()
136 .matchEthType(Ethernet.TYPE_IPV6)
137 .matchIPProtocol(IPv6.PROTOCOL_UDP)
138 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_V6_SERVER_PORT))
139 .matchUdpDst(TpPort.tpPort(UDP.DHCP_V6_SERVER_PORT))
140 .build();
141 static final Set<TrafficSelector> DHCP_SELECTORS = ImmutableSet.of(
142 CLIENT_SERVER_SELECTOR,
143 SERVER_RELAY_SELECTOR
144 );
Kalhee Kim45fede42017-09-05 19:05:06 +0000145 private static Logger log = LoggerFactory.getLogger(Dhcp6HandlerImpl.class);
Yi Tseng51301292017-07-28 13:02:59 -0700146
Kalhee Kim45fede42017-09-05 19:05:06 +0000147 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
148 protected DhcpRelayStore dhcpRelayStore;
Yi Tseng51301292017-07-28 13:02:59 -0700149
Kalhee Kim45fede42017-09-05 19:05:06 +0000150 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
151 protected PacketService packetService;
152
153 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Kalhee Kim45fede42017-09-05 19:05:06 +0000154 protected RouteStore routeStore;
155
156 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
157 protected InterfaceService interfaceService;
158
159 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
160 protected HostService hostService;
161
Yi Tsengaa417a62017-09-08 17:22:51 -0700162 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
163 protected HostProviderRegistry providerRegistry;
Kalhee Kim45fede42017-09-05 19:05:06 +0000164
Yi Tseng525ff402017-10-23 19:39:39 -0700165 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
166 protected CoreService coreService;
167
168 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Kalhee Kimba366062017-11-07 16:32:09 +0000169 protected DhcpFpmPrefixStore dhcpFpmPrefixStore;
170
171 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Yi Tseng525ff402017-10-23 19:39:39 -0700172 protected DeviceService deviceService;
173
174 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
175 protected FlowObjectiveService flowObjectiveService;
176
Yi Tsengaa417a62017-09-08 17:22:51 -0700177 protected HostProviderService providerService;
Yi Tseng525ff402017-10-23 19:39:39 -0700178 protected ApplicationId appId;
179 protected Multimap<DeviceId, VlanId> ignoredVlans = HashMultimap.create();
180 private InternalHostListener hostListener = new InternalHostListener();
181
Kalhee Kimba366062017-11-07 16:32:09 +0000182 private Boolean dhcpFpmEnabled = false;
183
Yi Tseng919b2df2017-09-07 16:22:51 -0700184 private List<DhcpServerInfo> defaultServerInfoList = Lists.newArrayList();
185 private List<DhcpServerInfo> indirectServerInfoList = Lists.newArrayList();
186
Kalhee Kim45fede42017-09-05 19:05:06 +0000187
188 // CLIENT message types
189 public static final Set<Byte> MSG_TYPE_FROM_CLIENT =
190 ImmutableSet.of(DHCP6.MsgType.SOLICIT.value(),
191 DHCP6.MsgType.REQUEST.value(),
192 DHCP6.MsgType.REBIND.value(),
193 DHCP6.MsgType.RENEW.value(),
194 DHCP6.MsgType.RELEASE.value(),
195 DHCP6.MsgType.DECLINE.value(),
196 DHCP6.MsgType.CONFIRM.value(),
197 DHCP6.MsgType.RELAY_FORW.value());
198 // SERVER message types
199 public static final Set<Byte> MSG_TYPE_FROM_SERVER =
200 ImmutableSet.of(DHCP6.MsgType.RELAY_REPL.value());
201
202 @Activate
203 protected void activate() {
Yi Tseng525ff402017-10-23 19:39:39 -0700204 appId = coreService.registerApplication(DHCP_V6_RELAY_APP);
Yi Tsengaa417a62017-09-08 17:22:51 -0700205 providerService = providerRegistry.register(this);
Kalhee Kim45fede42017-09-05 19:05:06 +0000206 hostService.addListener(hostListener);
Yi Tseng51301292017-07-28 13:02:59 -0700207 }
208
Kalhee Kim45fede42017-09-05 19:05:06 +0000209 @Deactivate
210 protected void deactivate() {
Yi Tsengaa417a62017-09-08 17:22:51 -0700211 providerRegistry.unregister(this);
Kalhee Kim45fede42017-09-05 19:05:06 +0000212 hostService.removeListener(hostListener);
Yi Tseng919b2df2017-09-07 16:22:51 -0700213 defaultServerInfoList.forEach(this::stopMonitoringIps);
214 defaultServerInfoList.clear();
215 indirectServerInfoList.forEach(this::stopMonitoringIps);
216 indirectServerInfoList.clear();
217 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000218
Yi Tseng919b2df2017-09-07 16:22:51 -0700219 private void stopMonitoringIps(DhcpServerInfo serverInfo) {
220 serverInfo.getDhcpGatewayIp6().ifPresent(gatewayIp -> {
221 hostService.stopMonitoringIp(gatewayIp);
222 });
223 serverInfo.getDhcpServerIp6().ifPresent(serverIp -> {
224 hostService.stopMonitoringIp(serverIp);
225 });
Yi Tseng51301292017-07-28 13:02:59 -0700226 }
227
Yi Tseng51301292017-07-28 13:02:59 -0700228 @Override
Yi Tseng919b2df2017-09-07 16:22:51 -0700229 public List<DhcpServerInfo> getDefaultDhcpServerInfoList() {
230 return defaultServerInfoList;
Yi Tseng51301292017-07-28 13:02:59 -0700231 }
232
233 @Override
Yi Tseng919b2df2017-09-07 16:22:51 -0700234 public List<DhcpServerInfo> getIndirectDhcpServerInfoList() {
235 return indirectServerInfoList;
Yi Tseng51301292017-07-28 13:02:59 -0700236 }
237
238 @Override
Yi Tseng525ff402017-10-23 19:39:39 -0700239 public void updateIgnoreVlanConfig(IgnoreDhcpConfig config) {
240 if (config == null) {
241 ignoredVlans.forEach(((deviceId, vlanId) -> {
242 processIgnoreVlanRule(deviceId, vlanId, REMOVE);
243 }));
244 return;
245 }
246 config.ignoredVlans().forEach((deviceId, vlanId) -> {
247 if (ignoredVlans.get(deviceId).contains(vlanId)) {
248 // don't need to process if it already ignored
249 return;
250 }
251 processIgnoreVlanRule(deviceId, vlanId, ADD);
252 });
253
254 ignoredVlans.forEach((deviceId, vlanId) -> {
255 if (!config.ignoredVlans().get(deviceId).contains(vlanId)) {
256 // not contains in new config, remove it
257 processIgnoreVlanRule(deviceId, vlanId, REMOVE);
258 }
259 });
260 }
261
262 @Override
Saurav Dasb805f1a2017-12-13 16:19:35 -0800263 public void removeIgnoreVlanState(IgnoreDhcpConfig config) {
264 if (config == null) {
265 ignoredVlans.clear();
266 return;
267 }
268 config.ignoredVlans().forEach((deviceId, vlanId) -> {
269 ignoredVlans.remove(deviceId, vlanId);
270 });
271 }
272
273 @Override
Kalhee Kim45fede42017-09-05 19:05:06 +0000274 public void processDhcpPacket(PacketContext context, BasePacket payload) {
275 checkNotNull(payload, "DHCP6 payload can't be null");
276 checkState(payload instanceof DHCP6, "Payload is not a DHCP6");
277 DHCP6 dhcp6Payload = (DHCP6) payload;
278 Ethernet receivedPacket = context.inPacket().parsed();
279
280 if (!configured()) {
281 log.warn("Missing DHCP6 relay server config. Abort packet processing");
Yi Tseng68ef26b2017-12-18 17:10:00 -0800282 log.trace("dhcp6 payload {}", dhcp6Payload);
Kalhee Kim45fede42017-09-05 19:05:06 +0000283
284 return;
285 }
286
287 byte msgType = dhcp6Payload.getMsgType();
Kalhee Kim45fede42017-09-05 19:05:06 +0000288
289 ConnectPoint inPort = context.inPacket().receivedFrom();
Jonathan Hart8ca2bc02017-11-30 18:23:42 -0800290
Kalhee Kim45fede42017-09-05 19:05:06 +0000291 Set<Interface> receivingInterfaces = interfaceService.getInterfacesByPort(inPort);
292 //ignore the packets if dhcp client interface is not configured on onos.
293 if (receivingInterfaces.isEmpty()) {
294 log.warn("Virtual interface is not configured on {}", inPort);
295 return;
296 }
297
298
299 if (MSG_TYPE_FROM_CLIENT.contains(msgType)) {
Kalhee Kim45fede42017-09-05 19:05:06 +0000300 InternalPacket ethernetClientPacket =
301 processDhcp6PacketFromClient(context, receivedPacket, receivingInterfaces);
302 if (ethernetClientPacket != null) {
303 forwardPacket(ethernetClientPacket);
304 }
305
306 } else if (MSG_TYPE_FROM_SERVER.contains(msgType)) {
Yi Tseng68ef26b2017-12-18 17:10:00 -0800307 log.debug("calling processDhcp6PacketFromServer with RELAY_REPL", msgType);
Kalhee Kim45fede42017-09-05 19:05:06 +0000308 InternalPacket ethernetPacketReply =
309 processDhcp6PacketFromServer(context, receivedPacket, receivingInterfaces);
310 if (ethernetPacketReply != null) {
311 forwardPacket(ethernetPacketReply);
312 }
313 } else {
Yi Tseng68ef26b2017-12-18 17:10:00 -0800314 log.warn("DHCP type {} not supported yet", msgType);
Kalhee Kim45fede42017-09-05 19:05:06 +0000315 }
316 }
317
318
319 /**
320 * Checks if this app has been configured.
321 *
322 * @return true if all information we need have been initialized
323 */
324 public boolean configured() {
Yi Tseng919b2df2017-09-07 16:22:51 -0700325 return !defaultServerInfoList.isEmpty();
Kalhee Kim45fede42017-09-05 19:05:06 +0000326 }
327
Yi Tsengaa417a62017-09-08 17:22:51 -0700328 @Override
329 public ProviderId id() {
Charles Chand988c282017-09-12 17:09:32 -0700330 return PROVIDER_ID;
Yi Tsengaa417a62017-09-08 17:22:51 -0700331 }
332
333 @Override
334 public void triggerProbe(Host host) {
335 // Do nothing here
336 }
337
Kalhee Kim45fede42017-09-05 19:05:06 +0000338 // the new class the contains Ethernet packet and destination port, kind of like adding
339 // internal header to the packet
340 private class InternalPacket {
341 Ethernet packet;
342 ConnectPoint destLocation;
343 public InternalPacket(Ethernet newPacket, ConnectPoint newLocation) {
344 packet = newPacket;
345 destLocation = newLocation;
346 }
347 void setLocation(ConnectPoint newLocation) {
348 destLocation = newLocation;
349 }
350 }
351
352 //forward the packet to ConnectPoint where the DHCP server is attached.
353 private void forwardPacket(InternalPacket packet) {
354 //send Packetout to dhcp server connectpoint.
355 if (packet.destLocation != null) {
356 TrafficTreatment t = DefaultTrafficTreatment.builder()
357 .setOutput(packet.destLocation.port()).build();
358 OutboundPacket o = new DefaultOutboundPacket(
359 packet.destLocation.deviceId(), t, ByteBuffer.wrap(packet.packet.serialize()));
360 if (log.isTraceEnabled()) {
361 log.trace("Relaying packet to destination {}", packet.destLocation);
362 }
363 packetService.emit(o);
364 } // if
365 }
366
367 /**
368 * Check if the host is directly connected to the network or not.
369 *
370 * @param dhcp6Payload the dhcp6 payload
371 * @return true if the host is directly connected to the network; false otherwise
372 */
373 private boolean directlyConnected(DHCP6 dhcp6Payload) {
374 log.debug("directlyConnected enters");
375
376 if (dhcp6Payload.getMsgType() != DHCP6.MsgType.RELAY_FORW.value() &&
377 dhcp6Payload.getMsgType() != DHCP6.MsgType.RELAY_REPL.value()) {
378 log.debug("directlyConnected true. MsgType {}", dhcp6Payload.getMsgType());
379
380 return true;
381 }
382
383 // Regardless of relay-forward or relay-replay, check if we see another relay message
384 DHCP6 dhcp6Payload2 = dhcp6PacketFromRelayPacket(dhcp6Payload);
385 if (dhcp6Payload2 != null) {
386 if (dhcp6Payload.getMsgType() == DHCP6.MsgType.RELAY_FORW.value()) {
387 log.debug("directlyConnected false. 1st realy-foward, 2nd MsgType {}", dhcp6Payload2.getMsgType());
388 return false;
389 } else {
390 // relay-reply
391 if (dhcp6Payload2.getMsgType() != DHCP6.MsgType.RELAY_REPL.value()) {
392 log.debug("directlyConnected true. 2nd MsgType {}", dhcp6Payload2.getMsgType());
393 return true; // must be directly connected
394 } else {
395 log.debug("directlyConnected false. 1st relay-reply, 2nd relay-reply MsgType {}",
396 dhcp6Payload2.getMsgType());
397 return false; // must be indirectly connected
398 }
399 }
400 } else {
Yi Tseng68ef26b2017-12-18 17:10:00 -0800401 log.debug("directlyConnected true.");
Kalhee Kim45fede42017-09-05 19:05:06 +0000402 return true;
403 }
404 }
405
406 /**
407 * extract DHCP6 payload from dhcp6 relay message within relay-forwrd/reply.
408 *
409 * @param dhcp6 dhcp6 relay-reply or relay-foward
410 * @return dhcp6Packet dhcp6 packet extracted from relay-message
411 */
412 private DHCP6 dhcp6PacketFromRelayPacket(DHCP6 dhcp6) {
413 log.debug("dhcp6PacketFromRelayPacket enters. dhcp6 {}", dhcp6);
414
415 // extract the relay message if exist
416 DHCP6 dhcp6Payload = dhcp6.getOptions().stream()
417 .filter(opt -> opt instanceof Dhcp6RelayOption)
418 .map(BasePacket::getPayload)
419 .map(pld -> (DHCP6) pld)
420 .findFirst()
421 .orElse(null);
422
423
424 if (dhcp6Payload == null) {
425 // Can't find dhcp payload
426 log.debug("Can't find dhcp6 payload from relay message");
427 } else {
428 log.debug("dhcp6 payload found from relay message {}", dhcp6Payload);
429 }
430
431 return dhcp6Payload;
432 }
433
434 /**
435 * find the leaf DHCP6 packet from multi-level relay packet.
436 *
437 * @param relayPacket dhcp6 relay packet
438 * @return leafPacket non-relay dhcp6 packet
439 */
440 private DHCP6 getDhcp6Leaf(DHCP6 relayPacket) {
441 DHCP6 dhcp6Parent = relayPacket;
442 DHCP6 dhcp6Child = null;
443
444 log.debug("getDhcp6Leaf entered.");
445 while (dhcp6Parent != null) {
446 dhcp6Child = dhcp6PacketFromRelayPacket(dhcp6Parent);
447
448 if (dhcp6Child != null) {
449 if (dhcp6Child.getMsgType() != DHCP6.MsgType.RELAY_FORW.value() &&
450 dhcp6Child.getMsgType() != DHCP6.MsgType.RELAY_REPL.value()) {
451 log.debug("leaf dhcp6 packet found.");
452 break;
453 } else {
454 // found another relay
455 // go for another loop
456 dhcp6Parent = dhcp6Child;
457 }
458 } else {
Yi Tseng68ef26b2017-12-18 17:10:00 -0800459 log.warn("Expected dhcp6 within relay pkt, but no dhcp6 leaf found.");
Kalhee Kim45fede42017-09-05 19:05:06 +0000460 break;
461 }
462 }
463 return dhcp6Child;
464 }
465
466 /**
467 * check if DHCP6 relay-reply is reply.
468 *
469 * @param relayPacket dhcp6 relay-reply
470 * @return boolean relay-reply contains ack
471 */
472 private boolean isDhcp6Reply(DHCP6 relayPacket) {
473 log.debug("isDhcp6Reply entered.");
474
475 DHCP6 leafDhcp6 = getDhcp6Leaf(relayPacket);
476
477 if (leafDhcp6 != null) {
478 if (leafDhcp6.getMsgType() == DHCP6.MsgType.REPLY.value()) {
479 log.debug("isDhcp6Reply true.");
480 return true; // must be directly connected
481 } else {
482 log.debug("isDhcp6Reply false. leaf dhcp6 is not replay. MsgType {}", leafDhcp6.getMsgType());
483 }
484 } else {
485 log.debug("isDhcp6Reply false. Expected dhcp6 within relay pkt but not found.");
486 }
487 log.debug("isDhcp6Reply false.");
488 return false;
489 }
490
491 /**
492 * check if DHCP6 is release or relay-forward contains release.
493 *
494 * @param dhcp6Payload dhcp6 packet
495 * @return boolean dhcp6 contains release
496 */
497 private boolean isDhcp6Release(DHCP6 dhcp6Payload) {
498
499 log.debug("isDhcp6Release entered.");
500
501 if (dhcp6Payload.getMsgType() == DHCP6.MsgType.RELEASE.value()) {
502 log.debug("isDhcp6Release true.");
503 return true; // must be directly connected
504 } else {
505 DHCP6 dhcp6Leaf = getDhcp6Leaf(dhcp6Payload);
506 if (dhcp6Leaf != null) {
507 if (dhcp6Leaf.getMsgType() == DHCP6.MsgType.RELEASE.value()) {
508 log.debug("isDhcp6Release true. indirectlry connected");
509 return true;
510 } else {
511 log.debug("leaf dhcp6 is not release. MsgType {}", dhcp6Leaf.getMsgType());
512 return false;
513 }
514 } else {
515 log.debug("isDhcp6Release false. dhcp6 is niether relay nor release.");
516 return false;
517 }
518 }
519 }
520
521 /**
522 * extract from dhcp6 packet client ipv6 address of given by dhcp server.
523 *
524 * @param dhcp6 the dhcp6 packet
525 * @return Ip6Address Ip6Address given by dhcp server, or null if not exists
526 */
527 private Ip6Address extractIpAddress(DHCP6 dhcp6) {
528 Ip6Address ip = null;
529
530 log.debug("extractIpAddress enters dhcp6 {}.", dhcp6);
531 // Extract IPv6 address from IA NA ot IA TA option
532 Optional<Dhcp6IaNaOption> iaNaOption = dhcp6.getOptions()
533 .stream()
534 .filter(opt -> opt instanceof Dhcp6IaNaOption)
535 .map(opt -> (Dhcp6IaNaOption) opt)
536 .findFirst();
537 Optional<Dhcp6IaTaOption> iaTaOption = dhcp6.getOptions()
538 .stream()
539 .filter(opt -> opt instanceof Dhcp6IaTaOption)
540 .map(opt -> (Dhcp6IaTaOption) opt)
541 .findFirst();
542 Optional<Dhcp6IaAddressOption> iaAddressOption;
543 if (iaNaOption.isPresent()) {
544 log.debug("Found IPv6 address from iaNaOption {}", iaNaOption);
545
546 iaAddressOption = iaNaOption.get().getOptions().stream()
547 .filter(opt -> opt instanceof Dhcp6IaAddressOption)
548 .map(opt -> (Dhcp6IaAddressOption) opt)
549 .findFirst();
550 } else if (iaTaOption.isPresent()) {
551 log.debug("Found IPv6 address from iaTaOption {}", iaTaOption);
552
553 iaAddressOption = iaTaOption.get().getOptions().stream()
554 .filter(opt -> opt instanceof Dhcp6IaAddressOption)
555 .map(opt -> (Dhcp6IaAddressOption) opt)
556 .findFirst();
557 } else {
558 iaAddressOption = Optional.empty();
559 }
560 if (iaAddressOption.isPresent()) {
561 ip = iaAddressOption.get().getIp6Address();
562 log.debug("Found IPv6 address from iaAddressOption {}", iaAddressOption);
563
564
565 } else {
566 log.debug("Can't find IPv6 address from DHCPv6 {}", dhcp6);
567 }
568
569 return ip;
570 }
571 /**
572 * extract from dhcp6 packet Prefix prefix provided by dhcp server.
573 *
574 * @param dhcp6 the dhcp6 payload
575 * @return IpPrefix Prefix Delegation prefix, or null if not exists.
576 */
577 private IpPrefix extractPrefix(DHCP6 dhcp6) {
Yi Tseng68ef26b2017-12-18 17:10:00 -0800578 log.trace("extractPrefix enters {}", dhcp6);
Kalhee Kim45fede42017-09-05 19:05:06 +0000579
580 // extract prefix
581 IpPrefix prefixPrefix = null;
582
583 Ip6Address prefixAddress = null;
584
585 // Extract IPv6 prefix from IA PD option
586 Optional<Dhcp6IaPdOption> iaPdOption = dhcp6.getOptions()
587 .stream()
588 .filter(opt -> opt instanceof Dhcp6IaPdOption)
589 .map(opt -> (Dhcp6IaPdOption) opt)
590 .findFirst();
591
592 Optional<Dhcp6IaPrefixOption> iaPrefixOption;
593 if (iaPdOption.isPresent()) {
Yi Tseng68ef26b2017-12-18 17:10:00 -0800594 log.trace("IA_PD option found {}", iaPdOption);
Kalhee Kim45fede42017-09-05 19:05:06 +0000595
596 iaPrefixOption = iaPdOption.get().getOptions().stream()
597 .filter(opt -> opt instanceof Dhcp6IaPrefixOption)
598 .map(opt -> (Dhcp6IaPrefixOption) opt)
599 .findFirst();
600 } else {
Yi Tseng68ef26b2017-12-18 17:10:00 -0800601 log.trace("IA_PD option NOT found");
Kalhee Kim45fede42017-09-05 19:05:06 +0000602
603 iaPrefixOption = Optional.empty();
604 }
605 if (iaPrefixOption.isPresent()) {
Yi Tseng68ef26b2017-12-18 17:10:00 -0800606 log.trace("IAPrefix Option within IA_PD option found {}", iaPrefixOption);
Kalhee Kim45fede42017-09-05 19:05:06 +0000607
608 prefixAddress = iaPrefixOption.get().getIp6Prefix();
609 int prefixLen = (int) iaPrefixOption.get().getPrefixLength();
Yi Tseng68ef26b2017-12-18 17:10:00 -0800610 log.trace("Prefix length is {} bits", prefixLen);
Kalhee Kim45fede42017-09-05 19:05:06 +0000611 prefixPrefix = IpPrefix.valueOf(prefixAddress, prefixLen);
Kalhee Kim45fede42017-09-05 19:05:06 +0000612 } else {
Yi Tseng68ef26b2017-12-18 17:10:00 -0800613 log.trace("Can't find IPv6 prefix from DHCPv6 {}", dhcp6);
Kalhee Kim45fede42017-09-05 19:05:06 +0000614 }
615
616 return prefixPrefix;
617 }
618
619 /**
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000620 * extract from dhcp6 packet ClientIdOption.
621 *
622 * @param directConnFlag directly connected host
623 * @param dhcp6Payload the dhcp6 payload
624 * @return Dhcp6ClientIdOption clientIdOption, or null if not exists.
625 */
626 private Dhcp6ClientIdOption extractClinedId(Boolean directConnFlag, DHCP6 dhcp6Payload) {
627 Dhcp6ClientIdOption clientIdOption;
628
629 if (directConnFlag) {
630 clientIdOption = dhcp6Payload.getOptions()
631 .stream()
632 .filter(opt -> opt instanceof Dhcp6ClientIdOption)
633 .map(opt -> (Dhcp6ClientIdOption) opt)
634 .findFirst()
635 .orElse(null);
636 } else {
637 DHCP6 leafDhcp = getDhcp6Leaf(dhcp6Payload);
638 clientIdOption = leafDhcp.getOptions()
639 .stream()
640 .filter(opt -> opt instanceof Dhcp6ClientIdOption)
641 .map(opt -> (Dhcp6ClientIdOption) opt)
642 .findFirst()
643 .orElse(null);
644 }
645
646 return clientIdOption;
647 }
648 /**
Kalhee Kim45fede42017-09-05 19:05:06 +0000649 * remove host or route.
650 *
651 * @param directConnFlag flag to show that packet is from directly connected client
652 * @param dhcp6Packet the dhcp6 payload
653 * @param clientPacket client's ethernet packet
654 * @param clientIpv6 client's Ipv6 packet
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +0000655 * @param clientInterface client interfaces
Kalhee Kim45fede42017-09-05 19:05:06 +0000656 */
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000657 private void removeHostOrRoute(boolean directConnFlag, ConnectPoint location,
658 DHCP6 dhcp6Packet,
Kalhee Kim45fede42017-09-05 19:05:06 +0000659 Ethernet clientPacket, IPv6 clientIpv6,
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +0000660 Interface clientInterface) {
Kalhee Kim45fede42017-09-05 19:05:06 +0000661 log.debug("extractPrefix enters {}", dhcp6Packet);
Kalhee Kim121ba922017-11-01 17:56:44 +0000662 VlanId vlanId = clientInterface.vlan();
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000663 MacAddress gwMac = clientPacket.getSourceMAC(); // could be gw or host
664 MacAddress leafClientMac;
665 byte leafMsgType;
666 log.debug("client mac {} client vlan {}", HexString.toHexString(gwMac.toBytes(), ":"), vlanId);
Kalhee Kim121ba922017-11-01 17:56:44 +0000667
Kalhee Kim45fede42017-09-05 19:05:06 +0000668 // add host or route
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000669 boolean isMsgRelease = isDhcp6Release(dhcp6Packet);
670 IpAddress ip;
671 IpPrefix ipPrefix = null;
672 if (directConnFlag) {
673 // Add to host store if it is connected to network directly
674 ip = extractIpAddress(dhcp6Packet);
675 if (ip != null) {
676 if (isMsgRelease) {
677 HostId hostId = HostId.hostId(gwMac, vlanId);
Kalhee Kim45fede42017-09-05 19:05:06 +0000678 log.debug("remove Host {} ip for directly connected.", hostId.toString());
Kalhee Kim45fede42017-09-05 19:05:06 +0000679 // Remove host's ip of when dhcp release msg is received
Yi Tsengaa417a62017-09-08 17:22:51 -0700680 providerService.removeIpFromHost(hostId, ip);
Kalhee Kim45fede42017-09-05 19:05:06 +0000681 }
682 } else {
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000683 log.debug("ipAddress not found. Do not remove Host {} for directly connected.",
684 HostId.hostId(gwMac, vlanId).toString());
685 }
686 leafMsgType = dhcp6Packet.getMsgType();
687 } else {
688 // Remove from route store if it is not connected to network directly
689 // pick out the first link-local ip address
690 IpAddress nextHopIp = getFirstIpByHost(gwMac, vlanId);
691 if (nextHopIp == null) {
692 log.warn("Can't find link-local IP address of gateway mac {} vlanId {}",
693 gwMac, vlanId);
694 return;
695 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000696
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000697 DHCP6 leafDhcp = getDhcp6Leaf(dhcp6Packet);
698 ip = extractIpAddress(leafDhcp);
699 if (ip == null) {
700 log.debug("ip is null");
701 } else {
702 if (isMsgRelease) {
Kalhee Kim45fede42017-09-05 19:05:06 +0000703 Route routeForIP = new Route(Route.Source.STATIC, ip.toIpPrefix(), nextHopIp);
704 log.debug("removing route of 128 address for indirectly connected.");
705 log.debug("128 ip {}, nexthop {}", HexString.toHexString(ip.toOctets(), ":"),
706 HexString.toHexString(nextHopIp.toOctets(), ":"));
707 routeStore.removeRoute(routeForIP);
708 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000709 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000710
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000711 ipPrefix = extractPrefix(leafDhcp);
712 if (ipPrefix == null) {
713 log.debug("ipPrefix is null ");
714 } else {
715 if (isMsgRelease) {
Kalhee Kim45fede42017-09-05 19:05:06 +0000716 Route routeForPrefix = new Route(Route.Source.STATIC, ipPrefix, nextHopIp);
717 log.debug("removing route of PD for indirectly connected.");
718 log.debug("pd ip {}, nexthop {}", HexString.toHexString(ipPrefix.address().toOctets(), ":"),
719 HexString.toHexString(nextHopIp.toOctets(), ":"));
720
721 routeStore.removeRoute(routeForPrefix);
Kalhee Kimba366062017-11-07 16:32:09 +0000722 if (this.dhcpFpmEnabled) {
723 dhcpFpmPrefixStore.removeFpmRecord(ipPrefix);
724 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000725 }
726 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000727 leafMsgType = leafDhcp.getMsgType();
Kalhee Kim45fede42017-09-05 19:05:06 +0000728 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000729
730 Dhcp6ClientIdOption clientIdOption = extractClinedId(directConnFlag, dhcp6Packet);
731 if (clientIdOption != null) {
732 log.warn("CLIENTID option found {}", clientIdOption);
733 if ((clientIdOption.getDuid().getDuidType() == Dhcp6Duid.DuidType.DUID_LLT) ||
734 (clientIdOption.getDuid().getDuidType() == Dhcp6Duid.DuidType.DUID_LL)) {
735 leafClientMac = MacAddress.valueOf(clientIdOption.getDuid().getLinkLayerAddress());
736 } else {
737 log.warn("Link-Layer Address not supported in CLIENTID option. No DhcpRelay Record created.");
738 return;
739 }
740
741 } else {
742 log.warn("CLIENTID option NOT found. No DhcpRelay Record created.");
743 return;
744 }
745
746 HostId hostId = HostId.hostId(leafClientMac, vlanId);
747 DhcpRecord record = dhcpRelayStore.getDhcpRecord(hostId).orElse(null);
748
749 if (leafMsgType == DHCP6.MsgType.RELEASE.value()) {
750 log.debug("DHCP6 RELEASE msg.");
751 if (record != null) {
752 if (ip != null) {
753 log.warn("DhcpRelay Record ip6Address is set to null.");
754 record.ip6Address(null);
755 }
756 if (ipPrefix != null) {
757 log.warn("DhcpRelay Record pdPrefix is set to null.");
758 record.pdPrefix(null);
759 }
760 log.debug("ip {} pd {}", record.ip6Address(), record.pdPrefix());
761
762 if (!record.ip6Address().isPresent() && !record.pdPrefix().isPresent()) {
763 log.warn("IP6 address and IP6 PD both are null. Remove record.");
764 dhcpRelayStore.removeDhcpRecord(HostId.hostId(leafClientMac, vlanId));
765 }
766 }
767 return;
768 }
769 if (record == null) {
770 record = new DhcpRecord(HostId.hostId(leafClientMac, vlanId));
771 } else {
772 record = record.clone();
773 }
774 record.addLocation(new HostLocation(location, System.currentTimeMillis()));
775 record.ip6Status(DHCP6.MsgType.getType(dhcp6Packet.getMsgType()));
776 record.setDirectlyConnected(directConnFlag);
777 if (!directConnFlag) {
778 // Update gateway mac address if the host is not directly connected
779 record.nextHop(gwMac);
780 }
781 record.updateLastSeen();
782 dhcpRelayStore.updateDhcpRecord(HostId.hostId(leafClientMac, vlanId), record);
783
784
Kalhee Kim45fede42017-09-05 19:05:06 +0000785 }
786
787 /**
788 * add host or route.
789 *
790 * @param directConnFlag flag to show that packet is from directly connected client
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000791 * @param location client side connect point
Kalhee Kim45fede42017-09-05 19:05:06 +0000792 * @param dhcp6Relay the dhcp6 payload
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000793 * @param embeddedDhcp6 the dhcp6 payload within relay
794 * @param gwMac client gw/host macAddress
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +0000795 * @param clientInterface client interface
Kalhee Kim45fede42017-09-05 19:05:06 +0000796 */
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000797 private void addHostOrRoute(boolean directConnFlag,
798 ConnectPoint location,
799 DHCP6 dhcp6Relay,
800 DHCP6 embeddedDhcp6,
801 MacAddress gwMac,
802 Interface clientInterface) {
Kalhee Kim45fede42017-09-05 19:05:06 +0000803 log.debug("addHostOrRoute entered.");
Kalhee Kim121ba922017-11-01 17:56:44 +0000804 VlanId vlanId = clientInterface.vlan();
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000805 Boolean isMsgReply = isDhcp6Reply(dhcp6Relay);
806 MacAddress leafClientMac;
807 Byte leafMsgType;
808
Kalhee Kim45fede42017-09-05 19:05:06 +0000809 // add host or route
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000810 IpAddress ip;
811 IpPrefix ipPrefix = null;
812 if (directConnFlag) {
813 // Add to host store if it connect to network directly
814 ip = extractIpAddress(embeddedDhcp6);
815 if (ip != null) {
816 if (isMsgReply) {
Kalhee Kim45fede42017-09-05 19:05:06 +0000817 Set<IpAddress> ips = Sets.newHashSet(ip);
Yi Tsengaa417a62017-09-08 17:22:51 -0700818
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000819 HostId hostId = HostId.hostId(gwMac, vlanId);
Yi Tsengaa417a62017-09-08 17:22:51 -0700820 Host host = hostService.getHost(hostId);
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +0000821 HostLocation hostLocation = new HostLocation(clientInterface.connectPoint(),
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000822 System.currentTimeMillis());
Yi Tsengaa417a62017-09-08 17:22:51 -0700823 Set<HostLocation> hostLocations = Sets.newHashSet(hostLocation);
824
825 if (host != null) {
826 // Dual homing support:
827 // if host exists, use old locations and new location
828 hostLocations.addAll(host.locations());
829 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000830 HostDescription desc = new DefaultHostDescription(gwMac, vlanId,
831 hostLocations, ips,
832 false);
Kalhee Kim45fede42017-09-05 19:05:06 +0000833 log.debug("adding Host for directly connected.");
834 log.debug("client mac {} client vlan {} hostlocation {}",
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000835 HexString.toHexString(gwMac.toBytes(), ":"),
Kalhee Kim45fede42017-09-05 19:05:06 +0000836 vlanId, hostLocation.toString());
837
838 // Replace the ip when dhcp server give the host new ip address
Yi Tsengaa417a62017-09-08 17:22:51 -0700839 providerService.hostDetected(hostId, desc, false);
Kalhee Kim45fede42017-09-05 19:05:06 +0000840 }
841 } else {
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000842 log.warn("ipAddress not found. Do not add Host {} for directly connected.",
843 HostId.hostId(gwMac, vlanId).toString());
844 }
845 leafMsgType = embeddedDhcp6.getMsgType();
846 } else {
847 // Add to route store if it does not connect to network directly
848 // pick out the first link-local ip address
849 IpAddress nextHopIp = getFirstIpByHost(gwMac, vlanId);
850 if (nextHopIp == null) {
851 log.warn("Can't find link-local IP address of gateway mac {} vlanId {}",
852 gwMac, vlanId);
853 return;
854 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000855
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000856 DHCP6 leafDhcp = getDhcp6Leaf(embeddedDhcp6);
857 ip = extractIpAddress(leafDhcp);
858 if (ip == null) {
Yi Tseng68ef26b2017-12-18 17:10:00 -0800859 log.debug("ip is null");
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000860 } else {
861 if (isMsgReply) {
Kalhee Kim45fede42017-09-05 19:05:06 +0000862 Route routeForIP = new Route(Route.Source.STATIC, ip.toIpPrefix(), nextHopIp);
Yi Tseng68ef26b2017-12-18 17:10:00 -0800863 log.debug("adding Route of 128 address for indirectly connected.");
Kalhee Kim45fede42017-09-05 19:05:06 +0000864 routeStore.updateRoute(routeForIP);
865 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000866 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000867
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000868 ipPrefix = extractPrefix(leafDhcp);
869 if (ipPrefix == null) {
Yi Tseng68ef26b2017-12-18 17:10:00 -0800870 log.debug("ipPrefix is null ");
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000871 } else {
872 if (isMsgReply) {
Kalhee Kim45fede42017-09-05 19:05:06 +0000873 Route routeForPrefix = new Route(Route.Source.STATIC, ipPrefix, nextHopIp);
Yi Tseng68ef26b2017-12-18 17:10:00 -0800874 log.debug("adding Route of PD for indirectly connected.");
Kalhee Kim45fede42017-09-05 19:05:06 +0000875 routeStore.updateRoute(routeForPrefix);
Kalhee Kimba366062017-11-07 16:32:09 +0000876 if (this.dhcpFpmEnabled) {
877 FpmRecord record = new FpmRecord(ipPrefix, nextHopIp, FpmRecord.Type.DHCP_RELAY);
878 dhcpFpmPrefixStore.addFpmRecord(ipPrefix, record);
879 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000880 }
881 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000882 leafMsgType = leafDhcp.getMsgType();
Kalhee Kim45fede42017-09-05 19:05:06 +0000883 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000884
885 Dhcp6ClientIdOption clientIdOption = extractClinedId(directConnFlag, embeddedDhcp6);
886 if (clientIdOption != null) {
887 log.debug("CLIENTID option found {}", clientIdOption);
888 if ((clientIdOption.getDuid().getDuidType() == Dhcp6Duid.DuidType.DUID_LLT) ||
889 (clientIdOption.getDuid().getDuidType() == Dhcp6Duid.DuidType.DUID_LL)) {
890 leafClientMac = MacAddress.valueOf(clientIdOption.getDuid().getLinkLayerAddress());
891 } else {
892 log.warn("Link-Layer Address not supported in CLIENTID option. No DhcpRelay Record created.");
893 return;
894 }
895
896 } else {
897 log.warn("CLIENTID option NOT found. No DhcpRelay Record created.");
898 return;
899 }
900
901 if (leafMsgType == DHCP6.MsgType.RELEASE.value() ||
902 (leafMsgType == DHCP6.MsgType.REPLY.value()) && ip == null) {
903 log.warn("DHCP6 RELEASE/REPLY(null ip) from Server. MsgType {}", DHCP6.MsgType.getType(leafMsgType));
904 return;
905 }
906
907 HostId hostId = HostId.hostId(leafClientMac, vlanId);
908 DhcpRecord record = dhcpRelayStore.getDhcpRecord(hostId).orElse(null);
909 if (record == null) {
910 record = new DhcpRecord(HostId.hostId(leafClientMac, vlanId));
911 } else {
912 record = record.clone();
913 }
914 record.addLocation(new HostLocation(location, System.currentTimeMillis()));
915 if (leafMsgType == DHCP6.MsgType.REPLY.value()) {
916 if (ip != null) {
917 log.debug("IP6 address is being stored into dhcp-relay store.");
918 log.debug("IP6 address {}", HexString.toHexString(ip.toOctets(), ":"));
919 record.ip6Address(ip.getIp6Address());
920 } else {
921 log.debug("IP6 address is not returned from server. Maybe only PD is returned.");
922 }
923 if (ipPrefix != null) {
924 log.debug("IP6 PD address is being stored into dhcp-relay store.");
925 log.debug("IP6 PD address {}", HexString.toHexString(ipPrefix.address().toOctets(), ":"));
926 record.pdPrefix(ipPrefix);
927 } else {
928 log.debug("IP6 PD address is not returned from server. Maybe only IPAddress is returned.");
929 }
930 }
931 record.ip6Status(DHCP6.MsgType.getType(dhcp6Relay.getMsgType()));
932 record.setDirectlyConnected(directConnFlag);
933 record.updateLastSeen();
934 dhcpRelayStore.updateDhcpRecord(HostId.hostId(leafClientMac, vlanId), record);
935
Kalhee Kim45fede42017-09-05 19:05:06 +0000936 }
937
938 /**
Yi Tseng25bfe372017-11-03 16:27:32 -0700939 * Build the DHCP6 solicit/request packet with gatewayip.
940 * TODO: method too long, need to be refactored.
Kalhee Kim45fede42017-09-05 19:05:06 +0000941 *
942 * @param context packet context
943 * @param clientPacket client ethernet packet
944 * @param clientInterfaces set of client side interfaces
945 */
Yi Tseng3bd57ac2017-11-29 14:39:18 -0800946 private InternalPacket processDhcp6PacketFromClient(PacketContext context,
947 Ethernet clientPacket, Set<Interface> clientInterfaces) {
948 ConnectPoint receivedFrom = context.inPacket().receivedFrom();
949 DeviceId receivedFromDevice = receivedFrom.deviceId();
950 DhcpServerInfo serverInfo;
951 Ip6Address dhcpServerIp = null;
952 ConnectPoint dhcpServerConnectPoint = null;
953 MacAddress dhcpConnectMac = null;
954 VlanId dhcpConnectVlan = null;
955 Ip6Address dhcpGatewayIp = null;
956 Ip6Address indirectDhcpServerIp = null;
957 ConnectPoint indirectDhcpServerConnectPoint = null;
958 MacAddress indirectDhcpConnectMac = null;
959 VlanId indirectDhcpConnectVlan = null;
960 Ip6Address indirectDhcpGatewayIp = null;
961 Ip6Address indirectRelayAgentIpFromCfg = null;
962 if (!defaultServerInfoList.isEmpty()) {
963 serverInfo = defaultServerInfoList.get(0);
964 dhcpConnectMac = serverInfo.getDhcpConnectMac().orElse(null);
965 dhcpGatewayIp = serverInfo.getDhcpGatewayIp6().orElse(null);
966 dhcpServerIp = serverInfo.getDhcpServerIp6().orElse(null);
967 dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
968 dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
969 }
970 if (!indirectServerInfoList.isEmpty()) {
971 serverInfo = indirectServerInfoList.get(0);
972 indirectDhcpConnectMac = serverInfo.getDhcpConnectMac().orElse(null);
973 indirectDhcpGatewayIp = serverInfo.getDhcpGatewayIp6().orElse(null);
974 indirectDhcpServerIp = serverInfo.getDhcpServerIp6().orElse(null);
975 indirectDhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
976 indirectDhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
977 indirectRelayAgentIpFromCfg = serverInfo.getRelayAgentIp6(receivedFromDevice).orElse(null);
978 }
979 Ip6Address relayAgentIp = getRelayAgentIPv6Address(clientInterfaces);
980 MacAddress relayAgentMac = clientInterfaces.iterator().next().mac();
981 if (relayAgentIp == null || relayAgentMac == null) {
982 log.warn("Missing DHCP relay agent interface Ipv6 addr config for "
983 + "packet from client on port: {}. Aborting packet processing",
984 clientInterfaces.iterator().next().connectPoint());
985 return null;
986 }
987 // get dhcp6 header.
988 IPv6 clientIpv6 = (IPv6) clientPacket.getPayload();
989 UDP clientUdp = (UDP) clientIpv6.getPayload();
990 DHCP6 clientDhcp6 = (DHCP6) clientUdp.getPayload();
991 boolean directConnFlag = directlyConnected(clientDhcp6);
992 Interface serverInterface;
993 if (directConnFlag) {
994 serverInterface = getServerInterface();
995 } else {
996 serverInterface = getIndirectServerInterface();
997 if (serverInterface == null) {
998 // Indirect server interface not found, use default server interface
999 serverInterface = getServerInterface();
1000 }
1001 }
1002 if (serverInterface == null) {
1003 log.warn("Can't get {} server interface, ignore", directConnFlag ? "direct" : "indirect");
1004 return null;
1005 }
1006 Ip6Address ipFacingServer = getFirstIpFromInterface(serverInterface);
1007 MacAddress macFacingServer = serverInterface.mac();
1008 if (ipFacingServer == null || macFacingServer == null) {
1009 log.warn("No IP v6 address for server Interface {}", serverInterface);
1010 return null;
1011 }
Yi Tseng68ef26b2017-12-18 17:10:00 -08001012 Ethernet etherReply = clientPacket.duplicate();
Yi Tseng3bd57ac2017-11-29 14:39:18 -08001013 etherReply.setSourceMACAddress(macFacingServer);
1014 if ((directConnFlag && dhcpConnectMac == null) ||
1015 !directConnFlag && indirectDhcpConnectMac == null && dhcpConnectMac == null) {
Yi Tseng68ef26b2017-12-18 17:10:00 -08001016 log.trace("Packet received from {} connected client.", directConnFlag ? "directly" : "indirectly");
1017 log.debug("DHCP6 {} not yet resolved .. Aborting DHCP packet processing from client on port: {}",
Yi Tseng3bd57ac2017-11-29 14:39:18 -08001018 (dhcpGatewayIp == null) ? "server IP " + dhcpServerIp
1019 : "gateway IP " + dhcpGatewayIp,
1020 clientInterfaces.iterator().next().connectPoint());
1021 return null;
1022 }
1023 if (dhcpServerConnectPoint == null) {
1024 log.warn("DHCP6 server connection point direct {} directConn {} indirectConn {} is not set up yet",
Yi Tseng25bfe372017-11-03 16:27:32 -07001025 directConnFlag, dhcpServerConnectPoint, indirectDhcpServerConnectPoint);
Kalhee Kim121ba922017-11-01 17:56:44 +00001026 return null;
1027 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001028
Yi Tseng25bfe372017-11-03 16:27:32 -07001029 etherReply.setDestinationMACAddress(dhcpConnectMac);
1030 etherReply.setVlanID(dhcpConnectVlan.toShort());
Kalhee Kim121ba922017-11-01 17:56:44 +00001031 IPv6 ipv6Packet = (IPv6) etherReply.getPayload();
1032 byte[] peerAddress = clientIpv6.getSourceAddress();
1033 ipv6Packet.setSourceAddress(ipFacingServer.toOctets());
Yi Tseng25bfe372017-11-03 16:27:32 -07001034 ipv6Packet.setDestinationAddress(dhcpServerIp.toOctets());
Kalhee Kim121ba922017-11-01 17:56:44 +00001035 UDP udpPacket = (UDP) ipv6Packet.getPayload();
1036 udpPacket.setSourcePort(UDP.DHCP_V6_SERVER_PORT);
1037 DHCP6 dhcp6Packet = (DHCP6) udpPacket.getPayload();
1038 byte[] dhcp6PacketByte = dhcp6Packet.serialize();
Charles Chana990ce92017-10-30 10:22:50 -07001039
Kalhee Kim121ba922017-11-01 17:56:44 +00001040 ConnectPoint clientConnectionPoint = context.inPacket().receivedFrom();
1041 VlanId vlanIdInUse = VlanId.vlanId(clientPacket.getVlanID());
1042 Interface clientInterface = interfaceService.getInterfacesByPort(clientConnectionPoint)
1043 .stream().filter(iface -> interfaceContainsVlan(iface, vlanIdInUse))
1044 .findFirst().orElse(null);
Kalhee Kim45fede42017-09-05 19:05:06 +00001045
Kalhee Kimea4b6c22017-11-09 14:38:37 +00001046 removeHostOrRoute(directConnFlag, clientConnectionPoint, dhcp6Packet, clientPacket,
1047 clientIpv6, clientInterface);
Kalhee Kim45b24182017-10-18 18:30:23 +00001048
Kalhee Kim121ba922017-11-01 17:56:44 +00001049 DHCP6 dhcp6Relay = new DHCP6();
1050 dhcp6Relay.setMsgType(DHCP6.MsgType.RELAY_FORW.value());
Kalhee Kim121ba922017-11-01 17:56:44 +00001051 if (directConnFlag) {
1052 dhcp6Relay.setLinkAddress(relayAgentIp.toOctets());
1053 log.debug("direct connection: relayAgentIp obtained dynamically {}",
1054 HexString.toHexString(relayAgentIp.toOctets(), ":"));
Kalhee Kim45fede42017-09-05 19:05:06 +00001055
Kalhee Kim121ba922017-11-01 17:56:44 +00001056 } else {
Yi Tseng25bfe372017-11-03 16:27:32 -07001057 if (indirectDhcpServerIp == null) {
Yi Tseng68ef26b2017-12-18 17:10:00 -08001058 log.debug("indirect DhcpServerIp not available, use default DhcpServerIp {}",
Yi Tseng25bfe372017-11-03 16:27:32 -07001059 HexString.toHexString(dhcpServerIp.toOctets()));
Kalhee Kimd21029f2017-09-26 20:21:53 +00001060 } else {
1061 // Indirect case, replace destination to indirect dhcp server if exist
1062 // Check if mac is obtained for valid server ip
Yi Tseng25bfe372017-11-03 16:27:32 -07001063 if (indirectDhcpConnectMac == null) {
Kalhee Kimd21029f2017-09-26 20:21:53 +00001064 log.warn("DHCP6 {} not yet resolved .. Aborting DHCP "
Yi Tseng25bfe372017-11-03 16:27:32 -07001065 + "packet processing from client on port: {}",
1066 (indirectDhcpGatewayIp == null) ? "server IP " + indirectDhcpServerIp
1067 : "gateway IP " + indirectDhcpGatewayIp,
1068 clientInterfaces.iterator().next().connectPoint());
Kalhee Kimd21029f2017-09-26 20:21:53 +00001069 return null;
1070 }
Yi Tseng25bfe372017-11-03 16:27:32 -07001071 etherReply.setDestinationMACAddress(indirectDhcpConnectMac);
1072 etherReply.setVlanID(indirectDhcpConnectVlan.toShort());
1073 ipv6Packet.setDestinationAddress(indirectDhcpServerIp.toOctets());
Kalhee Kimea4b6c22017-11-09 14:38:37 +00001074
1075
Kalhee Kimd21029f2017-09-26 20:21:53 +00001076 }
Yi Tseng25bfe372017-11-03 16:27:32 -07001077 if (indirectRelayAgentIpFromCfg == null) {
Kalhee Kim45fede42017-09-05 19:05:06 +00001078 dhcp6Relay.setLinkAddress(relayAgentIp.toOctets());
Yi Tseng68ef26b2017-12-18 17:10:00 -08001079 log.trace("indirect connection: relayAgentIp NOT availale from config file! Use dynamic. {}",
Yi Tseng25bfe372017-11-03 16:27:32 -07001080 HexString.toHexString(relayAgentIp.toOctets(), ":"));
1081 } else {
1082 dhcp6Relay.setLinkAddress(indirectRelayAgentIpFromCfg.toOctets());
Yi Tseng68ef26b2017-12-18 17:10:00 -08001083 log.trace("indirect connection: relayAgentIp from config file is available! {}",
Yi Tseng25bfe372017-11-03 16:27:32 -07001084 HexString.toHexString(indirectRelayAgentIpFromCfg.toOctets(), ":"));
1085 }
1086 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +00001087
Yi Tseng25bfe372017-11-03 16:27:32 -07001088 // peer address: address of the client or relay agent from which
1089 // the message to be relayed was received.
1090 dhcp6Relay.setPeerAddress(peerAddress);
1091 List<Dhcp6Option> options = new ArrayList<>();
1092 // directly connected case, hop count is zero; otherwise, hop count + 1
1093 if (directConnFlag) {
1094 dhcp6Relay.setHopCount((byte) 0);
1095 } else {
1096 dhcp6Relay.setHopCount((byte) (dhcp6Packet.getHopCount() + 1));
1097 }
1098 // create relay message option
1099 Dhcp6Option relayMessage = new Dhcp6Option();
1100 relayMessage.setCode(DHCP6.OptionCode.RELAY_MSG.value());
1101 relayMessage.setLength((short) dhcp6PacketByte.length);
1102 relayMessage.setData(dhcp6PacketByte);
1103 options.add(relayMessage);
1104 // create interfaceId option
1105 String inPortString = "-" + context.inPacket().receivedFrom().toString() + ":";
1106 Dhcp6Option interfaceId = new Dhcp6Option();
1107 interfaceId.setCode(DHCP6.OptionCode.INTERFACE_ID.value());
1108 byte[] clientSoureMacBytes = clientPacket.getSourceMACAddress();
1109 byte[] inPortStringBytes = inPortString.getBytes();
1110 byte[] vlanIdBytes = new byte[2];
1111 vlanIdBytes[0] = (byte) (clientPacket.getVlanID() & 0xff);
1112 vlanIdBytes[1] = (byte) ((clientPacket.getVlanID() >> 8) & 0xff);
1113 byte[] interfaceIdBytes = new byte[clientSoureMacBytes.length +
1114 inPortStringBytes.length + vlanIdBytes.length];
Yi Tseng68ef26b2017-12-18 17:10:00 -08001115 log.trace("Length: interfaceIdBytes {} clientSoureMacBytes {} inPortStringBytes {} vlan {}",
Yi Tseng25bfe372017-11-03 16:27:32 -07001116 interfaceIdBytes.length, clientSoureMacBytes.length, inPortStringBytes.length,
1117 vlanIdBytes.length);
1118 System.arraycopy(clientSoureMacBytes, 0, interfaceIdBytes, 0, clientSoureMacBytes.length);
1119 System.arraycopy(inPortStringBytes, 0, interfaceIdBytes, clientSoureMacBytes.length, inPortStringBytes.length);
1120 System.arraycopy(vlanIdBytes, 0, interfaceIdBytes, clientSoureMacBytes.length + inPortStringBytes.length,
1121 vlanIdBytes.length);
1122 interfaceId.setData(interfaceIdBytes);
1123 interfaceId.setLength((short) interfaceIdBytes.length);
1124 options.add(interfaceId);
1125 log.debug("interfaceId write srcMac {} portString {}",
1126 HexString.toHexString(clientSoureMacBytes, ":"), inPortString);
1127 dhcp6Relay.setOptions(options);
1128 udpPacket.setPayload(dhcp6Relay);
1129 udpPacket.resetChecksum();
1130 ipv6Packet.setPayload(udpPacket);
1131 ipv6Packet.setHopLimit((byte) 64);
1132 etherReply.setPayload(ipv6Packet);
1133 if (directConnFlag || indirectDhcpServerIp == null) {
1134 return new InternalPacket(etherReply, dhcpServerConnectPoint);
1135 } else {
1136 return new InternalPacket(etherReply, indirectDhcpServerConnectPoint);
1137 }
1138 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001139
1140 /**
1141 *
1142 * process the DHCP6 relay-reply packet from dhcp server.
1143 *
1144 * @param context packet context
1145 * @param receivedPacket server ethernet packet
1146 * @param recevingInterfaces set of server side interfaces
1147 */
1148 private InternalPacket processDhcp6PacketFromServer(PacketContext context,
1149 Ethernet receivedPacket, Set<Interface> recevingInterfaces) {
Yi Tseng25bfe372017-11-03 16:27:32 -07001150
1151 ConnectPoint receivedFrom = context.inPacket().receivedFrom();
1152 DeviceId receivedFromDevice = receivedFrom.deviceId();
1153
1154 // TODO: refactor
1155 DhcpServerInfo serverInfo;
1156 Ip6Address dhcpServerIp = null;
1157 ConnectPoint dhcpServerConnectPoint = null;
1158 MacAddress dhcpConnectMac = null;
1159 VlanId dhcpConnectVlan = null;
1160 Ip6Address dhcpGatewayIp = null;
1161
1162 Ip6Address indirectDhcpServerIp = null;
1163 ConnectPoint indirectDhcpServerConnectPoint = null;
1164 MacAddress indirectDhcpConnectMac = null;
1165 VlanId indirectDhcpConnectVlan = null;
1166 Ip6Address indirectDhcpGatewayIp = null;
1167 Ip6Address indirectRelayAgentIpFromCfg = null;
1168
1169 if (!defaultServerInfoList.isEmpty()) {
1170 serverInfo = defaultServerInfoList.get(0);
1171 dhcpConnectMac = serverInfo.getDhcpConnectMac().orElse(null);
1172 dhcpGatewayIp = serverInfo.getDhcpGatewayIp6().orElse(null);
1173 dhcpServerIp = serverInfo.getDhcpServerIp6().orElse(null);
1174 dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1175 dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1176 }
1177
1178 if (!indirectServerInfoList.isEmpty()) {
1179 serverInfo = indirectServerInfoList.get(0);
1180 indirectDhcpConnectMac = serverInfo.getDhcpConnectMac().orElse(null);
1181 indirectDhcpGatewayIp = serverInfo.getDhcpGatewayIp6().orElse(null);
1182 indirectDhcpServerIp = serverInfo.getDhcpServerIp6().orElse(null);
1183 indirectDhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1184 indirectDhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1185 indirectRelayAgentIpFromCfg = serverInfo.getRelayAgentIp6(receivedFromDevice).orElse(null);
1186 }
1187
Kalhee Kim45fede42017-09-05 19:05:06 +00001188 // get dhcp6 header.
Ray Milkeyf0c47612017-09-28 11:29:38 -07001189 Ethernet etherReply = receivedPacket.duplicate();
Kalhee Kim45fede42017-09-05 19:05:06 +00001190 IPv6 ipv6Packet = (IPv6) etherReply.getPayload();
1191 UDP udpPacket = (UDP) ipv6Packet.getPayload();
1192 DHCP6 dhcp6Relay = (DHCP6) udpPacket.getPayload();
1193
1194 Boolean directConnFlag = directlyConnected(dhcp6Relay);
Kalhee Kimd21029f2017-09-26 20:21:53 +00001195 ConnectPoint inPort = context.inPacket().receivedFrom();
Ray Milkeyfe0e0852018-01-18 11:14:05 -08001196 if ((directConnFlag || indirectDhcpServerIp == null)
Yi Tseng25bfe372017-11-03 16:27:32 -07001197 && !inPort.equals(dhcpServerConnectPoint)) {
Kalhee Kimd21029f2017-09-26 20:21:53 +00001198 log.warn("Receiving port {} is not the same as server connect point {} for direct or indirect-null",
Yi Tseng25bfe372017-11-03 16:27:32 -07001199 inPort, dhcpServerConnectPoint);
Kalhee Kimd21029f2017-09-26 20:21:53 +00001200 return null;
1201 }
1202
Yi Tseng25bfe372017-11-03 16:27:32 -07001203 if (!directConnFlag && indirectDhcpServerIp != null &&
1204 !inPort.equals(indirectDhcpServerConnectPoint)) {
Kalhee Kimd21029f2017-09-26 20:21:53 +00001205 log.warn("Receiving port {} is not the same as server connect point {} for indirect",
Yi Tseng25bfe372017-11-03 16:27:32 -07001206 inPort, indirectDhcpServerConnectPoint);
Kalhee Kimd21029f2017-09-26 20:21:53 +00001207 return null;
1208 }
1209
Kalhee Kim45fede42017-09-05 19:05:06 +00001210
1211 Dhcp6InterfaceIdOption interfaceIdOption = dhcp6Relay.getOptions().stream()
1212 .filter(opt -> opt instanceof Dhcp6InterfaceIdOption)
1213 .map(opt -> (Dhcp6InterfaceIdOption) opt)
1214 .findFirst()
1215 .orElse(null);
1216
1217 if (interfaceIdOption == null) {
1218 log.warn("Interface Id option is not present, abort packet...");
1219 return null;
1220 }
1221
1222 MacAddress peerMac = interfaceIdOption.getMacAddress();
1223 String clientConnectionPointStr = new String(interfaceIdOption.getInPort());
1224
1225 ConnectPoint clientConnectionPoint = ConnectPoint.deviceConnectPoint(clientConnectionPointStr);
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +00001226 VlanId vlanIdInUse = VlanId.vlanId(interfaceIdOption.getVlanId());
1227 Interface clientInterface = interfaceService.getInterfacesByPort(clientConnectionPoint)
1228 .stream()
1229 .filter(iface -> interfaceContainsVlan(iface, vlanIdInUse))
1230 .findFirst()
1231 .orElse(null);
1232 if (clientInterface == null) {
1233 log.warn("Cannot get client interface for from packet, abort... vlan {}", vlanIdInUse.toString());
Kalhee Kim45fede42017-09-05 19:05:06 +00001234 return null;
1235 }
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +00001236 MacAddress relayAgentMac = clientInterface.mac();
Kalhee Kim45fede42017-09-05 19:05:06 +00001237 if (relayAgentMac == null) {
Yi Tseng68ef26b2017-12-18 17:10:00 -08001238 log.warn("Can not get client interface mac, abort packet..");
Kalhee Kim45fede42017-09-05 19:05:06 +00001239 return null;
1240 }
1241 etherReply.setSourceMACAddress(relayAgentMac);
1242
1243 // find destMac
Yi Tseng68ef26b2017-12-18 17:10:00 -08001244 MacAddress clientMac;
Yi Tsengaa417a62017-09-08 17:22:51 -07001245 Ip6Address peerAddress = Ip6Address.valueOf(dhcp6Relay.getPeerAddress());
1246 Set<Host> clients = hostService.getHostsByIp(peerAddress);
Kalhee Kim45fede42017-09-05 19:05:06 +00001247 if (clients.isEmpty()) {
Yi Tseng68ef26b2017-12-18 17:10:00 -08001248 log.trace("There's no host found for this address {}",
Kalhee Kim45fede42017-09-05 19:05:06 +00001249 HexString.toHexString(dhcp6Relay.getPeerAddress(), ":"));
Yi Tseng68ef26b2017-12-18 17:10:00 -08001250 log.trace("Let's look up interfaceId {}", HexString.toHexString(peerMac.toBytes(), ":"));
Kalhee Kim45fede42017-09-05 19:05:06 +00001251 clientMac = peerMac;
1252 } else {
1253 clientMac = clients.iterator().next().mac();
1254 if (clientMac == null) {
1255 log.warn("No client mac address found, abort packet...");
1256 return null;
1257 }
Yi Tseng68ef26b2017-12-18 17:10:00 -08001258 log.trace("Client mac address found from getHostByIp");
Kalhee Kim45fede42017-09-05 19:05:06 +00001259
1260 }
1261 etherReply.setDestinationMACAddress(clientMac);
1262
1263 // ip header
1264 ipv6Packet.setSourceAddress(dhcp6Relay.getLinkAddress());
1265 ipv6Packet.setDestinationAddress(dhcp6Relay.getPeerAddress());
1266 // udp header
1267 udpPacket.setSourcePort(UDP.DHCP_V6_SERVER_PORT);
1268 if (directConnFlag) {
1269 udpPacket.setDestinationPort(UDP.DHCP_V6_CLIENT_PORT);
1270 } else {
1271 udpPacket.setDestinationPort(UDP.DHCP_V6_SERVER_PORT);
1272 }
1273
1274 DHCP6 embeddedDhcp6 = dhcp6Relay.getOptions().stream()
1275 .filter(opt -> opt instanceof Dhcp6RelayOption)
1276 .map(BasePacket::getPayload)
1277 .map(pld -> (DHCP6) pld)
1278 .findFirst()
1279 .orElse(null);
1280
1281
1282 // add host or route
Kalhee Kimea4b6c22017-11-09 14:38:37 +00001283 addHostOrRoute(directConnFlag, clientConnectionPoint, dhcp6Relay, embeddedDhcp6, clientMac, clientInterface);
Kalhee Kim45fede42017-09-05 19:05:06 +00001284
1285 udpPacket.setPayload(embeddedDhcp6);
1286 udpPacket.resetChecksum();
1287 ipv6Packet.setPayload(udpPacket);
1288 etherReply.setPayload(ipv6Packet);
1289
1290 return new InternalPacket(etherReply, clientConnectionPoint);
1291 }
1292
Yi Tseng919b2df2017-09-07 16:22:51 -07001293 // Returns the first v6 interface ip out of a set of interfaces or null.
Kalhee Kim45fede42017-09-05 19:05:06 +00001294 // Checks all interfaces, and ignores v6 interface ips
1295 private Ip6Address getRelayAgentIPv6Address(Set<Interface> intfs) {
1296 for (Interface intf : intfs) {
1297 for (InterfaceIpAddress ip : intf.ipAddressesList()) {
1298 Ip6Address relayAgentIp = ip.ipAddress().getIp6Address();
1299 if (relayAgentIp != null) {
1300 return relayAgentIp;
1301 }
1302 }
1303 }
1304 return null;
Yi Tseng51301292017-07-28 13:02:59 -07001305 }
Yi Tseng483ac6f2017-08-02 15:03:31 -07001306
1307 @Override
Kalhee Kimba366062017-11-07 16:32:09 +00001308 public void setDhcpFpmEnabled(Boolean enabled) {
1309 dhcpFpmEnabled = enabled;
1310 }
1311
1312 @Override
Yi Tseng483ac6f2017-08-02 15:03:31 -07001313 public void setDefaultDhcpServerConfigs(Collection<DhcpServerConfig> configs) {
Yi Tseng919b2df2017-09-07 16:22:51 -07001314 setDhcpServerConfigs(configs, defaultServerInfoList);
Yi Tseng919b2df2017-09-07 16:22:51 -07001315 }
1316
1317 @Override
1318 public void setIndirectDhcpServerConfigs(Collection<DhcpServerConfig> configs) {
1319 setDhcpServerConfigs(configs, indirectServerInfoList);
Yi Tseng919b2df2017-09-07 16:22:51 -07001320 }
1321
1322 public void setDhcpServerConfigs(Collection<DhcpServerConfig> configs, List<DhcpServerInfo> serverInfoList) {
Kalhee Kim45fede42017-09-05 19:05:06 +00001323 if (configs.size() == 0) {
1324 // no config to update
1325 return;
1326 }
1327
1328 // TODO: currently we pick up first DHCP server config.
1329 // Will use other server configs in the future for HA.
1330 DhcpServerConfig serverConfig = configs.iterator().next();
Yi Tseng919b2df2017-09-07 16:22:51 -07001331
Kalhee Kim45fede42017-09-05 19:05:06 +00001332 if (!serverConfig.getDhcpServerIp6().isPresent()) {
Yi Tseng919b2df2017-09-07 16:22:51 -07001333 // not a DHCPv6 config
Kalhee Kim45fede42017-09-05 19:05:06 +00001334 return;
1335 }
1336
Yi Tseng919b2df2017-09-07 16:22:51 -07001337 if (!serverInfoList.isEmpty()) {
1338 // remove old server info
1339 DhcpServerInfo oldServerInfo = serverInfoList.remove(0);
1340
1341 // stop monitoring gateway or server
1342 oldServerInfo.getDhcpGatewayIp6().ifPresent(gatewayIp -> {
1343 hostService.stopMonitoringIp(gatewayIp);
1344 });
1345 oldServerInfo.getDhcpServerIp6().ifPresent(serverIp -> {
1346 hostService.stopMonitoringIp(serverIp);
Yi Tseng525ff402017-10-23 19:39:39 -07001347 cancelDhcpPacket(serverIp);
Yi Tseng919b2df2017-09-07 16:22:51 -07001348 });
1349 }
1350
1351 // Create new server info according to the config
1352 DhcpServerInfo newServerInfo = new DhcpServerInfo(serverConfig,
1353 DhcpServerInfo.Version.DHCP_V6);
1354 checkState(newServerInfo.getDhcpServerConnectPoint().isPresent(),
1355 "Connect point not exists");
1356 checkState(newServerInfo.getDhcpServerIp6().isPresent(),
1357 "IP of DHCP server not exists");
1358
1359 log.debug("DHCP server connect point: {}", newServerInfo.getDhcpServerConnectPoint().orElse(null));
1360 log.debug("DHCP server IP: {}", newServerInfo.getDhcpServerIp6().orElse(null));
1361
Yi Tseng525ff402017-10-23 19:39:39 -07001362 Ip6Address serverIp = newServerInfo.getDhcpServerIp6().get();
1363 Ip6Address ipToProbe;
Yi Tseng919b2df2017-09-07 16:22:51 -07001364 if (newServerInfo.getDhcpGatewayIp6().isPresent()) {
1365 ipToProbe = newServerInfo.getDhcpGatewayIp6().get();
1366 } else {
1367 ipToProbe = newServerInfo.getDhcpServerIp6().orElse(null);
1368 }
1369 String hostToProbe = newServerInfo.getDhcpGatewayIp6()
1370 .map(ip -> "gateway").orElse("server");
1371
1372 log.debug("Probing to resolve {} IP {}", hostToProbe, ipToProbe);
Kalhee Kim45fede42017-09-05 19:05:06 +00001373 hostService.startMonitoringIp(ipToProbe);
1374
1375 Set<Host> hosts = hostService.getHostsByIp(ipToProbe);
1376 if (!hosts.isEmpty()) {
1377 Host host = hosts.iterator().next();
Yi Tseng919b2df2017-09-07 16:22:51 -07001378 newServerInfo.setDhcpConnectVlan(host.vlan());
1379 newServerInfo.setDhcpConnectMac(host.mac());
Kalhee Kim45fede42017-09-05 19:05:06 +00001380 }
Yi Tseng919b2df2017-09-07 16:22:51 -07001381 // Add new server info
Yi Tseng48cd0862017-11-09 13:54:12 -08001382 synchronized (this) {
1383 serverInfoList.clear();
1384 serverInfoList.add(0, newServerInfo);
1385 }
Yi Tseng525ff402017-10-23 19:39:39 -07001386 requestDhcpPacket(serverIp);
Kalhee Kim45fede42017-09-05 19:05:06 +00001387 }
1388
1389 class InternalHostListener implements HostListener {
1390 @Override
1391 public void event(HostEvent event) {
1392 switch (event.type()) {
1393 case HOST_ADDED:
1394 case HOST_UPDATED:
1395 hostUpdated(event.subject());
1396 break;
1397 case HOST_REMOVED:
1398 hostRemoved(event.subject());
1399 break;
Kalhee Kim45fede42017-09-05 19:05:06 +00001400 default:
1401 break;
1402 }
1403 }
1404 }
1405
1406 /**
Kalhee Kim45fede42017-09-05 19:05:06 +00001407 * Handle host updated.
1408 * If the host is DHCP server or gateway, update connect mac and vlan.
1409 *
1410 * @param host the host
1411 */
1412 private void hostUpdated(Host host) {
Yi Tseng525ff402017-10-23 19:39:39 -07001413 hostUpdated(host, defaultServerInfoList);
1414 hostUpdated(host, indirectServerInfoList);
Kalhee Kim45fede42017-09-05 19:05:06 +00001415 }
1416
Yi Tseng525ff402017-10-23 19:39:39 -07001417 private void hostUpdated(Host host, List<DhcpServerInfo> serverInfoList) {
1418 DhcpServerInfo serverInfo;
1419 Ip6Address targetIp;
1420 if (!serverInfoList.isEmpty()) {
1421 serverInfo = serverInfoList.get(0);
1422 Ip6Address serverIp = serverInfo.getDhcpServerIp6().orElse(null);
1423 targetIp = serverInfo.getDhcpGatewayIp6().orElse(null);
1424
1425 if (targetIp == null) {
1426 targetIp = serverIp;
1427 }
1428
1429 if (targetIp != null) {
1430 if (host.ipAddresses().contains(targetIp)) {
1431 serverInfo.setDhcpConnectMac(host.mac());
1432 serverInfo.setDhcpConnectVlan(host.vlan());
1433 requestDhcpPacket(serverIp);
1434 }
1435 }
1436 }
1437 }
1438
Kalhee Kim45fede42017-09-05 19:05:06 +00001439 /**
1440 * Handle host removed.
1441 * If the host is DHCP server or gateway, unset connect mac and vlan.
1442 *
1443 * @param host the host
1444 */
1445 private void hostRemoved(Host host) {
Yi Tseng525ff402017-10-23 19:39:39 -07001446 hostRemoved(host, defaultServerInfoList);
1447 hostRemoved(host, indirectServerInfoList);
Yi Tseng919b2df2017-09-07 16:22:51 -07001448 }
1449
Yi Tseng525ff402017-10-23 19:39:39 -07001450 private void hostRemoved(Host host, List<DhcpServerInfo> serverInfoList) {
1451 DhcpServerInfo serverInfo;
1452 Ip6Address targetIp;
1453
1454 if (!serverInfoList.isEmpty()) {
1455 serverInfo = serverInfoList.get(0);
1456 Ip6Address serverIp = serverInfo.getDhcpServerIp6().orElse(null);
1457 targetIp = serverInfo.getDhcpGatewayIp6().orElse(null);
1458
1459 if (targetIp == null) {
1460 targetIp = serverIp;
1461 }
1462
1463 if (targetIp != null) {
1464 if (host.ipAddresses().contains(targetIp)) {
1465 serverInfo.setDhcpConnectVlan(null);
1466 serverInfo.setDhcpConnectMac(null);
1467 cancelDhcpPacket(serverIp);
1468 }
1469 }
1470 }
1471 }
1472
Kalhee Kim45b24182017-10-18 18:30:23 +00001473 /**
1474 * Returns the first interface ip from interface.
1475 *
1476 * @param iface interface of one connect point
1477 * @return the first interface IP; null if not exists an IP address in
1478 * these interfaces
1479 */
1480 private Ip6Address getFirstIpFromInterface(Interface iface) {
1481 checkNotNull(iface, "Interface can't be null");
1482 return iface.ipAddressesList().stream()
1483 .map(InterfaceIpAddress::ipAddress)
1484 .filter(IpAddress::isIp6)
1485 .map(IpAddress::getIp6Address)
1486 .findFirst()
1487 .orElse(null);
1488 }
1489
1490 /**
1491 * Gets Interface facing to the server for default host.
1492 *
1493 * @return the Interface facing to the server; null if not found
1494 */
1495 private Interface getServerInterface() {
Yi Tseng25bfe372017-11-03 16:27:32 -07001496 DhcpServerInfo serverInfo;
1497 ConnectPoint dhcpServerConnectPoint;
1498 VlanId dhcpConnectVlan;
1499
1500 if (!defaultServerInfoList.isEmpty()) {
1501 serverInfo = defaultServerInfoList.get(0);
1502 dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1503 dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1504 } else {
Kalhee Kim45b24182017-10-18 18:30:23 +00001505 return null;
1506 }
Yi Tseng921e5b02017-11-17 17:14:41 -08001507 if (dhcpServerConnectPoint == null || dhcpConnectVlan == null) {
1508 log.info("Default DHCP server {} not resolve yet", serverInfo.getDhcpGatewayIp6());
1509 return null;
1510 }
Kalhee Kim45b24182017-10-18 18:30:23 +00001511 return interfaceService.getInterfacesByPort(dhcpServerConnectPoint)
1512 .stream()
1513 .filter(iface -> interfaceContainsVlan(iface, dhcpConnectVlan))
1514 .findFirst()
1515 .orElse(null);
1516 }
1517
1518 /**
1519 * Gets Interface facing to the server for indirect hosts.
1520 * Use default server Interface if indirect server not configured.
1521 *
1522 * @return the Interface facing to the server; null if not found
1523 */
1524 private Interface getIndirectServerInterface() {
Yi Tseng25bfe372017-11-03 16:27:32 -07001525 DhcpServerInfo serverInfo;
1526
1527 ConnectPoint indirectDhcpServerConnectPoint;
1528 VlanId indirectDhcpConnectVlan;
1529
1530 if (!indirectServerInfoList.isEmpty()) {
1531 serverInfo = indirectServerInfoList.get(0);
1532 indirectDhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1533 indirectDhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1534 } else {
Kalhee Kim45b24182017-10-18 18:30:23 +00001535 return getServerInterface();
1536 }
Yi Tseng921e5b02017-11-17 17:14:41 -08001537 if (indirectDhcpServerConnectPoint == null || indirectDhcpConnectVlan == null) {
1538 log.info("Indirect DHCP server {} not resolve yet", serverInfo.getDhcpGatewayIp6());
1539 return null;
1540 }
Kalhee Kim45b24182017-10-18 18:30:23 +00001541 return interfaceService.getInterfacesByPort(indirectDhcpServerConnectPoint)
1542 .stream()
1543 .filter(iface -> interfaceContainsVlan(iface, indirectDhcpConnectVlan))
1544 .findFirst()
1545 .orElse(null);
1546 }
1547
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +00001548 /**
1549 * Determind if an Interface contains a vlan id.
1550 *
1551 * @param iface the Interface
1552 * @param vlanId the vlan id
1553 * @return true if the Interface contains the vlan id
1554 */
1555 private boolean interfaceContainsVlan(Interface iface, VlanId vlanId) {
1556 if (vlanId.equals(VlanId.NONE)) {
1557 // untagged packet, check if vlan untagged or vlan native is not NONE
1558 return !iface.vlanUntagged().equals(VlanId.NONE) ||
1559 !iface.vlanNative().equals(VlanId.NONE);
1560 }
1561 // tagged packet, check if the interface contains the vlan
1562 return iface.vlanTagged().contains(vlanId);
1563 }
1564
Yi Tseng525ff402017-10-23 19:39:39 -07001565 private void requestDhcpPacket(Ip6Address serverIp) {
1566 requestServerDhcpPacket(serverIp);
1567 requestClientDhcpPacket(serverIp);
1568 }
1569
1570 private void cancelDhcpPacket(Ip6Address serverIp) {
1571 cancelServerDhcpPacket(serverIp);
1572 cancelClientDhcpPacket(serverIp);
1573 }
1574
1575 private void cancelServerDhcpPacket(Ip6Address serverIp) {
1576 TrafficSelector serverSelector =
1577 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1578 .matchIPv6Src(serverIp.toIpPrefix())
1579 .build();
1580 packetService.cancelPackets(serverSelector,
1581 PacketPriority.CONTROL,
1582 appId);
1583 }
1584
1585 private void requestServerDhcpPacket(Ip6Address serverIp) {
1586 TrafficSelector serverSelector =
1587 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1588 .matchIPv6Src(serverIp.toIpPrefix())
1589 .build();
1590 packetService.requestPackets(serverSelector,
1591 PacketPriority.CONTROL,
1592 appId);
1593 }
1594
1595 private void cancelClientDhcpPacket(Ip6Address serverIp) {
1596 // Packet comes from relay
1597 TrafficSelector indirectClientSelector =
1598 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1599 .matchIPv6Dst(serverIp.toIpPrefix())
1600 .build();
1601 packetService.cancelPackets(indirectClientSelector,
1602 PacketPriority.CONTROL,
1603 appId);
Yi Tseng41dde702017-11-02 15:21:10 -07001604 indirectClientSelector =
1605 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1606 .matchIPv6Dst(Ip6Address.ALL_DHCP_RELAY_AGENTS_AND_SERVERS.toIpPrefix())
1607 .build();
1608 packetService.cancelPackets(indirectClientSelector,
1609 PacketPriority.CONTROL,
1610 appId);
1611 indirectClientSelector =
1612 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1613 .matchIPv6Dst(Ip6Address.ALL_DHCP_SERVERS.toIpPrefix())
1614 .build();
1615 packetService.cancelPackets(indirectClientSelector,
1616 PacketPriority.CONTROL,
1617 appId);
Yi Tseng525ff402017-10-23 19:39:39 -07001618
1619 // Packet comes from client
1620 packetService.cancelPackets(CLIENT_SERVER_SELECTOR,
1621 PacketPriority.CONTROL,
1622 appId);
1623 }
1624
1625 private void requestClientDhcpPacket(Ip6Address serverIp) {
1626 // Packet comes from relay
1627 TrafficSelector indirectClientSelector =
1628 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1629 .matchIPv6Dst(serverIp.toIpPrefix())
1630 .build();
1631 packetService.requestPackets(indirectClientSelector,
1632 PacketPriority.CONTROL,
1633 appId);
Yi Tseng41dde702017-11-02 15:21:10 -07001634 indirectClientSelector =
1635 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1636 .matchIPv6Dst(Ip6Address.ALL_DHCP_RELAY_AGENTS_AND_SERVERS.toIpPrefix())
1637 .build();
1638 packetService.requestPackets(indirectClientSelector,
1639 PacketPriority.CONTROL,
1640 appId);
1641 indirectClientSelector =
1642 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1643 .matchIPv6Dst(Ip6Address.ALL_DHCP_SERVERS.toIpPrefix())
1644 .build();
1645 packetService.requestPackets(indirectClientSelector,
1646 PacketPriority.CONTROL,
1647 appId);
Yi Tseng525ff402017-10-23 19:39:39 -07001648
1649 // Packet comes from client
1650 packetService.requestPackets(CLIENT_SERVER_SELECTOR,
1651 PacketPriority.CONTROL,
1652 appId);
1653 }
1654
1655 /**
1656 * Process the ignore rules.
1657 *
1658 * @param deviceId the device id
1659 * @param vlanId the vlan to be ignored
1660 * @param op the operation, ADD to install; REMOVE to uninstall rules
1661 */
1662 private void processIgnoreVlanRule(DeviceId deviceId, VlanId vlanId, Objective.Operation op) {
Yi Tseng525ff402017-10-23 19:39:39 -07001663 AtomicInteger installedCount = new AtomicInteger(DHCP_SELECTORS.size());
1664 DHCP_SELECTORS.forEach(trafficSelector -> {
1665 TrafficSelector selector = DefaultTrafficSelector.builder(trafficSelector)
1666 .matchVlanId(vlanId)
1667 .build();
1668
1669 ForwardingObjective.Builder builder = DefaultForwardingObjective.builder()
1670 .withFlag(ForwardingObjective.Flag.VERSATILE)
1671 .withSelector(selector)
1672 .withPriority(IGNORE_CONTROL_PRIORITY)
Yi Tsengdbabeed2017-11-29 10:49:20 -08001673 .withTreatment(DefaultTrafficTreatment.emptyTreatment())
Yi Tseng525ff402017-10-23 19:39:39 -07001674 .fromApp(appId);
1675
1676
1677 ObjectiveContext objectiveContext = new ObjectiveContext() {
1678 @Override
1679 public void onSuccess(Objective objective) {
1680 log.info("Ignore rule {} (Vlan id {}, device {}, selector {})",
1681 op, vlanId, deviceId, selector);
1682 int countDown = installedCount.decrementAndGet();
1683 if (countDown != 0) {
1684 return;
1685 }
1686 switch (op) {
1687 case ADD:
1688 ignoredVlans.put(deviceId, vlanId);
1689 break;
1690 case REMOVE:
1691 ignoredVlans.remove(deviceId, vlanId);
1692 break;
1693 default:
1694 log.warn("Unsupported objective operation {}", op);
1695 break;
1696 }
1697 }
1698
1699 @Override
1700 public void onError(Objective objective, ObjectiveError error) {
1701 log.warn("Can't {} ignore rule (vlan id {}, selector {}, device {}) due to {}",
1702 op, vlanId, selector, deviceId, error);
1703 }
1704 };
1705
1706 ForwardingObjective fwd;
1707 switch (op) {
1708 case ADD:
1709 fwd = builder.add(objectiveContext);
1710 break;
1711 case REMOVE:
1712 fwd = builder.remove(objectiveContext);
1713 break;
1714 default:
1715 log.warn("Unsupported objective operation {}", op);
1716 return;
1717 }
1718
1719 Device device = deviceService.getDevice(deviceId);
1720 if (device == null || !device.is(Pipeliner.class)) {
1721 log.warn("Device {} is not available now, wait until device is available", deviceId);
1722 return;
1723 }
1724 flowObjectiveService.apply(deviceId, fwd);
1725 });
1726 }
Kalhee Kim121ba922017-11-01 17:56:44 +00001727
1728 /**
1729 * Find first ipaddress for a given Host info i.e. mac and vlan.
1730 *
1731 * @param clientMac client mac
1732 * @param vlanId packet's vlan
1733 * @return next-hop link-local ipaddress for a given host
1734 */
1735 private IpAddress getFirstIpByHost(MacAddress clientMac, VlanId vlanId) {
1736 IpAddress nextHopIp;
1737 // pick out the first link-local ip address
1738 HostId gwHostId = HostId.hostId(clientMac, vlanId);
1739 Host gwHost = hostService.getHost(gwHostId);
1740 if (gwHost == null) {
1741 log.warn("Can't find gateway host for hostId {}", gwHostId);
1742 return null;
1743 }
1744 nextHopIp = gwHost.ipAddresses()
1745 .stream()
1746 .filter(IpAddress::isIp6)
Yi Tseng68ef26b2017-12-18 17:10:00 -08001747 .filter(IpAddress::isLinkLocal)
Kalhee Kim121ba922017-11-01 17:56:44 +00001748 .map(IpAddress::getIp6Address)
1749 .findFirst()
1750 .orElse(null);
1751 return nextHopIp;
1752 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +00001753
Yi Tseng51301292017-07-28 13:02:59 -07001754}