blob: 50fd8bef5f81bb920273463046c06c5efffc1171 [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
Kalhee Kim1b5094f2017-09-05 19:05:06 +000020import org.apache.felix.scr.annotations.Activate;
21import org.apache.felix.scr.annotations.Deactivate;
22import com.google.common.base.MoreObjects;
23import 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;
Kalhee Kim1b5094f2017-09-05 19:05:06 +000050import org.onosproject.dhcprelay.store.DhcpRelayStore;
Yi Tseng4b013202017-09-08 17:22:51 -070051import org.onosproject.net.host.HostProvider;
52import org.onosproject.net.host.HostProviderRegistry;
53import org.onosproject.net.host.HostProviderService;
Kalhee Kim1b5094f2017-09-05 19:05:06 +000054import org.onosproject.net.host.HostService;
55import org.onosproject.net.host.DefaultHostDescription;
56import org.onosproject.net.host.HostDescription;
57import org.onosproject.net.host.InterfaceIpAddress;
58import org.onosproject.net.host.HostListener;
59import org.onosproject.net.host.HostEvent;
60import org.onosproject.net.intf.Interface;
61import org.onosproject.net.intf.InterfaceService;
Yi Tseng4b013202017-09-08 17:22:51 -070062import org.onosproject.net.provider.ProviderId;
Kalhee Kim1b5094f2017-09-05 19:05:06 +000063import org.onosproject.routeservice.Route;
64import org.onosproject.routeservice.RouteStore;
Yi Tsenge72fbb52017-08-02 15:03:31 -070065import org.onosproject.dhcprelay.config.DhcpServerConfig;
Yi Tseng51301292017-07-28 13:02:59 -070066import org.onosproject.net.ConnectPoint;
Kalhee Kim1b5094f2017-09-05 19:05:06 +000067import org.onosproject.net.Host;
68import org.onosproject.net.HostId;
69import org.onosproject.net.HostLocation;
70import org.onosproject.net.packet.DefaultOutboundPacket;
71import org.onosproject.net.packet.OutboundPacket;
Yi Tseng51301292017-07-28 13:02:59 -070072import org.onosproject.net.packet.PacketContext;
Kalhee Kim1b5094f2017-09-05 19:05:06 +000073import org.onosproject.net.packet.PacketService;
74import org.slf4j.Logger;
75import org.slf4j.LoggerFactory;
76import org.onosproject.net.flow.DefaultTrafficTreatment;
77import org.onosproject.net.flow.TrafficTreatment;
Yi Tseng51301292017-07-28 13:02:59 -070078
Kalhee Kim1b5094f2017-09-05 19:05:06 +000079
80import java.nio.ByteBuffer;
81import java.util.List;
Yi Tsenge72fbb52017-08-02 15:03:31 -070082import java.util.Collection;
Yi Tseng51301292017-07-28 13:02:59 -070083import java.util.Optional;
Kalhee Kim1b5094f2017-09-05 19:05:06 +000084import java.util.Set;
85import java.util.ArrayList;
86
87
88import static com.google.common.base.Preconditions.checkNotNull;
89import static com.google.common.base.Preconditions.checkState;
Yi Tseng51301292017-07-28 13:02:59 -070090
91@Component
92@Service
93@Property(name = "version", value = "6")
Yi Tseng4b013202017-09-08 17:22:51 -070094public class Dhcp6HandlerImpl implements DhcpHandler, HostProvider {
Charles Chan75edab72017-09-12 17:09:32 -070095 public static final String DHCP_V6_RELAY_APP = "org.onosproject.Dhcp6HandlerImpl";
96 public static final ProviderId PROVIDER_ID = new ProviderId("dhcp4", DHCP_V6_RELAY_APP);
Kalhee Kim1b5094f2017-09-05 19:05:06 +000097 private static Logger log = LoggerFactory.getLogger(Dhcp6HandlerImpl.class);
Yi Tseng51301292017-07-28 13:02:59 -070098
Kalhee Kim1b5094f2017-09-05 19:05:06 +000099 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
100 protected DhcpRelayStore dhcpRelayStore;
Yi Tseng51301292017-07-28 13:02:59 -0700101
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000102 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
103 protected PacketService packetService;
104
105 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000106 protected RouteStore routeStore;
107
108 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
109 protected InterfaceService interfaceService;
110
111 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
112 protected HostService hostService;
113
Yi Tseng4b013202017-09-08 17:22:51 -0700114 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
115 protected HostProviderRegistry providerRegistry;
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000116
Yi Tseng4b013202017-09-08 17:22:51 -0700117 private InternalHostListener hostListener = new InternalHostListener();
118 protected HostProviderService providerService;
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000119 private Ip6Address dhcpServerIp = null;
120 // dhcp server may be connected directly to the SDN network or
121 // via an external gateway. When connected directly, the dhcpConnectPoint, dhcpConnectMac,
122 // and dhcpConnectVlan refer to the server. When connected via the gateway, they refer
123 // to the gateway.
124 private ConnectPoint dhcpServerConnectPoint = null;
125 private MacAddress dhcpConnectMac = null;
126 private VlanId dhcpConnectVlan = null;
127 private Ip6Address dhcpGatewayIp = null;
128 private Ip6Address relayAgentIpFromCfg = null;
129
130 private Ip6Address indirectDhcpServerIp = null;
131 private ConnectPoint indirectDhcpServerConnectPoint = null;
132 private MacAddress indirectDhcpConnectMac = null;
133 private VlanId indirectDhcpConnectVlan = null;
134 private Ip6Address indirectDhcpGatewayIp = null;
135 private Ip6Address indirectRelayAgentIpFromCfg = null;
136
137
138 // CLIENT message types
139 public static final Set<Byte> MSG_TYPE_FROM_CLIENT =
140 ImmutableSet.of(DHCP6.MsgType.SOLICIT.value(),
141 DHCP6.MsgType.REQUEST.value(),
142 DHCP6.MsgType.REBIND.value(),
143 DHCP6.MsgType.RENEW.value(),
144 DHCP6.MsgType.RELEASE.value(),
145 DHCP6.MsgType.DECLINE.value(),
146 DHCP6.MsgType.CONFIRM.value(),
147 DHCP6.MsgType.RELAY_FORW.value());
148 // SERVER message types
149 public static final Set<Byte> MSG_TYPE_FROM_SERVER =
150 ImmutableSet.of(DHCP6.MsgType.RELAY_REPL.value());
151
152 @Activate
153 protected void activate() {
Yi Tseng4b013202017-09-08 17:22:51 -0700154 providerService = providerRegistry.register(this);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000155 hostService.addListener(hostListener);
Yi Tseng51301292017-07-28 13:02:59 -0700156 }
157
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000158 @Deactivate
159 protected void deactivate() {
Yi Tseng4b013202017-09-08 17:22:51 -0700160 providerRegistry.unregister(this);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000161 hostService.removeListener(hostListener);
162 this.dhcpConnectMac = null;
163 this.dhcpConnectVlan = null;
164
165 if (dhcpGatewayIp != null) {
166 hostService.stopMonitoringIp(dhcpGatewayIp);
167 } else if (dhcpServerIp != null) {
168 hostService.stopMonitoringIp(dhcpServerIp);
169 }
Yi Tseng51301292017-07-28 13:02:59 -0700170 }
171
Yi Tseng51301292017-07-28 13:02:59 -0700172 @Override
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000173 public void setDhcpServerIp(IpAddress dhcpServerIp) {
174 checkNotNull(dhcpServerIp, "DHCP server IP can't be null");
175 checkState(dhcpServerIp.isIp6(), "Invalid server IP for DHCPv6 relay handler");
176 this.dhcpServerIp = dhcpServerIp.getIp6Address();
Yi Tseng51301292017-07-28 13:02:59 -0700177 }
178
179 @Override
180 public void setDhcpServerConnectPoint(ConnectPoint dhcpServerConnectPoint) {
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000181 checkNotNull(dhcpServerConnectPoint, "Server connect point can't null");
182 this.dhcpServerConnectPoint = dhcpServerConnectPoint;
Yi Tseng51301292017-07-28 13:02:59 -0700183 }
184
185 @Override
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000186 public void setDhcpConnectMac(MacAddress dhcpConnectMac) {
187 this.dhcpConnectMac = dhcpConnectMac;
188 }
Yi Tseng51301292017-07-28 13:02:59 -0700189
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000190 @Override
191 public void setDhcpConnectVlan(VlanId dhcpConnectVlan) {
192 this.dhcpConnectVlan = dhcpConnectVlan;
193 }
194
195 @Override
196 public void setDhcpGatewayIp(IpAddress dhcpGatewayIp) {
197 if (dhcpGatewayIp != null) {
198 checkState(dhcpGatewayIp.isIp6(), "Invalid gateway IP for DHCPv6 relay handler");
199 this.dhcpGatewayIp = dhcpGatewayIp.getIp6Address();
200 } else {
201 // removes gateway config
202 this.dhcpGatewayIp = null;
203 }
204 }
205 @Override
206 public Optional<IpAddress> getDhcpServerIp() {
207 return Optional.ofNullable(dhcpServerIp);
208 }
209
210 @Override
211 public Optional<IpAddress> getDhcpGatewayIp() {
212 return Optional.ofNullable(dhcpGatewayIp);
213 }
214
215 @Override
216 public Optional<MacAddress> getDhcpConnectMac() {
217 return Optional.ofNullable(dhcpConnectMac);
218 }
219
220 // Indirect DHCP server
221
222 public void setIndirectDhcpServerIp(IpAddress dhcpServerIp) {
223 checkNotNull(dhcpServerIp, "DHCP indirect server IP can't be null");
224 checkState(dhcpServerIp.isIp6(), "Invalid indirect server IP for DHCPv6 relay handler");
225 this.indirectDhcpServerIp = dhcpServerIp.getIp6Address();
226 }
227
228
229 public void setIndirectDhcpServerConnectPoint(ConnectPoint dhcpServerConnectPoint) {
230 checkNotNull(dhcpServerConnectPoint, "Indirect Server connect point can't null");
231 this.indirectDhcpServerConnectPoint = dhcpServerConnectPoint;
232 }
233
234
235 public void setIndirectDhcpConnectMac(MacAddress dhcpConnectMac) {
236 this.indirectDhcpConnectMac = dhcpConnectMac;
237 }
238
239
240 public void setIndirectDhcpConnectVlan(VlanId dhcpConnectVlan) {
241 this.indirectDhcpConnectVlan = dhcpConnectVlan;
242 }
243
244
245 public void setIndirectDhcpGatewayIp(IpAddress dhcpGatewayIp) {
246 if (dhcpGatewayIp != null) {
247 checkState(dhcpGatewayIp.isIp6(), "Invalid indirect gateway IP for DHCPv6 relay handler");
248 this.indirectDhcpGatewayIp = dhcpGatewayIp.getIp6Address();
249 } else {
250 // removes gateway config
251 this.indirectDhcpGatewayIp = null;
252 }
253 }
254
255 public Optional<IpAddress> getIndirectDhcpServerIp() {
256 return Optional.ofNullable(indirectDhcpServerIp);
257 }
258
259
260 public Optional<IpAddress> getIndirectDhcpGatewayIp() {
261 return Optional.ofNullable(indirectDhcpGatewayIp);
262 }
263
264
265 public Optional<MacAddress> getIndirectDhcpConnectMac() {
266 return Optional.ofNullable(indirectDhcpConnectMac);
267 }
268
269
270 @Override
271 public void processDhcpPacket(PacketContext context, BasePacket payload) {
272 checkNotNull(payload, "DHCP6 payload can't be null");
273 checkState(payload instanceof DHCP6, "Payload is not a DHCP6");
274 DHCP6 dhcp6Payload = (DHCP6) payload;
275 Ethernet receivedPacket = context.inPacket().parsed();
276
277 if (!configured()) {
278 log.warn("Missing DHCP6 relay server config. Abort packet processing");
279 log.warn("dhcp6 payload {}", dhcp6Payload);
280
281 return;
282 }
283
284 byte msgType = dhcp6Payload.getMsgType();
285 log.warn("msgType is {}", msgType);
286
287 ConnectPoint inPort = context.inPacket().receivedFrom();
288 if (inPort == null) {
289 log.warn("incommin ConnectPoint is null");
290 }
291 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)) {
300
301 InternalPacket ethernetClientPacket =
302 processDhcp6PacketFromClient(context, receivedPacket, receivingInterfaces);
303 if (ethernetClientPacket != null) {
304 forwardPacket(ethernetClientPacket);
305 }
306
307 } else if (MSG_TYPE_FROM_SERVER.contains(msgType)) {
308 log.warn("calling processDhcp6PacketFromServer with RELAY_REPL", msgType);
309 InternalPacket ethernetPacketReply =
310 processDhcp6PacketFromServer(context, receivedPacket, receivingInterfaces);
311 if (ethernetPacketReply != null) {
312 forwardPacket(ethernetPacketReply);
313 }
314 } else {
315 log.warn("Not so fast, packet type {} not supported yet", msgType);
316 }
317 }
318
319
320 /**
321 * Checks if this app has been configured.
322 *
323 * @return true if all information we need have been initialized
324 */
325 public boolean configured() {
326 log.warn("dhcpServerConnectPoint {} dhcpServerIp {}",
327 this.dhcpServerConnectPoint, this.dhcpServerIp);
328 return this.dhcpServerConnectPoint != null && this.dhcpServerIp != null;
329 }
330
Yi Tseng4b013202017-09-08 17:22:51 -0700331 @Override
332 public ProviderId id() {
Charles Chan75edab72017-09-12 17:09:32 -0700333 return PROVIDER_ID;
Yi Tseng4b013202017-09-08 17:22:51 -0700334 }
335
336 @Override
337 public void triggerProbe(Host host) {
338 // Do nothing here
339 }
340
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000341 // the new class the contains Ethernet packet and destination port, kind of like adding
342 // internal header to the packet
343 private class InternalPacket {
344 Ethernet packet;
345 ConnectPoint destLocation;
346 public InternalPacket(Ethernet newPacket, ConnectPoint newLocation) {
347 packet = newPacket;
348 destLocation = newLocation;
349 }
350 void setLocation(ConnectPoint newLocation) {
351 destLocation = newLocation;
352 }
353 }
354
355 //forward the packet to ConnectPoint where the DHCP server is attached.
356 private void forwardPacket(InternalPacket packet) {
357 //send Packetout to dhcp server connectpoint.
358 if (packet.destLocation != null) {
359 TrafficTreatment t = DefaultTrafficTreatment.builder()
360 .setOutput(packet.destLocation.port()).build();
361 OutboundPacket o = new DefaultOutboundPacket(
362 packet.destLocation.deviceId(), t, ByteBuffer.wrap(packet.packet.serialize()));
363 if (log.isTraceEnabled()) {
364 log.trace("Relaying packet to destination {}", packet.destLocation);
365 }
366 packetService.emit(o);
367 } // if
368 }
369
370 /**
371 * Check if the host is directly connected to the network or not.
372 *
373 * @param dhcp6Payload the dhcp6 payload
374 * @return true if the host is directly connected to the network; false otherwise
375 */
376 private boolean directlyConnected(DHCP6 dhcp6Payload) {
377 log.debug("directlyConnected enters");
378
379 if (dhcp6Payload.getMsgType() != DHCP6.MsgType.RELAY_FORW.value() &&
380 dhcp6Payload.getMsgType() != DHCP6.MsgType.RELAY_REPL.value()) {
381 log.debug("directlyConnected true. MsgType {}", dhcp6Payload.getMsgType());
382
383 return true;
384 }
385
386 // Regardless of relay-forward or relay-replay, check if we see another relay message
387 DHCP6 dhcp6Payload2 = dhcp6PacketFromRelayPacket(dhcp6Payload);
388 if (dhcp6Payload2 != null) {
389 if (dhcp6Payload.getMsgType() == DHCP6.MsgType.RELAY_FORW.value()) {
390 log.debug("directlyConnected false. 1st realy-foward, 2nd MsgType {}", dhcp6Payload2.getMsgType());
391 return false;
392 } else {
393 // relay-reply
394 if (dhcp6Payload2.getMsgType() != DHCP6.MsgType.RELAY_REPL.value()) {
395 log.debug("directlyConnected true. 2nd MsgType {}", dhcp6Payload2.getMsgType());
396 return true; // must be directly connected
397 } else {
398 log.debug("directlyConnected false. 1st relay-reply, 2nd relay-reply MsgType {}",
399 dhcp6Payload2.getMsgType());
400 return false; // must be indirectly connected
401 }
402 }
403 } else {
404 log.warn("directlyConnected true.");
405 return true;
406 }
407 }
408
409 /**
410 * extract DHCP6 payload from dhcp6 relay message within relay-forwrd/reply.
411 *
412 * @param dhcp6 dhcp6 relay-reply or relay-foward
413 * @return dhcp6Packet dhcp6 packet extracted from relay-message
414 */
415 private DHCP6 dhcp6PacketFromRelayPacket(DHCP6 dhcp6) {
416 log.debug("dhcp6PacketFromRelayPacket enters. dhcp6 {}", dhcp6);
417
418 // extract the relay message if exist
419 DHCP6 dhcp6Payload = dhcp6.getOptions().stream()
420 .filter(opt -> opt instanceof Dhcp6RelayOption)
421 .map(BasePacket::getPayload)
422 .map(pld -> (DHCP6) pld)
423 .findFirst()
424 .orElse(null);
425
426
427 if (dhcp6Payload == null) {
428 // Can't find dhcp payload
429 log.debug("Can't find dhcp6 payload from relay message");
430 } else {
431 log.debug("dhcp6 payload found from relay message {}", dhcp6Payload);
432 }
433
434 return dhcp6Payload;
435 }
436
437 /**
438 * find the leaf DHCP6 packet from multi-level relay packet.
439 *
440 * @param relayPacket dhcp6 relay packet
441 * @return leafPacket non-relay dhcp6 packet
442 */
443 private DHCP6 getDhcp6Leaf(DHCP6 relayPacket) {
444 DHCP6 dhcp6Parent = relayPacket;
445 DHCP6 dhcp6Child = null;
446
447 log.debug("getDhcp6Leaf entered.");
448 while (dhcp6Parent != null) {
449 dhcp6Child = dhcp6PacketFromRelayPacket(dhcp6Parent);
450
451 if (dhcp6Child != null) {
452 if (dhcp6Child.getMsgType() != DHCP6.MsgType.RELAY_FORW.value() &&
453 dhcp6Child.getMsgType() != DHCP6.MsgType.RELAY_REPL.value()) {
454 log.debug("leaf dhcp6 packet found.");
455 break;
456 } else {
457 // found another relay
458 // go for another loop
459 dhcp6Parent = dhcp6Child;
460 }
461 } else {
462 log.warn("malformed pkt! Expected dhcp6 within relay pkt, but no dhcp6 leaf found.");
463 break;
464 }
465 }
466 return dhcp6Child;
467 }
468
469 /**
470 * check if DHCP6 relay-reply is reply.
471 *
472 * @param relayPacket dhcp6 relay-reply
473 * @return boolean relay-reply contains ack
474 */
475 private boolean isDhcp6Reply(DHCP6 relayPacket) {
476 log.debug("isDhcp6Reply entered.");
477
478 DHCP6 leafDhcp6 = getDhcp6Leaf(relayPacket);
479
480 if (leafDhcp6 != null) {
481 if (leafDhcp6.getMsgType() == DHCP6.MsgType.REPLY.value()) {
482 log.debug("isDhcp6Reply true.");
483 return true; // must be directly connected
484 } else {
485 log.debug("isDhcp6Reply false. leaf dhcp6 is not replay. MsgType {}", leafDhcp6.getMsgType());
486 }
487 } else {
488 log.debug("isDhcp6Reply false. Expected dhcp6 within relay pkt but not found.");
489 }
490 log.debug("isDhcp6Reply false.");
491 return false;
492 }
493
494 /**
495 * check if DHCP6 is release or relay-forward contains release.
496 *
497 * @param dhcp6Payload dhcp6 packet
498 * @return boolean dhcp6 contains release
499 */
500 private boolean isDhcp6Release(DHCP6 dhcp6Payload) {
501
502 log.debug("isDhcp6Release entered.");
503
504 if (dhcp6Payload.getMsgType() == DHCP6.MsgType.RELEASE.value()) {
505 log.debug("isDhcp6Release true.");
506 return true; // must be directly connected
507 } else {
508 DHCP6 dhcp6Leaf = getDhcp6Leaf(dhcp6Payload);
509 if (dhcp6Leaf != null) {
510 if (dhcp6Leaf.getMsgType() == DHCP6.MsgType.RELEASE.value()) {
511 log.debug("isDhcp6Release true. indirectlry connected");
512 return true;
513 } else {
514 log.debug("leaf dhcp6 is not release. MsgType {}", dhcp6Leaf.getMsgType());
515 return false;
516 }
517 } else {
518 log.debug("isDhcp6Release false. dhcp6 is niether relay nor release.");
519 return false;
520 }
521 }
522 }
523
524 /**
525 * extract from dhcp6 packet client ipv6 address of given by dhcp server.
526 *
527 * @param dhcp6 the dhcp6 packet
528 * @return Ip6Address Ip6Address given by dhcp server, or null if not exists
529 */
530 private Ip6Address extractIpAddress(DHCP6 dhcp6) {
531 Ip6Address ip = null;
532
533 log.debug("extractIpAddress enters dhcp6 {}.", dhcp6);
534 // Extract IPv6 address from IA NA ot IA TA option
535 Optional<Dhcp6IaNaOption> iaNaOption = dhcp6.getOptions()
536 .stream()
537 .filter(opt -> opt instanceof Dhcp6IaNaOption)
538 .map(opt -> (Dhcp6IaNaOption) opt)
539 .findFirst();
540 Optional<Dhcp6IaTaOption> iaTaOption = dhcp6.getOptions()
541 .stream()
542 .filter(opt -> opt instanceof Dhcp6IaTaOption)
543 .map(opt -> (Dhcp6IaTaOption) opt)
544 .findFirst();
545 Optional<Dhcp6IaAddressOption> iaAddressOption;
546 if (iaNaOption.isPresent()) {
547 log.debug("Found IPv6 address from iaNaOption {}", iaNaOption);
548
549 iaAddressOption = iaNaOption.get().getOptions().stream()
550 .filter(opt -> opt instanceof Dhcp6IaAddressOption)
551 .map(opt -> (Dhcp6IaAddressOption) opt)
552 .findFirst();
553 } else if (iaTaOption.isPresent()) {
554 log.debug("Found IPv6 address from iaTaOption {}", iaTaOption);
555
556 iaAddressOption = iaTaOption.get().getOptions().stream()
557 .filter(opt -> opt instanceof Dhcp6IaAddressOption)
558 .map(opt -> (Dhcp6IaAddressOption) opt)
559 .findFirst();
560 } else {
561 iaAddressOption = Optional.empty();
562 }
563 if (iaAddressOption.isPresent()) {
564 ip = iaAddressOption.get().getIp6Address();
565 log.debug("Found IPv6 address from iaAddressOption {}", iaAddressOption);
566
567
568 } else {
569 log.debug("Can't find IPv6 address from DHCPv6 {}", dhcp6);
570 }
571
572 return ip;
573 }
574 /**
575 * extract from dhcp6 packet Prefix prefix provided by dhcp server.
576 *
577 * @param dhcp6 the dhcp6 payload
578 * @return IpPrefix Prefix Delegation prefix, or null if not exists.
579 */
580 private IpPrefix extractPrefix(DHCP6 dhcp6) {
581 log.warn("extractPrefix enters {}", dhcp6);
582
583 // extract prefix
584 IpPrefix prefixPrefix = null;
585
586 Ip6Address prefixAddress = null;
587
588 // Extract IPv6 prefix from IA PD option
589 Optional<Dhcp6IaPdOption> iaPdOption = dhcp6.getOptions()
590 .stream()
591 .filter(opt -> opt instanceof Dhcp6IaPdOption)
592 .map(opt -> (Dhcp6IaPdOption) opt)
593 .findFirst();
594
595 Optional<Dhcp6IaPrefixOption> iaPrefixOption;
596 if (iaPdOption.isPresent()) {
597 log.warn("IA_PD option found {}", iaPdOption);
598
599 iaPrefixOption = iaPdOption.get().getOptions().stream()
600 .filter(opt -> opt instanceof Dhcp6IaPrefixOption)
601 .map(opt -> (Dhcp6IaPrefixOption) opt)
602 .findFirst();
603 } else {
604 log.warn("IA_PD option NOT found");
605
606 iaPrefixOption = Optional.empty();
607 }
608 if (iaPrefixOption.isPresent()) {
609 log.warn("IAPrefix Option within IA_PD option found {}", iaPrefixOption);
610
611 prefixAddress = iaPrefixOption.get().getIp6Prefix();
612 int prefixLen = (int) iaPrefixOption.get().getPrefixLength();
613 log.warn("Prefix length is {} bits", prefixLen);
614 prefixPrefix = IpPrefix.valueOf(prefixAddress, prefixLen);
615
616 } else {
617 log.warn("Can't find IPv6 prefix from DHCPv6 {}", dhcp6);
618 }
619
620 return prefixPrefix;
621 }
622
623 /**
624 * remove host or route.
625 *
626 * @param directConnFlag flag to show that packet is from directly connected client
627 * @param dhcp6Packet the dhcp6 payload
628 * @param clientPacket client's ethernet packet
629 * @param clientIpv6 client's Ipv6 packet
630 * @param clientInterfaces set of client interfaces
631 */
632 private void removeHostOrRoute(boolean directConnFlag, DHCP6 dhcp6Packet,
633 Ethernet clientPacket, IPv6 clientIpv6,
634 Set<Interface> clientInterfaces) {
635 log.debug("extractPrefix enters {}", dhcp6Packet);
636 // add host or route
637 if (isDhcp6Release(dhcp6Packet)) {
638 IpAddress ip = null;
639 if (directConnFlag) {
640 // Add to host store if it is connected to network directly
641 ip = extractIpAddress(dhcp6Packet);
642 if (ip != null) {
643 VlanId vlanId = clientInterfaces.iterator().next().vlan();
644 MacAddress clientMac = clientPacket.getSourceMAC();
645 HostId hostId = HostId.hostId(clientMac, vlanId);
646 log.debug("remove Host {} ip for directly connected.", hostId.toString());
647
648 log.debug("client mac {} client vlan {}", HexString.toHexString(clientMac.toBytes(), ":"), vlanId);
649
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000650 // Remove host's ip of when dhcp release msg is received
Yi Tseng4b013202017-09-08 17:22:51 -0700651 providerService.removeIpFromHost(hostId, ip);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000652 } else {
653 log.debug("ipAddress not found. Do not add Host for directly connected.");
654 }
655 } else {
656 // Remove from route store if it is not connected to network directly
657 IpAddress nextHopIp = IpAddress.valueOf(IpAddress.Version.INET6, clientIpv6.getSourceAddress());
658
659 DHCP6 leafDhcp = getDhcp6Leaf(dhcp6Packet);
660 ip = extractIpAddress(leafDhcp);
661 if (ip == null) {
662 log.debug("ip is null");
663 } else {
664 Route routeForIP = new Route(Route.Source.STATIC, ip.toIpPrefix(), nextHopIp);
665 log.debug("removing route of 128 address for indirectly connected.");
666 log.debug("128 ip {}, nexthop {}", HexString.toHexString(ip.toOctets(), ":"),
667 HexString.toHexString(nextHopIp.toOctets(), ":"));
668 routeStore.removeRoute(routeForIP);
669 }
670
671 IpPrefix ipPrefix = extractPrefix(leafDhcp);
672 if (ipPrefix == null) {
673 log.debug("ipPrefix is null ");
674 } else {
675 Route routeForPrefix = new Route(Route.Source.STATIC, ipPrefix, nextHopIp);
676 log.debug("removing route of PD for indirectly connected.");
677 log.debug("pd ip {}, nexthop {}", HexString.toHexString(ipPrefix.address().toOctets(), ":"),
678 HexString.toHexString(nextHopIp.toOctets(), ":"));
679
680 routeStore.removeRoute(routeForPrefix);
681 }
682 }
683 }
684 }
685
686 /**
687 * add host or route.
688 *
689 * @param directConnFlag flag to show that packet is from directly connected client
690 * @param dhcp6Relay the dhcp6 payload
691 * @param embeddedDhcp6 client's ethernet packetthe dhcp6 payload within relay
692 * @param clientMac client macAddress
693 * @param clientInterfaces set of client interfaces
694 */
695 private void addHostOrRoute(boolean directConnFlag, DHCP6 dhcp6Relay,
696 DHCP6 embeddedDhcp6,
697 MacAddress clientMac,
698 Set<Interface> clientInterfaces) {
699 log.debug("addHostOrRoute entered.");
700 // add host or route
701 if (isDhcp6Reply(dhcp6Relay)) {
702 IpAddress ip = null;
703 if (directConnFlag) {
704 // Add to host store if it connect to network directly
705 ip = extractIpAddress(embeddedDhcp6);
706 if (ip != null) {
707 Set<IpAddress> ips = Sets.newHashSet(ip);
Yi Tseng4b013202017-09-08 17:22:51 -0700708
709 // FIXME: we should use vlan id from original packet (solicit, request)
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000710 VlanId vlanId = clientInterfaces.iterator().next().vlan();
711 HostId hostId = HostId.hostId(clientMac, vlanId);
Yi Tseng4b013202017-09-08 17:22:51 -0700712 Host host = hostService.getHost(hostId);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000713 HostLocation hostLocation = new HostLocation(clientInterfaces.iterator().next().connectPoint(),
Yi Tseng4b013202017-09-08 17:22:51 -0700714 System.currentTimeMillis());
715 Set<HostLocation> hostLocations = Sets.newHashSet(hostLocation);
716
717 if (host != null) {
718 // Dual homing support:
719 // if host exists, use old locations and new location
720 hostLocations.addAll(host.locations());
721 }
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000722 HostDescription desc = new DefaultHostDescription(clientMac, vlanId,
Yi Tseng4b013202017-09-08 17:22:51 -0700723 hostLocations, ips,
724 false);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000725 log.debug("adding Host for directly connected.");
726 log.debug("client mac {} client vlan {} hostlocation {}",
727 HexString.toHexString(clientMac.toBytes(), ":"),
728 vlanId, hostLocation.toString());
729
730 // Replace the ip when dhcp server give the host new ip address
Yi Tseng4b013202017-09-08 17:22:51 -0700731 providerService.hostDetected(hostId, desc, false);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000732 } else {
733 log.warn("ipAddress not found. Do not add Host for directly connected.");
734 }
735 } else {
736 // Add to route store if it does not connect to network directly
737 IpAddress nextHopIp = IpAddress.valueOf(IpAddress.Version.INET6, dhcp6Relay.getPeerAddress());
738
739 DHCP6 leafDhcp = getDhcp6Leaf(embeddedDhcp6);
740 ip = extractIpAddress(leafDhcp);
741 if (ip == null) {
742 log.warn("ip is null");
743 } else {
744 Route routeForIP = new Route(Route.Source.STATIC, ip.toIpPrefix(), nextHopIp);
745 log.warn("adding Route of 128 address for indirectly connected.");
746 routeStore.updateRoute(routeForIP);
747 }
748
749 IpPrefix ipPrefix = extractPrefix(leafDhcp);
750 if (ipPrefix == null) {
751 log.warn("ipPrefix is null ");
752 } else {
753 Route routeForPrefix = new Route(Route.Source.STATIC, ipPrefix, nextHopIp);
754 log.warn("adding Route of PD for indirectly connected.");
755 routeStore.updateRoute(routeForPrefix);
756 }
757 }
758 }
759 }
760
761 /**
762 *
763 * build the DHCP6 solicit/request packet with gatewayip.
764 *
765 * @param context packet context
766 * @param clientPacket client ethernet packet
767 * @param clientInterfaces set of client side interfaces
768 */
769 private InternalPacket processDhcp6PacketFromClient(PacketContext context,
770 Ethernet clientPacket, Set<Interface> clientInterfaces) {
771 Ip6Address relayAgentIp = getRelayAgentIPv6Address(clientInterfaces);
772 MacAddress relayAgentMac = clientInterfaces.iterator().next().mac();
773 if (relayAgentIp == null || relayAgentMac == null) {
774 log.warn("Missing DHCP relay agent interface Ipv6 addr config for "
775 + "packet from client on port: {}. Aborting packet processing",
776 clientInterfaces.iterator().next().connectPoint());
777 return null;
778 }
779
780 // get dhcp6 header.
781
782 IPv6 clientIpv6 = (IPv6) clientPacket.getPayload();
783 UDP clientUdp = (UDP) clientIpv6.getPayload();
784 DHCP6 clientDhcp6 = (DHCP6) clientUdp.getPayload();
785
786 boolean directConnFlag = directlyConnected(clientDhcp6);
787
788 Ethernet etherReply = (Ethernet) clientPacket.clone();
789 etherReply.setSourceMACAddress(relayAgentMac);
790
791 if (directConnFlag && this.dhcpConnectMac == null) {
792 log.warn("DHCP6 {} not yet resolved .. Aborting DHCP "
793 + "packet processing from client on port: {}",
794 (this.dhcpGatewayIp == null) ? "server IP " + this.dhcpServerIp
795 : "gateway IP " + this.dhcpGatewayIp,
796 clientInterfaces.iterator().next().connectPoint());
797
798 return null;
799 }
800
801 if (!directConnFlag && 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,
806 clientInterfaces.iterator().next().connectPoint());
807
808 return null;
809
810 }
811
812 if (this.dhcpServerConnectPoint == null) {
813 log.warn("DHCP6 server connection point is not set up yet");
814 return null;
815 }
816
817 etherReply.setDestinationMACAddress(directConnFlag ? this.dhcpConnectMac : this.indirectDhcpConnectMac);
818 etherReply.setVlanID(directConnFlag ? this.dhcpConnectVlan.toShort() : this.indirectDhcpConnectVlan.toShort());
819
820 IPv6 ipv6Packet = (IPv6) etherReply.getPayload();
821 byte[] peerAddress = clientIpv6.getSourceAddress();
822 ipv6Packet.setSourceAddress(relayAgentIp.toOctets());
823 ipv6Packet.setDestinationAddress(directConnFlag ? this.dhcpServerIp.toOctets() :
824 this.indirectDhcpServerIp.toOctets());
825
826 UDP udpPacket = (UDP) ipv6Packet.getPayload();
827 udpPacket.setSourcePort(UDP.DHCP_V6_SERVER_PORT);
828 DHCP6 dhcp6Packet = (DHCP6) udpPacket.getPayload();
829 byte[] dhcp6PacketByte = dhcp6Packet.serialize();
830
831 // notify onos and quagga to release PD
832 //releasePD(dhcp6Packet);
833
834 removeHostOrRoute(directConnFlag, dhcp6Packet, clientPacket, clientIpv6, clientInterfaces);
835
836 DHCP6 dhcp6Relay = new DHCP6();
837 dhcp6Relay.setMsgType(DHCP6.MsgType.RELAY_FORW.value());
838 // link address: server uses the address to identify the link on which the client
839 // is located.
840 if (directConnFlag) {
841 dhcp6Relay.setLinkAddress(relayAgentIp.toOctets());
842 log.debug("direct connection: relayAgentIp obtained dynamically {}",
843 HexString.toHexString(relayAgentIp.toOctets(), ":"));
844
845 } else {
846 if (this.indirectRelayAgentIpFromCfg == null) {
847 dhcp6Relay.setLinkAddress(relayAgentIp.toOctets());
848 log.warn("indirect connection: relayAgentIp NOT availale from config file! {}",
849 HexString.toHexString(relayAgentIp.toOctets(), ":"));
850
851 } else {
852 dhcp6Relay.setLinkAddress(this.indirectRelayAgentIpFromCfg.toOctets());
853 log.debug("indirect connection: relayAgentIp from config file is available! {}",
854 HexString.toHexString(this.indirectRelayAgentIpFromCfg.toOctets(), ":"));
855 }
856 }
857
858 // peer address: address of the client or relay agent from which
859 // the message to be relayed was received.
860 dhcp6Relay.setPeerAddress(peerAddress);
861 List<Dhcp6Option> options = new ArrayList<Dhcp6Option>();
862
863 // directly connected case, hop count is zero
864 // otherwise, hop count + 1
865 if (directConnFlag) {
866 dhcp6Relay.setHopCount((byte) 0);
867 } else {
868 dhcp6Relay.setHopCount((byte) (dhcp6Packet.getHopCount() + 1));
869 }
870
871 // create relay message option
872 Dhcp6Option relayMessage = new Dhcp6Option();
873 relayMessage.setCode(DHCP6.OptionCode.RELAY_MSG.value());
874 relayMessage.setLength((short) dhcp6PacketByte.length);
875 relayMessage.setData(dhcp6PacketByte);
876 options.add(relayMessage);
877
878 // create interfaceId option
879 String inPortString = "-" + context.inPacket().receivedFrom().toString();
880 Dhcp6Option interfaceId = new Dhcp6Option();
881 interfaceId.setCode(DHCP6.OptionCode.INTERFACE_ID.value());
882 byte[] clientSoureMacBytes = clientPacket.getSourceMACAddress();
883 byte[] inPortStringBytes = inPortString.getBytes();
884 byte[] interfaceIdBytes = new byte[clientSoureMacBytes.length + inPortStringBytes.length];
885 log.debug("Length: interfaceIdBytes {} clientSoureMacBytes {} inPortStringBytes {} ",
886 interfaceIdBytes.length, clientSoureMacBytes.length, inPortStringBytes.length);
887
888 System.arraycopy(clientSoureMacBytes, 0, interfaceIdBytes, 0, clientSoureMacBytes.length);
889 System.arraycopy(inPortStringBytes, 0, interfaceIdBytes, clientSoureMacBytes.length, inPortStringBytes.length);
890
891 interfaceId.setData(interfaceIdBytes);
892 interfaceId.setLength((short) interfaceIdBytes.length);
893
894 options.add(interfaceId);
895
896 log.debug("interfaceId write srcMac {} portString {}",
897 HexString.toHexString(clientSoureMacBytes, ":"), inPortString);
898 dhcp6Relay.setOptions(options);
899 //dhcp6Packet.setPayload(dhcp6Relay);
900 //udpPacket.setPayload(dhcp6Packet);
901 udpPacket.setPayload(dhcp6Relay);
902 udpPacket.resetChecksum();
903 ipv6Packet.setPayload(udpPacket);
904 etherReply.setPayload(ipv6Packet);
905
906
907 return new InternalPacket(etherReply, this.dhcpServerConnectPoint);
908 }
909
910 /**
911 *
912 * process the DHCP6 relay-reply packet from dhcp server.
913 *
914 * @param context packet context
915 * @param receivedPacket server ethernet packet
916 * @param recevingInterfaces set of server side interfaces
917 */
918 private InternalPacket processDhcp6PacketFromServer(PacketContext context,
919 Ethernet receivedPacket, Set<Interface> recevingInterfaces) {
920 ConnectPoint inPort = context.inPacket().receivedFrom();
921 if (!inPort.equals(this.dhcpServerConnectPoint)) {
922 log.warn("Receiving port {} is not the same as server port {}",
923 inPort, this.dhcpServerConnectPoint);
924 return null;
925 }
926 // get dhcp6 header.
927 Ethernet etherReply = (Ethernet) receivedPacket.clone();
928 IPv6 ipv6Packet = (IPv6) etherReply.getPayload();
929 UDP udpPacket = (UDP) ipv6Packet.getPayload();
930 DHCP6 dhcp6Relay = (DHCP6) udpPacket.getPayload();
931
932 Boolean directConnFlag = directlyConnected(dhcp6Relay);
933
934 Dhcp6InterfaceIdOption interfaceIdOption = dhcp6Relay.getOptions().stream()
935 .filter(opt -> opt instanceof Dhcp6InterfaceIdOption)
936 .map(opt -> (Dhcp6InterfaceIdOption) opt)
937 .findFirst()
938 .orElse(null);
939
940 if (interfaceIdOption == null) {
941 log.warn("Interface Id option is not present, abort packet...");
942 return null;
943 }
944
945 MacAddress peerMac = interfaceIdOption.getMacAddress();
946 String clientConnectionPointStr = new String(interfaceIdOption.getInPort());
947
948 ConnectPoint clientConnectionPoint = ConnectPoint.deviceConnectPoint(clientConnectionPointStr);
949
950 Set<Interface> clientInterfaces = interfaceService.getInterfacesByPort(clientConnectionPoint);
951 if (clientInterfaces.isEmpty()) {
952 log.warn("Can not get client interface from packet, abort..");
953 return null;
954 }
955 MacAddress relayAgentMac = clientInterfaces.iterator().next().mac();
956 if (relayAgentMac == null) {
957 log.warn("Can not get interface mac, abort packet..");
958 return null;
959 }
960 etherReply.setSourceMACAddress(relayAgentMac);
961
962 // find destMac
963 MacAddress clientMac = null;
Yi Tseng4b013202017-09-08 17:22:51 -0700964 Ip6Address peerAddress = Ip6Address.valueOf(dhcp6Relay.getPeerAddress());
965 Set<Host> clients = hostService.getHostsByIp(peerAddress);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000966 if (clients.isEmpty()) {
967 log.warn("There's no host found for this address {}",
968 HexString.toHexString(dhcp6Relay.getPeerAddress(), ":"));
969 log.warn("Let's look up interfaceId {}", HexString.toHexString(peerMac.toBytes(), ":"));
970 clientMac = peerMac;
971 } else {
972 clientMac = clients.iterator().next().mac();
973 if (clientMac == null) {
974 log.warn("No client mac address found, abort packet...");
975 return null;
976 }
977 log.warn("Client mac address found from getHostByIp");
978
979 }
980 etherReply.setDestinationMACAddress(clientMac);
981
982 // ip header
983 ipv6Packet.setSourceAddress(dhcp6Relay.getLinkAddress());
984 ipv6Packet.setDestinationAddress(dhcp6Relay.getPeerAddress());
985 // udp header
986 udpPacket.setSourcePort(UDP.DHCP_V6_SERVER_PORT);
987 if (directConnFlag) {
988 udpPacket.setDestinationPort(UDP.DHCP_V6_CLIENT_PORT);
989 } else {
990 udpPacket.setDestinationPort(UDP.DHCP_V6_SERVER_PORT);
991 }
992
993 DHCP6 embeddedDhcp6 = dhcp6Relay.getOptions().stream()
994 .filter(opt -> opt instanceof Dhcp6RelayOption)
995 .map(BasePacket::getPayload)
996 .map(pld -> (DHCP6) pld)
997 .findFirst()
998 .orElse(null);
999
1000
1001 // add host or route
1002 addHostOrRoute(directConnFlag, dhcp6Relay, embeddedDhcp6, clientMac, clientInterfaces);
1003
1004 udpPacket.setPayload(embeddedDhcp6);
1005 udpPacket.resetChecksum();
1006 ipv6Packet.setPayload(udpPacket);
1007 etherReply.setPayload(ipv6Packet);
1008
1009 return new InternalPacket(etherReply, clientConnectionPoint);
1010 }
1011
1012 // Returns the first v4 interface ip out of a set of interfaces or null.
1013 // Checks all interfaces, and ignores v6 interface ips
1014 private Ip6Address getRelayAgentIPv6Address(Set<Interface> intfs) {
1015 for (Interface intf : intfs) {
1016 for (InterfaceIpAddress ip : intf.ipAddressesList()) {
1017 Ip6Address relayAgentIp = ip.ipAddress().getIp6Address();
1018 if (relayAgentIp != null) {
1019 return relayAgentIp;
1020 }
1021 }
1022 }
1023 return null;
Yi Tseng51301292017-07-28 13:02:59 -07001024 }
Yi Tsenge72fbb52017-08-02 15:03:31 -07001025
1026 @Override
1027 public void setDefaultDhcpServerConfigs(Collection<DhcpServerConfig> configs) {
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001028 if (configs.size() == 0) {
1029 // no config to update
1030 return;
1031 }
1032
1033 // TODO: currently we pick up first DHCP server config.
1034 // Will use other server configs in the future for HA.
1035 DhcpServerConfig serverConfig = configs.iterator().next();
1036 if (!serverConfig.getDhcpServerConnectPoint().isPresent()) {
1037 log.warn("Connect point not exists");
1038 return;
1039 }
1040 if (!serverConfig.getDhcpServerIp6().isPresent()) {
1041 log.warn("IP of DHCP6 server not exists");
1042 return;
1043 }
1044 Ip6Address oldServerIp = this.dhcpServerIp;
1045 Ip6Address oldGatewayIp = this.dhcpGatewayIp;
1046
1047 // stop monitoring gateway or server
1048 if (oldGatewayIp != null) {
1049 hostService.stopMonitoringIp(oldGatewayIp);
1050 } else if (oldServerIp != null) {
1051 hostService.stopMonitoringIp(oldServerIp);
1052 }
1053
1054 this.dhcpServerConnectPoint = serverConfig.getDhcpServerConnectPoint().get();
1055 this.dhcpServerIp = serverConfig.getDhcpServerIp6().get();
1056 this.dhcpGatewayIp = serverConfig.getDhcpGatewayIp6().orElse(null);
1057 this.relayAgentIpFromCfg = serverConfig.getRelayAgentIp6().orElse(null);
1058
1059
1060 // reset server mac and vlan
1061 this.dhcpConnectMac = null;
1062 this.dhcpConnectVlan = null;
1063
1064 log.info("DHCP6 server connect point: " + this.dhcpServerConnectPoint);
1065 log.info("DHCP6 server IP: " + this.dhcpServerIp);
1066
1067 IpAddress ipToProbe = MoreObjects.firstNonNull(this.dhcpGatewayIp, this.dhcpServerIp);
1068 String hostToProbe = this.dhcpGatewayIp != null ? "gateway" : "DHCP6 server";
1069
1070 if (ipToProbe == null) {
1071 log.warn("Server IP6 not set, can't probe it");
1072 return;
1073 }
1074
1075 log.info("Probing to resolve {} IP6 {}", hostToProbe, ipToProbe);
1076 hostService.startMonitoringIp(ipToProbe);
1077
1078 Set<Host> hosts = hostService.getHostsByIp(ipToProbe);
1079 if (!hosts.isEmpty()) {
1080 Host host = hosts.iterator().next();
1081 this.dhcpConnectVlan = host.vlan();
1082 this.dhcpConnectMac = host.mac();
1083 }
Yi Tsenge72fbb52017-08-02 15:03:31 -07001084
1085 }
1086
1087 @Override
1088 public void setIndirectDhcpServerConfigs(Collection<DhcpServerConfig> configs) {
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001089 if (configs.size() == 0) {
1090 // no config to update
1091 return;
1092 }
Yi Tsenge72fbb52017-08-02 15:03:31 -07001093
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001094 // TODO: currently we pick up Second DHCP server config for indirect.
1095 // Will use other server configs in the future for HA.
1096 DhcpServerConfig serverConfig = configs.iterator().next();
1097 checkState(serverConfig.getDhcpServerConnectPoint().isPresent(),
1098 "Connect point not exists");
1099 checkState(serverConfig.getDhcpServerIp6().isPresent(),
1100 "IP of DHCP6 server not exists");
1101 Ip6Address oldServerIp = this.indirectDhcpServerIp;
1102 Ip6Address oldGatewayIp = this.indirectDhcpGatewayIp;
1103
1104 // stop monitoring gateway or server
1105 if (oldGatewayIp != null) {
1106 hostService.stopMonitoringIp(oldGatewayIp);
1107 } else if (oldServerIp != null) {
1108 hostService.stopMonitoringIp(oldServerIp);
1109 }
1110
1111 this.indirectDhcpServerConnectPoint = serverConfig.getDhcpServerConnectPoint().get();
1112 this.indirectDhcpServerIp = serverConfig.getDhcpServerIp6().get();
1113 this.indirectDhcpGatewayIp = serverConfig.getDhcpGatewayIp6().orElse(null);
1114 this.indirectRelayAgentIpFromCfg = serverConfig.getRelayAgentIp6().orElse(null);
1115
1116
1117 // reset server mac and vlan
1118 this.indirectDhcpConnectMac = null;
1119 this.indirectDhcpConnectVlan = null;
1120
1121 log.info("DHCP6 server connect point: " + this.indirectDhcpServerConnectPoint);
1122 log.info("DHCP6 server IP: " + this.indirectDhcpServerIp);
1123
1124 IpAddress ipToProbe = MoreObjects.firstNonNull(this.indirectDhcpGatewayIp, this.indirectDhcpServerIp);
1125 String hostToProbe = this.indirectDhcpGatewayIp != null ? "gateway" : "DHCP6 server";
1126
1127 if (ipToProbe == null) {
1128 log.warn("Server IP6 not set, can't probe it");
1129 return;
1130 }
1131
1132 log.info("Probing to resolve {} IP6 {}", hostToProbe, ipToProbe);
1133 hostService.startMonitoringIp(ipToProbe);
1134
1135 Set<Host> hosts = hostService.getHostsByIp(ipToProbe);
1136 if (!hosts.isEmpty()) {
1137 Host host = hosts.iterator().next();
1138 this.indirectDhcpConnectVlan = host.vlan();
1139 this.indirectDhcpConnectMac = host.mac();
1140 }
1141 }
1142
1143 class InternalHostListener implements HostListener {
1144 @Override
1145 public void event(HostEvent event) {
1146 switch (event.type()) {
1147 case HOST_ADDED:
1148 case HOST_UPDATED:
1149 hostUpdated(event.subject());
1150 break;
1151 case HOST_REMOVED:
1152 hostRemoved(event.subject());
1153 break;
1154 case HOST_MOVED:
1155 hostMoved(event.subject());
1156 break;
1157 default:
1158 break;
1159 }
1160 }
1161 }
1162
1163 /**
1164 * Handle host move.
1165 * If the host DHCP server or gateway and it moved to the location different
1166 * to user configured, unsets the connect mac and vlan
1167 *
1168 * @param host the host
1169 */
1170 private void hostMoved(Host host) {
1171 if (this.dhcpServerConnectPoint == null && this.indirectDhcpServerConnectPoint == null) {
1172 return;
1173 }
1174 if (this.dhcpGatewayIp != null) {
1175 if (host.ipAddresses().contains(this.dhcpGatewayIp) &&
1176 !host.locations().contains(this.dhcpServerConnectPoint)) {
1177 this.dhcpConnectMac = null;
1178 this.dhcpConnectVlan = null;
1179 }
1180 }
1181 if (this.dhcpServerIp != null) {
1182 if (host.ipAddresses().contains(this.dhcpServerIp) &&
1183 !host.locations().contains(this.dhcpServerConnectPoint)) {
1184 this.dhcpConnectMac = null;
1185 this.dhcpConnectVlan = null;
1186 }
1187 }
1188 if (this.indirectDhcpGatewayIp != null) {
1189 if (host.ipAddresses().contains(this.indirectDhcpGatewayIp) &&
1190 !host.locations().contains(this.indirectDhcpServerConnectPoint)) {
1191 this.indirectDhcpConnectMac = null;
1192 this.indirectDhcpConnectVlan = null;
1193 }
1194 }
1195 if (this.indirectDhcpServerIp != null) {
1196 if (host.ipAddresses().contains(this.indirectDhcpServerIp) &&
1197 !host.locations().contains(this.indirectDhcpServerConnectPoint)) {
1198 this.indirectDhcpConnectMac = null;
1199 this.indirectDhcpConnectVlan = null;
1200 }
1201 }
1202 }
1203
1204 /**
1205 * Handle host updated.
1206 * If the host is DHCP server or gateway, update connect mac and vlan.
1207 *
1208 * @param host the host
1209 */
1210 private void hostUpdated(Host host) {
1211 if (this.dhcpGatewayIp != null) {
1212 if (host.ipAddresses().contains(this.dhcpGatewayIp)) {
1213 this.dhcpConnectMac = host.mac();
1214 this.dhcpConnectVlan = host.vlan();
1215 }
1216 }
1217 if (this.dhcpServerIp != null) {
1218 if (host.ipAddresses().contains(this.dhcpServerIp)) {
1219 this.dhcpConnectMac = host.mac();
1220 this.dhcpConnectVlan = host.vlan();
1221 }
1222 }
1223 if (this.indirectDhcpGatewayIp != null) {
1224 if (host.ipAddresses().contains(this.indirectDhcpGatewayIp)) {
1225 this.indirectDhcpConnectMac = host.mac();
1226 this.indirectDhcpConnectVlan = host.vlan();
1227 }
1228 }
1229 if (this.indirectDhcpServerIp != null) {
1230 if (host.ipAddresses().contains(this.indirectDhcpServerIp)) {
1231 this.indirectDhcpConnectMac = host.mac();
1232 this.indirectDhcpConnectVlan = host.vlan();
1233 }
1234 }
1235 }
1236
1237 /**
1238 * Handle host removed.
1239 * If the host is DHCP server or gateway, unset connect mac and vlan.
1240 *
1241 * @param host the host
1242 */
1243 private void hostRemoved(Host host) {
1244 if (this.dhcpGatewayIp != null) {
1245 if (host.ipAddresses().contains(this.dhcpGatewayIp)) {
1246 this.dhcpConnectMac = null;
1247 this.dhcpConnectVlan = null;
1248 }
1249 //return;
1250 }
1251 if (this.dhcpServerIp != null) {
1252 if (host.ipAddresses().contains(this.dhcpServerIp)) {
1253 this.dhcpConnectMac = null;
1254 this.dhcpConnectVlan = null;
1255 }
1256 }
1257 if (this.indirectDhcpGatewayIp != null) {
1258 if (host.ipAddresses().contains(this.indirectDhcpGatewayIp)) {
1259 this.indirectDhcpConnectMac = null;
1260 this.indirectDhcpConnectVlan = null;
1261 }
1262 //return;
1263 }
1264 if (this.indirectDhcpServerIp != null) {
1265 if (host.ipAddresses().contains(this.indirectDhcpServerIp)) {
1266 this.indirectDhcpConnectMac = null;
1267 this.indirectDhcpConnectVlan = null;
1268 }
1269 }
Yi Tsenge72fbb52017-08-02 15:03:31 -07001270 }
Yi Tseng51301292017-07-28 13:02:59 -07001271}