| /* |
| * Copyright 2016-present 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.ofdpa; |
| |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.ImmutableSet; |
| import org.onlab.packet.Ethernet; |
| import org.onlab.packet.IpPrefix; |
| import org.onlab.packet.VlanId; |
| import org.onosproject.core.ApplicationId; |
| import org.onosproject.core.GroupId; |
| import org.onosproject.net.Port; |
| import org.onosproject.net.PortNumber; |
| import org.onosproject.net.behaviour.NextGroup; |
| import org.onosproject.net.behaviour.PipelinerContext; |
| 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.TrafficSelector; |
| import org.onosproject.net.flow.TrafficTreatment; |
| import org.onosproject.net.flow.criteria.Criteria; |
| import org.onosproject.net.flow.criteria.Criterion; |
| 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.Icmpv6CodeCriterion; |
| import org.onosproject.net.flow.criteria.Icmpv6TypeCriterion; |
| import org.onosproject.net.flow.criteria.MplsBosCriterion; |
| import org.onosproject.net.flow.criteria.MplsCriterion; |
| 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.L3ModificationInstruction; |
| import org.onosproject.net.flowobjective.ForwardingObjective; |
| import org.onosproject.net.flowobjective.ObjectiveError; |
| import org.onosproject.net.group.DefaultGroupBucket; |
| import org.onosproject.net.group.DefaultGroupDescription; |
| import org.onosproject.net.group.DefaultGroupKey; |
| import org.onosproject.net.group.Group; |
| import org.onosproject.net.group.GroupBucket; |
| import org.onosproject.net.group.GroupBuckets; |
| import org.onosproject.net.group.GroupDescription; |
| import org.onosproject.net.group.GroupKey; |
| import org.onosproject.net.packet.PacketPriority; |
| import org.slf4j.Logger; |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.Deque; |
| import java.util.List; |
| import java.util.Objects; |
| |
| import static org.onlab.packet.IPv6.PROTOCOL_ICMP6; |
| import static org.onlab.packet.MacAddress.BROADCAST; |
| import static org.onlab.packet.MacAddress.NONE; |
| import static org.onosproject.driver.pipeline.ofdpa.OfdpaGroupHandlerUtility.*; |
| import static org.slf4j.LoggerFactory.getLogger; |
| |
| |
| /** |
| * Driver for software switch emulation of the OFDPA pipeline. |
| * The software switch is the CPqD OF 1.3 switch. Unfortunately the CPqD switch |
| * does not handle vlan tags and mpls labels simultaneously, which requires us |
| * to do some workarounds in the driver. This driver is meant for the use of |
| * the cpqd switch when MPLS is required. As a result this driver works only |
| * on incoming untagged packets. |
| */ |
| public class CpqdOfdpa2Pipeline extends Ofdpa2Pipeline { |
| |
| private final Logger log = getLogger(getClass()); |
| |
| /** |
| * Table that determines whether VLAN is popped before punting to controller. |
| * <p> |
| * This is a non-OFDPA table to emulate OFDPA packet in behavior. |
| * VLAN will be popped before punting if the VLAN is internally assigned. |
| * <p> |
| * Also note that 63 is the max table number in CpqD. |
| */ |
| private static final int PUNT_TABLE = 63; |
| |
| /** |
| * A static indirect group that pop vlan and punt to controller. |
| * <p> |
| * The purpose of using a group instead of immediate action is that this |
| * won't affect another copy on the data plane when write action exists. |
| */ |
| private static final int POP_VLAN_PUNT_GROUP_ID = 0xc0000000; |
| |
| @Override |
| protected boolean requireVlanExtensions() { |
| return false; |
| } |
| |
| /** |
| * Determines whether this pipeline support copy ttl instructions or not. |
| * |
| * @return true if copy ttl instructions are supported |
| */ |
| protected boolean supportCopyTtl() { |
| return true; |
| } |
| |
| /** |
| * Determines whether this pipeline support push mpls to vlan-tagged packets or not. |
| * <p> |
| * If not support, pop vlan before push entering unicast and mpls table. |
| * Side effect: HostService learns redundant hosts with same MAC but |
| * different VLAN. No known side effect on the network reachability. |
| * |
| * @return true if push mpls to vlan-tagged packets is supported |
| */ |
| protected boolean supportTaggedMpls() { |
| return false; |
| } |
| |
| /** |
| * Determines whether this pipeline support punt action in group bucket. |
| * |
| * @return true if punt action in group bucket is supported |
| */ |
| protected boolean supportPuntGroup() { |
| return false; |
| } |
| |
| @Override |
| protected void initDriverId() { |
| driverId = coreService.registerApplication( |
| "org.onosproject.driver.CpqdOfdpa2Pipeline"); |
| } |
| |
| @Override |
| protected void initGroupHander(PipelinerContext context) { |
| groupHandler = new CpqdOfdpa2GroupHandler(); |
| groupHandler.init(deviceId, context); |
| } |
| |
| /* |
| * Cpqd emulation does not require the non OF-standard rules for |
| * matching untagged packets that ofdpa uses. |
| * |
| * (non-Javadoc) |
| * @see org.onosproject.driver.pipeline.OFDPA2Pipeline#processVlanIdFilter |
| */ |
| @Override |
| protected List<FlowRule> processVlanIdFilter(PortCriterion portCriterion, |
| VlanIdCriterion vidCriterion, |
| VlanId assignedVlan, |
| ApplicationId applicationId) { |
| List<FlowRule> rules = new ArrayList<>(); |
| TrafficSelector.Builder selector = DefaultTrafficSelector.builder(); |
| TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder(); |
| selector.matchVlanId(vidCriterion.vlanId()); |
| treatment.transition(TMAC_TABLE); |
| |
| if (vidCriterion.vlanId() == VlanId.NONE) { |
| // untagged packets are assigned vlans |
| treatment.pushVlan().setVlanId(assignedVlan); |
| } |
| |
| // 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) { |
| // NOTE: Emulating OFDPA behavior by popping off internal assigned |
| // VLAN before sending to controller |
| if (supportPuntGroup() && vidCriterion.vlanId() == VlanId.NONE) { |
| GroupKey groupKey = popVlanPuntGroupKey(); |
| Group group = groupService.getGroup(deviceId, groupKey); |
| if (group != null) { |
| rules.add(buildPuntTableRule(pnum, assignedVlan)); |
| } else { |
| log.info("popVlanPuntGroup not found in dev:{}", deviceId); |
| return Collections.emptyList(); |
| } |
| } |
| |
| // 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; |
| } |
| |
| /** |
| * Creates punt table entry that matches IN_PORT and VLAN_VID and points to |
| * a group that pop vlan and punt. |
| * |
| * @param portNumber port number |
| * @param assignedVlan internally assigned vlan id |
| * @return punt table flow rule |
| */ |
| private FlowRule buildPuntTableRule(PortNumber portNumber, VlanId assignedVlan) { |
| TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder() |
| .matchInPort(portNumber) |
| .matchVlanId(assignedVlan); |
| TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder() |
| .group(new GroupId(POP_VLAN_PUNT_GROUP_ID)); |
| |
| return DefaultFlowRule.builder() |
| .forDevice(deviceId) |
| .withSelector(sbuilder.build()) |
| .withTreatment(tbuilder.build()) |
| .withPriority(PacketPriority.CONTROL.priorityValue()) |
| .fromApp(driverId) |
| .makePermanent() |
| .forTable(PUNT_TABLE).build(); |
| } |
| |
| /** |
| * Builds a punt to the controller rule for the arp protocol. |
| * <p> |
| * NOTE: CpqD cannot punt correctly in group bucket. The current impl will |
| * pop VLAN before sending to controller disregarding whether |
| * it's an internally assigned VLAN or a natural VLAN. |
| * Therefore, trunk port is not supported in CpqD. |
| * |
| * @param assignedVlan the internal assigned vlan id |
| * @param applicationId the application id |
| * @return the punt flow rule for the arp |
| */ |
| private FlowRule buildArpPunt(VlanId assignedVlan, ApplicationId applicationId) { |
| TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder() |
| .matchEthType(Ethernet.TYPE_ARP) |
| .matchVlanId(assignedVlan); |
| TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder() |
| .popVlan() |
| .punt(); |
| |
| return DefaultFlowRule.builder() |
| .forDevice(deviceId) |
| .withSelector(sbuilder.build()) |
| .withTreatment(tbuilder.build()) |
| .withPriority(PacketPriority.CONTROL.priorityValue() + 1) |
| .fromApp(applicationId) |
| .makePermanent() |
| .forTable(ACL_TABLE).build(); |
| } |
| |
| /** |
| * Builds a punt to the controller rule for the icmp v6 messages. |
| * <p> |
| * NOTE: CpqD cannot punt correctly in group bucket. The current impl will |
| * pop VLAN before sending to controller disregarding whether |
| * it's an internally assigned VLAN or a natural VLAN. |
| * Therefore, trunk port is not supported in CpqD. |
| * |
| * @param assignedVlan the internal assigned vlan id |
| * @param applicationId the application id |
| * @return the punt flow rule for the icmp v6 messages |
| */ |
| private FlowRule buildIcmpV6Punt(VlanId assignedVlan, ApplicationId applicationId) { |
| TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder() |
| .matchVlanId(assignedVlan) |
| .matchEthType(Ethernet.TYPE_IPV6) |
| .matchIPProtocol(PROTOCOL_ICMP6); |
| TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder() |
| .popVlan() |
| .punt(); |
| |
| return DefaultFlowRule.builder() |
| .forDevice(deviceId) |
| .withSelector(sbuilder.build()) |
| .withTreatment(tbuilder.build()) |
| .withPriority(PacketPriority.CONTROL.priorityValue() + 1) |
| .fromApp(applicationId) |
| .makePermanent() |
| .forTable(ACL_TABLE).build(); |
| } |
| |
| /* |
| * Cpqd emulation does not handle vlan tags and mpls labels correctly. |
| * Workaround requires popping off the VLAN tags in the TMAC table. |
| * |
| * (non-Javadoc) |
| * @see org.onosproject.driver.pipeline.OFDPA2Pipeline#processEthDstFilter |
| */ |
| @Override |
| protected List<FlowRule> processEthDstFilter(PortCriterion portCriterion, |
| EthCriterion ethCriterion, |
| VlanIdCriterion vidCriterion, |
| VlanId assignedVlan, |
| ApplicationId applicationId) { |
| // Consider PortNumber.ANY as wildcard. Match ETH_DST only |
| if (portCriterion != null && portCriterion.port() == PortNumber.ANY) { |
| return processEthDstOnlyFilter(ethCriterion, applicationId); |
| } |
| |
| // Multicast MAC |
| if (ethCriterion.mask() != null) { |
| return processMcastEthDstFilter(ethCriterion, applicationId); |
| } |
| |
| //handling untagged packets via assigned VLAN |
| if (vidCriterion.vlanId() == VlanId.NONE) { |
| vidCriterion = (VlanIdCriterion) Criteria.matchVlanId(assignedVlan); |
| } |
| // 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()); |
| } |
| |
| List<FlowRule> rules = new ArrayList<FlowRule>(); |
| for (PortNumber pnum : portnums) { |
| // TMAC rules for unicast IP packets |
| TrafficSelector.Builder selector = DefaultTrafficSelector.builder(); |
| TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder(); |
| selector.matchInPort(pnum); |
| selector.matchVlanId(vidCriterion.vlanId()); |
| selector.matchEthType(Ethernet.TYPE_IPV4); |
| selector.matchEthDst(ethCriterion.mac()); |
| if (!supportTaggedMpls()) { |
| treatment.popVlan(); |
| } |
| treatment.transition(UNICAST_ROUTING_TABLE); |
| FlowRule rule = DefaultFlowRule.builder() |
| .forDevice(deviceId) |
| .withSelector(selector.build()) |
| .withTreatment(treatment.build()) |
| .withPriority(DEFAULT_PRIORITY) |
| .fromApp(applicationId) |
| .makePermanent() |
| .forTable(TMAC_TABLE).build(); |
| rules.add(rule); |
| |
| // TMAC rules for MPLS packets |
| selector = DefaultTrafficSelector.builder(); |
| treatment = DefaultTrafficTreatment.builder(); |
| selector.matchInPort(pnum); |
| selector.matchVlanId(vidCriterion.vlanId()); |
| selector.matchEthType(Ethernet.MPLS_UNICAST); |
| selector.matchEthDst(ethCriterion.mac()); |
| if (!supportTaggedMpls()) { |
| treatment.popVlan(); |
| } |
| treatment.transition(MPLS_TABLE_0); |
| rule = DefaultFlowRule.builder() |
| .forDevice(deviceId) |
| .withSelector(selector.build()) |
| .withTreatment(treatment.build()) |
| .withPriority(DEFAULT_PRIORITY) |
| .fromApp(applicationId) |
| .makePermanent() |
| .forTable(TMAC_TABLE).build(); |
| rules.add(rule); |
| |
| // TMAC rules for IPv6 packets |
| selector = DefaultTrafficSelector.builder(); |
| treatment = DefaultTrafficTreatment.builder(); |
| selector.matchInPort(pnum); |
| selector.matchVlanId(vidCriterion.vlanId()); |
| selector.matchEthType(Ethernet.TYPE_IPV6); |
| selector.matchEthDst(ethCriterion.mac()); |
| if (!supportTaggedMpls()) { |
| treatment.popVlan(); |
| } |
| treatment.transition(UNICAST_ROUTING_TABLE); |
| rule = DefaultFlowRule.builder() |
| .forDevice(deviceId) |
| .withSelector(selector.build()) |
| .withTreatment(treatment.build()) |
| .withPriority(DEFAULT_PRIORITY) |
| .fromApp(applicationId) |
| .makePermanent() |
| .forTable(TMAC_TABLE).build(); |
| rules.add(rule); |
| } |
| return rules; |
| } |
| |
| @Override |
| protected List<FlowRule> processEthDstOnlyFilter(EthCriterion ethCriterion, |
| ApplicationId applicationId) { |
| TrafficSelector.Builder selector = DefaultTrafficSelector.builder(); |
| TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder(); |
| selector.matchEthType(Ethernet.TYPE_IPV4); |
| selector.matchEthDst(ethCriterion.mac()); |
| if (!supportTaggedMpls()) { |
| treatment.popVlan(); |
| } |
| treatment.transition(UNICAST_ROUTING_TABLE); |
| FlowRule rule = DefaultFlowRule.builder() |
| .forDevice(deviceId) |
| .withSelector(selector.build()) |
| .withTreatment(treatment.build()) |
| .withPriority(DEFAULT_PRIORITY) |
| .fromApp(applicationId) |
| .makePermanent() |
| .forTable(TMAC_TABLE).build(); |
| return ImmutableList.<FlowRule>builder().add(rule).build(); |
| } |
| |
| /* |
| * Cpqd emulation allows MPLS ecmp. |
| * |
| * (non-Javadoc) |
| * @see org.onosproject.driver.pipeline.OFDPA2Pipeline#processEthTypeSpecific |
| */ |
| @Override |
| protected Collection<FlowRule> processEthTypeSpecific(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_IPV6)) { |
| log.warn("processSpecific: Unsupported forwarding objective criteria" |
| + "ethType:{} in dev:{}", ethType, deviceId); |
| fail(fwd, ObjectiveError.UNSUPPORTED); |
| return Collections.emptySet(); |
| } |
| boolean defaultRule = false; |
| int forTableId = -1; |
| TrafficSelector.Builder filteredSelector = DefaultTrafficSelector.builder(); |
| TrafficSelector.Builder complementarySelector = DefaultTrafficSelector.builder(); |
| |
| if (ethType.ethType().toShort() == Ethernet.TYPE_IPV4) { |
| IpPrefix ipv4Dst = ((IPCriterion) selector.getCriterion(Criterion.Type.IPV4_DST)).ip(); |
| if (ipv4Dst.isMulticast()) { |
| if (ipv4Dst.prefixLength() != 32) { |
| log.warn("Multicast specific forwarding objective can only be /32"); |
| fail(fwd, ObjectiveError.BADPARAMS); |
| return ImmutableSet.of(); |
| } |
| VlanId assignedVlan = readVlanFromSelector(fwd.meta()); |
| if (assignedVlan == null) { |
| log.warn("VLAN ID required by multicast specific fwd obj is missing. Abort."); |
| fail(fwd, ObjectiveError.BADPARAMS); |
| return ImmutableSet.of(); |
| } |
| filteredSelector.matchVlanId(assignedVlan); |
| filteredSelector.matchEthType(Ethernet.TYPE_IPV4).matchIPDst(ipv4Dst); |
| forTableId = MULTICAST_ROUTING_TABLE; |
| log.debug("processing IPv4 multicast specific forwarding objective {} -> next:{}" |
| + " in dev:{}", fwd.id(), fwd.nextId(), deviceId); |
| } else { |
| if (ipv4Dst.prefixLength() == 0) { |
| // The entire IPV4_DST field is wildcarded intentionally |
| filteredSelector.matchEthType(Ethernet.TYPE_IPV4); |
| } else { |
| filteredSelector.matchEthType(Ethernet.TYPE_IPV4).matchIPDst(ipv4Dst); |
| } |
| forTableId = UNICAST_ROUTING_TABLE; |
| log.debug("processing IPv4 unicast specific forwarding objective {} -> next:{}" |
| + " in dev:{}", fwd.id(), fwd.nextId(), deviceId); |
| } |
| } else if (ethType.ethType().toShort() == Ethernet.TYPE_IPV6) { |
| if (buildIpv6Selector(filteredSelector, fwd) < 0) { |
| return Collections.emptyList(); |
| } |
| forTableId = UNICAST_ROUTING_TABLE; |
| } else { |
| filteredSelector |
| .matchEthType(Ethernet.MPLS_UNICAST) |
| .matchMplsLabel(((MplsCriterion) |
| selector.getCriterion(Criterion.Type.MPLS_LABEL)).label()); |
| MplsBosCriterion bos = (MplsBosCriterion) selector |
| .getCriterion(Criterion.Type.MPLS_BOS); |
| if (bos != null) { |
| filteredSelector.matchMplsBos(bos.mplsBos()); |
| } |
| forTableId = MPLS_TABLE_1; |
| log.debug("processing MPLS specific forwarding objective {} -> next:{}" |
| + " in dev {}", fwd.id(), fwd.nextId(), deviceId); |
| } |
| |
| TrafficTreatment.Builder tb = DefaultTrafficTreatment.builder(); |
| if (fwd.treatment() != null) { |
| for (Instruction i : fwd.treatment().allInstructions()) { |
| if (!supportCopyTtl() && i instanceof L3ModificationInstruction) { |
| L3ModificationInstruction l3instr = (L3ModificationInstruction) i; |
| if (l3instr.subtype().equals(L3ModificationInstruction.L3SubType.TTL_IN) || |
| l3instr.subtype().equals(L3ModificationInstruction.L3SubType.TTL_OUT)) { |
| continue; |
| } |
| } |
| /* |
| * NOTE: OF-DPA does not support immediate instruction in |
| * L3 unicast and MPLS table. |
| */ |
| tb.deferred().add(i); |
| } |
| } |
| |
| if (fwd.nextId() != null) { |
| NextGroup next = getGroupForNextObjective(fwd.nextId()); |
| if (next != null) { |
| List<Deque<GroupKey>> gkeys = appKryo.deserialize(next.data()); |
| // we only need the top level group's key to point the flow to it |
| Group group = groupService.getGroup(deviceId, gkeys.get(0).peekFirst()); |
| if (group == null) { |
| log.warn("Group with key:{} for next-id:{} not found in dev:{}", |
| gkeys.get(0).peekFirst(), fwd.nextId(), deviceId); |
| fail(fwd, ObjectiveError.GROUPMISSING); |
| return Collections.emptySet(); |
| } |
| tb.deferred().group(group.id()); |
| } |
| } |
| tb.transition(ACL_TABLE); |
| FlowRule.Builder ruleBuilder = DefaultFlowRule.builder() |
| .fromApp(fwd.appId()) |
| .withPriority(fwd.priority()) |
| .forDevice(deviceId) |
| .withSelector(filteredSelector.build()) |
| .withTreatment(tb.build()) |
| .forTable(forTableId); |
| |
| if (fwd.permanent()) { |
| ruleBuilder.makePermanent(); |
| } else { |
| ruleBuilder.makeTemporary(fwd.timeout()); |
| } |
| Collection<FlowRule> flowRuleCollection = new ArrayList<>(); |
| flowRuleCollection.add(ruleBuilder.build()); |
| if (defaultRule) { |
| flowRuleCollection.add( |
| defaultRoute(fwd, complementarySelector, forTableId, tb) |
| ); |
| log.debug("Default rule 0.0.0.0/0 is being installed two rules"); |
| } |
| return flowRuleCollection; |
| } |
| |
| @Override |
| protected Collection<FlowRule> processEthDstSpecific(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); |
| |
| if (vlanIdCriterion == null) { |
| log.warn("Forwarding objective for bridging requires vlan. Not " |
| + "installing fwd:{} in dev:{}", fwd.id(), deviceId); |
| fail(fwd, ObjectiveError.BADPARAMS); |
| return Collections.emptySet(); |
| } |
| |
| TrafficSelector.Builder filteredSelectorBuilder = |
| DefaultTrafficSelector.builder(); |
| // Do not match MacAddress for subnet broadcast entry |
| if (!ethCriterion.mac().equals(NONE) && !ethCriterion.mac().equals(BROADCAST)) { |
| filteredSelectorBuilder.matchEthDst(ethCriterion.mac()); |
| log.debug("processing L2 forwarding objective:{} -> next:{} in dev:{}", |
| fwd.id(), fwd.nextId(), deviceId); |
| } else { |
| log.debug("processing L2 Broadcast forwarding objective:{} -> next:{} " |
| + "in dev:{} for vlan:{}", |
| fwd.id(), fwd.nextId(), deviceId, vlanIdCriterion.vlanId()); |
| } |
| filteredSelectorBuilder.matchVlanId(vlanIdCriterion.vlanId()); |
| TrafficSelector filteredSelector = filteredSelectorBuilder.build(); |
| |
| if (fwd.treatment() != null) { |
| log.warn("Ignoring traffic treatment in fwd rule {} meant for L2 table" |
| + "for dev:{}. Expecting only nextId", fwd.id(), deviceId); |
| } |
| |
| TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder(); |
| if (fwd.nextId() != null) { |
| NextGroup next = getGroupForNextObjective(fwd.nextId()); |
| if (next != null) { |
| List<Deque<GroupKey>> gkeys = appKryo.deserialize(next.data()); |
| // we only need the top level group's key to point the flow to it |
| Group group = groupService.getGroup(deviceId, gkeys.get(0).peekFirst()); |
| if (group != null) { |
| treatmentBuilder.deferred().group(group.id()); |
| } else { |
| log.warn("Group with key:{} for next-id:{} not found in dev:{}", |
| gkeys.get(0).peekFirst(), fwd.nextId(), deviceId); |
| fail(fwd, ObjectiveError.GROUPMISSING); |
| return Collections.emptySet(); |
| } |
| } |
| } |
| treatmentBuilder.immediate().transition(ACL_TABLE); |
| 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(BRIDGING_TABLE); |
| if (fwd.permanent()) { |
| flowRuleBuilder.makePermanent(); |
| } else { |
| flowRuleBuilder.makeTemporary(fwd.timeout()); |
| } |
| rules.add(flowRuleBuilder.build()); |
| return rules; |
| } |
| |
| /* |
| * In the OF-DPA 2.0 pipeline, versatile forwarding objectives go to the |
| * ACL table. Because we pop off vlan tags in TMAC table, |
| * we need to avoid matching on vlans in the ACL table. |
| */ |
| @Override |
| protected Collection<FlowRule> processVersatile(ForwardingObjective fwd) { |
| log.info("Processing versatile forwarding objective"); |
| |
| EthTypeCriterion ethType = |
| (EthTypeCriterion) fwd.selector().getCriterion(Criterion.Type.ETH_TYPE); |
| if (ethType == null) { |
| log.error("Versatile forwarding objective must include ethType"); |
| fail(fwd, ObjectiveError.BADPARAMS); |
| return Collections.emptySet(); |
| } |
| if (fwd.nextId() == null && fwd.treatment() == null) { |
| log.error("Forwarding objective {} from {} must contain " |
| + "nextId or Treatment", fwd.selector(), fwd.appId()); |
| return Collections.emptySet(); |
| } |
| |
| TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder(); |
| fwd.selector().criteria().forEach(criterion -> { |
| if (criterion instanceof VlanIdCriterion) { |
| // avoid matching on vlans |
| return; |
| } else if (criterion instanceof Icmpv6TypeCriterion || |
| criterion instanceof Icmpv6CodeCriterion) { |
| /* |
| * We silenty discard these criterions, our current |
| * OFDPA platform does not support these matches on |
| * the ACL table. |
| */ |
| log.warn("ICMPv6 Type and ICMPv6 Code are not supported"); |
| } else { |
| sbuilder.add(criterion); |
| } |
| }); |
| |
| // XXX driver does not currently do type checking as per Tables 65-67 in |
| // OFDPA 2.0 spec. The only allowed treatment is a punt to the controller. |
| TrafficTreatment.Builder ttBuilder = DefaultTrafficTreatment.builder(); |
| if (fwd.treatment() != null) { |
| for (Instruction ins : fwd.treatment().allInstructions()) { |
| if (ins instanceof OutputInstruction) { |
| OutputInstruction o = (OutputInstruction) ins; |
| if (o.port() == PortNumber.CONTROLLER) { |
| ttBuilder.transition(PUNT_TABLE); |
| } else { |
| log.warn("Only allowed treatments in versatile forwarding " |
| + "objectives are punts to the controller"); |
| } |
| } else { |
| log.warn("Cannot process instruction in versatile fwd {}", ins); |
| } |
| } |
| if (fwd.treatment().clearedDeferred()) { |
| ttBuilder.wipeDeferred(); |
| } |
| } |
| if (fwd.nextId() != null) { |
| // overide case |
| NextGroup next = getGroupForNextObjective(fwd.nextId()); |
| List<Deque<GroupKey>> gkeys = appKryo.deserialize(next.data()); |
| // we only need the top level group's key to point the flow to it |
| Group group = groupService.getGroup(deviceId, gkeys.get(0).peekFirst()); |
| if (group == null) { |
| log.warn("Group with key:{} for next-id:{} not found in dev:{}", |
| gkeys.get(0).peekFirst(), fwd.nextId(), deviceId); |
| fail(fwd, ObjectiveError.GROUPMISSING); |
| return Collections.emptySet(); |
| } |
| ttBuilder.deferred().group(group.id()); |
| } |
| |
| FlowRule.Builder ruleBuilder = DefaultFlowRule.builder() |
| .fromApp(fwd.appId()) |
| .withPriority(fwd.priority()) |
| .forDevice(deviceId) |
| .withSelector(sbuilder.build()) |
| .withTreatment(ttBuilder.build()) |
| .makePermanent() |
| .forTable(ACL_TABLE); |
| return Collections.singletonList(ruleBuilder.build()); |
| } |
| |
| /* |
| * Cpqd emulation requires table-miss-entries in forwarding tables. |
| * Real OFDPA does not require these rules as they are put in by default. |
| * |
| * (non-Javadoc) |
| * @see org.onosproject.driver.pipeline.OFDPA2Pipeline#initializePipeline() |
| */ |
| @Override |
| protected void initializePipeline() { |
| initTableMiss(PORT_TABLE, VLAN_TABLE, null); |
| initTableMiss(VLAN_TABLE, ACL_TABLE, null); |
| initTableMiss(TMAC_TABLE, BRIDGING_TABLE, null); |
| initTableMiss(UNICAST_ROUTING_TABLE, ACL_TABLE, null); |
| initTableMiss(MULTICAST_ROUTING_TABLE, ACL_TABLE, null); |
| initTableMiss(MPLS_TABLE_0, MPLS_TABLE_1, null); |
| initTableMiss(MPLS_TABLE_1, ACL_TABLE, null); |
| initTableMiss(BRIDGING_TABLE, ACL_TABLE, null); |
| initTableMiss(ACL_TABLE, -1, null); |
| |
| if (supportPuntGroup()) { |
| initTableMiss(PUNT_TABLE, -1, |
| DefaultTrafficTreatment.builder().punt().build()); |
| initPopVlanPuntGroup(); |
| } else { |
| initTableMiss(PUNT_TABLE, -1, |
| DefaultTrafficTreatment.builder().popVlan().punt().build()); |
| } |
| } |
| |
| /** |
| * Install table-miss flow entry. |
| * |
| * If treatment exists, use it directly. |
| * Else if treatment does not exist but nextTable > 0, transit to next table. |
| * Else apply empty treatment. |
| * |
| * @param thisTable this table ID |
| * @param nextTable next table ID |
| * @param treatment traffic treatment to apply. |
| */ |
| private void initTableMiss(int thisTable, int nextTable, TrafficTreatment treatment) { |
| FlowRuleOperations.Builder ops = FlowRuleOperations.builder(); |
| TrafficSelector selector = DefaultTrafficSelector.builder().build(); |
| |
| if (treatment == null) { |
| TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder(); |
| if (nextTable > 0) { |
| tBuilder.transition(nextTable); |
| } |
| treatment = tBuilder.build(); |
| } |
| |
| FlowRule rule = DefaultFlowRule.builder() |
| .forDevice(deviceId) |
| .withSelector(selector) |
| .withTreatment(treatment) |
| .withPriority(LOWEST_PRIORITY) |
| .fromApp(driverId) |
| .makePermanent() |
| .forTable(thisTable).build(); |
| ops = ops.add(rule); |
| |
| flowRuleService.apply(ops.build(new FlowRuleOperationsContext() { |
| @Override |
| public void onSuccess(FlowRuleOperations ops) { |
| log.info("Initialized table {} on {}", thisTable, deviceId); |
| } |
| @Override |
| public void onError(FlowRuleOperations ops) { |
| log.warn("Failed to initialize table {} on {}", thisTable, deviceId); |
| } |
| })); |
| } |
| |
| /** |
| * Builds a indirect group contains pop_vlan and punt actions. |
| * <p> |
| * Using group instead of immediate action to ensure that |
| * the copy of packet on the data plane is not affected by the pop vlan action. |
| */ |
| private void initPopVlanPuntGroup() { |
| GroupKey groupKey = popVlanPuntGroupKey(); |
| TrafficTreatment bucketTreatment = DefaultTrafficTreatment.builder() |
| .popVlan().punt().build(); |
| GroupBucket bucket = |
| DefaultGroupBucket.createIndirectGroupBucket(bucketTreatment); |
| GroupDescription groupDesc = |
| new DefaultGroupDescription( |
| deviceId, |
| GroupDescription.Type.INDIRECT, |
| new GroupBuckets(Collections.singletonList(bucket)), |
| groupKey, |
| POP_VLAN_PUNT_GROUP_ID, |
| driverId); |
| groupService.addGroup(groupDesc); |
| |
| log.info("Initialized pop vlan punt group on {}", deviceId); |
| } |
| |
| /** |
| * Generates group key for a static indirect group that pop vlan and punt to |
| * controller. |
| * |
| * @return the group key of the indirect table |
| */ |
| private GroupKey popVlanPuntGroupKey() { |
| int hash = POP_VLAN_PUNT_GROUP_ID | (Objects.hash(deviceId) & FOUR_BIT_MASK); |
| return new DefaultGroupKey(Ofdpa2Pipeline.appKryo.serialize(hash)); |
| } |
| } |