From ee079c316c04a8cce35a685a6a5ddc75921bcc5e Mon Sep 17 00:00:00 2001
From: Ryan Izard <rizard@g.clemson.edu>
Date: Wed, 22 Jul 2015 08:10:38 +0200
Subject: [PATCH] Ooops. Forgot to include the new source file for OFDPAUtils
 (hiding in untracked files)...

---
 .../floodlightcontroller/util/OFDPAUtils.java | 581 ++++++++++++++++++
 1 file changed, 581 insertions(+)
 create mode 100644 src/main/java/net/floodlightcontroller/util/OFDPAUtils.java

diff --git a/src/main/java/net/floodlightcontroller/util/OFDPAUtils.java b/src/main/java/net/floodlightcontroller/util/OFDPAUtils.java
new file mode 100644
index 000000000..9aca3e462
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/util/OFDPAUtils.java
@@ -0,0 +1,581 @@
+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 net.floodlightcontroller.core.IOFSwitch;
+
+import org.projectfloodlight.openflow.protocol.OFFlowAdd;
+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.EthType;
+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);
+
+	private static class OFDPAGroupType { // TODO change these enums to static classes so that we don't rely on the ordering...
+		static final int L2_INTERFACE = 0;			/* 0 */
+		static final int L2_REWRITE = 1; 			/* 1 */
+		static final int L3_UNICAST = 2;				/* 2 */
+		static final int L2_MULTICAST = 3; 			/* 3 */
+		static final int L2_FLOOD = 4; 				/* 4 */
+		static final int L3_INTERFACE = 5; 			/* 5 */
+		static final int L3_MULTICAST = 6;			/* 6 */
+		static final int L3_ECMP = 7;				/* 7 */
+		static final int L2_DATA_CENTER_OVERLAY = 8; /* 8 */
+		static final int MPLS_LABEL = 9; 			/* 9 */
+		static final int MPLS_FORWARDING = 10;		/* 10 */
+		static final int L2_UNFILTERED_INTERFACE = 11; /* 11 */
+		static final int L2_LOOPBACK = 12;   		/* 12 */
+	}
+
+	private static class L2OverlaySubType {
+		static final int L2_OVERLAY_FLOOD_OVER_UNICAST_TUNNELS = 0;
+		static final int L2_OVERLAY_FLOOD_OVER_MULTICAST_TUNNELS = 1;
+		static final int L2_OVERLAY_MULTICAST_OVER_UNICAST_TUNNELS = 2;
+		static final int L2_OVERLAY_MULTICAST_OVER_MULTICAST_TUNNELS = 3;
+	}
+
+	private static class MPLSSubType {
+		static final int MPLS_INTERFACE = 0;
+		static final int MPLS_L2_VPN_LABEL = 1;
+		static final int MPLS_L3_VPN_LABEL = 2;
+		static final int MPLS_TUNNEL_LABEL_1 = 3;
+		static final int MPLS_TUNNEL_LABEL_2 = 4;
+	    static final int MPLS_SWAP_LABEL = 5;
+		static final int MPLS_FAST_FAILOVER = 6;
+		static final int MPLS_ECMP = 8;
+		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
+							// TODO fill in rest of ARP and IPV6 stuff here 
+							)
+					);
+
+	/**
+	 * 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;
+	}
+
+	/**
+	 * 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 addBridgingFlow(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 at least the destination MAC 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.ofVlan(0) : outVlan);
+		outPort = (outPort == null ? OFPort.ZERO : outPort);
+
+		/*
+		 * Ingress flow table (0) will automatically send to the
+		 * VLAN flow table (10), so insert nothing here.
+		 */
+
+		/*
+		 * 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> actions = new ArrayList<OFAction>();
+		Match.Builder mb = sw.getOFFactory().buildMatch();
+
+		/* If VLAN tag not present, add the internal tag of 1 */
+		if (!match.isExact(MatchField.VLAN_VID)) {
+			mb.setExact(MatchField.VLAN_VID, OFVlanVidMatch.UNTAGGED);
+			actions.add(sw.getOFFactory().actions().buildPushVlan().setEthertype(EthType.VLAN_FRAME).build());
+			actions.add(sw.getOFFactory().actions().setField(sw.getOFFactory().oxms().vlanVid(OFVlanVidMatch.ofVlan(1))));
+			instructions.add(sw.getOFFactory().instructions().applyActions(actions));
+		} else {
+			mb.setExact(MatchField.VLAN_VID, match.get(MatchField.VLAN_VID));
+		}
+
+		/* No matter what, output to the next table */
+		instructions.add(sw.getOFFactory().instructions().gotoTable(Tables.TERMINATION_MAC));
+
+		OFFlowAdd fa = sw.getOFFactory().buildFlowAdd()
+				.setBufferId(OFBufferId.NO_BUFFER)
+				.setCookie(cookie)
+				.setHardTimeout(hardTimeout)
+				.setIdleTimeout(idleTimeout)
+				.setPriority(priority)
+				.setInstructions(instructions)
+				.setTableId(Tables.VLAN)
+				.setMatch(mb.build())
+				.build();
+		sw.write(fa);
+
+		/*
+		 * Termination MAC table (20) will automatically send to the
+		 * bridging flow table (50), so also insert nothing here.
+		 */
+
+		/*
+		 * Unicast routing (30) and multicast routing (40) flow tables
+		 * are special use-case tables the application should program 
+		 * directly. As such, we won't consider them here.
+		 */
+
+		/*
+		 * Bridging table (50) should assign a write-action goto-group 
+		 * depending on the desired output action (single-port or 
+		 * flood). This can also be done in the next table, policy ACL,
+		 * which we will do. Bridging must match on the VLAN VID and the
+		 * dest MAC address of the packet. It must have a priority greater
+		 * than all less-specific flows in the table (i.e. wildcarded
+		 * flows). We will reserve priority 0 for a DLF (destination
+		 * lookup failure) flow, which would have all fields wildcarded.
+		 * The default on miss is to send to the policy ACL table (60),
+		 * but this will not occur if our flow is matched, so we need to
+		 * do this specifically in each flow we insert.
+		 */
+
+		instructions.clear();
+		actions.clear();
+		
+		/* No matter what, output to the next table */
+		instructions.add(sw.getOFFactory().instructions().gotoTable(Tables.POLICY_ACL));
+
+		fa = fa.createBuilder()
+				.setTableId(Tables.BRIDGING)
+				.setMatch(sw.getOFFactory().buildMatch()
+						.setExact(MatchField.VLAN_VID, fa.getMatch().get(MatchField.VLAN_VID)) /* might have been set to 1, so grab it here */
+						.setExact(MatchField.ETH_DST, match.get(MatchField.ETH_DST))
+						.build())
+						.setInstructions(instructions)
+						.build();
+		sw.write(fa);
+
+		/*
+		 * Policy ACL table (60) allows for more detailed matches. This
+		 * is where we will implement all the matches specified in the Match
+		 * object. The write-actions goto group inserted by the bridging 
+		 * table (50), or here (60), will be the output action taken upon 
+		 * a match, since this is the end of the pipeline. If we want to 
+		 * drop a packet for not matching, then no output group will be
+		 * assigned to the packet, thus dropping it.
+		 * 
+		 * A DLF (desintation lookup failure) flow can also be inserted
+		 * here to forward packets to the controller.
+		 */
+		
+		/* Set the group to which we want to output. This might be a L2 flood or interface. */
+		instructions.clear();
+		actions.clear();
+		if (outPort.equals(OFPort.ZERO)) {
+			/* Don't add a group at all --> DROP */
+		} else if (outPort.equals(OFPort.FLOOD) || outPort.equals(OFPort.ALL)) { // TODO how to distinguish in OF-DPA?
+			actions.add(
+					sw.getOFFactory().actions().group( // TODO Assume there is only one flood group per VLAN
+							GroupIds.createL2Flood(U16.ZERO, fa.getMatch().get(MatchField.VLAN_VID).getVlanVid())
+							)
+					);
+			instructions.add(sw.getOFFactory().instructions().writeActions(actions));
+		} else if (outPort.equals(OFPort.CONTROLLER)) {
+			actions.add(sw.getOFFactory().actions().output(OFPort.CONTROLLER, 0xFFffFFff));
+			instructions.add(sw.getOFFactory().instructions().applyActions(actions));
+		} else { /* assume port is a number valid on the switch */
+			actions.add(
+					sw.getOFFactory().actions().group(
+							GroupIds.createL2Interface(outPort, fa.getMatch().get(MatchField.VLAN_VID).getVlanVid())
+							)
+					);
+			instructions.add(sw.getOFFactory().instructions().writeActions(actions));
+		}
+		
+		/* We're allowed to match on anything in the Match at this point. */
+		fa = fa.createBuilder()
+				.setTableId(Tables.POLICY_ACL)
+				.setMatch(match)
+				.setInstructions(instructions)
+				.build();
+		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_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 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));
+		}
+	}
+}
-- 
GitLab