blob: e60cad8dfc5a6c55defaceb3205f34740bf67866 [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;
Charles Chan70fdd492018-03-07 17:36:06 -080022import com.google.common.collect.Multimaps;
Kalhee Kim45fede42017-09-05 19:05:06 +000023import org.apache.felix.scr.annotations.Activate;
24import org.apache.felix.scr.annotations.Deactivate;
Kalhee Kim45fede42017-09-05 19:05:06 +000025import com.google.common.collect.Sets;
26import com.google.common.collect.ImmutableSet;
Yi Tseng51301292017-07-28 13:02:59 -070027import org.apache.felix.scr.annotations.Component;
28import org.apache.felix.scr.annotations.Property;
Kalhee Kim45fede42017-09-05 19:05:06 +000029import org.apache.felix.scr.annotations.Reference;
30import org.apache.felix.scr.annotations.ReferenceCardinality;
Yi Tseng51301292017-07-28 13:02:59 -070031import org.apache.felix.scr.annotations.Service;
32import org.onlab.packet.BasePacket;
Kalhee Kim45fede42017-09-05 19:05:06 +000033import org.onlab.packet.DHCP6;
34import org.onlab.packet.IPv6;
35import org.onlab.packet.Ethernet;
36import org.onlab.packet.Ip6Address;
Yi Tseng51301292017-07-28 13:02:59 -070037import org.onlab.packet.IpAddress;
Kalhee Kim45fede42017-09-05 19:05:06 +000038import org.onlab.packet.IpPrefix;
Yi Tseng51301292017-07-28 13:02:59 -070039import org.onlab.packet.MacAddress;
Yi Tseng525ff402017-10-23 19:39:39 -070040import org.onlab.packet.TpPort;
Kalhee Kim45fede42017-09-05 19:05:06 +000041import org.onlab.packet.UDP;
Yi Tseng51301292017-07-28 13:02:59 -070042import org.onlab.packet.VlanId;
Lior Assoulinea21c0ca2018-01-28 16:18:48 -080043import org.onlab.packet.dhcp.Dhcp6ClientDataOption;
44import org.onlab.packet.dhcp.Dhcp6LeaseQueryOption;
Kalhee Kim45fede42017-09-05 19:05:06 +000045import org.onlab.packet.dhcp.Dhcp6RelayOption;
46import org.onlab.packet.dhcp.Dhcp6InterfaceIdOption;
Lior Assoulinea21c0ca2018-01-28 16:18:48 -080047import org.onlab.packet.dhcp.Dhcp6Option;
Kalhee Kim45fede42017-09-05 19:05:06 +000048import org.onlab.packet.dhcp.Dhcp6IaNaOption;
49import org.onlab.packet.dhcp.Dhcp6IaTaOption;
50import org.onlab.packet.dhcp.Dhcp6IaPdOption;
51import org.onlab.packet.dhcp.Dhcp6IaAddressOption;
52import org.onlab.packet.dhcp.Dhcp6IaPrefixOption;
Kalhee Kimea4b6c22017-11-09 14:38:37 +000053import org.onlab.packet.dhcp.Dhcp6ClientIdOption;
54import org.onlab.packet.dhcp.Dhcp6Duid;
Kalhee Kim495c9b22017-11-07 16:32:09 +000055import org.onlab.packet.DHCP6.MsgType;
Kalhee Kim45fede42017-09-05 19:05:06 +000056import org.onlab.util.HexString;
Yi Tseng525ff402017-10-23 19:39:39 -070057import org.onosproject.core.ApplicationId;
58import org.onosproject.core.CoreService;
Yi Tseng51301292017-07-28 13:02:59 -070059import org.onosproject.dhcprelay.api.DhcpHandler;
Yi Tseng919b2df2017-09-07 16:22:51 -070060import org.onosproject.dhcprelay.api.DhcpServerInfo;
Yi Tseng525ff402017-10-23 19:39:39 -070061import org.onosproject.dhcprelay.config.IgnoreDhcpConfig;
Kalhee Kim45fede42017-09-05 19:05:06 +000062import org.onosproject.dhcprelay.store.DhcpRelayStore;
Kalhee Kimea4b6c22017-11-09 14:38:37 +000063import org.onosproject.dhcprelay.store.DhcpRecord;
Kalhee Kimba366062017-11-07 16:32:09 +000064import org.onosproject.dhcprelay.store.DhcpFpmPrefixStore;
Kalhee Kimd94ceea2017-11-29 19:03:02 +000065import org.onosproject.dhcprelay.store.DhcpRelayCounters;
66import org.onosproject.dhcprelay.store.DhcpRelayCountersStore;
Yi Tseng525ff402017-10-23 19:39:39 -070067import org.onosproject.net.Device;
68import org.onosproject.net.DeviceId;
Kalhee Kimba366062017-11-07 16:32:09 +000069import org.onosproject.routing.fpm.api.FpmRecord;
Yi Tseng525ff402017-10-23 19:39:39 -070070import org.onosproject.net.behaviour.Pipeliner;
71import org.onosproject.net.device.DeviceService;
72import org.onosproject.net.flow.DefaultTrafficSelector;
73import org.onosproject.net.flow.TrafficSelector;
74import org.onosproject.net.flowobjective.DefaultForwardingObjective;
75import org.onosproject.net.flowobjective.FlowObjectiveService;
76import org.onosproject.net.flowobjective.ForwardingObjective;
77import org.onosproject.net.flowobjective.Objective;
78import org.onosproject.net.flowobjective.ObjectiveContext;
79import org.onosproject.net.flowobjective.ObjectiveError;
Yi Tsengaa417a62017-09-08 17:22:51 -070080import org.onosproject.net.host.HostProvider;
81import org.onosproject.net.host.HostProviderRegistry;
82import org.onosproject.net.host.HostProviderService;
Kalhee Kim45fede42017-09-05 19:05:06 +000083import org.onosproject.net.host.HostService;
84import org.onosproject.net.host.DefaultHostDescription;
85import org.onosproject.net.host.HostDescription;
86import org.onosproject.net.host.InterfaceIpAddress;
87import org.onosproject.net.host.HostListener;
88import org.onosproject.net.host.HostEvent;
89import org.onosproject.net.intf.Interface;
90import org.onosproject.net.intf.InterfaceService;
Yi Tseng525ff402017-10-23 19:39:39 -070091import org.onosproject.net.packet.PacketPriority;
Yi Tsengaa417a62017-09-08 17:22:51 -070092import org.onosproject.net.provider.ProviderId;
Kalhee Kim45fede42017-09-05 19:05:06 +000093import org.onosproject.routeservice.Route;
94import org.onosproject.routeservice.RouteStore;
Yi Tseng483ac6f2017-08-02 15:03:31 -070095import org.onosproject.dhcprelay.config.DhcpServerConfig;
Yi Tseng51301292017-07-28 13:02:59 -070096import org.onosproject.net.ConnectPoint;
Kalhee Kim45fede42017-09-05 19:05:06 +000097import org.onosproject.net.Host;
98import org.onosproject.net.HostId;
99import org.onosproject.net.HostLocation;
100import org.onosproject.net.packet.DefaultOutboundPacket;
101import org.onosproject.net.packet.OutboundPacket;
Yi Tseng51301292017-07-28 13:02:59 -0700102import org.onosproject.net.packet.PacketContext;
Kalhee Kim45fede42017-09-05 19:05:06 +0000103import org.onosproject.net.packet.PacketService;
104import org.slf4j.Logger;
105import org.slf4j.LoggerFactory;
106import org.onosproject.net.flow.DefaultTrafficTreatment;
107import org.onosproject.net.flow.TrafficTreatment;
Kalhee Kim495c9b22017-11-07 16:32:09 +0000108import org.onosproject.dhcprelay.Dhcp6HandlerUtil.InternalPacket;
Kalhee Kim45fede42017-09-05 19:05:06 +0000109import java.nio.ByteBuffer;
110import java.util.List;
Yi Tseng483ac6f2017-08-02 15:03:31 -0700111import java.util.Collection;
Yi Tseng51301292017-07-28 13:02:59 -0700112import java.util.Optional;
Kalhee Kim45fede42017-09-05 19:05:06 +0000113import java.util.Set;
114import java.util.ArrayList;
Charles Chan909cff82018-03-05 13:14:02 -0800115import java.util.concurrent.CopyOnWriteArrayList;
Jordan Halterman6328db72018-04-10 13:34:50 -0400116import java.util.concurrent.Executor;
Yi Tseng525ff402017-10-23 19:39:39 -0700117import java.util.concurrent.atomic.AtomicInteger;
Kalhee Kim45fede42017-09-05 19:05:06 +0000118import static com.google.common.base.Preconditions.checkNotNull;
119import static com.google.common.base.Preconditions.checkState;
Jordan Halterman6328db72018-04-10 13:34:50 -0400120import static java.util.concurrent.Executors.newSingleThreadExecutor;
121import static org.onlab.util.Tools.groupedThreads;
Yi Tseng525ff402017-10-23 19:39:39 -0700122import static org.onosproject.net.flowobjective.Objective.Operation.ADD;
123import static org.onosproject.net.flowobjective.Objective.Operation.REMOVE;
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000124import java.util.concurrent.Semaphore;
Yi Tseng51301292017-07-28 13:02:59 -0700125
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800126
Yi Tseng51301292017-07-28 13:02:59 -0700127@Component
128@Service
129@Property(name = "version", value = "6")
Yi Tsengaa417a62017-09-08 17:22:51 -0700130public class Dhcp6HandlerImpl implements DhcpHandler, HostProvider {
Charles Chand988c282017-09-12 17:09:32 -0700131 public static final String DHCP_V6_RELAY_APP = "org.onosproject.Dhcp6HandlerImpl";
Saurav Das7c6dec12017-09-13 14:35:56 -0700132 public static final ProviderId PROVIDER_ID = new ProviderId("dhcp6", DHCP_V6_RELAY_APP);
Yi Tseng525ff402017-10-23 19:39:39 -0700133 private static final int IGNORE_CONTROL_PRIORITY = PacketPriority.CONTROL.priorityValue() + 1000;
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000134 private String gCount = "global";
Yi Tseng525ff402017-10-23 19:39:39 -0700135 private static final TrafficSelector CLIENT_SERVER_SELECTOR = DefaultTrafficSelector.builder()
136 .matchEthType(Ethernet.TYPE_IPV6)
137 .matchIPProtocol(IPv6.PROTOCOL_UDP)
138 .matchIPv6Src(IpPrefix.IPV6_LINK_LOCAL_PREFIX)
139 .matchIPv6Dst(Ip6Address.ALL_DHCP_RELAY_AGENTS_AND_SERVERS.toIpPrefix())
140 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_V6_CLIENT_PORT))
141 .matchUdpDst(TpPort.tpPort(UDP.DHCP_V6_SERVER_PORT))
142 .build();
143 private static final TrafficSelector SERVER_RELAY_SELECTOR = DefaultTrafficSelector.builder()
144 .matchEthType(Ethernet.TYPE_IPV6)
145 .matchIPProtocol(IPv6.PROTOCOL_UDP)
146 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_V6_SERVER_PORT))
147 .matchUdpDst(TpPort.tpPort(UDP.DHCP_V6_SERVER_PORT))
148 .build();
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800149 // lease query reply is from server to client (no relay in between) - so we need to
150 // catch that scenario also ..
151 private static final TrafficSelector LEASE_QUERY_RESPONSE_SELECTOR = DefaultTrafficSelector.builder()
152 .matchEthType(Ethernet.TYPE_IPV6)
153 .matchIPProtocol(IPv6.PROTOCOL_UDP)
154 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_V6_SERVER_PORT))
155 .matchUdpDst(TpPort.tpPort(UDP.DHCP_V6_CLIENT_PORT))
156 .build();
Yi Tseng525ff402017-10-23 19:39:39 -0700157 static final Set<TrafficSelector> DHCP_SELECTORS = ImmutableSet.of(
158 CLIENT_SERVER_SELECTOR,
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800159 SERVER_RELAY_SELECTOR,
160 LEASE_QUERY_RESPONSE_SELECTOR
Yi Tseng525ff402017-10-23 19:39:39 -0700161 );
Kalhee Kim45fede42017-09-05 19:05:06 +0000162 private static Logger log = LoggerFactory.getLogger(Dhcp6HandlerImpl.class);
Yi Tseng51301292017-07-28 13:02:59 -0700163
Kalhee Kim45fede42017-09-05 19:05:06 +0000164 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
165 protected DhcpRelayStore dhcpRelayStore;
Yi Tseng51301292017-07-28 13:02:59 -0700166
Kalhee Kim45fede42017-09-05 19:05:06 +0000167 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000168 protected DhcpRelayCountersStore dhcpRelayCountersStore;
169
170 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Kalhee Kim45fede42017-09-05 19:05:06 +0000171 protected PacketService packetService;
172
173 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Kalhee Kim45fede42017-09-05 19:05:06 +0000174 protected RouteStore routeStore;
175
176 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
177 protected InterfaceService interfaceService;
178
179 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
180 protected HostService hostService;
181
Yi Tsengaa417a62017-09-08 17:22:51 -0700182 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
183 protected HostProviderRegistry providerRegistry;
Kalhee Kim45fede42017-09-05 19:05:06 +0000184
Yi Tseng525ff402017-10-23 19:39:39 -0700185 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
186 protected CoreService coreService;
187
188 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Kalhee Kimba366062017-11-07 16:32:09 +0000189 protected DhcpFpmPrefixStore dhcpFpmPrefixStore;
190
191 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Yi Tseng525ff402017-10-23 19:39:39 -0700192 protected DeviceService deviceService;
193
194 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
195 protected FlowObjectiveService flowObjectiveService;
196
Yi Tsengaa417a62017-09-08 17:22:51 -0700197 protected HostProviderService providerService;
Yi Tseng525ff402017-10-23 19:39:39 -0700198 protected ApplicationId appId;
Charles Chan70fdd492018-03-07 17:36:06 -0800199 protected Multimap<DeviceId, VlanId> ignoredVlans = Multimaps.synchronizedMultimap(HashMultimap.create());
Yi Tseng525ff402017-10-23 19:39:39 -0700200 private InternalHostListener hostListener = new InternalHostListener();
Kalhee Kimba366062017-11-07 16:32:09 +0000201 private Boolean dhcpFpmEnabled = false;
Kalhee Kim495c9b22017-11-07 16:32:09 +0000202 private Dhcp6HandlerUtil dhcp6HandlerUtil = new Dhcp6HandlerUtil();
Charles Chan909cff82018-03-05 13:14:02 -0800203 private List<DhcpServerInfo> defaultServerInfoList = new CopyOnWriteArrayList<>();
204 private List<DhcpServerInfo> indirectServerInfoList = new CopyOnWriteArrayList<>();
Jordan Halterman6328db72018-04-10 13:34:50 -0400205
206 private Executor hostEventExecutor = newSingleThreadExecutor(
207 groupedThreads("dhcp6-event-host", "%d", log));
208
Kalhee Kim495c9b22017-11-07 16:32:09 +0000209 private class IpAddressInfo {
210 Ip6Address ip6Address;
211 long prefTime;
212 }
213 private class PdPrefixInfo {
214 IpPrefix pdPrefix;
215 long prefTime;
216 }
217 protected int dhcp6PollInterval = 24 * 3600; // 24 hr period
218
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000219 // max 1 thread
220 static Semaphore recordSemaphore = new Semaphore(1);
Kalhee Kim45fede42017-09-05 19:05:06 +0000221
222 // CLIENT message types
223 public static final Set<Byte> MSG_TYPE_FROM_CLIENT =
224 ImmutableSet.of(DHCP6.MsgType.SOLICIT.value(),
225 DHCP6.MsgType.REQUEST.value(),
226 DHCP6.MsgType.REBIND.value(),
227 DHCP6.MsgType.RENEW.value(),
228 DHCP6.MsgType.RELEASE.value(),
229 DHCP6.MsgType.DECLINE.value(),
230 DHCP6.MsgType.CONFIRM.value(),
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800231 DHCP6.MsgType.RELAY_FORW.value(),
232 DHCP6.MsgType.LEASEQUERY.value());
Kalhee Kim45fede42017-09-05 19:05:06 +0000233 // SERVER message types
234 public static final Set<Byte> MSG_TYPE_FROM_SERVER =
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800235 ImmutableSet.of(DHCP6.MsgType.RELAY_REPL.value(),
236 DHCP6.MsgType.LEASEQUERY_REPLY.value());
Kalhee Kim45fede42017-09-05 19:05:06 +0000237
238 @Activate
239 protected void activate() {
Yi Tseng525ff402017-10-23 19:39:39 -0700240 appId = coreService.registerApplication(DHCP_V6_RELAY_APP);
Yi Tsengaa417a62017-09-08 17:22:51 -0700241 providerService = providerRegistry.register(this);
Kalhee Kim45fede42017-09-05 19:05:06 +0000242 hostService.addListener(hostListener);
Yi Tseng51301292017-07-28 13:02:59 -0700243 }
244
Kalhee Kim45fede42017-09-05 19:05:06 +0000245 @Deactivate
246 protected void deactivate() {
Yi Tsengaa417a62017-09-08 17:22:51 -0700247 providerRegistry.unregister(this);
Kalhee Kim45fede42017-09-05 19:05:06 +0000248 hostService.removeListener(hostListener);
Yi Tseng919b2df2017-09-07 16:22:51 -0700249 defaultServerInfoList.forEach(this::stopMonitoringIps);
250 defaultServerInfoList.clear();
251 indirectServerInfoList.forEach(this::stopMonitoringIps);
252 indirectServerInfoList.clear();
253 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000254
Yi Tseng919b2df2017-09-07 16:22:51 -0700255 private void stopMonitoringIps(DhcpServerInfo serverInfo) {
256 serverInfo.getDhcpGatewayIp6().ifPresent(gatewayIp -> {
257 hostService.stopMonitoringIp(gatewayIp);
258 });
259 serverInfo.getDhcpServerIp6().ifPresent(serverIp -> {
260 hostService.stopMonitoringIp(serverIp);
261 });
Yi Tseng51301292017-07-28 13:02:59 -0700262 }
263
Yi Tseng51301292017-07-28 13:02:59 -0700264 @Override
Yi Tseng919b2df2017-09-07 16:22:51 -0700265 public List<DhcpServerInfo> getDefaultDhcpServerInfoList() {
266 return defaultServerInfoList;
Yi Tseng51301292017-07-28 13:02:59 -0700267 }
268
269 @Override
Yi Tseng919b2df2017-09-07 16:22:51 -0700270 public List<DhcpServerInfo> getIndirectDhcpServerInfoList() {
271 return indirectServerInfoList;
Yi Tseng51301292017-07-28 13:02:59 -0700272 }
273
274 @Override
Yi Tseng525ff402017-10-23 19:39:39 -0700275 public void updateIgnoreVlanConfig(IgnoreDhcpConfig config) {
276 if (config == null) {
277 ignoredVlans.forEach(((deviceId, vlanId) -> {
278 processIgnoreVlanRule(deviceId, vlanId, REMOVE);
279 }));
280 return;
281 }
282 config.ignoredVlans().forEach((deviceId, vlanId) -> {
283 if (ignoredVlans.get(deviceId).contains(vlanId)) {
284 // don't need to process if it already ignored
285 return;
286 }
287 processIgnoreVlanRule(deviceId, vlanId, ADD);
288 });
Yi Tseng525ff402017-10-23 19:39:39 -0700289 ignoredVlans.forEach((deviceId, vlanId) -> {
290 if (!config.ignoredVlans().get(deviceId).contains(vlanId)) {
291 // not contains in new config, remove it
292 processIgnoreVlanRule(deviceId, vlanId, REMOVE);
293 }
294 });
295 }
296
297 @Override
Saurav Dasb805f1a2017-12-13 16:19:35 -0800298 public void removeIgnoreVlanState(IgnoreDhcpConfig config) {
299 if (config == null) {
300 ignoredVlans.clear();
301 return;
302 }
303 config.ignoredVlans().forEach((deviceId, vlanId) -> {
304 ignoredVlans.remove(deviceId, vlanId);
305 });
306 }
307
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800308 public DhcpRecord getDhcpRelayRecordFor(Ip6Address clientAddress) {
309
310 Collection<DhcpRecord> records = dhcpRelayStore.getDhcpRecords();
311 DhcpRecord dr = null;
312 for (DhcpRecord e:records) {
313 if (e.ip6Address().isPresent()) {
314 if (e.ip6Address().get().equals(clientAddress)) {
315 dr = e;
316 break;
317 }
318 }
319 }
320 return dr;
321 }
322
323 public MacAddress findNextHopMacForIp6FromRelayStore(Ip6Address clientAddress,
324 MacAddress clientMacAddress, VlanId vlanID) {
325
326 DhcpRecord dr = getDhcpRelayRecordFor(clientAddress);
327
328 if (dr != null) {
329 Optional<MacAddress> nextHopTempMac = dr.nextHopTemp();
330 if (nextHopTempMac.isPresent()) {
331 log.info("findNextHopForIp6FromRelayStore " + clientAddress + " got mac " + nextHopTempMac.toString());
332 return nextHopTempMac.get();
333 }
334 } else {
335 log.warn("findNextHopMacForIp6FromRelayStore could NOT find next hop for " + clientAddress);
336 return null;
337 }
338 return null;
339 }
340
341 public Ip6Address findNextHopIp6FromRelayStore(Ip6Address clientAddress) {
342
343 DhcpRecord dr = getDhcpRelayRecordFor(clientAddress);
344 if (dr != null) {
345 Optional<MacAddress> nextHopMac = dr.nextHop();
346 if (nextHopMac.isPresent()) {
347 // find the local ip6 from the host store
348 HostId gwHostId = HostId.hostId(nextHopMac.get(), dr.vlanId());
349 Host gwHost = hostService.getHost(gwHostId);
350 if (gwHost == null) {
351 log.warn("Can't find next hop host ID {}", gwHostId);
352 return null;
353 }
354 Ip6Address nextHopIp = gwHost.ipAddresses()
355 .stream()
356 .filter(IpAddress::isIp6)
357 .filter(IpAddress::isLinkLocal)
358 .map(IpAddress::getIp6Address)
359 .findFirst()
360 .orElse(null);
361
362 log.info("findNextHopIp6FromRelayStore " + clientAddress + " got mac " +
363 nextHopMac.toString() + " ip6 " + nextHopIp);
364 return nextHopIp;
365 }
366 } else {
367 log.warn("findNextHopIp6FromRelayStore could NOT find next hop for " + clientAddress);
368 return null;
369 }
370 return null;
371 }
372
373 private void setPotentialNextHopForIp6InRelayStore(Ip6Address clientAddress,
374 VlanId vlanId, MacAddress nh) {
375 DhcpRecord dr = getDhcpRelayRecordFor(clientAddress);
376 if (dr != null) {
377 dr.nextHopTemp(nh);
378 log.debug("LQ6 potential NH mac " + nh.toString() + " UPDATED in RelayRecord client " + clientAddress);
379 } else {
380 log.warn("LQ6 potential NH mac" + nh.toString() +
381 " NOT FOUND in RelayRecord for client - LQ rejected" + clientAddress);
382 }
383 }
384
385 public void handleLeaseQuery6ReplyMsg(PacketContext context, DHCP6 dhcp6Payload) {
386 ConnectPoint inPort = context.inPacket().receivedFrom();
387 log.info("Got LQV6-REPLY on port {}", inPort);
388 List<Dhcp6Option> lopt = dhcp6Payload.getOptions();
389 log.info("Options list: {}", lopt);
390 // find out if this lease is known is
391 Dhcp6ClientDataOption clientDataOption = dhcp6Payload.getOptions()
392 .stream()
393 .filter(opt -> opt instanceof Dhcp6ClientDataOption)
394 .map(pld -> (Dhcp6ClientDataOption) pld)
395 .findFirst()
396 .orElse(null);
397
398 if (clientDataOption == null) {
399 log.warn("clientDataOption option is not present, " +
400 "lease is UNKNOWN - not adding any new route...");
401 } else {
402 Dhcp6IaAddressOption aiAddressOption = clientDataOption.getOptions()
403 .stream()
404 .filter(opt -> opt instanceof Dhcp6IaAddressOption)
405 .map(pld -> (Dhcp6IaAddressOption) pld)
406 .findFirst()
407 .orElse(null);
408
409 Dhcp6ClientIdOption clientIdOption = clientDataOption.getOptions()
410 .stream()
411 .filter(opt -> opt instanceof Dhcp6ClientIdOption)
412 .map(pld -> (Dhcp6ClientIdOption) pld)
413 .findFirst()
414 .orElse(null);
415
416 if (aiAddressOption == null) {
417 log.warn("clientDataOption from DHCP server does not " +
418 "contains Dhcp6IaAddressOption for the client - giving up...");
419 } else {
420 Ip6Address clientAddress = aiAddressOption.getIp6Address();
421 MacAddress clientMacAddress = MacAddress.valueOf(clientIdOption.getDuid().getLinkLayerAddress());
422 Ethernet packet = context.inPacket().parsed();
423 VlanId vlanId = VlanId.vlanId(packet.getVlanID());
424 MacAddress potentialNextHopMac =
425 findNextHopMacForIp6FromRelayStore(clientAddress, clientMacAddress, vlanId);
426
427 if (potentialNextHopMac == null) {
428 log.warn("Can't find next hop host mac for client {} mac:{}/{}",
429 clientAddress, clientMacAddress, vlanId);
430 return;
431 } else {
432 log.info("Next hop mac for {}/{}/{} is {}", clientAddress,
433 clientMacAddress, vlanId, potentialNextHopMac.toString());
434 }
435 // search the next hop in the hosts store
436 HostId gwHostId = HostId.hostId(potentialNextHopMac, vlanId);
437 Host gwHost = hostService.getHost(gwHostId);
438 if (gwHost == null) {
439 log.warn("Can't find next hop host ID {}", gwHostId);
440 return;
441 }
442 Ip6Address nextHopIp = gwHost.ipAddresses()
443 .stream()
444 .filter(IpAddress::isIp6)
445 .filter(IpAddress::isLinkLocal)
446 .map(IpAddress::getIp6Address)
447 .findFirst()
448 .orElse(null);
449 if (nextHopIp == null) {
450 log.warn("Can't find IP6 address of next hop {}", gwHost);
451 return;
452 }
453 log.info("client " + clientAddress + " is known !");
Kalhee Kim54a97c92018-04-02 21:23:46 +0000454 Route routeForIP6 = new Route(Route.Source.DHCP, clientAddress.toIpPrefix(), nextHopIp);
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800455 log.debug("updating route of Client for indirectly connected.");
456 log.debug("client ip: " + clientAddress + ", next hop ip6: " + nextHopIp);
457 routeStore.updateRoute(routeForIP6);
458 }
459 }
460 }
461
Saurav Dasb805f1a2017-12-13 16:19:35 -0800462 @Override
Kalhee Kim45fede42017-09-05 19:05:06 +0000463 public void processDhcpPacket(PacketContext context, BasePacket payload) {
464 checkNotNull(payload, "DHCP6 payload can't be null");
465 checkState(payload instanceof DHCP6, "Payload is not a DHCP6");
466 DHCP6 dhcp6Payload = (DHCP6) payload;
467 Ethernet receivedPacket = context.inPacket().parsed();
468
469 if (!configured()) {
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800470 log.warn("Missing DHCP6 relay server config. " +
471 "Abort packet processing dhcp6 payload {}", dhcp6Payload);
Kalhee Kim45fede42017-09-05 19:05:06 +0000472 return;
473 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000474 byte msgTypeVal = dhcp6Payload.getMsgType();
475 MsgType msgType = DHCP6.MsgType.getType(msgTypeVal);
476 log.debug("msgType is {}", msgType);
Kalhee Kim45fede42017-09-05 19:05:06 +0000477
Kalhee Kim45fede42017-09-05 19:05:06 +0000478 ConnectPoint inPort = context.inPacket().receivedFrom();
Jonathan Hart8ca2bc02017-11-30 18:23:42 -0800479
Kalhee Kim495c9b22017-11-07 16:32:09 +0000480 if (inPort == null) {
481 log.warn("incomming ConnectPoint is null");
482 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000483 Set<Interface> receivingInterfaces = interfaceService.getInterfacesByPort(inPort);
484 //ignore the packets if dhcp client interface is not configured on onos.
485 if (receivingInterfaces.isEmpty()) {
486 log.warn("Virtual interface is not configured on {}", inPort);
487 return;
488 }
489
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800490 if (msgTypeVal == DHCP6.MsgType.LEASEQUERY.value()) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000491 List<InternalPacket> ethernetClientPacket =
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800492 processLQ6PacketFromClient(context, receivedPacket, receivingInterfaces, dhcp6Payload);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000493 for (InternalPacket internalPacket : ethernetClientPacket) {
494 forwardPacket(internalPacket);
Kalhee Kim45fede42017-09-05 19:05:06 +0000495 }
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800496 } else if (msgTypeVal == DHCP6.MsgType.LEASEQUERY_REPLY.value()) {
497
498 IPv6 clientIpv6 = (IPv6) receivedPacket.getPayload();
499 UDP clientUdp = (UDP) clientIpv6.getPayload();
500 DHCP6 clientDhcp6 = (DHCP6) clientUdp.getPayload();
501 Interface serverInterface = dhcp6HandlerUtil.directlyConnected(clientDhcp6) ?
502 getServerInterface() : getIndirectServerInterface();
Kalhee Kim45fede42017-09-05 19:05:06 +0000503 InternalPacket ethernetPacketReply =
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800504 dhcp6HandlerUtil.processLQ6PacketFromServer(
505 defaultServerInfoList, indirectServerInfoList,
506 serverInterface, interfaceService,
507 hostService,
508 context, receivedPacket, receivingInterfaces);
Kalhee Kim45fede42017-09-05 19:05:06 +0000509 if (ethernetPacketReply != null) {
510 forwardPacket(ethernetPacketReply);
511 }
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800512 handleLeaseQuery6ReplyMsg(context, dhcp6Payload);
Kalhee Kim45fede42017-09-05 19:05:06 +0000513 } else {
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800514 if (MSG_TYPE_FROM_CLIENT.contains(msgTypeVal)) {
515
516 List<InternalPacket> ethernetClientPacket =
517 processDhcp6PacketFromClient(context, receivedPacket, receivingInterfaces);
518 for (InternalPacket internalPacket : ethernetClientPacket) {
519 forwardPacket(internalPacket);
520 }
521 } else if (MSG_TYPE_FROM_SERVER.contains(msgTypeVal)) {
522 log.debug("calling processDhcp6PacketFromServer with RELAY_REPL {}", msgTypeVal);
523 InternalPacket ethernetPacketReply =
524 processDhcp6PacketFromServer(context, receivedPacket, receivingInterfaces);
525 if (ethernetPacketReply != null) {
526 forwardPacket(ethernetPacketReply);
527 }
528 } else {
529 log.warn("Not so fast, packet type {} not supported yet", msgTypeVal);
530 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000531 }
532 }
533
Kalhee Kim45fede42017-09-05 19:05:06 +0000534 /**
535 * Checks if this app has been configured.
536 *
537 * @return true if all information we need have been initialized
538 */
539 public boolean configured() {
Yi Tseng919b2df2017-09-07 16:22:51 -0700540 return !defaultServerInfoList.isEmpty();
Kalhee Kim45fede42017-09-05 19:05:06 +0000541 }
542
Yi Tsengaa417a62017-09-08 17:22:51 -0700543 @Override
544 public ProviderId id() {
Charles Chand988c282017-09-12 17:09:32 -0700545 return PROVIDER_ID;
Yi Tsengaa417a62017-09-08 17:22:51 -0700546 }
547
548 @Override
549 public void triggerProbe(Host host) {
550 // Do nothing here
551 }
552
Kalhee Kim45fede42017-09-05 19:05:06 +0000553 //forward the packet to ConnectPoint where the DHCP server is attached.
554 private void forwardPacket(InternalPacket packet) {
555 //send Packetout to dhcp server connectpoint.
556 if (packet.destLocation != null) {
557 TrafficTreatment t = DefaultTrafficTreatment.builder()
558 .setOutput(packet.destLocation.port()).build();
559 OutboundPacket o = new DefaultOutboundPacket(
560 packet.destLocation.deviceId(), t, ByteBuffer.wrap(packet.packet.serialize()));
561 if (log.isTraceEnabled()) {
562 log.trace("Relaying packet to destination {}", packet.destLocation);
563 }
564 packetService.emit(o);
565 } // if
566 }
567
Kalhee Kim45fede42017-09-05 19:05:06 +0000568 /**
569 * extract from dhcp6 packet client ipv6 address of given by dhcp server.
570 *
571 * @param dhcp6 the dhcp6 packet
Kalhee Kim495c9b22017-11-07 16:32:09 +0000572 * @return IpAddressInfo IpAddressInfo given by dhcp server, or null if not exists
Kalhee Kim45fede42017-09-05 19:05:06 +0000573 */
Kalhee Kim495c9b22017-11-07 16:32:09 +0000574 private IpAddressInfo extractIpAddress(DHCP6 dhcp6) {
575 IpAddressInfo ipInfo = new IpAddressInfo();
Kalhee Kim45fede42017-09-05 19:05:06 +0000576
577 log.debug("extractIpAddress enters dhcp6 {}.", dhcp6);
578 // Extract IPv6 address from IA NA ot IA TA option
579 Optional<Dhcp6IaNaOption> iaNaOption = dhcp6.getOptions()
580 .stream()
581 .filter(opt -> opt instanceof Dhcp6IaNaOption)
582 .map(opt -> (Dhcp6IaNaOption) opt)
583 .findFirst();
584 Optional<Dhcp6IaTaOption> iaTaOption = dhcp6.getOptions()
585 .stream()
586 .filter(opt -> opt instanceof Dhcp6IaTaOption)
587 .map(opt -> (Dhcp6IaTaOption) opt)
588 .findFirst();
589 Optional<Dhcp6IaAddressOption> iaAddressOption;
590 if (iaNaOption.isPresent()) {
591 log.debug("Found IPv6 address from iaNaOption {}", iaNaOption);
592
593 iaAddressOption = iaNaOption.get().getOptions().stream()
594 .filter(opt -> opt instanceof Dhcp6IaAddressOption)
595 .map(opt -> (Dhcp6IaAddressOption) opt)
596 .findFirst();
597 } else if (iaTaOption.isPresent()) {
598 log.debug("Found IPv6 address from iaTaOption {}", iaTaOption);
599
600 iaAddressOption = iaTaOption.get().getOptions().stream()
601 .filter(opt -> opt instanceof Dhcp6IaAddressOption)
602 .map(opt -> (Dhcp6IaAddressOption) opt)
603 .findFirst();
604 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000605 log.info("No IPv6 address found from iaTaOption {}", iaTaOption);
Kalhee Kim45fede42017-09-05 19:05:06 +0000606 iaAddressOption = Optional.empty();
607 }
608 if (iaAddressOption.isPresent()) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000609 ipInfo.ip6Address = iaAddressOption.get().getIp6Address();
610 ipInfo.prefTime = iaAddressOption.get().getPreferredLifetime();
Kalhee Kim45fede42017-09-05 19:05:06 +0000611 log.debug("Found IPv6 address from iaAddressOption {}", iaAddressOption);
Kalhee Kim45fede42017-09-05 19:05:06 +0000612 } else {
613 log.debug("Can't find IPv6 address from DHCPv6 {}", dhcp6);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000614 return null;
Kalhee Kim45fede42017-09-05 19:05:06 +0000615 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000616 return ipInfo;
Kalhee Kim45fede42017-09-05 19:05:06 +0000617 }
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800618
Kalhee Kim45fede42017-09-05 19:05:06 +0000619 /**
620 * extract from dhcp6 packet Prefix prefix provided by dhcp server.
621 *
622 * @param dhcp6 the dhcp6 payload
623 * @return IpPrefix Prefix Delegation prefix, or null if not exists.
624 */
Kalhee Kim495c9b22017-11-07 16:32:09 +0000625 private PdPrefixInfo extractPrefix(DHCP6 dhcp6) {
626 log.debug("extractPrefix enters {}", dhcp6);
Kalhee Kim45fede42017-09-05 19:05:06 +0000627
628 // extract prefix
Kalhee Kim495c9b22017-11-07 16:32:09 +0000629 PdPrefixInfo pdPrefixInfo = new PdPrefixInfo();
Kalhee Kim45fede42017-09-05 19:05:06 +0000630
631 Ip6Address prefixAddress = null;
632
633 // Extract IPv6 prefix from IA PD option
634 Optional<Dhcp6IaPdOption> iaPdOption = dhcp6.getOptions()
635 .stream()
636 .filter(opt -> opt instanceof Dhcp6IaPdOption)
637 .map(opt -> (Dhcp6IaPdOption) opt)
638 .findFirst();
639
640 Optional<Dhcp6IaPrefixOption> iaPrefixOption;
641 if (iaPdOption.isPresent()) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000642 log.debug("IA_PD option found {}", iaPdOption);
Kalhee Kim45fede42017-09-05 19:05:06 +0000643
644 iaPrefixOption = iaPdOption.get().getOptions().stream()
645 .filter(opt -> opt instanceof Dhcp6IaPrefixOption)
646 .map(opt -> (Dhcp6IaPrefixOption) opt)
647 .findFirst();
648 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000649 log.debug("IA_PD option NOT found");
Kalhee Kim45fede42017-09-05 19:05:06 +0000650
651 iaPrefixOption = Optional.empty();
652 }
653 if (iaPrefixOption.isPresent()) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000654 log.debug("IAPrefix Option within IA_PD option found {}", iaPrefixOption);
Kalhee Kim45fede42017-09-05 19:05:06 +0000655
656 prefixAddress = iaPrefixOption.get().getIp6Prefix();
657 int prefixLen = (int) iaPrefixOption.get().getPrefixLength();
Kalhee Kim495c9b22017-11-07 16:32:09 +0000658 log.debug("Prefix length is {} bits", prefixLen);
659 pdPrefixInfo.pdPrefix = IpPrefix.valueOf(prefixAddress, prefixLen);
660 pdPrefixInfo.prefTime = iaPrefixOption.get().getPreferredLifetime();
Kalhee Kim45fede42017-09-05 19:05:06 +0000661 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000662 log.debug("Can't find IPv6 prefix from DHCPv6 {}", dhcp6);
663 return null;
Kalhee Kim45fede42017-09-05 19:05:06 +0000664 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000665 return pdPrefixInfo;
Kalhee Kim45fede42017-09-05 19:05:06 +0000666 }
667
668 /**
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000669 * extract from dhcp6 packet ClientIdOption.
670 *
671 * @param directConnFlag directly connected host
672 * @param dhcp6Payload the dhcp6 payload
673 * @return Dhcp6ClientIdOption clientIdOption, or null if not exists.
674 */
675 private Dhcp6ClientIdOption extractClinedId(Boolean directConnFlag, DHCP6 dhcp6Payload) {
676 Dhcp6ClientIdOption clientIdOption;
677
678 if (directConnFlag) {
679 clientIdOption = dhcp6Payload.getOptions()
680 .stream()
681 .filter(opt -> opt instanceof Dhcp6ClientIdOption)
682 .map(opt -> (Dhcp6ClientIdOption) opt)
683 .findFirst()
684 .orElse(null);
685 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000686 DHCP6 leafDhcp = dhcp6HandlerUtil.getDhcp6Leaf(dhcp6Payload);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000687 clientIdOption = leafDhcp.getOptions()
688 .stream()
689 .filter(opt -> opt instanceof Dhcp6ClientIdOption)
690 .map(opt -> (Dhcp6ClientIdOption) opt)
691 .findFirst()
692 .orElse(null);
693 }
694
695 return clientIdOption;
696 }
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800697
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000698 /**
Kalhee Kim495c9b22017-11-07 16:32:09 +0000699 * remove host or route and update dhcp relay record attributes.
Kalhee Kim45fede42017-09-05 19:05:06 +0000700 *
701 * @param directConnFlag flag to show that packet is from directly connected client
Kalhee Kim495c9b22017-11-07 16:32:09 +0000702 * @param location client side connect point
Kalhee Kim45fede42017-09-05 19:05:06 +0000703 * @param dhcp6Packet the dhcp6 payload
704 * @param clientPacket client's ethernet packet
705 * @param clientIpv6 client's Ipv6 packet
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +0000706 * @param clientInterface client interfaces
Kalhee Kim45fede42017-09-05 19:05:06 +0000707 */
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000708 private void removeHostOrRoute(boolean directConnFlag, ConnectPoint location,
709 DHCP6 dhcp6Packet,
Kalhee Kim45fede42017-09-05 19:05:06 +0000710 Ethernet clientPacket, IPv6 clientIpv6,
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +0000711 Interface clientInterface) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000712 log.debug("removeHostOrRoute enters {}", dhcp6Packet);
Kalhee Kim121ba922017-11-01 17:56:44 +0000713 VlanId vlanId = clientInterface.vlan();
Kalhee Kim495c9b22017-11-07 16:32:09 +0000714 MacAddress srcMac = clientPacket.getSourceMAC(); // could be gw or host
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000715 MacAddress leafClientMac;
716 byte leafMsgType;
Kalhee Kim495c9b22017-11-07 16:32:09 +0000717 log.debug("client mac {} client vlan {}", HexString.toHexString(srcMac.toBytes(), ":"), vlanId);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000718
719 Dhcp6ClientIdOption clientIdOption = extractClinedId(directConnFlag, dhcp6Packet);
720 if (clientIdOption != null) {
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000721 if ((clientIdOption.getDuid().getDuidType() == Dhcp6Duid.DuidType.DUID_LLT) ||
722 (clientIdOption.getDuid().getDuidType() == Dhcp6Duid.DuidType.DUID_LL)) {
723 leafClientMac = MacAddress.valueOf(clientIdOption.getDuid().getLinkLayerAddress());
724 } else {
725 log.warn("Link-Layer Address not supported in CLIENTID option. No DhcpRelay Record created.");
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000726 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_LINKLOCAL_FAIL);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000727 return;
728 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000729 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000730 log.warn("CLIENTID option NOT found. Don't create DhcpRelay Record.");
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000731 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_CLIENTID_FAIL);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000732 return;
733 }
734
Kalhee Kim495c9b22017-11-07 16:32:09 +0000735 HostId leafHostId = HostId.hostId(leafClientMac, vlanId);
736 DhcpRecord record = dhcpRelayStore.getDhcpRecord(leafHostId).orElse(null);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000737 if (record == null) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000738 record = new DhcpRecord(leafHostId);
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000739 } else {
740 record = record.clone();
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000741 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000742
Kalhee Kim495c9b22017-11-07 16:32:09 +0000743 Boolean isMsgRelease = dhcp6HandlerUtil.isDhcp6Release(dhcp6Packet);
744 IpAddressInfo ipInfo;
745 PdPrefixInfo pdInfo = null;
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000746 if (directConnFlag) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000747 // Add to host store if it is connected to network directly
748 ipInfo = extractIpAddress(dhcp6Packet);
749 if (ipInfo != null) {
750 if (isMsgRelease) {
751 HostId hostId = HostId.hostId(srcMac, vlanId);
752 log.debug("remove Host {} ip for directly connected.", hostId.toString());
753 providerService.removeIpFromHost(hostId, ipInfo.ip6Address);
Kalhee Kim45fede42017-09-05 19:05:06 +0000754 }
755 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000756 log.debug("ipAddress not found. Do not remove Host {} for directly connected.",
757 HostId.hostId(srcMac, vlanId).toString());
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000758 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000759 leafMsgType = dhcp6Packet.getMsgType();
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000760 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000761 // Remove from route store if it is not connected to network directly
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000762 // pick out the first link-local ip address
Kalhee Kim495c9b22017-11-07 16:32:09 +0000763 IpAddress nextHopIp = getFirstIpByHost(directConnFlag, srcMac, vlanId);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000764 if (nextHopIp == null) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000765 log.warn("Can't find link-local IP address of gateway mac {} vlanId {}", srcMac, vlanId);
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000766 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_LINKLOCAL_GW);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000767 return;
768 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000769
Kalhee Kim495c9b22017-11-07 16:32:09 +0000770 DHCP6 leafDhcp = dhcp6HandlerUtil.getDhcp6Leaf(dhcp6Packet);
771 ipInfo = extractIpAddress(leafDhcp);
772 if (ipInfo == null) {
773 log.debug("ip is null");
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000774 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000775 if (isMsgRelease) {
Kalhee Kim54a97c92018-04-02 21:23:46 +0000776 Route routeForIP = new Route(Route.Source.DHCP, ipInfo.ip6Address.toIpPrefix(), nextHopIp);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000777 log.debug("removing route of 128 address for indirectly connected.");
778 log.debug("128 ip {}, nexthop {}",
779 HexString.toHexString(ipInfo.ip6Address.toOctets(), ":"),
780 HexString.toHexString(nextHopIp.toOctets(), ":"));
781 routeStore.removeRoute(routeForIP);
Kalhee Kim45fede42017-09-05 19:05:06 +0000782 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000783 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000784
Kalhee Kim495c9b22017-11-07 16:32:09 +0000785 pdInfo = extractPrefix(leafDhcp);
786 if (pdInfo == null) {
787 log.debug("ipPrefix is null ");
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000788 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000789 if (isMsgRelease) {
Kalhee Kim54a97c92018-04-02 21:23:46 +0000790 Route routeForPrefix = new Route(Route.Source.DHCP, pdInfo.pdPrefix, nextHopIp);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000791 log.debug("removing route of PD for indirectly connected.");
792 log.debug("pd ip {}, nexthop {}",
793 HexString.toHexString(pdInfo.pdPrefix.address().toOctets(), ":"),
794 HexString.toHexString(nextHopIp.toOctets(), ":"));
795
796 routeStore.removeRoute(routeForPrefix);
Kalhee Kimba366062017-11-07 16:32:09 +0000797 if (this.dhcpFpmEnabled) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000798 dhcpFpmPrefixStore.removeFpmRecord(pdInfo.pdPrefix);
Kalhee Kimba366062017-11-07 16:32:09 +0000799 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000800 }
801 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000802 leafMsgType = leafDhcp.getMsgType();
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000803 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000804
Kalhee Kim495c9b22017-11-07 16:32:09 +0000805 if (isMsgRelease) {
806 log.debug("DHCP6 RELEASE msg.");
807 if (record != null) {
808 if (ipInfo != null) {
809 log.debug("DhcpRelay Record ip6Address is set to null.");
810 record.ip6Address(null);
811 }
812 if (pdInfo != null) {
813 log.debug("DhcpRelay Record pdPrefix is set to null.");
814 }
815
816 if (!record.ip6Address().isPresent() && !record.pdPrefix().isPresent()) {
817 log.warn("IP6 address and IP6 PD both are null. Remove record.");
818 // do not remove a record. Let timer task handler it.
819 //dhcpRelayStore.removeDhcpRecord(HostId.hostId(leafClientMac, vlanId));
820 }
821 }
822 }
823
Ray Milkeyffe1a332018-01-24 10:41:14 -0800824 if (record != null) {
825 record.getV6Counters().incrementCounter(dhcp6HandlerUtil.getMsgTypeStr(leafMsgType));
826 record.addLocation(new HostLocation(location, System.currentTimeMillis()));
827 record.ip6Status(DHCP6.MsgType.getType(leafMsgType));
828 record.setDirectlyConnected(directConnFlag);
829 if (!directConnFlag) {
830 // Update gateway mac address if the host is not directly connected
831 record.nextHop(srcMac);
832 }
833 record.updateLastSeen();
Kalhee Kim495c9b22017-11-07 16:32:09 +0000834 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000835 dhcpRelayStore.updateDhcpRecord(leafHostId, record);
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000836 // TODO Use AtomicInteger for the counters
837 try {
838 recordSemaphore.acquire();
839 try {
840 dhcpRelayCountersStore.incrementCounter(gCount, dhcp6HandlerUtil.getMsgTypeStr(leafMsgType));
841 } finally {
842 // calling release() after a successful acquire()
843 recordSemaphore.release();
844 }
845 } catch (InterruptedException e) {
Ray Milkeyba547f92018-02-01 15:22:31 -0800846 Thread.currentThread().interrupt();
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000847 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000848 }
849
850 /**
851 * add host or route and update dhcp relay record.
852 *
853 * @param directConnFlag flag to show that packet is from directly connected client
854 * @param location client side connect point
855 * @param dhcp6Relay the dhcp6 payload
856 * @param embeddedDhcp6 the dhcp6 payload within relay
857 * @param srcMac client gw/host macAddress
858 * @param clientInterface client interface
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800859 * @param dhcpServerIp6Address DHCP server IP
Kalhee Kim495c9b22017-11-07 16:32:09 +0000860 */
861 private void addHostOrRoute(boolean directConnFlag, ConnectPoint location, DHCP6 dhcp6Relay,
862 DHCP6 embeddedDhcp6, MacAddress srcMac, Interface clientInterface) {
863 log.debug("addHostOrRoute entered.");
864 VlanId vlanId = clientInterface.vlan();
865 Boolean isMsgReply = dhcp6HandlerUtil.isDhcp6Reply(dhcp6Relay);
866 MacAddress leafClientMac;
867 Byte leafMsgType;
868
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000869 Dhcp6ClientIdOption clientIdOption = extractClinedId(directConnFlag, embeddedDhcp6);
870 if (clientIdOption != null) {
871 log.debug("CLIENTID option found {}", clientIdOption);
872 if ((clientIdOption.getDuid().getDuidType() == Dhcp6Duid.DuidType.DUID_LLT) ||
873 (clientIdOption.getDuid().getDuidType() == Dhcp6Duid.DuidType.DUID_LL)) {
874 leafClientMac = MacAddress.valueOf(clientIdOption.getDuid().getLinkLayerAddress());
875 } else {
876 log.warn("Link-Layer Address not supported in CLIENTID option. No DhcpRelay Record created.");
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000877 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_LINKLOCAL_FAIL);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000878 return;
879 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000880 } else {
881 log.warn("CLIENTID option NOT found. No DhcpRelay Record created.");
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000882 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_CLIENTID_FAIL);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000883 return;
884 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000885 HostId leafHostId = HostId.hostId(leafClientMac, vlanId);
886 DhcpRecord record = dhcpRelayStore.getDhcpRecord(leafHostId).orElse(null);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000887 if (record == null) {
888 record = new DhcpRecord(HostId.hostId(leafClientMac, vlanId));
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000889 } else {
890 record = record.clone();
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000891 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000892
893 IpAddressInfo ipInfo;
894 PdPrefixInfo pdInfo = null;
895 if (directConnFlag) {
896 // Add to host store if it connect to network directly
897 ipInfo = extractIpAddress(embeddedDhcp6);
898 if (ipInfo != null) {
899 if (isMsgReply) {
900 Set<IpAddress> ips = Sets.newHashSet(ipInfo.ip6Address);
901 HostId hostId = HostId.hostId(srcMac, vlanId);
902 Host host = hostService.getHost(hostId);
903 HostLocation hostLocation = new HostLocation(clientInterface.connectPoint(),
904 System.currentTimeMillis());
905 Set<HostLocation> hostLocations = Sets.newHashSet(hostLocation);
906 if (host != null) {
907 // Dual homing support:
908 // if host exists, use old locations and new location
909 hostLocations.addAll(host.locations());
910 }
911 HostDescription desc = new DefaultHostDescription(srcMac, vlanId, hostLocations, ips,
912 false);
913 log.debug("adding Host for directly connected.");
914 log.debug("client mac {} client vlan {} hostlocation {}",
915 HexString.toHexString(srcMac.toBytes(), ":"), vlanId, hostLocation.toString());
916 // Replace the ip when dhcp server give the host new ip address
917 providerService.hostDetected(hostId, desc, false);
918 }
919 } else {
920 log.warn("ipAddress not found. Do not add Host {} for directly connected.",
921 HostId.hostId(srcMac, vlanId).toString());
922 }
923 leafMsgType = embeddedDhcp6.getMsgType();
924 } else {
925 // Add to route store if it does not connect to network directly
926 // pick out the first link-local ip address
927 IpAddress nextHopIp = getFirstIpByHost(directConnFlag, srcMac, vlanId);
928 if (nextHopIp == null) {
929 log.warn("Can't find link-local IP address of gateway mac {} vlanId {}", srcMac, vlanId);
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000930 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_LINKLOCAL_GW);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000931 return;
932 }
933
934 DHCP6 leafDhcp = dhcp6HandlerUtil.getDhcp6Leaf(embeddedDhcp6);
935 ipInfo = extractIpAddress(leafDhcp);
936 if (ipInfo == null) {
937 log.debug("ip is null");
938 } else {
939 if (isMsgReply) {
Kalhee Kim54a97c92018-04-02 21:23:46 +0000940 Route routeForIP = new Route(Route.Source.DHCP, ipInfo.ip6Address.toIpPrefix(), nextHopIp);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000941 log.debug("adding Route of 128 address for indirectly connected.");
942 routeStore.updateRoute(routeForIP);
943 }
944 }
945
946 pdInfo = extractPrefix(leafDhcp);
947 if (pdInfo == null) {
948 log.debug("ipPrefix is null ");
949 } else {
950 if (isMsgReply) {
Kalhee Kim54a97c92018-04-02 21:23:46 +0000951 Route routeForPrefix = new Route(Route.Source.DHCP, pdInfo.pdPrefix, nextHopIp);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000952 log.debug("adding Route of PD for indirectly connected.");
953 routeStore.updateRoute(routeForPrefix);
954 if (this.dhcpFpmEnabled) {
955 FpmRecord fpmRecord = new FpmRecord(pdInfo.pdPrefix, nextHopIp, FpmRecord.Type.DHCP_RELAY);
956 dhcpFpmPrefixStore.addFpmRecord(pdInfo.pdPrefix, fpmRecord);
957 }
958 }
959 }
960 leafMsgType = leafDhcp.getMsgType();
961 }
962 if (leafMsgType == DHCP6.MsgType.RELEASE.value() ||
963 (leafMsgType == DHCP6.MsgType.REPLY.value()) && ipInfo == null) {
964 log.warn("DHCP6 RELEASE/REPLY(null ip) from Server. MsgType {}", leafMsgType);
965 //return;
966 }
967
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000968 record.addLocation(new HostLocation(location, System.currentTimeMillis()));
Kalhee Kim2f07c602017-11-15 18:57:53 +0000969
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000970 if (leafMsgType == DHCP6.MsgType.REPLY.value()) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000971 if (ipInfo != null) {
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000972 log.debug("IP6 address is being stored into dhcp-relay store.");
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800973 log.debug("Client IP6 address {}", HexString.toHexString(ipInfo.ip6Address.toOctets(), ":"));
Kalhee Kim495c9b22017-11-07 16:32:09 +0000974 record.ip6Address(ipInfo.ip6Address);
Kalhee Kim2f07c602017-11-15 18:57:53 +0000975 record.updateAddrPrefTime(ipInfo.prefTime);
976 record.updateLastIp6Update();
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800977 } else {
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000978 log.debug("IP6 address is not returned from server. Maybe only PD is returned.");
979 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000980 if (pdInfo != null) {
981 log.debug("IP6 PD address {}",
982 HexString.toHexString(pdInfo.pdPrefix.address().toOctets(), ":"));
Kalhee Kim2f07c602017-11-15 18:57:53 +0000983 record.pdPrefix(pdInfo.pdPrefix);
984 record.updatePdPrefTime(pdInfo.prefTime);
985 record.updateLastPdUpdate();
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000986 } else {
987 log.debug("IP6 PD address is not returned from server. Maybe only IPAddress is returned.");
988 }
989 }
Kalhee Kim2f07c602017-11-15 18:57:53 +0000990
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000991 record.getV6Counters().incrementCounter(dhcp6HandlerUtil.getMsgTypeStr(leafMsgType));
Kalhee Kim495c9b22017-11-07 16:32:09 +0000992 record.ip6Status(DHCP6.MsgType.getType(leafMsgType));
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000993 record.setDirectlyConnected(directConnFlag);
994 record.updateLastSeen();
Kalhee Kim495c9b22017-11-07 16:32:09 +0000995 dhcpRelayStore.updateDhcpRecord(leafHostId, record);
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000996 // TODO Use AtomicInteger for the counters
997 try {
998 recordSemaphore.acquire();
999 try {
1000 dhcpRelayCountersStore.incrementCounter(gCount, dhcp6HandlerUtil.getMsgTypeStr(leafMsgType));
1001 } finally {
1002 // calling release() after a successful acquire()
1003 recordSemaphore.release();
1004 }
1005 } catch (InterruptedException e) {
Ray Milkeyba547f92018-02-01 15:22:31 -08001006 Thread.currentThread().interrupt();
Kalhee Kimd94ceea2017-11-29 19:03:02 +00001007 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001008 }
1009
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001010 private List<InternalPacket> processLQ6PacketFromClient(PacketContext context,
1011 Ethernet clientPacket,
1012 Set<Interface> clientInterfaces,
1013 DHCP6 dhcp6Payload) {
1014 ConnectPoint inPort = context.inPacket().receivedFrom();
1015 log.info("Got LQ-REQUEST V6 on port {}", inPort);
1016 List<Dhcp6Option> lopt = dhcp6Payload.getOptions();
1017 log.info("Options list: {}", lopt);
1018 Dhcp6LeaseQueryOption lqoption = dhcp6Payload.getOptions()
1019 .stream()
1020 .filter(opt -> opt instanceof Dhcp6LeaseQueryOption)
1021 .map(pld -> (Dhcp6LeaseQueryOption) pld)
1022 .findFirst()
1023 .orElse(null);
1024
1025 if (lqoption == null) {
1026 // Can't find dhcp payload
1027 log.warn("Can't find dhcp6 lease query message - aborting");
1028 return null;
1029 } else {
1030 log.info("dhcp6 lqv6 options found: {}", lqoption);
1031 }
1032 log.warn("LQv6 for " + lqoption.linkAddress.toString() + " comes from " + inPort.toString());
1033 Ethernet packet = context.inPacket().parsed();
1034 Ip6Address clientAddress = lqoption.linkAddress;
1035 IPv6 ipv6Packet = (IPv6) packet.getPayload();
1036 Ip6Address nextHopIp = findNextHopIp6FromRelayStore(clientAddress);
1037
1038 // 1. only if there is a route to remove - remove it
1039 if (nextHopIp != null) {
Kalhee Kim54a97c92018-04-02 21:23:46 +00001040 Route routeForIP6 = new Route(Route.Source.DHCP, clientAddress.toIpPrefix(), nextHopIp);
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001041 log.debug("Removing route of Client " + clientAddress +
1042 " for indirectly connected - next hop ip6 " + nextHopIp);
1043 routeStore.removeRoute(routeForIP6);
1044 }
1045
1046 // 2. note the potential NH this packet came from in case it's a known lease
1047 // this NH will then be used to build the route
1048 MacAddress potentialNH = packet.getSourceMAC();
1049 VlanId vlanId = VlanId.vlanId(packet.getVlanID());
1050 setPotentialNextHopForIp6InRelayStore(clientAddress, vlanId, potentialNH);
1051
1052 // 3. route this LQ6 to all relevant servers
1053 IPv6 clientIpv6 = (IPv6) clientPacket.getPayload();
1054 UDP clientUdp = (UDP) clientIpv6.getPayload();
1055 DHCP6 clientDhcp6 = (DHCP6) clientUdp.getPayload();
1056
1057 boolean directConnFlag = dhcp6HandlerUtil.directlyConnected(clientDhcp6);
1058
1059 ConnectPoint clientConnectionPoint = context.inPacket().receivedFrom();
1060 VlanId vlanIdInUse = VlanId.vlanId(clientPacket.getVlanID());
1061 Interface clientInterface = interfaceService.getInterfacesByPort(clientConnectionPoint)
1062 .stream().filter(iface -> dhcp6HandlerUtil.interfaceContainsVlan(iface, vlanIdInUse))
1063 .findFirst()
1064 .orElse(null);
1065
1066 List<InternalPacket> internalPackets = new ArrayList<>();
1067 List<DhcpServerInfo> serverInfoList = findValidServerInfo(directConnFlag);
1068 List<DhcpServerInfo> copyServerInfoList = new ArrayList<DhcpServerInfo>(serverInfoList);
1069
1070 for (DhcpServerInfo serverInfo : copyServerInfoList) {
1071 if (!dhcp6HandlerUtil.checkDhcpServerConnPt(directConnFlag, serverInfo)) {
1072 log.warn("Can't get server connect point, ignore");
1073 continue;
1074 }
1075 DhcpServerInfo newServerInfo = getHostInfoForServerInfo(serverInfo, serverInfoList);
1076 if (newServerInfo == null) {
1077 log.warn("Can't get server interface with host info resolved, ignore");
1078 continue;
1079 }
1080
1081 Interface serverInterface = getServerInterface(newServerInfo);
1082 if (serverInterface == null) {
1083 log.warn("Can't get server interface, ignore");
1084 continue;
1085 }
1086
1087
1088 Ethernet etherRouted = (Ethernet) clientPacket.clone();
1089 MacAddress macFacingServer = serverInterface.mac();
1090 if (macFacingServer == null) {
1091 log.warn("No MAC address for server Interface {}", serverInterface);
1092 return null;
1093 }
1094 etherRouted.setSourceMACAddress(macFacingServer);
1095 etherRouted.setDestinationMACAddress(newServerInfo.getDhcpConnectMac().get());
1096 InternalPacket internalPacket =
1097 new Dhcp6HandlerUtil().new InternalPacket(etherRouted,
1098 serverInfo.getDhcpServerConnectPoint().get());
1099 internalPackets.add(internalPacket);
1100 log.debug("Sending LQ to DHCP server {}", newServerInfo.getDhcpServerIp6());
1101 }
1102 log.debug("num of client packets to send is{}", internalPackets.size());
1103
1104 return internalPackets;
1105 }
1106
Kalhee Kim45fede42017-09-05 19:05:06 +00001107 /**
Kalhee Kim495c9b22017-11-07 16:32:09 +00001108 * build the DHCP6 solicit/request packet with gatewayip.
Kalhee Kim45fede42017-09-05 19:05:06 +00001109 *
1110 * @param context packet context
1111 * @param clientPacket client ethernet packet
1112 * @param clientInterfaces set of client side interfaces
1113 */
Kalhee Kim495c9b22017-11-07 16:32:09 +00001114 private List<InternalPacket> processDhcp6PacketFromClient(PacketContext context,
1115 Ethernet clientPacket,
1116 Set<Interface> clientInterfaces) {
Yi Tseng3bd57ac2017-11-29 14:39:18 -08001117 ConnectPoint receivedFrom = context.inPacket().receivedFrom();
1118 DeviceId receivedFromDevice = receivedFrom.deviceId();
Kalhee Kim495c9b22017-11-07 16:32:09 +00001119 Ip6Address relayAgentIp = dhcp6HandlerUtil.getRelayAgentIPv6Address(clientInterfaces);
Yi Tseng3bd57ac2017-11-29 14:39:18 -08001120 MacAddress relayAgentMac = clientInterfaces.iterator().next().mac();
1121 if (relayAgentIp == null || relayAgentMac == null) {
1122 log.warn("Missing DHCP relay agent interface Ipv6 addr config for "
Kalhee Kim495c9b22017-11-07 16:32:09 +00001123 + "packet from client on port: {}. Aborting packet processing",
1124 clientInterfaces.iterator().next().connectPoint());
Kalhee Kimd94ceea2017-11-29 19:03:02 +00001125 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_CLIENT_INTF_MAC);
Charles Chan239492c2018-01-22 13:27:28 -08001126 return Lists.newArrayList();
Kalhee Kim121ba922017-11-01 17:56:44 +00001127 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001128
Kalhee Kim495c9b22017-11-07 16:32:09 +00001129 IPv6 clientIpv6 = (IPv6) clientPacket.getPayload();
1130 UDP clientUdp = (UDP) clientIpv6.getPayload();
1131 DHCP6 clientDhcp6 = (DHCP6) clientUdp.getPayload();
1132
1133 boolean directConnFlag = dhcp6HandlerUtil.directlyConnected(clientDhcp6);
Charles Chana990ce92017-10-30 10:22:50 -07001134
Kalhee Kim121ba922017-11-01 17:56:44 +00001135 ConnectPoint clientConnectionPoint = context.inPacket().receivedFrom();
1136 VlanId vlanIdInUse = VlanId.vlanId(clientPacket.getVlanID());
1137 Interface clientInterface = interfaceService.getInterfacesByPort(clientConnectionPoint)
Kalhee Kim495c9b22017-11-07 16:32:09 +00001138 .stream().filter(iface -> dhcp6HandlerUtil.interfaceContainsVlan(iface, vlanIdInUse))
1139 .findFirst()
1140 .orElse(null);
Kalhee Kim45fede42017-09-05 19:05:06 +00001141
Kalhee Kim495c9b22017-11-07 16:32:09 +00001142 List<InternalPacket> internalPackets = new ArrayList<>();
1143 List<DhcpServerInfo> serverInfoList = findValidServerInfo(directConnFlag);
1144 List<DhcpServerInfo> copyServerInfoList = new ArrayList<DhcpServerInfo>(serverInfoList);
Kalhee Kim45b24182017-10-18 18:30:23 +00001145
Kalhee Kim495c9b22017-11-07 16:32:09 +00001146 for (DhcpServerInfo serverInfo : copyServerInfoList) {
1147 if (!dhcp6HandlerUtil.checkDhcpServerConnPt(directConnFlag, serverInfo)) {
1148 log.warn("Can't get server connect point, ignore");
1149 continue;
1150 }
1151 DhcpServerInfo newServerInfo = getHostInfoForServerInfo(serverInfo, serverInfoList);
1152 if (newServerInfo == null) {
1153 log.warn("Can't get server interface with host info resolved, ignore");
1154 continue;
1155 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001156
Kalhee Kim495c9b22017-11-07 16:32:09 +00001157 Interface serverInterface = getServerInterface(newServerInfo);
1158 if (serverInterface == null) {
1159 log.warn("Can't get server interface, ignore");
1160 continue;
1161 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +00001162
Kalhee Kim495c9b22017-11-07 16:32:09 +00001163 Ethernet etherReply = dhcp6HandlerUtil.buildDhcp6PacketFromClient(context, clientPacket,
1164 clientInterfaces, newServerInfo, serverInterface);
1165 removeHostOrRoute(directConnFlag, clientConnectionPoint, clientDhcp6, clientPacket,
1166 clientIpv6, clientInterface);
Kalhee Kimea4b6c22017-11-09 14:38:37 +00001167
Kalhee Kim495c9b22017-11-07 16:32:09 +00001168 InternalPacket internalPacket = new Dhcp6HandlerUtil().new InternalPacket(etherReply,
1169 serverInfo.getDhcpServerConnectPoint().get());
1170 internalPackets.add(internalPacket);
1171 }
1172 log.debug("num of client packets to send is{}", internalPackets.size());
Kalhee Kimea4b6c22017-11-09 14:38:37 +00001173
Kalhee Kim495c9b22017-11-07 16:32:09 +00001174 return internalPackets;
1175 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001176
1177 /**
Kalhee Kim45fede42017-09-05 19:05:06 +00001178 * process the DHCP6 relay-reply packet from dhcp server.
1179 *
1180 * @param context packet context
1181 * @param receivedPacket server ethernet packet
1182 * @param recevingInterfaces set of server side interfaces
Kalhee Kim495c9b22017-11-07 16:32:09 +00001183 * @return internalPacket toward client
Kalhee Kim45fede42017-09-05 19:05:06 +00001184 */
1185 private InternalPacket processDhcp6PacketFromServer(PacketContext context,
1186 Ethernet receivedPacket, Set<Interface> recevingInterfaces) {
Kalhee Kim45fede42017-09-05 19:05:06 +00001187 // get dhcp6 header.
Ray Milkeyf0c47612017-09-28 11:29:38 -07001188 Ethernet etherReply = receivedPacket.duplicate();
Kalhee Kim45fede42017-09-05 19:05:06 +00001189 IPv6 ipv6Packet = (IPv6) etherReply.getPayload();
1190 UDP udpPacket = (UDP) ipv6Packet.getPayload();
1191 DHCP6 dhcp6Relay = (DHCP6) udpPacket.getPayload();
Kalhee Kim495c9b22017-11-07 16:32:09 +00001192 Boolean directConnFlag = dhcp6HandlerUtil.directlyConnected(dhcp6Relay);
Kalhee Kim45fede42017-09-05 19:05:06 +00001193
Kalhee Kim495c9b22017-11-07 16:32:09 +00001194 DHCP6 embeddedDhcp6 = dhcp6Relay.getOptions().stream()
1195 .filter(opt -> opt instanceof Dhcp6RelayOption)
1196 .map(BasePacket::getPayload)
1197 .map(pld -> (DHCP6) pld)
1198 .findFirst()
1199 .orElse(null);
Kalhee Kimd21029f2017-09-26 20:21:53 +00001200 ConnectPoint inPort = context.inPacket().receivedFrom();
Kalhee Kim495c9b22017-11-07 16:32:09 +00001201 DhcpServerInfo foundServerInfo = findServerInfoFromServer(directConnFlag, inPort);
Kalhee Kimd21029f2017-09-26 20:21:53 +00001202
Kalhee Kim495c9b22017-11-07 16:32:09 +00001203 if (foundServerInfo == null) {
1204 log.warn("Cannot find server info");
Kalhee Kimd94ceea2017-11-29 19:03:02 +00001205 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_SERVER_INFO);
Kalhee Kimd21029f2017-09-26 20:21:53 +00001206 return null;
Kalhee Kim495c9b22017-11-07 16:32:09 +00001207 } else {
1208 if (dhcp6HandlerUtil.isServerIpEmpty(foundServerInfo)) {
1209 log.warn("Cannot find server info's ipaddress");
Kalhee Kimd94ceea2017-11-29 19:03:02 +00001210 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_SERVER_IP6ADDR);
Kalhee Kim495c9b22017-11-07 16:32:09 +00001211 return null;
1212 }
Kalhee Kimd21029f2017-09-26 20:21:53 +00001213 }
1214
Kalhee Kim45fede42017-09-05 19:05:06 +00001215 Dhcp6InterfaceIdOption interfaceIdOption = dhcp6Relay.getOptions().stream()
1216 .filter(opt -> opt instanceof Dhcp6InterfaceIdOption)
1217 .map(opt -> (Dhcp6InterfaceIdOption) opt)
1218 .findFirst()
1219 .orElse(null);
Kalhee Kim45fede42017-09-05 19:05:06 +00001220 if (interfaceIdOption == null) {
1221 log.warn("Interface Id option is not present, abort packet...");
Kalhee Kimd94ceea2017-11-29 19:03:02 +00001222 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.OPTION_MISSING_FAIL);
Kalhee Kim45fede42017-09-05 19:05:06 +00001223 return null;
1224 }
1225
1226 MacAddress peerMac = interfaceIdOption.getMacAddress();
1227 String clientConnectionPointStr = new String(interfaceIdOption.getInPort());
Kalhee Kim45fede42017-09-05 19:05:06 +00001228 ConnectPoint clientConnectionPoint = ConnectPoint.deviceConnectPoint(clientConnectionPointStr);
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +00001229 VlanId vlanIdInUse = VlanId.vlanId(interfaceIdOption.getVlanId());
1230 Interface clientInterface = interfaceService.getInterfacesByPort(clientConnectionPoint)
Kalhee Kim495c9b22017-11-07 16:32:09 +00001231 .stream().filter(iface -> dhcp6HandlerUtil.interfaceContainsVlan(iface, vlanIdInUse))
1232 .findFirst().orElse(null);
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +00001233 if (clientInterface == null) {
1234 log.warn("Cannot get client interface for from packet, abort... vlan {}", vlanIdInUse.toString());
Kalhee Kimd94ceea2017-11-29 19:03:02 +00001235 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_MATCHING_INTF);
Kalhee Kim45fede42017-09-05 19:05:06 +00001236 return null;
1237 }
Kalhee Kim44fbda12018-04-03 21:08:18 +00001238 etherReply.setVlanID(vlanIdInUse.toShort());
1239
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +00001240 MacAddress relayAgentMac = clientInterface.mac();
Kalhee Kim45fede42017-09-05 19:05:06 +00001241 if (relayAgentMac == null) {
Yi Tseng68ef26b2017-12-18 17:10:00 -08001242 log.warn("Can not get client interface mac, abort packet..");
Kalhee Kimd94ceea2017-11-29 19:03:02 +00001243 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_CLIENT_INTF_MAC);
Kalhee Kim45fede42017-09-05 19:05:06 +00001244 return null;
1245 }
1246 etherReply.setSourceMACAddress(relayAgentMac);
1247
1248 // find destMac
Yi Tseng68ef26b2017-12-18 17:10:00 -08001249 MacAddress clientMac;
Yi Tsengaa417a62017-09-08 17:22:51 -07001250 Ip6Address peerAddress = Ip6Address.valueOf(dhcp6Relay.getPeerAddress());
1251 Set<Host> clients = hostService.getHostsByIp(peerAddress);
Kalhee Kim45fede42017-09-05 19:05:06 +00001252 if (clients.isEmpty()) {
Yi Tseng68ef26b2017-12-18 17:10:00 -08001253 log.trace("There's no host found for this address {}",
Kalhee Kim45fede42017-09-05 19:05:06 +00001254 HexString.toHexString(dhcp6Relay.getPeerAddress(), ":"));
Yi Tseng68ef26b2017-12-18 17:10:00 -08001255 log.trace("Let's look up interfaceId {}", HexString.toHexString(peerMac.toBytes(), ":"));
Kalhee Kim45fede42017-09-05 19:05:06 +00001256 clientMac = peerMac;
1257 } else {
1258 clientMac = clients.iterator().next().mac();
1259 if (clientMac == null) {
1260 log.warn("No client mac address found, abort packet...");
Kalhee Kimd94ceea2017-11-29 19:03:02 +00001261 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_CLIENT_INTF_MAC);
Kalhee Kim45fede42017-09-05 19:05:06 +00001262 return null;
1263 }
Yi Tseng68ef26b2017-12-18 17:10:00 -08001264 log.trace("Client mac address found from getHostByIp");
Kalhee Kim45fede42017-09-05 19:05:06 +00001265 }
1266 etherReply.setDestinationMACAddress(clientMac);
Kalhee Kim45fede42017-09-05 19:05:06 +00001267 // ip header
1268 ipv6Packet.setSourceAddress(dhcp6Relay.getLinkAddress());
1269 ipv6Packet.setDestinationAddress(dhcp6Relay.getPeerAddress());
1270 // udp header
1271 udpPacket.setSourcePort(UDP.DHCP_V6_SERVER_PORT);
1272 if (directConnFlag) {
1273 udpPacket.setDestinationPort(UDP.DHCP_V6_CLIENT_PORT);
1274 } else {
1275 udpPacket.setDestinationPort(UDP.DHCP_V6_SERVER_PORT);
1276 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001277 // add host or route
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001278 addHostOrRoute(directConnFlag, clientConnectionPoint, dhcp6Relay, embeddedDhcp6,
1279 clientMac, clientInterface);
1280
Kalhee Kim45fede42017-09-05 19:05:06 +00001281
1282 udpPacket.setPayload(embeddedDhcp6);
1283 udpPacket.resetChecksum();
1284 ipv6Packet.setPayload(udpPacket);
1285 etherReply.setPayload(ipv6Packet);
Kalhee Kim495c9b22017-11-07 16:32:09 +00001286 return new Dhcp6HandlerUtil().new InternalPacket(etherReply, clientConnectionPoint);
Kalhee Kim45fede42017-09-05 19:05:06 +00001287 }
1288
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001289 // Returns the first v6 interface ip out of a set of interfaces or null.
1290 // Checks all interfaces, and ignores v6 interface ips
1291 private Ip6Address getRelayAgentIPv6Address(Set<Interface> intfs) {
1292 for (Interface intf : intfs) {
1293 for (InterfaceIpAddress ip : intf.ipAddressesList()) {
1294 Ip6Address relayAgentIp = ip.ipAddress().getIp6Address();
1295 if (relayAgentIp != null) {
1296 return relayAgentIp;
1297 }
1298 }
1299 }
1300 return null;
1301 }
Yi Tseng483ac6f2017-08-02 15:03:31 -07001302
1303 @Override
Kalhee Kimba366062017-11-07 16:32:09 +00001304 public void setDhcpFpmEnabled(Boolean enabled) {
1305 dhcpFpmEnabled = enabled;
1306 }
1307
1308 @Override
Yi Tseng483ac6f2017-08-02 15:03:31 -07001309 public void setDefaultDhcpServerConfigs(Collection<DhcpServerConfig> configs) {
Kalhee Kim495c9b22017-11-07 16:32:09 +00001310 log.debug("setDefaultDhcpServerConfigs is called.");
Yi Tseng919b2df2017-09-07 16:22:51 -07001311 setDhcpServerConfigs(configs, defaultServerInfoList);
Yi Tseng919b2df2017-09-07 16:22:51 -07001312 }
1313
1314 @Override
1315 public void setIndirectDhcpServerConfigs(Collection<DhcpServerConfig> configs) {
Kalhee Kim495c9b22017-11-07 16:32:09 +00001316 log.debug("setIndirectDhcpServerConfigs is called.");
Yi Tseng919b2df2017-09-07 16:22:51 -07001317 setDhcpServerConfigs(configs, indirectServerInfoList);
Yi Tseng919b2df2017-09-07 16:22:51 -07001318 }
1319
1320 public void setDhcpServerConfigs(Collection<DhcpServerConfig> configs, List<DhcpServerInfo> serverInfoList) {
Kalhee Kim495c9b22017-11-07 16:32:09 +00001321 log.debug("config size {}.", configs.size());
1322
Kalhee Kim45fede42017-09-05 19:05:06 +00001323 if (configs.size() == 0) {
1324 // no config to update
1325 return;
1326 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001327 // TODO: currently we pick up first DHCP server config.
1328 // Will use other server configs in the future for HA.
Kalhee Kim495c9b22017-11-07 16:32:09 +00001329 Boolean isConfigValid = false;
1330 for (DhcpServerConfig serverConfig : configs) {
1331 if (serverConfig.getDhcpServerIp6().isPresent()) {
1332 isConfigValid = true;
1333 break;
1334 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001335 }
Kalhee Kim495c9b22017-11-07 16:32:09 +00001336 if (!isConfigValid) {
1337 log.warn("No IP V6 server address found.");
1338 return; // No IP V6 address found
1339 }
1340 for (DhcpServerInfo oldServerInfo : serverInfoList) {
Yi Tseng919b2df2017-09-07 16:22:51 -07001341 // stop monitoring gateway or server
1342 oldServerInfo.getDhcpGatewayIp6().ifPresent(gatewayIp -> {
1343 hostService.stopMonitoringIp(gatewayIp);
1344 });
1345 oldServerInfo.getDhcpServerIp6().ifPresent(serverIp -> {
1346 hostService.stopMonitoringIp(serverIp);
Yi Tseng525ff402017-10-23 19:39:39 -07001347 cancelDhcpPacket(serverIp);
Yi Tseng919b2df2017-09-07 16:22:51 -07001348 });
1349 }
Kalhee Kim495c9b22017-11-07 16:32:09 +00001350 serverInfoList.clear();
1351 for (DhcpServerConfig serverConfig : configs) {
1352 // Create new server info according to the config
1353 DhcpServerInfo newServerInfo = new DhcpServerInfo(serverConfig,
1354 DhcpServerInfo.Version.DHCP_V6);
1355 checkState(newServerInfo.getDhcpServerConnectPoint().isPresent(),
1356 "Connect point not exists");
1357 checkState(newServerInfo.getDhcpServerIp6().isPresent(),
1358 "IP of DHCP server not exists");
Yi Tseng919b2df2017-09-07 16:22:51 -07001359
Kalhee Kim495c9b22017-11-07 16:32:09 +00001360 log.debug("DHCP server connect point: {}", newServerInfo.getDhcpServerConnectPoint().orElse(null));
1361 log.debug("DHCP server IP: {}", newServerInfo.getDhcpServerIp6().orElse(null));
Yi Tseng919b2df2017-09-07 16:22:51 -07001362
Kalhee Kim495c9b22017-11-07 16:32:09 +00001363 Ip6Address serverIp = newServerInfo.getDhcpServerIp6().get();
1364 Ip6Address ipToProbe;
1365 if (newServerInfo.getDhcpGatewayIp6().isPresent()) {
1366 ipToProbe = newServerInfo.getDhcpGatewayIp6().get();
1367 } else {
1368 ipToProbe = newServerInfo.getDhcpServerIp6().orElse(null);
1369 }
1370 String hostToProbe = newServerInfo.getDhcpGatewayIp6()
1371 .map(ip -> "gateway").orElse("server");
Yi Tseng919b2df2017-09-07 16:22:51 -07001372
Kalhee Kim495c9b22017-11-07 16:32:09 +00001373 log.warn("Probing to resolve {} IP {}", hostToProbe, ipToProbe);
1374 hostService.startMonitoringIp(ipToProbe);
1375
1376 Set<Host> hosts = hostService.getHostsByIp(ipToProbe);
1377 if (!hosts.isEmpty()) {
1378 Host host = hosts.iterator().next();
1379 newServerInfo.setDhcpConnectVlan(host.vlan());
1380 newServerInfo.setDhcpConnectMac(host.mac());
1381 log.warn("Host found host {}", host);
1382
1383 } else {
1384 log.warn("No host found host ip {}", ipToProbe);
1385 }
1386 // Add new server info
1387 synchronized (this) {
1388 serverInfoList.add(newServerInfo);
1389 }
1390 if (!hosts.isEmpty()) {
1391 requestDhcpPacket(serverIp);
1392 }
Yi Tseng919b2df2017-09-07 16:22:51 -07001393 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001394 }
1395
1396 class InternalHostListener implements HostListener {
1397 @Override
1398 public void event(HostEvent event) {
1399 switch (event.type()) {
1400 case HOST_ADDED:
1401 case HOST_UPDATED:
Jordan Halterman6328db72018-04-10 13:34:50 -04001402 log.trace("Scheduled host event {}", event);
1403 hostEventExecutor.execute(() -> hostUpdated(event.subject()));
Kalhee Kim45fede42017-09-05 19:05:06 +00001404 break;
1405 case HOST_REMOVED:
Jordan Halterman6328db72018-04-10 13:34:50 -04001406 log.trace("Scheduled host event {}", event);
1407 hostEventExecutor.execute(() -> hostRemoved(event.subject()));
Kalhee Kim45fede42017-09-05 19:05:06 +00001408 break;
Kalhee Kim45fede42017-09-05 19:05:06 +00001409 default:
1410 break;
1411 }
1412 }
1413 }
1414
1415 /**
Kalhee Kim45fede42017-09-05 19:05:06 +00001416 * Handle host updated.
1417 * If the host is DHCP server or gateway, update connect mac and vlan.
1418 *
1419 * @param host the host
1420 */
1421 private void hostUpdated(Host host) {
Yi Tseng525ff402017-10-23 19:39:39 -07001422 hostUpdated(host, defaultServerInfoList);
1423 hostUpdated(host, indirectServerInfoList);
Kalhee Kim45fede42017-09-05 19:05:06 +00001424 }
1425
Yi Tseng525ff402017-10-23 19:39:39 -07001426 private void hostUpdated(Host host, List<DhcpServerInfo> serverInfoList) {
1427 DhcpServerInfo serverInfo;
1428 Ip6Address targetIp;
1429 if (!serverInfoList.isEmpty()) {
1430 serverInfo = serverInfoList.get(0);
1431 Ip6Address serverIp = serverInfo.getDhcpServerIp6().orElse(null);
1432 targetIp = serverInfo.getDhcpGatewayIp6().orElse(null);
1433
1434 if (targetIp == null) {
1435 targetIp = serverIp;
1436 }
Yi Tseng525ff402017-10-23 19:39:39 -07001437 if (targetIp != null) {
1438 if (host.ipAddresses().contains(targetIp)) {
1439 serverInfo.setDhcpConnectMac(host.mac());
1440 serverInfo.setDhcpConnectVlan(host.vlan());
1441 requestDhcpPacket(serverIp);
1442 }
1443 }
1444 }
1445 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001446 /**
1447 * Handle host removed.
1448 * If the host is DHCP server or gateway, unset connect mac and vlan.
1449 *
1450 * @param host the host
1451 */
1452 private void hostRemoved(Host host) {
Yi Tseng525ff402017-10-23 19:39:39 -07001453 hostRemoved(host, defaultServerInfoList);
1454 hostRemoved(host, indirectServerInfoList);
Yi Tseng919b2df2017-09-07 16:22:51 -07001455 }
1456
Yi Tseng525ff402017-10-23 19:39:39 -07001457 private void hostRemoved(Host host, List<DhcpServerInfo> serverInfoList) {
1458 DhcpServerInfo serverInfo;
1459 Ip6Address targetIp;
1460
1461 if (!serverInfoList.isEmpty()) {
1462 serverInfo = serverInfoList.get(0);
1463 Ip6Address serverIp = serverInfo.getDhcpServerIp6().orElse(null);
1464 targetIp = serverInfo.getDhcpGatewayIp6().orElse(null);
1465
1466 if (targetIp == null) {
1467 targetIp = serverIp;
1468 }
Yi Tseng525ff402017-10-23 19:39:39 -07001469 if (targetIp != null) {
1470 if (host.ipAddresses().contains(targetIp)) {
1471 serverInfo.setDhcpConnectVlan(null);
1472 serverInfo.setDhcpConnectMac(null);
1473 cancelDhcpPacket(serverIp);
1474 }
1475 }
1476 }
1477 }
1478
Kalhee Kim45b24182017-10-18 18:30:23 +00001479 /**
1480 * Returns the first interface ip from interface.
1481 *
1482 * @param iface interface of one connect point
1483 * @return the first interface IP; null if not exists an IP address in
1484 * these interfaces
1485 */
1486 private Ip6Address getFirstIpFromInterface(Interface iface) {
1487 checkNotNull(iface, "Interface can't be null");
1488 return iface.ipAddressesList().stream()
1489 .map(InterfaceIpAddress::ipAddress)
1490 .filter(IpAddress::isIp6)
1491 .map(IpAddress::getIp6Address)
1492 .findFirst()
1493 .orElse(null);
1494 }
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001495
1496 /**
1497 * Gets Interface facing to the server for default host.
1498 *
1499 * @return the Interface facing to the server; null if not found
1500 */
1501 private Interface getServerInterface() {
1502 DhcpServerInfo serverInfo;
1503 ConnectPoint dhcpServerConnectPoint;
1504 VlanId dhcpConnectVlan;
1505
1506 if (!defaultServerInfoList.isEmpty()) {
1507 serverInfo = defaultServerInfoList.get(0);
1508 dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1509 dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1510 } else {
1511 return null;
1512 }
1513 if (dhcpServerConnectPoint == null || dhcpConnectVlan == null) {
1514 log.info("Default DHCP server {} not resolve yet", serverInfo.getDhcpGatewayIp6());
1515 return null;
1516 }
1517 return interfaceService.getInterfacesByPort(dhcpServerConnectPoint)
1518 .stream()
1519 .filter(iface -> dhcp6HandlerUtil.interfaceContainsVlan(iface, dhcpConnectVlan))
1520 .findFirst()
1521 .orElse(null);
1522 }
1523
1524 /**
1525 * Gets Interface facing to the server for indirect hosts.
1526 * Use default server Interface if indirect server not configured.
1527 *
1528 * @return the Interface facing to the server; null if not found
1529 */
1530 private Interface getIndirectServerInterface() {
1531 DhcpServerInfo serverInfo;
1532
1533 ConnectPoint indirectDhcpServerConnectPoint;
1534 VlanId indirectDhcpConnectVlan;
1535
1536 if (!indirectServerInfoList.isEmpty()) {
1537 serverInfo = indirectServerInfoList.get(0);
1538 indirectDhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1539 indirectDhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1540 } else {
1541 return getServerInterface();
1542 }
1543 if (indirectDhcpServerConnectPoint == null || indirectDhcpConnectVlan == null) {
1544 log.info("Indirect DHCP server {} not resolve yet", serverInfo.getDhcpGatewayIp6());
1545 return null;
1546 }
1547 return interfaceService.getInterfacesByPort(indirectDhcpServerConnectPoint)
1548 .stream()
1549 .filter(iface -> dhcp6HandlerUtil.interfaceContainsVlan(iface, indirectDhcpConnectVlan))
1550 .findFirst()
1551 .orElse(null);
1552 }
1553
Kalhee Kim45b24182017-10-18 18:30:23 +00001554 /**
Kalhee Kim495c9b22017-11-07 16:32:09 +00001555 * Checks if serverInfo's host info (mac and vlan) is filled in; if not, fills in.
1556 *
1557 * @param serverInfo server information
1558 * @return newServerInfo if host info can be either found or filled in.
1559 */
1560 private DhcpServerInfo getHostInfoForServerInfo(DhcpServerInfo serverInfo, List<DhcpServerInfo> sererInfoList) {
1561 DhcpServerInfo newServerInfo = null;
1562 MacAddress dhcpServerConnectMac = serverInfo.getDhcpConnectMac().orElse(null);
1563 VlanId dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1564 ConnectPoint dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1565
1566 if (dhcpServerConnectMac != null && dhcpConnectVlan != null) {
1567 newServerInfo = serverInfo;
Charles Chan750c9992018-02-26 10:44:49 -08001568 log.debug("DHCP server {} host info found. ConnectPt{} Mac {} vlan {}", serverInfo.getDhcpServerIp6(),
Kalhee Kim495c9b22017-11-07 16:32:09 +00001569 dhcpServerConnectPoint, dhcpServerConnectMac, dhcpConnectVlan);
1570 } else {
1571 log.warn("DHCP server {} not resolve yet connectPt {} mac {} vlan {}", serverInfo.getDhcpServerIp6(),
1572 dhcpServerConnectPoint, dhcpServerConnectMac, dhcpConnectVlan);
1573
1574 Ip6Address ipToProbe;
1575 if (serverInfo.getDhcpGatewayIp6().isPresent()) {
1576 ipToProbe = serverInfo.getDhcpGatewayIp6().get();
1577 } else {
1578 ipToProbe = serverInfo.getDhcpServerIp6().orElse(null);
1579 }
1580 String hostToProbe = serverInfo.getDhcpGatewayIp6()
1581 .map(ip -> "gateway").orElse("server");
1582
1583 log.info("Dynamically probing to resolve {} IP {}", hostToProbe, ipToProbe);
1584 hostService.startMonitoringIp(ipToProbe);
1585
1586 Set<Host> hosts = hostService.getHostsByIp(ipToProbe);
1587 if (!hosts.isEmpty()) {
1588 int serverInfoIndex = sererInfoList.indexOf(serverInfo);
1589 Host host = hosts.iterator().next();
1590 serverInfo.setDhcpConnectVlan(host.vlan());
1591 serverInfo.setDhcpConnectMac(host.mac());
1592 // replace the serverInfo in the list
1593 sererInfoList.set(serverInfoIndex, serverInfo);
1594 newServerInfo = serverInfo;
1595 log.warn("Dynamically host found host {}", host);
1596 } else {
1597 log.warn("No host found host ip {} dynamically", ipToProbe);
1598 }
1599 }
1600 return newServerInfo;
1601 }
1602
1603 /**
Kalhee Kim45b24182017-10-18 18:30:23 +00001604 * Gets Interface facing to the server for default host.
1605 *
Kalhee Kim495c9b22017-11-07 16:32:09 +00001606 * @param serverInfo server information
Kalhee Kim45b24182017-10-18 18:30:23 +00001607 * @return the Interface facing to the server; null if not found
1608 */
Kalhee Kim495c9b22017-11-07 16:32:09 +00001609 private Interface getServerInterface(DhcpServerInfo serverInfo) {
1610 Interface serverInterface = null;
Yi Tseng25bfe372017-11-03 16:27:32 -07001611
Kalhee Kim495c9b22017-11-07 16:32:09 +00001612 ConnectPoint dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1613 VlanId dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1614
1615 if (dhcpServerConnectPoint != null && dhcpConnectVlan != null) {
1616 serverInterface = interfaceService.getInterfacesByPort(dhcpServerConnectPoint)
1617 .stream()
1618 .filter(iface -> dhcp6HandlerUtil.interfaceContainsVlan(iface, dhcpConnectVlan))
1619 .findFirst()
1620 .orElse(null);
Yi Tseng25bfe372017-11-03 16:27:32 -07001621 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +00001622 log.warn("DHCP server {} not resolve yet connectPoint {} vlan {}", serverInfo.getDhcpServerIp6(),
1623 dhcpServerConnectPoint, dhcpConnectVlan);
Kalhee Kim45b24182017-10-18 18:30:23 +00001624 }
Kalhee Kim495c9b22017-11-07 16:32:09 +00001625
1626 return serverInterface;
Kalhee Kim45b24182017-10-18 18:30:23 +00001627 }
1628
Yi Tseng525ff402017-10-23 19:39:39 -07001629 private void requestDhcpPacket(Ip6Address serverIp) {
1630 requestServerDhcpPacket(serverIp);
1631 requestClientDhcpPacket(serverIp);
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001632 requestServerLQPacket(serverIp);
Yi Tseng525ff402017-10-23 19:39:39 -07001633 }
1634
1635 private void cancelDhcpPacket(Ip6Address serverIp) {
1636 cancelServerDhcpPacket(serverIp);
1637 cancelClientDhcpPacket(serverIp);
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001638 cancelServerLQPacket(serverIp);
Yi Tseng525ff402017-10-23 19:39:39 -07001639 }
1640
1641 private void cancelServerDhcpPacket(Ip6Address serverIp) {
1642 TrafficSelector serverSelector =
1643 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1644 .matchIPv6Src(serverIp.toIpPrefix())
1645 .build();
1646 packetService.cancelPackets(serverSelector,
1647 PacketPriority.CONTROL,
1648 appId);
1649 }
1650
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001651 private void cancelServerLQPacket(Ip6Address serverIp) {
1652 TrafficSelector serverSelector =
1653 DefaultTrafficSelector.builder(LEASE_QUERY_RESPONSE_SELECTOR)
1654 .matchIPv6Src(serverIp.toIpPrefix())
1655 .build();
1656 packetService.cancelPackets(serverSelector,
1657 PacketPriority.CONTROL,
1658 appId);
1659 }
1660
Yi Tseng525ff402017-10-23 19:39:39 -07001661 private void requestServerDhcpPacket(Ip6Address serverIp) {
1662 TrafficSelector serverSelector =
1663 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1664 .matchIPv6Src(serverIp.toIpPrefix())
1665 .build();
1666 packetService.requestPackets(serverSelector,
1667 PacketPriority.CONTROL,
1668 appId);
1669 }
1670
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001671 private void requestServerLQPacket(Ip6Address serverIp) {
1672 TrafficSelector serverSelector =
1673 DefaultTrafficSelector.builder(LEASE_QUERY_RESPONSE_SELECTOR)
1674 .matchIPv6Src(serverIp.toIpPrefix())
1675 .build();
1676 packetService.requestPackets(serverSelector,
1677 PacketPriority.CONTROL,
1678 appId);
1679 }
1680
Yi Tseng525ff402017-10-23 19:39:39 -07001681 private void cancelClientDhcpPacket(Ip6Address serverIp) {
1682 // Packet comes from relay
1683 TrafficSelector indirectClientSelector =
1684 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1685 .matchIPv6Dst(serverIp.toIpPrefix())
1686 .build();
1687 packetService.cancelPackets(indirectClientSelector,
1688 PacketPriority.CONTROL,
1689 appId);
Yi Tseng41dde702017-11-02 15:21:10 -07001690 indirectClientSelector =
1691 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1692 .matchIPv6Dst(Ip6Address.ALL_DHCP_RELAY_AGENTS_AND_SERVERS.toIpPrefix())
1693 .build();
1694 packetService.cancelPackets(indirectClientSelector,
1695 PacketPriority.CONTROL,
1696 appId);
1697 indirectClientSelector =
1698 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1699 .matchIPv6Dst(Ip6Address.ALL_DHCP_SERVERS.toIpPrefix())
1700 .build();
1701 packetService.cancelPackets(indirectClientSelector,
1702 PacketPriority.CONTROL,
1703 appId);
Yi Tseng525ff402017-10-23 19:39:39 -07001704
1705 // Packet comes from client
1706 packetService.cancelPackets(CLIENT_SERVER_SELECTOR,
1707 PacketPriority.CONTROL,
1708 appId);
1709 }
1710
1711 private void requestClientDhcpPacket(Ip6Address serverIp) {
1712 // Packet comes from relay
1713 TrafficSelector indirectClientSelector =
1714 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1715 .matchIPv6Dst(serverIp.toIpPrefix())
1716 .build();
1717 packetService.requestPackets(indirectClientSelector,
1718 PacketPriority.CONTROL,
1719 appId);
Yi Tseng41dde702017-11-02 15:21:10 -07001720 indirectClientSelector =
1721 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1722 .matchIPv6Dst(Ip6Address.ALL_DHCP_RELAY_AGENTS_AND_SERVERS.toIpPrefix())
1723 .build();
1724 packetService.requestPackets(indirectClientSelector,
1725 PacketPriority.CONTROL,
1726 appId);
1727 indirectClientSelector =
1728 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1729 .matchIPv6Dst(Ip6Address.ALL_DHCP_SERVERS.toIpPrefix())
1730 .build();
1731 packetService.requestPackets(indirectClientSelector,
1732 PacketPriority.CONTROL,
1733 appId);
Yi Tseng525ff402017-10-23 19:39:39 -07001734
1735 // Packet comes from client
1736 packetService.requestPackets(CLIENT_SERVER_SELECTOR,
1737 PacketPriority.CONTROL,
1738 appId);
1739 }
1740
1741 /**
1742 * Process the ignore rules.
1743 *
1744 * @param deviceId the device id
1745 * @param vlanId the vlan to be ignored
1746 * @param op the operation, ADD to install; REMOVE to uninstall rules
1747 */
1748 private void processIgnoreVlanRule(DeviceId deviceId, VlanId vlanId, Objective.Operation op) {
Yi Tseng525ff402017-10-23 19:39:39 -07001749 AtomicInteger installedCount = new AtomicInteger(DHCP_SELECTORS.size());
1750 DHCP_SELECTORS.forEach(trafficSelector -> {
1751 TrafficSelector selector = DefaultTrafficSelector.builder(trafficSelector)
1752 .matchVlanId(vlanId)
1753 .build();
1754
1755 ForwardingObjective.Builder builder = DefaultForwardingObjective.builder()
1756 .withFlag(ForwardingObjective.Flag.VERSATILE)
1757 .withSelector(selector)
1758 .withPriority(IGNORE_CONTROL_PRIORITY)
Yi Tsengdbabeed2017-11-29 10:49:20 -08001759 .withTreatment(DefaultTrafficTreatment.emptyTreatment())
Yi Tseng525ff402017-10-23 19:39:39 -07001760 .fromApp(appId);
1761
1762
1763 ObjectiveContext objectiveContext = new ObjectiveContext() {
1764 @Override
1765 public void onSuccess(Objective objective) {
1766 log.info("Ignore rule {} (Vlan id {}, device {}, selector {})",
1767 op, vlanId, deviceId, selector);
1768 int countDown = installedCount.decrementAndGet();
1769 if (countDown != 0) {
1770 return;
1771 }
1772 switch (op) {
1773 case ADD:
1774 ignoredVlans.put(deviceId, vlanId);
1775 break;
1776 case REMOVE:
1777 ignoredVlans.remove(deviceId, vlanId);
1778 break;
1779 default:
1780 log.warn("Unsupported objective operation {}", op);
1781 break;
1782 }
1783 }
1784
1785 @Override
1786 public void onError(Objective objective, ObjectiveError error) {
1787 log.warn("Can't {} ignore rule (vlan id {}, selector {}, device {}) due to {}",
1788 op, vlanId, selector, deviceId, error);
1789 }
1790 };
1791
1792 ForwardingObjective fwd;
1793 switch (op) {
1794 case ADD:
1795 fwd = builder.add(objectiveContext);
1796 break;
1797 case REMOVE:
1798 fwd = builder.remove(objectiveContext);
1799 break;
1800 default:
1801 log.warn("Unsupported objective operation {}", op);
1802 return;
1803 }
1804
1805 Device device = deviceService.getDevice(deviceId);
1806 if (device == null || !device.is(Pipeliner.class)) {
1807 log.warn("Device {} is not available now, wait until device is available", deviceId);
1808 return;
1809 }
1810 flowObjectiveService.apply(deviceId, fwd);
1811 });
1812 }
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001813
Kalhee Kim121ba922017-11-01 17:56:44 +00001814 /**
1815 * Find first ipaddress for a given Host info i.e. mac and vlan.
1816 *
1817 * @param clientMac client mac
1818 * @param vlanId packet's vlan
1819 * @return next-hop link-local ipaddress for a given host
1820 */
Kalhee Kim495c9b22017-11-07 16:32:09 +00001821 private IpAddress getFirstIpByHost(Boolean directConnFlag, MacAddress clientMac, VlanId vlanId) {
Kalhee Kim121ba922017-11-01 17:56:44 +00001822 IpAddress nextHopIp;
1823 // pick out the first link-local ip address
1824 HostId gwHostId = HostId.hostId(clientMac, vlanId);
1825 Host gwHost = hostService.getHost(gwHostId);
1826 if (gwHost == null) {
1827 log.warn("Can't find gateway host for hostId {}", gwHostId);
1828 return null;
1829 }
Kalhee Kim495c9b22017-11-07 16:32:09 +00001830 if (directConnFlag) {
1831 nextHopIp = gwHost.ipAddresses()
1832 .stream()
1833 .filter(IpAddress::isIp6)
1834 .map(IpAddress::getIp6Address)
1835 .findFirst()
1836 .orElse(null);
1837 } else {
1838 nextHopIp = gwHost.ipAddresses()
1839 .stream()
1840 .filter(IpAddress::isIp6)
1841 .filter(ip6 -> ip6.isLinkLocal())
1842 .map(IpAddress::getIp6Address)
1843 .findFirst()
1844 .orElse(null);
1845 }
Kalhee Kim121ba922017-11-01 17:56:44 +00001846 return nextHopIp;
1847 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +00001848
Kalhee Kim495c9b22017-11-07 16:32:09 +00001849 private List<DhcpServerInfo> findValidServerInfo(boolean directConnFlag) {
1850 List<DhcpServerInfo> validServerInfo;
1851
1852 if (directConnFlag || indirectServerInfoList.isEmpty()) {
1853 validServerInfo = new ArrayList<DhcpServerInfo>(defaultServerInfoList);
1854 } else {
1855 validServerInfo = new ArrayList<DhcpServerInfo>(indirectServerInfoList);
1856 }
1857 return validServerInfo;
1858 }
1859
1860 private DhcpServerInfo findServerInfoFromServer(boolean directConnFlag, ConnectPoint inPort) {
1861 List<DhcpServerInfo> validServerInfoList = findValidServerInfo(directConnFlag);
1862 DhcpServerInfo foundServerInfo = null;
1863 for (DhcpServerInfo serverInfo : validServerInfoList) {
1864 if (inPort.equals(serverInfo.getDhcpServerConnectPoint().get())) {
1865 foundServerInfo = serverInfo;
Charles Chan750c9992018-02-26 10:44:49 -08001866 log.debug("ServerInfo found for Rcv port {} Server Connect Point {} for {}",
Kalhee Kim495c9b22017-11-07 16:32:09 +00001867 inPort, serverInfo.getDhcpServerConnectPoint(), directConnFlag ? "direct" : "indirect");
1868 break;
1869 } else {
1870 log.warn("Rcv port {} not the same as Server Connect Point {} for {}",
1871 inPort, serverInfo.getDhcpServerConnectPoint(), directConnFlag ? "direct" : "indirect");
1872 }
1873 }
1874 return foundServerInfo;
1875 }
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001876
Kalhee Kim495c9b22017-11-07 16:32:09 +00001877 /**
1878 * Set the dhcp6 lease expiry poll interval value.
1879 *
1880 * @param val poll interval value in seconds
1881 */
Kalhee Kim2f07c602017-11-15 18:57:53 +00001882 @Override
Kalhee Kim495c9b22017-11-07 16:32:09 +00001883 public void setDhcp6PollInterval(int val) {
1884 dhcp6PollInterval = val;
1885 }
1886
Kalhee Kim2f07c602017-11-15 18:57:53 +00001887 /**
1888 * get the dhcp6 lease expiry poll interval value.
1889 * This is a private function
1890 * @return poll interval value in seconds
1891 */
1892 private int getDhcp6PollInterval() {
1893 return dhcp6PollInterval;
1894 }
1895
1896 /**
1897 * Find lease-expired ipaddresses and pd prefixes.
1898 * Removing host/route/fpm entries.
1899 */
1900 public void timeTick() {
1901 long currentTime = System.currentTimeMillis();
1902 Collection<DhcpRecord> records = dhcpRelayStore.getDhcpRecords();
1903
1904 log.debug("timeTick called currenttime {} records num {} ", currentTime, records.size());
1905
1906 records.forEach(record -> {
1907 boolean addrOrPdRemoved = false;
1908 DHCP6.MsgType ip6Status = record.ip6Status().orElse(null);
1909 if (ip6Status == null) {
1910 log.debug("record is not valid v6 record.");
1911 return;
1912 }
1913
1914 if ((currentTime - record.getLastIp6Update()) >
1915 ((record.addrPrefTime() + getDhcp6PollInterval() / 2) * 1000)) {
1916 // remove ipaddress from host/route table
1917 IpAddress ip = record.ip6Address().orElse(null);
1918 if (ip != null) {
1919 if (record.directlyConnected()) {
1920 providerService.removeIpFromHost(HostId.hostId(record.macAddress(),
1921 record.vlanId()), ip);
1922 } else {
1923 MacAddress gwMac = record.nextHop().orElse(null);
1924 if (gwMac == null) {
1925 log.warn("Can't find gateway mac address from record {} for ip6Addr", record);
1926 return;
1927 }
1928 IpAddress nextHopIp = getFirstIpByHost(record.directlyConnected(),
1929 gwMac,
1930 record.vlanId());
Kalhee Kim54a97c92018-04-02 21:23:46 +00001931 Route route = new Route(Route.Source.DHCP, ip.toIpPrefix(), nextHopIp);
Kalhee Kim2f07c602017-11-15 18:57:53 +00001932 routeStore.removeRoute(route);
1933 }
1934 record.updateAddrPrefTime(0);
1935 record.ip6Address(null);
1936 addrOrPdRemoved = true;
1937 dhcpRelayStore.updateDhcpRecord(HostId.hostId(record.macAddress(),
1938 record.vlanId()), record);
1939 log.warn("IP6 address is set to null. delta {} lastUpdate {} addrPrefTime {}",
1940 (currentTime - record.getLastIp6Update()), record.getLastIp6Update(),
1941 record.addrPrefTime());
1942 }
1943 }
1944 if ((currentTime - record.getLastPdUpdate()) >
1945 ((record.pdPrefTime() + getDhcp6PollInterval() / 2) * 1000)) {
1946 // remove PD from route/fpm table
1947 IpPrefix pdIpPrefix = record.pdPrefix().orElse(null);
1948 if (pdIpPrefix != null) {
1949 if (record.directlyConnected()) {
1950 providerService.removeIpFromHost(HostId.hostId(record.macAddress(), record.vlanId()),
1951 pdIpPrefix.address().getIp6Address());
1952 } else {
1953 MacAddress gwMac = record.nextHop().orElse(null);
1954 if (gwMac == null) {
1955 log.warn("Can't find gateway mac address from record {} for PD prefix", record);
1956 return;
1957 }
1958 IpAddress nextHopIp = getFirstIpByHost(record.directlyConnected(),
1959 gwMac,
1960 record.vlanId());
Kalhee Kim54a97c92018-04-02 21:23:46 +00001961 Route route = new Route(Route.Source.DHCP, pdIpPrefix, nextHopIp);
Kalhee Kim2f07c602017-11-15 18:57:53 +00001962 routeStore.removeRoute(route);
1963 if (this.dhcpFpmEnabled) {
1964 dhcpFpmPrefixStore.removeFpmRecord(pdIpPrefix);
1965 }
1966 }
1967 record.updatePdPrefTime(0);
1968 record.pdPrefix(null);
1969 addrOrPdRemoved = true;
1970 dhcpRelayStore.updateDhcpRecord(HostId.hostId(record.macAddress(),
1971 record.vlanId()), record);
1972 log.warn("PD prefix is set to null.delta {} pdPrefTime {}",
1973 (currentTime - record.getLastPdUpdate()), record.pdPrefTime());
1974 }
1975 }
1976 if (addrOrPdRemoved &&
1977 !record.ip6Address().isPresent() && !record.pdPrefix().isPresent()) {
1978 log.warn("ip6Status {} IP6 address and IP6 PD both are null. Remove record.", ip6Status);
1979 dhcpRelayStore.removeDhcpRecord(HostId.hostId(record.macAddress(), record.vlanId()));
1980 }
1981 }
1982 );
1983 }
1984}