[CORD-634]Add dynamic config update support for interfaces
Change-Id: I065ef5df908864f10f60c5491db3ff9e502c4101
diff --git a/src/main/java/org/onosproject/segmentrouting/HostHandler.java b/src/main/java/org/onosproject/segmentrouting/HostHandler.java
index 539af62..07f6d8d 100644
--- a/src/main/java/org/onosproject/segmentrouting/HostHandler.java
+++ b/src/main/java/org/onosproject/segmentrouting/HostHandler.java
@@ -17,6 +17,7 @@
package org.onosproject.segmentrouting;
import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
import org.onlab.packet.MacAddress;
import org.onlab.packet.VlanId;
import org.onosproject.net.ConnectPoint;
@@ -400,4 +401,125 @@
srManager.defaultRoutingHandler.populateRoute(deviceId, ip.toIpPrefix(), mac, vlanId, port);
}
}
+
+ /**
+ * Populate or revoke a bridging rule on given deviceId that matches given vlanId,
+ * and hostMAC connected to given port, and output to given port only when
+ * vlan information is valid.
+ *
+ * @param deviceId device ID that host attaches to
+ * @param portNum port number that host attaches to
+ * @param hostMac mac address of the host connected to the switch port
+ * @param vlanId Vlan ID configured on the switch port
+ * @param popVlan true to pop Vlan tag at TrafficTreatment, false otherwise
+ * @param install true to populate the objective, false to revoke
+ */
+ private void updateBridgingRule(DeviceId deviceId, PortNumber portNum, MacAddress hostMac,
+ VlanId vlanId, boolean popVlan, boolean install) {
+ // Create host selector
+ TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
+ sbuilder.matchEthDst(hostMac);
+
+ // Create host meta
+ TrafficSelector.Builder mbuilder = DefaultTrafficSelector.builder();
+
+ sbuilder.matchVlanId(vlanId);
+ mbuilder.matchVlanId(vlanId);
+
+ // Create host treatment
+ TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
+ tbuilder.immediate().setOutput(portNum);
+
+ if (popVlan) {
+ tbuilder.immediate().popVlan();
+ }
+
+ int portNextObjId = srManager.getPortNextObjectiveId(deviceId, portNum,
+ tbuilder.build(), mbuilder.build(), install);
+ if (portNextObjId != -1) {
+ ForwardingObjective.Builder fob = DefaultForwardingObjective.builder()
+ .withFlag(ForwardingObjective.Flag.SPECIFIC)
+ .withSelector(sbuilder.build())
+ .nextStep(portNextObjId)
+ .withPriority(100)
+ .fromApp(srManager.appId)
+ .makePermanent();
+
+ ObjectiveContext context = new DefaultObjectiveContext(
+ (objective) -> log.debug("Brigding rule for {}/{} {}", hostMac, vlanId,
+ install ? "populated" : "revoked"),
+ (objective, error) -> log.warn("Failed to {} bridging rule for {}/{}: {}",
+ install ? "populate" : "revoke", hostMac, vlanId, error));
+ flowObjectiveService.forward(deviceId, install ? fob.add(context) : fob.remove(context));
+ } else {
+ log.warn("Failed to retrieve next objective for {}/{}", hostMac, vlanId);
+ }
+ }
+
+ /**
+ * Update forwarding objective for unicast bridging and unicast routing.
+ * Also check the validity of updated interface configuration on VLAN.
+ *
+ * @param deviceId device ID that host attaches to
+ * @param portNum port number that host attaches to
+ * @param vlanId Vlan ID configured on the switch port
+ * @param popVlan true to pop Vlan tag at TrafficTreatment, false otherwise
+ * @param install true to populate the objective, false to revoke
+ */
+ void processIntfVlanUpdatedEvent(DeviceId deviceId, PortNumber portNum, VlanId vlanId,
+ boolean popVlan, boolean install) {
+ ConnectPoint connectPoint = new ConnectPoint(deviceId, portNum);
+ Set<Host> hosts = hostService.getConnectedHosts(connectPoint);
+
+ if (hosts == null || hosts.size() == 0) {
+ return;
+ }
+
+ hosts.forEach(host -> {
+ MacAddress mac = host.mac();
+ VlanId hostVlanId = host.vlan();
+
+ // Check whether the host vlan is valid for new interface configuration
+ if ((!popVlan && hostVlanId.equals(vlanId)) ||
+ (popVlan && hostVlanId.equals(VlanId.NONE))) {
+ updateBridgingRule(deviceId, portNum, mac, vlanId, popVlan, install);
+ // Update Forwarding objective and corresponding simple Next objective
+ // for each host and IP address connected to given port
+ host.ipAddresses().forEach(ipAddress ->
+ srManager.routingRulePopulator.updateFwdObj(deviceId, portNum, ipAddress.toIpPrefix(),
+ mac, vlanId, popVlan, install)
+ );
+ }
+ });
+ }
+
+ /**
+ * Populate or revoke routing rule for each host, according to the updated
+ * subnet configuration on the interface.
+ * @param cp connect point of the updated interface
+ * @param ipPrefixSet IP Prefixes added or removed
+ * @param install true if IP Prefixes added, false otherwise
+ */
+ void processIntfIpUpdatedEvent(ConnectPoint cp, Set<IpPrefix> ipPrefixSet, boolean install) {
+ Set<Host> hosts = hostService.getConnectedHosts(cp);
+
+ if (hosts == null || hosts.size() == 0) {
+ log.warn("processIntfIpUpdatedEvent: No hosts connected to {}", cp);
+ return;
+ }
+
+ // Check whether the host IP address is in the interface's subnet
+ hosts.forEach(host ->
+ host.ipAddresses().forEach(hostIpAddress -> {
+ ipPrefixSet.forEach(ipPrefix -> {
+ if (install && ipPrefix.contains(hostIpAddress)) {
+ srManager.routingRulePopulator.populateRoute(cp.deviceId(), hostIpAddress.toIpPrefix(),
+ host.mac(), host.vlan(), cp.port());
+ } else if (!install && ipPrefix.contains(hostIpAddress)) {
+ srManager.routingRulePopulator.revokeRoute(cp.deviceId(), hostIpAddress.toIpPrefix(),
+ host.mac(), host.vlan(), cp.port());
+ }
+ });
+ }));
+ }
}
diff --git a/src/main/java/org/onosproject/segmentrouting/McastHandler.java b/src/main/java/org/onosproject/segmentrouting/McastHandler.java
index a51d2f4..a131e4a 100644
--- a/src/main/java/org/onosproject/segmentrouting/McastHandler.java
+++ b/src/main/java/org/onosproject/segmentrouting/McastHandler.java
@@ -806,4 +806,55 @@
}
return null;
}
+
+ /**
+ * Removes filtering objective for given device and port.
+ *
+ * @param deviceId device ID
+ * @param port ingress port number
+ * @param assignedVlan assigned VLAN ID
+ * @param mcastIp multicast IP address
+ */
+ private void removeFilterToDevice(DeviceId deviceId, PortNumber port, VlanId assignedVlan, IpAddress mcastIp) {
+ // Do nothing if the port is configured as suppressed
+ ConnectPoint connectPoint = new ConnectPoint(deviceId, port);
+ SegmentRoutingAppConfig appConfig = srManager.cfgService
+ .getConfig(srManager.appId, SegmentRoutingAppConfig.class);
+ if (appConfig != null && appConfig.suppressSubnet().contains(connectPoint)) {
+ log.info("Ignore suppressed port {}", connectPoint);
+ return;
+ }
+
+ FilteringObjective.Builder filtObjBuilder =
+ filterObjBuilder(deviceId, port, assignedVlan, mcastIp);
+ ObjectiveContext context = new DefaultObjectiveContext(
+ (objective) -> log.debug("Successfully removed filter on {}/{}, vlan {}",
+ deviceId, port.toLong(), assignedVlan),
+ (objective, error) ->
+ log.warn("Failed to remove filter on {}/{}, vlan {}: {}",
+ deviceId, port.toLong(), assignedVlan, error));
+ srManager.flowObjectiveService.filter(deviceId, filtObjBuilder.remove(context));
+ }
+
+ /**
+ * Adds or removes filtering objective for given device and port.
+ *
+ * @param deviceId device ID
+ * @param portNum ingress port number
+ * @param vlanId assigned VLAN ID
+ * @param install true to add, false to remove
+ */
+ protected void updateFilterToDevice(DeviceId deviceId, PortNumber portNum,
+ VlanId vlanId, boolean install) {
+ srManager.multicastRouteService.getRoutes().forEach(mcastRoute -> {
+ ConnectPoint source = srManager.multicastRouteService.fetchSource(mcastRoute);
+ if (source.deviceId().equals(deviceId) && source.port().equals(portNum)) {
+ if (install) {
+ addFilterToDevice(deviceId, portNum, vlanId, mcastRoute.group());
+ } else {
+ removeFilterToDevice(deviceId, portNum, vlanId, mcastRoute.group());
+ }
+ }
+ });
+ }
}
diff --git a/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java b/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
index 3fb38ba..85d6e1e 100644
--- a/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
+++ b/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
@@ -767,6 +767,23 @@
return true;
}
+ /**
+ * Updates filtering objectives for a single port. Should only be called by
+ * the master for a switch
+ * @param deviceId device identifier
+ * @param portNum port identifier for port to be filtered
+ * @param pushVlan true to push vlan, false otherwise
+ * @param vlanId vlan identifier
+ * @param install true to install the filtering objective, false to remove
+ */
+ void updateSinglePortFilters(DeviceId deviceId, PortNumber portNum,
+ boolean pushVlan, VlanId vlanId, boolean install) {
+ if (!processSinglePortFiltersInternal(deviceId, portNum, pushVlan, vlanId, install)) {
+ log.warn("Failed to update FilteringObjective for {}/{} with vlan {}",
+ deviceId, portNum, vlanId);
+ }
+ }
+
private boolean processSinglePortFiltersInternal(DeviceId deviceId, PortNumber portnum,
boolean pushVlan, VlanId vlanId, boolean install) {
FilteringObjective.Builder fob = buildFilteringObjective(deviceId, portnum, pushVlan, vlanId);
@@ -774,7 +791,7 @@
// error encountered during build
return false;
}
- log.debug("{} filtering objectives for dev/port:{}/{}",
+ log.debug("{} filtering objectives for dev/port: {}/{}",
install ? "Installing" : "Removing", deviceId, portnum);
ObjectiveContext context = new DefaultObjectiveContext(
(objective) -> log.debug("Filter for {}/{} {}", deviceId, portnum,
@@ -856,11 +873,44 @@
allIps.add(pairRouterIpv6);
}
for (IpAddress ipaddr : allIps) {
- TrafficSelector.Builder sbuilder = buildIpSelectorFromIpAddress(ipaddr);
- Optional<DeviceId> optDeviceId = Optional.of(deviceId);
+ populateSingleIpPunts(deviceId, ipaddr);
+ }
+ }
- srManager.packetService.requestPackets(sbuilder.build(),
- PacketPriority.CONTROL, srManager.appId, optDeviceId);
+ /**
+ * Creates a forwarding objective to punt all IP packets, destined to the
+ * specified IP address, which should be router's port IP address.
+ *
+ * @param deviceId the switch dpid for the router
+ * @param ipAddress the IP address of the router's port
+ */
+ void populateSingleIpPunts(DeviceId deviceId, IpAddress ipAddress) {
+ TrafficSelector.Builder sbuilder = buildIpSelectorFromIpAddress(ipAddress);
+ Optional<DeviceId> optDeviceId = Optional.of(deviceId);
+
+ srManager.packetService.requestPackets(sbuilder.build(),
+ PacketPriority.CONTROL, srManager.appId, optDeviceId);
+ }
+
+ /**
+ * Removes a forwarding objective to punt all IP packets, destined to the
+ * specified IP address, which should be router's port IP address.
+ *
+ * @param deviceId the switch dpid for the router
+ * @param ipAddress the IP address of the router's port
+ */
+ void revokeSingleIpPunts(DeviceId deviceId, IpAddress ipAddress) {
+ TrafficSelector.Builder sbuilder = buildIpSelectorFromIpAddress(ipAddress);
+ Optional<DeviceId> optDeviceId = Optional.of(deviceId);
+
+ try {
+ if (!ipAddress.equals(config.getRouterIpv4(deviceId)) &&
+ !ipAddress.equals(config.getRouterIpv6(deviceId))) {
+ srManager.packetService.cancelPackets(sbuilder.build(),
+ PacketPriority.CONTROL, srManager.appId, optDeviceId);
+ }
+ } catch (DeviceConfigNotFoundException e) {
+ log.warn(e.getMessage() + " Aborting revokeSingleIpPunts");
}
}
@@ -1033,38 +1083,122 @@
*/
void populateSubnetBroadcastRule(DeviceId deviceId) {
srManager.getVlanPortMap(deviceId).asMap().forEach((vlanId, ports) -> {
- int nextId = srManager.getVlanNextObjectiveId(deviceId, vlanId);
-
- if (nextId < 0) {
- log.error("Cannot install vlan {} broadcast rule in dev:{} due"
- + "to vlanId:{} or nextId:{}", vlanId, deviceId, vlanId, nextId);
- return;
- }
-
- // Driver should treat objective with MacAddress.NONE as the
- // subnet broadcast rule
- TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
- sbuilder.matchVlanId(vlanId);
- sbuilder.matchEthDst(MacAddress.NONE);
-
- ForwardingObjective.Builder fob = DefaultForwardingObjective.builder();
- fob.withFlag(Flag.SPECIFIC)
- .withSelector(sbuilder.build())
- .nextStep(nextId)
- .withPriority(SegmentRoutingService.FLOOD_PRIORITY)
- .fromApp(srManager.appId)
- .makePermanent();
- ObjectiveContext context = new DefaultObjectiveContext(
- (objective) -> log.debug("Vlan broadcast rule for {} populated", vlanId),
- (objective, error) ->
- log.warn("Failed to populate vlan broadcast rule for {}: {}", vlanId, error));
- srManager.flowObjectiveService.forward(deviceId, fob.add(context));
+ updateSubnetBroadcastRule(deviceId, vlanId, true);
});
}
+ /**
+ * Creates or removes a forwarding objective to broadcast packets to its subnet.
+ * @param deviceId switch ID to set the rule
+ * @param vlanId vlan ID to specify the subnet
+ * @param install true to install the rule, false to revoke the rule
+ */
+ void updateSubnetBroadcastRule(DeviceId deviceId, VlanId vlanId, boolean install) {
+ int nextId = srManager.getVlanNextObjectiveId(deviceId, vlanId);
+
+ if (nextId < 0) {
+ log.error("Cannot install vlan {} broadcast rule in dev:{} due"
+ + " to vlanId:{} or nextId:{}", vlanId, deviceId, vlanId, nextId);
+ return;
+ }
+
+ // Driver should treat objective with MacAddress.NONE as the
+ // subnet broadcast rule
+ TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
+ sbuilder.matchVlanId(vlanId);
+ sbuilder.matchEthDst(MacAddress.NONE);
+
+ ForwardingObjective.Builder fob = DefaultForwardingObjective.builder();
+ fob.withFlag(Flag.SPECIFIC)
+ .withSelector(sbuilder.build())
+ .nextStep(nextId)
+ .withPriority(SegmentRoutingService.FLOOD_PRIORITY)
+ .fromApp(srManager.appId)
+ .makePermanent();
+ ObjectiveContext context = new DefaultObjectiveContext(
+ (objective) -> log.debug("Vlan broadcast rule for {} populated", vlanId),
+ (objective, error) ->
+ log.warn("Failed to populate vlan broadcast rule for {}: {}", vlanId, error));
+
+ if (install) {
+ srManager.flowObjectiveService.forward(deviceId, fob.add(context));
+ } else {
+ srManager.flowObjectiveService.forward(deviceId, fob.remove(context));
+ }
+ }
+
private int getPriorityFromPrefix(IpPrefix prefix) {
return (prefix.isIp4()) ?
2000 * prefix.prefixLength() + SegmentRoutingService.MIN_IP_PRIORITY :
500 * prefix.prefixLength() + SegmentRoutingService.MIN_IP_PRIORITY;
}
+
+ /**
+ * Update Forwarding objective for each host and IP address connected to given port.
+ * And create corresponding Simple Next objective if it does not exist.
+ * Applied only when populating Forwarding objective
+ * @param deviceId switch ID to set the rule
+ * @param portNumber port number
+ * @param prefix IP prefix of the route
+ * @param hostMac MAC address of the next hop
+ * @param vlanId Vlan ID of the port
+ * @param popVlan true to pop vlan tag in TrafficTreatment
+ * @param install true to populate the forwarding objective, false to revoke
+ */
+ void updateFwdObj(DeviceId deviceId, PortNumber portNumber, IpPrefix prefix, MacAddress hostMac,
+ VlanId vlanId, boolean popVlan, boolean install) {
+ ForwardingObjective.Builder fob;
+ TrafficSelector.Builder sbuilder = buildIpSelectorFromIpPrefix(prefix);
+ MacAddress deviceMac;
+ try {
+ deviceMac = config.getDeviceMac(deviceId);
+ } catch (DeviceConfigNotFoundException e) {
+ log.warn(e.getMessage() + " Aborting updateFwdObj.");
+ return;
+ }
+
+ TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
+ tbuilder.deferred()
+ .setEthDst(hostMac)
+ .setEthSrc(deviceMac)
+ .setOutput(portNumber);
+
+ TrafficSelector.Builder mbuilder = DefaultTrafficSelector.builder();
+
+ if (!popVlan) {
+ tbuilder.setVlanId(vlanId);
+ } else {
+ mbuilder.matchVlanId(vlanId);
+ }
+
+ // if the objective is to revoke an existing rule, and for some reason
+ // the next-objective does not exist, then a new one should not be created
+ int portNextObjId = srManager.getPortNextObjectiveId(deviceId, portNumber,
+ tbuilder.build(), mbuilder.build(), install);
+ if (portNextObjId == -1) {
+ // Warning log will come from getPortNextObjective method
+ return;
+ }
+
+ fob = DefaultForwardingObjective.builder().withSelector(sbuilder.build())
+ .nextStep(portNextObjId).fromApp(srManager.appId).makePermanent()
+ .withPriority(getPriorityFromPrefix(prefix)).withFlag(ForwardingObjective.Flag.SPECIFIC);
+
+ ObjectiveContext context = new DefaultObjectiveContext(
+ (objective) -> log.debug("IP rule for route {} {}", prefix, install ? "installed" : "revoked"),
+ (objective, error) ->
+ log.warn("Failed to {} IP rule for route {}: {}",
+ install ? "install" : "revoke", prefix, error));
+ srManager.flowObjectiveService.forward(deviceId, install ? fob.add(context) : fob.remove(context));
+
+ if (!install) {
+ DefaultGroupHandler grpHandler = srManager.getGroupHandler(deviceId);
+ if (grpHandler == null) {
+ log.warn("updateFwdObj: groupHandler for device {} not found", deviceId);
+ } else {
+ // Remove L3UG for the given port and host
+ grpHandler.removeGroupFromPort(portNumber, tbuilder.build(), mbuilder.build());
+ }
+ }
+ }
}
diff --git a/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java b/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
index 9d4cf04..cd34844 100644
--- a/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
+++ b/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
@@ -15,6 +15,8 @@
*/
package org.onosproject.segmentrouting;
+import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
@@ -40,6 +42,7 @@
import org.onlab.packet.ICMP6;
import org.onlab.packet.IPv4;
import org.onlab.packet.IPv6;
+import org.onlab.packet.IpAddress;
import org.onlab.packet.IpPrefix;
import org.onlab.packet.VlanId;
import org.onlab.util.KryoNamespace;
@@ -56,6 +59,7 @@
import org.onosproject.net.Link;
import org.onosproject.net.Port;
import org.onosproject.net.PortNumber;
+import org.onosproject.net.config.ConfigException;
import org.onosproject.net.config.ConfigFactory;
import org.onosproject.net.config.NetworkConfigEvent;
import org.onosproject.net.config.NetworkConfigListener;
@@ -72,6 +76,7 @@
import org.onosproject.net.host.HostEvent;
import org.onosproject.net.host.HostListener;
import org.onosproject.net.host.HostService;
+import org.onosproject.net.host.InterfaceIpAddress;
import org.onosproject.net.intf.Interface;
import org.onosproject.net.intf.InterfaceService;
import org.onosproject.net.link.LinkEvent;
@@ -751,7 +756,6 @@
}
/**
- * Returns the next objective ID for the given subnet prefix. It is expected
* Returns the next objective ID for the given vlan id. It is expected
* that the next-objective has been pre-created from configuration.
*
@@ -1485,6 +1489,11 @@
case CONFIG_UPDATED:
log.info("Interface Config updated for {}", event.subject());
createOrUpdateDeviceConfiguration();
+
+ // Following code will be uncommented when [CORD-634] is fully implemented.
+ // [CORD-634] Add dynamic config support for interfaces
+ updateInterface((InterfaceConfig) event.config().get(),
+ (InterfaceConfig) event.prevConfig().get());
// TODO support dynamic configuration
break;
default:
@@ -1618,4 +1627,197 @@
}
}
+ private void updateInterface(InterfaceConfig conf, InterfaceConfig prevConf) {
+ try {
+ Set<Interface> intfs = conf.getInterfaces();
+ Set<Interface> prevIntfs = prevConf.getInterfaces();
+
+ // Now we only handle one interface config at each port.
+ if (intfs.size() != 1 || prevIntfs.size() != 1) {
+ log.warn("Interface update aborted - one at a time is allowed, " +
+ "but {} / {}(prev) received.", intfs.size(), prevIntfs.size());
+ return;
+ }
+
+ Interface intf = intfs.stream().findFirst().get();
+ Interface prevIntf = prevIntfs.stream().findFirst().get();
+
+ DeviceId deviceId = intf.connectPoint().deviceId();
+ PortNumber portNum = intf.connectPoint().port();
+
+ if (!mastershipService.isLocalMaster(deviceId)) {
+ log.debug("CONFIG_UPDATED event for interfaces should be " +
+ "handled by master node for device {}", deviceId);
+ return;
+ }
+
+ removeSubnetConfig(prevIntf.connectPoint(),
+ Sets.difference(new HashSet<>(prevIntf.ipAddressesList()),
+ new HashSet<>(intf.ipAddressesList())));
+
+ if (prevIntf.vlanNative() != VlanId.NONE && !intf.vlanNative().equals(prevIntf.vlanNative())) {
+ // RemoveVlanNative
+ updateVlanConfigInternal(deviceId, portNum, prevIntf.vlanNative(), true, false);
+ }
+
+ if (!prevIntf.vlanTagged().isEmpty() && !intf.vlanTagged().equals(prevIntf.vlanTagged())) {
+ // RemoveVlanTagged
+ prevIntf.vlanTagged().stream().filter(i -> !intf.vlanTagged().contains(i)).forEach(
+ vlanId -> updateVlanConfigInternal(deviceId, portNum, vlanId, false, false)
+ );
+ }
+
+ if (prevIntf.vlanUntagged() != VlanId.NONE && !intf.vlanUntagged().equals(prevIntf.vlanUntagged())) {
+ // RemoveVlanUntagged
+ updateVlanConfigInternal(deviceId, portNum, prevIntf.vlanUntagged(), true, false);
+ }
+
+ if (intf.vlanNative() != VlanId.NONE && !prevIntf.vlanNative().equals(intf.vlanNative())) {
+ // AddVlanNative
+ updateVlanConfigInternal(deviceId, portNum, intf.vlanNative(), true, true);
+ }
+
+ if (!intf.vlanTagged().isEmpty() && !intf.vlanTagged().equals(prevIntf.vlanTagged())) {
+ // AddVlanTagged
+ intf.vlanTagged().stream().filter(i -> !prevIntf.vlanTagged().contains(i)).forEach(
+ vlanId -> updateVlanConfigInternal(deviceId, portNum, vlanId, false, true)
+ );
+ }
+
+ if (intf.vlanUntagged() != VlanId.NONE && !prevIntf.vlanUntagged().equals(intf.vlanUntagged())) {
+ // AddVlanUntagged
+ updateVlanConfigInternal(deviceId, portNum, intf.vlanUntagged(), true, true);
+ }
+ addSubnetConfig(prevIntf.connectPoint(),
+ Sets.difference(new HashSet<>(intf.ipAddressesList()),
+ new HashSet<>(prevIntf.ipAddressesList())));
+ } catch (ConfigException e) {
+ log.error("Error in configuration");
+ }
+ }
+
+ private void updateVlanConfigInternal(DeviceId deviceId, PortNumber portNum,
+ VlanId vlanId, boolean pushVlan, boolean install) {
+ DefaultGroupHandler grpHandler = getGroupHandler(deviceId);
+ if (grpHandler == null) {
+ log.warn("Failed to retrieve group handler for device {}", deviceId);
+ return;
+ }
+
+ // Update filtering objective for a single port
+ routingRulePopulator.updateSinglePortFilters(deviceId, portNum, pushVlan, vlanId, install);
+
+ // Update filtering objective for multicast ingress port
+ mcastHandler.updateFilterToDevice(deviceId, portNum, vlanId, install);
+
+ int nextId = getVlanNextObjectiveId(deviceId, vlanId);
+
+ if (nextId != -1 && !install) {
+ // Update next objective for a single port as an output port
+ // Remove a single port from L2FG
+ grpHandler.updateGroupFromVlanConfiguration(portNum, Collections.singleton(vlanId), nextId, install);
+ // Remove L2 Bridging rule and L3 Unicast rule to the host
+ hostHandler.processIntfVlanUpdatedEvent(deviceId, portNum, vlanId, pushVlan, install);
+ // Remove broadcast forwarding rule and corresponding L2FG for VLAN
+ // only if there is no port configured on that VLAN ID
+ if (!getVlanPortMap(deviceId).containsKey(vlanId)) {
+ // Remove broadcast forwarding rule for the VLAN
+ routingRulePopulator.updateSubnetBroadcastRule(deviceId, vlanId, install);
+ // Remove L2FG for VLAN
+ grpHandler.removeBcastGroupFromVlan(deviceId, portNum, vlanId, pushVlan);
+ } else {
+ // Remove L2IG of the port
+ grpHandler.removePortNextObjective(deviceId, portNum, vlanId, pushVlan);
+ }
+ } else if (install) {
+ if (nextId != -1) {
+ // Add a single port to L2FG
+ grpHandler.updateGroupFromVlanConfiguration(portNum, Collections.singleton(vlanId), nextId, install);
+ } else {
+ // Create L2FG for VLAN
+ grpHandler.createBcastGroupFromVlan(vlanId, Collections.singleton(portNum));
+ routingRulePopulator.updateSubnetBroadcastRule(deviceId, vlanId, install);
+ }
+ hostHandler.processIntfVlanUpdatedEvent(deviceId, portNum, vlanId, pushVlan, install);
+ } else {
+ log.warn("Failed to retrieve next objective for vlan {} in device {}:{}", vlanId, deviceId, portNum);
+ }
+ }
+
+ private void removeSubnetConfig(ConnectPoint cp, Set<InterfaceIpAddress> ipAddressSet) {
+ Set<IpPrefix> ipPrefixSet = ipAddressSet.stream().
+ map(InterfaceIpAddress::subnetAddress).collect(Collectors.toSet());
+
+ Set<InterfaceIpAddress> deviceIntfIpAddrs = interfaceService.getInterfaces().stream()
+ .filter(intf -> intf.connectPoint().deviceId().equals(cp.deviceId()))
+ .filter(intf -> !intf.connectPoint().equals(cp))
+ .flatMap(intf -> intf.ipAddressesList().stream())
+ .collect(Collectors.toSet());
+ // 1. Partial subnet population
+ // Remove routing rules for removed subnet from previous configuration,
+ // which does not also exist in other interfaces in the same device
+ Set<IpPrefix> deviceIpPrefixSet = deviceIntfIpAddrs.stream()
+ .map(InterfaceIpAddress::subnetAddress)
+ .collect(Collectors.toSet());
+
+ defaultRoutingHandler.revokeSubnet(
+ ipPrefixSet.stream()
+ .filter(ipPrefix -> !deviceIpPrefixSet.contains(ipPrefix))
+ .collect(Collectors.toSet()));
+
+ // 2. Interface IP punts
+ // Remove IP punts for old Intf address
+ Set<IpAddress> deviceIpAddrs = deviceIntfIpAddrs.stream()
+ .map(InterfaceIpAddress::ipAddress)
+ .collect(Collectors.toSet());
+ ipAddressSet.stream()
+ .map(InterfaceIpAddress::ipAddress)
+ .filter(interfaceIpAddress -> !deviceIpAddrs.contains(interfaceIpAddress))
+ .forEach(interfaceIpAddress ->
+ routingRulePopulator.revokeSingleIpPunts(
+ cp.deviceId(), interfaceIpAddress));
+
+ // 3. Host unicast routing rule
+ // Remove unicast routing rule
+ hostHandler.processIntfIpUpdatedEvent(cp, ipPrefixSet, false);
+ }
+
+ private void addSubnetConfig(ConnectPoint cp, Set<InterfaceIpAddress> ipAddressSet) {
+ Set<IpPrefix> ipPrefixSet = ipAddressSet.stream().
+ map(InterfaceIpAddress::subnetAddress).collect(Collectors.toSet());
+
+ Set<InterfaceIpAddress> deviceIntfIpAddrs = interfaceService.getInterfaces().stream()
+ .filter(intf -> intf.connectPoint().deviceId().equals(cp.deviceId()))
+ .filter(intf -> !intf.connectPoint().equals(cp))
+ .flatMap(intf -> intf.ipAddressesList().stream())
+ .collect(Collectors.toSet());
+ // 1. Partial subnet population
+ // Add routing rules for newly added subnet, which does not also exist in
+ // other interfaces in the same device
+ Set<IpPrefix> deviceIpPrefixSet = deviceIntfIpAddrs.stream()
+ .map(InterfaceIpAddress::subnetAddress)
+ .collect(Collectors.toSet());
+
+ defaultRoutingHandler.populateSubnet(
+ Collections.singleton(cp),
+ ipPrefixSet.stream()
+ .filter(ipPrefix -> !deviceIpPrefixSet.contains(ipPrefix))
+ .collect(Collectors.toSet()));
+
+ // 2. Interface IP punts
+ // Add IP punts for new Intf address
+ Set<IpAddress> deviceIpAddrs = deviceIntfIpAddrs.stream()
+ .map(InterfaceIpAddress::ipAddress)
+ .collect(Collectors.toSet());
+ ipAddressSet.stream()
+ .map(InterfaceIpAddress::ipAddress)
+ .filter(interfaceIpAddress -> !deviceIpAddrs.contains(interfaceIpAddress))
+ .forEach(interfaceIpAddress ->
+ routingRulePopulator.populateSingleIpPunts(
+ cp.deviceId(), interfaceIpAddress));
+
+ // 3. Host unicast routing rule
+ // Add unicast routing rule
+ hostHandler.processIntfIpUpdatedEvent(cp, ipPrefixSet, true);
+ }
}
diff --git a/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultGroupHandler.java b/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultGroupHandler.java
index 1a9a80e..848e2ca 100644
--- a/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultGroupHandler.java
+++ b/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultGroupHandler.java
@@ -1016,13 +1016,64 @@
);
NextObjective nextObj = nextObjBuilder.add(context);
flowObjectiveService.next(deviceId, nextObj);
- log.debug("createBcastGroupFromVlan: Submited next objective {} in device {}",
+ log.debug("createBcastGroupFromVlan: Submitted next objective {} in device {}",
nextId, deviceId);
vlanNextObjStore.put(key, nextId);
}
/**
+ * Removes a single broadcast group from a given vlan id.
+ * The group should be empty.
+ * @param deviceId device Id to remove the group
+ * @param portNum port number related to the group
+ * @param vlanId vlan id of the broadcast group to remove
+ * @param popVlan true if the TrafficTreatment involves pop vlan tag action
+ */
+ public void removeBcastGroupFromVlan(DeviceId deviceId, PortNumber portNum,
+ VlanId vlanId, boolean popVlan) {
+ VlanNextObjectiveStoreKey key = new VlanNextObjectiveStoreKey(deviceId, vlanId);
+
+ if (!vlanNextObjStore.containsKey(key)) {
+ log.debug("Broadcast group for device {} and subnet {} does not exist",
+ deviceId, vlanId);
+ return;
+ }
+
+ TrafficSelector metadata =
+ DefaultTrafficSelector.builder().matchVlanId(vlanId).build();
+
+ int nextId = vlanNextObjStore.get(key);
+
+ NextObjective.Builder nextObjBuilder = DefaultNextObjective
+ .builder().withId(nextId)
+ .withType(NextObjective.Type.BROADCAST).fromApp(appId)
+ .withMeta(metadata);
+
+ TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
+ if (popVlan) {
+ tBuilder.popVlan();
+ }
+ tBuilder.setOutput(portNum);
+ nextObjBuilder.addTreatment(tBuilder.build());
+
+ ObjectiveContext context = new DefaultObjectiveContext(
+ (objective) ->
+ log.debug("removeBroadcastGroupFromVlan removed "
+ + "NextObj {} on {}", nextId, deviceId),
+ (objective, error) ->
+ log.warn("removeBroadcastGroupFromVlan failed to remove "
+ + " NextObj {} on {}: {}", nextId, deviceId, error)
+ );
+ NextObjective nextObj = nextObjBuilder.remove(context);
+ flowObjectiveService.next(deviceId, nextObj);
+ log.debug("removeBcastGroupFromVlan: Submited next objective {} in device {}",
+ nextId, deviceId);
+
+ vlanNextObjStore.remove(key, nextId);
+ }
+
+ /**
* Determine if we should pop given vlan before sending packets to the given port.
*
* @param portNumber port number
@@ -1073,6 +1124,48 @@
}
/**
+ * Removes simple next objective for a single port.
+ *
+ * @param deviceId device id that has the port to deal with
+ * @param portNum the outgoing port on the device
+ * @param vlanId vlan id associated with the port
+ * @param popVlan true if POP_VLAN action is applied on the packets, false otherwise
+ */
+ public void removePortNextObjective(DeviceId deviceId, PortNumber portNum, VlanId vlanId, boolean popVlan) {
+ TrafficSelector.Builder mbuilder = DefaultTrafficSelector.builder();
+ mbuilder.matchVlanId(vlanId);
+
+ TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
+ tbuilder.immediate().setOutput(portNum);
+ if (popVlan) {
+ tbuilder.immediate().popVlan();
+ }
+
+ int portNextObjId = srManager.getPortNextObjectiveId(deviceId, portNum,
+ tbuilder.build(), mbuilder.build(), false);
+
+ PortNextObjectiveStoreKey key = new PortNextObjectiveStoreKey(
+ deviceId, portNum, tbuilder.build(), mbuilder.build());
+ if (portNextObjId != -1 && portNextObjStore.containsKey(key)) {
+ NextObjective.Builder nextObjBuilder = DefaultNextObjective
+ .builder().withId(portNextObjId)
+ .withType(NextObjective.Type.SIMPLE).fromApp(appId);
+ ObjectiveContext context = new DefaultObjectiveContext(
+ (objective) -> log.debug("removePortNextObjective removes NextObj {} on {}",
+ portNextObjId, deviceId),
+ (objective, error) ->
+ log.warn("removePortNextObjective failed to remove NextObj {} on {}: {}",
+ portNextObjId, deviceId, error));
+ NextObjective nextObjective = nextObjBuilder.remove(context);
+ log.info("**removePortNextObjective: Submitted "
+ + "next objective {} in device {}",
+ portNextObjId, deviceId);
+ flowObjectiveService.next(deviceId, nextObjective);
+
+ portNextObjStore.remove(key);
+ }
+ }
+ /**
* Removes groups for the next objective ID given.
*
* @param objectiveId next objective ID to remove
@@ -1105,6 +1198,42 @@
return false;
}
+ /**
+ * Remove simple next objective for a single port. The treatments can include
+ * all outgoing actions that need to happen on the packet.
+ *
+ * @param portNum the outgoing port on the device
+ * @param treatment the actions applied on the packets (should include outport)
+ * @param meta optional data to pass to the driver
+ */
+ public void removeGroupFromPort(PortNumber portNum, TrafficTreatment treatment,
+ TrafficSelector meta) {
+ PortNextObjectiveStoreKey key = new PortNextObjectiveStoreKey(
+ deviceId, portNum, treatment, meta);
+ Integer nextId = portNextObjStore.get(key);
+
+ NextObjective.Builder nextObjBuilder = DefaultNextObjective
+ .builder().withId(nextId)
+ .withType(NextObjective.Type.SIMPLE)
+ .addTreatment(treatment)
+ .fromApp(appId)
+ .withMeta(meta);
+
+ ObjectiveContext context = new DefaultObjectiveContext(
+ (objective) ->
+ log.info("removeGroupFromPort installed "
+ + "NextObj {} on {}", nextId, deviceId),
+ (objective, error) ->
+ log.warn("removeGroupFromPort failed to install"
+ + " NextObj {} on {}: {}", nextId, deviceId, error)
+ );
+ NextObjective nextObj = nextObjBuilder.remove(context);
+ flowObjectiveService.next(deviceId, nextObj);
+ log.info("removeGroupFromPort: Submitted next objective {} in device {} "
+ + "for port {}", nextId, deviceId, portNum);
+
+ portNextObjStore.remove(key);
+ }
/**
* Removes all groups from all next objective stores.
@@ -1133,6 +1262,40 @@
bc.run();
}
+ public void updateGroupFromVlanConfiguration(PortNumber portNumber, Collection<VlanId> vlanIds,
+ int nextId, boolean install) {
+ vlanIds.forEach(vlanId -> updateGroupFromVlanInternal(vlanId, portNumber, nextId, install));
+ }
+
+ private void updateGroupFromVlanInternal(VlanId vlanId, PortNumber portNum, int nextId, boolean install) {
+ TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
+ if (toPopVlan(portNum, vlanId)) {
+ tBuilder.popVlan();
+ }
+ tBuilder.setOutput(portNum);
+
+ TrafficSelector metadata =
+ DefaultTrafficSelector.builder().matchVlanId(vlanId).build();
+
+ NextObjective.Builder nextObjBuilder = DefaultNextObjective
+ .builder().withId(nextId)
+ .withType(NextObjective.Type.BROADCAST).fromApp(appId)
+ .addTreatment(tBuilder.build())
+ .withMeta(metadata);
+
+ ObjectiveContext context = new DefaultObjectiveContext(
+ (objective) -> log.debug("port {} successfully removedFrom NextObj {} on {}",
+ portNum, nextId, deviceId),
+ (objective, error) ->
+ log.warn("port {} failed to removedFrom NextObj {} on {}: {}",
+ portNum, nextId, deviceId, error));
+
+ if (install) {
+ flowObjectiveService.next(deviceId, nextObjBuilder.addToExisting(context));
+ } else {
+ flowObjectiveService.next(deviceId, nextObjBuilder.removeFromExisting(context));
+ }
+ }
/**
* Performs bucket verification operation for all hash groups in this device.