blob: 9b9694e11374a494b7b059210e474243f4ecf218 [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;
Taras Lemkin41785912018-03-26 14:52:58 +000025import org.apache.felix.scr.annotations.Activate;
Yi Tseng51301292017-07-28 13:02:59 -070026import org.apache.felix.scr.annotations.Component;
Taras Lemkin41785912018-03-26 14:52:58 +000027import org.apache.felix.scr.annotations.Deactivate;
28import org.apache.felix.scr.annotations.Modified;
Yi Tseng51301292017-07-28 13:02:59 -070029import org.apache.felix.scr.annotations.Property;
Kalhee Kim45fede42017-09-05 19:05:06 +000030import org.apache.felix.scr.annotations.Reference;
31import org.apache.felix.scr.annotations.ReferenceCardinality;
Yi Tseng51301292017-07-28 13:02:59 -070032import org.apache.felix.scr.annotations.Service;
33import org.onlab.packet.BasePacket;
Kalhee Kim45fede42017-09-05 19:05:06 +000034import org.onlab.packet.DHCP6;
35import org.onlab.packet.IPv6;
36import org.onlab.packet.Ethernet;
37import org.onlab.packet.Ip6Address;
Yi Tseng51301292017-07-28 13:02:59 -070038import org.onlab.packet.IpAddress;
Kalhee Kim45fede42017-09-05 19:05:06 +000039import org.onlab.packet.IpPrefix;
Yi Tseng51301292017-07-28 13:02:59 -070040import org.onlab.packet.MacAddress;
Yi Tseng525ff402017-10-23 19:39:39 -070041import org.onlab.packet.TpPort;
Kalhee Kim45fede42017-09-05 19:05:06 +000042import org.onlab.packet.UDP;
Yi Tseng51301292017-07-28 13:02:59 -070043import org.onlab.packet.VlanId;
Lior Assoulinea21c0ca2018-01-28 16:18:48 -080044import org.onlab.packet.dhcp.Dhcp6ClientDataOption;
45import org.onlab.packet.dhcp.Dhcp6LeaseQueryOption;
Kalhee Kim45fede42017-09-05 19:05:06 +000046import org.onlab.packet.dhcp.Dhcp6RelayOption;
47import org.onlab.packet.dhcp.Dhcp6InterfaceIdOption;
Lior Assoulinea21c0ca2018-01-28 16:18:48 -080048import org.onlab.packet.dhcp.Dhcp6Option;
Kalhee Kim45fede42017-09-05 19:05:06 +000049import org.onlab.packet.dhcp.Dhcp6IaNaOption;
50import org.onlab.packet.dhcp.Dhcp6IaTaOption;
51import org.onlab.packet.dhcp.Dhcp6IaPdOption;
52import org.onlab.packet.dhcp.Dhcp6IaAddressOption;
53import org.onlab.packet.dhcp.Dhcp6IaPrefixOption;
Kalhee Kimea4b6c22017-11-09 14:38:37 +000054import org.onlab.packet.dhcp.Dhcp6ClientIdOption;
55import org.onlab.packet.dhcp.Dhcp6Duid;
Kalhee Kim495c9b22017-11-07 16:32:09 +000056import org.onlab.packet.DHCP6.MsgType;
Kalhee Kim45fede42017-09-05 19:05:06 +000057import org.onlab.util.HexString;
Taras Lemkin41785912018-03-26 14:52:58 +000058import org.onlab.util.Tools;
59import org.onosproject.cfg.ComponentConfigService;
Yi Tseng525ff402017-10-23 19:39:39 -070060import org.onosproject.core.ApplicationId;
61import org.onosproject.core.CoreService;
Yi Tseng51301292017-07-28 13:02:59 -070062import org.onosproject.dhcprelay.api.DhcpHandler;
Yi Tseng919b2df2017-09-07 16:22:51 -070063import org.onosproject.dhcprelay.api.DhcpServerInfo;
Yi Tseng525ff402017-10-23 19:39:39 -070064import org.onosproject.dhcprelay.config.IgnoreDhcpConfig;
Kalhee Kim45fede42017-09-05 19:05:06 +000065import org.onosproject.dhcprelay.store.DhcpRelayStore;
Kalhee Kimea4b6c22017-11-09 14:38:37 +000066import org.onosproject.dhcprelay.store.DhcpRecord;
Kalhee Kimba366062017-11-07 16:32:09 +000067import org.onosproject.dhcprelay.store.DhcpFpmPrefixStore;
Kalhee Kimd94ceea2017-11-29 19:03:02 +000068import org.onosproject.dhcprelay.store.DhcpRelayCountersStore;
Yi Tseng525ff402017-10-23 19:39:39 -070069import org.onosproject.net.Device;
70import org.onosproject.net.DeviceId;
Kalhee Kimba366062017-11-07 16:32:09 +000071import org.onosproject.routing.fpm.api.FpmRecord;
Yi Tseng525ff402017-10-23 19:39:39 -070072import org.onosproject.net.behaviour.Pipeliner;
73import org.onosproject.net.device.DeviceService;
74import org.onosproject.net.flow.DefaultTrafficSelector;
75import org.onosproject.net.flow.TrafficSelector;
76import org.onosproject.net.flowobjective.DefaultForwardingObjective;
77import org.onosproject.net.flowobjective.FlowObjectiveService;
78import org.onosproject.net.flowobjective.ForwardingObjective;
79import org.onosproject.net.flowobjective.Objective;
80import org.onosproject.net.flowobjective.ObjectiveContext;
81import org.onosproject.net.flowobjective.ObjectiveError;
Yi Tsengaa417a62017-09-08 17:22:51 -070082import org.onosproject.net.host.HostProvider;
83import org.onosproject.net.host.HostProviderRegistry;
84import org.onosproject.net.host.HostProviderService;
Kalhee Kim45fede42017-09-05 19:05:06 +000085import org.onosproject.net.host.HostService;
86import org.onosproject.net.host.DefaultHostDescription;
87import org.onosproject.net.host.HostDescription;
Kalhee Kim45fede42017-09-05 19:05:06 +000088import org.onosproject.net.host.HostListener;
89import org.onosproject.net.host.HostEvent;
90import org.onosproject.net.intf.Interface;
91import org.onosproject.net.intf.InterfaceService;
Yi Tseng525ff402017-10-23 19:39:39 -070092import org.onosproject.net.packet.PacketPriority;
Yi Tsengaa417a62017-09-08 17:22:51 -070093import org.onosproject.net.provider.ProviderId;
Kalhee Kim45fede42017-09-05 19:05:06 +000094import org.onosproject.routeservice.Route;
95import org.onosproject.routeservice.RouteStore;
Yi Tseng483ac6f2017-08-02 15:03:31 -070096import org.onosproject.dhcprelay.config.DhcpServerConfig;
Yi Tseng51301292017-07-28 13:02:59 -070097import org.onosproject.net.ConnectPoint;
Kalhee Kim45fede42017-09-05 19:05:06 +000098import org.onosproject.net.Host;
99import org.onosproject.net.HostId;
100import org.onosproject.net.HostLocation;
101import org.onosproject.net.packet.DefaultOutboundPacket;
102import org.onosproject.net.packet.OutboundPacket;
Yi Tseng51301292017-07-28 13:02:59 -0700103import org.onosproject.net.packet.PacketContext;
Kalhee Kim45fede42017-09-05 19:05:06 +0000104import org.onosproject.net.packet.PacketService;
Taras Lemkin41785912018-03-26 14:52:58 +0000105import org.osgi.service.component.ComponentContext;
Kalhee Kim45fede42017-09-05 19:05:06 +0000106import org.slf4j.Logger;
107import org.slf4j.LoggerFactory;
108import org.onosproject.net.flow.DefaultTrafficTreatment;
109import org.onosproject.net.flow.TrafficTreatment;
Kalhee Kim45fede42017-09-05 19:05:06 +0000110import java.nio.ByteBuffer;
Taras Lemkin41785912018-03-26 14:52:58 +0000111import java.util.ArrayList;
Yi Tseng483ac6f2017-08-02 15:03:31 -0700112import java.util.Collection;
Taras Lemkin41785912018-03-26 14:52:58 +0000113import java.util.Dictionary;
114import java.util.List;
Yi Tseng51301292017-07-28 13:02:59 -0700115import java.util.Optional;
Kalhee Kim45fede42017-09-05 19:05:06 +0000116import java.util.Set;
Charles Chan909cff82018-03-05 13:14:02 -0800117import java.util.concurrent.CopyOnWriteArrayList;
Jordan Halterman6328db72018-04-10 13:34:50 -0400118import java.util.concurrent.Executor;
Yi Tseng525ff402017-10-23 19:39:39 -0700119import java.util.concurrent.atomic.AtomicInteger;
Kalhee Kim45fede42017-09-05 19:05:06 +0000120import static com.google.common.base.Preconditions.checkNotNull;
121import static com.google.common.base.Preconditions.checkState;
Jordan Halterman6328db72018-04-10 13:34:50 -0400122import static java.util.concurrent.Executors.newSingleThreadExecutor;
123import static org.onlab.util.Tools.groupedThreads;
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
128@Component
129@Service
130@Property(name = "version", value = "6")
Yi Tsengaa417a62017-09-08 17:22:51 -0700131public class Dhcp6HandlerImpl implements DhcpHandler, HostProvider {
Charles Chand988c282017-09-12 17:09:32 -0700132 public static final String DHCP_V6_RELAY_APP = "org.onosproject.Dhcp6HandlerImpl";
Saurav Das7c6dec12017-09-13 14:35:56 -0700133 public static final ProviderId PROVIDER_ID = new ProviderId("dhcp6", DHCP_V6_RELAY_APP);
Yi Tseng525ff402017-10-23 19:39:39 -0700134 private static final int IGNORE_CONTROL_PRIORITY = PacketPriority.CONTROL.priorityValue() + 1000;
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000135 private String gCount = "global";
Taras Lemkin41785912018-03-26 14:52:58 +0000136 private static final String LQ_ROUTE_PROPERTY_NAME = "learnRouteFromLeasequery";
Yi Tseng525ff402017-10-23 19:39:39 -0700137 private static final TrafficSelector CLIENT_SERVER_SELECTOR = DefaultTrafficSelector.builder()
138 .matchEthType(Ethernet.TYPE_IPV6)
139 .matchIPProtocol(IPv6.PROTOCOL_UDP)
140 .matchIPv6Src(IpPrefix.IPV6_LINK_LOCAL_PREFIX)
141 .matchIPv6Dst(Ip6Address.ALL_DHCP_RELAY_AGENTS_AND_SERVERS.toIpPrefix())
142 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_V6_CLIENT_PORT))
143 .matchUdpDst(TpPort.tpPort(UDP.DHCP_V6_SERVER_PORT))
144 .build();
145 private static final TrafficSelector SERVER_RELAY_SELECTOR = DefaultTrafficSelector.builder()
146 .matchEthType(Ethernet.TYPE_IPV6)
147 .matchIPProtocol(IPv6.PROTOCOL_UDP)
148 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_V6_SERVER_PORT))
149 .matchUdpDst(TpPort.tpPort(UDP.DHCP_V6_SERVER_PORT))
150 .build();
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800151 // lease query reply is from server to client (no relay in between) - so we need to
152 // catch that scenario also ..
153 private static final TrafficSelector LEASE_QUERY_RESPONSE_SELECTOR = DefaultTrafficSelector.builder()
154 .matchEthType(Ethernet.TYPE_IPV6)
155 .matchIPProtocol(IPv6.PROTOCOL_UDP)
156 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_V6_SERVER_PORT))
157 .matchUdpDst(TpPort.tpPort(UDP.DHCP_V6_CLIENT_PORT))
158 .build();
Yi Tseng525ff402017-10-23 19:39:39 -0700159 static final Set<TrafficSelector> DHCP_SELECTORS = ImmutableSet.of(
160 CLIENT_SERVER_SELECTOR,
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800161 SERVER_RELAY_SELECTOR,
162 LEASE_QUERY_RESPONSE_SELECTOR
Yi Tseng525ff402017-10-23 19:39:39 -0700163 );
Kalhee Kim45fede42017-09-05 19:05:06 +0000164 private static Logger log = LoggerFactory.getLogger(Dhcp6HandlerImpl.class);
Yi Tseng51301292017-07-28 13:02:59 -0700165
Kalhee Kim45fede42017-09-05 19:05:06 +0000166 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
167 protected DhcpRelayStore dhcpRelayStore;
Yi Tseng51301292017-07-28 13:02:59 -0700168
Kalhee Kim45fede42017-09-05 19:05:06 +0000169 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000170 protected DhcpRelayCountersStore dhcpRelayCountersStore;
171
172 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Kalhee Kim45fede42017-09-05 19:05:06 +0000173 protected PacketService packetService;
174
175 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Kalhee Kim45fede42017-09-05 19:05:06 +0000176 protected RouteStore routeStore;
177
178 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
179 protected InterfaceService interfaceService;
180
181 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
182 protected HostService hostService;
183
Yi Tsengaa417a62017-09-08 17:22:51 -0700184 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
185 protected HostProviderRegistry providerRegistry;
Kalhee Kim45fede42017-09-05 19:05:06 +0000186
Yi Tseng525ff402017-10-23 19:39:39 -0700187 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
188 protected CoreService coreService;
189
190 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Kalhee Kimba366062017-11-07 16:32:09 +0000191 protected DhcpFpmPrefixStore dhcpFpmPrefixStore;
192
193 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Yi Tseng525ff402017-10-23 19:39:39 -0700194 protected DeviceService deviceService;
195
196 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
197 protected FlowObjectiveService flowObjectiveService;
198
Taras Lemkin41785912018-03-26 14:52:58 +0000199 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
200 protected ComponentConfigService cfgService;
201
202 @Property(name = Dhcp6HandlerImpl.LQ_ROUTE_PROPERTY_NAME, boolValue = false,
203 label = "Enable learning routing information from LQ")
204 private Boolean learnRouteFromLeasequery = Boolean.TRUE;
205
Yi Tsengaa417a62017-09-08 17:22:51 -0700206 protected HostProviderService providerService;
Yi Tseng525ff402017-10-23 19:39:39 -0700207 protected ApplicationId appId;
Charles Chan70fdd492018-03-07 17:36:06 -0800208 protected Multimap<DeviceId, VlanId> ignoredVlans = Multimaps.synchronizedMultimap(HashMultimap.create());
Yi Tseng525ff402017-10-23 19:39:39 -0700209 private InternalHostListener hostListener = new InternalHostListener();
Kalhee Kimba366062017-11-07 16:32:09 +0000210 private Boolean dhcpFpmEnabled = false;
Charles Chan909cff82018-03-05 13:14:02 -0800211 private List<DhcpServerInfo> defaultServerInfoList = new CopyOnWriteArrayList<>();
212 private List<DhcpServerInfo> indirectServerInfoList = new CopyOnWriteArrayList<>();
Jordan Halterman6328db72018-04-10 13:34:50 -0400213
214 private Executor hostEventExecutor = newSingleThreadExecutor(
215 groupedThreads("dhcp6-event-host", "%d", log));
216
Kalhee Kim495c9b22017-11-07 16:32:09 +0000217 private class IpAddressInfo {
218 Ip6Address ip6Address;
219 long prefTime;
220 }
221 private class PdPrefixInfo {
222 IpPrefix pdPrefix;
223 long prefTime;
224 }
225 protected int dhcp6PollInterval = 24 * 3600; // 24 hr period
226
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000227 // max 1 thread
228 static Semaphore recordSemaphore = new Semaphore(1);
Kalhee Kim45fede42017-09-05 19:05:06 +0000229
230 // CLIENT message types
231 public static final Set<Byte> MSG_TYPE_FROM_CLIENT =
232 ImmutableSet.of(DHCP6.MsgType.SOLICIT.value(),
233 DHCP6.MsgType.REQUEST.value(),
234 DHCP6.MsgType.REBIND.value(),
235 DHCP6.MsgType.RENEW.value(),
236 DHCP6.MsgType.RELEASE.value(),
237 DHCP6.MsgType.DECLINE.value(),
238 DHCP6.MsgType.CONFIRM.value(),
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800239 DHCP6.MsgType.RELAY_FORW.value(),
240 DHCP6.MsgType.LEASEQUERY.value());
Kalhee Kim45fede42017-09-05 19:05:06 +0000241 // SERVER message types
242 public static final Set<Byte> MSG_TYPE_FROM_SERVER =
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800243 ImmutableSet.of(DHCP6.MsgType.RELAY_REPL.value(),
244 DHCP6.MsgType.LEASEQUERY_REPLY.value());
Kalhee Kim45fede42017-09-05 19:05:06 +0000245
246 @Activate
Taras Lemkin41785912018-03-26 14:52:58 +0000247 protected void activate(ComponentContext context) {
248 cfgService.registerProperties(getClass());
249 modified(context);
Yi Tseng525ff402017-10-23 19:39:39 -0700250 appId = coreService.registerApplication(DHCP_V6_RELAY_APP);
Yi Tsengaa417a62017-09-08 17:22:51 -0700251 providerService = providerRegistry.register(this);
Kalhee Kim45fede42017-09-05 19:05:06 +0000252 hostService.addListener(hostListener);
Yi Tseng51301292017-07-28 13:02:59 -0700253 }
254
Kalhee Kim45fede42017-09-05 19:05:06 +0000255 @Deactivate
256 protected void deactivate() {
Taras Lemkin41785912018-03-26 14:52:58 +0000257 cfgService.unregisterProperties(getClass(), false);
Yi Tsengaa417a62017-09-08 17:22:51 -0700258 providerRegistry.unregister(this);
Kalhee Kim45fede42017-09-05 19:05:06 +0000259 hostService.removeListener(hostListener);
Yi Tseng919b2df2017-09-07 16:22:51 -0700260 defaultServerInfoList.forEach(this::stopMonitoringIps);
Charles Chana134ee32018-07-19 09:52:01 -0700261 defaultServerInfoList.forEach(info -> info.getDhcpServerIp6().ifPresent(this::cancelDhcpPacket));
Yi Tseng919b2df2017-09-07 16:22:51 -0700262 defaultServerInfoList.clear();
263 indirectServerInfoList.forEach(this::stopMonitoringIps);
Charles Chana134ee32018-07-19 09:52:01 -0700264 indirectServerInfoList.forEach(info -> info.getDhcpServerIp6().ifPresent(this::cancelDhcpPacket));
Yi Tseng919b2df2017-09-07 16:22:51 -0700265 indirectServerInfoList.clear();
266 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000267
Taras Lemkin41785912018-03-26 14:52:58 +0000268 @Modified
269 protected void modified(ComponentContext context) {
270 Dictionary<?, ?> properties = context.getProperties();
271 Boolean flag;
272 flag = Tools.isPropertyEnabled(properties, Dhcp6HandlerImpl.LQ_ROUTE_PROPERTY_NAME);
273 if (flag != null) {
274 learnRouteFromLeasequery = flag;
275 log.info("Learning routes from DHCP leasequery is {}",
276 learnRouteFromLeasequery ? "enabled" : "disabled");
277 }
278 }
279
Yi Tseng919b2df2017-09-07 16:22:51 -0700280 private void stopMonitoringIps(DhcpServerInfo serverInfo) {
281 serverInfo.getDhcpGatewayIp6().ifPresent(gatewayIp -> {
282 hostService.stopMonitoringIp(gatewayIp);
283 });
284 serverInfo.getDhcpServerIp6().ifPresent(serverIp -> {
285 hostService.stopMonitoringIp(serverIp);
286 });
Yi Tseng51301292017-07-28 13:02:59 -0700287 }
288
Yi Tseng51301292017-07-28 13:02:59 -0700289 @Override
Yi Tseng919b2df2017-09-07 16:22:51 -0700290 public List<DhcpServerInfo> getDefaultDhcpServerInfoList() {
291 return defaultServerInfoList;
Yi Tseng51301292017-07-28 13:02:59 -0700292 }
293
294 @Override
Yi Tseng919b2df2017-09-07 16:22:51 -0700295 public List<DhcpServerInfo> getIndirectDhcpServerInfoList() {
296 return indirectServerInfoList;
Yi Tseng51301292017-07-28 13:02:59 -0700297 }
298
299 @Override
Yi Tseng525ff402017-10-23 19:39:39 -0700300 public void updateIgnoreVlanConfig(IgnoreDhcpConfig config) {
301 if (config == null) {
302 ignoredVlans.forEach(((deviceId, vlanId) -> {
303 processIgnoreVlanRule(deviceId, vlanId, REMOVE);
304 }));
305 return;
306 }
307 config.ignoredVlans().forEach((deviceId, vlanId) -> {
308 if (ignoredVlans.get(deviceId).contains(vlanId)) {
309 // don't need to process if it already ignored
310 return;
311 }
312 processIgnoreVlanRule(deviceId, vlanId, ADD);
313 });
Yi Tseng525ff402017-10-23 19:39:39 -0700314 ignoredVlans.forEach((deviceId, vlanId) -> {
315 if (!config.ignoredVlans().get(deviceId).contains(vlanId)) {
316 // not contains in new config, remove it
317 processIgnoreVlanRule(deviceId, vlanId, REMOVE);
318 }
319 });
320 }
321
322 @Override
Saurav Dasb805f1a2017-12-13 16:19:35 -0800323 public void removeIgnoreVlanState(IgnoreDhcpConfig config) {
324 if (config == null) {
325 ignoredVlans.clear();
326 return;
327 }
328 config.ignoredVlans().forEach((deviceId, vlanId) -> {
329 ignoredVlans.remove(deviceId, vlanId);
330 });
331 }
332
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800333 public DhcpRecord getDhcpRelayRecordFor(Ip6Address clientAddress) {
334
Taras Lemkin41785912018-03-26 14:52:58 +0000335 Collection<DhcpRecord> records = dhcpRelayStore.getDhcpRecords();
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800336 DhcpRecord dr = null;
337 for (DhcpRecord e:records) {
338 if (e.ip6Address().isPresent()) {
339 if (e.ip6Address().get().equals(clientAddress)) {
340 dr = e;
341 break;
342 }
343 }
344 }
345 return dr;
346 }
347
348 public MacAddress findNextHopMacForIp6FromRelayStore(Ip6Address clientAddress,
349 MacAddress clientMacAddress, VlanId vlanID) {
350
351 DhcpRecord dr = getDhcpRelayRecordFor(clientAddress);
352
353 if (dr != null) {
354 Optional<MacAddress> nextHopTempMac = dr.nextHopTemp();
355 if (nextHopTempMac.isPresent()) {
356 log.info("findNextHopForIp6FromRelayStore " + clientAddress + " got mac " + nextHopTempMac.toString());
357 return nextHopTempMac.get();
358 }
359 } else {
360 log.warn("findNextHopMacForIp6FromRelayStore could NOT find next hop for " + clientAddress);
361 return null;
362 }
363 return null;
364 }
365
366 public Ip6Address findNextHopIp6FromRelayStore(Ip6Address clientAddress) {
367
368 DhcpRecord dr = getDhcpRelayRecordFor(clientAddress);
369 if (dr != null) {
370 Optional<MacAddress> nextHopMac = dr.nextHop();
371 if (nextHopMac.isPresent()) {
372 // find the local ip6 from the host store
373 HostId gwHostId = HostId.hostId(nextHopMac.get(), dr.vlanId());
374 Host gwHost = hostService.getHost(gwHostId);
375 if (gwHost == null) {
376 log.warn("Can't find next hop host ID {}", gwHostId);
377 return null;
378 }
379 Ip6Address nextHopIp = gwHost.ipAddresses()
380 .stream()
381 .filter(IpAddress::isIp6)
382 .filter(IpAddress::isLinkLocal)
383 .map(IpAddress::getIp6Address)
384 .findFirst()
385 .orElse(null);
386
387 log.info("findNextHopIp6FromRelayStore " + clientAddress + " got mac " +
388 nextHopMac.toString() + " ip6 " + nextHopIp);
389 return nextHopIp;
390 }
391 } else {
392 log.warn("findNextHopIp6FromRelayStore could NOT find next hop for " + clientAddress);
393 return null;
394 }
395 return null;
396 }
397
398 private void setPotentialNextHopForIp6InRelayStore(Ip6Address clientAddress,
399 VlanId vlanId, MacAddress nh) {
400 DhcpRecord dr = getDhcpRelayRecordFor(clientAddress);
401 if (dr != null) {
402 dr.nextHopTemp(nh);
403 log.debug("LQ6 potential NH mac " + nh.toString() + " UPDATED in RelayRecord client " + clientAddress);
404 } else {
405 log.warn("LQ6 potential NH mac" + nh.toString() +
406 " NOT FOUND in RelayRecord for client - LQ rejected" + clientAddress);
407 }
408 }
409
410 public void handleLeaseQuery6ReplyMsg(PacketContext context, DHCP6 dhcp6Payload) {
411 ConnectPoint inPort = context.inPacket().receivedFrom();
412 log.info("Got LQV6-REPLY on port {}", inPort);
413 List<Dhcp6Option> lopt = dhcp6Payload.getOptions();
414 log.info("Options list: {}", lopt);
415 // find out if this lease is known is
416 Dhcp6ClientDataOption clientDataOption = dhcp6Payload.getOptions()
417 .stream()
418 .filter(opt -> opt instanceof Dhcp6ClientDataOption)
419 .map(pld -> (Dhcp6ClientDataOption) pld)
420 .findFirst()
421 .orElse(null);
422
423 if (clientDataOption == null) {
424 log.warn("clientDataOption option is not present, " +
425 "lease is UNKNOWN - not adding any new route...");
426 } else {
427 Dhcp6IaAddressOption aiAddressOption = clientDataOption.getOptions()
428 .stream()
429 .filter(opt -> opt instanceof Dhcp6IaAddressOption)
430 .map(pld -> (Dhcp6IaAddressOption) pld)
431 .findFirst()
432 .orElse(null);
433
434 Dhcp6ClientIdOption clientIdOption = clientDataOption.getOptions()
435 .stream()
436 .filter(opt -> opt instanceof Dhcp6ClientIdOption)
437 .map(pld -> (Dhcp6ClientIdOption) pld)
438 .findFirst()
439 .orElse(null);
440
441 if (aiAddressOption == null) {
442 log.warn("clientDataOption from DHCP server does not " +
443 "contains Dhcp6IaAddressOption for the client - giving up...");
444 } else {
445 Ip6Address clientAddress = aiAddressOption.getIp6Address();
446 MacAddress clientMacAddress = MacAddress.valueOf(clientIdOption.getDuid().getLinkLayerAddress());
447 Ethernet packet = context.inPacket().parsed();
448 VlanId vlanId = VlanId.vlanId(packet.getVlanID());
449 MacAddress potentialNextHopMac =
450 findNextHopMacForIp6FromRelayStore(clientAddress, clientMacAddress, vlanId);
451
452 if (potentialNextHopMac == null) {
453 log.warn("Can't find next hop host mac for client {} mac:{}/{}",
454 clientAddress, clientMacAddress, vlanId);
455 return;
456 } else {
457 log.info("Next hop mac for {}/{}/{} is {}", clientAddress,
458 clientMacAddress, vlanId, potentialNextHopMac.toString());
459 }
460 // search the next hop in the hosts store
461 HostId gwHostId = HostId.hostId(potentialNextHopMac, vlanId);
462 Host gwHost = hostService.getHost(gwHostId);
463 if (gwHost == null) {
464 log.warn("Can't find next hop host ID {}", gwHostId);
465 return;
466 }
467 Ip6Address nextHopIp = gwHost.ipAddresses()
468 .stream()
469 .filter(IpAddress::isIp6)
470 .filter(IpAddress::isLinkLocal)
471 .map(IpAddress::getIp6Address)
472 .findFirst()
473 .orElse(null);
474 if (nextHopIp == null) {
475 log.warn("Can't find IP6 address of next hop {}", gwHost);
476 return;
477 }
478 log.info("client " + clientAddress + " is known !");
Kalhee Kim54a97c92018-04-02 21:23:46 +0000479 Route routeForIP6 = new Route(Route.Source.DHCP, clientAddress.toIpPrefix(), nextHopIp);
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800480 log.debug("updating route of Client for indirectly connected.");
481 log.debug("client ip: " + clientAddress + ", next hop ip6: " + nextHopIp);
482 routeStore.updateRoute(routeForIP6);
483 }
484 }
485 }
486
Saurav Dasb805f1a2017-12-13 16:19:35 -0800487 @Override
Kalhee Kim45fede42017-09-05 19:05:06 +0000488 public void processDhcpPacket(PacketContext context, BasePacket payload) {
489 checkNotNull(payload, "DHCP6 payload can't be null");
490 checkState(payload instanceof DHCP6, "Payload is not a DHCP6");
491 DHCP6 dhcp6Payload = (DHCP6) payload;
492 Ethernet receivedPacket = context.inPacket().parsed();
493
494 if (!configured()) {
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800495 log.warn("Missing DHCP6 relay server config. " +
496 "Abort packet processing dhcp6 payload {}", dhcp6Payload);
Kalhee Kim45fede42017-09-05 19:05:06 +0000497 return;
498 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000499 byte msgTypeVal = dhcp6Payload.getMsgType();
500 MsgType msgType = DHCP6.MsgType.getType(msgTypeVal);
501 log.debug("msgType is {}", msgType);
Kalhee Kim45fede42017-09-05 19:05:06 +0000502
Kalhee Kim45fede42017-09-05 19:05:06 +0000503 ConnectPoint inPort = context.inPacket().receivedFrom();
Jonathan Hart8ca2bc02017-11-30 18:23:42 -0800504
Kalhee Kim495c9b22017-11-07 16:32:09 +0000505 if (inPort == null) {
Taras Lemkin41785912018-03-26 14:52:58 +0000506 log.warn("incoming ConnectPoint is null");
Kalhee Kim495c9b22017-11-07 16:32:09 +0000507 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000508 Set<Interface> receivingInterfaces = interfaceService.getInterfacesByPort(inPort);
509 //ignore the packets if dhcp client interface is not configured on onos.
510 if (receivingInterfaces.isEmpty()) {
511 log.warn("Virtual interface is not configured on {}", inPort);
512 return;
513 }
514
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800515 if (msgTypeVal == DHCP6.MsgType.LEASEQUERY.value()) {
Taras Lemkin41785912018-03-26 14:52:58 +0000516 List<InternalPacket> ethernetClientPackets =
517 learnRouteFromLeasequery ?
518 processLQ6PacketFromClient(context, receivedPacket, receivingInterfaces, dhcp6Payload) :
519 processDhcp6ForwardOnly(context, receivedPacket, receivingInterfaces, dhcp6Payload);
520 for (InternalPacket internalPacket : ethernetClientPackets) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000521 forwardPacket(internalPacket);
Kalhee Kim45fede42017-09-05 19:05:06 +0000522 }
Taras Lemkin41785912018-03-26 14:52:58 +0000523 } else if (msgTypeVal == DHCP6.MsgType.LEASEQUERY_REPLY.value() && learnRouteFromLeasequery) {
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800524 IPv6 clientIpv6 = (IPv6) receivedPacket.getPayload();
525 UDP clientUdp = (UDP) clientIpv6.getPayload();
526 DHCP6 clientDhcp6 = (DHCP6) clientUdp.getPayload();
Taras Lemkin41785912018-03-26 14:52:58 +0000527 Interface serverInterface = Dhcp6HandlerUtil.directlyConnected(clientDhcp6) ?
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800528 getServerInterface() : getIndirectServerInterface();
Kalhee Kim45fede42017-09-05 19:05:06 +0000529 InternalPacket ethernetPacketReply =
Taras Lemkin41785912018-03-26 14:52:58 +0000530 Dhcp6HandlerUtil.processLQ6PacketFromServer(
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800531 defaultServerInfoList, indirectServerInfoList,
532 serverInterface, interfaceService,
533 hostService,
534 context, receivedPacket, receivingInterfaces);
Kalhee Kim45fede42017-09-05 19:05:06 +0000535 if (ethernetPacketReply != null) {
536 forwardPacket(ethernetPacketReply);
537 }
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800538 handleLeaseQuery6ReplyMsg(context, dhcp6Payload);
Taras Lemkin41785912018-03-26 14:52:58 +0000539 } else if (MSG_TYPE_FROM_CLIENT.contains(msgTypeVal)) {
540 List<InternalPacket> ethernetClientPacket =
541 processDhcp6PacketFromClient(context, receivedPacket, receivingInterfaces);
542 for (InternalPacket internalPacket : ethernetClientPacket) {
543 forwardPacket(internalPacket);
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800544 }
Taras Lemkin41785912018-03-26 14:52:58 +0000545 } else if (MSG_TYPE_FROM_SERVER.contains(msgTypeVal)) {
546 log.debug("calling processDhcp6PacketFromServer with RELAY_REPL {}, {}", receivedPacket, dhcp6Payload);
547 InternalPacket ethernetPacketReply =
548 processDhcp6PacketFromServer(context, receivedPacket, receivingInterfaces);
549 if (ethernetPacketReply != null) {
550 forwardPacket(ethernetPacketReply);
551 }
552 } else {
553 log.warn("Not so fast, packet type {} not supported yet", msgTypeVal);
Kalhee Kim45fede42017-09-05 19:05:06 +0000554 }
555 }
556
Kalhee Kim45fede42017-09-05 19:05:06 +0000557 /**
558 * Checks if this app has been configured.
559 *
560 * @return true if all information we need have been initialized
561 */
562 public boolean configured() {
Yi Tseng919b2df2017-09-07 16:22:51 -0700563 return !defaultServerInfoList.isEmpty();
Kalhee Kim45fede42017-09-05 19:05:06 +0000564 }
565
Yi Tsengaa417a62017-09-08 17:22:51 -0700566 @Override
567 public ProviderId id() {
Charles Chand988c282017-09-12 17:09:32 -0700568 return PROVIDER_ID;
Yi Tsengaa417a62017-09-08 17:22:51 -0700569 }
570
571 @Override
572 public void triggerProbe(Host host) {
573 // Do nothing here
574 }
575
Kalhee Kim45fede42017-09-05 19:05:06 +0000576 //forward the packet to ConnectPoint where the DHCP server is attached.
577 private void forwardPacket(InternalPacket packet) {
578 //send Packetout to dhcp server connectpoint.
Taras Lemkin41785912018-03-26 14:52:58 +0000579 if (packet.getDestLocation() != null) {
Kalhee Kim45fede42017-09-05 19:05:06 +0000580 TrafficTreatment t = DefaultTrafficTreatment.builder()
Taras Lemkin41785912018-03-26 14:52:58 +0000581 .setOutput(packet.getDestLocation().port()).build();
Kalhee Kim45fede42017-09-05 19:05:06 +0000582 OutboundPacket o = new DefaultOutboundPacket(
Taras Lemkin41785912018-03-26 14:52:58 +0000583 packet.getDestLocation().deviceId(), t, ByteBuffer.wrap(packet.getPacket().serialize()));
Kalhee Kim45fede42017-09-05 19:05:06 +0000584 packetService.emit(o);
Taras Lemkin41785912018-03-26 14:52:58 +0000585 if (log.isTraceEnabled()) {
586 IPv6 ip6 = (IPv6) packet.getPacket().getPayload();
587 UDP udp = (UDP) ip6.getPayload();
588 DHCP6 dhcp6 = (DHCP6) udp.getPayload();
589 log.trace("Relaying packet to destination {} eth: {} dhcp: {}",
590 packet.getDestLocation(), packet.getPacket(), dhcp6);
591 }
592
593 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000594 }
595
Kalhee Kim45fede42017-09-05 19:05:06 +0000596 /**
597 * extract from dhcp6 packet client ipv6 address of given by dhcp server.
598 *
599 * @param dhcp6 the dhcp6 packet
Kalhee Kim495c9b22017-11-07 16:32:09 +0000600 * @return IpAddressInfo IpAddressInfo given by dhcp server, or null if not exists
Kalhee Kim45fede42017-09-05 19:05:06 +0000601 */
Kalhee Kim495c9b22017-11-07 16:32:09 +0000602 private IpAddressInfo extractIpAddress(DHCP6 dhcp6) {
603 IpAddressInfo ipInfo = new IpAddressInfo();
Kalhee Kim45fede42017-09-05 19:05:06 +0000604
605 log.debug("extractIpAddress enters dhcp6 {}.", dhcp6);
606 // Extract IPv6 address from IA NA ot IA TA option
607 Optional<Dhcp6IaNaOption> iaNaOption = dhcp6.getOptions()
608 .stream()
609 .filter(opt -> opt instanceof Dhcp6IaNaOption)
610 .map(opt -> (Dhcp6IaNaOption) opt)
611 .findFirst();
612 Optional<Dhcp6IaTaOption> iaTaOption = dhcp6.getOptions()
613 .stream()
614 .filter(opt -> opt instanceof Dhcp6IaTaOption)
615 .map(opt -> (Dhcp6IaTaOption) opt)
616 .findFirst();
617 Optional<Dhcp6IaAddressOption> iaAddressOption;
618 if (iaNaOption.isPresent()) {
619 log.debug("Found IPv6 address from iaNaOption {}", iaNaOption);
620
621 iaAddressOption = iaNaOption.get().getOptions().stream()
622 .filter(opt -> opt instanceof Dhcp6IaAddressOption)
623 .map(opt -> (Dhcp6IaAddressOption) opt)
624 .findFirst();
625 } else if (iaTaOption.isPresent()) {
626 log.debug("Found IPv6 address from iaTaOption {}", iaTaOption);
627
628 iaAddressOption = iaTaOption.get().getOptions().stream()
629 .filter(opt -> opt instanceof Dhcp6IaAddressOption)
630 .map(opt -> (Dhcp6IaAddressOption) opt)
631 .findFirst();
632 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000633 log.info("No IPv6 address found from iaTaOption {}", iaTaOption);
Kalhee Kim45fede42017-09-05 19:05:06 +0000634 iaAddressOption = Optional.empty();
635 }
636 if (iaAddressOption.isPresent()) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000637 ipInfo.ip6Address = iaAddressOption.get().getIp6Address();
638 ipInfo.prefTime = iaAddressOption.get().getPreferredLifetime();
Kalhee Kim45fede42017-09-05 19:05:06 +0000639 log.debug("Found IPv6 address from iaAddressOption {}", iaAddressOption);
Kalhee Kim45fede42017-09-05 19:05:06 +0000640 } else {
641 log.debug("Can't find IPv6 address from DHCPv6 {}", dhcp6);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000642 return null;
Kalhee Kim45fede42017-09-05 19:05:06 +0000643 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000644 return ipInfo;
Kalhee Kim45fede42017-09-05 19:05:06 +0000645 }
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800646
Kalhee Kim45fede42017-09-05 19:05:06 +0000647 /**
648 * extract from dhcp6 packet Prefix prefix provided by dhcp server.
649 *
650 * @param dhcp6 the dhcp6 payload
651 * @return IpPrefix Prefix Delegation prefix, or null if not exists.
652 */
Kalhee Kim495c9b22017-11-07 16:32:09 +0000653 private PdPrefixInfo extractPrefix(DHCP6 dhcp6) {
654 log.debug("extractPrefix enters {}", dhcp6);
Kalhee Kim45fede42017-09-05 19:05:06 +0000655
656 // extract prefix
Kalhee Kim495c9b22017-11-07 16:32:09 +0000657 PdPrefixInfo pdPrefixInfo = new PdPrefixInfo();
Kalhee Kim45fede42017-09-05 19:05:06 +0000658
659 Ip6Address prefixAddress = null;
660
661 // Extract IPv6 prefix from IA PD option
662 Optional<Dhcp6IaPdOption> iaPdOption = dhcp6.getOptions()
663 .stream()
664 .filter(opt -> opt instanceof Dhcp6IaPdOption)
665 .map(opt -> (Dhcp6IaPdOption) opt)
666 .findFirst();
667
668 Optional<Dhcp6IaPrefixOption> iaPrefixOption;
669 if (iaPdOption.isPresent()) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000670 log.debug("IA_PD option found {}", iaPdOption);
Kalhee Kim45fede42017-09-05 19:05:06 +0000671
672 iaPrefixOption = iaPdOption.get().getOptions().stream()
673 .filter(opt -> opt instanceof Dhcp6IaPrefixOption)
674 .map(opt -> (Dhcp6IaPrefixOption) opt)
675 .findFirst();
676 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000677 log.debug("IA_PD option NOT found");
Kalhee Kim45fede42017-09-05 19:05:06 +0000678
679 iaPrefixOption = Optional.empty();
680 }
681 if (iaPrefixOption.isPresent()) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000682 log.debug("IAPrefix Option within IA_PD option found {}", iaPrefixOption);
Kalhee Kim45fede42017-09-05 19:05:06 +0000683
684 prefixAddress = iaPrefixOption.get().getIp6Prefix();
685 int prefixLen = (int) iaPrefixOption.get().getPrefixLength();
Kalhee Kim495c9b22017-11-07 16:32:09 +0000686 log.debug("Prefix length is {} bits", prefixLen);
687 pdPrefixInfo.pdPrefix = IpPrefix.valueOf(prefixAddress, prefixLen);
688 pdPrefixInfo.prefTime = iaPrefixOption.get().getPreferredLifetime();
Kalhee Kim45fede42017-09-05 19:05:06 +0000689 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000690 log.debug("Can't find IPv6 prefix from DHCPv6 {}", dhcp6);
691 return null;
Kalhee Kim45fede42017-09-05 19:05:06 +0000692 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000693 return pdPrefixInfo;
Kalhee Kim45fede42017-09-05 19:05:06 +0000694 }
695
696 /**
Kalhee Kim495c9b22017-11-07 16:32:09 +0000697 * remove host or route and update dhcp relay record attributes.
Kalhee Kim45fede42017-09-05 19:05:06 +0000698 *
699 * @param directConnFlag flag to show that packet is from directly connected client
Kalhee Kim495c9b22017-11-07 16:32:09 +0000700 * @param location client side connect point
Kalhee Kim45fede42017-09-05 19:05:06 +0000701 * @param dhcp6Packet the dhcp6 payload
702 * @param clientPacket client's ethernet packet
703 * @param clientIpv6 client's Ipv6 packet
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +0000704 * @param clientInterface client interfaces
Kalhee Kim45fede42017-09-05 19:05:06 +0000705 */
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000706 private void removeHostOrRoute(boolean directConnFlag, ConnectPoint location,
707 DHCP6 dhcp6Packet,
Kalhee Kim45fede42017-09-05 19:05:06 +0000708 Ethernet clientPacket, IPv6 clientIpv6,
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +0000709 Interface clientInterface) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000710 log.debug("removeHostOrRoute enters {}", dhcp6Packet);
Kalhee Kim121ba922017-11-01 17:56:44 +0000711 VlanId vlanId = clientInterface.vlan();
Kalhee Kim495c9b22017-11-07 16:32:09 +0000712 MacAddress srcMac = clientPacket.getSourceMAC(); // could be gw or host
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000713 MacAddress leafClientMac;
714 byte leafMsgType;
Kalhee Kim495c9b22017-11-07 16:32:09 +0000715 log.debug("client mac {} client vlan {}", HexString.toHexString(srcMac.toBytes(), ":"), vlanId);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000716
Charles Chanc7f63b62018-08-13 10:32:03 -0700717 Dhcp6ClientIdOption clientIdOption = Dhcp6HandlerUtil.extractClientId(directConnFlag, dhcp6Packet);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000718 if (clientIdOption != null) {
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000719 if ((clientIdOption.getDuid().getDuidType() == Dhcp6Duid.DuidType.DUID_LLT) ||
720 (clientIdOption.getDuid().getDuidType() == Dhcp6Duid.DuidType.DUID_LL)) {
721 leafClientMac = MacAddress.valueOf(clientIdOption.getDuid().getLinkLayerAddress());
722 } else {
723 log.warn("Link-Layer Address not supported in CLIENTID option. No DhcpRelay Record created.");
Charles Chancd1783f2019-02-01 11:31:50 -0800724 //dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_LINKLOCAL_FAIL);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000725 return;
726 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000727 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000728 log.warn("CLIENTID option NOT found. Don't create DhcpRelay Record.");
Charles Chancd1783f2019-02-01 11:31:50 -0800729 //dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_CLIENTID_FAIL);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000730 return;
731 }
732
Kalhee Kim495c9b22017-11-07 16:32:09 +0000733 HostId leafHostId = HostId.hostId(leafClientMac, vlanId);
734 DhcpRecord record = dhcpRelayStore.getDhcpRecord(leafHostId).orElse(null);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000735 if (record == null) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000736 record = new DhcpRecord(leafHostId);
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000737 } else {
738 record = record.clone();
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000739 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000740
Taras Lemkin41785912018-03-26 14:52:58 +0000741 Boolean isMsgRelease = Dhcp6HandlerUtil.isDhcp6Release(dhcp6Packet);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000742 IpAddressInfo ipInfo;
743 PdPrefixInfo pdInfo = null;
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000744 if (directConnFlag) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000745 // Add to host store if it is connected to network directly
746 ipInfo = extractIpAddress(dhcp6Packet);
747 if (ipInfo != null) {
748 if (isMsgRelease) {
749 HostId hostId = HostId.hostId(srcMac, vlanId);
750 log.debug("remove Host {} ip for directly connected.", hostId.toString());
751 providerService.removeIpFromHost(hostId, ipInfo.ip6Address);
Kalhee Kim45fede42017-09-05 19:05:06 +0000752 }
753 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000754 log.debug("ipAddress not found. Do not remove Host {} for directly connected.",
755 HostId.hostId(srcMac, vlanId).toString());
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000756 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000757 leafMsgType = dhcp6Packet.getMsgType();
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000758 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000759 // Remove from route store if it is not connected to network directly
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000760 // pick out the first link-local ip address
Kalhee Kim495c9b22017-11-07 16:32:09 +0000761 IpAddress nextHopIp = getFirstIpByHost(directConnFlag, srcMac, vlanId);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000762 if (nextHopIp == null) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000763 log.warn("Can't find link-local IP address of gateway mac {} vlanId {}", srcMac, vlanId);
Charles Chancd1783f2019-02-01 11:31:50 -0800764 //dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_LINKLOCAL_GW);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000765 return;
766 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000767
Taras Lemkin41785912018-03-26 14:52:58 +0000768 DHCP6 leafDhcp = Dhcp6HandlerUtil.getDhcp6Leaf(dhcp6Packet);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000769 ipInfo = extractIpAddress(leafDhcp);
770 if (ipInfo == null) {
771 log.debug("ip is null");
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000772 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000773 if (isMsgRelease) {
Kalhee Kim54a97c92018-04-02 21:23:46 +0000774 Route routeForIP = new Route(Route.Source.DHCP, ipInfo.ip6Address.toIpPrefix(), nextHopIp);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000775 log.debug("removing route of 128 address for indirectly connected.");
776 log.debug("128 ip {}, nexthop {}",
777 HexString.toHexString(ipInfo.ip6Address.toOctets(), ":"),
778 HexString.toHexString(nextHopIp.toOctets(), ":"));
779 routeStore.removeRoute(routeForIP);
Kalhee Kim45fede42017-09-05 19:05:06 +0000780 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000781 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000782
Kalhee Kim495c9b22017-11-07 16:32:09 +0000783 pdInfo = extractPrefix(leafDhcp);
784 if (pdInfo == null) {
785 log.debug("ipPrefix is null ");
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000786 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000787 if (isMsgRelease) {
Kalhee Kim54a97c92018-04-02 21:23:46 +0000788 Route routeForPrefix = new Route(Route.Source.DHCP, pdInfo.pdPrefix, nextHopIp);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000789 log.debug("removing route of PD for indirectly connected.");
790 log.debug("pd ip {}, nexthop {}",
791 HexString.toHexString(pdInfo.pdPrefix.address().toOctets(), ":"),
792 HexString.toHexString(nextHopIp.toOctets(), ":"));
793
794 routeStore.removeRoute(routeForPrefix);
Kalhee Kimba366062017-11-07 16:32:09 +0000795 if (this.dhcpFpmEnabled) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000796 dhcpFpmPrefixStore.removeFpmRecord(pdInfo.pdPrefix);
Kalhee Kimba366062017-11-07 16:32:09 +0000797 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000798 }
799 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000800 leafMsgType = leafDhcp.getMsgType();
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000801 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000802
Kalhee Kim495c9b22017-11-07 16:32:09 +0000803 if (isMsgRelease) {
804 log.debug("DHCP6 RELEASE msg.");
805 if (record != null) {
806 if (ipInfo != null) {
807 log.debug("DhcpRelay Record ip6Address is set to null.");
808 record.ip6Address(null);
809 }
810 if (pdInfo != null) {
811 log.debug("DhcpRelay Record pdPrefix is set to null.");
812 }
813
814 if (!record.ip6Address().isPresent() && !record.pdPrefix().isPresent()) {
815 log.warn("IP6 address and IP6 PD both are null. Remove record.");
816 // do not remove a record. Let timer task handler it.
817 //dhcpRelayStore.removeDhcpRecord(HostId.hostId(leafClientMac, vlanId));
818 }
819 }
820 }
821
Ray Milkeyffe1a332018-01-24 10:41:14 -0800822 if (record != null) {
Taras Lemkin41785912018-03-26 14:52:58 +0000823 record.getV6Counters().incrementCounter(Dhcp6HandlerUtil.getMsgTypeStr(leafMsgType));
Ray Milkeyffe1a332018-01-24 10:41:14 -0800824 record.addLocation(new HostLocation(location, System.currentTimeMillis()));
825 record.ip6Status(DHCP6.MsgType.getType(leafMsgType));
826 record.setDirectlyConnected(directConnFlag);
827 if (!directConnFlag) {
828 // Update gateway mac address if the host is not directly connected
829 record.nextHop(srcMac);
830 }
831 record.updateLastSeen();
Kalhee Kim495c9b22017-11-07 16:32:09 +0000832 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000833 dhcpRelayStore.updateDhcpRecord(leafHostId, record);
Charles Chancd1783f2019-02-01 11:31:50 -0800834 /*
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000835 // TODO Use AtomicInteger for the counters
836 try {
837 recordSemaphore.acquire();
838 try {
Taras Lemkin41785912018-03-26 14:52:58 +0000839 dhcpRelayCountersStore.incrementCounter(gCount, Dhcp6HandlerUtil.getMsgTypeStr(leafMsgType));
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000840 } finally {
841 // calling release() after a successful acquire()
842 recordSemaphore.release();
843 }
844 } catch (InterruptedException e) {
Ray Milkeyba547f92018-02-01 15:22:31 -0800845 Thread.currentThread().interrupt();
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000846 }
Charles Chancd1783f2019-02-01 11:31:50 -0800847 */
Kalhee Kim495c9b22017-11-07 16:32:09 +0000848 }
849
850 /**
851 * add host or route and update dhcp relay record.
852 *
853 * @param directConnFlag flag to show that packet is from directly connected client
854 * @param location client side connect point
855 * @param dhcp6Relay the dhcp6 payload
856 * @param embeddedDhcp6 the dhcp6 payload within relay
857 * @param srcMac client gw/host macAddress
858 * @param clientInterface client interface
pier4bdea352019-08-23 10:45:40 -0700859 * @param vlanIdInUse vlanid encoded in the interface id Option
Kalhee Kim495c9b22017-11-07 16:32:09 +0000860 */
861 private void addHostOrRoute(boolean directConnFlag, ConnectPoint location, DHCP6 dhcp6Relay,
pier4bdea352019-08-23 10:45:40 -0700862 DHCP6 embeddedDhcp6, MacAddress srcMac, Interface clientInterface,
863 VlanId vlanIdInUse) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000864 log.debug("addHostOrRoute entered.");
pier4bdea352019-08-23 10:45:40 -0700865 VlanId vlanId;
866 if (clientInterface.vlanTagged().isEmpty()) {
867 vlanId = clientInterface.vlan();
868 } else {
869 // might be multiple vlan in same interface
870 vlanId = vlanIdInUse;
871 }
872 if (vlanId == null) {
873 vlanId = VlanId.NONE;
874 }
875
Taras Lemkin41785912018-03-26 14:52:58 +0000876 Boolean isMsgReply = Dhcp6HandlerUtil.isDhcp6Reply(dhcp6Relay);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000877 MacAddress leafClientMac;
878 Byte leafMsgType;
879
Charles Chanc7f63b62018-08-13 10:32:03 -0700880 Dhcp6ClientIdOption clientIdOption = Dhcp6HandlerUtil.extractClientId(directConnFlag, embeddedDhcp6);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000881 if (clientIdOption != null) {
882 log.debug("CLIENTID option found {}", clientIdOption);
883 if ((clientIdOption.getDuid().getDuidType() == Dhcp6Duid.DuidType.DUID_LLT) ||
884 (clientIdOption.getDuid().getDuidType() == Dhcp6Duid.DuidType.DUID_LL)) {
885 leafClientMac = MacAddress.valueOf(clientIdOption.getDuid().getLinkLayerAddress());
886 } else {
887 log.warn("Link-Layer Address not supported in CLIENTID option. No DhcpRelay Record created.");
Charles Chancd1783f2019-02-01 11:31:50 -0800888 //dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_LINKLOCAL_FAIL);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000889 return;
890 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000891 } else {
892 log.warn("CLIENTID option NOT found. No DhcpRelay Record created.");
Charles Chancd1783f2019-02-01 11:31:50 -0800893 //dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_CLIENTID_FAIL);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000894 return;
895 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000896 HostId leafHostId = HostId.hostId(leafClientMac, vlanId);
897 DhcpRecord record = dhcpRelayStore.getDhcpRecord(leafHostId).orElse(null);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000898 if (record == null) {
899 record = new DhcpRecord(HostId.hostId(leafClientMac, vlanId));
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000900 } else {
901 record = record.clone();
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000902 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000903
904 IpAddressInfo ipInfo;
905 PdPrefixInfo pdInfo = null;
906 if (directConnFlag) {
907 // Add to host store if it connect to network directly
908 ipInfo = extractIpAddress(embeddedDhcp6);
909 if (ipInfo != null) {
910 if (isMsgReply) {
911 Set<IpAddress> ips = Sets.newHashSet(ipInfo.ip6Address);
912 HostId hostId = HostId.hostId(srcMac, vlanId);
913 Host host = hostService.getHost(hostId);
914 HostLocation hostLocation = new HostLocation(clientInterface.connectPoint(),
915 System.currentTimeMillis());
916 Set<HostLocation> hostLocations = Sets.newHashSet(hostLocation);
917 if (host != null) {
918 // Dual homing support:
919 // if host exists, use old locations and new location
920 hostLocations.addAll(host.locations());
921 }
922 HostDescription desc = new DefaultHostDescription(srcMac, vlanId, hostLocations, ips,
923 false);
924 log.debug("adding Host for directly connected.");
925 log.debug("client mac {} client vlan {} hostlocation {}",
926 HexString.toHexString(srcMac.toBytes(), ":"), vlanId, hostLocation.toString());
927 // Replace the ip when dhcp server give the host new ip address
928 providerService.hostDetected(hostId, desc, false);
929 }
930 } else {
931 log.warn("ipAddress not found. Do not add Host {} for directly connected.",
932 HostId.hostId(srcMac, vlanId).toString());
933 }
934 leafMsgType = embeddedDhcp6.getMsgType();
935 } else {
936 // Add to route store if it does not connect to network directly
937 // pick out the first link-local ip address
938 IpAddress nextHopIp = getFirstIpByHost(directConnFlag, srcMac, vlanId);
939 if (nextHopIp == null) {
940 log.warn("Can't find link-local IP address of gateway mac {} vlanId {}", srcMac, vlanId);
Charles Chancd1783f2019-02-01 11:31:50 -0800941 //dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_LINKLOCAL_GW);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000942 return;
943 }
944
Taras Lemkin41785912018-03-26 14:52:58 +0000945 DHCP6 leafDhcp = Dhcp6HandlerUtil.getDhcp6Leaf(embeddedDhcp6);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000946 ipInfo = extractIpAddress(leafDhcp);
947 if (ipInfo == null) {
948 log.debug("ip is null");
949 } else {
950 if (isMsgReply) {
Kalhee Kim54a97c92018-04-02 21:23:46 +0000951 Route routeForIP = new Route(Route.Source.DHCP, ipInfo.ip6Address.toIpPrefix(), nextHopIp);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000952 log.debug("adding Route of 128 address for indirectly connected.");
Daniel Ginsburgc1d6aaf2018-06-09 01:43:59 +0300953 routeStore.replaceRoute(routeForIP);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000954 }
955 }
956
957 pdInfo = extractPrefix(leafDhcp);
958 if (pdInfo == null) {
959 log.debug("ipPrefix is null ");
960 } else {
961 if (isMsgReply) {
Kalhee Kim54a97c92018-04-02 21:23:46 +0000962 Route routeForPrefix = new Route(Route.Source.DHCP, pdInfo.pdPrefix, nextHopIp);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000963 log.debug("adding Route of PD for indirectly connected.");
Daniel Ginsburgc1d6aaf2018-06-09 01:43:59 +0300964 routeStore.replaceRoute(routeForPrefix);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000965 if (this.dhcpFpmEnabled) {
966 FpmRecord fpmRecord = new FpmRecord(pdInfo.pdPrefix, nextHopIp, FpmRecord.Type.DHCP_RELAY);
967 dhcpFpmPrefixStore.addFpmRecord(pdInfo.pdPrefix, fpmRecord);
968 }
969 }
970 }
971 leafMsgType = leafDhcp.getMsgType();
972 }
973 if (leafMsgType == DHCP6.MsgType.RELEASE.value() ||
974 (leafMsgType == DHCP6.MsgType.REPLY.value()) && ipInfo == null) {
975 log.warn("DHCP6 RELEASE/REPLY(null ip) from Server. MsgType {}", leafMsgType);
976 //return;
977 }
978
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000979 record.addLocation(new HostLocation(location, System.currentTimeMillis()));
Kalhee Kim2f07c602017-11-15 18:57:53 +0000980
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000981 if (leafMsgType == DHCP6.MsgType.REPLY.value()) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000982 if (ipInfo != null) {
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000983 log.debug("IP6 address is being stored into dhcp-relay store.");
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800984 log.debug("Client IP6 address {}", HexString.toHexString(ipInfo.ip6Address.toOctets(), ":"));
Kalhee Kim495c9b22017-11-07 16:32:09 +0000985 record.ip6Address(ipInfo.ip6Address);
Kalhee Kim2f07c602017-11-15 18:57:53 +0000986 record.updateAddrPrefTime(ipInfo.prefTime);
987 record.updateLastIp6Update();
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800988 } else {
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000989 log.debug("IP6 address is not returned from server. Maybe only PD is returned.");
990 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000991 if (pdInfo != null) {
992 log.debug("IP6 PD address {}",
993 HexString.toHexString(pdInfo.pdPrefix.address().toOctets(), ":"));
Kalhee Kim2f07c602017-11-15 18:57:53 +0000994 record.pdPrefix(pdInfo.pdPrefix);
995 record.updatePdPrefTime(pdInfo.prefTime);
996 record.updateLastPdUpdate();
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000997 } else {
998 log.debug("IP6 PD address is not returned from server. Maybe only IPAddress is returned.");
999 }
1000 }
Kalhee Kim2f07c602017-11-15 18:57:53 +00001001
Taras Lemkin41785912018-03-26 14:52:58 +00001002 record.getV6Counters().incrementCounter(Dhcp6HandlerUtil.getMsgTypeStr(leafMsgType));
Kalhee Kim495c9b22017-11-07 16:32:09 +00001003 record.ip6Status(DHCP6.MsgType.getType(leafMsgType));
Kalhee Kimea4b6c22017-11-09 14:38:37 +00001004 record.setDirectlyConnected(directConnFlag);
1005 record.updateLastSeen();
Kalhee Kim495c9b22017-11-07 16:32:09 +00001006 dhcpRelayStore.updateDhcpRecord(leafHostId, record);
Charles Chancd1783f2019-02-01 11:31:50 -08001007 /*
Kalhee Kimd94ceea2017-11-29 19:03:02 +00001008 // TODO Use AtomicInteger for the counters
1009 try {
1010 recordSemaphore.acquire();
1011 try {
Taras Lemkin41785912018-03-26 14:52:58 +00001012 dhcpRelayCountersStore.incrementCounter(gCount, Dhcp6HandlerUtil.getMsgTypeStr(leafMsgType));
Kalhee Kimd94ceea2017-11-29 19:03:02 +00001013 } finally {
1014 // calling release() after a successful acquire()
1015 recordSemaphore.release();
1016 }
1017 } catch (InterruptedException e) {
Ray Milkeyba547f92018-02-01 15:22:31 -08001018 Thread.currentThread().interrupt();
Kalhee Kimd94ceea2017-11-29 19:03:02 +00001019 }
Charles Chancd1783f2019-02-01 11:31:50 -08001020 */
Kalhee Kim45fede42017-09-05 19:05:06 +00001021 }
1022
Taras Lemkin41785912018-03-26 14:52:58 +00001023 private List<InternalPacket> processDhcp6ForwardOnly(PacketContext context,
1024 Ethernet clientPacket,
1025 Set<Interface> clientInterfaces,
1026 DHCP6 dhcpPacket) {
1027 ConnectPoint inPort = context.inPacket().receivedFrom();
1028 log.trace("Got DHCPv6 on port {}", inPort);
1029 boolean directConnFlag = Dhcp6HandlerUtil.directlyConnected(dhcpPacket);
1030
1031 List<InternalPacket> internalPackets = new ArrayList<>();
1032 List<DhcpServerInfo> serverInfoList = findValidServerInfo(directConnFlag);
1033
1034 for (DhcpServerInfo dhcpServer : serverInfoList) {
Charles Chanc7f63b62018-08-13 10:32:03 -07001035 Interface serverInterface = getServerInterface(dhcpServer);
1036 if (serverInterface == null) {
1037 log.warn("Can't get server interface, ignore");
1038 continue;
1039 }
1040
Taras Lemkin41785912018-03-26 14:52:58 +00001041 Ethernet newPacket = Dhcp6HandlerUtil.buildDhcp6PacketFromClient(context,
Charles Chanc7f63b62018-08-13 10:32:03 -07001042 clientPacket, clientInterfaces, dhcpServer, serverInterface);
Taras Lemkin41785912018-03-26 14:52:58 +00001043 log.trace("Built packet for server {} : {}", dhcpServer, newPacket);
1044 internalPackets.add(InternalPacket.internalPacket(newPacket,
1045 dhcpServer.getDhcpServerConnectPoint().get()));
1046 }
1047
1048 return internalPackets;
1049 }
1050
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001051 private List<InternalPacket> processLQ6PacketFromClient(PacketContext context,
1052 Ethernet clientPacket,
1053 Set<Interface> clientInterfaces,
1054 DHCP6 dhcp6Payload) {
1055 ConnectPoint inPort = context.inPacket().receivedFrom();
1056 log.info("Got LQ-REQUEST V6 on port {}", inPort);
1057 List<Dhcp6Option> lopt = dhcp6Payload.getOptions();
1058 log.info("Options list: {}", lopt);
1059 Dhcp6LeaseQueryOption lqoption = dhcp6Payload.getOptions()
1060 .stream()
1061 .filter(opt -> opt instanceof Dhcp6LeaseQueryOption)
1062 .map(pld -> (Dhcp6LeaseQueryOption) pld)
1063 .findFirst()
1064 .orElse(null);
1065
1066 if (lqoption == null) {
1067 // Can't find dhcp payload
1068 log.warn("Can't find dhcp6 lease query message - aborting");
1069 return null;
1070 } else {
1071 log.info("dhcp6 lqv6 options found: {}", lqoption);
1072 }
1073 log.warn("LQv6 for " + lqoption.linkAddress.toString() + " comes from " + inPort.toString());
1074 Ethernet packet = context.inPacket().parsed();
1075 Ip6Address clientAddress = lqoption.linkAddress;
1076 IPv6 ipv6Packet = (IPv6) packet.getPayload();
1077 Ip6Address nextHopIp = findNextHopIp6FromRelayStore(clientAddress);
1078
1079 // 1. only if there is a route to remove - remove it
1080 if (nextHopIp != null) {
Kalhee Kim54a97c92018-04-02 21:23:46 +00001081 Route routeForIP6 = new Route(Route.Source.DHCP, clientAddress.toIpPrefix(), nextHopIp);
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001082 log.debug("Removing route of Client " + clientAddress +
1083 " for indirectly connected - next hop ip6 " + nextHopIp);
1084 routeStore.removeRoute(routeForIP6);
1085 }
1086
1087 // 2. note the potential NH this packet came from in case it's a known lease
1088 // this NH will then be used to build the route
1089 MacAddress potentialNH = packet.getSourceMAC();
1090 VlanId vlanId = VlanId.vlanId(packet.getVlanID());
1091 setPotentialNextHopForIp6InRelayStore(clientAddress, vlanId, potentialNH);
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001092 // 3. route this LQ6 to all relevant servers
1093 IPv6 clientIpv6 = (IPv6) clientPacket.getPayload();
1094 UDP clientUdp = (UDP) clientIpv6.getPayload();
1095 DHCP6 clientDhcp6 = (DHCP6) clientUdp.getPayload();
1096
Taras Lemkin41785912018-03-26 14:52:58 +00001097 boolean directConnFlag = Dhcp6HandlerUtil.directlyConnected(clientDhcp6);
Harshada Chaundkar6c54f202019-05-06 20:16:13 +00001098 boolean serverFound = false;
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001099 List<InternalPacket> internalPackets = new ArrayList<>();
1100 List<DhcpServerInfo> serverInfoList = findValidServerInfo(directConnFlag);
1101 List<DhcpServerInfo> copyServerInfoList = new ArrayList<DhcpServerInfo>(serverInfoList);
1102
1103 for (DhcpServerInfo serverInfo : copyServerInfoList) {
Taras Lemkin41785912018-03-26 14:52:58 +00001104 if (!Dhcp6HandlerUtil.checkDhcpServerConnPt(directConnFlag, serverInfo)) {
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001105 log.warn("Can't get server connect point, ignore");
1106 continue;
1107 }
1108 DhcpServerInfo newServerInfo = getHostInfoForServerInfo(serverInfo, serverInfoList);
1109 if (newServerInfo == null) {
Harshada Chaundkar6c54f202019-05-06 20:16:13 +00001110 log.debug("Can't get server interface with host info resolved, ignore serverInfo {} serverInfoList {}",
1111 serverInfo, serverInfoList);
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001112 continue;
1113 }
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001114 Interface serverInterface = getServerInterface(newServerInfo);
1115 if (serverInterface == null) {
Harshada Chaundkar6c54f202019-05-06 20:16:13 +00001116 log.debug("Can't get server interface, ignore for serverInfo {}, serverInfoList {}",
1117 serverInfo, serverInfoList);
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001118 continue;
1119 }
Harshada Chaundkar6c54f202019-05-06 20:16:13 +00001120
1121 serverFound = true;
1122 log.debug("Server Info Found {}", serverInfo.getDhcpConnectMac());
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001123 Ethernet etherRouted = (Ethernet) clientPacket.clone();
1124 MacAddress macFacingServer = serverInterface.mac();
1125 if (macFacingServer == null) {
1126 log.warn("No MAC address for server Interface {}", serverInterface);
1127 return null;
1128 }
1129 etherRouted.setSourceMACAddress(macFacingServer);
1130 etherRouted.setDestinationMACAddress(newServerInfo.getDhcpConnectMac().get());
1131 InternalPacket internalPacket =
Taras Lemkin41785912018-03-26 14:52:58 +00001132 InternalPacket.internalPacket(etherRouted,
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001133 serverInfo.getDhcpServerConnectPoint().get());
1134 internalPackets.add(internalPacket);
1135 log.debug("Sending LQ to DHCP server {}", newServerInfo.getDhcpServerIp6());
1136 }
Harshada Chaundkar6c54f202019-05-06 20:16:13 +00001137 if (!serverFound) {
1138 log.warn("ProcessDhcp6PacketFromClient No Server Found");
1139 }
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001140 log.debug("num of client packets to send is{}", internalPackets.size());
1141
1142 return internalPackets;
1143 }
1144
Kalhee Kim45fede42017-09-05 19:05:06 +00001145 /**
Kalhee Kim495c9b22017-11-07 16:32:09 +00001146 * build the DHCP6 solicit/request packet with gatewayip.
Kalhee Kim45fede42017-09-05 19:05:06 +00001147 *
1148 * @param context packet context
1149 * @param clientPacket client ethernet packet
1150 * @param clientInterfaces set of client side interfaces
1151 */
Kalhee Kim495c9b22017-11-07 16:32:09 +00001152 private List<InternalPacket> processDhcp6PacketFromClient(PacketContext context,
1153 Ethernet clientPacket,
1154 Set<Interface> clientInterfaces) {
Yi Tseng3bd57ac2017-11-29 14:39:18 -08001155 ConnectPoint receivedFrom = context.inPacket().receivedFrom();
Taras Lemkin41785912018-03-26 14:52:58 +00001156 Ip6Address relayAgentIp = Dhcp6HandlerUtil.getRelayAgentIPv6Address(clientInterfaces);
Yi Tseng3bd57ac2017-11-29 14:39:18 -08001157 MacAddress relayAgentMac = clientInterfaces.iterator().next().mac();
1158 if (relayAgentIp == null || relayAgentMac == null) {
1159 log.warn("Missing DHCP relay agent interface Ipv6 addr config for "
Kalhee Kim495c9b22017-11-07 16:32:09 +00001160 + "packet from client on port: {}. Aborting packet processing",
1161 clientInterfaces.iterator().next().connectPoint());
Charles Chancd1783f2019-02-01 11:31:50 -08001162 //dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_CLIENT_INTF_MAC);
Charles Chan239492c2018-01-22 13:27:28 -08001163 return Lists.newArrayList();
Kalhee Kim121ba922017-11-01 17:56:44 +00001164 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001165
Kalhee Kim495c9b22017-11-07 16:32:09 +00001166 IPv6 clientIpv6 = (IPv6) clientPacket.getPayload();
1167 UDP clientUdp = (UDP) clientIpv6.getPayload();
1168 DHCP6 clientDhcp6 = (DHCP6) clientUdp.getPayload();
1169
Taras Lemkin41785912018-03-26 14:52:58 +00001170 boolean directConnFlag = Dhcp6HandlerUtil.directlyConnected(clientDhcp6);
Charles Chana990ce92017-10-30 10:22:50 -07001171
Kalhee Kim121ba922017-11-01 17:56:44 +00001172 ConnectPoint clientConnectionPoint = context.inPacket().receivedFrom();
1173 VlanId vlanIdInUse = VlanId.vlanId(clientPacket.getVlanID());
1174 Interface clientInterface = interfaceService.getInterfacesByPort(clientConnectionPoint)
Taras Lemkin41785912018-03-26 14:52:58 +00001175 .stream().filter(iface -> Dhcp6HandlerUtil.interfaceContainsVlan(iface, vlanIdInUse))
Kalhee Kim495c9b22017-11-07 16:32:09 +00001176 .findFirst()
1177 .orElse(null);
Kalhee Kim45fede42017-09-05 19:05:06 +00001178
Kalhee Kim495c9b22017-11-07 16:32:09 +00001179 List<InternalPacket> internalPackets = new ArrayList<>();
1180 List<DhcpServerInfo> serverInfoList = findValidServerInfo(directConnFlag);
1181 List<DhcpServerInfo> copyServerInfoList = new ArrayList<DhcpServerInfo>(serverInfoList);
Kalhee Kim45b24182017-10-18 18:30:23 +00001182
Kalhee Kim495c9b22017-11-07 16:32:09 +00001183 for (DhcpServerInfo serverInfo : copyServerInfoList) {
Taras Lemkin41785912018-03-26 14:52:58 +00001184 if (!Dhcp6HandlerUtil.checkDhcpServerConnPt(directConnFlag, serverInfo)) {
Kalhee Kim495c9b22017-11-07 16:32:09 +00001185 log.warn("Can't get server connect point, ignore");
1186 continue;
1187 }
1188 DhcpServerInfo newServerInfo = getHostInfoForServerInfo(serverInfo, serverInfoList);
1189 if (newServerInfo == null) {
1190 log.warn("Can't get server interface with host info resolved, ignore");
1191 continue;
1192 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001193
Kalhee Kim495c9b22017-11-07 16:32:09 +00001194 Interface serverInterface = getServerInterface(newServerInfo);
1195 if (serverInterface == null) {
1196 log.warn("Can't get server interface, ignore");
1197 continue;
1198 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +00001199
Taras Lemkin41785912018-03-26 14:52:58 +00001200 Ethernet etherReply = Dhcp6HandlerUtil.buildDhcp6PacketFromClient(context, clientPacket,
Kalhee Kim495c9b22017-11-07 16:32:09 +00001201 clientInterfaces, newServerInfo, serverInterface);
1202 removeHostOrRoute(directConnFlag, clientConnectionPoint, clientDhcp6, clientPacket,
1203 clientIpv6, clientInterface);
Kalhee Kimea4b6c22017-11-09 14:38:37 +00001204
Taras Lemkin41785912018-03-26 14:52:58 +00001205 InternalPacket internalPacket = InternalPacket.internalPacket(etherReply,
Kalhee Kim495c9b22017-11-07 16:32:09 +00001206 serverInfo.getDhcpServerConnectPoint().get());
1207 internalPackets.add(internalPacket);
1208 }
1209 log.debug("num of client packets to send is{}", internalPackets.size());
Kalhee Kimea4b6c22017-11-09 14:38:37 +00001210
Kalhee Kim495c9b22017-11-07 16:32:09 +00001211 return internalPackets;
1212 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001213
1214 /**
Kalhee Kim45fede42017-09-05 19:05:06 +00001215 * process the DHCP6 relay-reply packet from dhcp server.
1216 *
1217 * @param context packet context
1218 * @param receivedPacket server ethernet packet
1219 * @param recevingInterfaces set of server side interfaces
Kalhee Kim495c9b22017-11-07 16:32:09 +00001220 * @return internalPacket toward client
Kalhee Kim45fede42017-09-05 19:05:06 +00001221 */
1222 private InternalPacket processDhcp6PacketFromServer(PacketContext context,
1223 Ethernet receivedPacket, Set<Interface> recevingInterfaces) {
Kalhee Kim45fede42017-09-05 19:05:06 +00001224 // get dhcp6 header.
Ray Milkeyf0c47612017-09-28 11:29:38 -07001225 Ethernet etherReply = receivedPacket.duplicate();
Kalhee Kim45fede42017-09-05 19:05:06 +00001226 IPv6 ipv6Packet = (IPv6) etherReply.getPayload();
1227 UDP udpPacket = (UDP) ipv6Packet.getPayload();
1228 DHCP6 dhcp6Relay = (DHCP6) udpPacket.getPayload();
Taras Lemkin41785912018-03-26 14:52:58 +00001229 Boolean directConnFlag = Dhcp6HandlerUtil.directlyConnected(dhcp6Relay);
Kalhee Kim45fede42017-09-05 19:05:06 +00001230
Kalhee Kim495c9b22017-11-07 16:32:09 +00001231 DHCP6 embeddedDhcp6 = dhcp6Relay.getOptions().stream()
1232 .filter(opt -> opt instanceof Dhcp6RelayOption)
1233 .map(BasePacket::getPayload)
1234 .map(pld -> (DHCP6) pld)
1235 .findFirst()
1236 .orElse(null);
Kalhee Kimd21029f2017-09-26 20:21:53 +00001237 ConnectPoint inPort = context.inPacket().receivedFrom();
Kalhee Kim495c9b22017-11-07 16:32:09 +00001238 DhcpServerInfo foundServerInfo = findServerInfoFromServer(directConnFlag, inPort);
Kalhee Kimd21029f2017-09-26 20:21:53 +00001239
Kalhee Kim495c9b22017-11-07 16:32:09 +00001240 if (foundServerInfo == null) {
jayakumarthazhath27aa7722018-09-25 15:56:51 -04001241 log.warn("Cannot find server info for {} server, inPort {}",
1242 directConnFlag ? "direct" : "indirect", inPort);
Charles Chancd1783f2019-02-01 11:31:50 -08001243 //dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_SERVER_INFO);
Kalhee Kimd21029f2017-09-26 20:21:53 +00001244 return null;
Kalhee Kim495c9b22017-11-07 16:32:09 +00001245 } else {
Taras Lemkin41785912018-03-26 14:52:58 +00001246 if (Dhcp6HandlerUtil.isServerIpEmpty(foundServerInfo)) {
Kalhee Kim495c9b22017-11-07 16:32:09 +00001247 log.warn("Cannot find server info's ipaddress");
Charles Chancd1783f2019-02-01 11:31:50 -08001248 //dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_SERVER_IP6ADDR);
Kalhee Kim495c9b22017-11-07 16:32:09 +00001249 return null;
1250 }
Kalhee Kimd21029f2017-09-26 20:21:53 +00001251 }
1252
Kalhee Kim45fede42017-09-05 19:05:06 +00001253 Dhcp6InterfaceIdOption interfaceIdOption = dhcp6Relay.getOptions().stream()
1254 .filter(opt -> opt instanceof Dhcp6InterfaceIdOption)
1255 .map(opt -> (Dhcp6InterfaceIdOption) opt)
1256 .findFirst()
1257 .orElse(null);
Kalhee Kim45fede42017-09-05 19:05:06 +00001258 if (interfaceIdOption == null) {
1259 log.warn("Interface Id option is not present, abort packet...");
Charles Chancd1783f2019-02-01 11:31:50 -08001260 //dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.OPTION_MISSING_FAIL);
Kalhee Kim45fede42017-09-05 19:05:06 +00001261 return null;
1262 }
1263
1264 MacAddress peerMac = interfaceIdOption.getMacAddress();
1265 String clientConnectionPointStr = new String(interfaceIdOption.getInPort());
Kalhee Kim45fede42017-09-05 19:05:06 +00001266 ConnectPoint clientConnectionPoint = ConnectPoint.deviceConnectPoint(clientConnectionPointStr);
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +00001267 VlanId vlanIdInUse = VlanId.vlanId(interfaceIdOption.getVlanId());
Harshada Chaundkar6c54f202019-05-06 20:16:13 +00001268
1269 log.debug("processDhcp6PacketFromServer Interface Id Mac {}, port{}, vlan {}",
1270 peerMac, clientConnectionPointStr, vlanIdInUse);
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +00001271 Interface clientInterface = interfaceService.getInterfacesByPort(clientConnectionPoint)
Taras Lemkin41785912018-03-26 14:52:58 +00001272 .stream().filter(iface -> Dhcp6HandlerUtil.interfaceContainsVlan(iface, vlanIdInUse))
Kalhee Kim495c9b22017-11-07 16:32:09 +00001273 .findFirst().orElse(null);
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +00001274 if (clientInterface == null) {
1275 log.warn("Cannot get client interface for from packet, abort... vlan {}", vlanIdInUse.toString());
Charles Chancd1783f2019-02-01 11:31:50 -08001276 //dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_MATCHING_INTF);
Kalhee Kim45fede42017-09-05 19:05:06 +00001277 return null;
1278 }
Kalhee Kim44fbda12018-04-03 21:08:18 +00001279 etherReply.setVlanID(vlanIdInUse.toShort());
1280
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +00001281 MacAddress relayAgentMac = clientInterface.mac();
Kalhee Kim45fede42017-09-05 19:05:06 +00001282 if (relayAgentMac == null) {
Yi Tseng68ef26b2017-12-18 17:10:00 -08001283 log.warn("Can not get client interface mac, abort packet..");
Charles Chancd1783f2019-02-01 11:31:50 -08001284 //dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_CLIENT_INTF_MAC);
Kalhee Kim45fede42017-09-05 19:05:06 +00001285 return null;
1286 }
1287 etherReply.setSourceMACAddress(relayAgentMac);
1288
1289 // find destMac
Yi Tseng68ef26b2017-12-18 17:10:00 -08001290 MacAddress clientMac;
Yi Tsengaa417a62017-09-08 17:22:51 -07001291 Ip6Address peerAddress = Ip6Address.valueOf(dhcp6Relay.getPeerAddress());
1292 Set<Host> clients = hostService.getHostsByIp(peerAddress);
Kalhee Kim45fede42017-09-05 19:05:06 +00001293 if (clients.isEmpty()) {
Yi Tseng68ef26b2017-12-18 17:10:00 -08001294 log.trace("There's no host found for this address {}",
Kalhee Kim45fede42017-09-05 19:05:06 +00001295 HexString.toHexString(dhcp6Relay.getPeerAddress(), ":"));
Yi Tseng68ef26b2017-12-18 17:10:00 -08001296 log.trace("Let's look up interfaceId {}", HexString.toHexString(peerMac.toBytes(), ":"));
Kalhee Kim45fede42017-09-05 19:05:06 +00001297 clientMac = peerMac;
1298 } else {
1299 clientMac = clients.iterator().next().mac();
1300 if (clientMac == null) {
1301 log.warn("No client mac address found, abort packet...");
Charles Chancd1783f2019-02-01 11:31:50 -08001302 //dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_CLIENT_INTF_MAC);
Kalhee Kim45fede42017-09-05 19:05:06 +00001303 return null;
1304 }
Yi Tseng68ef26b2017-12-18 17:10:00 -08001305 log.trace("Client mac address found from getHostByIp");
Kalhee Kim45fede42017-09-05 19:05:06 +00001306 }
1307 etherReply.setDestinationMACAddress(clientMac);
Kalhee Kim45fede42017-09-05 19:05:06 +00001308 // ip header
1309 ipv6Packet.setSourceAddress(dhcp6Relay.getLinkAddress());
1310 ipv6Packet.setDestinationAddress(dhcp6Relay.getPeerAddress());
1311 // udp header
1312 udpPacket.setSourcePort(UDP.DHCP_V6_SERVER_PORT);
1313 if (directConnFlag) {
1314 udpPacket.setDestinationPort(UDP.DHCP_V6_CLIENT_PORT);
1315 } else {
1316 udpPacket.setDestinationPort(UDP.DHCP_V6_SERVER_PORT);
1317 }
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001318
Kalhee Kim45fede42017-09-05 19:05:06 +00001319
Taras Lemkin41785912018-03-26 14:52:58 +00001320 boolean hostOrRouteAllowed = learnRouteFromLeasequery ||
1321 Dhcp6HandlerUtil.getDhcp6LeafMessageType(dhcp6Relay) != MsgType.LEASEQUERY_REPLY;
1322 log.debug("Can add host or route: {}", hostOrRouteAllowed);
1323
1324 if (hostOrRouteAllowed) {
1325 // add host or route
1326 addHostOrRoute(directConnFlag, clientConnectionPoint, dhcp6Relay, embeddedDhcp6,
pier4bdea352019-08-23 10:45:40 -07001327 clientMac, clientInterface, vlanIdInUse);
Taras Lemkin41785912018-03-26 14:52:58 +00001328 }
1329
Kalhee Kim45fede42017-09-05 19:05:06 +00001330 udpPacket.setPayload(embeddedDhcp6);
1331 udpPacket.resetChecksum();
1332 ipv6Packet.setPayload(udpPacket);
1333 etherReply.setPayload(ipv6Packet);
Taras Lemkin41785912018-03-26 14:52:58 +00001334 return InternalPacket.internalPacket(etherReply, clientConnectionPoint);
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001335 }
Yi Tseng483ac6f2017-08-02 15:03:31 -07001336
1337 @Override
Kalhee Kimba366062017-11-07 16:32:09 +00001338 public void setDhcpFpmEnabled(Boolean enabled) {
1339 dhcpFpmEnabled = enabled;
1340 }
1341
1342 @Override
Yi Tseng483ac6f2017-08-02 15:03:31 -07001343 public void setDefaultDhcpServerConfigs(Collection<DhcpServerConfig> configs) {
Kalhee Kim495c9b22017-11-07 16:32:09 +00001344 log.debug("setDefaultDhcpServerConfigs is called.");
Yi Tseng919b2df2017-09-07 16:22:51 -07001345 setDhcpServerConfigs(configs, defaultServerInfoList);
Yi Tseng919b2df2017-09-07 16:22:51 -07001346 }
1347
1348 @Override
1349 public void setIndirectDhcpServerConfigs(Collection<DhcpServerConfig> configs) {
Kalhee Kim495c9b22017-11-07 16:32:09 +00001350 log.debug("setIndirectDhcpServerConfigs is called.");
Yi Tseng919b2df2017-09-07 16:22:51 -07001351 setDhcpServerConfigs(configs, indirectServerInfoList);
Yi Tseng919b2df2017-09-07 16:22:51 -07001352 }
1353
1354 public void setDhcpServerConfigs(Collection<DhcpServerConfig> configs, List<DhcpServerInfo> serverInfoList) {
Kalhee Kim495c9b22017-11-07 16:32:09 +00001355 log.debug("config size {}.", configs.size());
1356
Kalhee Kim45fede42017-09-05 19:05:06 +00001357 if (configs.size() == 0) {
1358 // no config to update
1359 return;
1360 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001361 // TODO: currently we pick up first DHCP server config.
1362 // Will use other server configs in the future for HA.
Kalhee Kim495c9b22017-11-07 16:32:09 +00001363 Boolean isConfigValid = false;
1364 for (DhcpServerConfig serverConfig : configs) {
1365 if (serverConfig.getDhcpServerIp6().isPresent()) {
1366 isConfigValid = true;
1367 break;
1368 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001369 }
Kalhee Kim495c9b22017-11-07 16:32:09 +00001370 if (!isConfigValid) {
1371 log.warn("No IP V6 server address found.");
1372 return; // No IP V6 address found
1373 }
1374 for (DhcpServerInfo oldServerInfo : serverInfoList) {
Yi Tseng919b2df2017-09-07 16:22:51 -07001375 // stop monitoring gateway or server
1376 oldServerInfo.getDhcpGatewayIp6().ifPresent(gatewayIp -> {
1377 hostService.stopMonitoringIp(gatewayIp);
1378 });
1379 oldServerInfo.getDhcpServerIp6().ifPresent(serverIp -> {
1380 hostService.stopMonitoringIp(serverIp);
Yi Tseng525ff402017-10-23 19:39:39 -07001381 cancelDhcpPacket(serverIp);
Yi Tseng919b2df2017-09-07 16:22:51 -07001382 });
1383 }
Kalhee Kim495c9b22017-11-07 16:32:09 +00001384 serverInfoList.clear();
1385 for (DhcpServerConfig serverConfig : configs) {
1386 // Create new server info according to the config
1387 DhcpServerInfo newServerInfo = new DhcpServerInfo(serverConfig,
1388 DhcpServerInfo.Version.DHCP_V6);
1389 checkState(newServerInfo.getDhcpServerConnectPoint().isPresent(),
1390 "Connect point not exists");
1391 checkState(newServerInfo.getDhcpServerIp6().isPresent(),
1392 "IP of DHCP server not exists");
Yi Tseng919b2df2017-09-07 16:22:51 -07001393
Kalhee Kim495c9b22017-11-07 16:32:09 +00001394 log.debug("DHCP server connect point: {}", newServerInfo.getDhcpServerConnectPoint().orElse(null));
1395 log.debug("DHCP server IP: {}", newServerInfo.getDhcpServerIp6().orElse(null));
Yi Tseng919b2df2017-09-07 16:22:51 -07001396
Kalhee Kim495c9b22017-11-07 16:32:09 +00001397 Ip6Address serverIp = newServerInfo.getDhcpServerIp6().get();
1398 Ip6Address ipToProbe;
1399 if (newServerInfo.getDhcpGatewayIp6().isPresent()) {
1400 ipToProbe = newServerInfo.getDhcpGatewayIp6().get();
1401 } else {
1402 ipToProbe = newServerInfo.getDhcpServerIp6().orElse(null);
1403 }
1404 String hostToProbe = newServerInfo.getDhcpGatewayIp6()
1405 .map(ip -> "gateway").orElse("server");
Yi Tseng919b2df2017-09-07 16:22:51 -07001406
Kalhee Kim495c9b22017-11-07 16:32:09 +00001407 log.warn("Probing to resolve {} IP {}", hostToProbe, ipToProbe);
1408 hostService.startMonitoringIp(ipToProbe);
1409
1410 Set<Host> hosts = hostService.getHostsByIp(ipToProbe);
1411 if (!hosts.isEmpty()) {
1412 Host host = hosts.iterator().next();
1413 newServerInfo.setDhcpConnectVlan(host.vlan());
1414 newServerInfo.setDhcpConnectMac(host.mac());
1415 log.warn("Host found host {}", host);
1416
1417 } else {
1418 log.warn("No host found host ip {}", ipToProbe);
1419 }
1420 // Add new server info
1421 synchronized (this) {
1422 serverInfoList.add(newServerInfo);
1423 }
1424 if (!hosts.isEmpty()) {
1425 requestDhcpPacket(serverIp);
1426 }
Yi Tseng919b2df2017-09-07 16:22:51 -07001427 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001428 }
1429
1430 class InternalHostListener implements HostListener {
1431 @Override
1432 public void event(HostEvent event) {
1433 switch (event.type()) {
1434 case HOST_ADDED:
1435 case HOST_UPDATED:
Charles Chanc7f63b62018-08-13 10:32:03 -07001436 case HOST_MOVED:
Jordan Halterman6328db72018-04-10 13:34:50 -04001437 log.trace("Scheduled host event {}", event);
1438 hostEventExecutor.execute(() -> hostUpdated(event.subject()));
Kalhee Kim45fede42017-09-05 19:05:06 +00001439 break;
1440 case HOST_REMOVED:
Jordan Halterman6328db72018-04-10 13:34:50 -04001441 log.trace("Scheduled host event {}", event);
1442 hostEventExecutor.execute(() -> hostRemoved(event.subject()));
Kalhee Kim45fede42017-09-05 19:05:06 +00001443 break;
Kalhee Kim45fede42017-09-05 19:05:06 +00001444 default:
1445 break;
1446 }
1447 }
1448 }
1449
1450 /**
Kalhee Kim45fede42017-09-05 19:05:06 +00001451 * Handle host updated.
1452 * If the host is DHCP server or gateway, update connect mac and vlan.
1453 *
1454 * @param host the host
1455 */
1456 private void hostUpdated(Host host) {
Yi Tseng525ff402017-10-23 19:39:39 -07001457 hostUpdated(host, defaultServerInfoList);
1458 hostUpdated(host, indirectServerInfoList);
Kalhee Kim45fede42017-09-05 19:05:06 +00001459 }
1460
Yi Tseng525ff402017-10-23 19:39:39 -07001461 private void hostUpdated(Host host, List<DhcpServerInfo> serverInfoList) {
jjosep004c41357882018-08-21 09:01:10 -04001462 serverInfoList.stream().forEach(serverInfo -> {
Yi Tseng525ff402017-10-23 19:39:39 -07001463 Ip6Address serverIp = serverInfo.getDhcpServerIp6().orElse(null);
jjosep004c41357882018-08-21 09:01:10 -04001464 Ip6Address targetIp = serverInfo.getDhcpGatewayIp6().orElse(null);
Yi Tseng525ff402017-10-23 19:39:39 -07001465
1466 if (targetIp == null) {
1467 targetIp = serverIp;
1468 }
Yi Tseng525ff402017-10-23 19:39:39 -07001469 if (targetIp != null) {
1470 if (host.ipAddresses().contains(targetIp)) {
1471 serverInfo.setDhcpConnectMac(host.mac());
1472 serverInfo.setDhcpConnectVlan(host.vlan());
1473 requestDhcpPacket(serverIp);
1474 }
1475 }
jjosep004c41357882018-08-21 09:01:10 -04001476 });
Yi Tseng525ff402017-10-23 19:39:39 -07001477 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001478 /**
1479 * Handle host removed.
1480 * If the host is DHCP server or gateway, unset connect mac and vlan.
1481 *
1482 * @param host the host
1483 */
1484 private void hostRemoved(Host host) {
Yi Tseng525ff402017-10-23 19:39:39 -07001485 hostRemoved(host, defaultServerInfoList);
1486 hostRemoved(host, indirectServerInfoList);
Yi Tseng919b2df2017-09-07 16:22:51 -07001487 }
1488
Yi Tseng525ff402017-10-23 19:39:39 -07001489 private void hostRemoved(Host host, List<DhcpServerInfo> serverInfoList) {
jjosep004c41357882018-08-21 09:01:10 -04001490 serverInfoList.stream().forEach(serverInfo -> {
Yi Tseng525ff402017-10-23 19:39:39 -07001491 Ip6Address serverIp = serverInfo.getDhcpServerIp6().orElse(null);
jjosep004c41357882018-08-21 09:01:10 -04001492 Ip6Address targetIp = serverInfo.getDhcpGatewayIp6().orElse(null);
Yi Tseng525ff402017-10-23 19:39:39 -07001493
1494 if (targetIp == null) {
1495 targetIp = serverIp;
1496 }
Yi Tseng525ff402017-10-23 19:39:39 -07001497 if (targetIp != null) {
1498 if (host.ipAddresses().contains(targetIp)) {
1499 serverInfo.setDhcpConnectVlan(null);
1500 serverInfo.setDhcpConnectMac(null);
1501 cancelDhcpPacket(serverIp);
1502 }
1503 }
jjosep004c41357882018-08-21 09:01:10 -04001504 });
Yi Tseng525ff402017-10-23 19:39:39 -07001505 }
1506
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001507 /**
1508 * Gets Interface facing to the server for default host.
1509 *
1510 * @return the Interface facing to the server; null if not found
1511 */
1512 private Interface getServerInterface() {
1513 DhcpServerInfo serverInfo;
1514 ConnectPoint dhcpServerConnectPoint;
1515 VlanId dhcpConnectVlan;
1516
1517 if (!defaultServerInfoList.isEmpty()) {
1518 serverInfo = defaultServerInfoList.get(0);
1519 dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1520 dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1521 } else {
1522 return null;
1523 }
1524 if (dhcpServerConnectPoint == null || dhcpConnectVlan == null) {
1525 log.info("Default DHCP server {} not resolve yet", serverInfo.getDhcpGatewayIp6());
1526 return null;
1527 }
1528 return interfaceService.getInterfacesByPort(dhcpServerConnectPoint)
1529 .stream()
Taras Lemkin41785912018-03-26 14:52:58 +00001530 .filter(iface -> Dhcp6HandlerUtil.interfaceContainsVlan(iface, dhcpConnectVlan))
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001531 .findFirst()
1532 .orElse(null);
1533 }
1534
1535 /**
1536 * Gets Interface facing to the server for indirect hosts.
1537 * Use default server Interface if indirect server not configured.
1538 *
1539 * @return the Interface facing to the server; null if not found
1540 */
1541 private Interface getIndirectServerInterface() {
1542 DhcpServerInfo serverInfo;
1543
1544 ConnectPoint indirectDhcpServerConnectPoint;
1545 VlanId indirectDhcpConnectVlan;
1546
1547 if (!indirectServerInfoList.isEmpty()) {
1548 serverInfo = indirectServerInfoList.get(0);
1549 indirectDhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1550 indirectDhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1551 } else {
1552 return getServerInterface();
1553 }
1554 if (indirectDhcpServerConnectPoint == null || indirectDhcpConnectVlan == null) {
1555 log.info("Indirect DHCP server {} not resolve yet", serverInfo.getDhcpGatewayIp6());
1556 return null;
1557 }
1558 return interfaceService.getInterfacesByPort(indirectDhcpServerConnectPoint)
1559 .stream()
Taras Lemkin41785912018-03-26 14:52:58 +00001560 .filter(iface -> Dhcp6HandlerUtil.interfaceContainsVlan(iface, indirectDhcpConnectVlan))
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001561 .findFirst()
1562 .orElse(null);
1563 }
1564
Kalhee Kim45b24182017-10-18 18:30:23 +00001565 /**
Kalhee Kim495c9b22017-11-07 16:32:09 +00001566 * Checks if serverInfo's host info (mac and vlan) is filled in; if not, fills in.
1567 *
1568 * @param serverInfo server information
1569 * @return newServerInfo if host info can be either found or filled in.
1570 */
1571 private DhcpServerInfo getHostInfoForServerInfo(DhcpServerInfo serverInfo, List<DhcpServerInfo> sererInfoList) {
1572 DhcpServerInfo newServerInfo = null;
1573 MacAddress dhcpServerConnectMac = serverInfo.getDhcpConnectMac().orElse(null);
1574 VlanId dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1575 ConnectPoint dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1576
1577 if (dhcpServerConnectMac != null && dhcpConnectVlan != null) {
1578 newServerInfo = serverInfo;
Charles Chan750c9992018-02-26 10:44:49 -08001579 log.debug("DHCP server {} host info found. ConnectPt{} Mac {} vlan {}", serverInfo.getDhcpServerIp6(),
Kalhee Kim495c9b22017-11-07 16:32:09 +00001580 dhcpServerConnectPoint, dhcpServerConnectMac, dhcpConnectVlan);
1581 } else {
Harshada Chaundkar6c54f202019-05-06 20:16:13 +00001582 log.debug("DHCP server {} not resolve yet connectPt {} mac {} vlan {}", serverInfo.getDhcpServerIp6(),
Kalhee Kim495c9b22017-11-07 16:32:09 +00001583 dhcpServerConnectPoint, dhcpServerConnectMac, dhcpConnectVlan);
1584
1585 Ip6Address ipToProbe;
1586 if (serverInfo.getDhcpGatewayIp6().isPresent()) {
1587 ipToProbe = serverInfo.getDhcpGatewayIp6().get();
1588 } else {
1589 ipToProbe = serverInfo.getDhcpServerIp6().orElse(null);
1590 }
1591 String hostToProbe = serverInfo.getDhcpGatewayIp6()
1592 .map(ip -> "gateway").orElse("server");
1593
Harshada Chaundkar6c54f202019-05-06 20:16:13 +00001594 log.debug("Dynamically probing to resolve {} IP {}", hostToProbe, ipToProbe);
Kalhee Kim495c9b22017-11-07 16:32:09 +00001595 hostService.startMonitoringIp(ipToProbe);
1596
1597 Set<Host> hosts = hostService.getHostsByIp(ipToProbe);
1598 if (!hosts.isEmpty()) {
1599 int serverInfoIndex = sererInfoList.indexOf(serverInfo);
1600 Host host = hosts.iterator().next();
1601 serverInfo.setDhcpConnectVlan(host.vlan());
1602 serverInfo.setDhcpConnectMac(host.mac());
1603 // replace the serverInfo in the list
1604 sererInfoList.set(serverInfoIndex, serverInfo);
1605 newServerInfo = serverInfo;
1606 log.warn("Dynamically host found host {}", host);
1607 } else {
Harshada Chaundkar6c54f202019-05-06 20:16:13 +00001608 log.debug("No host found host ip {} dynamically", ipToProbe);
Kalhee Kim495c9b22017-11-07 16:32:09 +00001609 }
1610 }
1611 return newServerInfo;
1612 }
1613
1614 /**
Kalhee Kim45b24182017-10-18 18:30:23 +00001615 * Gets Interface facing to the server for default host.
1616 *
Kalhee Kim495c9b22017-11-07 16:32:09 +00001617 * @param serverInfo server information
Kalhee Kim45b24182017-10-18 18:30:23 +00001618 * @return the Interface facing to the server; null if not found
1619 */
Kalhee Kim495c9b22017-11-07 16:32:09 +00001620 private Interface getServerInterface(DhcpServerInfo serverInfo) {
1621 Interface serverInterface = null;
Yi Tseng25bfe372017-11-03 16:27:32 -07001622
Kalhee Kim495c9b22017-11-07 16:32:09 +00001623 ConnectPoint dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1624 VlanId dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1625
1626 if (dhcpServerConnectPoint != null && dhcpConnectVlan != null) {
1627 serverInterface = interfaceService.getInterfacesByPort(dhcpServerConnectPoint)
1628 .stream()
Taras Lemkin41785912018-03-26 14:52:58 +00001629 .filter(iface -> Dhcp6HandlerUtil.interfaceContainsVlan(iface, dhcpConnectVlan))
Kalhee Kim495c9b22017-11-07 16:32:09 +00001630 .findFirst()
1631 .orElse(null);
Yi Tseng25bfe372017-11-03 16:27:32 -07001632 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +00001633 log.warn("DHCP server {} not resolve yet connectPoint {} vlan {}", serverInfo.getDhcpServerIp6(),
1634 dhcpServerConnectPoint, dhcpConnectVlan);
Kalhee Kim45b24182017-10-18 18:30:23 +00001635 }
Kalhee Kim495c9b22017-11-07 16:32:09 +00001636
1637 return serverInterface;
Kalhee Kim45b24182017-10-18 18:30:23 +00001638 }
1639
Yi Tseng525ff402017-10-23 19:39:39 -07001640 private void requestDhcpPacket(Ip6Address serverIp) {
1641 requestServerDhcpPacket(serverIp);
1642 requestClientDhcpPacket(serverIp);
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001643 requestServerLQPacket(serverIp);
Yi Tseng525ff402017-10-23 19:39:39 -07001644 }
1645
1646 private void cancelDhcpPacket(Ip6Address serverIp) {
1647 cancelServerDhcpPacket(serverIp);
1648 cancelClientDhcpPacket(serverIp);
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001649 cancelServerLQPacket(serverIp);
Yi Tseng525ff402017-10-23 19:39:39 -07001650 }
1651
1652 private void cancelServerDhcpPacket(Ip6Address serverIp) {
1653 TrafficSelector serverSelector =
1654 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1655 .matchIPv6Src(serverIp.toIpPrefix())
1656 .build();
1657 packetService.cancelPackets(serverSelector,
1658 PacketPriority.CONTROL,
1659 appId);
1660 }
1661
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001662 private void cancelServerLQPacket(Ip6Address serverIp) {
1663 TrafficSelector serverSelector =
1664 DefaultTrafficSelector.builder(LEASE_QUERY_RESPONSE_SELECTOR)
1665 .matchIPv6Src(serverIp.toIpPrefix())
1666 .build();
1667 packetService.cancelPackets(serverSelector,
1668 PacketPriority.CONTROL,
1669 appId);
1670 }
1671
Yi Tseng525ff402017-10-23 19:39:39 -07001672 private void requestServerDhcpPacket(Ip6Address serverIp) {
1673 TrafficSelector serverSelector =
1674 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1675 .matchIPv6Src(serverIp.toIpPrefix())
1676 .build();
1677 packetService.requestPackets(serverSelector,
1678 PacketPriority.CONTROL,
1679 appId);
1680 }
1681
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001682 private void requestServerLQPacket(Ip6Address serverIp) {
1683 TrafficSelector serverSelector =
1684 DefaultTrafficSelector.builder(LEASE_QUERY_RESPONSE_SELECTOR)
1685 .matchIPv6Src(serverIp.toIpPrefix())
1686 .build();
1687 packetService.requestPackets(serverSelector,
1688 PacketPriority.CONTROL,
1689 appId);
1690 }
1691
Yi Tseng525ff402017-10-23 19:39:39 -07001692 private void cancelClientDhcpPacket(Ip6Address serverIp) {
1693 // Packet comes from relay
1694 TrafficSelector indirectClientSelector =
1695 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1696 .matchIPv6Dst(serverIp.toIpPrefix())
1697 .build();
1698 packetService.cancelPackets(indirectClientSelector,
1699 PacketPriority.CONTROL,
1700 appId);
Yi Tseng41dde702017-11-02 15:21:10 -07001701 indirectClientSelector =
1702 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1703 .matchIPv6Dst(Ip6Address.ALL_DHCP_RELAY_AGENTS_AND_SERVERS.toIpPrefix())
1704 .build();
1705 packetService.cancelPackets(indirectClientSelector,
1706 PacketPriority.CONTROL,
1707 appId);
1708 indirectClientSelector =
1709 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1710 .matchIPv6Dst(Ip6Address.ALL_DHCP_SERVERS.toIpPrefix())
1711 .build();
1712 packetService.cancelPackets(indirectClientSelector,
1713 PacketPriority.CONTROL,
1714 appId);
Yi Tseng525ff402017-10-23 19:39:39 -07001715
1716 // Packet comes from client
1717 packetService.cancelPackets(CLIENT_SERVER_SELECTOR,
1718 PacketPriority.CONTROL,
1719 appId);
1720 }
1721
1722 private void requestClientDhcpPacket(Ip6Address serverIp) {
1723 // Packet comes from relay
1724 TrafficSelector indirectClientSelector =
1725 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1726 .matchIPv6Dst(serverIp.toIpPrefix())
1727 .build();
1728 packetService.requestPackets(indirectClientSelector,
1729 PacketPriority.CONTROL,
1730 appId);
Yi Tseng41dde702017-11-02 15:21:10 -07001731 indirectClientSelector =
1732 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1733 .matchIPv6Dst(Ip6Address.ALL_DHCP_RELAY_AGENTS_AND_SERVERS.toIpPrefix())
1734 .build();
1735 packetService.requestPackets(indirectClientSelector,
1736 PacketPriority.CONTROL,
1737 appId);
1738 indirectClientSelector =
1739 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1740 .matchIPv6Dst(Ip6Address.ALL_DHCP_SERVERS.toIpPrefix())
1741 .build();
1742 packetService.requestPackets(indirectClientSelector,
1743 PacketPriority.CONTROL,
1744 appId);
Yi Tseng525ff402017-10-23 19:39:39 -07001745
1746 // Packet comes from client
1747 packetService.requestPackets(CLIENT_SERVER_SELECTOR,
1748 PacketPriority.CONTROL,
1749 appId);
1750 }
1751
1752 /**
1753 * Process the ignore rules.
1754 *
1755 * @param deviceId the device id
1756 * @param vlanId the vlan to be ignored
1757 * @param op the operation, ADD to install; REMOVE to uninstall rules
1758 */
1759 private void processIgnoreVlanRule(DeviceId deviceId, VlanId vlanId, Objective.Operation op) {
Yi Tseng525ff402017-10-23 19:39:39 -07001760 AtomicInteger installedCount = new AtomicInteger(DHCP_SELECTORS.size());
1761 DHCP_SELECTORS.forEach(trafficSelector -> {
1762 TrafficSelector selector = DefaultTrafficSelector.builder(trafficSelector)
1763 .matchVlanId(vlanId)
1764 .build();
1765
1766 ForwardingObjective.Builder builder = DefaultForwardingObjective.builder()
1767 .withFlag(ForwardingObjective.Flag.VERSATILE)
1768 .withSelector(selector)
1769 .withPriority(IGNORE_CONTROL_PRIORITY)
Yi Tsengdbabeed2017-11-29 10:49:20 -08001770 .withTreatment(DefaultTrafficTreatment.emptyTreatment())
Yi Tseng525ff402017-10-23 19:39:39 -07001771 .fromApp(appId);
1772
1773
1774 ObjectiveContext objectiveContext = new ObjectiveContext() {
1775 @Override
1776 public void onSuccess(Objective objective) {
1777 log.info("Ignore rule {} (Vlan id {}, device {}, selector {})",
1778 op, vlanId, deviceId, selector);
1779 int countDown = installedCount.decrementAndGet();
1780 if (countDown != 0) {
1781 return;
1782 }
1783 switch (op) {
1784 case ADD:
1785 ignoredVlans.put(deviceId, vlanId);
1786 break;
1787 case REMOVE:
1788 ignoredVlans.remove(deviceId, vlanId);
1789 break;
1790 default:
1791 log.warn("Unsupported objective operation {}", op);
1792 break;
1793 }
1794 }
1795
1796 @Override
1797 public void onError(Objective objective, ObjectiveError error) {
1798 log.warn("Can't {} ignore rule (vlan id {}, selector {}, device {}) due to {}",
1799 op, vlanId, selector, deviceId, error);
1800 }
1801 };
1802
1803 ForwardingObjective fwd;
1804 switch (op) {
1805 case ADD:
1806 fwd = builder.add(objectiveContext);
1807 break;
1808 case REMOVE:
1809 fwd = builder.remove(objectiveContext);
1810 break;
1811 default:
1812 log.warn("Unsupported objective operation {}", op);
1813 return;
1814 }
1815
1816 Device device = deviceService.getDevice(deviceId);
1817 if (device == null || !device.is(Pipeliner.class)) {
1818 log.warn("Device {} is not available now, wait until device is available", deviceId);
1819 return;
1820 }
1821 flowObjectiveService.apply(deviceId, fwd);
1822 });
1823 }
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001824
Kalhee Kim121ba922017-11-01 17:56:44 +00001825 /**
1826 * Find first ipaddress for a given Host info i.e. mac and vlan.
1827 *
1828 * @param clientMac client mac
1829 * @param vlanId packet's vlan
1830 * @return next-hop link-local ipaddress for a given host
1831 */
Kalhee Kim495c9b22017-11-07 16:32:09 +00001832 private IpAddress getFirstIpByHost(Boolean directConnFlag, MacAddress clientMac, VlanId vlanId) {
Kalhee Kim121ba922017-11-01 17:56:44 +00001833 IpAddress nextHopIp;
1834 // pick out the first link-local ip address
1835 HostId gwHostId = HostId.hostId(clientMac, vlanId);
1836 Host gwHost = hostService.getHost(gwHostId);
1837 if (gwHost == null) {
1838 log.warn("Can't find gateway host for hostId {}", gwHostId);
1839 return null;
1840 }
Kalhee Kim495c9b22017-11-07 16:32:09 +00001841 if (directConnFlag) {
1842 nextHopIp = gwHost.ipAddresses()
1843 .stream()
1844 .filter(IpAddress::isIp6)
1845 .map(IpAddress::getIp6Address)
1846 .findFirst()
1847 .orElse(null);
1848 } else {
1849 nextHopIp = gwHost.ipAddresses()
1850 .stream()
1851 .filter(IpAddress::isIp6)
1852 .filter(ip6 -> ip6.isLinkLocal())
1853 .map(IpAddress::getIp6Address)
1854 .findFirst()
1855 .orElse(null);
1856 }
Kalhee Kim121ba922017-11-01 17:56:44 +00001857 return nextHopIp;
1858 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +00001859
Kalhee Kim495c9b22017-11-07 16:32:09 +00001860 private List<DhcpServerInfo> findValidServerInfo(boolean directConnFlag) {
1861 List<DhcpServerInfo> validServerInfo;
1862
1863 if (directConnFlag || indirectServerInfoList.isEmpty()) {
1864 validServerInfo = new ArrayList<DhcpServerInfo>(defaultServerInfoList);
1865 } else {
1866 validServerInfo = new ArrayList<DhcpServerInfo>(indirectServerInfoList);
1867 }
1868 return validServerInfo;
1869 }
1870
1871 private DhcpServerInfo findServerInfoFromServer(boolean directConnFlag, ConnectPoint inPort) {
1872 List<DhcpServerInfo> validServerInfoList = findValidServerInfo(directConnFlag);
1873 DhcpServerInfo foundServerInfo = null;
1874 for (DhcpServerInfo serverInfo : validServerInfoList) {
1875 if (inPort.equals(serverInfo.getDhcpServerConnectPoint().get())) {
1876 foundServerInfo = serverInfo;
Charles Chan750c9992018-02-26 10:44:49 -08001877 log.debug("ServerInfo found for Rcv port {} Server Connect Point {} for {}",
Kalhee Kim495c9b22017-11-07 16:32:09 +00001878 inPort, serverInfo.getDhcpServerConnectPoint(), directConnFlag ? "direct" : "indirect");
1879 break;
Kalhee Kim495c9b22017-11-07 16:32:09 +00001880 }
1881 }
1882 return foundServerInfo;
1883 }
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001884
Kalhee Kim495c9b22017-11-07 16:32:09 +00001885 /**
1886 * Set the dhcp6 lease expiry poll interval value.
1887 *
1888 * @param val poll interval value in seconds
1889 */
Kalhee Kim2f07c602017-11-15 18:57:53 +00001890 @Override
Kalhee Kim495c9b22017-11-07 16:32:09 +00001891 public void setDhcp6PollInterval(int val) {
1892 dhcp6PollInterval = val;
1893 }
1894
Kalhee Kim2f07c602017-11-15 18:57:53 +00001895 /**
1896 * get the dhcp6 lease expiry poll interval value.
1897 * This is a private function
1898 * @return poll interval value in seconds
1899 */
1900 private int getDhcp6PollInterval() {
1901 return dhcp6PollInterval;
1902 }
1903
1904 /**
1905 * Find lease-expired ipaddresses and pd prefixes.
1906 * Removing host/route/fpm entries.
1907 */
1908 public void timeTick() {
1909 long currentTime = System.currentTimeMillis();
1910 Collection<DhcpRecord> records = dhcpRelayStore.getDhcpRecords();
1911
1912 log.debug("timeTick called currenttime {} records num {} ", currentTime, records.size());
1913
1914 records.forEach(record -> {
1915 boolean addrOrPdRemoved = false;
1916 DHCP6.MsgType ip6Status = record.ip6Status().orElse(null);
1917 if (ip6Status == null) {
1918 log.debug("record is not valid v6 record.");
1919 return;
1920 }
1921
1922 if ((currentTime - record.getLastIp6Update()) >
1923 ((record.addrPrefTime() + getDhcp6PollInterval() / 2) * 1000)) {
1924 // remove ipaddress from host/route table
1925 IpAddress ip = record.ip6Address().orElse(null);
1926 if (ip != null) {
1927 if (record.directlyConnected()) {
1928 providerService.removeIpFromHost(HostId.hostId(record.macAddress(),
1929 record.vlanId()), ip);
1930 } else {
1931 MacAddress gwMac = record.nextHop().orElse(null);
1932 if (gwMac == null) {
1933 log.warn("Can't find gateway mac address from record {} for ip6Addr", record);
1934 return;
1935 }
1936 IpAddress nextHopIp = getFirstIpByHost(record.directlyConnected(),
1937 gwMac,
1938 record.vlanId());
Kalhee Kim54a97c92018-04-02 21:23:46 +00001939 Route route = new Route(Route.Source.DHCP, ip.toIpPrefix(), nextHopIp);
Kalhee Kim2f07c602017-11-15 18:57:53 +00001940 routeStore.removeRoute(route);
1941 }
1942 record.updateAddrPrefTime(0);
1943 record.ip6Address(null);
1944 addrOrPdRemoved = true;
1945 dhcpRelayStore.updateDhcpRecord(HostId.hostId(record.macAddress(),
1946 record.vlanId()), record);
1947 log.warn("IP6 address is set to null. delta {} lastUpdate {} addrPrefTime {}",
1948 (currentTime - record.getLastIp6Update()), record.getLastIp6Update(),
1949 record.addrPrefTime());
1950 }
1951 }
1952 if ((currentTime - record.getLastPdUpdate()) >
1953 ((record.pdPrefTime() + getDhcp6PollInterval() / 2) * 1000)) {
1954 // remove PD from route/fpm table
1955 IpPrefix pdIpPrefix = record.pdPrefix().orElse(null);
1956 if (pdIpPrefix != null) {
1957 if (record.directlyConnected()) {
1958 providerService.removeIpFromHost(HostId.hostId(record.macAddress(), record.vlanId()),
1959 pdIpPrefix.address().getIp6Address());
1960 } else {
1961 MacAddress gwMac = record.nextHop().orElse(null);
1962 if (gwMac == null) {
1963 log.warn("Can't find gateway mac address from record {} for PD prefix", record);
1964 return;
1965 }
1966 IpAddress nextHopIp = getFirstIpByHost(record.directlyConnected(),
1967 gwMac,
1968 record.vlanId());
Kalhee Kim54a97c92018-04-02 21:23:46 +00001969 Route route = new Route(Route.Source.DHCP, pdIpPrefix, nextHopIp);
Kalhee Kim2f07c602017-11-15 18:57:53 +00001970 routeStore.removeRoute(route);
1971 if (this.dhcpFpmEnabled) {
1972 dhcpFpmPrefixStore.removeFpmRecord(pdIpPrefix);
1973 }
1974 }
1975 record.updatePdPrefTime(0);
1976 record.pdPrefix(null);
1977 addrOrPdRemoved = true;
1978 dhcpRelayStore.updateDhcpRecord(HostId.hostId(record.macAddress(),
1979 record.vlanId()), record);
1980 log.warn("PD prefix is set to null.delta {} pdPrefTime {}",
1981 (currentTime - record.getLastPdUpdate()), record.pdPrefTime());
1982 }
1983 }
1984 if (addrOrPdRemoved &&
1985 !record.ip6Address().isPresent() && !record.pdPrefix().isPresent()) {
1986 log.warn("ip6Status {} IP6 address and IP6 PD both are null. Remove record.", ip6Status);
1987 dhcpRelayStore.removeDhcpRecord(HostId.hostId(record.macAddress(), record.vlanId()));
1988 }
1989 }
1990 );
1991 }
1992}