blob: 0fed65ab2abf8c76490c99ef125c56b9fc3cead9 [file] [log] [blame]
Yi Tseng51301292017-07-28 13:02:59 -07001/*
2 * Copyright 2017-present Open Networking Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 */
Yi Tseng51301292017-07-28 13:02:59 -070017package org.onosproject.dhcprelay;
18
Yi Tseng525ff402017-10-23 19:39:39 -070019import com.google.common.collect.HashMultimap;
Yi Tseng919b2df2017-09-07 16:22:51 -070020import com.google.common.collect.Lists;
Yi Tseng525ff402017-10-23 19:39:39 -070021import com.google.common.collect.Multimap;
Kalhee Kim45fede42017-09-05 19:05:06 +000022import org.apache.felix.scr.annotations.Activate;
23import org.apache.felix.scr.annotations.Deactivate;
Kalhee Kim45fede42017-09-05 19:05:06 +000024import com.google.common.collect.Sets;
25import com.google.common.collect.ImmutableSet;
Yi Tseng51301292017-07-28 13:02:59 -070026import org.apache.felix.scr.annotations.Component;
27import org.apache.felix.scr.annotations.Property;
Kalhee Kim45fede42017-09-05 19:05:06 +000028import org.apache.felix.scr.annotations.Reference;
29import org.apache.felix.scr.annotations.ReferenceCardinality;
Yi Tseng51301292017-07-28 13:02:59 -070030import org.apache.felix.scr.annotations.Service;
31import org.onlab.packet.BasePacket;
Kalhee Kim45fede42017-09-05 19:05:06 +000032import org.onlab.packet.DHCP6;
33import org.onlab.packet.IPv6;
34import org.onlab.packet.Ethernet;
35import org.onlab.packet.Ip6Address;
Yi Tseng51301292017-07-28 13:02:59 -070036import org.onlab.packet.IpAddress;
Kalhee Kim45fede42017-09-05 19:05:06 +000037import org.onlab.packet.IpPrefix;
Yi Tseng51301292017-07-28 13:02:59 -070038import org.onlab.packet.MacAddress;
Yi Tseng525ff402017-10-23 19:39:39 -070039import org.onlab.packet.TpPort;
Kalhee Kim45fede42017-09-05 19:05:06 +000040import org.onlab.packet.UDP;
Yi Tseng51301292017-07-28 13:02:59 -070041import org.onlab.packet.VlanId;
Lior Assoulinea21c0ca2018-01-28 16:18:48 -080042import org.onlab.packet.dhcp.Dhcp6ClientDataOption;
43import org.onlab.packet.dhcp.Dhcp6LeaseQueryOption;
Kalhee Kim45fede42017-09-05 19:05:06 +000044import org.onlab.packet.dhcp.Dhcp6RelayOption;
45import org.onlab.packet.dhcp.Dhcp6InterfaceIdOption;
Lior Assoulinea21c0ca2018-01-28 16:18:48 -080046import org.onlab.packet.dhcp.Dhcp6Option;
Kalhee Kim45fede42017-09-05 19:05:06 +000047import org.onlab.packet.dhcp.Dhcp6IaNaOption;
48import org.onlab.packet.dhcp.Dhcp6IaTaOption;
49import org.onlab.packet.dhcp.Dhcp6IaPdOption;
50import org.onlab.packet.dhcp.Dhcp6IaAddressOption;
51import org.onlab.packet.dhcp.Dhcp6IaPrefixOption;
Kalhee Kimea4b6c22017-11-09 14:38:37 +000052import org.onlab.packet.dhcp.Dhcp6ClientIdOption;
53import org.onlab.packet.dhcp.Dhcp6Duid;
Kalhee Kim495c9b22017-11-07 16:32:09 +000054import org.onlab.packet.DHCP6.MsgType;
Kalhee Kim45fede42017-09-05 19:05:06 +000055import org.onlab.util.HexString;
Yi Tseng525ff402017-10-23 19:39:39 -070056import org.onosproject.core.ApplicationId;
57import org.onosproject.core.CoreService;
Yi Tseng51301292017-07-28 13:02:59 -070058import org.onosproject.dhcprelay.api.DhcpHandler;
Yi Tseng919b2df2017-09-07 16:22:51 -070059import org.onosproject.dhcprelay.api.DhcpServerInfo;
Yi Tseng525ff402017-10-23 19:39:39 -070060import org.onosproject.dhcprelay.config.IgnoreDhcpConfig;
Kalhee Kim45fede42017-09-05 19:05:06 +000061import org.onosproject.dhcprelay.store.DhcpRelayStore;
Kalhee Kimea4b6c22017-11-09 14:38:37 +000062import org.onosproject.dhcprelay.store.DhcpRecord;
Kalhee Kimba366062017-11-07 16:32:09 +000063import org.onosproject.dhcprelay.store.DhcpFpmPrefixStore;
Kalhee Kimd94ceea2017-11-29 19:03:02 +000064import org.onosproject.dhcprelay.store.DhcpRelayCounters;
65import org.onosproject.dhcprelay.store.DhcpRelayCountersStore;
Yi Tseng525ff402017-10-23 19:39:39 -070066import org.onosproject.net.Device;
67import org.onosproject.net.DeviceId;
Kalhee Kimba366062017-11-07 16:32:09 +000068import org.onosproject.routing.fpm.api.FpmRecord;
Yi Tseng525ff402017-10-23 19:39:39 -070069import org.onosproject.net.behaviour.Pipeliner;
70import org.onosproject.net.device.DeviceService;
71import org.onosproject.net.flow.DefaultTrafficSelector;
72import org.onosproject.net.flow.TrafficSelector;
73import org.onosproject.net.flowobjective.DefaultForwardingObjective;
74import org.onosproject.net.flowobjective.FlowObjectiveService;
75import org.onosproject.net.flowobjective.ForwardingObjective;
76import org.onosproject.net.flowobjective.Objective;
77import org.onosproject.net.flowobjective.ObjectiveContext;
78import org.onosproject.net.flowobjective.ObjectiveError;
Yi Tsengaa417a62017-09-08 17:22:51 -070079import org.onosproject.net.host.HostProvider;
80import org.onosproject.net.host.HostProviderRegistry;
81import org.onosproject.net.host.HostProviderService;
Kalhee Kim45fede42017-09-05 19:05:06 +000082import org.onosproject.net.host.HostService;
83import org.onosproject.net.host.DefaultHostDescription;
84import org.onosproject.net.host.HostDescription;
85import org.onosproject.net.host.InterfaceIpAddress;
86import org.onosproject.net.host.HostListener;
87import org.onosproject.net.host.HostEvent;
88import org.onosproject.net.intf.Interface;
89import org.onosproject.net.intf.InterfaceService;
Yi Tseng525ff402017-10-23 19:39:39 -070090import org.onosproject.net.packet.PacketPriority;
Yi Tsengaa417a62017-09-08 17:22:51 -070091import org.onosproject.net.provider.ProviderId;
Kalhee Kim45fede42017-09-05 19:05:06 +000092import org.onosproject.routeservice.Route;
93import org.onosproject.routeservice.RouteStore;
Yi Tseng483ac6f2017-08-02 15:03:31 -070094import org.onosproject.dhcprelay.config.DhcpServerConfig;
Yi Tseng51301292017-07-28 13:02:59 -070095import org.onosproject.net.ConnectPoint;
Kalhee Kim45fede42017-09-05 19:05:06 +000096import org.onosproject.net.Host;
97import org.onosproject.net.HostId;
98import org.onosproject.net.HostLocation;
99import org.onosproject.net.packet.DefaultOutboundPacket;
100import org.onosproject.net.packet.OutboundPacket;
Yi Tseng51301292017-07-28 13:02:59 -0700101import org.onosproject.net.packet.PacketContext;
Kalhee Kim45fede42017-09-05 19:05:06 +0000102import org.onosproject.net.packet.PacketService;
103import org.slf4j.Logger;
104import org.slf4j.LoggerFactory;
105import org.onosproject.net.flow.DefaultTrafficTreatment;
106import org.onosproject.net.flow.TrafficTreatment;
Kalhee Kim495c9b22017-11-07 16:32:09 +0000107import org.onosproject.dhcprelay.Dhcp6HandlerUtil.InternalPacket;
Kalhee Kim45fede42017-09-05 19:05:06 +0000108import java.nio.ByteBuffer;
109import java.util.List;
Yi Tseng483ac6f2017-08-02 15:03:31 -0700110import java.util.Collection;
Yi Tseng51301292017-07-28 13:02:59 -0700111import java.util.Optional;
Kalhee Kim45fede42017-09-05 19:05:06 +0000112import java.util.Set;
113import java.util.ArrayList;
Charles Chan909cff82018-03-05 13:14:02 -0800114import java.util.concurrent.CopyOnWriteArrayList;
Yi Tseng525ff402017-10-23 19:39:39 -0700115import java.util.concurrent.atomic.AtomicInteger;
Kalhee Kim45fede42017-09-05 19:05:06 +0000116import static com.google.common.base.Preconditions.checkNotNull;
117import static com.google.common.base.Preconditions.checkState;
Yi Tseng525ff402017-10-23 19:39:39 -0700118import static org.onosproject.net.flowobjective.Objective.Operation.ADD;
119import static org.onosproject.net.flowobjective.Objective.Operation.REMOVE;
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000120import java.util.concurrent.Semaphore;
Yi Tseng51301292017-07-28 13:02:59 -0700121
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800122
Yi Tseng51301292017-07-28 13:02:59 -0700123@Component
124@Service
125@Property(name = "version", value = "6")
Yi Tsengaa417a62017-09-08 17:22:51 -0700126public class Dhcp6HandlerImpl implements DhcpHandler, HostProvider {
Charles Chand988c282017-09-12 17:09:32 -0700127 public static final String DHCP_V6_RELAY_APP = "org.onosproject.Dhcp6HandlerImpl";
Saurav Das7c6dec12017-09-13 14:35:56 -0700128 public static final ProviderId PROVIDER_ID = new ProviderId("dhcp6", DHCP_V6_RELAY_APP);
Yi Tseng525ff402017-10-23 19:39:39 -0700129 private static final int IGNORE_CONTROL_PRIORITY = PacketPriority.CONTROL.priorityValue() + 1000;
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000130 private String gCount = "global";
Yi Tseng525ff402017-10-23 19:39:39 -0700131 private static final TrafficSelector CLIENT_SERVER_SELECTOR = DefaultTrafficSelector.builder()
132 .matchEthType(Ethernet.TYPE_IPV6)
133 .matchIPProtocol(IPv6.PROTOCOL_UDP)
134 .matchIPv6Src(IpPrefix.IPV6_LINK_LOCAL_PREFIX)
135 .matchIPv6Dst(Ip6Address.ALL_DHCP_RELAY_AGENTS_AND_SERVERS.toIpPrefix())
136 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_V6_CLIENT_PORT))
137 .matchUdpDst(TpPort.tpPort(UDP.DHCP_V6_SERVER_PORT))
138 .build();
139 private static final TrafficSelector SERVER_RELAY_SELECTOR = DefaultTrafficSelector.builder()
140 .matchEthType(Ethernet.TYPE_IPV6)
141 .matchIPProtocol(IPv6.PROTOCOL_UDP)
142 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_V6_SERVER_PORT))
143 .matchUdpDst(TpPort.tpPort(UDP.DHCP_V6_SERVER_PORT))
144 .build();
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800145 // lease query reply is from server to client (no relay in between) - so we need to
146 // catch that scenario also ..
147 private static final TrafficSelector LEASE_QUERY_RESPONSE_SELECTOR = DefaultTrafficSelector.builder()
148 .matchEthType(Ethernet.TYPE_IPV6)
149 .matchIPProtocol(IPv6.PROTOCOL_UDP)
150 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_V6_SERVER_PORT))
151 .matchUdpDst(TpPort.tpPort(UDP.DHCP_V6_CLIENT_PORT))
152 .build();
Yi Tseng525ff402017-10-23 19:39:39 -0700153 static final Set<TrafficSelector> DHCP_SELECTORS = ImmutableSet.of(
154 CLIENT_SERVER_SELECTOR,
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800155 SERVER_RELAY_SELECTOR,
156 LEASE_QUERY_RESPONSE_SELECTOR
Yi Tseng525ff402017-10-23 19:39:39 -0700157 );
Kalhee Kim45fede42017-09-05 19:05:06 +0000158 private static Logger log = LoggerFactory.getLogger(Dhcp6HandlerImpl.class);
Yi Tseng51301292017-07-28 13:02:59 -0700159
Kalhee Kim45fede42017-09-05 19:05:06 +0000160 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
161 protected DhcpRelayStore dhcpRelayStore;
Yi Tseng51301292017-07-28 13:02:59 -0700162
Kalhee Kim45fede42017-09-05 19:05:06 +0000163 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000164 protected DhcpRelayCountersStore dhcpRelayCountersStore;
165
166 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Kalhee Kim45fede42017-09-05 19:05:06 +0000167 protected PacketService packetService;
168
169 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Kalhee Kim45fede42017-09-05 19:05:06 +0000170 protected RouteStore routeStore;
171
172 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
173 protected InterfaceService interfaceService;
174
175 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
176 protected HostService hostService;
177
Yi Tsengaa417a62017-09-08 17:22:51 -0700178 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
179 protected HostProviderRegistry providerRegistry;
Kalhee Kim45fede42017-09-05 19:05:06 +0000180
Yi Tseng525ff402017-10-23 19:39:39 -0700181 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
182 protected CoreService coreService;
183
184 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Kalhee Kimba366062017-11-07 16:32:09 +0000185 protected DhcpFpmPrefixStore dhcpFpmPrefixStore;
186
187 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Yi Tseng525ff402017-10-23 19:39:39 -0700188 protected DeviceService deviceService;
189
190 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
191 protected FlowObjectiveService flowObjectiveService;
192
Yi Tsengaa417a62017-09-08 17:22:51 -0700193 protected HostProviderService providerService;
Yi Tseng525ff402017-10-23 19:39:39 -0700194 protected ApplicationId appId;
195 protected Multimap<DeviceId, VlanId> ignoredVlans = HashMultimap.create();
196 private InternalHostListener hostListener = new InternalHostListener();
Kalhee Kimba366062017-11-07 16:32:09 +0000197 private Boolean dhcpFpmEnabled = false;
Kalhee Kim495c9b22017-11-07 16:32:09 +0000198 private Dhcp6HandlerUtil dhcp6HandlerUtil = new Dhcp6HandlerUtil();
Charles Chan909cff82018-03-05 13:14:02 -0800199 private List<DhcpServerInfo> defaultServerInfoList = new CopyOnWriteArrayList<>();
200 private List<DhcpServerInfo> indirectServerInfoList = new CopyOnWriteArrayList<>();
Kalhee Kim495c9b22017-11-07 16:32:09 +0000201 private class IpAddressInfo {
202 Ip6Address ip6Address;
203 long prefTime;
204 }
205 private class PdPrefixInfo {
206 IpPrefix pdPrefix;
207 long prefTime;
208 }
209 protected int dhcp6PollInterval = 24 * 3600; // 24 hr period
210
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000211 // max 1 thread
212 static Semaphore recordSemaphore = new Semaphore(1);
Kalhee Kim45fede42017-09-05 19:05:06 +0000213
214 // CLIENT message types
215 public static final Set<Byte> MSG_TYPE_FROM_CLIENT =
216 ImmutableSet.of(DHCP6.MsgType.SOLICIT.value(),
217 DHCP6.MsgType.REQUEST.value(),
218 DHCP6.MsgType.REBIND.value(),
219 DHCP6.MsgType.RENEW.value(),
220 DHCP6.MsgType.RELEASE.value(),
221 DHCP6.MsgType.DECLINE.value(),
222 DHCP6.MsgType.CONFIRM.value(),
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800223 DHCP6.MsgType.RELAY_FORW.value(),
224 DHCP6.MsgType.LEASEQUERY.value());
Kalhee Kim45fede42017-09-05 19:05:06 +0000225 // SERVER message types
226 public static final Set<Byte> MSG_TYPE_FROM_SERVER =
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800227 ImmutableSet.of(DHCP6.MsgType.RELAY_REPL.value(),
228 DHCP6.MsgType.LEASEQUERY_REPLY.value());
Kalhee Kim45fede42017-09-05 19:05:06 +0000229
230 @Activate
231 protected void activate() {
Yi Tseng525ff402017-10-23 19:39:39 -0700232 appId = coreService.registerApplication(DHCP_V6_RELAY_APP);
Yi Tsengaa417a62017-09-08 17:22:51 -0700233 providerService = providerRegistry.register(this);
Kalhee Kim45fede42017-09-05 19:05:06 +0000234 hostService.addListener(hostListener);
Yi Tseng51301292017-07-28 13:02:59 -0700235 }
236
Kalhee Kim45fede42017-09-05 19:05:06 +0000237 @Deactivate
238 protected void deactivate() {
Yi Tsengaa417a62017-09-08 17:22:51 -0700239 providerRegistry.unregister(this);
Kalhee Kim45fede42017-09-05 19:05:06 +0000240 hostService.removeListener(hostListener);
Yi Tseng919b2df2017-09-07 16:22:51 -0700241 defaultServerInfoList.forEach(this::stopMonitoringIps);
242 defaultServerInfoList.clear();
243 indirectServerInfoList.forEach(this::stopMonitoringIps);
244 indirectServerInfoList.clear();
245 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000246
Yi Tseng919b2df2017-09-07 16:22:51 -0700247 private void stopMonitoringIps(DhcpServerInfo serverInfo) {
248 serverInfo.getDhcpGatewayIp6().ifPresent(gatewayIp -> {
249 hostService.stopMonitoringIp(gatewayIp);
250 });
251 serverInfo.getDhcpServerIp6().ifPresent(serverIp -> {
252 hostService.stopMonitoringIp(serverIp);
253 });
Yi Tseng51301292017-07-28 13:02:59 -0700254 }
255
Yi Tseng51301292017-07-28 13:02:59 -0700256 @Override
Yi Tseng919b2df2017-09-07 16:22:51 -0700257 public List<DhcpServerInfo> getDefaultDhcpServerInfoList() {
258 return defaultServerInfoList;
Yi Tseng51301292017-07-28 13:02:59 -0700259 }
260
261 @Override
Yi Tseng919b2df2017-09-07 16:22:51 -0700262 public List<DhcpServerInfo> getIndirectDhcpServerInfoList() {
263 return indirectServerInfoList;
Yi Tseng51301292017-07-28 13:02:59 -0700264 }
265
266 @Override
Yi Tseng525ff402017-10-23 19:39:39 -0700267 public void updateIgnoreVlanConfig(IgnoreDhcpConfig config) {
268 if (config == null) {
269 ignoredVlans.forEach(((deviceId, vlanId) -> {
270 processIgnoreVlanRule(deviceId, vlanId, REMOVE);
271 }));
272 return;
273 }
274 config.ignoredVlans().forEach((deviceId, vlanId) -> {
275 if (ignoredVlans.get(deviceId).contains(vlanId)) {
276 // don't need to process if it already ignored
277 return;
278 }
279 processIgnoreVlanRule(deviceId, vlanId, ADD);
280 });
Yi Tseng525ff402017-10-23 19:39:39 -0700281 ignoredVlans.forEach((deviceId, vlanId) -> {
282 if (!config.ignoredVlans().get(deviceId).contains(vlanId)) {
283 // not contains in new config, remove it
284 processIgnoreVlanRule(deviceId, vlanId, REMOVE);
285 }
286 });
287 }
288
289 @Override
Saurav Dasb805f1a2017-12-13 16:19:35 -0800290 public void removeIgnoreVlanState(IgnoreDhcpConfig config) {
291 if (config == null) {
292 ignoredVlans.clear();
293 return;
294 }
295 config.ignoredVlans().forEach((deviceId, vlanId) -> {
296 ignoredVlans.remove(deviceId, vlanId);
297 });
298 }
299
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800300 public DhcpRecord getDhcpRelayRecordFor(Ip6Address clientAddress) {
301
302 Collection<DhcpRecord> records = dhcpRelayStore.getDhcpRecords();
303 DhcpRecord dr = null;
304 for (DhcpRecord e:records) {
305 if (e.ip6Address().isPresent()) {
306 if (e.ip6Address().get().equals(clientAddress)) {
307 dr = e;
308 break;
309 }
310 }
311 }
312 return dr;
313 }
314
315 public MacAddress findNextHopMacForIp6FromRelayStore(Ip6Address clientAddress,
316 MacAddress clientMacAddress, VlanId vlanID) {
317
318 DhcpRecord dr = getDhcpRelayRecordFor(clientAddress);
319
320 if (dr != null) {
321 Optional<MacAddress> nextHopTempMac = dr.nextHopTemp();
322 if (nextHopTempMac.isPresent()) {
323 log.info("findNextHopForIp6FromRelayStore " + clientAddress + " got mac " + nextHopTempMac.toString());
324 return nextHopTempMac.get();
325 }
326 } else {
327 log.warn("findNextHopMacForIp6FromRelayStore could NOT find next hop for " + clientAddress);
328 return null;
329 }
330 return null;
331 }
332
333 public Ip6Address findNextHopIp6FromRelayStore(Ip6Address clientAddress) {
334
335 DhcpRecord dr = getDhcpRelayRecordFor(clientAddress);
336 if (dr != null) {
337 Optional<MacAddress> nextHopMac = dr.nextHop();
338 if (nextHopMac.isPresent()) {
339 // find the local ip6 from the host store
340 HostId gwHostId = HostId.hostId(nextHopMac.get(), dr.vlanId());
341 Host gwHost = hostService.getHost(gwHostId);
342 if (gwHost == null) {
343 log.warn("Can't find next hop host ID {}", gwHostId);
344 return null;
345 }
346 Ip6Address nextHopIp = gwHost.ipAddresses()
347 .stream()
348 .filter(IpAddress::isIp6)
349 .filter(IpAddress::isLinkLocal)
350 .map(IpAddress::getIp6Address)
351 .findFirst()
352 .orElse(null);
353
354 log.info("findNextHopIp6FromRelayStore " + clientAddress + " got mac " +
355 nextHopMac.toString() + " ip6 " + nextHopIp);
356 return nextHopIp;
357 }
358 } else {
359 log.warn("findNextHopIp6FromRelayStore could NOT find next hop for " + clientAddress);
360 return null;
361 }
362 return null;
363 }
364
365 private void setPotentialNextHopForIp6InRelayStore(Ip6Address clientAddress,
366 VlanId vlanId, MacAddress nh) {
367 DhcpRecord dr = getDhcpRelayRecordFor(clientAddress);
368 if (dr != null) {
369 dr.nextHopTemp(nh);
370 log.debug("LQ6 potential NH mac " + nh.toString() + " UPDATED in RelayRecord client " + clientAddress);
371 } else {
372 log.warn("LQ6 potential NH mac" + nh.toString() +
373 " NOT FOUND in RelayRecord for client - LQ rejected" + clientAddress);
374 }
375 }
376
377 public void handleLeaseQuery6ReplyMsg(PacketContext context, DHCP6 dhcp6Payload) {
378 ConnectPoint inPort = context.inPacket().receivedFrom();
379 log.info("Got LQV6-REPLY on port {}", inPort);
380 List<Dhcp6Option> lopt = dhcp6Payload.getOptions();
381 log.info("Options list: {}", lopt);
382 // find out if this lease is known is
383 Dhcp6ClientDataOption clientDataOption = dhcp6Payload.getOptions()
384 .stream()
385 .filter(opt -> opt instanceof Dhcp6ClientDataOption)
386 .map(pld -> (Dhcp6ClientDataOption) pld)
387 .findFirst()
388 .orElse(null);
389
390 if (clientDataOption == null) {
391 log.warn("clientDataOption option is not present, " +
392 "lease is UNKNOWN - not adding any new route...");
393 } else {
394 Dhcp6IaAddressOption aiAddressOption = clientDataOption.getOptions()
395 .stream()
396 .filter(opt -> opt instanceof Dhcp6IaAddressOption)
397 .map(pld -> (Dhcp6IaAddressOption) pld)
398 .findFirst()
399 .orElse(null);
400
401 Dhcp6ClientIdOption clientIdOption = clientDataOption.getOptions()
402 .stream()
403 .filter(opt -> opt instanceof Dhcp6ClientIdOption)
404 .map(pld -> (Dhcp6ClientIdOption) pld)
405 .findFirst()
406 .orElse(null);
407
408 if (aiAddressOption == null) {
409 log.warn("clientDataOption from DHCP server does not " +
410 "contains Dhcp6IaAddressOption for the client - giving up...");
411 } else {
412 Ip6Address clientAddress = aiAddressOption.getIp6Address();
413 MacAddress clientMacAddress = MacAddress.valueOf(clientIdOption.getDuid().getLinkLayerAddress());
414 Ethernet packet = context.inPacket().parsed();
415 VlanId vlanId = VlanId.vlanId(packet.getVlanID());
416 MacAddress potentialNextHopMac =
417 findNextHopMacForIp6FromRelayStore(clientAddress, clientMacAddress, vlanId);
418
419 if (potentialNextHopMac == null) {
420 log.warn("Can't find next hop host mac for client {} mac:{}/{}",
421 clientAddress, clientMacAddress, vlanId);
422 return;
423 } else {
424 log.info("Next hop mac for {}/{}/{} is {}", clientAddress,
425 clientMacAddress, vlanId, potentialNextHopMac.toString());
426 }
427 // search the next hop in the hosts store
428 HostId gwHostId = HostId.hostId(potentialNextHopMac, vlanId);
429 Host gwHost = hostService.getHost(gwHostId);
430 if (gwHost == null) {
431 log.warn("Can't find next hop host ID {}", gwHostId);
432 return;
433 }
434 Ip6Address nextHopIp = gwHost.ipAddresses()
435 .stream()
436 .filter(IpAddress::isIp6)
437 .filter(IpAddress::isLinkLocal)
438 .map(IpAddress::getIp6Address)
439 .findFirst()
440 .orElse(null);
441 if (nextHopIp == null) {
442 log.warn("Can't find IP6 address of next hop {}", gwHost);
443 return;
444 }
445 log.info("client " + clientAddress + " is known !");
446 Route routeForIP6 = new Route(Route.Source.STATIC, clientAddress.toIpPrefix(), nextHopIp);
447 log.debug("updating route of Client for indirectly connected.");
448 log.debug("client ip: " + clientAddress + ", next hop ip6: " + nextHopIp);
449 routeStore.updateRoute(routeForIP6);
450 }
451 }
452 }
453
Saurav Dasb805f1a2017-12-13 16:19:35 -0800454 @Override
Kalhee Kim45fede42017-09-05 19:05:06 +0000455 public void processDhcpPacket(PacketContext context, BasePacket payload) {
456 checkNotNull(payload, "DHCP6 payload can't be null");
457 checkState(payload instanceof DHCP6, "Payload is not a DHCP6");
458 DHCP6 dhcp6Payload = (DHCP6) payload;
459 Ethernet receivedPacket = context.inPacket().parsed();
460
461 if (!configured()) {
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800462 log.warn("Missing DHCP6 relay server config. " +
463 "Abort packet processing dhcp6 payload {}", dhcp6Payload);
Kalhee Kim45fede42017-09-05 19:05:06 +0000464 return;
465 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000466 byte msgTypeVal = dhcp6Payload.getMsgType();
467 MsgType msgType = DHCP6.MsgType.getType(msgTypeVal);
468 log.debug("msgType is {}", msgType);
Kalhee Kim45fede42017-09-05 19:05:06 +0000469
Kalhee Kim45fede42017-09-05 19:05:06 +0000470 ConnectPoint inPort = context.inPacket().receivedFrom();
Jonathan Hart8ca2bc02017-11-30 18:23:42 -0800471
Kalhee Kim495c9b22017-11-07 16:32:09 +0000472 if (inPort == null) {
473 log.warn("incomming ConnectPoint is null");
474 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000475 Set<Interface> receivingInterfaces = interfaceService.getInterfacesByPort(inPort);
476 //ignore the packets if dhcp client interface is not configured on onos.
477 if (receivingInterfaces.isEmpty()) {
478 log.warn("Virtual interface is not configured on {}", inPort);
479 return;
480 }
481
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800482 if (msgTypeVal == DHCP6.MsgType.LEASEQUERY.value()) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000483 List<InternalPacket> ethernetClientPacket =
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800484 processLQ6PacketFromClient(context, receivedPacket, receivingInterfaces, dhcp6Payload);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000485 for (InternalPacket internalPacket : ethernetClientPacket) {
486 forwardPacket(internalPacket);
Kalhee Kim45fede42017-09-05 19:05:06 +0000487 }
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800488 } else if (msgTypeVal == DHCP6.MsgType.LEASEQUERY_REPLY.value()) {
489
490 IPv6 clientIpv6 = (IPv6) receivedPacket.getPayload();
491 UDP clientUdp = (UDP) clientIpv6.getPayload();
492 DHCP6 clientDhcp6 = (DHCP6) clientUdp.getPayload();
493 Interface serverInterface = dhcp6HandlerUtil.directlyConnected(clientDhcp6) ?
494 getServerInterface() : getIndirectServerInterface();
Kalhee Kim45fede42017-09-05 19:05:06 +0000495 InternalPacket ethernetPacketReply =
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800496 dhcp6HandlerUtil.processLQ6PacketFromServer(
497 defaultServerInfoList, indirectServerInfoList,
498 serverInterface, interfaceService,
499 hostService,
500 context, receivedPacket, receivingInterfaces);
Kalhee Kim45fede42017-09-05 19:05:06 +0000501 if (ethernetPacketReply != null) {
502 forwardPacket(ethernetPacketReply);
503 }
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800504 handleLeaseQuery6ReplyMsg(context, dhcp6Payload);
Kalhee Kim45fede42017-09-05 19:05:06 +0000505 } else {
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800506 if (MSG_TYPE_FROM_CLIENT.contains(msgTypeVal)) {
507
508 List<InternalPacket> ethernetClientPacket =
509 processDhcp6PacketFromClient(context, receivedPacket, receivingInterfaces);
510 for (InternalPacket internalPacket : ethernetClientPacket) {
511 forwardPacket(internalPacket);
512 }
513 } else if (MSG_TYPE_FROM_SERVER.contains(msgTypeVal)) {
514 log.debug("calling processDhcp6PacketFromServer with RELAY_REPL {}", msgTypeVal);
515 InternalPacket ethernetPacketReply =
516 processDhcp6PacketFromServer(context, receivedPacket, receivingInterfaces);
517 if (ethernetPacketReply != null) {
518 forwardPacket(ethernetPacketReply);
519 }
520 } else {
521 log.warn("Not so fast, packet type {} not supported yet", msgTypeVal);
522 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000523 }
524 }
525
Kalhee Kim45fede42017-09-05 19:05:06 +0000526 /**
527 * Checks if this app has been configured.
528 *
529 * @return true if all information we need have been initialized
530 */
531 public boolean configured() {
Yi Tseng919b2df2017-09-07 16:22:51 -0700532 return !defaultServerInfoList.isEmpty();
Kalhee Kim45fede42017-09-05 19:05:06 +0000533 }
534
Yi Tsengaa417a62017-09-08 17:22:51 -0700535 @Override
536 public ProviderId id() {
Charles Chand988c282017-09-12 17:09:32 -0700537 return PROVIDER_ID;
Yi Tsengaa417a62017-09-08 17:22:51 -0700538 }
539
540 @Override
541 public void triggerProbe(Host host) {
542 // Do nothing here
543 }
544
Kalhee Kim45fede42017-09-05 19:05:06 +0000545 //forward the packet to ConnectPoint where the DHCP server is attached.
546 private void forwardPacket(InternalPacket packet) {
547 //send Packetout to dhcp server connectpoint.
548 if (packet.destLocation != null) {
549 TrafficTreatment t = DefaultTrafficTreatment.builder()
550 .setOutput(packet.destLocation.port()).build();
551 OutboundPacket o = new DefaultOutboundPacket(
552 packet.destLocation.deviceId(), t, ByteBuffer.wrap(packet.packet.serialize()));
553 if (log.isTraceEnabled()) {
554 log.trace("Relaying packet to destination {}", packet.destLocation);
555 }
556 packetService.emit(o);
557 } // if
558 }
559
Kalhee Kim45fede42017-09-05 19:05:06 +0000560 /**
561 * extract from dhcp6 packet client ipv6 address of given by dhcp server.
562 *
563 * @param dhcp6 the dhcp6 packet
Kalhee Kim495c9b22017-11-07 16:32:09 +0000564 * @return IpAddressInfo IpAddressInfo given by dhcp server, or null if not exists
Kalhee Kim45fede42017-09-05 19:05:06 +0000565 */
Kalhee Kim495c9b22017-11-07 16:32:09 +0000566 private IpAddressInfo extractIpAddress(DHCP6 dhcp6) {
567 IpAddressInfo ipInfo = new IpAddressInfo();
Kalhee Kim45fede42017-09-05 19:05:06 +0000568
569 log.debug("extractIpAddress enters dhcp6 {}.", dhcp6);
570 // Extract IPv6 address from IA NA ot IA TA option
571 Optional<Dhcp6IaNaOption> iaNaOption = dhcp6.getOptions()
572 .stream()
573 .filter(opt -> opt instanceof Dhcp6IaNaOption)
574 .map(opt -> (Dhcp6IaNaOption) opt)
575 .findFirst();
576 Optional<Dhcp6IaTaOption> iaTaOption = dhcp6.getOptions()
577 .stream()
578 .filter(opt -> opt instanceof Dhcp6IaTaOption)
579 .map(opt -> (Dhcp6IaTaOption) opt)
580 .findFirst();
581 Optional<Dhcp6IaAddressOption> iaAddressOption;
582 if (iaNaOption.isPresent()) {
583 log.debug("Found IPv6 address from iaNaOption {}", iaNaOption);
584
585 iaAddressOption = iaNaOption.get().getOptions().stream()
586 .filter(opt -> opt instanceof Dhcp6IaAddressOption)
587 .map(opt -> (Dhcp6IaAddressOption) opt)
588 .findFirst();
589 } else if (iaTaOption.isPresent()) {
590 log.debug("Found IPv6 address from iaTaOption {}", iaTaOption);
591
592 iaAddressOption = iaTaOption.get().getOptions().stream()
593 .filter(opt -> opt instanceof Dhcp6IaAddressOption)
594 .map(opt -> (Dhcp6IaAddressOption) opt)
595 .findFirst();
596 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000597 log.info("No IPv6 address found from iaTaOption {}", iaTaOption);
Kalhee Kim45fede42017-09-05 19:05:06 +0000598 iaAddressOption = Optional.empty();
599 }
600 if (iaAddressOption.isPresent()) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000601 ipInfo.ip6Address = iaAddressOption.get().getIp6Address();
602 ipInfo.prefTime = iaAddressOption.get().getPreferredLifetime();
Kalhee Kim45fede42017-09-05 19:05:06 +0000603 log.debug("Found IPv6 address from iaAddressOption {}", iaAddressOption);
Kalhee Kim45fede42017-09-05 19:05:06 +0000604 } else {
605 log.debug("Can't find IPv6 address from DHCPv6 {}", dhcp6);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000606 return null;
Kalhee Kim45fede42017-09-05 19:05:06 +0000607 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000608 return ipInfo;
Kalhee Kim45fede42017-09-05 19:05:06 +0000609 }
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800610
Kalhee Kim45fede42017-09-05 19:05:06 +0000611 /**
612 * extract from dhcp6 packet Prefix prefix provided by dhcp server.
613 *
614 * @param dhcp6 the dhcp6 payload
615 * @return IpPrefix Prefix Delegation prefix, or null if not exists.
616 */
Kalhee Kim495c9b22017-11-07 16:32:09 +0000617 private PdPrefixInfo extractPrefix(DHCP6 dhcp6) {
618 log.debug("extractPrefix enters {}", dhcp6);
Kalhee Kim45fede42017-09-05 19:05:06 +0000619
620 // extract prefix
Kalhee Kim495c9b22017-11-07 16:32:09 +0000621 PdPrefixInfo pdPrefixInfo = new PdPrefixInfo();
Kalhee Kim45fede42017-09-05 19:05:06 +0000622
623 Ip6Address prefixAddress = null;
624
625 // Extract IPv6 prefix from IA PD option
626 Optional<Dhcp6IaPdOption> iaPdOption = dhcp6.getOptions()
627 .stream()
628 .filter(opt -> opt instanceof Dhcp6IaPdOption)
629 .map(opt -> (Dhcp6IaPdOption) opt)
630 .findFirst();
631
632 Optional<Dhcp6IaPrefixOption> iaPrefixOption;
633 if (iaPdOption.isPresent()) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000634 log.debug("IA_PD option found {}", iaPdOption);
Kalhee Kim45fede42017-09-05 19:05:06 +0000635
636 iaPrefixOption = iaPdOption.get().getOptions().stream()
637 .filter(opt -> opt instanceof Dhcp6IaPrefixOption)
638 .map(opt -> (Dhcp6IaPrefixOption) opt)
639 .findFirst();
640 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000641 log.debug("IA_PD option NOT found");
Kalhee Kim45fede42017-09-05 19:05:06 +0000642
643 iaPrefixOption = Optional.empty();
644 }
645 if (iaPrefixOption.isPresent()) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000646 log.debug("IAPrefix Option within IA_PD option found {}", iaPrefixOption);
Kalhee Kim45fede42017-09-05 19:05:06 +0000647
648 prefixAddress = iaPrefixOption.get().getIp6Prefix();
649 int prefixLen = (int) iaPrefixOption.get().getPrefixLength();
Kalhee Kim495c9b22017-11-07 16:32:09 +0000650 log.debug("Prefix length is {} bits", prefixLen);
651 pdPrefixInfo.pdPrefix = IpPrefix.valueOf(prefixAddress, prefixLen);
652 pdPrefixInfo.prefTime = iaPrefixOption.get().getPreferredLifetime();
Kalhee Kim45fede42017-09-05 19:05:06 +0000653 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000654 log.debug("Can't find IPv6 prefix from DHCPv6 {}", dhcp6);
655 return null;
Kalhee Kim45fede42017-09-05 19:05:06 +0000656 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000657 return pdPrefixInfo;
Kalhee Kim45fede42017-09-05 19:05:06 +0000658 }
659
660 /**
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000661 * extract from dhcp6 packet ClientIdOption.
662 *
663 * @param directConnFlag directly connected host
664 * @param dhcp6Payload the dhcp6 payload
665 * @return Dhcp6ClientIdOption clientIdOption, or null if not exists.
666 */
667 private Dhcp6ClientIdOption extractClinedId(Boolean directConnFlag, DHCP6 dhcp6Payload) {
668 Dhcp6ClientIdOption clientIdOption;
669
670 if (directConnFlag) {
671 clientIdOption = dhcp6Payload.getOptions()
672 .stream()
673 .filter(opt -> opt instanceof Dhcp6ClientIdOption)
674 .map(opt -> (Dhcp6ClientIdOption) opt)
675 .findFirst()
676 .orElse(null);
677 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000678 DHCP6 leafDhcp = dhcp6HandlerUtil.getDhcp6Leaf(dhcp6Payload);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000679 clientIdOption = leafDhcp.getOptions()
680 .stream()
681 .filter(opt -> opt instanceof Dhcp6ClientIdOption)
682 .map(opt -> (Dhcp6ClientIdOption) opt)
683 .findFirst()
684 .orElse(null);
685 }
686
687 return clientIdOption;
688 }
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800689
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000690 /**
Kalhee Kim495c9b22017-11-07 16:32:09 +0000691 * remove host or route and update dhcp relay record attributes.
Kalhee Kim45fede42017-09-05 19:05:06 +0000692 *
693 * @param directConnFlag flag to show that packet is from directly connected client
Kalhee Kim495c9b22017-11-07 16:32:09 +0000694 * @param location client side connect point
Kalhee Kim45fede42017-09-05 19:05:06 +0000695 * @param dhcp6Packet the dhcp6 payload
696 * @param clientPacket client's ethernet packet
697 * @param clientIpv6 client's Ipv6 packet
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +0000698 * @param clientInterface client interfaces
Kalhee Kim45fede42017-09-05 19:05:06 +0000699 */
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000700 private void removeHostOrRoute(boolean directConnFlag, ConnectPoint location,
701 DHCP6 dhcp6Packet,
Kalhee Kim45fede42017-09-05 19:05:06 +0000702 Ethernet clientPacket, IPv6 clientIpv6,
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +0000703 Interface clientInterface) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000704 log.debug("removeHostOrRoute enters {}", dhcp6Packet);
Kalhee Kim121ba922017-11-01 17:56:44 +0000705 VlanId vlanId = clientInterface.vlan();
Kalhee Kim495c9b22017-11-07 16:32:09 +0000706 MacAddress srcMac = clientPacket.getSourceMAC(); // could be gw or host
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000707 MacAddress leafClientMac;
708 byte leafMsgType;
Kalhee Kim495c9b22017-11-07 16:32:09 +0000709 log.debug("client mac {} client vlan {}", HexString.toHexString(srcMac.toBytes(), ":"), vlanId);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000710
711 Dhcp6ClientIdOption clientIdOption = extractClinedId(directConnFlag, dhcp6Packet);
712 if (clientIdOption != null) {
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000713 if ((clientIdOption.getDuid().getDuidType() == Dhcp6Duid.DuidType.DUID_LLT) ||
714 (clientIdOption.getDuid().getDuidType() == Dhcp6Duid.DuidType.DUID_LL)) {
715 leafClientMac = MacAddress.valueOf(clientIdOption.getDuid().getLinkLayerAddress());
716 } else {
717 log.warn("Link-Layer Address not supported in CLIENTID option. No DhcpRelay Record created.");
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000718 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_LINKLOCAL_FAIL);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000719 return;
720 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000721 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000722 log.warn("CLIENTID option NOT found. Don't create DhcpRelay Record.");
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000723 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_CLIENTID_FAIL);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000724 return;
725 }
726
Kalhee Kim495c9b22017-11-07 16:32:09 +0000727 HostId leafHostId = HostId.hostId(leafClientMac, vlanId);
728 DhcpRecord record = dhcpRelayStore.getDhcpRecord(leafHostId).orElse(null);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000729 if (record == null) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000730 record = new DhcpRecord(leafHostId);
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000731 } else {
732 record = record.clone();
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000733 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000734
Kalhee Kim495c9b22017-11-07 16:32:09 +0000735 Boolean isMsgRelease = dhcp6HandlerUtil.isDhcp6Release(dhcp6Packet);
736 IpAddressInfo ipInfo;
737 PdPrefixInfo pdInfo = null;
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000738 if (directConnFlag) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000739 // Add to host store if it is connected to network directly
740 ipInfo = extractIpAddress(dhcp6Packet);
741 if (ipInfo != null) {
742 if (isMsgRelease) {
743 HostId hostId = HostId.hostId(srcMac, vlanId);
744 log.debug("remove Host {} ip for directly connected.", hostId.toString());
745 providerService.removeIpFromHost(hostId, ipInfo.ip6Address);
Kalhee Kim45fede42017-09-05 19:05:06 +0000746 }
747 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000748 log.debug("ipAddress not found. Do not remove Host {} for directly connected.",
749 HostId.hostId(srcMac, vlanId).toString());
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000750 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000751 leafMsgType = dhcp6Packet.getMsgType();
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000752 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000753 // Remove from route store if it is not connected to network directly
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000754 // pick out the first link-local ip address
Kalhee Kim495c9b22017-11-07 16:32:09 +0000755 IpAddress nextHopIp = getFirstIpByHost(directConnFlag, srcMac, vlanId);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000756 if (nextHopIp == null) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000757 log.warn("Can't find link-local IP address of gateway mac {} vlanId {}", srcMac, vlanId);
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000758 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_LINKLOCAL_GW);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000759 return;
760 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000761
Kalhee Kim495c9b22017-11-07 16:32:09 +0000762 DHCP6 leafDhcp = dhcp6HandlerUtil.getDhcp6Leaf(dhcp6Packet);
763 ipInfo = extractIpAddress(leafDhcp);
764 if (ipInfo == null) {
765 log.debug("ip is null");
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000766 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000767 if (isMsgRelease) {
768 Route routeForIP = new Route(Route.Source.STATIC, ipInfo.ip6Address.toIpPrefix(), nextHopIp);
769 log.debug("removing route of 128 address for indirectly connected.");
770 log.debug("128 ip {}, nexthop {}",
771 HexString.toHexString(ipInfo.ip6Address.toOctets(), ":"),
772 HexString.toHexString(nextHopIp.toOctets(), ":"));
773 routeStore.removeRoute(routeForIP);
Kalhee Kim45fede42017-09-05 19:05:06 +0000774 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000775 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000776
Kalhee Kim495c9b22017-11-07 16:32:09 +0000777 pdInfo = extractPrefix(leafDhcp);
778 if (pdInfo == null) {
779 log.debug("ipPrefix is null ");
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000780 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000781 if (isMsgRelease) {
782 Route routeForPrefix = new Route(Route.Source.STATIC, pdInfo.pdPrefix, nextHopIp);
783 log.debug("removing route of PD for indirectly connected.");
784 log.debug("pd ip {}, nexthop {}",
785 HexString.toHexString(pdInfo.pdPrefix.address().toOctets(), ":"),
786 HexString.toHexString(nextHopIp.toOctets(), ":"));
787
788 routeStore.removeRoute(routeForPrefix);
Kalhee Kimba366062017-11-07 16:32:09 +0000789 if (this.dhcpFpmEnabled) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000790 dhcpFpmPrefixStore.removeFpmRecord(pdInfo.pdPrefix);
Kalhee Kimba366062017-11-07 16:32:09 +0000791 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000792 }
793 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000794 leafMsgType = leafDhcp.getMsgType();
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000795 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000796
Kalhee Kim495c9b22017-11-07 16:32:09 +0000797 if (isMsgRelease) {
798 log.debug("DHCP6 RELEASE msg.");
799 if (record != null) {
800 if (ipInfo != null) {
801 log.debug("DhcpRelay Record ip6Address is set to null.");
802 record.ip6Address(null);
803 }
804 if (pdInfo != null) {
805 log.debug("DhcpRelay Record pdPrefix is set to null.");
806 }
807
808 if (!record.ip6Address().isPresent() && !record.pdPrefix().isPresent()) {
809 log.warn("IP6 address and IP6 PD both are null. Remove record.");
810 // do not remove a record. Let timer task handler it.
811 //dhcpRelayStore.removeDhcpRecord(HostId.hostId(leafClientMac, vlanId));
812 }
813 }
814 }
815
Ray Milkeyffe1a332018-01-24 10:41:14 -0800816 if (record != null) {
817 record.getV6Counters().incrementCounter(dhcp6HandlerUtil.getMsgTypeStr(leafMsgType));
818 record.addLocation(new HostLocation(location, System.currentTimeMillis()));
819 record.ip6Status(DHCP6.MsgType.getType(leafMsgType));
820 record.setDirectlyConnected(directConnFlag);
821 if (!directConnFlag) {
822 // Update gateway mac address if the host is not directly connected
823 record.nextHop(srcMac);
824 }
825 record.updateLastSeen();
Kalhee Kim495c9b22017-11-07 16:32:09 +0000826 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000827 dhcpRelayStore.updateDhcpRecord(leafHostId, record);
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000828 // TODO Use AtomicInteger for the counters
829 try {
830 recordSemaphore.acquire();
831 try {
832 dhcpRelayCountersStore.incrementCounter(gCount, dhcp6HandlerUtil.getMsgTypeStr(leafMsgType));
833 } finally {
834 // calling release() after a successful acquire()
835 recordSemaphore.release();
836 }
837 } catch (InterruptedException e) {
Ray Milkeyba547f92018-02-01 15:22:31 -0800838 Thread.currentThread().interrupt();
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000839 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000840 }
841
842 /**
843 * add host or route and update dhcp relay record.
844 *
845 * @param directConnFlag flag to show that packet is from directly connected client
846 * @param location client side connect point
847 * @param dhcp6Relay the dhcp6 payload
848 * @param embeddedDhcp6 the dhcp6 payload within relay
849 * @param srcMac client gw/host macAddress
850 * @param clientInterface client interface
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800851 * @param dhcpServerIp6Address DHCP server IP
Kalhee Kim495c9b22017-11-07 16:32:09 +0000852 */
853 private void addHostOrRoute(boolean directConnFlag, ConnectPoint location, DHCP6 dhcp6Relay,
854 DHCP6 embeddedDhcp6, MacAddress srcMac, Interface clientInterface) {
855 log.debug("addHostOrRoute entered.");
856 VlanId vlanId = clientInterface.vlan();
857 Boolean isMsgReply = dhcp6HandlerUtil.isDhcp6Reply(dhcp6Relay);
858 MacAddress leafClientMac;
859 Byte leafMsgType;
860
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000861 Dhcp6ClientIdOption clientIdOption = extractClinedId(directConnFlag, embeddedDhcp6);
862 if (clientIdOption != null) {
863 log.debug("CLIENTID option found {}", clientIdOption);
864 if ((clientIdOption.getDuid().getDuidType() == Dhcp6Duid.DuidType.DUID_LLT) ||
865 (clientIdOption.getDuid().getDuidType() == Dhcp6Duid.DuidType.DUID_LL)) {
866 leafClientMac = MacAddress.valueOf(clientIdOption.getDuid().getLinkLayerAddress());
867 } else {
868 log.warn("Link-Layer Address not supported in CLIENTID option. No DhcpRelay Record created.");
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000869 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_LINKLOCAL_FAIL);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000870 return;
871 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000872 } else {
873 log.warn("CLIENTID option NOT found. No DhcpRelay Record created.");
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000874 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_CLIENTID_FAIL);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000875 return;
876 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000877 HostId leafHostId = HostId.hostId(leafClientMac, vlanId);
878 DhcpRecord record = dhcpRelayStore.getDhcpRecord(leafHostId).orElse(null);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000879 if (record == null) {
880 record = new DhcpRecord(HostId.hostId(leafClientMac, vlanId));
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000881 } else {
882 record = record.clone();
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000883 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000884
885 IpAddressInfo ipInfo;
886 PdPrefixInfo pdInfo = null;
887 if (directConnFlag) {
888 // Add to host store if it connect to network directly
889 ipInfo = extractIpAddress(embeddedDhcp6);
890 if (ipInfo != null) {
891 if (isMsgReply) {
892 Set<IpAddress> ips = Sets.newHashSet(ipInfo.ip6Address);
893 HostId hostId = HostId.hostId(srcMac, vlanId);
894 Host host = hostService.getHost(hostId);
895 HostLocation hostLocation = new HostLocation(clientInterface.connectPoint(),
896 System.currentTimeMillis());
897 Set<HostLocation> hostLocations = Sets.newHashSet(hostLocation);
898 if (host != null) {
899 // Dual homing support:
900 // if host exists, use old locations and new location
901 hostLocations.addAll(host.locations());
902 }
903 HostDescription desc = new DefaultHostDescription(srcMac, vlanId, hostLocations, ips,
904 false);
905 log.debug("adding Host for directly connected.");
906 log.debug("client mac {} client vlan {} hostlocation {}",
907 HexString.toHexString(srcMac.toBytes(), ":"), vlanId, hostLocation.toString());
908 // Replace the ip when dhcp server give the host new ip address
909 providerService.hostDetected(hostId, desc, false);
910 }
911 } else {
912 log.warn("ipAddress not found. Do not add Host {} for directly connected.",
913 HostId.hostId(srcMac, vlanId).toString());
914 }
915 leafMsgType = embeddedDhcp6.getMsgType();
916 } else {
917 // Add to route store if it does not connect to network directly
918 // pick out the first link-local ip address
919 IpAddress nextHopIp = getFirstIpByHost(directConnFlag, srcMac, vlanId);
920 if (nextHopIp == null) {
921 log.warn("Can't find link-local IP address of gateway mac {} vlanId {}", srcMac, vlanId);
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000922 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_LINKLOCAL_GW);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000923 return;
924 }
925
926 DHCP6 leafDhcp = dhcp6HandlerUtil.getDhcp6Leaf(embeddedDhcp6);
927 ipInfo = extractIpAddress(leafDhcp);
928 if (ipInfo == null) {
929 log.debug("ip is null");
930 } else {
931 if (isMsgReply) {
932 Route routeForIP = new Route(Route.Source.STATIC, ipInfo.ip6Address.toIpPrefix(), nextHopIp);
933 log.debug("adding Route of 128 address for indirectly connected.");
934 routeStore.updateRoute(routeForIP);
935 }
936 }
937
938 pdInfo = extractPrefix(leafDhcp);
939 if (pdInfo == null) {
940 log.debug("ipPrefix is null ");
941 } else {
942 if (isMsgReply) {
943 Route routeForPrefix = new Route(Route.Source.STATIC, pdInfo.pdPrefix, nextHopIp);
944 log.debug("adding Route of PD for indirectly connected.");
945 routeStore.updateRoute(routeForPrefix);
946 if (this.dhcpFpmEnabled) {
947 FpmRecord fpmRecord = new FpmRecord(pdInfo.pdPrefix, nextHopIp, FpmRecord.Type.DHCP_RELAY);
948 dhcpFpmPrefixStore.addFpmRecord(pdInfo.pdPrefix, fpmRecord);
949 }
950 }
951 }
952 leafMsgType = leafDhcp.getMsgType();
953 }
954 if (leafMsgType == DHCP6.MsgType.RELEASE.value() ||
955 (leafMsgType == DHCP6.MsgType.REPLY.value()) && ipInfo == null) {
956 log.warn("DHCP6 RELEASE/REPLY(null ip) from Server. MsgType {}", leafMsgType);
957 //return;
958 }
959
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000960 record.addLocation(new HostLocation(location, System.currentTimeMillis()));
Kalhee Kim2f07c602017-11-15 18:57:53 +0000961
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000962 if (leafMsgType == DHCP6.MsgType.REPLY.value()) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000963 if (ipInfo != null) {
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000964 log.debug("IP6 address is being stored into dhcp-relay store.");
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800965 log.debug("Client IP6 address {}", HexString.toHexString(ipInfo.ip6Address.toOctets(), ":"));
Kalhee Kim495c9b22017-11-07 16:32:09 +0000966 record.ip6Address(ipInfo.ip6Address);
Kalhee Kim2f07c602017-11-15 18:57:53 +0000967 record.updateAddrPrefTime(ipInfo.prefTime);
968 record.updateLastIp6Update();
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800969 } else {
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000970 log.debug("IP6 address is not returned from server. Maybe only PD is returned.");
971 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000972 if (pdInfo != null) {
973 log.debug("IP6 PD address {}",
974 HexString.toHexString(pdInfo.pdPrefix.address().toOctets(), ":"));
Kalhee Kim2f07c602017-11-15 18:57:53 +0000975 record.pdPrefix(pdInfo.pdPrefix);
976 record.updatePdPrefTime(pdInfo.prefTime);
977 record.updateLastPdUpdate();
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000978 } else {
979 log.debug("IP6 PD address is not returned from server. Maybe only IPAddress is returned.");
980 }
981 }
Kalhee Kim2f07c602017-11-15 18:57:53 +0000982
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000983 record.getV6Counters().incrementCounter(dhcp6HandlerUtil.getMsgTypeStr(leafMsgType));
Kalhee Kim495c9b22017-11-07 16:32:09 +0000984 record.ip6Status(DHCP6.MsgType.getType(leafMsgType));
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000985 record.setDirectlyConnected(directConnFlag);
986 record.updateLastSeen();
Kalhee Kim495c9b22017-11-07 16:32:09 +0000987 dhcpRelayStore.updateDhcpRecord(leafHostId, record);
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000988 // TODO Use AtomicInteger for the counters
989 try {
990 recordSemaphore.acquire();
991 try {
992 dhcpRelayCountersStore.incrementCounter(gCount, dhcp6HandlerUtil.getMsgTypeStr(leafMsgType));
993 } finally {
994 // calling release() after a successful acquire()
995 recordSemaphore.release();
996 }
997 } catch (InterruptedException e) {
Ray Milkeyba547f92018-02-01 15:22:31 -0800998 Thread.currentThread().interrupt();
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000999 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001000 }
1001
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001002 private List<InternalPacket> processLQ6PacketFromClient(PacketContext context,
1003 Ethernet clientPacket,
1004 Set<Interface> clientInterfaces,
1005 DHCP6 dhcp6Payload) {
1006 ConnectPoint inPort = context.inPacket().receivedFrom();
1007 log.info("Got LQ-REQUEST V6 on port {}", inPort);
1008 List<Dhcp6Option> lopt = dhcp6Payload.getOptions();
1009 log.info("Options list: {}", lopt);
1010 Dhcp6LeaseQueryOption lqoption = dhcp6Payload.getOptions()
1011 .stream()
1012 .filter(opt -> opt instanceof Dhcp6LeaseQueryOption)
1013 .map(pld -> (Dhcp6LeaseQueryOption) pld)
1014 .findFirst()
1015 .orElse(null);
1016
1017 if (lqoption == null) {
1018 // Can't find dhcp payload
1019 log.warn("Can't find dhcp6 lease query message - aborting");
1020 return null;
1021 } else {
1022 log.info("dhcp6 lqv6 options found: {}", lqoption);
1023 }
1024 log.warn("LQv6 for " + lqoption.linkAddress.toString() + " comes from " + inPort.toString());
1025 Ethernet packet = context.inPacket().parsed();
1026 Ip6Address clientAddress = lqoption.linkAddress;
1027 IPv6 ipv6Packet = (IPv6) packet.getPayload();
1028 Ip6Address nextHopIp = findNextHopIp6FromRelayStore(clientAddress);
1029
1030 // 1. only if there is a route to remove - remove it
1031 if (nextHopIp != null) {
1032 Route routeForIP6 = new Route(Route.Source.STATIC, clientAddress.toIpPrefix(), nextHopIp);
1033 log.debug("Removing route of Client " + clientAddress +
1034 " for indirectly connected - next hop ip6 " + nextHopIp);
1035 routeStore.removeRoute(routeForIP6);
1036 }
1037
1038 // 2. note the potential NH this packet came from in case it's a known lease
1039 // this NH will then be used to build the route
1040 MacAddress potentialNH = packet.getSourceMAC();
1041 VlanId vlanId = VlanId.vlanId(packet.getVlanID());
1042 setPotentialNextHopForIp6InRelayStore(clientAddress, vlanId, potentialNH);
1043
1044 // 3. route this LQ6 to all relevant servers
1045 IPv6 clientIpv6 = (IPv6) clientPacket.getPayload();
1046 UDP clientUdp = (UDP) clientIpv6.getPayload();
1047 DHCP6 clientDhcp6 = (DHCP6) clientUdp.getPayload();
1048
1049 boolean directConnFlag = dhcp6HandlerUtil.directlyConnected(clientDhcp6);
1050
1051 ConnectPoint clientConnectionPoint = context.inPacket().receivedFrom();
1052 VlanId vlanIdInUse = VlanId.vlanId(clientPacket.getVlanID());
1053 Interface clientInterface = interfaceService.getInterfacesByPort(clientConnectionPoint)
1054 .stream().filter(iface -> dhcp6HandlerUtil.interfaceContainsVlan(iface, vlanIdInUse))
1055 .findFirst()
1056 .orElse(null);
1057
1058 List<InternalPacket> internalPackets = new ArrayList<>();
1059 List<DhcpServerInfo> serverInfoList = findValidServerInfo(directConnFlag);
1060 List<DhcpServerInfo> copyServerInfoList = new ArrayList<DhcpServerInfo>(serverInfoList);
1061
1062 for (DhcpServerInfo serverInfo : copyServerInfoList) {
1063 if (!dhcp6HandlerUtil.checkDhcpServerConnPt(directConnFlag, serverInfo)) {
1064 log.warn("Can't get server connect point, ignore");
1065 continue;
1066 }
1067 DhcpServerInfo newServerInfo = getHostInfoForServerInfo(serverInfo, serverInfoList);
1068 if (newServerInfo == null) {
1069 log.warn("Can't get server interface with host info resolved, ignore");
1070 continue;
1071 }
1072
1073 Interface serverInterface = getServerInterface(newServerInfo);
1074 if (serverInterface == null) {
1075 log.warn("Can't get server interface, ignore");
1076 continue;
1077 }
1078
1079
1080 Ethernet etherRouted = (Ethernet) clientPacket.clone();
1081 MacAddress macFacingServer = serverInterface.mac();
1082 if (macFacingServer == null) {
1083 log.warn("No MAC address for server Interface {}", serverInterface);
1084 return null;
1085 }
1086 etherRouted.setSourceMACAddress(macFacingServer);
1087 etherRouted.setDestinationMACAddress(newServerInfo.getDhcpConnectMac().get());
1088 InternalPacket internalPacket =
1089 new Dhcp6HandlerUtil().new InternalPacket(etherRouted,
1090 serverInfo.getDhcpServerConnectPoint().get());
1091 internalPackets.add(internalPacket);
1092 log.debug("Sending LQ to DHCP server {}", newServerInfo.getDhcpServerIp6());
1093 }
1094 log.debug("num of client packets to send is{}", internalPackets.size());
1095
1096 return internalPackets;
1097 }
1098
Kalhee Kim45fede42017-09-05 19:05:06 +00001099 /**
Kalhee Kim495c9b22017-11-07 16:32:09 +00001100 * build the DHCP6 solicit/request packet with gatewayip.
Kalhee Kim45fede42017-09-05 19:05:06 +00001101 *
1102 * @param context packet context
1103 * @param clientPacket client ethernet packet
1104 * @param clientInterfaces set of client side interfaces
1105 */
Kalhee Kim495c9b22017-11-07 16:32:09 +00001106 private List<InternalPacket> processDhcp6PacketFromClient(PacketContext context,
1107 Ethernet clientPacket,
1108 Set<Interface> clientInterfaces) {
Yi Tseng3bd57ac2017-11-29 14:39:18 -08001109 ConnectPoint receivedFrom = context.inPacket().receivedFrom();
1110 DeviceId receivedFromDevice = receivedFrom.deviceId();
Kalhee Kim495c9b22017-11-07 16:32:09 +00001111 Ip6Address relayAgentIp = dhcp6HandlerUtil.getRelayAgentIPv6Address(clientInterfaces);
Yi Tseng3bd57ac2017-11-29 14:39:18 -08001112 MacAddress relayAgentMac = clientInterfaces.iterator().next().mac();
1113 if (relayAgentIp == null || relayAgentMac == null) {
1114 log.warn("Missing DHCP relay agent interface Ipv6 addr config for "
Kalhee Kim495c9b22017-11-07 16:32:09 +00001115 + "packet from client on port: {}. Aborting packet processing",
1116 clientInterfaces.iterator().next().connectPoint());
Kalhee Kimd94ceea2017-11-29 19:03:02 +00001117 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_CLIENT_INTF_MAC);
Charles Chan239492c2018-01-22 13:27:28 -08001118 return Lists.newArrayList();
Kalhee Kim121ba922017-11-01 17:56:44 +00001119 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001120
Kalhee Kim495c9b22017-11-07 16:32:09 +00001121 IPv6 clientIpv6 = (IPv6) clientPacket.getPayload();
1122 UDP clientUdp = (UDP) clientIpv6.getPayload();
1123 DHCP6 clientDhcp6 = (DHCP6) clientUdp.getPayload();
1124
1125 boolean directConnFlag = dhcp6HandlerUtil.directlyConnected(clientDhcp6);
Charles Chana990ce92017-10-30 10:22:50 -07001126
Kalhee Kim121ba922017-11-01 17:56:44 +00001127 ConnectPoint clientConnectionPoint = context.inPacket().receivedFrom();
1128 VlanId vlanIdInUse = VlanId.vlanId(clientPacket.getVlanID());
1129 Interface clientInterface = interfaceService.getInterfacesByPort(clientConnectionPoint)
Kalhee Kim495c9b22017-11-07 16:32:09 +00001130 .stream().filter(iface -> dhcp6HandlerUtil.interfaceContainsVlan(iface, vlanIdInUse))
1131 .findFirst()
1132 .orElse(null);
Kalhee Kim45fede42017-09-05 19:05:06 +00001133
Kalhee Kim495c9b22017-11-07 16:32:09 +00001134 List<InternalPacket> internalPackets = new ArrayList<>();
1135 List<DhcpServerInfo> serverInfoList = findValidServerInfo(directConnFlag);
1136 List<DhcpServerInfo> copyServerInfoList = new ArrayList<DhcpServerInfo>(serverInfoList);
Kalhee Kim45b24182017-10-18 18:30:23 +00001137
Kalhee Kim495c9b22017-11-07 16:32:09 +00001138 for (DhcpServerInfo serverInfo : copyServerInfoList) {
1139 if (!dhcp6HandlerUtil.checkDhcpServerConnPt(directConnFlag, serverInfo)) {
1140 log.warn("Can't get server connect point, ignore");
1141 continue;
1142 }
1143 DhcpServerInfo newServerInfo = getHostInfoForServerInfo(serverInfo, serverInfoList);
1144 if (newServerInfo == null) {
1145 log.warn("Can't get server interface with host info resolved, ignore");
1146 continue;
1147 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001148
Kalhee Kim495c9b22017-11-07 16:32:09 +00001149 Interface serverInterface = getServerInterface(newServerInfo);
1150 if (serverInterface == null) {
1151 log.warn("Can't get server interface, ignore");
1152 continue;
1153 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +00001154
Kalhee Kim495c9b22017-11-07 16:32:09 +00001155 Ethernet etherReply = dhcp6HandlerUtil.buildDhcp6PacketFromClient(context, clientPacket,
1156 clientInterfaces, newServerInfo, serverInterface);
1157 removeHostOrRoute(directConnFlag, clientConnectionPoint, clientDhcp6, clientPacket,
1158 clientIpv6, clientInterface);
Kalhee Kimea4b6c22017-11-09 14:38:37 +00001159
Kalhee Kim495c9b22017-11-07 16:32:09 +00001160 InternalPacket internalPacket = new Dhcp6HandlerUtil().new InternalPacket(etherReply,
1161 serverInfo.getDhcpServerConnectPoint().get());
1162 internalPackets.add(internalPacket);
1163 }
1164 log.debug("num of client packets to send is{}", internalPackets.size());
Kalhee Kimea4b6c22017-11-09 14:38:37 +00001165
Kalhee Kim495c9b22017-11-07 16:32:09 +00001166 return internalPackets;
1167 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001168
1169 /**
Kalhee Kim45fede42017-09-05 19:05:06 +00001170 * process the DHCP6 relay-reply packet from dhcp server.
1171 *
1172 * @param context packet context
1173 * @param receivedPacket server ethernet packet
1174 * @param recevingInterfaces set of server side interfaces
Kalhee Kim495c9b22017-11-07 16:32:09 +00001175 * @return internalPacket toward client
Kalhee Kim45fede42017-09-05 19:05:06 +00001176 */
1177 private InternalPacket processDhcp6PacketFromServer(PacketContext context,
1178 Ethernet receivedPacket, Set<Interface> recevingInterfaces) {
Kalhee Kim45fede42017-09-05 19:05:06 +00001179 // get dhcp6 header.
Ray Milkeyf0c47612017-09-28 11:29:38 -07001180 Ethernet etherReply = receivedPacket.duplicate();
Kalhee Kim45fede42017-09-05 19:05:06 +00001181 IPv6 ipv6Packet = (IPv6) etherReply.getPayload();
1182 UDP udpPacket = (UDP) ipv6Packet.getPayload();
1183 DHCP6 dhcp6Relay = (DHCP6) udpPacket.getPayload();
Kalhee Kim495c9b22017-11-07 16:32:09 +00001184 Boolean directConnFlag = dhcp6HandlerUtil.directlyConnected(dhcp6Relay);
Kalhee Kim45fede42017-09-05 19:05:06 +00001185
Kalhee Kim495c9b22017-11-07 16:32:09 +00001186 DHCP6 embeddedDhcp6 = dhcp6Relay.getOptions().stream()
1187 .filter(opt -> opt instanceof Dhcp6RelayOption)
1188 .map(BasePacket::getPayload)
1189 .map(pld -> (DHCP6) pld)
1190 .findFirst()
1191 .orElse(null);
Kalhee Kimd21029f2017-09-26 20:21:53 +00001192 ConnectPoint inPort = context.inPacket().receivedFrom();
Kalhee Kim495c9b22017-11-07 16:32:09 +00001193 DhcpServerInfo foundServerInfo = findServerInfoFromServer(directConnFlag, inPort);
Kalhee Kimd21029f2017-09-26 20:21:53 +00001194
Kalhee Kim495c9b22017-11-07 16:32:09 +00001195 if (foundServerInfo == null) {
1196 log.warn("Cannot find server info");
Kalhee Kimd94ceea2017-11-29 19:03:02 +00001197 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_SERVER_INFO);
Kalhee Kimd21029f2017-09-26 20:21:53 +00001198 return null;
Kalhee Kim495c9b22017-11-07 16:32:09 +00001199 } else {
1200 if (dhcp6HandlerUtil.isServerIpEmpty(foundServerInfo)) {
1201 log.warn("Cannot find server info's ipaddress");
Kalhee Kimd94ceea2017-11-29 19:03:02 +00001202 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_SERVER_IP6ADDR);
Kalhee Kim495c9b22017-11-07 16:32:09 +00001203 return null;
1204 }
Kalhee Kimd21029f2017-09-26 20:21:53 +00001205 }
1206
Kalhee Kim45fede42017-09-05 19:05:06 +00001207 Dhcp6InterfaceIdOption interfaceIdOption = dhcp6Relay.getOptions().stream()
1208 .filter(opt -> opt instanceof Dhcp6InterfaceIdOption)
1209 .map(opt -> (Dhcp6InterfaceIdOption) opt)
1210 .findFirst()
1211 .orElse(null);
Kalhee Kim45fede42017-09-05 19:05:06 +00001212 if (interfaceIdOption == null) {
1213 log.warn("Interface Id option is not present, abort packet...");
Kalhee Kimd94ceea2017-11-29 19:03:02 +00001214 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.OPTION_MISSING_FAIL);
Kalhee Kim45fede42017-09-05 19:05:06 +00001215 return null;
1216 }
1217
1218 MacAddress peerMac = interfaceIdOption.getMacAddress();
1219 String clientConnectionPointStr = new String(interfaceIdOption.getInPort());
Kalhee Kim45fede42017-09-05 19:05:06 +00001220 ConnectPoint clientConnectionPoint = ConnectPoint.deviceConnectPoint(clientConnectionPointStr);
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +00001221 VlanId vlanIdInUse = VlanId.vlanId(interfaceIdOption.getVlanId());
1222 Interface clientInterface = interfaceService.getInterfacesByPort(clientConnectionPoint)
Kalhee Kim495c9b22017-11-07 16:32:09 +00001223 .stream().filter(iface -> dhcp6HandlerUtil.interfaceContainsVlan(iface, vlanIdInUse))
1224 .findFirst().orElse(null);
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +00001225 if (clientInterface == null) {
1226 log.warn("Cannot get client interface for from packet, abort... vlan {}", vlanIdInUse.toString());
Kalhee Kimd94ceea2017-11-29 19:03:02 +00001227 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_MATCHING_INTF);
Kalhee Kim45fede42017-09-05 19:05:06 +00001228 return null;
1229 }
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +00001230 MacAddress relayAgentMac = clientInterface.mac();
Kalhee Kim45fede42017-09-05 19:05:06 +00001231 if (relayAgentMac == null) {
Yi Tseng68ef26b2017-12-18 17:10:00 -08001232 log.warn("Can not get client interface mac, abort packet..");
Kalhee Kimd94ceea2017-11-29 19:03:02 +00001233 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_CLIENT_INTF_MAC);
Kalhee Kim45fede42017-09-05 19:05:06 +00001234 return null;
1235 }
1236 etherReply.setSourceMACAddress(relayAgentMac);
1237
1238 // find destMac
Yi Tseng68ef26b2017-12-18 17:10:00 -08001239 MacAddress clientMac;
Yi Tsengaa417a62017-09-08 17:22:51 -07001240 Ip6Address peerAddress = Ip6Address.valueOf(dhcp6Relay.getPeerAddress());
1241 Set<Host> clients = hostService.getHostsByIp(peerAddress);
Kalhee Kim45fede42017-09-05 19:05:06 +00001242 if (clients.isEmpty()) {
Yi Tseng68ef26b2017-12-18 17:10:00 -08001243 log.trace("There's no host found for this address {}",
Kalhee Kim45fede42017-09-05 19:05:06 +00001244 HexString.toHexString(dhcp6Relay.getPeerAddress(), ":"));
Yi Tseng68ef26b2017-12-18 17:10:00 -08001245 log.trace("Let's look up interfaceId {}", HexString.toHexString(peerMac.toBytes(), ":"));
Kalhee Kim45fede42017-09-05 19:05:06 +00001246 clientMac = peerMac;
1247 } else {
1248 clientMac = clients.iterator().next().mac();
1249 if (clientMac == null) {
1250 log.warn("No client mac address found, abort packet...");
Kalhee Kimd94ceea2017-11-29 19:03:02 +00001251 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_CLIENT_INTF_MAC);
Kalhee Kim45fede42017-09-05 19:05:06 +00001252 return null;
1253 }
Yi Tseng68ef26b2017-12-18 17:10:00 -08001254 log.trace("Client mac address found from getHostByIp");
Kalhee Kim45fede42017-09-05 19:05:06 +00001255 }
1256 etherReply.setDestinationMACAddress(clientMac);
Kalhee Kim45fede42017-09-05 19:05:06 +00001257 // ip header
1258 ipv6Packet.setSourceAddress(dhcp6Relay.getLinkAddress());
1259 ipv6Packet.setDestinationAddress(dhcp6Relay.getPeerAddress());
1260 // udp header
1261 udpPacket.setSourcePort(UDP.DHCP_V6_SERVER_PORT);
1262 if (directConnFlag) {
1263 udpPacket.setDestinationPort(UDP.DHCP_V6_CLIENT_PORT);
1264 } else {
1265 udpPacket.setDestinationPort(UDP.DHCP_V6_SERVER_PORT);
1266 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001267 // add host or route
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001268 addHostOrRoute(directConnFlag, clientConnectionPoint, dhcp6Relay, embeddedDhcp6,
1269 clientMac, clientInterface);
1270
Kalhee Kim45fede42017-09-05 19:05:06 +00001271
1272 udpPacket.setPayload(embeddedDhcp6);
1273 udpPacket.resetChecksum();
1274 ipv6Packet.setPayload(udpPacket);
1275 etherReply.setPayload(ipv6Packet);
Kalhee Kim495c9b22017-11-07 16:32:09 +00001276 return new Dhcp6HandlerUtil().new InternalPacket(etherReply, clientConnectionPoint);
Kalhee Kim45fede42017-09-05 19:05:06 +00001277 }
1278
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001279 // Returns the first v6 interface ip out of a set of interfaces or null.
1280 // Checks all interfaces, and ignores v6 interface ips
1281 private Ip6Address getRelayAgentIPv6Address(Set<Interface> intfs) {
1282 for (Interface intf : intfs) {
1283 for (InterfaceIpAddress ip : intf.ipAddressesList()) {
1284 Ip6Address relayAgentIp = ip.ipAddress().getIp6Address();
1285 if (relayAgentIp != null) {
1286 return relayAgentIp;
1287 }
1288 }
1289 }
1290 return null;
1291 }
Yi Tseng483ac6f2017-08-02 15:03:31 -07001292
1293 @Override
Kalhee Kimba366062017-11-07 16:32:09 +00001294 public void setDhcpFpmEnabled(Boolean enabled) {
1295 dhcpFpmEnabled = enabled;
1296 }
1297
1298 @Override
Yi Tseng483ac6f2017-08-02 15:03:31 -07001299 public void setDefaultDhcpServerConfigs(Collection<DhcpServerConfig> configs) {
Kalhee Kim495c9b22017-11-07 16:32:09 +00001300 log.debug("setDefaultDhcpServerConfigs is called.");
Yi Tseng919b2df2017-09-07 16:22:51 -07001301 setDhcpServerConfigs(configs, defaultServerInfoList);
Yi Tseng919b2df2017-09-07 16:22:51 -07001302 }
1303
1304 @Override
1305 public void setIndirectDhcpServerConfigs(Collection<DhcpServerConfig> configs) {
Kalhee Kim495c9b22017-11-07 16:32:09 +00001306 log.debug("setIndirectDhcpServerConfigs is called.");
Yi Tseng919b2df2017-09-07 16:22:51 -07001307 setDhcpServerConfigs(configs, indirectServerInfoList);
Yi Tseng919b2df2017-09-07 16:22:51 -07001308 }
1309
1310 public void setDhcpServerConfigs(Collection<DhcpServerConfig> configs, List<DhcpServerInfo> serverInfoList) {
Kalhee Kim495c9b22017-11-07 16:32:09 +00001311 log.debug("config size {}.", configs.size());
1312
Kalhee Kim45fede42017-09-05 19:05:06 +00001313 if (configs.size() == 0) {
1314 // no config to update
1315 return;
1316 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001317 // TODO: currently we pick up first DHCP server config.
1318 // Will use other server configs in the future for HA.
Kalhee Kim495c9b22017-11-07 16:32:09 +00001319 Boolean isConfigValid = false;
1320 for (DhcpServerConfig serverConfig : configs) {
1321 if (serverConfig.getDhcpServerIp6().isPresent()) {
1322 isConfigValid = true;
1323 break;
1324 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001325 }
Kalhee Kim495c9b22017-11-07 16:32:09 +00001326 if (!isConfigValid) {
1327 log.warn("No IP V6 server address found.");
1328 return; // No IP V6 address found
1329 }
1330 for (DhcpServerInfo oldServerInfo : serverInfoList) {
Yi Tseng919b2df2017-09-07 16:22:51 -07001331 // stop monitoring gateway or server
1332 oldServerInfo.getDhcpGatewayIp6().ifPresent(gatewayIp -> {
1333 hostService.stopMonitoringIp(gatewayIp);
1334 });
1335 oldServerInfo.getDhcpServerIp6().ifPresent(serverIp -> {
1336 hostService.stopMonitoringIp(serverIp);
Yi Tseng525ff402017-10-23 19:39:39 -07001337 cancelDhcpPacket(serverIp);
Yi Tseng919b2df2017-09-07 16:22:51 -07001338 });
1339 }
Kalhee Kim495c9b22017-11-07 16:32:09 +00001340 serverInfoList.clear();
1341 for (DhcpServerConfig serverConfig : configs) {
1342 // Create new server info according to the config
1343 DhcpServerInfo newServerInfo = new DhcpServerInfo(serverConfig,
1344 DhcpServerInfo.Version.DHCP_V6);
1345 checkState(newServerInfo.getDhcpServerConnectPoint().isPresent(),
1346 "Connect point not exists");
1347 checkState(newServerInfo.getDhcpServerIp6().isPresent(),
1348 "IP of DHCP server not exists");
Yi Tseng919b2df2017-09-07 16:22:51 -07001349
Kalhee Kim495c9b22017-11-07 16:32:09 +00001350 log.debug("DHCP server connect point: {}", newServerInfo.getDhcpServerConnectPoint().orElse(null));
1351 log.debug("DHCP server IP: {}", newServerInfo.getDhcpServerIp6().orElse(null));
Yi Tseng919b2df2017-09-07 16:22:51 -07001352
Kalhee Kim495c9b22017-11-07 16:32:09 +00001353 Ip6Address serverIp = newServerInfo.getDhcpServerIp6().get();
1354 Ip6Address ipToProbe;
1355 if (newServerInfo.getDhcpGatewayIp6().isPresent()) {
1356 ipToProbe = newServerInfo.getDhcpGatewayIp6().get();
1357 } else {
1358 ipToProbe = newServerInfo.getDhcpServerIp6().orElse(null);
1359 }
1360 String hostToProbe = newServerInfo.getDhcpGatewayIp6()
1361 .map(ip -> "gateway").orElse("server");
Yi Tseng919b2df2017-09-07 16:22:51 -07001362
Kalhee Kim495c9b22017-11-07 16:32:09 +00001363 log.warn("Probing to resolve {} IP {}", hostToProbe, ipToProbe);
1364 hostService.startMonitoringIp(ipToProbe);
1365
1366 Set<Host> hosts = hostService.getHostsByIp(ipToProbe);
1367 if (!hosts.isEmpty()) {
1368 Host host = hosts.iterator().next();
1369 newServerInfo.setDhcpConnectVlan(host.vlan());
1370 newServerInfo.setDhcpConnectMac(host.mac());
1371 log.warn("Host found host {}", host);
1372
1373 } else {
1374 log.warn("No host found host ip {}", ipToProbe);
1375 }
1376 // Add new server info
1377 synchronized (this) {
1378 serverInfoList.add(newServerInfo);
1379 }
1380 if (!hosts.isEmpty()) {
1381 requestDhcpPacket(serverIp);
1382 }
Yi Tseng919b2df2017-09-07 16:22:51 -07001383 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001384 }
1385
1386 class InternalHostListener implements HostListener {
1387 @Override
1388 public void event(HostEvent event) {
1389 switch (event.type()) {
1390 case HOST_ADDED:
1391 case HOST_UPDATED:
1392 hostUpdated(event.subject());
1393 break;
1394 case HOST_REMOVED:
1395 hostRemoved(event.subject());
1396 break;
Kalhee Kim45fede42017-09-05 19:05:06 +00001397 default:
1398 break;
1399 }
1400 }
1401 }
1402
1403 /**
Kalhee Kim45fede42017-09-05 19:05:06 +00001404 * Handle host updated.
1405 * If the host is DHCP server or gateway, update connect mac and vlan.
1406 *
1407 * @param host the host
1408 */
1409 private void hostUpdated(Host host) {
Yi Tseng525ff402017-10-23 19:39:39 -07001410 hostUpdated(host, defaultServerInfoList);
1411 hostUpdated(host, indirectServerInfoList);
Kalhee Kim45fede42017-09-05 19:05:06 +00001412 }
1413
Yi Tseng525ff402017-10-23 19:39:39 -07001414 private void hostUpdated(Host host, List<DhcpServerInfo> serverInfoList) {
1415 DhcpServerInfo serverInfo;
1416 Ip6Address targetIp;
1417 if (!serverInfoList.isEmpty()) {
1418 serverInfo = serverInfoList.get(0);
1419 Ip6Address serverIp = serverInfo.getDhcpServerIp6().orElse(null);
1420 targetIp = serverInfo.getDhcpGatewayIp6().orElse(null);
1421
1422 if (targetIp == null) {
1423 targetIp = serverIp;
1424 }
Yi Tseng525ff402017-10-23 19:39:39 -07001425 if (targetIp != null) {
1426 if (host.ipAddresses().contains(targetIp)) {
1427 serverInfo.setDhcpConnectMac(host.mac());
1428 serverInfo.setDhcpConnectVlan(host.vlan());
1429 requestDhcpPacket(serverIp);
1430 }
1431 }
1432 }
1433 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001434 /**
1435 * Handle host removed.
1436 * If the host is DHCP server or gateway, unset connect mac and vlan.
1437 *
1438 * @param host the host
1439 */
1440 private void hostRemoved(Host host) {
Yi Tseng525ff402017-10-23 19:39:39 -07001441 hostRemoved(host, defaultServerInfoList);
1442 hostRemoved(host, indirectServerInfoList);
Yi Tseng919b2df2017-09-07 16:22:51 -07001443 }
1444
Yi Tseng525ff402017-10-23 19:39:39 -07001445 private void hostRemoved(Host host, List<DhcpServerInfo> serverInfoList) {
1446 DhcpServerInfo serverInfo;
1447 Ip6Address targetIp;
1448
1449 if (!serverInfoList.isEmpty()) {
1450 serverInfo = serverInfoList.get(0);
1451 Ip6Address serverIp = serverInfo.getDhcpServerIp6().orElse(null);
1452 targetIp = serverInfo.getDhcpGatewayIp6().orElse(null);
1453
1454 if (targetIp == null) {
1455 targetIp = serverIp;
1456 }
Yi Tseng525ff402017-10-23 19:39:39 -07001457 if (targetIp != null) {
1458 if (host.ipAddresses().contains(targetIp)) {
1459 serverInfo.setDhcpConnectVlan(null);
1460 serverInfo.setDhcpConnectMac(null);
1461 cancelDhcpPacket(serverIp);
1462 }
1463 }
1464 }
1465 }
1466
Kalhee Kim45b24182017-10-18 18:30:23 +00001467 /**
1468 * Returns the first interface ip from interface.
1469 *
1470 * @param iface interface of one connect point
1471 * @return the first interface IP; null if not exists an IP address in
1472 * these interfaces
1473 */
1474 private Ip6Address getFirstIpFromInterface(Interface iface) {
1475 checkNotNull(iface, "Interface can't be null");
1476 return iface.ipAddressesList().stream()
1477 .map(InterfaceIpAddress::ipAddress)
1478 .filter(IpAddress::isIp6)
1479 .map(IpAddress::getIp6Address)
1480 .findFirst()
1481 .orElse(null);
1482 }
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001483
1484 /**
1485 * Gets Interface facing to the server for default host.
1486 *
1487 * @return the Interface facing to the server; null if not found
1488 */
1489 private Interface getServerInterface() {
1490 DhcpServerInfo serverInfo;
1491 ConnectPoint dhcpServerConnectPoint;
1492 VlanId dhcpConnectVlan;
1493
1494 if (!defaultServerInfoList.isEmpty()) {
1495 serverInfo = defaultServerInfoList.get(0);
1496 dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1497 dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1498 } else {
1499 return null;
1500 }
1501 if (dhcpServerConnectPoint == null || dhcpConnectVlan == null) {
1502 log.info("Default DHCP server {} not resolve yet", serverInfo.getDhcpGatewayIp6());
1503 return null;
1504 }
1505 return interfaceService.getInterfacesByPort(dhcpServerConnectPoint)
1506 .stream()
1507 .filter(iface -> dhcp6HandlerUtil.interfaceContainsVlan(iface, dhcpConnectVlan))
1508 .findFirst()
1509 .orElse(null);
1510 }
1511
1512 /**
1513 * Gets Interface facing to the server for indirect hosts.
1514 * Use default server Interface if indirect server not configured.
1515 *
1516 * @return the Interface facing to the server; null if not found
1517 */
1518 private Interface getIndirectServerInterface() {
1519 DhcpServerInfo serverInfo;
1520
1521 ConnectPoint indirectDhcpServerConnectPoint;
1522 VlanId indirectDhcpConnectVlan;
1523
1524 if (!indirectServerInfoList.isEmpty()) {
1525 serverInfo = indirectServerInfoList.get(0);
1526 indirectDhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1527 indirectDhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1528 } else {
1529 return getServerInterface();
1530 }
1531 if (indirectDhcpServerConnectPoint == null || indirectDhcpConnectVlan == null) {
1532 log.info("Indirect DHCP server {} not resolve yet", serverInfo.getDhcpGatewayIp6());
1533 return null;
1534 }
1535 return interfaceService.getInterfacesByPort(indirectDhcpServerConnectPoint)
1536 .stream()
1537 .filter(iface -> dhcp6HandlerUtil.interfaceContainsVlan(iface, indirectDhcpConnectVlan))
1538 .findFirst()
1539 .orElse(null);
1540 }
1541
Kalhee Kim45b24182017-10-18 18:30:23 +00001542 /**
Kalhee Kim495c9b22017-11-07 16:32:09 +00001543 * Checks if serverInfo's host info (mac and vlan) is filled in; if not, fills in.
1544 *
1545 * @param serverInfo server information
1546 * @return newServerInfo if host info can be either found or filled in.
1547 */
1548 private DhcpServerInfo getHostInfoForServerInfo(DhcpServerInfo serverInfo, List<DhcpServerInfo> sererInfoList) {
1549 DhcpServerInfo newServerInfo = null;
1550 MacAddress dhcpServerConnectMac = serverInfo.getDhcpConnectMac().orElse(null);
1551 VlanId dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1552 ConnectPoint dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1553
1554 if (dhcpServerConnectMac != null && dhcpConnectVlan != null) {
1555 newServerInfo = serverInfo;
Charles Chan750c9992018-02-26 10:44:49 -08001556 log.debug("DHCP server {} host info found. ConnectPt{} Mac {} vlan {}", serverInfo.getDhcpServerIp6(),
Kalhee Kim495c9b22017-11-07 16:32:09 +00001557 dhcpServerConnectPoint, dhcpServerConnectMac, dhcpConnectVlan);
1558 } else {
1559 log.warn("DHCP server {} not resolve yet connectPt {} mac {} vlan {}", serverInfo.getDhcpServerIp6(),
1560 dhcpServerConnectPoint, dhcpServerConnectMac, dhcpConnectVlan);
1561
1562 Ip6Address ipToProbe;
1563 if (serverInfo.getDhcpGatewayIp6().isPresent()) {
1564 ipToProbe = serverInfo.getDhcpGatewayIp6().get();
1565 } else {
1566 ipToProbe = serverInfo.getDhcpServerIp6().orElse(null);
1567 }
1568 String hostToProbe = serverInfo.getDhcpGatewayIp6()
1569 .map(ip -> "gateway").orElse("server");
1570
1571 log.info("Dynamically probing to resolve {} IP {}", hostToProbe, ipToProbe);
1572 hostService.startMonitoringIp(ipToProbe);
1573
1574 Set<Host> hosts = hostService.getHostsByIp(ipToProbe);
1575 if (!hosts.isEmpty()) {
1576 int serverInfoIndex = sererInfoList.indexOf(serverInfo);
1577 Host host = hosts.iterator().next();
1578 serverInfo.setDhcpConnectVlan(host.vlan());
1579 serverInfo.setDhcpConnectMac(host.mac());
1580 // replace the serverInfo in the list
1581 sererInfoList.set(serverInfoIndex, serverInfo);
1582 newServerInfo = serverInfo;
1583 log.warn("Dynamically host found host {}", host);
1584 } else {
1585 log.warn("No host found host ip {} dynamically", ipToProbe);
1586 }
1587 }
1588 return newServerInfo;
1589 }
1590
1591 /**
Kalhee Kim45b24182017-10-18 18:30:23 +00001592 * Gets Interface facing to the server for default host.
1593 *
Kalhee Kim495c9b22017-11-07 16:32:09 +00001594 * @param serverInfo server information
Kalhee Kim45b24182017-10-18 18:30:23 +00001595 * @return the Interface facing to the server; null if not found
1596 */
Kalhee Kim495c9b22017-11-07 16:32:09 +00001597 private Interface getServerInterface(DhcpServerInfo serverInfo) {
1598 Interface serverInterface = null;
Yi Tseng25bfe372017-11-03 16:27:32 -07001599
Kalhee Kim495c9b22017-11-07 16:32:09 +00001600 ConnectPoint dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1601 VlanId dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1602
1603 if (dhcpServerConnectPoint != null && dhcpConnectVlan != null) {
1604 serverInterface = interfaceService.getInterfacesByPort(dhcpServerConnectPoint)
1605 .stream()
1606 .filter(iface -> dhcp6HandlerUtil.interfaceContainsVlan(iface, dhcpConnectVlan))
1607 .findFirst()
1608 .orElse(null);
Yi Tseng25bfe372017-11-03 16:27:32 -07001609 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +00001610 log.warn("DHCP server {} not resolve yet connectPoint {} vlan {}", serverInfo.getDhcpServerIp6(),
1611 dhcpServerConnectPoint, dhcpConnectVlan);
Kalhee Kim45b24182017-10-18 18:30:23 +00001612 }
Kalhee Kim495c9b22017-11-07 16:32:09 +00001613
1614 return serverInterface;
Kalhee Kim45b24182017-10-18 18:30:23 +00001615 }
1616
Yi Tseng525ff402017-10-23 19:39:39 -07001617 private void requestDhcpPacket(Ip6Address serverIp) {
1618 requestServerDhcpPacket(serverIp);
1619 requestClientDhcpPacket(serverIp);
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001620 requestServerLQPacket(serverIp);
Yi Tseng525ff402017-10-23 19:39:39 -07001621 }
1622
1623 private void cancelDhcpPacket(Ip6Address serverIp) {
1624 cancelServerDhcpPacket(serverIp);
1625 cancelClientDhcpPacket(serverIp);
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001626 cancelServerLQPacket(serverIp);
Yi Tseng525ff402017-10-23 19:39:39 -07001627 }
1628
1629 private void cancelServerDhcpPacket(Ip6Address serverIp) {
1630 TrafficSelector serverSelector =
1631 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1632 .matchIPv6Src(serverIp.toIpPrefix())
1633 .build();
1634 packetService.cancelPackets(serverSelector,
1635 PacketPriority.CONTROL,
1636 appId);
1637 }
1638
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001639 private void cancelServerLQPacket(Ip6Address serverIp) {
1640 TrafficSelector serverSelector =
1641 DefaultTrafficSelector.builder(LEASE_QUERY_RESPONSE_SELECTOR)
1642 .matchIPv6Src(serverIp.toIpPrefix())
1643 .build();
1644 packetService.cancelPackets(serverSelector,
1645 PacketPriority.CONTROL,
1646 appId);
1647 }
1648
Yi Tseng525ff402017-10-23 19:39:39 -07001649 private void requestServerDhcpPacket(Ip6Address serverIp) {
1650 TrafficSelector serverSelector =
1651 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1652 .matchIPv6Src(serverIp.toIpPrefix())
1653 .build();
1654 packetService.requestPackets(serverSelector,
1655 PacketPriority.CONTROL,
1656 appId);
1657 }
1658
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001659 private void requestServerLQPacket(Ip6Address serverIp) {
1660 TrafficSelector serverSelector =
1661 DefaultTrafficSelector.builder(LEASE_QUERY_RESPONSE_SELECTOR)
1662 .matchIPv6Src(serverIp.toIpPrefix())
1663 .build();
1664 packetService.requestPackets(serverSelector,
1665 PacketPriority.CONTROL,
1666 appId);
1667 }
1668
Yi Tseng525ff402017-10-23 19:39:39 -07001669 private void cancelClientDhcpPacket(Ip6Address serverIp) {
1670 // Packet comes from relay
1671 TrafficSelector indirectClientSelector =
1672 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1673 .matchIPv6Dst(serverIp.toIpPrefix())
1674 .build();
1675 packetService.cancelPackets(indirectClientSelector,
1676 PacketPriority.CONTROL,
1677 appId);
Yi Tseng41dde702017-11-02 15:21:10 -07001678 indirectClientSelector =
1679 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1680 .matchIPv6Dst(Ip6Address.ALL_DHCP_RELAY_AGENTS_AND_SERVERS.toIpPrefix())
1681 .build();
1682 packetService.cancelPackets(indirectClientSelector,
1683 PacketPriority.CONTROL,
1684 appId);
1685 indirectClientSelector =
1686 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1687 .matchIPv6Dst(Ip6Address.ALL_DHCP_SERVERS.toIpPrefix())
1688 .build();
1689 packetService.cancelPackets(indirectClientSelector,
1690 PacketPriority.CONTROL,
1691 appId);
Yi Tseng525ff402017-10-23 19:39:39 -07001692
1693 // Packet comes from client
1694 packetService.cancelPackets(CLIENT_SERVER_SELECTOR,
1695 PacketPriority.CONTROL,
1696 appId);
1697 }
1698
1699 private void requestClientDhcpPacket(Ip6Address serverIp) {
1700 // Packet comes from relay
1701 TrafficSelector indirectClientSelector =
1702 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1703 .matchIPv6Dst(serverIp.toIpPrefix())
1704 .build();
1705 packetService.requestPackets(indirectClientSelector,
1706 PacketPriority.CONTROL,
1707 appId);
Yi Tseng41dde702017-11-02 15:21:10 -07001708 indirectClientSelector =
1709 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1710 .matchIPv6Dst(Ip6Address.ALL_DHCP_RELAY_AGENTS_AND_SERVERS.toIpPrefix())
1711 .build();
1712 packetService.requestPackets(indirectClientSelector,
1713 PacketPriority.CONTROL,
1714 appId);
1715 indirectClientSelector =
1716 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1717 .matchIPv6Dst(Ip6Address.ALL_DHCP_SERVERS.toIpPrefix())
1718 .build();
1719 packetService.requestPackets(indirectClientSelector,
1720 PacketPriority.CONTROL,
1721 appId);
Yi Tseng525ff402017-10-23 19:39:39 -07001722
1723 // Packet comes from client
1724 packetService.requestPackets(CLIENT_SERVER_SELECTOR,
1725 PacketPriority.CONTROL,
1726 appId);
1727 }
1728
1729 /**
1730 * Process the ignore rules.
1731 *
1732 * @param deviceId the device id
1733 * @param vlanId the vlan to be ignored
1734 * @param op the operation, ADD to install; REMOVE to uninstall rules
1735 */
1736 private void processIgnoreVlanRule(DeviceId deviceId, VlanId vlanId, Objective.Operation op) {
Yi Tseng525ff402017-10-23 19:39:39 -07001737 AtomicInteger installedCount = new AtomicInteger(DHCP_SELECTORS.size());
1738 DHCP_SELECTORS.forEach(trafficSelector -> {
1739 TrafficSelector selector = DefaultTrafficSelector.builder(trafficSelector)
1740 .matchVlanId(vlanId)
1741 .build();
1742
1743 ForwardingObjective.Builder builder = DefaultForwardingObjective.builder()
1744 .withFlag(ForwardingObjective.Flag.VERSATILE)
1745 .withSelector(selector)
1746 .withPriority(IGNORE_CONTROL_PRIORITY)
Yi Tsengdbabeed2017-11-29 10:49:20 -08001747 .withTreatment(DefaultTrafficTreatment.emptyTreatment())
Yi Tseng525ff402017-10-23 19:39:39 -07001748 .fromApp(appId);
1749
1750
1751 ObjectiveContext objectiveContext = new ObjectiveContext() {
1752 @Override
1753 public void onSuccess(Objective objective) {
1754 log.info("Ignore rule {} (Vlan id {}, device {}, selector {})",
1755 op, vlanId, deviceId, selector);
1756 int countDown = installedCount.decrementAndGet();
1757 if (countDown != 0) {
1758 return;
1759 }
1760 switch (op) {
1761 case ADD:
1762 ignoredVlans.put(deviceId, vlanId);
1763 break;
1764 case REMOVE:
1765 ignoredVlans.remove(deviceId, vlanId);
1766 break;
1767 default:
1768 log.warn("Unsupported objective operation {}", op);
1769 break;
1770 }
1771 }
1772
1773 @Override
1774 public void onError(Objective objective, ObjectiveError error) {
1775 log.warn("Can't {} ignore rule (vlan id {}, selector {}, device {}) due to {}",
1776 op, vlanId, selector, deviceId, error);
1777 }
1778 };
1779
1780 ForwardingObjective fwd;
1781 switch (op) {
1782 case ADD:
1783 fwd = builder.add(objectiveContext);
1784 break;
1785 case REMOVE:
1786 fwd = builder.remove(objectiveContext);
1787 break;
1788 default:
1789 log.warn("Unsupported objective operation {}", op);
1790 return;
1791 }
1792
1793 Device device = deviceService.getDevice(deviceId);
1794 if (device == null || !device.is(Pipeliner.class)) {
1795 log.warn("Device {} is not available now, wait until device is available", deviceId);
1796 return;
1797 }
1798 flowObjectiveService.apply(deviceId, fwd);
1799 });
1800 }
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001801
Kalhee Kim121ba922017-11-01 17:56:44 +00001802 /**
1803 * Find first ipaddress for a given Host info i.e. mac and vlan.
1804 *
1805 * @param clientMac client mac
1806 * @param vlanId packet's vlan
1807 * @return next-hop link-local ipaddress for a given host
1808 */
Kalhee Kim495c9b22017-11-07 16:32:09 +00001809 private IpAddress getFirstIpByHost(Boolean directConnFlag, MacAddress clientMac, VlanId vlanId) {
Kalhee Kim121ba922017-11-01 17:56:44 +00001810 IpAddress nextHopIp;
1811 // pick out the first link-local ip address
1812 HostId gwHostId = HostId.hostId(clientMac, vlanId);
1813 Host gwHost = hostService.getHost(gwHostId);
1814 if (gwHost == null) {
1815 log.warn("Can't find gateway host for hostId {}", gwHostId);
1816 return null;
1817 }
Kalhee Kim495c9b22017-11-07 16:32:09 +00001818 if (directConnFlag) {
1819 nextHopIp = gwHost.ipAddresses()
1820 .stream()
1821 .filter(IpAddress::isIp6)
1822 .map(IpAddress::getIp6Address)
1823 .findFirst()
1824 .orElse(null);
1825 } else {
1826 nextHopIp = gwHost.ipAddresses()
1827 .stream()
1828 .filter(IpAddress::isIp6)
1829 .filter(ip6 -> ip6.isLinkLocal())
1830 .map(IpAddress::getIp6Address)
1831 .findFirst()
1832 .orElse(null);
1833 }
Kalhee Kim121ba922017-11-01 17:56:44 +00001834 return nextHopIp;
1835 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +00001836
Kalhee Kim495c9b22017-11-07 16:32:09 +00001837 private List<DhcpServerInfo> findValidServerInfo(boolean directConnFlag) {
1838 List<DhcpServerInfo> validServerInfo;
1839
1840 if (directConnFlag || indirectServerInfoList.isEmpty()) {
1841 validServerInfo = new ArrayList<DhcpServerInfo>(defaultServerInfoList);
1842 } else {
1843 validServerInfo = new ArrayList<DhcpServerInfo>(indirectServerInfoList);
1844 }
1845 return validServerInfo;
1846 }
1847
1848 private DhcpServerInfo findServerInfoFromServer(boolean directConnFlag, ConnectPoint inPort) {
1849 List<DhcpServerInfo> validServerInfoList = findValidServerInfo(directConnFlag);
1850 DhcpServerInfo foundServerInfo = null;
1851 for (DhcpServerInfo serverInfo : validServerInfoList) {
1852 if (inPort.equals(serverInfo.getDhcpServerConnectPoint().get())) {
1853 foundServerInfo = serverInfo;
Charles Chan750c9992018-02-26 10:44:49 -08001854 log.debug("ServerInfo found for Rcv port {} Server Connect Point {} for {}",
Kalhee Kim495c9b22017-11-07 16:32:09 +00001855 inPort, serverInfo.getDhcpServerConnectPoint(), directConnFlag ? "direct" : "indirect");
1856 break;
1857 } else {
1858 log.warn("Rcv port {} not the same as Server Connect Point {} for {}",
1859 inPort, serverInfo.getDhcpServerConnectPoint(), directConnFlag ? "direct" : "indirect");
1860 }
1861 }
1862 return foundServerInfo;
1863 }
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001864
Kalhee Kim495c9b22017-11-07 16:32:09 +00001865 /**
1866 * Set the dhcp6 lease expiry poll interval value.
1867 *
1868 * @param val poll interval value in seconds
1869 */
Kalhee Kim2f07c602017-11-15 18:57:53 +00001870 @Override
Kalhee Kim495c9b22017-11-07 16:32:09 +00001871 public void setDhcp6PollInterval(int val) {
1872 dhcp6PollInterval = val;
1873 }
1874
Kalhee Kim2f07c602017-11-15 18:57:53 +00001875 /**
1876 * get the dhcp6 lease expiry poll interval value.
1877 * This is a private function
1878 * @return poll interval value in seconds
1879 */
1880 private int getDhcp6PollInterval() {
1881 return dhcp6PollInterval;
1882 }
1883
1884 /**
1885 * Find lease-expired ipaddresses and pd prefixes.
1886 * Removing host/route/fpm entries.
1887 */
1888 public void timeTick() {
1889 long currentTime = System.currentTimeMillis();
1890 Collection<DhcpRecord> records = dhcpRelayStore.getDhcpRecords();
1891
1892 log.debug("timeTick called currenttime {} records num {} ", currentTime, records.size());
1893
1894 records.forEach(record -> {
1895 boolean addrOrPdRemoved = false;
1896 DHCP6.MsgType ip6Status = record.ip6Status().orElse(null);
1897 if (ip6Status == null) {
1898 log.debug("record is not valid v6 record.");
1899 return;
1900 }
1901
1902 if ((currentTime - record.getLastIp6Update()) >
1903 ((record.addrPrefTime() + getDhcp6PollInterval() / 2) * 1000)) {
1904 // remove ipaddress from host/route table
1905 IpAddress ip = record.ip6Address().orElse(null);
1906 if (ip != null) {
1907 if (record.directlyConnected()) {
1908 providerService.removeIpFromHost(HostId.hostId(record.macAddress(),
1909 record.vlanId()), ip);
1910 } else {
1911 MacAddress gwMac = record.nextHop().orElse(null);
1912 if (gwMac == null) {
1913 log.warn("Can't find gateway mac address from record {} for ip6Addr", record);
1914 return;
1915 }
1916 IpAddress nextHopIp = getFirstIpByHost(record.directlyConnected(),
1917 gwMac,
1918 record.vlanId());
1919 Route route = new Route(Route.Source.STATIC, ip.toIpPrefix(), nextHopIp);
1920 routeStore.removeRoute(route);
1921 }
1922 record.updateAddrPrefTime(0);
1923 record.ip6Address(null);
1924 addrOrPdRemoved = true;
1925 dhcpRelayStore.updateDhcpRecord(HostId.hostId(record.macAddress(),
1926 record.vlanId()), record);
1927 log.warn("IP6 address is set to null. delta {} lastUpdate {} addrPrefTime {}",
1928 (currentTime - record.getLastIp6Update()), record.getLastIp6Update(),
1929 record.addrPrefTime());
1930 }
1931 }
1932 if ((currentTime - record.getLastPdUpdate()) >
1933 ((record.pdPrefTime() + getDhcp6PollInterval() / 2) * 1000)) {
1934 // remove PD from route/fpm table
1935 IpPrefix pdIpPrefix = record.pdPrefix().orElse(null);
1936 if (pdIpPrefix != null) {
1937 if (record.directlyConnected()) {
1938 providerService.removeIpFromHost(HostId.hostId(record.macAddress(), record.vlanId()),
1939 pdIpPrefix.address().getIp6Address());
1940 } else {
1941 MacAddress gwMac = record.nextHop().orElse(null);
1942 if (gwMac == null) {
1943 log.warn("Can't find gateway mac address from record {} for PD prefix", record);
1944 return;
1945 }
1946 IpAddress nextHopIp = getFirstIpByHost(record.directlyConnected(),
1947 gwMac,
1948 record.vlanId());
1949 Route route = new Route(Route.Source.STATIC, pdIpPrefix, nextHopIp);
1950 routeStore.removeRoute(route);
1951 if (this.dhcpFpmEnabled) {
1952 dhcpFpmPrefixStore.removeFpmRecord(pdIpPrefix);
1953 }
1954 }
1955 record.updatePdPrefTime(0);
1956 record.pdPrefix(null);
1957 addrOrPdRemoved = true;
1958 dhcpRelayStore.updateDhcpRecord(HostId.hostId(record.macAddress(),
1959 record.vlanId()), record);
1960 log.warn("PD prefix is set to null.delta {} pdPrefTime {}",
1961 (currentTime - record.getLastPdUpdate()), record.pdPrefTime());
1962 }
1963 }
1964 if (addrOrPdRemoved &&
1965 !record.ip6Address().isPresent() && !record.pdPrefix().isPresent()) {
1966 log.warn("ip6Status {} IP6 address and IP6 PD both are null. Remove record.", ip6Status);
1967 dhcpRelayStore.removeDhcpRecord(HostId.hostId(record.macAddress(), record.vlanId()));
1968 }
1969 }
1970 );
1971 }
1972}