blob: 4d1be53bb32095b9190319fceba38ddc2a079317 [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;
Yi Tseng525ff402017-10-23 19:39:39 -0700116import java.util.concurrent.atomic.AtomicInteger;
Kalhee Kim45fede42017-09-05 19:05:06 +0000117import static com.google.common.base.Preconditions.checkNotNull;
118import static com.google.common.base.Preconditions.checkState;
Yi Tseng525ff402017-10-23 19:39:39 -0700119import static org.onosproject.net.flowobjective.Objective.Operation.ADD;
120import static org.onosproject.net.flowobjective.Objective.Operation.REMOVE;
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000121import java.util.concurrent.Semaphore;
Yi Tseng51301292017-07-28 13:02:59 -0700122
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800123
Yi Tseng51301292017-07-28 13:02:59 -0700124@Component
125@Service
126@Property(name = "version", value = "6")
Yi Tsengaa417a62017-09-08 17:22:51 -0700127public class Dhcp6HandlerImpl implements DhcpHandler, HostProvider {
Charles Chand988c282017-09-12 17:09:32 -0700128 public static final String DHCP_V6_RELAY_APP = "org.onosproject.Dhcp6HandlerImpl";
Saurav Das7c6dec12017-09-13 14:35:56 -0700129 public static final ProviderId PROVIDER_ID = new ProviderId("dhcp6", DHCP_V6_RELAY_APP);
Yi Tseng525ff402017-10-23 19:39:39 -0700130 private static final int IGNORE_CONTROL_PRIORITY = PacketPriority.CONTROL.priorityValue() + 1000;
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000131 private String gCount = "global";
Yi Tseng525ff402017-10-23 19:39:39 -0700132 private static final TrafficSelector CLIENT_SERVER_SELECTOR = DefaultTrafficSelector.builder()
133 .matchEthType(Ethernet.TYPE_IPV6)
134 .matchIPProtocol(IPv6.PROTOCOL_UDP)
135 .matchIPv6Src(IpPrefix.IPV6_LINK_LOCAL_PREFIX)
136 .matchIPv6Dst(Ip6Address.ALL_DHCP_RELAY_AGENTS_AND_SERVERS.toIpPrefix())
137 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_V6_CLIENT_PORT))
138 .matchUdpDst(TpPort.tpPort(UDP.DHCP_V6_SERVER_PORT))
139 .build();
140 private static final TrafficSelector SERVER_RELAY_SELECTOR = DefaultTrafficSelector.builder()
141 .matchEthType(Ethernet.TYPE_IPV6)
142 .matchIPProtocol(IPv6.PROTOCOL_UDP)
143 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_V6_SERVER_PORT))
144 .matchUdpDst(TpPort.tpPort(UDP.DHCP_V6_SERVER_PORT))
145 .build();
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800146 // lease query reply is from server to client (no relay in between) - so we need to
147 // catch that scenario also ..
148 private static final TrafficSelector LEASE_QUERY_RESPONSE_SELECTOR = DefaultTrafficSelector.builder()
149 .matchEthType(Ethernet.TYPE_IPV6)
150 .matchIPProtocol(IPv6.PROTOCOL_UDP)
151 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_V6_SERVER_PORT))
152 .matchUdpDst(TpPort.tpPort(UDP.DHCP_V6_CLIENT_PORT))
153 .build();
Yi Tseng525ff402017-10-23 19:39:39 -0700154 static final Set<TrafficSelector> DHCP_SELECTORS = ImmutableSet.of(
155 CLIENT_SERVER_SELECTOR,
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800156 SERVER_RELAY_SELECTOR,
157 LEASE_QUERY_RESPONSE_SELECTOR
Yi Tseng525ff402017-10-23 19:39:39 -0700158 );
Kalhee Kim45fede42017-09-05 19:05:06 +0000159 private static Logger log = LoggerFactory.getLogger(Dhcp6HandlerImpl.class);
Yi Tseng51301292017-07-28 13:02:59 -0700160
Kalhee Kim45fede42017-09-05 19:05:06 +0000161 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
162 protected DhcpRelayStore dhcpRelayStore;
Yi Tseng51301292017-07-28 13:02:59 -0700163
Kalhee Kim45fede42017-09-05 19:05:06 +0000164 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000165 protected DhcpRelayCountersStore dhcpRelayCountersStore;
166
167 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Kalhee Kim45fede42017-09-05 19:05:06 +0000168 protected PacketService packetService;
169
170 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Kalhee Kim45fede42017-09-05 19:05:06 +0000171 protected RouteStore routeStore;
172
173 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
174 protected InterfaceService interfaceService;
175
176 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
177 protected HostService hostService;
178
Yi Tsengaa417a62017-09-08 17:22:51 -0700179 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
180 protected HostProviderRegistry providerRegistry;
Kalhee Kim45fede42017-09-05 19:05:06 +0000181
Yi Tseng525ff402017-10-23 19:39:39 -0700182 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
183 protected CoreService coreService;
184
185 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Kalhee Kimba366062017-11-07 16:32:09 +0000186 protected DhcpFpmPrefixStore dhcpFpmPrefixStore;
187
188 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Yi Tseng525ff402017-10-23 19:39:39 -0700189 protected DeviceService deviceService;
190
191 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
192 protected FlowObjectiveService flowObjectiveService;
193
Yi Tsengaa417a62017-09-08 17:22:51 -0700194 protected HostProviderService providerService;
Yi Tseng525ff402017-10-23 19:39:39 -0700195 protected ApplicationId appId;
Charles Chan70fdd492018-03-07 17:36:06 -0800196 protected Multimap<DeviceId, VlanId> ignoredVlans = Multimaps.synchronizedMultimap(HashMultimap.create());
Yi Tseng525ff402017-10-23 19:39:39 -0700197 private InternalHostListener hostListener = new InternalHostListener();
Kalhee Kimba366062017-11-07 16:32:09 +0000198 private Boolean dhcpFpmEnabled = false;
Kalhee Kim495c9b22017-11-07 16:32:09 +0000199 private Dhcp6HandlerUtil dhcp6HandlerUtil = new Dhcp6HandlerUtil();
Charles Chan909cff82018-03-05 13:14:02 -0800200 private List<DhcpServerInfo> defaultServerInfoList = new CopyOnWriteArrayList<>();
201 private List<DhcpServerInfo> indirectServerInfoList = new CopyOnWriteArrayList<>();
Kalhee Kim495c9b22017-11-07 16:32:09 +0000202 private class IpAddressInfo {
203 Ip6Address ip6Address;
204 long prefTime;
205 }
206 private class PdPrefixInfo {
207 IpPrefix pdPrefix;
208 long prefTime;
209 }
210 protected int dhcp6PollInterval = 24 * 3600; // 24 hr period
211
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000212 // max 1 thread
213 static Semaphore recordSemaphore = new Semaphore(1);
Kalhee Kim45fede42017-09-05 19:05:06 +0000214
215 // CLIENT message types
216 public static final Set<Byte> MSG_TYPE_FROM_CLIENT =
217 ImmutableSet.of(DHCP6.MsgType.SOLICIT.value(),
218 DHCP6.MsgType.REQUEST.value(),
219 DHCP6.MsgType.REBIND.value(),
220 DHCP6.MsgType.RENEW.value(),
221 DHCP6.MsgType.RELEASE.value(),
222 DHCP6.MsgType.DECLINE.value(),
223 DHCP6.MsgType.CONFIRM.value(),
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800224 DHCP6.MsgType.RELAY_FORW.value(),
225 DHCP6.MsgType.LEASEQUERY.value());
Kalhee Kim45fede42017-09-05 19:05:06 +0000226 // SERVER message types
227 public static final Set<Byte> MSG_TYPE_FROM_SERVER =
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800228 ImmutableSet.of(DHCP6.MsgType.RELAY_REPL.value(),
229 DHCP6.MsgType.LEASEQUERY_REPLY.value());
Kalhee Kim45fede42017-09-05 19:05:06 +0000230
231 @Activate
232 protected void activate() {
Yi Tseng525ff402017-10-23 19:39:39 -0700233 appId = coreService.registerApplication(DHCP_V6_RELAY_APP);
Yi Tsengaa417a62017-09-08 17:22:51 -0700234 providerService = providerRegistry.register(this);
Kalhee Kim45fede42017-09-05 19:05:06 +0000235 hostService.addListener(hostListener);
Yi Tseng51301292017-07-28 13:02:59 -0700236 }
237
Kalhee Kim45fede42017-09-05 19:05:06 +0000238 @Deactivate
239 protected void deactivate() {
Yi Tsengaa417a62017-09-08 17:22:51 -0700240 providerRegistry.unregister(this);
Kalhee Kim45fede42017-09-05 19:05:06 +0000241 hostService.removeListener(hostListener);
Yi Tseng919b2df2017-09-07 16:22:51 -0700242 defaultServerInfoList.forEach(this::stopMonitoringIps);
243 defaultServerInfoList.clear();
244 indirectServerInfoList.forEach(this::stopMonitoringIps);
245 indirectServerInfoList.clear();
246 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000247
Yi Tseng919b2df2017-09-07 16:22:51 -0700248 private void stopMonitoringIps(DhcpServerInfo serverInfo) {
249 serverInfo.getDhcpGatewayIp6().ifPresent(gatewayIp -> {
250 hostService.stopMonitoringIp(gatewayIp);
251 });
252 serverInfo.getDhcpServerIp6().ifPresent(serverIp -> {
253 hostService.stopMonitoringIp(serverIp);
254 });
Yi Tseng51301292017-07-28 13:02:59 -0700255 }
256
Yi Tseng51301292017-07-28 13:02:59 -0700257 @Override
Yi Tseng919b2df2017-09-07 16:22:51 -0700258 public List<DhcpServerInfo> getDefaultDhcpServerInfoList() {
259 return defaultServerInfoList;
Yi Tseng51301292017-07-28 13:02:59 -0700260 }
261
262 @Override
Yi Tseng919b2df2017-09-07 16:22:51 -0700263 public List<DhcpServerInfo> getIndirectDhcpServerInfoList() {
264 return indirectServerInfoList;
Yi Tseng51301292017-07-28 13:02:59 -0700265 }
266
267 @Override
Yi Tseng525ff402017-10-23 19:39:39 -0700268 public void updateIgnoreVlanConfig(IgnoreDhcpConfig config) {
269 if (config == null) {
270 ignoredVlans.forEach(((deviceId, vlanId) -> {
271 processIgnoreVlanRule(deviceId, vlanId, REMOVE);
272 }));
273 return;
274 }
275 config.ignoredVlans().forEach((deviceId, vlanId) -> {
276 if (ignoredVlans.get(deviceId).contains(vlanId)) {
277 // don't need to process if it already ignored
278 return;
279 }
280 processIgnoreVlanRule(deviceId, vlanId, ADD);
281 });
Yi Tseng525ff402017-10-23 19:39:39 -0700282 ignoredVlans.forEach((deviceId, vlanId) -> {
283 if (!config.ignoredVlans().get(deviceId).contains(vlanId)) {
284 // not contains in new config, remove it
285 processIgnoreVlanRule(deviceId, vlanId, REMOVE);
286 }
287 });
288 }
289
290 @Override
Saurav Dasb805f1a2017-12-13 16:19:35 -0800291 public void removeIgnoreVlanState(IgnoreDhcpConfig config) {
292 if (config == null) {
293 ignoredVlans.clear();
294 return;
295 }
296 config.ignoredVlans().forEach((deviceId, vlanId) -> {
297 ignoredVlans.remove(deviceId, vlanId);
298 });
299 }
300
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800301 public DhcpRecord getDhcpRelayRecordFor(Ip6Address clientAddress) {
302
303 Collection<DhcpRecord> records = dhcpRelayStore.getDhcpRecords();
304 DhcpRecord dr = null;
305 for (DhcpRecord e:records) {
306 if (e.ip6Address().isPresent()) {
307 if (e.ip6Address().get().equals(clientAddress)) {
308 dr = e;
309 break;
310 }
311 }
312 }
313 return dr;
314 }
315
316 public MacAddress findNextHopMacForIp6FromRelayStore(Ip6Address clientAddress,
317 MacAddress clientMacAddress, VlanId vlanID) {
318
319 DhcpRecord dr = getDhcpRelayRecordFor(clientAddress);
320
321 if (dr != null) {
322 Optional<MacAddress> nextHopTempMac = dr.nextHopTemp();
323 if (nextHopTempMac.isPresent()) {
324 log.info("findNextHopForIp6FromRelayStore " + clientAddress + " got mac " + nextHopTempMac.toString());
325 return nextHopTempMac.get();
326 }
327 } else {
328 log.warn("findNextHopMacForIp6FromRelayStore could NOT find next hop for " + clientAddress);
329 return null;
330 }
331 return null;
332 }
333
334 public Ip6Address findNextHopIp6FromRelayStore(Ip6Address clientAddress) {
335
336 DhcpRecord dr = getDhcpRelayRecordFor(clientAddress);
337 if (dr != null) {
338 Optional<MacAddress> nextHopMac = dr.nextHop();
339 if (nextHopMac.isPresent()) {
340 // find the local ip6 from the host store
341 HostId gwHostId = HostId.hostId(nextHopMac.get(), dr.vlanId());
342 Host gwHost = hostService.getHost(gwHostId);
343 if (gwHost == null) {
344 log.warn("Can't find next hop host ID {}", gwHostId);
345 return null;
346 }
347 Ip6Address nextHopIp = gwHost.ipAddresses()
348 .stream()
349 .filter(IpAddress::isIp6)
350 .filter(IpAddress::isLinkLocal)
351 .map(IpAddress::getIp6Address)
352 .findFirst()
353 .orElse(null);
354
355 log.info("findNextHopIp6FromRelayStore " + clientAddress + " got mac " +
356 nextHopMac.toString() + " ip6 " + nextHopIp);
357 return nextHopIp;
358 }
359 } else {
360 log.warn("findNextHopIp6FromRelayStore could NOT find next hop for " + clientAddress);
361 return null;
362 }
363 return null;
364 }
365
366 private void setPotentialNextHopForIp6InRelayStore(Ip6Address clientAddress,
367 VlanId vlanId, MacAddress nh) {
368 DhcpRecord dr = getDhcpRelayRecordFor(clientAddress);
369 if (dr != null) {
370 dr.nextHopTemp(nh);
371 log.debug("LQ6 potential NH mac " + nh.toString() + " UPDATED in RelayRecord client " + clientAddress);
372 } else {
373 log.warn("LQ6 potential NH mac" + nh.toString() +
374 " NOT FOUND in RelayRecord for client - LQ rejected" + clientAddress);
375 }
376 }
377
378 public void handleLeaseQuery6ReplyMsg(PacketContext context, DHCP6 dhcp6Payload) {
379 ConnectPoint inPort = context.inPacket().receivedFrom();
380 log.info("Got LQV6-REPLY on port {}", inPort);
381 List<Dhcp6Option> lopt = dhcp6Payload.getOptions();
382 log.info("Options list: {}", lopt);
383 // find out if this lease is known is
384 Dhcp6ClientDataOption clientDataOption = dhcp6Payload.getOptions()
385 .stream()
386 .filter(opt -> opt instanceof Dhcp6ClientDataOption)
387 .map(pld -> (Dhcp6ClientDataOption) pld)
388 .findFirst()
389 .orElse(null);
390
391 if (clientDataOption == null) {
392 log.warn("clientDataOption option is not present, " +
393 "lease is UNKNOWN - not adding any new route...");
394 } else {
395 Dhcp6IaAddressOption aiAddressOption = clientDataOption.getOptions()
396 .stream()
397 .filter(opt -> opt instanceof Dhcp6IaAddressOption)
398 .map(pld -> (Dhcp6IaAddressOption) pld)
399 .findFirst()
400 .orElse(null);
401
402 Dhcp6ClientIdOption clientIdOption = clientDataOption.getOptions()
403 .stream()
404 .filter(opt -> opt instanceof Dhcp6ClientIdOption)
405 .map(pld -> (Dhcp6ClientIdOption) pld)
406 .findFirst()
407 .orElse(null);
408
409 if (aiAddressOption == null) {
410 log.warn("clientDataOption from DHCP server does not " +
411 "contains Dhcp6IaAddressOption for the client - giving up...");
412 } else {
413 Ip6Address clientAddress = aiAddressOption.getIp6Address();
414 MacAddress clientMacAddress = MacAddress.valueOf(clientIdOption.getDuid().getLinkLayerAddress());
415 Ethernet packet = context.inPacket().parsed();
416 VlanId vlanId = VlanId.vlanId(packet.getVlanID());
417 MacAddress potentialNextHopMac =
418 findNextHopMacForIp6FromRelayStore(clientAddress, clientMacAddress, vlanId);
419
420 if (potentialNextHopMac == null) {
421 log.warn("Can't find next hop host mac for client {} mac:{}/{}",
422 clientAddress, clientMacAddress, vlanId);
423 return;
424 } else {
425 log.info("Next hop mac for {}/{}/{} is {}", clientAddress,
426 clientMacAddress, vlanId, potentialNextHopMac.toString());
427 }
428 // search the next hop in the hosts store
429 HostId gwHostId = HostId.hostId(potentialNextHopMac, vlanId);
430 Host gwHost = hostService.getHost(gwHostId);
431 if (gwHost == null) {
432 log.warn("Can't find next hop host ID {}", gwHostId);
433 return;
434 }
435 Ip6Address nextHopIp = gwHost.ipAddresses()
436 .stream()
437 .filter(IpAddress::isIp6)
438 .filter(IpAddress::isLinkLocal)
439 .map(IpAddress::getIp6Address)
440 .findFirst()
441 .orElse(null);
442 if (nextHopIp == null) {
443 log.warn("Can't find IP6 address of next hop {}", gwHost);
444 return;
445 }
446 log.info("client " + clientAddress + " is known !");
Kalhee Kim54a97c92018-04-02 21:23:46 +0000447 Route routeForIP6 = new Route(Route.Source.DHCP, clientAddress.toIpPrefix(), nextHopIp);
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800448 log.debug("updating route of Client for indirectly connected.");
449 log.debug("client ip: " + clientAddress + ", next hop ip6: " + nextHopIp);
450 routeStore.updateRoute(routeForIP6);
451 }
452 }
453 }
454
Saurav Dasb805f1a2017-12-13 16:19:35 -0800455 @Override
Kalhee Kim45fede42017-09-05 19:05:06 +0000456 public void processDhcpPacket(PacketContext context, BasePacket payload) {
457 checkNotNull(payload, "DHCP6 payload can't be null");
458 checkState(payload instanceof DHCP6, "Payload is not a DHCP6");
459 DHCP6 dhcp6Payload = (DHCP6) payload;
460 Ethernet receivedPacket = context.inPacket().parsed();
461
462 if (!configured()) {
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800463 log.warn("Missing DHCP6 relay server config. " +
464 "Abort packet processing dhcp6 payload {}", dhcp6Payload);
Kalhee Kim45fede42017-09-05 19:05:06 +0000465 return;
466 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000467 byte msgTypeVal = dhcp6Payload.getMsgType();
468 MsgType msgType = DHCP6.MsgType.getType(msgTypeVal);
469 log.debug("msgType is {}", msgType);
Kalhee Kim45fede42017-09-05 19:05:06 +0000470
Kalhee Kim45fede42017-09-05 19:05:06 +0000471 ConnectPoint inPort = context.inPacket().receivedFrom();
Jonathan Hart8ca2bc02017-11-30 18:23:42 -0800472
Kalhee Kim495c9b22017-11-07 16:32:09 +0000473 if (inPort == null) {
474 log.warn("incomming ConnectPoint is null");
475 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000476 Set<Interface> receivingInterfaces = interfaceService.getInterfacesByPort(inPort);
477 //ignore the packets if dhcp client interface is not configured on onos.
478 if (receivingInterfaces.isEmpty()) {
479 log.warn("Virtual interface is not configured on {}", inPort);
480 return;
481 }
482
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800483 if (msgTypeVal == DHCP6.MsgType.LEASEQUERY.value()) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000484 List<InternalPacket> ethernetClientPacket =
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800485 processLQ6PacketFromClient(context, receivedPacket, receivingInterfaces, dhcp6Payload);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000486 for (InternalPacket internalPacket : ethernetClientPacket) {
487 forwardPacket(internalPacket);
Kalhee Kim45fede42017-09-05 19:05:06 +0000488 }
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800489 } else if (msgTypeVal == DHCP6.MsgType.LEASEQUERY_REPLY.value()) {
490
491 IPv6 clientIpv6 = (IPv6) receivedPacket.getPayload();
492 UDP clientUdp = (UDP) clientIpv6.getPayload();
493 DHCP6 clientDhcp6 = (DHCP6) clientUdp.getPayload();
494 Interface serverInterface = dhcp6HandlerUtil.directlyConnected(clientDhcp6) ?
495 getServerInterface() : getIndirectServerInterface();
Kalhee Kim45fede42017-09-05 19:05:06 +0000496 InternalPacket ethernetPacketReply =
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800497 dhcp6HandlerUtil.processLQ6PacketFromServer(
498 defaultServerInfoList, indirectServerInfoList,
499 serverInterface, interfaceService,
500 hostService,
501 context, receivedPacket, receivingInterfaces);
Kalhee Kim45fede42017-09-05 19:05:06 +0000502 if (ethernetPacketReply != null) {
503 forwardPacket(ethernetPacketReply);
504 }
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800505 handleLeaseQuery6ReplyMsg(context, dhcp6Payload);
Kalhee Kim45fede42017-09-05 19:05:06 +0000506 } else {
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800507 if (MSG_TYPE_FROM_CLIENT.contains(msgTypeVal)) {
508
509 List<InternalPacket> ethernetClientPacket =
510 processDhcp6PacketFromClient(context, receivedPacket, receivingInterfaces);
511 for (InternalPacket internalPacket : ethernetClientPacket) {
512 forwardPacket(internalPacket);
513 }
514 } else if (MSG_TYPE_FROM_SERVER.contains(msgTypeVal)) {
515 log.debug("calling processDhcp6PacketFromServer with RELAY_REPL {}", msgTypeVal);
516 InternalPacket ethernetPacketReply =
517 processDhcp6PacketFromServer(context, receivedPacket, receivingInterfaces);
518 if (ethernetPacketReply != null) {
519 forwardPacket(ethernetPacketReply);
520 }
521 } else {
522 log.warn("Not so fast, packet type {} not supported yet", msgTypeVal);
523 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000524 }
525 }
526
Kalhee Kim45fede42017-09-05 19:05:06 +0000527 /**
528 * Checks if this app has been configured.
529 *
530 * @return true if all information we need have been initialized
531 */
532 public boolean configured() {
Yi Tseng919b2df2017-09-07 16:22:51 -0700533 return !defaultServerInfoList.isEmpty();
Kalhee Kim45fede42017-09-05 19:05:06 +0000534 }
535
Yi Tsengaa417a62017-09-08 17:22:51 -0700536 @Override
537 public ProviderId id() {
Charles Chand988c282017-09-12 17:09:32 -0700538 return PROVIDER_ID;
Yi Tsengaa417a62017-09-08 17:22:51 -0700539 }
540
541 @Override
542 public void triggerProbe(Host host) {
543 // Do nothing here
544 }
545
Kalhee Kim45fede42017-09-05 19:05:06 +0000546 //forward the packet to ConnectPoint where the DHCP server is attached.
547 private void forwardPacket(InternalPacket packet) {
548 //send Packetout to dhcp server connectpoint.
549 if (packet.destLocation != null) {
550 TrafficTreatment t = DefaultTrafficTreatment.builder()
551 .setOutput(packet.destLocation.port()).build();
552 OutboundPacket o = new DefaultOutboundPacket(
553 packet.destLocation.deviceId(), t, ByteBuffer.wrap(packet.packet.serialize()));
554 if (log.isTraceEnabled()) {
555 log.trace("Relaying packet to destination {}", packet.destLocation);
556 }
557 packetService.emit(o);
558 } // if
559 }
560
Kalhee Kim45fede42017-09-05 19:05:06 +0000561 /**
562 * extract from dhcp6 packet client ipv6 address of given by dhcp server.
563 *
564 * @param dhcp6 the dhcp6 packet
Kalhee Kim495c9b22017-11-07 16:32:09 +0000565 * @return IpAddressInfo IpAddressInfo given by dhcp server, or null if not exists
Kalhee Kim45fede42017-09-05 19:05:06 +0000566 */
Kalhee Kim495c9b22017-11-07 16:32:09 +0000567 private IpAddressInfo extractIpAddress(DHCP6 dhcp6) {
568 IpAddressInfo ipInfo = new IpAddressInfo();
Kalhee Kim45fede42017-09-05 19:05:06 +0000569
570 log.debug("extractIpAddress enters dhcp6 {}.", dhcp6);
571 // Extract IPv6 address from IA NA ot IA TA option
572 Optional<Dhcp6IaNaOption> iaNaOption = dhcp6.getOptions()
573 .stream()
574 .filter(opt -> opt instanceof Dhcp6IaNaOption)
575 .map(opt -> (Dhcp6IaNaOption) opt)
576 .findFirst();
577 Optional<Dhcp6IaTaOption> iaTaOption = dhcp6.getOptions()
578 .stream()
579 .filter(opt -> opt instanceof Dhcp6IaTaOption)
580 .map(opt -> (Dhcp6IaTaOption) opt)
581 .findFirst();
582 Optional<Dhcp6IaAddressOption> iaAddressOption;
583 if (iaNaOption.isPresent()) {
584 log.debug("Found IPv6 address from iaNaOption {}", iaNaOption);
585
586 iaAddressOption = iaNaOption.get().getOptions().stream()
587 .filter(opt -> opt instanceof Dhcp6IaAddressOption)
588 .map(opt -> (Dhcp6IaAddressOption) opt)
589 .findFirst();
590 } else if (iaTaOption.isPresent()) {
591 log.debug("Found IPv6 address from iaTaOption {}", iaTaOption);
592
593 iaAddressOption = iaTaOption.get().getOptions().stream()
594 .filter(opt -> opt instanceof Dhcp6IaAddressOption)
595 .map(opt -> (Dhcp6IaAddressOption) opt)
596 .findFirst();
597 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000598 log.info("No IPv6 address found from iaTaOption {}", iaTaOption);
Kalhee Kim45fede42017-09-05 19:05:06 +0000599 iaAddressOption = Optional.empty();
600 }
601 if (iaAddressOption.isPresent()) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000602 ipInfo.ip6Address = iaAddressOption.get().getIp6Address();
603 ipInfo.prefTime = iaAddressOption.get().getPreferredLifetime();
Kalhee Kim45fede42017-09-05 19:05:06 +0000604 log.debug("Found IPv6 address from iaAddressOption {}", iaAddressOption);
Kalhee Kim45fede42017-09-05 19:05:06 +0000605 } else {
606 log.debug("Can't find IPv6 address from DHCPv6 {}", dhcp6);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000607 return null;
Kalhee Kim45fede42017-09-05 19:05:06 +0000608 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000609 return ipInfo;
Kalhee Kim45fede42017-09-05 19:05:06 +0000610 }
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800611
Kalhee Kim45fede42017-09-05 19:05:06 +0000612 /**
613 * extract from dhcp6 packet Prefix prefix provided by dhcp server.
614 *
615 * @param dhcp6 the dhcp6 payload
616 * @return IpPrefix Prefix Delegation prefix, or null if not exists.
617 */
Kalhee Kim495c9b22017-11-07 16:32:09 +0000618 private PdPrefixInfo extractPrefix(DHCP6 dhcp6) {
619 log.debug("extractPrefix enters {}", dhcp6);
Kalhee Kim45fede42017-09-05 19:05:06 +0000620
621 // extract prefix
Kalhee Kim495c9b22017-11-07 16:32:09 +0000622 PdPrefixInfo pdPrefixInfo = new PdPrefixInfo();
Kalhee Kim45fede42017-09-05 19:05:06 +0000623
624 Ip6Address prefixAddress = null;
625
626 // Extract IPv6 prefix from IA PD option
627 Optional<Dhcp6IaPdOption> iaPdOption = dhcp6.getOptions()
628 .stream()
629 .filter(opt -> opt instanceof Dhcp6IaPdOption)
630 .map(opt -> (Dhcp6IaPdOption) opt)
631 .findFirst();
632
633 Optional<Dhcp6IaPrefixOption> iaPrefixOption;
634 if (iaPdOption.isPresent()) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000635 log.debug("IA_PD option found {}", iaPdOption);
Kalhee Kim45fede42017-09-05 19:05:06 +0000636
637 iaPrefixOption = iaPdOption.get().getOptions().stream()
638 .filter(opt -> opt instanceof Dhcp6IaPrefixOption)
639 .map(opt -> (Dhcp6IaPrefixOption) opt)
640 .findFirst();
641 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000642 log.debug("IA_PD option NOT found");
Kalhee Kim45fede42017-09-05 19:05:06 +0000643
644 iaPrefixOption = Optional.empty();
645 }
646 if (iaPrefixOption.isPresent()) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000647 log.debug("IAPrefix Option within IA_PD option found {}", iaPrefixOption);
Kalhee Kim45fede42017-09-05 19:05:06 +0000648
649 prefixAddress = iaPrefixOption.get().getIp6Prefix();
650 int prefixLen = (int) iaPrefixOption.get().getPrefixLength();
Kalhee Kim495c9b22017-11-07 16:32:09 +0000651 log.debug("Prefix length is {} bits", prefixLen);
652 pdPrefixInfo.pdPrefix = IpPrefix.valueOf(prefixAddress, prefixLen);
653 pdPrefixInfo.prefTime = iaPrefixOption.get().getPreferredLifetime();
Kalhee Kim45fede42017-09-05 19:05:06 +0000654 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000655 log.debug("Can't find IPv6 prefix from DHCPv6 {}", dhcp6);
656 return null;
Kalhee Kim45fede42017-09-05 19:05:06 +0000657 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000658 return pdPrefixInfo;
Kalhee Kim45fede42017-09-05 19:05:06 +0000659 }
660
661 /**
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000662 * extract from dhcp6 packet ClientIdOption.
663 *
664 * @param directConnFlag directly connected host
665 * @param dhcp6Payload the dhcp6 payload
666 * @return Dhcp6ClientIdOption clientIdOption, or null if not exists.
667 */
668 private Dhcp6ClientIdOption extractClinedId(Boolean directConnFlag, DHCP6 dhcp6Payload) {
669 Dhcp6ClientIdOption clientIdOption;
670
671 if (directConnFlag) {
672 clientIdOption = dhcp6Payload.getOptions()
673 .stream()
674 .filter(opt -> opt instanceof Dhcp6ClientIdOption)
675 .map(opt -> (Dhcp6ClientIdOption) opt)
676 .findFirst()
677 .orElse(null);
678 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000679 DHCP6 leafDhcp = dhcp6HandlerUtil.getDhcp6Leaf(dhcp6Payload);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000680 clientIdOption = leafDhcp.getOptions()
681 .stream()
682 .filter(opt -> opt instanceof Dhcp6ClientIdOption)
683 .map(opt -> (Dhcp6ClientIdOption) opt)
684 .findFirst()
685 .orElse(null);
686 }
687
688 return clientIdOption;
689 }
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800690
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000691 /**
Kalhee Kim495c9b22017-11-07 16:32:09 +0000692 * remove host or route and update dhcp relay record attributes.
Kalhee Kim45fede42017-09-05 19:05:06 +0000693 *
694 * @param directConnFlag flag to show that packet is from directly connected client
Kalhee Kim495c9b22017-11-07 16:32:09 +0000695 * @param location client side connect point
Kalhee Kim45fede42017-09-05 19:05:06 +0000696 * @param dhcp6Packet the dhcp6 payload
697 * @param clientPacket client's ethernet packet
698 * @param clientIpv6 client's Ipv6 packet
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +0000699 * @param clientInterface client interfaces
Kalhee Kim45fede42017-09-05 19:05:06 +0000700 */
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000701 private void removeHostOrRoute(boolean directConnFlag, ConnectPoint location,
702 DHCP6 dhcp6Packet,
Kalhee Kim45fede42017-09-05 19:05:06 +0000703 Ethernet clientPacket, IPv6 clientIpv6,
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +0000704 Interface clientInterface) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000705 log.debug("removeHostOrRoute enters {}", dhcp6Packet);
Kalhee Kim121ba922017-11-01 17:56:44 +0000706 VlanId vlanId = clientInterface.vlan();
Kalhee Kim495c9b22017-11-07 16:32:09 +0000707 MacAddress srcMac = clientPacket.getSourceMAC(); // could be gw or host
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000708 MacAddress leafClientMac;
709 byte leafMsgType;
Kalhee Kim495c9b22017-11-07 16:32:09 +0000710 log.debug("client mac {} client vlan {}", HexString.toHexString(srcMac.toBytes(), ":"), vlanId);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000711
712 Dhcp6ClientIdOption clientIdOption = extractClinedId(directConnFlag, dhcp6Packet);
713 if (clientIdOption != null) {
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000714 if ((clientIdOption.getDuid().getDuidType() == Dhcp6Duid.DuidType.DUID_LLT) ||
715 (clientIdOption.getDuid().getDuidType() == Dhcp6Duid.DuidType.DUID_LL)) {
716 leafClientMac = MacAddress.valueOf(clientIdOption.getDuid().getLinkLayerAddress());
717 } else {
718 log.warn("Link-Layer Address not supported in CLIENTID option. No DhcpRelay Record created.");
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000719 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_LINKLOCAL_FAIL);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000720 return;
721 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000722 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000723 log.warn("CLIENTID option NOT found. Don't create DhcpRelay Record.");
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000724 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_CLIENTID_FAIL);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000725 return;
726 }
727
Kalhee Kim495c9b22017-11-07 16:32:09 +0000728 HostId leafHostId = HostId.hostId(leafClientMac, vlanId);
729 DhcpRecord record = dhcpRelayStore.getDhcpRecord(leafHostId).orElse(null);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000730 if (record == null) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000731 record = new DhcpRecord(leafHostId);
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000732 } else {
733 record = record.clone();
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000734 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000735
Kalhee Kim495c9b22017-11-07 16:32:09 +0000736 Boolean isMsgRelease = dhcp6HandlerUtil.isDhcp6Release(dhcp6Packet);
737 IpAddressInfo ipInfo;
738 PdPrefixInfo pdInfo = null;
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000739 if (directConnFlag) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000740 // Add to host store if it is connected to network directly
741 ipInfo = extractIpAddress(dhcp6Packet);
742 if (ipInfo != null) {
743 if (isMsgRelease) {
744 HostId hostId = HostId.hostId(srcMac, vlanId);
745 log.debug("remove Host {} ip for directly connected.", hostId.toString());
746 providerService.removeIpFromHost(hostId, ipInfo.ip6Address);
Kalhee Kim45fede42017-09-05 19:05:06 +0000747 }
748 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000749 log.debug("ipAddress not found. Do not remove Host {} for directly connected.",
750 HostId.hostId(srcMac, vlanId).toString());
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000751 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000752 leafMsgType = dhcp6Packet.getMsgType();
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000753 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000754 // Remove from route store if it is not connected to network directly
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000755 // pick out the first link-local ip address
Kalhee Kim495c9b22017-11-07 16:32:09 +0000756 IpAddress nextHopIp = getFirstIpByHost(directConnFlag, srcMac, vlanId);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000757 if (nextHopIp == null) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000758 log.warn("Can't find link-local IP address of gateway mac {} vlanId {}", srcMac, vlanId);
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000759 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_LINKLOCAL_GW);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000760 return;
761 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000762
Kalhee Kim495c9b22017-11-07 16:32:09 +0000763 DHCP6 leafDhcp = dhcp6HandlerUtil.getDhcp6Leaf(dhcp6Packet);
764 ipInfo = extractIpAddress(leafDhcp);
765 if (ipInfo == null) {
766 log.debug("ip is null");
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000767 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000768 if (isMsgRelease) {
Kalhee Kim54a97c92018-04-02 21:23:46 +0000769 Route routeForIP = new Route(Route.Source.DHCP, ipInfo.ip6Address.toIpPrefix(), nextHopIp);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000770 log.debug("removing route of 128 address for indirectly connected.");
771 log.debug("128 ip {}, nexthop {}",
772 HexString.toHexString(ipInfo.ip6Address.toOctets(), ":"),
773 HexString.toHexString(nextHopIp.toOctets(), ":"));
774 routeStore.removeRoute(routeForIP);
Kalhee Kim45fede42017-09-05 19:05:06 +0000775 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000776 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000777
Kalhee Kim495c9b22017-11-07 16:32:09 +0000778 pdInfo = extractPrefix(leafDhcp);
779 if (pdInfo == null) {
780 log.debug("ipPrefix is null ");
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000781 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000782 if (isMsgRelease) {
Kalhee Kim54a97c92018-04-02 21:23:46 +0000783 Route routeForPrefix = new Route(Route.Source.DHCP, pdInfo.pdPrefix, nextHopIp);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000784 log.debug("removing route of PD for indirectly connected.");
785 log.debug("pd ip {}, nexthop {}",
786 HexString.toHexString(pdInfo.pdPrefix.address().toOctets(), ":"),
787 HexString.toHexString(nextHopIp.toOctets(), ":"));
788
789 routeStore.removeRoute(routeForPrefix);
Kalhee Kimba366062017-11-07 16:32:09 +0000790 if (this.dhcpFpmEnabled) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000791 dhcpFpmPrefixStore.removeFpmRecord(pdInfo.pdPrefix);
Kalhee Kimba366062017-11-07 16:32:09 +0000792 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000793 }
794 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000795 leafMsgType = leafDhcp.getMsgType();
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000796 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000797
Kalhee Kim495c9b22017-11-07 16:32:09 +0000798 if (isMsgRelease) {
799 log.debug("DHCP6 RELEASE msg.");
800 if (record != null) {
801 if (ipInfo != null) {
802 log.debug("DhcpRelay Record ip6Address is set to null.");
803 record.ip6Address(null);
804 }
805 if (pdInfo != null) {
806 log.debug("DhcpRelay Record pdPrefix is set to null.");
807 }
808
809 if (!record.ip6Address().isPresent() && !record.pdPrefix().isPresent()) {
810 log.warn("IP6 address and IP6 PD both are null. Remove record.");
811 // do not remove a record. Let timer task handler it.
812 //dhcpRelayStore.removeDhcpRecord(HostId.hostId(leafClientMac, vlanId));
813 }
814 }
815 }
816
Ray Milkeyffe1a332018-01-24 10:41:14 -0800817 if (record != null) {
818 record.getV6Counters().incrementCounter(dhcp6HandlerUtil.getMsgTypeStr(leafMsgType));
819 record.addLocation(new HostLocation(location, System.currentTimeMillis()));
820 record.ip6Status(DHCP6.MsgType.getType(leafMsgType));
821 record.setDirectlyConnected(directConnFlag);
822 if (!directConnFlag) {
823 // Update gateway mac address if the host is not directly connected
824 record.nextHop(srcMac);
825 }
826 record.updateLastSeen();
Kalhee Kim495c9b22017-11-07 16:32:09 +0000827 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000828 dhcpRelayStore.updateDhcpRecord(leafHostId, record);
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000829 // TODO Use AtomicInteger for the counters
830 try {
831 recordSemaphore.acquire();
832 try {
833 dhcpRelayCountersStore.incrementCounter(gCount, dhcp6HandlerUtil.getMsgTypeStr(leafMsgType));
834 } finally {
835 // calling release() after a successful acquire()
836 recordSemaphore.release();
837 }
838 } catch (InterruptedException e) {
Ray Milkeyba547f92018-02-01 15:22:31 -0800839 Thread.currentThread().interrupt();
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000840 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000841 }
842
843 /**
844 * add host or route and update dhcp relay record.
845 *
846 * @param directConnFlag flag to show that packet is from directly connected client
847 * @param location client side connect point
848 * @param dhcp6Relay the dhcp6 payload
849 * @param embeddedDhcp6 the dhcp6 payload within relay
850 * @param srcMac client gw/host macAddress
851 * @param clientInterface client interface
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800852 * @param dhcpServerIp6Address DHCP server IP
Kalhee Kim495c9b22017-11-07 16:32:09 +0000853 */
854 private void addHostOrRoute(boolean directConnFlag, ConnectPoint location, DHCP6 dhcp6Relay,
855 DHCP6 embeddedDhcp6, MacAddress srcMac, Interface clientInterface) {
856 log.debug("addHostOrRoute entered.");
857 VlanId vlanId = clientInterface.vlan();
858 Boolean isMsgReply = dhcp6HandlerUtil.isDhcp6Reply(dhcp6Relay);
859 MacAddress leafClientMac;
860 Byte leafMsgType;
861
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000862 Dhcp6ClientIdOption clientIdOption = extractClinedId(directConnFlag, embeddedDhcp6);
863 if (clientIdOption != null) {
864 log.debug("CLIENTID option found {}", clientIdOption);
865 if ((clientIdOption.getDuid().getDuidType() == Dhcp6Duid.DuidType.DUID_LLT) ||
866 (clientIdOption.getDuid().getDuidType() == Dhcp6Duid.DuidType.DUID_LL)) {
867 leafClientMac = MacAddress.valueOf(clientIdOption.getDuid().getLinkLayerAddress());
868 } else {
869 log.warn("Link-Layer Address not supported in CLIENTID option. No DhcpRelay Record created.");
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000870 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_LINKLOCAL_FAIL);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000871 return;
872 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000873 } else {
874 log.warn("CLIENTID option NOT found. No DhcpRelay Record created.");
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000875 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_CLIENTID_FAIL);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000876 return;
877 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000878 HostId leafHostId = HostId.hostId(leafClientMac, vlanId);
879 DhcpRecord record = dhcpRelayStore.getDhcpRecord(leafHostId).orElse(null);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000880 if (record == null) {
881 record = new DhcpRecord(HostId.hostId(leafClientMac, vlanId));
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000882 } else {
883 record = record.clone();
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000884 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000885
886 IpAddressInfo ipInfo;
887 PdPrefixInfo pdInfo = null;
888 if (directConnFlag) {
889 // Add to host store if it connect to network directly
890 ipInfo = extractIpAddress(embeddedDhcp6);
891 if (ipInfo != null) {
892 if (isMsgReply) {
893 Set<IpAddress> ips = Sets.newHashSet(ipInfo.ip6Address);
894 HostId hostId = HostId.hostId(srcMac, vlanId);
895 Host host = hostService.getHost(hostId);
896 HostLocation hostLocation = new HostLocation(clientInterface.connectPoint(),
897 System.currentTimeMillis());
898 Set<HostLocation> hostLocations = Sets.newHashSet(hostLocation);
899 if (host != null) {
900 // Dual homing support:
901 // if host exists, use old locations and new location
902 hostLocations.addAll(host.locations());
903 }
904 HostDescription desc = new DefaultHostDescription(srcMac, vlanId, hostLocations, ips,
905 false);
906 log.debug("adding Host for directly connected.");
907 log.debug("client mac {} client vlan {} hostlocation {}",
908 HexString.toHexString(srcMac.toBytes(), ":"), vlanId, hostLocation.toString());
909 // Replace the ip when dhcp server give the host new ip address
910 providerService.hostDetected(hostId, desc, false);
911 }
912 } else {
913 log.warn("ipAddress not found. Do not add Host {} for directly connected.",
914 HostId.hostId(srcMac, vlanId).toString());
915 }
916 leafMsgType = embeddedDhcp6.getMsgType();
917 } else {
918 // Add to route store if it does not connect to network directly
919 // pick out the first link-local ip address
920 IpAddress nextHopIp = getFirstIpByHost(directConnFlag, srcMac, vlanId);
921 if (nextHopIp == null) {
922 log.warn("Can't find link-local IP address of gateway mac {} vlanId {}", srcMac, vlanId);
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000923 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_LINKLOCAL_GW);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000924 return;
925 }
926
927 DHCP6 leafDhcp = dhcp6HandlerUtil.getDhcp6Leaf(embeddedDhcp6);
928 ipInfo = extractIpAddress(leafDhcp);
929 if (ipInfo == null) {
930 log.debug("ip is null");
931 } else {
932 if (isMsgReply) {
Kalhee Kim54a97c92018-04-02 21:23:46 +0000933 Route routeForIP = new Route(Route.Source.DHCP, ipInfo.ip6Address.toIpPrefix(), nextHopIp);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000934 log.debug("adding Route of 128 address for indirectly connected.");
935 routeStore.updateRoute(routeForIP);
936 }
937 }
938
939 pdInfo = extractPrefix(leafDhcp);
940 if (pdInfo == null) {
941 log.debug("ipPrefix is null ");
942 } else {
943 if (isMsgReply) {
Kalhee Kim54a97c92018-04-02 21:23:46 +0000944 Route routeForPrefix = new Route(Route.Source.DHCP, pdInfo.pdPrefix, nextHopIp);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000945 log.debug("adding Route of PD for indirectly connected.");
946 routeStore.updateRoute(routeForPrefix);
947 if (this.dhcpFpmEnabled) {
948 FpmRecord fpmRecord = new FpmRecord(pdInfo.pdPrefix, nextHopIp, FpmRecord.Type.DHCP_RELAY);
949 dhcpFpmPrefixStore.addFpmRecord(pdInfo.pdPrefix, fpmRecord);
950 }
951 }
952 }
953 leafMsgType = leafDhcp.getMsgType();
954 }
955 if (leafMsgType == DHCP6.MsgType.RELEASE.value() ||
956 (leafMsgType == DHCP6.MsgType.REPLY.value()) && ipInfo == null) {
957 log.warn("DHCP6 RELEASE/REPLY(null ip) from Server. MsgType {}", leafMsgType);
958 //return;
959 }
960
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000961 record.addLocation(new HostLocation(location, System.currentTimeMillis()));
Kalhee Kim2f07c602017-11-15 18:57:53 +0000962
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000963 if (leafMsgType == DHCP6.MsgType.REPLY.value()) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000964 if (ipInfo != null) {
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000965 log.debug("IP6 address is being stored into dhcp-relay store.");
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800966 log.debug("Client IP6 address {}", HexString.toHexString(ipInfo.ip6Address.toOctets(), ":"));
Kalhee Kim495c9b22017-11-07 16:32:09 +0000967 record.ip6Address(ipInfo.ip6Address);
Kalhee Kim2f07c602017-11-15 18:57:53 +0000968 record.updateAddrPrefTime(ipInfo.prefTime);
969 record.updateLastIp6Update();
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800970 } else {
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000971 log.debug("IP6 address is not returned from server. Maybe only PD is returned.");
972 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000973 if (pdInfo != null) {
974 log.debug("IP6 PD address {}",
975 HexString.toHexString(pdInfo.pdPrefix.address().toOctets(), ":"));
Kalhee Kim2f07c602017-11-15 18:57:53 +0000976 record.pdPrefix(pdInfo.pdPrefix);
977 record.updatePdPrefTime(pdInfo.prefTime);
978 record.updateLastPdUpdate();
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000979 } else {
980 log.debug("IP6 PD address is not returned from server. Maybe only IPAddress is returned.");
981 }
982 }
Kalhee Kim2f07c602017-11-15 18:57:53 +0000983
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000984 record.getV6Counters().incrementCounter(dhcp6HandlerUtil.getMsgTypeStr(leafMsgType));
Kalhee Kim495c9b22017-11-07 16:32:09 +0000985 record.ip6Status(DHCP6.MsgType.getType(leafMsgType));
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000986 record.setDirectlyConnected(directConnFlag);
987 record.updateLastSeen();
Kalhee Kim495c9b22017-11-07 16:32:09 +0000988 dhcpRelayStore.updateDhcpRecord(leafHostId, record);
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000989 // TODO Use AtomicInteger for the counters
990 try {
991 recordSemaphore.acquire();
992 try {
993 dhcpRelayCountersStore.incrementCounter(gCount, dhcp6HandlerUtil.getMsgTypeStr(leafMsgType));
994 } finally {
995 // calling release() after a successful acquire()
996 recordSemaphore.release();
997 }
998 } catch (InterruptedException e) {
Ray Milkeyba547f92018-02-01 15:22:31 -0800999 Thread.currentThread().interrupt();
Kalhee Kimd94ceea2017-11-29 19:03:02 +00001000 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001001 }
1002
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001003 private List<InternalPacket> processLQ6PacketFromClient(PacketContext context,
1004 Ethernet clientPacket,
1005 Set<Interface> clientInterfaces,
1006 DHCP6 dhcp6Payload) {
1007 ConnectPoint inPort = context.inPacket().receivedFrom();
1008 log.info("Got LQ-REQUEST V6 on port {}", inPort);
1009 List<Dhcp6Option> lopt = dhcp6Payload.getOptions();
1010 log.info("Options list: {}", lopt);
1011 Dhcp6LeaseQueryOption lqoption = dhcp6Payload.getOptions()
1012 .stream()
1013 .filter(opt -> opt instanceof Dhcp6LeaseQueryOption)
1014 .map(pld -> (Dhcp6LeaseQueryOption) pld)
1015 .findFirst()
1016 .orElse(null);
1017
1018 if (lqoption == null) {
1019 // Can't find dhcp payload
1020 log.warn("Can't find dhcp6 lease query message - aborting");
1021 return null;
1022 } else {
1023 log.info("dhcp6 lqv6 options found: {}", lqoption);
1024 }
1025 log.warn("LQv6 for " + lqoption.linkAddress.toString() + " comes from " + inPort.toString());
1026 Ethernet packet = context.inPacket().parsed();
1027 Ip6Address clientAddress = lqoption.linkAddress;
1028 IPv6 ipv6Packet = (IPv6) packet.getPayload();
1029 Ip6Address nextHopIp = findNextHopIp6FromRelayStore(clientAddress);
1030
1031 // 1. only if there is a route to remove - remove it
1032 if (nextHopIp != null) {
Kalhee Kim54a97c92018-04-02 21:23:46 +00001033 Route routeForIP6 = new Route(Route.Source.DHCP, clientAddress.toIpPrefix(), nextHopIp);
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001034 log.debug("Removing route of Client " + clientAddress +
1035 " for indirectly connected - next hop ip6 " + nextHopIp);
1036 routeStore.removeRoute(routeForIP6);
1037 }
1038
1039 // 2. note the potential NH this packet came from in case it's a known lease
1040 // this NH will then be used to build the route
1041 MacAddress potentialNH = packet.getSourceMAC();
1042 VlanId vlanId = VlanId.vlanId(packet.getVlanID());
1043 setPotentialNextHopForIp6InRelayStore(clientAddress, vlanId, potentialNH);
1044
1045 // 3. route this LQ6 to all relevant servers
1046 IPv6 clientIpv6 = (IPv6) clientPacket.getPayload();
1047 UDP clientUdp = (UDP) clientIpv6.getPayload();
1048 DHCP6 clientDhcp6 = (DHCP6) clientUdp.getPayload();
1049
1050 boolean directConnFlag = dhcp6HandlerUtil.directlyConnected(clientDhcp6);
1051
1052 ConnectPoint clientConnectionPoint = context.inPacket().receivedFrom();
1053 VlanId vlanIdInUse = VlanId.vlanId(clientPacket.getVlanID());
1054 Interface clientInterface = interfaceService.getInterfacesByPort(clientConnectionPoint)
1055 .stream().filter(iface -> dhcp6HandlerUtil.interfaceContainsVlan(iface, vlanIdInUse))
1056 .findFirst()
1057 .orElse(null);
1058
1059 List<InternalPacket> internalPackets = new ArrayList<>();
1060 List<DhcpServerInfo> serverInfoList = findValidServerInfo(directConnFlag);
1061 List<DhcpServerInfo> copyServerInfoList = new ArrayList<DhcpServerInfo>(serverInfoList);
1062
1063 for (DhcpServerInfo serverInfo : copyServerInfoList) {
1064 if (!dhcp6HandlerUtil.checkDhcpServerConnPt(directConnFlag, serverInfo)) {
1065 log.warn("Can't get server connect point, ignore");
1066 continue;
1067 }
1068 DhcpServerInfo newServerInfo = getHostInfoForServerInfo(serverInfo, serverInfoList);
1069 if (newServerInfo == null) {
1070 log.warn("Can't get server interface with host info resolved, ignore");
1071 continue;
1072 }
1073
1074 Interface serverInterface = getServerInterface(newServerInfo);
1075 if (serverInterface == null) {
1076 log.warn("Can't get server interface, ignore");
1077 continue;
1078 }
1079
1080
1081 Ethernet etherRouted = (Ethernet) clientPacket.clone();
1082 MacAddress macFacingServer = serverInterface.mac();
1083 if (macFacingServer == null) {
1084 log.warn("No MAC address for server Interface {}", serverInterface);
1085 return null;
1086 }
1087 etherRouted.setSourceMACAddress(macFacingServer);
1088 etherRouted.setDestinationMACAddress(newServerInfo.getDhcpConnectMac().get());
1089 InternalPacket internalPacket =
1090 new Dhcp6HandlerUtil().new InternalPacket(etherRouted,
1091 serverInfo.getDhcpServerConnectPoint().get());
1092 internalPackets.add(internalPacket);
1093 log.debug("Sending LQ to DHCP server {}", newServerInfo.getDhcpServerIp6());
1094 }
1095 log.debug("num of client packets to send is{}", internalPackets.size());
1096
1097 return internalPackets;
1098 }
1099
Kalhee Kim45fede42017-09-05 19:05:06 +00001100 /**
Kalhee Kim495c9b22017-11-07 16:32:09 +00001101 * build the DHCP6 solicit/request packet with gatewayip.
Kalhee Kim45fede42017-09-05 19:05:06 +00001102 *
1103 * @param context packet context
1104 * @param clientPacket client ethernet packet
1105 * @param clientInterfaces set of client side interfaces
1106 */
Kalhee Kim495c9b22017-11-07 16:32:09 +00001107 private List<InternalPacket> processDhcp6PacketFromClient(PacketContext context,
1108 Ethernet clientPacket,
1109 Set<Interface> clientInterfaces) {
Yi Tseng3bd57ac2017-11-29 14:39:18 -08001110 ConnectPoint receivedFrom = context.inPacket().receivedFrom();
1111 DeviceId receivedFromDevice = receivedFrom.deviceId();
Kalhee Kim495c9b22017-11-07 16:32:09 +00001112 Ip6Address relayAgentIp = dhcp6HandlerUtil.getRelayAgentIPv6Address(clientInterfaces);
Yi Tseng3bd57ac2017-11-29 14:39:18 -08001113 MacAddress relayAgentMac = clientInterfaces.iterator().next().mac();
1114 if (relayAgentIp == null || relayAgentMac == null) {
1115 log.warn("Missing DHCP relay agent interface Ipv6 addr config for "
Kalhee Kim495c9b22017-11-07 16:32:09 +00001116 + "packet from client on port: {}. Aborting packet processing",
1117 clientInterfaces.iterator().next().connectPoint());
Kalhee Kimd94ceea2017-11-29 19:03:02 +00001118 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_CLIENT_INTF_MAC);
Charles Chan239492c2018-01-22 13:27:28 -08001119 return Lists.newArrayList();
Kalhee Kim121ba922017-11-01 17:56:44 +00001120 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001121
Kalhee Kim495c9b22017-11-07 16:32:09 +00001122 IPv6 clientIpv6 = (IPv6) clientPacket.getPayload();
1123 UDP clientUdp = (UDP) clientIpv6.getPayload();
1124 DHCP6 clientDhcp6 = (DHCP6) clientUdp.getPayload();
1125
1126 boolean directConnFlag = dhcp6HandlerUtil.directlyConnected(clientDhcp6);
Charles Chana990ce92017-10-30 10:22:50 -07001127
Kalhee Kim121ba922017-11-01 17:56:44 +00001128 ConnectPoint clientConnectionPoint = context.inPacket().receivedFrom();
1129 VlanId vlanIdInUse = VlanId.vlanId(clientPacket.getVlanID());
1130 Interface clientInterface = interfaceService.getInterfacesByPort(clientConnectionPoint)
Kalhee Kim495c9b22017-11-07 16:32:09 +00001131 .stream().filter(iface -> dhcp6HandlerUtil.interfaceContainsVlan(iface, vlanIdInUse))
1132 .findFirst()
1133 .orElse(null);
Kalhee Kim45fede42017-09-05 19:05:06 +00001134
Kalhee Kim495c9b22017-11-07 16:32:09 +00001135 List<InternalPacket> internalPackets = new ArrayList<>();
1136 List<DhcpServerInfo> serverInfoList = findValidServerInfo(directConnFlag);
1137 List<DhcpServerInfo> copyServerInfoList = new ArrayList<DhcpServerInfo>(serverInfoList);
Kalhee Kim45b24182017-10-18 18:30:23 +00001138
Kalhee Kim495c9b22017-11-07 16:32:09 +00001139 for (DhcpServerInfo serverInfo : copyServerInfoList) {
1140 if (!dhcp6HandlerUtil.checkDhcpServerConnPt(directConnFlag, serverInfo)) {
1141 log.warn("Can't get server connect point, ignore");
1142 continue;
1143 }
1144 DhcpServerInfo newServerInfo = getHostInfoForServerInfo(serverInfo, serverInfoList);
1145 if (newServerInfo == null) {
1146 log.warn("Can't get server interface with host info resolved, ignore");
1147 continue;
1148 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001149
Kalhee Kim495c9b22017-11-07 16:32:09 +00001150 Interface serverInterface = getServerInterface(newServerInfo);
1151 if (serverInterface == null) {
1152 log.warn("Can't get server interface, ignore");
1153 continue;
1154 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +00001155
Kalhee Kim495c9b22017-11-07 16:32:09 +00001156 Ethernet etherReply = dhcp6HandlerUtil.buildDhcp6PacketFromClient(context, clientPacket,
1157 clientInterfaces, newServerInfo, serverInterface);
1158 removeHostOrRoute(directConnFlag, clientConnectionPoint, clientDhcp6, clientPacket,
1159 clientIpv6, clientInterface);
Kalhee Kimea4b6c22017-11-09 14:38:37 +00001160
Kalhee Kim495c9b22017-11-07 16:32:09 +00001161 InternalPacket internalPacket = new Dhcp6HandlerUtil().new InternalPacket(etherReply,
1162 serverInfo.getDhcpServerConnectPoint().get());
1163 internalPackets.add(internalPacket);
1164 }
1165 log.debug("num of client packets to send is{}", internalPackets.size());
Kalhee Kimea4b6c22017-11-09 14:38:37 +00001166
Kalhee Kim495c9b22017-11-07 16:32:09 +00001167 return internalPackets;
1168 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001169
1170 /**
Kalhee Kim45fede42017-09-05 19:05:06 +00001171 * process the DHCP6 relay-reply packet from dhcp server.
1172 *
1173 * @param context packet context
1174 * @param receivedPacket server ethernet packet
1175 * @param recevingInterfaces set of server side interfaces
Kalhee Kim495c9b22017-11-07 16:32:09 +00001176 * @return internalPacket toward client
Kalhee Kim45fede42017-09-05 19:05:06 +00001177 */
1178 private InternalPacket processDhcp6PacketFromServer(PacketContext context,
1179 Ethernet receivedPacket, Set<Interface> recevingInterfaces) {
Kalhee Kim45fede42017-09-05 19:05:06 +00001180 // get dhcp6 header.
Ray Milkeyf0c47612017-09-28 11:29:38 -07001181 Ethernet etherReply = receivedPacket.duplicate();
Kalhee Kim45fede42017-09-05 19:05:06 +00001182 IPv6 ipv6Packet = (IPv6) etherReply.getPayload();
1183 UDP udpPacket = (UDP) ipv6Packet.getPayload();
1184 DHCP6 dhcp6Relay = (DHCP6) udpPacket.getPayload();
Kalhee Kim495c9b22017-11-07 16:32:09 +00001185 Boolean directConnFlag = dhcp6HandlerUtil.directlyConnected(dhcp6Relay);
Kalhee Kim45fede42017-09-05 19:05:06 +00001186
Kalhee Kim495c9b22017-11-07 16:32:09 +00001187 DHCP6 embeddedDhcp6 = dhcp6Relay.getOptions().stream()
1188 .filter(opt -> opt instanceof Dhcp6RelayOption)
1189 .map(BasePacket::getPayload)
1190 .map(pld -> (DHCP6) pld)
1191 .findFirst()
1192 .orElse(null);
Kalhee Kimd21029f2017-09-26 20:21:53 +00001193 ConnectPoint inPort = context.inPacket().receivedFrom();
Kalhee Kim495c9b22017-11-07 16:32:09 +00001194 DhcpServerInfo foundServerInfo = findServerInfoFromServer(directConnFlag, inPort);
Kalhee Kimd21029f2017-09-26 20:21:53 +00001195
Kalhee Kim495c9b22017-11-07 16:32:09 +00001196 if (foundServerInfo == null) {
1197 log.warn("Cannot find server info");
Kalhee Kimd94ceea2017-11-29 19:03:02 +00001198 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_SERVER_INFO);
Kalhee Kimd21029f2017-09-26 20:21:53 +00001199 return null;
Kalhee Kim495c9b22017-11-07 16:32:09 +00001200 } else {
1201 if (dhcp6HandlerUtil.isServerIpEmpty(foundServerInfo)) {
1202 log.warn("Cannot find server info's ipaddress");
Kalhee Kimd94ceea2017-11-29 19:03:02 +00001203 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_SERVER_IP6ADDR);
Kalhee Kim495c9b22017-11-07 16:32:09 +00001204 return null;
1205 }
Kalhee Kimd21029f2017-09-26 20:21:53 +00001206 }
1207
Kalhee Kim45fede42017-09-05 19:05:06 +00001208 Dhcp6InterfaceIdOption interfaceIdOption = dhcp6Relay.getOptions().stream()
1209 .filter(opt -> opt instanceof Dhcp6InterfaceIdOption)
1210 .map(opt -> (Dhcp6InterfaceIdOption) opt)
1211 .findFirst()
1212 .orElse(null);
Kalhee Kim45fede42017-09-05 19:05:06 +00001213 if (interfaceIdOption == null) {
1214 log.warn("Interface Id option is not present, abort packet...");
Kalhee Kimd94ceea2017-11-29 19:03:02 +00001215 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.OPTION_MISSING_FAIL);
Kalhee Kim45fede42017-09-05 19:05:06 +00001216 return null;
1217 }
1218
1219 MacAddress peerMac = interfaceIdOption.getMacAddress();
1220 String clientConnectionPointStr = new String(interfaceIdOption.getInPort());
Kalhee Kim45fede42017-09-05 19:05:06 +00001221 ConnectPoint clientConnectionPoint = ConnectPoint.deviceConnectPoint(clientConnectionPointStr);
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +00001222 VlanId vlanIdInUse = VlanId.vlanId(interfaceIdOption.getVlanId());
1223 Interface clientInterface = interfaceService.getInterfacesByPort(clientConnectionPoint)
Kalhee Kim495c9b22017-11-07 16:32:09 +00001224 .stream().filter(iface -> dhcp6HandlerUtil.interfaceContainsVlan(iface, vlanIdInUse))
1225 .findFirst().orElse(null);
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +00001226 if (clientInterface == null) {
1227 log.warn("Cannot get client interface for from packet, abort... vlan {}", vlanIdInUse.toString());
Kalhee Kimd94ceea2017-11-29 19:03:02 +00001228 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_MATCHING_INTF);
Kalhee Kim45fede42017-09-05 19:05:06 +00001229 return null;
1230 }
Kalhee Kim44fbda12018-04-03 21:08:18 +00001231 etherReply.setVlanID(vlanIdInUse.toShort());
1232
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +00001233 MacAddress relayAgentMac = clientInterface.mac();
Kalhee Kim45fede42017-09-05 19:05:06 +00001234 if (relayAgentMac == null) {
Yi Tseng68ef26b2017-12-18 17:10:00 -08001235 log.warn("Can not get client interface mac, abort packet..");
Kalhee Kimd94ceea2017-11-29 19:03:02 +00001236 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_CLIENT_INTF_MAC);
Kalhee Kim45fede42017-09-05 19:05:06 +00001237 return null;
1238 }
1239 etherReply.setSourceMACAddress(relayAgentMac);
1240
1241 // find destMac
Yi Tseng68ef26b2017-12-18 17:10:00 -08001242 MacAddress clientMac;
Yi Tsengaa417a62017-09-08 17:22:51 -07001243 Ip6Address peerAddress = Ip6Address.valueOf(dhcp6Relay.getPeerAddress());
1244 Set<Host> clients = hostService.getHostsByIp(peerAddress);
Kalhee Kim45fede42017-09-05 19:05:06 +00001245 if (clients.isEmpty()) {
Yi Tseng68ef26b2017-12-18 17:10:00 -08001246 log.trace("There's no host found for this address {}",
Kalhee Kim45fede42017-09-05 19:05:06 +00001247 HexString.toHexString(dhcp6Relay.getPeerAddress(), ":"));
Yi Tseng68ef26b2017-12-18 17:10:00 -08001248 log.trace("Let's look up interfaceId {}", HexString.toHexString(peerMac.toBytes(), ":"));
Kalhee Kim45fede42017-09-05 19:05:06 +00001249 clientMac = peerMac;
1250 } else {
1251 clientMac = clients.iterator().next().mac();
1252 if (clientMac == null) {
1253 log.warn("No client mac address found, abort packet...");
Kalhee Kimd94ceea2017-11-29 19:03:02 +00001254 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_CLIENT_INTF_MAC);
Kalhee Kim45fede42017-09-05 19:05:06 +00001255 return null;
1256 }
Yi Tseng68ef26b2017-12-18 17:10:00 -08001257 log.trace("Client mac address found from getHostByIp");
Kalhee Kim45fede42017-09-05 19:05:06 +00001258 }
1259 etherReply.setDestinationMACAddress(clientMac);
Kalhee Kim45fede42017-09-05 19:05:06 +00001260 // ip header
1261 ipv6Packet.setSourceAddress(dhcp6Relay.getLinkAddress());
1262 ipv6Packet.setDestinationAddress(dhcp6Relay.getPeerAddress());
1263 // udp header
1264 udpPacket.setSourcePort(UDP.DHCP_V6_SERVER_PORT);
1265 if (directConnFlag) {
1266 udpPacket.setDestinationPort(UDP.DHCP_V6_CLIENT_PORT);
1267 } else {
1268 udpPacket.setDestinationPort(UDP.DHCP_V6_SERVER_PORT);
1269 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001270 // add host or route
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001271 addHostOrRoute(directConnFlag, clientConnectionPoint, dhcp6Relay, embeddedDhcp6,
1272 clientMac, clientInterface);
1273
Kalhee Kim45fede42017-09-05 19:05:06 +00001274
1275 udpPacket.setPayload(embeddedDhcp6);
1276 udpPacket.resetChecksum();
1277 ipv6Packet.setPayload(udpPacket);
1278 etherReply.setPayload(ipv6Packet);
Kalhee Kim495c9b22017-11-07 16:32:09 +00001279 return new Dhcp6HandlerUtil().new InternalPacket(etherReply, clientConnectionPoint);
Kalhee Kim45fede42017-09-05 19:05:06 +00001280 }
1281
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001282 // Returns the first v6 interface ip out of a set of interfaces or null.
1283 // Checks all interfaces, and ignores v6 interface ips
1284 private Ip6Address getRelayAgentIPv6Address(Set<Interface> intfs) {
1285 for (Interface intf : intfs) {
1286 for (InterfaceIpAddress ip : intf.ipAddressesList()) {
1287 Ip6Address relayAgentIp = ip.ipAddress().getIp6Address();
1288 if (relayAgentIp != null) {
1289 return relayAgentIp;
1290 }
1291 }
1292 }
1293 return null;
1294 }
Yi Tseng483ac6f2017-08-02 15:03:31 -07001295
1296 @Override
Kalhee Kimba366062017-11-07 16:32:09 +00001297 public void setDhcpFpmEnabled(Boolean enabled) {
1298 dhcpFpmEnabled = enabled;
1299 }
1300
1301 @Override
Yi Tseng483ac6f2017-08-02 15:03:31 -07001302 public void setDefaultDhcpServerConfigs(Collection<DhcpServerConfig> configs) {
Kalhee Kim495c9b22017-11-07 16:32:09 +00001303 log.debug("setDefaultDhcpServerConfigs is called.");
Yi Tseng919b2df2017-09-07 16:22:51 -07001304 setDhcpServerConfigs(configs, defaultServerInfoList);
Yi Tseng919b2df2017-09-07 16:22:51 -07001305 }
1306
1307 @Override
1308 public void setIndirectDhcpServerConfigs(Collection<DhcpServerConfig> configs) {
Kalhee Kim495c9b22017-11-07 16:32:09 +00001309 log.debug("setIndirectDhcpServerConfigs is called.");
Yi Tseng919b2df2017-09-07 16:22:51 -07001310 setDhcpServerConfigs(configs, indirectServerInfoList);
Yi Tseng919b2df2017-09-07 16:22:51 -07001311 }
1312
1313 public void setDhcpServerConfigs(Collection<DhcpServerConfig> configs, List<DhcpServerInfo> serverInfoList) {
Kalhee Kim495c9b22017-11-07 16:32:09 +00001314 log.debug("config size {}.", configs.size());
1315
Kalhee Kim45fede42017-09-05 19:05:06 +00001316 if (configs.size() == 0) {
1317 // no config to update
1318 return;
1319 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001320 // TODO: currently we pick up first DHCP server config.
1321 // Will use other server configs in the future for HA.
Kalhee Kim495c9b22017-11-07 16:32:09 +00001322 Boolean isConfigValid = false;
1323 for (DhcpServerConfig serverConfig : configs) {
1324 if (serverConfig.getDhcpServerIp6().isPresent()) {
1325 isConfigValid = true;
1326 break;
1327 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001328 }
Kalhee Kim495c9b22017-11-07 16:32:09 +00001329 if (!isConfigValid) {
1330 log.warn("No IP V6 server address found.");
1331 return; // No IP V6 address found
1332 }
1333 for (DhcpServerInfo oldServerInfo : serverInfoList) {
Yi Tseng919b2df2017-09-07 16:22:51 -07001334 // stop monitoring gateway or server
1335 oldServerInfo.getDhcpGatewayIp6().ifPresent(gatewayIp -> {
1336 hostService.stopMonitoringIp(gatewayIp);
1337 });
1338 oldServerInfo.getDhcpServerIp6().ifPresent(serverIp -> {
1339 hostService.stopMonitoringIp(serverIp);
Yi Tseng525ff402017-10-23 19:39:39 -07001340 cancelDhcpPacket(serverIp);
Yi Tseng919b2df2017-09-07 16:22:51 -07001341 });
1342 }
Kalhee Kim495c9b22017-11-07 16:32:09 +00001343 serverInfoList.clear();
1344 for (DhcpServerConfig serverConfig : configs) {
1345 // Create new server info according to the config
1346 DhcpServerInfo newServerInfo = new DhcpServerInfo(serverConfig,
1347 DhcpServerInfo.Version.DHCP_V6);
1348 checkState(newServerInfo.getDhcpServerConnectPoint().isPresent(),
1349 "Connect point not exists");
1350 checkState(newServerInfo.getDhcpServerIp6().isPresent(),
1351 "IP of DHCP server not exists");
Yi Tseng919b2df2017-09-07 16:22:51 -07001352
Kalhee Kim495c9b22017-11-07 16:32:09 +00001353 log.debug("DHCP server connect point: {}", newServerInfo.getDhcpServerConnectPoint().orElse(null));
1354 log.debug("DHCP server IP: {}", newServerInfo.getDhcpServerIp6().orElse(null));
Yi Tseng919b2df2017-09-07 16:22:51 -07001355
Kalhee Kim495c9b22017-11-07 16:32:09 +00001356 Ip6Address serverIp = newServerInfo.getDhcpServerIp6().get();
1357 Ip6Address ipToProbe;
1358 if (newServerInfo.getDhcpGatewayIp6().isPresent()) {
1359 ipToProbe = newServerInfo.getDhcpGatewayIp6().get();
1360 } else {
1361 ipToProbe = newServerInfo.getDhcpServerIp6().orElse(null);
1362 }
1363 String hostToProbe = newServerInfo.getDhcpGatewayIp6()
1364 .map(ip -> "gateway").orElse("server");
Yi Tseng919b2df2017-09-07 16:22:51 -07001365
Kalhee Kim495c9b22017-11-07 16:32:09 +00001366 log.warn("Probing to resolve {} IP {}", hostToProbe, ipToProbe);
1367 hostService.startMonitoringIp(ipToProbe);
1368
1369 Set<Host> hosts = hostService.getHostsByIp(ipToProbe);
1370 if (!hosts.isEmpty()) {
1371 Host host = hosts.iterator().next();
1372 newServerInfo.setDhcpConnectVlan(host.vlan());
1373 newServerInfo.setDhcpConnectMac(host.mac());
1374 log.warn("Host found host {}", host);
1375
1376 } else {
1377 log.warn("No host found host ip {}", ipToProbe);
1378 }
1379 // Add new server info
1380 synchronized (this) {
1381 serverInfoList.add(newServerInfo);
1382 }
1383 if (!hosts.isEmpty()) {
1384 requestDhcpPacket(serverIp);
1385 }
Yi Tseng919b2df2017-09-07 16:22:51 -07001386 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001387 }
1388
1389 class InternalHostListener implements HostListener {
1390 @Override
1391 public void event(HostEvent event) {
1392 switch (event.type()) {
1393 case HOST_ADDED:
1394 case HOST_UPDATED:
1395 hostUpdated(event.subject());
1396 break;
1397 case HOST_REMOVED:
1398 hostRemoved(event.subject());
1399 break;
Kalhee Kim45fede42017-09-05 19:05:06 +00001400 default:
1401 break;
1402 }
1403 }
1404 }
1405
1406 /**
Kalhee Kim45fede42017-09-05 19:05:06 +00001407 * Handle host updated.
1408 * If the host is DHCP server or gateway, update connect mac and vlan.
1409 *
1410 * @param host the host
1411 */
1412 private void hostUpdated(Host host) {
Yi Tseng525ff402017-10-23 19:39:39 -07001413 hostUpdated(host, defaultServerInfoList);
1414 hostUpdated(host, indirectServerInfoList);
Kalhee Kim45fede42017-09-05 19:05:06 +00001415 }
1416
Yi Tseng525ff402017-10-23 19:39:39 -07001417 private void hostUpdated(Host host, List<DhcpServerInfo> serverInfoList) {
1418 DhcpServerInfo serverInfo;
1419 Ip6Address targetIp;
1420 if (!serverInfoList.isEmpty()) {
1421 serverInfo = serverInfoList.get(0);
1422 Ip6Address serverIp = serverInfo.getDhcpServerIp6().orElse(null);
1423 targetIp = serverInfo.getDhcpGatewayIp6().orElse(null);
1424
1425 if (targetIp == null) {
1426 targetIp = serverIp;
1427 }
Yi Tseng525ff402017-10-23 19:39:39 -07001428 if (targetIp != null) {
1429 if (host.ipAddresses().contains(targetIp)) {
1430 serverInfo.setDhcpConnectMac(host.mac());
1431 serverInfo.setDhcpConnectVlan(host.vlan());
1432 requestDhcpPacket(serverIp);
1433 }
1434 }
1435 }
1436 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001437 /**
1438 * Handle host removed.
1439 * If the host is DHCP server or gateway, unset connect mac and vlan.
1440 *
1441 * @param host the host
1442 */
1443 private void hostRemoved(Host host) {
Yi Tseng525ff402017-10-23 19:39:39 -07001444 hostRemoved(host, defaultServerInfoList);
1445 hostRemoved(host, indirectServerInfoList);
Yi Tseng919b2df2017-09-07 16:22:51 -07001446 }
1447
Yi Tseng525ff402017-10-23 19:39:39 -07001448 private void hostRemoved(Host host, List<DhcpServerInfo> serverInfoList) {
1449 DhcpServerInfo serverInfo;
1450 Ip6Address targetIp;
1451
1452 if (!serverInfoList.isEmpty()) {
1453 serverInfo = serverInfoList.get(0);
1454 Ip6Address serverIp = serverInfo.getDhcpServerIp6().orElse(null);
1455 targetIp = serverInfo.getDhcpGatewayIp6().orElse(null);
1456
1457 if (targetIp == null) {
1458 targetIp = serverIp;
1459 }
Yi Tseng525ff402017-10-23 19:39:39 -07001460 if (targetIp != null) {
1461 if (host.ipAddresses().contains(targetIp)) {
1462 serverInfo.setDhcpConnectVlan(null);
1463 serverInfo.setDhcpConnectMac(null);
1464 cancelDhcpPacket(serverIp);
1465 }
1466 }
1467 }
1468 }
1469
Kalhee Kim45b24182017-10-18 18:30:23 +00001470 /**
1471 * Returns the first interface ip from interface.
1472 *
1473 * @param iface interface of one connect point
1474 * @return the first interface IP; null if not exists an IP address in
1475 * these interfaces
1476 */
1477 private Ip6Address getFirstIpFromInterface(Interface iface) {
1478 checkNotNull(iface, "Interface can't be null");
1479 return iface.ipAddressesList().stream()
1480 .map(InterfaceIpAddress::ipAddress)
1481 .filter(IpAddress::isIp6)
1482 .map(IpAddress::getIp6Address)
1483 .findFirst()
1484 .orElse(null);
1485 }
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001486
1487 /**
1488 * Gets Interface facing to the server for default host.
1489 *
1490 * @return the Interface facing to the server; null if not found
1491 */
1492 private Interface getServerInterface() {
1493 DhcpServerInfo serverInfo;
1494 ConnectPoint dhcpServerConnectPoint;
1495 VlanId dhcpConnectVlan;
1496
1497 if (!defaultServerInfoList.isEmpty()) {
1498 serverInfo = defaultServerInfoList.get(0);
1499 dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1500 dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1501 } else {
1502 return null;
1503 }
1504 if (dhcpServerConnectPoint == null || dhcpConnectVlan == null) {
1505 log.info("Default DHCP server {} not resolve yet", serverInfo.getDhcpGatewayIp6());
1506 return null;
1507 }
1508 return interfaceService.getInterfacesByPort(dhcpServerConnectPoint)
1509 .stream()
1510 .filter(iface -> dhcp6HandlerUtil.interfaceContainsVlan(iface, dhcpConnectVlan))
1511 .findFirst()
1512 .orElse(null);
1513 }
1514
1515 /**
1516 * Gets Interface facing to the server for indirect hosts.
1517 * Use default server Interface if indirect server not configured.
1518 *
1519 * @return the Interface facing to the server; null if not found
1520 */
1521 private Interface getIndirectServerInterface() {
1522 DhcpServerInfo serverInfo;
1523
1524 ConnectPoint indirectDhcpServerConnectPoint;
1525 VlanId indirectDhcpConnectVlan;
1526
1527 if (!indirectServerInfoList.isEmpty()) {
1528 serverInfo = indirectServerInfoList.get(0);
1529 indirectDhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1530 indirectDhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1531 } else {
1532 return getServerInterface();
1533 }
1534 if (indirectDhcpServerConnectPoint == null || indirectDhcpConnectVlan == null) {
1535 log.info("Indirect DHCP server {} not resolve yet", serverInfo.getDhcpGatewayIp6());
1536 return null;
1537 }
1538 return interfaceService.getInterfacesByPort(indirectDhcpServerConnectPoint)
1539 .stream()
1540 .filter(iface -> dhcp6HandlerUtil.interfaceContainsVlan(iface, indirectDhcpConnectVlan))
1541 .findFirst()
1542 .orElse(null);
1543 }
1544
Kalhee Kim45b24182017-10-18 18:30:23 +00001545 /**
Kalhee Kim495c9b22017-11-07 16:32:09 +00001546 * Checks if serverInfo's host info (mac and vlan) is filled in; if not, fills in.
1547 *
1548 * @param serverInfo server information
1549 * @return newServerInfo if host info can be either found or filled in.
1550 */
1551 private DhcpServerInfo getHostInfoForServerInfo(DhcpServerInfo serverInfo, List<DhcpServerInfo> sererInfoList) {
1552 DhcpServerInfo newServerInfo = null;
1553 MacAddress dhcpServerConnectMac = serverInfo.getDhcpConnectMac().orElse(null);
1554 VlanId dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1555 ConnectPoint dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1556
1557 if (dhcpServerConnectMac != null && dhcpConnectVlan != null) {
1558 newServerInfo = serverInfo;
Charles Chan750c9992018-02-26 10:44:49 -08001559 log.debug("DHCP server {} host info found. ConnectPt{} Mac {} vlan {}", serverInfo.getDhcpServerIp6(),
Kalhee Kim495c9b22017-11-07 16:32:09 +00001560 dhcpServerConnectPoint, dhcpServerConnectMac, dhcpConnectVlan);
1561 } else {
1562 log.warn("DHCP server {} not resolve yet connectPt {} mac {} vlan {}", serverInfo.getDhcpServerIp6(),
1563 dhcpServerConnectPoint, dhcpServerConnectMac, dhcpConnectVlan);
1564
1565 Ip6Address ipToProbe;
1566 if (serverInfo.getDhcpGatewayIp6().isPresent()) {
1567 ipToProbe = serverInfo.getDhcpGatewayIp6().get();
1568 } else {
1569 ipToProbe = serverInfo.getDhcpServerIp6().orElse(null);
1570 }
1571 String hostToProbe = serverInfo.getDhcpGatewayIp6()
1572 .map(ip -> "gateway").orElse("server");
1573
1574 log.info("Dynamically probing to resolve {} IP {}", hostToProbe, ipToProbe);
1575 hostService.startMonitoringIp(ipToProbe);
1576
1577 Set<Host> hosts = hostService.getHostsByIp(ipToProbe);
1578 if (!hosts.isEmpty()) {
1579 int serverInfoIndex = sererInfoList.indexOf(serverInfo);
1580 Host host = hosts.iterator().next();
1581 serverInfo.setDhcpConnectVlan(host.vlan());
1582 serverInfo.setDhcpConnectMac(host.mac());
1583 // replace the serverInfo in the list
1584 sererInfoList.set(serverInfoIndex, serverInfo);
1585 newServerInfo = serverInfo;
1586 log.warn("Dynamically host found host {}", host);
1587 } else {
1588 log.warn("No host found host ip {} dynamically", ipToProbe);
1589 }
1590 }
1591 return newServerInfo;
1592 }
1593
1594 /**
Kalhee Kim45b24182017-10-18 18:30:23 +00001595 * Gets Interface facing to the server for default host.
1596 *
Kalhee Kim495c9b22017-11-07 16:32:09 +00001597 * @param serverInfo server information
Kalhee Kim45b24182017-10-18 18:30:23 +00001598 * @return the Interface facing to the server; null if not found
1599 */
Kalhee Kim495c9b22017-11-07 16:32:09 +00001600 private Interface getServerInterface(DhcpServerInfo serverInfo) {
1601 Interface serverInterface = null;
Yi Tseng25bfe372017-11-03 16:27:32 -07001602
Kalhee Kim495c9b22017-11-07 16:32:09 +00001603 ConnectPoint dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1604 VlanId dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1605
1606 if (dhcpServerConnectPoint != null && dhcpConnectVlan != null) {
1607 serverInterface = interfaceService.getInterfacesByPort(dhcpServerConnectPoint)
1608 .stream()
1609 .filter(iface -> dhcp6HandlerUtil.interfaceContainsVlan(iface, dhcpConnectVlan))
1610 .findFirst()
1611 .orElse(null);
Yi Tseng25bfe372017-11-03 16:27:32 -07001612 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +00001613 log.warn("DHCP server {} not resolve yet connectPoint {} vlan {}", serverInfo.getDhcpServerIp6(),
1614 dhcpServerConnectPoint, dhcpConnectVlan);
Kalhee Kim45b24182017-10-18 18:30:23 +00001615 }
Kalhee Kim495c9b22017-11-07 16:32:09 +00001616
1617 return serverInterface;
Kalhee Kim45b24182017-10-18 18:30:23 +00001618 }
1619
Yi Tseng525ff402017-10-23 19:39:39 -07001620 private void requestDhcpPacket(Ip6Address serverIp) {
1621 requestServerDhcpPacket(serverIp);
1622 requestClientDhcpPacket(serverIp);
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001623 requestServerLQPacket(serverIp);
Yi Tseng525ff402017-10-23 19:39:39 -07001624 }
1625
1626 private void cancelDhcpPacket(Ip6Address serverIp) {
1627 cancelServerDhcpPacket(serverIp);
1628 cancelClientDhcpPacket(serverIp);
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001629 cancelServerLQPacket(serverIp);
Yi Tseng525ff402017-10-23 19:39:39 -07001630 }
1631
1632 private void cancelServerDhcpPacket(Ip6Address serverIp) {
1633 TrafficSelector serverSelector =
1634 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1635 .matchIPv6Src(serverIp.toIpPrefix())
1636 .build();
1637 packetService.cancelPackets(serverSelector,
1638 PacketPriority.CONTROL,
1639 appId);
1640 }
1641
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001642 private void cancelServerLQPacket(Ip6Address serverIp) {
1643 TrafficSelector serverSelector =
1644 DefaultTrafficSelector.builder(LEASE_QUERY_RESPONSE_SELECTOR)
1645 .matchIPv6Src(serverIp.toIpPrefix())
1646 .build();
1647 packetService.cancelPackets(serverSelector,
1648 PacketPriority.CONTROL,
1649 appId);
1650 }
1651
Yi Tseng525ff402017-10-23 19:39:39 -07001652 private void requestServerDhcpPacket(Ip6Address serverIp) {
1653 TrafficSelector serverSelector =
1654 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1655 .matchIPv6Src(serverIp.toIpPrefix())
1656 .build();
1657 packetService.requestPackets(serverSelector,
1658 PacketPriority.CONTROL,
1659 appId);
1660 }
1661
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001662 private void requestServerLQPacket(Ip6Address serverIp) {
1663 TrafficSelector serverSelector =
1664 DefaultTrafficSelector.builder(LEASE_QUERY_RESPONSE_SELECTOR)
1665 .matchIPv6Src(serverIp.toIpPrefix())
1666 .build();
1667 packetService.requestPackets(serverSelector,
1668 PacketPriority.CONTROL,
1669 appId);
1670 }
1671
Yi Tseng525ff402017-10-23 19:39:39 -07001672 private void cancelClientDhcpPacket(Ip6Address serverIp) {
1673 // Packet comes from relay
1674 TrafficSelector indirectClientSelector =
1675 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1676 .matchIPv6Dst(serverIp.toIpPrefix())
1677 .build();
1678 packetService.cancelPackets(indirectClientSelector,
1679 PacketPriority.CONTROL,
1680 appId);
Yi Tseng41dde702017-11-02 15:21:10 -07001681 indirectClientSelector =
1682 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1683 .matchIPv6Dst(Ip6Address.ALL_DHCP_RELAY_AGENTS_AND_SERVERS.toIpPrefix())
1684 .build();
1685 packetService.cancelPackets(indirectClientSelector,
1686 PacketPriority.CONTROL,
1687 appId);
1688 indirectClientSelector =
1689 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1690 .matchIPv6Dst(Ip6Address.ALL_DHCP_SERVERS.toIpPrefix())
1691 .build();
1692 packetService.cancelPackets(indirectClientSelector,
1693 PacketPriority.CONTROL,
1694 appId);
Yi Tseng525ff402017-10-23 19:39:39 -07001695
1696 // Packet comes from client
1697 packetService.cancelPackets(CLIENT_SERVER_SELECTOR,
1698 PacketPriority.CONTROL,
1699 appId);
1700 }
1701
1702 private void requestClientDhcpPacket(Ip6Address serverIp) {
1703 // Packet comes from relay
1704 TrafficSelector indirectClientSelector =
1705 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1706 .matchIPv6Dst(serverIp.toIpPrefix())
1707 .build();
1708 packetService.requestPackets(indirectClientSelector,
1709 PacketPriority.CONTROL,
1710 appId);
Yi Tseng41dde702017-11-02 15:21:10 -07001711 indirectClientSelector =
1712 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1713 .matchIPv6Dst(Ip6Address.ALL_DHCP_RELAY_AGENTS_AND_SERVERS.toIpPrefix())
1714 .build();
1715 packetService.requestPackets(indirectClientSelector,
1716 PacketPriority.CONTROL,
1717 appId);
1718 indirectClientSelector =
1719 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1720 .matchIPv6Dst(Ip6Address.ALL_DHCP_SERVERS.toIpPrefix())
1721 .build();
1722 packetService.requestPackets(indirectClientSelector,
1723 PacketPriority.CONTROL,
1724 appId);
Yi Tseng525ff402017-10-23 19:39:39 -07001725
1726 // Packet comes from client
1727 packetService.requestPackets(CLIENT_SERVER_SELECTOR,
1728 PacketPriority.CONTROL,
1729 appId);
1730 }
1731
1732 /**
1733 * Process the ignore rules.
1734 *
1735 * @param deviceId the device id
1736 * @param vlanId the vlan to be ignored
1737 * @param op the operation, ADD to install; REMOVE to uninstall rules
1738 */
1739 private void processIgnoreVlanRule(DeviceId deviceId, VlanId vlanId, Objective.Operation op) {
Yi Tseng525ff402017-10-23 19:39:39 -07001740 AtomicInteger installedCount = new AtomicInteger(DHCP_SELECTORS.size());
1741 DHCP_SELECTORS.forEach(trafficSelector -> {
1742 TrafficSelector selector = DefaultTrafficSelector.builder(trafficSelector)
1743 .matchVlanId(vlanId)
1744 .build();
1745
1746 ForwardingObjective.Builder builder = DefaultForwardingObjective.builder()
1747 .withFlag(ForwardingObjective.Flag.VERSATILE)
1748 .withSelector(selector)
1749 .withPriority(IGNORE_CONTROL_PRIORITY)
Yi Tsengdbabeed2017-11-29 10:49:20 -08001750 .withTreatment(DefaultTrafficTreatment.emptyTreatment())
Yi Tseng525ff402017-10-23 19:39:39 -07001751 .fromApp(appId);
1752
1753
1754 ObjectiveContext objectiveContext = new ObjectiveContext() {
1755 @Override
1756 public void onSuccess(Objective objective) {
1757 log.info("Ignore rule {} (Vlan id {}, device {}, selector {})",
1758 op, vlanId, deviceId, selector);
1759 int countDown = installedCount.decrementAndGet();
1760 if (countDown != 0) {
1761 return;
1762 }
1763 switch (op) {
1764 case ADD:
1765 ignoredVlans.put(deviceId, vlanId);
1766 break;
1767 case REMOVE:
1768 ignoredVlans.remove(deviceId, vlanId);
1769 break;
1770 default:
1771 log.warn("Unsupported objective operation {}", op);
1772 break;
1773 }
1774 }
1775
1776 @Override
1777 public void onError(Objective objective, ObjectiveError error) {
1778 log.warn("Can't {} ignore rule (vlan id {}, selector {}, device {}) due to {}",
1779 op, vlanId, selector, deviceId, error);
1780 }
1781 };
1782
1783 ForwardingObjective fwd;
1784 switch (op) {
1785 case ADD:
1786 fwd = builder.add(objectiveContext);
1787 break;
1788 case REMOVE:
1789 fwd = builder.remove(objectiveContext);
1790 break;
1791 default:
1792 log.warn("Unsupported objective operation {}", op);
1793 return;
1794 }
1795
1796 Device device = deviceService.getDevice(deviceId);
1797 if (device == null || !device.is(Pipeliner.class)) {
1798 log.warn("Device {} is not available now, wait until device is available", deviceId);
1799 return;
1800 }
1801 flowObjectiveService.apply(deviceId, fwd);
1802 });
1803 }
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001804
Kalhee Kim121ba922017-11-01 17:56:44 +00001805 /**
1806 * Find first ipaddress for a given Host info i.e. mac and vlan.
1807 *
1808 * @param clientMac client mac
1809 * @param vlanId packet's vlan
1810 * @return next-hop link-local ipaddress for a given host
1811 */
Kalhee Kim495c9b22017-11-07 16:32:09 +00001812 private IpAddress getFirstIpByHost(Boolean directConnFlag, MacAddress clientMac, VlanId vlanId) {
Kalhee Kim121ba922017-11-01 17:56:44 +00001813 IpAddress nextHopIp;
1814 // pick out the first link-local ip address
1815 HostId gwHostId = HostId.hostId(clientMac, vlanId);
1816 Host gwHost = hostService.getHost(gwHostId);
1817 if (gwHost == null) {
1818 log.warn("Can't find gateway host for hostId {}", gwHostId);
1819 return null;
1820 }
Kalhee Kim495c9b22017-11-07 16:32:09 +00001821 if (directConnFlag) {
1822 nextHopIp = gwHost.ipAddresses()
1823 .stream()
1824 .filter(IpAddress::isIp6)
1825 .map(IpAddress::getIp6Address)
1826 .findFirst()
1827 .orElse(null);
1828 } else {
1829 nextHopIp = gwHost.ipAddresses()
1830 .stream()
1831 .filter(IpAddress::isIp6)
1832 .filter(ip6 -> ip6.isLinkLocal())
1833 .map(IpAddress::getIp6Address)
1834 .findFirst()
1835 .orElse(null);
1836 }
Kalhee Kim121ba922017-11-01 17:56:44 +00001837 return nextHopIp;
1838 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +00001839
Kalhee Kim495c9b22017-11-07 16:32:09 +00001840 private List<DhcpServerInfo> findValidServerInfo(boolean directConnFlag) {
1841 List<DhcpServerInfo> validServerInfo;
1842
1843 if (directConnFlag || indirectServerInfoList.isEmpty()) {
1844 validServerInfo = new ArrayList<DhcpServerInfo>(defaultServerInfoList);
1845 } else {
1846 validServerInfo = new ArrayList<DhcpServerInfo>(indirectServerInfoList);
1847 }
1848 return validServerInfo;
1849 }
1850
1851 private DhcpServerInfo findServerInfoFromServer(boolean directConnFlag, ConnectPoint inPort) {
1852 List<DhcpServerInfo> validServerInfoList = findValidServerInfo(directConnFlag);
1853 DhcpServerInfo foundServerInfo = null;
1854 for (DhcpServerInfo serverInfo : validServerInfoList) {
1855 if (inPort.equals(serverInfo.getDhcpServerConnectPoint().get())) {
1856 foundServerInfo = serverInfo;
Charles Chan750c9992018-02-26 10:44:49 -08001857 log.debug("ServerInfo found for Rcv port {} Server Connect Point {} for {}",
Kalhee Kim495c9b22017-11-07 16:32:09 +00001858 inPort, serverInfo.getDhcpServerConnectPoint(), directConnFlag ? "direct" : "indirect");
1859 break;
1860 } else {
1861 log.warn("Rcv port {} not the same as Server Connect Point {} for {}",
1862 inPort, serverInfo.getDhcpServerConnectPoint(), directConnFlag ? "direct" : "indirect");
1863 }
1864 }
1865 return foundServerInfo;
1866 }
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001867
Kalhee Kim495c9b22017-11-07 16:32:09 +00001868 /**
1869 * Set the dhcp6 lease expiry poll interval value.
1870 *
1871 * @param val poll interval value in seconds
1872 */
Kalhee Kim2f07c602017-11-15 18:57:53 +00001873 @Override
Kalhee Kim495c9b22017-11-07 16:32:09 +00001874 public void setDhcp6PollInterval(int val) {
1875 dhcp6PollInterval = val;
1876 }
1877
Kalhee Kim2f07c602017-11-15 18:57:53 +00001878 /**
1879 * get the dhcp6 lease expiry poll interval value.
1880 * This is a private function
1881 * @return poll interval value in seconds
1882 */
1883 private int getDhcp6PollInterval() {
1884 return dhcp6PollInterval;
1885 }
1886
1887 /**
1888 * Find lease-expired ipaddresses and pd prefixes.
1889 * Removing host/route/fpm entries.
1890 */
1891 public void timeTick() {
1892 long currentTime = System.currentTimeMillis();
1893 Collection<DhcpRecord> records = dhcpRelayStore.getDhcpRecords();
1894
1895 log.debug("timeTick called currenttime {} records num {} ", currentTime, records.size());
1896
1897 records.forEach(record -> {
1898 boolean addrOrPdRemoved = false;
1899 DHCP6.MsgType ip6Status = record.ip6Status().orElse(null);
1900 if (ip6Status == null) {
1901 log.debug("record is not valid v6 record.");
1902 return;
1903 }
1904
1905 if ((currentTime - record.getLastIp6Update()) >
1906 ((record.addrPrefTime() + getDhcp6PollInterval() / 2) * 1000)) {
1907 // remove ipaddress from host/route table
1908 IpAddress ip = record.ip6Address().orElse(null);
1909 if (ip != null) {
1910 if (record.directlyConnected()) {
1911 providerService.removeIpFromHost(HostId.hostId(record.macAddress(),
1912 record.vlanId()), ip);
1913 } else {
1914 MacAddress gwMac = record.nextHop().orElse(null);
1915 if (gwMac == null) {
1916 log.warn("Can't find gateway mac address from record {} for ip6Addr", record);
1917 return;
1918 }
1919 IpAddress nextHopIp = getFirstIpByHost(record.directlyConnected(),
1920 gwMac,
1921 record.vlanId());
Kalhee Kim54a97c92018-04-02 21:23:46 +00001922 Route route = new Route(Route.Source.DHCP, ip.toIpPrefix(), nextHopIp);
Kalhee Kim2f07c602017-11-15 18:57:53 +00001923 routeStore.removeRoute(route);
1924 }
1925 record.updateAddrPrefTime(0);
1926 record.ip6Address(null);
1927 addrOrPdRemoved = true;
1928 dhcpRelayStore.updateDhcpRecord(HostId.hostId(record.macAddress(),
1929 record.vlanId()), record);
1930 log.warn("IP6 address is set to null. delta {} lastUpdate {} addrPrefTime {}",
1931 (currentTime - record.getLastIp6Update()), record.getLastIp6Update(),
1932 record.addrPrefTime());
1933 }
1934 }
1935 if ((currentTime - record.getLastPdUpdate()) >
1936 ((record.pdPrefTime() + getDhcp6PollInterval() / 2) * 1000)) {
1937 // remove PD from route/fpm table
1938 IpPrefix pdIpPrefix = record.pdPrefix().orElse(null);
1939 if (pdIpPrefix != null) {
1940 if (record.directlyConnected()) {
1941 providerService.removeIpFromHost(HostId.hostId(record.macAddress(), record.vlanId()),
1942 pdIpPrefix.address().getIp6Address());
1943 } else {
1944 MacAddress gwMac = record.nextHop().orElse(null);
1945 if (gwMac == null) {
1946 log.warn("Can't find gateway mac address from record {} for PD prefix", record);
1947 return;
1948 }
1949 IpAddress nextHopIp = getFirstIpByHost(record.directlyConnected(),
1950 gwMac,
1951 record.vlanId());
Kalhee Kim54a97c92018-04-02 21:23:46 +00001952 Route route = new Route(Route.Source.DHCP, pdIpPrefix, nextHopIp);
Kalhee Kim2f07c602017-11-15 18:57:53 +00001953 routeStore.removeRoute(route);
1954 if (this.dhcpFpmEnabled) {
1955 dhcpFpmPrefixStore.removeFpmRecord(pdIpPrefix);
1956 }
1957 }
1958 record.updatePdPrefTime(0);
1959 record.pdPrefix(null);
1960 addrOrPdRemoved = true;
1961 dhcpRelayStore.updateDhcpRecord(HostId.hostId(record.macAddress(),
1962 record.vlanId()), record);
1963 log.warn("PD prefix is set to null.delta {} pdPrefTime {}",
1964 (currentTime - record.getLastPdUpdate()), record.pdPrefTime());
1965 }
1966 }
1967 if (addrOrPdRemoved &&
1968 !record.ip6Address().isPresent() && !record.pdPrefix().isPresent()) {
1969 log.warn("ip6Status {} IP6 address and IP6 PD both are null. Remove record.", ip6Status);
1970 dhcpRelayStore.removeDhcpRecord(HostId.hostId(record.macAddress(), record.vlanId()));
1971 }
1972 }
1973 );
1974 }
1975}