diff options
Diffstat (limited to 'framework/src/onos/drivers')
14 files changed, 710 insertions, 353 deletions
diff --git a/framework/src/onos/drivers/features.xml b/framework/src/onos/drivers/features.xml index a7144492..e83f93fa 100644 --- a/framework/src/onos/drivers/features.xml +++ b/framework/src/onos/drivers/features.xml @@ -24,5 +24,7 @@ <bundle>mvn:${project.groupId}/onos-ovsdb-api/${project.version}</bundle> <bundle>mvn:${project.groupId}/onos-ovsdb-rfc/${project.version}</bundle> + + <bundle>mvn:${project.groupId}/onos-netconf-api/${project.version}</bundle> </feature> </features> diff --git a/framework/src/onos/drivers/pom.xml b/framework/src/onos/drivers/pom.xml index 56a39a8e..db0a3985 100644 --- a/framework/src/onos/drivers/pom.xml +++ b/framework/src/onos/drivers/pom.xml @@ -67,6 +67,11 @@ <artifactId>easymock</artifactId> <scope>test</scope> </dependency> + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onos-netconf-api</artifactId> + <version>${project.version}</version> + </dependency> <dependency> <groupId>org.apache.felix</groupId> diff --git a/framework/src/onos/drivers/src/main/java/org/onosproject/driver/extensions/NiciraExtensionInterpreter.java b/framework/src/onos/drivers/src/main/java/org/onosproject/driver/extensions/NiciraExtensionInterpreter.java index 082c5a6d..b8f1fd91 100644 --- a/framework/src/onos/drivers/src/main/java/org/onosproject/driver/extensions/NiciraExtensionInterpreter.java +++ b/framework/src/onos/drivers/src/main/java/org/onosproject/driver/extensions/NiciraExtensionInterpreter.java @@ -38,10 +38,12 @@ public class NiciraExtensionInterpreter extends AbstractHandlerBehaviour @Override public boolean supported(ExtensionType extensionType) { - if (extensionType.equals(ExtensionType.ExtensionTypes.NICIRA_SET_TUNNEL_DST)) { + if (extensionType.equals(ExtensionType.ExtensionTypes.NICIRA_SET_TUNNEL_DST.type())) { + return true; + } + if (extensionType.equals(ExtensionType.ExtensionTypes.NICIRA_RESUBMIT.type())) { return true; } - return false; } @@ -53,6 +55,9 @@ public class NiciraExtensionInterpreter extends AbstractHandlerBehaviour return factory.actions().setField(factory.oxms().tunnelIpv4Dst( IPv4Address.of(tunnelDst.tunnelDst().toInt()))); } + if (type.equals(ExtensionType.ExtensionTypes.NICIRA_RESUBMIT.type())) { + // TODO this will be implemented later + } return null; } @@ -78,6 +83,9 @@ public class NiciraExtensionInterpreter extends AbstractHandlerBehaviour if (type.equals(ExtensionType.ExtensionTypes.NICIRA_SET_TUNNEL_DST.type())) { return new NiciraSetTunnelDst(); } + if (type.equals(ExtensionType.ExtensionTypes.NICIRA_RESUBMIT.type())) { + return new NiciraResubmit(); + } throw new UnsupportedOperationException( "Driver does not support extension type " + type.toString()); } diff --git a/framework/src/onos/drivers/src/main/java/org/onosproject/driver/extensions/NiciraSetTunnelDst.java b/framework/src/onos/drivers/src/main/java/org/onosproject/driver/extensions/NiciraSetTunnelDst.java index 16aa1b07..a20b2479 100644 --- a/framework/src/onos/drivers/src/main/java/org/onosproject/driver/extensions/NiciraSetTunnelDst.java +++ b/framework/src/onos/drivers/src/main/java/org/onosproject/driver/extensions/NiciraSetTunnelDst.java @@ -49,6 +49,8 @@ public class NiciraSetTunnelDst extends AbstractExtensionInstruction { /** * Creates a new set tunnel destination instruction with a particular IPv4 * address. + * + * @param tunnelDst tunnel destination IPv4 address */ NiciraSetTunnelDst(Ip4Address tunnelDst) { checkNotNull(tunnelDst); diff --git a/framework/src/onos/drivers/src/main/java/org/onosproject/driver/handshaker/OFOpticalSwitchImplLINC13.java b/framework/src/onos/drivers/src/main/java/org/onosproject/driver/handshaker/OfOpticalSwitchImplLinc13.java index 7faee374..ff65e0c6 100644 --- a/framework/src/onos/drivers/src/main/java/org/onosproject/driver/handshaker/OFOpticalSwitchImplLINC13.java +++ b/framework/src/onos/drivers/src/main/java/org/onosproject/driver/handshaker/OfOpticalSwitchImplLinc13.java @@ -53,7 +53,7 @@ import java.util.concurrent.atomic.AtomicBoolean; * while it sends *all* ports (both tap and WDM ports, i.e., OCh and OMS) in the experimenter port desc stats reply. * */ -public class OFOpticalSwitchImplLINC13 +public class OfOpticalSwitchImplLinc13 extends AbstractOpenFlowSwitch implements OpenFlowOpticalSwitch { private final AtomicBoolean driverHandshakeComplete = new AtomicBoolean(false); diff --git a/framework/src/onos/drivers/src/main/java/org/onosproject/driver/ovsdb/OvsdbBridgeConfig.java b/framework/src/onos/drivers/src/main/java/org/onosproject/driver/ovsdb/OvsdbBridgeConfig.java index 77b48298..6451160a 100644 --- a/framework/src/onos/drivers/src/main/java/org/onosproject/driver/ovsdb/OvsdbBridgeConfig.java +++ b/framework/src/onos/drivers/src/main/java/org/onosproject/driver/ovsdb/OvsdbBridgeConfig.java @@ -54,6 +54,13 @@ public class OvsdbBridgeConfig extends AbstractHandlerBehaviour } @Override + public void addBridge(BridgeName bridgeName, String dpid, String exPortName) { + DriverHandler handler = handler(); + OvsdbClientService clientService = getOvsdbClientService(handler); + clientService.createBridge(bridgeName.name(), dpid, exPortName); + } + + @Override public boolean addBridge(BridgeName bridgeName, String dpid, List<ControllerInfo> controllers) { DriverHandler handler = handler(); OvsdbClientService clientService = getOvsdbClientService(handler); diff --git a/framework/src/onos/drivers/src/main/java/org/onosproject/driver/pipeline/CpqdOFDPA2Pipeline.java b/framework/src/onos/drivers/src/main/java/org/onosproject/driver/pipeline/CpqdOFDPA2Pipeline.java index a830ed49..0cb30d28 100644 --- a/framework/src/onos/drivers/src/main/java/org/onosproject/driver/pipeline/CpqdOFDPA2Pipeline.java +++ b/framework/src/onos/drivers/src/main/java/org/onosproject/driver/pipeline/CpqdOFDPA2Pipeline.java @@ -17,6 +17,16 @@ package org.onosproject.driver.pipeline; import static org.slf4j.LoggerFactory.getLogger; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +import org.onlab.packet.VlanId; +import org.onosproject.core.ApplicationId; +import org.onosproject.net.Port; +import org.onosproject.net.PortNumber; import org.onosproject.net.flow.DefaultFlowRule; import org.onosproject.net.flow.DefaultTrafficSelector; import org.onosproject.net.flow.DefaultTrafficTreatment; @@ -25,6 +35,8 @@ import org.onosproject.net.flow.FlowRuleOperations; import org.onosproject.net.flow.FlowRuleOperationsContext; import org.onosproject.net.flow.TrafficSelector; import org.onosproject.net.flow.TrafficTreatment; +import org.onosproject.net.flow.criteria.PortCriterion; +import org.onosproject.net.flow.criteria.VlanIdCriterion; import org.slf4j.Logger; @@ -37,16 +49,76 @@ public class CpqdOFDPA2Pipeline extends OFDPA2Pipeline { private final Logger log = getLogger(getClass()); @Override + protected List<FlowRule> processVlanIdFilter(PortCriterion portCriterion, + VlanIdCriterion vidCriterion, + VlanId assignedVlan, + ApplicationId applicationId) { + List<FlowRule> rules = new ArrayList<FlowRule>(); + TrafficSelector.Builder selector = DefaultTrafficSelector.builder(); + TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder(); + selector.matchVlanId(vidCriterion.vlanId()); + treatment.transition(TMAC_TABLE); + + VlanId storeVlan = null; + if (vidCriterion.vlanId() == VlanId.NONE) { + // untagged packets are assigned vlans + treatment.pushVlan().setVlanId(assignedVlan); + storeVlan = assignedVlan; + } else { + storeVlan = vidCriterion.vlanId(); + } + + // ofdpa cannot match on ALL portnumber, so we need to use separate + // rules for each port. + List<PortNumber> portnums = new ArrayList<PortNumber>(); + if (portCriterion.port() == PortNumber.ALL) { + for (Port port : deviceService.getPorts(deviceId)) { + if (port.number().toLong() > 0 && port.number().toLong() < OFPP_MAX) { + portnums.add(port.number()); + } + } + } else { + portnums.add(portCriterion.port()); + } + + for (PortNumber pnum : portnums) { + // update storage + port2Vlan.put(pnum, storeVlan); + Set<PortNumber> vlanPorts = vlan2Port.get(storeVlan); + if (vlanPorts == null) { + vlanPorts = Collections.newSetFromMap( + new ConcurrentHashMap<PortNumber, Boolean>()); + vlanPorts.add(pnum); + vlan2Port.put(storeVlan, vlanPorts); + } else { + vlanPorts.add(pnum); + } + // create rest of flowrule + selector.matchInPort(pnum); + FlowRule rule = DefaultFlowRule.builder() + .forDevice(deviceId) + .withSelector(selector.build()) + .withTreatment(treatment.build()) + .withPriority(DEFAULT_PRIORITY) + .fromApp(applicationId) + .makePermanent() + .forTable(VLAN_TABLE).build(); + rules.add(rule); + } + return rules; + } + + + @Override protected void initializePipeline() { processPortTable(); + // vlan table processing not required, as default is to drop packets + // which can be accomplished without a table-miss-entry. processTmacTable(); processIpTable(); + processMplsTable(); processBridgingTable(); processAclTable(); - // XXX implement table miss entries and default groups - //processVlanTable(); - //processMPLSTable(); - //processGroupTable(); } @Override @@ -140,6 +212,49 @@ public class CpqdOFDPA2Pipeline extends OFDPA2Pipeline { })); } + @Override + protected void processMplsTable() { + //table miss entry + FlowRuleOperations.Builder ops = FlowRuleOperations.builder(); + TrafficSelector.Builder selector = DefaultTrafficSelector.builder(); + TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder(); + selector = DefaultTrafficSelector.builder(); + treatment = DefaultTrafficTreatment.builder(); + treatment.transition(MPLS_TABLE_1); + FlowRule rule = DefaultFlowRule.builder() + .forDevice(deviceId) + .withSelector(selector.build()) + .withTreatment(treatment.build()) + .withPriority(LOWEST_PRIORITY) + .fromApp(driverId) + .makePermanent() + .forTable(MPLS_TABLE_0).build(); + ops = ops.add(rule); + + treatment.transition(ACL_TABLE); + rule = DefaultFlowRule.builder() + .forDevice(deviceId) + .withSelector(selector.build()) + .withTreatment(treatment.build()) + .withPriority(LOWEST_PRIORITY) + .fromApp(driverId) + .makePermanent() + .forTable(MPLS_TABLE_1).build(); + ops = ops.add(rule); + + flowRuleService.apply(ops.build(new FlowRuleOperationsContext() { + @Override + public void onSuccess(FlowRuleOperations ops) { + log.info("Initialized MPLS tables"); + } + + @Override + public void onError(FlowRuleOperations ops) { + log.info("Failed to initialize MPLS tables"); + } + })); + } + private void processBridgingTable() { //table miss entry FlowRuleOperations.Builder ops = FlowRuleOperations.builder(); diff --git a/framework/src/onos/drivers/src/main/java/org/onosproject/driver/pipeline/OFDPA2Pipeline.java b/framework/src/onos/drivers/src/main/java/org/onosproject/driver/pipeline/OFDPA2Pipeline.java index b1a1256a..cf3c7e89 100644 --- a/framework/src/onos/drivers/src/main/java/org/onosproject/driver/pipeline/OFDPA2Pipeline.java +++ b/framework/src/onos/drivers/src/main/java/org/onosproject/driver/pipeline/OFDPA2Pipeline.java @@ -23,11 +23,13 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; import org.onlab.osgi.ServiceDirectory; @@ -122,7 +124,7 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline protected static final long OFPP_MAX = 0xffffff00L; private static final int HIGHEST_PRIORITY = 0xffff; - private static final int DEFAULT_PRIORITY = 0x8000; + protected static final int DEFAULT_PRIORITY = 0x8000; protected static final int LOWEST_PRIORITY = 0x0; /* @@ -147,6 +149,7 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline private static final int L3UNICASTMASK = 0x20000000; //private static final int MPLSINTERFACEMASK = 0x90000000; private static final int L3ECMPMASK = 0x70000000; + private static final int L2FLOODMASK = 0x40000000; private final Logger log = getLogger(getClass()); private ServiceDirectory serviceDirectory; @@ -176,6 +179,13 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline private Set<IPCriterion> sentIpFilters = Collections.newSetFromMap( new ConcurrentHashMap<IPCriterion, Boolean>()); + // local stores for port-vlan mapping + Map<PortNumber, VlanId> port2Vlan = new ConcurrentHashMap<PortNumber, VlanId>(); + Map<VlanId, Set<PortNumber>> vlan2Port = new ConcurrentHashMap<VlanId, + Set<PortNumber>>(); + + + @Override public void init(DeviceId deviceId, PipelinerContext context) { this.serviceDirectory = context.directory(); @@ -275,26 +285,23 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline @Override public void next(NextObjective nextObjective) { - switch (nextObjective.type()) { - case SIMPLE: - Collection<TrafficTreatment> treatments = nextObjective.next(); - if (treatments.size() != 1) { - log.error("Next Objectives of type Simple should only have a " - + "single Traffic Treatment. Next Objective Id:{}", nextObjective.id()); - fail(nextObjective, ObjectiveError.BADPARAMS); - return; + log.debug("Processing NextObjective id{} op{}", nextObjective.id(), + nextObjective.op()); + if (nextObjective.op() == Objective.Operation.REMOVE) { + if (nextObjective.next().isEmpty()) { + removeGroup(nextObjective); + } else { + removeBucketFromGroup(nextObjective); } - processSimpleNextObjective(nextObjective); - break; - case HASHED: - case BROADCAST: - case FAILOVER: - fail(nextObjective, ObjectiveError.UNSUPPORTED); - log.warn("Unsupported next objective type {}", nextObjective.type()); - break; - default: - fail(nextObjective, ObjectiveError.UNKNOWN); - log.warn("Unknown next objective type {}", nextObjective.type()); + } else if (nextObjective.op() == Objective.Operation.ADD) { + NextGroup nextGroup = flowObjectiveStore.getNextGroup(nextObjective.id()); + if (nextGroup != null) { + addBucketToGroup(nextObjective); + } else { + addGroup(nextObjective); + } + } else { + log.warn("Unsupported operation {}", nextObjective.op()); } } @@ -302,6 +309,7 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline // Flow handling ////////////////////////////////////// + /** * As per OFDPA 2.0 TTP, filtering of VLAN ids, MAC addresses (for routing) * and IP addresses configured on switch ports happen in different tables. @@ -455,13 +463,19 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline TrafficSelector.Builder selector = DefaultTrafficSelector.builder(); TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder(); selector.matchVlanId(vidCriterion.vlanId()); + treatment.transition(TMAC_TABLE); + + VlanId storeVlan = null; if (vidCriterion.vlanId() == VlanId.NONE) { // untagged packets are assigned vlans treatment.pushVlan().setVlanId(assignedVlan); - // XXX ofdpa may require an additional vlan match on the assigned vlan - // and it may not require the push. + // XXX ofdpa will require an additional vlan match on the assigned vlan + // and it may not require the push. This is not in compliance with OF + // standard. Waiting on what the exact flows are going to look like. + storeVlan = assignedVlan; + } else { + storeVlan = vidCriterion.vlanId(); } - treatment.transition(TMAC_TABLE); // ofdpa cannot match on ALL portnumber, so we need to use separate // rules for each port. @@ -475,7 +489,20 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline } else { portnums.add(portCriterion.port()); } + for (PortNumber pnum : portnums) { + // update storage + port2Vlan.put(pnum, storeVlan); + Set<PortNumber> vlanPorts = vlan2Port.get(storeVlan); + if (vlanPorts == null) { + vlanPorts = Collections.newSetFromMap( + new ConcurrentHashMap<PortNumber, Boolean>()); + vlanPorts.add(pnum); + vlan2Port.put(storeVlan, vlanPorts); + } else { + vlanPorts.add(pnum); + } + // create rest of flowrule selector.matchInPort(pnum); FlowRule rule = DefaultFlowRule.builder() .forDevice(deviceId) @@ -707,10 +734,39 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline // Group handling ////////////////////////////////////// + private void addGroup(NextObjective nextObjective) { + switch (nextObjective.type()) { + case SIMPLE: + Collection<TrafficTreatment> treatments = nextObjective.next(); + if (treatments.size() != 1) { + log.error("Next Objectives of type Simple should only have a " + + "single Traffic Treatment. Next Objective Id:{}", + nextObjective.id()); + fail(nextObjective, ObjectiveError.BADPARAMS); + return; + } + processSimpleNextObjective(nextObjective); + break; + case BROADCAST: + processBroadcastNextObjective(nextObjective); + break; + case HASHED: + processHashedNextObjective(nextObjective); + break; + case FAILOVER: + fail(nextObjective, ObjectiveError.UNSUPPORTED); + log.warn("Unsupported next objective type {}", nextObjective.type()); + break; + default: + fail(nextObjective, ObjectiveError.UNKNOWN); + log.warn("Unknown next objective type {}", nextObjective.type()); + } + } + /** * As per the OFDPA 2.0 TTP, packets are sent out of ports by using * a chain of groups, namely an L3 Unicast Group that points to an L2 Interface - * Group which in turns points to an output port. The Next Objective passed + * Group which in-turn points to an output port. The Next Objective passed * in by the application has to be broken up into a group chain * to satisfy this TTP. * @@ -769,7 +825,9 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline Integer l3groupId = L3UNICASTMASK | (int) portNum; l3utt.group(new DefaultGroupId(l2groupId)); GroupChainElem gce = new GroupChainElem(l3groupkey, l3groupId, - l3utt.build(), nextObj.appId()); + GroupDescription.Type.INDIRECT, + Collections.singletonList(l3utt.build()), + nextObj.appId(), 1); // create object for local and distributed storage List<GroupKey> gkeys = new ArrayList<GroupKey>(); @@ -796,27 +854,201 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline } /** + * As per the OFDPA 2.0 TTP, packets are sent out of ports by using + * a chain of groups. The Next Objective passed in by the application + * has to be broken up into a group chain comprising of an + * L2 Flood group whose buckets point to L2 Interface groups. + * + * @param nextObj the nextObjective of type BROADCAST + */ + private void processBroadcastNextObjective(NextObjective nextObj) { + // break up broadcast next objective to multiple groups + Collection<TrafficTreatment> buckets = nextObj.next(); + + // each treatment is converted to an L2 interface group + int indicator = 0; + VlanId vlanid = null; + List<GroupInfo> groupInfoCollection = new ArrayList<>(); + for (TrafficTreatment treatment : buckets) { + TrafficTreatment.Builder newTreatment = DefaultTrafficTreatment.builder(); + PortNumber portNum = null; + // ensure that the only allowed treatments are pop-vlan and output + for (Instruction ins : treatment.allInstructions()) { + if (ins.type() == Instruction.Type.L2MODIFICATION) { + L2ModificationInstruction l2ins = (L2ModificationInstruction) ins; + switch (l2ins.subtype()) { + case VLAN_POP: + newTreatment.add(l2ins); + break; + default: + log.debug("action {} not permitted for broadcast nextObj", + l2ins.subtype()); + break; + } + } else if (ins.type() == Instruction.Type.OUTPUT) { + portNum = ((OutputInstruction) ins).port(); + newTreatment.add(ins); + } else { + log.debug("TrafficTreatment of type {} not permitted in " + + " broadcast nextObjective", ins.type()); + } + } + + // also ensure that all ports are in the same vlan + VlanId thisvlanid = port2Vlan.get(portNum); + if (vlanid == null) { + vlanid = thisvlanid; + } else { + if (!vlanid.equals(thisvlanid)) { + log.error("Driver requires all ports in a broadcast nextObj " + + "to be in the same vlan. Different vlans found " + + "{} and {}. Aborting group creation", vlanid, thisvlanid); + return; + } + } + + // assemble info for all l2 interface groups + indicator += GROUP1MASK; + int l2gk = nextObj.id() | indicator; + final GroupKey l2groupkey = new DefaultGroupKey(appKryo.serialize(l2gk)); + Integer l2groupId = L2INTERFACEMASK | (vlanid.toShort() << 16) | + (int) portNum.toLong(); + GroupBucket newbucket = + DefaultGroupBucket.createIndirectGroupBucket(newTreatment.build()); + + // store the info needed to create this group + groupInfoCollection.add(new GroupInfo(l2groupId, l2groupkey, newbucket)); + } + + // assemble info for l2 flood group + int l2floodgk = nextObj.id() | GROUP0MASK; + final GroupKey l2floodgroupkey = new DefaultGroupKey(appKryo.serialize(l2floodgk)); + Integer l2floodgroupId = L2FLOODMASK | (vlanid.toShort() << 16) | nextObj.id(); + // collection of treatment with groupids of l2 interface groups + List<TrafficTreatment> floodtt = new ArrayList<>(); + for (GroupInfo gi : groupInfoCollection) { + TrafficTreatment.Builder ttb = DefaultTrafficTreatment.builder(); + ttb.group(new DefaultGroupId(gi.groupId)); + floodtt.add(ttb.build()); + } + GroupChainElem gce = new GroupChainElem(l2floodgroupkey, l2floodgroupId, + GroupDescription.Type.ALL, + floodtt, + nextObj.appId(), + groupInfoCollection.size()); + + // create objects for local and distributed storage + List<GroupKey> gkeys = new ArrayList<GroupKey>(); + gkeys.add(l2floodgroupkey); // group0 in chain + OfdpaGroupChain ofdpaGrp = new OfdpaGroupChain(gkeys, nextObj); + + // store l2floodgroupkey with the ofdpaGroupChain for the nextObjective + // that depends on it + pendingNextObjectives.put(l2floodgroupkey, ofdpaGrp); + + for (GroupInfo gi : groupInfoCollection) { + // store all l2groupkeys with the groupChainElem for the l2floodgroup + // that depends on it + pendingGroups.put(gi.groupKey, gce); + + // create and send groups for all l2 interface groups + GroupDescription groupDescription = + new DefaultGroupDescription( + deviceId, + GroupDescription.Type.INDIRECT, + new GroupBuckets(Collections.singletonList(gi.groupBucket)), + gi.groupKey, + gi.groupId, + nextObj.appId()); + groupService.addGroup(groupDescription); + } + } + + private class GroupInfo { + private Integer groupId; + private GroupKey groupKey; + private GroupBucket groupBucket; + + GroupInfo(Integer groupId, GroupKey groupKey, GroupBucket groupBucket) { + this.groupBucket = groupBucket; + this.groupId = groupId; + this.groupKey = groupKey; + } + } + + private void processHashedNextObjective(NextObjective nextObj) { + // TODO Auto-generated method stub + } + + private void addBucketToGroup(NextObjective nextObjective) { + // TODO Auto-generated method stub + } + + private void removeBucketFromGroup(NextObjective nextObjective) { + // TODO Auto-generated method stub + } + + private void removeGroup(NextObjective nextObjective) { + // TODO Auto-generated method stub + } + + /** * Processes next element of a group chain. Assumption is that if this * group points to another group, the latter has already been created * and this driver has received notification for it. A second assumption is * that if there is another group waiting for this group then the appropriate * stores already have the information to act upon the notification for the * creating of this group. + * <p> + * The processing of the GroupChainElement depends on the number of groups + * this element is waiting on. For all group types other than SIMPLE, a + * GroupChainElement could be waiting on multiple groups. * * @param gce the group chain element to be processed next */ private void processGroupChain(GroupChainElem gce) { - GroupBucket bucket = DefaultGroupBucket - .createIndirectGroupBucket(gce.getBucketActions()); - GroupDescription groupDesc = new DefaultGroupDescription(deviceId, - GroupDescription.Type.INDIRECT, - new GroupBuckets(Collections.singletonList(bucket)), - gce.getGkey(), - gce.getGivenGroupId(), - gce.getAppId()); - groupService.addGroup(groupDesc); - } + int waitOnGroups = gce.decrementAndGetGroupsWaitedOn(); + if (waitOnGroups != 0) { + log.debug("GCE: {} waiting on {} groups. Not processing yet", + gce, waitOnGroups); + return; + } + List<GroupBucket> buckets = new ArrayList<>(); + switch (gce.groupType) { + case INDIRECT: + GroupBucket ibucket = DefaultGroupBucket + .createIndirectGroupBucket(gce.bucketActions.iterator().next()); + buckets.add(ibucket); + break; + case ALL: + for (TrafficTreatment tt : gce.bucketActions) { + GroupBucket abucket = DefaultGroupBucket + .createAllGroupBucket(tt); + buckets.add(abucket); + } + break; + case SELECT: + for (TrafficTreatment tt : gce.bucketActions) { + GroupBucket sbucket = DefaultGroupBucket + .createSelectGroupBucket(tt); + buckets.add(sbucket); + } + break; + case FAILOVER: + default: + log.error("Unknown or unimplemented GroupChainElem {}", gce); + } + if (buckets.size() > 0) { + GroupDescription groupDesc = new DefaultGroupDescription( + deviceId, gce.groupType, + new GroupBuckets(buckets), + gce.gkey, + gce.givenGroupId, + gce.appId); + groupService.addGroup(groupDesc); + } + } private class GroupChecker implements Runnable { @Override @@ -836,7 +1068,7 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline log.info("Group service processed group key {}. Processing next " + "group in group chain with group key {}", appKryo.deserialize(key.key()), - appKryo.deserialize(gce.getGkey().key())); + appKryo.deserialize(gce.gkey.key())); processGroupChain(gce); } else { OfdpaGroupChain obj = pendingNextObjectives.getIfPresent(key); @@ -865,7 +1097,7 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline log.info("group ADDED with group key {} .. " + "Processing next group in group chain with group key {}", appKryo.deserialize(key.key()), - appKryo.deserialize(gce.getGkey().key())); + appKryo.deserialize(gce.gkey.key())); processGroupChain(gce); } else { OfdpaGroupChain obj = pendingNextObjectives.getIfPresent(key); @@ -889,6 +1121,11 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline * look like group0 --> group 1 --> outPort. Information about the groups * themselves can be fetched from the Group Service using the group keys from * objects instantiating this class. + * + * XXX Revisit this - since the forwarding objective only ever needs the + * groupkey of the top-level group in the group chain, why store a series + * of groupkeys. Also the group-chain list only works for 1-to-1 chaining, + * not for 1-to-many chaining. */ private class OfdpaGroupChain implements NextGroup { private final NextObjective nextObj; @@ -924,33 +1161,40 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline * preceding groups in the group chain to be created. */ private class GroupChainElem { - private TrafficTreatment bucketActions; + private Collection<TrafficTreatment> bucketActions; private Integer givenGroupId; + private GroupDescription.Type groupType; private GroupKey gkey; private ApplicationId appId; + private AtomicInteger waitOnGroups; - public GroupChainElem(GroupKey gkey, Integer givenGroupId, - TrafficTreatment tr, ApplicationId appId) { + GroupChainElem(GroupKey gkey, Integer givenGroupId, + GroupDescription.Type groupType, + Collection<TrafficTreatment> tr, ApplicationId appId, + int waitOnGroups) { this.bucketActions = tr; this.givenGroupId = givenGroupId; + this.groupType = groupType; this.gkey = gkey; this.appId = appId; + this.waitOnGroups = new AtomicInteger(waitOnGroups); } - public TrafficTreatment getBucketActions() { - return bucketActions; - } - - public Integer getGivenGroupId() { - return givenGroupId; + /** + * This methods atomically decrements the counter for the number of + * groups this GroupChainElement is waiting on, for notifications from + * the Group Service. When this method returns a value of 0, this + * GroupChainElement is ready to be processed. + * + * @return integer indication of the number of notifications being waited on + */ + int decrementAndGetGroupsWaitedOn() { + return waitOnGroups.decrementAndGet(); } - public GroupKey getGkey() { - return gkey; - } - - public ApplicationId getAppId() { - return appId; + @Override + public String toString() { + return Integer.toHexString(givenGroupId); } } diff --git a/framework/src/onos/drivers/src/main/java/org/onosproject/driver/pipeline/OLTPipeline.java b/framework/src/onos/drivers/src/main/java/org/onosproject/driver/pipeline/OLTPipeline.java deleted file mode 100644 index c735af3f..00000000 --- a/framework/src/onos/drivers/src/main/java/org/onosproject/driver/pipeline/OLTPipeline.java +++ /dev/null @@ -1,238 +0,0 @@ -/* - * Copyright 2015 Open Networking Laboratory - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.onosproject.driver.pipeline; - -import org.onlab.osgi.ServiceDirectory; -import org.onlab.packet.EthType; -import org.onosproject.core.ApplicationId; -import org.onosproject.core.CoreService; -import org.onosproject.net.DefaultAnnotations; -import org.onosproject.net.Device; -import org.onosproject.net.DeviceId; -import org.onosproject.net.MastershipRole; -import org.onosproject.net.PortNumber; -import org.onosproject.net.behaviour.Pipeliner; -import org.onosproject.net.behaviour.PipelinerContext; -import org.onosproject.net.device.DefaultDeviceDescription; -import org.onosproject.net.device.DeviceDescription; -import org.onosproject.net.device.DeviceProvider; -import org.onosproject.net.device.DeviceProviderRegistry; -import org.onosproject.net.device.DeviceService; -import org.onosproject.net.driver.AbstractHandlerBehaviour; -import org.onosproject.net.flow.DefaultFlowRule; -import org.onosproject.net.flow.DefaultTrafficSelector; -import org.onosproject.net.flow.DefaultTrafficTreatment; -import org.onosproject.net.flow.FlowRule; -import org.onosproject.net.flow.FlowRuleOperations; -import org.onosproject.net.flow.FlowRuleOperationsContext; -import org.onosproject.net.flow.FlowRuleService; -import org.onosproject.net.flow.TrafficSelector; -import org.onosproject.net.flow.TrafficTreatment; -import org.onosproject.net.flow.instructions.Instructions; -import org.onosproject.net.flowobjective.FilteringObjective; -import org.onosproject.net.flowobjective.ForwardingObjective; -import org.onosproject.net.flowobjective.NextObjective; -import org.onosproject.net.flowobjective.ObjectiveError; -import org.onosproject.net.packet.PacketPriority; -import org.onosproject.net.provider.AbstractProvider; -import org.onosproject.net.provider.ProviderId; -import org.slf4j.Logger; - -import static com.google.common.base.Preconditions.checkNotNull; -import static org.slf4j.LoggerFactory.getLogger; - -/** - * Pipeliner for OLT device. - */ -public class OLTPipeline extends AbstractHandlerBehaviour implements Pipeliner { - - private final Logger log = getLogger(getClass()); - - static final ProviderId PID = new ProviderId("olt", "org.onosproject.olt", true); - - static final String DEVICE = "isAccess"; - static final String OLT = "true"; - - private ServiceDirectory serviceDirectory; - private FlowRuleService flowRuleService; - private DeviceId deviceId; - private CoreService coreService; - - private ApplicationId appId; - - private DeviceProvider provider = new AnnotationProvider(); - - - @Override - public void init(DeviceId deviceId, PipelinerContext context) { - this.serviceDirectory = context.directory(); - this.deviceId = deviceId; - DeviceProviderRegistry registry = - serviceDirectory.get(DeviceProviderRegistry.class); - flowRuleService = serviceDirectory.get(FlowRuleService.class); - coreService = serviceDirectory.get(CoreService.class); - - /*try { - DeviceProviderService providerService = registry.register(provider); - providerService.deviceConnected(deviceId, - description(deviceId, DEVICE, OLT)); - } finally { - registry.unregister(provider); - }*/ - - appId = coreService.registerApplication( - "org.onosproject.driver.OLTPipeline"); - - TrafficSelector selector = DefaultTrafficSelector.builder() - .matchEthType(EthType.EtherType.EAPOL.ethType().toShort()) - .build(); - - TrafficTreatment treatment = DefaultTrafficTreatment.builder() - .punt() - .build(); - - FlowRule flowRule = new DefaultFlowRule(deviceId, selector, treatment, - PacketPriority.CONTROL.priorityValue(), - appId, 0, true, null); - - //flowRuleService.applyFlowRules(flowRule); - } - - @Override - public void filter(FilteringObjective filter) { - throw new UnsupportedOperationException("OLT does not filter."); - } - - @Override - public void forward(ForwardingObjective fwd) { - FlowRuleOperations.Builder flowBuilder = FlowRuleOperations.builder(); - - if (fwd.flag() != ForwardingObjective.Flag.VERSATILE) { - throw new UnsupportedOperationException( - "Only VERSATILE is supported."); - } - - boolean isPunt = fwd.treatment().immediate().stream().anyMatch(i -> { - if (i instanceof Instructions.OutputInstruction) { - Instructions.OutputInstruction out = (Instructions.OutputInstruction) i; - return out.port().equals(PortNumber.CONTROLLER); - } - return false; - }); - - if (isPunt) { - return; - } - - TrafficSelector selector = fwd.selector(); - TrafficTreatment treatment = fwd.treatment(); - - FlowRule.Builder ruleBuilder = DefaultFlowRule.builder() - .forDevice(deviceId) - .withSelector(selector) - .withTreatment(treatment) - .fromApp(fwd.appId()) - .withPriority(fwd.priority()); - - if (fwd.permanent()) { - ruleBuilder.makePermanent(); - } else { - ruleBuilder.makeTemporary(fwd.timeout()); - } - - switch (fwd.op()) { - case ADD: - flowBuilder.add(ruleBuilder.build()); - break; - case REMOVE: - flowBuilder.remove(ruleBuilder.build()); - break; - default: - log.warn("Unknown operation {}", fwd.op()); - } - - flowRuleService.apply(flowBuilder.build(new FlowRuleOperationsContext() { - @Override - public void onSuccess(FlowRuleOperations ops) { - if (fwd.context().isPresent()) { - fwd.context().get().onSuccess(fwd); - } - } - - @Override - public void onError(FlowRuleOperations ops) { - if (fwd.context().isPresent()) { - fwd.context().get().onError(fwd, ObjectiveError.FLOWINSTALLATIONFAILED); - } - } - })); - } - - @Override - public void next(NextObjective nextObjective) { - throw new UnsupportedOperationException("OLT does not next hop."); - } - - /** - * Build a device description. - * - * @param deviceId a deviceId - * @param key the key of the annotation - * @param value the value for the annotation - * @return a device description - */ - private DeviceDescription description(DeviceId deviceId, String key, String value) { - DeviceService deviceService = serviceDirectory.get(DeviceService.class); - Device device = deviceService.getDevice(deviceId); - - checkNotNull(device, "Device not found in device service."); - - DefaultAnnotations.Builder builder = DefaultAnnotations.builder(); - if (value != null) { - builder.set(key, value); - } else { - builder.remove(key); - } - return new DefaultDeviceDescription(device.id().uri(), device.type(), - device.manufacturer(), device.hwVersion(), - device.swVersion(), device.serialNumber(), - device.chassisId(), builder.build()); - } - - /** - * Simple ancillary provider used to annotate device. - */ - private static final class AnnotationProvider - extends AbstractProvider implements DeviceProvider { - private AnnotationProvider() { - super(PID); - } - - @Override - public void triggerProbe(DeviceId deviceId) { - } - - @Override - public void roleChanged(DeviceId deviceId, MastershipRole newRole) { - } - - @Override - public boolean isReachable(DeviceId deviceId) { - return false; - } - } - -} diff --git a/framework/src/onos/drivers/src/main/java/org/onosproject/driver/pipeline/PicaPipeline.java b/framework/src/onos/drivers/src/main/java/org/onosproject/driver/pipeline/PicaPipeline.java index 32d6b103..69d20835 100644 --- a/framework/src/onos/drivers/src/main/java/org/onosproject/driver/pipeline/PicaPipeline.java +++ b/framework/src/onos/drivers/src/main/java/org/onosproject/driver/pipeline/PicaPipeline.java @@ -462,10 +462,10 @@ public class PicaPipeline extends AbstractHandlerBehaviour implements Pipeliner private void initializePipeline() { //processIpUnicastTable(true); - processACLTable(true); + processAclTable(true); } - private void processACLTable(boolean install) { + private void processAclTable(boolean install) { TrafficSelector.Builder selector; TrafficTreatment.Builder treatment; FlowRuleOperations.Builder ops = FlowRuleOperations.builder(); diff --git a/framework/src/onos/drivers/src/main/java/org/onosproject/driver/pipeline/SoftRouterPipeline.java b/framework/src/onos/drivers/src/main/java/org/onosproject/driver/pipeline/SoftRouterPipeline.java index 23ef5f2b..bd49e688 100644 --- a/framework/src/onos/drivers/src/main/java/org/onosproject/driver/pipeline/SoftRouterPipeline.java +++ b/framework/src/onos/drivers/src/main/java/org/onosproject/driver/pipeline/SoftRouterPipeline.java @@ -15,13 +15,6 @@ */ package org.onosproject.driver.pipeline; -import static org.slf4j.LoggerFactory.getLogger; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.concurrent.ConcurrentHashMap; - import org.onlab.osgi.ServiceDirectory; import org.onlab.packet.Ethernet; import org.onlab.packet.MacAddress; @@ -57,8 +50,15 @@ import org.onosproject.net.flowobjective.ForwardingObjective; import org.onosproject.net.flowobjective.NextObjective; import org.onosproject.net.flowobjective.Objective; import org.onosproject.net.flowobjective.ObjectiveError; -import org.slf4j.Logger; import org.onosproject.store.serializers.KryoNamespaces; +import org.slf4j.Logger; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.concurrent.ConcurrentHashMap; + +import static org.slf4j.LoggerFactory.getLogger; /** * Simple 2-Table Pipeline for Software/NPU based routers. This pipeline @@ -100,7 +100,7 @@ public class SoftRouterPipeline extends AbstractHandlerBehaviour implements Pipe flowRuleService = serviceDirectory.get(FlowRuleService.class); flowObjectiveStore = context.store(); driverId = coreService.registerApplication( - "org.onosproject.driver.OVSCorsaPipeline"); + "org.onosproject.driver.SoftRouterPipeline"); filters = Collections.newSetFromMap(new ConcurrentHashMap<Filter, Boolean>()); pendingVersatiles = Collections.newSetFromMap( new ConcurrentHashMap<ForwardingObjective, Boolean>()); diff --git a/framework/src/onos/drivers/src/main/java/org/onosproject/driver/pipeline/SpringOpenTTP.java b/framework/src/onos/drivers/src/main/java/org/onosproject/driver/pipeline/SpringOpenTTP.java index 31297ff4..b5541065 100644 --- a/framework/src/onos/drivers/src/main/java/org/onosproject/driver/pipeline/SpringOpenTTP.java +++ b/framework/src/onos/drivers/src/main/java/org/onosproject/driver/pipeline/SpringOpenTTP.java @@ -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(); } - 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); } diff --git a/framework/src/onos/drivers/src/main/java/org/onosproject/driver/pipeline/SpringOpenTTPDell.java b/framework/src/onos/drivers/src/main/java/org/onosproject/driver/pipeline/SpringOpenTTPDell.java index 3267d550..91f2679c 100644 --- a/framework/src/onos/drivers/src/main/java/org/onosproject/driver/pipeline/SpringOpenTTPDell.java +++ b/framework/src/onos/drivers/src/main/java/org/onosproject/driver/pipeline/SpringOpenTTPDell.java @@ -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"); diff --git a/framework/src/onos/drivers/src/main/resources/onos-drivers.xml b/framework/src/onos/drivers/src/main/resources/onos-drivers.xml index af498832..0349e1c2 100644 --- a/framework/src/onos/drivers/src/main/resources/onos-drivers.xml +++ b/framework/src/onos/drivers/src/main/resources/onos-drivers.xml @@ -37,6 +37,13 @@ <behaviour api="org.onosproject.net.behaviour.ExtensionResolver" impl="org.onosproject.driver.extensions.NiciraExtensionInterpreter" /> </driver> + <driver name="ovs-netconf" extends="default" + manufacturer="Nicira, Inc\." hwVersion="Open vSwitch" swVersion="2\..*"> + <behaviour api="org.onosproject.openflow.controller.driver.OpenFlowSwitchDriver" + impl="org.onosproject.driver.handshaker.NiciraSwitchHandshaker"/> + <behaviour api="org.onosproject.net.behaviour.ControllerConfig" + impl="org.onosproject.driver.netconf.NetconfControllerConfig"/> + </driver> <driver name="ovs-corsa" extends="ovs" manufacturer="Corsa" hwVersion="emulation" swVersion="0.0.0"> <behaviour api="org.onosproject.net.behaviour.Pipeliner" @@ -61,7 +68,7 @@ manufacturer="FlowForwarding.org" hwVersion="Unknown" swVersion="LINC-OE OpenFlow Software Switch 1.1"> <behaviour api="org.onosproject.openflow.controller.driver.OpenFlowSwitchDriver" - impl="org.onosproject.driver.handshaker.OFOpticalSwitchImplLINC13"/> + impl="org.onosproject.driver.handshaker.OfOpticalSwitchImplLinc13"/> </driver> <driver name="corsa" manufacturer="Corsa" hwVersion="Corsa Element" swVersion="2.3.1"> @@ -78,12 +85,12 @@ <driver name="pmc-olt" extends="default" manufacturer="Big Switch Networks" hwVersion="ivs 0.5" swVersion="ivs 0.5"> <behaviour api="org.onosproject.net.behaviour.Pipeliner" - impl="org.onosproject.driver.pipeline.OLTPipeline"/> + impl="org.onosproject.driver.pipeline.OltPipeline"/> </driver> <driver name="g.fast" extends="default" manufacturer="TEST1" hwVersion="TEST2" swVersion="TEST3"> <behaviour api="org.onosproject.net.behaviour.Pipeliner" - impl="org.onosproject.driver.pipeline.OLTPipeline"/> + impl="org.onosproject.driver.pipeline.OltPipeline"/> </driver> <!-- The SoftRouter driver is meant to be used by any software/NPU based ~ switch that wishes to implement a simple 2-table router. To use this @@ -124,7 +131,7 @@ <behaviour api="org.onosproject.openflow.controller.driver.OpenFlowSwitchDriver" impl="org.onosproject.driver.handshaker.CalientFiberSwitchHandshaker"/> </driver> - <driver name="onosfw" extends="default" + <driver name="onosfw" extends="ovs" manufacturer="" hwVersion="" swVersion=""> <behaviour api="org.onosproject.net.behaviour.Pipeliner" impl="org.onosproject.driver.pipeline.OpenVSwitchPipeline"/> @@ -134,5 +141,8 @@ <behaviour api="org.onosproject.openflow.controller.driver.OpenFlowSwitchDriver" impl="org.onosproject.driver.handshaker.OFOpticalSwitch13"/> </driver> + <driver name="aos" extends="ofdpa" + manufacturer="Accton" hwVersion=".*" swVersion="1.*"> + </driver> </drivers> |