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 @@ ...@@ -36,6 +36,12 @@
36 <groupId>com.google.guava</groupId> 36 <groupId>com.google.guava</groupId>
37 <artifactId>guava</artifactId> 37 <artifactId>guava</artifactId>
38 </dependency> 38 </dependency>
39 +
40 + <dependency>
41 + <groupId>org.onlab.onos</groupId>
42 + <artifactId>onlab-thirdparty</artifactId>
43 + </dependency>
44 +
39 </dependencies> 45 </dependencies>
40 46
41 </project> 47 </project>
......
1 +package org.onlab.onos.sdnip;
2 +
3 +import static com.google.common.base.Preconditions.checkNotNull;
4 +
5 +import java.util.Objects;
6 +
7 +import org.onlab.packet.IpAddress;
8 +import org.onlab.packet.IpPrefix;
9 +
10 +import com.google.common.base.MoreObjects;
11 +
12 +/**
13 + * Represents a route entry for an IP prefix.
14 + */
15 +public class RouteEntry {
16 + private final IpPrefix prefix; // The IP prefix
17 + private final IpAddress nextHop; // Next-hop IP address
18 +
19 + /**
20 + * Class constructor.
21 + *
22 + * @param prefix the IP prefix of the route
23 + * @param nextHop the next hop IP address for the route
24 + */
25 + public RouteEntry(IpPrefix prefix, IpAddress nextHop) {
26 + this.prefix = checkNotNull(prefix);
27 + this.nextHop = checkNotNull(nextHop);
28 + }
29 +
30 + /**
31 + * Returns the IP prefix of the route.
32 + *
33 + * @return the IP prefix of the route
34 + */
35 + public IpPrefix prefix() {
36 + return prefix;
37 + }
38 +
39 + /**
40 + * Returns the next hop IP address for the route.
41 + *
42 + * @return the next hop IP address for the route
43 + */
44 + public IpAddress nextHop() {
45 + return nextHop;
46 + }
47 +
48 + /**
49 + * Creates the binary string representation of an IPv4 prefix.
50 + * The string length is equal to the prefix length.
51 + *
52 + * @param ip4Prefix the IPv4 prefix to use
53 + * @return the binary string representation
54 + */
55 + static String createBinaryString(IpPrefix ip4Prefix) {
56 + if (ip4Prefix.prefixLength() == 0) {
57 + return "";
58 + }
59 +
60 + StringBuilder result = new StringBuilder(ip4Prefix.prefixLength());
61 + long value = ip4Prefix.toRealInt();
62 + for (int i = 0; i < ip4Prefix.prefixLength(); i++) {
63 + long mask = 1 << (IpAddress.MAX_INET_MASK - 1 - i);
64 + result.append(((value & mask) == 0) ? "0" : "1");
65 + }
66 + return result.toString();
67 + }
68 +
69 + @Override
70 + public boolean equals(Object other) {
71 + if (this == other) {
72 + return true;
73 + }
74 +
75 + //
76 + // NOTE: Subclasses are considered as change of identity, hence
77 + // equals() will return false if the class type doesn't match.
78 + //
79 + if (other == null || getClass() != other.getClass()) {
80 + return false;
81 + }
82 +
83 + RouteEntry otherRoute = (RouteEntry) other;
84 + return Objects.equals(this.prefix, otherRoute.prefix) &&
85 + Objects.equals(this.nextHop, otherRoute.nextHop);
86 + }
87 +
88 + @Override
89 + public int hashCode() {
90 + return Objects.hash(prefix, nextHop);
91 + }
92 +
93 + @Override
94 + public String toString() {
95 + return MoreObjects.toStringHelper(getClass())
96 + .add("prefix", prefix)
97 + .add("nextHop", nextHop)
98 + .toString();
99 + }
100 +}
1 +package org.onlab.onos.sdnip;
2 +
3 +/**
4 + * An interface to receive route updates from route providers.
5 + */
6 +public interface RouteListener {
7 + /**
8 + * Receives a route update from a route provider.
9 + *
10 + * @param routeUpdate the updated route information
11 + */
12 + public void update(RouteUpdate routeUpdate);
13 +}
1 +package org.onlab.onos.sdnip;
2 +
3 +import static com.google.common.base.Preconditions.checkNotNull;
4 +
5 +import java.util.Objects;
6 +
7 +import com.google.common.base.MoreObjects;
8 +
9 +/**
10 + * Represents a change in routing information.
11 + */
12 +public class RouteUpdate {
13 + private final Type type; // The route update type
14 + private final RouteEntry routeEntry; // The updated route entry
15 +
16 + /**
17 + * Specifies the type of a route update.
18 + * <p/>
19 + * Route updates can either provide updated information for a route, or
20 + * withdraw a previously updated route.
21 + */
22 + public enum Type {
23 + /**
24 + * The update contains updated route information for a route.
25 + */
26 + UPDATE,
27 + /**
28 + * The update withdraws the route, meaning any previous information is
29 + * no longer valid.
30 + */
31 + DELETE
32 + }
33 +
34 + /**
35 + * Class constructor.
36 + *
37 + * @param type the type of the route update
38 + * @param routeEntry the route entry with the update
39 + */
40 + public RouteUpdate(Type type, RouteEntry routeEntry) {
41 + this.type = type;
42 + this.routeEntry = checkNotNull(routeEntry);
43 + }
44 +
45 + /**
46 + * Returns the type of the route update.
47 + *
48 + * @return the type of the update
49 + */
50 + public Type type() {
51 + return type;
52 + }
53 +
54 + /**
55 + * Returns the route entry the route update is for.
56 + *
57 + * @return the route entry the route update is for
58 + */
59 + public RouteEntry routeEntry() {
60 + return routeEntry;
61 + }
62 +
63 + @Override
64 + public boolean equals(Object other) {
65 + if (other == this) {
66 + return true;
67 + }
68 +
69 + if (!(other instanceof RouteUpdate)) {
70 + return false;
71 + }
72 +
73 + RouteUpdate otherUpdate = (RouteUpdate) other;
74 +
75 + return Objects.equals(this.type, otherUpdate.type) &&
76 + Objects.equals(this.routeEntry, otherUpdate.routeEntry);
77 + }
78 +
79 + @Override
80 + public int hashCode() {
81 + return Objects.hash(type, routeEntry);
82 + }
83 +
84 + @Override
85 + public String toString() {
86 + return MoreObjects.toStringHelper(getClass())
87 + .add("type", type)
88 + .add("routeEntry", routeEntry)
89 + .toString();
90 + }
91 +}
This diff is collapsed. Click to expand it.
...@@ -9,7 +9,10 @@ import org.apache.felix.scr.annotations.Reference; ...@@ -9,7 +9,10 @@ import org.apache.felix.scr.annotations.Reference;
9 import org.apache.felix.scr.annotations.ReferenceCardinality; 9 import org.apache.felix.scr.annotations.ReferenceCardinality;
10 import org.onlab.onos.net.host.HostService; 10 import org.onlab.onos.net.host.HostService;
11 import org.onlab.onos.net.intent.IntentService; 11 import org.onlab.onos.net.intent.IntentService;
12 +import org.onlab.onos.sdnip.RouteUpdate.Type;
12 import org.onlab.onos.sdnip.config.SdnIpConfigReader; 13 import org.onlab.onos.sdnip.config.SdnIpConfigReader;
14 +import org.onlab.packet.IpAddress;
15 +import org.onlab.packet.IpPrefix;
13 import org.slf4j.Logger; 16 import org.slf4j.Logger;
14 17
15 /** 18 /**
...@@ -28,6 +31,7 @@ public class SdnIp { ...@@ -28,6 +31,7 @@ public class SdnIp {
28 31
29 private SdnIpConfigReader config; 32 private SdnIpConfigReader config;
30 private PeerConnectivity peerConnectivity; 33 private PeerConnectivity peerConnectivity;
34 + private Router router;
31 35
32 @Activate 36 @Activate
33 protected void activate() { 37 protected void activate() {
...@@ -41,6 +45,14 @@ public class SdnIp { ...@@ -41,6 +45,14 @@ public class SdnIp {
41 peerConnectivity = new PeerConnectivity(config, interfaceService, intentService); 45 peerConnectivity = new PeerConnectivity(config, interfaceService, intentService);
42 peerConnectivity.start(); 46 peerConnectivity.start();
43 47
48 + router = new Router(intentService, hostService, config, interfaceService);
49 + router.start();
50 +
51 + // TODO need to disable link discovery on external ports
52 +
53 + router.update(new RouteUpdate(Type.UPDATE, new RouteEntry(
54 + IpPrefix.valueOf("172.16.20.0/24"),
55 + IpAddress.valueOf("192.168.10.1"))));
44 } 56 }
45 57
46 @Deactivate 58 @Deactivate
......
1 +package org.onlab.onos.sdnip;
2 +
3 +import static org.hamcrest.Matchers.is;
4 +import static org.hamcrest.Matchers.not;
5 +import static org.junit.Assert.assertThat;
6 +
7 +import org.junit.Test;
8 +import org.onlab.packet.IpAddress;
9 +import org.onlab.packet.IpPrefix;
10 +
11 +/**
12 + * Unit tests for the RouteEntry class.
13 + */
14 +public class RouteEntryTest {
15 + /**
16 + * Tests valid class constructor.
17 + */
18 + @Test
19 + public void testConstructor() {
20 + IpPrefix prefix = IpPrefix.valueOf("1.2.3.0/24");
21 + IpAddress nextHop = IpAddress.valueOf("5.6.7.8");
22 +
23 + RouteEntry routeEntry = new RouteEntry(prefix, nextHop);
24 + assertThat(routeEntry.toString(),
25 + is("RouteEntry{prefix=1.2.3.0/24, nextHop=5.6.7.8}"));
26 + }
27 +
28 + /**
29 + * Tests invalid class constructor for null IPv4 prefix.
30 + */
31 + @Test(expected = NullPointerException.class)
32 + public void testInvalidConstructorNullPrefix() {
33 + IpPrefix prefix = null;
34 + IpAddress nextHop = IpAddress.valueOf("5.6.7.8");
35 +
36 + new RouteEntry(prefix, nextHop);
37 + }
38 +
39 + /**
40 + * Tests invalid class constructor for null IPv4 next-hop.
41 + */
42 + @Test(expected = NullPointerException.class)
43 + public void testInvalidConstructorNullNextHop() {
44 + IpPrefix prefix = IpPrefix.valueOf("1.2.3.0/24");
45 + IpAddress nextHop = null;
46 +
47 + new RouteEntry(prefix, nextHop);
48 + }
49 +
50 + /**
51 + * Tests getting the fields of a route entry.
52 + */
53 + @Test
54 + public void testGetFields() {
55 + IpPrefix prefix = IpPrefix.valueOf("1.2.3.0/24");
56 + IpAddress nextHop = IpAddress.valueOf("5.6.7.8");
57 +
58 + RouteEntry routeEntry = new RouteEntry(prefix, nextHop);
59 + assertThat(routeEntry.prefix(), is(prefix));
60 + assertThat(routeEntry.nextHop(), is(nextHop));
61 + }
62 +
63 + /**
64 + * Tests creating a binary string from IPv4 prefix.
65 + */
66 + @Test
67 + public void testCreateBinaryString() {
68 + IpPrefix prefix;
69 +
70 + prefix = IpPrefix.valueOf("0.0.0.0/0");
71 + assertThat(RouteEntry.createBinaryString(prefix), is(""));
72 +
73 + prefix = IpPrefix.valueOf("192.168.166.0/22");
74 + assertThat(RouteEntry.createBinaryString(prefix),
75 + is("1100000010101000101001"));
76 +
77 + prefix = IpPrefix.valueOf("192.168.166.0/23");
78 + assertThat(RouteEntry.createBinaryString(prefix),
79 + is("11000000101010001010011"));
80 +
81 + prefix = IpPrefix.valueOf("192.168.166.0/24");
82 + assertThat(RouteEntry.createBinaryString(prefix),
83 + is("110000001010100010100110"));
84 +
85 + prefix = IpPrefix.valueOf("130.162.10.1/25");
86 + assertThat(RouteEntry.createBinaryString(prefix),
87 + is("1000001010100010000010100"));
88 +
89 + prefix = IpPrefix.valueOf("255.255.255.255/32");
90 + assertThat(RouteEntry.createBinaryString(prefix),
91 + is("11111111111111111111111111111111"));
92 + }
93 +
94 + /**
95 + * Tests equality of {@link RouteEntry}.
96 + */
97 + @Test
98 + public void testEquality() {
99 + IpPrefix prefix1 = IpPrefix.valueOf("1.2.3.0/24");
100 + IpAddress nextHop1 = IpAddress.valueOf("5.6.7.8");
101 + RouteEntry routeEntry1 = new RouteEntry(prefix1, nextHop1);
102 +
103 + IpPrefix prefix2 = IpPrefix.valueOf("1.2.3.0/24");
104 + IpAddress nextHop2 = IpAddress.valueOf("5.6.7.8");
105 + RouteEntry routeEntry2 = new RouteEntry(prefix2, nextHop2);
106 +
107 + assertThat(routeEntry1, is(routeEntry2));
108 + }
109 +
110 + /**
111 + * Tests non-equality of {@link RouteEntry}.
112 + */
113 + @Test
114 + public void testNonEquality() {
115 + IpPrefix prefix1 = IpPrefix.valueOf("1.2.3.0/24");
116 + IpAddress nextHop1 = IpAddress.valueOf("5.6.7.8");
117 + RouteEntry routeEntry1 = new RouteEntry(prefix1, nextHop1);
118 +
119 + IpPrefix prefix2 = IpPrefix.valueOf("1.2.3.0/25"); // Different
120 + IpAddress nextHop2 = IpAddress.valueOf("5.6.7.8");
121 + RouteEntry routeEntry2 = new RouteEntry(prefix2, nextHop2);
122 +
123 + IpPrefix prefix3 = IpPrefix.valueOf("1.2.3.0/24");
124 + IpAddress nextHop3 = IpAddress.valueOf("5.6.7.9"); // Different
125 + RouteEntry routeEntry3 = new RouteEntry(prefix3, nextHop3);
126 +
127 + assertThat(routeEntry1, is(not(routeEntry2)));
128 + assertThat(routeEntry1, is(not(routeEntry3)));
129 + }
130 +
131 + /**
132 + * Tests object string representation.
133 + */
134 + @Test
135 + public void testToString() {
136 + IpPrefix prefix = IpPrefix.valueOf("1.2.3.0/24");
137 + IpAddress nextHop = IpAddress.valueOf("5.6.7.8");
138 + RouteEntry routeEntry = new RouteEntry(prefix, nextHop);
139 +
140 + assertThat(routeEntry.toString(),
141 + is("RouteEntry{prefix=1.2.3.0/24, nextHop=5.6.7.8}"));
142 + }
143 +}
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
30 30
31 <bundle>mvn:org.codehaus.jackson/jackson-core-asl/1.9.13</bundle> 31 <bundle>mvn:org.codehaus.jackson/jackson-core-asl/1.9.13</bundle>
32 <bundle>mvn:org.codehaus.jackson/jackson-mapper-asl/1.9.13</bundle> 32 <bundle>mvn:org.codehaus.jackson/jackson-mapper-asl/1.9.13</bundle>
33 + <bundle>mvn:org.onlab.onos/onlab-thirdparty/1.0.0-SNAPSHOT</bundle>
33 </feature> 34 </feature>
34 35
35 <feature name="onos-thirdparty-web" version="1.0.0" 36 <feature name="onos-thirdparty-web" version="1.0.0"
......
...@@ -107,6 +107,12 @@ ...@@ -107,6 +107,12 @@
107 </dependency> 107 </dependency>
108 108
109 <dependency> 109 <dependency>
110 + <groupId>com.googlecode.concurrent-trees</groupId>
111 + <artifactId>concurrent-trees</artifactId>
112 + <version>2.4.0</version>
113 + </dependency>
114 +
115 + <dependency>
110 <groupId>commons-lang</groupId> 116 <groupId>commons-lang</groupId>
111 <artifactId>commons-lang</artifactId> 117 <artifactId>commons-lang</artifactId>
112 <version>2.6</version> 118 <version>2.6</version>
...@@ -266,6 +272,13 @@ ...@@ -266,6 +272,13 @@
266 <artifactId>onos-of-api</artifactId> 272 <artifactId>onos-of-api</artifactId>
267 <version>${project.version}</version> 273 <version>${project.version}</version>
268 </dependency> 274 </dependency>
275 +
276 + <dependency>
277 + <groupId>org.onlab.onos</groupId>
278 + <artifactId>onlab-thirdparty</artifactId>
279 + <version>${project.version}</version>
280 + </dependency>
281 +
269 <dependency> 282 <dependency>
270 <groupId>org.onlab.onos</groupId> 283 <groupId>org.onlab.onos</groupId>
271 <artifactId>onos-of-api</artifactId> 284 <artifactId>onos-of-api</artifactId>
......
...@@ -191,6 +191,15 @@ public final class IpAddress { ...@@ -191,6 +191,15 @@ public final class IpAddress {
191 } 191 }
192 192
193 /** 193 /**
194 + * Converts the IP address to a /32 IP prefix.
195 + *
196 + * @return the new IP prefix
197 + */
198 + public IpPrefix toPrefix() {
199 + return IpPrefix.valueOf(octets, MAX_INET_MASK);
200 + }
201 +
202 + /**
194 * Helper for computing the mask value from CIDR. 203 * Helper for computing the mask value from CIDR.
195 * 204 *
196 * @return an integer bitmask 205 * @return an integer bitmask
......
1 +<?xml version="1.0" encoding="UTF-8"?>
2 +<project xmlns="http://maven.apache.org/POM/4.0.0"
3 + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
5 + <modelVersion>4.0.0</modelVersion>
6 +
7 + <parent>
8 + <groupId>org.onlab.onos</groupId>
9 + <artifactId>onlab-utils</artifactId>
10 + <version>1.0.0-SNAPSHOT</version>
11 + <relativePath>../pom.xml</relativePath>
12 + </parent>
13 +
14 + <artifactId>onlab-thirdparty</artifactId>
15 + <packaging>bundle</packaging>
16 +
17 + <description>ONLab third-party dependencies</description>
18 +
19 + <dependencies>
20 + <dependency>
21 + <groupId>com.googlecode.concurrent-trees</groupId>
22 + <artifactId>concurrent-trees</artifactId>
23 + <version>2.4.0</version>
24 + </dependency>
25 + </dependencies>
26 +
27 + <build>
28 + <plugins>
29 + <plugin>
30 + <groupId>org.apache.maven.plugins</groupId>
31 + <artifactId>maven-shade-plugin</artifactId>
32 + <version>2.3</version>
33 + <configuration>
34 + <filters>
35 + <filter>
36 + <artifact>com.googlecode.concurrent-trees:concurrent-trees</artifact>
37 + <includes>
38 + <include>com/googlecode/**</include>
39 + </includes>
40 +
41 + </filter>
42 + <filter>
43 + <artifact>com.google.guava:guava</artifact>
44 + <excludes>
45 + <exclude>**</exclude>
46 + </excludes>
47 + </filter>
48 + </filters>
49 + </configuration>
50 + <executions>
51 + <execution>
52 + <phase>package</phase>
53 + <goals>
54 + <goal>shade</goal>
55 + </goals>
56 + </execution>
57 + </executions>
58 + </plugin>
59 + <plugin>
60 + <groupId>org.apache.felix</groupId>
61 + <artifactId>maven-bundle-plugin</artifactId>
62 + <configuration>
63 + <instructions>
64 + <Export-Package>
65 + com.googlecode.concurrenttrees.*
66 + </Export-Package>
67 + </instructions>
68 + </configuration>
69 + </plugin>
70 + </plugins>
71 + </build>
72 +
73 +</project>
1 +package org.onlab.thirdparty;
2 +
3 +
4 +/**
5 + * Empty class required to get the onlab-thirdparty module to build properly.
6 + * <p/>
7 + * TODO Figure out how to remove this.
8 + */
9 +public class OnlabThirdparty {
10 +
11 +}