blob: 43db0403cfaa11ede34a205129f64c11d707a216 [file] [log] [blame]
Jonathan Hart9bdaaec2016-08-22 13:33:45 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2016-present Open Networking Foundation
Jonathan Hart9bdaaec2016-08-22 13:33:45 -07003 *
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;
Ray Milkeyfacf2862017-08-03 11:58:29 -070041import org.onosproject.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
Pier Luigi65a0ab32017-01-12 20:52:38 -080099 @Property(name = "arpEnabled", boolValue = true,
100 label = "Enable Address resolution protocol")
101 protected boolean arpEnabled = true;
102
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700103 @Property(name = "ndpEnabled", boolValue = false,
104 label = "Enable IPv6 neighbour discovery")
105 protected boolean ndpEnabled = false;
106
Pier Luigi65a0ab32017-01-12 20:52:38 -0800107 @Property(name = "requestInterceptsEnabled", boolValue = true,
108 label = "Enable requesting packet intercepts")
109 private boolean requestInterceptsEnabled = true;
110
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700111 private static final String APP_NAME = "org.onosproject.neighbour";
112 private ApplicationId appId;
113
Jonathan Hartc004adf2016-09-15 16:50:04 -0700114 private final SetMultimap<ConnectPoint, NeighbourHandlerRegistration> packetHandlers =
Jonathan Hartc4f681c2016-09-09 07:14:25 -0700115 Multimaps.synchronizedSetMultimap(HashMultimap.create());
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700116
117 private final InternalPacketProcessor processor = new InternalPacketProcessor();
Jonathan Hart584ea2d2016-10-11 10:49:16 +0200118 private NeighbourMessageActions actions;
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700119
120 @Activate
121 protected void activate(ComponentContext context) {
122 appId = coreService.registerApplication(APP_NAME);
123
124 componentConfigService.registerProperties(getClass());
125 modified(context);
126
Jonathan Hart584ea2d2016-10-11 10:49:16 +0200127 actions = new DefaultNeighbourMessageActions(packetService, edgeService);
128
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700129 packetService.addProcessor(processor, PacketProcessor.director(1));
130 }
131
132 @Deactivate
133 protected void deactivate() {
134 cancelPackets();
135 packetService.removeProcessor(processor);
136 componentConfigService.unregisterProperties(getClass(), false);
137 }
138
139 @Modified
140 protected void modified(ComponentContext context) {
141 Dictionary<?, ?> properties = context.getProperties();
142 Boolean flag;
143
144 flag = Tools.isPropertyEnabled(properties, "ndpEnabled");
145 if (flag != null) {
146 ndpEnabled = flag;
147 log.info("IPv6 neighbor discovery is {}",
148 ndpEnabled ? "enabled" : "disabled");
149 }
150
Pier Luigi65a0ab32017-01-12 20:52:38 -0800151 flag = Tools.isPropertyEnabled(properties, "arpEnabled");
152 if (flag != null) {
153 arpEnabled = flag;
154 log.info("Address resolution protocol is {}",
155 arpEnabled ? "enabled" : "disabled");
156 }
157
158 flag = Tools.isPropertyEnabled(properties, "requestInterceptsEnabled");
159 if (flag == null) {
160 log.info("Request intercepts is not configured, " +
161 "using current value of {}", requestInterceptsEnabled);
162 } else {
163 requestInterceptsEnabled = flag;
164 log.info("Configured. Request intercepts is {}",
165 requestInterceptsEnabled ? "enabled" : "disabled");
166 }
167
Jonathan Hartc004adf2016-09-15 16:50:04 -0700168 synchronized (packetHandlers) {
Pier Luigi65a0ab32017-01-12 20:52:38 -0800169 if (!packetHandlers.isEmpty() && requestInterceptsEnabled) {
Jonathan Hartc004adf2016-09-15 16:50:04 -0700170 requestPackets();
Pier Luigi65a0ab32017-01-12 20:52:38 -0800171 } else {
172 cancelPackets();
Jonathan Hartc004adf2016-09-15 16:50:04 -0700173 }
174 }
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700175 }
176
177 private void requestPackets() {
Pier Luigi65a0ab32017-01-12 20:52:38 -0800178 if (arpEnabled) {
179 packetService.requestPackets(buildArpSelector(), CONTROL, appId);
180 } else {
181 packetService.cancelPackets(buildArpSelector(), CONTROL, appId);
182 }
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700183
184 if (ndpEnabled) {
185 packetService.requestPackets(buildNeighborSolicitationSelector(),
186 CONTROL, appId);
187 packetService.requestPackets(buildNeighborAdvertisementSelector(),
188 CONTROL, appId);
189 } else {
190 packetService.cancelPackets(buildNeighborSolicitationSelector(),
191 CONTROL, appId);
192 packetService.cancelPackets(buildNeighborAdvertisementSelector(),
193 CONTROL, appId);
194 }
195 }
196
197 private void cancelPackets() {
198 packetService.cancelPackets(buildArpSelector(), CONTROL, appId);
199 packetService.cancelPackets(buildNeighborSolicitationSelector(),
200 CONTROL, appId);
201 packetService.cancelPackets(buildNeighborAdvertisementSelector(),
202 CONTROL, appId);
203 }
204
205 private TrafficSelector buildArpSelector() {
206 return DefaultTrafficSelector.builder()
207 .matchEthType(TYPE_ARP)
208 .build();
209 }
210
211 private TrafficSelector buildNeighborSolicitationSelector() {
212 return DefaultTrafficSelector.builder()
213 .matchEthType(TYPE_IPV6)
214 .matchIPProtocol(PROTOCOL_ICMP6)
215 .matchIcmpv6Type(NEIGHBOR_SOLICITATION)
216 .build();
217 }
218
219 private TrafficSelector buildNeighborAdvertisementSelector() {
220 return DefaultTrafficSelector.builder()
221 .matchEthType(TYPE_IPV6)
222 .matchIPProtocol(PROTOCOL_ICMP6)
223 .matchIcmpv6Type(NEIGHBOR_ADVERTISEMENT)
224 .build();
225 }
226
227 @Override
Jonathan Hartc4f681c2016-09-09 07:14:25 -0700228 public void registerNeighbourHandler(ConnectPoint connectPoint,
229 NeighbourMessageHandler handler,
230 ApplicationId appId) {
Jonathan Hartc004adf2016-09-15 16:50:04 -0700231 register(connectPoint, new HandlerRegistration(handler, appId));
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700232 }
233
234 @Override
Jonathan Hartc4f681c2016-09-09 07:14:25 -0700235 public void registerNeighbourHandler(Interface intf,
236 NeighbourMessageHandler handler,
237 ApplicationId appId) {
Jonathan Hartc004adf2016-09-15 16:50:04 -0700238 register(intf.connectPoint(), new HandlerRegistration(handler, intf, appId));
239 }
240
241 private void register(ConnectPoint connectPoint, HandlerRegistration registration) {
242 synchronized (packetHandlers) {
Pier Ventre09f88b82017-01-09 23:01:05 -0800243 if (packetHandlers.isEmpty() && requestInterceptsEnabled) {
Jonathan Hartc004adf2016-09-15 16:50:04 -0700244 requestPackets();
245 }
246 packetHandlers.put(connectPoint, registration);
247 }
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700248 }
249
250 @Override
Jonathan Hartc4f681c2016-09-09 07:14:25 -0700251 public void unregisterNeighbourHandler(ConnectPoint connectPoint,
252 NeighbourMessageHandler handler,
253 ApplicationId appId) {
Jonathan Hartc004adf2016-09-15 16:50:04 -0700254 unregister(connectPoint, new HandlerRegistration(handler, appId));
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700255 }
256
257 @Override
Jonathan Hartc4f681c2016-09-09 07:14:25 -0700258 public void unregisterNeighbourHandler(Interface intf,
259 NeighbourMessageHandler handler,
260 ApplicationId appId) {
Jonathan Hartc004adf2016-09-15 16:50:04 -0700261 unregister(intf.connectPoint(), new HandlerRegistration(handler, intf, appId));
262 }
263
264 private void unregister(ConnectPoint connectPoint, HandlerRegistration registration) {
265 synchronized (packetHandlers) {
266 packetHandlers.remove(connectPoint, registration);
267
268 if (packetHandlers.isEmpty()) {
269 cancelPackets();
270 }
271 }
Jonathan Hartc4f681c2016-09-09 07:14:25 -0700272 }
273
274 @Override
275 public void unregisterNeighbourHandlers(ApplicationId appId) {
276 synchronized (packetHandlers) {
277 Iterator<NeighbourHandlerRegistration> it = packetHandlers.values().iterator();
278
279 while (it.hasNext()) {
280 NeighbourHandlerRegistration registration = it.next();
281 if (registration.appId().equals(appId)) {
282 it.remove();
283 }
284 }
Jonathan Hartc004adf2016-09-15 16:50:04 -0700285
286 if (packetHandlers.isEmpty()) {
287 cancelPackets();
288 }
Jonathan Hartc4f681c2016-09-09 07:14:25 -0700289 }
290 }
291
292 @Override
293 public Map<ConnectPoint, Collection<NeighbourHandlerRegistration>> getHandlerRegistrations() {
294 return ImmutableMap.copyOf(Multimaps.asMap(packetHandlers));
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700295 }
296
Jonathan Hart584ea2d2016-10-11 10:49:16 +0200297 private void handlePacket(PacketContext context) {
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700298 InboundPacket pkt = context.inPacket();
299 Ethernet ethPkt = pkt.parsed();
300
301 NeighbourMessageContext msgContext =
302 DefaultNeighbourMessageContext.createContext(ethPkt, pkt.receivedFrom(), actions);
303
304 if (msgContext == null) {
305 return;
306 }
307
Pier Ventre0ba98522016-09-19 15:49:14 -0700308 if (handleMessage(msgContext)) {
309 context.block();
310 }
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700311
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700312 }
313
Pier Ventre0ba98522016-09-19 15:49:14 -0700314 private boolean handleMessage(NeighbourMessageContext context) {
Jonathan Hartc4f681c2016-09-09 07:14:25 -0700315 Collection<NeighbourHandlerRegistration> handlers = packetHandlers.get(context.inPort());
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700316
Pier Ventre0ba98522016-09-19 15:49:14 -0700317 Collection<NeighbourHandlerRegistration> handled = handlers
318 .stream()
319 .filter(registration -> registration.intf() == null || matches(context, registration.intf()))
320 .collect(Collectors.toSet());
321
322 handled.forEach(registration -> registration.handler().handleMessage(context, hostService));
323
324 return !handled.isEmpty();
325
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700326 }
327
Jonathan Hartc4f681c2016-09-09 07:14:25 -0700328 /**
329 * Checks that incoming packet matches the parameters of the interface.
330 * This means that if the interface specifies a particular parameter
331 * (VLAN, IP address, etc.) then the incoming packet should match those
332 * parameters.
333 *
334 * @param context incoming message context
335 * @param intf interface to check
336 * @return true if the incoming message matches the interface, otherwise false
337 */
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700338 private boolean matches(NeighbourMessageContext context, Interface intf) {
339 checkNotNull(context);
340 checkNotNull(intf);
341
342 boolean matches = true;
Jonathan Hartc4f681c2016-09-09 07:14:25 -0700343 // For non-broadcast packets, if the interface has a MAC address check that
344 // the destination MAC address of the packet matches the interface MAC
345 if (!context.dstMac().isBroadcast() &&
346 !intf.mac().equals(MacAddress.NONE) &&
347 !intf.mac().equals(context.dstMac())) {
348 matches = false;
349 }
350 // If the interface has a VLAN, check that the packet's VLAN matches
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700351 if (!intf.vlan().equals(VlanId.NONE) && !intf.vlan().equals(context.vlan())) {
352 matches = false;
353 }
Jonathan Hartc4f681c2016-09-09 07:14:25 -0700354 // If the interface has IP addresses, check that the packet's target IP
355 // address matches one of the interface IP addresses
356 if (!intf.ipAddressesList().isEmpty() && !hasIp(intf, context.target())) {
357 matches = false;
358 }
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700359
360 return matches;
361 }
362
Jonathan Hartc4f681c2016-09-09 07:14:25 -0700363 /**
364 * Returns true if the interface has the given IP address.
365 *
366 * @param intf interface to check
367 * @param ip IP address
368 * @return true if the IP is configured on the interface, otherwise false
369 */
370 private boolean hasIp(Interface intf, IpAddress ip) {
371 return intf.ipAddressesList().stream()
372 .anyMatch(intfAddress -> intfAddress.ipAddress().equals(ip));
373 }
374
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700375 /**
376 * Stores a neighbour message handler registration.
377 */
Jonathan Hartc4f681c2016-09-09 07:14:25 -0700378 private class HandlerRegistration implements NeighbourHandlerRegistration {
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700379 private final Interface intf;
380 private final NeighbourMessageHandler handler;
Jonathan Hartc4f681c2016-09-09 07:14:25 -0700381 private final ApplicationId appId;
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700382
383 /**
384 * Creates a new handler registration.
385 *
386 * @param handler neighbour message handler
387 */
Jonathan Hartc4f681c2016-09-09 07:14:25 -0700388 public HandlerRegistration(NeighbourMessageHandler handler, ApplicationId appId) {
389 this(handler, null, appId);
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700390 }
391
392 /**
393 * Creates a new handler registration.
394 *
395 * @param handler neighbour message handler
396 * @param intf interface
397 */
Jonathan Hartc4f681c2016-09-09 07:14:25 -0700398 public HandlerRegistration(NeighbourMessageHandler handler, Interface intf, ApplicationId appId) {
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700399 this.intf = intf;
400 this.handler = handler;
Jonathan Hartc4f681c2016-09-09 07:14:25 -0700401 this.appId = appId;
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700402 }
403
Jonathan Hartc4f681c2016-09-09 07:14:25 -0700404 @Override
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700405 public Interface intf() {
406 return intf;
407 }
408
Jonathan Hartc4f681c2016-09-09 07:14:25 -0700409 @Override
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700410 public NeighbourMessageHandler handler() {
411 return handler;
412 }
413
414 @Override
Jonathan Hartc4f681c2016-09-09 07:14:25 -0700415 public ApplicationId appId() {
416 return appId;
417 }
418
419 @Override
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700420 public boolean equals(Object other) {
421 if (this == other) {
422 return true;
423 }
424
425 if (!(other instanceof HandlerRegistration)) {
426 return false;
427 }
428
429 HandlerRegistration that = (HandlerRegistration) other;
430
431 return Objects.equals(intf, that.intf) &&
Jonathan Hartc4f681c2016-09-09 07:14:25 -0700432 Objects.equals(handler, that.handler) &&
433 Objects.equals(appId, that.appId);
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700434 }
435
436 @Override
437 public int hashCode() {
Jonathan Hartc4f681c2016-09-09 07:14:25 -0700438 return Objects.hash(intf, handler, appId);
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700439 }
440 }
441
442 /**
443 * Packet processor for incoming packets.
444 */
445 private class InternalPacketProcessor implements PacketProcessor {
446
447 @Override
448 public void process(PacketContext context) {
449 // Stop processing if the packet has been handled, since we
450 // can't do any more to it.
451 if (context.isHandled()) {
452 return;
453 }
454
455 InboundPacket pkt = context.inPacket();
456 Ethernet ethPkt = pkt.parsed();
457 if (ethPkt == null) {
458 return;
459 }
460
461 if (ethPkt.getEtherType() == TYPE_ARP) {
462 // handle ARP packets
463 handlePacket(context);
464 } else if (ethPkt.getEtherType() == TYPE_IPV6) {
465 IPv6 ipv6 = (IPv6) ethPkt.getPayload();
466 if (ipv6.getNextHeader() == IPv6.PROTOCOL_ICMP6) {
467 ICMP6 icmp6 = (ICMP6) ipv6.getPayload();
468 if (icmp6.getIcmpType() == NEIGHBOR_SOLICITATION ||
469 icmp6.getIcmpType() == NEIGHBOR_ADVERTISEMENT) {
470 // handle ICMPv6 solicitations and advertisements (NDP)
471 handlePacket(context);
472 }
473 }
474 }
475 }
476 }
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700477}