blob: 80d29fcb91a9b6534bde54f09f9fc0d64bc0c952 [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;
Yi Tseng525ff402017-10-23 19:39:39 -0700114import java.util.concurrent.atomic.AtomicInteger;
Kalhee Kim45fede42017-09-05 19:05:06 +0000115import static com.google.common.base.Preconditions.checkNotNull;
116import static com.google.common.base.Preconditions.checkState;
Yi Tseng525ff402017-10-23 19:39:39 -0700117import static org.onosproject.net.flowobjective.Objective.Operation.ADD;
118import static org.onosproject.net.flowobjective.Objective.Operation.REMOVE;
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000119import java.util.concurrent.Semaphore;
Yi Tseng51301292017-07-28 13:02:59 -0700120
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800121
Yi Tseng51301292017-07-28 13:02:59 -0700122@Component
123@Service
124@Property(name = "version", value = "6")
Yi Tsengaa417a62017-09-08 17:22:51 -0700125public class Dhcp6HandlerImpl implements DhcpHandler, HostProvider {
Charles Chand988c282017-09-12 17:09:32 -0700126 public static final String DHCP_V6_RELAY_APP = "org.onosproject.Dhcp6HandlerImpl";
Saurav Das7c6dec12017-09-13 14:35:56 -0700127 public static final ProviderId PROVIDER_ID = new ProviderId("dhcp6", DHCP_V6_RELAY_APP);
Yi Tseng525ff402017-10-23 19:39:39 -0700128 private static final int IGNORE_CONTROL_PRIORITY = PacketPriority.CONTROL.priorityValue() + 1000;
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000129 private String gCount = "global";
Yi Tseng525ff402017-10-23 19:39:39 -0700130 private static final TrafficSelector CLIENT_SERVER_SELECTOR = DefaultTrafficSelector.builder()
131 .matchEthType(Ethernet.TYPE_IPV6)
132 .matchIPProtocol(IPv6.PROTOCOL_UDP)
133 .matchIPv6Src(IpPrefix.IPV6_LINK_LOCAL_PREFIX)
134 .matchIPv6Dst(Ip6Address.ALL_DHCP_RELAY_AGENTS_AND_SERVERS.toIpPrefix())
135 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_V6_CLIENT_PORT))
136 .matchUdpDst(TpPort.tpPort(UDP.DHCP_V6_SERVER_PORT))
137 .build();
138 private static final TrafficSelector SERVER_RELAY_SELECTOR = DefaultTrafficSelector.builder()
139 .matchEthType(Ethernet.TYPE_IPV6)
140 .matchIPProtocol(IPv6.PROTOCOL_UDP)
141 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_V6_SERVER_PORT))
142 .matchUdpDst(TpPort.tpPort(UDP.DHCP_V6_SERVER_PORT))
143 .build();
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800144 // lease query reply is from server to client (no relay in between) - so we need to
145 // catch that scenario also ..
146 private static final TrafficSelector LEASE_QUERY_RESPONSE_SELECTOR = DefaultTrafficSelector.builder()
147 .matchEthType(Ethernet.TYPE_IPV6)
148 .matchIPProtocol(IPv6.PROTOCOL_UDP)
149 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_V6_SERVER_PORT))
150 .matchUdpDst(TpPort.tpPort(UDP.DHCP_V6_CLIENT_PORT))
151 .build();
Yi Tseng525ff402017-10-23 19:39:39 -0700152 static final Set<TrafficSelector> DHCP_SELECTORS = ImmutableSet.of(
153 CLIENT_SERVER_SELECTOR,
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800154 SERVER_RELAY_SELECTOR,
155 LEASE_QUERY_RESPONSE_SELECTOR
Yi Tseng525ff402017-10-23 19:39:39 -0700156 );
Kalhee Kim45fede42017-09-05 19:05:06 +0000157 private static Logger log = LoggerFactory.getLogger(Dhcp6HandlerImpl.class);
Yi Tseng51301292017-07-28 13:02:59 -0700158
Kalhee Kim45fede42017-09-05 19:05:06 +0000159 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
160 protected DhcpRelayStore dhcpRelayStore;
Yi Tseng51301292017-07-28 13:02:59 -0700161
Kalhee Kim45fede42017-09-05 19:05:06 +0000162 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000163 protected DhcpRelayCountersStore dhcpRelayCountersStore;
164
165 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Kalhee Kim45fede42017-09-05 19:05:06 +0000166 protected PacketService packetService;
167
168 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Kalhee Kim45fede42017-09-05 19:05:06 +0000169 protected RouteStore routeStore;
170
171 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
172 protected InterfaceService interfaceService;
173
174 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
175 protected HostService hostService;
176
Yi Tsengaa417a62017-09-08 17:22:51 -0700177 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
178 protected HostProviderRegistry providerRegistry;
Kalhee Kim45fede42017-09-05 19:05:06 +0000179
Yi Tseng525ff402017-10-23 19:39:39 -0700180 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
181 protected CoreService coreService;
182
183 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Kalhee Kimba366062017-11-07 16:32:09 +0000184 protected DhcpFpmPrefixStore dhcpFpmPrefixStore;
185
186 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Yi Tseng525ff402017-10-23 19:39:39 -0700187 protected DeviceService deviceService;
188
189 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
190 protected FlowObjectiveService flowObjectiveService;
191
Yi Tsengaa417a62017-09-08 17:22:51 -0700192 protected HostProviderService providerService;
Yi Tseng525ff402017-10-23 19:39:39 -0700193 protected ApplicationId appId;
194 protected Multimap<DeviceId, VlanId> ignoredVlans = HashMultimap.create();
195 private InternalHostListener hostListener = new InternalHostListener();
Kalhee Kimba366062017-11-07 16:32:09 +0000196 private Boolean dhcpFpmEnabled = false;
Kalhee Kim495c9b22017-11-07 16:32:09 +0000197 private Dhcp6HandlerUtil dhcp6HandlerUtil = new Dhcp6HandlerUtil();
Yi Tseng919b2df2017-09-07 16:22:51 -0700198 private List<DhcpServerInfo> defaultServerInfoList = Lists.newArrayList();
199 private List<DhcpServerInfo> indirectServerInfoList = Lists.newArrayList();
Kalhee Kim495c9b22017-11-07 16:32:09 +0000200 private class IpAddressInfo {
201 Ip6Address ip6Address;
202 long prefTime;
203 }
204 private class PdPrefixInfo {
205 IpPrefix pdPrefix;
206 long prefTime;
207 }
208 protected int dhcp6PollInterval = 24 * 3600; // 24 hr period
209
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000210 // max 1 thread
211 static Semaphore recordSemaphore = new Semaphore(1);
Kalhee Kim45fede42017-09-05 19:05:06 +0000212
213 // CLIENT message types
214 public static final Set<Byte> MSG_TYPE_FROM_CLIENT =
215 ImmutableSet.of(DHCP6.MsgType.SOLICIT.value(),
216 DHCP6.MsgType.REQUEST.value(),
217 DHCP6.MsgType.REBIND.value(),
218 DHCP6.MsgType.RENEW.value(),
219 DHCP6.MsgType.RELEASE.value(),
220 DHCP6.MsgType.DECLINE.value(),
221 DHCP6.MsgType.CONFIRM.value(),
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800222 DHCP6.MsgType.RELAY_FORW.value(),
223 DHCP6.MsgType.LEASEQUERY.value());
Kalhee Kim45fede42017-09-05 19:05:06 +0000224 // SERVER message types
225 public static final Set<Byte> MSG_TYPE_FROM_SERVER =
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800226 ImmutableSet.of(DHCP6.MsgType.RELAY_REPL.value(),
227 DHCP6.MsgType.LEASEQUERY_REPLY.value());
Kalhee Kim45fede42017-09-05 19:05:06 +0000228
229 @Activate
230 protected void activate() {
Yi Tseng525ff402017-10-23 19:39:39 -0700231 appId = coreService.registerApplication(DHCP_V6_RELAY_APP);
Yi Tsengaa417a62017-09-08 17:22:51 -0700232 providerService = providerRegistry.register(this);
Kalhee Kim45fede42017-09-05 19:05:06 +0000233 hostService.addListener(hostListener);
Yi Tseng51301292017-07-28 13:02:59 -0700234 }
235
Kalhee Kim45fede42017-09-05 19:05:06 +0000236 @Deactivate
237 protected void deactivate() {
Yi Tsengaa417a62017-09-08 17:22:51 -0700238 providerRegistry.unregister(this);
Kalhee Kim45fede42017-09-05 19:05:06 +0000239 hostService.removeListener(hostListener);
Yi Tseng919b2df2017-09-07 16:22:51 -0700240 defaultServerInfoList.forEach(this::stopMonitoringIps);
241 defaultServerInfoList.clear();
242 indirectServerInfoList.forEach(this::stopMonitoringIps);
243 indirectServerInfoList.clear();
244 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000245
Yi Tseng919b2df2017-09-07 16:22:51 -0700246 private void stopMonitoringIps(DhcpServerInfo serverInfo) {
247 serverInfo.getDhcpGatewayIp6().ifPresent(gatewayIp -> {
248 hostService.stopMonitoringIp(gatewayIp);
249 });
250 serverInfo.getDhcpServerIp6().ifPresent(serverIp -> {
251 hostService.stopMonitoringIp(serverIp);
252 });
Yi Tseng51301292017-07-28 13:02:59 -0700253 }
254
Yi Tseng51301292017-07-28 13:02:59 -0700255 @Override
Yi Tseng919b2df2017-09-07 16:22:51 -0700256 public List<DhcpServerInfo> getDefaultDhcpServerInfoList() {
257 return defaultServerInfoList;
Yi Tseng51301292017-07-28 13:02:59 -0700258 }
259
260 @Override
Yi Tseng919b2df2017-09-07 16:22:51 -0700261 public List<DhcpServerInfo> getIndirectDhcpServerInfoList() {
262 return indirectServerInfoList;
Yi Tseng51301292017-07-28 13:02:59 -0700263 }
264
265 @Override
Yi Tseng525ff402017-10-23 19:39:39 -0700266 public void updateIgnoreVlanConfig(IgnoreDhcpConfig config) {
267 if (config == null) {
268 ignoredVlans.forEach(((deviceId, vlanId) -> {
269 processIgnoreVlanRule(deviceId, vlanId, REMOVE);
270 }));
271 return;
272 }
273 config.ignoredVlans().forEach((deviceId, vlanId) -> {
274 if (ignoredVlans.get(deviceId).contains(vlanId)) {
275 // don't need to process if it already ignored
276 return;
277 }
278 processIgnoreVlanRule(deviceId, vlanId, ADD);
279 });
Yi Tseng525ff402017-10-23 19:39:39 -0700280 ignoredVlans.forEach((deviceId, vlanId) -> {
281 if (!config.ignoredVlans().get(deviceId).contains(vlanId)) {
282 // not contains in new config, remove it
283 processIgnoreVlanRule(deviceId, vlanId, REMOVE);
284 }
285 });
286 }
287
288 @Override
Saurav Dasb805f1a2017-12-13 16:19:35 -0800289 public void removeIgnoreVlanState(IgnoreDhcpConfig config) {
290 if (config == null) {
291 ignoredVlans.clear();
292 return;
293 }
294 config.ignoredVlans().forEach((deviceId, vlanId) -> {
295 ignoredVlans.remove(deviceId, vlanId);
296 });
297 }
298
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800299 public DhcpRecord getDhcpRelayRecordFor(Ip6Address clientAddress) {
300
301 Collection<DhcpRecord> records = dhcpRelayStore.getDhcpRecords();
302 DhcpRecord dr = null;
303 for (DhcpRecord e:records) {
304 if (e.ip6Address().isPresent()) {
305 if (e.ip6Address().get().equals(clientAddress)) {
306 dr = e;
307 break;
308 }
309 }
310 }
311 return dr;
312 }
313
314 public MacAddress findNextHopMacForIp6FromRelayStore(Ip6Address clientAddress,
315 MacAddress clientMacAddress, VlanId vlanID) {
316
317 DhcpRecord dr = getDhcpRelayRecordFor(clientAddress);
318
319 if (dr != null) {
320 Optional<MacAddress> nextHopTempMac = dr.nextHopTemp();
321 if (nextHopTempMac.isPresent()) {
322 log.info("findNextHopForIp6FromRelayStore " + clientAddress + " got mac " + nextHopTempMac.toString());
323 return nextHopTempMac.get();
324 }
325 } else {
326 log.warn("findNextHopMacForIp6FromRelayStore could NOT find next hop for " + clientAddress);
327 return null;
328 }
329 return null;
330 }
331
332 public Ip6Address findNextHopIp6FromRelayStore(Ip6Address clientAddress) {
333
334 DhcpRecord dr = getDhcpRelayRecordFor(clientAddress);
335 if (dr != null) {
336 Optional<MacAddress> nextHopMac = dr.nextHop();
337 if (nextHopMac.isPresent()) {
338 // find the local ip6 from the host store
339 HostId gwHostId = HostId.hostId(nextHopMac.get(), dr.vlanId());
340 Host gwHost = hostService.getHost(gwHostId);
341 if (gwHost == null) {
342 log.warn("Can't find next hop host ID {}", gwHostId);
343 return null;
344 }
345 Ip6Address nextHopIp = gwHost.ipAddresses()
346 .stream()
347 .filter(IpAddress::isIp6)
348 .filter(IpAddress::isLinkLocal)
349 .map(IpAddress::getIp6Address)
350 .findFirst()
351 .orElse(null);
352
353 log.info("findNextHopIp6FromRelayStore " + clientAddress + " got mac " +
354 nextHopMac.toString() + " ip6 " + nextHopIp);
355 return nextHopIp;
356 }
357 } else {
358 log.warn("findNextHopIp6FromRelayStore could NOT find next hop for " + clientAddress);
359 return null;
360 }
361 return null;
362 }
363
364 private void setPotentialNextHopForIp6InRelayStore(Ip6Address clientAddress,
365 VlanId vlanId, MacAddress nh) {
366 DhcpRecord dr = getDhcpRelayRecordFor(clientAddress);
367 if (dr != null) {
368 dr.nextHopTemp(nh);
369 log.debug("LQ6 potential NH mac " + nh.toString() + " UPDATED in RelayRecord client " + clientAddress);
370 } else {
371 log.warn("LQ6 potential NH mac" + nh.toString() +
372 " NOT FOUND in RelayRecord for client - LQ rejected" + clientAddress);
373 }
374 }
375
376 public void handleLeaseQuery6ReplyMsg(PacketContext context, DHCP6 dhcp6Payload) {
377 ConnectPoint inPort = context.inPacket().receivedFrom();
378 log.info("Got LQV6-REPLY on port {}", inPort);
379 List<Dhcp6Option> lopt = dhcp6Payload.getOptions();
380 log.info("Options list: {}", lopt);
381 // find out if this lease is known is
382 Dhcp6ClientDataOption clientDataOption = dhcp6Payload.getOptions()
383 .stream()
384 .filter(opt -> opt instanceof Dhcp6ClientDataOption)
385 .map(pld -> (Dhcp6ClientDataOption) pld)
386 .findFirst()
387 .orElse(null);
388
389 if (clientDataOption == null) {
390 log.warn("clientDataOption option is not present, " +
391 "lease is UNKNOWN - not adding any new route...");
392 } else {
393 Dhcp6IaAddressOption aiAddressOption = clientDataOption.getOptions()
394 .stream()
395 .filter(opt -> opt instanceof Dhcp6IaAddressOption)
396 .map(pld -> (Dhcp6IaAddressOption) pld)
397 .findFirst()
398 .orElse(null);
399
400 Dhcp6ClientIdOption clientIdOption = clientDataOption.getOptions()
401 .stream()
402 .filter(opt -> opt instanceof Dhcp6ClientIdOption)
403 .map(pld -> (Dhcp6ClientIdOption) pld)
404 .findFirst()
405 .orElse(null);
406
407 if (aiAddressOption == null) {
408 log.warn("clientDataOption from DHCP server does not " +
409 "contains Dhcp6IaAddressOption for the client - giving up...");
410 } else {
411 Ip6Address clientAddress = aiAddressOption.getIp6Address();
412 MacAddress clientMacAddress = MacAddress.valueOf(clientIdOption.getDuid().getLinkLayerAddress());
413 Ethernet packet = context.inPacket().parsed();
414 VlanId vlanId = VlanId.vlanId(packet.getVlanID());
415 MacAddress potentialNextHopMac =
416 findNextHopMacForIp6FromRelayStore(clientAddress, clientMacAddress, vlanId);
417
418 if (potentialNextHopMac == null) {
419 log.warn("Can't find next hop host mac for client {} mac:{}/{}",
420 clientAddress, clientMacAddress, vlanId);
421 return;
422 } else {
423 log.info("Next hop mac for {}/{}/{} is {}", clientAddress,
424 clientMacAddress, vlanId, potentialNextHopMac.toString());
425 }
426 // search the next hop in the hosts store
427 HostId gwHostId = HostId.hostId(potentialNextHopMac, vlanId);
428 Host gwHost = hostService.getHost(gwHostId);
429 if (gwHost == null) {
430 log.warn("Can't find next hop host ID {}", gwHostId);
431 return;
432 }
433 Ip6Address nextHopIp = gwHost.ipAddresses()
434 .stream()
435 .filter(IpAddress::isIp6)
436 .filter(IpAddress::isLinkLocal)
437 .map(IpAddress::getIp6Address)
438 .findFirst()
439 .orElse(null);
440 if (nextHopIp == null) {
441 log.warn("Can't find IP6 address of next hop {}", gwHost);
442 return;
443 }
444 log.info("client " + clientAddress + " is known !");
445 Route routeForIP6 = new Route(Route.Source.STATIC, clientAddress.toIpPrefix(), nextHopIp);
446 log.debug("updating route of Client for indirectly connected.");
447 log.debug("client ip: " + clientAddress + ", next hop ip6: " + nextHopIp);
448 routeStore.updateRoute(routeForIP6);
449 }
450 }
451 }
452
Saurav Dasb805f1a2017-12-13 16:19:35 -0800453 @Override
Kalhee Kim45fede42017-09-05 19:05:06 +0000454 public void processDhcpPacket(PacketContext context, BasePacket payload) {
455 checkNotNull(payload, "DHCP6 payload can't be null");
456 checkState(payload instanceof DHCP6, "Payload is not a DHCP6");
457 DHCP6 dhcp6Payload = (DHCP6) payload;
458 Ethernet receivedPacket = context.inPacket().parsed();
459
460 if (!configured()) {
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800461 log.warn("Missing DHCP6 relay server config. " +
462 "Abort packet processing dhcp6 payload {}", dhcp6Payload);
Kalhee Kim45fede42017-09-05 19:05:06 +0000463 return;
464 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000465 byte msgTypeVal = dhcp6Payload.getMsgType();
466 MsgType msgType = DHCP6.MsgType.getType(msgTypeVal);
467 log.debug("msgType is {}", msgType);
Kalhee Kim45fede42017-09-05 19:05:06 +0000468
Kalhee Kim45fede42017-09-05 19:05:06 +0000469 ConnectPoint inPort = context.inPacket().receivedFrom();
Jonathan Hart8ca2bc02017-11-30 18:23:42 -0800470
Kalhee Kim495c9b22017-11-07 16:32:09 +0000471 if (inPort == null) {
472 log.warn("incomming ConnectPoint is null");
473 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000474 Set<Interface> receivingInterfaces = interfaceService.getInterfacesByPort(inPort);
475 //ignore the packets if dhcp client interface is not configured on onos.
476 if (receivingInterfaces.isEmpty()) {
477 log.warn("Virtual interface is not configured on {}", inPort);
478 return;
479 }
480
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800481 if (msgTypeVal == DHCP6.MsgType.LEASEQUERY.value()) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000482 List<InternalPacket> ethernetClientPacket =
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800483 processLQ6PacketFromClient(context, receivedPacket, receivingInterfaces, dhcp6Payload);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000484 for (InternalPacket internalPacket : ethernetClientPacket) {
485 forwardPacket(internalPacket);
Kalhee Kim45fede42017-09-05 19:05:06 +0000486 }
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800487 } else if (msgTypeVal == DHCP6.MsgType.LEASEQUERY_REPLY.value()) {
488
489 IPv6 clientIpv6 = (IPv6) receivedPacket.getPayload();
490 UDP clientUdp = (UDP) clientIpv6.getPayload();
491 DHCP6 clientDhcp6 = (DHCP6) clientUdp.getPayload();
492 Interface serverInterface = dhcp6HandlerUtil.directlyConnected(clientDhcp6) ?
493 getServerInterface() : getIndirectServerInterface();
Kalhee Kim45fede42017-09-05 19:05:06 +0000494 InternalPacket ethernetPacketReply =
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800495 dhcp6HandlerUtil.processLQ6PacketFromServer(
496 defaultServerInfoList, indirectServerInfoList,
497 serverInterface, interfaceService,
498 hostService,
499 context, receivedPacket, receivingInterfaces);
Kalhee Kim45fede42017-09-05 19:05:06 +0000500 if (ethernetPacketReply != null) {
501 forwardPacket(ethernetPacketReply);
502 }
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800503 handleLeaseQuery6ReplyMsg(context, dhcp6Payload);
Kalhee Kim45fede42017-09-05 19:05:06 +0000504 } else {
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800505 if (MSG_TYPE_FROM_CLIENT.contains(msgTypeVal)) {
506
507 List<InternalPacket> ethernetClientPacket =
508 processDhcp6PacketFromClient(context, receivedPacket, receivingInterfaces);
509 for (InternalPacket internalPacket : ethernetClientPacket) {
510 forwardPacket(internalPacket);
511 }
512 } else if (MSG_TYPE_FROM_SERVER.contains(msgTypeVal)) {
513 log.debug("calling processDhcp6PacketFromServer with RELAY_REPL {}", msgTypeVal);
514 InternalPacket ethernetPacketReply =
515 processDhcp6PacketFromServer(context, receivedPacket, receivingInterfaces);
516 if (ethernetPacketReply != null) {
517 forwardPacket(ethernetPacketReply);
518 }
519 } else {
520 log.warn("Not so fast, packet type {} not supported yet", msgTypeVal);
521 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000522 }
523 }
524
Kalhee Kim45fede42017-09-05 19:05:06 +0000525 /**
526 * Checks if this app has been configured.
527 *
528 * @return true if all information we need have been initialized
529 */
530 public boolean configured() {
Yi Tseng919b2df2017-09-07 16:22:51 -0700531 return !defaultServerInfoList.isEmpty();
Kalhee Kim45fede42017-09-05 19:05:06 +0000532 }
533
Yi Tsengaa417a62017-09-08 17:22:51 -0700534 @Override
535 public ProviderId id() {
Charles Chand988c282017-09-12 17:09:32 -0700536 return PROVIDER_ID;
Yi Tsengaa417a62017-09-08 17:22:51 -0700537 }
538
539 @Override
540 public void triggerProbe(Host host) {
541 // Do nothing here
542 }
543
Kalhee Kim45fede42017-09-05 19:05:06 +0000544 //forward the packet to ConnectPoint where the DHCP server is attached.
545 private void forwardPacket(InternalPacket packet) {
546 //send Packetout to dhcp server connectpoint.
547 if (packet.destLocation != null) {
548 TrafficTreatment t = DefaultTrafficTreatment.builder()
549 .setOutput(packet.destLocation.port()).build();
550 OutboundPacket o = new DefaultOutboundPacket(
551 packet.destLocation.deviceId(), t, ByteBuffer.wrap(packet.packet.serialize()));
552 if (log.isTraceEnabled()) {
553 log.trace("Relaying packet to destination {}", packet.destLocation);
554 }
555 packetService.emit(o);
556 } // if
557 }
558
Kalhee Kim45fede42017-09-05 19:05:06 +0000559 /**
560 * extract from dhcp6 packet client ipv6 address of given by dhcp server.
561 *
562 * @param dhcp6 the dhcp6 packet
Kalhee Kim495c9b22017-11-07 16:32:09 +0000563 * @return IpAddressInfo IpAddressInfo given by dhcp server, or null if not exists
Kalhee Kim45fede42017-09-05 19:05:06 +0000564 */
Kalhee Kim495c9b22017-11-07 16:32:09 +0000565 private IpAddressInfo extractIpAddress(DHCP6 dhcp6) {
566 IpAddressInfo ipInfo = new IpAddressInfo();
Kalhee Kim45fede42017-09-05 19:05:06 +0000567
568 log.debug("extractIpAddress enters dhcp6 {}.", dhcp6);
569 // Extract IPv6 address from IA NA ot IA TA option
570 Optional<Dhcp6IaNaOption> iaNaOption = dhcp6.getOptions()
571 .stream()
572 .filter(opt -> opt instanceof Dhcp6IaNaOption)
573 .map(opt -> (Dhcp6IaNaOption) opt)
574 .findFirst();
575 Optional<Dhcp6IaTaOption> iaTaOption = dhcp6.getOptions()
576 .stream()
577 .filter(opt -> opt instanceof Dhcp6IaTaOption)
578 .map(opt -> (Dhcp6IaTaOption) opt)
579 .findFirst();
580 Optional<Dhcp6IaAddressOption> iaAddressOption;
581 if (iaNaOption.isPresent()) {
582 log.debug("Found IPv6 address from iaNaOption {}", iaNaOption);
583
584 iaAddressOption = iaNaOption.get().getOptions().stream()
585 .filter(opt -> opt instanceof Dhcp6IaAddressOption)
586 .map(opt -> (Dhcp6IaAddressOption) opt)
587 .findFirst();
588 } else if (iaTaOption.isPresent()) {
589 log.debug("Found IPv6 address from iaTaOption {}", iaTaOption);
590
591 iaAddressOption = iaTaOption.get().getOptions().stream()
592 .filter(opt -> opt instanceof Dhcp6IaAddressOption)
593 .map(opt -> (Dhcp6IaAddressOption) opt)
594 .findFirst();
595 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000596 log.info("No IPv6 address found from iaTaOption {}", iaTaOption);
Kalhee Kim45fede42017-09-05 19:05:06 +0000597 iaAddressOption = Optional.empty();
598 }
599 if (iaAddressOption.isPresent()) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000600 ipInfo.ip6Address = iaAddressOption.get().getIp6Address();
601 ipInfo.prefTime = iaAddressOption.get().getPreferredLifetime();
Kalhee Kim45fede42017-09-05 19:05:06 +0000602 log.debug("Found IPv6 address from iaAddressOption {}", iaAddressOption);
Kalhee Kim45fede42017-09-05 19:05:06 +0000603 } else {
604 log.debug("Can't find IPv6 address from DHCPv6 {}", dhcp6);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000605 return null;
Kalhee Kim45fede42017-09-05 19:05:06 +0000606 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000607 return ipInfo;
Kalhee Kim45fede42017-09-05 19:05:06 +0000608 }
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800609
Kalhee Kim45fede42017-09-05 19:05:06 +0000610 /**
611 * extract from dhcp6 packet Prefix prefix provided by dhcp server.
612 *
613 * @param dhcp6 the dhcp6 payload
614 * @return IpPrefix Prefix Delegation prefix, or null if not exists.
615 */
Kalhee Kim495c9b22017-11-07 16:32:09 +0000616 private PdPrefixInfo extractPrefix(DHCP6 dhcp6) {
617 log.debug("extractPrefix enters {}", dhcp6);
Kalhee Kim45fede42017-09-05 19:05:06 +0000618
619 // extract prefix
Kalhee Kim495c9b22017-11-07 16:32:09 +0000620 PdPrefixInfo pdPrefixInfo = new PdPrefixInfo();
Kalhee Kim45fede42017-09-05 19:05:06 +0000621
622 Ip6Address prefixAddress = null;
623
624 // Extract IPv6 prefix from IA PD option
625 Optional<Dhcp6IaPdOption> iaPdOption = dhcp6.getOptions()
626 .stream()
627 .filter(opt -> opt instanceof Dhcp6IaPdOption)
628 .map(opt -> (Dhcp6IaPdOption) opt)
629 .findFirst();
630
631 Optional<Dhcp6IaPrefixOption> iaPrefixOption;
632 if (iaPdOption.isPresent()) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000633 log.debug("IA_PD option found {}", iaPdOption);
Kalhee Kim45fede42017-09-05 19:05:06 +0000634
635 iaPrefixOption = iaPdOption.get().getOptions().stream()
636 .filter(opt -> opt instanceof Dhcp6IaPrefixOption)
637 .map(opt -> (Dhcp6IaPrefixOption) opt)
638 .findFirst();
639 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000640 log.debug("IA_PD option NOT found");
Kalhee Kim45fede42017-09-05 19:05:06 +0000641
642 iaPrefixOption = Optional.empty();
643 }
644 if (iaPrefixOption.isPresent()) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000645 log.debug("IAPrefix Option within IA_PD option found {}", iaPrefixOption);
Kalhee Kim45fede42017-09-05 19:05:06 +0000646
647 prefixAddress = iaPrefixOption.get().getIp6Prefix();
648 int prefixLen = (int) iaPrefixOption.get().getPrefixLength();
Kalhee Kim495c9b22017-11-07 16:32:09 +0000649 log.debug("Prefix length is {} bits", prefixLen);
650 pdPrefixInfo.pdPrefix = IpPrefix.valueOf(prefixAddress, prefixLen);
651 pdPrefixInfo.prefTime = iaPrefixOption.get().getPreferredLifetime();
Kalhee Kim45fede42017-09-05 19:05:06 +0000652 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000653 log.debug("Can't find IPv6 prefix from DHCPv6 {}", dhcp6);
654 return null;
Kalhee Kim45fede42017-09-05 19:05:06 +0000655 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000656 return pdPrefixInfo;
Kalhee Kim45fede42017-09-05 19:05:06 +0000657 }
658
659 /**
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000660 * extract from dhcp6 packet ClientIdOption.
661 *
662 * @param directConnFlag directly connected host
663 * @param dhcp6Payload the dhcp6 payload
664 * @return Dhcp6ClientIdOption clientIdOption, or null if not exists.
665 */
666 private Dhcp6ClientIdOption extractClinedId(Boolean directConnFlag, DHCP6 dhcp6Payload) {
667 Dhcp6ClientIdOption clientIdOption;
668
669 if (directConnFlag) {
670 clientIdOption = dhcp6Payload.getOptions()
671 .stream()
672 .filter(opt -> opt instanceof Dhcp6ClientIdOption)
673 .map(opt -> (Dhcp6ClientIdOption) opt)
674 .findFirst()
675 .orElse(null);
676 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000677 DHCP6 leafDhcp = dhcp6HandlerUtil.getDhcp6Leaf(dhcp6Payload);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000678 clientIdOption = leafDhcp.getOptions()
679 .stream()
680 .filter(opt -> opt instanceof Dhcp6ClientIdOption)
681 .map(opt -> (Dhcp6ClientIdOption) opt)
682 .findFirst()
683 .orElse(null);
684 }
685
686 return clientIdOption;
687 }
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800688
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000689 /**
Kalhee Kim495c9b22017-11-07 16:32:09 +0000690 * remove host or route and update dhcp relay record attributes.
Kalhee Kim45fede42017-09-05 19:05:06 +0000691 *
692 * @param directConnFlag flag to show that packet is from directly connected client
Kalhee Kim495c9b22017-11-07 16:32:09 +0000693 * @param location client side connect point
Kalhee Kim45fede42017-09-05 19:05:06 +0000694 * @param dhcp6Packet the dhcp6 payload
695 * @param clientPacket client's ethernet packet
696 * @param clientIpv6 client's Ipv6 packet
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +0000697 * @param clientInterface client interfaces
Kalhee Kim45fede42017-09-05 19:05:06 +0000698 */
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000699 private void removeHostOrRoute(boolean directConnFlag, ConnectPoint location,
700 DHCP6 dhcp6Packet,
Kalhee Kim45fede42017-09-05 19:05:06 +0000701 Ethernet clientPacket, IPv6 clientIpv6,
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +0000702 Interface clientInterface) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000703 log.debug("removeHostOrRoute enters {}", dhcp6Packet);
Kalhee Kim121ba922017-11-01 17:56:44 +0000704 VlanId vlanId = clientInterface.vlan();
Kalhee Kim495c9b22017-11-07 16:32:09 +0000705 MacAddress srcMac = clientPacket.getSourceMAC(); // could be gw or host
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000706 MacAddress leafClientMac;
707 byte leafMsgType;
Kalhee Kim495c9b22017-11-07 16:32:09 +0000708 log.debug("client mac {} client vlan {}", HexString.toHexString(srcMac.toBytes(), ":"), vlanId);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000709
710 Dhcp6ClientIdOption clientIdOption = extractClinedId(directConnFlag, dhcp6Packet);
711 if (clientIdOption != null) {
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000712 if ((clientIdOption.getDuid().getDuidType() == Dhcp6Duid.DuidType.DUID_LLT) ||
713 (clientIdOption.getDuid().getDuidType() == Dhcp6Duid.DuidType.DUID_LL)) {
714 leafClientMac = MacAddress.valueOf(clientIdOption.getDuid().getLinkLayerAddress());
715 } else {
716 log.warn("Link-Layer Address not supported in CLIENTID option. No DhcpRelay Record created.");
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000717 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_LINKLOCAL_FAIL);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000718 return;
719 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000720 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000721 log.warn("CLIENTID option NOT found. Don't create DhcpRelay Record.");
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000722 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_CLIENTID_FAIL);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000723 return;
724 }
725
Kalhee Kim495c9b22017-11-07 16:32:09 +0000726 HostId leafHostId = HostId.hostId(leafClientMac, vlanId);
727 DhcpRecord record = dhcpRelayStore.getDhcpRecord(leafHostId).orElse(null);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000728 if (record == null) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000729 record = new DhcpRecord(leafHostId);
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000730 } else {
731 record = record.clone();
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000732 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000733
Kalhee Kim495c9b22017-11-07 16:32:09 +0000734 Boolean isMsgRelease = dhcp6HandlerUtil.isDhcp6Release(dhcp6Packet);
735 IpAddressInfo ipInfo;
736 PdPrefixInfo pdInfo = null;
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000737 if (directConnFlag) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000738 // Add to host store if it is connected to network directly
739 ipInfo = extractIpAddress(dhcp6Packet);
740 if (ipInfo != null) {
741 if (isMsgRelease) {
742 HostId hostId = HostId.hostId(srcMac, vlanId);
743 log.debug("remove Host {} ip for directly connected.", hostId.toString());
744 providerService.removeIpFromHost(hostId, ipInfo.ip6Address);
Kalhee Kim45fede42017-09-05 19:05:06 +0000745 }
746 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000747 log.debug("ipAddress not found. Do not remove Host {} for directly connected.",
748 HostId.hostId(srcMac, vlanId).toString());
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000749 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000750 leafMsgType = dhcp6Packet.getMsgType();
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000751 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000752 // Remove from route store if it is not connected to network directly
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000753 // pick out the first link-local ip address
Kalhee Kim495c9b22017-11-07 16:32:09 +0000754 IpAddress nextHopIp = getFirstIpByHost(directConnFlag, srcMac, vlanId);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000755 if (nextHopIp == null) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000756 log.warn("Can't find link-local IP address of gateway mac {} vlanId {}", srcMac, vlanId);
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000757 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_LINKLOCAL_GW);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000758 return;
759 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000760
Kalhee Kim495c9b22017-11-07 16:32:09 +0000761 DHCP6 leafDhcp = dhcp6HandlerUtil.getDhcp6Leaf(dhcp6Packet);
762 ipInfo = extractIpAddress(leafDhcp);
763 if (ipInfo == null) {
764 log.debug("ip is null");
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000765 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000766 if (isMsgRelease) {
767 Route routeForIP = new Route(Route.Source.STATIC, ipInfo.ip6Address.toIpPrefix(), nextHopIp);
768 log.debug("removing route of 128 address for indirectly connected.");
769 log.debug("128 ip {}, nexthop {}",
770 HexString.toHexString(ipInfo.ip6Address.toOctets(), ":"),
771 HexString.toHexString(nextHopIp.toOctets(), ":"));
772 routeStore.removeRoute(routeForIP);
Kalhee Kim45fede42017-09-05 19:05:06 +0000773 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000774 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000775
Kalhee Kim495c9b22017-11-07 16:32:09 +0000776 pdInfo = extractPrefix(leafDhcp);
777 if (pdInfo == null) {
778 log.debug("ipPrefix is null ");
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000779 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000780 if (isMsgRelease) {
781 Route routeForPrefix = new Route(Route.Source.STATIC, pdInfo.pdPrefix, nextHopIp);
782 log.debug("removing route of PD for indirectly connected.");
783 log.debug("pd ip {}, nexthop {}",
784 HexString.toHexString(pdInfo.pdPrefix.address().toOctets(), ":"),
785 HexString.toHexString(nextHopIp.toOctets(), ":"));
786
787 routeStore.removeRoute(routeForPrefix);
Kalhee Kimba366062017-11-07 16:32:09 +0000788 if (this.dhcpFpmEnabled) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000789 dhcpFpmPrefixStore.removeFpmRecord(pdInfo.pdPrefix);
Kalhee Kimba366062017-11-07 16:32:09 +0000790 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000791 }
792 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000793 leafMsgType = leafDhcp.getMsgType();
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000794 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000795
Kalhee Kim495c9b22017-11-07 16:32:09 +0000796 if (isMsgRelease) {
797 log.debug("DHCP6 RELEASE msg.");
798 if (record != null) {
799 if (ipInfo != null) {
800 log.debug("DhcpRelay Record ip6Address is set to null.");
801 record.ip6Address(null);
802 }
803 if (pdInfo != null) {
804 log.debug("DhcpRelay Record pdPrefix is set to null.");
805 }
806
807 if (!record.ip6Address().isPresent() && !record.pdPrefix().isPresent()) {
808 log.warn("IP6 address and IP6 PD both are null. Remove record.");
809 // do not remove a record. Let timer task handler it.
810 //dhcpRelayStore.removeDhcpRecord(HostId.hostId(leafClientMac, vlanId));
811 }
812 }
813 }
814
Ray Milkeyffe1a332018-01-24 10:41:14 -0800815 if (record != null) {
816 record.getV6Counters().incrementCounter(dhcp6HandlerUtil.getMsgTypeStr(leafMsgType));
817 record.addLocation(new HostLocation(location, System.currentTimeMillis()));
818 record.ip6Status(DHCP6.MsgType.getType(leafMsgType));
819 record.setDirectlyConnected(directConnFlag);
820 if (!directConnFlag) {
821 // Update gateway mac address if the host is not directly connected
822 record.nextHop(srcMac);
823 }
824 record.updateLastSeen();
Kalhee Kim495c9b22017-11-07 16:32:09 +0000825 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000826 dhcpRelayStore.updateDhcpRecord(leafHostId, record);
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000827 // TODO Use AtomicInteger for the counters
828 try {
829 recordSemaphore.acquire();
830 try {
831 dhcpRelayCountersStore.incrementCounter(gCount, dhcp6HandlerUtil.getMsgTypeStr(leafMsgType));
832 } finally {
833 // calling release() after a successful acquire()
834 recordSemaphore.release();
835 }
836 } catch (InterruptedException e) {
Ray Milkeyba547f92018-02-01 15:22:31 -0800837 Thread.currentThread().interrupt();
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000838 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000839 }
840
841 /**
842 * add host or route and update dhcp relay record.
843 *
844 * @param directConnFlag flag to show that packet is from directly connected client
845 * @param location client side connect point
846 * @param dhcp6Relay the dhcp6 payload
847 * @param embeddedDhcp6 the dhcp6 payload within relay
848 * @param srcMac client gw/host macAddress
849 * @param clientInterface client interface
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800850 * @param dhcpServerIp6Address DHCP server IP
Kalhee Kim495c9b22017-11-07 16:32:09 +0000851 */
852 private void addHostOrRoute(boolean directConnFlag, ConnectPoint location, DHCP6 dhcp6Relay,
853 DHCP6 embeddedDhcp6, MacAddress srcMac, Interface clientInterface) {
854 log.debug("addHostOrRoute entered.");
855 VlanId vlanId = clientInterface.vlan();
856 Boolean isMsgReply = dhcp6HandlerUtil.isDhcp6Reply(dhcp6Relay);
857 MacAddress leafClientMac;
858 Byte leafMsgType;
859
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000860 Dhcp6ClientIdOption clientIdOption = extractClinedId(directConnFlag, embeddedDhcp6);
861 if (clientIdOption != null) {
862 log.debug("CLIENTID option found {}", clientIdOption);
863 if ((clientIdOption.getDuid().getDuidType() == Dhcp6Duid.DuidType.DUID_LLT) ||
864 (clientIdOption.getDuid().getDuidType() == Dhcp6Duid.DuidType.DUID_LL)) {
865 leafClientMac = MacAddress.valueOf(clientIdOption.getDuid().getLinkLayerAddress());
866 } else {
867 log.warn("Link-Layer Address not supported in CLIENTID option. No DhcpRelay Record created.");
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000868 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_LINKLOCAL_FAIL);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000869 return;
870 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000871 } else {
872 log.warn("CLIENTID option NOT found. No DhcpRelay Record created.");
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000873 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_CLIENTID_FAIL);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000874 return;
875 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000876 HostId leafHostId = HostId.hostId(leafClientMac, vlanId);
877 DhcpRecord record = dhcpRelayStore.getDhcpRecord(leafHostId).orElse(null);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000878 if (record == null) {
879 record = new DhcpRecord(HostId.hostId(leafClientMac, vlanId));
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000880 } else {
881 record = record.clone();
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000882 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000883
884 IpAddressInfo ipInfo;
885 PdPrefixInfo pdInfo = null;
886 if (directConnFlag) {
887 // Add to host store if it connect to network directly
888 ipInfo = extractIpAddress(embeddedDhcp6);
889 if (ipInfo != null) {
890 if (isMsgReply) {
891 Set<IpAddress> ips = Sets.newHashSet(ipInfo.ip6Address);
892 HostId hostId = HostId.hostId(srcMac, vlanId);
893 Host host = hostService.getHost(hostId);
894 HostLocation hostLocation = new HostLocation(clientInterface.connectPoint(),
895 System.currentTimeMillis());
896 Set<HostLocation> hostLocations = Sets.newHashSet(hostLocation);
897 if (host != null) {
898 // Dual homing support:
899 // if host exists, use old locations and new location
900 hostLocations.addAll(host.locations());
901 }
902 HostDescription desc = new DefaultHostDescription(srcMac, vlanId, hostLocations, ips,
903 false);
904 log.debug("adding Host for directly connected.");
905 log.debug("client mac {} client vlan {} hostlocation {}",
906 HexString.toHexString(srcMac.toBytes(), ":"), vlanId, hostLocation.toString());
907 // Replace the ip when dhcp server give the host new ip address
908 providerService.hostDetected(hostId, desc, false);
909 }
910 } else {
911 log.warn("ipAddress not found. Do not add Host {} for directly connected.",
912 HostId.hostId(srcMac, vlanId).toString());
913 }
914 leafMsgType = embeddedDhcp6.getMsgType();
915 } else {
916 // Add to route store if it does not connect to network directly
917 // pick out the first link-local ip address
918 IpAddress nextHopIp = getFirstIpByHost(directConnFlag, srcMac, vlanId);
919 if (nextHopIp == null) {
920 log.warn("Can't find link-local IP address of gateway mac {} vlanId {}", srcMac, vlanId);
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000921 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_LINKLOCAL_GW);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000922 return;
923 }
924
925 DHCP6 leafDhcp = dhcp6HandlerUtil.getDhcp6Leaf(embeddedDhcp6);
926 ipInfo = extractIpAddress(leafDhcp);
927 if (ipInfo == null) {
928 log.debug("ip is null");
929 } else {
930 if (isMsgReply) {
931 Route routeForIP = new Route(Route.Source.STATIC, ipInfo.ip6Address.toIpPrefix(), nextHopIp);
932 log.debug("adding Route of 128 address for indirectly connected.");
933 routeStore.updateRoute(routeForIP);
934 }
935 }
936
937 pdInfo = extractPrefix(leafDhcp);
938 if (pdInfo == null) {
939 log.debug("ipPrefix is null ");
940 } else {
941 if (isMsgReply) {
942 Route routeForPrefix = new Route(Route.Source.STATIC, pdInfo.pdPrefix, nextHopIp);
943 log.debug("adding Route of PD for indirectly connected.");
944 routeStore.updateRoute(routeForPrefix);
945 if (this.dhcpFpmEnabled) {
946 FpmRecord fpmRecord = new FpmRecord(pdInfo.pdPrefix, nextHopIp, FpmRecord.Type.DHCP_RELAY);
947 dhcpFpmPrefixStore.addFpmRecord(pdInfo.pdPrefix, fpmRecord);
948 }
949 }
950 }
951 leafMsgType = leafDhcp.getMsgType();
952 }
953 if (leafMsgType == DHCP6.MsgType.RELEASE.value() ||
954 (leafMsgType == DHCP6.MsgType.REPLY.value()) && ipInfo == null) {
955 log.warn("DHCP6 RELEASE/REPLY(null ip) from Server. MsgType {}", leafMsgType);
956 //return;
957 }
958
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000959 record.addLocation(new HostLocation(location, System.currentTimeMillis()));
Kalhee Kim2f07c602017-11-15 18:57:53 +0000960
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000961 if (leafMsgType == DHCP6.MsgType.REPLY.value()) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000962 if (ipInfo != null) {
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000963 log.debug("IP6 address is being stored into dhcp-relay store.");
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800964 log.debug("Client IP6 address {}", HexString.toHexString(ipInfo.ip6Address.toOctets(), ":"));
Kalhee Kim495c9b22017-11-07 16:32:09 +0000965 record.ip6Address(ipInfo.ip6Address);
Kalhee Kim2f07c602017-11-15 18:57:53 +0000966 record.updateAddrPrefTime(ipInfo.prefTime);
967 record.updateLastIp6Update();
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800968 } else {
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000969 log.debug("IP6 address is not returned from server. Maybe only PD is returned.");
970 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000971 if (pdInfo != null) {
972 log.debug("IP6 PD address {}",
973 HexString.toHexString(pdInfo.pdPrefix.address().toOctets(), ":"));
Kalhee Kim2f07c602017-11-15 18:57:53 +0000974 record.pdPrefix(pdInfo.pdPrefix);
975 record.updatePdPrefTime(pdInfo.prefTime);
976 record.updateLastPdUpdate();
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000977 } else {
978 log.debug("IP6 PD address is not returned from server. Maybe only IPAddress is returned.");
979 }
980 }
Kalhee Kim2f07c602017-11-15 18:57:53 +0000981
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000982 record.getV6Counters().incrementCounter(dhcp6HandlerUtil.getMsgTypeStr(leafMsgType));
Kalhee Kim495c9b22017-11-07 16:32:09 +0000983 record.ip6Status(DHCP6.MsgType.getType(leafMsgType));
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000984 record.setDirectlyConnected(directConnFlag);
985 record.updateLastSeen();
Kalhee Kim495c9b22017-11-07 16:32:09 +0000986 dhcpRelayStore.updateDhcpRecord(leafHostId, record);
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000987 // TODO Use AtomicInteger for the counters
988 try {
989 recordSemaphore.acquire();
990 try {
991 dhcpRelayCountersStore.incrementCounter(gCount, dhcp6HandlerUtil.getMsgTypeStr(leafMsgType));
992 } finally {
993 // calling release() after a successful acquire()
994 recordSemaphore.release();
995 }
996 } catch (InterruptedException e) {
Ray Milkeyba547f92018-02-01 15:22:31 -0800997 Thread.currentThread().interrupt();
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000998 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000999 }
1000
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001001 private List<InternalPacket> processLQ6PacketFromClient(PacketContext context,
1002 Ethernet clientPacket,
1003 Set<Interface> clientInterfaces,
1004 DHCP6 dhcp6Payload) {
1005 ConnectPoint inPort = context.inPacket().receivedFrom();
1006 log.info("Got LQ-REQUEST V6 on port {}", inPort);
1007 List<Dhcp6Option> lopt = dhcp6Payload.getOptions();
1008 log.info("Options list: {}", lopt);
1009 Dhcp6LeaseQueryOption lqoption = dhcp6Payload.getOptions()
1010 .stream()
1011 .filter(opt -> opt instanceof Dhcp6LeaseQueryOption)
1012 .map(pld -> (Dhcp6LeaseQueryOption) pld)
1013 .findFirst()
1014 .orElse(null);
1015
1016 if (lqoption == null) {
1017 // Can't find dhcp payload
1018 log.warn("Can't find dhcp6 lease query message - aborting");
1019 return null;
1020 } else {
1021 log.info("dhcp6 lqv6 options found: {}", lqoption);
1022 }
1023 log.warn("LQv6 for " + lqoption.linkAddress.toString() + " comes from " + inPort.toString());
1024 Ethernet packet = context.inPacket().parsed();
1025 Ip6Address clientAddress = lqoption.linkAddress;
1026 IPv6 ipv6Packet = (IPv6) packet.getPayload();
1027 Ip6Address nextHopIp = findNextHopIp6FromRelayStore(clientAddress);
1028
1029 // 1. only if there is a route to remove - remove it
1030 if (nextHopIp != null) {
1031 Route routeForIP6 = new Route(Route.Source.STATIC, clientAddress.toIpPrefix(), nextHopIp);
1032 log.debug("Removing route of Client " + clientAddress +
1033 " for indirectly connected - next hop ip6 " + nextHopIp);
1034 routeStore.removeRoute(routeForIP6);
1035 }
1036
1037 // 2. note the potential NH this packet came from in case it's a known lease
1038 // this NH will then be used to build the route
1039 MacAddress potentialNH = packet.getSourceMAC();
1040 VlanId vlanId = VlanId.vlanId(packet.getVlanID());
1041 setPotentialNextHopForIp6InRelayStore(clientAddress, vlanId, potentialNH);
1042
1043 // 3. route this LQ6 to all relevant servers
1044 IPv6 clientIpv6 = (IPv6) clientPacket.getPayload();
1045 UDP clientUdp = (UDP) clientIpv6.getPayload();
1046 DHCP6 clientDhcp6 = (DHCP6) clientUdp.getPayload();
1047
1048 boolean directConnFlag = dhcp6HandlerUtil.directlyConnected(clientDhcp6);
1049
1050 ConnectPoint clientConnectionPoint = context.inPacket().receivedFrom();
1051 VlanId vlanIdInUse = VlanId.vlanId(clientPacket.getVlanID());
1052 Interface clientInterface = interfaceService.getInterfacesByPort(clientConnectionPoint)
1053 .stream().filter(iface -> dhcp6HandlerUtil.interfaceContainsVlan(iface, vlanIdInUse))
1054 .findFirst()
1055 .orElse(null);
1056
1057 List<InternalPacket> internalPackets = new ArrayList<>();
1058 List<DhcpServerInfo> serverInfoList = findValidServerInfo(directConnFlag);
1059 List<DhcpServerInfo> copyServerInfoList = new ArrayList<DhcpServerInfo>(serverInfoList);
1060
1061 for (DhcpServerInfo serverInfo : copyServerInfoList) {
1062 if (!dhcp6HandlerUtil.checkDhcpServerConnPt(directConnFlag, serverInfo)) {
1063 log.warn("Can't get server connect point, ignore");
1064 continue;
1065 }
1066 DhcpServerInfo newServerInfo = getHostInfoForServerInfo(serverInfo, serverInfoList);
1067 if (newServerInfo == null) {
1068 log.warn("Can't get server interface with host info resolved, ignore");
1069 continue;
1070 }
1071
1072 Interface serverInterface = getServerInterface(newServerInfo);
1073 if (serverInterface == null) {
1074 log.warn("Can't get server interface, ignore");
1075 continue;
1076 }
1077
1078
1079 Ethernet etherRouted = (Ethernet) clientPacket.clone();
1080 MacAddress macFacingServer = serverInterface.mac();
1081 if (macFacingServer == null) {
1082 log.warn("No MAC address for server Interface {}", serverInterface);
1083 return null;
1084 }
1085 etherRouted.setSourceMACAddress(macFacingServer);
1086 etherRouted.setDestinationMACAddress(newServerInfo.getDhcpConnectMac().get());
1087 InternalPacket internalPacket =
1088 new Dhcp6HandlerUtil().new InternalPacket(etherRouted,
1089 serverInfo.getDhcpServerConnectPoint().get());
1090 internalPackets.add(internalPacket);
1091 log.debug("Sending LQ to DHCP server {}", newServerInfo.getDhcpServerIp6());
1092 }
1093 log.debug("num of client packets to send is{}", internalPackets.size());
1094
1095 return internalPackets;
1096 }
1097
Kalhee Kim45fede42017-09-05 19:05:06 +00001098 /**
Kalhee Kim495c9b22017-11-07 16:32:09 +00001099 * build the DHCP6 solicit/request packet with gatewayip.
Kalhee Kim45fede42017-09-05 19:05:06 +00001100 *
1101 * @param context packet context
1102 * @param clientPacket client ethernet packet
1103 * @param clientInterfaces set of client side interfaces
1104 */
Kalhee Kim495c9b22017-11-07 16:32:09 +00001105 private List<InternalPacket> processDhcp6PacketFromClient(PacketContext context,
1106 Ethernet clientPacket,
1107 Set<Interface> clientInterfaces) {
Yi Tseng3bd57ac2017-11-29 14:39:18 -08001108 ConnectPoint receivedFrom = context.inPacket().receivedFrom();
1109 DeviceId receivedFromDevice = receivedFrom.deviceId();
Kalhee Kim495c9b22017-11-07 16:32:09 +00001110 Ip6Address relayAgentIp = dhcp6HandlerUtil.getRelayAgentIPv6Address(clientInterfaces);
Yi Tseng3bd57ac2017-11-29 14:39:18 -08001111 MacAddress relayAgentMac = clientInterfaces.iterator().next().mac();
1112 if (relayAgentIp == null || relayAgentMac == null) {
1113 log.warn("Missing DHCP relay agent interface Ipv6 addr config for "
Kalhee Kim495c9b22017-11-07 16:32:09 +00001114 + "packet from client on port: {}. Aborting packet processing",
1115 clientInterfaces.iterator().next().connectPoint());
Kalhee Kimd94ceea2017-11-29 19:03:02 +00001116 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_CLIENT_INTF_MAC);
Charles Chan239492c2018-01-22 13:27:28 -08001117 return Lists.newArrayList();
Kalhee Kim121ba922017-11-01 17:56:44 +00001118 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001119
Kalhee Kim495c9b22017-11-07 16:32:09 +00001120 IPv6 clientIpv6 = (IPv6) clientPacket.getPayload();
1121 UDP clientUdp = (UDP) clientIpv6.getPayload();
1122 DHCP6 clientDhcp6 = (DHCP6) clientUdp.getPayload();
1123
1124 boolean directConnFlag = dhcp6HandlerUtil.directlyConnected(clientDhcp6);
Charles Chana990ce92017-10-30 10:22:50 -07001125
Kalhee Kim121ba922017-11-01 17:56:44 +00001126 ConnectPoint clientConnectionPoint = context.inPacket().receivedFrom();
1127 VlanId vlanIdInUse = VlanId.vlanId(clientPacket.getVlanID());
1128 Interface clientInterface = interfaceService.getInterfacesByPort(clientConnectionPoint)
Kalhee Kim495c9b22017-11-07 16:32:09 +00001129 .stream().filter(iface -> dhcp6HandlerUtil.interfaceContainsVlan(iface, vlanIdInUse))
1130 .findFirst()
1131 .orElse(null);
Kalhee Kim45fede42017-09-05 19:05:06 +00001132
Kalhee Kim495c9b22017-11-07 16:32:09 +00001133 List<InternalPacket> internalPackets = new ArrayList<>();
1134 List<DhcpServerInfo> serverInfoList = findValidServerInfo(directConnFlag);
1135 List<DhcpServerInfo> copyServerInfoList = new ArrayList<DhcpServerInfo>(serverInfoList);
Kalhee Kim45b24182017-10-18 18:30:23 +00001136
Kalhee Kim495c9b22017-11-07 16:32:09 +00001137 for (DhcpServerInfo serverInfo : copyServerInfoList) {
1138 if (!dhcp6HandlerUtil.checkDhcpServerConnPt(directConnFlag, serverInfo)) {
1139 log.warn("Can't get server connect point, ignore");
1140 continue;
1141 }
1142 DhcpServerInfo newServerInfo = getHostInfoForServerInfo(serverInfo, serverInfoList);
1143 if (newServerInfo == null) {
1144 log.warn("Can't get server interface with host info resolved, ignore");
1145 continue;
1146 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001147
Kalhee Kim495c9b22017-11-07 16:32:09 +00001148 Interface serverInterface = getServerInterface(newServerInfo);
1149 if (serverInterface == null) {
1150 log.warn("Can't get server interface, ignore");
1151 continue;
1152 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +00001153
Kalhee Kim495c9b22017-11-07 16:32:09 +00001154 Ethernet etherReply = dhcp6HandlerUtil.buildDhcp6PacketFromClient(context, clientPacket,
1155 clientInterfaces, newServerInfo, serverInterface);
1156 removeHostOrRoute(directConnFlag, clientConnectionPoint, clientDhcp6, clientPacket,
1157 clientIpv6, clientInterface);
Kalhee Kimea4b6c22017-11-09 14:38:37 +00001158
Kalhee Kim495c9b22017-11-07 16:32:09 +00001159 InternalPacket internalPacket = new Dhcp6HandlerUtil().new InternalPacket(etherReply,
1160 serverInfo.getDhcpServerConnectPoint().get());
1161 internalPackets.add(internalPacket);
1162 }
1163 log.debug("num of client packets to send is{}", internalPackets.size());
Kalhee Kimea4b6c22017-11-09 14:38:37 +00001164
Kalhee Kim495c9b22017-11-07 16:32:09 +00001165 return internalPackets;
1166 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001167
1168 /**
Kalhee Kim45fede42017-09-05 19:05:06 +00001169 * process the DHCP6 relay-reply packet from dhcp server.
1170 *
1171 * @param context packet context
1172 * @param receivedPacket server ethernet packet
1173 * @param recevingInterfaces set of server side interfaces
Kalhee Kim495c9b22017-11-07 16:32:09 +00001174 * @return internalPacket toward client
Kalhee Kim45fede42017-09-05 19:05:06 +00001175 */
1176 private InternalPacket processDhcp6PacketFromServer(PacketContext context,
1177 Ethernet receivedPacket, Set<Interface> recevingInterfaces) {
Kalhee Kim45fede42017-09-05 19:05:06 +00001178 // get dhcp6 header.
Ray Milkeyf0c47612017-09-28 11:29:38 -07001179 Ethernet etherReply = receivedPacket.duplicate();
Kalhee Kim45fede42017-09-05 19:05:06 +00001180 IPv6 ipv6Packet = (IPv6) etherReply.getPayload();
1181 UDP udpPacket = (UDP) ipv6Packet.getPayload();
1182 DHCP6 dhcp6Relay = (DHCP6) udpPacket.getPayload();
Kalhee Kim495c9b22017-11-07 16:32:09 +00001183 Boolean directConnFlag = dhcp6HandlerUtil.directlyConnected(dhcp6Relay);
Kalhee Kim45fede42017-09-05 19:05:06 +00001184
Kalhee Kim495c9b22017-11-07 16:32:09 +00001185 DHCP6 embeddedDhcp6 = dhcp6Relay.getOptions().stream()
1186 .filter(opt -> opt instanceof Dhcp6RelayOption)
1187 .map(BasePacket::getPayload)
1188 .map(pld -> (DHCP6) pld)
1189 .findFirst()
1190 .orElse(null);
Kalhee Kimd21029f2017-09-26 20:21:53 +00001191 ConnectPoint inPort = context.inPacket().receivedFrom();
Kalhee Kim495c9b22017-11-07 16:32:09 +00001192 DhcpServerInfo foundServerInfo = findServerInfoFromServer(directConnFlag, inPort);
Kalhee Kimd21029f2017-09-26 20:21:53 +00001193
Kalhee Kim495c9b22017-11-07 16:32:09 +00001194 if (foundServerInfo == null) {
1195 log.warn("Cannot find server info");
Kalhee Kimd94ceea2017-11-29 19:03:02 +00001196 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_SERVER_INFO);
Kalhee Kimd21029f2017-09-26 20:21:53 +00001197 return null;
Kalhee Kim495c9b22017-11-07 16:32:09 +00001198 } else {
1199 if (dhcp6HandlerUtil.isServerIpEmpty(foundServerInfo)) {
1200 log.warn("Cannot find server info's ipaddress");
Kalhee Kimd94ceea2017-11-29 19:03:02 +00001201 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_SERVER_IP6ADDR);
Kalhee Kim495c9b22017-11-07 16:32:09 +00001202 return null;
1203 }
Kalhee Kimd21029f2017-09-26 20:21:53 +00001204 }
1205
Kalhee Kim45fede42017-09-05 19:05:06 +00001206 Dhcp6InterfaceIdOption interfaceIdOption = dhcp6Relay.getOptions().stream()
1207 .filter(opt -> opt instanceof Dhcp6InterfaceIdOption)
1208 .map(opt -> (Dhcp6InterfaceIdOption) opt)
1209 .findFirst()
1210 .orElse(null);
Kalhee Kim45fede42017-09-05 19:05:06 +00001211 if (interfaceIdOption == null) {
1212 log.warn("Interface Id option is not present, abort packet...");
Kalhee Kimd94ceea2017-11-29 19:03:02 +00001213 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.OPTION_MISSING_FAIL);
Kalhee Kim45fede42017-09-05 19:05:06 +00001214 return null;
1215 }
1216
1217 MacAddress peerMac = interfaceIdOption.getMacAddress();
1218 String clientConnectionPointStr = new String(interfaceIdOption.getInPort());
Kalhee Kim45fede42017-09-05 19:05:06 +00001219 ConnectPoint clientConnectionPoint = ConnectPoint.deviceConnectPoint(clientConnectionPointStr);
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +00001220 VlanId vlanIdInUse = VlanId.vlanId(interfaceIdOption.getVlanId());
1221 Interface clientInterface = interfaceService.getInterfacesByPort(clientConnectionPoint)
Kalhee Kim495c9b22017-11-07 16:32:09 +00001222 .stream().filter(iface -> dhcp6HandlerUtil.interfaceContainsVlan(iface, vlanIdInUse))
1223 .findFirst().orElse(null);
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +00001224 if (clientInterface == null) {
1225 log.warn("Cannot get client interface for from packet, abort... vlan {}", vlanIdInUse.toString());
Kalhee Kimd94ceea2017-11-29 19:03:02 +00001226 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_MATCHING_INTF);
Kalhee Kim45fede42017-09-05 19:05:06 +00001227 return null;
1228 }
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +00001229 MacAddress relayAgentMac = clientInterface.mac();
Kalhee Kim45fede42017-09-05 19:05:06 +00001230 if (relayAgentMac == null) {
Yi Tseng68ef26b2017-12-18 17:10:00 -08001231 log.warn("Can not get client interface mac, abort packet..");
Kalhee Kimd94ceea2017-11-29 19:03:02 +00001232 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_CLIENT_INTF_MAC);
Kalhee Kim45fede42017-09-05 19:05:06 +00001233 return null;
1234 }
1235 etherReply.setSourceMACAddress(relayAgentMac);
1236
1237 // find destMac
Yi Tseng68ef26b2017-12-18 17:10:00 -08001238 MacAddress clientMac;
Yi Tsengaa417a62017-09-08 17:22:51 -07001239 Ip6Address peerAddress = Ip6Address.valueOf(dhcp6Relay.getPeerAddress());
1240 Set<Host> clients = hostService.getHostsByIp(peerAddress);
Kalhee Kim45fede42017-09-05 19:05:06 +00001241 if (clients.isEmpty()) {
Yi Tseng68ef26b2017-12-18 17:10:00 -08001242 log.trace("There's no host found for this address {}",
Kalhee Kim45fede42017-09-05 19:05:06 +00001243 HexString.toHexString(dhcp6Relay.getPeerAddress(), ":"));
Yi Tseng68ef26b2017-12-18 17:10:00 -08001244 log.trace("Let's look up interfaceId {}", HexString.toHexString(peerMac.toBytes(), ":"));
Kalhee Kim45fede42017-09-05 19:05:06 +00001245 clientMac = peerMac;
1246 } else {
1247 clientMac = clients.iterator().next().mac();
1248 if (clientMac == null) {
1249 log.warn("No client mac address found, abort packet...");
Kalhee Kimd94ceea2017-11-29 19:03:02 +00001250 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_CLIENT_INTF_MAC);
Kalhee Kim45fede42017-09-05 19:05:06 +00001251 return null;
1252 }
Yi Tseng68ef26b2017-12-18 17:10:00 -08001253 log.trace("Client mac address found from getHostByIp");
Kalhee Kim45fede42017-09-05 19:05:06 +00001254 }
1255 etherReply.setDestinationMACAddress(clientMac);
Kalhee Kim45fede42017-09-05 19:05:06 +00001256 // ip header
1257 ipv6Packet.setSourceAddress(dhcp6Relay.getLinkAddress());
1258 ipv6Packet.setDestinationAddress(dhcp6Relay.getPeerAddress());
1259 // udp header
1260 udpPacket.setSourcePort(UDP.DHCP_V6_SERVER_PORT);
1261 if (directConnFlag) {
1262 udpPacket.setDestinationPort(UDP.DHCP_V6_CLIENT_PORT);
1263 } else {
1264 udpPacket.setDestinationPort(UDP.DHCP_V6_SERVER_PORT);
1265 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001266 // add host or route
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001267 addHostOrRoute(directConnFlag, clientConnectionPoint, dhcp6Relay, embeddedDhcp6,
1268 clientMac, clientInterface);
1269
Kalhee Kim45fede42017-09-05 19:05:06 +00001270
1271 udpPacket.setPayload(embeddedDhcp6);
1272 udpPacket.resetChecksum();
1273 ipv6Packet.setPayload(udpPacket);
1274 etherReply.setPayload(ipv6Packet);
Kalhee Kim495c9b22017-11-07 16:32:09 +00001275 return new Dhcp6HandlerUtil().new InternalPacket(etherReply, clientConnectionPoint);
Kalhee Kim45fede42017-09-05 19:05:06 +00001276 }
1277
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001278 // Returns the first v6 interface ip out of a set of interfaces or null.
1279 // Checks all interfaces, and ignores v6 interface ips
1280 private Ip6Address getRelayAgentIPv6Address(Set<Interface> intfs) {
1281 for (Interface intf : intfs) {
1282 for (InterfaceIpAddress ip : intf.ipAddressesList()) {
1283 Ip6Address relayAgentIp = ip.ipAddress().getIp6Address();
1284 if (relayAgentIp != null) {
1285 return relayAgentIp;
1286 }
1287 }
1288 }
1289 return null;
1290 }
Yi Tseng483ac6f2017-08-02 15:03:31 -07001291
1292 @Override
Kalhee Kimba366062017-11-07 16:32:09 +00001293 public void setDhcpFpmEnabled(Boolean enabled) {
1294 dhcpFpmEnabled = enabled;
1295 }
1296
1297 @Override
Yi Tseng483ac6f2017-08-02 15:03:31 -07001298 public void setDefaultDhcpServerConfigs(Collection<DhcpServerConfig> configs) {
Kalhee Kim495c9b22017-11-07 16:32:09 +00001299 log.debug("setDefaultDhcpServerConfigs is called.");
Yi Tseng919b2df2017-09-07 16:22:51 -07001300 setDhcpServerConfigs(configs, defaultServerInfoList);
Yi Tseng919b2df2017-09-07 16:22:51 -07001301 }
1302
1303 @Override
1304 public void setIndirectDhcpServerConfigs(Collection<DhcpServerConfig> configs) {
Kalhee Kim495c9b22017-11-07 16:32:09 +00001305 log.debug("setIndirectDhcpServerConfigs is called.");
Yi Tseng919b2df2017-09-07 16:22:51 -07001306 setDhcpServerConfigs(configs, indirectServerInfoList);
Yi Tseng919b2df2017-09-07 16:22:51 -07001307 }
1308
1309 public void setDhcpServerConfigs(Collection<DhcpServerConfig> configs, List<DhcpServerInfo> serverInfoList) {
Kalhee Kim495c9b22017-11-07 16:32:09 +00001310 log.debug("config size {}.", configs.size());
1311
Kalhee Kim45fede42017-09-05 19:05:06 +00001312 if (configs.size() == 0) {
1313 // no config to update
1314 return;
1315 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001316 // TODO: currently we pick up first DHCP server config.
1317 // Will use other server configs in the future for HA.
Kalhee Kim495c9b22017-11-07 16:32:09 +00001318 Boolean isConfigValid = false;
1319 for (DhcpServerConfig serverConfig : configs) {
1320 if (serverConfig.getDhcpServerIp6().isPresent()) {
1321 isConfigValid = true;
1322 break;
1323 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001324 }
Kalhee Kim495c9b22017-11-07 16:32:09 +00001325 if (!isConfigValid) {
1326 log.warn("No IP V6 server address found.");
1327 return; // No IP V6 address found
1328 }
1329 for (DhcpServerInfo oldServerInfo : serverInfoList) {
Yi Tseng919b2df2017-09-07 16:22:51 -07001330 // stop monitoring gateway or server
1331 oldServerInfo.getDhcpGatewayIp6().ifPresent(gatewayIp -> {
1332 hostService.stopMonitoringIp(gatewayIp);
1333 });
1334 oldServerInfo.getDhcpServerIp6().ifPresent(serverIp -> {
1335 hostService.stopMonitoringIp(serverIp);
Yi Tseng525ff402017-10-23 19:39:39 -07001336 cancelDhcpPacket(serverIp);
Yi Tseng919b2df2017-09-07 16:22:51 -07001337 });
1338 }
Kalhee Kim495c9b22017-11-07 16:32:09 +00001339 serverInfoList.clear();
1340 for (DhcpServerConfig serverConfig : configs) {
1341 // Create new server info according to the config
1342 DhcpServerInfo newServerInfo = new DhcpServerInfo(serverConfig,
1343 DhcpServerInfo.Version.DHCP_V6);
1344 checkState(newServerInfo.getDhcpServerConnectPoint().isPresent(),
1345 "Connect point not exists");
1346 checkState(newServerInfo.getDhcpServerIp6().isPresent(),
1347 "IP of DHCP server not exists");
Yi Tseng919b2df2017-09-07 16:22:51 -07001348
Kalhee Kim495c9b22017-11-07 16:32:09 +00001349 log.debug("DHCP server connect point: {}", newServerInfo.getDhcpServerConnectPoint().orElse(null));
1350 log.debug("DHCP server IP: {}", newServerInfo.getDhcpServerIp6().orElse(null));
Yi Tseng919b2df2017-09-07 16:22:51 -07001351
Kalhee Kim495c9b22017-11-07 16:32:09 +00001352 Ip6Address serverIp = newServerInfo.getDhcpServerIp6().get();
1353 Ip6Address ipToProbe;
1354 if (newServerInfo.getDhcpGatewayIp6().isPresent()) {
1355 ipToProbe = newServerInfo.getDhcpGatewayIp6().get();
1356 } else {
1357 ipToProbe = newServerInfo.getDhcpServerIp6().orElse(null);
1358 }
1359 String hostToProbe = newServerInfo.getDhcpGatewayIp6()
1360 .map(ip -> "gateway").orElse("server");
Yi Tseng919b2df2017-09-07 16:22:51 -07001361
Kalhee Kim495c9b22017-11-07 16:32:09 +00001362 log.warn("Probing to resolve {} IP {}", hostToProbe, ipToProbe);
1363 hostService.startMonitoringIp(ipToProbe);
1364
1365 Set<Host> hosts = hostService.getHostsByIp(ipToProbe);
1366 if (!hosts.isEmpty()) {
1367 Host host = hosts.iterator().next();
1368 newServerInfo.setDhcpConnectVlan(host.vlan());
1369 newServerInfo.setDhcpConnectMac(host.mac());
1370 log.warn("Host found host {}", host);
1371
1372 } else {
1373 log.warn("No host found host ip {}", ipToProbe);
1374 }
1375 // Add new server info
1376 synchronized (this) {
1377 serverInfoList.add(newServerInfo);
1378 }
1379 if (!hosts.isEmpty()) {
1380 requestDhcpPacket(serverIp);
1381 }
Yi Tseng919b2df2017-09-07 16:22:51 -07001382 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001383 }
1384
1385 class InternalHostListener implements HostListener {
1386 @Override
1387 public void event(HostEvent event) {
1388 switch (event.type()) {
1389 case HOST_ADDED:
1390 case HOST_UPDATED:
1391 hostUpdated(event.subject());
1392 break;
1393 case HOST_REMOVED:
1394 hostRemoved(event.subject());
1395 break;
Kalhee Kim45fede42017-09-05 19:05:06 +00001396 default:
1397 break;
1398 }
1399 }
1400 }
1401
1402 /**
Kalhee Kim45fede42017-09-05 19:05:06 +00001403 * Handle host updated.
1404 * If the host is DHCP server or gateway, update connect mac and vlan.
1405 *
1406 * @param host the host
1407 */
1408 private void hostUpdated(Host host) {
Yi Tseng525ff402017-10-23 19:39:39 -07001409 hostUpdated(host, defaultServerInfoList);
1410 hostUpdated(host, indirectServerInfoList);
Kalhee Kim45fede42017-09-05 19:05:06 +00001411 }
1412
Yi Tseng525ff402017-10-23 19:39:39 -07001413 private void hostUpdated(Host host, List<DhcpServerInfo> serverInfoList) {
1414 DhcpServerInfo serverInfo;
1415 Ip6Address targetIp;
1416 if (!serverInfoList.isEmpty()) {
1417 serverInfo = serverInfoList.get(0);
1418 Ip6Address serverIp = serverInfo.getDhcpServerIp6().orElse(null);
1419 targetIp = serverInfo.getDhcpGatewayIp6().orElse(null);
1420
1421 if (targetIp == null) {
1422 targetIp = serverIp;
1423 }
Yi Tseng525ff402017-10-23 19:39:39 -07001424 if (targetIp != null) {
1425 if (host.ipAddresses().contains(targetIp)) {
1426 serverInfo.setDhcpConnectMac(host.mac());
1427 serverInfo.setDhcpConnectVlan(host.vlan());
1428 requestDhcpPacket(serverIp);
1429 }
1430 }
1431 }
1432 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001433 /**
1434 * Handle host removed.
1435 * If the host is DHCP server or gateway, unset connect mac and vlan.
1436 *
1437 * @param host the host
1438 */
1439 private void hostRemoved(Host host) {
Yi Tseng525ff402017-10-23 19:39:39 -07001440 hostRemoved(host, defaultServerInfoList);
1441 hostRemoved(host, indirectServerInfoList);
Yi Tseng919b2df2017-09-07 16:22:51 -07001442 }
1443
Yi Tseng525ff402017-10-23 19:39:39 -07001444 private void hostRemoved(Host host, List<DhcpServerInfo> serverInfoList) {
1445 DhcpServerInfo serverInfo;
1446 Ip6Address targetIp;
1447
1448 if (!serverInfoList.isEmpty()) {
1449 serverInfo = serverInfoList.get(0);
1450 Ip6Address serverIp = serverInfo.getDhcpServerIp6().orElse(null);
1451 targetIp = serverInfo.getDhcpGatewayIp6().orElse(null);
1452
1453 if (targetIp == null) {
1454 targetIp = serverIp;
1455 }
Yi Tseng525ff402017-10-23 19:39:39 -07001456 if (targetIp != null) {
1457 if (host.ipAddresses().contains(targetIp)) {
1458 serverInfo.setDhcpConnectVlan(null);
1459 serverInfo.setDhcpConnectMac(null);
1460 cancelDhcpPacket(serverIp);
1461 }
1462 }
1463 }
1464 }
1465
Kalhee Kim45b24182017-10-18 18:30:23 +00001466 /**
1467 * Returns the first interface ip from interface.
1468 *
1469 * @param iface interface of one connect point
1470 * @return the first interface IP; null if not exists an IP address in
1471 * these interfaces
1472 */
1473 private Ip6Address getFirstIpFromInterface(Interface iface) {
1474 checkNotNull(iface, "Interface can't be null");
1475 return iface.ipAddressesList().stream()
1476 .map(InterfaceIpAddress::ipAddress)
1477 .filter(IpAddress::isIp6)
1478 .map(IpAddress::getIp6Address)
1479 .findFirst()
1480 .orElse(null);
1481 }
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001482
1483 /**
1484 * Gets Interface facing to the server for default host.
1485 *
1486 * @return the Interface facing to the server; null if not found
1487 */
1488 private Interface getServerInterface() {
1489 DhcpServerInfo serverInfo;
1490 ConnectPoint dhcpServerConnectPoint;
1491 VlanId dhcpConnectVlan;
1492
1493 if (!defaultServerInfoList.isEmpty()) {
1494 serverInfo = defaultServerInfoList.get(0);
1495 dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1496 dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1497 } else {
1498 return null;
1499 }
1500 if (dhcpServerConnectPoint == null || dhcpConnectVlan == null) {
1501 log.info("Default DHCP server {} not resolve yet", serverInfo.getDhcpGatewayIp6());
1502 return null;
1503 }
1504 return interfaceService.getInterfacesByPort(dhcpServerConnectPoint)
1505 .stream()
1506 .filter(iface -> dhcp6HandlerUtil.interfaceContainsVlan(iface, dhcpConnectVlan))
1507 .findFirst()
1508 .orElse(null);
1509 }
1510
1511 /**
1512 * Gets Interface facing to the server for indirect hosts.
1513 * Use default server Interface if indirect server not configured.
1514 *
1515 * @return the Interface facing to the server; null if not found
1516 */
1517 private Interface getIndirectServerInterface() {
1518 DhcpServerInfo serverInfo;
1519
1520 ConnectPoint indirectDhcpServerConnectPoint;
1521 VlanId indirectDhcpConnectVlan;
1522
1523 if (!indirectServerInfoList.isEmpty()) {
1524 serverInfo = indirectServerInfoList.get(0);
1525 indirectDhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1526 indirectDhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1527 } else {
1528 return getServerInterface();
1529 }
1530 if (indirectDhcpServerConnectPoint == null || indirectDhcpConnectVlan == null) {
1531 log.info("Indirect DHCP server {} not resolve yet", serverInfo.getDhcpGatewayIp6());
1532 return null;
1533 }
1534 return interfaceService.getInterfacesByPort(indirectDhcpServerConnectPoint)
1535 .stream()
1536 .filter(iface -> dhcp6HandlerUtil.interfaceContainsVlan(iface, indirectDhcpConnectVlan))
1537 .findFirst()
1538 .orElse(null);
1539 }
1540
Kalhee Kim45b24182017-10-18 18:30:23 +00001541 /**
Kalhee Kim495c9b22017-11-07 16:32:09 +00001542 * Checks if serverInfo's host info (mac and vlan) is filled in; if not, fills in.
1543 *
1544 * @param serverInfo server information
1545 * @return newServerInfo if host info can be either found or filled in.
1546 */
1547 private DhcpServerInfo getHostInfoForServerInfo(DhcpServerInfo serverInfo, List<DhcpServerInfo> sererInfoList) {
1548 DhcpServerInfo newServerInfo = null;
1549 MacAddress dhcpServerConnectMac = serverInfo.getDhcpConnectMac().orElse(null);
1550 VlanId dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1551 ConnectPoint dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1552
1553 if (dhcpServerConnectMac != null && dhcpConnectVlan != null) {
1554 newServerInfo = serverInfo;
1555 log.info("DHCP server {} host info found. ConnectPt{} Mac {} vlan {}", serverInfo.getDhcpServerIp6(),
1556 dhcpServerConnectPoint, dhcpServerConnectMac, dhcpConnectVlan);
1557 } else {
1558 log.warn("DHCP server {} not resolve yet connectPt {} mac {} vlan {}", serverInfo.getDhcpServerIp6(),
1559 dhcpServerConnectPoint, dhcpServerConnectMac, dhcpConnectVlan);
1560
1561 Ip6Address ipToProbe;
1562 if (serverInfo.getDhcpGatewayIp6().isPresent()) {
1563 ipToProbe = serverInfo.getDhcpGatewayIp6().get();
1564 } else {
1565 ipToProbe = serverInfo.getDhcpServerIp6().orElse(null);
1566 }
1567 String hostToProbe = serverInfo.getDhcpGatewayIp6()
1568 .map(ip -> "gateway").orElse("server");
1569
1570 log.info("Dynamically probing to resolve {} IP {}", hostToProbe, ipToProbe);
1571 hostService.startMonitoringIp(ipToProbe);
1572
1573 Set<Host> hosts = hostService.getHostsByIp(ipToProbe);
1574 if (!hosts.isEmpty()) {
1575 int serverInfoIndex = sererInfoList.indexOf(serverInfo);
1576 Host host = hosts.iterator().next();
1577 serverInfo.setDhcpConnectVlan(host.vlan());
1578 serverInfo.setDhcpConnectMac(host.mac());
1579 // replace the serverInfo in the list
1580 sererInfoList.set(serverInfoIndex, serverInfo);
1581 newServerInfo = serverInfo;
1582 log.warn("Dynamically host found host {}", host);
1583 } else {
1584 log.warn("No host found host ip {} dynamically", ipToProbe);
1585 }
1586 }
1587 return newServerInfo;
1588 }
1589
1590 /**
Kalhee Kim45b24182017-10-18 18:30:23 +00001591 * Gets Interface facing to the server for default host.
1592 *
Kalhee Kim495c9b22017-11-07 16:32:09 +00001593 * @param serverInfo server information
Kalhee Kim45b24182017-10-18 18:30:23 +00001594 * @return the Interface facing to the server; null if not found
1595 */
Kalhee Kim495c9b22017-11-07 16:32:09 +00001596 private Interface getServerInterface(DhcpServerInfo serverInfo) {
1597 Interface serverInterface = null;
Yi Tseng25bfe372017-11-03 16:27:32 -07001598
Kalhee Kim495c9b22017-11-07 16:32:09 +00001599 ConnectPoint dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1600 VlanId dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1601
1602 if (dhcpServerConnectPoint != null && dhcpConnectVlan != null) {
1603 serverInterface = interfaceService.getInterfacesByPort(dhcpServerConnectPoint)
1604 .stream()
1605 .filter(iface -> dhcp6HandlerUtil.interfaceContainsVlan(iface, dhcpConnectVlan))
1606 .findFirst()
1607 .orElse(null);
Yi Tseng25bfe372017-11-03 16:27:32 -07001608 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +00001609 log.warn("DHCP server {} not resolve yet connectPoint {} vlan {}", serverInfo.getDhcpServerIp6(),
1610 dhcpServerConnectPoint, dhcpConnectVlan);
Kalhee Kim45b24182017-10-18 18:30:23 +00001611 }
Kalhee Kim495c9b22017-11-07 16:32:09 +00001612
1613 return serverInterface;
Kalhee Kim45b24182017-10-18 18:30:23 +00001614 }
1615
Yi Tseng525ff402017-10-23 19:39:39 -07001616 private void requestDhcpPacket(Ip6Address serverIp) {
1617 requestServerDhcpPacket(serverIp);
1618 requestClientDhcpPacket(serverIp);
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001619 requestServerLQPacket(serverIp);
Yi Tseng525ff402017-10-23 19:39:39 -07001620 }
1621
1622 private void cancelDhcpPacket(Ip6Address serverIp) {
1623 cancelServerDhcpPacket(serverIp);
1624 cancelClientDhcpPacket(serverIp);
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001625 cancelServerLQPacket(serverIp);
Yi Tseng525ff402017-10-23 19:39:39 -07001626 }
1627
1628 private void cancelServerDhcpPacket(Ip6Address serverIp) {
1629 TrafficSelector serverSelector =
1630 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1631 .matchIPv6Src(serverIp.toIpPrefix())
1632 .build();
1633 packetService.cancelPackets(serverSelector,
1634 PacketPriority.CONTROL,
1635 appId);
1636 }
1637
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001638 private void cancelServerLQPacket(Ip6Address serverIp) {
1639 TrafficSelector serverSelector =
1640 DefaultTrafficSelector.builder(LEASE_QUERY_RESPONSE_SELECTOR)
1641 .matchIPv6Src(serverIp.toIpPrefix())
1642 .build();
1643 packetService.cancelPackets(serverSelector,
1644 PacketPriority.CONTROL,
1645 appId);
1646 }
1647
Yi Tseng525ff402017-10-23 19:39:39 -07001648 private void requestServerDhcpPacket(Ip6Address serverIp) {
1649 TrafficSelector serverSelector =
1650 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1651 .matchIPv6Src(serverIp.toIpPrefix())
1652 .build();
1653 packetService.requestPackets(serverSelector,
1654 PacketPriority.CONTROL,
1655 appId);
1656 }
1657
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001658 private void requestServerLQPacket(Ip6Address serverIp) {
1659 TrafficSelector serverSelector =
1660 DefaultTrafficSelector.builder(LEASE_QUERY_RESPONSE_SELECTOR)
1661 .matchIPv6Src(serverIp.toIpPrefix())
1662 .build();
1663 packetService.requestPackets(serverSelector,
1664 PacketPriority.CONTROL,
1665 appId);
1666 }
1667
Yi Tseng525ff402017-10-23 19:39:39 -07001668 private void cancelClientDhcpPacket(Ip6Address serverIp) {
1669 // Packet comes from relay
1670 TrafficSelector indirectClientSelector =
1671 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1672 .matchIPv6Dst(serverIp.toIpPrefix())
1673 .build();
1674 packetService.cancelPackets(indirectClientSelector,
1675 PacketPriority.CONTROL,
1676 appId);
Yi Tseng41dde702017-11-02 15:21:10 -07001677 indirectClientSelector =
1678 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1679 .matchIPv6Dst(Ip6Address.ALL_DHCP_RELAY_AGENTS_AND_SERVERS.toIpPrefix())
1680 .build();
1681 packetService.cancelPackets(indirectClientSelector,
1682 PacketPriority.CONTROL,
1683 appId);
1684 indirectClientSelector =
1685 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1686 .matchIPv6Dst(Ip6Address.ALL_DHCP_SERVERS.toIpPrefix())
1687 .build();
1688 packetService.cancelPackets(indirectClientSelector,
1689 PacketPriority.CONTROL,
1690 appId);
Yi Tseng525ff402017-10-23 19:39:39 -07001691
1692 // Packet comes from client
1693 packetService.cancelPackets(CLIENT_SERVER_SELECTOR,
1694 PacketPriority.CONTROL,
1695 appId);
1696 }
1697
1698 private void requestClientDhcpPacket(Ip6Address serverIp) {
1699 // Packet comes from relay
1700 TrafficSelector indirectClientSelector =
1701 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1702 .matchIPv6Dst(serverIp.toIpPrefix())
1703 .build();
1704 packetService.requestPackets(indirectClientSelector,
1705 PacketPriority.CONTROL,
1706 appId);
Yi Tseng41dde702017-11-02 15:21:10 -07001707 indirectClientSelector =
1708 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1709 .matchIPv6Dst(Ip6Address.ALL_DHCP_RELAY_AGENTS_AND_SERVERS.toIpPrefix())
1710 .build();
1711 packetService.requestPackets(indirectClientSelector,
1712 PacketPriority.CONTROL,
1713 appId);
1714 indirectClientSelector =
1715 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1716 .matchIPv6Dst(Ip6Address.ALL_DHCP_SERVERS.toIpPrefix())
1717 .build();
1718 packetService.requestPackets(indirectClientSelector,
1719 PacketPriority.CONTROL,
1720 appId);
Yi Tseng525ff402017-10-23 19:39:39 -07001721
1722 // Packet comes from client
1723 packetService.requestPackets(CLIENT_SERVER_SELECTOR,
1724 PacketPriority.CONTROL,
1725 appId);
1726 }
1727
1728 /**
1729 * Process the ignore rules.
1730 *
1731 * @param deviceId the device id
1732 * @param vlanId the vlan to be ignored
1733 * @param op the operation, ADD to install; REMOVE to uninstall rules
1734 */
1735 private void processIgnoreVlanRule(DeviceId deviceId, VlanId vlanId, Objective.Operation op) {
Yi Tseng525ff402017-10-23 19:39:39 -07001736 AtomicInteger installedCount = new AtomicInteger(DHCP_SELECTORS.size());
1737 DHCP_SELECTORS.forEach(trafficSelector -> {
1738 TrafficSelector selector = DefaultTrafficSelector.builder(trafficSelector)
1739 .matchVlanId(vlanId)
1740 .build();
1741
1742 ForwardingObjective.Builder builder = DefaultForwardingObjective.builder()
1743 .withFlag(ForwardingObjective.Flag.VERSATILE)
1744 .withSelector(selector)
1745 .withPriority(IGNORE_CONTROL_PRIORITY)
Yi Tsengdbabeed2017-11-29 10:49:20 -08001746 .withTreatment(DefaultTrafficTreatment.emptyTreatment())
Yi Tseng525ff402017-10-23 19:39:39 -07001747 .fromApp(appId);
1748
1749
1750 ObjectiveContext objectiveContext = new ObjectiveContext() {
1751 @Override
1752 public void onSuccess(Objective objective) {
1753 log.info("Ignore rule {} (Vlan id {}, device {}, selector {})",
1754 op, vlanId, deviceId, selector);
1755 int countDown = installedCount.decrementAndGet();
1756 if (countDown != 0) {
1757 return;
1758 }
1759 switch (op) {
1760 case ADD:
1761 ignoredVlans.put(deviceId, vlanId);
1762 break;
1763 case REMOVE:
1764 ignoredVlans.remove(deviceId, vlanId);
1765 break;
1766 default:
1767 log.warn("Unsupported objective operation {}", op);
1768 break;
1769 }
1770 }
1771
1772 @Override
1773 public void onError(Objective objective, ObjectiveError error) {
1774 log.warn("Can't {} ignore rule (vlan id {}, selector {}, device {}) due to {}",
1775 op, vlanId, selector, deviceId, error);
1776 }
1777 };
1778
1779 ForwardingObjective fwd;
1780 switch (op) {
1781 case ADD:
1782 fwd = builder.add(objectiveContext);
1783 break;
1784 case REMOVE:
1785 fwd = builder.remove(objectiveContext);
1786 break;
1787 default:
1788 log.warn("Unsupported objective operation {}", op);
1789 return;
1790 }
1791
1792 Device device = deviceService.getDevice(deviceId);
1793 if (device == null || !device.is(Pipeliner.class)) {
1794 log.warn("Device {} is not available now, wait until device is available", deviceId);
1795 return;
1796 }
1797 flowObjectiveService.apply(deviceId, fwd);
1798 });
1799 }
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001800
Kalhee Kim121ba922017-11-01 17:56:44 +00001801 /**
1802 * Find first ipaddress for a given Host info i.e. mac and vlan.
1803 *
1804 * @param clientMac client mac
1805 * @param vlanId packet's vlan
1806 * @return next-hop link-local ipaddress for a given host
1807 */
Kalhee Kim495c9b22017-11-07 16:32:09 +00001808 private IpAddress getFirstIpByHost(Boolean directConnFlag, MacAddress clientMac, VlanId vlanId) {
Kalhee Kim121ba922017-11-01 17:56:44 +00001809 IpAddress nextHopIp;
1810 // pick out the first link-local ip address
1811 HostId gwHostId = HostId.hostId(clientMac, vlanId);
1812 Host gwHost = hostService.getHost(gwHostId);
1813 if (gwHost == null) {
1814 log.warn("Can't find gateway host for hostId {}", gwHostId);
1815 return null;
1816 }
Kalhee Kim495c9b22017-11-07 16:32:09 +00001817 if (directConnFlag) {
1818 nextHopIp = gwHost.ipAddresses()
1819 .stream()
1820 .filter(IpAddress::isIp6)
1821 .map(IpAddress::getIp6Address)
1822 .findFirst()
1823 .orElse(null);
1824 } else {
1825 nextHopIp = gwHost.ipAddresses()
1826 .stream()
1827 .filter(IpAddress::isIp6)
1828 .filter(ip6 -> ip6.isLinkLocal())
1829 .map(IpAddress::getIp6Address)
1830 .findFirst()
1831 .orElse(null);
1832 }
Kalhee Kim121ba922017-11-01 17:56:44 +00001833 return nextHopIp;
1834 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +00001835
Kalhee Kim495c9b22017-11-07 16:32:09 +00001836 private List<DhcpServerInfo> findValidServerInfo(boolean directConnFlag) {
1837 List<DhcpServerInfo> validServerInfo;
1838
1839 if (directConnFlag || indirectServerInfoList.isEmpty()) {
1840 validServerInfo = new ArrayList<DhcpServerInfo>(defaultServerInfoList);
1841 } else {
1842 validServerInfo = new ArrayList<DhcpServerInfo>(indirectServerInfoList);
1843 }
1844 return validServerInfo;
1845 }
1846
1847 private DhcpServerInfo findServerInfoFromServer(boolean directConnFlag, ConnectPoint inPort) {
1848 List<DhcpServerInfo> validServerInfoList = findValidServerInfo(directConnFlag);
1849 DhcpServerInfo foundServerInfo = null;
1850 for (DhcpServerInfo serverInfo : validServerInfoList) {
1851 if (inPort.equals(serverInfo.getDhcpServerConnectPoint().get())) {
1852 foundServerInfo = serverInfo;
1853 log.warn("ServerInfo found for Rcv port {} Server Connect Point {} for {}",
1854 inPort, serverInfo.getDhcpServerConnectPoint(), directConnFlag ? "direct" : "indirect");
1855 break;
1856 } else {
1857 log.warn("Rcv port {} not the same as Server Connect Point {} for {}",
1858 inPort, serverInfo.getDhcpServerConnectPoint(), directConnFlag ? "direct" : "indirect");
1859 }
1860 }
1861 return foundServerInfo;
1862 }
Lior Assoulinea21c0ca2018-01-28 16:18:48 -08001863
Kalhee Kim495c9b22017-11-07 16:32:09 +00001864 /**
1865 * Set the dhcp6 lease expiry poll interval value.
1866 *
1867 * @param val poll interval value in seconds
1868 */
Kalhee Kim2f07c602017-11-15 18:57:53 +00001869 @Override
Kalhee Kim495c9b22017-11-07 16:32:09 +00001870 public void setDhcp6PollInterval(int val) {
1871 dhcp6PollInterval = val;
1872 }
1873
Kalhee Kim2f07c602017-11-15 18:57:53 +00001874 /**
1875 * get the dhcp6 lease expiry poll interval value.
1876 * This is a private function
1877 * @return poll interval value in seconds
1878 */
1879 private int getDhcp6PollInterval() {
1880 return dhcp6PollInterval;
1881 }
1882
1883 /**
1884 * Find lease-expired ipaddresses and pd prefixes.
1885 * Removing host/route/fpm entries.
1886 */
1887 public void timeTick() {
1888 long currentTime = System.currentTimeMillis();
1889 Collection<DhcpRecord> records = dhcpRelayStore.getDhcpRecords();
1890
1891 log.debug("timeTick called currenttime {} records num {} ", currentTime, records.size());
1892
1893 records.forEach(record -> {
1894 boolean addrOrPdRemoved = false;
1895 DHCP6.MsgType ip6Status = record.ip6Status().orElse(null);
1896 if (ip6Status == null) {
1897 log.debug("record is not valid v6 record.");
1898 return;
1899 }
1900
1901 if ((currentTime - record.getLastIp6Update()) >
1902 ((record.addrPrefTime() + getDhcp6PollInterval() / 2) * 1000)) {
1903 // remove ipaddress from host/route table
1904 IpAddress ip = record.ip6Address().orElse(null);
1905 if (ip != null) {
1906 if (record.directlyConnected()) {
1907 providerService.removeIpFromHost(HostId.hostId(record.macAddress(),
1908 record.vlanId()), ip);
1909 } else {
1910 MacAddress gwMac = record.nextHop().orElse(null);
1911 if (gwMac == null) {
1912 log.warn("Can't find gateway mac address from record {} for ip6Addr", record);
1913 return;
1914 }
1915 IpAddress nextHopIp = getFirstIpByHost(record.directlyConnected(),
1916 gwMac,
1917 record.vlanId());
1918 Route route = new Route(Route.Source.STATIC, ip.toIpPrefix(), nextHopIp);
1919 routeStore.removeRoute(route);
1920 }
1921 record.updateAddrPrefTime(0);
1922 record.ip6Address(null);
1923 addrOrPdRemoved = true;
1924 dhcpRelayStore.updateDhcpRecord(HostId.hostId(record.macAddress(),
1925 record.vlanId()), record);
1926 log.warn("IP6 address is set to null. delta {} lastUpdate {} addrPrefTime {}",
1927 (currentTime - record.getLastIp6Update()), record.getLastIp6Update(),
1928 record.addrPrefTime());
1929 }
1930 }
1931 if ((currentTime - record.getLastPdUpdate()) >
1932 ((record.pdPrefTime() + getDhcp6PollInterval() / 2) * 1000)) {
1933 // remove PD from route/fpm table
1934 IpPrefix pdIpPrefix = record.pdPrefix().orElse(null);
1935 if (pdIpPrefix != null) {
1936 if (record.directlyConnected()) {
1937 providerService.removeIpFromHost(HostId.hostId(record.macAddress(), record.vlanId()),
1938 pdIpPrefix.address().getIp6Address());
1939 } else {
1940 MacAddress gwMac = record.nextHop().orElse(null);
1941 if (gwMac == null) {
1942 log.warn("Can't find gateway mac address from record {} for PD prefix", record);
1943 return;
1944 }
1945 IpAddress nextHopIp = getFirstIpByHost(record.directlyConnected(),
1946 gwMac,
1947 record.vlanId());
1948 Route route = new Route(Route.Source.STATIC, pdIpPrefix, nextHopIp);
1949 routeStore.removeRoute(route);
1950 if (this.dhcpFpmEnabled) {
1951 dhcpFpmPrefixStore.removeFpmRecord(pdIpPrefix);
1952 }
1953 }
1954 record.updatePdPrefTime(0);
1955 record.pdPrefix(null);
1956 addrOrPdRemoved = true;
1957 dhcpRelayStore.updateDhcpRecord(HostId.hostId(record.macAddress(),
1958 record.vlanId()), record);
1959 log.warn("PD prefix is set to null.delta {} pdPrefTime {}",
1960 (currentTime - record.getLastPdUpdate()), record.pdPrefTime());
1961 }
1962 }
1963 if (addrOrPdRemoved &&
1964 !record.ip6Address().isPresent() && !record.pdPrefix().isPresent()) {
1965 log.warn("ip6Status {} IP6 address and IP6 PD both are null. Remove record.", ip6Status);
1966 dhcpRelayStore.removeDhcpRecord(HostId.hostId(record.macAddress(), record.vlanId()));
1967 }
1968 }
1969 );
1970 }
1971}