blob: c4ae632d95d5d1aebd3eeaecfefc4784082c7aa5 [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;
Charles Chan1122cae2018-03-07 17:36:06 -080022import com.google.common.collect.Multimaps;
Kalhee Kim1b5094f2017-09-05 19:05:06 +000023import org.apache.felix.scr.annotations.Activate;
24import org.apache.felix.scr.annotations.Deactivate;
Kalhee Kim1b5094f2017-09-05 19:05:06 +000025import com.google.common.collect.Sets;
26import com.google.common.collect.ImmutableSet;
Yi Tseng51301292017-07-28 13:02:59 -070027import org.apache.felix.scr.annotations.Component;
28import org.apache.felix.scr.annotations.Property;
Kalhee Kim1b5094f2017-09-05 19:05:06 +000029import org.apache.felix.scr.annotations.Reference;
30import org.apache.felix.scr.annotations.ReferenceCardinality;
Yi Tseng51301292017-07-28 13:02:59 -070031import org.apache.felix.scr.annotations.Service;
32import org.onlab.packet.BasePacket;
Kalhee Kim1b5094f2017-09-05 19:05:06 +000033import org.onlab.packet.DHCP6;
34import org.onlab.packet.IPv6;
35import org.onlab.packet.Ethernet;
36import org.onlab.packet.Ip6Address;
Yi Tseng51301292017-07-28 13:02:59 -070037import org.onlab.packet.IpAddress;
Kalhee Kim1b5094f2017-09-05 19:05:06 +000038import org.onlab.packet.IpPrefix;
Yi Tseng51301292017-07-28 13:02:59 -070039import org.onlab.packet.MacAddress;
Yi Tseng7da339e2017-10-23 19:39:39 -070040import org.onlab.packet.TpPort;
Kalhee Kim1b5094f2017-09-05 19:05:06 +000041import org.onlab.packet.UDP;
Yi Tseng51301292017-07-28 13:02:59 -070042import org.onlab.packet.VlanId;
Kalhee Kim1b5094f2017-09-05 19:05:06 +000043import org.onlab.packet.dhcp.Dhcp6RelayOption;
44import org.onlab.packet.dhcp.Dhcp6InterfaceIdOption;
45import org.onlab.packet.dhcp.Dhcp6Option;
46import org.onlab.packet.dhcp.Dhcp6IaNaOption;
47import org.onlab.packet.dhcp.Dhcp6IaTaOption;
48import org.onlab.packet.dhcp.Dhcp6IaPdOption;
49import org.onlab.packet.dhcp.Dhcp6IaAddressOption;
50import org.onlab.packet.dhcp.Dhcp6IaPrefixOption;
51import org.onlab.util.HexString;
Yi Tseng7da339e2017-10-23 19:39:39 -070052import org.onosproject.core.ApplicationId;
53import org.onosproject.core.CoreService;
Yi Tseng51301292017-07-28 13:02:59 -070054import org.onosproject.dhcprelay.api.DhcpHandler;
Yi Tseng2fe8f3f2017-09-07 16:22:51 -070055import org.onosproject.dhcprelay.api.DhcpServerInfo;
Yi Tseng7da339e2017-10-23 19:39:39 -070056import org.onosproject.dhcprelay.config.IgnoreDhcpConfig;
Kalhee Kim1b5094f2017-09-05 19:05:06 +000057import org.onosproject.dhcprelay.store.DhcpRelayStore;
Yi Tseng7da339e2017-10-23 19:39:39 -070058import org.onosproject.net.Device;
59import org.onosproject.net.DeviceId;
60import org.onosproject.net.behaviour.Pipeliner;
61import org.onosproject.net.device.DeviceService;
62import org.onosproject.net.flow.DefaultTrafficSelector;
63import org.onosproject.net.flow.TrafficSelector;
64import org.onosproject.net.flowobjective.DefaultForwardingObjective;
65import org.onosproject.net.flowobjective.FlowObjectiveService;
66import org.onosproject.net.flowobjective.ForwardingObjective;
67import org.onosproject.net.flowobjective.Objective;
68import org.onosproject.net.flowobjective.ObjectiveContext;
69import org.onosproject.net.flowobjective.ObjectiveError;
Yi Tseng4b013202017-09-08 17:22:51 -070070import org.onosproject.net.host.HostProvider;
71import org.onosproject.net.host.HostProviderRegistry;
72import org.onosproject.net.host.HostProviderService;
Kalhee Kim1b5094f2017-09-05 19:05:06 +000073import org.onosproject.net.host.HostService;
74import org.onosproject.net.host.DefaultHostDescription;
75import org.onosproject.net.host.HostDescription;
76import org.onosproject.net.host.InterfaceIpAddress;
77import org.onosproject.net.host.HostListener;
78import org.onosproject.net.host.HostEvent;
79import org.onosproject.net.intf.Interface;
80import org.onosproject.net.intf.InterfaceService;
Yi Tseng7da339e2017-10-23 19:39:39 -070081import org.onosproject.net.packet.PacketPriority;
Yi Tseng4b013202017-09-08 17:22:51 -070082import org.onosproject.net.provider.ProviderId;
Kalhee Kim1b5094f2017-09-05 19:05:06 +000083import org.onosproject.routeservice.Route;
84import org.onosproject.routeservice.RouteStore;
Yi Tsenge72fbb52017-08-02 15:03:31 -070085import org.onosproject.dhcprelay.config.DhcpServerConfig;
Yi Tseng51301292017-07-28 13:02:59 -070086import org.onosproject.net.ConnectPoint;
Kalhee Kim1b5094f2017-09-05 19:05:06 +000087import org.onosproject.net.Host;
88import org.onosproject.net.HostId;
89import org.onosproject.net.HostLocation;
90import org.onosproject.net.packet.DefaultOutboundPacket;
91import org.onosproject.net.packet.OutboundPacket;
Yi Tseng51301292017-07-28 13:02:59 -070092import org.onosproject.net.packet.PacketContext;
Kalhee Kim1b5094f2017-09-05 19:05:06 +000093import org.onosproject.net.packet.PacketService;
94import org.slf4j.Logger;
95import org.slf4j.LoggerFactory;
96import org.onosproject.net.flow.DefaultTrafficTreatment;
97import org.onosproject.net.flow.TrafficTreatment;
Yi Tseng51301292017-07-28 13:02:59 -070098
Kalhee Kim1b5094f2017-09-05 19:05:06 +000099
100import java.nio.ByteBuffer;
101import java.util.List;
Yi Tsenge72fbb52017-08-02 15:03:31 -0700102import java.util.Collection;
Yi Tseng51301292017-07-28 13:02:59 -0700103import java.util.Optional;
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000104import java.util.Set;
105import java.util.ArrayList;
Charles Chana18afb82018-03-05 13:14:02 -0800106import java.util.concurrent.CopyOnWriteArrayList;
Yi Tseng7da339e2017-10-23 19:39:39 -0700107import java.util.concurrent.atomic.AtomicInteger;
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000108
109
110import static com.google.common.base.Preconditions.checkNotNull;
111import static com.google.common.base.Preconditions.checkState;
Yi Tseng7da339e2017-10-23 19:39:39 -0700112import static org.onosproject.net.flowobjective.Objective.Operation.ADD;
113import static org.onosproject.net.flowobjective.Objective.Operation.REMOVE;
Yi Tseng51301292017-07-28 13:02:59 -0700114
115@Component
116@Service
117@Property(name = "version", value = "6")
Yi Tseng4b013202017-09-08 17:22:51 -0700118public class Dhcp6HandlerImpl implements DhcpHandler, HostProvider {
Charles Chan75edab72017-09-12 17:09:32 -0700119 public static final String DHCP_V6_RELAY_APP = "org.onosproject.Dhcp6HandlerImpl";
Saurav Das37415cc2017-09-13 14:35:56 -0700120 public static final ProviderId PROVIDER_ID = new ProviderId("dhcp6", DHCP_V6_RELAY_APP);
Yi Tseng7da339e2017-10-23 19:39:39 -0700121 private static final int IGNORE_CONTROL_PRIORITY = PacketPriority.CONTROL.priorityValue() + 1000;
122
123 private static final TrafficSelector CLIENT_SERVER_SELECTOR = DefaultTrafficSelector.builder()
124 .matchEthType(Ethernet.TYPE_IPV6)
125 .matchIPProtocol(IPv6.PROTOCOL_UDP)
126 .matchIPv6Src(IpPrefix.IPV6_LINK_LOCAL_PREFIX)
127 .matchIPv6Dst(Ip6Address.ALL_DHCP_RELAY_AGENTS_AND_SERVERS.toIpPrefix())
128 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_V6_CLIENT_PORT))
129 .matchUdpDst(TpPort.tpPort(UDP.DHCP_V6_SERVER_PORT))
130 .build();
131 private static final TrafficSelector SERVER_RELAY_SELECTOR = DefaultTrafficSelector.builder()
132 .matchEthType(Ethernet.TYPE_IPV6)
133 .matchIPProtocol(IPv6.PROTOCOL_UDP)
134 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_V6_SERVER_PORT))
135 .matchUdpDst(TpPort.tpPort(UDP.DHCP_V6_SERVER_PORT))
136 .build();
137 static final Set<TrafficSelector> DHCP_SELECTORS = ImmutableSet.of(
138 CLIENT_SERVER_SELECTOR,
139 SERVER_RELAY_SELECTOR
140 );
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000141 private static Logger log = LoggerFactory.getLogger(Dhcp6HandlerImpl.class);
Yi Tseng51301292017-07-28 13:02:59 -0700142
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000143 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
144 protected DhcpRelayStore dhcpRelayStore;
Yi Tseng51301292017-07-28 13:02:59 -0700145
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000146 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
147 protected PacketService packetService;
148
149 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000150 protected RouteStore routeStore;
151
152 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
153 protected InterfaceService interfaceService;
154
155 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
156 protected HostService hostService;
157
Yi Tseng4b013202017-09-08 17:22:51 -0700158 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
159 protected HostProviderRegistry providerRegistry;
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000160
Yi Tseng7da339e2017-10-23 19:39:39 -0700161 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
162 protected CoreService coreService;
163
164 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
165 protected DeviceService deviceService;
166
167 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
168 protected FlowObjectiveService flowObjectiveService;
169
Yi Tseng4b013202017-09-08 17:22:51 -0700170 protected HostProviderService providerService;
Yi Tseng7da339e2017-10-23 19:39:39 -0700171 protected ApplicationId appId;
Charles Chan1122cae2018-03-07 17:36:06 -0800172 protected Multimap<DeviceId, VlanId> ignoredVlans = Multimaps.synchronizedMultimap(HashMultimap.create());
Yi Tseng7da339e2017-10-23 19:39:39 -0700173 private InternalHostListener hostListener = new InternalHostListener();
174
Charles Chana18afb82018-03-05 13:14:02 -0800175 private List<DhcpServerInfo> defaultServerInfoList = new CopyOnWriteArrayList<>();
176 private List<DhcpServerInfo> indirectServerInfoList = new CopyOnWriteArrayList<>();
Yi Tseng2fe8f3f2017-09-07 16:22:51 -0700177
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000178
179 // CLIENT message types
180 public static final Set<Byte> MSG_TYPE_FROM_CLIENT =
181 ImmutableSet.of(DHCP6.MsgType.SOLICIT.value(),
182 DHCP6.MsgType.REQUEST.value(),
183 DHCP6.MsgType.REBIND.value(),
184 DHCP6.MsgType.RENEW.value(),
185 DHCP6.MsgType.RELEASE.value(),
186 DHCP6.MsgType.DECLINE.value(),
187 DHCP6.MsgType.CONFIRM.value(),
188 DHCP6.MsgType.RELAY_FORW.value());
189 // SERVER message types
190 public static final Set<Byte> MSG_TYPE_FROM_SERVER =
191 ImmutableSet.of(DHCP6.MsgType.RELAY_REPL.value());
192
193 @Activate
194 protected void activate() {
Yi Tseng7da339e2017-10-23 19:39:39 -0700195 appId = coreService.registerApplication(DHCP_V6_RELAY_APP);
Yi Tseng4b013202017-09-08 17:22:51 -0700196 providerService = providerRegistry.register(this);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000197 hostService.addListener(hostListener);
Yi Tseng51301292017-07-28 13:02:59 -0700198 }
199
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000200 @Deactivate
201 protected void deactivate() {
Yi Tseng4b013202017-09-08 17:22:51 -0700202 providerRegistry.unregister(this);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000203 hostService.removeListener(hostListener);
Yi Tseng2fe8f3f2017-09-07 16:22:51 -0700204 defaultServerInfoList.forEach(this::stopMonitoringIps);
205 defaultServerInfoList.clear();
206 indirectServerInfoList.forEach(this::stopMonitoringIps);
207 indirectServerInfoList.clear();
208 }
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000209
Yi Tseng2fe8f3f2017-09-07 16:22:51 -0700210 private void stopMonitoringIps(DhcpServerInfo serverInfo) {
211 serverInfo.getDhcpGatewayIp6().ifPresent(gatewayIp -> {
212 hostService.stopMonitoringIp(gatewayIp);
213 });
214 serverInfo.getDhcpServerIp6().ifPresent(serverIp -> {
215 hostService.stopMonitoringIp(serverIp);
216 });
Yi Tseng51301292017-07-28 13:02:59 -0700217 }
218
Yi Tseng51301292017-07-28 13:02:59 -0700219 @Override
Yi Tseng2fe8f3f2017-09-07 16:22:51 -0700220 public List<DhcpServerInfo> getDefaultDhcpServerInfoList() {
221 return defaultServerInfoList;
Yi Tseng51301292017-07-28 13:02:59 -0700222 }
223
224 @Override
Yi Tseng2fe8f3f2017-09-07 16:22:51 -0700225 public List<DhcpServerInfo> getIndirectDhcpServerInfoList() {
226 return indirectServerInfoList;
Yi Tseng51301292017-07-28 13:02:59 -0700227 }
228
229 @Override
Yi Tseng7da339e2017-10-23 19:39:39 -0700230 public void updateIgnoreVlanConfig(IgnoreDhcpConfig config) {
231 if (config == null) {
232 ignoredVlans.forEach(((deviceId, vlanId) -> {
233 processIgnoreVlanRule(deviceId, vlanId, REMOVE);
234 }));
235 return;
236 }
237 config.ignoredVlans().forEach((deviceId, vlanId) -> {
238 if (ignoredVlans.get(deviceId).contains(vlanId)) {
239 // don't need to process if it already ignored
240 return;
241 }
242 processIgnoreVlanRule(deviceId, vlanId, ADD);
243 });
244
245 ignoredVlans.forEach((deviceId, vlanId) -> {
246 if (!config.ignoredVlans().get(deviceId).contains(vlanId)) {
247 // not contains in new config, remove it
248 processIgnoreVlanRule(deviceId, vlanId, REMOVE);
249 }
250 });
251 }
252
253 @Override
Saurav Dasba7eb1a2017-12-13 16:19:35 -0800254 public void removeIgnoreVlanState(IgnoreDhcpConfig config) {
255 if (config == null) {
256 ignoredVlans.clear();
257 return;
258 }
259 config.ignoredVlans().forEach((deviceId, vlanId) -> {
260 ignoredVlans.remove(deviceId, vlanId);
261 });
262 }
263
264 @Override
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000265 public void processDhcpPacket(PacketContext context, BasePacket payload) {
266 checkNotNull(payload, "DHCP6 payload can't be null");
267 checkState(payload instanceof DHCP6, "Payload is not a DHCP6");
268 DHCP6 dhcp6Payload = (DHCP6) payload;
269 Ethernet receivedPacket = context.inPacket().parsed();
270
271 if (!configured()) {
272 log.warn("Missing DHCP6 relay server config. Abort packet processing");
Yi Tseng30262a22017-12-18 17:50:55 -0800273 log.trace("dhcp6 payload {}", dhcp6Payload);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000274 return;
275 }
276
277 byte msgType = dhcp6Payload.getMsgType();
Yi Tseng30262a22017-12-18 17:50:55 -0800278 log.trace("msgType is {}", msgType);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000279
280 ConnectPoint inPort = context.inPacket().receivedFrom();
281 if (inPort == null) {
Yi Tseng30262a22017-12-18 17:50:55 -0800282 log.trace("incommin ConnectPoint is null");
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000283 }
284 Set<Interface> receivingInterfaces = interfaceService.getInterfacesByPort(inPort);
285 //ignore the packets if dhcp client interface is not configured on onos.
286 if (receivingInterfaces.isEmpty()) {
287 log.warn("Virtual interface is not configured on {}", inPort);
288 return;
289 }
290
291
292 if (MSG_TYPE_FROM_CLIENT.contains(msgType)) {
293
294 InternalPacket ethernetClientPacket =
295 processDhcp6PacketFromClient(context, receivedPacket, receivingInterfaces);
296 if (ethernetClientPacket != null) {
297 forwardPacket(ethernetClientPacket);
298 }
299
300 } else if (MSG_TYPE_FROM_SERVER.contains(msgType)) {
Yi Tseng30262a22017-12-18 17:50:55 -0800301 log.trace("calling processDhcp6PacketFromServer with RELAY_REPL", msgType);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000302 InternalPacket ethernetPacketReply =
303 processDhcp6PacketFromServer(context, receivedPacket, receivingInterfaces);
304 if (ethernetPacketReply != null) {
305 forwardPacket(ethernetPacketReply);
306 }
307 } else {
Yi Tseng30262a22017-12-18 17:50:55 -0800308 log.warn("DHCP type {} not supported yet", msgType);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000309 }
310 }
311
312
313 /**
314 * Checks if this app has been configured.
315 *
316 * @return true if all information we need have been initialized
317 */
318 public boolean configured() {
Yi Tseng2fe8f3f2017-09-07 16:22:51 -0700319 return !defaultServerInfoList.isEmpty();
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000320 }
321
Yi Tseng4b013202017-09-08 17:22:51 -0700322 @Override
323 public ProviderId id() {
Charles Chan75edab72017-09-12 17:09:32 -0700324 return PROVIDER_ID;
Yi Tseng4b013202017-09-08 17:22:51 -0700325 }
326
327 @Override
328 public void triggerProbe(Host host) {
329 // Do nothing here
330 }
331
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000332 // the new class the contains Ethernet packet and destination port, kind of like adding
333 // internal header to the packet
334 private class InternalPacket {
335 Ethernet packet;
336 ConnectPoint destLocation;
337 public InternalPacket(Ethernet newPacket, ConnectPoint newLocation) {
338 packet = newPacket;
339 destLocation = newLocation;
340 }
341 void setLocation(ConnectPoint newLocation) {
342 destLocation = newLocation;
343 }
344 }
345
346 //forward the packet to ConnectPoint where the DHCP server is attached.
347 private void forwardPacket(InternalPacket packet) {
348 //send Packetout to dhcp server connectpoint.
349 if (packet.destLocation != null) {
350 TrafficTreatment t = DefaultTrafficTreatment.builder()
351 .setOutput(packet.destLocation.port()).build();
352 OutboundPacket o = new DefaultOutboundPacket(
353 packet.destLocation.deviceId(), t, ByteBuffer.wrap(packet.packet.serialize()));
354 if (log.isTraceEnabled()) {
355 log.trace("Relaying packet to destination {}", packet.destLocation);
356 }
357 packetService.emit(o);
358 } // if
359 }
360
361 /**
362 * Check if the host is directly connected to the network or not.
363 *
364 * @param dhcp6Payload the dhcp6 payload
365 * @return true if the host is directly connected to the network; false otherwise
366 */
367 private boolean directlyConnected(DHCP6 dhcp6Payload) {
368 log.debug("directlyConnected enters");
369
370 if (dhcp6Payload.getMsgType() != DHCP6.MsgType.RELAY_FORW.value() &&
371 dhcp6Payload.getMsgType() != DHCP6.MsgType.RELAY_REPL.value()) {
372 log.debug("directlyConnected true. MsgType {}", dhcp6Payload.getMsgType());
373
374 return true;
375 }
376
377 // Regardless of relay-forward or relay-replay, check if we see another relay message
378 DHCP6 dhcp6Payload2 = dhcp6PacketFromRelayPacket(dhcp6Payload);
379 if (dhcp6Payload2 != null) {
380 if (dhcp6Payload.getMsgType() == DHCP6.MsgType.RELAY_FORW.value()) {
381 log.debug("directlyConnected false. 1st realy-foward, 2nd MsgType {}", dhcp6Payload2.getMsgType());
382 return false;
383 } else {
384 // relay-reply
385 if (dhcp6Payload2.getMsgType() != DHCP6.MsgType.RELAY_REPL.value()) {
386 log.debug("directlyConnected true. 2nd MsgType {}", dhcp6Payload2.getMsgType());
387 return true; // must be directly connected
388 } else {
389 log.debug("directlyConnected false. 1st relay-reply, 2nd relay-reply MsgType {}",
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000390 dhcp6Payload2.getMsgType());
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000391 return false; // must be indirectly connected
392 }
393 }
394 } else {
Yi Tseng30262a22017-12-18 17:50:55 -0800395 log.trace("directlyConnected true.");
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000396 return true;
397 }
398 }
399
400 /**
401 * extract DHCP6 payload from dhcp6 relay message within relay-forwrd/reply.
402 *
403 * @param dhcp6 dhcp6 relay-reply or relay-foward
404 * @return dhcp6Packet dhcp6 packet extracted from relay-message
405 */
406 private DHCP6 dhcp6PacketFromRelayPacket(DHCP6 dhcp6) {
407 log.debug("dhcp6PacketFromRelayPacket enters. dhcp6 {}", dhcp6);
408
409 // extract the relay message if exist
410 DHCP6 dhcp6Payload = dhcp6.getOptions().stream()
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000411 .filter(opt -> opt instanceof Dhcp6RelayOption)
412 .map(BasePacket::getPayload)
413 .map(pld -> (DHCP6) pld)
414 .findFirst()
415 .orElse(null);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000416
417
418 if (dhcp6Payload == null) {
419 // Can't find dhcp payload
420 log.debug("Can't find dhcp6 payload from relay message");
421 } else {
422 log.debug("dhcp6 payload found from relay message {}", dhcp6Payload);
423 }
424
425 return dhcp6Payload;
426 }
427
428 /**
429 * find the leaf DHCP6 packet from multi-level relay packet.
430 *
431 * @param relayPacket dhcp6 relay packet
432 * @return leafPacket non-relay dhcp6 packet
433 */
434 private DHCP6 getDhcp6Leaf(DHCP6 relayPacket) {
435 DHCP6 dhcp6Parent = relayPacket;
436 DHCP6 dhcp6Child = null;
437
438 log.debug("getDhcp6Leaf entered.");
439 while (dhcp6Parent != null) {
440 dhcp6Child = dhcp6PacketFromRelayPacket(dhcp6Parent);
441
442 if (dhcp6Child != null) {
443 if (dhcp6Child.getMsgType() != DHCP6.MsgType.RELAY_FORW.value() &&
444 dhcp6Child.getMsgType() != DHCP6.MsgType.RELAY_REPL.value()) {
445 log.debug("leaf dhcp6 packet found.");
446 break;
447 } else {
448 // found another relay
449 // go for another loop
450 dhcp6Parent = dhcp6Child;
451 }
452 } else {
Yi Tseng30262a22017-12-18 17:50:55 -0800453 log.warn("Expected dhcp6 within relay pkt, but no dhcp6 leaf found.");
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000454 break;
455 }
456 }
457 return dhcp6Child;
458 }
459
460 /**
461 * check if DHCP6 relay-reply is reply.
462 *
463 * @param relayPacket dhcp6 relay-reply
464 * @return boolean relay-reply contains ack
465 */
466 private boolean isDhcp6Reply(DHCP6 relayPacket) {
467 log.debug("isDhcp6Reply entered.");
468
469 DHCP6 leafDhcp6 = getDhcp6Leaf(relayPacket);
470
471 if (leafDhcp6 != null) {
472 if (leafDhcp6.getMsgType() == DHCP6.MsgType.REPLY.value()) {
473 log.debug("isDhcp6Reply true.");
474 return true; // must be directly connected
475 } else {
476 log.debug("isDhcp6Reply false. leaf dhcp6 is not replay. MsgType {}", leafDhcp6.getMsgType());
477 }
478 } else {
479 log.debug("isDhcp6Reply false. Expected dhcp6 within relay pkt but not found.");
480 }
481 log.debug("isDhcp6Reply false.");
482 return false;
483 }
484
485 /**
486 * check if DHCP6 is release or relay-forward contains release.
487 *
488 * @param dhcp6Payload dhcp6 packet
489 * @return boolean dhcp6 contains release
490 */
491 private boolean isDhcp6Release(DHCP6 dhcp6Payload) {
492
493 log.debug("isDhcp6Release entered.");
494
495 if (dhcp6Payload.getMsgType() == DHCP6.MsgType.RELEASE.value()) {
496 log.debug("isDhcp6Release true.");
497 return true; // must be directly connected
498 } else {
499 DHCP6 dhcp6Leaf = getDhcp6Leaf(dhcp6Payload);
500 if (dhcp6Leaf != null) {
501 if (dhcp6Leaf.getMsgType() == DHCP6.MsgType.RELEASE.value()) {
502 log.debug("isDhcp6Release true. indirectlry connected");
503 return true;
504 } else {
505 log.debug("leaf dhcp6 is not release. MsgType {}", dhcp6Leaf.getMsgType());
506 return false;
507 }
508 } else {
509 log.debug("isDhcp6Release false. dhcp6 is niether relay nor release.");
510 return false;
511 }
512 }
513 }
514
515 /**
516 * extract from dhcp6 packet client ipv6 address of given by dhcp server.
517 *
518 * @param dhcp6 the dhcp6 packet
519 * @return Ip6Address Ip6Address given by dhcp server, or null if not exists
520 */
521 private Ip6Address extractIpAddress(DHCP6 dhcp6) {
522 Ip6Address ip = null;
523
524 log.debug("extractIpAddress enters dhcp6 {}.", dhcp6);
525 // Extract IPv6 address from IA NA ot IA TA option
526 Optional<Dhcp6IaNaOption> iaNaOption = dhcp6.getOptions()
527 .stream()
528 .filter(opt -> opt instanceof Dhcp6IaNaOption)
529 .map(opt -> (Dhcp6IaNaOption) opt)
530 .findFirst();
531 Optional<Dhcp6IaTaOption> iaTaOption = dhcp6.getOptions()
532 .stream()
533 .filter(opt -> opt instanceof Dhcp6IaTaOption)
534 .map(opt -> (Dhcp6IaTaOption) opt)
535 .findFirst();
536 Optional<Dhcp6IaAddressOption> iaAddressOption;
537 if (iaNaOption.isPresent()) {
538 log.debug("Found IPv6 address from iaNaOption {}", iaNaOption);
539
540 iaAddressOption = iaNaOption.get().getOptions().stream()
541 .filter(opt -> opt instanceof Dhcp6IaAddressOption)
542 .map(opt -> (Dhcp6IaAddressOption) opt)
543 .findFirst();
544 } else if (iaTaOption.isPresent()) {
545 log.debug("Found IPv6 address from iaTaOption {}", iaTaOption);
546
547 iaAddressOption = iaTaOption.get().getOptions().stream()
548 .filter(opt -> opt instanceof Dhcp6IaAddressOption)
549 .map(opt -> (Dhcp6IaAddressOption) opt)
550 .findFirst();
551 } else {
552 iaAddressOption = Optional.empty();
553 }
554 if (iaAddressOption.isPresent()) {
555 ip = iaAddressOption.get().getIp6Address();
556 log.debug("Found IPv6 address from iaAddressOption {}", iaAddressOption);
557
558
559 } else {
560 log.debug("Can't find IPv6 address from DHCPv6 {}", dhcp6);
561 }
562
563 return ip;
564 }
565 /**
566 * extract from dhcp6 packet Prefix prefix provided by dhcp server.
567 *
568 * @param dhcp6 the dhcp6 payload
569 * @return IpPrefix Prefix Delegation prefix, or null if not exists.
570 */
571 private IpPrefix extractPrefix(DHCP6 dhcp6) {
Yi Tseng30262a22017-12-18 17:50:55 -0800572 log.trace("extractPrefix enters {}", dhcp6);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000573
574 // extract prefix
575 IpPrefix prefixPrefix = null;
576
577 Ip6Address prefixAddress = null;
578
579 // Extract IPv6 prefix from IA PD option
580 Optional<Dhcp6IaPdOption> iaPdOption = dhcp6.getOptions()
581 .stream()
582 .filter(opt -> opt instanceof Dhcp6IaPdOption)
583 .map(opt -> (Dhcp6IaPdOption) opt)
584 .findFirst();
585
586 Optional<Dhcp6IaPrefixOption> iaPrefixOption;
587 if (iaPdOption.isPresent()) {
Yi Tseng30262a22017-12-18 17:50:55 -0800588 log.debug("IA_PD option found {}", iaPdOption);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000589
590 iaPrefixOption = iaPdOption.get().getOptions().stream()
591 .filter(opt -> opt instanceof Dhcp6IaPrefixOption)
592 .map(opt -> (Dhcp6IaPrefixOption) opt)
593 .findFirst();
594 } else {
Yi Tseng30262a22017-12-18 17:50:55 -0800595 log.debug("IA_PD option NOT found");
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000596
597 iaPrefixOption = Optional.empty();
598 }
599 if (iaPrefixOption.isPresent()) {
Yi Tseng30262a22017-12-18 17:50:55 -0800600 log.trace("IAPrefix Option within IA_PD option found {}", iaPrefixOption);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000601
602 prefixAddress = iaPrefixOption.get().getIp6Prefix();
603 int prefixLen = (int) iaPrefixOption.get().getPrefixLength();
Yi Tseng30262a22017-12-18 17:50:55 -0800604 log.debug("Prefix length is {} bits", prefixLen);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000605 prefixPrefix = IpPrefix.valueOf(prefixAddress, prefixLen);
606
607 } else {
Yi Tseng30262a22017-12-18 17:50:55 -0800608 log.debug("Can't find IPv6 prefix from DHCPv6 {}", dhcp6);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000609 }
610
611 return prefixPrefix;
612 }
613
614 /**
615 * remove host or route.
616 *
617 * @param directConnFlag flag to show that packet is from directly connected client
618 * @param dhcp6Packet the dhcp6 payload
619 * @param clientPacket client's ethernet packet
620 * @param clientIpv6 client's Ipv6 packet
Kalhee Kimaa5172a2017-09-15 17:43:27 +0000621 * @param clientInterface client interfaces
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000622 */
623 private void removeHostOrRoute(boolean directConnFlag, DHCP6 dhcp6Packet,
624 Ethernet clientPacket, IPv6 clientIpv6,
Kalhee Kimaa5172a2017-09-15 17:43:27 +0000625 Interface clientInterface) {
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000626 log.debug("extractPrefix enters {}", dhcp6Packet);
Kalhee Kimc60c7e22017-11-01 17:56:44 +0000627 VlanId vlanId = clientInterface.vlan();
628 MacAddress clientMac = clientPacket.getSourceMAC();
629 log.debug("client mac {} client vlan {}", HexString.toHexString(clientMac.toBytes(), ":"), vlanId);
630
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000631 // add host or route
632 if (isDhcp6Release(dhcp6Packet)) {
633 IpAddress ip = null;
634 if (directConnFlag) {
635 // Add to host store if it is connected to network directly
636 ip = extractIpAddress(dhcp6Packet);
637 if (ip != null) {
Kalhee Kimc60c7e22017-11-01 17:56:44 +0000638
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000639 HostId hostId = HostId.hostId(clientMac, vlanId);
640 log.debug("remove Host {} ip for directly connected.", hostId.toString());
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000641 // Remove host's ip of when dhcp release msg is received
Yi Tseng4b013202017-09-08 17:22:51 -0700642 providerService.removeIpFromHost(hostId, ip);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000643 } else {
644 log.debug("ipAddress not found. Do not add Host for directly connected.");
645 }
646 } else {
647 // Remove from route store if it is not connected to network directly
Kalhee Kimc60c7e22017-11-01 17:56:44 +0000648 // pick out the first link-local ip address
649 IpAddress nextHopIp = getFirstIpByHost(clientMac, vlanId);
650 if (nextHopIp == null) {
651 log.warn("Can't find link-local IP address of gateway mac {} vlanId {}",
652 clientMac, vlanId);
653 return;
654 }
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000655
656 DHCP6 leafDhcp = getDhcp6Leaf(dhcp6Packet);
657 ip = extractIpAddress(leafDhcp);
658 if (ip == null) {
659 log.debug("ip is null");
660 } else {
661 Route routeForIP = new Route(Route.Source.STATIC, ip.toIpPrefix(), nextHopIp);
662 log.debug("removing route of 128 address for indirectly connected.");
663 log.debug("128 ip {}, nexthop {}", HexString.toHexString(ip.toOctets(), ":"),
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000664 HexString.toHexString(nextHopIp.toOctets(), ":"));
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000665 routeStore.removeRoute(routeForIP);
666 }
667
668 IpPrefix ipPrefix = extractPrefix(leafDhcp);
669 if (ipPrefix == null) {
670 log.debug("ipPrefix is null ");
671 } else {
672 Route routeForPrefix = new Route(Route.Source.STATIC, ipPrefix, nextHopIp);
673 log.debug("removing route of PD for indirectly connected.");
674 log.debug("pd ip {}, nexthop {}", HexString.toHexString(ipPrefix.address().toOctets(), ":"),
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000675 HexString.toHexString(nextHopIp.toOctets(), ":"));
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000676
677 routeStore.removeRoute(routeForPrefix);
678 }
679 }
680 }
681 }
682
683 /**
684 * add host or route.
685 *
686 * @param directConnFlag flag to show that packet is from directly connected client
687 * @param dhcp6Relay the dhcp6 payload
688 * @param embeddedDhcp6 client's ethernet packetthe dhcp6 payload within relay
689 * @param clientMac client macAddress
Kalhee Kimaa5172a2017-09-15 17:43:27 +0000690 * @param clientInterface client interface
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000691 */
692 private void addHostOrRoute(boolean directConnFlag, DHCP6 dhcp6Relay,
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000693 DHCP6 embeddedDhcp6,
694 MacAddress clientMac,
695 Interface clientInterface) {
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000696 log.debug("addHostOrRoute entered.");
Kalhee Kimc60c7e22017-11-01 17:56:44 +0000697 VlanId vlanId = clientInterface.vlan();
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000698 // add host or route
699 if (isDhcp6Reply(dhcp6Relay)) {
700 IpAddress ip = null;
701 if (directConnFlag) {
702 // Add to host store if it connect to network directly
703 ip = extractIpAddress(embeddedDhcp6);
704 if (ip != null) {
705 Set<IpAddress> ips = Sets.newHashSet(ip);
Yi Tseng4b013202017-09-08 17:22:51 -0700706
707 // FIXME: we should use vlan id from original packet (solicit, request)
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000708 HostId hostId = HostId.hostId(clientMac, vlanId);
Yi Tseng4b013202017-09-08 17:22:51 -0700709 Host host = hostService.getHost(hostId);
Kalhee Kimaa5172a2017-09-15 17:43:27 +0000710 HostLocation hostLocation = new HostLocation(clientInterface.connectPoint(),
Yi Tseng4b013202017-09-08 17:22:51 -0700711 System.currentTimeMillis());
712 Set<HostLocation> hostLocations = Sets.newHashSet(hostLocation);
713
714 if (host != null) {
715 // Dual homing support:
716 // if host exists, use old locations and new location
717 hostLocations.addAll(host.locations());
718 }
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000719 HostDescription desc = new DefaultHostDescription(clientMac, vlanId,
Yi Tseng4b013202017-09-08 17:22:51 -0700720 hostLocations, ips,
721 false);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000722 log.debug("adding Host for directly connected.");
723 log.debug("client mac {} client vlan {} hostlocation {}",
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000724 HexString.toHexString(clientMac.toBytes(), ":"),
725 vlanId, hostLocation.toString());
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000726
727 // Replace the ip when dhcp server give the host new ip address
Yi Tseng4b013202017-09-08 17:22:51 -0700728 providerService.hostDetected(hostId, desc, false);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000729 } else {
Yi Tseng30262a22017-12-18 17:50:55 -0800730 log.debug("ipAddress not found. Do not add Host for directly connected.");
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000731 }
732 } else {
733 // Add to route store if it does not connect to network directly
Kalhee Kimc60c7e22017-11-01 17:56:44 +0000734 // pick out the first link-local ip address
735 IpAddress nextHopIp = getFirstIpByHost(clientMac, vlanId);
736 if (nextHopIp == null) {
737 log.warn("Can't find link-local IP address of gateway mac {} vlanId {}",
738 clientMac, vlanId);
739 return;
740 }
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000741
742 DHCP6 leafDhcp = getDhcp6Leaf(embeddedDhcp6);
743 ip = extractIpAddress(leafDhcp);
744 if (ip == null) {
Yi Tseng30262a22017-12-18 17:50:55 -0800745 log.trace("ip is null");
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000746 } else {
747 Route routeForIP = new Route(Route.Source.STATIC, ip.toIpPrefix(), nextHopIp);
Yi Tseng30262a22017-12-18 17:50:55 -0800748 log.trace("adding Route of 128 address for indirectly connected.");
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000749 routeStore.updateRoute(routeForIP);
750 }
751
752 IpPrefix ipPrefix = extractPrefix(leafDhcp);
753 if (ipPrefix == null) {
Yi Tseng30262a22017-12-18 17:50:55 -0800754 log.trace("ipPrefix is null ");
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000755 } else {
756 Route routeForPrefix = new Route(Route.Source.STATIC, ipPrefix, nextHopIp);
Yi Tseng30262a22017-12-18 17:50:55 -0800757 log.trace("adding Route of PD for indirectly connected.");
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000758 routeStore.updateRoute(routeForPrefix);
759 }
760 }
761 }
762 }
763
764 /**
Yi Tsengc44dc2e2017-11-03 16:27:32 -0700765 * Build the DHCP6 solicit/request packet with gatewayip.
766 * TODO: method too long, need to be refactored.
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000767 *
768 * @param context packet context
769 * @param clientPacket client ethernet packet
770 * @param clientInterfaces set of client side interfaces
771 */
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000772 private InternalPacket processDhcp6PacketFromClient(PacketContext context,
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000773 Ethernet clientPacket, Set<Interface> clientInterfaces) {
Yi Tsengc44dc2e2017-11-03 16:27:32 -0700774 ConnectPoint receivedFrom = context.inPacket().receivedFrom();
775 DeviceId receivedFromDevice = receivedFrom.deviceId();
776 DhcpServerInfo serverInfo;
777 Ip6Address dhcpServerIp = null;
778 ConnectPoint dhcpServerConnectPoint = null;
779 MacAddress dhcpConnectMac = null;
780 VlanId dhcpConnectVlan = null;
781 Ip6Address dhcpGatewayIp = null;
782 Ip6Address indirectDhcpServerIp = null;
783 ConnectPoint indirectDhcpServerConnectPoint = null;
784 MacAddress indirectDhcpConnectMac = null;
785 VlanId indirectDhcpConnectVlan = null;
786 Ip6Address indirectDhcpGatewayIp = null;
787 Ip6Address indirectRelayAgentIpFromCfg = null;
788 if (!defaultServerInfoList.isEmpty()) {
789 serverInfo = defaultServerInfoList.get(0);
790 dhcpConnectMac = serverInfo.getDhcpConnectMac().orElse(null);
791 dhcpGatewayIp = serverInfo.getDhcpGatewayIp6().orElse(null);
792 dhcpServerIp = serverInfo.getDhcpServerIp6().orElse(null);
793 dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
794 dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
795 }
796 if (!indirectServerInfoList.isEmpty()) {
797 serverInfo = indirectServerInfoList.get(0);
798 indirectDhcpConnectMac = serverInfo.getDhcpConnectMac().orElse(null);
799 indirectDhcpGatewayIp = serverInfo.getDhcpGatewayIp6().orElse(null);
800 indirectDhcpServerIp = serverInfo.getDhcpServerIp6().orElse(null);
801 indirectDhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
802 indirectDhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
803 indirectRelayAgentIpFromCfg = serverInfo.getRelayAgentIp6(receivedFromDevice).orElse(null);
804 }
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000805 Ip6Address relayAgentIp = getRelayAgentIPv6Address(clientInterfaces);
806 MacAddress relayAgentMac = clientInterfaces.iterator().next().mac();
807 if (relayAgentIp == null || relayAgentMac == null) {
808 log.warn("Missing DHCP relay agent interface Ipv6 addr config for "
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000809 + "packet from client on port: {}. Aborting packet processing",
810 clientInterfaces.iterator().next().connectPoint());
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000811 return null;
812 }
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000813 // get dhcp6 header.
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000814 IPv6 clientIpv6 = (IPv6) clientPacket.getPayload();
815 UDP clientUdp = (UDP) clientIpv6.getPayload();
816 DHCP6 clientDhcp6 = (DHCP6) clientUdp.getPayload();
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000817 boolean directConnFlag = directlyConnected(clientDhcp6);
Yi Tsengdc510082017-11-29 14:39:18 -0800818 Interface serverInterface;
819 if (directConnFlag) {
820 serverInterface = getServerInterface();
821 } else {
822 serverInterface = getIndirectServerInterface();
823 if (serverInterface == null) {
824 // Indirect server interface not found, use default server interface
825 serverInterface = getServerInterface();
826 }
827 }
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000828 if (serverInterface == null) {
829 log.warn("Can't get {} server interface, ignore", directConnFlag ? "direct" : "indirect");
830 return null;
831 }
832 Ip6Address ipFacingServer = getFirstIpFromInterface(serverInterface);
833 MacAddress macFacingServer = serverInterface.mac();
834 if (ipFacingServer == null || macFacingServer == null) {
835 log.warn("No IP v6 address for server Interface {}", serverInterface);
836 return null;
837 }
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000838 Ethernet etherReply = (Ethernet) clientPacket.clone();
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000839 etherReply.setSourceMACAddress(macFacingServer);
Yi Tsengc44dc2e2017-11-03 16:27:32 -0700840 if ((directConnFlag && dhcpConnectMac == null) ||
841 !directConnFlag && indirectDhcpConnectMac == null && dhcpConnectMac == null) {
Yi Tseng30262a22017-12-18 17:50:55 -0800842 log.trace("Packet received from {} connected client.", directConnFlag ? "directly" : "indirectly");
Charles Chanf6a77be2017-10-30 13:44:33 -0700843 log.warn("DHCP6 {} not yet resolved .. Aborting DHCP packet processing from client on port: {}",
Yi Tsengc44dc2e2017-11-03 16:27:32 -0700844 (dhcpGatewayIp == null) ? "server IP " + dhcpServerIp
845 : "gateway IP " + dhcpGatewayIp,
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000846 clientInterfaces.iterator().next().connectPoint());
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000847 return null;
848 }
Yi Tsengc44dc2e2017-11-03 16:27:32 -0700849 if (dhcpServerConnectPoint == null) {
Kalhee Kim2575d2c2017-09-26 20:21:53 +0000850 log.warn("DHCP6 server connection point direct {} directConn {} indirectConn {} is not set up yet",
Yi Tsengc44dc2e2017-11-03 16:27:32 -0700851 directConnFlag, dhcpServerConnectPoint, indirectDhcpServerConnectPoint);
852
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000853 return null;
854 }
855
Yi Tsengc44dc2e2017-11-03 16:27:32 -0700856 etherReply.setDestinationMACAddress(dhcpConnectMac);
857 etherReply.setVlanID(dhcpConnectVlan.toShort());
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000858
859 IPv6 ipv6Packet = (IPv6) etherReply.getPayload();
Kalhee Kimc60c7e22017-11-01 17:56:44 +0000860 byte[] peerAddress = clientIpv6.getSourceAddress();
Kalhee Kim45c93852017-10-27 20:16:26 +0000861 ipv6Packet.setSourceAddress(ipFacingServer.toOctets());
Yi Tsengc44dc2e2017-11-03 16:27:32 -0700862 ipv6Packet.setDestinationAddress(dhcpServerIp.toOctets());
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000863
864 UDP udpPacket = (UDP) ipv6Packet.getPayload();
865 udpPacket.setSourcePort(UDP.DHCP_V6_SERVER_PORT);
866 DHCP6 dhcp6Packet = (DHCP6) udpPacket.getPayload();
867 byte[] dhcp6PacketByte = dhcp6Packet.serialize();
868
869 // notify onos and quagga to release PD
870 //releasePD(dhcp6Packet);
Kalhee Kimaa5172a2017-09-15 17:43:27 +0000871 ConnectPoint clientConnectionPoint = context.inPacket().receivedFrom();
872 VlanId vlanIdInUse = VlanId.vlanId(clientPacket.getVlanID());
873 Interface clientInterface = interfaceService.getInterfacesByPort(clientConnectionPoint)
Charles Chanf6a77be2017-10-30 13:44:33 -0700874 .stream().filter(iface -> interfaceContainsVlan(iface, vlanIdInUse))
875 .findFirst().orElse(null);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000876
Kalhee Kimaa5172a2017-09-15 17:43:27 +0000877 removeHostOrRoute(directConnFlag, dhcp6Packet, clientPacket, clientIpv6, clientInterface);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000878
879 DHCP6 dhcp6Relay = new DHCP6();
880 dhcp6Relay.setMsgType(DHCP6.MsgType.RELAY_FORW.value());
881 // link address: server uses the address to identify the link on which the client
882 // is located.
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000883 if (directConnFlag) {
884 dhcp6Relay.setLinkAddress(relayAgentIp.toOctets());
885 log.debug("direct connection: relayAgentIp obtained dynamically {}",
886 HexString.toHexString(relayAgentIp.toOctets(), ":"));
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000887
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000888 } else {
Yi Tsengc44dc2e2017-11-03 16:27:32 -0700889 if (indirectDhcpServerIp == null) {
Yi Tseng30262a22017-12-18 17:50:55 -0800890 log.debug("indirect DhcpServerIp not available, use default DhcpServerIp {}",
Yi Tsengc44dc2e2017-11-03 16:27:32 -0700891 HexString.toHexString(dhcpServerIp.toOctets()));
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000892 } else {
893 // Indirect case, replace destination to indirect dhcp server if exist
894 // Check if mac is obtained for valid server ip
Yi Tsengc44dc2e2017-11-03 16:27:32 -0700895 if (indirectDhcpConnectMac == null) {
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000896 log.warn("DHCP6 {} not yet resolved .. Aborting DHCP "
897 + "packet processing from client on port: {}",
Yi Tsengc44dc2e2017-11-03 16:27:32 -0700898 (indirectDhcpGatewayIp == null) ? "server IP " + indirectDhcpServerIp
899 : "gateway IP " + indirectDhcpGatewayIp,
Kalhee Kim2575d2c2017-09-26 20:21:53 +0000900 clientInterfaces.iterator().next().connectPoint());
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000901 return null;
902 }
Yi Tsengc44dc2e2017-11-03 16:27:32 -0700903 etherReply.setDestinationMACAddress(indirectDhcpConnectMac);
904 etherReply.setVlanID(indirectDhcpConnectVlan.toShort());
905 ipv6Packet.setDestinationAddress(indirectDhcpServerIp.toOctets());
Kalhee Kim2575d2c2017-09-26 20:21:53 +0000906
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000907 }
Yi Tsengc44dc2e2017-11-03 16:27:32 -0700908 if (indirectRelayAgentIpFromCfg == null) {
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000909 dhcp6Relay.setLinkAddress(relayAgentIp.toOctets());
Yi Tseng30262a22017-12-18 17:50:55 -0800910 log.debug("indirect connection: relayAgentIp NOT availale from config file! Use dynamic. {}",
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000911 HexString.toHexString(relayAgentIp.toOctets(), ":"));
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000912 } else {
Yi Tsengc44dc2e2017-11-03 16:27:32 -0700913 dhcp6Relay.setLinkAddress(indirectRelayAgentIpFromCfg.toOctets());
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000914 log.debug("indirect connection: relayAgentIp from config file is available! {}",
Yi Tsengc44dc2e2017-11-03 16:27:32 -0700915 HexString.toHexString(indirectRelayAgentIpFromCfg.toOctets(), ":"));
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000916 }
917 }
Yi Tsengc44dc2e2017-11-03 16:27:32 -0700918 // peer address: address of the client or relay agent from which
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000919 // the message to be relayed was received.
Kalhee Kimc60c7e22017-11-01 17:56:44 +0000920 dhcp6Relay.setPeerAddress(peerAddress);
Yi Tsengc44dc2e2017-11-03 16:27:32 -0700921 List<Dhcp6Option> options = new ArrayList<>();
922 // directly connected case, hop count is zero; otherwise, hop count + 1
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000923 if (directConnFlag) {
924 dhcp6Relay.setHopCount((byte) 0);
925 } else {
926 dhcp6Relay.setHopCount((byte) (dhcp6Packet.getHopCount() + 1));
927 }
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000928 // create relay message option
929 Dhcp6Option relayMessage = new Dhcp6Option();
930 relayMessage.setCode(DHCP6.OptionCode.RELAY_MSG.value());
931 relayMessage.setLength((short) dhcp6PacketByte.length);
932 relayMessage.setData(dhcp6PacketByte);
933 options.add(relayMessage);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000934 // create interfaceId option
Kalhee Kimaa5172a2017-09-15 17:43:27 +0000935 String inPortString = "-" + context.inPacket().receivedFrom().toString() + ":";
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000936 Dhcp6Option interfaceId = new Dhcp6Option();
937 interfaceId.setCode(DHCP6.OptionCode.INTERFACE_ID.value());
938 byte[] clientSoureMacBytes = clientPacket.getSourceMACAddress();
939 byte[] inPortStringBytes = inPortString.getBytes();
Kalhee Kimaa5172a2017-09-15 17:43:27 +0000940 byte[] vlanIdBytes = new byte[2];
941 vlanIdBytes[0] = (byte) (clientPacket.getVlanID() & 0xff);
942 vlanIdBytes[1] = (byte) ((clientPacket.getVlanID() >> 8) & 0xff);
943 byte[] interfaceIdBytes = new byte[clientSoureMacBytes.length +
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000944 inPortStringBytes.length + vlanIdBytes.length];
Kalhee Kimaa5172a2017-09-15 17:43:27 +0000945 log.debug("Length: interfaceIdBytes {} clientSoureMacBytes {} inPortStringBytes {} vlan {}",
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000946 interfaceIdBytes.length, clientSoureMacBytes.length, inPortStringBytes.length,
947 vlanIdBytes.length);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000948 System.arraycopy(clientSoureMacBytes, 0, interfaceIdBytes, 0, clientSoureMacBytes.length);
949 System.arraycopy(inPortStringBytes, 0, interfaceIdBytes, clientSoureMacBytes.length, inPortStringBytes.length);
Kalhee Kimaa5172a2017-09-15 17:43:27 +0000950 System.arraycopy(vlanIdBytes, 0, interfaceIdBytes, clientSoureMacBytes.length + inPortStringBytes.length,
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000951 vlanIdBytes.length);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000952 interfaceId.setData(interfaceIdBytes);
953 interfaceId.setLength((short) interfaceIdBytes.length);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000954 options.add(interfaceId);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000955 log.debug("interfaceId write srcMac {} portString {}",
956 HexString.toHexString(clientSoureMacBytes, ":"), inPortString);
957 dhcp6Relay.setOptions(options);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000958 udpPacket.setPayload(dhcp6Relay);
959 udpPacket.resetChecksum();
960 ipv6Packet.setPayload(udpPacket);
Charles Chan7edf7642017-10-09 11:07:25 -0400961 ipv6Packet.setHopLimit((byte) 64);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000962 etherReply.setPayload(ipv6Packet);
Yi Tsengc44dc2e2017-11-03 16:27:32 -0700963 if (directConnFlag || indirectDhcpServerIp == null) {
964 return new InternalPacket(etherReply, dhcpServerConnectPoint);
Kalhee Kim2575d2c2017-09-26 20:21:53 +0000965 } else {
Yi Tsengc44dc2e2017-11-03 16:27:32 -0700966 return new InternalPacket(etherReply, indirectDhcpServerConnectPoint);
Kalhee Kim2575d2c2017-09-26 20:21:53 +0000967 }
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000968 }
969
Yi Tsengc44dc2e2017-11-03 16:27:32 -0700970
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000971 /**
972 *
973 * process the DHCP6 relay-reply packet from dhcp server.
974 *
975 * @param context packet context
976 * @param receivedPacket server ethernet packet
977 * @param recevingInterfaces set of server side interfaces
978 */
979 private InternalPacket processDhcp6PacketFromServer(PacketContext context,
980 Ethernet receivedPacket, Set<Interface> recevingInterfaces) {
Yi Tsengc44dc2e2017-11-03 16:27:32 -0700981
982 ConnectPoint receivedFrom = context.inPacket().receivedFrom();
983 DeviceId receivedFromDevice = receivedFrom.deviceId();
984
985 // TODO: refactor
986 DhcpServerInfo serverInfo;
987 Ip6Address dhcpServerIp = null;
988 ConnectPoint dhcpServerConnectPoint = null;
989 MacAddress dhcpConnectMac = null;
990 VlanId dhcpConnectVlan = null;
991 Ip6Address dhcpGatewayIp = null;
992
993 Ip6Address indirectDhcpServerIp = null;
994 ConnectPoint indirectDhcpServerConnectPoint = null;
995 MacAddress indirectDhcpConnectMac = null;
996 VlanId indirectDhcpConnectVlan = null;
997 Ip6Address indirectDhcpGatewayIp = null;
998 Ip6Address indirectRelayAgentIpFromCfg = null;
999
1000 if (!defaultServerInfoList.isEmpty()) {
1001 serverInfo = defaultServerInfoList.get(0);
1002 dhcpConnectMac = serverInfo.getDhcpConnectMac().orElse(null);
1003 dhcpGatewayIp = serverInfo.getDhcpGatewayIp6().orElse(null);
1004 dhcpServerIp = serverInfo.getDhcpServerIp6().orElse(null);
1005 dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1006 dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1007 }
1008
1009 if (!indirectServerInfoList.isEmpty()) {
1010 serverInfo = indirectServerInfoList.get(0);
1011 indirectDhcpConnectMac = serverInfo.getDhcpConnectMac().orElse(null);
1012 indirectDhcpGatewayIp = serverInfo.getDhcpGatewayIp6().orElse(null);
1013 indirectDhcpServerIp = serverInfo.getDhcpServerIp6().orElse(null);
1014 indirectDhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1015 indirectDhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1016 indirectRelayAgentIpFromCfg = serverInfo.getRelayAgentIp6(receivedFromDevice).orElse(null);
1017 }
1018
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001019 // get dhcp6 header.
1020 Ethernet etherReply = (Ethernet) receivedPacket.clone();
1021 IPv6 ipv6Packet = (IPv6) etherReply.getPayload();
1022 UDP udpPacket = (UDP) ipv6Packet.getPayload();
1023 DHCP6 dhcp6Relay = (DHCP6) udpPacket.getPayload();
1024
1025 Boolean directConnFlag = directlyConnected(dhcp6Relay);
Kalhee Kim2575d2c2017-09-26 20:21:53 +00001026 ConnectPoint inPort = context.inPacket().receivedFrom();
Yi Tsengc44dc2e2017-11-03 16:27:32 -07001027 if ((directConnFlag || (!directConnFlag && indirectDhcpServerIp == null))
1028 && !inPort.equals(dhcpServerConnectPoint)) {
Kalhee Kim2575d2c2017-09-26 20:21:53 +00001029 log.warn("Receiving port {} is not the same as server connect point {} for direct or indirect-null",
Yi Tsengc44dc2e2017-11-03 16:27:32 -07001030 inPort, dhcpServerConnectPoint);
Kalhee Kim2575d2c2017-09-26 20:21:53 +00001031 return null;
1032 }
1033
Yi Tsengc44dc2e2017-11-03 16:27:32 -07001034 if (!directConnFlag && indirectDhcpServerIp != null &&
1035 !inPort.equals(indirectDhcpServerConnectPoint)) {
Kalhee Kim2575d2c2017-09-26 20:21:53 +00001036 log.warn("Receiving port {} is not the same as server connect point {} for indirect",
Yi Tsengc44dc2e2017-11-03 16:27:32 -07001037 inPort, indirectDhcpServerConnectPoint);
Kalhee Kim2575d2c2017-09-26 20:21:53 +00001038 return null;
1039 }
1040
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001041
1042 Dhcp6InterfaceIdOption interfaceIdOption = dhcp6Relay.getOptions().stream()
1043 .filter(opt -> opt instanceof Dhcp6InterfaceIdOption)
1044 .map(opt -> (Dhcp6InterfaceIdOption) opt)
1045 .findFirst()
1046 .orElse(null);
1047
1048 if (interfaceIdOption == null) {
1049 log.warn("Interface Id option is not present, abort packet...");
1050 return null;
1051 }
1052
1053 MacAddress peerMac = interfaceIdOption.getMacAddress();
1054 String clientConnectionPointStr = new String(interfaceIdOption.getInPort());
1055
1056 ConnectPoint clientConnectionPoint = ConnectPoint.deviceConnectPoint(clientConnectionPointStr);
Kalhee Kimaa5172a2017-09-15 17:43:27 +00001057 VlanId vlanIdInUse = VlanId.vlanId(interfaceIdOption.getVlanId());
1058 Interface clientInterface = interfaceService.getInterfacesByPort(clientConnectionPoint)
1059 .stream()
1060 .filter(iface -> interfaceContainsVlan(iface, vlanIdInUse))
1061 .findFirst()
1062 .orElse(null);
1063 if (clientInterface == null) {
1064 log.warn("Cannot get client interface for from packet, abort... vlan {}", vlanIdInUse.toString());
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001065 return null;
1066 }
Kalhee Kimaa5172a2017-09-15 17:43:27 +00001067 MacAddress relayAgentMac = clientInterface.mac();
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001068 if (relayAgentMac == null) {
1069 log.warn("Can not get interface mac, abort packet..");
1070 return null;
1071 }
1072 etherReply.setSourceMACAddress(relayAgentMac);
1073
1074 // find destMac
1075 MacAddress clientMac = null;
Yi Tseng4b013202017-09-08 17:22:51 -07001076 Ip6Address peerAddress = Ip6Address.valueOf(dhcp6Relay.getPeerAddress());
1077 Set<Host> clients = hostService.getHostsByIp(peerAddress);
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001078 if (clients.isEmpty()) {
Yi Tseng30262a22017-12-18 17:50:55 -08001079 log.debug("There's no host found for this address {}",
Kalhee Kim683ba3b2017-10-18 18:30:23 +00001080 HexString.toHexString(dhcp6Relay.getPeerAddress(), ":"));
Yi Tseng30262a22017-12-18 17:50:55 -08001081 log.debug("Let's look up interfaceId {}", HexString.toHexString(peerMac.toBytes(), ":"));
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001082 clientMac = peerMac;
1083 } else {
1084 clientMac = clients.iterator().next().mac();
1085 if (clientMac == null) {
1086 log.warn("No client mac address found, abort packet...");
1087 return null;
1088 }
Yi Tseng30262a22017-12-18 17:50:55 -08001089 log.trace("Client mac address found from getHostByIp");
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001090 }
1091 etherReply.setDestinationMACAddress(clientMac);
1092
1093 // ip header
1094 ipv6Packet.setSourceAddress(dhcp6Relay.getLinkAddress());
1095 ipv6Packet.setDestinationAddress(dhcp6Relay.getPeerAddress());
1096 // udp header
1097 udpPacket.setSourcePort(UDP.DHCP_V6_SERVER_PORT);
1098 if (directConnFlag) {
1099 udpPacket.setDestinationPort(UDP.DHCP_V6_CLIENT_PORT);
1100 } else {
1101 udpPacket.setDestinationPort(UDP.DHCP_V6_SERVER_PORT);
1102 }
1103
1104 DHCP6 embeddedDhcp6 = dhcp6Relay.getOptions().stream()
Kalhee Kim683ba3b2017-10-18 18:30:23 +00001105 .filter(opt -> opt instanceof Dhcp6RelayOption)
1106 .map(BasePacket::getPayload)
1107 .map(pld -> (DHCP6) pld)
1108 .findFirst()
1109 .orElse(null);
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001110
1111
1112 // add host or route
Kalhee Kimaa5172a2017-09-15 17:43:27 +00001113 addHostOrRoute(directConnFlag, dhcp6Relay, embeddedDhcp6, clientMac, clientInterface);
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001114
1115 udpPacket.setPayload(embeddedDhcp6);
1116 udpPacket.resetChecksum();
1117 ipv6Packet.setPayload(udpPacket);
1118 etherReply.setPayload(ipv6Packet);
1119
1120 return new InternalPacket(etherReply, clientConnectionPoint);
1121 }
1122
Yi Tseng2fe8f3f2017-09-07 16:22:51 -07001123 // Returns the first v6 interface ip out of a set of interfaces or null.
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001124 // Checks all interfaces, and ignores v6 interface ips
1125 private Ip6Address getRelayAgentIPv6Address(Set<Interface> intfs) {
1126 for (Interface intf : intfs) {
1127 for (InterfaceIpAddress ip : intf.ipAddressesList()) {
1128 Ip6Address relayAgentIp = ip.ipAddress().getIp6Address();
1129 if (relayAgentIp != null) {
1130 return relayAgentIp;
1131 }
1132 }
1133 }
1134 return null;
Yi Tseng51301292017-07-28 13:02:59 -07001135 }
Yi Tsenge72fbb52017-08-02 15:03:31 -07001136
1137 @Override
1138 public void setDefaultDhcpServerConfigs(Collection<DhcpServerConfig> configs) {
Yi Tseng2fe8f3f2017-09-07 16:22:51 -07001139 setDhcpServerConfigs(configs, defaultServerInfoList);
Yi Tseng2fe8f3f2017-09-07 16:22:51 -07001140 }
1141
1142 @Override
1143 public void setIndirectDhcpServerConfigs(Collection<DhcpServerConfig> configs) {
1144 setDhcpServerConfigs(configs, indirectServerInfoList);
Yi Tseng2fe8f3f2017-09-07 16:22:51 -07001145 }
1146
1147 public void setDhcpServerConfigs(Collection<DhcpServerConfig> configs, List<DhcpServerInfo> serverInfoList) {
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001148 if (configs.size() == 0) {
1149 // no config to update
1150 return;
1151 }
1152
1153 // TODO: currently we pick up first DHCP server config.
1154 // Will use other server configs in the future for HA.
1155 DhcpServerConfig serverConfig = configs.iterator().next();
Yi Tseng2fe8f3f2017-09-07 16:22:51 -07001156
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001157 if (!serverConfig.getDhcpServerIp6().isPresent()) {
Yi Tseng2fe8f3f2017-09-07 16:22:51 -07001158 // not a DHCPv6 config
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001159 return;
1160 }
1161
Yi Tseng2fe8f3f2017-09-07 16:22:51 -07001162 if (!serverInfoList.isEmpty()) {
1163 // remove old server info
1164 DhcpServerInfo oldServerInfo = serverInfoList.remove(0);
1165
1166 // stop monitoring gateway or server
1167 oldServerInfo.getDhcpGatewayIp6().ifPresent(gatewayIp -> {
1168 hostService.stopMonitoringIp(gatewayIp);
1169 });
1170 oldServerInfo.getDhcpServerIp6().ifPresent(serverIp -> {
1171 hostService.stopMonitoringIp(serverIp);
Yi Tseng7da339e2017-10-23 19:39:39 -07001172 cancelDhcpPacket(serverIp);
Yi Tseng2fe8f3f2017-09-07 16:22:51 -07001173 });
1174 }
1175
1176 // Create new server info according to the config
1177 DhcpServerInfo newServerInfo = new DhcpServerInfo(serverConfig,
1178 DhcpServerInfo.Version.DHCP_V6);
1179 checkState(newServerInfo.getDhcpServerConnectPoint().isPresent(),
1180 "Connect point not exists");
1181 checkState(newServerInfo.getDhcpServerIp6().isPresent(),
1182 "IP of DHCP server not exists");
1183
1184 log.debug("DHCP server connect point: {}", newServerInfo.getDhcpServerConnectPoint().orElse(null));
1185 log.debug("DHCP server IP: {}", newServerInfo.getDhcpServerIp6().orElse(null));
1186
Yi Tseng7da339e2017-10-23 19:39:39 -07001187 Ip6Address serverIp = newServerInfo.getDhcpServerIp6().get();
1188 Ip6Address ipToProbe;
Yi Tseng2fe8f3f2017-09-07 16:22:51 -07001189 if (newServerInfo.getDhcpGatewayIp6().isPresent()) {
1190 ipToProbe = newServerInfo.getDhcpGatewayIp6().get();
1191 } else {
1192 ipToProbe = newServerInfo.getDhcpServerIp6().orElse(null);
1193 }
1194 String hostToProbe = newServerInfo.getDhcpGatewayIp6()
1195 .map(ip -> "gateway").orElse("server");
1196
1197 log.debug("Probing to resolve {} IP {}", hostToProbe, ipToProbe);
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001198 hostService.startMonitoringIp(ipToProbe);
1199
1200 Set<Host> hosts = hostService.getHostsByIp(ipToProbe);
1201 if (!hosts.isEmpty()) {
1202 Host host = hosts.iterator().next();
Yi Tseng2fe8f3f2017-09-07 16:22:51 -07001203 newServerInfo.setDhcpConnectVlan(host.vlan());
1204 newServerInfo.setDhcpConnectMac(host.mac());
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001205 }
Yi Tseng2fe8f3f2017-09-07 16:22:51 -07001206 // Add new server info
Yi Tseng053682b2017-11-09 13:54:12 -08001207 synchronized (this) {
1208 serverInfoList.clear();
1209 serverInfoList.add(0, newServerInfo);
1210 }
Yi Tseng7da339e2017-10-23 19:39:39 -07001211 requestDhcpPacket(serverIp);
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001212 }
1213
1214 class InternalHostListener implements HostListener {
1215 @Override
1216 public void event(HostEvent event) {
1217 switch (event.type()) {
1218 case HOST_ADDED:
1219 case HOST_UPDATED:
1220 hostUpdated(event.subject());
1221 break;
1222 case HOST_REMOVED:
1223 hostRemoved(event.subject());
1224 break;
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001225 default:
1226 break;
1227 }
1228 }
1229 }
1230
1231 /**
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001232 * Handle host updated.
1233 * If the host is DHCP server or gateway, update connect mac and vlan.
1234 *
1235 * @param host the host
1236 */
1237 private void hostUpdated(Host host) {
Yi Tseng7da339e2017-10-23 19:39:39 -07001238 hostUpdated(host, defaultServerInfoList);
1239 hostUpdated(host, indirectServerInfoList);
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001240 }
1241
Yi Tseng7da339e2017-10-23 19:39:39 -07001242 private void hostUpdated(Host host, List<DhcpServerInfo> serverInfoList) {
1243 DhcpServerInfo serverInfo;
1244 Ip6Address targetIp;
1245 if (!serverInfoList.isEmpty()) {
1246 serverInfo = serverInfoList.get(0);
1247 Ip6Address serverIp = serverInfo.getDhcpServerIp6().orElse(null);
1248 targetIp = serverInfo.getDhcpGatewayIp6().orElse(null);
1249
1250 if (targetIp == null) {
1251 targetIp = serverIp;
1252 }
1253
1254 if (targetIp != null) {
1255 if (host.ipAddresses().contains(targetIp)) {
1256 serverInfo.setDhcpConnectMac(host.mac());
1257 serverInfo.setDhcpConnectVlan(host.vlan());
1258 requestDhcpPacket(serverIp);
1259 }
1260 }
1261 }
1262 }
1263
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001264 /**
1265 * Handle host removed.
1266 * If the host is DHCP server or gateway, unset connect mac and vlan.
1267 *
1268 * @param host the host
1269 */
1270 private void hostRemoved(Host host) {
Yi Tseng7da339e2017-10-23 19:39:39 -07001271 hostRemoved(host, defaultServerInfoList);
1272 hostRemoved(host, indirectServerInfoList);
Yi Tseng2fe8f3f2017-09-07 16:22:51 -07001273 }
1274
Yi Tseng7da339e2017-10-23 19:39:39 -07001275 private void hostRemoved(Host host, List<DhcpServerInfo> serverInfoList) {
1276 DhcpServerInfo serverInfo;
1277 Ip6Address targetIp;
1278
1279 if (!serverInfoList.isEmpty()) {
1280 serverInfo = serverInfoList.get(0);
1281 Ip6Address serverIp = serverInfo.getDhcpServerIp6().orElse(null);
1282 targetIp = serverInfo.getDhcpGatewayIp6().orElse(null);
1283
1284 if (targetIp == null) {
1285 targetIp = serverIp;
1286 }
1287
1288 if (targetIp != null) {
1289 if (host.ipAddresses().contains(targetIp)) {
1290 serverInfo.setDhcpConnectVlan(null);
1291 serverInfo.setDhcpConnectMac(null);
1292 cancelDhcpPacket(serverIp);
1293 }
1294 }
1295 }
1296 }
1297
Kalhee Kim683ba3b2017-10-18 18:30:23 +00001298 /**
1299 * Returns the first interface ip from interface.
1300 *
1301 * @param iface interface of one connect point
1302 * @return the first interface IP; null if not exists an IP address in
1303 * these interfaces
1304 */
1305 private Ip6Address getFirstIpFromInterface(Interface iface) {
1306 checkNotNull(iface, "Interface can't be null");
1307 return iface.ipAddressesList().stream()
1308 .map(InterfaceIpAddress::ipAddress)
1309 .filter(IpAddress::isIp6)
1310 .map(IpAddress::getIp6Address)
1311 .findFirst()
1312 .orElse(null);
1313 }
1314
1315 /**
1316 * Gets Interface facing to the server for default host.
1317 *
1318 * @return the Interface facing to the server; null if not found
1319 */
1320 private Interface getServerInterface() {
Yi Tsengc44dc2e2017-11-03 16:27:32 -07001321 DhcpServerInfo serverInfo;
1322 ConnectPoint dhcpServerConnectPoint;
1323 VlanId dhcpConnectVlan;
1324
1325 if (!defaultServerInfoList.isEmpty()) {
1326 serverInfo = defaultServerInfoList.get(0);
1327 dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1328 dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1329 } else {
Kalhee Kim683ba3b2017-10-18 18:30:23 +00001330 return null;
1331 }
Yi Tseng8f49a732017-11-17 17:14:41 -08001332 if (dhcpServerConnectPoint == null || dhcpConnectVlan == null) {
1333 log.info("Default DHCP server {} not resolve yet", serverInfo.getDhcpGatewayIp6());
1334 return null;
1335 }
Kalhee Kim683ba3b2017-10-18 18:30:23 +00001336 return interfaceService.getInterfacesByPort(dhcpServerConnectPoint)
1337 .stream()
1338 .filter(iface -> interfaceContainsVlan(iface, dhcpConnectVlan))
1339 .findFirst()
1340 .orElse(null);
1341 }
1342
1343 /**
1344 * Gets Interface facing to the server for indirect hosts.
1345 * Use default server Interface if indirect server not configured.
1346 *
1347 * @return the Interface facing to the server; null if not found
1348 */
1349 private Interface getIndirectServerInterface() {
Yi Tsengc44dc2e2017-11-03 16:27:32 -07001350 DhcpServerInfo serverInfo;
1351
1352 ConnectPoint indirectDhcpServerConnectPoint;
1353 VlanId indirectDhcpConnectVlan;
1354
1355 if (!indirectServerInfoList.isEmpty()) {
1356 serverInfo = indirectServerInfoList.get(0);
1357 indirectDhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1358 indirectDhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1359 } else {
Kalhee Kim683ba3b2017-10-18 18:30:23 +00001360 return getServerInterface();
1361 }
Yi Tseng8f49a732017-11-17 17:14:41 -08001362 if (indirectDhcpServerConnectPoint == null || indirectDhcpConnectVlan == null) {
1363 log.info("Indirect DHCP server {} not resolve yet", serverInfo.getDhcpGatewayIp6());
1364 return null;
1365 }
Kalhee Kim683ba3b2017-10-18 18:30:23 +00001366 return interfaceService.getInterfacesByPort(indirectDhcpServerConnectPoint)
1367 .stream()
1368 .filter(iface -> interfaceContainsVlan(iface, indirectDhcpConnectVlan))
1369 .findFirst()
1370 .orElse(null);
1371 }
1372
Kalhee Kimaa5172a2017-09-15 17:43:27 +00001373 /**
1374 * Determind if an Interface contains a vlan id.
1375 *
1376 * @param iface the Interface
1377 * @param vlanId the vlan id
1378 * @return true if the Interface contains the vlan id
1379 */
1380 private boolean interfaceContainsVlan(Interface iface, VlanId vlanId) {
1381 if (vlanId.equals(VlanId.NONE)) {
1382 // untagged packet, check if vlan untagged or vlan native is not NONE
1383 return !iface.vlanUntagged().equals(VlanId.NONE) ||
1384 !iface.vlanNative().equals(VlanId.NONE);
1385 }
1386 // tagged packet, check if the interface contains the vlan
1387 return iface.vlanTagged().contains(vlanId);
1388 }
1389
Yi Tseng7da339e2017-10-23 19:39:39 -07001390 private void requestDhcpPacket(Ip6Address serverIp) {
1391 requestServerDhcpPacket(serverIp);
1392 requestClientDhcpPacket(serverIp);
1393 }
1394
1395 private void cancelDhcpPacket(Ip6Address serverIp) {
1396 cancelServerDhcpPacket(serverIp);
1397 cancelClientDhcpPacket(serverIp);
1398 }
1399
1400 private void cancelServerDhcpPacket(Ip6Address serverIp) {
1401 TrafficSelector serverSelector =
1402 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1403 .matchIPv6Src(serverIp.toIpPrefix())
1404 .build();
1405 packetService.cancelPackets(serverSelector,
1406 PacketPriority.CONTROL,
1407 appId);
1408 }
1409
1410 private void requestServerDhcpPacket(Ip6Address serverIp) {
1411 TrafficSelector serverSelector =
1412 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1413 .matchIPv6Src(serverIp.toIpPrefix())
1414 .build();
1415 packetService.requestPackets(serverSelector,
1416 PacketPriority.CONTROL,
1417 appId);
1418 }
1419
1420 private void cancelClientDhcpPacket(Ip6Address serverIp) {
1421 // Packet comes from relay
1422 TrafficSelector indirectClientSelector =
1423 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1424 .matchIPv6Dst(serverIp.toIpPrefix())
1425 .build();
1426 packetService.cancelPackets(indirectClientSelector,
1427 PacketPriority.CONTROL,
1428 appId);
Yi Tseng3f2119b2017-11-02 15:21:10 -07001429 indirectClientSelector =
1430 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1431 .matchIPv6Dst(Ip6Address.ALL_DHCP_RELAY_AGENTS_AND_SERVERS.toIpPrefix())
1432 .build();
1433 packetService.cancelPackets(indirectClientSelector,
1434 PacketPriority.CONTROL,
1435 appId);
1436 indirectClientSelector =
1437 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1438 .matchIPv6Dst(Ip6Address.ALL_DHCP_SERVERS.toIpPrefix())
1439 .build();
1440 packetService.cancelPackets(indirectClientSelector,
1441 PacketPriority.CONTROL,
1442 appId);
Yi Tseng7da339e2017-10-23 19:39:39 -07001443
1444 // Packet comes from client
1445 packetService.cancelPackets(CLIENT_SERVER_SELECTOR,
1446 PacketPriority.CONTROL,
1447 appId);
1448 }
1449
1450 private void requestClientDhcpPacket(Ip6Address serverIp) {
1451 // Packet comes from relay
1452 TrafficSelector indirectClientSelector =
1453 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1454 .matchIPv6Dst(serverIp.toIpPrefix())
1455 .build();
1456 packetService.requestPackets(indirectClientSelector,
1457 PacketPriority.CONTROL,
1458 appId);
Yi Tseng3f2119b2017-11-02 15:21:10 -07001459 indirectClientSelector =
1460 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1461 .matchIPv6Dst(Ip6Address.ALL_DHCP_RELAY_AGENTS_AND_SERVERS.toIpPrefix())
1462 .build();
1463 packetService.requestPackets(indirectClientSelector,
1464 PacketPriority.CONTROL,
1465 appId);
1466 indirectClientSelector =
1467 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1468 .matchIPv6Dst(Ip6Address.ALL_DHCP_SERVERS.toIpPrefix())
1469 .build();
1470 packetService.requestPackets(indirectClientSelector,
1471 PacketPriority.CONTROL,
1472 appId);
Yi Tseng7da339e2017-10-23 19:39:39 -07001473
1474 // Packet comes from client
1475 packetService.requestPackets(CLIENT_SERVER_SELECTOR,
1476 PacketPriority.CONTROL,
1477 appId);
1478 }
1479
1480 /**
1481 * Process the ignore rules.
1482 *
1483 * @param deviceId the device id
1484 * @param vlanId the vlan to be ignored
1485 * @param op the operation, ADD to install; REMOVE to uninstall rules
1486 */
1487 private void processIgnoreVlanRule(DeviceId deviceId, VlanId vlanId, Objective.Operation op) {
Yi Tseng7da339e2017-10-23 19:39:39 -07001488 AtomicInteger installedCount = new AtomicInteger(DHCP_SELECTORS.size());
1489 DHCP_SELECTORS.forEach(trafficSelector -> {
1490 TrafficSelector selector = DefaultTrafficSelector.builder(trafficSelector)
1491 .matchVlanId(vlanId)
1492 .build();
1493
1494 ForwardingObjective.Builder builder = DefaultForwardingObjective.builder()
1495 .withFlag(ForwardingObjective.Flag.VERSATILE)
1496 .withSelector(selector)
1497 .withPriority(IGNORE_CONTROL_PRIORITY)
Yi Tsengf29ffd72017-11-29 10:49:20 -08001498 .withTreatment(DefaultTrafficTreatment.emptyTreatment())
Yi Tseng7da339e2017-10-23 19:39:39 -07001499 .fromApp(appId);
1500
1501
1502 ObjectiveContext objectiveContext = new ObjectiveContext() {
1503 @Override
1504 public void onSuccess(Objective objective) {
1505 log.info("Ignore rule {} (Vlan id {}, device {}, selector {})",
1506 op, vlanId, deviceId, selector);
1507 int countDown = installedCount.decrementAndGet();
1508 if (countDown != 0) {
1509 return;
1510 }
1511 switch (op) {
1512 case ADD:
1513 ignoredVlans.put(deviceId, vlanId);
1514 break;
1515 case REMOVE:
1516 ignoredVlans.remove(deviceId, vlanId);
1517 break;
1518 default:
1519 log.warn("Unsupported objective operation {}", op);
1520 break;
1521 }
1522 }
1523
1524 @Override
1525 public void onError(Objective objective, ObjectiveError error) {
1526 log.warn("Can't {} ignore rule (vlan id {}, selector {}, device {}) due to {}",
1527 op, vlanId, selector, deviceId, error);
1528 }
1529 };
1530
1531 ForwardingObjective fwd;
1532 switch (op) {
1533 case ADD:
1534 fwd = builder.add(objectiveContext);
1535 break;
1536 case REMOVE:
1537 fwd = builder.remove(objectiveContext);
1538 break;
1539 default:
1540 log.warn("Unsupported objective operation {}", op);
1541 return;
1542 }
1543
1544 Device device = deviceService.getDevice(deviceId);
1545 if (device == null || !device.is(Pipeliner.class)) {
1546 log.warn("Device {} is not available now, wait until device is available", deviceId);
1547 return;
1548 }
1549 flowObjectiveService.apply(deviceId, fwd);
1550 });
1551 }
Kalhee Kimc60c7e22017-11-01 17:56:44 +00001552
1553 /**
1554 * Find first ipaddress for a given Host info i.e. mac and vlan.
1555 *
1556 * @param clientMac client mac
1557 * @param vlanId packet's vlan
1558 * @return next-hop link-local ipaddress for a given host
1559 */
1560 private IpAddress getFirstIpByHost(MacAddress clientMac, VlanId vlanId) {
1561 IpAddress nextHopIp;
1562 // pick out the first link-local ip address
1563 HostId gwHostId = HostId.hostId(clientMac, vlanId);
1564 Host gwHost = hostService.getHost(gwHostId);
1565 if (gwHost == null) {
1566 log.warn("Can't find gateway host for hostId {}", gwHostId);
1567 return null;
1568 }
1569 nextHopIp = gwHost.ipAddresses()
1570 .stream()
1571 .filter(IpAddress::isIp6)
Yi Tseng30262a22017-12-18 17:50:55 -08001572 .filter(IpAddress::isLinkLocal)
Kalhee Kimc60c7e22017-11-01 17:56:44 +00001573 .map(IpAddress::getIp6Address)
1574 .findFirst()
1575 .orElse(null);
1576 return nextHopIp;
1577 }
Yi Tseng51301292017-07-28 13:02:59 -07001578}