CORD-546 Push L3 unicast rules for bgp peers when they are learned
- Change L3 unicast group id/key generation to include src MAC
- Note: Only flows are removed when a peer is gone
since the group may still be referenced by routes announced by peer.
It does no harm even if the group is not referenced.
- Note: We assume that peer does not move or update IP
Also fix several SR/VR integration issues, including
- Do not push broadcast group for /32
Change-Id: Ifb03601f5341f8b7717ea1fbccbc569b07f66476
diff --git a/apps/routing/src/main/java/org/onosproject/routing/impl/ControlPlaneRedirectManager.java b/apps/routing/src/main/java/org/onosproject/routing/impl/ControlPlaneRedirectManager.java
index 54a6966..b85f79f 100644
--- a/apps/routing/src/main/java/org/onosproject/routing/impl/ControlPlaneRedirectManager.java
+++ b/apps/routing/src/main/java/org/onosproject/routing/impl/ControlPlaneRedirectManager.java
@@ -16,19 +16,25 @@
package org.onosproject.routing.impl;
+import com.google.common.collect.ImmutableSortedSet;
+import com.google.common.collect.Maps;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.onlab.packet.EthType;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.MacAddress;
import org.onlab.packet.VlanId;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
import org.onosproject.incubator.net.intf.Interface;
import org.onosproject.incubator.net.intf.InterfaceService;
+import org.onosproject.mastership.MastershipService;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.DeviceId;
+import org.onosproject.net.Host;
import org.onosproject.net.PortNumber;
import org.onosproject.net.config.NetworkConfigEvent;
import org.onosproject.net.config.NetworkConfigListener;
@@ -45,15 +51,23 @@
import org.onosproject.net.flowobjective.FlowObjectiveService;
import org.onosproject.net.flowobjective.ForwardingObjective;
import org.onosproject.net.flowobjective.NextObjective;
+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.routing.RoutingService;
import org.onosproject.routing.config.RouterConfig;
import org.slf4j.Logger;
import java.util.Collections;
+import java.util.Iterator;
import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
import static org.slf4j.LoggerFactory.getLogger;
+import static com.google.common.base.Preconditions.checkState;
/**
* Manages connectivity between peers redirecting control traffic to a routing
@@ -64,7 +78,8 @@
private final Logger log = getLogger(getClass());
- private static final int PRIORITY = 40001;
+ private static final int MIN_IP_PRIORITY = 10;
+ private static final int ACL_PRIORITY = 40001;
private static final int OSPF_IP_PROTO = 0x59;
private static final String APP_NAME = "org.onosproject.cpredirect";
@@ -73,6 +88,7 @@
private ConnectPoint controlPlaneConnectPoint;
private boolean ospfEnabled = false;
private List<String> interfaces = Collections.emptyList();
+ private Map<Host, Set<Integer>> peerNextId = Maps.newConcurrentMap();
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected CoreService coreService;
@@ -89,9 +105,16 @@
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected NetworkConfigService networkConfigService;
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected MastershipService mastershipService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected HostService hostService;
+
private final InternalDeviceListener deviceListener = new InternalDeviceListener();
private final InternalNetworkConfigListener networkConfigListener =
new InternalNetworkConfigListener();
+ private final InternalHostListener hostListener = new InternalHostListener();
@Activate
public void activate() {
@@ -99,6 +122,7 @@
deviceService.addListener(deviceListener);
networkConfigService.addListener(networkConfigListener);
+ hostService.addListener(hostListener);
updateConfig();
}
@@ -107,6 +131,7 @@
public void deactivate() {
deviceService.removeListener(deviceListener);
networkConfigService.removeListener(networkConfigListener);
+ hostService.removeListener(hostListener);
}
private void updateConfig() {
@@ -309,7 +334,7 @@
fobBuilder.nextStep(nextId);
}
fobBuilder.fromApp(appId)
- .withPriority(PRIORITY)
+ .withPriority(ACL_PRIORITY)
.withFlag(ForwardingObjective.Flag.VERSATILE);
return add ? fobBuilder.add() : fobBuilder.remove();
@@ -366,4 +391,149 @@
}
}
}
+
+ /**
+ * Listener for host events.
+ */
+ private class InternalHostListener implements HostListener {
+ private void peerAdded(HostEvent event) {
+ Host peer = event.subject();
+ Optional<Interface> peerIntf =
+ interfaceService.getInterfacesByPort(peer.location()).stream()
+ .filter(intf -> interfaces.isEmpty() || interfaces.contains(intf.name()))
+ .filter(intf -> peer.vlan().equals(intf.vlan()))
+ .findFirst();
+ if (!peerIntf.isPresent()) {
+ log.debug("Adding peer {}/{} on {} but the interface is not configured",
+ peer.mac(), peer.vlan(), peer.location());
+ return;
+ }
+
+ // Generate L3 Unicast groups and store it in the map
+ int toRouterL3Unicast = createPeerGroup(peer.mac(), peerIntf.get().mac(),
+ peer.vlan(), peer.location().deviceId(), controlPlaneConnectPoint.port());
+ int toPeerL3Unicast = createPeerGroup(peerIntf.get().mac(), peer.mac(),
+ peer.vlan(), peer.location().deviceId(), peer.location().port());
+ peerNextId.put(peer, ImmutableSortedSet.of(toRouterL3Unicast, toPeerL3Unicast));
+
+ // From peer to router
+ peerIntf.get().ipAddresses().forEach(routerIp -> {
+ flowObjectiveService.forward(peer.location().deviceId(),
+ createPeerObjBuilder(toRouterL3Unicast, routerIp.ipAddress().toIpPrefix()).add());
+ });
+
+ // From router to peer
+ peer.ipAddresses().forEach(peerIp -> {
+ flowObjectiveService.forward(peer.location().deviceId(),
+ createPeerObjBuilder(toPeerL3Unicast, peerIp.toIpPrefix()).add());
+ });
+ }
+
+ private void peerRemoved(HostEvent event) {
+ Host peer = event.subject();
+ Optional<Interface> peerIntf =
+ interfaceService.getInterfacesByPort(peer.location()).stream()
+ .filter(intf -> interfaces.isEmpty() || interfaces.contains(intf.name()))
+ .filter(intf -> peer.vlan().equals(intf.vlan()))
+ .findFirst();
+ if (!peerIntf.isPresent()) {
+ log.debug("Removing peer {}/{} on {} but the interface is not configured",
+ peer.mac(), peer.vlan(), peer.location());
+ return;
+ }
+
+ Set<Integer> nextIds = peerNextId.get(peer);
+ checkState(peerNextId.get(peer) != null,
+ "Peer nextId should not be null");
+ checkState(peerNextId.get(peer).size() == 2,
+ "Wrong nextId associated with the peer");
+ Iterator<Integer> iter = peerNextId.get(peer).iterator();
+ int toRouterL3Unicast = iter.next();
+ int toPeerL3Unicast = iter.next();
+
+ // From peer to router
+ peerIntf.get().ipAddresses().forEach(routerIp -> {
+ flowObjectiveService.forward(peer.location().deviceId(),
+ createPeerObjBuilder(toRouterL3Unicast, routerIp.ipAddress().toIpPrefix()).remove());
+ });
+
+ // From router to peer
+ peer.ipAddresses().forEach(peerIp -> {
+ flowObjectiveService.forward(peer.location().deviceId(),
+ createPeerObjBuilder(toPeerL3Unicast, peerIp.toIpPrefix()).remove());
+ });
+ }
+
+ private ForwardingObjective.Builder createPeerObjBuilder(
+ int nextId, IpPrefix ipAddresses) {
+ TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
+ sbuilder.matchEthType(EthType.EtherType.IPV4.ethType().toShort());
+ sbuilder.matchIPDst(ipAddresses);
+ DefaultForwardingObjective.Builder builder =
+ DefaultForwardingObjective.builder()
+ .withSelector(sbuilder.build())
+ .fromApp(appId)
+ .withPriority(getPriorityFromPrefix(ipAddresses))
+ .withFlag(ForwardingObjective.Flag.SPECIFIC);
+ if (nextId != -1) {
+ builder.nextStep(nextId);
+ }
+ return builder;
+ }
+
+ private int createPeerGroup(MacAddress srcMac, MacAddress dstMac,
+ VlanId vlanId, DeviceId deviceId, PortNumber port) {
+ int nextId = flowObjectiveService.allocateNextId();
+ NextObjective.Builder nextObjBuilder = DefaultNextObjective.builder()
+ .withId(nextId)
+ .withType(NextObjective.Type.SIMPLE)
+ .fromApp(appId);
+
+ TrafficTreatment.Builder ttBuilder = DefaultTrafficTreatment.builder();
+ ttBuilder.setEthSrc(srcMac);
+ ttBuilder.setEthDst(dstMac);
+ ttBuilder.setOutput(port);
+ nextObjBuilder.addTreatment(ttBuilder.build());
+
+ TrafficSelector.Builder metabuilder = DefaultTrafficSelector.builder();
+ VlanId matchVlanId = (vlanId.equals(VlanId.NONE)) ?
+ VlanId.vlanId(SingleSwitchFibInstaller.ASSIGNED_VLAN) :
+ vlanId;
+ metabuilder.matchVlanId(matchVlanId);
+ nextObjBuilder.withMeta(metabuilder.build());
+
+ flowObjectiveService.next(deviceId, nextObjBuilder.add());
+ return nextId;
+ }
+
+ @Override
+ public void event(HostEvent event) {
+ DeviceId deviceId = event.subject().location().deviceId();
+ if (!mastershipService.isLocalMaster(deviceId)) {
+ return;
+ }
+ switch (event.type()) {
+ case HOST_ADDED:
+ peerAdded(event);
+ break;
+ case HOST_MOVED:
+ //TODO We assume BGP peer does not move for now
+ break;
+ case HOST_REMOVED:
+ peerRemoved(event);
+ break;
+ case HOST_UPDATED:
+ //TODO We assume BGP peer does not change IP for now
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ private int getPriorityFromPrefix(IpPrefix prefix) {
+ return (prefix.isIp4()) ?
+ 2000 * prefix.prefixLength() + MIN_IP_PRIORITY :
+ 500 * prefix.prefixLength() + MIN_IP_PRIORITY;
+ }
}
diff --git a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
index 271ffa0..f77804f 100644
--- a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
+++ b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
@@ -558,12 +558,16 @@
*/
public void populateSubnetBroadcastRule(DeviceId deviceId) {
config.getSubnets(deviceId).forEach(subnet -> {
+ if (subnet.prefixLength() == 0 ||
+ subnet.prefixLength() == IpPrefix.MAX_INET_MASK_LENGTH) {
+ return;
+ }
int nextId = srManager.getSubnetNextObjectiveId(deviceId, subnet);
VlanId vlanId = srManager.getSubnetAssignedVlanId(deviceId, subnet);
if (nextId < 0 || vlanId == null) {
- log.error("Cannot install subnet broadcast rule in dev:{} due"
- + "to vlanId:{} or nextId:{}", vlanId, nextId);
+ log.error("Cannot install subnet {} broadcast rule in dev:{} due"
+ + "to vlanId:{} or nextId:{}", subnet, deviceId, vlanId, nextId);
return;
}
diff --git a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/DeviceConfiguration.java b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/DeviceConfiguration.java
index bf8c4d7..932b8ad 100644
--- a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/DeviceConfiguration.java
+++ b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/DeviceConfiguration.java
@@ -278,6 +278,10 @@
PortNumber port = entry.getKey();
Ip4Prefix subnet = entry.getValue();
+ if (subnet.prefixLength() == IpPrefix.MAX_INET_MASK_LENGTH) {
+ return;
+ }
+
if (subnetPortMap.containsKey(subnet)) {
subnetPortMap.get(subnet).add(port);
} else {
diff --git a/drivers/default/src/main/java/org/onosproject/driver/pipeline/OFDPA2GroupHandler.java b/drivers/default/src/main/java/org/onosproject/driver/pipeline/OFDPA2GroupHandler.java
index 883182b..3c7394a 100644
--- a/drivers/default/src/main/java/org/onosproject/driver/pipeline/OFDPA2GroupHandler.java
+++ b/drivers/default/src/main/java/org/onosproject/driver/pipeline/OFDPA2GroupHandler.java
@@ -1,6 +1,5 @@
package org.onosproject.driver.pipeline;
-import com.google.common.base.Objects;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.RemovalCause;
@@ -46,6 +45,7 @@
import java.util.Deque;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
@@ -333,6 +333,7 @@
VlanId vlanid = null;
long portNum = 0;
boolean setVlan = false, popVlan = false;
+ MacAddress srcMac = MacAddress.ZERO;
MacAddress dstMac = MacAddress.ZERO;
for (Instruction ins : treatment.allInstructions()) {
if (ins.type() == Instruction.Type.L2MODIFICATION) {
@@ -343,7 +344,8 @@
outerTtb.setEthDst(dstMac);
break;
case ETH_SRC:
- outerTtb.setEthSrc(((L2ModificationInstruction.ModEtherInstruction) l2ins).mac());
+ srcMac = ((L2ModificationInstruction.ModEtherInstruction) l2ins).mac();
+ outerTtb.setEthSrc(srcMac);
break;
case VLAN_ID:
vlanid = ((L2ModificationInstruction.ModVlanIdInstruction) l2ins).vlanId();
@@ -433,11 +435,10 @@
mplsgroupkey, nextId);
} else {
// outer group is L3Unicast
- int l3groupId = L3_UNICAST_TYPE |
- (TYPE_MASK & (int) (dstMac.toLong() & 0xffff) << 6 | (int) portNum);
- int l3gk = L3_UNICAST_TYPE |
- (TYPE_MASK & (deviceId.hashCode() << 22 |
- (int) (dstMac.toLong() & 0xffff) << 6 | (int) portNum));
+ int l3GroupIdHash = Objects.hash(srcMac, dstMac, portNum);
+ int l3groupId = L3_UNICAST_TYPE | (TYPE_MASK & l3GroupIdHash);
+ int l3GroupKeyHash = Objects.hash(deviceId, srcMac, dstMac, portNum);
+ int l3gk = L3_UNICAST_TYPE | (TYPE_MASK & l3GroupKeyHash);
final GroupKey l3groupkey = new DefaultGroupKey(OFDPA2Pipeline.appKryo.serialize(l3gk));
outerTtb.group(new DefaultGroupId(l2groupId));
// create the l3unicast group description to wait for the
@@ -1059,7 +1060,7 @@
DeviceId deviceId, VlanId vlanId, long portNumber) {
int portLowerBits = (int) portNumber & PORT_LOWER_BITS_MASK;
long portHigherBits = portNumber & PORT_HIGHER_BITS_MASK;
- int hash = Objects.hashCode(deviceId, vlanId, portHigherBits);
+ int hash = Objects.hash(deviceId, vlanId, portHigherBits);
return L2_INTERFACE_TYPE | (TYPE_MASK & hash << 6) | portLowerBits;
}