blob: 8d817274b05b541d4328d91b404db78d6b28ccd3 [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 Tseng2fe8f3f2017-09-07 16:22:51 -070020import com.google.common.collect.Lists;
Kalhee Kim1b5094f2017-09-05 19:05:06 +000021import org.apache.felix.scr.annotations.Activate;
22import org.apache.felix.scr.annotations.Deactivate;
Kalhee Kim1b5094f2017-09-05 19:05:06 +000023import com.google.common.collect.Sets;
24import com.google.common.collect.ImmutableSet;
Yi Tseng51301292017-07-28 13:02:59 -070025import org.apache.felix.scr.annotations.Component;
26import org.apache.felix.scr.annotations.Property;
Kalhee Kim1b5094f2017-09-05 19:05:06 +000027import org.apache.felix.scr.annotations.Reference;
28import org.apache.felix.scr.annotations.ReferenceCardinality;
Yi Tseng51301292017-07-28 13:02:59 -070029import org.apache.felix.scr.annotations.Service;
30import org.onlab.packet.BasePacket;
Kalhee Kim1b5094f2017-09-05 19:05:06 +000031import org.onlab.packet.DHCP6;
32import org.onlab.packet.IPv6;
33import org.onlab.packet.Ethernet;
34import org.onlab.packet.Ip6Address;
Yi Tseng51301292017-07-28 13:02:59 -070035import org.onlab.packet.IpAddress;
Kalhee Kim1b5094f2017-09-05 19:05:06 +000036import org.onlab.packet.IpPrefix;
Yi Tseng51301292017-07-28 13:02:59 -070037import org.onlab.packet.MacAddress;
Kalhee Kim1b5094f2017-09-05 19:05:06 +000038import org.onlab.packet.UDP;
Yi Tseng51301292017-07-28 13:02:59 -070039import org.onlab.packet.VlanId;
Kalhee Kim1b5094f2017-09-05 19:05:06 +000040import org.onlab.packet.dhcp.Dhcp6RelayOption;
41import org.onlab.packet.dhcp.Dhcp6InterfaceIdOption;
42import org.onlab.packet.dhcp.Dhcp6Option;
43import org.onlab.packet.dhcp.Dhcp6IaNaOption;
44import org.onlab.packet.dhcp.Dhcp6IaTaOption;
45import org.onlab.packet.dhcp.Dhcp6IaPdOption;
46import org.onlab.packet.dhcp.Dhcp6IaAddressOption;
47import org.onlab.packet.dhcp.Dhcp6IaPrefixOption;
48import org.onlab.util.HexString;
Yi Tseng51301292017-07-28 13:02:59 -070049import org.onosproject.dhcprelay.api.DhcpHandler;
Yi Tseng2fe8f3f2017-09-07 16:22:51 -070050import org.onosproject.dhcprelay.api.DhcpServerInfo;
Kalhee Kim1b5094f2017-09-05 19:05:06 +000051import org.onosproject.dhcprelay.store.DhcpRelayStore;
Yi Tseng4b013202017-09-08 17:22:51 -070052import org.onosproject.net.host.HostProvider;
53import org.onosproject.net.host.HostProviderRegistry;
54import org.onosproject.net.host.HostProviderService;
Kalhee Kim1b5094f2017-09-05 19:05:06 +000055import org.onosproject.net.host.HostService;
56import org.onosproject.net.host.DefaultHostDescription;
57import org.onosproject.net.host.HostDescription;
58import org.onosproject.net.host.InterfaceIpAddress;
59import org.onosproject.net.host.HostListener;
60import org.onosproject.net.host.HostEvent;
61import org.onosproject.net.intf.Interface;
62import org.onosproject.net.intf.InterfaceService;
Yi Tseng4b013202017-09-08 17:22:51 -070063import org.onosproject.net.provider.ProviderId;
Kalhee Kim1b5094f2017-09-05 19:05:06 +000064import org.onosproject.routeservice.Route;
65import org.onosproject.routeservice.RouteStore;
Yi Tsenge72fbb52017-08-02 15:03:31 -070066import org.onosproject.dhcprelay.config.DhcpServerConfig;
Yi Tseng51301292017-07-28 13:02:59 -070067import org.onosproject.net.ConnectPoint;
Kalhee Kim1b5094f2017-09-05 19:05:06 +000068import org.onosproject.net.Host;
69import org.onosproject.net.HostId;
70import org.onosproject.net.HostLocation;
71import org.onosproject.net.packet.DefaultOutboundPacket;
72import org.onosproject.net.packet.OutboundPacket;
Yi Tseng51301292017-07-28 13:02:59 -070073import org.onosproject.net.packet.PacketContext;
Kalhee Kim1b5094f2017-09-05 19:05:06 +000074import org.onosproject.net.packet.PacketService;
75import org.slf4j.Logger;
76import org.slf4j.LoggerFactory;
77import org.onosproject.net.flow.DefaultTrafficTreatment;
78import org.onosproject.net.flow.TrafficTreatment;
Yi Tseng51301292017-07-28 13:02:59 -070079
Kalhee Kim1b5094f2017-09-05 19:05:06 +000080
81import java.nio.ByteBuffer;
82import java.util.List;
Yi Tsenge72fbb52017-08-02 15:03:31 -070083import java.util.Collection;
Yi Tseng51301292017-07-28 13:02:59 -070084import java.util.Optional;
Kalhee Kim1b5094f2017-09-05 19:05:06 +000085import java.util.Set;
86import java.util.ArrayList;
Yi Tseng2fe8f3f2017-09-07 16:22:51 -070087import java.util.stream.Collectors;
Kalhee Kim1b5094f2017-09-05 19:05:06 +000088
89
90import static com.google.common.base.Preconditions.checkNotNull;
91import static com.google.common.base.Preconditions.checkState;
Yi Tseng51301292017-07-28 13:02:59 -070092
93@Component
94@Service
95@Property(name = "version", value = "6")
Yi Tseng4b013202017-09-08 17:22:51 -070096public class Dhcp6HandlerImpl implements DhcpHandler, HostProvider {
Charles Chan75edab72017-09-12 17:09:32 -070097 public static final String DHCP_V6_RELAY_APP = "org.onosproject.Dhcp6HandlerImpl";
Saurav Das37415cc2017-09-13 14:35:56 -070098 public static final ProviderId PROVIDER_ID = new ProviderId("dhcp6", DHCP_V6_RELAY_APP);
Kalhee Kim1b5094f2017-09-05 19:05:06 +000099 private static Logger log = LoggerFactory.getLogger(Dhcp6HandlerImpl.class);
Yi Tseng51301292017-07-28 13:02:59 -0700100
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000101 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
102 protected DhcpRelayStore dhcpRelayStore;
Yi Tseng51301292017-07-28 13:02:59 -0700103
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000104 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
105 protected PacketService packetService;
106
107 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000108 protected RouteStore routeStore;
109
110 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
111 protected InterfaceService interfaceService;
112
113 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
114 protected HostService hostService;
115
Yi Tseng4b013202017-09-08 17:22:51 -0700116 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
117 protected HostProviderRegistry providerRegistry;
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000118
Yi Tseng4b013202017-09-08 17:22:51 -0700119 private InternalHostListener hostListener = new InternalHostListener();
120 protected HostProviderService providerService;
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000121 private Ip6Address dhcpServerIp = null;
122 // dhcp server may be connected directly to the SDN network or
123 // via an external gateway. When connected directly, the dhcpConnectPoint, dhcpConnectMac,
124 // and dhcpConnectVlan refer to the server. When connected via the gateway, they refer
125 // to the gateway.
126 private ConnectPoint dhcpServerConnectPoint = null;
127 private MacAddress dhcpConnectMac = null;
128 private VlanId dhcpConnectVlan = null;
129 private Ip6Address dhcpGatewayIp = null;
130 private Ip6Address relayAgentIpFromCfg = null;
131
132 private Ip6Address indirectDhcpServerIp = null;
133 private ConnectPoint indirectDhcpServerConnectPoint = null;
134 private MacAddress indirectDhcpConnectMac = null;
135 private VlanId indirectDhcpConnectVlan = null;
136 private Ip6Address indirectDhcpGatewayIp = null;
137 private Ip6Address indirectRelayAgentIpFromCfg = null;
138
Yi Tseng2fe8f3f2017-09-07 16:22:51 -0700139 private List<DhcpServerInfo> defaultServerInfoList = Lists.newArrayList();
140 private List<DhcpServerInfo> indirectServerInfoList = Lists.newArrayList();
141
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000142
143 // CLIENT message types
144 public static final Set<Byte> MSG_TYPE_FROM_CLIENT =
145 ImmutableSet.of(DHCP6.MsgType.SOLICIT.value(),
146 DHCP6.MsgType.REQUEST.value(),
147 DHCP6.MsgType.REBIND.value(),
148 DHCP6.MsgType.RENEW.value(),
149 DHCP6.MsgType.RELEASE.value(),
150 DHCP6.MsgType.DECLINE.value(),
151 DHCP6.MsgType.CONFIRM.value(),
152 DHCP6.MsgType.RELAY_FORW.value());
153 // SERVER message types
154 public static final Set<Byte> MSG_TYPE_FROM_SERVER =
155 ImmutableSet.of(DHCP6.MsgType.RELAY_REPL.value());
156
157 @Activate
158 protected void activate() {
Yi Tseng4b013202017-09-08 17:22:51 -0700159 providerService = providerRegistry.register(this);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000160 hostService.addListener(hostListener);
Yi Tseng51301292017-07-28 13:02:59 -0700161 }
162
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000163 @Deactivate
164 protected void deactivate() {
Yi Tseng4b013202017-09-08 17:22:51 -0700165 providerRegistry.unregister(this);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000166 hostService.removeListener(hostListener);
Yi Tseng2fe8f3f2017-09-07 16:22:51 -0700167 defaultServerInfoList.forEach(this::stopMonitoringIps);
168 defaultServerInfoList.clear();
169 indirectServerInfoList.forEach(this::stopMonitoringIps);
170 indirectServerInfoList.clear();
171 }
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000172
Yi Tseng2fe8f3f2017-09-07 16:22:51 -0700173 private void stopMonitoringIps(DhcpServerInfo serverInfo) {
174 serverInfo.getDhcpGatewayIp6().ifPresent(gatewayIp -> {
175 hostService.stopMonitoringIp(gatewayIp);
176 });
177 serverInfo.getDhcpServerIp6().ifPresent(serverIp -> {
178 hostService.stopMonitoringIp(serverIp);
179 });
Yi Tseng51301292017-07-28 13:02:59 -0700180 }
181
Yi Tseng51301292017-07-28 13:02:59 -0700182 @Override
Yi Tseng2fe8f3f2017-09-07 16:22:51 -0700183 public List<DhcpServerInfo> getDefaultDhcpServerInfoList() {
184 return defaultServerInfoList;
Yi Tseng51301292017-07-28 13:02:59 -0700185 }
186
187 @Override
Yi Tseng2fe8f3f2017-09-07 16:22:51 -0700188 public List<DhcpServerInfo> getIndirectDhcpServerInfoList() {
189 return indirectServerInfoList;
Yi Tseng51301292017-07-28 13:02:59 -0700190 }
191
192 @Override
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000193 public void processDhcpPacket(PacketContext context, BasePacket payload) {
194 checkNotNull(payload, "DHCP6 payload can't be null");
195 checkState(payload instanceof DHCP6, "Payload is not a DHCP6");
196 DHCP6 dhcp6Payload = (DHCP6) payload;
197 Ethernet receivedPacket = context.inPacket().parsed();
198
199 if (!configured()) {
200 log.warn("Missing DHCP6 relay server config. Abort packet processing");
201 log.warn("dhcp6 payload {}", dhcp6Payload);
202
203 return;
204 }
205
206 byte msgType = dhcp6Payload.getMsgType();
207 log.warn("msgType is {}", msgType);
208
209 ConnectPoint inPort = context.inPacket().receivedFrom();
210 if (inPort == null) {
211 log.warn("incommin ConnectPoint is null");
212 }
213 Set<Interface> receivingInterfaces = interfaceService.getInterfacesByPort(inPort);
214 //ignore the packets if dhcp client interface is not configured on onos.
215 if (receivingInterfaces.isEmpty()) {
216 log.warn("Virtual interface is not configured on {}", inPort);
217 return;
218 }
219
220
221 if (MSG_TYPE_FROM_CLIENT.contains(msgType)) {
222
223 InternalPacket ethernetClientPacket =
224 processDhcp6PacketFromClient(context, receivedPacket, receivingInterfaces);
225 if (ethernetClientPacket != null) {
226 forwardPacket(ethernetClientPacket);
227 }
228
229 } else if (MSG_TYPE_FROM_SERVER.contains(msgType)) {
230 log.warn("calling processDhcp6PacketFromServer with RELAY_REPL", msgType);
231 InternalPacket ethernetPacketReply =
232 processDhcp6PacketFromServer(context, receivedPacket, receivingInterfaces);
233 if (ethernetPacketReply != null) {
234 forwardPacket(ethernetPacketReply);
235 }
236 } else {
237 log.warn("Not so fast, packet type {} not supported yet", msgType);
238 }
239 }
240
241
242 /**
243 * Checks if this app has been configured.
244 *
245 * @return true if all information we need have been initialized
246 */
247 public boolean configured() {
Yi Tseng2fe8f3f2017-09-07 16:22:51 -0700248 return !defaultServerInfoList.isEmpty();
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000249 }
250
Yi Tseng4b013202017-09-08 17:22:51 -0700251 @Override
252 public ProviderId id() {
Charles Chan75edab72017-09-12 17:09:32 -0700253 return PROVIDER_ID;
Yi Tseng4b013202017-09-08 17:22:51 -0700254 }
255
256 @Override
257 public void triggerProbe(Host host) {
258 // Do nothing here
259 }
260
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000261 // the new class the contains Ethernet packet and destination port, kind of like adding
262 // internal header to the packet
263 private class InternalPacket {
264 Ethernet packet;
265 ConnectPoint destLocation;
266 public InternalPacket(Ethernet newPacket, ConnectPoint newLocation) {
267 packet = newPacket;
268 destLocation = newLocation;
269 }
270 void setLocation(ConnectPoint newLocation) {
271 destLocation = newLocation;
272 }
273 }
274
275 //forward the packet to ConnectPoint where the DHCP server is attached.
276 private void forwardPacket(InternalPacket packet) {
277 //send Packetout to dhcp server connectpoint.
278 if (packet.destLocation != null) {
279 TrafficTreatment t = DefaultTrafficTreatment.builder()
280 .setOutput(packet.destLocation.port()).build();
281 OutboundPacket o = new DefaultOutboundPacket(
282 packet.destLocation.deviceId(), t, ByteBuffer.wrap(packet.packet.serialize()));
283 if (log.isTraceEnabled()) {
284 log.trace("Relaying packet to destination {}", packet.destLocation);
285 }
286 packetService.emit(o);
287 } // if
288 }
289
290 /**
291 * Check if the host is directly connected to the network or not.
292 *
293 * @param dhcp6Payload the dhcp6 payload
294 * @return true if the host is directly connected to the network; false otherwise
295 */
296 private boolean directlyConnected(DHCP6 dhcp6Payload) {
297 log.debug("directlyConnected enters");
298
299 if (dhcp6Payload.getMsgType() != DHCP6.MsgType.RELAY_FORW.value() &&
300 dhcp6Payload.getMsgType() != DHCP6.MsgType.RELAY_REPL.value()) {
301 log.debug("directlyConnected true. MsgType {}", dhcp6Payload.getMsgType());
302
303 return true;
304 }
305
306 // Regardless of relay-forward or relay-replay, check if we see another relay message
307 DHCP6 dhcp6Payload2 = dhcp6PacketFromRelayPacket(dhcp6Payload);
308 if (dhcp6Payload2 != null) {
309 if (dhcp6Payload.getMsgType() == DHCP6.MsgType.RELAY_FORW.value()) {
310 log.debug("directlyConnected false. 1st realy-foward, 2nd MsgType {}", dhcp6Payload2.getMsgType());
311 return false;
312 } else {
313 // relay-reply
314 if (dhcp6Payload2.getMsgType() != DHCP6.MsgType.RELAY_REPL.value()) {
315 log.debug("directlyConnected true. 2nd MsgType {}", dhcp6Payload2.getMsgType());
316 return true; // must be directly connected
317 } else {
318 log.debug("directlyConnected false. 1st relay-reply, 2nd relay-reply MsgType {}",
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000319 dhcp6Payload2.getMsgType());
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000320 return false; // must be indirectly connected
321 }
322 }
323 } else {
324 log.warn("directlyConnected true.");
325 return true;
326 }
327 }
328
329 /**
330 * extract DHCP6 payload from dhcp6 relay message within relay-forwrd/reply.
331 *
332 * @param dhcp6 dhcp6 relay-reply or relay-foward
333 * @return dhcp6Packet dhcp6 packet extracted from relay-message
334 */
335 private DHCP6 dhcp6PacketFromRelayPacket(DHCP6 dhcp6) {
336 log.debug("dhcp6PacketFromRelayPacket enters. dhcp6 {}", dhcp6);
337
338 // extract the relay message if exist
339 DHCP6 dhcp6Payload = dhcp6.getOptions().stream()
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000340 .filter(opt -> opt instanceof Dhcp6RelayOption)
341 .map(BasePacket::getPayload)
342 .map(pld -> (DHCP6) pld)
343 .findFirst()
344 .orElse(null);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000345
346
347 if (dhcp6Payload == null) {
348 // Can't find dhcp payload
349 log.debug("Can't find dhcp6 payload from relay message");
350 } else {
351 log.debug("dhcp6 payload found from relay message {}", dhcp6Payload);
352 }
353
354 return dhcp6Payload;
355 }
356
357 /**
358 * find the leaf DHCP6 packet from multi-level relay packet.
359 *
360 * @param relayPacket dhcp6 relay packet
361 * @return leafPacket non-relay dhcp6 packet
362 */
363 private DHCP6 getDhcp6Leaf(DHCP6 relayPacket) {
364 DHCP6 dhcp6Parent = relayPacket;
365 DHCP6 dhcp6Child = null;
366
367 log.debug("getDhcp6Leaf entered.");
368 while (dhcp6Parent != null) {
369 dhcp6Child = dhcp6PacketFromRelayPacket(dhcp6Parent);
370
371 if (dhcp6Child != null) {
372 if (dhcp6Child.getMsgType() != DHCP6.MsgType.RELAY_FORW.value() &&
373 dhcp6Child.getMsgType() != DHCP6.MsgType.RELAY_REPL.value()) {
374 log.debug("leaf dhcp6 packet found.");
375 break;
376 } else {
377 // found another relay
378 // go for another loop
379 dhcp6Parent = dhcp6Child;
380 }
381 } else {
382 log.warn("malformed pkt! Expected dhcp6 within relay pkt, but no dhcp6 leaf found.");
383 break;
384 }
385 }
386 return dhcp6Child;
387 }
388
389 /**
390 * check if DHCP6 relay-reply is reply.
391 *
392 * @param relayPacket dhcp6 relay-reply
393 * @return boolean relay-reply contains ack
394 */
395 private boolean isDhcp6Reply(DHCP6 relayPacket) {
396 log.debug("isDhcp6Reply entered.");
397
398 DHCP6 leafDhcp6 = getDhcp6Leaf(relayPacket);
399
400 if (leafDhcp6 != null) {
401 if (leafDhcp6.getMsgType() == DHCP6.MsgType.REPLY.value()) {
402 log.debug("isDhcp6Reply true.");
403 return true; // must be directly connected
404 } else {
405 log.debug("isDhcp6Reply false. leaf dhcp6 is not replay. MsgType {}", leafDhcp6.getMsgType());
406 }
407 } else {
408 log.debug("isDhcp6Reply false. Expected dhcp6 within relay pkt but not found.");
409 }
410 log.debug("isDhcp6Reply false.");
411 return false;
412 }
413
414 /**
415 * check if DHCP6 is release or relay-forward contains release.
416 *
417 * @param dhcp6Payload dhcp6 packet
418 * @return boolean dhcp6 contains release
419 */
420 private boolean isDhcp6Release(DHCP6 dhcp6Payload) {
421
422 log.debug("isDhcp6Release entered.");
423
424 if (dhcp6Payload.getMsgType() == DHCP6.MsgType.RELEASE.value()) {
425 log.debug("isDhcp6Release true.");
426 return true; // must be directly connected
427 } else {
428 DHCP6 dhcp6Leaf = getDhcp6Leaf(dhcp6Payload);
429 if (dhcp6Leaf != null) {
430 if (dhcp6Leaf.getMsgType() == DHCP6.MsgType.RELEASE.value()) {
431 log.debug("isDhcp6Release true. indirectlry connected");
432 return true;
433 } else {
434 log.debug("leaf dhcp6 is not release. MsgType {}", dhcp6Leaf.getMsgType());
435 return false;
436 }
437 } else {
438 log.debug("isDhcp6Release false. dhcp6 is niether relay nor release.");
439 return false;
440 }
441 }
442 }
443
444 /**
445 * extract from dhcp6 packet client ipv6 address of given by dhcp server.
446 *
447 * @param dhcp6 the dhcp6 packet
448 * @return Ip6Address Ip6Address given by dhcp server, or null if not exists
449 */
450 private Ip6Address extractIpAddress(DHCP6 dhcp6) {
451 Ip6Address ip = null;
452
453 log.debug("extractIpAddress enters dhcp6 {}.", dhcp6);
454 // Extract IPv6 address from IA NA ot IA TA option
455 Optional<Dhcp6IaNaOption> iaNaOption = dhcp6.getOptions()
456 .stream()
457 .filter(opt -> opt instanceof Dhcp6IaNaOption)
458 .map(opt -> (Dhcp6IaNaOption) opt)
459 .findFirst();
460 Optional<Dhcp6IaTaOption> iaTaOption = dhcp6.getOptions()
461 .stream()
462 .filter(opt -> opt instanceof Dhcp6IaTaOption)
463 .map(opt -> (Dhcp6IaTaOption) opt)
464 .findFirst();
465 Optional<Dhcp6IaAddressOption> iaAddressOption;
466 if (iaNaOption.isPresent()) {
467 log.debug("Found IPv6 address from iaNaOption {}", iaNaOption);
468
469 iaAddressOption = iaNaOption.get().getOptions().stream()
470 .filter(opt -> opt instanceof Dhcp6IaAddressOption)
471 .map(opt -> (Dhcp6IaAddressOption) opt)
472 .findFirst();
473 } else if (iaTaOption.isPresent()) {
474 log.debug("Found IPv6 address from iaTaOption {}", iaTaOption);
475
476 iaAddressOption = iaTaOption.get().getOptions().stream()
477 .filter(opt -> opt instanceof Dhcp6IaAddressOption)
478 .map(opt -> (Dhcp6IaAddressOption) opt)
479 .findFirst();
480 } else {
481 iaAddressOption = Optional.empty();
482 }
483 if (iaAddressOption.isPresent()) {
484 ip = iaAddressOption.get().getIp6Address();
485 log.debug("Found IPv6 address from iaAddressOption {}", iaAddressOption);
486
487
488 } else {
489 log.debug("Can't find IPv6 address from DHCPv6 {}", dhcp6);
490 }
491
492 return ip;
493 }
494 /**
495 * extract from dhcp6 packet Prefix prefix provided by dhcp server.
496 *
497 * @param dhcp6 the dhcp6 payload
498 * @return IpPrefix Prefix Delegation prefix, or null if not exists.
499 */
500 private IpPrefix extractPrefix(DHCP6 dhcp6) {
501 log.warn("extractPrefix enters {}", dhcp6);
502
503 // extract prefix
504 IpPrefix prefixPrefix = null;
505
506 Ip6Address prefixAddress = null;
507
508 // Extract IPv6 prefix from IA PD option
509 Optional<Dhcp6IaPdOption> iaPdOption = dhcp6.getOptions()
510 .stream()
511 .filter(opt -> opt instanceof Dhcp6IaPdOption)
512 .map(opt -> (Dhcp6IaPdOption) opt)
513 .findFirst();
514
515 Optional<Dhcp6IaPrefixOption> iaPrefixOption;
516 if (iaPdOption.isPresent()) {
517 log.warn("IA_PD option found {}", iaPdOption);
518
519 iaPrefixOption = iaPdOption.get().getOptions().stream()
520 .filter(opt -> opt instanceof Dhcp6IaPrefixOption)
521 .map(opt -> (Dhcp6IaPrefixOption) opt)
522 .findFirst();
523 } else {
524 log.warn("IA_PD option NOT found");
525
526 iaPrefixOption = Optional.empty();
527 }
528 if (iaPrefixOption.isPresent()) {
529 log.warn("IAPrefix Option within IA_PD option found {}", iaPrefixOption);
530
531 prefixAddress = iaPrefixOption.get().getIp6Prefix();
532 int prefixLen = (int) iaPrefixOption.get().getPrefixLength();
533 log.warn("Prefix length is {} bits", prefixLen);
534 prefixPrefix = IpPrefix.valueOf(prefixAddress, prefixLen);
535
536 } else {
537 log.warn("Can't find IPv6 prefix from DHCPv6 {}", dhcp6);
538 }
539
540 return prefixPrefix;
541 }
542
543 /**
544 * remove host or route.
545 *
546 * @param directConnFlag flag to show that packet is from directly connected client
547 * @param dhcp6Packet the dhcp6 payload
548 * @param clientPacket client's ethernet packet
549 * @param clientIpv6 client's Ipv6 packet
Kalhee Kimaa5172a2017-09-15 17:43:27 +0000550 * @param clientInterface client interfaces
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000551 */
552 private void removeHostOrRoute(boolean directConnFlag, DHCP6 dhcp6Packet,
553 Ethernet clientPacket, IPv6 clientIpv6,
Kalhee Kimaa5172a2017-09-15 17:43:27 +0000554 Interface clientInterface) {
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000555 log.debug("extractPrefix enters {}", dhcp6Packet);
556 // add host or route
557 if (isDhcp6Release(dhcp6Packet)) {
558 IpAddress ip = null;
559 if (directConnFlag) {
560 // Add to host store if it is connected to network directly
561 ip = extractIpAddress(dhcp6Packet);
562 if (ip != null) {
Kalhee Kimaa5172a2017-09-15 17:43:27 +0000563 VlanId vlanId = clientInterface.vlan();
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000564 MacAddress clientMac = clientPacket.getSourceMAC();
565 HostId hostId = HostId.hostId(clientMac, vlanId);
566 log.debug("remove Host {} ip for directly connected.", hostId.toString());
567
568 log.debug("client mac {} client vlan {}", HexString.toHexString(clientMac.toBytes(), ":"), vlanId);
569
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000570 // Remove host's ip of when dhcp release msg is received
Yi Tseng4b013202017-09-08 17:22:51 -0700571 providerService.removeIpFromHost(hostId, ip);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000572 } else {
573 log.debug("ipAddress not found. Do not add Host for directly connected.");
574 }
575 } else {
576 // Remove from route store if it is not connected to network directly
577 IpAddress nextHopIp = IpAddress.valueOf(IpAddress.Version.INET6, clientIpv6.getSourceAddress());
578
579 DHCP6 leafDhcp = getDhcp6Leaf(dhcp6Packet);
580 ip = extractIpAddress(leafDhcp);
581 if (ip == null) {
582 log.debug("ip is null");
583 } else {
584 Route routeForIP = new Route(Route.Source.STATIC, ip.toIpPrefix(), nextHopIp);
585 log.debug("removing route of 128 address for indirectly connected.");
586 log.debug("128 ip {}, nexthop {}", HexString.toHexString(ip.toOctets(), ":"),
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000587 HexString.toHexString(nextHopIp.toOctets(), ":"));
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000588 routeStore.removeRoute(routeForIP);
589 }
590
591 IpPrefix ipPrefix = extractPrefix(leafDhcp);
592 if (ipPrefix == null) {
593 log.debug("ipPrefix is null ");
594 } else {
595 Route routeForPrefix = new Route(Route.Source.STATIC, ipPrefix, nextHopIp);
596 log.debug("removing route of PD for indirectly connected.");
597 log.debug("pd ip {}, nexthop {}", HexString.toHexString(ipPrefix.address().toOctets(), ":"),
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000598 HexString.toHexString(nextHopIp.toOctets(), ":"));
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000599
600 routeStore.removeRoute(routeForPrefix);
601 }
602 }
603 }
604 }
605
606 /**
607 * add host or route.
608 *
609 * @param directConnFlag flag to show that packet is from directly connected client
610 * @param dhcp6Relay the dhcp6 payload
611 * @param embeddedDhcp6 client's ethernet packetthe dhcp6 payload within relay
612 * @param clientMac client macAddress
Kalhee Kimaa5172a2017-09-15 17:43:27 +0000613 * @param clientInterface client interface
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000614 */
615 private void addHostOrRoute(boolean directConnFlag, DHCP6 dhcp6Relay,
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000616 DHCP6 embeddedDhcp6,
617 MacAddress clientMac,
618 Interface clientInterface) {
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000619 log.debug("addHostOrRoute entered.");
620 // add host or route
621 if (isDhcp6Reply(dhcp6Relay)) {
622 IpAddress ip = null;
623 if (directConnFlag) {
624 // Add to host store if it connect to network directly
625 ip = extractIpAddress(embeddedDhcp6);
626 if (ip != null) {
627 Set<IpAddress> ips = Sets.newHashSet(ip);
Yi Tseng4b013202017-09-08 17:22:51 -0700628
629 // FIXME: we should use vlan id from original packet (solicit, request)
Kalhee Kimaa5172a2017-09-15 17:43:27 +0000630 VlanId vlanId = clientInterface.vlan();
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000631 HostId hostId = HostId.hostId(clientMac, vlanId);
Yi Tseng4b013202017-09-08 17:22:51 -0700632 Host host = hostService.getHost(hostId);
Kalhee Kimaa5172a2017-09-15 17:43:27 +0000633 HostLocation hostLocation = new HostLocation(clientInterface.connectPoint(),
Yi Tseng4b013202017-09-08 17:22:51 -0700634 System.currentTimeMillis());
635 Set<HostLocation> hostLocations = Sets.newHashSet(hostLocation);
636
637 if (host != null) {
638 // Dual homing support:
639 // if host exists, use old locations and new location
640 hostLocations.addAll(host.locations());
641 }
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000642 HostDescription desc = new DefaultHostDescription(clientMac, vlanId,
Yi Tseng4b013202017-09-08 17:22:51 -0700643 hostLocations, ips,
644 false);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000645 log.debug("adding Host for directly connected.");
646 log.debug("client mac {} client vlan {} hostlocation {}",
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000647 HexString.toHexString(clientMac.toBytes(), ":"),
648 vlanId, hostLocation.toString());
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000649
650 // Replace the ip when dhcp server give the host new ip address
Yi Tseng4b013202017-09-08 17:22:51 -0700651 providerService.hostDetected(hostId, desc, false);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000652 } else {
653 log.warn("ipAddress not found. Do not add Host for directly connected.");
654 }
655 } else {
656 // Add to route store if it does not connect to network directly
657 IpAddress nextHopIp = IpAddress.valueOf(IpAddress.Version.INET6, dhcp6Relay.getPeerAddress());
658
659 DHCP6 leafDhcp = getDhcp6Leaf(embeddedDhcp6);
660 ip = extractIpAddress(leafDhcp);
661 if (ip == null) {
662 log.warn("ip is null");
663 } else {
664 Route routeForIP = new Route(Route.Source.STATIC, ip.toIpPrefix(), nextHopIp);
665 log.warn("adding Route of 128 address for indirectly connected.");
666 routeStore.updateRoute(routeForIP);
667 }
668
669 IpPrefix ipPrefix = extractPrefix(leafDhcp);
670 if (ipPrefix == null) {
671 log.warn("ipPrefix is null ");
672 } else {
673 Route routeForPrefix = new Route(Route.Source.STATIC, ipPrefix, nextHopIp);
674 log.warn("adding Route of PD for indirectly connected.");
675 routeStore.updateRoute(routeForPrefix);
676 }
677 }
678 }
679 }
680
681 /**
682 *
683 * build the DHCP6 solicit/request packet with gatewayip.
684 *
685 * @param context packet context
686 * @param clientPacket client ethernet packet
687 * @param clientInterfaces set of client side interfaces
688 */
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000689 private InternalPacket processDhcp6PacketFromClient(PacketContext context,
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000690 Ethernet clientPacket, Set<Interface> clientInterfaces) {
691 Ip6Address relayAgentIp = getRelayAgentIPv6Address(clientInterfaces);
692 MacAddress relayAgentMac = clientInterfaces.iterator().next().mac();
693 if (relayAgentIp == null || relayAgentMac == null) {
694 log.warn("Missing DHCP relay agent interface Ipv6 addr config for "
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000695 + "packet from client on port: {}. Aborting packet processing",
696 clientInterfaces.iterator().next().connectPoint());
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000697 return null;
698 }
699
700 // get dhcp6 header.
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000701 IPv6 clientIpv6 = (IPv6) clientPacket.getPayload();
702 UDP clientUdp = (UDP) clientIpv6.getPayload();
703 DHCP6 clientDhcp6 = (DHCP6) clientUdp.getPayload();
704
705 boolean directConnFlag = directlyConnected(clientDhcp6);
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000706 Interface serverInterface = directConnFlag ? getServerInterface() : getIndirectServerInterface();
707 if (serverInterface == null) {
708 log.warn("Can't get {} server interface, ignore", directConnFlag ? "direct" : "indirect");
709 return null;
710 }
711 Ip6Address ipFacingServer = getFirstIpFromInterface(serverInterface);
712 MacAddress macFacingServer = serverInterface.mac();
713 if (ipFacingServer == null || macFacingServer == null) {
714 log.warn("No IP v6 address for server Interface {}", serverInterface);
715 return null;
716 }
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000717
718 Ethernet etherReply = (Ethernet) clientPacket.clone();
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000719 etherReply.setSourceMACAddress(macFacingServer);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000720
Kalhee Kim2575d2c2017-09-26 20:21:53 +0000721 if ((directConnFlag && this.dhcpConnectMac == null) ||
722 !directConnFlag && this.indirectDhcpConnectMac == null && this.dhcpConnectMac == null) {
723 log.warn("Packet received from {} connected client.", directConnFlag ? "directly" : "indirectly");
Charles Chanf6a77be2017-10-30 13:44:33 -0700724 log.warn("DHCP6 {} not yet resolved .. Aborting DHCP packet processing from client on port: {}",
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000725 (this.dhcpGatewayIp == null) ? "server IP " + this.dhcpServerIp
726 : "gateway IP " + this.dhcpGatewayIp,
727 clientInterfaces.iterator().next().connectPoint());
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000728 return null;
729 }
730
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000731 if (this.dhcpServerConnectPoint == null) {
Kalhee Kim2575d2c2017-09-26 20:21:53 +0000732 log.warn("DHCP6 server connection point direct {} directConn {} indirectConn {} is not set up yet",
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000733 directConnFlag, this.dhcpServerConnectPoint, this.indirectDhcpServerConnectPoint);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000734 return null;
735 }
736
Kalhee Kimaa5172a2017-09-15 17:43:27 +0000737 etherReply.setDestinationMACAddress(this.dhcpConnectMac);
738 etherReply.setVlanID(this.dhcpConnectVlan.toShort());
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000739
740 IPv6 ipv6Packet = (IPv6) etherReply.getPayload();
Kalhee Kim45c93852017-10-27 20:16:26 +0000741 Ip6Address peerAddress = null;
742 if (directConnFlag) {
743 peerAddress = Ip6Address.valueOf(ipv6Packet.getSourceAddress());
Kalhee Kim45c93852017-10-27 20:16:26 +0000744 } else {
745 MacAddress gwOrClientMac = MacAddress.valueOf(clientPacket.getSourceMACAddress());
746 VlanId vlanId = VlanId.vlanId(clientPacket.getVlanID());
747 HostId gwOrClientHostId = HostId.hostId(gwOrClientMac, vlanId);
748 Host gwOrClientHost = hostService.getHost(gwOrClientHostId);
749
750 if (gwOrClientHost == null) {
751 log.warn("Can't find client gateway/server host {}", gwOrClientHostId);
752 return null;
753 }
754 // pick out the first gloabl ip address
Charles Chanf6a77be2017-10-30 13:44:33 -0700755 peerAddress = gwOrClientHost.ipAddresses().stream()
756 .filter(IpAddress::isIp6).filter(ip6 -> !ip6.isLinkLocal())
757 .map(IpAddress::getIp6Address).findFirst().orElse(null);
Kalhee Kim45c93852017-10-27 20:16:26 +0000758
759 if (peerAddress == null) {
760 log.warn("Can't find client gateway/server for mac {} ip {}", gwOrClientMac,
761 HexString.toHexString(ipv6Packet.getSourceAddress()));
Charles Chanf6a77be2017-10-30 13:44:33 -0700762 log.warn("Can't find IP address of client gateway/ClienHost address {} for peerAddress",
763 gwOrClientHost);
Kalhee Kim45c93852017-10-27 20:16:26 +0000764 return null;
765 }
766 }
767 ipv6Packet.setSourceAddress(ipFacingServer.toOctets());
Kalhee Kimaa5172a2017-09-15 17:43:27 +0000768 ipv6Packet.setDestinationAddress(this.dhcpServerIp.toOctets());
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000769
770 UDP udpPacket = (UDP) ipv6Packet.getPayload();
771 udpPacket.setSourcePort(UDP.DHCP_V6_SERVER_PORT);
772 DHCP6 dhcp6Packet = (DHCP6) udpPacket.getPayload();
773 byte[] dhcp6PacketByte = dhcp6Packet.serialize();
774
775 // notify onos and quagga to release PD
776 //releasePD(dhcp6Packet);
Kalhee Kimaa5172a2017-09-15 17:43:27 +0000777 ConnectPoint clientConnectionPoint = context.inPacket().receivedFrom();
778 VlanId vlanIdInUse = VlanId.vlanId(clientPacket.getVlanID());
779 Interface clientInterface = interfaceService.getInterfacesByPort(clientConnectionPoint)
Charles Chanf6a77be2017-10-30 13:44:33 -0700780 .stream().filter(iface -> interfaceContainsVlan(iface, vlanIdInUse))
781 .findFirst().orElse(null);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000782
Kalhee Kimaa5172a2017-09-15 17:43:27 +0000783 removeHostOrRoute(directConnFlag, dhcp6Packet, clientPacket, clientIpv6, clientInterface);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000784
785 DHCP6 dhcp6Relay = new DHCP6();
786 dhcp6Relay.setMsgType(DHCP6.MsgType.RELAY_FORW.value());
787 // link address: server uses the address to identify the link on which the client
788 // is located.
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000789 if (directConnFlag) {
790 dhcp6Relay.setLinkAddress(relayAgentIp.toOctets());
791 log.debug("direct connection: relayAgentIp obtained dynamically {}",
792 HexString.toHexString(relayAgentIp.toOctets(), ":"));
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000793
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000794 } else {
795 if (this.indirectDhcpServerIp == null) {
796 log.warn("indirect DhcpServerIp not available, use default DhcpServerIp {}",
Kalhee Kim2575d2c2017-09-26 20:21:53 +0000797 HexString.toHexString(this.dhcpServerIp.toOctets()));
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000798 } else {
799 // Indirect case, replace destination to indirect dhcp server if exist
800 // Check if mac is obtained for valid server ip
801 if (this.indirectDhcpConnectMac == null) {
802 log.warn("DHCP6 {} not yet resolved .. Aborting DHCP "
803 + "packet processing from client on port: {}",
804 (this.indirectDhcpGatewayIp == null) ? "server IP " + this.indirectDhcpServerIp
805 : "gateway IP " + this.indirectDhcpGatewayIp,
Kalhee Kim2575d2c2017-09-26 20:21:53 +0000806 clientInterfaces.iterator().next().connectPoint());
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000807 return null;
808 }
809 etherReply.setDestinationMACAddress(this.indirectDhcpConnectMac);
810 etherReply.setVlanID(this.indirectDhcpConnectVlan.toShort());
811 ipv6Packet.setDestinationAddress(this.indirectDhcpServerIp.toOctets());
Kalhee Kim2575d2c2017-09-26 20:21:53 +0000812
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000813 }
814 if (this.indirectRelayAgentIpFromCfg == null) {
815 dhcp6Relay.setLinkAddress(relayAgentIp.toOctets());
816 log.warn("indirect connection: relayAgentIp NOT availale from config file! Use dynamic. {}",
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000817 HexString.toHexString(relayAgentIp.toOctets(), ":"));
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000818 } else {
819 dhcp6Relay.setLinkAddress(this.indirectRelayAgentIpFromCfg.toOctets());
820 log.debug("indirect connection: relayAgentIp from config file is available! {}",
821 HexString.toHexString(this.indirectRelayAgentIpFromCfg.toOctets(), ":"));
822 }
823 }
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000824
825 // peer address: address of the client or relay agent from which
826 // the message to be relayed was received.
Kalhee Kim45c93852017-10-27 20:16:26 +0000827 dhcp6Relay.setPeerAddress(peerAddress.toOctets());
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000828 List<Dhcp6Option> options = new ArrayList<Dhcp6Option>();
829
830 // directly connected case, hop count is zero
831 // otherwise, hop count + 1
832 if (directConnFlag) {
833 dhcp6Relay.setHopCount((byte) 0);
834 } else {
835 dhcp6Relay.setHopCount((byte) (dhcp6Packet.getHopCount() + 1));
836 }
837
838 // create relay message option
839 Dhcp6Option relayMessage = new Dhcp6Option();
840 relayMessage.setCode(DHCP6.OptionCode.RELAY_MSG.value());
841 relayMessage.setLength((short) dhcp6PacketByte.length);
842 relayMessage.setData(dhcp6PacketByte);
843 options.add(relayMessage);
844
845 // create interfaceId option
Kalhee Kimaa5172a2017-09-15 17:43:27 +0000846 String inPortString = "-" + context.inPacket().receivedFrom().toString() + ":";
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000847 Dhcp6Option interfaceId = new Dhcp6Option();
848 interfaceId.setCode(DHCP6.OptionCode.INTERFACE_ID.value());
849 byte[] clientSoureMacBytes = clientPacket.getSourceMACAddress();
850 byte[] inPortStringBytes = inPortString.getBytes();
Kalhee Kimaa5172a2017-09-15 17:43:27 +0000851 byte[] vlanIdBytes = new byte[2];
852 vlanIdBytes[0] = (byte) (clientPacket.getVlanID() & 0xff);
853 vlanIdBytes[1] = (byte) ((clientPacket.getVlanID() >> 8) & 0xff);
854 byte[] interfaceIdBytes = new byte[clientSoureMacBytes.length +
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000855 inPortStringBytes.length + vlanIdBytes.length];
Kalhee Kimaa5172a2017-09-15 17:43:27 +0000856 log.debug("Length: interfaceIdBytes {} clientSoureMacBytes {} inPortStringBytes {} vlan {}",
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000857 interfaceIdBytes.length, clientSoureMacBytes.length, inPortStringBytes.length,
858 vlanIdBytes.length);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000859
860 System.arraycopy(clientSoureMacBytes, 0, interfaceIdBytes, 0, clientSoureMacBytes.length);
861 System.arraycopy(inPortStringBytes, 0, interfaceIdBytes, clientSoureMacBytes.length, inPortStringBytes.length);
Kalhee Kimaa5172a2017-09-15 17:43:27 +0000862 System.arraycopy(vlanIdBytes, 0, interfaceIdBytes, clientSoureMacBytes.length + inPortStringBytes.length,
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000863 vlanIdBytes.length);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000864
865 interfaceId.setData(interfaceIdBytes);
866 interfaceId.setLength((short) interfaceIdBytes.length);
867
868 options.add(interfaceId);
869
870 log.debug("interfaceId write srcMac {} portString {}",
871 HexString.toHexString(clientSoureMacBytes, ":"), inPortString);
872 dhcp6Relay.setOptions(options);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000873 udpPacket.setPayload(dhcp6Relay);
874 udpPacket.resetChecksum();
875 ipv6Packet.setPayload(udpPacket);
Charles Chan7edf7642017-10-09 11:07:25 -0400876 ipv6Packet.setHopLimit((byte) 64);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000877 etherReply.setPayload(ipv6Packet);
878
Kalhee Kim2575d2c2017-09-26 20:21:53 +0000879 if (directConnFlag) {
880 return new InternalPacket(etherReply, this.dhcpServerConnectPoint);
881 } else {
882 if (this.indirectDhcpServerIp == null) {
883 return new InternalPacket(etherReply, this.dhcpServerConnectPoint);
884 } else {
885 return new InternalPacket(etherReply, this.indirectDhcpServerConnectPoint);
886 }
887 }
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000888 }
889
890 /**
891 *
892 * process the DHCP6 relay-reply packet from dhcp server.
893 *
894 * @param context packet context
895 * @param receivedPacket server ethernet packet
896 * @param recevingInterfaces set of server side interfaces
897 */
898 private InternalPacket processDhcp6PacketFromServer(PacketContext context,
899 Ethernet receivedPacket, Set<Interface> recevingInterfaces) {
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000900 // get dhcp6 header.
901 Ethernet etherReply = (Ethernet) receivedPacket.clone();
902 IPv6 ipv6Packet = (IPv6) etherReply.getPayload();
903 UDP udpPacket = (UDP) ipv6Packet.getPayload();
904 DHCP6 dhcp6Relay = (DHCP6) udpPacket.getPayload();
905
906 Boolean directConnFlag = directlyConnected(dhcp6Relay);
Kalhee Kim2575d2c2017-09-26 20:21:53 +0000907 ConnectPoint inPort = context.inPacket().receivedFrom();
908 if ((directConnFlag || (!directConnFlag && this.indirectDhcpServerIp == null))
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000909 && !inPort.equals(this.dhcpServerConnectPoint)) {
Kalhee Kim2575d2c2017-09-26 20:21:53 +0000910 log.warn("Receiving port {} is not the same as server connect point {} for direct or indirect-null",
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000911 inPort, this.dhcpServerConnectPoint);
Kalhee Kim2575d2c2017-09-26 20:21:53 +0000912 return null;
913 }
914
915 if (!directConnFlag && this.indirectDhcpServerIp != null &&
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000916 !inPort.equals(this.indirectDhcpServerConnectPoint)) {
Kalhee Kim2575d2c2017-09-26 20:21:53 +0000917 log.warn("Receiving port {} is not the same as server connect point {} for indirect",
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000918 inPort, this.indirectDhcpServerConnectPoint);
Kalhee Kim2575d2c2017-09-26 20:21:53 +0000919 return null;
920 }
921
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000922
923 Dhcp6InterfaceIdOption interfaceIdOption = dhcp6Relay.getOptions().stream()
924 .filter(opt -> opt instanceof Dhcp6InterfaceIdOption)
925 .map(opt -> (Dhcp6InterfaceIdOption) opt)
926 .findFirst()
927 .orElse(null);
928
929 if (interfaceIdOption == null) {
930 log.warn("Interface Id option is not present, abort packet...");
931 return null;
932 }
933
934 MacAddress peerMac = interfaceIdOption.getMacAddress();
935 String clientConnectionPointStr = new String(interfaceIdOption.getInPort());
936
937 ConnectPoint clientConnectionPoint = ConnectPoint.deviceConnectPoint(clientConnectionPointStr);
Kalhee Kimaa5172a2017-09-15 17:43:27 +0000938 VlanId vlanIdInUse = VlanId.vlanId(interfaceIdOption.getVlanId());
939 Interface clientInterface = interfaceService.getInterfacesByPort(clientConnectionPoint)
940 .stream()
941 .filter(iface -> interfaceContainsVlan(iface, vlanIdInUse))
942 .findFirst()
943 .orElse(null);
944 if (clientInterface == null) {
945 log.warn("Cannot get client interface for from packet, abort... vlan {}", vlanIdInUse.toString());
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000946 return null;
947 }
Kalhee Kimaa5172a2017-09-15 17:43:27 +0000948 MacAddress relayAgentMac = clientInterface.mac();
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000949 if (relayAgentMac == null) {
950 log.warn("Can not get interface mac, abort packet..");
951 return null;
952 }
953 etherReply.setSourceMACAddress(relayAgentMac);
954
955 // find destMac
956 MacAddress clientMac = null;
Yi Tseng4b013202017-09-08 17:22:51 -0700957 Ip6Address peerAddress = Ip6Address.valueOf(dhcp6Relay.getPeerAddress());
958 Set<Host> clients = hostService.getHostsByIp(peerAddress);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000959 if (clients.isEmpty()) {
960 log.warn("There's no host found for this address {}",
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000961 HexString.toHexString(dhcp6Relay.getPeerAddress(), ":"));
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000962 log.warn("Let's look up interfaceId {}", HexString.toHexString(peerMac.toBytes(), ":"));
963 clientMac = peerMac;
964 } else {
965 clientMac = clients.iterator().next().mac();
966 if (clientMac == null) {
967 log.warn("No client mac address found, abort packet...");
968 return null;
969 }
970 log.warn("Client mac address found from getHostByIp");
971
972 }
973 etherReply.setDestinationMACAddress(clientMac);
974
975 // ip header
976 ipv6Packet.setSourceAddress(dhcp6Relay.getLinkAddress());
977 ipv6Packet.setDestinationAddress(dhcp6Relay.getPeerAddress());
978 // udp header
979 udpPacket.setSourcePort(UDP.DHCP_V6_SERVER_PORT);
980 if (directConnFlag) {
981 udpPacket.setDestinationPort(UDP.DHCP_V6_CLIENT_PORT);
982 } else {
983 udpPacket.setDestinationPort(UDP.DHCP_V6_SERVER_PORT);
984 }
985
986 DHCP6 embeddedDhcp6 = dhcp6Relay.getOptions().stream()
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000987 .filter(opt -> opt instanceof Dhcp6RelayOption)
988 .map(BasePacket::getPayload)
989 .map(pld -> (DHCP6) pld)
990 .findFirst()
991 .orElse(null);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000992
993
994 // add host or route
Kalhee Kimaa5172a2017-09-15 17:43:27 +0000995 addHostOrRoute(directConnFlag, dhcp6Relay, embeddedDhcp6, clientMac, clientInterface);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000996
997 udpPacket.setPayload(embeddedDhcp6);
998 udpPacket.resetChecksum();
999 ipv6Packet.setPayload(udpPacket);
1000 etherReply.setPayload(ipv6Packet);
1001
1002 return new InternalPacket(etherReply, clientConnectionPoint);
1003 }
1004
Yi Tseng2fe8f3f2017-09-07 16:22:51 -07001005 // Returns the first v6 interface ip out of a set of interfaces or null.
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001006 // Checks all interfaces, and ignores v6 interface ips
1007 private Ip6Address getRelayAgentIPv6Address(Set<Interface> intfs) {
1008 for (Interface intf : intfs) {
1009 for (InterfaceIpAddress ip : intf.ipAddressesList()) {
1010 Ip6Address relayAgentIp = ip.ipAddress().getIp6Address();
1011 if (relayAgentIp != null) {
1012 return relayAgentIp;
1013 }
1014 }
1015 }
1016 return null;
Yi Tseng51301292017-07-28 13:02:59 -07001017 }
Yi Tsenge72fbb52017-08-02 15:03:31 -07001018
1019 @Override
1020 public void setDefaultDhcpServerConfigs(Collection<DhcpServerConfig> configs) {
Yi Tseng2fe8f3f2017-09-07 16:22:51 -07001021 setDhcpServerConfigs(configs, defaultServerInfoList);
1022 reloadServerSettings();
1023 }
1024
1025 @Override
1026 public void setIndirectDhcpServerConfigs(Collection<DhcpServerConfig> configs) {
1027 setDhcpServerConfigs(configs, indirectServerInfoList);
1028 reloadServerSettings();
1029 }
1030
1031 public void setDhcpServerConfigs(Collection<DhcpServerConfig> configs, List<DhcpServerInfo> serverInfoList) {
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001032 if (configs.size() == 0) {
1033 // no config to update
1034 return;
1035 }
1036
1037 // TODO: currently we pick up first DHCP server config.
1038 // Will use other server configs in the future for HA.
1039 DhcpServerConfig serverConfig = configs.iterator().next();
Yi Tseng2fe8f3f2017-09-07 16:22:51 -07001040
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001041 if (!serverConfig.getDhcpServerIp6().isPresent()) {
Yi Tseng2fe8f3f2017-09-07 16:22:51 -07001042 // not a DHCPv6 config
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001043 return;
1044 }
1045
Yi Tseng2fe8f3f2017-09-07 16:22:51 -07001046 if (!serverInfoList.isEmpty()) {
1047 // remove old server info
1048 DhcpServerInfo oldServerInfo = serverInfoList.remove(0);
1049
1050 // stop monitoring gateway or server
1051 oldServerInfo.getDhcpGatewayIp6().ifPresent(gatewayIp -> {
1052 hostService.stopMonitoringIp(gatewayIp);
1053 });
1054 oldServerInfo.getDhcpServerIp6().ifPresent(serverIp -> {
1055 hostService.stopMonitoringIp(serverIp);
1056 });
1057 }
1058
1059 // Create new server info according to the config
1060 DhcpServerInfo newServerInfo = new DhcpServerInfo(serverConfig,
1061 DhcpServerInfo.Version.DHCP_V6);
1062 checkState(newServerInfo.getDhcpServerConnectPoint().isPresent(),
1063 "Connect point not exists");
1064 checkState(newServerInfo.getDhcpServerIp6().isPresent(),
1065 "IP of DHCP server not exists");
1066
1067 log.debug("DHCP server connect point: {}", newServerInfo.getDhcpServerConnectPoint().orElse(null));
1068 log.debug("DHCP server IP: {}", newServerInfo.getDhcpServerIp6().orElse(null));
1069
1070 IpAddress ipToProbe;
1071 if (newServerInfo.getDhcpGatewayIp6().isPresent()) {
1072 ipToProbe = newServerInfo.getDhcpGatewayIp6().get();
1073 } else {
1074 ipToProbe = newServerInfo.getDhcpServerIp6().orElse(null);
1075 }
1076 String hostToProbe = newServerInfo.getDhcpGatewayIp6()
1077 .map(ip -> "gateway").orElse("server");
1078
1079 log.debug("Probing to resolve {} IP {}", hostToProbe, ipToProbe);
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001080 hostService.startMonitoringIp(ipToProbe);
1081
1082 Set<Host> hosts = hostService.getHostsByIp(ipToProbe);
1083 if (!hosts.isEmpty()) {
1084 Host host = hosts.iterator().next();
Yi Tseng2fe8f3f2017-09-07 16:22:51 -07001085 newServerInfo.setDhcpConnectVlan(host.vlan());
1086 newServerInfo.setDhcpConnectMac(host.mac());
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001087 }
Yi Tseng2fe8f3f2017-09-07 16:22:51 -07001088 // Add new server info
1089 serverInfoList.add(0, newServerInfo);
Yi Tsenge72fbb52017-08-02 15:03:31 -07001090
Yi Tseng2fe8f3f2017-09-07 16:22:51 -07001091 // Remove duplicated server info
1092 Set<DhcpServerInfo> nonDupServerInfoList = Sets.newLinkedHashSet();
1093 nonDupServerInfoList.addAll(serverInfoList);
1094 serverInfoList.clear();
1095 serverInfoList.addAll(nonDupServerInfoList);
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001096 }
1097
1098 class InternalHostListener implements HostListener {
1099 @Override
1100 public void event(HostEvent event) {
1101 switch (event.type()) {
1102 case HOST_ADDED:
1103 case HOST_UPDATED:
1104 hostUpdated(event.subject());
1105 break;
1106 case HOST_REMOVED:
1107 hostRemoved(event.subject());
1108 break;
1109 case HOST_MOVED:
1110 hostMoved(event.subject());
1111 break;
1112 default:
1113 break;
1114 }
1115 }
1116 }
1117
1118 /**
1119 * Handle host move.
1120 * If the host DHCP server or gateway and it moved to the location different
1121 * to user configured, unsets the connect mac and vlan
1122 *
1123 * @param host the host
1124 */
1125 private void hostMoved(Host host) {
Yi Tseng2fe8f3f2017-09-07 16:22:51 -07001126 Set<ConnectPoint> hostConnectPoints = host.locations().stream()
1127 .map(hl -> new ConnectPoint(hl.elementId(), hl.port()))
1128 .collect(Collectors.toSet());
1129 DhcpServerInfo serverInfo;
1130 ConnectPoint dhcpServerConnectPoint;
1131 Ip6Address dhcpGatewayIp;
1132 Ip6Address dhcpServerIp;
1133
1134 if (!defaultServerInfoList.isEmpty()) {
1135 serverInfo = defaultServerInfoList.get(0);
1136 dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1137 dhcpGatewayIp = serverInfo.getDhcpGatewayIp6().orElse(null);
1138 dhcpServerIp = serverInfo.getDhcpServerIp6().orElse(null);
1139 if (dhcpServerConnectPoint == null) {
1140 return;
1141 }
1142 if (dhcpGatewayIp != null) {
1143 if (host.ipAddresses().contains(dhcpGatewayIp) &&
1144 !hostConnectPoints.contains(dhcpServerConnectPoint)) {
1145 serverInfo.setDhcpConnectVlan(null);
1146 serverInfo.setDhcpConnectMac(null);
1147 }
1148 }
1149 if (dhcpServerIp != null) {
1150 if (host.ipAddresses().contains(dhcpServerIp) &&
1151 !hostConnectPoints.contains(dhcpServerConnectPoint)) {
1152 serverInfo.setDhcpConnectVlan(null);
1153 serverInfo.setDhcpConnectMac(null);
1154 }
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001155 }
1156 }
Yi Tseng2fe8f3f2017-09-07 16:22:51 -07001157
1158 if (!indirectServerInfoList.isEmpty()) {
1159 serverInfo = indirectServerInfoList.get(0);
1160 dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1161 dhcpGatewayIp = serverInfo.getDhcpGatewayIp6().orElse(null);
1162 dhcpServerIp = serverInfo.getDhcpServerIp6().orElse(null);
1163 if (dhcpServerConnectPoint == null) {
1164 return;
1165 }
1166 if (dhcpGatewayIp != null) {
1167 if (host.ipAddresses().contains(dhcpGatewayIp) &&
1168 !hostConnectPoints.contains(dhcpServerConnectPoint)) {
1169 serverInfo.setDhcpConnectVlan(null);
1170 serverInfo.setDhcpConnectMac(null);
1171 }
1172 }
1173 if (dhcpServerIp != null) {
1174 if (host.ipAddresses().contains(dhcpServerIp) &&
1175 !hostConnectPoints.contains(dhcpServerConnectPoint)) {
1176 serverInfo.setDhcpConnectVlan(null);
1177 serverInfo.setDhcpConnectMac(null);
1178 }
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001179 }
1180 }
Yi Tseng2fe8f3f2017-09-07 16:22:51 -07001181 reloadServerSettings();
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001182 }
1183
1184 /**
1185 * Handle host updated.
1186 * If the host is DHCP server or gateway, update connect mac and vlan.
1187 *
1188 * @param host the host
1189 */
1190 private void hostUpdated(Host host) {
Yi Tseng2fe8f3f2017-09-07 16:22:51 -07001191 DhcpServerInfo serverInfo;
1192 Ip6Address dhcpGatewayIp;
1193 Ip6Address dhcpServerIp;
1194 if (!defaultServerInfoList.isEmpty()) {
1195 serverInfo = defaultServerInfoList.get(0);
1196 dhcpGatewayIp = serverInfo.getDhcpGatewayIp6().orElse(null);
1197 dhcpServerIp = serverInfo.getDhcpServerIp6().orElse(null);
1198 if (dhcpGatewayIp != null) {
1199 if (host.ipAddresses().contains(dhcpGatewayIp)) {
1200 serverInfo.setDhcpConnectMac(host.mac());
1201 serverInfo.setDhcpConnectVlan(host.vlan());
1202 }
1203 }
1204 if (dhcpServerIp != null) {
1205 if (host.ipAddresses().contains(dhcpServerIp)) {
1206 serverInfo.setDhcpConnectMac(host.mac());
1207 serverInfo.setDhcpConnectVlan(host.vlan());
1208 }
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001209 }
1210 }
Yi Tseng2fe8f3f2017-09-07 16:22:51 -07001211
1212 if (!indirectServerInfoList.isEmpty()) {
1213 serverInfo = indirectServerInfoList.get(0);
1214 dhcpGatewayIp = serverInfo.getDhcpGatewayIp6().orElse(null);
1215 dhcpServerIp = serverInfo.getDhcpServerIp6().orElse(null);
1216 if (dhcpGatewayIp != null) {
1217 if (host.ipAddresses().contains(dhcpGatewayIp)) {
1218 serverInfo.setDhcpConnectMac(host.mac());
1219 serverInfo.setDhcpConnectVlan(host.vlan());
1220 }
1221 }
1222 if (dhcpServerIp != null) {
1223 if (host.ipAddresses().contains(dhcpServerIp)) {
1224 serverInfo.setDhcpConnectMac(host.mac());
1225 serverInfo.setDhcpConnectVlan(host.vlan());
1226 }
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001227 }
1228 }
Yi Tseng2fe8f3f2017-09-07 16:22:51 -07001229 reloadServerSettings();
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001230 }
1231
1232 /**
1233 * Handle host removed.
1234 * If the host is DHCP server or gateway, unset connect mac and vlan.
1235 *
1236 * @param host the host
1237 */
1238 private void hostRemoved(Host host) {
Yi Tseng2fe8f3f2017-09-07 16:22:51 -07001239 DhcpServerInfo serverInfo;
1240 Ip6Address dhcpGatewayIp;
1241 Ip6Address dhcpServerIp;
1242
1243 if (!defaultServerInfoList.isEmpty()) {
1244 serverInfo = defaultServerInfoList.get(0);
1245 dhcpGatewayIp = serverInfo.getDhcpGatewayIp6().orElse(null);
1246 dhcpServerIp = serverInfo.getDhcpServerIp6().orElse(null);
1247
1248 if (dhcpGatewayIp != null) {
1249 if (host.ipAddresses().contains(dhcpGatewayIp)) {
1250 serverInfo.setDhcpConnectVlan(null);
1251 serverInfo.setDhcpConnectMac(null);
1252 }
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001253 }
Yi Tseng2fe8f3f2017-09-07 16:22:51 -07001254 if (dhcpServerIp != null) {
1255 if (host.ipAddresses().contains(dhcpServerIp)) {
1256 serverInfo.setDhcpConnectVlan(null);
1257 serverInfo.setDhcpConnectMac(null);
1258 }
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001259 }
1260 }
Yi Tseng2fe8f3f2017-09-07 16:22:51 -07001261
1262 if (!indirectServerInfoList.isEmpty()) {
1263 serverInfo = indirectServerInfoList.get(0);
1264 dhcpGatewayIp = serverInfo.getDhcpGatewayIp6().orElse(null);
1265 dhcpServerIp = serverInfo.getDhcpServerIp6().orElse(null);
1266
1267 if (dhcpGatewayIp != null) {
1268 if (host.ipAddresses().contains(dhcpGatewayIp)) {
1269 serverInfo.setDhcpConnectVlan(null);
1270 serverInfo.setDhcpConnectMac(null);
1271 }
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001272 }
Yi Tseng2fe8f3f2017-09-07 16:22:51 -07001273 if (dhcpServerIp != null) {
1274 if (host.ipAddresses().contains(dhcpServerIp)) {
1275 serverInfo.setDhcpConnectVlan(null);
1276 serverInfo.setDhcpConnectMac(null);
1277 }
1278 }
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001279 }
Yi Tseng2fe8f3f2017-09-07 16:22:51 -07001280 reloadServerSettings();
1281 }
1282
1283 private void reloadServerSettings() {
1284 DhcpServerInfo serverInfo;
1285 if (!defaultServerInfoList.isEmpty()) {
1286 serverInfo = defaultServerInfoList.get(0);
1287 this.dhcpConnectMac = serverInfo.getDhcpConnectMac().orElse(null);
1288 this.dhcpGatewayIp = serverInfo.getDhcpGatewayIp6().orElse(null);
1289 this.dhcpServerIp = serverInfo.getDhcpServerIp6().orElse(null);
1290 this.dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1291 this.dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1292 }
1293
1294 if (!indirectServerInfoList.isEmpty()) {
1295 serverInfo = indirectServerInfoList.get(0);
1296 this.indirectDhcpConnectMac = serverInfo.getDhcpConnectMac().orElse(null);
1297 this.indirectDhcpGatewayIp = serverInfo.getDhcpGatewayIp6().orElse(null);
1298 this.indirectDhcpServerIp = serverInfo.getDhcpServerIp6().orElse(null);
1299 this.indirectDhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1300 this.indirectDhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1301 this.indirectRelayAgentIpFromCfg = serverInfo.getRelayAgentIp6().orElse(null);
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001302 }
Yi Tsenge72fbb52017-08-02 15:03:31 -07001303 }
Kalhee Kim683ba3b2017-10-18 18:30:23 +00001304
1305 /**
1306 * Returns the first interface ip from interface.
1307 *
1308 * @param iface interface of one connect point
1309 * @return the first interface IP; null if not exists an IP address in
1310 * these interfaces
1311 */
1312 private Ip6Address getFirstIpFromInterface(Interface iface) {
1313 checkNotNull(iface, "Interface can't be null");
1314 return iface.ipAddressesList().stream()
1315 .map(InterfaceIpAddress::ipAddress)
1316 .filter(IpAddress::isIp6)
1317 .map(IpAddress::getIp6Address)
1318 .findFirst()
1319 .orElse(null);
1320 }
1321
1322 /**
1323 * Gets Interface facing to the server for default host.
1324 *
1325 * @return the Interface facing to the server; null if not found
1326 */
1327 private Interface getServerInterface() {
1328 if (dhcpServerConnectPoint == null || dhcpConnectVlan == null) {
1329 return null;
1330 }
1331 return interfaceService.getInterfacesByPort(dhcpServerConnectPoint)
1332 .stream()
1333 .filter(iface -> interfaceContainsVlan(iface, dhcpConnectVlan))
1334 .findFirst()
1335 .orElse(null);
1336 }
1337
1338 /**
1339 * Gets Interface facing to the server for indirect hosts.
1340 * Use default server Interface if indirect server not configured.
1341 *
1342 * @return the Interface facing to the server; null if not found
1343 */
1344 private Interface getIndirectServerInterface() {
1345 if (indirectDhcpServerConnectPoint == null || indirectDhcpConnectVlan == null) {
1346 return getServerInterface();
1347 }
1348 return interfaceService.getInterfacesByPort(indirectDhcpServerConnectPoint)
1349 .stream()
1350 .filter(iface -> interfaceContainsVlan(iface, indirectDhcpConnectVlan))
1351 .findFirst()
1352 .orElse(null);
1353 }
1354
Kalhee Kimaa5172a2017-09-15 17:43:27 +00001355 /**
1356 * Determind if an Interface contains a vlan id.
1357 *
1358 * @param iface the Interface
1359 * @param vlanId the vlan id
1360 * @return true if the Interface contains the vlan id
1361 */
1362 private boolean interfaceContainsVlan(Interface iface, VlanId vlanId) {
1363 if (vlanId.equals(VlanId.NONE)) {
1364 // untagged packet, check if vlan untagged or vlan native is not NONE
1365 return !iface.vlanUntagged().equals(VlanId.NONE) ||
1366 !iface.vlanNative().equals(VlanId.NONE);
1367 }
1368 // tagged packet, check if the interface contains the vlan
1369 return iface.vlanTagged().contains(vlanId);
1370 }
1371
Yi Tseng51301292017-07-28 13:02:59 -07001372}