/* NX1.0+(29): struct nx_action_sample. */
NXAST_RAW_SAMPLE,
+ /* NX1.0+(38): struct nx_action_sample2. */
+ NXAST_RAW_SAMPLE2,
/* NX1.0+(34): struct nx_action_conjunction. */
NXAST_RAW_CONJUNCTION,
}
for (i = 0; i < bundle->n_slaves; i++) {
- uint16_t ofp_port = ntohs(((ovs_be16 *)(nab + 1))[i]);
+ ofp_port_t ofp_port = u16_to_ofp(ntohs(((ovs_be16 *)(nab + 1))[i]));
ofpbuf_put(ofpacts, &ofp_port, sizeof ofp_port);
bundle = ofpacts->header;
}
*
* Resubmit actions may be used any number of times within a set of actions.
*
- * Resubmit actions may nest to an implementation-defined depth. Beyond this
- * implementation-defined depth, further resubmit actions are simply ignored.
+ * Resubmit actions may nest. To prevent infinite loops and excessive resource
+ * use, the implementation may limit nesting depth and the total number of
+ * resubmits:
+ *
+ * - Open vSwitch 1.0.1 and earlier did not support recursion.
+ *
+ * - Open vSwitch 1.0.2 and 1.0.3 limited recursion to 8 levels.
+ *
+ * - Open vSwitch 1.1 and 1.2 limited recursion to 16 levels.
+ *
+ * - Open vSwitch 1.2 through 1.8 limited recursion to 32 levels.
+ *
+ * - Open vSwitch 1.9 through 2.0 limited recursion to 64 levels.
+ *
+ * - Open vSwitch 2.1 through 2.5 limited recursion to 64 levels and impose
+ * a total limit of 4,096 resubmits per flow translation (earlier versions
+ * did not impose any total limit).
*
* NXAST_RESUBMIT ignores 'table' and 'pad'. NXAST_RESUBMIT_TABLE requires
* 'pad' to be all-bits-zero.
note = ofpact_put_NOTE(out);
note->length = length;
ofpbuf_put(out, nan->note, length);
+ note = out->header;
ofpact_finish_NOTE(out, ¬e);
return 0;
};
OFP_ASSERT(sizeof(struct nx_action_sample) == 24);
+/* Action structure for NXAST_SAMPLE2.
+ *
+ * This replacement for NXAST_SAMPLE makes it support exporting
+ * egress tunnel information. */
+struct nx_action_sample2 {
+ ovs_be16 type; /* OFPAT_VENDOR. */
+ ovs_be16 len; /* Length is 32. */
+ ovs_be32 vendor; /* NX_VENDOR_ID. */
+ ovs_be16 subtype; /* NXAST_SAMPLE. */
+ ovs_be16 probability; /* Fraction of packets to sample. */
+ ovs_be32 collector_set_id; /* ID of collector set in OVSDB. */
+ ovs_be32 obs_domain_id; /* ID of sampling observation domain. */
+ ovs_be32 obs_point_id; /* ID of sampling observation point. */
+ ovs_be16 sampling_port; /* Sampling port. */
+ uint8_t pad[6]; /* Pad to a multiple of 8 bytes */
+ };
+ OFP_ASSERT(sizeof(struct nx_action_sample2) == 32);
+
static enum ofperr
decode_NXAST_RAW_SAMPLE(const struct nx_action_sample *nas,
enum ofp_version ofp_version OVS_UNUSED,
struct ofpact_sample *sample;
sample = ofpact_put_SAMPLE(out);
+ sample->ofpact.raw = NXAST_RAW_SAMPLE;
+ sample->probability = ntohs(nas->probability);
+ sample->collector_set_id = ntohl(nas->collector_set_id);
+ sample->obs_domain_id = ntohl(nas->obs_domain_id);
+ sample->obs_point_id = ntohl(nas->obs_point_id);
+ /* Default value for sampling port is OFPP_NONE */
+ sample->sampling_port = OFPP_NONE;
+
+ if (sample->probability == 0) {
+ return OFPERR_OFPBAC_BAD_ARGUMENT;
+ }
+
+ return 0;
+}
+
+static enum ofperr
+decode_NXAST_RAW_SAMPLE2(const struct nx_action_sample2 *nas,
+ enum ofp_version ofp_version OVS_UNUSED,
+ struct ofpbuf *out)
+{
+ struct ofpact_sample *sample;
+
+ sample = ofpact_put_SAMPLE(out);
+ sample->ofpact.raw = NXAST_RAW_SAMPLE2;
sample->probability = ntohs(nas->probability);
sample->collector_set_id = ntohl(nas->collector_set_id);
sample->obs_domain_id = ntohl(nas->obs_domain_id);
sample->obs_point_id = ntohl(nas->obs_point_id);
+ sample->sampling_port = u16_to_ofp(ntohs(nas->sampling_port));
if (sample->probability == 0) {
return OFPERR_OFPBAC_BAD_ARGUMENT;
encode_SAMPLE(const struct ofpact_sample *sample,
enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out)
{
- struct nx_action_sample *nas;
-
- nas = put_NXAST_SAMPLE(out);
- nas->probability = htons(sample->probability);
- nas->collector_set_id = htonl(sample->collector_set_id);
- nas->obs_domain_id = htonl(sample->obs_domain_id);
- nas->obs_point_id = htonl(sample->obs_point_id);
+ if (sample->ofpact.raw == NXAST_RAW_SAMPLE2
+ || sample->sampling_port != OFPP_NONE) {
+ struct nx_action_sample2 *nas = put_NXAST_SAMPLE2(out);
+ nas->probability = htons(sample->probability);
+ nas->collector_set_id = htonl(sample->collector_set_id);
+ nas->obs_domain_id = htonl(sample->obs_domain_id);
+ nas->obs_point_id = htonl(sample->obs_point_id);
+ nas->sampling_port = htons(ofp_to_u16(sample->sampling_port));
+ } else {
+ struct nx_action_sample *nas = put_NXAST_SAMPLE(out);
+ nas->probability = htons(sample->probability);
+ nas->collector_set_id = htonl(sample->collector_set_id);
+ nas->obs_domain_id = htonl(sample->obs_domain_id);
+ nas->obs_point_id = htonl(sample->obs_point_id);
+ }
}
/* Parses 'arg' as the argument to a "sample" action, and appends such an
enum ofputil_protocol *usable_protocols OVS_UNUSED)
{
struct ofpact_sample *os = ofpact_put_SAMPLE(ofpacts);
- char *key, *value;
+ os->sampling_port = OFPP_NONE;
+ char *key, *value;
while (ofputil_parse_key_value(&arg, &key, &value)) {
char *error = NULL;
error = str_to_u32(value, &os->obs_domain_id);
} else if (!strcmp(key, "obs_point_id")) {
error = str_to_u32(value, &os->obs_point_id);
+ } else if (!strcmp(key, "sampling_port")) {
+ if (!ofputil_port_from_string(value, &os->sampling_port)) {
+ error = xasprintf("%s: unknown port", value);
+ }
} else {
error = xasprintf("invalid key \"%s\" in \"sample\" argument",
key);
if (os->probability == 0) {
return xstrdup("non-zero \"probability\" must be specified on sample");
}
+
return NULL;
}
ds_put_format(s, "%ssample(%s%sprobability=%s%"PRIu16
",%scollector_set_id=%s%"PRIu32
",%sobs_domain_id=%s%"PRIu32
- ",%sobs_point_id=%s%"PRIu32"%s)%s",
+ ",%sobs_point_id=%s%"PRIu32,
colors.paren, colors.end,
colors.param, colors.end, a->probability,
colors.param, colors.end, a->collector_set_id,
colors.param, colors.end, a->obs_domain_id,
- colors.param, colors.end, a->obs_point_id,
- colors.paren, colors.end);
+ colors.param, colors.end, a->obs_point_id);
+ if (a->sampling_port != OFPP_NONE) {
+ ds_put_format(s, ",%ssampling_port=%s%"PRIu16,
+ colors.param, colors.end, a->sampling_port);
+ }
+ ds_put_format(s, "%s)%s", colors.paren, colors.end);
}
\f
/* debug_recirc instruction. */
nat = ofpact_put_NAT(out);
nat->flags = ntohs(nan->flags);
+ /* Check for unknown or mutually exclusive flags. */
+ if ((nat->flags & ~NX_NAT_F_MASK)
+ || (nat->flags & NX_NAT_F_SRC && nat->flags & NX_NAT_F_DST)
+ || (nat->flags & NX_NAT_F_PROTO_HASH
+ && nat->flags & NX_NAT_F_PROTO_RANDOM)) {
+ return OFPERR_OFPBAC_BAD_ARGUMENT;
+ }
+
#define NX_NAT_GET_OPT(DST, SRC, LEN, TYPE) \
(LEN >= sizeof(TYPE) \
? (memcpy(DST, SRC, sizeof(TYPE)), LEN -= sizeof(TYPE), \
}
}
if (on->flags & NX_NAT_F_SRC && on->flags & NX_NAT_F_DST) {
- return xasprintf("May only specify one of \"snat\" or \"dnat\".");
+ return xasprintf("May only specify one of \"src\" or \"dst\".");
}
if (!(on->flags & NX_NAT_F_SRC || on->flags & NX_NAT_F_DST)) {
if (on->flags) {
- return xasprintf("Flags allowed only with \"snat\" or \"dnat\".");
+ return xasprintf("Flags allowed only with \"src\" or \"dst\".");
}
if (on->range_af != AF_UNSPEC) {
- return xasprintf("Range allowed only with \"snat\" or \"dnat\".");
+ return xasprintf("Range allowed only with \"src\" or \"dst\".");
}
}
+ if (on->flags & NX_NAT_F_PROTO_HASH && on->flags & NX_NAT_F_PROTO_RANDOM) {
+ return xasprintf("Both \"hash\" and \"random\" are not allowed.");
+ }
+
return NULL;
}
* not be sent anywhere. */
if (!ofpacts_copy_last(action_list, action_set, OFPACT_GROUP) &&
!ofpacts_copy_last(action_list, action_set, OFPACT_OUTPUT) &&
- !ofpacts_copy_last(action_list, action_set, OFPACT_RESUBMIT)) {
+ !ofpacts_copy_last(action_list, action_set, OFPACT_RESUBMIT) &&
+ !ofpacts_copy_last(action_list, action_set, OFPACT_CT)) {
ofpbuf_clear(action_list);
}
}
case OFP13_VERSION:
case OFP14_VERSION:
case OFP15_VERSION:
+ case OFP16_VERSION:
default:
return of12;
}