blob: 525aedfb2b112b8e4103163053919f2d50e61729 [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
106import java.nio.ByteBuffer;
107import java.util.List;
Yi Tseng483ac6f2017-08-02 15:03:31 -0700108import java.util.Collection;
Yi Tseng51301292017-07-28 13:02:59 -0700109import java.util.Optional;
Kalhee Kim45fede42017-09-05 19:05:06 +0000110import java.util.Set;
111import java.util.ArrayList;
Yi Tseng525ff402017-10-23 19:39:39 -0700112import java.util.concurrent.atomic.AtomicInteger;
Kalhee Kim45fede42017-09-05 19:05:06 +0000113import static com.google.common.base.Preconditions.checkNotNull;
114import static com.google.common.base.Preconditions.checkState;
Yi Tseng525ff402017-10-23 19:39:39 -0700115import static org.onosproject.net.flowobjective.Objective.Operation.ADD;
116import static org.onosproject.net.flowobjective.Objective.Operation.REMOVE;
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000117import java.util.concurrent.Semaphore;
Yi Tseng51301292017-07-28 13:02:59 -0700118
119@Component
120@Service
121@Property(name = "version", value = "6")
Yi Tsengaa417a62017-09-08 17:22:51 -0700122public class Dhcp6HandlerImpl implements DhcpHandler, HostProvider {
Charles Chand988c282017-09-12 17:09:32 -0700123 public static final String DHCP_V6_RELAY_APP = "org.onosproject.Dhcp6HandlerImpl";
Saurav Das7c6dec12017-09-13 14:35:56 -0700124 public static final ProviderId PROVIDER_ID = new ProviderId("dhcp6", DHCP_V6_RELAY_APP);
Yi Tseng525ff402017-10-23 19:39:39 -0700125 private static final int IGNORE_CONTROL_PRIORITY = PacketPriority.CONTROL.priorityValue() + 1000;
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000126 private String gCount = "global";
Yi Tseng525ff402017-10-23 19:39:39 -0700127 private static final TrafficSelector CLIENT_SERVER_SELECTOR = DefaultTrafficSelector.builder()
128 .matchEthType(Ethernet.TYPE_IPV6)
129 .matchIPProtocol(IPv6.PROTOCOL_UDP)
130 .matchIPv6Src(IpPrefix.IPV6_LINK_LOCAL_PREFIX)
131 .matchIPv6Dst(Ip6Address.ALL_DHCP_RELAY_AGENTS_AND_SERVERS.toIpPrefix())
132 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_V6_CLIENT_PORT))
133 .matchUdpDst(TpPort.tpPort(UDP.DHCP_V6_SERVER_PORT))
134 .build();
135 private static final TrafficSelector SERVER_RELAY_SELECTOR = DefaultTrafficSelector.builder()
136 .matchEthType(Ethernet.TYPE_IPV6)
137 .matchIPProtocol(IPv6.PROTOCOL_UDP)
138 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_V6_SERVER_PORT))
139 .matchUdpDst(TpPort.tpPort(UDP.DHCP_V6_SERVER_PORT))
140 .build();
141 static final Set<TrafficSelector> DHCP_SELECTORS = ImmutableSet.of(
142 CLIENT_SERVER_SELECTOR,
143 SERVER_RELAY_SELECTOR
144 );
Kalhee Kim45fede42017-09-05 19:05:06 +0000145 private static Logger log = LoggerFactory.getLogger(Dhcp6HandlerImpl.class);
Yi Tseng51301292017-07-28 13:02:59 -0700146
Kalhee Kim45fede42017-09-05 19:05:06 +0000147 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
148 protected DhcpRelayStore dhcpRelayStore;
Yi Tseng51301292017-07-28 13:02:59 -0700149
Kalhee Kim45fede42017-09-05 19:05:06 +0000150 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000151 protected DhcpRelayCountersStore dhcpRelayCountersStore;
152
153 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Kalhee Kim45fede42017-09-05 19:05:06 +0000154 protected PacketService packetService;
155
156 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Kalhee Kim45fede42017-09-05 19:05:06 +0000157 protected RouteStore routeStore;
158
159 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
160 protected InterfaceService interfaceService;
161
162 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
163 protected HostService hostService;
164
Yi Tsengaa417a62017-09-08 17:22:51 -0700165 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
166 protected HostProviderRegistry providerRegistry;
Kalhee Kim45fede42017-09-05 19:05:06 +0000167
Yi Tseng525ff402017-10-23 19:39:39 -0700168 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
169 protected CoreService coreService;
170
171 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Kalhee Kimba366062017-11-07 16:32:09 +0000172 protected DhcpFpmPrefixStore dhcpFpmPrefixStore;
173
174 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Yi Tseng525ff402017-10-23 19:39:39 -0700175 protected DeviceService deviceService;
176
177 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
178 protected FlowObjectiveService flowObjectiveService;
179
Yi Tsengaa417a62017-09-08 17:22:51 -0700180 protected HostProviderService providerService;
Yi Tseng525ff402017-10-23 19:39:39 -0700181 protected ApplicationId appId;
182 protected Multimap<DeviceId, VlanId> ignoredVlans = HashMultimap.create();
183 private InternalHostListener hostListener = new InternalHostListener();
184
Kalhee Kimba366062017-11-07 16:32:09 +0000185 private Boolean dhcpFpmEnabled = false;
186
Kalhee Kim495c9b22017-11-07 16:32:09 +0000187 private Dhcp6HandlerUtil dhcp6HandlerUtil = new Dhcp6HandlerUtil();
188
Yi Tseng919b2df2017-09-07 16:22:51 -0700189 private List<DhcpServerInfo> defaultServerInfoList = Lists.newArrayList();
190 private List<DhcpServerInfo> indirectServerInfoList = Lists.newArrayList();
Kalhee Kim495c9b22017-11-07 16:32:09 +0000191 private class IpAddressInfo {
192 Ip6Address ip6Address;
193 long prefTime;
194 }
195 private class PdPrefixInfo {
196 IpPrefix pdPrefix;
197 long prefTime;
198 }
199 protected int dhcp6PollInterval = 24 * 3600; // 24 hr period
200
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000201 // max 1 thread
202 static Semaphore recordSemaphore = new Semaphore(1);
Kalhee Kim45fede42017-09-05 19:05:06 +0000203
204 // CLIENT message types
205 public static final Set<Byte> MSG_TYPE_FROM_CLIENT =
206 ImmutableSet.of(DHCP6.MsgType.SOLICIT.value(),
207 DHCP6.MsgType.REQUEST.value(),
208 DHCP6.MsgType.REBIND.value(),
209 DHCP6.MsgType.RENEW.value(),
210 DHCP6.MsgType.RELEASE.value(),
211 DHCP6.MsgType.DECLINE.value(),
212 DHCP6.MsgType.CONFIRM.value(),
213 DHCP6.MsgType.RELAY_FORW.value());
214 // SERVER message types
215 public static final Set<Byte> MSG_TYPE_FROM_SERVER =
216 ImmutableSet.of(DHCP6.MsgType.RELAY_REPL.value());
217
218 @Activate
219 protected void activate() {
Yi Tseng525ff402017-10-23 19:39:39 -0700220 appId = coreService.registerApplication(DHCP_V6_RELAY_APP);
Yi Tsengaa417a62017-09-08 17:22:51 -0700221 providerService = providerRegistry.register(this);
Kalhee Kim45fede42017-09-05 19:05:06 +0000222 hostService.addListener(hostListener);
Yi Tseng51301292017-07-28 13:02:59 -0700223 }
224
Kalhee Kim45fede42017-09-05 19:05:06 +0000225 @Deactivate
226 protected void deactivate() {
Yi Tsengaa417a62017-09-08 17:22:51 -0700227 providerRegistry.unregister(this);
Kalhee Kim45fede42017-09-05 19:05:06 +0000228 hostService.removeListener(hostListener);
Yi Tseng919b2df2017-09-07 16:22:51 -0700229 defaultServerInfoList.forEach(this::stopMonitoringIps);
230 defaultServerInfoList.clear();
231 indirectServerInfoList.forEach(this::stopMonitoringIps);
232 indirectServerInfoList.clear();
233 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000234
Yi Tseng919b2df2017-09-07 16:22:51 -0700235 private void stopMonitoringIps(DhcpServerInfo serverInfo) {
236 serverInfo.getDhcpGatewayIp6().ifPresent(gatewayIp -> {
237 hostService.stopMonitoringIp(gatewayIp);
238 });
239 serverInfo.getDhcpServerIp6().ifPresent(serverIp -> {
240 hostService.stopMonitoringIp(serverIp);
241 });
Yi Tseng51301292017-07-28 13:02:59 -0700242 }
243
Yi Tseng51301292017-07-28 13:02:59 -0700244 @Override
Yi Tseng919b2df2017-09-07 16:22:51 -0700245 public List<DhcpServerInfo> getDefaultDhcpServerInfoList() {
246 return defaultServerInfoList;
Yi Tseng51301292017-07-28 13:02:59 -0700247 }
248
249 @Override
Yi Tseng919b2df2017-09-07 16:22:51 -0700250 public List<DhcpServerInfo> getIndirectDhcpServerInfoList() {
251 return indirectServerInfoList;
Yi Tseng51301292017-07-28 13:02:59 -0700252 }
253
254 @Override
Yi Tseng525ff402017-10-23 19:39:39 -0700255 public void updateIgnoreVlanConfig(IgnoreDhcpConfig config) {
256 if (config == null) {
257 ignoredVlans.forEach(((deviceId, vlanId) -> {
258 processIgnoreVlanRule(deviceId, vlanId, REMOVE);
259 }));
260 return;
261 }
262 config.ignoredVlans().forEach((deviceId, vlanId) -> {
263 if (ignoredVlans.get(deviceId).contains(vlanId)) {
264 // don't need to process if it already ignored
265 return;
266 }
267 processIgnoreVlanRule(deviceId, vlanId, ADD);
268 });
269
270 ignoredVlans.forEach((deviceId, vlanId) -> {
271 if (!config.ignoredVlans().get(deviceId).contains(vlanId)) {
272 // not contains in new config, remove it
273 processIgnoreVlanRule(deviceId, vlanId, REMOVE);
274 }
275 });
276 }
277
278 @Override
Saurav Dasb805f1a2017-12-13 16:19:35 -0800279 public void removeIgnoreVlanState(IgnoreDhcpConfig config) {
280 if (config == null) {
281 ignoredVlans.clear();
282 return;
283 }
284 config.ignoredVlans().forEach((deviceId, vlanId) -> {
285 ignoredVlans.remove(deviceId, vlanId);
286 });
287 }
288
289 @Override
Kalhee Kim45fede42017-09-05 19:05:06 +0000290 public void processDhcpPacket(PacketContext context, BasePacket payload) {
291 checkNotNull(payload, "DHCP6 payload can't be null");
292 checkState(payload instanceof DHCP6, "Payload is not a DHCP6");
293 DHCP6 dhcp6Payload = (DHCP6) payload;
294 Ethernet receivedPacket = context.inPacket().parsed();
295
296 if (!configured()) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000297 log.warn("Missing DHCP6 relay server config. Abort packet processing dhcp6 payload {}", dhcp6Payload);
Kalhee Kim45fede42017-09-05 19:05:06 +0000298 return;
299 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000300 byte msgTypeVal = dhcp6Payload.getMsgType();
301 MsgType msgType = DHCP6.MsgType.getType(msgTypeVal);
302 log.debug("msgType is {}", msgType);
Kalhee Kim45fede42017-09-05 19:05:06 +0000303
Kalhee Kim45fede42017-09-05 19:05:06 +0000304 ConnectPoint inPort = context.inPacket().receivedFrom();
Jonathan Hart8ca2bc02017-11-30 18:23:42 -0800305
Kalhee Kim495c9b22017-11-07 16:32:09 +0000306 if (inPort == null) {
307 log.warn("incomming ConnectPoint is null");
308 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000309 Set<Interface> receivingInterfaces = interfaceService.getInterfacesByPort(inPort);
310 //ignore the packets if dhcp client interface is not configured on onos.
311 if (receivingInterfaces.isEmpty()) {
312 log.warn("Virtual interface is not configured on {}", inPort);
313 return;
314 }
315
Kalhee Kim495c9b22017-11-07 16:32:09 +0000316 if (MSG_TYPE_FROM_CLIENT.contains(msgTypeVal)) {
Kalhee Kim45fede42017-09-05 19:05:06 +0000317
Kalhee Kim495c9b22017-11-07 16:32:09 +0000318 List<InternalPacket> ethernetClientPacket =
Kalhee Kim45fede42017-09-05 19:05:06 +0000319 processDhcp6PacketFromClient(context, receivedPacket, receivingInterfaces);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000320 for (InternalPacket internalPacket : ethernetClientPacket) {
321 forwardPacket(internalPacket);
Kalhee Kim45fede42017-09-05 19:05:06 +0000322 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000323 } else if (MSG_TYPE_FROM_SERVER.contains(msgTypeVal)) {
324 log.debug("calling processDhcp6PacketFromServer with RELAY_REPL {}", msgTypeVal);
Kalhee Kim45fede42017-09-05 19:05:06 +0000325 InternalPacket ethernetPacketReply =
326 processDhcp6PacketFromServer(context, receivedPacket, receivingInterfaces);
327 if (ethernetPacketReply != null) {
328 forwardPacket(ethernetPacketReply);
329 }
330 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000331 log.warn("Not so fast, packet type {} not supported yet", msgTypeVal);
Kalhee Kim45fede42017-09-05 19:05:06 +0000332 }
333 }
334
Kalhee Kim45fede42017-09-05 19:05:06 +0000335 /**
336 * Checks if this app has been configured.
337 *
338 * @return true if all information we need have been initialized
339 */
340 public boolean configured() {
Yi Tseng919b2df2017-09-07 16:22:51 -0700341 return !defaultServerInfoList.isEmpty();
Kalhee Kim45fede42017-09-05 19:05:06 +0000342 }
343
Yi Tsengaa417a62017-09-08 17:22:51 -0700344 @Override
345 public ProviderId id() {
Charles Chand988c282017-09-12 17:09:32 -0700346 return PROVIDER_ID;
Yi Tsengaa417a62017-09-08 17:22:51 -0700347 }
348
349 @Override
350 public void triggerProbe(Host host) {
351 // Do nothing here
352 }
353
Kalhee Kim495c9b22017-11-07 16:32:09 +0000354
Kalhee Kim45fede42017-09-05 19:05:06 +0000355
356 //forward the packet to ConnectPoint where the DHCP server is attached.
357 private void forwardPacket(InternalPacket packet) {
358 //send Packetout to dhcp server connectpoint.
359 if (packet.destLocation != null) {
360 TrafficTreatment t = DefaultTrafficTreatment.builder()
361 .setOutput(packet.destLocation.port()).build();
362 OutboundPacket o = new DefaultOutboundPacket(
363 packet.destLocation.deviceId(), t, ByteBuffer.wrap(packet.packet.serialize()));
364 if (log.isTraceEnabled()) {
365 log.trace("Relaying packet to destination {}", packet.destLocation);
366 }
367 packetService.emit(o);
368 } // if
369 }
370
Kalhee Kim45fede42017-09-05 19:05:06 +0000371
372
Kalhee Kim45fede42017-09-05 19:05:06 +0000373
374 /**
375 * extract from dhcp6 packet client ipv6 address of given by dhcp server.
376 *
377 * @param dhcp6 the dhcp6 packet
Kalhee Kim495c9b22017-11-07 16:32:09 +0000378 * @return IpAddressInfo IpAddressInfo given by dhcp server, or null if not exists
Kalhee Kim45fede42017-09-05 19:05:06 +0000379 */
Kalhee Kim495c9b22017-11-07 16:32:09 +0000380 private IpAddressInfo extractIpAddress(DHCP6 dhcp6) {
381 IpAddressInfo ipInfo = new IpAddressInfo();
Kalhee Kim45fede42017-09-05 19:05:06 +0000382
383 log.debug("extractIpAddress enters dhcp6 {}.", dhcp6);
384 // Extract IPv6 address from IA NA ot IA TA option
385 Optional<Dhcp6IaNaOption> iaNaOption = dhcp6.getOptions()
386 .stream()
387 .filter(opt -> opt instanceof Dhcp6IaNaOption)
388 .map(opt -> (Dhcp6IaNaOption) opt)
389 .findFirst();
390 Optional<Dhcp6IaTaOption> iaTaOption = dhcp6.getOptions()
391 .stream()
392 .filter(opt -> opt instanceof Dhcp6IaTaOption)
393 .map(opt -> (Dhcp6IaTaOption) opt)
394 .findFirst();
395 Optional<Dhcp6IaAddressOption> iaAddressOption;
396 if (iaNaOption.isPresent()) {
397 log.debug("Found IPv6 address from iaNaOption {}", iaNaOption);
398
399 iaAddressOption = iaNaOption.get().getOptions().stream()
400 .filter(opt -> opt instanceof Dhcp6IaAddressOption)
401 .map(opt -> (Dhcp6IaAddressOption) opt)
402 .findFirst();
403 } else if (iaTaOption.isPresent()) {
404 log.debug("Found IPv6 address from iaTaOption {}", iaTaOption);
405
406 iaAddressOption = iaTaOption.get().getOptions().stream()
407 .filter(opt -> opt instanceof Dhcp6IaAddressOption)
408 .map(opt -> (Dhcp6IaAddressOption) opt)
409 .findFirst();
410 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000411 log.info("No IPv6 address found from iaTaOption {}", iaTaOption);
Kalhee Kim45fede42017-09-05 19:05:06 +0000412 iaAddressOption = Optional.empty();
413 }
414 if (iaAddressOption.isPresent()) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000415 ipInfo.ip6Address = iaAddressOption.get().getIp6Address();
416 ipInfo.prefTime = iaAddressOption.get().getPreferredLifetime();
Kalhee Kim45fede42017-09-05 19:05:06 +0000417 log.debug("Found IPv6 address from iaAddressOption {}", iaAddressOption);
Kalhee Kim45fede42017-09-05 19:05:06 +0000418 } else {
419 log.debug("Can't find IPv6 address from DHCPv6 {}", dhcp6);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000420 return null;
Kalhee Kim45fede42017-09-05 19:05:06 +0000421 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000422 return ipInfo;
Kalhee Kim45fede42017-09-05 19:05:06 +0000423 }
424 /**
425 * extract from dhcp6 packet Prefix prefix provided by dhcp server.
426 *
427 * @param dhcp6 the dhcp6 payload
428 * @return IpPrefix Prefix Delegation prefix, or null if not exists.
429 */
Kalhee Kim495c9b22017-11-07 16:32:09 +0000430 private PdPrefixInfo extractPrefix(DHCP6 dhcp6) {
431 log.debug("extractPrefix enters {}", dhcp6);
Kalhee Kim45fede42017-09-05 19:05:06 +0000432
433 // extract prefix
Kalhee Kim495c9b22017-11-07 16:32:09 +0000434 PdPrefixInfo pdPrefixInfo = new PdPrefixInfo();
Kalhee Kim45fede42017-09-05 19:05:06 +0000435
436 Ip6Address prefixAddress = null;
437
438 // Extract IPv6 prefix from IA PD option
439 Optional<Dhcp6IaPdOption> iaPdOption = dhcp6.getOptions()
440 .stream()
441 .filter(opt -> opt instanceof Dhcp6IaPdOption)
442 .map(opt -> (Dhcp6IaPdOption) opt)
443 .findFirst();
444
445 Optional<Dhcp6IaPrefixOption> iaPrefixOption;
446 if (iaPdOption.isPresent()) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000447 log.debug("IA_PD option found {}", iaPdOption);
Kalhee Kim45fede42017-09-05 19:05:06 +0000448
449 iaPrefixOption = iaPdOption.get().getOptions().stream()
450 .filter(opt -> opt instanceof Dhcp6IaPrefixOption)
451 .map(opt -> (Dhcp6IaPrefixOption) opt)
452 .findFirst();
453 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000454 log.debug("IA_PD option NOT found");
Kalhee Kim45fede42017-09-05 19:05:06 +0000455
456 iaPrefixOption = Optional.empty();
457 }
458 if (iaPrefixOption.isPresent()) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000459 log.debug("IAPrefix Option within IA_PD option found {}", iaPrefixOption);
Kalhee Kim45fede42017-09-05 19:05:06 +0000460
461 prefixAddress = iaPrefixOption.get().getIp6Prefix();
462 int prefixLen = (int) iaPrefixOption.get().getPrefixLength();
Kalhee Kim495c9b22017-11-07 16:32:09 +0000463 log.debug("Prefix length is {} bits", prefixLen);
464 pdPrefixInfo.pdPrefix = IpPrefix.valueOf(prefixAddress, prefixLen);
465 pdPrefixInfo.prefTime = iaPrefixOption.get().getPreferredLifetime();
Kalhee Kim45fede42017-09-05 19:05:06 +0000466 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000467 log.debug("Can't find IPv6 prefix from DHCPv6 {}", dhcp6);
468 return null;
Kalhee Kim45fede42017-09-05 19:05:06 +0000469 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000470 return pdPrefixInfo;
Kalhee Kim45fede42017-09-05 19:05:06 +0000471 }
472
473 /**
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000474 * extract from dhcp6 packet ClientIdOption.
475 *
476 * @param directConnFlag directly connected host
477 * @param dhcp6Payload the dhcp6 payload
478 * @return Dhcp6ClientIdOption clientIdOption, or null if not exists.
479 */
480 private Dhcp6ClientIdOption extractClinedId(Boolean directConnFlag, DHCP6 dhcp6Payload) {
481 Dhcp6ClientIdOption clientIdOption;
482
483 if (directConnFlag) {
484 clientIdOption = dhcp6Payload.getOptions()
485 .stream()
486 .filter(opt -> opt instanceof Dhcp6ClientIdOption)
487 .map(opt -> (Dhcp6ClientIdOption) opt)
488 .findFirst()
489 .orElse(null);
490 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000491 DHCP6 leafDhcp = dhcp6HandlerUtil.getDhcp6Leaf(dhcp6Payload);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000492 clientIdOption = leafDhcp.getOptions()
493 .stream()
494 .filter(opt -> opt instanceof Dhcp6ClientIdOption)
495 .map(opt -> (Dhcp6ClientIdOption) opt)
496 .findFirst()
497 .orElse(null);
498 }
499
500 return clientIdOption;
501 }
502 /**
Kalhee Kim495c9b22017-11-07 16:32:09 +0000503 * remove host or route and update dhcp relay record attributes.
Kalhee Kim45fede42017-09-05 19:05:06 +0000504 *
505 * @param directConnFlag flag to show that packet is from directly connected client
Kalhee Kim495c9b22017-11-07 16:32:09 +0000506 * @param location client side connect point
Kalhee Kim45fede42017-09-05 19:05:06 +0000507 * @param dhcp6Packet the dhcp6 payload
508 * @param clientPacket client's ethernet packet
509 * @param clientIpv6 client's Ipv6 packet
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +0000510 * @param clientInterface client interfaces
Kalhee Kim45fede42017-09-05 19:05:06 +0000511 */
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000512 private void removeHostOrRoute(boolean directConnFlag, ConnectPoint location,
513 DHCP6 dhcp6Packet,
Kalhee Kim45fede42017-09-05 19:05:06 +0000514 Ethernet clientPacket, IPv6 clientIpv6,
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +0000515 Interface clientInterface) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000516 log.debug("removeHostOrRoute enters {}", dhcp6Packet);
Kalhee Kim121ba922017-11-01 17:56:44 +0000517 VlanId vlanId = clientInterface.vlan();
Kalhee Kim495c9b22017-11-07 16:32:09 +0000518 MacAddress srcMac = clientPacket.getSourceMAC(); // could be gw or host
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000519 MacAddress leafClientMac;
520 byte leafMsgType;
Kalhee Kim495c9b22017-11-07 16:32:09 +0000521 log.debug("client mac {} client vlan {}", HexString.toHexString(srcMac.toBytes(), ":"), vlanId);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000522
523 Dhcp6ClientIdOption clientIdOption = extractClinedId(directConnFlag, dhcp6Packet);
524 if (clientIdOption != null) {
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000525 if ((clientIdOption.getDuid().getDuidType() == Dhcp6Duid.DuidType.DUID_LLT) ||
526 (clientIdOption.getDuid().getDuidType() == Dhcp6Duid.DuidType.DUID_LL)) {
527 leafClientMac = MacAddress.valueOf(clientIdOption.getDuid().getLinkLayerAddress());
528 } else {
529 log.warn("Link-Layer Address not supported in CLIENTID option. No DhcpRelay Record created.");
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000530 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_LINKLOCAL_FAIL);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000531 return;
532 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000533 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000534 log.warn("CLIENTID option NOT found. Don't create DhcpRelay Record.");
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000535 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_CLIENTID_FAIL);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000536 return;
537 }
538
Kalhee Kim495c9b22017-11-07 16:32:09 +0000539 HostId leafHostId = HostId.hostId(leafClientMac, vlanId);
540 DhcpRecord record = dhcpRelayStore.getDhcpRecord(leafHostId).orElse(null);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000541 if (record == null) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000542 record = new DhcpRecord(leafHostId);
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000543 } else {
544 record = record.clone();
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000545 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000546
Kalhee Kim495c9b22017-11-07 16:32:09 +0000547 Boolean isMsgRelease = dhcp6HandlerUtil.isDhcp6Release(dhcp6Packet);
548 IpAddressInfo ipInfo;
549 PdPrefixInfo pdInfo = null;
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000550 if (directConnFlag) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000551 // Add to host store if it is connected to network directly
552 ipInfo = extractIpAddress(dhcp6Packet);
553 if (ipInfo != null) {
554 if (isMsgRelease) {
555 HostId hostId = HostId.hostId(srcMac, vlanId);
556 log.debug("remove Host {} ip for directly connected.", hostId.toString());
557 providerService.removeIpFromHost(hostId, ipInfo.ip6Address);
Kalhee Kim45fede42017-09-05 19:05:06 +0000558 }
559 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000560 log.debug("ipAddress not found. Do not remove Host {} for directly connected.",
561 HostId.hostId(srcMac, vlanId).toString());
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000562 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000563 leafMsgType = dhcp6Packet.getMsgType();
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000564 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000565 // Remove from route store if it is not connected to network directly
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000566 // pick out the first link-local ip address
Kalhee Kim495c9b22017-11-07 16:32:09 +0000567 IpAddress nextHopIp = getFirstIpByHost(directConnFlag, srcMac, vlanId);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000568 if (nextHopIp == null) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000569 log.warn("Can't find link-local IP address of gateway mac {} vlanId {}", srcMac, vlanId);
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000570 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_LINKLOCAL_GW);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000571 return;
572 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000573
Kalhee Kim495c9b22017-11-07 16:32:09 +0000574 DHCP6 leafDhcp = dhcp6HandlerUtil.getDhcp6Leaf(dhcp6Packet);
575 ipInfo = extractIpAddress(leafDhcp);
576 if (ipInfo == null) {
577 log.debug("ip is null");
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000578 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000579 if (isMsgRelease) {
580 Route routeForIP = new Route(Route.Source.STATIC, ipInfo.ip6Address.toIpPrefix(), nextHopIp);
581 log.debug("removing route of 128 address for indirectly connected.");
582 log.debug("128 ip {}, nexthop {}",
583 HexString.toHexString(ipInfo.ip6Address.toOctets(), ":"),
584 HexString.toHexString(nextHopIp.toOctets(), ":"));
585 routeStore.removeRoute(routeForIP);
Kalhee Kim45fede42017-09-05 19:05:06 +0000586 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000587 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000588
Kalhee Kim495c9b22017-11-07 16:32:09 +0000589 pdInfo = extractPrefix(leafDhcp);
590 if (pdInfo == null) {
591 log.debug("ipPrefix is null ");
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000592 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000593 if (isMsgRelease) {
594 Route routeForPrefix = new Route(Route.Source.STATIC, pdInfo.pdPrefix, nextHopIp);
595 log.debug("removing route of PD for indirectly connected.");
596 log.debug("pd ip {}, nexthop {}",
597 HexString.toHexString(pdInfo.pdPrefix.address().toOctets(), ":"),
598 HexString.toHexString(nextHopIp.toOctets(), ":"));
599
600 routeStore.removeRoute(routeForPrefix);
Kalhee Kimba366062017-11-07 16:32:09 +0000601 if (this.dhcpFpmEnabled) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000602 dhcpFpmPrefixStore.removeFpmRecord(pdInfo.pdPrefix);
Kalhee Kimba366062017-11-07 16:32:09 +0000603 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000604 }
605 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000606 leafMsgType = leafDhcp.getMsgType();
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000607 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000608
Kalhee Kim495c9b22017-11-07 16:32:09 +0000609 if (isMsgRelease) {
610 log.debug("DHCP6 RELEASE msg.");
611 if (record != null) {
612 if (ipInfo != null) {
613 log.debug("DhcpRelay Record ip6Address is set to null.");
614 record.ip6Address(null);
615 }
616 if (pdInfo != null) {
617 log.debug("DhcpRelay Record pdPrefix is set to null.");
618 }
619
620 if (!record.ip6Address().isPresent() && !record.pdPrefix().isPresent()) {
621 log.warn("IP6 address and IP6 PD both are null. Remove record.");
622 // do not remove a record. Let timer task handler it.
623 //dhcpRelayStore.removeDhcpRecord(HostId.hostId(leafClientMac, vlanId));
624 }
625 }
626 }
627
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000628 record.getV6Counters().incrementCounter(dhcp6HandlerUtil.getMsgTypeStr(leafMsgType));
Kalhee Kim495c9b22017-11-07 16:32:09 +0000629 record.addLocation(new HostLocation(location, System.currentTimeMillis()));
630 record.ip6Status(DHCP6.MsgType.getType(leafMsgType));
631 record.setDirectlyConnected(directConnFlag);
632 if (!directConnFlag) {
633 // Update gateway mac address if the host is not directly connected
634 record.nextHop(srcMac);
635 }
636 record.updateLastSeen();
637 dhcpRelayStore.updateDhcpRecord(leafHostId, record);
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000638 // TODO Use AtomicInteger for the counters
639 try {
640 recordSemaphore.acquire();
641 try {
642 dhcpRelayCountersStore.incrementCounter(gCount, dhcp6HandlerUtil.getMsgTypeStr(leafMsgType));
643 } finally {
644 // calling release() after a successful acquire()
645 recordSemaphore.release();
646 }
647 } catch (InterruptedException e) {
648 e.printStackTrace();
649 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000650 }
651
652 /**
653 * add host or route and update dhcp relay record.
654 *
655 * @param directConnFlag flag to show that packet is from directly connected client
656 * @param location client side connect point
657 * @param dhcp6Relay the dhcp6 payload
658 * @param embeddedDhcp6 the dhcp6 payload within relay
659 * @param srcMac client gw/host macAddress
660 * @param clientInterface client interface
661 */
662 private void addHostOrRoute(boolean directConnFlag, ConnectPoint location, DHCP6 dhcp6Relay,
663 DHCP6 embeddedDhcp6, MacAddress srcMac, Interface clientInterface) {
664 log.debug("addHostOrRoute entered.");
665 VlanId vlanId = clientInterface.vlan();
666 Boolean isMsgReply = dhcp6HandlerUtil.isDhcp6Reply(dhcp6Relay);
667 MacAddress leafClientMac;
668 Byte leafMsgType;
669
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000670 Dhcp6ClientIdOption clientIdOption = extractClinedId(directConnFlag, embeddedDhcp6);
671 if (clientIdOption != null) {
672 log.debug("CLIENTID option found {}", clientIdOption);
673 if ((clientIdOption.getDuid().getDuidType() == Dhcp6Duid.DuidType.DUID_LLT) ||
674 (clientIdOption.getDuid().getDuidType() == Dhcp6Duid.DuidType.DUID_LL)) {
675 leafClientMac = MacAddress.valueOf(clientIdOption.getDuid().getLinkLayerAddress());
676 } else {
677 log.warn("Link-Layer Address not supported in CLIENTID option. No DhcpRelay Record created.");
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000678 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_LINKLOCAL_FAIL);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000679 return;
680 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000681 } else {
682 log.warn("CLIENTID option NOT found. No DhcpRelay Record created.");
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000683 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_CLIENTID_FAIL);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000684 return;
685 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000686 HostId leafHostId = HostId.hostId(leafClientMac, vlanId);
687 DhcpRecord record = dhcpRelayStore.getDhcpRecord(leafHostId).orElse(null);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000688 if (record == null) {
689 record = new DhcpRecord(HostId.hostId(leafClientMac, vlanId));
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000690 } else {
691 record = record.clone();
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000692 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000693
694 IpAddressInfo ipInfo;
695 PdPrefixInfo pdInfo = null;
696 if (directConnFlag) {
697 // Add to host store if it connect to network directly
698 ipInfo = extractIpAddress(embeddedDhcp6);
699 if (ipInfo != null) {
700 if (isMsgReply) {
701 Set<IpAddress> ips = Sets.newHashSet(ipInfo.ip6Address);
702 HostId hostId = HostId.hostId(srcMac, vlanId);
703 Host host = hostService.getHost(hostId);
704 HostLocation hostLocation = new HostLocation(clientInterface.connectPoint(),
705 System.currentTimeMillis());
706 Set<HostLocation> hostLocations = Sets.newHashSet(hostLocation);
707 if (host != null) {
708 // Dual homing support:
709 // if host exists, use old locations and new location
710 hostLocations.addAll(host.locations());
711 }
712 HostDescription desc = new DefaultHostDescription(srcMac, vlanId, hostLocations, ips,
713 false);
714 log.debug("adding Host for directly connected.");
715 log.debug("client mac {} client vlan {} hostlocation {}",
716 HexString.toHexString(srcMac.toBytes(), ":"), vlanId, hostLocation.toString());
717 // Replace the ip when dhcp server give the host new ip address
718 providerService.hostDetected(hostId, desc, false);
719 }
720 } else {
721 log.warn("ipAddress not found. Do not add Host {} for directly connected.",
722 HostId.hostId(srcMac, vlanId).toString());
723 }
724 leafMsgType = embeddedDhcp6.getMsgType();
725 } else {
726 // Add to route store if it does not connect to network directly
727 // pick out the first link-local ip address
728 IpAddress nextHopIp = getFirstIpByHost(directConnFlag, srcMac, vlanId);
729 if (nextHopIp == null) {
730 log.warn("Can't find link-local IP address of gateway mac {} vlanId {}", srcMac, vlanId);
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000731 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_LINKLOCAL_GW);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000732 return;
733 }
734
735 DHCP6 leafDhcp = dhcp6HandlerUtil.getDhcp6Leaf(embeddedDhcp6);
736 ipInfo = extractIpAddress(leafDhcp);
737 if (ipInfo == null) {
738 log.debug("ip is null");
739 } else {
740 if (isMsgReply) {
741 Route routeForIP = new Route(Route.Source.STATIC, ipInfo.ip6Address.toIpPrefix(), nextHopIp);
742 log.debug("adding Route of 128 address for indirectly connected.");
743 routeStore.updateRoute(routeForIP);
744 }
745 }
746
747 pdInfo = extractPrefix(leafDhcp);
748 if (pdInfo == null) {
749 log.debug("ipPrefix is null ");
750 } else {
751 if (isMsgReply) {
752 Route routeForPrefix = new Route(Route.Source.STATIC, pdInfo.pdPrefix, nextHopIp);
753 log.debug("adding Route of PD for indirectly connected.");
754 routeStore.updateRoute(routeForPrefix);
755 if (this.dhcpFpmEnabled) {
756 FpmRecord fpmRecord = new FpmRecord(pdInfo.pdPrefix, nextHopIp, FpmRecord.Type.DHCP_RELAY);
757 dhcpFpmPrefixStore.addFpmRecord(pdInfo.pdPrefix, fpmRecord);
758 }
759 }
760 }
761 leafMsgType = leafDhcp.getMsgType();
762 }
763 if (leafMsgType == DHCP6.MsgType.RELEASE.value() ||
764 (leafMsgType == DHCP6.MsgType.REPLY.value()) && ipInfo == null) {
765 log.warn("DHCP6 RELEASE/REPLY(null ip) from Server. MsgType {}", leafMsgType);
766 //return;
767 }
768
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000769 record.addLocation(new HostLocation(location, System.currentTimeMillis()));
770 if (leafMsgType == DHCP6.MsgType.REPLY.value()) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000771 if (ipInfo != null) {
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000772 log.debug("IP6 address is being stored into dhcp-relay store.");
Kalhee Kim495c9b22017-11-07 16:32:09 +0000773 log.debug("IP6 address {}", HexString.toHexString(ipInfo.ip6Address.toOctets(), ":"));
774 record.ip6Address(ipInfo.ip6Address);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000775 } else {
776 log.debug("IP6 address is not returned from server. Maybe only PD is returned.");
777 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000778 if (pdInfo != null) {
779 log.debug("IP6 PD address {}",
780 HexString.toHexString(pdInfo.pdPrefix.address().toOctets(), ":"));
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000781 } else {
782 log.debug("IP6 PD address is not returned from server. Maybe only IPAddress is returned.");
783 }
784 }
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000785 record.getV6Counters().incrementCounter(dhcp6HandlerUtil.getMsgTypeStr(leafMsgType));
Kalhee Kim495c9b22017-11-07 16:32:09 +0000786 record.ip6Status(DHCP6.MsgType.getType(leafMsgType));
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000787 record.setDirectlyConnected(directConnFlag);
788 record.updateLastSeen();
Kalhee Kim495c9b22017-11-07 16:32:09 +0000789 dhcpRelayStore.updateDhcpRecord(leafHostId, record);
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000790 // TODO Use AtomicInteger for the counters
791 try {
792 recordSemaphore.acquire();
793 try {
794 dhcpRelayCountersStore.incrementCounter(gCount, dhcp6HandlerUtil.getMsgTypeStr(leafMsgType));
795 } finally {
796 // calling release() after a successful acquire()
797 recordSemaphore.release();
798 }
799 } catch (InterruptedException e) {
800 e.printStackTrace();
801 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000802 }
803
804 /**
Kalhee Kim495c9b22017-11-07 16:32:09 +0000805 * build the DHCP6 solicit/request packet with gatewayip.
Kalhee Kim45fede42017-09-05 19:05:06 +0000806 *
807 * @param context packet context
808 * @param clientPacket client ethernet packet
809 * @param clientInterfaces set of client side interfaces
810 */
Kalhee Kim495c9b22017-11-07 16:32:09 +0000811 private List<InternalPacket> processDhcp6PacketFromClient(PacketContext context,
812 Ethernet clientPacket,
813 Set<Interface> clientInterfaces) {
Yi Tseng3bd57ac2017-11-29 14:39:18 -0800814 ConnectPoint receivedFrom = context.inPacket().receivedFrom();
815 DeviceId receivedFromDevice = receivedFrom.deviceId();
Kalhee Kim495c9b22017-11-07 16:32:09 +0000816 Ip6Address relayAgentIp = dhcp6HandlerUtil.getRelayAgentIPv6Address(clientInterfaces);
Yi Tseng3bd57ac2017-11-29 14:39:18 -0800817 MacAddress relayAgentMac = clientInterfaces.iterator().next().mac();
818 if (relayAgentIp == null || relayAgentMac == null) {
819 log.warn("Missing DHCP relay agent interface Ipv6 addr config for "
Kalhee Kim495c9b22017-11-07 16:32:09 +0000820 + "packet from client on port: {}. Aborting packet processing",
821 clientInterfaces.iterator().next().connectPoint());
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000822 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_CLIENT_INTF_MAC);
Kalhee Kim121ba922017-11-01 17:56:44 +0000823 return null;
824 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000825
Kalhee Kim495c9b22017-11-07 16:32:09 +0000826 IPv6 clientIpv6 = (IPv6) clientPacket.getPayload();
827 UDP clientUdp = (UDP) clientIpv6.getPayload();
828 DHCP6 clientDhcp6 = (DHCP6) clientUdp.getPayload();
829
830 boolean directConnFlag = dhcp6HandlerUtil.directlyConnected(clientDhcp6);
Charles Chana990ce92017-10-30 10:22:50 -0700831
Kalhee Kim121ba922017-11-01 17:56:44 +0000832 ConnectPoint clientConnectionPoint = context.inPacket().receivedFrom();
833 VlanId vlanIdInUse = VlanId.vlanId(clientPacket.getVlanID());
834 Interface clientInterface = interfaceService.getInterfacesByPort(clientConnectionPoint)
Kalhee Kim495c9b22017-11-07 16:32:09 +0000835 .stream().filter(iface -> dhcp6HandlerUtil.interfaceContainsVlan(iface, vlanIdInUse))
836 .findFirst()
837 .orElse(null);
Kalhee Kim45fede42017-09-05 19:05:06 +0000838
Kalhee Kim495c9b22017-11-07 16:32:09 +0000839 List<InternalPacket> internalPackets = new ArrayList<>();
840 List<DhcpServerInfo> serverInfoList = findValidServerInfo(directConnFlag);
841 List<DhcpServerInfo> copyServerInfoList = new ArrayList<DhcpServerInfo>(serverInfoList);
Kalhee Kim45b24182017-10-18 18:30:23 +0000842
Kalhee Kim495c9b22017-11-07 16:32:09 +0000843 for (DhcpServerInfo serverInfo : copyServerInfoList) {
844 if (!dhcp6HandlerUtil.checkDhcpServerConnPt(directConnFlag, serverInfo)) {
845 log.warn("Can't get server connect point, ignore");
846 continue;
847 }
848 DhcpServerInfo newServerInfo = getHostInfoForServerInfo(serverInfo, serverInfoList);
849 if (newServerInfo == null) {
850 log.warn("Can't get server interface with host info resolved, ignore");
851 continue;
852 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000853
Kalhee Kim495c9b22017-11-07 16:32:09 +0000854 Interface serverInterface = getServerInterface(newServerInfo);
855 if (serverInterface == null) {
856 log.warn("Can't get server interface, ignore");
857 continue;
858 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000859
Kalhee Kim495c9b22017-11-07 16:32:09 +0000860 Ethernet etherReply = dhcp6HandlerUtil.buildDhcp6PacketFromClient(context, clientPacket,
861 clientInterfaces, newServerInfo, serverInterface);
862 removeHostOrRoute(directConnFlag, clientConnectionPoint, clientDhcp6, clientPacket,
863 clientIpv6, clientInterface);
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000864
Kalhee Kim495c9b22017-11-07 16:32:09 +0000865 InternalPacket internalPacket = new Dhcp6HandlerUtil().new InternalPacket(etherReply,
866 serverInfo.getDhcpServerConnectPoint().get());
867 internalPackets.add(internalPacket);
868 }
869 log.debug("num of client packets to send is{}", internalPackets.size());
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000870
Kalhee Kim495c9b22017-11-07 16:32:09 +0000871 return internalPackets;
872 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000873
874 /**
Kalhee Kim45fede42017-09-05 19:05:06 +0000875 * process the DHCP6 relay-reply packet from dhcp server.
876 *
877 * @param context packet context
878 * @param receivedPacket server ethernet packet
879 * @param recevingInterfaces set of server side interfaces
Kalhee Kim495c9b22017-11-07 16:32:09 +0000880 * @return internalPacket toward client
Kalhee Kim45fede42017-09-05 19:05:06 +0000881 */
882 private InternalPacket processDhcp6PacketFromServer(PacketContext context,
883 Ethernet receivedPacket, Set<Interface> recevingInterfaces) {
Kalhee Kim45fede42017-09-05 19:05:06 +0000884 // get dhcp6 header.
Ray Milkeyf0c47612017-09-28 11:29:38 -0700885 Ethernet etherReply = receivedPacket.duplicate();
Kalhee Kim45fede42017-09-05 19:05:06 +0000886 IPv6 ipv6Packet = (IPv6) etherReply.getPayload();
887 UDP udpPacket = (UDP) ipv6Packet.getPayload();
888 DHCP6 dhcp6Relay = (DHCP6) udpPacket.getPayload();
Kalhee Kim495c9b22017-11-07 16:32:09 +0000889 Boolean directConnFlag = dhcp6HandlerUtil.directlyConnected(dhcp6Relay);
Kalhee Kim45fede42017-09-05 19:05:06 +0000890
Kalhee Kim495c9b22017-11-07 16:32:09 +0000891 DHCP6 embeddedDhcp6 = dhcp6Relay.getOptions().stream()
892 .filter(opt -> opt instanceof Dhcp6RelayOption)
893 .map(BasePacket::getPayload)
894 .map(pld -> (DHCP6) pld)
895 .findFirst()
896 .orElse(null);
Kalhee Kimd21029f2017-09-26 20:21:53 +0000897 ConnectPoint inPort = context.inPacket().receivedFrom();
Kalhee Kim495c9b22017-11-07 16:32:09 +0000898 DhcpServerInfo foundServerInfo = findServerInfoFromServer(directConnFlag, inPort);
Kalhee Kimd21029f2017-09-26 20:21:53 +0000899
Kalhee Kim495c9b22017-11-07 16:32:09 +0000900 if (foundServerInfo == null) {
901 log.warn("Cannot find server info");
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000902 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_SERVER_INFO);
Kalhee Kimd21029f2017-09-26 20:21:53 +0000903 return null;
Kalhee Kim495c9b22017-11-07 16:32:09 +0000904 } else {
905 if (dhcp6HandlerUtil.isServerIpEmpty(foundServerInfo)) {
906 log.warn("Cannot find server info's ipaddress");
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000907 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_SERVER_IP6ADDR);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000908 return null;
909 }
Kalhee Kimd21029f2017-09-26 20:21:53 +0000910 }
911
Kalhee Kim45fede42017-09-05 19:05:06 +0000912 Dhcp6InterfaceIdOption interfaceIdOption = dhcp6Relay.getOptions().stream()
913 .filter(opt -> opt instanceof Dhcp6InterfaceIdOption)
914 .map(opt -> (Dhcp6InterfaceIdOption) opt)
915 .findFirst()
916 .orElse(null);
Kalhee Kim45fede42017-09-05 19:05:06 +0000917 if (interfaceIdOption == null) {
918 log.warn("Interface Id option is not present, abort packet...");
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000919 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.OPTION_MISSING_FAIL);
Kalhee Kim45fede42017-09-05 19:05:06 +0000920 return null;
921 }
922
923 MacAddress peerMac = interfaceIdOption.getMacAddress();
924 String clientConnectionPointStr = new String(interfaceIdOption.getInPort());
Kalhee Kim45fede42017-09-05 19:05:06 +0000925 ConnectPoint clientConnectionPoint = ConnectPoint.deviceConnectPoint(clientConnectionPointStr);
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +0000926 VlanId vlanIdInUse = VlanId.vlanId(interfaceIdOption.getVlanId());
927 Interface clientInterface = interfaceService.getInterfacesByPort(clientConnectionPoint)
Kalhee Kim495c9b22017-11-07 16:32:09 +0000928 .stream().filter(iface -> dhcp6HandlerUtil.interfaceContainsVlan(iface, vlanIdInUse))
929 .findFirst().orElse(null);
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +0000930 if (clientInterface == null) {
931 log.warn("Cannot get client interface for from packet, abort... vlan {}", vlanIdInUse.toString());
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000932 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_MATCHING_INTF);
Kalhee Kim45fede42017-09-05 19:05:06 +0000933 return null;
934 }
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +0000935 MacAddress relayAgentMac = clientInterface.mac();
Kalhee Kim45fede42017-09-05 19:05:06 +0000936 if (relayAgentMac == null) {
Yi Tseng68ef26b2017-12-18 17:10:00 -0800937 log.warn("Can not get client interface mac, abort packet..");
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000938 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_CLIENT_INTF_MAC);
Kalhee Kim45fede42017-09-05 19:05:06 +0000939 return null;
940 }
941 etherReply.setSourceMACAddress(relayAgentMac);
942
943 // find destMac
Yi Tseng68ef26b2017-12-18 17:10:00 -0800944 MacAddress clientMac;
Yi Tsengaa417a62017-09-08 17:22:51 -0700945 Ip6Address peerAddress = Ip6Address.valueOf(dhcp6Relay.getPeerAddress());
946 Set<Host> clients = hostService.getHostsByIp(peerAddress);
Kalhee Kim45fede42017-09-05 19:05:06 +0000947 if (clients.isEmpty()) {
Yi Tseng68ef26b2017-12-18 17:10:00 -0800948 log.trace("There's no host found for this address {}",
Kalhee Kim45fede42017-09-05 19:05:06 +0000949 HexString.toHexString(dhcp6Relay.getPeerAddress(), ":"));
Yi Tseng68ef26b2017-12-18 17:10:00 -0800950 log.trace("Let's look up interfaceId {}", HexString.toHexString(peerMac.toBytes(), ":"));
Kalhee Kim45fede42017-09-05 19:05:06 +0000951 clientMac = peerMac;
952 } else {
953 clientMac = clients.iterator().next().mac();
954 if (clientMac == null) {
955 log.warn("No client mac address found, abort packet...");
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000956 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_CLIENT_INTF_MAC);
Kalhee Kim45fede42017-09-05 19:05:06 +0000957 return null;
958 }
Yi Tseng68ef26b2017-12-18 17:10:00 -0800959 log.trace("Client mac address found from getHostByIp");
Kalhee Kim45fede42017-09-05 19:05:06 +0000960 }
961 etherReply.setDestinationMACAddress(clientMac);
Kalhee Kim45fede42017-09-05 19:05:06 +0000962 // ip header
963 ipv6Packet.setSourceAddress(dhcp6Relay.getLinkAddress());
964 ipv6Packet.setDestinationAddress(dhcp6Relay.getPeerAddress());
965 // udp header
966 udpPacket.setSourcePort(UDP.DHCP_V6_SERVER_PORT);
967 if (directConnFlag) {
968 udpPacket.setDestinationPort(UDP.DHCP_V6_CLIENT_PORT);
969 } else {
970 udpPacket.setDestinationPort(UDP.DHCP_V6_SERVER_PORT);
971 }
Kalhee Kim45fede42017-09-05 19:05:06 +0000972 // add host or route
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000973 addHostOrRoute(directConnFlag, clientConnectionPoint, dhcp6Relay, embeddedDhcp6, clientMac, clientInterface);
Kalhee Kim45fede42017-09-05 19:05:06 +0000974
975 udpPacket.setPayload(embeddedDhcp6);
976 udpPacket.resetChecksum();
977 ipv6Packet.setPayload(udpPacket);
978 etherReply.setPayload(ipv6Packet);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000979 return new Dhcp6HandlerUtil().new InternalPacket(etherReply, clientConnectionPoint);
Kalhee Kim45fede42017-09-05 19:05:06 +0000980 }
981
Yi Tseng483ac6f2017-08-02 15:03:31 -0700982
983 @Override
Kalhee Kimba366062017-11-07 16:32:09 +0000984 public void setDhcpFpmEnabled(Boolean enabled) {
985 dhcpFpmEnabled = enabled;
986 }
987
988 @Override
Yi Tseng483ac6f2017-08-02 15:03:31 -0700989 public void setDefaultDhcpServerConfigs(Collection<DhcpServerConfig> configs) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000990 log.debug("setDefaultDhcpServerConfigs is called.");
Yi Tseng919b2df2017-09-07 16:22:51 -0700991 setDhcpServerConfigs(configs, defaultServerInfoList);
Yi Tseng919b2df2017-09-07 16:22:51 -0700992 }
993
994 @Override
995 public void setIndirectDhcpServerConfigs(Collection<DhcpServerConfig> configs) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000996 log.debug("setIndirectDhcpServerConfigs is called.");
Yi Tseng919b2df2017-09-07 16:22:51 -0700997 setDhcpServerConfigs(configs, indirectServerInfoList);
Yi Tseng919b2df2017-09-07 16:22:51 -0700998 }
999
1000 public void setDhcpServerConfigs(Collection<DhcpServerConfig> configs, List<DhcpServerInfo> serverInfoList) {
Kalhee Kim495c9b22017-11-07 16:32:09 +00001001 log.debug("config size {}.", configs.size());
1002
Kalhee Kim45fede42017-09-05 19:05:06 +00001003 if (configs.size() == 0) {
1004 // no config to update
1005 return;
1006 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001007 // TODO: currently we pick up first DHCP server config.
1008 // Will use other server configs in the future for HA.
Kalhee Kim495c9b22017-11-07 16:32:09 +00001009 Boolean isConfigValid = false;
1010 for (DhcpServerConfig serverConfig : configs) {
1011 if (serverConfig.getDhcpServerIp6().isPresent()) {
1012 isConfigValid = true;
1013 break;
1014 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001015 }
Kalhee Kim495c9b22017-11-07 16:32:09 +00001016 if (!isConfigValid) {
1017 log.warn("No IP V6 server address found.");
1018 return; // No IP V6 address found
1019 }
1020 for (DhcpServerInfo oldServerInfo : serverInfoList) {
Yi Tseng919b2df2017-09-07 16:22:51 -07001021 // stop monitoring gateway or server
1022 oldServerInfo.getDhcpGatewayIp6().ifPresent(gatewayIp -> {
1023 hostService.stopMonitoringIp(gatewayIp);
1024 });
1025 oldServerInfo.getDhcpServerIp6().ifPresent(serverIp -> {
1026 hostService.stopMonitoringIp(serverIp);
Yi Tseng525ff402017-10-23 19:39:39 -07001027 cancelDhcpPacket(serverIp);
Yi Tseng919b2df2017-09-07 16:22:51 -07001028 });
1029 }
Kalhee Kim495c9b22017-11-07 16:32:09 +00001030 serverInfoList.clear();
1031 for (DhcpServerConfig serverConfig : configs) {
1032 // Create new server info according to the config
1033 DhcpServerInfo newServerInfo = new DhcpServerInfo(serverConfig,
1034 DhcpServerInfo.Version.DHCP_V6);
1035 checkState(newServerInfo.getDhcpServerConnectPoint().isPresent(),
1036 "Connect point not exists");
1037 checkState(newServerInfo.getDhcpServerIp6().isPresent(),
1038 "IP of DHCP server not exists");
Yi Tseng919b2df2017-09-07 16:22:51 -07001039
Kalhee Kim495c9b22017-11-07 16:32:09 +00001040 log.debug("DHCP server connect point: {}", newServerInfo.getDhcpServerConnectPoint().orElse(null));
1041 log.debug("DHCP server IP: {}", newServerInfo.getDhcpServerIp6().orElse(null));
Yi Tseng919b2df2017-09-07 16:22:51 -07001042
Kalhee Kim495c9b22017-11-07 16:32:09 +00001043 Ip6Address serverIp = newServerInfo.getDhcpServerIp6().get();
1044 Ip6Address ipToProbe;
1045 if (newServerInfo.getDhcpGatewayIp6().isPresent()) {
1046 ipToProbe = newServerInfo.getDhcpGatewayIp6().get();
1047 } else {
1048 ipToProbe = newServerInfo.getDhcpServerIp6().orElse(null);
1049 }
1050 String hostToProbe = newServerInfo.getDhcpGatewayIp6()
1051 .map(ip -> "gateway").orElse("server");
Yi Tseng919b2df2017-09-07 16:22:51 -07001052
Kalhee Kim495c9b22017-11-07 16:32:09 +00001053 log.warn("Probing to resolve {} IP {}", hostToProbe, ipToProbe);
1054 hostService.startMonitoringIp(ipToProbe);
1055
1056 Set<Host> hosts = hostService.getHostsByIp(ipToProbe);
1057 if (!hosts.isEmpty()) {
1058 Host host = hosts.iterator().next();
1059 newServerInfo.setDhcpConnectVlan(host.vlan());
1060 newServerInfo.setDhcpConnectMac(host.mac());
1061 log.warn("Host found host {}", host);
1062
1063 } else {
1064 log.warn("No host found host ip {}", ipToProbe);
1065 }
1066 // Add new server info
1067 synchronized (this) {
1068 serverInfoList.add(newServerInfo);
1069 }
1070 if (!hosts.isEmpty()) {
1071 requestDhcpPacket(serverIp);
1072 }
Yi Tseng919b2df2017-09-07 16:22:51 -07001073 }
Kalhee Kim45fede42017-09-05 19:05:06 +00001074 }
1075
1076 class InternalHostListener implements HostListener {
1077 @Override
1078 public void event(HostEvent event) {
1079 switch (event.type()) {
1080 case HOST_ADDED:
1081 case HOST_UPDATED:
1082 hostUpdated(event.subject());
1083 break;
1084 case HOST_REMOVED:
1085 hostRemoved(event.subject());
1086 break;
Kalhee Kim45fede42017-09-05 19:05:06 +00001087 default:
1088 break;
1089 }
1090 }
1091 }
1092
1093 /**
Kalhee Kim45fede42017-09-05 19:05:06 +00001094 * Handle host updated.
1095 * If the host is DHCP server or gateway, update connect mac and vlan.
1096 *
1097 * @param host the host
1098 */
1099 private void hostUpdated(Host host) {
Yi Tseng525ff402017-10-23 19:39:39 -07001100 hostUpdated(host, defaultServerInfoList);
1101 hostUpdated(host, indirectServerInfoList);
Kalhee Kim45fede42017-09-05 19:05:06 +00001102 }
1103
Yi Tseng525ff402017-10-23 19:39:39 -07001104 private void hostUpdated(Host host, List<DhcpServerInfo> serverInfoList) {
1105 DhcpServerInfo serverInfo;
1106 Ip6Address targetIp;
1107 if (!serverInfoList.isEmpty()) {
1108 serverInfo = serverInfoList.get(0);
1109 Ip6Address serverIp = serverInfo.getDhcpServerIp6().orElse(null);
1110 targetIp = serverInfo.getDhcpGatewayIp6().orElse(null);
1111
1112 if (targetIp == null) {
1113 targetIp = serverIp;
1114 }
Yi Tseng525ff402017-10-23 19:39:39 -07001115 if (targetIp != null) {
1116 if (host.ipAddresses().contains(targetIp)) {
1117 serverInfo.setDhcpConnectMac(host.mac());
1118 serverInfo.setDhcpConnectVlan(host.vlan());
1119 requestDhcpPacket(serverIp);
1120 }
1121 }
1122 }
1123 }
1124
Kalhee Kim45fede42017-09-05 19:05:06 +00001125 /**
1126 * Handle host removed.
1127 * If the host is DHCP server or gateway, unset connect mac and vlan.
1128 *
1129 * @param host the host
1130 */
1131 private void hostRemoved(Host host) {
Yi Tseng525ff402017-10-23 19:39:39 -07001132 hostRemoved(host, defaultServerInfoList);
1133 hostRemoved(host, indirectServerInfoList);
Yi Tseng919b2df2017-09-07 16:22:51 -07001134 }
1135
Yi Tseng525ff402017-10-23 19:39:39 -07001136 private void hostRemoved(Host host, List<DhcpServerInfo> serverInfoList) {
1137 DhcpServerInfo serverInfo;
1138 Ip6Address targetIp;
1139
1140 if (!serverInfoList.isEmpty()) {
1141 serverInfo = serverInfoList.get(0);
1142 Ip6Address serverIp = serverInfo.getDhcpServerIp6().orElse(null);
1143 targetIp = serverInfo.getDhcpGatewayIp6().orElse(null);
1144
1145 if (targetIp == null) {
1146 targetIp = serverIp;
1147 }
Yi Tseng525ff402017-10-23 19:39:39 -07001148 if (targetIp != null) {
1149 if (host.ipAddresses().contains(targetIp)) {
1150 serverInfo.setDhcpConnectVlan(null);
1151 serverInfo.setDhcpConnectMac(null);
1152 cancelDhcpPacket(serverIp);
1153 }
1154 }
1155 }
1156 }
1157
Kalhee Kim45b24182017-10-18 18:30:23 +00001158 /**
1159 * Returns the first interface ip from interface.
1160 *
1161 * @param iface interface of one connect point
1162 * @return the first interface IP; null if not exists an IP address in
1163 * these interfaces
1164 */
1165 private Ip6Address getFirstIpFromInterface(Interface iface) {
1166 checkNotNull(iface, "Interface can't be null");
1167 return iface.ipAddressesList().stream()
1168 .map(InterfaceIpAddress::ipAddress)
1169 .filter(IpAddress::isIp6)
1170 .map(IpAddress::getIp6Address)
1171 .findFirst()
1172 .orElse(null);
1173 }
Kalhee Kim45b24182017-10-18 18:30:23 +00001174 /**
Kalhee Kim495c9b22017-11-07 16:32:09 +00001175 * Checks if serverInfo's host info (mac and vlan) is filled in; if not, fills in.
1176 *
1177 * @param serverInfo server information
1178 * @return newServerInfo if host info can be either found or filled in.
1179 */
1180 private DhcpServerInfo getHostInfoForServerInfo(DhcpServerInfo serverInfo, List<DhcpServerInfo> sererInfoList) {
1181 DhcpServerInfo newServerInfo = null;
1182 MacAddress dhcpServerConnectMac = serverInfo.getDhcpConnectMac().orElse(null);
1183 VlanId dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1184 ConnectPoint dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1185
1186 if (dhcpServerConnectMac != null && dhcpConnectVlan != null) {
1187 newServerInfo = serverInfo;
1188 log.info("DHCP server {} host info found. ConnectPt{} Mac {} vlan {}", serverInfo.getDhcpServerIp6(),
1189 dhcpServerConnectPoint, dhcpServerConnectMac, dhcpConnectVlan);
1190 } else {
1191 log.warn("DHCP server {} not resolve yet connectPt {} mac {} vlan {}", serverInfo.getDhcpServerIp6(),
1192 dhcpServerConnectPoint, dhcpServerConnectMac, dhcpConnectVlan);
1193
1194 Ip6Address ipToProbe;
1195 if (serverInfo.getDhcpGatewayIp6().isPresent()) {
1196 ipToProbe = serverInfo.getDhcpGatewayIp6().get();
1197 } else {
1198 ipToProbe = serverInfo.getDhcpServerIp6().orElse(null);
1199 }
1200 String hostToProbe = serverInfo.getDhcpGatewayIp6()
1201 .map(ip -> "gateway").orElse("server");
1202
1203 log.info("Dynamically probing to resolve {} IP {}", hostToProbe, ipToProbe);
1204 hostService.startMonitoringIp(ipToProbe);
1205
1206 Set<Host> hosts = hostService.getHostsByIp(ipToProbe);
1207 if (!hosts.isEmpty()) {
1208 int serverInfoIndex = sererInfoList.indexOf(serverInfo);
1209 Host host = hosts.iterator().next();
1210 serverInfo.setDhcpConnectVlan(host.vlan());
1211 serverInfo.setDhcpConnectMac(host.mac());
1212 // replace the serverInfo in the list
1213 sererInfoList.set(serverInfoIndex, serverInfo);
1214 newServerInfo = serverInfo;
1215 log.warn("Dynamically host found host {}", host);
1216 } else {
1217 log.warn("No host found host ip {} dynamically", ipToProbe);
1218 }
1219 }
1220 return newServerInfo;
1221 }
1222
1223 /**
Kalhee Kim45b24182017-10-18 18:30:23 +00001224 * Gets Interface facing to the server for default host.
1225 *
Kalhee Kim495c9b22017-11-07 16:32:09 +00001226 * @param serverInfo server information
Kalhee Kim45b24182017-10-18 18:30:23 +00001227 * @return the Interface facing to the server; null if not found
1228 */
Kalhee Kim495c9b22017-11-07 16:32:09 +00001229 private Interface getServerInterface(DhcpServerInfo serverInfo) {
1230 Interface serverInterface = null;
Yi Tseng25bfe372017-11-03 16:27:32 -07001231
Kalhee Kim495c9b22017-11-07 16:32:09 +00001232 ConnectPoint dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
1233 VlanId dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
1234
1235 if (dhcpServerConnectPoint != null && dhcpConnectVlan != null) {
1236 serverInterface = interfaceService.getInterfacesByPort(dhcpServerConnectPoint)
1237 .stream()
1238 .filter(iface -> dhcp6HandlerUtil.interfaceContainsVlan(iface, dhcpConnectVlan))
1239 .findFirst()
1240 .orElse(null);
Yi Tseng25bfe372017-11-03 16:27:32 -07001241 } else {
Kalhee Kim495c9b22017-11-07 16:32:09 +00001242 log.warn("DHCP server {} not resolve yet connectPoint {} vlan {}", serverInfo.getDhcpServerIp6(),
1243 dhcpServerConnectPoint, dhcpConnectVlan);
Kalhee Kim45b24182017-10-18 18:30:23 +00001244 }
Kalhee Kim495c9b22017-11-07 16:32:09 +00001245
1246 return serverInterface;
Kalhee Kim45b24182017-10-18 18:30:23 +00001247 }
1248
Kalhee Kim0c0cb0b2017-09-15 17:43:27 +00001249
Yi Tseng525ff402017-10-23 19:39:39 -07001250 private void requestDhcpPacket(Ip6Address serverIp) {
1251 requestServerDhcpPacket(serverIp);
1252 requestClientDhcpPacket(serverIp);
1253 }
1254
1255 private void cancelDhcpPacket(Ip6Address serverIp) {
1256 cancelServerDhcpPacket(serverIp);
1257 cancelClientDhcpPacket(serverIp);
1258 }
1259
1260 private void cancelServerDhcpPacket(Ip6Address serverIp) {
1261 TrafficSelector serverSelector =
1262 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1263 .matchIPv6Src(serverIp.toIpPrefix())
1264 .build();
1265 packetService.cancelPackets(serverSelector,
1266 PacketPriority.CONTROL,
1267 appId);
1268 }
1269
1270 private void requestServerDhcpPacket(Ip6Address serverIp) {
1271 TrafficSelector serverSelector =
1272 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1273 .matchIPv6Src(serverIp.toIpPrefix())
1274 .build();
1275 packetService.requestPackets(serverSelector,
1276 PacketPriority.CONTROL,
1277 appId);
1278 }
1279
1280 private void cancelClientDhcpPacket(Ip6Address serverIp) {
1281 // Packet comes from relay
1282 TrafficSelector indirectClientSelector =
1283 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1284 .matchIPv6Dst(serverIp.toIpPrefix())
1285 .build();
1286 packetService.cancelPackets(indirectClientSelector,
1287 PacketPriority.CONTROL,
1288 appId);
Yi Tseng41dde702017-11-02 15:21:10 -07001289 indirectClientSelector =
1290 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1291 .matchIPv6Dst(Ip6Address.ALL_DHCP_RELAY_AGENTS_AND_SERVERS.toIpPrefix())
1292 .build();
1293 packetService.cancelPackets(indirectClientSelector,
1294 PacketPriority.CONTROL,
1295 appId);
1296 indirectClientSelector =
1297 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1298 .matchIPv6Dst(Ip6Address.ALL_DHCP_SERVERS.toIpPrefix())
1299 .build();
1300 packetService.cancelPackets(indirectClientSelector,
1301 PacketPriority.CONTROL,
1302 appId);
Yi Tseng525ff402017-10-23 19:39:39 -07001303
1304 // Packet comes from client
1305 packetService.cancelPackets(CLIENT_SERVER_SELECTOR,
1306 PacketPriority.CONTROL,
1307 appId);
1308 }
1309
1310 private void requestClientDhcpPacket(Ip6Address serverIp) {
1311 // Packet comes from relay
1312 TrafficSelector indirectClientSelector =
1313 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1314 .matchIPv6Dst(serverIp.toIpPrefix())
1315 .build();
1316 packetService.requestPackets(indirectClientSelector,
1317 PacketPriority.CONTROL,
1318 appId);
Yi Tseng41dde702017-11-02 15:21:10 -07001319 indirectClientSelector =
1320 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1321 .matchIPv6Dst(Ip6Address.ALL_DHCP_RELAY_AGENTS_AND_SERVERS.toIpPrefix())
1322 .build();
1323 packetService.requestPackets(indirectClientSelector,
1324 PacketPriority.CONTROL,
1325 appId);
1326 indirectClientSelector =
1327 DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
1328 .matchIPv6Dst(Ip6Address.ALL_DHCP_SERVERS.toIpPrefix())
1329 .build();
1330 packetService.requestPackets(indirectClientSelector,
1331 PacketPriority.CONTROL,
1332 appId);
Yi Tseng525ff402017-10-23 19:39:39 -07001333
1334 // Packet comes from client
1335 packetService.requestPackets(CLIENT_SERVER_SELECTOR,
1336 PacketPriority.CONTROL,
1337 appId);
1338 }
1339
1340 /**
1341 * Process the ignore rules.
1342 *
1343 * @param deviceId the device id
1344 * @param vlanId the vlan to be ignored
1345 * @param op the operation, ADD to install; REMOVE to uninstall rules
1346 */
1347 private void processIgnoreVlanRule(DeviceId deviceId, VlanId vlanId, Objective.Operation op) {
Yi Tseng525ff402017-10-23 19:39:39 -07001348 AtomicInteger installedCount = new AtomicInteger(DHCP_SELECTORS.size());
1349 DHCP_SELECTORS.forEach(trafficSelector -> {
1350 TrafficSelector selector = DefaultTrafficSelector.builder(trafficSelector)
1351 .matchVlanId(vlanId)
1352 .build();
1353
1354 ForwardingObjective.Builder builder = DefaultForwardingObjective.builder()
1355 .withFlag(ForwardingObjective.Flag.VERSATILE)
1356 .withSelector(selector)
1357 .withPriority(IGNORE_CONTROL_PRIORITY)
Yi Tsengdbabeed2017-11-29 10:49:20 -08001358 .withTreatment(DefaultTrafficTreatment.emptyTreatment())
Yi Tseng525ff402017-10-23 19:39:39 -07001359 .fromApp(appId);
1360
1361
1362 ObjectiveContext objectiveContext = new ObjectiveContext() {
1363 @Override
1364 public void onSuccess(Objective objective) {
1365 log.info("Ignore rule {} (Vlan id {}, device {}, selector {})",
1366 op, vlanId, deviceId, selector);
1367 int countDown = installedCount.decrementAndGet();
1368 if (countDown != 0) {
1369 return;
1370 }
1371 switch (op) {
1372 case ADD:
1373 ignoredVlans.put(deviceId, vlanId);
1374 break;
1375 case REMOVE:
1376 ignoredVlans.remove(deviceId, vlanId);
1377 break;
1378 default:
1379 log.warn("Unsupported objective operation {}", op);
1380 break;
1381 }
1382 }
1383
1384 @Override
1385 public void onError(Objective objective, ObjectiveError error) {
1386 log.warn("Can't {} ignore rule (vlan id {}, selector {}, device {}) due to {}",
1387 op, vlanId, selector, deviceId, error);
1388 }
1389 };
1390
1391 ForwardingObjective fwd;
1392 switch (op) {
1393 case ADD:
1394 fwd = builder.add(objectiveContext);
1395 break;
1396 case REMOVE:
1397 fwd = builder.remove(objectiveContext);
1398 break;
1399 default:
1400 log.warn("Unsupported objective operation {}", op);
1401 return;
1402 }
1403
1404 Device device = deviceService.getDevice(deviceId);
1405 if (device == null || !device.is(Pipeliner.class)) {
1406 log.warn("Device {} is not available now, wait until device is available", deviceId);
1407 return;
1408 }
1409 flowObjectiveService.apply(deviceId, fwd);
1410 });
1411 }
Kalhee Kim121ba922017-11-01 17:56:44 +00001412 /**
1413 * Find first ipaddress for a given Host info i.e. mac and vlan.
1414 *
1415 * @param clientMac client mac
1416 * @param vlanId packet's vlan
1417 * @return next-hop link-local ipaddress for a given host
1418 */
Kalhee Kim495c9b22017-11-07 16:32:09 +00001419 private IpAddress getFirstIpByHost(Boolean directConnFlag, MacAddress clientMac, VlanId vlanId) {
Kalhee Kim121ba922017-11-01 17:56:44 +00001420 IpAddress nextHopIp;
1421 // pick out the first link-local ip address
1422 HostId gwHostId = HostId.hostId(clientMac, vlanId);
1423 Host gwHost = hostService.getHost(gwHostId);
1424 if (gwHost == null) {
1425 log.warn("Can't find gateway host for hostId {}", gwHostId);
1426 return null;
1427 }
Kalhee Kim495c9b22017-11-07 16:32:09 +00001428 if (directConnFlag) {
1429 nextHopIp = gwHost.ipAddresses()
1430 .stream()
1431 .filter(IpAddress::isIp6)
1432 .map(IpAddress::getIp6Address)
1433 .findFirst()
1434 .orElse(null);
1435 } else {
1436 nextHopIp = gwHost.ipAddresses()
1437 .stream()
1438 .filter(IpAddress::isIp6)
1439 .filter(ip6 -> ip6.isLinkLocal())
1440 .map(IpAddress::getIp6Address)
1441 .findFirst()
1442 .orElse(null);
1443 }
Kalhee Kim121ba922017-11-01 17:56:44 +00001444 return nextHopIp;
1445 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +00001446
Kalhee Kim495c9b22017-11-07 16:32:09 +00001447 private List<DhcpServerInfo> findValidServerInfo(boolean directConnFlag) {
1448 List<DhcpServerInfo> validServerInfo;
1449
1450 if (directConnFlag || indirectServerInfoList.isEmpty()) {
1451 validServerInfo = new ArrayList<DhcpServerInfo>(defaultServerInfoList);
1452 } else {
1453 validServerInfo = new ArrayList<DhcpServerInfo>(indirectServerInfoList);
1454 }
1455 return validServerInfo;
1456 }
1457
1458 private DhcpServerInfo findServerInfoFromServer(boolean directConnFlag, ConnectPoint inPort) {
1459 List<DhcpServerInfo> validServerInfoList = findValidServerInfo(directConnFlag);
1460 DhcpServerInfo foundServerInfo = null;
1461 for (DhcpServerInfo serverInfo : validServerInfoList) {
1462 if (inPort.equals(serverInfo.getDhcpServerConnectPoint().get())) {
1463 foundServerInfo = serverInfo;
1464 log.warn("ServerInfo found for Rcv port {} Server Connect Point {} for {}",
1465 inPort, serverInfo.getDhcpServerConnectPoint(), directConnFlag ? "direct" : "indirect");
1466 break;
1467 } else {
1468 log.warn("Rcv port {} not the same as Server Connect Point {} for {}",
1469 inPort, serverInfo.getDhcpServerConnectPoint(), directConnFlag ? "direct" : "indirect");
1470 }
1471 }
1472 return foundServerInfo;
1473 }
1474 /**
1475 * Set the dhcp6 lease expiry poll interval value.
1476 *
1477 * @param val poll interval value in seconds
1478 */
1479 public void setDhcp6PollInterval(int val) {
1480 dhcp6PollInterval = val;
1481 }
1482
Kalhee Kimd94ceea2017-11-29 19:03:02 +00001483 }