Initial skeleton of BgpRouter app.

Added BGP tunnelling and received routes.
Added pushing groups and can now ping through the router.

Change-Id: I21a265bd72e40fc430bd392201fadccbdd67be94
diff --git a/apps/bgprouter/pom.xml b/apps/bgprouter/pom.xml
new file mode 100644
index 0000000..0dbdf31
--- /dev/null
+++ b/apps/bgprouter/pom.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2015 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.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>onos-apps</artifactId>
+        <groupId>org.onosproject</groupId>
+        <version>1.1.0-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>onos-app-bgprouter</artifactId>
+
+    <packaging>bundle</packaging>
+    <description>BGP router application</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-app-routing-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-cli</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.compendium</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.core</artifactId>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/apps/bgprouter/src/main/java/org/onosproject/bgprouter/BgpRouter.java b/apps/bgprouter/src/main/java/org/onosproject/bgprouter/BgpRouter.java
new file mode 100644
index 0000000..22389c3
--- /dev/null
+++ b/apps/bgprouter/src/main/java/org/onosproject/bgprouter/BgpRouter.java
@@ -0,0 +1,227 @@
+/*
+ * Copyright 2015 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.bgprouter;
+
+import com.google.common.collect.ConcurrentHashMultiset;
+import com.google.common.collect.Multiset;
+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.onlab.packet.Ethernet;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.flow.DefaultFlowRule;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.FlowRuleService;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.group.DefaultGroupBucket;
+import org.onosproject.net.group.DefaultGroupDescription;
+import org.onosproject.net.group.Group;
+import org.onosproject.net.group.GroupBucket;
+import org.onosproject.net.group.GroupBuckets;
+import org.onosproject.net.group.GroupDescription;
+import org.onosproject.net.group.GroupKey;
+import org.onosproject.net.group.GroupService;
+import org.onosproject.net.packet.PacketService;
+import org.onosproject.routingapi.FibListener;
+import org.onosproject.routingapi.FibUpdate;
+import org.onosproject.routingapi.RoutingService;
+import org.onosproject.routingapi.config.Interface;
+import org.onosproject.routingapi.config.RoutingConfigurationService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * BgpRouter component.
+ */
+@Component(immediate = true)
+public class BgpRouter {
+
+    private static final Logger log = LoggerFactory.getLogger(BgpRouter.class);
+
+    private static final String BGP_ROUTER_APP = "org.onosproject.bgprouter";
+
+    private static final int PRIORITY = 1;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CoreService coreService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected FlowRuleService flowService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected GroupService groupService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected RoutingService routingService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected RoutingConfigurationService configService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected PacketService packetService;
+
+    private ApplicationId appId;
+
+    private final Multiset<NextHop> nextHops = ConcurrentHashMultiset.create();
+    private final Map<NextHop, NextHopGroupKey> groups = new HashMap<>();
+
+    private DeviceId deviceId = DeviceId.deviceId("of:00000000000000a1"); // TODO config
+
+    private TunnellingConnectivityManager connectivityManager;
+
+    @Activate
+    protected void activate() {
+        log.info("Bgp1Router started");
+        appId = coreService.registerApplication(BGP_ROUTER_APP);
+
+        connectivityManager = new TunnellingConnectivityManager(appId,
+                                                                configService,
+                                                                packetService);
+
+        routingService.start(new InternalFibListener());
+
+        connectivityManager.start();
+
+        log.info("BgpRouter started");
+    }
+
+    @Deactivate
+    protected void deactivate() {
+        routingService.stop();
+        connectivityManager.stop();
+
+        log.info("BgpRouter stopped");
+    }
+
+    private void updateFibEntry(Collection<FibUpdate> updates) {
+        for (FibUpdate update : updates) {
+            NextHop nextHop = new NextHop(update.entry().nextHopIp(),
+                                          update.entry().nextHopMac());
+
+            addNextHop(nextHop);
+
+            TrafficSelector selector = DefaultTrafficSelector.builder()
+                    .matchEthType(Ethernet.TYPE_IPV4)
+                    .matchIPDst(update.entry().prefix())
+                    .build();
+
+            // TODO ensure group exists
+            NextHopGroupKey groupKey = groups.get(nextHop);
+            Group group = groupService.getGroup(deviceId, groupKey);
+            if (group == null) {
+                // TODO handle this
+                log.warn("oops, group {} wasn't there");
+                continue;
+            }
+
+            TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                    .group(group.id())
+                    .build();
+
+            FlowRule flowRule = new DefaultFlowRule(deviceId, selector, treatment,
+                                                    PRIORITY, appId, 0, true,
+                                                    FlowRule.Type.IP);
+
+            flowService.applyFlowRules(flowRule);
+        }
+    }
+
+    private void deleteFibEntry(Collection<FibUpdate> withdraws) {
+        for (FibUpdate update : withdraws) {
+            NextHop nextHop = new NextHop(update.entry().nextHopIp(),
+                                          update.entry().nextHopMac());
+
+            deleteNextHop(nextHop);
+
+            TrafficSelector selector = DefaultTrafficSelector.builder()
+                    .matchIPDst(update.entry().prefix())
+                    .build();
+
+            FlowRule flowRule = new DefaultFlowRule(deviceId, selector, null,
+                                                    PRIORITY, appId, 0, true,
+                                                    FlowRule.Type.IP);
+
+            flowService.removeFlowRules(flowRule);
+        }
+    }
+
+    private void addNextHop(NextHop nextHop) {
+        if (nextHops.add(nextHop, 1) == 0) {
+            // There was no next hop in the multiset
+
+            Interface egressIntf = configService.getMatchingInterface(nextHop.ip());
+            if (egressIntf == null) {
+                log.warn("no egress interface found for {}", nextHop);
+                return;
+            }
+
+            NextHopGroupKey groupKey = new NextHopGroupKey(nextHop.ip());
+            groups.put(nextHop, groupKey);
+
+            TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                    .setEthSrc(egressIntf.mac())
+                    .setEthDst(nextHop.mac())
+                    .setVlanId(egressIntf.vlan())
+                    .setOutput(egressIntf.connectPoint().port())
+                    .build();
+
+            GroupBucket bucket = DefaultGroupBucket.createIndirectGroupBucket(treatment);
+
+            GroupDescription groupDescription
+                    = new DefaultGroupDescription(deviceId,
+                                                  GroupDescription.Type.INDIRECT,
+                                                  new GroupBuckets(Collections
+                                                                           .singletonList(bucket)),
+                                                  groupKey,
+                                                  appId);
+
+            groupService.addGroup(groupDescription);
+        }
+    }
+
+    private void deleteNextHop(NextHop nextHop) {
+        if (nextHops.remove(nextHop, 1) <= 1) {
+            // There was one or less next hops, so there are now none
+
+            log.debug("removing group");
+
+            GroupKey groupKey = groups.remove(nextHop);
+            groupService.removeGroup(deviceId, groupKey, appId);
+        }
+    }
+
+    private class InternalFibListener implements FibListener {
+
+        @Override
+        public void update(Collection<FibUpdate> updates,
+                           Collection<FibUpdate> withdraws) {
+            BgpRouter.this.deleteFibEntry(withdraws);
+            BgpRouter.this.updateFibEntry(updates);
+        }
+    }
+}
diff --git a/apps/bgprouter/src/main/java/org/onosproject/bgprouter/NextHop.java b/apps/bgprouter/src/main/java/org/onosproject/bgprouter/NextHop.java
new file mode 100644
index 0000000..9e39c45
--- /dev/null
+++ b/apps/bgprouter/src/main/java/org/onosproject/bgprouter/NextHop.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2015 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.bgprouter;
+
+import com.google.common.base.MoreObjects;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+
+import java.util.Objects;
+
+/**
+ * Created by jono on 2/12/15.
+ */
+public class NextHop {
+
+    private final IpAddress ip;
+    private final MacAddress mac;
+
+    public NextHop(IpAddress ip, MacAddress mac) {
+        this.ip = ip;
+        this.mac = mac;
+    }
+
+    public IpAddress ip() {
+        return ip;
+    }
+
+    public MacAddress mac() {
+        return mac;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (!(o instanceof NextHop)) {
+            return false;
+        }
+
+        NextHop that = (NextHop) o;
+
+        return Objects.equals(this.ip, that.ip) &&
+                Objects.equals(this.mac, that.mac);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(ip, mac);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(getClass())
+                .add("ip", ip)
+                .add("mac", mac)
+                .toString();
+    }
+}
diff --git a/apps/bgprouter/src/main/java/org/onosproject/bgprouter/NextHopGroupKey.java b/apps/bgprouter/src/main/java/org/onosproject/bgprouter/NextHopGroupKey.java
new file mode 100644
index 0000000..5193f4b
--- /dev/null
+++ b/apps/bgprouter/src/main/java/org/onosproject/bgprouter/NextHopGroupKey.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2015 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.bgprouter;
+
+import com.google.common.base.MoreObjects;
+import org.onlab.packet.IpAddress;
+import org.onosproject.net.group.GroupKey;
+
+import java.util.Objects;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Created by jono on 2/16/15.
+ */
+public class NextHopGroupKey implements GroupKey {
+
+    private final IpAddress address;
+
+    public NextHopGroupKey(IpAddress address) {
+        this.address = checkNotNull(address);
+    }
+
+    public IpAddress address() {
+        return address;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (!(o instanceof NextHopGroupKey)) {
+            return false;
+        }
+
+        NextHopGroupKey that = (NextHopGroupKey) o;
+
+        return Objects.equals(this.address, that.address);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(address);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(getClass())
+                .add("address", address)
+                .toString();
+    }
+}
diff --git a/apps/bgprouter/src/main/java/org/onosproject/bgprouter/TunnellingConnectivityManager.java b/apps/bgprouter/src/main/java/org/onosproject/bgprouter/TunnellingConnectivityManager.java
new file mode 100644
index 0000000..eac5ba9
--- /dev/null
+++ b/apps/bgprouter/src/main/java/org/onosproject/bgprouter/TunnellingConnectivityManager.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2015 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.bgprouter;
+
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.IPv4;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.TCP;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.packet.DefaultOutboundPacket;
+import org.onosproject.net.packet.OutboundPacket;
+import org.onosproject.net.packet.PacketContext;
+import org.onosproject.net.packet.PacketPriority;
+import org.onosproject.net.packet.PacketProcessor;
+import org.onosproject.net.packet.PacketService;
+import org.onosproject.routingapi.config.BgpPeer;
+import org.onosproject.routingapi.config.BgpSpeaker;
+import org.onosproject.routingapi.config.InterfaceAddress;
+import org.onosproject.routingapi.config.RoutingConfigurationService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * Manages connectivity between peers by tunnelling BGP traffic through
+ * OpenFlow packet-ins and packet-outs.
+ */
+public class TunnellingConnectivityManager {
+
+    private static final short BGP_PORT = 179;
+
+    private final ApplicationId appId;
+
+    private final PacketService packetService;
+    private final RoutingConfigurationService configService;
+
+    private final BgpProcessor processor = new BgpProcessor();
+
+    public TunnellingConnectivityManager(ApplicationId appId,
+                                         RoutingConfigurationService configService,
+                                         PacketService packetService) {
+        this.appId = appId;
+        this.configService = configService;
+        this.packetService = packetService;
+    }
+
+    public void start() {
+        packetService.addProcessor(processor, PacketProcessor.ADVISOR_MAX + 3);
+
+        TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
+
+        // Request packets with BGP port as their TCP source port
+        selector.matchEthType(Ethernet.TYPE_IPV4);
+        selector.matchIPProtocol(IPv4.PROTOCOL_TCP);
+        selector.matchTcpSrc(BGP_PORT);
+
+        packetService.requestPackets(selector.build(), PacketPriority.CONTROL,
+                                     appId);
+
+        selector = DefaultTrafficSelector.builder();
+
+        // Request packets with BGP port as their TCP destination port
+        selector.matchEthType(Ethernet.TYPE_IPV4);
+        selector.matchIPProtocol(IPv4.PROTOCOL_TCP);
+        selector.matchTcpDst(BGP_PORT);
+
+        packetService.requestPackets(selector.build(), PacketPriority.CONTROL,
+                                     appId);
+    }
+
+    public void stop() {
+        packetService.removeProcessor(processor);
+        // Should revoke packet requests in the future
+    }
+
+    /**
+     * Forwards a BGP packet to another connect point.
+     *
+     * @param context the packet context of the incoming packet
+     */
+    private void forward(PacketContext context) {
+
+        ConnectPoint outputPort = null;
+        Logger log = LoggerFactory.getLogger(getClass());
+
+
+        IPv4 ipv4 = (IPv4) context.inPacket().parsed().getPayload();
+        IpAddress dstAddress = IpAddress.valueOf(ipv4.getDestinationAddress());
+
+        for (BgpSpeaker speaker : configService.getBgpSpeakers().values()) {
+            if (context.inPacket().receivedFrom().equals(speaker.connectPoint())) {
+                BgpPeer peer = configService.getBgpPeers().get(dstAddress);
+                if (peer != null) {
+                    outputPort = peer.connectPoint();
+                }
+                break;
+            }
+            for (InterfaceAddress addr : speaker.interfaceAddresses()) {
+                if (addr.ipAddress().equals(dstAddress) && !context.inPacket()
+                        .receivedFrom().equals(speaker.connectPoint())) {
+                    outputPort = speaker.connectPoint();
+                }
+            }
+        }
+
+        if (outputPort != null) {
+            TrafficTreatment t = DefaultTrafficTreatment.builder()
+                    .setOutput(outputPort.port()).build();
+            OutboundPacket o = new DefaultOutboundPacket(
+                    outputPort.deviceId(), t, context.inPacket().unparsed());
+            packetService.emit(o);
+        }
+    }
+
+    /**
+     * Packet processor responsible receiving and filtering BGP packets.
+     */
+    private class BgpProcessor implements PacketProcessor {
+
+        @Override
+        public void process(PacketContext context) {
+            // Stop processing if the packet has been handled, since we
+            // can't do any more to it.
+            if (context.isHandled()) {
+                return;
+            }
+
+            Ethernet packet = context.inPacket().parsed();
+
+            if (packet == null) {
+                return;
+            }
+
+            if (packet.getEtherType() == Ethernet.TYPE_IPV4) {
+                IPv4 ipv4Packet = (IPv4) packet.getPayload();
+                if (ipv4Packet.getProtocol() == IPv4.PROTOCOL_TCP) {
+                    TCP tcpPacket = (TCP) ipv4Packet.getPayload();
+
+                    if (tcpPacket.getDestinationPort() == BGP_PORT ||
+                            tcpPacket.getSourcePort() == BGP_PORT) {
+                        forward(context);
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/apps/pom.xml b/apps/pom.xml
index 1f6bac8..167e64c 100644
--- a/apps/pom.xml
+++ b/apps/pom.xml
@@ -47,6 +47,7 @@
         <module>election</module>
         <module>routing</module>
         <module>routing-api</module>
+        <module>bgprouter</module>
     </modules>
 
     <properties>
diff --git a/apps/routing/pom.xml b/apps/routing/pom.xml
index 055d0b8..e05bed1 100644
--- a/apps/routing/pom.xml
+++ b/apps/routing/pom.xml
@@ -43,7 +43,7 @@
             <version>${project.version}</version>
         </dependency>
 
-	<dependency>
+	    <dependency>
             <groupId>org.onosproject</groupId>
             <artifactId>onlab-thirdparty</artifactId>
             <version>${project.version}</version>
diff --git a/features/features.xml b/features/features.xml
index aed7d7c..23e833a 100644
--- a/features/features.xml
+++ b/features/features.xml
@@ -215,6 +215,16 @@
         <bundle>mvn:org.onosproject/onos-app-routing/@ONOS-VERSION</bundle>
     </feature>
 
+    <feature name="onos-app-bgprouter" version="@FEATURE-VERSION"
+             description="BGP router application">
+        <feature>onos-api</feature>
+        <feature>onos-app-proxyarp</feature>
+        <feature>onos-app-config</feature>
+        <bundle>mvn:org.onosproject/onos-app-bgprouter/@ONOS-VERSION</bundle>
+	<bundle>mvn:org.onosproject/onos-app-routing-api/@ONOS-VERSION</bundle>
+	<bundle>mvn:org.onosproject/onos-app-routing/@ONOS-VERSION</bundle>
+    </feature>
+
     <feature name="onos-app-calendar" version="@FEATURE-VERSION"
              description="REST interface for scheduling intents from an external calendar">
         <feature>onos-api</feature>