[GEΑΝΤ] Refactored: sample migration from SDN-IP to SDX-L3.
Change-Id: I8845c877886e0575a3f0412a6b99043dba11a6fb
diff --git a/sdx-l3/app.xml b/sdx-l3/app.xml
new file mode 100644
index 0000000..4f62cdf
--- /dev/null
+++ b/sdx-l3/app.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+ ~ Copyright 2014-2015 Open Networking Laboratory
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<app name="org.onosproject.sdxl3" origin="GEANT" version="${project.version}"
+ featuresRepo="mvn:${project.groupId}/${project.artifactId}/${project.version}/xml/features"
+ features="${project.artifactId}">
+ <description>${project.description}</description>
+ <artifact>mvn:${project.groupId}/onos-app-routing-api/${project.version}</artifact>
+ <artifact>mvn:${project.groupId}/onos-app-routing/${project.version}</artifact>
+ <artifact>mvn:${project.groupId}/${project.artifactId}/${project.version}</artifact>
+</app>
diff --git a/sdx-l3/features.xml b/sdx-l3/features.xml
new file mode 100644
index 0000000..dbe3f0a
--- /dev/null
+++ b/sdx-l3/features.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+
+<!--
+ ~ Copyright 2014-2015 Open Networking Laboratory
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<features xmlns="http://karaf.apache.org/xmlns/features/v1.2.0" name="${project.artifactId}-${project.version}">
+ <feature name="onos-app-sdx-l3" version="${project.version}"
+ description="${project.description}">
+ <feature>onos-api</feature>
+ <bundle>mvn:${project.groupId}/onos-app-routing-api/${project.version}</bundle>
+ <bundle>mvn:${project.groupId}/onos-app-routing/${project.version}</bundle>
+ <bundle>mvn:${project.groupId}/onos-app-sdx-l3/${project.version}</bundle>
+ </feature>
+</features>
diff --git a/sdx-l3/pom.xml b/sdx-l3/pom.xml
new file mode 100644
index 0000000..7a51fef
--- /dev/null
+++ b/sdx-l3/pom.xml
@@ -0,0 +1,103 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+ ~ Copyright 2016 Open Networking Laboratory
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-apps</artifactId>
+ <version>1.5.0-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>onos-app-sdx-l3</artifactId>
+ <packaging>bundle</packaging>
+
+ <description>SDX-L3 application for GEANT</description>
+
+ <dependencies>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onlab-misc</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onlab-junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-api</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ <classifier>tests</classifier>
+ </dependency>
+
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-app-routing</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-app-routing-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-app-routing-api</artifactId>
+ <scope>test</scope>
+ <classifier>tests</classifier>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-cli</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.karaf.shell</groupId>
+ <artifactId>org.apache.karaf.shell.console</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.easymock</groupId>
+ <artifactId>easymock</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+</project>
diff --git a/sdx-l3/src/main/java/org/onosproject/sdxl3/SdxL3.java b/sdx-l3/src/main/java/org/onosproject/sdxl3/SdxL3.java
new file mode 100644
index 0000000..f01cd80
--- /dev/null
+++ b/sdx-l3/src/main/java/org/onosproject/sdxl3/SdxL3.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.sdxl3;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onlab.packet.Ethernet;
+import org.onosproject.app.ApplicationService;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.incubator.component.ComponentService;
+import org.onosproject.incubator.net.intf.InterfaceService;
+import org.onosproject.net.config.NetworkConfigService;
+import org.onosproject.net.edge.EdgePortService;
+import org.onosproject.net.host.HostService;
+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.onosproject.routing.IntentSynchronizationAdminService;
+import org.onosproject.routing.IntentSynchronizationService;
+import org.onosproject.routing.RoutingService;
+import org.onosproject.routing.config.BgpConfig;
+import org.slf4j.Logger;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Component for the SDX-L3 application.
+ */
+@Component(immediate = true)
+public class SdxL3 {
+
+ public static final String SDX_L3_APP = "org.onosproject.sdxl3";
+
+ 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 NetworkConfigService networkConfigService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected InterfaceService interfaceService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected IntentSynchronizationService intentSynchronizer;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected IntentSynchronizationAdminService intentSynchronizerAdmin;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ComponentService componentService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected PacketService packetService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected EdgePortService edgeService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected HostService hostService;
+
+ protected ArpHandler arpHandler = null;
+
+ private InternalPacketProcessor processor = null;
+
+ private ApplicationId appId;
+
+ private static List<String> components = new ArrayList<>();
+ static {
+ components.add("org.onosproject.routing.bgp.BgpSessionManager");
+ components.add("org.onosproject.routing.impl.Router");
+ components.add(org.onosproject.sdxl3.impl.PeerConnectivityManager.class.getName());
+ components.add(org.onosproject.sdxl3.SdxL3Fib.class.getName());
+ }
+
+ @Activate
+ protected void activate() {
+ components.forEach(name -> componentService.activate(appId, name));
+
+ appId = coreService.registerApplication(SDX_L3_APP);
+
+ ApplicationId routerAppId = coreService.getAppId(RoutingService.ROUTER_APP_ID);
+ BgpConfig bgpConfig =
+ networkConfigService.getConfig(routerAppId, RoutingService.CONFIG_CLASS);
+
+ arpHandler = new ArpHandler(bgpConfig,
+ edgeService,
+ hostService,
+ packetService,
+ interfaceService);
+ processor = new InternalPacketProcessor();
+ packetService.addProcessor(processor, PacketProcessor.director(2));
+
+ // TODO fix removing intents
+ applicationService.registerDeactivateHook(appId,
+ intentSynchronizerAdmin::removeIntents);
+
+ log.info("SDX-L3 started");
+ }
+
+ @Deactivate
+ protected void deactivate() {
+ components.forEach(name -> componentService.deactivate(appId, name));
+
+ packetService.removeProcessor(processor);
+
+ log.info("SDX-L3 stopped");
+ }
+
+ private class InternalPacketProcessor implements PacketProcessor {
+ @Override
+ public void process(PacketContext context) {
+
+ if (context.isHandled()) {
+ return;
+ }
+
+ InboundPacket pkt = context.inPacket();
+ Ethernet ethernet = pkt.parsed();
+ if (ethernet.getEtherType() == Ethernet.TYPE_ARP) {
+ arpHandler.processPacketIn(pkt);
+ }
+ }
+ }
+
+}
diff --git a/sdx-l3/src/main/java/org/onosproject/sdxl3/SdxL3ArpHandler.java b/sdx-l3/src/main/java/org/onosproject/sdxl3/SdxL3ArpHandler.java
new file mode 100644
index 0000000..795d301
--- /dev/null
+++ b/sdx-l3/src/main/java/org/onosproject/sdxl3/SdxL3ArpHandler.java
@@ -0,0 +1,535 @@
+/*
+ * Copyright 2016 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.sdxl3;
+
+import org.onlab.packet.ARP;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.ICMP6;
+import org.onlab.packet.IPv6;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.Ip6Address;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+import org.onlab.packet.ndp.NeighborAdvertisement;
+import org.onlab.packet.ndp.NeighborDiscoveryOptions;
+import org.onlab.packet.ndp.NeighborSolicitation;
+import org.onosproject.incubator.net.intf.Interface;
+import org.onosproject.incubator.net.intf.InterfaceService;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.Host;
+import org.onosproject.net.edge.EdgePortService;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.host.HostService;
+import org.onosproject.net.packet.DefaultOutboundPacket;
+import org.onosproject.net.packet.InboundPacket;
+import org.onosproject.net.packet.PacketService;
+import org.onosproject.routing.config.BgpConfig;
+
+import java.nio.ByteBuffer;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.net.HostId.hostId;
+import static org.onosproject.security.AppGuard.checkPermission;
+import static org.onosproject.security.AppPermission.Type.PACKET_WRITE;
+
+public class SdxL3ArpHandler {
+
+ private static final String REQUEST_NULL = "ARP or NDP request cannot be null.";
+ private static final String MSG_NOT_REQUEST = "Message is not an ARP or NDP request";
+
+ private BgpConfig bgpConfig;
+ private EdgePortService edgeService;
+ private HostService hostService;
+ private PacketService packetService;
+ private InterfaceService interfaceService;
+
+ private enum Protocol {
+ ARP, NDP
+ }
+
+ private enum MessageType {
+ REQUEST, REPLY
+ }
+
+ /**
+ * Creates an ArpHandler object.
+ *
+ */
+ public SdxL3ArpHandler(BgpConfig bgpConfig,
+ EdgePortService edgeService,
+ HostService hostService,
+ PacketService packetService,
+ InterfaceService interfaceService) {
+ this.bgpConfig = bgpConfig;
+ this.edgeService = edgeService;
+ this.hostService = hostService;
+ this.packetService = packetService;
+ this.interfaceService = interfaceService;
+ }
+
+ /**
+ * Processes incoming ARP packets.
+ *
+ * @param pkt incoming packet
+ */
+ public void processPacketIn(InboundPacket pkt) {
+ checkPermission(PACKET_WRITE);
+
+ Ethernet eth = pkt.parsed();
+ checkNotNull(eth, REQUEST_NULL);
+
+ ConnectPoint inPort = pkt.receivedFrom();
+ MessageContext context = createContext(eth, inPort);
+ if (context != null) {
+ replyInternal(context);
+ }
+ }
+
+ /**
+ * Handles a request message also when it concerns an SDX peering address.
+ *
+ * If the MAC address of the target is known, we can reply directly to the
+ * requestor. Otherwise, we forward the request out other ports in an
+ * attempt to find the correct host.
+ *
+ * @param context request message context to process
+ */
+ private void replyInternal(MessageContext context) {
+ checkNotNull(context);
+ checkArgument(context.type() == MessageType.REQUEST, MSG_NOT_REQUEST);
+
+ if (hasIpAddress(context.inPort())) {
+ // If the request came from outside the network, only reply if it was
+ // for one of our external addresses.
+
+ interfaceService.getInterfacesByPort(context.inPort())
+ .stream()
+ .filter(intf -> intf.ipAddresses()
+ .stream()
+ .anyMatch(ia -> ia.ipAddress().equals(context.target())))
+ .forEach(intf -> buildAndSendReply(context, intf.mac()));
+
+ if (!isPeerAddress(context.sender()) || !isPeerAddress(context.target())) {
+ // Only care about requests from/towards external BGP peers
+ return;
+ }
+ }
+
+ // See if we have the target host in the host store
+ Set<Host> hosts = hostService.getHostsByIp(context.target());
+
+ Host dst = null;
+ Host src = hostService.getHost(hostId(context.srcMac(), context.vlan()));
+
+ for (Host host : hosts) {
+ if (host.vlan().equals(context.vlan())) {
+ dst = host;
+ break;
+ }
+ }
+
+ if (src != null && dst != null) {
+ // We know the target host so we can respond
+ buildAndSendReply(context, dst.mac());
+ return;
+ }
+
+ // If the source address matches one of our external addresses
+ // it could be a request from an internal host to an external
+ // address. Forward it over to the correct port.
+ boolean matched = false;
+ Set<Interface> interfaces = interfaceService.getInterfacesByIp(context.sender());
+ for (Interface intf : interfaces) {
+ if (intf.vlan().equals(context.vlan())) {
+ matched = true;
+ sendTo(context.packet(), intf.connectPoint());
+ break;
+ }
+ }
+
+ if (matched) {
+ return;
+ }
+
+ // If the packets has a vlanId look if there are some other
+ // interfaces in the configuration on the same vlan and broadcast
+ // the packet out just of through those interfaces.
+ VlanId vlanId = context.vlan();
+
+ Set<Interface> filteredVlanInterfaces =
+ filterVlanInterfacesNoIp(interfaceService.getInterfacesByVlan(vlanId));
+
+ if (vlanId != null
+ && !vlanId.equals(VlanId.NONE)
+ && confContainsVlans(vlanId, context.inPort())) {
+ vlanFlood(context.packet(), filteredVlanInterfaces, context.inPort);
+ return;
+ }
+
+ // The request couldn't be resolved.
+ // Flood the request on all ports except the incoming port.
+ flood(context.packet(), context.inPort());
+ }
+
+ private boolean isPeerAddress(IpAddress ip) {
+ return bgpConfig.bgpSpeakers()
+ .stream()
+ .flatMap(speaker -> speaker.peers().stream())
+ .anyMatch(peerAddress -> peerAddress.equals(ip));
+ }
+
+ private Set<Interface> filterVlanInterfacesNoIp(Set<Interface> vlanInterfaces) {
+ return vlanInterfaces
+ .stream()
+ .filter(intf -> intf.ipAddresses().isEmpty())
+ .collect(Collectors.toSet());
+ }
+
+ /**
+ * States if the interface configuration contains more than one interface configured
+ * on a specific vlan, including the interface passed as argument.
+ *
+ * @param vlanId the vlanid to look for in the interface configuration
+ * @param connectPoint the connect point to exclude from the search
+ * @return true if interfaces are found. False otherwise
+ */
+ private boolean confContainsVlans(VlanId vlanId, ConnectPoint connectPoint) {
+ Set<Interface> vlanInterfaces = interfaceService.getInterfacesByVlan(vlanId);
+ return interfaceService.getInterfacesByVlan(vlanId)
+ .stream()
+ .anyMatch(intf -> intf.connectPoint().equals(connectPoint) && intf.ipAddresses().isEmpty())
+ && vlanInterfaces.size() > 1;
+ }
+
+ /**
+ * Builds and sends a reply message given a request context and the resolved
+ * MAC address to answer with.
+ *
+ * @param context message context of request
+ * @param targetMac MAC address to be given in the response
+ */
+ private void buildAndSendReply(MessageContext context, MacAddress targetMac) {
+ switch (context.protocol()) {
+ case ARP:
+ sendTo(ARP.buildArpReply((Ip4Address) context.target(),
+ targetMac, context.packet()), context.inPort());
+ break;
+ case NDP:
+ sendTo(buildNdpReply((Ip6Address) context.target(), targetMac,
+ context.packet()), context.inPort());
+ break;
+ default:
+ break;
+ }
+ }
+
+ /**
+ * Outputs a packet out a specific port.
+ *
+ * @param packet the packet to send
+ * @param outPort the port to send it out
+ */
+ private void sendTo(Ethernet packet, ConnectPoint outPort) {
+ sendTo(outPort, ByteBuffer.wrap(packet.serialize()));
+ }
+
+ /**
+ * Outputs a packet out a specific port.
+ *
+ * @param outPort port to send it out
+ * @param packet packet to send
+ */
+ private void sendTo(ConnectPoint outPort, ByteBuffer packet) {
+ if (!edgeService.isEdgePoint(outPort)) {
+ // Sanity check to make sure we don't send the packet out an
+ // internal port and create a loop (could happen due to
+ // misconfiguration).
+ return;
+ }
+
+ TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder();
+ builder.setOutput(outPort.port());
+ packetService.emit(new DefaultOutboundPacket(outPort.deviceId(),
+ builder.build(), packet));
+ }
+
+ /**
+ * Returns whether the given port has any IP addresses configured or not.
+ *
+ * @param connectPoint the port to check
+ * @return true if the port has at least one IP address configured,
+ * false otherwise
+ */
+ private boolean hasIpAddress(ConnectPoint connectPoint) {
+ return interfaceService.getInterfacesByPort(connectPoint)
+ .stream()
+ .flatMap(intf -> intf.ipAddresses().stream())
+ .findAny()
+ .isPresent();
+ }
+
+ /**
+ * Returns whether the given port has any VLAN configured or not.
+ *
+ * @param connectPoint the port to check
+ * @return true if the port has at least one VLAN configured,
+ * false otherwise
+ */
+ private boolean hasVlan(ConnectPoint connectPoint) {
+ return interfaceService.getInterfacesByPort(connectPoint)
+ .stream()
+ .filter(intf -> !intf.vlan().equals(VlanId.NONE))
+ .findAny()
+ .isPresent();
+ }
+
+ /**
+ * Flood the arp request at all edges on a specifc VLAN.
+ *
+ * @param request the arp request
+ * @param dsts the destination interfaces
+ * @param inPort the connect point the arp request was received on
+ */
+ private void vlanFlood(Ethernet request, Set<Interface> dsts, ConnectPoint inPort) {
+ TrafficTreatment.Builder builder = null;
+ ByteBuffer buf = ByteBuffer.wrap(request.serialize());
+
+ for (Interface intf : dsts) {
+ ConnectPoint cPoint = intf.connectPoint();
+ if (cPoint.equals(inPort)) {
+ continue;
+ }
+
+ builder = DefaultTrafficTreatment.builder();
+ builder.setOutput(cPoint.port());
+ packetService.emit(new DefaultOutboundPacket(cPoint.deviceId(),
+ builder.build(), buf));
+ }
+ }
+
+ /**
+ * Flood the arp request at all edges in the network.
+ *
+ * @param request the arp request
+ * @param inPort the connect point the arp request was received on
+ */
+ private void flood(Ethernet request, ConnectPoint inPort) {
+ TrafficTreatment.Builder builder = null;
+ ByteBuffer buf = ByteBuffer.wrap(request.serialize());
+
+ for (ConnectPoint connectPoint : edgeService.getEdgePoints()) {
+ if (hasIpAddress(connectPoint)
+ || hasVlan(connectPoint)
+ || connectPoint.equals(inPort)) {
+ continue;
+ }
+
+ builder = DefaultTrafficTreatment.builder();
+ builder.setOutput(connectPoint.port());
+ packetService.emit(new DefaultOutboundPacket(connectPoint.deviceId(),
+ builder.build(), buf));
+ }
+ }
+
+ /**
+ * Builds an Neighbor Discovery reply based on a request.
+ *
+ * @param srcIp the IP address to use as the reply source
+ * @param srcMac the MAC address to use as the reply source
+ * @param request the Neighbor Solicitation request we got
+ * @return an Ethernet frame containing the Neighbor Advertisement reply
+ */
+ private Ethernet buildNdpReply(Ip6Address srcIp, MacAddress srcMac,
+ Ethernet request) {
+ Ethernet eth = new Ethernet();
+ eth.setDestinationMACAddress(request.getSourceMAC());
+ eth.setSourceMACAddress(srcMac);
+ eth.setEtherType(Ethernet.TYPE_IPV6);
+ eth.setVlanID(request.getVlanID());
+
+ IPv6 requestIp = (IPv6) request.getPayload();
+ IPv6 ipv6 = new IPv6();
+ ipv6.setSourceAddress(srcIp.toOctets());
+ ipv6.setDestinationAddress(requestIp.getSourceAddress());
+ ipv6.setHopLimit((byte) 255);
+
+ ICMP6 icmp6 = new ICMP6();
+ icmp6.setIcmpType(ICMP6.NEIGHBOR_ADVERTISEMENT);
+ icmp6.setIcmpCode((byte) 0);
+
+ NeighborAdvertisement nadv = new NeighborAdvertisement();
+ nadv.setTargetAddress(srcIp.toOctets());
+ nadv.setSolicitedFlag((byte) 1);
+ nadv.setOverrideFlag((byte) 1);
+ nadv.addOption(NeighborDiscoveryOptions.TYPE_TARGET_LL_ADDRESS,
+ srcMac.toBytes());
+
+ icmp6.setPayload(nadv);
+ ipv6.setPayload(icmp6);
+ eth.setPayload(ipv6);
+ return eth;
+ }
+
+ /**
+ * Attempts to create a MessageContext for the given Ethernet frame. If the
+ * frame is a valid ARP or NDP request or response, a context will be
+ * created.
+ *
+ * @param eth input Ethernet frame
+ * @param inPort in port
+ * @return MessageContext if the packet was ARP or NDP, otherwise null
+ */
+ private MessageContext createContext(Ethernet eth, ConnectPoint inPort) {
+ if (eth.getEtherType() == Ethernet.TYPE_ARP) {
+ return createArpContext(eth, inPort);
+ } else if (eth.getEtherType() == Ethernet.TYPE_IPV6) {
+ return createNdpContext(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);
+ }
+
+ /**
+ * Extracts context information from NDP packets.
+ *
+ * @param eth input Ethernet frame that is thought to be NDP
+ * @param inPort in port
+ * @return MessageContext object if the packet was a valid NDP packet,
+ * otherwise null
+ */
+ private MessageContext createNdpContext(Ethernet eth, ConnectPoint inPort) {
+ if (eth.getEtherType() != Ethernet.TYPE_IPV6) {
+ return null;
+ }
+ IPv6 ipv6 = (IPv6) eth.getPayload();
+
+ if (ipv6.getNextHeader() != IPv6.PROTOCOL_ICMP6) {
+ return null;
+ }
+ ICMP6 icmpv6 = (ICMP6) ipv6.getPayload();
+
+ IpAddress sender = Ip6Address.valueOf(ipv6.getSourceAddress());
+ IpAddress target = null;
+
+ MessageType type;
+ if (icmpv6.getIcmpType() == ICMP6.NEIGHBOR_SOLICITATION) {
+ type = MessageType.REQUEST;
+ NeighborSolicitation nsol = (NeighborSolicitation) icmpv6.getPayload();
+ target = Ip6Address.valueOf(nsol.getTargetAddress());
+ } else if (icmpv6.getIcmpType() == ICMP6.NEIGHBOR_ADVERTISEMENT) {
+ type = MessageType.REPLY;
+ } else {
+ return null;
+ }
+
+ return new MessageContext(eth, inPort, Protocol.NDP, type, target, sender);
+ }
+
+ /**
+ * Provides context information for a particular ARP or NDP message, with
+ * a unified interface to access data regardless of protocol.
+ */
+ 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;
+ }
+ }
+
+}
diff --git a/sdx-l3/src/main/java/org/onosproject/sdxl3/SdxL3Fib.java b/sdx-l3/src/main/java/org/onosproject/sdxl3/SdxL3Fib.java
new file mode 100644
index 0000000..68829e1
--- /dev/null
+++ b/sdx-l3/src/main/java/org/onosproject/sdxl3/SdxL3Fib.java
@@ -0,0 +1,253 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.sdxl3;
+
+import com.google.common.collect.ImmutableList;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.incubator.net.intf.Interface;
+import org.onosproject.incubator.net.intf.InterfaceService;
+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.Constraint;
+import org.onosproject.net.intent.Key;
+import org.onosproject.net.intent.MultiPointToSinglePointIntent;
+import org.onosproject.net.intent.constraint.PartialFailureConstraint;
+import org.onosproject.routing.FibListener;
+import org.onosproject.routing.FibUpdate;
+import org.onosproject.routing.IntentSynchronizationService;
+import org.onosproject.routing.RoutingService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+/**
+ * FIB component of SDX-L3.
+ */
+@Component(immediate = true, enabled = false)
+public class SdxL3Fib {
+ private Logger log = LoggerFactory.getLogger(getClass());
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected InterfaceService interfaceService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected IntentSynchronizationService intentSynchronizer;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected CoreService coreService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected RoutingService routingService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected PeerConnectivityService peerConnectivity;
+
+ private final InternalFibListener fibListener = new InternalFibListener();
+
+ private static final int PRIORITY_OFFSET = 100;
+ private static final int PRIORITY_MULTIPLIER = 5;
+ protected static final ImmutableList<Constraint> CONSTRAINTS
+ = ImmutableList.of(new PartialFailureConstraint());
+
+ private final Map<IpPrefix, MultiPointToSinglePointIntent> routeIntents
+ = new ConcurrentHashMap<>();
+
+ private ApplicationId appId;
+
+ @Activate
+ public void activate() {
+ appId = coreService.getAppId(SdxL3.SDX_L3_APP);
+
+ routingService.addFibListener(fibListener);
+ routingService.start();
+ }
+
+ @Deactivate
+ public void deactivate() {
+ // TODO remove listener
+ routingService.stop();
+ }
+
+ private void update(Collection<FibUpdate> updates, Collection<FibUpdate> withdraws) {
+ int submitCount = 0, withdrawCount = 0;
+ //
+ // NOTE: Semantically, we MUST withdraw existing intents before
+ // submitting new intents.
+ //
+ synchronized (this) {
+ MultiPointToSinglePointIntent intent;
+
+ //
+ // Prepare the Intent batch operations for the intents to withdraw
+ //
+ for (FibUpdate withdraw : withdraws) {
+ checkArgument(withdraw.type() == FibUpdate.Type.DELETE,
+ "FibUpdate with wrong type in withdraws list");
+
+ IpPrefix prefix = withdraw.entry().prefix();
+ intent = routeIntents.remove(prefix);
+ if (intent == null) {
+ log.trace("SDX-L3 No intent in routeIntents to delete " +
+ "for prefix: {}", prefix);
+ continue;
+ }
+ intentSynchronizer.withdraw(intent);
+ withdrawCount++;
+ }
+
+ //
+ // Prepare the Intent batch operations for the intents to submit
+ //
+ for (FibUpdate update : updates) {
+ checkArgument(update.type() == FibUpdate.Type.UPDATE,
+ "FibUpdate with wrong type in updates list");
+
+ IpPrefix prefix = update.entry().prefix();
+ intent = generateRouteIntent(prefix, update.entry().nextHopIp(),
+ update.entry().nextHopMac());
+
+ if (intent == null) {
+ // This preserves the old semantics - if an intent can't be
+ // generated, we don't do anything with that prefix. But
+ // perhaps we should withdraw the old intent anyway?
+ continue;
+ }
+
+ routeIntents.put(prefix, intent);
+ intentSynchronizer.submit(intent);
+ submitCount++;
+ }
+
+ log.debug("SDX-L3 submitted {}/{}, withdrew = {}/{}", submitCount,
+ updates.size(), withdrawCount, withdraws.size());
+ }
+ }
+
+ /**
+ * Generates a route intent for a prefix, the next hop IP address, and
+ * the next hop MAC address.
+ * <p/>
+ * This method will find the egress interface for the intent.
+ * Intent will match dst IP prefix and rewrite dst MAC address at all other
+ * border switches, then forward packets according to dst MAC address.
+ *
+ * @param prefix IP prefix of the route to add
+ * @param nextHopIpAddress IP address of the next hop
+ * @param nextHopMacAddress MAC address of the next hop
+ * @return the generated intent, or null if no intent should be submitted
+ */
+ private MultiPointToSinglePointIntent generateRouteIntent(
+ IpPrefix prefix,
+ IpAddress nextHopIpAddress,
+ MacAddress nextHopMacAddress) {
+
+ // Find the attachment point (egress interface) of the next hop
+ Interface egressInterface = peerConnectivity.getInterfaceForPeer(nextHopIpAddress);
+
+ if (egressInterface == null) {
+ log.warn("No outgoing interface found for {}",
+ nextHopIpAddress);
+ return null;
+ }
+
+ // Generate the intent itself
+ Set<ConnectPoint> ingressPorts = new HashSet<>();
+ ConnectPoint egressPort = egressInterface.connectPoint();
+ log.debug("Generating intent for prefix {}, next hop mac {}",
+ prefix, nextHopMacAddress);
+
+ for (Interface intf : interfaceService.getInterfaces()) {
+ // TODO this should be only peering interfaces
+ if (!intf.connectPoint().equals(egressInterface.connectPoint())) {
+ ConnectPoint srcPort = intf.connectPoint();
+ ingressPorts.add(srcPort);
+ }
+ }
+
+ // Match the destination IP prefix at the first hop
+ TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
+ if (prefix.isIp4()) {
+ selector.matchEthType(Ethernet.TYPE_IPV4);
+ // if it is default route, then we do not need match destination
+ // IP address
+ if (prefix.prefixLength() != 0) {
+ selector.matchIPDst(prefix);
+ }
+ } else {
+ selector.matchEthType(Ethernet.TYPE_IPV6);
+ // if it is default route, then we do not need match destination
+ // IP address
+ if (prefix.prefixLength() != 0) {
+ selector.matchIPv6Dst(prefix);
+ }
+
+ }
+
+ // Rewrite the destination MAC address
+ TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder()
+ .setEthDst(nextHopMacAddress);
+ if (!egressInterface.vlan().equals(VlanId.NONE)) {
+ treatment.setVlanId(egressInterface.vlan());
+ // If we set VLAN ID, we have to make sure a VLAN tag exists.
+ // TODO support no VLAN -> VLAN routing
+ selector.matchVlanId(VlanId.ANY);
+ }
+
+ int priority =
+ prefix.prefixLength() * PRIORITY_MULTIPLIER + PRIORITY_OFFSET;
+ Key key = Key.of(prefix.toString(), appId);
+ return MultiPointToSinglePointIntent.builder()
+ .appId(appId)
+ .key(key)
+ .selector(selector.build())
+ .treatment(treatment.build())
+ .ingressPoints(ingressPorts)
+ .egressPoint(egressPort)
+ .priority(priority)
+ .constraints(CONSTRAINTS)
+ .build();
+ }
+
+ private class InternalFibListener implements FibListener {
+ @Override
+ public void update(Collection<FibUpdate> updates, Collection<FibUpdate> withdraws) {
+ SdxL3Fib.this.update(updates, withdraws);
+ }
+ }
+
+}
diff --git a/sdx-l3/src/main/java/org/onosproject/sdxl3/SdxL3PeerService.java b/sdx-l3/src/main/java/org/onosproject/sdxl3/SdxL3PeerService.java
new file mode 100644
index 0000000..6a2542c
--- /dev/null
+++ b/sdx-l3/src/main/java/org/onosproject/sdxl3/SdxL3PeerService.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2014-2016 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.sdxl3;
+
+import org.onlab.packet.IpAddress;
+import org.onosproject.incubator.net.intf.Interface;
+
+/*
+ * Service for managing peers connections
+ */
+public interface SdxL3PeerService {
+
+ String CONFIG_KEY = "bgpPeers";
+
+ /**
+ * Returns the interface used as connection point to peer.
+ *
+ * @param peeringAddress IP address of peer
+ * @return interface to the peer
+ */
+ Interface getInterfaceForPeer(IpAddress peeringAddress);
+}
diff --git a/sdx-l3/src/main/java/org/onosproject/sdxl3/cli/BgpPeersListCommand.java b/sdx-l3/src/main/java/org/onosproject/sdxl3/cli/BgpPeersListCommand.java
new file mode 100644
index 0000000..09f237b
--- /dev/null
+++ b/sdx-l3/src/main/java/org/onosproject/sdxl3/cli/BgpPeersListCommand.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.sdxl3.cli;
+
+import com.google.common.collect.Lists;
+import org.apache.karaf.shell.commands.Command;
+import org.onlab.packet.IpAddress;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.net.config.NetworkConfigService;
+import org.onosproject.routing.RoutingService;
+import org.onosproject.routing.config.BgpConfig;
+import org.onosproject.sdxl3.SdxL3;
+import org.onosproject.sdxl3.config.BgpPeersConfig;
+
+import java.util.Comparator;
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * Lists the BGP peers configured in the system.
+ */
+@Command(scope = "onos", name = "bgp-peers",
+ description = "Lists all BGP peers")
+public class BgpPeersListCommand extends AbstractShellCommand {
+
+ private static final String FORMAT = "ip=%s, interface=%s";
+ private static final String NAME_FORMAT = "%s: " + FORMAT;
+ private static final String AUTO_SELECTION = "auto";
+ public static final String NO_PEERS = "No peers configured";
+
+ private static final Comparator<BgpPeersConfig.PeerConfig> PEER_COMPARATOR =
+ Comparator.comparing(p -> p.ip());
+ public static final String EMPTY = "";
+
+ @Override
+ protected void execute() {
+ NetworkConfigService configService = get(NetworkConfigService.class);
+ CoreService coreService = get(CoreService.class);
+
+ ApplicationId routerAppId = coreService.getAppId(RoutingService.ROUTER_APP_ID);
+ BgpConfig bgpConfig = configService.getConfig(routerAppId, RoutingService.CONFIG_CLASS);
+
+ ApplicationId sdxL3AppId = coreService.getAppId(SdxL3.SDX_L3_APP);
+ BgpPeersConfig peersConfig = configService.
+ getConfig(sdxL3AppId, BgpPeersConfig.class);
+
+ if (bgpConfig == null && peersConfig == null) {
+ print(NO_PEERS);
+ return;
+ }
+
+ List<IpAddress> peeringAddresses = Lists.newArrayList();
+ if (bgpConfig != null) {
+ peeringAddresses = getPeeringAddresses(bgpConfig);
+ }
+
+ List<BgpPeersConfig.PeerConfig> bgpPeers =
+ Lists.newArrayList();
+ if (peersConfig != null) {
+ bgpPeers.addAll(peersConfig.bgpPeers());
+ }
+
+ bgpPeers = mergePeers(peeringAddresses, bgpPeers);
+
+ if (bgpPeers.isEmpty()) {
+ print(NO_PEERS);
+ return;
+ }
+
+ bgpPeers.sort(PEER_COMPARATOR);
+ bgpPeers.forEach(p -> {
+ if (p.name().isPresent()) {
+ if (p.interfaceName() != EMPTY) {
+ print(NAME_FORMAT, p.name().get(), p.ip(), p.interfaceName());
+ } else {
+ print(NAME_FORMAT, p.name().get(), p.ip(), AUTO_SELECTION);
+ }
+ } else if (p.interfaceName() != EMPTY) {
+ print(FORMAT, p.ip(), p.interfaceName());
+ } else {
+ print(FORMAT, p.ip(), AUTO_SELECTION);
+ }
+ });
+ }
+
+ private List<IpAddress> getPeeringAddresses(BgpConfig bgpConfig) {
+ List<IpAddress> peeringAddresses = Lists.newArrayList();
+
+ List<BgpConfig.BgpSpeakerConfig> bgpSpeakers =
+ Lists.newArrayList(bgpConfig.bgpSpeakers());
+ bgpSpeakers.forEach(
+ s -> peeringAddresses.addAll(s.peers()));
+
+ return peeringAddresses;
+ }
+
+ private List<BgpPeersConfig.PeerConfig> mergePeers(
+ List<IpAddress> peeringAddresses,
+ List<BgpPeersConfig.PeerConfig> bgpPeers) {
+ peeringAddresses.forEach(a -> {
+ boolean exists = bgpPeers.stream()
+ .filter(p -> p.ip().equals(a))
+ .findAny().isPresent();
+ if (!exists) {
+ bgpPeers.add(new BgpPeersConfig
+ .PeerConfig(Optional.<String>empty(), a, EMPTY));
+ }
+ });
+
+ return bgpPeers;
+ }
+}
diff --git a/sdx-l3/src/main/java/org/onosproject/sdxl3/cli/package-info.java b/sdx-l3/src/main/java/org/onosproject/sdxl3/cli/package-info.java
new file mode 100644
index 0000000..a5216de
--- /dev/null
+++ b/sdx-l3/src/main/java/org/onosproject/sdxl3/cli/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * SDX-L3 command-line handlers.
+ */
+package org.onosproject.sdxl3.cli;
diff --git a/sdx-l3/src/main/java/org/onosproject/sdxl3/config/BgpPeersConfig.java b/sdx-l3/src/main/java/org/onosproject/sdxl3/config/BgpPeersConfig.java
new file mode 100644
index 0000000..dd89aed
--- /dev/null
+++ b/sdx-l3/src/main/java/org/onosproject/sdxl3/config/BgpPeersConfig.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.sdxl3.config;
+
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.google.common.collect.Sets;
+import org.onlab.packet.IpAddress;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.net.config.Config;
+
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+public class BgpPeersConfig extends Config<ApplicationId> {
+ public static final String NAME = "name";
+ public static final String IP = "ip";
+ public static final String INTERFACE = "interface";
+
+ /**
+ * Gets the set of configured BGP peers.
+ *
+ * @return BGP peers
+ */
+ public Set<PeerConfig> bgpPeers() {
+ Set<PeerConfig> peers = Sets.newHashSet();
+
+ ArrayNode peersNode = array;
+ peersNode.forEach(jsonNode -> {
+ Optional<String> name;
+ if (jsonNode.get(NAME) == null) {
+ name = Optional.empty();
+ } else {
+ name = Optional.of(jsonNode.get(NAME).asText());
+ }
+
+ peers.add(new PeerConfig(name,
+ IpAddress.valueOf(jsonNode.path(IP).asText()),
+ jsonNode.path(INTERFACE).asText()));
+ });
+
+ return peers;
+ }
+
+ /**
+ * Gets the interface name configured for a given BGP peer.
+ *
+ * @param peerAddress IP address of the peer
+ * @return interface name
+ */
+
+ public String getInterfaceNameForPeer(IpAddress peerAddress) {
+ Optional<PeerConfig> match = bgpPeers()
+ .stream()
+ .filter(p -> p.ip().equals(peerAddress))
+ .findAny();
+
+ if (match.isPresent()) {
+ return match.get().interfaceName();
+ } else {
+ return null;
+ }
+ }
+
+
+ /**
+ * Configuration for a BGP peer.
+ */
+ public static class PeerConfig {
+
+ private Optional<String> name;
+ private IpAddress ip;
+ private String interfaceName;
+
+ public PeerConfig(Optional<String> name, IpAddress ip, String interfaceName) {
+ this.name = checkNotNull(name);
+ this.ip = checkNotNull(ip);
+ this.interfaceName = checkNotNull(interfaceName);
+ }
+
+ public Optional<String> name() {
+ return name;
+ }
+
+ public IpAddress ip() {
+ return ip;
+ }
+
+ public String interfaceName() {
+ return interfaceName;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof PeerConfig) {
+ final PeerConfig that = (PeerConfig) obj;
+ return Objects.equals(this.name, that.name) &&
+ Objects.equals(this.ip, that.ip) &&
+ Objects.equals(this.interfaceName, that.interfaceName);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(name, ip, interfaceName);
+ }
+ }
+}
diff --git a/sdx-l3/src/main/java/org/onosproject/sdxl3/config/package-info.java b/sdx-l3/src/main/java/org/onosproject/sdxl3/config/package-info.java
new file mode 100644
index 0000000..ebcfa19
--- /dev/null
+++ b/sdx-l3/src/main/java/org/onosproject/sdxl3/config/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * BGP peers configuration interfaces.
+ */
+package org.onosproject.sdxl3.config;
\ No newline at end of file
diff --git a/sdx-l3/src/main/java/org/onosproject/sdxl3/impl/SdxL3PeerManager.java b/sdx-l3/src/main/java/org/onosproject/sdxl3/impl/SdxL3PeerManager.java
new file mode 100644
index 0000000..b881ca2
--- /dev/null
+++ b/sdx-l3/src/main/java/org/onosproject/sdxl3/impl/SdxL3PeerManager.java
@@ -0,0 +1,460 @@
+/*
+ * Copyright 2016 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.sdxl3.impl;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.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.TpPort;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.incubator.net.intf.Interface;
+import org.onosproject.incubator.net.intf.InterfaceService;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.config.ConfigFactory;
+import org.onosproject.net.config.NetworkConfigEvent;
+import org.onosproject.net.config.NetworkConfigListener;
+import org.onosproject.net.config.NetworkConfigRegistry;
+import org.onosproject.net.config.NetworkConfigService;
+import org.onosproject.net.config.basics.SubjectFactories;
+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.host.InterfaceIpAddress;
+import org.onosproject.net.intent.IntentUtils;
+import org.onosproject.net.intent.Key;
+import org.onosproject.net.intent.PointToPointIntent;
+import org.onosproject.routing.IntentSynchronizationService;
+import org.onosproject.routing.RoutingService;
+import org.onosproject.routing.config.BgpConfig;
+import org.onosproject.sdxl3.PeerConnectivityService;
+import org.onosproject.sdxl3.SdxL3;
+import org.onosproject.sdxl3.config.BgpPeersConfig;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Manages the connectivity requirements between peers.
+ */
+@Service
+@Component(immediate = true, enabled = false)
+public class SdxL3PeerManager implements SdxL3PeerService {
+
+ private static final String CONFIG_KEY = "bgpPeers";
+
+ private static final int PRIORITY_OFFSET = 1000;
+
+ 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(
+ PeerConnectivityManager.class);
+
+ private static final short BGP_PORT = 179;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected CoreService coreService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected NetworkConfigService configService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected InterfaceService interfaceService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected IntentSynchronizationService intentSynchronizer;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected NetworkConfigRegistry registry;
+
+ private ConfigFactory configFactory =
+ new ConfigFactory(SubjectFactories.APP_SUBJECT_FACTORY,
+ BgpPeersConfig.class, CONFIG_KEY) {
+ @Override
+ public BgpPeersConfig createConfig() {
+ return new BgpPeersConfig();
+ }
+ };
+
+ private ApplicationId sdxAppId;
+ private ApplicationId routerAppId;
+
+ private final Map<Key, PointToPointIntent> peerIntents = new HashMap<>();
+
+ private final InternalNetworkConfigListener configListener
+ = new InternalNetworkConfigListener();
+
+ @Activate
+ public void activate() {
+ sdxAppId = coreService.getAppId(SdxL3.SDX_L3_APP);
+ routerAppId = coreService.getAppId(RoutingService.ROUTER_APP_ID);
+
+ registry.registerConfigFactory(configFactory);
+
+ configService.addListener(configListener);
+
+ setUpConnectivity();
+
+ log.info("Connectivity with BGP peers established");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ configService.removeListener(configListener);
+
+ log.info("Connectivity with BGP peers stopped");
+ }
+
+ /**
+ * Sets up paths to establish connectivity between all internal
+ * BGP speakers and external BGP peers.
+ */
+ private void setUpConnectivity() {
+ BgpConfig config = configService.getConfig(routerAppId, RoutingService.CONFIG_CLASS);
+
+ if (config == null) {
+ log.warn("No BgpConfig found");
+ return;
+ }
+
+ Map<Key, PointToPointIntent> existingIntents = new HashMap<>(peerIntents);
+
+ for (BgpConfig.BgpSpeakerConfig bgpSpeaker : config.bgpSpeakers()) {
+ log.debug("Start to set up BGP paths for BGP speaker: {}",
+ bgpSpeaker);
+
+ buildSpeakerIntents(bgpSpeaker).forEach(i -> {
+ PointToPointIntent intent = existingIntents.remove(i.key());
+ if (intent == null || !IntentUtils.intentsAreEqual(i, intent)) {
+ peerIntents.put(i.key(), i);
+ intentSynchronizer.submit(i);
+ }
+ });
+ }
+
+ // Remove any remaining intents that we used to have that we don't need
+ // anymore
+ existingIntents.values().forEach(i -> {
+ peerIntents.remove(i.key());
+ intentSynchronizer.withdraw(i);
+ });
+ }
+
+ private Collection<PointToPointIntent> buildSpeakerIntents(BgpConfig.BgpSpeakerConfig speaker) {
+ List<PointToPointIntent> intents = new ArrayList<>();
+
+ for (IpAddress peerAddress : speaker.peers()) {
+ Interface peeringInterface = getInterfaceForPeer(peerAddress);
+
+ if (peeringInterface == null) {
+ log.debug("No peering interface found for peer {} on speaker {}",
+ peerAddress, speaker);
+ continue;
+ }
+
+ IpAddress peeringAddress = null;
+ for (InterfaceIpAddress address : peeringInterface.ipAddresses()) {
+ if (address.subnetAddress().contains(peerAddress)) {
+ peeringAddress = address.ipAddress();
+ break;
+ }
+ }
+
+ checkNotNull(peeringAddress);
+
+ intents.addAll(buildIntents(speaker.connectPoint(), peeringAddress,
+ peeringInterface.connectPoint(), peerAddress));
+ }
+
+ return intents;
+ }
+
+ private Interface getConfiguredInterfaceForPeer(IpAddress peerAddress) {
+ if (sdxAppId == null) {
+ return null;
+ }
+
+ BgpPeersConfig config = configService.getConfig(sdxAppId, BgpPeersConfig.class);
+ if (config == null) {
+ return null;
+ }
+
+ String intfName = config.getInterfaceNameForPeer(peerAddress);
+ if (intfName != null) {
+ return interfaceService.getInterfaceByName(intfName);
+ }
+ return null;
+ }
+
+ /**
+ * 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 destination TCP port 179
+ selector = buildSelector(tcpProtocol,
+ ipOne,
+ ipTwo,
+ null,
+ BGP_PORT);
+
+ key = buildKey(ipOne, ipTwo, SUFFIX_DST);
+
+ intents.add(PointToPointIntent.builder()
+ .appId(sdxAppId)
+ .key(key)
+ .selector(selector)
+ .treatment(treatment)
+ .ingressPoint(portOne)
+ .egressPoint(portTwo)
+ .priority(PRIORITY_OFFSET)
+ .build());
+
+ // 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(sdxAppId)
+ .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(sdxAppId)
+ .key(key)
+ .selector(selector)
+ .treatment(treatment)
+ .ingressPoint(portTwo)
+ .egressPoint(portOne)
+ .priority(PRIORITY_OFFSET)
+ .build());
+
+ // Path from BGP peer to BGP speaker matching source TCP port 179
+ selector = buildSelector(tcpProtocol,
+ ipTwo,
+ ipOne,
+ BGP_PORT,
+ null);
+
+ key = buildKey(ipTwo, ipOne, SUFFIX_SRC);
+
+ intents.add(PointToPointIntent.builder()
+ .appId(sdxAppId)
+ .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(sdxAppId)
+ .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(sdxAppId)
+ .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, sdxAppId);
+ }
+
+ @Override
+ public Interface getInterfaceForPeer(IpAddress peeringAddress) {
+ Interface peeringInterface = getConfiguredInterfaceForPeer(peeringAddress);
+ if (peeringInterface == null) {
+ peeringInterface = interfaceService.getMatchingInterface(peeringAddress);
+ }
+ return peeringInterface;
+ }
+
+ private class InternalNetworkConfigListener implements NetworkConfigListener {
+
+ @Override
+ public void event(NetworkConfigEvent event) {
+ switch (event.type()) {
+ case CONFIG_REGISTERED:
+ break;
+ case CONFIG_UNREGISTERED:
+ break;
+ case CONFIG_ADDED:
+ case CONFIG_UPDATED:
+ case CONFIG_REMOVED:
+ if (event.configClass() == RoutingService.CONFIG_CLASS) {
+ setUpConnectivity();
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+}
diff --git a/sdx-l3/src/main/java/org/onosproject/sdxl3/impl/package-info.java b/sdx-l3/src/main/java/org/onosproject/sdxl3/impl/package-info.java
new file mode 100644
index 0000000..d172822
--- /dev/null
+++ b/sdx-l3/src/main/java/org/onosproject/sdxl3/impl/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2016 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.
+ */
+
+/**
+ * Implementation of BGP peers connectivity functionality.
+ */
+package org.onosproject.sdxl3.impl;
\ No newline at end of file
diff --git a/sdx-l3/src/main/java/org/onosproject/sdxl3/package-info.java b/sdx-l3/src/main/java/org/onosproject/sdxl3/package-info.java
new file mode 100644
index 0000000..eb7b599
--- /dev/null
+++ b/sdx-l3/src/main/java/org/onosproject/sdxl3/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * SDX-L3 application for GEANT.
+ */
+package org.onosproject.sdxl3;
diff --git a/sdx-l3/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/sdx-l3/src/main/resources/OSGI-INF/blueprint/shell-config.xml
new file mode 100644
index 0000000..4757f78
--- /dev/null
+++ b/sdx-l3/src/main/resources/OSGI-INF/blueprint/shell-config.xml
@@ -0,0 +1,23 @@
+<!--
+ ~ Copyright 2014-2015 Open Networking Laboratory
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
+ <command-bundle xmlns="http://karaf.apache.org/xmlns/shell/v1.1.0">
+ <command>
+ <action class="org.onosproject.sdxl3.cli.BgpPeersListCommand"/>
+ </command>
+ </command-bundle>
+</blueprint>
diff --git a/sdx-l3/src/main/resources/config-examples/README b/sdx-l3/src/main/resources/config-examples/README
new file mode 100644
index 0000000..061d0c6
--- /dev/null
+++ b/sdx-l3/src/main/resources/config-examples/README
@@ -0,0 +1,2 @@
+The SDX-L3 configuration should be copied to network configuration file:
+ $ONOS_HOME/tools/package/config/network-cfg.json
diff --git a/sdx-l3/src/main/resources/config-examples/network-cfg.json b/sdx-l3/src/main/resources/config-examples/network-cfg.json
new file mode 100644
index 0000000..a51ef92
--- /dev/null
+++ b/sdx-l3/src/main/resources/config-examples/network-cfg.json
@@ -0,0 +1,72 @@
+{
+ "ports" : {
+ "of:00000000000000a1/1" : {
+ "interfaces" : [
+ {
+ "name" : "AS1-conn1",
+ "ips" : [ "10.0.1.101/24" ],
+ "mac" : "00:00:00:00:00:01"
+ }
+ ]
+ },
+ "of:00000000000000a2/1" : {
+ "interfaces" : [
+ {
+ "name" : "AS1-conn2",
+ "ips" : [ "10.0.1.101/24" ],
+ "mac" : "00:00:00:00:00:01"
+ }
+ ]
+ },
+ "of:00000000000000a5/1" : {
+ "interfaces" : [
+ {
+ "name" : "AS3-conn1",
+ "ips" : [ "10.0.3.101/24" ],
+ "mac" : "00:00:00:00:00:01"
+ }
+ ]
+ },
+ "of:00000000000000a6/1" : {
+ "interfaces" : [
+ {
+ "name" : "AS4-conn1",
+ "ips" : [ "10.0.4.101/24" ],
+ "mac" : "00:00:00:00:00:01"
+ }
+ ]
+ }
+ },
+ "apps" : {
+ "org.onosproject.sdxl3" : {
+ "bgpPeers" : [
+ {
+ "name" : "AS1-Router1",
+ "ip" : "10.0.1.1",
+ "interface" : "AS1-conn1"
+ },
+ {
+ "name" : "AS1-Router2",
+ "ip" : "10.0.1.129",
+ "interface" : "AS1-conn2"
+ }
+ ]
+ },
+ "org.onosproject.router" : {
+ "bgp" : {
+ "bgpSpeakers" : [
+ {
+ "name" : "bgp",
+ "connectPoint" : "of:00000000000000a3/1",
+ "peers" : [
+ "10.0.1.1",
+ "10.0.1.129",
+ "10.0.3.1",
+ "10.0.4.1"
+ ]
+ }
+ ]
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/sdx-l3/src/main/resources/config-examples/route-server_network-cfg.json b/sdx-l3/src/main/resources/config-examples/route-server_network-cfg.json
new file mode 100644
index 0000000..c2ea2e3
--- /dev/null
+++ b/sdx-l3/src/main/resources/config-examples/route-server_network-cfg.json
@@ -0,0 +1,52 @@
+{
+ "ports" : {
+ "of:00000000000000a1/1" : {
+ "interfaces" : [
+ {
+ "name" : "AS 65001",
+ "ips" : [ "10.0.0.3/24" ],
+ "mac" : "00:00:00:00:00:01"
+ }
+ ]
+ },
+ "of:00000000000000a1/2" : {
+ "interfaces" : [
+ {
+ "name" : "AS 65002",
+ "ips" : [ "10.0.0.3/24" ],
+ "mac" : "00:00:00:00:00:01"
+ }
+ ]
+ }
+ },
+ "apps" : {
+ "org.onosproject.sdxl3" : {
+ "bgpPeers" : [
+ {
+ "name" : "AS65001-R1",
+ "ip" : "10.0.0.1",
+ "interface" : "AS 65001"
+ },
+ {
+ "name" : "AS65002-R2",
+ "ip" : "10.0.0.2",
+ "interface" : "AS 65002"
+ }
+ ]
+ },
+ "org.onosproject.router" : {
+ "bgp" : {
+ "bgpSpeakers" : [
+ {
+ "name" : "bgp",
+ "connectPoint" : "of:00000000000000a1/3",
+ "peers" : [
+ "10.0.0.1",
+ "10.0.0.2"
+ ]
+ }
+ ]
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/sdx-l3/src/test/java/org/onosproject/sdxl3/BgpPeersConfigTest.java b/sdx-l3/src/test/java/org/onosproject/sdxl3/BgpPeersConfigTest.java
new file mode 100644
index 0000000..fc2a6ed
--- /dev/null
+++ b/sdx-l3/src/test/java/org/onosproject/sdxl3/BgpPeersConfigTest.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.sdxl3;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.common.collect.Sets;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.packet.IpAddress;
+import org.onosproject.TestApplicationId;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.incubator.net.config.basics.ConfigException;
+import org.onosproject.net.config.Config;
+import org.onosproject.net.config.ConfigApplyDelegate;
+import org.onosproject.sdxl3.config.BgpPeersConfig;
+
+import java.util.Optional;
+import java.util.Set;
+
+import static junit.framework.TestCase.assertEquals;
+
+public class BgpPeersConfigTest {
+ private static final ApplicationId APP_ID =
+ new TestApplicationId(SdxL3.SDX_L3_APP);
+ private static final String KEY = "key";
+
+ public static final String NAME1 = "peer1";
+ public static final String IP1_STRING = "10.0.1.1";
+ public static final IpAddress IP1 = IpAddress.valueOf(IP1_STRING);
+ public static final String INTF1 = "conn_point1";
+
+ public static final String IP2_STRING = "10.0.2.1";
+ public static final IpAddress IP2 = IpAddress.valueOf(IP2_STRING);
+ public static final String INTF2 = "conn_point2";
+
+ private static final String JSON_TREE = "[{" +
+ "\"" + BgpPeersConfig.NAME + "\" : \"" + NAME1 + "\", " +
+ "\"" + BgpPeersConfig.IP + "\" : \"" + IP1_STRING + "\", " +
+ "\"" + BgpPeersConfig.INTERFACE + "\" : \"" + INTF1 + "\"}, " +
+ "{ \"" + BgpPeersConfig.IP + "\" : \"" + IP2_STRING + "\", " +
+ "\"" + BgpPeersConfig.INTERFACE + "\" : \"" + INTF2 + "\"}] ";
+
+ BgpPeersConfig peersConfig = new BgpPeersConfig();
+
+ private final ConfigApplyDelegate delegate = new MockCfgDelegate();
+ private final ObjectMapper mapper = new ObjectMapper();
+
+ @Before
+ public void setUp() throws Exception {
+ JsonNode node = new ObjectMapper().readTree(JSON_TREE);
+ peersConfig.init(APP_ID, KEY, node, mapper, delegate);
+ }
+
+ /**
+ * Tests if peers can be retrieved from JSON.
+ */
+ @Test
+ public void testBgpPeers() throws Exception {
+ assertEquals(createPeers(), peersConfig.bgpPeers());
+ }
+
+ /**
+ * Tests if interface name can be retrieved for given peer's IP.
+ */
+ @Test
+ public void testGetInterfaceNameForPeer() throws Exception {
+ assertEquals(INTF1, peersConfig.getInterfaceNameForPeer(IP1));
+ }
+
+ private Set<BgpPeersConfig.PeerConfig> createPeers() throws ConfigException {
+ Set<BgpPeersConfig.PeerConfig> peers = Sets.newHashSet();
+
+ peers.add(new BgpPeersConfig.PeerConfig(Optional.of(NAME1), IP1, INTF1));
+ peers.add(new BgpPeersConfig.PeerConfig(Optional.empty(), IP2, INTF2));
+
+ return peers;
+ }
+
+ private class MockCfgDelegate implements ConfigApplyDelegate {
+
+ @Override
+ public void onApply(@SuppressWarnings("rawtypes") Config config) {
+ config.apply();
+ }
+
+ }
+}
+