[ovs-dev] [PATCH v2] ofp-actions: Add delete field action

Yi-Hung Wei yihung.wei at gmail.com
Tue Apr 14 20:33:28 UTC 2020


This patch adds a new OpenFlow action, delete field, to delete a
field in packets.  Currently, only the tun_metadata fields are
supported.

One use case to add this action is to support multiple versions
of geneve tunnel metadatas to be exchanged among different versions
of networks.  For example, we may introduce tun_metadata2 to
replace old tun_metadata1, but still want to provide backward
compatibility to the older release.  In this case, in the new
OpenFlow pipeline, we would like to support the case to receive a
packet with tun_metadata1, do some processing.  And if the packet
is going to a switch in the newer release, we would like to delete
the value in tun_metadata1 and set a value into tun_metadata2.

Currently, ovs does not provide an action to remove a value in
tun_metadata if the value is present.  This patch fulfills the gap
by adding the delete_field action.  For example, the OpenFlow
syntax to delete tun_metadata1 is:

    actions=delete_field:tun_metadata1

Signed-off-by: Yi-Hung Wei <yihung.wei at gmail.com>
---
Travis CI: https://travis-ci.org/github/YiHungWei/ovs/builds/674955333
---
 NEWS                              |  1 +
 include/openvswitch/ofp-actions.h | 13 +++++-
 lib/nx-match.c                    | 20 ++++++++-
 lib/nx-match.h                    |  4 +-
 lib/ofp-actions.c                 | 90 ++++++++++++++++++++++++++++++++++++++-
 lib/ovs-actions.xml               | 16 +++++++
 lib/tun-metadata.c                | 17 ++++++++
 lib/tun-metadata.h                |  1 +
 ofproto/ofproto-dpif-xlate.c      | 24 ++++++++++-
 tests/ofp-actions.at              |  3 ++
 tests/tunnel.at                   | 37 ++++++++++++++++
 11 files changed, 221 insertions(+), 5 deletions(-)

diff --git a/NEWS b/NEWS
index 70bd17584594..58fbcedcb9bf 100644
--- a/NEWS
+++ b/NEWS
@@ -5,6 +5,7 @@ Post-v2.13.0
      * The OpenFlow ofp_desc/serial_num may now be configured by setting the
        value of other-config:dp-sn in the Bridge table.
      * Added support to watch CONTROLLER port status in fast failover group.
+     * New action "delete_field".
    - DPDK:
      * Deprecated DPDK pdump packet capture support removed.
      * Deprecated DPDK ring ports (dpdkr) are no longer supported.
diff --git a/include/openvswitch/ofp-actions.h b/include/openvswitch/ofp-actions.h
index c8948e0d694f..226e86d0b0a5 100644
--- a/include/openvswitch/ofp-actions.h
+++ b/include/openvswitch/ofp-actions.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2013, 2014, 2015, 2016, 2017, 2019 Nicira, Inc.
+ * Copyright (c) 2012-2017, 2019-2020 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -94,6 +94,7 @@ struct vl_mff_map;
     OFPACT(PUSH_MPLS,       ofpact_push_mpls,   ofpact, "push_mpls")    \
     OFPACT(POP_MPLS,        ofpact_pop_mpls,    ofpact, "pop_mpls")     \
     OFPACT(DEC_NSH_TTL,     ofpact_null,        ofpact, "dec_nsh_ttl")  \
+    OFPACT(DELETE_FIELD,    ofpact_delete_field, ofpact, "delete_field") \
                                                                         \
     /* Generic encap & decap */                                         \
     OFPACT(ENCAP,           ofpact_encap,       props, "encap")         \
@@ -576,6 +577,16 @@ struct ofpact_pop_mpls {
     );
 };
 
+/* OFPACT_DELETE_FIELD.
+ *
+ * Used for NXAST_DELETE_FIELD. */
+struct ofpact_delete_field {
+    OFPACT_PADDED_MEMBERS(
+        struct ofpact ofpact;
+        const struct mf_field *field;
+    );
+};
+
 /* OFPACT_SET_TUNNEL.
  *
  * Used for NXAST_SET_TUNNEL, NXAST_SET_TUNNEL64. */
diff --git a/lib/nx-match.c b/lib/nx-match.c
index 058816c7b88f..3ffd7d9d7711 100644
--- a/lib/nx-match.c
+++ b/lib/nx-match.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017 Nicira, Inc.
+ * Copyright (c) 2010-2017, 2020 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -1994,6 +1994,24 @@ nxm_execute_stack_pop(const struct ofpact_stack *pop,
     }
 }
 
+/* Parses a field from '*s' into '*field'.  If successful, stores the
+ * reference to the field in '*field', and returns NULL.  On failure,
+ * returns a malloc()'ed error message.
+ */
+char * OVS_WARN_UNUSED_RESULT
+mf_parse_field(const struct mf_field **field, const char *s)
+{
+    const struct nxm_field *f;
+    int s_len = strlen(s);
+
+    f = nxm_field_by_name(s, s_len);
+    (*field) = f ? mf_from_id(f->id) : mf_from_name_len(s, s_len);
+    if (!*field) {
+        return xasprintf("unknown field `%s'", s);
+    }
+    return NULL;
+}
+
 /* Formats 'sf' into 's' in a format normally acceptable to
  * mf_parse_subfield().  (It won't be acceptable if sf->field is NULL or if
  * sf->field has no NXM name.) */
diff --git a/lib/nx-match.h b/lib/nx-match.h
index 9be40a98150e..3120ac0a0d5f 100644
--- a/lib/nx-match.h
+++ b/lib/nx-match.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010-2017 Nicira, Inc.
+ * Copyright (c) 2010-2017, 2020 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -42,6 +42,8 @@ struct vl_mff_map;
  * See include/openflow/nicira-ext.h for NXM specification.
  */
 
+char * mf_parse_field(const struct mf_field **field, const char *s)
+    OVS_WARN_UNUSED_RESULT;
 void mf_format_subfield(const struct mf_subfield *, struct ds *);
 char *mf_parse_subfield__(struct mf_subfield *sf, const char **s)
     OVS_WARN_UNUSED_RESULT;
diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c
index a94d1a7ca918..be08a53fd589 100644
--- a/lib/ofp-actions.c
+++ b/lib/ofp-actions.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008-2017, 2019 Nicira, Inc.
+ * Copyright (c) 2008-2017, 2019-2020 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -361,6 +361,9 @@ enum ofp_raw_action_type {
     /* NX1.0+(49): struct nx_action_check_pkt_larger, ... VLMFF */
     NXAST_RAW_CHECK_PKT_LARGER,
 
+    /* NX1.0+(50): struct nx_action_delete_field. VLMFF */
+    NXAST_RAW_DELETE_FIELD,
+
 /* ## ------------------ ## */
 /* ## Debugging actions. ## */
 /* ## ------------------ ## */
@@ -500,6 +503,7 @@ ofpact_next_flattened(const struct ofpact *ofpact)
     case OFPACT_DECAP:
     case OFPACT_DEC_NSH_TTL:
     case OFPACT_CHECK_PKT_LARGER:
+    case OFPACT_DELETE_FIELD:
         return ofpact_next(ofpact);
 
     case OFPACT_CLONE:
@@ -4140,6 +4144,87 @@ check_SET_TUNNEL(const struct ofpact_tunnel *a OVS_UNUSED,
     return 0;
 }
 
+/* Delete field action. */
+
+/* Action structure for DELETE_FIELD */
+struct nx_action_delete_field {
+    ovs_be16 type;          /* OFPAT_VENDOR */
+    ovs_be16 len;           /* Length is 24. */
+    ovs_be32 vendor;        /* NX_VENDOR_ID. */
+    ovs_be16 subtype;       /* NXAST_DELETE_FIELD. */
+    /* Followed by:
+     * - OXM/NXM header for field to delete (4 or 8 bytes).
+     * - Enough 0-bytes to pad out the action to 24 bytes. */
+    uint8_t pad[14];
+};
+OFP_ASSERT(sizeof(struct nx_action_delete_field ) == 24);
+
+static enum ofperr
+decode_NXAST_RAW_DELETE_FIELD(const struct nx_action_delete_field *nadf,
+                              enum ofp_version ofp_version OVS_UNUSED,
+                              const struct vl_mff_map *vl_mff_map,
+                              uint64_t *tlv_bitmap, struct ofpbuf *out)
+{
+    struct ofpact_delete_field *delete_field;
+    enum ofperr err;
+
+    delete_field = ofpact_put_DELETE_FIELD(out);
+    delete_field->ofpact.raw = NXAST_RAW_DELETE_FIELD;
+
+    struct ofpbuf b = ofpbuf_const_initializer(nadf, ntohs(nadf->len));
+    ofpbuf_pull(&b, OBJECT_OFFSETOF(nadf, pad));
+
+    err = mf_vl_mff_nx_pull_header(&b, vl_mff_map, &delete_field->field,
+                                   NULL, tlv_bitmap);
+    if (err) {
+        return err;
+    }
+
+    return 0;
+}
+
+static void
+encode_DELETE_FIELD(const struct ofpact_delete_field *delete_field,
+                    enum ofp_version ofp_version OVS_UNUSED,
+                    struct ofpbuf *out)
+{
+    struct nx_action_delete_field *nadf = put_NXAST_DELETE_FIELD(out);
+    size_t size = out->size;
+
+    out->size = size - sizeof nadf->pad;
+    nx_put_mff_header(out, delete_field->field, 0, false);
+    out->size = size;
+}
+
+static char * OVS_WARN_UNUSED_RESULT
+parse_DELETE_FIELD(char *arg, const struct ofpact_parse_params *pp)
+{
+    struct ofpact_delete_field *delete_field;
+
+    delete_field = ofpact_put_DELETE_FIELD(pp->ofpacts);
+    return mf_parse_field(&delete_field->field, arg);
+}
+
+static void
+format_DELETE_FIELD(const struct ofpact_delete_field *odf,
+                          const struct ofpact_format_params *fp)
+{
+    ds_put_format(fp->s, "%sdelete_field:%s", colors.param,
+                  colors.end);
+    ds_put_format(fp->s, "%s", odf->field->name);
+}
+
+static enum ofperr
+check_DELETE_FIELD(const struct ofpact_delete_field *odf,
+                         struct ofpact_check_params *cp OVS_UNUSED)
+{
+    if (odf->field->id < MFF_TUN_METADATA0 ||
+        odf->field->id > MFF_TUN_METADATA63) {
+        return OFPERR_OFPBAC_BAD_ARGUMENT;
+    }
+    return 0;
+}
+
 /* Set queue action. */
 
 static enum ofperr
@@ -7870,6 +7955,7 @@ action_set_classify(const struct ofpact *a)
     case OFPACT_DEBUG_RECIRC:
     case OFPACT_DEBUG_SLOW:
     case OFPACT_CHECK_PKT_LARGER:
+    case OFPACT_DELETE_FIELD:
         return ACTION_SLOT_INVALID;
 
     default:
@@ -8073,6 +8159,7 @@ ovs_instruction_type_from_ofpact_type(enum ofpact_type type,
     case OFPACT_DECAP:
     case OFPACT_DEC_NSH_TTL:
     case OFPACT_CHECK_PKT_LARGER:
+    case OFPACT_DELETE_FIELD:
     default:
         return OVSINST_OFPIT11_APPLY_ACTIONS;
     }
@@ -8984,6 +9071,7 @@ ofpact_outputs_to_port(const struct ofpact *ofpact, ofp_port_t port)
     case OFPACT_DECAP:
     case OFPACT_DEC_NSH_TTL:
     case OFPACT_CHECK_PKT_LARGER:
+    case OFPACT_DELETE_FIELD:
     default:
         return false;
     }
diff --git a/lib/ovs-actions.xml b/lib/ovs-actions.xml
index ab8e08b84d8b..060a079b4852 100644
--- a/lib/ovs-actions.xml
+++ b/lib/ovs-actions.xml
@@ -1552,6 +1552,22 @@ for <var>i</var> in [1,<var>n_slaves</var>]:
         This action was added in Open vSwitch 2.11.90.
       </p>
     </action>
+
+    <action name="DELETE_FIELD">
+      <h2>The <code>delete_field</code> action</h2>
+      <syntax><code>delete_field:</code><var>field</var></syntax>
+
+      <p>
+        The <code>delete_field</code> action deletes a field in the syntax
+        described under ``Field Specifications'' above.  Currently, only
+        the tun_metadta fields are supported.
+      </p>
+
+      <p>
+        This action was added in Open vSwitch 2.13.90.
+      </p>
+    </action>
+
   </group>
 
   <group title="Metadata Actions">
diff --git a/lib/tun-metadata.c b/lib/tun-metadata.c
index f8a0e19524e9..c0b0ae044897 100644
--- a/lib/tun-metadata.c
+++ b/lib/tun-metadata.c
@@ -261,6 +261,23 @@ tun_metadata_write(struct flow_tnl *tnl,
                        value->tun_metadata + mf->n_bytes - loc->len, loc, idx);
 }
 
+/* Deletes field 'mf' in 'tnl' (in non-UDPIF format).
+ * 'mf' must be an MFF_TUN_METADATA* field.
+ */
+void
+tun_metadata_delete(struct flow_tnl *tnl, const struct mf_field *mf)
+{
+    unsigned int idx;
+
+    if (tnl->flags & FLOW_TNL_F_UDPIF) {
+        return;
+    }
+
+    idx = mf->id - MFF_TUN_METADATA0;
+    ovs_assert(idx < TUN_METADATA_NUM_OPTS);
+    ULLONG_SET0(tnl->metadata.present.map, idx);
+}
+
 static const struct tun_metadata_loc *
 metadata_loc_from_match(const struct tun_table *map, struct match *match,
                         const char *name, unsigned int idx,
diff --git a/lib/tun-metadata.h b/lib/tun-metadata.h
index 7dad9504b8da..67dedae2522b 100644
--- a/lib/tun-metadata.h
+++ b/lib/tun-metadata.h
@@ -47,6 +47,7 @@ void tun_metadata_read(const struct flow_tnl *,
                        const struct mf_field *, union mf_value *);
 void tun_metadata_write(struct flow_tnl *,
                         const struct mf_field *, const union mf_value *);
+void tun_metadata_delete(struct flow_tnl *, const struct mf_field *);
 void tun_metadata_set_match(const struct mf_field *,
                             const union mf_value *value,
                             const union mf_value *mask, struct match *,
diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index abce976c6c2f..80fba84cb1a5 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2019 Nicira, Inc.
+/* Copyright (c) 2009-2017, 2019-2020 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -5159,6 +5159,21 @@ compose_dec_mpls_ttl_action(struct xlate_ctx *ctx)
     return true;
 }
 
+static void
+xlate_delete_field(struct xlate_ctx *ctx,
+                   struct flow *flow,
+                   const struct ofpact_delete_field *odf)
+{
+    struct ds s = DS_EMPTY_INITIALIZER;
+
+    /* Currently, only tun_metadata is allowed for delete_field action. */
+    tun_metadata_delete(&flow->tunnel, odf->field);
+
+    ds_put_format(&s, "delete %s", odf->field->name);
+    xlate_report(ctx, OFT_DETAIL, "%s", ds_cstr(&s));
+    ds_destroy(&s);
+}
+
 /* Emits an action that outputs to 'port', within 'ctx'.
  *
  * 'controller_len' affects only packets sent to an OpenFlow controller.  It
@@ -5684,6 +5699,7 @@ reversible_actions(const struct ofpact *ofpacts, size_t ofpacts_len)
         case OFPACT_WRITE_ACTIONS:
         case OFPACT_WRITE_METADATA:
         case OFPACT_CHECK_PKT_LARGER:
+        case OFPACT_DELETE_FIELD:
             break;
 
         case OFPACT_CT:
@@ -5993,6 +6009,7 @@ freeze_unroll_actions(const struct ofpact *a, const struct ofpact *end,
         case OFPACT_CT_CLEAR:
         case OFPACT_NAT:
         case OFPACT_CHECK_PKT_LARGER:
+        case OFPACT_DELETE_FIELD:
             /* These may not generate PACKET INs. */
             break;
 
@@ -6653,6 +6670,7 @@ recirc_for_mpls(const struct ofpact *a, struct xlate_ctx *ctx)
     case OFPACT_WRITE_METADATA:
     case OFPACT_GOTO_TABLE:
     case OFPACT_CHECK_PKT_LARGER:
+    case OFPACT_DELETE_FIELD:
     default:
         break;
     }
@@ -7030,6 +7048,10 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
             xlate_fin_timeout(ctx, ofpact_get_FIN_TIMEOUT(a));
             break;
 
+        case OFPACT_DELETE_FIELD:
+            xlate_delete_field(ctx, flow, ofpact_get_DELETE_FIELD(a));
+            break;
+
         case OFPACT_CLEAR_ACTIONS:
             xlate_report_action_set(ctx, "was");
             ofpbuf_clear(&ctx->action_set);
diff --git a/tests/ofp-actions.at b/tests/ofp-actions.at
index 4893280a998f..28b2099a0c71 100644
--- a/tests/ofp-actions.at
+++ b/tests/ofp-actions.at
@@ -316,6 +316,9 @@ ffff 0018 00002320 0031 05dc 000000010004000000000000
 # actions=check_pkt_larger(1000)->NXM_NX_XXREG1[4]
 ffff 0018 00002320 0031 03e8 00040001e010000000000000
 
+# actions=delete_field:tun_metadata10
+ffff 0018 00002320 0032 00 01 64 7c 00 00 00 00 000000000000
+
 ])
 sed '/^[[#&]]/d' < test-data > input.txt
 sed -n 's/^# //p; /^$/p' < test-data > expout
diff --git a/tests/tunnel.at b/tests/tunnel.at
index d65bf4412aa9..b3764aed8e9e 100644
--- a/tests/tunnel.at
+++ b/tests/tunnel.at
@@ -890,6 +890,43 @@ Datapath actions: set(tunnel(dst=1.1.1.1,ttl=64,tp_dst=6081,geneve({class=0xffff
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
+AT_SETUP([tunnel - Delete Geneve option])
+OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=geneve \
+                    options:remote_ip=1.1.1.1 ofport_request=1 \
+                    -- add-port br0 p2 -- set Interface p2 type=dummy \
+                    ofport_request=2 ofport_request=2])
+OVS_VSWITCHD_DISABLE_TUNNEL_PUSH_POP
+
+AT_CHECK([ovs-ofctl add-tlv-map br0 "{class=0xffff,type=0,len=4}->tun_metadata0,{class=0xffff,type=1,len=4}->tun_metadata1,{class=0xffff,type=2,len=4}->tun_metadata3"])
+
+AT_DATA([flows.txt], [dnl
+table=0,tun_metadata0=0x11112222,actions=set_field:0x55556666->tun_metadata1,resubmit(,1)
+table=0,tun_metadata0=0x33334444,actions=delete_field:tun_metadata0,set_field:0x77778888->tun_metadata1,resubmit(,1)
+table=0,tun_metadata0=0x88889999,actions=delete_field:tun_metadata3,resubmit(,1)
+table=1,actions=IN_PORT
+])
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'recirc_id(0),tunnel(tun_id=0x0,src=1.1.1.1,dst=1.1.1.2,ttl=64,geneve({class=0xffff,type=0,len=4,0x11112222}),flags(df|key)),in_port(6081),skb_mark(0),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(frag=no)'], [0], [stdout])
+AT_CHECK([tail -2 stdout], [0],
+  [Megaflow: recirc_id=0,eth,ip,tun_id=0,tun_src=1.1.1.1,tun_dst=1.1.1.2,tun_tos=0,tun_flags=+df-csum+key,tun_metadata0=0x11112222,tun_metadata1=NP,in_port=1,nw_ecn=0,nw_frag=no
+Datapath actions: set(tunnel(dst=1.1.1.1,ttl=64,tp_dst=6081,geneve({class=0xffff,type=0,len=4,0x11112222}{class=0xffff,type=0x1,len=4,0x55556666}),flags(df))),6081
+])
+
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'recirc_id(0),tunnel(tun_id=0x0,src=1.1.1.1,dst=1.1.1.2,ttl=64,geneve({class=0xffff,type=0,len=4,0x33334444}),flags(df|key)),in_port(6081),skb_mark(0),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(frag=no)'], [0], [stdout])
+AT_CHECK([tail -2 stdout], [0],
+  [Megaflow: recirc_id=0,eth,ip,tun_id=0,tun_src=1.1.1.1,tun_dst=1.1.1.2,tun_tos=0,tun_flags=+df-csum+key,tun_metadata0=0x33334444,tun_metadata1=NP,in_port=1,nw_ecn=0,nw_frag=no
+Datapath actions: set(tunnel(dst=1.1.1.1,ttl=64,tp_dst=6081,geneve({class=0xffff,type=0x1,len=4,0x77778888}),flags(df))),6081
+])
+
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'recirc_id(0),tunnel(tun_id=0x0,src=1.1.1.1,dst=1.1.1.2,ttl=64,geneve({class=0xffff,type=0,len=4,0x88889999}),flags(df|key)),in_port(6081),skb_mark(0),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(frag=no)'], [0], [stdout])
+AT_CHECK([tail -2 stdout], [0],
+  [Megaflow: recirc_id=0,eth,ip,tun_id=0,tun_src=1.1.1.1,tun_dst=1.1.1.2,tun_tos=0,tun_flags=+df-csum+key,tun_metadata0=0x88889999,in_port=1,nw_ecn=0,nw_frag=no
+Datapath actions: set(tunnel(dst=1.1.1.1,ttl=64,tp_dst=6081,geneve({class=0xffff,type=0,len=4,0x88889999}),flags(df))),6081
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
 AT_SETUP([tunnel - concomitant IPv6 and IPv4 tunnels])
 OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=vxlan \
                     options:remote_ip=1.1.1.1 ofport_request=1 \
-- 
2.7.4



More information about the dev mailing list