diff --git a/src/main/java/net/floodlightcontroller/util/ActionUtils.java b/src/main/java/net/floodlightcontroller/util/ActionUtils.java index 117226b5788fb042b7ef9e6eb45758ca925feaa8..6d905ab9627e07dbfa4b308546a2821ea609cbca 100644 --- a/src/main/java/net/floodlightcontroller/util/ActionUtils.java +++ b/src/main/java/net/floodlightcontroller/util/ActionUtils.java @@ -125,1428 +125,1395 @@ import com.fasterxml.jackson.core.JsonParser; * @author Ryan Izard <ryan.izard@bigswitch.com, rizard@g.clemson.edu> */ public class ActionUtils { - private static final Logger log = LoggerFactory.getLogger(ActionUtils.class); + private static final Logger log = LoggerFactory.getLogger(ActionUtils.class); - /* OF1.3 ACTIONS (includes OF1.0) */ - public static final String STR_OUTPUT = "output"; - public static final String STR_ENQUEUE = "enqueue"; - public static final String STR_VLAN_STRIP = "strip_vlan"; - public static final String STR_VLAN_POP = "pop_vlan"; - public static final String STR_VLAN_PUSH = "push_vlan"; - public static final String STR_VLAN_SET_PCP = "set_vlan_pcp"; - public static final String STR_VLAN_SET_VID = "set_vlan_vid"; - public static final String STR_QUEUE_SET = "set_queue"; - public static final String STR_DL_SRC_SET = "set_eth_src"; - public static final String STR_DL_DST_SET = "set_eth_dst"; - public static final String STR_NW_SRC_SET = "set_ipv4_src"; - public static final String STR_NW_DST_SET = "set_ipv4_dst"; - public static final String STR_NW_ECN_SET = "set_ip_ecn"; - public static final String STR_NW_TOS_SET = "set_ip_tos"; - public static final String STR_NW_TTL_SET = "set_ip_ttl"; - public static final String STR_NW_TTL_DEC = "dec_ip_ttl"; - public static final String STR_TTL_IN_COPY = "copy_ip_ttl_in"; - public static final String STR_TTL_OUT_COPY = "copy_ip_ttl_out"; - public static final String STR_MPLS_LABEL_SET = "set_mpls_label"; - public static final String STR_MPLS_TC_SET = "set_mpls_tc"; - public static final String STR_MPLS_TTL_SET = "set_mpls_ttl"; - public static final String STR_MPLS_TTL_DEC = "dec_mpls_ttl"; - public static final String STR_MPLS_PUSH = "push_mpls"; - public static final String STR_MPLS_POP = "pop_mpls"; - public static final String STR_TP_SRC_SET = "set_tp_src"; - public static final String STR_TP_DST_SET = "set_tp_dst"; - public static final String STR_PBB_PUSH = "push_pbb"; - public static final String STR_PBB_POP = "pop_pbb"; - public static final String STR_GROUP = "group"; - public static final String STR_FIELD_SET = "set_field"; - public static final String STR_FIELD_COPY = "copy_field"; - public static final String STR_METER = "meter"; - public static final String STR_EXPERIMENTER = "experimenter"; - public static final String STR_NOT_APPLICABLE = "n/a"; + /* OF1.3 ACTIONS (includes OF1.0) */ + public static final String STR_OUTPUT = "output"; + public static final String STR_ENQUEUE = "enqueue"; + public static final String STR_VLAN_STRIP = "strip_vlan"; + public static final String STR_VLAN_POP = "pop_vlan"; + public static final String STR_VLAN_PUSH = "push_vlan"; + public static final String STR_VLAN_SET_PCP = "set_vlan_pcp"; + public static final String STR_VLAN_SET_VID = "set_vlan_vid"; + public static final String STR_QUEUE_SET = "set_queue"; + public static final String STR_DL_SRC_SET = "set_eth_src"; + public static final String STR_DL_DST_SET = "set_eth_dst"; + public static final String STR_NW_SRC_SET = "set_ipv4_src"; + public static final String STR_NW_DST_SET = "set_ipv4_dst"; + public static final String STR_NW_ECN_SET = "set_ip_ecn"; + public static final String STR_NW_TOS_SET = "set_ip_tos"; + public static final String STR_NW_TTL_SET = "set_ip_ttl"; + public static final String STR_NW_TTL_DEC = "dec_ip_ttl"; + public static final String STR_TTL_IN_COPY = "copy_ip_ttl_in"; + public static final String STR_TTL_OUT_COPY = "copy_ip_ttl_out"; + public static final String STR_MPLS_LABEL_SET = "set_mpls_label"; + public static final String STR_MPLS_TC_SET = "set_mpls_tc"; + public static final String STR_MPLS_TTL_SET = "set_mpls_ttl"; + public static final String STR_MPLS_TTL_DEC = "dec_mpls_ttl"; + public static final String STR_MPLS_PUSH = "push_mpls"; + public static final String STR_MPLS_POP = "pop_mpls"; + public static final String STR_TP_SRC_SET = "set_tp_src"; + public static final String STR_TP_DST_SET = "set_tp_dst"; + public static final String STR_PBB_PUSH = "push_pbb"; + public static final String STR_PBB_POP = "pop_pbb"; + public static final String STR_GROUP = "group"; + public static final String STR_FIELD_SET = "set_field"; + public static final String STR_FIELD_COPY = "copy_field"; + public static final String STR_METER = "meter"; + public static final String STR_EXPERIMENTER = "experimenter"; + public static final String STR_NOT_APPLICABLE = "n/a"; - /* OF1.3 set-field operations are defined as any OF1.3 match. - * We will borrow MatchUtils's String definitions of all OF1.3 - * set-field operations to be consistent. - */ + /* OF1.3 set-field operations are defined as any OF1.3 match. + * We will borrow MatchUtils's String definitions of all OF1.3 + * set-field operations to be consistent. + */ - private static final JsonFactory jsonFactory = new JsonFactory(); - private static final String JSON_EMPTY_OBJECT = "{}"; + private static final JsonFactory jsonFactory = new JsonFactory(); + private static final String JSON_EMPTY_OBJECT = "{}"; - /** - * Returns a String representation of all the OpenFlow actions. - * @param actions; A list of OFActions to encode into one string - * @return A dpctl-style string of the actions - */ - public static String actionsToString(List<OFAction> actions) { - StringBuilder sb = new StringBuilder(); - for (OFAction a : actions) { - if (sb.length() > 0) { - sb.append(','); - } - switch(a.getType()) { - case OUTPUT: - sb.append(STR_OUTPUT).append("=").append(Integer.toString(((OFActionOutput)a).getPort().getPortNumber())); - break; - case ENQUEUE: - long queue = ((OFActionEnqueue)a).getQueueId(); - OFPort port = ((OFActionEnqueue)a).getPort(); - sb.append(STR_ENQUEUE).append("=").append(Integer.toString(port.getPortNumber())).append(":0x").append(String.format("%02x", queue)); - break; - case STRIP_VLAN: - sb.append(STR_VLAN_STRIP); - break; - case POP_VLAN: - sb.append(STR_VLAN_POP); - break; - case PUSH_VLAN: - sb.append(STR_VLAN_PUSH).append("=").append(Integer.toString(((OFActionPushVlan)a).getEthertype().getValue())); - break; - case SET_VLAN_VID: - sb.append(STR_VLAN_SET_VID).append("=").append(Short.toString(((OFActionSetVlanVid)a).getVlanVid().getVlan())); - break; - case SET_VLAN_PCP: - sb.append(STR_VLAN_SET_PCP).append("=").append(Byte.toString(((OFActionSetVlanPcp)a).getVlanPcp().getValue())); - break; - case SET_QUEUE: - sb.append(STR_QUEUE_SET).append("=").append(Long.toString(((OFActionSetQueue)a).getQueueId())); - case SET_DL_SRC: - sb.append(STR_DL_SRC_SET).append("=").append( ((OFActionSetDlSrc)a).getDlAddr().toString()); - break; - case SET_DL_DST: - sb.append(STR_DL_DST_SET).append("=").append(((OFActionSetDlDst)a).getDlAddr().toString()); - break; - case SET_NW_ECN: - sb.append(STR_NW_ECN_SET).append("=").append(Byte.toString(((OFActionSetNwEcn)a).getNwEcn().getEcnValue())); - break; - case SET_NW_TOS: - sb.append(STR_NW_TOS_SET).append("=").append(Short.toString(((OFActionSetNwTos)a).getNwTos())); - break; - case SET_NW_TTL: - sb.append(STR_NW_TTL_SET).append("=").append(Short.toString(((OFActionSetNwTtl)a).getNwTtl())); - break; - case DEC_NW_TTL: - sb.append(STR_NW_TTL_DEC); - break; - case SET_MPLS_LABEL: - sb.append(STR_MPLS_LABEL_SET).append("=").append(Long.toString(((OFActionSetMplsLabel)a).getMplsLabel())); - break; - case SET_MPLS_TC: - sb.append(STR_MPLS_TC_SET).append("=").append(Short.toString(((OFActionSetMplsTc)a).getMplsTc())); - break; - case SET_MPLS_TTL: - sb.append(STR_MPLS_TTL_SET).append("=").append(Short.toString(((OFActionSetMplsTtl)a).getMplsTtl())); - break; - case DEC_MPLS_TTL: - sb.append(STR_MPLS_TTL_DEC); - break; - case PUSH_MPLS: - sb.append(STR_MPLS_PUSH).append("=").append(Integer.toString(((OFActionPushMpls)a).getEthertype().getValue())); - break; - case POP_MPLS: - sb.append(STR_MPLS_POP).append("=").append(Integer.toString(((OFActionPopMpls)a).getEthertype().getValue())); - break; - case SET_NW_SRC: - sb.append(STR_NW_SRC_SET).append("=").append(((OFActionSetNwSrc)a).getNwAddr().toString()); - break; - case SET_NW_DST: - sb.append(STR_NW_DST_SET).append("=").append(((OFActionSetNwDst)a).getNwAddr().toString()); - break; - case SET_TP_SRC: - sb.append(STR_TP_SRC_SET).append("=").append(((OFActionSetTpSrc)a).getTpPort().toString()); - break; - case SET_TP_DST: - sb.append(STR_TP_DST_SET).append("=").append(((OFActionSetTpDst)a).getTpPort().toString()); - break; - case COPY_TTL_IN: - sb.append(STR_TTL_IN_COPY); - break; - case COPY_TTL_OUT: - sb.append(STR_TTL_OUT_COPY); - break; - case PUSH_PBB: - sb.append(STR_PBB_PUSH).append("=").append(Integer.toString(((OFActionPushPbb)a).getEthertype().getValue())); - break; - case POP_PBB: - sb.append(STR_PBB_POP); - break; - case EXPERIMENTER: - sb.append(STR_EXPERIMENTER).append("=").append(Long.toString(((OFActionExperimenter)a).getExperimenter())); - break; - case GROUP: - sb.append(STR_GROUP).append("=").append(Integer.toString(((OFActionGroup)a).getGroup().getGroupNumber())); - break; - case SET_FIELD: - log.debug("Got Set-Field action. Setting {}", ((OFActionSetField)a)); - /* ARP */ - if (((OFActionSetField)a).getField() instanceof OFOxmArpOp) { - sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_ARP_OPCODE) - .append(MatchUtils.SET_FIELD_DELIM) - .append(Integer.toString(((OFOxmArpOp) ((OFActionSetField) a).getField()).getValue().getOpcode())); - } else if (((OFActionSetField)a).getField() instanceof OFOxmArpSha) { - sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_ARP_SHA) - .append(MatchUtils.SET_FIELD_DELIM) - .append(((OFOxmArpSha) ((OFActionSetField) a).getField()).getValue().toString()); // macaddress formats string already - } else if (((OFActionSetField)a).getField() instanceof OFOxmArpTha) { - sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_ARP_DHA) - .append(MatchUtils.SET_FIELD_DELIM) - .append(((OFOxmArpTha) ((OFActionSetField) a).getField()).getValue().toString()); - } else if (((OFActionSetField)a).getField() instanceof OFOxmArpSpa) { - sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_ARP_SPA) - .append(MatchUtils.SET_FIELD_DELIM) - .append(((OFOxmArpSpa) ((OFActionSetField) a).getField()).getValue().toString()); // ipaddress formats string already - } else if (((OFActionSetField)a).getField() instanceof OFOxmArpTpa) { - sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_ARP_DPA) - .append(MatchUtils.SET_FIELD_DELIM) - .append(((OFOxmArpTpa) ((OFActionSetField) a).getField()).getValue().toString()); - } else if (((OFActionSetField)a).getField() instanceof OFOxmIpv6NdSll) { - sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_IPV6_ND_SLL) - .append(MatchUtils.SET_FIELD_DELIM) - .append(((OFOxmIpv6NdSll) ((OFActionSetField) a).getField()).getValue().toString()); // macaddress formats string already - } else if (((OFActionSetField)a).getField() instanceof OFOxmIpv6NdTll) { - sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_IPV6_ND_TLL) - .append(MatchUtils.SET_FIELD_DELIM) - .append(((OFOxmIpv6NdTll) ((OFActionSetField) a).getField()).getValue().toString()); // macaddress formats string already - } else if (((OFActionSetField)a).getField() instanceof OFOxmIpv6NdTarget) { - sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_IPV6_ND_TARGET) - .append(MatchUtils.SET_FIELD_DELIM) - .append(((OFOxmIpv6NdTarget) ((OFActionSetField) a).getField()).getValue().toString()); - } - /* DATA LAYER */ - else if (((OFActionSetField)a).getField() instanceof OFOxmEthType) { - sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_DL_TYPE) - .append(MatchUtils.SET_FIELD_DELIM) - .append(Integer.toString(((OFOxmEthType) ((OFActionSetField) a).getField()).getValue().getValue())); - } else if (((OFActionSetField)a).getField() instanceof OFOxmEthSrc) { - sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_DL_SRC) - .append(MatchUtils.SET_FIELD_DELIM) - .append(((OFOxmEthSrc) ((OFActionSetField) a).getField()).getValue().toString()); - } else if (((OFActionSetField)a).getField() instanceof OFOxmEthDst) { - sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_DL_DST) - .append(MatchUtils.SET_FIELD_DELIM) - .append(((OFOxmEthDst) ((OFActionSetField) a).getField()).getValue().toString()); - } else if (((OFActionSetField)a).getField() instanceof OFOxmVlanVid) { - sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_DL_VLAN) - .append(MatchUtils.SET_FIELD_DELIM) - .append(Short.toString(((OFOxmVlanVid) ((OFActionSetField) a).getField()).getValue().getVlan())); - } else if (((OFActionSetField)a).getField() instanceof OFOxmVlanPcp) { - sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_DL_VLAN_PCP) - .append(MatchUtils.SET_FIELD_DELIM) - .append(Byte.toString(((OFOxmVlanPcp) ((OFActionSetField) a).getField()).getValue().getValue())); - } - /* ICMP */ - else if (((OFActionSetField)a).getField() instanceof OFOxmIcmpv4Code) { - sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_ICMP_CODE) - .append(MatchUtils.SET_FIELD_DELIM) - .append(Short.toString(((OFOxmIcmpv4Code) ((OFActionSetField) a).getField()).getValue().getCode())); - } else if (((OFActionSetField)a).getField() instanceof OFOxmIcmpv4Type) { - sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_ICMP_TYPE) - .append(MatchUtils.SET_FIELD_DELIM) - .append(Short.toString(((OFOxmIcmpv4Type) ((OFActionSetField) a).getField()).getValue().getType())); - } else if (((OFActionSetField)a).getField() instanceof OFOxmIcmpv6Code) { - sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_ICMPV6_CODE) - .append(MatchUtils.SET_FIELD_DELIM) - .append(Short.toString(((OFOxmIcmpv6Code) ((OFActionSetField) a).getField()).getValue().getRaw())); - } else if (((OFActionSetField)a).getField() instanceof OFOxmIcmpv6Type) { - sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_ICMPV6_TYPE) - .append(MatchUtils.SET_FIELD_DELIM) - .append(Short.toString(((OFOxmIcmpv6Type) ((OFActionSetField) a).getField()).getValue().getRaw())); - } - /* NETWORK LAYER */ - else if (((OFActionSetField)a).getField() instanceof OFOxmIpProto) { - sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_NW_PROTO) - .append(MatchUtils.SET_FIELD_DELIM) - .append(Short.toString(((OFOxmIpProto) ((OFActionSetField) a).getField()).getValue().getIpProtocolNumber())); - } else if (((OFActionSetField)a).getField() instanceof OFOxmIpv4Src) { - sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_NW_SRC) - .append(MatchUtils.SET_FIELD_DELIM) - .append(((OFOxmIpv4Src) ((OFActionSetField) a).getField()).getValue().toString()); - } else if (((OFActionSetField)a).getField() instanceof OFOxmIpv4Dst) { - sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_NW_DST) - .append(MatchUtils.SET_FIELD_DELIM) - .append(((OFOxmIpv4Dst) ((OFActionSetField) a).getField()).getValue().toString()); - } else if (((OFActionSetField)a).getField() instanceof OFOxmIpv6Src) { - sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_IPV6_SRC) - .append(MatchUtils.SET_FIELD_DELIM) - .append(((OFOxmIpv6Src) ((OFActionSetField) a).getField()).getValue().toString()); - } else if (((OFActionSetField)a).getField() instanceof OFOxmIpv6Dst) { - sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_IPV6_DST) - .append(MatchUtils.SET_FIELD_DELIM) - .append(((OFOxmIpv6Dst) ((OFActionSetField) a).getField()).getValue().toString()); - } else if (((OFActionSetField)a).getField() instanceof OFOxmIpv6Flabel) { - sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_IPV6_FLOW_LABEL) - .append(MatchUtils.SET_FIELD_DELIM) - .append(((OFOxmIpv6Flabel) ((OFActionSetField) a).getField()).getValue().toString()); - } else if (((OFActionSetField)a).getField() instanceof OFOxmIpv6Exthdr) { - sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_IPV6_EXTHDR) - .append(MatchUtils.SET_FIELD_DELIM) - .append(((OFOxmIpv6Exthdr) ((OFActionSetField) a).getField()).getValue().toString()); - } else if (((OFActionSetField)a).getField() instanceof OFOxmIpEcn) { - sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_NW_ECN) - .append(MatchUtils.SET_FIELD_DELIM) - .append(Byte.toString(((OFOxmIpEcn) ((OFActionSetField) a).getField()).getValue().getEcnValue())); - } else if (((OFActionSetField)a).getField() instanceof OFOxmIpDscp) { - sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_NW_DSCP) - .append(MatchUtils.SET_FIELD_DELIM) - .append(Byte.toString(((OFOxmIpDscp) ((OFActionSetField) a).getField()).getValue().getDscpValue())); - } - /* TRANSPORT LAYER, TCP, UDP, and SCTP */ - else if (((OFActionSetField)a).getField() instanceof OFOxmTcpSrc) { - sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_TCP_SRC) - .append(MatchUtils.SET_FIELD_DELIM) - .append(Integer.toString(((OFOxmTcpSrc) ((OFActionSetField) a).getField()).getValue().getPort())); - } else if (((OFActionSetField)a).getField() instanceof OFOxmTcpDst) { - sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_TCP_DST) - .append(MatchUtils.SET_FIELD_DELIM) - .append(Integer.toString(((OFOxmTcpDst) ((OFActionSetField) a).getField()).getValue().getPort())); - } else if (((OFActionSetField)a).getField() instanceof OFOxmUdpSrc) { - sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_UDP_SRC) - .append(MatchUtils.SET_FIELD_DELIM) - .append(Integer.toString(((OFOxmUdpSrc) ((OFActionSetField) a).getField()).getValue().getPort())); - } else if (((OFActionSetField)a).getField() instanceof OFOxmUdpDst) { - sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_UDP_DST) - .append(MatchUtils.SET_FIELD_DELIM) - .append(Integer.toString(((OFOxmUdpDst) ((OFActionSetField) a).getField()).getValue().getPort())); - } else if (((OFActionSetField)a).getField() instanceof OFOxmSctpSrc) { - sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_SCTP_SRC) - .append(MatchUtils.SET_FIELD_DELIM) - .append(Integer.toString(((OFOxmSctpSrc) ((OFActionSetField) a).getField()).getValue().getPort())); - } else if (((OFActionSetField)a).getField() instanceof OFOxmSctpDst) { - sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_SCTP_DST) - .append(MatchUtils.SET_FIELD_DELIM) - .append(Integer.toString(((OFOxmSctpDst) ((OFActionSetField) a).getField()).getValue().getPort())); - } else if (((OFActionSetField)a).getField() instanceof OFOxmTcpFlags) { - sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_TCP_FLAGS) - .append(MatchUtils.SET_FIELD_DELIM) - .append(Integer.toString(((OFOxmTcpFlags) ((OFActionSetField) a).getField()).getValue().getValue())); - } - /* MPLS */ - else if (((OFActionSetField)a).getField() instanceof OFOxmMplsLabel) { - sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_MPLS_LABEL) - .append(MatchUtils.SET_FIELD_DELIM) - .append(Long.toString(((OFOxmMplsLabel) ((OFActionSetField) a).getField()).getValue().getValue())); - } else if (((OFActionSetField)a).getField() instanceof OFOxmMplsTc) { - sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_MPLS_TC) - .append(MatchUtils.SET_FIELD_DELIM) - .append(Short.toString(((OFOxmMplsTc) ((OFActionSetField) a).getField()).getValue().getValue())); - } else if (((OFActionSetField)a).getField() instanceof OFOxmMplsBos) { - sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_MPLS_BOS) - .append(MatchUtils.SET_FIELD_DELIM) - .append(Boolean.toString(((OFOxmMplsBos) ((OFActionSetField) a).getField()).getValue().getValue())); - } - /* ACTSET_OUTPUT */ - else if (((OFActionSetField)a).getField() instanceof OFOxmActsetOutput) { - sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_ACTSET_OUTPUT) - .append(MatchUtils.SET_FIELD_DELIM) - .append(Integer.toString(((OFOxmActsetOutput) ((OFActionSetField) a).getField()).getValue().getPortNumber())); - } - /* PACKET_TYPE */ - else if (((OFActionSetField)a).getField() instanceof OFOxmPacketType) { // TODO hard-coded "/" as delimiter...fix this - sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_PACKET_TYPE) - .append(MatchUtils.SET_FIELD_DELIM) - .append(Integer.toString(((OFOxmPacketType) ((OFActionSetField) a).getField()).getValue().getNamespace())) - .append("/") - .append(Integer.toString(((OFOxmPacketType) ((OFActionSetField) a).getField()).getValue().getNsType())); - } - /* METADATA */ - else if (((OFActionSetField)a).getField() instanceof OFOxmMetadata) { - sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_METADATA) - .append(MatchUtils.SET_FIELD_DELIM) - .append(Long.toString(((OFOxmMetadata) ((OFActionSetField) a).getField()).getValue().getValue().getValue())); - } else { - log.error("Could not decode Set-Field action field: {}", ((OFActionSetField) a)); - } - break; - case COPY_FIELD: - sb.append(STR_FIELD_COPY).append("=").append(copyFieldToJson((OFActionCopyField) a)); - break; - case METER: - sb.append(STR_METER).append("=").append(Long.toString(((OFActionMeter)a).getMeterId())); - break; - default: - log.error("Could not decode action: {}", a); - break; - } + /** + * Returns a String representation of all the OpenFlow actions. + * @param actions; A list of OFActions to encode into one string + * @return A dpctl-style string of the actions + */ + public static String actionsToString(List<OFAction> actions) { + StringBuilder sb = new StringBuilder(); + for (OFAction a : actions) { + if (sb.length() > 0) { + sb.append(','); + } + switch(a.getType()) { + case OUTPUT: + sb.append(STR_OUTPUT).append("=").append(Integer.toString(((OFActionOutput)a).getPort().getPortNumber())); + break; + case ENQUEUE: + long queue = ((OFActionEnqueue)a).getQueueId(); + sb.append(STR_ENQUEUE).append("=").append(portToString(((OFActionEnqueue)a).getPort())).append(":0x").append(String.format("%02x", queue)); + break; + case STRIP_VLAN: + sb.append(STR_VLAN_STRIP); + break; + case POP_VLAN: + sb.append(STR_VLAN_POP); + break; + case PUSH_VLAN: + sb.append(STR_VLAN_PUSH).append("=").append(Integer.toString(((OFActionPushVlan)a).getEthertype().getValue())); + break; + case SET_VLAN_VID: + sb.append(STR_VLAN_SET_VID).append("=").append(Short.toString(((OFActionSetVlanVid)a).getVlanVid().getVlan())); + break; + case SET_VLAN_PCP: + sb.append(STR_VLAN_SET_PCP).append("=").append(Byte.toString(((OFActionSetVlanPcp)a).getVlanPcp().getValue())); + break; + case SET_QUEUE: + sb.append(STR_QUEUE_SET).append("=").append(Long.toString(((OFActionSetQueue)a).getQueueId())); + case SET_DL_SRC: + sb.append(STR_DL_SRC_SET).append("=").append( ((OFActionSetDlSrc)a).getDlAddr().toString()); + break; + case SET_DL_DST: + sb.append(STR_DL_DST_SET).append("=").append(((OFActionSetDlDst)a).getDlAddr().toString()); + break; + case SET_NW_ECN: + sb.append(STR_NW_ECN_SET).append("=").append(Byte.toString(((OFActionSetNwEcn)a).getNwEcn().getEcnValue())); + break; + case SET_NW_TOS: + sb.append(STR_NW_TOS_SET).append("=").append(Short.toString(((OFActionSetNwTos)a).getNwTos())); + break; + case SET_NW_TTL: + sb.append(STR_NW_TTL_SET).append("=").append(Short.toString(((OFActionSetNwTtl)a).getNwTtl())); + break; + case DEC_NW_TTL: + sb.append(STR_NW_TTL_DEC); + break; + case SET_MPLS_LABEL: + sb.append(STR_MPLS_LABEL_SET).append("=").append(Long.toString(((OFActionSetMplsLabel)a).getMplsLabel())); + break; + case SET_MPLS_TC: + sb.append(STR_MPLS_TC_SET).append("=").append(Short.toString(((OFActionSetMplsTc)a).getMplsTc())); + break; + case SET_MPLS_TTL: + sb.append(STR_MPLS_TTL_SET).append("=").append(Short.toString(((OFActionSetMplsTtl)a).getMplsTtl())); + break; + case DEC_MPLS_TTL: + sb.append(STR_MPLS_TTL_DEC); + break; + case PUSH_MPLS: + sb.append(STR_MPLS_PUSH).append("=").append(Integer.toString(((OFActionPushMpls)a).getEthertype().getValue())); + break; + case POP_MPLS: + sb.append(STR_MPLS_POP).append("=").append(Integer.toString(((OFActionPopMpls)a).getEthertype().getValue())); + break; + case SET_NW_SRC: + sb.append(STR_NW_SRC_SET).append("=").append(((OFActionSetNwSrc)a).getNwAddr().toString()); + break; + case SET_NW_DST: + sb.append(STR_NW_DST_SET).append("=").append(((OFActionSetNwDst)a).getNwAddr().toString()); + break; + case SET_TP_SRC: + sb.append(STR_TP_SRC_SET).append("=").append(((OFActionSetTpSrc)a).getTpPort().toString()); + break; + case SET_TP_DST: + sb.append(STR_TP_DST_SET).append("=").append(((OFActionSetTpDst)a).getTpPort().toString()); + break; + case COPY_TTL_IN: + sb.append(STR_TTL_IN_COPY); + break; + case COPY_TTL_OUT: + sb.append(STR_TTL_OUT_COPY); + break; + case PUSH_PBB: + sb.append(STR_PBB_PUSH).append("=").append(Integer.toString(((OFActionPushPbb)a).getEthertype().getValue())); + break; + case POP_PBB: + sb.append(STR_PBB_POP); + break; + case EXPERIMENTER: + sb.append(STR_EXPERIMENTER).append("=").append(Long.toString(((OFActionExperimenter)a).getExperimenter())); + break; + case GROUP: + sb.append(STR_GROUP).append("=").append(Integer.toString(((OFActionGroup)a).getGroup().getGroupNumber())); + break; + case SET_FIELD: + log.debug("Got Set-Field action. Setting {}", ((OFActionSetField)a)); + /* ARP */ + if (((OFActionSetField)a).getField() instanceof OFOxmArpOp) { + sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_ARP_OPCODE) + .append(MatchUtils.SET_FIELD_DELIM) + .append(Integer.toString(((OFOxmArpOp) ((OFActionSetField) a).getField()).getValue().getOpcode())); + } else if (((OFActionSetField)a).getField() instanceof OFOxmArpSha) { + sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_ARP_SHA) + .append(MatchUtils.SET_FIELD_DELIM) + .append(((OFOxmArpSha) ((OFActionSetField) a).getField()).getValue().toString()); // macaddress formats string already + } else if (((OFActionSetField)a).getField() instanceof OFOxmArpTha) { + sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_ARP_DHA) + .append(MatchUtils.SET_FIELD_DELIM) + .append(((OFOxmArpTha) ((OFActionSetField) a).getField()).getValue().toString()); + } else if (((OFActionSetField)a).getField() instanceof OFOxmArpSpa) { + sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_ARP_SPA) + .append(MatchUtils.SET_FIELD_DELIM) + .append(((OFOxmArpSpa) ((OFActionSetField) a).getField()).getValue().toString()); // ipaddress formats string already + } else if (((OFActionSetField)a).getField() instanceof OFOxmArpTpa) { + sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_ARP_DPA) + .append(MatchUtils.SET_FIELD_DELIM) + .append(((OFOxmArpTpa) ((OFActionSetField) a).getField()).getValue().toString()); + } else if (((OFActionSetField)a).getField() instanceof OFOxmIpv6NdSll) { + sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_IPV6_ND_SLL) + .append(MatchUtils.SET_FIELD_DELIM) + .append(((OFOxmIpv6NdSll) ((OFActionSetField) a).getField()).getValue().toString()); // macaddress formats string already + } else if (((OFActionSetField)a).getField() instanceof OFOxmIpv6NdTll) { + sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_IPV6_ND_TLL) + .append(MatchUtils.SET_FIELD_DELIM) + .append(((OFOxmIpv6NdTll) ((OFActionSetField) a).getField()).getValue().toString()); // macaddress formats string already + } else if (((OFActionSetField)a).getField() instanceof OFOxmIpv6NdTarget) { + sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_IPV6_ND_TARGET) + .append(MatchUtils.SET_FIELD_DELIM) + .append(((OFOxmIpv6NdTarget) ((OFActionSetField) a).getField()).getValue().toString()); + } + /* DATA LAYER */ + else if (((OFActionSetField)a).getField() instanceof OFOxmEthType) { + sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_DL_TYPE) + .append(MatchUtils.SET_FIELD_DELIM) + .append(Integer.toString(((OFOxmEthType) ((OFActionSetField) a).getField()).getValue().getValue())); + } else if (((OFActionSetField)a).getField() instanceof OFOxmEthSrc) { + sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_DL_SRC) + .append(MatchUtils.SET_FIELD_DELIM) + .append(((OFOxmEthSrc) ((OFActionSetField) a).getField()).getValue().toString()); + } else if (((OFActionSetField)a).getField() instanceof OFOxmEthDst) { + sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_DL_DST) + .append(MatchUtils.SET_FIELD_DELIM) + .append(((OFOxmEthDst) ((OFActionSetField) a).getField()).getValue().toString()); + } else if (((OFActionSetField)a).getField() instanceof OFOxmVlanVid) { + sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_DL_VLAN) + .append(MatchUtils.SET_FIELD_DELIM) + .append(Short.toString(((OFOxmVlanVid) ((OFActionSetField) a).getField()).getValue().getVlan())); + } else if (((OFActionSetField)a).getField() instanceof OFOxmVlanPcp) { + sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_DL_VLAN_PCP) + .append(MatchUtils.SET_FIELD_DELIM) + .append(Byte.toString(((OFOxmVlanPcp) ((OFActionSetField) a).getField()).getValue().getValue())); + } + /* ICMP */ + else if (((OFActionSetField)a).getField() instanceof OFOxmIcmpv4Code) { + sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_ICMP_CODE) + .append(MatchUtils.SET_FIELD_DELIM) + .append(Short.toString(((OFOxmIcmpv4Code) ((OFActionSetField) a).getField()).getValue().getCode())); + } else if (((OFActionSetField)a).getField() instanceof OFOxmIcmpv4Type) { + sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_ICMP_TYPE) + .append(MatchUtils.SET_FIELD_DELIM) + .append(Short.toString(((OFOxmIcmpv4Type) ((OFActionSetField) a).getField()).getValue().getType())); + } else if (((OFActionSetField)a).getField() instanceof OFOxmIcmpv6Code) { + sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_ICMPV6_CODE) + .append(MatchUtils.SET_FIELD_DELIM) + .append(Short.toString(((OFOxmIcmpv6Code) ((OFActionSetField) a).getField()).getValue().getRaw())); + } else if (((OFActionSetField)a).getField() instanceof OFOxmIcmpv6Type) { + sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_ICMPV6_TYPE) + .append(MatchUtils.SET_FIELD_DELIM) + .append(Short.toString(((OFOxmIcmpv6Type) ((OFActionSetField) a).getField()).getValue().getRaw())); + } + /* NETWORK LAYER */ + else if (((OFActionSetField)a).getField() instanceof OFOxmIpProto) { + sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_NW_PROTO) + .append(MatchUtils.SET_FIELD_DELIM) + .append(Short.toString(((OFOxmIpProto) ((OFActionSetField) a).getField()).getValue().getIpProtocolNumber())); + } else if (((OFActionSetField)a).getField() instanceof OFOxmIpv4Src) { + sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_NW_SRC) + .append(MatchUtils.SET_FIELD_DELIM) + .append(((OFOxmIpv4Src) ((OFActionSetField) a).getField()).getValue().toString()); + } else if (((OFActionSetField)a).getField() instanceof OFOxmIpv4Dst) { + sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_NW_DST) + .append(MatchUtils.SET_FIELD_DELIM) + .append(((OFOxmIpv4Dst) ((OFActionSetField) a).getField()).getValue().toString()); + } else if (((OFActionSetField)a).getField() instanceof OFOxmIpv6Src) { + sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_IPV6_SRC) + .append(MatchUtils.SET_FIELD_DELIM) + .append(((OFOxmIpv6Src) ((OFActionSetField) a).getField()).getValue().toString()); + } else if (((OFActionSetField)a).getField() instanceof OFOxmIpv6Dst) { + sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_IPV6_DST) + .append(MatchUtils.SET_FIELD_DELIM) + .append(((OFOxmIpv6Dst) ((OFActionSetField) a).getField()).getValue().toString()); + } else if (((OFActionSetField)a).getField() instanceof OFOxmIpv6Flabel) { + sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_IPV6_FLOW_LABEL) + .append(MatchUtils.SET_FIELD_DELIM) + .append(((OFOxmIpv6Flabel) ((OFActionSetField) a).getField()).getValue().toString()); + } else if (((OFActionSetField)a).getField() instanceof OFOxmIpv6Exthdr) { + sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_IPV6_EXTHDR) + .append(MatchUtils.SET_FIELD_DELIM) + .append(((OFOxmIpv6Exthdr) ((OFActionSetField) a).getField()).getValue().toString()); + } else if (((OFActionSetField)a).getField() instanceof OFOxmIpEcn) { + sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_NW_ECN) + .append(MatchUtils.SET_FIELD_DELIM) + .append(Byte.toString(((OFOxmIpEcn) ((OFActionSetField) a).getField()).getValue().getEcnValue())); + } else if (((OFActionSetField)a).getField() instanceof OFOxmIpDscp) { + sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_NW_DSCP) + .append(MatchUtils.SET_FIELD_DELIM) + .append(Byte.toString(((OFOxmIpDscp) ((OFActionSetField) a).getField()).getValue().getDscpValue())); + } + /* TRANSPORT LAYER, TCP, UDP, and SCTP */ + else if (((OFActionSetField)a).getField() instanceof OFOxmTcpSrc) { + sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_TCP_SRC) + .append(MatchUtils.SET_FIELD_DELIM) + .append(Integer.toString(((OFOxmTcpSrc) ((OFActionSetField) a).getField()).getValue().getPort())); + } else if (((OFActionSetField)a).getField() instanceof OFOxmTcpDst) { + sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_TCP_DST) + .append(MatchUtils.SET_FIELD_DELIM) + .append(Integer.toString(((OFOxmTcpDst) ((OFActionSetField) a).getField()).getValue().getPort())); + } else if (((OFActionSetField)a).getField() instanceof OFOxmUdpSrc) { + sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_UDP_SRC) + .append(MatchUtils.SET_FIELD_DELIM) + .append(Integer.toString(((OFOxmUdpSrc) ((OFActionSetField) a).getField()).getValue().getPort())); + } else if (((OFActionSetField)a).getField() instanceof OFOxmUdpDst) { + sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_UDP_DST) + .append(MatchUtils.SET_FIELD_DELIM) + .append(Integer.toString(((OFOxmUdpDst) ((OFActionSetField) a).getField()).getValue().getPort())); + } else if (((OFActionSetField)a).getField() instanceof OFOxmSctpSrc) { + sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_SCTP_SRC) + .append(MatchUtils.SET_FIELD_DELIM) + .append(Integer.toString(((OFOxmSctpSrc) ((OFActionSetField) a).getField()).getValue().getPort())); + } else if (((OFActionSetField)a).getField() instanceof OFOxmSctpDst) { + sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_SCTP_DST) + .append(MatchUtils.SET_FIELD_DELIM) + .append(Integer.toString(((OFOxmSctpDst) ((OFActionSetField) a).getField()).getValue().getPort())); + } else if (((OFActionSetField)a).getField() instanceof OFOxmTcpFlags) { + sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_TCP_FLAGS) + .append(MatchUtils.SET_FIELD_DELIM) + .append(Integer.toString(((OFOxmTcpFlags) ((OFActionSetField) a).getField()).getValue().getValue())); + } + /* MPLS */ + else if (((OFActionSetField)a).getField() instanceof OFOxmMplsLabel) { + sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_MPLS_LABEL) + .append(MatchUtils.SET_FIELD_DELIM) + .append(Long.toString(((OFOxmMplsLabel) ((OFActionSetField) a).getField()).getValue().getValue())); + } else if (((OFActionSetField)a).getField() instanceof OFOxmMplsTc) { + sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_MPLS_TC) + .append(MatchUtils.SET_FIELD_DELIM) + .append(Short.toString(((OFOxmMplsTc) ((OFActionSetField) a).getField()).getValue().getValue())); + } else if (((OFActionSetField)a).getField() instanceof OFOxmMplsBos) { + sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_MPLS_BOS) + .append(MatchUtils.SET_FIELD_DELIM) + .append(Boolean.toString(((OFOxmMplsBos) ((OFActionSetField) a).getField()).getValue().getValue())); + } + /* ACTSET_OUTPUT */ + else if (((OFActionSetField)a).getField() instanceof OFOxmActsetOutput) { + sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_ACTSET_OUTPUT) + .append(MatchUtils.SET_FIELD_DELIM) + .append(Integer.toString(((OFOxmActsetOutput) ((OFActionSetField) a).getField()).getValue().getPortNumber())); + } + /* PACKET_TYPE */ + else if (((OFActionSetField)a).getField() instanceof OFOxmPacketType) { // TODO hard-coded "/" as delimiter...fix this + sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_PACKET_TYPE) + .append(MatchUtils.SET_FIELD_DELIM) + .append(Integer.toString(((OFOxmPacketType) ((OFActionSetField) a).getField()).getValue().getNamespace())) + .append("/") + .append(Integer.toString(((OFOxmPacketType) ((OFActionSetField) a).getField()).getValue().getNsType())); + } + /* METADATA */ + else if (((OFActionSetField)a).getField() instanceof OFOxmMetadata) { + sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_METADATA) + .append(MatchUtils.SET_FIELD_DELIM) + .append(Long.toString(((OFOxmMetadata) ((OFActionSetField) a).getField()).getValue().getValue().getValue())); + } else { + log.error("Could not decode Set-Field action field: {}", ((OFActionSetField) a)); + } + break; + case COPY_FIELD: + sb.append(STR_FIELD_COPY).append("=").append(copyFieldToJson((OFActionCopyField) a)); + break; + case METER: + sb.append(STR_METER).append("=").append(Long.toString(((OFActionMeter)a).getMeterId())); + break; + default: + log.error("Could not decode action: {}", a); + break; + } - } + } - return sb.toString(); - } + return sb.toString(); + } - public static List<OFAction> fromString(String s, OFVersion v) { - List<OFAction> actions = new LinkedList<OFAction>(); - OFFactory f = OFFactories.getFactory(v); - if (s != null && !s.trim().isEmpty()) { - s = s.toLowerCase(); - String[] bigStringSplit = s.split(","); // split into separate action=value or action=key@value pairs + public static List<OFAction> fromString(String s, OFVersion v) { + List<OFAction> actions = new LinkedList<OFAction>(); + OFFactory f = OFFactories.getFactory(v); + if (s != null && !s.trim().isEmpty()) { + s = s.toLowerCase(); + String[] bigStringSplit = s.split(","); // split into separate action=value or action=key@value pairs - String[] tmp; - ArrayDeque<String[]> actionToDecode = new ArrayDeque<String[]>(); - for (int i = 0; i < bigStringSplit.length; i++) { - tmp = bigStringSplit[i].split("="); // split into separate [action, value] or [action, key@value] singles - if (tmp.length != 2) { - log.debug("Token " + bigStringSplit[i] + " does not have form 'key=value' parsing " + s); - } - actionToDecode.add(tmp); // actionToDecode contains [key, value] pairs. Create a queue of pairs to process. - } + String[] tmp; + ArrayDeque<String[]> actionToDecode = new ArrayDeque<String[]>(); + for (int i = 0; i < bigStringSplit.length; i++) { + tmp = bigStringSplit[i].split("="); // split into separate [action, value] or [action, key@value] singles + if (tmp.length != 2) { + log.debug("Token " + bigStringSplit[i] + " does not have form 'key=value' parsing " + s); + } + actionToDecode.add(tmp); // actionToDecode contains [key, value] pairs. Create a queue of pairs to process. + } - while (!actionToDecode.isEmpty()) { - String[] keyPair = actionToDecode.pollFirst(); - String key; - String pair; - if (keyPair.length != 2) { - log.debug("[Key, Value] {} does not have form 'key=value' parsing, which is okay for some actions e.g. 'pop_vlan'.", keyPair); - key = keyPair[0]; // could the be case of a constant actions (e.g. copy_ttl_in) - pair = ""; - } else { - key = keyPair[0]; - pair = keyPair[1]; - } + while (!actionToDecode.isEmpty()) { + String[] keyPair = actionToDecode.pollFirst(); + String key; + String pair; + if (keyPair.length != 2) { + log.debug("[Key, Value] {} does not have form 'key=value' parsing, which is okay for some actions e.g. 'pop_vlan'.", keyPair); + key = keyPair[0]; // could the be case of a constant actions (e.g. copy_ttl_in) + pair = ""; + } else { + key = keyPair[0]; + pair = keyPair[1]; + } - OFAction a = null; - try { - switch (key) { - case STR_OUTPUT: - a = decode_output(pair, v); - break; - case STR_ENQUEUE: - a = decode_enqueue(pair, v); - break; - case STR_DL_SRC_SET: - a = decode_set_src_mac(pair, v); - break; - case STR_DL_DST_SET: - a = decode_set_dst_mac(pair, v); - break; - case STR_EXPERIMENTER: - //no-op. Not implemented - log.error("OFAction EXPERIMENTER not implemented."); - break; - case STR_FIELD_SET: /* ONLY OF1.1+ should get in here. These should only be header fields valid within a set-field. */ - String[] actionData = pair.split(MatchUtils.SET_FIELD_DELIM); - if (actionData.length != 2) { - throw new IllegalArgumentException("[Action, Data] " + keyPair + " does not have form 'action=data'" + actionData); - } + OFAction a = null; + try { + switch (key) { + case STR_OUTPUT: + a = decode_output(pair, v); + break; + case STR_ENQUEUE: + a = decode_enqueue(pair, v); + break; + case STR_DL_SRC_SET: + a = decode_set_src_mac(pair, v); + break; + case STR_DL_DST_SET: + a = decode_set_dst_mac(pair, v); + break; + case STR_EXPERIMENTER: + //no-op. Not implemented + log.error("OFAction EXPERIMENTER not implemented."); + break; + case STR_FIELD_SET: /* ONLY OF1.1+ should get in here. These should only be header fields valid within a set-field. */ + String[] actionData = pair.split(MatchUtils.SET_FIELD_DELIM); + if (actionData.length != 2) { + throw new IllegalArgumentException("[Action, Data] " + keyPair + " does not have form 'action=data'" + actionData); + } - switch (actionData[0]) { - case MatchUtils.STR_ARP_OPCODE: - if (actionData[1].startsWith("0x")) { - a = f.actions().buildSetField() - .setField(f.oxms().buildArpOp().setValue(ArpOpcode.of(Integer.parseInt(actionData[1].replaceFirst("0x", ""), 16))).build()) - .build(); - } else { - a = f.actions().buildSetField() - .setField(f.oxms().buildArpOp().setValue(ArpOpcode.of(Integer.parseInt(actionData[1]))).build()) - .build(); - } - break; - case MatchUtils.STR_ARP_SHA: - a = f.actions().buildSetField() - .setField(f.oxms().buildArpSha().setValue(MacAddress.of(actionData[1])).build()) - .build(); - break; - case MatchUtils.STR_ARP_DHA: - a = f.actions().buildSetField() - .setField(f.oxms().buildArpTha().setValue(MacAddress.of(actionData[1])).build()) - .build(); - break; - case MatchUtils.STR_ARP_SPA: - a = f.actions().buildSetField() - .setField(f.oxms().buildArpSpa().setValue(IPv4Address.of(actionData[1])).build()) - .build(); - break; - case MatchUtils.STR_ARP_DPA: - a = f.actions().buildSetField() - .setField(f.oxms().buildArpTpa().setValue(IPv4Address.of(actionData[1])).build()) - .build(); - break; - case MatchUtils.STR_IPV6_ND_SLL: - a = f.actions().buildSetField() - .setField(f.oxms().buildIpv6NdSll().setValue(MacAddress.of(actionData[1])).build()) - .build(); - break; - case MatchUtils.STR_IPV6_ND_TLL: - a = f.actions().buildSetField() - .setField(f.oxms().buildIpv6NdTll().setValue(MacAddress.of(actionData[1])).build()) - .build(); - break; - case MatchUtils.STR_IPV6_ND_TARGET: - a = f.actions().buildSetField() - .setField(f.oxms().buildIpv6NdTarget().setValue(IPv6Address.of(actionData[1])).build()) - .build(); - break; - case MatchUtils.STR_DL_TYPE: - if (actionData[1].startsWith("0x")) { - a = f.actions().buildSetField() - .setField(f.oxms().buildEthType().setValue(EthType.of(Integer.parseInt(actionData[1].replaceFirst("0x", ""), 16))).build()) - .build(); - } else { - a = f.actions().buildSetField() - .setField(f.oxms().buildEthType().setValue(EthType.of(Integer.parseInt(actionData[1]))).build()) - .build(); - } - break; - case MatchUtils.STR_DL_SRC: - a = f.actions().buildSetField() - .setField(f.oxms().buildEthSrc().setValue(MacAddress.of(actionData[1])).build()) - .build(); - break; - case MatchUtils.STR_DL_DST: - a = f.actions().buildSetField() - .setField(f.oxms().buildEthDst().setValue(MacAddress.of(actionData[1])).build()) - .build(); - break; - case MatchUtils.STR_DL_VLAN: - if (actionData[1].startsWith("0x")) { - a = f.actions().buildSetField() - .setField(f.oxms().buildVlanVid().setValue(OFVlanVidMatch.ofVlan(Integer.parseInt(actionData[1].replaceFirst("0x", ""), 16))).build()) - .build(); - } else { - a = f.actions().buildSetField() - .setField(f.oxms().buildVlanVid().setValue(OFVlanVidMatch.ofVlan(Integer.parseInt(actionData[1]))).build()) - .build(); - } - break; - case MatchUtils.STR_DL_VLAN_PCP: - if (actionData[1].startsWith("0x")) { - a = f.actions().buildSetField() - .setField(f.oxms().buildVlanPcp().setValue(VlanPcp.of(Byte.parseByte(actionData[1].replaceFirst("0x", ""), 16))).build()) - .build(); - } else { - a = f.actions().buildSetField() - .setField(f.oxms().buildVlanPcp().setValue(VlanPcp.of(Byte.parseByte(actionData[1]))).build()) - .build(); - } - break; - case MatchUtils.STR_ICMP_CODE: - if (actionData[1].startsWith("0x")) { - a = f.actions().buildSetField() - .setField(f.oxms().buildIcmpv4Code().setValue(ICMPv4Code.of(Short.parseShort(actionData[1].replaceFirst("0x", ""), 16))).build()) - .build(); - } else { - a = f.actions().buildSetField() - .setField(f.oxms().buildIcmpv4Code().setValue(ICMPv4Code.of(Short.parseShort(actionData[1]))).build()) - .build(); - } - break; - case MatchUtils.STR_ICMP_TYPE: - if (actionData[1].startsWith("0x")) { - a = f.actions().buildSetField() - .setField(f.oxms().buildIcmpv4Type().setValue(ICMPv4Type.of(Short.parseShort(actionData[1].replaceFirst("0x", ""), 16))).build()) - .build(); - } else { - a = f.actions().buildSetField() - .setField(f.oxms().buildIcmpv4Type().setValue(ICMPv4Type.of(Short.parseShort(actionData[1]))).build()) - .build(); - } - break; - case MatchUtils.STR_ICMPV6_CODE: - if (actionData[1].startsWith("0x")) { - a = f.actions().buildSetField() - .setField(f.oxms().buildIcmpv6Code().setValue(U8.of(Short.parseShort(actionData[1].replaceFirst("0x", ""), 16))).build()) - .build(); - } else { - a = f.actions().buildSetField() - .setField(f.oxms().buildIcmpv6Code().setValue(U8.of(Short.parseShort(actionData[1]))).build()) - .build(); - } - break; - case MatchUtils.STR_ICMPV6_TYPE: - if (actionData[1].startsWith("0x")) { - a = f.actions().buildSetField() - .setField(f.oxms().buildIcmpv6Type().setValue(U8.of(Short.parseShort(actionData[1].replaceFirst("0x", ""), 16))).build()) - .build(); - } else { - a = f.actions().buildSetField() - .setField(f.oxms().buildIcmpv6Type().setValue(U8.of(Short.parseShort(actionData[1]))).build()) - .build(); - } - break; - case MatchUtils.STR_NW_PROTO: - if (actionData[1].startsWith("0x")) { - a = f.actions().buildSetField() - .setField(f.oxms().buildIpProto().setValue(IpProtocol.of(Short.parseShort(actionData[1].replaceFirst("0x", ""), 16))).build()) - .build(); - } else { - a = f.actions().buildSetField() - .setField(f.oxms().buildIpProto().setValue(IpProtocol.of(Short.parseShort(actionData[1]))).build()) - .build(); - } - break; - case MatchUtils.STR_NW_SRC: - a = f.actions().buildSetField() - .setField(f.oxms().buildIpv4Src().setValue(IPv4Address.of(actionData[1])).build()) - .build(); - break; - case MatchUtils.STR_NW_DST: - a = f.actions().buildSetField() - .setField(f.oxms().buildIpv4Dst().setValue(IPv4Address.of(actionData[1])).build()) - .build(); - break; - case MatchUtils.STR_IPV6_SRC: - a = f.actions().buildSetField() - .setField(f.oxms().buildIpv6Src().setValue(IPv6Address.of(actionData[1])).build()) - .build(); - break; - case MatchUtils.STR_IPV6_DST: - a = f.actions().buildSetField() - .setField(f.oxms().buildIpv6Dst().setValue(IPv6Address.of(actionData[1])).build()) - .build(); - break; - case MatchUtils.STR_IPV6_FLOW_LABEL: - if (actionData[1].startsWith("0x")) { - a = f.actions().buildSetField() - .setField(f.oxms().buildIpv6Flabel().setValue(IPv6FlowLabel.of(Integer.parseInt(actionData[1].replaceFirst("0x", ""), 16))).build()) - .build(); - } else { - a = f.actions().buildSetField() - .setField(f.oxms().buildIpv6Flabel().setValue(IPv6FlowLabel.of(Integer.parseInt(actionData[1]))).build()) - .build(); - } - break; - case MatchUtils.STR_NW_ECN: - if (actionData[1].startsWith("0x")) { - a = f.actions().buildSetField() - .setField(f.oxms().buildIpEcn().setValue(IpEcn.of(Byte.parseByte(actionData[1].replaceFirst("0x", ""), 16))).build()) - .build(); - } else { - a = f.actions().buildSetField() - .setField(f.oxms().buildIpEcn().setValue(IpEcn.of(Byte.parseByte(actionData[1]))).build()) - .build(); - } - break; - case MatchUtils.STR_NW_DSCP: - if (actionData[1].startsWith("0x")) { - a = f.actions().buildSetField() - .setField(f.oxms().buildIpDscp().setValue(IpDscp.of(Byte.parseByte(actionData[1].replaceFirst("0x", ""), 16))).build()) - .build(); - } else { - a = f.actions().buildSetField() - .setField(f.oxms().buildIpDscp().setValue(IpDscp.of(Byte.parseByte(actionData[1]))).build()) - .build(); - } - break; - case MatchUtils.STR_SCTP_SRC: - a = f.actions().buildSetField() - .setField(f.oxms().buildSctpSrc().setValue(TransportPort.of(Integer.parseInt(actionData[1]))).build()) - .build(); - break; - case MatchUtils.STR_SCTP_DST: - a = f.actions().buildSetField() - .setField(f.oxms().buildSctpDst().setValue(TransportPort.of(Integer.parseInt(actionData[1]))).build()) - .build(); - break; - case MatchUtils.STR_TCP_SRC: - a = f.actions().buildSetField() - .setField(f.oxms().buildTcpSrc().setValue(TransportPort.of(Integer.parseInt(actionData[1]))).build()) - .build(); - break; - case MatchUtils.STR_TCP_DST: - a = f.actions().buildSetField() - .setField(f.oxms().buildTcpDst().setValue(TransportPort.of(Integer.parseInt(actionData[1]))).build()) - .build(); - break; - case MatchUtils.STR_UDP_SRC: - a = f.actions().buildSetField() - .setField(f.oxms().buildUdpSrc().setValue(TransportPort.of(Integer.parseInt(actionData[1]))).build()) - .build(); - break; - case MatchUtils.STR_UDP_DST: - a = f.actions().buildSetField() - .setField(f.oxms().buildUdpDst().setValue(TransportPort.of(Integer.parseInt(actionData[1]))).build()) - .build(); - break; - case MatchUtils.STR_MPLS_LABEL: - if (actionData[1].startsWith("0x")) { - a = f.actions().buildSetField() - .setField(f.oxms().buildMplsLabel().setValue(U32.of(Long.parseLong(actionData[1].replaceFirst("0x", ""), 16))).build()) - .build(); - } else { - a = f.actions().buildSetField() - .setField(f.oxms().buildMplsLabel().setValue(U32.of(Long.parseLong(actionData[1]))).build()) - .build(); - } - break; - case MatchUtils.STR_MPLS_TC: - if (actionData[1].startsWith("0x")) { - a = f.actions().buildSetField() - .setField(f.oxms().buildMplsTc().setValue(U8.of(Short.parseShort(actionData[1].replaceFirst("0x", ""), 16))).build()) - .build(); - } else { - a = f.actions().buildSetField() - .setField(f.oxms().buildMplsTc().setValue(U8.of(Short.parseShort(actionData[1]))).build()) - .build(); - } - break; - case MatchUtils.STR_MPLS_BOS: - a = f.actions().buildSetField() - .setField(f.oxms().buildMplsBos().setValue(OFBooleanValue.of(Boolean.parseBoolean(actionData[1]))).build()) // interprets anything other than "true" as false - .build(); - break; - case MatchUtils.STR_METADATA: - if (actionData[1].startsWith("0x")) { - a = f.actions().buildSetField() - .setField(f.oxms().buildMetadata().setValue(OFMetadata.of(U64.of(Long.parseLong(actionData[1].replaceFirst("0x", ""), 16)))).build()) - .build(); - } else { - a = f.actions().buildSetField() - .setField(f.oxms().buildMetadata().setValue(OFMetadata.of(U64.of(Long.parseLong(actionData[1])))).build()) - .build(); - } - break; - case MatchUtils.STR_ACTSET_OUTPUT: - if (actionData[1].startsWith("0x")) { - a = f.actions().buildSetField() - .setField(f.oxms().buildActsetOutput().setValue(OFPort.of(U32.of(Long.parseLong(actionData[1].replaceFirst("0x", ""), 16)).getRaw())).build()) - .build(); - } else { - a = f.actions().buildSetField() - .setField(f.oxms().buildActsetOutput().setValue(OFPort.of(U32.of(Long.parseLong(actionData[1])).getRaw())).build()) - .build(); - } - break; - case MatchUtils.STR_TCP_FLAGS: - if (actionData[1].startsWith("0x")) { - a = f.actions().buildSetField() - .setField(f.oxms().buildTcpFlags().setValue(U16.of(Integer.parseInt(actionData[1].replaceFirst("0x", ""), 16))).build()) - .build(); - } else { - a = f.actions().buildSetField() - .setField(f.oxms().buildTcpFlags().setValue(U16.of(Integer.parseInt(actionData[1]))).build()) - .build(); - } - break; - default: - log.error("Unexpected OF1.2+ setfield '{}'", actionData); - break; - } - break; - case STR_GROUP: - if (pair.startsWith("0x")) { - a = f.actions().buildGroup() - .setGroup(OFGroup.of(Integer.parseInt(pair.replaceFirst("0x", ""), 16))) - .build(); - } else { - a = f.actions().buildGroup() - .setGroup(OFGroup.of(Integer.parseInt(pair))) - .build(); - } - break; - case STR_MPLS_LABEL_SET: - if (pair.startsWith("0x")) { - a = f.actions().buildSetMplsLabel() - .setMplsLabel(Long.parseLong(pair.replaceFirst("0x", ""), 16)) - .build(); - } else { - a = f.actions().buildSetMplsLabel() - .setMplsLabel(Long.parseLong(pair)) - .build(); - } - break; - case STR_MPLS_POP: - if (pair.startsWith("0x")) { - a = f.actions().buildPopMpls() - .setEthertype(EthType.of(Integer.parseInt(pair.replaceFirst("0x", ""), 16))) - .build(); - } else { - a = f.actions().buildPopMpls() - .setEthertype(EthType.of(Integer.parseInt(pair))) - .build(); - } - break; - case STR_MPLS_PUSH: - if (pair.startsWith("0x")) { - a = f.actions().buildPushMpls() - .setEthertype(EthType.of(Integer.parseInt(pair.replaceFirst("0x", ""), 16))) - .build(); - } else { - a = f.actions().buildPushMpls() - .setEthertype(EthType.of(Integer.parseInt(pair))) - .build(); - } - break; - case STR_MPLS_TC_SET: - if (pair.startsWith("0x")) { - a = f.actions().buildSetMplsTc() - .setMplsTc(Short.parseShort(pair.replaceFirst("0x", ""), 16)) - .build(); - } else { - a = f.actions().buildSetMplsTc() - .setMplsTc(Short.parseShort(pair)) - .build(); - } - break; - case STR_MPLS_TTL_DEC: - a = f.actions().decMplsTtl(); - break; - case STR_MPLS_TTL_SET: - if (pair.startsWith("0x")) { - a = f.actions().buildSetMplsTtl() - .setMplsTtl(Short.parseShort(pair.replaceFirst("0x", ""), 16)) - .build(); - } else { - a = f.actions().buildSetMplsTtl() - .setMplsTtl(Short.parseShort(pair)) - .build(); - } - break; - case STR_NW_TOS_SET: - a = decode_set_tos_bits(pair, v); // should only be used by OF1.0 - break; - case STR_NW_SRC_SET: - a = decode_set_src_ip(pair, v); - break; - case STR_NW_DST_SET: - a = decode_set_dst_ip(pair, v); - break; - case STR_NW_ECN_SET: // loxi does not support DSCP set for OF1.1 - if (pair.startsWith("0x")) { - a = f.actions().buildSetNwEcn() - .setNwEcn(IpEcn.of(Byte.parseByte(pair.replaceFirst("0x", ""), 16))) - .build(); - } else { - a = f.actions().buildSetNwEcn() - .setNwEcn(IpEcn.of(Byte.parseByte(pair))) - .build(); - } - break; - case STR_NW_TTL_DEC: - a = f.actions().decNwTtl(); - break; - case STR_NW_TTL_SET: - if (pair.startsWith("0x")) { - a = f.actions().buildSetNwTtl() - .setNwTtl(Short.parseShort(pair.replaceFirst("0x", ""), 16)) - .build(); - } else { - a = f.actions().buildSetNwTtl() - .setNwTtl(Short.parseShort(pair)) - .build(); - } - break; - case STR_PBB_POP: - a = f.actions().popPbb(); - break; - case STR_PBB_PUSH: - if (pair.startsWith("0x")) { - a = f.actions().buildPushPbb() - .setEthertype(EthType.of(Integer.parseInt(pair.replaceFirst("0x", ""), 16))) - .build(); - } else { - a = f.actions().buildPushPbb() - .setEthertype(EthType.of(Integer.parseInt(pair))) - .build(); - } - break; - case STR_QUEUE_SET: - if (pair.startsWith("0x")) { - a = f.actions().buildSetQueue() - .setQueueId(Long.parseLong(pair.replaceFirst("0x", ""), 16)) - .build(); - } else { - a = f.actions().buildSetQueue() - .setQueueId(Long.parseLong(pair)) - .build(); - } - break; - case STR_TP_SRC_SET: - a = decode_set_src_port(pair, v); - break; - case STR_TP_DST_SET: - a = decode_set_dst_port(pair, v); - break; - case STR_TTL_IN_COPY: - a = f.actions().copyTtlIn(); - break; - case STR_TTL_OUT_COPY: - a = f.actions().copyTtlOut(); - break; - case STR_VLAN_POP: - a = f.actions().popVlan(); - break; - case STR_VLAN_PUSH: - if (pair.startsWith("0x")) { - a = f.actions().buildPushVlan() - .setEthertype(EthType.of(Integer.parseInt(pair.replaceFirst("0x", ""), 16))) - .build(); - } else { - a = f.actions().buildPushVlan() - .setEthertype(EthType.of(Integer.parseInt(pair))) - .build(); - } - break; - case STR_VLAN_STRIP: - a = f.actions().stripVlan(); - break; - case STR_VLAN_SET_VID: - a = decode_set_vlan_id(pair, v); - break; - case STR_VLAN_SET_PCP: - a = decode_set_vlan_priority(pair, v); - break; - case STR_METER: - if (pair.startsWith("0x")) { - a = f.actions().buildMeter() - .setMeterId(Long.parseLong(pair.replaceFirst("0x", ""), 16)) - .build(); - } else { - a = f.actions().buildMeter() - .setMeterId(Long.parseLong(pair)) - .build(); - } - break; - case STR_FIELD_COPY: - a = (OFAction) copyFieldFromJson(pair, f.getVersion()); - break; - default: - log.error("Unexpected action key '{}'", keyPair); - break; - } + switch (actionData[0]) { + case MatchUtils.STR_ARP_OPCODE: + if (actionData[1].startsWith("0x")) { + a = f.actions().buildSetField() + .setField(f.oxms().buildArpOp().setValue(ArpOpcode.of(Integer.parseInt(actionData[1].replaceFirst("0x", ""), 16))).build()) + .build(); + } else { + a = f.actions().buildSetField() + .setField(f.oxms().buildArpOp().setValue(ArpOpcode.of(Integer.parseInt(actionData[1]))).build()) + .build(); + } + break; + case MatchUtils.STR_ARP_SHA: + a = f.actions().buildSetField() + .setField(f.oxms().buildArpSha().setValue(MacAddress.of(actionData[1])).build()) + .build(); + break; + case MatchUtils.STR_ARP_DHA: + a = f.actions().buildSetField() + .setField(f.oxms().buildArpTha().setValue(MacAddress.of(actionData[1])).build()) + .build(); + break; + case MatchUtils.STR_ARP_SPA: + a = f.actions().buildSetField() + .setField(f.oxms().buildArpSpa().setValue(IPv4Address.of(actionData[1])).build()) + .build(); + break; + case MatchUtils.STR_ARP_DPA: + a = f.actions().buildSetField() + .setField(f.oxms().buildArpTpa().setValue(IPv4Address.of(actionData[1])).build()) + .build(); + break; + case MatchUtils.STR_IPV6_ND_SLL: + a = f.actions().buildSetField() + .setField(f.oxms().buildIpv6NdSll().setValue(MacAddress.of(actionData[1])).build()) + .build(); + break; + case MatchUtils.STR_IPV6_ND_TLL: + a = f.actions().buildSetField() + .setField(f.oxms().buildIpv6NdTll().setValue(MacAddress.of(actionData[1])).build()) + .build(); + break; + case MatchUtils.STR_IPV6_ND_TARGET: + a = f.actions().buildSetField() + .setField(f.oxms().buildIpv6NdTarget().setValue(IPv6Address.of(actionData[1])).build()) + .build(); + break; + case MatchUtils.STR_DL_TYPE: + if (actionData[1].startsWith("0x")) { + a = f.actions().buildSetField() + .setField(f.oxms().buildEthType().setValue(EthType.of(Integer.parseInt(actionData[1].replaceFirst("0x", ""), 16))).build()) + .build(); + } else { + a = f.actions().buildSetField() + .setField(f.oxms().buildEthType().setValue(EthType.of(Integer.parseInt(actionData[1]))).build()) + .build(); + } + break; + case MatchUtils.STR_DL_SRC: + a = f.actions().buildSetField() + .setField(f.oxms().buildEthSrc().setValue(MacAddress.of(actionData[1])).build()) + .build(); + break; + case MatchUtils.STR_DL_DST: + a = f.actions().buildSetField() + .setField(f.oxms().buildEthDst().setValue(MacAddress.of(actionData[1])).build()) + .build(); + break; + case MatchUtils.STR_DL_VLAN: + if (actionData[1].startsWith("0x")) { + a = f.actions().buildSetField() + .setField(f.oxms().buildVlanVid().setValue(OFVlanVidMatch.ofVlan(Integer.parseInt(actionData[1].replaceFirst("0x", ""), 16))).build()) + .build(); + } else { + a = f.actions().buildSetField() + .setField(f.oxms().buildVlanVid().setValue(OFVlanVidMatch.ofVlan(Integer.parseInt(actionData[1]))).build()) + .build(); + } + break; + case MatchUtils.STR_DL_VLAN_PCP: + if (actionData[1].startsWith("0x")) { + a = f.actions().buildSetField() + .setField(f.oxms().buildVlanPcp().setValue(VlanPcp.of(Byte.parseByte(actionData[1].replaceFirst("0x", ""), 16))).build()) + .build(); + } else { + a = f.actions().buildSetField() + .setField(f.oxms().buildVlanPcp().setValue(VlanPcp.of(Byte.parseByte(actionData[1]))).build()) + .build(); + } + break; + case MatchUtils.STR_ICMP_CODE: + if (actionData[1].startsWith("0x")) { + a = f.actions().buildSetField() + .setField(f.oxms().buildIcmpv4Code().setValue(ICMPv4Code.of(Short.parseShort(actionData[1].replaceFirst("0x", ""), 16))).build()) + .build(); + } else { + a = f.actions().buildSetField() + .setField(f.oxms().buildIcmpv4Code().setValue(ICMPv4Code.of(Short.parseShort(actionData[1]))).build()) + .build(); + } + break; + case MatchUtils.STR_ICMP_TYPE: + if (actionData[1].startsWith("0x")) { + a = f.actions().buildSetField() + .setField(f.oxms().buildIcmpv4Type().setValue(ICMPv4Type.of(Short.parseShort(actionData[1].replaceFirst("0x", ""), 16))).build()) + .build(); + } else { + a = f.actions().buildSetField() + .setField(f.oxms().buildIcmpv4Type().setValue(ICMPv4Type.of(Short.parseShort(actionData[1]))).build()) + .build(); + } + break; + case MatchUtils.STR_ICMPV6_CODE: + if (actionData[1].startsWith("0x")) { + a = f.actions().buildSetField() + .setField(f.oxms().buildIcmpv6Code().setValue(U8.of(Short.parseShort(actionData[1].replaceFirst("0x", ""), 16))).build()) + .build(); + } else { + a = f.actions().buildSetField() + .setField(f.oxms().buildIcmpv6Code().setValue(U8.of(Short.parseShort(actionData[1]))).build()) + .build(); + } + break; + case MatchUtils.STR_ICMPV6_TYPE: + if (actionData[1].startsWith("0x")) { + a = f.actions().buildSetField() + .setField(f.oxms().buildIcmpv6Type().setValue(U8.of(Short.parseShort(actionData[1].replaceFirst("0x", ""), 16))).build()) + .build(); + } else { + a = f.actions().buildSetField() + .setField(f.oxms().buildIcmpv6Type().setValue(U8.of(Short.parseShort(actionData[1]))).build()) + .build(); + } + break; + case MatchUtils.STR_NW_PROTO: + if (actionData[1].startsWith("0x")) { + a = f.actions().buildSetField() + .setField(f.oxms().buildIpProto().setValue(IpProtocol.of(Short.parseShort(actionData[1].replaceFirst("0x", ""), 16))).build()) + .build(); + } else { + a = f.actions().buildSetField() + .setField(f.oxms().buildIpProto().setValue(IpProtocol.of(Short.parseShort(actionData[1]))).build()) + .build(); + } + break; + case MatchUtils.STR_NW_SRC: + a = f.actions().buildSetField() + .setField(f.oxms().buildIpv4Src().setValue(IPv4Address.of(actionData[1])).build()) + .build(); + break; + case MatchUtils.STR_NW_DST: + a = f.actions().buildSetField() + .setField(f.oxms().buildIpv4Dst().setValue(IPv4Address.of(actionData[1])).build()) + .build(); + break; + case MatchUtils.STR_IPV6_SRC: + a = f.actions().buildSetField() + .setField(f.oxms().buildIpv6Src().setValue(IPv6Address.of(actionData[1])).build()) + .build(); + break; + case MatchUtils.STR_IPV6_DST: + a = f.actions().buildSetField() + .setField(f.oxms().buildIpv6Dst().setValue(IPv6Address.of(actionData[1])).build()) + .build(); + break; + case MatchUtils.STR_IPV6_FLOW_LABEL: + if (actionData[1].startsWith("0x")) { + a = f.actions().buildSetField() + .setField(f.oxms().buildIpv6Flabel().setValue(IPv6FlowLabel.of(Integer.parseInt(actionData[1].replaceFirst("0x", ""), 16))).build()) + .build(); + } else { + a = f.actions().buildSetField() + .setField(f.oxms().buildIpv6Flabel().setValue(IPv6FlowLabel.of(Integer.parseInt(actionData[1]))).build()) + .build(); + } + break; + case MatchUtils.STR_NW_ECN: + if (actionData[1].startsWith("0x")) { + a = f.actions().buildSetField() + .setField(f.oxms().buildIpEcn().setValue(IpEcn.of(Byte.parseByte(actionData[1].replaceFirst("0x", ""), 16))).build()) + .build(); + } else { + a = f.actions().buildSetField() + .setField(f.oxms().buildIpEcn().setValue(IpEcn.of(Byte.parseByte(actionData[1]))).build()) + .build(); + } + break; + case MatchUtils.STR_NW_DSCP: + if (actionData[1].startsWith("0x")) { + a = f.actions().buildSetField() + .setField(f.oxms().buildIpDscp().setValue(IpDscp.of(Byte.parseByte(actionData[1].replaceFirst("0x", ""), 16))).build()) + .build(); + } else { + a = f.actions().buildSetField() + .setField(f.oxms().buildIpDscp().setValue(IpDscp.of(Byte.parseByte(actionData[1]))).build()) + .build(); + } + break; + case MatchUtils.STR_SCTP_SRC: + a = f.actions().buildSetField() + .setField(f.oxms().buildSctpSrc().setValue(TransportPort.of(Integer.parseInt(actionData[1]))).build()) + .build(); + break; + case MatchUtils.STR_SCTP_DST: + a = f.actions().buildSetField() + .setField(f.oxms().buildSctpDst().setValue(TransportPort.of(Integer.parseInt(actionData[1]))).build()) + .build(); + break; + case MatchUtils.STR_TCP_SRC: + a = f.actions().buildSetField() + .setField(f.oxms().buildTcpSrc().setValue(TransportPort.of(Integer.parseInt(actionData[1]))).build()) + .build(); + break; + case MatchUtils.STR_TCP_DST: + a = f.actions().buildSetField() + .setField(f.oxms().buildTcpDst().setValue(TransportPort.of(Integer.parseInt(actionData[1]))).build()) + .build(); + break; + case MatchUtils.STR_UDP_SRC: + a = f.actions().buildSetField() + .setField(f.oxms().buildUdpSrc().setValue(TransportPort.of(Integer.parseInt(actionData[1]))).build()) + .build(); + break; + case MatchUtils.STR_UDP_DST: + a = f.actions().buildSetField() + .setField(f.oxms().buildUdpDst().setValue(TransportPort.of(Integer.parseInt(actionData[1]))).build()) + .build(); + break; + case MatchUtils.STR_MPLS_LABEL: + if (actionData[1].startsWith("0x")) { + a = f.actions().buildSetField() + .setField(f.oxms().buildMplsLabel().setValue(U32.of(Long.parseLong(actionData[1].replaceFirst("0x", ""), 16))).build()) + .build(); + } else { + a = f.actions().buildSetField() + .setField(f.oxms().buildMplsLabel().setValue(U32.of(Long.parseLong(actionData[1]))).build()) + .build(); + } + break; + case MatchUtils.STR_MPLS_TC: + if (actionData[1].startsWith("0x")) { + a = f.actions().buildSetField() + .setField(f.oxms().buildMplsTc().setValue(U8.of(Short.parseShort(actionData[1].replaceFirst("0x", ""), 16))).build()) + .build(); + } else { + a = f.actions().buildSetField() + .setField(f.oxms().buildMplsTc().setValue(U8.of(Short.parseShort(actionData[1]))).build()) + .build(); + } + break; + case MatchUtils.STR_MPLS_BOS: + a = f.actions().buildSetField() + .setField(f.oxms().buildMplsBos().setValue(OFBooleanValue.of(Boolean.parseBoolean(actionData[1]))).build()) // interprets anything other than "true" as false + .build(); + break; + case MatchUtils.STR_METADATA: + if (actionData[1].startsWith("0x")) { + a = f.actions().buildSetField() + .setField(f.oxms().buildMetadata().setValue(OFMetadata.of(U64.of(Long.parseLong(actionData[1].replaceFirst("0x", ""), 16)))).build()) + .build(); + } else { + a = f.actions().buildSetField() + .setField(f.oxms().buildMetadata().setValue(OFMetadata.of(U64.of(Long.parseLong(actionData[1])))).build()) + .build(); + } + break; + case MatchUtils.STR_ACTSET_OUTPUT: + a = f.actions().buildSetField() + .setField(f.oxms().buildActsetOutput().setValue(portFromString(actionData[1])).build()) + .build(); + + break; + case MatchUtils.STR_TCP_FLAGS: + if (actionData[1].startsWith("0x")) { + a = f.actions().buildSetField() + .setField(f.oxms().buildTcpFlags().setValue(U16.of(Integer.parseInt(actionData[1].replaceFirst("0x", ""), 16))).build()) + .build(); + } else { + a = f.actions().buildSetField() + .setField(f.oxms().buildTcpFlags().setValue(U16.of(Integer.parseInt(actionData[1]))).build()) + .build(); + } + break; + default: + log.error("Unexpected OF1.2+ setfield '{}'", actionData); + break; + } + break; + case STR_GROUP: + if (pair.startsWith("0x")) { + a = f.actions().buildGroup() + .setGroup(OFGroup.of(Integer.parseInt(pair.replaceFirst("0x", ""), 16))) + .build(); + } else { + a = f.actions().buildGroup() + .setGroup(OFGroup.of(Integer.parseInt(pair))) + .build(); + } + break; + case STR_MPLS_LABEL_SET: + if (pair.startsWith("0x")) { + a = f.actions().buildSetMplsLabel() + .setMplsLabel(Long.parseLong(pair.replaceFirst("0x", ""), 16)) + .build(); + } else { + a = f.actions().buildSetMplsLabel() + .setMplsLabel(Long.parseLong(pair)) + .build(); + } + break; + case STR_MPLS_POP: + if (pair.startsWith("0x")) { + a = f.actions().buildPopMpls() + .setEthertype(EthType.of(Integer.parseInt(pair.replaceFirst("0x", ""), 16))) + .build(); + } else { + a = f.actions().buildPopMpls() + .setEthertype(EthType.of(Integer.parseInt(pair))) + .build(); + } + break; + case STR_MPLS_PUSH: + if (pair.startsWith("0x")) { + a = f.actions().buildPushMpls() + .setEthertype(EthType.of(Integer.parseInt(pair.replaceFirst("0x", ""), 16))) + .build(); + } else { + a = f.actions().buildPushMpls() + .setEthertype(EthType.of(Integer.parseInt(pair))) + .build(); + } + break; + case STR_MPLS_TC_SET: + if (pair.startsWith("0x")) { + a = f.actions().buildSetMplsTc() + .setMplsTc(Short.parseShort(pair.replaceFirst("0x", ""), 16)) + .build(); + } else { + a = f.actions().buildSetMplsTc() + .setMplsTc(Short.parseShort(pair)) + .build(); + } + break; + case STR_MPLS_TTL_DEC: + a = f.actions().decMplsTtl(); + break; + case STR_MPLS_TTL_SET: + if (pair.startsWith("0x")) { + a = f.actions().buildSetMplsTtl() + .setMplsTtl(Short.parseShort(pair.replaceFirst("0x", ""), 16)) + .build(); + } else { + a = f.actions().buildSetMplsTtl() + .setMplsTtl(Short.parseShort(pair)) + .build(); + } + break; + case STR_NW_TOS_SET: + a = decode_set_tos_bits(pair, v); // should only be used by OF1.0 + break; + case STR_NW_SRC_SET: + a = decode_set_src_ip(pair, v); + break; + case STR_NW_DST_SET: + a = decode_set_dst_ip(pair, v); + break; + case STR_NW_ECN_SET: // loxi does not support DSCP set for OF1.1 + if (pair.startsWith("0x")) { + a = f.actions().buildSetNwEcn() + .setNwEcn(IpEcn.of(Byte.parseByte(pair.replaceFirst("0x", ""), 16))) + .build(); + } else { + a = f.actions().buildSetNwEcn() + .setNwEcn(IpEcn.of(Byte.parseByte(pair))) + .build(); + } + break; + case STR_NW_TTL_DEC: + a = f.actions().decNwTtl(); + break; + case STR_NW_TTL_SET: + if (pair.startsWith("0x")) { + a = f.actions().buildSetNwTtl() + .setNwTtl(Short.parseShort(pair.replaceFirst("0x", ""), 16)) + .build(); + } else { + a = f.actions().buildSetNwTtl() + .setNwTtl(Short.parseShort(pair)) + .build(); + } + break; + case STR_PBB_POP: + a = f.actions().popPbb(); + break; + case STR_PBB_PUSH: + if (pair.startsWith("0x")) { + a = f.actions().buildPushPbb() + .setEthertype(EthType.of(Integer.parseInt(pair.replaceFirst("0x", ""), 16))) + .build(); + } else { + a = f.actions().buildPushPbb() + .setEthertype(EthType.of(Integer.parseInt(pair))) + .build(); + } + break; + case STR_QUEUE_SET: + if (pair.startsWith("0x")) { + a = f.actions().buildSetQueue() + .setQueueId(Long.parseLong(pair.replaceFirst("0x", ""), 16)) + .build(); + } else { + a = f.actions().buildSetQueue() + .setQueueId(Long.parseLong(pair)) + .build(); + } + break; + case STR_TP_SRC_SET: + a = decode_set_src_port(pair, v); + break; + case STR_TP_DST_SET: + a = decode_set_dst_port(pair, v); + break; + case STR_TTL_IN_COPY: + a = f.actions().copyTtlIn(); + break; + case STR_TTL_OUT_COPY: + a = f.actions().copyTtlOut(); + break; + case STR_VLAN_POP: + a = f.actions().popVlan(); + break; + case STR_VLAN_PUSH: + if (pair.startsWith("0x")) { + a = f.actions().buildPushVlan() + .setEthertype(EthType.of(Integer.parseInt(pair.replaceFirst("0x", ""), 16))) + .build(); + } else { + a = f.actions().buildPushVlan() + .setEthertype(EthType.of(Integer.parseInt(pair))) + .build(); + } + break; + case STR_VLAN_STRIP: + a = f.actions().stripVlan(); + break; + case STR_VLAN_SET_VID: + a = decode_set_vlan_id(pair, v); + break; + case STR_VLAN_SET_PCP: + a = decode_set_vlan_priority(pair, v); + break; + case STR_METER: + if (pair.startsWith("0x")) { + a = f.actions().buildMeter() + .setMeterId(Long.parseLong(pair.replaceFirst("0x", ""), 16)) + .build(); + } else { + a = f.actions().buildMeter() + .setMeterId(Long.parseLong(pair)) + .build(); + } + break; + case STR_FIELD_COPY: + a = (OFAction) copyFieldFromJson(pair, f.getVersion()); + break; + default: + log.error("Unexpected action key '{}'", keyPair); + break; + } - } catch (Exception e) { - log.error("Illegal Action: {}", e.getMessage()); - } - if (a != null) { - actions.add(a); - } - } - } else { - log.debug("actions not found --> drop"); - } - return actions; - } + } catch (Exception e) { + log.error("Illegal Action: {}", e.getMessage()); + } + if (a != null) { + actions.add(a); + } + } + } else { + log.debug("actions not found --> drop"); + } + return actions; + } - /** - * Append OFActionCopyField object to an existing JsonGenerator. - * This method assumes the field name of the action has been - * written already, if required. The appended data will - * be formatted as follows: - * { - * "src_field":"name", - * "dst_field":"name", - * "src_offset_bits":"bits", - * "dst_offset_bits":"bits", - * "num_bits":"bits" - * } - * @param jsonGen - * @param c - */ - public static void copyFieldToJson(JsonGenerator jsonGen, OFActionCopyField c) { - jsonGen.configure(Feature.WRITE_NUMBERS_AS_STRINGS, true); + /** + * Append OFActionCopyField object to an existing JsonGenerator. + * This method assumes the field name of the action has been + * written already, if required. The appended data will + * be formatted as follows: + * { + * "src_field":"name", + * "dst_field":"name", + * "src_offset_bits":"bits", + * "dst_offset_bits":"bits", + * "num_bits":"bits" + * } + * @param jsonGen + * @param c + */ + public static void copyFieldToJson(JsonGenerator jsonGen, OFActionCopyField c) { + jsonGen.configure(Feature.WRITE_NUMBERS_AS_STRINGS, true); - try { - jsonGen.writeStartObject(); - Iterator<OFOxm<?>> i = c.getOxmIds().iterator(); - if (i.hasNext()) { - jsonGen.writeStringField("src_field" , OXMUtils.oxmIdToString(U32.of(i.next().getCanonical().getTypeLen()))); - } else { - log.error("either src_field or dst_field or both not set in {}", c); - } - if (i.hasNext()) { - jsonGen.writeStringField("dst_field" , OXMUtils.oxmIdToString(U32.of(i.next().getCanonical().getTypeLen()))); - } else { - log.error("either src_field or dst_field not set in {}", c); - } - if (i.hasNext()) { - log.warn("OFOxmList should only have src_field followed by dst_field. Extra field {}", i.next()); - } - jsonGen.writeNumberField("src_offset_bits", c.getSrcOffset()); - jsonGen.writeNumberField("dst_offset_bits", c.getDstOffset()); - jsonGen.writeNumberField("num_bits", c.getNBits()); - jsonGen.writeEndObject(); - jsonGen.close(); - } catch (IOException e) { - log.error("Error composing OFActionCopyField JSON object. {}", e.getMessage()); - return; - } - } + try { + jsonGen.writeStartObject(); + Iterator<OFOxm<?>> i = c.getOxmIds().iterator(); + if (i.hasNext()) { + jsonGen.writeStringField("src_field" , OXMUtils.oxmIdToString(U32.of(i.next().getCanonical().getTypeLen()))); + } else { + log.error("either src_field or dst_field or both not set in {}", c); + } + if (i.hasNext()) { + jsonGen.writeStringField("dst_field" , OXMUtils.oxmIdToString(U32.of(i.next().getCanonical().getTypeLen()))); + } else { + log.error("either src_field or dst_field not set in {}", c); + } + if (i.hasNext()) { + log.warn("OFOxmList should only have src_field followed by dst_field. Extra field {}", i.next()); + } + jsonGen.writeNumberField("src_offset_bits", c.getSrcOffset()); + jsonGen.writeNumberField("dst_offset_bits", c.getDstOffset()); + jsonGen.writeNumberField("num_bits", c.getNBits()); + jsonGen.writeEndObject(); + jsonGen.close(); + } catch (IOException e) { + log.error("Error composing OFActionCopyField JSON object. {}", e.getMessage()); + return; + } + } - /** - * Convert OFActionCopyField object to a JSON string. - * This method assumes the field name of the action has been - * written already, if required. The appended data will - * be formatted as follows: - * { - * "src_field":"name", - * "dst_field":"name", - * "src_offset_bits":"bits", - * "dst_offset_bits":"bits", - * "num_bits":"bits" - * } - * @param jsonGen - * @param c - */ - public static String copyFieldToJson(OFActionCopyField c) { - Writer w = new StringWriter(); - JsonGenerator jsonGen; - try { - jsonGen = jsonFactory.createGenerator(w); - } catch (IOException e) { - log.error("Could not instantiate JSON Generator. {}", e.getMessage()); - return JSON_EMPTY_OBJECT; - } + /** + * Convert OFActionCopyField object to a JSON string. + * This method assumes the field name of the action has been + * written already, if required. The appended data will + * be formatted as follows: + * { + * "src_field":"name", + * "dst_field":"name", + * "src_offset_bits":"bits", + * "dst_offset_bits":"bits", + * "num_bits":"bits" + * } + * @param jsonGen + * @param c + */ + public static String copyFieldToJson(OFActionCopyField c) { + Writer w = new StringWriter(); + JsonGenerator jsonGen; + try { + jsonGen = jsonFactory.createGenerator(w); + } catch (IOException e) { + log.error("Could not instantiate JSON Generator. {}", e.getMessage()); + return JSON_EMPTY_OBJECT; + } - copyFieldToJson(jsonGen, c); + copyFieldToJson(jsonGen, c); - return w.toString(); /* overridden impl returns contents of Writer's StringBuffer */ - } + return w.toString(); /* overridden impl returns contents of Writer's StringBuffer */ + } - /** - * Convert a JSON string to an OFActionCopyField object. - * The format of the input JSON is expected to be: - * { - * "src_field":"name", - * "dst_field":"name", - * "src_offset_bits":"bits", - * "dst_offset_bits":"bits", - * "num_bits":"bits" - * } - * @param json - * @param v - * @return - */ - public static OFActionCopyField copyFieldFromJson(String json, OFVersion v) { - if (json == null) { - throw new IllegalArgumentException("JSON string cannot be null"); - } - if (v == null) { - throw new IllegalArgumentException("OFVersion cannot be null"); - } + /** + * Convert a JSON string to an OFActionCopyField object. + * The format of the input JSON is expected to be: + * { + * "src_field":"name", + * "dst_field":"name", + * "src_offset_bits":"bits", + * "dst_offset_bits":"bits", + * "num_bits":"bits" + * } + * @param json + * @param v + * @return + */ + public static OFActionCopyField copyFieldFromJson(String json, OFVersion v) { + if (json == null) { + throw new IllegalArgumentException("JSON string cannot be null"); + } + if (v == null) { + throw new IllegalArgumentException("OFVersion cannot be null"); + } - final JsonParser jp; - try { - jp = jsonFactory.createParser(json); - } catch (IOException e) { - log.error("Could not create JSON parser for OFActionCopyField {}", json); - return null; - } - try { - if (jp.nextToken() != JsonToken.START_OBJECT) { - throw new IOException("Expected START_OBJECT"); - } - - OFActionCopyField.Builder b = OFFactories.getFactory(v).buildActionCopyField(); - OFOxm<?> srcField = null; - OFOxm<?> dstField = null; - - while (jp.nextToken() != JsonToken.END_OBJECT) { - String key = jp.getCurrentName().toLowerCase().trim(); - jp.nextToken(); - String value = jp.getText().toLowerCase().trim(); - switch (key) { - case "src_field": - srcField = OXMUtils.oxmStringToOxm(value, v); - break; - case "dst_field": - dstField = OXMUtils.oxmStringToOxm(value, v); - break; - case "src_offset_bits": - b.setSrcOffset(Integer.parseInt(value)); - break; - case "dst_offset_bits": - b.setDstOffset(Integer.parseInt(value)); - break; - case "num_bits": - b.setNBits(Integer.parseInt(value)); - break; - default: - log.warn("Unexpected OFActionCopyField key {}", key); - break; - } - } - if (srcField == null || dstField == null) { - log.error("Src and dst OXMs must be specified. Got {} and {}, respectively", srcField, dstField); - return null; - } else { - b.setOxmIds(OFOxmList.of(srcField, dstField)); - return b.build(); - } - } catch (IOException e) { - log.error("Could not parse: {}", json); - log.error("JSON parse error message: {}", e.getMessage()); - return null; - } - } + final JsonParser jp; + try { + jp = jsonFactory.createParser(json); + } catch (IOException e) { + log.error("Could not create JSON parser for OFActionCopyField {}", json); + return null; + } + try { + if (jp.nextToken() != JsonToken.START_OBJECT) { + throw new IOException("Expected START_OBJECT"); + } - /** - * Parses OFFlowMod actions from strings. - * @param fmb The OFFlowMod.Builder to set the actions for - * @param s The string containing all the actions - * @param log A logger to log for errors. - */ - public static void fromString(OFFlowMod.Builder fmb, String s) { - List<OFAction> actions = fromString(s, fmb.getVersion()); - log.debug("actions: {}", actions); - fmb.setActions(actions); - return; - } + OFActionCopyField.Builder b = OFFactories.getFactory(v).buildActionCopyField(); + OFOxm<?> srcField = null; + OFOxm<?> dstField = null; - /** - * Parse string and numerical port representations. - * The key and delimiter for the action should be omitted, and only the - * data should be presented to this decoder. Data can be any signed integer - * or hex (w/leading 0x prefix) as a string or the special string port - * STR_PORT_* as defined in {@link MatchUtils}. - * - * @param actionToDecode; The action as a string to decode - * @param version; The OF version to create the action for - * @return - */ - private static OFActionOutput decode_output(String actionToDecode, OFVersion version) { - OFActionOutput.Builder ab = OFFactories.getFactory(version).actions().buildOutput(); - actionToDecode = actionToDecode.trim().toLowerCase(); - OFPort port = OFPort.ZERO; - switch (actionToDecode) { - case MatchUtils.STR_PORT_ALL: - port = OFPort.ALL; - break; - case MatchUtils.STR_PORT_CONTROLLER: - port = OFPort.CONTROLLER; - break; - case MatchUtils.STR_PORT_FLOOD: - port = OFPort.FLOOD; - break; - case MatchUtils.STR_PORT_IN_PORT: - port = OFPort.IN_PORT; - break; - case MatchUtils.STR_PORT_LOCAL: - port = OFPort.LOCAL; - break; - case MatchUtils.STR_PORT_NORMAL: - port = OFPort.NORMAL; - break; - case MatchUtils.STR_PORT_TABLE: - port = OFPort.TABLE; - break; - default: - log.debug("Port {} was not a special port string. Parsing as raw int or hex", actionToDecode); - } - if (!port.equals(OFPort.ZERO)) { - ab.setPort(port); - ab.setMaxLen(Integer.MAX_VALUE); - log.debug("action {}", ab); - return ab.build(); - } else { - try { - port = OFPort.of(U32.of(actionToDecode.contains("0x") ? - Long.parseLong(actionToDecode.replaceFirst("0x", ""), 16) : - Long.parseLong(actionToDecode)).getRaw()); - ab.setPort(port); - ab.setMaxLen(Integer.MAX_VALUE); - return ab.build(); - } catch (NumberFormatException e) { - log.error("Could not parse port: '{}'", actionToDecode); - return null; - } - } - } + while (jp.nextToken() != JsonToken.END_OBJECT) { + String key = jp.getCurrentName().toLowerCase().trim(); + jp.nextToken(); + String value = jp.getText().toLowerCase().trim(); + switch (key) { + case "src_field": + srcField = OXMUtils.oxmStringToOxm(value, v); + break; + case "dst_field": + dstField = OXMUtils.oxmStringToOxm(value, v); + break; + case "src_offset_bits": + b.setSrcOffset(Integer.parseInt(value)); + break; + case "dst_offset_bits": + b.setDstOffset(Integer.parseInt(value)); + break; + case "num_bits": + b.setNBits(Integer.parseInt(value)); + break; + default: + log.warn("Unexpected OFActionCopyField key {}", key); + break; + } + } + if (srcField == null || dstField == null) { + log.error("Src and dst OXMs must be specified. Got {} and {}, respectively", srcField, dstField); + return null; + } else { + b.setOxmIds(OFOxmList.of(srcField, dstField)); + return b.build(); + } + } catch (IOException e) { + log.error("Could not parse: {}", json); + log.error("JSON parse error message: {}", e.getMessage()); + return null; + } + } - /** - * Parse enqueue actions. - * The key and delimiter for the action should be omitted, and only the - * data should be presented to this decoder. Data with a leading 0x is permitted. - * - * @param actionToDecode; The action as a string to decode - * @param version; The OF version to create the action for - * @return - */ - private static OFActionEnqueue decode_enqueue(String actionToDecode, OFVersion version) { - Matcher n = Pattern.compile("(?:((?:0x)?\\d+)\\:((?:0x)?\\d+))").matcher(actionToDecode); - if (n.matches()) { - OFPort port = OFPort.of(0); - if (n.group(1) != null) { - try { - port = OFPort.of(get_short(n.group(1))); - } - catch (NumberFormatException e) { - log.debug("Invalid port-num in: '{}' (error ignored)", actionToDecode); - return null; - } - } + /** + * Parses OFFlowMod actions from strings. + * @param fmb The OFFlowMod.Builder to set the actions for + * @param s The string containing all the actions + * @param log A logger to log for errors. + */ + public static void fromString(OFFlowMod.Builder fmb, String s) { + List<OFAction> actions = fromString(s, fmb.getVersion()); + log.debug("actions: {}", actions); + fmb.setActions(actions); + return; + } - int queueid = 0; - if (n.group(2) != null) { - try { - queueid = get_int(n.group(2)); - } - catch (NumberFormatException e) { - log.debug("Invalid queue-id in: '{}' (error ignored)", actionToDecode); - return null; - } - } - OFActionEnqueue a = OFFactories.getFactory(version).actions().buildEnqueue() - .setPort(port) - .setQueueId(queueid) - .build(); - log.debug("action {}", a); - return a; - } - else { - log.debug("Invalid action: '{}'", actionToDecode); - return null; - } - } + public static OFPort portFromString(String s) { + return MatchUtils.portFromString(s); + } + + public static String portToString(OFPort p) { + return MatchUtils.portToString(p); + } - /** - * Parse set_vlan_id actions. - * The key and delimiter for the action should be omitted, and only the - * data should be presented to this decoder. Data with a leading 0x is permitted. - * - * @param actionToDecode; The action as a string to decode - * @param version; The OF version to create the action for - * @return - */ - private static OFActionSetVlanVid decode_set_vlan_id(String actionToDecode, OFVersion version) { - Matcher n = Pattern.compile("((?:0x)?\\d+)").matcher(actionToDecode); - if (n.matches()) { - if (n.group(1) != null) { - try { - VlanVid vlanid = VlanVid.ofVlan(get_short(n.group(1))); - OFActionSetVlanVid a = OFFactories.getFactory(version).actions().buildSetVlanVid() - .setVlanVid(vlanid) - .build(); - log.debug("action {}", a); - return a; - } - catch (NumberFormatException e) { - log.debug("Invalid VLAN in: {} (error ignored)", actionToDecode); - return null; - } - } - } - else { - log.debug("Invalid action: '{}'", actionToDecode); - return null; - } - return null; - } + /** + * Parse string and numerical port representations. + * The key and delimiter for the action should be omitted, and only the + * data should be presented to this decoder. Data can be any signed integer + * or hex (w/leading 0x prefix) as a string or the special string port + * STR_PORT_* as defined in {@link MatchUtils}. + * + * @param actionToDecode; The action as a string to decode + * @param version; The OF version to create the action for + * @return + */ + private static OFActionOutput decode_output(String actionToDecode, OFVersion version) { + OFActionOutput.Builder ab = OFFactories.getFactory(version).actions().buildOutput(); + OFPort port = portFromString(actionToDecode); + if (port == null) { + log.error("Could not parse output port {}", actionToDecode); + return null; + } else { + ab.setPort(port); + ab.setMaxLen(Integer.MAX_VALUE); + log.debug("action {}", ab); + return ab.build(); + } + } - /** - * Parse set_vlan_pcp actions. - * The key and delimiter for the action should be omitted, and only the - * data should be presented to this decoder. Data with a leading 0x is permitted. - * - * @param actionToDecode; The action as a string to decode - * @param version; The OF version to create the action for - * @return - */ - private static OFActionSetVlanPcp decode_set_vlan_priority(String actionToDecode, OFVersion version) { - Matcher n = Pattern.compile("((?:0x)?\\d+)").matcher(actionToDecode); - if (n.matches()) { - if (n.group(1) != null) { - try { - OFActionSetVlanPcp a = OFFactories.getFactory(version).actions().buildSetVlanPcp() - .setVlanPcp(VlanPcp.of(get_byte(n.group(1)))) - .build(); - log.debug("action {}", a); - return a; - } - catch (NumberFormatException e) { - log.debug("Invalid VLAN priority in: {} (error ignored)", actionToDecode); - return null; - } - } - } - else { - log.debug("Invalid action: '{}'", actionToDecode); - return null; - } - return null; - } + /** + * Parse enqueue actions. + * The key and delimiter for the action should be omitted, and only the + * data should be presented to this decoder. Data with a leading 0x is permitted. + * + * @param actionToDecode; The action as a string to decode + * @param version; The OF version to create the action for + * @return + */ + private static OFActionEnqueue decode_enqueue(String actionToDecode, OFVersion version) { + Matcher n = Pattern.compile("(?:((?:0x)?\\d+)\\:((?:0x)?\\d+))").matcher(actionToDecode); + if (n.matches()) { + OFPort port; + if (n.group(1) != null) { + port = portFromString(n.group(1)); + if (port == null) { + log.error("Invalid port {}", n.group(1)); + return null; + } + } else { + log.error("Missing port number for enqueue action"); + return null; + } - /** - * Parse set_dl_src actions. - * The key and delimiter for the action should be omitted, and only the - * data should be presented to this decoder. - * - * @param actionToDecode; The action as a string to decode - * @param version; The OF version to create the action for - * @return - */ - private static OFActionSetDlSrc decode_set_src_mac(String actionToDecode, OFVersion version) { - try { - OFActionSetDlSrc a = OFFactories.getFactory(version).actions().buildSetDlSrc() - .setDlAddr(MacAddress.of(actionToDecode)) - .build(); - log.debug("action {}", a); - return a; - } - catch (Exception e) { - log.debug("Invalid action: '{}'", actionToDecode); - return null; - } - } + int queueid = 0; + if (n.group(2) != null) { + try { + queueid = get_int(n.group(2)); + } + catch (NumberFormatException e) { + log.debug("Invalid queue-id in: '{}' (error ignored)", actionToDecode); + return null; + } + } + OFActionEnqueue a = OFFactories.getFactory(version).actions().buildEnqueue() + .setPort(port) + .setQueueId(queueid) + .build(); + log.debug("action {}", a); + return a; + } + else { + log.debug("Invalid action: '{}'", actionToDecode); + return null; + } + } - /** - * Parse set_dl_dst actions. - * The key and delimiter for the action should be omitted, and only the - * data should be presented to this decoder. - * - * @param actionToDecode; The action as a string to decode - * @param version; The OF version to create the action for - * @return - */ - private static OFActionSetDlDst decode_set_dst_mac(String actionToDecode, OFVersion version) { - try { - OFActionSetDlDst a = OFFactories.getFactory(version).actions().buildSetDlDst() - .setDlAddr(MacAddress.of(actionToDecode)) - .build(); - log.debug("action {}", a); - return a; - } - catch (Exception e) { - log.debug("Invalid action: '{}'", actionToDecode); - return null; - } - } + /** + * Parse set_vlan_id actions. + * The key and delimiter for the action should be omitted, and only the + * data should be presented to this decoder. Data with a leading 0x is permitted. + * + * @param actionToDecode; The action as a string to decode + * @param version; The OF version to create the action for + * @return + */ + private static OFActionSetVlanVid decode_set_vlan_id(String actionToDecode, OFVersion version) { + Matcher n = Pattern.compile("((?:0x)?\\d+)").matcher(actionToDecode); + if (n.matches()) { + if (n.group(1) != null) { + try { + VlanVid vlanid = VlanVid.ofVlan(get_short(n.group(1))); + OFActionSetVlanVid a = OFFactories.getFactory(version).actions().buildSetVlanVid() + .setVlanVid(vlanid) + .build(); + log.debug("action {}", a); + return a; + } + catch (NumberFormatException e) { + log.debug("Invalid VLAN in: {} (error ignored)", actionToDecode); + return null; + } + } + } + else { + log.debug("Invalid action: '{}'", actionToDecode); + return null; + } + return null; + } - /** - * Parse set_tos actions. - * The key and delimiter for the action should be omitted, and only the - * data should be presented to this decoder. A leading 0x is permitted. - * - * @param actionToDecode; The action as a string to decode - * @param version; The OF version to create the action for - * @return - */ - private static OFActionSetNwTos decode_set_tos_bits(String actionToDecode, OFVersion version) { - Matcher n = Pattern.compile("((?:0x)?\\d+)").matcher(actionToDecode); - if (n.matches()) { - if (n.group(1) != null) { - try { - OFActionSetNwTos a = OFFactories.getFactory(version).actions().buildSetNwTos() - .setNwTos(get_byte(n.group(1))) - .build(); - log.debug("action {}", a); - return a; - } - catch (NumberFormatException e) { - log.debug("Invalid dst-port in: {} (error ignored)", actionToDecode); - return null; - } - } - } - else { - log.debug("Invalid action: '{}'", actionToDecode); - return null; - } - return null; - } + /** + * Parse set_vlan_pcp actions. + * The key and delimiter for the action should be omitted, and only the + * data should be presented to this decoder. Data with a leading 0x is permitted. + * + * @param actionToDecode; The action as a string to decode + * @param version; The OF version to create the action for + * @return + */ + private static OFActionSetVlanPcp decode_set_vlan_priority(String actionToDecode, OFVersion version) { + Matcher n = Pattern.compile("((?:0x)?\\d+)").matcher(actionToDecode); + if (n.matches()) { + if (n.group(1) != null) { + try { + OFActionSetVlanPcp a = OFFactories.getFactory(version).actions().buildSetVlanPcp() + .setVlanPcp(VlanPcp.of(get_byte(n.group(1)))) + .build(); + log.debug("action {}", a); + return a; + } + catch (NumberFormatException e) { + log.debug("Invalid VLAN priority in: {} (error ignored)", actionToDecode); + return null; + } + } + } + else { + log.debug("Invalid action: '{}'", actionToDecode); + return null; + } + return null; + } - /** - * Parse set_nw_src actions. - * The key and delimiter for the action should be omitted, and only the - * data should be presented to this decoder. - * - * @param actionToDecode; The action as a string to decode - * @param version; The OF version to create the action for - * @return - */ - private static OFActionSetNwSrc decode_set_src_ip(String actionToDecode, OFVersion version) { - try { - OFActionSetNwSrc a = OFFactories.getFactory(version).actions().buildSetNwSrc() - .setNwAddr(IPv4Address.of(actionToDecode)) - .build(); - log.debug("action {}", a); - return a; - } catch (Exception e) { - log.debug("Invalid action: '{}'", actionToDecode); - return null; - } - } + /** + * Parse set_dl_src actions. + * The key and delimiter for the action should be omitted, and only the + * data should be presented to this decoder. + * + * @param actionToDecode; The action as a string to decode + * @param version; The OF version to create the action for + * @return + */ + private static OFActionSetDlSrc decode_set_src_mac(String actionToDecode, OFVersion version) { + try { + OFActionSetDlSrc a = OFFactories.getFactory(version).actions().buildSetDlSrc() + .setDlAddr(MacAddress.of(actionToDecode)) + .build(); + log.debug("action {}", a); + return a; + } + catch (Exception e) { + log.debug("Invalid action: '{}'", actionToDecode); + return null; + } + } - /** - * Parse set_nw_dst actions. - * The key and delimiter for the action should be omitted, and only the - * data should be presented to this decoder. - * - * @param actionToDecode; The action as a string to decode - * @param version; The OF version to create the action for - * @return - */ - private static OFActionSetNwDst decode_set_dst_ip(String actionToDecode, OFVersion version) { - try { - OFActionSetNwDst a = OFFactories.getFactory(version).actions().buildSetNwDst() - .setNwAddr(IPv4Address.of(actionToDecode)) - .build(); - log.debug("action {}", a); - return a; - } catch (Exception e) { - log.debug("Invalid action: '{}'", actionToDecode); - return null; - } - } + /** + * Parse set_dl_dst actions. + * The key and delimiter for the action should be omitted, and only the + * data should be presented to this decoder. + * + * @param actionToDecode; The action as a string to decode + * @param version; The OF version to create the action for + * @return + */ + private static OFActionSetDlDst decode_set_dst_mac(String actionToDecode, OFVersion version) { + try { + OFActionSetDlDst a = OFFactories.getFactory(version).actions().buildSetDlDst() + .setDlAddr(MacAddress.of(actionToDecode)) + .build(); + log.debug("action {}", a); + return a; + } + catch (Exception e) { + log.debug("Invalid action: '{}'", actionToDecode); + return null; + } + } - /** - * Parse set_tp_src actions. - * The key and delimiter for the action should be omitted, and only the - * data should be presented to this decoder. A leading 0x is permitted. - * - * @param actionToDecode; The action as a string to decode - * @param version; The OF version to create the action for - * @return - */ - private static OFActionSetTpSrc decode_set_src_port(String actionToDecode, OFVersion version) { - try { - OFActionSetTpSrc a = OFFactories.getFactory(version).actions().buildSetTpSrc() - .setTpPort(TransportPort.of(Integer.parseInt(actionToDecode))) - .build(); - log.debug("action {}", a); - return a; - } - catch (NumberFormatException e) { - log.debug("Invalid src-port in: {} (error ignored)", actionToDecode); - return null; - } - } + /** + * Parse set_tos actions. + * The key and delimiter for the action should be omitted, and only the + * data should be presented to this decoder. A leading 0x is permitted. + * + * @param actionToDecode; The action as a string to decode + * @param version; The OF version to create the action for + * @return + */ + private static OFActionSetNwTos decode_set_tos_bits(String actionToDecode, OFVersion version) { + Matcher n = Pattern.compile("((?:0x)?\\d+)").matcher(actionToDecode); + if (n.matches()) { + if (n.group(1) != null) { + try { + OFActionSetNwTos a = OFFactories.getFactory(version).actions().buildSetNwTos() + .setNwTos(get_byte(n.group(1))) + .build(); + log.debug("action {}", a); + return a; + } + catch (NumberFormatException e) { + log.debug("Invalid dst-port in: {} (error ignored)", actionToDecode); + return null; + } + } + } + else { + log.debug("Invalid action: '{}'", actionToDecode); + return null; + } + return null; + } - /** - * Parse set_tp_dst actions. - * The key and delimiter for the action should be omitted, and only the - * data should be presented to this decoder. A leading 0x is permitted. - * - * @param actionToDecode; The action as a string to decode - * @param version; The OF version to create the action for - * @return - */ - private static OFAction decode_set_dst_port(String actionToDecode, OFVersion version) { - try { - OFActionSetTpSrc a = OFFactories.getFactory(version).actions().buildSetTpSrc() - .setTpPort(TransportPort.of(Integer.parseInt(actionToDecode))) - .build(); - log.debug("action {}", a); - return a; - } - catch (NumberFormatException e) { - log.debug("Invalid src-port in: {} (error ignored)", actionToDecode); - return null; - } - } + /** + * Parse set_nw_src actions. + * The key and delimiter for the action should be omitted, and only the + * data should be presented to this decoder. + * + * @param actionToDecode; The action as a string to decode + * @param version; The OF version to create the action for + * @return + */ + private static OFActionSetNwSrc decode_set_src_ip(String actionToDecode, OFVersion version) { + try { + OFActionSetNwSrc a = OFFactories.getFactory(version).actions().buildSetNwSrc() + .setNwAddr(IPv4Address.of(actionToDecode)) + .build(); + log.debug("action {}", a); + return a; + } catch (Exception e) { + log.debug("Invalid action: '{}'", actionToDecode); + return null; + } + } - /** - * Parse int as decimal, hex (start with 0x or #) or octal (starts with 0) - * @param str - * @return - */ - private static int get_int(String str) { - return Integer.decode(str); - } + /** + * Parse set_nw_dst actions. + * The key and delimiter for the action should be omitted, and only the + * data should be presented to this decoder. + * + * @param actionToDecode; The action as a string to decode + * @param version; The OF version to create the action for + * @return + */ + private static OFActionSetNwDst decode_set_dst_ip(String actionToDecode, OFVersion version) { + try { + OFActionSetNwDst a = OFFactories.getFactory(version).actions().buildSetNwDst() + .setNwAddr(IPv4Address.of(actionToDecode)) + .build(); + log.debug("action {}", a); + return a; + } catch (Exception e) { + log.debug("Invalid action: '{}'", actionToDecode); + return null; + } + } - /** - * Parse short as decimal, hex (start with 0x or #) or octal (starts with 0) - * @param str - * @return - */ - private static short get_short(String str) { - return (short)(int)Integer.decode(str); - } + /** + * Parse set_tp_src actions. + * The key and delimiter for the action should be omitted, and only the + * data should be presented to this decoder. A leading 0x is permitted. + * + * @param actionToDecode; The action as a string to decode + * @param version; The OF version to create the action for + * @return + */ + private static OFActionSetTpSrc decode_set_src_port(String actionToDecode, OFVersion version) { + try { + OFActionSetTpSrc a = OFFactories.getFactory(version).actions().buildSetTpSrc() + .setTpPort(TransportPort.of(Integer.parseInt(actionToDecode))) + .build(); + log.debug("action {}", a); + return a; + } + catch (NumberFormatException e) { + log.debug("Invalid src-port in: {} (error ignored)", actionToDecode); + return null; + } + } - /** - * Parse byte as decimal, hex (start with 0x or #) or octal (starts with 0) - * @param str - * @return - */ - private static byte get_byte(String str) { - return Integer.decode(str).byteValue(); - } + /** + * Parse set_tp_dst actions. + * The key and delimiter for the action should be omitted, and only the + * data should be presented to this decoder. A leading 0x is permitted. + * + * @param actionToDecode; The action as a string to decode + * @param version; The OF version to create the action for + * @return + */ + private static OFAction decode_set_dst_port(String actionToDecode, OFVersion version) { + try { + OFActionSetTpSrc a = OFFactories.getFactory(version).actions().buildSetTpSrc() + .setTpPort(TransportPort.of(Integer.parseInt(actionToDecode))) + .build(); + log.debug("action {}", a); + return a; + } + catch (NumberFormatException e) { + log.debug("Invalid src-port in: {} (error ignored)", actionToDecode); + return null; + } + } -} + /** + * Parse int as decimal, hex (start with 0x or #) or octal (starts with 0) + * @param str + * @return + */ + private static int get_int(String str) { + return Integer.parseInt(str); + } + + /** + * Parse short as decimal, hex (start with 0x or #) or octal (starts with 0) + * @param str + * @return + */ + private static short get_short(String str) { + return (short) Integer.parseInt(str); + } + + /** + * Parse byte as decimal, hex (start with 0x or #) or octal (starts with 0) + * @param str + * @return + */ + private static byte get_byte(String str) { + return (byte) Integer.parseInt(str); + } +} \ No newline at end of file diff --git a/src/main/java/net/floodlightcontroller/util/GroupUtils.java b/src/main/java/net/floodlightcontroller/util/GroupUtils.java index 33bb2a6eafbe9dcdd8d54860322850dfb3a228ab..36bbab38a965f25489e4d4e1eb9194ecd91810a6 100644 --- a/src/main/java/net/floodlightcontroller/util/GroupUtils.java +++ b/src/main/java/net/floodlightcontroller/util/GroupUtils.java @@ -16,7 +16,6 @@ import org.projectfloodlight.openflow.protocol.OFGroupMod; import org.projectfloodlight.openflow.protocol.OFGroupModify; import org.projectfloodlight.openflow.protocol.OFGroupType; import org.projectfloodlight.openflow.types.OFGroup; -import org.projectfloodlight.openflow.types.OFPort; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -35,305 +34,344 @@ import com.fasterxml.jackson.databind.MappingJsonFactory; * */ public class GroupUtils { - private static final Logger log = LoggerFactory.getLogger(GroupUtils.class); - - public static final String GROUP_ID = "group_id"; - - public static final String GROUP_TYPE = "group_type"; - public static final String GROUP_TYPE_FF = "fast_failover"; - public static final String GROUP_TYPE_ALL = "all"; - public static final String GROUP_TYPE_SELECT = "select"; - public static final String GROUP_TYPE_INDIRECT = "indirect"; - - public static final String GROUP_BUCKETS = "group_buckets"; - public static final String BUCKET_ID = "bucket_id"; - public static final String BUCKET_WEIGHT = "bucket_weight"; - public static final String BUCKET_WATCH_PORT = "bucket_watch_port"; - public static final String BUCKET_WATCH_GROUP = "bucket_watch_group"; - public static final String BUCKET_ACTIONS = "bucket_actions"; - - private static final JsonFactory jsonFactory = new JsonFactory(); - - private static final String JSON_EMPTY_ARRAY = "[]"; - private static final String JSON_EMPTY_VALUE = ""; - - private GroupUtils() { } - - public static OFGroupModify toGroupModify(OFGroupMod gm) { - return OFFactories.getFactory(gm.getVersion()).buildGroupModify() - .setBuckets(gm.getBuckets()) - .setGroup(gm.getGroup()) - .setGroupType(gm.getGroupType()) - .setXid(gm.getXid()) - .build(); - } - - public static OFGroupDelete toGroupDelete(OFGroupMod gm) { - return OFFactories.getFactory(gm.getVersion()).buildGroupDelete() - /* don't care about buckets or type */ - .setGroup(gm.getGroup()) - .setGroupType(gm.getGroupType()) - .setXid(gm.getXid()) - .build(); - } - - public static OFGroupAdd toGroupAdd(OFGroupMod gm) { - return OFFactories.getFactory(gm.getVersion()).buildGroupAdd() - .setBuckets(gm.getBuckets()) - .setGroup(gm.getGroup()) - .setGroupType(gm.getGroupType()) - .setXid(gm.getXid()) - .build(); - } - - public static boolean setGroupIdFromString(OFGroupMod.Builder g, String s) { - if (g == null) { - throw new IllegalArgumentException("OFGroupMod cannot be null"); - } - if (s == null) { - throw new IllegalArgumentException("String cannot be null"); - } - - s = s.trim().toLowerCase(); - try { - g.setGroup(OFGroup.of(s.startsWith("0x") ? Integer.parseInt(s, 16) : Integer.parseInt(s))); - return true; - } catch (Exception e) { - log.error("Could not parse group ID {}", s); - return false; - } - } - - public static OFGroupType groupTypeFromString(String s) { - if (s == null) { - throw new IllegalArgumentException("String cannot be null"); - } - - s = s.trim().toLowerCase(); - - switch (s) { - case GROUP_TYPE_ALL: - return OFGroupType.ALL; - case GROUP_TYPE_FF: - return OFGroupType.FF; - case GROUP_TYPE_INDIRECT: - return OFGroupType.INDIRECT; - case GROUP_TYPE_SELECT: - return OFGroupType.SELECT; - default: - log.error("Unrecognized group type {}", s); - return null; - } - } - - public static boolean setGroupTypeFromString(OFGroupMod.Builder g, String s) { - if (g == null) { - throw new IllegalArgumentException("OFGroupMod cannot be null"); - } - - OFGroupType t = groupTypeFromString(s); - if (t != null) { - g.setGroupType(t); - return true; - } - return false; - } - - public static String groupTypeToString(OFGroupType t) { - if (t == null) { - throw new IllegalArgumentException("OFGroupType cannot be null"); - } - - switch (t) { - case ALL: - return GROUP_TYPE_ALL; - case FF: - return GROUP_TYPE_FF; - case INDIRECT: - return GROUP_TYPE_INDIRECT; - case SELECT: - return GROUP_TYPE_SELECT; - default: - log.error("Unrecognized group type {}", t); - return JSON_EMPTY_VALUE; - } - } - - /** - * Append an array of buckets to an existing JsonGenerator. - * This method assumes the field name of the array has been - * written already, if required. The appended data will - * be formatted as follows: - * [ - * { - * bucket-1 - * }, - * { - * bucket-2 - * }, - * ..., - * { - * bucket-n - * } - * ] - * @param jsonGen - * @param bucketList - */ - public static void groupBucketsToJsonArray(JsonGenerator jsonGen, List<OFBucket> bucketList) { - jsonGen.configure(Feature.WRITE_NUMBERS_AS_STRINGS, true); - - try { - int bucketId = 1; - jsonGen.writeStartArray(); - for (OFBucket b : bucketList) { - jsonGen.writeStartObject(); - jsonGen.writeNumberField(BUCKET_ID, bucketId++); /* not preserved from original, but indicates order */ - jsonGen.writeNumberField(BUCKET_WATCH_GROUP, b.getWatchGroup().getGroupNumber()); - jsonGen.writeNumberField(BUCKET_WATCH_PORT, b.getWatchPort().getPortNumber()); - jsonGen.writeNumberField(BUCKET_WEIGHT, b.getWeight()); - jsonGen.writeStringField(BUCKET_ACTIONS, ActionUtils.actionsToString(b.getActions())); /* TODO update to object array */ - jsonGen.writeEndObject(); - } - jsonGen.writeEndArray(); - } catch (IOException e) { - log.error("Error composing group bucket JSON array. {}", e.getMessage()); - return; - } - } - - /** - * Convert a list of group buckets into a JSON-formatted - * string. The string data will be formatted as follows: - * [ - * { - * bucket-1 - * }, - * { - * bucket-2 - * }, - * ..., - * { - * bucket-n - * } - * ] - * @param bucketList - * @return the string, formatted as described above - */ - public static String groupBucketsToJsonArray(List<OFBucket> bucketList) { - Writer w = new StringWriter(); - JsonGenerator jsonGen; - try { - jsonGen = jsonFactory.createGenerator(w); - } catch (IOException e) { - log.error("Could not instantiate JSON Generator. {}", e.getMessage()); - return JSON_EMPTY_ARRAY; - } - - groupBucketsToJsonArray(jsonGen, bucketList); - - return w.toString(); /* overridden impl returns contents of Writer's StringBuffer */ - } - - /** - * Convert a JSON-formatted string of group buckets to a - * Java list of buckets. The JSON format expected is: - * [ - * { - * bucket-1 - * }, - * { - * bucket-2 - * }, - * ..., - * { - * bucket-n - * } - * ] - * @param g the group-mod message to add the buckets - * @param json the string, formatted as described above - */ - public static boolean setGroupBucketsFromJsonArray(OFGroupMod.Builder g, String json) { - final Map<Integer, OFBucket> bucketsById = new HashMap<Integer, OFBucket>(); - final MappingJsonFactory f = new MappingJsonFactory(); - - if (g == null) { - throw new IllegalArgumentException("OFGroupMod cannot be null"); - } - if (json == null) { - throw new IllegalArgumentException("JSON string cannot be null"); - } - - final JsonParser jp; - try { - jp = f.createParser(json); - } catch (IOException e) { - log.error("Could not create JSON parser for {}", json); - return false; - } - - try { - if (jp.nextToken() != JsonToken.START_ARRAY) { - throw new IOException("Expected START_ARRAY"); - } - - while (jp.nextToken() != JsonToken.END_ARRAY) { - if (jp.getCurrentToken() != JsonToken.START_OBJECT) { - throw new IOException("Expected START_OBJECT"); - } - - OFBucket.Builder b = OFFactories.getFactory(g.getVersion()).buildBucket(); - int bucketId = -1; - - while (jp.nextToken() != JsonToken.END_OBJECT) { - if (jp.getCurrentToken() != JsonToken.FIELD_NAME) { - throw new IOException("Expected FIELD_NAME"); - } - - String key = jp.getCurrentName().toLowerCase().trim(); - jp.nextToken(); - String value = jp.getText().toLowerCase().trim(); - switch (key) { - case BUCKET_ID: - bucketId = value.startsWith("0x") ? Integer.parseInt(value, 16) : Integer.parseInt(value); - break; - case BUCKET_WATCH_GROUP: - b.setWatchGroup(OFGroup.of(value.startsWith("0x") ? Integer.parseInt(value, 16) : Integer.parseInt(value))); - break; - case BUCKET_WATCH_PORT: - b.setWatchPort(OFPort.of(value.startsWith("0x") ? Integer.parseInt(value, 16) : Integer.parseInt(value))); - break; - case BUCKET_WEIGHT: - b.setWeight(value.startsWith("0x") ? Integer.parseInt(value, 16) : Integer.parseInt(value)); - break; - case BUCKET_ACTIONS: - b.setActions(ActionUtils.fromString(value, b.getVersion())); - break; - default: - log.warn("Unknown bucket key {}", key); - break; - } - } - if (bucketId != -1) { - bucketsById.put(bucketId, b.build()); - } else { - log.error("Must provide a bucket ID for bucket {}", b); - } - } - } catch (IOException e) { - log.error("Could not parse: {}", json); - log.error("JSON parse error message: {}", e.getMessage()); - return false; - } - - g.setBuckets( - bucketsById.entrySet() - .stream() - .sorted( /* invert to get sorted smallest to largest */ - (e1, e2) -> Integer.compare(e1.getKey(), e2.getKey()) - ) - .map(Map.Entry::getValue) - .collect(Collectors.toList()) - ); - return true; - } + private static final Logger log = LoggerFactory.getLogger(GroupUtils.class); + + public static final String GROUP_ID = "group_id"; + public static final String GROUP_ID_MAX = "max"; + public static final String GROUP_ID_ANY = "any"; + public static final String GROUP_ID_ALL = "all"; + + public static final String GROUP_TYPE = "group_type"; + public static final String GROUP_TYPE_FF = "fast_failover"; + public static final String GROUP_TYPE_ALL = "all"; + public static final String GROUP_TYPE_SELECT = "select"; + public static final String GROUP_TYPE_INDIRECT = "indirect"; + + public static final String GROUP_BUCKETS = "group_buckets"; + public static final String BUCKET_ID = "bucket_id"; + public static final String BUCKET_WEIGHT = "bucket_weight"; + public static final String BUCKET_WATCH_PORT = "bucket_watch_port"; + public static final String BUCKET_WATCH_GROUP = "bucket_watch_group"; + public static final String BUCKET_ACTIONS = "bucket_actions"; + + private static final JsonFactory jsonFactory = new JsonFactory(); + + private static final String JSON_EMPTY_ARRAY = "[]"; + private static final String JSON_EMPTY_VALUE = ""; + + private GroupUtils() { } + + public static OFGroupModify toGroupModify(OFGroupMod gm) { + return OFFactories.getFactory(gm.getVersion()).buildGroupModify() + .setBuckets(gm.getBuckets()) + .setGroup(gm.getGroup()) + .setGroupType(gm.getGroupType()) + .setXid(gm.getXid()) + .build(); + } + + public static OFGroupDelete toGroupDelete(OFGroupMod gm) { + return OFFactories.getFactory(gm.getVersion()).buildGroupDelete() + /* don't care about buckets or type */ + .setGroup(gm.getGroup()) + .setGroupType(gm.getGroupType()) + .setXid(gm.getXid()) + .build(); + } + + public static OFGroupAdd toGroupAdd(OFGroupMod gm) { + return OFFactories.getFactory(gm.getVersion()).buildGroupAdd() + .setBuckets(gm.getBuckets()) + .setGroup(gm.getGroup()) + .setGroupType(gm.getGroupType()) + .setXid(gm.getXid()) + .build(); + } + + public static boolean setGroupIdFromString(OFGroupMod.Builder g, String s) { + if (g == null) { + throw new IllegalArgumentException("OFGroupMod cannot be null"); + } + if (s == null) { + throw new IllegalArgumentException("String cannot be null"); + } + OFGroup group = groupIdFromString(s); + if (group == null) { + log.error("Could not set group ID {} due to parse error", s); + return false; + } else { + g.setGroup(group); + return true; + } + } + + public static OFGroup groupIdFromString(String s) { + if (s == null) { + throw new IllegalArgumentException("String cannot be null"); + } + s = s.trim().toLowerCase(); + try { + if (s.equals(GROUP_ID_ALL)) { + return OFGroup.ALL; + } else if (s.equals(GROUP_ID_ANY)) { + return OFGroup.ANY; + } else if (s.equals(GROUP_ID_MAX)) { + return OFGroup.MAX; + } else { + return OFGroup.of(s.startsWith("0x") ? Integer.parseInt(s.replaceFirst("0x", ""), 16) : Integer.parseInt(s)); + } + } catch (Exception e) { + log.error("Could not parse group ID {}", s); + return null; + } + } + + public static String groupIdToString(OFGroup g) { + if (g == null) { + throw new IllegalArgumentException("Group ID cannot be null"); + } + if (g.equals(OFGroup.ALL)) { + return GROUP_ID_ALL; + } + if (g.equals(OFGroup.ANY)) { + return GROUP_ID_ANY; + } + if (g.equals(OFGroup.MAX)) { + return GROUP_ID_MAX; + } + return Integer.toString(g.getGroupNumber()); + } + + public static OFGroupType groupTypeFromString(String s) { + if (s == null) { + throw new IllegalArgumentException("String cannot be null"); + } + + s = s.trim().toLowerCase(); + + switch (s) { + case GROUP_TYPE_ALL: + return OFGroupType.ALL; + case GROUP_TYPE_FF: + return OFGroupType.FF; + case GROUP_TYPE_INDIRECT: + return OFGroupType.INDIRECT; + case GROUP_TYPE_SELECT: + return OFGroupType.SELECT; + default: + log.error("Unrecognized group type {}", s); + return null; + } + } + + public static boolean setGroupTypeFromString(OFGroupMod.Builder g, String s) { + if (g == null) { + throw new IllegalArgumentException("OFGroupMod cannot be null"); + } + + OFGroupType t = groupTypeFromString(s); + if (t != null) { + g.setGroupType(t); + return true; + } + return false; + } + + public static String groupTypeToString(OFGroupType t) { + if (t == null) { + throw new IllegalArgumentException("OFGroupType cannot be null"); + } + + switch (t) { + case ALL: + return GROUP_TYPE_ALL; + case FF: + return GROUP_TYPE_FF; + case INDIRECT: + return GROUP_TYPE_INDIRECT; + case SELECT: + return GROUP_TYPE_SELECT; + default: + log.error("Unrecognized group type {}", t); + return JSON_EMPTY_VALUE; + } + } + + /** + * Append an array of buckets to an existing JsonGenerator. + * This method assumes the field name of the array has been + * written already, if required. The appended data will + * be formatted as follows: + * [ + * { + * bucket-1 + * }, + * { + * bucket-2 + * }, + * ..., + * { + * bucket-n + * } + * ] + * @param jsonGen + * @param bucketList + */ + public static void groupBucketsToJsonArray(JsonGenerator jsonGen, List<OFBucket> bucketList) { + jsonGen.configure(Feature.WRITE_NUMBERS_AS_STRINGS, true); + + try { + int bucketId = 1; + jsonGen.writeStartArray(); + for (OFBucket b : bucketList) { + jsonGen.writeStartObject(); + jsonGen.writeNumberField(BUCKET_ID, bucketId++); /* not preserved from original, but indicates order */ + jsonGen.writeStringField(BUCKET_WATCH_GROUP, groupIdToString(b.getWatchGroup())); + jsonGen.writeStringField(BUCKET_WATCH_PORT, MatchUtils.portToString(b.getWatchPort())); + jsonGen.writeNumberField(BUCKET_WEIGHT, b.getWeight()); + jsonGen.writeStringField(BUCKET_ACTIONS, ActionUtils.actionsToString(b.getActions())); /* TODO update to object array */ + jsonGen.writeEndObject(); + } + jsonGen.writeEndArray(); + } catch (IOException e) { + log.error("Error composing group bucket JSON array. {}", e.getMessage()); + return; + } + } + + /** + * Convert a list of group buckets into a JSON-formatted + * string. The string data will be formatted as follows: + * [ + * { + * bucket-1 + * }, + * { + * bucket-2 + * }, + * ..., + * { + * bucket-n + * } + * ] + * @param bucketList + * @return the string, formatted as described above + */ + public static String groupBucketsToJsonArray(List<OFBucket> bucketList) { + Writer w = new StringWriter(); + JsonGenerator jsonGen; + try { + jsonGen = jsonFactory.createGenerator(w); + } catch (IOException e) { + log.error("Could not instantiate JSON Generator. {}", e.getMessage()); + return JSON_EMPTY_ARRAY; + } + + groupBucketsToJsonArray(jsonGen, bucketList); + + return w.toString(); /* overridden impl returns contents of Writer's StringBuffer */ + } + + /** + * Convert a JSON-formatted string of group buckets to a + * Java list of buckets. The JSON format expected is: + * [ + * { + * bucket-1 + * }, + * { + * bucket-2 + * }, + * ..., + * { + * bucket-n + * } + * ] + * @param g the group-mod message to add the buckets + * @param json the string, formatted as described above + */ + public static boolean setGroupBucketsFromJsonArray(OFGroupMod.Builder g, String json) { + final Map<Integer, OFBucket> bucketsById = new HashMap<Integer, OFBucket>(); + final MappingJsonFactory f = new MappingJsonFactory(); + + if (g == null) { + throw new IllegalArgumentException("OFGroupMod cannot be null"); + } + if (json == null) { + throw new IllegalArgumentException("JSON string cannot be null"); + } + + final JsonParser jp; + try { + jp = f.createParser(json); + } catch (IOException e) { + log.error("Could not create JSON parser for {}", json); + return false; + } + + try { + if (jp.nextToken() != JsonToken.START_ARRAY) { + throw new IOException("Expected START_ARRAY"); + } + + while (jp.nextToken() != JsonToken.END_ARRAY) { + if (jp.getCurrentToken() != JsonToken.START_OBJECT) { + throw new IOException("Expected START_OBJECT"); + } + + OFBucket.Builder b = OFFactories.getFactory(g.getVersion()).buildBucket(); + int bucketId = -1; + + while (jp.nextToken() != JsonToken.END_OBJECT) { + if (jp.getCurrentToken() != JsonToken.FIELD_NAME) { + throw new IOException("Expected FIELD_NAME"); + } + + String key = jp.getCurrentName().toLowerCase().trim(); + jp.nextToken(); + String value = jp.getText().toLowerCase().trim(); + switch (key) { + case BUCKET_ID: + bucketId = value.startsWith("0x") ? Integer.parseInt(value.replaceFirst("0x", ""), 16) : Integer.parseInt(value); + break; + case BUCKET_WATCH_GROUP: + b.setWatchGroup(groupIdFromString(key)); + break; + case BUCKET_WATCH_PORT: + b.setWatchPort(MatchUtils.portFromString(key)); + break; + case BUCKET_WEIGHT: + b.setWeight(value.startsWith("0x") ? Integer.parseInt(value.replaceFirst("0x", ""), 16) : Integer.parseInt(value)); + break; + case BUCKET_ACTIONS: + b.setActions(ActionUtils.fromString(value, b.getVersion())); // TODO update to JSON + break; + default: + log.warn("Unknown bucket key {}", key); + break; + } + } + if (bucketId != -1) { + bucketsById.put(bucketId, b.build()); + } else { + log.error("Must provide a bucket ID for bucket {}", b); + } + } + } catch (IOException e) { + log.error("Could not parse: {}", json); + log.error("JSON parse error message: {}", e.getMessage()); + return false; + } + + g.setBuckets( + bucketsById.entrySet() + .stream() + .sorted( /* invert to get sorted smallest to largest */ + (e1, e2) -> Integer.compare(e1.getKey(), e2.getKey()) + ) + .map(Map.Entry::getValue) + .collect(Collectors.toList()) + ); + return true; + } } \ No newline at end of file diff --git a/src/main/java/net/floodlightcontroller/util/MatchUtils.java b/src/main/java/net/floodlightcontroller/util/MatchUtils.java index 77fbf3492f79fe9edd99979cd3b6ed96016a5f91..b11a9ed061096b97c7dc5dc608651a8145770c24 100644 --- a/src/main/java/net/floodlightcontroller/util/MatchUtils.java +++ b/src/main/java/net/floodlightcontroller/util/MatchUtils.java @@ -27,7 +27,6 @@ import org.projectfloodlight.openflow.types.IpProtocol; import org.projectfloodlight.openflow.types.MacAddress; import org.projectfloodlight.openflow.types.OFBooleanValue; import org.projectfloodlight.openflow.types.OFMetadata; -import org.projectfloodlight.openflow.types.OFPort; import org.projectfloodlight.openflow.types.OFVlanVidMatch; import org.projectfloodlight.openflow.types.OFVlanVidMatchWithMask; import org.projectfloodlight.openflow.types.PacketType; @@ -37,6 +36,7 @@ import org.projectfloodlight.openflow.types.U32; import org.projectfloodlight.openflow.types.U64; import org.projectfloodlight.openflow.types.U8; import org.projectfloodlight.openflow.types.VlanPcp; +import org.projectfloodlight.openflow.types.OFPort; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -77,6 +77,9 @@ public class MatchUtils { public static final String STR_PORT_FLOOD = "flood"; public static final String STR_PORT_NORMAL = "normal"; public static final String STR_PORT_TABLE = "table"; + public static final String STR_PORT_MAX = "max"; + public static final String STR_PORT_ANY = "any"; + public static final String STR_IN_PHYS_PORT = "in_phys_port"; @@ -611,16 +614,9 @@ public class MatchUtils { switch (key_value[0]) { case STR_IN_PORT: if (dataMask.length == 1) { - if (dataMask[0].equals(STR_PORT_LOCAL)) { - mb.setExact(MatchField.IN_PORT, OFPort.LOCAL); - } else if (dataMask[0].equals(STR_PORT_CONTROLLER)) { - mb.setExact(MatchField.IN_PORT, OFPort.CONTROLLER); - } else { - mb.setExact(MatchField.IN_PORT, OFPort.of(U32.of(dataMask[0].contains("0x") ? Long.parseLong(dataMask[0].replaceFirst("0x", ""), 16) : Long.parseLong(dataMask[0])).getRaw())); - } + mb.setExact(MatchField.IN_PORT, portFromString(dataMask[0])); } else { - mb.setMasked(MatchField.IN_PORT, OFPort.of(U32.of(dataMask[0].contains("0x") ? Long.parseLong(dataMask[0].replaceFirst("0x", ""), 16) : Long.parseLong(dataMask[0])).getRaw()), - OFPort.of(U32.of(dataMask[1].contains("0x") ? Long.parseLong(dataMask[1].replaceFirst("0x", ""), 16) : Long.parseLong(dataMask[1])).getRaw())); + mb.setMasked(MatchField.IN_PORT, portFromString(dataMask[0]), portFromString(dataMask[1])); } break; case STR_DL_DST: /* Only accept hex-string for MAC addresses */ @@ -1027,10 +1023,9 @@ public class MatchUtils { log.warn("Why are we here?"); }*/ if (dataMask.length == 1) { - mb.setExact(MatchField.ACTSET_OUTPUT, OFPort.of(U32.of(dataMask[0].contains("0x") ? Long.parseLong(dataMask[0].replaceFirst("0x", ""), 16) : Long.parseLong(dataMask[0])).getRaw())); + mb.setExact(MatchField.ACTSET_OUTPUT, portFromString(dataMask[0])); } else { - mb.setMasked(MatchField.ACTSET_OUTPUT, OFPort.of(U32.of(dataMask[0].contains("0x") ? Long.parseLong(dataMask[0].replaceFirst("0x", ""), 16) : Long.parseLong(dataMask[0])).getRaw()), - OFPort.of(U32.of(dataMask[1].contains("0x") ? Long.parseLong(dataMask[1].replaceFirst("0x", ""), 16) : Long.parseLong(dataMask[1])).getRaw())); + mb.setMasked(MatchField.ACTSET_OUTPUT, portFromString(dataMask[0]), portFromString(dataMask[1])); } break; case STR_PACKET_TYPE: @@ -1049,4 +1044,78 @@ public class MatchUtils { } return mb.build(); } -} + + public static OFPort portFromString(String s) { + if (s == null) { + throw new IllegalArgumentException("Port string cannot be null"); + } + + s = s.trim().toLowerCase(); + switch (s) { + case MatchUtils.STR_PORT_ALL: + return OFPort.ALL; + case MatchUtils.STR_PORT_CONTROLLER: + return OFPort.CONTROLLER; + case MatchUtils.STR_PORT_FLOOD: + return OFPort.FLOOD; + case MatchUtils.STR_PORT_IN_PORT: + return OFPort.IN_PORT; + case MatchUtils.STR_PORT_LOCAL: + return OFPort.LOCAL; + case MatchUtils.STR_PORT_NORMAL: + return OFPort.NORMAL; + case MatchUtils.STR_PORT_TABLE: + return OFPort.TABLE; + case MatchUtils.STR_PORT_MAX: + return OFPort.MAX; + case MatchUtils.STR_PORT_ANY: + return OFPort.ANY; + default: + log.debug("Port {} was not a special port string. Parsing as raw int or hex", s); + } + + try { + return OFPort.of(U32.of(s.contains("0x") ? + Long.parseLong(s.replaceFirst("0x", ""), 16) : + Long.parseLong(s)).getRaw()); + } catch (NumberFormatException e) { + log.error("Could not parse port '{}'", s); + return null; + } + } + + public static String portToString(OFPort p) { + if (p == null) { + throw new IllegalArgumentException("Port cannot be null"); + } + + if (p.equals(OFPort.ALL)) { + return STR_PORT_ALL; + } + if (p.equals(OFPort.ANY)) { + return STR_PORT_ANY; + } + if (p.equals(OFPort.CONTROLLER)) { + return STR_PORT_CONTROLLER; + } + if (p.equals(OFPort.FLOOD)) { + return STR_PORT_FLOOD; + } + if (p.equals(OFPort.IN_PORT)) { + return STR_PORT_IN_PORT; + } + if (p.equals(OFPort.LOCAL)) { + return STR_PORT_LOCAL; + } + if (p.equals(OFPort.NORMAL)) { + return STR_PORT_NORMAL; + } + if (p.equals(OFPort.MAX)) { + return STR_PORT_MAX; + } + if (p.equals(OFPort.TABLE)) { + return STR_PORT_TABLE; + } + return Integer.toString(p.getPortNumber()); + } +} \ No newline at end of file