CORD-512 Support vSG <-> vRouter default route
- Support multiple subnets per port. getIpPort() will only return the first non-/32 and non-/0 subnet
/32 is used as vSG subnet
/0 is used as default gateway
- Support multiple L3 unicast group on a single port
Change the way to generate the group ID and group key
- Special case for 0.0.0.0 host. Push a /0 to IP table instead of /32
- Implement vRouterConfig
Put VR MAC to TMAC table of all leaves when config added
When processEthDst see PortNumber.ANY in key, match ETH_DST only
- For OFDPA, wipe existing instruction before sending to controller
So packet that misses L3 unicast table won't be sent to controller twice
- For SpringOpenTTP, pop VLAN before sending to controller
- Move several constant definitions to SegmentRoutingService
- Add minimum priority for IP rules such that /0 won't collide with zero priority default rules
- Update the config sample
Use VLAN=-1 for hosts
Add example for default route
Change-Id: Id751697ce36a7e5c13b3859350ff21b585c38525
diff --git a/pom.xml b/pom.xml
index 5562b9f..68777ef 100644
--- a/pom.xml
+++ b/pom.xml
@@ -97,6 +97,14 @@
<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>
</dependencies>
<build>
diff --git a/src/main/java/org/onosproject/segmentrouting/IcmpHandler.java b/src/main/java/org/onosproject/segmentrouting/IcmpHandler.java
index a8d16e8..d59fb12 100644
--- a/src/main/java/org/onosproject/segmentrouting/IcmpHandler.java
+++ b/src/main/java/org/onosproject/segmentrouting/IcmpHandler.java
@@ -168,7 +168,7 @@
treatment, ByteBuffer.wrap(payload.serialize()));
srManager.packetService.emit(packet);
} else {
- log.warn("Send a MPLS packet as a ICMP response");
+ log.info("Send a MPLS packet as a ICMP response");
TrafficTreatment treatment = DefaultTrafficTreatment.builder()
.setOutput(outport.port())
.build();
diff --git a/src/main/java/org/onosproject/segmentrouting/NetworkConfigEventHandler.java b/src/main/java/org/onosproject/segmentrouting/NetworkConfigEventHandler.java
new file mode 100644
index 0000000..afb7ec9
--- /dev/null
+++ b/src/main/java/org/onosproject/segmentrouting/NetworkConfigEventHandler.java
@@ -0,0 +1,156 @@
+package org.onosproject.segmentrouting;
+
+import com.google.common.collect.ImmutableSet;
+import org.onlab.packet.MacAddress;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.config.NetworkConfigEvent;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.flow.criteria.Criteria;
+import org.onosproject.net.flowobjective.DefaultFilteringObjective;
+import org.onosproject.net.flowobjective.FilteringObjective;
+import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
+import org.onosproject.segmentrouting.config.SegmentRoutingAppConfig;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Handles network config events.
+ */
+public class NetworkConfigEventHandler {
+ private static final Logger log = LoggerFactory.getLogger(NetworkConfigEventHandler.class);
+ private final SegmentRoutingManager srManager;
+ private final DeviceService deviceService;
+
+ /**
+ * Constructs Network Config Event Handler.
+ *
+ * @param srManager instance of {@link SegmentRoutingManager}
+ */
+ public NetworkConfigEventHandler(SegmentRoutingManager srManager) {
+ this.srManager = srManager;
+ this.deviceService = srManager.deviceService;
+ }
+
+ /**
+ * Processes vRouter config added event.
+ *
+ * @param event network config added event
+ */
+ protected void processVRouterConfigAdded(NetworkConfigEvent event) {
+ log.info("Processing vRouter CONFIG_ADDED");
+ SegmentRoutingAppConfig config = (SegmentRoutingAppConfig) event.config().get();
+ deviceService.getAvailableDevices().forEach(device -> {
+ populateVRouter(device.id(), getMacAddresses(config));
+ });
+ }
+
+ /**
+ * Processes vRouter config updated event.
+ *
+ * @param event network config updated event
+ */
+ protected void processVRouterConfigUpdated(NetworkConfigEvent event) {
+ log.info("Processing vRouter CONFIG_UPDATED");
+ SegmentRoutingAppConfig config = (SegmentRoutingAppConfig) event.config().get();
+ SegmentRoutingAppConfig prevConfig = (SegmentRoutingAppConfig) event.prevConfig().get();
+ deviceService.getAvailableDevices().forEach(device -> {
+ Set<MacAddress> macAddresses = getMacAddresses(config);
+ Set<MacAddress> prevMacAddresses = getMacAddresses(prevConfig);
+ // Avoid removing and re-adding unchanged MAC addresses since
+ // FlowObjective does not guarantee the execution order.
+ Set<MacAddress> sameMacAddresses = new HashSet<>(macAddresses);
+ sameMacAddresses.retainAll(prevMacAddresses);
+ macAddresses.removeAll(sameMacAddresses);
+ prevMacAddresses.removeAll(sameMacAddresses);
+
+ revokeVRouter(device.id(), prevMacAddresses);
+ populateVRouter(device.id(), macAddresses);
+ });
+
+ }
+
+ /**
+ * Processes vRouter config removed event.
+ *
+ * @param event network config removed event
+ */
+ protected void processVRouterConfigRemoved(NetworkConfigEvent event) {
+ log.info("Processing vRouter CONFIG_REMOVED");
+ SegmentRoutingAppConfig prevConfig = (SegmentRoutingAppConfig) event.prevConfig().get();
+ deviceService.getAvailableDevices().forEach(device -> {
+ revokeVRouter(device.id(), getMacAddresses(prevConfig));
+ });
+ }
+
+ /**
+ * Populates initial vRouter rules.
+ *
+ * @param deviceId device ID
+ */
+ public void initVRouters(DeviceId deviceId) {
+ SegmentRoutingAppConfig config =
+ srManager.cfgService.getConfig(srManager.appId, SegmentRoutingAppConfig.class);
+ populateVRouter(deviceId, getMacAddresses(config));
+ }
+
+ private void populateVRouter(DeviceId deviceId, Set<MacAddress> pendingAdd) {
+ if (!isEdge(deviceId)) {
+ return;
+ }
+ getVRouterFlowObjBuilders(pendingAdd).forEach(foBuilder -> {
+ srManager.flowObjectiveService.
+ filter(deviceId, foBuilder.add(new SRObjectiveContext(deviceId,
+ SRObjectiveContext.ObjectiveType.FILTER)));
+ });
+ }
+
+ private void revokeVRouter(DeviceId deviceId, Set<MacAddress> pendingRemove) {
+ if (!isEdge(deviceId)) {
+ return;
+ }
+ getVRouterFlowObjBuilders(pendingRemove).forEach(foBuilder -> {
+ srManager.flowObjectiveService.
+ filter(deviceId, foBuilder.remove(new SRObjectiveContext(deviceId,
+ SRObjectiveContext.ObjectiveType.FILTER)));
+ });
+ }
+
+ private Set<FilteringObjective.Builder> getVRouterFlowObjBuilders(Set<MacAddress> macAddresses) {
+ ImmutableSet.Builder<FilteringObjective.Builder> setBuilder = ImmutableSet.builder();
+ macAddresses.forEach(macAddress -> {
+ FilteringObjective.Builder fobuilder = DefaultFilteringObjective.builder();
+ fobuilder.withKey(Criteria.matchInPort(PortNumber.ANY))
+ .addCondition(Criteria.matchEthDst(macAddress))
+ .permit()
+ .withPriority(SegmentRoutingService.DEFAULT_PRIORITY)
+ .fromApp(srManager.appId);
+ setBuilder.add(fobuilder);
+ });
+ return setBuilder.build();
+ }
+
+ private Set<MacAddress> getMacAddresses(SegmentRoutingAppConfig config) {
+ if (config == null) {
+ return ImmutableSet.of();
+ }
+
+ HashSet<MacAddress> macAddresses = new HashSet<>();
+ config.vRouterMacs().forEach(mac -> {
+ macAddresses.add(mac);
+ });
+ return ImmutableSet.copyOf(macAddresses);
+ }
+
+ private boolean isEdge(DeviceId deviceId) {
+ try {
+ if (srManager.deviceConfiguration.isEdgeDevice(deviceId)) {
+ return true;
+ }
+ } catch (DeviceConfigNotFoundException e) { }
+ return false;
+ }
+}
diff --git a/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java b/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
index 6017e8b..10ac6d6 100644
--- a/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
+++ b/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
@@ -39,11 +39,8 @@
import org.onosproject.net.flowobjective.DefaultForwardingObjective;
import org.onosproject.net.flowobjective.FilteringObjective;
import org.onosproject.net.flowobjective.ForwardingObjective;
-import org.onosproject.net.flowobjective.Objective;
-import org.onosproject.net.flowobjective.ObjectiveError;
import org.onosproject.net.flowobjective.ForwardingObjective.Builder;
import org.onosproject.net.flowobjective.ForwardingObjective.Flag;
-import org.onosproject.net.flowobjective.ObjectiveContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -67,14 +64,6 @@
private SegmentRoutingManager srManager;
private DeviceConfiguration config;
- private static final int HIGHEST_PRIORITY = 0xffff;
- //
- private static final int XCONNECT_PRIORITY = 1000;
- private static final int DEFAULT_PRIORITY = 100;
- private static final int FLOOD_PRIORITY = 5;
- private static final long OFPP_MAX = 0xffffff00L;
-
-
/**
* Creates a RoutingRulePopulator object.
*
@@ -160,12 +149,21 @@
throws DeviceConfigNotFoundException {
MacAddress deviceMac;
deviceMac = config.getDeviceMac(deviceId);
+ int priority;
TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
sbuilder.matchEthType(Ethernet.TYPE_IPV4);
- sbuilder.matchIPDst(IpPrefix.valueOf(hostIp, IpPrefix.MAX_INET_MASK_LENGTH));
+ // Special case for default route
+ if (hostIp.isZero()) {
+ sbuilder.matchIPDst(IpPrefix.valueOf(hostIp, 0));
+ priority = SegmentRoutingService.MIN_IP_PRIORITY;
+ } else {
+ Ip4Prefix hostIpPrefix = Ip4Prefix.valueOf(hostIp, IpPrefix.MAX_INET_MASK_LENGTH);
+ sbuilder.matchIPDst(hostIpPrefix);
+ priority = getPriorityFromPrefix(hostIpPrefix);
+ }
TrafficSelector selector = sbuilder.build();
tbuilder.deferred()
@@ -192,7 +190,8 @@
.withSelector(selector)
.nextStep(portNextObjId)
.fromApp(srManager.appId).makePermanent()
- .withPriority(DEFAULT_PRIORITY).withFlag(ForwardingObjective.Flag.SPECIFIC);
+ .withPriority(priority)
+ .withFlag(ForwardingObjective.Flag.SPECIFIC);
}
/**
@@ -277,7 +276,7 @@
.makePermanent()
.nextStep(nextId)
.withSelector(selector)
- .withPriority(2000 * ipPrefix.prefixLength())
+ .withPriority(getPriorityFromPrefix(ipPrefix))
.withFlag(ForwardingObjective.Flag.SPECIFIC);
if (treatment != null) {
fwdBuilder.withTreatment(treatment);
@@ -386,7 +385,7 @@
for (ForwardingObjective.Builder fwdObjBuilder : fwdObjBuilders) {
((Builder) ((Builder) fwdObjBuilder.fromApp(srManager.appId)
.makePermanent()).withSelector(selector)
- .withPriority(DEFAULT_PRIORITY))
+ .withPriority(SegmentRoutingService.DEFAULT_PRIORITY))
.withFlag(ForwardingObjective.Flag.SPECIFIC);
srManager.flowObjectiveService.
forward(deviceId,
@@ -472,7 +471,9 @@
}
for (Port port : srManager.deviceService.getPorts(deviceId)) {
- if (port.number().toLong() > 0 && port.number().toLong() < OFPP_MAX) {
+ if (port.number().toLong() > 0 &&
+ port.number().toLong() < SegmentRoutingService.OFPP_MAX &&
+ port.isEnabled()) {
Ip4Prefix portSubnet = config.getPortSubnet(deviceId, port.number());
VlanId assignedVlan = (portSubnet == null)
? VlanId.vlanId(SegmentRoutingManager.ASSIGNED_VLAN_NO_SUBNET)
@@ -482,7 +483,7 @@
fob.withKey(Criteria.matchInPort(port.number()))
.addCondition(Criteria.matchEthDst(deviceMac))
.addCondition(Criteria.matchVlanId(VlanId.NONE))
- .withPriority(DEFAULT_PRIORITY);
+ .withPriority(SegmentRoutingService.DEFAULT_PRIORITY);
// vlan assignment is valid only if this instance is master
if (srManager.mastershipService.isLocalMaster(deviceId)) {
TrafficTreatment tt = DefaultTrafficTreatment.builder()
@@ -520,7 +521,7 @@
return;
}
ForwardingObjective.Builder puntIp = DefaultForwardingObjective.builder();
- Set<Ip4Address> allIps = new HashSet<Ip4Address>(config.getPortIPs(deviceId));
+ Set<Ip4Address> allIps = new HashSet<>(config.getPortIPs(deviceId));
allIps.add(routerIp);
for (Ip4Address ipaddr : allIps) {
TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
@@ -532,7 +533,7 @@
puntIp.withSelector(sbuilder.build());
puntIp.withTreatment(tbuilder.build());
puntIp.withFlag(Flag.VERSATILE)
- .withPriority(HIGHEST_PRIORITY)
+ .withPriority(SegmentRoutingService.HIGHEST_PRIORITY)
.makePermanent()
.fromApp(srManager.appId);
log.debug("Installing forwarding objective to punt port IP addresses");
@@ -576,7 +577,7 @@
fob.withFlag(Flag.SPECIFIC)
.withSelector(sbuilder.build())
.nextStep(nextId)
- .withPriority(FLOOD_PRIORITY)
+ .withPriority(SegmentRoutingService.FLOOD_PRIORITY)
.fromApp(srManager.appId)
.makePermanent();
@@ -611,7 +612,7 @@
fob.withKey(Criteria.matchInPort(connectPoint.port()))
.addCondition(Criteria.matchVlanId(vlanId))
.addCondition(Criteria.matchEthDst(MacAddress.NONE))
- .withPriority(XCONNECT_PRIORITY);
+ .withPriority(SegmentRoutingService.XCONNECT_PRIORITY);
fob.permit().fromApp(srManager.appId);
srManager.flowObjectiveService
@@ -657,7 +658,7 @@
fob.withFlag(Flag.SPECIFIC)
.withSelector(sbuilder.build())
.nextStep(nextId)
- .withPriority(DEFAULT_PRIORITY)
+ .withPriority(SegmentRoutingService.DEFAULT_PRIORITY)
.fromApp(srManager.appId)
.makePermanent();
@@ -671,29 +672,9 @@
});
}
- private static class SRObjectiveContext implements ObjectiveContext {
- enum ObjectiveType {
- FILTER,
- FORWARDING
- }
- final DeviceId deviceId;
- final ObjectiveType type;
-
- SRObjectiveContext(DeviceId deviceId, ObjectiveType type) {
- this.deviceId = deviceId;
- this.type = type;
- }
- @Override
- public void onSuccess(Objective objective) {
- log.debug("{} objective operation successful in device {}",
- type.name(), deviceId);
- }
-
- @Override
- public void onError(Objective objective, ObjectiveError error) {
- log.warn("{} objective {} operation failed with error: {} in device {}",
- type.name(), objective, error, deviceId);
- }
+ private int getPriorityFromPrefix(IpPrefix prefix) {
+ return (prefix.isIp4()) ?
+ 2000 * prefix.prefixLength() + SegmentRoutingService.MIN_IP_PRIORITY :
+ 500 * prefix.prefixLength() + SegmentRoutingService.MIN_IP_PRIORITY;
}
-
}
diff --git a/src/main/java/org/onosproject/segmentrouting/SRObjectiveContext.java b/src/main/java/org/onosproject/segmentrouting/SRObjectiveContext.java
new file mode 100644
index 0000000..8dd75de
--- /dev/null
+++ b/src/main/java/org/onosproject/segmentrouting/SRObjectiveContext.java
@@ -0,0 +1,40 @@
+package org.onosproject.segmentrouting;
+
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.flowobjective.Objective;
+import org.onosproject.net.flowobjective.ObjectiveContext;
+import org.onosproject.net.flowobjective.ObjectiveError;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Segment Routing Flow Objective Context.
+ */
+public class SRObjectiveContext implements ObjectiveContext {
+ enum ObjectiveType {
+ FILTER,
+ FORWARDING
+ }
+ private final DeviceId deviceId;
+ private final ObjectiveType type;
+
+ private static final Logger log = LoggerFactory
+ .getLogger(SegmentRoutingManager.class);
+
+ SRObjectiveContext(DeviceId deviceId, ObjectiveType type) {
+ this.deviceId = deviceId;
+ this.type = type;
+ }
+ @Override
+ public void onSuccess(Objective objective) {
+ log.debug("{} objective operation successful in device {}",
+ type.name(), deviceId);
+ }
+
+ @Override
+ public void onError(Objective objective, ObjectiveError error) {
+ log.warn("{} objective {} operation failed with error: {} in device {}",
+ type.name(), objective, error, deviceId);
+ }
+}
+
diff --git a/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java b/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
index 592c0b1..32e03de 100644
--- a/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
+++ b/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
@@ -54,7 +54,8 @@
import org.onosproject.net.packet.PacketPriority;
import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
import org.onosproject.segmentrouting.config.DeviceConfiguration;
-import org.onosproject.segmentrouting.config.SegmentRoutingConfig;
+import org.onosproject.segmentrouting.config.SegmentRoutingDeviceConfig;
+import org.onosproject.segmentrouting.config.SegmentRoutingAppConfig;
import org.onosproject.segmentrouting.grouphandler.DefaultGroupHandler;
import org.onosproject.segmentrouting.grouphandler.NeighborSet;
import org.onosproject.segmentrouting.grouphandler.NeighborSetNextObjectiveStoreKey;
@@ -130,6 +131,12 @@
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected MastershipService mastershipService;
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected StorageService storageService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected NetworkConfigRegistry cfgService;
+
protected ArpHandler arpHandler = null;
protected IcmpHandler icmpHandler = null;
protected IpHandler ipHandler = null;
@@ -143,7 +150,9 @@
private InternalPacketProcessor processor = null;
private InternalLinkListener linkListener = null;
private InternalDeviceListener deviceListener = null;
+ private NetworkConfigEventHandler netcfgHandler = null;
private InternalEventHandler eventHandler = new InternalEventHandler();
+ private final InternalHostListener hostListener = new InternalHostListener();
private ScheduledExecutorService executorService = Executors
.newScheduledThreadPool(1);
@@ -181,27 +190,28 @@
private EventuallyConsistentMap<String, Tunnel> tunnelStore = null;
private EventuallyConsistentMap<String, Policy> policyStore = null;
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected StorageService storageService;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected NetworkConfigRegistry cfgService;
-
private final InternalConfigListener cfgListener =
new InternalConfigListener(this);
- @SuppressWarnings({ "unchecked", "rawtypes" })
- private final ConfigFactory cfgFactory =
- new ConfigFactory(SubjectFactories.DEVICE_SUBJECT_FACTORY,
- SegmentRoutingConfig.class,
+ private final ConfigFactory<DeviceId, SegmentRoutingDeviceConfig> cfgDeviceFactory =
+ new ConfigFactory<DeviceId, SegmentRoutingDeviceConfig>(SubjectFactories.DEVICE_SUBJECT_FACTORY,
+ SegmentRoutingDeviceConfig.class,
"segmentrouting") {
@Override
- public SegmentRoutingConfig createConfig() {
- return new SegmentRoutingConfig();
+ public SegmentRoutingDeviceConfig createConfig() {
+ return new SegmentRoutingDeviceConfig();
}
};
- private final InternalHostListener hostListener = new InternalHostListener();
+ private final ConfigFactory<ApplicationId, SegmentRoutingAppConfig> cfgAppFactory =
+ new ConfigFactory<ApplicationId, SegmentRoutingAppConfig>(SubjectFactories.APP_SUBJECT_FACTORY,
+ SegmentRoutingAppConfig.class,
+ "segmentrouting") {
+ @Override
+ public SegmentRoutingAppConfig createConfig() {
+ return new SegmentRoutingAppConfig();
+ }
+ };
private Object threadSchedulerLock = new Object();
private static int numOfEventsQueued = 0;
@@ -223,7 +233,7 @@
@Activate
protected void activate() {
appId = coreService
- .registerApplication("org.onosproject.segmentrouting");
+ .registerApplication(SR_APP_ID);
kryoBuilder = new KryoNamespace.Builder()
.register(NeighborSetNextObjectiveStoreKey.class,
@@ -309,14 +319,15 @@
.build();
cfgService.addListener(cfgListener);
- cfgService.registerConfigFactory(cfgFactory);
-
- hostService.addListener(hostListener);
+ cfgService.registerConfigFactory(cfgDeviceFactory);
+ cfgService.registerConfigFactory(cfgAppFactory);
processor = new InternalPacketProcessor();
linkListener = new InternalLinkListener();
deviceListener = new InternalDeviceListener();
+ netcfgHandler = new NetworkConfigEventHandler(this);
+ hostService.addListener(hostListener);
packetService.addProcessor(processor, PacketProcessor.director(2));
linkService.addListener(linkListener);
deviceService.addListener(deviceListener);
@@ -334,7 +345,8 @@
@Deactivate
protected void deactivate() {
cfgService.removeListener(cfgListener);
- cfgService.unregisterConfigFactory(cfgFactory);
+ cfgService.unregisterConfigFactory(cfgDeviceFactory);
+ cfgService.unregisterConfigFactory(cfgAppFactory);
// Withdraw ARP packet-in
TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
@@ -456,10 +468,17 @@
nextAssignedVlan = (short) (Collections.min(assignedVlans) - 1);
}
for (Ip4Prefix unsub : unassignedSubnets) {
- subnetVidStore.put(new SubnetAssignedVidStoreKey(deviceId, unsub),
- VlanId.vlanId(nextAssignedVlan--));
- log.info("Assigned vlan: {} to subnet: {} on device: {}",
- nextAssignedVlan + 1, unsub, deviceId);
+ // Special case for default route. Assign default VLAN ID to /32 and /0 subnets
+ if (unsub.prefixLength() == IpPrefix.MAX_INET_MASK_LENGTH ||
+ unsub.prefixLength() == 0) {
+ subnetVidStore.put(new SubnetAssignedVidStoreKey(deviceId, unsub),
+ VlanId.vlanId(ASSIGNED_VLAN_NO_SUBNET));
+ } else {
+ subnetVidStore.put(new SubnetAssignedVidStoreKey(deviceId, unsub),
+ VlanId.vlanId(nextAssignedVlan--));
+ log.info("Assigned vlan: {} to subnet: {} on device: {}",
+ nextAssignedVlan + 1, unsub, deviceId);
+ }
}
return subnetVidStore.get(new SubnetAssignedVidStoreKey(deviceId, subnet));
@@ -766,6 +785,8 @@
groupHandler.createGroupsForXConnect(device.id());
routingRulePopulator.populateXConnectBroadcastRule(device.id());
}
+
+ netcfgHandler.initVRouters(device.id());
}
private void processPortRemoved(Device device, Port port) {
@@ -851,14 +872,33 @@
@Override
public void event(NetworkConfigEvent event) {
- if (event.configClass().equals(SegmentRoutingConfig.class)) {
- if (event.type() == NetworkConfigEvent.Type.CONFIG_ADDED) {
- log.info("Network configuration added.");
- configureNetwork();
+ // TODO move this part to NetworkConfigEventHandler
+ if (event.configClass().equals(SegmentRoutingDeviceConfig.class)) {
+ switch (event.type()) {
+ case CONFIG_ADDED:
+ log.info("Segment Routing Config added.");
+ configureNetwork();
+ break;
+ case CONFIG_UPDATED:
+ log.info("Segment Routing Config updated.");
+ // TODO support dynamic configuration
+ break;
+ default:
+ break;
}
- if (event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED) {
- log.info("Network configuration updated.");
- // TODO support dynamic configuration
+ } else if (event.configClass().equals(SegmentRoutingAppConfig.class)) {
+ switch (event.type()) {
+ case CONFIG_ADDED:
+ netcfgHandler.processVRouterConfigAdded(event);
+ break;
+ case CONFIG_UPDATED:
+ netcfgHandler.processVRouterConfigUpdated(event);
+ break;
+ case CONFIG_REMOVED:
+ netcfgHandler.processVRouterConfigRemoved(event);
+ break;
+ default:
+ break;
}
}
}
diff --git a/src/main/java/org/onosproject/segmentrouting/SegmentRoutingService.java b/src/main/java/org/onosproject/segmentrouting/SegmentRoutingService.java
index 44bd453..f82ad3e 100644
--- a/src/main/java/org/onosproject/segmentrouting/SegmentRoutingService.java
+++ b/src/main/java/org/onosproject/segmentrouting/SegmentRoutingService.java
@@ -21,6 +21,40 @@
* Segment Routing Service for REST API.
*/
public interface SegmentRoutingService {
+ /**
+ * Segment Routing App ID.
+ */
+ String SR_APP_ID = "org.onosproject.segmentrouting";
+
+ /**
+ * Highest flow priority.
+ */
+ int HIGHEST_PRIORITY = 0xffff;
+
+ /**
+ * VLAN cross-connect priority.
+ */
+ int XCONNECT_PRIORITY = 1000;
+
+ /**
+ * Default flow priority.
+ */
+ int DEFAULT_PRIORITY = 100;
+
+ /**
+ * Minimum IP priority.
+ *
+ * Should > 0 such that priority of /0 will not conflict with lowest
+ * priority default entries.
+ */
+ int MIN_IP_PRIORITY = 10;
+
+ /**
+ * Subnet flooding flow priority.
+ */
+ int FLOOD_PRIORITY = 5;
+
+ long OFPP_MAX = 0xffffff00L;
/**
* Returns all tunnels.
diff --git a/src/main/java/org/onosproject/segmentrouting/config/DeviceConfiguration.java b/src/main/java/org/onosproject/segmentrouting/config/DeviceConfiguration.java
index 3d2d337..db4bc63 100644
--- a/src/main/java/org/onosproject/segmentrouting/config/DeviceConfiguration.java
+++ b/src/main/java/org/onosproject/segmentrouting/config/DeviceConfiguration.java
@@ -15,9 +15,12 @@
*/
package org.onosproject.segmentrouting.config;
+import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.SetMultimap;
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.incubator.net.config.basics.ConfigException;
@@ -37,6 +40,7 @@
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@@ -59,13 +63,13 @@
Ip4Address ip;
MacAddress mac;
boolean isEdge;
- HashMap<PortNumber, Ip4Address> gatewayIps;
- HashMap<PortNumber, Ip4Prefix> subnets;
+ Map<PortNumber, Ip4Address> gatewayIps;
+ SetMultimap<PortNumber, Ip4Prefix> subnets;
Map<Integer, Set<Integer>> adjacencySids;
public SegmentRouterInfo() {
gatewayIps = new HashMap<>();
- subnets = new HashMap<>();
+ subnets = HashMultimap.create();
}
}
@@ -78,10 +82,10 @@
public DeviceConfiguration(NetworkConfigRegistry cfgService) {
// Read config from device subject, excluding gatewayIps and subnets.
Set<DeviceId> deviceSubjects =
- cfgService.getSubjects(DeviceId.class, SegmentRoutingConfig.class);
+ cfgService.getSubjects(DeviceId.class, SegmentRoutingDeviceConfig.class);
deviceSubjects.forEach(subject -> {
- SegmentRoutingConfig config =
- cfgService.getConfig(subject, SegmentRoutingConfig.class);
+ SegmentRoutingDeviceConfig config =
+ cfgService.getConfig(subject, SegmentRoutingDeviceConfig.class);
SegmentRouterInfo info = new SegmentRouterInfo();
info.deviceId = subject;
info.nodeSid = config.nodeSid();
@@ -119,7 +123,11 @@
// Extract subnet information
Set<InterfaceIpAddress> interfaceAddresses = networkInterface.ipAddresses();
interfaceAddresses.forEach(interfaceAddress -> {
- info.gatewayIps.put(port, interfaceAddress.ipAddress().getIp4Address());
+ // Do not add /0 and /32 to gateway IP list
+ int prefixLength = interfaceAddress.subnetAddress().prefixLength();
+ if (prefixLength != 0 && prefixLength != IpPrefix.MAX_INET_MASK_LENGTH) {
+ info.gatewayIps.put(port, interfaceAddress.ipAddress().getIp4Address());
+ }
info.subnets.put(port, interfaceAddress.subnetAddress().getIp4Prefix());
});
@@ -247,9 +255,13 @@
Map<Ip4Prefix, List<PortNumber>> subnetPortMap = new HashMap<>();
// Construct subnet-port mapping from port-subnet mapping
- Map<PortNumber, Ip4Prefix> portSubnetMap =
+ SetMultimap<PortNumber, Ip4Prefix> portSubnetMap =
this.deviceConfigMap.get(deviceId).subnets;
- portSubnetMap.forEach((port, subnet) -> {
+
+ portSubnetMap.entries().forEach(entry -> {
+ PortNumber port = entry.getKey();
+ Ip4Prefix subnet = entry.getValue();
+
if (subnetPortMap.containsKey(subnet)) {
subnetPortMap.get(subnet).add(port);
} else {
@@ -258,7 +270,6 @@
subnetPortMap.put(subnet, ports);
}
});
-
return subnetPortMap;
}
@@ -322,21 +333,6 @@
}
/**
- * Returns the configured IP addresses per port
- * for a segment router.
- *
- * @param deviceId device identifier
- * @return map of port to gateway IP addresses or null if not found
- */
- public Map<PortNumber, Ip4Address> getPortIPMap(DeviceId deviceId) {
- SegmentRouterInfo srinfo = deviceConfigMap.get(deviceId);
- if (srinfo != null) {
- return srinfo.gatewayIps;
- }
- return null;
- }
-
- /**
* Returns the configured subnet prefixes for a segment router.
*
* @param deviceId device identifier
@@ -353,8 +349,8 @@
}
/**
- * Returns the configured subnet on the given port, or null if no
- * subnet has been configured on the port.
+ * Returns the configured non-/32 and non-/0 subnet on the given port,
+ * or null if no subnet has been configured on the port.
*
* @param deviceId device identifier
* @param pnum port identifier
@@ -363,7 +359,12 @@
public Ip4Prefix getPortSubnet(DeviceId deviceId, PortNumber pnum) {
SegmentRouterInfo srinfo = deviceConfigMap.get(deviceId);
if (srinfo != null) {
- return srinfo.subnets.get(pnum);
+ Optional<Ip4Prefix> result = srinfo.subnets.get(pnum).stream()
+ .filter(subnet ->
+ subnet.getIp4Prefix().prefixLength() != IpPrefix.MAX_INET_MASK_LENGTH &&
+ subnet.getIp4Prefix().prefixLength() != 0)
+ .findFirst();
+ return (result.isPresent()) ? result.get() : null;
}
return null;
}
@@ -378,7 +379,7 @@
public Ip4Address getRouterIpAddressForASubnetHost(Ip4Address destIpAddress) {
for (Map.Entry<DeviceId, SegmentRouterInfo> entry:
deviceConfigMap.entrySet()) {
- for (Ip4Prefix prefix:entry.getValue().subnets.values()) {
+ for (Ip4Prefix prefix : entry.getValue().subnets.values()) {
if (prefix.contains(destIpAddress)) {
return entry.getValue().ip;
}
@@ -428,7 +429,8 @@
}
for (Ip4Prefix subnet: subnets) {
- if (subnet.contains(hostIp)) {
+ // Exclude /0 since it is a special case used for default route
+ if (subnet.prefixLength() != 0 && subnet.contains(hostIp)) {
return true;
}
}
diff --git a/src/main/java/org/onosproject/segmentrouting/config/SegmentRoutingAppConfig.java b/src/main/java/org/onosproject/segmentrouting/config/SegmentRoutingAppConfig.java
new file mode 100644
index 0000000..e39fb18
--- /dev/null
+++ b/src/main/java/org/onosproject/segmentrouting/config/SegmentRoutingAppConfig.java
@@ -0,0 +1,97 @@
+/*
+ * 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.segmentrouting.config;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.google.common.collect.ImmutableSet;
+import org.onlab.packet.MacAddress;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.net.config.Config;
+import java.util.Set;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+
+/**
+ * App configuration object for Segment Routing.
+ */
+public class SegmentRoutingAppConfig extends Config<ApplicationId> {
+ private static final String VROUTER_MACS = "vRouterMacs";
+
+ @Override
+ public boolean isValid() {
+ return hasOnlyFields(VROUTER_MACS) && vRouterMacs() != null;
+ }
+
+ /**
+ * Gets vRouters from the config.
+ *
+ * @return a set of vRouter MAC addresses
+ */
+ public Set<MacAddress> vRouterMacs() {
+ if (!object.has(VROUTER_MACS)) {
+ return null;
+ }
+
+ ImmutableSet.Builder<MacAddress> builder = ImmutableSet.builder();
+ ArrayNode arrayNode = (ArrayNode) object.path(VROUTER_MACS);
+ for (JsonNode jsonNode : arrayNode) {
+ MacAddress mac;
+
+ String macStr = jsonNode.asText(null);
+ if (macStr == null) {
+ return null;
+ }
+ try {
+ mac = MacAddress.valueOf(macStr);
+ } catch (IllegalArgumentException e) {
+ return null;
+ }
+
+ builder.add(mac);
+ }
+ return builder.build();
+ }
+
+ /**
+ * Sets vRouters to the config.
+ *
+ * @param vRouterMacs a set of vRouter MAC addresses
+ * @return this {@link SegmentRoutingAppConfig}
+ */
+ public SegmentRoutingAppConfig setVRouterMacs(Set<MacAddress> vRouterMacs) {
+ if (vRouterMacs == null) {
+ object.remove(VROUTER_MACS);
+ } else {
+ ArrayNode arrayNode = mapper.createArrayNode();
+
+ vRouterMacs.forEach(mac -> {
+ arrayNode.add(mac.toString());
+ });
+
+ object.set(VROUTER_MACS, arrayNode);
+ }
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ return toStringHelper(this)
+ .add("vRouterMacs", vRouterMacs())
+ .toString();
+ }
+}
diff --git a/src/main/java/org/onosproject/segmentrouting/config/SegmentRoutingConfig.java b/src/main/java/org/onosproject/segmentrouting/config/SegmentRoutingDeviceConfig.java
similarity index 87%
rename from src/main/java/org/onosproject/segmentrouting/config/SegmentRoutingConfig.java
rename to src/main/java/org/onosproject/segmentrouting/config/SegmentRoutingDeviceConfig.java
index 64486c6..10c69ca 100644
--- a/src/main/java/org/onosproject/segmentrouting/config/SegmentRoutingConfig.java
+++ b/src/main/java/org/onosproject/segmentrouting/config/SegmentRoutingDeviceConfig.java
@@ -34,7 +34,7 @@
/**
* Configuration object for Segment Routing Application.
*/
-public class SegmentRoutingConfig extends Config<DeviceId> {
+public class SegmentRoutingDeviceConfig extends Config<DeviceId> {
private static final String NAME = "name";
private static final String IP = "routerIp";
private static final String MAC = "routerMac";
@@ -71,8 +71,8 @@
* @param name name of the router.
* @return the config of the router.
*/
- public SegmentRoutingConfig setName(String name) {
- return (SegmentRoutingConfig) setOrClear(NAME, name);
+ public SegmentRoutingDeviceConfig setName(String name) {
+ return (SegmentRoutingDeviceConfig) setOrClear(NAME, name);
}
/**
@@ -91,8 +91,8 @@
* @param ip IP address of the router.
* @return the config of the router.
*/
- public SegmentRoutingConfig setRouterIp(String ip) {
- return (SegmentRoutingConfig) setOrClear(IP, ip);
+ public SegmentRoutingDeviceConfig setRouterIp(String ip) {
+ return (SegmentRoutingDeviceConfig) setOrClear(IP, ip);
}
/**
@@ -111,8 +111,8 @@
* @param mac MAC address of the router.
* @return the config of the router.
*/
- public SegmentRoutingConfig setRouterMac(String mac) {
- return (SegmentRoutingConfig) setOrClear(MAC, mac);
+ public SegmentRoutingDeviceConfig setRouterMac(String mac) {
+ return (SegmentRoutingDeviceConfig) setOrClear(MAC, mac);
}
/**
@@ -130,8 +130,8 @@
* @param sid node SID of the router.
* @return the config of the router.
*/
- public SegmentRoutingConfig setNodeSid(int sid) {
- return (SegmentRoutingConfig) setOrClear(SID, sid);
+ public SegmentRoutingDeviceConfig setNodeSid(int sid) {
+ return (SegmentRoutingDeviceConfig) setOrClear(SID, sid);
}
/**
@@ -154,8 +154,8 @@
* @param isEdgeRouter true if the router is an edge router.
* @return the config of the router.
*/
- public SegmentRoutingConfig setIsEdgeRouter(boolean isEdgeRouter) {
- return (SegmentRoutingConfig) setOrClear(EDGE, isEdgeRouter);
+ public SegmentRoutingDeviceConfig setIsEdgeRouter(boolean isEdgeRouter) {
+ return (SegmentRoutingDeviceConfig) setOrClear(EDGE, isEdgeRouter);
}
/**
@@ -197,7 +197,7 @@
* @param adjacencySids adjacency SIDs of the router.
* @return the config of the router.
*/
- public SegmentRoutingConfig setAdjacencySids(Map<Integer, Set<Integer>> adjacencySids) {
+ public SegmentRoutingDeviceConfig setAdjacencySids(Map<Integer, Set<Integer>> adjacencySids) {
if (adjacencySids == null) {
object.remove(ADJSIDS);
} else {
diff --git a/src/test/java/org/onosproject/segmentrouting/config/SegmentRoutingAppConfigTest.java b/src/test/java/org/onosproject/segmentrouting/config/SegmentRoutingAppConfigTest.java
new file mode 100644
index 0000000..7755059
--- /dev/null
+++ b/src/test/java/org/onosproject/segmentrouting/config/SegmentRoutingAppConfigTest.java
@@ -0,0 +1,111 @@
+/*
+ * 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.segmentrouting.config;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.common.collect.ImmutableSet;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.packet.MacAddress;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.TestApplicationId;
+import org.onosproject.net.config.Config;
+import org.onosproject.net.config.ConfigApplyDelegate;
+import org.onosproject.segmentrouting.SegmentRoutingService;
+import java.util.Set;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Tests for class {@link SegmentRoutingAppConfig}.
+ */
+public class SegmentRoutingAppConfigTest {
+ private static final ApplicationId APP_ID =
+ new TestApplicationId(SegmentRoutingService.SR_APP_ID);
+
+ private SegmentRoutingAppConfig config;
+ private MacAddress routerMac1;
+ private MacAddress routerMac2;
+ private MacAddress routerMac3;
+
+ /**
+ * Initialize test related variables.
+ *
+ * @throws Exception
+ */
+ @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);
+ ConfigApplyDelegate delegate = new MockDelegate();
+
+ config = new SegmentRoutingAppConfig();
+ config.init(subject, key, jsonNode, mapper, delegate);
+ }
+
+ /**
+ * Tests vRouters 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));
+ }
+
+ /**
+ * Tests vRouters setter.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testSetVRouters() throws Exception {
+ ImmutableSet.Builder<MacAddress> builder = ImmutableSet.builder();
+ builder.add(routerMac3);
+ config.setVRouterMacs(builder.build());
+
+ Set<MacAddress> macs = config.vRouterMacs();
+ assertThat(macs.size(), is(1));
+ assertTrue(macs.contains(routerMac3));
+ }
+
+ private class MockDelegate implements ConfigApplyDelegate {
+ @Override
+ public void onApply(Config config) {
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/org/onosproject/segmentrouting/config/SegmentRoutingConfigTest.java b/src/test/java/org/onosproject/segmentrouting/config/SegmentRoutingDeviceConfigTest.java
similarity index 94%
rename from src/test/java/org/onosproject/segmentrouting/config/SegmentRoutingConfigTest.java
rename to src/test/java/org/onosproject/segmentrouting/config/SegmentRoutingDeviceConfigTest.java
index 3e5daa5..fd661b6 100644
--- a/src/test/java/org/onosproject/segmentrouting/config/SegmentRoutingConfigTest.java
+++ b/src/test/java/org/onosproject/segmentrouting/config/SegmentRoutingDeviceConfigTest.java
@@ -36,10 +36,10 @@
import static org.junit.Assert.assertTrue;
/**
- * Tests for class {@link SegmentRoutingConfig}.
+ * Tests for class {@link SegmentRoutingDeviceConfig}.
*/
-public class SegmentRoutingConfigTest {
- private SegmentRoutingConfig config;
+public class SegmentRoutingDeviceConfigTest {
+ private SegmentRoutingDeviceConfig config;
private Map<Integer, Set<Integer>> adjacencySids1;
private Map<Integer, Set<Integer>> adjacencySids2;
@@ -72,12 +72,12 @@
adjacencySids2.put(300, ports3);
DeviceId subject = DeviceId.deviceId("of:0000000000000001");
- String key = "org.onosproject.segmentrouting";
+ String key = "segmentrouting";
ObjectMapper mapper = new ObjectMapper();
JsonNode jsonNode = mapper.readTree(jsonString);
ConfigApplyDelegate delegate = new MockDelegate();
- config = new SegmentRoutingConfig();
+ config = new SegmentRoutingDeviceConfig();
config.init(subject, key, jsonNode, mapper, delegate);
}