ofp-actions: Set-Field OF 1.0/1.1 compatibility.
authorJarno Rajahalme <jrajahalme@nicira.com>
Thu, 24 Oct 2013 20:19:30 +0000 (13:19 -0700)
committerBen Pfaff <blp@nicira.com>
Fri, 1 Nov 2013 23:02:57 +0000 (16:02 -0700)
Output set field actions as standard OF1.0/1.1 set actions or to
reg_load instructions, when a compatible set action(s) do not exist.

Signed-off-by: Jarno Rajahalme <jrajahalme@nicira.com>
Signed-off-by: Ben Pfaff <blp@nicira.com>
lib/ofp-actions.c
lib/ofp-actions.h
tests/ovs-ofctl.at

index 761cea5..f166a73 100644 (file)
@@ -857,6 +857,203 @@ set_field_to_nxast(const struct ofpact_set_field *sf, struct ofpbuf *openflow)
     }
 }
 
+/* Convert 'sf' to standard OpenFlow 1.1 actions, if we can, falling back
+ * to Nicira extensions if we must.
+ *
+ * We check only meta-flow types that can appear within set field actions and
+ * that have a mapping to compatible action types.  These struct mf_field
+ * definitions have a defined OXM or NXM header value and specify the field as
+ * writable. */
+static void
+set_field_to_openflow11(const struct ofpact_set_field *sf,
+                        struct ofpbuf *openflow)
+{
+    switch ((int) sf->field->id) {
+    case MFF_VLAN_TCI:
+        /* NXM_OF_VLAN_TCI to OpenFlow 1.1 mapping:
+         *
+         * If CFI=1, Add or modify VLAN VID & PCP.
+         *    OpenFlow 1.1 set actions only apply if the packet
+         *    already has VLAN tags.  To be sure that is the case
+         *    we have to push a VLAN header.  As we do not support
+         *    multiple layers of VLANs, this is a no-op, if a VLAN
+         *    header already exists.  This may backfire, however,
+         *    when we start supporting multiple layers of VLANs.
+         * If CFI=0, strip VLAN header, if any.
+         */
+        if (sf->value.be16 & htons(VLAN_CFI)) {
+            /* Push a VLAN tag, if one was not seen at action validation
+             * time. */
+            if (!sf->flow_has_vlan) {
+                ofputil_put_OFPAT11_PUSH_VLAN(openflow)->ethertype
+                    = htons(ETH_TYPE_VLAN_8021Q);
+            }
+            ofputil_put_OFPAT11_SET_VLAN_VID(openflow)->vlan_vid
+                = sf->value.be16 & htons(VLAN_VID_MASK);
+            ofputil_put_OFPAT11_SET_VLAN_PCP(openflow)->vlan_pcp
+                = vlan_tci_to_pcp(sf->value.be16);
+        } else {
+            /* If the flow did not match on vlan, we have no way of
+             * knowing if the vlan tag exists, so we must POP just to be
+             * sure. */
+            ofputil_put_OFPAT11_POP_VLAN(openflow);
+        }
+        break;
+
+    case MFF_VLAN_VID:
+        /* OXM VLAN_PCP to OpenFlow 1.1.
+         * Set field on OXM_OF_VLAN_VID onlyapplies to an existing vlan
+         * tag.  Clear the OFPVID_PRESENT bit.
+         */
+        ofputil_put_OFPAT11_SET_VLAN_VID(openflow)->vlan_vid
+            = sf->value.be16 & htons(VLAN_VID_MASK);
+        break;
+
+    case MFF_VLAN_PCP:
+        /* OXM VLAN_PCP to OpenFlow 1.1.
+         * OXM_OF_VLAN_PCP only applies to existing vlan tag. */
+        ofputil_put_OFPAT11_SET_VLAN_PCP(openflow)->vlan_pcp = sf->value.u8;
+        break;
+
+    case MFF_ETH_SRC:
+        memcpy(ofputil_put_OFPAT11_SET_DL_SRC(openflow)->dl_addr,
+               sf->value.mac, ETH_ADDR_LEN);
+        break;
+
+    case MFF_ETH_DST:
+        memcpy(ofputil_put_OFPAT11_SET_DL_DST(openflow)->dl_addr,
+               sf->value.mac, ETH_ADDR_LEN);
+        break;
+
+    case MFF_IPV4_SRC:
+        ofputil_put_OFPAT11_SET_NW_SRC(openflow)->nw_addr = sf->value.be32;
+        break;
+
+    case MFF_IPV4_DST:
+        ofputil_put_OFPAT11_SET_NW_DST(openflow)->nw_addr = sf->value.be32;
+        break;
+
+    case MFF_IP_DSCP:
+        ofputil_put_OFPAT11_SET_NW_TOS(openflow)->nw_tos = sf->value.u8;
+        break;
+
+    case MFF_IP_DSCP_SHIFTED:
+        ofputil_put_OFPAT11_SET_NW_TOS(openflow)->nw_tos = sf->value.u8 << 2;
+        break;
+
+    case MFF_IP_ECN:
+        ofputil_put_OFPAT11_SET_NW_ECN(openflow)->nw_ecn = sf->value.u8;
+        break;
+
+    case MFF_IP_TTL:
+        ofputil_put_OFPAT11_SET_NW_TTL(openflow)->nw_ttl = sf->value.u8;
+        break;
+
+    case MFF_TCP_SRC:
+    case MFF_UDP_SRC:
+    case MFF_SCTP_SRC:
+        ofputil_put_OFPAT11_SET_TP_SRC(openflow)->tp_port = sf->value.be16;
+        break;
+
+    case MFF_TCP_DST:
+    case MFF_UDP_DST:
+    case MFF_SCTP_DST:
+        ofputil_put_OFPAT11_SET_TP_DST(openflow)->tp_port = sf->value.be16;
+        break;
+
+    case MFF_MPLS_TC:           /* XXX */
+    case MFF_MPLS_LABEL:        /* XXX */
+    default:
+        set_field_to_nxast(sf, openflow);
+        break;
+    }
+}
+
+/* Convert 'sf' to standard OpenFlow 1.0 actions, if we can, falling back
+ * to Nicira extensions if we must.
+ *
+ * We check only meta-flow types that can appear within set field actions and
+ * that have a mapping to compatible action types.  These struct mf_field
+ * definitions have a defined OXM or NXM header value and specify the field as
+ * writable. */
+static void
+set_field_to_openflow10(const struct ofpact_set_field *sf,
+                        struct ofpbuf *openflow)
+{
+    switch ((int) sf->field->id) {
+    case MFF_VLAN_TCI:
+        /* NXM_OF_VLAN_TCI to OpenFlow 1.0 mapping:
+         *
+         * If CFI=1, Add or modify VLAN VID & PCP.
+         * If CFI=0, strip VLAN header, if any.
+         */
+        if (sf->value.be16 & htons(VLAN_CFI)) {
+            ofputil_put_OFPAT10_SET_VLAN_VID(openflow)->vlan_vid
+                = sf->value.be16 & htons(VLAN_VID_MASK);
+            ofputil_put_OFPAT10_SET_VLAN_PCP(openflow)->vlan_pcp
+                = vlan_tci_to_pcp(sf->value.be16);
+        } else {
+            ofputil_put_OFPAT10_STRIP_VLAN(openflow);
+        }
+        break;
+
+    case MFF_VLAN_VID:
+        /* OXM VLAN_VID to OpenFlow 1.0.
+         * Set field on OXM_OF_VLAN_VID onlyapplies to an existing vlan
+         * tag.  Clear the OFPVID_PRESENT bit.
+         */
+        ofputil_put_OFPAT10_SET_VLAN_VID(openflow)->vlan_vid
+            = sf->value.be16 & htons(VLAN_VID_MASK);
+        break;
+
+    case MFF_VLAN_PCP:
+        /* OXM VLAN_PCP to OpenFlow 1.0.
+         * OXM_OF_VLAN_PCP only applies to existing vlan tag. */
+        ofputil_put_OFPAT10_SET_VLAN_PCP(openflow)->vlan_pcp = sf->value.u8;
+        break;
+
+    case MFF_ETH_SRC:
+        memcpy(ofputil_put_OFPAT10_SET_DL_SRC(openflow)->dl_addr,
+               sf->value.mac, ETH_ADDR_LEN);
+        break;
+
+    case MFF_ETH_DST:
+        memcpy(ofputil_put_OFPAT10_SET_DL_DST(openflow)->dl_addr,
+               sf->value.mac, ETH_ADDR_LEN);
+        break;
+
+    case MFF_IPV4_SRC:
+        ofputil_put_OFPAT10_SET_NW_SRC(openflow)->nw_addr = sf->value.be32;
+        break;
+
+    case MFF_IPV4_DST:
+        ofputil_put_OFPAT10_SET_NW_DST(openflow)->nw_addr = sf->value.be32;
+        break;
+
+    case MFF_IP_DSCP:
+        ofputil_put_OFPAT10_SET_NW_TOS(openflow)->nw_tos = sf->value.u8;
+        break;
+
+    case MFF_IP_DSCP_SHIFTED:
+        ofputil_put_OFPAT10_SET_NW_TOS(openflow)->nw_tos = sf->value.u8 << 2;
+        break;
+
+    case MFF_TCP_SRC:
+    case MFF_UDP_SRC:
+        ofputil_put_OFPAT10_SET_TP_SRC(openflow)->tp_port = sf->value.be16;
+        break;
+
+    case MFF_TCP_DST:
+    case MFF_UDP_DST:
+        ofputil_put_OFPAT10_SET_TP_DST(openflow)->tp_port = sf->value.be16;
+        break;
+
+    default:
+        set_field_to_nxast(sf, openflow);
+        break;
+    }
+}
+
 static void
 set_field_to_openflow(const struct ofpact_set_field *sf,
                       struct ofpbuf *openflow)
@@ -865,8 +1062,12 @@ set_field_to_openflow(const struct ofpact_set_field *sf,
 
     if (oh->version >= OFP12_VERSION) {
         set_field_to_openflow12(sf, openflow);
+    } else if (oh->version == OFP11_VERSION) {
+        set_field_to_openflow11(sf, openflow);
+    } else if (oh->version == OFP10_VERSION) {
+        set_field_to_openflow10(sf, openflow);
     } else {
-        set_field_to_nxast(sf, openflow);
+        NOT_REACHED();
     }
 }
 
@@ -1789,6 +1990,15 @@ ofpact_check__(struct ofpact *a, struct flow *flow, ofp_port_t max_ports,
                          mf->name);
             return OFPERR_OFPBAC_MATCH_INCONSISTENT;
         }
+        /* Remember if we saw a vlan tag in the flow to aid translating to
+         * OpenFlow 1.1 if need be. */
+        ofpact_get_SET_FIELD(a)->flow_has_vlan =
+            (flow->vlan_tci & htons(VLAN_CFI)) == htons(VLAN_CFI);
+        if (mf->id == MFF_VLAN_TCI) {
+            /* The set field may add or remove the vlan tag,
+             * Mark the status temporarily. */
+            flow->vlan_tci = ofpact_get_SET_FIELD(a)->value.be16;
+        }
         return 0;
 
     case OFPACT_STACK_PUSH:
index 0478a9b..2268a36 100644 (file)
@@ -379,6 +379,7 @@ enum ofpact_mpls_position {
 struct ofpact_set_field {
     struct ofpact ofpact;
     const struct mf_field *field;
+    bool flow_has_vlan;   /* VLAN present at action validation time. */
     union mf_value value;
 };
 
index 01ef8e7..39f382f 100644 (file)
@@ -154,7 +154,7 @@ OFPT_FLOW_MOD: ADD tcp,nw_src=192.168.0.3,tp_dst=80 actions=set_queue:37,output:
 OFPT_FLOW_MOD: ADD udp,nw_src=192.168.0.3,tp_dst=53 actions=pop_queue,output:1
 OFPT_FLOW_MOD: ADD priority=60000 cookie:0x123456789abcdef hard:10 actions=CONTROLLER:65535
 OFPT_FLOW_MOD: ADD actions=note:41.42.43.00.00.00,note:00.01.02.03.04.05.06.07.00.00.00.00.00.00,note:00.00.00.00.00.00
-OFPT_FLOW_MOD: ADD ip actions=load:0xa04034d->NXM_OF_IP_SRC[]
+OFPT_FLOW_MOD: ADD ip actions=mod_nw_src:10.4.3.77
 OFPT_FLOW_MOD: ADD sctp actions=drop
 OFPT_FLOW_MOD: ADD sctp actions=drop
 OFPT_FLOW_MOD: ADD in_port=0 actions=resubmit:0
@@ -191,7 +191,7 @@ OFPT_FLOW_MOD (OF1.1): ADD table:255 tcp,nw_src=192.168.0.3,tp_dst=80 actions=se
 OFPT_FLOW_MOD (OF1.1): ADD table:255 udp,nw_src=192.168.0.3,tp_dst=53 actions=mod_nw_ecn:2,output:1
 OFPT_FLOW_MOD (OF1.1): ADD table:255 priority=60000 cookie:0x123456789abcdef hard:10 actions=CONTROLLER:65535
 OFPT_FLOW_MOD (OF1.1): ADD table:255 actions=note:41.42.43.00.00.00,note:00.01.02.03.04.05.06.07.00.00.00.00.00.00,note:00.00.00.00.00.00
-OFPT_FLOW_MOD (OF1.1): ADD table:255 ip actions=mod_nw_ttl:1,load:0xa04034d->NXM_OF_IP_SRC[]
+OFPT_FLOW_MOD (OF1.1): ADD table:255 ip actions=mod_nw_ttl:1,mod_nw_src:10.4.3.77
 OFPT_FLOW_MOD (OF1.1): ADD table:255 sctp actions=drop
 OFPT_FLOW_MOD (OF1.1): ADD table:255 sctp actions=drop
 OFPT_FLOW_MOD (OF1.1): ADD table:255 in_port=0 actions=resubmit:0