blob: 0101c84e4ecb8d17b5bb74a730ed2c31434c92ba [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
Ray Milkeyb65d7842017-08-03 16:28:24 -070017package org.onosproject.net.neighbour.impl;
Jonathan Hart9bdaaec2016-08-22 13:33:45 -070018
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;
Ray Milkeyb65d7842017-08-03 16:28:24 -070042import org.onosproject.net.neighbour.NeighbourHandlerRegistration;
43import org.onosproject.net.neighbour.NeighbourMessageActions;
44import org.onosproject.net.neighbour.NeighbourMessageContext;
45import org.onosproject.net.neighbour.NeighbourMessageHandler;
46import org.onosproject.net.neighbour.NeighbourResolutionService;
Jonathan Hart9bdaaec2016-08-22 13:33:45 -070047import 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() {
Ray Milkeyfb1c7622018-05-18 10:16:53 -0700294 synchronized (packetHandlers) {
295 return ImmutableMap.copyOf(Multimaps.asMap(packetHandlers));
296 }
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700297 }
298
Jonathan Hart584ea2d2016-10-11 10:49:16 +0200299 private void handlePacket(PacketContext context) {
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700300 InboundPacket pkt = context.inPacket();
301 Ethernet ethPkt = pkt.parsed();
302
303 NeighbourMessageContext msgContext =
304 DefaultNeighbourMessageContext.createContext(ethPkt, pkt.receivedFrom(), actions);
305
306 if (msgContext == null) {
307 return;
308 }
309
Pier Ventre0ba98522016-09-19 15:49:14 -0700310 if (handleMessage(msgContext)) {
311 context.block();
312 }
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700313
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700314 }
315
Pier Ventre0ba98522016-09-19 15:49:14 -0700316 private boolean handleMessage(NeighbourMessageContext context) {
Ray Milkeyfb1c7622018-05-18 10:16:53 -0700317 Collection<NeighbourHandlerRegistration> handled;
318 synchronized (packetHandlers) {
319 handled = packetHandlers.get(context.inPort())
320 .stream()
321 .filter(registration -> registration.intf() == null || matches(context, registration.intf()))
322 .collect(Collectors.toSet());
323 }
Pier Ventre0ba98522016-09-19 15:49:14 -0700324 handled.forEach(registration -> registration.handler().handleMessage(context, hostService));
325
326 return !handled.isEmpty();
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700327 }
328
Jonathan Hartc4f681c2016-09-09 07:14:25 -0700329 /**
330 * Checks that incoming packet matches the parameters of the interface.
331 * This means that if the interface specifies a particular parameter
332 * (VLAN, IP address, etc.) then the incoming packet should match those
333 * parameters.
334 *
335 * @param context incoming message context
336 * @param intf interface to check
337 * @return true if the incoming message matches the interface, otherwise false
338 */
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700339 private boolean matches(NeighbourMessageContext context, Interface intf) {
340 checkNotNull(context);
341 checkNotNull(intf);
342
343 boolean matches = true;
Jonathan Hartc4f681c2016-09-09 07:14:25 -0700344 // For non-broadcast packets, if the interface has a MAC address check that
345 // the destination MAC address of the packet matches the interface MAC
346 if (!context.dstMac().isBroadcast() &&
347 !intf.mac().equals(MacAddress.NONE) &&
348 !intf.mac().equals(context.dstMac())) {
349 matches = false;
350 }
351 // If the interface has a VLAN, check that the packet's VLAN matches
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700352 if (!intf.vlan().equals(VlanId.NONE) && !intf.vlan().equals(context.vlan())) {
353 matches = false;
354 }
Jonathan Hartc4f681c2016-09-09 07:14:25 -0700355 // If the interface has IP addresses, check that the packet's target IP
356 // address matches one of the interface IP addresses
357 if (!intf.ipAddressesList().isEmpty() && !hasIp(intf, context.target())) {
358 matches = false;
359 }
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700360
361 return matches;
362 }
363
Jonathan Hartc4f681c2016-09-09 07:14:25 -0700364 /**
365 * Returns true if the interface has the given IP address.
366 *
367 * @param intf interface to check
368 * @param ip IP address
369 * @return true if the IP is configured on the interface, otherwise false
370 */
371 private boolean hasIp(Interface intf, IpAddress ip) {
372 return intf.ipAddressesList().stream()
373 .anyMatch(intfAddress -> intfAddress.ipAddress().equals(ip));
374 }
375
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700376 /**
377 * Stores a neighbour message handler registration.
378 */
Jonathan Hartc4f681c2016-09-09 07:14:25 -0700379 private class HandlerRegistration implements NeighbourHandlerRegistration {
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700380 private final Interface intf;
381 private final NeighbourMessageHandler handler;
Jonathan Hartc4f681c2016-09-09 07:14:25 -0700382 private final ApplicationId appId;
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700383
384 /**
385 * Creates a new handler registration.
386 *
387 * @param handler neighbour message handler
388 */
Jonathan Hartc4f681c2016-09-09 07:14:25 -0700389 public HandlerRegistration(NeighbourMessageHandler handler, ApplicationId appId) {
390 this(handler, null, appId);
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700391 }
392
393 /**
394 * Creates a new handler registration.
395 *
396 * @param handler neighbour message handler
397 * @param intf interface
398 */
Jonathan Hartc4f681c2016-09-09 07:14:25 -0700399 public HandlerRegistration(NeighbourMessageHandler handler, Interface intf, ApplicationId appId) {
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700400 this.intf = intf;
401 this.handler = handler;
Jonathan Hartc4f681c2016-09-09 07:14:25 -0700402 this.appId = appId;
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700403 }
404
Jonathan Hartc4f681c2016-09-09 07:14:25 -0700405 @Override
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700406 public Interface intf() {
407 return intf;
408 }
409
Jonathan Hartc4f681c2016-09-09 07:14:25 -0700410 @Override
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700411 public NeighbourMessageHandler handler() {
412 return handler;
413 }
414
415 @Override
Jonathan Hartc4f681c2016-09-09 07:14:25 -0700416 public ApplicationId appId() {
417 return appId;
418 }
419
420 @Override
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700421 public boolean equals(Object other) {
422 if (this == other) {
423 return true;
424 }
425
426 if (!(other instanceof HandlerRegistration)) {
427 return false;
428 }
429
430 HandlerRegistration that = (HandlerRegistration) other;
431
432 return Objects.equals(intf, that.intf) &&
Jonathan Hartc4f681c2016-09-09 07:14:25 -0700433 Objects.equals(handler, that.handler) &&
434 Objects.equals(appId, that.appId);
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700435 }
436
437 @Override
438 public int hashCode() {
Jonathan Hartc4f681c2016-09-09 07:14:25 -0700439 return Objects.hash(intf, handler, appId);
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700440 }
441 }
442
443 /**
444 * Packet processor for incoming packets.
445 */
446 private class InternalPacketProcessor implements PacketProcessor {
447
448 @Override
449 public void process(PacketContext context) {
450 // Stop processing if the packet has been handled, since we
451 // can't do any more to it.
452 if (context.isHandled()) {
453 return;
454 }
455
456 InboundPacket pkt = context.inPacket();
457 Ethernet ethPkt = pkt.parsed();
458 if (ethPkt == null) {
459 return;
460 }
461
462 if (ethPkt.getEtherType() == TYPE_ARP) {
463 // handle ARP packets
464 handlePacket(context);
465 } else if (ethPkt.getEtherType() == TYPE_IPV6) {
466 IPv6 ipv6 = (IPv6) ethPkt.getPayload();
467 if (ipv6.getNextHeader() == IPv6.PROTOCOL_ICMP6) {
468 ICMP6 icmp6 = (ICMP6) ipv6.getPayload();
469 if (icmp6.getIcmpType() == NEIGHBOR_SOLICITATION ||
470 icmp6.getIcmpType() == NEIGHBOR_ADVERTISEMENT) {
471 // handle ICMPv6 solicitations and advertisements (NDP)
472 handlePacket(context);
473 }
474 }
475 }
476 }
477 }
Jonathan Hart9bdaaec2016-08-22 13:33:45 -0700478}