@@ -855,6 +855,24 @@ enum ovs_nat_attr {
#define OVS_NAT_ATTR_MAX (__OVS_NAT_ATTR_MAX - 1)
+/*
+ * enum ovs_check_pkt_len_attr - Attributes for %OVS_ACTION_ATTR_CHECK_PKT_LEN.
+ *
+ * @OVS_CHECK_PKT_LEN_ATTR_PKT_LEN: u16 Packet length to check for.
+ * @OVS_CHECK_PKT_LEN_ATTR_USERSPACE_COND: u8 comparison condition to send
+ * the packet to userspace. One of OVS_CHECK_PKT_LEN_COND_*.
+ * @OVS_CHECK_PKT_LEN_ATTR_USERPACE - Nested OVS_USERSPACE_ATTR_* actions.
+ */
+enum ovs_check_pkt_len_attr {
+ OVS_CHECK_PKT_LEN_ATTR_UNSPEC,
+ OVS_CHECK_PKT_LEN_ATTR_PKT_LEN,
+ OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER,
+ OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL,
+ __OVS_CHECK_PKT_LEN_ATTR_MAX,
+};
+
+#define OVS_CHECK_PKT_LEN_ATTR_MAX (__OVS_CHECK_PKT_LEN_ATTR_MAX - 1)
+
/**
* enum ovs_action_attr - Action types.
*
@@ -909,8 +927,6 @@ enum ovs_nat_attr {
* tunnel header.
* @OVS_ACTION_ATTR_METER: Run packet through a meter, which may drop the
* packet, or modify the packet (e.g., change the DSCP field).
- * @OVS_ACTION_ATTR_CLONE: make a copy of the packet and execute a list of
- * actions without affecting the original packet and key.
*/
enum ovs_action_attr {
@@ -936,12 +952,13 @@ enum ovs_action_attr {
OVS_ACTION_ATTR_CT_CLEAR, /* No argument. */
OVS_ACTION_ATTR_PUSH_NSH, /* Nested OVS_NSH_KEY_ATTR_*. */
OVS_ACTION_ATTR_POP_NSH, /* No argument. */
- OVS_ACTION_ATTR_METER, /* u32 meter number. */
- OVS_ACTION_ATTR_CLONE, /* Nested OVS_CLONE_ATTR_*. */
+ OVS_ACTION_ATTR_METER, /* u32 meter number. */
+ OVS_ACTION_ATTR_CHECK_PKT_LEN, /* Nested OVS_CHECK_PKT_LEN_ATTR_*. */
#ifndef __KERNEL__
OVS_ACTION_ATTR_TUNNEL_PUSH, /* struct ovs_action_push_tnl*/
OVS_ACTION_ATTR_TUNNEL_POP, /* u32 port number. */
+ OVS_ACTION_ATTR_CLONE, /* Nested OVS_CLONE_ATTR_*. */
#endif
__OVS_ACTION_ATTR_MAX, /* Nothing past this will be accepted
* from userspace. */
@@ -1034,9 +1051,4 @@ struct ovs_zone_limit {
__u32 count;
};
-#define OVS_CLONE_ATTR_EXEC 0 /* Specify an u32 value. When nonzero,
- * actions in clone will not change flow
- * keys. False otherwise.
- */
-
#endif /* _LINUX_OPENVSWITCH_H */
@@ -123,6 +123,8 @@ struct vl_mff_map;
OFPACT(NAT, ofpact_nat, ofpact, "nat") \
OFPACT(OUTPUT_TRUNC, ofpact_output_trunc,ofpact, "output_trunc") \
OFPACT(CLONE, ofpact_nest, actions, "clone") \
+ OFPACT(CHECK_PKT_LARGER, ofpact_check_pkt_larger, ofpact, \
+ "check_pkt_larger") \
\
/* Debugging actions. \
* \
@@ -225,6 +227,13 @@ ofpact_last(const struct ofpact *a, const struct ofpact *ofpacts,
return ofpact_next(a) == ofpact_end(ofpacts, ofpact_len);
}
+static inline bool
+ofpact_remaining_len(const struct ofpact *a, const struct ofpact *ofpacts,
+ size_t ofpact_len)
+{
+ return ofpact_len - ((uint8_t *)a - (uint8_t *)ofpacts);
+}
+
static inline const struct ofpact *
ofpact_find_type_flattened(const struct ofpact *a, enum ofpact_type type,
const struct ofpact * const end)
@@ -620,6 +629,15 @@ struct ofpact_meter {
);
};
+/* OFPACT_CHECK_PKT_LARGER.
+ *
+ * Used for NXAST_CHECK_PKT_LARGER. */
+struct ofpact_check_pkt_larger {
+ struct ofpact ofpact;
+ uint16_t pkt_len;
+ struct mf_subfield dst;
+};
+
/* OFPACT_WRITE_ACTIONS, OFPACT_CLONE.
*
* Used for OFPIT11_WRITE_ACTIONS, NXAST_CLONE. */
@@ -6814,6 +6814,7 @@ dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
case OVS_ACTION_ATTR_PUSH_NSH:
case OVS_ACTION_ATTR_POP_NSH:
case OVS_ACTION_ATTR_CT_CLEAR:
+ case OVS_ACTION_ATTR_CHECK_PKT_LEN:
case __OVS_ACTION_ATTR_MAX:
OVS_NOT_REACHED();
}
@@ -1274,6 +1274,7 @@ dpif_execute_helper_cb(void *aux_, struct dp_packet_batch *packets_,
case OVS_ACTION_ATTR_POP_NSH:
case OVS_ACTION_ATTR_CT_CLEAR:
case OVS_ACTION_ATTR_UNSPEC:
+ case OVS_ACTION_ATTR_CHECK_PKT_LEN:
case __OVS_ACTION_ATTR_MAX:
OVS_NOT_REACHED();
}
@@ -642,6 +642,64 @@ odp_execute_clone(void *dp, struct dp_packet_batch *batch, bool steal,
}
}
+static void
+odp_execute_check_pkt_len(void *dp, struct dp_packet *packet, bool steal,
+ const struct nlattr *action,
+ odp_execute_cb dp_execute_action)
+{
+ const struct nlattr *a;
+ const struct nlattr *attrs[OVS_CHECK_PKT_LEN_ATTR_MAX + 1];
+ const struct nlattr *subactions = NULL;
+ size_t subactions_size = 0;
+ struct dp_packet_batch pb;
+ size_t left;
+
+ bool is_greater = false;
+ memset(attrs, 0, sizeof(attrs));
+ NL_NESTED_FOR_EACH_UNSAFE (a, left, action) {
+ int type = nl_attr_type(a);
+
+ if (!type || type > OVS_CHECK_PKT_LEN_ATTR_MAX || attrs[type]) {
+ OVS_NOT_REACHED();
+ }
+ attrs[type] = a;
+ }
+
+ if (left) {
+ OVS_NOT_REACHED();
+ }
+ a = attrs[OVS_CHECK_PKT_LEN_ATTR_PKT_LEN];
+ if (!a) {
+ OVS_NOT_REACHED();
+ }
+
+ is_greater = dp_packet_size(packet) > nl_attr_get_u16(a);
+ if (is_greater) {
+ a = attrs[OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER];
+ } else {
+ a = attrs[OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL];
+ }
+
+ if (a) {
+ subactions = nl_attr_get(a);
+ subactions_size = nl_attr_get_size(a);
+ }
+
+ if (!steal) {
+ /* The 'subactions' may modify the packet, but the modification
+ * should not propagate beyond this action. Make a copy
+ * the packet in case we don't own the packet, so that the
+ * 'subactions' are only applid to check_pkt_len. 'odp_execute_actions'
+ * will free the clone. */
+ packet = dp_packet_clone(packet);
+ }
+ /* If subactions is NULL, the packet will be freed by
+ * odp_execute_actions. */
+ dp_packet_batch_init_packet(&pb, packet);
+ odp_execute_actions(dp, &pb, true, subactions, subactions_size,
+ dp_execute_action);
+}
+
static bool
requires_datapath_assistance(const struct nlattr *a)
{
@@ -673,6 +731,7 @@ requires_datapath_assistance(const struct nlattr *a)
case OVS_ACTION_ATTR_PUSH_NSH:
case OVS_ACTION_ATTR_POP_NSH:
case OVS_ACTION_ATTR_CT_CLEAR:
+ case OVS_ACTION_ATTR_CHECK_PKT_LEN:
return false;
case OVS_ACTION_ATTR_UNSPEC:
@@ -900,6 +959,19 @@ odp_execute_actions(void *dp, struct dp_packet_batch *batch, bool steal,
}
break;
+ case OVS_ACTION_ATTR_CHECK_PKT_LEN:
+ DP_PACKET_BATCH_FOR_EACH (i, packet, batch) {
+ odp_execute_check_pkt_len(dp, packet, steal && last_action, a,
+ dp_execute_action);
+ }
+
+ if (last_action) {
+ /* We do not need to free the packets.
+ * odp_execute_check_pkt_len() has stolen them. */
+ return;
+ }
+ break;
+
case OVS_ACTION_ATTR_OUTPUT:
case OVS_ACTION_ATTR_TUNNEL_PUSH:
case OVS_ACTION_ATTR_TUNNEL_POP:
@@ -131,6 +131,7 @@ odp_action_len(uint16_t type)
case OVS_ACTION_ATTR_CLONE: return ATTR_LEN_VARIABLE;
case OVS_ACTION_ATTR_PUSH_NSH: return ATTR_LEN_VARIABLE;
case OVS_ACTION_ATTR_POP_NSH: return 0;
+ case OVS_ACTION_ATTR_CHECK_PKT_LEN: return ATTR_LEN_VARIABLE;
case OVS_ACTION_ATTR_UNSPEC:
case __OVS_ACTION_ATTR_MAX:
@@ -1042,6 +1043,38 @@ format_odp_set_nsh(struct ds *ds, const struct nlattr *attr)
ds_put_cstr(ds, "))");
}
+static void
+format_odp_check_pkt_len_action(struct ds *ds, const struct nlattr *attr,
+ const struct hmap *portno_names OVS_UNUSED)
+{
+ static const struct nl_policy ovs_cpl_policy[] = {
+ [OVS_CHECK_PKT_LEN_ATTR_PKT_LEN] = { .type = NL_A_U16 },
+ };
+ struct nlattr *a[ARRAY_SIZE(ovs_cpl_policy)];
+ ds_put_cstr(ds, "check_pkt_len");
+ if (!nl_parse_nested(attr, ovs_cpl_policy, a, ARRAY_SIZE(a))) {
+ ds_put_cstr(ds, "(error)");
+ return;
+ }
+
+ uint16_t pkt_len = nl_attr_get_u16(a[OVS_CHECK_PKT_LEN_ATTR_PKT_LEN]);
+ ds_put_format(ds, "(> %u ? (", pkt_len);
+ const struct nlattr *acts;
+ acts = nl_attr_find_nested(attr,
+ OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER);
+ if (acts) {
+ format_odp_actions(ds, nl_attr_get(acts), nl_attr_get_size(acts),
+ portno_names);
+ }
+ ds_put_cstr(ds, ") : (");
+ acts = nl_attr_find_nested(attr,
+ OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL);
+ if (acts) {
+ format_odp_actions(ds, nl_attr_get(acts), nl_attr_get_size(acts),
+ portno_names);
+ }
+ ds_put_cstr(ds, "))");
+}
static void
format_odp_action(struct ds *ds, const struct nlattr *a,
@@ -1181,6 +1214,9 @@ format_odp_action(struct ds *ds, const struct nlattr *a,
case OVS_ACTION_ATTR_POP_NSH:
ds_put_cstr(ds, "pop_nsh()");
break;
+ case OVS_ACTION_ATTR_CHECK_PKT_LEN:
+ format_odp_check_pkt_len_action(ds, a, portno_names);
+ break;
case OVS_ACTION_ATTR_UNSPEC:
case __OVS_ACTION_ATTR_MAX:
default:
@@ -355,6 +355,8 @@ enum ofp_raw_action_type {
/* NX1.3+(48): void. */
NXAST_RAW_DEC_NSH_TTL,
+ /* NX1.0+(49): struct nx_action_check_pkt_larger, ... */
+ OFPAT_RAW_CHECK_PKT_LARGER,
/* ## ------------------ ## */
/* ## Debugging actions. ## */
/* ## ------------------ ## */
@@ -492,6 +494,7 @@ ofpact_next_flattened(const struct ofpact *ofpact)
case OFPACT_ENCAP:
case OFPACT_DECAP:
case OFPACT_DEC_NSH_TTL:
+ case OFPACT_CHECK_PKT_LARGER:
return ofpact_next(ofpact);
case OFPACT_CLONE:
@@ -7400,6 +7403,97 @@ check_WRITE_METADATA(const struct ofpact_metadata *a OVS_UNUSED,
return 0;
}
+/* Check packet length action. */
+
+struct nx_action_check_pkt_larger {
+ ovs_be16 type; /* OFPAT_VENDOR. */
+ ovs_be16 len; /* 24. */
+ ovs_be32 vendor; /* NX_VENDOR_ID. */
+ ovs_be16 subtype; /* NXAST_OUTPUT_REG. */
+ uint8_t pad[6];
+ ovs_be16 pkt_len; /* Length of the packet to check. */
+ ovs_be16 ofs_nbits; /* (ofs << 6) | (n_bits - 1). */
+ ovs_be32 dst; /* Destination register. */
+};
+
+OFP_ASSERT(sizeof(struct nx_action_check_pkt_larger) == 24);
+
+static enum ofperr
+decode_OFPAT_RAW_CHECK_PKT_LARGER(
+ const struct nx_action_check_pkt_larger *ncpl,
+ enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out)
+{
+ struct ofpact_check_pkt_larger *check_pkt_larger =
+ ofpact_put_CHECK_PKT_LARGER(out);
+ check_pkt_larger->pkt_len = ntohs(ncpl->pkt_len);
+ const struct mf_field *mf = mf_from_nxm_header(ntohl(ncpl->dst), NULL);
+ check_pkt_larger->dst.field = mf;
+ check_pkt_larger->dst.ofs = nxm_decode_ofs(ncpl->ofs_nbits);
+ check_pkt_larger->dst.n_bits = nxm_decode_n_bits(ncpl->ofs_nbits);;
+ return 0;
+}
+
+static void
+encode_CHECK_PKT_LARGER(const struct ofpact_check_pkt_larger *check_pkt_larger,
+ enum ofp_version ofp_version OVS_UNUSED,
+ struct ofpbuf *out OVS_UNUSED)
+{
+ struct nx_action_check_pkt_larger *ncpl = put_OFPAT_CHECK_PKT_LARGER(out);
+ ncpl->pkt_len = htons(check_pkt_larger->pkt_len);
+ ncpl->ofs_nbits = nxm_encode_ofs_nbits(
+ check_pkt_larger->dst.ofs, check_pkt_larger->dst.n_bits);
+ if (check_pkt_larger->dst.field) {
+ ncpl->dst = htonl(nxm_header_from_mff(check_pkt_larger->dst.field));
+ }
+}
+
+static char * OVS_WARN_UNUSED_RESULT
+parse_CHECK_PKT_LARGER(char *arg, const struct ofpact_parse_params *pp)
+{
+ char *value;
+ char *delim;
+ char *key;
+ char *error = set_field_split_str(arg, &key, &value, &delim);
+ if (error) {
+ return error;
+ }
+ delim[0] = '\0';
+ if (value[strlen(value) - 1] ==')') {
+ value[strlen(value) - 1] = '\0';
+ }
+ struct mf_subfield dst;
+ error = mf_parse_subfield(&dst, key);
+ if (error) {
+ return error;
+ }
+
+ struct ofpact_check_pkt_larger *check_pkt_larger =
+ ofpact_put_CHECK_PKT_LARGER(pp->ofpacts);
+ error = str_to_u16(value, NULL, &check_pkt_larger->pkt_len);
+ if (error) {
+ return error;
+ }
+ check_pkt_larger->dst = dst;
+ return NULL;
+}
+
+static void
+format_CHECK_PKT_LARGER(const struct ofpact_check_pkt_larger *a,
+ const struct ofpact_format_params *fp)
+{
+ ds_put_format(fp->s, "%scheck_pkt_larger(%s%"PRIu32")->",
+ colors.param, colors.end, a->pkt_len);
+ mf_format_subfield(&a->dst, fp->s);
+}
+
+static enum ofperr
+check_CHECK_PKT_LARGER(const struct ofpact_check_pkt_larger *a OVS_UNUSED,
+ const struct ofpact_check_params *cp OVS_UNUSED)
+{
+ return 0;
+}
+
+
/* Goto-Table instruction. */
static void
@@ -7686,6 +7780,7 @@ action_set_classify(const struct ofpact *a)
case OFPACT_WRITE_METADATA:
case OFPACT_DEBUG_RECIRC:
case OFPACT_DEBUG_SLOW:
+ case OFPACT_CHECK_PKT_LARGER:
return ACTION_SLOT_INVALID;
default:
@@ -7885,6 +7980,7 @@ ovs_instruction_type_from_ofpact_type(enum ofpact_type type)
case OFPACT_ENCAP:
case OFPACT_DECAP:
case OFPACT_DEC_NSH_TTL:
+ case OFPACT_CHECK_PKT_LARGER:
default:
return OVSINST_OFPIT11_APPLY_ACTIONS;
}
@@ -8755,6 +8851,7 @@ ofpact_outputs_to_port(const struct ofpact *ofpact, ofp_port_t port)
case OFPACT_ENCAP:
case OFPACT_DECAP:
case OFPACT_DEC_NSH_TTL:
+ case OFPACT_CHECK_PKT_LARGER:
default:
return false;
}
@@ -8991,7 +9088,6 @@ ofpacts_parse__(char *str, const struct ofpact_parse_params *pp,
enum ofpact_type type;
char *error = NULL;
ofp_port_t port;
-
if (ofpact_type_from_name(key, &type)) {
error = ofpact_parse(type, value, pp);
inst = ovs_instruction_type_from_ofpact_type(type);
@@ -335,6 +335,16 @@ ofputil_parse_key_value(char **stringp, char **keyp, char **valuep)
char *value = *stringp;
size_t value_len = parse_value(value, value_delims);
char value_delim = value[value_len];
+
+ /* Handle the special case if the value is of the form "(x)->y".
+ * After parsing, 'valuep' will be pointing to - "x)->y".
+ * */
+ if (key_delim == '(' && value[value_len] == ')' &&
+ value[value_len + 1] == '-' && value[value_len + 2] == '>') {
+ value_delims = ", \t\r\n";
+ value_len += parse_value(&value[value_len], value_delims);
+ value_delim = value[value_len];
+ }
value[value_len] = '\0';
*stringp += value_len + (value_delim != '\0');
@@ -3014,6 +3014,7 @@ dpif_ipfix_read_actions(const struct flow *flow,
case OVS_ACTION_ATTR_POP_ETH:
case OVS_ACTION_ATTR_PUSH_NSH:
case OVS_ACTION_ATTR_POP_NSH:
+ case OVS_ACTION_ATTR_CHECK_PKT_LEN:
case OVS_ACTION_ATTR_UNSPEC:
case __OVS_ACTION_ATTR_MAX:
default:
@@ -1222,6 +1222,7 @@ dpif_sflow_read_actions(const struct flow *flow,
case OVS_ACTION_ATTR_PUSH_NSH:
case OVS_ACTION_ATTR_POP_NSH:
case OVS_ACTION_ATTR_UNSPEC:
+ case OVS_ACTION_ATTR_CHECK_PKT_LEN:
case __OVS_ACTION_ATTR_MAX:
default:
break;
@@ -5565,6 +5565,7 @@ reversible_actions(const struct ofpact *ofpacts, size_t ofpacts_len)
case OFPACT_UNROLL_XLATE:
case OFPACT_WRITE_ACTIONS:
case OFPACT_WRITE_METADATA:
+ case OFPACT_CHECK_PKT_LARGER:
break;
case OFPACT_CT:
@@ -5873,6 +5874,7 @@ freeze_unroll_actions(const struct ofpact *a, const struct ofpact *end,
case OFPACT_CT:
case OFPACT_CT_CLEAR:
case OFPACT_NAT:
+ case OFPACT_CHECK_PKT_LARGER:
/* These may not generate PACKET INs. */
break;
@@ -6065,6 +6067,97 @@ compose_ct_clear_action(struct xlate_ctx *ctx)
}
}
+static void
+xlate_check_pkt_larger(struct xlate_ctx *ctx,
+ struct ofpact_check_pkt_larger *check_pkt_larger,
+ const struct ofpact *remaining_acts,
+ size_t remaining_acts_len)
+{
+ if (!check_pkt_larger->dst.field) {
+ return;
+ }
+
+ union mf_subvalue value;
+ memset(&value, 0, sizeof value);
+ if (!ctx->xbridge->support.check_pkt_len) {
+ uint8_t is_pkt_larger = 0;
+ if (ctx->xin->packet) {
+ is_pkt_larger =
+ dp_packet_size(ctx->xin->packet) > check_pkt_larger->pkt_len;
+ }
+ value.u8_val = is_pkt_larger;
+ mf_write_subfield_flow(&check_pkt_larger->dst, &value,
+ &ctx->xin->flow);
+ /* If datapath doesn't support check_pkt_len action, then set the
+ * SLOW_ACTION flag so that ..
+ */
+ ctx->xout->slow |= SLOW_ACTION;
+ return;
+ }
+
+ struct ofpbuf old_stack = ctx->stack;
+ union mf_subvalue new_stack[1024 / sizeof(union mf_subvalue)];
+ ofpbuf_use_stub(&ctx->stack, new_stack, sizeof new_stack);
+ ofpbuf_put(&ctx->stack, old_stack.data, old_stack.size);
+
+ struct ofpbuf old_action_set = ctx->action_set;
+ uint64_t actset_stub[1024 / 8];
+ ofpbuf_use_stub(&ctx->action_set, actset_stub, sizeof actset_stub);
+ ofpbuf_put(&ctx->action_set, old_action_set.data, old_action_set.size);
+
+ struct flow old_flow = ctx->xin->flow;
+ xlate_commit_actions(ctx);
+ struct flow old_base = ctx->base_flow;
+ bool old_was_mpls = ctx->was_mpls;
+ bool old_conntracked = ctx->conntracked;
+
+ size_t offset = nl_msg_start_nested(ctx->odp_actions,
+ OVS_ACTION_ATTR_CHECK_PKT_LEN);
+ nl_msg_put_u16(ctx->odp_actions, OVS_CHECK_PKT_LEN_ATTR_PKT_LEN,
+ check_pkt_larger->pkt_len);
+ size_t offset_attr = nl_msg_start_nested(
+ ctx->odp_actions, OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER);
+ value.u8_val = 1;
+ mf_write_subfield_flow(&check_pkt_larger->dst, &value, &ctx->xin->flow);
+ do_xlate_actions(remaining_acts, remaining_acts_len, ctx, true, false);
+ if (!ctx->freezing) {
+ xlate_action_set(ctx);
+ }
+ if (ctx->freezing) {
+ finish_freezing(ctx);
+ }
+ nl_msg_end_non_empty_nested(ctx->odp_actions, offset_attr);
+
+ ctx->base_flow = old_base;
+ ctx->was_mpls = old_was_mpls;
+ ctx->conntracked = old_conntracked;
+ ctx->xin->flow = old_flow;
+
+ offset_attr = nl_msg_start_nested(
+ ctx->odp_actions, OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL);
+ value.u8_val = 0;
+ mf_write_subfield_flow(&check_pkt_larger->dst, &value, &ctx->xin->flow);
+ do_xlate_actions(remaining_acts, remaining_acts_len, ctx, true, false);
+ if (!ctx->freezing) {
+ xlate_action_set(ctx);
+ }
+ if (ctx->freezing) {
+ finish_freezing(ctx);
+ }
+ nl_msg_end_non_empty_nested(ctx->odp_actions, offset_attr);
+ nl_msg_end_nested(ctx->odp_actions, offset);
+
+ ofpbuf_uninit(&ctx->action_set);
+ ctx->action_set = old_action_set;
+ ofpbuf_uninit(&ctx->stack);
+ ctx->stack = old_stack;
+ ctx->base_flow = old_base;
+ ctx->was_mpls = old_was_mpls;
+ ctx->conntracked = old_conntracked;
+ ctx->xin->flow = old_flow;
+ ctx->exit = true;
+}
+
static void
rewrite_flow_encap_ethernet(struct xlate_ctx *ctx,
struct flow *flow,
@@ -6387,6 +6480,7 @@ recirc_for_mpls(const struct ofpact *a, struct xlate_ctx *ctx)
case OFPACT_WRITE_ACTIONS:
case OFPACT_WRITE_METADATA:
case OFPACT_GOTO_TABLE:
+ case OFPACT_CHECK_PKT_LARGER:
default:
break;
}
@@ -6842,6 +6936,21 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
case OFPACT_DEBUG_SLOW:
ctx->xout->slow |= SLOW_ACTION;
break;
+
+ case OFPACT_CHECK_PKT_LARGER: {
+ if (last) {
+ /* If this is last action, then there is no need to
+ * translate the action. */
+ break;
+ }
+ const struct ofpact *remaining_acts = ofpact_next(a);
+ size_t remaining_acts_len = ofpact_remaining_len(remaining_acts,
+ ofpacts,
+ ofpacts_len);
+ xlate_check_pkt_larger(ctx, ofpact_get_CHECK_PKT_LARGER(a),
+ remaining_acts, remaining_acts_len);
+ break;
+ }
}
/* Check if need to store this and the remaining actions for later
@@ -1290,6 +1290,52 @@ check_ct_clear(struct dpif_backer *backer)
return supported;
}
+
+/* Tests whether 'backer''s datapath supports the
+ * OVS_ACTION_ATTR_CHECK_PKT_LEN action. */
+static bool
+check_check_pkt_len(struct dpif_backer *backer)
+{
+ struct odputil_keybuf keybuf;
+ struct ofpbuf actions;
+ struct ofpbuf key;
+ struct flow flow;
+ bool supported;
+
+ struct odp_flow_key_parms odp_parms = {
+ .flow = &flow,
+ .probe = true,
+ };
+
+ memset(&flow, 0, sizeof flow);
+ ofpbuf_use_stack(&key, &keybuf, sizeof keybuf);
+ odp_flow_key_from_flow(&odp_parms, &key);
+ ofpbuf_init(&actions, 64);
+ size_t cpl_start;
+ size_t nested_action_start;
+ cpl_start = nl_msg_start_nested(&actions, OVS_ACTION_ATTR_CHECK_PKT_LEN);
+ nl_msg_put_u16(&actions, OVS_CHECK_PKT_LEN_ATTR_PKT_LEN, 0);
+
+ nested_action_start = nl_msg_start_nested(
+ &actions, OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER);
+ nl_msg_put_flag(&actions, OVS_ACTION_ATTR_CT_CLEAR);
+ nl_msg_end_nested(&actions, nested_action_start);
+
+ nested_action_start = nl_msg_start_nested(
+ &actions, OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL);
+ nl_msg_put_flag(&actions, OVS_ACTION_ATTR_CT_CLEAR);
+ nl_msg_end_nested(&actions, nested_action_start);
+ nl_msg_end_nested(&actions, cpl_start);
+
+ supported = dpif_probe_feature(backer->dpif, "check_pkt_len", &key,
+ &actions, NULL);
+ ofpbuf_uninit(&actions);
+ VLOG_INFO("%s: Datapath %s check_pkt_len action",
+ dpif_name(backer->dpif), (supported) ? "supports"
+ : "does not support");
+ return supported;
+}
+
/* Probe the highest dp_hash algorithm supported by the datapath. */
static size_t
check_max_dp_hash_alg(struct dpif_backer *backer)
@@ -1397,6 +1443,7 @@ check_support(struct dpif_backer *backer)
backer->rt_support.ct_eventmask = check_ct_eventmask(backer);
backer->rt_support.ct_clear = check_ct_clear(backer);
backer->rt_support.max_hash_alg = check_max_dp_hash_alg(backer);
+ backer->rt_support.check_pkt_len = check_check_pkt_len(backer);
/* Flow fields. */
backer->rt_support.odp.ct_state = check_ct_state(backer);
@@ -192,7 +192,10 @@ struct group_dpif *group_dpif_lookup(struct ofproto_dpif *,
DPIF_SUPPORT_FIELD(bool, ct_clear, "Conntrack clear") \
\
/* Highest supported dp_hash algorithm. */ \
- DPIF_SUPPORT_FIELD(size_t, max_hash_alg, "Max dp_hash algorithm")
+ DPIF_SUPPORT_FIELD(size_t, max_hash_alg, "Max dp_hash algorithm") \
+ \
+ /* True if the datapath supports OVS_ACTION_ATTR_CHECK_PKT_LEN. */ \
+ DPIF_SUPPORT_FIELD(bool, check_pkt_len, "Check pkt length action")
/* Stores the various features which the corresponding backer supports. */
struct dpif_backer_support {