@@ -855,6 +855,36 @@ 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_USERSPACE_COND,
+ OVS_CHECK_PKT_LEN_ATTR_USERPACE,
+ __OVS_CHECK_PKT_LEN_ATTR_MAX,
+};
+
+/*
+ * Send the packet to userspace if the packet length is greater than the
+ * specified value in OVS_CHECK_PKT_LEN_ATTR_PKT_LEN attr.
+ */
+#define OVS_CHECK_PKT_LEN_COND_GREATER 1
+
+/*
+ * Send the packet to userspace if the packet length is lesser than
+ * or equal to the specified value in OVS_CHECK_PKT_LEN_ATTR_PKT_LEN attr.
+ */
+#define OVS_CHECK_PKT_LEN_COND_LESSER_EQ 2
+
+#define OVS_CHECK_PKT_LEN_ATTR_MAX (__OVS_CHECK_PKT_LEN_ATTR_MAX - 1)
+
/**
* enum ovs_action_attr - Action types.
*
@@ -935,6 +965,7 @@ enum ovs_action_attr {
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_CHECK_PKT_LEN, /* Nested OVS_CHECK_PKT_LEN_ATTR_*. */
#ifndef __KERNEL__
OVS_ACTION_ATTR_TUNNEL_PUSH, /* struct ovs_action_push_tnl*/
@@ -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. \
* \
@@ -572,6 +574,15 @@ struct ofpact_meter {
uint32_t provider_meter_id;
};
+/* 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. */
@@ -6813,6 +6813,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();
}
@@ -673,6 +673,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 +901,9 @@ odp_execute_actions(void *dp, struct dp_packet_batch *batch, bool steal,
}
break;
+ case OVS_ACTION_ATTR_CHECK_PKT_LEN: {
+ break;
+ }
case OVS_ACTION_ATTR_OUTPUT:
case OVS_ACTION_ATTR_TUNNEL_PUSH:
case OVS_ACTION_ATTR_TUNNEL_POP:
@@ -124,6 +124,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:
@@ -1035,6 +1036,42 @@ 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)
+{
+ static const struct nl_policy ovs_cpl_policy[] = {
+ [OVS_CHECK_PKT_LEN_ATTR_PKT_LEN] = { .type = NL_A_U16 },
+ [OVS_CHECK_PKT_LEN_ATTR_USERSPACE_COND] = { .type = NL_A_U8 },
+ [OVS_CHECK_PKT_LEN_ATTR_USERPACE] = { .type = NL_A_NESTED }
+ };
+ struct nlattr *a[ARRAY_SIZE(ovs_cpl_policy)];
+ const struct nlattr *userspace;
+
+ 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;
+ }
+
+ uint8_t cond = nl_attr_get_u8(a[OVS_CHECK_PKT_LEN_ATTR_USERSPACE_COND]);
+ if (cond != OVS_CHECK_PKT_LEN_COND_GREATER &&
+ cond != OVS_CHECK_PKT_LEN_COND_LESSER_EQ) {
+ ds_put_cstr(ds, "(error)");
+ return;
+ }
+
+ uint16_t pkt_len = nl_attr_get_u8(a[OVS_CHECK_PKT_LEN_ATTR_PKT_LEN]);
+ if (cond == OVS_CHECK_PKT_LEN_COND_GREATER) {
+ ds_put_format(ds, "(> %u ? ", pkt_len);
+ } else {
+ ds_put_format(ds, "(<= %u ? ", pkt_len);
+ }
+
+ userspace = nl_attr_get(a[OVS_CHECK_PKT_LEN_ATTR_USERPACE]);
+ format_odp_userspace_action(ds, userspace, portno_names);
+ ds_put_cstr(ds, ")");
+}
static void
format_odp_action(struct ds *ds, const struct nlattr *a,
@@ -1174,6 +1211,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:
@@ -45,7 +45,9 @@ struct pkt_metadata;
SPR(SLOW_ACTION, "action", \
"Uses action(s) not supported by datapath") \
SPR(SLOW_MATCH, "match", \
- "Datapath can't match specifically enough")
+ "Datapath can't match specifically enough") \
+ SPR(CHECK_PKT_LEN, "check_pkt_len", \
+ "Check packet length condition matched")
/* Indexes for slow-path reasons. Client code uses "enum slow_path_reason"
* values instead of these, these are just a way to construct those. */
@@ -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:
@@ -7399,6 +7402,94 @@ 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';
+ 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
@@ -7685,6 +7776,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:
@@ -7884,6 +7976,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;
}
@@ -8754,6 +8847,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;
}
@@ -8990,7 +9084,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);
@@ -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;
@@ -193,7 +193,8 @@ enum upcall_type {
SFLOW_UPCALL, /* sFlow sample. */
FLOW_SAMPLE_UPCALL, /* Per-flow sampling. */
IPFIX_UPCALL, /* Per-bridge sampling. */
- CONTROLLER_UPCALL /* Destined for the controller. */
+ CONTROLLER_UPCALL, /* Destined for the controller. */
+ CHECK_PKT_LEN_UPCALL /* check packet length condition upcall. */
};
enum reval_result {
@@ -368,9 +369,11 @@ static int ukey_create_from_dpif_flow(const struct udpif *,
struct udpif_key **);
static void ukey_get_actions(struct udpif_key *, const struct nlattr **actions,
size_t *size);
-static bool ukey_install__(struct udpif *, struct udpif_key *ukey)
+static bool ukey_install__(struct udpif *, struct udpif_key *ukey,
+ bool force_replace_old_ukey)
OVS_TRY_LOCK(true, ukey->mutex);
-static bool ukey_install(struct udpif *udpif, struct udpif_key *ukey);
+static bool ukey_install(struct udpif *udpif, struct udpif_key *ukey,
+ bool force_replace_old_ukey);
static void transition_ukey_at(struct udpif_key *ukey, enum ukey_state dst,
const char *where)
OVS_REQUIRES(ukey->mutex);
@@ -1045,6 +1048,9 @@ classify_upcall(enum dpif_upcall_type type, const struct nlattr *userdata,
if (cookie->type == USER_ACTION_COOKIE_SFLOW) {
return SFLOW_UPCALL;
} else if (cookie->type == USER_ACTION_COOKIE_SLOW_PATH) {
+ if (cookie->slow_path.reason == CHECK_PKT_LEN) {
+ return CHECK_PKT_LEN_UPCALL;
+ }
return SLOW_PATH_UPCALL;
} else if (cookie->type == USER_ACTION_COOKIE_FLOW_SAMPLE) {
return FLOW_SAMPLE_UPCALL;
@@ -1117,7 +1123,8 @@ upcall_receive(struct upcall *upcall, const struct dpif_backer *backer,
upcall->type = classify_upcall(type, userdata, &upcall->cookie);
if (upcall->type == BAD_UPCALL) {
return EAGAIN;
- } else if (upcall->type == MISS_UPCALL) {
+ } else if (upcall->type == MISS_UPCALL ||
+ upcall->type == CHECK_PKT_LEN_UPCALL) {
error = xlate_lookup(backer, flow, &upcall->ofproto, &upcall->ipfix,
&upcall->sflow, NULL, &upcall->ofp_in_port);
if (error) {
@@ -1247,7 +1254,7 @@ upcall_xlate(struct udpif *udpif, struct upcall *upcall,
/* This function is also called for slow-pathed flows. As we are only
* going to create new datapath flows for actual datapath misses, there is
* no point in creating a ukey otherwise. */
- if (upcall->type == MISS_UPCALL) {
+ if (upcall->type == MISS_UPCALL || upcall->type == CHECK_PKT_LEN_UPCALL) {
upcall->ukey = ukey_create_from_upcall(upcall, wc);
}
}
@@ -1272,7 +1279,8 @@ upcall_uninit(struct upcall *upcall)
}
}
-/* If there are less flows than the limit, and this is a miss upcall which
+/* If there are less flows than the limit, and this is a miss upcall or
+ * check pkt len upcall which
*
* - Has no recirc_id, OR
* - Has a recirc_id and we can get a reference on the recirc ctx,
@@ -1283,7 +1291,7 @@ should_install_flow(struct udpif *udpif, struct upcall *upcall)
{
unsigned int flow_limit;
- if (upcall->type != MISS_UPCALL) {
+ if (upcall->type != MISS_UPCALL && upcall->type != CHECK_PKT_LEN_UPCALL) {
return false;
} else if (upcall->recirc && !upcall->have_recirc_ref) {
VLOG_DBG_RL(&rl, "upcall: no reference for recirc flow");
@@ -1338,7 +1346,7 @@ upcall_cb(const struct dp_packet *packet, const struct flow *flow, ovs_u128 *ufi
goto out;
}
- if (upcall.ukey && !ukey_install(udpif, upcall.ukey)) {
+ if (upcall.ukey && !ukey_install(udpif, upcall.ukey, false)) {
static struct vlog_rate_limit rll = VLOG_RATE_LIMIT_INIT(1, 1);
VLOG_WARN_RL(&rll, "upcall_cb failure: ukey installation fails");
error = ENOSPC;
@@ -1399,6 +1407,7 @@ dpif_read_actions(struct udpif *udpif, struct upcall *upcall,
case MISS_UPCALL:
case SLOW_PATH_UPCALL:
case CONTROLLER_UPCALL:
+ case CHECK_PKT_LEN_UPCALL:
default:
break;
}
@@ -1417,6 +1426,7 @@ process_upcall(struct udpif *udpif, struct upcall *upcall,
switch (upcall->type) {
case MISS_UPCALL:
case SLOW_PATH_UPCALL:
+ case CHECK_PKT_LEN_UPCALL:
upcall_xlate(udpif, upcall, odp_actions, wc);
return 0;
@@ -1583,8 +1593,9 @@ handle_upcalls(struct udpif *udpif, struct upcall *upcalls,
if (should_install_flow(udpif, upcall)) {
struct udpif_key *ukey = upcall->ukey;
-
- if (ukey_install(udpif, ukey)) {
+ bool force_replace_old_ukey =
+ upcall->type == CHECK_PKT_LEN_UPCALL ? true : false;
+ if (ukey_install(udpif, ukey, force_replace_old_ukey)) {
upcall->ukey_persists = true;
put_op_init(&ops[n_ops++], ukey, DPIF_FP_CREATE);
}
@@ -1813,14 +1824,14 @@ ukey_create_from_dpif_flow(const struct udpif *udpif,
static bool
try_ukey_replace(struct umap *umap, struct udpif_key *old_ukey,
- struct udpif_key *new_ukey)
+ struct udpif_key *new_ukey, bool force_replace_old_ukey)
OVS_REQUIRES(umap->mutex)
OVS_TRY_LOCK(true, new_ukey->mutex)
{
bool replaced = false;
if (!ovs_mutex_trylock(&old_ukey->mutex)) {
- if (old_ukey->state == UKEY_EVICTED) {
+ if (old_ukey->state == UKEY_EVICTED || force_replace_old_ukey) {
/* The flow was deleted during the current revalidator dump,
* but its ukey won't be fully cleaned up until the sweep phase.
* In the mean time, we are receiving upcalls for this traffic.
@@ -1849,7 +1860,8 @@ try_ukey_replace(struct umap *umap, struct udpif_key *old_ukey,
* On success, returns true, installs the ukey and returns it in a locked
* state. Otherwise, returns false. */
static bool
-ukey_install__(struct udpif *udpif, struct udpif_key *new_ukey)
+ukey_install__(struct udpif *udpif, struct udpif_key *new_ukey,
+ bool force_replace_old_ukey)
OVS_TRY_LOCK(true, new_ukey->mutex)
{
struct umap *umap;
@@ -1865,7 +1877,8 @@ ukey_install__(struct udpif *udpif, struct udpif_key *new_ukey)
/* Uncommon case: A ukey is already installed with the same UFID. */
if (old_ukey->key_len == new_ukey->key_len
&& !memcmp(old_ukey->key, new_ukey->key, new_ukey->key_len)) {
- locked = try_ukey_replace(umap, old_ukey, new_ukey);
+ locked = try_ukey_replace(umap, old_ukey, new_ukey,
+ force_replace_old_ukey);
} else {
struct ds ds = DS_EMPTY_INITIALIZER;
@@ -1940,11 +1953,12 @@ transition_ukey_at(struct udpif_key *ukey, enum ukey_state dst,
}
static bool
-ukey_install(struct udpif *udpif, struct udpif_key *ukey)
+ukey_install(struct udpif *udpif, struct udpif_key *ukey,
+ bool force_replace_old_ukey)
{
bool installed;
- installed = ukey_install__(udpif, ukey);
+ installed = ukey_install__(udpif, ukey, force_replace_old_ukey);
if (installed) {
ovs_mutex_unlock(&ukey->mutex);
}
@@ -1985,7 +1999,7 @@ ukey_acquire(struct udpif *udpif, const struct dpif_flow *flow,
if (retval) {
goto done;
}
- install = ukey_install__(udpif, ukey);
+ install = ukey_install__(udpif, ukey, false);
if (install) {
retval = 0;
} else {
@@ -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,51 @@ 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)
+{
+ if (!ctx->xin->packet || !check_pkt_larger->dst.field) {
+ return;
+ }
+
+ union mf_subvalue value;
+ memset(&value, 0, sizeof value);
+ value.u8_val =
+ dp_packet_size(ctx->xin->packet) > check_pkt_larger->pkt_len;
+ mf_write_subfield_flow(&check_pkt_larger->dst, &value, &ctx->xin->flow);
+ if (ctx->xbridge->support.check_pkt_len) {
+ 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);
+ uint8_t condition = OVS_CHECK_PKT_LEN_COND_GREATER;
+ if (dp_packet_size(ctx->xin->packet) > check_pkt_larger->pkt_len) {
+ condition = OVS_CHECK_PKT_LEN_COND_LESSER_EQ;
+ }
+ nl_msg_put_u8(ctx->odp_actions, OVS_CHECK_PKT_LEN_ATTR_USERSPACE_COND,
+ condition);
+
+ struct user_action_cookie cookie = {
+ .type = USER_ACTION_COOKIE_SLOW_PATH,
+ .ofp_in_port = ctx->xin->flow.in_port.ofp_port,
+ .ofproto_uuid = ctx->xbridge->ofproto->uuid,
+ .slow_path.reason = CHECK_PKT_LEN,
+ };
+ odp_port_t odp_port = ofp_port_to_odp_port(
+ ctx->xbridge, ctx->xin->flow.in_port.ofp_port);
+ uint32_t pid = dpif_port_get_pid(ctx->xbridge->dpif, odp_port);
+ size_t userspace_offset = nl_msg_start_nested(
+ ctx->odp_actions, OVS_CHECK_PKT_LEN_ATTR_USERPACE);
+ odp_put_userspace_action(pid, &cookie, sizeof cookie, ODPP_NONE,
+ false, ctx->odp_actions);
+ nl_msg_end_nested(ctx->odp_actions, userspace_offset);
+ nl_msg_end_nested(ctx->odp_actions, offset);
+ } else {
+ ctx->xout->slow |= SLOW_ACTION;
+ }
+}
+
static void
rewrite_flow_encap_ethernet(struct xlate_ctx *ctx,
struct flow *flow,
@@ -6387,6 +6434,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 +6890,10 @@ 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:
+ xlate_check_pkt_larger(ctx, ofpact_get_CHECK_PKT_LARGER(a));
+ break;
}
/* Check if need to store this and the remaining actions for later
@@ -1290,6 +1290,51 @@ 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;
+ 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);
+ nl_msg_put_u8(&actions, OVS_CHECK_PKT_LEN_ATTR_USERSPACE_COND, 1);
+ size_t cpl_userspace_offset = nl_msg_start_nested(
+ &actions, OVS_CHECK_PKT_LEN_ATTR_USERPACE);
+ struct user_action_cookie cookie = {
+ .type = USER_ACTION_COOKIE_SLOW_PATH,
+ .ofp_in_port = 0,
+ };
+ odp_put_userspace_action(1, &cookie, sizeof cookie, ODPP_NONE,
+ false, &actions);
+ nl_msg_end_nested(&actions, cpl_userspace_offset);
+ 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 +1442,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 {