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>