blob: 4b786f74946f2d541c83524dd0305860b642f0bf [file] [log] [blame]
Jonathan Hart9bdaaec2016-08-22 13:33:45 -07001/*
2 * Copyright 2016-present Open Networking Laboratory
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
17package org.onosproject.incubator.net.neighbour.impl;
18
Jonathan Hartc4f681c2016-09-09 07:14:25 -070019import com.google.common.collect.HashMultimap;
20import com.google.common.collect.ImmutableMap;
Jonathan Hart9bdaaec2016-08-22 13:33:45 -070021import com.google.common.collect.Multimaps;
Jonathan Hartc4f681c2016-09-09 07:14:25 -070022import com.google.common.collect.SetMultimap;
Jonathan Hart9bdaaec2016-08-22 13:33:45 -070023import org.apache.felix.scr.annotations.Activate;
24import org.apache.felix.scr.annotations.Component;
25import org.apache.felix.scr.annotations.Deactivate;
26import org.apache.felix.scr.annotations.Modified;
27import org.apache.felix.scr.annotations.Property;
28import org.apache.felix.scr.annotations.Reference;
29import org.apache.felix.scr.annotations.ReferenceCardinality;
30import org.apache.felix.scr.annotations.Service;
Jonathan Hart9bdaaec2016-08-22 13:33:45 -070031import org.onlab.packet.Ethernet;
32import org.onlab.packet.ICMP6;
33import org.onlab.packet.IPv6;
Jonathan Hartc4f681c2016-09-09 07:14:25 -070034import org.onlab.packet.IpAddress;
Jonathan Hart9bdaaec2016-08-22 13:33:45 -070035import org.onlab.packet.MacAddress;
36import org.onlab.packet.VlanId;
Jonathan Hart9bdaaec2016-08-22 13:33:45 -070037import org.onlab.util.Tools;
38import org.onosproject.cfg.ComponentConfigService;
39import org.onosproject.core.ApplicationId;
40import org.onosproject.core.CoreService;
41import org.onosproject.incubator.net.intf.Interface;
Jonathan Hartc4f681c2016-09-09 07:14:25 -070042import org.onosproject.incubator.net.neighbour.NeighbourHandlerRegistration;
Jonathan Hart9bdaaec2016-08-22 13:33:45 -070043import org.onosproject.incubator.net.neighbour.NeighbourMessageActions;
44import org.onosproject.incubator.net.neighbour.NeighbourMessageContext;
45import org.onosproject.incubator.net.neighbour.NeighbourMessageHandler;
46import org.onosproject.incubator.net.neighbour.NeighbourResolutionService;
47import org.onosproject.net.ConnectPoint;
48import org.onosproject.net.edge.EdgePortService;
49import org.onosproject.net.flow.DefaultTrafficSelector;
Jonathan Hart9bdaaec2016-08-22 13:33:45 -070050import org.onosproject.net.flow.TrafficSelector;
Jonathan Hart9bdaaec2016-08-22 13:33:45 -070051import org.onosproject.net.host.HostService;
Jonathan Hart9bdaaec2016-08-22 13:33:45 -070052import org.onosproject.net.packet.InboundPacket;
53import org.onosproject.net.packet.PacketContext;
54import org.onosproject.net.packet.PacketProcessor;
55import org.onosproject.net.packet.PacketService;
56import org.osgi.service.component.ComponentContext;
57import org.slf4j.Logger;
58import org.slf4j.LoggerFactory;
59
Jonathan Hartc4f681c2016-09-09 07:14:25 -070060import java.util.Collection;
Jonathan Hart9bdaaec2016-08-22 13:33:45 -070061import java.util.Dictionary;
Jonathan Hartc4f681c2016-09-09 07:14:25 -070062import java.util.Iterator;
63import java.util.Map;
Jonathan Hart9bdaaec2016-08-22 13:33:45 -070064import java.util.Objects;
Pier Ventre0ba98522016-09-19 15:49:14 -070065import java.util.stream.Collectors;
Jonathan Hart9bdaaec2016-08-22 13:33:45 -070066
67import static com.google.common.base.Preconditions.checkNotNull;
68import static org.onlab.packet.Ethernet.TYPE_ARP;
69import static org.onlab.packet.Ethernet.TYPE_IPV6;
70import static org.onlab.packet.ICMP6.NEIGHBOR_ADVERTISEMENT;
71import static org.onlab.packet.ICMP6.NEIGHBOR_SOLICITATION;
72import static org.onlab.packet.IPv6.PROTOCOL_ICMP6;
73import static org.onosproject.net.packet.PacketPriority.CONTROL;
74
75/**
76 * Manages handlers for neighbour messages.
77 */
78@Service
Jonathan Hartc4f681c2016-09-09 07:14:25 -070079@Component(immediate = true)
Jonathan Hart1e393bb2016-09-14 08:51:09 -070080public class NeighbourResolutionManager implements NeighbourResolutionService {
Jonathan Hart9bdaaec2016-08-22 13:33:45 -070081
82 private final Logger log = LoggerFactory.getLogger(getClass());
83
84 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
85 protected CoreService coreService;
86
87 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
88 protected HostService hostService;
89
90 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
91 protected EdgePortService edgeService;
92
93 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
94 protected PacketService packetService;
95
96 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
97 protected ComponentConfigService componentConfigService;
98
99 @Property(name = "ndpEnabled", boolValue = false,
100 label = "Enable IPv6 neighbour discovery")
101 protected boolean ndpEnabled = false;
102
103 private static final String APP_NAME = "org.onosproject.neighbour";
104 private ApplicationId appId;
105
Jonathan Hartc004adf2016-09-15 16:50:04 -0700106 private final SetMultimap<ConnectPoint, NeighbourHandlerRegistration> packetHandlers =
Jonathan Hartc4f681c2016-09-09 07:14:25 -0700107 Multimaps.synchronizedSetMultimap(HashMultimap.create());
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700108
109 private final InternalPacketProcessor processor = new InternalPacketProcessor();
Jonathan Hart584ea2d2016-10-11 10:49:16 +0200110 private NeighbourMessageActions actions;
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700111
112 @Activate
113 protected void activate(ComponentContext context) {
114 appId = coreService.registerApplication(APP_NAME);
115
116 componentConfigService.registerProperties(getClass());
117 modified(context);
118
Jonathan Hart584ea2d2016-10-11 10:49:16 +0200119 actions = new DefaultNeighbourMessageActions(packetService, edgeService);
120
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700121 packetService.addProcessor(processor, PacketProcessor.director(1));
122 }
123
124 @Deactivate
125 protected void deactivate() {
126 cancelPackets();
127 packetService.removeProcessor(processor);
128 componentConfigService.unregisterProperties(getClass(), false);
129 }
130
131 @Modified
132 protected void modified(ComponentContext context) {
133 Dictionary<?, ?> properties = context.getProperties();
134 Boolean flag;
135
136 flag = Tools.isPropertyEnabled(properties, "ndpEnabled");
137 if (flag != null) {
138 ndpEnabled = flag;
139 log.info("IPv6 neighbor discovery is {}",
140 ndpEnabled ? "enabled" : "disabled");
141 }
142
Jonathan Hartc004adf2016-09-15 16:50:04 -0700143 synchronized (packetHandlers) {
144 if (!packetHandlers.isEmpty()) {
145 requestPackets();
146 }
147 }
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700148 }
149
150 private void requestPackets() {
151 packetService.requestPackets(buildArpSelector(), CONTROL, appId);
152
153 if (ndpEnabled) {
154 packetService.requestPackets(buildNeighborSolicitationSelector(),
155 CONTROL, appId);
156 packetService.requestPackets(buildNeighborAdvertisementSelector(),
157 CONTROL, appId);
158 } else {
159 packetService.cancelPackets(buildNeighborSolicitationSelector(),
160 CONTROL, appId);
161 packetService.cancelPackets(buildNeighborAdvertisementSelector(),
162 CONTROL, appId);
163 }
164 }
165
166 private void cancelPackets() {
167 packetService.cancelPackets(buildArpSelector(), CONTROL, appId);
168 packetService.cancelPackets(buildNeighborSolicitationSelector(),
169 CONTROL, appId);
170 packetService.cancelPackets(buildNeighborAdvertisementSelector(),
171 CONTROL, appId);
172 }
173
174 private TrafficSelector buildArpSelector() {
175 return DefaultTrafficSelector.builder()
176 .matchEthType(TYPE_ARP)
177 .build();
178 }
179
180 private TrafficSelector buildNeighborSolicitationSelector() {
181 return DefaultTrafficSelector.builder()
182 .matchEthType(TYPE_IPV6)
183 .matchIPProtocol(PROTOCOL_ICMP6)
184 .matchIcmpv6Type(NEIGHBOR_SOLICITATION)
185 .build();
186 }
187
188 private TrafficSelector buildNeighborAdvertisementSelector() {
189 return DefaultTrafficSelector.builder()
190 .matchEthType(TYPE_IPV6)
191 .matchIPProtocol(PROTOCOL_ICMP6)
192 .matchIcmpv6Type(NEIGHBOR_ADVERTISEMENT)
193 .build();
194 }
195
196 @Override
Jonathan Hartc4f681c2016-09-09 07:14:25 -0700197 public void registerNeighbourHandler(ConnectPoint connectPoint,
198 NeighbourMessageHandler handler,
199 ApplicationId appId) {
Jonathan Hartc004adf2016-09-15 16:50:04 -0700200 register(connectPoint, new HandlerRegistration(handler, appId));
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700201 }
202
203 @Override
Jonathan Hartc4f681c2016-09-09 07:14:25 -0700204 public void registerNeighbourHandler(Interface intf,
205 NeighbourMessageHandler handler,
206 ApplicationId appId) {
Jonathan Hartc004adf2016-09-15 16:50:04 -0700207 register(intf.connectPoint(), new HandlerRegistration(handler, intf, appId));
208 }
209
210 private void register(ConnectPoint connectPoint, HandlerRegistration registration) {
211 synchronized (packetHandlers) {
212 if (packetHandlers.isEmpty()) {
213 requestPackets();
214 }
215 packetHandlers.put(connectPoint, registration);
216 }
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700217 }
218
219 @Override
Jonathan Hartc4f681c2016-09-09 07:14:25 -0700220 public void unregisterNeighbourHandler(ConnectPoint connectPoint,
221 NeighbourMessageHandler handler,
222 ApplicationId appId) {
Jonathan Hartc004adf2016-09-15 16:50:04 -0700223 unregister(connectPoint, new HandlerRegistration(handler, appId));
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700224 }
225
226 @Override
Jonathan Hartc4f681c2016-09-09 07:14:25 -0700227 public void unregisterNeighbourHandler(Interface intf,
228 NeighbourMessageHandler handler,
229 ApplicationId appId) {
Jonathan Hartc004adf2016-09-15 16:50:04 -0700230 unregister(intf.connectPoint(), new HandlerRegistration(handler, intf, appId));
231 }
232
233 private void unregister(ConnectPoint connectPoint, HandlerRegistration registration) {
234 synchronized (packetHandlers) {
235 packetHandlers.remove(connectPoint, registration);
236
237 if (packetHandlers.isEmpty()) {
238 cancelPackets();
239 }
240 }
Jonathan Hartc4f681c2016-09-09 07:14:25 -0700241 }
242
243 @Override
244 public void unregisterNeighbourHandlers(ApplicationId appId) {
245 synchronized (packetHandlers) {
246 Iterator<NeighbourHandlerRegistration> it = packetHandlers.values().iterator();
247
248 while (it.hasNext()) {
249 NeighbourHandlerRegistration registration = it.next();
250 if (registration.appId().equals(appId)) {
251 it.remove();
252 }
253 }
Jonathan Hartc004adf2016-09-15 16:50:04 -0700254
255 if (packetHandlers.isEmpty()) {
256 cancelPackets();
257 }
Jonathan Hartc4f681c2016-09-09 07:14:25 -0700258 }
259 }
260
261 @Override
262 public Map<ConnectPoint, Collection<NeighbourHandlerRegistration>> getHandlerRegistrations() {
263 return ImmutableMap.copyOf(Multimaps.asMap(packetHandlers));
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700264 }
265
Jonathan Hart584ea2d2016-10-11 10:49:16 +0200266 private void handlePacket(PacketContext context) {
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700267 InboundPacket pkt = context.inPacket();
268 Ethernet ethPkt = pkt.parsed();
269
270 NeighbourMessageContext msgContext =
271 DefaultNeighbourMessageContext.createContext(ethPkt, pkt.receivedFrom(), actions);
272
273 if (msgContext == null) {
274 return;
275 }
276
Pier Ventre0ba98522016-09-19 15:49:14 -0700277 if (handleMessage(msgContext)) {
278 context.block();
279 }
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700280
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700281 }
282
Pier Ventre0ba98522016-09-19 15:49:14 -0700283 private boolean handleMessage(NeighbourMessageContext context) {
Jonathan Hartc4f681c2016-09-09 07:14:25 -0700284 Collection<NeighbourHandlerRegistration> handlers = packetHandlers.get(context.inPort());
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700285
Pier Ventre0ba98522016-09-19 15:49:14 -0700286 Collection<NeighbourHandlerRegistration> handled = handlers
287 .stream()
288 .filter(registration -> registration.intf() == null || matches(context, registration.intf()))
289 .collect(Collectors.toSet());
290
291 handled.forEach(registration -> registration.handler().handleMessage(context, hostService));
292
293 return !handled.isEmpty();
294
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700295 }
296
Jonathan Hartc4f681c2016-09-09 07:14:25 -0700297 /**
298 * Checks that incoming packet matches the parameters of the interface.
299 * This means that if the interface specifies a particular parameter
300 * (VLAN, IP address, etc.) then the incoming packet should match those
301 * parameters.
302 *
303 * @param context incoming message context
304 * @param intf interface to check
305 * @return true if the incoming message matches the interface, otherwise false
306 */
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700307 private boolean matches(NeighbourMessageContext context, Interface intf) {
308 checkNotNull(context);
309 checkNotNull(intf);
310
311 boolean matches = true;
Jonathan Hartc4f681c2016-09-09 07:14:25 -0700312 // For non-broadcast packets, if the interface has a MAC address check that
313 // the destination MAC address of the packet matches the interface MAC
314 if (!context.dstMac().isBroadcast() &&
315 !intf.mac().equals(MacAddress.NONE) &&
316 !intf.mac().equals(context.dstMac())) {
317 matches = false;
318 }
319 // If the interface has a VLAN, check that the packet's VLAN matches
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700320 if (!intf.vlan().equals(VlanId.NONE) && !intf.vlan().equals(context.vlan())) {
321 matches = false;
322 }
Jonathan Hartc4f681c2016-09-09 07:14:25 -0700323 // If the interface has IP addresses, check that the packet's target IP
324 // address matches one of the interface IP addresses
325 if (!intf.ipAddressesList().isEmpty() && !hasIp(intf, context.target())) {
326 matches = false;
327 }
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700328
329 return matches;
330 }
331
Jonathan Hartc4f681c2016-09-09 07:14:25 -0700332 /**
333 * Returns true if the interface has the given IP address.
334 *
335 * @param intf interface to check
336 * @param ip IP address
337 * @return true if the IP is configured on the interface, otherwise false
338 */
339 private boolean hasIp(Interface intf, IpAddress ip) {
340 return intf.ipAddressesList().stream()
341 .anyMatch(intfAddress -> intfAddress.ipAddress().equals(ip));
342 }
343
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700344 /**
345 * Stores a neighbour message handler registration.
346 */
Jonathan Hartc4f681c2016-09-09 07:14:25 -0700347 private class HandlerRegistration implements NeighbourHandlerRegistration {
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700348 private final Interface intf;
349 private final NeighbourMessageHandler handler;
Jonathan Hartc4f681c2016-09-09 07:14:25 -0700350 private final ApplicationId appId;
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700351
352 /**
353 * Creates a new handler registration.
354 *
355 * @param handler neighbour message handler
356 */
Jonathan Hartc4f681c2016-09-09 07:14:25 -0700357 public HandlerRegistration(NeighbourMessageHandler handler, ApplicationId appId) {
358 this(handler, null, appId);
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700359 }
360
361 /**
362 * Creates a new handler registration.
363 *
364 * @param handler neighbour message handler
365 * @param intf interface
366 */
Jonathan Hartc4f681c2016-09-09 07:14:25 -0700367 public HandlerRegistration(NeighbourMessageHandler handler, Interface intf, ApplicationId appId) {
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700368 this.intf = intf;
369 this.handler = handler;
Jonathan Hartc4f681c2016-09-09 07:14:25 -0700370 this.appId = appId;
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700371 }
372
Jonathan Hartc4f681c2016-09-09 07:14:25 -0700373 @Override
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700374 public Interface intf() {
375 return intf;
376 }
377
Jonathan Hartc4f681c2016-09-09 07:14:25 -0700378 @Override
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700379 public NeighbourMessageHandler handler() {
380 return handler;
381 }
382
383 @Override
Jonathan Hartc4f681c2016-09-09 07:14:25 -0700384 public ApplicationId appId() {
385 return appId;
386 }
387
388 @Override
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700389 public boolean equals(Object other) {
390 if (this == other) {
391 return true;
392 }
393
394 if (!(other instanceof HandlerRegistration)) {
395 return false;
396 }
397
398 HandlerRegistration that = (HandlerRegistration) other;
399
400 return Objects.equals(intf, that.intf) &&
Jonathan Hartc4f681c2016-09-09 07:14:25 -0700401 Objects.equals(handler, that.handler) &&
402 Objects.equals(appId, that.appId);
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700403 }
404
405 @Override
406 public int hashCode() {
Jonathan Hartc4f681c2016-09-09 07:14:25 -0700407 return Objects.hash(intf, handler, appId);
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700408 }
409 }
410
411 /**
412 * Packet processor for incoming packets.
413 */
414 private class InternalPacketProcessor implements PacketProcessor {
415
416 @Override
417 public void process(PacketContext context) {
418 // Stop processing if the packet has been handled, since we
419 // can't do any more to it.
420 if (context.isHandled()) {
421 return;
422 }
423
424 InboundPacket pkt = context.inPacket();
425 Ethernet ethPkt = pkt.parsed();
426 if (ethPkt == null) {
427 return;
428 }
429
430 if (ethPkt.getEtherType() == TYPE_ARP) {
431 // handle ARP packets
432 handlePacket(context);
433 } else if (ethPkt.getEtherType() == TYPE_IPV6) {
434 IPv6 ipv6 = (IPv6) ethPkt.getPayload();
435 if (ipv6.getNextHeader() == IPv6.PROTOCOL_ICMP6) {
436 ICMP6 icmp6 = (ICMP6) ipv6.getPayload();
437 if (icmp6.getIcmpType() == NEIGHBOR_SOLICITATION ||
438 icmp6.getIcmpType() == NEIGHBOR_ADVERTISEMENT) {
439 // handle ICMPv6 solicitations and advertisements (NDP)
440 handlePacket(context);
441 }
442 }
443 }
444 }
445 }
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700446}