Charles Chan
Committed by Gerrit Code Review

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
......@@ -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.DefaultNextObjective;
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 @@ public class ControlPlaneRedirectManager {
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 @@ public class ControlPlaneRedirectManager {
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 @@ public class ControlPlaneRedirectManager {
@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 @@ public class ControlPlaneRedirectManager {
deviceService.addListener(deviceListener);
networkConfigService.addListener(networkConfigListener);
hostService.addListener(hostListener);
updateConfig();
}
......@@ -107,6 +131,7 @@ public class ControlPlaneRedirectManager {
public void deactivate() {
deviceService.removeListener(deviceListener);
networkConfigService.removeListener(networkConfigListener);
hostService.removeListener(hostListener);
}
private void updateConfig() {
......@@ -309,7 +334,7 @@ public class ControlPlaneRedirectManager {
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 @@ public class ControlPlaneRedirectManager {
}
}
}
/**
* 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;
}
}
......
......@@ -558,12 +558,16 @@ public class RoutingRulePopulator {
*/
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;
}
......
......@@ -278,6 +278,10 @@ public class DeviceConfiguration implements DeviceProperties {
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 {
......
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.Collections;
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 @@ public class OFDPA2GroupHandler {
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 @@ public class OFDPA2GroupHandler {
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 @@ public class OFDPA2GroupHandler {
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 @@ public class OFDPA2GroupHandler {
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;
}
......