From patchwork Wed Jul 6 17:58:56 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Horman X-Patchwork-Id: 645419 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 3rl7n41ZTJz9s1h for ; Thu, 7 Jul 2016 03:59:36 +1000 (AEST) Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=netronome-com.20150623.gappssmtp.com header.i=@netronome-com.20150623.gappssmtp.com header.b=vmGhvrit; dkim-atps=neutral Received: from archives.nicira.com (localhost [127.0.0.1]) by archives.nicira.com (Postfix) with ESMTP id E049C10A9F; Wed, 6 Jul 2016 10:59:19 -0700 (PDT) X-Original-To: dev@openvswitch.org Delivered-To: dev@openvswitch.org Received: from mx1e3.cudamail.com (mx1.cudamail.com [69.90.118.67]) by archives.nicira.com (Postfix) with ESMTPS id 6C03110A92 for ; Wed, 6 Jul 2016 10:59:18 -0700 (PDT) Received: from bar5.cudamail.com (localhost [127.0.0.1]) by mx1e3.cudamail.com (Postfix) with ESMTPS id EC3DE4202F5 for ; Wed, 6 Jul 2016 11:59:17 -0600 (MDT) X-ASG-Debug-ID: 1467827956-09eadd1b1a1784b0001-byXFYA Received: from mx3-pf2.cudamail.com ([192.168.14.1]) by bar5.cudamail.com with ESMTP id HGOmNBXuHnTMCBQW (version=TLSv1 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO) for ; Wed, 06 Jul 2016 11:59:16 -0600 (MDT) X-Barracuda-Envelope-From: simon.horman@netronome.com X-Barracuda-RBL-Trusted-Forwarder: 192.168.14.1 Received: from unknown (HELO mail-wm0-f43.google.com) (74.125.82.43) by mx3-pf2.cudamail.com with ESMTPS (AES128-SHA encrypted); 6 Jul 2016 17:59:14 -0000 Received-SPF: neutral (mx3-pf2.cudamail.com: 74.125.82.43 is neither permitted nor denied by SPF record at mktomail.com) X-Barracuda-Apparent-Source-IP: 74.125.82.43 X-Barracuda-RBL-IP: 74.125.82.43 Received: by mail-wm0-f43.google.com with SMTP id f126so182913739wma.1 for ; Wed, 06 Jul 2016 10:59:14 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=netronome-com.20150623.gappssmtp.com; s=20150623; h=from:to:subject:date:message-id:in-reply-to:references; bh=3+hYIaqAcy578q0AL3S4jQ/6Zhr9kB2jUwVw5uVNcXA=; b=vmGhvrit+9I37nwr61rObnaXFxIcf0jdDYQn52dX6mJ6nj/B8yQgoLj6kmBD3BWZAF BnvkDZyAK63ZQBKLcJwK56nYeYXdS2mN0K8qaniEf/Yc4Sh3F4ZV+0MOMTINFUmM1Ye/ 00sjiMA3DHYu+uo2FBIE4uZcte+r8zm8JgcKsldCfruHtCFlKr+iM+ek0iuHBE4DoEnq HbNeCu5TV8U3+98IhQ45goy6Jqqt/I+f26b4rLQDa1BSpilJj4DzMCOoHNmOtTDpI2pW Kc93+CGEZnwUAjuXVpsJ3P5kHY9XTAtlxcgf/VCRzQMxwY/ko7bMnLN4f4le0taT5h2p sJxA== 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:in-reply-to :references; bh=3+hYIaqAcy578q0AL3S4jQ/6Zhr9kB2jUwVw5uVNcXA=; b=OpAxyzQ/atff5PWLx5q83u9S7YqLosVhPCTmsW0o51pByYIEp4nzzyO30sYXP1bf3D WUWPUW2NyI3iQ6sDDYCGKS9PeJnMYrvrhiWDob/Sz1uF2oIlQMR597MWbPTUr4KZtFwc M9Qt/tfh54PCv8/PJpqmQTKYMzesjQfmXFiCFRz3BMjlxB8UmPx8zJwbRqMLepfYFQgs ZAdgDlvOFLC3KOya8ldVc1hqOHqxQqPi1dcjYFUkgG5PY2X/ujLWK/zrmD95oFbbFmx5 rsWTdIrwiruwSnh7a1ZzcejWvaInMgCaLjaUvXYSzGuRNQ+6PetnmgAqZcDeAutaxcqn wJvg== X-Gm-Message-State: ALyK8tJ/U9jHADg73wsfkC33Zj0q7q3uF699jtJbz72TQ04vPaD0qx0oNF9ktUdcpQWBCAcr X-Received: by 10.194.70.41 with SMTP id j9mr21735579wju.30.1467827950784; Wed, 06 Jul 2016 10:59:10 -0700 (PDT) Received: from penelope.isobedori.kobe.vergenet.net (dhcp-077-251-078-056.chello.nl. [77.251.78.56]) by smtp.gmail.com with ESMTPSA id hf7sm4617150wjc.48.2016.07.06.10.59.09 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Wed, 06 Jul 2016 10:59:09 -0700 (PDT) X-CudaMail-Envelope-Sender: simon.horman@netronome.com From: Simon Horman To: dev@openvswitch.org X-CudaMail-MID: CM-V2-705034344 X-CudaMail-DTE: 070616 X-CudaMail-Originating-IP: 74.125.82.43 Date: Wed, 6 Jul 2016 19:58:56 +0200 X-ASG-Orig-Subj: [##CM-V2-705034344##][PATCH v12 2/3] userspace: add layer 3 flow and switching support Message-Id: <1467827937-32353-3-git-send-email-simon.horman@netronome.com> X-Mailer: git-send-email 2.7.0.rc3.207.g0ac5344 In-Reply-To: <1467827937-32353-1-git-send-email-simon.horman@netronome.com> References: <1467827937-32353-1-git-send-email-simon.horman@netronome.com> X-GBUdb-Analysis: 0, 74.125.82.43, Ugly c=0.425302 p=-0.258065 Source Normal X-MessageSniffer-Rules: 0-0-0-32767-c X-Barracuda-Connect: UNKNOWN[192.168.14.1] X-Barracuda-Start-Time: 1467827956 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: 0.60 X-Barracuda-Spam-Status: No, SCORE=0.60 using global scores of TAG_LEVEL=3.5 QUARANTINE_LEVEL=1000.0 KILL_LEVEL=4.0 tests=BSF_SC5_MJ1963, DKIM_SIGNED, RDNS_NONE X-Barracuda-Spam-Report: Code version 3.2, rules version 3.2.3.31064 Rule breakdown below pts rule name description ---- ---------------------- -------------------------------------------------- 0.00 DKIM_SIGNED Domain Keys Identified Mail: message has a signature 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] [PATCH v12 2/3] userspace: add layer 3 flow and switching support 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" From: Lorand Jakab This commit relaxes the assumption that all packets have an Ethernet header, and adds support for layer 3 flows. For each packet received on the Linux kernel datapath the l2 and l3 members of struct ofpbuf are intialized appropriately, and some functions now expect this (notably flow_extract()), in order to differentiate between layer 2 and layer 3 packets. struct flow has now a new 'base_layer' member, because we cannot assume that a flow has no Ethernet header when eth_src and eth_dst are 0. For layer 3 packets, the protocol type is still stored in the eth_type member. Switching L2->L3 and L3->L2 are both implemented by adding the pop_eth and push_eth actions respectively when a transition is detected. The push_eth action puts 0s on both source and destination MACs. These addresses can be modified with mod_dl_dst and mod_dl_src actions. Added new prerequisite MFP_ETHERNET for fields MFF_ETH_SRC, MFF_ETH_DST, MFF_VLAN_TCI, MFF_DL_VLAN, MFF_VLAN_VID and MFF_DL_VLAN_PCP. L3 packets are differentiated from L2 packets by the absence of the OVS_KEY_ATTR_ETHERNET attribute in the flow key. Signed-off-by: Lorand Jakab Signed-off-by: Simon Horman Acked-by: Ben Pfaff --- v11 [Simon Horman] * Fold in previously separate patches "userspace: add layer 3 support to packet metadata" "userspace: extend layer 3 support to cover non-IP packets" * Remove spurious duplication of FLOW_MAX_MPLS_LABELS * Use OVS_KEY_ATTR_ETHERTYPE instead of adding new OVS_KEY_ATTR_PACKET_ETHERTYPE attribute * Commit push/pop_eth actions rather than manually adding them to odp_actions * Added Ack from Ben Pfaff v10 [Simon Horman] * Ensure that packets sent to controller have an ethernet header * Avoid poping ethernet header multiple times resulting in a corrupted packet v9 [Simon Horman] * Rebase: in particular update to use dp_packet rather than ofpbuf * Add dp_packet_is_l3() helper * Make flow_wc_map() LAYER_2/3 aware * mf_are_prereqs_ok() * Make Ethernet prerequisite of MPLS fields * Use if() rather than goto to handle LAYER_2/3 in odp_flow_key_from_flow__() * Only get in_xport if needed in compose_output_action__() v1 - v8 [Lorand Jakab] clanup: commit push/pop eth Signed-off-by: Simon Horman --- build-aux/extract-ofp-fields | 1 + include/openvswitch/flow.h | 17 +++++- include/openvswitch/match.h | 1 + include/openvswitch/meta-flow.h | 9 +-- include/openvswitch/ofp-print.h | 8 ++- lib/dp-packet.h | 14 ++++- lib/dpif-netdev.c | 4 +- lib/dpif-netlink.c | 4 ++ lib/dpif.c | 7 +-- lib/flow.c | 104 +++++++++++++++++++++------------- lib/flow.h | 2 + lib/match.c | 9 ++- lib/meta-flow.c | 10 ++++ lib/netdev-bsd.c | 2 + lib/netdev-dummy.c | 1 + lib/netdev-linux.c | 2 + lib/nx-match.c | 2 +- lib/odp-util.c | 122 +++++++++++++++++++++++++++------------- lib/odp-util.h | 2 +- lib/ofp-print.c | 27 +++++++-- lib/ofp-util.c | 2 +- lib/packets.c | 15 ++++- lib/packets.h | 2 + ofproto/ofproto-dpif-rid.h | 2 +- ofproto/ofproto-dpif-xlate.c | 30 +++++++--- ofproto/ofproto-dpif-xlate.h | 2 +- ofproto/ofproto-dpif.c | 2 +- tests/ofproto-dpif.at | 6 +- tests/tunnel-push-pop-ipv6.at | 10 ++-- tests/tunnel-push-pop.at | 10 ++-- tests/tunnel.at | 10 ++-- 31 files changed, 302 insertions(+), 137 deletions(-) diff --git a/build-aux/extract-ofp-fields b/build-aux/extract-ofp-fields index 8d43e4b14179..7f58788cedc5 100755 --- a/build-aux/extract-ofp-fields +++ b/build-aux/extract-ofp-fields @@ -35,6 +35,7 @@ FORMATTING = {"decimal": ("MFS_DECIMAL", 1, 8), "TCP flags": ("MFS_TCP_FLAGS", 2, 2)} PREREQS = {"none": "MFP_NONE", + "Ethernet": "MFP_ETHERNET", "ARP": "MFP_ARP", "VLAN VID": "MFP_VLAN_VID", "IPv4": "MFP_IPV4", diff --git a/include/openvswitch/flow.h b/include/openvswitch/flow.h index 03d406ba2986..87afb6f32a4b 100644 --- a/include/openvswitch/flow.h +++ b/include/openvswitch/flow.h @@ -23,7 +23,7 @@ /* This sequence number should be incremented whenever anything involving flows * or the wildcarding of flows changes. This will cause build assertion * failures in places which likely need to be updated. */ -#define FLOW_WC_SEQ 35 +#define FLOW_WC_SEQ 36 /* Number of Open vSwitch extension 32-bit registers. */ #define FLOW_N_REGS 8 @@ -52,6 +52,11 @@ BUILD_ASSERT_DECL(FLOW_TNL_F_OAM == NX_TUN_FLAG_OAM); const char *flow_tun_flag_to_string(uint32_t flags); +enum base_layer { + LAYER_2 = 0, + LAYER_3 = 1 +}; + /* Maximum number of supported MPLS labels. */ #define FLOW_MAX_MPLS_LABELS 3 @@ -71,6 +76,10 @@ const char *flow_tun_flag_to_string(uint32_t flags); * lower layer fields are first used to determine if the later fields need to * be looked at. This enables better wildcarding for datapath flows. * + * The starting layer is specified by 'base_layer'. When 'base_layer' is + * LAYER_3, dl_src, dl_tci, and vlan_tci are not used for matching. The + * dl_type field is still used to specify the layer 3 protocol. + * * NOTE: Order of the fields is significant, any change in the order must be * reflected in miniflow_extract()! */ @@ -92,6 +101,8 @@ struct flow { ovs_u128 ct_label; /* Connection label. */ uint32_t conj_id; /* Conjunction ID. */ ofp_port_t actset_output; /* Output port in action set. */ + uint8_t base_layer; /* Fields start at this layer */ + uint8_t pad2[7]; /* Pad to 64 bits. */ /* L2, Order the same as in the Ethernet header! (64-bit aligned) */ struct eth_addr dl_dst; /* Ethernet destination address. */ @@ -129,8 +140,8 @@ BUILD_ASSERT_DECL(sizeof(struct flow_tnl) % sizeof(uint64_t) == 0); /* Remember to update FLOW_WC_SEQ when changing 'struct flow'. */ BUILD_ASSERT_DECL(offsetof(struct flow, igmp_group_ip4) + sizeof(uint32_t) - == sizeof(struct flow_tnl) + 216 - && FLOW_WC_SEQ == 35); + == sizeof(struct flow_tnl) + 224 + && FLOW_WC_SEQ == 36); /* Incremental points at which flow classification may be performed in * segments. diff --git a/include/openvswitch/match.h b/include/openvswitch/match.h index c955753d70a9..60da412eb695 100644 --- a/include/openvswitch/match.h +++ b/include/openvswitch/match.h @@ -96,6 +96,7 @@ void match_set_ct_mark(struct match *, uint32_t ct_mark); void match_set_ct_mark_masked(struct match *, uint32_t ct_mark, uint32_t mask); void match_set_ct_label(struct match *, ovs_u128 ct_label); void match_set_ct_label_masked(struct match *, ovs_u128 ct_label, ovs_u128 mask); +void match_set_base_layer(struct match *, uint8_t base_layer); void match_set_skb_priority(struct match *, uint32_t skb_priority); void match_set_dl_type(struct match *, ovs_be16); void match_set_dl_src(struct match *, const struct eth_addr ); diff --git a/include/openvswitch/meta-flow.h b/include/openvswitch/meta-flow.h index 84a0946d1c6b..2d78b577f76a 100644 --- a/include/openvswitch/meta-flow.h +++ b/include/openvswitch/meta-flow.h @@ -1020,7 +1020,7 @@ enum OVS_PACKED_ENUM mf_field_id { * Type: be16. * Maskable: bitwise. * Formatting: hexadecimal. - * Prerequisites: none. + * Prerequisites: Ethernet. * Access: read/write. * NXM: NXM_OF_VLAN_TCI(4) since v1.1. * OXM: none. @@ -1036,7 +1036,7 @@ enum OVS_PACKED_ENUM mf_field_id { * Type: be16 (low 12 bits). * Maskable: no. * Formatting: decimal. - * Prerequisites: none. + * Prerequisites: Ethernet. * Access: read/write. * NXM: none. * OXM: none. @@ -1054,7 +1054,7 @@ enum OVS_PACKED_ENUM mf_field_id { * Type: be16 (low 12 bits). * Maskable: bitwise. * Formatting: decimal. - * Prerequisites: none. + * Prerequisites: Ethernet. * Access: read/write. * NXM: none. * OXM: OXM_OF_VLAN_VID(6) since OF1.2 and v1.7. @@ -1070,7 +1070,7 @@ enum OVS_PACKED_ENUM mf_field_id { * Type: u8 (low 3 bits). * Maskable: no. * Formatting: decimal. - * Prerequisites: none. + * Prerequisites: Ethernet. * Access: read/write. * NXM: none. * OXM: none. @@ -1793,6 +1793,7 @@ enum OVS_PACKED_ENUM mf_prereqs { MFP_NONE, /* L2 requirements. */ + MFP_ETHERNET, MFP_ARP, MFP_VLAN_VID, MFP_IPV4, diff --git a/include/openvswitch/ofp-print.h b/include/openvswitch/ofp-print.h index 58fd4039d61f..dce80a7cbc88 100644 --- a/include/openvswitch/ofp-print.h +++ b/include/openvswitch/ofp-print.h @@ -21,6 +21,9 @@ #include #include +#include + +#include struct ds; struct ofp10_match; @@ -29,6 +32,7 @@ struct ofp_header; struct ofputil_flow_stats; struct ofputil_table_features; struct ofputil_table_stats; +struct dp_packet; #ifdef __cplusplus extern "C" { @@ -41,7 +45,9 @@ void ofp10_match_print(struct ds *, const struct ofp10_match *, int verbosity); char *ofp_to_string(const void *, size_t, int verbosity); char *ofp10_match_to_string(const struct ofp10_match *, int verbosity); -char *ofp_packet_to_string(const void *data, size_t len); +char *ofp_packet_to_string(const void *data, size_t len, + ovs_be16 packet_ethertype); +char *ofp_dp_packet_to_string(const struct dp_packet *); void ofp_print_flow_stats(struct ds *, struct ofputil_flow_stats *); void ofp_print_version(const struct ofp_header *, struct ds *); diff --git a/lib/dp-packet.h b/lib/dp-packet.h index 7c1e6373ce62..ac6f4d21a099 100644 --- a/lib/dp-packet.h +++ b/lib/dp-packet.h @@ -256,12 +256,20 @@ dp_packet_equal(const struct dp_packet *a, const struct dp_packet *b) !memcmp(dp_packet_data(a), dp_packet_data(b), dp_packet_size(a)); } -/* Get the start of the Ethernet frame. 'l3_ofs' marks the end of the l2 - * headers, so return NULL if it is not set. */ +static inline bool +dp_packet_is_l3(const struct dp_packet *b) +{ + return b->l3_ofs == 0 || b->l2_5_ofs == 0; +} + +/* Get the start of the Ethernet frame. Return NULL if 'b' is an l3 packet + * or if 'l3_ofs', which marks the end of the l2 headers, is not set. */ static inline void * dp_packet_l2(const struct dp_packet *b) { - return (b->l3_ofs != UINT16_MAX) ? dp_packet_data(b) : NULL; + return (b->l3_ofs != UINT16_MAX && !dp_packet_is_l3(b)) + ? dp_packet_data(b) + : NULL; } /* Resets all layer offsets. 'l3' offset must be set before 'l2' can be diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c index 24e032f555bd..a795547aafb6 100644 --- a/lib/dpif-netdev.c +++ b/lib/dpif-netdev.c @@ -3530,9 +3530,7 @@ dp_netdev_upcall(struct dp_netdev_pmd_thread *pmd, struct dp_packet *packet_, ofpbuf_init(&key, 0); odp_flow_key_from_flow(&odp_parms, &key); - packet_str = ofp_packet_to_string(dp_packet_data(packet_), - dp_packet_size(packet_)); - + packet_str = ofp_dp_packet_to_string(packet_); odp_flow_key_format(key.data, key.size, &ds); VLOG_DBG("%s: %s upcall:\n%s\n%s", dp->name, diff --git a/lib/dpif-netlink.c b/lib/dpif-netlink.c index e2bea2309169..9fcf40aa57f4 100644 --- a/lib/dpif-netlink.c +++ b/lib/dpif-netlink.c @@ -2013,6 +2013,10 @@ parse_odp_packet(const struct dpif_netlink *dpif, struct ofpbuf *buf, (char *)dp_packet_data(&upcall->packet) + sizeof(struct nlattr)); dp_packet_set_size(&upcall->packet, nl_attr_get_size(a[OVS_PACKET_ATTR_PACKET])); + if (!nl_attr_find__(upcall->key, upcall->key_len, OVS_KEY_ATTR_ETHERNET)) { + dp_packet_set_l3(&upcall->packet, dp_packet_data(&upcall->packet)); + } + *dp_ifindex = ovs_header->dp_ifindex; return 0; diff --git a/lib/dpif.c b/lib/dpif.c index 4c89e5d3ab5d..0d494b55c91c 100644 --- a/lib/dpif.c +++ b/lib/dpif.c @@ -1413,9 +1413,7 @@ dpif_print_packet(struct dpif *dpif, struct dpif_upcall *upcall) struct ds flow; char *packet; - packet = ofp_packet_to_string(dp_packet_data(&upcall->packet), - dp_packet_size(&upcall->packet)); - + packet = ofp_dp_packet_to_string(&upcall->packet); ds_init(&flow); odp_flow_key_format(upcall->key, upcall->key_len, &flow); @@ -1708,8 +1706,7 @@ log_execute_message(struct dpif *dpif, const struct dpif_execute *execute, struct ds ds = DS_EMPTY_INITIALIZER; char *packet; - packet = ofp_packet_to_string(dp_packet_data(execute->packet), - dp_packet_size(execute->packet)); + packet = ofp_dp_packet_to_string(execute->packet); ds_put_format(&ds, "%s: %sexecute ", dpif_name(dpif), (subexecute ? "sub-" diff --git a/lib/flow.c b/lib/flow.c index a4c1215c7a5c..abe7c220e5fd 100644 --- a/lib/flow.c +++ b/lib/flow.c @@ -124,7 +124,7 @@ struct mf_ctx { * away. Some GCC versions gave warnings on ALWAYS_INLINE, so these are * defined as macros. */ -#if (FLOW_WC_SEQ != 35) +#if (FLOW_WC_SEQ != 36) #define MINIFLOW_ASSERT(X) ovs_assert(X) BUILD_MESSAGE("FLOW_WC_SEQ changed: miniflow_extract() will have runtime " "assertions enabled. Consider updating FLOW_WC_SEQ after " @@ -439,18 +439,18 @@ invalid: arp_buf[1] = eth_addr_zero; } -/* Initializes 'flow' members from 'packet' and 'md' +/* Initializes 'flow' members from 'packet' and 'md'. + * Expects packet->l3_ofs to be set to 0 for layer 3 packets. * - * Initializes 'packet' header l2 pointer to the start of the Ethernet - * header, and the layer offsets as follows: + * Initializes the layer offsets as follows: * * - packet->l2_5_ofs to the start of the MPLS shim header, or UINT16_MAX - * when there is no MPLS shim header. + * when there is no MPLS shim header, or Ethernet header * - * - packet->l3_ofs to just past the Ethernet header, or just past the - * vlan_header if one is present, to the first byte of the payload of the - * Ethernet frame. UINT16_MAX if the frame is too short to contain an - * Ethernet header. + * - packet->l3_ofs (if not 0) to just past the Ethernet header, or just + * past the vlan_header if one is present, to the first byte of the + * payload of the Ethernet frame. UINT16_MAX if the frame is too short to + * contain an Ethernet header. * * - packet->l4_ofs to just past the IPv4 header, if one is present and * has at least the content used for the fields of interest for the flow, @@ -481,9 +481,10 @@ miniflow_extract(struct dp_packet *packet, struct miniflow *dst) uint64_t *values = miniflow_values(dst); struct mf_ctx mf = { FLOWMAP_EMPTY_INITIALIZER, values, values + FLOW_U64S }; - const char *l2; + const char *frame; ovs_be16 dl_type; uint8_t nw_frag, nw_tos, nw_ttl, nw_proto; + bool is_l3 = dp_packet_is_l3(packet); /* Metadata. */ if (flow_tnl_dst_is_set(&md->tunnel)) { @@ -531,23 +532,39 @@ miniflow_extract(struct dp_packet *packet, struct miniflow *dst) } /* Initialize packet's layer pointer and offsets. */ - l2 = data; + frame = data; dp_packet_reset_offsets(packet); - /* Must have full Ethernet header to proceed. */ - if (OVS_UNLIKELY(size < sizeof(struct eth_header))) { - goto out; + if (!is_l3) { + /* No need to store a zero value for base_layer in the miniflow + * which would cost an extra word of storage. */ + BUILD_ASSERT(LAYER_2 == 0); + + /* Must have full Ethernet header to proceed. */ + if (OVS_UNLIKELY(size < sizeof(struct eth_header))) { + goto out; + } else { + ovs_be16 vlan_tci; + + /* Link layer. */ + ASSERT_SEQUENTIAL(dl_dst, dl_src); + miniflow_push_macs(mf, dl_dst, data); + /* dl_type, vlan_tci. */ + vlan_tci = parse_vlan(&data, &size); + dl_type = parse_ethertype(&data, &size); + miniflow_push_be16(mf, dl_type, dl_type); + miniflow_push_be16(mf, vlan_tci, vlan_tci); + } } else { - ovs_be16 vlan_tci; + packet->l3_ofs = 0; + miniflow_pad_from_64(mf, base_layer); + miniflow_push_uint8(mf, base_layer, LAYER_3); + miniflow_pad_to_64(mf, base_layer); - /* Link layer. */ - ASSERT_SEQUENTIAL(dl_dst, dl_src); - miniflow_push_macs(mf, dl_dst, data); - /* dl_type, vlan_tci. */ - vlan_tci = parse_vlan(&data, &size); - dl_type = parse_ethertype(&data, &size); + dl_type = packet->md.packet_ethertype; + miniflow_pad_from_64(mf, dl_type); miniflow_push_be16(mf, dl_type, dl_type); - miniflow_push_be16(mf, vlan_tci, vlan_tci); + miniflow_push_be16(mf, vlan_tci, 0); } /* Parse mpls. */ @@ -555,13 +572,13 @@ miniflow_extract(struct dp_packet *packet, struct miniflow *dst) int count; const void *mpls = data; - packet->l2_5_ofs = (char *)data - l2; + packet->l2_5_ofs = (char *)data - frame; count = parse_mpls(&data, &size); miniflow_push_words_32(mf, mpls_lse, mpls, count); } /* Network layer. */ - packet->l3_ofs = (char *)data - l2; + packet->l3_ofs = (char *)data - frame; nw_frag = 0; if (OVS_LIKELY(dl_type == htons(ETH_TYPE_IP))) { @@ -738,7 +755,7 @@ miniflow_extract(struct dp_packet *packet, struct miniflow *dst) goto out; } - packet->l4_ofs = (char *)data - l2; + packet->l4_ofs = (char *)data - frame; miniflow_push_be32(mf, nw_frag, BYTES_TO_BE32(nw_frag, nw_tos, nw_ttl, nw_proto)); @@ -842,7 +859,7 @@ flow_get_metadata(const struct flow *flow, struct match *flow_metadata) { int i; - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 35); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 36); match_init_catchall(flow_metadata); if (flow->tunnel.tun_id != htonll(0)) { @@ -898,6 +915,10 @@ flow_get_metadata(const struct flow *flow, struct match *flow_metadata) if (!ovs_u128_is_zero(flow->ct_label)) { match_set_ct_label(flow_metadata, flow->ct_label); } + + if (flow->base_layer != LAYER_2) { + match_set_base_layer(flow_metadata, flow->base_layer); + } } const char *ct_state_to_string(uint32_t state) @@ -1248,7 +1269,7 @@ void flow_wildcards_init_for_packet(struct flow_wildcards *wc, memset(&wc->masks, 0x0, sizeof wc->masks); /* Update this function whenever struct flow changes. */ - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 35); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 36); if (flow_tnl_dst_is_set(&flow->tunnel)) { if (flow->tunnel.flags & FLOW_TNL_F_KEY) { @@ -1295,10 +1316,13 @@ void flow_wildcards_init_for_packet(struct flow_wildcards *wc, /* actset_output wildcarded. */ - WC_MASK_FIELD(wc, dl_dst); - WC_MASK_FIELD(wc, dl_src); + if (flow->base_layer == LAYER_2) { + WC_MASK_FIELD(wc, dl_dst); + WC_MASK_FIELD(wc, dl_src); + WC_MASK_FIELD(wc, vlan_tci); + } + WC_MASK_FIELD(wc, dl_type); - WC_MASK_FIELD(wc, vlan_tci); if (flow->dl_type == htons(ETH_TYPE_IP)) { WC_MASK_FIELD(wc, nw_src); @@ -1365,7 +1389,7 @@ void flow_wc_map(const struct flow *flow, struct flowmap *map) { /* Update this function whenever struct flow changes. */ - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 35); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 36); flowmap_init(map); @@ -1388,15 +1412,18 @@ flow_wc_map(const struct flow *flow, struct flowmap *map) FLOWMAP_SET(map, recirc_id); FLOWMAP_SET(map, dp_hash); FLOWMAP_SET(map, in_port); - FLOWMAP_SET(map, dl_dst); - FLOWMAP_SET(map, dl_src); FLOWMAP_SET(map, dl_type); - FLOWMAP_SET(map, vlan_tci); FLOWMAP_SET(map, ct_state); FLOWMAP_SET(map, ct_zone); FLOWMAP_SET(map, ct_mark); FLOWMAP_SET(map, ct_label); + if (flow->base_layer == LAYER_2) { + FLOWMAP_SET(map, dl_dst); + FLOWMAP_SET(map, dl_src); + FLOWMAP_SET(map, vlan_tci); + } + /* Ethertype-dependent fields. */ if (OVS_LIKELY(flow->dl_type == htons(ETH_TYPE_IP))) { FLOWMAP_SET(map, nw_src); @@ -1449,12 +1476,13 @@ void flow_wildcards_clear_non_packet_fields(struct flow_wildcards *wc) { /* Update this function whenever struct flow changes. */ - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 35); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 36); memset(&wc->masks.metadata, 0, sizeof wc->masks.metadata); memset(&wc->masks.regs, 0, sizeof wc->masks.regs); wc->masks.actset_output = 0; wc->masks.conj_id = 0; + wc->masks.base_layer = 0; } /* Returns true if 'wc' matches every packet, false if 'wc' fixes any bits or @@ -1584,7 +1612,7 @@ flow_wildcards_set_xreg_mask(struct flow_wildcards *wc, int idx, uint64_t mask) uint32_t miniflow_hash_5tuple(const struct miniflow *flow, uint32_t basis) { - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 35); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 36); uint32_t hash = basis; if (flow) { @@ -1631,7 +1659,7 @@ ASSERT_SEQUENTIAL(ipv6_src, ipv6_dst); uint32_t flow_hash_5tuple(const struct flow *flow, uint32_t basis) { - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 35); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 36); uint32_t hash = basis; if (flow) { @@ -2098,7 +2126,7 @@ flow_push_mpls(struct flow *flow, int n, ovs_be16 mpls_eth_type, flow->mpls_lse[0] = set_mpls_lse_values(ttl, tc, 1, htonl(label)); /* Clear all L3 and L4 fields and dp_hash. */ - BUILD_ASSERT(FLOW_WC_SEQ == 35); + BUILD_ASSERT(FLOW_WC_SEQ == 36); memset((char *) flow + FLOW_SEGMENT_2_ENDS_AT, 0, sizeof(struct flow) - FLOW_SEGMENT_2_ENDS_AT); flow->dp_hash = 0; diff --git a/lib/flow.h b/lib/flow.h index 5479677410d0..36bb31955ec9 100644 --- a/lib/flow.h +++ b/lib/flow.h @@ -826,6 +826,8 @@ pkt_metadata_from_flow(struct pkt_metadata *md, const struct flow *flow) md->ct_zone = flow->ct_zone; md->ct_mark = flow->ct_mark; md->ct_label = flow->ct_label; + md->base_layer = flow->base_layer; + md->packet_ethertype = flow->dl_type; } static inline bool is_ip_any(const struct flow *flow) diff --git a/lib/match.c b/lib/match.c index db78831c720b..0c5ca655d7d3 100644 --- a/lib/match.c +++ b/lib/match.c @@ -369,6 +369,13 @@ match_set_ct_label_masked(struct match *match, ovs_u128 value, ovs_u128 mask) } void +match_set_base_layer(struct match *match, uint8_t base_layer) +{ + match->flow.base_layer = base_layer; + match->wc.masks.base_layer = UINT8_MAX; +} + +void match_set_dl_type(struct match *match, ovs_be16 dl_type) { match->wc.masks.dl_type = OVS_BE16_MAX; @@ -1060,7 +1067,7 @@ match_format(const struct match *match, struct ds *s, int priority) int i; - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 35); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 36); if (priority != OFP_DEFAULT_PRIORITY) { ds_put_format(s, "%spriority=%s%d,", diff --git a/lib/meta-flow.c b/lib/meta-flow.c index e160de171de6..a8d74d5099f9 100644 --- a/lib/meta-flow.c +++ b/lib/meta-flow.c @@ -369,6 +369,8 @@ mf_are_prereqs_ok(const struct mf_field *mf, const struct flow *flow) case MFP_NONE: return true; + case MFP_ETHERNET: + return flow->base_layer == LAYER_2; case MFP_ARP: return (flow->dl_type == htons(ETH_TYPE_ARP) || flow->dl_type == htons(ETH_TYPE_RARP)); @@ -449,6 +451,9 @@ mf_mask_field_and_prereqs__(const struct mf_field *mf, case MFP_VLAN_VID: WC_MASK_FIELD_MASK(wc, vlan_tci, htons(VLAN_CFI)); break; + case MFP_ETHERNET: + WC_MASK_FIELD(wc, base_layer); + break; case MFP_NONE: break; } @@ -485,6 +490,11 @@ mf_bitmap_set_field_and_prereqs(const struct mf_field *mf, struct mf_bitmap *bm) case MFP_VLAN_VID: bitmap_set1(bm->bm, MFF_VLAN_TCI); break; + case MFP_ETHERNET: + bitmap_set1(bm->bm, MFF_ETH_SRC); + bitmap_set1(bm->bm, MFF_ETH_DST); + bitmap_set1(bm->bm, MFF_ETH_TYPE); + break; case MFP_NONE: break; } diff --git a/lib/netdev-bsd.c b/lib/netdev-bsd.c index 2e92d9768012..3373835eae6d 100644 --- a/lib/netdev-bsd.c +++ b/lib/netdev-bsd.c @@ -580,6 +580,7 @@ netdev_rxq_bsd_recv_pcap(struct netdev_rxq_bsd *rxq, struct dp_packet *buffer) if (ret > 0) { dp_packet_set_size(buffer, dp_packet_size(buffer) + arg.retval); + dp_packet_reset_offsets(buffer); return 0; } if (ret == -1) { @@ -606,6 +607,7 @@ netdev_rxq_bsd_recv_tap(struct netdev_rxq_bsd *rxq, struct dp_packet *buffer) ssize_t retval = read(rxq->fd, dp_packet_data(buffer), size); if (retval >= 0) { dp_packet_set_size(buffer, dp_packet_size(buffer) + retval); + dp_packet_reset_offsets(buffer); return 0; } else if (errno != EINTR) { if (errno != EAGAIN) { diff --git a/lib/netdev-dummy.c b/lib/netdev-dummy.c index 24c107e261f8..1a74f39106d0 100644 --- a/lib/netdev-dummy.c +++ b/lib/netdev-dummy.c @@ -273,6 +273,7 @@ dummy_packet_stream_run(struct netdev_dummy *dev, struct dummy_packet_stream *s) dp_packet_clone(&s->rxbuf), 0); dp_packet_clear(&s->rxbuf); } + dp_packet_reset_offsets(&s->rxbuf); } else if (retval != -EAGAIN) { error = (retval < 0 ? -retval : dp_packet_size(&s->rxbuf) ? EPROTO diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c index ef11d12bae46..d7d442442182 100644 --- a/lib/netdev-linux.c +++ b/lib/netdev-linux.c @@ -1047,6 +1047,7 @@ netdev_linux_rxq_recv_sock(int fd, struct dp_packet *buffer) } dp_packet_set_size(buffer, dp_packet_size(buffer) + retval); + dp_packet_reset_offsets(buffer); for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg; cmsg = CMSG_NXTHDR(&msgh, cmsg)) { const struct tpacket_auxdata *aux; @@ -1087,6 +1088,7 @@ netdev_linux_rxq_recv_tap(int fd, struct dp_packet *buffer) } dp_packet_set_size(buffer, dp_packet_size(buffer) + retval); + dp_packet_reset_offsets(buffer); return 0; } diff --git a/lib/nx-match.c b/lib/nx-match.c index 9a2ada9ddefc..505c6f9bcac3 100644 --- a/lib/nx-match.c +++ b/lib/nx-match.c @@ -917,7 +917,7 @@ nx_put_raw(struct ofpbuf *b, enum ofp_version oxm, const struct match *match, int match_len; int i; - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 35); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 36); /* Metadata. */ if (match->wc.masks.dp_hash) { diff --git a/lib/odp-util.c b/lib/odp-util.c index ef213a69f6c7..83d91c1c419b 100644 --- a/lib/odp-util.c +++ b/lib/odp-util.c @@ -4307,7 +4307,7 @@ odp_flow_key_from_flow__(const struct odp_flow_key_parms *parms, bool export_mask, struct ofpbuf *buf) { struct ovs_key_ethernet *eth_key; - size_t encap; + size_t encap = 0; const struct flow *flow = parms->flow; const struct flow *data = export_mask ? parms->mask : parms->flow; @@ -4345,41 +4345,43 @@ odp_flow_key_from_flow__(const struct odp_flow_key_parms *parms, nl_msg_put_odp_port(buf, OVS_KEY_ATTR_IN_PORT, data->in_port.odp_port); } - eth_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_ETHERNET, - sizeof *eth_key); - get_ethernet_key(data, eth_key); + if (flow->base_layer == LAYER_2) { + eth_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_ETHERNET, + sizeof *eth_key); + get_ethernet_key(data, eth_key); - if (flow->vlan_tci != htons(0) || flow->dl_type == htons(ETH_TYPE_VLAN)) { - if (export_mask) { - nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, OVS_BE16_MAX); - } else { - nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, htons(ETH_TYPE_VLAN)); - } - nl_msg_put_be16(buf, OVS_KEY_ATTR_VLAN, data->vlan_tci); - encap = nl_msg_start_nested(buf, OVS_KEY_ATTR_ENCAP); - if (flow->vlan_tci == htons(0)) { - goto unencap; + if (flow->vlan_tci != htons(0) || + flow->dl_type == htons(ETH_TYPE_VLAN)) { + if (export_mask) { + nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, OVS_BE16_MAX); + } else { + nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, + htons(ETH_TYPE_VLAN)); + } + nl_msg_put_be16(buf, OVS_KEY_ATTR_VLAN, data->vlan_tci); + encap = nl_msg_start_nested(buf, OVS_KEY_ATTR_ENCAP); + if (flow->vlan_tci == htons(0)) { + goto unencap; + } } - } else { - encap = 0; - } - if (ntohs(flow->dl_type) < ETH_TYPE_MIN) { - /* For backwards compatibility with kernels that don't support - * wildcarding, the following convention is used to encode the - * OVS_KEY_ATTR_ETHERTYPE for key and mask: - * - * key mask matches - * -------- -------- ------- - * >0x5ff 0xffff Specified Ethernet II Ethertype. - * >0x5ff 0 Any Ethernet II or non-Ethernet II frame. - * 0xffff Any non-Ethernet II frame (except valid - * 802.3 SNAP packet with valid eth_type). - */ - if (export_mask) { - nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, OVS_BE16_MAX); + if (ntohs(flow->dl_type) < ETH_TYPE_MIN) { + /* For backwards compatibility with kernels that don't support + * wildcarding, the following convention is used to encode the + * OVS_KEY_ATTR_ETHERTYPE for key and mask: + * + * key mask matches + * -------- -------- ------- + * >0x5ff 0xffff Specified Ethernet II Ethertype. + * >0x5ff 0 Any Ethernet II or non-Ethernet II frame. + * 0xffff Any non-Ethernet II frame (except valid + * 802.3 SNAP packet with valid eth_type). + */ + if (export_mask) { + nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, OVS_BE16_MAX); + } + goto unencap; } - goto unencap; } nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, data->dl_type); @@ -4538,6 +4540,10 @@ odp_key_from_pkt_metadata(struct ofpbuf *buf, const struct pkt_metadata *md) if (md->in_port.odp_port != ODPP_NONE) { nl_msg_put_odp_port(buf, OVS_KEY_ATTR_IN_PORT, md->in_port.odp_port); } + + if (md->base_layer == LAYER_3) { + nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, md->packet_ethertype); + } } /* Generate packet metadata from the given ODP flow key. */ @@ -4546,10 +4552,13 @@ odp_key_to_pkt_metadata(const struct nlattr *key, size_t key_len, struct pkt_metadata *md) { const struct nlattr *nla; + ovs_be16 ethertype; size_t left; uint32_t wanted_attrs = 1u << OVS_KEY_ATTR_PRIORITY | 1u << OVS_KEY_ATTR_SKB_MARK | 1u << OVS_KEY_ATTR_TUNNEL | - 1u << OVS_KEY_ATTR_IN_PORT; + 1u << OVS_KEY_ATTR_IN_PORT | 1u << OVS_KEY_ATTR_ETHERTYPE | + 1u << OVS_KEY_ATTR_ETHERNET | 1u << OVS_KEY_ATTR_IPV4 | + 1u << OVS_KEY_ATTR_IPV6; pkt_metadata_init(md, ODPP_NONE); @@ -4614,14 +4623,38 @@ odp_key_to_pkt_metadata(const struct nlattr *key, size_t key_len, md->in_port.odp_port = nl_attr_get_odp_port(nla); wanted_attrs &= ~(1u << OVS_KEY_ATTR_IN_PORT); break; + case OVS_KEY_ATTR_ETHERNET: + wanted_attrs &= ~(1u << OVS_KEY_ATTR_ETHERNET); + break; + case OVS_KEY_ATTR_ETHERTYPE: + ethertype = nl_attr_get_be16(nla); + wanted_attrs &= ~(1u << OVS_KEY_ATTR_ETHERTYPE); + break; + case OVS_KEY_ATTR_IPV4: + wanted_attrs &= ~(1u << OVS_KEY_ATTR_IPV4); + break; + case OVS_KEY_ATTR_IPV6: + wanted_attrs &= ~(1u << OVS_KEY_ATTR_IPV6); + break; default: break; } if (!wanted_attrs) { - return; /* Have everything. */ + break; /* Have everything. */ } } + + /* OVS_KEY_ATTR_ETHERTYPE present and OVS_KEY_ATTR_ETHERTYPE absent + * indicates Layer 3. */ + if (!(wanted_attrs & (1u << OVS_KEY_ATTR_ETHERTYPE)) && + wanted_attrs & (1u << OVS_KEY_ATTR_ETHERTYPE)) { + md->base_layer = LAYER_3; + md->packet_ethertype = ethertype; + } else { + md->base_layer = LAYER_2; + } + } uint32_t @@ -4785,7 +4818,15 @@ parse_ethertype(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1], *expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ETHERTYPE; } else { if (!is_mask) { - flow->dl_type = htons(FLOW_DL_TYPE_NONE); + if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_IPV4)) { + flow->dl_type = htons(ETH_TYPE_IP); + } else if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_IPV6)) { + flow->dl_type = htons(ETH_TYPE_IPV6); + } else { + flow->dl_type = htons(FLOW_DL_TYPE_NONE); + } + } else if (src_flow->base_layer == LAYER_3) { + flow->dl_type = htons(0xffff); } else if (ntohs(src_flow->dl_type) < ETH_TYPE_MIN) { /* See comments in odp_flow_key_from_flow__(). */ VLOG_ERR_RL(&rl, "mask expected for non-Ethernet II frame"); @@ -5204,12 +5245,13 @@ odp_flow_key_to_flow__(const struct nlattr *key, size_t key_len, eth_key = nl_attr_get(attrs[OVS_KEY_ATTR_ETHERNET]); put_ethernet_key(eth_key, flow); - if (is_mask) { - expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ETHERNET; - } - } - if (!is_mask) { + flow->base_layer = LAYER_2; expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ETHERNET; + } else if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ETHERTYPE)) { + flow->base_layer = LAYER_3; + expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ETHERTYPE; + } else if (is_mask && src_flow->base_layer == LAYER_3) { + flow->base_layer = LAYER_3; } /* Get Ethertype or 802.1Q TPID or FLOW_DL_TYPE_NONE. */ diff --git a/lib/odp-util.h b/lib/odp-util.h index 53ee66175984..50e52e49be67 100644 --- a/lib/odp-util.h +++ b/lib/odp-util.h @@ -141,7 +141,7 @@ void odp_portno_names_destroy(struct hmap *portno_names); * add another field and forget to adjust this value. */ #define ODPUTIL_FLOW_KEY_BYTES 640 -BUILD_ASSERT_DECL(FLOW_WC_SEQ == 35); +BUILD_ASSERT_DECL(FLOW_WC_SEQ == 36); /* A buffer with sufficient size and alignment to hold an nlattr-formatted flow * key. An array of "struct nlattr" might not, in theory, be sufficiently diff --git a/lib/ofp-print.c b/lib/ofp-print.c index 919c95d64c37..10467c6e081e 100644 --- a/lib/ofp-print.c +++ b/lib/ofp-print.c @@ -55,10 +55,10 @@ static void ofp_print_queue_name(struct ds *string, uint32_t port); static void ofp_print_error(struct ds *, enum ofperr); -/* Returns a string that represents the contents of the Ethernet frame in the +/* Returns a string that represents the contents of the packet in the * 'len' bytes starting at 'data'. The caller must free the returned string.*/ char * -ofp_packet_to_string(const void *data, size_t len) +ofp_packet_to_string(const void *data, size_t len, ovs_be16 packet_ethertype) { struct ds ds = DS_EMPTY_INITIALIZER; struct dp_packet buf; @@ -66,6 +66,11 @@ ofp_packet_to_string(const void *data, size_t len) size_t l4_size; dp_packet_use_const(&buf, data, len); + if (packet_ethertype) { + /* This is a layer 3 packet */ + buf.md.packet_ethertype = packet_ethertype; + buf.l3_ofs = 0; + } flow_extract(&buf, &flow); flow_format(&ds, &flow); @@ -96,6 +101,17 @@ ofp_packet_to_string(const void *data, size_t len) return ds_cstr(&ds); } +/* Returns a string that represents the contents of the packet in the + * 'len' bytes starting at 'data'. The caller must free the returned string.*/ +char * +ofp_dp_packet_to_string(const struct dp_packet *p) +{ + ovs_assert(!dp_packet_is_l3(p) || ntohs(p->md.packet_ethertype)); + return ofp_packet_to_string(dp_packet_data(p), dp_packet_size(p), + dp_packet_is_l3(p) ? p->md.packet_ethertype + : htons(0)); +} + static void format_hex_arg(struct ds *s, const uint8_t *data, size_t len) { @@ -200,7 +216,7 @@ ofp_print_packet_in(struct ds *string, const struct ofp_header *oh, if (verbosity > 0) { char *packet = ofp_packet_to_string(public->packet, - public->packet_len); + public->packet_len, htons(0)); ds_put_cstr(string, packet); free(packet); } @@ -236,7 +252,8 @@ ofp_print_packet_out(struct ds *string, const struct ofp_header *oh, if (po.buffer_id == UINT32_MAX) { ds_put_format(string, " data_len=%"PRIuSIZE, po.packet_len); if (verbosity > 0 && po.packet_len > 0) { - char *packet = ofp_packet_to_string(po.packet, po.packet_len); + char *packet = ofp_packet_to_string(po.packet, po.packet_len, + htons(0)); ds_put_char(string, '\n'); ds_put_cstr(string, packet); free(packet); @@ -3706,5 +3723,5 @@ ofp_print(FILE *stream, const void *oh, size_t len, int verbosity) void ofp_print_packet(FILE *stream, const void *data, size_t len) { - print_and_free(stream, ofp_packet_to_string(data, len)); + print_and_free(stream, ofp_packet_to_string(data, len, htons(0))); } diff --git a/lib/ofp-util.c b/lib/ofp-util.c index 2b214ea3b6f4..4ae83df11218 100644 --- a/lib/ofp-util.c +++ b/lib/ofp-util.c @@ -101,7 +101,7 @@ ofputil_netmask_to_wcbits(ovs_be32 netmask) void ofputil_wildcard_from_ofpfw10(uint32_t ofpfw, struct flow_wildcards *wc) { - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 35); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 36); /* Initialize most of wc. */ flow_wildcards_init_catchall(wc); diff --git a/lib/packets.c b/lib/packets.c index 1aaf16de01ec..82801ea7754d 100644 --- a/lib/packets.c +++ b/lib/packets.c @@ -232,16 +232,24 @@ push_eth(struct dp_packet *packet, const struct eth_addr *dst, eh->eth_src = *src; } -/* Removes Ethernet header, including all VLAN and MPLS headers, from 'packet'. +/* Removes Ethernet header, including VLAN header, from 'packet'. * * Previous to calling this function, 'ofpbuf_l3(packet)' must not be NULL */ void pop_eth(struct dp_packet *packet) { + char *l2_5 = dp_packet_l2_5(packet);; + int increment; + ovs_assert(dp_packet_l3(packet) != NULL); - dp_packet_resize_l2_5(packet, -packet->l3_ofs); - dp_packet_set_l2_5(packet, NULL); + if (l2_5) { + increment = packet->l2_5_ofs; + } else { + increment = packet->l3_ofs; + } + + dp_packet_resize_l2(packet, -increment); } /* Set ethertype of the packet. */ @@ -251,6 +259,7 @@ set_ethertype(struct dp_packet *packet, ovs_be16 eth_type) struct eth_header *eh = dp_packet_l2(packet); if (!eh) { + packet->md.packet_ethertype = eth_type; return; } diff --git a/lib/packets.h b/lib/packets.h index 58de05ff2026..7c3484d7ebea 100644 --- a/lib/packets.h +++ b/lib/packets.h @@ -104,6 +104,8 @@ struct pkt_metadata { uint32_t ct_mark; /* Connection mark. */ ovs_u128 ct_label; /* Connection label. */ union flow_in_port in_port; /* Input port. */ + ovs_be16 packet_ethertype; /* Ethertype of the packet */ + uint8_t base_layer; /* Packet starts at this layer */ struct flow_tnl tunnel; /* Encapsulating tunnel parameters. Note that * if 'ip_dst' == 0, the rest of the fields may * be uninitialized. */ diff --git a/ofproto/ofproto-dpif-rid.h b/ofproto/ofproto-dpif-rid.h index c34d76022ea5..3bca81777450 100644 --- a/ofproto/ofproto-dpif-rid.h +++ b/ofproto/ofproto-dpif-rid.h @@ -99,7 +99,7 @@ struct rule; /* Metadata for restoring pipeline context after recirculation. Helpers * are inlined below to keep them together with the definition for easier * updates. */ -BUILD_ASSERT_DECL(FLOW_WC_SEQ == 35); +BUILD_ASSERT_DECL(FLOW_WC_SEQ == 36); struct frozen_metadata { /* Metadata in struct flow. */ diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c index 1977b6b25501..3bac590b20c3 100644 --- a/ofproto/ofproto-dpif-xlate.c +++ b/ofproto/ofproto-dpif-xlate.c @@ -156,6 +156,7 @@ struct xport { bool may_enable; /* May be enabled in bonds. */ bool is_tunnel; /* Is a tunnel port. */ + bool is_layer3; /* Is a layer 3 port. */ struct cfm *cfm; /* CFM handle or null. */ struct bfd *bfd; /* BFD handle or null. */ @@ -602,7 +603,7 @@ static void xlate_xport_set(struct xport *xport, odp_port_t odp_port, int stp_port_no, const struct rstp_port *rstp_port, enum ofputil_port_config config, enum ofputil_port_state state, bool is_tunnel, - bool may_enable); + bool may_enable, bool is_layer3); static void xlate_xbridge_remove(struct xlate_cfg *, struct xbridge *); static void xlate_xbundle_remove(struct xlate_cfg *, struct xbundle *); static void xlate_xport_remove(struct xlate_cfg *, struct xport *); @@ -765,12 +766,13 @@ xlate_xport_set(struct xport *xport, odp_port_t odp_port, const struct bfd *bfd, const struct lldp *lldp, int stp_port_no, const struct rstp_port* rstp_port, enum ofputil_port_config config, enum ofputil_port_state state, - bool is_tunnel, bool may_enable) + bool is_tunnel, bool may_enable, bool is_layer3) { xport->config = config; xport->state = state; xport->stp_port_no = stp_port_no; xport->is_tunnel = is_tunnel; + xport->is_layer3 = is_layer3; xport->may_enable = may_enable; xport->odp_port = odp_port; @@ -861,7 +863,7 @@ xlate_xport_copy(struct xbridge *xbridge, struct xbundle *xbundle, xlate_xport_set(new_xport, xport->odp_port, xport->netdev, xport->cfm, xport->bfd, xport->lldp, xport->stp_port_no, xport->rstp_port, xport->config, xport->state, - xport->is_tunnel, xport->may_enable); + xport->is_tunnel, xport->may_enable, xport->is_layer3); if (xport->peer) { struct xport *peer = xport_lookup(new_xcfg, xport->peer->ofport); @@ -1099,7 +1101,7 @@ xlate_ofport_set(struct ofproto_dpif *ofproto, struct ofbundle *ofbundle, const struct ofproto_port_queue *qdscp_list, size_t n_qdscp, enum ofputil_port_config config, enum ofputil_port_state state, bool is_tunnel, - bool may_enable) + bool may_enable, bool is_layer3) { size_t i; struct xport *xport; @@ -1120,7 +1122,7 @@ xlate_ofport_set(struct ofproto_dpif *ofproto, struct ofbundle *ofbundle, xlate_xport_set(xport, odp_port, netdev, cfm, bfd, lldp, stp_port_no, rstp_port, config, state, is_tunnel, - may_enable); + may_enable, is_layer3); if (xport->peer) { xport->peer->peer = NULL; @@ -2476,7 +2478,7 @@ xlate_normal(struct xlate_ctx *ctx) } /* Learn source MAC. */ - if (ctx->xin->may_learn) { + if (ctx->xin->may_learn && !in_port->is_layer3) { update_learning_table(ctx->xbridge, flow, wc, vlan, in_xbundle); } if (ctx->xin->xcache) { @@ -3007,7 +3009,7 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port, /* If 'struct flow' gets additional metadata, we'll need to zero it out * before traversing a patch port. */ - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 35); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 36); memset(&flow_tnl, 0, sizeof flow_tnl); if (!xport) { @@ -3045,6 +3047,14 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port, } } + if (xport->is_layer3) { + if (flow->base_layer == LAYER_2) { + flow->base_layer = LAYER_3; + } + } else if (flow->base_layer == LAYER_3) { + flow->base_layer = LAYER_2; + } + if (xport->peer) { const struct xport *peer = xport->peer; struct flow old_flow = ctx->xin->flow; @@ -3675,9 +3685,14 @@ execute_controller_action(struct xlate_ctx *ctx, int len, uint16_t controller_id, const uint8_t *userdata, size_t userdata_len) { + struct flow *flow = &ctx->xin->flow; struct dp_packet_batch batch; struct dp_packet *packet; + if (flow->base_layer == LAYER_3) { + flow->base_layer = LAYER_2; + } + ctx->xout->slow |= SLOW_CONTROLLER; xlate_commit_actions(ctx); if (!ctx->xin->packet) { @@ -3685,6 +3700,7 @@ execute_controller_action(struct xlate_ctx *ctx, int len, } packet = dp_packet_clone(ctx->xin->packet); + packet_batch_init_packet(&batch, packet); odp_execute_actions(NULL, &batch, false, ctx->odp_actions->data, ctx->odp_actions->size, NULL); diff --git a/ofproto/ofproto-dpif-xlate.h b/ofproto/ofproto-dpif-xlate.h index 7808a603e2d4..3ca1c77169b2 100644 --- a/ofproto/ofproto-dpif-xlate.h +++ b/ofproto/ofproto-dpif-xlate.h @@ -174,7 +174,7 @@ void xlate_ofport_set(struct ofproto_dpif *, struct ofbundle *, const struct ofproto_port_queue *qdscp, size_t n_qdscp, enum ofputil_port_config, enum ofputil_port_state, bool is_tunnel, - bool may_enable); + bool may_enable, bool is_l3); void xlate_ofport_remove(struct ofport_dpif *); struct ofproto_dpif * xlate_lookup_ofproto(const struct dpif_backer *, diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c index ce9383ad4772..89637468fb59 100644 --- a/ofproto/ofproto-dpif.c +++ b/ofproto/ofproto-dpif.c @@ -641,7 +641,7 @@ type_run(const char *type) ofport->rstp_port, ofport->qdscp, ofport->n_qdscp, ofport->up.pp.config, ofport->up.pp.state, ofport->is_tunnel, - ofport->may_enable); + ofport->may_enable, ofport->is_layer3); } xlate_txn_commit(); } diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at index 8287d90fce37..42392d46651c 100644 --- a/tests/ofproto-dpif.at +++ b/tests/ofproto-dpif.at @@ -4683,15 +4683,15 @@ in_port=2 actions=output:1 ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) -odp_flow="in_port(p1)" -br_flow="in_port=1" +odp_flow="in_port(p1),eth(src=00:00:00:00:00:00,dst=00:00:00:00:00:00)" +br_flow="in_port=1,dl_dst=00:00:00:00:00:00" # Test command: ofproto/trace odp_flow with in_port as a name. AT_CHECK([ovs-appctl ofproto/trace "$odp_flow"], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [dnl Datapath actions: 2 ]) -odp_flow="in_port(1)" +odp_flow="in_port(1),eth(src=00:00:00:00:00:00,dst=00:00:00:00:00:00)" # Test command: ofproto/trace odp_flow AT_CHECK([ovs-appctl ofproto/trace "$odp_flow"], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [dnl diff --git a/tests/tunnel-push-pop-ipv6.at b/tests/tunnel-push-pop-ipv6.at index e88776c45fb2..ad48a099e5ed 100644 --- a/tests/tunnel-push-pop-ipv6.at +++ b/tests/tunnel-push-pop-ipv6.at @@ -88,28 +88,28 @@ AT_CHECK([tail -1 stdout], [0], dnl Check VXLAN tunnel push AT_CHECK([ovs-ofctl add-flow int-br action=2]) -AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout]) +AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: tnl_push(tnl_port(4789),header(size=70,type=4,eth(dst=f8:bc:12:44:34:b6,src=aa:55:aa:55:00:00,dl_type=0x86dd),ipv6(src=2001:cafe::88,dst=2001:cafe::92,label=0,proto=17,tclass=0x0,hlimit=64),udp(src=0,dst=4789,csum=0xffff),vxlan(flags=0x8000000,vni=0x7b)),out_port(100)) ]) dnl Check VXLAN tunnel push set tunnel id by flow and checksum AT_CHECK([ovs-ofctl add-flow int-br "actions=set_tunnel:124,4"]) -AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout]) +AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: tnl_push(tnl_port(4789),header(size=70,type=4,eth(dst=f8:bc:12:44:34:b7,src=aa:55:aa:55:00:00,dl_type=0x86dd),ipv6(src=2001:cafe::88,dst=2001:cafe::93,label=0,proto=17,tclass=0x0,hlimit=64),udp(src=0,dst=4789,csum=0xffff),vxlan(flags=0x8000000,vni=0x7c)),out_port(100)) ]) dnl Check GRE tunnel push AT_CHECK([ovs-ofctl add-flow int-br action=3]) -AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout]) +AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: tnl_push(tnl_port(3),header(size=62,type=3,eth(dst=f8:bc:12:44:34:b6,src=aa:55:aa:55:00:00,dl_type=0x86dd),ipv6(src=2001:cafe::88,dst=2001:cafe::92,label=0,proto=47,tclass=0x0,hlimit=64),gre((flags=0x2000,proto=0x6558),key=0x1c8)),out_port(100)) ]) dnl Check Geneve tunnel push AT_CHECK([ovs-ofctl add-flow int-br "actions=set_field:2001:cafe::92->tun_ipv6_dst,5"]) -AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout]) +AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: tnl_push(tnl_port(6081),header(size=70,type=5,eth(dst=f8:bc:12:44:34:b6,src=aa:55:aa:55:00:00,dl_type=0x86dd),ipv6(src=2001:cafe::88,dst=2001:cafe::92,label=0,proto=17,tclass=0x0,hlimit=64),udp(src=0,dst=6081,csum=0xffff),geneve(vni=0x7b)),out_port(100)) ]) @@ -117,7 +117,7 @@ AT_CHECK([tail -1 stdout], [0], dnl Check Geneve tunnel push with options AT_CHECK([ovs-ofctl add-tlv-map int-br "{class=0xffff,type=0x80,len=4}->tun_metadata0"]) AT_CHECK([ovs-ofctl add-flow int-br "actions=set_field:2001:cafe::92->tun_ipv6_dst,set_field:0xa->tun_metadata0,5"]) -AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout]) +AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: tnl_push(tnl_port(6081),header(size=78,type=5,eth(dst=f8:bc:12:44:34:b6,src=aa:55:aa:55:00:00,dl_type=0x86dd),ipv6(src=2001:cafe::88,dst=2001:cafe::92,label=0,proto=17,tclass=0x0,hlimit=64),udp(src=0,dst=6081,csum=0xffff),geneve(crit,vni=0x7b,options({class=0xffff,type=0x80,len=4,0xa}))),out_port(100)) ]) diff --git a/tests/tunnel-push-pop.at b/tests/tunnel-push-pop.at index 40c2058d97b4..2d43df063593 100644 --- a/tests/tunnel-push-pop.at +++ b/tests/tunnel-push-pop.at @@ -93,28 +93,28 @@ AT_CHECK([tail -1 stdout], [0], dnl Check VXLAN tunnel push AT_CHECK([ovs-ofctl add-flow int-br action=2]) -AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout]) +AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: tnl_push(tnl_port(4789),header(size=50,type=4,eth(dst=f8:bc:12:44:34:b6,src=aa:55:aa:55:00:00,dl_type=0x0800),ipv4(src=1.1.2.88,dst=1.1.2.92,proto=17,tos=0,ttl=64,frag=0x4000),udp(src=0,dst=4789,csum=0x0),vxlan(flags=0x8000000,vni=0x7b)),out_port(100)) ]) dnl Check VXLAN tunnel push set tunnel id by flow and checksum AT_CHECK([ovs-ofctl add-flow int-br "actions=set_tunnel:124,4"]) -AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout]) +AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: tnl_push(tnl_port(4789),header(size=50,type=4,eth(dst=f8:bc:12:44:34:b7,src=aa:55:aa:55:00:00,dl_type=0x0800),ipv4(src=1.1.2.88,dst=1.1.2.93,proto=17,tos=0,ttl=64,frag=0x4000),udp(src=0,dst=4789,csum=0xffff),vxlan(flags=0x8000000,vni=0x7c)),out_port(100)) ]) dnl Check GRE tunnel push AT_CHECK([ovs-ofctl add-flow int-br action=3]) -AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout]) +AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: tnl_push(tnl_port(3),header(size=42,type=3,eth(dst=f8:bc:12:44:34:b6,src=aa:55:aa:55:00:00,dl_type=0x0800),ipv4(src=1.1.2.88,dst=1.1.2.92,proto=47,tos=0,ttl=64,frag=0x4000),gre((flags=0x2000,proto=0x6558),key=0x1c8)),out_port(100)) ]) dnl Check Geneve tunnel push AT_CHECK([ovs-ofctl add-flow int-br "actions=set_field:1.1.2.92->tun_dst,5"]) -AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout]) +AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: tnl_push(tnl_port(6081),header(size=50,type=5,eth(dst=f8:bc:12:44:34:b6,src=aa:55:aa:55:00:00,dl_type=0x0800),ipv4(src=1.1.2.88,dst=1.1.2.92,proto=17,tos=0,ttl=64,frag=0x4000),udp(src=0,dst=6081,csum=0x0),geneve(vni=0x7b)),out_port(100)) ]) @@ -122,7 +122,7 @@ AT_CHECK([tail -1 stdout], [0], dnl Check Geneve tunnel push with options AT_CHECK([ovs-ofctl add-tlv-map int-br "{class=0xffff,type=0x80,len=4}->tun_metadata0"]) AT_CHECK([ovs-ofctl add-flow int-br "actions=set_field:1.1.2.92->tun_dst,set_field:0xa->tun_metadata0,5"]) -AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout]) +AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: tnl_push(tnl_port(6081),header(size=58,type=5,eth(dst=f8:bc:12:44:34:b6,src=aa:55:aa:55:00:00,dl_type=0x0800),ipv4(src=1.1.2.88,dst=1.1.2.92,proto=17,tos=0,ttl=64,frag=0x4000),udp(src=0,dst=6081,csum=0x0),geneve(crit,vni=0x7b,options({class=0xffff,type=0x80,len=4,0xa}))),out_port(100)) ]) diff --git a/tests/tunnel.at b/tests/tunnel.at index 7f82785dc7c1..3c9984fcf9c4 100644 --- a/tests/tunnel.at +++ b/tests/tunnel.at @@ -434,14 +434,14 @@ AT_CHECK([tail -1 stdout], [0], ]) dnl Option match -AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'recirc_id(0),tunnel(tun_id=0x0,src=1.1.1.1,dst=1.1.1.2,ttl=64,geneve({class=0xffff,type=0,len=4,0xb}),flags(df|key)),in_port(6081),skb_mark(0),eth_type(0x0800),ipv4(frag=no)'], [0], [stdout]) +AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'recirc_id(0),tunnel(tun_id=0x0,src=1.1.1.1,dst=1.1.1.2,ttl=64,geneve({class=0xffff,type=0,len=4,0xb}),flags(df|key)),in_port(6081),skb_mark(0),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(frag=no)'], [0], [stdout]) AT_CHECK([tail -2 stdout], [0], [Megaflow: pkt_mark=0,recirc_id=0,ip,tun_id=0,tun_src=1.1.1.1,tun_dst=1.1.1.2,tun_tos=0,tun_flags=+df-csum+key,tun_metadata0=0xb/0xf,in_port=1,nw_frag=no Datapath actions: 2 ]) dnl Skip unknown option -AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'recirc_id(0),tunnel(tun_id=0x0,src=1.1.1.1,dst=1.1.1.2,ttl=64,geneve({class=0xffff,type=0,len=4,0xb}{class=0xffff,type=2,len=4,0xc}),flags(df|key)),in_port(6081),skb_mark(0),eth_type(0x0800),ipv4(frag=no)'], [0], [stdout]) +AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'recirc_id(0),tunnel(tun_id=0x0,src=1.1.1.1,dst=1.1.1.2,ttl=64,geneve({class=0xffff,type=0,len=4,0xb}{class=0xffff,type=2,len=4,0xc}),flags(df|key)),in_port(6081),skb_mark(0),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(frag=no)'], [0], [stdout]) AT_CHECK([tail -2 stdout], [0], [Megaflow: pkt_mark=0,recirc_id=0,ip,tun_id=0,tun_src=1.1.1.1,tun_dst=1.1.1.2,tun_tos=0,tun_flags=+df-csum+key,tun_metadata0=0xb/0xf,in_port=1,nw_frag=no Datapath actions: 2 @@ -475,7 +475,7 @@ AT_CHECK([ovs-ofctl del-tlv-map br0 "{class=0xffff,type=3,len=4}->tun_metadata3" AT_CHECK([ovs-ofctl add-tlv-map br0 "{class=0xffff,type=3,len=8}->tun_metadata3"]) AT_CHECK([ovs-ofctl add-flow br0 tun_metadata3=0x1234567890abcdef,actions=2]) -AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'recirc_id(0),tunnel(tun_id=0x0,src=1.1.1.1,dst=1.1.1.2,ttl=64,geneve({class=0xffff,type=3,len=8,0x1234567890abcdef}),flags(df|key)),in_port(6081),skb_mark(0),eth_type(0x0800),ipv4(frag=no)'], [0], [stdout]) +AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'recirc_id(0),tunnel(tun_id=0x0,src=1.1.1.1,dst=1.1.1.2,ttl=64,geneve({class=0xffff,type=3,len=8,0x1234567890abcdef}),flags(df|key)),in_port(6081),skb_mark(0),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(frag=no)'], [0], [stdout]) AT_CHECK([tail -2 stdout], [0], [Megaflow: pkt_mark=0,recirc_id=0,ip,tun_id=0,tun_src=1.1.1.1,tun_dst=1.1.1.2,tun_tos=0,tun_flags=+df-csum+key,tun_metadata3=0x1234567890abcdef,in_port=1,nw_frag=no Datapath actions: 2 @@ -510,13 +510,13 @@ AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], NXST_FLOW reply: ]) -AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'recirc_id(0),tunnel(tun_id=0x0,src=1.1.1.1,dst=1.1.1.2,ttl=64,geneve({class=0xffff,type=0,len=4,0x12345678}),flags(df|key)),in_port(6081),skb_mark(0),eth_type(0x0800),ipv4(frag=no)'], [0], [stdout]) +AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'recirc_id(0),tunnel(tun_id=0x0,src=1.1.1.1,dst=1.1.1.2,ttl=64,geneve({class=0xffff,type=0,len=4,0x12345678}),flags(df|key)),in_port(6081),skb_mark(0),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(frag=no)'], [0], [stdout]) AT_CHECK([tail -2 stdout], [0], [Megaflow: pkt_mark=0,recirc_id=0,ip,tun_id=0,tun_src=1.1.1.1,tun_dst=1.1.1.2,tun_tos=0,tun_flags=+df-csum+key,tun_metadata0,tun_metadata1=NP,tun_metadata2=NP,in_port=1,nw_frag=no Datapath actions: 2 ]) -AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'recirc_id(0),tunnel(tun_id=0x0,src=1.1.1.1,dst=1.1.1.2,ttl=64,geneve({class=0xffff,type=1,len=0}),flags(df|key)),in_port(6081),skb_mark(0),eth_type(0x0800),ipv4(frag=no)'], [0], [stdout]) +AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'recirc_id(0),tunnel(tun_id=0x0,src=1.1.1.1,dst=1.1.1.2,ttl=64,geneve({class=0xffff,type=1,len=0}),flags(df|key)),in_port(6081),skb_mark(0),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(frag=no)'], [0], [stdout]) AT_CHECK([tail -2 stdout], [0], [Megaflow: pkt_mark=0,recirc_id=0,ip,tun_id=0,tun_src=1.1.1.1,tun_dst=1.1.1.2,tun_tos=0,tun_flags=+df-csum+key,tun_metadata1,tun_metadata2=NP,in_port=1,nw_ecn=0,nw_frag=no Datapath actions: set(tunnel(tun_id=0x0,dst=1.1.1.1,ttl=64,geneve({class=0xffff,type=0x1,len=0}),flags(df|key))),6081