blob: b4571e62c3db15e472f7aba4d3e6c581eefd74e6 [file] [log] [blame]
Yi Tseng51301292017-07-28 13:02:59 -07001/*
2 * Copyright 2017-present Open Networking Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 */
17
18package org.onosproject.dhcprelay;
19
Yi Tseng7da339e2017-10-23 19:39:39 -070020import com.google.common.collect.HashMultimap;
Yi Tseng2fe8f3f2017-09-07 16:22:51 -070021import com.google.common.collect.Lists;
Yi Tseng7da339e2017-10-23 19:39:39 -070022import com.google.common.collect.Multimap;
Kalhee Kim1b5094f2017-09-05 19:05:06 +000023import org.apache.felix.scr.annotations.Activate;
24import org.apache.felix.scr.annotations.Deactivate;
Kalhee Kim1b5094f2017-09-05 19:05:06 +000025import com.google.common.collect.Sets;
26import com.google.common.collect.ImmutableSet;
Yi Tseng51301292017-07-28 13:02:59 -070027import org.apache.felix.scr.annotations.Component;
28import org.apache.felix.scr.annotations.Property;
Kalhee Kim1b5094f2017-09-05 19:05:06 +000029import org.apache.felix.scr.annotations.Reference;
30import org.apache.felix.scr.annotations.ReferenceCardinality;
Yi Tseng51301292017-07-28 13:02:59 -070031import org.apache.felix.scr.annotations.Service;
32import org.onlab.packet.BasePacket;
Kalhee Kim1b5094f2017-09-05 19:05:06 +000033import org.onlab.packet.DHCP6;
34import org.onlab.packet.IPv6;
35import org.onlab.packet.Ethernet;
36import org.onlab.packet.Ip6Address;
Yi Tseng51301292017-07-28 13:02:59 -070037import org.onlab.packet.IpAddress;
Kalhee Kim1b5094f2017-09-05 19:05:06 +000038import org.onlab.packet.IpPrefix;
Yi Tseng51301292017-07-28 13:02:59 -070039import org.onlab.packet.MacAddress;
Yi Tseng7da339e2017-10-23 19:39:39 -070040import org.onlab.packet.TpPort;
Kalhee Kim1b5094f2017-09-05 19:05:06 +000041import org.onlab.packet.UDP;
Yi Tseng51301292017-07-28 13:02:59 -070042import org.onlab.packet.VlanId;
Kalhee Kim1b5094f2017-09-05 19:05:06 +000043import org.onlab.packet.dhcp.Dhcp6RelayOption;
44import org.onlab.packet.dhcp.Dhcp6InterfaceIdOption;
45import org.onlab.packet.dhcp.Dhcp6Option;
46import org.onlab.packet.dhcp.Dhcp6IaNaOption;
47import org.onlab.packet.dhcp.Dhcp6IaTaOption;
48import org.onlab.packet.dhcp.Dhcp6IaPdOption;
49import org.onlab.packet.dhcp.Dhcp6IaAddressOption;
50import org.onlab.packet.dhcp.Dhcp6IaPrefixOption;
51import org.onlab.util.HexString;
Yi Tseng7da339e2017-10-23 19:39:39 -070052import org.onosproject.core.ApplicationId;
53import org.onosproject.core.CoreService;
Yi Tseng51301292017-07-28 13:02:59 -070054import org.onosproject.dhcprelay.api.DhcpHandler;
Yi Tseng2fe8f3f2017-09-07 16:22:51 -070055import org.onosproject.dhcprelay.api.DhcpServerInfo;
Yi Tseng7da339e2017-10-23 19:39:39 -070056import org.onosproject.dhcprelay.config.IgnoreDhcpConfig;
Kalhee Kim1b5094f2017-09-05 19:05:06 +000057import org.onosproject.dhcprelay.store.DhcpRelayStore;
Yi Tseng7da339e2017-10-23 19:39:39 -070058import org.onosproject.net.Device;
59import org.onosproject.net.DeviceId;
60import org.onosproject.net.behaviour.Pipeliner;
61import org.onosproject.net.device.DeviceService;
62import org.onosproject.net.flow.DefaultTrafficSelector;
63import org.onosproject.net.flow.TrafficSelector;
64import org.onosproject.net.flowobjective.DefaultForwardingObjective;
65import org.onosproject.net.flowobjective.FlowObjectiveService;
66import org.onosproject.net.flowobjective.ForwardingObjective;
67import org.onosproject.net.flowobjective.Objective;
68import org.onosproject.net.flowobjective.ObjectiveContext;
69import org.onosproject.net.flowobjective.ObjectiveError;
Yi Tseng4b013202017-09-08 17:22:51 -070070import org.onosproject.net.host.HostProvider;
71import org.onosproject.net.host.HostProviderRegistry;
72import org.onosproject.net.host.HostProviderService;
Kalhee Kim1b5094f2017-09-05 19:05:06 +000073import org.onosproject.net.host.HostService;
74import org.onosproject.net.host.DefaultHostDescription;
75import org.onosproject.net.host.HostDescription;
76import org.onosproject.net.host.InterfaceIpAddress;
77import org.onosproject.net.host.HostListener;
78import org.onosproject.net.host.HostEvent;
79import org.onosproject.net.intf.Interface;
80import org.onosproject.net.intf.InterfaceService;
Yi Tseng7da339e2017-10-23 19:39:39 -070081import org.onosproject.net.packet.PacketPriority;
Yi Tseng4b013202017-09-08 17:22:51 -070082import org.onosproject.net.provider.ProviderId;
Kalhee Kim1b5094f2017-09-05 19:05:06 +000083import org.onosproject.routeservice.Route;
84import org.onosproject.routeservice.RouteStore;
Yi Tsenge72fbb52017-08-02 15:03:31 -070085import org.onosproject.dhcprelay.config.DhcpServerConfig;
Yi Tseng51301292017-07-28 13:02:59 -070086import org.onosproject.net.ConnectPoint;
Kalhee Kim1b5094f2017-09-05 19:05:06 +000087import org.onosproject.net.Host;
88import org.onosproject.net.HostId;
89import org.onosproject.net.HostLocation;
90import org.onosproject.net.packet.DefaultOutboundPacket;
91import org.onosproject.net.packet.OutboundPacket;
Yi Tseng51301292017-07-28 13:02:59 -070092import org.onosproject.net.packet.PacketContext;
Kalhee Kim1b5094f2017-09-05 19:05:06 +000093import org.onosproject.net.packet.PacketService;
94import org.slf4j.Logger;
95import org.slf4j.LoggerFactory;
96import org.onosproject.net.flow.DefaultTrafficTreatment;
97import org.onosproject.net.flow.TrafficTreatment;
Yi Tseng51301292017-07-28 13:02:59 -070098
Kalhee Kim1b5094f2017-09-05 19:05:06 +000099
100import java.nio.ByteBuffer;
101import java.util.List;
Yi Tsenge72fbb52017-08-02 15:03:31 -0700102import java.util.Collection;
Yi Tseng51301292017-07-28 13:02:59 -0700103import java.util.Optional;
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000104import java.util.Set;
105import java.util.ArrayList;
Yi Tseng7da339e2017-10-23 19:39:39 -0700106import java.util.concurrent.atomic.AtomicInteger;
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000107
108
109import static com.google.common.base.Preconditions.checkNotNull;
110import static com.google.common.base.Preconditions.checkState;
Yi Tseng7da339e2017-10-23 19:39:39 -0700111import static org.onosproject.net.flowobjective.Objective.Operation.ADD;
112import static org.onosproject.net.flowobjective.Objective.Operation.REMOVE;
Yi Tseng51301292017-07-28 13:02:59 -0700113
114@Component
115@Service
116@Property(name = "version", value = "6")
Yi Tseng4b013202017-09-08 17:22:51 -0700117public class Dhcp6HandlerImpl implements DhcpHandler, HostProvider {
Charles Chan75edab72017-09-12 17:09:32 -0700118 public static final String DHCP_V6_RELAY_APP = "org.onosproject.Dhcp6HandlerImpl";
Saurav Das37415cc2017-09-13 14:35:56 -0700119 public static final ProviderId PROVIDER_ID = new ProviderId("dhcp6", DHCP_V6_RELAY_APP);
Yi Tseng7da339e2017-10-23 19:39:39 -0700120 private static final int IGNORE_CONTROL_PRIORITY = PacketPriority.CONTROL.priorityValue() + 1000;
121
122 private static final TrafficSelector CLIENT_SERVER_SELECTOR = DefaultTrafficSelector.builder()
123 .matchEthType(Ethernet.TYPE_IPV6)
124 .matchIPProtocol(IPv6.PROTOCOL_UDP)
125 .matchIPv6Src(IpPrefix.IPV6_LINK_LOCAL_PREFIX)
126 .matchIPv6Dst(Ip6Address.ALL_DHCP_RELAY_AGENTS_AND_SERVERS.toIpPrefix())
127 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_V6_CLIENT_PORT))
128 .matchUdpDst(TpPort.tpPort(UDP.DHCP_V6_SERVER_PORT))
129 .build();
130 private static final TrafficSelector SERVER_RELAY_SELECTOR = DefaultTrafficSelector.builder()
131 .matchEthType(Ethernet.TYPE_IPV6)
132 .matchIPProtocol(IPv6.PROTOCOL_UDP)
133 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_V6_SERVER_PORT))
134 .matchUdpDst(TpPort.tpPort(UDP.DHCP_V6_SERVER_PORT))
135 .build();
136 static final Set<TrafficSelector> DHCP_SELECTORS = ImmutableSet.of(
137 CLIENT_SERVER_SELECTOR,
138 SERVER_RELAY_SELECTOR
139 );
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000140 private static Logger log = LoggerFactory.getLogger(Dhcp6HandlerImpl.class);
Yi Tseng51301292017-07-28 13:02:59 -0700141
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000142 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
143 protected DhcpRelayStore dhcpRelayStore;
Yi Tseng51301292017-07-28 13:02:59 -0700144
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000145 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
146 protected PacketService packetService;
147
148 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000149 protected RouteStore routeStore;
150
151 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
152 protected InterfaceService interfaceService;
153
154 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
155 protected HostService hostService;
156
Yi Tseng4b013202017-09-08 17:22:51 -0700157 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
158 protected HostProviderRegistry providerRegistry;
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000159
Yi Tseng7da339e2017-10-23 19:39:39 -0700160 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
161 protected CoreService coreService;
162
163 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
164 protected DeviceService deviceService;
165
166 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
167 protected FlowObjectiveService flowObjectiveService;
168
Yi Tseng4b013202017-09-08 17:22:51 -0700169 protected HostProviderService providerService;
Yi Tseng7da339e2017-10-23 19:39:39 -0700170 protected ApplicationId appId;
171 protected Multimap<DeviceId, VlanId> ignoredVlans = HashMultimap.create();
172 private InternalHostListener hostListener = new InternalHostListener();
173
Yi Tseng2fe8f3f2017-09-07 16:22:51 -0700174 private List<DhcpServerInfo> defaultServerInfoList = Lists.newArrayList();
175 private List<DhcpServerInfo> indirectServerInfoList = Lists.newArrayList();
176
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000177
178 // CLIENT message types
179 public static final Set<Byte> MSG_TYPE_FROM_CLIENT =
180 ImmutableSet.of(DHCP6.MsgType.SOLICIT.value(),
181 DHCP6.MsgType.REQUEST.value(),
182 DHCP6.MsgType.REBIND.value(),
183 DHCP6.MsgType.RENEW.value(),
184 DHCP6.MsgType.RELEASE.value(),
185 DHCP6.MsgType.DECLINE.value(),
186 DHCP6.MsgType.CONFIRM.value(),
187 DHCP6.MsgType.RELAY_FORW.value());
188 // SERVER message types
189 public static final Set<Byte> MSG_TYPE_FROM_SERVER =
190 ImmutableSet.of(DHCP6.MsgType.RELAY_REPL.value());
191
192 @Activate
193 protected void activate() {
Yi Tseng7da339e2017-10-23 19:39:39 -0700194 appId = coreService.registerApplication(DHCP_V6_RELAY_APP);
Yi Tseng4b013202017-09-08 17:22:51 -0700195 providerService = providerRegistry.register(this);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000196 hostService.addListener(hostListener);
Yi Tseng51301292017-07-28 13:02:59 -0700197 }
198
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000199 @Deactivate
200 protected void deactivate() {
Yi Tseng4b013202017-09-08 17:22:51 -0700201 providerRegistry.unregister(this);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000202 hostService.removeListener(hostListener);
Yi Tseng2fe8f3f2017-09-07 16:22:51 -0700203 defaultServerInfoList.forEach(this::stopMonitoringIps);
204 defaultServerInfoList.clear();
205 indirectServerInfoList.forEach(this::stopMonitoringIps);
206 indirectServerInfoList.clear();
207 }
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000208
Yi Tseng2fe8f3f2017-09-07 16:22:51 -0700209 private void stopMonitoringIps(DhcpServerInfo serverInfo) {
210 serverInfo.getDhcpGatewayIp6().ifPresent(gatewayIp -> {
211 hostService.stopMonitoringIp(gatewayIp);
212 });
213 serverInfo.getDhcpServerIp6().ifPresent(serverIp -> {
214 hostService.stopMonitoringIp(serverIp);
215 });
Yi Tseng51301292017-07-28 13:02:59 -0700216 }
217
Yi Tseng51301292017-07-28 13:02:59 -0700218 @Override
Yi Tseng2fe8f3f2017-09-07 16:22:51 -0700219 public List<DhcpServerInfo> getDefaultDhcpServerInfoList() {
220 return defaultServerInfoList;
Yi Tseng51301292017-07-28 13:02:59 -0700221 }
222
223 @Override
Yi Tseng2fe8f3f2017-09-07 16:22:51 -0700224 public List<DhcpServerInfo> getIndirectDhcpServerInfoList() {
225 return indirectServerInfoList;
Yi Tseng51301292017-07-28 13:02:59 -0700226 }
227
228 @Override
Yi Tseng7da339e2017-10-23 19:39:39 -0700229 public void updateIgnoreVlanConfig(IgnoreDhcpConfig config) {
230 if (config == null) {
231 ignoredVlans.forEach(((deviceId, vlanId) -> {
232 processIgnoreVlanRule(deviceId, vlanId, REMOVE);
233 }));
234 return;
235 }
236 config.ignoredVlans().forEach((deviceId, vlanId) -> {
237 if (ignoredVlans.get(deviceId).contains(vlanId)) {
238 // don't need to process if it already ignored
239 return;
240 }
241 processIgnoreVlanRule(deviceId, vlanId, ADD);
242 });
243
244 ignoredVlans.forEach((deviceId, vlanId) -> {
245 if (!config.ignoredVlans().get(deviceId).contains(vlanId)) {
246 // not contains in new config, remove it
247 processIgnoreVlanRule(deviceId, vlanId, REMOVE);
248 }
249 });
250 }
251
252 @Override
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000253 public void processDhcpPacket(PacketContext context, BasePacket payload) {
254 checkNotNull(payload, "DHCP6 payload can't be null");
255 checkState(payload instanceof DHCP6, "Payload is not a DHCP6");
256 DHCP6 dhcp6Payload = (DHCP6) payload;
257 Ethernet receivedPacket = context.inPacket().parsed();
258
259 if (!configured()) {
260 log.warn("Missing DHCP6 relay server config. Abort packet processing");
261 log.warn("dhcp6 payload {}", dhcp6Payload);
262
263 return;
264 }
265
266 byte msgType = dhcp6Payload.getMsgType();
267 log.warn("msgType is {}", msgType);
268
269 ConnectPoint inPort = context.inPacket().receivedFrom();
270 if (inPort == null) {
271 log.warn("incommin ConnectPoint is null");
272 }
273 Set<Interface> receivingInterfaces = interfaceService.getInterfacesByPort(inPort);
274 //ignore the packets if dhcp client interface is not configured on onos.
275 if (receivingInterfaces.isEmpty()) {
276 log.warn("Virtual interface is not configured on {}", inPort);
277 return;
278 }
279
280
281 if (MSG_TYPE_FROM_CLIENT.contains(msgType)) {
282
283 InternalPacket ethernetClientPacket =
284 processDhcp6PacketFromClient(context, receivedPacket, receivingInterfaces);
285 if (ethernetClientPacket != null) {
286 forwardPacket(ethernetClientPacket);
287 }
288
289 } else if (MSG_TYPE_FROM_SERVER.contains(msgType)) {
290 log.warn("calling processDhcp6PacketFromServer with RELAY_REPL", msgType);
291 InternalPacket ethernetPacketReply =
292 processDhcp6PacketFromServer(context, receivedPacket, receivingInterfaces);
293 if (ethernetPacketReply != null) {
294 forwardPacket(ethernetPacketReply);
295 }
296 } else {
297 log.warn("Not so fast, packet type {} not supported yet", msgType);
298 }
299 }
300
301
302 /**
303 * Checks if this app has been configured.
304 *
305 * @return true if all information we need have been initialized
306 */
307 public boolean configured() {
Yi Tseng2fe8f3f2017-09-07 16:22:51 -0700308 return !defaultServerInfoList.isEmpty();
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000309 }
310
Yi Tseng4b013202017-09-08 17:22:51 -0700311 @Override
312 public ProviderId id() {
Charles Chan75edab72017-09-12 17:09:32 -0700313 return PROVIDER_ID;
Yi Tseng4b013202017-09-08 17:22:51 -0700314 }
315
316 @Override
317 public void triggerProbe(Host host) {
318 // Do nothing here
319 }
320
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000321 // the new class the contains Ethernet packet and destination port, kind of like adding
322 // internal header to the packet
323 private class InternalPacket {
324 Ethernet packet;
325 ConnectPoint destLocation;
326 public InternalPacket(Ethernet newPacket, ConnectPoint newLocation) {
327 packet = newPacket;
328 destLocation = newLocation;
329 }
330 void setLocation(ConnectPoint newLocation) {
331 destLocation = newLocation;
332 }
333 }
334
335 //forward the packet to ConnectPoint where the DHCP server is attached.
336 private void forwardPacket(InternalPacket packet) {
337 //send Packetout to dhcp server connectpoint.
338 if (packet.destLocation != null) {
339 TrafficTreatment t = DefaultTrafficTreatment.builder()
340 .setOutput(packet.destLocation.port()).build();
341 OutboundPacket o = new DefaultOutboundPacket(
342 packet.destLocation.deviceId(), t, ByteBuffer.wrap(packet.packet.serialize()));
343 if (log.isTraceEnabled()) {
344 log.trace("Relaying packet to destination {}", packet.destLocation);
345 }
346 packetService.emit(o);
347 } // if
348 }
349
350 /**
351 * Check if the host is directly connected to the network or not.
352 *
353 * @param dhcp6Payload the dhcp6 payload
354 * @return true if the host is directly connected to the network; false otherwise
355 */
356 private boolean directlyConnected(DHCP6 dhcp6Payload) {
357 log.debug("directlyConnected enters");
358
359 if (dhcp6Payload.getMsgType() != DHCP6.MsgType.RELAY_FORW.value() &&
360 dhcp6Payload.getMsgType() != DHCP6.MsgType.RELAY_REPL.value()) {
361 log.debug("directlyConnected true. MsgType {}", dhcp6Payload.getMsgType());
362
363 return true;
364 }
365
366 // Regardless of relay-forward or relay-replay, check if we see another relay message
367 DHCP6 dhcp6Payload2 = dhcp6PacketFromRelayPacket(dhcp6Payload);
368 if (dhcp6Payload2 != null) {
369 if (dhcp6Payload.getMsgType() == DHCP6.MsgType.RELAY_FORW.value()) {
370 log.debug("directlyConnected false. 1st realy-foward, 2nd MsgType {}", dhcp6Payload2.getMsgType());
371 return false;
372 } else {
373 // relay-reply
374 if (dhcp6Payload2.getMsgType() != DHCP6.MsgType.RELAY_REPL.value()) {
375 log.debug("directlyConnected true. 2nd MsgType {}", dhcp6Payload2.getMsgType());
376 return true; // must be directly connected
377 } else {
378 log.debug("directlyConnected false. 1st relay-reply, 2nd relay-reply MsgType {}",
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000379 dhcp6Payload2.getMsgType());
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000380 return false; // must be indirectly connected
381 }
382 }
383 } else {
384 log.warn("directlyConnected true.");
385 return true;
386 }
387 }
388
389 /**
390 * extract DHCP6 payload from dhcp6 relay message within relay-forwrd/reply.
391 *
392 * @param dhcp6 dhcp6 relay-reply or relay-foward
393 * @return dhcp6Packet dhcp6 packet extracted from relay-message
394 */
395 private DHCP6 dhcp6PacketFromRelayPacket(DHCP6 dhcp6) {
396 log.debug("dhcp6PacketFromRelayPacket enters. dhcp6 {}", dhcp6);
397
398 // extract the relay message if exist
399 DHCP6 dhcp6Payload = dhcp6.getOptions().stream()
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000400 .filter(opt -> opt instanceof Dhcp6RelayOption)
401 .map(BasePacket::getPayload)
402 .map(pld -> (DHCP6) pld)
403 .findFirst()
404 .orElse(null);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000405
406
407 if (dhcp6Payload == null) {
408 // Can't find dhcp payload
409 log.debug("Can't find dhcp6 payload from relay message");
410 } else {
411 log.debug("dhcp6 payload found from relay message {}", dhcp6Payload);
412 }
413
414 return dhcp6Payload;
415 }
416
417 /**
418 * find the leaf DHCP6 packet from multi-level relay packet.
419 *
420 * @param relayPacket dhcp6 relay packet
421 * @return leafPacket non-relay dhcp6 packet
422 */
423 private DHCP6 getDhcp6Leaf(DHCP6 relayPacket) {
424 DHCP6 dhcp6Parent = relayPacket;
425 DHCP6 dhcp6Child = null;
426
427 log.debug("getDhcp6Leaf entered.");
428 while (dhcp6Parent != null) {
429 dhcp6Child = dhcp6PacketFromRelayPacket(dhcp6Parent);
430
431 if (dhcp6Child != null) {
432 if (dhcp6Child.getMsgType() != DHCP6.MsgType.RELAY_FORW.value() &&
433 dhcp6Child.getMsgType() != DHCP6.MsgType.RELAY_REPL.value()) {
434 log.debug("leaf dhcp6 packet found.");
435 break;
436 } else {
437 // found another relay
438 // go for another loop
439 dhcp6Parent = dhcp6Child;
440 }
441 } else {
442 log.warn("malformed pkt! Expected dhcp6 within relay pkt, but no dhcp6 leaf found.");
443 break;
444 }
445 }
446 return dhcp6Child;
447 }
448
449 /**
450 * check if DHCP6 relay-reply is reply.
451 *
452 * @param relayPacket dhcp6 relay-reply
453 * @return boolean relay-reply contains ack
454 */
455 private boolean isDhcp6Reply(DHCP6 relayPacket) {
456 log.debug("isDhcp6Reply entered.");
457
458 DHCP6 leafDhcp6 = getDhcp6Leaf(relayPacket);
459
460 if (leafDhcp6 != null) {
461 if (leafDhcp6.getMsgType() == DHCP6.MsgType.REPLY.value()) {
462 log.debug("isDhcp6Reply true.");
463 return true; // must be directly connected
464 } else {
465 log.debug("isDhcp6Reply false. leaf dhcp6 is not replay. MsgType {}", leafDhcp6.getMsgType());
466 }
467 } else {
468 log.debug("isDhcp6Reply false. Expected dhcp6 within relay pkt but not found.");
469 }
470 log.debug("isDhcp6Reply false.");
471 return false;
472 }
473
474 /**
475 * check if DHCP6 is release or relay-forward contains release.
476 *
477 * @param dhcp6Payload dhcp6 packet
478 * @return boolean dhcp6 contains release
479 */
480 private boolean isDhcp6Release(DHCP6 dhcp6Payload) {
481
482 log.debug("isDhcp6Release entered.");
483
484 if (dhcp6Payload.getMsgType() == DHCP6.MsgType.RELEASE.value()) {
485 log.debug("isDhcp6Release true.");
486 return true; // must be directly connected
487 } else {
488 DHCP6 dhcp6Leaf = getDhcp6Leaf(dhcp6Payload);
489 if (dhcp6Leaf != null) {
490 if (dhcp6Leaf.getMsgType() == DHCP6.MsgType.RELEASE.value()) {
491 log.debug("isDhcp6Release true. indirectlry connected");
492 return true;
493 } else {
494 log.debug("leaf dhcp6 is not release. MsgType {}", dhcp6Leaf.getMsgType());
495 return false;
496 }
497 } else {
498 log.debug("isDhcp6Release false. dhcp6 is niether relay nor release.");
499 return false;
500 }
501 }
502 }
503
504 /**
505 * extract from dhcp6 packet client ipv6 address of given by dhcp server.
506 *
507 * @param dhcp6 the dhcp6 packet
508 * @return Ip6Address Ip6Address given by dhcp server, or null if not exists
509 */
510 private Ip6Address extractIpAddress(DHCP6 dhcp6) {
511 Ip6Address ip = null;
512
513 log.debug("extractIpAddress enters dhcp6 {}.", dhcp6);
514 // Extract IPv6 address from IA NA ot IA TA option
515 Optional<Dhcp6IaNaOption> iaNaOption = dhcp6.getOptions()
516 .stream()
517 .filter(opt -> opt instanceof Dhcp6IaNaOption)
518 .map(opt -> (Dhcp6IaNaOption) opt)
519 .findFirst();
520 Optional<Dhcp6IaTaOption> iaTaOption = dhcp6.getOptions()
521 .stream()
522 .filter(opt -> opt instanceof Dhcp6IaTaOption)
523 .map(opt -> (Dhcp6IaTaOption) opt)
524 .findFirst();
525 Optional<Dhcp6IaAddressOption> iaAddressOption;
526 if (iaNaOption.isPresent()) {
527 log.debug("Found IPv6 address from iaNaOption {}", iaNaOption);
528
529 iaAddressOption = iaNaOption.get().getOptions().stream()
530 .filter(opt -> opt instanceof Dhcp6IaAddressOption)
531 .map(opt -> (Dhcp6IaAddressOption) opt)
532 .findFirst();
533 } else if (iaTaOption.isPresent()) {
534 log.debug("Found IPv6 address from iaTaOption {}", iaTaOption);
535
536 iaAddressOption = iaTaOption.get().getOptions().stream()
537 .filter(opt -> opt instanceof Dhcp6IaAddressOption)
538 .map(opt -> (Dhcp6IaAddressOption) opt)
539 .findFirst();
540 } else {
541 iaAddressOption = Optional.empty();
542 }
543 if (iaAddressOption.isPresent()) {
544 ip = iaAddressOption.get().getIp6Address();
545 log.debug("Found IPv6 address from iaAddressOption {}", iaAddressOption);
546
547
548 } else {
549 log.debug("Can't find IPv6 address from DHCPv6 {}", dhcp6);
550 }
551
552 return ip;
553 }
554 /**
555 * extract from dhcp6 packet Prefix prefix provided by dhcp server.
556 *
557 * @param dhcp6 the dhcp6 payload
558 * @return IpPrefix Prefix Delegation prefix, or null if not exists.
559 */
560 private IpPrefix extractPrefix(DHCP6 dhcp6) {
561 log.warn("extractPrefix enters {}", dhcp6);
562
563 // extract prefix
564 IpPrefix prefixPrefix = null;
565
566 Ip6Address prefixAddress = null;
567
568 // Extract IPv6 prefix from IA PD option
569 Optional<Dhcp6IaPdOption> iaPdOption = dhcp6.getOptions()
570 .stream()
571 .filter(opt -> opt instanceof Dhcp6IaPdOption)
572 .map(opt -> (Dhcp6IaPdOption) opt)
573 .findFirst();
574
575 Optional<Dhcp6IaPrefixOption> iaPrefixOption;
576 if (iaPdOption.isPresent()) {
577 log.warn("IA_PD option found {}", iaPdOption);
578
579 iaPrefixOption = iaPdOption.get().getOptions().stream()
580 .filter(opt -> opt instanceof Dhcp6IaPrefixOption)
581 .map(opt -> (Dhcp6IaPrefixOption) opt)
582 .findFirst();
583 } else {
584 log.warn("IA_PD option NOT found");
585
586 iaPrefixOption = Optional.empty();
587 }
588 if (iaPrefixOption.isPresent()) {
589 log.warn("IAPrefix Option within IA_PD option found {}", iaPrefixOption);
590
591 prefixAddress = iaPrefixOption.get().getIp6Prefix();
592 int prefixLen = (int) iaPrefixOption.get().getPrefixLength();
593 log.warn("Prefix length is {} bits", prefixLen);
594 prefixPrefix = IpPrefix.valueOf(prefixAddress, prefixLen);
595
596 } else {
597 log.warn("Can't find IPv6 prefix from DHCPv6 {}", dhcp6);
598 }
599
600 return prefixPrefix;
601 }
602
603 /**
604 * remove host or route.
605 *
606 * @param directConnFlag flag to show that packet is from directly connected client
607 * @param dhcp6Packet the dhcp6 payload
608 * @param clientPacket client's ethernet packet
609 * @param clientIpv6 client's Ipv6 packet
Kalhee Kimaa5172a2017-09-15 17:43:27 +0000610 * @param clientInterface client interfaces
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000611 */
612 private void removeHostOrRoute(boolean directConnFlag, DHCP6 dhcp6Packet,
613 Ethernet clientPacket, IPv6 clientIpv6,
Kalhee Kimaa5172a2017-09-15 17:43:27 +0000614 Interface clientInterface) {
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000615 log.debug("extractPrefix enters {}", dhcp6Packet);
Kalhee Kimc60c7e22017-11-01 17:56:44 +0000616 VlanId vlanId = clientInterface.vlan();
617 MacAddress clientMac = clientPacket.getSourceMAC();
618 log.debug("client mac {} client vlan {}", HexString.toHexString(clientMac.toBytes(), ":"), vlanId);
619
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000620 // add host or route
621 if (isDhcp6Release(dhcp6Packet)) {
622 IpAddress ip = null;
623 if (directConnFlag) {
624 // Add to host store if it is connected to network directly
625 ip = extractIpAddress(dhcp6Packet);
626 if (ip != null) {
Kalhee Kimc60c7e22017-11-01 17:56:44 +0000627
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000628 HostId hostId = HostId.hostId(clientMac, vlanId);
629 log.debug("remove Host {} ip for directly connected.", hostId.toString());
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000630 // Remove host's ip of when dhcp release msg is received
Yi Tseng4b013202017-09-08 17:22:51 -0700631 providerService.removeIpFromHost(hostId, ip);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000632 } else {
633 log.debug("ipAddress not found. Do not add Host for directly connected.");
634 }
635 } else {
636 // Remove from route store if it is not connected to network directly
Kalhee Kimc60c7e22017-11-01 17:56:44 +0000637 // pick out the first link-local ip address
638 IpAddress nextHopIp = getFirstIpByHost(clientMac, vlanId);
639 if (nextHopIp == null) {
640 log.warn("Can't find link-local IP address of gateway mac {} vlanId {}",
641 clientMac, vlanId);
642 return;
643 }
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000644
645 DHCP6 leafDhcp = getDhcp6Leaf(dhcp6Packet);
646 ip = extractIpAddress(leafDhcp);
647 if (ip == null) {
648 log.debug("ip is null");
649 } else {
650 Route routeForIP = new Route(Route.Source.STATIC, ip.toIpPrefix(), nextHopIp);
651 log.debug("removing route of 128 address for indirectly connected.");
652 log.debug("128 ip {}, nexthop {}", HexString.toHexString(ip.toOctets(), ":"),
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000653 HexString.toHexString(nextHopIp.toOctets(), ":"));
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000654 routeStore.removeRoute(routeForIP);
655 }
656
657 IpPrefix ipPrefix = extractPrefix(leafDhcp);
658 if (ipPrefix == null) {
659 log.debug("ipPrefix is null ");
660 } else {
661 Route routeForPrefix = new Route(Route.Source.STATIC, ipPrefix, nextHopIp);
662 log.debug("removing route of PD for indirectly connected.");
663 log.debug("pd ip {}, nexthop {}", HexString.toHexString(ipPrefix.address().toOctets(), ":"),
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000664 HexString.toHexString(nextHopIp.toOctets(), ":"));
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000665
666 routeStore.removeRoute(routeForPrefix);
667 }
668 }
669 }
670 }
671
672 /**
673 * add host or route.
674 *
675 * @param directConnFlag flag to show that packet is from directly connected client
676 * @param dhcp6Relay the dhcp6 payload
677 * @param embeddedDhcp6 client's ethernet packetthe dhcp6 payload within relay
678 * @param clientMac client macAddress
Kalhee Kimaa5172a2017-09-15 17:43:27 +0000679 * @param clientInterface client interface
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000680 */
681 private void addHostOrRoute(boolean directConnFlag, DHCP6 dhcp6Relay,
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000682 DHCP6 embeddedDhcp6,
683 MacAddress clientMac,
684 Interface clientInterface) {
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000685 log.debug("addHostOrRoute entered.");
Kalhee Kimc60c7e22017-11-01 17:56:44 +0000686 VlanId vlanId = clientInterface.vlan();
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000687 // add host or route
688 if (isDhcp6Reply(dhcp6Relay)) {
689 IpAddress ip = null;
690 if (directConnFlag) {
691 // Add to host store if it connect to network directly
692 ip = extractIpAddress(embeddedDhcp6);
693 if (ip != null) {
694 Set<IpAddress> ips = Sets.newHashSet(ip);
Yi Tseng4b013202017-09-08 17:22:51 -0700695
696 // FIXME: we should use vlan id from original packet (solicit, request)
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000697 HostId hostId = HostId.hostId(clientMac, vlanId);
Yi Tseng4b013202017-09-08 17:22:51 -0700698 Host host = hostService.getHost(hostId);
Kalhee Kimaa5172a2017-09-15 17:43:27 +0000699 HostLocation hostLocation = new HostLocation(clientInterface.connectPoint(),
Yi Tseng4b013202017-09-08 17:22:51 -0700700 System.currentTimeMillis());
701 Set<HostLocation> hostLocations = Sets.newHashSet(hostLocation);
702
703 if (host != null) {
704 // Dual homing support:
705 // if host exists, use old locations and new location
706 hostLocations.addAll(host.locations());
707 }
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000708 HostDescription desc = new DefaultHostDescription(clientMac, vlanId,
Yi Tseng4b013202017-09-08 17:22:51 -0700709 hostLocations, ips,
710 false);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000711 log.debug("adding Host for directly connected.");
712 log.debug("client mac {} client vlan {} hostlocation {}",
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000713 HexString.toHexString(clientMac.toBytes(), ":"),
714 vlanId, hostLocation.toString());
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000715
716 // Replace the ip when dhcp server give the host new ip address
Yi Tseng4b013202017-09-08 17:22:51 -0700717 providerService.hostDetected(hostId, desc, false);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000718 } else {
719 log.warn("ipAddress not found. Do not add Host for directly connected.");
720 }
721 } else {
722 // Add to route store if it does not connect to network directly
Kalhee Kimc60c7e22017-11-01 17:56:44 +0000723 // pick out the first link-local ip address
724 IpAddress nextHopIp = getFirstIpByHost(clientMac, vlanId);
725 if (nextHopIp == null) {
726 log.warn("Can't find link-local IP address of gateway mac {} vlanId {}",
727 clientMac, vlanId);
728 return;
729 }
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000730
731 DHCP6 leafDhcp = getDhcp6Leaf(embeddedDhcp6);
732 ip = extractIpAddress(leafDhcp);
733 if (ip == null) {
734 log.warn("ip is null");
735 } else {
736 Route routeForIP = new Route(Route.Source.STATIC, ip.toIpPrefix(), nextHopIp);
737 log.warn("adding Route of 128 address for indirectly connected.");
738 routeStore.updateRoute(routeForIP);
739 }
740
741 IpPrefix ipPrefix = extractPrefix(leafDhcp);
742 if (ipPrefix == null) {
743 log.warn("ipPrefix is null ");
744 } else {
745 Route routeForPrefix = new Route(Route.Source.STATIC, ipPrefix, nextHopIp);
746 log.warn("adding Route of PD for indirectly connected.");
747 routeStore.updateRoute(routeForPrefix);
748 }
749 }
750 }
751 }
752
753 /**
Yi Tsengc44dc2e2017-11-03 16:27:32 -0700754 * Build the DHCP6 solicit/request packet with gatewayip.
755 * TODO: method too long, need to be refactored.
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000756 *
757 * @param context packet context
758 * @param clientPacket client ethernet packet
759 * @param clientInterfaces set of client side interfaces
760 */
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000761 private InternalPacket processDhcp6PacketFromClient(PacketContext context,
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000762 Ethernet clientPacket, Set<Interface> clientInterfaces) {
Yi Tsengc44dc2e2017-11-03 16:27:32 -0700763 ConnectPoint receivedFrom = context.inPacket().receivedFrom();
764 DeviceId receivedFromDevice = receivedFrom.deviceId();
765 DhcpServerInfo serverInfo;
766 Ip6Address dhcpServerIp = null;
767 ConnectPoint dhcpServerConnectPoint = null;
768 MacAddress dhcpConnectMac = null;
769 VlanId dhcpConnectVlan = null;
770 Ip6Address dhcpGatewayIp = null;
771 Ip6Address indirectDhcpServerIp = null;
772 ConnectPoint indirectDhcpServerConnectPoint = null;
773 MacAddress indirectDhcpConnectMac = null;
774 VlanId indirectDhcpConnectVlan = null;
775 Ip6Address indirectDhcpGatewayIp = null;
776 Ip6Address indirectRelayAgentIpFromCfg = null;
777 if (!defaultServerInfoList.isEmpty()) {
778 serverInfo = defaultServerInfoList.get(0);
779 dhcpConnectMac = serverInfo.getDhcpConnectMac().orElse(null);
780 dhcpGatewayIp = serverInfo.getDhcpGatewayIp6().orElse(null);
781 dhcpServerIp = serverInfo.getDhcpServerIp6().orElse(null);
782 dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
783 dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
784 }
785 if (!indirectServerInfoList.isEmpty()) {
786 serverInfo = indirectServerInfoList.get(0);
787 indirectDhcpConnectMac = serverInfo.getDhcpConnectMac().orElse(null);
788 indirectDhcpGatewayIp = serverInfo.getDhcpGatewayIp6().orElse(null);
789 indirectDhcpServerIp = serverInfo.getDhcpServerIp6().orElse(null);
790 indirectDhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
791 indirectDhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
792 indirectRelayAgentIpFromCfg = serverInfo.getRelayAgentIp6(receivedFromDevice).orElse(null);
793 }
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000794 Ip6Address relayAgentIp = getRelayAgentIPv6Address(clientInterfaces);
795 MacAddress relayAgentMac = clientInterfaces.iterator().next().mac();
796 if (relayAgentIp == null || relayAgentMac == null) {
797 log.warn("Missing DHCP relay agent interface Ipv6 addr config for "
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000798 + "packet from client on port: {}. Aborting packet processing",
799 clientInterfaces.iterator().next().connectPoint());
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000800 return null;
801 }
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000802 // get dhcp6 header.
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000803 IPv6 clientIpv6 = (IPv6) clientPacket.getPayload();
804 UDP clientUdp = (UDP) clientIpv6.getPayload();
805 DHCP6 clientDhcp6 = (DHCP6) clientUdp.getPayload();
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000806 boolean directConnFlag = directlyConnected(clientDhcp6);
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000807 Interface serverInterface = directConnFlag ? getServerInterface() : getIndirectServerInterface();
808 if (serverInterface == null) {
809 log.warn("Can't get {} server interface, ignore", directConnFlag ? "direct" : "indirect");
810 return null;
811 }
812 Ip6Address ipFacingServer = getFirstIpFromInterface(serverInterface);
813 MacAddress macFacingServer = serverInterface.mac();
814 if (ipFacingServer == null || macFacingServer == null) {
815 log.warn("No IP v6 address for server Interface {}", serverInterface);
816 return null;
817 }
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000818 Ethernet etherReply = (Ethernet) clientPacket.clone();
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000819 etherReply.setSourceMACAddress(macFacingServer);
Yi Tsengc44dc2e2017-11-03 16:27:32 -0700820 if ((directConnFlag && dhcpConnectMac == null) ||
821 !directConnFlag && indirectDhcpConnectMac == null && dhcpConnectMac == null) {
Kalhee Kim2575d2c2017-09-26 20:21:53 +0000822 log.warn("Packet received from {} connected client.", directConnFlag ? "directly" : "indirectly");
Charles Chanf6a77be2017-10-30 13:44:33 -0700823 log.warn("DHCP6 {} not yet resolved .. Aborting DHCP packet processing from client on port: {}",
Yi Tsengc44dc2e2017-11-03 16:27:32 -0700824 (dhcpGatewayIp == null) ? "server IP " + dhcpServerIp
825 : "gateway IP " + dhcpGatewayIp,
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000826 clientInterfaces.iterator().next().connectPoint());
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000827 return null;
828 }
Yi Tsengc44dc2e2017-11-03 16:27:32 -0700829 if (dhcpServerConnectPoint == null) {
Kalhee Kim2575d2c2017-09-26 20:21:53 +0000830 log.warn("DHCP6 server connection point direct {} directConn {} indirectConn {} is not set up yet",
Yi Tsengc44dc2e2017-11-03 16:27:32 -0700831 directConnFlag, dhcpServerConnectPoint, indirectDhcpServerConnectPoint);
832
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000833 return null;
834 }
835
Yi Tsengc44dc2e2017-11-03 16:27:32 -0700836 etherReply.setDestinationMACAddress(dhcpConnectMac);
837 etherReply.setVlanID(dhcpConnectVlan.toShort());
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000838
839 IPv6 ipv6Packet = (IPv6) etherReply.getPayload();
Kalhee Kimc60c7e22017-11-01 17:56:44 +0000840 byte[] peerAddress = clientIpv6.getSourceAddress();
Kalhee Kim45c93852017-10-27 20:16:26 +0000841 ipv6Packet.setSourceAddress(ipFacingServer.toOctets());
Yi Tsengc44dc2e2017-11-03 16:27:32 -0700842 ipv6Packet.setDestinationAddress(dhcpServerIp.toOctets());
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000843
844 UDP udpPacket = (UDP) ipv6Packet.getPayload();
845 udpPacket.setSourcePort(UDP.DHCP_V6_SERVER_PORT);
846 DHCP6 dhcp6Packet = (DHCP6) udpPacket.getPayload();
847 byte[] dhcp6PacketByte = dhcp6Packet.serialize();
848
849 // notify onos and quagga to release PD
850 //releasePD(dhcp6Packet);
Kalhee Kimaa5172a2017-09-15 17:43:27 +0000851 ConnectPoint clientConnectionPoint = context.inPacket().receivedFrom();
852 VlanId vlanIdInUse = VlanId.vlanId(clientPacket.getVlanID());
853 Interface clientInterface = interfaceService.getInterfacesByPort(clientConnectionPoint)
Charles Chanf6a77be2017-10-30 13:44:33 -0700854 .stream().filter(iface -> interfaceContainsVlan(iface, vlanIdInUse))
855 .findFirst().orElse(null);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000856
Kalhee Kimaa5172a2017-09-15 17:43:27 +0000857 removeHostOrRoute(directConnFlag, dhcp6Packet, clientPacket, clientIpv6, clientInterface);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000858
859 DHCP6 dhcp6Relay = new DHCP6();
860 dhcp6Relay.setMsgType(DHCP6.MsgType.RELAY_FORW.value());
861 // link address: server uses the address to identify the link on which the client
862 // is located.
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000863 if (directConnFlag) {
864 dhcp6Relay.setLinkAddress(relayAgentIp.toOctets());
865 log.debug("direct connection: relayAgentIp obtained dynamically {}",
866 HexString.toHexString(relayAgentIp.toOctets(), ":"));
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000867
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000868 } else {
Yi Tsengc44dc2e2017-11-03 16:27:32 -0700869 if (indirectDhcpServerIp == null) {
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000870 log.warn("indirect DhcpServerIp not available, use default DhcpServerIp {}",
Yi Tsengc44dc2e2017-11-03 16:27:32 -0700871 HexString.toHexString(dhcpServerIp.toOctets()));
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000872 } else {
873 // Indirect case, replace destination to indirect dhcp server if exist
874 // Check if mac is obtained for valid server ip
Yi Tsengc44dc2e2017-11-03 16:27:32 -0700875 if (indirectDhcpConnectMac == null) {
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000876 log.warn("DHCP6 {} not yet resolved .. Aborting DHCP "
877 + "packet processing from client on port: {}",
Yi Tsengc44dc2e2017-11-03 16:27:32 -0700878 (indirectDhcpGatewayIp == null) ? "server IP " + indirectDhcpServerIp
879 : "gateway IP " + indirectDhcpGatewayIp,
Kalhee Kim2575d2c2017-09-26 20:21:53 +0000880 clientInterfaces.iterator().next().connectPoint());
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000881 return null;
882 }
Yi Tsengc44dc2e2017-11-03 16:27:32 -0700883 etherReply.setDestinationMACAddress(indirectDhcpConnectMac);
884 etherReply.setVlanID(indirectDhcpConnectVlan.toShort());
885 ipv6Packet.setDestinationAddress(indirectDhcpServerIp.toOctets());
Kalhee Kim2575d2c2017-09-26 20:21:53 +0000886
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000887 }
Yi Tsengc44dc2e2017-11-03 16:27:32 -0700888 if (indirectRelayAgentIpFromCfg == null) {
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000889 dhcp6Relay.setLinkAddress(relayAgentIp.toOctets());
890 log.warn("indirect connection: relayAgentIp NOT availale from config file! Use dynamic. {}",
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000891 HexString.toHexString(relayAgentIp.toOctets(), ":"));
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000892 } else {
Yi Tsengc44dc2e2017-11-03 16:27:32 -0700893 dhcp6Relay.setLinkAddress(indirectRelayAgentIpFromCfg.toOctets());
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000894 log.debug("indirect connection: relayAgentIp from config file is available! {}",
Yi Tsengc44dc2e2017-11-03 16:27:32 -0700895 HexString.toHexString(indirectRelayAgentIpFromCfg.toOctets(), ":"));
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000896 }
897 }
Yi Tsengc44dc2e2017-11-03 16:27:32 -0700898 // peer address: address of the client or relay agent from which
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000899 // the message to be relayed was received.
Kalhee Kimc60c7e22017-11-01 17:56:44 +0000900 dhcp6Relay.setPeerAddress(peerAddress);
Yi Tsengc44dc2e2017-11-03 16:27:32 -0700901 List<Dhcp6Option> options = new ArrayList<>();
902 // directly connected case, hop count is zero; otherwise, hop count + 1
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000903 if (directConnFlag) {
904 dhcp6Relay.setHopCount((byte) 0);
905 } else {
906 dhcp6Relay.setHopCount((byte) (dhcp6Packet.getHopCount() + 1));
907 }
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000908 // create relay message option
909 Dhcp6Option relayMessage = new Dhcp6Option();
910 relayMessage.setCode(DHCP6.OptionCode.RELAY_MSG.value());
911 relayMessage.setLength((short) dhcp6PacketByte.length);
912 relayMessage.setData(dhcp6PacketByte);
913 options.add(relayMessage);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000914 // create interfaceId option
Kalhee Kimaa5172a2017-09-15 17:43:27 +0000915 String inPortString = "-" + context.inPacket().receivedFrom().toString() + ":";
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000916 Dhcp6Option interfaceId = new Dhcp6Option();
917 interfaceId.setCode(DHCP6.OptionCode.INTERFACE_ID.value());
918 byte[] clientSoureMacBytes = clientPacket.getSourceMACAddress();
919 byte[] inPortStringBytes = inPortString.getBytes();
Kalhee Kimaa5172a2017-09-15 17:43:27 +0000920 byte[] vlanIdBytes = new byte[2];
921 vlanIdBytes[0] = (byte) (clientPacket.getVlanID() & 0xff);
922 vlanIdBytes[1] = (byte) ((clientPacket.getVlanID() >> 8) & 0xff);
923 byte[] interfaceIdBytes = new byte[clientSoureMacBytes.length +
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000924 inPortStringBytes.length + vlanIdBytes.length];
Kalhee Kimaa5172a2017-09-15 17:43:27 +0000925 log.debug("Length: interfaceIdBytes {} clientSoureMacBytes {} inPortStringBytes {} vlan {}",
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000926 interfaceIdBytes.length, clientSoureMacBytes.length, inPortStringBytes.length,
927 vlanIdBytes.length);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000928 System.arraycopy(clientSoureMacBytes, 0, interfaceIdBytes, 0, clientSoureMacBytes.length);
929 System.arraycopy(inPortStringBytes, 0, interfaceIdBytes, clientSoureMacBytes.length, inPortStringBytes.length);
Kalhee Kimaa5172a2017-09-15 17:43:27 +0000930 System.arraycopy(vlanIdBytes, 0, interfaceIdBytes, clientSoureMacBytes.length + inPortStringBytes.length,
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000931 vlanIdBytes.length);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000932 interfaceId.setData(interfaceIdBytes);
933 interfaceId.setLength((short) interfaceIdBytes.length);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000934 options.add(interfaceId);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000935 log.debug("interfaceId write srcMac {} portString {}",
936 HexString.toHexString(clientSoureMacBytes, ":"), inPortString);
937 dhcp6Relay.setOptions(options);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000938 udpPacket.setPayload(dhcp6Relay);
939 udpPacket.resetChecksum();
940 ipv6Packet.setPayload(udpPacket);
Charles Chan7edf7642017-10-09 11:07:25 -0400941 ipv6Packet.setHopLimit((byte) 64);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000942 etherReply.setPayload(ipv6Packet);
Yi Tsengc44dc2e2017-11-03 16:27:32 -0700943 if (directConnFlag || indirectDhcpServerIp == null) {
944 return new InternalPacket(etherReply, dhcpServerConnectPoint);
Kalhee Kim2575d2c2017-09-26 20:21:53 +0000945 } else {
Yi Tsengc44dc2e2017-11-03 16:27:32 -0700946 return new InternalPacket(etherReply, indirectDhcpServerConnectPoint);
Kalhee Kim2575d2c2017-09-26 20:21:53 +0000947 }
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000948 }
949
Yi Tsengc44dc2e2017-11-03 16:27:32 -0700950
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000951 /**
952 *
953 * process the DHCP6 relay-reply packet from dhcp server.
954 *
955 * @param context packet context
956 * @param receivedPacket server ethernet packet
957 * @param recevingInterfaces set of server side interfaces
958 */
959 private InternalPacket processDhcp6PacketFromServer(PacketContext context,
960 Ethernet receivedPacket, Set<Interface> recevingInterfaces) {
Yi Tsengc44dc2e2017-11-03 16:27:32 -0700961
962 ConnectPoint receivedFrom = context.inPacket().receivedFrom();
963 DeviceId receivedFromDevice = receivedFrom.deviceId();
964
965 // TODO: refactor
966 DhcpServerInfo serverInfo;
967 Ip6Address dhcpServerIp = null;
968 ConnectPoint dhcpServerConnectPoint = null;
969 MacAddress dhcpConnectMac = null;
970 VlanId dhcpConnectVlan = null;
971 Ip6Address dhcpGatewayIp = null;
972
973 Ip6Address indirectDhcpServerIp = null;
974 ConnectPoint indirectDhcpServerConnectPoint = null;
975 MacAddress indirectDhcpConnectMac = null;
976 VlanId indirectDhcpConnectVlan = null;
977 Ip6Address indirectDhcpGatewayIp = null;
978 Ip6Address indirectRelayAgentIpFromCfg = null;
979
980 if (!defaultServerInfoList.isEmpty()) {
981 serverInfo = defaultServerInfoList.get(0);
982 dhcpConnectMac = serverInfo.getDhcpConnectMac().orElse(null);
983 dhcpGatewayIp = serverInfo.getDhcpGatewayIp6().orElse(null);
984 dhcpServerIp = serverInfo.getDhcpServerIp6().orElse(null);
985 dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
986 dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
987 }
988
989 if (!indirectServerInfoList.isEmpty()) {
990 serverInfo = indirectServerInfoList.get(0);
991 indirectDhcpConnectMac = serverInfo.getDhcpConnectMac().orElse(null);
992 indirectDhcpGatewayIp = serverInfo.getDhcpGatewayIp6().orElse(null);
993 indirectDhcpServerIp = serverInfo.getDhcpServerIp6().orElse(null);
994 indirectDhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
995 indirectDhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
996 indirectRelayAgentIpFromCfg = serverInfo.getRelayAgentIp6(receivedFromDevice).orElse(null);
997 }
998
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000999 // get dhcp6 header.
1000 Ethernet etherReply = (Ethernet) receivedPacket.clone();
1001 IPv6 ipv6Packet = (IPv6) etherReply.getPayload();
1002 UDP udpPacket = (UDP) ipv6Packet.getPayload();
1003 DHCP6 dhcp6Relay = (DHCP6) udpPacket.getPayload();
1004
1005 Boolean directConnFlag = directlyConnected(dhcp6Relay);
Kalhee Kim2575d2c2017-09-26 20:21:53 +00001006 ConnectPoint inPort = context.inPacket().receivedFrom();
Yi Tsengc44dc2e2017-11-03 16:27:32 -07001007 if ((directConnFlag || (!directConnFlag && indirectDhcpServerIp == null))
1008 && !inPort.equals(dhcpServerConnectPoint)) {
Kalhee Kim2575d2c2017-09-26 20:21:53 +00001009 log.warn("Receiving port {} is not the same as server connect point {} for direct or indirect-null",
Yi Tsengc44dc2e2017-11-03 16:27:32 -07001010 inPort, dhcpServerConnectPoint);
Kalhee Kim2575d2c2017-09-26 20:21:53 +00001011 return null;
1012 }
1013
Yi Tsengc44dc2e2017-11-03 16:27:32 -07001014 if (!directConnFlag && indirectDhcpServerIp != null &&
1015 !inPort.equals(indirectDhcpServerConnectPoint)) {
Kalhee Kim2575d2c2017-09-26 20:21:53 +00001016 log.warn("Receiving port {} is not the same as server connect point {} for indirect",
Yi Tsengc44dc2e2017-11-03 16:27:32 -07001017 inPort, indirectDhcpServerConnectPoint);
Kalhee Kim2575d2c2017-09-26 20:21:53 +00001018 return null;
1019 }
1020
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001021
1022 Dhcp6InterfaceIdOption interfaceIdOption = dhcp6Relay.getOptions().stream()
1023 .filter(opt -> opt instanceof Dhcp6InterfaceIdOption)
1024 .map(opt -> (Dhcp6InterfaceIdOption) opt)
1025 .findFirst()
1026 .orElse(null);
1027
1028 if (interfaceIdOption == null) {
1029 log.warn("Interface Id option is not present, abort packet...");
1030 return null;
1031 }
1032
1033 MacAddress peerMac = interfaceIdOption.getMacAddress();
1034 String clientConnectionPointStr = new String(interfaceIdOption.getInPort());
1035
1036 ConnectPoint clientConnectionPoint = ConnectPoint.deviceConnectPoint(clientConnectionPointStr);
Kalhee Kimaa5172a2017-09-15 17:43:27 +00001037 VlanId vlanIdInUse = VlanId.vlanId(interfaceIdOption.getVlanId());
1038 Interface clientInterface = interfaceService.getInterfacesByPort(clientConnectionPoint)
1039 .stream()
1040 .filter(iface -> interfaceContainsVlan(iface, vlanIdInUse))
1041 .findFirst()
1042 .orElse(null);
1043 if (clientInterface == null) {
1044 log.warn("Cannot get client interface for from packet, abort... vlan {}", vlanIdInUse.toString());
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001045 return null;
1046 }
Kalhee Kimaa5172a2017-09-15 17:43:27 +00001047 MacAddress relayAgentMac = clientInterface.mac();
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001048 if (relayAgentMac == null) {
1049 log.warn("Can not get interface mac, abort packet..");
1050 return null;
1051 }
1052 etherReply.setSourceMACAddress(relayAgentMac);
1053
1054 // find destMac
1055 MacAddress clientMac = null;
Yi Tseng4b013202017-09-08 17:22:51 -07001056 Ip6Address peerAddress = Ip6Address.valueOf(dhcp6Relay.getPeerAddress());
1057 Set<Host> clients = hostService.getHostsByIp(peerAddress);
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001058 if (clients.isEmpty()) {
1059 log.warn("There's no host found for this address {}",
Kalhee Kim683ba3b2017-10-18 18:30:23 +00001060 HexString.toHexString(dhcp6Relay.getPeerAddress(), ":"));
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001061 log.warn("Let's look up interfaceId {}", HexString.toHexString(peerMac.toBytes(), ":"));
1062 clientMac = peerMac;
1063 } else {
1064 clientMac = clients.iterator().next().mac();
1065 if (clientMac == null) {
1066 log.warn("No client mac address found, abort packet...");
1067 return null;
1068 }
1069 log.warn("Client mac address found from getHostByIp");
1070
1071 }
1072 etherReply.setDestinationMACAddress(clientMac);
1073
1074 // ip header
1075 ipv6Packet.setSourceAddress(dhcp6Relay.getLinkAddress());
1076 ipv6Packet.setDestinationAddress(dhcp6Relay.getPeerAddress());
1077 // udp header
1078 udpPacket.setSourcePort(UDP.DHCP_V6_SERVER_PORT);
1079 if (directConnFlag) {
1080 udpPacket.setDestinationPort(UDP.DHCP_V6_CLIENT_PORT);
1081 } else {
1082 udpPacket.setDestinationPort(UDP.DHCP_V6_SERVER_PORT);
1083 }
1084
1085 DHCP6 embeddedDhcp6 = dhcp6Relay.getOptions().stream()
Kalhee Kim683ba3b2017-10-18 18:30:23 +00001086 .filter(opt -> opt instanceof Dhcp6RelayOption)
1087 .map(BasePacket::getPayload)
1088 .map(pld -> (DHCP6) pld)
1089 .findFirst()
1090 .orElse(null);
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001091
1092
1093 // add host or route
Kalhee Kimaa5172a2017-09-15 17:43:27 +00001094 addHostOrRoute(directConnFlag, dhcp6Relay, embeddedDhcp6, clientMac, clientInterface);
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001095
1096 udpPacket.setPayload(embeddedDhcp6);
1097 udpPacket.resetChecksum();
1098 ipv6Packet.setPayload(udpPacket);
1099 etherReply.setPayload(ipv6Packet);
1100
1101 return new InternalPacket(etherReply, clientConnectionPoint);
1102 }
1103
Yi Tseng2fe8f3f2017-09-07 16:22:51 -07001104 // Returns the first v6 interface ip out of a set of interfaces or null.
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001105 // Checks all interfaces, and ignores v6 interface ips
1106 private Ip6Address getRelayAgentIPv6Address(Set<Interface> intfs) {
1107 for (Interface intf : intfs) {
1108 for (InterfaceIpAddress ip : intf.ipAddressesList()) {
1109 Ip6Address relayAgentIp = ip.ipAddress().getIp6Address();
1110 if (relayAgentIp != null) {
1111 return relayAgentIp;
1112 }
1113 }
1114 }
1115 return null;
Yi Tseng51301292017-07-28 13:02:59 -07001116 }
Yi Tsenge72fbb52017-08-02 15:03:31 -07001117
1118 @Override
1119 public void setDefaultDhcpServerConfigs(Collection<DhcpServerConfig> configs) {
Yi Tseng2fe8f3f2017-09-07 16:22:51 -07001120 setDhcpServerConfigs(configs, defaultServerInfoList);
Yi Tseng2fe8f3f2017-09-07 16:22:51 -07001121 }
1122
1123 @Override
1124 public void setIndirectDhcpServerConfigs(Collection<DhcpServerConfig> configs) {
1125 setDhcpServerConfigs(configs, indirectServerInfoList);
Yi Tseng2fe8f3f2017-09-07 16:22:51 -07001126 }
1127
1128 public void setDhcpServerConfigs(Collection<DhcpServerConfig> configs, List<DhcpServerInfo> serverInfoList) {
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001129 if (configs.size() == 0) {
1130 // no config to update
1131 return;
1132 }
1133
1134 // TODO: currently we pick up first DHCP server config.
1135 // Will use other server configs in the future for HA.
1136 DhcpServerConfig serverConfig = configs.iterator().next();
Yi Tseng2fe8f3f2017-09-07 16:22:51 -07001137
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001138 if (!serverConfig.getDhcpServerIp6().isPresent()) {
Yi Tseng2fe8f3f2017-09-07 16:22:51 -07001139 // not a DHCPv6 config
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001140 return;
1141 }
1142
Yi Tseng2fe8f3f2017-09-07 16:22:51 -07001143 if (!serverInfoList.isEmpty()) {
1144 // remove old server info
1145 DhcpServerInfo oldServerInfo = serverInfoList.remove(0);
1146
1147 // stop monitoring gateway or server
1148 oldServerInfo.getDhcpGatewayIp6().ifPresent(gatewayIp -> {
1149 hostService.stopMonitoringIp(gatewayIp);
1150 });
1151 oldServerInfo.getDhcpServerIp6().ifPresent(serverIp -> {
1152 hostService.stopMonitoringIp(serverIp);
Yi Tseng7da339e2017-10-23 19:39:39 -07001153 cancelDhcpPacket(serverIp);
Yi Tseng2fe8f3f2017-09-07 16:22:51 -07001154 });
1155 }
1156
1157 // Create new server info according to the config
1158 DhcpServerInfo newServerInfo = new DhcpServerInfo(serverConfig,
1159 DhcpServerInfo.Version.DHCP_V6);
1160 checkState(newServerInfo.getDhcpServerConnectPoint().isPresent(),
1161 "Connect point not exists");
1162 checkState(newServerInfo.getDhcpServerIp6().isPresent(),
1163 "IP of DHCP server not exists");
1164
1165 log.debug("DHCP server connect point: {}", newServerInfo.getDhcpServerConnectPoint().orElse(null));
1166 log.debug("DHCP server IP: {}", newServerInfo.getDhcpServerIp6().orElse(null));
1167
Yi Tseng7da339e2017-10-23 19:39:39 -07001168 Ip6Address serverIp = newServerInfo.getDhcpServerIp6().get();
1169 Ip6Address ipToProbe;
Yi Tseng2fe8f3f2017-09-07 16:22:51 -07001170 if (newServerInfo.getDhcpGatewayIp6().isPresent()) {
1171 ipToProbe = newServerInfo.getDhcpGatewayIp6().get();
1172 } else {
1173 ipToProbe = newServerInfo.getDhcpServerIp6().orElse(null);
1174 }
1175 String hostToProbe = newServerInfo.getDhcpGatewayIp6()
1176 .map(ip -> "gateway").orElse("server");
1177
1178 log.debug("Probing to resolve {} IP {}", hostToProbe, ipToProbe);
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001179 hostService.startMonitoringIp(ipToProbe);
1180
1181 Set<Host> hosts = hostService.getHostsByIp(ipToProbe);
1182 if (!hosts.isEmpty()) {
1183 Host host = hosts.iterator().next();
Yi Tseng2fe8f3f2017-09-07 16:22:51 -07001184 newServerInfo.setDhcpConnectVlan(host.vlan());
1185 newServerInfo.setDhcpConnectMac(host.mac());
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001186 }
Yi Tseng2fe8f3f2017-09-07 16:22:51 -07001187 // Add new server info
Yi Tseng053682b2017-11-09 13:54:12 -08001188 synchronized (this) {
1189 serverInfoList.clear();
1190 serverInfoList.add(0, newServerInfo);
1191 }
Yi Tseng7da339e2017-10-23 19:39:39 -07001192 requestDhcpPacket(serverIp);
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001193 }
1194
1195 class InternalHostListener implements HostListener {
1196 @Override
1197 public void event(HostEvent event) {
1198 switch (event.type()) {
1199 case HOST_ADDED:
1200 case HOST_UPDATED:
1201 hostUpdated(event.subject());
1202 break;
1203 case HOST_REMOVED:
1204 hostRemoved(event.subject());
1205 break;
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001206 default:
1207 break;
1208 }
1209 }
1210 }
1211
1212 /**
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001213 * Handle host updated.
1214 * If the host is DHCP server or gateway, update connect mac and vlan.
1215 *
1216 * @param host the host
1217 */
1218 private void hostUpdated(Host host) {
Yi Tseng7da339e2017-10-23 19:39:39 -07001219 hostUpdated(host, defaultServerInfoList);
1220 hostUpdated(host, indirectServerInfoList);
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001221 }
1222
Yi Tseng7da339e2017-10-23 19:39:39 -07001223 private void hostUpdated(Host host, List<DhcpServerInfo> serverInfoList) {
1224 DhcpServerInfo serverInfo;
1225 Ip6Address targetIp;
1226 if (!serverInfoList.isEmpty()) {
1227 serverInfo = serverInfoList.get(0);
1228 Ip6Address serverIp = serverInfo.getDhcpServerIp6().orElse(null);
1229 targetIp = serverInfo.getDhcpGatewayIp6().orElse(null);
1230
1231 if (targetIp == null) {
1232 targetIp = serverIp;
1233 }
1234
1235 if (targetIp != null) {
1236 if (host.ipAddresses().contains(targetIp)) {
1237 serverInfo.setDhcpConnectMac(host.mac());
1238 serverInfo.setDhcpConnectVlan(host.vlan());
1239 requestDhcpPacket(serverIp);
1240 }
1241 }
1242 }
1243 }
1244
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001245 /**
1246 * Handle host removed.
1247 * If the host is DHCP server or gateway, unset connect mac and vlan.
1248 *
1249 * @param host the host
1250 */
1251 private void hostRemoved(Host host) {
Yi Tseng7da339e2017-10-23 19:39:39 -07001252 hostRemoved(host, defaultServerInfoList);
1253 hostRemoved(host, indirectServerInfoList);
Yi Tseng2fe8f3f2017-09-07 16:22:51 -07001254 }
1255
Yi Tseng7da339e2017-10-23 19:39:39 -07001256 private void hostRemoved(Host host, List<DhcpServerInfo> serverInfoList) {
1257 DhcpServerInfo serverInfo;
1258 Ip6Address targetIp;
1259
1260 if (!serverInfoList.isEmpty()) {
1261 serverInfo = serverInfoList.get(0);
1262 Ip6Address serverIp = serverInfo.getDhcpServerIp6().orElse(null);
1263 targetIp = serverInfo.getDhcpGatewayIp6().orElse(null);
1264
1265 if (targetIp == null) {
1266 targetIp = serverIp;
1267 }
1268
1269 if (targetIp != null) {
1270 if (host.ipAddresses().contains(targetIp)) {
1271 serverInfo.setDhcpConnectVlan(null);
1272 serverInfo.setDhcpConnectMac(null);
1273 cancelDhcpPacket(serverIp);
1274 }
1275 }
1276 }
1277 }
1278
Kalhee Kim683ba3b2017-10-18 18:30:23 +00001279 /**
1280 * Returns the first interface ip from interface.
1281 *
1282 * @param iface interface of one connect point
1283 * @return the first interface IP; null if not exists an IP address in
1284 * these interfaces
1285 */
1286 private Ip6Address getFirstIpFromInterface(Interface iface) {
1287 checkNotNull(iface, "Interface can't be null");
1288 return iface.ipAddressesList().stream()
1289 .map(InterfaceIpAddress::ipAddress)
1290 .filter(IpAddress::isIp6)
1291 .map(IpAddress::getIp6Address)
1292 .findFirst()
1293 .orElse(null);
1294 }
1295
1296 /**
1297 * Gets Interface facing to the server for default host.
1298 *
1299 * @return the Interface facing to the server; null if not found
1300 */
1301 private Interface getServerInterface() {
Yi Tsengc44dc2e2017-11-03 16:27:32 -07001302 DhcpServerInfo serverInfo;
1303 ConnectPoint dhcpServerConnectPoint;
1304 VlanId dhcpConnectVlan;
1305
1306 if (!defaultServerInfoList.isEmpty()) {
1307 serverInfo = defaultServerInfoList.get(0);
1308 dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1309 dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1310 } else {
Kalhee Kim683ba3b2017-10-18 18:30:23 +00001311 return null;
1312 }
Yi Tseng8f49a732017-11-17 17:14:41 -08001313 if (dhcpServerConnectPoint == null || dhcpConnectVlan == null) {
1314 log.info("Default DHCP server {} not resolve yet", serverInfo.getDhcpGatewayIp6());
1315 return null;
1316 }
Kalhee Kim683ba3b2017-10-18 18:30:23 +00001317 return interfaceService.getInterfacesByPort(dhcpServerConnectPoint)
1318 .stream()
1319 .filter(iface -> interfaceContainsVlan(iface, dhcpConnectVlan))
1320 .findFirst()
1321 .orElse(null);
1322 }
1323
1324 /**
1325 * Gets Interface facing to the server for indirect hosts.
1326 * Use default server Interface if indirect server not configured.
1327 *
1328 * @return the Interface facing to the server; null if not found
1329 */
1330 private Interface getIndirectServerInterface() {
Yi Tsengc44dc2e2017-11-03 16:27:32 -07001331 DhcpServerInfo serverInfo;
1332
1333 ConnectPoint indirectDhcpServerConnectPoint;
1334 VlanId indirectDhcpConnectVlan;
1335
1336 if (!indirectServerInfoList.isEmpty()) {
1337 serverInfo = indirectServerInfoList.get(0);
1338 indirectDhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1339 indirectDhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1340 } else {
Kalhee Kim683ba3b2017-10-18 18:30:23 +00001341 return getServerInterface();
1342 }
Yi Tseng8f49a732017-11-17 17:14:41 -08001343 if (indirectDhcpServerConnectPoint == null || indirectDhcpConnectVlan == null) {
1344 log.info("Indirect DHCP server {} not resolve yet", serverInfo.getDhcpGatewayIp6());
1345 return null;
1346 }
Kalhee Kim683ba3b2017-10-18 18:30:23 +00001347 return interfaceService.getInterfacesByPort(indirectDhcpServerConnectPoint)
1348 .stream()
1349 .filter(iface -> interfaceContainsVlan(iface, indirectDhcpConnectVlan))
1350 .findFirst()
1351 .orElse(null);
1352 }
1353
Kalhee Kimaa5172a2017-09-15 17:43:27 +00001354 /**
1355 * Determind if an Interface contains a vlan id.
1356 *
1357 * @param iface the Interface
1358 * @param vlanId the vlan id
1359 * @return true if the Interface contains the vlan id
1360 */
1361 private boolean interfaceContainsVlan(Interface iface, VlanId vlanId) {
1362 if (vlanId.equals(VlanId.NONE)) {
1363 // untagged packet, check if vlan untagged or vlan native is not NONE
1364 return !iface.vlanUntagged().equals(VlanId.NONE) ||
1365 !iface.vlanNative().equals(VlanId.NONE);
1366 }
1367 // tagged packet, check if the interface contains the vlan
1368 return iface.vlanTagged().contains(vlanId);
1369 }
1370
Yi Tseng7da339e2017-10-23 19:39:39 -07001371 private void requestDhcpPacket(Ip6Address serverIp) {
1372 requestServerDhcpPacket(serverIp);
1373 requestClientDhcpPacket(serverIp);
1374 }
1375
1376 private void cancelDhcpPacket(Ip6Address serverIp) {
1377 cancelServerDhcpPacket(serverIp);
1378 cancelClientDhcpPacket(serverIp);
1379 }
1380
1381 private void cancelServerDhcpPacket(Ip6Address serverIp) {
1382 TrafficSelector serverSelector =
1383 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1384 .matchIPv6Src(serverIp.toIpPrefix())
1385 .build();
1386 packetService.cancelPackets(serverSelector,
1387 PacketPriority.CONTROL,
1388 appId);
1389 }
1390
1391 private void requestServerDhcpPacket(Ip6Address serverIp) {
1392 TrafficSelector serverSelector =
1393 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1394 .matchIPv6Src(serverIp.toIpPrefix())
1395 .build();
1396 packetService.requestPackets(serverSelector,
1397 PacketPriority.CONTROL,
1398 appId);
1399 }
1400
1401 private void cancelClientDhcpPacket(Ip6Address serverIp) {
1402 // Packet comes from relay
1403 TrafficSelector indirectClientSelector =
1404 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1405 .matchIPv6Dst(serverIp.toIpPrefix())
1406 .build();
1407 packetService.cancelPackets(indirectClientSelector,
1408 PacketPriority.CONTROL,
1409 appId);
Yi Tseng3f2119b2017-11-02 15:21:10 -07001410 indirectClientSelector =
1411 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1412 .matchIPv6Dst(Ip6Address.ALL_DHCP_RELAY_AGENTS_AND_SERVERS.toIpPrefix())
1413 .build();
1414 packetService.cancelPackets(indirectClientSelector,
1415 PacketPriority.CONTROL,
1416 appId);
1417 indirectClientSelector =
1418 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1419 .matchIPv6Dst(Ip6Address.ALL_DHCP_SERVERS.toIpPrefix())
1420 .build();
1421 packetService.cancelPackets(indirectClientSelector,
1422 PacketPriority.CONTROL,
1423 appId);
Yi Tseng7da339e2017-10-23 19:39:39 -07001424
1425 // Packet comes from client
1426 packetService.cancelPackets(CLIENT_SERVER_SELECTOR,
1427 PacketPriority.CONTROL,
1428 appId);
1429 }
1430
1431 private void requestClientDhcpPacket(Ip6Address serverIp) {
1432 // Packet comes from relay
1433 TrafficSelector indirectClientSelector =
1434 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1435 .matchIPv6Dst(serverIp.toIpPrefix())
1436 .build();
1437 packetService.requestPackets(indirectClientSelector,
1438 PacketPriority.CONTROL,
1439 appId);
Yi Tseng3f2119b2017-11-02 15:21:10 -07001440 indirectClientSelector =
1441 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1442 .matchIPv6Dst(Ip6Address.ALL_DHCP_RELAY_AGENTS_AND_SERVERS.toIpPrefix())
1443 .build();
1444 packetService.requestPackets(indirectClientSelector,
1445 PacketPriority.CONTROL,
1446 appId);
1447 indirectClientSelector =
1448 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1449 .matchIPv6Dst(Ip6Address.ALL_DHCP_SERVERS.toIpPrefix())
1450 .build();
1451 packetService.requestPackets(indirectClientSelector,
1452 PacketPriority.CONTROL,
1453 appId);
Yi Tseng7da339e2017-10-23 19:39:39 -07001454
1455 // Packet comes from client
1456 packetService.requestPackets(CLIENT_SERVER_SELECTOR,
1457 PacketPriority.CONTROL,
1458 appId);
1459 }
1460
1461 /**
1462 * Process the ignore rules.
1463 *
1464 * @param deviceId the device id
1465 * @param vlanId the vlan to be ignored
1466 * @param op the operation, ADD to install; REMOVE to uninstall rules
1467 */
1468 private void processIgnoreVlanRule(DeviceId deviceId, VlanId vlanId, Objective.Operation op) {
Yi Tseng7da339e2017-10-23 19:39:39 -07001469 AtomicInteger installedCount = new AtomicInteger(DHCP_SELECTORS.size());
1470 DHCP_SELECTORS.forEach(trafficSelector -> {
1471 TrafficSelector selector = DefaultTrafficSelector.builder(trafficSelector)
1472 .matchVlanId(vlanId)
1473 .build();
1474
1475 ForwardingObjective.Builder builder = DefaultForwardingObjective.builder()
1476 .withFlag(ForwardingObjective.Flag.VERSATILE)
1477 .withSelector(selector)
1478 .withPriority(IGNORE_CONTROL_PRIORITY)
Yi Tsengf29ffd72017-11-29 10:49:20 -08001479 .withTreatment(DefaultTrafficTreatment.emptyTreatment())
Yi Tseng7da339e2017-10-23 19:39:39 -07001480 .fromApp(appId);
1481
1482
1483 ObjectiveContext objectiveContext = new ObjectiveContext() {
1484 @Override
1485 public void onSuccess(Objective objective) {
1486 log.info("Ignore rule {} (Vlan id {}, device {}, selector {})",
1487 op, vlanId, deviceId, selector);
1488 int countDown = installedCount.decrementAndGet();
1489 if (countDown != 0) {
1490 return;
1491 }
1492 switch (op) {
1493 case ADD:
1494 ignoredVlans.put(deviceId, vlanId);
1495 break;
1496 case REMOVE:
1497 ignoredVlans.remove(deviceId, vlanId);
1498 break;
1499 default:
1500 log.warn("Unsupported objective operation {}", op);
1501 break;
1502 }
1503 }
1504
1505 @Override
1506 public void onError(Objective objective, ObjectiveError error) {
1507 log.warn("Can't {} ignore rule (vlan id {}, selector {}, device {}) due to {}",
1508 op, vlanId, selector, deviceId, error);
1509 }
1510 };
1511
1512 ForwardingObjective fwd;
1513 switch (op) {
1514 case ADD:
1515 fwd = builder.add(objectiveContext);
1516 break;
1517 case REMOVE:
1518 fwd = builder.remove(objectiveContext);
1519 break;
1520 default:
1521 log.warn("Unsupported objective operation {}", op);
1522 return;
1523 }
1524
1525 Device device = deviceService.getDevice(deviceId);
1526 if (device == null || !device.is(Pipeliner.class)) {
1527 log.warn("Device {} is not available now, wait until device is available", deviceId);
1528 return;
1529 }
1530 flowObjectiveService.apply(deviceId, fwd);
1531 });
1532 }
Kalhee Kimc60c7e22017-11-01 17:56:44 +00001533
1534 /**
1535 * Find first ipaddress for a given Host info i.e. mac and vlan.
1536 *
1537 * @param clientMac client mac
1538 * @param vlanId packet's vlan
1539 * @return next-hop link-local ipaddress for a given host
1540 */
1541 private IpAddress getFirstIpByHost(MacAddress clientMac, VlanId vlanId) {
1542 IpAddress nextHopIp;
1543 // pick out the first link-local ip address
1544 HostId gwHostId = HostId.hostId(clientMac, vlanId);
1545 Host gwHost = hostService.getHost(gwHostId);
1546 if (gwHost == null) {
1547 log.warn("Can't find gateway host for hostId {}", gwHostId);
1548 return null;
1549 }
1550 nextHopIp = gwHost.ipAddresses()
1551 .stream()
1552 .filter(IpAddress::isIp6)
1553 .filter(ip6 -> ip6.isLinkLocal())
1554 .map(IpAddress::getIp6Address)
1555 .findFirst()
1556 .orElse(null);
1557 return nextHopIp;
1558 }
Yi Tseng51301292017-07-28 13:02:59 -07001559}