blob: 654c79d33d16038d209577490bfdb7b0aa2102c6 [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 */
Yi Tseng51301292017-07-28 13:02:59 -070017package org.onosproject.dhcprelay;
18
Yi Tseng525ff402017-10-23 19:39:39 -070019import com.google.common.collect.HashMultimap;
Yi Tseng919b2df2017-09-07 16:22:51 -070020import com.google.common.collect.Lists;
Yi Tseng525ff402017-10-23 19:39:39 -070021import com.google.common.collect.Multimap;
Kalhee Kim45fede42017-09-05 19:05:06 +000022import org.apache.felix.scr.annotations.Activate;
23import org.apache.felix.scr.annotations.Deactivate;
Kalhee Kim45fede42017-09-05 19:05:06 +000024import com.google.common.collect.Sets;
25import com.google.common.collect.ImmutableSet;
Yi Tseng51301292017-07-28 13:02:59 -070026import org.apache.felix.scr.annotations.Component;
27import org.apache.felix.scr.annotations.Property;
Kalhee Kim45fede42017-09-05 19:05:06 +000028import org.apache.felix.scr.annotations.Reference;
29import org.apache.felix.scr.annotations.ReferenceCardinality;
Yi Tseng51301292017-07-28 13:02:59 -070030import org.apache.felix.scr.annotations.Service;
31import org.onlab.packet.BasePacket;
Kalhee Kim45fede42017-09-05 19:05:06 +000032import org.onlab.packet.DHCP6;
33import org.onlab.packet.IPv6;
34import org.onlab.packet.Ethernet;
35import org.onlab.packet.Ip6Address;
Yi Tseng51301292017-07-28 13:02:59 -070036import org.onlab.packet.IpAddress;
Kalhee Kim45fede42017-09-05 19:05:06 +000037import org.onlab.packet.IpPrefix;
Yi Tseng51301292017-07-28 13:02:59 -070038import org.onlab.packet.MacAddress;
Yi Tseng525ff402017-10-23 19:39:39 -070039import org.onlab.packet.TpPort;
Kalhee Kim45fede42017-09-05 19:05:06 +000040import org.onlab.packet.UDP;
Yi Tseng51301292017-07-28 13:02:59 -070041import org.onlab.packet.VlanId;
Kalhee Kim45fede42017-09-05 19:05:06 +000042import org.onlab.packet.dhcp.Dhcp6RelayOption;
43import org.onlab.packet.dhcp.Dhcp6InterfaceIdOption;
Kalhee Kim45fede42017-09-05 19:05:06 +000044import org.onlab.packet.dhcp.Dhcp6IaNaOption;
45import org.onlab.packet.dhcp.Dhcp6IaTaOption;
46import org.onlab.packet.dhcp.Dhcp6IaPdOption;
47import org.onlab.packet.dhcp.Dhcp6IaAddressOption;
48import org.onlab.packet.dhcp.Dhcp6IaPrefixOption;
Kalhee Kimea4b6c22017-11-09 14:38:37 +000049import org.onlab.packet.dhcp.Dhcp6ClientIdOption;
50import org.onlab.packet.dhcp.Dhcp6Duid;
Kalhee Kim495c9b22017-11-07 16:32:09 +000051import org.onlab.packet.DHCP6.MsgType;
Kalhee Kim45fede42017-09-05 19:05:06 +000052import org.onlab.util.HexString;
Yi Tseng525ff402017-10-23 19:39:39 -070053import org.onosproject.core.ApplicationId;
54import org.onosproject.core.CoreService;
Yi Tseng51301292017-07-28 13:02:59 -070055import org.onosproject.dhcprelay.api.DhcpHandler;
Yi Tseng919b2df2017-09-07 16:22:51 -070056import org.onosproject.dhcprelay.api.DhcpServerInfo;
Yi Tseng525ff402017-10-23 19:39:39 -070057import org.onosproject.dhcprelay.config.IgnoreDhcpConfig;
Kalhee Kim45fede42017-09-05 19:05:06 +000058import org.onosproject.dhcprelay.store.DhcpRelayStore;
Kalhee Kimea4b6c22017-11-09 14:38:37 +000059import org.onosproject.dhcprelay.store.DhcpRecord;
Kalhee Kimba366062017-11-07 16:32:09 +000060import org.onosproject.dhcprelay.store.DhcpFpmPrefixStore;
Yi Tseng525ff402017-10-23 19:39:39 -070061import org.onosproject.net.Device;
62import org.onosproject.net.DeviceId;
Kalhee Kimba366062017-11-07 16:32:09 +000063import org.onosproject.routing.fpm.api.FpmRecord;
Yi Tseng525ff402017-10-23 19:39:39 -070064import org.onosproject.net.behaviour.Pipeliner;
65import org.onosproject.net.device.DeviceService;
66import org.onosproject.net.flow.DefaultTrafficSelector;
67import org.onosproject.net.flow.TrafficSelector;
68import org.onosproject.net.flowobjective.DefaultForwardingObjective;
69import org.onosproject.net.flowobjective.FlowObjectiveService;
70import org.onosproject.net.flowobjective.ForwardingObjective;
71import org.onosproject.net.flowobjective.Objective;
72import org.onosproject.net.flowobjective.ObjectiveContext;
73import org.onosproject.net.flowobjective.ObjectiveError;
Yi Tsengaa417a62017-09-08 17:22:51 -070074import org.onosproject.net.host.HostProvider;
75import org.onosproject.net.host.HostProviderRegistry;
76import org.onosproject.net.host.HostProviderService;
Kalhee Kim45fede42017-09-05 19:05:06 +000077import org.onosproject.net.host.HostService;
78import org.onosproject.net.host.DefaultHostDescription;
79import org.onosproject.net.host.HostDescription;
80import org.onosproject.net.host.InterfaceIpAddress;
81import org.onosproject.net.host.HostListener;
82import org.onosproject.net.host.HostEvent;
83import org.onosproject.net.intf.Interface;
84import org.onosproject.net.intf.InterfaceService;
Yi Tseng525ff402017-10-23 19:39:39 -070085import org.onosproject.net.packet.PacketPriority;
Yi Tsengaa417a62017-09-08 17:22:51 -070086import org.onosproject.net.provider.ProviderId;
Kalhee Kim45fede42017-09-05 19:05:06 +000087import org.onosproject.routeservice.Route;
88import org.onosproject.routeservice.RouteStore;
Yi Tseng483ac6f2017-08-02 15:03:31 -070089import org.onosproject.dhcprelay.config.DhcpServerConfig;
Yi Tseng51301292017-07-28 13:02:59 -070090import org.onosproject.net.ConnectPoint;
Kalhee Kim45fede42017-09-05 19:05:06 +000091import org.onosproject.net.Host;
92import org.onosproject.net.HostId;
93import org.onosproject.net.HostLocation;
94import org.onosproject.net.packet.DefaultOutboundPacket;
95import org.onosproject.net.packet.OutboundPacket;
Yi Tseng51301292017-07-28 13:02:59 -070096import org.onosproject.net.packet.PacketContext;
Kalhee Kim45fede42017-09-05 19:05:06 +000097import org.onosproject.net.packet.PacketService;
98import org.slf4j.Logger;
99import org.slf4j.LoggerFactory;
100import org.onosproject.net.flow.DefaultTrafficTreatment;
101import org.onosproject.net.flow.TrafficTreatment;
Kalhee Kim495c9b22017-11-07 16:32:09 +0000102import org.onosproject.dhcprelay.Dhcp6HandlerUtil.InternalPacket;
Kalhee Kim45fede42017-09-05 19:05:06 +0000103
104import java.nio.ByteBuffer;
105import java.util.List;
Yi Tseng483ac6f2017-08-02 15:03:31 -0700106import java.util.Collection;
Yi Tseng51301292017-07-28 13:02:59 -0700107import java.util.Optional;
Kalhee Kim45fede42017-09-05 19:05:06 +0000108import java.util.Set;
109import java.util.ArrayList;
Kalhee Kim495c9b22017-11-07 16:32:09 +0000110
Yi Tseng525ff402017-10-23 19:39:39 -0700111import java.util.concurrent.atomic.AtomicInteger;
Kalhee Kim45fede42017-09-05 19:05:06 +0000112import static com.google.common.base.Preconditions.checkNotNull;
113import static com.google.common.base.Preconditions.checkState;
Yi Tseng525ff402017-10-23 19:39:39 -0700114import static org.onosproject.net.flowobjective.Objective.Operation.ADD;
115import static org.onosproject.net.flowobjective.Objective.Operation.REMOVE;
Yi Tseng51301292017-07-28 13:02:59 -0700116
117@Component
118@Service
119@Property(name = "version", value = "6")
Yi Tsengaa417a62017-09-08 17:22:51 -0700120public class Dhcp6HandlerImpl implements DhcpHandler, HostProvider {
Charles Chand988c282017-09-12 17:09:32 -0700121 public static final String DHCP_V6_RELAY_APP = "org.onosproject.Dhcp6HandlerImpl";
Saurav Das7c6dec12017-09-13 14:35:56 -0700122 public static final ProviderId PROVIDER_ID = new ProviderId("dhcp6", DHCP_V6_RELAY_APP);
Yi Tseng525ff402017-10-23 19:39:39 -0700123 private static final int IGNORE_CONTROL_PRIORITY = PacketPriority.CONTROL.priorityValue() + 1000;
124
125 private static final TrafficSelector CLIENT_SERVER_SELECTOR = DefaultTrafficSelector.builder()
126 .matchEthType(Ethernet.TYPE_IPV6)
127 .matchIPProtocol(IPv6.PROTOCOL_UDP)
128 .matchIPv6Src(IpPrefix.IPV6_LINK_LOCAL_PREFIX)
129 .matchIPv6Dst(Ip6Address.ALL_DHCP_RELAY_AGENTS_AND_SERVERS.toIpPrefix())
130 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_V6_CLIENT_PORT))
131 .matchUdpDst(TpPort.tpPort(UDP.DHCP_V6_SERVER_PORT))
132 .build();
133 private static final TrafficSelector SERVER_RELAY_SELECTOR = DefaultTrafficSelector.builder()
134 .matchEthType(Ethernet.TYPE_IPV6)
135 .matchIPProtocol(IPv6.PROTOCOL_UDP)
136 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_V6_SERVER_PORT))
137 .matchUdpDst(TpPort.tpPort(UDP.DHCP_V6_SERVER_PORT))
138 .build();
139 static final Set<TrafficSelector> DHCP_SELECTORS = ImmutableSet.of(
140 CLIENT_SERVER_SELECTOR,
141 SERVER_RELAY_SELECTOR
142 );
Kalhee Kim45fede42017-09-05 19:05:06 +0000143 private static Logger log = LoggerFactory.getLogger(Dhcp6HandlerImpl.class);
Yi Tseng51301292017-07-28 13:02:59 -0700144
Kalhee Kim45fede42017-09-05 19:05:06 +0000145 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
146 protected DhcpRelayStore dhcpRelayStore;
Yi Tseng51301292017-07-28 13:02:59 -0700147
Kalhee Kim45fede42017-09-05 19:05:06 +0000148 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
149 protected PacketService packetService;
150
151 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Kalhee Kim45fede42017-09-05 19:05:06 +0000152 protected RouteStore routeStore;
153
154 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
155 protected InterfaceService interfaceService;
156
157 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
158 protected HostService hostService;
159
Yi Tsengaa417a62017-09-08 17:22:51 -0700160 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
161 protected HostProviderRegistry providerRegistry;
Kalhee Kim45fede42017-09-05 19:05:06 +0000162
Yi Tseng525ff402017-10-23 19:39:39 -0700163 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
164 protected CoreService coreService;
165
166 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Kalhee Kimba366062017-11-07 16:32:09 +0000167 protected DhcpFpmPrefixStore dhcpFpmPrefixStore;
168
169 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Yi Tseng525ff402017-10-23 19:39:39 -0700170 protected DeviceService deviceService;
171
172 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
173 protected FlowObjectiveService flowObjectiveService;
174
Yi Tsengaa417a62017-09-08 17:22:51 -0700175 protected HostProviderService providerService;
Yi Tseng525ff402017-10-23 19:39:39 -0700176 protected ApplicationId appId;
177 protected Multimap<DeviceId, VlanId> ignoredVlans = HashMultimap.create();
178 private InternalHostListener hostListener = new InternalHostListener();
179
Kalhee Kimba366062017-11-07 16:32:09 +0000180 private Boolean dhcpFpmEnabled = false;
181
Kalhee Kim495c9b22017-11-07 16:32:09 +0000182 private Dhcp6HandlerUtil dhcp6HandlerUtil = new Dhcp6HandlerUtil();
183
Yi Tseng919b2df2017-09-07 16:22:51 -0700184 private List<DhcpServerInfo> defaultServerInfoList = Lists.newArrayList();
185 private List<DhcpServerInfo> indirectServerInfoList = Lists.newArrayList();
Kalhee Kim495c9b22017-11-07 16:32:09 +0000186 private class IpAddressInfo {
187 Ip6Address ip6Address;
188 long prefTime;
189 }
190 private class PdPrefixInfo {
191 IpPrefix pdPrefix;
192 long prefTime;
193 }
194 protected int dhcp6PollInterval = 24 * 3600; // 24 hr period
195
Yi Tseng919b2df2017-09-07 16:22:51 -0700196
Kalhee Kim45fede42017-09-05 19:05:06 +0000197
198 // CLIENT message types
199 public static final Set<Byte> MSG_TYPE_FROM_CLIENT =
200 ImmutableSet.of(DHCP6.MsgType.SOLICIT.value(),
201 DHCP6.MsgType.REQUEST.value(),
202 DHCP6.MsgType.REBIND.value(),
203 DHCP6.MsgType.RENEW.value(),
204 DHCP6.MsgType.RELEASE.value(),
205 DHCP6.MsgType.DECLINE.value(),
206 DHCP6.MsgType.CONFIRM.value(),
207 DHCP6.MsgType.RELAY_FORW.value());
208 // SERVER message types
209 public static final Set<Byte> MSG_TYPE_FROM_SERVER =
210 ImmutableSet.of(DHCP6.MsgType.RELAY_REPL.value());
211
212 @Activate
213 protected void activate() {
Yi Tseng525ff402017-10-23 19:39:39 -0700214 appId = coreService.registerApplication(DHCP_V6_RELAY_APP);
Yi Tsengaa417a62017-09-08 17:22:51 -0700215 providerService = providerRegistry.register(this);
Kalhee Kim45fede42017-09-05 19:05:06 +0000216 hostService.addListener(hostListener);
Yi Tseng51301292017-07-28 13:02:59 -0700217 }
218
Kalhee Kim45fede42017-09-05 19:05:06 +0000219 @Deactivate
220 protected void deactivate() {
Yi Tsengaa417a62017-09-08 17:22:51 -0700221 providerRegistry.unregister(this);
Kalhee Kim45fede42017-09-05 19:05:06 +0000222 hostService.removeListener(hostListener);
Yi Tseng919b2df2017-09-07 16:22:51 -0700223 defaultServerInfoList.forEach(this::stopMonitoringIps);
224 defaultServerInfoList.clear();
225 indirectServerInfoList.forEach(this::stopMonitoringIps);
226 indirectServerInfoList.clear();
227 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000228
Yi Tseng919b2df2017-09-07 16:22:51 -0700229 private void stopMonitoringIps(DhcpServerInfo serverInfo) {
230 serverInfo.getDhcpGatewayIp6().ifPresent(gatewayIp -> {
231 hostService.stopMonitoringIp(gatewayIp);
232 });
233 serverInfo.getDhcpServerIp6().ifPresent(serverIp -> {
234 hostService.stopMonitoringIp(serverIp);
235 });
Yi Tseng51301292017-07-28 13:02:59 -0700236 }
237
Yi Tseng51301292017-07-28 13:02:59 -0700238 @Override
Yi Tseng919b2df2017-09-07 16:22:51 -0700239 public List<DhcpServerInfo> getDefaultDhcpServerInfoList() {
240 return defaultServerInfoList;
Yi Tseng51301292017-07-28 13:02:59 -0700241 }
242
243 @Override
Yi Tseng919b2df2017-09-07 16:22:51 -0700244 public List<DhcpServerInfo> getIndirectDhcpServerInfoList() {
245 return indirectServerInfoList;
Yi Tseng51301292017-07-28 13:02:59 -0700246 }
247
248 @Override
Yi Tseng525ff402017-10-23 19:39:39 -0700249 public void updateIgnoreVlanConfig(IgnoreDhcpConfig config) {
250 if (config == null) {
251 ignoredVlans.forEach(((deviceId, vlanId) -> {
252 processIgnoreVlanRule(deviceId, vlanId, REMOVE);
253 }));
254 return;
255 }
256 config.ignoredVlans().forEach((deviceId, vlanId) -> {
257 if (ignoredVlans.get(deviceId).contains(vlanId)) {
258 // don't need to process if it already ignored
259 return;
260 }
261 processIgnoreVlanRule(deviceId, vlanId, ADD);
262 });
263
264 ignoredVlans.forEach((deviceId, vlanId) -> {
265 if (!config.ignoredVlans().get(deviceId).contains(vlanId)) {
266 // not contains in new config, remove it
267 processIgnoreVlanRule(deviceId, vlanId, REMOVE);
268 }
269 });
270 }
271
272 @Override
Saurav Dasb805f1a2017-12-13 16:19:35 -0800273 public void removeIgnoreVlanState(IgnoreDhcpConfig config) {
274 if (config == null) {
275 ignoredVlans.clear();
276 return;
277 }
278 config.ignoredVlans().forEach((deviceId, vlanId) -> {
279 ignoredVlans.remove(deviceId, vlanId);
280 });
281 }
282
283 @Override
Kalhee Kim45fede42017-09-05 19:05:06 +0000284 public void processDhcpPacket(PacketContext context, BasePacket payload) {
285 checkNotNull(payload, "DHCP6 payload can't be null");
286 checkState(payload instanceof DHCP6, "Payload is not a DHCP6");
287 DHCP6 dhcp6Payload = (DHCP6) payload;
288 Ethernet receivedPacket = context.inPacket().parsed();
289
290 if (!configured()) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000291 log.warn("Missing DHCP6 relay server config. Abort packet processing dhcp6 payload {}", dhcp6Payload);
Kalhee Kim45fede42017-09-05 19:05:06 +0000292 return;
293 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000294 byte msgTypeVal = dhcp6Payload.getMsgType();
295 MsgType msgType = DHCP6.MsgType.getType(msgTypeVal);
296 log.debug("msgType is {}", msgType);
Kalhee Kim45fede42017-09-05 19:05:06 +0000297
Kalhee Kim45fede42017-09-05 19:05:06 +0000298
299 ConnectPoint inPort = context.inPacket().receivedFrom();
Jonathan Hart8ca2bc02017-11-30 18:23:42 -0800300
Kalhee Kim495c9b22017-11-07 16:32:09 +0000301 if (inPort == null) {
302 log.warn("incomming ConnectPoint is null");
303 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000304 Set<Interface> receivingInterfaces = interfaceService.getInterfacesByPort(inPort);
305 //ignore the packets if dhcp client interface is not configured on onos.
306 if (receivingInterfaces.isEmpty()) {
307 log.warn("Virtual interface is not configured on {}", inPort);
308 return;
309 }
310
Kalhee Kim495c9b22017-11-07 16:32:09 +0000311 if (MSG_TYPE_FROM_CLIENT.contains(msgTypeVal)) {
Kalhee Kim45fede42017-09-05 19:05:06 +0000312
Kalhee Kim495c9b22017-11-07 16:32:09 +0000313 List<InternalPacket> ethernetClientPacket =
Kalhee Kim45fede42017-09-05 19:05:06 +0000314 processDhcp6PacketFromClient(context, receivedPacket, receivingInterfaces);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000315 for (InternalPacket internalPacket : ethernetClientPacket) {
316 forwardPacket(internalPacket);
Kalhee Kim45fede42017-09-05 19:05:06 +0000317 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000318 } else if (MSG_TYPE_FROM_SERVER.contains(msgTypeVal)) {
319 log.debug("calling processDhcp6PacketFromServer with RELAY_REPL {}", msgTypeVal);
Kalhee Kim45fede42017-09-05 19:05:06 +0000320 InternalPacket ethernetPacketReply =
321 processDhcp6PacketFromServer(context, receivedPacket, receivingInterfaces);
322 if (ethernetPacketReply != null) {
323 forwardPacket(ethernetPacketReply);
324 }
325 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000326 log.warn("Not so fast, packet type {} not supported yet", msgTypeVal);
Kalhee Kim45fede42017-09-05 19:05:06 +0000327 }
328 }
329
Kalhee Kim45fede42017-09-05 19:05:06 +0000330 /**
331 * Checks if this app has been configured.
332 *
333 * @return true if all information we need have been initialized
334 */
335 public boolean configured() {
Yi Tseng919b2df2017-09-07 16:22:51 -0700336 return !defaultServerInfoList.isEmpty();
Kalhee Kim45fede42017-09-05 19:05:06 +0000337 }
338
Yi Tsengaa417a62017-09-08 17:22:51 -0700339 @Override
340 public ProviderId id() {
Charles Chand988c282017-09-12 17:09:32 -0700341 return PROVIDER_ID;
Yi Tsengaa417a62017-09-08 17:22:51 -0700342 }
343
344 @Override
345 public void triggerProbe(Host host) {
346 // Do nothing here
347 }
348
Kalhee Kim495c9b22017-11-07 16:32:09 +0000349
Kalhee Kim45fede42017-09-05 19:05:06 +0000350
351 //forward the packet to ConnectPoint where the DHCP server is attached.
352 private void forwardPacket(InternalPacket packet) {
353 //send Packetout to dhcp server connectpoint.
354 if (packet.destLocation != null) {
355 TrafficTreatment t = DefaultTrafficTreatment.builder()
356 .setOutput(packet.destLocation.port()).build();
357 OutboundPacket o = new DefaultOutboundPacket(
358 packet.destLocation.deviceId(), t, ByteBuffer.wrap(packet.packet.serialize()));
359 if (log.isTraceEnabled()) {
360 log.trace("Relaying packet to destination {}", packet.destLocation);
361 }
362 packetService.emit(o);
363 } // if
364 }
365
Kalhee Kim45fede42017-09-05 19:05:06 +0000366
367
Kalhee Kim45fede42017-09-05 19:05:06 +0000368
369 /**
370 * extract from dhcp6 packet client ipv6 address of given by dhcp server.
371 *
372 * @param dhcp6 the dhcp6 packet
Kalhee Kim495c9b22017-11-07 16:32:09 +0000373 * @return IpAddressInfo IpAddressInfo given by dhcp server, or null if not exists
Kalhee Kim45fede42017-09-05 19:05:06 +0000374 */
Kalhee Kim495c9b22017-11-07 16:32:09 +0000375 private IpAddressInfo extractIpAddress(DHCP6 dhcp6) {
376 IpAddressInfo ipInfo = new IpAddressInfo();
Kalhee Kim45fede42017-09-05 19:05:06 +0000377
378 log.debug("extractIpAddress enters dhcp6 {}.", dhcp6);
379 // Extract IPv6 address from IA NA ot IA TA option
380 Optional<Dhcp6IaNaOption> iaNaOption = dhcp6.getOptions()
381 .stream()
382 .filter(opt -> opt instanceof Dhcp6IaNaOption)
383 .map(opt -> (Dhcp6IaNaOption) opt)
384 .findFirst();
385 Optional<Dhcp6IaTaOption> iaTaOption = dhcp6.getOptions()
386 .stream()
387 .filter(opt -> opt instanceof Dhcp6IaTaOption)
388 .map(opt -> (Dhcp6IaTaOption) opt)
389 .findFirst();
390 Optional<Dhcp6IaAddressOption> iaAddressOption;
391 if (iaNaOption.isPresent()) {
392 log.debug("Found IPv6 address from iaNaOption {}", iaNaOption);
393
394 iaAddressOption = iaNaOption.get().getOptions().stream()
395 .filter(opt -> opt instanceof Dhcp6IaAddressOption)
396 .map(opt -> (Dhcp6IaAddressOption) opt)
397 .findFirst();
398 } else if (iaTaOption.isPresent()) {
399 log.debug("Found IPv6 address from iaTaOption {}", iaTaOption);
400
401 iaAddressOption = iaTaOption.get().getOptions().stream()
402 .filter(opt -> opt instanceof Dhcp6IaAddressOption)
403 .map(opt -> (Dhcp6IaAddressOption) opt)
404 .findFirst();
405 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000406 log.info("No IPv6 address found from iaTaOption {}", iaTaOption);
Kalhee Kim45fede42017-09-05 19:05:06 +0000407 iaAddressOption = Optional.empty();
408 }
409 if (iaAddressOption.isPresent()) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000410 ipInfo.ip6Address = iaAddressOption.get().getIp6Address();
411 ipInfo.prefTime = iaAddressOption.get().getPreferredLifetime();
Kalhee Kim45fede42017-09-05 19:05:06 +0000412 log.debug("Found IPv6 address from iaAddressOption {}", iaAddressOption);
Kalhee Kim45fede42017-09-05 19:05:06 +0000413 } else {
414 log.debug("Can't find IPv6 address from DHCPv6 {}", dhcp6);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000415 return null;
Kalhee Kim45fede42017-09-05 19:05:06 +0000416 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000417 return ipInfo;
Kalhee Kim45fede42017-09-05 19:05:06 +0000418 }
419 /**
420 * extract from dhcp6 packet Prefix prefix provided by dhcp server.
421 *
422 * @param dhcp6 the dhcp6 payload
423 * @return IpPrefix Prefix Delegation prefix, or null if not exists.
424 */
Kalhee Kim495c9b22017-11-07 16:32:09 +0000425 private PdPrefixInfo extractPrefix(DHCP6 dhcp6) {
426 log.debug("extractPrefix enters {}", dhcp6);
Kalhee Kim45fede42017-09-05 19:05:06 +0000427
428 // extract prefix
Kalhee Kim495c9b22017-11-07 16:32:09 +0000429 PdPrefixInfo pdPrefixInfo = new PdPrefixInfo();
Kalhee Kim45fede42017-09-05 19:05:06 +0000430
431 Ip6Address prefixAddress = null;
432
433 // Extract IPv6 prefix from IA PD option
434 Optional<Dhcp6IaPdOption> iaPdOption = dhcp6.getOptions()
435 .stream()
436 .filter(opt -> opt instanceof Dhcp6IaPdOption)
437 .map(opt -> (Dhcp6IaPdOption) opt)
438 .findFirst();
439
440 Optional<Dhcp6IaPrefixOption> iaPrefixOption;
441 if (iaPdOption.isPresent()) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000442 log.debug("IA_PD option found {}", iaPdOption);
Kalhee Kim45fede42017-09-05 19:05:06 +0000443
444 iaPrefixOption = iaPdOption.get().getOptions().stream()
445 .filter(opt -> opt instanceof Dhcp6IaPrefixOption)
446 .map(opt -> (Dhcp6IaPrefixOption) opt)
447 .findFirst();
448 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000449 log.debug("IA_PD option NOT found");
Kalhee Kim45fede42017-09-05 19:05:06 +0000450
451 iaPrefixOption = Optional.empty();
452 }
453 if (iaPrefixOption.isPresent()) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000454 log.debug("IAPrefix Option within IA_PD option found {}", iaPrefixOption);
Kalhee Kim45fede42017-09-05 19:05:06 +0000455
456 prefixAddress = iaPrefixOption.get().getIp6Prefix();
457 int prefixLen = (int) iaPrefixOption.get().getPrefixLength();
Kalhee Kim495c9b22017-11-07 16:32:09 +0000458 log.debug("Prefix length is {} bits", prefixLen);
459 pdPrefixInfo.pdPrefix = IpPrefix.valueOf(prefixAddress, prefixLen);
460 pdPrefixInfo.prefTime = iaPrefixOption.get().getPreferredLifetime();
Kalhee Kim45fede42017-09-05 19:05:06 +0000461 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000462 log.debug("Can't find IPv6 prefix from DHCPv6 {}", dhcp6);
463 return null;
Kalhee Kim45fede42017-09-05 19:05:06 +0000464 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000465 return pdPrefixInfo;
Kalhee Kim45fede42017-09-05 19:05:06 +0000466 }
467
468 /**
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000469 * extract from dhcp6 packet ClientIdOption.
470 *
471 * @param directConnFlag directly connected host
472 * @param dhcp6Payload the dhcp6 payload
473 * @return Dhcp6ClientIdOption clientIdOption, or null if not exists.
474 */
475 private Dhcp6ClientIdOption extractClinedId(Boolean directConnFlag, DHCP6 dhcp6Payload) {
476 Dhcp6ClientIdOption clientIdOption;
477
478 if (directConnFlag) {
479 clientIdOption = dhcp6Payload.getOptions()
480 .stream()
481 .filter(opt -> opt instanceof Dhcp6ClientIdOption)
482 .map(opt -> (Dhcp6ClientIdOption) opt)
483 .findFirst()
484 .orElse(null);
485 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000486 DHCP6 leafDhcp = dhcp6HandlerUtil.getDhcp6Leaf(dhcp6Payload);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000487 clientIdOption = leafDhcp.getOptions()
488 .stream()
489 .filter(opt -> opt instanceof Dhcp6ClientIdOption)
490 .map(opt -> (Dhcp6ClientIdOption) opt)
491 .findFirst()
492 .orElse(null);
493 }
494
495 return clientIdOption;
496 }
497 /**
Kalhee Kim495c9b22017-11-07 16:32:09 +0000498 * remove host or route and update dhcp relay record attributes.
Kalhee Kim45fede42017-09-05 19:05:06 +0000499 *
500 * @param directConnFlag flag to show that packet is from directly connected client
Kalhee Kim495c9b22017-11-07 16:32:09 +0000501 * @param location client side connect point
Kalhee Kim45fede42017-09-05 19:05:06 +0000502 * @param dhcp6Packet the dhcp6 payload
503 * @param clientPacket client's ethernet packet
504 * @param clientIpv6 client's Ipv6 packet
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +0000505 * @param clientInterface client interfaces
Kalhee Kim45fede42017-09-05 19:05:06 +0000506 */
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000507 private void removeHostOrRoute(boolean directConnFlag, ConnectPoint location,
508 DHCP6 dhcp6Packet,
Kalhee Kim45fede42017-09-05 19:05:06 +0000509 Ethernet clientPacket, IPv6 clientIpv6,
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +0000510 Interface clientInterface) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000511 log.debug("removeHostOrRoute enters {}", dhcp6Packet);
Kalhee Kim121ba922017-11-01 17:56:44 +0000512 VlanId vlanId = clientInterface.vlan();
Kalhee Kim495c9b22017-11-07 16:32:09 +0000513 MacAddress srcMac = clientPacket.getSourceMAC(); // could be gw or host
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000514 MacAddress leafClientMac;
515 byte leafMsgType;
Kalhee Kim495c9b22017-11-07 16:32:09 +0000516 log.debug("client mac {} client vlan {}", HexString.toHexString(srcMac.toBytes(), ":"), vlanId);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000517
518 Dhcp6ClientIdOption clientIdOption = extractClinedId(directConnFlag, dhcp6Packet);
519 if (clientIdOption != null) {
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000520 if ((clientIdOption.getDuid().getDuidType() == Dhcp6Duid.DuidType.DUID_LLT) ||
521 (clientIdOption.getDuid().getDuidType() == Dhcp6Duid.DuidType.DUID_LL)) {
522 leafClientMac = MacAddress.valueOf(clientIdOption.getDuid().getLinkLayerAddress());
523 } else {
524 log.warn("Link-Layer Address not supported in CLIENTID option. No DhcpRelay Record created.");
525 return;
526 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000527 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000528 log.warn("CLIENTID option NOT found. Don't create DhcpRelay Record.");
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000529 return;
530 }
531
Kalhee Kim495c9b22017-11-07 16:32:09 +0000532 HostId leafHostId = HostId.hostId(leafClientMac, vlanId);
533 DhcpRecord record = dhcpRelayStore.getDhcpRecord(leafHostId).orElse(null);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000534 if (record == null) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000535 record = new DhcpRecord(leafHostId);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000536 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000537
Kalhee Kim495c9b22017-11-07 16:32:09 +0000538 Boolean isMsgRelease = dhcp6HandlerUtil.isDhcp6Release(dhcp6Packet);
539 IpAddressInfo ipInfo;
540 PdPrefixInfo pdInfo = null;
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000541 if (directConnFlag) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000542 // Add to host store if it is connected to network directly
543 ipInfo = extractIpAddress(dhcp6Packet);
544 if (ipInfo != null) {
545 if (isMsgRelease) {
546 HostId hostId = HostId.hostId(srcMac, vlanId);
547 log.debug("remove Host {} ip for directly connected.", hostId.toString());
548 providerService.removeIpFromHost(hostId, ipInfo.ip6Address);
Kalhee Kim45fede42017-09-05 19:05:06 +0000549 }
550 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000551 log.debug("ipAddress not found. Do not remove Host {} for directly connected.",
552 HostId.hostId(srcMac, vlanId).toString());
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000553 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000554 leafMsgType = dhcp6Packet.getMsgType();
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000555 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000556 // Remove from route store if it is not connected to network directly
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000557 // pick out the first link-local ip address
Kalhee Kim495c9b22017-11-07 16:32:09 +0000558 IpAddress nextHopIp = getFirstIpByHost(directConnFlag, srcMac, vlanId);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000559 if (nextHopIp == null) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000560 log.warn("Can't find link-local IP address of gateway mac {} vlanId {}", srcMac, vlanId);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000561 return;
562 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000563
Kalhee Kim495c9b22017-11-07 16:32:09 +0000564 DHCP6 leafDhcp = dhcp6HandlerUtil.getDhcp6Leaf(dhcp6Packet);
565 ipInfo = extractIpAddress(leafDhcp);
566 if (ipInfo == null) {
567 log.debug("ip is null");
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000568 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000569 if (isMsgRelease) {
570 Route routeForIP = new Route(Route.Source.STATIC, ipInfo.ip6Address.toIpPrefix(), nextHopIp);
571 log.debug("removing route of 128 address for indirectly connected.");
572 log.debug("128 ip {}, nexthop {}",
573 HexString.toHexString(ipInfo.ip6Address.toOctets(), ":"),
574 HexString.toHexString(nextHopIp.toOctets(), ":"));
575 routeStore.removeRoute(routeForIP);
Kalhee Kim45fede42017-09-05 19:05:06 +0000576 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000577 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000578
Kalhee Kim495c9b22017-11-07 16:32:09 +0000579 pdInfo = extractPrefix(leafDhcp);
580 if (pdInfo == null) {
581 log.debug("ipPrefix is null ");
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000582 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000583 if (isMsgRelease) {
584 Route routeForPrefix = new Route(Route.Source.STATIC, pdInfo.pdPrefix, nextHopIp);
585 log.debug("removing route of PD for indirectly connected.");
586 log.debug("pd ip {}, nexthop {}",
587 HexString.toHexString(pdInfo.pdPrefix.address().toOctets(), ":"),
588 HexString.toHexString(nextHopIp.toOctets(), ":"));
589
590 routeStore.removeRoute(routeForPrefix);
Kalhee Kimba366062017-11-07 16:32:09 +0000591 if (this.dhcpFpmEnabled) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000592 dhcpFpmPrefixStore.removeFpmRecord(pdInfo.pdPrefix);
Kalhee Kimba366062017-11-07 16:32:09 +0000593 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000594 }
595 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000596 leafMsgType = leafDhcp.getMsgType();
Kalhee Kim45fede42017-09-05 19:05:06 +0000597 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000598
Kalhee Kim495c9b22017-11-07 16:32:09 +0000599 if (isMsgRelease) {
600 log.debug("DHCP6 RELEASE msg.");
601 if (record != null) {
602 if (ipInfo != null) {
603 log.debug("DhcpRelay Record ip6Address is set to null.");
604 record.ip6Address(null);
605 }
606 if (pdInfo != null) {
607 log.debug("DhcpRelay Record pdPrefix is set to null.");
608 }
609
610 if (!record.ip6Address().isPresent() && !record.pdPrefix().isPresent()) {
611 log.warn("IP6 address and IP6 PD both are null. Remove record.");
612 // do not remove a record. Let timer task handler it.
613 //dhcpRelayStore.removeDhcpRecord(HostId.hostId(leafClientMac, vlanId));
614 }
615 }
616 }
617
618 record.addLocation(new HostLocation(location, System.currentTimeMillis()));
619 record.ip6Status(DHCP6.MsgType.getType(leafMsgType));
620 record.setDirectlyConnected(directConnFlag);
621 if (!directConnFlag) {
622 // Update gateway mac address if the host is not directly connected
623 record.nextHop(srcMac);
624 }
625 record.updateLastSeen();
626 dhcpRelayStore.updateDhcpRecord(leafHostId, record);
627 }
628
629 /**
630 * add host or route and update dhcp relay record.
631 *
632 * @param directConnFlag flag to show that packet is from directly connected client
633 * @param location client side connect point
634 * @param dhcp6Relay the dhcp6 payload
635 * @param embeddedDhcp6 the dhcp6 payload within relay
636 * @param srcMac client gw/host macAddress
637 * @param clientInterface client interface
638 */
639 private void addHostOrRoute(boolean directConnFlag, ConnectPoint location, DHCP6 dhcp6Relay,
640 DHCP6 embeddedDhcp6, MacAddress srcMac, Interface clientInterface) {
641 log.debug("addHostOrRoute entered.");
642 VlanId vlanId = clientInterface.vlan();
643 Boolean isMsgReply = dhcp6HandlerUtil.isDhcp6Reply(dhcp6Relay);
644 MacAddress leafClientMac;
645 Byte leafMsgType;
646
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000647 Dhcp6ClientIdOption clientIdOption = extractClinedId(directConnFlag, embeddedDhcp6);
648 if (clientIdOption != null) {
649 log.debug("CLIENTID option found {}", clientIdOption);
650 if ((clientIdOption.getDuid().getDuidType() == Dhcp6Duid.DuidType.DUID_LLT) ||
651 (clientIdOption.getDuid().getDuidType() == Dhcp6Duid.DuidType.DUID_LL)) {
652 leafClientMac = MacAddress.valueOf(clientIdOption.getDuid().getLinkLayerAddress());
653 } else {
654 log.warn("Link-Layer Address not supported in CLIENTID option. No DhcpRelay Record created.");
655 return;
656 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000657 } else {
658 log.warn("CLIENTID option NOT found. No DhcpRelay Record created.");
659 return;
660 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000661 HostId leafHostId = HostId.hostId(leafClientMac, vlanId);
662 DhcpRecord record = dhcpRelayStore.getDhcpRecord(leafHostId).orElse(null);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000663 if (record == null) {
664 record = new DhcpRecord(HostId.hostId(leafClientMac, vlanId));
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000665 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000666
667 IpAddressInfo ipInfo;
668 PdPrefixInfo pdInfo = null;
669 if (directConnFlag) {
670 // Add to host store if it connect to network directly
671 ipInfo = extractIpAddress(embeddedDhcp6);
672 if (ipInfo != null) {
673 if (isMsgReply) {
674 Set<IpAddress> ips = Sets.newHashSet(ipInfo.ip6Address);
675 HostId hostId = HostId.hostId(srcMac, vlanId);
676 Host host = hostService.getHost(hostId);
677 HostLocation hostLocation = new HostLocation(clientInterface.connectPoint(),
678 System.currentTimeMillis());
679 Set<HostLocation> hostLocations = Sets.newHashSet(hostLocation);
680 if (host != null) {
681 // Dual homing support:
682 // if host exists, use old locations and new location
683 hostLocations.addAll(host.locations());
684 }
685 HostDescription desc = new DefaultHostDescription(srcMac, vlanId, hostLocations, ips,
686 false);
687 log.debug("adding Host for directly connected.");
688 log.debug("client mac {} client vlan {} hostlocation {}",
689 HexString.toHexString(srcMac.toBytes(), ":"), vlanId, hostLocation.toString());
690 // Replace the ip when dhcp server give the host new ip address
691 providerService.hostDetected(hostId, desc, false);
692 }
693 } else {
694 log.warn("ipAddress not found. Do not add Host {} for directly connected.",
695 HostId.hostId(srcMac, vlanId).toString());
696 }
697 leafMsgType = embeddedDhcp6.getMsgType();
698 } else {
699 // Add to route store if it does not connect to network directly
700 // pick out the first link-local ip address
701 IpAddress nextHopIp = getFirstIpByHost(directConnFlag, srcMac, vlanId);
702 if (nextHopIp == null) {
703 log.warn("Can't find link-local IP address of gateway mac {} vlanId {}", srcMac, vlanId);
704 return;
705 }
706
707 DHCP6 leafDhcp = dhcp6HandlerUtil.getDhcp6Leaf(embeddedDhcp6);
708 ipInfo = extractIpAddress(leafDhcp);
709 if (ipInfo == null) {
710 log.debug("ip is null");
711 } else {
712 if (isMsgReply) {
713 Route routeForIP = new Route(Route.Source.STATIC, ipInfo.ip6Address.toIpPrefix(), nextHopIp);
714 log.debug("adding Route of 128 address for indirectly connected.");
715 routeStore.updateRoute(routeForIP);
716 }
717 }
718
719 pdInfo = extractPrefix(leafDhcp);
720 if (pdInfo == null) {
721 log.debug("ipPrefix is null ");
722 } else {
723 if (isMsgReply) {
724 Route routeForPrefix = new Route(Route.Source.STATIC, pdInfo.pdPrefix, nextHopIp);
725 log.debug("adding Route of PD for indirectly connected.");
726 routeStore.updateRoute(routeForPrefix);
727 if (this.dhcpFpmEnabled) {
728 FpmRecord fpmRecord = new FpmRecord(pdInfo.pdPrefix, nextHopIp, FpmRecord.Type.DHCP_RELAY);
729 dhcpFpmPrefixStore.addFpmRecord(pdInfo.pdPrefix, fpmRecord);
730 }
731 }
732 }
733 leafMsgType = leafDhcp.getMsgType();
734 }
735 if (leafMsgType == DHCP6.MsgType.RELEASE.value() ||
736 (leafMsgType == DHCP6.MsgType.REPLY.value()) && ipInfo == null) {
737 log.warn("DHCP6 RELEASE/REPLY(null ip) from Server. MsgType {}", leafMsgType);
738 //return;
739 }
740
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000741 record.addLocation(new HostLocation(location, System.currentTimeMillis()));
742 if (leafMsgType == DHCP6.MsgType.REPLY.value()) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000743 if (ipInfo != null) {
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000744 log.debug("IP6 address is being stored into dhcp-relay store.");
Kalhee Kim495c9b22017-11-07 16:32:09 +0000745 log.debug("IP6 address {}", HexString.toHexString(ipInfo.ip6Address.toOctets(), ":"));
746 record.ip6Address(ipInfo.ip6Address);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000747 } else {
748 log.debug("IP6 address is not returned from server. Maybe only PD is returned.");
749 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000750 if (pdInfo != null) {
751 log.debug("IP6 PD address {}",
752 HexString.toHexString(pdInfo.pdPrefix.address().toOctets(), ":"));
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000753 } else {
754 log.debug("IP6 PD address is not returned from server. Maybe only IPAddress is returned.");
755 }
756 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000757 record.ip6Status(DHCP6.MsgType.getType(leafMsgType));
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000758 record.setDirectlyConnected(directConnFlag);
759 record.updateLastSeen();
Kalhee Kim495c9b22017-11-07 16:32:09 +0000760 dhcpRelayStore.updateDhcpRecord(leafHostId, record);
Kalhee Kim45fede42017-09-05 19:05:06 +0000761 }
762
763 /**
Kalhee Kim495c9b22017-11-07 16:32:09 +0000764 * build the DHCP6 solicit/request packet with gatewayip.
Kalhee Kim45fede42017-09-05 19:05:06 +0000765 *
766 * @param context packet context
767 * @param clientPacket client ethernet packet
768 * @param clientInterfaces set of client side interfaces
769 */
Kalhee Kim495c9b22017-11-07 16:32:09 +0000770 private List<InternalPacket> processDhcp6PacketFromClient(PacketContext context,
771 Ethernet clientPacket,
772 Set<Interface> clientInterfaces) {
Yi Tseng3bd57ac2017-11-29 14:39:18 -0800773 ConnectPoint receivedFrom = context.inPacket().receivedFrom();
774 DeviceId receivedFromDevice = receivedFrom.deviceId();
Kalhee Kim495c9b22017-11-07 16:32:09 +0000775 Ip6Address relayAgentIp = dhcp6HandlerUtil.getRelayAgentIPv6Address(clientInterfaces);
Yi Tseng3bd57ac2017-11-29 14:39:18 -0800776 MacAddress relayAgentMac = clientInterfaces.iterator().next().mac();
777 if (relayAgentIp == null || relayAgentMac == null) {
778 log.warn("Missing DHCP relay agent interface Ipv6 addr config for "
Kalhee Kim495c9b22017-11-07 16:32:09 +0000779 + "packet from client on port: {}. Aborting packet processing",
780 clientInterfaces.iterator().next().connectPoint());
Kalhee Kim121ba922017-11-01 17:56:44 +0000781 return null;
782 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000783
Kalhee Kim495c9b22017-11-07 16:32:09 +0000784 IPv6 clientIpv6 = (IPv6) clientPacket.getPayload();
785 UDP clientUdp = (UDP) clientIpv6.getPayload();
786 DHCP6 clientDhcp6 = (DHCP6) clientUdp.getPayload();
787
788 boolean directConnFlag = dhcp6HandlerUtil.directlyConnected(clientDhcp6);
Charles Chana990ce92017-10-30 10:22:50 -0700789
Kalhee Kim121ba922017-11-01 17:56:44 +0000790 ConnectPoint clientConnectionPoint = context.inPacket().receivedFrom();
791 VlanId vlanIdInUse = VlanId.vlanId(clientPacket.getVlanID());
792 Interface clientInterface = interfaceService.getInterfacesByPort(clientConnectionPoint)
Kalhee Kim495c9b22017-11-07 16:32:09 +0000793 .stream().filter(iface -> dhcp6HandlerUtil.interfaceContainsVlan(iface, vlanIdInUse))
794 .findFirst()
795 .orElse(null);
Kalhee Kim45fede42017-09-05 19:05:06 +0000796
Kalhee Kim495c9b22017-11-07 16:32:09 +0000797 List<InternalPacket> internalPackets = new ArrayList<>();
798 List<DhcpServerInfo> serverInfoList = findValidServerInfo(directConnFlag);
799 List<DhcpServerInfo> copyServerInfoList = new ArrayList<DhcpServerInfo>(serverInfoList);
Kalhee Kim45b24182017-10-18 18:30:23 +0000800
Kalhee Kim495c9b22017-11-07 16:32:09 +0000801 for (DhcpServerInfo serverInfo : copyServerInfoList) {
802 if (!dhcp6HandlerUtil.checkDhcpServerConnPt(directConnFlag, serverInfo)) {
803 log.warn("Can't get server connect point, ignore");
804 continue;
805 }
806 DhcpServerInfo newServerInfo = getHostInfoForServerInfo(serverInfo, serverInfoList);
807 if (newServerInfo == null) {
808 log.warn("Can't get server interface with host info resolved, ignore");
809 continue;
810 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000811
Kalhee Kim495c9b22017-11-07 16:32:09 +0000812 Interface serverInterface = getServerInterface(newServerInfo);
813 if (serverInterface == null) {
814 log.warn("Can't get server interface, ignore");
815 continue;
816 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000817
Kalhee Kim495c9b22017-11-07 16:32:09 +0000818 Ethernet etherReply = dhcp6HandlerUtil.buildDhcp6PacketFromClient(context, clientPacket,
819 clientInterfaces, newServerInfo, serverInterface);
820 removeHostOrRoute(directConnFlag, clientConnectionPoint, clientDhcp6, clientPacket,
821 clientIpv6, clientInterface);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000822
Kalhee Kim495c9b22017-11-07 16:32:09 +0000823 InternalPacket internalPacket = new Dhcp6HandlerUtil().new InternalPacket(etherReply,
824 serverInfo.getDhcpServerConnectPoint().get());
825 internalPackets.add(internalPacket);
826 }
827 log.debug("num of client packets to send is{}", internalPackets.size());
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000828
Kalhee Kim495c9b22017-11-07 16:32:09 +0000829 return internalPackets;
830 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000831
832 /**
Kalhee Kim45fede42017-09-05 19:05:06 +0000833 * process the DHCP6 relay-reply packet from dhcp server.
834 *
835 * @param context packet context
836 * @param receivedPacket server ethernet packet
837 * @param recevingInterfaces set of server side interfaces
Kalhee Kim495c9b22017-11-07 16:32:09 +0000838 * @return internalPacket toward client
Kalhee Kim45fede42017-09-05 19:05:06 +0000839 */
840 private InternalPacket processDhcp6PacketFromServer(PacketContext context,
841 Ethernet receivedPacket, Set<Interface> recevingInterfaces) {
Kalhee Kim45fede42017-09-05 19:05:06 +0000842 // get dhcp6 header.
Ray Milkeyf0c47612017-09-28 11:29:38 -0700843 Ethernet etherReply = receivedPacket.duplicate();
Kalhee Kim45fede42017-09-05 19:05:06 +0000844 IPv6 ipv6Packet = (IPv6) etherReply.getPayload();
845 UDP udpPacket = (UDP) ipv6Packet.getPayload();
846 DHCP6 dhcp6Relay = (DHCP6) udpPacket.getPayload();
Kalhee Kim495c9b22017-11-07 16:32:09 +0000847 Boolean directConnFlag = dhcp6HandlerUtil.directlyConnected(dhcp6Relay);
Kalhee Kim45fede42017-09-05 19:05:06 +0000848
Kalhee Kim495c9b22017-11-07 16:32:09 +0000849 DHCP6 embeddedDhcp6 = dhcp6Relay.getOptions().stream()
850 .filter(opt -> opt instanceof Dhcp6RelayOption)
851 .map(BasePacket::getPayload)
852 .map(pld -> (DHCP6) pld)
853 .findFirst()
854 .orElse(null);
855
Kalhee Kimd21029f2017-09-26 20:21:53 +0000856 ConnectPoint inPort = context.inPacket().receivedFrom();
Kalhee Kim495c9b22017-11-07 16:32:09 +0000857 DhcpServerInfo foundServerInfo = findServerInfoFromServer(directConnFlag, inPort);
Kalhee Kimd21029f2017-09-26 20:21:53 +0000858
Kalhee Kim495c9b22017-11-07 16:32:09 +0000859 if (foundServerInfo == null) {
860 log.warn("Cannot find server info");
Kalhee Kimd21029f2017-09-26 20:21:53 +0000861 return null;
Kalhee Kim495c9b22017-11-07 16:32:09 +0000862 } else {
863 if (dhcp6HandlerUtil.isServerIpEmpty(foundServerInfo)) {
864 log.warn("Cannot find server info's ipaddress");
865 return null;
866 }
Kalhee Kimd21029f2017-09-26 20:21:53 +0000867 }
868
Kalhee Kim45fede42017-09-05 19:05:06 +0000869 Dhcp6InterfaceIdOption interfaceIdOption = dhcp6Relay.getOptions().stream()
870 .filter(opt -> opt instanceof Dhcp6InterfaceIdOption)
871 .map(opt -> (Dhcp6InterfaceIdOption) opt)
872 .findFirst()
873 .orElse(null);
Kalhee Kim45fede42017-09-05 19:05:06 +0000874 if (interfaceIdOption == null) {
875 log.warn("Interface Id option is not present, abort packet...");
876 return null;
877 }
878
879 MacAddress peerMac = interfaceIdOption.getMacAddress();
880 String clientConnectionPointStr = new String(interfaceIdOption.getInPort());
Kalhee Kim45fede42017-09-05 19:05:06 +0000881 ConnectPoint clientConnectionPoint = ConnectPoint.deviceConnectPoint(clientConnectionPointStr);
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +0000882 VlanId vlanIdInUse = VlanId.vlanId(interfaceIdOption.getVlanId());
883 Interface clientInterface = interfaceService.getInterfacesByPort(clientConnectionPoint)
Kalhee Kim495c9b22017-11-07 16:32:09 +0000884 .stream().filter(iface -> dhcp6HandlerUtil.interfaceContainsVlan(iface, vlanIdInUse))
885 .findFirst().orElse(null);
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +0000886 if (clientInterface == null) {
887 log.warn("Cannot get client interface for from packet, abort... vlan {}", vlanIdInUse.toString());
Kalhee Kim45fede42017-09-05 19:05:06 +0000888 return null;
889 }
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +0000890 MacAddress relayAgentMac = clientInterface.mac();
Kalhee Kim45fede42017-09-05 19:05:06 +0000891 if (relayAgentMac == null) {
Yi Tseng68ef26b2017-12-18 17:10:00 -0800892 log.warn("Can not get client interface mac, abort packet..");
Kalhee Kim45fede42017-09-05 19:05:06 +0000893 return null;
894 }
895 etherReply.setSourceMACAddress(relayAgentMac);
896
897 // find destMac
Yi Tseng68ef26b2017-12-18 17:10:00 -0800898 MacAddress clientMac;
Yi Tsengaa417a62017-09-08 17:22:51 -0700899 Ip6Address peerAddress = Ip6Address.valueOf(dhcp6Relay.getPeerAddress());
900 Set<Host> clients = hostService.getHostsByIp(peerAddress);
Kalhee Kim45fede42017-09-05 19:05:06 +0000901 if (clients.isEmpty()) {
Yi Tseng68ef26b2017-12-18 17:10:00 -0800902 log.trace("There's no host found for this address {}",
Kalhee Kim45fede42017-09-05 19:05:06 +0000903 HexString.toHexString(dhcp6Relay.getPeerAddress(), ":"));
Yi Tseng68ef26b2017-12-18 17:10:00 -0800904 log.trace("Let's look up interfaceId {}", HexString.toHexString(peerMac.toBytes(), ":"));
Kalhee Kim45fede42017-09-05 19:05:06 +0000905 clientMac = peerMac;
906 } else {
907 clientMac = clients.iterator().next().mac();
908 if (clientMac == null) {
909 log.warn("No client mac address found, abort packet...");
910 return null;
911 }
Yi Tseng68ef26b2017-12-18 17:10:00 -0800912 log.trace("Client mac address found from getHostByIp");
Kalhee Kim45fede42017-09-05 19:05:06 +0000913 }
914 etherReply.setDestinationMACAddress(clientMac);
Kalhee Kim45fede42017-09-05 19:05:06 +0000915 // ip header
916 ipv6Packet.setSourceAddress(dhcp6Relay.getLinkAddress());
917 ipv6Packet.setDestinationAddress(dhcp6Relay.getPeerAddress());
918 // udp header
919 udpPacket.setSourcePort(UDP.DHCP_V6_SERVER_PORT);
920 if (directConnFlag) {
921 udpPacket.setDestinationPort(UDP.DHCP_V6_CLIENT_PORT);
922 } else {
923 udpPacket.setDestinationPort(UDP.DHCP_V6_SERVER_PORT);
924 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000925 // add host or route
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000926 addHostOrRoute(directConnFlag, clientConnectionPoint, dhcp6Relay, embeddedDhcp6, clientMac, clientInterface);
Kalhee Kim45fede42017-09-05 19:05:06 +0000927
928 udpPacket.setPayload(embeddedDhcp6);
929 udpPacket.resetChecksum();
930 ipv6Packet.setPayload(udpPacket);
931 etherReply.setPayload(ipv6Packet);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000932 return new Dhcp6HandlerUtil().new InternalPacket(etherReply, clientConnectionPoint);
Kalhee Kim45fede42017-09-05 19:05:06 +0000933 }
934
Yi Tseng483ac6f2017-08-02 15:03:31 -0700935
936 @Override
Kalhee Kimba366062017-11-07 16:32:09 +0000937 public void setDhcpFpmEnabled(Boolean enabled) {
938 dhcpFpmEnabled = enabled;
939 }
940
941 @Override
Yi Tseng483ac6f2017-08-02 15:03:31 -0700942 public void setDefaultDhcpServerConfigs(Collection<DhcpServerConfig> configs) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000943 log.debug("setDefaultDhcpServerConfigs is called.");
944
Yi Tseng919b2df2017-09-07 16:22:51 -0700945 setDhcpServerConfigs(configs, defaultServerInfoList);
Yi Tseng919b2df2017-09-07 16:22:51 -0700946 }
947
948 @Override
949 public void setIndirectDhcpServerConfigs(Collection<DhcpServerConfig> configs) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000950 log.debug("setIndirectDhcpServerConfigs is called.");
951
Yi Tseng919b2df2017-09-07 16:22:51 -0700952 setDhcpServerConfigs(configs, indirectServerInfoList);
Yi Tseng919b2df2017-09-07 16:22:51 -0700953 }
954
955 public void setDhcpServerConfigs(Collection<DhcpServerConfig> configs, List<DhcpServerInfo> serverInfoList) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000956 log.debug("config size {}.", configs.size());
957
Kalhee Kim45fede42017-09-05 19:05:06 +0000958 if (configs.size() == 0) {
959 // no config to update
960 return;
961 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000962 // TODO: currently we pick up first DHCP server config.
963 // Will use other server configs in the future for HA.
Kalhee Kim495c9b22017-11-07 16:32:09 +0000964 Boolean isConfigValid = false;
965 for (DhcpServerConfig serverConfig : configs) {
966 if (serverConfig.getDhcpServerIp6().isPresent()) {
967 isConfigValid = true;
968 break;
969 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000970 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000971 if (!isConfigValid) {
972 log.warn("No IP V6 server address found.");
973 return; // No IP V6 address found
974 }
975 for (DhcpServerInfo oldServerInfo : serverInfoList) {
Yi Tseng919b2df2017-09-07 16:22:51 -0700976 // stop monitoring gateway or server
977 oldServerInfo.getDhcpGatewayIp6().ifPresent(gatewayIp -> {
978 hostService.stopMonitoringIp(gatewayIp);
979 });
980 oldServerInfo.getDhcpServerIp6().ifPresent(serverIp -> {
981 hostService.stopMonitoringIp(serverIp);
Yi Tseng525ff402017-10-23 19:39:39 -0700982 cancelDhcpPacket(serverIp);
Yi Tseng919b2df2017-09-07 16:22:51 -0700983 });
984 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000985 serverInfoList.clear();
986 for (DhcpServerConfig serverConfig : configs) {
987 // Create new server info according to the config
988 DhcpServerInfo newServerInfo = new DhcpServerInfo(serverConfig,
989 DhcpServerInfo.Version.DHCP_V6);
990 checkState(newServerInfo.getDhcpServerConnectPoint().isPresent(),
991 "Connect point not exists");
992 checkState(newServerInfo.getDhcpServerIp6().isPresent(),
993 "IP of DHCP server not exists");
Yi Tseng919b2df2017-09-07 16:22:51 -0700994
Kalhee Kim495c9b22017-11-07 16:32:09 +0000995 log.debug("DHCP server connect point: {}", newServerInfo.getDhcpServerConnectPoint().orElse(null));
996 log.debug("DHCP server IP: {}", newServerInfo.getDhcpServerIp6().orElse(null));
Yi Tseng919b2df2017-09-07 16:22:51 -0700997
Kalhee Kim495c9b22017-11-07 16:32:09 +0000998 Ip6Address serverIp = newServerInfo.getDhcpServerIp6().get();
999 Ip6Address ipToProbe;
1000 if (newServerInfo.getDhcpGatewayIp6().isPresent()) {
1001 ipToProbe = newServerInfo.getDhcpGatewayIp6().get();
1002 } else {
1003 ipToProbe = newServerInfo.getDhcpServerIp6().orElse(null);
1004 }
1005 String hostToProbe = newServerInfo.getDhcpGatewayIp6()
1006 .map(ip -> "gateway").orElse("server");
Yi Tseng919b2df2017-09-07 16:22:51 -07001007
Kalhee Kim495c9b22017-11-07 16:32:09 +00001008 log.warn("Probing to resolve {} IP {}", hostToProbe, ipToProbe);
1009 hostService.startMonitoringIp(ipToProbe);
1010
1011 Set<Host> hosts = hostService.getHostsByIp(ipToProbe);
1012 if (!hosts.isEmpty()) {
1013 Host host = hosts.iterator().next();
1014 newServerInfo.setDhcpConnectVlan(host.vlan());
1015 newServerInfo.setDhcpConnectMac(host.mac());
1016 log.warn("Host found host {}", host);
1017
1018 } else {
1019 log.warn("No host found host ip {}", ipToProbe);
1020 }
1021 // Add new server info
1022 synchronized (this) {
1023 serverInfoList.add(newServerInfo);
1024 }
1025 if (!hosts.isEmpty()) {
1026 requestDhcpPacket(serverIp);
1027 }
Yi Tseng919b2df2017-09-07 16:22:51 -07001028 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001029 }
1030
1031 class InternalHostListener implements HostListener {
1032 @Override
1033 public void event(HostEvent event) {
1034 switch (event.type()) {
1035 case HOST_ADDED:
1036 case HOST_UPDATED:
1037 hostUpdated(event.subject());
1038 break;
1039 case HOST_REMOVED:
1040 hostRemoved(event.subject());
1041 break;
Kalhee Kim45fede42017-09-05 19:05:06 +00001042 default:
1043 break;
1044 }
1045 }
1046 }
1047
1048 /**
Kalhee Kim45fede42017-09-05 19:05:06 +00001049 * Handle host updated.
1050 * If the host is DHCP server or gateway, update connect mac and vlan.
1051 *
1052 * @param host the host
1053 */
1054 private void hostUpdated(Host host) {
Yi Tseng525ff402017-10-23 19:39:39 -07001055 hostUpdated(host, defaultServerInfoList);
1056 hostUpdated(host, indirectServerInfoList);
Kalhee Kim45fede42017-09-05 19:05:06 +00001057 }
1058
Yi Tseng525ff402017-10-23 19:39:39 -07001059 private void hostUpdated(Host host, List<DhcpServerInfo> serverInfoList) {
1060 DhcpServerInfo serverInfo;
1061 Ip6Address targetIp;
1062 if (!serverInfoList.isEmpty()) {
1063 serverInfo = serverInfoList.get(0);
1064 Ip6Address serverIp = serverInfo.getDhcpServerIp6().orElse(null);
1065 targetIp = serverInfo.getDhcpGatewayIp6().orElse(null);
1066
1067 if (targetIp == null) {
1068 targetIp = serverIp;
1069 }
Yi Tseng525ff402017-10-23 19:39:39 -07001070 if (targetIp != null) {
1071 if (host.ipAddresses().contains(targetIp)) {
1072 serverInfo.setDhcpConnectMac(host.mac());
1073 serverInfo.setDhcpConnectVlan(host.vlan());
1074 requestDhcpPacket(serverIp);
1075 }
1076 }
1077 }
1078 }
1079
Kalhee Kim45fede42017-09-05 19:05:06 +00001080 /**
1081 * Handle host removed.
1082 * If the host is DHCP server or gateway, unset connect mac and vlan.
1083 *
1084 * @param host the host
1085 */
1086 private void hostRemoved(Host host) {
Yi Tseng525ff402017-10-23 19:39:39 -07001087 hostRemoved(host, defaultServerInfoList);
1088 hostRemoved(host, indirectServerInfoList);
Yi Tseng919b2df2017-09-07 16:22:51 -07001089 }
1090
Yi Tseng525ff402017-10-23 19:39:39 -07001091 private void hostRemoved(Host host, List<DhcpServerInfo> serverInfoList) {
1092 DhcpServerInfo serverInfo;
1093 Ip6Address targetIp;
1094
1095 if (!serverInfoList.isEmpty()) {
1096 serverInfo = serverInfoList.get(0);
1097 Ip6Address serverIp = serverInfo.getDhcpServerIp6().orElse(null);
1098 targetIp = serverInfo.getDhcpGatewayIp6().orElse(null);
1099
1100 if (targetIp == null) {
1101 targetIp = serverIp;
1102 }
Yi Tseng525ff402017-10-23 19:39:39 -07001103 if (targetIp != null) {
1104 if (host.ipAddresses().contains(targetIp)) {
1105 serverInfo.setDhcpConnectVlan(null);
1106 serverInfo.setDhcpConnectMac(null);
1107 cancelDhcpPacket(serverIp);
1108 }
1109 }
1110 }
1111 }
1112
Kalhee Kim45b24182017-10-18 18:30:23 +00001113 /**
1114 * Returns the first interface ip from interface.
1115 *
1116 * @param iface interface of one connect point
1117 * @return the first interface IP; null if not exists an IP address in
1118 * these interfaces
1119 */
1120 private Ip6Address getFirstIpFromInterface(Interface iface) {
1121 checkNotNull(iface, "Interface can't be null");
1122 return iface.ipAddressesList().stream()
1123 .map(InterfaceIpAddress::ipAddress)
1124 .filter(IpAddress::isIp6)
1125 .map(IpAddress::getIp6Address)
1126 .findFirst()
1127 .orElse(null);
1128 }
1129
1130 /**
Kalhee Kim495c9b22017-11-07 16:32:09 +00001131 * Checks if serverInfo's host info (mac and vlan) is filled in; if not, fills in.
1132 *
1133 * @param serverInfo server information
1134 * @return newServerInfo if host info can be either found or filled in.
1135 */
1136 private DhcpServerInfo getHostInfoForServerInfo(DhcpServerInfo serverInfo, List<DhcpServerInfo> sererInfoList) {
1137 DhcpServerInfo newServerInfo = null;
1138 MacAddress dhcpServerConnectMac = serverInfo.getDhcpConnectMac().orElse(null);
1139 VlanId dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1140 ConnectPoint dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1141
1142 if (dhcpServerConnectMac != null && dhcpConnectVlan != null) {
1143 newServerInfo = serverInfo;
1144 log.info("DHCP server {} host info found. ConnectPt{} Mac {} vlan {}", serverInfo.getDhcpServerIp6(),
1145 dhcpServerConnectPoint, dhcpServerConnectMac, dhcpConnectVlan);
1146 } else {
1147 log.warn("DHCP server {} not resolve yet connectPt {} mac {} vlan {}", serverInfo.getDhcpServerIp6(),
1148 dhcpServerConnectPoint, dhcpServerConnectMac, dhcpConnectVlan);
1149
1150 Ip6Address ipToProbe;
1151 if (serverInfo.getDhcpGatewayIp6().isPresent()) {
1152 ipToProbe = serverInfo.getDhcpGatewayIp6().get();
1153 } else {
1154 ipToProbe = serverInfo.getDhcpServerIp6().orElse(null);
1155 }
1156 String hostToProbe = serverInfo.getDhcpGatewayIp6()
1157 .map(ip -> "gateway").orElse("server");
1158
1159 log.info("Dynamically probing to resolve {} IP {}", hostToProbe, ipToProbe);
1160 hostService.startMonitoringIp(ipToProbe);
1161
1162 Set<Host> hosts = hostService.getHostsByIp(ipToProbe);
1163 if (!hosts.isEmpty()) {
1164 int serverInfoIndex = sererInfoList.indexOf(serverInfo);
1165 Host host = hosts.iterator().next();
1166 serverInfo.setDhcpConnectVlan(host.vlan());
1167 serverInfo.setDhcpConnectMac(host.mac());
1168 // replace the serverInfo in the list
1169 sererInfoList.set(serverInfoIndex, serverInfo);
1170 newServerInfo = serverInfo;
1171 log.warn("Dynamically host found host {}", host);
1172 } else {
1173 log.warn("No host found host ip {} dynamically", ipToProbe);
1174 }
1175 }
1176 return newServerInfo;
1177 }
1178
1179 /**
Kalhee Kim45b24182017-10-18 18:30:23 +00001180 * Gets Interface facing to the server for default host.
1181 *
Kalhee Kim495c9b22017-11-07 16:32:09 +00001182 * @param serverInfo server information
Kalhee Kim45b24182017-10-18 18:30:23 +00001183 * @return the Interface facing to the server; null if not found
1184 */
Kalhee Kim495c9b22017-11-07 16:32:09 +00001185 private Interface getServerInterface(DhcpServerInfo serverInfo) {
1186 Interface serverInterface = null;
Yi Tseng25bfe372017-11-03 16:27:32 -07001187
Kalhee Kim495c9b22017-11-07 16:32:09 +00001188 ConnectPoint dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1189 VlanId dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1190
1191 if (dhcpServerConnectPoint != null && dhcpConnectVlan != null) {
1192 serverInterface = interfaceService.getInterfacesByPort(dhcpServerConnectPoint)
1193 .stream()
1194 .filter(iface -> dhcp6HandlerUtil.interfaceContainsVlan(iface, dhcpConnectVlan))
1195 .findFirst()
1196 .orElse(null);
Yi Tseng25bfe372017-11-03 16:27:32 -07001197 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +00001198 log.warn("DHCP server {} not resolve yet connectPoint {} vlan {}", serverInfo.getDhcpServerIp6(),
1199 dhcpServerConnectPoint, dhcpConnectVlan);
Kalhee Kim45b24182017-10-18 18:30:23 +00001200 }
Kalhee Kim495c9b22017-11-07 16:32:09 +00001201
1202 return serverInterface;
Kalhee Kim45b24182017-10-18 18:30:23 +00001203 }
1204
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +00001205
Yi Tseng525ff402017-10-23 19:39:39 -07001206 private void requestDhcpPacket(Ip6Address serverIp) {
1207 requestServerDhcpPacket(serverIp);
1208 requestClientDhcpPacket(serverIp);
1209 }
1210
1211 private void cancelDhcpPacket(Ip6Address serverIp) {
1212 cancelServerDhcpPacket(serverIp);
1213 cancelClientDhcpPacket(serverIp);
1214 }
1215
1216 private void cancelServerDhcpPacket(Ip6Address serverIp) {
1217 TrafficSelector serverSelector =
1218 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1219 .matchIPv6Src(serverIp.toIpPrefix())
1220 .build();
1221 packetService.cancelPackets(serverSelector,
1222 PacketPriority.CONTROL,
1223 appId);
1224 }
1225
1226 private void requestServerDhcpPacket(Ip6Address serverIp) {
1227 TrafficSelector serverSelector =
1228 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1229 .matchIPv6Src(serverIp.toIpPrefix())
1230 .build();
1231 packetService.requestPackets(serverSelector,
1232 PacketPriority.CONTROL,
1233 appId);
1234 }
1235
1236 private void cancelClientDhcpPacket(Ip6Address serverIp) {
1237 // Packet comes from relay
1238 TrafficSelector indirectClientSelector =
1239 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1240 .matchIPv6Dst(serverIp.toIpPrefix())
1241 .build();
1242 packetService.cancelPackets(indirectClientSelector,
1243 PacketPriority.CONTROL,
1244 appId);
Yi Tseng41dde702017-11-02 15:21:10 -07001245 indirectClientSelector =
1246 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1247 .matchIPv6Dst(Ip6Address.ALL_DHCP_RELAY_AGENTS_AND_SERVERS.toIpPrefix())
1248 .build();
1249 packetService.cancelPackets(indirectClientSelector,
1250 PacketPriority.CONTROL,
1251 appId);
1252 indirectClientSelector =
1253 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1254 .matchIPv6Dst(Ip6Address.ALL_DHCP_SERVERS.toIpPrefix())
1255 .build();
1256 packetService.cancelPackets(indirectClientSelector,
1257 PacketPriority.CONTROL,
1258 appId);
Yi Tseng525ff402017-10-23 19:39:39 -07001259
1260 // Packet comes from client
1261 packetService.cancelPackets(CLIENT_SERVER_SELECTOR,
1262 PacketPriority.CONTROL,
1263 appId);
1264 }
1265
1266 private void requestClientDhcpPacket(Ip6Address serverIp) {
1267 // Packet comes from relay
1268 TrafficSelector indirectClientSelector =
1269 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1270 .matchIPv6Dst(serverIp.toIpPrefix())
1271 .build();
1272 packetService.requestPackets(indirectClientSelector,
1273 PacketPriority.CONTROL,
1274 appId);
Yi Tseng41dde702017-11-02 15:21:10 -07001275 indirectClientSelector =
1276 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1277 .matchIPv6Dst(Ip6Address.ALL_DHCP_RELAY_AGENTS_AND_SERVERS.toIpPrefix())
1278 .build();
1279 packetService.requestPackets(indirectClientSelector,
1280 PacketPriority.CONTROL,
1281 appId);
1282 indirectClientSelector =
1283 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1284 .matchIPv6Dst(Ip6Address.ALL_DHCP_SERVERS.toIpPrefix())
1285 .build();
1286 packetService.requestPackets(indirectClientSelector,
1287 PacketPriority.CONTROL,
1288 appId);
Yi Tseng525ff402017-10-23 19:39:39 -07001289
1290 // Packet comes from client
1291 packetService.requestPackets(CLIENT_SERVER_SELECTOR,
1292 PacketPriority.CONTROL,
1293 appId);
1294 }
1295
1296 /**
1297 * Process the ignore rules.
1298 *
1299 * @param deviceId the device id
1300 * @param vlanId the vlan to be ignored
1301 * @param op the operation, ADD to install; REMOVE to uninstall rules
1302 */
1303 private void processIgnoreVlanRule(DeviceId deviceId, VlanId vlanId, Objective.Operation op) {
Yi Tseng525ff402017-10-23 19:39:39 -07001304 AtomicInteger installedCount = new AtomicInteger(DHCP_SELECTORS.size());
1305 DHCP_SELECTORS.forEach(trafficSelector -> {
1306 TrafficSelector selector = DefaultTrafficSelector.builder(trafficSelector)
1307 .matchVlanId(vlanId)
1308 .build();
1309
1310 ForwardingObjective.Builder builder = DefaultForwardingObjective.builder()
1311 .withFlag(ForwardingObjective.Flag.VERSATILE)
1312 .withSelector(selector)
1313 .withPriority(IGNORE_CONTROL_PRIORITY)
Yi Tsengdbabeed2017-11-29 10:49:20 -08001314 .withTreatment(DefaultTrafficTreatment.emptyTreatment())
Yi Tseng525ff402017-10-23 19:39:39 -07001315 .fromApp(appId);
1316
1317
1318 ObjectiveContext objectiveContext = new ObjectiveContext() {
1319 @Override
1320 public void onSuccess(Objective objective) {
1321 log.info("Ignore rule {} (Vlan id {}, device {}, selector {})",
1322 op, vlanId, deviceId, selector);
1323 int countDown = installedCount.decrementAndGet();
1324 if (countDown != 0) {
1325 return;
1326 }
1327 switch (op) {
1328 case ADD:
1329 ignoredVlans.put(deviceId, vlanId);
1330 break;
1331 case REMOVE:
1332 ignoredVlans.remove(deviceId, vlanId);
1333 break;
1334 default:
1335 log.warn("Unsupported objective operation {}", op);
1336 break;
1337 }
1338 }
1339
1340 @Override
1341 public void onError(Objective objective, ObjectiveError error) {
1342 log.warn("Can't {} ignore rule (vlan id {}, selector {}, device {}) due to {}",
1343 op, vlanId, selector, deviceId, error);
1344 }
1345 };
1346
1347 ForwardingObjective fwd;
1348 switch (op) {
1349 case ADD:
1350 fwd = builder.add(objectiveContext);
1351 break;
1352 case REMOVE:
1353 fwd = builder.remove(objectiveContext);
1354 break;
1355 default:
1356 log.warn("Unsupported objective operation {}", op);
1357 return;
1358 }
1359
1360 Device device = deviceService.getDevice(deviceId);
1361 if (device == null || !device.is(Pipeliner.class)) {
1362 log.warn("Device {} is not available now, wait until device is available", deviceId);
1363 return;
1364 }
1365 flowObjectiveService.apply(deviceId, fwd);
1366 });
1367 }
Kalhee Kim121ba922017-11-01 17:56:44 +00001368 /**
1369 * Find first ipaddress for a given Host info i.e. mac and vlan.
1370 *
1371 * @param clientMac client mac
1372 * @param vlanId packet's vlan
1373 * @return next-hop link-local ipaddress for a given host
1374 */
Kalhee Kim495c9b22017-11-07 16:32:09 +00001375 private IpAddress getFirstIpByHost(Boolean directConnFlag, MacAddress clientMac, VlanId vlanId) {
Kalhee Kim121ba922017-11-01 17:56:44 +00001376 IpAddress nextHopIp;
1377 // pick out the first link-local ip address
1378 HostId gwHostId = HostId.hostId(clientMac, vlanId);
1379 Host gwHost = hostService.getHost(gwHostId);
1380 if (gwHost == null) {
1381 log.warn("Can't find gateway host for hostId {}", gwHostId);
1382 return null;
1383 }
Kalhee Kim495c9b22017-11-07 16:32:09 +00001384 if (directConnFlag) {
1385 nextHopIp = gwHost.ipAddresses()
1386 .stream()
1387 .filter(IpAddress::isIp6)
1388 .map(IpAddress::getIp6Address)
1389 .findFirst()
1390 .orElse(null);
1391 } else {
1392 nextHopIp = gwHost.ipAddresses()
1393 .stream()
1394 .filter(IpAddress::isIp6)
1395 .filter(ip6 -> ip6.isLinkLocal())
1396 .map(IpAddress::getIp6Address)
1397 .findFirst()
1398 .orElse(null);
1399 }
Kalhee Kim121ba922017-11-01 17:56:44 +00001400 return nextHopIp;
1401 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +00001402
Kalhee Kim495c9b22017-11-07 16:32:09 +00001403 private List<DhcpServerInfo> findValidServerInfo(boolean directConnFlag) {
1404 List<DhcpServerInfo> validServerInfo;
1405
1406 if (directConnFlag || indirectServerInfoList.isEmpty()) {
1407 validServerInfo = new ArrayList<DhcpServerInfo>(defaultServerInfoList);
1408 } else {
1409 validServerInfo = new ArrayList<DhcpServerInfo>(indirectServerInfoList);
1410 }
1411 return validServerInfo;
1412 }
1413
1414 private DhcpServerInfo findServerInfoFromServer(boolean directConnFlag, ConnectPoint inPort) {
1415 List<DhcpServerInfo> validServerInfoList = findValidServerInfo(directConnFlag);
1416 DhcpServerInfo foundServerInfo = null;
1417 for (DhcpServerInfo serverInfo : validServerInfoList) {
1418 if (inPort.equals(serverInfo.getDhcpServerConnectPoint().get())) {
1419 foundServerInfo = serverInfo;
1420 log.warn("ServerInfo found for Rcv port {} Server Connect Point {} for {}",
1421 inPort, serverInfo.getDhcpServerConnectPoint(), directConnFlag ? "direct" : "indirect");
1422 break;
1423 } else {
1424 log.warn("Rcv port {} not the same as Server Connect Point {} for {}",
1425 inPort, serverInfo.getDhcpServerConnectPoint(), directConnFlag ? "direct" : "indirect");
1426 }
1427 }
1428 return foundServerInfo;
1429 }
1430 /**
1431 * Set the dhcp6 lease expiry poll interval value.
1432 *
1433 * @param val poll interval value in seconds
1434 */
1435 public void setDhcp6PollInterval(int val) {
1436 dhcp6PollInterval = val;
1437 }
1438
1439 /**
1440 * Get the dhcp6 lease expiry poll interval value.
1441 *
1442 * @return poll interval value in seconds
1443 */
1444 public int getDhcp6PollInterval() {
1445 return dhcp6PollInterval;
1446 }
Yi Tseng51301292017-07-28 13:02:59 -07001447}