From patchwork Thu Jan 3 18:18:00 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Numan Siddique X-Patchwork-Id: 1020466 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 43Vx4H0TFDz9s3l for ; Fri, 4 Jan 2019 05:18:23 +1100 (AEDT) Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id E089DC6F; Thu, 3 Jan 2019 18:18:18 +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 15B0F49F for ; Thu, 3 Jan 2019 18:18:18 +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 EAC8F76A for ; Thu, 3 Jan 2019 18:18:16 +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 5DB8AC0A1AE8; Thu, 3 Jan 2019 18:18:16 +0000 (UTC) Received: from nusiddiq.mac (ovpn-116-36.sin2.redhat.com [10.67.116.36]) by smtp.corp.redhat.com (Postfix) with ESMTP id 4D7FD60C47; Thu, 3 Jan 2019 18:18:14 +0000 (UTC) From: nusiddiq@redhat.com To: dev@openvswitch.org Date: Thu, 3 Jan 2019 23:48:00 +0530 Message-Id: <20190103181800.17328-1-nusiddiq@redhat.com> In-Reply-To: <20190103181729.17206-1-nusiddiq@redhat.com> References: <20190103181729.17206-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.31]); Thu, 03 Jan 2019 18:18:16 +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] [RFC v2 1/5] 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 [Please note, this patch is submitted as RFC in ovs-dev ML to get feedback before submitting to netdev ML] This patch adds a new action 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 (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'. 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: Ben Pfaff CC: Justin Pettit CC: Pravin B Shelar --- include/uapi/linux/openvswitch.h | 25 ++++- net/openvswitch/actions.c | 55 +++++++++- net/openvswitch/flow_netlink.c | 175 +++++++++++++++++++++++++++++++ 3 files changed, 253 insertions(+), 2 deletions(-) diff --git a/include/uapi/linux/openvswitch.h b/include/uapi/linux/openvswitch.h index dbe0cbe4f1b7..c395baffdd42 100644 --- a/include/uapi/linux/openvswitch.h +++ b/include/uapi/linux/openvswitch.h @@ -798,6 +798,27 @@ struct ovs_action_push_eth { struct ovs_key_ethernet addresses; }; +/* + * 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_ACTIONS_IF_GREATER: Nested OVS_ACTION_ATTR_* + * actions to apply if the packer length is greater than the specified + * length in the attr - OVS_CHECK_PKT_LEN_ATTR_PKT_LEN. + * @OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL - Nested OVS_ACTION_ATTR_* + * actions to apply if the packer length is lesser or equal to the specified + * length in the attr - OVS_CHECK_PKT_LEN_ATTR_PKT_LEN. + */ +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. * @@ -842,7 +863,8 @@ struct ovs_action_push_eth { * 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 length of the packet and + * execute a set of actions as specified in OVS_CHECK_PKT_LEN_ATTR_*. * Only a single header can be set with a single %OVS_ACTION_ATTR_SET. Not all * fields within a header are modifiable, e.g. the IPv4 protocol and fragment * type may not be changed. @@ -875,6 +897,7 @@ enum ovs_action_attr { OVS_ACTION_ATTR_PUSH_NSH, /* Nested OVS_NSH_KEY_ATTR_*. */ OVS_ACTION_ATTR_POP_NSH, /* No argument. */ OVS_ACTION_ATTR_METER, /* u32 meter ID. */ + OVS_ACTION_ATTR_CHECK_PKT_LEN, /* Nested OVS_CHECK_PKT_LEN_ATTR_*. */ OVS_ACTION_ATTR_CLONE, /* Nested OVS_CLONE_ATTR_*. */ __OVS_ACTION_ATTR_MAX, /* Nothing past this will be accepted diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c index e47ebbbe71b8..9551c07eae92 100644 --- a/net/openvswitch/actions.c +++ b/net/openvswitch/actions.c @@ -169,6 +169,10 @@ static int clone_execute(struct datapath *dp, struct sk_buff *skb, const struct nlattr *actions, int len, bool last, bool clone_flow_key); +static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, + struct sw_flow_key *key, + const struct nlattr *attr, int len); + static void update_ethertype(struct sk_buff *skb, struct ethhdr *hdr, __be16 ethertype) { @@ -1213,6 +1217,46 @@ 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 *a; + const struct nlattr *attrs[OVS_CHECK_PKT_LEN_ATTR_MAX + 1]; + u16 actual_pkt_len; + u16 pkt_len = 0; + int rem; + + memset(attrs, 0, sizeof(attrs)); + nla_for_each_nested(a, attr, rem) { + int type = nla_type(a); + + if (!type || type > OVS_CHECK_PKT_LEN_ATTR_MAX || attrs[type]) + return 1; + attrs[type] = a; + } + if (rem) + return 1; + + if (!attrs[OVS_CHECK_PKT_LEN_ATTR_PKT_LEN]) + return 1; + + a = attrs[OVS_CHECK_PKT_LEN_ATTR_PKT_LEN]; + pkt_len = nla_get_u16(a); + actual_pkt_len = skb->len + (skb_vlan_tag_present(skb) ? VLAN_HLEN : 0); + + if (actual_pkt_len > pkt_len) + a = attrs[OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER]; + else + a = attrs[OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL]; + + if (a) + return clone_execute(dp, skb, key, 0, nla_data(a), nla_len(a), + last, !last); + + return 0; +} + /* Execute a list of actions against 'skb'. */ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, struct sw_flow_key *key, @@ -1374,8 +1418,17 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, 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)) { kfree_skb(skb); return err; diff --git a/net/openvswitch/flow_netlink.c b/net/openvswitch/flow_netlink.c index 435a4bdf8f89..93b8e91315da 100644 --- a/net/openvswitch/flow_netlink.c +++ b/net/openvswitch/flow_netlink.c @@ -91,6 +91,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; } @@ -2838,6 +2839,93 @@ static int validate_userspace(const struct nlattr *attr) return 0; } +static int copy_action(const struct nlattr *from, + struct sw_flow_actions **sfa, bool log); + +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) +{ + const struct nlattr *attrs[OVS_CHECK_PKT_LEN_ATTR_MAX + 1]; + const struct nlattr *a; + const struct nlattr *pkt_len, *acts_if_greater, *acts_if_lesser_eq; + int rem, start, err, nested_acts_start; + + memset(attrs, 0, sizeof(attrs)); + nla_for_each_nested(a, attr, rem) { + int type = nla_type(a); + + if (!type || type > OVS_CHECK_PKT_LEN_ATTR_MAX || attrs[type]) + return -EINVAL; + attrs[type] = a; + } + if (rem) + return -EINVAL; + + pkt_len = attrs[OVS_CHECK_PKT_LEN_ATTR_PKT_LEN]; + if (!pkt_len || nla_len(pkt_len) != sizeof(u16)) + return -EINVAL; + + acts_if_greater = attrs[OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER]; + if (acts_if_greater && nla_len(acts_if_greater) && + nla_len(acts_if_greater) < NLA_HDRLEN) + return -EINVAL; + + acts_if_lesser_eq = attrs[OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL]; + if (acts_if_lesser_eq && nla_len(acts_if_lesser_eq) && + nla_len(acts_if_lesser_eq) < NLA_HDRLEN) + 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; + + err = copy_action(pkt_len, sfa, log); + if (err) + return err; + + if (acts_if_greater) { + 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); + } + + if (acts_if_lesser_eq) { + 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); + } + + add_nested_action_end(*sfa, start); + return 0; +} + static int copy_action(const struct nlattr *from, struct sw_flow_actions **sfa, bool log) { @@ -2884,6 +2972,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); @@ -3085,6 +3174,15 @@ static int __ovs_nla_copy_actions(struct net *net, const struct nlattr *attr, break; } + case OVS_ACTION_ATTR_CHECK_PKT_LEN: + err = validate_and_copy_check_pkt_len(net, a, key, sfa, + eth_type, + vlan_tci, log); + if (err) + return err; + skip_copy = true; + break; + default: OVS_NLERR(log, "Unknown Action type %d", type); return -EINVAL; @@ -3183,6 +3281,77 @@ 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; + int err = -1, rem; + const struct nlattr *a; + const struct nlattr *attrs[OVS_CHECK_PKT_LEN_ATTR_MAX + 1]; + + memset(attrs, 0, sizeof(attrs)); + nla_for_each_nested(a, attr, rem) { + int type = nla_type(a); + + if (!type || type > OVS_CHECK_PKT_LEN_ATTR_MAX || attrs[type]) + return err; + attrs[type] = a; + } + if (rem) + return err; + + a = attrs[OVS_CHECK_PKT_LEN_ATTR_PKT_LEN]; + if (!a) + return err; + + err = -EMSGSIZE; + start = nla_nest_start(skb, OVS_ACTION_ATTR_CHECK_PKT_LEN); + if (!start) + return err; + + if (nla_put_u16(skb, OVS_CHECK_PKT_LEN_ATTR_PKT_LEN, nla_get_u16(a))) + goto out; + + a = attrs[OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER]; + if (a) { + ac_start = nla_nest_start(skb, + OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER); + if (!ac_start) + goto out; + + if (ovs_nla_put_actions(nla_data(a), nla_len(a), skb)) { + nla_nest_cancel(skb, ac_start); + goto out; + } else { + nla_nest_end(skb, ac_start); + } + } + + a = attrs[OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL]; + if (a) { + ac_start = nla_nest_start(skb, + OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL); + if (!ac_start) + goto out; + + if (ovs_nla_put_actions(nla_data(a), nla_len(a), skb)) { + nla_nest_cancel(skb, ac_start); + goto out; + } else { + nla_nest_end(skb, ac_start); + } + } + + err = 0; +out: + if (err) + nla_nest_cancel(skb, start); + else + nla_nest_end(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); @@ -3277,6 +3446,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; From patchwork Thu Jan 3 18:18:29 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Numan Siddique X-Patchwork-Id: 1020467 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 43Vx561g0Lz9s3l for ; Fri, 4 Jan 2019 05:19:06 +1100 (AEDT) Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id B2B2BBE1; Thu, 3 Jan 2019 18:18:38 +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 BE124D20 for ; Thu, 3 Jan 2019 18:18:36 +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 2F2A3802 for ; Thu, 3 Jan 2019 18:18:35 +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 AD30DB0828; Thu, 3 Jan 2019 18:18:34 +0000 (UTC) Received: from nusiddiq.mac (ovpn-116-36.sin2.redhat.com [10.67.116.36]) by smtp.corp.redhat.com (Postfix) with ESMTP id DBBD119C7E; Thu, 3 Jan 2019 18:18:32 +0000 (UTC) From: nusiddiq@redhat.com To: dev@openvswitch.org Date: Thu, 3 Jan 2019 23:48:29 +0530 Message-Id: <20190103181829.17420-1-nusiddiq@redhat.com> In-Reply-To: <20190103181729.17206-1-nusiddiq@redhat.com> References: <20190103181729.17206-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.39]); Thu, 03 Jan 2019 18:18:34 +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] [RFC v2 2/5] 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 is still WIP. 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 Suppose if a packet is received from in_port=1, the packet is sent to userspace (MISS_UPCALL). The action 'check_pkt_larger' will be translated as - check_pkt_len( > 100 ? (3) : (4)) datapath will check the packet length and if the packet length is greater than 100, it will be output to port 3, else it will be 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 [1] TODO: - Add test cases. - Add documentation Request to suggest a better name for the action in case 'check_pkt_larger' seems odd. [1] - 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 --- .../linux/compat/include/linux/openvswitch.h | 30 +++-- include/openvswitch/ofp-actions.h | 18 +++ lib/dpif-netdev.c | 1 + lib/dpif.c | 1 + lib/odp-execute.c | 4 + lib/odp-util.c | 36 ++++++ lib/ofp-actions.c | 98 +++++++++++++++- lib/ofp-parse.c | 10 ++ ofproto/ofproto-dpif-ipfix.c | 1 + ofproto/ofproto-dpif-sflow.c | 1 + ofproto/ofproto-dpif-xlate.c | 108 ++++++++++++++++++ ofproto/ofproto-dpif.c | 47 ++++++++ ofproto/ofproto-dpif.h | 5 +- 13 files changed, 349 insertions(+), 11 deletions(-) diff --git a/datapath/linux/compat/include/linux/openvswitch.h b/datapath/linux/compat/include/linux/openvswitch.h index 9b087f1b0..26dd69dcd 100644 --- a/datapath/linux/compat/include/linux/openvswitch.h +++ b/datapath/linux/compat/include/linux/openvswitch.h @@ -855,6 +855,24 @@ enum ovs_nat_attr { #define OVS_NAT_ATTR_MAX (__OVS_NAT_ATTR_MAX - 1) +/* + * enum ovs_check_pkt_len_attr - Attributes for %OVS_ACTION_ATTR_CHECK_PKT_LEN. + * + * @OVS_CHECK_PKT_LEN_ATTR_PKT_LEN: u16 Packet length to check for. + * @OVS_CHECK_PKT_LEN_ATTR_USERSPACE_COND: u8 comparison condition to send + * the packet to userspace. One of OVS_CHECK_PKT_LEN_COND_*. + * @OVS_CHECK_PKT_LEN_ATTR_USERPACE - Nested OVS_USERSPACE_ATTR_* actions. + */ +enum ovs_check_pkt_len_attr { + OVS_CHECK_PKT_LEN_ATTR_UNSPEC, + OVS_CHECK_PKT_LEN_ATTR_PKT_LEN, + OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER, + OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL, + __OVS_CHECK_PKT_LEN_ATTR_MAX, +}; + +#define OVS_CHECK_PKT_LEN_ATTR_MAX (__OVS_CHECK_PKT_LEN_ATTR_MAX - 1) + /** * enum ovs_action_attr - Action types. * @@ -909,8 +927,6 @@ enum ovs_nat_attr { * tunnel header. * @OVS_ACTION_ATTR_METER: Run packet through a meter, which may drop the * packet, or modify the packet (e.g., change the DSCP field). - * @OVS_ACTION_ATTR_CLONE: make a copy of the packet and execute a list of - * actions without affecting the original packet and key. */ enum ovs_action_attr { @@ -936,12 +952,13 @@ enum ovs_action_attr { OVS_ACTION_ATTR_CT_CLEAR, /* No argument. */ OVS_ACTION_ATTR_PUSH_NSH, /* Nested OVS_NSH_KEY_ATTR_*. */ OVS_ACTION_ATTR_POP_NSH, /* No argument. */ - OVS_ACTION_ATTR_METER, /* u32 meter number. */ - OVS_ACTION_ATTR_CLONE, /* Nested OVS_CLONE_ATTR_*. */ + OVS_ACTION_ATTR_METER, /* u32 meter number. */ + OVS_ACTION_ATTR_CHECK_PKT_LEN, /* Nested OVS_CHECK_PKT_LEN_ATTR_*. */ #ifndef __KERNEL__ OVS_ACTION_ATTR_TUNNEL_PUSH, /* struct ovs_action_push_tnl*/ OVS_ACTION_ATTR_TUNNEL_POP, /* u32 port number. */ + OVS_ACTION_ATTR_CLONE, /* Nested OVS_CLONE_ATTR_*. */ #endif __OVS_ACTION_ATTR_MAX, /* Nothing past this will be accepted * from userspace. */ @@ -1034,9 +1051,4 @@ struct ovs_zone_limit { __u32 count; }; -#define OVS_CLONE_ATTR_EXEC 0 /* Specify an u32 value. When nonzero, - * actions in clone will not change flow - * keys. False otherwise. - */ - #endif /* _LINUX_OPENVSWITCH_H */ diff --git a/include/openvswitch/ofp-actions.h b/include/openvswitch/ofp-actions.h index 4daf5ad07..7e063afff 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 bool +ofpact_remaining_len(const struct ofpact *a, const struct ofpact *ofpacts, + size_t ofpact_len) +{ + return ofpact_len - ((uint8_t *)a - (uint8_t *)ofpacts); +} + static inline const struct ofpact * ofpact_find_type_flattened(const struct ofpact *a, enum ofpact_type type, const struct ofpact * const end) @@ -620,6 +629,15 @@ struct ofpact_meter { ); }; +/* OFPACT_CHECK_PKT_LARGER. + * + * Used for NXAST_CHECK_PKT_LARGER. */ +struct ofpact_check_pkt_larger { + struct ofpact ofpact; + uint16_t pkt_len; + struct mf_subfield dst; +}; + /* OFPACT_WRITE_ACTIONS, OFPACT_CLONE. * * Used for OFPIT11_WRITE_ACTIONS, NXAST_CLONE. */ diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c index 1564db9c6..931ae95b9 100644 --- a/lib/dpif-netdev.c +++ b/lib/dpif-netdev.c @@ -6814,6 +6814,7 @@ dp_execute_cb(void *aux_, struct dp_packet_batch *packets_, case OVS_ACTION_ATTR_PUSH_NSH: case OVS_ACTION_ATTR_POP_NSH: case OVS_ACTION_ATTR_CT_CLEAR: + case OVS_ACTION_ATTR_CHECK_PKT_LEN: case __OVS_ACTION_ATTR_MAX: OVS_NOT_REACHED(); } diff --git a/lib/dpif.c b/lib/dpif.c index e35f11147..8d0e303cc 100644 --- a/lib/dpif.c +++ b/lib/dpif.c @@ -1274,6 +1274,7 @@ dpif_execute_helper_cb(void *aux_, struct dp_packet_batch *packets_, case OVS_ACTION_ATTR_POP_NSH: case OVS_ACTION_ATTR_CT_CLEAR: case OVS_ACTION_ATTR_UNSPEC: + case OVS_ACTION_ATTR_CHECK_PKT_LEN: case __OVS_ACTION_ATTR_MAX: OVS_NOT_REACHED(); } diff --git a/lib/odp-execute.c b/lib/odp-execute.c index 3b6890e95..29fc4435a 100644 --- a/lib/odp-execute.c +++ b/lib/odp-execute.c @@ -673,6 +673,7 @@ requires_datapath_assistance(const struct nlattr *a) case OVS_ACTION_ATTR_PUSH_NSH: case OVS_ACTION_ATTR_POP_NSH: case OVS_ACTION_ATTR_CT_CLEAR: + case OVS_ACTION_ATTR_CHECK_PKT_LEN: return false; case OVS_ACTION_ATTR_UNSPEC: @@ -900,6 +901,9 @@ odp_execute_actions(void *dp, struct dp_packet_batch *batch, bool steal, } break; + case OVS_ACTION_ATTR_CHECK_PKT_LEN: { + break; + } case OVS_ACTION_ATTR_OUTPUT: case OVS_ACTION_ATTR_TUNNEL_PUSH: case OVS_ACTION_ATTR_TUNNEL_POP: diff --git a/lib/odp-util.c b/lib/odp-util.c index cb6a5f204..d16f7e2a2 100644 --- a/lib/odp-util.c +++ b/lib/odp-util.c @@ -131,6 +131,7 @@ odp_action_len(uint16_t type) case OVS_ACTION_ATTR_CLONE: return ATTR_LEN_VARIABLE; case OVS_ACTION_ATTR_PUSH_NSH: return ATTR_LEN_VARIABLE; case OVS_ACTION_ATTR_POP_NSH: return 0; + case OVS_ACTION_ATTR_CHECK_PKT_LEN: return ATTR_LEN_VARIABLE; case OVS_ACTION_ATTR_UNSPEC: case __OVS_ACTION_ATTR_MAX: @@ -1042,6 +1043,38 @@ format_odp_set_nsh(struct ds *ds, const struct nlattr *attr) ds_put_cstr(ds, "))"); } +static void +format_odp_check_pkt_len_action(struct ds *ds, const struct nlattr *attr, + const struct hmap *portno_names OVS_UNUSED) +{ + static const struct nl_policy ovs_cpl_policy[] = { + [OVS_CHECK_PKT_LEN_ATTR_PKT_LEN] = { .type = NL_A_U16 }, + }; + struct nlattr *a[ARRAY_SIZE(ovs_cpl_policy)]; + ds_put_cstr(ds, "check_pkt_len"); + if (!nl_parse_nested(attr, ovs_cpl_policy, a, ARRAY_SIZE(a))) { + ds_put_cstr(ds, "(error)"); + return; + } + + uint16_t pkt_len = nl_attr_get_u16(a[OVS_CHECK_PKT_LEN_ATTR_PKT_LEN]); + ds_put_format(ds, "(> %u ? (", pkt_len); + const struct nlattr *acts; + acts = nl_attr_find_nested(attr, + OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER); + if (acts) { + format_odp_actions(ds, nl_attr_get(acts), nl_attr_get_size(acts), + portno_names); + } + ds_put_cstr(ds, ") : ("); + acts = nl_attr_find_nested(attr, + OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL); + if (acts) { + format_odp_actions(ds, nl_attr_get(acts), nl_attr_get_size(acts), + portno_names); + } + ds_put_cstr(ds, "))"); +} static void format_odp_action(struct ds *ds, const struct nlattr *a, @@ -1181,6 +1214,9 @@ format_odp_action(struct ds *ds, const struct nlattr *a, case OVS_ACTION_ATTR_POP_NSH: ds_put_cstr(ds, "pop_nsh()"); break; + case OVS_ACTION_ATTR_CHECK_PKT_LEN: + format_odp_check_pkt_len_action(ds, a, portno_names); + break; case OVS_ACTION_ATTR_UNSPEC: case __OVS_ACTION_ATTR_MAX: default: diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c index 96e39d6c6..7d09be818 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, ... */ + OFPAT_RAW_CHECK_PKT_LARGER, /* ## ------------------ ## */ /* ## Debugging actions. ## */ /* ## ------------------ ## */ @@ -492,6 +494,7 @@ ofpact_next_flattened(const struct ofpact *ofpact) case OFPACT_ENCAP: case OFPACT_DECAP: case OFPACT_DEC_NSH_TTL: + case OFPACT_CHECK_PKT_LARGER: return ofpact_next(ofpact); case OFPACT_CLONE: @@ -7400,6 +7403,97 @@ check_WRITE_METADATA(const struct ofpact_metadata *a OVS_UNUSED, return 0; } +/* Check packet length action. */ + +struct nx_action_check_pkt_larger { + ovs_be16 type; /* OFPAT_VENDOR. */ + ovs_be16 len; /* 24. */ + ovs_be32 vendor; /* NX_VENDOR_ID. */ + ovs_be16 subtype; /* NXAST_OUTPUT_REG. */ + uint8_t pad[6]; + ovs_be16 pkt_len; /* Length of the packet to check. */ + ovs_be16 ofs_nbits; /* (ofs << 6) | (n_bits - 1). */ + ovs_be32 dst; /* Destination register. */ +}; + +OFP_ASSERT(sizeof(struct nx_action_check_pkt_larger) == 24); + +static enum ofperr +decode_OFPAT_RAW_CHECK_PKT_LARGER( + const struct nx_action_check_pkt_larger *ncpl, + enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) +{ + struct ofpact_check_pkt_larger *check_pkt_larger = + ofpact_put_CHECK_PKT_LARGER(out); + check_pkt_larger->pkt_len = ntohs(ncpl->pkt_len); + const struct mf_field *mf = mf_from_nxm_header(ntohl(ncpl->dst), NULL); + check_pkt_larger->dst.field = mf; + check_pkt_larger->dst.ofs = nxm_decode_ofs(ncpl->ofs_nbits); + check_pkt_larger->dst.n_bits = nxm_decode_n_bits(ncpl->ofs_nbits);; + return 0; +} + +static void +encode_CHECK_PKT_LARGER(const struct ofpact_check_pkt_larger *check_pkt_larger, + enum ofp_version ofp_version OVS_UNUSED, + struct ofpbuf *out OVS_UNUSED) +{ + struct nx_action_check_pkt_larger *ncpl = put_OFPAT_CHECK_PKT_LARGER(out); + ncpl->pkt_len = htons(check_pkt_larger->pkt_len); + ncpl->ofs_nbits = nxm_encode_ofs_nbits( + check_pkt_larger->dst.ofs, check_pkt_larger->dst.n_bits); + if (check_pkt_larger->dst.field) { + ncpl->dst = htonl(nxm_header_from_mff(check_pkt_larger->dst.field)); + } +} + +static char * OVS_WARN_UNUSED_RESULT +parse_CHECK_PKT_LARGER(char *arg, const struct ofpact_parse_params *pp) +{ + char *value; + char *delim; + char *key; + char *error = set_field_split_str(arg, &key, &value, &delim); + if (error) { + return error; + } + delim[0] = '\0'; + if (value[strlen(value) - 1] ==')') { + value[strlen(value) - 1] = '\0'; + } + struct mf_subfield dst; + error = mf_parse_subfield(&dst, key); + if (error) { + return error; + } + + struct ofpact_check_pkt_larger *check_pkt_larger = + ofpact_put_CHECK_PKT_LARGER(pp->ofpacts); + error = str_to_u16(value, NULL, &check_pkt_larger->pkt_len); + if (error) { + return error; + } + check_pkt_larger->dst = dst; + return NULL; +} + +static void +format_CHECK_PKT_LARGER(const struct ofpact_check_pkt_larger *a, + const struct ofpact_format_params *fp) +{ + ds_put_format(fp->s, "%scheck_pkt_larger(%s%"PRIu32")->", + colors.param, colors.end, a->pkt_len); + mf_format_subfield(&a->dst, fp->s); +} + +static enum ofperr +check_CHECK_PKT_LARGER(const struct ofpact_check_pkt_larger *a OVS_UNUSED, + const struct ofpact_check_params *cp OVS_UNUSED) +{ + return 0; +} + + /* Goto-Table instruction. */ static void @@ -7686,6 +7780,7 @@ action_set_classify(const struct ofpact *a) case OFPACT_WRITE_METADATA: case OFPACT_DEBUG_RECIRC: case OFPACT_DEBUG_SLOW: + case OFPACT_CHECK_PKT_LARGER: return ACTION_SLOT_INVALID; default: @@ -7885,6 +7980,7 @@ ovs_instruction_type_from_ofpact_type(enum ofpact_type type) case OFPACT_ENCAP: case OFPACT_DECAP: case OFPACT_DEC_NSH_TTL: + case OFPACT_CHECK_PKT_LARGER: default: return OVSINST_OFPIT11_APPLY_ACTIONS; } @@ -8755,6 +8851,7 @@ ofpact_outputs_to_port(const struct ofpact *ofpact, ofp_port_t port) case OFPACT_ENCAP: case OFPACT_DECAP: case OFPACT_DEC_NSH_TTL: + case OFPACT_CHECK_PKT_LARGER: default: return false; } @@ -8991,7 +9088,6 @@ ofpacts_parse__(char *str, const struct ofpact_parse_params *pp, enum ofpact_type type; char *error = NULL; ofp_port_t port; - if (ofpact_type_from_name(key, &type)) { error = ofpact_parse(type, value, pp); inst = ovs_instruction_type_from_ofpact_type(type); 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/ofproto/ofproto-dpif-ipfix.c b/ofproto/ofproto-dpif-ipfix.c index 402980678..4b44a773a 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 7da31753c..b21b40bb1 100644 --- a/ofproto/ofproto-dpif-sflow.c +++ b/ofproto/ofproto-dpif-sflow.c @@ -1222,6 +1222,7 @@ dpif_sflow_read_actions(const struct flow *flow, case OVS_ACTION_ATTR_PUSH_NSH: case OVS_ACTION_ATTR_POP_NSH: case OVS_ACTION_ATTR_UNSPEC: + case OVS_ACTION_ATTR_CHECK_PKT_LEN: case __OVS_ACTION_ATTR_MAX: default: break; diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c index 839fddd99..ec7238183 100644 --- a/ofproto/ofproto-dpif-xlate.c +++ b/ofproto/ofproto-dpif-xlate.c @@ -5565,6 +5565,7 @@ reversible_actions(const struct ofpact *ofpacts, size_t ofpacts_len) case OFPACT_UNROLL_XLATE: case OFPACT_WRITE_ACTIONS: case OFPACT_WRITE_METADATA: + case OFPACT_CHECK_PKT_LARGER: break; case OFPACT_CT: @@ -5873,6 +5874,7 @@ freeze_unroll_actions(const struct ofpact *a, const struct ofpact *end, case OFPACT_CT: case OFPACT_CT_CLEAR: case OFPACT_NAT: + case OFPACT_CHECK_PKT_LARGER: /* These may not generate PACKET INs. */ break; @@ -6065,6 +6067,96 @@ compose_ct_clear_action(struct xlate_ctx *ctx) } } +static void +xlate_check_pkt_larger(struct xlate_ctx *ctx, + struct ofpact_check_pkt_larger *check_pkt_larger, + const struct ofpact *remaining_acts, + size_t remaining_acts_len) +{ + if (!check_pkt_larger->dst.field) { + return; + } + + union mf_subvalue value; + memset(&value, 0, sizeof value); + if (!ctx->xbridge->support.check_pkt_len) { + uint8_t is_pkt_larger = 0; + if (ctx->xin->packet) { + is_pkt_larger = + dp_packet_size(ctx->xin->packet) > check_pkt_larger->pkt_len; + } + value.u8_val = is_pkt_larger; + mf_write_subfield_flow(&check_pkt_larger->dst, &value, &ctx->xin->flow); + /* If datapath doesn't support check_pkt_len action, then set the + * SLOW_ACTION flag so that .. + */ + ctx->xout->slow |= SLOW_ACTION; + return; + } + + struct ofpbuf old_stack = ctx->stack; + union mf_subvalue new_stack[1024 / sizeof(union mf_subvalue)]; + ofpbuf_use_stub(&ctx->stack, new_stack, sizeof new_stack); + ofpbuf_put(&ctx->stack, old_stack.data, old_stack.size); + + struct ofpbuf old_action_set = ctx->action_set; + uint64_t actset_stub[1024 / 8]; + ofpbuf_use_stub(&ctx->action_set, actset_stub, sizeof actset_stub); + ofpbuf_put(&ctx->action_set, old_action_set.data, old_action_set.size); + + struct flow old_flow = ctx->xin->flow; + xlate_commit_actions(ctx); + struct flow old_base = ctx->base_flow; + bool old_was_mpls = ctx->was_mpls; + bool old_conntracked = ctx->conntracked; + + size_t offset = nl_msg_start_nested(ctx->odp_actions, + OVS_ACTION_ATTR_CHECK_PKT_LEN); + nl_msg_put_u16(ctx->odp_actions, OVS_CHECK_PKT_LEN_ATTR_PKT_LEN, + check_pkt_larger->pkt_len); + size_t offset_attr = nl_msg_start_nested( + ctx->odp_actions, OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER); + value.u8_val = 1; + mf_write_subfield_flow(&check_pkt_larger->dst, &value, &ctx->xin->flow); + do_xlate_actions(remaining_acts, remaining_acts_len, ctx, true, false); + if (!ctx->freezing) { + xlate_action_set(ctx); + } + if (ctx->freezing) { + finish_freezing(ctx); + } + nl_msg_end_non_empty_nested(ctx->odp_actions, offset_attr); + + ctx->base_flow = old_base; + ctx->was_mpls = old_was_mpls; + ctx->conntracked = old_conntracked; + ctx->xin->flow = old_flow; + + offset_attr = nl_msg_start_nested( + ctx->odp_actions, OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL); + value.u8_val = 0; + mf_write_subfield_flow(&check_pkt_larger->dst, &value, &ctx->xin->flow); + do_xlate_actions(remaining_acts, remaining_acts_len, ctx, true, false); + if (!ctx->freezing) { + xlate_action_set(ctx); + } + if (ctx->freezing) { + finish_freezing(ctx); + } + nl_msg_end_non_empty_nested(ctx->odp_actions, offset_attr); + nl_msg_end_nested(ctx->odp_actions, offset); + + ofpbuf_uninit(&ctx->action_set); + ctx->action_set = old_action_set; + ofpbuf_uninit(&ctx->stack); + ctx->stack = old_stack; + ctx->base_flow = old_base; + ctx->was_mpls = old_was_mpls; + ctx->conntracked = old_conntracked; + ctx->xin->flow = old_flow; + ctx->exit = true; +} + static void rewrite_flow_encap_ethernet(struct xlate_ctx *ctx, struct flow *flow, @@ -6387,6 +6479,7 @@ recirc_for_mpls(const struct ofpact *a, struct xlate_ctx *ctx) case OFPACT_WRITE_ACTIONS: case OFPACT_WRITE_METADATA: case OFPACT_GOTO_TABLE: + case OFPACT_CHECK_PKT_LARGER: default: break; } @@ -6842,6 +6935,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 2dc5778e1..a34fab89c 100644 --- a/ofproto/ofproto-dpif.c +++ b/ofproto/ofproto-dpif.c @@ -1290,6 +1290,52 @@ check_ct_clear(struct dpif_backer *backer) return supported; } + +/* Tests whether 'backer''s datapath supports the + * OVS_ACTION_ATTR_CHECK_PKT_LEN action. */ +static bool +check_check_pkt_len(struct dpif_backer *backer) +{ + struct odputil_keybuf keybuf; + struct ofpbuf actions; + struct ofpbuf key; + struct flow flow; + bool supported; + + struct odp_flow_key_parms odp_parms = { + .flow = &flow, + .probe = true, + }; + + memset(&flow, 0, sizeof flow); + ofpbuf_use_stack(&key, &keybuf, sizeof keybuf); + odp_flow_key_from_flow(&odp_parms, &key); + ofpbuf_init(&actions, 64); + size_t cpl_start; + size_t nested_action_start; + cpl_start = nl_msg_start_nested(&actions, OVS_ACTION_ATTR_CHECK_PKT_LEN); + nl_msg_put_u16(&actions, OVS_CHECK_PKT_LEN_ATTR_PKT_LEN, 0); + + nested_action_start = nl_msg_start_nested( + &actions, OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER); + nl_msg_put_flag(&actions, OVS_ACTION_ATTR_CT_CLEAR); + nl_msg_end_nested(&actions, nested_action_start); + + nested_action_start = nl_msg_start_nested( + &actions, OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL); + nl_msg_put_flag(&actions, OVS_ACTION_ATTR_CT_CLEAR); + nl_msg_end_nested(&actions, nested_action_start); + nl_msg_end_nested(&actions, cpl_start); + + supported = dpif_probe_feature(backer->dpif, "check_pkt_len", &key, + &actions, NULL); + ofpbuf_uninit(&actions); + VLOG_INFO("%s: Datapath %s check_pkt_len action", + dpif_name(backer->dpif), (supported) ? "supports" + : "does not support"); + return supported; +} + /* Probe the highest dp_hash algorithm supported by the datapath. */ static size_t check_max_dp_hash_alg(struct dpif_backer *backer) @@ -1397,6 +1443,7 @@ check_support(struct dpif_backer *backer) backer->rt_support.ct_eventmask = check_ct_eventmask(backer); backer->rt_support.ct_clear = check_ct_clear(backer); backer->rt_support.max_hash_alg = check_max_dp_hash_alg(backer); + backer->rt_support.check_pkt_len = check_check_pkt_len(backer); /* Flow fields. */ backer->rt_support.odp.ct_state = check_ct_state(backer); 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 { From patchwork Thu Jan 3 18:18: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: 1020468 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 43Vx5l5Nztz9s3l for ; Fri, 4 Jan 2019 05:19:39 +1100 (AEDT) Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id 61060D26; Thu, 3 Jan 2019 18:18:58 +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 E46E2D25 for ; Thu, 3 Jan 2019 18:18:56 +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 002C485E for ; Thu, 3 Jan 2019 18:18:53 +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 84E9C7EBB7 for ; Thu, 3 Jan 2019 18:18:53 +0000 (UTC) Received: from nusiddiq.mac (ovpn-116-36.sin2.redhat.com [10.67.116.36]) by smtp.corp.redhat.com (Postfix) with ESMTP id 92B2860C68; Thu, 3 Jan 2019 18:18:51 +0000 (UTC) From: nusiddiq@redhat.com To: dev@openvswitch.org Date: Thu, 3 Jan 2019 23:48:48 +0530 Message-Id: <20190103181848.17514-1-nusiddiq@redhat.com> In-Reply-To: <20190103181729.17206-1-nusiddiq@redhat.com> References: <20190103181729.17206-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]); Thu, 03 Jan 2019 18:18: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] [RFC v2 3/5] 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 are expected to be used as nested OVN actions inside OVN actions like icmp4, icmp6 etc. This patch adds only one field for now - icmp4.frag_mtu. It should be fairly straightforward to add similar fields in the near future. This field is expected to be used as an inner field with in the 'icmp4' action. Eg. "icmp4 {"eth.dst <-> eth.src; " "icmp4.type = 3; /* Destination Unreachable */ " "icmp4.code = 4; /* Fragmentation Needed */ " icmp4.frag_mtu = 1442; ... "next; };", 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 --- include/ovn/actions.h | 25 +++- include/ovn/automake.mk | 3 +- include/ovn/expr.h | 5 + {ovn/lib => include/ovn}/logical-fields.h | 41 +++++++ ovn/controller/lflow.c | 1 + ovn/controller/lflow.h | 2 +- ovn/controller/pinctrl.c | 23 +++- ovn/lib/actions.c | 135 +++++++++++++++++----- ovn/lib/automake.mk | 1 - ovn/lib/expr.c | 17 ++- ovn/lib/logical-fields.c | 36 +++++- ovn/northd/ovn-northd.c | 2 +- ovn/ovn-sb.xml | 11 ++ ovn/utilities/ovn-trace.c | 5 +- tests/ovn.at | 18 ++- tests/test-ovn.c | 2 +- 16 files changed, 282 insertions(+), 45 deletions(-) rename {ovn/lib => include/ovn}/logical-fields.h (71%) diff --git a/include/ovn/actions.h b/include/ovn/actions.h index 1c0c67ce6..095b60df0 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_*. */ @@ -229,6 +244,8 @@ struct ovnact_nest { struct ovnact ovnact; struct ovnact *nested; size_t nested_len; + struct ovnact *nested_ovnfields; + size_t nested_ovnfields_len; }; /* OVNACT_GET_ARP, OVNACT_GET_ND. */ @@ -461,6 +478,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 71% rename from ovn/lib/logical-fields.h rename to include/ovn/logical-fields.h index 95759a8bb..968faf875 100644 --- a/ovn/lib/logical-fields.h +++ b/include/ovn/logical-fields.h @@ -81,4 +81,45 @@ enum mff_log_flags { MLF_NESTED_CONTAINER = (1 << MLF_NESTED_CONTAINER_BIT), }; +/* OVN logical fields + * =================== + * These are the fields which OVN supports modifying within an OVN action + * which gets translated to OFFlow controller action. The value to be set + * for these fields will be encoded with in the 'userdata' field of the + * 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 5aebbfc2c..59bfe33cc 100644 --- a/ovn/controller/pinctrl.c +++ b/ovn/controller/pinctrl.c @@ -42,9 +42,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" @@ -450,6 +450,27 @@ pinctrl_handle_icmp(const struct flow *ip_flow, struct dp_packet *pkt_in, 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); + uint8_t *n_ovnfield_acts = ofpbuf_try_pull(userdata, + sizeof *n_ovnfield_acts); + if (n_ovnfield_acts && *n_ovnfield_acts) { + for (uint8_t i = 0; i < *n_ovnfield_acts; i++) { + struct ovnfield_act_header *oah = + ofpbuf_try_pull(userdata, sizeof *oah); + + if (!oah) { + break; + } + switch (ntohs(oah->id)) { + case OVN_ICMP4_FRAG_MTU: { + ovs_be16 *mtu = ofpbuf_try_pull(userdata, sizeof *mtu); + if (mtu) { + ih->icmp_fields.frag.mtu = *mtu; + } + break; + } + } + } + } } else { struct ip6_hdr *nh = dp_packet_put_zeros(&packet, sizeof *nh); struct icmp6_error_header *ih; diff --git a/ovn/lib/actions.c b/ovn/lib/actions.c index 7b7a89478..a2000667e 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" @@ -194,6 +193,7 @@ struct action_context { struct ofpbuf *ovnacts; /* Actions. */ struct expr *prereqs; /* Prerequisites to apply to match. */ int depth; /* Current nested action depth. */ + struct ofpbuf *ovnfield_acts; /* Actions for OVN fields. */ }; static void parse_actions(struct action_context *, enum lex_type sentinel); @@ -366,19 +366,33 @@ ovnact_next_free(struct ovnact_next *a OVS_UNUSED) 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 ofpbuf *ovnacts = ctx->ovnacts; + size_t ofs = ovnacts->size; + struct ovnact_load *load; + if (lhs->symbol->ovn_field) { + if (!ctx->ovnfield_acts) { + lexer_syntax_error(ctx->lexer, + "Invalid usage of OVN field : %s", + lhs->symbol->name); + return; + } + ovnacts = ctx->ovnfield_acts; + load = ovnact_put_OVNFIELD_LOAD(ovnacts); + } else { + load = ovnact_put_LOAD(ovnacts); + } + load->dst = *lhs; char *error = expr_type_check(lhs, lhs->n_bits, true); if (error) { - ctx->ovnacts->size = ofs; + ovnacts->size = ofs; lexer_error(ctx->lexer, "%s", error); free(error); return; } if (!expr_constant_parse(ctx->lexer, lhs, &load->imm)) { - ctx->ovnacts->size = ofs; + ovnacts->size = ofs; return; } } @@ -1089,7 +1103,7 @@ encode_CT_CLEAR(const struct ovnact_null *null OVS_UNUSED, * actions on a packet derived from the one being processed. */ static void parse_nested_action(struct action_context *ctx, enum ovnact_type type, - const char *prereq) + const char *prereq, bool parse_ovn_fields) { if (!lexer_force_match(ctx->lexer, LEX_T_LCURLY)) { return; @@ -1102,14 +1116,19 @@ parse_nested_action(struct action_context *ctx, enum ovnact_type type, uint64_t stub[1024 / 8]; struct ofpbuf nested = OFPBUF_STUB_INITIALIZER(stub); - + uint64_t ovnfields_stub[512 / 8]; + struct ofpbuf nested_ovnfields = OFPBUF_STUB_INITIALIZER(ovnfields_stub); struct action_context inner_ctx = { .pp = ctx->pp, .lexer = ctx->lexer, .ovnacts = &nested, .prereqs = NULL, .depth = ctx->depth + 1, + .ovnfield_acts = NULL, }; + if (parse_ovn_fields) { + inner_ctx.ovnfield_acts = &nested_ovnfields; + } parse_actions(&inner_ctx, LEX_T_RCURLY); if (prereq) { @@ -1134,61 +1153,68 @@ parse_nested_action(struct action_context *ctx, enum ovnact_type type, OVNACT_ALIGN(sizeof *on)); on->nested_len = nested.size; on->nested = ofpbuf_steal_data(&nested); + on->nested_ovnfields_len = nested_ovnfields.size; + if (parse_ovn_fields && on->nested_ovnfields_len) { + on->nested_ovnfields = ofpbuf_steal_data(&nested_ovnfields); + } } static void parse_ARP(struct action_context *ctx) { - parse_nested_action(ctx, OVNACT_ARP, "ip4"); + parse_nested_action(ctx, OVNACT_ARP, "ip4", false); } static void parse_ICMP4(struct action_context *ctx) { - parse_nested_action(ctx, OVNACT_ICMP4, "ip4"); + parse_nested_action(ctx, OVNACT_ICMP4, "ip4", true); } static void parse_ICMP6(struct action_context *ctx) { - parse_nested_action(ctx, OVNACT_ICMP6, "ip6"); + parse_nested_action(ctx, OVNACT_ICMP6, "ip6", false); } static void parse_TCP_RESET(struct action_context *ctx) { - parse_nested_action(ctx, OVNACT_TCP_RESET, "tcp"); + parse_nested_action(ctx, OVNACT_TCP_RESET, "tcp", false); } static void parse_ND_NA(struct action_context *ctx) { - parse_nested_action(ctx, OVNACT_ND_NA, "nd_ns"); + parse_nested_action(ctx, OVNACT_ND_NA, "nd_ns", false); } static void parse_ND_NA_ROUTER(struct action_context *ctx) { - parse_nested_action(ctx, OVNACT_ND_NA_ROUTER, "nd_ns"); + parse_nested_action(ctx, OVNACT_ND_NA_ROUTER, "nd_ns", false); } static void parse_ND_NS(struct action_context *ctx) { - parse_nested_action(ctx, OVNACT_ND_NS, "ip6"); + parse_nested_action(ctx, OVNACT_ND_NS, "ip6", false); } static void parse_CLONE(struct action_context *ctx) { - parse_nested_action(ctx, OVNACT_CLONE, NULL); + parse_nested_action(ctx, OVNACT_CLONE, NULL, false); } static void format_nested_action(const struct ovnact_nest *on, const char *name, - struct ds *s) + struct ds *s, bool format_ovn_fields) { ds_put_format(s, "%s { ", name); + if (format_ovn_fields && on->nested_ovnfields_len) { + ovnacts_format(on->nested_ovnfields, on->nested_ovnfields_len, s); + } ovnacts_format(on->nested, on->nested_len, s); ds_put_format(s, " };"); } @@ -1196,56 +1222,57 @@ format_nested_action(const struct ovnact_nest *on, const char *name, static void format_ARP(const struct ovnact_nest *nest, struct ds *s) { - format_nested_action(nest, "arp", s); + format_nested_action(nest, "arp", s, false); } static void format_ICMP4(const struct ovnact_nest *nest, struct ds *s) { - format_nested_action(nest, "icmp4", s); + format_nested_action(nest, "icmp4", s, true); } static void format_ICMP6(const struct ovnact_nest *nest, struct ds *s) { - format_nested_action(nest, "icmp6", s); + format_nested_action(nest, "icmp6", s, false); } static void format_TCP_RESET(const struct ovnact_nest *nest, struct ds *s) { - format_nested_action(nest, "tcp_reset", s); + format_nested_action(nest, "tcp_reset", s, false); } static void format_ND_NA(const struct ovnact_nest *nest, struct ds *s) { - format_nested_action(nest, "nd_na", s); + format_nested_action(nest, "nd_na", s, false); } static void format_ND_NA_ROUTER(const struct ovnact_nest *nest, struct ds *s) { - format_nested_action(nest, "nd_na_router", s); + format_nested_action(nest, "nd_na_router", s, false); } static void format_ND_NS(const struct ovnact_nest *nest, struct ds *s) { - format_nested_action(nest, "nd_ns", s); + format_nested_action(nest, "nd_ns", s, false); } static void format_CLONE(const struct ovnact_nest *nest, struct ds *s) { - format_nested_action(nest, "clone", s); + format_nested_action(nest, "clone", s, false); } static void encode_nested_actions(const struct ovnact_nest *on, const struct ovnact_encode_params *ep, enum action_opcode opcode, - struct ofpbuf *ofpacts) + struct ofpbuf *ofpacts, + bool encode_ovn_fields) { /* Convert nested actions into ofpacts. */ uint64_t inner_ofpacts_stub[1024 / 8]; @@ -1258,6 +1285,13 @@ encode_nested_actions(const struct ovnact_nest *on, * switch inside an OFPT_PACKET_OUT message. */ size_t oc_offset = encode_start_controller_op(opcode, false, NX_CTLR_NO_METER, ofpacts); + if (encode_ovn_fields) { + uint8_t *n_ovnfields_acts = ofpbuf_put_zeros(ofpacts, 1); + ovnacts_encode(on->nested_ovnfields, on->nested_ovnfields_len, + ep, ofpacts); + *n_ovnfields_acts = ovnacts_count(on->nested_ovnfields, + on->nested_ovnfields_len); + } ofpacts_put_openflow_actions(inner_ofpacts.data, inner_ofpacts.size, ofpacts, OFP13_VERSION); encode_finish_controller_op(oc_offset, ofpacts); @@ -1271,7 +1305,7 @@ encode_ARP(const struct ovnact_nest *on, const struct ovnact_encode_params *ep, struct ofpbuf *ofpacts) { - encode_nested_actions(on, ep, ACTION_OPCODE_ARP, ofpacts); + encode_nested_actions(on, ep, ACTION_OPCODE_ARP, ofpacts, false); } static void @@ -1279,7 +1313,7 @@ encode_ICMP4(const struct ovnact_nest *on, const struct ovnact_encode_params *ep, struct ofpbuf *ofpacts) { - encode_nested_actions(on, ep, ACTION_OPCODE_ICMP, ofpacts); + encode_nested_actions(on, ep, ACTION_OPCODE_ICMP, ofpacts, true); } static void @@ -1287,7 +1321,7 @@ encode_ICMP6(const struct ovnact_nest *on, const struct ovnact_encode_params *ep, struct ofpbuf *ofpacts) { - encode_nested_actions(on, ep, ACTION_OPCODE_ICMP, ofpacts); + encode_nested_actions(on, ep, ACTION_OPCODE_ICMP, ofpacts, false); } static void @@ -1295,7 +1329,7 @@ encode_TCP_RESET(const struct ovnact_nest *on, const struct ovnact_encode_params *ep, struct ofpbuf *ofpacts) { - encode_nested_actions(on, ep, ACTION_OPCODE_TCP_RESET, ofpacts); + encode_nested_actions(on, ep, ACTION_OPCODE_TCP_RESET, ofpacts, false); } static void @@ -1303,7 +1337,7 @@ encode_ND_NA(const struct ovnact_nest *on, const struct ovnact_encode_params *ep, struct ofpbuf *ofpacts) { - encode_nested_actions(on, ep, ACTION_OPCODE_ND_NA, ofpacts); + encode_nested_actions(on, ep, ACTION_OPCODE_ND_NA, ofpacts, false); } static void @@ -1311,7 +1345,8 @@ encode_ND_NA_ROUTER(const struct ovnact_nest *on, const struct ovnact_encode_params *ep, struct ofpbuf *ofpacts) { - encode_nested_actions(on, ep, ACTION_OPCODE_ND_NA_ROUTER, ofpacts); + encode_nested_actions(on, ep, ACTION_OPCODE_ND_NA_ROUTER, ofpacts, + false); } static void @@ -1319,7 +1354,7 @@ encode_ND_NS(const struct ovnact_nest *on, const struct ovnact_encode_params *ep, struct ofpbuf *ofpacts) { - encode_nested_actions(on, ep, ACTION_OPCODE_ND_NS, ofpacts); + encode_nested_actions(on, ep, ACTION_OPCODE_ND_NS, ofpacts, false); } static void @@ -2295,6 +2330,42 @@ 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); + struct ovnfield_act_header *oah = ofpbuf_put_zeros(ofpacts, sizeof *oah); + oah->id = htons(f->id); + switch (f->id) { + case OVN_ICMP4_FRAG_MTU: + oah->len = ntohs(sizeof(ovs_be16)); + ofpbuf_put(ofpacts, &load->imm.value.be16_int, sizeof(ovs_be16)); + 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 6850d4f72..ebf829278 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/ovn-sb.xml b/ovn/ovn-sb.xml index 8ffef403a..0784df537 100644 --- a/ovn/ovn-sb.xml +++ b/ovn/ovn-sb.xml @@ -1843,6 +1843,17 @@
  • icmp4.code = 1 (host unreachable)
  • +

    + This action also allows setting the fragmentation mtu in the + ICMPv4 packet header as per RFC 1191. To set this + icmp4.frag_mtu field can be used. +

    + +
      +
    • icmp4.type = 3 (destination unreachable)
    • +
    • icmp4.code = 4 (Fragmentation Needed)
    • +
    • icmp4.frag_mtu = 1500
    • +

    Prerequisite: ip4

    diff --git a/ovn/utilities/ovn-trace.c b/ovn/utilities/ovn-trace.c index cbf3e54d2..6f9c3df09 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" @@ -2106,6 +2106,9 @@ 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: + break; } } diff --git a/tests/ovn.at b/tests/ovn.at index 28856b4fd..6c7a054af 100644 --- a/tests/ovn.at +++ b/tests/ovn.at @@ -1280,14 +1280,28 @@ reg1[0] = put_nd_ra_opts(addr_mode = "slaac", mtu = 10.0.0.4, slla = ae:01:02:03 # icmp4 icmp4 { 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) + encodes as controller(userdata=00.00.00.0a.00.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 { }; formats as icmp4 { drop; }; - encodes as controller(userdata=00.00.00.0a.00.00.00.00) + encodes as controller(userdata=00.00.00.0a.00.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; + formats as icmp4 { icmp4.frag_mtu = 1500; eth.dst = ff:ff:ff:ff:ff:ff; output; }; output; + encodes as controller(userdata=00.00.00.0a.00.00.00.00.01.00.00.00.02.05.dc.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.frag_mtu - Should fail +icmp6 { eth.dst = ff:ff:ff:ff:ff:ff; icmp4.frag_mtu = 1500; output; }; output; + Syntax error at `1500' Invalid usage of OVN field : icmp4.frag_mtu. + +# icmp4.frag_mtu - Should fail +icmp4.frag_mtu = 1500; + Syntax error at `1500' Invalid usage of OVN field : icmp4.frag_mtu. + # 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 ebaf21673..5f248a860 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 Thu Jan 3 18:19:02 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Numan Siddique X-Patchwork-Id: 1020469 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 43Vx6Y3n4Fz9s3l for ; Fri, 4 Jan 2019 05:20:21 +1100 (AEDT) Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id 24F9DD37; Thu, 3 Jan 2019 18:19:08 +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 7F158D36 for ; Thu, 3 Jan 2019 18:19:07 +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 D95E3802 for ; Thu, 3 Jan 2019 18:19:06 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.phx2.redhat.com [10.5.11.16]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 71783E3DEF for ; Thu, 3 Jan 2019 18:19:06 +0000 (UTC) Received: from nusiddiq.mac (ovpn-116-36.sin2.redhat.com [10.67.116.36]) by smtp.corp.redhat.com (Postfix) with ESMTP id 5932C4C484; Thu, 3 Jan 2019 18:19:05 +0000 (UTC) From: nusiddiq@redhat.com To: dev@openvswitch.org Date: Thu, 3 Jan 2019 23:49:02 +0530 Message-Id: <20190103181902.17607-1-nusiddiq@redhat.com> In-Reply-To: <20190103181729.17206-1-nusiddiq@redhat.com> References: <20190103181729.17206-1-nusiddiq@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.16 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.38]); Thu, 03 Jan 2019 18:19:06 +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] [RFC v2 4/5] 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 ICMP4 packet if the packet length is greater than the specified length. Signed-off-by: Numan Siddique --- include/ovn/actions.h | 10 +++++++- ovn/controller/pinctrl.c | 15 +++++++++++ ovn/lib/actions.c | 53 +++++++++++++++++++++++++++++++++++++++ ovn/ovn-sb.xml | 21 ++++++++++++++++ ovn/utilities/ovn-trace.c | 4 ++- tests/ovn.at | 10 ++++++++ 6 files changed, 111 insertions(+), 2 deletions(-) diff --git a/include/ovn/actions.h b/include/ovn/actions.h index 095b60df0..6571e2aee 100644 --- a/include/ovn/actions.h +++ b/include/ovn/actions.h @@ -81,7 +81,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 { @@ -311,6 +312,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/controller/pinctrl.c b/ovn/controller/pinctrl.c index 59bfe33cc..ba6d4a24e 100644 --- a/ovn/controller/pinctrl.c +++ b/ovn/controller/pinctrl.c @@ -452,6 +452,8 @@ pinctrl_handle_icmp(const struct flow *ip_flow, struct dp_packet *pkt_in, packet_set_icmp(&packet, ICMP4_DST_UNREACH, 1); uint8_t *n_ovnfield_acts = ofpbuf_try_pull(userdata, sizeof *n_ovnfield_acts); + bool include_orig_ip_datagram = false; + if (n_ovnfield_acts && *n_ovnfield_acts) { for (uint8_t i = 0; i < *n_ovnfield_acts; i++) { struct ovnfield_act_header *oah = @@ -465,12 +467,25 @@ pinctrl_handle_icmp(const struct flow *ip_flow, struct dp_packet *pkt_in, ovs_be16 *mtu = ofpbuf_try_pull(userdata, sizeof *mtu); if (mtu) { ih->icmp_fields.frag.mtu = *mtu; + include_orig_ip_datagram = true; } break; } } } } + + if (include_orig_ip_datagram) { + size_t orig_ip_datagram_len = sizeof(struct ip_header) + 8; + uint8_t *data = dp_packet_put_zeros(&packet, orig_ip_datagram_len); + memcpy(data, dp_packet_l3(pkt_in), orig_ip_datagram_len); + nh->ip_tot_len += htons(orig_ip_datagram_len); + ih->icmp_csum = 0; + ih->icmp_csum = csum(ih, sizeof *ih + orig_ip_datagram_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; diff --git a/ovn/lib/actions.c b/ovn/lib/actions.c index a2000667e..ea9120b79 100644 --- a/ovn/lib/actions.c +++ b/ovn/lib/actions.c @@ -2366,6 +2366,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) @@ -2395,6 +2444,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 0784df537..3588c2a95 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 6f9c3df09..0ebb5723d 100644 --- a/ovn/utilities/ovn-trace.c +++ b/ovn/utilities/ovn-trace.c @@ -2109,8 +2109,10 @@ trace_actions(const struct ovnact *ovnacts, size_t ovnacts_len, case OVNACT_OVNFIELD_LOAD: break; - } + case OVNACT_CHECK_PKT_LARGER: + break; + } } ds_destroy(&s); } diff --git a/tests/ovn.at b/tests/ovn.at index 6c7a054af..442b3a917 100644 --- a/tests/ovn.at +++ b/tests/ovn.at @@ -1330,6 +1330,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 Thu Jan 3 18:19:16 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Numan Siddique X-Patchwork-Id: 1020470 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 43Vx786MH0z9s3l for ; Fri, 4 Jan 2019 05:20:52 +1100 (AEDT) Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id BDC51D2F; Thu, 3 Jan 2019 18:19:26 +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 9D8C2D2F for ; Thu, 3 Jan 2019 18:19:25 +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 B88C3897 for ; Thu, 3 Jan 2019 18:19:23 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.phx2.redhat.com [10.5.11.16]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 50EF319D224 for ; Thu, 3 Jan 2019 18:19:23 +0000 (UTC) Received: from nusiddiq.mac (ovpn-116-36.sin2.redhat.com [10.67.116.36]) by smtp.corp.redhat.com (Postfix) with ESMTP id 1A18B4C484; Thu, 3 Jan 2019 18:19:21 +0000 (UTC) From: nusiddiq@redhat.com To: dev@openvswitch.org Date: Thu, 3 Jan 2019 23:49:16 +0530 Message-Id: <20190103181916.17678-1-nusiddiq@redhat.com> In-Reply-To: <20190103181729.17206-1-nusiddiq@redhat.com> References: <20190103181729.17206-1-nusiddiq@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.16 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.29]); Thu, 03 Jan 2019 18:19:23 +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] [RFC v2 5/5] 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 Signed-off-by: Numan Siddique --- ovn/northd/ovn-northd.8.xml | 83 +++++++++++++++++- ovn/northd/ovn-northd.c | 89 ++++++++++++++++++- tests/ovn.at | 165 ++++++++++++++++++++++++++++++++++++ 3 files changed, 331 insertions(+), 6 deletions(-) diff --git a/ovn/northd/ovn-northd.8.xml b/ovn/northd/ovn-northd.8.xml index 392a5efc9..a3922e8e1 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 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 ebf829278..9f0621c41 100644 --- a/ovn/northd/ovn-northd.c +++ b/ovn/northd/ovn-northd.c @@ -143,8 +143,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") \ @@ -180,6 +182,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 @@ -6574,7 +6578,84 @@ 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); + ds_put_format(&actions, + "icmp4 {" + "icmp4.type = 3; /* Destination Unreachable. */ " + "icmp4.code = 4; /* Frag Needed and DF was Set. */ " + "icmp4.frag_mtu = %d; " + "eth.dst = %s; " + "ip4.dst = ip4.src; " + "ip4.src = %s; " + "ip.ttl = 255; " + REGBIT_EGRESS_LOOPBACK" = 1; " + "next(pipeline=ingress, table=0); };", + gw_mtu - 58, rp->lrp_networks.ea_s, + rp->lrp_networks.ipv4_addrs[0].addr_s); + 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 @@ -6614,7 +6695,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 442b3a917..117b3ac3d 100644 --- a/tests/ovn.at +++ b/tests/ovn.at @@ -11707,6 +11707,7 @@ as hv2 start_daemon ovn-controller OVN_CLEANUP([hv1],[hv2]) + AT_CLEANUP AT_SETUP([ovn -- ovn-nbctl duplicate addresses]) @@ -11765,6 +11766,170 @@ 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 + packet=${packet}${src_ip}${dst_ip}0304000000000000 + packet=${packet}000000000000000000000000000000000000 + packet=${packet}000000000000000000000000000000000000 + packet=${packet}000000000000000000000000000000000000 + packet=${packet}000000000000000000000000000000000000 + + # 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 100. + 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=$pkt_len + src_ip=`ip_to_hex 10 0 0 1` + dst_ip=`ip_to_hex 10 0 0 3` + orig_pkt_src_ip=`ip_to_hex 10 0 0 3` + orig_pkt_dst_ip=`ip_to_hex 172 168 0 3` + icmp_reply=${src_mac}${dst_mac}08004500003800004000fe0168c1 + icmp_reply=${icmp_reply}${src_ip}${dst_ip}0304bd7f0000${mtu} + icmp_reply=${icmp_reply}4500${pkt_len}000000003f010100 + icmp_reply=${icmp_reply}${orig_pkt_src_ip}${orig_pkt_dst_ip} + icmp_reply=${icmp_reply}0304000000000000 + 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])