package net.floodlightcontroller.util;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import javax.annotation.Nonnull;
import net.floodlightcontroller.core.IOFSwitch;
import org.projectfloodlight.openflow.protocol.OFBucket;
import org.projectfloodlight.openflow.protocol.OFFlowAdd;
import org.projectfloodlight.openflow.protocol.OFGroupAdd;
import org.projectfloodlight.openflow.protocol.OFGroupType;
import org.projectfloodlight.openflow.protocol.OFVersion;
import org.projectfloodlight.openflow.protocol.action.OFAction;
import org.projectfloodlight.openflow.protocol.instruction.OFInstruction;
import org.projectfloodlight.openflow.protocol.match.Match;
import org.projectfloodlight.openflow.protocol.match.MatchField;
import org.projectfloodlight.openflow.protocol.match.MatchFields;
import org.projectfloodlight.openflow.types.OFBufferId;
import org.projectfloodlight.openflow.types.OFGroup;
import org.projectfloodlight.openflow.types.OFPort;
import org.projectfloodlight.openflow.types.OFVlanVidMatch;
import org.projectfloodlight.openflow.types.TableId;
import org.projectfloodlight.openflow.types.U16;
import org.projectfloodlight.openflow.types.U32;
import org.projectfloodlight.openflow.types.U64;
import org.projectfloodlight.openflow.types.VlanVid;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The Broadcom OF-DPA 2.0 specification provides an interesting
* packet processing pipeline. Most OpenFlow programmers are
* accustomed to programming Open vSwitch or other software switches,
* where much if not all of the OpenFlow specification is supported.
*
* Hardware switch vendors wishing to support OpenFlow typically mimic
* software switches and allow programmers access to uniform flow
* tables, where the switch will automatically move operations not
* supported in hardware to software/the CPU, also known as the
* "slow path." In order to operate on packets in hardware, switch
* vendors need to expose some of the low-level details, features, and
* shortcomings to the programmer. That is where OF-DPA comes in.
*
* On compatible Broadcom-based switches, in conjunction with the Indigo
* OpenFlow agent running onboard the switch, OpenFlow controllers can
* leverage complete hardware forwarding of packets in the data plane.
* This does not necessarily include the ability to perform all actions
* and matches in hardware; however, it increases the available set of
* actions and matches and give the controller access to powerful
* features onboard the switch, such as routing and tunneling.
*
* OFDPAUtils provides an abstraction when programming a Broadcom
* OF-DPA switch in order to ease conformance to the OF-DPA 2.0 spec
* from the controller and modules.
*
* @author Ryan Izard, ryan.izard@bigswitch.com, rizard@g.clemson.edu
*/
public class OFDPAUtils {
private OFDPAUtils() {}
private static final Logger log = LoggerFactory.getLogger(OFDPAUtils.class);
/* These cannot be changed, since they're final (public == okay for the ones we want to inform about) */
public static final int PRIORITY = 1000;
public static final int DLF_PRIORITY = 0;
public static final int HARD_TIMEOUT = 0;
public static final int IDLE_TIMEOUT = 0;
public static final U64 APP_COOKIE = U64.of(Long.parseLong("00FFDDBBAA", 16)); /* OF-DPA sub B for P :-) */
private static class OFDPAGroupType {
private static final int L2_INTERFACE = 0; /* 0 */
private static final int L2_REWRITE = 1; /* 1 */
private static final int L3_UNICAST = 2; /* 2 */
private static final int L2_MULTICAST = 3; /* 3 */
private static final int L2_FLOOD = 4; /* 4 */
private static final int L3_INTERFACE = 5; /* 5 */
private static final int L3_MULTICAST = 6; /* 6 */
private static final int L3_ECMP = 7; /* 7 */
private static final int L2_DATA_CENTER_OVERLAY = 8; /* 8 */
private static final int MPLS_LABEL = 9; /* 9 */
private static final int MPLS_FORWARDING = 10; /* 10 */
private static final int L2_UNFILTERED_INTERFACE = 11; /* 11 */
private static final int L2_LOOPBACK = 12; /* 12 */
}
private static class L2OverlaySubType {
private static final int L2_OVERLAY_FLOOD_OVER_UNICAST_TUNNELS = 0;
private static final int L2_OVERLAY_FLOOD_OVER_MULTICAST_TUNNELS = 1;
private static final int L2_OVERLAY_MULTICAST_OVER_UNICAST_TUNNELS = 2;
private static final int L2_OVERLAY_MULTICAST_OVER_MULTICAST_TUNNELS = 3;
}
private static class MPLSSubType {
private static final int MPLS_INTERFACE = 0;
private static final int MPLS_L2_VPN_LABEL = 1;
private static final int MPLS_L3_VPN_LABEL = 2;
private static final int MPLS_TUNNEL_LABEL_1 = 3;
private static final int MPLS_TUNNEL_LABEL_2 = 4;
private static final int MPLS_SWAP_LABEL = 5;
private static final int MPLS_FAST_FAILOVER = 6;
private static final int MPLS_ECMP = 8;
private static final int MPLS_L2_TAG = 10;
}
public static class Tables {
public static final TableId INGRESS_PORT = TableId.of(0);
public static final TableId VLAN = TableId.of(10);
public static final TableId TERMINATION_MAC = TableId.of(20);
public static final TableId UNICAST_ROUTING = TableId.of(30);
public static final TableId MULITCAST_ROUTING = TableId.of(40);
public static final TableId BRIDGING = TableId.of(50);
public static final TableId POLICY_ACL = TableId.of(60);
}
private static final List<MatchFields> ALLOWED_MATCHES =
Collections.unmodifiableList(
Arrays.asList(
MatchFields.IN_PORT,
MatchFields.ETH_SRC,
MatchFields.ETH_DST,
MatchFields.ETH_TYPE,
MatchFields.VLAN_VID,
MatchFields.VLAN_PCP,
MatchFields.TUNNEL_ID,
MatchFields.IP_PROTO,
MatchFields.IPV4_SRC,
MatchFields.IPV4_DST,
MatchFields.IP_DSCP,
MatchFields.IP_ECN,
MatchFields.ARP_SPA,
MatchFields.ICMPV4_CODE,
MatchFields.ICMPV4_TYPE,
MatchFields.IPV6_SRC,
MatchFields.IPV6_DST,
MatchFields.IPV6_FLABEL,
MatchFields.ICMPV6_CODE,
MatchFields.ICMPV6_TYPE,
MatchFields.TCP_SRC,
MatchFields.TCP_DST,
MatchFields.UDP_SRC,
MatchFields.UDP_DST,
MatchFields.SCTP_SRC,
MatchFields.SCTP_DST
)
);
/**
* Determine whether or not the provided switch is an OF-DPA switch.
*
* @param sw, the switch to check
* @return true if the switch is an OF-DPA switch; false otherwise
*/
public static boolean isOFDPASwitch(IOFSwitch s) {
/*
* Many switches might have the same table IDs as an OF-DPA switch,
* so we can't check the IDs only. Alternatively, we can check the
* name, description, etc. to see if it aligns with an OF-DPA-set
* description. This isn't fool-proof, but it'll work for now.
*/
if (s.getSwitchDescription().getSoftwareDescription().toLowerCase().contains("of-dpa")) {
return true;
} else {
return false;
}
}
/**
* Get the MatchFields that an OF-DPA switch supports matching.
* Note that this does not specify match codependencies or
* mutually exclusive matches.
*
* @return, an unmodifiable list of potential MatchFields
*/
public static List<MatchFields> getSupportedMatchFields() {
return ALLOWED_MATCHES;
}
/**
* Examine all the MatchFields in a Match object and pick out
* the MatchFields that are not supported by OF-DPA.
*
* @param m
* @return
*/
public static List<MatchFields> checkMatchFields(Match m) {
List<MatchFields> unsupported = null;
Iterator<MatchField<?>> mfi = m.getMatchFields().iterator();
while (mfi.hasNext()) {
MatchField<?> mf = mfi.next();
if (!getSupportedMatchFields().contains(mf.id)) {
if (unsupported == null) {
unsupported = new ArrayList<MatchFields>();
}
unsupported.add(mf.id);
}
}
return unsupported;
}
/**
* Add the OF-DPA groups and flows necessary to facilitate future forwarding/learning
* switch flows. The switch provided must be an OFDPA 2.0 compliant switch.
* Use VLAN tag of null or VlanVid.ZERO for untagged. VLAN tag 1 may not be used; it is
* reserved as an internal VLAN for untagged ports on no VLAN (like a home switch).
*
* This function will add the flows that permit all packets in the VLAN specified and
* on the ports specified to reach the bridging table, where a DLF flow will forward
* them to the controller if another higher priority L2 flow does not match in the
* bridging table. All ethertypes will match this DLF flow.
*
* Use {@link OFDPAUtils#addLearningSwitchFlow(IOFSwitch, U64, int, int, int, Match, VlanVid, OFPort) this function }
* to insert learning switch flows at a later point based on packets forwarded to the
* controller from the DLF flow.
*
* @param sw
* @param vlan
* @param ports
* @return
*/
public static boolean addLearningSwitchPrereqs(@Nonnull IOFSwitch sw, VlanVid vlan, @Nonnull List<OFPortModeTuple> ports) {
/*
* Both of these must complete. If the first fails, the second will not be executed.
*/
return addLearningSwitchPrereqGroups(sw, vlan, ports) && addLearningSwitchPrereqFlows(sw, vlan, ports);
}
/**
* Note: Prefer {@link OFDPAUtils#addLearningSwitchPrereqs(IOFSwitch, VlanVid, List) this function}
* over this function unless you know what you are doing.
*
* Add the OFDPA groups necessary to facilitate future forwarding/learning
* switch flows. The switch provided must be an OFDPA 2.0 compliant switch.
* If a VLAN tag is provided, it will be expected that all ports in the
* accompanying list of ports are tagged and not access. Use VLAN tag of
* null or VlanVid.ZERO for untagged. VLAN tag 1 may not be used; it is
* reserved as an internal VLAN.
*
* @param sw
* @param vlan
* @param ports
* @return
*/
private static boolean addLearningSwitchPrereqGroups(@Nonnull IOFSwitch sw, VlanVid vlan, @Nonnull List<OFPortModeTuple> ports) {
if (sw == null) {
throw new NullPointerException("Switch cannot be null.");
}
if (vlan == null) {
vlan = VlanVid.ZERO; /* set untagged */
} else if (vlan.equals(VlanVid.ofVlan(1))) {
throw new IllegalArgumentException("VLAN cannot be 1. VLAN 1 is an reserved VLAN for internal use inside the OFDPA switch.");
}
if (ports == null) {
throw new NullPointerException("List of ports cannot be null. Must specify at least 2 valid switch ports.");
} else if (ports.size() < 2) {
throw new IllegalArgumentException("List of ports must contain at least 2 valid switch ports.");
} else {
/* verify ports are valid switch ports */
for (OFPortModeTuple p : ports) {
if (sw.getOFFactory().getVersion().equals(OFVersion.OF_10) && (sw.getPort(p.getPort()) == null || p.getPort().getShortPortNumber() > 0xFF00)) {
throw new IllegalArgumentException("Port " + p.getPort().getPortNumber() + " is not a valid port on switch " + sw.getId().toString());
} else if (!sw.getOFFactory().getVersion().equals(OFVersion.OF_10) && (sw.getPort(p.getPort()) == null || U32.of(p.getPort().getPortNumber()).compareTo(U32.of(0xffFFff00)) != -1)) {
throw new IllegalArgumentException("Port " + p.getPort().getPortNumber() + " is not a valid port on switch " + sw.getId().toString());
}
}
}
/*
* For each output port, add an L2 interface group.
*/
for (OFPortModeTuple p : ports) {
List<OFAction> actions = new ArrayList<OFAction>();
if (vlan.equals(VlanVid.ZERO) || p.getMode() == OFPortMode.ACCESS) { /* if it's untagged (internal=1) or access, pop the tag */
actions.add(sw.getOFFactory().actions().popVlan());
}
actions.add(sw.getOFFactory().actions().output(p.getPort(), 0xffFFffFF));
OFGroupAdd ga = sw.getOFFactory().buildGroupAdd()
.setGroup(GroupIds.createL2Interface(p.getPort(), vlan.equals(VlanVid.ZERO) ? VlanVid.ofVlan(1) : vlan))
.setGroupType(OFGroupType.INDIRECT)
.setBuckets(Collections.singletonList(
sw.getOFFactory().buildBucket()
.setActions(actions)
.build()))
.build();
sw.write(ga);
}
/*
* For the VLAN provided (or internal VLAN 1 if untagged),
* add an L2 flood group.
*/
List<OFBucket> bucketList = new ArrayList<OFBucket>(ports.size());
for (OFPortModeTuple p : ports) {
List<OFAction> actions = new ArrayList<OFAction>();
actions.add(sw.getOFFactory().actions().group(GroupIds.createL2Interface(p.getPort(), vlan.equals(VlanVid.ZERO) ? VlanVid.ofVlan(1) : vlan)));
bucketList.add(sw.getOFFactory().buildBucket().setActions(actions).build());
}
OFGroupAdd ga = sw.getOFFactory().buildGroupAdd() /* use the VLAN ID as the group ID */
.setGroup(GroupIds.createL2Flood(U16.of((vlan.equals(VlanVid.ZERO) ? VlanVid.ofVlan(1) : vlan).getVlan()), vlan.equals(VlanVid.ZERO) ? VlanVid.ofVlan(1) : vlan))
.setGroupType(OFGroupType.ALL)
.setBuckets(bucketList)
.build();
sw.write(ga);
return true;
}
/**
* Note: Prefer {@link OFDPAUtils#addLearningSwitchPrereqs(IOFSwitch, VlanVid, List) this function}
* over this function unless you know what you are doing.
*
* Add the OFDPA flows necessary to facilitate future forwarding/learning
* switch flows. The switch provided must be an OFDPA 2.0 compliant switch.
* If a VLAN tag is provided, it will be expected that all ports in the
* accompanying list of ports are tagged and not access. Use VLAN tag of
* null or VlanVid.ZERO for untagged. VLAN tag 1 may not be used; it is
* reserved as an internal VLAN.
*
* @param sw
* @param vlan
* @param ports
* @return
*/
private static boolean addLearningSwitchPrereqFlows(@Nonnull IOFSwitch sw, VlanVid vlan, @Nonnull List<OFPortModeTuple> ports) {
if (sw == null) {
throw new NullPointerException("Switch cannot be null.");
}
if (vlan == null) {
vlan = VlanVid.ZERO; /* set untagged */
} else if (vlan.equals(VlanVid.ofVlan(1))) {
throw new IllegalArgumentException("VLAN cannot be 1. VLAN 1 is an reserved VLAN for internal use inside the OFDPA switch.");
}
if (ports == null) {
throw new NullPointerException("List of ports cannot be null. Must specify at least 2 valid switch ports.");
} else if (ports.size() < 2) {
throw new IllegalArgumentException("List of ports must contain at least 2 valid switch ports.");
} else {
/* verify ports are valid switch ports */
for (OFPortModeTuple p : ports) {
if (sw.getOFFactory().getVersion().equals(OFVersion.OF_10) && (sw.getPort(p.getPort()) == null || p.getPort().getShortPortNumber() > 0xFF00)) {
throw new IllegalArgumentException("Port " + p.getPort().getPortNumber() + " is not a valid port on switch " + sw.getId().toString());
} else if (!sw.getOFFactory().getVersion().equals(OFVersion.OF_10) && (sw.getPort(p.getPort()) == null || U32.of(p.getPort().getPortNumber()).compareTo(U32.of(0xffFFff00)) != -1)) {
throw new IllegalArgumentException("Port " + p.getPort().getPortNumber() + " is not a valid port on switch " + sw.getId().toString());
}
}
}
/*
* VLAN flow table (10) will drop by default, so we need to
* add a flow that will always direct the packet to the next
* flow table, the termination MAC table (20). If a VLAN tag
* is not present, then we need to append a tag. The tag to
* append will either be the tag specified in outVlan or the
* default tag of 1. Only VLANs are handled in this table.
*/
ArrayList<OFInstruction> instructions = new ArrayList<OFInstruction>();
ArrayList<OFAction> applyActions = new ArrayList<OFAction>();
ArrayList<OFAction> writeActions = new ArrayList<OFAction>();
Match.Builder mb = sw.getOFFactory().buildMatch();
OFFlowAdd.Builder fab = sw.getOFFactory().buildFlowAdd();
/* These are common to all flows for VLAN flow table. */
fab.setBufferId(OFBufferId.NO_BUFFER)
.setCookie(APP_COOKIE)
.setHardTimeout(HARD_TIMEOUT)
.setIdleTimeout(IDLE_TIMEOUT)
.setPriority(PRIORITY)
.setTableId(Tables.VLAN);
/*
* For each port, if it's an access port, must first push flow for tagged,
* THEN push flow for untagged. If it's trunk and access, do the same.
* If it's just trunk, then only push the tagged flow.
*/
for (OFPortModeTuple p : ports) {
/* OF-DPA requires match on VLAN tag for both trunk and access ports */
mb.setExact(MatchField.VLAN_VID, OFVlanVidMatch.ofVlanVid((vlan.equals(VlanVid.ZERO) ? VlanVid.ofVlan(1) : vlan)));
/* No matter what, we need to match on the ingress port */
mb.setExact(MatchField.IN_PORT, p.getPort());
/* We have to do this for OF-DPA. It's like adding the VLAN to the switch on that port. */
/* Happens automatically: actions.add(sw.getOFFactory().actions().buildPushVlan().setEthertype(EthType.VLAN_FRAME).build()); */
applyActions.add(sw.getOFFactory().actions().setField(sw.getOFFactory().oxms().vlanVid(OFVlanVidMatch.ofVlanVid((vlan.equals(VlanVid.ZERO) ? VlanVid.ofVlan(1) : vlan)))));
instructions.add(sw.getOFFactory().instructions().applyActions(applyActions));
/* No matter what, output to the next table */
instructions.add(sw.getOFFactory().instructions().gotoTable(Tables.TERMINATION_MAC));
fab.setInstructions(instructions)
.setMatch(mb.build())
.build();
sw.write(fab.build());
if (log.isDebugEnabled()) {
log.debug("Writing tagged prereq flow to VLAN flow table {}", fab.build().toString());
}
/* Don't forget to empty out our containers for the next iteration (or below). */
instructions.clear();
applyActions.clear();
mb = sw.getOFFactory().buildMatch();
/* Here, if the port is access, add another untagged flow */
if (vlan.equals(VlanVid.ZERO) || p.getMode() == OFPortMode.ACCESS) {
mb.setExact(MatchField.VLAN_VID, OFVlanVidMatch.UNTAGGED); //TODO verify this
mb.setExact(MatchField.IN_PORT, p.getPort());
applyActions.add(sw.getOFFactory().actions().setField(sw.getOFFactory().oxms().vlanVid(OFVlanVidMatch.ofVlanVid((vlan.equals(VlanVid.ZERO) ? VlanVid.ofVlan(1) : vlan)))));
instructions.add(sw.getOFFactory().instructions().applyActions(applyActions));
instructions.add(sw.getOFFactory().instructions().gotoTable(Tables.TERMINATION_MAC));
fab.setInstructions(instructions)
.setMatch(mb.build())
.build();
sw.write(fab.build());
if (log.isDebugEnabled()) {
log.debug("Writing untagged prereq flow to VLAN flow table {}", fab.build().toString());
}
/* Don't forget to empty out our containers for the next iteration (or below). */
instructions.clear();
applyActions.clear();
mb = sw.getOFFactory().buildMatch();
}
}
/* Termination MAC table auto-forwards to bridging table (50) */
/*
* We will insert a DLF flow to send to controller in the bridging table (50).
*/
writeActions.add(sw.getOFFactory().actions().group(OFDPAUtils.GroupIds.createL2Flood(
U16.of((vlan.equals(VlanVid.ZERO) ? VlanVid.ofVlan(1) : vlan).getVlan()) /* ID */,
vlan.equals(VlanVid.ZERO) ? VlanVid.ofVlan(1) : vlan))); /* bogus action */
applyActions.add(sw.getOFFactory().actions().output(OFPort.CONTROLLER, 0xffFFffFF)); /* real, intended action */
instructions.add(sw.getOFFactory().instructions().writeActions(writeActions));
instructions.add(sw.getOFFactory().instructions().applyActions(applyActions));
instructions.add(sw.getOFFactory().instructions().gotoTable(Tables.POLICY_ACL)); /* must go to policy ACL otherwise dropped; bogus though */
fab = fab.setMatch(sw.getOFFactory().buildMatch()
.setExact(MatchField.VLAN_VID, OFVlanVidMatch.ofVlanVid(vlan.equals(VlanVid.ZERO) ? VlanVid.ofVlan(1) : vlan)) /* must match on just VLAN; dst MAC wildcarded */
.build())
.setInstructions(instructions)
.setPriority(DLF_PRIORITY) /* lower priority */
.setTableId(Tables.BRIDGING);
sw.write(fab.build());
if (log.isDebugEnabled()) {
log.debug("Writing DLF flow to bridging table {}", fab.build().toString());
}
return true;
}
/**
* Note: Must have called {@link OFDPAUtils#addLearningSwitchPrereqs(IOFSwitch, VlanVid, List) this function } prior to calling
* this function. It is assumed you have done the aforementioned with the same VLAN and ports, otherwise you will likely
* get a very grumpy OF-DPA switch.
*
* Based on an intent described by a Match and an output OFPort,
* add a flow to an OF-DPA switch, conforming to the pipeline it
* exposes. The provided Match must contain at least the destination
* MAC address. If you would like to match on an untagged ethernet
* frame, omit MatchField.VLAN_VID from the Match object so that it
* is wildcarded. To the contrary, if you would like to match on a
* specific VLAN tag, please include this field. It is not possible
* to match on both tagged and untagged packets in a single flow.
*
* Implementation note: If a VLAN tag is not provided in the Match, the
* default VLAN of 1 will be assigned for internal use only; it will
* not appear on any packets egress the switch.
*
* If the packet is to be sent out on a different VLAN (e.g. for VLAN
* translation) then the VlanVid must be specified.
*
* An output OFPort must be specified. If the desired action is to
* flood, use OFPort.FLOOD; for sending to the controller, use
* OFPort.CONTROLLER; etc.
*
* @param s, must be an OF-DPA switch
* @param m, must contain at least the destination MAC
* @param outVlan, either a valid VLAN ID or ZERO for untagged
* @param outPort, either a valid physical port number or ZERO (for drop), ALL, FLOOD, or CONTROLLER
* @return true upon success; false if switch is not an OF-DPA switch
*/
public static boolean addLearningSwitchFlow(IOFSwitch sw, U64 cookie, int priority, int hardTimeout, int idleTimeout, Match match, VlanVid outVlan, OFPort outPort) {
if (!isOFDPASwitch(sw)) {
log.error("Switch {} is not an OF-DPA switch. Not inserting flows.", sw.getId().toString());
return false;
}
/*
* Prepare and/or check arguments against requirements.
*/
cookie = (cookie == null ? U64.ZERO : cookie);
priority = (priority < 1 ? 1 : priority);
hardTimeout = (hardTimeout < 0 ? 0 : hardTimeout);
idleTimeout = (idleTimeout < 0 ? 0 : idleTimeout);
if (match == null || !match.isExact(MatchField.ETH_DST)) {
log.error("OF-DPA 2.0 requires the destination MAC be matched in order to forward through its pipeline.");
return false;
} else if (match == null || !match.isExact(MatchField.VLAN_VID)) {
log.error("OF-DPA 2.0 requires the VLAN be matched in order to forward through its pipeline.");
return false;
} else {
List<MatchFields> mfs = checkMatchFields(match);
if (mfs != null) {
log.error("OF-DPA 2.0 does not support matching on the following fields: {}", mfs.toString());
return false;
}
}
outVlan = (outVlan == null ? VlanVid.ZERO : outVlan);
outPort = (outPort == null ? OFPort.ZERO : outPort);
/*
* Add flow to bridging table that matches on dst MAC and outputs
* to the known port where the next hop or destination resides.
*/
ArrayList<OFInstruction> instructions = new ArrayList<OFInstruction>();
ArrayList<OFAction> actions = new ArrayList<OFAction>();
actions.add(sw.getOFFactory().actions().group(GroupIds.createL2Interface(outPort, (outVlan.equals(VlanVid.ZERO) ? VlanVid.ofVlan(1) : outVlan))));
instructions.add(sw.getOFFactory().instructions().writeActions(actions));
instructions.add(sw.getOFFactory().instructions().gotoTable(Tables.POLICY_ACL)); /* must go here or dropped */
OFFlowAdd fa = sw.getOFFactory().buildFlowAdd()
.setMatch(sw.getOFFactory().buildMatch()
.setExact(MatchField.VLAN_VID, match.get(MatchField.VLAN_VID))
.setExact(MatchField.ETH_DST, match.get(MatchField.ETH_DST))
.build())
.setPriority(priority)
.setIdleTimeout(idleTimeout)
.setHardTimeout(hardTimeout)
.setBufferId(OFBufferId.NO_BUFFER)
.setCookie(OFDPAUtils.APP_COOKIE)
.setTableId(OFDPAUtils.Tables.BRIDGING)
.setInstructions(instructions)
.build();
log.debug("Writing learning switch flow to bridging table: {}", fa);
sw.write(fa);
/*
* Policy ACL table (60) allows for more detailed matches. The
* write-actions goto group inserted by the bridging table (50)
* will be the output action taken upon a match. The policy ACL
* table allows for (optional) additional matches to take place.
* If we want to drop a packet instead of forwarding it, then
* the output group must be cleared in a flow that matches on
* the destination MAC and VLAN (same as bridging).
*/
/*
* For now, let's assume we only match on VLAN and destination MAC.
* If that's the case, we should be able to do L2 forwarding b/t
* ports residing on the same VLAN.
*
* We're allowed to match on anything in the Match (supplied as an argument to this function) at this point.
fa = sw.getOFFactory().buildFlowAdd()
.setBufferId(OFBufferId.NO_BUFFER)
.setCookie(cookie)
.setHardTimeout(hardTimeout)
.setIdleTimeout(idleTimeout)
.setPriority(priority)
.setTableId(Tables.POLICY_ACL)
.setMatch(match)
.setInstructions(instructions)
.build();
log.debug("Writing learning switch flow to policy ACL table: {}", fa);
sw.write(fa); */
return true;
}
/**
* Generates group IDs according to the OF-DPA 2.0 spec. A create
* helper function is provided for each type of group, including
* sub-types, which should be used to set a valid group ID in any
* group mod sent to any OF-DPA switch.
*/
public static class GroupIds {
private GroupIds() {}
public static OFGroup createL2Interface(OFPort p, VlanVid v) { //0
return OFGroup.of(0 | p.getShortPortNumber() | (v.getVlan() << 16) | (OFDPAGroupType.L2_INTERFACE << 28));
}
/**
* Only bits 0-27 of id are used. Bits 28-31 are ignored.
* @param id
* @return
*/
public static OFGroup createL2Rewrite(U32 id) { //1
return OFGroup.of(0 | (id.getRaw() & 0x0FffFFff) | (OFDPAGroupType.L2_REWRITE << 28));
}
/**
* Only bits 0-27 of id are used. Bits 28-31 are ignored.
* @param id
* @return
*/
public static OFGroup createL3Unicast(U32 id) { //2
return OFGroup.of(0 | (id.getRaw() & 0x0FffFFff) | (OFDPAGroupType.L3_UNICAST << 28));
}
public static OFGroup createL2Multicast(U16 id, VlanVid v) { //3
return OFGroup.of(0 | id.getRaw() | (v.getVlan() << 16) | (OFDPAGroupType.L2_MULTICAST << 28));
}
public static OFGroup createL2Flood(U16 id, VlanVid v) { //4
return OFGroup.of(0 | id.getRaw() | (v.getVlan() << 16) | (OFDPAGroupType.L2_FLOOD << 28));
}
/**
* Only bits 0-27 of id are used. Bits 28-31 are ignored.
* @param id
* @return
*/
public static OFGroup createL3Interface(U32 id) { //5
return OFGroup.of(0 | (id.getRaw() & 0x0FffFFff) | (OFDPAGroupType.L3_INTERFACE << 28));
}
public static OFGroup createL3Multicast(U16 id, VlanVid v) { //6
return OFGroup.of(0 | id.getRaw() | (v.getVlan() << 16) | (OFDPAGroupType.L3_MULTICAST << 28));
}
/**
* Only bits 0-27 of id are used. Bits 28-31 are ignored.
* @param id
* @return
*/
public static OFGroup createL3ECMP(U32 id) { //7
return OFGroup.of(0 | (id.getRaw() & 0x0FffFFff) | (OFDPAGroupType.L3_ECMP << 28));
}
/**
* Only bits 0-9 of index are used. Bits 10-15 are ignored.
* @param index
* @param tunnelId
* @return
*/
public static OFGroup createL2DCOFloodOverUnicastTunnels(U16 index, U16 tunnelId) { //8
return OFGroup.of(0 | (index.getRaw() & 0x03ff)
| (tunnelId.getRaw() << 12)
| (L2OverlaySubType.L2_OVERLAY_FLOOD_OVER_UNICAST_TUNNELS << 10)
| (OFDPAGroupType.L2_DATA_CENTER_OVERLAY << 28));
}
/**
* Only bits 0-9 of index are used. Bits 10-15 are ignored.
* @param index
* @param tunnelId
* @return
*/
public static OFGroup createL2DCOFloodOverMulticastTunnels(U16 index, U16 tunnelId) { //8
return OFGroup.of(0 | (index.getRaw() & 0x03ff)
| (tunnelId.getRaw() << 12)
| (L2OverlaySubType.L2_OVERLAY_FLOOD_OVER_MULTICAST_TUNNELS << 10)
| (OFDPAGroupType.L2_DATA_CENTER_OVERLAY << 28));
}
/**
* Only bits 0-9 of index are used. Bits 10-15 are ignored.
* @param index
* @param tunnelId
* @return
*/
public static OFGroup createL2DCOMulticastOverUnicastTunnels(U16 index, U16 tunnelId) { //8
return OFGroup.of(0 | (index.getRaw() & 0x03ff)
| (tunnelId.getRaw() << 12)
| (L2OverlaySubType.L2_OVERLAY_MULTICAST_OVER_UNICAST_TUNNELS << 10)
| (OFDPAGroupType.L2_DATA_CENTER_OVERLAY << 28));
}
/**
* Only bits 0-9 of index are used. Bits 10-15 are ignored.
* @param index
* @param tunnelId
* @return
*/
public static OFGroup createL2DCOMulticastOverMulticastTunnels(U16 index, U16 tunnelId) { //8
return OFGroup.of(0 | (index.getRaw() & 0x03ff)
| (tunnelId.getRaw() << 12)
| (L2OverlaySubType.L2_OVERLAY_MULTICAST_OVER_MULTICAST_TUNNELS << 10)
| (OFDPAGroupType.L2_DATA_CENTER_OVERLAY << 28));
}
/**
* Only bits 0-23 of index are used. Bits 24-31 are ignored.
* @param index
* @return
*/
public static OFGroup createMPLSInterfaceLabel(U32 index) { //9
return OFGroup.of(0 | (index.getRaw() & 0x00ffFFff)
| (MPLSSubType.MPLS_INTERFACE << 24)
| (OFDPAGroupType.MPLS_LABEL << 28));
}
/**
* Only bits 0-23 of index are used. Bits 24-31 are ignored.
* @param index
* @return
*/
public static OFGroup createMPLSL2VPNLabel(U32 index) { //9
return OFGroup.of(0 | (index.getRaw() & 0x00ffFFff)
| (MPLSSubType.MPLS_L2_VPN_LABEL << 24)
| (OFDPAGroupType.MPLS_LABEL << 28));
}
/**
* Only bits 0-23 of index are used. Bits 24-31 are ignored.
* @param index
* @return
*/
public static OFGroup createMPLSL3VPNLabel(U32 index) { //9
return OFGroup.of(0 | (index.getRaw() & 0x00ffFFff)
| (MPLSSubType.MPLS_L3_VPN_LABEL << 24)
| (OFDPAGroupType.MPLS_LABEL << 28));
}
/**
* Only bits 0-23 of index are used. Bits 24-31 are ignored.
* @param index
* @return
*/
public static OFGroup createMPLSTunnelLable1(U32 index) { //9
return OFGroup.of(0 | (index.getRaw() & 0x00ffFFff)
| (MPLSSubType.MPLS_TUNNEL_LABEL_1 << 24)
| (OFDPAGroupType.MPLS_LABEL << 28));
}
/**
* Only bits 0-23 of index are used. Bits 24-31 are ignored.
* @param index
* @return
*/
public static OFGroup createMPLSTunnelLabel2(U32 index) { //9
return OFGroup.of(0 | (index.getRaw() & 0x00ffFFff)
| (MPLSSubType.MPLS_TUNNEL_LABEL_2 << 24)
| (OFDPAGroupType.MPLS_LABEL << 28));
}
/**
* Only bits 0-23 of index are used. Bits 24-31 are ignored.
* @param index
* @return
*/
public static OFGroup createMPLSSwapLabel(U32 index) { //9
return OFGroup.of(0 | (index.getRaw() & 0x00ffFFff)
| (MPLSSubType.MPLS_SWAP_LABEL << 24)
| (OFDPAGroupType.MPLS_LABEL << 28));
}
/**
* Only bits 0-23 of index are used. Bits 24-31 are ignored.
* @param index
* @return
*/
public static OFGroup createMPLSForwardingFastFailover(U32 index) { //10
return OFGroup.of(0 | (index.getRaw() & 0x00ffFFff)
| (MPLSSubType.MPLS_FAST_FAILOVER << 24)
| (OFDPAGroupType.MPLS_FORWARDING << 28));
}
/**
* Only bits 0-23 of index are used. Bits 24-31 are ignored.
* @param index
* @return
*/
public static OFGroup createMPLSForwardingECMP(U32 index) { //10
return OFGroup.of(0 | (index.getRaw() & 0x00ffFFff)
| (MPLSSubType.MPLS_ECMP << 24)
| (OFDPAGroupType.MPLS_FORWARDING << 28));
}
/**
* Only bits 0-23 of index are used. Bits 24-31 are ignored.
* @param index
* @return
*/
public static OFGroup createMPLSForwardingL2Tag(U32 index) { //10
return OFGroup.of(0 | (index.getRaw() & 0x00ffFFff)
| (MPLSSubType.MPLS_L2_TAG << 24)
| (OFDPAGroupType.MPLS_FORWARDING << 28));
}
public static OFGroup createL2UnfilteredInterface(OFPort p) { //11
return OFGroup.of(0 | p.getShortPortNumber() | (OFDPAGroupType.L2_UNFILTERED_INTERFACE << 28));
}
public static OFGroup createL2Loopback(OFPort p) { //12
return OFGroup.of(0 | p.getShortPortNumber() | (OFDPAGroupType.L2_LOOPBACK << 28));
}
}
}