blob: dc2a1cab082c8ae19890785ca1dd96ff2f3c5c02 [file] [log] [blame]
Yi Tseng51301292017-07-28 13:02:59 -07001/*
2 * Copyright 2017-present Open Networking Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 */
17
18package org.onosproject.dhcprelay;
19
Yi Tseng7da339e2017-10-23 19:39:39 -070020import com.google.common.collect.HashMultimap;
Yi Tseng2fe8f3f2017-09-07 16:22:51 -070021import com.google.common.collect.Lists;
Yi Tseng7da339e2017-10-23 19:39:39 -070022import com.google.common.collect.Multimap;
Kalhee Kim1b5094f2017-09-05 19:05:06 +000023import org.apache.felix.scr.annotations.Activate;
24import org.apache.felix.scr.annotations.Deactivate;
Kalhee Kim1b5094f2017-09-05 19:05:06 +000025import com.google.common.collect.Sets;
26import com.google.common.collect.ImmutableSet;
Yi Tseng51301292017-07-28 13:02:59 -070027import org.apache.felix.scr.annotations.Component;
28import org.apache.felix.scr.annotations.Property;
Kalhee Kim1b5094f2017-09-05 19:05:06 +000029import org.apache.felix.scr.annotations.Reference;
30import org.apache.felix.scr.annotations.ReferenceCardinality;
Yi Tseng51301292017-07-28 13:02:59 -070031import org.apache.felix.scr.annotations.Service;
32import org.onlab.packet.BasePacket;
Kalhee Kim1b5094f2017-09-05 19:05:06 +000033import org.onlab.packet.DHCP6;
34import org.onlab.packet.IPv6;
35import org.onlab.packet.Ethernet;
36import org.onlab.packet.Ip6Address;
Yi Tseng51301292017-07-28 13:02:59 -070037import org.onlab.packet.IpAddress;
Kalhee Kim1b5094f2017-09-05 19:05:06 +000038import org.onlab.packet.IpPrefix;
Yi Tseng51301292017-07-28 13:02:59 -070039import org.onlab.packet.MacAddress;
Yi Tseng7da339e2017-10-23 19:39:39 -070040import org.onlab.packet.TpPort;
Kalhee Kim1b5094f2017-09-05 19:05:06 +000041import org.onlab.packet.UDP;
Yi Tseng51301292017-07-28 13:02:59 -070042import org.onlab.packet.VlanId;
Kalhee Kim1b5094f2017-09-05 19:05:06 +000043import org.onlab.packet.dhcp.Dhcp6RelayOption;
44import org.onlab.packet.dhcp.Dhcp6InterfaceIdOption;
45import org.onlab.packet.dhcp.Dhcp6Option;
46import org.onlab.packet.dhcp.Dhcp6IaNaOption;
47import org.onlab.packet.dhcp.Dhcp6IaTaOption;
48import org.onlab.packet.dhcp.Dhcp6IaPdOption;
49import org.onlab.packet.dhcp.Dhcp6IaAddressOption;
50import org.onlab.packet.dhcp.Dhcp6IaPrefixOption;
51import org.onlab.util.HexString;
Yi Tseng7da339e2017-10-23 19:39:39 -070052import org.onosproject.core.ApplicationId;
53import org.onosproject.core.CoreService;
Yi Tseng51301292017-07-28 13:02:59 -070054import org.onosproject.dhcprelay.api.DhcpHandler;
Yi Tseng2fe8f3f2017-09-07 16:22:51 -070055import org.onosproject.dhcprelay.api.DhcpServerInfo;
Yi Tseng7da339e2017-10-23 19:39:39 -070056import org.onosproject.dhcprelay.config.IgnoreDhcpConfig;
Kalhee Kim1b5094f2017-09-05 19:05:06 +000057import org.onosproject.dhcprelay.store.DhcpRelayStore;
Yi Tseng7da339e2017-10-23 19:39:39 -070058import org.onosproject.net.Device;
59import org.onosproject.net.DeviceId;
60import org.onosproject.net.behaviour.Pipeliner;
61import org.onosproject.net.device.DeviceService;
62import org.onosproject.net.flow.DefaultTrafficSelector;
63import org.onosproject.net.flow.TrafficSelector;
64import org.onosproject.net.flowobjective.DefaultForwardingObjective;
65import org.onosproject.net.flowobjective.FlowObjectiveService;
66import org.onosproject.net.flowobjective.ForwardingObjective;
67import org.onosproject.net.flowobjective.Objective;
68import org.onosproject.net.flowobjective.ObjectiveContext;
69import org.onosproject.net.flowobjective.ObjectiveError;
Yi Tseng4b013202017-09-08 17:22:51 -070070import org.onosproject.net.host.HostProvider;
71import org.onosproject.net.host.HostProviderRegistry;
72import org.onosproject.net.host.HostProviderService;
Kalhee Kim1b5094f2017-09-05 19:05:06 +000073import org.onosproject.net.host.HostService;
74import org.onosproject.net.host.DefaultHostDescription;
75import org.onosproject.net.host.HostDescription;
76import org.onosproject.net.host.InterfaceIpAddress;
77import org.onosproject.net.host.HostListener;
78import org.onosproject.net.host.HostEvent;
79import org.onosproject.net.intf.Interface;
80import org.onosproject.net.intf.InterfaceService;
Yi Tseng7da339e2017-10-23 19:39:39 -070081import org.onosproject.net.packet.PacketPriority;
Yi Tseng4b013202017-09-08 17:22:51 -070082import org.onosproject.net.provider.ProviderId;
Kalhee Kim1b5094f2017-09-05 19:05:06 +000083import org.onosproject.routeservice.Route;
84import org.onosproject.routeservice.RouteStore;
Yi Tsenge72fbb52017-08-02 15:03:31 -070085import org.onosproject.dhcprelay.config.DhcpServerConfig;
Yi Tseng51301292017-07-28 13:02:59 -070086import org.onosproject.net.ConnectPoint;
Kalhee Kim1b5094f2017-09-05 19:05:06 +000087import org.onosproject.net.Host;
88import org.onosproject.net.HostId;
89import org.onosproject.net.HostLocation;
90import org.onosproject.net.packet.DefaultOutboundPacket;
91import org.onosproject.net.packet.OutboundPacket;
Yi Tseng51301292017-07-28 13:02:59 -070092import org.onosproject.net.packet.PacketContext;
Kalhee Kim1b5094f2017-09-05 19:05:06 +000093import org.onosproject.net.packet.PacketService;
94import org.slf4j.Logger;
95import org.slf4j.LoggerFactory;
96import org.onosproject.net.flow.DefaultTrafficTreatment;
97import org.onosproject.net.flow.TrafficTreatment;
Yi Tseng51301292017-07-28 13:02:59 -070098
Kalhee Kim1b5094f2017-09-05 19:05:06 +000099
100import java.nio.ByteBuffer;
101import java.util.List;
Yi Tsenge72fbb52017-08-02 15:03:31 -0700102import java.util.Collection;
Yi Tseng51301292017-07-28 13:02:59 -0700103import java.util.Optional;
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000104import java.util.Set;
105import java.util.ArrayList;
Yi Tseng7da339e2017-10-23 19:39:39 -0700106import java.util.concurrent.atomic.AtomicInteger;
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000107
108
109import static com.google.common.base.Preconditions.checkNotNull;
110import static com.google.common.base.Preconditions.checkState;
Yi Tseng7da339e2017-10-23 19:39:39 -0700111import static org.onosproject.net.flowobjective.Objective.Operation.ADD;
112import static org.onosproject.net.flowobjective.Objective.Operation.REMOVE;
Yi Tseng51301292017-07-28 13:02:59 -0700113
114@Component
115@Service
116@Property(name = "version", value = "6")
Yi Tseng4b013202017-09-08 17:22:51 -0700117public class Dhcp6HandlerImpl implements DhcpHandler, HostProvider {
Charles Chan75edab72017-09-12 17:09:32 -0700118 public static final String DHCP_V6_RELAY_APP = "org.onosproject.Dhcp6HandlerImpl";
Saurav Das37415cc2017-09-13 14:35:56 -0700119 public static final ProviderId PROVIDER_ID = new ProviderId("dhcp6", DHCP_V6_RELAY_APP);
Yi Tseng7da339e2017-10-23 19:39:39 -0700120 private static final int IGNORE_CONTROL_PRIORITY = PacketPriority.CONTROL.priorityValue() + 1000;
121
122 private static final TrafficSelector CLIENT_SERVER_SELECTOR = DefaultTrafficSelector.builder()
123 .matchEthType(Ethernet.TYPE_IPV6)
124 .matchIPProtocol(IPv6.PROTOCOL_UDP)
125 .matchIPv6Src(IpPrefix.IPV6_LINK_LOCAL_PREFIX)
126 .matchIPv6Dst(Ip6Address.ALL_DHCP_RELAY_AGENTS_AND_SERVERS.toIpPrefix())
127 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_V6_CLIENT_PORT))
128 .matchUdpDst(TpPort.tpPort(UDP.DHCP_V6_SERVER_PORT))
129 .build();
130 private static final TrafficSelector SERVER_RELAY_SELECTOR = DefaultTrafficSelector.builder()
131 .matchEthType(Ethernet.TYPE_IPV6)
132 .matchIPProtocol(IPv6.PROTOCOL_UDP)
133 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_V6_SERVER_PORT))
134 .matchUdpDst(TpPort.tpPort(UDP.DHCP_V6_SERVER_PORT))
135 .build();
136 static final Set<TrafficSelector> DHCP_SELECTORS = ImmutableSet.of(
137 CLIENT_SERVER_SELECTOR,
138 SERVER_RELAY_SELECTOR
139 );
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000140 private static Logger log = LoggerFactory.getLogger(Dhcp6HandlerImpl.class);
Yi Tseng51301292017-07-28 13:02:59 -0700141
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000142 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
143 protected DhcpRelayStore dhcpRelayStore;
Yi Tseng51301292017-07-28 13:02:59 -0700144
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000145 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
146 protected PacketService packetService;
147
148 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000149 protected RouteStore routeStore;
150
151 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
152 protected InterfaceService interfaceService;
153
154 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
155 protected HostService hostService;
156
Yi Tseng4b013202017-09-08 17:22:51 -0700157 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
158 protected HostProviderRegistry providerRegistry;
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000159
Yi Tseng7da339e2017-10-23 19:39:39 -0700160 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
161 protected CoreService coreService;
162
163 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
164 protected DeviceService deviceService;
165
166 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
167 protected FlowObjectiveService flowObjectiveService;
168
Yi Tseng4b013202017-09-08 17:22:51 -0700169 protected HostProviderService providerService;
Yi Tseng7da339e2017-10-23 19:39:39 -0700170 protected ApplicationId appId;
171 protected Multimap<DeviceId, VlanId> ignoredVlans = HashMultimap.create();
172 private InternalHostListener hostListener = new InternalHostListener();
173
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000174 private Ip6Address dhcpServerIp = null;
175 // dhcp server may be connected directly to the SDN network or
176 // via an external gateway. When connected directly, the dhcpConnectPoint, dhcpConnectMac,
177 // and dhcpConnectVlan refer to the server. When connected via the gateway, they refer
178 // to the gateway.
179 private ConnectPoint dhcpServerConnectPoint = null;
180 private MacAddress dhcpConnectMac = null;
181 private VlanId dhcpConnectVlan = null;
182 private Ip6Address dhcpGatewayIp = null;
183 private Ip6Address relayAgentIpFromCfg = null;
184
185 private Ip6Address indirectDhcpServerIp = null;
186 private ConnectPoint indirectDhcpServerConnectPoint = null;
187 private MacAddress indirectDhcpConnectMac = null;
188 private VlanId indirectDhcpConnectVlan = null;
189 private Ip6Address indirectDhcpGatewayIp = null;
190 private Ip6Address indirectRelayAgentIpFromCfg = null;
191
Yi Tseng2fe8f3f2017-09-07 16:22:51 -0700192 private List<DhcpServerInfo> defaultServerInfoList = Lists.newArrayList();
193 private List<DhcpServerInfo> indirectServerInfoList = Lists.newArrayList();
194
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000195
196 // CLIENT message types
197 public static final Set<Byte> MSG_TYPE_FROM_CLIENT =
198 ImmutableSet.of(DHCP6.MsgType.SOLICIT.value(),
199 DHCP6.MsgType.REQUEST.value(),
200 DHCP6.MsgType.REBIND.value(),
201 DHCP6.MsgType.RENEW.value(),
202 DHCP6.MsgType.RELEASE.value(),
203 DHCP6.MsgType.DECLINE.value(),
204 DHCP6.MsgType.CONFIRM.value(),
205 DHCP6.MsgType.RELAY_FORW.value());
206 // SERVER message types
207 public static final Set<Byte> MSG_TYPE_FROM_SERVER =
208 ImmutableSet.of(DHCP6.MsgType.RELAY_REPL.value());
209
210 @Activate
211 protected void activate() {
Yi Tseng7da339e2017-10-23 19:39:39 -0700212 appId = coreService.registerApplication(DHCP_V6_RELAY_APP);
Yi Tseng4b013202017-09-08 17:22:51 -0700213 providerService = providerRegistry.register(this);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000214 hostService.addListener(hostListener);
Yi Tseng51301292017-07-28 13:02:59 -0700215 }
216
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000217 @Deactivate
218 protected void deactivate() {
Yi Tseng4b013202017-09-08 17:22:51 -0700219 providerRegistry.unregister(this);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000220 hostService.removeListener(hostListener);
Yi Tseng2fe8f3f2017-09-07 16:22:51 -0700221 defaultServerInfoList.forEach(this::stopMonitoringIps);
222 defaultServerInfoList.clear();
223 indirectServerInfoList.forEach(this::stopMonitoringIps);
224 indirectServerInfoList.clear();
225 }
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000226
Yi Tseng2fe8f3f2017-09-07 16:22:51 -0700227 private void stopMonitoringIps(DhcpServerInfo serverInfo) {
228 serverInfo.getDhcpGatewayIp6().ifPresent(gatewayIp -> {
229 hostService.stopMonitoringIp(gatewayIp);
230 });
231 serverInfo.getDhcpServerIp6().ifPresent(serverIp -> {
232 hostService.stopMonitoringIp(serverIp);
233 });
Yi Tseng51301292017-07-28 13:02:59 -0700234 }
235
Yi Tseng51301292017-07-28 13:02:59 -0700236 @Override
Yi Tseng2fe8f3f2017-09-07 16:22:51 -0700237 public List<DhcpServerInfo> getDefaultDhcpServerInfoList() {
238 return defaultServerInfoList;
Yi Tseng51301292017-07-28 13:02:59 -0700239 }
240
241 @Override
Yi Tseng2fe8f3f2017-09-07 16:22:51 -0700242 public List<DhcpServerInfo> getIndirectDhcpServerInfoList() {
243 return indirectServerInfoList;
Yi Tseng51301292017-07-28 13:02:59 -0700244 }
245
246 @Override
Yi Tseng7da339e2017-10-23 19:39:39 -0700247 public void updateIgnoreVlanConfig(IgnoreDhcpConfig config) {
248 if (config == null) {
249 ignoredVlans.forEach(((deviceId, vlanId) -> {
250 processIgnoreVlanRule(deviceId, vlanId, REMOVE);
251 }));
252 return;
253 }
254 config.ignoredVlans().forEach((deviceId, vlanId) -> {
255 if (ignoredVlans.get(deviceId).contains(vlanId)) {
256 // don't need to process if it already ignored
257 return;
258 }
259 processIgnoreVlanRule(deviceId, vlanId, ADD);
260 });
261
262 ignoredVlans.forEach((deviceId, vlanId) -> {
263 if (!config.ignoredVlans().get(deviceId).contains(vlanId)) {
264 // not contains in new config, remove it
265 processIgnoreVlanRule(deviceId, vlanId, REMOVE);
266 }
267 });
268 }
269
270 @Override
Kalhee Kim1b5094f2017-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();
285 log.warn("msgType is {}", msgType);
286
287 ConnectPoint inPort = context.inPacket().receivedFrom();
288 if (inPort == null) {
289 log.warn("incommin ConnectPoint is null");
290 }
291 Set<Interface> receivingInterfaces = interfaceService.getInterfacesByPort(inPort);
292 //ignore the packets if dhcp client interface is not configured on onos.
293 if (receivingInterfaces.isEmpty()) {
294 log.warn("Virtual interface is not configured on {}", inPort);
295 return;
296 }
297
298
299 if (MSG_TYPE_FROM_CLIENT.contains(msgType)) {
300
301 InternalPacket ethernetClientPacket =
302 processDhcp6PacketFromClient(context, receivedPacket, receivingInterfaces);
303 if (ethernetClientPacket != null) {
304 forwardPacket(ethernetClientPacket);
305 }
306
307 } else if (MSG_TYPE_FROM_SERVER.contains(msgType)) {
308 log.warn("calling processDhcp6PacketFromServer with RELAY_REPL", msgType);
309 InternalPacket ethernetPacketReply =
310 processDhcp6PacketFromServer(context, receivedPacket, receivingInterfaces);
311 if (ethernetPacketReply != null) {
312 forwardPacket(ethernetPacketReply);
313 }
314 } else {
315 log.warn("Not so fast, packet type {} not supported yet", msgType);
316 }
317 }
318
319
320 /**
321 * Checks if this app has been configured.
322 *
323 * @return true if all information we need have been initialized
324 */
325 public boolean configured() {
Yi Tseng2fe8f3f2017-09-07 16:22:51 -0700326 return !defaultServerInfoList.isEmpty();
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000327 }
328
Yi Tseng4b013202017-09-08 17:22:51 -0700329 @Override
330 public ProviderId id() {
Charles Chan75edab72017-09-12 17:09:32 -0700331 return PROVIDER_ID;
Yi Tseng4b013202017-09-08 17:22:51 -0700332 }
333
334 @Override
335 public void triggerProbe(Host host) {
336 // Do nothing here
337 }
338
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000339 // the new class the contains Ethernet packet and destination port, kind of like adding
340 // internal header to the packet
341 private class InternalPacket {
342 Ethernet packet;
343 ConnectPoint destLocation;
344 public InternalPacket(Ethernet newPacket, ConnectPoint newLocation) {
345 packet = newPacket;
346 destLocation = newLocation;
347 }
348 void setLocation(ConnectPoint newLocation) {
349 destLocation = newLocation;
350 }
351 }
352
353 //forward the packet to ConnectPoint where the DHCP server is attached.
354 private void forwardPacket(InternalPacket packet) {
355 //send Packetout to dhcp server connectpoint.
356 if (packet.destLocation != null) {
357 TrafficTreatment t = DefaultTrafficTreatment.builder()
358 .setOutput(packet.destLocation.port()).build();
359 OutboundPacket o = new DefaultOutboundPacket(
360 packet.destLocation.deviceId(), t, ByteBuffer.wrap(packet.packet.serialize()));
361 if (log.isTraceEnabled()) {
362 log.trace("Relaying packet to destination {}", packet.destLocation);
363 }
364 packetService.emit(o);
365 } // if
366 }
367
368 /**
369 * Check if the host is directly connected to the network or not.
370 *
371 * @param dhcp6Payload the dhcp6 payload
372 * @return true if the host is directly connected to the network; false otherwise
373 */
374 private boolean directlyConnected(DHCP6 dhcp6Payload) {
375 log.debug("directlyConnected enters");
376
377 if (dhcp6Payload.getMsgType() != DHCP6.MsgType.RELAY_FORW.value() &&
378 dhcp6Payload.getMsgType() != DHCP6.MsgType.RELAY_REPL.value()) {
379 log.debug("directlyConnected true. MsgType {}", dhcp6Payload.getMsgType());
380
381 return true;
382 }
383
384 // Regardless of relay-forward or relay-replay, check if we see another relay message
385 DHCP6 dhcp6Payload2 = dhcp6PacketFromRelayPacket(dhcp6Payload);
386 if (dhcp6Payload2 != null) {
387 if (dhcp6Payload.getMsgType() == DHCP6.MsgType.RELAY_FORW.value()) {
388 log.debug("directlyConnected false. 1st realy-foward, 2nd MsgType {}", dhcp6Payload2.getMsgType());
389 return false;
390 } else {
391 // relay-reply
392 if (dhcp6Payload2.getMsgType() != DHCP6.MsgType.RELAY_REPL.value()) {
393 log.debug("directlyConnected true. 2nd MsgType {}", dhcp6Payload2.getMsgType());
394 return true; // must be directly connected
395 } else {
396 log.debug("directlyConnected false. 1st relay-reply, 2nd relay-reply MsgType {}",
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000397 dhcp6Payload2.getMsgType());
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000398 return false; // must be indirectly connected
399 }
400 }
401 } else {
402 log.warn("directlyConnected true.");
403 return true;
404 }
405 }
406
407 /**
408 * extract DHCP6 payload from dhcp6 relay message within relay-forwrd/reply.
409 *
410 * @param dhcp6 dhcp6 relay-reply or relay-foward
411 * @return dhcp6Packet dhcp6 packet extracted from relay-message
412 */
413 private DHCP6 dhcp6PacketFromRelayPacket(DHCP6 dhcp6) {
414 log.debug("dhcp6PacketFromRelayPacket enters. dhcp6 {}", dhcp6);
415
416 // extract the relay message if exist
417 DHCP6 dhcp6Payload = dhcp6.getOptions().stream()
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000418 .filter(opt -> opt instanceof Dhcp6RelayOption)
419 .map(BasePacket::getPayload)
420 .map(pld -> (DHCP6) pld)
421 .findFirst()
422 .orElse(null);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000423
424
425 if (dhcp6Payload == null) {
426 // Can't find dhcp payload
427 log.debug("Can't find dhcp6 payload from relay message");
428 } else {
429 log.debug("dhcp6 payload found from relay message {}", dhcp6Payload);
430 }
431
432 return dhcp6Payload;
433 }
434
435 /**
436 * find the leaf DHCP6 packet from multi-level relay packet.
437 *
438 * @param relayPacket dhcp6 relay packet
439 * @return leafPacket non-relay dhcp6 packet
440 */
441 private DHCP6 getDhcp6Leaf(DHCP6 relayPacket) {
442 DHCP6 dhcp6Parent = relayPacket;
443 DHCP6 dhcp6Child = null;
444
445 log.debug("getDhcp6Leaf entered.");
446 while (dhcp6Parent != null) {
447 dhcp6Child = dhcp6PacketFromRelayPacket(dhcp6Parent);
448
449 if (dhcp6Child != null) {
450 if (dhcp6Child.getMsgType() != DHCP6.MsgType.RELAY_FORW.value() &&
451 dhcp6Child.getMsgType() != DHCP6.MsgType.RELAY_REPL.value()) {
452 log.debug("leaf dhcp6 packet found.");
453 break;
454 } else {
455 // found another relay
456 // go for another loop
457 dhcp6Parent = dhcp6Child;
458 }
459 } else {
460 log.warn("malformed pkt! Expected dhcp6 within relay pkt, but no dhcp6 leaf found.");
461 break;
462 }
463 }
464 return dhcp6Child;
465 }
466
467 /**
468 * check if DHCP6 relay-reply is reply.
469 *
470 * @param relayPacket dhcp6 relay-reply
471 * @return boolean relay-reply contains ack
472 */
473 private boolean isDhcp6Reply(DHCP6 relayPacket) {
474 log.debug("isDhcp6Reply entered.");
475
476 DHCP6 leafDhcp6 = getDhcp6Leaf(relayPacket);
477
478 if (leafDhcp6 != null) {
479 if (leafDhcp6.getMsgType() == DHCP6.MsgType.REPLY.value()) {
480 log.debug("isDhcp6Reply true.");
481 return true; // must be directly connected
482 } else {
483 log.debug("isDhcp6Reply false. leaf dhcp6 is not replay. MsgType {}", leafDhcp6.getMsgType());
484 }
485 } else {
486 log.debug("isDhcp6Reply false. Expected dhcp6 within relay pkt but not found.");
487 }
488 log.debug("isDhcp6Reply false.");
489 return false;
490 }
491
492 /**
493 * check if DHCP6 is release or relay-forward contains release.
494 *
495 * @param dhcp6Payload dhcp6 packet
496 * @return boolean dhcp6 contains release
497 */
498 private boolean isDhcp6Release(DHCP6 dhcp6Payload) {
499
500 log.debug("isDhcp6Release entered.");
501
502 if (dhcp6Payload.getMsgType() == DHCP6.MsgType.RELEASE.value()) {
503 log.debug("isDhcp6Release true.");
504 return true; // must be directly connected
505 } else {
506 DHCP6 dhcp6Leaf = getDhcp6Leaf(dhcp6Payload);
507 if (dhcp6Leaf != null) {
508 if (dhcp6Leaf.getMsgType() == DHCP6.MsgType.RELEASE.value()) {
509 log.debug("isDhcp6Release true. indirectlry connected");
510 return true;
511 } else {
512 log.debug("leaf dhcp6 is not release. MsgType {}", dhcp6Leaf.getMsgType());
513 return false;
514 }
515 } else {
516 log.debug("isDhcp6Release false. dhcp6 is niether relay nor release.");
517 return false;
518 }
519 }
520 }
521
522 /**
523 * extract from dhcp6 packet client ipv6 address of given by dhcp server.
524 *
525 * @param dhcp6 the dhcp6 packet
526 * @return Ip6Address Ip6Address given by dhcp server, or null if not exists
527 */
528 private Ip6Address extractIpAddress(DHCP6 dhcp6) {
529 Ip6Address ip = null;
530
531 log.debug("extractIpAddress enters dhcp6 {}.", dhcp6);
532 // Extract IPv6 address from IA NA ot IA TA option
533 Optional<Dhcp6IaNaOption> iaNaOption = dhcp6.getOptions()
534 .stream()
535 .filter(opt -> opt instanceof Dhcp6IaNaOption)
536 .map(opt -> (Dhcp6IaNaOption) opt)
537 .findFirst();
538 Optional<Dhcp6IaTaOption> iaTaOption = dhcp6.getOptions()
539 .stream()
540 .filter(opt -> opt instanceof Dhcp6IaTaOption)
541 .map(opt -> (Dhcp6IaTaOption) opt)
542 .findFirst();
543 Optional<Dhcp6IaAddressOption> iaAddressOption;
544 if (iaNaOption.isPresent()) {
545 log.debug("Found IPv6 address from iaNaOption {}", iaNaOption);
546
547 iaAddressOption = iaNaOption.get().getOptions().stream()
548 .filter(opt -> opt instanceof Dhcp6IaAddressOption)
549 .map(opt -> (Dhcp6IaAddressOption) opt)
550 .findFirst();
551 } else if (iaTaOption.isPresent()) {
552 log.debug("Found IPv6 address from iaTaOption {}", iaTaOption);
553
554 iaAddressOption = iaTaOption.get().getOptions().stream()
555 .filter(opt -> opt instanceof Dhcp6IaAddressOption)
556 .map(opt -> (Dhcp6IaAddressOption) opt)
557 .findFirst();
558 } else {
559 iaAddressOption = Optional.empty();
560 }
561 if (iaAddressOption.isPresent()) {
562 ip = iaAddressOption.get().getIp6Address();
563 log.debug("Found IPv6 address from iaAddressOption {}", iaAddressOption);
564
565
566 } else {
567 log.debug("Can't find IPv6 address from DHCPv6 {}", dhcp6);
568 }
569
570 return ip;
571 }
572 /**
573 * extract from dhcp6 packet Prefix prefix provided by dhcp server.
574 *
575 * @param dhcp6 the dhcp6 payload
576 * @return IpPrefix Prefix Delegation prefix, or null if not exists.
577 */
578 private IpPrefix extractPrefix(DHCP6 dhcp6) {
579 log.warn("extractPrefix enters {}", dhcp6);
580
581 // extract prefix
582 IpPrefix prefixPrefix = null;
583
584 Ip6Address prefixAddress = null;
585
586 // Extract IPv6 prefix from IA PD option
587 Optional<Dhcp6IaPdOption> iaPdOption = dhcp6.getOptions()
588 .stream()
589 .filter(opt -> opt instanceof Dhcp6IaPdOption)
590 .map(opt -> (Dhcp6IaPdOption) opt)
591 .findFirst();
592
593 Optional<Dhcp6IaPrefixOption> iaPrefixOption;
594 if (iaPdOption.isPresent()) {
595 log.warn("IA_PD option found {}", iaPdOption);
596
597 iaPrefixOption = iaPdOption.get().getOptions().stream()
598 .filter(opt -> opt instanceof Dhcp6IaPrefixOption)
599 .map(opt -> (Dhcp6IaPrefixOption) opt)
600 .findFirst();
601 } else {
602 log.warn("IA_PD option NOT found");
603
604 iaPrefixOption = Optional.empty();
605 }
606 if (iaPrefixOption.isPresent()) {
607 log.warn("IAPrefix Option within IA_PD option found {}", iaPrefixOption);
608
609 prefixAddress = iaPrefixOption.get().getIp6Prefix();
610 int prefixLen = (int) iaPrefixOption.get().getPrefixLength();
611 log.warn("Prefix length is {} bits", prefixLen);
612 prefixPrefix = IpPrefix.valueOf(prefixAddress, prefixLen);
613
614 } else {
615 log.warn("Can't find IPv6 prefix from DHCPv6 {}", dhcp6);
616 }
617
618 return prefixPrefix;
619 }
620
621 /**
622 * remove host or route.
623 *
624 * @param directConnFlag flag to show that packet is from directly connected client
625 * @param dhcp6Packet the dhcp6 payload
626 * @param clientPacket client's ethernet packet
627 * @param clientIpv6 client's Ipv6 packet
Kalhee Kimaa5172a2017-09-15 17:43:27 +0000628 * @param clientInterface client interfaces
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000629 */
630 private void removeHostOrRoute(boolean directConnFlag, DHCP6 dhcp6Packet,
631 Ethernet clientPacket, IPv6 clientIpv6,
Kalhee Kimaa5172a2017-09-15 17:43:27 +0000632 Interface clientInterface) {
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000633 log.debug("extractPrefix enters {}", dhcp6Packet);
634 // add host or route
635 if (isDhcp6Release(dhcp6Packet)) {
636 IpAddress ip = null;
637 if (directConnFlag) {
638 // Add to host store if it is connected to network directly
639 ip = extractIpAddress(dhcp6Packet);
640 if (ip != null) {
Kalhee Kimaa5172a2017-09-15 17:43:27 +0000641 VlanId vlanId = clientInterface.vlan();
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000642 MacAddress clientMac = clientPacket.getSourceMAC();
643 HostId hostId = HostId.hostId(clientMac, vlanId);
644 log.debug("remove Host {} ip for directly connected.", hostId.toString());
645
646 log.debug("client mac {} client vlan {}", HexString.toHexString(clientMac.toBytes(), ":"), vlanId);
647
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000648 // Remove host's ip of when dhcp release msg is received
Yi Tseng4b013202017-09-08 17:22:51 -0700649 providerService.removeIpFromHost(hostId, ip);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000650 } else {
651 log.debug("ipAddress not found. Do not add Host for directly connected.");
652 }
653 } else {
654 // Remove from route store if it is not connected to network directly
655 IpAddress nextHopIp = IpAddress.valueOf(IpAddress.Version.INET6, clientIpv6.getSourceAddress());
656
657 DHCP6 leafDhcp = getDhcp6Leaf(dhcp6Packet);
658 ip = extractIpAddress(leafDhcp);
659 if (ip == null) {
660 log.debug("ip is null");
661 } else {
662 Route routeForIP = new Route(Route.Source.STATIC, ip.toIpPrefix(), nextHopIp);
663 log.debug("removing route of 128 address for indirectly connected.");
664 log.debug("128 ip {}, nexthop {}", HexString.toHexString(ip.toOctets(), ":"),
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000665 HexString.toHexString(nextHopIp.toOctets(), ":"));
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000666 routeStore.removeRoute(routeForIP);
667 }
668
669 IpPrefix ipPrefix = extractPrefix(leafDhcp);
670 if (ipPrefix == null) {
671 log.debug("ipPrefix is null ");
672 } else {
673 Route routeForPrefix = new Route(Route.Source.STATIC, ipPrefix, nextHopIp);
674 log.debug("removing route of PD for indirectly connected.");
675 log.debug("pd ip {}, nexthop {}", HexString.toHexString(ipPrefix.address().toOctets(), ":"),
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000676 HexString.toHexString(nextHopIp.toOctets(), ":"));
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000677
678 routeStore.removeRoute(routeForPrefix);
679 }
680 }
681 }
682 }
683
684 /**
685 * add host or route.
686 *
687 * @param directConnFlag flag to show that packet is from directly connected client
688 * @param dhcp6Relay the dhcp6 payload
689 * @param embeddedDhcp6 client's ethernet packetthe dhcp6 payload within relay
690 * @param clientMac client macAddress
Kalhee Kimaa5172a2017-09-15 17:43:27 +0000691 * @param clientInterface client interface
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000692 */
693 private void addHostOrRoute(boolean directConnFlag, DHCP6 dhcp6Relay,
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000694 DHCP6 embeddedDhcp6,
695 MacAddress clientMac,
696 Interface clientInterface) {
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000697 log.debug("addHostOrRoute entered.");
698 // add host or route
699 if (isDhcp6Reply(dhcp6Relay)) {
700 IpAddress ip = null;
701 if (directConnFlag) {
702 // Add to host store if it connect to network directly
703 ip = extractIpAddress(embeddedDhcp6);
704 if (ip != null) {
705 Set<IpAddress> ips = Sets.newHashSet(ip);
Yi Tseng4b013202017-09-08 17:22:51 -0700706
707 // FIXME: we should use vlan id from original packet (solicit, request)
Kalhee Kimaa5172a2017-09-15 17:43:27 +0000708 VlanId vlanId = clientInterface.vlan();
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000709 HostId hostId = HostId.hostId(clientMac, vlanId);
Yi Tseng4b013202017-09-08 17:22:51 -0700710 Host host = hostService.getHost(hostId);
Kalhee Kimaa5172a2017-09-15 17:43:27 +0000711 HostLocation hostLocation = new HostLocation(clientInterface.connectPoint(),
Yi Tseng4b013202017-09-08 17:22:51 -0700712 System.currentTimeMillis());
713 Set<HostLocation> hostLocations = Sets.newHashSet(hostLocation);
714
715 if (host != null) {
716 // Dual homing support:
717 // if host exists, use old locations and new location
718 hostLocations.addAll(host.locations());
719 }
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000720 HostDescription desc = new DefaultHostDescription(clientMac, vlanId,
Yi Tseng4b013202017-09-08 17:22:51 -0700721 hostLocations, ips,
722 false);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000723 log.debug("adding Host for directly connected.");
724 log.debug("client mac {} client vlan {} hostlocation {}",
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000725 HexString.toHexString(clientMac.toBytes(), ":"),
726 vlanId, hostLocation.toString());
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000727
728 // Replace the ip when dhcp server give the host new ip address
Yi Tseng4b013202017-09-08 17:22:51 -0700729 providerService.hostDetected(hostId, desc, false);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000730 } else {
731 log.warn("ipAddress not found. Do not add Host for directly connected.");
732 }
733 } else {
734 // Add to route store if it does not connect to network directly
735 IpAddress nextHopIp = IpAddress.valueOf(IpAddress.Version.INET6, dhcp6Relay.getPeerAddress());
736
737 DHCP6 leafDhcp = getDhcp6Leaf(embeddedDhcp6);
738 ip = extractIpAddress(leafDhcp);
739 if (ip == null) {
740 log.warn("ip is null");
741 } else {
742 Route routeForIP = new Route(Route.Source.STATIC, ip.toIpPrefix(), nextHopIp);
743 log.warn("adding Route of 128 address for indirectly connected.");
744 routeStore.updateRoute(routeForIP);
745 }
746
747 IpPrefix ipPrefix = extractPrefix(leafDhcp);
748 if (ipPrefix == null) {
749 log.warn("ipPrefix is null ");
750 } else {
751 Route routeForPrefix = new Route(Route.Source.STATIC, ipPrefix, nextHopIp);
752 log.warn("adding Route of PD for indirectly connected.");
753 routeStore.updateRoute(routeForPrefix);
754 }
755 }
756 }
757 }
758
759 /**
760 *
761 * build the DHCP6 solicit/request packet with gatewayip.
762 *
763 * @param context packet context
764 * @param clientPacket client ethernet packet
765 * @param clientInterfaces set of client side interfaces
766 */
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000767 private InternalPacket processDhcp6PacketFromClient(PacketContext context,
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000768 Ethernet clientPacket, Set<Interface> clientInterfaces) {
769 Ip6Address relayAgentIp = getRelayAgentIPv6Address(clientInterfaces);
770 MacAddress relayAgentMac = clientInterfaces.iterator().next().mac();
771 if (relayAgentIp == null || relayAgentMac == null) {
772 log.warn("Missing DHCP relay agent interface Ipv6 addr config for "
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000773 + "packet from client on port: {}. Aborting packet processing",
774 clientInterfaces.iterator().next().connectPoint());
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000775 return null;
776 }
777
778 // get dhcp6 header.
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000779 IPv6 clientIpv6 = (IPv6) clientPacket.getPayload();
780 UDP clientUdp = (UDP) clientIpv6.getPayload();
781 DHCP6 clientDhcp6 = (DHCP6) clientUdp.getPayload();
782
783 boolean directConnFlag = directlyConnected(clientDhcp6);
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000784 Interface serverInterface = directConnFlag ? getServerInterface() : getIndirectServerInterface();
785 if (serverInterface == null) {
786 log.warn("Can't get {} server interface, ignore", directConnFlag ? "direct" : "indirect");
787 return null;
788 }
789 Ip6Address ipFacingServer = getFirstIpFromInterface(serverInterface);
790 MacAddress macFacingServer = serverInterface.mac();
791 if (ipFacingServer == null || macFacingServer == null) {
792 log.warn("No IP v6 address for server Interface {}", serverInterface);
793 return null;
794 }
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000795
796 Ethernet etherReply = (Ethernet) clientPacket.clone();
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000797 etherReply.setSourceMACAddress(macFacingServer);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000798
Kalhee Kim2575d2c2017-09-26 20:21:53 +0000799 if ((directConnFlag && this.dhcpConnectMac == null) ||
800 !directConnFlag && this.indirectDhcpConnectMac == null && this.dhcpConnectMac == null) {
801 log.warn("Packet received from {} connected client.", directConnFlag ? "directly" : "indirectly");
Charles Chanf6a77be2017-10-30 13:44:33 -0700802 log.warn("DHCP6 {} not yet resolved .. Aborting DHCP packet processing from client on port: {}",
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000803 (this.dhcpGatewayIp == null) ? "server IP " + this.dhcpServerIp
804 : "gateway IP " + this.dhcpGatewayIp,
805 clientInterfaces.iterator().next().connectPoint());
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000806 return null;
807 }
808
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000809 if (this.dhcpServerConnectPoint == null) {
Kalhee Kim2575d2c2017-09-26 20:21:53 +0000810 log.warn("DHCP6 server connection point direct {} directConn {} indirectConn {} is not set up yet",
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000811 directConnFlag, this.dhcpServerConnectPoint, this.indirectDhcpServerConnectPoint);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000812 return null;
813 }
814
Kalhee Kimaa5172a2017-09-15 17:43:27 +0000815 etherReply.setDestinationMACAddress(this.dhcpConnectMac);
816 etherReply.setVlanID(this.dhcpConnectVlan.toShort());
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000817
818 IPv6 ipv6Packet = (IPv6) etherReply.getPayload();
Kalhee Kim45c93852017-10-27 20:16:26 +0000819 Ip6Address peerAddress = null;
820 if (directConnFlag) {
821 peerAddress = Ip6Address.valueOf(ipv6Packet.getSourceAddress());
Kalhee Kim45c93852017-10-27 20:16:26 +0000822 } else {
823 MacAddress gwOrClientMac = MacAddress.valueOf(clientPacket.getSourceMACAddress());
824 VlanId vlanId = VlanId.vlanId(clientPacket.getVlanID());
825 HostId gwOrClientHostId = HostId.hostId(gwOrClientMac, vlanId);
826 Host gwOrClientHost = hostService.getHost(gwOrClientHostId);
827
828 if (gwOrClientHost == null) {
829 log.warn("Can't find client gateway/server host {}", gwOrClientHostId);
830 return null;
831 }
832 // pick out the first gloabl ip address
Charles Chanf6a77be2017-10-30 13:44:33 -0700833 peerAddress = gwOrClientHost.ipAddresses().stream()
834 .filter(IpAddress::isIp6).filter(ip6 -> !ip6.isLinkLocal())
835 .map(IpAddress::getIp6Address).findFirst().orElse(null);
Kalhee Kim45c93852017-10-27 20:16:26 +0000836
837 if (peerAddress == null) {
838 log.warn("Can't find client gateway/server for mac {} ip {}", gwOrClientMac,
839 HexString.toHexString(ipv6Packet.getSourceAddress()));
Charles Chanf6a77be2017-10-30 13:44:33 -0700840 log.warn("Can't find IP address of client gateway/ClienHost address {} for peerAddress",
841 gwOrClientHost);
Kalhee Kim45c93852017-10-27 20:16:26 +0000842 return null;
843 }
844 }
845 ipv6Packet.setSourceAddress(ipFacingServer.toOctets());
Kalhee Kimaa5172a2017-09-15 17:43:27 +0000846 ipv6Packet.setDestinationAddress(this.dhcpServerIp.toOctets());
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000847
848 UDP udpPacket = (UDP) ipv6Packet.getPayload();
849 udpPacket.setSourcePort(UDP.DHCP_V6_SERVER_PORT);
850 DHCP6 dhcp6Packet = (DHCP6) udpPacket.getPayload();
851 byte[] dhcp6PacketByte = dhcp6Packet.serialize();
852
853 // notify onos and quagga to release PD
854 //releasePD(dhcp6Packet);
Kalhee Kimaa5172a2017-09-15 17:43:27 +0000855 ConnectPoint clientConnectionPoint = context.inPacket().receivedFrom();
856 VlanId vlanIdInUse = VlanId.vlanId(clientPacket.getVlanID());
857 Interface clientInterface = interfaceService.getInterfacesByPort(clientConnectionPoint)
Charles Chanf6a77be2017-10-30 13:44:33 -0700858 .stream().filter(iface -> interfaceContainsVlan(iface, vlanIdInUse))
859 .findFirst().orElse(null);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000860
Kalhee Kimaa5172a2017-09-15 17:43:27 +0000861 removeHostOrRoute(directConnFlag, dhcp6Packet, clientPacket, clientIpv6, clientInterface);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000862
863 DHCP6 dhcp6Relay = new DHCP6();
864 dhcp6Relay.setMsgType(DHCP6.MsgType.RELAY_FORW.value());
865 // link address: server uses the address to identify the link on which the client
866 // is located.
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000867 if (directConnFlag) {
868 dhcp6Relay.setLinkAddress(relayAgentIp.toOctets());
869 log.debug("direct connection: relayAgentIp obtained dynamically {}",
870 HexString.toHexString(relayAgentIp.toOctets(), ":"));
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000871
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000872 } else {
873 if (this.indirectDhcpServerIp == null) {
874 log.warn("indirect DhcpServerIp not available, use default DhcpServerIp {}",
Kalhee Kim2575d2c2017-09-26 20:21:53 +0000875 HexString.toHexString(this.dhcpServerIp.toOctets()));
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000876 } else {
877 // Indirect case, replace destination to indirect dhcp server if exist
878 // Check if mac is obtained for valid server ip
879 if (this.indirectDhcpConnectMac == null) {
880 log.warn("DHCP6 {} not yet resolved .. Aborting DHCP "
881 + "packet processing from client on port: {}",
882 (this.indirectDhcpGatewayIp == null) ? "server IP " + this.indirectDhcpServerIp
883 : "gateway IP " + this.indirectDhcpGatewayIp,
Kalhee Kim2575d2c2017-09-26 20:21:53 +0000884 clientInterfaces.iterator().next().connectPoint());
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000885 return null;
886 }
887 etherReply.setDestinationMACAddress(this.indirectDhcpConnectMac);
888 etherReply.setVlanID(this.indirectDhcpConnectVlan.toShort());
889 ipv6Packet.setDestinationAddress(this.indirectDhcpServerIp.toOctets());
Kalhee Kim2575d2c2017-09-26 20:21:53 +0000890
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000891 }
892 if (this.indirectRelayAgentIpFromCfg == null) {
893 dhcp6Relay.setLinkAddress(relayAgentIp.toOctets());
894 log.warn("indirect connection: relayAgentIp NOT availale from config file! Use dynamic. {}",
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000895 HexString.toHexString(relayAgentIp.toOctets(), ":"));
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000896 } else {
897 dhcp6Relay.setLinkAddress(this.indirectRelayAgentIpFromCfg.toOctets());
898 log.debug("indirect connection: relayAgentIp from config file is available! {}",
899 HexString.toHexString(this.indirectRelayAgentIpFromCfg.toOctets(), ":"));
900 }
901 }
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000902
903 // peer address: address of the client or relay agent from which
904 // the message to be relayed was received.
Kalhee Kim45c93852017-10-27 20:16:26 +0000905 dhcp6Relay.setPeerAddress(peerAddress.toOctets());
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000906 List<Dhcp6Option> options = new ArrayList<Dhcp6Option>();
907
908 // directly connected case, hop count is zero
909 // otherwise, hop count + 1
910 if (directConnFlag) {
911 dhcp6Relay.setHopCount((byte) 0);
912 } else {
913 dhcp6Relay.setHopCount((byte) (dhcp6Packet.getHopCount() + 1));
914 }
915
916 // create relay message option
917 Dhcp6Option relayMessage = new Dhcp6Option();
918 relayMessage.setCode(DHCP6.OptionCode.RELAY_MSG.value());
919 relayMessage.setLength((short) dhcp6PacketByte.length);
920 relayMessage.setData(dhcp6PacketByte);
921 options.add(relayMessage);
922
923 // create interfaceId option
Kalhee Kimaa5172a2017-09-15 17:43:27 +0000924 String inPortString = "-" + context.inPacket().receivedFrom().toString() + ":";
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000925 Dhcp6Option interfaceId = new Dhcp6Option();
926 interfaceId.setCode(DHCP6.OptionCode.INTERFACE_ID.value());
927 byte[] clientSoureMacBytes = clientPacket.getSourceMACAddress();
928 byte[] inPortStringBytes = inPortString.getBytes();
Kalhee Kimaa5172a2017-09-15 17:43:27 +0000929 byte[] vlanIdBytes = new byte[2];
930 vlanIdBytes[0] = (byte) (clientPacket.getVlanID() & 0xff);
931 vlanIdBytes[1] = (byte) ((clientPacket.getVlanID() >> 8) & 0xff);
932 byte[] interfaceIdBytes = new byte[clientSoureMacBytes.length +
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000933 inPortStringBytes.length + vlanIdBytes.length];
Kalhee Kimaa5172a2017-09-15 17:43:27 +0000934 log.debug("Length: interfaceIdBytes {} clientSoureMacBytes {} inPortStringBytes {} vlan {}",
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000935 interfaceIdBytes.length, clientSoureMacBytes.length, inPortStringBytes.length,
936 vlanIdBytes.length);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000937
938 System.arraycopy(clientSoureMacBytes, 0, interfaceIdBytes, 0, clientSoureMacBytes.length);
939 System.arraycopy(inPortStringBytes, 0, interfaceIdBytes, clientSoureMacBytes.length, inPortStringBytes.length);
Kalhee Kimaa5172a2017-09-15 17:43:27 +0000940 System.arraycopy(vlanIdBytes, 0, interfaceIdBytes, clientSoureMacBytes.length + inPortStringBytes.length,
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000941 vlanIdBytes.length);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000942
943 interfaceId.setData(interfaceIdBytes);
944 interfaceId.setLength((short) interfaceIdBytes.length);
945
946 options.add(interfaceId);
947
948 log.debug("interfaceId write srcMac {} portString {}",
949 HexString.toHexString(clientSoureMacBytes, ":"), inPortString);
950 dhcp6Relay.setOptions(options);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000951 udpPacket.setPayload(dhcp6Relay);
952 udpPacket.resetChecksum();
953 ipv6Packet.setPayload(udpPacket);
Charles Chan7edf7642017-10-09 11:07:25 -0400954 ipv6Packet.setHopLimit((byte) 64);
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000955 etherReply.setPayload(ipv6Packet);
956
Kalhee Kim2575d2c2017-09-26 20:21:53 +0000957 if (directConnFlag) {
958 return new InternalPacket(etherReply, this.dhcpServerConnectPoint);
959 } else {
960 if (this.indirectDhcpServerIp == null) {
961 return new InternalPacket(etherReply, this.dhcpServerConnectPoint);
962 } else {
963 return new InternalPacket(etherReply, this.indirectDhcpServerConnectPoint);
964 }
965 }
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000966 }
967
968 /**
969 *
970 * process the DHCP6 relay-reply packet from dhcp server.
971 *
972 * @param context packet context
973 * @param receivedPacket server ethernet packet
974 * @param recevingInterfaces set of server side interfaces
975 */
976 private InternalPacket processDhcp6PacketFromServer(PacketContext context,
977 Ethernet receivedPacket, Set<Interface> recevingInterfaces) {
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000978 // get dhcp6 header.
979 Ethernet etherReply = (Ethernet) receivedPacket.clone();
980 IPv6 ipv6Packet = (IPv6) etherReply.getPayload();
981 UDP udpPacket = (UDP) ipv6Packet.getPayload();
982 DHCP6 dhcp6Relay = (DHCP6) udpPacket.getPayload();
983
984 Boolean directConnFlag = directlyConnected(dhcp6Relay);
Kalhee Kim2575d2c2017-09-26 20:21:53 +0000985 ConnectPoint inPort = context.inPacket().receivedFrom();
986 if ((directConnFlag || (!directConnFlag && this.indirectDhcpServerIp == null))
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000987 && !inPort.equals(this.dhcpServerConnectPoint)) {
Kalhee Kim2575d2c2017-09-26 20:21:53 +0000988 log.warn("Receiving port {} is not the same as server connect point {} for direct or indirect-null",
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000989 inPort, this.dhcpServerConnectPoint);
Kalhee Kim2575d2c2017-09-26 20:21:53 +0000990 return null;
991 }
992
993 if (!directConnFlag && this.indirectDhcpServerIp != null &&
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000994 !inPort.equals(this.indirectDhcpServerConnectPoint)) {
Kalhee Kim2575d2c2017-09-26 20:21:53 +0000995 log.warn("Receiving port {} is not the same as server connect point {} for indirect",
Kalhee Kim683ba3b2017-10-18 18:30:23 +0000996 inPort, this.indirectDhcpServerConnectPoint);
Kalhee Kim2575d2c2017-09-26 20:21:53 +0000997 return null;
998 }
999
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001000
1001 Dhcp6InterfaceIdOption interfaceIdOption = dhcp6Relay.getOptions().stream()
1002 .filter(opt -> opt instanceof Dhcp6InterfaceIdOption)
1003 .map(opt -> (Dhcp6InterfaceIdOption) opt)
1004 .findFirst()
1005 .orElse(null);
1006
1007 if (interfaceIdOption == null) {
1008 log.warn("Interface Id option is not present, abort packet...");
1009 return null;
1010 }
1011
1012 MacAddress peerMac = interfaceIdOption.getMacAddress();
1013 String clientConnectionPointStr = new String(interfaceIdOption.getInPort());
1014
1015 ConnectPoint clientConnectionPoint = ConnectPoint.deviceConnectPoint(clientConnectionPointStr);
Kalhee Kimaa5172a2017-09-15 17:43:27 +00001016 VlanId vlanIdInUse = VlanId.vlanId(interfaceIdOption.getVlanId());
1017 Interface clientInterface = interfaceService.getInterfacesByPort(clientConnectionPoint)
1018 .stream()
1019 .filter(iface -> interfaceContainsVlan(iface, vlanIdInUse))
1020 .findFirst()
1021 .orElse(null);
1022 if (clientInterface == null) {
1023 log.warn("Cannot get client interface for from packet, abort... vlan {}", vlanIdInUse.toString());
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001024 return null;
1025 }
Kalhee Kimaa5172a2017-09-15 17:43:27 +00001026 MacAddress relayAgentMac = clientInterface.mac();
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001027 if (relayAgentMac == null) {
1028 log.warn("Can not get interface mac, abort packet..");
1029 return null;
1030 }
1031 etherReply.setSourceMACAddress(relayAgentMac);
1032
1033 // find destMac
1034 MacAddress clientMac = null;
Yi Tseng4b013202017-09-08 17:22:51 -07001035 Ip6Address peerAddress = Ip6Address.valueOf(dhcp6Relay.getPeerAddress());
1036 Set<Host> clients = hostService.getHostsByIp(peerAddress);
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001037 if (clients.isEmpty()) {
1038 log.warn("There's no host found for this address {}",
Kalhee Kim683ba3b2017-10-18 18:30:23 +00001039 HexString.toHexString(dhcp6Relay.getPeerAddress(), ":"));
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001040 log.warn("Let's look up interfaceId {}", HexString.toHexString(peerMac.toBytes(), ":"));
1041 clientMac = peerMac;
1042 } else {
1043 clientMac = clients.iterator().next().mac();
1044 if (clientMac == null) {
1045 log.warn("No client mac address found, abort packet...");
1046 return null;
1047 }
1048 log.warn("Client mac address found from getHostByIp");
1049
1050 }
1051 etherReply.setDestinationMACAddress(clientMac);
1052
1053 // ip header
1054 ipv6Packet.setSourceAddress(dhcp6Relay.getLinkAddress());
1055 ipv6Packet.setDestinationAddress(dhcp6Relay.getPeerAddress());
1056 // udp header
1057 udpPacket.setSourcePort(UDP.DHCP_V6_SERVER_PORT);
1058 if (directConnFlag) {
1059 udpPacket.setDestinationPort(UDP.DHCP_V6_CLIENT_PORT);
1060 } else {
1061 udpPacket.setDestinationPort(UDP.DHCP_V6_SERVER_PORT);
1062 }
1063
1064 DHCP6 embeddedDhcp6 = dhcp6Relay.getOptions().stream()
Kalhee Kim683ba3b2017-10-18 18:30:23 +00001065 .filter(opt -> opt instanceof Dhcp6RelayOption)
1066 .map(BasePacket::getPayload)
1067 .map(pld -> (DHCP6) pld)
1068 .findFirst()
1069 .orElse(null);
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001070
1071
1072 // add host or route
Kalhee Kimaa5172a2017-09-15 17:43:27 +00001073 addHostOrRoute(directConnFlag, dhcp6Relay, embeddedDhcp6, clientMac, clientInterface);
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001074
1075 udpPacket.setPayload(embeddedDhcp6);
1076 udpPacket.resetChecksum();
1077 ipv6Packet.setPayload(udpPacket);
1078 etherReply.setPayload(ipv6Packet);
1079
1080 return new InternalPacket(etherReply, clientConnectionPoint);
1081 }
1082
Yi Tseng2fe8f3f2017-09-07 16:22:51 -07001083 // Returns the first v6 interface ip out of a set of interfaces or null.
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001084 // Checks all interfaces, and ignores v6 interface ips
1085 private Ip6Address getRelayAgentIPv6Address(Set<Interface> intfs) {
1086 for (Interface intf : intfs) {
1087 for (InterfaceIpAddress ip : intf.ipAddressesList()) {
1088 Ip6Address relayAgentIp = ip.ipAddress().getIp6Address();
1089 if (relayAgentIp != null) {
1090 return relayAgentIp;
1091 }
1092 }
1093 }
1094 return null;
Yi Tseng51301292017-07-28 13:02:59 -07001095 }
Yi Tsenge72fbb52017-08-02 15:03:31 -07001096
1097 @Override
1098 public void setDefaultDhcpServerConfigs(Collection<DhcpServerConfig> configs) {
Yi Tseng2fe8f3f2017-09-07 16:22:51 -07001099 setDhcpServerConfigs(configs, defaultServerInfoList);
1100 reloadServerSettings();
1101 }
1102
1103 @Override
1104 public void setIndirectDhcpServerConfigs(Collection<DhcpServerConfig> configs) {
1105 setDhcpServerConfigs(configs, indirectServerInfoList);
1106 reloadServerSettings();
1107 }
1108
1109 public void setDhcpServerConfigs(Collection<DhcpServerConfig> configs, List<DhcpServerInfo> serverInfoList) {
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001110 if (configs.size() == 0) {
1111 // no config to update
1112 return;
1113 }
1114
1115 // TODO: currently we pick up first DHCP server config.
1116 // Will use other server configs in the future for HA.
1117 DhcpServerConfig serverConfig = configs.iterator().next();
Yi Tseng2fe8f3f2017-09-07 16:22:51 -07001118
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001119 if (!serverConfig.getDhcpServerIp6().isPresent()) {
Yi Tseng2fe8f3f2017-09-07 16:22:51 -07001120 // not a DHCPv6 config
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001121 return;
1122 }
1123
Yi Tseng2fe8f3f2017-09-07 16:22:51 -07001124 if (!serverInfoList.isEmpty()) {
1125 // remove old server info
1126 DhcpServerInfo oldServerInfo = serverInfoList.remove(0);
1127
1128 // stop monitoring gateway or server
1129 oldServerInfo.getDhcpGatewayIp6().ifPresent(gatewayIp -> {
1130 hostService.stopMonitoringIp(gatewayIp);
1131 });
1132 oldServerInfo.getDhcpServerIp6().ifPresent(serverIp -> {
1133 hostService.stopMonitoringIp(serverIp);
Yi Tseng7da339e2017-10-23 19:39:39 -07001134 cancelDhcpPacket(serverIp);
Yi Tseng2fe8f3f2017-09-07 16:22:51 -07001135 });
1136 }
1137
1138 // Create new server info according to the config
1139 DhcpServerInfo newServerInfo = new DhcpServerInfo(serverConfig,
1140 DhcpServerInfo.Version.DHCP_V6);
1141 checkState(newServerInfo.getDhcpServerConnectPoint().isPresent(),
1142 "Connect point not exists");
1143 checkState(newServerInfo.getDhcpServerIp6().isPresent(),
1144 "IP of DHCP server not exists");
1145
1146 log.debug("DHCP server connect point: {}", newServerInfo.getDhcpServerConnectPoint().orElse(null));
1147 log.debug("DHCP server IP: {}", newServerInfo.getDhcpServerIp6().orElse(null));
1148
Yi Tseng7da339e2017-10-23 19:39:39 -07001149 Ip6Address serverIp = newServerInfo.getDhcpServerIp6().get();
1150 Ip6Address ipToProbe;
Yi Tseng2fe8f3f2017-09-07 16:22:51 -07001151 if (newServerInfo.getDhcpGatewayIp6().isPresent()) {
1152 ipToProbe = newServerInfo.getDhcpGatewayIp6().get();
1153 } else {
1154 ipToProbe = newServerInfo.getDhcpServerIp6().orElse(null);
1155 }
1156 String hostToProbe = newServerInfo.getDhcpGatewayIp6()
1157 .map(ip -> "gateway").orElse("server");
1158
1159 log.debug("Probing to resolve {} IP {}", hostToProbe, ipToProbe);
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001160 hostService.startMonitoringIp(ipToProbe);
1161
1162 Set<Host> hosts = hostService.getHostsByIp(ipToProbe);
1163 if (!hosts.isEmpty()) {
1164 Host host = hosts.iterator().next();
Yi Tseng2fe8f3f2017-09-07 16:22:51 -07001165 newServerInfo.setDhcpConnectVlan(host.vlan());
1166 newServerInfo.setDhcpConnectMac(host.mac());
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001167 }
Yi Tseng2fe8f3f2017-09-07 16:22:51 -07001168 // Add new server info
1169 serverInfoList.add(0, newServerInfo);
Yi Tsenge72fbb52017-08-02 15:03:31 -07001170
Yi Tseng2fe8f3f2017-09-07 16:22:51 -07001171 // Remove duplicated server info
1172 Set<DhcpServerInfo> nonDupServerInfoList = Sets.newLinkedHashSet();
1173 nonDupServerInfoList.addAll(serverInfoList);
1174 serverInfoList.clear();
1175 serverInfoList.addAll(nonDupServerInfoList);
Yi Tseng7da339e2017-10-23 19:39:39 -07001176 requestDhcpPacket(serverIp);
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001177 }
1178
1179 class InternalHostListener implements HostListener {
1180 @Override
1181 public void event(HostEvent event) {
1182 switch (event.type()) {
1183 case HOST_ADDED:
1184 case HOST_UPDATED:
1185 hostUpdated(event.subject());
1186 break;
1187 case HOST_REMOVED:
1188 hostRemoved(event.subject());
1189 break;
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001190 default:
1191 break;
1192 }
1193 }
1194 }
1195
1196 /**
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001197 * Handle host updated.
1198 * If the host is DHCP server or gateway, update connect mac and vlan.
1199 *
1200 * @param host the host
1201 */
1202 private void hostUpdated(Host host) {
Yi Tseng7da339e2017-10-23 19:39:39 -07001203 hostUpdated(host, defaultServerInfoList);
1204 hostUpdated(host, indirectServerInfoList);
Yi Tseng2fe8f3f2017-09-07 16:22:51 -07001205 reloadServerSettings();
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001206 }
1207
Yi Tseng7da339e2017-10-23 19:39:39 -07001208 private void hostUpdated(Host host, List<DhcpServerInfo> serverInfoList) {
1209 DhcpServerInfo serverInfo;
1210 Ip6Address targetIp;
1211 if (!serverInfoList.isEmpty()) {
1212 serverInfo = serverInfoList.get(0);
1213 Ip6Address serverIp = serverInfo.getDhcpServerIp6().orElse(null);
1214 targetIp = serverInfo.getDhcpGatewayIp6().orElse(null);
1215
1216 if (targetIp == null) {
1217 targetIp = serverIp;
1218 }
1219
1220 if (targetIp != null) {
1221 if (host.ipAddresses().contains(targetIp)) {
1222 serverInfo.setDhcpConnectMac(host.mac());
1223 serverInfo.setDhcpConnectVlan(host.vlan());
1224 requestDhcpPacket(serverIp);
1225 }
1226 }
1227 }
1228 }
1229
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001230 /**
1231 * Handle host removed.
1232 * If the host is DHCP server or gateway, unset connect mac and vlan.
1233 *
1234 * @param host the host
1235 */
1236 private void hostRemoved(Host host) {
Yi Tseng7da339e2017-10-23 19:39:39 -07001237 hostRemoved(host, defaultServerInfoList);
1238 hostRemoved(host, indirectServerInfoList);
Yi Tseng2fe8f3f2017-09-07 16:22:51 -07001239 reloadServerSettings();
1240 }
1241
Yi Tseng7da339e2017-10-23 19:39:39 -07001242 private void hostRemoved(Host host, List<DhcpServerInfo> serverInfoList) {
1243 DhcpServerInfo serverInfo;
1244 Ip6Address targetIp;
1245
1246 if (!serverInfoList.isEmpty()) {
1247 serverInfo = serverInfoList.get(0);
1248 Ip6Address serverIp = serverInfo.getDhcpServerIp6().orElse(null);
1249 targetIp = serverInfo.getDhcpGatewayIp6().orElse(null);
1250
1251 if (targetIp == null) {
1252 targetIp = serverIp;
1253 }
1254
1255 if (targetIp != null) {
1256 if (host.ipAddresses().contains(targetIp)) {
1257 serverInfo.setDhcpConnectVlan(null);
1258 serverInfo.setDhcpConnectMac(null);
1259 cancelDhcpPacket(serverIp);
1260 }
1261 }
1262 }
1263 }
1264
Yi Tseng2fe8f3f2017-09-07 16:22:51 -07001265 private void reloadServerSettings() {
1266 DhcpServerInfo serverInfo;
1267 if (!defaultServerInfoList.isEmpty()) {
1268 serverInfo = defaultServerInfoList.get(0);
1269 this.dhcpConnectMac = serverInfo.getDhcpConnectMac().orElse(null);
1270 this.dhcpGatewayIp = serverInfo.getDhcpGatewayIp6().orElse(null);
1271 this.dhcpServerIp = serverInfo.getDhcpServerIp6().orElse(null);
1272 this.dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1273 this.dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1274 }
1275
1276 if (!indirectServerInfoList.isEmpty()) {
1277 serverInfo = indirectServerInfoList.get(0);
1278 this.indirectDhcpConnectMac = serverInfo.getDhcpConnectMac().orElse(null);
1279 this.indirectDhcpGatewayIp = serverInfo.getDhcpGatewayIp6().orElse(null);
1280 this.indirectDhcpServerIp = serverInfo.getDhcpServerIp6().orElse(null);
1281 this.indirectDhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1282 this.indirectDhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1283 this.indirectRelayAgentIpFromCfg = serverInfo.getRelayAgentIp6().orElse(null);
Kalhee Kim1b5094f2017-09-05 19:05:06 +00001284 }
Yi Tsenge72fbb52017-08-02 15:03:31 -07001285 }
Kalhee Kim683ba3b2017-10-18 18:30:23 +00001286
1287 /**
1288 * Returns the first interface ip from interface.
1289 *
1290 * @param iface interface of one connect point
1291 * @return the first interface IP; null if not exists an IP address in
1292 * these interfaces
1293 */
1294 private Ip6Address getFirstIpFromInterface(Interface iface) {
1295 checkNotNull(iface, "Interface can't be null");
1296 return iface.ipAddressesList().stream()
1297 .map(InterfaceIpAddress::ipAddress)
1298 .filter(IpAddress::isIp6)
1299 .map(IpAddress::getIp6Address)
1300 .findFirst()
1301 .orElse(null);
1302 }
1303
1304 /**
1305 * Gets Interface facing to the server for default host.
1306 *
1307 * @return the Interface facing to the server; null if not found
1308 */
1309 private Interface getServerInterface() {
1310 if (dhcpServerConnectPoint == null || dhcpConnectVlan == null) {
1311 return null;
1312 }
1313 return interfaceService.getInterfacesByPort(dhcpServerConnectPoint)
1314 .stream()
1315 .filter(iface -> interfaceContainsVlan(iface, dhcpConnectVlan))
1316 .findFirst()
1317 .orElse(null);
1318 }
1319
1320 /**
1321 * Gets Interface facing to the server for indirect hosts.
1322 * Use default server Interface if indirect server not configured.
1323 *
1324 * @return the Interface facing to the server; null if not found
1325 */
1326 private Interface getIndirectServerInterface() {
1327 if (indirectDhcpServerConnectPoint == null || indirectDhcpConnectVlan == null) {
1328 return getServerInterface();
1329 }
1330 return interfaceService.getInterfacesByPort(indirectDhcpServerConnectPoint)
1331 .stream()
1332 .filter(iface -> interfaceContainsVlan(iface, indirectDhcpConnectVlan))
1333 .findFirst()
1334 .orElse(null);
1335 }
1336
Kalhee Kimaa5172a2017-09-15 17:43:27 +00001337 /**
1338 * Determind if an Interface contains a vlan id.
1339 *
1340 * @param iface the Interface
1341 * @param vlanId the vlan id
1342 * @return true if the Interface contains the vlan id
1343 */
1344 private boolean interfaceContainsVlan(Interface iface, VlanId vlanId) {
1345 if (vlanId.equals(VlanId.NONE)) {
1346 // untagged packet, check if vlan untagged or vlan native is not NONE
1347 return !iface.vlanUntagged().equals(VlanId.NONE) ||
1348 !iface.vlanNative().equals(VlanId.NONE);
1349 }
1350 // tagged packet, check if the interface contains the vlan
1351 return iface.vlanTagged().contains(vlanId);
1352 }
1353
Yi Tseng7da339e2017-10-23 19:39:39 -07001354 private void requestDhcpPacket(Ip6Address serverIp) {
1355 requestServerDhcpPacket(serverIp);
1356 requestClientDhcpPacket(serverIp);
1357 }
1358
1359 private void cancelDhcpPacket(Ip6Address serverIp) {
1360 cancelServerDhcpPacket(serverIp);
1361 cancelClientDhcpPacket(serverIp);
1362 }
1363
1364 private void cancelServerDhcpPacket(Ip6Address serverIp) {
1365 TrafficSelector serverSelector =
1366 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1367 .matchIPv6Src(serverIp.toIpPrefix())
1368 .build();
1369 packetService.cancelPackets(serverSelector,
1370 PacketPriority.CONTROL,
1371 appId);
1372 }
1373
1374 private void requestServerDhcpPacket(Ip6Address serverIp) {
1375 TrafficSelector serverSelector =
1376 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1377 .matchIPv6Src(serverIp.toIpPrefix())
1378 .build();
1379 packetService.requestPackets(serverSelector,
1380 PacketPriority.CONTROL,
1381 appId);
1382 }
1383
1384 private void cancelClientDhcpPacket(Ip6Address serverIp) {
1385 // Packet comes from relay
1386 TrafficSelector indirectClientSelector =
1387 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1388 .matchIPv6Dst(serverIp.toIpPrefix())
1389 .build();
1390 packetService.cancelPackets(indirectClientSelector,
1391 PacketPriority.CONTROL,
1392 appId);
1393
1394 // Packet comes from client
1395 packetService.cancelPackets(CLIENT_SERVER_SELECTOR,
1396 PacketPriority.CONTROL,
1397 appId);
1398 }
1399
1400 private void requestClientDhcpPacket(Ip6Address serverIp) {
1401 // Packet comes from relay
1402 TrafficSelector indirectClientSelector =
1403 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1404 .matchIPv6Dst(serverIp.toIpPrefix())
1405 .build();
1406 packetService.requestPackets(indirectClientSelector,
1407 PacketPriority.CONTROL,
1408 appId);
1409
1410 // Packet comes from client
1411 packetService.requestPackets(CLIENT_SERVER_SELECTOR,
1412 PacketPriority.CONTROL,
1413 appId);
1414 }
1415
1416 /**
1417 * Process the ignore rules.
1418 *
1419 * @param deviceId the device id
1420 * @param vlanId the vlan to be ignored
1421 * @param op the operation, ADD to install; REMOVE to uninstall rules
1422 */
1423 private void processIgnoreVlanRule(DeviceId deviceId, VlanId vlanId, Objective.Operation op) {
1424 TrafficTreatment dropTreatment = DefaultTrafficTreatment.builder().wipeDeferred().build();
1425 AtomicInteger installedCount = new AtomicInteger(DHCP_SELECTORS.size());
1426 DHCP_SELECTORS.forEach(trafficSelector -> {
1427 TrafficSelector selector = DefaultTrafficSelector.builder(trafficSelector)
1428 .matchVlanId(vlanId)
1429 .build();
1430
1431 ForwardingObjective.Builder builder = DefaultForwardingObjective.builder()
1432 .withFlag(ForwardingObjective.Flag.VERSATILE)
1433 .withSelector(selector)
1434 .withPriority(IGNORE_CONTROL_PRIORITY)
1435 .withTreatment(dropTreatment)
1436 .fromApp(appId);
1437
1438
1439 ObjectiveContext objectiveContext = new ObjectiveContext() {
1440 @Override
1441 public void onSuccess(Objective objective) {
1442 log.info("Ignore rule {} (Vlan id {}, device {}, selector {})",
1443 op, vlanId, deviceId, selector);
1444 int countDown = installedCount.decrementAndGet();
1445 if (countDown != 0) {
1446 return;
1447 }
1448 switch (op) {
1449 case ADD:
1450 ignoredVlans.put(deviceId, vlanId);
1451 break;
1452 case REMOVE:
1453 ignoredVlans.remove(deviceId, vlanId);
1454 break;
1455 default:
1456 log.warn("Unsupported objective operation {}", op);
1457 break;
1458 }
1459 }
1460
1461 @Override
1462 public void onError(Objective objective, ObjectiveError error) {
1463 log.warn("Can't {} ignore rule (vlan id {}, selector {}, device {}) due to {}",
1464 op, vlanId, selector, deviceId, error);
1465 }
1466 };
1467
1468 ForwardingObjective fwd;
1469 switch (op) {
1470 case ADD:
1471 fwd = builder.add(objectiveContext);
1472 break;
1473 case REMOVE:
1474 fwd = builder.remove(objectiveContext);
1475 break;
1476 default:
1477 log.warn("Unsupported objective operation {}", op);
1478 return;
1479 }
1480
1481 Device device = deviceService.getDevice(deviceId);
1482 if (device == null || !device.is(Pipeliner.class)) {
1483 log.warn("Device {} is not available now, wait until device is available", deviceId);
1484 return;
1485 }
1486 flowObjectiveService.apply(deviceId, fwd);
1487 });
1488 }
Yi Tseng51301292017-07-28 13:02:59 -07001489}