Jonathan Hart

Port the Router functionality from SDN-IP.

As part of this we added an onlab-thirdparty artifact which allows us to
bring in dependencies that aren't bundles.
......@@ -36,6 +36,12 @@
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>org.onlab.onos</groupId>
<artifactId>onlab-thirdparty</artifactId>
</dependency>
</dependencies>
</project>
......
package org.onlab.onos.sdnip;
import static com.google.common.base.Preconditions.checkNotNull;
import java.util.Objects;
import org.onlab.packet.IpAddress;
import org.onlab.packet.IpPrefix;
import com.google.common.base.MoreObjects;
/**
* Represents a route entry for an IP prefix.
*/
public class RouteEntry {
private final IpPrefix prefix; // The IP prefix
private final IpAddress nextHop; // Next-hop IP address
/**
* Class constructor.
*
* @param prefix the IP prefix of the route
* @param nextHop the next hop IP address for the route
*/
public RouteEntry(IpPrefix prefix, IpAddress nextHop) {
this.prefix = checkNotNull(prefix);
this.nextHop = checkNotNull(nextHop);
}
/**
* Returns the IP prefix of the route.
*
* @return the IP prefix of the route
*/
public IpPrefix prefix() {
return prefix;
}
/**
* Returns the next hop IP address for the route.
*
* @return the next hop IP address for the route
*/
public IpAddress nextHop() {
return nextHop;
}
/**
* Creates the binary string representation of an IPv4 prefix.
* The string length is equal to the prefix length.
*
* @param ip4Prefix the IPv4 prefix to use
* @return the binary string representation
*/
static String createBinaryString(IpPrefix ip4Prefix) {
if (ip4Prefix.prefixLength() == 0) {
return "";
}
StringBuilder result = new StringBuilder(ip4Prefix.prefixLength());
long value = ip4Prefix.toRealInt();
for (int i = 0; i < ip4Prefix.prefixLength(); i++) {
long mask = 1 << (IpAddress.MAX_INET_MASK - 1 - i);
result.append(((value & mask) == 0) ? "0" : "1");
}
return result.toString();
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
//
// NOTE: Subclasses are considered as change of identity, hence
// equals() will return false if the class type doesn't match.
//
if (other == null || getClass() != other.getClass()) {
return false;
}
RouteEntry otherRoute = (RouteEntry) other;
return Objects.equals(this.prefix, otherRoute.prefix) &&
Objects.equals(this.nextHop, otherRoute.nextHop);
}
@Override
public int hashCode() {
return Objects.hash(prefix, nextHop);
}
@Override
public String toString() {
return MoreObjects.toStringHelper(getClass())
.add("prefix", prefix)
.add("nextHop", nextHop)
.toString();
}
}
package org.onlab.onos.sdnip;
/**
* An interface to receive route updates from route providers.
*/
public interface RouteListener {
/**
* Receives a route update from a route provider.
*
* @param routeUpdate the updated route information
*/
public void update(RouteUpdate routeUpdate);
}
package org.onlab.onos.sdnip;
import static com.google.common.base.Preconditions.checkNotNull;
import java.util.Objects;
import com.google.common.base.MoreObjects;
/**
* Represents a change in routing information.
*/
public class RouteUpdate {
private final Type type; // The route update type
private final RouteEntry routeEntry; // The updated route entry
/**
* Specifies the type of a route update.
* <p/>
* Route updates can either provide updated information for a route, or
* withdraw a previously updated route.
*/
public enum Type {
/**
* The update contains updated route information for a route.
*/
UPDATE,
/**
* The update withdraws the route, meaning any previous information is
* no longer valid.
*/
DELETE
}
/**
* Class constructor.
*
* @param type the type of the route update
* @param routeEntry the route entry with the update
*/
public RouteUpdate(Type type, RouteEntry routeEntry) {
this.type = type;
this.routeEntry = checkNotNull(routeEntry);
}
/**
* Returns the type of the route update.
*
* @return the type of the update
*/
public Type type() {
return type;
}
/**
* Returns the route entry the route update is for.
*
* @return the route entry the route update is for
*/
public RouteEntry routeEntry() {
return routeEntry;
}
@Override
public boolean equals(Object other) {
if (other == this) {
return true;
}
if (!(other instanceof RouteUpdate)) {
return false;
}
RouteUpdate otherUpdate = (RouteUpdate) other;
return Objects.equals(this.type, otherUpdate.type) &&
Objects.equals(this.routeEntry, otherUpdate.routeEntry);
}
@Override
public int hashCode() {
return Objects.hash(type, routeEntry);
}
@Override
public String toString() {
return MoreObjects.toStringHelper(getClass())
.add("type", type)
.add("routeEntry", routeEntry)
.toString();
}
}
This diff is collapsed. Click to expand it.
......@@ -9,7 +9,10 @@ import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.onlab.onos.net.host.HostService;
import org.onlab.onos.net.intent.IntentService;
import org.onlab.onos.sdnip.RouteUpdate.Type;
import org.onlab.onos.sdnip.config.SdnIpConfigReader;
import org.onlab.packet.IpAddress;
import org.onlab.packet.IpPrefix;
import org.slf4j.Logger;
/**
......@@ -28,6 +31,7 @@ public class SdnIp {
private SdnIpConfigReader config;
private PeerConnectivity peerConnectivity;
private Router router;
@Activate
protected void activate() {
......@@ -41,6 +45,14 @@ public class SdnIp {
peerConnectivity = new PeerConnectivity(config, interfaceService, intentService);
peerConnectivity.start();
router = new Router(intentService, hostService, config, interfaceService);
router.start();
// TODO need to disable link discovery on external ports
router.update(new RouteUpdate(Type.UPDATE, new RouteEntry(
IpPrefix.valueOf("172.16.20.0/24"),
IpAddress.valueOf("192.168.10.1"))));
}
@Deactivate
......
package org.onlab.onos.sdnip;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertThat;
import org.junit.Test;
import org.onlab.packet.IpAddress;
import org.onlab.packet.IpPrefix;
/**
* Unit tests for the RouteEntry class.
*/
public class RouteEntryTest {
/**
* Tests valid class constructor.
*/
@Test
public void testConstructor() {
IpPrefix prefix = IpPrefix.valueOf("1.2.3.0/24");
IpAddress nextHop = IpAddress.valueOf("5.6.7.8");
RouteEntry routeEntry = new RouteEntry(prefix, nextHop);
assertThat(routeEntry.toString(),
is("RouteEntry{prefix=1.2.3.0/24, nextHop=5.6.7.8}"));
}
/**
* Tests invalid class constructor for null IPv4 prefix.
*/
@Test(expected = NullPointerException.class)
public void testInvalidConstructorNullPrefix() {
IpPrefix prefix = null;
IpAddress nextHop = IpAddress.valueOf("5.6.7.8");
new RouteEntry(prefix, nextHop);
}
/**
* Tests invalid class constructor for null IPv4 next-hop.
*/
@Test(expected = NullPointerException.class)
public void testInvalidConstructorNullNextHop() {
IpPrefix prefix = IpPrefix.valueOf("1.2.3.0/24");
IpAddress nextHop = null;
new RouteEntry(prefix, nextHop);
}
/**
* Tests getting the fields of a route entry.
*/
@Test
public void testGetFields() {
IpPrefix prefix = IpPrefix.valueOf("1.2.3.0/24");
IpAddress nextHop = IpAddress.valueOf("5.6.7.8");
RouteEntry routeEntry = new RouteEntry(prefix, nextHop);
assertThat(routeEntry.prefix(), is(prefix));
assertThat(routeEntry.nextHop(), is(nextHop));
}
/**
* Tests creating a binary string from IPv4 prefix.
*/
@Test
public void testCreateBinaryString() {
IpPrefix prefix;
prefix = IpPrefix.valueOf("0.0.0.0/0");
assertThat(RouteEntry.createBinaryString(prefix), is(""));
prefix = IpPrefix.valueOf("192.168.166.0/22");
assertThat(RouteEntry.createBinaryString(prefix),
is("1100000010101000101001"));
prefix = IpPrefix.valueOf("192.168.166.0/23");
assertThat(RouteEntry.createBinaryString(prefix),
is("11000000101010001010011"));
prefix = IpPrefix.valueOf("192.168.166.0/24");
assertThat(RouteEntry.createBinaryString(prefix),
is("110000001010100010100110"));
prefix = IpPrefix.valueOf("130.162.10.1/25");
assertThat(RouteEntry.createBinaryString(prefix),
is("1000001010100010000010100"));
prefix = IpPrefix.valueOf("255.255.255.255/32");
assertThat(RouteEntry.createBinaryString(prefix),
is("11111111111111111111111111111111"));
}
/**
* Tests equality of {@link RouteEntry}.
*/
@Test
public void testEquality() {
IpPrefix prefix1 = IpPrefix.valueOf("1.2.3.0/24");
IpAddress nextHop1 = IpAddress.valueOf("5.6.7.8");
RouteEntry routeEntry1 = new RouteEntry(prefix1, nextHop1);
IpPrefix prefix2 = IpPrefix.valueOf("1.2.3.0/24");
IpAddress nextHop2 = IpAddress.valueOf("5.6.7.8");
RouteEntry routeEntry2 = new RouteEntry(prefix2, nextHop2);
assertThat(routeEntry1, is(routeEntry2));
}
/**
* Tests non-equality of {@link RouteEntry}.
*/
@Test
public void testNonEquality() {
IpPrefix prefix1 = IpPrefix.valueOf("1.2.3.0/24");
IpAddress nextHop1 = IpAddress.valueOf("5.6.7.8");
RouteEntry routeEntry1 = new RouteEntry(prefix1, nextHop1);
IpPrefix prefix2 = IpPrefix.valueOf("1.2.3.0/25"); // Different
IpAddress nextHop2 = IpAddress.valueOf("5.6.7.8");
RouteEntry routeEntry2 = new RouteEntry(prefix2, nextHop2);
IpPrefix prefix3 = IpPrefix.valueOf("1.2.3.0/24");
IpAddress nextHop3 = IpAddress.valueOf("5.6.7.9"); // Different
RouteEntry routeEntry3 = new RouteEntry(prefix3, nextHop3);
assertThat(routeEntry1, is(not(routeEntry2)));
assertThat(routeEntry1, is(not(routeEntry3)));
}
/**
* Tests object string representation.
*/
@Test
public void testToString() {
IpPrefix prefix = IpPrefix.valueOf("1.2.3.0/24");
IpAddress nextHop = IpAddress.valueOf("5.6.7.8");
RouteEntry routeEntry = new RouteEntry(prefix, nextHop);
assertThat(routeEntry.toString(),
is("RouteEntry{prefix=1.2.3.0/24, nextHop=5.6.7.8}"));
}
}
......@@ -30,6 +30,7 @@
<bundle>mvn:org.codehaus.jackson/jackson-core-asl/1.9.13</bundle>
<bundle>mvn:org.codehaus.jackson/jackson-mapper-asl/1.9.13</bundle>
<bundle>mvn:org.onlab.onos/onlab-thirdparty/1.0.0-SNAPSHOT</bundle>
</feature>
<feature name="onos-thirdparty-web" version="1.0.0"
......
......@@ -107,7 +107,13 @@
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<groupId>com.googlecode.concurrent-trees</groupId>
<artifactId>concurrent-trees</artifactId>
<version>2.4.0</version>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
......@@ -266,6 +272,13 @@
<artifactId>onos-of-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.onlab.onos</groupId>
<artifactId>onlab-thirdparty</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.onlab.onos</groupId>
<artifactId>onos-of-api</artifactId>
......
......@@ -191,6 +191,15 @@ public final class IpAddress {
}
/**
* Converts the IP address to a /32 IP prefix.
*
* @return the new IP prefix
*/
public IpPrefix toPrefix() {
return IpPrefix.valueOf(octets, MAX_INET_MASK);
}
/**
* Helper for computing the mask value from CIDR.
*
* @return an integer bitmask
......
<?xml version="1.0" encoding="UTF-8"?>
<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.onlab.onos</groupId>
<artifactId>onlab-utils</artifactId>
<version>1.0.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>onlab-thirdparty</artifactId>
<packaging>bundle</packaging>
<description>ONLab third-party dependencies</description>
<dependencies>
<dependency>
<groupId>com.googlecode.concurrent-trees</groupId>
<artifactId>concurrent-trees</artifactId>
<version>2.4.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.3</version>
<configuration>
<filters>
<filter>
<artifact>com.googlecode.concurrent-trees:concurrent-trees</artifact>
<includes>
<include>com/googlecode/**</include>
</includes>
</filter>
<filter>
<artifact>com.google.guava:guava</artifact>
<excludes>
<exclude>**</exclude>
</excludes>
</filter>
</filters>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<configuration>
<instructions>
<Export-Package>
com.googlecode.concurrenttrees.*
</Export-Package>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
</project>
package org.onlab.thirdparty;
/**
* Empty class required to get the onlab-thirdparty module to build properly.
* <p/>
* TODO Figure out how to remove this.
*/
public class OnlabThirdparty {
}