blob: 2990e5afba8c53941875ab4b1ab81b9c81a3fc51 [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;
Kalhee Kim45fede42017-09-05 19:05:06 +000042import org.onlab.packet.dhcp.Dhcp6RelayOption;
43import org.onlab.packet.dhcp.Dhcp6InterfaceIdOption;
Kalhee Kim45fede42017-09-05 19:05:06 +000044import org.onlab.packet.dhcp.Dhcp6IaNaOption;
45import org.onlab.packet.dhcp.Dhcp6IaTaOption;
46import org.onlab.packet.dhcp.Dhcp6IaPdOption;
47import org.onlab.packet.dhcp.Dhcp6IaAddressOption;
48import org.onlab.packet.dhcp.Dhcp6IaPrefixOption;
Kalhee Kimea4b6c22017-11-09 14:38:37 +000049import org.onlab.packet.dhcp.Dhcp6ClientIdOption;
50import org.onlab.packet.dhcp.Dhcp6Duid;
Kalhee Kim495c9b22017-11-07 16:32:09 +000051import org.onlab.packet.DHCP6.MsgType;
Kalhee Kim45fede42017-09-05 19:05:06 +000052import org.onlab.util.HexString;
Yi Tseng525ff402017-10-23 19:39:39 -070053import org.onosproject.core.ApplicationId;
54import org.onosproject.core.CoreService;
Yi Tseng51301292017-07-28 13:02:59 -070055import org.onosproject.dhcprelay.api.DhcpHandler;
Yi Tseng919b2df2017-09-07 16:22:51 -070056import org.onosproject.dhcprelay.api.DhcpServerInfo;
Yi Tseng525ff402017-10-23 19:39:39 -070057import org.onosproject.dhcprelay.config.IgnoreDhcpConfig;
Kalhee Kim45fede42017-09-05 19:05:06 +000058import org.onosproject.dhcprelay.store.DhcpRelayStore;
Kalhee Kimea4b6c22017-11-09 14:38:37 +000059import org.onosproject.dhcprelay.store.DhcpRecord;
Kalhee Kimba366062017-11-07 16:32:09 +000060import org.onosproject.dhcprelay.store.DhcpFpmPrefixStore;
Kalhee Kimd94ceea2017-11-29 19:03:02 +000061import org.onosproject.dhcprelay.store.DhcpRelayCounters;
62import org.onosproject.dhcprelay.store.DhcpRelayCountersStore;
Yi Tseng525ff402017-10-23 19:39:39 -070063import org.onosproject.net.Device;
64import org.onosproject.net.DeviceId;
Kalhee Kimba366062017-11-07 16:32:09 +000065import org.onosproject.routing.fpm.api.FpmRecord;
Yi Tseng525ff402017-10-23 19:39:39 -070066import org.onosproject.net.behaviour.Pipeliner;
67import org.onosproject.net.device.DeviceService;
68import org.onosproject.net.flow.DefaultTrafficSelector;
69import org.onosproject.net.flow.TrafficSelector;
70import org.onosproject.net.flowobjective.DefaultForwardingObjective;
71import org.onosproject.net.flowobjective.FlowObjectiveService;
72import org.onosproject.net.flowobjective.ForwardingObjective;
73import org.onosproject.net.flowobjective.Objective;
74import org.onosproject.net.flowobjective.ObjectiveContext;
75import org.onosproject.net.flowobjective.ObjectiveError;
Yi Tsengaa417a62017-09-08 17:22:51 -070076import org.onosproject.net.host.HostProvider;
77import org.onosproject.net.host.HostProviderRegistry;
78import org.onosproject.net.host.HostProviderService;
Kalhee Kim45fede42017-09-05 19:05:06 +000079import org.onosproject.net.host.HostService;
80import org.onosproject.net.host.DefaultHostDescription;
81import org.onosproject.net.host.HostDescription;
82import org.onosproject.net.host.InterfaceIpAddress;
83import org.onosproject.net.host.HostListener;
84import org.onosproject.net.host.HostEvent;
85import org.onosproject.net.intf.Interface;
86import org.onosproject.net.intf.InterfaceService;
Yi Tseng525ff402017-10-23 19:39:39 -070087import org.onosproject.net.packet.PacketPriority;
Yi Tsengaa417a62017-09-08 17:22:51 -070088import org.onosproject.net.provider.ProviderId;
Kalhee Kim45fede42017-09-05 19:05:06 +000089import org.onosproject.routeservice.Route;
90import org.onosproject.routeservice.RouteStore;
Yi Tseng483ac6f2017-08-02 15:03:31 -070091import org.onosproject.dhcprelay.config.DhcpServerConfig;
Yi Tseng51301292017-07-28 13:02:59 -070092import org.onosproject.net.ConnectPoint;
Kalhee Kim45fede42017-09-05 19:05:06 +000093import org.onosproject.net.Host;
94import org.onosproject.net.HostId;
95import org.onosproject.net.HostLocation;
96import org.onosproject.net.packet.DefaultOutboundPacket;
97import org.onosproject.net.packet.OutboundPacket;
Yi Tseng51301292017-07-28 13:02:59 -070098import org.onosproject.net.packet.PacketContext;
Kalhee Kim45fede42017-09-05 19:05:06 +000099import org.onosproject.net.packet.PacketService;
100import org.slf4j.Logger;
101import org.slf4j.LoggerFactory;
102import org.onosproject.net.flow.DefaultTrafficTreatment;
103import org.onosproject.net.flow.TrafficTreatment;
Kalhee Kim495c9b22017-11-07 16:32:09 +0000104import org.onosproject.dhcprelay.Dhcp6HandlerUtil.InternalPacket;
Kalhee Kim45fede42017-09-05 19:05:06 +0000105
Kalhee Kim2f07c602017-11-15 18:57:53 +0000106
Kalhee Kim45fede42017-09-05 19:05:06 +0000107import java.nio.ByteBuffer;
108import java.util.List;
Yi Tseng483ac6f2017-08-02 15:03:31 -0700109import java.util.Collection;
Yi Tseng51301292017-07-28 13:02:59 -0700110import java.util.Optional;
Kalhee Kim45fede42017-09-05 19:05:06 +0000111import java.util.Set;
112import java.util.ArrayList;
Yi Tseng525ff402017-10-23 19:39:39 -0700113import java.util.concurrent.atomic.AtomicInteger;
Kalhee Kim45fede42017-09-05 19:05:06 +0000114import static com.google.common.base.Preconditions.checkNotNull;
115import static com.google.common.base.Preconditions.checkState;
Yi Tseng525ff402017-10-23 19:39:39 -0700116import static org.onosproject.net.flowobjective.Objective.Operation.ADD;
117import static org.onosproject.net.flowobjective.Objective.Operation.REMOVE;
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000118import java.util.concurrent.Semaphore;
Yi Tseng51301292017-07-28 13:02:59 -0700119
120@Component
121@Service
122@Property(name = "version", value = "6")
Yi Tsengaa417a62017-09-08 17:22:51 -0700123public class Dhcp6HandlerImpl implements DhcpHandler, HostProvider {
Charles Chand988c282017-09-12 17:09:32 -0700124 public static final String DHCP_V6_RELAY_APP = "org.onosproject.Dhcp6HandlerImpl";
Saurav Das7c6dec12017-09-13 14:35:56 -0700125 public static final ProviderId PROVIDER_ID = new ProviderId("dhcp6", DHCP_V6_RELAY_APP);
Yi Tseng525ff402017-10-23 19:39:39 -0700126 private static final int IGNORE_CONTROL_PRIORITY = PacketPriority.CONTROL.priorityValue() + 1000;
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000127 private String gCount = "global";
Yi Tseng525ff402017-10-23 19:39:39 -0700128 private static final TrafficSelector CLIENT_SERVER_SELECTOR = DefaultTrafficSelector.builder()
129 .matchEthType(Ethernet.TYPE_IPV6)
130 .matchIPProtocol(IPv6.PROTOCOL_UDP)
131 .matchIPv6Src(IpPrefix.IPV6_LINK_LOCAL_PREFIX)
132 .matchIPv6Dst(Ip6Address.ALL_DHCP_RELAY_AGENTS_AND_SERVERS.toIpPrefix())
133 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_V6_CLIENT_PORT))
134 .matchUdpDst(TpPort.tpPort(UDP.DHCP_V6_SERVER_PORT))
135 .build();
136 private static final TrafficSelector SERVER_RELAY_SELECTOR = DefaultTrafficSelector.builder()
137 .matchEthType(Ethernet.TYPE_IPV6)
138 .matchIPProtocol(IPv6.PROTOCOL_UDP)
139 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_V6_SERVER_PORT))
140 .matchUdpDst(TpPort.tpPort(UDP.DHCP_V6_SERVER_PORT))
141 .build();
142 static final Set<TrafficSelector> DHCP_SELECTORS = ImmutableSet.of(
143 CLIENT_SERVER_SELECTOR,
144 SERVER_RELAY_SELECTOR
145 );
Kalhee Kim45fede42017-09-05 19:05:06 +0000146 private static Logger log = LoggerFactory.getLogger(Dhcp6HandlerImpl.class);
Yi Tseng51301292017-07-28 13:02:59 -0700147
Kalhee Kim45fede42017-09-05 19:05:06 +0000148 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
149 protected DhcpRelayStore dhcpRelayStore;
Yi Tseng51301292017-07-28 13:02:59 -0700150
Kalhee Kim45fede42017-09-05 19:05:06 +0000151 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000152 protected DhcpRelayCountersStore dhcpRelayCountersStore;
153
154 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Kalhee Kim45fede42017-09-05 19:05:06 +0000155 protected PacketService packetService;
156
157 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Kalhee Kim45fede42017-09-05 19:05:06 +0000158 protected RouteStore routeStore;
159
160 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
161 protected InterfaceService interfaceService;
162
163 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
164 protected HostService hostService;
165
Yi Tsengaa417a62017-09-08 17:22:51 -0700166 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
167 protected HostProviderRegistry providerRegistry;
Kalhee Kim45fede42017-09-05 19:05:06 +0000168
Yi Tseng525ff402017-10-23 19:39:39 -0700169 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
170 protected CoreService coreService;
171
172 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Kalhee Kimba366062017-11-07 16:32:09 +0000173 protected DhcpFpmPrefixStore dhcpFpmPrefixStore;
174
175 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Yi Tseng525ff402017-10-23 19:39:39 -0700176 protected DeviceService deviceService;
177
178 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
179 protected FlowObjectiveService flowObjectiveService;
180
Yi Tsengaa417a62017-09-08 17:22:51 -0700181 protected HostProviderService providerService;
Yi Tseng525ff402017-10-23 19:39:39 -0700182 protected ApplicationId appId;
183 protected Multimap<DeviceId, VlanId> ignoredVlans = HashMultimap.create();
184 private InternalHostListener hostListener = new InternalHostListener();
185
Kalhee Kimba366062017-11-07 16:32:09 +0000186 private Boolean dhcpFpmEnabled = false;
187
Kalhee Kim495c9b22017-11-07 16:32:09 +0000188 private Dhcp6HandlerUtil dhcp6HandlerUtil = new Dhcp6HandlerUtil();
189
Yi Tseng919b2df2017-09-07 16:22:51 -0700190 private List<DhcpServerInfo> defaultServerInfoList = Lists.newArrayList();
191 private List<DhcpServerInfo> indirectServerInfoList = Lists.newArrayList();
Kalhee Kim495c9b22017-11-07 16:32:09 +0000192 private class IpAddressInfo {
193 Ip6Address ip6Address;
194 long prefTime;
195 }
196 private class PdPrefixInfo {
197 IpPrefix pdPrefix;
198 long prefTime;
199 }
200 protected int dhcp6PollInterval = 24 * 3600; // 24 hr period
201
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000202 // max 1 thread
203 static Semaphore recordSemaphore = new Semaphore(1);
Kalhee Kim45fede42017-09-05 19:05:06 +0000204
205 // CLIENT message types
206 public static final Set<Byte> MSG_TYPE_FROM_CLIENT =
207 ImmutableSet.of(DHCP6.MsgType.SOLICIT.value(),
208 DHCP6.MsgType.REQUEST.value(),
209 DHCP6.MsgType.REBIND.value(),
210 DHCP6.MsgType.RENEW.value(),
211 DHCP6.MsgType.RELEASE.value(),
212 DHCP6.MsgType.DECLINE.value(),
213 DHCP6.MsgType.CONFIRM.value(),
214 DHCP6.MsgType.RELAY_FORW.value());
215 // SERVER message types
216 public static final Set<Byte> MSG_TYPE_FROM_SERVER =
217 ImmutableSet.of(DHCP6.MsgType.RELAY_REPL.value());
218
219 @Activate
220 protected void activate() {
Yi Tseng525ff402017-10-23 19:39:39 -0700221 appId = coreService.registerApplication(DHCP_V6_RELAY_APP);
Yi Tsengaa417a62017-09-08 17:22:51 -0700222 providerService = providerRegistry.register(this);
Kalhee Kim45fede42017-09-05 19:05:06 +0000223 hostService.addListener(hostListener);
Yi Tseng51301292017-07-28 13:02:59 -0700224 }
225
Kalhee Kim45fede42017-09-05 19:05:06 +0000226 @Deactivate
227 protected void deactivate() {
Yi Tsengaa417a62017-09-08 17:22:51 -0700228 providerRegistry.unregister(this);
Kalhee Kim45fede42017-09-05 19:05:06 +0000229 hostService.removeListener(hostListener);
Yi Tseng919b2df2017-09-07 16:22:51 -0700230 defaultServerInfoList.forEach(this::stopMonitoringIps);
231 defaultServerInfoList.clear();
232 indirectServerInfoList.forEach(this::stopMonitoringIps);
233 indirectServerInfoList.clear();
234 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000235
Yi Tseng919b2df2017-09-07 16:22:51 -0700236 private void stopMonitoringIps(DhcpServerInfo serverInfo) {
237 serverInfo.getDhcpGatewayIp6().ifPresent(gatewayIp -> {
238 hostService.stopMonitoringIp(gatewayIp);
239 });
240 serverInfo.getDhcpServerIp6().ifPresent(serverIp -> {
241 hostService.stopMonitoringIp(serverIp);
242 });
Yi Tseng51301292017-07-28 13:02:59 -0700243 }
244
Yi Tseng51301292017-07-28 13:02:59 -0700245 @Override
Yi Tseng919b2df2017-09-07 16:22:51 -0700246 public List<DhcpServerInfo> getDefaultDhcpServerInfoList() {
247 return defaultServerInfoList;
Yi Tseng51301292017-07-28 13:02:59 -0700248 }
249
250 @Override
Yi Tseng919b2df2017-09-07 16:22:51 -0700251 public List<DhcpServerInfo> getIndirectDhcpServerInfoList() {
252 return indirectServerInfoList;
Yi Tseng51301292017-07-28 13:02:59 -0700253 }
254
255 @Override
Yi Tseng525ff402017-10-23 19:39:39 -0700256 public void updateIgnoreVlanConfig(IgnoreDhcpConfig config) {
257 if (config == null) {
258 ignoredVlans.forEach(((deviceId, vlanId) -> {
259 processIgnoreVlanRule(deviceId, vlanId, REMOVE);
260 }));
261 return;
262 }
263 config.ignoredVlans().forEach((deviceId, vlanId) -> {
264 if (ignoredVlans.get(deviceId).contains(vlanId)) {
265 // don't need to process if it already ignored
266 return;
267 }
268 processIgnoreVlanRule(deviceId, vlanId, ADD);
269 });
270
271 ignoredVlans.forEach((deviceId, vlanId) -> {
272 if (!config.ignoredVlans().get(deviceId).contains(vlanId)) {
273 // not contains in new config, remove it
274 processIgnoreVlanRule(deviceId, vlanId, REMOVE);
275 }
276 });
277 }
278
279 @Override
Saurav Dasb805f1a2017-12-13 16:19:35 -0800280 public void removeIgnoreVlanState(IgnoreDhcpConfig config) {
281 if (config == null) {
282 ignoredVlans.clear();
283 return;
284 }
285 config.ignoredVlans().forEach((deviceId, vlanId) -> {
286 ignoredVlans.remove(deviceId, vlanId);
287 });
288 }
289
290 @Override
Kalhee Kim45fede42017-09-05 19:05:06 +0000291 public void processDhcpPacket(PacketContext context, BasePacket payload) {
292 checkNotNull(payload, "DHCP6 payload can't be null");
293 checkState(payload instanceof DHCP6, "Payload is not a DHCP6");
294 DHCP6 dhcp6Payload = (DHCP6) payload;
295 Ethernet receivedPacket = context.inPacket().parsed();
296
297 if (!configured()) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000298 log.warn("Missing DHCP6 relay server config. Abort packet processing dhcp6 payload {}", dhcp6Payload);
Kalhee Kim45fede42017-09-05 19:05:06 +0000299 return;
300 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000301 byte msgTypeVal = dhcp6Payload.getMsgType();
302 MsgType msgType = DHCP6.MsgType.getType(msgTypeVal);
303 log.debug("msgType is {}", msgType);
Kalhee Kim45fede42017-09-05 19:05:06 +0000304
Kalhee Kim45fede42017-09-05 19:05:06 +0000305 ConnectPoint inPort = context.inPacket().receivedFrom();
Jonathan Hart8ca2bc02017-11-30 18:23:42 -0800306
Kalhee Kim495c9b22017-11-07 16:32:09 +0000307 if (inPort == null) {
308 log.warn("incomming ConnectPoint is null");
309 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000310 Set<Interface> receivingInterfaces = interfaceService.getInterfacesByPort(inPort);
311 //ignore the packets if dhcp client interface is not configured on onos.
312 if (receivingInterfaces.isEmpty()) {
313 log.warn("Virtual interface is not configured on {}", inPort);
314 return;
315 }
316
Kalhee Kim495c9b22017-11-07 16:32:09 +0000317 if (MSG_TYPE_FROM_CLIENT.contains(msgTypeVal)) {
Kalhee Kim45fede42017-09-05 19:05:06 +0000318
Kalhee Kim495c9b22017-11-07 16:32:09 +0000319 List<InternalPacket> ethernetClientPacket =
Kalhee Kim45fede42017-09-05 19:05:06 +0000320 processDhcp6PacketFromClient(context, receivedPacket, receivingInterfaces);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000321 for (InternalPacket internalPacket : ethernetClientPacket) {
322 forwardPacket(internalPacket);
Kalhee Kim45fede42017-09-05 19:05:06 +0000323 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000324 } else if (MSG_TYPE_FROM_SERVER.contains(msgTypeVal)) {
325 log.debug("calling processDhcp6PacketFromServer with RELAY_REPL {}", msgTypeVal);
Kalhee Kim45fede42017-09-05 19:05:06 +0000326 InternalPacket ethernetPacketReply =
327 processDhcp6PacketFromServer(context, receivedPacket, receivingInterfaces);
328 if (ethernetPacketReply != null) {
329 forwardPacket(ethernetPacketReply);
330 }
331 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000332 log.warn("Not so fast, packet type {} not supported yet", msgTypeVal);
Kalhee Kim45fede42017-09-05 19:05:06 +0000333 }
334 }
335
Kalhee Kim45fede42017-09-05 19:05:06 +0000336 /**
337 * Checks if this app has been configured.
338 *
339 * @return true if all information we need have been initialized
340 */
341 public boolean configured() {
Yi Tseng919b2df2017-09-07 16:22:51 -0700342 return !defaultServerInfoList.isEmpty();
Kalhee Kim45fede42017-09-05 19:05:06 +0000343 }
344
Yi Tsengaa417a62017-09-08 17:22:51 -0700345 @Override
346 public ProviderId id() {
Charles Chand988c282017-09-12 17:09:32 -0700347 return PROVIDER_ID;
Yi Tsengaa417a62017-09-08 17:22:51 -0700348 }
349
350 @Override
351 public void triggerProbe(Host host) {
352 // Do nothing here
353 }
354
Kalhee Kim495c9b22017-11-07 16:32:09 +0000355
Kalhee Kim45fede42017-09-05 19:05:06 +0000356
357 //forward the packet to ConnectPoint where the DHCP server is attached.
358 private void forwardPacket(InternalPacket packet) {
359 //send Packetout to dhcp server connectpoint.
360 if (packet.destLocation != null) {
361 TrafficTreatment t = DefaultTrafficTreatment.builder()
362 .setOutput(packet.destLocation.port()).build();
363 OutboundPacket o = new DefaultOutboundPacket(
364 packet.destLocation.deviceId(), t, ByteBuffer.wrap(packet.packet.serialize()));
365 if (log.isTraceEnabled()) {
366 log.trace("Relaying packet to destination {}", packet.destLocation);
367 }
368 packetService.emit(o);
369 } // if
370 }
371
Kalhee Kim45fede42017-09-05 19:05:06 +0000372
373
Kalhee Kim45fede42017-09-05 19:05:06 +0000374
375 /**
376 * extract from dhcp6 packet client ipv6 address of given by dhcp server.
377 *
378 * @param dhcp6 the dhcp6 packet
Kalhee Kim495c9b22017-11-07 16:32:09 +0000379 * @return IpAddressInfo IpAddressInfo given by dhcp server, or null if not exists
Kalhee Kim45fede42017-09-05 19:05:06 +0000380 */
Kalhee Kim495c9b22017-11-07 16:32:09 +0000381 private IpAddressInfo extractIpAddress(DHCP6 dhcp6) {
382 IpAddressInfo ipInfo = new IpAddressInfo();
Kalhee Kim45fede42017-09-05 19:05:06 +0000383
384 log.debug("extractIpAddress enters dhcp6 {}.", dhcp6);
385 // Extract IPv6 address from IA NA ot IA TA option
386 Optional<Dhcp6IaNaOption> iaNaOption = dhcp6.getOptions()
387 .stream()
388 .filter(opt -> opt instanceof Dhcp6IaNaOption)
389 .map(opt -> (Dhcp6IaNaOption) opt)
390 .findFirst();
391 Optional<Dhcp6IaTaOption> iaTaOption = dhcp6.getOptions()
392 .stream()
393 .filter(opt -> opt instanceof Dhcp6IaTaOption)
394 .map(opt -> (Dhcp6IaTaOption) opt)
395 .findFirst();
396 Optional<Dhcp6IaAddressOption> iaAddressOption;
397 if (iaNaOption.isPresent()) {
398 log.debug("Found IPv6 address from iaNaOption {}", iaNaOption);
399
400 iaAddressOption = iaNaOption.get().getOptions().stream()
401 .filter(opt -> opt instanceof Dhcp6IaAddressOption)
402 .map(opt -> (Dhcp6IaAddressOption) opt)
403 .findFirst();
404 } else if (iaTaOption.isPresent()) {
405 log.debug("Found IPv6 address from iaTaOption {}", iaTaOption);
406
407 iaAddressOption = iaTaOption.get().getOptions().stream()
408 .filter(opt -> opt instanceof Dhcp6IaAddressOption)
409 .map(opt -> (Dhcp6IaAddressOption) opt)
410 .findFirst();
411 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000412 log.info("No IPv6 address found from iaTaOption {}", iaTaOption);
Kalhee Kim45fede42017-09-05 19:05:06 +0000413 iaAddressOption = Optional.empty();
414 }
415 if (iaAddressOption.isPresent()) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000416 ipInfo.ip6Address = iaAddressOption.get().getIp6Address();
417 ipInfo.prefTime = iaAddressOption.get().getPreferredLifetime();
Kalhee Kim45fede42017-09-05 19:05:06 +0000418 log.debug("Found IPv6 address from iaAddressOption {}", iaAddressOption);
Kalhee Kim45fede42017-09-05 19:05:06 +0000419 } else {
420 log.debug("Can't find IPv6 address from DHCPv6 {}", dhcp6);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000421 return null;
Kalhee Kim45fede42017-09-05 19:05:06 +0000422 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000423 return ipInfo;
Kalhee Kim45fede42017-09-05 19:05:06 +0000424 }
425 /**
426 * extract from dhcp6 packet Prefix prefix provided by dhcp server.
427 *
428 * @param dhcp6 the dhcp6 payload
429 * @return IpPrefix Prefix Delegation prefix, or null if not exists.
430 */
Kalhee Kim495c9b22017-11-07 16:32:09 +0000431 private PdPrefixInfo extractPrefix(DHCP6 dhcp6) {
432 log.debug("extractPrefix enters {}", dhcp6);
Kalhee Kim45fede42017-09-05 19:05:06 +0000433
434 // extract prefix
Kalhee Kim495c9b22017-11-07 16:32:09 +0000435 PdPrefixInfo pdPrefixInfo = new PdPrefixInfo();
Kalhee Kim45fede42017-09-05 19:05:06 +0000436
437 Ip6Address prefixAddress = null;
438
439 // Extract IPv6 prefix from IA PD option
440 Optional<Dhcp6IaPdOption> iaPdOption = dhcp6.getOptions()
441 .stream()
442 .filter(opt -> opt instanceof Dhcp6IaPdOption)
443 .map(opt -> (Dhcp6IaPdOption) opt)
444 .findFirst();
445
446 Optional<Dhcp6IaPrefixOption> iaPrefixOption;
447 if (iaPdOption.isPresent()) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000448 log.debug("IA_PD option found {}", iaPdOption);
Kalhee Kim45fede42017-09-05 19:05:06 +0000449
450 iaPrefixOption = iaPdOption.get().getOptions().stream()
451 .filter(opt -> opt instanceof Dhcp6IaPrefixOption)
452 .map(opt -> (Dhcp6IaPrefixOption) opt)
453 .findFirst();
454 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000455 log.debug("IA_PD option NOT found");
Kalhee Kim45fede42017-09-05 19:05:06 +0000456
457 iaPrefixOption = Optional.empty();
458 }
459 if (iaPrefixOption.isPresent()) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000460 log.debug("IAPrefix Option within IA_PD option found {}", iaPrefixOption);
Kalhee Kim45fede42017-09-05 19:05:06 +0000461
462 prefixAddress = iaPrefixOption.get().getIp6Prefix();
463 int prefixLen = (int) iaPrefixOption.get().getPrefixLength();
Kalhee Kim495c9b22017-11-07 16:32:09 +0000464 log.debug("Prefix length is {} bits", prefixLen);
465 pdPrefixInfo.pdPrefix = IpPrefix.valueOf(prefixAddress, prefixLen);
466 pdPrefixInfo.prefTime = iaPrefixOption.get().getPreferredLifetime();
Kalhee Kim45fede42017-09-05 19:05:06 +0000467 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000468 log.debug("Can't find IPv6 prefix from DHCPv6 {}", dhcp6);
469 return null;
Kalhee Kim45fede42017-09-05 19:05:06 +0000470 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000471 return pdPrefixInfo;
Kalhee Kim45fede42017-09-05 19:05:06 +0000472 }
473
474 /**
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000475 * extract from dhcp6 packet ClientIdOption.
476 *
477 * @param directConnFlag directly connected host
478 * @param dhcp6Payload the dhcp6 payload
479 * @return Dhcp6ClientIdOption clientIdOption, or null if not exists.
480 */
481 private Dhcp6ClientIdOption extractClinedId(Boolean directConnFlag, DHCP6 dhcp6Payload) {
482 Dhcp6ClientIdOption clientIdOption;
483
484 if (directConnFlag) {
485 clientIdOption = dhcp6Payload.getOptions()
486 .stream()
487 .filter(opt -> opt instanceof Dhcp6ClientIdOption)
488 .map(opt -> (Dhcp6ClientIdOption) opt)
489 .findFirst()
490 .orElse(null);
491 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000492 DHCP6 leafDhcp = dhcp6HandlerUtil.getDhcp6Leaf(dhcp6Payload);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000493 clientIdOption = leafDhcp.getOptions()
494 .stream()
495 .filter(opt -> opt instanceof Dhcp6ClientIdOption)
496 .map(opt -> (Dhcp6ClientIdOption) opt)
497 .findFirst()
498 .orElse(null);
499 }
500
501 return clientIdOption;
502 }
503 /**
Kalhee Kim495c9b22017-11-07 16:32:09 +0000504 * remove host or route and update dhcp relay record attributes.
Kalhee Kim45fede42017-09-05 19:05:06 +0000505 *
506 * @param directConnFlag flag to show that packet is from directly connected client
Kalhee Kim495c9b22017-11-07 16:32:09 +0000507 * @param location client side connect point
Kalhee Kim45fede42017-09-05 19:05:06 +0000508 * @param dhcp6Packet the dhcp6 payload
509 * @param clientPacket client's ethernet packet
510 * @param clientIpv6 client's Ipv6 packet
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +0000511 * @param clientInterface client interfaces
Kalhee Kim45fede42017-09-05 19:05:06 +0000512 */
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000513 private void removeHostOrRoute(boolean directConnFlag, ConnectPoint location,
514 DHCP6 dhcp6Packet,
Kalhee Kim45fede42017-09-05 19:05:06 +0000515 Ethernet clientPacket, IPv6 clientIpv6,
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +0000516 Interface clientInterface) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000517 log.debug("removeHostOrRoute enters {}", dhcp6Packet);
Kalhee Kim121ba922017-11-01 17:56:44 +0000518 VlanId vlanId = clientInterface.vlan();
Kalhee Kim495c9b22017-11-07 16:32:09 +0000519 MacAddress srcMac = clientPacket.getSourceMAC(); // could be gw or host
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000520 MacAddress leafClientMac;
521 byte leafMsgType;
Kalhee Kim495c9b22017-11-07 16:32:09 +0000522 log.debug("client mac {} client vlan {}", HexString.toHexString(srcMac.toBytes(), ":"), vlanId);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000523
524 Dhcp6ClientIdOption clientIdOption = extractClinedId(directConnFlag, dhcp6Packet);
525 if (clientIdOption != null) {
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000526 if ((clientIdOption.getDuid().getDuidType() == Dhcp6Duid.DuidType.DUID_LLT) ||
527 (clientIdOption.getDuid().getDuidType() == Dhcp6Duid.DuidType.DUID_LL)) {
528 leafClientMac = MacAddress.valueOf(clientIdOption.getDuid().getLinkLayerAddress());
529 } else {
530 log.warn("Link-Layer Address not supported in CLIENTID option. No DhcpRelay Record created.");
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000531 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_LINKLOCAL_FAIL);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000532 return;
533 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000534 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000535 log.warn("CLIENTID option NOT found. Don't create DhcpRelay Record.");
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000536 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_CLIENTID_FAIL);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000537 return;
538 }
539
Kalhee Kim495c9b22017-11-07 16:32:09 +0000540 HostId leafHostId = HostId.hostId(leafClientMac, vlanId);
541 DhcpRecord record = dhcpRelayStore.getDhcpRecord(leafHostId).orElse(null);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000542 if (record == null) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000543 record = new DhcpRecord(leafHostId);
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000544 } else {
545 record = record.clone();
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000546 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000547
Kalhee Kim495c9b22017-11-07 16:32:09 +0000548 Boolean isMsgRelease = dhcp6HandlerUtil.isDhcp6Release(dhcp6Packet);
549 IpAddressInfo ipInfo;
550 PdPrefixInfo pdInfo = null;
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000551 if (directConnFlag) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000552 // Add to host store if it is connected to network directly
553 ipInfo = extractIpAddress(dhcp6Packet);
554 if (ipInfo != null) {
555 if (isMsgRelease) {
556 HostId hostId = HostId.hostId(srcMac, vlanId);
557 log.debug("remove Host {} ip for directly connected.", hostId.toString());
558 providerService.removeIpFromHost(hostId, ipInfo.ip6Address);
Kalhee Kim45fede42017-09-05 19:05:06 +0000559 }
560 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000561 log.debug("ipAddress not found. Do not remove Host {} for directly connected.",
562 HostId.hostId(srcMac, vlanId).toString());
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000563 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000564 leafMsgType = dhcp6Packet.getMsgType();
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000565 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000566 // Remove from route store if it is not connected to network directly
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000567 // pick out the first link-local ip address
Kalhee Kim495c9b22017-11-07 16:32:09 +0000568 IpAddress nextHopIp = getFirstIpByHost(directConnFlag, srcMac, vlanId);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000569 if (nextHopIp == null) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000570 log.warn("Can't find link-local IP address of gateway mac {} vlanId {}", srcMac, vlanId);
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000571 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_LINKLOCAL_GW);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000572 return;
573 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000574
Kalhee Kim495c9b22017-11-07 16:32:09 +0000575 DHCP6 leafDhcp = dhcp6HandlerUtil.getDhcp6Leaf(dhcp6Packet);
576 ipInfo = extractIpAddress(leafDhcp);
577 if (ipInfo == null) {
578 log.debug("ip is null");
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000579 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000580 if (isMsgRelease) {
581 Route routeForIP = new Route(Route.Source.STATIC, ipInfo.ip6Address.toIpPrefix(), nextHopIp);
582 log.debug("removing route of 128 address for indirectly connected.");
583 log.debug("128 ip {}, nexthop {}",
584 HexString.toHexString(ipInfo.ip6Address.toOctets(), ":"),
585 HexString.toHexString(nextHopIp.toOctets(), ":"));
586 routeStore.removeRoute(routeForIP);
Kalhee Kim45fede42017-09-05 19:05:06 +0000587 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000588 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000589
Kalhee Kim495c9b22017-11-07 16:32:09 +0000590 pdInfo = extractPrefix(leafDhcp);
591 if (pdInfo == null) {
592 log.debug("ipPrefix is null ");
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000593 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000594 if (isMsgRelease) {
595 Route routeForPrefix = new Route(Route.Source.STATIC, pdInfo.pdPrefix, nextHopIp);
596 log.debug("removing route of PD for indirectly connected.");
597 log.debug("pd ip {}, nexthop {}",
598 HexString.toHexString(pdInfo.pdPrefix.address().toOctets(), ":"),
599 HexString.toHexString(nextHopIp.toOctets(), ":"));
600
601 routeStore.removeRoute(routeForPrefix);
Kalhee Kimba366062017-11-07 16:32:09 +0000602 if (this.dhcpFpmEnabled) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000603 dhcpFpmPrefixStore.removeFpmRecord(pdInfo.pdPrefix);
Kalhee Kimba366062017-11-07 16:32:09 +0000604 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000605 }
606 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000607 leafMsgType = leafDhcp.getMsgType();
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000608 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000609
Kalhee Kim495c9b22017-11-07 16:32:09 +0000610 if (isMsgRelease) {
611 log.debug("DHCP6 RELEASE msg.");
612 if (record != null) {
613 if (ipInfo != null) {
614 log.debug("DhcpRelay Record ip6Address is set to null.");
615 record.ip6Address(null);
616 }
617 if (pdInfo != null) {
618 log.debug("DhcpRelay Record pdPrefix is set to null.");
619 }
620
621 if (!record.ip6Address().isPresent() && !record.pdPrefix().isPresent()) {
622 log.warn("IP6 address and IP6 PD both are null. Remove record.");
623 // do not remove a record. Let timer task handler it.
624 //dhcpRelayStore.removeDhcpRecord(HostId.hostId(leafClientMac, vlanId));
625 }
626 }
627 }
628
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000629 record.getV6Counters().incrementCounter(dhcp6HandlerUtil.getMsgTypeStr(leafMsgType));
Kalhee Kim495c9b22017-11-07 16:32:09 +0000630 record.addLocation(new HostLocation(location, System.currentTimeMillis()));
631 record.ip6Status(DHCP6.MsgType.getType(leafMsgType));
632 record.setDirectlyConnected(directConnFlag);
633 if (!directConnFlag) {
634 // Update gateway mac address if the host is not directly connected
635 record.nextHop(srcMac);
636 }
637 record.updateLastSeen();
638 dhcpRelayStore.updateDhcpRecord(leafHostId, record);
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000639 // TODO Use AtomicInteger for the counters
640 try {
641 recordSemaphore.acquire();
642 try {
643 dhcpRelayCountersStore.incrementCounter(gCount, dhcp6HandlerUtil.getMsgTypeStr(leafMsgType));
644 } finally {
645 // calling release() after a successful acquire()
646 recordSemaphore.release();
647 }
648 } catch (InterruptedException e) {
649 e.printStackTrace();
650 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000651 }
652
653 /**
654 * add host or route and update dhcp relay record.
655 *
656 * @param directConnFlag flag to show that packet is from directly connected client
657 * @param location client side connect point
658 * @param dhcp6Relay the dhcp6 payload
659 * @param embeddedDhcp6 the dhcp6 payload within relay
660 * @param srcMac client gw/host macAddress
661 * @param clientInterface client interface
662 */
663 private void addHostOrRoute(boolean directConnFlag, ConnectPoint location, DHCP6 dhcp6Relay,
664 DHCP6 embeddedDhcp6, MacAddress srcMac, Interface clientInterface) {
665 log.debug("addHostOrRoute entered.");
666 VlanId vlanId = clientInterface.vlan();
667 Boolean isMsgReply = dhcp6HandlerUtil.isDhcp6Reply(dhcp6Relay);
668 MacAddress leafClientMac;
669 Byte leafMsgType;
670
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000671 Dhcp6ClientIdOption clientIdOption = extractClinedId(directConnFlag, embeddedDhcp6);
672 if (clientIdOption != null) {
673 log.debug("CLIENTID option found {}", clientIdOption);
674 if ((clientIdOption.getDuid().getDuidType() == Dhcp6Duid.DuidType.DUID_LLT) ||
675 (clientIdOption.getDuid().getDuidType() == Dhcp6Duid.DuidType.DUID_LL)) {
676 leafClientMac = MacAddress.valueOf(clientIdOption.getDuid().getLinkLayerAddress());
677 } else {
678 log.warn("Link-Layer Address not supported in CLIENTID option. No DhcpRelay Record created.");
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000679 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_LINKLOCAL_FAIL);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000680 return;
681 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000682 } else {
683 log.warn("CLIENTID option NOT found. No DhcpRelay Record created.");
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000684 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_CLIENTID_FAIL);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000685 return;
686 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000687 HostId leafHostId = HostId.hostId(leafClientMac, vlanId);
688 DhcpRecord record = dhcpRelayStore.getDhcpRecord(leafHostId).orElse(null);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000689 if (record == null) {
690 record = new DhcpRecord(HostId.hostId(leafClientMac, vlanId));
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000691 } else {
692 record = record.clone();
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000693 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000694
695 IpAddressInfo ipInfo;
696 PdPrefixInfo pdInfo = null;
697 if (directConnFlag) {
698 // Add to host store if it connect to network directly
699 ipInfo = extractIpAddress(embeddedDhcp6);
700 if (ipInfo != null) {
701 if (isMsgReply) {
702 Set<IpAddress> ips = Sets.newHashSet(ipInfo.ip6Address);
703 HostId hostId = HostId.hostId(srcMac, vlanId);
704 Host host = hostService.getHost(hostId);
705 HostLocation hostLocation = new HostLocation(clientInterface.connectPoint(),
706 System.currentTimeMillis());
707 Set<HostLocation> hostLocations = Sets.newHashSet(hostLocation);
708 if (host != null) {
709 // Dual homing support:
710 // if host exists, use old locations and new location
711 hostLocations.addAll(host.locations());
712 }
713 HostDescription desc = new DefaultHostDescription(srcMac, vlanId, hostLocations, ips,
714 false);
715 log.debug("adding Host for directly connected.");
716 log.debug("client mac {} client vlan {} hostlocation {}",
717 HexString.toHexString(srcMac.toBytes(), ":"), vlanId, hostLocation.toString());
718 // Replace the ip when dhcp server give the host new ip address
719 providerService.hostDetected(hostId, desc, false);
720 }
721 } else {
722 log.warn("ipAddress not found. Do not add Host {} for directly connected.",
723 HostId.hostId(srcMac, vlanId).toString());
724 }
725 leafMsgType = embeddedDhcp6.getMsgType();
726 } else {
727 // Add to route store if it does not connect to network directly
728 // pick out the first link-local ip address
729 IpAddress nextHopIp = getFirstIpByHost(directConnFlag, srcMac, vlanId);
730 if (nextHopIp == null) {
731 log.warn("Can't find link-local IP address of gateway mac {} vlanId {}", srcMac, vlanId);
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000732 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_LINKLOCAL_GW);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000733 return;
734 }
735
736 DHCP6 leafDhcp = dhcp6HandlerUtil.getDhcp6Leaf(embeddedDhcp6);
737 ipInfo = extractIpAddress(leafDhcp);
738 if (ipInfo == null) {
739 log.debug("ip is null");
740 } else {
741 if (isMsgReply) {
742 Route routeForIP = new Route(Route.Source.STATIC, ipInfo.ip6Address.toIpPrefix(), nextHopIp);
743 log.debug("adding Route of 128 address for indirectly connected.");
744 routeStore.updateRoute(routeForIP);
745 }
746 }
747
748 pdInfo = extractPrefix(leafDhcp);
749 if (pdInfo == null) {
750 log.debug("ipPrefix is null ");
751 } else {
752 if (isMsgReply) {
753 Route routeForPrefix = new Route(Route.Source.STATIC, pdInfo.pdPrefix, nextHopIp);
754 log.debug("adding Route of PD for indirectly connected.");
755 routeStore.updateRoute(routeForPrefix);
756 if (this.dhcpFpmEnabled) {
757 FpmRecord fpmRecord = new FpmRecord(pdInfo.pdPrefix, nextHopIp, FpmRecord.Type.DHCP_RELAY);
758 dhcpFpmPrefixStore.addFpmRecord(pdInfo.pdPrefix, fpmRecord);
759 }
760 }
761 }
762 leafMsgType = leafDhcp.getMsgType();
763 }
764 if (leafMsgType == DHCP6.MsgType.RELEASE.value() ||
765 (leafMsgType == DHCP6.MsgType.REPLY.value()) && ipInfo == null) {
766 log.warn("DHCP6 RELEASE/REPLY(null ip) from Server. MsgType {}", leafMsgType);
767 //return;
768 }
769
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000770 record.addLocation(new HostLocation(location, System.currentTimeMillis()));
Kalhee Kim2f07c602017-11-15 18:57:53 +0000771
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000772 if (leafMsgType == DHCP6.MsgType.REPLY.value()) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000773 if (ipInfo != null) {
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000774 log.debug("IP6 address is being stored into dhcp-relay store.");
Kalhee Kim495c9b22017-11-07 16:32:09 +0000775 log.debug("IP6 address {}", HexString.toHexString(ipInfo.ip6Address.toOctets(), ":"));
776 record.ip6Address(ipInfo.ip6Address);
Kalhee Kim2f07c602017-11-15 18:57:53 +0000777 record.updateAddrPrefTime(ipInfo.prefTime);
778 record.updateLastIp6Update();
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000779 } else {
780 log.debug("IP6 address is not returned from server. Maybe only PD is returned.");
781 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000782 if (pdInfo != null) {
783 log.debug("IP6 PD address {}",
784 HexString.toHexString(pdInfo.pdPrefix.address().toOctets(), ":"));
Kalhee Kim2f07c602017-11-15 18:57:53 +0000785 record.pdPrefix(pdInfo.pdPrefix);
786 record.updatePdPrefTime(pdInfo.prefTime);
787 record.updateLastPdUpdate();
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000788 } else {
789 log.debug("IP6 PD address is not returned from server. Maybe only IPAddress is returned.");
790 }
791 }
Kalhee Kim2f07c602017-11-15 18:57:53 +0000792
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000793 record.getV6Counters().incrementCounter(dhcp6HandlerUtil.getMsgTypeStr(leafMsgType));
Kalhee Kim495c9b22017-11-07 16:32:09 +0000794 record.ip6Status(DHCP6.MsgType.getType(leafMsgType));
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000795 record.setDirectlyConnected(directConnFlag);
796 record.updateLastSeen();
Kalhee Kim495c9b22017-11-07 16:32:09 +0000797 dhcpRelayStore.updateDhcpRecord(leafHostId, record);
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000798 // TODO Use AtomicInteger for the counters
799 try {
800 recordSemaphore.acquire();
801 try {
802 dhcpRelayCountersStore.incrementCounter(gCount, dhcp6HandlerUtil.getMsgTypeStr(leafMsgType));
803 } finally {
804 // calling release() after a successful acquire()
805 recordSemaphore.release();
806 }
807 } catch (InterruptedException e) {
808 e.printStackTrace();
809 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000810 }
811
812 /**
Kalhee Kim495c9b22017-11-07 16:32:09 +0000813 * build the DHCP6 solicit/request packet with gatewayip.
Kalhee Kim45fede42017-09-05 19:05:06 +0000814 *
815 * @param context packet context
816 * @param clientPacket client ethernet packet
817 * @param clientInterfaces set of client side interfaces
818 */
Kalhee Kim495c9b22017-11-07 16:32:09 +0000819 private List<InternalPacket> processDhcp6PacketFromClient(PacketContext context,
820 Ethernet clientPacket,
821 Set<Interface> clientInterfaces) {
Yi Tseng3bd57ac2017-11-29 14:39:18 -0800822 ConnectPoint receivedFrom = context.inPacket().receivedFrom();
823 DeviceId receivedFromDevice = receivedFrom.deviceId();
Kalhee Kim495c9b22017-11-07 16:32:09 +0000824 Ip6Address relayAgentIp = dhcp6HandlerUtil.getRelayAgentIPv6Address(clientInterfaces);
Yi Tseng3bd57ac2017-11-29 14:39:18 -0800825 MacAddress relayAgentMac = clientInterfaces.iterator().next().mac();
826 if (relayAgentIp == null || relayAgentMac == null) {
827 log.warn("Missing DHCP relay agent interface Ipv6 addr config for "
Kalhee Kim495c9b22017-11-07 16:32:09 +0000828 + "packet from client on port: {}. Aborting packet processing",
829 clientInterfaces.iterator().next().connectPoint());
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000830 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_CLIENT_INTF_MAC);
Charles Chan239492c2018-01-22 13:27:28 -0800831 return Lists.newArrayList();
Kalhee Kim121ba922017-11-01 17:56:44 +0000832 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000833
Kalhee Kim495c9b22017-11-07 16:32:09 +0000834 IPv6 clientIpv6 = (IPv6) clientPacket.getPayload();
835 UDP clientUdp = (UDP) clientIpv6.getPayload();
836 DHCP6 clientDhcp6 = (DHCP6) clientUdp.getPayload();
837
838 boolean directConnFlag = dhcp6HandlerUtil.directlyConnected(clientDhcp6);
Charles Chana990ce92017-10-30 10:22:50 -0700839
Kalhee Kim121ba922017-11-01 17:56:44 +0000840 ConnectPoint clientConnectionPoint = context.inPacket().receivedFrom();
841 VlanId vlanIdInUse = VlanId.vlanId(clientPacket.getVlanID());
842 Interface clientInterface = interfaceService.getInterfacesByPort(clientConnectionPoint)
Kalhee Kim495c9b22017-11-07 16:32:09 +0000843 .stream().filter(iface -> dhcp6HandlerUtil.interfaceContainsVlan(iface, vlanIdInUse))
844 .findFirst()
845 .orElse(null);
Kalhee Kim45fede42017-09-05 19:05:06 +0000846
Kalhee Kim495c9b22017-11-07 16:32:09 +0000847 List<InternalPacket> internalPackets = new ArrayList<>();
848 List<DhcpServerInfo> serverInfoList = findValidServerInfo(directConnFlag);
849 List<DhcpServerInfo> copyServerInfoList = new ArrayList<DhcpServerInfo>(serverInfoList);
Kalhee Kim45b24182017-10-18 18:30:23 +0000850
Kalhee Kim495c9b22017-11-07 16:32:09 +0000851 for (DhcpServerInfo serverInfo : copyServerInfoList) {
852 if (!dhcp6HandlerUtil.checkDhcpServerConnPt(directConnFlag, serverInfo)) {
853 log.warn("Can't get server connect point, ignore");
854 continue;
855 }
856 DhcpServerInfo newServerInfo = getHostInfoForServerInfo(serverInfo, serverInfoList);
857 if (newServerInfo == null) {
858 log.warn("Can't get server interface with host info resolved, ignore");
859 continue;
860 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000861
Kalhee Kim495c9b22017-11-07 16:32:09 +0000862 Interface serverInterface = getServerInterface(newServerInfo);
863 if (serverInterface == null) {
864 log.warn("Can't get server interface, ignore");
865 continue;
866 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000867
Kalhee Kim495c9b22017-11-07 16:32:09 +0000868 Ethernet etherReply = dhcp6HandlerUtil.buildDhcp6PacketFromClient(context, clientPacket,
869 clientInterfaces, newServerInfo, serverInterface);
870 removeHostOrRoute(directConnFlag, clientConnectionPoint, clientDhcp6, clientPacket,
871 clientIpv6, clientInterface);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000872
Kalhee Kim495c9b22017-11-07 16:32:09 +0000873 InternalPacket internalPacket = new Dhcp6HandlerUtil().new InternalPacket(etherReply,
874 serverInfo.getDhcpServerConnectPoint().get());
875 internalPackets.add(internalPacket);
876 }
877 log.debug("num of client packets to send is{}", internalPackets.size());
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000878
Kalhee Kim495c9b22017-11-07 16:32:09 +0000879 return internalPackets;
880 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000881
882 /**
Kalhee Kim45fede42017-09-05 19:05:06 +0000883 * process the DHCP6 relay-reply packet from dhcp server.
884 *
885 * @param context packet context
886 * @param receivedPacket server ethernet packet
887 * @param recevingInterfaces set of server side interfaces
Kalhee Kim495c9b22017-11-07 16:32:09 +0000888 * @return internalPacket toward client
Kalhee Kim45fede42017-09-05 19:05:06 +0000889 */
890 private InternalPacket processDhcp6PacketFromServer(PacketContext context,
891 Ethernet receivedPacket, Set<Interface> recevingInterfaces) {
Kalhee Kim45fede42017-09-05 19:05:06 +0000892 // get dhcp6 header.
Ray Milkeyf0c47612017-09-28 11:29:38 -0700893 Ethernet etherReply = receivedPacket.duplicate();
Kalhee Kim45fede42017-09-05 19:05:06 +0000894 IPv6 ipv6Packet = (IPv6) etherReply.getPayload();
895 UDP udpPacket = (UDP) ipv6Packet.getPayload();
896 DHCP6 dhcp6Relay = (DHCP6) udpPacket.getPayload();
Kalhee Kim495c9b22017-11-07 16:32:09 +0000897 Boolean directConnFlag = dhcp6HandlerUtil.directlyConnected(dhcp6Relay);
Kalhee Kim45fede42017-09-05 19:05:06 +0000898
Kalhee Kim495c9b22017-11-07 16:32:09 +0000899 DHCP6 embeddedDhcp6 = dhcp6Relay.getOptions().stream()
900 .filter(opt -> opt instanceof Dhcp6RelayOption)
901 .map(BasePacket::getPayload)
902 .map(pld -> (DHCP6) pld)
903 .findFirst()
904 .orElse(null);
Kalhee Kimd21029f2017-09-26 20:21:53 +0000905 ConnectPoint inPort = context.inPacket().receivedFrom();
Kalhee Kim495c9b22017-11-07 16:32:09 +0000906 DhcpServerInfo foundServerInfo = findServerInfoFromServer(directConnFlag, inPort);
Kalhee Kimd21029f2017-09-26 20:21:53 +0000907
Kalhee Kim495c9b22017-11-07 16:32:09 +0000908 if (foundServerInfo == null) {
909 log.warn("Cannot find server info");
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000910 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_SERVER_INFO);
Kalhee Kimd21029f2017-09-26 20:21:53 +0000911 return null;
Kalhee Kim495c9b22017-11-07 16:32:09 +0000912 } else {
913 if (dhcp6HandlerUtil.isServerIpEmpty(foundServerInfo)) {
914 log.warn("Cannot find server info's ipaddress");
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000915 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_SERVER_IP6ADDR);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000916 return null;
917 }
Kalhee Kimd21029f2017-09-26 20:21:53 +0000918 }
919
Kalhee Kim45fede42017-09-05 19:05:06 +0000920 Dhcp6InterfaceIdOption interfaceIdOption = dhcp6Relay.getOptions().stream()
921 .filter(opt -> opt instanceof Dhcp6InterfaceIdOption)
922 .map(opt -> (Dhcp6InterfaceIdOption) opt)
923 .findFirst()
924 .orElse(null);
Kalhee Kim45fede42017-09-05 19:05:06 +0000925 if (interfaceIdOption == null) {
926 log.warn("Interface Id option is not present, abort packet...");
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000927 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.OPTION_MISSING_FAIL);
Kalhee Kim45fede42017-09-05 19:05:06 +0000928 return null;
929 }
930
931 MacAddress peerMac = interfaceIdOption.getMacAddress();
932 String clientConnectionPointStr = new String(interfaceIdOption.getInPort());
Kalhee Kim45fede42017-09-05 19:05:06 +0000933 ConnectPoint clientConnectionPoint = ConnectPoint.deviceConnectPoint(clientConnectionPointStr);
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +0000934 VlanId vlanIdInUse = VlanId.vlanId(interfaceIdOption.getVlanId());
935 Interface clientInterface = interfaceService.getInterfacesByPort(clientConnectionPoint)
Kalhee Kim495c9b22017-11-07 16:32:09 +0000936 .stream().filter(iface -> dhcp6HandlerUtil.interfaceContainsVlan(iface, vlanIdInUse))
937 .findFirst().orElse(null);
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +0000938 if (clientInterface == null) {
939 log.warn("Cannot get client interface for from packet, abort... vlan {}", vlanIdInUse.toString());
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000940 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_MATCHING_INTF);
Kalhee Kim45fede42017-09-05 19:05:06 +0000941 return null;
942 }
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +0000943 MacAddress relayAgentMac = clientInterface.mac();
Kalhee Kim45fede42017-09-05 19:05:06 +0000944 if (relayAgentMac == null) {
Yi Tseng68ef26b2017-12-18 17:10:00 -0800945 log.warn("Can not get client interface mac, abort packet..");
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000946 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_CLIENT_INTF_MAC);
Kalhee Kim45fede42017-09-05 19:05:06 +0000947 return null;
948 }
949 etherReply.setSourceMACAddress(relayAgentMac);
950
951 // find destMac
Yi Tseng68ef26b2017-12-18 17:10:00 -0800952 MacAddress clientMac;
Yi Tsengaa417a62017-09-08 17:22:51 -0700953 Ip6Address peerAddress = Ip6Address.valueOf(dhcp6Relay.getPeerAddress());
954 Set<Host> clients = hostService.getHostsByIp(peerAddress);
Kalhee Kim45fede42017-09-05 19:05:06 +0000955 if (clients.isEmpty()) {
Yi Tseng68ef26b2017-12-18 17:10:00 -0800956 log.trace("There's no host found for this address {}",
Kalhee Kim45fede42017-09-05 19:05:06 +0000957 HexString.toHexString(dhcp6Relay.getPeerAddress(), ":"));
Yi Tseng68ef26b2017-12-18 17:10:00 -0800958 log.trace("Let's look up interfaceId {}", HexString.toHexString(peerMac.toBytes(), ":"));
Kalhee Kim45fede42017-09-05 19:05:06 +0000959 clientMac = peerMac;
960 } else {
961 clientMac = clients.iterator().next().mac();
962 if (clientMac == null) {
963 log.warn("No client mac address found, abort packet...");
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000964 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_CLIENT_INTF_MAC);
Kalhee Kim45fede42017-09-05 19:05:06 +0000965 return null;
966 }
Yi Tseng68ef26b2017-12-18 17:10:00 -0800967 log.trace("Client mac address found from getHostByIp");
Kalhee Kim45fede42017-09-05 19:05:06 +0000968 }
969 etherReply.setDestinationMACAddress(clientMac);
Kalhee Kim45fede42017-09-05 19:05:06 +0000970 // ip header
971 ipv6Packet.setSourceAddress(dhcp6Relay.getLinkAddress());
972 ipv6Packet.setDestinationAddress(dhcp6Relay.getPeerAddress());
973 // udp header
974 udpPacket.setSourcePort(UDP.DHCP_V6_SERVER_PORT);
975 if (directConnFlag) {
976 udpPacket.setDestinationPort(UDP.DHCP_V6_CLIENT_PORT);
977 } else {
978 udpPacket.setDestinationPort(UDP.DHCP_V6_SERVER_PORT);
979 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000980 // add host or route
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000981 addHostOrRoute(directConnFlag, clientConnectionPoint, dhcp6Relay, embeddedDhcp6, clientMac, clientInterface);
Kalhee Kim45fede42017-09-05 19:05:06 +0000982
983 udpPacket.setPayload(embeddedDhcp6);
984 udpPacket.resetChecksum();
985 ipv6Packet.setPayload(udpPacket);
986 etherReply.setPayload(ipv6Packet);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000987 return new Dhcp6HandlerUtil().new InternalPacket(etherReply, clientConnectionPoint);
Kalhee Kim45fede42017-09-05 19:05:06 +0000988 }
989
Yi Tseng483ac6f2017-08-02 15:03:31 -0700990
991 @Override
Kalhee Kimba366062017-11-07 16:32:09 +0000992 public void setDhcpFpmEnabled(Boolean enabled) {
993 dhcpFpmEnabled = enabled;
994 }
995
996 @Override
Yi Tseng483ac6f2017-08-02 15:03:31 -0700997 public void setDefaultDhcpServerConfigs(Collection<DhcpServerConfig> configs) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000998 log.debug("setDefaultDhcpServerConfigs is called.");
Yi Tseng919b2df2017-09-07 16:22:51 -0700999 setDhcpServerConfigs(configs, defaultServerInfoList);
Yi Tseng919b2df2017-09-07 16:22:51 -07001000 }
1001
1002 @Override
1003 public void setIndirectDhcpServerConfigs(Collection<DhcpServerConfig> configs) {
Kalhee Kim495c9b22017-11-07 16:32:09 +00001004 log.debug("setIndirectDhcpServerConfigs is called.");
Yi Tseng919b2df2017-09-07 16:22:51 -07001005 setDhcpServerConfigs(configs, indirectServerInfoList);
Yi Tseng919b2df2017-09-07 16:22:51 -07001006 }
1007
1008 public void setDhcpServerConfigs(Collection<DhcpServerConfig> configs, List<DhcpServerInfo> serverInfoList) {
Kalhee Kim495c9b22017-11-07 16:32:09 +00001009 log.debug("config size {}.", configs.size());
1010
Kalhee Kim45fede42017-09-05 19:05:06 +00001011 if (configs.size() == 0) {
1012 // no config to update
1013 return;
1014 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001015 // TODO: currently we pick up first DHCP server config.
1016 // Will use other server configs in the future for HA.
Kalhee Kim495c9b22017-11-07 16:32:09 +00001017 Boolean isConfigValid = false;
1018 for (DhcpServerConfig serverConfig : configs) {
1019 if (serverConfig.getDhcpServerIp6().isPresent()) {
1020 isConfigValid = true;
1021 break;
1022 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001023 }
Kalhee Kim495c9b22017-11-07 16:32:09 +00001024 if (!isConfigValid) {
1025 log.warn("No IP V6 server address found.");
1026 return; // No IP V6 address found
1027 }
1028 for (DhcpServerInfo oldServerInfo : serverInfoList) {
Yi Tseng919b2df2017-09-07 16:22:51 -07001029 // stop monitoring gateway or server
1030 oldServerInfo.getDhcpGatewayIp6().ifPresent(gatewayIp -> {
1031 hostService.stopMonitoringIp(gatewayIp);
1032 });
1033 oldServerInfo.getDhcpServerIp6().ifPresent(serverIp -> {
1034 hostService.stopMonitoringIp(serverIp);
Yi Tseng525ff402017-10-23 19:39:39 -07001035 cancelDhcpPacket(serverIp);
Yi Tseng919b2df2017-09-07 16:22:51 -07001036 });
1037 }
Kalhee Kim495c9b22017-11-07 16:32:09 +00001038 serverInfoList.clear();
1039 for (DhcpServerConfig serverConfig : configs) {
1040 // Create new server info according to the config
1041 DhcpServerInfo newServerInfo = new DhcpServerInfo(serverConfig,
1042 DhcpServerInfo.Version.DHCP_V6);
1043 checkState(newServerInfo.getDhcpServerConnectPoint().isPresent(),
1044 "Connect point not exists");
1045 checkState(newServerInfo.getDhcpServerIp6().isPresent(),
1046 "IP of DHCP server not exists");
Yi Tseng919b2df2017-09-07 16:22:51 -07001047
Kalhee Kim495c9b22017-11-07 16:32:09 +00001048 log.debug("DHCP server connect point: {}", newServerInfo.getDhcpServerConnectPoint().orElse(null));
1049 log.debug("DHCP server IP: {}", newServerInfo.getDhcpServerIp6().orElse(null));
Yi Tseng919b2df2017-09-07 16:22:51 -07001050
Kalhee Kim495c9b22017-11-07 16:32:09 +00001051 Ip6Address serverIp = newServerInfo.getDhcpServerIp6().get();
1052 Ip6Address ipToProbe;
1053 if (newServerInfo.getDhcpGatewayIp6().isPresent()) {
1054 ipToProbe = newServerInfo.getDhcpGatewayIp6().get();
1055 } else {
1056 ipToProbe = newServerInfo.getDhcpServerIp6().orElse(null);
1057 }
1058 String hostToProbe = newServerInfo.getDhcpGatewayIp6()
1059 .map(ip -> "gateway").orElse("server");
Yi Tseng919b2df2017-09-07 16:22:51 -07001060
Kalhee Kim495c9b22017-11-07 16:32:09 +00001061 log.warn("Probing to resolve {} IP {}", hostToProbe, ipToProbe);
1062 hostService.startMonitoringIp(ipToProbe);
1063
1064 Set<Host> hosts = hostService.getHostsByIp(ipToProbe);
1065 if (!hosts.isEmpty()) {
1066 Host host = hosts.iterator().next();
1067 newServerInfo.setDhcpConnectVlan(host.vlan());
1068 newServerInfo.setDhcpConnectMac(host.mac());
1069 log.warn("Host found host {}", host);
1070
1071 } else {
1072 log.warn("No host found host ip {}", ipToProbe);
1073 }
1074 // Add new server info
1075 synchronized (this) {
1076 serverInfoList.add(newServerInfo);
1077 }
1078 if (!hosts.isEmpty()) {
1079 requestDhcpPacket(serverIp);
1080 }
Yi Tseng919b2df2017-09-07 16:22:51 -07001081 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001082 }
1083
1084 class InternalHostListener implements HostListener {
1085 @Override
1086 public void event(HostEvent event) {
1087 switch (event.type()) {
1088 case HOST_ADDED:
1089 case HOST_UPDATED:
1090 hostUpdated(event.subject());
1091 break;
1092 case HOST_REMOVED:
1093 hostRemoved(event.subject());
1094 break;
Kalhee Kim45fede42017-09-05 19:05:06 +00001095 default:
1096 break;
1097 }
1098 }
1099 }
1100
1101 /**
Kalhee Kim45fede42017-09-05 19:05:06 +00001102 * Handle host updated.
1103 * If the host is DHCP server or gateway, update connect mac and vlan.
1104 *
1105 * @param host the host
1106 */
1107 private void hostUpdated(Host host) {
Yi Tseng525ff402017-10-23 19:39:39 -07001108 hostUpdated(host, defaultServerInfoList);
1109 hostUpdated(host, indirectServerInfoList);
Kalhee Kim45fede42017-09-05 19:05:06 +00001110 }
1111
Yi Tseng525ff402017-10-23 19:39:39 -07001112 private void hostUpdated(Host host, List<DhcpServerInfo> serverInfoList) {
1113 DhcpServerInfo serverInfo;
1114 Ip6Address targetIp;
1115 if (!serverInfoList.isEmpty()) {
1116 serverInfo = serverInfoList.get(0);
1117 Ip6Address serverIp = serverInfo.getDhcpServerIp6().orElse(null);
1118 targetIp = serverInfo.getDhcpGatewayIp6().orElse(null);
1119
1120 if (targetIp == null) {
1121 targetIp = serverIp;
1122 }
Yi Tseng525ff402017-10-23 19:39:39 -07001123 if (targetIp != null) {
1124 if (host.ipAddresses().contains(targetIp)) {
1125 serverInfo.setDhcpConnectMac(host.mac());
1126 serverInfo.setDhcpConnectVlan(host.vlan());
1127 requestDhcpPacket(serverIp);
1128 }
1129 }
1130 }
1131 }
1132
Kalhee Kim45fede42017-09-05 19:05:06 +00001133 /**
1134 * Handle host removed.
1135 * If the host is DHCP server or gateway, unset connect mac and vlan.
1136 *
1137 * @param host the host
1138 */
1139 private void hostRemoved(Host host) {
Yi Tseng525ff402017-10-23 19:39:39 -07001140 hostRemoved(host, defaultServerInfoList);
1141 hostRemoved(host, indirectServerInfoList);
Yi Tseng919b2df2017-09-07 16:22:51 -07001142 }
1143
Yi Tseng525ff402017-10-23 19:39:39 -07001144 private void hostRemoved(Host host, List<DhcpServerInfo> serverInfoList) {
1145 DhcpServerInfo serverInfo;
1146 Ip6Address targetIp;
1147
1148 if (!serverInfoList.isEmpty()) {
1149 serverInfo = serverInfoList.get(0);
1150 Ip6Address serverIp = serverInfo.getDhcpServerIp6().orElse(null);
1151 targetIp = serverInfo.getDhcpGatewayIp6().orElse(null);
1152
1153 if (targetIp == null) {
1154 targetIp = serverIp;
1155 }
Yi Tseng525ff402017-10-23 19:39:39 -07001156 if (targetIp != null) {
1157 if (host.ipAddresses().contains(targetIp)) {
1158 serverInfo.setDhcpConnectVlan(null);
1159 serverInfo.setDhcpConnectMac(null);
1160 cancelDhcpPacket(serverIp);
1161 }
1162 }
1163 }
1164 }
1165
Kalhee Kim45b24182017-10-18 18:30:23 +00001166 /**
1167 * Returns the first interface ip from interface.
1168 *
1169 * @param iface interface of one connect point
1170 * @return the first interface IP; null if not exists an IP address in
1171 * these interfaces
1172 */
1173 private Ip6Address getFirstIpFromInterface(Interface iface) {
1174 checkNotNull(iface, "Interface can't be null");
1175 return iface.ipAddressesList().stream()
1176 .map(InterfaceIpAddress::ipAddress)
1177 .filter(IpAddress::isIp6)
1178 .map(IpAddress::getIp6Address)
1179 .findFirst()
1180 .orElse(null);
1181 }
Kalhee Kim45b24182017-10-18 18:30:23 +00001182 /**
Kalhee Kim495c9b22017-11-07 16:32:09 +00001183 * Checks if serverInfo's host info (mac and vlan) is filled in; if not, fills in.
1184 *
1185 * @param serverInfo server information
1186 * @return newServerInfo if host info can be either found or filled in.
1187 */
1188 private DhcpServerInfo getHostInfoForServerInfo(DhcpServerInfo serverInfo, List<DhcpServerInfo> sererInfoList) {
1189 DhcpServerInfo newServerInfo = null;
1190 MacAddress dhcpServerConnectMac = serverInfo.getDhcpConnectMac().orElse(null);
1191 VlanId dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1192 ConnectPoint dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1193
1194 if (dhcpServerConnectMac != null && dhcpConnectVlan != null) {
1195 newServerInfo = serverInfo;
1196 log.info("DHCP server {} host info found. ConnectPt{} Mac {} vlan {}", serverInfo.getDhcpServerIp6(),
1197 dhcpServerConnectPoint, dhcpServerConnectMac, dhcpConnectVlan);
1198 } else {
1199 log.warn("DHCP server {} not resolve yet connectPt {} mac {} vlan {}", serverInfo.getDhcpServerIp6(),
1200 dhcpServerConnectPoint, dhcpServerConnectMac, dhcpConnectVlan);
1201
1202 Ip6Address ipToProbe;
1203 if (serverInfo.getDhcpGatewayIp6().isPresent()) {
1204 ipToProbe = serverInfo.getDhcpGatewayIp6().get();
1205 } else {
1206 ipToProbe = serverInfo.getDhcpServerIp6().orElse(null);
1207 }
1208 String hostToProbe = serverInfo.getDhcpGatewayIp6()
1209 .map(ip -> "gateway").orElse("server");
1210
1211 log.info("Dynamically probing to resolve {} IP {}", hostToProbe, ipToProbe);
1212 hostService.startMonitoringIp(ipToProbe);
1213
1214 Set<Host> hosts = hostService.getHostsByIp(ipToProbe);
1215 if (!hosts.isEmpty()) {
1216 int serverInfoIndex = sererInfoList.indexOf(serverInfo);
1217 Host host = hosts.iterator().next();
1218 serverInfo.setDhcpConnectVlan(host.vlan());
1219 serverInfo.setDhcpConnectMac(host.mac());
1220 // replace the serverInfo in the list
1221 sererInfoList.set(serverInfoIndex, serverInfo);
1222 newServerInfo = serverInfo;
1223 log.warn("Dynamically host found host {}", host);
1224 } else {
1225 log.warn("No host found host ip {} dynamically", ipToProbe);
1226 }
1227 }
1228 return newServerInfo;
1229 }
1230
1231 /**
Kalhee Kim45b24182017-10-18 18:30:23 +00001232 * Gets Interface facing to the server for default host.
1233 *
Kalhee Kim495c9b22017-11-07 16:32:09 +00001234 * @param serverInfo server information
Kalhee Kim45b24182017-10-18 18:30:23 +00001235 * @return the Interface facing to the server; null if not found
1236 */
Kalhee Kim495c9b22017-11-07 16:32:09 +00001237 private Interface getServerInterface(DhcpServerInfo serverInfo) {
1238 Interface serverInterface = null;
Yi Tseng25bfe372017-11-03 16:27:32 -07001239
Kalhee Kim495c9b22017-11-07 16:32:09 +00001240 ConnectPoint dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1241 VlanId dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1242
1243 if (dhcpServerConnectPoint != null && dhcpConnectVlan != null) {
1244 serverInterface = interfaceService.getInterfacesByPort(dhcpServerConnectPoint)
1245 .stream()
1246 .filter(iface -> dhcp6HandlerUtil.interfaceContainsVlan(iface, dhcpConnectVlan))
1247 .findFirst()
1248 .orElse(null);
Yi Tseng25bfe372017-11-03 16:27:32 -07001249 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +00001250 log.warn("DHCP server {} not resolve yet connectPoint {} vlan {}", serverInfo.getDhcpServerIp6(),
1251 dhcpServerConnectPoint, dhcpConnectVlan);
Kalhee Kim45b24182017-10-18 18:30:23 +00001252 }
Kalhee Kim495c9b22017-11-07 16:32:09 +00001253
1254 return serverInterface;
Kalhee Kim45b24182017-10-18 18:30:23 +00001255 }
1256
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +00001257
Yi Tseng525ff402017-10-23 19:39:39 -07001258 private void requestDhcpPacket(Ip6Address serverIp) {
1259 requestServerDhcpPacket(serverIp);
1260 requestClientDhcpPacket(serverIp);
1261 }
1262
1263 private void cancelDhcpPacket(Ip6Address serverIp) {
1264 cancelServerDhcpPacket(serverIp);
1265 cancelClientDhcpPacket(serverIp);
1266 }
1267
1268 private void cancelServerDhcpPacket(Ip6Address serverIp) {
1269 TrafficSelector serverSelector =
1270 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1271 .matchIPv6Src(serverIp.toIpPrefix())
1272 .build();
1273 packetService.cancelPackets(serverSelector,
1274 PacketPriority.CONTROL,
1275 appId);
1276 }
1277
1278 private void requestServerDhcpPacket(Ip6Address serverIp) {
1279 TrafficSelector serverSelector =
1280 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1281 .matchIPv6Src(serverIp.toIpPrefix())
1282 .build();
1283 packetService.requestPackets(serverSelector,
1284 PacketPriority.CONTROL,
1285 appId);
1286 }
1287
1288 private void cancelClientDhcpPacket(Ip6Address serverIp) {
1289 // Packet comes from relay
1290 TrafficSelector indirectClientSelector =
1291 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1292 .matchIPv6Dst(serverIp.toIpPrefix())
1293 .build();
1294 packetService.cancelPackets(indirectClientSelector,
1295 PacketPriority.CONTROL,
1296 appId);
Yi Tseng41dde702017-11-02 15:21:10 -07001297 indirectClientSelector =
1298 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1299 .matchIPv6Dst(Ip6Address.ALL_DHCP_RELAY_AGENTS_AND_SERVERS.toIpPrefix())
1300 .build();
1301 packetService.cancelPackets(indirectClientSelector,
1302 PacketPriority.CONTROL,
1303 appId);
1304 indirectClientSelector =
1305 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1306 .matchIPv6Dst(Ip6Address.ALL_DHCP_SERVERS.toIpPrefix())
1307 .build();
1308 packetService.cancelPackets(indirectClientSelector,
1309 PacketPriority.CONTROL,
1310 appId);
Yi Tseng525ff402017-10-23 19:39:39 -07001311
1312 // Packet comes from client
1313 packetService.cancelPackets(CLIENT_SERVER_SELECTOR,
1314 PacketPriority.CONTROL,
1315 appId);
1316 }
1317
1318 private void requestClientDhcpPacket(Ip6Address serverIp) {
1319 // Packet comes from relay
1320 TrafficSelector indirectClientSelector =
1321 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1322 .matchIPv6Dst(serverIp.toIpPrefix())
1323 .build();
1324 packetService.requestPackets(indirectClientSelector,
1325 PacketPriority.CONTROL,
1326 appId);
Yi Tseng41dde702017-11-02 15:21:10 -07001327 indirectClientSelector =
1328 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1329 .matchIPv6Dst(Ip6Address.ALL_DHCP_RELAY_AGENTS_AND_SERVERS.toIpPrefix())
1330 .build();
1331 packetService.requestPackets(indirectClientSelector,
1332 PacketPriority.CONTROL,
1333 appId);
1334 indirectClientSelector =
1335 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1336 .matchIPv6Dst(Ip6Address.ALL_DHCP_SERVERS.toIpPrefix())
1337 .build();
1338 packetService.requestPackets(indirectClientSelector,
1339 PacketPriority.CONTROL,
1340 appId);
Yi Tseng525ff402017-10-23 19:39:39 -07001341
1342 // Packet comes from client
1343 packetService.requestPackets(CLIENT_SERVER_SELECTOR,
1344 PacketPriority.CONTROL,
1345 appId);
1346 }
1347
1348 /**
1349 * Process the ignore rules.
1350 *
1351 * @param deviceId the device id
1352 * @param vlanId the vlan to be ignored
1353 * @param op the operation, ADD to install; REMOVE to uninstall rules
1354 */
1355 private void processIgnoreVlanRule(DeviceId deviceId, VlanId vlanId, Objective.Operation op) {
Yi Tseng525ff402017-10-23 19:39:39 -07001356 AtomicInteger installedCount = new AtomicInteger(DHCP_SELECTORS.size());
1357 DHCP_SELECTORS.forEach(trafficSelector -> {
1358 TrafficSelector selector = DefaultTrafficSelector.builder(trafficSelector)
1359 .matchVlanId(vlanId)
1360 .build();
1361
1362 ForwardingObjective.Builder builder = DefaultForwardingObjective.builder()
1363 .withFlag(ForwardingObjective.Flag.VERSATILE)
1364 .withSelector(selector)
1365 .withPriority(IGNORE_CONTROL_PRIORITY)
Yi Tsengdbabeed2017-11-29 10:49:20 -08001366 .withTreatment(DefaultTrafficTreatment.emptyTreatment())
Yi Tseng525ff402017-10-23 19:39:39 -07001367 .fromApp(appId);
1368
1369
1370 ObjectiveContext objectiveContext = new ObjectiveContext() {
1371 @Override
1372 public void onSuccess(Objective objective) {
1373 log.info("Ignore rule {} (Vlan id {}, device {}, selector {})",
1374 op, vlanId, deviceId, selector);
1375 int countDown = installedCount.decrementAndGet();
1376 if (countDown != 0) {
1377 return;
1378 }
1379 switch (op) {
1380 case ADD:
1381 ignoredVlans.put(deviceId, vlanId);
1382 break;
1383 case REMOVE:
1384 ignoredVlans.remove(deviceId, vlanId);
1385 break;
1386 default:
1387 log.warn("Unsupported objective operation {}", op);
1388 break;
1389 }
1390 }
1391
1392 @Override
1393 public void onError(Objective objective, ObjectiveError error) {
1394 log.warn("Can't {} ignore rule (vlan id {}, selector {}, device {}) due to {}",
1395 op, vlanId, selector, deviceId, error);
1396 }
1397 };
1398
1399 ForwardingObjective fwd;
1400 switch (op) {
1401 case ADD:
1402 fwd = builder.add(objectiveContext);
1403 break;
1404 case REMOVE:
1405 fwd = builder.remove(objectiveContext);
1406 break;
1407 default:
1408 log.warn("Unsupported objective operation {}", op);
1409 return;
1410 }
1411
1412 Device device = deviceService.getDevice(deviceId);
1413 if (device == null || !device.is(Pipeliner.class)) {
1414 log.warn("Device {} is not available now, wait until device is available", deviceId);
1415 return;
1416 }
1417 flowObjectiveService.apply(deviceId, fwd);
1418 });
1419 }
Kalhee Kim121ba922017-11-01 17:56:44 +00001420 /**
1421 * Find first ipaddress for a given Host info i.e. mac and vlan.
1422 *
1423 * @param clientMac client mac
1424 * @param vlanId packet's vlan
1425 * @return next-hop link-local ipaddress for a given host
1426 */
Kalhee Kim495c9b22017-11-07 16:32:09 +00001427 private IpAddress getFirstIpByHost(Boolean directConnFlag, MacAddress clientMac, VlanId vlanId) {
Kalhee Kim121ba922017-11-01 17:56:44 +00001428 IpAddress nextHopIp;
1429 // pick out the first link-local ip address
1430 HostId gwHostId = HostId.hostId(clientMac, vlanId);
1431 Host gwHost = hostService.getHost(gwHostId);
1432 if (gwHost == null) {
1433 log.warn("Can't find gateway host for hostId {}", gwHostId);
1434 return null;
1435 }
Kalhee Kim495c9b22017-11-07 16:32:09 +00001436 if (directConnFlag) {
1437 nextHopIp = gwHost.ipAddresses()
1438 .stream()
1439 .filter(IpAddress::isIp6)
1440 .map(IpAddress::getIp6Address)
1441 .findFirst()
1442 .orElse(null);
1443 } else {
1444 nextHopIp = gwHost.ipAddresses()
1445 .stream()
1446 .filter(IpAddress::isIp6)
1447 .filter(ip6 -> ip6.isLinkLocal())
1448 .map(IpAddress::getIp6Address)
1449 .findFirst()
1450 .orElse(null);
1451 }
Kalhee Kim121ba922017-11-01 17:56:44 +00001452 return nextHopIp;
1453 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +00001454
Kalhee Kim495c9b22017-11-07 16:32:09 +00001455 private List<DhcpServerInfo> findValidServerInfo(boolean directConnFlag) {
1456 List<DhcpServerInfo> validServerInfo;
1457
1458 if (directConnFlag || indirectServerInfoList.isEmpty()) {
1459 validServerInfo = new ArrayList<DhcpServerInfo>(defaultServerInfoList);
1460 } else {
1461 validServerInfo = new ArrayList<DhcpServerInfo>(indirectServerInfoList);
1462 }
1463 return validServerInfo;
1464 }
1465
1466 private DhcpServerInfo findServerInfoFromServer(boolean directConnFlag, ConnectPoint inPort) {
1467 List<DhcpServerInfo> validServerInfoList = findValidServerInfo(directConnFlag);
1468 DhcpServerInfo foundServerInfo = null;
1469 for (DhcpServerInfo serverInfo : validServerInfoList) {
1470 if (inPort.equals(serverInfo.getDhcpServerConnectPoint().get())) {
1471 foundServerInfo = serverInfo;
1472 log.warn("ServerInfo found for Rcv port {} Server Connect Point {} for {}",
1473 inPort, serverInfo.getDhcpServerConnectPoint(), directConnFlag ? "direct" : "indirect");
1474 break;
1475 } else {
1476 log.warn("Rcv port {} not the same as Server Connect Point {} for {}",
1477 inPort, serverInfo.getDhcpServerConnectPoint(), directConnFlag ? "direct" : "indirect");
1478 }
1479 }
1480 return foundServerInfo;
1481 }
1482 /**
1483 * Set the dhcp6 lease expiry poll interval value.
1484 *
1485 * @param val poll interval value in seconds
1486 */
Kalhee Kim2f07c602017-11-15 18:57:53 +00001487 @Override
Kalhee Kim495c9b22017-11-07 16:32:09 +00001488 public void setDhcp6PollInterval(int val) {
1489 dhcp6PollInterval = val;
1490 }
1491
Kalhee Kim2f07c602017-11-15 18:57:53 +00001492 /**
1493 * get the dhcp6 lease expiry poll interval value.
1494 * This is a private function
1495 * @return poll interval value in seconds
1496 */
1497 private int getDhcp6PollInterval() {
1498 return dhcp6PollInterval;
1499 }
1500
1501 /**
1502 * Find lease-expired ipaddresses and pd prefixes.
1503 * Removing host/route/fpm entries.
1504 */
1505 public void timeTick() {
1506 long currentTime = System.currentTimeMillis();
1507 Collection<DhcpRecord> records = dhcpRelayStore.getDhcpRecords();
1508
1509 log.debug("timeTick called currenttime {} records num {} ", currentTime, records.size());
1510
1511 records.forEach(record -> {
1512 boolean addrOrPdRemoved = false;
1513 DHCP6.MsgType ip6Status = record.ip6Status().orElse(null);
1514 if (ip6Status == null) {
1515 log.debug("record is not valid v6 record.");
1516 return;
1517 }
1518
1519 if ((currentTime - record.getLastIp6Update()) >
1520 ((record.addrPrefTime() + getDhcp6PollInterval() / 2) * 1000)) {
1521 // remove ipaddress from host/route table
1522 IpAddress ip = record.ip6Address().orElse(null);
1523 if (ip != null) {
1524 if (record.directlyConnected()) {
1525 providerService.removeIpFromHost(HostId.hostId(record.macAddress(),
1526 record.vlanId()), ip);
1527 } else {
1528 MacAddress gwMac = record.nextHop().orElse(null);
1529 if (gwMac == null) {
1530 log.warn("Can't find gateway mac address from record {} for ip6Addr", record);
1531 return;
1532 }
1533 IpAddress nextHopIp = getFirstIpByHost(record.directlyConnected(),
1534 gwMac,
1535 record.vlanId());
1536 Route route = new Route(Route.Source.STATIC, ip.toIpPrefix(), nextHopIp);
1537 routeStore.removeRoute(route);
1538 }
1539 record.updateAddrPrefTime(0);
1540 record.ip6Address(null);
1541 addrOrPdRemoved = true;
1542 dhcpRelayStore.updateDhcpRecord(HostId.hostId(record.macAddress(),
1543 record.vlanId()), record);
1544 log.warn("IP6 address is set to null. delta {} lastUpdate {} addrPrefTime {}",
1545 (currentTime - record.getLastIp6Update()), record.getLastIp6Update(),
1546 record.addrPrefTime());
1547 }
1548 }
1549 if ((currentTime - record.getLastPdUpdate()) >
1550 ((record.pdPrefTime() + getDhcp6PollInterval() / 2) * 1000)) {
1551 // remove PD from route/fpm table
1552 IpPrefix pdIpPrefix = record.pdPrefix().orElse(null);
1553 if (pdIpPrefix != null) {
1554 if (record.directlyConnected()) {
1555 providerService.removeIpFromHost(HostId.hostId(record.macAddress(), record.vlanId()),
1556 pdIpPrefix.address().getIp6Address());
1557 } else {
1558 MacAddress gwMac = record.nextHop().orElse(null);
1559 if (gwMac == null) {
1560 log.warn("Can't find gateway mac address from record {} for PD prefix", record);
1561 return;
1562 }
1563 IpAddress nextHopIp = getFirstIpByHost(record.directlyConnected(),
1564 gwMac,
1565 record.vlanId());
1566 Route route = new Route(Route.Source.STATIC, pdIpPrefix, nextHopIp);
1567 routeStore.removeRoute(route);
1568 if (this.dhcpFpmEnabled) {
1569 dhcpFpmPrefixStore.removeFpmRecord(pdIpPrefix);
1570 }
1571 }
1572 record.updatePdPrefTime(0);
1573 record.pdPrefix(null);
1574 addrOrPdRemoved = true;
1575 dhcpRelayStore.updateDhcpRecord(HostId.hostId(record.macAddress(),
1576 record.vlanId()), record);
1577 log.warn("PD prefix is set to null.delta {} pdPrefTime {}",
1578 (currentTime - record.getLastPdUpdate()), record.pdPrefTime());
1579 }
1580 }
1581 if (addrOrPdRemoved &&
1582 !record.ip6Address().isPresent() && !record.pdPrefix().isPresent()) {
1583 log.warn("ip6Status {} IP6 address and IP6 PD both are null. Remove record.", ip6Status);
1584 dhcpRelayStore.removeDhcpRecord(HostId.hostId(record.macAddress(), record.vlanId()));
1585 }
1586 }
1587 );
1588 }
1589}