CORD-508 SegmentRouting / vRouter integration
- Added excludePorts config to SegmentRouting
SR does not push VLAN filtering rule to excluded ports
SR ignores hosts learned from excluded ports
- Use separate default route config
Don't need to config 0/0 on the interface anymore
Change-Id: Iea75d60c2d5f5368e79652b1bf192a6ced49030d
diff --git a/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java b/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
index 10ac6d6..271ffa0 100644
--- a/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
+++ b/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
@@ -471,8 +471,9 @@
}
for (Port port : srManager.deviceService.getPorts(deviceId)) {
- if (port.number().toLong() > 0 &&
- port.number().toLong() < SegmentRoutingService.OFPP_MAX &&
+ ConnectPoint cp = new ConnectPoint(deviceId, port.number());
+ // TODO: Handles dynamic port events when we are ready for dynamic config
+ if (!srManager.deviceConfiguration.excludedPorts().contains(cp) &&
port.isEnabled()) {
Ip4Prefix portSubnet = config.getPortSubnet(deviceId, port.number());
VlanId assignedVlan = (portSubnet == null)
diff --git a/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java b/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
index 32e03de..03b4f40 100644
--- a/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
+++ b/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
@@ -814,7 +814,8 @@
* Reads network config and initializes related data structure accordingly.
*/
public void configureNetwork() {
- deviceConfiguration = new DeviceConfiguration(segmentRoutingManager.cfgService);
+ deviceConfiguration = new DeviceConfiguration(appId,
+ segmentRoutingManager.cfgService);
arpHandler = new ArpHandler(segmentRoutingManager);
icmpHandler = new IcmpHandler(segmentRoutingManager);
@@ -987,22 +988,25 @@
Set<IpAddress> ips = event.subject().ipAddresses();
log.info("Host {}/{} is added at {}:{}", mac, vlanId, deviceId, port);
- // Populate bridging table entry
- log.debug("Populate L2 table entry for host {} at {}:{}",
- mac, deviceId, port);
- ForwardingObjective.Builder fob =
- getForwardingObjectiveBuilder(deviceId, mac, vlanId, port);
- flowObjectiveService.forward(deviceId, fob.add(
- new BridgingTableObjectiveContext(mac, vlanId)
- ));
+ if (!deviceConfiguration.excludedPorts()
+ .contains(new ConnectPoint(deviceId, port))) {
+ // Populate bridging table entry
+ log.debug("Populate L2 table entry for host {} at {}:{}",
+ mac, deviceId, port);
+ ForwardingObjective.Builder fob =
+ getForwardingObjectiveBuilder(deviceId, mac, vlanId, port);
+ flowObjectiveService.forward(deviceId, fob.add(
+ new BridgingTableObjectiveContext(mac, vlanId)
+ ));
- // Populate IP table entry
- ips.forEach(ip -> {
- if (ip.isIp4()) {
- routingRulePopulator.populateIpRuleForHost(
- deviceId, ip.getIp4Address(), mac, port);
- }
- });
+ // Populate IP table entry
+ ips.forEach(ip -> {
+ if (ip.isIp4()) {
+ routingRulePopulator.populateIpRuleForHost(
+ deviceId, ip.getIp4Address(), mac, port);
+ }
+ });
+ }
}
private void processHostRemoveEvent(HostEvent event) {
@@ -1013,20 +1017,23 @@
Set<IpAddress> ips = event.subject().ipAddresses();
log.debug("Host {}/{} is removed from {}:{}", mac, vlanId, deviceId, port);
- // Revoke bridging table entry
- ForwardingObjective.Builder fob =
- getForwardingObjectiveBuilder(deviceId, mac, vlanId, port);
- flowObjectiveService.forward(deviceId, fob.remove(
- new BridgingTableObjectiveContext(mac, vlanId)
- ));
+ if (!deviceConfiguration.excludedPorts()
+ .contains(new ConnectPoint(deviceId, port))) {
+ // Revoke bridging table entry
+ ForwardingObjective.Builder fob =
+ getForwardingObjectiveBuilder(deviceId, mac, vlanId, port);
+ flowObjectiveService.forward(deviceId, fob.remove(
+ new BridgingTableObjectiveContext(mac, vlanId)
+ ));
- // Revoke IP table entry
- ips.forEach(ip -> {
- if (ip.isIp4()) {
- routingRulePopulator.revokeIpRuleForHost(
- deviceId, ip.getIp4Address(), mac, port);
- }
- });
+ // Revoke IP table entry
+ ips.forEach(ip -> {
+ if (ip.isIp4()) {
+ routingRulePopulator.revokeIpRuleForHost(
+ deviceId, ip.getIp4Address(), mac, port);
+ }
+ });
+ }
}
private void processHostMovedEvent(HostEvent event) {
@@ -1041,35 +1048,41 @@
log.debug("Host {}/{} is moved from {}:{} to {}:{}",
mac, vlanId, prevDeviceId, prevPort, newDeviceId, newPort);
- // Revoke previous bridging table entry
- ForwardingObjective.Builder prevFob =
- getForwardingObjectiveBuilder(prevDeviceId, mac, vlanId, prevPort);
- flowObjectiveService.forward(prevDeviceId, prevFob.remove(
- new BridgingTableObjectiveContext(mac, vlanId)
- ));
+ if (!deviceConfiguration.excludedPorts()
+ .contains(new ConnectPoint(prevDeviceId, prevPort))) {
+ // Revoke previous bridging table entry
+ ForwardingObjective.Builder prevFob =
+ getForwardingObjectiveBuilder(prevDeviceId, mac, vlanId, prevPort);
+ flowObjectiveService.forward(prevDeviceId, prevFob.remove(
+ new BridgingTableObjectiveContext(mac, vlanId)
+ ));
- // Revoke previous IP table entry
- prevIps.forEach(ip -> {
- if (ip.isIp4()) {
- routingRulePopulator.revokeIpRuleForHost(
- prevDeviceId, ip.getIp4Address(), mac, prevPort);
- }
- });
+ // Revoke previous IP table entry
+ prevIps.forEach(ip -> {
+ if (ip.isIp4()) {
+ routingRulePopulator.revokeIpRuleForHost(
+ prevDeviceId, ip.getIp4Address(), mac, prevPort);
+ }
+ });
+ }
- // Populate new bridging table entry
- ForwardingObjective.Builder newFob =
- getForwardingObjectiveBuilder(newDeviceId, mac, vlanId, newPort);
- flowObjectiveService.forward(newDeviceId, newFob.add(
- new BridgingTableObjectiveContext(mac, vlanId)
- ));
+ if (!deviceConfiguration.excludedPorts()
+ .contains(new ConnectPoint(newDeviceId, newPort))) {
+ // Populate new bridging table entry
+ ForwardingObjective.Builder newFob =
+ getForwardingObjectiveBuilder(newDeviceId, mac, vlanId, newPort);
+ flowObjectiveService.forward(newDeviceId, newFob.add(
+ new BridgingTableObjectiveContext(mac, vlanId)
+ ));
- // Populate new IP table entry
- newIps.forEach(ip -> {
- if (ip.isIp4()) {
- routingRulePopulator.populateIpRuleForHost(
- newDeviceId, ip.getIp4Address(), mac, newPort);
- }
- });
+ // Populate new IP table entry
+ newIps.forEach(ip -> {
+ if (ip.isIp4()) {
+ routingRulePopulator.populateIpRuleForHost(
+ newDeviceId, ip.getIp4Address(), mac, newPort);
+ }
+ });
+ }
}
private void processHostUpdatedEvent(HostEvent event) {
@@ -1083,21 +1096,27 @@
Set<IpAddress> newIps = event.subject().ipAddresses();
log.debug("Host {}/{} is updated", mac, vlanId);
- // Revoke previous IP table entry
- prevIps.forEach(ip -> {
- if (ip.isIp4()) {
- routingRulePopulator.revokeIpRuleForHost(
- prevDeviceId, ip.getIp4Address(), mac, prevPort);
- }
- });
+ if (!deviceConfiguration.excludedPorts()
+ .contains(new ConnectPoint(prevDeviceId, prevPort))) {
+ // Revoke previous IP table entry
+ prevIps.forEach(ip -> {
+ if (ip.isIp4()) {
+ routingRulePopulator.revokeIpRuleForHost(
+ prevDeviceId, ip.getIp4Address(), mac, prevPort);
+ }
+ });
+ }
- // Populate new IP table entry
- newIps.forEach(ip -> {
- if (ip.isIp4()) {
- routingRulePopulator.populateIpRuleForHost(
- newDeviceId, ip.getIp4Address(), mac, newPort);
- }
- });
+ if (!deviceConfiguration.excludedPorts()
+ .contains(new ConnectPoint(newDeviceId, newPort))) {
+ // Populate new IP table entry
+ newIps.forEach(ip -> {
+ if (ip.isIp4()) {
+ routingRulePopulator.populateIpRuleForHost(
+ newDeviceId, ip.getIp4Address(), mac, newPort);
+ }
+ });
+ }
}
@Override
diff --git a/src/main/java/org/onosproject/segmentrouting/config/DeviceConfiguration.java b/src/main/java/org/onosproject/segmentrouting/config/DeviceConfiguration.java
index db4bc63..f222bb3 100644
--- a/src/main/java/org/onosproject/segmentrouting/config/DeviceConfiguration.java
+++ b/src/main/java/org/onosproject/segmentrouting/config/DeviceConfiguration.java
@@ -18,11 +18,13 @@
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.SetMultimap;
+import com.google.common.collect.Sets;
import org.onlab.packet.Ip4Address;
import org.onlab.packet.Ip4Prefix;
import org.onlab.packet.IpPrefix;
import org.onlab.packet.MacAddress;
import org.onlab.packet.VlanId;
+import org.onosproject.core.ApplicationId;
import org.onosproject.incubator.net.config.basics.ConfigException;
import org.onosproject.incubator.net.config.basics.InterfaceConfig;
import org.onosproject.incubator.net.intf.Interface;
@@ -51,11 +53,12 @@
*/
public class DeviceConfiguration implements DeviceProperties {
- private static final Logger log = LoggerFactory
- .getLogger(DeviceConfiguration.class);
+ private static final Logger log = LoggerFactory.getLogger(DeviceConfiguration.class);
private final List<Integer> allSegmentIds = new ArrayList<>();
private final Map<DeviceId, SegmentRouterInfo> deviceConfigMap = new ConcurrentHashMap<>();
private final Map<VlanId, List<ConnectPoint>> xConnects = new ConcurrentHashMap<>();
+ private final Set<ConnectPoint> excludedPorts = Sets.newConcurrentHashSet();
+ private SegmentRoutingAppConfig appConfig;
private class SegmentRouterInfo {
int nodeSid;
@@ -77,9 +80,11 @@
* Constructs device configuration for all Segment Router devices,
* organizing the data into various maps for easier access.
*
+ * @param appId application id
* @param cfgService config service
*/
- public DeviceConfiguration(NetworkConfigRegistry cfgService) {
+ public DeviceConfiguration(ApplicationId appId,
+ NetworkConfigRegistry cfgService) {
// Read config from device subject, excluding gatewayIps and subnets.
Set<DeviceId> deviceSubjects =
cfgService.getSubjects(DeviceId.class, SegmentRoutingDeviceConfig.class);
@@ -98,6 +103,11 @@
allSegmentIds.add(info.nodeSid);
});
+ // Read excluded port names from config
+ appConfig = cfgService.getConfig(appId, SegmentRoutingAppConfig.class);
+ Set<String> excludePorts = (appConfig != null) ?
+ appConfig.excludePorts() : ImmutableSet.of();
+
// Read gatewayIps and subnets from port subject.
Set<ConnectPoint> portSubjects =
cfgService.getSubjects(ConnectPoint.class, InterfaceConfig.class);
@@ -112,6 +122,12 @@
return;
}
networkInterfaces.forEach(networkInterface -> {
+ // Do not process excluded ports
+ if (excludePorts.contains(networkInterface.name())) {
+ excludedPorts.add(subject);
+ return;
+ }
+
VlanId vlanId = networkInterface.vlan();
ConnectPoint connectPoint = networkInterface.connectPoint();
DeviceId dpid = connectPoint.deviceId();
@@ -343,7 +359,13 @@
if (srinfo != null) {
log.trace("getSubnets for device{} is {}", deviceId,
srinfo.subnets.values());
- return ImmutableSet.copyOf(srinfo.subnets.values());
+
+ ImmutableSet.Builder<Ip4Prefix> builder = ImmutableSet.builder();
+ builder.addAll(srinfo.subnets.values());
+ if (deviceId.equals(appConfig.vRouterId())) {
+ builder.add(Ip4Prefix.valueOf("0.0.0.0/0"));
+ }
+ return builder.build();
}
return null;
}
@@ -464,4 +486,13 @@
SegmentRouterInfo srinfo = deviceConfigMap.get(deviceId);
return srinfo != null && srinfo.adjacencySids.containsKey(sid);
}
+
+ /**
+ * Returns a set of excluded ports.
+ *
+ * @return excluded ports
+ */
+ public Set<ConnectPoint> excludedPorts() {
+ return excludedPorts;
+ }
}
\ No newline at end of file
diff --git a/src/main/java/org/onosproject/segmentrouting/config/SegmentRoutingAppConfig.java b/src/main/java/org/onosproject/segmentrouting/config/SegmentRoutingAppConfig.java
index e39fb18..163cc1c 100644
--- a/src/main/java/org/onosproject/segmentrouting/config/SegmentRoutingAppConfig.java
+++ b/src/main/java/org/onosproject/segmentrouting/config/SegmentRoutingAppConfig.java
@@ -21,6 +21,7 @@
import com.google.common.collect.ImmutableSet;
import org.onlab.packet.MacAddress;
import org.onosproject.core.ApplicationId;
+import org.onosproject.net.DeviceId;
import org.onosproject.net.config.Config;
import java.util.Set;
@@ -31,10 +32,14 @@
*/
public class SegmentRoutingAppConfig extends Config<ApplicationId> {
private static final String VROUTER_MACS = "vRouterMacs";
+ private static final String VROUTER_ID = "vRouterId";
+ private static final String EXCLUDE_PORTS = "excludePorts";
@Override
public boolean isValid() {
- return hasOnlyFields(VROUTER_MACS) && vRouterMacs() != null;
+ return hasOnlyFields(VROUTER_MACS, VROUTER_ID, EXCLUDE_PORTS) &&
+ vRouterMacs() != null && vRouterId() != null &&
+ excludePorts() != null;
}
/**
@@ -88,10 +93,84 @@
return this;
}
+ /**
+ * Gets vRouter device ID.
+ *
+ * @return vRouter device ID, or null if not valid
+ */
+ public DeviceId vRouterId() {
+ if (!object.has(VROUTER_ID)) {
+ return null;
+ }
+
+ try {
+ return DeviceId.deviceId(object.path(VROUTER_ID).asText());
+ } catch (IllegalArgumentException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Sets vRouter device ID.
+ *
+ * @param vRouterId vRouter device ID
+ * @return this {@link SegmentRoutingAppConfig}
+ */
+ public SegmentRoutingAppConfig setVRouterId(DeviceId vRouterId) {
+ if (vRouterId == null) {
+ object.remove(VROUTER_ID);
+ } else {
+ object.put(VROUTER_ID, vRouterId.toString());
+ }
+ return this;
+ }
+
+ /**
+ * Gets names of ports that are ignored by SegmentRouting.
+ *
+ * @return set of port names
+ */
+ public Set<String> excludePorts() {
+ if (!object.has(EXCLUDE_PORTS)) {
+ return null;
+ }
+
+ ImmutableSet.Builder<String> builder = ImmutableSet.builder();
+ ArrayNode arrayNode = (ArrayNode) object.path(EXCLUDE_PORTS);
+ for (JsonNode jsonNode : arrayNode) {
+ String portName = jsonNode.asText(null);
+ if (portName == null) {
+ return null;
+ }
+ builder.add(portName);
+ }
+ return builder.build();
+ }
+
+ /**
+ * Sets names of ports that are ignored by SegmentRouting.
+ *
+ * @paran excludePorts names of ports that are ignored by SegmentRouting
+ * @return this {@link SegmentRoutingAppConfig}
+ */
+ public SegmentRoutingAppConfig setExcludePorts(Set<String> excludePorts) {
+ if (excludePorts == null) {
+ object.remove(EXCLUDE_PORTS);
+ } else {
+ ArrayNode arrayNode = mapper.createArrayNode();
+ excludePorts.forEach(portName -> {
+ arrayNode.add(portName);
+ });
+ object.set(EXCLUDE_PORTS, arrayNode);
+ }
+ return this;
+ }
+
@Override
public String toString() {
return toStringHelper(this)
.add("vRouterMacs", vRouterMacs())
+ .add("excludePorts", excludePorts())
.toString();
}
}
diff --git a/src/test/java/org/onosproject/segmentrouting/config/SegmentRoutingAppConfigTest.java b/src/test/java/org/onosproject/segmentrouting/config/SegmentRoutingAppConfigTest.java
index 7755059..8acef5c 100644
--- a/src/test/java/org/onosproject/segmentrouting/config/SegmentRoutingAppConfigTest.java
+++ b/src/test/java/org/onosproject/segmentrouting/config/SegmentRoutingAppConfigTest.java
@@ -24,6 +24,7 @@
import org.onlab.packet.MacAddress;
import org.onosproject.core.ApplicationId;
import org.onosproject.TestApplicationId;
+import org.onosproject.net.DeviceId;
import org.onosproject.net.config.Config;
import org.onosproject.net.config.ConfigApplyDelegate;
import org.onosproject.segmentrouting.SegmentRoutingService;
@@ -32,6 +33,7 @@
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
/**
* Tests for class {@link SegmentRoutingAppConfig}.
@@ -41,9 +43,34 @@
new TestApplicationId(SegmentRoutingService.SR_APP_ID);
private SegmentRoutingAppConfig config;
- private MacAddress routerMac1;
- private MacAddress routerMac2;
- private MacAddress routerMac3;
+ private SegmentRoutingAppConfig invalidConfig;
+ private static final String JSON_STRING = "{" +
+ "\"vRouterMacs\" : [" +
+ " \"00:00:00:00:00:01\"," +
+ " \"00:00:00:00:00:02\"" +
+ "]," +
+ "\"vRouterId\" : \"of:1\"," +
+ "\"excludePorts\" : [" +
+ " \"port1\"," +
+ " \"port2\"" +
+ "]}";
+ private static final String INVALID_JSON_STRING = "{" +
+ "\"vRouterMacs\" : [" +
+ " \"00:00:00:00:00:01\"," +
+ " \"00:00:00:00:00:02\"" +
+ "]," +
+ "\"excludePorts\" : [" +
+ " \"port1\"," +
+ " \"port2\"" +
+ "]}";
+ private static final MacAddress ROUTER_MAC_1 = MacAddress.valueOf("00:00:00:00:00:01");
+ private static final MacAddress ROUTER_MAC_2 = MacAddress.valueOf("00:00:00:00:00:02");
+ private static final MacAddress ROUTER_MAC_3 = MacAddress.valueOf("00:00:00:00:00:03");
+ private static final String PORT_NAME_1 = "port1";
+ private static final String PORT_NAME_2 = "port2";
+ private static final String PORT_NAME_3 = "port3";
+ private static final DeviceId VROUTER_ID_1 = DeviceId.deviceId("of:1");
+ private static final DeviceId VROUTER_ID_2 = DeviceId.deviceId("of:2");
/**
* Initialize test related variables.
@@ -52,55 +79,107 @@
*/
@Before
public void setUp() throws Exception {
- String jsonString = "{" +
- "\"vRouterMacs\" : [" +
- " \"00:00:00:00:00:01\"," +
- " \"00:00:00:00:00:02\"" +
- "]}";
-
- routerMac1 = MacAddress.valueOf("00:00:00:00:00:01");
- routerMac2 = MacAddress.valueOf("00:00:00:00:00:02");
- routerMac3 = MacAddress.valueOf("00:00:00:00:00:03");
-
ApplicationId subject = APP_ID;
String key = SegmentRoutingService.SR_APP_ID;
ObjectMapper mapper = new ObjectMapper();
- JsonNode jsonNode = mapper.readTree(jsonString);
+ JsonNode jsonNode = mapper.readTree(JSON_STRING);
+ JsonNode invalidJsonNode = mapper.readTree(INVALID_JSON_STRING);
ConfigApplyDelegate delegate = new MockDelegate();
config = new SegmentRoutingAppConfig();
config.init(subject, key, jsonNode, mapper, delegate);
+ invalidConfig = new SegmentRoutingAppConfig();
+ invalidConfig.init(subject, key, invalidJsonNode, mapper, delegate);
}
/**
- * Tests vRouters getter.
+ * Tests config validity.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testIsValid() throws Exception {
+ assertTrue(config.isValid());
+ assertFalse(invalidConfig.isValid());
+ }
+
+ /**
+ * Tests vRouterMacs getter.
*
* @throws Exception
*/
@Test
public void testVRouters() throws Exception {
- assertTrue(config.isValid());
-
Set<MacAddress> vRouters = config.vRouterMacs();
assertThat(vRouters.size(), is(2));
- assertTrue(vRouters.contains(routerMac1));
- assertTrue(vRouters.contains(routerMac2));
+ assertTrue(vRouters.contains(ROUTER_MAC_1));
+ assertTrue(vRouters.contains(ROUTER_MAC_2));
}
/**
- * Tests vRouters setter.
+ * Tests vRouterMacs setter.
*
* @throws Exception
*/
@Test
public void testSetVRouters() throws Exception {
ImmutableSet.Builder<MacAddress> builder = ImmutableSet.builder();
- builder.add(routerMac3);
+ builder.add(ROUTER_MAC_3);
config.setVRouterMacs(builder.build());
Set<MacAddress> macs = config.vRouterMacs();
assertThat(macs.size(), is(1));
- assertTrue(macs.contains(routerMac3));
+ assertTrue(macs.contains(ROUTER_MAC_3));
+ }
+
+ /**
+ * Tests vRouterId getter.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testVRouterId() throws Exception {
+ assertThat(config.vRouterId(), is(VROUTER_ID_1));
+ }
+
+ /**
+ * Tests vRouterId setter.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testSetVRouterId() throws Exception {
+ config.setVRouterId(VROUTER_ID_2);
+ assertThat(config.vRouterId(), is(VROUTER_ID_2));
+ }
+
+ /**
+ * Tests excludePort getter.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testExcludePorts() throws Exception {
+ Set<String> excludePorts = config.excludePorts();
+ assertThat(excludePorts.size(), is(2));
+ assertTrue(excludePorts.contains(PORT_NAME_1));
+ assertTrue(excludePorts.contains(PORT_NAME_2));
+ }
+
+ /**
+ * Tests excludePort setter.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testSetExcludePorts() throws Exception {
+ ImmutableSet.Builder<String> builder = ImmutableSet.builder();
+ builder.add(PORT_NAME_3);
+ config.setExcludePorts(builder.build());
+
+ Set<String> excludePorts = config.excludePorts();
+ assertThat(excludePorts.size(), is(1));
+ assertTrue(excludePorts.contains(PORT_NAME_3));
}
private class MockDelegate implements ConfigApplyDelegate {