blob: e7e56d0b3bb23d227bffea0aad76af9b89a5d80b [file] [log] [blame]
Yi Tseng51301292017-07-28 13:02:59 -07001/*
2 * Copyright 2017-present Open Networking Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 */
17
18package org.onosproject.dhcprelay;
19
Yi Tseng525ff402017-10-23 19:39:39 -070020import com.google.common.collect.HashMultimap;
Yi Tseng919b2df2017-09-07 16:22:51 -070021import com.google.common.collect.Lists;
Yi Tseng525ff402017-10-23 19:39:39 -070022import com.google.common.collect.Multimap;
Kalhee Kim45fede42017-09-05 19:05:06 +000023import org.apache.felix.scr.annotations.Activate;
24import org.apache.felix.scr.annotations.Deactivate;
Kalhee Kim45fede42017-09-05 19:05:06 +000025import com.google.common.collect.Sets;
26import com.google.common.collect.ImmutableSet;
Yi Tseng51301292017-07-28 13:02:59 -070027import org.apache.felix.scr.annotations.Component;
28import org.apache.felix.scr.annotations.Property;
Kalhee Kim45fede42017-09-05 19:05:06 +000029import org.apache.felix.scr.annotations.Reference;
30import org.apache.felix.scr.annotations.ReferenceCardinality;
Yi Tseng51301292017-07-28 13:02:59 -070031import org.apache.felix.scr.annotations.Service;
32import org.onlab.packet.BasePacket;
Kalhee Kim45fede42017-09-05 19:05:06 +000033import org.onlab.packet.DHCP6;
34import org.onlab.packet.IPv6;
35import org.onlab.packet.Ethernet;
36import org.onlab.packet.Ip6Address;
Yi Tseng51301292017-07-28 13:02:59 -070037import org.onlab.packet.IpAddress;
Kalhee Kim45fede42017-09-05 19:05:06 +000038import org.onlab.packet.IpPrefix;
Yi Tseng51301292017-07-28 13:02:59 -070039import org.onlab.packet.MacAddress;
Yi Tseng525ff402017-10-23 19:39:39 -070040import org.onlab.packet.TpPort;
Kalhee Kim45fede42017-09-05 19:05:06 +000041import org.onlab.packet.UDP;
Yi Tseng51301292017-07-28 13:02:59 -070042import org.onlab.packet.VlanId;
Kalhee Kim45fede42017-09-05 19:05:06 +000043import org.onlab.packet.dhcp.Dhcp6RelayOption;
44import org.onlab.packet.dhcp.Dhcp6InterfaceIdOption;
45import org.onlab.packet.dhcp.Dhcp6Option;
46import org.onlab.packet.dhcp.Dhcp6IaNaOption;
47import org.onlab.packet.dhcp.Dhcp6IaTaOption;
48import org.onlab.packet.dhcp.Dhcp6IaPdOption;
49import org.onlab.packet.dhcp.Dhcp6IaAddressOption;
50import org.onlab.packet.dhcp.Dhcp6IaPrefixOption;
51import org.onlab.util.HexString;
Yi Tseng525ff402017-10-23 19:39:39 -070052import org.onosproject.core.ApplicationId;
53import org.onosproject.core.CoreService;
Yi Tseng51301292017-07-28 13:02:59 -070054import org.onosproject.dhcprelay.api.DhcpHandler;
Yi Tseng919b2df2017-09-07 16:22:51 -070055import org.onosproject.dhcprelay.api.DhcpServerInfo;
Yi Tseng525ff402017-10-23 19:39:39 -070056import org.onosproject.dhcprelay.config.IgnoreDhcpConfig;
Kalhee Kim45fede42017-09-05 19:05:06 +000057import org.onosproject.dhcprelay.store.DhcpRelayStore;
Kalhee Kimba366062017-11-07 16:32:09 +000058import org.onosproject.dhcprelay.store.DhcpFpmPrefixStore;
Yi Tseng525ff402017-10-23 19:39:39 -070059import org.onosproject.net.Device;
60import org.onosproject.net.DeviceId;
Kalhee Kimba366062017-11-07 16:32:09 +000061import org.onosproject.routing.fpm.api.FpmRecord;
Yi Tseng525ff402017-10-23 19:39:39 -070062import org.onosproject.net.behaviour.Pipeliner;
63import org.onosproject.net.device.DeviceService;
64import org.onosproject.net.flow.DefaultTrafficSelector;
65import org.onosproject.net.flow.TrafficSelector;
66import org.onosproject.net.flowobjective.DefaultForwardingObjective;
67import org.onosproject.net.flowobjective.FlowObjectiveService;
68import org.onosproject.net.flowobjective.ForwardingObjective;
69import org.onosproject.net.flowobjective.Objective;
70import org.onosproject.net.flowobjective.ObjectiveContext;
71import org.onosproject.net.flowobjective.ObjectiveError;
Yi Tsengaa417a62017-09-08 17:22:51 -070072import org.onosproject.net.host.HostProvider;
73import org.onosproject.net.host.HostProviderRegistry;
74import org.onosproject.net.host.HostProviderService;
Kalhee Kim45fede42017-09-05 19:05:06 +000075import org.onosproject.net.host.HostService;
76import org.onosproject.net.host.DefaultHostDescription;
77import org.onosproject.net.host.HostDescription;
78import org.onosproject.net.host.InterfaceIpAddress;
79import org.onosproject.net.host.HostListener;
80import org.onosproject.net.host.HostEvent;
81import org.onosproject.net.intf.Interface;
82import org.onosproject.net.intf.InterfaceService;
Yi Tseng525ff402017-10-23 19:39:39 -070083import org.onosproject.net.packet.PacketPriority;
Yi Tsengaa417a62017-09-08 17:22:51 -070084import org.onosproject.net.provider.ProviderId;
Kalhee Kim45fede42017-09-05 19:05:06 +000085import org.onosproject.routeservice.Route;
86import org.onosproject.routeservice.RouteStore;
Yi Tseng483ac6f2017-08-02 15:03:31 -070087import org.onosproject.dhcprelay.config.DhcpServerConfig;
Yi Tseng51301292017-07-28 13:02:59 -070088import org.onosproject.net.ConnectPoint;
Kalhee Kim45fede42017-09-05 19:05:06 +000089import org.onosproject.net.Host;
90import org.onosproject.net.HostId;
91import org.onosproject.net.HostLocation;
92import org.onosproject.net.packet.DefaultOutboundPacket;
93import org.onosproject.net.packet.OutboundPacket;
Yi Tseng51301292017-07-28 13:02:59 -070094import org.onosproject.net.packet.PacketContext;
Kalhee Kim45fede42017-09-05 19:05:06 +000095import org.onosproject.net.packet.PacketService;
96import org.slf4j.Logger;
97import org.slf4j.LoggerFactory;
98import org.onosproject.net.flow.DefaultTrafficTreatment;
99import org.onosproject.net.flow.TrafficTreatment;
Yi Tseng51301292017-07-28 13:02:59 -0700100
Kalhee Kim45fede42017-09-05 19:05:06 +0000101
102import java.nio.ByteBuffer;
103import java.util.List;
Yi Tseng483ac6f2017-08-02 15:03:31 -0700104import java.util.Collection;
Yi Tseng51301292017-07-28 13:02:59 -0700105import java.util.Optional;
Kalhee Kim45fede42017-09-05 19:05:06 +0000106import java.util.Set;
107import java.util.ArrayList;
Yi Tseng525ff402017-10-23 19:39:39 -0700108import java.util.concurrent.atomic.AtomicInteger;
Kalhee Kim45fede42017-09-05 19:05:06 +0000109
110
111import static com.google.common.base.Preconditions.checkNotNull;
112import static com.google.common.base.Preconditions.checkState;
Yi Tseng525ff402017-10-23 19:39:39 -0700113import static org.onosproject.net.flowobjective.Objective.Operation.ADD;
114import static org.onosproject.net.flowobjective.Objective.Operation.REMOVE;
Yi Tseng51301292017-07-28 13:02:59 -0700115
116@Component
117@Service
118@Property(name = "version", value = "6")
Yi Tsengaa417a62017-09-08 17:22:51 -0700119public class Dhcp6HandlerImpl implements DhcpHandler, HostProvider {
Charles Chand988c282017-09-12 17:09:32 -0700120 public static final String DHCP_V6_RELAY_APP = "org.onosproject.Dhcp6HandlerImpl";
Saurav Das7c6dec12017-09-13 14:35:56 -0700121 public static final ProviderId PROVIDER_ID = new ProviderId("dhcp6", DHCP_V6_RELAY_APP);
Yi Tseng525ff402017-10-23 19:39:39 -0700122 private static final int IGNORE_CONTROL_PRIORITY = PacketPriority.CONTROL.priorityValue() + 1000;
123
124 private static final TrafficSelector CLIENT_SERVER_SELECTOR = DefaultTrafficSelector.builder()
125 .matchEthType(Ethernet.TYPE_IPV6)
126 .matchIPProtocol(IPv6.PROTOCOL_UDP)
127 .matchIPv6Src(IpPrefix.IPV6_LINK_LOCAL_PREFIX)
128 .matchIPv6Dst(Ip6Address.ALL_DHCP_RELAY_AGENTS_AND_SERVERS.toIpPrefix())
129 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_V6_CLIENT_PORT))
130 .matchUdpDst(TpPort.tpPort(UDP.DHCP_V6_SERVER_PORT))
131 .build();
132 private static final TrafficSelector SERVER_RELAY_SELECTOR = DefaultTrafficSelector.builder()
133 .matchEthType(Ethernet.TYPE_IPV6)
134 .matchIPProtocol(IPv6.PROTOCOL_UDP)
135 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_V6_SERVER_PORT))
136 .matchUdpDst(TpPort.tpPort(UDP.DHCP_V6_SERVER_PORT))
137 .build();
138 static final Set<TrafficSelector> DHCP_SELECTORS = ImmutableSet.of(
139 CLIENT_SERVER_SELECTOR,
140 SERVER_RELAY_SELECTOR
141 );
Kalhee Kim45fede42017-09-05 19:05:06 +0000142 private static Logger log = LoggerFactory.getLogger(Dhcp6HandlerImpl.class);
Yi Tseng51301292017-07-28 13:02:59 -0700143
Kalhee Kim45fede42017-09-05 19:05:06 +0000144 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
145 protected DhcpRelayStore dhcpRelayStore;
Yi Tseng51301292017-07-28 13:02:59 -0700146
Kalhee Kim45fede42017-09-05 19:05:06 +0000147 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
148 protected PacketService packetService;
149
150 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Kalhee Kim45fede42017-09-05 19:05:06 +0000151 protected RouteStore routeStore;
152
153 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
154 protected InterfaceService interfaceService;
155
156 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
157 protected HostService hostService;
158
Yi Tsengaa417a62017-09-08 17:22:51 -0700159 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
160 protected HostProviderRegistry providerRegistry;
Kalhee Kim45fede42017-09-05 19:05:06 +0000161
Yi Tseng525ff402017-10-23 19:39:39 -0700162 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
163 protected CoreService coreService;
164
165 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Kalhee Kimba366062017-11-07 16:32:09 +0000166 protected DhcpFpmPrefixStore dhcpFpmPrefixStore;
167
168 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Yi Tseng525ff402017-10-23 19:39:39 -0700169 protected DeviceService deviceService;
170
171 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
172 protected FlowObjectiveService flowObjectiveService;
173
Yi Tsengaa417a62017-09-08 17:22:51 -0700174 protected HostProviderService providerService;
Yi Tseng525ff402017-10-23 19:39:39 -0700175 protected ApplicationId appId;
176 protected Multimap<DeviceId, VlanId> ignoredVlans = HashMultimap.create();
177 private InternalHostListener hostListener = new InternalHostListener();
178
Kalhee Kimba366062017-11-07 16:32:09 +0000179 private Boolean dhcpFpmEnabled = false;
180
Yi Tseng919b2df2017-09-07 16:22:51 -0700181 private List<DhcpServerInfo> defaultServerInfoList = Lists.newArrayList();
182 private List<DhcpServerInfo> indirectServerInfoList = Lists.newArrayList();
183
Kalhee Kim45fede42017-09-05 19:05:06 +0000184
185 // CLIENT message types
186 public static final Set<Byte> MSG_TYPE_FROM_CLIENT =
187 ImmutableSet.of(DHCP6.MsgType.SOLICIT.value(),
188 DHCP6.MsgType.REQUEST.value(),
189 DHCP6.MsgType.REBIND.value(),
190 DHCP6.MsgType.RENEW.value(),
191 DHCP6.MsgType.RELEASE.value(),
192 DHCP6.MsgType.DECLINE.value(),
193 DHCP6.MsgType.CONFIRM.value(),
194 DHCP6.MsgType.RELAY_FORW.value());
195 // SERVER message types
196 public static final Set<Byte> MSG_TYPE_FROM_SERVER =
197 ImmutableSet.of(DHCP6.MsgType.RELAY_REPL.value());
198
199 @Activate
200 protected void activate() {
Yi Tseng525ff402017-10-23 19:39:39 -0700201 appId = coreService.registerApplication(DHCP_V6_RELAY_APP);
Yi Tsengaa417a62017-09-08 17:22:51 -0700202 providerService = providerRegistry.register(this);
Kalhee Kim45fede42017-09-05 19:05:06 +0000203 hostService.addListener(hostListener);
Yi Tseng51301292017-07-28 13:02:59 -0700204 }
205
Kalhee Kim45fede42017-09-05 19:05:06 +0000206 @Deactivate
207 protected void deactivate() {
Yi Tsengaa417a62017-09-08 17:22:51 -0700208 providerRegistry.unregister(this);
Kalhee Kim45fede42017-09-05 19:05:06 +0000209 hostService.removeListener(hostListener);
Yi Tseng919b2df2017-09-07 16:22:51 -0700210 defaultServerInfoList.forEach(this::stopMonitoringIps);
211 defaultServerInfoList.clear();
212 indirectServerInfoList.forEach(this::stopMonitoringIps);
213 indirectServerInfoList.clear();
214 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000215
Yi Tseng919b2df2017-09-07 16:22:51 -0700216 private void stopMonitoringIps(DhcpServerInfo serverInfo) {
217 serverInfo.getDhcpGatewayIp6().ifPresent(gatewayIp -> {
218 hostService.stopMonitoringIp(gatewayIp);
219 });
220 serverInfo.getDhcpServerIp6().ifPresent(serverIp -> {
221 hostService.stopMonitoringIp(serverIp);
222 });
Yi Tseng51301292017-07-28 13:02:59 -0700223 }
224
Yi Tseng51301292017-07-28 13:02:59 -0700225 @Override
Yi Tseng919b2df2017-09-07 16:22:51 -0700226 public List<DhcpServerInfo> getDefaultDhcpServerInfoList() {
227 return defaultServerInfoList;
Yi Tseng51301292017-07-28 13:02:59 -0700228 }
229
230 @Override
Yi Tseng919b2df2017-09-07 16:22:51 -0700231 public List<DhcpServerInfo> getIndirectDhcpServerInfoList() {
232 return indirectServerInfoList;
Yi Tseng51301292017-07-28 13:02:59 -0700233 }
234
235 @Override
Yi Tseng525ff402017-10-23 19:39:39 -0700236 public void updateIgnoreVlanConfig(IgnoreDhcpConfig config) {
237 if (config == null) {
238 ignoredVlans.forEach(((deviceId, vlanId) -> {
239 processIgnoreVlanRule(deviceId, vlanId, REMOVE);
240 }));
241 return;
242 }
243 config.ignoredVlans().forEach((deviceId, vlanId) -> {
244 if (ignoredVlans.get(deviceId).contains(vlanId)) {
245 // don't need to process if it already ignored
246 return;
247 }
248 processIgnoreVlanRule(deviceId, vlanId, ADD);
249 });
250
251 ignoredVlans.forEach((deviceId, vlanId) -> {
252 if (!config.ignoredVlans().get(deviceId).contains(vlanId)) {
253 // not contains in new config, remove it
254 processIgnoreVlanRule(deviceId, vlanId, REMOVE);
255 }
256 });
257 }
258
259 @Override
Kalhee Kim45fede42017-09-05 19:05:06 +0000260 public void processDhcpPacket(PacketContext context, BasePacket payload) {
261 checkNotNull(payload, "DHCP6 payload can't be null");
262 checkState(payload instanceof DHCP6, "Payload is not a DHCP6");
263 DHCP6 dhcp6Payload = (DHCP6) payload;
264 Ethernet receivedPacket = context.inPacket().parsed();
265
266 if (!configured()) {
267 log.warn("Missing DHCP6 relay server config. Abort packet processing");
268 log.warn("dhcp6 payload {}", dhcp6Payload);
269
270 return;
271 }
272
273 byte msgType = dhcp6Payload.getMsgType();
274 log.warn("msgType is {}", msgType);
275
276 ConnectPoint inPort = context.inPacket().receivedFrom();
277 if (inPort == null) {
278 log.warn("incommin ConnectPoint is null");
279 }
280 Set<Interface> receivingInterfaces = interfaceService.getInterfacesByPort(inPort);
281 //ignore the packets if dhcp client interface is not configured on onos.
282 if (receivingInterfaces.isEmpty()) {
283 log.warn("Virtual interface is not configured on {}", inPort);
284 return;
285 }
286
287
288 if (MSG_TYPE_FROM_CLIENT.contains(msgType)) {
289
290 InternalPacket ethernetClientPacket =
291 processDhcp6PacketFromClient(context, receivedPacket, receivingInterfaces);
292 if (ethernetClientPacket != null) {
293 forwardPacket(ethernetClientPacket);
294 }
295
296 } else if (MSG_TYPE_FROM_SERVER.contains(msgType)) {
297 log.warn("calling processDhcp6PacketFromServer with RELAY_REPL", msgType);
298 InternalPacket ethernetPacketReply =
299 processDhcp6PacketFromServer(context, receivedPacket, receivingInterfaces);
300 if (ethernetPacketReply != null) {
301 forwardPacket(ethernetPacketReply);
302 }
303 } else {
304 log.warn("Not so fast, packet type {} not supported yet", msgType);
305 }
306 }
307
308
309 /**
310 * Checks if this app has been configured.
311 *
312 * @return true if all information we need have been initialized
313 */
314 public boolean configured() {
Yi Tseng919b2df2017-09-07 16:22:51 -0700315 return !defaultServerInfoList.isEmpty();
Kalhee Kim45fede42017-09-05 19:05:06 +0000316 }
317
Yi Tsengaa417a62017-09-08 17:22:51 -0700318 @Override
319 public ProviderId id() {
Charles Chand988c282017-09-12 17:09:32 -0700320 return PROVIDER_ID;
Yi Tsengaa417a62017-09-08 17:22:51 -0700321 }
322
323 @Override
324 public void triggerProbe(Host host) {
325 // Do nothing here
326 }
327
Kalhee Kim45fede42017-09-05 19:05:06 +0000328 // the new class the contains Ethernet packet and destination port, kind of like adding
329 // internal header to the packet
330 private class InternalPacket {
331 Ethernet packet;
332 ConnectPoint destLocation;
333 public InternalPacket(Ethernet newPacket, ConnectPoint newLocation) {
334 packet = newPacket;
335 destLocation = newLocation;
336 }
337 void setLocation(ConnectPoint newLocation) {
338 destLocation = newLocation;
339 }
340 }
341
342 //forward the packet to ConnectPoint where the DHCP server is attached.
343 private void forwardPacket(InternalPacket packet) {
344 //send Packetout to dhcp server connectpoint.
345 if (packet.destLocation != null) {
346 TrafficTreatment t = DefaultTrafficTreatment.builder()
347 .setOutput(packet.destLocation.port()).build();
348 OutboundPacket o = new DefaultOutboundPacket(
349 packet.destLocation.deviceId(), t, ByteBuffer.wrap(packet.packet.serialize()));
350 if (log.isTraceEnabled()) {
351 log.trace("Relaying packet to destination {}", packet.destLocation);
352 }
353 packetService.emit(o);
354 } // if
355 }
356
357 /**
358 * Check if the host is directly connected to the network or not.
359 *
360 * @param dhcp6Payload the dhcp6 payload
361 * @return true if the host is directly connected to the network; false otherwise
362 */
363 private boolean directlyConnected(DHCP6 dhcp6Payload) {
364 log.debug("directlyConnected enters");
365
366 if (dhcp6Payload.getMsgType() != DHCP6.MsgType.RELAY_FORW.value() &&
367 dhcp6Payload.getMsgType() != DHCP6.MsgType.RELAY_REPL.value()) {
368 log.debug("directlyConnected true. MsgType {}", dhcp6Payload.getMsgType());
369
370 return true;
371 }
372
373 // Regardless of relay-forward or relay-replay, check if we see another relay message
374 DHCP6 dhcp6Payload2 = dhcp6PacketFromRelayPacket(dhcp6Payload);
375 if (dhcp6Payload2 != null) {
376 if (dhcp6Payload.getMsgType() == DHCP6.MsgType.RELAY_FORW.value()) {
377 log.debug("directlyConnected false. 1st realy-foward, 2nd MsgType {}", dhcp6Payload2.getMsgType());
378 return false;
379 } else {
380 // relay-reply
381 if (dhcp6Payload2.getMsgType() != DHCP6.MsgType.RELAY_REPL.value()) {
382 log.debug("directlyConnected true. 2nd MsgType {}", dhcp6Payload2.getMsgType());
383 return true; // must be directly connected
384 } else {
385 log.debug("directlyConnected false. 1st relay-reply, 2nd relay-reply MsgType {}",
386 dhcp6Payload2.getMsgType());
387 return false; // must be indirectly connected
388 }
389 }
390 } else {
391 log.warn("directlyConnected true.");
392 return true;
393 }
394 }
395
396 /**
397 * extract DHCP6 payload from dhcp6 relay message within relay-forwrd/reply.
398 *
399 * @param dhcp6 dhcp6 relay-reply or relay-foward
400 * @return dhcp6Packet dhcp6 packet extracted from relay-message
401 */
402 private DHCP6 dhcp6PacketFromRelayPacket(DHCP6 dhcp6) {
403 log.debug("dhcp6PacketFromRelayPacket enters. dhcp6 {}", dhcp6);
404
405 // extract the relay message if exist
406 DHCP6 dhcp6Payload = dhcp6.getOptions().stream()
407 .filter(opt -> opt instanceof Dhcp6RelayOption)
408 .map(BasePacket::getPayload)
409 .map(pld -> (DHCP6) pld)
410 .findFirst()
411 .orElse(null);
412
413
414 if (dhcp6Payload == null) {
415 // Can't find dhcp payload
416 log.debug("Can't find dhcp6 payload from relay message");
417 } else {
418 log.debug("dhcp6 payload found from relay message {}", dhcp6Payload);
419 }
420
421 return dhcp6Payload;
422 }
423
424 /**
425 * find the leaf DHCP6 packet from multi-level relay packet.
426 *
427 * @param relayPacket dhcp6 relay packet
428 * @return leafPacket non-relay dhcp6 packet
429 */
430 private DHCP6 getDhcp6Leaf(DHCP6 relayPacket) {
431 DHCP6 dhcp6Parent = relayPacket;
432 DHCP6 dhcp6Child = null;
433
434 log.debug("getDhcp6Leaf entered.");
435 while (dhcp6Parent != null) {
436 dhcp6Child = dhcp6PacketFromRelayPacket(dhcp6Parent);
437
438 if (dhcp6Child != null) {
439 if (dhcp6Child.getMsgType() != DHCP6.MsgType.RELAY_FORW.value() &&
440 dhcp6Child.getMsgType() != DHCP6.MsgType.RELAY_REPL.value()) {
441 log.debug("leaf dhcp6 packet found.");
442 break;
443 } else {
444 // found another relay
445 // go for another loop
446 dhcp6Parent = dhcp6Child;
447 }
448 } else {
449 log.warn("malformed pkt! Expected dhcp6 within relay pkt, but no dhcp6 leaf found.");
450 break;
451 }
452 }
453 return dhcp6Child;
454 }
455
456 /**
457 * check if DHCP6 relay-reply is reply.
458 *
459 * @param relayPacket dhcp6 relay-reply
460 * @return boolean relay-reply contains ack
461 */
462 private boolean isDhcp6Reply(DHCP6 relayPacket) {
463 log.debug("isDhcp6Reply entered.");
464
465 DHCP6 leafDhcp6 = getDhcp6Leaf(relayPacket);
466
467 if (leafDhcp6 != null) {
468 if (leafDhcp6.getMsgType() == DHCP6.MsgType.REPLY.value()) {
469 log.debug("isDhcp6Reply true.");
470 return true; // must be directly connected
471 } else {
472 log.debug("isDhcp6Reply false. leaf dhcp6 is not replay. MsgType {}", leafDhcp6.getMsgType());
473 }
474 } else {
475 log.debug("isDhcp6Reply false. Expected dhcp6 within relay pkt but not found.");
476 }
477 log.debug("isDhcp6Reply false.");
478 return false;
479 }
480
481 /**
482 * check if DHCP6 is release or relay-forward contains release.
483 *
484 * @param dhcp6Payload dhcp6 packet
485 * @return boolean dhcp6 contains release
486 */
487 private boolean isDhcp6Release(DHCP6 dhcp6Payload) {
488
489 log.debug("isDhcp6Release entered.");
490
491 if (dhcp6Payload.getMsgType() == DHCP6.MsgType.RELEASE.value()) {
492 log.debug("isDhcp6Release true.");
493 return true; // must be directly connected
494 } else {
495 DHCP6 dhcp6Leaf = getDhcp6Leaf(dhcp6Payload);
496 if (dhcp6Leaf != null) {
497 if (dhcp6Leaf.getMsgType() == DHCP6.MsgType.RELEASE.value()) {
498 log.debug("isDhcp6Release true. indirectlry connected");
499 return true;
500 } else {
501 log.debug("leaf dhcp6 is not release. MsgType {}", dhcp6Leaf.getMsgType());
502 return false;
503 }
504 } else {
505 log.debug("isDhcp6Release false. dhcp6 is niether relay nor release.");
506 return false;
507 }
508 }
509 }
510
511 /**
512 * extract from dhcp6 packet client ipv6 address of given by dhcp server.
513 *
514 * @param dhcp6 the dhcp6 packet
515 * @return Ip6Address Ip6Address given by dhcp server, or null if not exists
516 */
517 private Ip6Address extractIpAddress(DHCP6 dhcp6) {
518 Ip6Address ip = null;
519
520 log.debug("extractIpAddress enters dhcp6 {}.", dhcp6);
521 // Extract IPv6 address from IA NA ot IA TA option
522 Optional<Dhcp6IaNaOption> iaNaOption = dhcp6.getOptions()
523 .stream()
524 .filter(opt -> opt instanceof Dhcp6IaNaOption)
525 .map(opt -> (Dhcp6IaNaOption) opt)
526 .findFirst();
527 Optional<Dhcp6IaTaOption> iaTaOption = dhcp6.getOptions()
528 .stream()
529 .filter(opt -> opt instanceof Dhcp6IaTaOption)
530 .map(opt -> (Dhcp6IaTaOption) opt)
531 .findFirst();
532 Optional<Dhcp6IaAddressOption> iaAddressOption;
533 if (iaNaOption.isPresent()) {
534 log.debug("Found IPv6 address from iaNaOption {}", iaNaOption);
535
536 iaAddressOption = iaNaOption.get().getOptions().stream()
537 .filter(opt -> opt instanceof Dhcp6IaAddressOption)
538 .map(opt -> (Dhcp6IaAddressOption) opt)
539 .findFirst();
540 } else if (iaTaOption.isPresent()) {
541 log.debug("Found IPv6 address from iaTaOption {}", iaTaOption);
542
543 iaAddressOption = iaTaOption.get().getOptions().stream()
544 .filter(opt -> opt instanceof Dhcp6IaAddressOption)
545 .map(opt -> (Dhcp6IaAddressOption) opt)
546 .findFirst();
547 } else {
548 iaAddressOption = Optional.empty();
549 }
550 if (iaAddressOption.isPresent()) {
551 ip = iaAddressOption.get().getIp6Address();
552 log.debug("Found IPv6 address from iaAddressOption {}", iaAddressOption);
553
554
555 } else {
556 log.debug("Can't find IPv6 address from DHCPv6 {}", dhcp6);
557 }
558
559 return ip;
560 }
561 /**
562 * extract from dhcp6 packet Prefix prefix provided by dhcp server.
563 *
564 * @param dhcp6 the dhcp6 payload
565 * @return IpPrefix Prefix Delegation prefix, or null if not exists.
566 */
567 private IpPrefix extractPrefix(DHCP6 dhcp6) {
568 log.warn("extractPrefix enters {}", dhcp6);
569
570 // extract prefix
571 IpPrefix prefixPrefix = null;
572
573 Ip6Address prefixAddress = null;
574
575 // Extract IPv6 prefix from IA PD option
576 Optional<Dhcp6IaPdOption> iaPdOption = dhcp6.getOptions()
577 .stream()
578 .filter(opt -> opt instanceof Dhcp6IaPdOption)
579 .map(opt -> (Dhcp6IaPdOption) opt)
580 .findFirst();
581
582 Optional<Dhcp6IaPrefixOption> iaPrefixOption;
583 if (iaPdOption.isPresent()) {
584 log.warn("IA_PD option found {}", iaPdOption);
585
586 iaPrefixOption = iaPdOption.get().getOptions().stream()
587 .filter(opt -> opt instanceof Dhcp6IaPrefixOption)
588 .map(opt -> (Dhcp6IaPrefixOption) opt)
589 .findFirst();
590 } else {
591 log.warn("IA_PD option NOT found");
592
593 iaPrefixOption = Optional.empty();
594 }
595 if (iaPrefixOption.isPresent()) {
596 log.warn("IAPrefix Option within IA_PD option found {}", iaPrefixOption);
597
598 prefixAddress = iaPrefixOption.get().getIp6Prefix();
599 int prefixLen = (int) iaPrefixOption.get().getPrefixLength();
600 log.warn("Prefix length is {} bits", prefixLen);
601 prefixPrefix = IpPrefix.valueOf(prefixAddress, prefixLen);
602
603 } else {
604 log.warn("Can't find IPv6 prefix from DHCPv6 {}", dhcp6);
605 }
606
607 return prefixPrefix;
608 }
609
610 /**
611 * remove host or route.
612 *
613 * @param directConnFlag flag to show that packet is from directly connected client
614 * @param dhcp6Packet the dhcp6 payload
615 * @param clientPacket client's ethernet packet
616 * @param clientIpv6 client's Ipv6 packet
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +0000617 * @param clientInterface client interfaces
Kalhee Kim45fede42017-09-05 19:05:06 +0000618 */
619 private void removeHostOrRoute(boolean directConnFlag, DHCP6 dhcp6Packet,
620 Ethernet clientPacket, IPv6 clientIpv6,
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +0000621 Interface clientInterface) {
Kalhee Kim45fede42017-09-05 19:05:06 +0000622 log.debug("extractPrefix enters {}", dhcp6Packet);
Kalhee Kim121ba922017-11-01 17:56:44 +0000623 VlanId vlanId = clientInterface.vlan();
624 MacAddress clientMac = clientPacket.getSourceMAC();
625 log.debug("client mac {} client vlan {}", HexString.toHexString(clientMac.toBytes(), ":"), vlanId);
626
Kalhee Kim45fede42017-09-05 19:05:06 +0000627 // add host or route
628 if (isDhcp6Release(dhcp6Packet)) {
629 IpAddress ip = null;
630 if (directConnFlag) {
631 // Add to host store if it is connected to network directly
632 ip = extractIpAddress(dhcp6Packet);
633 if (ip != null) {
Kalhee Kim121ba922017-11-01 17:56:44 +0000634
Kalhee Kim45fede42017-09-05 19:05:06 +0000635 HostId hostId = HostId.hostId(clientMac, vlanId);
636 log.debug("remove Host {} ip for directly connected.", hostId.toString());
Kalhee Kim45fede42017-09-05 19:05:06 +0000637 // Remove host's ip of when dhcp release msg is received
Yi Tsengaa417a62017-09-08 17:22:51 -0700638 providerService.removeIpFromHost(hostId, ip);
Kalhee Kim45fede42017-09-05 19:05:06 +0000639 } else {
640 log.debug("ipAddress not found. Do not add Host for directly connected.");
641 }
642 } else {
643 // Remove from route store if it is not connected to network directly
Kalhee Kim121ba922017-11-01 17:56:44 +0000644 // pick out the first link-local ip address
645 IpAddress nextHopIp = getFirstIpByHost(clientMac, vlanId);
646 if (nextHopIp == null) {
647 log.warn("Can't find link-local IP address of gateway mac {} vlanId {}",
648 clientMac, vlanId);
649 return;
650 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000651
652 DHCP6 leafDhcp = getDhcp6Leaf(dhcp6Packet);
653 ip = extractIpAddress(leafDhcp);
654 if (ip == null) {
655 log.debug("ip is null");
656 } else {
657 Route routeForIP = new Route(Route.Source.STATIC, ip.toIpPrefix(), nextHopIp);
658 log.debug("removing route of 128 address for indirectly connected.");
659 log.debug("128 ip {}, nexthop {}", HexString.toHexString(ip.toOctets(), ":"),
660 HexString.toHexString(nextHopIp.toOctets(), ":"));
661 routeStore.removeRoute(routeForIP);
662 }
663
664 IpPrefix ipPrefix = extractPrefix(leafDhcp);
665 if (ipPrefix == null) {
666 log.debug("ipPrefix is null ");
667 } else {
668 Route routeForPrefix = new Route(Route.Source.STATIC, ipPrefix, nextHopIp);
669 log.debug("removing route of PD for indirectly connected.");
670 log.debug("pd ip {}, nexthop {}", HexString.toHexString(ipPrefix.address().toOctets(), ":"),
671 HexString.toHexString(nextHopIp.toOctets(), ":"));
672
673 routeStore.removeRoute(routeForPrefix);
Kalhee Kimba366062017-11-07 16:32:09 +0000674 if (this.dhcpFpmEnabled) {
675 dhcpFpmPrefixStore.removeFpmRecord(ipPrefix);
676 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000677 }
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 Kim0c0cb0b2017-09-15 17:43:27 +0000689 * @param clientInterface client interface
Kalhee Kim45fede42017-09-05 19:05:06 +0000690 */
691 private void addHostOrRoute(boolean directConnFlag, DHCP6 dhcp6Relay,
692 DHCP6 embeddedDhcp6,
693 MacAddress clientMac,
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +0000694 Interface clientInterface) {
Kalhee Kim45fede42017-09-05 19:05:06 +0000695 log.debug("addHostOrRoute entered.");
Kalhee Kim121ba922017-11-01 17:56:44 +0000696 VlanId vlanId = clientInterface.vlan();
Kalhee Kim45fede42017-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 Tsengaa417a62017-09-08 17:22:51 -0700705
706 // FIXME: we should use vlan id from original packet (solicit, request)
Kalhee Kim45fede42017-09-05 19:05:06 +0000707 HostId hostId = HostId.hostId(clientMac, vlanId);
Yi Tsengaa417a62017-09-08 17:22:51 -0700708 Host host = hostService.getHost(hostId);
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +0000709 HostLocation hostLocation = new HostLocation(clientInterface.connectPoint(),
Yi Tsengaa417a62017-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 Kim45fede42017-09-05 19:05:06 +0000718 HostDescription desc = new DefaultHostDescription(clientMac, vlanId,
Yi Tsengaa417a62017-09-08 17:22:51 -0700719 hostLocations, ips,
720 false);
Kalhee Kim45fede42017-09-05 19:05:06 +0000721 log.debug("adding Host for directly connected.");
722 log.debug("client mac {} client vlan {} hostlocation {}",
723 HexString.toHexString(clientMac.toBytes(), ":"),
724 vlanId, hostLocation.toString());
725
726 // Replace the ip when dhcp server give the host new ip address
Yi Tsengaa417a62017-09-08 17:22:51 -0700727 providerService.hostDetected(hostId, desc, false);
Kalhee Kim45fede42017-09-05 19:05:06 +0000728 } else {
729 log.warn("ipAddress not found. Do not add Host for directly connected.");
730 }
731 } else {
732 // Add to route store if it does not connect to network directly
Kalhee Kim121ba922017-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 Kim45fede42017-09-05 19:05:06 +0000740
741 DHCP6 leafDhcp = getDhcp6Leaf(embeddedDhcp6);
742 ip = extractIpAddress(leafDhcp);
743 if (ip == null) {
744 log.warn("ip is null");
745 } else {
746 Route routeForIP = new Route(Route.Source.STATIC, ip.toIpPrefix(), nextHopIp);
747 log.warn("adding Route of 128 address for indirectly connected.");
748 routeStore.updateRoute(routeForIP);
749 }
750
751 IpPrefix ipPrefix = extractPrefix(leafDhcp);
752 if (ipPrefix == null) {
753 log.warn("ipPrefix is null ");
754 } else {
755 Route routeForPrefix = new Route(Route.Source.STATIC, ipPrefix, nextHopIp);
756 log.warn("adding Route of PD for indirectly connected.");
757 routeStore.updateRoute(routeForPrefix);
Kalhee Kimba366062017-11-07 16:32:09 +0000758 if (this.dhcpFpmEnabled) {
759 FpmRecord record = new FpmRecord(ipPrefix, nextHopIp, FpmRecord.Type.DHCP_RELAY);
760 dhcpFpmPrefixStore.addFpmRecord(ipPrefix, record);
761 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000762 }
763 }
764 }
765 }
766
767 /**
Yi Tseng25bfe372017-11-03 16:27:32 -0700768 * Build the DHCP6 solicit/request packet with gatewayip.
769 * TODO: method too long, need to be refactored.
Kalhee Kim45fede42017-09-05 19:05:06 +0000770 *
771 * @param context packet context
772 * @param clientPacket client ethernet packet
773 * @param clientInterfaces set of client side interfaces
774 */
775 private InternalPacket processDhcp6PacketFromClient(PacketContext context,
Yi Tseng25bfe372017-11-03 16:27:32 -0700776 Ethernet clientPacket, Set<Interface> clientInterfaces) {
777 ConnectPoint receivedFrom = context.inPacket().receivedFrom();
778 DeviceId receivedFromDevice = receivedFrom.deviceId();
779 DhcpServerInfo serverInfo;
780 Ip6Address dhcpServerIp = null;
781 ConnectPoint dhcpServerConnectPoint = null;
782 MacAddress dhcpConnectMac = null;
783 VlanId dhcpConnectVlan = null;
784 Ip6Address dhcpGatewayIp = null;
785 Ip6Address indirectDhcpServerIp = null;
786 ConnectPoint indirectDhcpServerConnectPoint = null;
787 MacAddress indirectDhcpConnectMac = null;
788 VlanId indirectDhcpConnectVlan = null;
789 Ip6Address indirectDhcpGatewayIp = null;
790 Ip6Address indirectRelayAgentIpFromCfg = null;
791 if (!defaultServerInfoList.isEmpty()) {
792 serverInfo = defaultServerInfoList.get(0);
793 dhcpConnectMac = serverInfo.getDhcpConnectMac().orElse(null);
794 dhcpGatewayIp = serverInfo.getDhcpGatewayIp6().orElse(null);
795 dhcpServerIp = serverInfo.getDhcpServerIp6().orElse(null);
796 dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
797 dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
798 }
799 if (!indirectServerInfoList.isEmpty()) {
800 serverInfo = indirectServerInfoList.get(0);
801 indirectDhcpConnectMac = serverInfo.getDhcpConnectMac().orElse(null);
802 indirectDhcpGatewayIp = serverInfo.getDhcpGatewayIp6().orElse(null);
803 indirectDhcpServerIp = serverInfo.getDhcpServerIp6().orElse(null);
804 indirectDhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
805 indirectDhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
806 indirectRelayAgentIpFromCfg = serverInfo.getRelayAgentIp6(receivedFromDevice).orElse(null);
807 }
Kalhee Kim45b24182017-10-18 18:30:23 +0000808 Ip6Address relayAgentIp = getRelayAgentIPv6Address(clientInterfaces);
809 MacAddress relayAgentMac = clientInterfaces.iterator().next().mac();
810 if (relayAgentIp == null || relayAgentMac == null) {
811 log.warn("Missing DHCP relay agent interface Ipv6 addr config for "
Yi Tseng25bfe372017-11-03 16:27:32 -0700812 + "packet from client on port: {}. Aborting packet processing",
813 clientInterfaces.iterator().next().connectPoint());
Kalhee Kim45b24182017-10-18 18:30:23 +0000814 return null;
815 }
Kalhee Kim45b24182017-10-18 18:30:23 +0000816 // get dhcp6 header.
Kalhee Kim45b24182017-10-18 18:30:23 +0000817 IPv6 clientIpv6 = (IPv6) clientPacket.getPayload();
818 UDP clientUdp = (UDP) clientIpv6.getPayload();
819 DHCP6 clientDhcp6 = (DHCP6) clientUdp.getPayload();
Kalhee Kim45b24182017-10-18 18:30:23 +0000820 boolean directConnFlag = directlyConnected(clientDhcp6);
821 Interface serverInterface = directConnFlag ? getServerInterface() : getIndirectServerInterface();
822 if (serverInterface == null) {
823 log.warn("Can't get {} server interface, ignore", directConnFlag ? "direct" : "indirect");
824 return null;
825 }
826 Ip6Address ipFacingServer = getFirstIpFromInterface(serverInterface);
827 MacAddress macFacingServer = serverInterface.mac();
828 if (ipFacingServer == null || macFacingServer == null) {
829 log.warn("No IP v6 address for server Interface {}", serverInterface);
830 return null;
831 }
Kalhee Kim45b24182017-10-18 18:30:23 +0000832 Ethernet etherReply = clientPacket.duplicate();
833 etherReply.setSourceMACAddress(macFacingServer);
Yi Tseng25bfe372017-11-03 16:27:32 -0700834 if ((directConnFlag && dhcpConnectMac == null) ||
835 !directConnFlag && indirectDhcpConnectMac == null && dhcpConnectMac == null) {
Kalhee Kim45b24182017-10-18 18:30:23 +0000836 log.warn("Packet received from {} connected client.", directConnFlag ? "directly" : "indirectly");
Charles Chan6fa3b4f2017-10-30 13:51:22 -0700837 log.warn("DHCP6 {} not yet resolved .. Aborting DHCP packet processing from client on port: {}",
Yi Tseng25bfe372017-11-03 16:27:32 -0700838 (dhcpGatewayIp == null) ? "server IP " + dhcpServerIp
839 : "gateway IP " + dhcpGatewayIp,
840 clientInterfaces.iterator().next().connectPoint());
Kalhee Kim45b24182017-10-18 18:30:23 +0000841 return null;
842 }
Yi Tseng25bfe372017-11-03 16:27:32 -0700843 if (dhcpServerConnectPoint == null) {
Kalhee Kim45b24182017-10-18 18:30:23 +0000844 log.warn("DHCP6 server connection point direct {} directConn {} indirectConn {} is not set up yet",
Yi Tseng25bfe372017-11-03 16:27:32 -0700845 directConnFlag, dhcpServerConnectPoint, indirectDhcpServerConnectPoint);
Kalhee Kim121ba922017-11-01 17:56:44 +0000846 return null;
847 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000848
Yi Tseng25bfe372017-11-03 16:27:32 -0700849 etherReply.setDestinationMACAddress(dhcpConnectMac);
850 etherReply.setVlanID(dhcpConnectVlan.toShort());
Kalhee Kim121ba922017-11-01 17:56:44 +0000851 IPv6 ipv6Packet = (IPv6) etherReply.getPayload();
852 byte[] peerAddress = clientIpv6.getSourceAddress();
853 ipv6Packet.setSourceAddress(ipFacingServer.toOctets());
Yi Tseng25bfe372017-11-03 16:27:32 -0700854 ipv6Packet.setDestinationAddress(dhcpServerIp.toOctets());
Kalhee Kim121ba922017-11-01 17:56:44 +0000855 UDP udpPacket = (UDP) ipv6Packet.getPayload();
856 udpPacket.setSourcePort(UDP.DHCP_V6_SERVER_PORT);
857 DHCP6 dhcp6Packet = (DHCP6) udpPacket.getPayload();
858 byte[] dhcp6PacketByte = dhcp6Packet.serialize();
Charles Chana990ce92017-10-30 10:22:50 -0700859
Kalhee Kim121ba922017-11-01 17:56:44 +0000860 ConnectPoint clientConnectionPoint = context.inPacket().receivedFrom();
861 VlanId vlanIdInUse = VlanId.vlanId(clientPacket.getVlanID());
862 Interface clientInterface = interfaceService.getInterfacesByPort(clientConnectionPoint)
863 .stream().filter(iface -> interfaceContainsVlan(iface, vlanIdInUse))
864 .findFirst().orElse(null);
Kalhee Kim45fede42017-09-05 19:05:06 +0000865
Kalhee Kim121ba922017-11-01 17:56:44 +0000866 removeHostOrRoute(directConnFlag, dhcp6Packet, clientPacket, clientIpv6, clientInterface);
Kalhee Kim45b24182017-10-18 18:30:23 +0000867
Kalhee Kim121ba922017-11-01 17:56:44 +0000868 DHCP6 dhcp6Relay = new DHCP6();
869 dhcp6Relay.setMsgType(DHCP6.MsgType.RELAY_FORW.value());
Kalhee Kim121ba922017-11-01 17:56:44 +0000870 if (directConnFlag) {
871 dhcp6Relay.setLinkAddress(relayAgentIp.toOctets());
872 log.debug("direct connection: relayAgentIp obtained dynamically {}",
873 HexString.toHexString(relayAgentIp.toOctets(), ":"));
Kalhee Kim45fede42017-09-05 19:05:06 +0000874
Kalhee Kim121ba922017-11-01 17:56:44 +0000875 } else {
Yi Tseng25bfe372017-11-03 16:27:32 -0700876 if (indirectDhcpServerIp == null) {
Kalhee Kim121ba922017-11-01 17:56:44 +0000877 log.warn("indirect DhcpServerIp not available, use default DhcpServerIp {}",
Yi Tseng25bfe372017-11-03 16:27:32 -0700878 HexString.toHexString(dhcpServerIp.toOctets()));
Kalhee Kimd21029f2017-09-26 20:21:53 +0000879 } else {
880 // Indirect case, replace destination to indirect dhcp server if exist
881 // Check if mac is obtained for valid server ip
Yi Tseng25bfe372017-11-03 16:27:32 -0700882 if (indirectDhcpConnectMac == null) {
Kalhee Kimd21029f2017-09-26 20:21:53 +0000883 log.warn("DHCP6 {} not yet resolved .. Aborting DHCP "
Yi Tseng25bfe372017-11-03 16:27:32 -0700884 + "packet processing from client on port: {}",
885 (indirectDhcpGatewayIp == null) ? "server IP " + indirectDhcpServerIp
886 : "gateway IP " + indirectDhcpGatewayIp,
887 clientInterfaces.iterator().next().connectPoint());
Kalhee Kimd21029f2017-09-26 20:21:53 +0000888 return null;
889 }
Yi Tseng25bfe372017-11-03 16:27:32 -0700890 etherReply.setDestinationMACAddress(indirectDhcpConnectMac);
891 etherReply.setVlanID(indirectDhcpConnectVlan.toShort());
892 ipv6Packet.setDestinationAddress(indirectDhcpServerIp.toOctets());
Kalhee Kimd21029f2017-09-26 20:21:53 +0000893 }
Yi Tseng25bfe372017-11-03 16:27:32 -0700894 if (indirectRelayAgentIpFromCfg == null) {
Kalhee Kim45fede42017-09-05 19:05:06 +0000895 dhcp6Relay.setLinkAddress(relayAgentIp.toOctets());
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +0000896 log.warn("indirect connection: relayAgentIp NOT availale from config file! Use dynamic. {}",
Yi Tseng25bfe372017-11-03 16:27:32 -0700897 HexString.toHexString(relayAgentIp.toOctets(), ":"));
898 } else {
899 dhcp6Relay.setLinkAddress(indirectRelayAgentIpFromCfg.toOctets());
900 log.debug("indirect connection: relayAgentIp from config file is available! {}",
901 HexString.toHexString(indirectRelayAgentIpFromCfg.toOctets(), ":"));
902 }
903 }
904 // peer address: address of the client or relay agent from which
905 // the message to be relayed was received.
906 dhcp6Relay.setPeerAddress(peerAddress);
907 List<Dhcp6Option> options = new ArrayList<>();
908 // directly connected case, hop count is zero; otherwise, hop count + 1
909 if (directConnFlag) {
910 dhcp6Relay.setHopCount((byte) 0);
911 } else {
912 dhcp6Relay.setHopCount((byte) (dhcp6Packet.getHopCount() + 1));
913 }
914 // create relay message option
915 Dhcp6Option relayMessage = new Dhcp6Option();
916 relayMessage.setCode(DHCP6.OptionCode.RELAY_MSG.value());
917 relayMessage.setLength((short) dhcp6PacketByte.length);
918 relayMessage.setData(dhcp6PacketByte);
919 options.add(relayMessage);
920 // create interfaceId option
921 String inPortString = "-" + context.inPacket().receivedFrom().toString() + ":";
922 Dhcp6Option interfaceId = new Dhcp6Option();
923 interfaceId.setCode(DHCP6.OptionCode.INTERFACE_ID.value());
924 byte[] clientSoureMacBytes = clientPacket.getSourceMACAddress();
925 byte[] inPortStringBytes = inPortString.getBytes();
926 byte[] vlanIdBytes = new byte[2];
927 vlanIdBytes[0] = (byte) (clientPacket.getVlanID() & 0xff);
928 vlanIdBytes[1] = (byte) ((clientPacket.getVlanID() >> 8) & 0xff);
929 byte[] interfaceIdBytes = new byte[clientSoureMacBytes.length +
930 inPortStringBytes.length + vlanIdBytes.length];
931 log.debug("Length: interfaceIdBytes {} clientSoureMacBytes {} inPortStringBytes {} vlan {}",
932 interfaceIdBytes.length, clientSoureMacBytes.length, inPortStringBytes.length,
933 vlanIdBytes.length);
934 System.arraycopy(clientSoureMacBytes, 0, interfaceIdBytes, 0, clientSoureMacBytes.length);
935 System.arraycopy(inPortStringBytes, 0, interfaceIdBytes, clientSoureMacBytes.length, inPortStringBytes.length);
936 System.arraycopy(vlanIdBytes, 0, interfaceIdBytes, clientSoureMacBytes.length + inPortStringBytes.length,
937 vlanIdBytes.length);
938 interfaceId.setData(interfaceIdBytes);
939 interfaceId.setLength((short) interfaceIdBytes.length);
940 options.add(interfaceId);
941 log.debug("interfaceId write srcMac {} portString {}",
942 HexString.toHexString(clientSoureMacBytes, ":"), inPortString);
943 dhcp6Relay.setOptions(options);
944 udpPacket.setPayload(dhcp6Relay);
945 udpPacket.resetChecksum();
946 ipv6Packet.setPayload(udpPacket);
947 ipv6Packet.setHopLimit((byte) 64);
948 etherReply.setPayload(ipv6Packet);
949 if (directConnFlag || indirectDhcpServerIp == null) {
950 return new InternalPacket(etherReply, dhcpServerConnectPoint);
951 } else {
952 return new InternalPacket(etherReply, indirectDhcpServerConnectPoint);
953 }
954 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000955
956 /**
957 *
958 * process the DHCP6 relay-reply packet from dhcp server.
959 *
960 * @param context packet context
961 * @param receivedPacket server ethernet packet
962 * @param recevingInterfaces set of server side interfaces
963 */
964 private InternalPacket processDhcp6PacketFromServer(PacketContext context,
965 Ethernet receivedPacket, Set<Interface> recevingInterfaces) {
Yi Tseng25bfe372017-11-03 16:27:32 -0700966
967 ConnectPoint receivedFrom = context.inPacket().receivedFrom();
968 DeviceId receivedFromDevice = receivedFrom.deviceId();
969
970 // TODO: refactor
971 DhcpServerInfo serverInfo;
972 Ip6Address dhcpServerIp = null;
973 ConnectPoint dhcpServerConnectPoint = null;
974 MacAddress dhcpConnectMac = null;
975 VlanId dhcpConnectVlan = null;
976 Ip6Address dhcpGatewayIp = null;
977
978 Ip6Address indirectDhcpServerIp = null;
979 ConnectPoint indirectDhcpServerConnectPoint = null;
980 MacAddress indirectDhcpConnectMac = null;
981 VlanId indirectDhcpConnectVlan = null;
982 Ip6Address indirectDhcpGatewayIp = null;
983 Ip6Address indirectRelayAgentIpFromCfg = null;
984
985 if (!defaultServerInfoList.isEmpty()) {
986 serverInfo = defaultServerInfoList.get(0);
987 dhcpConnectMac = serverInfo.getDhcpConnectMac().orElse(null);
988 dhcpGatewayIp = serverInfo.getDhcpGatewayIp6().orElse(null);
989 dhcpServerIp = serverInfo.getDhcpServerIp6().orElse(null);
990 dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
991 dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
992 }
993
994 if (!indirectServerInfoList.isEmpty()) {
995 serverInfo = indirectServerInfoList.get(0);
996 indirectDhcpConnectMac = serverInfo.getDhcpConnectMac().orElse(null);
997 indirectDhcpGatewayIp = serverInfo.getDhcpGatewayIp6().orElse(null);
998 indirectDhcpServerIp = serverInfo.getDhcpServerIp6().orElse(null);
999 indirectDhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1000 indirectDhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1001 indirectRelayAgentIpFromCfg = serverInfo.getRelayAgentIp6(receivedFromDevice).orElse(null);
1002 }
1003
Kalhee Kim45fede42017-09-05 19:05:06 +00001004 // get dhcp6 header.
Ray Milkeyf0c47612017-09-28 11:29:38 -07001005 Ethernet etherReply = receivedPacket.duplicate();
Kalhee Kim45fede42017-09-05 19:05:06 +00001006 IPv6 ipv6Packet = (IPv6) etherReply.getPayload();
1007 UDP udpPacket = (UDP) ipv6Packet.getPayload();
1008 DHCP6 dhcp6Relay = (DHCP6) udpPacket.getPayload();
1009
1010 Boolean directConnFlag = directlyConnected(dhcp6Relay);
Kalhee Kimd21029f2017-09-26 20:21:53 +00001011 ConnectPoint inPort = context.inPacket().receivedFrom();
Yi Tseng25bfe372017-11-03 16:27:32 -07001012 if ((directConnFlag || (!directConnFlag && indirectDhcpServerIp == null))
1013 && !inPort.equals(dhcpServerConnectPoint)) {
Kalhee Kimd21029f2017-09-26 20:21:53 +00001014 log.warn("Receiving port {} is not the same as server connect point {} for direct or indirect-null",
Yi Tseng25bfe372017-11-03 16:27:32 -07001015 inPort, dhcpServerConnectPoint);
Kalhee Kimd21029f2017-09-26 20:21:53 +00001016 return null;
1017 }
1018
Yi Tseng25bfe372017-11-03 16:27:32 -07001019 if (!directConnFlag && indirectDhcpServerIp != null &&
1020 !inPort.equals(indirectDhcpServerConnectPoint)) {
Kalhee Kimd21029f2017-09-26 20:21:53 +00001021 log.warn("Receiving port {} is not the same as server connect point {} for indirect",
Yi Tseng25bfe372017-11-03 16:27:32 -07001022 inPort, indirectDhcpServerConnectPoint);
Kalhee Kimd21029f2017-09-26 20:21:53 +00001023 return null;
1024 }
1025
Kalhee Kim45fede42017-09-05 19:05:06 +00001026
1027 Dhcp6InterfaceIdOption interfaceIdOption = dhcp6Relay.getOptions().stream()
1028 .filter(opt -> opt instanceof Dhcp6InterfaceIdOption)
1029 .map(opt -> (Dhcp6InterfaceIdOption) opt)
1030 .findFirst()
1031 .orElse(null);
1032
1033 if (interfaceIdOption == null) {
1034 log.warn("Interface Id option is not present, abort packet...");
1035 return null;
1036 }
1037
1038 MacAddress peerMac = interfaceIdOption.getMacAddress();
1039 String clientConnectionPointStr = new String(interfaceIdOption.getInPort());
1040
1041 ConnectPoint clientConnectionPoint = ConnectPoint.deviceConnectPoint(clientConnectionPointStr);
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +00001042 VlanId vlanIdInUse = VlanId.vlanId(interfaceIdOption.getVlanId());
1043 Interface clientInterface = interfaceService.getInterfacesByPort(clientConnectionPoint)
1044 .stream()
1045 .filter(iface -> interfaceContainsVlan(iface, vlanIdInUse))
1046 .findFirst()
1047 .orElse(null);
1048 if (clientInterface == null) {
1049 log.warn("Cannot get client interface for from packet, abort... vlan {}", vlanIdInUse.toString());
Kalhee Kim45fede42017-09-05 19:05:06 +00001050 return null;
1051 }
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +00001052 MacAddress relayAgentMac = clientInterface.mac();
Kalhee Kim45fede42017-09-05 19:05:06 +00001053 if (relayAgentMac == null) {
1054 log.warn("Can not get interface mac, abort packet..");
1055 return null;
1056 }
1057 etherReply.setSourceMACAddress(relayAgentMac);
1058
1059 // find destMac
1060 MacAddress clientMac = null;
Yi Tsengaa417a62017-09-08 17:22:51 -07001061 Ip6Address peerAddress = Ip6Address.valueOf(dhcp6Relay.getPeerAddress());
1062 Set<Host> clients = hostService.getHostsByIp(peerAddress);
Kalhee Kim45fede42017-09-05 19:05:06 +00001063 if (clients.isEmpty()) {
1064 log.warn("There's no host found for this address {}",
1065 HexString.toHexString(dhcp6Relay.getPeerAddress(), ":"));
1066 log.warn("Let's look up interfaceId {}", HexString.toHexString(peerMac.toBytes(), ":"));
1067 clientMac = peerMac;
1068 } else {
1069 clientMac = clients.iterator().next().mac();
1070 if (clientMac == null) {
1071 log.warn("No client mac address found, abort packet...");
1072 return null;
1073 }
1074 log.warn("Client mac address found from getHostByIp");
1075
1076 }
1077 etherReply.setDestinationMACAddress(clientMac);
1078
1079 // ip header
1080 ipv6Packet.setSourceAddress(dhcp6Relay.getLinkAddress());
1081 ipv6Packet.setDestinationAddress(dhcp6Relay.getPeerAddress());
1082 // udp header
1083 udpPacket.setSourcePort(UDP.DHCP_V6_SERVER_PORT);
1084 if (directConnFlag) {
1085 udpPacket.setDestinationPort(UDP.DHCP_V6_CLIENT_PORT);
1086 } else {
1087 udpPacket.setDestinationPort(UDP.DHCP_V6_SERVER_PORT);
1088 }
1089
1090 DHCP6 embeddedDhcp6 = dhcp6Relay.getOptions().stream()
1091 .filter(opt -> opt instanceof Dhcp6RelayOption)
1092 .map(BasePacket::getPayload)
1093 .map(pld -> (DHCP6) pld)
1094 .findFirst()
1095 .orElse(null);
1096
1097
1098 // add host or route
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +00001099 addHostOrRoute(directConnFlag, dhcp6Relay, embeddedDhcp6, clientMac, clientInterface);
Kalhee Kim45fede42017-09-05 19:05:06 +00001100
1101 udpPacket.setPayload(embeddedDhcp6);
1102 udpPacket.resetChecksum();
1103 ipv6Packet.setPayload(udpPacket);
1104 etherReply.setPayload(ipv6Packet);
1105
1106 return new InternalPacket(etherReply, clientConnectionPoint);
1107 }
1108
Yi Tseng919b2df2017-09-07 16:22:51 -07001109 // Returns the first v6 interface ip out of a set of interfaces or null.
Kalhee Kim45fede42017-09-05 19:05:06 +00001110 // Checks all interfaces, and ignores v6 interface ips
1111 private Ip6Address getRelayAgentIPv6Address(Set<Interface> intfs) {
1112 for (Interface intf : intfs) {
1113 for (InterfaceIpAddress ip : intf.ipAddressesList()) {
1114 Ip6Address relayAgentIp = ip.ipAddress().getIp6Address();
1115 if (relayAgentIp != null) {
1116 return relayAgentIp;
1117 }
1118 }
1119 }
1120 return null;
Yi Tseng51301292017-07-28 13:02:59 -07001121 }
Yi Tseng483ac6f2017-08-02 15:03:31 -07001122
1123 @Override
Kalhee Kimba366062017-11-07 16:32:09 +00001124 public void setDhcpFpmEnabled(Boolean enabled) {
1125 dhcpFpmEnabled = enabled;
1126 }
1127
1128 @Override
Yi Tseng483ac6f2017-08-02 15:03:31 -07001129 public void setDefaultDhcpServerConfigs(Collection<DhcpServerConfig> configs) {
Yi Tseng919b2df2017-09-07 16:22:51 -07001130 setDhcpServerConfigs(configs, defaultServerInfoList);
Yi Tseng919b2df2017-09-07 16:22:51 -07001131 }
1132
1133 @Override
1134 public void setIndirectDhcpServerConfigs(Collection<DhcpServerConfig> configs) {
1135 setDhcpServerConfigs(configs, indirectServerInfoList);
Yi Tseng919b2df2017-09-07 16:22:51 -07001136 }
1137
1138 public void setDhcpServerConfigs(Collection<DhcpServerConfig> configs, List<DhcpServerInfo> serverInfoList) {
Kalhee Kim45fede42017-09-05 19:05:06 +00001139 if (configs.size() == 0) {
1140 // no config to update
1141 return;
1142 }
1143
1144 // TODO: currently we pick up first DHCP server config.
1145 // Will use other server configs in the future for HA.
1146 DhcpServerConfig serverConfig = configs.iterator().next();
Yi Tseng919b2df2017-09-07 16:22:51 -07001147
Kalhee Kim45fede42017-09-05 19:05:06 +00001148 if (!serverConfig.getDhcpServerIp6().isPresent()) {
Yi Tseng919b2df2017-09-07 16:22:51 -07001149 // not a DHCPv6 config
Kalhee Kim45fede42017-09-05 19:05:06 +00001150 return;
1151 }
1152
Yi Tseng919b2df2017-09-07 16:22:51 -07001153 if (!serverInfoList.isEmpty()) {
1154 // remove old server info
1155 DhcpServerInfo oldServerInfo = serverInfoList.remove(0);
1156
1157 // stop monitoring gateway or server
1158 oldServerInfo.getDhcpGatewayIp6().ifPresent(gatewayIp -> {
1159 hostService.stopMonitoringIp(gatewayIp);
1160 });
1161 oldServerInfo.getDhcpServerIp6().ifPresent(serverIp -> {
1162 hostService.stopMonitoringIp(serverIp);
Yi Tseng525ff402017-10-23 19:39:39 -07001163 cancelDhcpPacket(serverIp);
Yi Tseng919b2df2017-09-07 16:22:51 -07001164 });
1165 }
1166
1167 // Create new server info according to the config
1168 DhcpServerInfo newServerInfo = new DhcpServerInfo(serverConfig,
1169 DhcpServerInfo.Version.DHCP_V6);
1170 checkState(newServerInfo.getDhcpServerConnectPoint().isPresent(),
1171 "Connect point not exists");
1172 checkState(newServerInfo.getDhcpServerIp6().isPresent(),
1173 "IP of DHCP server not exists");
1174
1175 log.debug("DHCP server connect point: {}", newServerInfo.getDhcpServerConnectPoint().orElse(null));
1176 log.debug("DHCP server IP: {}", newServerInfo.getDhcpServerIp6().orElse(null));
1177
Yi Tseng525ff402017-10-23 19:39:39 -07001178 Ip6Address serverIp = newServerInfo.getDhcpServerIp6().get();
1179 Ip6Address ipToProbe;
Yi Tseng919b2df2017-09-07 16:22:51 -07001180 if (newServerInfo.getDhcpGatewayIp6().isPresent()) {
1181 ipToProbe = newServerInfo.getDhcpGatewayIp6().get();
1182 } else {
1183 ipToProbe = newServerInfo.getDhcpServerIp6().orElse(null);
1184 }
1185 String hostToProbe = newServerInfo.getDhcpGatewayIp6()
1186 .map(ip -> "gateway").orElse("server");
1187
1188 log.debug("Probing to resolve {} IP {}", hostToProbe, ipToProbe);
Kalhee Kim45fede42017-09-05 19:05:06 +00001189 hostService.startMonitoringIp(ipToProbe);
1190
1191 Set<Host> hosts = hostService.getHostsByIp(ipToProbe);
1192 if (!hosts.isEmpty()) {
1193 Host host = hosts.iterator().next();
Yi Tseng919b2df2017-09-07 16:22:51 -07001194 newServerInfo.setDhcpConnectVlan(host.vlan());
1195 newServerInfo.setDhcpConnectMac(host.mac());
Kalhee Kim45fede42017-09-05 19:05:06 +00001196 }
Yi Tseng919b2df2017-09-07 16:22:51 -07001197 // Add new server info
Yi Tseng48cd0862017-11-09 13:54:12 -08001198 synchronized (this) {
1199 serverInfoList.clear();
1200 serverInfoList.add(0, newServerInfo);
1201 }
Yi Tseng525ff402017-10-23 19:39:39 -07001202 requestDhcpPacket(serverIp);
Kalhee Kim45fede42017-09-05 19:05:06 +00001203 }
1204
1205 class InternalHostListener implements HostListener {
1206 @Override
1207 public void event(HostEvent event) {
1208 switch (event.type()) {
1209 case HOST_ADDED:
1210 case HOST_UPDATED:
1211 hostUpdated(event.subject());
1212 break;
1213 case HOST_REMOVED:
1214 hostRemoved(event.subject());
1215 break;
Kalhee Kim45fede42017-09-05 19:05:06 +00001216 default:
1217 break;
1218 }
1219 }
1220 }
1221
1222 /**
Kalhee Kim45fede42017-09-05 19:05:06 +00001223 * Handle host updated.
1224 * If the host is DHCP server or gateway, update connect mac and vlan.
1225 *
1226 * @param host the host
1227 */
1228 private void hostUpdated(Host host) {
Yi Tseng525ff402017-10-23 19:39:39 -07001229 hostUpdated(host, defaultServerInfoList);
1230 hostUpdated(host, indirectServerInfoList);
Kalhee Kim45fede42017-09-05 19:05:06 +00001231 }
1232
Yi Tseng525ff402017-10-23 19:39:39 -07001233 private void hostUpdated(Host host, List<DhcpServerInfo> serverInfoList) {
1234 DhcpServerInfo serverInfo;
1235 Ip6Address targetIp;
1236 if (!serverInfoList.isEmpty()) {
1237 serverInfo = serverInfoList.get(0);
1238 Ip6Address serverIp = serverInfo.getDhcpServerIp6().orElse(null);
1239 targetIp = serverInfo.getDhcpGatewayIp6().orElse(null);
1240
1241 if (targetIp == null) {
1242 targetIp = serverIp;
1243 }
1244
1245 if (targetIp != null) {
1246 if (host.ipAddresses().contains(targetIp)) {
1247 serverInfo.setDhcpConnectMac(host.mac());
1248 serverInfo.setDhcpConnectVlan(host.vlan());
1249 requestDhcpPacket(serverIp);
1250 }
1251 }
1252 }
1253 }
1254
Kalhee Kim45fede42017-09-05 19:05:06 +00001255 /**
1256 * Handle host removed.
1257 * If the host is DHCP server or gateway, unset connect mac and vlan.
1258 *
1259 * @param host the host
1260 */
1261 private void hostRemoved(Host host) {
Yi Tseng525ff402017-10-23 19:39:39 -07001262 hostRemoved(host, defaultServerInfoList);
1263 hostRemoved(host, indirectServerInfoList);
Yi Tseng919b2df2017-09-07 16:22:51 -07001264 }
1265
Yi Tseng525ff402017-10-23 19:39:39 -07001266 private void hostRemoved(Host host, List<DhcpServerInfo> serverInfoList) {
1267 DhcpServerInfo serverInfo;
1268 Ip6Address targetIp;
1269
1270 if (!serverInfoList.isEmpty()) {
1271 serverInfo = serverInfoList.get(0);
1272 Ip6Address serverIp = serverInfo.getDhcpServerIp6().orElse(null);
1273 targetIp = serverInfo.getDhcpGatewayIp6().orElse(null);
1274
1275 if (targetIp == null) {
1276 targetIp = serverIp;
1277 }
1278
1279 if (targetIp != null) {
1280 if (host.ipAddresses().contains(targetIp)) {
1281 serverInfo.setDhcpConnectVlan(null);
1282 serverInfo.setDhcpConnectMac(null);
1283 cancelDhcpPacket(serverIp);
1284 }
1285 }
1286 }
1287 }
1288
Kalhee Kim45b24182017-10-18 18:30:23 +00001289 /**
1290 * Returns the first interface ip from interface.
1291 *
1292 * @param iface interface of one connect point
1293 * @return the first interface IP; null if not exists an IP address in
1294 * these interfaces
1295 */
1296 private Ip6Address getFirstIpFromInterface(Interface iface) {
1297 checkNotNull(iface, "Interface can't be null");
1298 return iface.ipAddressesList().stream()
1299 .map(InterfaceIpAddress::ipAddress)
1300 .filter(IpAddress::isIp6)
1301 .map(IpAddress::getIp6Address)
1302 .findFirst()
1303 .orElse(null);
1304 }
1305
1306 /**
1307 * Gets Interface facing to the server for default host.
1308 *
1309 * @return the Interface facing to the server; null if not found
1310 */
1311 private Interface getServerInterface() {
Yi Tseng25bfe372017-11-03 16:27:32 -07001312 DhcpServerInfo serverInfo;
1313 ConnectPoint dhcpServerConnectPoint;
1314 VlanId dhcpConnectVlan;
1315
1316 if (!defaultServerInfoList.isEmpty()) {
1317 serverInfo = defaultServerInfoList.get(0);
1318 dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1319 dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1320 } else {
Kalhee Kim45b24182017-10-18 18:30:23 +00001321 return null;
1322 }
Yi Tseng921e5b02017-11-17 17:14:41 -08001323 if (dhcpServerConnectPoint == null || dhcpConnectVlan == null) {
1324 log.info("Default DHCP server {} not resolve yet", serverInfo.getDhcpGatewayIp6());
1325 return null;
1326 }
Kalhee Kim45b24182017-10-18 18:30:23 +00001327 return interfaceService.getInterfacesByPort(dhcpServerConnectPoint)
1328 .stream()
1329 .filter(iface -> interfaceContainsVlan(iface, dhcpConnectVlan))
1330 .findFirst()
1331 .orElse(null);
1332 }
1333
1334 /**
1335 * Gets Interface facing to the server for indirect hosts.
1336 * Use default server Interface if indirect server not configured.
1337 *
1338 * @return the Interface facing to the server; null if not found
1339 */
1340 private Interface getIndirectServerInterface() {
Yi Tseng25bfe372017-11-03 16:27:32 -07001341 DhcpServerInfo serverInfo;
1342
1343 ConnectPoint indirectDhcpServerConnectPoint;
1344 VlanId indirectDhcpConnectVlan;
1345
1346 if (!indirectServerInfoList.isEmpty()) {
1347 serverInfo = indirectServerInfoList.get(0);
1348 indirectDhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1349 indirectDhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1350 } else {
Kalhee Kim45b24182017-10-18 18:30:23 +00001351 return getServerInterface();
1352 }
Yi Tseng921e5b02017-11-17 17:14:41 -08001353 if (indirectDhcpServerConnectPoint == null || indirectDhcpConnectVlan == null) {
1354 log.info("Indirect DHCP server {} not resolve yet", serverInfo.getDhcpGatewayIp6());
1355 return null;
1356 }
Kalhee Kim45b24182017-10-18 18:30:23 +00001357 return interfaceService.getInterfacesByPort(indirectDhcpServerConnectPoint)
1358 .stream()
1359 .filter(iface -> interfaceContainsVlan(iface, indirectDhcpConnectVlan))
1360 .findFirst()
1361 .orElse(null);
1362 }
1363
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +00001364 /**
1365 * Determind if an Interface contains a vlan id.
1366 *
1367 * @param iface the Interface
1368 * @param vlanId the vlan id
1369 * @return true if the Interface contains the vlan id
1370 */
1371 private boolean interfaceContainsVlan(Interface iface, VlanId vlanId) {
1372 if (vlanId.equals(VlanId.NONE)) {
1373 // untagged packet, check if vlan untagged or vlan native is not NONE
1374 return !iface.vlanUntagged().equals(VlanId.NONE) ||
1375 !iface.vlanNative().equals(VlanId.NONE);
1376 }
1377 // tagged packet, check if the interface contains the vlan
1378 return iface.vlanTagged().contains(vlanId);
1379 }
1380
Yi Tseng525ff402017-10-23 19:39:39 -07001381 private void requestDhcpPacket(Ip6Address serverIp) {
1382 requestServerDhcpPacket(serverIp);
1383 requestClientDhcpPacket(serverIp);
1384 }
1385
1386 private void cancelDhcpPacket(Ip6Address serverIp) {
1387 cancelServerDhcpPacket(serverIp);
1388 cancelClientDhcpPacket(serverIp);
1389 }
1390
1391 private void cancelServerDhcpPacket(Ip6Address serverIp) {
1392 TrafficSelector serverSelector =
1393 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1394 .matchIPv6Src(serverIp.toIpPrefix())
1395 .build();
1396 packetService.cancelPackets(serverSelector,
1397 PacketPriority.CONTROL,
1398 appId);
1399 }
1400
1401 private void requestServerDhcpPacket(Ip6Address serverIp) {
1402 TrafficSelector serverSelector =
1403 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1404 .matchIPv6Src(serverIp.toIpPrefix())
1405 .build();
1406 packetService.requestPackets(serverSelector,
1407 PacketPriority.CONTROL,
1408 appId);
1409 }
1410
1411 private void cancelClientDhcpPacket(Ip6Address serverIp) {
1412 // Packet comes from relay
1413 TrafficSelector indirectClientSelector =
1414 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1415 .matchIPv6Dst(serverIp.toIpPrefix())
1416 .build();
1417 packetService.cancelPackets(indirectClientSelector,
1418 PacketPriority.CONTROL,
1419 appId);
Yi Tseng41dde702017-11-02 15:21:10 -07001420 indirectClientSelector =
1421 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1422 .matchIPv6Dst(Ip6Address.ALL_DHCP_RELAY_AGENTS_AND_SERVERS.toIpPrefix())
1423 .build();
1424 packetService.cancelPackets(indirectClientSelector,
1425 PacketPriority.CONTROL,
1426 appId);
1427 indirectClientSelector =
1428 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1429 .matchIPv6Dst(Ip6Address.ALL_DHCP_SERVERS.toIpPrefix())
1430 .build();
1431 packetService.cancelPackets(indirectClientSelector,
1432 PacketPriority.CONTROL,
1433 appId);
Yi Tseng525ff402017-10-23 19:39:39 -07001434
1435 // Packet comes from client
1436 packetService.cancelPackets(CLIENT_SERVER_SELECTOR,
1437 PacketPriority.CONTROL,
1438 appId);
1439 }
1440
1441 private void requestClientDhcpPacket(Ip6Address serverIp) {
1442 // Packet comes from relay
1443 TrafficSelector indirectClientSelector =
1444 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1445 .matchIPv6Dst(serverIp.toIpPrefix())
1446 .build();
1447 packetService.requestPackets(indirectClientSelector,
1448 PacketPriority.CONTROL,
1449 appId);
Yi Tseng41dde702017-11-02 15:21:10 -07001450 indirectClientSelector =
1451 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1452 .matchIPv6Dst(Ip6Address.ALL_DHCP_RELAY_AGENTS_AND_SERVERS.toIpPrefix())
1453 .build();
1454 packetService.requestPackets(indirectClientSelector,
1455 PacketPriority.CONTROL,
1456 appId);
1457 indirectClientSelector =
1458 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1459 .matchIPv6Dst(Ip6Address.ALL_DHCP_SERVERS.toIpPrefix())
1460 .build();
1461 packetService.requestPackets(indirectClientSelector,
1462 PacketPriority.CONTROL,
1463 appId);
Yi Tseng525ff402017-10-23 19:39:39 -07001464
1465 // Packet comes from client
1466 packetService.requestPackets(CLIENT_SERVER_SELECTOR,
1467 PacketPriority.CONTROL,
1468 appId);
1469 }
1470
1471 /**
1472 * Process the ignore rules.
1473 *
1474 * @param deviceId the device id
1475 * @param vlanId the vlan to be ignored
1476 * @param op the operation, ADD to install; REMOVE to uninstall rules
1477 */
1478 private void processIgnoreVlanRule(DeviceId deviceId, VlanId vlanId, Objective.Operation op) {
Yi Tseng525ff402017-10-23 19:39:39 -07001479 AtomicInteger installedCount = new AtomicInteger(DHCP_SELECTORS.size());
1480 DHCP_SELECTORS.forEach(trafficSelector -> {
1481 TrafficSelector selector = DefaultTrafficSelector.builder(trafficSelector)
1482 .matchVlanId(vlanId)
1483 .build();
1484
1485 ForwardingObjective.Builder builder = DefaultForwardingObjective.builder()
1486 .withFlag(ForwardingObjective.Flag.VERSATILE)
1487 .withSelector(selector)
1488 .withPriority(IGNORE_CONTROL_PRIORITY)
Yi Tsengdbabeed2017-11-29 10:49:20 -08001489 .withTreatment(DefaultTrafficTreatment.emptyTreatment())
Yi Tseng525ff402017-10-23 19:39:39 -07001490 .fromApp(appId);
1491
1492
1493 ObjectiveContext objectiveContext = new ObjectiveContext() {
1494 @Override
1495 public void onSuccess(Objective objective) {
1496 log.info("Ignore rule {} (Vlan id {}, device {}, selector {})",
1497 op, vlanId, deviceId, selector);
1498 int countDown = installedCount.decrementAndGet();
1499 if (countDown != 0) {
1500 return;
1501 }
1502 switch (op) {
1503 case ADD:
1504 ignoredVlans.put(deviceId, vlanId);
1505 break;
1506 case REMOVE:
1507 ignoredVlans.remove(deviceId, vlanId);
1508 break;
1509 default:
1510 log.warn("Unsupported objective operation {}", op);
1511 break;
1512 }
1513 }
1514
1515 @Override
1516 public void onError(Objective objective, ObjectiveError error) {
1517 log.warn("Can't {} ignore rule (vlan id {}, selector {}, device {}) due to {}",
1518 op, vlanId, selector, deviceId, error);
1519 }
1520 };
1521
1522 ForwardingObjective fwd;
1523 switch (op) {
1524 case ADD:
1525 fwd = builder.add(objectiveContext);
1526 break;
1527 case REMOVE:
1528 fwd = builder.remove(objectiveContext);
1529 break;
1530 default:
1531 log.warn("Unsupported objective operation {}", op);
1532 return;
1533 }
1534
1535 Device device = deviceService.getDevice(deviceId);
1536 if (device == null || !device.is(Pipeliner.class)) {
1537 log.warn("Device {} is not available now, wait until device is available", deviceId);
1538 return;
1539 }
1540 flowObjectiveService.apply(deviceId, fwd);
1541 });
1542 }
Kalhee Kim121ba922017-11-01 17:56:44 +00001543
1544 /**
1545 * Find first ipaddress for a given Host info i.e. mac and vlan.
1546 *
1547 * @param clientMac client mac
1548 * @param vlanId packet's vlan
1549 * @return next-hop link-local ipaddress for a given host
1550 */
1551 private IpAddress getFirstIpByHost(MacAddress clientMac, VlanId vlanId) {
1552 IpAddress nextHopIp;
1553 // pick out the first link-local ip address
1554 HostId gwHostId = HostId.hostId(clientMac, vlanId);
1555 Host gwHost = hostService.getHost(gwHostId);
1556 if (gwHost == null) {
1557 log.warn("Can't find gateway host for hostId {}", gwHostId);
1558 return null;
1559 }
1560 nextHopIp = gwHost.ipAddresses()
1561 .stream()
1562 .filter(IpAddress::isIp6)
1563 .filter(ip6 -> ip6.isLinkLocal())
1564 .map(IpAddress::getIp6Address)
1565 .findFirst()
1566 .orElse(null);
1567 return nextHopIp;
1568 }
Yi Tseng51301292017-07-28 13:02:59 -07001569}