blob: 087a5c94b033a3879f2ba15fde2209185bb1f30d [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
Saurav Dasb805f1a2017-12-13 16:19:35 -0800260 public void removeIgnoreVlanState(IgnoreDhcpConfig config) {
261 if (config == null) {
262 ignoredVlans.clear();
263 return;
264 }
265 config.ignoredVlans().forEach((deviceId, vlanId) -> {
266 ignoredVlans.remove(deviceId, vlanId);
267 });
268 }
269
270 @Override
Kalhee Kim45fede42017-09-05 19:05:06 +0000271 public void processDhcpPacket(PacketContext context, BasePacket payload) {
272 checkNotNull(payload, "DHCP6 payload can't be null");
273 checkState(payload instanceof DHCP6, "Payload is not a DHCP6");
274 DHCP6 dhcp6Payload = (DHCP6) payload;
275 Ethernet receivedPacket = context.inPacket().parsed();
276
277 if (!configured()) {
278 log.warn("Missing DHCP6 relay server config. Abort packet processing");
279 log.warn("dhcp6 payload {}", dhcp6Payload);
280
281 return;
282 }
283
284 byte msgType = dhcp6Payload.getMsgType();
Kalhee Kim45fede42017-09-05 19:05:06 +0000285
286 ConnectPoint inPort = context.inPacket().receivedFrom();
Jonathan Hart8ca2bc02017-11-30 18:23:42 -0800287
Kalhee Kim45fede42017-09-05 19:05:06 +0000288 Set<Interface> receivingInterfaces = interfaceService.getInterfacesByPort(inPort);
289 //ignore the packets if dhcp client interface is not configured on onos.
290 if (receivingInterfaces.isEmpty()) {
291 log.warn("Virtual interface is not configured on {}", inPort);
292 return;
293 }
294
295
296 if (MSG_TYPE_FROM_CLIENT.contains(msgType)) {
297
298 InternalPacket ethernetClientPacket =
299 processDhcp6PacketFromClient(context, receivedPacket, receivingInterfaces);
300 if (ethernetClientPacket != null) {
301 forwardPacket(ethernetClientPacket);
302 }
303
304 } else if (MSG_TYPE_FROM_SERVER.contains(msgType)) {
305 log.warn("calling processDhcp6PacketFromServer with RELAY_REPL", msgType);
306 InternalPacket ethernetPacketReply =
307 processDhcp6PacketFromServer(context, receivedPacket, receivingInterfaces);
308 if (ethernetPacketReply != null) {
309 forwardPacket(ethernetPacketReply);
310 }
311 } else {
312 log.warn("Not so fast, packet type {} not supported yet", msgType);
313 }
314 }
315
316
317 /**
318 * Checks if this app has been configured.
319 *
320 * @return true if all information we need have been initialized
321 */
322 public boolean configured() {
Yi Tseng919b2df2017-09-07 16:22:51 -0700323 return !defaultServerInfoList.isEmpty();
Kalhee Kim45fede42017-09-05 19:05:06 +0000324 }
325
Yi Tsengaa417a62017-09-08 17:22:51 -0700326 @Override
327 public ProviderId id() {
Charles Chand988c282017-09-12 17:09:32 -0700328 return PROVIDER_ID;
Yi Tsengaa417a62017-09-08 17:22:51 -0700329 }
330
331 @Override
332 public void triggerProbe(Host host) {
333 // Do nothing here
334 }
335
Kalhee Kim45fede42017-09-05 19:05:06 +0000336 // the new class the contains Ethernet packet and destination port, kind of like adding
337 // internal header to the packet
338 private class InternalPacket {
339 Ethernet packet;
340 ConnectPoint destLocation;
341 public InternalPacket(Ethernet newPacket, ConnectPoint newLocation) {
342 packet = newPacket;
343 destLocation = newLocation;
344 }
345 void setLocation(ConnectPoint newLocation) {
346 destLocation = newLocation;
347 }
348 }
349
350 //forward the packet to ConnectPoint where the DHCP server is attached.
351 private void forwardPacket(InternalPacket packet) {
352 //send Packetout to dhcp server connectpoint.
353 if (packet.destLocation != null) {
354 TrafficTreatment t = DefaultTrafficTreatment.builder()
355 .setOutput(packet.destLocation.port()).build();
356 OutboundPacket o = new DefaultOutboundPacket(
357 packet.destLocation.deviceId(), t, ByteBuffer.wrap(packet.packet.serialize()));
358 if (log.isTraceEnabled()) {
359 log.trace("Relaying packet to destination {}", packet.destLocation);
360 }
361 packetService.emit(o);
362 } // if
363 }
364
365 /**
366 * Check if the host is directly connected to the network or not.
367 *
368 * @param dhcp6Payload the dhcp6 payload
369 * @return true if the host is directly connected to the network; false otherwise
370 */
371 private boolean directlyConnected(DHCP6 dhcp6Payload) {
372 log.debug("directlyConnected enters");
373
374 if (dhcp6Payload.getMsgType() != DHCP6.MsgType.RELAY_FORW.value() &&
375 dhcp6Payload.getMsgType() != DHCP6.MsgType.RELAY_REPL.value()) {
376 log.debug("directlyConnected true. MsgType {}", dhcp6Payload.getMsgType());
377
378 return true;
379 }
380
381 // Regardless of relay-forward or relay-replay, check if we see another relay message
382 DHCP6 dhcp6Payload2 = dhcp6PacketFromRelayPacket(dhcp6Payload);
383 if (dhcp6Payload2 != null) {
384 if (dhcp6Payload.getMsgType() == DHCP6.MsgType.RELAY_FORW.value()) {
385 log.debug("directlyConnected false. 1st realy-foward, 2nd MsgType {}", dhcp6Payload2.getMsgType());
386 return false;
387 } else {
388 // relay-reply
389 if (dhcp6Payload2.getMsgType() != DHCP6.MsgType.RELAY_REPL.value()) {
390 log.debug("directlyConnected true. 2nd MsgType {}", dhcp6Payload2.getMsgType());
391 return true; // must be directly connected
392 } else {
393 log.debug("directlyConnected false. 1st relay-reply, 2nd relay-reply MsgType {}",
394 dhcp6Payload2.getMsgType());
395 return false; // must be indirectly connected
396 }
397 }
398 } else {
399 log.warn("directlyConnected true.");
400 return true;
401 }
402 }
403
404 /**
405 * extract DHCP6 payload from dhcp6 relay message within relay-forwrd/reply.
406 *
407 * @param dhcp6 dhcp6 relay-reply or relay-foward
408 * @return dhcp6Packet dhcp6 packet extracted from relay-message
409 */
410 private DHCP6 dhcp6PacketFromRelayPacket(DHCP6 dhcp6) {
411 log.debug("dhcp6PacketFromRelayPacket enters. dhcp6 {}", dhcp6);
412
413 // extract the relay message if exist
414 DHCP6 dhcp6Payload = dhcp6.getOptions().stream()
415 .filter(opt -> opt instanceof Dhcp6RelayOption)
416 .map(BasePacket::getPayload)
417 .map(pld -> (DHCP6) pld)
418 .findFirst()
419 .orElse(null);
420
421
422 if (dhcp6Payload == null) {
423 // Can't find dhcp payload
424 log.debug("Can't find dhcp6 payload from relay message");
425 } else {
426 log.debug("dhcp6 payload found from relay message {}", dhcp6Payload);
427 }
428
429 return dhcp6Payload;
430 }
431
432 /**
433 * find the leaf DHCP6 packet from multi-level relay packet.
434 *
435 * @param relayPacket dhcp6 relay packet
436 * @return leafPacket non-relay dhcp6 packet
437 */
438 private DHCP6 getDhcp6Leaf(DHCP6 relayPacket) {
439 DHCP6 dhcp6Parent = relayPacket;
440 DHCP6 dhcp6Child = null;
441
442 log.debug("getDhcp6Leaf entered.");
443 while (dhcp6Parent != null) {
444 dhcp6Child = dhcp6PacketFromRelayPacket(dhcp6Parent);
445
446 if (dhcp6Child != null) {
447 if (dhcp6Child.getMsgType() != DHCP6.MsgType.RELAY_FORW.value() &&
448 dhcp6Child.getMsgType() != DHCP6.MsgType.RELAY_REPL.value()) {
449 log.debug("leaf dhcp6 packet found.");
450 break;
451 } else {
452 // found another relay
453 // go for another loop
454 dhcp6Parent = dhcp6Child;
455 }
456 } else {
457 log.warn("malformed pkt! Expected dhcp6 within relay pkt, but no dhcp6 leaf found.");
458 break;
459 }
460 }
461 return dhcp6Child;
462 }
463
464 /**
465 * check if DHCP6 relay-reply is reply.
466 *
467 * @param relayPacket dhcp6 relay-reply
468 * @return boolean relay-reply contains ack
469 */
470 private boolean isDhcp6Reply(DHCP6 relayPacket) {
471 log.debug("isDhcp6Reply entered.");
472
473 DHCP6 leafDhcp6 = getDhcp6Leaf(relayPacket);
474
475 if (leafDhcp6 != null) {
476 if (leafDhcp6.getMsgType() == DHCP6.MsgType.REPLY.value()) {
477 log.debug("isDhcp6Reply true.");
478 return true; // must be directly connected
479 } else {
480 log.debug("isDhcp6Reply false. leaf dhcp6 is not replay. MsgType {}", leafDhcp6.getMsgType());
481 }
482 } else {
483 log.debug("isDhcp6Reply false. Expected dhcp6 within relay pkt but not found.");
484 }
485 log.debug("isDhcp6Reply false.");
486 return false;
487 }
488
489 /**
490 * check if DHCP6 is release or relay-forward contains release.
491 *
492 * @param dhcp6Payload dhcp6 packet
493 * @return boolean dhcp6 contains release
494 */
495 private boolean isDhcp6Release(DHCP6 dhcp6Payload) {
496
497 log.debug("isDhcp6Release entered.");
498
499 if (dhcp6Payload.getMsgType() == DHCP6.MsgType.RELEASE.value()) {
500 log.debug("isDhcp6Release true.");
501 return true; // must be directly connected
502 } else {
503 DHCP6 dhcp6Leaf = getDhcp6Leaf(dhcp6Payload);
504 if (dhcp6Leaf != null) {
505 if (dhcp6Leaf.getMsgType() == DHCP6.MsgType.RELEASE.value()) {
506 log.debug("isDhcp6Release true. indirectlry connected");
507 return true;
508 } else {
509 log.debug("leaf dhcp6 is not release. MsgType {}", dhcp6Leaf.getMsgType());
510 return false;
511 }
512 } else {
513 log.debug("isDhcp6Release false. dhcp6 is niether relay nor release.");
514 return false;
515 }
516 }
517 }
518
519 /**
520 * extract from dhcp6 packet client ipv6 address of given by dhcp server.
521 *
522 * @param dhcp6 the dhcp6 packet
523 * @return Ip6Address Ip6Address given by dhcp server, or null if not exists
524 */
525 private Ip6Address extractIpAddress(DHCP6 dhcp6) {
526 Ip6Address ip = null;
527
528 log.debug("extractIpAddress enters dhcp6 {}.", dhcp6);
529 // Extract IPv6 address from IA NA ot IA TA option
530 Optional<Dhcp6IaNaOption> iaNaOption = dhcp6.getOptions()
531 .stream()
532 .filter(opt -> opt instanceof Dhcp6IaNaOption)
533 .map(opt -> (Dhcp6IaNaOption) opt)
534 .findFirst();
535 Optional<Dhcp6IaTaOption> iaTaOption = dhcp6.getOptions()
536 .stream()
537 .filter(opt -> opt instanceof Dhcp6IaTaOption)
538 .map(opt -> (Dhcp6IaTaOption) opt)
539 .findFirst();
540 Optional<Dhcp6IaAddressOption> iaAddressOption;
541 if (iaNaOption.isPresent()) {
542 log.debug("Found IPv6 address from iaNaOption {}", iaNaOption);
543
544 iaAddressOption = iaNaOption.get().getOptions().stream()
545 .filter(opt -> opt instanceof Dhcp6IaAddressOption)
546 .map(opt -> (Dhcp6IaAddressOption) opt)
547 .findFirst();
548 } else if (iaTaOption.isPresent()) {
549 log.debug("Found IPv6 address from iaTaOption {}", iaTaOption);
550
551 iaAddressOption = iaTaOption.get().getOptions().stream()
552 .filter(opt -> opt instanceof Dhcp6IaAddressOption)
553 .map(opt -> (Dhcp6IaAddressOption) opt)
554 .findFirst();
555 } else {
556 iaAddressOption = Optional.empty();
557 }
558 if (iaAddressOption.isPresent()) {
559 ip = iaAddressOption.get().getIp6Address();
560 log.debug("Found IPv6 address from iaAddressOption {}", iaAddressOption);
561
562
563 } else {
564 log.debug("Can't find IPv6 address from DHCPv6 {}", dhcp6);
565 }
566
567 return ip;
568 }
569 /**
570 * extract from dhcp6 packet Prefix prefix provided by dhcp server.
571 *
572 * @param dhcp6 the dhcp6 payload
573 * @return IpPrefix Prefix Delegation prefix, or null if not exists.
574 */
575 private IpPrefix extractPrefix(DHCP6 dhcp6) {
576 log.warn("extractPrefix enters {}", dhcp6);
577
578 // extract prefix
579 IpPrefix prefixPrefix = null;
580
581 Ip6Address prefixAddress = null;
582
583 // Extract IPv6 prefix from IA PD option
584 Optional<Dhcp6IaPdOption> iaPdOption = dhcp6.getOptions()
585 .stream()
586 .filter(opt -> opt instanceof Dhcp6IaPdOption)
587 .map(opt -> (Dhcp6IaPdOption) opt)
588 .findFirst();
589
590 Optional<Dhcp6IaPrefixOption> iaPrefixOption;
591 if (iaPdOption.isPresent()) {
592 log.warn("IA_PD option found {}", iaPdOption);
593
594 iaPrefixOption = iaPdOption.get().getOptions().stream()
595 .filter(opt -> opt instanceof Dhcp6IaPrefixOption)
596 .map(opt -> (Dhcp6IaPrefixOption) opt)
597 .findFirst();
598 } else {
599 log.warn("IA_PD option NOT found");
600
601 iaPrefixOption = Optional.empty();
602 }
603 if (iaPrefixOption.isPresent()) {
604 log.warn("IAPrefix Option within IA_PD option found {}", iaPrefixOption);
605
606 prefixAddress = iaPrefixOption.get().getIp6Prefix();
607 int prefixLen = (int) iaPrefixOption.get().getPrefixLength();
608 log.warn("Prefix length is {} bits", prefixLen);
609 prefixPrefix = IpPrefix.valueOf(prefixAddress, prefixLen);
610
611 } else {
612 log.warn("Can't find IPv6 prefix from DHCPv6 {}", dhcp6);
613 }
614
615 return prefixPrefix;
616 }
617
618 /**
619 * remove host or route.
620 *
621 * @param directConnFlag flag to show that packet is from directly connected client
622 * @param dhcp6Packet the dhcp6 payload
623 * @param clientPacket client's ethernet packet
624 * @param clientIpv6 client's Ipv6 packet
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +0000625 * @param clientInterface client interfaces
Kalhee Kim45fede42017-09-05 19:05:06 +0000626 */
627 private void removeHostOrRoute(boolean directConnFlag, DHCP6 dhcp6Packet,
628 Ethernet clientPacket, IPv6 clientIpv6,
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +0000629 Interface clientInterface) {
Kalhee Kim45fede42017-09-05 19:05:06 +0000630 log.debug("extractPrefix enters {}", dhcp6Packet);
Kalhee Kim121ba922017-11-01 17:56:44 +0000631 VlanId vlanId = clientInterface.vlan();
632 MacAddress clientMac = clientPacket.getSourceMAC();
633 log.debug("client mac {} client vlan {}", HexString.toHexString(clientMac.toBytes(), ":"), vlanId);
634
Kalhee Kim45fede42017-09-05 19:05:06 +0000635 // add host or route
636 if (isDhcp6Release(dhcp6Packet)) {
637 IpAddress ip = null;
638 if (directConnFlag) {
639 // Add to host store if it is connected to network directly
640 ip = extractIpAddress(dhcp6Packet);
641 if (ip != null) {
Kalhee Kim121ba922017-11-01 17:56:44 +0000642
Kalhee Kim45fede42017-09-05 19:05:06 +0000643 HostId hostId = HostId.hostId(clientMac, vlanId);
644 log.debug("remove Host {} ip for directly connected.", hostId.toString());
Kalhee Kim45fede42017-09-05 19:05:06 +0000645 // Remove host's ip of when dhcp release msg is received
Yi Tsengaa417a62017-09-08 17:22:51 -0700646 providerService.removeIpFromHost(hostId, ip);
Kalhee Kim45fede42017-09-05 19:05:06 +0000647 } else {
648 log.debug("ipAddress not found. Do not add Host for directly connected.");
649 }
650 } else {
651 // Remove from route store if it is not connected to network directly
Kalhee Kim121ba922017-11-01 17:56:44 +0000652 // pick out the first link-local ip address
653 IpAddress nextHopIp = getFirstIpByHost(clientMac, vlanId);
654 if (nextHopIp == null) {
655 log.warn("Can't find link-local IP address of gateway mac {} vlanId {}",
656 clientMac, vlanId);
657 return;
658 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000659
660 DHCP6 leafDhcp = getDhcp6Leaf(dhcp6Packet);
661 ip = extractIpAddress(leafDhcp);
662 if (ip == null) {
663 log.debug("ip is null");
664 } else {
665 Route routeForIP = new Route(Route.Source.STATIC, ip.toIpPrefix(), nextHopIp);
666 log.debug("removing route of 128 address for indirectly connected.");
667 log.debug("128 ip {}, nexthop {}", HexString.toHexString(ip.toOctets(), ":"),
668 HexString.toHexString(nextHopIp.toOctets(), ":"));
669 routeStore.removeRoute(routeForIP);
670 }
671
672 IpPrefix ipPrefix = extractPrefix(leafDhcp);
673 if (ipPrefix == null) {
674 log.debug("ipPrefix is null ");
675 } else {
676 Route routeForPrefix = new Route(Route.Source.STATIC, ipPrefix, nextHopIp);
677 log.debug("removing route of PD for indirectly connected.");
678 log.debug("pd ip {}, nexthop {}", HexString.toHexString(ipPrefix.address().toOctets(), ":"),
679 HexString.toHexString(nextHopIp.toOctets(), ":"));
680
681 routeStore.removeRoute(routeForPrefix);
Kalhee Kimba366062017-11-07 16:32:09 +0000682 if (this.dhcpFpmEnabled) {
683 dhcpFpmPrefixStore.removeFpmRecord(ipPrefix);
684 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000685 }
686 }
687 }
688 }
689
690 /**
691 * add host or route.
692 *
693 * @param directConnFlag flag to show that packet is from directly connected client
694 * @param dhcp6Relay the dhcp6 payload
695 * @param embeddedDhcp6 client's ethernet packetthe dhcp6 payload within relay
696 * @param clientMac client macAddress
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +0000697 * @param clientInterface client interface
Kalhee Kim45fede42017-09-05 19:05:06 +0000698 */
699 private void addHostOrRoute(boolean directConnFlag, DHCP6 dhcp6Relay,
700 DHCP6 embeddedDhcp6,
701 MacAddress clientMac,
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +0000702 Interface clientInterface) {
Kalhee Kim45fede42017-09-05 19:05:06 +0000703 log.debug("addHostOrRoute entered.");
Kalhee Kim121ba922017-11-01 17:56:44 +0000704 VlanId vlanId = clientInterface.vlan();
Kalhee Kim45fede42017-09-05 19:05:06 +0000705 // add host or route
706 if (isDhcp6Reply(dhcp6Relay)) {
707 IpAddress ip = null;
708 if (directConnFlag) {
709 // Add to host store if it connect to network directly
710 ip = extractIpAddress(embeddedDhcp6);
711 if (ip != null) {
712 Set<IpAddress> ips = Sets.newHashSet(ip);
Yi Tsengaa417a62017-09-08 17:22:51 -0700713
714 // FIXME: we should use vlan id from original packet (solicit, request)
Kalhee Kim45fede42017-09-05 19:05:06 +0000715 HostId hostId = HostId.hostId(clientMac, vlanId);
Yi Tsengaa417a62017-09-08 17:22:51 -0700716 Host host = hostService.getHost(hostId);
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +0000717 HostLocation hostLocation = new HostLocation(clientInterface.connectPoint(),
Yi Tsengaa417a62017-09-08 17:22:51 -0700718 System.currentTimeMillis());
719 Set<HostLocation> hostLocations = Sets.newHashSet(hostLocation);
720
721 if (host != null) {
722 // Dual homing support:
723 // if host exists, use old locations and new location
724 hostLocations.addAll(host.locations());
725 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000726 HostDescription desc = new DefaultHostDescription(clientMac, vlanId,
Yi Tsengaa417a62017-09-08 17:22:51 -0700727 hostLocations, ips,
728 false);
Kalhee Kim45fede42017-09-05 19:05:06 +0000729 log.debug("adding Host for directly connected.");
730 log.debug("client mac {} client vlan {} hostlocation {}",
731 HexString.toHexString(clientMac.toBytes(), ":"),
732 vlanId, hostLocation.toString());
733
734 // Replace the ip when dhcp server give the host new ip address
Yi Tsengaa417a62017-09-08 17:22:51 -0700735 providerService.hostDetected(hostId, desc, false);
Kalhee Kim45fede42017-09-05 19:05:06 +0000736 } else {
737 log.warn("ipAddress not found. Do not add Host for directly connected.");
738 }
739 } else {
740 // Add to route store if it does not connect to network directly
Kalhee Kim121ba922017-11-01 17:56:44 +0000741 // pick out the first link-local ip address
742 IpAddress nextHopIp = getFirstIpByHost(clientMac, vlanId);
743 if (nextHopIp == null) {
744 log.warn("Can't find link-local IP address of gateway mac {} vlanId {}",
745 clientMac, vlanId);
746 return;
747 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000748
749 DHCP6 leafDhcp = getDhcp6Leaf(embeddedDhcp6);
750 ip = extractIpAddress(leafDhcp);
751 if (ip == null) {
752 log.warn("ip is null");
753 } else {
754 Route routeForIP = new Route(Route.Source.STATIC, ip.toIpPrefix(), nextHopIp);
755 log.warn("adding Route of 128 address for indirectly connected.");
756 routeStore.updateRoute(routeForIP);
757 }
758
759 IpPrefix ipPrefix = extractPrefix(leafDhcp);
760 if (ipPrefix == null) {
761 log.warn("ipPrefix is null ");
762 } else {
763 Route routeForPrefix = new Route(Route.Source.STATIC, ipPrefix, nextHopIp);
764 log.warn("adding Route of PD for indirectly connected.");
765 routeStore.updateRoute(routeForPrefix);
Kalhee Kimba366062017-11-07 16:32:09 +0000766 if (this.dhcpFpmEnabled) {
767 FpmRecord record = new FpmRecord(ipPrefix, nextHopIp, FpmRecord.Type.DHCP_RELAY);
768 dhcpFpmPrefixStore.addFpmRecord(ipPrefix, record);
769 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000770 }
771 }
772 }
773 }
774
775 /**
Yi Tseng25bfe372017-11-03 16:27:32 -0700776 * Build the DHCP6 solicit/request packet with gatewayip.
777 * TODO: method too long, need to be refactored.
Kalhee Kim45fede42017-09-05 19:05:06 +0000778 *
779 * @param context packet context
780 * @param clientPacket client ethernet packet
781 * @param clientInterfaces set of client side interfaces
782 */
Yi Tseng3bd57ac2017-11-29 14:39:18 -0800783 private InternalPacket processDhcp6PacketFromClient(PacketContext context,
784 Ethernet clientPacket, Set<Interface> clientInterfaces) {
785 ConnectPoint receivedFrom = context.inPacket().receivedFrom();
786 DeviceId receivedFromDevice = receivedFrom.deviceId();
787 DhcpServerInfo serverInfo;
788 Ip6Address dhcpServerIp = null;
789 ConnectPoint dhcpServerConnectPoint = null;
790 MacAddress dhcpConnectMac = null;
791 VlanId dhcpConnectVlan = null;
792 Ip6Address dhcpGatewayIp = null;
793 Ip6Address indirectDhcpServerIp = null;
794 ConnectPoint indirectDhcpServerConnectPoint = null;
795 MacAddress indirectDhcpConnectMac = null;
796 VlanId indirectDhcpConnectVlan = null;
797 Ip6Address indirectDhcpGatewayIp = null;
798 Ip6Address indirectRelayAgentIpFromCfg = null;
799 if (!defaultServerInfoList.isEmpty()) {
800 serverInfo = defaultServerInfoList.get(0);
801 dhcpConnectMac = serverInfo.getDhcpConnectMac().orElse(null);
802 dhcpGatewayIp = serverInfo.getDhcpGatewayIp6().orElse(null);
803 dhcpServerIp = serverInfo.getDhcpServerIp6().orElse(null);
804 dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
805 dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
806 }
807 if (!indirectServerInfoList.isEmpty()) {
808 serverInfo = indirectServerInfoList.get(0);
809 indirectDhcpConnectMac = serverInfo.getDhcpConnectMac().orElse(null);
810 indirectDhcpGatewayIp = serverInfo.getDhcpGatewayIp6().orElse(null);
811 indirectDhcpServerIp = serverInfo.getDhcpServerIp6().orElse(null);
812 indirectDhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
813 indirectDhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
814 indirectRelayAgentIpFromCfg = serverInfo.getRelayAgentIp6(receivedFromDevice).orElse(null);
815 }
816 Ip6Address relayAgentIp = getRelayAgentIPv6Address(clientInterfaces);
817 MacAddress relayAgentMac = clientInterfaces.iterator().next().mac();
818 if (relayAgentIp == null || relayAgentMac == null) {
819 log.warn("Missing DHCP relay agent interface Ipv6 addr config for "
820 + "packet from client on port: {}. Aborting packet processing",
821 clientInterfaces.iterator().next().connectPoint());
822 return null;
823 }
824 // get dhcp6 header.
825 IPv6 clientIpv6 = (IPv6) clientPacket.getPayload();
826 UDP clientUdp = (UDP) clientIpv6.getPayload();
827 DHCP6 clientDhcp6 = (DHCP6) clientUdp.getPayload();
828 boolean directConnFlag = directlyConnected(clientDhcp6);
829 Interface serverInterface;
830 if (directConnFlag) {
831 serverInterface = getServerInterface();
832 } else {
833 serverInterface = getIndirectServerInterface();
834 if (serverInterface == null) {
835 // Indirect server interface not found, use default server interface
836 serverInterface = getServerInterface();
837 }
838 }
839 if (serverInterface == null) {
840 log.warn("Can't get {} server interface, ignore", directConnFlag ? "direct" : "indirect");
841 return null;
842 }
843 Ip6Address ipFacingServer = getFirstIpFromInterface(serverInterface);
844 MacAddress macFacingServer = serverInterface.mac();
845 if (ipFacingServer == null || macFacingServer == null) {
846 log.warn("No IP v6 address for server Interface {}", serverInterface);
847 return null;
848 }
849 Ethernet etherReply = (Ethernet) clientPacket.duplicate();
850 etherReply.setSourceMACAddress(macFacingServer);
851 if ((directConnFlag && dhcpConnectMac == null) ||
852 !directConnFlag && indirectDhcpConnectMac == null && dhcpConnectMac == null) {
853 log.warn("Packet received from {} connected client.", directConnFlag ? "directly" : "indirectly");
854 log.warn("DHCP6 {} not yet resolved .. Aborting DHCP packet processing from client on port: {}",
855 (dhcpGatewayIp == null) ? "server IP " + dhcpServerIp
856 : "gateway IP " + dhcpGatewayIp,
857 clientInterfaces.iterator().next().connectPoint());
858 return null;
859 }
860 if (dhcpServerConnectPoint == null) {
861 log.warn("DHCP6 server connection point direct {} directConn {} indirectConn {} is not set up yet",
Yi Tseng25bfe372017-11-03 16:27:32 -0700862 directConnFlag, dhcpServerConnectPoint, indirectDhcpServerConnectPoint);
Kalhee Kim121ba922017-11-01 17:56:44 +0000863 return null;
864 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000865
Yi Tseng25bfe372017-11-03 16:27:32 -0700866 etherReply.setDestinationMACAddress(dhcpConnectMac);
867 etherReply.setVlanID(dhcpConnectVlan.toShort());
Kalhee Kim121ba922017-11-01 17:56:44 +0000868 IPv6 ipv6Packet = (IPv6) etherReply.getPayload();
869 byte[] peerAddress = clientIpv6.getSourceAddress();
870 ipv6Packet.setSourceAddress(ipFacingServer.toOctets());
Yi Tseng25bfe372017-11-03 16:27:32 -0700871 ipv6Packet.setDestinationAddress(dhcpServerIp.toOctets());
Kalhee Kim121ba922017-11-01 17:56:44 +0000872 UDP udpPacket = (UDP) ipv6Packet.getPayload();
873 udpPacket.setSourcePort(UDP.DHCP_V6_SERVER_PORT);
874 DHCP6 dhcp6Packet = (DHCP6) udpPacket.getPayload();
875 byte[] dhcp6PacketByte = dhcp6Packet.serialize();
Charles Chana990ce92017-10-30 10:22:50 -0700876
Kalhee Kim121ba922017-11-01 17:56:44 +0000877 ConnectPoint clientConnectionPoint = context.inPacket().receivedFrom();
878 VlanId vlanIdInUse = VlanId.vlanId(clientPacket.getVlanID());
879 Interface clientInterface = interfaceService.getInterfacesByPort(clientConnectionPoint)
880 .stream().filter(iface -> interfaceContainsVlan(iface, vlanIdInUse))
881 .findFirst().orElse(null);
Kalhee Kim45fede42017-09-05 19:05:06 +0000882
Kalhee Kim121ba922017-11-01 17:56:44 +0000883 removeHostOrRoute(directConnFlag, dhcp6Packet, clientPacket, clientIpv6, clientInterface);
Kalhee Kim45b24182017-10-18 18:30:23 +0000884
Kalhee Kim121ba922017-11-01 17:56:44 +0000885 DHCP6 dhcp6Relay = new DHCP6();
886 dhcp6Relay.setMsgType(DHCP6.MsgType.RELAY_FORW.value());
Kalhee Kim121ba922017-11-01 17:56:44 +0000887 if (directConnFlag) {
888 dhcp6Relay.setLinkAddress(relayAgentIp.toOctets());
889 log.debug("direct connection: relayAgentIp obtained dynamically {}",
890 HexString.toHexString(relayAgentIp.toOctets(), ":"));
Kalhee Kim45fede42017-09-05 19:05:06 +0000891
Kalhee Kim121ba922017-11-01 17:56:44 +0000892 } else {
Yi Tseng25bfe372017-11-03 16:27:32 -0700893 if (indirectDhcpServerIp == null) {
Kalhee Kim121ba922017-11-01 17:56:44 +0000894 log.warn("indirect DhcpServerIp not available, use default DhcpServerIp {}",
Yi Tseng25bfe372017-11-03 16:27:32 -0700895 HexString.toHexString(dhcpServerIp.toOctets()));
Kalhee Kimd21029f2017-09-26 20:21:53 +0000896 } else {
897 // Indirect case, replace destination to indirect dhcp server if exist
898 // Check if mac is obtained for valid server ip
Yi Tseng25bfe372017-11-03 16:27:32 -0700899 if (indirectDhcpConnectMac == null) {
Kalhee Kimd21029f2017-09-26 20:21:53 +0000900 log.warn("DHCP6 {} not yet resolved .. Aborting DHCP "
Yi Tseng25bfe372017-11-03 16:27:32 -0700901 + "packet processing from client on port: {}",
902 (indirectDhcpGatewayIp == null) ? "server IP " + indirectDhcpServerIp
903 : "gateway IP " + indirectDhcpGatewayIp,
904 clientInterfaces.iterator().next().connectPoint());
Kalhee Kimd21029f2017-09-26 20:21:53 +0000905 return null;
906 }
Yi Tseng25bfe372017-11-03 16:27:32 -0700907 etherReply.setDestinationMACAddress(indirectDhcpConnectMac);
908 etherReply.setVlanID(indirectDhcpConnectVlan.toShort());
909 ipv6Packet.setDestinationAddress(indirectDhcpServerIp.toOctets());
Kalhee Kimd21029f2017-09-26 20:21:53 +0000910 }
Yi Tseng25bfe372017-11-03 16:27:32 -0700911 if (indirectRelayAgentIpFromCfg == null) {
Kalhee Kim45fede42017-09-05 19:05:06 +0000912 dhcp6Relay.setLinkAddress(relayAgentIp.toOctets());
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +0000913 log.warn("indirect connection: relayAgentIp NOT availale from config file! Use dynamic. {}",
Yi Tseng25bfe372017-11-03 16:27:32 -0700914 HexString.toHexString(relayAgentIp.toOctets(), ":"));
915 } else {
916 dhcp6Relay.setLinkAddress(indirectRelayAgentIpFromCfg.toOctets());
917 log.debug("indirect connection: relayAgentIp from config file is available! {}",
918 HexString.toHexString(indirectRelayAgentIpFromCfg.toOctets(), ":"));
919 }
920 }
921 // peer address: address of the client or relay agent from which
922 // the message to be relayed was received.
923 dhcp6Relay.setPeerAddress(peerAddress);
924 List<Dhcp6Option> options = new ArrayList<>();
925 // directly connected case, hop count is zero; otherwise, hop count + 1
926 if (directConnFlag) {
927 dhcp6Relay.setHopCount((byte) 0);
928 } else {
929 dhcp6Relay.setHopCount((byte) (dhcp6Packet.getHopCount() + 1));
930 }
931 // create relay message option
932 Dhcp6Option relayMessage = new Dhcp6Option();
933 relayMessage.setCode(DHCP6.OptionCode.RELAY_MSG.value());
934 relayMessage.setLength((short) dhcp6PacketByte.length);
935 relayMessage.setData(dhcp6PacketByte);
936 options.add(relayMessage);
937 // create interfaceId option
938 String inPortString = "-" + context.inPacket().receivedFrom().toString() + ":";
939 Dhcp6Option interfaceId = new Dhcp6Option();
940 interfaceId.setCode(DHCP6.OptionCode.INTERFACE_ID.value());
941 byte[] clientSoureMacBytes = clientPacket.getSourceMACAddress();
942 byte[] inPortStringBytes = inPortString.getBytes();
943 byte[] vlanIdBytes = new byte[2];
944 vlanIdBytes[0] = (byte) (clientPacket.getVlanID() & 0xff);
945 vlanIdBytes[1] = (byte) ((clientPacket.getVlanID() >> 8) & 0xff);
946 byte[] interfaceIdBytes = new byte[clientSoureMacBytes.length +
947 inPortStringBytes.length + vlanIdBytes.length];
948 log.debug("Length: interfaceIdBytes {} clientSoureMacBytes {} inPortStringBytes {} vlan {}",
949 interfaceIdBytes.length, clientSoureMacBytes.length, inPortStringBytes.length,
950 vlanIdBytes.length);
951 System.arraycopy(clientSoureMacBytes, 0, interfaceIdBytes, 0, clientSoureMacBytes.length);
952 System.arraycopy(inPortStringBytes, 0, interfaceIdBytes, clientSoureMacBytes.length, inPortStringBytes.length);
953 System.arraycopy(vlanIdBytes, 0, interfaceIdBytes, clientSoureMacBytes.length + inPortStringBytes.length,
954 vlanIdBytes.length);
955 interfaceId.setData(interfaceIdBytes);
956 interfaceId.setLength((short) interfaceIdBytes.length);
957 options.add(interfaceId);
958 log.debug("interfaceId write srcMac {} portString {}",
959 HexString.toHexString(clientSoureMacBytes, ":"), inPortString);
960 dhcp6Relay.setOptions(options);
961 udpPacket.setPayload(dhcp6Relay);
962 udpPacket.resetChecksum();
963 ipv6Packet.setPayload(udpPacket);
964 ipv6Packet.setHopLimit((byte) 64);
965 etherReply.setPayload(ipv6Packet);
966 if (directConnFlag || indirectDhcpServerIp == null) {
967 return new InternalPacket(etherReply, dhcpServerConnectPoint);
968 } else {
969 return new InternalPacket(etherReply, indirectDhcpServerConnectPoint);
970 }
971 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000972
973 /**
974 *
975 * process the DHCP6 relay-reply packet from dhcp server.
976 *
977 * @param context packet context
978 * @param receivedPacket server ethernet packet
979 * @param recevingInterfaces set of server side interfaces
980 */
981 private InternalPacket processDhcp6PacketFromServer(PacketContext context,
982 Ethernet receivedPacket, Set<Interface> recevingInterfaces) {
Yi Tseng25bfe372017-11-03 16:27:32 -0700983
984 ConnectPoint receivedFrom = context.inPacket().receivedFrom();
985 DeviceId receivedFromDevice = receivedFrom.deviceId();
986
987 // TODO: refactor
988 DhcpServerInfo serverInfo;
989 Ip6Address dhcpServerIp = null;
990 ConnectPoint dhcpServerConnectPoint = null;
991 MacAddress dhcpConnectMac = null;
992 VlanId dhcpConnectVlan = null;
993 Ip6Address dhcpGatewayIp = null;
994
995 Ip6Address indirectDhcpServerIp = null;
996 ConnectPoint indirectDhcpServerConnectPoint = null;
997 MacAddress indirectDhcpConnectMac = null;
998 VlanId indirectDhcpConnectVlan = null;
999 Ip6Address indirectDhcpGatewayIp = null;
1000 Ip6Address indirectRelayAgentIpFromCfg = null;
1001
1002 if (!defaultServerInfoList.isEmpty()) {
1003 serverInfo = defaultServerInfoList.get(0);
1004 dhcpConnectMac = serverInfo.getDhcpConnectMac().orElse(null);
1005 dhcpGatewayIp = serverInfo.getDhcpGatewayIp6().orElse(null);
1006 dhcpServerIp = serverInfo.getDhcpServerIp6().orElse(null);
1007 dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1008 dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1009 }
1010
1011 if (!indirectServerInfoList.isEmpty()) {
1012 serverInfo = indirectServerInfoList.get(0);
1013 indirectDhcpConnectMac = serverInfo.getDhcpConnectMac().orElse(null);
1014 indirectDhcpGatewayIp = serverInfo.getDhcpGatewayIp6().orElse(null);
1015 indirectDhcpServerIp = serverInfo.getDhcpServerIp6().orElse(null);
1016 indirectDhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1017 indirectDhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1018 indirectRelayAgentIpFromCfg = serverInfo.getRelayAgentIp6(receivedFromDevice).orElse(null);
1019 }
1020
Kalhee Kim45fede42017-09-05 19:05:06 +00001021 // get dhcp6 header.
Ray Milkeyf0c47612017-09-28 11:29:38 -07001022 Ethernet etherReply = receivedPacket.duplicate();
Kalhee Kim45fede42017-09-05 19:05:06 +00001023 IPv6 ipv6Packet = (IPv6) etherReply.getPayload();
1024 UDP udpPacket = (UDP) ipv6Packet.getPayload();
1025 DHCP6 dhcp6Relay = (DHCP6) udpPacket.getPayload();
1026
1027 Boolean directConnFlag = directlyConnected(dhcp6Relay);
Kalhee Kimd21029f2017-09-26 20:21:53 +00001028 ConnectPoint inPort = context.inPacket().receivedFrom();
Yi Tseng25bfe372017-11-03 16:27:32 -07001029 if ((directConnFlag || (!directConnFlag && indirectDhcpServerIp == null))
1030 && !inPort.equals(dhcpServerConnectPoint)) {
Kalhee Kimd21029f2017-09-26 20:21:53 +00001031 log.warn("Receiving port {} is not the same as server connect point {} for direct or indirect-null",
Yi Tseng25bfe372017-11-03 16:27:32 -07001032 inPort, dhcpServerConnectPoint);
Kalhee Kimd21029f2017-09-26 20:21:53 +00001033 return null;
1034 }
1035
Yi Tseng25bfe372017-11-03 16:27:32 -07001036 if (!directConnFlag && indirectDhcpServerIp != null &&
1037 !inPort.equals(indirectDhcpServerConnectPoint)) {
Kalhee Kimd21029f2017-09-26 20:21:53 +00001038 log.warn("Receiving port {} is not the same as server connect point {} for indirect",
Yi Tseng25bfe372017-11-03 16:27:32 -07001039 inPort, indirectDhcpServerConnectPoint);
Kalhee Kimd21029f2017-09-26 20:21:53 +00001040 return null;
1041 }
1042
Kalhee Kim45fede42017-09-05 19:05:06 +00001043
1044 Dhcp6InterfaceIdOption interfaceIdOption = dhcp6Relay.getOptions().stream()
1045 .filter(opt -> opt instanceof Dhcp6InterfaceIdOption)
1046 .map(opt -> (Dhcp6InterfaceIdOption) opt)
1047 .findFirst()
1048 .orElse(null);
1049
1050 if (interfaceIdOption == null) {
1051 log.warn("Interface Id option is not present, abort packet...");
1052 return null;
1053 }
1054
1055 MacAddress peerMac = interfaceIdOption.getMacAddress();
1056 String clientConnectionPointStr = new String(interfaceIdOption.getInPort());
1057
1058 ConnectPoint clientConnectionPoint = ConnectPoint.deviceConnectPoint(clientConnectionPointStr);
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +00001059 VlanId vlanIdInUse = VlanId.vlanId(interfaceIdOption.getVlanId());
1060 Interface clientInterface = interfaceService.getInterfacesByPort(clientConnectionPoint)
1061 .stream()
1062 .filter(iface -> interfaceContainsVlan(iface, vlanIdInUse))
1063 .findFirst()
1064 .orElse(null);
1065 if (clientInterface == null) {
1066 log.warn("Cannot get client interface for from packet, abort... vlan {}", vlanIdInUse.toString());
Kalhee Kim45fede42017-09-05 19:05:06 +00001067 return null;
1068 }
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +00001069 MacAddress relayAgentMac = clientInterface.mac();
Kalhee Kim45fede42017-09-05 19:05:06 +00001070 if (relayAgentMac == null) {
1071 log.warn("Can not get interface mac, abort packet..");
1072 return null;
1073 }
1074 etherReply.setSourceMACAddress(relayAgentMac);
1075
1076 // find destMac
1077 MacAddress clientMac = null;
Yi Tsengaa417a62017-09-08 17:22:51 -07001078 Ip6Address peerAddress = Ip6Address.valueOf(dhcp6Relay.getPeerAddress());
1079 Set<Host> clients = hostService.getHostsByIp(peerAddress);
Kalhee Kim45fede42017-09-05 19:05:06 +00001080 if (clients.isEmpty()) {
1081 log.warn("There's no host found for this address {}",
1082 HexString.toHexString(dhcp6Relay.getPeerAddress(), ":"));
1083 log.warn("Let's look up interfaceId {}", HexString.toHexString(peerMac.toBytes(), ":"));
1084 clientMac = peerMac;
1085 } else {
1086 clientMac = clients.iterator().next().mac();
1087 if (clientMac == null) {
1088 log.warn("No client mac address found, abort packet...");
1089 return null;
1090 }
1091 log.warn("Client mac address found from getHostByIp");
1092
1093 }
1094 etherReply.setDestinationMACAddress(clientMac);
1095
1096 // ip header
1097 ipv6Packet.setSourceAddress(dhcp6Relay.getLinkAddress());
1098 ipv6Packet.setDestinationAddress(dhcp6Relay.getPeerAddress());
1099 // udp header
1100 udpPacket.setSourcePort(UDP.DHCP_V6_SERVER_PORT);
1101 if (directConnFlag) {
1102 udpPacket.setDestinationPort(UDP.DHCP_V6_CLIENT_PORT);
1103 } else {
1104 udpPacket.setDestinationPort(UDP.DHCP_V6_SERVER_PORT);
1105 }
1106
1107 DHCP6 embeddedDhcp6 = dhcp6Relay.getOptions().stream()
1108 .filter(opt -> opt instanceof Dhcp6RelayOption)
1109 .map(BasePacket::getPayload)
1110 .map(pld -> (DHCP6) pld)
1111 .findFirst()
1112 .orElse(null);
1113
1114
1115 // add host or route
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +00001116 addHostOrRoute(directConnFlag, dhcp6Relay, embeddedDhcp6, clientMac, clientInterface);
Kalhee Kim45fede42017-09-05 19:05:06 +00001117
1118 udpPacket.setPayload(embeddedDhcp6);
1119 udpPacket.resetChecksum();
1120 ipv6Packet.setPayload(udpPacket);
1121 etherReply.setPayload(ipv6Packet);
1122
1123 return new InternalPacket(etherReply, clientConnectionPoint);
1124 }
1125
Yi Tseng919b2df2017-09-07 16:22:51 -07001126 // Returns the first v6 interface ip out of a set of interfaces or null.
Kalhee Kim45fede42017-09-05 19:05:06 +00001127 // Checks all interfaces, and ignores v6 interface ips
1128 private Ip6Address getRelayAgentIPv6Address(Set<Interface> intfs) {
1129 for (Interface intf : intfs) {
1130 for (InterfaceIpAddress ip : intf.ipAddressesList()) {
1131 Ip6Address relayAgentIp = ip.ipAddress().getIp6Address();
1132 if (relayAgentIp != null) {
1133 return relayAgentIp;
1134 }
1135 }
1136 }
1137 return null;
Yi Tseng51301292017-07-28 13:02:59 -07001138 }
Yi Tseng483ac6f2017-08-02 15:03:31 -07001139
1140 @Override
Kalhee Kimba366062017-11-07 16:32:09 +00001141 public void setDhcpFpmEnabled(Boolean enabled) {
1142 dhcpFpmEnabled = enabled;
1143 }
1144
1145 @Override
Yi Tseng483ac6f2017-08-02 15:03:31 -07001146 public void setDefaultDhcpServerConfigs(Collection<DhcpServerConfig> configs) {
Yi Tseng919b2df2017-09-07 16:22:51 -07001147 setDhcpServerConfigs(configs, defaultServerInfoList);
Yi Tseng919b2df2017-09-07 16:22:51 -07001148 }
1149
1150 @Override
1151 public void setIndirectDhcpServerConfigs(Collection<DhcpServerConfig> configs) {
1152 setDhcpServerConfigs(configs, indirectServerInfoList);
Yi Tseng919b2df2017-09-07 16:22:51 -07001153 }
1154
1155 public void setDhcpServerConfigs(Collection<DhcpServerConfig> configs, List<DhcpServerInfo> serverInfoList) {
Kalhee Kim45fede42017-09-05 19:05:06 +00001156 if (configs.size() == 0) {
1157 // no config to update
1158 return;
1159 }
1160
1161 // TODO: currently we pick up first DHCP server config.
1162 // Will use other server configs in the future for HA.
1163 DhcpServerConfig serverConfig = configs.iterator().next();
Yi Tseng919b2df2017-09-07 16:22:51 -07001164
Kalhee Kim45fede42017-09-05 19:05:06 +00001165 if (!serverConfig.getDhcpServerIp6().isPresent()) {
Yi Tseng919b2df2017-09-07 16:22:51 -07001166 // not a DHCPv6 config
Kalhee Kim45fede42017-09-05 19:05:06 +00001167 return;
1168 }
1169
Yi Tseng919b2df2017-09-07 16:22:51 -07001170 if (!serverInfoList.isEmpty()) {
1171 // remove old server info
1172 DhcpServerInfo oldServerInfo = serverInfoList.remove(0);
1173
1174 // stop monitoring gateway or server
1175 oldServerInfo.getDhcpGatewayIp6().ifPresent(gatewayIp -> {
1176 hostService.stopMonitoringIp(gatewayIp);
1177 });
1178 oldServerInfo.getDhcpServerIp6().ifPresent(serverIp -> {
1179 hostService.stopMonitoringIp(serverIp);
Yi Tseng525ff402017-10-23 19:39:39 -07001180 cancelDhcpPacket(serverIp);
Yi Tseng919b2df2017-09-07 16:22:51 -07001181 });
1182 }
1183
1184 // Create new server info according to the config
1185 DhcpServerInfo newServerInfo = new DhcpServerInfo(serverConfig,
1186 DhcpServerInfo.Version.DHCP_V6);
1187 checkState(newServerInfo.getDhcpServerConnectPoint().isPresent(),
1188 "Connect point not exists");
1189 checkState(newServerInfo.getDhcpServerIp6().isPresent(),
1190 "IP of DHCP server not exists");
1191
1192 log.debug("DHCP server connect point: {}", newServerInfo.getDhcpServerConnectPoint().orElse(null));
1193 log.debug("DHCP server IP: {}", newServerInfo.getDhcpServerIp6().orElse(null));
1194
Yi Tseng525ff402017-10-23 19:39:39 -07001195 Ip6Address serverIp = newServerInfo.getDhcpServerIp6().get();
1196 Ip6Address ipToProbe;
Yi Tseng919b2df2017-09-07 16:22:51 -07001197 if (newServerInfo.getDhcpGatewayIp6().isPresent()) {
1198 ipToProbe = newServerInfo.getDhcpGatewayIp6().get();
1199 } else {
1200 ipToProbe = newServerInfo.getDhcpServerIp6().orElse(null);
1201 }
1202 String hostToProbe = newServerInfo.getDhcpGatewayIp6()
1203 .map(ip -> "gateway").orElse("server");
1204
1205 log.debug("Probing to resolve {} IP {}", hostToProbe, ipToProbe);
Kalhee Kim45fede42017-09-05 19:05:06 +00001206 hostService.startMonitoringIp(ipToProbe);
1207
1208 Set<Host> hosts = hostService.getHostsByIp(ipToProbe);
1209 if (!hosts.isEmpty()) {
1210 Host host = hosts.iterator().next();
Yi Tseng919b2df2017-09-07 16:22:51 -07001211 newServerInfo.setDhcpConnectVlan(host.vlan());
1212 newServerInfo.setDhcpConnectMac(host.mac());
Kalhee Kim45fede42017-09-05 19:05:06 +00001213 }
Yi Tseng919b2df2017-09-07 16:22:51 -07001214 // Add new server info
Yi Tseng48cd0862017-11-09 13:54:12 -08001215 synchronized (this) {
1216 serverInfoList.clear();
1217 serverInfoList.add(0, newServerInfo);
1218 }
Yi Tseng525ff402017-10-23 19:39:39 -07001219 requestDhcpPacket(serverIp);
Kalhee Kim45fede42017-09-05 19:05:06 +00001220 }
1221
1222 class InternalHostListener implements HostListener {
1223 @Override
1224 public void event(HostEvent event) {
1225 switch (event.type()) {
1226 case HOST_ADDED:
1227 case HOST_UPDATED:
1228 hostUpdated(event.subject());
1229 break;
1230 case HOST_REMOVED:
1231 hostRemoved(event.subject());
1232 break;
Kalhee Kim45fede42017-09-05 19:05:06 +00001233 default:
1234 break;
1235 }
1236 }
1237 }
1238
1239 /**
Kalhee Kim45fede42017-09-05 19:05:06 +00001240 * Handle host updated.
1241 * If the host is DHCP server or gateway, update connect mac and vlan.
1242 *
1243 * @param host the host
1244 */
1245 private void hostUpdated(Host host) {
Yi Tseng525ff402017-10-23 19:39:39 -07001246 hostUpdated(host, defaultServerInfoList);
1247 hostUpdated(host, indirectServerInfoList);
Kalhee Kim45fede42017-09-05 19:05:06 +00001248 }
1249
Yi Tseng525ff402017-10-23 19:39:39 -07001250 private void hostUpdated(Host host, List<DhcpServerInfo> serverInfoList) {
1251 DhcpServerInfo serverInfo;
1252 Ip6Address targetIp;
1253 if (!serverInfoList.isEmpty()) {
1254 serverInfo = serverInfoList.get(0);
1255 Ip6Address serverIp = serverInfo.getDhcpServerIp6().orElse(null);
1256 targetIp = serverInfo.getDhcpGatewayIp6().orElse(null);
1257
1258 if (targetIp == null) {
1259 targetIp = serverIp;
1260 }
1261
1262 if (targetIp != null) {
1263 if (host.ipAddresses().contains(targetIp)) {
1264 serverInfo.setDhcpConnectMac(host.mac());
1265 serverInfo.setDhcpConnectVlan(host.vlan());
1266 requestDhcpPacket(serverIp);
1267 }
1268 }
1269 }
1270 }
1271
Kalhee Kim45fede42017-09-05 19:05:06 +00001272 /**
1273 * Handle host removed.
1274 * If the host is DHCP server or gateway, unset connect mac and vlan.
1275 *
1276 * @param host the host
1277 */
1278 private void hostRemoved(Host host) {
Yi Tseng525ff402017-10-23 19:39:39 -07001279 hostRemoved(host, defaultServerInfoList);
1280 hostRemoved(host, indirectServerInfoList);
Yi Tseng919b2df2017-09-07 16:22:51 -07001281 }
1282
Yi Tseng525ff402017-10-23 19:39:39 -07001283 private void hostRemoved(Host host, List<DhcpServerInfo> serverInfoList) {
1284 DhcpServerInfo serverInfo;
1285 Ip6Address targetIp;
1286
1287 if (!serverInfoList.isEmpty()) {
1288 serverInfo = serverInfoList.get(0);
1289 Ip6Address serverIp = serverInfo.getDhcpServerIp6().orElse(null);
1290 targetIp = serverInfo.getDhcpGatewayIp6().orElse(null);
1291
1292 if (targetIp == null) {
1293 targetIp = serverIp;
1294 }
1295
1296 if (targetIp != null) {
1297 if (host.ipAddresses().contains(targetIp)) {
1298 serverInfo.setDhcpConnectVlan(null);
1299 serverInfo.setDhcpConnectMac(null);
1300 cancelDhcpPacket(serverIp);
1301 }
1302 }
1303 }
1304 }
1305
Kalhee Kim45b24182017-10-18 18:30:23 +00001306 /**
1307 * Returns the first interface ip from interface.
1308 *
1309 * @param iface interface of one connect point
1310 * @return the first interface IP; null if not exists an IP address in
1311 * these interfaces
1312 */
1313 private Ip6Address getFirstIpFromInterface(Interface iface) {
1314 checkNotNull(iface, "Interface can't be null");
1315 return iface.ipAddressesList().stream()
1316 .map(InterfaceIpAddress::ipAddress)
1317 .filter(IpAddress::isIp6)
1318 .map(IpAddress::getIp6Address)
1319 .findFirst()
1320 .orElse(null);
1321 }
1322
1323 /**
1324 * Gets Interface facing to the server for default host.
1325 *
1326 * @return the Interface facing to the server; null if not found
1327 */
1328 private Interface getServerInterface() {
Yi Tseng25bfe372017-11-03 16:27:32 -07001329 DhcpServerInfo serverInfo;
1330 ConnectPoint dhcpServerConnectPoint;
1331 VlanId dhcpConnectVlan;
1332
1333 if (!defaultServerInfoList.isEmpty()) {
1334 serverInfo = defaultServerInfoList.get(0);
1335 dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1336 dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1337 } else {
Kalhee Kim45b24182017-10-18 18:30:23 +00001338 return null;
1339 }
Yi Tseng921e5b02017-11-17 17:14:41 -08001340 if (dhcpServerConnectPoint == null || dhcpConnectVlan == null) {
1341 log.info("Default DHCP server {} not resolve yet", serverInfo.getDhcpGatewayIp6());
1342 return null;
1343 }
Kalhee Kim45b24182017-10-18 18:30:23 +00001344 return interfaceService.getInterfacesByPort(dhcpServerConnectPoint)
1345 .stream()
1346 .filter(iface -> interfaceContainsVlan(iface, dhcpConnectVlan))
1347 .findFirst()
1348 .orElse(null);
1349 }
1350
1351 /**
1352 * Gets Interface facing to the server for indirect hosts.
1353 * Use default server Interface if indirect server not configured.
1354 *
1355 * @return the Interface facing to the server; null if not found
1356 */
1357 private Interface getIndirectServerInterface() {
Yi Tseng25bfe372017-11-03 16:27:32 -07001358 DhcpServerInfo serverInfo;
1359
1360 ConnectPoint indirectDhcpServerConnectPoint;
1361 VlanId indirectDhcpConnectVlan;
1362
1363 if (!indirectServerInfoList.isEmpty()) {
1364 serverInfo = indirectServerInfoList.get(0);
1365 indirectDhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1366 indirectDhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1367 } else {
Kalhee Kim45b24182017-10-18 18:30:23 +00001368 return getServerInterface();
1369 }
Yi Tseng921e5b02017-11-17 17:14:41 -08001370 if (indirectDhcpServerConnectPoint == null || indirectDhcpConnectVlan == null) {
1371 log.info("Indirect DHCP server {} not resolve yet", serverInfo.getDhcpGatewayIp6());
1372 return null;
1373 }
Kalhee Kim45b24182017-10-18 18:30:23 +00001374 return interfaceService.getInterfacesByPort(indirectDhcpServerConnectPoint)
1375 .stream()
1376 .filter(iface -> interfaceContainsVlan(iface, indirectDhcpConnectVlan))
1377 .findFirst()
1378 .orElse(null);
1379 }
1380
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +00001381 /**
1382 * Determind if an Interface contains a vlan id.
1383 *
1384 * @param iface the Interface
1385 * @param vlanId the vlan id
1386 * @return true if the Interface contains the vlan id
1387 */
1388 private boolean interfaceContainsVlan(Interface iface, VlanId vlanId) {
1389 if (vlanId.equals(VlanId.NONE)) {
1390 // untagged packet, check if vlan untagged or vlan native is not NONE
1391 return !iface.vlanUntagged().equals(VlanId.NONE) ||
1392 !iface.vlanNative().equals(VlanId.NONE);
1393 }
1394 // tagged packet, check if the interface contains the vlan
1395 return iface.vlanTagged().contains(vlanId);
1396 }
1397
Yi Tseng525ff402017-10-23 19:39:39 -07001398 private void requestDhcpPacket(Ip6Address serverIp) {
1399 requestServerDhcpPacket(serverIp);
1400 requestClientDhcpPacket(serverIp);
1401 }
1402
1403 private void cancelDhcpPacket(Ip6Address serverIp) {
1404 cancelServerDhcpPacket(serverIp);
1405 cancelClientDhcpPacket(serverIp);
1406 }
1407
1408 private void cancelServerDhcpPacket(Ip6Address serverIp) {
1409 TrafficSelector serverSelector =
1410 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1411 .matchIPv6Src(serverIp.toIpPrefix())
1412 .build();
1413 packetService.cancelPackets(serverSelector,
1414 PacketPriority.CONTROL,
1415 appId);
1416 }
1417
1418 private void requestServerDhcpPacket(Ip6Address serverIp) {
1419 TrafficSelector serverSelector =
1420 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1421 .matchIPv6Src(serverIp.toIpPrefix())
1422 .build();
1423 packetService.requestPackets(serverSelector,
1424 PacketPriority.CONTROL,
1425 appId);
1426 }
1427
1428 private void cancelClientDhcpPacket(Ip6Address serverIp) {
1429 // Packet comes from relay
1430 TrafficSelector indirectClientSelector =
1431 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1432 .matchIPv6Dst(serverIp.toIpPrefix())
1433 .build();
1434 packetService.cancelPackets(indirectClientSelector,
1435 PacketPriority.CONTROL,
1436 appId);
Yi Tseng41dde702017-11-02 15:21:10 -07001437 indirectClientSelector =
1438 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1439 .matchIPv6Dst(Ip6Address.ALL_DHCP_RELAY_AGENTS_AND_SERVERS.toIpPrefix())
1440 .build();
1441 packetService.cancelPackets(indirectClientSelector,
1442 PacketPriority.CONTROL,
1443 appId);
1444 indirectClientSelector =
1445 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1446 .matchIPv6Dst(Ip6Address.ALL_DHCP_SERVERS.toIpPrefix())
1447 .build();
1448 packetService.cancelPackets(indirectClientSelector,
1449 PacketPriority.CONTROL,
1450 appId);
Yi Tseng525ff402017-10-23 19:39:39 -07001451
1452 // Packet comes from client
1453 packetService.cancelPackets(CLIENT_SERVER_SELECTOR,
1454 PacketPriority.CONTROL,
1455 appId);
1456 }
1457
1458 private void requestClientDhcpPacket(Ip6Address serverIp) {
1459 // Packet comes from relay
1460 TrafficSelector indirectClientSelector =
1461 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1462 .matchIPv6Dst(serverIp.toIpPrefix())
1463 .build();
1464 packetService.requestPackets(indirectClientSelector,
1465 PacketPriority.CONTROL,
1466 appId);
Yi Tseng41dde702017-11-02 15:21:10 -07001467 indirectClientSelector =
1468 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1469 .matchIPv6Dst(Ip6Address.ALL_DHCP_RELAY_AGENTS_AND_SERVERS.toIpPrefix())
1470 .build();
1471 packetService.requestPackets(indirectClientSelector,
1472 PacketPriority.CONTROL,
1473 appId);
1474 indirectClientSelector =
1475 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1476 .matchIPv6Dst(Ip6Address.ALL_DHCP_SERVERS.toIpPrefix())
1477 .build();
1478 packetService.requestPackets(indirectClientSelector,
1479 PacketPriority.CONTROL,
1480 appId);
Yi Tseng525ff402017-10-23 19:39:39 -07001481
1482 // Packet comes from client
1483 packetService.requestPackets(CLIENT_SERVER_SELECTOR,
1484 PacketPriority.CONTROL,
1485 appId);
1486 }
1487
1488 /**
1489 * Process the ignore rules.
1490 *
1491 * @param deviceId the device id
1492 * @param vlanId the vlan to be ignored
1493 * @param op the operation, ADD to install; REMOVE to uninstall rules
1494 */
1495 private void processIgnoreVlanRule(DeviceId deviceId, VlanId vlanId, Objective.Operation op) {
Yi Tseng525ff402017-10-23 19:39:39 -07001496 AtomicInteger installedCount = new AtomicInteger(DHCP_SELECTORS.size());
1497 DHCP_SELECTORS.forEach(trafficSelector -> {
1498 TrafficSelector selector = DefaultTrafficSelector.builder(trafficSelector)
1499 .matchVlanId(vlanId)
1500 .build();
1501
1502 ForwardingObjective.Builder builder = DefaultForwardingObjective.builder()
1503 .withFlag(ForwardingObjective.Flag.VERSATILE)
1504 .withSelector(selector)
1505 .withPriority(IGNORE_CONTROL_PRIORITY)
Yi Tsengdbabeed2017-11-29 10:49:20 -08001506 .withTreatment(DefaultTrafficTreatment.emptyTreatment())
Yi Tseng525ff402017-10-23 19:39:39 -07001507 .fromApp(appId);
1508
1509
1510 ObjectiveContext objectiveContext = new ObjectiveContext() {
1511 @Override
1512 public void onSuccess(Objective objective) {
1513 log.info("Ignore rule {} (Vlan id {}, device {}, selector {})",
1514 op, vlanId, deviceId, selector);
1515 int countDown = installedCount.decrementAndGet();
1516 if (countDown != 0) {
1517 return;
1518 }
1519 switch (op) {
1520 case ADD:
1521 ignoredVlans.put(deviceId, vlanId);
1522 break;
1523 case REMOVE:
1524 ignoredVlans.remove(deviceId, vlanId);
1525 break;
1526 default:
1527 log.warn("Unsupported objective operation {}", op);
1528 break;
1529 }
1530 }
1531
1532 @Override
1533 public void onError(Objective objective, ObjectiveError error) {
1534 log.warn("Can't {} ignore rule (vlan id {}, selector {}, device {}) due to {}",
1535 op, vlanId, selector, deviceId, error);
1536 }
1537 };
1538
1539 ForwardingObjective fwd;
1540 switch (op) {
1541 case ADD:
1542 fwd = builder.add(objectiveContext);
1543 break;
1544 case REMOVE:
1545 fwd = builder.remove(objectiveContext);
1546 break;
1547 default:
1548 log.warn("Unsupported objective operation {}", op);
1549 return;
1550 }
1551
1552 Device device = deviceService.getDevice(deviceId);
1553 if (device == null || !device.is(Pipeliner.class)) {
1554 log.warn("Device {} is not available now, wait until device is available", deviceId);
1555 return;
1556 }
1557 flowObjectiveService.apply(deviceId, fwd);
1558 });
1559 }
Kalhee Kim121ba922017-11-01 17:56:44 +00001560
1561 /**
1562 * Find first ipaddress for a given Host info i.e. mac and vlan.
1563 *
1564 * @param clientMac client mac
1565 * @param vlanId packet's vlan
1566 * @return next-hop link-local ipaddress for a given host
1567 */
1568 private IpAddress getFirstIpByHost(MacAddress clientMac, VlanId vlanId) {
1569 IpAddress nextHopIp;
1570 // pick out the first link-local ip address
1571 HostId gwHostId = HostId.hostId(clientMac, vlanId);
1572 Host gwHost = hostService.getHost(gwHostId);
1573 if (gwHost == null) {
1574 log.warn("Can't find gateway host for hostId {}", gwHostId);
1575 return null;
1576 }
1577 nextHopIp = gwHost.ipAddresses()
1578 .stream()
1579 .filter(IpAddress::isIp6)
1580 .filter(ip6 -> ip6.isLinkLocal())
1581 .map(IpAddress::getIp6Address)
1582 .findFirst()
1583 .orElse(null);
1584 return nextHopIp;
1585 }
Yi Tseng51301292017-07-28 13:02:59 -07001586}