summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorNuman Siddique <nusiddiq@redhat.com>2019-04-23 00:53:38 +0530
committerBen Pfaff <blp@ovn.org>2019-04-22 12:56:50 -0700
commit5b34f8fc3b38b430c05ee39a0c84f8e9da24cd3a (patch)
tree561b6e9457c8f8f3646945210b1f5c64046b049e /lib
parent718be50dae64d108815af0aebb24f214f35a4665 (diff)
downloadopenvswitch-5b34f8fc3b38b430c05ee39a0c84f8e9da24cd3a.tar.gz
Add a new OVS action check_pkt_larger
This patch adds a new action 'check_pkt_larger' which checks if the packet is larger than the given size and stores the result in the destination register. Usage: check_pkt_larger(len)->REGISTER Eg. match=...,actions=check_pkt_larger(1442)->NXM_NX_REG0[0],next; This patch makes use of the new datapath action - 'check_pkt_len' which was recently added in the commit [1]. At the start of ovs-vswitchd, datapath is probed for this action. If the datapath action is present, then 'check_pkt_larger' makes use of this datapath action. Datapath action 'check_pkt_len' takes these nlattrs * OVS_CHECK_PKT_LEN_ATTR_PKT_LEN - 'pkt_len' to check for * OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER (optional) - Nested actions to apply if the packet length is greater than the specified 'pkt_len' * OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL (optional) - Nested actions to apply if the packet length is lesser or equal to the specified 'pkt_len'. Let's say we have these flows added to an OVS bridge br-int table=0, priority=100 in_port=1,ip,actions=check_pkt_larger:100->NXM_NX_REG0[0],resubmit(,1) table=1, priority=200,in_port=1,ip,reg0=0x1/0x1 actions=output:3 table=1, priority=100,in_port=1,ip,actions=output:4 Then the action 'check_pkt_larger' will be translated as - check_pkt_len(size=100,gt(3),le(4)) datapath will check the packet length and if the packet length is greater than 100, it will output to port 3, else it will output to port 4. In case, datapath doesn't support 'check_pkt_len' action, the OVS action 'check_pkt_larger' sets SLOW_ACTION so that datapath flow is not added. This OVS action is intended to be used by OVN to check the packet length and generate an ICMP packet with type 3, code 4 and next hop mtu in the logical router pipeline if the MTU of the physical interface is lesser than the packet length. More information can be found here [2] [1] - https://kernel.googlesource.com/pub/scm/linux/kernel/git/davem/net-next/+/4d5ec89fc8d14dcdab7214a0c13a1c7321dc6ea9 [2] - https://mail.openvswitch.org/pipermail/ovs-discuss/2018-July/047039.html Reported-at: https://mail.openvswitch.org/pipermail/ovs-discuss/2018-July/047039.html Suggested-by: Ben Pfaff <blp@ovn.org> Signed-off-by: Numan Siddique <nusiddiq@redhat.com> CC: Ben Pfaff <blp@ovn.org> CC: Gregory Rose <gvrose8192@gmail.com> Acked-by: Mark Michelson <mmichels@redhat.com> Signed-off-by: Ben Pfaff <blp@ovn.org>
Diffstat (limited to 'lib')
-rw-r--r--lib/dpif-netdev.c1
-rw-r--r--lib/dpif.c1
-rw-r--r--lib/odp-execute.c57
-rw-r--r--lib/odp-util.c86
-rw-r--r--lib/ofp-actions.c126
-rw-r--r--lib/ofp-parse.c10
-rw-r--r--lib/ovs-actions.xml37
7 files changed, 317 insertions, 1 deletions
diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
index 3240f5f76..bd63376d9 100644
--- a/lib/dpif-netdev.c
+++ b/lib/dpif-netdev.c
@@ -7241,6 +7241,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();
}
diff --git a/lib/dpif.c b/lib/dpif.c
index 063ba2052..a18fb1b02 100644
--- a/lib/dpif.c
+++ b/lib/dpif.c
@@ -1266,6 +1266,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();
}
diff --git a/lib/odp-execute.c b/lib/odp-execute.c
index 3ad91547c..563ad1da8 100644
--- a/lib/odp-execute.c
+++ b/lib/odp-execute.c
@@ -674,6 +674,49 @@ 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)
+{
+ static const struct nl_policy ovs_cpl_policy[] = {
+ [OVS_CHECK_PKT_LEN_ATTR_PKT_LEN] = { .type = NL_A_U16 },
+ [OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER] = { .type = NL_A_NESTED },
+ [OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL]
+ = { .type = NL_A_NESTED },
+ };
+ struct nlattr *attrs[ARRAY_SIZE(ovs_cpl_policy)];
+
+ if (!nl_parse_nested(action, ovs_cpl_policy, attrs, ARRAY_SIZE(attrs))) {
+ OVS_NOT_REACHED();
+ }
+
+ const struct nlattr *a;
+ struct dp_packet_batch pb;
+
+ a = attrs[OVS_CHECK_PKT_LEN_ATTR_PKT_LEN];
+ bool 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 (!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 nl_attr_get(a) is NULL, the packet will be freed by
+ * odp_execute_actions. */
+ dp_packet_batch_init_packet(&pb, packet);
+ odp_execute_actions(dp, &pb, true, nl_attr_get(a), nl_attr_get_size(a),
+ dp_execute_action);
+}
+
static bool
requires_datapath_assistance(const struct nlattr *a)
{
@@ -705,6 +748,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:
@@ -932,6 +976,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:
diff --git a/lib/odp-util.c b/lib/odp-util.c
index f60536c1d..1b2347d6f 100644
--- a/lib/odp-util.c
+++ b/lib/odp-util.c
@@ -132,6 +132,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:
@@ -1044,6 +1045,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 OVS_UNUSED)
+{
+ static const struct nl_policy ovs_cpl_policy[] = {
+ [OVS_CHECK_PKT_LEN_ATTR_PKT_LEN] = { .type = NL_A_U16 },
+ [OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER] = { .type = NL_A_NESTED },
+ [OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL]
+ = { .type = NL_A_NESTED },
+ };
+ 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;
+ }
+
+ if (!a[OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER] ||
+ !a[OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL]) {
+ 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, "(size=%u,gt(", pkt_len);
+ const struct nlattr *acts;
+ acts = a[OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER];
+ format_odp_actions(ds, nl_attr_get(acts), nl_attr_get_size(acts),
+ portno_names);
+
+ ds_put_cstr(ds, "),le(");
+ acts = a[OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL];
+ 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,
@@ -1183,6 +1220,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:
@@ -2400,6 +2440,52 @@ parse_odp_action(const char *s, const struct simap *port_names,
}
{
+ uint16_t pkt_len;
+ int n = -1;
+ if (ovs_scan(s, "check_pkt_len(size=%"SCNi16",gt(%n", &pkt_len, &n)) {
+ size_t cpl_ofs, actions_ofs;
+ cpl_ofs = nl_msg_start_nested(actions,
+ OVS_ACTION_ATTR_CHECK_PKT_LEN);
+ nl_msg_put_u16(actions, OVS_CHECK_PKT_LEN_ATTR_PKT_LEN, pkt_len);
+ actions_ofs = nl_msg_start_nested(
+ actions, OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER);
+
+ int retval;
+ if (!strncasecmp(s + n, "drop", 4)) {
+ n += 4;
+ } else {
+ retval = parse_action_list(s + n, port_names, actions);
+ if (retval < 0) {
+ return retval;
+ }
+
+ n += retval;
+ }
+ nl_msg_end_nested(actions, actions_ofs);
+ retval = -1;
+ if (!ovs_scan(s + n, "),le(%n", &retval)) {
+ return -EINVAL;
+ }
+ n += retval;
+
+ actions_ofs = nl_msg_start_nested(
+ actions, OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL);
+ if (!strncasecmp(s + n, "drop", 4)) {
+ n += 4;
+ } else {
+ retval = parse_action_list(s + n, port_names, actions);
+ if (retval < 0) {
+ return retval;
+ }
+ n += retval;
+ }
+ nl_msg_end_nested(actions, actions_ofs);
+ nl_msg_end_nested(actions, cpl_ofs);
+ return s[n + 1] == ')' ? n + 2 : -EINVAL;
+ }
+ }
+
+ {
int retval;
retval = parse_conntrack_action(s, actions);
diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c
index 1340614ec..1a24063d0 100644
--- a/lib/ofp-actions.c
+++ b/lib/ofp-actions.c
@@ -355,6 +355,9 @@ enum ofp_raw_action_type {
/* NX1.3+(48): void. */
NXAST_RAW_DEC_NSH_TTL,
+ /* NX1.0+(49): struct nx_action_check_pkt_larger, ... VLMFF */
+ NXAST_RAW_CHECK_PKT_LARGER,
+
/* ## ------------------ ## */
/* ## Debugging actions. ## */
/* ## ------------------ ## */
@@ -492,6 +495,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:
@@ -7429,6 +7433,124 @@ 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. */
+ ovs_be16 pkt_len; /* Length of the packet to check. */
+ ovs_be16 offset; /* Result bit offset in destination. */
+ /* Followed by:
+ * - 'dst', as an OXM/NXM header (either 4 or 8 bytes).
+ * - Enough 0-bytes to pad the action out to 24 bytes. */
+ uint8_t pad[10];
+};
+
+OFP_ASSERT(sizeof(struct nx_action_check_pkt_larger) == 24);
+
+static enum ofperr
+decode_NXAST_RAW_CHECK_PKT_LARGER(
+ const struct nx_action_check_pkt_larger *ncpl,
+ enum ofp_version ofp_version OVS_UNUSED,
+ const struct vl_mff_map *vl_mff_map, uint64_t *tlv_bitmap,
+ struct ofpbuf *out)
+{
+ struct ofpact_check_pkt_larger *check_pkt_larger;
+ enum ofperr error;
+
+ check_pkt_larger = ofpact_put_CHECK_PKT_LARGER(out);
+ check_pkt_larger->pkt_len = ntohs(ncpl->pkt_len);
+ check_pkt_larger->dst.ofs = ntohs(ncpl->offset);
+ check_pkt_larger->dst.n_bits = 1;
+
+ struct ofpbuf b = ofpbuf_const_initializer(ncpl, ntohs(ncpl->len));
+ ofpbuf_pull(&b, OBJECT_OFFSETOF(ncpl, pad));
+
+ error = mf_vl_mff_nx_pull_header(&b, vl_mff_map,
+ &check_pkt_larger->dst.field,
+ NULL, tlv_bitmap);
+ if (error) {
+ return error;
+ }
+
+ if (!is_all_zeros(b.data, b.size)) {
+ return OFPERR_NXBRC_MUST_BE_ZERO;
+ }
+
+ return mf_check_dst(&check_pkt_larger->dst, NULL);
+}
+
+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)
+{
+ struct nx_action_check_pkt_larger *ncpl = put_NXAST_CHECK_PKT_LARGER(out);
+ ncpl->pkt_len = htons(check_pkt_larger->pkt_len);
+ ncpl->offset = htons(check_pkt_larger->dst.ofs);
+
+ if (check_pkt_larger->dst.field) {
+ size_t size = out->size;
+ out->size = size - sizeof ncpl->pad;
+ nx_put_mff_header(out, check_pkt_larger->dst.field, 0, false);
+ out->size = size;
+ }
+}
+
+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;
+ }
+
+ if (dst.n_bits != 1) {
+ return xstrdup("Only 1-bit destination field is allowed");
+ }
+
+ 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
@@ -7715,6 +7837,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:
@@ -7914,6 +8037,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;
}
@@ -8783,6 +8907,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;
}
@@ -9019,7 +9144,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);
diff --git a/lib/ofp-parse.c b/lib/ofp-parse.c
index a8b5a877c..a90b926ef 100644
--- a/lib/ofp-parse.c
+++ b/lib/ofp-parse.c
@@ -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');
diff --git a/lib/ovs-actions.xml b/lib/ovs-actions.xml
index edf0511c6..cfd9b81be 100644
--- a/lib/ovs-actions.xml
+++ b/lib/ovs-actions.xml
@@ -1453,6 +1453,43 @@ $ ovs-ofctl -O OpenFlow10 add-flow br0 actions=mod_nw_src:1.2.3.4
</p>
</conformance>
</action>
+
+ <action name="CHECK_PKT_LARGER">
+ <h2>The <code>check_pkt_larger</code> action</h2>
+ <syntax>
+ <code>check_pkt_larger(<var>pkt_len</var>)-&gt;<var>dst</var></code>
+ </syntax>
+
+ <p>
+ Checks if the packet is larger than the specified length in
+ <var>pkt_len</var>. If so, stores 1 in <var>dst</var>, which should be
+ a 1-bit field; if not, stores 0.
+ </p>
+
+ <p>
+ The packet length to check againt the argument <var>pkt_len</var>
+ includes the L2 header and L2 payload of the packet, but not the VLAN
+ tag (if present).
+ </p>
+
+ <p>
+ Examples:
+ </p>
+
+ <ul>
+ <li>
+ <code>check_pkt_larger(1500)-&gt;reg0[0]</code>
+ </li>
+
+ <li>
+ <code>check_pkt_larger(8000)-&gt;reg9[10]</code>
+ </li>
+ </ul>
+
+ <p>
+ This action was added in Open vSwitch 2.11.90.
+ </p>
+ </action>
</group>
<group title="Metadata Actions">