Jonathan Hart
Committed by Gerrit Code Review

Reimplemented the ProxyArp app on top of the NeighbourResolutionService.

Also some small API changes to track app ID of handler registrations, and
improved interface matching in NeighbourPacketManager.

Added CLI to view handler registrations.

Change-Id: I8cd0b91a16d9ec60287b65f9d8fc5e3cd87560e8
COMPILE_DEPS = [
'//lib:CORE_DEPS',
'//incubator/api:onos-incubator-api',
]
osgi_jar (
......
......@@ -43,6 +43,10 @@
<groupId>org.osgi</groupId>
<artifactId>org.osgi.compendium</artifactId>
</dependency>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-incubator-api</artifactId>
</dependency>
</dependencies>
</project>
......
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.proxyarp;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
import org.onosproject.incubator.net.neighbour.DefaultNeighbourMessageHandler;
import org.onosproject.incubator.net.neighbour.NeighbourResolutionService;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.edge.EdgePortEvent;
import org.onosproject.net.edge.EdgePortListener;
import org.onosproject.net.edge.EdgePortService;
/**
* Implements proxy ARP and NDP functionality by considering the entire network
* as a single L2 broadcast domain.
* <p>
* This application maintains a DefaultNeighbourMessageHandler on all edge ports
* in the network, and the handler implements the desired proxying functionality.
* </p>
*/
@Component(immediate = true)
public class DefaultProxyArp {
private static final String APP_NAME = "org.onosproject.proxyarp";
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected EdgePortService edgeService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected NeighbourResolutionService neighbourResolutionService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected CoreService coreService;
private ApplicationId appId;
private InternalEdgeListener edgeListener = new InternalEdgeListener();
private DefaultNeighbourMessageHandler defaultHandler = new DefaultNeighbourMessageHandler();
@Activate
protected void activate() {
appId = coreService.registerApplication(APP_NAME);
edgeService.addListener(edgeListener);
edgeService.getEdgePoints().forEach(this::addDefault);
}
@Deactivate
protected void deactivate() {
edgeService.removeListener(edgeListener);
neighbourResolutionService.unregisterNeighbourHandlers(appId);
}
private void addDefault(ConnectPoint port) {
neighbourResolutionService.registerNeighbourHandler(port, defaultHandler, appId);
}
private void removeDefault(ConnectPoint port) {
neighbourResolutionService.unregisterNeighbourHandler(port, defaultHandler, appId);
}
private class InternalEdgeListener implements EdgePortListener {
@Override
public void event(EdgePortEvent event) {
switch (event.type()) {
case EDGE_PORT_ADDED:
addDefault(event.subject());
break;
case EDGE_PORT_REMOVED:
removeDefault(event.subject());
break;
default:
break;
}
}
}
}
......@@ -51,8 +51,11 @@ import static org.slf4j.LoggerFactory.getLogger;
/**
* Sample reactive proxy arp application.
*
* @deprecated in Hummingbird release
*/
@Component(immediate = true)
@Deprecated
@Component(enabled = false)
public class ProxyArp {
private final Logger log = getLogger(getClass());
......
......@@ -15,6 +15,6 @@
*/
/**
* Proxy Arp application that handles arp resolution for you.
* Application that handles ARP and NDP resolution on a single L2 broadcast domain.
*/
package org.onosproject.proxyarp;
......
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.cli.net;
import org.apache.karaf.shell.commands.Command;
import org.onosproject.cli.AbstractShellCommand;
import org.onosproject.incubator.net.intf.Interface;
import org.onosproject.incubator.net.neighbour.NeighbourResolutionService;
/**
* Lists neighbour message handlers.
*/
@Command(scope = "onos", name = "neighbour-handlers",
description = "Lists neighbour message handlers")
public class NeighbourHandlersListCommand extends AbstractShellCommand {
private static final String FORMAT = "%20s: interface=%s, class=%s";
@Override
protected void execute() {
NeighbourResolutionService service = get(NeighbourResolutionService.class);
service.getHandlerRegistrations().forEach((cp, list) -> {
list.forEach(hr -> print(FORMAT, cp, intfToName(hr.intf()),
hr.handler().getClass().getCanonicalName()));
});
}
private String intfToName(Interface intf) {
return (intf == null) ? "(None)" : intf.name();
}
}
......@@ -485,6 +485,9 @@
<command>
<action class="org.onosproject.cli.net.PacketProcessorsListCommand"/>
</command>
<command>
<action class="org.onosproject.cli.net.NeighbourHandlersListCommand"/>
</command>
<command>
<action class="org.onosproject.cli.net.AddTestFlowsCommand"/>
......
......@@ -22,8 +22,11 @@ import org.onosproject.net.packet.PacketContext;
/**
* Service for processing ARP or NDP requests on behalf of applications.
*
* @deprecated in Hummingbird release. Use NeighbourResolutionService instead.
*/
// TODO: move to the peer host package
@Deprecated
public interface ProxyArpService {
/**
......
......@@ -22,7 +22,11 @@ import java.nio.ByteBuffer;
/**
* State distribution mechanism for the proxy ARP service.
*
* @deprecated in Hummingbird release. This is no longer necessary as there are
* other solutions for the problem this was solving.
*/
@Deprecated
public interface ProxyArpStore {
/**
......
......@@ -63,7 +63,12 @@ import static org.onosproject.security.AppGuard.checkPermission;
import static org.slf4j.LoggerFactory.getLogger;
import static org.onosproject.security.AppPermission.Type.*;
/**
* Implementation of the proxy ARP service.
*
* @deprecated in Hummingbird release
*/
@Deprecated
@Component(immediate = true)
@Service
public class ProxyArpManager implements ProxyArpService {
......
......@@ -50,7 +50,11 @@ import static org.onlab.util.Tools.groupedThreads;
/**
* Implementation of proxy ARP distribution mechanism.
*
* @deprecated in Hummingbird release. This is no longer necessary as there are
* other solutions for the problem this was solving.
*/
@Deprecated
@Component(immediate = true)
@Service
public class DistributedProxyArpStore implements ProxyArpStore {
......
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.incubator.net.neighbour;
import org.onosproject.core.ApplicationId;
import org.onosproject.incubator.net.intf.Interface;
/**
* Information about the registration of a neighbour message handler.
*/
public interface NeighbourHandlerRegistration {
/**
* Gets the interface of the registration.
*
* @return interface
*/
Interface intf();
/**
* Gets the neighbour message handler.
*
* @return message handler
*/
NeighbourMessageHandler handler();
/**
* Gets the ID of the application that registered the handler.
*
* @return application ID
*/
ApplicationId appId();
}
......@@ -76,6 +76,16 @@ public interface NeighbourMessageContext {
MacAddress srcMac();
/**
* Gets the destination MAC address of the message.
* <p>
* Only valid for reply packets, will be null for request packets.
* </p>
*
* @return target MAC address
*/
MacAddress dstMac();
/**
* Gets the target IP address of the message.
*
* @return target IP address
......
......@@ -17,9 +17,13 @@
package org.onosproject.incubator.net.neighbour;
import com.google.common.annotations.Beta;
import org.onosproject.core.ApplicationId;
import org.onosproject.incubator.net.intf.Interface;
import org.onosproject.net.ConnectPoint;
import java.util.Collection;
import java.util.Map;
/**
* Provides a means of registering logic for handling neighbour messages.
*/
......@@ -32,8 +36,10 @@ public interface NeighbourResolutionService {
*
* @param connectPoint connect point
* @param handler neighbour message handler
* @param appId application ID
*/
void registerNeighbourHandler(ConnectPoint connectPoint, NeighbourMessageHandler handler);
void registerNeighbourHandler(ConnectPoint connectPoint, NeighbourMessageHandler handler,
ApplicationId appId);
/**
* Registers a neighbour message handler for all neighbour messages incoming
......@@ -42,8 +48,10 @@ public interface NeighbourResolutionService {
*
* @param intf interface
* @param handler neighbour message handler
* @param appId application ID
*/
void registerNeighbourHandler(Interface intf, NeighbourMessageHandler handler);
void registerNeighbourHandler(Interface intf, NeighbourMessageHandler handler,
ApplicationId appId);
/**
* Unregisters a neighbour message handler that was assigned to a connect
......@@ -51,14 +59,34 @@ public interface NeighbourResolutionService {
*
* @param connectPoint connect point
* @param handler neighbour message handler
* @param appId application ID
*/
void unregisterNeighbourHandler(ConnectPoint connectPoint, NeighbourMessageHandler handler);
void unregisterNeighbourHandler(ConnectPoint connectPoint, NeighbourMessageHandler handler,
ApplicationId appId);
/**
* Unregisters a neighbour message handler that was assigned to an interface.
*
* @param intf interface
* @param handler neighbour message handler
* @param appId application ID
*/
void unregisterNeighbourHandler(Interface intf, NeighbourMessageHandler handler,
ApplicationId appId);
/**
* Unregisters all neighbour handlers that were registered by the given
* application.
*
* @param appId application ID
*/
void unregisterNeighbourHandlers(ApplicationId appId);
/**
* Gets the neighbour message handlers that have been registered with the
* service.
*
* @return neighbour message handlers indexed by connect point
*/
void unregisterNeighbourHandler(Interface intf, NeighbourMessageHandler handler);
Map<ConnectPoint, Collection<NeighbourHandlerRegistration>> getHandlerRegistrations();
}
......
......@@ -105,7 +105,12 @@ public class DefaultNeighbourMessageContext implements NeighbourMessageContext {
@Override
public MacAddress srcMac() {
return MacAddress.valueOf(eth.getSourceMACAddress());
return eth.getSourceMAC();
}
@Override
public MacAddress dstMac() {
return eth.getDestinationMAC();
}
@Override
......
......@@ -16,9 +16,10 @@
package org.onosproject.incubator.net.neighbour.impl;
import com.google.common.collect.LinkedListMultimap;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Multimaps;
import com.google.common.collect.SetMultimap;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
......@@ -33,6 +34,7 @@ import org.onlab.packet.ICMP6;
import org.onlab.packet.IPv6;
import org.onlab.packet.Ip4Address;
import org.onlab.packet.Ip6Address;
import org.onlab.packet.IpAddress;
import org.onlab.packet.MacAddress;
import org.onlab.packet.VlanId;
import org.onlab.packet.ndp.NeighborAdvertisement;
......@@ -42,6 +44,7 @@ import org.onosproject.cfg.ComponentConfigService;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
import org.onosproject.incubator.net.intf.Interface;
import org.onosproject.incubator.net.neighbour.NeighbourHandlerRegistration;
import org.onosproject.incubator.net.neighbour.NeighbourMessageActions;
import org.onosproject.incubator.net.neighbour.NeighbourMessageContext;
import org.onosproject.incubator.net.neighbour.NeighbourMessageHandler;
......@@ -63,8 +66,10 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.nio.ByteBuffer;
import java.util.Collection;
import java.util.Dictionary;
import java.util.List;
import java.util.Iterator;
import java.util.Map;
import java.util.Objects;
import static com.google.common.base.Preconditions.checkNotNull;
......@@ -79,7 +84,7 @@ import static org.onosproject.net.packet.PacketPriority.CONTROL;
* Manages handlers for neighbour messages.
*/
@Service
@Component(immediate = true, enabled = false)
@Component(immediate = true)
public class NeighbourPacketManager implements NeighbourResolutionService {
private final Logger log = LoggerFactory.getLogger(getClass());
......@@ -106,8 +111,8 @@ public class NeighbourPacketManager implements NeighbourResolutionService {
private static final String APP_NAME = "org.onosproject.neighbour";
private ApplicationId appId;
private ListMultimap<ConnectPoint, HandlerRegistration> packetHandlers =
Multimaps.synchronizedListMultimap(LinkedListMultimap.create());
private SetMultimap<ConnectPoint, NeighbourHandlerRegistration> packetHandlers =
Multimaps.synchronizedSetMultimap(HashMultimap.create());
private final InternalPacketProcessor processor = new InternalPacketProcessor();
private final InternalNeighbourMessageActions actions = new InternalNeighbourMessageActions();
......@@ -191,23 +196,52 @@ public class NeighbourPacketManager implements NeighbourResolutionService {
}
@Override
public void registerNeighbourHandler(ConnectPoint connectPoint, NeighbourMessageHandler handler) {
packetHandlers.put(connectPoint, new HandlerRegistration(handler));
public void registerNeighbourHandler(ConnectPoint connectPoint,
NeighbourMessageHandler handler,
ApplicationId appId) {
packetHandlers.put(connectPoint, new HandlerRegistration(handler, appId));
}
@Override
public void registerNeighbourHandler(Interface intf, NeighbourMessageHandler handler) {
packetHandlers.put(intf.connectPoint(), new HandlerRegistration(handler, intf));
public void registerNeighbourHandler(Interface intf,
NeighbourMessageHandler handler,
ApplicationId appId) {
packetHandlers.put(intf.connectPoint(),
new HandlerRegistration(handler, intf, appId));
}
@Override
public void unregisterNeighbourHandler(ConnectPoint connectPoint, NeighbourMessageHandler handler) {
packetHandlers.remove(connectPoint, handler);
public void unregisterNeighbourHandler(ConnectPoint connectPoint,
NeighbourMessageHandler handler,
ApplicationId appId) {
packetHandlers.remove(connectPoint, new HandlerRegistration(handler, appId));
}
@Override
public void unregisterNeighbourHandler(Interface intf, NeighbourMessageHandler handler) {
packetHandlers.remove(intf.connectPoint(), handler);
public void unregisterNeighbourHandler(Interface intf,
NeighbourMessageHandler handler,
ApplicationId appId) {
packetHandlers.remove(intf.connectPoint(),
new HandlerRegistration(handler, intf, appId));
}
@Override
public void unregisterNeighbourHandlers(ApplicationId appId) {
synchronized (packetHandlers) {
Iterator<NeighbourHandlerRegistration> it = packetHandlers.values().iterator();
while (it.hasNext()) {
NeighbourHandlerRegistration registration = it.next();
if (registration.appId().equals(appId)) {
it.remove();
}
}
}
}
@Override
public Map<ConnectPoint, Collection<NeighbourHandlerRegistration>> getHandlerRegistrations() {
return ImmutableMap.copyOf(Multimaps.asMap(packetHandlers));
}
public void handlePacket(PacketContext context) {
......@@ -227,7 +261,7 @@ public class NeighbourPacketManager implements NeighbourResolutionService {
}
private void handleMessage(NeighbourMessageContext context) {
List<HandlerRegistration> handlers = packetHandlers.get(context.inPort());
Collection<NeighbourHandlerRegistration> handlers = packetHandlers.get(context.inPort());
handlers.forEach(registration -> {
if (registration.intf() == null || matches(context, registration.intf())) {
......@@ -236,18 +270,53 @@ public class NeighbourPacketManager implements NeighbourResolutionService {
});
}
/**
* Checks that incoming packet matches the parameters of the interface.
* This means that if the interface specifies a particular parameter
* (VLAN, IP address, etc.) then the incoming packet should match those
* parameters.
*
* @param context incoming message context
* @param intf interface to check
* @return true if the incoming message matches the interface, otherwise false
*/
private boolean matches(NeighbourMessageContext context, Interface intf) {
checkNotNull(context);
checkNotNull(intf);
boolean matches = true;
// For non-broadcast packets, if the interface has a MAC address check that
// the destination MAC address of the packet matches the interface MAC
if (!context.dstMac().isBroadcast() &&
!intf.mac().equals(MacAddress.NONE) &&
!intf.mac().equals(context.dstMac())) {
matches = false;
}
// If the interface has a VLAN, check that the packet's VLAN matches
if (!intf.vlan().equals(VlanId.NONE) && !intf.vlan().equals(context.vlan())) {
matches = false;
}
// If the interface has IP addresses, check that the packet's target IP
// address matches one of the interface IP addresses
if (!intf.ipAddressesList().isEmpty() && !hasIp(intf, context.target())) {
matches = false;
}
return matches;
}
/**
* Returns true if the interface has the given IP address.
*
* @param intf interface to check
* @param ip IP address
* @return true if the IP is configured on the interface, otherwise false
*/
private boolean hasIp(Interface intf, IpAddress ip) {
return intf.ipAddressesList().stream()
.anyMatch(intfAddress -> intfAddress.ipAddress().equals(ip));
}
private void reply(NeighbourMessageContext context, MacAddress targetMac) {
switch (context.protocol()) {
......@@ -295,7 +364,7 @@ public class NeighbourPacketManager implements NeighbourResolutionService {
}
/**
* Builds an Neighbor Discovery reply based on a request.
* Builds an NDP reply based on a request.
*
* @param srcIp the IP address to use as the reply source
* @param srcMac the MAC address to use as the reply source
......@@ -336,17 +405,18 @@ public class NeighbourPacketManager implements NeighbourResolutionService {
/**
* Stores a neighbour message handler registration.
*/
private class HandlerRegistration {
private class HandlerRegistration implements NeighbourHandlerRegistration {
private final Interface intf;
private final NeighbourMessageHandler handler;
private final ApplicationId appId;
/**
* Creates a new handler registration.
*
* @param handler neighbour message handler
*/
public HandlerRegistration(NeighbourMessageHandler handler) {
this(handler, null);
public HandlerRegistration(NeighbourMessageHandler handler, ApplicationId appId) {
this(handler, null, appId);
}
/**
......@@ -355,30 +425,28 @@ public class NeighbourPacketManager implements NeighbourResolutionService {
* @param handler neighbour message handler
* @param intf interface
*/
public HandlerRegistration(NeighbourMessageHandler handler, Interface intf) {
public HandlerRegistration(NeighbourMessageHandler handler, Interface intf, ApplicationId appId) {
this.intf = intf;
this.handler = handler;
this.appId = appId;
}
/**
* Gets the interface of the registration.
*
* @return interface
*/
@Override
public Interface intf() {
return intf;
}
/**
* Gets the neighbour message handler.
*
* @return message handler
*/
@Override
public NeighbourMessageHandler handler() {
return handler;
}
@Override
public ApplicationId appId() {
return appId;
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
......@@ -391,12 +459,13 @@ public class NeighbourPacketManager implements NeighbourResolutionService {
HandlerRegistration that = (HandlerRegistration) other;
return Objects.equals(intf, that.intf) &&
Objects.equals(handler, that.handler);
Objects.equals(handler, that.handler) &&
Objects.equals(appId, that.appId);
}
@Override
public int hashCode() {
return Objects.hash(intf, handler);
return Objects.hash(intf, handler, appId);
}
}
......@@ -450,7 +519,7 @@ public class NeighbourPacketManager implements NeighbourResolutionService {
@Override
public void proxy(NeighbourMessageContext context, Interface outIntf) {
// TODO implement
}
@Override
......