Adding BGP provider code to support EVPN

Change-Id: Ic5508749b64a47a70f1aabd9324e9f89e85fa39f
diff --git a/providers/bgp/route/BUCK b/providers/bgp/route/BUCK
new file mode 100755
index 0000000..bf38465
--- /dev/null
+++ b/providers/bgp/route/BUCK
@@ -0,0 +1,16 @@
+COMPILE_DEPS = [
+  '//lib:CORE_DEPS',
+  '//protocols/bgp/api:onos-protocols-bgp-api',
+  '//protocols/bgp/bgpio:onos-protocols-bgp-bgpio',
+  '//incubator/store:onos-incubator-store',
+  '//incubator/api:onos-incubator-api',
+]
+
+TEST_DEPS = [
+  '//lib:TEST_ADAPTERS',
+]
+
+osgi_jar_with_tests(
+  deps = COMPILE_DEPS,
+  test_deps = TEST_DEPS,
+)
diff --git a/providers/bgp/route/pom.xml b/providers/bgp/route/pom.xml
new file mode 100755
index 0000000..e3bffcd
--- /dev/null
+++ b/providers/bgp/route/pom.xml
@@ -0,0 +1,50 @@
+<!--
+  ~ Copyright 2017-present Open Networking Foundation
+  ~
+  ~ 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">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.onosproject</groupId>
+        <artifactId>onos-bgp-providers</artifactId>
+        <version>1.11.0-SNAPSHOT</version>
+    </parent>
+    <artifactId>onos-bgp-provider-route</artifactId>
+    <packaging>bundle</packaging>
+    <description>BGP route provider</description>
+    <dependencies>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-bgp-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-incubator-api</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+            <classifier>tests</classifier>
+        </dependency>
+        <dependency>
+            <groupId>org.easymock</groupId>
+            <artifactId>easymock</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-bgp-api</artifactId>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/providers/bgp/route/src/main/java/org/onosproject/provider/bgp/route/impl/BgpRouteProvider.java b/providers/bgp/route/src/main/java/org/onosproject/provider/bgp/route/impl/BgpRouteProvider.java
new file mode 100755
index 0000000..81bd1d5
--- /dev/null
+++ b/providers/bgp/route/src/main/java/org/onosproject/provider/bgp/route/impl/BgpRouteProvider.java
@@ -0,0 +1,394 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.provider.bgp.route.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.onlab.packet.Ip4Address;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.MacAddress;
+import org.onosproject.bgp.controller.BgpController;
+import org.onosproject.bgp.controller.BgpId;
+import org.onosproject.bgp.controller.BgpPeer.FlowSpecOperation;
+import org.onosproject.bgp.controller.BgpRouteListener;
+import org.onosproject.bgpio.protocol.BgpEvpnNlri;
+import org.onosproject.bgpio.protocol.BgpUpdateMsg;
+import org.onosproject.bgpio.protocol.evpn.BgpEvpnNlriImpl;
+import org.onosproject.bgpio.protocol.evpn.BgpEvpnRouteType;
+import org.onosproject.bgpio.protocol.evpn.BgpEvpnRouteType2Nlri;
+import org.onosproject.bgpio.types.BgpEvpnEsi;
+import org.onosproject.bgpio.types.BgpEvpnLabel;
+import org.onosproject.bgpio.types.BgpExtendedCommunity;
+import org.onosproject.bgpio.types.BgpNlriType;
+import org.onosproject.bgpio.types.BgpValueType;
+import org.onosproject.bgpio.types.MpReachNlri;
+import org.onosproject.bgpio.types.MpUnReachNlri;
+import org.onosproject.bgpio.types.RouteDistinguisher;
+import org.onosproject.bgpio.types.RouteTarget;
+import org.onosproject.incubator.net.routing.EvpnRoute;
+import org.onosproject.incubator.net.routing.EvpnRoute.Source;
+import org.onosproject.incubator.net.routing.EvpnRouteAdminService;
+import org.onosproject.incubator.net.routing.EvpnRouteEvent;
+import org.onosproject.incubator.net.routing.EvpnRouteListener;
+import org.onosproject.incubator.net.routing.VpnRouteTarget;
+import org.onosproject.net.provider.AbstractProvider;
+import org.onosproject.net.provider.ProviderId;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * Provider which uses an BGP controller to update/delete route.
+ */
+@Component(immediate = true)
+public class BgpRouteProvider extends AbstractProvider {
+
+    /**
+     * Creates an instance of BGP route provider.
+     */
+    public BgpRouteProvider() {
+        super(new ProviderId("route",
+                             "org.onosproject.provider.bgp.route.impl"));
+    }
+
+    private static final Logger log = LoggerFactory
+            .getLogger(BgpRouteProvider.class);
+
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected BgpController controller;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected EvpnRouteAdminService evpnRouteAdminService;
+
+    private final InternalEvpnRouteListener routeListener = new
+            InternalEvpnRouteListener();
+    private final InternalBgpRouteListener bgpRouteListener = new
+            InternalBgpRouteListener();
+
+
+    @Activate
+    public void activate() {
+        controller.addRouteListener(bgpRouteListener);
+        evpnRouteAdminService.addListener(routeListener);
+        log.info("Started");
+    }
+
+    @Deactivate
+    public void deactivate() {
+        controller.removeRouteListener(bgpRouteListener);
+        evpnRouteAdminService.removeListener(routeListener);
+        log.info("Stopped");
+    }
+
+    /**
+     * Handles the bgp route update message.
+     *
+     * @param operationType operationType
+     * @param rdString      rd
+     * @param exportRtList  rt export
+     * @param nextHop       next hop
+     * @param macAddress    mac address
+     * @param ipAddress     ip address
+     * @param labelInt      label
+     */
+    private void sendUpdateMessage(FlowSpecOperation operationType,
+                                   String rdString,
+                                   List<VpnRouteTarget> exportRtList,
+                                   IpAddress nextHop,
+                                   MacAddress macAddress,
+                                   InetAddress ipAddress,
+                                   int labelInt) {
+        log.info("sendUpdateMessage 1");
+
+        List<BgpEvpnNlri> eVpnNlri = new ArrayList<BgpEvpnNlri>();
+        RouteDistinguisher rd = stringToRD(rdString);
+        BgpEvpnEsi esi = new BgpEvpnEsi(new byte[10]);
+        int ethernetTagID = 0;
+        BgpEvpnLabel mplsLabel1 = intToLabel(labelInt);
+        BgpEvpnLabel mplsLabel2 = null;
+
+        List<BgpValueType> extCom = new ArrayList<BgpValueType>();
+        if ((operationType == FlowSpecOperation.UPDATE)
+                && (!exportRtList.isEmpty())) {
+            for (VpnRouteTarget rt : exportRtList) {
+                RouteTarget rTarget = stringToRT(rt.getRouteTarget());
+                extCom.add(rTarget);
+            }
+        }
+        BgpEvpnRouteType2Nlri routeTypeSpec =
+                new BgpEvpnRouteType2Nlri(rd,
+                                          esi,
+                                          ethernetTagID,
+                                          macAddress,
+                                          ipAddress,
+                                          mplsLabel1,
+                                          mplsLabel2);
+        BgpEvpnNlri nlri = new BgpEvpnNlriImpl(BgpEvpnRouteType
+                                                       .MAC_IP_ADVERTISEMENT
+                                                       .getType(),
+                                               routeTypeSpec);
+        eVpnNlri.add(nlri);
+        log.info("sendUpdateMessage 2");
+        controller.getPeers().forEach(peer -> {
+            log.info("Send route update eVpnComponents {} to peer {}",
+                     eVpnNlri, peer);
+            peer.updateEvpnNlri(operationType, nextHop, extCom, eVpnNlri);
+        });
+
+    }
+
+    private static RouteDistinguisher stringToRD(String rdString) {
+        if (rdString.contains(":")) {
+            if ((rdString.indexOf(":") != 0)
+                    && (rdString.indexOf(":") != rdString.length() - 1)) {
+                String[] tem = rdString.split(":");
+                short as = (short) Integer.parseInt(tem[0]);
+                int assignednum = Integer.parseInt(tem[1]);
+                long rd = ((long) assignednum & 0xFFFFFFFFL)
+                        | (((long) as << 32) & 0xFFFFFFFF00000000L);
+                return new RouteDistinguisher(rd);
+            }
+        }
+        return null;
+
+    }
+
+    private static String rdToString(RouteDistinguisher rd) {
+        long rdLong = rd.getRouteDistinguisher();
+        int as = (int) ((rdLong & 0xFFFFFFFF00000000L) >> 32);
+        int assignednum = (int) (rdLong & 0xFFFFFFFFL);
+        String result = as + ":" + assignednum;
+        return result;
+    }
+
+    private static RouteTarget stringToRT(String rdString) {
+        if (rdString.contains(":")) {
+            if ((rdString.indexOf(":") != 0)
+                    && (rdString.indexOf(":") != rdString.length() - 1)) {
+                String[] tem = rdString.split(":");
+                short as = Short.parseShort(tem[0]);
+                int assignednum = Integer.parseInt(tem[1]);
+
+                byte[] rt = new byte[]{(byte) ((as >> 8) & 0xFF),
+                        (byte) (as & 0xFF),
+                        (byte) ((assignednum >> 24) & 0xFF),
+                        (byte) ((assignednum >> 16) & 0xFF),
+                        (byte) ((assignednum >> 8) & 0xFF),
+                        (byte) (assignednum & 0xFF)};
+                short type = 0x02;
+                return new RouteTarget(type, rt);
+            }
+        }
+        return null;
+
+    }
+
+    private static String rtToString(RouteTarget rt) {
+        byte[] b = rt.getRouteTarget();
+
+        int assignednum = b[5] & 0xFF | (b[4] & 0xFF) << 8 | (b[3] & 0xFF) << 16
+                | (b[2] & 0xFF) << 24;
+        short as = (short) (b[1] & 0xFF | (b[0] & 0xFF) << 8);
+        String result = as + ":" + assignednum;
+        return result;
+    }
+
+    private static BgpEvpnLabel intToLabel(int labelInt) {
+        byte[] label = new byte[]{(byte) ((labelInt >> 16) & 0xFF),
+                (byte) ((labelInt >> 8) & 0xFF),
+                (byte) (labelInt & 0xFF)};
+
+        return new BgpEvpnLabel(label);
+    }
+
+    private static int labelToInt(BgpEvpnLabel label) {
+        byte[] b = label.getMplsLabel();
+        return b[2] & 0xFF | (b[1] & 0xFF) << 8 | (b[0] & 0xFF) << 16;
+
+    }
+
+    private class InternalBgpRouteListener implements BgpRouteListener {
+
+        @Override
+        public void processRoute(BgpId bgpId, BgpUpdateMsg updateMsg) {
+            log.info("Evpn route event received from BGP protocol");
+            List<BgpValueType> pathAttr = updateMsg.bgpPathAttributes()
+                    .pathAttributes();
+            Iterator<BgpValueType> iterator = pathAttr.iterator();
+            RouteTarget rt = null;
+            List<VpnRouteTarget> exportRt = new LinkedList<>();
+            List<BgpEvpnNlri> evpnReachNlri = new LinkedList<>();
+            List<BgpEvpnNlri> evpnUnreachNlri = new LinkedList<>();
+
+            Ip4Address ipNextHop = null;
+            while (iterator.hasNext()) {
+                BgpValueType attr = iterator.next();
+                if (attr instanceof MpReachNlri) {
+                    MpReachNlri mpReachNlri = (MpReachNlri) attr;
+                    ipNextHop = mpReachNlri.nexthop4();
+                    if (mpReachNlri
+                            .getNlriDetailsType() == BgpNlriType.EVPN) {
+                        evpnReachNlri.addAll(mpReachNlri.bgpEvpnNlri());
+                    }
+
+                }
+                if (attr instanceof MpUnReachNlri) {
+                    MpUnReachNlri mpUnReachNlri = (MpUnReachNlri) attr;
+                    if (mpUnReachNlri
+                            .getNlriDetailsType() == BgpNlriType.EVPN) {
+                        evpnUnreachNlri.addAll(mpUnReachNlri.bgpEvpnNlri());
+                    }
+                }
+
+                if (attr instanceof BgpExtendedCommunity) {
+                    BgpExtendedCommunity extCom = (BgpExtendedCommunity) attr;
+                    Iterator<BgpValueType> extIte = extCom.fsActionTlv()
+                            .iterator();
+                    while (extIte.hasNext()) {
+                        BgpValueType extAttr = extIte.next();
+                        if (extAttr instanceof RouteTarget) {
+                            rt = (RouteTarget) extAttr;
+                            exportRt.add(VpnRouteTarget
+                                                 .routeTarget(rtToString(rt)));
+                            break;
+                        }
+                    }
+                }
+            }
+
+            if ((!exportRt.isEmpty()) && (!evpnReachNlri.isEmpty())) {
+                for (BgpEvpnNlri nlri : evpnReachNlri) {
+                    if (nlri.getRouteType() == BgpEvpnRouteType
+                            .MAC_IP_ADVERTISEMENT) {
+                        BgpEvpnRouteType2Nlri macIpAdvNlri
+                                = (BgpEvpnRouteType2Nlri) nlri
+                                .getNlri();
+                        MacAddress macAddress = macIpAdvNlri.getMacAddress();
+                        Ip4Address ipAddress = Ip4Address
+                                .valueOf(macIpAdvNlri.getIpAddress());
+                        RouteDistinguisher rd = macIpAdvNlri
+                                .getRouteDistinguisher();
+                        BgpEvpnLabel label = macIpAdvNlri.getMplsLable1();
+                        log.info("Route Provider received bgp packet {} " +
+                                         "to route system.",
+                                 macIpAdvNlri.toString());
+                        // Add route to route system
+                        Source source = Source.REMOTE;
+                        EvpnRoute evpnRoute = new EvpnRoute(source,
+                                                            macAddress,
+                                                            IpPrefix.valueOf(ipAddress, 32),
+                                                            ipNextHop,
+                                                            rdToString(rd),
+                                                            null, //empty rt
+                                                            exportRt,
+                                                            labelToInt(label));
+
+                        evpnRouteAdminService.update(Collections
+                                                             .singleton(evpnRoute));
+                    }
+                }
+            }
+
+            if (!evpnUnreachNlri.isEmpty()) {
+                for (BgpEvpnNlri nlri : evpnUnreachNlri) {
+                    if (nlri.getRouteType() == BgpEvpnRouteType
+                            .MAC_IP_ADVERTISEMENT) {
+                        BgpEvpnRouteType2Nlri macIpAdvNlri
+                                = (BgpEvpnRouteType2Nlri) nlri
+                                .getNlri();
+                        MacAddress macAddress = macIpAdvNlri.getMacAddress();
+                        Ip4Address ipAddress = Ip4Address
+                                .valueOf(macIpAdvNlri.getIpAddress());
+                        RouteDistinguisher rd = macIpAdvNlri
+                                .getRouteDistinguisher();
+                        BgpEvpnLabel label = macIpAdvNlri.getMplsLable1();
+                        log.info("Route Provider received bgp packet {} " +
+                                         "and remove from route system.",
+                                 macIpAdvNlri.toString());
+                        // Delete route from route system
+                        Source source = Source.REMOTE;
+                        // For mpUnreachNlri, nexthop and rt is null
+                        EvpnRoute evpnRoute = new EvpnRoute(source,
+                                                            macAddress,
+                                                            IpPrefix.valueOf(ipAddress, 32),
+                                                            null,
+                                                            rdToString(rd),
+                                                            null,
+                                                            null,
+                                                            labelToInt(label));
+
+                        evpnRouteAdminService.withdraw(Collections
+                                                               .singleton(evpnRoute));
+                    }
+                }
+            }
+        }
+    }
+
+    private class InternalEvpnRouteListener implements EvpnRouteListener {
+
+        @Override
+        public void event(EvpnRouteEvent event) {
+            log.info("evpnroute event is received from evpn route manager");
+            FlowSpecOperation operationType = null;
+            EvpnRoute route = event.subject();
+            EvpnRoute evpnRoute = route;
+            log.info("Event received for public route {}", evpnRoute);
+            if (evpnRoute.source().equals(Source.REMOTE)) {
+                return;
+            }
+            switch (event.type()) {
+                case ROUTE_ADDED:
+                case ROUTE_UPDATED:
+                    log.info("route added");
+                    operationType = FlowSpecOperation.UPDATE;
+                    break;
+                case ROUTE_REMOVED:
+                    log.info("route deleted");
+                    operationType = FlowSpecOperation.DELETE;
+                    break;
+                default:
+                    break;
+            }
+
+            String rdString = evpnRoute.routeDistinguisher()
+                    .getRouteDistinguisher();
+            MacAddress macAddress = evpnRoute.prefixMac();
+            InetAddress inetAddress = evpnRoute.prefixIp().address().toInetAddress();
+            IpAddress nextHop = evpnRoute.ipNextHop();
+            List<VpnRouteTarget> exportRtList = evpnRoute
+                    .exportRouteTarget();
+            int labelInt = evpnRoute.label().getLabel();
+
+            sendUpdateMessage(operationType,
+                              rdString,
+                              exportRtList,
+                              nextHop,
+                              macAddress,
+                              inetAddress,
+                              labelInt);
+        }
+    }
+}
\ No newline at end of file
diff --git a/providers/bgp/route/src/main/java/org/onosproject/provider/bgp/route/impl/package-info.java b/providers/bgp/route/src/main/java/org/onosproject/provider/bgp/route/impl/package-info.java
new file mode 100755
index 0000000..97f14ab
--- /dev/null
+++ b/providers/bgp/route/src/main/java/org/onosproject/provider/bgp/route/impl/package-info.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.
+ */
+/**
+ * Provider that uses BGP controller as a means of infrastructure route exchange.
+ */
+package org.onosproject.provider.bgp.route.impl;
\ No newline at end of file