Post-v1.2.0
------------------------
+ - OpenFlow:
+ - Added an OpenFlow extension which allows the "output" action to accept
+ NXM fields.
- ovs-appctl:
- New "version" command to determine version of running daemon
- ovs-vswitchd:
NXAST_AUTOPATH, /* struct nx_action_autopath */
NXAST_BUNDLE, /* struct nx_action_bundle */
NXAST_BUNDLE_LOAD, /* struct nx_action_bundle */
- NXAST_RESUBMIT_TABLE /* struct nx_action_resubmit */
+ NXAST_RESUBMIT_TABLE, /* struct nx_action_resubmit */
+ NXAST_OUTPUT_REG /* struct nx_action_output_reg */
};
/* Header for Nicira-defined actions. */
NX_BD_ALG_HRW /* Highest Random Weight. */
};
\f
+/* Action structure for NXAST_OUTPUT_REG.
+ *
+ * Outputs to the OpenFlow port number written to src[ofs:ofs+nbits].
+ *
+ * The format and semantics of 'src' and 'ofs_nbits' are similar to those for
+ * the NXAST_REG_LOAD action.
+ *
+ * The acceptable nxm_header values for 'src' are the same as the acceptable
+ * nxm_header values for the 'src' field of NXAST_REG_MOVE.
+ *
+ * The 'max_len' field indicates the number of bytes to send when the chosen
+ * port is OFPP_CONTROLLER. Its semantics are equivalent to the 'max_len'
+ * field of OFPAT_OUTPUT.
+ *
+ * The 'zero' field is required to be zeroed for forward compatibility. */
+struct nx_action_output_reg {
+ ovs_be16 type; /* OFPAT_VENDOR. */
+ ovs_be16 len; /* 24. */
+ ovs_be32 vendor; /* NX_VENDOR_ID. */
+ ovs_be16 subtype; /* NXAST_OUTPUT_REG. */
+
+ ovs_be16 ofs_nbits; /* (ofs << 6) | (n_bits - 1). */
+ ovs_be32 src; /* Source. */
+
+ ovs_be16 max_len; /* Max length to send to controller. */
+
+ uint8_t zero[6]; /* Reserved, must be zero. */
+};
+OFP_ASSERT(sizeof(struct nx_action_output_reg) == 24);
+\f
/* Flexible flow specifications (aka NXM = Nicira Extended Match).
*
* OpenFlow 1.0 has "struct ofp_match" for specifying flow matches. This
return false;
}
+static void
+parse_output(struct ofpbuf *b, char *arg)
+{
+ if (strchr(arg, '[')) {
+ struct nx_action_output_reg *naor;
+ int ofs, n_bits;
+ uint32_t src;
+
+ nxm_parse_field_bits(arg, &src, &ofs, &n_bits);
+
+ naor = put_action(b, sizeof *naor, OFPAT_VENDOR);
+ naor->vendor = htonl(NX_VENDOR_ID);
+ naor->subtype = htons(NXAST_OUTPUT_REG);
+ naor->ofs_nbits = nxm_encode_ofs_nbits(ofs, n_bits);
+ naor->src = htonl(src);
+ naor->max_len = htons(UINT16_MAX);
+ } else {
+ put_output_action(b, str_to_u32(arg));
+ }
+}
+
static void
parse_resubmit(struct nx_action_resubmit *nar, char *arg)
{
} else if (!strcasecmp(act, "bundle_load")) {
bundle_parse_load(b, arg);
} else if (!strcasecmp(act, "output")) {
- put_output_action(b, str_to_u32(arg));
+ parse_output(b, arg);
} else if (!strcasecmp(act, "enqueue")) {
char *sp = NULL;
char *port_s = strtok_r(arg, ":q", &sp);
const struct nx_action_reg_load *load;
const struct nx_action_multipath *nam;
const struct nx_action_autopath *naa;
+ const struct nx_action_output_reg *naor;
uint16_t port;
switch (code) {
bundle_format((const struct nx_action_bundle *) a, s);
break;
+ case OFPUTIL_NXAST_OUTPUT_REG:
+ naor = (const struct nx_action_output_reg *) a;
+ ds_put_cstr(s, "output:");
+ nxm_format_field_bits(s, ntohl(naor->src),
+ nxm_decode_ofs(naor->ofs_nbits),
+ nxm_decode_n_bits(naor->ofs_nbits));
+
default:
break;
}
return 0;
}
+static int
+check_output_reg(const struct nx_action_output_reg *naor,
+ const struct flow *flow)
+{
+ size_t i;
+
+ for (i = 0; i < sizeof naor->zero; i++) {
+ if (naor->zero[i]) {
+ return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_ARGUMENT);
+ }
+ }
+
+ return nxm_src_check(naor->src, nxm_decode_ofs(naor->ofs_nbits),
+ nxm_decode_n_bits(naor->ofs_nbits), flow);
+}
+
int
validate_actions(const union ofp_action *actions, size_t n_actions,
const struct flow *flow, int max_ports)
max_ports, flow);
break;
+ case OFPUTIL_NXAST_OUTPUT_REG:
+ error = check_output_reg((const struct nx_action_output_reg *) a,
+ flow);
+ break;
+
case OFPUTIL_NXAST_RESUBMIT_TABLE:
error = check_resubmit_table(
(const struct nx_action_resubmit *) a);
NXAST_ACTION(NXAST_BUNDLE, struct nx_action_bundle, true);
NXAST_ACTION(NXAST_BUNDLE_LOAD, struct nx_action_bundle, true);
NXAST_ACTION(NXAST_RESUBMIT_TABLE, struct nx_action_resubmit, false);
+ NXAST_ACTION(NXAST_OUTPUT_REG, struct nx_action_output_reg, false);
#undef NXAST_ACTION
case NXAST_SNAT__OBSOLETE:
OFPUTIL_NXAST_AUTOPATH,
OFPUTIL_NXAST_BUNDLE,
OFPUTIL_NXAST_BUNDLE_LOAD,
- OFPUTIL_NXAST_RESUBMIT_TABLE
+ OFPUTIL_NXAST_RESUBMIT_TABLE,
+ OFPUTIL_NXAST_OUTPUT_REG
};
int ofputil_decode_action(const union ofp_action *);
}
}
+static void
+xlate_output_reg_action(struct action_xlate_ctx *ctx,
+ const struct nx_action_output_reg *naor)
+{
+ uint64_t ofp_port;
+
+ ofp_port = nxm_read_field_bits(naor->src, naor->ofs_nbits, &ctx->flow);
+
+ if (ofp_port <= UINT16_MAX) {
+ xlate_output_action__(ctx, ofp_port, ntohs(naor->max_len));
+ }
+}
+
static void
xlate_output_action(struct action_xlate_ctx *ctx,
const struct ofp_action_output *oao)
const struct nx_action_multipath *nam;
const struct nx_action_autopath *naa;
const struct nx_action_bundle *nab;
+ const struct nx_action_output_reg *naor;
enum ofputil_action_code code;
ovs_be64 tun_id;
bundle_execute_load(nab, &ctx->flow, slave_enabled_cb,
ctx->ofproto);
break;
+
+ case OFPUTIL_NXAST_OUTPUT_REG:
+ naor = (const struct nx_action_output_reg *) ia;
+ xlate_output_reg_action(ctx, naor);
+ break;
}
}
}
])
OFPROTO_STOP
AT_CLEANUP
+
+AT_SETUP([ofproto-dpif - output])
+OFPROTO_START
+AT_DATA([flows.txt], [dnl
+in_port=1 actions=resubmit:2,resubmit:3,resubmit:4,resubmit:5,resubmit:6,resubmit:7
+in_port=2 actions=output:9
+in_port=3 actions=load:55->NXM_NX_REG0[[]],output:NXM_NX_REG0[[]],load:66->NXM_NX_REG1[[]]
+in_port=4 actions=output:10,output:NXM_NX_REG0[[]],output:NXM_NX_REG1[[]],output:11
+in_port=5 actions=load:77->NXM_NX_REG0[[0..15]],load:88->NXM_NX_REG0[[16..31]]
+in_port=6 actions=output:NXM_NX_REG0[[0..15]],output:NXM_NX_REG0[[16..31]]
+in_port=7 actions=load:0x110000ff->NXM_NX_REG0[[]],output:NXM_NX_REG0[[]]
+])
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+AT_CHECK([ovs-appctl -t test-openflowd ofproto/trace br0 'in_port(1),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),icmp(type=8,code=0)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+ [Datapath actions: 9,55,10,55,66,11,77,88
+])
+OFPROTO_STOP
+AT_CLEANUP
actions=bundle_load(symmetric_l4,60,hrw,ofport,NXM_NX_REG0[0..30],slaves:)
actions=output:1,bundle_load(eth_src,0,hrw,ofport,NXM_NX_REG0[16..31],slaves:1),output:2
actions=resubmit:1,resubmit(2),resubmit(,3),resubmit(2,3)
+actions=output:1,output:NXM_NX_REG0[],output:2,output:NXM_NX_REG1[16..31],output:3
]])
AT_CHECK([ovs-ofctl parse-flows flows.txt
NXT_FLOW_MOD: ADD table:255 actions=bundle_load(symmetric_l4,60,hrw,ofport,NXM_NX_REG0[0..30],slaves:)
NXT_FLOW_MOD: ADD table:255 actions=output:1,bundle_load(eth_src,0,hrw,ofport,NXM_NX_REG0[16..31],slaves:1),output:2
NXT_FLOW_MOD: ADD table:255 actions=resubmit:1,resubmit:2,resubmit(,3),resubmit(2,3)
+NXT_FLOW_MOD: ADD table:255 actions=output:1,output:NXM_NX_REG0[],output:2,output:NXM_NX_REG1[16..31],output:3
]])
AT_CLEANUP
.
.RS
.IP \fBoutput\fR:\fIport\fR
-Outputs the packet on the port specified by \fIport\fR.
+.IQ \fBoutput\fR:\fIsrc\fB[\fIstart\fB..\fIend\fB]
+Outputs the packet. If \fIport\fR is an OpenFlow port number, outputs directly
+to it. Otherwise, outputs to the OpenFlow port number read from \fIsrc\fR
+which must be an NXM field as described above. Outputting to an NXM field is
+an OpenFlow extension which is not supported by standard OpenFlow switches.
+.IP
+Example: \fBoutput:NXM_NX_REG0[16..31]\fR outputs to the OpenFlow port number
+written in the upper half of register 0.
.
.IP \fBenqueue\fR:\fIport\fB:\fIqueue\fR
Enqueues the packet on the specified \fIqueue\fR within port