blob: d809d53a1e2a5fa588f9afc65dff029db50955f6 [file] [log] [blame]
Yi Tseng51301292017-07-28 13:02:59 -07001/*
2 * Copyright 2017-present Open Networking Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 */
17
18package org.onosproject.dhcprelay;
19
Yi Tseng525ff402017-10-23 19:39:39 -070020import com.google.common.collect.HashMultimap;
Yi Tseng919b2df2017-09-07 16:22:51 -070021import com.google.common.collect.Lists;
Yi Tseng525ff402017-10-23 19:39:39 -070022import com.google.common.collect.Multimap;
Kalhee Kim45fede42017-09-05 19:05:06 +000023import org.apache.felix.scr.annotations.Activate;
24import org.apache.felix.scr.annotations.Deactivate;
Kalhee Kim45fede42017-09-05 19:05:06 +000025import com.google.common.collect.Sets;
26import com.google.common.collect.ImmutableSet;
Yi Tseng51301292017-07-28 13:02:59 -070027import org.apache.felix.scr.annotations.Component;
28import org.apache.felix.scr.annotations.Property;
Kalhee Kim45fede42017-09-05 19:05:06 +000029import org.apache.felix.scr.annotations.Reference;
30import org.apache.felix.scr.annotations.ReferenceCardinality;
Yi Tseng51301292017-07-28 13:02:59 -070031import org.apache.felix.scr.annotations.Service;
32import org.onlab.packet.BasePacket;
Kalhee Kim45fede42017-09-05 19:05:06 +000033import org.onlab.packet.DHCP6;
34import org.onlab.packet.IPv6;
35import org.onlab.packet.Ethernet;
36import org.onlab.packet.Ip6Address;
Yi Tseng51301292017-07-28 13:02:59 -070037import org.onlab.packet.IpAddress;
Kalhee Kim45fede42017-09-05 19:05:06 +000038import org.onlab.packet.IpPrefix;
Yi Tseng51301292017-07-28 13:02:59 -070039import org.onlab.packet.MacAddress;
Yi Tseng525ff402017-10-23 19:39:39 -070040import org.onlab.packet.TpPort;
Kalhee Kim45fede42017-09-05 19:05:06 +000041import org.onlab.packet.UDP;
Yi Tseng51301292017-07-28 13:02:59 -070042import org.onlab.packet.VlanId;
Kalhee Kim45fede42017-09-05 19:05:06 +000043import org.onlab.packet.dhcp.Dhcp6RelayOption;
44import org.onlab.packet.dhcp.Dhcp6InterfaceIdOption;
45import org.onlab.packet.dhcp.Dhcp6Option;
46import org.onlab.packet.dhcp.Dhcp6IaNaOption;
47import org.onlab.packet.dhcp.Dhcp6IaTaOption;
48import org.onlab.packet.dhcp.Dhcp6IaPdOption;
49import org.onlab.packet.dhcp.Dhcp6IaAddressOption;
50import org.onlab.packet.dhcp.Dhcp6IaPrefixOption;
51import org.onlab.util.HexString;
Yi Tseng525ff402017-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 Tseng919b2df2017-09-07 16:22:51 -070055import org.onosproject.dhcprelay.api.DhcpServerInfo;
Yi Tseng525ff402017-10-23 19:39:39 -070056import org.onosproject.dhcprelay.config.IgnoreDhcpConfig;
Kalhee Kim45fede42017-09-05 19:05:06 +000057import org.onosproject.dhcprelay.store.DhcpRelayStore;
Yi Tseng525ff402017-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 Tsengaa417a62017-09-08 17:22:51 -070070import org.onosproject.net.host.HostProvider;
71import org.onosproject.net.host.HostProviderRegistry;
72import org.onosproject.net.host.HostProviderService;
Kalhee Kim45fede42017-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 Tseng525ff402017-10-23 19:39:39 -070081import org.onosproject.net.packet.PacketPriority;
Yi Tsengaa417a62017-09-08 17:22:51 -070082import org.onosproject.net.provider.ProviderId;
Kalhee Kim45fede42017-09-05 19:05:06 +000083import org.onosproject.routeservice.Route;
84import org.onosproject.routeservice.RouteStore;
Yi Tseng483ac6f2017-08-02 15:03:31 -070085import org.onosproject.dhcprelay.config.DhcpServerConfig;
Yi Tseng51301292017-07-28 13:02:59 -070086import org.onosproject.net.ConnectPoint;
Kalhee Kim45fede42017-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 Kim45fede42017-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 Kim45fede42017-09-05 19:05:06 +000099
100import java.nio.ByteBuffer;
101import java.util.List;
Yi Tseng483ac6f2017-08-02 15:03:31 -0700102import java.util.Collection;
Yi Tseng51301292017-07-28 13:02:59 -0700103import java.util.Optional;
Kalhee Kim45fede42017-09-05 19:05:06 +0000104import java.util.Set;
105import java.util.ArrayList;
Yi Tseng525ff402017-10-23 19:39:39 -0700106import java.util.concurrent.atomic.AtomicInteger;
Kalhee Kim45fede42017-09-05 19:05:06 +0000107
108
109import static com.google.common.base.Preconditions.checkNotNull;
110import static com.google.common.base.Preconditions.checkState;
Yi Tseng525ff402017-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 Tsengaa417a62017-09-08 17:22:51 -0700117public class Dhcp6HandlerImpl implements DhcpHandler, HostProvider {
Charles Chand988c282017-09-12 17:09:32 -0700118 public static final String DHCP_V6_RELAY_APP = "org.onosproject.Dhcp6HandlerImpl";
Saurav Das7c6dec12017-09-13 14:35:56 -0700119 public static final ProviderId PROVIDER_ID = new ProviderId("dhcp6", DHCP_V6_RELAY_APP);
Yi Tseng525ff402017-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 Kim45fede42017-09-05 19:05:06 +0000140 private static Logger log = LoggerFactory.getLogger(Dhcp6HandlerImpl.class);
Yi Tseng51301292017-07-28 13:02:59 -0700141
Kalhee Kim45fede42017-09-05 19:05:06 +0000142 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
143 protected DhcpRelayStore dhcpRelayStore;
Yi Tseng51301292017-07-28 13:02:59 -0700144
Kalhee Kim45fede42017-09-05 19:05:06 +0000145 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
146 protected PacketService packetService;
147
148 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Kalhee Kim45fede42017-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 Tsengaa417a62017-09-08 17:22:51 -0700157 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
158 protected HostProviderRegistry providerRegistry;
Kalhee Kim45fede42017-09-05 19:05:06 +0000159
Yi Tseng525ff402017-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 Tsengaa417a62017-09-08 17:22:51 -0700169 protected HostProviderService providerService;
Yi Tseng525ff402017-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 Tseng919b2df2017-09-07 16:22:51 -0700174 private List<DhcpServerInfo> defaultServerInfoList = Lists.newArrayList();
175 private List<DhcpServerInfo> indirectServerInfoList = Lists.newArrayList();
176
Kalhee Kim45fede42017-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 Tseng525ff402017-10-23 19:39:39 -0700194 appId = coreService.registerApplication(DHCP_V6_RELAY_APP);
Yi Tsengaa417a62017-09-08 17:22:51 -0700195 providerService = providerRegistry.register(this);
Kalhee Kim45fede42017-09-05 19:05:06 +0000196 hostService.addListener(hostListener);
Yi Tseng51301292017-07-28 13:02:59 -0700197 }
198
Kalhee Kim45fede42017-09-05 19:05:06 +0000199 @Deactivate
200 protected void deactivate() {
Yi Tsengaa417a62017-09-08 17:22:51 -0700201 providerRegistry.unregister(this);
Kalhee Kim45fede42017-09-05 19:05:06 +0000202 hostService.removeListener(hostListener);
Yi Tseng919b2df2017-09-07 16:22:51 -0700203 defaultServerInfoList.forEach(this::stopMonitoringIps);
204 defaultServerInfoList.clear();
205 indirectServerInfoList.forEach(this::stopMonitoringIps);
206 indirectServerInfoList.clear();
207 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000208
Yi Tseng919b2df2017-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 Tseng919b2df2017-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 Tseng919b2df2017-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 Tseng525ff402017-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 Kim45fede42017-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 Tseng919b2df2017-09-07 16:22:51 -0700308 return !defaultServerInfoList.isEmpty();
Kalhee Kim45fede42017-09-05 19:05:06 +0000309 }
310
Yi Tsengaa417a62017-09-08 17:22:51 -0700311 @Override
312 public ProviderId id() {
Charles Chand988c282017-09-12 17:09:32 -0700313 return PROVIDER_ID;
Yi Tsengaa417a62017-09-08 17:22:51 -0700314 }
315
316 @Override
317 public void triggerProbe(Host host) {
318 // Do nothing here
319 }
320
Kalhee Kim45fede42017-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 {}",
379 dhcp6Payload2.getMsgType());
380 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()
400 .filter(opt -> opt instanceof Dhcp6RelayOption)
401 .map(BasePacket::getPayload)
402 .map(pld -> (DHCP6) pld)
403 .findFirst()
404 .orElse(null);
405
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 Kim0c0cb0b2017-09-15 17:43:27 +0000610 * @param clientInterface client interfaces
Kalhee Kim45fede42017-09-05 19:05:06 +0000611 */
612 private void removeHostOrRoute(boolean directConnFlag, DHCP6 dhcp6Packet,
613 Ethernet clientPacket, IPv6 clientIpv6,
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +0000614 Interface clientInterface) {
Kalhee Kim45fede42017-09-05 19:05:06 +0000615 log.debug("extractPrefix enters {}", dhcp6Packet);
Kalhee Kim121ba922017-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 Kim45fede42017-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 Kim121ba922017-11-01 17:56:44 +0000627
Kalhee Kim45fede42017-09-05 19:05:06 +0000628 HostId hostId = HostId.hostId(clientMac, vlanId);
629 log.debug("remove Host {} ip for directly connected.", hostId.toString());
Kalhee Kim45fede42017-09-05 19:05:06 +0000630 // Remove host's ip of when dhcp release msg is received
Yi Tsengaa417a62017-09-08 17:22:51 -0700631 providerService.removeIpFromHost(hostId, ip);
Kalhee Kim45fede42017-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 Kim121ba922017-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 Kim45fede42017-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(), ":"),
653 HexString.toHexString(nextHopIp.toOctets(), ":"));
654 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(), ":"),
664 HexString.toHexString(nextHopIp.toOctets(), ":"));
665
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 Kim0c0cb0b2017-09-15 17:43:27 +0000679 * @param clientInterface client interface
Kalhee Kim45fede42017-09-05 19:05:06 +0000680 */
681 private void addHostOrRoute(boolean directConnFlag, DHCP6 dhcp6Relay,
682 DHCP6 embeddedDhcp6,
683 MacAddress clientMac,
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +0000684 Interface clientInterface) {
Kalhee Kim45fede42017-09-05 19:05:06 +0000685 log.debug("addHostOrRoute entered.");
Kalhee Kim121ba922017-11-01 17:56:44 +0000686 VlanId vlanId = clientInterface.vlan();
Kalhee Kim45fede42017-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 Tsengaa417a62017-09-08 17:22:51 -0700695
696 // FIXME: we should use vlan id from original packet (solicit, request)
Kalhee Kim45fede42017-09-05 19:05:06 +0000697 HostId hostId = HostId.hostId(clientMac, vlanId);
Yi Tsengaa417a62017-09-08 17:22:51 -0700698 Host host = hostService.getHost(hostId);
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +0000699 HostLocation hostLocation = new HostLocation(clientInterface.connectPoint(),
Yi Tsengaa417a62017-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 Kim45fede42017-09-05 19:05:06 +0000708 HostDescription desc = new DefaultHostDescription(clientMac, vlanId,
Yi Tsengaa417a62017-09-08 17:22:51 -0700709 hostLocations, ips,
710 false);
Kalhee Kim45fede42017-09-05 19:05:06 +0000711 log.debug("adding Host for directly connected.");
712 log.debug("client mac {} client vlan {} hostlocation {}",
713 HexString.toHexString(clientMac.toBytes(), ":"),
714 vlanId, hostLocation.toString());
715
716 // Replace the ip when dhcp server give the host new ip address
Yi Tsengaa417a62017-09-08 17:22:51 -0700717 providerService.hostDetected(hostId, desc, false);
Kalhee Kim45fede42017-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 Kim121ba922017-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 Kim45fede42017-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 Tseng25bfe372017-11-03 16:27:32 -0700754 * Build the DHCP6 solicit/request packet with gatewayip.
755 * TODO: method too long, need to be refactored.
Kalhee Kim45fede42017-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 */
761 private InternalPacket processDhcp6PacketFromClient(PacketContext context,
Yi Tseng25bfe372017-11-03 16:27:32 -0700762 Ethernet clientPacket, Set<Interface> clientInterfaces) {
763 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 Kim45b24182017-10-18 18:30:23 +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 "
Yi Tseng25bfe372017-11-03 16:27:32 -0700798 + "packet from client on port: {}. Aborting packet processing",
799 clientInterfaces.iterator().next().connectPoint());
Kalhee Kim45b24182017-10-18 18:30:23 +0000800 return null;
801 }
Kalhee Kim45b24182017-10-18 18:30:23 +0000802 // get dhcp6 header.
Kalhee Kim45b24182017-10-18 18:30:23 +0000803 IPv6 clientIpv6 = (IPv6) clientPacket.getPayload();
804 UDP clientUdp = (UDP) clientIpv6.getPayload();
805 DHCP6 clientDhcp6 = (DHCP6) clientUdp.getPayload();
Kalhee Kim45b24182017-10-18 18:30:23 +0000806 boolean directConnFlag = directlyConnected(clientDhcp6);
807 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 Kim45b24182017-10-18 18:30:23 +0000818 Ethernet etherReply = clientPacket.duplicate();
819 etherReply.setSourceMACAddress(macFacingServer);
Yi Tseng25bfe372017-11-03 16:27:32 -0700820 if ((directConnFlag && dhcpConnectMac == null) ||
821 !directConnFlag && indirectDhcpConnectMac == null && dhcpConnectMac == null) {
Kalhee Kim45b24182017-10-18 18:30:23 +0000822 log.warn("Packet received from {} connected client.", directConnFlag ? "directly" : "indirectly");
Charles Chan6fa3b4f2017-10-30 13:51:22 -0700823 log.warn("DHCP6 {} not yet resolved .. Aborting DHCP packet processing from client on port: {}",
Yi Tseng25bfe372017-11-03 16:27:32 -0700824 (dhcpGatewayIp == null) ? "server IP " + dhcpServerIp
825 : "gateway IP " + dhcpGatewayIp,
826 clientInterfaces.iterator().next().connectPoint());
Kalhee Kim45b24182017-10-18 18:30:23 +0000827 return null;
828 }
Yi Tseng25bfe372017-11-03 16:27:32 -0700829 if (dhcpServerConnectPoint == null) {
Kalhee Kim45b24182017-10-18 18:30:23 +0000830 log.warn("DHCP6 server connection point direct {} directConn {} indirectConn {} is not set up yet",
Yi Tseng25bfe372017-11-03 16:27:32 -0700831 directConnFlag, dhcpServerConnectPoint, indirectDhcpServerConnectPoint);
Kalhee Kim121ba922017-11-01 17:56:44 +0000832 return null;
833 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000834
Yi Tseng25bfe372017-11-03 16:27:32 -0700835 etherReply.setDestinationMACAddress(dhcpConnectMac);
836 etherReply.setVlanID(dhcpConnectVlan.toShort());
Kalhee Kim45fede42017-09-05 19:05:06 +0000837
Kalhee Kim121ba922017-11-01 17:56:44 +0000838 IPv6 ipv6Packet = (IPv6) etherReply.getPayload();
839 byte[] peerAddress = clientIpv6.getSourceAddress();
840 ipv6Packet.setSourceAddress(ipFacingServer.toOctets());
Yi Tseng25bfe372017-11-03 16:27:32 -0700841 ipv6Packet.setDestinationAddress(dhcpServerIp.toOctets());
Charles Chana990ce92017-10-30 10:22:50 -0700842
Kalhee Kim121ba922017-11-01 17:56:44 +0000843 UDP udpPacket = (UDP) ipv6Packet.getPayload();
844 udpPacket.setSourcePort(UDP.DHCP_V6_SERVER_PORT);
845 DHCP6 dhcp6Packet = (DHCP6) udpPacket.getPayload();
846 byte[] dhcp6PacketByte = dhcp6Packet.serialize();
Charles Chana990ce92017-10-30 10:22:50 -0700847
Kalhee Kim121ba922017-11-01 17:56:44 +0000848 // notify onos and quagga to release PD
849 //releasePD(dhcp6Packet);
850 ConnectPoint clientConnectionPoint = context.inPacket().receivedFrom();
851 VlanId vlanIdInUse = VlanId.vlanId(clientPacket.getVlanID());
852 Interface clientInterface = interfaceService.getInterfacesByPort(clientConnectionPoint)
853 .stream().filter(iface -> interfaceContainsVlan(iface, vlanIdInUse))
854 .findFirst().orElse(null);
Kalhee Kim45fede42017-09-05 19:05:06 +0000855
Kalhee Kim121ba922017-11-01 17:56:44 +0000856 removeHostOrRoute(directConnFlag, dhcp6Packet, clientPacket, clientIpv6, clientInterface);
Kalhee Kim45b24182017-10-18 18:30:23 +0000857
Kalhee Kim121ba922017-11-01 17:56:44 +0000858 DHCP6 dhcp6Relay = new DHCP6();
859 dhcp6Relay.setMsgType(DHCP6.MsgType.RELAY_FORW.value());
860 // link address: server uses the address to identify the link on which the client
861 // is located.
862 if (directConnFlag) {
863 dhcp6Relay.setLinkAddress(relayAgentIp.toOctets());
864 log.debug("direct connection: relayAgentIp obtained dynamically {}",
865 HexString.toHexString(relayAgentIp.toOctets(), ":"));
Kalhee Kim45fede42017-09-05 19:05:06 +0000866
Kalhee Kim121ba922017-11-01 17:56:44 +0000867 } else {
Yi Tseng25bfe372017-11-03 16:27:32 -0700868 if (indirectDhcpServerIp == null) {
Kalhee Kim121ba922017-11-01 17:56:44 +0000869 log.warn("indirect DhcpServerIp not available, use default DhcpServerIp {}",
Yi Tseng25bfe372017-11-03 16:27:32 -0700870 HexString.toHexString(dhcpServerIp.toOctets()));
Kalhee Kimd21029f2017-09-26 20:21:53 +0000871 } else {
872 // Indirect case, replace destination to indirect dhcp server if exist
873 // Check if mac is obtained for valid server ip
Yi Tseng25bfe372017-11-03 16:27:32 -0700874 if (indirectDhcpConnectMac == null) {
Kalhee Kimd21029f2017-09-26 20:21:53 +0000875 log.warn("DHCP6 {} not yet resolved .. Aborting DHCP "
Yi Tseng25bfe372017-11-03 16:27:32 -0700876 + "packet processing from client on port: {}",
877 (indirectDhcpGatewayIp == null) ? "server IP " + indirectDhcpServerIp
878 : "gateway IP " + indirectDhcpGatewayIp,
879 clientInterfaces.iterator().next().connectPoint());
Kalhee Kimd21029f2017-09-26 20:21:53 +0000880 return null;
881 }
Yi Tseng25bfe372017-11-03 16:27:32 -0700882 etherReply.setDestinationMACAddress(indirectDhcpConnectMac);
883 etherReply.setVlanID(indirectDhcpConnectVlan.toShort());
884 ipv6Packet.setDestinationAddress(indirectDhcpServerIp.toOctets());
Kalhee Kimd21029f2017-09-26 20:21:53 +0000885
886 }
Yi Tseng25bfe372017-11-03 16:27:32 -0700887 if (indirectRelayAgentIpFromCfg == null) {
Kalhee Kim45fede42017-09-05 19:05:06 +0000888 dhcp6Relay.setLinkAddress(relayAgentIp.toOctets());
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +0000889 log.warn("indirect connection: relayAgentIp NOT availale from config file! Use dynamic. {}",
Yi Tseng25bfe372017-11-03 16:27:32 -0700890 HexString.toHexString(relayAgentIp.toOctets(), ":"));
891 } else {
892 dhcp6Relay.setLinkAddress(indirectRelayAgentIpFromCfg.toOctets());
893 log.debug("indirect connection: relayAgentIp from config file is available! {}",
894 HexString.toHexString(indirectRelayAgentIpFromCfg.toOctets(), ":"));
895 }
896 }
897 // peer address: address of the client or relay agent from which
898 // the message to be relayed was received.
899 dhcp6Relay.setPeerAddress(peerAddress);
900 List<Dhcp6Option> options = new ArrayList<>();
901 // directly connected case, hop count is zero; otherwise, hop count + 1
902 if (directConnFlag) {
903 dhcp6Relay.setHopCount((byte) 0);
904 } else {
905 dhcp6Relay.setHopCount((byte) (dhcp6Packet.getHopCount() + 1));
906 }
907 // create relay message option
908 Dhcp6Option relayMessage = new Dhcp6Option();
909 relayMessage.setCode(DHCP6.OptionCode.RELAY_MSG.value());
910 relayMessage.setLength((short) dhcp6PacketByte.length);
911 relayMessage.setData(dhcp6PacketByte);
912 options.add(relayMessage);
913 // create interfaceId option
914 String inPortString = "-" + context.inPacket().receivedFrom().toString() + ":";
915 Dhcp6Option interfaceId = new Dhcp6Option();
916 interfaceId.setCode(DHCP6.OptionCode.INTERFACE_ID.value());
917 byte[] clientSoureMacBytes = clientPacket.getSourceMACAddress();
918 byte[] inPortStringBytes = inPortString.getBytes();
919 byte[] vlanIdBytes = new byte[2];
920 vlanIdBytes[0] = (byte) (clientPacket.getVlanID() & 0xff);
921 vlanIdBytes[1] = (byte) ((clientPacket.getVlanID() >> 8) & 0xff);
922 byte[] interfaceIdBytes = new byte[clientSoureMacBytes.length +
923 inPortStringBytes.length + vlanIdBytes.length];
924 log.debug("Length: interfaceIdBytes {} clientSoureMacBytes {} inPortStringBytes {} vlan {}",
925 interfaceIdBytes.length, clientSoureMacBytes.length, inPortStringBytes.length,
926 vlanIdBytes.length);
927 System.arraycopy(clientSoureMacBytes, 0, interfaceIdBytes, 0, clientSoureMacBytes.length);
928 System.arraycopy(inPortStringBytes, 0, interfaceIdBytes, clientSoureMacBytes.length, inPortStringBytes.length);
929 System.arraycopy(vlanIdBytes, 0, interfaceIdBytes, clientSoureMacBytes.length + inPortStringBytes.length,
930 vlanIdBytes.length);
931 interfaceId.setData(interfaceIdBytes);
932 interfaceId.setLength((short) interfaceIdBytes.length);
933 options.add(interfaceId);
934 log.debug("interfaceId write srcMac {} portString {}",
935 HexString.toHexString(clientSoureMacBytes, ":"), inPortString);
936 dhcp6Relay.setOptions(options);
937 udpPacket.setPayload(dhcp6Relay);
938 udpPacket.resetChecksum();
939 ipv6Packet.setPayload(udpPacket);
940 ipv6Packet.setHopLimit((byte) 64);
941 etherReply.setPayload(ipv6Packet);
942 if (directConnFlag || indirectDhcpServerIp == null) {
943 return new InternalPacket(etherReply, dhcpServerConnectPoint);
944 } else {
945 return new InternalPacket(etherReply, indirectDhcpServerConnectPoint);
946 }
947 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000948
949 /**
950 *
951 * process the DHCP6 relay-reply packet from dhcp server.
952 *
953 * @param context packet context
954 * @param receivedPacket server ethernet packet
955 * @param recevingInterfaces set of server side interfaces
956 */
957 private InternalPacket processDhcp6PacketFromServer(PacketContext context,
958 Ethernet receivedPacket, Set<Interface> recevingInterfaces) {
Yi Tseng25bfe372017-11-03 16:27:32 -0700959
960 ConnectPoint receivedFrom = context.inPacket().receivedFrom();
961 DeviceId receivedFromDevice = receivedFrom.deviceId();
962
963 // TODO: refactor
964 DhcpServerInfo serverInfo;
965 Ip6Address dhcpServerIp = null;
966 ConnectPoint dhcpServerConnectPoint = null;
967 MacAddress dhcpConnectMac = null;
968 VlanId dhcpConnectVlan = null;
969 Ip6Address dhcpGatewayIp = null;
970
971 Ip6Address indirectDhcpServerIp = null;
972 ConnectPoint indirectDhcpServerConnectPoint = null;
973 MacAddress indirectDhcpConnectMac = null;
974 VlanId indirectDhcpConnectVlan = null;
975 Ip6Address indirectDhcpGatewayIp = null;
976 Ip6Address indirectRelayAgentIpFromCfg = null;
977
978 if (!defaultServerInfoList.isEmpty()) {
979 serverInfo = defaultServerInfoList.get(0);
980 dhcpConnectMac = serverInfo.getDhcpConnectMac().orElse(null);
981 dhcpGatewayIp = serverInfo.getDhcpGatewayIp6().orElse(null);
982 dhcpServerIp = serverInfo.getDhcpServerIp6().orElse(null);
983 dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
984 dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
985 }
986
987 if (!indirectServerInfoList.isEmpty()) {
988 serverInfo = indirectServerInfoList.get(0);
989 indirectDhcpConnectMac = serverInfo.getDhcpConnectMac().orElse(null);
990 indirectDhcpGatewayIp = serverInfo.getDhcpGatewayIp6().orElse(null);
991 indirectDhcpServerIp = serverInfo.getDhcpServerIp6().orElse(null);
992 indirectDhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
993 indirectDhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
994 indirectRelayAgentIpFromCfg = serverInfo.getRelayAgentIp6(receivedFromDevice).orElse(null);
995 }
996
Kalhee Kim45fede42017-09-05 19:05:06 +0000997 // get dhcp6 header.
Ray Milkeyf0c47612017-09-28 11:29:38 -0700998 Ethernet etherReply = receivedPacket.duplicate();
Kalhee Kim45fede42017-09-05 19:05:06 +0000999 IPv6 ipv6Packet = (IPv6) etherReply.getPayload();
1000 UDP udpPacket = (UDP) ipv6Packet.getPayload();
1001 DHCP6 dhcp6Relay = (DHCP6) udpPacket.getPayload();
1002
1003 Boolean directConnFlag = directlyConnected(dhcp6Relay);
Kalhee Kimd21029f2017-09-26 20:21:53 +00001004 ConnectPoint inPort = context.inPacket().receivedFrom();
Yi Tseng25bfe372017-11-03 16:27:32 -07001005 if ((directConnFlag || (!directConnFlag && indirectDhcpServerIp == null))
1006 && !inPort.equals(dhcpServerConnectPoint)) {
Kalhee Kimd21029f2017-09-26 20:21:53 +00001007 log.warn("Receiving port {} is not the same as server connect point {} for direct or indirect-null",
Yi Tseng25bfe372017-11-03 16:27:32 -07001008 inPort, dhcpServerConnectPoint);
Kalhee Kimd21029f2017-09-26 20:21:53 +00001009 return null;
1010 }
1011
Yi Tseng25bfe372017-11-03 16:27:32 -07001012 if (!directConnFlag && indirectDhcpServerIp != null &&
1013 !inPort.equals(indirectDhcpServerConnectPoint)) {
Kalhee Kimd21029f2017-09-26 20:21:53 +00001014 log.warn("Receiving port {} is not the same as server connect point {} for indirect",
Yi Tseng25bfe372017-11-03 16:27:32 -07001015 inPort, indirectDhcpServerConnectPoint);
Kalhee Kimd21029f2017-09-26 20:21:53 +00001016 return null;
1017 }
1018
Kalhee Kim45fede42017-09-05 19:05:06 +00001019
1020 Dhcp6InterfaceIdOption interfaceIdOption = dhcp6Relay.getOptions().stream()
1021 .filter(opt -> opt instanceof Dhcp6InterfaceIdOption)
1022 .map(opt -> (Dhcp6InterfaceIdOption) opt)
1023 .findFirst()
1024 .orElse(null);
1025
1026 if (interfaceIdOption == null) {
1027 log.warn("Interface Id option is not present, abort packet...");
1028 return null;
1029 }
1030
1031 MacAddress peerMac = interfaceIdOption.getMacAddress();
1032 String clientConnectionPointStr = new String(interfaceIdOption.getInPort());
1033
1034 ConnectPoint clientConnectionPoint = ConnectPoint.deviceConnectPoint(clientConnectionPointStr);
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +00001035 VlanId vlanIdInUse = VlanId.vlanId(interfaceIdOption.getVlanId());
1036 Interface clientInterface = interfaceService.getInterfacesByPort(clientConnectionPoint)
1037 .stream()
1038 .filter(iface -> interfaceContainsVlan(iface, vlanIdInUse))
1039 .findFirst()
1040 .orElse(null);
1041 if (clientInterface == null) {
1042 log.warn("Cannot get client interface for from packet, abort... vlan {}", vlanIdInUse.toString());
Kalhee Kim45fede42017-09-05 19:05:06 +00001043 return null;
1044 }
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +00001045 MacAddress relayAgentMac = clientInterface.mac();
Kalhee Kim45fede42017-09-05 19:05:06 +00001046 if (relayAgentMac == null) {
1047 log.warn("Can not get interface mac, abort packet..");
1048 return null;
1049 }
1050 etherReply.setSourceMACAddress(relayAgentMac);
1051
1052 // find destMac
1053 MacAddress clientMac = null;
Yi Tsengaa417a62017-09-08 17:22:51 -07001054 Ip6Address peerAddress = Ip6Address.valueOf(dhcp6Relay.getPeerAddress());
1055 Set<Host> clients = hostService.getHostsByIp(peerAddress);
Kalhee Kim45fede42017-09-05 19:05:06 +00001056 if (clients.isEmpty()) {
1057 log.warn("There's no host found for this address {}",
1058 HexString.toHexString(dhcp6Relay.getPeerAddress(), ":"));
1059 log.warn("Let's look up interfaceId {}", HexString.toHexString(peerMac.toBytes(), ":"));
1060 clientMac = peerMac;
1061 } else {
1062 clientMac = clients.iterator().next().mac();
1063 if (clientMac == null) {
1064 log.warn("No client mac address found, abort packet...");
1065 return null;
1066 }
1067 log.warn("Client mac address found from getHostByIp");
1068
1069 }
1070 etherReply.setDestinationMACAddress(clientMac);
1071
1072 // ip header
1073 ipv6Packet.setSourceAddress(dhcp6Relay.getLinkAddress());
1074 ipv6Packet.setDestinationAddress(dhcp6Relay.getPeerAddress());
1075 // udp header
1076 udpPacket.setSourcePort(UDP.DHCP_V6_SERVER_PORT);
1077 if (directConnFlag) {
1078 udpPacket.setDestinationPort(UDP.DHCP_V6_CLIENT_PORT);
1079 } else {
1080 udpPacket.setDestinationPort(UDP.DHCP_V6_SERVER_PORT);
1081 }
1082
1083 DHCP6 embeddedDhcp6 = dhcp6Relay.getOptions().stream()
1084 .filter(opt -> opt instanceof Dhcp6RelayOption)
1085 .map(BasePacket::getPayload)
1086 .map(pld -> (DHCP6) pld)
1087 .findFirst()
1088 .orElse(null);
1089
1090
1091 // add host or route
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +00001092 addHostOrRoute(directConnFlag, dhcp6Relay, embeddedDhcp6, clientMac, clientInterface);
Kalhee Kim45fede42017-09-05 19:05:06 +00001093
1094 udpPacket.setPayload(embeddedDhcp6);
1095 udpPacket.resetChecksum();
1096 ipv6Packet.setPayload(udpPacket);
1097 etherReply.setPayload(ipv6Packet);
1098
1099 return new InternalPacket(etherReply, clientConnectionPoint);
1100 }
1101
Yi Tseng919b2df2017-09-07 16:22:51 -07001102 // Returns the first v6 interface ip out of a set of interfaces or null.
Kalhee Kim45fede42017-09-05 19:05:06 +00001103 // Checks all interfaces, and ignores v6 interface ips
1104 private Ip6Address getRelayAgentIPv6Address(Set<Interface> intfs) {
1105 for (Interface intf : intfs) {
1106 for (InterfaceIpAddress ip : intf.ipAddressesList()) {
1107 Ip6Address relayAgentIp = ip.ipAddress().getIp6Address();
1108 if (relayAgentIp != null) {
1109 return relayAgentIp;
1110 }
1111 }
1112 }
1113 return null;
Yi Tseng51301292017-07-28 13:02:59 -07001114 }
Yi Tseng483ac6f2017-08-02 15:03:31 -07001115
1116 @Override
1117 public void setDefaultDhcpServerConfigs(Collection<DhcpServerConfig> configs) {
Yi Tseng919b2df2017-09-07 16:22:51 -07001118 setDhcpServerConfigs(configs, defaultServerInfoList);
Yi Tseng919b2df2017-09-07 16:22:51 -07001119 }
1120
1121 @Override
1122 public void setIndirectDhcpServerConfigs(Collection<DhcpServerConfig> configs) {
1123 setDhcpServerConfigs(configs, indirectServerInfoList);
Yi Tseng919b2df2017-09-07 16:22:51 -07001124 }
1125
1126 public void setDhcpServerConfigs(Collection<DhcpServerConfig> configs, List<DhcpServerInfo> serverInfoList) {
Kalhee Kim45fede42017-09-05 19:05:06 +00001127 if (configs.size() == 0) {
1128 // no config to update
1129 return;
1130 }
1131
1132 // TODO: currently we pick up first DHCP server config.
1133 // Will use other server configs in the future for HA.
1134 DhcpServerConfig serverConfig = configs.iterator().next();
Yi Tseng919b2df2017-09-07 16:22:51 -07001135
Kalhee Kim45fede42017-09-05 19:05:06 +00001136 if (!serverConfig.getDhcpServerIp6().isPresent()) {
Yi Tseng919b2df2017-09-07 16:22:51 -07001137 // not a DHCPv6 config
Kalhee Kim45fede42017-09-05 19:05:06 +00001138 return;
1139 }
1140
Yi Tseng919b2df2017-09-07 16:22:51 -07001141 if (!serverInfoList.isEmpty()) {
1142 // remove old server info
1143 DhcpServerInfo oldServerInfo = serverInfoList.remove(0);
1144
1145 // stop monitoring gateway or server
1146 oldServerInfo.getDhcpGatewayIp6().ifPresent(gatewayIp -> {
1147 hostService.stopMonitoringIp(gatewayIp);
1148 });
1149 oldServerInfo.getDhcpServerIp6().ifPresent(serverIp -> {
1150 hostService.stopMonitoringIp(serverIp);
Yi Tseng525ff402017-10-23 19:39:39 -07001151 cancelDhcpPacket(serverIp);
Yi Tseng919b2df2017-09-07 16:22:51 -07001152 });
1153 }
1154
1155 // Create new server info according to the config
1156 DhcpServerInfo newServerInfo = new DhcpServerInfo(serverConfig,
1157 DhcpServerInfo.Version.DHCP_V6);
1158 checkState(newServerInfo.getDhcpServerConnectPoint().isPresent(),
1159 "Connect point not exists");
1160 checkState(newServerInfo.getDhcpServerIp6().isPresent(),
1161 "IP of DHCP server not exists");
1162
1163 log.debug("DHCP server connect point: {}", newServerInfo.getDhcpServerConnectPoint().orElse(null));
1164 log.debug("DHCP server IP: {}", newServerInfo.getDhcpServerIp6().orElse(null));
1165
Yi Tseng525ff402017-10-23 19:39:39 -07001166 Ip6Address serverIp = newServerInfo.getDhcpServerIp6().get();
1167 Ip6Address ipToProbe;
Yi Tseng919b2df2017-09-07 16:22:51 -07001168 if (newServerInfo.getDhcpGatewayIp6().isPresent()) {
1169 ipToProbe = newServerInfo.getDhcpGatewayIp6().get();
1170 } else {
1171 ipToProbe = newServerInfo.getDhcpServerIp6().orElse(null);
1172 }
1173 String hostToProbe = newServerInfo.getDhcpGatewayIp6()
1174 .map(ip -> "gateway").orElse("server");
1175
1176 log.debug("Probing to resolve {} IP {}", hostToProbe, ipToProbe);
Kalhee Kim45fede42017-09-05 19:05:06 +00001177 hostService.startMonitoringIp(ipToProbe);
1178
1179 Set<Host> hosts = hostService.getHostsByIp(ipToProbe);
1180 if (!hosts.isEmpty()) {
1181 Host host = hosts.iterator().next();
Yi Tseng919b2df2017-09-07 16:22:51 -07001182 newServerInfo.setDhcpConnectVlan(host.vlan());
1183 newServerInfo.setDhcpConnectMac(host.mac());
Kalhee Kim45fede42017-09-05 19:05:06 +00001184 }
Yi Tseng919b2df2017-09-07 16:22:51 -07001185 // Add new server info
Yi Tseng48cd0862017-11-09 13:54:12 -08001186 synchronized (this) {
1187 serverInfoList.clear();
1188 serverInfoList.add(0, newServerInfo);
1189 }
Yi Tseng525ff402017-10-23 19:39:39 -07001190 requestDhcpPacket(serverIp);
Kalhee Kim45fede42017-09-05 19:05:06 +00001191 }
1192
1193 class InternalHostListener implements HostListener {
1194 @Override
1195 public void event(HostEvent event) {
1196 switch (event.type()) {
1197 case HOST_ADDED:
1198 case HOST_UPDATED:
1199 hostUpdated(event.subject());
1200 break;
1201 case HOST_REMOVED:
1202 hostRemoved(event.subject());
1203 break;
Kalhee Kim45fede42017-09-05 19:05:06 +00001204 default:
1205 break;
1206 }
1207 }
1208 }
1209
1210 /**
Kalhee Kim45fede42017-09-05 19:05:06 +00001211 * Handle host updated.
1212 * If the host is DHCP server or gateway, update connect mac and vlan.
1213 *
1214 * @param host the host
1215 */
1216 private void hostUpdated(Host host) {
Yi Tseng525ff402017-10-23 19:39:39 -07001217 hostUpdated(host, defaultServerInfoList);
1218 hostUpdated(host, indirectServerInfoList);
Kalhee Kim45fede42017-09-05 19:05:06 +00001219 }
1220
Yi Tseng525ff402017-10-23 19:39:39 -07001221 private void hostUpdated(Host host, List<DhcpServerInfo> serverInfoList) {
1222 DhcpServerInfo serverInfo;
1223 Ip6Address targetIp;
1224 if (!serverInfoList.isEmpty()) {
1225 serverInfo = serverInfoList.get(0);
1226 Ip6Address serverIp = serverInfo.getDhcpServerIp6().orElse(null);
1227 targetIp = serverInfo.getDhcpGatewayIp6().orElse(null);
1228
1229 if (targetIp == null) {
1230 targetIp = serverIp;
1231 }
1232
1233 if (targetIp != null) {
1234 if (host.ipAddresses().contains(targetIp)) {
1235 serverInfo.setDhcpConnectMac(host.mac());
1236 serverInfo.setDhcpConnectVlan(host.vlan());
1237 requestDhcpPacket(serverIp);
1238 }
1239 }
1240 }
1241 }
1242
Kalhee Kim45fede42017-09-05 19:05:06 +00001243 /**
1244 * Handle host removed.
1245 * If the host is DHCP server or gateway, unset connect mac and vlan.
1246 *
1247 * @param host the host
1248 */
1249 private void hostRemoved(Host host) {
Yi Tseng525ff402017-10-23 19:39:39 -07001250 hostRemoved(host, defaultServerInfoList);
1251 hostRemoved(host, indirectServerInfoList);
Yi Tseng919b2df2017-09-07 16:22:51 -07001252 }
1253
Yi Tseng525ff402017-10-23 19:39:39 -07001254 private void hostRemoved(Host host, List<DhcpServerInfo> serverInfoList) {
1255 DhcpServerInfo serverInfo;
1256 Ip6Address targetIp;
1257
1258 if (!serverInfoList.isEmpty()) {
1259 serverInfo = serverInfoList.get(0);
1260 Ip6Address serverIp = serverInfo.getDhcpServerIp6().orElse(null);
1261 targetIp = serverInfo.getDhcpGatewayIp6().orElse(null);
1262
1263 if (targetIp == null) {
1264 targetIp = serverIp;
1265 }
1266
1267 if (targetIp != null) {
1268 if (host.ipAddresses().contains(targetIp)) {
1269 serverInfo.setDhcpConnectVlan(null);
1270 serverInfo.setDhcpConnectMac(null);
1271 cancelDhcpPacket(serverIp);
1272 }
1273 }
1274 }
1275 }
1276
Kalhee Kim45b24182017-10-18 18:30:23 +00001277 /**
1278 * Returns the first interface ip from interface.
1279 *
1280 * @param iface interface of one connect point
1281 * @return the first interface IP; null if not exists an IP address in
1282 * these interfaces
1283 */
1284 private Ip6Address getFirstIpFromInterface(Interface iface) {
1285 checkNotNull(iface, "Interface can't be null");
1286 return iface.ipAddressesList().stream()
1287 .map(InterfaceIpAddress::ipAddress)
1288 .filter(IpAddress::isIp6)
1289 .map(IpAddress::getIp6Address)
1290 .findFirst()
1291 .orElse(null);
1292 }
1293
1294 /**
1295 * Gets Interface facing to the server for default host.
1296 *
1297 * @return the Interface facing to the server; null if not found
1298 */
1299 private Interface getServerInterface() {
Yi Tseng25bfe372017-11-03 16:27:32 -07001300 DhcpServerInfo serverInfo;
1301 ConnectPoint dhcpServerConnectPoint;
1302 VlanId dhcpConnectVlan;
1303
1304 if (!defaultServerInfoList.isEmpty()) {
1305 serverInfo = defaultServerInfoList.get(0);
1306 dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1307 dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1308 } else {
Kalhee Kim45b24182017-10-18 18:30:23 +00001309 return null;
1310 }
Yi Tseng25bfe372017-11-03 16:27:32 -07001311
Kalhee Kim45b24182017-10-18 18:30:23 +00001312 return interfaceService.getInterfacesByPort(dhcpServerConnectPoint)
1313 .stream()
1314 .filter(iface -> interfaceContainsVlan(iface, dhcpConnectVlan))
1315 .findFirst()
1316 .orElse(null);
1317 }
1318
1319 /**
1320 * Gets Interface facing to the server for indirect hosts.
1321 * Use default server Interface if indirect server not configured.
1322 *
1323 * @return the Interface facing to the server; null if not found
1324 */
1325 private Interface getIndirectServerInterface() {
Yi Tseng25bfe372017-11-03 16:27:32 -07001326 DhcpServerInfo serverInfo;
1327
1328 ConnectPoint indirectDhcpServerConnectPoint;
1329 VlanId indirectDhcpConnectVlan;
1330
1331 if (!indirectServerInfoList.isEmpty()) {
1332 serverInfo = indirectServerInfoList.get(0);
1333 indirectDhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1334 indirectDhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1335 } else {
Kalhee Kim45b24182017-10-18 18:30:23 +00001336 return getServerInterface();
1337 }
Yi Tseng25bfe372017-11-03 16:27:32 -07001338
Kalhee Kim45b24182017-10-18 18:30:23 +00001339 return interfaceService.getInterfacesByPort(indirectDhcpServerConnectPoint)
1340 .stream()
1341 .filter(iface -> interfaceContainsVlan(iface, indirectDhcpConnectVlan))
1342 .findFirst()
1343 .orElse(null);
1344 }
1345
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +00001346 /**
1347 * Determind if an Interface contains a vlan id.
1348 *
1349 * @param iface the Interface
1350 * @param vlanId the vlan id
1351 * @return true if the Interface contains the vlan id
1352 */
1353 private boolean interfaceContainsVlan(Interface iface, VlanId vlanId) {
1354 if (vlanId.equals(VlanId.NONE)) {
1355 // untagged packet, check if vlan untagged or vlan native is not NONE
1356 return !iface.vlanUntagged().equals(VlanId.NONE) ||
1357 !iface.vlanNative().equals(VlanId.NONE);
1358 }
1359 // tagged packet, check if the interface contains the vlan
1360 return iface.vlanTagged().contains(vlanId);
1361 }
1362
Yi Tseng525ff402017-10-23 19:39:39 -07001363 private void requestDhcpPacket(Ip6Address serverIp) {
1364 requestServerDhcpPacket(serverIp);
1365 requestClientDhcpPacket(serverIp);
1366 }
1367
1368 private void cancelDhcpPacket(Ip6Address serverIp) {
1369 cancelServerDhcpPacket(serverIp);
1370 cancelClientDhcpPacket(serverIp);
1371 }
1372
1373 private void cancelServerDhcpPacket(Ip6Address serverIp) {
1374 TrafficSelector serverSelector =
1375 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1376 .matchIPv6Src(serverIp.toIpPrefix())
1377 .build();
1378 packetService.cancelPackets(serverSelector,
1379 PacketPriority.CONTROL,
1380 appId);
1381 }
1382
1383 private void requestServerDhcpPacket(Ip6Address serverIp) {
1384 TrafficSelector serverSelector =
1385 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1386 .matchIPv6Src(serverIp.toIpPrefix())
1387 .build();
1388 packetService.requestPackets(serverSelector,
1389 PacketPriority.CONTROL,
1390 appId);
1391 }
1392
1393 private void cancelClientDhcpPacket(Ip6Address serverIp) {
1394 // Packet comes from relay
1395 TrafficSelector indirectClientSelector =
1396 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1397 .matchIPv6Dst(serverIp.toIpPrefix())
1398 .build();
1399 packetService.cancelPackets(indirectClientSelector,
1400 PacketPriority.CONTROL,
1401 appId);
Yi Tseng41dde702017-11-02 15:21:10 -07001402 indirectClientSelector =
1403 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1404 .matchIPv6Dst(Ip6Address.ALL_DHCP_RELAY_AGENTS_AND_SERVERS.toIpPrefix())
1405 .build();
1406 packetService.cancelPackets(indirectClientSelector,
1407 PacketPriority.CONTROL,
1408 appId);
1409 indirectClientSelector =
1410 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1411 .matchIPv6Dst(Ip6Address.ALL_DHCP_SERVERS.toIpPrefix())
1412 .build();
1413 packetService.cancelPackets(indirectClientSelector,
1414 PacketPriority.CONTROL,
1415 appId);
Yi Tseng525ff402017-10-23 19:39:39 -07001416
1417 // Packet comes from client
1418 packetService.cancelPackets(CLIENT_SERVER_SELECTOR,
1419 PacketPriority.CONTROL,
1420 appId);
1421 }
1422
1423 private void requestClientDhcpPacket(Ip6Address serverIp) {
1424 // Packet comes from relay
1425 TrafficSelector indirectClientSelector =
1426 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1427 .matchIPv6Dst(serverIp.toIpPrefix())
1428 .build();
1429 packetService.requestPackets(indirectClientSelector,
1430 PacketPriority.CONTROL,
1431 appId);
Yi Tseng41dde702017-11-02 15:21:10 -07001432 indirectClientSelector =
1433 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1434 .matchIPv6Dst(Ip6Address.ALL_DHCP_RELAY_AGENTS_AND_SERVERS.toIpPrefix())
1435 .build();
1436 packetService.requestPackets(indirectClientSelector,
1437 PacketPriority.CONTROL,
1438 appId);
1439 indirectClientSelector =
1440 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1441 .matchIPv6Dst(Ip6Address.ALL_DHCP_SERVERS.toIpPrefix())
1442 .build();
1443 packetService.requestPackets(indirectClientSelector,
1444 PacketPriority.CONTROL,
1445 appId);
Yi Tseng525ff402017-10-23 19:39:39 -07001446
1447 // Packet comes from client
1448 packetService.requestPackets(CLIENT_SERVER_SELECTOR,
1449 PacketPriority.CONTROL,
1450 appId);
1451 }
1452
1453 /**
1454 * Process the ignore rules.
1455 *
1456 * @param deviceId the device id
1457 * @param vlanId the vlan to be ignored
1458 * @param op the operation, ADD to install; REMOVE to uninstall rules
1459 */
1460 private void processIgnoreVlanRule(DeviceId deviceId, VlanId vlanId, Objective.Operation op) {
1461 TrafficTreatment dropTreatment = DefaultTrafficTreatment.builder().wipeDeferred().build();
1462 AtomicInteger installedCount = new AtomicInteger(DHCP_SELECTORS.size());
1463 DHCP_SELECTORS.forEach(trafficSelector -> {
1464 TrafficSelector selector = DefaultTrafficSelector.builder(trafficSelector)
1465 .matchVlanId(vlanId)
1466 .build();
1467
1468 ForwardingObjective.Builder builder = DefaultForwardingObjective.builder()
1469 .withFlag(ForwardingObjective.Flag.VERSATILE)
1470 .withSelector(selector)
1471 .withPriority(IGNORE_CONTROL_PRIORITY)
1472 .withTreatment(dropTreatment)
1473 .fromApp(appId);
1474
1475
1476 ObjectiveContext objectiveContext = new ObjectiveContext() {
1477 @Override
1478 public void onSuccess(Objective objective) {
1479 log.info("Ignore rule {} (Vlan id {}, device {}, selector {})",
1480 op, vlanId, deviceId, selector);
1481 int countDown = installedCount.decrementAndGet();
1482 if (countDown != 0) {
1483 return;
1484 }
1485 switch (op) {
1486 case ADD:
1487 ignoredVlans.put(deviceId, vlanId);
1488 break;
1489 case REMOVE:
1490 ignoredVlans.remove(deviceId, vlanId);
1491 break;
1492 default:
1493 log.warn("Unsupported objective operation {}", op);
1494 break;
1495 }
1496 }
1497
1498 @Override
1499 public void onError(Objective objective, ObjectiveError error) {
1500 log.warn("Can't {} ignore rule (vlan id {}, selector {}, device {}) due to {}",
1501 op, vlanId, selector, deviceId, error);
1502 }
1503 };
1504
1505 ForwardingObjective fwd;
1506 switch (op) {
1507 case ADD:
1508 fwd = builder.add(objectiveContext);
1509 break;
1510 case REMOVE:
1511 fwd = builder.remove(objectiveContext);
1512 break;
1513 default:
1514 log.warn("Unsupported objective operation {}", op);
1515 return;
1516 }
1517
1518 Device device = deviceService.getDevice(deviceId);
1519 if (device == null || !device.is(Pipeliner.class)) {
1520 log.warn("Device {} is not available now, wait until device is available", deviceId);
1521 return;
1522 }
1523 flowObjectiveService.apply(deviceId, fwd);
1524 });
1525 }
Kalhee Kim121ba922017-11-01 17:56:44 +00001526
1527 /**
1528 * Find first ipaddress for a given Host info i.e. mac and vlan.
1529 *
1530 * @param clientMac client mac
1531 * @param vlanId packet's vlan
1532 * @return next-hop link-local ipaddress for a given host
1533 */
1534 private IpAddress getFirstIpByHost(MacAddress clientMac, VlanId vlanId) {
1535 IpAddress nextHopIp;
1536 // pick out the first link-local ip address
1537 HostId gwHostId = HostId.hostId(clientMac, vlanId);
1538 Host gwHost = hostService.getHost(gwHostId);
1539 if (gwHost == null) {
1540 log.warn("Can't find gateway host for hostId {}", gwHostId);
1541 return null;
1542 }
1543 nextHopIp = gwHost.ipAddresses()
1544 .stream()
1545 .filter(IpAddress::isIp6)
1546 .filter(ip6 -> ip6.isLinkLocal())
1547 .map(IpAddress::getIp6Address)
1548 .findFirst()
1549 .orElse(null);
1550 return nextHopIp;
1551 }
Yi Tseng51301292017-07-28 13:02:59 -07001552}