From patchwork Wed May 4 18:00:21 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: William Tu X-Patchwork-Id: 618573 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from archives.nicira.com (archives.nicira.com [96.126.127.54]) by ozlabs.org (Postfix) with ESMTP id 3r0QnL02svz9t6j for ; Thu, 5 May 2016 04:00:37 +1000 (AEST) Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b=NW0ygwmC; dkim-atps=neutral Received: from archives.nicira.com (localhost [127.0.0.1]) by archives.nicira.com (Postfix) with ESMTP id 31A4C10709; Wed, 4 May 2016 11:00:36 -0700 (PDT) X-Original-To: dev@openvswitch.org Delivered-To: dev@openvswitch.org Received: from mx3v3.cudamail.com (mx3.cudamail.com [64.34.241.5]) by archives.nicira.com (Postfix) with ESMTPS id 03F7310704 for ; Wed, 4 May 2016 11:00:35 -0700 (PDT) Received: from bar6.cudamail.com (localhost [127.0.0.1]) by mx3v3.cudamail.com (Postfix) with ESMTPS id 5C237161615 for ; Wed, 4 May 2016 12:00:34 -0600 (MDT) X-ASG-Debug-ID: 1462384830-0b3237599f197280001-byXFYA Received: from mx1-pf2.cudamail.com ([192.168.24.2]) by bar6.cudamail.com with ESMTP id sGEcTLKo1uyXEMRz (version=TLSv1 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO) for ; Wed, 04 May 2016 12:00:30 -0600 (MDT) X-Barracuda-Envelope-From: u9012063@gmail.com X-Barracuda-RBL-Trusted-Forwarder: 192.168.24.2 Received: from unknown (HELO mail-pf0-f173.google.com) (209.85.192.173) by mx1-pf2.cudamail.com with ESMTPS (RC4-SHA encrypted); 4 May 2016 18:00:29 -0000 Received-SPF: pass (mx1-pf2.cudamail.com: SPF record at _netblocks.google.com designates 209.85.192.173 as permitted sender) X-Barracuda-RBL-Trusted-Forwarder: 209.85.192.173 Received: by mail-pf0-f173.google.com with SMTP id y69so27630940pfb.1 for ; Wed, 04 May 2016 11:00:29 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:subject:date:message-id; bh=nsjeuo43CxwibzToXdZRvZ/myRzYNk49OXJEXGZsPZw=; b=NW0ygwmC3kNFyrMfCFdVm1/Bd6752gbbQiG1HVO/PUYBXXBxgwzf4ElPLtSxM0Bc5N oI5fRFE7zyy9HZ29jGWIzqG5oUCkPIKtr9Yml2Xe1uFSpgg549Fo0w+YiZQjFcaun8yh QJJzVruX2RIbGTtYOcjN3UjVRe7WpSLuuTaRFgJVsmgAwdrFa8KxijWmEW+g3ExmY1ps IuI1jZTgFKMBiMHeZxshTuzbcbz7EhWhXUv2i1l/+7L14KktMQ3aI0uw/FZuQOl1X7c/ nRFuvoO1iIXnSPJ3QnZNkhp+o/TaqmirvwIk+lElfVy8F1aaDTswAndCZHwobNpE4tvR YZAQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:subject:date:message-id; bh=nsjeuo43CxwibzToXdZRvZ/myRzYNk49OXJEXGZsPZw=; b=UD7DeGEpKF/j2Qc/W0o489VxWQFSmY+0AOv0Diy5GGe+IY+VxsvCodPBaTXpb7GzuG hvdO8b3FWFjB+d8EG5fnvBRCVqIrh9Dxyj2p1m5+KgdG9RnzJd6mXMZlrIhYZOwKza5U 6AuCgHd2ns+QVLSQUTQBNDGdPDfGSNDPZjmE0URbBEgioyHZNZcrCJQg4cTDMwPgGIgN MlQ3G0/e9q1J10JeTW1KsKFKTD7SrXa3RgTn26b/vU2ruxKuN2+DLNf5lAllO6+JznZ/ 4YdI8kCag8h65Hiee6zbZSOUyd5dUdeE+KDbVq7jgbg5lP7W38tc3ofNbbMLvygafhHL hQdQ== X-Gm-Message-State: AOPr4FVD8+G7iFvinj8/GztN2zDMHY+P54JE+gFfAnSVjyvw9sTG7NulnZEn4RJMmCczDg== X-Received: by 10.98.2.16 with SMTP id 16mr13984122pfc.148.1462384828684; Wed, 04 May 2016 11:00:28 -0700 (PDT) Received: from vm-dev.localdomain ([208.91.1.34]) by smtp.gmail.com with ESMTPSA id n6sm7677477pfa.2.2016.05.04.11.00.27 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Wed, 04 May 2016 11:00:28 -0700 (PDT) X-CudaMail-Envelope-Sender: u9012063@gmail.com X-Barracuda-Apparent-Source-IP: 208.91.1.34 From: William Tu To: dev@openvswitch.org X-CudaMail-MID: CM-E2-503059480 X-CudaMail-DTE: 050416 X-CudaMail-Originating-IP: 209.85.192.173 Date: Wed, 4 May 2016 11:00:21 -0700 X-ASG-Orig-Subj: [##CM-E2-503059480##][PATCHv3 RFC] ofp-actions: Add a new action to truncate a packet. Message-Id: <1462384821-7400-1-git-send-email-u9012063@gmail.com> X-Mailer: git-send-email 2.5.0 X-GBUdb-Analysis: 0, 209.85.192.173, Ugly c=0.437809 p=-0.485714 Source Normal X-MessageSniffer-Rules: 0-0-0-32767-c X-Barracuda-Connect: UNKNOWN[192.168.24.2] X-Barracuda-Start-Time: 1462384830 X-Barracuda-Encrypted: DHE-RSA-AES256-SHA X-Barracuda-URL: https://web.cudamail.com:443/cgi-mod/mark.cgi X-Barracuda-BRTS-Status: 1 X-Virus-Scanned: by bsmtpd at cudamail.com X-Barracuda-Spam-Score: 1.60 X-Barracuda-Spam-Status: No, SCORE=1.60 using per-user scores of TAG_LEVEL=3.5 QUARANTINE_LEVEL=1000.0 KILL_LEVEL=4.0 tests=BSF_RULE7568M, BSF_RULE_7582B, BSF_SC5_MJ1963, DKIM_SIGNED, MAILTO_TO_SPAM_ADDR, RDNS_NONE X-Barracuda-Spam-Report: Code version 3.2, rules version 3.2.3.29295 Rule breakdown below pts rule name description ---- ---------------------- -------------------------------------------------- 0.00 DKIM_SIGNED Domain Keys Identified Mail: message has a signature 0.00 MAILTO_TO_SPAM_ADDR URI: Includes a link to a likely spammer email 0.50 BSF_RULE7568M Custom Rule 7568M 0.50 BSF_RULE_7582B Custom Rule 7582B 0.10 RDNS_NONE Delivered to trusted network by a host with no rDNS 0.50 BSF_SC5_MJ1963 Custom Rule MJ1963 Subject: [ovs-dev] [PATCHv3 RFC] ofp-actions: Add a new action to truncate a packet. X-BeenThere: dev@openvswitch.org X-Mailman-Version: 2.1.16 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: dev-bounces@openvswitch.org Sender: "dev" The patch proposes adding a new action to support packet truncation. The new action is formatted as 'output(port=n,max_len=m)', as output to port n, with packet size being MIN(original_size, m). One use case is to enable port mirroring to send smaller packets to the destination port so that only useful packet information is mirrored/copied, saving some performance overhead of copying entire packet payload. Example use case is below as well as shown in the testcases: - Output to port 1 with max_len 100 bytes. - The output packet size on port 1 will be MIN(original_packet_size, 100). # ovs-ofctl add-flow br0 'actions=output(port=1,max_len=100)' - The scope of max_len is limited to output action itself. The following packet size of output:1 and output:2 will be intact. # ovs-ofctl add-flow br0 \ 'actions=output(port=1,max_len=100),output:1,output:2' - The Datapath actions shows: # Datapath actions: trunc(100),1,1,2 Implementation/Limitaions: - The patch adds a new OpenFlow extension action OFPACT_OUTPUT_TRUNC, and a new datapath action, OVS_ACTION_ATTR_TRUNC. An OFPACT_OUTPUT_TRUNC will translate into a datapath truncate action, followed by a datapath output action. - The OVS_ACTION_ATTR_TRUNC action only marks the packet with a truncate size (max_len). Actual truncation happens when the packet is about to be transmitted. - Only "output(port=[0-9]*,max_len=)" is supported. Output to any OFPUTIL_NAMED_PORTS are not supported. - Compatibility: If the openvswitch kernel module does not support OVS_ACTION_ATTR_TRUNC, it falls back to userspace slow path, do the packet truncation and send to the output port. Performance: - For kernel datapath, since a packet is always cloned when seeing an output action, we trim the cloned packet in ovs_vport_send() so different vports are agnostic to truncate action. No performance overhead is introduced. - For userspace datapath, if !may_steal, truncate on the current packet affects the rest of actions. To avoid, two options are considered: 1) at netdev_send(), clone the packet, truncate it, and pass down to specific nedev provider's send. Or, 2) at each netdev provider's send, truncate the packet. This avoids extra copy of the packet, but more intrusive to every netdev providers. This patch implements (2) at netdev-linux and netdev-dummy. TODO: - Truncate support for mirror/ipfix/slfow configuration will be in separate patch. Signed-off-by: William Tu --- v2: https://patchwork.ozlabs.org/patch/605082/ v2->v3 - Separate the truncate action and output action in datapath. - Add truncate support for tunnel push and pop. - Fix clang error. - Use pskb_trim instead of skb_trim. - Fallback to userspace truncate action when the openvswitch kernel module does not support. - Disallow truncate port to be any OFPUTIL_NAMED_PORTS. - Add more testcases. --- datapath/actions.c | 17 ++- datapath/datapath.h | 1 + datapath/flow_netlink.c | 8 ++ datapath/linux/compat/include/linux/openvswitch.h | 8 ++ datapath/vport.c | 6 + include/openvswitch/ofp-actions.h | 10 ++ lib/dp-packet.c | 1 + lib/dp-packet.h | 33 +++++ lib/dpif-netdev.c | 23 +++- lib/dpif.c | 21 +++ lib/netdev-dummy.c | 4 + lib/netdev-linux.c | 4 + lib/netdev.c | 6 + lib/odp-execute.c | 10 ++ lib/odp-util.c | 8 ++ lib/ofp-actions.c | 101 +++++++++++++++ ofproto/ofproto-dpif-sflow.c | 3 +- ofproto/ofproto-dpif-xlate.c | 43 ++++++ ofproto/ofproto-dpif.c | 52 ++++++++ ofproto/ofproto-dpif.h | 3 + tests/ofproto-dpif.at | 76 +++++++++++ tests/system-traffic.at | 151 ++++++++++++++++++++++ 22 files changed, 583 insertions(+), 6 deletions(-) diff --git a/datapath/actions.c b/datapath/actions.c index dcf8591..92db16f 100644 --- a/datapath/actions.c +++ b/datapath/actions.c @@ -738,10 +738,13 @@ err: } static void do_output(struct datapath *dp, struct sk_buff *skb, int out_port, - struct sw_flow_key *key) + uint16_t max_len, struct sw_flow_key *key) { struct vport *vport = ovs_vport_rcu(dp, out_port); + if (unlikely(max_len != 0)) + OVS_CB(skb)->max_len = max_len; + if (likely(vport)) { u16 mru = OVS_CB(skb)->mru; @@ -1034,6 +1037,7 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, * is slightly obscure just to avoid that. */ int prev_port = -1; + uint16_t max_len = 0; const struct nlattr *a; int rem; @@ -1045,9 +1049,10 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, struct sk_buff *out_skb = skb_clone(skb, GFP_ATOMIC); if (out_skb) - do_output(dp, out_skb, prev_port, key); + do_output(dp, out_skb, prev_port, max_len, key); prev_port = -1; + max_len = 0; } switch (nla_type(a)) { @@ -1055,6 +1060,12 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, prev_port = nla_get_u32(a); break; + case OVS_ACTION_ATTR_TRUNC: { + struct ovs_action_trunc *trunc = nla_data(a); + max_len = trunc->max_len; + break; + } + case OVS_ACTION_ATTR_USERSPACE: output_userspace(dp, skb, key, a, attr, len); break; @@ -1126,7 +1137,7 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, } if (prev_port != -1) - do_output(dp, skb, prev_port, key); + do_output(dp, skb, prev_port, max_len, key); else consume_skb(skb); diff --git a/datapath/datapath.h b/datapath/datapath.h index ceb3372..178d81d 100644 --- a/datapath/datapath.h +++ b/datapath/datapath.h @@ -102,6 +102,7 @@ struct datapath { struct ovs_skb_cb { struct vport *input_vport; u16 mru; + u16 max_len; /* 0 means original packet size. */ }; #define OVS_CB(skb) ((struct ovs_skb_cb *)(skb)->cb) diff --git a/datapath/flow_netlink.c b/datapath/flow_netlink.c index 6ffcc53..2a2f17a 100644 --- a/datapath/flow_netlink.c +++ b/datapath/flow_netlink.c @@ -2181,6 +2181,7 @@ static int __ovs_nla_copy_actions(struct net *net, const struct nlattr *attr, [OVS_ACTION_ATTR_SAMPLE] = (u32)-1, [OVS_ACTION_ATTR_HASH] = sizeof(struct ovs_action_hash), [OVS_ACTION_ATTR_CT] = (u32)-1, + [OVS_ACTION_ATTR_TRUNC] = sizeof(struct ovs_action_trunc), }; const struct ovs_action_push_vlan *vlan; int type = nla_type(a); @@ -2207,6 +2208,13 @@ static int __ovs_nla_copy_actions(struct net *net, const struct nlattr *attr, return -EINVAL; break; + case OVS_ACTION_ATTR_TRUNC: { + const struct ovs_action_trunc *trunc = nla_data(a); + if (trunc->max_len < OVS_ACTION_OUTPUT_MIN) + return -EINVAL; + break; + } + case OVS_ACTION_ATTR_HASH: { const struct ovs_action_hash *act_hash = nla_data(a); diff --git a/datapath/linux/compat/include/linux/openvswitch.h b/datapath/linux/compat/include/linux/openvswitch.h index 3b39ebb..13b3799 100644 --- a/datapath/linux/compat/include/linux/openvswitch.h +++ b/datapath/linux/compat/include/linux/openvswitch.h @@ -600,6 +600,12 @@ enum ovs_userspace_attr { #define OVS_USERSPACE_ATTR_MAX (__OVS_USERSPACE_ATTR_MAX - 1) +struct ovs_action_trunc { + uint16_t max_len; /* 0 means original packet size. */ +}; +/* Minimum packet size max_len can have, 60 = ETH_MIN_FRAME_LEN. */ +#define OVS_ACTION_OUTPUT_MIN 60 + /** * struct ovs_action_push_mpls - %OVS_ACTION_ATTR_PUSH_MPLS action argument. * @mpls_lse: MPLS label stack entry to push. @@ -742,6 +748,7 @@ enum ovs_nat_attr { * enum ovs_action_attr - Action types. * * @OVS_ACTION_ATTR_OUTPUT: Output packet to port. + * @OVS_ACTION_ATTR_TRUNC: Output packet to port with truncated packet size. * @OVS_ACTION_ATTR_USERSPACE: Send packet to userspace according to nested * %OVS_USERSPACE_ATTR_* attributes. * @OVS_ACTION_ATTR_PUSH_VLAN: Push a new outermost 802.1Q header onto the @@ -807,6 +814,7 @@ enum ovs_action_attr { OVS_ACTION_ATTR_TUNNEL_PUSH, /* struct ovs_action_push_tnl*/ OVS_ACTION_ATTR_TUNNEL_POP, /* u32 port number. */ #endif + OVS_ACTION_ATTR_TRUNC, /* u16 max_len */ __OVS_ACTION_ATTR_MAX, /* Nothing past this will be accepted * from userspace. */ diff --git a/datapath/vport.c b/datapath/vport.c index 44b9dfb..72a80cc 100644 --- a/datapath/vport.c +++ b/datapath/vport.c @@ -487,6 +487,8 @@ int ovs_vport_receive(struct vport *vport, struct sk_buff *skb, OVS_CB(skb)->input_vport = vport; OVS_CB(skb)->mru = 0; + OVS_CB(skb)->max_len = 0; + if (unlikely(dev_net(skb->dev) != ovs_dp_get_net(vport->dp))) { u32 mark; @@ -615,6 +617,7 @@ static unsigned int packet_length(const struct sk_buff *skb) void ovs_vport_send(struct vport *vport, struct sk_buff *skb) { int mtu = vport->dev->mtu; + u16 max_len = OVS_CB(skb)->max_len; if (unlikely(packet_length(skb) > mtu && !skb_is_gso(skb))) { net_warn_ratelimited("%s: dropped over-mtu packet: %d > %d\n", @@ -624,6 +627,9 @@ void ovs_vport_send(struct vport *vport, struct sk_buff *skb) goto drop; } + if (unlikely(max_len >= OVS_ACTION_OUTPUT_MIN)) + pskb_trim(skb, max_len); + skb->dev = vport->dev; vport->ops->send(skb); return; diff --git a/include/openvswitch/ofp-actions.h b/include/openvswitch/ofp-actions.h index 0608ac1..28b517f 100644 --- a/include/openvswitch/ofp-actions.h +++ b/include/openvswitch/ofp-actions.h @@ -108,6 +108,7 @@ OFPACT(UNROLL_XLATE, ofpact_unroll_xlate, ofpact, "unroll_xlate") \ OFPACT(CT, ofpact_conntrack, ofpact, "ct") \ OFPACT(NAT, ofpact_nat, ofpact, "nat") \ + OFPACT(OUTPUT_TRUNC, ofpact_output_trunc,ofpact, "output_trunc") \ \ /* Debugging actions. \ * \ @@ -290,6 +291,15 @@ struct ofpact_output_reg { struct mf_subfield src; }; +/* OFPACT_OUTPUT_TRUNC. + * + * Used for NXAST_OUTPUT_TRUNC. */ +struct ofpact_output_trunc { + struct ofpact ofpact; + ofp_port_t port; /* Output port. */ + uint16_t max_len; /* Max send len. */ +}; + /* Bundle slave choice algorithm to apply. * * In the descriptions below, 'slaves' is the list of possible slaves in the diff --git a/lib/dp-packet.c b/lib/dp-packet.c index 0c85d50..9772769 100644 --- a/lib/dp-packet.c +++ b/lib/dp-packet.c @@ -30,6 +30,7 @@ dp_packet_init__(struct dp_packet *b, size_t allocated, enum dp_packet_source so dp_packet_reset_offsets(b); pkt_metadata_init(&b->md, 0); dp_packet_rss_invalidate(b); + b->max_len = 0; } static void diff --git a/lib/dp-packet.h b/lib/dp-packet.h index 000b09d..921fa19 100644 --- a/lib/dp-packet.h +++ b/lib/dp-packet.h @@ -58,6 +58,8 @@ struct dp_packet { * or UINT16_MAX. */ uint16_t l4_ofs; /* Transport-level header offset, or UINT16_MAX. */ + uint16_t max_len; /* Max packet sent size. + 0 means sent with original size. */ struct pkt_metadata md; }; @@ -489,6 +491,37 @@ dp_packet_set_allocated(struct dp_packet *b, uint16_t s) } #endif +static inline bool +dp_packet_valid_trunc(struct dp_packet *b) +{ + return b->max_len != 0; +} + +static inline void +dp_packet_reset_trunc(struct dp_packet *b) +{ + b->max_len = 0; +} + +static inline uint32_t +dp_packet_set_trunc(struct dp_packet *b, uint16_t max_len) +{ + if (max_len < OVS_ACTION_OUTPUT_MIN || + max_len >= dp_packet_size(b)) { + dp_packet_reset_trunc(b); + return dp_packet_size(b); + } + + b->max_len = max_len; + return max_len; +} + +static inline uint32_t +dp_packet_get_trunc(struct dp_packet *b) +{ + return b->max_len; +} + static inline void * dp_packet_data(const struct dp_packet *b) { diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c index 1e8a37c..3a56b02 100644 --- a/lib/dpif-netdev.c +++ b/lib/dpif-netdev.c @@ -3742,6 +3742,7 @@ dp_execute_cb(void *aux_, struct dp_packet **packets, int cnt, case OVS_ACTION_ATTR_TUNNEL_PUSH: if (*depth < MAX_RECIRC_DEPTH) { struct dp_packet *tnl_pkt[NETDEV_MAX_BURST]; + struct dp_packet **orig_packets = packets; int err; if (!may_steal) { @@ -3749,6 +3750,14 @@ dp_execute_cb(void *aux_, struct dp_packet **packets, int cnt, packets = tnl_pkt; } + for (int i = 0; i < cnt; i++) { + if (dp_packet_valid_trunc(orig_packets[i])) { + dp_packet_set_size(packets[i], + dp_packet_get_trunc(orig_packets[i])); + dp_packet_reset_trunc(orig_packets[i]); + } + } + err = push_tnl_action(dp, a, packets, cnt); if (!err) { (*depth)++; @@ -3763,6 +3772,7 @@ dp_execute_cb(void *aux_, struct dp_packet **packets, int cnt, case OVS_ACTION_ATTR_TUNNEL_POP: if (*depth < MAX_RECIRC_DEPTH) { + struct dp_packet **orig_packets = packets; odp_port_t portno = u32_to_odp(nl_attr_get_u32(a)); p = dp_netdev_lookup_port(dp, portno); @@ -3771,8 +3781,16 @@ dp_execute_cb(void *aux_, struct dp_packet **packets, int cnt, int err; if (!may_steal) { - dp_netdev_clone_pkt_batch(tnl_pkt, packets, cnt); - packets = tnl_pkt; + dp_netdev_clone_pkt_batch(tnl_pkt, packets, cnt); + packets = tnl_pkt; + } + + for (int i = 0; i < cnt; i++) { + if (dp_packet_valid_trunc(orig_packets[i])) { + dp_packet_set_size(packets[i], + dp_packet_get_trunc(orig_packets[i])); + dp_packet_reset_trunc(orig_packets[i]); + } } err = netdev_pop_header(p->netdev, packets, cnt); @@ -3866,6 +3884,7 @@ dp_execute_cb(void *aux_, struct dp_packet **packets, int cnt, case OVS_ACTION_ATTR_SAMPLE: case OVS_ACTION_ATTR_HASH: case OVS_ACTION_ATTR_UNSPEC: + case OVS_ACTION_ATTR_TRUNC: case __OVS_ACTION_ATTR_MAX: OVS_NOT_REACHED(); } diff --git a/lib/dpif.c b/lib/dpif.c index 2c64d9e..f75277a 100644 --- a/lib/dpif.c +++ b/lib/dpif.c @@ -1093,12 +1093,14 @@ dpif_execute_helper_cb(void *aux_, struct dp_packet **packets, int cnt, struct dpif_execute_helper_aux *aux = aux_; int type = nl_attr_type(action); struct dp_packet *packet = *packets; + struct dp_packet *trunc_packet = NULL, *orig_packet; ovs_assert(cnt == 1); switch ((enum ovs_action_attr)type) { case OVS_ACTION_ATTR_CT: case OVS_ACTION_ATTR_OUTPUT: + case OVS_ACTION_ATTR_TRUNC: case OVS_ACTION_ATTR_TUNNEL_PUSH: case OVS_ACTION_ATTR_TUNNEL_POP: case OVS_ACTION_ATTR_USERSPACE: @@ -1125,6 +1127,18 @@ dpif_execute_helper_cb(void *aux_, struct dp_packet **packets, int cnt, execute.actions_len = NLA_ALIGN(action->nla_len); } + orig_packet = packet; + + if (dp_packet_valid_trunc(packet)) { + if (!may_steal) { + trunc_packet = dp_packet_clone(packet); + packet = trunc_packet; + } + /* Truncation applies to the clone packet or the original + * packet with may_steal == true. */ + dp_packet_set_size(packet, dp_packet_get_trunc(orig_packet)); + } + execute.packet = packet; execute.needs_help = false; execute.probe = false; @@ -1135,6 +1149,13 @@ dpif_execute_helper_cb(void *aux_, struct dp_packet **packets, int cnt, if (dst_set) { ofpbuf_uninit(&execute_actions); } + /* Reset the truncation state so next output action is intact. */ + if (dp_packet_valid_trunc(orig_packet)) { + dp_packet_reset_trunc(orig_packet); + if (!may_steal) { + dp_packet_delete(trunc_packet); + } + } break; } diff --git a/lib/netdev-dummy.c b/lib/netdev-dummy.c index 5acb4e1..eb3dc25 100644 --- a/lib/netdev-dummy.c +++ b/lib/netdev-dummy.c @@ -955,6 +955,10 @@ netdev_dummy_send(struct netdev *netdev, int qid OVS_UNUSED, const void *buffer = dp_packet_data(pkts[i]); size_t size = dp_packet_size(pkts[i]); + if (dp_packet_valid_trunc(pkts[i])) { + size = dp_packet_get_trunc(pkts[i]); + } + if (size < ETH_HEADER_LEN) { error = EMSGSIZE; break; diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c index a2b6b8a..1be318b 100644 --- a/lib/netdev-linux.c +++ b/lib/netdev-linux.c @@ -1170,6 +1170,10 @@ netdev_linux_send(struct netdev *netdev_, int qid OVS_UNUSED, size_t size = dp_packet_size(pkts[i]); ssize_t retval; + if (dp_packet_valid_trunc(pkts[i])) { + size = dp_packet_get_trunc(pkts[i]); + } + if (!is_tap_netdev(netdev_)) { /* Use our AF_PACKET socket to send to this device. */ struct sockaddr_ll sll; diff --git a/lib/netdev.c b/lib/netdev.c index 3e50694..ff0375b 100644 --- a/lib/netdev.c +++ b/lib/netdev.c @@ -763,6 +763,12 @@ netdev_send(struct netdev *netdev, int qid, struct dp_packet **buffers, if (!error) { COVERAGE_INC(netdev_sent); } + + if (!may_steal) { + for (int i = 0; i < cnt; i++) { + dp_packet_reset_trunc(buffers[i]); + } + } return error; } diff --git a/lib/odp-execute.c b/lib/odp-execute.c index 7efd9ec..4183290 100644 --- a/lib/odp-execute.c +++ b/lib/odp-execute.c @@ -501,6 +501,7 @@ requires_datapath_assistance(const struct nlattr *a) case OVS_ACTION_ATTR_HASH: case OVS_ACTION_ATTR_PUSH_MPLS: case OVS_ACTION_ATTR_POP_MPLS: + case OVS_ACTION_ATTR_TRUNC: return false; case OVS_ACTION_ATTR_UNSPEC: @@ -542,6 +543,15 @@ odp_execute_actions(void *dp, struct dp_packet **packets, int cnt, bool steal, } switch ((enum ovs_action_attr) type) { + case OVS_ACTION_ATTR_TRUNC: { + const struct ovs_action_trunc *trunc = + nl_attr_get_unspec(a, sizeof *trunc); + for (i = 0; i < cnt; i++) { + dp_packet_set_trunc(packets[i], trunc->max_len); + } + break; + } + case OVS_ACTION_ATTR_HASH: { const struct ovs_action_hash *hash_act = nl_attr_get(a); diff --git a/lib/odp-util.c b/lib/odp-util.c index 10fb6c2..cfae9cf 100644 --- a/lib/odp-util.c +++ b/lib/odp-util.c @@ -107,6 +107,7 @@ odp_action_len(uint16_t type) switch ((enum ovs_action_attr) type) { case OVS_ACTION_ATTR_OUTPUT: return sizeof(uint32_t); + case OVS_ACTION_ATTR_TRUNC: return sizeof(struct ovs_action_trunc); case OVS_ACTION_ATTR_TUNNEL_PUSH: return ATTR_LEN_VARIABLE; case OVS_ACTION_ATTR_TUNNEL_POP: return sizeof(uint32_t); case OVS_ACTION_ATTR_USERSPACE: return ATTR_LEN_VARIABLE; @@ -775,6 +776,13 @@ format_odp_action(struct ds *ds, const struct nlattr *a) case OVS_ACTION_ATTR_OUTPUT: ds_put_format(ds, "%"PRIu32, nl_attr_get_u32(a)); break; + case OVS_ACTION_ATTR_TRUNC: { + const struct ovs_action_trunc *trunc = + nl_attr_get_unspec(a, sizeof *trunc); + ds_put_format(ds, "trunc(%"PRIu16")", trunc->max_len); + break; + } + break; case OVS_ACTION_ATTR_TUNNEL_POP: ds_put_format(ds, "tnl_pop(%"PRIu32")", nl_attr_get_u32(a)); break; diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c index 39b6fbc..2344d41 100644 --- a/lib/ofp-actions.c +++ b/lib/ofp-actions.c @@ -299,6 +299,9 @@ enum ofp_raw_action_type { /* NX1.0+(36): struct nx_action_nat, ... */ NXAST_RAW_NAT, + /* NX1.0+(38): struct nx_action_output_trunc. */ + NXAST_RAW_OUTPUT_TRUNC, + /* ## ------------------ ## */ /* ## Debugging actions. ## */ /* ## ------------------ ## */ @@ -379,6 +382,7 @@ ofpact_next_flattened(const struct ofpact *ofpact) case OFPACT_CONTROLLER: case OFPACT_ENQUEUE: case OFPACT_OUTPUT_REG: + case OFPACT_OUTPUT_TRUNC: case OFPACT_BUNDLE: case OFPACT_SET_FIELD: case OFPACT_SET_VLAN_VID: @@ -536,6 +540,34 @@ encode_OUTPUT(const struct ofpact_output *output, } static char * OVS_WARN_UNUSED_RESULT +parse_truncate_subfield(struct ofpact_output_trunc *output_trunc, + const char *arg_) +{ + char *key, *value; + char *arg = CONST_CAST(char *, arg_); + + while (ofputil_parse_key_value(&arg, &key, &value)) { + + if (!strcmp(key, "port")) { + unsigned int port; + if (!str_to_uint(value, 10, &port)) { + return xasprintf("%s: named port is not supported", value); + } + if (!ofputil_port_from_string(value, &output_trunc->port)) { + return xasprintf("%s: output to unknown truncate port", + value); + } + } else if (!strcmp(key, "max_len")) { + return str_to_u16(value, key, &output_trunc->max_len); + } else { + return xasprintf("invalid key '%s' in output_trunc argument", + key); + } + } + return NULL; +} + +static char * OVS_WARN_UNUSED_RESULT parse_OUTPUT(const char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { @@ -545,6 +577,10 @@ parse_OUTPUT(const char *arg, struct ofpbuf *ofpacts, output_reg = ofpact_put_OUTPUT_REG(ofpacts); output_reg->max_len = UINT16_MAX; return mf_parse_subfield(&output_reg->src, arg); + } else if (strstr(arg, "port") && strstr(arg, "max_len")) { + struct ofpact_output_trunc *output_trunc; + output_trunc = ofpact_put_OUTPUT_TRUNC(ofpacts); + return parse_truncate_subfield(output_trunc, arg); } else { struct ofpact_output *output; @@ -5512,6 +5548,63 @@ parse_NAT(char *arg, struct ofpbuf *ofpacts, return NULL; } +/* Truncate output action. */ +struct nx_action_output_trunc { + ovs_be16 type; /* OFPAT_VENDOR. */ + ovs_be16 len; /* At least 16. */ + ovs_be32 vendor; /* NX_VENDOR_ID. */ + ovs_be16 subtype; /* NXAST_OUTPUT_TRUNC. */ + ovs_be16 max_len; /* Truncate packet to size bytes */ + ovs_be32 port; /* Output port */ +}; +OFP_ASSERT(sizeof(struct nx_action_output_trunc) == 16); + +static enum ofperr +decode_NXAST_RAW_OUTPUT_TRUNC(const struct nx_action_output_trunc *natrc, + enum ofp_version ofp_version OVS_UNUSED, + struct ofpbuf *out) +{ + struct ofpact_output_trunc *output_trunc; + + output_trunc = ofpact_put_OUTPUT_TRUNC(out); + output_trunc->max_len = ntohs(natrc->max_len); + output_trunc->port = ntohl(natrc->port); + + if (output_trunc->max_len < OVS_ACTION_OUTPUT_MIN) { + return OFPERR_OFPBAC_BAD_ARGUMENT; + } + return 0; +} + +static void +encode_OUTPUT_TRUNC(const struct ofpact_output_trunc *output_trunc, + enum ofp_version ofp_version OVS_UNUSED, + struct ofpbuf *out) +{ + struct nx_action_output_trunc *natrc = put_NXAST_OUTPUT_TRUNC(out); + + natrc->max_len = htons(output_trunc->max_len); + natrc->port = htonl(output_trunc->port); +} + +static char * OVS_WARN_UNUSED_RESULT +parse_OUTPUT_TRUNC(const char *arg, struct ofpbuf *ofpacts OVS_UNUSED, + enum ofputil_protocol *usable_protocols OVS_UNUSED) +{ + /* Disable output_trunc parsing. Expose as output(port=N,max_len=M) and + * reuse parse_OUTPUT to parse output_trunc action. */ + return xasprintf("unknown action %s", arg); +} + +static void +format_OUTPUT_TRUNC(const struct ofpact_output_trunc *a, struct ds *s) +{ + if (ofp_to_u16(a->port) < ofp_to_u16(OFPP_MAX)) { + ds_put_format(s, "%soutput%s(port=%"PRIu16",max_len=%"PRIu16")", + colors.special, colors.end, a->port, a->max_len); + } +} + /* Meter instruction. */ @@ -5906,6 +5999,7 @@ ofpact_is_set_or_move_action(const struct ofpact *a) case OFPACT_NOTE: case OFPACT_OUTPUT: case OFPACT_OUTPUT_REG: + case OFPACT_OUTPUT_TRUNC: case OFPACT_POP_MPLS: case OFPACT_POP_QUEUE: case OFPACT_PUSH_MPLS: @@ -5934,6 +6028,7 @@ ofpact_is_allowed_in_actions_set(const struct ofpact *a) case OFPACT_DEC_TTL: case OFPACT_GROUP: case OFPACT_OUTPUT: + case OFPACT_OUTPUT_TRUNC: case OFPACT_POP_MPLS: case OFPACT_PUSH_MPLS: case OFPACT_PUSH_VLAN: @@ -6157,6 +6252,7 @@ ovs_instruction_type_from_ofpact_type(enum ofpact_type type) case OFPACT_CONTROLLER: case OFPACT_ENQUEUE: case OFPACT_OUTPUT_REG: + case OFPACT_OUTPUT_TRUNC: case OFPACT_BUNDLE: case OFPACT_SET_VLAN_VID: case OFPACT_SET_VLAN_PCP: @@ -6585,6 +6681,10 @@ ofpact_check__(enum ofputil_protocol *usable_protocols, struct ofpact *a, case OFPACT_OUTPUT_REG: return mf_check_src(&ofpact_get_OUTPUT_REG(a)->src, flow); + case OFPACT_OUTPUT_TRUNC: + return ofpact_check_output_port(ofpact_get_OUTPUT_TRUNC(a)->port, + max_ports); + case OFPACT_BUNDLE: return bundle_check(ofpact_get_BUNDLE(a), max_ports, flow); @@ -7262,6 +7362,7 @@ ofpact_outputs_to_port(const struct ofpact *ofpact, ofp_port_t port) return port == OFPP_CONTROLLER; case OFPACT_OUTPUT_REG: + case OFPACT_OUTPUT_TRUNC: case OFPACT_BUNDLE: case OFPACT_SET_VLAN_VID: case OFPACT_SET_VLAN_PCP: diff --git a/ofproto/ofproto-dpif-sflow.c b/ofproto/ofproto-dpif-sflow.c index fbc82b7..7b2d4e0 100644 --- a/ofproto/ofproto-dpif-sflow.c +++ b/ofproto/ofproto-dpif-sflow.c @@ -1140,7 +1140,8 @@ dpif_sflow_read_actions(const struct flow *flow, } break; - case OVS_ACTION_ATTR_USERSPACE: + case OVS_ACTION_ATTR_TRUNC: + case OVS_ACTION_ATTR_USERSPACE: case OVS_ACTION_ATTR_RECIRC: case OVS_ACTION_ATTR_HASH: case OVS_ACTION_ATTR_CT: diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c index 7a201bd..d28d872 100644 --- a/ofproto/ofproto-dpif-xlate.c +++ b/ofproto/ofproto-dpif-xlate.c @@ -3925,6 +3925,43 @@ xlate_output_reg_action(struct xlate_ctx *ctx, } static void +xlate_output_trunc_action(struct xlate_ctx *ctx, + ofp_port_t port, uint16_t max_len) +{ + bool support_trunc = ctx->xbridge->support.trunc; + struct ovs_action_trunc *trunc; + + switch (port) { + case OFPP_IN_PORT: + case OFPP_TABLE: + case OFPP_NORMAL: + case OFPP_FLOOD: + case OFPP_ALL: + /* Controller can use max_len in output + * action to truncate packets. */ + case OFPP_CONTROLLER: + case OFPP_NONE: + case OFPP_LOCAL: + xlate_report(ctx, "output_trunc does not support named port"); + break; + default: + if (port != ctx->xin->flow.in_port.ofp_port) { + trunc = nl_msg_put_unspec_uninit(ctx->odp_actions, + OVS_ACTION_ATTR_TRUNC, + sizeof *trunc); + trunc->max_len = max_len; + xlate_output_action(ctx, port, max_len, false); + if (!support_trunc) { + ctx->xout->slow |= SLOW_ACTION; + } + } else { + xlate_report(ctx, "skipping output to input port"); + } + break; + } +} + +static void xlate_enqueue_action(struct xlate_ctx *ctx, const struct ofpact_enqueue *enqueue) { @@ -4209,6 +4246,7 @@ freeze_unroll_actions(const struct ofpact *a, const struct ofpact *end, for (; a < end; a = ofpact_next(a)) { switch (a->type) { case OFPACT_OUTPUT_REG: + case OFPACT_OUTPUT_TRUNC: case OFPACT_GROUP: case OFPACT_OUTPUT: case OFPACT_CONTROLLER: @@ -4718,6 +4756,11 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len, xlate_output_reg_action(ctx, ofpact_get_OUTPUT_REG(a)); break; + case OFPACT_OUTPUT_TRUNC: + xlate_output_trunc_action(ctx, ofpact_get_OUTPUT_TRUNC(a)->port, + ofpact_get_OUTPUT_TRUNC(a)->max_len); + break; + case OFPACT_LEARN: xlate_learn_action(ctx, ofpact_get_LEARN(a)); break; diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c index 285e377..15aa5aa 100644 --- a/ofproto/ofproto-dpif.c +++ b/ofproto/ofproto-dpif.c @@ -1237,6 +1237,57 @@ check_masked_set_action(struct dpif_backer *backer) return !error; } +/* Tests whether 'backer''s datapath supports truncation of a packet in + * OVS_ACTION_ATTR_TRUNC. We need to disable some features on older + * datapaths that don't support this feature. */ +static bool +check_trunc_action(struct dpif_backer *backer) +{ + struct eth_header *eth; + struct ofpbuf actions; + struct dpif_execute execute; + struct dp_packet packet; + struct ovs_action_trunc *trunc; + int error; + + /* Compose an action with output(port:1, + * max_len:OVS_ACTION_OUTPUT_MIN + 1). + * This translates to one truncate action and one output action. */ + ofpbuf_init(&actions, 64); + trunc = nl_msg_put_unspec_uninit(&actions, + OVS_ACTION_ATTR_TRUNC, sizeof *trunc); + + trunc->max_len = OVS_ACTION_OUTPUT_MIN + 1; + nl_msg_put_odp_port(&actions, OVS_ACTION_ATTR_OUTPUT, 1); + + /* Compose a dummy Ethernet packet. */ + dp_packet_init(&packet, ETH_HEADER_LEN); + eth = dp_packet_put_zeros(&packet, ETH_HEADER_LEN); + eth->eth_type = htons(0x1234); + + /* Execute the actions. On older datapaths this fails with EINVAL, on + * newer datapaths it succeeds. */ + execute.actions = actions.data; + execute.actions_len = actions.size; + execute.packet = &packet; + execute.needs_help = false; + execute.probe = true; + execute.mtu = 0; + + error = dpif_execute(backer->dpif, &execute); + + dp_packet_uninit(&packet); + ofpbuf_uninit(&actions); + + if (error) { + /* Truncate action is not supported. */ + VLOG_INFO("%s: Datapath does not support truncate action", + dpif_name(backer->dpif)); + } + return !error; +} + + #define CHECK_FEATURE__(NAME, SUPPORT, FIELD, VALUE) \ static bool \ check_##NAME(struct dpif_backer *backer) \ @@ -1288,6 +1339,7 @@ check_support(struct dpif_backer *backer) backer->support.odp.recirc = check_recirc(backer); backer->support.odp.max_mpls_depth = check_max_mpls_depth(backer); backer->support.masked_set_action = check_masked_set_action(backer); + backer->support.trunc = check_trunc_action(backer); backer->support.ufid = check_ufid(backer); backer->support.tnl_push_pop = dpif_supports_tnl_push_pop(backer->dpif); diff --git a/ofproto/ofproto-dpif.h b/ofproto/ofproto-dpif.h index 91bf463..feec333 100644 --- a/ofproto/ofproto-dpif.h +++ b/ofproto/ofproto-dpif.h @@ -90,6 +90,9 @@ struct dpif_backer_support { /* True if the datapath supports OVS_FLOW_ATTR_UFID. */ bool ufid; + /* True if the datapath supports OVS_ACTION_ATTR_TRUNC action. */ + bool trunc; + /* Each member represents support for related OVS_KEY_ATTR_* fields. */ struct odp_support odp; }; diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at index 9ac2e2a..5db1c7e 100644 --- a/tests/ofproto-dpif.at +++ b/tests/ofproto-dpif.at @@ -5312,6 +5312,82 @@ PORTNAME portName=p2 ])]) +AT_SETUP([ofproto-dpif - output(port=N,max_len=M) truncate action]) +OVS_VSWITCHD_START +add_of_ports br0 1 2 3 4 5 + +AT_CHECK([ovs-vsctl -- set Interface p1 type=dummy options:pcap=p1.pcap]) +AT_CHECK([ovs-vsctl -- set Interface p2 type=dummy options:pstream=punix:p2.sock]) +AT_CHECK([ovs-vsctl -- set Interface p3 type=dummy options:stream=unix:p2.sock]) +AT_CHECK([ovs-vsctl -- set Interface p4 type=dummy options:pstream=punix:p4.sock]) +AT_CHECK([ovs-vsctl -- set Interface p5 type=dummy options:stream=unix:p4.sock]) + +AT_DATA([flows.txt], [dnl +in_port=3,actions=drop +in_port=5,actions=drop +in_port=1,actions=output(port=2,max_len=64),output:4 +]) +AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) + +dnl Datapath actions +AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=4,ttl=128,frag=no),tcp(src=8,dst=9)'], [0], [stdout]) +AT_CHECK([tail -1 stdout], [0], +[Datapath actions: trunc(64),2,4 +]) + +dnl An 170 byte packet +AT_CHECK([ovs-appctl netdev-dummy/receive p1 '000c29c8a0a4005056c0000808004500009cb4a6000040019003c0a8da01c0a8da640800cb5fa762000556f431ad0009388e08090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f']) + +AT_CHECK([ovs-ofctl parse-pcap p1.pcap], [0], [dnl +icmp,in_port=ANY,vlan_tci=0x0000,dl_src=00:50:56:c0:00:08,dl_dst=00:0c:29:c8:a0:a4,nw_src=192.168.218.1,nw_dst=192.168.218.100,nw_tos=0,nw_ecn=0,nw_ttl=64,icmp_type=8,icmp_code=0 +]) + +dnl packet with truncated size +AT_CHECK([ovs-ofctl dump-flows br0 | grep "in_port=3" | sed -n 's/.*\(n\_bytes=64\).*/\1/p'], [0], [dnl +n_bytes=64 +]) +dnl packet with original size +AT_CHECK([ovs-ofctl dump-flows br0 | grep "in_port=5" | sed -n 's/.*\(n\_bytes=170\).*/\1/p'], [0], [dnl +n_bytes=170 +]) + +dnl More complicated case +AT_CHECK([ovs-ofctl del-flows br0]) +AT_DATA([flows.txt], [dnl +in_port=3,actions=drop +in_port=5,actions=drop +in_port=1,actions=output(port=2,max_len=64),output(port=2,max_len=128),output(port=4,max_len=60),output:2,output:4 +]) +AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) + +dnl Datapath actions +AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=4,ttl=128,frag=no),tcp(src=8,dst=9)'], [0], [stdout]) +AT_CHECK([tail -1 stdout], [0], +[Datapath actions: trunc(64),2,trunc(128),2,trunc(60),4,2,4 +]) + +dnl An 170 byte packet +AT_CHECK([ovs-appctl netdev-dummy/receive p1 '000c29c8a0a4005056c0000808004500009cb4a6000040019003c0a8da01c0a8da640800cb5fa762000556f431ad0009388e08090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f']) + +sleep 1 +dnl packet size: 64 + 128 + 170 = 362 +AT_CHECK([ovs-ofctl dump-flows br0 | grep "in_port=3" | sed -n 's/.*\(n\_bytes=362\).*/\1/p'], [0], [dnl +n_bytes=362 +]) +dnl packet size: 60 + 170 = 230 +AT_CHECK([ovs-ofctl dump-flows br0 | grep "in_port=5" | sed -n 's/.*\(n\_bytes=230\).*/\1/p'], [0], [dnl +n_bytes=230 +]) + +dnl syntax checking +AT_CHECK([ovs-ofctl add-flow br0 'actions=output(port=ALL,max_len=100)'], [1], [], [dnl +ovs-ofctl: ALL: named port is not supported +]) + +OVS_VSWITCHD_STOP +AT_CLEANUP + + AT_SETUP([ofproto-dpif - sFlow packet sampling - IPv4 collector]) CHECK_SFLOW_SAMPLING_PACKET([127.0.0.1]) AT_CLEANUP diff --git a/tests/system-traffic.at b/tests/system-traffic.at index 669226d..7bb5ec0 100644 --- a/tests/system-traffic.at +++ b/tests/system-traffic.at @@ -228,6 +228,157 @@ NS_CHECK_EXEC([at_ns0], [ping -s 3200 -q -c 3 -i 0.3 -w 2 10.1.1.100 | FORMAT_PI OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP +AT_SETUP([datapath - output(port=N,max_len=M) truncate action]) +OVS_TRAFFIC_VSWITCHD_START() +AT_CHECK([ovs-ofctl del-flows br0]) + +dnl Create p0 and ovs-p0(1) +ADD_NAMESPACES(at_ns0) +ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") +NS_CHECK_EXEC([at_ns0], [ip link set dev p0 address e6:66:c1:11:11:11]) +NS_CHECK_EXEC([at_ns0], [arp -s 10.1.1.2 e6:66:c1:22:22:22]) + +dnl Create p1(3) and ovs-p1(2), packets received from ovs-p1 will appear in p1 +AT_CHECK([ip link add p1 type veth peer name ovs-p1]) +on_exit 'ip link del ovs-p1' +AT_CHECK([ip link set dev ovs-p1 up]) +AT_CHECK([ip link set dev p1 up]) +AT_CHECK([ovs-vsctl add-port br0 ovs-p1 -- set interface ovs-p1 ofport_request=2]) +dnl Use p1 to check the truncated packet +AT_CHECK([ovs-vsctl add-port br0 p1 -- set interface p1 ofport_request=3]) + +dnl Create p2(5) and ovs-p2(4) +AT_CHECK([ip link add p2 type veth peer name ovs-p2]) +on_exit 'ip link del ovs-p2' +AT_CHECK([ip link set dev ovs-p2 up]) +AT_CHECK([ip link set dev p2 up]) +AT_CHECK([ovs-vsctl add-port br0 ovs-p2 -- set interface ovs-p2 ofport_request=4]) +dnl Use p2 to check the truncated packet +AT_CHECK([ovs-vsctl add-port br0 p2 -- set interface p2 ofport_request=5]) + +dnl basic test +AT_CHECK([ovs-ofctl del-flows br0]) +AT_DATA([flows.txt], [dnl +in_port=3 dl_dst=e6:66:c1:22:22:22 actions=drop +in_port=5 dl_dst=e6:66:c1:22:22:22 actions=drop +in_port=1 dl_dst=e6:66:c1:22:22:22 actions=output(port=2,max_len=100),output:4 +]) +AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) + +NS_CHECK_EXEC([at_ns0], [ping -q -c 1 -s 1024 -w 0 10.1.1.2 | FORMAT_PING], [0], [dnl +1 packets transmitted, 0 received, 100% packet loss, time 0ms +]) +dnl packet with truncated size +AT_CHECK([ovs-ofctl dump-flows br0 table=0 | grep "in_port=3" | sed -n 's/.*\(n\_bytes=100\).*/\1/p'], [0], [dnl +n_bytes=100 +]) +dnl packet with original size +AT_CHECK([ovs-ofctl dump-flows br0 table=0 | grep "in_port=5" | sed -n 's/.*\(n\_bytes=1066\).*/\1/p'], [0], [dnl +n_bytes=1066 +]) + +dnl more complicated output actions +AT_CHECK([ovs-ofctl del-flows br0]) +AT_DATA([flows.txt], [dnl +in_port=3 dl_dst=e6:66:c1:22:22:22 actions=drop +in_port=5 dl_dst=e6:66:c1:22:22:22 actions=drop +in_port=1 dl_dst=e6:66:c1:22:22:22 actions=output(port=2,max_len=100),output:4,output(port=2,max_len=100),output(port=4,max_len=100),output:2 +]) +AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) + +NS_CHECK_EXEC([at_ns0], [ping -q -c 1 -s 1024 -w 0 10.1.1.2 | FORMAT_PING], [0], [dnl +1 packets transmitted, 0 received, 100% packet loss, time 0ms +]) +dnl 100 + 100 + 1066 = 1266 +AT_CHECK([ovs-ofctl dump-flows br0 table=0 | grep "in_port=3" | sed -n 's/.*\(n\_bytes=1266\).*/\1/p'], [0], [dnl +n_bytes=1266 +]) +dnl 1066 + 100 = 1166 +AT_CHECK([ovs-ofctl dump-flows br0 table=0 | grep "in_port=5" | sed -n 's/.*\(n\_bytes=1166\).*/\1/p'], [0], [dnl +n_bytes=1166 +]) + +OVS_TRAFFIC_VSWITCHD_STOP +AT_CLEANUP + +dnl Create 2 bridges and 2 namespaces to test truncate over +dnl GRE tunnel: +dnl br0: overlay bridge +dnl ns1: connect to br0, with IP:10.1.1.2 +dnl br-underlay: with IP: 172.31.1.100 +dnl ns0: connect to br-underlay, with IP: 10.1.1.1 +AT_SETUP([datapath - truncate and forward to gre tunnel]) +OVS_CHECK_GRE() +OVS_TRAFFIC_VSWITCHD_START() + +ADD_BR([br-underlay]) +ADD_NAMESPACES(at_ns0) +ADD_NAMESPACES(at_ns1) +AT_CHECK([ovs-ofctl add-flow br-underlay "actions=normal"]) + +dnl Set up underlay link from host into the namespace using veth pair. +ADD_VETH(p0, at_ns0, br-underlay, "172.31.1.1/24") +AT_CHECK([ip addr add dev br-underlay "172.31.1.100/24"]) +AT_CHECK([ip link set dev br-underlay up]) + +dnl Set up tunnel endpoints on OVS outside the namespace and with a native +dnl linux device inside the namespace. +ADD_OVS_TUNNEL([gre], [br0], [at_gre0], [172.31.1.1], [10.1.1.100/24]) +ADD_NATIVE_TUNNEL([gretap], [ns_gre0], [at_ns0], [172.31.1.100], [10.1.1.1/24]) +AT_CHECK([ovs-vsctl -- set interface at_gre0 ofport_request=1]) +NS_CHECK_EXEC([at_ns0], [ip link set dev ns_gre0 address e6:66:c1:11:11:11]) +NS_CHECK_EXEC([at_ns0], [arp -s 10.1.1.2 e6:66:c1:22:22:22]) + +dnl Set up (p1 and ovs-p1) at br0 +ADD_VETH(p1, at_ns1, br0, '10.1.1.2/24') +AT_CHECK([ovs-vsctl -- set interface ovs-p1 ofport_request=2]) +NS_CHECK_EXEC([at_ns1], [ip link set dev p1 address e6:66:c1:22:22:22]) +NS_CHECK_EXEC([at_ns1], [arp -s 10.1.1.1 e6:66:c1:11:11:11]) + +dnl Set up (p2 and ovs-p2) as loopback for verifying packet size +AT_CHECK([ip link add p2 type veth peer name ovs-p2]) +on_exit 'ip link del ovs-p2' +AT_CHECK([ip link set dev ovs-p2 up]) +AT_CHECK([ip link set dev p2 up]) +AT_CHECK([ovs-vsctl add-port br0 ovs-p2 -- set interface ovs-p2 ofport_request=3]) +AT_CHECK([ovs-vsctl add-port br0 p2 -- set interface p2 ofport_request=4]) + +dnl use this file as payload file for ncat +AT_CHECK([dd if=/dev/urandom of=payload200.bin bs=200 count=1 2> /dev/null]) +on_exit 'rm -f payload200.bin' + +AT_CHECK([ovs-ofctl del-flows br0]) +AT_DATA([flows.txt], [dnl +priority=99,in_port=1,actions=output(port=2,max_len=100),output(port=3,max_len=100) +priority=99,in_port=2,actions=output(port=1,max_len=100) +priority=99,in_port=4,udp,actions=drop +priority=1,in_port=4,actions=drop +]) +AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) + +AT_CHECK([ovs-ofctl del-flows br-underlay]) +AT_DATA([flows-underlay.txt], [dnl +priority=99,dl_type=0x0800,nw_proto=47,in_port=1,actions=LOCAL +priority=99,dl_type=0x0800,nw_proto=47,in_port=LOCAL,actions=1 +priority=1,actions=NORMAL +]) +AT_CHECK([ovs-ofctl add-flows br-underlay flows-underlay.txt]) + +dnl check tunnel push path, from at_ns1 to at_ns0 +NS_CHECK_EXEC([at_ns1], [nc -u 10.1.1.1 1234 < payload200.bin]) +dnl Before truncation = ETH(14) + IP(20) + UDP(8) + 200 = 242B +AT_CHECK([ovs-ofctl dump-flows br0 | grep "n_bytes=242" > /dev/null]) +dnl After truncation = ETH(14) + GRE(4) + IP(20) + 100 = 138B +AT_CHECK([ovs-ofctl dump-flows br-underlay | grep "n_bytes=138" > /dev/null]) + +dnl check tunnel pop path, from at_ns0 to at_ns1 +NS_CHECK_EXEC([at_ns0], [nc -u 10.1.1.2 5678 < payload200.bin]) +dnl After truncation = 100 byte at loopback device p2(4) +AT_CHECK([ovs-ofctl dump-flows br0 | grep "n_bytes=100" > /dev/null]) + +OVS_TRAFFIC_VSWITCHD_STOP +AT_CLEANUP + AT_SETUP([conntrack - controller]) CHECK_CONNTRACK() OVS_TRAFFIC_VSWITCHD_START()