aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS2
-rw-r--r--include/openflow/nicira-ext.h19
-rw-r--r--lib/nx-match.c146
-rw-r--r--lib/nx-match.h26
-rw-r--r--lib/ofp-actions.c38
-rw-r--r--lib/ofp-actions.h10
-rw-r--r--lib/ofp-parse.c6
-rw-r--r--lib/ofp-util.def2
-rw-r--r--ofproto/ofproto-dpif.c19
-rw-r--r--tests/ofproto-dpif.at20
-rw-r--r--tests/ovs-ofctl.at2
-rw-r--r--utilities/ovs-ofctl.8.in17
12 files changed, 307 insertions, 0 deletions
diff --git a/NEWS b/NEWS
index 7fb09326..23532782 100644
--- 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
diff --git a/include/openflow/nicira-ext.h b/include/openflow/nicira-ext.h
index cdb19520..f9b0af45 100644
--- a/include/openflow/nicira-ext.h
+++ b/include/openflow/nicira-ext.h
@@ -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
diff --git a/lib/nx-match.c b/lib/nx-match.c
index e5545dea..6efc94df 100644
--- a/lib/nx-match.c
+++ b/lib/nx-match.c
@@ -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);
}
+
+/* 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);
+ }
+ }
+}
diff --git a/lib/nx-match.h b/lib/nx-match.h
index 6a57297c..7d316d80 100644
--- a/lib/nx-match.h
+++ b/lib/nx-match.h
@@ -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);
diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c
index 6dabc5a4..d405d2d4 100644
--- a/lib/ofp-actions.c
+++ b/lib/ofp-actions.c
@@ -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;
diff --git a/lib/ofp-actions.h b/lib/ofp-actions.h
index 2d934f98..0189c8ae 100644
--- a/lib/ofp-actions.h
+++ b/lib/ofp-actions.h
@@ -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. */
diff --git a/lib/ofp-parse.c b/lib/ofp-parse.c
index f1802048..e8abc9f6 100644
--- a/lib/ofp-parse.c
+++ b/lib/ofp-parse.c
@@ -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;
}
}
diff --git a/lib/ofp-util.def b/lib/ofp-util.def
index 439d34ed..b7dde483 100644
--- a/lib/ofp-util.def
+++ b/lib/ofp-util.def
@@ -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")
diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
index f37d8406..10e4e23b 100644
--- a/ofproto/ofproto-dpif.c
+++ b/ofproto/ofproto-dpif.c
@@ -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'
diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
index b795f561..7915792c 100644
--- a/tests/ofproto-dpif.at
+++ b/tests/ofproto-dpif.at
@@ -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])
diff --git a/tests/ovs-ofctl.at b/tests/ovs-ofctl.at
index 7395fd14..075f2e49 100644
--- a/tests/ovs-ofctl.at
+++ b/tests/ovs-ofctl.at
@@ -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
diff --git a/utilities/ovs-ofctl.8.in b/utilities/ovs-ofctl.8.in
index 609df9f2..e7d9cfb5 100644
--- a/utilities/ovs-ofctl.8.in
+++ b/utilities/ovs-ofctl.8.in
@@ -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