Implemented SDN-IP/BgpRouter neighbour handling using NeighbourResolutionService

Change-Id: Ia7e3f18bd69d56cb2f46f815eaa2352533a54ed0
diff --git a/apps/bgprouter/BUCK b/apps/bgprouter/BUCK
index 62ec78e..0e1870f 100644
--- a/apps/bgprouter/BUCK
+++ b/apps/bgprouter/BUCK
@@ -19,6 +19,5 @@
     category = 'Traffic Steering',
     url = 'http://onosproject.org',
     description = 'BGP router application.',
-    required_apps = [ 'org.onosproject.proxyarp' ],
     included_bundles = BUNDLES,
 )
diff --git a/apps/bgprouter/app.xml b/apps/bgprouter/app.xml
index 2b5c117..91e010a 100644
--- a/apps/bgprouter/app.xml
+++ b/apps/bgprouter/app.xml
@@ -22,5 +22,4 @@
     <artifact>mvn:${project.groupId}/${project.artifactId}/${project.version}</artifact>
     <artifact>mvn:${project.groupId}/onos-app-routing-api/${project.version}</artifact>
     <artifact>mvn:${project.groupId}/onos-app-routing/${project.version}</artifact>
-    <artifact>mvn:${project.groupId}/onos-app-proxyarp/${project.version}</artifact>
 </app>
diff --git a/apps/bgprouter/src/main/java/org/onosproject/bgprouter/BgpRouter.java b/apps/bgprouter/src/main/java/org/onosproject/bgprouter/BgpRouter.java
index 0ae6fa8..e6941c3 100644
--- a/apps/bgprouter/src/main/java/org/onosproject/bgprouter/BgpRouter.java
+++ b/apps/bgprouter/src/main/java/org/onosproject/bgprouter/BgpRouter.java
@@ -95,6 +95,7 @@
     static {
         components.add("org.onosproject.routing.bgp.BgpSessionManager");
         components.add("org.onosproject.routing.impl.Router");
+        components.add("org.onosproject.routing.impl.BgpSpeakerNeighbourHandler");
         components.add("org.onosproject.routing.impl.SingleSwitchFibInstaller");
     }
 
diff --git a/apps/routing/src/main/java/org/onosproject/routing/impl/BgpSpeakerNeighbourHandler.java b/apps/routing/src/main/java/org/onosproject/routing/impl/BgpSpeakerNeighbourHandler.java
new file mode 100644
index 0000000..aeaf13d
--- /dev/null
+++ b/apps/routing/src/main/java/org/onosproject/routing/impl/BgpSpeakerNeighbourHandler.java
@@ -0,0 +1,230 @@
+/*
+ * 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.routing.impl;
+
+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.intf.Interface;
+import org.onosproject.incubator.net.intf.InterfaceEvent;
+import org.onosproject.incubator.net.intf.InterfaceListener;
+import org.onosproject.incubator.net.intf.InterfaceService;
+import org.onosproject.incubator.net.neighbour.NeighbourMessageContext;
+import org.onosproject.incubator.net.neighbour.NeighbourMessageHandler;
+import org.onosproject.incubator.net.neighbour.NeighbourResolutionService;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.Host;
+import org.onosproject.net.config.NetworkConfigEvent;
+import org.onosproject.net.config.NetworkConfigListener;
+import org.onosproject.net.config.NetworkConfigService;
+import org.onosproject.net.host.HostService;
+import org.onosproject.routing.RoutingService;
+import org.onosproject.routing.config.BgpConfig;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import static org.onosproject.net.HostId.hostId;
+
+/**
+ * Manages neighbour message handlers for the use case of internal BGP speakers
+ * connected to the network at some point that are exchanging neighbour
+ * resolution messages with external routers that are connected behind interfaces.
+ * <p>
+ * For each internal speaker port we use a handler that proxies packets from
+ * that port to the appropriate external-facing interface port.
+ * For each external interface, we use a handler that responds to requests based
+ * on the interface configuration and proxies replies back the the internal BGP
+ * speaker.
+ * </p>
+ */
+@Component(immediate = true, enabled = false)
+public class BgpSpeakerNeighbourHandler {
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CoreService coreService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected NetworkConfigService configService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected InterfaceService interfaceService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected NeighbourResolutionService neighbourService;
+
+    private ApplicationId appId;
+
+    private Set<ConnectPoint> speakerConnectPoints = new HashSet<>();
+
+
+    private InternalNetworkConfigListener configListener = new InternalNetworkConfigListener();
+    private InternalInterfaceListener interfaceListener = new InternalInterfaceListener();
+
+    private ExternalInterfaceNeighbourHandler externalHandler = new ExternalInterfaceNeighbourHandler();
+    private InternalSpeakerNeighbourHandler internalHandler = new InternalSpeakerNeighbourHandler();
+
+    @Activate
+    protected void activate() {
+        appId = coreService.registerApplication(RoutingService.ROUTER_APP_ID);
+        configService.addListener(configListener);
+        interfaceService.addListener(interfaceListener);
+
+        interfaceService.getInterfaces().forEach(
+                intf -> neighbourService.registerNeighbourHandler(intf, externalHandler, appId));
+        configureSpeakerHandlers();
+    }
+
+    @Deactivate
+    protected void deactivate() {
+        configService.removeListener(configListener);
+        interfaceService.removeListener(interfaceListener);
+
+        neighbourService.unregisterNeighbourHandlers(appId);
+    }
+
+    private void configureSpeakerHandlers() {
+        BgpConfig config = configService.getConfig(appId, RoutingService.CONFIG_CLASS);
+
+        if (config == null) {
+            return;
+        }
+
+        speakerConnectPoints.forEach(
+                cp -> neighbourService.unregisterNeighbourHandler(cp, internalHandler, appId));
+        speakerConnectPoints.clear();
+
+        config.bgpSpeakers().forEach(speaker -> {
+            neighbourService.registerNeighbourHandler(speaker.connectPoint(), internalHandler, appId);
+            speakerConnectPoints.add(speaker.connectPoint());
+        });
+    }
+
+    private void updateInterface(Interface intf) {
+        // Only use interfaces that have an IP address
+        if (!intf.ipAddresses().isEmpty()) {
+            neighbourService.registerNeighbourHandler(intf, externalHandler, appId);
+        }
+    }
+
+    private void removeInterface(Interface intf) {
+        neighbourService.unregisterNeighbourHandler(intf, externalHandler, appId);
+    }
+
+    /**
+     * Neighbour message handler for external facing ports that have interface
+     * configuration.
+     */
+    public class ExternalInterfaceNeighbourHandler implements
+            NeighbourMessageHandler {
+
+        @Override
+        public void handleMessage(NeighbourMessageContext context, HostService hostService) {
+            switch (context.type()) {
+            case REQUEST:
+                // Reply to requests that target our configured interface IP
+                // address on this port. Drop all other requests.
+
+                interfaceService.getInterfacesByPort(context.inPort())
+                        .stream()
+                        .filter(intf -> intf.ipAddresses()
+                                .stream()
+                                .anyMatch(ia -> ia.ipAddress().equals(context.target())))
+                        .forEach(intf -> context.reply(intf.mac()));
+
+                break;
+            case REPLY:
+                // Proxy replies over to our internal BGP speaker if the host
+                // is known to us
+                Host h = hostService.getHost(hostId(context.dstMac(), context.vlan()));
+
+                if (h == null) {
+                    context.drop();
+                } else {
+                    context.proxy(h.location());
+                }
+                break;
+            default:
+                break;
+            }
+        }
+
+    }
+
+    /**
+     * Neighbour message handler for ports connected to the internal BGP speakers.
+     */
+    private class InternalSpeakerNeighbourHandler implements
+            NeighbourMessageHandler {
+        @Override
+        public void handleMessage(NeighbourMessageContext context, HostService hostService) {
+            // For messages coming from a BGP speaker, look at the sender address
+            // to find the interface to proxy to
+            interfaceService.getInterfacesByIp(context.sender())
+                    .stream()
+                    .filter(intf -> intf.vlan().equals(context.vlan()))
+                    .map(intf -> intf.connectPoint())
+                    .forEach(context::proxy);
+        }
+    }
+
+    private class InternalNetworkConfigListener implements
+            NetworkConfigListener {
+
+        @Override
+        public void event(NetworkConfigEvent event) {
+            switch (event.type()) {
+            case CONFIG_REGISTERED:
+                break;
+            case CONFIG_UNREGISTERED:
+                break;
+            case CONFIG_ADDED:
+            case CONFIG_UPDATED:
+            case CONFIG_REMOVED:
+                if (event.configClass() == RoutingService.CONFIG_CLASS) {
+                    configureSpeakerHandlers();
+                }
+                break;
+            default:
+                break;
+            }
+        }
+    }
+
+    private class InternalInterfaceListener implements InterfaceListener {
+
+        @Override
+        public void event(InterfaceEvent event) {
+            switch (event.type()) {
+            case INTERFACE_ADDED:
+                updateInterface(event.subject());
+                break;
+            case INTERFACE_UPDATED:
+                break;
+            case INTERFACE_REMOVED:
+                removeInterface(event.subject());
+                break;
+            default:
+                break;
+            }
+        }
+    }
+}
diff --git a/apps/sdnip/BUCK b/apps/sdnip/BUCK
index b15d478..d934d74 100644
--- a/apps/sdnip/BUCK
+++ b/apps/sdnip/BUCK
@@ -26,6 +26,5 @@
   category = 'Utility',
   url = 'http://onosproject.org',
   included_bundles = BUNDLES,
-  required_apps = [ 'org.onosproject.proxyarp' ],
   description = 'SDN-IP peering application',
 )
diff --git a/apps/sdnip/app.xml b/apps/sdnip/app.xml
index 1a803e2..4bea73c 100644
--- a/apps/sdnip/app.xml
+++ b/apps/sdnip/app.xml
@@ -17,7 +17,7 @@
 <app name="org.onosproject.sdnip" origin="ON.Lab" version="${project.version}"
      category="Traffic Steering" url="http://onosproject.org" title="SDN-IP App"
      featuresRepo="mvn:${project.groupId}/${project.artifactId}/${project.version}/xml/features"
-     features="${project.artifactId}" apps="org.onosproject.proxyarp">
+     features="${project.artifactId}">
     <description>${project.description}</description>
     <artifact>mvn:${project.groupId}/${project.artifactId}/${project.version}</artifact>
     <artifact>mvn:${project.groupId}/onos-app-routing-api/${project.version}</artifact>
diff --git a/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIp.java b/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIp.java
index 201a909..643bb2b 100644
--- a/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIp.java
+++ b/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIp.java
@@ -68,6 +68,7 @@
 
     private final List<String> components = ImmutableList.of(
             "org.onosproject.routing.bgp.BgpSessionManager",
+            "org.onosproject.routing.impl.BgpSpeakerNeighbourHandler",
             org.onosproject.sdnip.SdnIpFib.class.getName()
     );