From patchwork Fri Apr 12 13:05:28 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Numan Siddique X-Patchwork-Id: 1084733 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=openvswitch.org (client-ip=140.211.169.12; helo=mail.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=redhat.com Received: from mail.linuxfoundation.org (mail.linuxfoundation.org [140.211.169.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 44gdRs1Zp1z9s5c for ; Fri, 12 Apr 2019 23:05:45 +1000 (AEST) Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id 9454B11CA; Fri, 12 Apr 2019 13:05:42 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@mail.linuxfoundation.org Received: from smtp1.linuxfoundation.org (smtp1.linux-foundation.org [172.17.192.35]) by mail.linuxfoundation.org (Postfix) with ESMTPS id 4C5241192 for ; Fri, 12 Apr 2019 13:05:41 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.7.6 Received: from mx1.redhat.com (mx1.redhat.com [209.132.183.28]) by smtp1.linuxfoundation.org (Postfix) with ESMTPS id 7BFE1855 for ; Fri, 12 Apr 2019 13:05:37 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.phx2.redhat.com [10.5.11.23]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 1675D36809; Fri, 12 Apr 2019 13:05:37 +0000 (UTC) Received: from nusiddiq.mac (unknown [10.74.10.41]) by smtp.corp.redhat.com (Postfix) with ESMTP id 0357719C6F; Fri, 12 Apr 2019 13:05:33 +0000 (UTC) From: nusiddiq@redhat.com To: dev@openvswitch.org Date: Fri, 12 Apr 2019 18:35:28 +0530 Message-Id: <20190412130528.28429-1-nusiddiq@redhat.com> In-Reply-To: <20190412130447.28304-1-nusiddiq@redhat.com> References: <20190412130447.28304-1-nusiddiq@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.84 on 10.5.11.23 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.30]); Fri, 12 Apr 2019 13:05:37 +0000 (UTC) X-Spam-Status: No, score=-6.9 required=5.0 tests=BAYES_00,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on smtp1.linux-foundation.org Subject: [ovs-dev] [PATCH v2 1/6] Add a new OVS action check_pkt_larger X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: ovs-dev-bounces@openvswitch.org Errors-To: ovs-dev-bounces@openvswitch.org From: Numan Siddique 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 Signed-off-by: Numan Siddique CC: Ben Pfaff Acked-by: Mark Michelson --- NEWS | 2 + .../linux/compat/include/linux/openvswitch.h | 22 +++ include/openvswitch/ofp-actions.h | 19 ++ lib/dpif-netdev.c | 1 + lib/dpif.c | 1 + lib/odp-execute.c | 72 ++++++++ lib/odp-util.c | 86 +++++++++ lib/ofp-actions.c | 121 ++++++++++++- lib/ofp-parse.c | 10 ++ lib/ovs-actions.xml | 31 ++++ ofproto/ofproto-dpif-ipfix.c | 1 + ofproto/ofproto-dpif-sflow.c | 1 + ofproto/ofproto-dpif-xlate.c | 131 ++++++++++++++ ofproto/ofproto-dpif.c | 43 +++++ ofproto/ofproto-dpif.h | 5 +- tests/odp.at | 5 + tests/ofp-actions.at | 6 + tests/ofproto-dpif.at | 163 ++++++++++++++++++ 18 files changed, 718 insertions(+), 2 deletions(-) diff --git a/NEWS b/NEWS index 1e4744dbd..7a9b3aeec 100644 --- a/NEWS +++ b/NEWS @@ -5,6 +5,7 @@ Post-v2.11.0 hugepage memory that can be used by DPDK. - OpenFlow: * Removed support for OpenFlow 1.6 (draft), which ONF abandoned. + * New action "check_pkt_larger". - Userspace datapath: * ICMPv6 ND enhancements: support for match and set ND options type and reserved fields. @@ -17,6 +18,7 @@ Post-v2.11.0 conntrack fragmentation support. * New "ovs-appctl dpctl/ipf-get-status" command for userspace datapath conntrack fragmentation support. + * New action "check_pkt_len". - OVSDB: * OVSDB clients can now resynchronize with clustered servers much more quickly after a brief disconnection, saving bandwidth and CPU time. diff --git a/datapath/linux/compat/include/linux/openvswitch.h b/datapath/linux/compat/include/linux/openvswitch.h index d5aa09d8d..ce364e577 100644 --- a/datapath/linux/compat/include/linux/openvswitch.h +++ b/datapath/linux/compat/include/linux/openvswitch.h @@ -863,6 +863,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. * @@ -919,6 +937,9 @@ enum ovs_nat_attr { * 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. + * @OVS_ACTION_ATTR_CHECK_PKT_LEN: Check the packet length and execute a set + * of actions if greater than the specified packet length, else execute + * another set of actions. */ enum ovs_action_attr { @@ -946,6 +967,7 @@ enum ovs_action_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_CHECK_PKT_LEN, /* Nested OVS_CHECK_PKT_LEN_ATTR_*. */ #ifndef __KERNEL__ OVS_ACTION_ATTR_TUNNEL_PUSH, /* struct ovs_action_push_tnl*/ diff --git a/include/openvswitch/ofp-actions.h b/include/openvswitch/ofp-actions.h index 14c5eab74..436c4aadf 100644 --- a/include/openvswitch/ofp-actions.h +++ b/include/openvswitch/ofp-actions.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 size_t +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,16 @@ struct ofpact_meter { ); }; +/* OFPACT_CHECK_PKT_LARGER. + * + * Used for NXAST_CHECK_PKT_LARGER. */ +struct ofpact_check_pkt_larger { + OFPACT_PADDED_MEMBERS( + struct ofpact ofpact; + struct mf_subfield dst; + uint16_t pkt_len; + ); +}; /* OFPACT_WRITE_ACTIONS, OFPACT_CLONE. * * Used for OFPIT11_WRITE_ACTIONS, NXAST_CLONE. */ diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c index 4d6d0c372..f906a3de3 100644 --- a/lib/dpif-netdev.c +++ b/lib/dpif-netdev.c @@ -7249,6 +7249,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..d3b52c110 100644 --- a/lib/odp-execute.c +++ b/lib/odp-execute.c @@ -674,6 +674,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) { @@ -705,6 +763,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 +991,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 b6552c5c2..1a9a1b163 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: @@ -2399,6 +2439,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; diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c index 1340614ec..eff469a6a 100644 --- a/lib/ofp-actions.c +++ b/lib/ofp-actions.c @@ -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, ... VLMFF */ + NXAST_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: @@ -7429,6 +7432,120 @@ 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 ofs_nbits; /* (ofs << 6) | (n_bits - 1). */ + /* 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 = nxm_decode_ofs(ncpl->ofs_nbits); + check_pkt_larger->dst.n_bits = nxm_decode_n_bits(ncpl->ofs_nbits); + + 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->ofs_nbits = nxm_encode_ofs_nbits( + check_pkt_larger->dst.ofs, check_pkt_larger->dst.n_bits); + 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; + } + + 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 +7832,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 +8032,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 +8902,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 +9139,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..85a48917b 100644 --- a/lib/ovs-actions.xml +++ b/lib/ovs-actions.xml @@ -1453,6 +1453,37 @@ $ ovs-ofctl -O OpenFlow10 add-flow br0 actions=mod_nw_src:1.2.3.4

+ + +

The check_pkt_larger action

+ + check_pkt_larger(pkt_len)->dst + + +

+ Checks if the packet is larger than the specified length in + pkt_len and stores the result 1 in the + OpenFlow register field 1-bit specified in dst. +

+ +

+ Examples: +

+ +
    +
  • + check_pkt_larger(1500)->reg0[0] +
  • + +
  • + check_pkt_larger(8000)->reg9[10] +
  • +
+ +

+ This action was added in Open vSwitch 2.11.90. +

+
diff --git a/ofproto/ofproto-dpif-ipfix.c b/ofproto/ofproto-dpif-ipfix.c index 5ea1097f9..b8bd1b814 100644 --- a/ofproto/ofproto-dpif-ipfix.c +++ b/ofproto/ofproto-dpif-ipfix.c @@ -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: diff --git a/ofproto/ofproto-dpif-sflow.c b/ofproto/ofproto-dpif-sflow.c index 22cbdae7a..03bd763c6 100644 --- a/ofproto/ofproto-dpif-sflow.c +++ b/ofproto/ofproto-dpif-sflow.c @@ -1223,6 +1223,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; diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c index c4014d71b..5cee37f7b 100644 --- a/ofproto/ofproto-dpif-xlate.c +++ b/ofproto/ofproto-dpif-xlate.c @@ -487,6 +487,7 @@ ctx_cancel_freeze(struct xlate_ctx *ctx) ctx->recirc_update_dp_hash = false; ofpbuf_clear(&ctx->frozen_actions); ctx->frozen_actions.header = NULL; + ctx->pause = NULL; } } @@ -5584,6 +5585,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: @@ -5892,6 +5894,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; @@ -6084,6 +6087,118 @@ compose_ct_clear_action(struct xlate_ctx *ctx) } } +/* check_pkt_larger action checks the packet length and stores the + * result in the register bit. We translate this action to the + * datapath action - 'check_pkt_len' whose format + * is: 'check_pkt_len(pkt_len, ge(actions), le(actions))'. + * + * We first set the destination register bit to 1 and call + * 'do_xlate_actions' for the case - packet len greater than + * the specified packet length. + * + * We then set the destination register bit to 0 and call + * 'do_xlate_actions' for the case - packet length is lesser or + * equal to the specified packet length. + * + * It is possible for freezing to happen for both the cases. + */ +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) +{ + 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. If we don't set SLOW_ACTION, we + * will push a flow to the datapath based on the packet length + * in ctx->xin->packet. For subsequent patches which match the + * same flow, datapath will apply the actions without considering + * the packet length. This results in wrong actions being applied. + */ + 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_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; + + /* If the flow translation for the IF_GREATER case requires freezing, + * then ctx->exit would be true. Reset to false so that we can + * do flow translation for 'IF_LESS_EQUAL' case. finish_freezing() + * would have taken care of Undoing the changes done for freeze. */ + ctx->exit = false; + + 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_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, @@ -6406,6 +6521,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; } @@ -6861,6 +6977,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 diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c index f0d387ccd..7722bbed5 100644 --- a/ofproto/ofproto-dpif.c +++ b/ofproto/ofproto-dpif.c @@ -1290,6 +1290,48 @@ 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, 100); + + /* Putting these actions without any data is good enough to check + * if check_pkt_len is supported or not. */ + nl_msg_put_flag(&actions, OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER); + nl_msg_put_flag(&actions, OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL); + + 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 +1439,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); diff --git a/ofproto/ofproto-dpif.h b/ofproto/ofproto-dpif.h index 1a404c82f..cd5321eb9 100644 --- a/ofproto/ofproto-dpif.h +++ b/ofproto/ofproto-dpif.h @@ -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 { diff --git a/tests/odp.at b/tests/odp.at index f92f989ca..3173e30a9 100644 --- a/tests/odp.at +++ b/tests/odp.at @@ -107,6 +107,7 @@ sed -i'back' 's/\(in_port(1)\),\(eth\)/\1,packet_type(ns=0,id=0),\2/' odp-out.tx AT_CHECK_UNQUOTED([ovstest test-odp parse-keys < odp-in.txt], [0], [`cat odp-out.txt` ]) + AT_CLEANUP AT_SETUP([OVS datapath wildcarded key parsing and formatting - valid forms]) @@ -378,6 +379,10 @@ clone(1) clone(clone(push_vlan(vid=12,pcp=0),2),1) set(tunnel(tun_id=0x1,dst=1.1.1.1,ttl=64,erspan(ver=1,idx=0x7),flags(df|key))) set(tunnel(tun_id=0x1,dst=1.1.1.1,ttl=64,erspan(ver=2,dir=1,hwid=0x1),flags(df|key))) +check_pkt_len(size=200,gt(4),le(5)) +check_pkt_len(size=200,gt(drop),le(5)) +check_pkt_len(size=200,gt(ct(nat)),le(drop)) +check_pkt_len(size=200,gt(set(eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15))),le(set(eth(src=00:01:02:03:04:06,dst=10:11:12:13:14:16)))) ]) AT_CHECK_UNQUOTED([ovstest test-odp parse-actions < actions.txt], [0], [`cat actions.txt` diff --git a/tests/ofp-actions.at b/tests/ofp-actions.at index 38c2b5cbd..2641891d9 100644 --- a/tests/ofp-actions.at +++ b/tests/ofp-actions.at @@ -310,6 +310,12 @@ ffff 0018 00002320 0025 0000 0005 5000 1122334455 000000 ffff 0048 00002320 0023 0001 00000000 0000 FF 000000 0000 dnl ffff 0030 00002320 0024 00 00 0011 000c fe800000 00000000 020c 29ff fe88 a18b fe800000 00000000 020c 29ff fe88 0001 +# actions=check_pkt_larger(1500)->NXM_NX_REG0[0] +ffff 0018 00002320 0031 05dc 000000010004000000000000 + +# actions=check_pkt_larger(1000)->NXM_NX_XXREG1[4] +ffff 0018 00002320 0031 03e8 01000001e010000000000000 + ]) sed '/^[[#&]]/d' < test-data > input.txt sed -n 's/^# //p; /^$/p' < test-data > expout diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at index 4aad14320..9b0231b88 100644 --- a/tests/ofproto-dpif.at +++ b/tests/ofproto-dpif.at @@ -10503,3 +10503,166 @@ AT_CHECK([grep flow_del ovs-vswitchd.log], [1]) OVS_VSWITCHD_STOP AT_CLEANUP + +AT_SETUP([ofproto-dpif - check_pkt_larger action]) +OVS_VSWITCHD_START +add_of_ports br0 1 2 3 4 + +AT_DATA([flows.txt], [dnl +table=0,in_port=1 actions=check_pkt_larger(200)->NXM_NX_REG0[[0]],resubmit(,1) +table=1,in_port=1,reg0=0x1/0x1 actions=output:2,resubmit(,2) +table=1,in_port=1,actions=output:3,resubmit(,2) +table=2,in_port=1,actions=mod_dl_dst:82:82:82:82:82:82,output:4 +]) + +AT_CHECK([ovs-ofctl --protocols=OpenFlow10 add-flows br0 flows.txt]) +AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.10.10.2,dst=10.10.10.1,proto=1,tos=1,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout]) +AT_CHECK([tail -1 stdout], [0], [dnl +Datapath actions: check_pkt_len(size=200,gt(2,set(eth(dst=82:82:82:82:82:82)),4),le(3,set(eth(dst=82:82:82:82:82:82)),4)) +]) + +dnl Test flow xlate check_pkt_large clone action without using datapath check_pkt_len action. +AT_CHECK([ovs-appctl dpif/set-dp-features br0 check_pkt_len false], [0], [ignore]) + +AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.10.10.2,dst=10.10.10.1,proto=1,tos=1,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout]) +AT_CHECK([tail -3 stdout], [0], [dnl +Datapath actions: 3,set(eth(dst=82:82:82:82:82:82)),4 +This flow is handled by the userspace slow path because it: + - Uses action(s) not supported by datapath. +]) + +dnl Enable datapath check_pkt_len action +AT_CHECK([ovs-appctl dpif/set-dp-features br0 check_pkt_len true], [0], [ignore]) + +ovs-ofctl del-flows br0 + +AT_DATA([flows.txt], [dnl +table=0,in_port=1 actions=check_pkt_larger(200)->NXM_NX_REG0[[0]],resubmit(,1) +table=1,in_port=1,priority=200,reg0=0x1/0x1 actions=output:2 +]) + +AT_CHECK([ovs-ofctl --protocols=OpenFlow10 add-flows br0 flows.txt]) +AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.10.10.2,dst=10.10.10.1,proto=1,tos=1,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout]) +AT_CHECK([tail -1 stdout], [0], [dnl +Datapath actions: check_pkt_len(size=200,gt(2),le(drop)) +]) + +ovs-ofctl del-flows br0 +AT_DATA([flows.txt], [dnl +table=0,in_port=1 actions=check_pkt_larger(200)->NXM_NX_REG0[[0]] +]) + +AT_CHECK([ovs-ofctl --protocols=OpenFlow10 add-flows br0 flows.txt]) +AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.10.10.2,dst=10.10.10.1,proto=1,tos=1,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout]) +AT_CHECK([tail -1 stdout], [0], [dnl +Datapath actions: check_pkt_len(size=200,gt(drop),le(drop)) +]) + +ovs-ofctl del-flows br0 +AT_DATA([flows.txt], [dnl +table=0,in_port=1 actions=check_pkt_larger(200)->NXM_NX_REG0[[0]],resubmit(,1) +table=1,in_port=1,priority=200,reg0=0x1/0x1,ip actions=clone(set_field:192.168.3.3->ip_src),clone(set_field:192.168.4.4->ip_dst,output:2),clone(mod_dl_src:80:81:81:81:81:81,set_field:192.168.5.5->ip_dst,output:3),output:4 +table=1,in_port=1,priority=0,ip actions=clone(set_field:192.168.3.3->ip_src),clone(set_field:192.168.4.4->ip_dst,output:2),clone(ct(commit),output:3),output:4 +]) + +AT_CHECK([ovs-ofctl --protocols=OpenFlow10 add-flows br0 flows.txt]) +AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.10.10.2,dst=10.10.10.1,proto=1,tos=1,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout]) +AT_CHECK([tail -1 stdout], [0], [dnl +Datapath actions: check_pkt_len(size=200,gt(set(ipv4(dst=192.168.4.4)),2,set(eth(src=80:81:81:81:81:81)),set(ipv4(dst=192.168.5.5)),3,set(eth(src=50:54:00:00:00:09)),set(ipv4(dst=10.10.10.1)),4),le(set(ipv4(dst=192.168.4.4)),2,set(ipv4(dst=10.10.10.1)),clone(ct(commit),3),4)) +]) + +AT_DATA([flows.txt], [dnl +table=0,priority=0 actions=check_pkt_larger(200)->NXM_NX_REG0[[0]],resubmit(,1) +table=1,in_port=1,priority=200,reg0=0x1/0x1,ip actions=clone(set_field:192.168.3.3->ip_src, resubmit(,0)) +table=1,in_port=1,priority=0,ip actions=clone(set_field:192.168.3.4->ip_src, resubmit(,0)) +]) + +AT_CHECK([ovs-ofctl --protocols=OpenFlow10 add-flows br0 flows.txt]) +ovs-ofctl dump-flows br0 + +AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.10.10.2,dst=10.10.10.1,proto=1,tos=1,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout]) +AT_CHECK([tail -3 stdout], [0], [dnl +Megaflow: recirc_id=0,eth,ip,reg0=0/0x1,in_port=1,nw_src=10.10.10.2,nw_frag=no +Datapath actions: drop +Translation failed (Recursion too deep), packet is dropped. +]) + +ovs-ofctl del-flows br0 +AT_DATA([flows.txt], [dnl +table=0,priority=0 actions=check_pkt_larger(200)->NXM_NX_REG0[[0]],resubmit(,1) +table=1,ip,nw_src=192.168.3.3 actions=output:3 +table=1,ip,nw_src=192.168.3.4 actions=output:4 +table=1,reg0=0x1/0x1,ip actions=clone(set_field:192.168.3.3->ip_src, resubmit(,0)) +table=1,ip actions=clone(set_field:192.168.3.4->ip_src, resubmit(,0)) +]) + +AT_CHECK([ovs-ofctl --protocols=OpenFlow10 add-flows br0 flows.txt]) +ovs-ofctl dump-flows br0 + +AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.10.10.2,dst=10.10.10.1,proto=1,tos=1,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout]) +AT_CHECK([tail -1 stdout], [0], [dnl +Datapath actions: check_pkt_len(size=200,gt(set(ipv4(src=192.168.3.3)),check_pkt_len(size=200,gt(3),le(3))),le(set(ipv4(src=192.168.3.4)),check_pkt_len(size=200,gt(4),le(4)))) +]) + +ovs-ofctl del-flows br0 +AT_DATA([flows.txt], [dnl +table=0,in_port=1 actions=check_pkt_larger(200)->NXM_NX_REG0[[0]],resubmit(,1) +table=1,in_port=1,reg0=0x1/0x1 actions=mod_dl_dst:82:82:82:82:82:82,controller(),resubmit(,2) +table=1,in_port=1 actions=resubmit(,2) +table=2,ip,dl_dst=82:82:82:82:82:82 actions=ct(table=3) +table=2,ip,dl_dst=50:54:00:00:00:0a actions=ct(table=3) +table=3,ip,reg0=0x1/0x1 actions=output:2 +table=3,ip actions=output:4 +]) + +AT_CHECK([ovs-ofctl --protocols=OpenFlow10 add-flows br0 flows.txt]) +AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.10.10.2,dst=10.10.10.1,proto=1,tos=1,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout]) +AT_CHECK([cat stdout | grep Datapath -B1], [0], [dnl +Megaflow: recirc_id=0,eth,ip,in_port=1,dl_dst=50:54:00:00:00:0a,nw_frag=no +Datapath actions: check_pkt_len(size=200,gt(set(eth(dst=82:82:82:82:82:82)),userspace(pid=0,controller(reason=1,dont_send=1,continuation=0,recirc_id=1,rule_cookie=0,controller_id=0,max_len=65535)),ct,recirc(0x2)),le(ct,recirc(0x3))) +-- +Megaflow: recirc_id=0x2,eth,ip,in_port=1,nw_frag=no +Datapath actions: 2 +-- +Megaflow: recirc_id=0x3,eth,ip,in_port=1,nw_frag=no +Datapath actions: 4 +]) + +OVS_VSWITCHD_STOP +AT_CLEANUP + +AT_SETUP([ofproto-dpif - check_pkt_larger with continuation and ct]) +OVS_VSWITCHD_START +add_of_ports --pcap br0 `seq 1 4` + +AT_CAPTURE_FILE([ofctl_monitor0.log]) +AT_CHECK([ovs-ofctl monitor br0 resume --detach --no-chdir --pidfile=ovs-ofctl0.pid 2> ofctl_monitor0.log]) + +AT_DATA([flows.txt], [dnl +table=0,in_port=1 actions=check_pkt_larger(150)->NXM_NX_REG0[[0]],resubmit(,1) +table=1,ip,reg0=0x1/0x1 actions=mod_dl_dst:82:82:82:82:82:82,controller(pause),resubmit(,2) +table=1,ip,reg0=0 actions=mod_dl_dst:83:83:83:83:83:83,controller(pause),resubmit(,2) +table=2,ip,dl_dst=82:82:82:82:82:82 actions=ct(table=3) +table=2,ip,dl_dst=83:83:83:83:83:83 actions=ct(table=3) +table=3,ip,reg0=0x1/0x1 actions=ct(commit),output:2 +table=3,ip actions=ct(commit),output:4 +]) + +AT_CHECK([ovs-ofctl --protocols=OpenFlow10 add-flows br0 flows.txt]) + +flow="in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.10.10.2,dst=10.10.10.1,proto=1,tos=1,ttl=128,frag=no),icmp(type=8,code=0)" + +AT_CHECK([ovs-appctl netdev-dummy/receive p1 "$flow"], [0], [stdout]) + +OVS_WAIT_UNTIL([test 1 = `ovs-ofctl parse-pcap p4-tx.pcap \ +| grep dl_dst=83:83:83:83:83:83 | wc -l`]) +AT_CHECK([test 0 = `ovs-ofctl parse-pcap p2-tx.pcap | wc -l`]) + +AT_CHECK([ovs-appctl netdev-dummy/receive p1 "$flow" --len 200], [0], [stdout]) + +OVS_WAIT_UNTIL([test 1 = `ovs-ofctl parse-pcap p2-tx.pcap \ +| grep dl_dst=82:82:82:82:82:82 | wc -l`]) +AT_CHECK([test 1 = `ovs-ofctl parse-pcap p2-tx.pcap | wc -l`]) + +OVS_VSWITCHD_STOP +AT_CLEANUP From patchwork Fri Apr 12 13:05:37 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Numan Siddique X-Patchwork-Id: 1084734 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=openvswitch.org (client-ip=140.211.169.12; helo=mail.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=redhat.com Received: from mail.linuxfoundation.org (mail.linuxfoundation.org [140.211.169.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 44gdSq3XSwz9s5c for ; Fri, 12 Apr 2019 23:06:35 +1000 (AEST) Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id A0EDF1205; Fri, 12 Apr 2019 13:05:47 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@mail.linuxfoundation.org Received: from smtp1.linuxfoundation.org (smtp1.linux-foundation.org [172.17.192.35]) by mail.linuxfoundation.org (Postfix) with ESMTPS id BA2E211FB for ; Fri, 12 Apr 2019 13:05:46 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.7.6 Received: from mx1.redhat.com (mx1.redhat.com [209.132.183.28]) by smtp1.linuxfoundation.org (Postfix) with ESMTPS id 061AB76D for ; Fri, 12 Apr 2019 13:05:42 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.phx2.redhat.com [10.5.11.14]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 94E5AC062ECD for ; Fri, 12 Apr 2019 13:05:42 +0000 (UTC) Received: from nusiddiq.mac (unknown [10.74.10.41]) by smtp.corp.redhat.com (Postfix) with ESMTP id 1DF635D9CD; Fri, 12 Apr 2019 13:05:40 +0000 (UTC) From: nusiddiq@redhat.com To: dev@openvswitch.org Date: Fri, 12 Apr 2019 18:35:37 +0530 Message-Id: <20190412130537.28478-1-nusiddiq@redhat.com> In-Reply-To: <20190412130447.28304-1-nusiddiq@redhat.com> References: <20190412130447.28304-1-nusiddiq@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.14 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.31]); Fri, 12 Apr 2019 13:05:42 +0000 (UTC) X-Spam-Status: No, score=-6.9 required=5.0 tests=BAYES_00,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on smtp1.linux-foundation.org Subject: [ovs-dev] [PATCH v2 2/6] datapath: Add a new action check_pkt_len X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: ovs-dev-bounces@openvswitch.org Errors-To: ovs-dev-bounces@openvswitch.org From: Numan Siddique Upstream commit: commit 4d5ec89fc8d14dcdab7214a0c13a1c7321dc6ea9 Author: Numan Siddique Date: Tue Mar 26 06:13:46 2019 +0530 net: openvswitch: Add a new action check_pkt_len This patch adds a new action - 'check_pkt_len' which checks the packet length and executes a set of actions if the packet length is greater than the specified length or executes another set of actions if the packet length is lesser or equal to. This action takes below nlattrs * OVS_CHECK_PKT_LEN_ATTR_PKT_LEN - 'pkt_len' to check for * OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER - Nested actions to apply if the packet length is greater than the specified 'pkt_len' * OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL - Nested actions to apply if the packet length is lesser or equal to the specified 'pkt_len'. The main use case for adding this action is to solve the packet drops because of MTU mismatch in OVN virtual networking solution. When a VM (which belongs to a logical switch of OVN) sends a packet destined to go via the gateway router and if the nic which provides external connectivity, has a lesser MTU, OVS drops the packet if the packet length is greater than this MTU. With the help of this action, OVN will check the packet length and if it is greater than the MTU size, it will generate an ICMP packet (type 3, code 4) and includes the next hop mtu in it so that the sender can fragment the packets. Reported-at: https://mail.openvswitch.org/pipermail/ovs-discuss/2018-July/047039.html Suggested-by: Ben Pfaff Signed-off-by: Numan Siddique CC: Gregory Rose CC: Pravin B Shelar Acked-by: Pravin B Shelar Tested-by: Greg Rose Reviewed-by: Greg Rose Signed-off-by: David S. Miller Use of 'nla_parse_strict()' (in validate_and_copy_check_len()) is available only in recent kernels. So changed it to 'nla_parse_nested()'. Signed-off-by: Numan Siddique --- datapath/actions.c | 44 +++++ datapath/flow_netlink.c | 171 ++++++++++++++++++ .../linux/compat/include/linux/openvswitch.h | 17 ++ 3 files changed, 232 insertions(+) diff --git a/datapath/actions.c b/datapath/actions.c index 8abe70aa5..f5db12389 100644 --- a/datapath/actions.c +++ b/datapath/actions.c @@ -1219,6 +1219,40 @@ static int execute_recirc(struct datapath *dp, struct sk_buff *skb, return clone_execute(dp, skb, key, recirc_id, NULL, 0, last, true); } +static int execute_check_pkt_len(struct datapath *dp, struct sk_buff *skb, + struct sw_flow_key *key, + const struct nlattr *attr, bool last) +{ + const struct nlattr *actions, *cpl_arg; + const struct check_pkt_len_arg *arg; + int rem = nla_len(attr); + bool clone_flow_key; + + /* The first netlink attribute in 'attr' is always + * 'OVS_CHECK_PKT_LEN_ATTR_ARG'. + */ + cpl_arg = nla_data(attr); + arg = nla_data(cpl_arg); + + if (skb->len <= arg->pkt_len) { + /* Second netlink attribute in 'attr' is always + * 'OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL'. + */ + actions = nla_next(cpl_arg, &rem); + clone_flow_key = !arg->exec_for_lesser_equal; + } else { + /* Third netlink attribute in 'attr' is always + * 'OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER'. + */ + actions = nla_next(cpl_arg, &rem); + actions = nla_next(actions, &rem); + clone_flow_key = !arg->exec_for_greater; + } + + return clone_execute(dp, skb, key, 0, nla_data(actions), + nla_len(actions), last, clone_flow_key); +} + /* Execute a list of actions against 'skb'. */ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, struct sw_flow_key *key, @@ -1379,6 +1413,16 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, return err; break; } + + case OVS_ACTION_ATTR_CHECK_PKT_LEN: { + bool last = nla_is_last(a, rem); + + err = execute_check_pkt_len(dp, skb, key, a, last); + if (last) + return err; + + break; + } } if (unlikely(err)) { diff --git a/datapath/flow_netlink.c b/datapath/flow_netlink.c index e5e469a84..f915fc0d2 100644 --- a/datapath/flow_netlink.c +++ b/datapath/flow_netlink.c @@ -93,6 +93,7 @@ static bool actions_may_change_flow(const struct nlattr *actions) case OVS_ACTION_ATTR_SET: case OVS_ACTION_ATTR_SET_MASKED: case OVS_ACTION_ATTR_METER: + case OVS_ACTION_ATTR_CHECK_PKT_LEN: default: return true; } @@ -2846,6 +2847,87 @@ static int validate_userspace(const struct nlattr *attr) return 0; } +static const struct nla_policy cpl_policy[OVS_CHECK_PKT_LEN_ATTR_MAX + 1] = { + [OVS_CHECK_PKT_LEN_ATTR_PKT_LEN] = {.type = NLA_U16 }, + [OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER] = {.type = NLA_NESTED }, + [OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL] = {.type = NLA_NESTED }, +}; + +static int validate_and_copy_check_pkt_len(struct net *net, + const struct nlattr *attr, + const struct sw_flow_key *key, + struct sw_flow_actions **sfa, + __be16 eth_type, __be16 vlan_tci, + bool log, bool last) +{ + const struct nlattr *acts_if_greater, *acts_if_lesser_eq; + struct nlattr *a[OVS_CHECK_PKT_LEN_ATTR_MAX + 1]; + struct check_pkt_len_arg arg; + int nested_acts_start; + int start, err; + + err = nla_parse_nested(a, OVS_CHECK_PKT_LEN_ATTR_MAX, attr, + cpl_policy, NULL); + if (err) + return err; + + if (!a[OVS_CHECK_PKT_LEN_ATTR_PKT_LEN] || + !nla_get_u16(a[OVS_CHECK_PKT_LEN_ATTR_PKT_LEN])) + return -EINVAL; + + acts_if_lesser_eq = a[OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL]; + acts_if_greater = a[OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER]; + + /* Both the nested action should be present. */ + if (!acts_if_greater || !acts_if_lesser_eq) + return -EINVAL; + + /* validation done, copy the nested actions. */ + start = add_nested_action_start(sfa, OVS_ACTION_ATTR_CHECK_PKT_LEN, + log); + if (start < 0) + return start; + + arg.pkt_len = nla_get_u16(a[OVS_CHECK_PKT_LEN_ATTR_PKT_LEN]); + arg.exec_for_lesser_equal = + last || !actions_may_change_flow(acts_if_lesser_eq); + arg.exec_for_greater = + last || !actions_may_change_flow(acts_if_greater); + + err = ovs_nla_add_action(sfa, OVS_CHECK_PKT_LEN_ATTR_ARG, &arg, + sizeof(arg), log); + if (err) + return err; + + nested_acts_start = add_nested_action_start(sfa, + OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL, log); + if (nested_acts_start < 0) + return nested_acts_start; + + err = __ovs_nla_copy_actions(net, acts_if_lesser_eq, key, sfa, + eth_type, vlan_tci, log); + + if (err) + return err; + + add_nested_action_end(*sfa, nested_acts_start); + + nested_acts_start = add_nested_action_start(sfa, + OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER, log); + if (nested_acts_start < 0) + return nested_acts_start; + + err = __ovs_nla_copy_actions(net, acts_if_greater, key, sfa, + eth_type, vlan_tci, log); + + if (err) + return err; + + add_nested_action_end(*sfa, nested_acts_start); + add_nested_action_end(*sfa, start); + return 0; +} + static int copy_action(const struct nlattr *from, struct sw_flow_actions **sfa, bool log) { @@ -2892,6 +2974,7 @@ static int __ovs_nla_copy_actions(struct net *net, const struct nlattr *attr, [OVS_ACTION_ATTR_POP_NSH] = 0, [OVS_ACTION_ATTR_METER] = sizeof(u32), [OVS_ACTION_ATTR_CLONE] = (u32)-1, + [OVS_ACTION_ATTR_CHECK_PKT_LEN] = (u32)-1, }; const struct ovs_action_push_vlan *vlan; int type = nla_type(a); @@ -3093,6 +3176,19 @@ static int __ovs_nla_copy_actions(struct net *net, const struct nlattr *attr, break; } + case OVS_ACTION_ATTR_CHECK_PKT_LEN: { + bool last = nla_is_last(a, rem); + + err = validate_and_copy_check_pkt_len(net, a, key, sfa, + eth_type, + vlan_tci, log, + last); + if (err) + return err; + skip_copy = true; + break; + } + default: OVS_NLERR(log, "Unknown Action type %d", type); return -EINVAL; @@ -3191,6 +3287,75 @@ static int clone_action_to_attr(const struct nlattr *attr, return err; } +static int check_pkt_len_action_to_attr(const struct nlattr *attr, + struct sk_buff *skb) +{ + struct nlattr *start, *ac_start = NULL; + const struct check_pkt_len_arg *arg; + const struct nlattr *a, *cpl_arg; + int err = 0, rem = nla_len(attr); + + start = nla_nest_start(skb, OVS_ACTION_ATTR_CHECK_PKT_LEN); + if (!start) + return -EMSGSIZE; + + /* The first nested attribute in 'attr' is always + * 'OVS_CHECK_PKT_LEN_ATTR_ARG'. + */ + cpl_arg = nla_data(attr); + arg = nla_data(cpl_arg); + + if (nla_put_u16(skb, OVS_CHECK_PKT_LEN_ATTR_PKT_LEN, arg->pkt_len)) { + err = -EMSGSIZE; + goto out; + } + + /* Second nested attribute in 'attr' is always + * 'OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL'. + */ + a = nla_next(cpl_arg, &rem); + ac_start = nla_nest_start(skb, + OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL); + if (!ac_start) { + err = -EMSGSIZE; + goto out; + } + + err = ovs_nla_put_actions(nla_data(a), nla_len(a), skb); + if (err) { + nla_nest_cancel(skb, ac_start); + goto out; + } else { + nla_nest_end(skb, ac_start); + } + + /* Third nested attribute in 'attr' is always + * OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER. + */ + a = nla_next(a, &rem); + ac_start = nla_nest_start(skb, + OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER); + if (!ac_start) { + err = -EMSGSIZE; + goto out; + } + + err = ovs_nla_put_actions(nla_data(a), nla_len(a), skb); + if (err) { + nla_nest_cancel(skb, ac_start); + goto out; + } else { + nla_nest_end(skb, ac_start); + } + + nla_nest_end(skb, start); + return 0; + +out: + nla_nest_cancel(skb, start); + return err; +} + static int set_action_to_attr(const struct nlattr *a, struct sk_buff *skb) { const struct nlattr *ovs_key = nla_data(a); @@ -3285,6 +3450,12 @@ int ovs_nla_put_actions(const struct nlattr *attr, int len, struct sk_buff *skb) return err; break; + case OVS_ACTION_ATTR_CHECK_PKT_LEN: + err = check_pkt_len_action_to_attr(a, skb); + if (err) + return err; + break; + default: if (nla_put(skb, type, nla_len(a), nla_data(a))) return -EMSGSIZE; diff --git a/datapath/linux/compat/include/linux/openvswitch.h b/datapath/linux/compat/include/linux/openvswitch.h index ce364e577..65a003a62 100644 --- a/datapath/linux/compat/include/linux/openvswitch.h +++ b/datapath/linux/compat/include/linux/openvswitch.h @@ -877,10 +877,27 @@ enum ovs_check_pkt_len_attr { OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER, OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL, __OVS_CHECK_PKT_LEN_ATTR_MAX, + +#ifdef __KERNEL__ + OVS_CHECK_PKT_LEN_ATTR_ARG /* struct check_pkt_len_arg */ +#endif }; #define OVS_CHECK_PKT_LEN_ATTR_MAX (__OVS_CHECK_PKT_LEN_ATTR_MAX - 1) +#ifdef __KERNEL__ +struct check_pkt_len_arg { + u16 pkt_len; /* Same value as OVS_CHECK_PKT_LEN_ATTR_PKT_LEN'. */ + bool exec_for_greater; /* When true, actions in IF_GREATE will + * not change flow keys. False otherwise. + */ + bool exec_for_lesser_equal; /* When true, actions in IF_LESS_EQUAL + * will not change flow keys. False + * otherwise. + */ +}; +#endif + /** * enum ovs_action_attr - Action types. * From patchwork Fri Apr 12 13:05:43 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Numan Siddique X-Patchwork-Id: 1084735 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=openvswitch.org (client-ip=140.211.169.12; helo=mail.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=redhat.com Received: from mail.linuxfoundation.org (mail.linuxfoundation.org [140.211.169.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 44gdTY5kPSz9s5c for ; Fri, 12 Apr 2019 23:07:13 +1000 (AEST) Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id 5D5371235; Fri, 12 Apr 2019 13:05:51 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@mail.linuxfoundation.org Received: from smtp1.linuxfoundation.org (smtp1.linux-foundation.org [172.17.192.35]) by mail.linuxfoundation.org (Postfix) with ESMTPS id D01B711FB for ; Fri, 12 Apr 2019 13:05:49 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.7.6 Received: from mx1.redhat.com (mx1.redhat.com [209.132.183.28]) by smtp1.linuxfoundation.org (Postfix) with ESMTPS id 61EED860 for ; Fri, 12 Apr 2019 13:05:48 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.phx2.redhat.com [10.5.11.15]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 0674E9F74C for ; Fri, 12 Apr 2019 13:05:48 +0000 (UTC) Received: from nusiddiq.mac (unknown [10.74.10.41]) by smtp.corp.redhat.com (Postfix) with ESMTP id D822B5D6A9; Fri, 12 Apr 2019 13:05:45 +0000 (UTC) From: nusiddiq@redhat.com To: dev@openvswitch.org Date: Fri, 12 Apr 2019 18:35:43 +0530 Message-Id: <20190412130543.28527-1-nusiddiq@redhat.com> In-Reply-To: <20190412130447.28304-1-nusiddiq@redhat.com> References: <20190412130447.28304-1-nusiddiq@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.15 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.39]); Fri, 12 Apr 2019 13:05:48 +0000 (UTC) X-Spam-Status: No, score=-6.9 required=5.0 tests=BAYES_00,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on smtp1.linux-foundation.org Subject: [ovs-dev] [PATCH v2 3/6] ovn: Add a new OVN field icmp4.frag_mtu X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: ovs-dev-bounces@openvswitch.org Errors-To: ovs-dev-bounces@openvswitch.org From: Numan Siddique In order to support OVN specific fields (which are not yet supported in OpenvSwitch to set or modify values) a generic OVN field support is added in this patch. These OVN fields gets translated to controller actions. This patch adds only one field for now - icmp4.frag_mtu. It should be fairly straightforward to add similar fields in the near future. Example usage. action=(icmp4 {"eth.dst <-> eth.src; " "icmp4.type = 3; /* Destination Unreachable */ " "icmp4.code = 4; /* Fragmentation Needed */ " icmp4.frag_mtu = 1442; ... "next; };") action=(icmp4.frag_mtu = 1500; ..) pinctrl module of ovn-controller will set the specified value in the the low-order 16 bits of the ICMP4 header field that is labelled "unused" in the ICMP specification as defined in the RFC 1191. Upcoming patch will use it to send an icmp4 packet if the source IPv4 packet destined to go via external gateway needs to be fragmented. Signed-off-by: Numan Siddique Acked-by: Mark Michelson --- include/ovn/actions.h | 27 +++++++++- include/ovn/automake.mk | 3 +- include/ovn/expr.h | 5 ++ {ovn/lib => include/ovn}/logical-fields.h | 39 ++++++++++++++ ovn/controller/lflow.c | 1 + ovn/controller/lflow.h | 2 +- ovn/controller/pinctrl.c | 62 ++++++++++++++++++++++- ovn/lib/actions.c | 46 ++++++++++++++++- ovn/lib/automake.mk | 1 - ovn/lib/expr.c | 17 ++++++- ovn/lib/logical-fields.c | 36 ++++++++++++- ovn/northd/ovn-northd.c | 2 +- ovn/utilities/ovn-trace.c | 25 ++++++++- tests/ovn.at | 8 +++ tests/test-ovn.c | 2 +- 15 files changed, 263 insertions(+), 13 deletions(-) rename {ovn/lib => include/ovn}/logical-fields.h (73%) diff --git a/include/ovn/actions.h b/include/ovn/actions.h index 1c0c67ce6..89e28c50c 100644 --- a/include/ovn/actions.h +++ b/include/ovn/actions.h @@ -80,7 +80,8 @@ struct ovn_extend_table; OVNACT(LOG, ovnact_log) \ OVNACT(PUT_ND_RA_OPTS, ovnact_put_opts) \ OVNACT(ND_NS, ovnact_nest) \ - OVNACT(SET_METER, ovnact_set_meter) + OVNACT(SET_METER, ovnact_set_meter) \ + OVNACT(OVNFIELD_LOAD, ovnact_load) /* enum ovnact_type, with a member OVNACT_ for each action. */ enum OVS_PACKED_ENUM ovnact_type { @@ -142,6 +143,20 @@ ovnact_end(const struct ovnact *ovnacts, size_t ovnacts_len) #define OVNACT_FOR_EACH(POS, OVNACTS, OVNACTS_LEN) \ for ((POS) = (OVNACTS); (POS) < ovnact_end(OVNACTS, OVNACTS_LEN); \ (POS) = ovnact_next(POS)) + +static inline int +ovnacts_count(const struct ovnact *ovnacts, size_t ovnacts_len) +{ + uint8_t n_ovnacts = 0; + if (ovnacts) { + const struct ovnact *a; + + OVNACT_FOR_EACH (a, ovnacts, ovnacts_len) { + n_ovnacts++; + } + } + return n_ovnacts; +} /* Action structure for each OVNACT_*. */ @@ -452,6 +467,10 @@ enum action_opcode { * The actions, in OpenFlow 1.3 format, follow the action_header. */ ACTION_OPCODE_ND_NA_ROUTER, + + /* MTU value (to put in the icmp4 header field - frag_mtu) follow the + * action header. */ + ACTION_OPCODE_PUT_ICMP4_FRAG_MTU, }; /* Header. */ @@ -461,6 +480,12 @@ struct action_header { }; BUILD_ASSERT_DECL(sizeof(struct action_header) == 8); +OVS_PACKED( +struct ovnfield_act_header { + ovs_be16 id; /* one of enum ovnfield_id. */ + ovs_be16 len; /* Length of the ovnfield data. */ +}); + struct ovnact_parse_params { /* A table of "struct expr_symbol"s to support (as one would provide to * expr_parse()). */ diff --git a/include/ovn/automake.mk b/include/ovn/automake.mk index d2924c2f6..54b0e2c0e 100644 --- a/include/ovn/automake.mk +++ b/include/ovn/automake.mk @@ -2,4 +2,5 @@ ovnincludedir = $(includedir)/ovn ovninclude_HEADERS = \ include/ovn/actions.h \ include/ovn/expr.h \ - include/ovn/lex.h + include/ovn/lex.h \ + include/ovn/logical-fields.h diff --git a/include/ovn/expr.h b/include/ovn/expr.h index 3995e62f0..a68fd6e1c 100644 --- a/include/ovn/expr.h +++ b/include/ovn/expr.h @@ -58,6 +58,7 @@ #include "openvswitch/list.h" #include "openvswitch/match.h" #include "openvswitch/meta-flow.h" +#include "logical-fields.h" struct ds; struct expr; @@ -244,6 +245,7 @@ struct expr_symbol { int width; const struct mf_field *field; /* Fields only, otherwise NULL. */ + const struct ovn_field *ovn_field; /* OVN Fields only, otherwise NULL. */ const struct expr_symbol *parent; /* Subfields only, otherwise NULL. */ int parent_ofs; /* Subfields only, otherwise 0. */ char *predicate; /* Predicates only, otherwise NULL. */ @@ -284,6 +286,9 @@ struct expr_symbol *expr_symtab_add_string(struct shash *symtab, struct expr_symbol *expr_symtab_add_predicate(struct shash *symtab, const char *name, const char *expansion); +struct expr_symbol *expr_symtab_add_ovn_field(struct shash *symtab, + const char *name, + enum ovn_field_id id); void expr_symtab_destroy(struct shash *symtab); /* Expression type. */ diff --git a/ovn/lib/logical-fields.h b/include/ovn/logical-fields.h similarity index 73% rename from ovn/lib/logical-fields.h rename to include/ovn/logical-fields.h index 95759a8bb..164b338b5 100644 --- a/ovn/lib/logical-fields.h +++ b/include/ovn/logical-fields.h @@ -81,4 +81,43 @@ enum mff_log_flags { MLF_NESTED_CONTAINER = (1 << MLF_NESTED_CONTAINER_BIT), }; +/* OVN logical fields + * =================== + * These are the fields which OVN supports modifying which gets translated + * to OFFlow controller action. + * + * OpenvSwitch doesn't support modifying these fields yet. If a field is + * supported later by OpenvSwitch, it can be deleted from here. + */ + +enum ovn_field_id { + /* + * Name: "icmp4.frag_mtu" - + * Type: be16 + * Description: Sets the low-order 16 bits of the ICMP4 header field + * (that is labelled "unused" in the ICMP specification) of the ICMP4 + * packet as per the RFC 1191. + */ + OVN_ICMP4_FRAG_MTU, + + OVN_FIELD_N_IDS +}; + +struct ovn_field { + enum ovn_field_id id; + const char *name; + unsigned int n_bytes; /* Width of the field in bytes. */ + unsigned int n_bits; /* Number of significant bits in field. */ +}; + +static inline const struct ovn_field * +ovn_field_from_id(enum ovn_field_id id) +{ + extern const struct ovn_field ovn_fields[OVN_FIELD_N_IDS]; + ovs_assert((unsigned int) id < OVN_FIELD_N_IDS); + return &ovn_fields[id]; +} + +const struct ovn_field *ovn_field_from_name(const char *name); +void ovn_destroy_ovnfields(void); #endif /* ovn/lib/logical-fields.h */ diff --git a/ovn/controller/lflow.c b/ovn/controller/lflow.c index 8db81927e..432edbc70 100644 --- a/ovn/controller/lflow.c +++ b/ovn/controller/lflow.c @@ -495,4 +495,5 @@ lflow_destroy(void) { expr_symtab_destroy(&symtab); shash_destroy(&symtab); + ovn_destroy_ovnfields(); } diff --git a/ovn/controller/lflow.h b/ovn/controller/lflow.h index d19338140..efa066737 100644 --- a/ovn/controller/lflow.h +++ b/ovn/controller/lflow.h @@ -16,7 +16,7 @@ #ifndef OVN_LFLOW_H #define OVN_LFLOW_H 1 -#include "ovn/lib/logical-fields.h" +#include "ovn/logical-fields.h" /* Logical_Flow table translation to OpenFlow * ========================================== diff --git a/ovn/controller/pinctrl.c b/ovn/controller/pinctrl.c index 58bee8726..63202e6d4 100644 --- a/ovn/controller/pinctrl.c +++ b/ovn/controller/pinctrl.c @@ -43,9 +43,9 @@ #include "ovn/actions.h" #include "ovn/lex.h" #include "ovn/lib/acl-log.h" -#include "ovn/lib/logical-fields.h" #include "ovn/lib/ovn-l7.h" #include "ovn/lib/ovn-util.h" +#include "ovn/logical-fields.h" #include "openvswitch/poll-loop.h" #include "openvswitch/rconn.h" #include "socket-util.h" @@ -200,6 +200,12 @@ static void pinctrl_handle_nd_ns(struct rconn *swconn, struct dp_packet *pkt_in, const struct match *md, struct ofpbuf *userdata); +static void pinctrl_handle_put_icmp4_frag_mtu(struct rconn *swconn, + const struct flow *in_flow, + struct dp_packet *pkt_in, + struct ofputil_packet_in *pin, + struct ofpbuf *userdata, + struct ofpbuf *continuation); static void init_ipv6_ras(void); static void destroy_ipv6_ras(void); static void ipv6_ra_wait(long long int send_ipv6_ra_time); @@ -1676,6 +1682,11 @@ process_packet_in(struct rconn *swconn, const struct ofp_header *msg) &userdata); break; + case ACTION_OPCODE_PUT_ICMP4_FRAG_MTU: + pinctrl_handle_put_icmp4_frag_mtu(swconn, &headers, &packet, + &pin, &userdata, &continuation); + break; + default: VLOG_WARN_RL(&rl, "unrecognized packet-in opcode %"PRIu32, ntohl(ah->opcode)); @@ -3178,3 +3189,52 @@ exit: queue_msg(swconn, ofputil_encode_resume(pin, continuation, proto)); dp_packet_uninit(pkt_out_ptr); } + +/* Called with in the pinctrl_handler thread context. */ +static void +pinctrl_handle_put_icmp4_frag_mtu(struct rconn *swconn, + const struct flow *in_flow, + struct dp_packet *pkt_in, + struct ofputil_packet_in *pin, + struct ofpbuf *userdata, + struct ofpbuf *continuation) +{ + enum ofp_version version = rconn_get_version(swconn); + enum ofputil_protocol proto = ofputil_protocol_from_ofp_version(version); + struct dp_packet *pkt_out = NULL; + + /* This action only works for ICMPv4 packets. */ + if (!is_icmpv4(in_flow, NULL)) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); + VLOG_WARN_RL(&rl, "put_icmp4_frag_mtu action on non-ICMPv4 packet"); + goto exit; + } + + ovs_be16 *mtu = ofpbuf_try_pull(userdata, sizeof *mtu); + if (!mtu) { + goto exit; + } + + pkt_out = dp_packet_clone(pkt_in); + pkt_out->l2_5_ofs = pkt_in->l2_5_ofs; + pkt_out->l2_pad_size = pkt_in->l2_pad_size; + pkt_out->l3_ofs = pkt_in->l3_ofs; + pkt_out->l4_ofs = pkt_in->l4_ofs; + + struct ip_header *nh = dp_packet_l3(pkt_out); + struct icmp_header *ih = dp_packet_l4(pkt_out); + ovs_be16 old_frag_mtu = ih->icmp_fields.frag.mtu; + ih->icmp_fields.frag.mtu = *mtu; + ih->icmp_csum = recalc_csum16(ih->icmp_csum, old_frag_mtu, *mtu); + nh->ip_csum = 0; + nh->ip_csum = csum(nh, sizeof *nh); + + pin->packet = dp_packet_data(pkt_out); + pin->packet_len = dp_packet_size(pkt_out); + +exit: + queue_msg(swconn, ofputil_encode_resume(pin, continuation, proto)); + if (pkt_out) { + dp_packet_delete(pkt_out); + } +} diff --git a/ovn/lib/actions.c b/ovn/lib/actions.c index eb7e5badd..e8940b091 100644 --- a/ovn/lib/actions.c +++ b/ovn/lib/actions.c @@ -23,7 +23,6 @@ #include "ovn-l7.h" #include "hash.h" #include "lib/packets.h" -#include "logical-fields.h" #include "nx-match.h" #include "openvswitch/dynamic-string.h" #include "openvswitch/hmap.h" @@ -367,7 +366,13 @@ static void parse_LOAD(struct action_context *ctx, const struct expr_field *lhs) { size_t ofs = ctx->ovnacts->size; - struct ovnact_load *load = ovnact_put_LOAD(ctx->ovnacts); + struct ovnact_load *load; + if (lhs->symbol->ovn_field) { + load = ovnact_put_OVNFIELD_LOAD(ctx->ovnacts); + } else { + load = ovnact_put_LOAD(ctx->ovnacts); + } + load->dst = *lhs; char *error = expr_type_check(lhs, lhs->n_bits, true); @@ -2297,6 +2302,43 @@ ovnact_set_meter_free(struct ovnact_set_meter *ct OVS_UNUSED) { } +static void +format_OVNFIELD_LOAD(const struct ovnact_load *load , struct ds *s) +{ + const struct ovn_field *f = ovn_field_from_name(load->dst.symbol->name); + switch (f->id) { + case OVN_ICMP4_FRAG_MTU: + ds_put_format(s, "%s = %u;", f->name, + ntohs(load->imm.value.be16_int)); + break; + + case OVN_FIELD_N_IDS: + default: + OVS_NOT_REACHED(); + } +} + +static void +encode_OVNFIELD_LOAD(const struct ovnact_load *load, + const struct ovnact_encode_params *ep OVS_UNUSED, + struct ofpbuf *ofpacts) +{ + const struct ovn_field *f = ovn_field_from_name(load->dst.symbol->name); + switch (f->id) { + case OVN_ICMP4_FRAG_MTU: { + size_t oc_offset = encode_start_controller_op( + ACTION_OPCODE_PUT_ICMP4_FRAG_MTU, true, NX_CTLR_NO_METER, + ofpacts); + ofpbuf_put(ofpacts, &load->imm.value.be16_int, sizeof(ovs_be16)); + encode_finish_controller_op(oc_offset, ofpacts); + break; + } + case OVN_FIELD_N_IDS: + default: + OVS_NOT_REACHED(); + } +} + /* Parses an assignment or exchange or put_dhcp_opts action. */ static void parse_set_action(struct action_context *ctx) diff --git a/ovn/lib/automake.mk b/ovn/lib/automake.mk index 6178fc2d5..f8a5b5f0f 100644 --- a/ovn/lib/automake.mk +++ b/ovn/lib/automake.mk @@ -17,7 +17,6 @@ ovn_lib_libovn_la_SOURCES = \ ovn/lib/ovn-util.c \ ovn/lib/ovn-util.h \ ovn/lib/logical-fields.c \ - ovn/lib/logical-fields.h nodist_ovn_lib_libovn_la_SOURCES = \ ovn/lib/ovn-nb-idl.c \ ovn/lib/ovn-nb-idl.h \ diff --git a/ovn/lib/expr.c b/ovn/lib/expr.c index 8cfdf34fa..e21206120 100644 --- a/ovn/lib/expr.c +++ b/ovn/lib/expr.c @@ -17,7 +17,6 @@ #include #include "byte-order.h" #include "openvswitch/json.h" -#include "logical-fields.h" #include "nx-match.h" #include "openvswitch/dynamic-string.h" #include "openvswitch/match.h" @@ -26,6 +25,7 @@ #include "openvswitch/shash.h" #include "ovn/expr.h" #include "ovn/lex.h" +#include "ovn/logical-fields.h" #include "simap.h" #include "sset.h" #include "util.h" @@ -1381,6 +1381,8 @@ expr_symbol_format(const struct expr_symbol *symbol, struct ds *s) expr_field_format(&f, s); } else if (symbol->predicate) { ds_put_cstr(s, symbol->predicate); + } else if (symbol->ovn_field) { + ds_put_cstr(s, symbol->name); } else { nx_format_field_name(symbol->field->id, OFP13_VERSION, s); } @@ -1556,6 +1558,19 @@ expr_symtab_add_predicate(struct shash *symtab, const char *name, return symbol; } +struct expr_symbol * +expr_symtab_add_ovn_field(struct shash *symtab, const char *name, + enum ovn_field_id id) +{ + const struct ovn_field *ovn_field = ovn_field_from_id(id); + struct expr_symbol *symbol; + + symbol = add_symbol(symtab, name, ovn_field->n_bits, NULL, + EXPR_L_NOMINAL, false, true); + symbol->ovn_field = ovn_field; + return symbol; +} + /* Destroys 'symtab' and all of its symbols. */ void expr_symtab_destroy(struct shash *symtab) diff --git a/ovn/lib/logical-fields.c b/ovn/lib/logical-fields.c index a8b5e3c51..579537d6b 100644 --- a/ovn/lib/logical-fields.c +++ b/ovn/lib/logical-fields.c @@ -15,13 +15,25 @@ #include -#include "logical-fields.h" - #include "openvswitch/shash.h" #include "ovn/expr.h" +#include "ovn/logical-fields.h" #include "ovs-thread.h" #include "packets.h" +/* Silence a warning. */ +extern const struct ovn_field ovn_fields[OVN_FIELD_N_IDS]; + +const struct ovn_field ovn_fields[OVN_FIELD_N_IDS] = { + { + OVN_ICMP4_FRAG_MTU, + "icmp4.frag_mtu", + 2, 16, + }, +}; + +static struct shash ovnfield_by_name; + static void add_subregister(const char *name, const char *parent_name, int parent_idx, @@ -203,4 +215,24 @@ ovn_init_symtab(struct shash *symtab) expr_symtab_add_predicate(symtab, "sctp", "ip.proto == 132"); expr_symtab_add_field(symtab, "sctp.src", MFF_SCTP_SRC, "sctp", false); expr_symtab_add_field(symtab, "sctp.dst", MFF_SCTP_DST, "sctp", false); + + shash_init(&ovnfield_by_name); + for (int i = 0; i < OVN_FIELD_N_IDS; i++) { + const struct ovn_field *of = &ovn_fields[i]; + ovs_assert(of->id == i); /* Fields must be in the enum order. */ + shash_add_once(&ovnfield_by_name, of->name, of); + } + expr_symtab_add_ovn_field(symtab, "icmp4.frag_mtu", OVN_ICMP4_FRAG_MTU); +} + +const struct ovn_field * +ovn_field_from_name(const char *name) +{ + return shash_find_data(&ovnfield_by_name, name); +} + +void +ovn_destroy_ovnfields(void) +{ + shash_destroy(&ovnfield_by_name); } diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c index 05b8aad4f..5614f9fa3 100644 --- a/ovn/northd/ovn-northd.c +++ b/ovn/northd/ovn-northd.c @@ -29,12 +29,12 @@ #include "openvswitch/json.h" #include "ovn/lex.h" #include "ovn/lib/chassis-index.h" -#include "ovn/lib/logical-fields.h" #include "ovn/lib/ovn-l7.h" #include "ovn/lib/ovn-nb-idl.h" #include "ovn/lib/ovn-sb-idl.h" #include "ovn/lib/ovn-util.h" #include "ovn/actions.h" +#include "ovn/logical-fields.h" #include "packets.h" #include "openvswitch/poll-loop.h" #include "smap.h" diff --git a/ovn/utilities/ovn-trace.c b/ovn/utilities/ovn-trace.c index 8899496a5..e03179c8f 100644 --- a/ovn/utilities/ovn-trace.c +++ b/ovn/utilities/ovn-trace.c @@ -35,8 +35,8 @@ #include "ovn/actions.h" #include "ovn/expr.h" #include "ovn/lex.h" +#include "ovn/logical-fields.h" #include "ovn/lib/acl-log.h" -#include "ovn/lib/logical-fields.h" #include "ovn/lib/ovn-l7.h" #include "ovn/lib/ovn-sb-idl.h" #include "ovn/lib/ovn-util.h" @@ -1938,6 +1938,25 @@ execute_log(const struct ovnact_log *log, struct flow *uflow, free(packet_str); } +static void +execute_ovnfield_load(const struct ovnact_load *load, + struct ovs_list *super) +{ + const struct ovn_field *f = ovn_field_from_name(load->dst.symbol->name); + switch (f->id) { + case OVN_ICMP4_FRAG_MTU: { + ovntrace_node_append(super, OVNTRACE_NODE_MODIFY, + "icmp4.frag_mtu = %u", + ntohs(load->imm.value.be16_int)); + break; + } + + case OVN_FIELD_N_IDS: + default: + OVS_NOT_REACHED(); + } +} + static void trace_actions(const struct ovnact *ovnacts, size_t ovnacts_len, const struct ovntrace_datapath *dp, struct flow *uflow, @@ -2106,6 +2125,10 @@ trace_actions(const struct ovnact *ovnacts, size_t ovnacts_len, execute_tcp_reset(ovnact_get_TCP_RESET(a), dp, uflow, table_id, pipeline, super); break; + + case OVNACT_OVNFIELD_LOAD: + execute_ovnfield_load(ovnact_get_OVNFIELD_LOAD(a), super); + break; } } diff --git a/tests/ovn.at b/tests/ovn.at index e7746cb0f..02ab94a87 100644 --- a/tests/ovn.at +++ b/tests/ovn.at @@ -1290,6 +1290,14 @@ icmp4 { }; encodes as controller(userdata=00.00.00.0a.00.00.00.00) has prereqs ip4 +# icmp4 with icmp4.frag_mtu +icmp4 { eth.dst = ff:ff:ff:ff:ff:ff; icmp4.frag_mtu = 1500; output; }; output; + encodes as controller(userdata=00.00.00.0a.00.00.00.00.00.19.00.10.80.00.06.06.ff.ff.ff.ff.ff.ff.00.00.ff.ff.00.28.00.00.23.20.00.25.00.00.00.00.00.00.00.03.00.0e.00.00.00.0d.00.00.00.00.05.dc.00.00.00.04.00.04.00.00.00.00.ff.ff.00.10.00.00.23.20.00.0e.ff.f8.40.00.00.00),resubmit(,64) + has prereqs ip4 + +icmp4.frag_mtu = 1500; + encodes as controller(userdata=00.00.00.0d.00.00.00.00.05.dc,pause) + # icmp6 icmp6 { eth.dst = ff:ff:ff:ff:ff:ff; output; }; output; encodes as controller(userdata=00.00.00.0a.00.00.00.00.00.19.00.10.80.00.06.06.ff.ff.ff.ff.ff.ff.00.00.ff.ff.00.10.00.00.23.20.00.0e.ff.f8.40.00.00.00),resubmit(,64) diff --git a/tests/test-ovn.c b/tests/test-ovn.c index 9e3112bf9..7cce9c2ae 100644 --- a/tests/test-ovn.c +++ b/tests/test-ovn.c @@ -31,7 +31,7 @@ #include "ovn/actions.h" #include "ovn/expr.h" #include "ovn/lex.h" -#include "ovn/lib/logical-fields.h" +#include "ovn/logical-fields.h" #include "ovn/lib/ovn-l7.h" #include "ovn/lib/extend-table.h" #include "ovs-thread.h" From patchwork Fri Apr 12 13:05:48 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Numan Siddique X-Patchwork-Id: 1084737 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=openvswitch.org (client-ip=140.211.169.12; helo=mail.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=redhat.com Received: from mail.linuxfoundation.org (mail.linuxfoundation.org [140.211.169.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 44gdVX0lDTz9s5c for ; Fri, 12 Apr 2019 23:08:04 +1000 (AEST) Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id 3C204126B; Fri, 12 Apr 2019 13:05:56 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@mail.linuxfoundation.org Received: from smtp1.linuxfoundation.org (smtp1.linux-foundation.org [172.17.192.35]) by mail.linuxfoundation.org (Postfix) with ESMTPS id 958C8124D for ; Fri, 12 Apr 2019 13:05:54 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.7.6 Received: from mx1.redhat.com (mx1.redhat.com [209.132.183.28]) by smtp1.linuxfoundation.org (Postfix) with ESMTPS id A1A3A855 for ; Fri, 12 Apr 2019 13:05:53 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.phx2.redhat.com [10.5.11.13]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 465E23082E5F for ; Fri, 12 Apr 2019 13:05:53 +0000 (UTC) Received: from nusiddiq.mac (unknown [10.74.10.41]) by smtp.corp.redhat.com (Postfix) with ESMTP id 8C14C6091F; Fri, 12 Apr 2019 13:05:51 +0000 (UTC) From: nusiddiq@redhat.com To: dev@openvswitch.org Date: Fri, 12 Apr 2019 18:35:48 +0530 Message-Id: <20190412130548.28591-1-nusiddiq@redhat.com> In-Reply-To: <20190412130447.28304-1-nusiddiq@redhat.com> References: <20190412130447.28304-1-nusiddiq@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.13 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.46]); Fri, 12 Apr 2019 13:05:53 +0000 (UTC) X-Spam-Status: No, score=-6.9 required=5.0 tests=BAYES_00,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on smtp1.linux-foundation.org Subject: [ovs-dev] [PATCH v2 4/6] ovn: Add a new OVN action 'icmp4_error' X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: ovs-dev-bounces@openvswitch.org Errors-To: ovs-dev-bounces@openvswitch.org From: Numan Siddique This action is similar to the existing 'icmp4' OVN action except that that this action is expected to be used to generate an ICMPv4 packet in response to an error in original IP packet. When this action injects the icmpv4 packet, it also copies the original IP datagram following the icmp4 header as per RFC 1122: 3.2.2 Signed-off-by: Numan Siddique Acked-by: Mark Michelson --- include/ovn/actions.h | 7 ++++++ ovn/controller/pinctrl.c | 47 +++++++++++++++++++++++++++++++++++++-- ovn/lib/actions.c | 22 ++++++++++++++++++ ovn/ovn-sb.xml | 20 ++++++++++++----- ovn/utilities/ovn-trace.c | 5 +++++ tests/ovn.at | 15 +++++++++++++ 6 files changed, 109 insertions(+), 7 deletions(-) diff --git a/include/ovn/actions.h b/include/ovn/actions.h index 89e28c50c..af8b0a4a0 100644 --- a/include/ovn/actions.h +++ b/include/ovn/actions.h @@ -65,6 +65,7 @@ struct ovn_extend_table; OVNACT(CLONE, ovnact_nest) \ OVNACT(ARP, ovnact_nest) \ OVNACT(ICMP4, ovnact_nest) \ + OVNACT(ICMP4_ERROR, ovnact_nest) \ OVNACT(ICMP6, ovnact_nest) \ OVNACT(TCP_RESET, ovnact_nest) \ OVNACT(ND_NA, ovnact_nest) \ @@ -471,6 +472,12 @@ enum action_opcode { /* MTU value (to put in the icmp4 header field - frag_mtu) follow the * action header. */ ACTION_OPCODE_PUT_ICMP4_FRAG_MTU, + + /* "icmp4_error { ...actions... }". + * + * The actions, in OpenFlow 1.3 format, follow the action_header. + */ + ACTION_OPCODE_ICMP4_ERROR, }; /* Header. */ diff --git a/ovn/controller/pinctrl.c b/ovn/controller/pinctrl.c index 63202e6d4..2f22a57b2 100644 --- a/ovn/controller/pinctrl.c +++ b/ovn/controller/pinctrl.c @@ -531,7 +531,8 @@ pinctrl_handle_arp(struct rconn *swconn, const struct flow *ip_flow, static void pinctrl_handle_icmp(struct rconn *swconn, const struct flow *ip_flow, struct dp_packet *pkt_in, - const struct match *md, struct ofpbuf *userdata) + const struct match *md, struct ofpbuf *userdata, + bool include_orig_ip_datagram) { /* This action only works for IP packets, and the switch should only send * us IP packets this way, but check here just to be sure. */ @@ -556,6 +557,16 @@ pinctrl_handle_icmp(struct rconn *swconn, const struct flow *ip_flow, eh->eth_src = ip_flow->dl_src; if (get_dl_type(ip_flow) == htons(ETH_TYPE_IP)) { + struct ip_header *in_ip = dp_packet_l3(pkt_in); + uint16_t in_ip_len = ntohs(in_ip->ip_tot_len); + if (in_ip_len < IP_HEADER_LEN) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); + VLOG_WARN_RL(&rl, + "ICMP action on IP packet with invalid length (%u)", + in_ip_len); + return; + } + struct ip_header *nh = dp_packet_put_zeros(&packet, sizeof *nh); eh->eth_type = htons(ETH_TYPE_IP); @@ -571,6 +582,33 @@ pinctrl_handle_icmp(struct rconn *swconn, const struct flow *ip_flow, struct icmp_header *ih = dp_packet_put_zeros(&packet, sizeof *ih); dp_packet_set_l4(&packet, ih); packet_set_icmp(&packet, ICMP4_DST_UNREACH, 1); + + if (include_orig_ip_datagram) { + /* RFC 1122: 3.2.2 MUST send at least the IP header and 8 bytes + * of header. MAY send more. + * RFC says return as much as we can without exceeding 576 + * bytes. + * So, lets return as much as we can. */ + + /* Calculate available room to include the original IP + data. */ + nh = dp_packet_l3(&packet); + uint16_t room = 576 - (sizeof *eh + ntohs(nh->ip_tot_len)); + if (in_ip_len > room) { + in_ip_len = room; + } + dp_packet_put(&packet, in_ip, in_ip_len); + + /* dp_packet_put may reallocate the buffer. Get the l3 and l4 + * header pointers again. */ + nh = dp_packet_l3(&packet); + ih = dp_packet_l4(&packet); + uint16_t ip_total_len = ntohs(nh->ip_tot_len) + in_ip_len; + nh->ip_tot_len = htons(ip_total_len); + ih->icmp_csum = 0; + ih->icmp_csum = csum(ih, sizeof *ih + in_ip_len); + nh->ip_csum = 0; + nh->ip_csum = csum(nh, sizeof *nh); + } } else { struct ip6_hdr *nh = dp_packet_put_zeros(&packet, sizeof *nh); struct icmp6_error_header *ih; @@ -1674,7 +1712,12 @@ process_packet_in(struct rconn *swconn, const struct ofp_header *msg) case ACTION_OPCODE_ICMP: pinctrl_handle_icmp(swconn, &headers, &packet, &pin.flow_metadata, - &userdata); + &userdata, false); + break; + + case ACTION_OPCODE_ICMP4_ERROR: + pinctrl_handle_icmp(swconn, &headers, &packet, &pin.flow_metadata, + &userdata, true); break; case ACTION_OPCODE_TCP_RESET: diff --git a/ovn/lib/actions.c b/ovn/lib/actions.c index e8940b091..f78e6ffcb 100644 --- a/ovn/lib/actions.c +++ b/ovn/lib/actions.c @@ -1153,6 +1153,12 @@ parse_ICMP4(struct action_context *ctx) parse_nested_action(ctx, OVNACT_ICMP4, "ip4"); } +static void +parse_ICMP4_ERROR(struct action_context *ctx) +{ + parse_nested_action(ctx, OVNACT_ICMP4_ERROR, "ip4"); +} + static void parse_ICMP6(struct action_context *ctx) { @@ -1210,6 +1216,12 @@ format_ICMP4(const struct ovnact_nest *nest, struct ds *s) format_nested_action(nest, "icmp4", s); } +static void +format_ICMP4_ERROR(const struct ovnact_nest *nest, struct ds *s) +{ + format_nested_action(nest, "icmp4_error", s); +} + static void format_ICMP6(const struct ovnact_nest *nest, struct ds *s) { @@ -1287,6 +1299,14 @@ encode_ICMP4(const struct ovnact_nest *on, encode_nested_actions(on, ep, ACTION_OPCODE_ICMP, ofpacts); } +static void +encode_ICMP4_ERROR(const struct ovnact_nest *on, + const struct ovnact_encode_params *ep, + struct ofpbuf *ofpacts) +{ + encode_nested_actions(on, ep, ACTION_OPCODE_ICMP4_ERROR, ofpacts); +} + static void encode_ICMP6(const struct ovnact_nest *on, const struct ovnact_encode_params *ep, @@ -2412,6 +2432,8 @@ parse_action(struct action_context *ctx) parse_ARP(ctx); } else if (lexer_match_id(ctx->lexer, "icmp4")) { parse_ICMP4(ctx); + } else if (lexer_match_id(ctx->lexer, "icmp4_error")) { + parse_ICMP4_ERROR(ctx); } else if (lexer_match_id(ctx->lexer, "icmp6")) { parse_ICMP6(ctx); } else if (lexer_match_id(ctx->lexer, "tcp_reset")) { diff --git a/ovn/ovn-sb.xml b/ovn/ovn-sb.xml index 4e080abff..0d35fe4fc 100644 --- a/ovn/ovn-sb.xml +++ b/ovn/ovn-sb.xml @@ -1820,19 +1820,22 @@
icmp4 { action; ... };
+
+ icmp4_error { action; ... }; +

Temporarily replaces the IPv4 packet being processed by an ICMPv4 packet and executes each nested action on the ICMPv4 - packet. Actions following the icmp4 action, if any, + packet. Actions following these actions, if any, apply to the original, unmodified packet.

- The ICMPv4 packet that this action operates on is initialized based - on the IPv4 packet being processed, as follows. These are default - values that the nested actions will probably want to change. - Ethernet and IPv4 fields not listed here are not changed: + The ICMPv4 packet that these actions operates on is initialized + based on the IPv4 packet being processed, as follows. These are + default values that the nested actions will probably want to + change. Ethernet and IPv4 fields not listed here are not changed:

    @@ -1843,6 +1846,13 @@
  • icmp4.code = 1 (host unreachable)
+

+ icmp4_error action is expected to be used to + generate an ICMPv4 packet in response to an error in original + IP packet. When this action generates the ICMPv4 packet, it + also copies the original IP datagram following the ICMPv4 header + as per RFC 1122: 3.2.2. +

Prerequisite: ip4

diff --git a/ovn/utilities/ovn-trace.c b/ovn/utilities/ovn-trace.c index e03179c8f..28e2bb075 100644 --- a/ovn/utilities/ovn-trace.c +++ b/ovn/utilities/ovn-trace.c @@ -2116,6 +2116,11 @@ trace_actions(const struct ovnact *ovnacts, size_t ovnacts_len, super); break; + case OVNACT_ICMP4_ERROR: + execute_icmp4(ovnact_get_ICMP4_ERROR(a), dp, uflow, table_id, + pipeline, super); + break; + case OVNACT_ICMP6: execute_icmp6(ovnact_get_ICMP6(a), dp, uflow, table_id, pipeline, super); diff --git a/tests/ovn.at b/tests/ovn.at index 02ab94a87..92a6bed26 100644 --- a/tests/ovn.at +++ b/tests/ovn.at @@ -1295,6 +1295,21 @@ icmp4 { eth.dst = ff:ff:ff:ff:ff:ff; icmp4.frag_mtu = 1500; output; }; output; encodes as controller(userdata=00.00.00.0a.00.00.00.00.00.19.00.10.80.00.06.06.ff.ff.ff.ff.ff.ff.00.00.ff.ff.00.28.00.00.23.20.00.25.00.00.00.00.00.00.00.03.00.0e.00.00.00.0d.00.00.00.00.05.dc.00.00.00.04.00.04.00.00.00.00.ff.ff.00.10.00.00.23.20.00.0e.ff.f8.40.00.00.00),resubmit(,64) has prereqs ip4 +# icmp4_error +icmp4_error { eth.dst = ff:ff:ff:ff:ff:ff; output; }; output; + encodes as controller(userdata=00.00.00.0e.00.00.00.00.00.19.00.10.80.00.06.06.ff.ff.ff.ff.ff.ff.00.00.ff.ff.00.10.00.00.23.20.00.0e.ff.f8.40.00.00.00),resubmit(,64) + has prereqs ip4 + +icmp4_error { }; + formats as icmp4_error { drop; }; + encodes as controller(userdata=00.00.00.0e.00.00.00.00) + has prereqs ip4 + +# icmp4_error with icmp4.frag_mtu +icmp4_error { eth.dst = ff:ff:ff:ff:ff:ff; icmp4.frag_mtu = 1500; output; }; output; + encodes as controller(userdata=00.00.00.0e.00.00.00.00.00.19.00.10.80.00.06.06.ff.ff.ff.ff.ff.ff.00.00.ff.ff.00.28.00.00.23.20.00.25.00.00.00.00.00.00.00.03.00.0e.00.00.00.0d.00.00.00.00.05.dc.00.00.00.04.00.04.00.00.00.00.ff.ff.00.10.00.00.23.20.00.0e.ff.f8.40.00.00.00),resubmit(,64) + has prereqs ip4 + icmp4.frag_mtu = 1500; encodes as controller(userdata=00.00.00.0d.00.00.00.00.05.dc,pause) From patchwork Fri Apr 12 13:05:54 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Numan Siddique X-Patchwork-Id: 1084738 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=openvswitch.org (client-ip=140.211.169.12; helo=mail.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=redhat.com Received: from mail.linuxfoundation.org (mail.linuxfoundation.org [140.211.169.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 44gdW90MVzz9s9h for ; Fri, 12 Apr 2019 23:08:36 +1000 (AEST) Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id 01BD911BC; Fri, 12 Apr 2019 13:06:01 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@mail.linuxfoundation.org Received: from smtp1.linuxfoundation.org (smtp1.linux-foundation.org [172.17.192.35]) by mail.linuxfoundation.org (Postfix) with ESMTPS id 27BE7127F for ; Fri, 12 Apr 2019 13:06:00 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.7.6 Received: from mx1.redhat.com (mx1.redhat.com [209.132.183.28]) by smtp1.linuxfoundation.org (Postfix) with ESMTPS id 8175E855 for ; Fri, 12 Apr 2019 13:05:59 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx07.intmail.prod.int.phx2.redhat.com [10.5.11.22]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 20CB33082129 for ; Fri, 12 Apr 2019 13:05:59 +0000 (UTC) Received: from nusiddiq.mac (unknown [10.74.10.41]) by smtp.corp.redhat.com (Postfix) with ESMTP id A666D1001E61; Fri, 12 Apr 2019 13:05:57 +0000 (UTC) From: nusiddiq@redhat.com To: dev@openvswitch.org Date: Fri, 12 Apr 2019 18:35:54 +0530 Message-Id: <20190412130554.28643-1-nusiddiq@redhat.com> In-Reply-To: <20190412130447.28304-1-nusiddiq@redhat.com> References: <20190412130447.28304-1-nusiddiq@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.84 on 10.5.11.22 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.42]); Fri, 12 Apr 2019 13:05:59 +0000 (UTC) X-Spam-Status: No, score=-6.9 required=5.0 tests=BAYES_00,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on smtp1.linux-foundation.org Subject: [ovs-dev] [PATCH v2 5/6] ovn: Support OVS action 'check_pkt_larger' in OVN X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: ovs-dev-bounces@openvswitch.org Errors-To: ovs-dev-bounces@openvswitch.org From: Numan Siddique Previous commit added a new OVS action 'check_pkt_larger'. This patch supports that action in OVN. The syntax to use this would be reg0[0] = check_pkt_larger(LEN) Upcoming commit will make use of this action in ovn-northd and will generate an ICMPv4 packet if the packet length is greater than the specified length. Signed-off-by: Numan Siddique Acked-by: Mark Michelson --- include/ovn/actions.h | 10 +++++++- ovn/lib/actions.c | 53 +++++++++++++++++++++++++++++++++++++++ ovn/ovn-sb.xml | 21 ++++++++++++++++ ovn/utilities/ovn-trace.c | 4 ++- tests/ovn.at | 10 ++++++++ 5 files changed, 96 insertions(+), 2 deletions(-) diff --git a/include/ovn/actions.h b/include/ovn/actions.h index af8b0a4a0..e07ad9aa3 100644 --- a/include/ovn/actions.h +++ b/include/ovn/actions.h @@ -82,7 +82,8 @@ struct ovn_extend_table; OVNACT(PUT_ND_RA_OPTS, ovnact_put_opts) \ OVNACT(ND_NS, ovnact_nest) \ OVNACT(SET_METER, ovnact_set_meter) \ - OVNACT(OVNFIELD_LOAD, ovnact_load) + OVNACT(OVNFIELD_LOAD, ovnact_load) \ + OVNACT(CHECK_PKT_LARGER, ovnact_check_pkt_larger) /* enum ovnact_type, with a member OVNACT_ for each action. */ enum OVS_PACKED_ENUM ovnact_type { @@ -310,6 +311,13 @@ struct ovnact_set_meter { uint64_t burst; /* burst rate field, in kbps. */ }; +/* OVNACT_CHECK_IP_PKT_LARGER. */ +struct ovnact_check_pkt_larger { + struct ovnact ovnact; + uint16_t pkt_len; + struct expr_field dst; /* 1-bit destination field. */ +}; + /* Internal use by the helpers below. */ void ovnact_init(struct ovnact *, enum ovnact_type, size_t len); void *ovnact_put(struct ofpbuf *, enum ovnact_type, size_t len); diff --git a/ovn/lib/actions.c b/ovn/lib/actions.c index f78e6ffcb..a37e115e5 100644 --- a/ovn/lib/actions.c +++ b/ovn/lib/actions.c @@ -2359,6 +2359,55 @@ encode_OVNFIELD_LOAD(const struct ovnact_load *load, } } +static void +parse_check_pkt_larger(struct action_context *ctx, + const struct expr_field *dst, + struct ovnact_check_pkt_larger *cipl) +{ + int pkt_len; + + lexer_get(ctx->lexer); /* Skip check_pkt_len. */ + if (!lexer_force_match(ctx->lexer, LEX_T_LPAREN) + || !lexer_get_int(ctx->lexer, &pkt_len) + || !lexer_force_match(ctx->lexer, LEX_T_RPAREN)) { + return; + } + + /* Validate that the destination is a 1-bit, modifiable field. */ + char *error = expr_type_check(dst, 1, true); + if (error) { + lexer_error(ctx->lexer, "%s", error); + free(error); + return; + } + cipl->dst = *dst; + cipl->pkt_len = pkt_len; +} + +static void +format_CHECK_PKT_LARGER(const struct ovnact_check_pkt_larger *cipl, + struct ds *s) +{ + expr_field_format(&cipl->dst, s); + ds_put_format(s, " = check_pkt_larger(%d);", cipl->pkt_len); +} + +static void +encode_CHECK_PKT_LARGER(const struct ovnact_check_pkt_larger *cipl, + const struct ovnact_encode_params *ep OVS_UNUSED, + struct ofpbuf *ofpacts) +{ + struct ofpact_check_pkt_larger *check_pkt_larger = + ofpact_put_CHECK_PKT_LARGER(ofpacts); + check_pkt_larger->pkt_len = cipl->pkt_len; + check_pkt_larger->dst = expr_resolve_field(&cipl->dst); +} + +static void +ovnact_check_pkt_larger_free(struct ovnact_check_pkt_larger *cipl OVS_UNUSED) +{ +} + /* Parses an assignment or exchange or put_dhcp_opts action. */ static void parse_set_action(struct action_context *ctx) @@ -2388,6 +2437,10 @@ parse_set_action(struct action_context *ctx) && lexer_lookahead(ctx->lexer) == LEX_T_LPAREN) { parse_put_nd_ra_opts(ctx, &lhs, ovnact_put_PUT_ND_RA_OPTS(ctx->ovnacts)); + } else if (!strcmp(ctx->lexer->token.s, "check_pkt_larger") + && lexer_lookahead(ctx->lexer) == LEX_T_LPAREN) { + parse_check_pkt_larger(ctx, &lhs, + ovnact_put_CHECK_PKT_LARGER(ctx->ovnacts)); } else { parse_assignment_action(ctx, false, &lhs); } diff --git a/ovn/ovn-sb.xml b/ovn/ovn-sb.xml index 0d35fe4fc..9d611c9ab 100644 --- a/ovn/ovn-sb.xml +++ b/ovn/ovn-sb.xml @@ -1766,6 +1766,27 @@

Example: set_meter(100, 1000);

+ +
R = check_pkt_larger(L)
+
+

+ Parameters: packet length L to check for + in bytes. +

+ +

+ Result: stored to a 1-bit subfield R. +

+ +

+ This is a logical equivalent of the OpenFlow + check_pkt_larger action. If the packet is larger + than the length specified in L, it stores 1 in the + subfield R. +

+ +

Example: reg0[6] = check_pkt_larger(1000);

+
diff --git a/ovn/utilities/ovn-trace.c b/ovn/utilities/ovn-trace.c index 28e2bb075..9718077aa 100644 --- a/ovn/utilities/ovn-trace.c +++ b/ovn/utilities/ovn-trace.c @@ -2134,8 +2134,10 @@ trace_actions(const struct ovnact *ovnacts, size_t ovnacts_len, case OVNACT_OVNFIELD_LOAD: execute_ovnfield_load(ovnact_get_OVNFIELD_LOAD(a), super); break; - } + case OVNACT_CHECK_PKT_LARGER: + break; + } } ds_destroy(&s); } diff --git a/tests/ovn.at b/tests/ovn.at index 92a6bed26..043697120 100644 --- a/tests/ovn.at +++ b/tests/ovn.at @@ -1341,6 +1341,16 @@ ip4.src <-> ip6.src[0..31]; encodes as push:NXM_NX_IPV6_SRC[0..31],push:NXM_OF_IP_SRC[],pop:NXM_NX_IPV6_SRC[0..31],pop:NXM_OF_IP_SRC[] has prereqs eth.type == 0x800 && eth.type == 0x86dd +# check_pkt_larger +reg0[0] = check_pkt_larger(1500); + encodes as check_pkt_larger(1500)->NXM_NX_XXREG0[96] + +reg0 = check_pkt_larger(1500); + Cannot use 32-bit field reg0[0..31] where 1-bit field is required. + +reg0 = check_pkt_larger(foo); + Syntax error at `foo' expecting `;'. + # Miscellaneous negative tests. ; Syntax error at `;'. From patchwork Fri Apr 12 13:05:59 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Numan Siddique X-Patchwork-Id: 1084739 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=openvswitch.org (client-ip=140.211.169.12; helo=mail.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=redhat.com Received: from mail.linuxfoundation.org (mail.linuxfoundation.org [140.211.169.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 44gdWn2bsNz9s5c for ; Fri, 12 Apr 2019 23:09:09 +1000 (AEST) Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id 3D4D712ED; Fri, 12 Apr 2019 13:06:21 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@mail.linuxfoundation.org Received: from smtp1.linuxfoundation.org (smtp1.linux-foundation.org [172.17.192.35]) by mail.linuxfoundation.org (Postfix) with ESMTPS id 18DE512D6 for ; Fri, 12 Apr 2019 13:06:20 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.7.6 Received: from mx1.redhat.com (mx1.redhat.com [209.132.183.28]) by smtp1.linuxfoundation.org (Postfix) with ESMTPS id C06C9854 for ; Fri, 12 Apr 2019 13:06:10 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 161C3811D5 for ; Fri, 12 Apr 2019 13:06:10 +0000 (UTC) Received: from nusiddiq.mac (unknown [10.74.10.41]) by smtp.corp.redhat.com (Postfix) with ESMTP id 3CD4C60FCD; Fri, 12 Apr 2019 13:06:07 +0000 (UTC) From: nusiddiq@redhat.com To: dev@openvswitch.org Date: Fri, 12 Apr 2019 18:35:59 +0530 Message-Id: <20190412130559.28693-1-nusiddiq@redhat.com> In-Reply-To: <20190412130447.28304-1-nusiddiq@redhat.com> References: <20190412130447.28304-1-nusiddiq@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.12 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.27]); Fri, 12 Apr 2019 13:06:10 +0000 (UTC) X-Spam-Status: No, score=-6.9 required=5.0 tests=BAYES_00,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on smtp1.linux-foundation.org Subject: [ovs-dev] [PATCH v2 6/6] ovn: Generate ICMPv4 packet in router pipeline for larger packets X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: ovs-dev-bounces@openvswitch.org Errors-To: ovs-dev-bounces@openvswitch.org From: Numan Siddique This patch adds 2 stages in router pipeline after ARP_RESOLVE and adds the logical flows to check the packet length and generate ICMPv4 packet. * S_ROUTER_IN_CHK_PKT_LEN - Which checks the packet length using check_pkt_larger OVN action * S_ROUTER_IN_LARGER_PKTS - Which generates icmp packet with type 3 (Destination Unreachable), code 4 (Frag Needed and DF was Set) icmp4.frag_mtu = gw_mtu In order to add these logical flows, CMS should set the option 'gateway_mtu' for the distributed logical router port. Signed-off-by: Numan Siddique Acked-by: Mark Michelson --- ovn/northd/ovn-northd.8.xml | 83 +++++++++++++++++- ovn/northd/ovn-northd.c | 92 +++++++++++++++++++- tests/ovn.at | 167 ++++++++++++++++++++++++++++++++++++ 3 files changed, 336 insertions(+), 6 deletions(-) diff --git a/ovn/northd/ovn-northd.8.xml b/ovn/northd/ovn-northd.8.xml index 392a5efc9..345023eb4 100644 --- a/ovn/northd/ovn-northd.8.xml +++ b/ovn/northd/ovn-northd.8.xml @@ -2002,7 +2002,86 @@ next; -

Ingress Table 9: Gateway Redirect

+

Ingress Table 9: Check packet length

+ +

+ For distributed logical routers with distributed gateway port configured + with options:gateway_mtu to a valid integer value, this + table adds a priority-50 logical flow with the match + ip4 && outport == GW_PORT where + GW_PORT is the distributed gateway router port and applies the + action check_pkt_larger and advances the packet to the + next table. +

+ +
+REGBIT_PKT_LARGER = check_pkt_larger(L); next;
+    
+ +

+ where L is the packet length to check for. If the packet + is larger than L, it stores 1 in the register bit + REGBIT_PKT_LARGER. The value of + L is taken from column of + row. +

+ +

+ This table adds one priority-0 fallback flow that matches all packets + and advances to the next table. +

+ +

Ingress Table 10: Handle larger packets

+ +

+ For distributed logical routers with distributed gateway port configured + with options:gateway_mtu to a valid integer value, this + table adds the following priority-50 logical flow for each + logical router port with the match ip4 && + inport == LRP && outport == GW_PORT + && REGBIT_PKT_LARGER, where LRP is the logical + router port and GW_PORT is the distributed gateway router port + and applies the following action +

+ +
+icmp4 {
+    icmp4.type = 3; /* Destination Unreachable. */
+    icmp4.code = 4;  /* Frag Needed and DF was Set. */
+    icmp4.frag_mtu = M;
+    eth.dst = E;
+    ip4.dst = ip4.src;
+    ip4.src = I;
+    ip.ttl = 255;
+    REGBIT_EGRESS_LOOPBACK = 1;
+    next(pipeline=ingress, table=0);
+};
+    
+ +
    +
  • + Where M is the (fragment MTU - 58) whose value is taken from + column of + row. +
  • + +
  • + E is the Ethernet address of the logical router port. +
  • + +
  • + I is the IPv4 address of the logical router port. +
  • +
+ +

+ This table adds one priority-0 fallback flow that matches all packets + and advances to the next table. +

+ +

Ingress Table 11: Gateway Redirect

For distributed logical routers where one of the logical router @@ -2059,7 +2138,7 @@ next; -

Ingress Table 10: ARP Request

+

Ingress Table 12: ARP Request

In the common case where the Ethernet destination has been resolved, this diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c index 5614f9fa3..92e0e9c9d 100644 --- a/ovn/northd/ovn-northd.c +++ b/ovn/northd/ovn-northd.c @@ -142,8 +142,10 @@ enum ovn_stage { PIPELINE_STAGE(ROUTER, IN, ND_RA_RESPONSE, 6, "lr_in_nd_ra_response") \ PIPELINE_STAGE(ROUTER, IN, IP_ROUTING, 7, "lr_in_ip_routing") \ PIPELINE_STAGE(ROUTER, IN, ARP_RESOLVE, 8, "lr_in_arp_resolve") \ - PIPELINE_STAGE(ROUTER, IN, GW_REDIRECT, 9, "lr_in_gw_redirect") \ - PIPELINE_STAGE(ROUTER, IN, ARP_REQUEST, 10, "lr_in_arp_request") \ + PIPELINE_STAGE(ROUTER, IN, CHK_PKT_LEN , 9, "lr_in_chk_pkt_len") \ + PIPELINE_STAGE(ROUTER, IN, LARGER_PKTS, 10,"lr_in_larger_pkts") \ + PIPELINE_STAGE(ROUTER, IN, GW_REDIRECT, 11, "lr_in_gw_redirect") \ + PIPELINE_STAGE(ROUTER, IN, ARP_REQUEST, 12, "lr_in_arp_request") \ \ /* Logical router egress stages. */ \ PIPELINE_STAGE(ROUTER, OUT, UNDNAT, 0, "lr_out_undnat") \ @@ -179,6 +181,8 @@ enum ovn_stage { * logical router dropping packets with source IP address equals * one of the logical router's own IP addresses. */ #define REGBIT_EGRESS_LOOPBACK "reg9[1]" +/* Register to store the result of check_pkt_larger action. */ +#define REGBIT_PKT_LARGER "reg9[2]" /* Returns an "enum ovn_stage" built from the arguments. */ static enum ovn_stage @@ -6595,7 +6599,87 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, "get_nd(outport, xxreg0); next;"); } - /* Logical router ingress table 9: Gateway redirect. + /* Local router ingress table 9: Check packet length. + * + * Any IPv4 packet with outport set to the distributed gateway + * router port, check the packet length and store the result in the + * 'REGBIT_PKT_LARGER' register bit. + * + * Local router ingress table 10: Handle larger packets. + * + * Any IPv4 packet with outport set to the distributed gateway + * router port and the 'REGBIT_PKT_LARGER' register bit is set, + * generate ICMPv4 packet with type 3 (Destination Unreachable) and + * code 4 (Fragmentation needed). + * */ + HMAP_FOR_EACH (od, key_node, datapaths) { + if (!od->nbr) { + continue; + } + + /* Packets are allowed by default. */ + ovn_lflow_add(lflows, od, S_ROUTER_IN_CHK_PKT_LEN, 0, "1", + "next;"); + ovn_lflow_add(lflows, od, S_ROUTER_IN_LARGER_PKTS, 0, "1", + "next;"); + + if (od->l3dgw_port && od->l3redirect_port) { + int gw_mtu = 0; + if (od->l3dgw_port->nbrp) { + gw_mtu = smap_get_int(&od->l3dgw_port->nbrp->options, + "gateway_mtu", 0); + } + /* Add the flows only if gateway_mtu is configured. */ + if (gw_mtu <= 0) { + continue; + } + + ds_clear(&match); + ds_put_format(&match, "outport == %s && ip4", + od->l3dgw_port->json_key); + + ds_clear(&actions); + ds_put_format(&actions, + REGBIT_PKT_LARGER" = check_pkt_larger(%d);" + " next;", gw_mtu); + ovn_lflow_add(lflows, od, S_ROUTER_IN_CHK_PKT_LEN, 50, + ds_cstr(&match), ds_cstr(&actions)); + + for (size_t i = 0; i < od->nbr->n_ports; i++) { + struct ovn_port *rp = ovn_port_find(ports, + od->nbr->ports[i]->name); + if (!rp || rp == od->l3dgw_port) { + continue; + } + ds_clear(&match); + ds_put_format(&match, "inport == %s && outport == %s && ip4 " + "&& "REGBIT_PKT_LARGER, + rp->json_key, od->l3dgw_port->json_key); + + ds_clear(&actions); + /* Set icmp4.frag_mtu to gw_mtu - 58. 58 is the Geneve tunnel + * overhead. */ + ds_put_format(&actions, + "icmp4_error {" + REGBIT_EGRESS_LOOPBACK" = 1; " + "eth.dst = %s; " + "ip4.dst = ip4.src; " + "ip4.src = %s; " + "ip.ttl = 255; " + "icmp4.type = 3; /* Destination Unreachable. */ " + "icmp4.code = 4; /* Frag Needed and DF was Set. */ " + "icmp4.frag_mtu = %d; " + "next(pipeline=ingress, table=0); };", + rp->lrp_networks.ea_s, + rp->lrp_networks.ipv4_addrs[0].addr_s, + gw_mtu - 18); + ovn_lflow_add(lflows, od, S_ROUTER_IN_LARGER_PKTS, 50, + ds_cstr(&match), ds_cstr(&actions)); + } + } + } + + /* Logical router ingress table 11: Gateway redirect. * * For traffic with outport equal to the l3dgw_port * on a distributed router, this table redirects a subset @@ -6635,7 +6719,7 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, ovn_lflow_add(lflows, od, S_ROUTER_IN_GW_REDIRECT, 0, "1", "next;"); } - /* Local router ingress table 10: ARP request. + /* Local router ingress table 12: ARP request. * * In the common case where the Ethernet destination has been resolved, * this table outputs the packet (priority 0). Otherwise, it composes diff --git a/tests/ovn.at b/tests/ovn.at index 043697120..acb8c832d 100644 --- a/tests/ovn.at +++ b/tests/ovn.at @@ -11972,6 +11972,7 @@ as hv2 start_daemon ovn-controller OVN_CLEANUP([hv1],[hv2]) + AT_CLEANUP AT_SETUP([ovn -- ovn-nbctl duplicate addresses]) @@ -12030,6 +12031,172 @@ AT_CHECK([ovn-nbctl lsp-set-addresses sw2-p1 "00:00:00:00:00:04 aef0::1"]) AT_CLEANUP +AT_SETUP([ovn -- router - check packet length - icmp defrag]) +AT_KEYWORDS([check packet length]) +AT_SKIP_IF([test $HAVE_PYTHON = no]) +ovn_start + +ovn-nbctl ls-add sw0 +ovn-nbctl lsp-add sw0 sw0-port1 +ovn-nbctl lsp-set-addresses sw0-port1 "50:54:00:00:00:01 10.0.0.3" + +ovn-nbctl lr-add lr0 +ovn-nbctl lrp-add lr0 lr0-sw0 00:00:00:00:ff:01 10.0.0.1/24 +ovn-nbctl lsp-add sw0 sw0-lr0 +ovn-nbctl lsp-set-type sw0-lr0 router +ovn-nbctl lsp-set-addresses sw0-lr0 router +ovn-nbctl lsp-set-options sw0-lr0 router-port=lr0-sw0 + +ovn-nbctl ls-add public +ovn-nbctl lrp-add lr0 lr0-public 00:00:20:20:12:13 172.168.0.100/24 +ovn-nbctl lsp-add public public-lr0 +ovn-nbctl lsp-set-type public-lr0 router +ovn-nbctl lsp-set-addresses public-lr0 router +ovn-nbctl lsp-set-options public-lr0 router-port=lr0-public + +# localnet port +ovn-nbctl lsp-add public ln-public +ovn-nbctl lsp-set-type ln-public localnet +ovn-nbctl lsp-set-addresses ln-public unknown +ovn-nbctl lsp-set-options ln-public network_name=phys + +ovn-nbctl lrp-set-gateway-chassis lr0-public hv1 20 +ovn-nbctl lr-nat-add lr0 snat 172.168.0.100 10.0.0.0/24 + +net_add n1 + +sim_add hv1 +as hv1 +ovs-vsctl add-br br-phys +ovn_attach n1 br-phys 192.168.0.1 +ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys +ovs-vsctl -- add-port br-int hv1-vif1 -- \ + set interface hv1-vif1 external-ids:iface-id=sw0-port1 \ + options:tx_pcap=hv1/vif1-tx.pcap \ + options:rxq_pcap=hv1/vif1-rx.pcap \ + ofport-request=1 + +reset_pcap_file() { + local iface=$1 + local pcap_file=$2 + ovs-vsctl -- set Interface $iface options:tx_pcap=dummy-tx.pcap \ + options:rxq_pcap=dummy-rx.pcap + rm -f ${pcap_file}*.pcap + ovs-vsctl -- set Interface $iface options:tx_pcap=${pcap_file}-tx.pcap \ + options:rxq_pcap=${pcap_file}-rx.pcap +} + +ip_to_hex() { + printf "%02x%02x%02x%02x" "$@" +} + +test_ip_packet_larger() { + local icmp_pmtu_reply_expected=$1 + + # Send ip packet from sw0-port1 to outside + src_mac="505400000001" # sw-port1 mac + dst_mac="00000000ff01" # sw0-lr0 mac (internal router leg) + src_ip=`ip_to_hex 10 0 0 3` + dst_ip=`ip_to_hex 172 168 0 3` + # Set the packet length to 100. + pkt_len=0064 + packet=${dst_mac}${src_mac}08004500${pkt_len}0000000040010000 + orig_packet_l3=${src_ip}${dst_ip}0304000000000000 + orig_packet_l3=${orig_packet_l3}000000000000000000000000000000000000 + orig_packet_l3=${orig_packet_l3}000000000000000000000000000000000000 + orig_packet_l3=${orig_packet_l3}000000000000000000000000000000000000 + orig_packet_l3=${orig_packet_l3}000000000000000000000000000000000000 + packet=${packet}${orig_packet_l3} + + # If icmp_pmtu_reply_expected is 0, it means the packet is lesser than + # the gateway mtu and should be delivered to the provider bridge via the + # localnet port. + # If icmp_pmtu_reply_expected is 1, it means the packet is larger than + # the gateway mtu and ovn-controller should drop the packet and instead + # generate ICMPv4 Destination Unreachable message with pmtu set to 42. + if test $icmp_pmtu_reply_expected = 0; then + # Packet to expect at br-phys. + src_mac="000020201213" + dst_mac="00000012af11" + src_ip=`ip_to_hex 10 0 0 3` + dst_ip=`ip_to_hex 172 168 0 3` + expected=${dst_mac}${src_mac}08004500${pkt_len}000000003f010100 + expected=${expected}${src_ip}${dst_ip}0304000000000000 + expected=${expected}000000000000000000000000000000000000 + expected=${expected}000000000000000000000000000000000000 + expected=${expected}000000000000000000000000000000000000 + expected=${expected}000000000000000000000000000000000000 + echo $expected > br_phys_n1.expected + else + # MTU would be 100 - 18 = 82 (hex 0052) + mtu=0052 + src_ip=`ip_to_hex 10 0 0 1` + dst_ip=`ip_to_hex 10 0 0 3` + # pkt len should be 128 (28 (icmp packet) + 100 (orig ip + payload)) + reply_pkt_len=0080 + ip_csum=bd91 + icmp_reply=${src_mac}${dst_mac}08004500${reply_pkt_len}00004000fe016879 + icmp_reply=${icmp_reply}${src_ip}${dst_ip}0304${ip_csum}0000${mtu} + icmp_reply=${icmp_reply}4500${pkt_len}000000003f010100 + icmp_reply=${icmp_reply}${orig_packet_l3} + echo $icmp_reply > hv1-vif1.expected + fi + + as hv1 reset_pcap_file br-phys_n1 hv1/br-phys_n1 + as hv1 reset_pcap_file hv1-vif1 hv1/vif1 + + # Send packet from sw0-port1 to outside + as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 $packet + + if test $icmp_pmtu_reply_expected = 0; then + OVN_CHECK_PACKETS([hv1/br-phys_n1-tx.pcap], [br_phys_n1.expected]) + $PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif1-tx.pcap > packets + AT_CHECK([cat packets], [0], []) + else + OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [hv1-vif1.expected]) + $PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/br-phys_n1-tx.pcap > \ + packets + AT_CHECK([cat packets], [0], []) + fi +} + +ovn-nbctl show +ovn-sbctl show + +AT_CHECK([as hv1 ovs-ofctl dump-flows br-int \ +| grep "check_pkt_larger" | wc -l], [0], [[0 +]]) +dp_uuid=$(ovn-sbctl find datapath_binding | grep sw0 -B2 | grep _uuid | \ +awk '{print $3}') +ovn-sbctl create MAC_Binding ip=172.168.0.3 datapath=$dp_uuid \ +logical_port=lr0-public mac="00\:00\:00\:12\:af\:11" + +# Set the gateway mtu to 100. If the packet length is > 100, ovn-controller +# should send icmp host not reachable with pmtu set to 100. +ovn-nbctl --wait=hv set logical_router_port lr0-public options:gateway_mtu=100 +as hv3 ovs-appctl netdev-dummy/receive hv3-vif1 $arp_reply +OVS_WAIT_UNTIL([ + test `as hv1 ovs-ofctl dump-flows br-int | grep "check_pkt_larger(100)" | \ + wc -l` -eq 1 +]) + +icmp_reply_expected=1 +test_ip_packet_larger $icmp_reply_expected + +# Set the gateway mtu to 500. +ovn-nbctl --wait=hv set logical_router_port lr0-public options:gateway_mtu=500 +as hv3 ovs-appctl netdev-dummy/receive hv3-vif1 $arp_reply +OVS_WAIT_UNTIL([ + test `as hv1 ovs-ofctl dump-flows br-int | grep "check_pkt_larger(500)" | \ + wc -l` -eq 1 +]) + +# Now the packet should be sent via the localnet port to br-phys. +icmp_reply_expected=0 +test_ip_packet_larger $icmp_reply_expected +OVN_CLEANUP([hv1]) +AT_CLEANUP + AT_SETUP([ovn -- IP packet buffering]) AT_KEYWORDS([ip-buffering]) AT_SKIP_IF([test $HAVE_PYTHON = no])