Charles Chan
Committed by Gerrit Code Review

[CORD-46] Implement L2 switching in Segment Routing

DONE
- Update SpringOpenTTP to support bridging table emulation
- Populate low priority subnet broadcast entry for bridging table
- Move IP entry population to host event handler as well
- Update ArpHandler to handle intra-rack ARP forwarding/flooding
- Move TTL_OUT action from IP table to corresponding group
    Since hardware does not support TTL_OUT in the IP table
- Populate entries to bridging table (MAC learning)
- Emulate src mac table

Not in this submission
- Emulate src-mac table behavior
- Pop vlan in the group instead of the flow

Change-Id: Ib69357c1889ccddaa4daa272d9f5843790ee1a3c
......@@ -20,10 +20,10 @@ import org.onlab.packet.Ethernet;
import org.onlab.packet.Ip4Address;
import org.onlab.packet.IpAddress;
import org.onlab.packet.MacAddress;
import org.onlab.packet.VlanId;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.DeviceId;
import org.onosproject.net.Host;
import org.onosproject.net.Port;
import org.onosproject.net.PortNumber;
import org.onosproject.net.flow.DefaultTrafficTreatment;
import org.onosproject.net.flow.TrafficTreatment;
......@@ -60,12 +60,21 @@ public class ArpHandler {
/**
* Processes incoming ARP packets.
*
* If it is an ARP request to router itself or known hosts,
* then it sends ARP response.
* If it is an ARP request to unknown hosts in its own subnet,
* then it flood the ARP request to the ports.
* If it is an ARP response, then set a flow rule for the host
* and forward any IP packets to the host in the packet buffer to the host.
* <p>
* Note: We handles all ARP packet in, even for those ARP packets between
* hosts in the same subnet.
* For an ARP packet with broadcast destination MAC,
* some switches pipelines will send it to the controller due to table miss,
* other swithches will flood the packets directly in the data plane without
* packet in.
* We can deal with both cases.
*
* @param pkt incoming packet
*/
......@@ -86,29 +95,56 @@ public class ArpHandler {
if (arp.getOpCode() == ARP.OP_REQUEST) {
handleArpRequest(deviceId, connectPoint, ethernet);
} else {
srManager.ipHandler.forwardPackets(deviceId, hostIpAddress);
handleArpReply(deviceId, connectPoint, ethernet);
}
}
private void handleArpRequest(DeviceId deviceId, ConnectPoint inPort, Ethernet payload) {
ARP arpRequest = (ARP) payload.getPayload();
VlanId vlanId = VlanId.vlanId(payload.getVlanID());
HostId targetHostId = HostId.hostId(MacAddress.valueOf(
arpRequest.getTargetHardwareAddress()));
arpRequest.getTargetHardwareAddress()),
vlanId);
// ARP request for router
// ARP request for router. Send ARP reply.
if (isArpReqForRouter(deviceId, arpRequest)) {
Ip4Address targetAddress = Ip4Address.valueOf(arpRequest.getTargetProtocolAddress());
sendArpResponse(arpRequest, config.getRouterMacForAGatewayIp(targetAddress));
sendArpResponse(arpRequest, config.getRouterMacForAGatewayIp(targetAddress), vlanId);
} else {
Host targetHost = srManager.hostService.getHost(targetHostId);
// ARP request for known hosts
// ARP request for known hosts. Send proxy ARP reply on behalf of the target.
if (targetHost != null) {
sendArpResponse(arpRequest, targetHost.mac());
removeVlanAndForward(payload, targetHost.location());
// ARP request for unknown host in the subnet. Flood in the subnet.
} else {
removeVlanAndFlood(payload, inPort);
}
}
}
// ARP request for unknown host in the subnet
} else if (isArpReqForSubnet(deviceId, arpRequest)) {
flood(payload, inPort);
private void handleArpReply(DeviceId deviceId, ConnectPoint inPort, Ethernet payload) {
ARP arpReply = (ARP) payload.getPayload();
VlanId vlanId = VlanId.vlanId(payload.getVlanID());
HostId targetHostId = HostId.hostId(MacAddress.valueOf(
arpReply.getTargetHardwareAddress()),
vlanId);
// ARP reply for router. Process all pending IP packets.
if (isArpReqForRouter(deviceId, arpReply)) {
Ip4Address hostIpAddress = Ip4Address.valueOf(arpReply.getSenderProtocolAddress());
srManager.ipHandler.forwardPackets(deviceId, hostIpAddress);
} else {
Host targetHost = srManager.hostService.getHost(targetHostId);
// ARP reply for known hosts. Forward to the host.
if (targetHost != null) {
removeVlanAndForward(payload, targetHost.location());
// ARP reply for unknown host, Flood in the subnet.
} else {
// Don't flood to non-edge ports
if (vlanId.equals(VlanId.vlanId(srManager.ASSIGNED_VLAN_NO_SUBNET))) {
return;
}
removeVlanAndFlood(payload, inPort);
}
}
}
......@@ -126,14 +162,6 @@ public class ArpHandler {
return false;
}
private boolean isArpReqForSubnet(DeviceId deviceId, ARP arpRequest) {
return config.getSubnets(deviceId).stream()
.anyMatch((prefix)->
prefix.contains(Ip4Address.
valueOf(arpRequest.
getTargetProtocolAddress())));
}
/**
* Sends an APR request for the target IP address to all ports except in-port.
*
......@@ -170,11 +198,10 @@ public class ArpHandler {
.setSourceMACAddress(senderMacAddress)
.setEtherType(Ethernet.TYPE_ARP).setPayload(arpRequest);
flood(eth, inPort);
removeVlanAndFlood(eth, inPort);
}
private void sendArpResponse(ARP arpRequest, MacAddress targetMac) {
private void sendArpResponse(ARP arpRequest, MacAddress targetMac, VlanId vlanId) {
ARP arpReply = new ARP();
arpReply.setHardwareType(ARP.HW_TYPE_ETHERNET)
.setProtocolType(ARP.PROTO_TYPE_IP)
......@@ -193,8 +220,9 @@ public class ArpHandler {
.setEtherType(Ethernet.TYPE_ARP).setPayload(arpReply);
HostId dstId = HostId.hostId(MacAddress.valueOf(
arpReply.getTargetHardwareAddress()));
HostId dstId = HostId.hostId(
MacAddress.valueOf(arpReply.getTargetHardwareAddress()),
vlanId);
Host dst = srManager.hostService.getHost(dstId);
if (dst == null) {
log.warn("Cannot send ARP response to unknown device");
......@@ -209,19 +237,51 @@ public class ArpHandler {
srManager.packetService.emit(packet);
}
private void flood(Ethernet request, ConnectPoint inPort) {
TrafficTreatment.Builder builder;
ByteBuffer buf = ByteBuffer.wrap(request.serialize());
for (Port port: srManager.deviceService.getPorts(inPort.deviceId())) {
if (!port.number().equals(inPort.port()) &&
port.number().toLong() > 0) {
builder = DefaultTrafficTreatment.builder();
builder.setOutput(port.number());
srManager.packetService.emit(new DefaultOutboundPacket(inPort.deviceId(),
builder.build(), buf));
}
/**
* Remove VLAN tag and flood to all ports in the same subnet.
*
* @param packet packet to be flooded
* @param inPort where the packet comes from
*/
private void removeVlanAndFlood(Ethernet packet, ConnectPoint inPort) {
Ip4Address targetProtocolAddress = Ip4Address.valueOf(
((ARP) packet.getPayload()).getTargetProtocolAddress()
);
srManager.deviceConfiguration.getSubnetPortsMap(inPort.deviceId()).forEach((subnet, ports) -> {
if (subnet.contains(targetProtocolAddress)) {
ports.stream()
.filter(port -> port != inPort.port())
.forEach(port -> {
removeVlanAndForward(packet, new ConnectPoint(inPort.deviceId(), port));
});
}
});
}
/**
* Remove VLAN tag and packet out to given port.
*
* Note: In current implementation, we expect all communication with
* end hosts within a subnet to be untagged.
* <p>
* For those pipelines that internally assigns a VLAN, the VLAN tag will be
* removed before egress.
* <p>
* For those pipelines that do not assign internal VLAN, the packet remains
* untagged.
*
* @param packet packet to be forwarded
* @param outPort where the packet should be forwarded
*/
private void removeVlanAndForward(Ethernet packet, ConnectPoint outPort) {
packet.setEtherType(Ethernet.TYPE_ARP);
packet.setVlanID(Ethernet.VLAN_UNTAGGED);
ByteBuffer buf = ByteBuffer.wrap(packet.serialize());
TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
tbuilder.setOutput(outPort.port());
srManager.packetService.emit(new DefaultOutboundPacket(outPort.deviceId(),
tbuilder.build(), buf));
}
}
......
......@@ -105,8 +105,17 @@ public class IcmpHandler {
}
}
/**
* Sends an ICMP reply message.
*
* Note: we assume that packets sending from the edge switches to the hosts
* have untagged VLAN.
* @param icmpRequest the original ICMP request
* @param outport the output port where the ICMP reply should be sent to
*/
private void sendICMPResponse(Ethernet icmpRequest, ConnectPoint outport) {
// Note: We assume that packets arrive at the edge switches have
// untagged VLAN.
Ethernet icmpReplyEth = new Ethernet();
IPv4 icmpRequestIpv4 = (IPv4) icmpRequest.getPayload();
......@@ -129,7 +138,6 @@ public class IcmpHandler {
icmpReplyEth.setEtherType(Ethernet.TYPE_IPV4);
icmpReplyEth.setDestinationMACAddress(icmpRequest.getSourceMACAddress());
icmpReplyEth.setSourceMACAddress(icmpRequest.getDestinationMACAddress());
icmpReplyEth.setVlanID(icmpRequest.getVlanID());
Ip4Address destIpAddress = Ip4Address.valueOf(icmpReplyIpv4.getDestinationAddress());
Ip4Address destRouterAddress = config.getRouterIpAddressForASubnetHost(destIpAddress);
......
......@@ -55,7 +55,6 @@ import java.util.concurrent.atomic.AtomicLong;
import static com.google.common.base.Preconditions.checkNotNull;
public class RoutingRulePopulator {
private static final Logger log = LoggerFactory
.getLogger(RoutingRulePopulator.class);
......@@ -105,13 +104,45 @@ public class RoutingRulePopulator {
*/
public void populateIpRuleForHost(DeviceId deviceId, Ip4Address hostIp,
MacAddress hostMac, PortNumber outPort) {
MacAddress deviceMac;
log.debug("Populate IP table entry for host {} at {}:{}",
hostIp, deviceId, outPort);
ForwardingObjective.Builder fwdBuilder;
try {
deviceMac = config.getDeviceMac(deviceId);
fwdBuilder = getForwardingObjectiveBuilder(
deviceId, hostIp, hostMac, outPort);
} catch (DeviceConfigNotFoundException e) {
log.warn(e.getMessage() + " Aborting populateIpRuleForHost.");
return;
}
srManager.flowObjectiveService.
forward(deviceId, fwdBuilder.add(new SRObjectiveContext(deviceId,
SRObjectiveContext.ObjectiveType.FORWARDING)));
rulePopulationCounter.incrementAndGet();
}
public void revokeIpRuleForHost(DeviceId deviceId, Ip4Address hostIp,
MacAddress hostMac, PortNumber outPort) {
log.debug("Revoke IP table entry for host {} at {}:{}",
hostIp, deviceId, outPort);
ForwardingObjective.Builder fwdBuilder;
try {
fwdBuilder = getForwardingObjectiveBuilder(
deviceId, hostIp, hostMac, outPort);
} catch (DeviceConfigNotFoundException e) {
log.warn(e.getMessage() + " Aborting revokeIpRuleForHost.");
return;
}
srManager.flowObjectiveService.
forward(deviceId, fwdBuilder.remove(new SRObjectiveContext(deviceId,
SRObjectiveContext.ObjectiveType.FORWARDING)));
}
private ForwardingObjective.Builder getForwardingObjectiveBuilder(
DeviceId deviceId, Ip4Address hostIp,
MacAddress hostMac, PortNumber outPort)
throws DeviceConfigNotFoundException {
MacAddress deviceMac;
deviceMac = config.getDeviceMac(deviceId);
TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
......@@ -127,19 +158,10 @@ public class RoutingRulePopulator {
TrafficTreatment treatment = tbuilder.build();
TrafficSelector selector = sbuilder.build();
ForwardingObjective.Builder fwdBuilder = DefaultForwardingObjective
.builder().fromApp(srManager.appId).makePermanent()
return DefaultForwardingObjective.builder()
.fromApp(srManager.appId).makePermanent()
.withSelector(selector).withTreatment(treatment)
.withPriority(100).withFlag(ForwardingObjective.Flag.SPECIFIC);
log.debug("Installing IPv4 forwarding objective "
+ "for host {} in switch {}", hostIp, deviceId);
srManager.flowObjectiveService.
forward(deviceId,
fwdBuilder.
add(new SRObjectiveContext(deviceId,
SRObjectiveContext.ObjectiveType.FORWARDING)));
rulePopulationCounter.incrementAndGet();
}
/**
......@@ -186,26 +208,25 @@ public class RoutingRulePopulator {
}
TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
sbuilder.matchIPDst(ipPrefix);
sbuilder.matchEthType(Ethernet.TYPE_IPV4);
TrafficSelector selector = sbuilder.build();
NeighborSet ns = null;
TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
NeighborSet ns;
TrafficTreatment treatment;
// If the next hop is the same as the final destination, then MPLS label
// is not set.
if (nextHops.size() == 1 && nextHops.toArray()[0].equals(destSw)) {
tbuilder.deferred().decNwTtl();
tbuilder.immediate().decNwTtl();
ns = new NeighborSet(nextHops);
treatment = tbuilder.build();
} else {
tbuilder.deferred().copyTtlOut();
ns = new NeighborSet(nextHops, segmentId);
treatment = null;
}
TrafficTreatment treatment = tbuilder.build();
TrafficSelector selector = sbuilder.build();
if (srManager.getNextObjectiveId(deviceId, ns) <= 0) {
log.warn("No next objective in {} for ns: {}", deviceId, ns);
return false;
......@@ -216,10 +237,12 @@ public class RoutingRulePopulator {
.fromApp(srManager.appId)
.makePermanent()
.nextStep(srManager.getNextObjectiveId(deviceId, ns))
.withTreatment(treatment)
.withSelector(selector)
.withPriority(100)
.withFlag(ForwardingObjective.Flag.SPECIFIC);
if (treatment != null) {
fwdBuilder.withTreatment(treatment);
}
log.debug("Installing IPv4 forwarding objective "
+ "for router IP/subnet {} in switch {}",
ipPrefix,
......@@ -423,8 +446,6 @@ public class RoutingRulePopulator {
? VlanId.vlanId(SegmentRoutingManager.ASSIGNED_VLAN_NO_SUBNET)
: srManager.getSubnetAssignedVlanId(deviceId, portSubnet);
FilteringObjective.Builder fob = DefaultFilteringObjective.builder();
fob.withKey(Criteria.matchInPort(port.number()))
.addCondition(Criteria.matchEthDst(deviceMac))
......@@ -469,14 +490,14 @@ public class RoutingRulePopulator {
Set<Ip4Address> allIps = new HashSet<Ip4Address>(config.getPortIPs(deviceId));
allIps.add(routerIp);
for (Ip4Address ipaddr : allIps) {
TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
selector.matchEthType(Ethernet.TYPE_IPV4);
selector.matchIPDst(IpPrefix.valueOf(ipaddr,
TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
sbuilder.matchEthType(Ethernet.TYPE_IPV4);
sbuilder.matchIPDst(IpPrefix.valueOf(ipaddr,
IpPrefix.MAX_INET_MASK_LENGTH));
treatment.setOutput(PortNumber.CONTROLLER);
puntIp.withSelector(selector.build());
puntIp.withTreatment(treatment.build());
tbuilder.setOutput(PortNumber.CONTROLLER);
puntIp.withSelector(sbuilder.build());
puntIp.withTreatment(tbuilder.build());
puntIp.withFlag(Flag.VERSATILE)
.withPriority(HIGHEST_PRIORITY)
.makePermanent()
......@@ -489,6 +510,48 @@ public class RoutingRulePopulator {
}
}
/**
* Populates a forwarding objective to send packets that miss other high
* priority Bridging Table entries to a group that contains all ports of
* its subnet.
*
* Note: We assume that packets sending from the edge switches to the hosts
* have untagged VLAN.
* The VLAN tag will be popped later in the flooding group.
*
* @param deviceId switch ID to set the rules
*/
public void populateSubnetBroadcastRule(DeviceId deviceId) {
config.getSubnets(deviceId).forEach(subnet -> {
int nextId = srManager.getSubnetNextObjectiveId(deviceId, subnet);
VlanId vlanId = srManager.getSubnetAssignedVlanId(deviceId, subnet);
/* 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(5)
.fromApp(srManager.appId)
.makePermanent();
srManager.flowObjectiveService.forward(
deviceId,
fob.add(new SRObjectiveContext(
deviceId,
SRObjectiveContext.ObjectiveType.FORWARDING)
)
);
});
}
private PortNumber selectOnePort(DeviceId srcId, Set<DeviceId> destIds) {
Set<Link> links = srManager.linkService.getDeviceLinks(srcId);
......
......@@ -22,6 +22,7 @@ import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.apache.felix.scr.annotations.Service;
import org.onlab.packet.Ethernet;
import org.onlab.packet.MacAddress;
import org.onlab.packet.VlanId;
import org.onlab.packet.IPv4;
import org.onlab.packet.Ip4Address;
......@@ -33,11 +34,23 @@ import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
import org.onosproject.event.Event;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.PortNumber;
import org.onosproject.net.config.ConfigFactory;
import org.onosproject.net.config.NetworkConfigEvent;
import org.onosproject.net.config.NetworkConfigRegistry;
import org.onosproject.net.config.NetworkConfigListener;
import org.onosproject.net.config.basics.SubjectFactories;
import org.onosproject.net.flow.DefaultTrafficSelector;
import org.onosproject.net.flow.DefaultTrafficTreatment;
import org.onosproject.net.flow.TrafficSelector;
import org.onosproject.net.flow.TrafficTreatment;
import org.onosproject.net.flowobjective.DefaultForwardingObjective;
import org.onosproject.net.flowobjective.ForwardingObjective;
import org.onosproject.net.flowobjective.Objective;
import org.onosproject.net.flowobjective.ObjectiveContext;
import org.onosproject.net.flowobjective.ObjectiveError;
import org.onosproject.net.host.HostEvent;
import org.onosproject.net.host.HostListener;
import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
import org.onosproject.segmentrouting.config.DeviceConfiguration;
import org.onosproject.segmentrouting.config.SegmentRoutingConfig;
......@@ -139,11 +152,13 @@ public class SegmentRoutingManager implements SegmentRoutingService {
private static ScheduledFuture<?> eventHandlerFuture = null;
private ConcurrentLinkedQueue<Event> eventQueue = new ConcurrentLinkedQueue<Event>();
private Map<DeviceId, DefaultGroupHandler> groupHandlerMap = new ConcurrentHashMap<DeviceId, DefaultGroupHandler>();
private Map<DeviceId, DefaultGroupHandler> groupHandlerMap =
new ConcurrentHashMap<DeviceId, DefaultGroupHandler>();
// Per device next objective ID store with (device id + neighbor set) as key
private EventuallyConsistentMap<NeighborSetNextObjectiveStoreKey,
Integer> nsNextObjStore = null;
private EventuallyConsistentMap<SubnetNextObjectiveStoreKey, Integer> subnetNextObjStore = null;
private EventuallyConsistentMap<NeighborSetNextObjectiveStoreKey, Integer>
nsNextObjStore = null;
private EventuallyConsistentMap<SubnetNextObjectiveStoreKey, Integer>
subnetNextObjStore = null;
private EventuallyConsistentMap<String, Tunnel> tunnelStore = null;
private EventuallyConsistentMap<String, Policy> policyStore = null;
// Per device, per-subnet assigned-vlans store, with (device id + subnet
......@@ -170,6 +185,8 @@ public class SegmentRoutingManager implements SegmentRoutingService {
}
};
private final HostListener hostListener = new InternalHostListener();
private Object threadSchedulerLock = new Object();
private static int numOfEventsQueued = 0;
private static int numOfEventsExecuted = 0;
......@@ -259,6 +276,8 @@ public class SegmentRoutingManager implements SegmentRoutingService {
cfgService.addListener(cfgListener);
cfgService.registerConfigFactory(cfgFactory);
hostService.addListener(hostListener);
processor = new InternalPacketProcessor();
linkListener = new InternalLinkListener();
deviceListener = new InternalDeviceListener();
......@@ -637,6 +656,7 @@ public class SegmentRoutingManager implements SegmentRoutingService {
if (mastershipService.isLocalMaster(device.id())) {
DefaultGroupHandler groupHandler = groupHandlerMap.get(device.id());
groupHandler.createGroupsFromSubnetConfig();
routingRulePopulator.populateSubnetBroadcastRule(device.id());
}
}
......@@ -703,6 +723,7 @@ public class SegmentRoutingManager implements SegmentRoutingService {
if (mastershipService.isLocalMaster(device.id())) {
DefaultGroupHandler groupHandler = groupHandlerMap.get(device.id());
groupHandler.createGroupsFromSubnetConfig();
routingRulePopulator.populateSubnetBroadcastRule(device.id());
}
}
......@@ -723,4 +744,205 @@ public class SegmentRoutingManager implements SegmentRoutingService {
}
}
}
private class InternalHostListener implements HostListener {
private ForwardingObjective.Builder getForwardingObjectiveBuilder(
MacAddress mac, VlanId vlanId, PortNumber port) {
TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
sbuilder.matchEthDst(mac);
sbuilder.matchVlanId(vlanId);
TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
// TODO Move popVlan from flow action to group action
tbuilder.immediate().popVlan();
tbuilder.immediate().setOutput(port);
return DefaultForwardingObjective.builder()
.withFlag(ForwardingObjective.Flag.SPECIFIC)
.withSelector(sbuilder.build())
.withTreatment(tbuilder.build())
.withPriority(100)
.fromApp(appId)
.makePermanent();
}
private void processHostAddedEvent(HostEvent event) {
MacAddress mac = event.subject().mac();
VlanId vlanId = event.subject().vlan();
DeviceId deviceId = event.subject().location().deviceId();
PortNumber port = event.subject().location().port();
Set<IpAddress> ips = event.subject().ipAddresses();
log.debug("Host {}/{} is added at {}:{}", mac, vlanId, deviceId, port);
// TODO Move bridging table population to a separate class
// Populate bridging table entry
ForwardingObjective.Builder fob =
getForwardingObjectiveBuilder(mac, vlanId, port);
flowObjectiveService.forward(deviceId, fob.add(
new BridgingTableObjectiveContext(mac, vlanId)
));
// Populate IP table entry
ips.forEach(ip -> {
if (ip.isIp4()) {
routingRulePopulator.populateIpRuleForHost(
deviceId, ip.getIp4Address(), mac, port);
}
});
}
private void processHostRemoveEvent(HostEvent event) {
MacAddress mac = event.subject().mac();
VlanId vlanId = event.subject().vlan();
DeviceId deviceId = event.subject().location().deviceId();
PortNumber port = event.subject().location().port();
Set<IpAddress> ips = event.subject().ipAddresses();
log.debug("Host {}/{} is removed from {}:{}", mac, vlanId, deviceId, port);
// Revoke bridging table entry
ForwardingObjective.Builder fob =
getForwardingObjectiveBuilder(mac, vlanId, port);
flowObjectiveService.forward(deviceId, fob.remove(
new BridgingTableObjectiveContext(mac, vlanId)
));
// Revoke IP table entry
ips.forEach(ip -> {
if (ip.isIp4()) {
routingRulePopulator.revokeIpRuleForHost(
deviceId, ip.getIp4Address(), mac, port);
}
});
}
private void processHostMovedEvent(HostEvent event) {
MacAddress mac = event.subject().mac();
VlanId vlanId = event.subject().vlan();
DeviceId prevDeviceId = event.prevSubject().location().deviceId();
PortNumber prevPort = event.prevSubject().location().port();
Set<IpAddress> prevIps = event.prevSubject().ipAddresses();
DeviceId newDeviceId = event.subject().location().deviceId();
PortNumber newPort = event.subject().location().port();
Set<IpAddress> newIps = event.subject().ipAddresses();
log.debug("Host {}/{} is moved from {}:{} to {}:{}",
mac, vlanId, prevDeviceId, prevPort, newDeviceId, newPort);
// Revoke previous bridging table entry
ForwardingObjective.Builder prevFob =
getForwardingObjectiveBuilder(mac, vlanId, prevPort);
flowObjectiveService.forward(prevDeviceId, prevFob.remove(
new BridgingTableObjectiveContext(mac, vlanId)
));
// Revoke previous IP table entry
prevIps.forEach(ip -> {
if (ip.isIp4()) {
routingRulePopulator.revokeIpRuleForHost(
prevDeviceId, ip.getIp4Address(), mac, prevPort);
}
});
// Populate new bridging table entry
ForwardingObjective.Builder newFob =
getForwardingObjectiveBuilder(mac, vlanId, prevPort);
flowObjectiveService.forward(newDeviceId, newFob.add(
new BridgingTableObjectiveContext(mac, vlanId)
));
// Populate new IP table entry
newIps.forEach(ip -> {
if (ip.isIp4()) {
routingRulePopulator.populateIpRuleForHost(
newDeviceId, ip.getIp4Address(), mac, newPort);
}
});
}
private void processHostUpdatedEvent(HostEvent event) {
MacAddress mac = event.subject().mac();
VlanId vlanId = event.subject().vlan();
DeviceId prevDeviceId = event.prevSubject().location().deviceId();
PortNumber prevPort = event.prevSubject().location().port();
Set<IpAddress> prevIps = event.prevSubject().ipAddresses();
DeviceId newDeviceId = event.subject().location().deviceId();
PortNumber newPort = event.subject().location().port();
Set<IpAddress> newIps = event.subject().ipAddresses();
log.debug("Host {}/{} is updated", mac, vlanId);
// Revoke previous IP table entry
prevIps.forEach(ip -> {
if (ip.isIp4()) {
routingRulePopulator.revokeIpRuleForHost(
prevDeviceId, ip.getIp4Address(), mac, prevPort);
}
});
// Populate new IP table entry
newIps.forEach(ip -> {
if (ip.isIp4()) {
routingRulePopulator.populateIpRuleForHost(
newDeviceId, ip.getIp4Address(), mac, newPort);
}
});
}
@Override
public void event(HostEvent event) {
// Do not proceed without mastership
DeviceId deviceId = event.subject().location().deviceId();
if (!mastershipService.isLocalMaster(deviceId)) {
return;
}
switch (event.type()) {
case HOST_ADDED:
processHostAddedEvent(event);
break;
case HOST_MOVED:
processHostMovedEvent(event);
break;
case HOST_REMOVED:
processHostRemoveEvent(event);
break;
case HOST_UPDATED:
processHostUpdatedEvent(event);
break;
default:
log.warn("Unsupported host event type: {}", event.type());
break;
}
}
}
private static class BridgingTableObjectiveContext implements ObjectiveContext {
final MacAddress mac;
final VlanId vlanId;
BridgingTableObjectiveContext(MacAddress mac, VlanId vlanId) {
this.mac = mac;
this.vlanId = vlanId;
}
@Override
public void onSuccess(Objective objective) {
if (objective.op() == Objective.Operation.ADD) {
log.debug("Successfully populate bridging table entry for {}/{}",
mac, vlanId);
} else {
log.debug("Successfully revoke bridging table entry for {}/{}",
mac, vlanId);
}
}
@Override
public void onError(Objective objective, ObjectiveError error) {
if (objective.op() == Objective.Operation.ADD) {
log.debug("Fail to populate bridging table entry for {}/{}. {}",
mac, vlanId, error);
} else {
log.debug("Fail to revoke bridging table entry for {}/{}. {}",
mac, vlanId, error);
}
}
}
}
......
......@@ -224,8 +224,8 @@ public class DefaultGroupHandler {
.setEthSrc(nodeMacAddr);
if (ns.getEdgeLabel() != NeighborSet.NO_EDGE_LABEL) {
tBuilder.pushMpls()
.setMpls(MplsLabel.
mplsLabel(ns.getEdgeLabel()));
.copyTtlOut()
.setMpls(MplsLabel.mplsLabel(ns.getEdgeLabel()));
}
Integer nextId = nsNextObjStore.
......@@ -292,8 +292,9 @@ public class DefaultGroupHandler {
.setEthDst(dstMac)
.setEthSrc(nodeMacAddr);
if (ns.getEdgeLabel() != NeighborSet.NO_EDGE_LABEL) {
tBuilder.pushMpls().setMpls(MplsLabel.mplsLabel(ns
.getEdgeLabel()));
tBuilder.pushMpls()
.copyTtlOut()
.setMpls(MplsLabel.mplsLabel(ns.getEdgeLabel()));
}
Integer nextId = nsNextObjStore.
......@@ -536,8 +537,9 @@ public class DefaultGroupHandler {
.setEthDst(deviceMac)
.setEthSrc(nodeMacAddr);
if (ns.getEdgeLabel() != NeighborSet.NO_EDGE_LABEL) {
tBuilder.pushMpls().setMpls(MplsLabel.mplsLabel(ns
.getEdgeLabel()));
tBuilder.pushMpls()
.copyTtlOut()
.setMpls(MplsLabel.mplsLabel(ns.getEdgeLabel()));
}
nextObjBuilder.addTreatment(tBuilder.build());
}
......
......@@ -25,6 +25,7 @@ import com.google.common.cache.RemovalNotification;
import org.onlab.osgi.ServiceDirectory;
import org.onlab.packet.Ethernet;
import org.onlab.packet.MacAddress;
import org.onlab.packet.VlanId;
import org.onlab.util.KryoNamespace;
import org.onosproject.core.ApplicationId;
......@@ -54,6 +55,7 @@ import org.onosproject.net.flow.criteria.PortCriterion;
import org.onosproject.net.flow.criteria.VlanIdCriterion;
import org.onosproject.net.flow.instructions.Instruction;
import org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModVlanIdInstruction;
import org.onosproject.net.flowobjective.FilteringObjective;
import org.onosproject.net.flowobjective.FlowObjectiveStore;
import org.onosproject.net.flowobjective.ForwardingObjective;
......@@ -94,7 +96,9 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour
private static final int TABLE_TMAC = 1;
private static final int TABLE_IPV4_UNICAST = 2;
private static final int TABLE_MPLS = 3;
private static final int TABLE_DMAC = 4;
private static final int TABLE_ACL = 5;
private static final int TABLE_SMAC = 6;
/**
* Set the default values. These variables will get overwritten based on the
......@@ -104,7 +108,9 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour
protected int tmacTableId = TABLE_TMAC;
protected int ipv4UnicastTableId = TABLE_IPV4_UNICAST;
protected int mplsTableId = TABLE_MPLS;
protected int dstMacTableId = TABLE_DMAC;
protected int aclTableId = TABLE_ACL;
protected int srcMacTableId = TABLE_SMAC;
protected final Logger log = getLogger(getClass());
......@@ -448,12 +454,14 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour
fwd.treatment().allInstructions().get(0).type() == Instruction.Type.OUTPUT) {
OutputInstruction o = (OutputInstruction) fwd.treatment().allInstructions().get(0);
if (o.port() == PortNumber.CONTROLLER) {
log.warn("Punts to the controller are handled by misses in"
+ " the TMAC, IP and MPLS tables.");
return Collections.emptySet();
}
treatmentBuilder.punt();
treatment = treatmentBuilder.build();
} else {
treatment = fwd.treatment();
}
} else {
treatment = fwd.treatment();
}
} else {
log.warn("VERSATILE forwarding objective needs next objective ID "
+ "or treatment.");
......@@ -475,19 +483,52 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour
return Collections.singletonList(ruleBuilder.build());
}
protected Collection<FlowRule> processSpecific(ForwardingObjective fwd) {
log.debug("Processing specific");
private boolean isSupportedEthTypeObjective(ForwardingObjective fwd) {
TrafficSelector selector = fwd.selector();
EthTypeCriterion ethType = (EthTypeCriterion) selector
.getCriterion(Criterion.Type.ETH_TYPE);
if ((ethType == null) ||
(ethType.ethType().toShort() != Ethernet.TYPE_IPV4) &&
(ethType.ethType().toShort() != Ethernet.MPLS_UNICAST)) {
((ethType.ethType().toShort() != Ethernet.TYPE_IPV4) &&
(ethType.ethType().toShort() != Ethernet.MPLS_UNICAST))) {
return false;
}
return true;
}
private boolean isSupportedEthDstObjective(ForwardingObjective fwd) {
TrafficSelector selector = fwd.selector();
EthCriterion ethDst = (EthCriterion) selector
.getCriterion(Criterion.Type.ETH_DST);
VlanIdCriterion vlanId = (VlanIdCriterion) selector
.getCriterion(Criterion.Type.VLAN_VID);
if (ethDst == null && vlanId == null) {
return false;
}
return true;
}
protected Collection<FlowRule> processSpecific(ForwardingObjective fwd) {
log.debug("Processing specific");
boolean isEthTypeObj = isSupportedEthTypeObjective(fwd);
boolean isEthDstObj = isSupportedEthDstObjective(fwd);
if (isEthTypeObj) {
return processEthTypeSpecificObjective(fwd);
} else if (isEthDstObj) {
return processEthDstSpecificObjective(fwd);
} else {
log.warn("processSpecific: Unsupported "
+ "forwarding objective criteraia");
fail(fwd, ObjectiveError.UNSUPPORTED);
return Collections.emptySet();
}
}
protected Collection<FlowRule>
processEthTypeSpecificObjective(ForwardingObjective fwd) {
TrafficSelector selector = fwd.selector();
EthTypeCriterion ethType = (EthTypeCriterion) selector
.getCriterion(Criterion.Type.ETH_TYPE);
TrafficSelector.Builder filteredSelectorBuilder =
DefaultTrafficSelector.builder();
......@@ -565,59 +606,167 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour
}
protected List<FlowRule> processEthDstFilter(Criterion c,
protected Collection<FlowRule>
processEthDstSpecificObjective(ForwardingObjective fwd) {
List<FlowRule> rules = new ArrayList<>();
// Build filtered selector
TrafficSelector selector = fwd.selector();
EthCriterion ethCriterion = (EthCriterion) selector
.getCriterion(Criterion.Type.ETH_DST);
VlanIdCriterion vlanIdCriterion = (VlanIdCriterion) selector
.getCriterion(Criterion.Type.VLAN_VID);
TrafficSelector.Builder filteredSelectorBuilder =
DefaultTrafficSelector.builder();
// Do not match MacAddress for subnet broadcast entry
if (!ethCriterion.mac().equals(MacAddress.NONE)) {
filteredSelectorBuilder.matchEthDst(ethCriterion.mac());
}
filteredSelectorBuilder.matchVlanId(vlanIdCriterion.vlanId());
TrafficSelector filteredSelector = filteredSelectorBuilder.build();
// Build filtered treatment
TrafficTreatment.Builder treatmentBuilder =
DefaultTrafficTreatment.builder();
if (fwd.treatment() != null) {
treatmentBuilder.deferred();
fwd.treatment().allInstructions().forEach(treatmentBuilder::add);
}
if (fwd.nextId() != null) {
NextGroup next = flowObjectiveStore.getNextGroup(fwd.nextId());
if (next != null) {
GroupKey key = appKryo.deserialize(next.data());
Group group = groupService.getGroup(deviceId, key);
if (group != null) {
treatmentBuilder.deferred().group(group.id());
} else {
log.warn("Group Missing");
fail(fwd, ObjectiveError.GROUPMISSING);
return Collections.emptySet();
}
}
}
treatmentBuilder.immediate().transition(aclTableId);
TrafficTreatment filteredTreatment = treatmentBuilder.build();
// Build bridging table entries
FlowRule.Builder flowRuleBuilder = DefaultFlowRule.builder();
flowRuleBuilder.fromApp(fwd.appId())
.withPriority(fwd.priority())
.forDevice(deviceId)
.withSelector(filteredSelector)
.withTreatment(filteredTreatment)
.forTable(dstMacTableId);
if (fwd.permanent()) {
flowRuleBuilder.makePermanent();
} else {
flowRuleBuilder.makeTemporary(fwd.timeout());
}
rules.add(flowRuleBuilder.build());
/*
// TODO Emulate source MAC table behavior
// Do not install source MAC table entry for subnet broadcast
if (!ethCriterion.mac().equals(MacAddress.NONE)) {
// Build filtered selector
selector = fwd.selector();
ethCriterion = (EthCriterion) selector.getCriterion(Criterion.Type.ETH_DST);
filteredSelectorBuilder = DefaultTrafficSelector.builder();
filteredSelectorBuilder.matchEthSrc(ethCriterion.mac());
filteredSelector = filteredSelectorBuilder.build();
// Build empty treatment. Apply existing instruction if match.
treatmentBuilder = DefaultTrafficTreatment.builder();
filteredTreatment = treatmentBuilder.build();
// Build bridging table entries
flowRuleBuilder = DefaultFlowRule.builder();
flowRuleBuilder.fromApp(fwd.appId())
.withPriority(fwd.priority())
.forDevice(deviceId)
.withSelector(filteredSelector)
.withTreatment(filteredTreatment)
.forTable(srcMacTableId)
.makePermanent();
rules.add(flowRuleBuilder.build());
}
*/
return rules;
}
protected List<FlowRule> processEthDstFilter(EthCriterion ethCriterion,
VlanIdCriterion vlanIdCriterion,
FilteringObjective filt,
VlanId assignedVlan,
ApplicationId applicationId) {
//handling untagged packets via assigned VLAN
if (vlanIdCriterion.vlanId() == VlanId.NONE) {
vlanIdCriterion = (VlanIdCriterion) Criteria.matchVlanId(assignedVlan);
}
/*
* Note: CpqD switches do not handle MPLS-related operation properly
* for a packet with VLAN tag. We pop VLAN here as a workaround.
* Side effect: HostService learns redundant hosts with same MAC but
* different VLAN. No known side effect on the network reachability.
*/
List<FlowRule> rules = new ArrayList<>();
EthCriterion e = (EthCriterion) c;
TrafficSelector.Builder selectorIp = DefaultTrafficSelector
.builder();
TrafficTreatment.Builder treatmentIp = DefaultTrafficTreatment
.builder();
selectorIp.matchEthDst(e.mac());
selectorIp.matchEthDst(ethCriterion.mac());
selectorIp.matchEthType(Ethernet.TYPE_IPV4);
selectorIp.matchVlanId(vlanIdCriterion.vlanId());
treatmentIp.popVlan();
treatmentIp.transition(ipv4UnicastTableId);
FlowRule ruleIp = DefaultFlowRule.builder().forDevice(deviceId)
.withSelector(selectorIp.build())
.withTreatment(treatmentIp.build())
.withPriority(filt.priority()).fromApp(applicationId)
.makePermanent().forTable(tmacTableId).build();
log.debug("adding IP ETH rule for MAC: {}", e.mac());
log.debug("adding IP ETH rule for MAC: {}", ethCriterion.mac());
rules.add(ruleIp);
TrafficSelector.Builder selectorMpls = DefaultTrafficSelector
.builder();
TrafficTreatment.Builder treatmentMpls = DefaultTrafficTreatment
.builder();
selectorMpls.matchEthDst(e.mac());
selectorMpls.matchEthDst(ethCriterion.mac());
selectorMpls.matchEthType(Ethernet.MPLS_UNICAST);
selectorMpls.matchVlanId(vlanIdCriterion.vlanId());
treatmentMpls.popVlan();
treatmentMpls.transition(mplsTableId);
FlowRule ruleMpls = DefaultFlowRule.builder()
.forDevice(deviceId).withSelector(selectorMpls.build())
.withTreatment(treatmentMpls.build())
.withPriority(filt.priority()).fromApp(applicationId)
.makePermanent().forTable(tmacTableId).build();
log.debug("adding MPLS ETH rule for MAC: {}", e.mac());
log.debug("adding MPLS ETH rule for MAC: {}", ethCriterion.mac());
rules.add(ruleMpls);
return rules;
}
protected List<FlowRule> processVlanIdFilter(Criterion c,
protected List<FlowRule> processVlanIdFilter(VlanIdCriterion vlanIdCriterion,
FilteringObjective filt,
VlanId assignedVlan,
ApplicationId applicationId) {
List<FlowRule> rules = new ArrayList<>();
VlanIdCriterion v = (VlanIdCriterion) c;
log.debug("adding rule for VLAN: {}", v.vlanId());
log.debug("adding rule for VLAN: {}", vlanIdCriterion.vlanId());
TrafficSelector.Builder selector = DefaultTrafficSelector
.builder();
TrafficTreatment.Builder treatment = DefaultTrafficTreatment
.builder();
PortCriterion p = (PortCriterion) filt.key();
if (v.vlanId() != VlanId.NONE) {
selector.matchVlanId(v.vlanId());
if (vlanIdCriterion.vlanId() != VlanId.NONE) {
selector.matchVlanId(vlanIdCriterion.vlanId());
selector.matchInPort(p.port());
treatment.deferred().popVlan();
} else {
selector.matchInPort(p.port());
treatment.immediate().pushVlan().setVlanId(assignedVlan);
}
treatment.transition(tmacTableId);
FlowRule rule = DefaultFlowRule.builder().forDevice(deviceId)
......@@ -641,30 +790,79 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour
fail(filt, ObjectiveError.UNKNOWN);
return;
}
EthCriterion ethCriterion = null;
VlanIdCriterion vlanIdCriterion = null;
// convert filtering conditions for switch-intfs into flowrules
FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
for (Criterion c : filt.conditions()) {
if (c.type() == Criterion.Type.ETH_DST) {
for (FlowRule rule : processEthDstFilter(c,
filt,
applicationId)) {
ops = install ? ops.add(rule) : ops.remove(rule);
}
} else if (c.type() == Criterion.Type.VLAN_VID) {
for (FlowRule rule : processVlanIdFilter(c,
filt,
applicationId)) {
ops = install ? ops.add(rule) : ops.remove(rule);
}
} else if (c.type() == Criterion.Type.IPV4_DST) {
for (Criterion criterion : filt.conditions()) {
if (criterion.type() == Criterion.Type.ETH_DST) {
ethCriterion = (EthCriterion) criterion;
} else if (criterion.type() == Criterion.Type.VLAN_VID) {
vlanIdCriterion = (VlanIdCriterion) criterion;
} else if (criterion.type() == Criterion.Type.IPV4_DST) {
log.debug("driver does not process IP filtering rules as it " +
"sends all misses in the IP table to the controller");
} else {
log.warn("Driver does not currently process filtering condition"
+ " of type: {}", c.type());
+ " of type: {}", criterion.type());
fail(filt, ObjectiveError.UNSUPPORTED);
}
}
VlanId assignedVlan = null;
if (vlanIdCriterion != null && vlanIdCriterion.vlanId() == VlanId.NONE) {
// Assign a VLAN ID to untagged packets
if (filt.meta() == null) {
log.error("Missing metadata in filtering objective required "
+ "for vlan assignment in dev {}", deviceId);
fail(filt, ObjectiveError.BADPARAMS);
return;
}
for (Instruction i : filt.meta().allInstructions()) {
if (i instanceof ModVlanIdInstruction) {
assignedVlan = ((ModVlanIdInstruction) i).vlanId();
}
}
if (assignedVlan == null) {
log.error("Driver requires an assigned vlan-id to tag incoming "
+ "untagged packets. Not processing vlan filters on "
+ "device {}", deviceId);
fail(filt, ObjectiveError.BADPARAMS);
return;
}
}
if (ethCriterion == null) {
log.debug("filtering objective missing dstMac, cannot program TMAC table");
} else {
for (FlowRule tmacRule : processEthDstFilter(ethCriterion,
vlanIdCriterion,
filt,
assignedVlan,
applicationId)) {
log.debug("adding MAC filtering rules in TMAC table: {} for dev: {}",
tmacRule, deviceId);
ops = install ? ops.add(tmacRule) : ops.remove(tmacRule);
}
}
if (ethCriterion == null || vlanIdCriterion == null) {
log.debug("filtering objective missing dstMac or vlan, cannot program"
+ "Vlan Table");
} else {
for (FlowRule vlanRule : processVlanIdFilter(vlanIdCriterion,
filt,
assignedVlan,
applicationId)) {
log.debug("adding VLAN filtering rule in VLAN table: {} for dev: {}",
vlanRule, deviceId);
ops = install ? ops.add(vlanRule) : ops.remove(vlanRule);
}
}
// apply filtering flow rules
flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
@Override
......@@ -686,10 +884,10 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour
protected void setTableMissEntries() {
// set all table-miss-entries
populateTableMissEntry(vlanTableId, true, false, false, -1);
populateTableMissEntry(tmacTableId, true, false, false, -1);
populateTableMissEntry(ipv4UnicastTableId, false, true, true,
aclTableId);
populateTableMissEntry(tmacTableId, false, false, true, dstMacTableId);
populateTableMissEntry(ipv4UnicastTableId, false, true, true, aclTableId);
populateTableMissEntry(mplsTableId, false, true, true, aclTableId);
populateTableMissEntry(dstMacTableId, false, false, true, aclTableId);
populateTableMissEntry(aclTableId, false, false, false, -1);
}
......
......@@ -21,6 +21,7 @@ import java.util.List;
import org.onlab.packet.Ethernet;
import org.onlab.packet.MacAddress;
import org.onlab.packet.VlanId;
import org.onosproject.core.ApplicationId;
import org.onosproject.net.behaviour.NextGroup;
import org.onosproject.net.flow.DefaultFlowRule;
......@@ -34,6 +35,7 @@ import org.onosproject.net.flow.criteria.EthCriterion;
import org.onosproject.net.flow.criteria.EthTypeCriterion;
import org.onosproject.net.flow.criteria.IPCriterion;
import org.onosproject.net.flow.criteria.MplsCriterion;
import org.onosproject.net.flow.criteria.VlanIdCriterion;
import org.onosproject.net.flow.instructions.Instruction;
import org.onosproject.net.flowobjective.FilteringObjective;
import org.onosproject.net.flowobjective.ForwardingObjective;
......@@ -175,12 +177,13 @@ public class SpringOpenTTPDell extends SpringOpenTTP {
//Dell switches need ETH_DST based match condition in all IP table entries.
//So while processing the ETH_DST based filtering objective, store
//the device MAC to be used locally to use it while pushing the IP rules.
protected List<FlowRule> processEthDstFilter(Criterion c,
protected List<FlowRule> processEthDstFilter(EthCriterion ethCriterion,
VlanIdCriterion vlanIdCriterion,
FilteringObjective filt,
VlanId assignedVlan,
ApplicationId applicationId) {
// Store device termination Mac to be used in IP flow entries
EthCriterion e = (EthCriterion) c;
deviceTMac = e.mac();
deviceTMac = ethCriterion.mac();
log.debug("For now not adding any TMAC rules "
+ "into Dell switches as it is ignoring");
......@@ -189,8 +192,9 @@ public class SpringOpenTTPDell extends SpringOpenTTP {
}
@Override
protected List<FlowRule> processVlanIdFilter(Criterion c,
protected List<FlowRule> processVlanIdFilter(VlanIdCriterion vlanIdCriterion,
FilteringObjective filt,
VlanId assignedVlan,
ApplicationId applicationId) {
log.debug("For now not adding any VLAN rules "
+ "into Dell switches as it is ignoring");
......