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