blob: 17028d6c893e7f735a7ebc6374eaa97a526fb384 [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");
Yi Tseng68ef26b2017-12-18 17:10:00 -0800279 log.trace("dhcp6 payload {}", dhcp6Payload);
Kalhee Kim45fede42017-09-05 19:05:06 +0000280
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)) {
Kalhee Kim45fede42017-09-05 19:05:06 +0000297 InternalPacket ethernetClientPacket =
298 processDhcp6PacketFromClient(context, receivedPacket, receivingInterfaces);
299 if (ethernetClientPacket != null) {
300 forwardPacket(ethernetClientPacket);
301 }
302
303 } else if (MSG_TYPE_FROM_SERVER.contains(msgType)) {
Yi Tseng68ef26b2017-12-18 17:10:00 -0800304 log.debug("calling processDhcp6PacketFromServer with RELAY_REPL", msgType);
Kalhee Kim45fede42017-09-05 19:05:06 +0000305 InternalPacket ethernetPacketReply =
306 processDhcp6PacketFromServer(context, receivedPacket, receivingInterfaces);
307 if (ethernetPacketReply != null) {
308 forwardPacket(ethernetPacketReply);
309 }
310 } else {
Yi Tseng68ef26b2017-12-18 17:10:00 -0800311 log.warn("DHCP type {} not supported yet", msgType);
Kalhee Kim45fede42017-09-05 19:05:06 +0000312 }
313 }
314
315
316 /**
317 * Checks if this app has been configured.
318 *
319 * @return true if all information we need have been initialized
320 */
321 public boolean configured() {
Yi Tseng919b2df2017-09-07 16:22:51 -0700322 return !defaultServerInfoList.isEmpty();
Kalhee Kim45fede42017-09-05 19:05:06 +0000323 }
324
Yi Tsengaa417a62017-09-08 17:22:51 -0700325 @Override
326 public ProviderId id() {
Charles Chand988c282017-09-12 17:09:32 -0700327 return PROVIDER_ID;
Yi Tsengaa417a62017-09-08 17:22:51 -0700328 }
329
330 @Override
331 public void triggerProbe(Host host) {
332 // Do nothing here
333 }
334
Kalhee Kim45fede42017-09-05 19:05:06 +0000335 // the new class the contains Ethernet packet and destination port, kind of like adding
336 // internal header to the packet
337 private class InternalPacket {
338 Ethernet packet;
339 ConnectPoint destLocation;
340 public InternalPacket(Ethernet newPacket, ConnectPoint newLocation) {
341 packet = newPacket;
342 destLocation = newLocation;
343 }
344 void setLocation(ConnectPoint newLocation) {
345 destLocation = newLocation;
346 }
347 }
348
349 //forward the packet to ConnectPoint where the DHCP server is attached.
350 private void forwardPacket(InternalPacket packet) {
351 //send Packetout to dhcp server connectpoint.
352 if (packet.destLocation != null) {
353 TrafficTreatment t = DefaultTrafficTreatment.builder()
354 .setOutput(packet.destLocation.port()).build();
355 OutboundPacket o = new DefaultOutboundPacket(
356 packet.destLocation.deviceId(), t, ByteBuffer.wrap(packet.packet.serialize()));
357 if (log.isTraceEnabled()) {
358 log.trace("Relaying packet to destination {}", packet.destLocation);
359 }
360 packetService.emit(o);
361 } // if
362 }
363
364 /**
365 * Check if the host is directly connected to the network or not.
366 *
367 * @param dhcp6Payload the dhcp6 payload
368 * @return true if the host is directly connected to the network; false otherwise
369 */
370 private boolean directlyConnected(DHCP6 dhcp6Payload) {
371 log.debug("directlyConnected enters");
372
373 if (dhcp6Payload.getMsgType() != DHCP6.MsgType.RELAY_FORW.value() &&
374 dhcp6Payload.getMsgType() != DHCP6.MsgType.RELAY_REPL.value()) {
375 log.debug("directlyConnected true. MsgType {}", dhcp6Payload.getMsgType());
376
377 return true;
378 }
379
380 // Regardless of relay-forward or relay-replay, check if we see another relay message
381 DHCP6 dhcp6Payload2 = dhcp6PacketFromRelayPacket(dhcp6Payload);
382 if (dhcp6Payload2 != null) {
383 if (dhcp6Payload.getMsgType() == DHCP6.MsgType.RELAY_FORW.value()) {
384 log.debug("directlyConnected false. 1st realy-foward, 2nd MsgType {}", dhcp6Payload2.getMsgType());
385 return false;
386 } else {
387 // relay-reply
388 if (dhcp6Payload2.getMsgType() != DHCP6.MsgType.RELAY_REPL.value()) {
389 log.debug("directlyConnected true. 2nd MsgType {}", dhcp6Payload2.getMsgType());
390 return true; // must be directly connected
391 } else {
392 log.debug("directlyConnected false. 1st relay-reply, 2nd relay-reply MsgType {}",
393 dhcp6Payload2.getMsgType());
394 return false; // must be indirectly connected
395 }
396 }
397 } else {
Yi Tseng68ef26b2017-12-18 17:10:00 -0800398 log.debug("directlyConnected true.");
Kalhee Kim45fede42017-09-05 19:05:06 +0000399 return true;
400 }
401 }
402
403 /**
404 * extract DHCP6 payload from dhcp6 relay message within relay-forwrd/reply.
405 *
406 * @param dhcp6 dhcp6 relay-reply or relay-foward
407 * @return dhcp6Packet dhcp6 packet extracted from relay-message
408 */
409 private DHCP6 dhcp6PacketFromRelayPacket(DHCP6 dhcp6) {
410 log.debug("dhcp6PacketFromRelayPacket enters. dhcp6 {}", dhcp6);
411
412 // extract the relay message if exist
413 DHCP6 dhcp6Payload = dhcp6.getOptions().stream()
414 .filter(opt -> opt instanceof Dhcp6RelayOption)
415 .map(BasePacket::getPayload)
416 .map(pld -> (DHCP6) pld)
417 .findFirst()
418 .orElse(null);
419
420
421 if (dhcp6Payload == null) {
422 // Can't find dhcp payload
423 log.debug("Can't find dhcp6 payload from relay message");
424 } else {
425 log.debug("dhcp6 payload found from relay message {}", dhcp6Payload);
426 }
427
428 return dhcp6Payload;
429 }
430
431 /**
432 * find the leaf DHCP6 packet from multi-level relay packet.
433 *
434 * @param relayPacket dhcp6 relay packet
435 * @return leafPacket non-relay dhcp6 packet
436 */
437 private DHCP6 getDhcp6Leaf(DHCP6 relayPacket) {
438 DHCP6 dhcp6Parent = relayPacket;
439 DHCP6 dhcp6Child = null;
440
441 log.debug("getDhcp6Leaf entered.");
442 while (dhcp6Parent != null) {
443 dhcp6Child = dhcp6PacketFromRelayPacket(dhcp6Parent);
444
445 if (dhcp6Child != null) {
446 if (dhcp6Child.getMsgType() != DHCP6.MsgType.RELAY_FORW.value() &&
447 dhcp6Child.getMsgType() != DHCP6.MsgType.RELAY_REPL.value()) {
448 log.debug("leaf dhcp6 packet found.");
449 break;
450 } else {
451 // found another relay
452 // go for another loop
453 dhcp6Parent = dhcp6Child;
454 }
455 } else {
Yi Tseng68ef26b2017-12-18 17:10:00 -0800456 log.warn("Expected dhcp6 within relay pkt, but no dhcp6 leaf found.");
Kalhee Kim45fede42017-09-05 19:05:06 +0000457 break;
458 }
459 }
460 return dhcp6Child;
461 }
462
463 /**
464 * check if DHCP6 relay-reply is reply.
465 *
466 * @param relayPacket dhcp6 relay-reply
467 * @return boolean relay-reply contains ack
468 */
469 private boolean isDhcp6Reply(DHCP6 relayPacket) {
470 log.debug("isDhcp6Reply entered.");
471
472 DHCP6 leafDhcp6 = getDhcp6Leaf(relayPacket);
473
474 if (leafDhcp6 != null) {
475 if (leafDhcp6.getMsgType() == DHCP6.MsgType.REPLY.value()) {
476 log.debug("isDhcp6Reply true.");
477 return true; // must be directly connected
478 } else {
479 log.debug("isDhcp6Reply false. leaf dhcp6 is not replay. MsgType {}", leafDhcp6.getMsgType());
480 }
481 } else {
482 log.debug("isDhcp6Reply false. Expected dhcp6 within relay pkt but not found.");
483 }
484 log.debug("isDhcp6Reply false.");
485 return false;
486 }
487
488 /**
489 * check if DHCP6 is release or relay-forward contains release.
490 *
491 * @param dhcp6Payload dhcp6 packet
492 * @return boolean dhcp6 contains release
493 */
494 private boolean isDhcp6Release(DHCP6 dhcp6Payload) {
495
496 log.debug("isDhcp6Release entered.");
497
498 if (dhcp6Payload.getMsgType() == DHCP6.MsgType.RELEASE.value()) {
499 log.debug("isDhcp6Release true.");
500 return true; // must be directly connected
501 } else {
502 DHCP6 dhcp6Leaf = getDhcp6Leaf(dhcp6Payload);
503 if (dhcp6Leaf != null) {
504 if (dhcp6Leaf.getMsgType() == DHCP6.MsgType.RELEASE.value()) {
505 log.debug("isDhcp6Release true. indirectlry connected");
506 return true;
507 } else {
508 log.debug("leaf dhcp6 is not release. MsgType {}", dhcp6Leaf.getMsgType());
509 return false;
510 }
511 } else {
512 log.debug("isDhcp6Release false. dhcp6 is niether relay nor release.");
513 return false;
514 }
515 }
516 }
517
518 /**
519 * extract from dhcp6 packet client ipv6 address of given by dhcp server.
520 *
521 * @param dhcp6 the dhcp6 packet
522 * @return Ip6Address Ip6Address given by dhcp server, or null if not exists
523 */
524 private Ip6Address extractIpAddress(DHCP6 dhcp6) {
525 Ip6Address ip = null;
526
527 log.debug("extractIpAddress enters dhcp6 {}.", dhcp6);
528 // Extract IPv6 address from IA NA ot IA TA option
529 Optional<Dhcp6IaNaOption> iaNaOption = dhcp6.getOptions()
530 .stream()
531 .filter(opt -> opt instanceof Dhcp6IaNaOption)
532 .map(opt -> (Dhcp6IaNaOption) opt)
533 .findFirst();
534 Optional<Dhcp6IaTaOption> iaTaOption = dhcp6.getOptions()
535 .stream()
536 .filter(opt -> opt instanceof Dhcp6IaTaOption)
537 .map(opt -> (Dhcp6IaTaOption) opt)
538 .findFirst();
539 Optional<Dhcp6IaAddressOption> iaAddressOption;
540 if (iaNaOption.isPresent()) {
541 log.debug("Found IPv6 address from iaNaOption {}", iaNaOption);
542
543 iaAddressOption = iaNaOption.get().getOptions().stream()
544 .filter(opt -> opt instanceof Dhcp6IaAddressOption)
545 .map(opt -> (Dhcp6IaAddressOption) opt)
546 .findFirst();
547 } else if (iaTaOption.isPresent()) {
548 log.debug("Found IPv6 address from iaTaOption {}", iaTaOption);
549
550 iaAddressOption = iaTaOption.get().getOptions().stream()
551 .filter(opt -> opt instanceof Dhcp6IaAddressOption)
552 .map(opt -> (Dhcp6IaAddressOption) opt)
553 .findFirst();
554 } else {
555 iaAddressOption = Optional.empty();
556 }
557 if (iaAddressOption.isPresent()) {
558 ip = iaAddressOption.get().getIp6Address();
559 log.debug("Found IPv6 address from iaAddressOption {}", iaAddressOption);
560
561
562 } else {
563 log.debug("Can't find IPv6 address from DHCPv6 {}", dhcp6);
564 }
565
566 return ip;
567 }
568 /**
569 * extract from dhcp6 packet Prefix prefix provided by dhcp server.
570 *
571 * @param dhcp6 the dhcp6 payload
572 * @return IpPrefix Prefix Delegation prefix, or null if not exists.
573 */
574 private IpPrefix extractPrefix(DHCP6 dhcp6) {
Yi Tseng68ef26b2017-12-18 17:10:00 -0800575 log.trace("extractPrefix enters {}", dhcp6);
Kalhee Kim45fede42017-09-05 19:05:06 +0000576
577 // extract prefix
578 IpPrefix prefixPrefix = null;
579
580 Ip6Address prefixAddress = null;
581
582 // Extract IPv6 prefix from IA PD option
583 Optional<Dhcp6IaPdOption> iaPdOption = dhcp6.getOptions()
584 .stream()
585 .filter(opt -> opt instanceof Dhcp6IaPdOption)
586 .map(opt -> (Dhcp6IaPdOption) opt)
587 .findFirst();
588
589 Optional<Dhcp6IaPrefixOption> iaPrefixOption;
590 if (iaPdOption.isPresent()) {
Yi Tseng68ef26b2017-12-18 17:10:00 -0800591 log.trace("IA_PD option found {}", iaPdOption);
Kalhee Kim45fede42017-09-05 19:05:06 +0000592
593 iaPrefixOption = iaPdOption.get().getOptions().stream()
594 .filter(opt -> opt instanceof Dhcp6IaPrefixOption)
595 .map(opt -> (Dhcp6IaPrefixOption) opt)
596 .findFirst();
597 } else {
Yi Tseng68ef26b2017-12-18 17:10:00 -0800598 log.trace("IA_PD option NOT found");
Kalhee Kim45fede42017-09-05 19:05:06 +0000599
600 iaPrefixOption = Optional.empty();
601 }
602 if (iaPrefixOption.isPresent()) {
Yi Tseng68ef26b2017-12-18 17:10:00 -0800603 log.trace("IAPrefix Option within IA_PD option found {}", iaPrefixOption);
Kalhee Kim45fede42017-09-05 19:05:06 +0000604
605 prefixAddress = iaPrefixOption.get().getIp6Prefix();
606 int prefixLen = (int) iaPrefixOption.get().getPrefixLength();
Yi Tseng68ef26b2017-12-18 17:10:00 -0800607 log.trace("Prefix length is {} bits", prefixLen);
Kalhee Kim45fede42017-09-05 19:05:06 +0000608 prefixPrefix = IpPrefix.valueOf(prefixAddress, prefixLen);
Kalhee Kim45fede42017-09-05 19:05:06 +0000609 } else {
Yi Tseng68ef26b2017-12-18 17:10:00 -0800610 log.trace("Can't find IPv6 prefix from DHCPv6 {}", dhcp6);
Kalhee Kim45fede42017-09-05 19:05:06 +0000611 }
612
613 return prefixPrefix;
614 }
615
616 /**
617 * remove host or route.
618 *
619 * @param directConnFlag flag to show that packet is from directly connected client
620 * @param dhcp6Packet the dhcp6 payload
621 * @param clientPacket client's ethernet packet
622 * @param clientIpv6 client's Ipv6 packet
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +0000623 * @param clientInterface client interfaces
Kalhee Kim45fede42017-09-05 19:05:06 +0000624 */
625 private void removeHostOrRoute(boolean directConnFlag, DHCP6 dhcp6Packet,
626 Ethernet clientPacket, IPv6 clientIpv6,
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +0000627 Interface clientInterface) {
Kalhee Kim45fede42017-09-05 19:05:06 +0000628 log.debug("extractPrefix enters {}", dhcp6Packet);
Kalhee Kim121ba922017-11-01 17:56:44 +0000629 VlanId vlanId = clientInterface.vlan();
630 MacAddress clientMac = clientPacket.getSourceMAC();
631 log.debug("client mac {} client vlan {}", HexString.toHexString(clientMac.toBytes(), ":"), vlanId);
632
Kalhee Kim45fede42017-09-05 19:05:06 +0000633 // add host or route
634 if (isDhcp6Release(dhcp6Packet)) {
635 IpAddress ip = null;
636 if (directConnFlag) {
637 // Add to host store if it is connected to network directly
638 ip = extractIpAddress(dhcp6Packet);
639 if (ip != null) {
Kalhee Kim121ba922017-11-01 17:56:44 +0000640
Kalhee Kim45fede42017-09-05 19:05:06 +0000641 HostId hostId = HostId.hostId(clientMac, vlanId);
642 log.debug("remove Host {} ip for directly connected.", hostId.toString());
Kalhee Kim45fede42017-09-05 19:05:06 +0000643 // Remove host's ip of when dhcp release msg is received
Yi Tsengaa417a62017-09-08 17:22:51 -0700644 providerService.removeIpFromHost(hostId, ip);
Kalhee Kim45fede42017-09-05 19:05:06 +0000645 } else {
646 log.debug("ipAddress not found. Do not add Host for directly connected.");
647 }
648 } else {
649 // Remove from route store if it is not connected to network directly
Kalhee Kim121ba922017-11-01 17:56:44 +0000650 // pick out the first link-local ip address
651 IpAddress nextHopIp = getFirstIpByHost(clientMac, vlanId);
652 if (nextHopIp == null) {
653 log.warn("Can't find link-local IP address of gateway mac {} vlanId {}",
654 clientMac, vlanId);
655 return;
656 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000657
658 DHCP6 leafDhcp = getDhcp6Leaf(dhcp6Packet);
659 ip = extractIpAddress(leafDhcp);
660 if (ip == null) {
661 log.debug("ip is null");
662 } else {
663 Route routeForIP = new Route(Route.Source.STATIC, ip.toIpPrefix(), nextHopIp);
664 log.debug("removing route of 128 address for indirectly connected.");
665 log.debug("128 ip {}, nexthop {}", HexString.toHexString(ip.toOctets(), ":"),
666 HexString.toHexString(nextHopIp.toOctets(), ":"));
667 routeStore.removeRoute(routeForIP);
668 }
669
670 IpPrefix ipPrefix = extractPrefix(leafDhcp);
671 if (ipPrefix == null) {
672 log.debug("ipPrefix is null ");
673 } else {
674 Route routeForPrefix = new Route(Route.Source.STATIC, ipPrefix, nextHopIp);
675 log.debug("removing route of PD for indirectly connected.");
676 log.debug("pd ip {}, nexthop {}", HexString.toHexString(ipPrefix.address().toOctets(), ":"),
677 HexString.toHexString(nextHopIp.toOctets(), ":"));
678
679 routeStore.removeRoute(routeForPrefix);
Kalhee Kimba366062017-11-07 16:32:09 +0000680 if (this.dhcpFpmEnabled) {
681 dhcpFpmPrefixStore.removeFpmRecord(ipPrefix);
682 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000683 }
684 }
685 }
686 }
687
688 /**
689 * add host or route.
690 *
691 * @param directConnFlag flag to show that packet is from directly connected client
692 * @param dhcp6Relay the dhcp6 payload
693 * @param embeddedDhcp6 client's ethernet packetthe dhcp6 payload within relay
694 * @param clientMac client macAddress
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +0000695 * @param clientInterface client interface
Kalhee Kim45fede42017-09-05 19:05:06 +0000696 */
697 private void addHostOrRoute(boolean directConnFlag, DHCP6 dhcp6Relay,
698 DHCP6 embeddedDhcp6,
699 MacAddress clientMac,
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +0000700 Interface clientInterface) {
Kalhee Kim45fede42017-09-05 19:05:06 +0000701 log.debug("addHostOrRoute entered.");
Kalhee Kim121ba922017-11-01 17:56:44 +0000702 VlanId vlanId = clientInterface.vlan();
Kalhee Kim45fede42017-09-05 19:05:06 +0000703 // add host or route
704 if (isDhcp6Reply(dhcp6Relay)) {
705 IpAddress ip = null;
706 if (directConnFlag) {
707 // Add to host store if it connect to network directly
708 ip = extractIpAddress(embeddedDhcp6);
709 if (ip != null) {
710 Set<IpAddress> ips = Sets.newHashSet(ip);
Yi Tsengaa417a62017-09-08 17:22:51 -0700711
712 // FIXME: we should use vlan id from original packet (solicit, request)
Kalhee Kim45fede42017-09-05 19:05:06 +0000713 HostId hostId = HostId.hostId(clientMac, vlanId);
Yi Tsengaa417a62017-09-08 17:22:51 -0700714 Host host = hostService.getHost(hostId);
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +0000715 HostLocation hostLocation = new HostLocation(clientInterface.connectPoint(),
Yi Tsengaa417a62017-09-08 17:22:51 -0700716 System.currentTimeMillis());
717 Set<HostLocation> hostLocations = Sets.newHashSet(hostLocation);
718
719 if (host != null) {
720 // Dual homing support:
721 // if host exists, use old locations and new location
722 hostLocations.addAll(host.locations());
723 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000724 HostDescription desc = new DefaultHostDescription(clientMac, vlanId,
Yi Tsengaa417a62017-09-08 17:22:51 -0700725 hostLocations, ips,
726 false);
Kalhee Kim45fede42017-09-05 19:05:06 +0000727 log.debug("adding Host for directly connected.");
728 log.debug("client mac {} client vlan {} hostlocation {}",
729 HexString.toHexString(clientMac.toBytes(), ":"),
730 vlanId, hostLocation.toString());
731
732 // Replace the ip when dhcp server give the host new ip address
Yi Tsengaa417a62017-09-08 17:22:51 -0700733 providerService.hostDetected(hostId, desc, false);
Kalhee Kim45fede42017-09-05 19:05:06 +0000734 } else {
Yi Tseng68ef26b2017-12-18 17:10:00 -0800735 log.debug("ipAddress not found. Do not add Host for directly connected.");
Kalhee Kim45fede42017-09-05 19:05:06 +0000736 }
737 } else {
738 // Add to route store if it does not connect to network directly
Kalhee Kim121ba922017-11-01 17:56:44 +0000739 // pick out the first link-local ip address
740 IpAddress nextHopIp = getFirstIpByHost(clientMac, vlanId);
741 if (nextHopIp == null) {
742 log.warn("Can't find link-local IP address of gateway mac {} vlanId {}",
743 clientMac, vlanId);
744 return;
745 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000746
747 DHCP6 leafDhcp = getDhcp6Leaf(embeddedDhcp6);
748 ip = extractIpAddress(leafDhcp);
749 if (ip == null) {
Yi Tseng68ef26b2017-12-18 17:10:00 -0800750 log.debug("ip is null");
Kalhee Kim45fede42017-09-05 19:05:06 +0000751 } else {
752 Route routeForIP = new Route(Route.Source.STATIC, ip.toIpPrefix(), nextHopIp);
Yi Tseng68ef26b2017-12-18 17:10:00 -0800753 log.debug("adding Route of 128 address for indirectly connected.");
Kalhee Kim45fede42017-09-05 19:05:06 +0000754 routeStore.updateRoute(routeForIP);
755 }
756
757 IpPrefix ipPrefix = extractPrefix(leafDhcp);
758 if (ipPrefix == null) {
Yi Tseng68ef26b2017-12-18 17:10:00 -0800759 log.debug("ipPrefix is null ");
Kalhee Kim45fede42017-09-05 19:05:06 +0000760 } else {
761 Route routeForPrefix = new Route(Route.Source.STATIC, ipPrefix, nextHopIp);
Yi Tseng68ef26b2017-12-18 17:10:00 -0800762 log.debug("adding Route of PD for indirectly connected.");
Kalhee Kim45fede42017-09-05 19:05:06 +0000763 routeStore.updateRoute(routeForPrefix);
Kalhee Kimba366062017-11-07 16:32:09 +0000764 if (this.dhcpFpmEnabled) {
765 FpmRecord record = new FpmRecord(ipPrefix, nextHopIp, FpmRecord.Type.DHCP_RELAY);
766 dhcpFpmPrefixStore.addFpmRecord(ipPrefix, record);
767 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000768 }
769 }
770 }
771 }
772
773 /**
Yi Tseng25bfe372017-11-03 16:27:32 -0700774 * Build the DHCP6 solicit/request packet with gatewayip.
775 * TODO: method too long, need to be refactored.
Kalhee Kim45fede42017-09-05 19:05:06 +0000776 *
777 * @param context packet context
778 * @param clientPacket client ethernet packet
779 * @param clientInterfaces set of client side interfaces
780 */
Yi Tseng3bd57ac2017-11-29 14:39:18 -0800781 private InternalPacket processDhcp6PacketFromClient(PacketContext context,
782 Ethernet clientPacket, Set<Interface> clientInterfaces) {
783 ConnectPoint receivedFrom = context.inPacket().receivedFrom();
784 DeviceId receivedFromDevice = receivedFrom.deviceId();
785 DhcpServerInfo serverInfo;
786 Ip6Address dhcpServerIp = null;
787 ConnectPoint dhcpServerConnectPoint = null;
788 MacAddress dhcpConnectMac = null;
789 VlanId dhcpConnectVlan = null;
790 Ip6Address dhcpGatewayIp = null;
791 Ip6Address indirectDhcpServerIp = null;
792 ConnectPoint indirectDhcpServerConnectPoint = null;
793 MacAddress indirectDhcpConnectMac = null;
794 VlanId indirectDhcpConnectVlan = null;
795 Ip6Address indirectDhcpGatewayIp = null;
796 Ip6Address indirectRelayAgentIpFromCfg = null;
797 if (!defaultServerInfoList.isEmpty()) {
798 serverInfo = defaultServerInfoList.get(0);
799 dhcpConnectMac = serverInfo.getDhcpConnectMac().orElse(null);
800 dhcpGatewayIp = serverInfo.getDhcpGatewayIp6().orElse(null);
801 dhcpServerIp = serverInfo.getDhcpServerIp6().orElse(null);
802 dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
803 dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
804 }
805 if (!indirectServerInfoList.isEmpty()) {
806 serverInfo = indirectServerInfoList.get(0);
807 indirectDhcpConnectMac = serverInfo.getDhcpConnectMac().orElse(null);
808 indirectDhcpGatewayIp = serverInfo.getDhcpGatewayIp6().orElse(null);
809 indirectDhcpServerIp = serverInfo.getDhcpServerIp6().orElse(null);
810 indirectDhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
811 indirectDhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
812 indirectRelayAgentIpFromCfg = serverInfo.getRelayAgentIp6(receivedFromDevice).orElse(null);
813 }
814 Ip6Address relayAgentIp = getRelayAgentIPv6Address(clientInterfaces);
815 MacAddress relayAgentMac = clientInterfaces.iterator().next().mac();
816 if (relayAgentIp == null || relayAgentMac == null) {
817 log.warn("Missing DHCP relay agent interface Ipv6 addr config for "
818 + "packet from client on port: {}. Aborting packet processing",
819 clientInterfaces.iterator().next().connectPoint());
820 return null;
821 }
822 // get dhcp6 header.
823 IPv6 clientIpv6 = (IPv6) clientPacket.getPayload();
824 UDP clientUdp = (UDP) clientIpv6.getPayload();
825 DHCP6 clientDhcp6 = (DHCP6) clientUdp.getPayload();
826 boolean directConnFlag = directlyConnected(clientDhcp6);
827 Interface serverInterface;
828 if (directConnFlag) {
829 serverInterface = getServerInterface();
830 } else {
831 serverInterface = getIndirectServerInterface();
832 if (serverInterface == null) {
833 // Indirect server interface not found, use default server interface
834 serverInterface = getServerInterface();
835 }
836 }
837 if (serverInterface == null) {
838 log.warn("Can't get {} server interface, ignore", directConnFlag ? "direct" : "indirect");
839 return null;
840 }
841 Ip6Address ipFacingServer = getFirstIpFromInterface(serverInterface);
842 MacAddress macFacingServer = serverInterface.mac();
843 if (ipFacingServer == null || macFacingServer == null) {
844 log.warn("No IP v6 address for server Interface {}", serverInterface);
845 return null;
846 }
Yi Tseng68ef26b2017-12-18 17:10:00 -0800847 Ethernet etherReply = clientPacket.duplicate();
Yi Tseng3bd57ac2017-11-29 14:39:18 -0800848 etherReply.setSourceMACAddress(macFacingServer);
849 if ((directConnFlag && dhcpConnectMac == null) ||
850 !directConnFlag && indirectDhcpConnectMac == null && dhcpConnectMac == null) {
Yi Tseng68ef26b2017-12-18 17:10:00 -0800851 log.trace("Packet received from {} connected client.", directConnFlag ? "directly" : "indirectly");
852 log.debug("DHCP6 {} not yet resolved .. Aborting DHCP packet processing from client on port: {}",
Yi Tseng3bd57ac2017-11-29 14:39:18 -0800853 (dhcpGatewayIp == null) ? "server IP " + dhcpServerIp
854 : "gateway IP " + dhcpGatewayIp,
855 clientInterfaces.iterator().next().connectPoint());
856 return null;
857 }
858 if (dhcpServerConnectPoint == null) {
859 log.warn("DHCP6 server connection point direct {} directConn {} indirectConn {} is not set up yet",
Yi Tseng25bfe372017-11-03 16:27:32 -0700860 directConnFlag, dhcpServerConnectPoint, indirectDhcpServerConnectPoint);
Kalhee Kim121ba922017-11-01 17:56:44 +0000861 return null;
862 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000863
Yi Tseng25bfe372017-11-03 16:27:32 -0700864 etherReply.setDestinationMACAddress(dhcpConnectMac);
865 etherReply.setVlanID(dhcpConnectVlan.toShort());
Kalhee Kim121ba922017-11-01 17:56:44 +0000866 IPv6 ipv6Packet = (IPv6) etherReply.getPayload();
867 byte[] peerAddress = clientIpv6.getSourceAddress();
868 ipv6Packet.setSourceAddress(ipFacingServer.toOctets());
Yi Tseng25bfe372017-11-03 16:27:32 -0700869 ipv6Packet.setDestinationAddress(dhcpServerIp.toOctets());
Kalhee Kim121ba922017-11-01 17:56:44 +0000870 UDP udpPacket = (UDP) ipv6Packet.getPayload();
871 udpPacket.setSourcePort(UDP.DHCP_V6_SERVER_PORT);
872 DHCP6 dhcp6Packet = (DHCP6) udpPacket.getPayload();
873 byte[] dhcp6PacketByte = dhcp6Packet.serialize();
Charles Chana990ce92017-10-30 10:22:50 -0700874
Kalhee Kim121ba922017-11-01 17:56:44 +0000875 ConnectPoint clientConnectionPoint = context.inPacket().receivedFrom();
876 VlanId vlanIdInUse = VlanId.vlanId(clientPacket.getVlanID());
877 Interface clientInterface = interfaceService.getInterfacesByPort(clientConnectionPoint)
878 .stream().filter(iface -> interfaceContainsVlan(iface, vlanIdInUse))
879 .findFirst().orElse(null);
Kalhee Kim45fede42017-09-05 19:05:06 +0000880
Kalhee Kim121ba922017-11-01 17:56:44 +0000881 removeHostOrRoute(directConnFlag, dhcp6Packet, clientPacket, clientIpv6, clientInterface);
Kalhee Kim45b24182017-10-18 18:30:23 +0000882
Kalhee Kim121ba922017-11-01 17:56:44 +0000883 DHCP6 dhcp6Relay = new DHCP6();
884 dhcp6Relay.setMsgType(DHCP6.MsgType.RELAY_FORW.value());
Kalhee Kim121ba922017-11-01 17:56:44 +0000885 if (directConnFlag) {
886 dhcp6Relay.setLinkAddress(relayAgentIp.toOctets());
887 log.debug("direct connection: relayAgentIp obtained dynamically {}",
888 HexString.toHexString(relayAgentIp.toOctets(), ":"));
Kalhee Kim45fede42017-09-05 19:05:06 +0000889
Kalhee Kim121ba922017-11-01 17:56:44 +0000890 } else {
Yi Tseng25bfe372017-11-03 16:27:32 -0700891 if (indirectDhcpServerIp == null) {
Yi Tseng68ef26b2017-12-18 17:10:00 -0800892 log.debug("indirect DhcpServerIp not available, use default DhcpServerIp {}",
Yi Tseng25bfe372017-11-03 16:27:32 -0700893 HexString.toHexString(dhcpServerIp.toOctets()));
Kalhee Kimd21029f2017-09-26 20:21:53 +0000894 } else {
895 // Indirect case, replace destination to indirect dhcp server if exist
896 // Check if mac is obtained for valid server ip
Yi Tseng25bfe372017-11-03 16:27:32 -0700897 if (indirectDhcpConnectMac == null) {
Kalhee Kimd21029f2017-09-26 20:21:53 +0000898 log.warn("DHCP6 {} not yet resolved .. Aborting DHCP "
Yi Tseng25bfe372017-11-03 16:27:32 -0700899 + "packet processing from client on port: {}",
900 (indirectDhcpGatewayIp == null) ? "server IP " + indirectDhcpServerIp
901 : "gateway IP " + indirectDhcpGatewayIp,
902 clientInterfaces.iterator().next().connectPoint());
Kalhee Kimd21029f2017-09-26 20:21:53 +0000903 return null;
904 }
Yi Tseng25bfe372017-11-03 16:27:32 -0700905 etherReply.setDestinationMACAddress(indirectDhcpConnectMac);
906 etherReply.setVlanID(indirectDhcpConnectVlan.toShort());
907 ipv6Packet.setDestinationAddress(indirectDhcpServerIp.toOctets());
Kalhee Kimd21029f2017-09-26 20:21:53 +0000908 }
Yi Tseng25bfe372017-11-03 16:27:32 -0700909 if (indirectRelayAgentIpFromCfg == null) {
Kalhee Kim45fede42017-09-05 19:05:06 +0000910 dhcp6Relay.setLinkAddress(relayAgentIp.toOctets());
Yi Tseng68ef26b2017-12-18 17:10:00 -0800911 log.trace("indirect connection: relayAgentIp NOT availale from config file! Use dynamic. {}",
Yi Tseng25bfe372017-11-03 16:27:32 -0700912 HexString.toHexString(relayAgentIp.toOctets(), ":"));
913 } else {
914 dhcp6Relay.setLinkAddress(indirectRelayAgentIpFromCfg.toOctets());
Yi Tseng68ef26b2017-12-18 17:10:00 -0800915 log.trace("indirect connection: relayAgentIp from config file is available! {}",
Yi Tseng25bfe372017-11-03 16:27:32 -0700916 HexString.toHexString(indirectRelayAgentIpFromCfg.toOctets(), ":"));
917 }
918 }
919 // peer address: address of the client or relay agent from which
920 // the message to be relayed was received.
921 dhcp6Relay.setPeerAddress(peerAddress);
922 List<Dhcp6Option> options = new ArrayList<>();
923 // directly connected case, hop count is zero; otherwise, hop count + 1
924 if (directConnFlag) {
925 dhcp6Relay.setHopCount((byte) 0);
926 } else {
927 dhcp6Relay.setHopCount((byte) (dhcp6Packet.getHopCount() + 1));
928 }
929 // create relay message option
930 Dhcp6Option relayMessage = new Dhcp6Option();
931 relayMessage.setCode(DHCP6.OptionCode.RELAY_MSG.value());
932 relayMessage.setLength((short) dhcp6PacketByte.length);
933 relayMessage.setData(dhcp6PacketByte);
934 options.add(relayMessage);
935 // create interfaceId option
936 String inPortString = "-" + context.inPacket().receivedFrom().toString() + ":";
937 Dhcp6Option interfaceId = new Dhcp6Option();
938 interfaceId.setCode(DHCP6.OptionCode.INTERFACE_ID.value());
939 byte[] clientSoureMacBytes = clientPacket.getSourceMACAddress();
940 byte[] inPortStringBytes = inPortString.getBytes();
941 byte[] vlanIdBytes = new byte[2];
942 vlanIdBytes[0] = (byte) (clientPacket.getVlanID() & 0xff);
943 vlanIdBytes[1] = (byte) ((clientPacket.getVlanID() >> 8) & 0xff);
944 byte[] interfaceIdBytes = new byte[clientSoureMacBytes.length +
945 inPortStringBytes.length + vlanIdBytes.length];
Yi Tseng68ef26b2017-12-18 17:10:00 -0800946 log.trace("Length: interfaceIdBytes {} clientSoureMacBytes {} inPortStringBytes {} vlan {}",
Yi Tseng25bfe372017-11-03 16:27:32 -0700947 interfaceIdBytes.length, clientSoureMacBytes.length, inPortStringBytes.length,
948 vlanIdBytes.length);
949 System.arraycopy(clientSoureMacBytes, 0, interfaceIdBytes, 0, clientSoureMacBytes.length);
950 System.arraycopy(inPortStringBytes, 0, interfaceIdBytes, clientSoureMacBytes.length, inPortStringBytes.length);
951 System.arraycopy(vlanIdBytes, 0, interfaceIdBytes, clientSoureMacBytes.length + inPortStringBytes.length,
952 vlanIdBytes.length);
953 interfaceId.setData(interfaceIdBytes);
954 interfaceId.setLength((short) interfaceIdBytes.length);
955 options.add(interfaceId);
956 log.debug("interfaceId write srcMac {} portString {}",
957 HexString.toHexString(clientSoureMacBytes, ":"), inPortString);
958 dhcp6Relay.setOptions(options);
959 udpPacket.setPayload(dhcp6Relay);
960 udpPacket.resetChecksum();
961 ipv6Packet.setPayload(udpPacket);
962 ipv6Packet.setHopLimit((byte) 64);
963 etherReply.setPayload(ipv6Packet);
964 if (directConnFlag || indirectDhcpServerIp == null) {
965 return new InternalPacket(etherReply, dhcpServerConnectPoint);
966 } else {
967 return new InternalPacket(etherReply, indirectDhcpServerConnectPoint);
968 }
969 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000970
971 /**
972 *
973 * process the DHCP6 relay-reply packet from dhcp server.
974 *
975 * @param context packet context
976 * @param receivedPacket server ethernet packet
977 * @param recevingInterfaces set of server side interfaces
978 */
979 private InternalPacket processDhcp6PacketFromServer(PacketContext context,
980 Ethernet receivedPacket, Set<Interface> recevingInterfaces) {
Yi Tseng25bfe372017-11-03 16:27:32 -0700981
982 ConnectPoint receivedFrom = context.inPacket().receivedFrom();
983 DeviceId receivedFromDevice = receivedFrom.deviceId();
984
985 // TODO: refactor
986 DhcpServerInfo serverInfo;
987 Ip6Address dhcpServerIp = null;
988 ConnectPoint dhcpServerConnectPoint = null;
989 MacAddress dhcpConnectMac = null;
990 VlanId dhcpConnectVlan = null;
991 Ip6Address dhcpGatewayIp = null;
992
993 Ip6Address indirectDhcpServerIp = null;
994 ConnectPoint indirectDhcpServerConnectPoint = null;
995 MacAddress indirectDhcpConnectMac = null;
996 VlanId indirectDhcpConnectVlan = null;
997 Ip6Address indirectDhcpGatewayIp = null;
998 Ip6Address indirectRelayAgentIpFromCfg = null;
999
1000 if (!defaultServerInfoList.isEmpty()) {
1001 serverInfo = defaultServerInfoList.get(0);
1002 dhcpConnectMac = serverInfo.getDhcpConnectMac().orElse(null);
1003 dhcpGatewayIp = serverInfo.getDhcpGatewayIp6().orElse(null);
1004 dhcpServerIp = serverInfo.getDhcpServerIp6().orElse(null);
1005 dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1006 dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1007 }
1008
1009 if (!indirectServerInfoList.isEmpty()) {
1010 serverInfo = indirectServerInfoList.get(0);
1011 indirectDhcpConnectMac = serverInfo.getDhcpConnectMac().orElse(null);
1012 indirectDhcpGatewayIp = serverInfo.getDhcpGatewayIp6().orElse(null);
1013 indirectDhcpServerIp = serverInfo.getDhcpServerIp6().orElse(null);
1014 indirectDhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1015 indirectDhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1016 indirectRelayAgentIpFromCfg = serverInfo.getRelayAgentIp6(receivedFromDevice).orElse(null);
1017 }
1018
Kalhee Kim45fede42017-09-05 19:05:06 +00001019 // get dhcp6 header.
Ray Milkeyf0c47612017-09-28 11:29:38 -07001020 Ethernet etherReply = receivedPacket.duplicate();
Kalhee Kim45fede42017-09-05 19:05:06 +00001021 IPv6 ipv6Packet = (IPv6) etherReply.getPayload();
1022 UDP udpPacket = (UDP) ipv6Packet.getPayload();
1023 DHCP6 dhcp6Relay = (DHCP6) udpPacket.getPayload();
1024
1025 Boolean directConnFlag = directlyConnected(dhcp6Relay);
Kalhee Kimd21029f2017-09-26 20:21:53 +00001026 ConnectPoint inPort = context.inPacket().receivedFrom();
Ray Milkeyfe0e0852018-01-18 11:14:05 -08001027 if ((directConnFlag || indirectDhcpServerIp == null)
Yi Tseng25bfe372017-11-03 16:27:32 -07001028 && !inPort.equals(dhcpServerConnectPoint)) {
Kalhee Kimd21029f2017-09-26 20:21:53 +00001029 log.warn("Receiving port {} is not the same as server connect point {} for direct or indirect-null",
Yi Tseng25bfe372017-11-03 16:27:32 -07001030 inPort, dhcpServerConnectPoint);
Kalhee Kimd21029f2017-09-26 20:21:53 +00001031 return null;
1032 }
1033
Yi Tseng25bfe372017-11-03 16:27:32 -07001034 if (!directConnFlag && indirectDhcpServerIp != null &&
1035 !inPort.equals(indirectDhcpServerConnectPoint)) {
Kalhee Kimd21029f2017-09-26 20:21:53 +00001036 log.warn("Receiving port {} is not the same as server connect point {} for indirect",
Yi Tseng25bfe372017-11-03 16:27:32 -07001037 inPort, indirectDhcpServerConnectPoint);
Kalhee Kimd21029f2017-09-26 20:21:53 +00001038 return null;
1039 }
1040
Kalhee Kim45fede42017-09-05 19:05:06 +00001041
1042 Dhcp6InterfaceIdOption interfaceIdOption = dhcp6Relay.getOptions().stream()
1043 .filter(opt -> opt instanceof Dhcp6InterfaceIdOption)
1044 .map(opt -> (Dhcp6InterfaceIdOption) opt)
1045 .findFirst()
1046 .orElse(null);
1047
1048 if (interfaceIdOption == null) {
1049 log.warn("Interface Id option is not present, abort packet...");
1050 return null;
1051 }
1052
1053 MacAddress peerMac = interfaceIdOption.getMacAddress();
1054 String clientConnectionPointStr = new String(interfaceIdOption.getInPort());
1055
1056 ConnectPoint clientConnectionPoint = ConnectPoint.deviceConnectPoint(clientConnectionPointStr);
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +00001057 VlanId vlanIdInUse = VlanId.vlanId(interfaceIdOption.getVlanId());
1058 Interface clientInterface = interfaceService.getInterfacesByPort(clientConnectionPoint)
1059 .stream()
1060 .filter(iface -> interfaceContainsVlan(iface, vlanIdInUse))
1061 .findFirst()
1062 .orElse(null);
1063 if (clientInterface == null) {
1064 log.warn("Cannot get client interface for from packet, abort... vlan {}", vlanIdInUse.toString());
Kalhee Kim45fede42017-09-05 19:05:06 +00001065 return null;
1066 }
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +00001067 MacAddress relayAgentMac = clientInterface.mac();
Kalhee Kim45fede42017-09-05 19:05:06 +00001068 if (relayAgentMac == null) {
Yi Tseng68ef26b2017-12-18 17:10:00 -08001069 log.warn("Can not get client interface mac, abort packet..");
Kalhee Kim45fede42017-09-05 19:05:06 +00001070 return null;
1071 }
1072 etherReply.setSourceMACAddress(relayAgentMac);
1073
1074 // find destMac
Yi Tseng68ef26b2017-12-18 17:10:00 -08001075 MacAddress clientMac;
Yi Tsengaa417a62017-09-08 17:22:51 -07001076 Ip6Address peerAddress = Ip6Address.valueOf(dhcp6Relay.getPeerAddress());
1077 Set<Host> clients = hostService.getHostsByIp(peerAddress);
Kalhee Kim45fede42017-09-05 19:05:06 +00001078 if (clients.isEmpty()) {
Yi Tseng68ef26b2017-12-18 17:10:00 -08001079 log.trace("There's no host found for this address {}",
Kalhee Kim45fede42017-09-05 19:05:06 +00001080 HexString.toHexString(dhcp6Relay.getPeerAddress(), ":"));
Yi Tseng68ef26b2017-12-18 17:10:00 -08001081 log.trace("Let's look up interfaceId {}", HexString.toHexString(peerMac.toBytes(), ":"));
Kalhee Kim45fede42017-09-05 19:05:06 +00001082 clientMac = peerMac;
1083 } else {
1084 clientMac = clients.iterator().next().mac();
1085 if (clientMac == null) {
1086 log.warn("No client mac address found, abort packet...");
1087 return null;
1088 }
Yi Tseng68ef26b2017-12-18 17:10:00 -08001089 log.trace("Client mac address found from getHostByIp");
Kalhee Kim45fede42017-09-05 19:05:06 +00001090
1091 }
1092 etherReply.setDestinationMACAddress(clientMac);
1093
1094 // ip header
1095 ipv6Packet.setSourceAddress(dhcp6Relay.getLinkAddress());
1096 ipv6Packet.setDestinationAddress(dhcp6Relay.getPeerAddress());
1097 // udp header
1098 udpPacket.setSourcePort(UDP.DHCP_V6_SERVER_PORT);
1099 if (directConnFlag) {
1100 udpPacket.setDestinationPort(UDP.DHCP_V6_CLIENT_PORT);
1101 } else {
1102 udpPacket.setDestinationPort(UDP.DHCP_V6_SERVER_PORT);
1103 }
1104
1105 DHCP6 embeddedDhcp6 = dhcp6Relay.getOptions().stream()
1106 .filter(opt -> opt instanceof Dhcp6RelayOption)
1107 .map(BasePacket::getPayload)
1108 .map(pld -> (DHCP6) pld)
1109 .findFirst()
1110 .orElse(null);
1111
1112
1113 // add host or route
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +00001114 addHostOrRoute(directConnFlag, dhcp6Relay, embeddedDhcp6, clientMac, clientInterface);
Kalhee Kim45fede42017-09-05 19:05:06 +00001115
1116 udpPacket.setPayload(embeddedDhcp6);
1117 udpPacket.resetChecksum();
1118 ipv6Packet.setPayload(udpPacket);
1119 etherReply.setPayload(ipv6Packet);
1120
1121 return new InternalPacket(etherReply, clientConnectionPoint);
1122 }
1123
Yi Tseng919b2df2017-09-07 16:22:51 -07001124 // Returns the first v6 interface ip out of a set of interfaces or null.
Kalhee Kim45fede42017-09-05 19:05:06 +00001125 // Checks all interfaces, and ignores v6 interface ips
1126 private Ip6Address getRelayAgentIPv6Address(Set<Interface> intfs) {
1127 for (Interface intf : intfs) {
1128 for (InterfaceIpAddress ip : intf.ipAddressesList()) {
1129 Ip6Address relayAgentIp = ip.ipAddress().getIp6Address();
1130 if (relayAgentIp != null) {
1131 return relayAgentIp;
1132 }
1133 }
1134 }
1135 return null;
Yi Tseng51301292017-07-28 13:02:59 -07001136 }
Yi Tseng483ac6f2017-08-02 15:03:31 -07001137
1138 @Override
Kalhee Kimba366062017-11-07 16:32:09 +00001139 public void setDhcpFpmEnabled(Boolean enabled) {
1140 dhcpFpmEnabled = enabled;
1141 }
1142
1143 @Override
Yi Tseng483ac6f2017-08-02 15:03:31 -07001144 public void setDefaultDhcpServerConfigs(Collection<DhcpServerConfig> configs) {
Yi Tseng919b2df2017-09-07 16:22:51 -07001145 setDhcpServerConfigs(configs, defaultServerInfoList);
Yi Tseng919b2df2017-09-07 16:22:51 -07001146 }
1147
1148 @Override
1149 public void setIndirectDhcpServerConfigs(Collection<DhcpServerConfig> configs) {
1150 setDhcpServerConfigs(configs, indirectServerInfoList);
Yi Tseng919b2df2017-09-07 16:22:51 -07001151 }
1152
1153 public void setDhcpServerConfigs(Collection<DhcpServerConfig> configs, List<DhcpServerInfo> serverInfoList) {
Kalhee Kim45fede42017-09-05 19:05:06 +00001154 if (configs.size() == 0) {
1155 // no config to update
1156 return;
1157 }
1158
1159 // TODO: currently we pick up first DHCP server config.
1160 // Will use other server configs in the future for HA.
1161 DhcpServerConfig serverConfig = configs.iterator().next();
Yi Tseng919b2df2017-09-07 16:22:51 -07001162
Kalhee Kim45fede42017-09-05 19:05:06 +00001163 if (!serverConfig.getDhcpServerIp6().isPresent()) {
Yi Tseng919b2df2017-09-07 16:22:51 -07001164 // not a DHCPv6 config
Kalhee Kim45fede42017-09-05 19:05:06 +00001165 return;
1166 }
1167
Yi Tseng919b2df2017-09-07 16:22:51 -07001168 if (!serverInfoList.isEmpty()) {
1169 // remove old server info
1170 DhcpServerInfo oldServerInfo = serverInfoList.remove(0);
1171
1172 // stop monitoring gateway or server
1173 oldServerInfo.getDhcpGatewayIp6().ifPresent(gatewayIp -> {
1174 hostService.stopMonitoringIp(gatewayIp);
1175 });
1176 oldServerInfo.getDhcpServerIp6().ifPresent(serverIp -> {
1177 hostService.stopMonitoringIp(serverIp);
Yi Tseng525ff402017-10-23 19:39:39 -07001178 cancelDhcpPacket(serverIp);
Yi Tseng919b2df2017-09-07 16:22:51 -07001179 });
1180 }
1181
1182 // Create new server info according to the config
1183 DhcpServerInfo newServerInfo = new DhcpServerInfo(serverConfig,
1184 DhcpServerInfo.Version.DHCP_V6);
1185 checkState(newServerInfo.getDhcpServerConnectPoint().isPresent(),
1186 "Connect point not exists");
1187 checkState(newServerInfo.getDhcpServerIp6().isPresent(),
1188 "IP of DHCP server not exists");
1189
1190 log.debug("DHCP server connect point: {}", newServerInfo.getDhcpServerConnectPoint().orElse(null));
1191 log.debug("DHCP server IP: {}", newServerInfo.getDhcpServerIp6().orElse(null));
1192
Yi Tseng525ff402017-10-23 19:39:39 -07001193 Ip6Address serverIp = newServerInfo.getDhcpServerIp6().get();
1194 Ip6Address ipToProbe;
Yi Tseng919b2df2017-09-07 16:22:51 -07001195 if (newServerInfo.getDhcpGatewayIp6().isPresent()) {
1196 ipToProbe = newServerInfo.getDhcpGatewayIp6().get();
1197 } else {
1198 ipToProbe = newServerInfo.getDhcpServerIp6().orElse(null);
1199 }
1200 String hostToProbe = newServerInfo.getDhcpGatewayIp6()
1201 .map(ip -> "gateway").orElse("server");
1202
1203 log.debug("Probing to resolve {} IP {}", hostToProbe, ipToProbe);
Kalhee Kim45fede42017-09-05 19:05:06 +00001204 hostService.startMonitoringIp(ipToProbe);
1205
1206 Set<Host> hosts = hostService.getHostsByIp(ipToProbe);
1207 if (!hosts.isEmpty()) {
1208 Host host = hosts.iterator().next();
Yi Tseng919b2df2017-09-07 16:22:51 -07001209 newServerInfo.setDhcpConnectVlan(host.vlan());
1210 newServerInfo.setDhcpConnectMac(host.mac());
Kalhee Kim45fede42017-09-05 19:05:06 +00001211 }
Yi Tseng919b2df2017-09-07 16:22:51 -07001212 // Add new server info
Yi Tseng48cd0862017-11-09 13:54:12 -08001213 synchronized (this) {
1214 serverInfoList.clear();
1215 serverInfoList.add(0, newServerInfo);
1216 }
Yi Tseng525ff402017-10-23 19:39:39 -07001217 requestDhcpPacket(serverIp);
Kalhee Kim45fede42017-09-05 19:05:06 +00001218 }
1219
1220 class InternalHostListener implements HostListener {
1221 @Override
1222 public void event(HostEvent event) {
1223 switch (event.type()) {
1224 case HOST_ADDED:
1225 case HOST_UPDATED:
1226 hostUpdated(event.subject());
1227 break;
1228 case HOST_REMOVED:
1229 hostRemoved(event.subject());
1230 break;
Kalhee Kim45fede42017-09-05 19:05:06 +00001231 default:
1232 break;
1233 }
1234 }
1235 }
1236
1237 /**
Kalhee Kim45fede42017-09-05 19:05:06 +00001238 * Handle host updated.
1239 * If the host is DHCP server or gateway, update connect mac and vlan.
1240 *
1241 * @param host the host
1242 */
1243 private void hostUpdated(Host host) {
Yi Tseng525ff402017-10-23 19:39:39 -07001244 hostUpdated(host, defaultServerInfoList);
1245 hostUpdated(host, indirectServerInfoList);
Kalhee Kim45fede42017-09-05 19:05:06 +00001246 }
1247
Yi Tseng525ff402017-10-23 19:39:39 -07001248 private void hostUpdated(Host host, List<DhcpServerInfo> serverInfoList) {
1249 DhcpServerInfo serverInfo;
1250 Ip6Address targetIp;
1251 if (!serverInfoList.isEmpty()) {
1252 serverInfo = serverInfoList.get(0);
1253 Ip6Address serverIp = serverInfo.getDhcpServerIp6().orElse(null);
1254 targetIp = serverInfo.getDhcpGatewayIp6().orElse(null);
1255
1256 if (targetIp == null) {
1257 targetIp = serverIp;
1258 }
1259
1260 if (targetIp != null) {
1261 if (host.ipAddresses().contains(targetIp)) {
1262 serverInfo.setDhcpConnectMac(host.mac());
1263 serverInfo.setDhcpConnectVlan(host.vlan());
1264 requestDhcpPacket(serverIp);
1265 }
1266 }
1267 }
1268 }
1269
Kalhee Kim45fede42017-09-05 19:05:06 +00001270 /**
1271 * Handle host removed.
1272 * If the host is DHCP server or gateway, unset connect mac and vlan.
1273 *
1274 * @param host the host
1275 */
1276 private void hostRemoved(Host host) {
Yi Tseng525ff402017-10-23 19:39:39 -07001277 hostRemoved(host, defaultServerInfoList);
1278 hostRemoved(host, indirectServerInfoList);
Yi Tseng919b2df2017-09-07 16:22:51 -07001279 }
1280
Yi Tseng525ff402017-10-23 19:39:39 -07001281 private void hostRemoved(Host host, List<DhcpServerInfo> serverInfoList) {
1282 DhcpServerInfo serverInfo;
1283 Ip6Address targetIp;
1284
1285 if (!serverInfoList.isEmpty()) {
1286 serverInfo = serverInfoList.get(0);
1287 Ip6Address serverIp = serverInfo.getDhcpServerIp6().orElse(null);
1288 targetIp = serverInfo.getDhcpGatewayIp6().orElse(null);
1289
1290 if (targetIp == null) {
1291 targetIp = serverIp;
1292 }
1293
1294 if (targetIp != null) {
1295 if (host.ipAddresses().contains(targetIp)) {
1296 serverInfo.setDhcpConnectVlan(null);
1297 serverInfo.setDhcpConnectMac(null);
1298 cancelDhcpPacket(serverIp);
1299 }
1300 }
1301 }
1302 }
1303
Kalhee Kim45b24182017-10-18 18:30:23 +00001304 /**
1305 * Returns the first interface ip from interface.
1306 *
1307 * @param iface interface of one connect point
1308 * @return the first interface IP; null if not exists an IP address in
1309 * these interfaces
1310 */
1311 private Ip6Address getFirstIpFromInterface(Interface iface) {
1312 checkNotNull(iface, "Interface can't be null");
1313 return iface.ipAddressesList().stream()
1314 .map(InterfaceIpAddress::ipAddress)
1315 .filter(IpAddress::isIp6)
1316 .map(IpAddress::getIp6Address)
1317 .findFirst()
1318 .orElse(null);
1319 }
1320
1321 /**
1322 * Gets Interface facing to the server for default host.
1323 *
1324 * @return the Interface facing to the server; null if not found
1325 */
1326 private Interface getServerInterface() {
Yi Tseng25bfe372017-11-03 16:27:32 -07001327 DhcpServerInfo serverInfo;
1328 ConnectPoint dhcpServerConnectPoint;
1329 VlanId dhcpConnectVlan;
1330
1331 if (!defaultServerInfoList.isEmpty()) {
1332 serverInfo = defaultServerInfoList.get(0);
1333 dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1334 dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1335 } else {
Kalhee Kim45b24182017-10-18 18:30:23 +00001336 return null;
1337 }
Yi Tseng921e5b02017-11-17 17:14:41 -08001338 if (dhcpServerConnectPoint == null || dhcpConnectVlan == null) {
1339 log.info("Default DHCP server {} not resolve yet", serverInfo.getDhcpGatewayIp6());
1340 return null;
1341 }
Kalhee Kim45b24182017-10-18 18:30:23 +00001342 return interfaceService.getInterfacesByPort(dhcpServerConnectPoint)
1343 .stream()
1344 .filter(iface -> interfaceContainsVlan(iface, dhcpConnectVlan))
1345 .findFirst()
1346 .orElse(null);
1347 }
1348
1349 /**
1350 * Gets Interface facing to the server for indirect hosts.
1351 * Use default server Interface if indirect server not configured.
1352 *
1353 * @return the Interface facing to the server; null if not found
1354 */
1355 private Interface getIndirectServerInterface() {
Yi Tseng25bfe372017-11-03 16:27:32 -07001356 DhcpServerInfo serverInfo;
1357
1358 ConnectPoint indirectDhcpServerConnectPoint;
1359 VlanId indirectDhcpConnectVlan;
1360
1361 if (!indirectServerInfoList.isEmpty()) {
1362 serverInfo = indirectServerInfoList.get(0);
1363 indirectDhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1364 indirectDhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1365 } else {
Kalhee Kim45b24182017-10-18 18:30:23 +00001366 return getServerInterface();
1367 }
Yi Tseng921e5b02017-11-17 17:14:41 -08001368 if (indirectDhcpServerConnectPoint == null || indirectDhcpConnectVlan == null) {
1369 log.info("Indirect DHCP server {} not resolve yet", serverInfo.getDhcpGatewayIp6());
1370 return null;
1371 }
Kalhee Kim45b24182017-10-18 18:30:23 +00001372 return interfaceService.getInterfacesByPort(indirectDhcpServerConnectPoint)
1373 .stream()
1374 .filter(iface -> interfaceContainsVlan(iface, indirectDhcpConnectVlan))
1375 .findFirst()
1376 .orElse(null);
1377 }
1378
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +00001379 /**
1380 * Determind if an Interface contains a vlan id.
1381 *
1382 * @param iface the Interface
1383 * @param vlanId the vlan id
1384 * @return true if the Interface contains the vlan id
1385 */
1386 private boolean interfaceContainsVlan(Interface iface, VlanId vlanId) {
1387 if (vlanId.equals(VlanId.NONE)) {
1388 // untagged packet, check if vlan untagged or vlan native is not NONE
1389 return !iface.vlanUntagged().equals(VlanId.NONE) ||
1390 !iface.vlanNative().equals(VlanId.NONE);
1391 }
1392 // tagged packet, check if the interface contains the vlan
1393 return iface.vlanTagged().contains(vlanId);
1394 }
1395
Yi Tseng525ff402017-10-23 19:39:39 -07001396 private void requestDhcpPacket(Ip6Address serverIp) {
1397 requestServerDhcpPacket(serverIp);
1398 requestClientDhcpPacket(serverIp);
1399 }
1400
1401 private void cancelDhcpPacket(Ip6Address serverIp) {
1402 cancelServerDhcpPacket(serverIp);
1403 cancelClientDhcpPacket(serverIp);
1404 }
1405
1406 private void cancelServerDhcpPacket(Ip6Address serverIp) {
1407 TrafficSelector serverSelector =
1408 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1409 .matchIPv6Src(serverIp.toIpPrefix())
1410 .build();
1411 packetService.cancelPackets(serverSelector,
1412 PacketPriority.CONTROL,
1413 appId);
1414 }
1415
1416 private void requestServerDhcpPacket(Ip6Address serverIp) {
1417 TrafficSelector serverSelector =
1418 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1419 .matchIPv6Src(serverIp.toIpPrefix())
1420 .build();
1421 packetService.requestPackets(serverSelector,
1422 PacketPriority.CONTROL,
1423 appId);
1424 }
1425
1426 private void cancelClientDhcpPacket(Ip6Address serverIp) {
1427 // Packet comes from relay
1428 TrafficSelector indirectClientSelector =
1429 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1430 .matchIPv6Dst(serverIp.toIpPrefix())
1431 .build();
1432 packetService.cancelPackets(indirectClientSelector,
1433 PacketPriority.CONTROL,
1434 appId);
Yi Tseng41dde702017-11-02 15:21:10 -07001435 indirectClientSelector =
1436 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1437 .matchIPv6Dst(Ip6Address.ALL_DHCP_RELAY_AGENTS_AND_SERVERS.toIpPrefix())
1438 .build();
1439 packetService.cancelPackets(indirectClientSelector,
1440 PacketPriority.CONTROL,
1441 appId);
1442 indirectClientSelector =
1443 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1444 .matchIPv6Dst(Ip6Address.ALL_DHCP_SERVERS.toIpPrefix())
1445 .build();
1446 packetService.cancelPackets(indirectClientSelector,
1447 PacketPriority.CONTROL,
1448 appId);
Yi Tseng525ff402017-10-23 19:39:39 -07001449
1450 // Packet comes from client
1451 packetService.cancelPackets(CLIENT_SERVER_SELECTOR,
1452 PacketPriority.CONTROL,
1453 appId);
1454 }
1455
1456 private void requestClientDhcpPacket(Ip6Address serverIp) {
1457 // Packet comes from relay
1458 TrafficSelector indirectClientSelector =
1459 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1460 .matchIPv6Dst(serverIp.toIpPrefix())
1461 .build();
1462 packetService.requestPackets(indirectClientSelector,
1463 PacketPriority.CONTROL,
1464 appId);
Yi Tseng41dde702017-11-02 15:21:10 -07001465 indirectClientSelector =
1466 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1467 .matchIPv6Dst(Ip6Address.ALL_DHCP_RELAY_AGENTS_AND_SERVERS.toIpPrefix())
1468 .build();
1469 packetService.requestPackets(indirectClientSelector,
1470 PacketPriority.CONTROL,
1471 appId);
1472 indirectClientSelector =
1473 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1474 .matchIPv6Dst(Ip6Address.ALL_DHCP_SERVERS.toIpPrefix())
1475 .build();
1476 packetService.requestPackets(indirectClientSelector,
1477 PacketPriority.CONTROL,
1478 appId);
Yi Tseng525ff402017-10-23 19:39:39 -07001479
1480 // Packet comes from client
1481 packetService.requestPackets(CLIENT_SERVER_SELECTOR,
1482 PacketPriority.CONTROL,
1483 appId);
1484 }
1485
1486 /**
1487 * Process the ignore rules.
1488 *
1489 * @param deviceId the device id
1490 * @param vlanId the vlan to be ignored
1491 * @param op the operation, ADD to install; REMOVE to uninstall rules
1492 */
1493 private void processIgnoreVlanRule(DeviceId deviceId, VlanId vlanId, Objective.Operation op) {
Yi Tseng525ff402017-10-23 19:39:39 -07001494 AtomicInteger installedCount = new AtomicInteger(DHCP_SELECTORS.size());
1495 DHCP_SELECTORS.forEach(trafficSelector -> {
1496 TrafficSelector selector = DefaultTrafficSelector.builder(trafficSelector)
1497 .matchVlanId(vlanId)
1498 .build();
1499
1500 ForwardingObjective.Builder builder = DefaultForwardingObjective.builder()
1501 .withFlag(ForwardingObjective.Flag.VERSATILE)
1502 .withSelector(selector)
1503 .withPriority(IGNORE_CONTROL_PRIORITY)
Yi Tsengdbabeed2017-11-29 10:49:20 -08001504 .withTreatment(DefaultTrafficTreatment.emptyTreatment())
Yi Tseng525ff402017-10-23 19:39:39 -07001505 .fromApp(appId);
1506
1507
1508 ObjectiveContext objectiveContext = new ObjectiveContext() {
1509 @Override
1510 public void onSuccess(Objective objective) {
1511 log.info("Ignore rule {} (Vlan id {}, device {}, selector {})",
1512 op, vlanId, deviceId, selector);
1513 int countDown = installedCount.decrementAndGet();
1514 if (countDown != 0) {
1515 return;
1516 }
1517 switch (op) {
1518 case ADD:
1519 ignoredVlans.put(deviceId, vlanId);
1520 break;
1521 case REMOVE:
1522 ignoredVlans.remove(deviceId, vlanId);
1523 break;
1524 default:
1525 log.warn("Unsupported objective operation {}", op);
1526 break;
1527 }
1528 }
1529
1530 @Override
1531 public void onError(Objective objective, ObjectiveError error) {
1532 log.warn("Can't {} ignore rule (vlan id {}, selector {}, device {}) due to {}",
1533 op, vlanId, selector, deviceId, error);
1534 }
1535 };
1536
1537 ForwardingObjective fwd;
1538 switch (op) {
1539 case ADD:
1540 fwd = builder.add(objectiveContext);
1541 break;
1542 case REMOVE:
1543 fwd = builder.remove(objectiveContext);
1544 break;
1545 default:
1546 log.warn("Unsupported objective operation {}", op);
1547 return;
1548 }
1549
1550 Device device = deviceService.getDevice(deviceId);
1551 if (device == null || !device.is(Pipeliner.class)) {
1552 log.warn("Device {} is not available now, wait until device is available", deviceId);
1553 return;
1554 }
1555 flowObjectiveService.apply(deviceId, fwd);
1556 });
1557 }
Kalhee Kim121ba922017-11-01 17:56:44 +00001558
1559 /**
1560 * Find first ipaddress for a given Host info i.e. mac and vlan.
1561 *
1562 * @param clientMac client mac
1563 * @param vlanId packet's vlan
1564 * @return next-hop link-local ipaddress for a given host
1565 */
1566 private IpAddress getFirstIpByHost(MacAddress clientMac, VlanId vlanId) {
1567 IpAddress nextHopIp;
1568 // pick out the first link-local ip address
1569 HostId gwHostId = HostId.hostId(clientMac, vlanId);
1570 Host gwHost = hostService.getHost(gwHostId);
1571 if (gwHost == null) {
1572 log.warn("Can't find gateway host for hostId {}", gwHostId);
1573 return null;
1574 }
1575 nextHopIp = gwHost.ipAddresses()
1576 .stream()
1577 .filter(IpAddress::isIp6)
Yi Tseng68ef26b2017-12-18 17:10:00 -08001578 .filter(IpAddress::isLinkLocal)
Kalhee Kim121ba922017-11-01 17:56:44 +00001579 .map(IpAddress::getIp6Address)
1580 .findFirst()
1581 .orElse(null);
1582 return nextHopIp;
1583 }
Yi Tseng51301292017-07-28 13:02:59 -07001584}