blob: e5f52bf79929f43a8720b9cc04e808dd551ae40b [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 Tseng7da339e2017-10-23 19:39:39 -070021import com.google.common.collect.Multimap;
Kalhee Kim1b5094f2017-09-05 19:05:06 +000022import org.apache.felix.scr.annotations.Activate;
23import org.apache.felix.scr.annotations.Deactivate;
Kalhee Kim1b5094f2017-09-05 19:05:06 +000024import com.google.common.collect.Sets;
25import com.google.common.collect.ImmutableSet;
Yi Tseng51301292017-07-28 13:02:59 -070026import org.apache.felix.scr.annotations.Component;
27import org.apache.felix.scr.annotations.Property;
Kalhee Kim1b5094f2017-09-05 19:05:06 +000028import org.apache.felix.scr.annotations.Reference;
29import org.apache.felix.scr.annotations.ReferenceCardinality;
Yi Tseng51301292017-07-28 13:02:59 -070030import org.apache.felix.scr.annotations.Service;
31import org.onlab.packet.BasePacket;
Kalhee Kim1b5094f2017-09-05 19:05:06 +000032import org.onlab.packet.DHCP6;
33import org.onlab.packet.IPv6;
34import org.onlab.packet.Ethernet;
35import org.onlab.packet.Ip6Address;
Yi Tseng51301292017-07-28 13:02:59 -070036import org.onlab.packet.IpAddress;
Kalhee Kim1b5094f2017-09-05 19:05:06 +000037import org.onlab.packet.IpPrefix;
Yi Tseng51301292017-07-28 13:02:59 -070038import org.onlab.packet.MacAddress;
Yi Tseng7da339e2017-10-23 19:39:39 -070039import org.onlab.packet.TpPort;
Kalhee Kim1b5094f2017-09-05 19:05:06 +000040import org.onlab.packet.UDP;
Yi Tseng51301292017-07-28 13:02:59 -070041import org.onlab.packet.VlanId;
Kalhee Kim1b5094f2017-09-05 19:05:06 +000042import org.onlab.packet.dhcp.Dhcp6RelayOption;
43import org.onlab.packet.dhcp.Dhcp6InterfaceIdOption;
44import org.onlab.packet.dhcp.Dhcp6Option;
45import org.onlab.packet.dhcp.Dhcp6IaNaOption;
46import org.onlab.packet.dhcp.Dhcp6IaTaOption;
47import org.onlab.packet.dhcp.Dhcp6IaPdOption;
48import org.onlab.packet.dhcp.Dhcp6IaAddressOption;
49import org.onlab.packet.dhcp.Dhcp6IaPrefixOption;
50import org.onlab.util.HexString;
Yi Tseng7da339e2017-10-23 19:39:39 -070051import org.onosproject.core.ApplicationId;
52import org.onosproject.core.CoreService;
Yi Tseng51301292017-07-28 13:02:59 -070053import org.onosproject.dhcprelay.api.DhcpHandler;
Yi Tseng2fe8f3f2017-09-07 16:22:51 -070054import org.onosproject.dhcprelay.api.DhcpServerInfo;
Yi Tseng7da339e2017-10-23 19:39:39 -070055import org.onosproject.dhcprelay.config.IgnoreDhcpConfig;
Kalhee Kim1b5094f2017-09-05 19:05:06 +000056import org.onosproject.dhcprelay.store.DhcpRelayStore;
Yi Tseng7da339e2017-10-23 19:39:39 -070057import org.onosproject.net.Device;
58import org.onosproject.net.DeviceId;
59import org.onosproject.net.behaviour.Pipeliner;
60import org.onosproject.net.device.DeviceService;
61import org.onosproject.net.flow.DefaultTrafficSelector;
62import org.onosproject.net.flow.TrafficSelector;
63import org.onosproject.net.flowobjective.DefaultForwardingObjective;
64import org.onosproject.net.flowobjective.FlowObjectiveService;
65import org.onosproject.net.flowobjective.ForwardingObjective;
66import org.onosproject.net.flowobjective.Objective;
67import org.onosproject.net.flowobjective.ObjectiveContext;
68import org.onosproject.net.flowobjective.ObjectiveError;
Yi Tseng4b013202017-09-08 17:22:51 -070069import org.onosproject.net.host.HostProvider;
70import org.onosproject.net.host.HostProviderRegistry;
71import org.onosproject.net.host.HostProviderService;
Kalhee Kim1b5094f2017-09-05 19:05:06 +000072import org.onosproject.net.host.HostService;
73import org.onosproject.net.host.DefaultHostDescription;
74import org.onosproject.net.host.HostDescription;
75import org.onosproject.net.host.InterfaceIpAddress;
76import org.onosproject.net.host.HostListener;
77import org.onosproject.net.host.HostEvent;
78import org.onosproject.net.intf.Interface;
79import org.onosproject.net.intf.InterfaceService;
Yi Tseng7da339e2017-10-23 19:39:39 -070080import org.onosproject.net.packet.PacketPriority;
Yi Tseng4b013202017-09-08 17:22:51 -070081import org.onosproject.net.provider.ProviderId;
Kalhee Kim1b5094f2017-09-05 19:05:06 +000082import org.onosproject.routeservice.Route;
83import org.onosproject.routeservice.RouteStore;
Yi Tsenge72fbb52017-08-02 15:03:31 -070084import org.onosproject.dhcprelay.config.DhcpServerConfig;
Yi Tseng51301292017-07-28 13:02:59 -070085import org.onosproject.net.ConnectPoint;
Kalhee Kim1b5094f2017-09-05 19:05:06 +000086import org.onosproject.net.Host;
87import org.onosproject.net.HostId;
88import org.onosproject.net.HostLocation;
89import org.onosproject.net.packet.DefaultOutboundPacket;
90import org.onosproject.net.packet.OutboundPacket;
Yi Tseng51301292017-07-28 13:02:59 -070091import org.onosproject.net.packet.PacketContext;
Kalhee Kim1b5094f2017-09-05 19:05:06 +000092import org.onosproject.net.packet.PacketService;
93import org.slf4j.Logger;
94import org.slf4j.LoggerFactory;
95import org.onosproject.net.flow.DefaultTrafficTreatment;
96import org.onosproject.net.flow.TrafficTreatment;
Yi Tseng51301292017-07-28 13:02:59 -070097
Kalhee Kim1b5094f2017-09-05 19:05:06 +000098
99import java.nio.ByteBuffer;
100import java.util.List;
Yi Tsenge72fbb52017-08-02 15:03:31 -0700101import java.util.Collection;
Yi Tseng51301292017-07-28 13:02:59 -0700102import java.util.Optional;
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000103import java.util.Set;
104import java.util.ArrayList;
Charles Chana18afb82018-03-05 13:14:02 -0800105import java.util.concurrent.CopyOnWriteArrayList;
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
Charles Chana18afb82018-03-05 13:14:02 -0800174 private List<DhcpServerInfo> defaultServerInfoList = new CopyOnWriteArrayList<>();
175 private List<DhcpServerInfo> indirectServerInfoList = new CopyOnWriteArrayList<>();
Yi Tseng2fe8f3f2017-09-07 16:22:51 -0700176
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
Saurav Dasba7eb1a2017-12-13 16:19:35 -0800253 public void removeIgnoreVlanState(IgnoreDhcpConfig config) {
254 if (config == null) {
255 ignoredVlans.clear();
256 return;
257 }
258 config.ignoredVlans().forEach((deviceId, vlanId) -> {
259 ignoredVlans.remove(deviceId, vlanId);
260 });
261 }
262
263 @Override
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000264 public void processDhcpPacket(PacketContext context, BasePacket payload) {
265 checkNotNull(payload, "DHCP6 payload can't be null");
266 checkState(payload instanceof DHCP6, "Payload is not a DHCP6");
267 DHCP6 dhcp6Payload = (DHCP6) payload;
268 Ethernet receivedPacket = context.inPacket().parsed();
269
270 if (!configured()) {
271 log.warn("Missing DHCP6 relay server config. Abort packet processing");
Yi Tseng30262a22017-12-18 17:50:55 -0800272 log.trace("dhcp6 payload {}", dhcp6Payload);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000273 return;
274 }
275
276 byte msgType = dhcp6Payload.getMsgType();
Yi Tseng30262a22017-12-18 17:50:55 -0800277 log.trace("msgType is {}", msgType);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000278
279 ConnectPoint inPort = context.inPacket().receivedFrom();
280 if (inPort == null) {
Yi Tseng30262a22017-12-18 17:50:55 -0800281 log.trace("incommin ConnectPoint is null");
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000282 }
283 Set<Interface> receivingInterfaces = interfaceService.getInterfacesByPort(inPort);
284 //ignore the packets if dhcp client interface is not configured on onos.
285 if (receivingInterfaces.isEmpty()) {
286 log.warn("Virtual interface is not configured on {}", inPort);
287 return;
288 }
289
290
291 if (MSG_TYPE_FROM_CLIENT.contains(msgType)) {
292
293 InternalPacket ethernetClientPacket =
294 processDhcp6PacketFromClient(context, receivedPacket, receivingInterfaces);
295 if (ethernetClientPacket != null) {
296 forwardPacket(ethernetClientPacket);
297 }
298
299 } else if (MSG_TYPE_FROM_SERVER.contains(msgType)) {
Yi Tseng30262a22017-12-18 17:50:55 -0800300 log.trace("calling processDhcp6PacketFromServer with RELAY_REPL", msgType);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000301 InternalPacket ethernetPacketReply =
302 processDhcp6PacketFromServer(context, receivedPacket, receivingInterfaces);
303 if (ethernetPacketReply != null) {
304 forwardPacket(ethernetPacketReply);
305 }
306 } else {
Yi Tseng30262a22017-12-18 17:50:55 -0800307 log.warn("DHCP type {} not supported yet", msgType);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000308 }
309 }
310
311
312 /**
313 * Checks if this app has been configured.
314 *
315 * @return true if all information we need have been initialized
316 */
317 public boolean configured() {
Yi Tseng2fe8f3f2017-09-07 16:22:51 -0700318 return !defaultServerInfoList.isEmpty();
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000319 }
320
Yi Tseng4b013202017-09-08 17:22:51 -0700321 @Override
322 public ProviderId id() {
Charles Chan75edab72017-09-12 17:09:32 -0700323 return PROVIDER_ID;
Yi Tseng4b013202017-09-08 17:22:51 -0700324 }
325
326 @Override
327 public void triggerProbe(Host host) {
328 // Do nothing here
329 }
330
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000331 // the new class the contains Ethernet packet and destination port, kind of like adding
332 // internal header to the packet
333 private class InternalPacket {
334 Ethernet packet;
335 ConnectPoint destLocation;
336 public InternalPacket(Ethernet newPacket, ConnectPoint newLocation) {
337 packet = newPacket;
338 destLocation = newLocation;
339 }
340 void setLocation(ConnectPoint newLocation) {
341 destLocation = newLocation;
342 }
343 }
344
345 //forward the packet to ConnectPoint where the DHCP server is attached.
346 private void forwardPacket(InternalPacket packet) {
347 //send Packetout to dhcp server connectpoint.
348 if (packet.destLocation != null) {
349 TrafficTreatment t = DefaultTrafficTreatment.builder()
350 .setOutput(packet.destLocation.port()).build();
351 OutboundPacket o = new DefaultOutboundPacket(
352 packet.destLocation.deviceId(), t, ByteBuffer.wrap(packet.packet.serialize()));
353 if (log.isTraceEnabled()) {
354 log.trace("Relaying packet to destination {}", packet.destLocation);
355 }
356 packetService.emit(o);
357 } // if
358 }
359
360 /**
361 * Check if the host is directly connected to the network or not.
362 *
363 * @param dhcp6Payload the dhcp6 payload
364 * @return true if the host is directly connected to the network; false otherwise
365 */
366 private boolean directlyConnected(DHCP6 dhcp6Payload) {
367 log.debug("directlyConnected enters");
368
369 if (dhcp6Payload.getMsgType() != DHCP6.MsgType.RELAY_FORW.value() &&
370 dhcp6Payload.getMsgType() != DHCP6.MsgType.RELAY_REPL.value()) {
371 log.debug("directlyConnected true. MsgType {}", dhcp6Payload.getMsgType());
372
373 return true;
374 }
375
376 // Regardless of relay-forward or relay-replay, check if we see another relay message
377 DHCP6 dhcp6Payload2 = dhcp6PacketFromRelayPacket(dhcp6Payload);
378 if (dhcp6Payload2 != null) {
379 if (dhcp6Payload.getMsgType() == DHCP6.MsgType.RELAY_FORW.value()) {
380 log.debug("directlyConnected false. 1st realy-foward, 2nd MsgType {}", dhcp6Payload2.getMsgType());
381 return false;
382 } else {
383 // relay-reply
384 if (dhcp6Payload2.getMsgType() != DHCP6.MsgType.RELAY_REPL.value()) {
385 log.debug("directlyConnected true. 2nd MsgType {}", dhcp6Payload2.getMsgType());
386 return true; // must be directly connected
387 } else {
388 log.debug("directlyConnected false. 1st relay-reply, 2nd relay-reply MsgType {}",
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000389 dhcp6Payload2.getMsgType());
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000390 return false; // must be indirectly connected
391 }
392 }
393 } else {
Yi Tseng30262a22017-12-18 17:50:55 -0800394 log.trace("directlyConnected true.");
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000395 return true;
396 }
397 }
398
399 /**
400 * extract DHCP6 payload from dhcp6 relay message within relay-forwrd/reply.
401 *
402 * @param dhcp6 dhcp6 relay-reply or relay-foward
403 * @return dhcp6Packet dhcp6 packet extracted from relay-message
404 */
405 private DHCP6 dhcp6PacketFromRelayPacket(DHCP6 dhcp6) {
406 log.debug("dhcp6PacketFromRelayPacket enters. dhcp6 {}", dhcp6);
407
408 // extract the relay message if exist
409 DHCP6 dhcp6Payload = dhcp6.getOptions().stream()
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000410 .filter(opt -> opt instanceof Dhcp6RelayOption)
411 .map(BasePacket::getPayload)
412 .map(pld -> (DHCP6) pld)
413 .findFirst()
414 .orElse(null);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000415
416
417 if (dhcp6Payload == null) {
418 // Can't find dhcp payload
419 log.debug("Can't find dhcp6 payload from relay message");
420 } else {
421 log.debug("dhcp6 payload found from relay message {}", dhcp6Payload);
422 }
423
424 return dhcp6Payload;
425 }
426
427 /**
428 * find the leaf DHCP6 packet from multi-level relay packet.
429 *
430 * @param relayPacket dhcp6 relay packet
431 * @return leafPacket non-relay dhcp6 packet
432 */
433 private DHCP6 getDhcp6Leaf(DHCP6 relayPacket) {
434 DHCP6 dhcp6Parent = relayPacket;
435 DHCP6 dhcp6Child = null;
436
437 log.debug("getDhcp6Leaf entered.");
438 while (dhcp6Parent != null) {
439 dhcp6Child = dhcp6PacketFromRelayPacket(dhcp6Parent);
440
441 if (dhcp6Child != null) {
442 if (dhcp6Child.getMsgType() != DHCP6.MsgType.RELAY_FORW.value() &&
443 dhcp6Child.getMsgType() != DHCP6.MsgType.RELAY_REPL.value()) {
444 log.debug("leaf dhcp6 packet found.");
445 break;
446 } else {
447 // found another relay
448 // go for another loop
449 dhcp6Parent = dhcp6Child;
450 }
451 } else {
Yi Tseng30262a22017-12-18 17:50:55 -0800452 log.warn("Expected dhcp6 within relay pkt, but no dhcp6 leaf found.");
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000453 break;
454 }
455 }
456 return dhcp6Child;
457 }
458
459 /**
460 * check if DHCP6 relay-reply is reply.
461 *
462 * @param relayPacket dhcp6 relay-reply
463 * @return boolean relay-reply contains ack
464 */
465 private boolean isDhcp6Reply(DHCP6 relayPacket) {
466 log.debug("isDhcp6Reply entered.");
467
468 DHCP6 leafDhcp6 = getDhcp6Leaf(relayPacket);
469
470 if (leafDhcp6 != null) {
471 if (leafDhcp6.getMsgType() == DHCP6.MsgType.REPLY.value()) {
472 log.debug("isDhcp6Reply true.");
473 return true; // must be directly connected
474 } else {
475 log.debug("isDhcp6Reply false. leaf dhcp6 is not replay. MsgType {}", leafDhcp6.getMsgType());
476 }
477 } else {
478 log.debug("isDhcp6Reply false. Expected dhcp6 within relay pkt but not found.");
479 }
480 log.debug("isDhcp6Reply false.");
481 return false;
482 }
483
484 /**
485 * check if DHCP6 is release or relay-forward contains release.
486 *
487 * @param dhcp6Payload dhcp6 packet
488 * @return boolean dhcp6 contains release
489 */
490 private boolean isDhcp6Release(DHCP6 dhcp6Payload) {
491
492 log.debug("isDhcp6Release entered.");
493
494 if (dhcp6Payload.getMsgType() == DHCP6.MsgType.RELEASE.value()) {
495 log.debug("isDhcp6Release true.");
496 return true; // must be directly connected
497 } else {
498 DHCP6 dhcp6Leaf = getDhcp6Leaf(dhcp6Payload);
499 if (dhcp6Leaf != null) {
500 if (dhcp6Leaf.getMsgType() == DHCP6.MsgType.RELEASE.value()) {
501 log.debug("isDhcp6Release true. indirectlry connected");
502 return true;
503 } else {
504 log.debug("leaf dhcp6 is not release. MsgType {}", dhcp6Leaf.getMsgType());
505 return false;
506 }
507 } else {
508 log.debug("isDhcp6Release false. dhcp6 is niether relay nor release.");
509 return false;
510 }
511 }
512 }
513
514 /**
515 * extract from dhcp6 packet client ipv6 address of given by dhcp server.
516 *
517 * @param dhcp6 the dhcp6 packet
518 * @return Ip6Address Ip6Address given by dhcp server, or null if not exists
519 */
520 private Ip6Address extractIpAddress(DHCP6 dhcp6) {
521 Ip6Address ip = null;
522
523 log.debug("extractIpAddress enters dhcp6 {}.", dhcp6);
524 // Extract IPv6 address from IA NA ot IA TA option
525 Optional<Dhcp6IaNaOption> iaNaOption = dhcp6.getOptions()
526 .stream()
527 .filter(opt -> opt instanceof Dhcp6IaNaOption)
528 .map(opt -> (Dhcp6IaNaOption) opt)
529 .findFirst();
530 Optional<Dhcp6IaTaOption> iaTaOption = dhcp6.getOptions()
531 .stream()
532 .filter(opt -> opt instanceof Dhcp6IaTaOption)
533 .map(opt -> (Dhcp6IaTaOption) opt)
534 .findFirst();
535 Optional<Dhcp6IaAddressOption> iaAddressOption;
536 if (iaNaOption.isPresent()) {
537 log.debug("Found IPv6 address from iaNaOption {}", iaNaOption);
538
539 iaAddressOption = iaNaOption.get().getOptions().stream()
540 .filter(opt -> opt instanceof Dhcp6IaAddressOption)
541 .map(opt -> (Dhcp6IaAddressOption) opt)
542 .findFirst();
543 } else if (iaTaOption.isPresent()) {
544 log.debug("Found IPv6 address from iaTaOption {}", iaTaOption);
545
546 iaAddressOption = iaTaOption.get().getOptions().stream()
547 .filter(opt -> opt instanceof Dhcp6IaAddressOption)
548 .map(opt -> (Dhcp6IaAddressOption) opt)
549 .findFirst();
550 } else {
551 iaAddressOption = Optional.empty();
552 }
553 if (iaAddressOption.isPresent()) {
554 ip = iaAddressOption.get().getIp6Address();
555 log.debug("Found IPv6 address from iaAddressOption {}", iaAddressOption);
556
557
558 } else {
559 log.debug("Can't find IPv6 address from DHCPv6 {}", dhcp6);
560 }
561
562 return ip;
563 }
564 /**
565 * extract from dhcp6 packet Prefix prefix provided by dhcp server.
566 *
567 * @param dhcp6 the dhcp6 payload
568 * @return IpPrefix Prefix Delegation prefix, or null if not exists.
569 */
570 private IpPrefix extractPrefix(DHCP6 dhcp6) {
Yi Tseng30262a22017-12-18 17:50:55 -0800571 log.trace("extractPrefix enters {}", dhcp6);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000572
573 // extract prefix
574 IpPrefix prefixPrefix = null;
575
576 Ip6Address prefixAddress = null;
577
578 // Extract IPv6 prefix from IA PD option
579 Optional<Dhcp6IaPdOption> iaPdOption = dhcp6.getOptions()
580 .stream()
581 .filter(opt -> opt instanceof Dhcp6IaPdOption)
582 .map(opt -> (Dhcp6IaPdOption) opt)
583 .findFirst();
584
585 Optional<Dhcp6IaPrefixOption> iaPrefixOption;
586 if (iaPdOption.isPresent()) {
Yi Tseng30262a22017-12-18 17:50:55 -0800587 log.debug("IA_PD option found {}", iaPdOption);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000588
589 iaPrefixOption = iaPdOption.get().getOptions().stream()
590 .filter(opt -> opt instanceof Dhcp6IaPrefixOption)
591 .map(opt -> (Dhcp6IaPrefixOption) opt)
592 .findFirst();
593 } else {
Yi Tseng30262a22017-12-18 17:50:55 -0800594 log.debug("IA_PD option NOT found");
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000595
596 iaPrefixOption = Optional.empty();
597 }
598 if (iaPrefixOption.isPresent()) {
Yi Tseng30262a22017-12-18 17:50:55 -0800599 log.trace("IAPrefix Option within IA_PD option found {}", iaPrefixOption);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000600
601 prefixAddress = iaPrefixOption.get().getIp6Prefix();
602 int prefixLen = (int) iaPrefixOption.get().getPrefixLength();
Yi Tseng30262a22017-12-18 17:50:55 -0800603 log.debug("Prefix length is {} bits", prefixLen);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000604 prefixPrefix = IpPrefix.valueOf(prefixAddress, prefixLen);
605
606 } else {
Yi Tseng30262a22017-12-18 17:50:55 -0800607 log.debug("Can't find IPv6 prefix from DHCPv6 {}", dhcp6);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000608 }
609
610 return prefixPrefix;
611 }
612
613 /**
614 * remove host or route.
615 *
616 * @param directConnFlag flag to show that packet is from directly connected client
617 * @param dhcp6Packet the dhcp6 payload
618 * @param clientPacket client's ethernet packet
619 * @param clientIpv6 client's Ipv6 packet
Kalhee Kimaa5172a2017-09-15 17:43:27 +0000620 * @param clientInterface client interfaces
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000621 */
622 private void removeHostOrRoute(boolean directConnFlag, DHCP6 dhcp6Packet,
623 Ethernet clientPacket, IPv6 clientIpv6,
Kalhee Kimaa5172a2017-09-15 17:43:27 +0000624 Interface clientInterface) {
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000625 log.debug("extractPrefix enters {}", dhcp6Packet);
Kalhee Kimc60c7e22017-11-01 17:56:44 +0000626 VlanId vlanId = clientInterface.vlan();
627 MacAddress clientMac = clientPacket.getSourceMAC();
628 log.debug("client mac {} client vlan {}", HexString.toHexString(clientMac.toBytes(), ":"), vlanId);
629
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000630 // add host or route
631 if (isDhcp6Release(dhcp6Packet)) {
632 IpAddress ip = null;
633 if (directConnFlag) {
634 // Add to host store if it is connected to network directly
635 ip = extractIpAddress(dhcp6Packet);
636 if (ip != null) {
Kalhee Kimc60c7e22017-11-01 17:56:44 +0000637
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000638 HostId hostId = HostId.hostId(clientMac, vlanId);
639 log.debug("remove Host {} ip for directly connected.", hostId.toString());
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000640 // Remove host's ip of when dhcp release msg is received
Yi Tseng4b013202017-09-08 17:22:51 -0700641 providerService.removeIpFromHost(hostId, ip);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000642 } else {
643 log.debug("ipAddress not found. Do not add Host for directly connected.");
644 }
645 } else {
646 // Remove from route store if it is not connected to network directly
Kalhee Kimc60c7e22017-11-01 17:56:44 +0000647 // pick out the first link-local ip address
648 IpAddress nextHopIp = getFirstIpByHost(clientMac, vlanId);
649 if (nextHopIp == null) {
650 log.warn("Can't find link-local IP address of gateway mac {} vlanId {}",
651 clientMac, vlanId);
652 return;
653 }
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000654
655 DHCP6 leafDhcp = getDhcp6Leaf(dhcp6Packet);
656 ip = extractIpAddress(leafDhcp);
657 if (ip == null) {
658 log.debug("ip is null");
659 } else {
660 Route routeForIP = new Route(Route.Source.STATIC, ip.toIpPrefix(), nextHopIp);
661 log.debug("removing route of 128 address for indirectly connected.");
662 log.debug("128 ip {}, nexthop {}", HexString.toHexString(ip.toOctets(), ":"),
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000663 HexString.toHexString(nextHopIp.toOctets(), ":"));
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000664 routeStore.removeRoute(routeForIP);
665 }
666
667 IpPrefix ipPrefix = extractPrefix(leafDhcp);
668 if (ipPrefix == null) {
669 log.debug("ipPrefix is null ");
670 } else {
671 Route routeForPrefix = new Route(Route.Source.STATIC, ipPrefix, nextHopIp);
672 log.debug("removing route of PD for indirectly connected.");
673 log.debug("pd ip {}, nexthop {}", HexString.toHexString(ipPrefix.address().toOctets(), ":"),
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000674 HexString.toHexString(nextHopIp.toOctets(), ":"));
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000675
676 routeStore.removeRoute(routeForPrefix);
677 }
678 }
679 }
680 }
681
682 /**
683 * add host or route.
684 *
685 * @param directConnFlag flag to show that packet is from directly connected client
686 * @param dhcp6Relay the dhcp6 payload
687 * @param embeddedDhcp6 client's ethernet packetthe dhcp6 payload within relay
688 * @param clientMac client macAddress
Kalhee Kimaa5172a2017-09-15 17:43:27 +0000689 * @param clientInterface client interface
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000690 */
691 private void addHostOrRoute(boolean directConnFlag, DHCP6 dhcp6Relay,
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000692 DHCP6 embeddedDhcp6,
693 MacAddress clientMac,
694 Interface clientInterface) {
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000695 log.debug("addHostOrRoute entered.");
Kalhee Kimc60c7e22017-11-01 17:56:44 +0000696 VlanId vlanId = clientInterface.vlan();
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000697 // add host or route
698 if (isDhcp6Reply(dhcp6Relay)) {
699 IpAddress ip = null;
700 if (directConnFlag) {
701 // Add to host store if it connect to network directly
702 ip = extractIpAddress(embeddedDhcp6);
703 if (ip != null) {
704 Set<IpAddress> ips = Sets.newHashSet(ip);
Yi Tseng4b013202017-09-08 17:22:51 -0700705
706 // FIXME: we should use vlan id from original packet (solicit, request)
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000707 HostId hostId = HostId.hostId(clientMac, vlanId);
Yi Tseng4b013202017-09-08 17:22:51 -0700708 Host host = hostService.getHost(hostId);
Kalhee Kimaa5172a2017-09-15 17:43:27 +0000709 HostLocation hostLocation = new HostLocation(clientInterface.connectPoint(),
Yi Tseng4b013202017-09-08 17:22:51 -0700710 System.currentTimeMillis());
711 Set<HostLocation> hostLocations = Sets.newHashSet(hostLocation);
712
713 if (host != null) {
714 // Dual homing support:
715 // if host exists, use old locations and new location
716 hostLocations.addAll(host.locations());
717 }
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000718 HostDescription desc = new DefaultHostDescription(clientMac, vlanId,
Yi Tseng4b013202017-09-08 17:22:51 -0700719 hostLocations, ips,
720 false);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000721 log.debug("adding Host for directly connected.");
722 log.debug("client mac {} client vlan {} hostlocation {}",
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000723 HexString.toHexString(clientMac.toBytes(), ":"),
724 vlanId, hostLocation.toString());
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000725
726 // Replace the ip when dhcp server give the host new ip address
Yi Tseng4b013202017-09-08 17:22:51 -0700727 providerService.hostDetected(hostId, desc, false);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000728 } else {
Yi Tseng30262a22017-12-18 17:50:55 -0800729 log.debug("ipAddress not found. Do not add Host for directly connected.");
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000730 }
731 } else {
732 // Add to route store if it does not connect to network directly
Kalhee Kimc60c7e22017-11-01 17:56:44 +0000733 // pick out the first link-local ip address
734 IpAddress nextHopIp = getFirstIpByHost(clientMac, vlanId);
735 if (nextHopIp == null) {
736 log.warn("Can't find link-local IP address of gateway mac {} vlanId {}",
737 clientMac, vlanId);
738 return;
739 }
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000740
741 DHCP6 leafDhcp = getDhcp6Leaf(embeddedDhcp6);
742 ip = extractIpAddress(leafDhcp);
743 if (ip == null) {
Yi Tseng30262a22017-12-18 17:50:55 -0800744 log.trace("ip is null");
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000745 } else {
746 Route routeForIP = new Route(Route.Source.STATIC, ip.toIpPrefix(), nextHopIp);
Yi Tseng30262a22017-12-18 17:50:55 -0800747 log.trace("adding Route of 128 address for indirectly connected.");
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000748 routeStore.updateRoute(routeForIP);
749 }
750
751 IpPrefix ipPrefix = extractPrefix(leafDhcp);
752 if (ipPrefix == null) {
Yi Tseng30262a22017-12-18 17:50:55 -0800753 log.trace("ipPrefix is null ");
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000754 } else {
755 Route routeForPrefix = new Route(Route.Source.STATIC, ipPrefix, nextHopIp);
Yi Tseng30262a22017-12-18 17:50:55 -0800756 log.trace("adding Route of PD for indirectly connected.");
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000757 routeStore.updateRoute(routeForPrefix);
758 }
759 }
760 }
761 }
762
763 /**
Yi Tsengc44dc2e2017-11-03 16:27:32 -0700764 * Build the DHCP6 solicit/request packet with gatewayip.
765 * TODO: method too long, need to be refactored.
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000766 *
767 * @param context packet context
768 * @param clientPacket client ethernet packet
769 * @param clientInterfaces set of client side interfaces
770 */
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000771 private InternalPacket processDhcp6PacketFromClient(PacketContext context,
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000772 Ethernet clientPacket, Set<Interface> clientInterfaces) {
Yi Tsengc44dc2e2017-11-03 16:27:32 -0700773 ConnectPoint receivedFrom = context.inPacket().receivedFrom();
774 DeviceId receivedFromDevice = receivedFrom.deviceId();
775 DhcpServerInfo serverInfo;
776 Ip6Address dhcpServerIp = null;
777 ConnectPoint dhcpServerConnectPoint = null;
778 MacAddress dhcpConnectMac = null;
779 VlanId dhcpConnectVlan = null;
780 Ip6Address dhcpGatewayIp = null;
781 Ip6Address indirectDhcpServerIp = null;
782 ConnectPoint indirectDhcpServerConnectPoint = null;
783 MacAddress indirectDhcpConnectMac = null;
784 VlanId indirectDhcpConnectVlan = null;
785 Ip6Address indirectDhcpGatewayIp = null;
786 Ip6Address indirectRelayAgentIpFromCfg = null;
787 if (!defaultServerInfoList.isEmpty()) {
788 serverInfo = defaultServerInfoList.get(0);
789 dhcpConnectMac = serverInfo.getDhcpConnectMac().orElse(null);
790 dhcpGatewayIp = serverInfo.getDhcpGatewayIp6().orElse(null);
791 dhcpServerIp = serverInfo.getDhcpServerIp6().orElse(null);
792 dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
793 dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
794 }
795 if (!indirectServerInfoList.isEmpty()) {
796 serverInfo = indirectServerInfoList.get(0);
797 indirectDhcpConnectMac = serverInfo.getDhcpConnectMac().orElse(null);
798 indirectDhcpGatewayIp = serverInfo.getDhcpGatewayIp6().orElse(null);
799 indirectDhcpServerIp = serverInfo.getDhcpServerIp6().orElse(null);
800 indirectDhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
801 indirectDhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
802 indirectRelayAgentIpFromCfg = serverInfo.getRelayAgentIp6(receivedFromDevice).orElse(null);
803 }
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000804 Ip6Address relayAgentIp = getRelayAgentIPv6Address(clientInterfaces);
805 MacAddress relayAgentMac = clientInterfaces.iterator().next().mac();
806 if (relayAgentIp == null || relayAgentMac == null) {
807 log.warn("Missing DHCP relay agent interface Ipv6 addr config for "
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000808 + "packet from client on port: {}. Aborting packet processing",
809 clientInterfaces.iterator().next().connectPoint());
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000810 return null;
811 }
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000812 // get dhcp6 header.
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000813 IPv6 clientIpv6 = (IPv6) clientPacket.getPayload();
814 UDP clientUdp = (UDP) clientIpv6.getPayload();
815 DHCP6 clientDhcp6 = (DHCP6) clientUdp.getPayload();
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000816 boolean directConnFlag = directlyConnected(clientDhcp6);
Yi Tsengdc510082017-11-29 14:39:18 -0800817 Interface serverInterface;
818 if (directConnFlag) {
819 serverInterface = getServerInterface();
820 } else {
821 serverInterface = getIndirectServerInterface();
822 if (serverInterface == null) {
823 // Indirect server interface not found, use default server interface
824 serverInterface = getServerInterface();
825 }
826 }
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000827 if (serverInterface == null) {
828 log.warn("Can't get {} server interface, ignore", directConnFlag ? "direct" : "indirect");
829 return null;
830 }
831 Ip6Address ipFacingServer = getFirstIpFromInterface(serverInterface);
832 MacAddress macFacingServer = serverInterface.mac();
833 if (ipFacingServer == null || macFacingServer == null) {
834 log.warn("No IP v6 address for server Interface {}", serverInterface);
835 return null;
836 }
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000837 Ethernet etherReply = (Ethernet) clientPacket.clone();
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000838 etherReply.setSourceMACAddress(macFacingServer);
Yi Tsengc44dc2e2017-11-03 16:27:32 -0700839 if ((directConnFlag && dhcpConnectMac == null) ||
840 !directConnFlag && indirectDhcpConnectMac == null && dhcpConnectMac == null) {
Yi Tseng30262a22017-12-18 17:50:55 -0800841 log.trace("Packet received from {} connected client.", directConnFlag ? "directly" : "indirectly");
Charles Chanf6a77be2017-10-30 13:44:33 -0700842 log.warn("DHCP6 {} not yet resolved .. Aborting DHCP packet processing from client on port: {}",
Yi Tsengc44dc2e2017-11-03 16:27:32 -0700843 (dhcpGatewayIp == null) ? "server IP " + dhcpServerIp
844 : "gateway IP " + dhcpGatewayIp,
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000845 clientInterfaces.iterator().next().connectPoint());
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000846 return null;
847 }
Yi Tsengc44dc2e2017-11-03 16:27:32 -0700848 if (dhcpServerConnectPoint == null) {
Kalhee Kim2575d2c2017-09-26 20:21:53 +0000849 log.warn("DHCP6 server connection point direct {} directConn {} indirectConn {} is not set up yet",
Yi Tsengc44dc2e2017-11-03 16:27:32 -0700850 directConnFlag, dhcpServerConnectPoint, indirectDhcpServerConnectPoint);
851
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000852 return null;
853 }
854
Yi Tsengc44dc2e2017-11-03 16:27:32 -0700855 etherReply.setDestinationMACAddress(dhcpConnectMac);
856 etherReply.setVlanID(dhcpConnectVlan.toShort());
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000857
858 IPv6 ipv6Packet = (IPv6) etherReply.getPayload();
Kalhee Kimc60c7e22017-11-01 17:56:44 +0000859 byte[] peerAddress = clientIpv6.getSourceAddress();
Kalhee Kim45c93852017-10-27 20:16:26 +0000860 ipv6Packet.setSourceAddress(ipFacingServer.toOctets());
Yi Tsengc44dc2e2017-11-03 16:27:32 -0700861 ipv6Packet.setDestinationAddress(dhcpServerIp.toOctets());
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000862
863 UDP udpPacket = (UDP) ipv6Packet.getPayload();
864 udpPacket.setSourcePort(UDP.DHCP_V6_SERVER_PORT);
865 DHCP6 dhcp6Packet = (DHCP6) udpPacket.getPayload();
866 byte[] dhcp6PacketByte = dhcp6Packet.serialize();
867
868 // notify onos and quagga to release PD
869 //releasePD(dhcp6Packet);
Kalhee Kimaa5172a2017-09-15 17:43:27 +0000870 ConnectPoint clientConnectionPoint = context.inPacket().receivedFrom();
871 VlanId vlanIdInUse = VlanId.vlanId(clientPacket.getVlanID());
872 Interface clientInterface = interfaceService.getInterfacesByPort(clientConnectionPoint)
Charles Chanf6a77be2017-10-30 13:44:33 -0700873 .stream().filter(iface -> interfaceContainsVlan(iface, vlanIdInUse))
874 .findFirst().orElse(null);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000875
Kalhee Kimaa5172a2017-09-15 17:43:27 +0000876 removeHostOrRoute(directConnFlag, dhcp6Packet, clientPacket, clientIpv6, clientInterface);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000877
878 DHCP6 dhcp6Relay = new DHCP6();
879 dhcp6Relay.setMsgType(DHCP6.MsgType.RELAY_FORW.value());
880 // link address: server uses the address to identify the link on which the client
881 // is located.
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000882 if (directConnFlag) {
883 dhcp6Relay.setLinkAddress(relayAgentIp.toOctets());
884 log.debug("direct connection: relayAgentIp obtained dynamically {}",
885 HexString.toHexString(relayAgentIp.toOctets(), ":"));
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000886
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000887 } else {
Yi Tsengc44dc2e2017-11-03 16:27:32 -0700888 if (indirectDhcpServerIp == null) {
Yi Tseng30262a22017-12-18 17:50:55 -0800889 log.debug("indirect DhcpServerIp not available, use default DhcpServerIp {}",
Yi Tsengc44dc2e2017-11-03 16:27:32 -0700890 HexString.toHexString(dhcpServerIp.toOctets()));
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000891 } else {
892 // Indirect case, replace destination to indirect dhcp server if exist
893 // Check if mac is obtained for valid server ip
Yi Tsengc44dc2e2017-11-03 16:27:32 -0700894 if (indirectDhcpConnectMac == null) {
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000895 log.warn("DHCP6 {} not yet resolved .. Aborting DHCP "
896 + "packet processing from client on port: {}",
Yi Tsengc44dc2e2017-11-03 16:27:32 -0700897 (indirectDhcpGatewayIp == null) ? "server IP " + indirectDhcpServerIp
898 : "gateway IP " + indirectDhcpGatewayIp,
Kalhee Kim2575d2c2017-09-26 20:21:53 +0000899 clientInterfaces.iterator().next().connectPoint());
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000900 return null;
901 }
Yi Tsengc44dc2e2017-11-03 16:27:32 -0700902 etherReply.setDestinationMACAddress(indirectDhcpConnectMac);
903 etherReply.setVlanID(indirectDhcpConnectVlan.toShort());
904 ipv6Packet.setDestinationAddress(indirectDhcpServerIp.toOctets());
Kalhee Kim2575d2c2017-09-26 20:21:53 +0000905
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000906 }
Yi Tsengc44dc2e2017-11-03 16:27:32 -0700907 if (indirectRelayAgentIpFromCfg == null) {
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000908 dhcp6Relay.setLinkAddress(relayAgentIp.toOctets());
Yi Tseng30262a22017-12-18 17:50:55 -0800909 log.debug("indirect connection: relayAgentIp NOT availale from config file! Use dynamic. {}",
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000910 HexString.toHexString(relayAgentIp.toOctets(), ":"));
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000911 } else {
Yi Tsengc44dc2e2017-11-03 16:27:32 -0700912 dhcp6Relay.setLinkAddress(indirectRelayAgentIpFromCfg.toOctets());
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000913 log.debug("indirect connection: relayAgentIp from config file is available! {}",
Yi Tsengc44dc2e2017-11-03 16:27:32 -0700914 HexString.toHexString(indirectRelayAgentIpFromCfg.toOctets(), ":"));
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000915 }
916 }
Yi Tsengc44dc2e2017-11-03 16:27:32 -0700917 // peer address: address of the client or relay agent from which
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000918 // the message to be relayed was received.
Kalhee Kimc60c7e22017-11-01 17:56:44 +0000919 dhcp6Relay.setPeerAddress(peerAddress);
Yi Tsengc44dc2e2017-11-03 16:27:32 -0700920 List<Dhcp6Option> options = new ArrayList<>();
921 // directly connected case, hop count is zero; otherwise, hop count + 1
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000922 if (directConnFlag) {
923 dhcp6Relay.setHopCount((byte) 0);
924 } else {
925 dhcp6Relay.setHopCount((byte) (dhcp6Packet.getHopCount() + 1));
926 }
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000927 // create relay message option
928 Dhcp6Option relayMessage = new Dhcp6Option();
929 relayMessage.setCode(DHCP6.OptionCode.RELAY_MSG.value());
930 relayMessage.setLength((short) dhcp6PacketByte.length);
931 relayMessage.setData(dhcp6PacketByte);
932 options.add(relayMessage);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000933 // create interfaceId option
Kalhee Kimaa5172a2017-09-15 17:43:27 +0000934 String inPortString = "-" + context.inPacket().receivedFrom().toString() + ":";
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000935 Dhcp6Option interfaceId = new Dhcp6Option();
936 interfaceId.setCode(DHCP6.OptionCode.INTERFACE_ID.value());
937 byte[] clientSoureMacBytes = clientPacket.getSourceMACAddress();
938 byte[] inPortStringBytes = inPortString.getBytes();
Kalhee Kimaa5172a2017-09-15 17:43:27 +0000939 byte[] vlanIdBytes = new byte[2];
940 vlanIdBytes[0] = (byte) (clientPacket.getVlanID() & 0xff);
941 vlanIdBytes[1] = (byte) ((clientPacket.getVlanID() >> 8) & 0xff);
942 byte[] interfaceIdBytes = new byte[clientSoureMacBytes.length +
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000943 inPortStringBytes.length + vlanIdBytes.length];
Kalhee Kimaa5172a2017-09-15 17:43:27 +0000944 log.debug("Length: interfaceIdBytes {} clientSoureMacBytes {} inPortStringBytes {} vlan {}",
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000945 interfaceIdBytes.length, clientSoureMacBytes.length, inPortStringBytes.length,
946 vlanIdBytes.length);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000947 System.arraycopy(clientSoureMacBytes, 0, interfaceIdBytes, 0, clientSoureMacBytes.length);
948 System.arraycopy(inPortStringBytes, 0, interfaceIdBytes, clientSoureMacBytes.length, inPortStringBytes.length);
Kalhee Kimaa5172a2017-09-15 17:43:27 +0000949 System.arraycopy(vlanIdBytes, 0, interfaceIdBytes, clientSoureMacBytes.length + inPortStringBytes.length,
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000950 vlanIdBytes.length);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000951 interfaceId.setData(interfaceIdBytes);
952 interfaceId.setLength((short) interfaceIdBytes.length);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000953 options.add(interfaceId);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000954 log.debug("interfaceId write srcMac {} portString {}",
955 HexString.toHexString(clientSoureMacBytes, ":"), inPortString);
956 dhcp6Relay.setOptions(options);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000957 udpPacket.setPayload(dhcp6Relay);
958 udpPacket.resetChecksum();
959 ipv6Packet.setPayload(udpPacket);
Charles Chan7edf7642017-10-09 11:07:25 -0400960 ipv6Packet.setHopLimit((byte) 64);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000961 etherReply.setPayload(ipv6Packet);
Yi Tsengc44dc2e2017-11-03 16:27:32 -0700962 if (directConnFlag || indirectDhcpServerIp == null) {
963 return new InternalPacket(etherReply, dhcpServerConnectPoint);
Kalhee Kim2575d2c2017-09-26 20:21:53 +0000964 } else {
Yi Tsengc44dc2e2017-11-03 16:27:32 -0700965 return new InternalPacket(etherReply, indirectDhcpServerConnectPoint);
Kalhee Kim2575d2c2017-09-26 20:21:53 +0000966 }
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000967 }
968
Yi Tsengc44dc2e2017-11-03 16:27:32 -0700969
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000970 /**
971 *
972 * process the DHCP6 relay-reply packet from dhcp server.
973 *
974 * @param context packet context
975 * @param receivedPacket server ethernet packet
976 * @param recevingInterfaces set of server side interfaces
977 */
978 private InternalPacket processDhcp6PacketFromServer(PacketContext context,
979 Ethernet receivedPacket, Set<Interface> recevingInterfaces) {
Yi Tsengc44dc2e2017-11-03 16:27:32 -0700980
981 ConnectPoint receivedFrom = context.inPacket().receivedFrom();
982 DeviceId receivedFromDevice = receivedFrom.deviceId();
983
984 // TODO: refactor
985 DhcpServerInfo serverInfo;
986 Ip6Address dhcpServerIp = null;
987 ConnectPoint dhcpServerConnectPoint = null;
988 MacAddress dhcpConnectMac = null;
989 VlanId dhcpConnectVlan = null;
990 Ip6Address dhcpGatewayIp = null;
991
992 Ip6Address indirectDhcpServerIp = null;
993 ConnectPoint indirectDhcpServerConnectPoint = null;
994 MacAddress indirectDhcpConnectMac = null;
995 VlanId indirectDhcpConnectVlan = null;
996 Ip6Address indirectDhcpGatewayIp = null;
997 Ip6Address indirectRelayAgentIpFromCfg = null;
998
999 if (!defaultServerInfoList.isEmpty()) {
1000 serverInfo = defaultServerInfoList.get(0);
1001 dhcpConnectMac = serverInfo.getDhcpConnectMac().orElse(null);
1002 dhcpGatewayIp = serverInfo.getDhcpGatewayIp6().orElse(null);
1003 dhcpServerIp = serverInfo.getDhcpServerIp6().orElse(null);
1004 dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1005 dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1006 }
1007
1008 if (!indirectServerInfoList.isEmpty()) {
1009 serverInfo = indirectServerInfoList.get(0);
1010 indirectDhcpConnectMac = serverInfo.getDhcpConnectMac().orElse(null);
1011 indirectDhcpGatewayIp = serverInfo.getDhcpGatewayIp6().orElse(null);
1012 indirectDhcpServerIp = serverInfo.getDhcpServerIp6().orElse(null);
1013 indirectDhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1014 indirectDhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1015 indirectRelayAgentIpFromCfg = serverInfo.getRelayAgentIp6(receivedFromDevice).orElse(null);
1016 }
1017
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001018 // get dhcp6 header.
1019 Ethernet etherReply = (Ethernet) receivedPacket.clone();
1020 IPv6 ipv6Packet = (IPv6) etherReply.getPayload();
1021 UDP udpPacket = (UDP) ipv6Packet.getPayload();
1022 DHCP6 dhcp6Relay = (DHCP6) udpPacket.getPayload();
1023
1024 Boolean directConnFlag = directlyConnected(dhcp6Relay);
Kalhee Kim2575d2c2017-09-26 20:21:53 +00001025 ConnectPoint inPort = context.inPacket().receivedFrom();
Yi Tsengc44dc2e2017-11-03 16:27:32 -07001026 if ((directConnFlag || (!directConnFlag && indirectDhcpServerIp == null))
1027 && !inPort.equals(dhcpServerConnectPoint)) {
Kalhee Kim2575d2c2017-09-26 20:21:53 +00001028 log.warn("Receiving port {} is not the same as server connect point {} for direct or indirect-null",
Yi Tsengc44dc2e2017-11-03 16:27:32 -07001029 inPort, dhcpServerConnectPoint);
Kalhee Kim2575d2c2017-09-26 20:21:53 +00001030 return null;
1031 }
1032
Yi Tsengc44dc2e2017-11-03 16:27:32 -07001033 if (!directConnFlag && indirectDhcpServerIp != null &&
1034 !inPort.equals(indirectDhcpServerConnectPoint)) {
Kalhee Kim2575d2c2017-09-26 20:21:53 +00001035 log.warn("Receiving port {} is not the same as server connect point {} for indirect",
Yi Tsengc44dc2e2017-11-03 16:27:32 -07001036 inPort, indirectDhcpServerConnectPoint);
Kalhee Kim2575d2c2017-09-26 20:21:53 +00001037 return null;
1038 }
1039
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001040
1041 Dhcp6InterfaceIdOption interfaceIdOption = dhcp6Relay.getOptions().stream()
1042 .filter(opt -> opt instanceof Dhcp6InterfaceIdOption)
1043 .map(opt -> (Dhcp6InterfaceIdOption) opt)
1044 .findFirst()
1045 .orElse(null);
1046
1047 if (interfaceIdOption == null) {
1048 log.warn("Interface Id option is not present, abort packet...");
1049 return null;
1050 }
1051
1052 MacAddress peerMac = interfaceIdOption.getMacAddress();
1053 String clientConnectionPointStr = new String(interfaceIdOption.getInPort());
1054
1055 ConnectPoint clientConnectionPoint = ConnectPoint.deviceConnectPoint(clientConnectionPointStr);
Kalhee Kimaa5172a2017-09-15 17:43:27 +00001056 VlanId vlanIdInUse = VlanId.vlanId(interfaceIdOption.getVlanId());
1057 Interface clientInterface = interfaceService.getInterfacesByPort(clientConnectionPoint)
1058 .stream()
1059 .filter(iface -> interfaceContainsVlan(iface, vlanIdInUse))
1060 .findFirst()
1061 .orElse(null);
1062 if (clientInterface == null) {
1063 log.warn("Cannot get client interface for from packet, abort... vlan {}", vlanIdInUse.toString());
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001064 return null;
1065 }
Kalhee Kimaa5172a2017-09-15 17:43:27 +00001066 MacAddress relayAgentMac = clientInterface.mac();
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001067 if (relayAgentMac == null) {
1068 log.warn("Can not get interface mac, abort packet..");
1069 return null;
1070 }
1071 etherReply.setSourceMACAddress(relayAgentMac);
1072
1073 // find destMac
1074 MacAddress clientMac = null;
Yi Tseng4b013202017-09-08 17:22:51 -07001075 Ip6Address peerAddress = Ip6Address.valueOf(dhcp6Relay.getPeerAddress());
1076 Set<Host> clients = hostService.getHostsByIp(peerAddress);
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001077 if (clients.isEmpty()) {
Yi Tseng30262a22017-12-18 17:50:55 -08001078 log.debug("There's no host found for this address {}",
Kalhee Kim683ba3b2017-10-18 18:30:23 +00001079 HexString.toHexString(dhcp6Relay.getPeerAddress(), ":"));
Yi Tseng30262a22017-12-18 17:50:55 -08001080 log.debug("Let's look up interfaceId {}", HexString.toHexString(peerMac.toBytes(), ":"));
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001081 clientMac = peerMac;
1082 } else {
1083 clientMac = clients.iterator().next().mac();
1084 if (clientMac == null) {
1085 log.warn("No client mac address found, abort packet...");
1086 return null;
1087 }
Yi Tseng30262a22017-12-18 17:50:55 -08001088 log.trace("Client mac address found from getHostByIp");
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001089 }
1090 etherReply.setDestinationMACAddress(clientMac);
1091
1092 // ip header
1093 ipv6Packet.setSourceAddress(dhcp6Relay.getLinkAddress());
1094 ipv6Packet.setDestinationAddress(dhcp6Relay.getPeerAddress());
1095 // udp header
1096 udpPacket.setSourcePort(UDP.DHCP_V6_SERVER_PORT);
1097 if (directConnFlag) {
1098 udpPacket.setDestinationPort(UDP.DHCP_V6_CLIENT_PORT);
1099 } else {
1100 udpPacket.setDestinationPort(UDP.DHCP_V6_SERVER_PORT);
1101 }
1102
1103 DHCP6 embeddedDhcp6 = dhcp6Relay.getOptions().stream()
Kalhee Kim683ba3b2017-10-18 18:30:23 +00001104 .filter(opt -> opt instanceof Dhcp6RelayOption)
1105 .map(BasePacket::getPayload)
1106 .map(pld -> (DHCP6) pld)
1107 .findFirst()
1108 .orElse(null);
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001109
1110
1111 // add host or route
Kalhee Kimaa5172a2017-09-15 17:43:27 +00001112 addHostOrRoute(directConnFlag, dhcp6Relay, embeddedDhcp6, clientMac, clientInterface);
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001113
1114 udpPacket.setPayload(embeddedDhcp6);
1115 udpPacket.resetChecksum();
1116 ipv6Packet.setPayload(udpPacket);
1117 etherReply.setPayload(ipv6Packet);
1118
1119 return new InternalPacket(etherReply, clientConnectionPoint);
1120 }
1121
Yi Tseng2fe8f3f2017-09-07 16:22:51 -07001122 // Returns the first v6 interface ip out of a set of interfaces or null.
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001123 // Checks all interfaces, and ignores v6 interface ips
1124 private Ip6Address getRelayAgentIPv6Address(Set<Interface> intfs) {
1125 for (Interface intf : intfs) {
1126 for (InterfaceIpAddress ip : intf.ipAddressesList()) {
1127 Ip6Address relayAgentIp = ip.ipAddress().getIp6Address();
1128 if (relayAgentIp != null) {
1129 return relayAgentIp;
1130 }
1131 }
1132 }
1133 return null;
Yi Tseng51301292017-07-28 13:02:59 -07001134 }
Yi Tsenge72fbb52017-08-02 15:03:31 -07001135
1136 @Override
1137 public void setDefaultDhcpServerConfigs(Collection<DhcpServerConfig> configs) {
Yi Tseng2fe8f3f2017-09-07 16:22:51 -07001138 setDhcpServerConfigs(configs, defaultServerInfoList);
Yi Tseng2fe8f3f2017-09-07 16:22:51 -07001139 }
1140
1141 @Override
1142 public void setIndirectDhcpServerConfigs(Collection<DhcpServerConfig> configs) {
1143 setDhcpServerConfigs(configs, indirectServerInfoList);
Yi Tseng2fe8f3f2017-09-07 16:22:51 -07001144 }
1145
1146 public void setDhcpServerConfigs(Collection<DhcpServerConfig> configs, List<DhcpServerInfo> serverInfoList) {
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001147 if (configs.size() == 0) {
1148 // no config to update
1149 return;
1150 }
1151
1152 // TODO: currently we pick up first DHCP server config.
1153 // Will use other server configs in the future for HA.
1154 DhcpServerConfig serverConfig = configs.iterator().next();
Yi Tseng2fe8f3f2017-09-07 16:22:51 -07001155
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001156 if (!serverConfig.getDhcpServerIp6().isPresent()) {
Yi Tseng2fe8f3f2017-09-07 16:22:51 -07001157 // not a DHCPv6 config
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001158 return;
1159 }
1160
Yi Tseng2fe8f3f2017-09-07 16:22:51 -07001161 if (!serverInfoList.isEmpty()) {
1162 // remove old server info
1163 DhcpServerInfo oldServerInfo = serverInfoList.remove(0);
1164
1165 // stop monitoring gateway or server
1166 oldServerInfo.getDhcpGatewayIp6().ifPresent(gatewayIp -> {
1167 hostService.stopMonitoringIp(gatewayIp);
1168 });
1169 oldServerInfo.getDhcpServerIp6().ifPresent(serverIp -> {
1170 hostService.stopMonitoringIp(serverIp);
Yi Tseng7da339e2017-10-23 19:39:39 -07001171 cancelDhcpPacket(serverIp);
Yi Tseng2fe8f3f2017-09-07 16:22:51 -07001172 });
1173 }
1174
1175 // Create new server info according to the config
1176 DhcpServerInfo newServerInfo = new DhcpServerInfo(serverConfig,
1177 DhcpServerInfo.Version.DHCP_V6);
1178 checkState(newServerInfo.getDhcpServerConnectPoint().isPresent(),
1179 "Connect point not exists");
1180 checkState(newServerInfo.getDhcpServerIp6().isPresent(),
1181 "IP of DHCP server not exists");
1182
1183 log.debug("DHCP server connect point: {}", newServerInfo.getDhcpServerConnectPoint().orElse(null));
1184 log.debug("DHCP server IP: {}", newServerInfo.getDhcpServerIp6().orElse(null));
1185
Yi Tseng7da339e2017-10-23 19:39:39 -07001186 Ip6Address serverIp = newServerInfo.getDhcpServerIp6().get();
1187 Ip6Address ipToProbe;
Yi Tseng2fe8f3f2017-09-07 16:22:51 -07001188 if (newServerInfo.getDhcpGatewayIp6().isPresent()) {
1189 ipToProbe = newServerInfo.getDhcpGatewayIp6().get();
1190 } else {
1191 ipToProbe = newServerInfo.getDhcpServerIp6().orElse(null);
1192 }
1193 String hostToProbe = newServerInfo.getDhcpGatewayIp6()
1194 .map(ip -> "gateway").orElse("server");
1195
1196 log.debug("Probing to resolve {} IP {}", hostToProbe, ipToProbe);
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001197 hostService.startMonitoringIp(ipToProbe);
1198
1199 Set<Host> hosts = hostService.getHostsByIp(ipToProbe);
1200 if (!hosts.isEmpty()) {
1201 Host host = hosts.iterator().next();
Yi Tseng2fe8f3f2017-09-07 16:22:51 -07001202 newServerInfo.setDhcpConnectVlan(host.vlan());
1203 newServerInfo.setDhcpConnectMac(host.mac());
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001204 }
Yi Tseng2fe8f3f2017-09-07 16:22:51 -07001205 // Add new server info
Yi Tseng053682b2017-11-09 13:54:12 -08001206 synchronized (this) {
1207 serverInfoList.clear();
1208 serverInfoList.add(0, newServerInfo);
1209 }
Yi Tseng7da339e2017-10-23 19:39:39 -07001210 requestDhcpPacket(serverIp);
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001211 }
1212
1213 class InternalHostListener implements HostListener {
1214 @Override
1215 public void event(HostEvent event) {
1216 switch (event.type()) {
1217 case HOST_ADDED:
1218 case HOST_UPDATED:
1219 hostUpdated(event.subject());
1220 break;
1221 case HOST_REMOVED:
1222 hostRemoved(event.subject());
1223 break;
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001224 default:
1225 break;
1226 }
1227 }
1228 }
1229
1230 /**
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001231 * Handle host updated.
1232 * If the host is DHCP server or gateway, update connect mac and vlan.
1233 *
1234 * @param host the host
1235 */
1236 private void hostUpdated(Host host) {
Yi Tseng7da339e2017-10-23 19:39:39 -07001237 hostUpdated(host, defaultServerInfoList);
1238 hostUpdated(host, indirectServerInfoList);
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001239 }
1240
Yi Tseng7da339e2017-10-23 19:39:39 -07001241 private void hostUpdated(Host host, List<DhcpServerInfo> serverInfoList) {
1242 DhcpServerInfo serverInfo;
1243 Ip6Address targetIp;
1244 if (!serverInfoList.isEmpty()) {
1245 serverInfo = serverInfoList.get(0);
1246 Ip6Address serverIp = serverInfo.getDhcpServerIp6().orElse(null);
1247 targetIp = serverInfo.getDhcpGatewayIp6().orElse(null);
1248
1249 if (targetIp == null) {
1250 targetIp = serverIp;
1251 }
1252
1253 if (targetIp != null) {
1254 if (host.ipAddresses().contains(targetIp)) {
1255 serverInfo.setDhcpConnectMac(host.mac());
1256 serverInfo.setDhcpConnectVlan(host.vlan());
1257 requestDhcpPacket(serverIp);
1258 }
1259 }
1260 }
1261 }
1262
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001263 /**
1264 * Handle host removed.
1265 * If the host is DHCP server or gateway, unset connect mac and vlan.
1266 *
1267 * @param host the host
1268 */
1269 private void hostRemoved(Host host) {
Yi Tseng7da339e2017-10-23 19:39:39 -07001270 hostRemoved(host, defaultServerInfoList);
1271 hostRemoved(host, indirectServerInfoList);
Yi Tseng2fe8f3f2017-09-07 16:22:51 -07001272 }
1273
Yi Tseng7da339e2017-10-23 19:39:39 -07001274 private void hostRemoved(Host host, List<DhcpServerInfo> serverInfoList) {
1275 DhcpServerInfo serverInfo;
1276 Ip6Address targetIp;
1277
1278 if (!serverInfoList.isEmpty()) {
1279 serverInfo = serverInfoList.get(0);
1280 Ip6Address serverIp = serverInfo.getDhcpServerIp6().orElse(null);
1281 targetIp = serverInfo.getDhcpGatewayIp6().orElse(null);
1282
1283 if (targetIp == null) {
1284 targetIp = serverIp;
1285 }
1286
1287 if (targetIp != null) {
1288 if (host.ipAddresses().contains(targetIp)) {
1289 serverInfo.setDhcpConnectVlan(null);
1290 serverInfo.setDhcpConnectMac(null);
1291 cancelDhcpPacket(serverIp);
1292 }
1293 }
1294 }
1295 }
1296
Kalhee Kim683ba3b2017-10-18 18:30:23 +00001297 /**
1298 * Returns the first interface ip from interface.
1299 *
1300 * @param iface interface of one connect point
1301 * @return the first interface IP; null if not exists an IP address in
1302 * these interfaces
1303 */
1304 private Ip6Address getFirstIpFromInterface(Interface iface) {
1305 checkNotNull(iface, "Interface can't be null");
1306 return iface.ipAddressesList().stream()
1307 .map(InterfaceIpAddress::ipAddress)
1308 .filter(IpAddress::isIp6)
1309 .map(IpAddress::getIp6Address)
1310 .findFirst()
1311 .orElse(null);
1312 }
1313
1314 /**
1315 * Gets Interface facing to the server for default host.
1316 *
1317 * @return the Interface facing to the server; null if not found
1318 */
1319 private Interface getServerInterface() {
Yi Tsengc44dc2e2017-11-03 16:27:32 -07001320 DhcpServerInfo serverInfo;
1321 ConnectPoint dhcpServerConnectPoint;
1322 VlanId dhcpConnectVlan;
1323
1324 if (!defaultServerInfoList.isEmpty()) {
1325 serverInfo = defaultServerInfoList.get(0);
1326 dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1327 dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1328 } else {
Kalhee Kim683ba3b2017-10-18 18:30:23 +00001329 return null;
1330 }
Yi Tseng8f49a732017-11-17 17:14:41 -08001331 if (dhcpServerConnectPoint == null || dhcpConnectVlan == null) {
1332 log.info("Default DHCP server {} not resolve yet", serverInfo.getDhcpGatewayIp6());
1333 return null;
1334 }
Kalhee Kim683ba3b2017-10-18 18:30:23 +00001335 return interfaceService.getInterfacesByPort(dhcpServerConnectPoint)
1336 .stream()
1337 .filter(iface -> interfaceContainsVlan(iface, dhcpConnectVlan))
1338 .findFirst()
1339 .orElse(null);
1340 }
1341
1342 /**
1343 * Gets Interface facing to the server for indirect hosts.
1344 * Use default server Interface if indirect server not configured.
1345 *
1346 * @return the Interface facing to the server; null if not found
1347 */
1348 private Interface getIndirectServerInterface() {
Yi Tsengc44dc2e2017-11-03 16:27:32 -07001349 DhcpServerInfo serverInfo;
1350
1351 ConnectPoint indirectDhcpServerConnectPoint;
1352 VlanId indirectDhcpConnectVlan;
1353
1354 if (!indirectServerInfoList.isEmpty()) {
1355 serverInfo = indirectServerInfoList.get(0);
1356 indirectDhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1357 indirectDhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1358 } else {
Kalhee Kim683ba3b2017-10-18 18:30:23 +00001359 return getServerInterface();
1360 }
Yi Tseng8f49a732017-11-17 17:14:41 -08001361 if (indirectDhcpServerConnectPoint == null || indirectDhcpConnectVlan == null) {
1362 log.info("Indirect DHCP server {} not resolve yet", serverInfo.getDhcpGatewayIp6());
1363 return null;
1364 }
Kalhee Kim683ba3b2017-10-18 18:30:23 +00001365 return interfaceService.getInterfacesByPort(indirectDhcpServerConnectPoint)
1366 .stream()
1367 .filter(iface -> interfaceContainsVlan(iface, indirectDhcpConnectVlan))
1368 .findFirst()
1369 .orElse(null);
1370 }
1371
Kalhee Kimaa5172a2017-09-15 17:43:27 +00001372 /**
1373 * Determind if an Interface contains a vlan id.
1374 *
1375 * @param iface the Interface
1376 * @param vlanId the vlan id
1377 * @return true if the Interface contains the vlan id
1378 */
1379 private boolean interfaceContainsVlan(Interface iface, VlanId vlanId) {
1380 if (vlanId.equals(VlanId.NONE)) {
1381 // untagged packet, check if vlan untagged or vlan native is not NONE
1382 return !iface.vlanUntagged().equals(VlanId.NONE) ||
1383 !iface.vlanNative().equals(VlanId.NONE);
1384 }
1385 // tagged packet, check if the interface contains the vlan
1386 return iface.vlanTagged().contains(vlanId);
1387 }
1388
Yi Tseng7da339e2017-10-23 19:39:39 -07001389 private void requestDhcpPacket(Ip6Address serverIp) {
1390 requestServerDhcpPacket(serverIp);
1391 requestClientDhcpPacket(serverIp);
1392 }
1393
1394 private void cancelDhcpPacket(Ip6Address serverIp) {
1395 cancelServerDhcpPacket(serverIp);
1396 cancelClientDhcpPacket(serverIp);
1397 }
1398
1399 private void cancelServerDhcpPacket(Ip6Address serverIp) {
1400 TrafficSelector serverSelector =
1401 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1402 .matchIPv6Src(serverIp.toIpPrefix())
1403 .build();
1404 packetService.cancelPackets(serverSelector,
1405 PacketPriority.CONTROL,
1406 appId);
1407 }
1408
1409 private void requestServerDhcpPacket(Ip6Address serverIp) {
1410 TrafficSelector serverSelector =
1411 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1412 .matchIPv6Src(serverIp.toIpPrefix())
1413 .build();
1414 packetService.requestPackets(serverSelector,
1415 PacketPriority.CONTROL,
1416 appId);
1417 }
1418
1419 private void cancelClientDhcpPacket(Ip6Address serverIp) {
1420 // Packet comes from relay
1421 TrafficSelector indirectClientSelector =
1422 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1423 .matchIPv6Dst(serverIp.toIpPrefix())
1424 .build();
1425 packetService.cancelPackets(indirectClientSelector,
1426 PacketPriority.CONTROL,
1427 appId);
Yi Tseng3f2119b2017-11-02 15:21:10 -07001428 indirectClientSelector =
1429 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1430 .matchIPv6Dst(Ip6Address.ALL_DHCP_RELAY_AGENTS_AND_SERVERS.toIpPrefix())
1431 .build();
1432 packetService.cancelPackets(indirectClientSelector,
1433 PacketPriority.CONTROL,
1434 appId);
1435 indirectClientSelector =
1436 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1437 .matchIPv6Dst(Ip6Address.ALL_DHCP_SERVERS.toIpPrefix())
1438 .build();
1439 packetService.cancelPackets(indirectClientSelector,
1440 PacketPriority.CONTROL,
1441 appId);
Yi Tseng7da339e2017-10-23 19:39:39 -07001442
1443 // Packet comes from client
1444 packetService.cancelPackets(CLIENT_SERVER_SELECTOR,
1445 PacketPriority.CONTROL,
1446 appId);
1447 }
1448
1449 private void requestClientDhcpPacket(Ip6Address serverIp) {
1450 // Packet comes from relay
1451 TrafficSelector indirectClientSelector =
1452 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1453 .matchIPv6Dst(serverIp.toIpPrefix())
1454 .build();
1455 packetService.requestPackets(indirectClientSelector,
1456 PacketPriority.CONTROL,
1457 appId);
Yi Tseng3f2119b2017-11-02 15:21:10 -07001458 indirectClientSelector =
1459 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1460 .matchIPv6Dst(Ip6Address.ALL_DHCP_RELAY_AGENTS_AND_SERVERS.toIpPrefix())
1461 .build();
1462 packetService.requestPackets(indirectClientSelector,
1463 PacketPriority.CONTROL,
1464 appId);
1465 indirectClientSelector =
1466 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1467 .matchIPv6Dst(Ip6Address.ALL_DHCP_SERVERS.toIpPrefix())
1468 .build();
1469 packetService.requestPackets(indirectClientSelector,
1470 PacketPriority.CONTROL,
1471 appId);
Yi Tseng7da339e2017-10-23 19:39:39 -07001472
1473 // Packet comes from client
1474 packetService.requestPackets(CLIENT_SERVER_SELECTOR,
1475 PacketPriority.CONTROL,
1476 appId);
1477 }
1478
1479 /**
1480 * Process the ignore rules.
1481 *
1482 * @param deviceId the device id
1483 * @param vlanId the vlan to be ignored
1484 * @param op the operation, ADD to install; REMOVE to uninstall rules
1485 */
1486 private void processIgnoreVlanRule(DeviceId deviceId, VlanId vlanId, Objective.Operation op) {
Yi Tseng7da339e2017-10-23 19:39:39 -07001487 AtomicInteger installedCount = new AtomicInteger(DHCP_SELECTORS.size());
1488 DHCP_SELECTORS.forEach(trafficSelector -> {
1489 TrafficSelector selector = DefaultTrafficSelector.builder(trafficSelector)
1490 .matchVlanId(vlanId)
1491 .build();
1492
1493 ForwardingObjective.Builder builder = DefaultForwardingObjective.builder()
1494 .withFlag(ForwardingObjective.Flag.VERSATILE)
1495 .withSelector(selector)
1496 .withPriority(IGNORE_CONTROL_PRIORITY)
Yi Tsengf29ffd72017-11-29 10:49:20 -08001497 .withTreatment(DefaultTrafficTreatment.emptyTreatment())
Yi Tseng7da339e2017-10-23 19:39:39 -07001498 .fromApp(appId);
1499
1500
1501 ObjectiveContext objectiveContext = new ObjectiveContext() {
1502 @Override
1503 public void onSuccess(Objective objective) {
1504 log.info("Ignore rule {} (Vlan id {}, device {}, selector {})",
1505 op, vlanId, deviceId, selector);
1506 int countDown = installedCount.decrementAndGet();
1507 if (countDown != 0) {
1508 return;
1509 }
1510 switch (op) {
1511 case ADD:
1512 ignoredVlans.put(deviceId, vlanId);
1513 break;
1514 case REMOVE:
1515 ignoredVlans.remove(deviceId, vlanId);
1516 break;
1517 default:
1518 log.warn("Unsupported objective operation {}", op);
1519 break;
1520 }
1521 }
1522
1523 @Override
1524 public void onError(Objective objective, ObjectiveError error) {
1525 log.warn("Can't {} ignore rule (vlan id {}, selector {}, device {}) due to {}",
1526 op, vlanId, selector, deviceId, error);
1527 }
1528 };
1529
1530 ForwardingObjective fwd;
1531 switch (op) {
1532 case ADD:
1533 fwd = builder.add(objectiveContext);
1534 break;
1535 case REMOVE:
1536 fwd = builder.remove(objectiveContext);
1537 break;
1538 default:
1539 log.warn("Unsupported objective operation {}", op);
1540 return;
1541 }
1542
1543 Device device = deviceService.getDevice(deviceId);
1544 if (device == null || !device.is(Pipeliner.class)) {
1545 log.warn("Device {} is not available now, wait until device is available", deviceId);
1546 return;
1547 }
1548 flowObjectiveService.apply(deviceId, fwd);
1549 });
1550 }
Kalhee Kimc60c7e22017-11-01 17:56:44 +00001551
1552 /**
1553 * Find first ipaddress for a given Host info i.e. mac and vlan.
1554 *
1555 * @param clientMac client mac
1556 * @param vlanId packet's vlan
1557 * @return next-hop link-local ipaddress for a given host
1558 */
1559 private IpAddress getFirstIpByHost(MacAddress clientMac, VlanId vlanId) {
1560 IpAddress nextHopIp;
1561 // pick out the first link-local ip address
1562 HostId gwHostId = HostId.hostId(clientMac, vlanId);
1563 Host gwHost = hostService.getHost(gwHostId);
1564 if (gwHost == null) {
1565 log.warn("Can't find gateway host for hostId {}", gwHostId);
1566 return null;
1567 }
1568 nextHopIp = gwHost.ipAddresses()
1569 .stream()
1570 .filter(IpAddress::isIp6)
Yi Tseng30262a22017-12-18 17:50:55 -08001571 .filter(IpAddress::isLinkLocal)
Kalhee Kimc60c7e22017-11-01 17:56:44 +00001572 .map(IpAddress::getIp6Address)
1573 .findFirst()
1574 .orElse(null);
1575 return nextHopIp;
1576 }
Yi Tseng51301292017-07-28 13:02:59 -07001577}