blob: a912614a149cbba657375ccfce2b7e2279e0dc20 [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 com.google.common.collect.Sets;
24import com.google.common.collect.ImmutableSet;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070025import org.osgi.service.component.annotations.Activate;
26import org.osgi.service.component.annotations.Component;
27import org.osgi.service.component.annotations.Deactivate;
28import org.osgi.service.component.annotations.Modified;
29import org.osgi.service.component.annotations.Reference;
30import org.osgi.service.component.annotations.ReferenceCardinality;
Yi Tseng51301292017-07-28 13:02:59 -070031import org.onlab.packet.BasePacket;
Kalhee Kim45fede42017-09-05 19:05:06 +000032import org.onlab.packet.DHCP6;
33import org.onlab.packet.IPv6;
34import org.onlab.packet.Ethernet;
35import org.onlab.packet.Ip6Address;
Yi Tseng51301292017-07-28 13:02:59 -070036import org.onlab.packet.IpAddress;
Kalhee Kim45fede42017-09-05 19:05:06 +000037import org.onlab.packet.IpPrefix;
Yi Tseng51301292017-07-28 13:02:59 -070038import org.onlab.packet.MacAddress;
Yi Tseng525ff402017-10-23 19:39:39 -070039import org.onlab.packet.TpPort;
Kalhee Kim45fede42017-09-05 19:05:06 +000040import org.onlab.packet.UDP;
Yi Tseng51301292017-07-28 13:02:59 -070041import org.onlab.packet.VlanId;
Lior Assoulinea21c0ca2018-01-28 16:18:48 -080042import org.onlab.packet.dhcp.Dhcp6ClientDataOption;
43import org.onlab.packet.dhcp.Dhcp6LeaseQueryOption;
Kalhee Kim45fede42017-09-05 19:05:06 +000044import org.onlab.packet.dhcp.Dhcp6RelayOption;
45import org.onlab.packet.dhcp.Dhcp6InterfaceIdOption;
Lior Assoulinea21c0ca2018-01-28 16:18:48 -080046import org.onlab.packet.dhcp.Dhcp6Option;
Kalhee Kim45fede42017-09-05 19:05:06 +000047import org.onlab.packet.dhcp.Dhcp6IaNaOption;
48import org.onlab.packet.dhcp.Dhcp6IaTaOption;
49import org.onlab.packet.dhcp.Dhcp6IaPdOption;
50import org.onlab.packet.dhcp.Dhcp6IaAddressOption;
51import org.onlab.packet.dhcp.Dhcp6IaPrefixOption;
Kalhee Kimea4b6c22017-11-09 14:38:37 +000052import org.onlab.packet.dhcp.Dhcp6ClientIdOption;
53import org.onlab.packet.dhcp.Dhcp6Duid;
Kalhee Kim495c9b22017-11-07 16:32:09 +000054import org.onlab.packet.DHCP6.MsgType;
Kalhee Kim45fede42017-09-05 19:05:06 +000055import org.onlab.util.HexString;
Taras Lemkin96a0d342018-03-26 14:52:58 +000056import org.onlab.util.Tools;
57import org.onosproject.cfg.ComponentConfigService;
Yi Tseng525ff402017-10-23 19:39:39 -070058import org.onosproject.core.ApplicationId;
59import org.onosproject.core.CoreService;
Yi Tseng51301292017-07-28 13:02:59 -070060import org.onosproject.dhcprelay.api.DhcpHandler;
Yi Tseng919b2df2017-09-07 16:22:51 -070061import org.onosproject.dhcprelay.api.DhcpServerInfo;
Yi Tseng525ff402017-10-23 19:39:39 -070062import org.onosproject.dhcprelay.config.IgnoreDhcpConfig;
Kalhee Kim45fede42017-09-05 19:05:06 +000063import org.onosproject.dhcprelay.store.DhcpRelayStore;
Kalhee Kimea4b6c22017-11-09 14:38:37 +000064import org.onosproject.dhcprelay.store.DhcpRecord;
Kalhee Kimba366062017-11-07 16:32:09 +000065import org.onosproject.dhcprelay.store.DhcpFpmPrefixStore;
Kalhee Kimd94ceea2017-11-29 19:03:02 +000066import 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;
Kalhee Kim45fede42017-09-05 19:05:06 +000086import org.onosproject.net.host.HostListener;
87import org.onosproject.net.host.HostEvent;
88import org.onosproject.net.intf.Interface;
89import org.onosproject.net.intf.InterfaceService;
Yi Tseng525ff402017-10-23 19:39:39 -070090import org.onosproject.net.packet.PacketPriority;
Yi Tsengaa417a62017-09-08 17:22:51 -070091import org.onosproject.net.provider.ProviderId;
Kalhee Kim45fede42017-09-05 19:05:06 +000092import org.onosproject.routeservice.Route;
93import org.onosproject.routeservice.RouteStore;
Yi Tseng483ac6f2017-08-02 15:03:31 -070094import org.onosproject.dhcprelay.config.DhcpServerConfig;
Yi Tseng51301292017-07-28 13:02:59 -070095import org.onosproject.net.ConnectPoint;
Kalhee Kim45fede42017-09-05 19:05:06 +000096import org.onosproject.net.Host;
97import org.onosproject.net.HostId;
98import org.onosproject.net.HostLocation;
99import org.onosproject.net.packet.DefaultOutboundPacket;
100import org.onosproject.net.packet.OutboundPacket;
Yi Tseng51301292017-07-28 13:02:59 -0700101import org.onosproject.net.packet.PacketContext;
Kalhee Kim45fede42017-09-05 19:05:06 +0000102import org.onosproject.net.packet.PacketService;
Taras Lemkin96a0d342018-03-26 14:52:58 +0000103import org.osgi.service.component.ComponentContext;
Kalhee Kim45fede42017-09-05 19:05:06 +0000104import org.slf4j.Logger;
105import org.slf4j.LoggerFactory;
106import org.onosproject.net.flow.DefaultTrafficTreatment;
107import org.onosproject.net.flow.TrafficTreatment;
Kalhee Kim45fede42017-09-05 19:05:06 +0000108import java.nio.ByteBuffer;
Taras Lemkin96a0d342018-03-26 14:52:58 +0000109import java.util.ArrayList;
Yi Tseng483ac6f2017-08-02 15:03:31 -0700110import java.util.Collection;
Taras Lemkin96a0d342018-03-26 14:52:58 +0000111import java.util.Dictionary;
112import java.util.List;
Yi Tseng51301292017-07-28 13:02:59 -0700113import java.util.Optional;
Kalhee Kim45fede42017-09-05 19:05:06 +0000114import java.util.Set;
Charles Chan909cff82018-03-05 13:14:02 -0800115import java.util.concurrent.CopyOnWriteArrayList;
Jordan Halterman6328db72018-04-10 13:34:50 -0400116import java.util.concurrent.Executor;
Yi Tseng525ff402017-10-23 19:39:39 -0700117import java.util.concurrent.atomic.AtomicInteger;
Kalhee Kim45fede42017-09-05 19:05:06 +0000118import static com.google.common.base.Preconditions.checkNotNull;
119import static com.google.common.base.Preconditions.checkState;
Jordan Halterman6328db72018-04-10 13:34:50 -0400120import static java.util.concurrent.Executors.newSingleThreadExecutor;
121import static org.onlab.util.Tools.groupedThreads;
Ray Milkey687c00c2018-10-31 10:18:41 -0700122import static org.onosproject.dhcprelay.OsgiPropertyConstants.LEARN_ROUTE_FROM_LEASE_QUERY;
123import static org.onosproject.dhcprelay.OsgiPropertyConstants.LEARN_ROUTE_FROM_LEASE_QUERY_DEFAULT;
Yi Tseng525ff402017-10-23 19:39:39 -0700124import static org.onosproject.net.flowobjective.Objective.Operation.ADD;
125import static org.onosproject.net.flowobjective.Objective.Operation.REMOVE;
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000126import java.util.concurrent.Semaphore;
Yi Tseng51301292017-07-28 13:02:59 -0700127
Ray Milkey687c00c2018-10-31 10:18:41 -0700128@Component(
129 service = { DhcpHandler.class, HostProvider.class },
130 property = {
Ray Milkey5504bd22019-03-22 16:24:38 -0700131 "_version:Integer = 6",
Ray Milkey687c00c2018-10-31 10:18:41 -0700132 LEARN_ROUTE_FROM_LEASE_QUERY + ":Boolean=" + LEARN_ROUTE_FROM_LEASE_QUERY_DEFAULT
133 }
134)
135
Yi Tsengaa417a62017-09-08 17:22:51 -0700136public class Dhcp6HandlerImpl implements DhcpHandler, HostProvider {
Charles Chand988c282017-09-12 17:09:32 -0700137 public static final String DHCP_V6_RELAY_APP = "org.onosproject.Dhcp6HandlerImpl";
Saurav Das7c6dec12017-09-13 14:35:56 -0700138 public static final ProviderId PROVIDER_ID = new ProviderId("dhcp6", DHCP_V6_RELAY_APP);
Yi Tseng525ff402017-10-23 19:39:39 -0700139 private static final int IGNORE_CONTROL_PRIORITY = PacketPriority.CONTROL.priorityValue() + 1000;
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000140 private String gCount = "global";
Yi Tseng525ff402017-10-23 19:39:39 -0700141 private static final TrafficSelector CLIENT_SERVER_SELECTOR = DefaultTrafficSelector.builder()
142 .matchEthType(Ethernet.TYPE_IPV6)
143 .matchIPProtocol(IPv6.PROTOCOL_UDP)
144 .matchIPv6Src(IpPrefix.IPV6_LINK_LOCAL_PREFIX)
145 .matchIPv6Dst(Ip6Address.ALL_DHCP_RELAY_AGENTS_AND_SERVERS.toIpPrefix())
146 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_V6_CLIENT_PORT))
147 .matchUdpDst(TpPort.tpPort(UDP.DHCP_V6_SERVER_PORT))
148 .build();
149 private static final TrafficSelector SERVER_RELAY_SELECTOR = DefaultTrafficSelector.builder()
150 .matchEthType(Ethernet.TYPE_IPV6)
151 .matchIPProtocol(IPv6.PROTOCOL_UDP)
152 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_V6_SERVER_PORT))
153 .matchUdpDst(TpPort.tpPort(UDP.DHCP_V6_SERVER_PORT))
154 .build();
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800155 // lease query reply is from server to client (no relay in between) - so we need to
156 // catch that scenario also ..
157 private static final TrafficSelector LEASE_QUERY_RESPONSE_SELECTOR = DefaultTrafficSelector.builder()
158 .matchEthType(Ethernet.TYPE_IPV6)
159 .matchIPProtocol(IPv6.PROTOCOL_UDP)
160 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_V6_SERVER_PORT))
161 .matchUdpDst(TpPort.tpPort(UDP.DHCP_V6_CLIENT_PORT))
162 .build();
Yi Tseng525ff402017-10-23 19:39:39 -0700163 static final Set<TrafficSelector> DHCP_SELECTORS = ImmutableSet.of(
164 CLIENT_SERVER_SELECTOR,
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800165 SERVER_RELAY_SELECTOR,
166 LEASE_QUERY_RESPONSE_SELECTOR
Yi Tseng525ff402017-10-23 19:39:39 -0700167 );
Kalhee Kim45fede42017-09-05 19:05:06 +0000168 private static Logger log = LoggerFactory.getLogger(Dhcp6HandlerImpl.class);
Yi Tseng51301292017-07-28 13:02:59 -0700169
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700170 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Kalhee Kim45fede42017-09-05 19:05:06 +0000171 protected DhcpRelayStore dhcpRelayStore;
Yi Tseng51301292017-07-28 13:02:59 -0700172
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700173 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000174 protected DhcpRelayCountersStore dhcpRelayCountersStore;
175
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700176 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Kalhee Kim45fede42017-09-05 19:05:06 +0000177 protected PacketService packetService;
178
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700179 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Kalhee Kim45fede42017-09-05 19:05:06 +0000180 protected RouteStore routeStore;
181
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700182 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Kalhee Kim45fede42017-09-05 19:05:06 +0000183 protected InterfaceService interfaceService;
184
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700185 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Kalhee Kim45fede42017-09-05 19:05:06 +0000186 protected HostService hostService;
187
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700188 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Yi Tsengaa417a62017-09-08 17:22:51 -0700189 protected HostProviderRegistry providerRegistry;
Kalhee Kim45fede42017-09-05 19:05:06 +0000190
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700191 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Yi Tseng525ff402017-10-23 19:39:39 -0700192 protected CoreService coreService;
193
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700194 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Kalhee Kimba366062017-11-07 16:32:09 +0000195 protected DhcpFpmPrefixStore dhcpFpmPrefixStore;
196
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700197 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Yi Tseng525ff402017-10-23 19:39:39 -0700198 protected DeviceService deviceService;
199
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700200 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Yi Tseng525ff402017-10-23 19:39:39 -0700201 protected FlowObjectiveService flowObjectiveService;
202
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700203 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Taras Lemkin96a0d342018-03-26 14:52:58 +0000204 protected ComponentConfigService cfgService;
205
Ray Milkey687c00c2018-10-31 10:18:41 -0700206 /** Enable learning routing information from LQ. */
207 private Boolean learnRouteFromLeasequery = LEARN_ROUTE_FROM_LEASE_QUERY_DEFAULT;
Taras Lemkin96a0d342018-03-26 14:52:58 +0000208
Yi Tsengaa417a62017-09-08 17:22:51 -0700209 protected HostProviderService providerService;
Yi Tseng525ff402017-10-23 19:39:39 -0700210 protected ApplicationId appId;
Charles Chan70fdd492018-03-07 17:36:06 -0800211 protected Multimap<DeviceId, VlanId> ignoredVlans = Multimaps.synchronizedMultimap(HashMultimap.create());
Yi Tseng525ff402017-10-23 19:39:39 -0700212 private InternalHostListener hostListener = new InternalHostListener();
Kalhee Kimba366062017-11-07 16:32:09 +0000213 private Boolean dhcpFpmEnabled = false;
Charles Chan909cff82018-03-05 13:14:02 -0800214 private List<DhcpServerInfo> defaultServerInfoList = new CopyOnWriteArrayList<>();
215 private List<DhcpServerInfo> indirectServerInfoList = new CopyOnWriteArrayList<>();
Jordan Halterman6328db72018-04-10 13:34:50 -0400216
217 private Executor hostEventExecutor = newSingleThreadExecutor(
218 groupedThreads("dhcp6-event-host", "%d", log));
219
Kalhee Kim495c9b22017-11-07 16:32:09 +0000220 private class IpAddressInfo {
221 Ip6Address ip6Address;
222 long prefTime;
223 }
224 private class PdPrefixInfo {
225 IpPrefix pdPrefix;
226 long prefTime;
227 }
228 protected int dhcp6PollInterval = 24 * 3600; // 24 hr period
229
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000230 // max 1 thread
231 static Semaphore recordSemaphore = new Semaphore(1);
Kalhee Kim45fede42017-09-05 19:05:06 +0000232
233 // CLIENT message types
234 public static final Set<Byte> MSG_TYPE_FROM_CLIENT =
235 ImmutableSet.of(DHCP6.MsgType.SOLICIT.value(),
236 DHCP6.MsgType.REQUEST.value(),
237 DHCP6.MsgType.REBIND.value(),
238 DHCP6.MsgType.RENEW.value(),
239 DHCP6.MsgType.RELEASE.value(),
240 DHCP6.MsgType.DECLINE.value(),
241 DHCP6.MsgType.CONFIRM.value(),
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800242 DHCP6.MsgType.RELAY_FORW.value(),
243 DHCP6.MsgType.LEASEQUERY.value());
Kalhee Kim45fede42017-09-05 19:05:06 +0000244 // SERVER message types
245 public static final Set<Byte> MSG_TYPE_FROM_SERVER =
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800246 ImmutableSet.of(DHCP6.MsgType.RELAY_REPL.value(),
247 DHCP6.MsgType.LEASEQUERY_REPLY.value());
Kalhee Kim45fede42017-09-05 19:05:06 +0000248
249 @Activate
Taras Lemkin96a0d342018-03-26 14:52:58 +0000250 protected void activate(ComponentContext context) {
251 cfgService.registerProperties(getClass());
252 modified(context);
Yi Tseng525ff402017-10-23 19:39:39 -0700253 appId = coreService.registerApplication(DHCP_V6_RELAY_APP);
Yi Tsengaa417a62017-09-08 17:22:51 -0700254 providerService = providerRegistry.register(this);
Kalhee Kim45fede42017-09-05 19:05:06 +0000255 hostService.addListener(hostListener);
Yi Tseng51301292017-07-28 13:02:59 -0700256 }
257
Kalhee Kim45fede42017-09-05 19:05:06 +0000258 @Deactivate
259 protected void deactivate() {
Taras Lemkin96a0d342018-03-26 14:52:58 +0000260 cfgService.unregisterProperties(getClass(), false);
Yi Tsengaa417a62017-09-08 17:22:51 -0700261 providerRegistry.unregister(this);
Kalhee Kim45fede42017-09-05 19:05:06 +0000262 hostService.removeListener(hostListener);
Yi Tseng919b2df2017-09-07 16:22:51 -0700263 defaultServerInfoList.forEach(this::stopMonitoringIps);
Charles Chanfc1c22e2018-07-19 09:52:01 -0700264 defaultServerInfoList.forEach(info -> info.getDhcpServerIp6().ifPresent(this::cancelDhcpPacket));
Yi Tseng919b2df2017-09-07 16:22:51 -0700265 defaultServerInfoList.clear();
266 indirectServerInfoList.forEach(this::stopMonitoringIps);
Charles Chanfc1c22e2018-07-19 09:52:01 -0700267 indirectServerInfoList.forEach(info -> info.getDhcpServerIp6().ifPresent(this::cancelDhcpPacket));
Yi Tseng919b2df2017-09-07 16:22:51 -0700268 indirectServerInfoList.clear();
269 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000270
Taras Lemkin96a0d342018-03-26 14:52:58 +0000271 @Modified
272 protected void modified(ComponentContext context) {
273 Dictionary<?, ?> properties = context.getProperties();
274 Boolean flag;
Ray Milkey687c00c2018-10-31 10:18:41 -0700275 flag = Tools.isPropertyEnabled(properties, LEARN_ROUTE_FROM_LEASE_QUERY);
Taras Lemkin96a0d342018-03-26 14:52:58 +0000276 if (flag != null) {
277 learnRouteFromLeasequery = flag;
278 log.info("Learning routes from DHCP leasequery is {}",
279 learnRouteFromLeasequery ? "enabled" : "disabled");
280 }
281 }
282
Yi Tseng919b2df2017-09-07 16:22:51 -0700283 private void stopMonitoringIps(DhcpServerInfo serverInfo) {
284 serverInfo.getDhcpGatewayIp6().ifPresent(gatewayIp -> {
285 hostService.stopMonitoringIp(gatewayIp);
286 });
287 serverInfo.getDhcpServerIp6().ifPresent(serverIp -> {
288 hostService.stopMonitoringIp(serverIp);
289 });
Yi Tseng51301292017-07-28 13:02:59 -0700290 }
291
Yi Tseng51301292017-07-28 13:02:59 -0700292 @Override
Yi Tseng919b2df2017-09-07 16:22:51 -0700293 public List<DhcpServerInfo> getDefaultDhcpServerInfoList() {
294 return defaultServerInfoList;
Yi Tseng51301292017-07-28 13:02:59 -0700295 }
296
297 @Override
Yi Tseng919b2df2017-09-07 16:22:51 -0700298 public List<DhcpServerInfo> getIndirectDhcpServerInfoList() {
299 return indirectServerInfoList;
Yi Tseng51301292017-07-28 13:02:59 -0700300 }
301
302 @Override
Yi Tseng525ff402017-10-23 19:39:39 -0700303 public void updateIgnoreVlanConfig(IgnoreDhcpConfig config) {
304 if (config == null) {
305 ignoredVlans.forEach(((deviceId, vlanId) -> {
306 processIgnoreVlanRule(deviceId, vlanId, REMOVE);
307 }));
308 return;
309 }
310 config.ignoredVlans().forEach((deviceId, vlanId) -> {
311 if (ignoredVlans.get(deviceId).contains(vlanId)) {
312 // don't need to process if it already ignored
313 return;
314 }
315 processIgnoreVlanRule(deviceId, vlanId, ADD);
316 });
Yi Tseng525ff402017-10-23 19:39:39 -0700317 ignoredVlans.forEach((deviceId, vlanId) -> {
318 if (!config.ignoredVlans().get(deviceId).contains(vlanId)) {
319 // not contains in new config, remove it
320 processIgnoreVlanRule(deviceId, vlanId, REMOVE);
321 }
322 });
323 }
324
325 @Override
Saurav Dasb805f1a2017-12-13 16:19:35 -0800326 public void removeIgnoreVlanState(IgnoreDhcpConfig config) {
327 if (config == null) {
328 ignoredVlans.clear();
329 return;
330 }
331 config.ignoredVlans().forEach((deviceId, vlanId) -> {
332 ignoredVlans.remove(deviceId, vlanId);
333 });
334 }
335
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800336 public DhcpRecord getDhcpRelayRecordFor(Ip6Address clientAddress) {
337
Taras Lemkin96a0d342018-03-26 14:52:58 +0000338 Collection<DhcpRecord> records = dhcpRelayStore.getDhcpRecords();
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800339 DhcpRecord dr = null;
340 for (DhcpRecord e:records) {
341 if (e.ip6Address().isPresent()) {
342 if (e.ip6Address().get().equals(clientAddress)) {
343 dr = e;
344 break;
345 }
346 }
347 }
348 return dr;
349 }
350
351 public MacAddress findNextHopMacForIp6FromRelayStore(Ip6Address clientAddress,
352 MacAddress clientMacAddress, VlanId vlanID) {
353
354 DhcpRecord dr = getDhcpRelayRecordFor(clientAddress);
355
356 if (dr != null) {
357 Optional<MacAddress> nextHopTempMac = dr.nextHopTemp();
358 if (nextHopTempMac.isPresent()) {
359 log.info("findNextHopForIp6FromRelayStore " + clientAddress + " got mac " + nextHopTempMac.toString());
360 return nextHopTempMac.get();
361 }
362 } else {
363 log.warn("findNextHopMacForIp6FromRelayStore could NOT find next hop for " + clientAddress);
364 return null;
365 }
366 return null;
367 }
368
369 public Ip6Address findNextHopIp6FromRelayStore(Ip6Address clientAddress) {
370
371 DhcpRecord dr = getDhcpRelayRecordFor(clientAddress);
372 if (dr != null) {
373 Optional<MacAddress> nextHopMac = dr.nextHop();
374 if (nextHopMac.isPresent()) {
375 // find the local ip6 from the host store
376 HostId gwHostId = HostId.hostId(nextHopMac.get(), dr.vlanId());
377 Host gwHost = hostService.getHost(gwHostId);
378 if (gwHost == null) {
379 log.warn("Can't find next hop host ID {}", gwHostId);
380 return null;
381 }
382 Ip6Address nextHopIp = gwHost.ipAddresses()
383 .stream()
384 .filter(IpAddress::isIp6)
385 .filter(IpAddress::isLinkLocal)
386 .map(IpAddress::getIp6Address)
387 .findFirst()
388 .orElse(null);
389
390 log.info("findNextHopIp6FromRelayStore " + clientAddress + " got mac " +
391 nextHopMac.toString() + " ip6 " + nextHopIp);
392 return nextHopIp;
393 }
394 } else {
395 log.warn("findNextHopIp6FromRelayStore could NOT find next hop for " + clientAddress);
396 return null;
397 }
398 return null;
399 }
400
401 private void setPotentialNextHopForIp6InRelayStore(Ip6Address clientAddress,
402 VlanId vlanId, MacAddress nh) {
403 DhcpRecord dr = getDhcpRelayRecordFor(clientAddress);
404 if (dr != null) {
405 dr.nextHopTemp(nh);
406 log.debug("LQ6 potential NH mac " + nh.toString() + " UPDATED in RelayRecord client " + clientAddress);
407 } else {
408 log.warn("LQ6 potential NH mac" + nh.toString() +
409 " NOT FOUND in RelayRecord for client - LQ rejected" + clientAddress);
410 }
411 }
412
413 public void handleLeaseQuery6ReplyMsg(PacketContext context, DHCP6 dhcp6Payload) {
414 ConnectPoint inPort = context.inPacket().receivedFrom();
415 log.info("Got LQV6-REPLY on port {}", inPort);
416 List<Dhcp6Option> lopt = dhcp6Payload.getOptions();
417 log.info("Options list: {}", lopt);
418 // find out if this lease is known is
419 Dhcp6ClientDataOption clientDataOption = dhcp6Payload.getOptions()
420 .stream()
421 .filter(opt -> opt instanceof Dhcp6ClientDataOption)
422 .map(pld -> (Dhcp6ClientDataOption) pld)
423 .findFirst()
424 .orElse(null);
425
426 if (clientDataOption == null) {
427 log.warn("clientDataOption option is not present, " +
428 "lease is UNKNOWN - not adding any new route...");
429 } else {
430 Dhcp6IaAddressOption aiAddressOption = clientDataOption.getOptions()
431 .stream()
432 .filter(opt -> opt instanceof Dhcp6IaAddressOption)
433 .map(pld -> (Dhcp6IaAddressOption) pld)
434 .findFirst()
435 .orElse(null);
436
437 Dhcp6ClientIdOption clientIdOption = clientDataOption.getOptions()
438 .stream()
439 .filter(opt -> opt instanceof Dhcp6ClientIdOption)
440 .map(pld -> (Dhcp6ClientIdOption) pld)
441 .findFirst()
442 .orElse(null);
443
444 if (aiAddressOption == null) {
445 log.warn("clientDataOption from DHCP server does not " +
446 "contains Dhcp6IaAddressOption for the client - giving up...");
447 } else {
448 Ip6Address clientAddress = aiAddressOption.getIp6Address();
449 MacAddress clientMacAddress = MacAddress.valueOf(clientIdOption.getDuid().getLinkLayerAddress());
450 Ethernet packet = context.inPacket().parsed();
451 VlanId vlanId = VlanId.vlanId(packet.getVlanID());
452 MacAddress potentialNextHopMac =
453 findNextHopMacForIp6FromRelayStore(clientAddress, clientMacAddress, vlanId);
454
455 if (potentialNextHopMac == null) {
456 log.warn("Can't find next hop host mac for client {} mac:{}/{}",
457 clientAddress, clientMacAddress, vlanId);
458 return;
459 } else {
460 log.info("Next hop mac for {}/{}/{} is {}", clientAddress,
461 clientMacAddress, vlanId, potentialNextHopMac.toString());
462 }
463 // search the next hop in the hosts store
464 HostId gwHostId = HostId.hostId(potentialNextHopMac, vlanId);
465 Host gwHost = hostService.getHost(gwHostId);
466 if (gwHost == null) {
467 log.warn("Can't find next hop host ID {}", gwHostId);
468 return;
469 }
470 Ip6Address nextHopIp = gwHost.ipAddresses()
471 .stream()
472 .filter(IpAddress::isIp6)
473 .filter(IpAddress::isLinkLocal)
474 .map(IpAddress::getIp6Address)
475 .findFirst()
476 .orElse(null);
477 if (nextHopIp == null) {
478 log.warn("Can't find IP6 address of next hop {}", gwHost);
479 return;
480 }
481 log.info("client " + clientAddress + " is known !");
Kalhee Kim54a97c92018-04-02 21:23:46 +0000482 Route routeForIP6 = new Route(Route.Source.DHCP, clientAddress.toIpPrefix(), nextHopIp);
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800483 log.debug("updating route of Client for indirectly connected.");
484 log.debug("client ip: " + clientAddress + ", next hop ip6: " + nextHopIp);
485 routeStore.updateRoute(routeForIP6);
486 }
487 }
488 }
489
Saurav Dasb805f1a2017-12-13 16:19:35 -0800490 @Override
Kalhee Kim45fede42017-09-05 19:05:06 +0000491 public void processDhcpPacket(PacketContext context, BasePacket payload) {
492 checkNotNull(payload, "DHCP6 payload can't be null");
493 checkState(payload instanceof DHCP6, "Payload is not a DHCP6");
494 DHCP6 dhcp6Payload = (DHCP6) payload;
495 Ethernet receivedPacket = context.inPacket().parsed();
496
497 if (!configured()) {
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800498 log.warn("Missing DHCP6 relay server config. " +
499 "Abort packet processing dhcp6 payload {}", dhcp6Payload);
Kalhee Kim45fede42017-09-05 19:05:06 +0000500 return;
501 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000502 byte msgTypeVal = dhcp6Payload.getMsgType();
503 MsgType msgType = DHCP6.MsgType.getType(msgTypeVal);
504 log.debug("msgType is {}", msgType);
Kalhee Kim45fede42017-09-05 19:05:06 +0000505
Kalhee Kim45fede42017-09-05 19:05:06 +0000506 ConnectPoint inPort = context.inPacket().receivedFrom();
Jonathan Hart8ca2bc02017-11-30 18:23:42 -0800507
Kalhee Kim495c9b22017-11-07 16:32:09 +0000508 if (inPort == null) {
Taras Lemkin96a0d342018-03-26 14:52:58 +0000509 log.warn("incoming ConnectPoint is null");
Kalhee Kim495c9b22017-11-07 16:32:09 +0000510 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000511 Set<Interface> receivingInterfaces = interfaceService.getInterfacesByPort(inPort);
512 //ignore the packets if dhcp client interface is not configured on onos.
513 if (receivingInterfaces.isEmpty()) {
514 log.warn("Virtual interface is not configured on {}", inPort);
515 return;
516 }
517
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800518 if (msgTypeVal == DHCP6.MsgType.LEASEQUERY.value()) {
Taras Lemkin96a0d342018-03-26 14:52:58 +0000519 List<InternalPacket> ethernetClientPackets =
520 learnRouteFromLeasequery ?
521 processLQ6PacketFromClient(context, receivedPacket, receivingInterfaces, dhcp6Payload) :
522 processDhcp6ForwardOnly(context, receivedPacket, receivingInterfaces, dhcp6Payload);
523 for (InternalPacket internalPacket : ethernetClientPackets) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000524 forwardPacket(internalPacket);
Kalhee Kim45fede42017-09-05 19:05:06 +0000525 }
Taras Lemkin96a0d342018-03-26 14:52:58 +0000526 } else if (msgTypeVal == DHCP6.MsgType.LEASEQUERY_REPLY.value() && learnRouteFromLeasequery) {
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800527 IPv6 clientIpv6 = (IPv6) receivedPacket.getPayload();
528 UDP clientUdp = (UDP) clientIpv6.getPayload();
529 DHCP6 clientDhcp6 = (DHCP6) clientUdp.getPayload();
Taras Lemkin96a0d342018-03-26 14:52:58 +0000530 Interface serverInterface = Dhcp6HandlerUtil.directlyConnected(clientDhcp6) ?
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800531 getServerInterface() : getIndirectServerInterface();
Kalhee Kim45fede42017-09-05 19:05:06 +0000532 InternalPacket ethernetPacketReply =
Taras Lemkin96a0d342018-03-26 14:52:58 +0000533 Dhcp6HandlerUtil.processLQ6PacketFromServer(
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800534 defaultServerInfoList, indirectServerInfoList,
535 serverInterface, interfaceService,
536 hostService,
537 context, receivedPacket, receivingInterfaces);
Kalhee Kim45fede42017-09-05 19:05:06 +0000538 if (ethernetPacketReply != null) {
539 forwardPacket(ethernetPacketReply);
540 }
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800541 handleLeaseQuery6ReplyMsg(context, dhcp6Payload);
Taras Lemkin96a0d342018-03-26 14:52:58 +0000542 } else if (MSG_TYPE_FROM_CLIENT.contains(msgTypeVal)) {
543 List<InternalPacket> ethernetClientPacket =
544 processDhcp6PacketFromClient(context, receivedPacket, receivingInterfaces);
545 for (InternalPacket internalPacket : ethernetClientPacket) {
546 forwardPacket(internalPacket);
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800547 }
Taras Lemkin96a0d342018-03-26 14:52:58 +0000548 } else if (MSG_TYPE_FROM_SERVER.contains(msgTypeVal)) {
549 log.debug("calling processDhcp6PacketFromServer with RELAY_REPL {}, {}", receivedPacket, dhcp6Payload);
550 InternalPacket ethernetPacketReply =
551 processDhcp6PacketFromServer(context, receivedPacket, receivingInterfaces);
552 if (ethernetPacketReply != null) {
553 forwardPacket(ethernetPacketReply);
554 }
555 } else {
556 log.warn("Not so fast, packet type {} not supported yet", msgTypeVal);
Kalhee Kim45fede42017-09-05 19:05:06 +0000557 }
558 }
559
Kalhee Kim45fede42017-09-05 19:05:06 +0000560 /**
561 * Checks if this app has been configured.
562 *
563 * @return true if all information we need have been initialized
564 */
565 public boolean configured() {
Yi Tseng919b2df2017-09-07 16:22:51 -0700566 return !defaultServerInfoList.isEmpty();
Kalhee Kim45fede42017-09-05 19:05:06 +0000567 }
568
Yi Tsengaa417a62017-09-08 17:22:51 -0700569 @Override
570 public ProviderId id() {
Charles Chand988c282017-09-12 17:09:32 -0700571 return PROVIDER_ID;
Yi Tsengaa417a62017-09-08 17:22:51 -0700572 }
573
574 @Override
575 public void triggerProbe(Host host) {
576 // Do nothing here
577 }
578
Kalhee Kim45fede42017-09-05 19:05:06 +0000579 //forward the packet to ConnectPoint where the DHCP server is attached.
580 private void forwardPacket(InternalPacket packet) {
581 //send Packetout to dhcp server connectpoint.
Taras Lemkin96a0d342018-03-26 14:52:58 +0000582 if (packet.getDestLocation() != null) {
Kalhee Kim45fede42017-09-05 19:05:06 +0000583 TrafficTreatment t = DefaultTrafficTreatment.builder()
Taras Lemkin96a0d342018-03-26 14:52:58 +0000584 .setOutput(packet.getDestLocation().port()).build();
Kalhee Kim45fede42017-09-05 19:05:06 +0000585 OutboundPacket o = new DefaultOutboundPacket(
Taras Lemkin96a0d342018-03-26 14:52:58 +0000586 packet.getDestLocation().deviceId(), t, ByteBuffer.wrap(packet.getPacket().serialize()));
Kalhee Kim45fede42017-09-05 19:05:06 +0000587 packetService.emit(o);
Taras Lemkin96a0d342018-03-26 14:52:58 +0000588 if (log.isTraceEnabled()) {
589 IPv6 ip6 = (IPv6) packet.getPacket().getPayload();
590 UDP udp = (UDP) ip6.getPayload();
591 DHCP6 dhcp6 = (DHCP6) udp.getPayload();
592 log.trace("Relaying packet to destination {} eth: {} dhcp: {}",
593 packet.getDestLocation(), packet.getPacket(), dhcp6);
594 }
595
596 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000597 }
598
Kalhee Kim45fede42017-09-05 19:05:06 +0000599 /**
600 * extract from dhcp6 packet client ipv6 address of given by dhcp server.
601 *
602 * @param dhcp6 the dhcp6 packet
Kalhee Kim495c9b22017-11-07 16:32:09 +0000603 * @return IpAddressInfo IpAddressInfo given by dhcp server, or null if not exists
Kalhee Kim45fede42017-09-05 19:05:06 +0000604 */
Kalhee Kim495c9b22017-11-07 16:32:09 +0000605 private IpAddressInfo extractIpAddress(DHCP6 dhcp6) {
606 IpAddressInfo ipInfo = new IpAddressInfo();
Kalhee Kim45fede42017-09-05 19:05:06 +0000607
608 log.debug("extractIpAddress enters dhcp6 {}.", dhcp6);
609 // Extract IPv6 address from IA NA ot IA TA option
610 Optional<Dhcp6IaNaOption> iaNaOption = dhcp6.getOptions()
611 .stream()
612 .filter(opt -> opt instanceof Dhcp6IaNaOption)
613 .map(opt -> (Dhcp6IaNaOption) opt)
614 .findFirst();
615 Optional<Dhcp6IaTaOption> iaTaOption = dhcp6.getOptions()
616 .stream()
617 .filter(opt -> opt instanceof Dhcp6IaTaOption)
618 .map(opt -> (Dhcp6IaTaOption) opt)
619 .findFirst();
620 Optional<Dhcp6IaAddressOption> iaAddressOption;
621 if (iaNaOption.isPresent()) {
622 log.debug("Found IPv6 address from iaNaOption {}", iaNaOption);
623
624 iaAddressOption = iaNaOption.get().getOptions().stream()
625 .filter(opt -> opt instanceof Dhcp6IaAddressOption)
626 .map(opt -> (Dhcp6IaAddressOption) opt)
627 .findFirst();
628 } else if (iaTaOption.isPresent()) {
629 log.debug("Found IPv6 address from iaTaOption {}", iaTaOption);
630
631 iaAddressOption = iaTaOption.get().getOptions().stream()
632 .filter(opt -> opt instanceof Dhcp6IaAddressOption)
633 .map(opt -> (Dhcp6IaAddressOption) opt)
634 .findFirst();
635 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000636 log.info("No IPv6 address found from iaTaOption {}", iaTaOption);
Kalhee Kim45fede42017-09-05 19:05:06 +0000637 iaAddressOption = Optional.empty();
638 }
639 if (iaAddressOption.isPresent()) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000640 ipInfo.ip6Address = iaAddressOption.get().getIp6Address();
641 ipInfo.prefTime = iaAddressOption.get().getPreferredLifetime();
Kalhee Kim45fede42017-09-05 19:05:06 +0000642 log.debug("Found IPv6 address from iaAddressOption {}", iaAddressOption);
Kalhee Kim45fede42017-09-05 19:05:06 +0000643 } else {
644 log.debug("Can't find IPv6 address from DHCPv6 {}", dhcp6);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000645 return null;
Kalhee Kim45fede42017-09-05 19:05:06 +0000646 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000647 return ipInfo;
Kalhee Kim45fede42017-09-05 19:05:06 +0000648 }
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800649
Kalhee Kim45fede42017-09-05 19:05:06 +0000650 /**
651 * extract from dhcp6 packet Prefix prefix provided by dhcp server.
652 *
653 * @param dhcp6 the dhcp6 payload
654 * @return IpPrefix Prefix Delegation prefix, or null if not exists.
655 */
Kalhee Kim495c9b22017-11-07 16:32:09 +0000656 private PdPrefixInfo extractPrefix(DHCP6 dhcp6) {
657 log.debug("extractPrefix enters {}", dhcp6);
Kalhee Kim45fede42017-09-05 19:05:06 +0000658
659 // extract prefix
Kalhee Kim495c9b22017-11-07 16:32:09 +0000660 PdPrefixInfo pdPrefixInfo = new PdPrefixInfo();
Kalhee Kim45fede42017-09-05 19:05:06 +0000661
662 Ip6Address prefixAddress = null;
663
664 // Extract IPv6 prefix from IA PD option
665 Optional<Dhcp6IaPdOption> iaPdOption = dhcp6.getOptions()
666 .stream()
667 .filter(opt -> opt instanceof Dhcp6IaPdOption)
668 .map(opt -> (Dhcp6IaPdOption) opt)
669 .findFirst();
670
671 Optional<Dhcp6IaPrefixOption> iaPrefixOption;
672 if (iaPdOption.isPresent()) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000673 log.debug("IA_PD option found {}", iaPdOption);
Kalhee Kim45fede42017-09-05 19:05:06 +0000674
675 iaPrefixOption = iaPdOption.get().getOptions().stream()
676 .filter(opt -> opt instanceof Dhcp6IaPrefixOption)
677 .map(opt -> (Dhcp6IaPrefixOption) opt)
678 .findFirst();
679 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000680 log.debug("IA_PD option NOT found");
Kalhee Kim45fede42017-09-05 19:05:06 +0000681
682 iaPrefixOption = Optional.empty();
683 }
684 if (iaPrefixOption.isPresent()) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000685 log.debug("IAPrefix Option within IA_PD option found {}", iaPrefixOption);
Kalhee Kim45fede42017-09-05 19:05:06 +0000686
687 prefixAddress = iaPrefixOption.get().getIp6Prefix();
688 int prefixLen = (int) iaPrefixOption.get().getPrefixLength();
Kalhee Kim495c9b22017-11-07 16:32:09 +0000689 log.debug("Prefix length is {} bits", prefixLen);
690 pdPrefixInfo.pdPrefix = IpPrefix.valueOf(prefixAddress, prefixLen);
691 pdPrefixInfo.prefTime = iaPrefixOption.get().getPreferredLifetime();
Kalhee Kim45fede42017-09-05 19:05:06 +0000692 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000693 log.debug("Can't find IPv6 prefix from DHCPv6 {}", dhcp6);
694 return null;
Kalhee Kim45fede42017-09-05 19:05:06 +0000695 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000696 return pdPrefixInfo;
Kalhee Kim45fede42017-09-05 19:05:06 +0000697 }
698
699 /**
Kalhee Kim495c9b22017-11-07 16:32:09 +0000700 * remove host or route and update dhcp relay record attributes.
Kalhee Kim45fede42017-09-05 19:05:06 +0000701 *
702 * @param directConnFlag flag to show that packet is from directly connected client
Kalhee Kim495c9b22017-11-07 16:32:09 +0000703 * @param location client side connect point
Kalhee Kim45fede42017-09-05 19:05:06 +0000704 * @param dhcp6Packet the dhcp6 payload
705 * @param clientPacket client's ethernet packet
706 * @param clientIpv6 client's Ipv6 packet
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +0000707 * @param clientInterface client interfaces
Kalhee Kim45fede42017-09-05 19:05:06 +0000708 */
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000709 private void removeHostOrRoute(boolean directConnFlag, ConnectPoint location,
710 DHCP6 dhcp6Packet,
Kalhee Kim45fede42017-09-05 19:05:06 +0000711 Ethernet clientPacket, IPv6 clientIpv6,
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +0000712 Interface clientInterface) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000713 log.debug("removeHostOrRoute enters {}", dhcp6Packet);
Kalhee Kim121ba922017-11-01 17:56:44 +0000714 VlanId vlanId = clientInterface.vlan();
Kalhee Kim495c9b22017-11-07 16:32:09 +0000715 MacAddress srcMac = clientPacket.getSourceMAC(); // could be gw or host
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000716 MacAddress leafClientMac;
717 byte leafMsgType;
Kalhee Kim495c9b22017-11-07 16:32:09 +0000718 log.debug("client mac {} client vlan {}", HexString.toHexString(srcMac.toBytes(), ":"), vlanId);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000719
Charles Chan24a96ff2018-08-13 10:32:03 -0700720 Dhcp6ClientIdOption clientIdOption = Dhcp6HandlerUtil.extractClientId(directConnFlag, dhcp6Packet);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000721 if (clientIdOption != null) {
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000722 if ((clientIdOption.getDuid().getDuidType() == Dhcp6Duid.DuidType.DUID_LLT) ||
723 (clientIdOption.getDuid().getDuidType() == Dhcp6Duid.DuidType.DUID_LL)) {
724 leafClientMac = MacAddress.valueOf(clientIdOption.getDuid().getLinkLayerAddress());
725 } else {
726 log.warn("Link-Layer Address not supported in CLIENTID option. No DhcpRelay Record created.");
Charles Chanb87495c2019-02-01 11:31:50 -0800727 //dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_LINKLOCAL_FAIL);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000728 return;
729 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000730 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000731 log.warn("CLIENTID option NOT found. Don't create DhcpRelay Record.");
Charles Chanb87495c2019-02-01 11:31:50 -0800732 //dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_CLIENTID_FAIL);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000733 return;
734 }
735
Kalhee Kim495c9b22017-11-07 16:32:09 +0000736 HostId leafHostId = HostId.hostId(leafClientMac, vlanId);
737 DhcpRecord record = dhcpRelayStore.getDhcpRecord(leafHostId).orElse(null);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000738 if (record == null) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000739 record = new DhcpRecord(leafHostId);
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000740 } else {
741 record = record.clone();
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000742 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000743
Taras Lemkin96a0d342018-03-26 14:52:58 +0000744 Boolean isMsgRelease = Dhcp6HandlerUtil.isDhcp6Release(dhcp6Packet);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000745 IpAddressInfo ipInfo;
746 PdPrefixInfo pdInfo = null;
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000747 if (directConnFlag) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000748 // Add to host store if it is connected to network directly
749 ipInfo = extractIpAddress(dhcp6Packet);
750 if (ipInfo != null) {
751 if (isMsgRelease) {
752 HostId hostId = HostId.hostId(srcMac, vlanId);
753 log.debug("remove Host {} ip for directly connected.", hostId.toString());
754 providerService.removeIpFromHost(hostId, ipInfo.ip6Address);
Kalhee Kim45fede42017-09-05 19:05:06 +0000755 }
756 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000757 log.debug("ipAddress not found. Do not remove Host {} for directly connected.",
758 HostId.hostId(srcMac, vlanId).toString());
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000759 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000760 leafMsgType = dhcp6Packet.getMsgType();
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000761 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000762 // Remove from route store if it is not connected to network directly
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000763 // pick out the first link-local ip address
Kalhee Kim495c9b22017-11-07 16:32:09 +0000764 IpAddress nextHopIp = getFirstIpByHost(directConnFlag, srcMac, vlanId);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000765 if (nextHopIp == null) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000766 log.warn("Can't find link-local IP address of gateway mac {} vlanId {}", srcMac, vlanId);
Charles Chanb87495c2019-02-01 11:31:50 -0800767 //dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_LINKLOCAL_GW);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000768 return;
769 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000770
Taras Lemkin96a0d342018-03-26 14:52:58 +0000771 DHCP6 leafDhcp = Dhcp6HandlerUtil.getDhcp6Leaf(dhcp6Packet);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000772 ipInfo = extractIpAddress(leafDhcp);
773 if (ipInfo == null) {
774 log.debug("ip is null");
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000775 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000776 if (isMsgRelease) {
Kalhee Kim54a97c92018-04-02 21:23:46 +0000777 Route routeForIP = new Route(Route.Source.DHCP, ipInfo.ip6Address.toIpPrefix(), nextHopIp);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000778 log.debug("removing route of 128 address for indirectly connected.");
779 log.debug("128 ip {}, nexthop {}",
780 HexString.toHexString(ipInfo.ip6Address.toOctets(), ":"),
781 HexString.toHexString(nextHopIp.toOctets(), ":"));
782 routeStore.removeRoute(routeForIP);
Kalhee Kim45fede42017-09-05 19:05:06 +0000783 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000784 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000785
Kalhee Kim495c9b22017-11-07 16:32:09 +0000786 pdInfo = extractPrefix(leafDhcp);
787 if (pdInfo == null) {
788 log.debug("ipPrefix is null ");
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000789 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000790 if (isMsgRelease) {
Kalhee Kim54a97c92018-04-02 21:23:46 +0000791 Route routeForPrefix = new Route(Route.Source.DHCP, pdInfo.pdPrefix, nextHopIp);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000792 log.debug("removing route of PD for indirectly connected.");
793 log.debug("pd ip {}, nexthop {}",
794 HexString.toHexString(pdInfo.pdPrefix.address().toOctets(), ":"),
795 HexString.toHexString(nextHopIp.toOctets(), ":"));
796
797 routeStore.removeRoute(routeForPrefix);
Kalhee Kimba366062017-11-07 16:32:09 +0000798 if (this.dhcpFpmEnabled) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000799 dhcpFpmPrefixStore.removeFpmRecord(pdInfo.pdPrefix);
Kalhee Kimba366062017-11-07 16:32:09 +0000800 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000801 }
802 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000803 leafMsgType = leafDhcp.getMsgType();
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000804 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000805
Kalhee Kim495c9b22017-11-07 16:32:09 +0000806 if (isMsgRelease) {
807 log.debug("DHCP6 RELEASE msg.");
808 if (record != null) {
809 if (ipInfo != null) {
810 log.debug("DhcpRelay Record ip6Address is set to null.");
811 record.ip6Address(null);
812 }
813 if (pdInfo != null) {
814 log.debug("DhcpRelay Record pdPrefix is set to null.");
815 }
816
817 if (!record.ip6Address().isPresent() && !record.pdPrefix().isPresent()) {
818 log.warn("IP6 address and IP6 PD both are null. Remove record.");
819 // do not remove a record. Let timer task handler it.
820 //dhcpRelayStore.removeDhcpRecord(HostId.hostId(leafClientMac, vlanId));
821 }
822 }
823 }
824
Ray Milkeyffe1a332018-01-24 10:41:14 -0800825 if (record != null) {
Taras Lemkin96a0d342018-03-26 14:52:58 +0000826 record.getV6Counters().incrementCounter(Dhcp6HandlerUtil.getMsgTypeStr(leafMsgType));
Ray Milkeyffe1a332018-01-24 10:41:14 -0800827 record.addLocation(new HostLocation(location, System.currentTimeMillis()));
828 record.ip6Status(DHCP6.MsgType.getType(leafMsgType));
829 record.setDirectlyConnected(directConnFlag);
830 if (!directConnFlag) {
831 // Update gateway mac address if the host is not directly connected
832 record.nextHop(srcMac);
833 }
834 record.updateLastSeen();
Kalhee Kim495c9b22017-11-07 16:32:09 +0000835 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000836 dhcpRelayStore.updateDhcpRecord(leafHostId, record);
Charles Chanb87495c2019-02-01 11:31:50 -0800837 /*
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000838 // TODO Use AtomicInteger for the counters
839 try {
840 recordSemaphore.acquire();
841 try {
Taras Lemkin96a0d342018-03-26 14:52:58 +0000842 dhcpRelayCountersStore.incrementCounter(gCount, Dhcp6HandlerUtil.getMsgTypeStr(leafMsgType));
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000843 } finally {
844 // calling release() after a successful acquire()
845 recordSemaphore.release();
846 }
847 } catch (InterruptedException e) {
Ray Milkeyba547f92018-02-01 15:22:31 -0800848 Thread.currentThread().interrupt();
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000849 }
Charles Chanb87495c2019-02-01 11:31:50 -0800850 */
Kalhee Kim495c9b22017-11-07 16:32:09 +0000851 }
852
853 /**
854 * add host or route and update dhcp relay record.
855 *
856 * @param directConnFlag flag to show that packet is from directly connected client
857 * @param location client side connect point
858 * @param dhcp6Relay the dhcp6 payload
859 * @param embeddedDhcp6 the dhcp6 payload within relay
860 * @param srcMac client gw/host macAddress
861 * @param clientInterface client interface
pierd9909e82019-08-23 10:45:40 -0700862 * @param vlanIdInUse vlanid encoded in the interface id Option
Kalhee Kim495c9b22017-11-07 16:32:09 +0000863 */
864 private void addHostOrRoute(boolean directConnFlag, ConnectPoint location, DHCP6 dhcp6Relay,
pierd9909e82019-08-23 10:45:40 -0700865 DHCP6 embeddedDhcp6, MacAddress srcMac, Interface clientInterface,
866 VlanId vlanIdInUse) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000867 log.debug("addHostOrRoute entered.");
pierd9909e82019-08-23 10:45:40 -0700868 VlanId vlanId;
869 if (clientInterface.vlanTagged().isEmpty()) {
870 vlanId = clientInterface.vlan();
871 } else {
872 // might be multiple vlan in same interface
873 vlanId = vlanIdInUse;
874 }
875 if (vlanId == null) {
876 vlanId = VlanId.NONE;
877 }
878
Taras Lemkin96a0d342018-03-26 14:52:58 +0000879 Boolean isMsgReply = Dhcp6HandlerUtil.isDhcp6Reply(dhcp6Relay);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000880 MacAddress leafClientMac;
881 Byte leafMsgType;
882
Charles Chan24a96ff2018-08-13 10:32:03 -0700883 Dhcp6ClientIdOption clientIdOption = Dhcp6HandlerUtil.extractClientId(directConnFlag, embeddedDhcp6);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000884 if (clientIdOption != null) {
885 log.debug("CLIENTID option found {}", clientIdOption);
886 if ((clientIdOption.getDuid().getDuidType() == Dhcp6Duid.DuidType.DUID_LLT) ||
887 (clientIdOption.getDuid().getDuidType() == Dhcp6Duid.DuidType.DUID_LL)) {
888 leafClientMac = MacAddress.valueOf(clientIdOption.getDuid().getLinkLayerAddress());
889 } else {
890 log.warn("Link-Layer Address not supported in CLIENTID option. No DhcpRelay Record created.");
Charles Chanb87495c2019-02-01 11:31:50 -0800891 //dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_LINKLOCAL_FAIL);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000892 return;
893 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000894 } else {
895 log.warn("CLIENTID option NOT found. No DhcpRelay Record created.");
Charles Chanb87495c2019-02-01 11:31:50 -0800896 //dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_CLIENTID_FAIL);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000897 return;
898 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000899 HostId leafHostId = HostId.hostId(leafClientMac, vlanId);
900 DhcpRecord record = dhcpRelayStore.getDhcpRecord(leafHostId).orElse(null);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000901 if (record == null) {
902 record = new DhcpRecord(HostId.hostId(leafClientMac, vlanId));
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000903 } else {
904 record = record.clone();
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000905 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000906
907 IpAddressInfo ipInfo;
908 PdPrefixInfo pdInfo = null;
909 if (directConnFlag) {
910 // Add to host store if it connect to network directly
911 ipInfo = extractIpAddress(embeddedDhcp6);
912 if (ipInfo != null) {
913 if (isMsgReply) {
914 Set<IpAddress> ips = Sets.newHashSet(ipInfo.ip6Address);
915 HostId hostId = HostId.hostId(srcMac, vlanId);
916 Host host = hostService.getHost(hostId);
917 HostLocation hostLocation = new HostLocation(clientInterface.connectPoint(),
918 System.currentTimeMillis());
919 Set<HostLocation> hostLocations = Sets.newHashSet(hostLocation);
920 if (host != null) {
921 // Dual homing support:
922 // if host exists, use old locations and new location
923 hostLocations.addAll(host.locations());
924 }
925 HostDescription desc = new DefaultHostDescription(srcMac, vlanId, hostLocations, ips,
926 false);
927 log.debug("adding Host for directly connected.");
928 log.debug("client mac {} client vlan {} hostlocation {}",
929 HexString.toHexString(srcMac.toBytes(), ":"), vlanId, hostLocation.toString());
930 // Replace the ip when dhcp server give the host new ip address
931 providerService.hostDetected(hostId, desc, false);
932 }
933 } else {
934 log.warn("ipAddress not found. Do not add Host {} for directly connected.",
935 HostId.hostId(srcMac, vlanId).toString());
936 }
937 leafMsgType = embeddedDhcp6.getMsgType();
938 } else {
939 // Add to route store if it does not connect to network directly
940 // pick out the first link-local ip address
941 IpAddress nextHopIp = getFirstIpByHost(directConnFlag, srcMac, vlanId);
942 if (nextHopIp == null) {
943 log.warn("Can't find link-local IP address of gateway mac {} vlanId {}", srcMac, vlanId);
Charles Chanb87495c2019-02-01 11:31:50 -0800944 //dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_LINKLOCAL_GW);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000945 return;
946 }
947
Taras Lemkin96a0d342018-03-26 14:52:58 +0000948 DHCP6 leafDhcp = Dhcp6HandlerUtil.getDhcp6Leaf(embeddedDhcp6);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000949 ipInfo = extractIpAddress(leafDhcp);
950 if (ipInfo == null) {
951 log.debug("ip is null");
952 } else {
953 if (isMsgReply) {
Kalhee Kim54a97c92018-04-02 21:23:46 +0000954 Route routeForIP = new Route(Route.Source.DHCP, ipInfo.ip6Address.toIpPrefix(), nextHopIp);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000955 log.debug("adding Route of 128 address for indirectly connected.");
Daniel Ginsburg83b76452018-06-09 01:43:59 +0300956 routeStore.replaceRoute(routeForIP);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000957 }
958 }
959
960 pdInfo = extractPrefix(leafDhcp);
961 if (pdInfo == null) {
962 log.debug("ipPrefix is null ");
963 } else {
964 if (isMsgReply) {
Kalhee Kim54a97c92018-04-02 21:23:46 +0000965 Route routeForPrefix = new Route(Route.Source.DHCP, pdInfo.pdPrefix, nextHopIp);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000966 log.debug("adding Route of PD for indirectly connected.");
Daniel Ginsburg83b76452018-06-09 01:43:59 +0300967 routeStore.replaceRoute(routeForPrefix);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000968 if (this.dhcpFpmEnabled) {
969 FpmRecord fpmRecord = new FpmRecord(pdInfo.pdPrefix, nextHopIp, FpmRecord.Type.DHCP_RELAY);
970 dhcpFpmPrefixStore.addFpmRecord(pdInfo.pdPrefix, fpmRecord);
971 }
972 }
973 }
974 leafMsgType = leafDhcp.getMsgType();
975 }
976 if (leafMsgType == DHCP6.MsgType.RELEASE.value() ||
977 (leafMsgType == DHCP6.MsgType.REPLY.value()) && ipInfo == null) {
978 log.warn("DHCP6 RELEASE/REPLY(null ip) from Server. MsgType {}", leafMsgType);
979 //return;
980 }
981
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000982 record.addLocation(new HostLocation(location, System.currentTimeMillis()));
Kalhee Kim2f07c602017-11-15 18:57:53 +0000983
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000984 if (leafMsgType == DHCP6.MsgType.REPLY.value()) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000985 if (ipInfo != null) {
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000986 log.debug("IP6 address is being stored into dhcp-relay store.");
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800987 log.debug("Client IP6 address {}", HexString.toHexString(ipInfo.ip6Address.toOctets(), ":"));
Kalhee Kim495c9b22017-11-07 16:32:09 +0000988 record.ip6Address(ipInfo.ip6Address);
Kalhee Kim2f07c602017-11-15 18:57:53 +0000989 record.updateAddrPrefTime(ipInfo.prefTime);
990 record.updateLastIp6Update();
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800991 } else {
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000992 log.debug("IP6 address is not returned from server. Maybe only PD is returned.");
993 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000994 if (pdInfo != null) {
995 log.debug("IP6 PD address {}",
996 HexString.toHexString(pdInfo.pdPrefix.address().toOctets(), ":"));
Kalhee Kim2f07c602017-11-15 18:57:53 +0000997 record.pdPrefix(pdInfo.pdPrefix);
998 record.updatePdPrefTime(pdInfo.prefTime);
999 record.updateLastPdUpdate();
Kalhee Kimea4b6c22017-11-09 14:38:37 +00001000 } else {
1001 log.debug("IP6 PD address is not returned from server. Maybe only IPAddress is returned.");
1002 }
1003 }
Kalhee Kim2f07c602017-11-15 18:57:53 +00001004
Taras Lemkin96a0d342018-03-26 14:52:58 +00001005 record.getV6Counters().incrementCounter(Dhcp6HandlerUtil.getMsgTypeStr(leafMsgType));
Kalhee Kim495c9b22017-11-07 16:32:09 +00001006 record.ip6Status(DHCP6.MsgType.getType(leafMsgType));
Kalhee Kimea4b6c22017-11-09 14:38:37 +00001007 record.setDirectlyConnected(directConnFlag);
1008 record.updateLastSeen();
Kalhee Kim495c9b22017-11-07 16:32:09 +00001009 dhcpRelayStore.updateDhcpRecord(leafHostId, record);
Charles Chanb87495c2019-02-01 11:31:50 -08001010 /*
Kalhee Kimd94ceea2017-11-29 19:03:02 +00001011 // TODO Use AtomicInteger for the counters
1012 try {
1013 recordSemaphore.acquire();
1014 try {
Taras Lemkin96a0d342018-03-26 14:52:58 +00001015 dhcpRelayCountersStore.incrementCounter(gCount, Dhcp6HandlerUtil.getMsgTypeStr(leafMsgType));
Kalhee Kimd94ceea2017-11-29 19:03:02 +00001016 } finally {
1017 // calling release() after a successful acquire()
1018 recordSemaphore.release();
1019 }
1020 } catch (InterruptedException e) {
Ray Milkeyba547f92018-02-01 15:22:31 -08001021 Thread.currentThread().interrupt();
Kalhee Kimd94ceea2017-11-29 19:03:02 +00001022 }
Charles Chanb87495c2019-02-01 11:31:50 -08001023 */
Kalhee Kim45fede42017-09-05 19:05:06 +00001024 }
1025
Taras Lemkin96a0d342018-03-26 14:52:58 +00001026 private List<InternalPacket> processDhcp6ForwardOnly(PacketContext context,
1027 Ethernet clientPacket,
1028 Set<Interface> clientInterfaces,
1029 DHCP6 dhcpPacket) {
1030 ConnectPoint inPort = context.inPacket().receivedFrom();
1031 log.trace("Got DHCPv6 on port {}", inPort);
1032 boolean directConnFlag = Dhcp6HandlerUtil.directlyConnected(dhcpPacket);
1033
1034 List<InternalPacket> internalPackets = new ArrayList<>();
1035 List<DhcpServerInfo> serverInfoList = findValidServerInfo(directConnFlag);
1036
1037 for (DhcpServerInfo dhcpServer : serverInfoList) {
Charles Chan24a96ff2018-08-13 10:32:03 -07001038 Interface serverInterface = getServerInterface(dhcpServer);
1039 if (serverInterface == null) {
1040 log.warn("Can't get server interface, ignore");
1041 continue;
1042 }
1043
Taras Lemkin96a0d342018-03-26 14:52:58 +00001044 Ethernet newPacket = Dhcp6HandlerUtil.buildDhcp6PacketFromClient(context,
Charles Chan24a96ff2018-08-13 10:32:03 -07001045 clientPacket, clientInterfaces, dhcpServer, serverInterface);
Taras Lemkin96a0d342018-03-26 14:52:58 +00001046 log.trace("Built packet for server {} : {}", dhcpServer, newPacket);
1047 internalPackets.add(InternalPacket.internalPacket(newPacket,
1048 dhcpServer.getDhcpServerConnectPoint().get()));
1049 }
1050
1051 return internalPackets;
1052 }
1053
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001054 private List<InternalPacket> processLQ6PacketFromClient(PacketContext context,
1055 Ethernet clientPacket,
1056 Set<Interface> clientInterfaces,
1057 DHCP6 dhcp6Payload) {
1058 ConnectPoint inPort = context.inPacket().receivedFrom();
1059 log.info("Got LQ-REQUEST V6 on port {}", inPort);
1060 List<Dhcp6Option> lopt = dhcp6Payload.getOptions();
1061 log.info("Options list: {}", lopt);
1062 Dhcp6LeaseQueryOption lqoption = dhcp6Payload.getOptions()
1063 .stream()
1064 .filter(opt -> opt instanceof Dhcp6LeaseQueryOption)
1065 .map(pld -> (Dhcp6LeaseQueryOption) pld)
1066 .findFirst()
1067 .orElse(null);
1068
1069 if (lqoption == null) {
1070 // Can't find dhcp payload
1071 log.warn("Can't find dhcp6 lease query message - aborting");
1072 return null;
1073 } else {
1074 log.info("dhcp6 lqv6 options found: {}", lqoption);
1075 }
1076 log.warn("LQv6 for " + lqoption.linkAddress.toString() + " comes from " + inPort.toString());
1077 Ethernet packet = context.inPacket().parsed();
1078 Ip6Address clientAddress = lqoption.linkAddress;
1079 IPv6 ipv6Packet = (IPv6) packet.getPayload();
1080 Ip6Address nextHopIp = findNextHopIp6FromRelayStore(clientAddress);
1081
1082 // 1. only if there is a route to remove - remove it
1083 if (nextHopIp != null) {
Kalhee Kim54a97c92018-04-02 21:23:46 +00001084 Route routeForIP6 = new Route(Route.Source.DHCP, clientAddress.toIpPrefix(), nextHopIp);
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001085 log.debug("Removing route of Client " + clientAddress +
1086 " for indirectly connected - next hop ip6 " + nextHopIp);
1087 routeStore.removeRoute(routeForIP6);
1088 }
1089
1090 // 2. note the potential NH this packet came from in case it's a known lease
1091 // this NH will then be used to build the route
1092 MacAddress potentialNH = packet.getSourceMAC();
1093 VlanId vlanId = VlanId.vlanId(packet.getVlanID());
1094 setPotentialNextHopForIp6InRelayStore(clientAddress, vlanId, potentialNH);
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001095 // 3. route this LQ6 to all relevant servers
1096 IPv6 clientIpv6 = (IPv6) clientPacket.getPayload();
1097 UDP clientUdp = (UDP) clientIpv6.getPayload();
1098 DHCP6 clientDhcp6 = (DHCP6) clientUdp.getPayload();
1099
Taras Lemkin96a0d342018-03-26 14:52:58 +00001100 boolean directConnFlag = Dhcp6HandlerUtil.directlyConnected(clientDhcp6);
Harshada Chaundkarf0780fe2019-05-06 20:16:13 +00001101 boolean serverFound = false;
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001102 List<InternalPacket> internalPackets = new ArrayList<>();
1103 List<DhcpServerInfo> serverInfoList = findValidServerInfo(directConnFlag);
1104 List<DhcpServerInfo> copyServerInfoList = new ArrayList<DhcpServerInfo>(serverInfoList);
1105
1106 for (DhcpServerInfo serverInfo : copyServerInfoList) {
Taras Lemkin96a0d342018-03-26 14:52:58 +00001107 if (!Dhcp6HandlerUtil.checkDhcpServerConnPt(directConnFlag, serverInfo)) {
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001108 log.warn("Can't get server connect point, ignore");
1109 continue;
1110 }
1111 DhcpServerInfo newServerInfo = getHostInfoForServerInfo(serverInfo, serverInfoList);
1112 if (newServerInfo == null) {
Harshada Chaundkarf0780fe2019-05-06 20:16:13 +00001113 log.debug("Can't get server interface with host info resolved, ignore serverInfo {} serverInfoList {}",
1114 serverInfo, serverInfoList);
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001115 continue;
1116 }
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001117 Interface serverInterface = getServerInterface(newServerInfo);
1118 if (serverInterface == null) {
Harshada Chaundkarf0780fe2019-05-06 20:16:13 +00001119 log.debug("Can't get server interface, ignore for serverInfo {}, serverInfoList {}",
1120 serverInfo, serverInfoList);
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001121 continue;
1122 }
Harshada Chaundkarf0780fe2019-05-06 20:16:13 +00001123
1124 serverFound = true;
1125 log.debug("Server Info Found {}", serverInfo.getDhcpConnectMac());
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001126 Ethernet etherRouted = (Ethernet) clientPacket.clone();
1127 MacAddress macFacingServer = serverInterface.mac();
1128 if (macFacingServer == null) {
1129 log.warn("No MAC address for server Interface {}", serverInterface);
1130 return null;
1131 }
1132 etherRouted.setSourceMACAddress(macFacingServer);
1133 etherRouted.setDestinationMACAddress(newServerInfo.getDhcpConnectMac().get());
1134 InternalPacket internalPacket =
Taras Lemkin96a0d342018-03-26 14:52:58 +00001135 InternalPacket.internalPacket(etherRouted,
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001136 serverInfo.getDhcpServerConnectPoint().get());
1137 internalPackets.add(internalPacket);
1138 log.debug("Sending LQ to DHCP server {}", newServerInfo.getDhcpServerIp6());
1139 }
Harshada Chaundkarf0780fe2019-05-06 20:16:13 +00001140 if (!serverFound) {
1141 log.warn("ProcessDhcp6PacketFromClient No Server Found");
1142 }
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001143 log.debug("num of client packets to send is{}", internalPackets.size());
1144
1145 return internalPackets;
1146 }
1147
Kalhee Kim45fede42017-09-05 19:05:06 +00001148 /**
Kalhee Kim495c9b22017-11-07 16:32:09 +00001149 * build the DHCP6 solicit/request packet with gatewayip.
Kalhee Kim45fede42017-09-05 19:05:06 +00001150 *
1151 * @param context packet context
1152 * @param clientPacket client ethernet packet
1153 * @param clientInterfaces set of client side interfaces
1154 */
Kalhee Kim495c9b22017-11-07 16:32:09 +00001155 private List<InternalPacket> processDhcp6PacketFromClient(PacketContext context,
1156 Ethernet clientPacket,
1157 Set<Interface> clientInterfaces) {
Yi Tseng3bd57ac2017-11-29 14:39:18 -08001158 ConnectPoint receivedFrom = context.inPacket().receivedFrom();
Taras Lemkin96a0d342018-03-26 14:52:58 +00001159 Ip6Address relayAgentIp = Dhcp6HandlerUtil.getRelayAgentIPv6Address(clientInterfaces);
Yi Tseng3bd57ac2017-11-29 14:39:18 -08001160 MacAddress relayAgentMac = clientInterfaces.iterator().next().mac();
1161 if (relayAgentIp == null || relayAgentMac == null) {
1162 log.warn("Missing DHCP relay agent interface Ipv6 addr config for "
Kalhee Kim495c9b22017-11-07 16:32:09 +00001163 + "packet from client on port: {}. Aborting packet processing",
1164 clientInterfaces.iterator().next().connectPoint());
Charles Chanb87495c2019-02-01 11:31:50 -08001165 //dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_CLIENT_INTF_MAC);
Charles Chan239492c2018-01-22 13:27:28 -08001166 return Lists.newArrayList();
Kalhee Kim121ba922017-11-01 17:56:44 +00001167 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001168
Kalhee Kim495c9b22017-11-07 16:32:09 +00001169 IPv6 clientIpv6 = (IPv6) clientPacket.getPayload();
1170 UDP clientUdp = (UDP) clientIpv6.getPayload();
1171 DHCP6 clientDhcp6 = (DHCP6) clientUdp.getPayload();
1172
Taras Lemkin96a0d342018-03-26 14:52:58 +00001173 boolean directConnFlag = Dhcp6HandlerUtil.directlyConnected(clientDhcp6);
Charles Chana990ce92017-10-30 10:22:50 -07001174
Kalhee Kim121ba922017-11-01 17:56:44 +00001175 ConnectPoint clientConnectionPoint = context.inPacket().receivedFrom();
1176 VlanId vlanIdInUse = VlanId.vlanId(clientPacket.getVlanID());
1177 Interface clientInterface = interfaceService.getInterfacesByPort(clientConnectionPoint)
Taras Lemkin96a0d342018-03-26 14:52:58 +00001178 .stream().filter(iface -> Dhcp6HandlerUtil.interfaceContainsVlan(iface, vlanIdInUse))
Kalhee Kim495c9b22017-11-07 16:32:09 +00001179 .findFirst()
1180 .orElse(null);
Kalhee Kim45fede42017-09-05 19:05:06 +00001181
Kalhee Kim495c9b22017-11-07 16:32:09 +00001182 List<InternalPacket> internalPackets = new ArrayList<>();
1183 List<DhcpServerInfo> serverInfoList = findValidServerInfo(directConnFlag);
1184 List<DhcpServerInfo> copyServerInfoList = new ArrayList<DhcpServerInfo>(serverInfoList);
Kalhee Kim45b24182017-10-18 18:30:23 +00001185
Kalhee Kim495c9b22017-11-07 16:32:09 +00001186 for (DhcpServerInfo serverInfo : copyServerInfoList) {
Taras Lemkin96a0d342018-03-26 14:52:58 +00001187 if (!Dhcp6HandlerUtil.checkDhcpServerConnPt(directConnFlag, serverInfo)) {
Kalhee Kim495c9b22017-11-07 16:32:09 +00001188 log.warn("Can't get server connect point, ignore");
1189 continue;
1190 }
1191 DhcpServerInfo newServerInfo = getHostInfoForServerInfo(serverInfo, serverInfoList);
1192 if (newServerInfo == null) {
1193 log.warn("Can't get server interface with host info resolved, ignore");
1194 continue;
1195 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001196
Kalhee Kim495c9b22017-11-07 16:32:09 +00001197 Interface serverInterface = getServerInterface(newServerInfo);
1198 if (serverInterface == null) {
1199 log.warn("Can't get server interface, ignore");
1200 continue;
1201 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +00001202
Taras Lemkin96a0d342018-03-26 14:52:58 +00001203 Ethernet etherReply = Dhcp6HandlerUtil.buildDhcp6PacketFromClient(context, clientPacket,
Kalhee Kim495c9b22017-11-07 16:32:09 +00001204 clientInterfaces, newServerInfo, serverInterface);
1205 removeHostOrRoute(directConnFlag, clientConnectionPoint, clientDhcp6, clientPacket,
1206 clientIpv6, clientInterface);
Kalhee Kimea4b6c22017-11-09 14:38:37 +00001207
Taras Lemkin96a0d342018-03-26 14:52:58 +00001208 InternalPacket internalPacket = InternalPacket.internalPacket(etherReply,
Kalhee Kim495c9b22017-11-07 16:32:09 +00001209 serverInfo.getDhcpServerConnectPoint().get());
1210 internalPackets.add(internalPacket);
1211 }
1212 log.debug("num of client packets to send is{}", internalPackets.size());
Kalhee Kimea4b6c22017-11-09 14:38:37 +00001213
Kalhee Kim495c9b22017-11-07 16:32:09 +00001214 return internalPackets;
1215 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001216
1217 /**
Kalhee Kim45fede42017-09-05 19:05:06 +00001218 * process the DHCP6 relay-reply packet from dhcp server.
1219 *
1220 * @param context packet context
1221 * @param receivedPacket server ethernet packet
1222 * @param recevingInterfaces set of server side interfaces
Kalhee Kim495c9b22017-11-07 16:32:09 +00001223 * @return internalPacket toward client
Kalhee Kim45fede42017-09-05 19:05:06 +00001224 */
1225 private InternalPacket processDhcp6PacketFromServer(PacketContext context,
1226 Ethernet receivedPacket, Set<Interface> recevingInterfaces) {
Kalhee Kim45fede42017-09-05 19:05:06 +00001227 // get dhcp6 header.
Ray Milkeyf0c47612017-09-28 11:29:38 -07001228 Ethernet etherReply = receivedPacket.duplicate();
Kalhee Kim45fede42017-09-05 19:05:06 +00001229 IPv6 ipv6Packet = (IPv6) etherReply.getPayload();
1230 UDP udpPacket = (UDP) ipv6Packet.getPayload();
1231 DHCP6 dhcp6Relay = (DHCP6) udpPacket.getPayload();
Taras Lemkin96a0d342018-03-26 14:52:58 +00001232 Boolean directConnFlag = Dhcp6HandlerUtil.directlyConnected(dhcp6Relay);
Kalhee Kim45fede42017-09-05 19:05:06 +00001233
Kalhee Kim495c9b22017-11-07 16:32:09 +00001234 DHCP6 embeddedDhcp6 = dhcp6Relay.getOptions().stream()
1235 .filter(opt -> opt instanceof Dhcp6RelayOption)
1236 .map(BasePacket::getPayload)
1237 .map(pld -> (DHCP6) pld)
1238 .findFirst()
1239 .orElse(null);
Kalhee Kimd21029f2017-09-26 20:21:53 +00001240 ConnectPoint inPort = context.inPacket().receivedFrom();
Kalhee Kim495c9b22017-11-07 16:32:09 +00001241 DhcpServerInfo foundServerInfo = findServerInfoFromServer(directConnFlag, inPort);
Kalhee Kimd21029f2017-09-26 20:21:53 +00001242
Kalhee Kim495c9b22017-11-07 16:32:09 +00001243 if (foundServerInfo == null) {
jayakumarthazhathed5f05d2018-09-25 15:56:51 -04001244 log.warn("Cannot find server info for {} server, inPort {}",
1245 directConnFlag ? "direct" : "indirect", inPort);
Charles Chanb87495c2019-02-01 11:31:50 -08001246 //dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_SERVER_INFO);
Kalhee Kimd21029f2017-09-26 20:21:53 +00001247 return null;
Kalhee Kim495c9b22017-11-07 16:32:09 +00001248 } else {
Taras Lemkin96a0d342018-03-26 14:52:58 +00001249 if (Dhcp6HandlerUtil.isServerIpEmpty(foundServerInfo)) {
Kalhee Kim495c9b22017-11-07 16:32:09 +00001250 log.warn("Cannot find server info's ipaddress");
Charles Chanb87495c2019-02-01 11:31:50 -08001251 //dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_SERVER_IP6ADDR);
Kalhee Kim495c9b22017-11-07 16:32:09 +00001252 return null;
1253 }
Kalhee Kimd21029f2017-09-26 20:21:53 +00001254 }
1255
Kalhee Kim45fede42017-09-05 19:05:06 +00001256 Dhcp6InterfaceIdOption interfaceIdOption = dhcp6Relay.getOptions().stream()
1257 .filter(opt -> opt instanceof Dhcp6InterfaceIdOption)
1258 .map(opt -> (Dhcp6InterfaceIdOption) opt)
1259 .findFirst()
1260 .orElse(null);
Kalhee Kim45fede42017-09-05 19:05:06 +00001261 if (interfaceIdOption == null) {
1262 log.warn("Interface Id option is not present, abort packet...");
Charles Chanb87495c2019-02-01 11:31:50 -08001263 //dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.OPTION_MISSING_FAIL);
Kalhee Kim45fede42017-09-05 19:05:06 +00001264 return null;
1265 }
1266
1267 MacAddress peerMac = interfaceIdOption.getMacAddress();
1268 String clientConnectionPointStr = new String(interfaceIdOption.getInPort());
Kalhee Kim45fede42017-09-05 19:05:06 +00001269 ConnectPoint clientConnectionPoint = ConnectPoint.deviceConnectPoint(clientConnectionPointStr);
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +00001270 VlanId vlanIdInUse = VlanId.vlanId(interfaceIdOption.getVlanId());
Harshada Chaundkarf0780fe2019-05-06 20:16:13 +00001271
1272 log.debug("processDhcp6PacketFromServer Interface Id Mac {}, port{}, vlan {}",
1273 peerMac, clientConnectionPointStr, vlanIdInUse);
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +00001274 Interface clientInterface = interfaceService.getInterfacesByPort(clientConnectionPoint)
Taras Lemkin96a0d342018-03-26 14:52:58 +00001275 .stream().filter(iface -> Dhcp6HandlerUtil.interfaceContainsVlan(iface, vlanIdInUse))
Kalhee Kim495c9b22017-11-07 16:32:09 +00001276 .findFirst().orElse(null);
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +00001277 if (clientInterface == null) {
1278 log.warn("Cannot get client interface for from packet, abort... vlan {}", vlanIdInUse.toString());
Charles Chanb87495c2019-02-01 11:31:50 -08001279 //dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_MATCHING_INTF);
Kalhee Kim45fede42017-09-05 19:05:06 +00001280 return null;
1281 }
Kalhee Kim44fbda12018-04-03 21:08:18 +00001282 etherReply.setVlanID(vlanIdInUse.toShort());
1283
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +00001284 MacAddress relayAgentMac = clientInterface.mac();
Kalhee Kim45fede42017-09-05 19:05:06 +00001285 if (relayAgentMac == null) {
Yi Tseng68ef26b2017-12-18 17:10:00 -08001286 log.warn("Can not get client interface mac, abort packet..");
Charles Chanb87495c2019-02-01 11:31:50 -08001287 //dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_CLIENT_INTF_MAC);
Kalhee Kim45fede42017-09-05 19:05:06 +00001288 return null;
1289 }
1290 etherReply.setSourceMACAddress(relayAgentMac);
1291
1292 // find destMac
Yi Tseng68ef26b2017-12-18 17:10:00 -08001293 MacAddress clientMac;
Yi Tsengaa417a62017-09-08 17:22:51 -07001294 Ip6Address peerAddress = Ip6Address.valueOf(dhcp6Relay.getPeerAddress());
1295 Set<Host> clients = hostService.getHostsByIp(peerAddress);
Kalhee Kim45fede42017-09-05 19:05:06 +00001296 if (clients.isEmpty()) {
Yi Tseng68ef26b2017-12-18 17:10:00 -08001297 log.trace("There's no host found for this address {}",
Kalhee Kim45fede42017-09-05 19:05:06 +00001298 HexString.toHexString(dhcp6Relay.getPeerAddress(), ":"));
Yi Tseng68ef26b2017-12-18 17:10:00 -08001299 log.trace("Let's look up interfaceId {}", HexString.toHexString(peerMac.toBytes(), ":"));
Kalhee Kim45fede42017-09-05 19:05:06 +00001300 clientMac = peerMac;
1301 } else {
1302 clientMac = clients.iterator().next().mac();
1303 if (clientMac == null) {
1304 log.warn("No client mac address found, abort packet...");
Charles Chanb87495c2019-02-01 11:31:50 -08001305 //dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_CLIENT_INTF_MAC);
Kalhee Kim45fede42017-09-05 19:05:06 +00001306 return null;
1307 }
Yi Tseng68ef26b2017-12-18 17:10:00 -08001308 log.trace("Client mac address found from getHostByIp");
Kalhee Kim45fede42017-09-05 19:05:06 +00001309 }
1310 etherReply.setDestinationMACAddress(clientMac);
Kalhee Kim45fede42017-09-05 19:05:06 +00001311 // ip header
1312 ipv6Packet.setSourceAddress(dhcp6Relay.getLinkAddress());
1313 ipv6Packet.setDestinationAddress(dhcp6Relay.getPeerAddress());
1314 // udp header
1315 udpPacket.setSourcePort(UDP.DHCP_V6_SERVER_PORT);
1316 if (directConnFlag) {
1317 udpPacket.setDestinationPort(UDP.DHCP_V6_CLIENT_PORT);
1318 } else {
1319 udpPacket.setDestinationPort(UDP.DHCP_V6_SERVER_PORT);
1320 }
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001321
Kalhee Kim45fede42017-09-05 19:05:06 +00001322
Taras Lemkin96a0d342018-03-26 14:52:58 +00001323 boolean hostOrRouteAllowed = learnRouteFromLeasequery ||
1324 Dhcp6HandlerUtil.getDhcp6LeafMessageType(dhcp6Relay) != MsgType.LEASEQUERY_REPLY;
1325 log.debug("Can add host or route: {}", hostOrRouteAllowed);
1326
1327 if (hostOrRouteAllowed) {
1328 // add host or route
1329 addHostOrRoute(directConnFlag, clientConnectionPoint, dhcp6Relay, embeddedDhcp6,
pierd9909e82019-08-23 10:45:40 -07001330 clientMac, clientInterface, vlanIdInUse);
Taras Lemkin96a0d342018-03-26 14:52:58 +00001331 }
1332
Kalhee Kim45fede42017-09-05 19:05:06 +00001333 udpPacket.setPayload(embeddedDhcp6);
1334 udpPacket.resetChecksum();
1335 ipv6Packet.setPayload(udpPacket);
1336 etherReply.setPayload(ipv6Packet);
Taras Lemkin96a0d342018-03-26 14:52:58 +00001337 return InternalPacket.internalPacket(etherReply, clientConnectionPoint);
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001338 }
Yi Tseng483ac6f2017-08-02 15:03:31 -07001339
1340 @Override
Kalhee Kimba366062017-11-07 16:32:09 +00001341 public void setDhcpFpmEnabled(Boolean enabled) {
1342 dhcpFpmEnabled = enabled;
1343 }
1344
1345 @Override
Yi Tseng483ac6f2017-08-02 15:03:31 -07001346 public void setDefaultDhcpServerConfigs(Collection<DhcpServerConfig> configs) {
Kalhee Kim495c9b22017-11-07 16:32:09 +00001347 log.debug("setDefaultDhcpServerConfigs is called.");
Yi Tseng919b2df2017-09-07 16:22:51 -07001348 setDhcpServerConfigs(configs, defaultServerInfoList);
Yi Tseng919b2df2017-09-07 16:22:51 -07001349 }
1350
1351 @Override
1352 public void setIndirectDhcpServerConfigs(Collection<DhcpServerConfig> configs) {
Kalhee Kim495c9b22017-11-07 16:32:09 +00001353 log.debug("setIndirectDhcpServerConfigs is called.");
Yi Tseng919b2df2017-09-07 16:22:51 -07001354 setDhcpServerConfigs(configs, indirectServerInfoList);
Yi Tseng919b2df2017-09-07 16:22:51 -07001355 }
1356
1357 public void setDhcpServerConfigs(Collection<DhcpServerConfig> configs, List<DhcpServerInfo> serverInfoList) {
Kalhee Kim495c9b22017-11-07 16:32:09 +00001358 log.debug("config size {}.", configs.size());
1359
Kalhee Kim45fede42017-09-05 19:05:06 +00001360 if (configs.size() == 0) {
1361 // no config to update
1362 return;
1363 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001364 // TODO: currently we pick up first DHCP server config.
1365 // Will use other server configs in the future for HA.
Kalhee Kim495c9b22017-11-07 16:32:09 +00001366 Boolean isConfigValid = false;
1367 for (DhcpServerConfig serverConfig : configs) {
1368 if (serverConfig.getDhcpServerIp6().isPresent()) {
1369 isConfigValid = true;
1370 break;
1371 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001372 }
Kalhee Kim495c9b22017-11-07 16:32:09 +00001373 if (!isConfigValid) {
1374 log.warn("No IP V6 server address found.");
1375 return; // No IP V6 address found
1376 }
1377 for (DhcpServerInfo oldServerInfo : serverInfoList) {
Yi Tseng919b2df2017-09-07 16:22:51 -07001378 // stop monitoring gateway or server
1379 oldServerInfo.getDhcpGatewayIp6().ifPresent(gatewayIp -> {
1380 hostService.stopMonitoringIp(gatewayIp);
1381 });
1382 oldServerInfo.getDhcpServerIp6().ifPresent(serverIp -> {
1383 hostService.stopMonitoringIp(serverIp);
Yi Tseng525ff402017-10-23 19:39:39 -07001384 cancelDhcpPacket(serverIp);
Yi Tseng919b2df2017-09-07 16:22:51 -07001385 });
1386 }
Kalhee Kim495c9b22017-11-07 16:32:09 +00001387 serverInfoList.clear();
1388 for (DhcpServerConfig serverConfig : configs) {
1389 // Create new server info according to the config
1390 DhcpServerInfo newServerInfo = new DhcpServerInfo(serverConfig,
1391 DhcpServerInfo.Version.DHCP_V6);
1392 checkState(newServerInfo.getDhcpServerConnectPoint().isPresent(),
1393 "Connect point not exists");
1394 checkState(newServerInfo.getDhcpServerIp6().isPresent(),
1395 "IP of DHCP server not exists");
Yi Tseng919b2df2017-09-07 16:22:51 -07001396
Kalhee Kim495c9b22017-11-07 16:32:09 +00001397 log.debug("DHCP server connect point: {}", newServerInfo.getDhcpServerConnectPoint().orElse(null));
1398 log.debug("DHCP server IP: {}", newServerInfo.getDhcpServerIp6().orElse(null));
Yi Tseng919b2df2017-09-07 16:22:51 -07001399
Kalhee Kim495c9b22017-11-07 16:32:09 +00001400 Ip6Address serverIp = newServerInfo.getDhcpServerIp6().get();
1401 Ip6Address ipToProbe;
1402 if (newServerInfo.getDhcpGatewayIp6().isPresent()) {
1403 ipToProbe = newServerInfo.getDhcpGatewayIp6().get();
1404 } else {
1405 ipToProbe = newServerInfo.getDhcpServerIp6().orElse(null);
1406 }
1407 String hostToProbe = newServerInfo.getDhcpGatewayIp6()
1408 .map(ip -> "gateway").orElse("server");
Yi Tseng919b2df2017-09-07 16:22:51 -07001409
Kalhee Kim495c9b22017-11-07 16:32:09 +00001410 log.warn("Probing to resolve {} IP {}", hostToProbe, ipToProbe);
1411 hostService.startMonitoringIp(ipToProbe);
1412
1413 Set<Host> hosts = hostService.getHostsByIp(ipToProbe);
1414 if (!hosts.isEmpty()) {
1415 Host host = hosts.iterator().next();
1416 newServerInfo.setDhcpConnectVlan(host.vlan());
1417 newServerInfo.setDhcpConnectMac(host.mac());
1418 log.warn("Host found host {}", host);
1419
1420 } else {
1421 log.warn("No host found host ip {}", ipToProbe);
1422 }
1423 // Add new server info
1424 synchronized (this) {
1425 serverInfoList.add(newServerInfo);
1426 }
1427 if (!hosts.isEmpty()) {
1428 requestDhcpPacket(serverIp);
1429 }
Yi Tseng919b2df2017-09-07 16:22:51 -07001430 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001431 }
1432
1433 class InternalHostListener implements HostListener {
1434 @Override
1435 public void event(HostEvent event) {
1436 switch (event.type()) {
1437 case HOST_ADDED:
1438 case HOST_UPDATED:
Charles Chan24a96ff2018-08-13 10:32:03 -07001439 case HOST_MOVED:
Jordan Halterman6328db72018-04-10 13:34:50 -04001440 log.trace("Scheduled host event {}", event);
1441 hostEventExecutor.execute(() -> hostUpdated(event.subject()));
Kalhee Kim45fede42017-09-05 19:05:06 +00001442 break;
1443 case HOST_REMOVED:
Jordan Halterman6328db72018-04-10 13:34:50 -04001444 log.trace("Scheduled host event {}", event);
1445 hostEventExecutor.execute(() -> hostRemoved(event.subject()));
Kalhee Kim45fede42017-09-05 19:05:06 +00001446 break;
Kalhee Kim45fede42017-09-05 19:05:06 +00001447 default:
1448 break;
1449 }
1450 }
1451 }
1452
1453 /**
Kalhee Kim45fede42017-09-05 19:05:06 +00001454 * Handle host updated.
1455 * If the host is DHCP server or gateway, update connect mac and vlan.
1456 *
1457 * @param host the host
1458 */
1459 private void hostUpdated(Host host) {
Yi Tseng525ff402017-10-23 19:39:39 -07001460 hostUpdated(host, defaultServerInfoList);
1461 hostUpdated(host, indirectServerInfoList);
Kalhee Kim45fede42017-09-05 19:05:06 +00001462 }
1463
Yi Tseng525ff402017-10-23 19:39:39 -07001464 private void hostUpdated(Host host, List<DhcpServerInfo> serverInfoList) {
jjosep004c05f55ab2018-08-21 09:01:10 -04001465 serverInfoList.stream().forEach(serverInfo -> {
Yi Tseng525ff402017-10-23 19:39:39 -07001466 Ip6Address serverIp = serverInfo.getDhcpServerIp6().orElse(null);
jjosep004c05f55ab2018-08-21 09:01:10 -04001467 Ip6Address targetIp = serverInfo.getDhcpGatewayIp6().orElse(null);
Yi Tseng525ff402017-10-23 19:39:39 -07001468
1469 if (targetIp == null) {
1470 targetIp = serverIp;
1471 }
Yi Tseng525ff402017-10-23 19:39:39 -07001472 if (targetIp != null) {
1473 if (host.ipAddresses().contains(targetIp)) {
1474 serverInfo.setDhcpConnectMac(host.mac());
1475 serverInfo.setDhcpConnectVlan(host.vlan());
1476 requestDhcpPacket(serverIp);
1477 }
1478 }
jjosep004c05f55ab2018-08-21 09:01:10 -04001479 });
Yi Tseng525ff402017-10-23 19:39:39 -07001480 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001481 /**
1482 * Handle host removed.
1483 * If the host is DHCP server or gateway, unset connect mac and vlan.
1484 *
1485 * @param host the host
1486 */
1487 private void hostRemoved(Host host) {
Yi Tseng525ff402017-10-23 19:39:39 -07001488 hostRemoved(host, defaultServerInfoList);
1489 hostRemoved(host, indirectServerInfoList);
Yi Tseng919b2df2017-09-07 16:22:51 -07001490 }
1491
Yi Tseng525ff402017-10-23 19:39:39 -07001492 private void hostRemoved(Host host, List<DhcpServerInfo> serverInfoList) {
jjosep004c05f55ab2018-08-21 09:01:10 -04001493 serverInfoList.stream().forEach(serverInfo -> {
Yi Tseng525ff402017-10-23 19:39:39 -07001494 Ip6Address serverIp = serverInfo.getDhcpServerIp6().orElse(null);
jjosep004c05f55ab2018-08-21 09:01:10 -04001495 Ip6Address targetIp = serverInfo.getDhcpGatewayIp6().orElse(null);
Yi Tseng525ff402017-10-23 19:39:39 -07001496
1497 if (targetIp == null) {
1498 targetIp = serverIp;
1499 }
Yi Tseng525ff402017-10-23 19:39:39 -07001500 if (targetIp != null) {
1501 if (host.ipAddresses().contains(targetIp)) {
1502 serverInfo.setDhcpConnectVlan(null);
1503 serverInfo.setDhcpConnectMac(null);
1504 cancelDhcpPacket(serverIp);
1505 }
1506 }
jjosep004c05f55ab2018-08-21 09:01:10 -04001507 });
Yi Tseng525ff402017-10-23 19:39:39 -07001508 }
1509
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001510 /**
1511 * Gets Interface facing to the server for default host.
1512 *
1513 * @return the Interface facing to the server; null if not found
1514 */
1515 private Interface getServerInterface() {
1516 DhcpServerInfo serverInfo;
1517 ConnectPoint dhcpServerConnectPoint;
1518 VlanId dhcpConnectVlan;
1519
1520 if (!defaultServerInfoList.isEmpty()) {
1521 serverInfo = defaultServerInfoList.get(0);
1522 dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1523 dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1524 } else {
1525 return null;
1526 }
1527 if (dhcpServerConnectPoint == null || dhcpConnectVlan == null) {
1528 log.info("Default DHCP server {} not resolve yet", serverInfo.getDhcpGatewayIp6());
1529 return null;
1530 }
1531 return interfaceService.getInterfacesByPort(dhcpServerConnectPoint)
1532 .stream()
Taras Lemkin96a0d342018-03-26 14:52:58 +00001533 .filter(iface -> Dhcp6HandlerUtil.interfaceContainsVlan(iface, dhcpConnectVlan))
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001534 .findFirst()
1535 .orElse(null);
1536 }
1537
1538 /**
1539 * Gets Interface facing to the server for indirect hosts.
1540 * Use default server Interface if indirect server not configured.
1541 *
1542 * @return the Interface facing to the server; null if not found
1543 */
1544 private Interface getIndirectServerInterface() {
1545 DhcpServerInfo serverInfo;
1546
1547 ConnectPoint indirectDhcpServerConnectPoint;
1548 VlanId indirectDhcpConnectVlan;
1549
1550 if (!indirectServerInfoList.isEmpty()) {
1551 serverInfo = indirectServerInfoList.get(0);
1552 indirectDhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1553 indirectDhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1554 } else {
1555 return getServerInterface();
1556 }
1557 if (indirectDhcpServerConnectPoint == null || indirectDhcpConnectVlan == null) {
1558 log.info("Indirect DHCP server {} not resolve yet", serverInfo.getDhcpGatewayIp6());
1559 return null;
1560 }
1561 return interfaceService.getInterfacesByPort(indirectDhcpServerConnectPoint)
1562 .stream()
Taras Lemkin96a0d342018-03-26 14:52:58 +00001563 .filter(iface -> Dhcp6HandlerUtil.interfaceContainsVlan(iface, indirectDhcpConnectVlan))
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001564 .findFirst()
1565 .orElse(null);
1566 }
1567
Kalhee Kim45b24182017-10-18 18:30:23 +00001568 /**
Kalhee Kim495c9b22017-11-07 16:32:09 +00001569 * Checks if serverInfo's host info (mac and vlan) is filled in; if not, fills in.
1570 *
1571 * @param serverInfo server information
1572 * @return newServerInfo if host info can be either found or filled in.
1573 */
1574 private DhcpServerInfo getHostInfoForServerInfo(DhcpServerInfo serverInfo, List<DhcpServerInfo> sererInfoList) {
1575 DhcpServerInfo newServerInfo = null;
1576 MacAddress dhcpServerConnectMac = serverInfo.getDhcpConnectMac().orElse(null);
1577 VlanId dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1578 ConnectPoint dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1579
1580 if (dhcpServerConnectMac != null && dhcpConnectVlan != null) {
1581 newServerInfo = serverInfo;
Charles Chan750c9992018-02-26 10:44:49 -08001582 log.debug("DHCP server {} host info found. ConnectPt{} Mac {} vlan {}", serverInfo.getDhcpServerIp6(),
Kalhee Kim495c9b22017-11-07 16:32:09 +00001583 dhcpServerConnectPoint, dhcpServerConnectMac, dhcpConnectVlan);
1584 } else {
Harshada Chaundkarf0780fe2019-05-06 20:16:13 +00001585 log.debug("DHCP server {} not resolve yet connectPt {} mac {} vlan {}", serverInfo.getDhcpServerIp6(),
Kalhee Kim495c9b22017-11-07 16:32:09 +00001586 dhcpServerConnectPoint, dhcpServerConnectMac, dhcpConnectVlan);
1587
1588 Ip6Address ipToProbe;
1589 if (serverInfo.getDhcpGatewayIp6().isPresent()) {
1590 ipToProbe = serverInfo.getDhcpGatewayIp6().get();
1591 } else {
1592 ipToProbe = serverInfo.getDhcpServerIp6().orElse(null);
1593 }
1594 String hostToProbe = serverInfo.getDhcpGatewayIp6()
1595 .map(ip -> "gateway").orElse("server");
1596
Harshada Chaundkarf0780fe2019-05-06 20:16:13 +00001597 log.debug("Dynamically probing to resolve {} IP {}", hostToProbe, ipToProbe);
Kalhee Kim495c9b22017-11-07 16:32:09 +00001598 hostService.startMonitoringIp(ipToProbe);
1599
1600 Set<Host> hosts = hostService.getHostsByIp(ipToProbe);
1601 if (!hosts.isEmpty()) {
1602 int serverInfoIndex = sererInfoList.indexOf(serverInfo);
1603 Host host = hosts.iterator().next();
1604 serverInfo.setDhcpConnectVlan(host.vlan());
1605 serverInfo.setDhcpConnectMac(host.mac());
1606 // replace the serverInfo in the list
1607 sererInfoList.set(serverInfoIndex, serverInfo);
1608 newServerInfo = serverInfo;
1609 log.warn("Dynamically host found host {}", host);
1610 } else {
Harshada Chaundkarf0780fe2019-05-06 20:16:13 +00001611 log.debug("No host found host ip {} dynamically", ipToProbe);
Kalhee Kim495c9b22017-11-07 16:32:09 +00001612 }
1613 }
1614 return newServerInfo;
1615 }
1616
1617 /**
Kalhee Kim45b24182017-10-18 18:30:23 +00001618 * Gets Interface facing to the server for default host.
1619 *
Kalhee Kim495c9b22017-11-07 16:32:09 +00001620 * @param serverInfo server information
Kalhee Kim45b24182017-10-18 18:30:23 +00001621 * @return the Interface facing to the server; null if not found
1622 */
Kalhee Kim495c9b22017-11-07 16:32:09 +00001623 private Interface getServerInterface(DhcpServerInfo serverInfo) {
1624 Interface serverInterface = null;
Yi Tseng25bfe372017-11-03 16:27:32 -07001625
Kalhee Kim495c9b22017-11-07 16:32:09 +00001626 ConnectPoint dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1627 VlanId dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1628
1629 if (dhcpServerConnectPoint != null && dhcpConnectVlan != null) {
1630 serverInterface = interfaceService.getInterfacesByPort(dhcpServerConnectPoint)
1631 .stream()
Taras Lemkin96a0d342018-03-26 14:52:58 +00001632 .filter(iface -> Dhcp6HandlerUtil.interfaceContainsVlan(iface, dhcpConnectVlan))
Kalhee Kim495c9b22017-11-07 16:32:09 +00001633 .findFirst()
1634 .orElse(null);
Yi Tseng25bfe372017-11-03 16:27:32 -07001635 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +00001636 log.warn("DHCP server {} not resolve yet connectPoint {} vlan {}", serverInfo.getDhcpServerIp6(),
1637 dhcpServerConnectPoint, dhcpConnectVlan);
Kalhee Kim45b24182017-10-18 18:30:23 +00001638 }
Kalhee Kim495c9b22017-11-07 16:32:09 +00001639
1640 return serverInterface;
Kalhee Kim45b24182017-10-18 18:30:23 +00001641 }
1642
Yi Tseng525ff402017-10-23 19:39:39 -07001643 private void requestDhcpPacket(Ip6Address serverIp) {
1644 requestServerDhcpPacket(serverIp);
1645 requestClientDhcpPacket(serverIp);
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001646 requestServerLQPacket(serverIp);
Yi Tseng525ff402017-10-23 19:39:39 -07001647 }
1648
1649 private void cancelDhcpPacket(Ip6Address serverIp) {
1650 cancelServerDhcpPacket(serverIp);
1651 cancelClientDhcpPacket(serverIp);
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001652 cancelServerLQPacket(serverIp);
Yi Tseng525ff402017-10-23 19:39:39 -07001653 }
1654
1655 private void cancelServerDhcpPacket(Ip6Address serverIp) {
1656 TrafficSelector serverSelector =
1657 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1658 .matchIPv6Src(serverIp.toIpPrefix())
1659 .build();
1660 packetService.cancelPackets(serverSelector,
1661 PacketPriority.CONTROL,
1662 appId);
1663 }
1664
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001665 private void cancelServerLQPacket(Ip6Address serverIp) {
1666 TrafficSelector serverSelector =
1667 DefaultTrafficSelector.builder(LEASE_QUERY_RESPONSE_SELECTOR)
1668 .matchIPv6Src(serverIp.toIpPrefix())
1669 .build();
1670 packetService.cancelPackets(serverSelector,
1671 PacketPriority.CONTROL,
1672 appId);
1673 }
1674
Yi Tseng525ff402017-10-23 19:39:39 -07001675 private void requestServerDhcpPacket(Ip6Address serverIp) {
1676 TrafficSelector serverSelector =
1677 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1678 .matchIPv6Src(serverIp.toIpPrefix())
1679 .build();
1680 packetService.requestPackets(serverSelector,
1681 PacketPriority.CONTROL,
1682 appId);
1683 }
1684
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001685 private void requestServerLQPacket(Ip6Address serverIp) {
1686 TrafficSelector serverSelector =
1687 DefaultTrafficSelector.builder(LEASE_QUERY_RESPONSE_SELECTOR)
1688 .matchIPv6Src(serverIp.toIpPrefix())
1689 .build();
1690 packetService.requestPackets(serverSelector,
1691 PacketPriority.CONTROL,
1692 appId);
1693 }
1694
Yi Tseng525ff402017-10-23 19:39:39 -07001695 private void cancelClientDhcpPacket(Ip6Address serverIp) {
1696 // Packet comes from relay
1697 TrafficSelector indirectClientSelector =
1698 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1699 .matchIPv6Dst(serverIp.toIpPrefix())
1700 .build();
1701 packetService.cancelPackets(indirectClientSelector,
1702 PacketPriority.CONTROL,
1703 appId);
Yi Tseng41dde702017-11-02 15:21:10 -07001704 indirectClientSelector =
1705 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1706 .matchIPv6Dst(Ip6Address.ALL_DHCP_RELAY_AGENTS_AND_SERVERS.toIpPrefix())
1707 .build();
1708 packetService.cancelPackets(indirectClientSelector,
1709 PacketPriority.CONTROL,
1710 appId);
1711 indirectClientSelector =
1712 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1713 .matchIPv6Dst(Ip6Address.ALL_DHCP_SERVERS.toIpPrefix())
1714 .build();
1715 packetService.cancelPackets(indirectClientSelector,
1716 PacketPriority.CONTROL,
1717 appId);
Yi Tseng525ff402017-10-23 19:39:39 -07001718
1719 // Packet comes from client
1720 packetService.cancelPackets(CLIENT_SERVER_SELECTOR,
1721 PacketPriority.CONTROL,
1722 appId);
1723 }
1724
1725 private void requestClientDhcpPacket(Ip6Address serverIp) {
1726 // Packet comes from relay
1727 TrafficSelector indirectClientSelector =
1728 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1729 .matchIPv6Dst(serverIp.toIpPrefix())
1730 .build();
1731 packetService.requestPackets(indirectClientSelector,
1732 PacketPriority.CONTROL,
1733 appId);
Yi Tseng41dde702017-11-02 15:21:10 -07001734 indirectClientSelector =
1735 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1736 .matchIPv6Dst(Ip6Address.ALL_DHCP_RELAY_AGENTS_AND_SERVERS.toIpPrefix())
1737 .build();
1738 packetService.requestPackets(indirectClientSelector,
1739 PacketPriority.CONTROL,
1740 appId);
1741 indirectClientSelector =
1742 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1743 .matchIPv6Dst(Ip6Address.ALL_DHCP_SERVERS.toIpPrefix())
1744 .build();
1745 packetService.requestPackets(indirectClientSelector,
1746 PacketPriority.CONTROL,
1747 appId);
Yi Tseng525ff402017-10-23 19:39:39 -07001748
1749 // Packet comes from client
1750 packetService.requestPackets(CLIENT_SERVER_SELECTOR,
1751 PacketPriority.CONTROL,
1752 appId);
1753 }
1754
1755 /**
1756 * Process the ignore rules.
1757 *
1758 * @param deviceId the device id
1759 * @param vlanId the vlan to be ignored
1760 * @param op the operation, ADD to install; REMOVE to uninstall rules
1761 */
1762 private void processIgnoreVlanRule(DeviceId deviceId, VlanId vlanId, Objective.Operation op) {
Yi Tseng525ff402017-10-23 19:39:39 -07001763 AtomicInteger installedCount = new AtomicInteger(DHCP_SELECTORS.size());
1764 DHCP_SELECTORS.forEach(trafficSelector -> {
1765 TrafficSelector selector = DefaultTrafficSelector.builder(trafficSelector)
1766 .matchVlanId(vlanId)
1767 .build();
1768
1769 ForwardingObjective.Builder builder = DefaultForwardingObjective.builder()
1770 .withFlag(ForwardingObjective.Flag.VERSATILE)
1771 .withSelector(selector)
1772 .withPriority(IGNORE_CONTROL_PRIORITY)
Yi Tsengdbabeed2017-11-29 10:49:20 -08001773 .withTreatment(DefaultTrafficTreatment.emptyTreatment())
Yi Tseng525ff402017-10-23 19:39:39 -07001774 .fromApp(appId);
1775
1776
1777 ObjectiveContext objectiveContext = new ObjectiveContext() {
1778 @Override
1779 public void onSuccess(Objective objective) {
1780 log.info("Ignore rule {} (Vlan id {}, device {}, selector {})",
1781 op, vlanId, deviceId, selector);
1782 int countDown = installedCount.decrementAndGet();
1783 if (countDown != 0) {
1784 return;
1785 }
1786 switch (op) {
1787 case ADD:
1788 ignoredVlans.put(deviceId, vlanId);
1789 break;
1790 case REMOVE:
1791 ignoredVlans.remove(deviceId, vlanId);
1792 break;
1793 default:
1794 log.warn("Unsupported objective operation {}", op);
1795 break;
1796 }
1797 }
1798
1799 @Override
1800 public void onError(Objective objective, ObjectiveError error) {
1801 log.warn("Can't {} ignore rule (vlan id {}, selector {}, device {}) due to {}",
1802 op, vlanId, selector, deviceId, error);
1803 }
1804 };
1805
1806 ForwardingObjective fwd;
1807 switch (op) {
1808 case ADD:
1809 fwd = builder.add(objectiveContext);
1810 break;
1811 case REMOVE:
1812 fwd = builder.remove(objectiveContext);
1813 break;
1814 default:
1815 log.warn("Unsupported objective operation {}", op);
1816 return;
1817 }
1818
1819 Device device = deviceService.getDevice(deviceId);
1820 if (device == null || !device.is(Pipeliner.class)) {
1821 log.warn("Device {} is not available now, wait until device is available", deviceId);
1822 return;
1823 }
1824 flowObjectiveService.apply(deviceId, fwd);
1825 });
1826 }
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001827
Kalhee Kim121ba922017-11-01 17:56:44 +00001828 /**
1829 * Find first ipaddress for a given Host info i.e. mac and vlan.
1830 *
1831 * @param clientMac client mac
1832 * @param vlanId packet's vlan
1833 * @return next-hop link-local ipaddress for a given host
1834 */
Kalhee Kim495c9b22017-11-07 16:32:09 +00001835 private IpAddress getFirstIpByHost(Boolean directConnFlag, MacAddress clientMac, VlanId vlanId) {
Kalhee Kim121ba922017-11-01 17:56:44 +00001836 IpAddress nextHopIp;
1837 // pick out the first link-local ip address
1838 HostId gwHostId = HostId.hostId(clientMac, vlanId);
1839 Host gwHost = hostService.getHost(gwHostId);
1840 if (gwHost == null) {
1841 log.warn("Can't find gateway host for hostId {}", gwHostId);
1842 return null;
1843 }
Kalhee Kim495c9b22017-11-07 16:32:09 +00001844 if (directConnFlag) {
1845 nextHopIp = gwHost.ipAddresses()
1846 .stream()
1847 .filter(IpAddress::isIp6)
1848 .map(IpAddress::getIp6Address)
1849 .findFirst()
1850 .orElse(null);
1851 } else {
1852 nextHopIp = gwHost.ipAddresses()
1853 .stream()
1854 .filter(IpAddress::isIp6)
1855 .filter(ip6 -> ip6.isLinkLocal())
1856 .map(IpAddress::getIp6Address)
1857 .findFirst()
1858 .orElse(null);
1859 }
Kalhee Kim121ba922017-11-01 17:56:44 +00001860 return nextHopIp;
1861 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +00001862
Kalhee Kim495c9b22017-11-07 16:32:09 +00001863 private List<DhcpServerInfo> findValidServerInfo(boolean directConnFlag) {
1864 List<DhcpServerInfo> validServerInfo;
1865
1866 if (directConnFlag || indirectServerInfoList.isEmpty()) {
1867 validServerInfo = new ArrayList<DhcpServerInfo>(defaultServerInfoList);
1868 } else {
1869 validServerInfo = new ArrayList<DhcpServerInfo>(indirectServerInfoList);
1870 }
1871 return validServerInfo;
1872 }
1873
1874 private DhcpServerInfo findServerInfoFromServer(boolean directConnFlag, ConnectPoint inPort) {
1875 List<DhcpServerInfo> validServerInfoList = findValidServerInfo(directConnFlag);
1876 DhcpServerInfo foundServerInfo = null;
1877 for (DhcpServerInfo serverInfo : validServerInfoList) {
1878 if (inPort.equals(serverInfo.getDhcpServerConnectPoint().get())) {
1879 foundServerInfo = serverInfo;
Charles Chan750c9992018-02-26 10:44:49 -08001880 log.debug("ServerInfo found for Rcv port {} Server Connect Point {} for {}",
Kalhee Kim495c9b22017-11-07 16:32:09 +00001881 inPort, serverInfo.getDhcpServerConnectPoint(), directConnFlag ? "direct" : "indirect");
1882 break;
Kalhee Kim495c9b22017-11-07 16:32:09 +00001883 }
1884 }
1885 return foundServerInfo;
1886 }
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001887
Kalhee Kim495c9b22017-11-07 16:32:09 +00001888 /**
1889 * Set the dhcp6 lease expiry poll interval value.
1890 *
1891 * @param val poll interval value in seconds
1892 */
Kalhee Kim2f07c602017-11-15 18:57:53 +00001893 @Override
Kalhee Kim495c9b22017-11-07 16:32:09 +00001894 public void setDhcp6PollInterval(int val) {
1895 dhcp6PollInterval = val;
1896 }
1897
Kalhee Kim2f07c602017-11-15 18:57:53 +00001898 /**
1899 * get the dhcp6 lease expiry poll interval value.
1900 * This is a private function
1901 * @return poll interval value in seconds
1902 */
1903 private int getDhcp6PollInterval() {
1904 return dhcp6PollInterval;
1905 }
1906
1907 /**
1908 * Find lease-expired ipaddresses and pd prefixes.
1909 * Removing host/route/fpm entries.
1910 */
1911 public void timeTick() {
1912 long currentTime = System.currentTimeMillis();
1913 Collection<DhcpRecord> records = dhcpRelayStore.getDhcpRecords();
1914
1915 log.debug("timeTick called currenttime {} records num {} ", currentTime, records.size());
1916
1917 records.forEach(record -> {
1918 boolean addrOrPdRemoved = false;
1919 DHCP6.MsgType ip6Status = record.ip6Status().orElse(null);
1920 if (ip6Status == null) {
1921 log.debug("record is not valid v6 record.");
1922 return;
1923 }
1924
1925 if ((currentTime - record.getLastIp6Update()) >
1926 ((record.addrPrefTime() + getDhcp6PollInterval() / 2) * 1000)) {
1927 // remove ipaddress from host/route table
1928 IpAddress ip = record.ip6Address().orElse(null);
1929 if (ip != null) {
1930 if (record.directlyConnected()) {
1931 providerService.removeIpFromHost(HostId.hostId(record.macAddress(),
1932 record.vlanId()), ip);
1933 } else {
1934 MacAddress gwMac = record.nextHop().orElse(null);
1935 if (gwMac == null) {
1936 log.warn("Can't find gateway mac address from record {} for ip6Addr", record);
1937 return;
1938 }
1939 IpAddress nextHopIp = getFirstIpByHost(record.directlyConnected(),
1940 gwMac,
1941 record.vlanId());
Kalhee Kim54a97c92018-04-02 21:23:46 +00001942 Route route = new Route(Route.Source.DHCP, ip.toIpPrefix(), nextHopIp);
Kalhee Kim2f07c602017-11-15 18:57:53 +00001943 routeStore.removeRoute(route);
1944 }
1945 record.updateAddrPrefTime(0);
1946 record.ip6Address(null);
1947 addrOrPdRemoved = true;
1948 dhcpRelayStore.updateDhcpRecord(HostId.hostId(record.macAddress(),
1949 record.vlanId()), record);
1950 log.warn("IP6 address is set to null. delta {} lastUpdate {} addrPrefTime {}",
1951 (currentTime - record.getLastIp6Update()), record.getLastIp6Update(),
1952 record.addrPrefTime());
1953 }
1954 }
1955 if ((currentTime - record.getLastPdUpdate()) >
1956 ((record.pdPrefTime() + getDhcp6PollInterval() / 2) * 1000)) {
1957 // remove PD from route/fpm table
1958 IpPrefix pdIpPrefix = record.pdPrefix().orElse(null);
1959 if (pdIpPrefix != null) {
1960 if (record.directlyConnected()) {
1961 providerService.removeIpFromHost(HostId.hostId(record.macAddress(), record.vlanId()),
1962 pdIpPrefix.address().getIp6Address());
1963 } else {
1964 MacAddress gwMac = record.nextHop().orElse(null);
1965 if (gwMac == null) {
1966 log.warn("Can't find gateway mac address from record {} for PD prefix", record);
1967 return;
1968 }
1969 IpAddress nextHopIp = getFirstIpByHost(record.directlyConnected(),
1970 gwMac,
1971 record.vlanId());
Kalhee Kim54a97c92018-04-02 21:23:46 +00001972 Route route = new Route(Route.Source.DHCP, pdIpPrefix, nextHopIp);
Kalhee Kim2f07c602017-11-15 18:57:53 +00001973 routeStore.removeRoute(route);
1974 if (this.dhcpFpmEnabled) {
1975 dhcpFpmPrefixStore.removeFpmRecord(pdIpPrefix);
1976 }
1977 }
1978 record.updatePdPrefTime(0);
1979 record.pdPrefix(null);
1980 addrOrPdRemoved = true;
1981 dhcpRelayStore.updateDhcpRecord(HostId.hostId(record.macAddress(),
1982 record.vlanId()), record);
1983 log.warn("PD prefix is set to null.delta {} pdPrefTime {}",
1984 (currentTime - record.getLastPdUpdate()), record.pdPrefTime());
1985 }
1986 }
1987 if (addrOrPdRemoved &&
1988 !record.ip6Address().isPresent() && !record.pdPrefix().isPresent()) {
1989 log.warn("ip6Status {} IP6 address and IP6 PD both are null. Remove record.", ip6Status);
1990 dhcpRelayStore.removeDhcpRecord(HostId.hostId(record.macAddress(), record.vlanId()));
1991 }
1992 }
1993 );
1994 }
1995}