nicira-ext: Add Nicira actions NXAST_STACK_PUSH and NXAST_STACK_POP.
authorAndy Zhou <azhou@nicira.com>
Wed, 6 Mar 2013 00:27:55 +0000 (16:27 -0800)
committerBen Pfaff <blp@nicira.com>
Wed, 6 Mar 2013 21:40:28 +0000 (13:40 -0800)
The Push action takes a single parameter. Any source allowed by NXAST_REG_MOVE
is allowed to be pushed onto the stack. When the source is a bit field,
its value will be right shifted to bit zero before being pushed onto the
stack. The remaining bits will be set to zero.

The Pop action also takes a single parameter. Any destination allowed by
NXAST_REG_MOVE can be used as the destination of the action. The value, in
case of a bit field, will be taken from top of the stack, starting from
bit zero.

The stack size is not limited. The initial 8KB is statically allocated to
efficiently handle most common use cases. When more stack space is
required, the stack can grow using malloc().

Signed-off-by: Andy Zhou <azhou@nicira.com>
Signed-off-by: Ben Pfaff <blp@nicira.com>
12 files changed:
NEWS
include/openflow/nicira-ext.h
lib/nx-match.c
lib/nx-match.h
lib/ofp-actions.c
lib/ofp-actions.h
lib/ofp-parse.c
lib/ofp-util.def
ofproto/ofproto-dpif.c
tests/ofproto-dpif.at
tests/ovs-ofctl.at
utilities/ovs-ofctl.8.in

diff --git a/NEWS b/NEWS
index 7fb0932..2353278 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -9,6 +9,8 @@ post-v1.10.0
     - OpenFlow:
       * The "dec_mpls_ttl" and "set_mpls_ttl" actions from OpenFlow
         1.1 and later are now implemented.
+      * New "stack" extension for use in actions, to push and pop from
+        NXM fields.
 
 
 v1.10.0 - xx xxx xxxx
index cdb1952..f9b0af4 100644 (file)
@@ -308,6 +308,8 @@ enum nx_action_subtype {
     NXAST_POP_MPLS,             /* struct nx_action_pop_mpls */
     NXAST_SET_MPLS_TTL,         /* struct nx_action_ttl */
     NXAST_DEC_MPLS_TTL,         /* struct nx_action_header */
+    NXAST_STACK_PUSH,           /* struct nx_action_stack */
+    NXAST_STACK_POP,            /* struct nx_action_stack */
 };
 
 /* Header for Nicira-defined actions. */
@@ -562,6 +564,23 @@ struct nx_action_reg_load {
 };
 OFP_ASSERT(sizeof(struct nx_action_reg_load) == 24);
 
+/* Action structure for NXAST_STACK_PUSH and NXAST_STACK_POP.
+ *
+ * Pushes (or pops) field[offset: offset + n_bits] to (or from)
+ * top of the stack.
+ */
+struct nx_action_stack {
+    ovs_be16 type;                  /* OFPAT_VENDOR. */
+    ovs_be16 len;                   /* Length is 16. */
+    ovs_be32 vendor;                /* NX_VENDOR_ID. */
+    ovs_be16 subtype;               /* NXAST_REG_PUSH or NXAST_REG_POP. */
+    ovs_be16 offset;                /* Bit offset into the field. */
+    ovs_be32 field;                 /* The field used for push or pop. */
+    ovs_be16 n_bits;                /* (n_bits + 1) bits of the field. */
+    uint8_t zero[6];                /* Reserved, must be zero. */
+};
+OFP_ASSERT(sizeof(struct nx_action_stack) == 24);
+
 /* Action structure for NXAST_NOTE.
  *
  * This action has no effect.  It is variable length.  The switch does not
index e5545de..6efc94d 100644 (file)
@@ -1312,3 +1312,149 @@ nxm_reg_load(const struct mf_subfield *dst, uint64_t src_data,
                  sizeof src_data_be * 8);
     mf_write_subfield_flow(dst, &src_subvalue, flow);
 }
+\f
+/* nxm_parse_stack_action, works for both push() and pop(). */
+void
+nxm_parse_stack_action(struct ofpact_stack *stack_action, const char *s)
+{
+    s = mf_parse_subfield(&stack_action->subfield, s);
+    if (*s != '\0') {
+        ovs_fatal(0, "%s: trailing garbage following push or pop", s);
+    }
+}
+
+void
+nxm_format_stack_push(const struct ofpact_stack *push, struct ds *s)
+{
+    ds_put_cstr(s, "push:");
+    mf_format_subfield(&push->subfield, s);
+}
+
+void
+nxm_format_stack_pop(const struct ofpact_stack *pop, struct ds *s)
+{
+    ds_put_cstr(s, "pop:");
+    mf_format_subfield(&pop->subfield, s);
+}
+
+/* Common set for both push and pop actions. */
+static void
+stack_action_from_openflow__(const struct nx_action_stack *nasp,
+                                    struct ofpact_stack *stack_action)
+{
+    stack_action->subfield.field = mf_from_nxm_header(ntohl(nasp->field));
+    stack_action->subfield.ofs = ntohs(nasp->offset);
+    stack_action->subfield.n_bits = ntohs(nasp->n_bits);
+}
+
+static void
+nxm_stack_to_nxast__(const struct ofpact_stack *stack_action,
+                            struct nx_action_stack *nasp)
+{
+    nasp->offset = htons(stack_action->subfield.ofs);
+    nasp->n_bits = htons(stack_action->subfield.n_bits);
+    nasp->field = htonl(stack_action->subfield.field->nxm_header);
+}
+
+enum ofperr
+nxm_stack_push_from_openflow(const struct nx_action_stack *nasp,
+                             struct ofpbuf *ofpacts)
+{
+    struct ofpact_stack *push;
+
+    push = ofpact_put_STACK_PUSH(ofpacts);
+    stack_action_from_openflow__(nasp, push);
+
+    return nxm_stack_push_check(push, NULL);
+}
+
+enum ofperr
+nxm_stack_pop_from_openflow(const struct nx_action_stack *nasp,
+                             struct ofpbuf *ofpacts)
+{
+    struct ofpact_stack *pop;
+
+    pop = ofpact_put_STACK_POP(ofpacts);
+    stack_action_from_openflow__(nasp, pop);
+
+    return nxm_stack_pop_check(pop, NULL);
+}
+
+enum ofperr
+nxm_stack_push_check(const struct ofpact_stack *push,
+                     const struct flow *flow)
+{
+    return mf_check_src(&push->subfield, flow);
+}
+
+enum ofperr
+nxm_stack_pop_check(const struct ofpact_stack *pop,
+                    const struct flow *flow)
+{
+    return mf_check_dst(&pop->subfield, flow);
+}
+
+void
+nxm_stack_push_to_nxast(const struct ofpact_stack *stack,
+                        struct ofpbuf *openflow)
+{
+    nxm_stack_to_nxast__(stack, ofputil_put_NXAST_STACK_PUSH(openflow));
+}
+
+void
+nxm_stack_pop_to_nxast(const struct ofpact_stack *stack,
+                       struct ofpbuf *openflow)
+{
+    nxm_stack_to_nxast__(stack, ofputil_put_NXAST_STACK_POP(openflow));
+}
+
+/* nxm_execute_stack_push(), nxm_execute_stack_pop(). */
+static void
+nx_stack_push(struct ofpbuf *stack, union mf_subvalue *v)
+{
+    ofpbuf_put(stack, v, sizeof *v);
+}
+
+static union mf_subvalue *
+nx_stack_pop(struct ofpbuf *stack)
+{
+    union mf_subvalue *v = NULL;
+
+    if (stack->size) {
+        stack->size -= sizeof *v;
+        v = (union mf_subvalue *) ofpbuf_tail(stack);
+    }
+
+    return v;
+}
+
+void
+nxm_execute_stack_push(const struct ofpact_stack *push,
+                       const struct flow *flow, struct ofpbuf *stack)
+{
+    union mf_subvalue dst_value;
+
+    mf_read_subfield(&push->subfield, flow, &dst_value);
+    nx_stack_push(stack, &dst_value);
+}
+
+void
+nxm_execute_stack_pop(const struct ofpact_stack *pop,
+                      struct flow *flow, struct ofpbuf *stack)
+{
+    union mf_subvalue *src_value;
+
+    src_value = nx_stack_pop(stack);
+
+    /* Only pop if stack is not empty. Otherwise, give warning. */
+    if (src_value) {
+        mf_write_subfield_flow(&pop->subfield, src_value, flow);
+    } else {
+        if (!VLOG_DROP_WARN(&rl)) {
+            char *flow_str = flow_to_string(flow);
+            VLOG_WARN_RL(&rl, "Failed to pop from an empty stack. On flow \n"
+                           " %s", flow_str);
+            free(flow_str);
+        }
+    }
+}
index 6a57297..7d316d8 100644 (file)
@@ -29,10 +29,12 @@ struct match;
 struct mf_subfield;
 struct ofpact_reg_move;
 struct ofpact_reg_load;
+struct ofpact_stack;
 struct ofpbuf;
 struct nx_action_reg_load;
 struct nx_action_reg_move;
 
+
 /* Nicira Extended Match (NXM) flexible flow match helper functions.
  *
  * See include/openflow/nicira-ext.h for NXM specification.
@@ -83,6 +85,30 @@ void nxm_execute_reg_load(const struct ofpact_reg_load *, struct flow *);
 void nxm_reg_load(const struct mf_subfield *, uint64_t src_data,
                   struct flow *);
 
+void nxm_parse_stack_action(struct ofpact_stack *, const char *);
+
+void nxm_format_stack_push(const struct ofpact_stack *, struct ds *);
+void nxm_format_stack_pop(const struct ofpact_stack *, struct ds *);
+
+enum ofperr nxm_stack_push_from_openflow(const struct nx_action_stack *,
+                                       struct ofpbuf *ofpacts);
+enum ofperr nxm_stack_pop_from_openflow(const struct nx_action_stack *,
+                                       struct ofpbuf *ofpacts);
+enum ofperr nxm_stack_push_check(const struct ofpact_stack *,
+                                 const  struct flow *);
+enum ofperr nxm_stack_pop_check(const struct ofpact_stack *,
+                               const struct flow *);
+
+void nxm_stack_push_to_nxast(const struct ofpact_stack *,
+                           struct ofpbuf *openflow);
+void nxm_stack_pop_to_nxast(const struct ofpact_stack *,
+                           struct ofpbuf *openflow);
+
+void nxm_execute_stack_push(const struct ofpact_stack *,
+                            const struct flow *, struct ofpbuf *);
+void nxm_execute_stack_pop(const struct ofpact_stack *,
+                            struct flow *, struct ofpbuf *);
+
 int nxm_field_bytes(uint32_t header);
 int nxm_field_bits(uint32_t header);
 
index 6dabc5a..d405d2d 100644 (file)
@@ -339,6 +339,16 @@ ofpact_from_nxast(const union ofp_action *a, enum ofputil_action_code code,
             (const struct nx_action_reg_load *) a, out);
         break;
 
+    case OFPUTIL_NXAST_STACK_PUSH:
+        error = nxm_stack_push_from_openflow(
+            (const struct nx_action_stack *) a, out);
+        break;
+
+    case OFPUTIL_NXAST_STACK_POP:
+        error = nxm_stack_pop_from_openflow(
+            (const struct nx_action_stack *) a, out);
+        break;
+
     case OFPUTIL_NXAST_NOTE:
         nan = (const struct nx_action_note *) a;
         note_from_openflow(nan, out);
@@ -1155,6 +1165,12 @@ ofpact_check__(const struct ofpact *a, const struct flow *flow, int max_ports,
             return nxm_reg_load_check(ofpact_get_REG_LOAD(a), flow);
         }
 
+    case OFPACT_STACK_PUSH:
+        return nxm_stack_push_check(ofpact_get_STACK_PUSH(a), flow);
+
+    case OFPACT_STACK_POP:
+        return nxm_stack_pop_check(ofpact_get_STACK_POP(a), flow);
+
     case OFPACT_DEC_TTL:
     case OFPACT_SET_MPLS_TTL:
     case OFPACT_DEC_MPLS_TTL:
@@ -1401,6 +1417,14 @@ ofpact_to_nxast(const struct ofpact *a, struct ofpbuf *out)
         nxm_reg_load_to_nxast(ofpact_get_REG_LOAD(a), out);
         break;
 
+    case OFPACT_STACK_PUSH:
+        nxm_stack_push_to_nxast(ofpact_get_STACK_PUSH(a), out);
+        break;
+
+    case OFPACT_STACK_POP:
+        nxm_stack_pop_to_nxast(ofpact_get_STACK_POP(a), out);
+        break;
+
     case OFPACT_DEC_TTL:
         ofpact_dec_ttl_to_nxast(ofpact_get_DEC_TTL(a), out);
         break;
@@ -1580,6 +1604,8 @@ ofpact_to_openflow10(const struct ofpact *a, struct ofpbuf *out)
     case OFPACT_BUNDLE:
     case OFPACT_REG_MOVE:
     case OFPACT_REG_LOAD:
+    case OFPACT_STACK_PUSH:
+    case OFPACT_STACK_POP:
     case OFPACT_DEC_TTL:
     case OFPACT_SET_MPLS_TTL:
     case OFPACT_DEC_MPLS_TTL:
@@ -1748,6 +1774,8 @@ ofpact_to_openflow11(const struct ofpact *a, struct ofpbuf *out)
     case OFPACT_BUNDLE:
     case OFPACT_REG_MOVE:
     case OFPACT_REG_LOAD:
+    case OFPACT_STACK_PUSH:
+    case OFPACT_STACK_POP:
     case OFPACT_SET_TUNNEL:
     case OFPACT_POP_QUEUE:
     case OFPACT_FIN_TIMEOUT:
@@ -1867,6 +1895,8 @@ ofpact_outputs_to_port(const struct ofpact *ofpact, uint16_t port)
     case OFPACT_SET_L4_DST_PORT:
     case OFPACT_REG_MOVE:
     case OFPACT_REG_LOAD:
+    case OFPACT_STACK_PUSH:
+    case OFPACT_STACK_POP:
     case OFPACT_DEC_TTL:
     case OFPACT_SET_MPLS_TTL:
     case OFPACT_DEC_MPLS_TTL:
@@ -2088,6 +2118,14 @@ ofpact_format(const struct ofpact *a, struct ds *s)
         nxm_format_reg_load(ofpact_get_REG_LOAD(a), s);
         break;
 
+    case OFPACT_STACK_PUSH:
+        nxm_format_stack_push(ofpact_get_STACK_PUSH(a), s);
+        break;
+
+    case OFPACT_STACK_POP:
+        nxm_format_stack_pop(ofpact_get_STACK_POP(a), s);
+        break;
+
     case OFPACT_DEC_TTL:
         print_dec_ttl(ofpact_get_DEC_TTL(a), s);
         break;
index 2d934f9..0189c8a 100644 (file)
@@ -70,6 +70,8 @@
     DEFINE_OFPACT(SET_L4_DST_PORT, ofpact_l4_port,       ofpact)    \
     DEFINE_OFPACT(REG_MOVE,        ofpact_reg_move,      ofpact)    \
     DEFINE_OFPACT(REG_LOAD,        ofpact_reg_load,      ofpact)    \
+    DEFINE_OFPACT(STACK_PUSH,      ofpact_stack,         ofpact)    \
+    DEFINE_OFPACT(STACK_POP,       ofpact_stack,         ofpact)    \
     DEFINE_OFPACT(DEC_TTL,         ofpact_cnt_ids,       cnt_ids)   \
     DEFINE_OFPACT(SET_MPLS_TTL,    ofpact_mpls_ttl,      ofpact)    \
     DEFINE_OFPACT(DEC_MPLS_TTL,    ofpact_null,          ofpact)    \
@@ -304,6 +306,14 @@ struct ofpact_reg_move {
     struct mf_subfield dst;
 };
 
+/* OFPACT_STACK_PUSH.
+ *
+ * Used for NXAST_STACK_PUSH and NXAST_STACK_POP. */
+struct ofpact_stack {
+    struct ofpact ofpact;
+    struct mf_subfield subfield;
+};
+
 /* OFPACT_REG_LOAD.
  *
  * Used for NXAST_REG_LOAD, OFPAT12_SET_FIELD. */
index f180204..e8abc9f 100644 (file)
@@ -591,6 +591,12 @@ parse_named_action(enum ofputil_action_code code, const struct flow *flow,
         ofpact_put_POP_MPLS(ofpacts)->ethertype =
             htons(str_to_u16(arg, "pop_mpls"));
         break;
+    case OFPUTIL_NXAST_STACK_PUSH:
+        nxm_parse_stack_action(ofpact_put_STACK_PUSH(ofpacts), arg);
+        break;
+    case OFPUTIL_NXAST_STACK_POP:
+        nxm_parse_stack_action(ofpact_put_STACK_POP(ofpacts), arg);
+        break;
     }
 }
 
index 439d34e..b7dde48 100644 (file)
@@ -50,6 +50,8 @@ NXAST_ACTION(NXAST_SET_QUEUE,       nx_action_set_queue,    0, "set_queue")
 NXAST_ACTION(NXAST_POP_QUEUE,       nx_action_pop_queue,    0, "pop_queue")
 NXAST_ACTION(NXAST_REG_MOVE,        nx_action_reg_move,     0, "move")
 NXAST_ACTION(NXAST_REG_LOAD,        nx_action_reg_load,     0, "load")
+NXAST_ACTION(NXAST_STACK_PUSH,      nx_action_stack,        0, "push")
+NXAST_ACTION(NXAST_STACK_POP,       nx_action_stack,        0, "pop")
 NXAST_ACTION(NXAST_NOTE,            nx_action_note,         1, "note")
 NXAST_ACTION(NXAST_SET_TUNNEL64,    nx_action_set_tunnel64, 0, "set_tunnel64")
 NXAST_ACTION(NXAST_MULTIPATH,       nx_action_multipath,    0, "multipath")
index f37d840..10e4e23 100644 (file)
@@ -216,6 +216,11 @@ struct action_xlate_ctx {
      * this flow when actions change header fields. */
     struct flow flow;
 
+    /* stack for the push and pop actions.
+     * Each stack element is of the type "union mf_subvalue". */
+    struct ofpbuf stack;
+    union mf_subvalue init_stack[1024 / sizeof(union mf_subvalue)];
+
     /* The packet corresponding to 'flow', or a null pointer if we are
      * revalidating without a packet to refer to. */
     const struct ofpbuf *packet;
@@ -6404,6 +6409,16 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
             nxm_execute_reg_load(ofpact_get_REG_LOAD(a), &ctx->flow);
             break;
 
+        case OFPACT_STACK_PUSH:
+            nxm_execute_stack_push(ofpact_get_STACK_PUSH(a), &ctx->flow,
+                                   &ctx->stack);
+            break;
+
+        case OFPACT_STACK_POP:
+            nxm_execute_stack_pop(ofpact_get_STACK_POP(a), &ctx->flow,
+                                  &ctx->stack);
+            break;
+
         case OFPACT_PUSH_MPLS:
             execute_mpls_push_action(ctx, ofpact_get_PUSH_MPLS(a)->ethertype);
             break;
@@ -6573,6 +6588,8 @@ xlate_actions(struct action_xlate_ctx *ctx,
     ctx->table_id = 0;
     ctx->exit = false;
 
+    ofpbuf_use_stub(&ctx->stack, ctx->init_stack, sizeof ctx->init_stack);
+
     if (ctx->ofproto->has_mirrors || hit_resubmit_limit) {
         /* Do this conditionally because the copy is expensive enough that it
          * shows up in profiles. */
@@ -6657,6 +6674,8 @@ xlate_actions(struct action_xlate_ctx *ctx,
         }
         fix_sflow_action(ctx);
     }
+
+    ofpbuf_uninit(&ctx->stack);
 }
 
 /* Translates the 'ofpacts_len' bytes of "struct ofpact"s starting at 'ofpacts'
index b795f56..7915792 100644 (file)
@@ -45,6 +45,7 @@ in_port=12 actions=load:0x10->NXM_NX_REG0[[]],load:0x11->NXM_NX_REG1[[]],load:0x
 in_port=13 actions=load:0x13->NXM_NX_REG3[[]],load:0x14->NXM_NX_REG4[[]],load:0x15->NXM_NX_REG5[[]]
 in_port=14 actions=load:0x16->NXM_NX_REG6[[]],load:0x17->NXM_NX_REG7[[]]
 in_port=15,reg0=0x10,reg1=0x11,reg2=0x12,reg3=0x13,reg4=0x14,reg5=0x15,reg6=0x16,reg7=0x17 actions=output:33
+
 ])
 AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
 AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(90),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout])
@@ -54,6 +55,25 @@ AT_CHECK([tail -1 stdout], [0],
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
+AT_SETUP([ofproto-dpif - push-pop])
+OVS_VSWITCHD_START
+ADD_OF_PORTS([br0], [20], [21], [22], [33], [90])
+AT_DATA([flows.txt], [dnl
+in_port=90           actions=load:20->NXM_NX_REG0[[0..7]],load:21->NXM_NX_REG1[[0..7]],load:22->NXM_NX_REG2[[0..7]], load:33->NXM_NX_REG3[[0..7]], push:NXM_NX_REG0[[]], push:NXM_NX_REG1[[0..7]],push:NXM_NX_REG2[[0..15]], push:NXM_NX_REG3[[]], resubmit:2, resubmit:3, resubmit:4, resubmit:5
+in_port=2            actions=pop:NXM_NX_REG0[[0..7]],output:NXM_NX_REG0[[]]
+in_port=3            actions=pop:NXM_NX_REG1[[0..7]],output:NXM_NX_REG1[[]]
+in_port=4            actions=pop:NXM_NX_REG2[[0..15]],output:NXM_NX_REG2[[]]
+in_port=5            actions=pop:NXM_NX_REG3[[]],output:NXM_NX_REG3[[]]
+
+])
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(90),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+  [Datapath actions: 33,22,21,20
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
 AT_SETUP([ofproto-dpif - output])
 OVS_VSWITCHD_START
 ADD_OF_PORTS([br0], [1], [9], [10], [11], [55], [66], [77], [88])
index 7395fd1..075f2e4 100644 (file)
@@ -243,6 +243,7 @@ tun_id=0x1234,cookie=0x5678,actions=flood
 actions=drop
 reg0=123,actions=move:NXM_NX_REG0[0..5]->NXM_NX_REG1[26..31],load:55->NXM_NX_REG2[0..31],move:NXM_NX_REG0[0..31]->NXM_NX_TUN_ID[0..31],move:NXM_NX_REG0[0..15]->NXM_OF_VLAN_TCI[]
 actions=move:OXM_OF_ETH_DST[]->OXM_OF_ETH_SRC[]
+actions=push:NXM_NX_REG0[0..31],pop:NXM_NX_REG0[]
 vlan_tci=0x1123/0x1fff,actions=drop
 ]])
 AT_CHECK([ovs-ofctl -F nxm -mmm parse-flows flows.txt], [0], [stdout], [stderr])
@@ -271,6 +272,7 @@ NXT_FLOW_MOD: ADD NXM_NX_TUN_ID(0000000000001234) cookie:0x5678 actions=FLOOD
 NXT_FLOW_MOD: ADD <any> actions=drop
 NXT_FLOW_MOD: ADD NXM_NX_REG0(0000007b) actions=move:NXM_NX_REG0[0..5]->NXM_NX_REG1[26..31],load:0x37->NXM_NX_REG2[],move:NXM_NX_REG0[]->NXM_NX_TUN_ID[0..31],move:NXM_NX_REG0[0..15]->NXM_OF_VLAN_TCI[]
 NXT_FLOW_MOD: ADD <any> actions=move:NXM_OF_ETH_DST[]->NXM_OF_ETH_SRC[]
+NXT_FLOW_MOD: ADD <any> actions=push:NXM_NX_REG0[],pop:NXM_NX_REG0[]
 NXT_FLOW_MOD: ADD NXM_OF_VLAN_TCI_W(1123/1fff) actions=drop
 ]])
 AT_CLEANUP
index 609df9f..e7d9cfb 100644 (file)
@@ -1063,6 +1063,23 @@ in field \fIdst\fR.
 Example: \fBload:55\->NXM_NX_REG2[0..5]\fR loads value 55 (bit pattern
 \fB110111\fR) into bits 0 through 5, inclusive, in register 2.
 .
+.IP "\fBpush:\fIsrc\fB[\fIstart\fB..\fIend\fB]"
+Pushes \fIstart\fR to \fIend\fR bits inclusive, in fields
+on top of the stack.
+.IP
+Example: \fBpush:NXM_NX_REG2[0..5]\fR push the value stored in register
+2 bits 0 through 5, inclusive, on to the internal stack.
+.
+.IP "\fBpop:\fIdst\fB[\fIstart\fB..\fIend\fB]"
+Pops from the top of the stack, retrieves the \fIstart\fR to \fIend\fR bits
+inclusive, from the value popped and store them into the corresponding
+bits in \fIdst\fR.
+.
+.IP
+Example: \fBpop:NXM_NX_REG2[0..5]\fR pops the value from top of the stack.
+Set register 2 bits 0 through 5, inclusive, based on bits 0 through 5 from the
+value just popped.
+.
 .IP "\fBset_field:\fIvalue\fB\->\fIdst"
 Writes the literal \fIvalue\fR into the field \fIdst\fR, which should
 be specified as a name used for matching.  (This is similar to