package net.floodlightcontroller.util;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.util.ArrayDeque;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.projectfloodlight.openflow.protocol.OFActionCopyField;
import org.projectfloodlight.openflow.protocol.OFFactories;
import org.projectfloodlight.openflow.protocol.OFFactory;
import org.projectfloodlight.openflow.protocol.OFFlowMod;
import org.projectfloodlight.openflow.protocol.OFOxmList;
import org.projectfloodlight.openflow.protocol.OFVersion;
import org.projectfloodlight.openflow.protocol.action.OFAction;
import org.projectfloodlight.openflow.protocol.action.OFActionEnqueue;
import org.projectfloodlight.openflow.protocol.action.OFActionExperimenter;
import org.projectfloodlight.openflow.protocol.action.OFActionGroup;
import org.projectfloodlight.openflow.protocol.action.OFActionMeter;
import org.projectfloodlight.openflow.protocol.action.OFActionOutput;
import org.projectfloodlight.openflow.protocol.action.OFActionPopMpls;
import org.projectfloodlight.openflow.protocol.action.OFActionPushMpls;
import org.projectfloodlight.openflow.protocol.action.OFActionPushPbb;
import org.projectfloodlight.openflow.protocol.action.OFActionPushVlan;
import org.projectfloodlight.openflow.protocol.action.OFActionSetDlDst;
import org.projectfloodlight.openflow.protocol.action.OFActionSetDlSrc;
import org.projectfloodlight.openflow.protocol.action.OFActionSetField;
import org.projectfloodlight.openflow.protocol.action.OFActionSetMplsLabel;
import org.projectfloodlight.openflow.protocol.action.OFActionSetMplsTc;
import org.projectfloodlight.openflow.protocol.action.OFActionSetMplsTtl;
import org.projectfloodlight.openflow.protocol.action.OFActionSetNwDst;
import org.projectfloodlight.openflow.protocol.action.OFActionSetNwEcn;
import org.projectfloodlight.openflow.protocol.action.OFActionSetNwSrc;
import org.projectfloodlight.openflow.protocol.action.OFActionSetNwTos;
import org.projectfloodlight.openflow.protocol.action.OFActionSetNwTtl;
import org.projectfloodlight.openflow.protocol.action.OFActionSetQueue;
import org.projectfloodlight.openflow.protocol.action.OFActionSetTpDst;
import org.projectfloodlight.openflow.protocol.action.OFActionSetTpSrc;
import org.projectfloodlight.openflow.protocol.action.OFActionSetVlanPcp;
import org.projectfloodlight.openflow.protocol.action.OFActionSetVlanVid;
import org.projectfloodlight.openflow.protocol.oxm.OFOxm;
import org.projectfloodlight.openflow.protocol.oxm.OFOxmActsetOutput;
import org.projectfloodlight.openflow.protocol.oxm.OFOxmArpOp;
import org.projectfloodlight.openflow.protocol.oxm.OFOxmArpSha;
import org.projectfloodlight.openflow.protocol.oxm.OFOxmArpSpa;
import org.projectfloodlight.openflow.protocol.oxm.OFOxmArpTha;
import org.projectfloodlight.openflow.protocol.oxm.OFOxmArpTpa;
import org.projectfloodlight.openflow.protocol.oxm.OFOxmEthDst;
import org.projectfloodlight.openflow.protocol.oxm.OFOxmEthSrc;
import org.projectfloodlight.openflow.protocol.oxm.OFOxmEthType;
import org.projectfloodlight.openflow.protocol.oxm.OFOxmIcmpv4Code;
import org.projectfloodlight.openflow.protocol.oxm.OFOxmIcmpv4Type;
import org.projectfloodlight.openflow.protocol.oxm.OFOxmIcmpv6Code;
import org.projectfloodlight.openflow.protocol.oxm.OFOxmIcmpv6Type;
import org.projectfloodlight.openflow.protocol.oxm.OFOxmIpDscp;
import org.projectfloodlight.openflow.protocol.oxm.OFOxmIpEcn;
import org.projectfloodlight.openflow.protocol.oxm.OFOxmIpProto;
import org.projectfloodlight.openflow.protocol.oxm.OFOxmIpv4Dst;
import org.projectfloodlight.openflow.protocol.oxm.OFOxmIpv4Src;
import org.projectfloodlight.openflow.protocol.oxm.OFOxmIpv6Dst;
import org.projectfloodlight.openflow.protocol.oxm.OFOxmIpv6Exthdr;
import org.projectfloodlight.openflow.protocol.oxm.OFOxmIpv6Flabel;
import org.projectfloodlight.openflow.protocol.oxm.OFOxmIpv6NdSll;
import org.projectfloodlight.openflow.protocol.oxm.OFOxmIpv6NdTarget;
import org.projectfloodlight.openflow.protocol.oxm.OFOxmIpv6NdTll;
import org.projectfloodlight.openflow.protocol.oxm.OFOxmIpv6Src;
import org.projectfloodlight.openflow.protocol.oxm.OFOxmMetadata;
import org.projectfloodlight.openflow.protocol.oxm.OFOxmMplsBos;
import org.projectfloodlight.openflow.protocol.oxm.OFOxmMplsLabel;
import org.projectfloodlight.openflow.protocol.oxm.OFOxmMplsTc;
import org.projectfloodlight.openflow.protocol.oxm.OFOxmPacketType;
import org.projectfloodlight.openflow.protocol.oxm.OFOxmSctpDst;
import org.projectfloodlight.openflow.protocol.oxm.OFOxmSctpSrc;
import org.projectfloodlight.openflow.protocol.oxm.OFOxmTcpDst;
import org.projectfloodlight.openflow.protocol.oxm.OFOxmTcpFlags;
import org.projectfloodlight.openflow.protocol.oxm.OFOxmTcpSrc;
import org.projectfloodlight.openflow.protocol.oxm.OFOxmUdpDst;
import org.projectfloodlight.openflow.protocol.oxm.OFOxmUdpSrc;
import org.projectfloodlight.openflow.protocol.oxm.OFOxmVlanPcp;
import org.projectfloodlight.openflow.protocol.oxm.OFOxmVlanVid;
import org.projectfloodlight.openflow.types.ArpOpcode;
import org.projectfloodlight.openflow.types.EthType;
import org.projectfloodlight.openflow.types.ICMPv4Code;
import org.projectfloodlight.openflow.types.ICMPv4Type;
import org.projectfloodlight.openflow.types.IPv4Address;
import org.projectfloodlight.openflow.types.IPv6Address;
import org.projectfloodlight.openflow.types.IPv6FlowLabel;
import org.projectfloodlight.openflow.types.IpDscp;
import org.projectfloodlight.openflow.types.IpEcn;
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.TransportPort;
import org.projectfloodlight.openflow.types.U16;
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.VlanVid;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.core.JsonGenerator.Feature;
import com.fasterxml.jackson.core.JsonParser;
/**
* OFAction helper functions. Use with any OpenFlowJ-Loxi Action.
* String utility functions for converting OFActions to and from
* dpctl/ofctl-style strings, which is primarily used by the
* static flow pusher.
*
* Includes string methods refactored from StaticFlowEntryPusher
*
* @author Ryan Izard <ryan.izard@bigswitch.com, rizard@g.clemson.edu>
*/
public class ActionUtils {
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 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 = "{}";
/**
* 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(ActionUtils.portToString(((OFActionOutput)a).getPort()));
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();
}
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.
}
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);
}
switch (actionData[0]) {
case MatchUtils.STR_ARP_OPCODE:
a = f.actions().buildSetField()
.setField(f.oxms().buildArpOp()
.setValue(ArpOpcode.of(ParseUtils.parseHexOrDecInt(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:
a = f.actions().buildSetField()
.setField(f.oxms().buildEthType()
.setValue(EthType.of(ParseUtils.parseHexOrDecInt(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:
a = f.actions().buildSetField()
.setField(f.oxms().buildVlanVid()
.setValue(OFVlanVidMatch.ofVlan(ParseUtils.parseHexOrDecInt(actionData[1])))
.build())
.build();
break;
case MatchUtils.STR_DL_VLAN_PCP:
a = f.actions().buildSetField()
.setField(f.oxms().buildVlanPcp()
.setValue(VlanPcp.of(ParseUtils.parseHexOrDecByte(actionData[1])))
.build())
.build();
break;
case MatchUtils.STR_ICMP_CODE:
a = f.actions().buildSetField()
.setField(f.oxms().buildIcmpv4Code()
.setValue(ICMPv4Code.of(ParseUtils.parseHexOrDecShort(actionData[1])))
.build())
.build();
break;
case MatchUtils.STR_ICMP_TYPE:
a = f.actions().buildSetField()
.setField(f.oxms().buildIcmpv4Type()
.setValue(ICMPv4Type.of(ParseUtils.parseHexOrDecShort(actionData[1])))
.build())
.build();
break;
case MatchUtils.STR_ICMPV6_CODE:
a = f.actions().buildSetField()
.setField(f.oxms().buildIcmpv6Code()
.setValue(U8.of(ParseUtils.parseHexOrDecShort(actionData[1])))
.build())
.build();
break;
case MatchUtils.STR_ICMPV6_TYPE:
a = f.actions().buildSetField()
.setField(f.oxms().buildIcmpv6Type()
.setValue(U8.of(ParseUtils.parseHexOrDecShort(actionData[1])))
.build())
.build();
break;
case MatchUtils.STR_NW_PROTO:
a = f.actions().buildSetField()
.setField(f.oxms().buildIpProto()
.setValue(IpProtocol.of(ParseUtils.parseHexOrDecShort(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:
a = f.actions().buildSetField()
.setField(f.oxms().buildIpv6Flabel()
.setValue(IPv6FlowLabel.of(ParseUtils.parseHexOrDecInt(actionData[1])))
.build())
.build();
break;
case MatchUtils.STR_NW_ECN:
a = f.actions().buildSetField()
.setField(f.oxms().buildIpEcn()
.setValue(IpEcn.of(ParseUtils.parseHexOrDecByte(actionData[1])))
.build())
.build();
break;
case MatchUtils.STR_NW_DSCP:
a = f.actions().buildSetField()
.setField(f.oxms().buildIpDscp()
.setValue(IpDscp.of(ParseUtils.parseHexOrDecByte(actionData[1])))
.build())
.build();
break;
case MatchUtils.STR_SCTP_SRC:
a = f.actions().buildSetField()
.setField(f.oxms().buildSctpSrc()
.setValue(TransportPort.of(ParseUtils.parseHexOrDecInt(actionData[1])))
.build())
.build();
break;
case MatchUtils.STR_SCTP_DST:
a = f.actions().buildSetField()
.setField(f.oxms().buildSctpDst()
.setValue(TransportPort.of(ParseUtils.parseHexOrDecInt(actionData[1])))
.build())
.build();
break;
case MatchUtils.STR_TCP_SRC:
a = f.actions().buildSetField()
.setField(f.oxms().buildTcpSrc()
.setValue(TransportPort.of(ParseUtils.parseHexOrDecInt(actionData[1])))
.build())
.build();
break;
case MatchUtils.STR_TCP_DST:
a = f.actions().buildSetField()
.setField(f.oxms().buildTcpDst()
.setValue(TransportPort.of(ParseUtils.parseHexOrDecInt(actionData[1])))
.build())
.build();
break;
case MatchUtils.STR_UDP_SRC:
a = f.actions().buildSetField()
.setField(f.oxms().buildUdpSrc()
.setValue(TransportPort.of(ParseUtils.parseHexOrDecInt(actionData[1])))
.build())
.build();
break;
case MatchUtils.STR_UDP_DST:
a = f.actions().buildSetField()
.setField(f.oxms().buildUdpDst()
.setValue(TransportPort.of(ParseUtils.parseHexOrDecInt(actionData[1])))
.build())
.build();
break;
case MatchUtils.STR_MPLS_LABEL:
a = f.actions().buildSetField()
.setField(f.oxms().buildMplsLabel()
.setValue(U32.of(ParseUtils.parseHexOrDecLong(actionData[1])))
.build())
.build();
break;
case MatchUtils.STR_MPLS_TC:
a = f.actions().buildSetField()
.setField(f.oxms().buildMplsTc()
.setValue(U8.of(ParseUtils.parseHexOrDecShort(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:
a = f.actions().buildSetField()
.setField(f.oxms().buildMetadata()
.setValue(OFMetadata.of(U64.of(ParseUtils.parseHexOrDecLong(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:
a = f.actions().buildSetField()
.setField(f.oxms().buildTcpFlags()
.setValue(U16.of(ParseUtils.parseHexOrDecInt(actionData[1])))
.build())
.build();
break;
default:
log.error("Unexpected OF1.2+ setfield '{}'", actionData);
break;
}
break;
case STR_GROUP:
a = f.actions().buildGroup()
.setGroup(GroupUtils.groupIdFromString(pair))
.build();
break;
case STR_MPLS_LABEL_SET:
a = f.actions().buildSetMplsLabel()
.setMplsLabel(ParseUtils.parseHexOrDecLong(pair))
.build();
break;
case STR_MPLS_POP:
a = f.actions().buildPopMpls()
.setEthertype(EthType.of(ParseUtils.parseHexOrDecInt(pair)))
.build();
break;
case STR_MPLS_PUSH:
a = f.actions().buildPushMpls()
.setEthertype(EthType.of(ParseUtils.parseHexOrDecInt(pair)))
.build();
break;
case STR_MPLS_TC_SET:
a = f.actions().buildSetMplsTc()
.setMplsTc(ParseUtils.parseHexOrDecShort(pair))
.build();
break;
case STR_MPLS_TTL_DEC:
a = f.actions().decMplsTtl();
break;
case STR_MPLS_TTL_SET:
a = f.actions().buildSetMplsTtl()
.setMplsTtl(ParseUtils.parseHexOrDecShort(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
a = f.actions().buildSetNwEcn()
.setNwEcn(IpEcn.of(ParseUtils.parseHexOrDecByte(pair)))
.build();
break;
case STR_NW_TTL_DEC:
a = f.actions().decNwTtl();
break;
case STR_NW_TTL_SET:
a = f.actions().buildSetNwTtl()
.setNwTtl(ParseUtils.parseHexOrDecShort(pair))
.build();
break;
case STR_PBB_POP:
a = f.actions().popPbb();
break;
case STR_PBB_PUSH:
a = f.actions().buildPushPbb()
.setEthertype(EthType.of(ParseUtils.parseHexOrDecInt(pair)))
.build();
break;
case STR_QUEUE_SET:
a = f.actions().buildSetQueue()
.setQueueId(ParseUtils.parseHexOrDecLong(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:
a = f.actions().buildPushVlan()
.setEthertype(EthType.of(ParseUtils.parseHexOrDecInt(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:
a = f.actions().buildMeter()
.setMeterId(ParseUtils.parseHexOrDecLong(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;
}
/**
* 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;
}
}
/**
* 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);
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");
}
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(ParseUtils.parseHexOrDecInt(value));
break;
case "dst_offset_bits":
b.setDstOffset(ParseUtils.parseHexOrDecInt(value));
break;
case "num_bits":
b.setNBits(ParseUtils.parseHexOrDecInt(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;
}
}
/**
* 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;
}
public static OFPort portFromString(String s) {
return MatchUtils.portFromString(s);
}
public static String portToString(OFPort p) {
return MatchUtils.portToString(p);
}
/**
* 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 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;
}
int queueid = 0;
if (n.group(2) != null) {
try {
queueid = ParseUtils.parseHexOrDecInt(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_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(ParseUtils.parseHexOrDecShort(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_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(ParseUtils.parseHexOrDecByte(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_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_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_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(ParseUtils.parseHexOrDecByte(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_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_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_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_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 {
OFActionSetTpDst a = OFFactories.getFactory(version).actions().buildSetTpDst()
.setTpPort(TransportPort.of(Integer.parseInt(actionToDecode)))
.build();
log.debug("action {}", a);
return a;
}
catch (NumberFormatException e) {
log.debug("Invalid dst-port in: {} (error ignored)", actionToDecode);
return null;
}
}
}