Castor app - Needs final review and merge

Change-Id: Ieb32596216ac848e9661c0785427bfe96fb958c6
diff --git a/apps/castor/BUCK b/apps/castor/BUCK
new file mode 100644
index 0000000..1e2cd89
--- /dev/null
+++ b/apps/castor/BUCK
@@ -0,0 +1,40 @@
+COMPILE_DEPS = [
+  '//lib:CORE_DEPS',
+  '//core/api:onos-api',
+  '//lib:javax.ws.rs-api',
+  '//lib:jersey-server',
+  '//utils/rest:onlab-rest',
+  '//core/store/serializers:onos-core-serializers',
+  '//incubator/api:onos-incubator-api',
+  '//apps/routing-api:onos-apps-routing-api',
+]
+
+BUNDLES = [
+  '//apps/castor:onos-apps-castor',
+  '//apps/routing-api:onos-apps-routing-api',
+  '//apps/routing:onos-apps-routing',
+]
+
+TEST_DEPS = [
+  '//lib:TEST_ADAPTERS',
+  '//incubator/api:onos-incubator-api-tests',
+  '//apps/routing-api:onos-apps-routing-api-tests',
+]
+
+osgi_jar_with_tests (
+  deps = COMPILE_DEPS,
+  test_deps = TEST_DEPS,
+  web_context = '/onos/castor',
+  api_title = 'Castor App',
+  api_version = '1.0',
+  api_description = 'REST API for Castor App',
+  api_package = 'org.onosproject.castor',
+)
+
+onos_app (
+  title = 'Castor App',
+  category = 'Utility',
+  url = 'http://onosproject.org',
+  included_bundles = BUNDLES,
+  description = 'Castor application',
+)
\ No newline at end of file
diff --git a/apps/castor/app.xml b/apps/castor/app.xml
new file mode 100644
index 0000000..9ad2603
--- /dev/null
+++ b/apps/castor/app.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ 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.
+  -->
+<app name="org.onosproject.castor" origin="ON.Lab" version="${project.version}"
+     category="default" url="http://onosproject.org"
+     featuresRepo="mvn:${project.groupId}/${project.artifactId}/${project.version}/xml/features"
+     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>
+    <artifact>mvn:${project.groupId}/onos-app-routing/${project.version}</artifact>
+</app>
diff --git a/apps/castor/features.xml b/apps/castor/features.xml
new file mode 100644
index 0000000..5046699
--- /dev/null
+++ b/apps/castor/features.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!--
+  ~ 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.
+  -->
+<features xmlns="http://karaf.apache.org/xmlns/features/v1.2.0" name="${project.artifactId}-${project.version}">
+    <feature name="onos-app-castor" version="${project.version}"
+             description="${project.description}">
+        <feature>onos-api</feature>
+        <bundle>mvn:${project.groupId}/onos-app-castor/${project.version}</bundle>
+        <bundle>mvn:${project.groupId}/onos-app-routing-api/${project.version}</bundle>
+        <bundle>mvn:${project.groupId}/onos-app-routing/${project.version}</bundle>
+    </feature>
+</features>
diff --git a/apps/castor/src/main/java/org/onosproject/castor/ArpService.java b/apps/castor/src/main/java/org/onosproject/castor/ArpService.java
new file mode 100644
index 0000000..3df12f8
--- /dev/null
+++ b/apps/castor/src/main/java/org/onosproject/castor/ArpService.java
@@ -0,0 +1,39 @@
+/*
+ * 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.castor;
+
+import org.onosproject.net.packet.PacketContext;
+
+/**
+ * Interface for processing and handling ARP related events.
+ */
+public interface ArpService {
+
+    /**
+     * Creates an ARP packet to probe for the peer's mac address.
+     *
+     * @param peer A Peer
+     */
+    void createArp(Peer peer);
+
+    /**
+     * Handles the ARP packet in.
+     *
+     * @param context packet context to handle
+     * @return true if handled
+     */
+    boolean handlePacket(PacketContext context);
+}
diff --git a/apps/castor/src/main/java/org/onosproject/castor/Castor.java b/apps/castor/src/main/java/org/onosproject/castor/Castor.java
new file mode 100644
index 0000000..b604b77
--- /dev/null
+++ b/apps/castor/src/main/java/org/onosproject/castor/Castor.java
@@ -0,0 +1,63 @@
+/*
+ * 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.castor;
+
+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.app.ApplicationService;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.routing.IntentSynchronizationService;
+import org.slf4j.Logger;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Component of the Castor Class.
+ */
+
+@Component(immediate = true)
+public class Castor {
+
+    public static final String CASTOR_APP = "org.onosproject.castor";
+    private final Logger log = getLogger(getClass());
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CoreService coreService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected ApplicationService applicationService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected IntentSynchronizationService intentSynchronizer;
+
+    private ApplicationId appId;
+
+    @Activate
+    protected void activate() {
+        appId = coreService.registerApplication(CASTOR_APP);
+        applicationService.registerDeactivateHook(appId, () -> {
+            intentSynchronizer.removeIntentsByAppId(appId);
+        });
+    }
+
+    @Deactivate
+    protected void deactivate() {
+    }
+}
diff --git a/apps/castor/src/main/java/org/onosproject/castor/CastorArpManager.java b/apps/castor/src/main/java/org/onosproject/castor/CastorArpManager.java
new file mode 100644
index 0000000..cfe9d11
--- /dev/null
+++ b/apps/castor/src/main/java/org/onosproject/castor/CastorArpManager.java
@@ -0,0 +1,417 @@
+/*
+ * 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.castor;
+
+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.apache.felix.scr.annotations.Service;
+import org.onlab.packet.ARP;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
+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.InboundPacket;
+import org.onosproject.net.packet.PacketContext;
+import org.onosproject.net.packet.PacketProcessor;
+import org.onosproject.net.packet.PacketService;
+import org.slf4j.Logger;
+
+import java.nio.ByteBuffer;
+import java.util.Optional;
+import java.util.Set;
+
+import static org.onlab.packet.Ethernet.TYPE_ARP;
+import static org.onosproject.net.packet.PacketPriority.CONTROL;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Component for managing the ARPs.
+ */
+
+@Component(immediate = true, enabled = true)
+@Service
+public class CastorArpManager implements ArpService  {
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected ConnectivityManagerService connectivityManager;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected PacketService packetService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CoreService coreService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CastorStore castorStore;
+
+    private ProxyArpProcessor processor = new ProxyArpProcessor();
+
+    private final Logger log = getLogger(getClass());
+    private static final int FLOW_PRIORITY = 500;
+    private static final MacAddress ARP_SOURCEMAC = MacAddress.valueOf("00:00:00:00:00:01");
+    private static final MacAddress ARP_DEST = MacAddress.valueOf("00:00:00:00:00:00");
+    private static final byte[] ZERO_MAC_ADDRESS = MacAddress.ZERO.toBytes();
+    private static final IpAddress ARP_SRC = Ip4Address.valueOf("0.0.0.0");
+
+    private ApplicationId appId;
+    Optional<DeviceId> deviceID = null;
+
+    private enum Protocol {
+        ARP
+    }
+
+    private enum MessageType {
+        REQUEST, REPLY
+    }
+
+    @Activate
+    public void activate() {
+        appId = coreService.getAppId(Castor.CASTOR_APP);
+        packetService.addProcessor(processor, PacketProcessor.director(1));
+        requestPackets();
+    }
+
+    @Deactivate
+    public void deactivate() {
+        withdrawIntercepts();
+        packetService.removeProcessor(processor);
+        processor = null;
+    }
+
+    /**
+     * Used to request the ARP packets.
+     */
+    private void requestPackets() {
+        TrafficSelector.Builder selectorBuilder =
+                DefaultTrafficSelector.builder();
+        selectorBuilder.matchEthType(TYPE_ARP);
+        packetService.requestPackets(selectorBuilder.build(), CONTROL, appId);
+    }
+
+    /**
+     * Withdraws the requested ARP packets.
+     */
+    private void withdrawIntercepts() {
+        TrafficSelector.Builder selectorBuilder =
+                DefaultTrafficSelector.builder();
+        selectorBuilder.matchEthType(TYPE_ARP);
+        packetService.cancelPackets(selectorBuilder.build(), CONTROL, appId, deviceID);
+    }
+
+    /**
+     * Forwards the ARP packet to the specified connect point via packet out.
+     *
+     * @param context The packet context
+     */
+    private void forward(MessageContext context) {
+
+        TrafficTreatment.Builder builder = null;
+        Ethernet eth = context.packet();
+        ByteBuffer buf = ByteBuffer.wrap(eth.serialize());
+
+        IpAddress target = context.target();
+        String value = getMatchingConnectPoint(target);
+        if (value != null) {
+            ConnectPoint connectPoint = ConnectPoint.deviceConnectPoint(value);
+            builder = DefaultTrafficTreatment.builder();
+            builder.setOutput(connectPoint.port());
+            packetService.emit(new DefaultOutboundPacket(connectPoint.deviceId(), builder.build(), buf));
+        }
+    }
+
+    @Override
+    public void createArp(Peer peer) {
+
+        Ethernet packet = null;
+        packet = buildArpRequest(peer);
+        ByteBuffer buf = ByteBuffer.wrap(packet.serialize());
+        ConnectPoint connectPoint = ConnectPoint.deviceConnectPoint(peer.getPort());
+
+        TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder();
+        builder.setOutput(connectPoint.port());
+        packetService.emit(new DefaultOutboundPacket(connectPoint.deviceId(), builder.build(), buf));
+
+    }
+
+    /**
+     * Builds the ARP request when MAC is not known.
+     *
+     * @param peer The Peer whose MAC is not known.
+     * @return Ethernet
+     */
+    private Ethernet buildArpRequest(Peer peer) {
+        ARP arp = new ARP();
+        arp.setHardwareType(ARP.HW_TYPE_ETHERNET)
+                .setHardwareAddressLength((byte) Ethernet.DATALAYER_ADDRESS_LENGTH)
+                .setProtocolType(ARP.PROTO_TYPE_IP)
+                .setProtocolAddressLength((byte) IpAddress.INET_BYTE_LENGTH)
+                .setOpCode(ARP.OP_REQUEST);
+
+        arp.setSenderHardwareAddress(ARP_SOURCEMAC.toBytes())
+                .setSenderProtocolAddress(ARP_SRC.toOctets())
+                .setTargetHardwareAddress(ZERO_MAC_ADDRESS)
+                .setTargetProtocolAddress(IpAddress.valueOf(peer.getIpAddress()).toOctets());
+
+        Ethernet ethernet = new Ethernet();
+        ethernet.setEtherType(Ethernet.TYPE_ARP)
+                .setDestinationMACAddress(MacAddress.BROADCAST)
+                .setSourceMACAddress(ARP_SOURCEMAC)
+                .setPayload(arp);
+        ethernet.setPad(true);
+
+        return ethernet;
+    }
+
+    /**
+     * Gets the matching connect point corresponding to the peering IP address.
+     *
+     * @param target Target IP address
+     * @return Connect point as a String
+     */
+    private String getMatchingConnectPoint(IpAddress target) {
+        Set<Peer> peers = castorStore.getAllPeers();
+        for (Peer peer : peers) {
+            IpAddress match = IpAddress.valueOf(peer.getIpAddress());
+            if (match.equals(target)) {
+                return peer.getPort();
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns the matching Peer or route server on a Connect Point.
+     *
+     * @param connectPoint The peering connect point.
+     * @return Peer or Route Server
+     */
+    private Peer getMatchingPeer(ConnectPoint connectPoint) {
+
+        for (Peer peer : castorStore.getAllPeers()) {
+            if (connectPoint.equals(ConnectPoint.deviceConnectPoint(peer.getPort()))) {
+                return peer;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns matching BGP Peer on a connect point.
+     *
+     * @param connectPoint The peering connect point.
+     * @return The Peer
+     */
+    private Peer getMatchingCustomer(ConnectPoint connectPoint) {
+
+        for (Peer peer : castorStore.getCustomers()) {
+            if (connectPoint.equals(ConnectPoint.deviceConnectPoint(peer.getPort()))) {
+                return peer;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Updates the IP address to mac address map.
+     *
+     * @param context The message context.
+     */
+    private void updateMac(MessageContext context) {
+
+        if ((castorStore.getAddressMap()).containsKey(context.sender())) {
+            return;
+        }
+        Ethernet eth = context.packet();
+        MacAddress macAddress = eth.getSourceMAC();
+        IpAddress ipAddress = context.sender();
+        castorStore.setAddressMap(ipAddress, macAddress);
+    }
+
+    /**
+     * Setup the layer two flows if not already installed after an ARP packet is received.
+     * If the layer 2 status is true, means layer two flows are already provisioned.
+     * If the status was false, layer 2 flows will be installed at this point. This
+     * happens when the mac address of a peer was not known at the time of its addition.
+     *
+     * @param msgContext The message context.
+     */
+    private void handleArpForL2(MessageContext msgContext) {
+
+        ConnectPoint cp = msgContext.inPort();
+        Peer peer = getMatchingCustomer(cp);
+
+        if (peer != null && !peer.getl2Status()) {
+            connectivityManager.setUpL2(peer);
+        }
+    }
+
+    @Override
+    public boolean handlePacket(PacketContext context) {
+
+        InboundPacket pkt = context.inPacket();
+        Ethernet ethPkt = pkt.parsed();
+
+        if (ethPkt == null) {
+            return false;
+        }
+
+        MessageContext msgContext = createContext(ethPkt, pkt.receivedFrom());
+
+        if (msgContext == null) {
+            return false;
+        }
+        switch (msgContext.type()) {
+            case REPLY:
+                forward(msgContext);
+                updateMac(msgContext);
+                handleArpForL2(msgContext);
+                break;
+            case REQUEST:
+                forward(msgContext);
+                updateMac(msgContext);
+                handleArpForL2(msgContext);
+                break;
+            default:
+                return false;
+        }
+        context.block();
+        return true;
+    }
+
+    private MessageContext createContext(Ethernet eth, ConnectPoint inPort) {
+        if (eth.getEtherType() == Ethernet.TYPE_ARP) {
+            return createArpContext(eth, inPort);
+        }
+        return null;
+    }
+
+    /**
+     * Extracts context information from ARP packets.
+     *
+     * @param eth input Ethernet frame that is thought to be ARP
+     * @param inPort in port
+     * @return MessageContext object if the packet was a valid ARP packet,
+     * otherwise null
+     */
+    private MessageContext createArpContext(Ethernet eth, ConnectPoint inPort) {
+        if (eth.getEtherType() != Ethernet.TYPE_ARP) {
+            return null;
+        }
+
+        ARP arp = (ARP) eth.getPayload();
+
+        IpAddress target = Ip4Address.valueOf(arp.getTargetProtocolAddress());
+        IpAddress sender = Ip4Address.valueOf(arp.getSenderProtocolAddress());
+
+        MessageType type;
+        if (arp.getOpCode() == ARP.OP_REQUEST) {
+            type = MessageType.REQUEST;
+        } else if (arp.getOpCode() == ARP.OP_REPLY) {
+            type = MessageType.REPLY;
+        } else {
+            return null;
+        }
+        return new MessageContext(eth, inPort, Protocol.ARP, type, target, sender);
+    }
+
+    private class MessageContext {
+        private Protocol protocol;
+        private MessageType type;
+
+        private IpAddress target;
+        private IpAddress sender;
+
+        private Ethernet eth;
+        private ConnectPoint inPort;
+
+        public MessageContext(Ethernet eth, ConnectPoint inPort,
+                              Protocol protocol, MessageType type,
+                              IpAddress target, IpAddress sender) {
+            this.eth = eth;
+            this.inPort = inPort;
+            this.protocol = protocol;
+            this.type = type;
+            this.target = target;
+            this.sender = sender;
+        }
+
+        public ConnectPoint inPort() {
+            return inPort;
+        }
+
+        public Ethernet packet() {
+            return eth;
+        }
+
+        public Protocol protocol() {
+            return protocol;
+        }
+
+        public MessageType type() {
+            return type;
+        }
+
+        public VlanId vlan() {
+            return VlanId.vlanId(eth.getVlanID());
+        }
+
+        public MacAddress srcMac() {
+            return MacAddress.valueOf(eth.getSourceMACAddress());
+        }
+
+        public IpAddress target() {
+            return target;
+        }
+
+        public IpAddress sender() {
+            return sender;
+        }
+    }
+    private class ProxyArpProcessor implements PacketProcessor {
+
+        @Override
+        public void process(PacketContext context) {
+
+            if (context.isHandled()) {
+                return;
+            }
+            InboundPacket pkt = context.inPacket();
+            Ethernet ethPkt = pkt.parsed();
+            if (ethPkt == null) {
+                return;
+            }
+            if (ethPkt.getEtherType() == TYPE_ARP) {
+                //handle the arp packet.
+                handlePacket(context);
+            } else {
+                return;
+            }
+        }
+    }
+}
diff --git a/apps/castor/src/main/java/org/onosproject/castor/CastorStore.java b/apps/castor/src/main/java/org/onosproject/castor/CastorStore.java
new file mode 100644
index 0000000..6c883d5
--- /dev/null
+++ b/apps/castor/src/main/java/org/onosproject/castor/CastorStore.java
@@ -0,0 +1,146 @@
+/*
+ * 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.castor;
+
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onosproject.net.intent.Key;
+import org.onosproject.net.intent.MultiPointToSinglePointIntent;
+import org.onosproject.net.intent.PointToPointIntent;
+
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Interface to access Castor Distributed Store.
+ */
+public interface CastorStore {
+
+    /**
+     *Returns list of all peers including the route servers.
+     *
+     * @return list of Peer
+     */
+    Set<Peer> getAllPeers();
+
+    /**
+     * Store a Peer.
+     *
+     * @param peer The Peer to store
+     */
+    void storePeer(Peer peer);
+
+    /**
+     * Get the Route Servers.
+     *
+     * @return List of Servers
+     */
+    Set<Peer> getServers();
+
+    /**
+     * Store a Route Server.
+     *
+     * @param peer The Server
+     */
+    void storeServer(Peer peer);
+
+    /**
+     * Returns the list of added BGP Peers.
+     *
+     * @return List of Peer
+     */
+    Set<Peer> getCustomers();
+
+    /**
+     * Store a Customer.
+     *
+     * @param peer The Customer to be stored
+     */
+    void storeCustomer(Peer peer);
+
+    /**
+     * Returns the map of currently known mac addresses from ARP.
+     *
+     * @return map of IP address to Mac
+     */
+    Map<IpAddress, MacAddress> getAddressMap();
+
+    /**
+     * Sets the mapping from IP address to Mac.
+     *
+     * @param ip IP Address
+     * @param mac MAC Address
+     */
+    void setAddressMap(IpAddress ip, MacAddress mac);
+
+    /**
+     * Returns all the peer intents.
+     *
+     * @return PointToPointIntent
+     */
+    Map<Key, PointToPointIntent> getPeerIntents();
+
+    /**
+     * Stores a Peer Intent.
+     *
+     * @param key The intent
+     * @param intent Key
+     */
+    void storePeerIntent(Key key, PointToPointIntent intent);
+
+    /**
+     * Returns layer2 intents.
+     *
+     * @return MultiPointToSinglePointIntent
+     */
+    Map<String, MultiPointToSinglePointIntent> getLayer2Intents();
+
+    /**
+     * Stores a layer2 intent.
+     *
+     * @param key Key
+     * @param intent The intent
+     */
+    void storeLayer2Intent(String key, MultiPointToSinglePointIntent intent);
+
+    /**
+     * Returns the mapping of Customer names to their IP address.
+     *
+     * @return HashMap
+     */
+    Map<String, String> getCustomersMap();
+
+    /**
+     * Removes a customer and its associated flows from the network.
+     *
+     * @param peer The Customer
+     */
+    void removeCustomer(Peer peer);
+
+    /**
+     * Removes a peer intent from the store.
+     *
+     * @param key Key for the intent
+     */
+    void removePeerIntent(Key key);
+
+    /**
+     * Removes the layer2 intent from the store.
+     *
+     * @param key Key for intent
+     */
+    void removeLayer2Intent(String key);
+}
diff --git a/apps/castor/src/main/java/org/onosproject/castor/CastorWebApplication.java b/apps/castor/src/main/java/org/onosproject/castor/CastorWebApplication.java
new file mode 100644
index 0000000..07d7bb2
--- /dev/null
+++ b/apps/castor/src/main/java/org/onosproject/castor/CastorWebApplication.java
@@ -0,0 +1,31 @@
+/*
+ * 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.castor;
+
+import org.onlab.rest.AbstractWebApplication;
+
+import java.util.Set;
+
+/**
+ * REST API web application.
+ */
+public class CastorWebApplication extends AbstractWebApplication {
+    @Override
+    public Set<Class<?>> getClasses() {
+        return getClasses(CastorWebResource.class);
+    }
+}
diff --git a/apps/castor/src/main/java/org/onosproject/castor/CastorWebResource.java b/apps/castor/src/main/java/org/onosproject/castor/CastorWebResource.java
new file mode 100644
index 0000000..8c08998
--- /dev/null
+++ b/apps/castor/src/main/java/org/onosproject/castor/CastorWebResource.java
@@ -0,0 +1,154 @@
+/*
+ * 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.castor;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.onlab.packet.IpAddress;
+import org.onosproject.rest.AbstractWebResource;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+/**
+ * The Web Resource for REST API calls to the Castor application.
+ */
+@Path("castor")
+public class CastorWebResource  extends AbstractWebResource {
+
+
+    /**
+     * Get the present ARP Mapping.
+     * Use this to get the present ARP map stored by Castor
+     *
+     * @return 200 OK
+     */
+    @GET
+    @Path("mac-map")
+    public Response getMac() {
+        String result = get(CastorStore.class).getAddressMap().toString();
+        ObjectNode node = mapper().createObjectNode().put("response", result);
+        return ok(node).build();
+    }
+
+    /**
+     * Get list of added peers.
+     * List of peers added.
+     *
+     * @return 200 OK
+     */
+    @GET
+    @Path("get-peers")
+    public Response getPeers() {
+        String result = get(CastorStore.class).getCustomersMap().toString();
+        ObjectNode node = mapper().createObjectNode().put("response", result);
+        return ok(node).build();
+    }
+
+    /**
+     * Add a Peer.
+     * Use this to add a Customer or a BGP Peer
+     *
+     * @onos.rsModel PeerModel
+     * @param incomingData json Data
+     * @return 200 OK
+     */
+    @POST
+    @Path("add-peer")
+    @Consumes(MediaType.APPLICATION_JSON)
+    public Response addPeer(String incomingData) {
+
+        String arpResult = ", Mac was known";
+
+        try {
+            ObjectMapper mapper = new ObjectMapper();
+            Peer peer = mapper.readValue(incomingData, Peer.class);
+            get(ConnectivityManagerService.class).setUpConnectivity(peer);
+            if ((get(CastorStore.class)).getAddressMap()
+                    .get(IpAddress.valueOf(peer.getIpAddress())) != null) {
+                get(ConnectivityManagerService.class).setUpL2(peer);
+            } else {
+                get(ArpService.class).createArp(peer);
+                arpResult = ", ARP packet sent, MAC was not known";
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+            String result = "Unable to process due to some reason, Try again";
+            ObjectNode node = mapper().createObjectNode().put("response", result);
+            return ok(node).build();
+        }
+
+        String result = "Success: Peer Entered" + arpResult;
+        ObjectNode node = mapper().createObjectNode().put("response", result);
+        return ok(node).build();
+    }
+
+    /**
+     * Delete a Peer.
+     * Use this to delete a Peer. IpAddress should match as entered while adding.
+     *
+     * @onos.rsModel PeerModel
+     * @param incomingData json Data
+     * @return 200 OK
+     */
+    @POST
+    @Path("delete-peer")
+    @Consumes(MediaType.APPLICATION_JSON)
+    public Response deletePeer(String incomingData) {
+
+        try {
+            ObjectMapper mapper = new ObjectMapper();
+            Peer peer = mapper.readValue(incomingData, Peer.class);
+            get(ConnectivityManagerService.class).deletePeer(peer);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return Response.status(500).entity("Unable to delete the peer").build();
+        }
+        String result = "Peer Deleted";
+        ObjectNode node = mapper().createObjectNode().put("response", result);
+        return ok(node).build();
+    }
+
+    /**
+     * Add router server.
+     * Use this to add to add Route Servers for initializing
+     *
+     * @onos.rsModel PeerModel
+     * @param incomingData json Data
+     * @return 200 OK
+     */
+    @POST
+    @Path("route-server")
+    @Consumes(MediaType.APPLICATION_JSON)
+    public Response addRouteServer(String incomingData) {
+
+        try {
+            ObjectMapper mapper = new ObjectMapper();
+            Peer peer = mapper.readValue(incomingData, Peer.class);
+            get(ConnectivityManagerService.class).start(peer);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return Response.status(500).entity("Unable to add the route server").build();
+        }
+        String result = "Server Entered";
+        ObjectNode node = mapper().createObjectNode().put("response", result);
+        return ok(node).build();
+    }
+}
diff --git a/apps/castor/src/main/java/org/onosproject/castor/ConnectivityManager.java b/apps/castor/src/main/java/org/onosproject/castor/ConnectivityManager.java
new file mode 100644
index 0000000..bd55d0d
--- /dev/null
+++ b/apps/castor/src/main/java/org/onosproject/castor/ConnectivityManager.java
@@ -0,0 +1,478 @@
+/*
+ * 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.castor;
+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.apache.felix.scr.annotations.Service;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.IPv4;
+import org.onlab.packet.IPv6;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.TpPort;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+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.intent.Key;
+import org.onosproject.net.intent.MultiPointToSinglePointIntent;
+import org.onosproject.net.intent.PointToPointIntent;
+import org.onosproject.routing.IntentSynchronizationService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Manages the connectivity requirements between peers.
+ */
+@Component(immediate = true, enabled = true)
+@Service
+public class ConnectivityManager implements ConnectivityManagerService {
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected IntentSynchronizationService intentSynchronizer;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CoreService coreService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CastorStore castorStore;
+
+    private static final int PRIORITY_OFFSET = 1000;
+    private static final int FLOW_PRIORITY = 500;
+    private static final String SUFFIX_DST = "dst";
+    private static final String SUFFIX_SRC = "src";
+    private static final String SUFFIX_ICMP = "icmp";
+
+    private static final Logger log = LoggerFactory.getLogger(ConnectivityManager.class);
+
+    private static final short BGP_PORT = 179;
+
+    private ApplicationId appId;
+
+    @Activate
+    public void activate() {
+        appId = coreService.getAppId(Castor.CASTOR_APP);
+    }
+
+    @Deactivate
+    public void deactivate() {
+    }
+
+    /**
+     * Inputs the Route Servers.
+     */
+    @Override
+    public void start(Peer server) {
+        //routeServers.add(server);
+        //allPeers.add(server);
+        castorStore.storePeer(server);
+        castorStore.storeServer(server);
+    }
+
+    /**
+     * Stops the peer connectivity manager.
+     */
+    public void stop() {};
+
+    /**
+     * Sets up paths to establish connectivity between all internal.
+     * BGP speakers and external BGP peers.
+     */
+    @Override
+    public void setUpConnectivity(Peer peer) {
+
+        if (!castorStore.getCustomers().contains(peer)) {
+            castorStore.storePeer(peer);
+            castorStore.storeCustomer(peer);
+        }
+
+        for (Peer routeServer : castorStore.getServers()) {
+            log.debug("Start to set up BGP paths for BGP peer and Route Server"
+                    + peer + "&" + routeServer);
+
+            buildSpeakerIntents(routeServer, peer).forEach(i -> {
+                    castorStore.storePeerIntent(i.key(), i);
+                    intentSynchronizer.submit(i);
+                });
+        }
+    }
+
+    private Collection<PointToPointIntent> buildSpeakerIntents(Peer speaker, Peer peer) {
+        List<PointToPointIntent> intents = new ArrayList<>();
+
+        IpAddress peerAddress = IpAddress.valueOf(peer.getIpAddress());
+        IpAddress speakerAddress = IpAddress.valueOf(speaker.getIpAddress());
+
+        checkNotNull(peerAddress);
+
+        intents.addAll(buildIntents(ConnectPoint.deviceConnectPoint(speaker.getPort()), speakerAddress,
+                ConnectPoint.deviceConnectPoint(peer.getPort()), peerAddress));
+
+        return intents;
+    }
+
+    /**
+     * Builds the required intents between the two pairs of connect points and
+     * IP addresses.
+     *
+     * @param portOne the first connect point
+     * @param ipOne the first IP address
+     * @param portTwo the second connect point
+     * @param ipTwo the second IP address
+     * @return the intents to install
+     */
+    private Collection<PointToPointIntent> buildIntents(ConnectPoint portOne,
+                                                        IpAddress ipOne,
+                                                        ConnectPoint portTwo,
+                                                        IpAddress ipTwo) {
+
+        List<PointToPointIntent> intents = new ArrayList<>();
+
+        TrafficTreatment treatment = DefaultTrafficTreatment.emptyTreatment();
+        TrafficSelector selector;
+        Key key;
+
+        byte tcpProtocol;
+        byte icmpProtocol;
+
+        if (ipOne.isIp4()) {
+            tcpProtocol = IPv4.PROTOCOL_TCP;
+            icmpProtocol = IPv4.PROTOCOL_ICMP;
+        } else {
+            tcpProtocol = IPv6.PROTOCOL_TCP;
+            icmpProtocol = IPv6.PROTOCOL_ICMP6;
+        }
+
+        // Path from BGP speaker to BGP peer matching source TCP port 179
+        selector = buildSelector(tcpProtocol,
+                ipOne,
+                ipTwo,
+                BGP_PORT,
+                null);
+
+        key = buildKey(ipOne, ipTwo, SUFFIX_SRC);
+
+        intents.add(PointToPointIntent.builder()
+                .appId(appId)
+                .key(key)
+                .selector(selector)
+                .treatment(treatment)
+                .ingressPoint(portOne)
+                .egressPoint(portTwo)
+                .priority(PRIORITY_OFFSET)
+                .build());
+
+        // Path from BGP peer to BGP speaker matching destination TCP port 179
+        selector = buildSelector(tcpProtocol,
+                ipTwo,
+                ipOne,
+                null,
+                BGP_PORT);
+
+        key = buildKey(ipTwo, ipOne, SUFFIX_DST);
+
+        intents.add(PointToPointIntent.builder()
+                .appId(appId)
+                .key(key)
+                .selector(selector)
+                .treatment(treatment)
+                .ingressPoint(portTwo)
+                .egressPoint(portOne)
+                .priority(PRIORITY_OFFSET)
+                .build());
+
+        // ICMP path from BGP speaker to BGP peer
+        selector = buildSelector(icmpProtocol,
+                ipOne,
+                ipTwo,
+                null,
+                null);
+
+        key = buildKey(ipOne, ipTwo, SUFFIX_ICMP);
+
+        intents.add(PointToPointIntent.builder()
+                .appId(appId)
+                .key(key)
+                .selector(selector)
+                .treatment(treatment)
+                .ingressPoint(portOne)
+                .egressPoint(portTwo)
+                .priority(PRIORITY_OFFSET)
+                .build());
+
+        // ICMP path from BGP peer to BGP speaker
+        selector = buildSelector(icmpProtocol,
+                ipTwo,
+                ipOne,
+                null,
+                null);
+
+        key = buildKey(ipTwo, ipOne, SUFFIX_ICMP);
+
+        intents.add(PointToPointIntent.builder()
+                .appId(appId)
+                .key(key)
+                .selector(selector)
+                .treatment(treatment)
+                .ingressPoint(portTwo)
+                .egressPoint(portOne)
+                .priority(PRIORITY_OFFSET)
+                .build());
+
+        return intents;
+    }
+
+    /**
+     * Builds a traffic selector based on the set of input parameters.
+     *
+     * @param ipProto IP protocol
+     * @param srcIp source IP address
+     * @param dstIp destination IP address
+     * @param srcTcpPort source TCP port, or null if shouldn't be set
+     * @param dstTcpPort destination TCP port, or null if shouldn't be set
+     * @return the new traffic selector
+     */
+    private TrafficSelector buildSelector(byte ipProto, IpAddress srcIp,
+                                          IpAddress dstIp, Short srcTcpPort,
+                                          Short dstTcpPort) {
+        TrafficSelector.Builder builder = DefaultTrafficSelector.builder().matchIPProtocol(ipProto);
+
+        if (dstIp.isIp4()) {
+            builder.matchEthType(Ethernet.TYPE_IPV4)
+                    .matchIPSrc(IpPrefix.valueOf(srcIp, IpPrefix.MAX_INET_MASK_LENGTH))
+                    .matchIPDst(IpPrefix.valueOf(dstIp, IpPrefix.MAX_INET_MASK_LENGTH));
+        } else {
+            builder.matchEthType(Ethernet.TYPE_IPV6)
+                    .matchIPv6Src(IpPrefix.valueOf(srcIp, IpPrefix.MAX_INET6_MASK_LENGTH))
+                    .matchIPv6Dst(IpPrefix.valueOf(dstIp, IpPrefix.MAX_INET6_MASK_LENGTH));
+        }
+
+        if (srcTcpPort != null) {
+            builder.matchTcpSrc(TpPort.tpPort(srcTcpPort));
+        }
+
+        if (dstTcpPort != null) {
+            builder.matchTcpDst(TpPort.tpPort(dstTcpPort));
+        }
+
+        return builder.build();
+    }
+
+    /**
+     * Builds an intent Key for a point-to-point intent based off the source
+     * and destination IP address, as well as a suffix String to distinguish
+     * between different types of intents between the same source and
+     * destination.
+     *
+     * @param srcIp source IP address
+     * @param dstIp destination IP address
+     * @param suffix suffix string
+     * @return intent key
+     */
+    private Key buildKey(IpAddress srcIp, IpAddress dstIp, String suffix) {
+        String keyString = new StringBuilder()
+                .append(srcIp.toString())
+                .append("-")
+                .append(dstIp.toString())
+                .append("-")
+                .append(suffix)
+                .toString();
+
+        return Key.of(keyString, appId);
+    }
+
+    @Override
+    public void setUpL2(Peer peer) {
+
+        // First update all the previous existing intents. Update ingress points.
+
+        if (!castorStore.getLayer2Intents().isEmpty()) {
+            updateExistingL2Intents(peer);
+        }
+
+        Set<ConnectPoint> ingressPorts = new HashSet<>();
+        ConnectPoint egressPort = ConnectPoint.deviceConnectPoint(peer.getPort());
+
+        for (Peer inPeer : castorStore.getAllPeers()) {
+            if (!inPeer.getName().equals(peer.getName())) {
+                ingressPorts.add(ConnectPoint.deviceConnectPoint(inPeer.getPort()));
+            }
+        }
+        TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
+        MacAddress macAddress = castorStore.getAddressMap().get(IpAddress.valueOf(peer.getIpAddress()));
+        selector.matchEthDst(macAddress);
+        TrafficTreatment treatment = DefaultTrafficTreatment.emptyTreatment();
+        Key key = Key.of(peer.getIpAddress(), appId);
+
+        MultiPointToSinglePointIntent intent = MultiPointToSinglePointIntent.builder()
+                .appId(appId)
+                .key(key)
+                .selector(selector.build())
+                .treatment(treatment)
+                .ingressPoints(ingressPorts)
+                .egressPoint(egressPort)
+                .priority(FLOW_PRIORITY)
+                .build();
+        intentSynchronizer.submit(intent);
+        castorStore.storeLayer2Intent(peer.getIpAddress(), intent);
+        castorStore.removeCustomer(peer);
+        peer.setL2(true);
+        castorStore.storeCustomer(peer);
+    }
+
+    /**
+     * Updates the existing layer 2 flows. Whenever a new Peer is added, it is also
+     * added as the ingress point to the existing layer two flows.
+     *
+     * @param peer The Peer being added.
+     */
+    private void updateExistingL2Intents(Peer peer) {
+
+        Collection<MultiPointToSinglePointIntent> oldIntents = castorStore.getLayer2Intents().values();
+
+        for (MultiPointToSinglePointIntent oldIntent : oldIntents) {
+
+            Set<ConnectPoint> ingressPoints = oldIntent.ingressPoints();
+            ConnectPoint egressPoint = oldIntent.egressPoint();
+            if (ingressPoints.add(ConnectPoint.deviceConnectPoint(peer.getPort()))) {
+
+                MultiPointToSinglePointIntent updatedMp2pIntent =
+                        MultiPointToSinglePointIntent.builder()
+                                .appId(appId)
+                                .key(oldIntent.key())
+                                .selector(oldIntent.selector())
+                                .treatment(oldIntent.treatment())
+                                .ingressPoints(ingressPoints)
+                                .egressPoint(egressPoint)
+                                .priority(oldIntent.priority())
+                                .build();
+
+                //layer2Intents.put(peer.getIpAddress(), updatedMp2pIntent);
+                castorStore.storeLayer2Intent(peer.getIpAddress(), updatedMp2pIntent);
+                intentSynchronizer.submit(updatedMp2pIntent);
+            }
+        }
+    }
+
+    @Override
+    public void deletePeer(Peer peer) {
+
+        if (castorStore.getCustomers().contains(peer)) {
+
+            deletel3(peer);
+
+            for (Peer customer : castorStore.getCustomers()) {
+                if (customer.getIpAddress().equals(peer.getIpAddress()) && customer.getl2Status()) {
+                    deleteL2(customer);
+                    updateL2AfterDeletion(customer);
+                }
+            }
+            castorStore.removeCustomer(peer);
+        }
+    }
+
+    /**
+     * Delete all the flows between the Peer being deleted and the Route Servers
+     * to kill the BGP sessions. It uses the keys to retrive the previous intents
+     * and withdraw them.
+     *
+     * @param peer The Peer being deleted.
+     */
+    private void deletel3(Peer peer) {
+
+        List<Key> keys = new LinkedList<>();
+        IpAddress ipTwo = IpAddress.valueOf(peer.getIpAddress());
+
+        for (Peer server : castorStore.getServers()) {
+            IpAddress ipOne = IpAddress.valueOf(server.getIpAddress());
+            keys.add(buildKey(ipOne, ipTwo, SUFFIX_SRC));
+            keys.add(buildKey(ipTwo, ipOne, SUFFIX_DST));
+            keys.add(buildKey(ipOne, ipTwo, SUFFIX_ICMP));
+            keys.add(buildKey(ipTwo, ipOne, SUFFIX_ICMP));
+        }
+        for (Key keyDel : keys) {
+
+            PointToPointIntent intent = castorStore.getPeerIntents().get(keyDel);
+            intentSynchronizer.withdraw(intent);
+            castorStore.removePeerIntent(keyDel);
+        }
+    }
+
+    /**
+     * Deletes the layer two flows for the peer being deleted.
+     *
+     * @param peer The Peer being deleted.
+     */
+    private void deleteL2(Peer peer) {
+        intentSynchronizer.withdraw(castorStore.getLayer2Intents().get(peer.getIpAddress()));
+        castorStore.removeLayer2Intent(peer.getIpAddress());
+    }
+
+    /**
+     * Updates all the layer 2 flows after successful deletion of a Peer.
+     * The Peer being deleted is removed from the ingress points of all
+     * other flows.
+     *
+     * @param peer The Peer being deleted.
+     */
+    private void updateL2AfterDeletion(Peer peer) {
+        Collection<MultiPointToSinglePointIntent> oldIntents = castorStore.getLayer2Intents().values();
+        Map<String, MultiPointToSinglePointIntent> intents = new HashMap<>();
+
+        for (MultiPointToSinglePointIntent oldIntent : oldIntents) {
+
+            Set<ConnectPoint> ingressPoints = oldIntent.ingressPoints();
+            ConnectPoint egressPoint = oldIntent.egressPoint();
+            if (ingressPoints.remove(ConnectPoint.deviceConnectPoint(peer.getPort()))) {
+
+                MultiPointToSinglePointIntent updatedMp2pIntent =
+                        MultiPointToSinglePointIntent.builder()
+                                .appId(appId)
+                                .key(oldIntent.key())
+                                .selector(oldIntent.selector())
+                                .treatment(oldIntent.treatment())
+                                .ingressPoints(ingressPoints)
+                                .egressPoint(egressPoint)
+                                .priority(oldIntent.priority())
+                                .build();
+
+                intents.put(peer.getIpAddress(), updatedMp2pIntent);
+                intentSynchronizer.submit(updatedMp2pIntent);
+            }
+        }
+        for (String key : intents.keySet()) {
+            castorStore.storeLayer2Intent(key, intents.get(key));
+        }
+    }
+}
diff --git a/apps/castor/src/main/java/org/onosproject/castor/ConnectivityManagerService.java b/apps/castor/src/main/java/org/onosproject/castor/ConnectivityManagerService.java
new file mode 100644
index 0000000..61e9ace
--- /dev/null
+++ b/apps/castor/src/main/java/org/onosproject/castor/ConnectivityManagerService.java
@@ -0,0 +1,53 @@
+/*
+ * 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.castor;
+
+/**
+ * Service Interface for Connectivity Manager.
+ */
+public interface ConnectivityManagerService {
+
+    /**
+     * Start to add the initial config by adding route servers.
+     *
+     * @param peer The Route Server
+     */
+    void start(Peer peer);
+
+    /**
+     * Sets up connectivity of the peer being added.
+     * This will set up the connectivity between the Peer and the Route Servers.
+     *
+     * @param peer The Peer to be added.
+     */
+    void setUpConnectivity(Peer peer);
+
+    /**
+     * Provisions the layer two flows for the Peer.
+     *
+     * @param peer The Peer for which layer 2 is to be configured.
+     */
+    void setUpL2(Peer peer);
+
+    /**
+     * Deletes a BGP Peer.
+     * This will delete all the flow entries corresponding to the Peer and update the other ones.
+     *
+     * @param peer The Peer to be deleted.
+     */
+    void deletePeer(Peer peer);
+
+}
diff --git a/apps/castor/src/main/java/org/onosproject/castor/DistributedCastorStore.java b/apps/castor/src/main/java/org/onosproject/castor/DistributedCastorStore.java
new file mode 100644
index 0000000..d879db6
--- /dev/null
+++ b/apps/castor/src/main/java/org/onosproject/castor/DistributedCastorStore.java
@@ -0,0 +1,222 @@
+/*
+ * 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.castor;
+
+import com.google.common.collect.ImmutableSet;
+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.apache.felix.scr.annotations.Service;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onlab.util.KryoNamespace;
+import org.onosproject.net.intent.Key;
+import org.onosproject.net.intent.MultiPointToSinglePointIntent;
+import org.onosproject.net.intent.PointToPointIntent;
+import org.onosproject.store.service.ConsistentMap;
+import org.onosproject.store.service.DistributedSet;
+import org.onosproject.store.service.Serializer;
+import org.onosproject.store.service.StorageService;
+import org.onosproject.store.service.Versioned;
+import org.onosproject.store.serializers.KryoNamespaces;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Distributed Store for Castor.
+ */
+
+@Component(immediate = true)
+@Service
+public class DistributedCastorStore implements CastorStore {
+
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected StorageService storageService;
+
+    private ConsistentMap<IpAddress, MacAddress> addressMap;
+    private ConsistentMap<Key, PointToPointIntent> peerIntents;
+    private ConsistentMap<String, MultiPointToSinglePointIntent> layer2Intents;
+    private DistributedSet<Peer> allPeers;
+    private DistributedSet<Peer> customers;
+    private DistributedSet<Peer> routeServers;
+
+
+    @Activate
+    protected void activate() {
+
+        addressMap = storageService.<IpAddress, MacAddress>consistentMapBuilder()
+                .withName("castor-addressMap")
+                .withSerializer(Serializer.using(KryoNamespaces.API))
+                .build();
+
+        peerIntents = storageService.<Key, PointToPointIntent>consistentMapBuilder()
+                .withName("castor-peerIntents")
+                .withSerializer(Serializer.using(KryoNamespaces.API))
+                .build();
+
+        layer2Intents = storageService.<String, MultiPointToSinglePointIntent>consistentMapBuilder()
+                .withName("castor-layer2Intents")
+                .withSerializer(Serializer.using(KryoNamespaces.API))
+                .build();
+
+        allPeers = storageService.<Peer>setBuilder()
+                .withName("castor-allPeers")
+                .withSerializer(Serializer.using(
+                        new KryoNamespace.Builder()
+                                .register(KryoNamespaces.API)
+                                .register(Peer.class)
+                                .build()))
+                .build()
+                .asDistributedSet();
+
+        customers = storageService.<Peer>setBuilder()
+                .withName("castor-customers")
+                .withSerializer(Serializer.using(
+                        new KryoNamespace.Builder()
+                                .register(KryoNamespaces.API)
+                                .register(Peer.class)
+                                .build()))
+                .build()
+                .asDistributedSet();
+
+        routeServers = storageService.<Peer>setBuilder()
+                .withName("castor-routeServers")
+                .withSerializer(Serializer.using(
+                        new KryoNamespace.Builder()
+                                .register(KryoNamespaces.API)
+                                .register(Peer.class)
+                                .build()))
+                .build()
+                .asDistributedSet();
+
+        log.info("Started");
+
+    }
+
+    @Deactivate
+    protected void deactivate() {
+
+    }
+
+    @Override
+    public Set<Peer> getAllPeers() {
+        return ImmutableSet.copyOf(allPeers);
+    }
+
+    @Override
+    public void storePeer(Peer peer) {
+        allPeers.add(peer);
+    }
+
+    @Override
+    public Set<Peer> getServers() {
+        return ImmutableSet.copyOf(routeServers);
+    }
+
+    @Override
+    public void storeServer(Peer server) {
+        routeServers.add(server);
+    }
+
+    @Override
+    public Set<Peer> getCustomers() {
+        return ImmutableSet.copyOf(customers);
+    }
+
+    @Override
+    public void storeCustomer(Peer customer) {
+        customers.add(customer);
+    }
+
+    @Override
+    public Map<IpAddress, MacAddress> getAddressMap() {
+        Map<IpAddress, MacAddress> validMapping = new HashMap<>();
+        for (Map.Entry<IpAddress, Versioned<MacAddress>> entry: addressMap.entrySet()) {
+            validMapping.put(entry.getKey(), entry.getValue().value());
+        }
+        return validMapping;
+    }
+
+    @Override
+    public void setAddressMap(IpAddress ip, MacAddress mac) {
+        addressMap.put(ip, mac);
+    }
+
+    @Override
+    public Map<Key, PointToPointIntent> getPeerIntents() {
+        Map<Key, PointToPointIntent> validMapping = new HashMap<>();
+        for (Map.Entry<Key, Versioned<PointToPointIntent>> entry: peerIntents.entrySet()) {
+            validMapping.put(entry.getKey(), entry.getValue().value());
+        }
+        return validMapping;
+    }
+
+    @Override
+    public void storePeerIntent(Key key, PointToPointIntent intent) {
+        peerIntents.put(key, intent);
+
+    }
+
+    @Override
+    public Map<String, MultiPointToSinglePointIntent> getLayer2Intents() {
+        Map<String, MultiPointToSinglePointIntent> validMapping = new HashMap<>();
+        for (Map.Entry<String, Versioned<MultiPointToSinglePointIntent>> entry: layer2Intents.entrySet()) {
+            validMapping.put(entry.getKey(), entry.getValue().value());
+        }
+        return validMapping;
+    }
+
+    @Override
+    public void storeLayer2Intent(String key, MultiPointToSinglePointIntent intent) {
+        layer2Intents.put(key, intent);
+    }
+
+    @Override
+    public Map<String, String> getCustomersMap() {
+        Map<String, String> peerMap = new HashMap<>();
+        for (Peer cust : customers) {
+            peerMap.put(cust.getName(), cust.getIpAddress());
+        }
+        return peerMap;
+    }
+
+    @Override
+    public void removeCustomer(Peer peer) {
+        customers.remove(peer);
+        allPeers.remove(peer);
+    }
+
+    @Override
+    public void removePeerIntent(Key key) {
+        peerIntents.remove(key);
+
+    }
+
+    @Override
+    public void removeLayer2Intent(String key) {
+        layer2Intents.remove(key);
+
+    }
+}
+
diff --git a/apps/castor/src/main/java/org/onosproject/castor/Peer.java b/apps/castor/src/main/java/org/onosproject/castor/Peer.java
new file mode 100644
index 0000000..cbe5532
--- /dev/null
+++ b/apps/castor/src/main/java/org/onosproject/castor/Peer.java
@@ -0,0 +1,131 @@
+/*
+ * 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.castor;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * POJO class for the Peer and the Route Servers.
+ */
+
+@XmlRootElement
+public class Peer {
+
+    private String name;
+    private String dpid;
+    private String ipAddress;
+    private String port;
+    private boolean l2;
+
+    public Peer() {}
+
+    public Peer(String name, String dpid, String ipAddress, String port) {
+        this.name = name;
+        this.dpid = dpid;
+        this.ipAddress = ipAddress;
+        this.port = port;
+        this.l2 = false;
+    }
+
+    public void setDpid(String dpid) {
+        this.dpid = dpid;
+    }
+
+    public void setIpAddress(String ipAddress) {
+        this.ipAddress = ipAddress;
+    }
+
+    public void setPort(String port) {
+        this.port = port;
+    }
+
+    /**
+     * The name of the Peer or Customer to be added.
+     *
+     * @param name A String name.
+     */
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    /**
+     * Specifies if the layer two flows for this peer are configured or not.
+     *
+     * @param value True if layer two configured.
+     */
+    public void setL2(boolean value) {
+        this.l2 = value;
+    }
+
+    /**
+     * Returns the name of the Peer or the Customer.
+     *
+     * @return The String name.
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * Returns the IP Address of the Peer.
+     *
+     * @return IP Address.
+     */
+    public String getIpAddress() {
+        return ipAddress;
+    }
+
+    /**
+     * Returns the port number where the Peer is attached.
+     *
+     * @return String Connect Point
+     */
+    public String getPort() {
+        return port;
+    }
+
+    /**
+     * Returns the layer two status of the Peer.
+     *
+     * @return True if layer two set.
+     */
+    public boolean getl2Status() {
+        return l2;
+    }
+
+    public String getDpid() {
+        return dpid;
+    }
+
+    @Override
+    public boolean equals(Object ob) {
+        if (ob == null) {
+            return false;
+        }
+        Peer other = (Peer) ob;
+        if (this.ipAddress.equals(other.ipAddress)) {
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 3;
+        hash = 53 * hash + (this.ipAddress != null ? this.ipAddress.hashCode() : 0);
+        return hash;
+    }
+}
diff --git a/apps/castor/src/main/java/org/onosproject/castor/package-info.java b/apps/castor/src/main/java/org/onosproject/castor/package-info.java
new file mode 100644
index 0000000..a036db5
--- /dev/null
+++ b/apps/castor/src/main/java/org/onosproject/castor/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+/**
+ * Castor Exchange Point application.
+ */
+package org.onosproject.castor;
diff --git a/apps/castor/src/main/resources/definitions/PeerModel.json b/apps/castor/src/main/resources/definitions/PeerModel.json
new file mode 100644
index 0000000..3c2cdc2
--- /dev/null
+++ b/apps/castor/src/main/resources/definitions/PeerModel.json
@@ -0,0 +1,28 @@
+{
+  "type": "object",
+  "title": "peer",
+  "required": [
+    "name",
+    "dpid",
+    "ipAddress",
+    "port"
+  ],
+  "properties": {
+    "name": {
+      "type": "string",
+      "example": "Peer/Server 1"
+    },
+    "dpid": {
+      "type": "string",
+      "example": "0000000000000027"
+    },
+    "ipAddress": {
+      "type": "string",
+      "example": "192.168.1.1"
+    },
+    "port": {
+      "type": "string",
+      "example": "of:0000000000000027/30"
+    }
+  }
+}
\ No newline at end of file
diff --git a/apps/castor/src/main/webapp/WEB-INF/web.xml b/apps/castor/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..d3e9eac
--- /dev/null
+++ b/apps/castor/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ 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.
+  -->
+<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xmlns="http://java.sun.com/xml/ns/javaee"
+         xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
+         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
+         id="ONOS" version="2.5">
+    <display-name>Castor App REST API v1.0</display-name>
+
+    <security-constraint>
+        <web-resource-collection>
+            <web-resource-name>Secured</web-resource-name>
+            <url-pattern>/*</url-pattern>
+        </web-resource-collection>
+        <auth-constraint>
+            <role-name>admin</role-name>
+        </auth-constraint>
+    </security-constraint>
+
+    <security-role>
+        <role-name>admin</role-name>
+    </security-role>
+
+    <login-config>
+        <auth-method>BASIC</auth-method>
+        <realm-name>karaf</realm-name>
+    </login-config>
+
+    <servlet>
+        <servlet-name>JAX-RS Service</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.onosproject.castor.CastorWebApplication</param-value>
+        </init-param>
+
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+
+    <servlet-mapping>
+        <servlet-name>JAX-RS Service</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+</web-app>
diff --git a/apps/pom.xml b/apps/pom.xml
index ee70c83..05b92c9 100644
--- a/apps/pom.xml
+++ b/apps/pom.xml
@@ -82,7 +82,6 @@
         <module>tenbi</module>
     </modules>
 
-
     <properties>
         <web.context>default</web.context>
     </properties>
@@ -123,4 +122,4 @@
         </plugins>
     </build>
 
-</project>
+</project>
\ No newline at end of file
diff --git a/modules.defs b/modules.defs
index 924e1aa..572cc78 100644
--- a/modules.defs
+++ b/modules.defs
@@ -163,6 +163,7 @@
     '//apps/xosclient:onos-apps-xosclient-oar',
     '//apps/scalablegateway:onos-apps-scalablegateway-oar',
     '//apps/patchpanel:onos-apps-patchpanel-oar',
+    '//apps/castor:onos-apps-castor-oar',
 ]
 
 APP_JARS = [