From patchwork Tue Mar 7 00:22:44 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jarno Rajahalme X-Patchwork-Id: 736017 X-Patchwork-Delegate: joestringer@nicira.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from mail.linuxfoundation.org (mail.linuxfoundation.org [140.211.169.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3vcd0n1YNlz9sNd for ; Tue, 7 Mar 2017 11:32:57 +1100 (AEDT) Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id A824EC66; Tue, 7 Mar 2017 00:23:37 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@mail.linuxfoundation.org Received: from smtp2.linuxfoundation.org (smtp2.linux-foundation.org [172.17.192.36]) by mail.linuxfoundation.org (Postfix) with ESMTPS id D28A6C5E for ; Tue, 7 Mar 2017 00:23:34 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.7.6 Received: from relay2-d.mail.gandi.net (relay2-d.mail.gandi.net [217.70.183.194]) by smtp2.linuxfoundation.org (Postfix) with ESMTPS id D95881DABF for ; Tue, 7 Mar 2017 00:23:26 +0000 (UTC) Received: from mfilter16-d.gandi.net (mfilter16-d.gandi.net [217.70.178.144]) by relay2-d.mail.gandi.net (Postfix) with ESMTP id 850D0C5A65; Tue, 7 Mar 2017 01:23:25 +0100 (CET) X-Virus-Scanned: Debian amavisd-new at mfilter16-d.gandi.net Received: from relay2-d.mail.gandi.net ([IPv6:::ffff:217.70.183.194]) by mfilter16-d.gandi.net (mfilter16-d.gandi.net [::ffff:10.0.15.180]) (amavisd-new, port 10024) with ESMTP id NCshcA4_xZj2; Tue, 7 Mar 2017 01:23:20 +0100 (CET) X-Originating-IP: 208.91.1.34 Received: from sc9-mailhost3.vmware.com (unknown [208.91.1.34]) (Authenticated sender: jarno@ovn.org) by relay2-d.mail.gandi.net (Postfix) with ESMTPSA id 0C10BC5A53; Tue, 7 Mar 2017 01:23:19 +0100 (CET) From: Jarno Rajahalme To: dev@openvswitch.org Date: Mon, 6 Mar 2017 16:22:44 -0800 Message-Id: <1488846170-23012-17-git-send-email-jarno@ovn.org> X-Mailer: git-send-email 2.1.4 In-Reply-To: <1488846170-23012-1-git-send-email-jarno@ovn.org> References: <1488846170-23012-1-git-send-email-jarno@ovn.org> X-Spam-Status: No, score=-2.6 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_LOW autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on smtp2.linux-foundation.org Subject: [ovs-dev] [PATCH v3 16/22] odp: Support conntrack orig tuple key. X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: ovs-dev-bounces@openvswitch.org Errors-To: ovs-dev-bounces@openvswitch.org Userspace support for datapath original direction conntrack tuple. Signed-off-by: Jarno Rajahalme Acked-by: Joe Stringer --- build-aux/extract-ofp-fields | 3 + include/openvswitch/flow.h | 15 ++- include/openvswitch/match.h | 16 +++ include/openvswitch/meta-flow.h | 136 ++++++++++++++++++++++++ lib/conntrack.c | 43 ++++++-- lib/flow.c | 228 ++++++++++++++++++++++++++++------------ lib/flow.h | 50 +++++++++ lib/match.c | 110 ++++++++++++++++++- lib/meta-flow.c | 157 ++++++++++++++++++++++++++- lib/meta-flow.xml | 92 ++++++++++++++++ lib/nx-match.c | 18 +++- lib/odp-util.c | 86 +++++++++++++++ lib/odp-util.h | 8 +- lib/ofp-util.c | 2 +- lib/packets.h | 5 + ofproto/ofproto-dpif-rid.h | 2 +- ofproto/ofproto-dpif-xlate.c | 13 ++- ofproto/ofproto-dpif.c | 2 + tests/odp.at | 2 +- tests/ofproto-dpif.at | 30 +++--- tests/ofproto.at | 7 ++ tests/system-traffic.at | 142 +++++++++++++++++++++++-- 22 files changed, 1058 insertions(+), 109 deletions(-) diff --git a/build-aux/extract-ofp-fields b/build-aux/extract-ofp-fields index 498b887..a26d558 100755 --- a/build-aux/extract-ofp-fields +++ b/build-aux/extract-ofp-fields @@ -44,6 +44,9 @@ PREREQS = {"none": "MFP_NONE", "IPv4": "MFP_IPV4", "IPv6": "MFP_IPV6", "IPv4/IPv6": "MFP_IP_ANY", + "CT": "MFP_CT_VALID", + "CTv4": "MFP_CTV4_VALID", + "CTv6": "MFP_CTV6_VALID", "MPLS": "MFP_MPLS", "TCP": "MFP_TCP", "UDP": "MFP_UDP", diff --git a/include/openvswitch/flow.h b/include/openvswitch/flow.h index 9169272..5cd78e4 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 36 +#define FLOW_WC_SEQ 37 /* Number of Open vSwitch extension 32-bit registers. */ #define FLOW_N_REGS 16 @@ -92,7 +92,7 @@ struct flow { union flow_in_port in_port; /* Input port.*/ uint32_t recirc_id; /* Must be exact match. */ uint8_t ct_state; /* Connection tracking state. */ - uint8_t pad0; + uint8_t ct_nw_proto; /* CT orig tuple IP protocol. */ uint16_t ct_zone; /* Connection tracking zone. */ uint32_t ct_mark; /* Connection mark.*/ uint8_t pad1[4]; /* Pad to 64 bits. */ @@ -110,8 +110,12 @@ struct flow { /* L3 (64-bit aligned) */ ovs_be32 nw_src; /* IPv4 source address or ARP SPA. */ ovs_be32 nw_dst; /* IPv4 destination address or ARP TPA. */ + ovs_be32 ct_nw_src; /* CT orig tuple IPv4 source address. */ + ovs_be32 ct_nw_dst; /* CT orig tuple IPv4 destination address. */ struct in6_addr ipv6_src; /* IPv6 source address. */ struct in6_addr ipv6_dst; /* IPv6 destination address. */ + struct in6_addr ct_ipv6_src; /* CT orig tuple IPv6 source address. */ + struct in6_addr ct_ipv6_dst; /* CT orig tuple IPv6 destination address. */ ovs_be32 ipv6_label; /* IPv6 flow label. */ uint8_t nw_frag; /* FLOW_FRAG_* flags. */ uint8_t nw_tos; /* IP ToS (including DSCP and ECN). */ @@ -126,8 +130,11 @@ struct flow { /* L4 (64-bit aligned) */ ovs_be16 tp_src; /* TCP/UDP/SCTP source port/ICMP type. */ ovs_be16 tp_dst; /* TCP/UDP/SCTP destination port/ICMP code. */ + ovs_be16 ct_tp_src; /* CT original tuple source port/ICMP type. */ + ovs_be16 ct_tp_dst; /* CT original tuple dst port/ICMP code. */ ovs_be32 igmp_group_ip4; /* IGMP group IPv4 address. * Keep last for BUILD_ASSERT_DECL below. */ + ovs_be32 pad4; /* Pad to 64 bits. */ }; BUILD_ASSERT_DECL(sizeof(struct flow) % sizeof(uint64_t) == 0); BUILD_ASSERT_DECL(sizeof(struct flow_tnl) % sizeof(uint64_t) == 0); @@ -136,8 +143,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) + 248 - && FLOW_WC_SEQ == 36); + == sizeof(struct flow_tnl) + 292 + && FLOW_WC_SEQ == 37); /* Incremental points at which flow classification may be performed in * segments. diff --git a/include/openvswitch/match.h b/include/openvswitch/match.h index 0b5f050..06fa04c 100644 --- a/include/openvswitch/match.h +++ b/include/openvswitch/match.h @@ -99,6 +99,22 @@ 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_ct_nw_src(struct match *, ovs_be32); +void match_set_ct_nw_src_masked(struct match *, ovs_be32, ovs_be32 mask); +void match_set_ct_nw_dst(struct match *, ovs_be32); +void match_set_ct_nw_dst_masked(struct match *, ovs_be32, ovs_be32 mask); +void match_set_ct_nw_proto(struct match *, uint8_t); +void match_set_ct_tp_src(struct match *, ovs_be16); +void match_set_ct_tp_src_masked(struct match *, ovs_be16, ovs_be16 mask); +void match_set_ct_tp_dst(struct match *, ovs_be16); +void match_set_ct_tp_dst_masked(struct match *, ovs_be16, ovs_be16 mask); +void match_set_ct_ipv6_src(struct match *, const struct in6_addr *); +void match_set_ct_ipv6_src_masked(struct match *, const struct in6_addr *, + const struct in6_addr *); +void match_set_ct_ipv6_dst(struct match *, const struct in6_addr *); +void match_set_ct_ipv6_dst_masked(struct match *, const struct in6_addr *, + const struct in6_addr *); + 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 aac9945..26e1021 100644 --- a/include/openvswitch/meta-flow.h +++ b/include/openvswitch/meta-flow.h @@ -740,6 +740,139 @@ enum OVS_PACKED_ENUM mf_field_id { */ MFF_CT_LABEL, + /* "ct_nw_proto". + * + * The "protocol" byte in the IPv4 or IPv6 header for the original + * direction conntrack tuple, or of the master conntrack entry, if the + * current connection is a related connection. + * + * The value is initially zero and populated by the CT action. The value + * remains zero after the CT action only if the packet can not be + * associated with a valid connection, in which case the prerequisites + * for matching this field ("CT") are not met. + * + * Type: u8. + * Maskable: no. + * Formatting: decimal. + * Prerequisites: CT. + * Access: read-only. + * NXM: NXM_NX_CT_NW_PROTO(119) since v2.7. + * OXM: none. + */ + MFF_CT_NW_PROTO, + + /* "ct_nw_src". + * + * IPv4 source address of the original direction tuple of the conntrack + * entry, or of the master conntrack entry, if the current connection is a + * related connection. + * + * The value is populated by the CT action. + * + * Type: be32. + * Maskable: bitwise. + * Formatting: IPv4. + * Prerequisites: CTv4. + * Access: read-only. + * NXM: NXM_NX_CT_NW_SRC(120) since v2.7. + * OXM: none. + * Prefix lookup member: ct_nw_src. + */ + MFF_CT_NW_SRC, + + /* "ct_nw_dst". + * + * IPv4 destination address of the original direction tuple of the + * conntrack entry, or of the master conntrack entry, if the current + * connection is a related connection. + * + * The value is populated by the CT action. + * + * Type: be32. + * Maskable: bitwise. + * Formatting: IPv4. + * Prerequisites: CTv4. + * Access: read-only. + * NXM: NXM_NX_CT_NW_DST(121) since v2.7. + * OXM: none. + * Prefix lookup member: ct_nw_dst. + */ + MFF_CT_NW_DST, + + /* "ct_ipv6_src". + * + * IPv6 source address of the original direction tuple of the conntrack + * entry, or of the master conntrack entry, if the current connection is a + * related connection. + * + * The value is populated by the CT action. + * + * Type: be128. + * Maskable: bitwise. + * Formatting: IPv6. + * Prerequisites: CTv6. + * Access: read-only. + * NXM: NXM_NX_CT_IPV6_SRC(122) since v2.7. + * OXM: none. + * Prefix lookup member: ct_ipv6_src. + */ + MFF_CT_IPV6_SRC, + + /* "ct_ipv6_dst". + * + * IPv6 destination address of the original direction tuple of the + * conntrack entry, or of the master conntrack entry, if the current + * connection is a related connection. + * + * The value is populated by the CT action. + * + * Type: be128. + * Maskable: bitwise. + * Formatting: IPv6. + * Prerequisites: CTv6. + * Access: read-only. + * NXM: NXM_NX_CT_IPV6_DST(123) since v2.7. + * OXM: none. + * Prefix lookup member: ct_ipv6_dst. + */ + MFF_CT_IPV6_DST, + + /* "ct_tp_src". + * + * Transport layer source port of the original direction tuple of the + * conntrack entry, or of the master conntrack entry, if the current + * connection is a related connection. + * + * The value is populated by the CT action. + * + * Type: be16. + * Maskable: bitwise. + * Formatting: decimal. + * Prerequisites: CT. + * Access: read-only. + * NXM: NXM_NX_CT_TP_SRC(124) since v2.7. + * OXM: none. + */ + MFF_CT_TP_SRC, + + /* "ct_tp_dst". + * + * Transport layer destination port of the original direction tuple of the + * conntrack entry, or of the master conntrack entry, if the current + * connection is a related connection. + * + * The value is populated by the CT action. + * + * Type: be16. + * Maskable: bitwise. + * Formatting: decimal. + * Prerequisites: CT. + * Access: read-only. + * NXM: NXM_NX_CT_TP_DST(125) since v2.7. + * OXM: none. + */ + MFF_CT_TP_DST, + #if FLOW_N_REGS == 16 /* "reg". * @@ -1689,6 +1822,9 @@ enum OVS_PACKED_ENUM mf_prereqs { MFP_SCTP, /* On IPv4 or IPv6. */ MFP_ICMPV4, MFP_ICMPV6, + MFP_CT_VALID, /* Implies IPv4 or IPv6. */ + MFP_CTV4_VALID, /* MFP_CT_VALID and IPv4. */ + MFP_CTV6_VALID, /* MFP_CT_VALID and IPv6. */ /* L2+L3+L4 requirements. */ MFP_ND, diff --git a/lib/conntrack.c b/lib/conntrack.c index 677c0d2..8ae501b 100644 --- a/lib/conntrack.c +++ b/lib/conntrack.c @@ -159,12 +159,44 @@ static unsigned hash_to_bucket(uint32_t hash) static void write_ct_md(struct dp_packet *pkt, uint16_t state, uint16_t zone, - uint32_t mark, ovs_u128 label) + const struct conn *conn, const struct conn_key *key) { pkt->md.ct_state = state | CS_TRACKED; pkt->md.ct_zone = zone; - pkt->md.ct_mark = mark; - pkt->md.ct_label = label; + pkt->md.ct_mark = conn ? conn->mark : 0; + pkt->md.ct_label = conn ? conn->label : OVS_U128_ZERO; + + /* Use the original direction tuple if we have it. */ + if (conn) { + key = &conn->key; + } + pkt->md.ct_orig_tuple_ipv6 = false; + if (key) { + if (key->dl_type == htons(ETH_TYPE_IP)) { + pkt->md.ct_orig_tuple.ipv4 = (struct ovs_key_ct_tuple_ipv4) { + key->src.addr.ipv4_aligned, + key->dst.addr.ipv4_aligned, + key->nw_proto != IPPROTO_ICMP + ? key->src.port : htons(key->src.icmp_type), + key->nw_proto != IPPROTO_ICMP + ? key->dst.port : htons(key->src.icmp_code), + key->nw_proto, + }; + } else if (key->dl_type == htons(ETH_TYPE_IPV6)) { + pkt->md.ct_orig_tuple_ipv6 = true; + pkt->md.ct_orig_tuple.ipv6 = (struct ovs_key_ct_tuple_ipv6) { + key->src.addr.ipv6_aligned, + key->dst.addr.ipv6_aligned, + key->nw_proto != IPPROTO_ICMPV6 + ? key->src.port : htons(key->src.icmp_type), + key->nw_proto != IPPROTO_ICMPV6 + ? key->dst.port : htons(key->src.icmp_code), + key->nw_proto, + }; + } + } else { + memset(&pkt->md.ct_orig_tuple, 0, sizeof pkt->md.ct_orig_tuple); + } } static struct conn * @@ -254,8 +286,7 @@ process_one(struct conntrack *ct, struct dp_packet *pkt, } } - write_ct_md(pkt, state, zone, conn ? conn->mark : 0, - conn ? conn->label : OVS_U128_ZERO); + write_ct_md(pkt, state, zone, conn, &ctx->key); return conn; } @@ -306,7 +337,7 @@ conntrack_execute(struct conntrack *ct, struct dp_packet_batch *pkt_batch, unsigned bucket; if (!conn_key_extract(ct, pkts[i], dl_type, &ctxs[i], zone)) { - write_ct_md(pkts[i], CS_INVALID, zone, 0, OVS_U128_ZERO); + write_ct_md(pkts[i], CS_INVALID, zone, NULL, NULL); continue; } diff --git a/lib/flow.c b/lib/flow.c index 0c95b75..b476fce 100644 --- a/lib/flow.c +++ b/lib/flow.c @@ -125,7 +125,7 @@ struct mf_ctx { * away. Some GCC versions gave warnings on ALWAYS_INLINE, so these are * defined as macros. */ -#if (FLOW_WC_SEQ != 36) +#if (FLOW_WC_SEQ != 37) #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 " @@ -312,6 +312,11 @@ BUILD_MESSAGE("FLOW_WC_SEQ changed: miniflow_extract() will have runtime " #define miniflow_push_macs(MF, FIELD, VALUEP) \ miniflow_push_macs_(MF, offsetof(struct flow, FIELD), VALUEP) +/* Return the pointer to the miniflow data when called BEFORE the corresponding + * push. */ +#define miniflow_pointer(MF, FIELD) \ + (void *)((uint8_t *)MF.data + ((offsetof(struct flow, FIELD)) % 8)) + /* Pulls the MPLS headers at '*datap' and returns the count of them. */ static inline int parse_mpls(const void **datap, size_t *sizep) @@ -383,61 +388,67 @@ parse_ethertype(const void **datap, size_t *sizep) return htons(FLOW_DL_TYPE_NONE); } -static inline void +/* Returns 'true' if the packet is an ND packet. In that case the '*nd_target' + * and 'arp_buf[]' are filled in. If the packet is not an ND pacet, 'false' is + * returned and no values are filled in on '*nd_target' or 'arp_buf[]'. */ +static inline bool parse_icmpv6(const void **datap, size_t *sizep, const struct icmp6_hdr *icmp, const struct in6_addr **nd_target, struct eth_addr arp_buf[2]) { - if (icmp->icmp6_code == 0 && - (icmp->icmp6_type == ND_NEIGHBOR_SOLICIT || - icmp->icmp6_type == ND_NEIGHBOR_ADVERT)) { + if (icmp->icmp6_code != 0 || + (icmp->icmp6_type != ND_NEIGHBOR_SOLICIT && + icmp->icmp6_type != ND_NEIGHBOR_ADVERT)) { + return false; + } - *nd_target = data_try_pull(datap, sizep, sizeof **nd_target); - if (OVS_UNLIKELY(!*nd_target)) { - return; - } + arp_buf[0] = eth_addr_zero; + arp_buf[1] = eth_addr_zero; + *nd_target = data_try_pull(datap, sizep, sizeof **nd_target); + if (OVS_UNLIKELY(!*nd_target)) { + return true; + } - while (*sizep >= 8) { - /* The minimum size of an option is 8 bytes, which also is - * the size of Ethernet link-layer options. */ - const struct ovs_nd_opt *nd_opt = *datap; - int opt_len = nd_opt->nd_opt_len * ND_OPT_LEN; + while (*sizep >= 8) { + /* The minimum size of an option is 8 bytes, which also is + * the size of Ethernet link-layer options. */ + const struct ovs_nd_opt *nd_opt = *datap; + int opt_len = nd_opt->nd_opt_len * ND_OPT_LEN; - if (!opt_len || opt_len > *sizep) { - return; - } + if (!opt_len || opt_len > *sizep) { + return true; + } - /* Store the link layer address if the appropriate option is - * provided. It is considered an error if the same link - * layer option is specified twice. */ - if (nd_opt->nd_opt_type == ND_OPT_SOURCE_LINKADDR - && opt_len == 8) { - if (OVS_LIKELY(eth_addr_is_zero(arp_buf[0]))) { - arp_buf[0] = nd_opt->nd_opt_mac; - } else { - goto invalid; - } - } else if (nd_opt->nd_opt_type == ND_OPT_TARGET_LINKADDR - && opt_len == 8) { - if (OVS_LIKELY(eth_addr_is_zero(arp_buf[1]))) { - arp_buf[1] = nd_opt->nd_opt_mac; - } else { - goto invalid; - } + /* Store the link layer address if the appropriate option is + * provided. It is considered an error if the same link + * layer option is specified twice. */ + if (nd_opt->nd_opt_type == ND_OPT_SOURCE_LINKADDR + && opt_len == 8) { + if (OVS_LIKELY(eth_addr_is_zero(arp_buf[0]))) { + arp_buf[0] = nd_opt->nd_opt_mac; + } else { + goto invalid; } - - if (OVS_UNLIKELY(!data_try_pull(datap, sizep, opt_len))) { - return; + } else if (nd_opt->nd_opt_type == ND_OPT_TARGET_LINKADDR + && opt_len == 8) { + if (OVS_LIKELY(eth_addr_is_zero(arp_buf[1]))) { + arp_buf[1] = nd_opt->nd_opt_mac; + } else { + goto invalid; } } - } - return; + if (OVS_UNLIKELY(!data_try_pull(datap, sizep, opt_len))) { + return true; + } + } + return true; invalid: *nd_target = NULL; arp_buf[0] = eth_addr_zero; arp_buf[1] = eth_addr_zero; + return true; } static inline bool @@ -561,6 +572,8 @@ miniflow_extract(struct dp_packet *packet, struct miniflow *dst) const char *l2; ovs_be16 dl_type; uint8_t nw_frag, nw_tos, nw_ttl, nw_proto; + uint8_t *ct_nw_proto_p = NULL; + ovs_be16 ct_tp_src = 0, ct_tp_dst = 0; /* Metadata. */ if (flow_tnl_dst_is_set(&md->tunnel)) { @@ -594,7 +607,8 @@ miniflow_extract(struct dp_packet *packet, struct miniflow *dst) if (md->recirc_id || md->ct_state) { miniflow_push_uint32(mf, recirc_id, md->recirc_id); miniflow_push_uint8(mf, ct_state, md->ct_state); - miniflow_push_uint8(mf, pad0, 0); + ct_nw_proto_p = miniflow_pointer(mf, ct_nw_proto); + miniflow_push_uint8(mf, ct_nw_proto, 0); miniflow_push_uint16(mf, ct_zone, md->ct_zone); } @@ -670,6 +684,15 @@ miniflow_extract(struct dp_packet *packet, struct miniflow *dst) /* Push both source and destination address at once. */ miniflow_push_words(mf, nw_src, &nh->ip_src, 1); + if (ct_nw_proto_p && !md->ct_orig_tuple_ipv6) { + *ct_nw_proto_p = md->ct_orig_tuple.ipv4.ipv4_proto; + if (*ct_nw_proto_p) { + miniflow_push_words(mf, ct_nw_src, + &md->ct_orig_tuple.ipv4.ipv4_src, 1); + ct_tp_src = md->ct_orig_tuple.ipv4.src_port; + ct_tp_dst = md->ct_orig_tuple.ipv4.dst_port; + } + } miniflow_push_be32(mf, ipv6_label, 0); /* Padding for IPv4. */ @@ -708,6 +731,17 @@ miniflow_extract(struct dp_packet *packet, struct miniflow *dst) sizeof nh->ip6_src / 8); miniflow_push_words(mf, ipv6_dst, &nh->ip6_dst, sizeof nh->ip6_dst / 8); + if (ct_nw_proto_p && md->ct_orig_tuple_ipv6) { + *ct_nw_proto_p = md->ct_orig_tuple.ipv6.ipv6_proto; + if (*ct_nw_proto_p) { + miniflow_push_words(mf, ct_ipv6_src, + &md->ct_orig_tuple.ipv6.ipv6_src, + 2 * + sizeof md->ct_orig_tuple.ipv6.ipv6_src / 8); + ct_tp_src = md->ct_orig_tuple.ipv6.src_port; + ct_tp_dst = md->ct_orig_tuple.ipv6.dst_port; + } + } tc_flow = get_16aligned_be32(&nh->ip6_flow); { @@ -770,7 +804,8 @@ miniflow_extract(struct dp_packet *packet, struct miniflow *dst) TCP_FLAGS_BE32(tcp->tcp_ctl)); miniflow_push_be16(mf, tp_src, tcp->tcp_src); miniflow_push_be16(mf, tp_dst, tcp->tcp_dst); - miniflow_pad_to_64(mf, tp_dst); + miniflow_push_be16(mf, ct_tp_src, ct_tp_src); + miniflow_push_be16(mf, ct_tp_dst, ct_tp_dst); } } else if (OVS_LIKELY(nw_proto == IPPROTO_UDP)) { if (OVS_LIKELY(size >= UDP_HEADER_LEN)) { @@ -778,7 +813,8 @@ miniflow_extract(struct dp_packet *packet, struct miniflow *dst) miniflow_push_be16(mf, tp_src, udp->udp_src); miniflow_push_be16(mf, tp_dst, udp->udp_dst); - miniflow_pad_to_64(mf, tp_dst); + miniflow_push_be16(mf, ct_tp_src, ct_tp_src); + miniflow_push_be16(mf, ct_tp_dst, ct_tp_dst); } } else if (OVS_LIKELY(nw_proto == IPPROTO_SCTP)) { if (OVS_LIKELY(size >= SCTP_HEADER_LEN)) { @@ -786,7 +822,8 @@ miniflow_extract(struct dp_packet *packet, struct miniflow *dst) miniflow_push_be16(mf, tp_src, sctp->sctp_src); miniflow_push_be16(mf, tp_dst, sctp->sctp_dst); - miniflow_pad_to_64(mf, tp_dst); + miniflow_push_be16(mf, ct_tp_src, ct_tp_src); + miniflow_push_be16(mf, ct_tp_dst, ct_tp_dst); } } else if (OVS_LIKELY(nw_proto == IPPROTO_ICMP)) { if (OVS_LIKELY(size >= ICMP_HEADER_LEN)) { @@ -794,7 +831,8 @@ miniflow_extract(struct dp_packet *packet, struct miniflow *dst) miniflow_push_be16(mf, tp_src, htons(icmp->icmp_type)); miniflow_push_be16(mf, tp_dst, htons(icmp->icmp_code)); - miniflow_pad_to_64(mf, tp_dst); + miniflow_push_be16(mf, ct_tp_src, ct_tp_src); + miniflow_push_be16(mf, ct_tp_dst, ct_tp_dst); } } else if (OVS_LIKELY(nw_proto == IPPROTO_IGMP)) { if (OVS_LIKELY(size >= IGMP_HEADER_LEN)) { @@ -802,25 +840,35 @@ miniflow_extract(struct dp_packet *packet, struct miniflow *dst) miniflow_push_be16(mf, tp_src, htons(igmp->igmp_type)); miniflow_push_be16(mf, tp_dst, htons(igmp->igmp_code)); + miniflow_push_be16(mf, ct_tp_src, ct_tp_src); + miniflow_push_be16(mf, ct_tp_dst, ct_tp_dst); miniflow_push_be32(mf, igmp_group_ip4, get_16aligned_be32(&igmp->group)); + miniflow_pad_to_64(mf, igmp_group_ip4); } } else if (OVS_LIKELY(nw_proto == IPPROTO_ICMPV6)) { if (OVS_LIKELY(size >= sizeof(struct icmp6_hdr))) { - const struct in6_addr *nd_target = NULL; - struct eth_addr arp_buf[2] = { { { { 0 } } } }; + const struct in6_addr *nd_target; + struct eth_addr arp_buf[2]; const struct icmp6_hdr *icmp = data_pull(&data, &size, sizeof *icmp); - parse_icmpv6(&data, &size, icmp, &nd_target, arp_buf); - if (nd_target) { - miniflow_push_words(mf, nd_target, nd_target, - sizeof *nd_target / sizeof(uint64_t)); + if (parse_icmpv6(&data, &size, icmp, &nd_target, arp_buf)) { + if (nd_target) { + miniflow_push_words(mf, nd_target, nd_target, + sizeof *nd_target / sizeof(uint64_t)); + } + miniflow_push_macs(mf, arp_sha, arp_buf); + miniflow_pad_to_64(mf, arp_tha); + miniflow_push_be16(mf, tp_src, htons(icmp->icmp6_type)); + miniflow_push_be16(mf, tp_dst, htons(icmp->icmp6_code)); + miniflow_pad_to_64(mf, tp_dst); + } else { + /* ICMPv6 but not ND. */ + miniflow_push_be16(mf, tp_src, htons(icmp->icmp6_type)); + miniflow_push_be16(mf, tp_dst, htons(icmp->icmp6_code)); + miniflow_push_be16(mf, ct_tp_src, ct_tp_src); + miniflow_push_be16(mf, ct_tp_dst, ct_tp_dst); } - miniflow_push_macs(mf, arp_sha, arp_buf); - miniflow_pad_to_64(mf, arp_tha); - miniflow_push_be16(mf, tp_src, htons(icmp->icmp6_type)); - miniflow_push_be16(mf, tp_dst, htons(icmp->icmp6_code)); - miniflow_pad_to_64(mf, tp_dst); } } } @@ -870,7 +918,7 @@ flow_get_metadata(const struct flow *flow, struct match *flow_metadata) { int i; - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 36); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 37); match_init_catchall(flow_metadata); if (flow->tunnel.tun_id != htonll(0)) { @@ -916,6 +964,21 @@ flow_get_metadata(const struct flow *flow, struct match *flow_metadata) match_set_in_port(flow_metadata, flow->in_port.ofp_port); if (flow->ct_state != 0) { match_set_ct_state(flow_metadata, flow->ct_state); + if (is_ct_valid(flow, NULL, NULL) && flow->ct_nw_proto != 0) { + if (flow->dl_type == htons(ETH_TYPE_IP)) { + match_set_ct_nw_src(flow_metadata, flow->ct_nw_src); + match_set_ct_nw_dst(flow_metadata, flow->ct_nw_dst); + match_set_ct_nw_proto(flow_metadata, flow->ct_nw_proto); + match_set_ct_tp_src(flow_metadata, flow->ct_tp_src); + match_set_ct_tp_dst(flow_metadata, flow->ct_tp_dst); + } else if (flow->dl_type == htons(ETH_TYPE_IPV6)) { + match_set_ct_ipv6_src(flow_metadata, &flow->ct_ipv6_src); + match_set_ct_ipv6_dst(flow_metadata, &flow->ct_ipv6_dst); + match_set_ct_nw_proto(flow_metadata, flow->ct_nw_proto); + match_set_ct_tp_src(flow_metadata, flow->ct_tp_src); + match_set_ct_tp_dst(flow_metadata, flow->ct_tp_dst); + } + } } if (flow->ct_zone != 0) { match_set_ct_zone(flow_metadata, flow->ct_zone); @@ -1237,6 +1300,18 @@ flow_format(struct ds *ds, const struct flow *flow) if (ovs_u128_is_zero(flow->ct_label)) { WC_UNMASK_FIELD(wc, ct_label); } + if (!is_ct_valid(flow, &match.wc, NULL) || !flow->ct_nw_proto) { + WC_UNMASK_FIELD(wc, ct_nw_proto); + WC_UNMASK_FIELD(wc, ct_tp_src); + WC_UNMASK_FIELD(wc, ct_tp_dst); + if (flow->dl_type == htons(ETH_TYPE_IP)) { + WC_UNMASK_FIELD(wc, ct_nw_src); + WC_UNMASK_FIELD(wc, ct_nw_dst); + } else if (flow->dl_type == htons(ETH_TYPE_IPV6)) { + WC_UNMASK_FIELD(wc, ct_ipv6_src); + WC_UNMASK_FIELD(wc, ct_ipv6_dst); + } + } for (int i = 0; i < FLOW_N_REGS; i++) { if (!flow->regs[i]) { WC_UNMASK_FIELD(wc, regs[i]); @@ -1276,7 +1351,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 == 36); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 37); if (flow_tnl_dst_is_set(&flow->tunnel)) { if (flow->tunnel.flags & FLOW_TNL_F_KEY) { @@ -1332,10 +1407,20 @@ void flow_wildcards_init_for_packet(struct flow_wildcards *wc, if (flow->dl_type == htons(ETH_TYPE_IP)) { WC_MASK_FIELD(wc, nw_src); WC_MASK_FIELD(wc, nw_dst); + WC_MASK_FIELD(wc, ct_nw_src); + WC_MASK_FIELD(wc, ct_nw_dst); } else if (flow->dl_type == htons(ETH_TYPE_IPV6)) { WC_MASK_FIELD(wc, ipv6_src); WC_MASK_FIELD(wc, ipv6_dst); WC_MASK_FIELD(wc, ipv6_label); + if (is_nd(flow, wc)) { + WC_MASK_FIELD(wc, arp_sha); + WC_MASK_FIELD(wc, arp_tha); + WC_MASK_FIELD(wc, nd_target); + } else { + WC_MASK_FIELD(wc, ct_ipv6_src); + WC_MASK_FIELD(wc, ct_ipv6_dst); + } } else if (flow->dl_type == htons(ETH_TYPE_ARP) || flow->dl_type == htons(ETH_TYPE_RARP)) { WC_MASK_FIELD(wc, nw_src); @@ -1361,6 +1446,9 @@ void flow_wildcards_init_for_packet(struct flow_wildcards *wc, WC_MASK_FIELD(wc, nw_tos); WC_MASK_FIELD(wc, nw_ttl); WC_MASK_FIELD(wc, nw_proto); + WC_MASK_FIELD(wc, ct_nw_proto); + WC_MASK_FIELD(wc, ct_tp_src); + WC_MASK_FIELD(wc, ct_tp_dst); /* No transport layer header in later fragments. */ if (!(flow->nw_frag & FLOW_NW_FRAG_LATER) && @@ -1375,10 +1463,6 @@ void flow_wildcards_init_for_packet(struct flow_wildcards *wc, if (flow->nw_proto == IPPROTO_TCP) { WC_MASK_FIELD(wc, tcp_flags); - } else if (flow->nw_proto == IPPROTO_ICMPV6) { - WC_MASK_FIELD(wc, arp_sha); - WC_MASK_FIELD(wc, arp_tha); - WC_MASK_FIELD(wc, nd_target); } else if (flow->nw_proto == IPPROTO_IGMP) { WC_MASK_FIELD(wc, igmp_group_ip4); } @@ -1394,7 +1478,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 == 36); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 37); flowmap_init(map); @@ -1436,6 +1520,11 @@ flow_wc_map(const struct flow *flow, struct flowmap *map) FLOWMAP_SET(map, nw_ttl); FLOWMAP_SET(map, tp_src); FLOWMAP_SET(map, tp_dst); + FLOWMAP_SET(map, ct_nw_proto); + FLOWMAP_SET(map, ct_nw_src); + FLOWMAP_SET(map, ct_nw_dst); + FLOWMAP_SET(map, ct_tp_src); + FLOWMAP_SET(map, ct_tp_dst); if (OVS_UNLIKELY(flow->nw_proto == IPPROTO_IGMP)) { FLOWMAP_SET(map, igmp_group_ip4); @@ -1453,11 +1542,16 @@ flow_wc_map(const struct flow *flow, struct flowmap *map) FLOWMAP_SET(map, tp_src); FLOWMAP_SET(map, tp_dst); - if (OVS_UNLIKELY(flow->nw_proto == IPPROTO_ICMPV6)) { + if (OVS_UNLIKELY(is_nd(flow, NULL))) { FLOWMAP_SET(map, nd_target); FLOWMAP_SET(map, arp_sha); FLOWMAP_SET(map, arp_tha); } else { + FLOWMAP_SET(map, ct_nw_proto); + FLOWMAP_SET(map, ct_ipv6_src); + FLOWMAP_SET(map, ct_ipv6_dst); + FLOWMAP_SET(map, ct_tp_src); + FLOWMAP_SET(map, ct_tp_dst); FLOWMAP_SET(map, tcp_flags); } } else if (eth_type_mpls(flow->dl_type)) { @@ -1478,7 +1572,7 @@ void flow_wildcards_clear_non_packet_fields(struct flow_wildcards *wc) { /* Update this function whenever struct flow changes. */ - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 36); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 37); memset(&wc->masks.metadata, 0, sizeof wc->masks.metadata); memset(&wc->masks.regs, 0, sizeof wc->masks.regs); @@ -1622,7 +1716,7 @@ flow_wildcards_set_xxreg_mask(struct flow_wildcards *wc, int idx, uint32_t miniflow_hash_5tuple(const struct miniflow *flow, uint32_t basis) { - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 36); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 37); uint32_t hash = basis; if (flow) { @@ -1669,7 +1763,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 == 36); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 37); uint32_t hash = basis; if (flow) { @@ -2137,7 +2231,7 @@ flow_push_mpls(struct flow *flow, int n, ovs_be16 mpls_eth_type, if (clear_flow_L3) { /* Clear all L3 and L4 fields and dp_hash. */ - BUILD_ASSERT(FLOW_WC_SEQ == 36); + BUILD_ASSERT(FLOW_WC_SEQ == 37); 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 62315bc..14a3004 100644 --- a/lib/flow.h +++ b/lib/flow.h @@ -862,9 +862,35 @@ flow_union_with_miniflow(struct flow *dst, const struct miniflow *src) flow_union_with_miniflow_subset(dst, src, src->map); } +static inline bool is_ct_valid(const struct flow *flow, + const struct flow_wildcards *mask, + struct flow_wildcards *wc) +{ + /* Matches are checked with 'mask' and without 'wc'. */ + if (mask && !wc) { + /* Must match at least one of the bits that implies a valid + * conntrack entry, or an explicit not-invalid. */ + return flow->ct_state & (CS_NEW | CS_ESTABLISHED | CS_RELATED + | CS_REPLY_DIR | CS_SRC_NAT | CS_DST_NAT) + || (flow->ct_state & CS_TRACKED + && mask->masks.ct_state & CS_INVALID + && !(flow->ct_state & CS_INVALID)); + } + /* Else we are checking a fully extracted flow, where valid CT state always + * has either 'new', 'established', or 'reply_dir' bit set. */ +#define CS_VALID_MASK (CS_NEW | CS_ESTABLISHED | CS_REPLY_DIR) + if (wc) { + wc->masks.ct_state |= CS_VALID_MASK; + } + return flow->ct_state & CS_VALID_MASK; +} + static inline void pkt_metadata_from_flow(struct pkt_metadata *md, const struct flow *flow) { + /* Update this function whenever struct flow changes. */ + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 37); + md->recirc_id = flow->recirc_id; md->dp_hash = flow->dp_hash; flow_tnl_copy__(&md->tunnel, &flow->tunnel); @@ -875,6 +901,30 @@ 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->ct_orig_tuple_ipv6 = false; + if (is_ct_valid(flow, NULL, NULL)) { + if (flow->dl_type == htons(ETH_TYPE_IP)) { + md->ct_orig_tuple.ipv4 = (struct ovs_key_ct_tuple_ipv4) { + flow->ct_nw_src, + flow->ct_nw_dst, + flow->ct_tp_src, + flow->ct_tp_dst, + flow->ct_nw_proto, + }; + } else if (flow->dl_type == htons(ETH_TYPE_IPV6)) { + md->ct_orig_tuple_ipv6 = true; + md->ct_orig_tuple.ipv6 = (struct ovs_key_ct_tuple_ipv6) { + flow->ct_ipv6_src, + flow->ct_ipv6_dst, + flow->ct_tp_src, + flow->ct_tp_dst, + flow->ct_nw_proto, + }; + } + } else { + memset(&md->ct_orig_tuple, 0, sizeof md->ct_orig_tuple); + } } /* Often, during translation we need to read a value from a flow('FLOW') and diff --git a/lib/match.c b/lib/match.c index 882bf0c..1a5b4ba 100644 --- a/lib/match.c +++ b/lib/match.c @@ -384,6 +384,99 @@ match_set_ct_label_masked(struct match *match, ovs_u128 value, ovs_u128 mask) } void +match_set_ct_nw_src(struct match *match, ovs_be32 ct_nw_src) +{ + match->flow.ct_nw_src = ct_nw_src; + match->wc.masks.ct_nw_src = OVS_BE32_MAX; +} + +void +match_set_ct_nw_src_masked(struct match *match, ovs_be32 ct_nw_src, + ovs_be32 mask) +{ + match->flow.ct_nw_src = ct_nw_src & mask; + match->wc.masks.ct_nw_src = mask; +} + +void +match_set_ct_nw_dst(struct match *match, ovs_be32 ct_nw_dst) +{ + match->flow.ct_nw_dst = ct_nw_dst; + match->wc.masks.ct_nw_dst = OVS_BE32_MAX; +} + +void +match_set_ct_nw_dst_masked(struct match *match, ovs_be32 ct_nw_dst, + ovs_be32 mask) +{ + match->flow.ct_nw_dst = ct_nw_dst & mask; + match->wc.masks.ct_nw_dst = mask; +} + +void +match_set_ct_nw_proto(struct match *match, uint8_t ct_nw_proto) +{ + match->flow.ct_nw_proto = ct_nw_proto; + match->wc.masks.ct_nw_proto = UINT8_MAX; +} + +void +match_set_ct_tp_src(struct match *match, ovs_be16 ct_tp_src) +{ + match_set_ct_tp_src_masked(match, ct_tp_src, OVS_BE16_MAX); +} + +void +match_set_ct_tp_src_masked(struct match *match, ovs_be16 port, ovs_be16 mask) +{ + match->flow.ct_tp_src = port & mask; + match->wc.masks.ct_tp_src = mask; +} + +void +match_set_ct_tp_dst(struct match *match, ovs_be16 ct_tp_dst) +{ + match_set_ct_tp_dst_masked(match, ct_tp_dst, OVS_BE16_MAX); +} + +void +match_set_ct_tp_dst_masked(struct match *match, ovs_be16 port, ovs_be16 mask) +{ + match->flow.ct_tp_dst = port & mask; + match->wc.masks.ct_tp_dst = mask; +} + +void +match_set_ct_ipv6_src(struct match *match, const struct in6_addr *src) +{ + match->flow.ct_ipv6_src = *src; + match->wc.masks.ct_ipv6_src = in6addr_exact; +} + +void +match_set_ct_ipv6_src_masked(struct match *match, const struct in6_addr *src, + const struct in6_addr *mask) +{ + match->flow.ct_ipv6_src = ipv6_addr_bitand(src, mask); + match->wc.masks.ct_ipv6_src = *mask; +} + +void +match_set_ct_ipv6_dst(struct match *match, const struct in6_addr *dst) +{ + match->flow.ct_ipv6_dst = *dst; + match->wc.masks.ct_ipv6_dst = in6addr_exact; +} + +void +match_set_ct_ipv6_dst_masked(struct match *match, const struct in6_addr *dst, + const struct in6_addr *mask) +{ + match->flow.ct_ipv6_dst = ipv6_addr_bitand(dst, mask); + match->wc.masks.ct_ipv6_dst = *mask; +} + +void match_set_dl_type(struct match *match, ovs_be16 dl_type) { match->wc.masks.dl_type = OVS_BE16_MAX; @@ -1075,7 +1168,7 @@ match_format(const struct match *match, struct ds *s, int priority) int i; - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 36); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 37); if (priority != OFP_DEFAULT_PRIORITY) { ds_put_format(s, "%spriority=%s%d,", @@ -1137,6 +1230,21 @@ match_format(const struct match *match, struct ds *s, int priority) format_ct_label_masked(s, &f->ct_label, &wc->masks.ct_label); } + format_ip_netmask(s, "ct_nw_src", f->ct_nw_src, + wc->masks.ct_nw_src); + format_ipv6_netmask(s, "ct_ipv6_src", &f->ct_ipv6_src, + &wc->masks.ct_ipv6_src); + format_ip_netmask(s, "ct_nw_dst", f->ct_nw_dst, + wc->masks.ct_nw_dst); + format_ipv6_netmask(s, "ct_ipv6_dst", &f->ct_ipv6_dst, + &wc->masks.ct_ipv6_dst); + if (wc->masks.ct_nw_proto) { + ds_put_format(s, "%sct_nw_proto=%s%"PRIu8",", + colors.param, colors.end, f->ct_nw_proto); + format_be16_masked(s, "ct_tp_src", f->ct_tp_src, wc->masks.ct_tp_src); + format_be16_masked(s, "ct_tp_dst", f->ct_tp_dst, wc->masks.ct_tp_dst); + } + if (wc->masks.dl_type) { skip_type = true; if (f->dl_type == htons(ETH_TYPE_IP)) { diff --git a/lib/meta-flow.c b/lib/meta-flow.c index 9d635a3..38d051e 100644 --- a/lib/meta-flow.c +++ b/lib/meta-flow.c @@ -246,6 +246,20 @@ mf_is_all_wild(const struct mf_field *mf, const struct flow_wildcards *wc) return !wc->masks.ct_mark; case MFF_CT_LABEL: return ovs_u128_is_zero(wc->masks.ct_label); + case MFF_CT_NW_PROTO: + return !wc->masks.ct_nw_proto; + case MFF_CT_NW_SRC: + return !wc->masks.ct_nw_src; + case MFF_CT_NW_DST: + return !wc->masks.ct_nw_dst; + case MFF_CT_TP_SRC: + return !wc->masks.ct_tp_src; + case MFF_CT_TP_DST: + return !wc->masks.ct_tp_dst; + case MFF_CT_IPV6_SRC: + return ipv6_mask_is_any(&wc->masks.ct_ipv6_src); + case MFF_CT_IPV6_DST: + return ipv6_mask_is_any(&wc->masks.ct_ipv6_dst); CASE_MFF_REGS: return !wc->masks.regs[mf->id - MFF_REG0]; CASE_MFF_XREGS: @@ -383,7 +397,7 @@ mf_is_mask_valid(const struct mf_field *mf, const union mf_value *mask) * Sets inspected bits in 'wc', if non-NULL. */ static bool mf_are_prereqs_ok__(const struct mf_field *mf, const struct flow *flow, - const struct flow_wildcards *mask OVS_UNUSED, + const struct flow_wildcards *mask, struct flow_wildcards *wc) { switch (mf->prereqs) { @@ -402,6 +416,14 @@ mf_are_prereqs_ok__(const struct mf_field *mf, const struct flow *flow, return eth_type_mpls(flow->dl_type); case MFP_IP_ANY: return is_ip_any(flow); + case MFP_CT_VALID: + return is_ct_valid(flow, mask, wc); + case MFP_CTV4_VALID: + return flow->dl_type == htons(ETH_TYPE_IP) + && is_ct_valid(flow, mask, wc); + case MFP_CTV6_VALID: + return flow->dl_type == htons(ETH_TYPE_IPV6) + && is_ct_valid(flow, mask, wc); case MFP_TCP: /* Matching !FRAG_LATER is not enforced (mask is not checked). */ return is_tcp(flow, wc) && !(flow->nw_frag & FLOW_NW_FRAG_LATER); @@ -475,6 +497,13 @@ mf_is_value_valid(const struct mf_field *mf, const union mf_value *value) case MFF_CT_ZONE: case MFF_CT_MARK: case MFF_CT_LABEL: + case MFF_CT_NW_PROTO: + case MFF_CT_NW_SRC: + case MFF_CT_NW_DST: + case MFF_CT_IPV6_SRC: + case MFF_CT_IPV6_DST: + case MFF_CT_TP_SRC: + case MFF_CT_TP_DST: CASE_MFF_REGS: CASE_MFF_XREGS: CASE_MFF_XXREGS: @@ -649,6 +678,34 @@ mf_get_value(const struct mf_field *mf, const struct flow *flow, value->be128 = hton128(flow->ct_label); break; + case MFF_CT_NW_PROTO: + value->u8 = flow->ct_nw_proto; + break; + + case MFF_CT_NW_SRC: + value->be32 = flow->ct_nw_src; + break; + + case MFF_CT_NW_DST: + value->be32 = flow->ct_nw_dst; + break; + + case MFF_CT_IPV6_SRC: + value->ipv6 = flow->ct_ipv6_src; + break; + + case MFF_CT_IPV6_DST: + value->ipv6 = flow->ct_ipv6_dst; + break; + + case MFF_CT_TP_SRC: + value->be16 = flow->ct_tp_src; + break; + + case MFF_CT_TP_DST: + value->be16 = flow->ct_tp_dst; + break; + CASE_MFF_REGS: value->be32 = htonl(flow->regs[mf->id - MFF_REG0]); break; @@ -911,6 +968,34 @@ mf_set_value(const struct mf_field *mf, match_set_ct_label(match, ntoh128(value->be128)); break; + case MFF_CT_NW_PROTO: + match_set_ct_nw_proto(match, value->u8); + break; + + case MFF_CT_NW_SRC: + match_set_ct_nw_src(match, value->be32); + break; + + case MFF_CT_NW_DST: + match_set_ct_nw_dst(match, value->be32); + break; + + case MFF_CT_IPV6_SRC: + match_set_ct_ipv6_src(match, &value->ipv6); + break; + + case MFF_CT_IPV6_DST: + match_set_ct_ipv6_dst(match, &value->ipv6); + break; + + case MFF_CT_TP_SRC: + match_set_ct_tp_src(match, value->be16); + break; + + case MFF_CT_TP_DST: + match_set_ct_tp_dst(match, value->be16); + break; + CASE_MFF_REGS: match_set_reg(match, mf->id - MFF_REG0, ntohl(value->be32)); break; @@ -1242,6 +1327,34 @@ mf_set_flow_value(const struct mf_field *mf, flow->ct_label = ntoh128(value->be128); break; + case MFF_CT_NW_PROTO: + flow->ct_nw_proto = value->u8; + break; + + case MFF_CT_NW_SRC: + flow->ct_nw_src = value->be32; + break; + + case MFF_CT_NW_DST: + flow->ct_nw_dst = value->be32; + break; + + case MFF_CT_IPV6_SRC: + flow->ct_ipv6_src = value->ipv6; + break; + + case MFF_CT_IPV6_DST: + flow->ct_ipv6_dst = value->ipv6; + break; + + case MFF_CT_TP_SRC: + flow->ct_tp_src = value->be16; + break; + + case MFF_CT_TP_DST: + flow->ct_tp_dst = value->be16; + break; + CASE_MFF_REGS: flow->regs[mf->id - MFF_REG0] = ntohl(value->be32); break; @@ -1571,6 +1684,41 @@ mf_set_wild(const struct mf_field *mf, struct match *match, char **err_str) memset(&match->wc.masks.ct_label, 0, sizeof(match->wc.masks.ct_label)); break; + case MFF_CT_NW_PROTO: + match->flow.ct_nw_proto = 0; + match->wc.masks.ct_nw_proto = 0; + break; + + case MFF_CT_NW_SRC: + match->flow.ct_nw_src = 0; + match->wc.masks.ct_nw_src = 0; + break; + + case MFF_CT_NW_DST: + match->flow.ct_nw_dst = 0; + match->wc.masks.ct_nw_dst = 0; + break; + + case MFF_CT_IPV6_SRC: + memset(&match->flow.ct_ipv6_src, 0, sizeof(match->flow.ct_ipv6_src)); + WC_UNMASK_FIELD(&match->wc, ct_ipv6_src); + break; + + case MFF_CT_IPV6_DST: + memset(&match->flow.ct_ipv6_dst, 0, sizeof(match->flow.ct_ipv6_dst)); + WC_UNMASK_FIELD(&match->wc, ct_ipv6_dst); + break; + + case MFF_CT_TP_SRC: + match->flow.ct_tp_src = 0; + match->wc.masks.ct_tp_src = 0; + break; + + case MFF_CT_TP_DST: + match->flow.ct_tp_dst = 0; + match->wc.masks.ct_tp_dst = 0; + break; + CASE_MFF_REGS: match_set_reg_masked(match, mf->id - MFF_REG0, 0, 0); break; @@ -1773,6 +1921,13 @@ mf_set(const struct mf_field *mf, switch (mf->id) { case MFF_CT_ZONE: + case MFF_CT_NW_PROTO: + case MFF_CT_NW_SRC: + case MFF_CT_NW_DST: + case MFF_CT_IPV6_SRC: + case MFF_CT_IPV6_DST: + case MFF_CT_TP_SRC: + case MFF_CT_TP_DST: case MFF_RECIRC_ID: case MFF_CONJ_ID: case MFF_IN_PORT: diff --git a/lib/meta-flow.xml b/lib/meta-flow.xml index 3db0f82..5efd431 100644 --- a/lib/meta-flow.xml +++ b/lib/meta-flow.xml @@ -2479,6 +2479,98 @@ actions=clone(load:0->NXM_OF_IN_PORT[],output:123) parameter to the ct action, to the connection to which the current packet belongs. + +

+ Open vSwitch 2.8 introduced the matching support for connection + tracker original direction 5-tuple fields. +

+ +

+ For non-committed non-related connections the conntrack original + direction tuple fields always have the same values as the + corresponding headers in the packet itself. For any other packets of + a committed connection the conntrack original direction tuple fields + reflect the values from that initial non-committed non-related packet, + and thus may be different from the actual packet headers, as the + actual packet headers may be in reverse direction (for reply packets), + transformed by NAT (when \fBnat\fR option was applied to the + connection), or be of different protocol (i.e., when an ICMP response + is sent to an UDP packet). In case of related connections, e.g., an + FTP data connection, the original direction tuple contains the + original direction headers from the master connection, e.g., an FTP + control connection. +

+ +

+ The following fields are populated by the ct action, and require a + match to a valid connection tracking state as a prerequisite, in + addition to the IP or IPv6 ethertype match. Examples of valid + connection tracking state matches include \fBct_state=+new\fR, + \fBct_state=+est\fR, \fBct_state=+rel\fR, and \fBct_state=+trk-inv\fR. +

+ + + Matches IPv4 conntrack original direction tuple source address. + See the paragraphs above for general description to the + conntrack original direction tuple. Introduced in Open vSwitch + 2.8. + + + + Matches IPv4 conntrack original direction tuple destination address. + See the paragraphs above for general description to the + conntrack original direction tuple. Introduced in Open vSwitch + 2.8. + + + + Matches IPv6 conntrack original direction tuple source address. + See the paragraphs above for general description to the + conntrack original direction tuple. Introduced in Open vSwitch + 2.8. + + + + Matches IPv6 conntrack original direction tuple destination address. + See the paragraphs above for general description to the + conntrack original direction tuple. Introduced in Open vSwitch + 2.8. + + + + Matches conntrack original direction tuple IP protocol type, + which is specified as a decimal number between 0 and 255, + inclusive (e.g. 1 to match ICMP packets or 6 to match TCP + packets). In case of, for example, an ICMP response to an UDP + packet, this may be different from the IP protocol type of the + packet itself. See the paragraphs above for general description + to the conntrack original direction tuple. Introduced in Open + vSwitch 2.8. + + + + Bitwise match on the conntrack original direction tuple + transport source, when + MFF_CT_NW_PROTO has value 6 for TCP, 17 for UDP, or + 132 for SCTP. When MFF_CT_NW_PROTO has value 1 for + ICMP, or 58 for ICMPv6, the lower 8 bits of + MFF_CT_TP_SRC matches the conntrack original + direction ICMP type. See the paragraphs above for general + description to the conntrack original direction + tuple. Introduced in Open vSwitch 2.8. + + + + Bitwise match on the conntrack original direction tuple + transport destination port, when + MFF_CT_NW_PROTO has value 6 for TCP, 17 for UDP, or + 132 for SCTP. When MFF_CT_NW_PROTO has value 1 for + ICMP, or 58 for ICMPv6, the lower 8 bits of + MFF_CT_TP_DST matches the conntrack original + direction ICMP code. See the paragraphs above for general + description to the conntrack original direction + tuple. Introduced in Open vSwitch 2.8. + diff --git a/lib/nx-match.c b/lib/nx-match.c index bcc1347..35a312f 100644 --- a/lib/nx-match.c +++ b/lib/nx-match.c @@ -970,7 +970,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 == 36); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 37); /* Metadata. */ if (match->wc.masks.dp_hash) { @@ -1118,7 +1118,21 @@ nx_put_raw(struct ofpbuf *b, enum ofp_version oxm, const struct match *match, htonl(match->wc.masks.ct_mark)); nxm_put_128m(b, MFF_CT_LABEL, oxm, hton128(flow->ct_label), hton128(match->wc.masks.ct_label)); - + nxm_put_32m(b, MFF_CT_NW_SRC, oxm, + flow->ct_nw_src, match->wc.masks.ct_nw_src); + nxm_put_ipv6(b, MFF_CT_IPV6_SRC, oxm, + &flow->ct_ipv6_src, &match->wc.masks.ct_ipv6_src); + nxm_put_32m(b, MFF_CT_NW_DST, oxm, + flow->ct_nw_dst, match->wc.masks.ct_nw_dst); + nxm_put_ipv6(b, MFF_CT_IPV6_DST, oxm, + &flow->ct_ipv6_dst, &match->wc.masks.ct_ipv6_dst); + if (flow->ct_nw_proto) { + nxm_put_8(b, MFF_CT_NW_PROTO, oxm, flow->ct_nw_proto); + nxm_put_16m(b, MFF_CT_TP_SRC, oxm, + flow->ct_tp_src, match->wc.masks.ct_tp_src); + nxm_put_16m(b, MFF_CT_TP_DST, oxm, + flow->ct_tp_dst, match->wc.masks.ct_tp_dst); + } /* OpenFlow 1.1+ Metadata. */ nxm_put_64m(b, MFF_METADATA, oxm, flow->metadata, match->wc.masks.metadata); diff --git a/lib/odp-util.c b/lib/odp-util.c index 9f474e0..939e378 100644 --- a/lib/odp-util.c +++ b/lib/odp-util.c @@ -4154,6 +4154,22 @@ parse_odp_key_mask_attr(const char *s, const struct simap *port_names, SCAN_SINGLE("ct_mark(", uint32_t, u32, OVS_KEY_ATTR_CT_MARK); SCAN_SINGLE("ct_label(", ovs_u128, u128, OVS_KEY_ATTR_CT_LABELS); + SCAN_BEGIN("ct_tuple4(", struct ovs_key_ct_tuple_ipv4) { + SCAN_FIELD("src=", ipv4, ipv4_src); + SCAN_FIELD("dst=", ipv4, ipv4_dst); + SCAN_FIELD("proto=", u8, ipv4_proto); + SCAN_FIELD("tp_src=", be16, src_port); + SCAN_FIELD("tp_dst=", be16, dst_port); + } SCAN_END(OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV4); + + SCAN_BEGIN("ct_tuple6(", struct ovs_key_ct_tuple_ipv6) { + SCAN_FIELD("src=", in6_addr, ipv6_src); + SCAN_FIELD("dst=", in6_addr, ipv6_dst); + SCAN_FIELD("proto=", u8, ipv6_proto); + SCAN_FIELD("tp_src=", be16, src_port); + SCAN_FIELD("tp_dst=", be16, dst_port); + } SCAN_END(OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6); + SCAN_BEGIN_NESTED("tunnel(", OVS_KEY_ATTR_TUNNEL) { SCAN_FIELD_NESTED("tun_id=", ovs_be64, be64, OVS_TUNNEL_KEY_ATTR_ID); SCAN_FIELD_NESTED("src=", ovs_be32, ipv4, OVS_TUNNEL_KEY_ATTR_IPV4_SRC); @@ -4404,6 +4420,29 @@ odp_flow_key_from_flow__(const struct odp_flow_key_parms *parms, nl_msg_put_unspec(buf, OVS_KEY_ATTR_CT_LABELS, &data->ct_label, sizeof(data->ct_label)); } + if (parms->support.ct_orig_tuple && flow->ct_nw_proto) { + if (flow->dl_type == htons(ETH_TYPE_IP)) { + struct ovs_key_ct_tuple_ipv4 ct = { + data->ct_nw_src, + data->ct_nw_dst, + data->ct_tp_src, + data->ct_tp_dst, + data->ct_nw_proto, + }; + nl_msg_put_unspec(buf, OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV4, &ct, + sizeof ct); + } else if (flow->dl_type == htons(ETH_TYPE_IPV6)) { + struct ovs_key_ct_tuple_ipv6 ct = { + data->ct_ipv6_src, + data->ct_ipv6_dst, + data->ct_tp_src, + data->ct_tp_dst, + data->ct_nw_proto, + }; + nl_msg_put_unspec(buf, OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6, &ct, + sizeof ct); + } + } if (parms->support.recirc) { nl_msg_put_u32(buf, OVS_KEY_ATTR_RECIRC_ID, data->recirc_id); nl_msg_put_u32(buf, OVS_KEY_ATTR_DP_HASH, data->dp_hash); @@ -4600,6 +4639,19 @@ odp_key_from_pkt_metadata(struct ofpbuf *buf, const struct pkt_metadata *md) nl_msg_put_unspec(buf, OVS_KEY_ATTR_CT_LABELS, &md->ct_label, sizeof(md->ct_label)); } + if (md->ct_orig_tuple_ipv6) { + if (md->ct_orig_tuple.ipv6.ipv6_proto) { + nl_msg_put_unspec(buf, OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6, + &md->ct_orig_tuple.ipv6, + sizeof md->ct_orig_tuple.ipv6); + } + } else { + if (md->ct_orig_tuple.ipv4.ipv4_proto) { + nl_msg_put_unspec(buf, OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV4, + &md->ct_orig_tuple.ipv4, + sizeof md->ct_orig_tuple.ipv4); + } + } } /* Add an ingress port attribute if 'odp_in_port' is not the magical @@ -4668,6 +4720,21 @@ odp_key_to_pkt_metadata(const struct nlattr *key, size_t key_len, wanted_attrs &= ~(1u << OVS_KEY_ATTR_CT_LABELS); break; } + case OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV4: { + const struct ovs_key_ct_tuple_ipv4 *ct = nl_attr_get(nla); + md->ct_orig_tuple.ipv4 = *ct; + md->ct_orig_tuple_ipv6 = false; + wanted_attrs &= ~(1u << OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV4); + break; + } + case OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6: { + const struct ovs_key_ct_tuple_ipv6 *ct = nl_attr_get(nla); + + md->ct_orig_tuple.ipv6 = *ct; + md->ct_orig_tuple_ipv6 = true; + wanted_attrs &= ~(1u << OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6); + break; + } case OVS_KEY_ATTR_TUNNEL: { enum odp_key_fitness res; @@ -5241,6 +5308,25 @@ odp_flow_key_to_flow__(const struct nlattr *key, size_t key_len, flow->ct_label = *cl; expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_CT_LABELS; } + if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV4)) { + const struct ovs_key_ct_tuple_ipv4 *ct = nl_attr_get(attrs[OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV4]); + flow->ct_nw_src = ct->ipv4_src; + flow->ct_nw_dst = ct->ipv4_dst; + flow->ct_nw_proto = ct->ipv4_proto; + flow->ct_tp_src = ct->src_port; + flow->ct_tp_dst = ct->dst_port; + expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV4; + } + if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6)) { + const struct ovs_key_ct_tuple_ipv6 *ct = nl_attr_get(attrs[OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6]); + + flow->ct_ipv6_src = ct->ipv6_src; + flow->ct_ipv6_dst = ct->ipv6_dst; + flow->ct_nw_proto = ct->ipv6_proto; + flow->ct_tp_src = ct->src_port; + flow->ct_tp_dst = ct->dst_port; + expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6; + } if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_TUNNEL)) { enum odp_key_fitness res; diff --git a/lib/odp-util.h b/lib/odp-util.h index 42011bc..2d00815 100644 --- a/lib/odp-util.h +++ b/lib/odp-util.h @@ -127,6 +127,7 @@ void odp_portno_names_destroy(struct hmap *portno_names); * OVS_KEY_ATTR_CT_ZONE 2 2 4 8 * OVS_KEY_ATTR_CT_MARK 4 -- 4 8 * OVS_KEY_ATTR_CT_LABEL 16 -- 4 20 + * OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6 40 -- 4 44 * OVS_KEY_ATTR_ETHERNET 12 -- 4 16 * OVS_KEY_ATTR_ETHERTYPE 2 2 4 8 (outer VLAN ethertype) * OVS_KEY_ATTR_VLAN 2 2 4 8 @@ -136,13 +137,13 @@ void odp_portno_names_destroy(struct hmap *portno_names); * OVS_KEY_ATTR_ICMPV6 2 2 4 8 * OVS_KEY_ATTR_ND 28 -- 4 32 * ---------------------------------------------------------- - * total 572 + * total 616 * * We include some slack space in case the calculation isn't quite right or we * add another field and forget to adjust this value. */ #define ODPUTIL_FLOW_KEY_BYTES 640 -BUILD_ASSERT_DECL(FLOW_WC_SEQ == 36); +BUILD_ASSERT_DECL(FLOW_WC_SEQ == 37); /* 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 @@ -185,6 +186,9 @@ struct odp_support { * 'ct_state'. The above 'ct_state' member must be true for this * to make sense */ bool ct_state_nat; + + bool ct_orig_tuple; /* Conntrack original direction tuple matching + * supported. */ }; struct odp_flow_key_parms { diff --git a/lib/ofp-util.c b/lib/ofp-util.c index ed66dd1..7881480 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 == 36); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 37); /* Initialize most of wc. */ flow_wildcards_init_catchall(wc); diff --git a/lib/packets.h b/lib/packets.h index f7e1d82..a4767f0 100644 --- a/lib/packets.h +++ b/lib/packets.h @@ -100,9 +100,14 @@ struct pkt_metadata { uint32_t skb_priority; /* Packet priority for QoS. */ uint32_t pkt_mark; /* Packet mark. */ uint8_t ct_state; /* Connection state. */ + bool ct_orig_tuple_ipv6; uint16_t ct_zone; /* Connection zone. */ uint32_t ct_mark; /* Connection mark. */ ovs_u128 ct_label; /* Connection label. */ + union { /* Populated only for non-zero 'ct_state'. */ + struct ovs_key_ct_tuple_ipv4 ipv4; + struct ovs_key_ct_tuple_ipv6 ipv6; /* Used only if */ + } ct_orig_tuple; /* 'ct_orig_tuple_ipv6' is set */ union flow_in_port in_port; /* Input port. */ struct flow_tnl tunnel; /* Encapsulating tunnel parameters. Note that * if 'ip_dst' == 0, the rest of the fields may diff --git a/ofproto/ofproto-dpif-rid.h b/ofproto/ofproto-dpif-rid.h index c357591..dfe54ff 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 == 36); +BUILD_ASSERT_DECL(FLOW_WC_SEQ == 37); struct frozen_metadata { /* Metadata in struct flow. */ diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c index eda34f0..13af6d3 100644 --- a/ofproto/ofproto-dpif-xlate.c +++ b/ofproto/ofproto-dpif-xlate.c @@ -3061,6 +3061,17 @@ clear_conntrack(struct xlate_ctx *ctx) flow->ct_zone = 0; flow->ct_mark = 0; flow->ct_label = OVS_U128_ZERO; + + flow->ct_nw_proto = 0; + flow->ct_tp_src = 0; + flow->ct_tp_dst = 0; + if (flow->dl_type == htons(ETH_TYPE_IP)) { + flow->ct_nw_src = 0; + flow->ct_nw_dst = 0; + } if (flow->dl_type == htons(ETH_TYPE_IPV6)) { + memset(&flow->ct_ipv6_src, 0, sizeof flow->ct_ipv6_src); + memset(&flow->ct_ipv6_dst, 0, sizeof flow->ct_ipv6_dst); + } } static bool @@ -3095,7 +3106,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 == 36); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 37); memset(&flow_tnl, 0, sizeof flow_tnl); if (!xport) { diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c index 0bf3786..7e8b08f 100644 --- a/ofproto/ofproto-dpif.c +++ b/ofproto/ofproto-dpif.c @@ -1184,6 +1184,7 @@ CHECK_FEATURE(ct_zone) CHECK_FEATURE(ct_mark) CHECK_FEATURE__(ct_label, ct_label, ct_label.u64.lo, 1) CHECK_FEATURE__(ct_state_nat, ct_state, ct_state, CS_TRACKED|CS_SRC_NAT) +CHECK_FEATURE__(ct_orig_tuple, ct_orig_tuple, ct_nw_proto, 1) #undef CHECK_FEATURE #undef CHECK_FEATURE__ @@ -1210,6 +1211,7 @@ check_support(struct dpif_backer *backer) backer->support.odp.ct_label = check_ct_label(backer); backer->support.odp.ct_state_nat = check_ct_state_nat(backer); + backer->support.odp.ct_orig_tuple = check_ct_orig_tuple(backer); } static int diff --git a/tests/odp.at b/tests/odp.at index db1e827..459ff35 100644 --- a/tests/odp.at +++ b/tests/odp.at @@ -163,7 +163,7 @@ s/$/)/' odp-base.txt echo echo '# Valid forms with conntrack fields.' - sed 's/\(eth([[^)]]*)\),/\1,ct_state(+trk),ct_zone(0x5\/0xff),ct_mark(0x10305070\/0xf0f0f0f0),ct_label(0x1234567890abcdef1234567890abcdef\/0x102030405060708090a0b0c0d0e0f0),/' odp-base.txt + sed 's/\(eth([[^)]]*)\),/\1,ct_state(+trk),ct_zone(0x5\/0xff),ct_mark(0x10305070\/0xf0f0f0f0),ct_label(0x1234567890abcdef1234567890abcdef\/0x102030405060708090a0b0c0d0e0f0),ct_tuple4(src=10.10.10.10,dst=20.20.20.20,proto=17,tp_src=1,tp_dst=2),/' odp-base.txt echo echo '# Valid forms with IP first fragment.' diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at index f1415e4..6f55d43 100644 --- a/tests/ofproto-dpif.at +++ b/tests/ofproto-dpif.at @@ -8447,7 +8447,7 @@ AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): cookie=0x0 total_len=42 in_port=1 (via action) data_len=42 (unbuffered) udp,vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,nw_src=10.1.1.1,nw_dst=10.1.1.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=1,tp_dst=2 udp_csum:e9d6 dnl -NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x0 total_len=42 ct_state=est|rpl|trk,in_port=2 (via action) data_len=42 (unbuffered) +NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x0 total_len=42 ct_state=est|rpl|trk,ct_nw_src=10.1.1.1,ct_nw_dst=10.1.1.2,ct_nw_proto=17,ct_tp_src=1,ct_tp_dst=2,in_port=2 (via action) data_len=42 (unbuffered) udp,vlan_tci=0x0000,dl_src=50:54:00:00:00:0a,dl_dst=50:54:00:00:00:09,nw_src=10.1.1.2,nw_dst=10.1.1.1,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=2,tp_dst=1 udp_csum:e9d6 ]) @@ -8470,7 +8470,7 @@ AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): cookie=0x0 total_len=42 in_port=1 (via action) data_len=42 (unbuffered) udp,vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,nw_src=10.1.1.1,nw_dst=10.1.1.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=3,tp_dst=4 udp_csum:e9d2 dnl -NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x0 total_len=42 ct_state=est|rpl|trk,in_port=2 (via action) data_len=42 (unbuffered) +NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x0 total_len=42 ct_state=est|rpl|trk,ct_nw_src=10.1.1.1,ct_nw_dst=10.1.1.2,ct_nw_proto=17,ct_tp_src=3,ct_tp_dst=4,in_port=2 (via action) data_len=42 (unbuffered) udp,vlan_tci=0x0000,dl_src=50:54:00:00:00:0a,dl_dst=50:54:00:00:00:09,nw_src=10.1.1.2,nw_dst=10.1.1.1,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=4,tp_dst=3 udp_csum:e9d2 ]) @@ -8520,7 +8520,7 @@ dnl happens because the ct_state field is available only after recirc. AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): cookie=0x0 total_len=62 in_port=1 (via action) data_len=62 (unbuffered) udp6,vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,ipv6_src=2001:db8::1,ipv6_dst=2001:db8::2,ipv6_label=0x00000,nw_tos=112,nw_ecn=0,nw_ttl=128,tp_src=1,tp_dst=2 udp_csum:a466 -NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x0 total_len=62 ct_state=est|rpl|trk,in_port=2 (via action) data_len=62 (unbuffered) +NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x0 total_len=62 ct_state=est|rpl|trk,ct_ipv6_src=2001:db8::1,ct_ipv6_dst=2001:db8::2,ct_nw_proto=17,ct_tp_src=1,ct_tp_dst=2,in_port=2 (via action) data_len=62 (unbuffered) udp6,vlan_tci=0x0000,dl_src=50:54:00:00:00:0a,dl_dst=50:54:00:00:00:09,ipv6_src=2001:db8::2,ipv6_dst=2001:db8::1,ipv6_label=0x00000,nw_tos=112,nw_ecn=0,nw_ttl=128,tp_src=2,tp_dst=1 udp_csum:a466 ]) @@ -8631,7 +8631,7 @@ OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit]) dnl Check this output. Only one reply must be there AT_CHECK([cat ofctl_monitor.log], [0], [dnl -NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x0 total_len=42 ct_state=est|rpl|trk,in_port=2 (via action) data_len=42 (unbuffered) +NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x0 total_len=42 ct_state=est|rpl|trk,ct_nw_src=10.1.1.1,ct_nw_dst=10.1.1.2,ct_nw_proto=17,ct_tp_src=1,ct_tp_dst=2,in_port=2 (via action) data_len=42 (unbuffered) udp,vlan_tci=0x0000,dl_src=50:54:00:00:00:0a,dl_dst=50:54:00:00:00:09,nw_src=10.1.1.2,nw_dst=10.1.1.1,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=2,tp_dst=1 udp_csum:e9d6 dnl OFPT_ECHO_REQUEST (xid=0x0): 0 bytes of payload @@ -8724,13 +8724,13 @@ AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN (xid=0x0): cookie=0x0 total_len=42 in_port=1 (via action) data_len=42 (unbuffered) udp,vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,nw_src=10.1.1.1,nw_dst=10.1.1.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=1,tp_dst=2 udp_csum:e9d6 dnl -NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x0 total_len=42 ct_state=est|rpl|trk,in_port=2 (via action) data_len=42 (unbuffered) +NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x0 total_len=42 ct_state=est|rpl|trk,ct_nw_src=10.1.1.1,ct_nw_dst=10.1.1.2,ct_nw_proto=17,ct_tp_src=1,ct_tp_dst=2,in_port=2 (via action) data_len=42 (unbuffered) udp,vlan_tci=0x0000,dl_src=50:54:00:00:00:0a,dl_dst=50:54:00:00:00:09,nw_src=10.1.1.2,nw_dst=10.1.1.1,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=2,tp_dst=1 udp_csum:e9d6 dnl NXT_PACKET_IN (xid=0x0): cookie=0x0 total_len=42 in_port=3 (via action) data_len=42 (unbuffered) udp,vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,nw_src=10.1.1.1,nw_dst=10.1.1.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=1,tp_dst=2 udp_csum:e9d6 dnl -NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x0 total_len=42 ct_state=est|rpl|trk,ct_zone=1,in_port=4 (via action) data_len=42 (unbuffered) +NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x0 total_len=42 ct_state=est|rpl|trk,ct_zone=1,ct_nw_src=10.1.1.1,ct_nw_dst=10.1.1.2,ct_nw_proto=17,ct_tp_src=1,ct_tp_dst=2,in_port=4 (via action) data_len=42 (unbuffered) udp,vlan_tci=0x0000,dl_src=50:54:00:00:00:0a,dl_dst=50:54:00:00:00:09,nw_src=10.1.1.2,nw_dst=10.1.1.1,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=2,tp_dst=1 udp_csum:e9d6 ]) @@ -8777,10 +8777,10 @@ OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit]) dnl Check this output. We only see the latter two packets, not the first. AT_CHECK([cat ofctl_monitor.log], [0], [dnl -NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x0 total_len=42 ct_state=new|trk,in_port=1 (via action) data_len=42 (unbuffered) +NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x0 total_len=42 ct_state=new|trk,ct_nw_src=10.1.1.1,ct_nw_dst=10.1.1.2,ct_nw_proto=17,ct_tp_src=1,ct_tp_dst=2,in_port=1 (via action) data_len=42 (unbuffered) udp,vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,nw_src=10.1.1.1,nw_dst=10.1.1.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=1,tp_dst=2 udp_csum:e9d6 dnl -NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x0 total_len=42 ct_state=est|rpl|trk,in_port=2 (via action) data_len=42 (unbuffered) +NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x0 total_len=42 ct_state=est|rpl|trk,ct_nw_src=10.1.1.1,ct_nw_dst=10.1.1.2,ct_nw_proto=17,ct_tp_src=1,ct_tp_dst=2,in_port=2 (via action) data_len=42 (unbuffered) udp,vlan_tci=0x0000,dl_src=50:54:00:00:00:0a,dl_dst=50:54:00:00:00:09,nw_src=10.1.1.2,nw_dst=10.1.1.1,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=2,tp_dst=1 udp_csum:e9d6 ]) @@ -8827,10 +8827,10 @@ OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit]) dnl Check this output. We only see the first and the last packet AT_CHECK([cat ofctl_monitor.log], [0], [dnl -NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x0 total_len=47 ct_state=new|trk,in_port=1 (via action) data_len=47 (unbuffered) +NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x0 total_len=47 ct_state=new|trk,ct_nw_src=172.16.0.1,ct_nw_dst=172.16.0.2,ct_nw_proto=17,ct_tp_src=41614,ct_tp_dst=5555,in_port=1 (via action) data_len=47 (unbuffered) udp,vlan_tci=0x0000,dl_src=e6:4c:47:35:28:c9,dl_dst=c6:f9:4e:cb:72:db,nw_src=172.16.0.1,nw_dst=172.16.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=41614,tp_dst=5555 udp_csum:2096 dnl -NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x0 total_len=75 ct_state=rel|rpl|trk,in_port=2 (via action) data_len=75 (unbuffered) +NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x0 total_len=75 ct_state=rel|rpl|trk,ct_nw_src=172.16.0.1,ct_nw_dst=172.16.0.2,ct_nw_proto=17,ct_tp_src=41614,ct_tp_dst=5555,in_port=2 (via action) data_len=75 (unbuffered) icmp,vlan_tci=0x0000,dl_src=c6:f9:4e:cb:72:db,dl_dst=e6:4c:47:35:28:c9,nw_src=172.16.0.2,nw_dst=172.16.0.1,nw_tos=192,nw_ecn=0,nw_ttl=64,icmp_type=3,icmp_code=3 icmp_csum:553f ]) @@ -8888,10 +8888,10 @@ dnl NXT_PACKET_IN (xid=0x0): cookie=0x0 total_len=42 ct_mark=0x5,in_port=1 (via action) data_len=42 (unbuffered) udp,vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,nw_src=10.1.1.1,nw_dst=10.1.1.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=5,tp_dst=6 udp_csum:e9ce dnl -NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x0 total_len=42 ct_state=est|rpl|trk,ct_mark=0x1,in_port=2 (via action) data_len=42 (unbuffered) +NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x0 total_len=42 ct_state=est|rpl|trk,ct_mark=0x1,ct_nw_src=10.1.1.1,ct_nw_dst=10.1.1.2,ct_nw_proto=17,ct_tp_src=1,ct_tp_dst=2,in_port=2 (via action) data_len=42 (unbuffered) udp,vlan_tci=0x0000,dl_src=50:54:00:00:00:0a,dl_dst=50:54:00:00:00:09,nw_src=10.1.1.2,nw_dst=10.1.1.1,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=2,tp_dst=1 udp_csum:e9d6 dnl -NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x0 total_len=42 ct_state=est|rpl|trk,ct_mark=0x3,in_port=2 (via action) data_len=42 (unbuffered) +NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x0 total_len=42 ct_state=est|rpl|trk,ct_mark=0x3,ct_nw_src=10.1.1.1,ct_nw_dst=10.1.1.2,ct_nw_proto=17,ct_tp_src=3,ct_tp_dst=4,in_port=2 (via action) data_len=42 (unbuffered) udp,vlan_tci=0x0000,dl_src=50:54:00:00:00:0a,dl_dst=50:54:00:00:00:09,nw_src=10.1.1.2,nw_dst=10.1.1.1,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=4,tp_dst=3 udp_csum:e9d2 ]) @@ -8936,10 +8936,10 @@ OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit]) dnl Check this output. AT_CHECK([cat ofctl_monitor.log], [0], [dnl -NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x0 total_len=42 ct_state=est|rpl|trk,ct_label=0x1,in_port=2 (via action) data_len=42 (unbuffered) +NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x0 total_len=42 ct_state=est|rpl|trk,ct_label=0x1,ct_nw_src=10.1.1.1,ct_nw_dst=10.1.1.2,ct_nw_proto=17,ct_tp_src=1,ct_tp_dst=2,in_port=2 (via action) data_len=42 (unbuffered) udp,vlan_tci=0x0000,dl_src=50:54:00:00:00:0a,dl_dst=50:54:00:00:00:09,nw_src=10.1.1.2,nw_dst=10.1.1.1,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=2,tp_dst=1 udp_csum:e9d6 dnl -NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x0 total_len=42 ct_state=est|rpl|trk,ct_label=0x2,in_port=2 (via action) data_len=42 (unbuffered) +NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x0 total_len=42 ct_state=est|rpl|trk,ct_label=0x2,ct_nw_src=10.1.1.1,ct_nw_dst=10.1.1.2,ct_nw_proto=17,ct_tp_src=3,ct_tp_dst=4,in_port=2 (via action) data_len=42 (unbuffered) udp,vlan_tci=0x0000,dl_src=50:54:00:00:00:0a,dl_dst=50:54:00:00:00:09,nw_src=10.1.1.2,nw_dst=10.1.1.1,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=4,tp_dst=3 udp_csum:e9d2 ]) @@ -9215,7 +9215,7 @@ dnl NXT_PACKET_IN (xid=0x0): cookie=0x0 total_len=42 in_port=1 (via action) data_len=42 (unbuffered) udp,vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,nw_src=10.1.1.1,nw_dst=10.1.1.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=1,tp_dst=2 udp_csum:e9d6 dnl -NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x0 total_len=42 ct_state=est|rpl|trk,in_port=2 (via action) data_len=42 (unbuffered) +NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x0 total_len=42 ct_state=est|rpl|trk,ct_nw_src=10.1.1.1,ct_nw_dst=10.1.1.2,ct_nw_proto=17,ct_tp_src=1,ct_tp_dst=2,in_port=2 (via action) data_len=42 (unbuffered) udp,vlan_tci=0x0000,dl_src=50:54:00:00:00:0a,dl_dst=50:54:00:00:00:09,nw_src=10.1.1.2,nw_dst=10.1.1.1,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=2,tp_dst=1 udp_csum:e9d6 dnl NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x0 total_len=42 in_port=2 (via action) data_len=42 (unbuffered) diff --git a/tests/ofproto.at b/tests/ofproto.at index c899ec8..94b1149 100644 --- a/tests/ofproto.at +++ b/tests/ofproto.at @@ -2406,6 +2406,13 @@ metadata in_port in_port_oxm pkt_mark ct_mark ct_label reg0 reg1 reg2 reg3 reg4 ct_zone: exact match or wildcard ct_mark: arbitrary mask ct_label: arbitrary mask + ct_nw_proto: exact match or wildcard + ct_nw_src: arbitrary mask + ct_nw_dst: arbitrary mask + ct_ipv6_src: arbitrary mask + ct_ipv6_dst: arbitrary mask + ct_tp_src: arbitrary mask + ct_tp_dst: arbitrary mask reg0: arbitrary mask reg1: arbitrary mask reg2: arbitrary mask diff --git a/tests/system-traffic.at b/tests/system-traffic.at index bb7a497..4ba4b08 100644 --- a/tests/system-traffic.at +++ b/tests/system-traffic.at @@ -612,6 +612,7 @@ AT_BANNER([conntrack]) AT_SETUP([conntrack - controller]) CHECK_CONNTRACK() OVS_TRAFFIC_VSWITCHD_START() +AT_CHECK([ovs-appctl vlog/set dpif:dbg dpif_netdev:dbg ofproto_dpif_upcall:dbg]) ADD_NAMESPACES(at_ns0, at_ns1) @@ -645,7 +646,7 @@ dnl Check this output. We only see the latter two packets, not the first. AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN2 (xid=0x0): total_len=42 in_port=1 (via action) data_len=42 (unbuffered) udp,vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,nw_src=10.1.1.1,nw_dst=10.1.1.2,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=1,tp_dst=2 udp_csum:0 -NXT_PACKET_IN2 (xid=0x0): cookie=0x0 total_len=42 ct_state=est|rpl|trk,in_port=2 (via action) data_len=42 (unbuffered) +NXT_PACKET_IN2 (xid=0x0): cookie=0x0 total_len=42 ct_state=est|rpl|trk,ct_nw_src=10.1.1.1,ct_nw_dst=10.1.1.2,ct_nw_proto=17,ct_tp_src=1,ct_tp_dst=2,in_port=2 (via action) data_len=42 (unbuffered) udp,vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,nw_src=10.1.1.2,nw_dst=10.1.1.1,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=2,tp_dst=1 udp_csum:0 ]) @@ -1353,9 +1354,9 @@ dnl Check this output. We only see the latter two packets, not the first. AT_CHECK([cat ofctl_monitor.log], [0], [dnl NXT_PACKET_IN2 (xid=0x0): table_id=1 cookie=0x0 total_len=75 ct_state=inv|trk,in_port=2 (via action) data_len=75 (unbuffered) icmp,vlan_tci=0x0000,dl_src=c6:f5:4e:cb:72:db,dl_dst=f6:4c:47:35:28:c9,nw_src=172.16.0.4,nw_dst=172.16.0.3,nw_tos=192,nw_ecn=0,nw_ttl=64,icmp_type=3,icmp_code=3 icmp_csum:da49 -NXT_PACKET_IN2 (xid=0x0): table_id=1 cookie=0x0 total_len=47 ct_state=new|trk,in_port=1 (via action) data_len=47 (unbuffered) +NXT_PACKET_IN2 (xid=0x0): table_id=1 cookie=0x0 total_len=47 ct_state=new|trk,ct_nw_src=172.16.0.1,ct_nw_dst=172.16.0.2,ct_nw_proto=17,ct_tp_src=41614,ct_tp_dst=5555,in_port=1 (via action) data_len=47 (unbuffered) udp,vlan_tci=0x0000,dl_src=e6:4c:47:35:28:c9,dl_dst=c6:f9:4e:cb:72:db,nw_src=172.16.0.1,nw_dst=172.16.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=41614,tp_dst=5555 udp_csum:2096 -NXT_PACKET_IN2 (xid=0x0): table_id=1 cookie=0x0 total_len=75 ct_state=rel|rpl|trk,in_port=2 (via action) data_len=75 (unbuffered) +NXT_PACKET_IN2 (xid=0x0): table_id=1 cookie=0x0 total_len=75 ct_state=rel|rpl|trk,ct_nw_src=172.16.0.1,ct_nw_dst=172.16.0.2,ct_nw_proto=17,ct_tp_src=41614,ct_tp_dst=5555,in_port=2 (via action) data_len=75 (unbuffered) icmp,vlan_tci=0x0000,dl_src=c6:f9:4e:cb:72:db,dl_dst=e6:4c:47:35:28:c9,nw_src=172.16.0.2,nw_dst=172.16.0.1,nw_tos=192,nw_ecn=0,nw_ttl=64,icmp_type=3,icmp_code=3 icmp_csum:553f ]) @@ -1369,7 +1370,7 @@ AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(172.16.0.3)], [0], [dnl OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP -AT_SETUP([conntrack - IPv4 fragmentation ]) +AT_SETUP([conntrack - IPv4 fragmentation]) CHECK_CONNTRACK() CHECK_CONNTRACK_FRAG() OVS_TRAFFIC_VSWITCHD_START() @@ -2818,6 +2819,72 @@ dnl separate from the above to easier identify issues in this code on different dnl kernels. CHECK_FTP_NAT_POST_RECIRC([seqadj], [10.1.1.240], [0x0a0101f0]) + +dnl CHECK_FTP_NAT_ORIG_TUPLE(TITLE, IP_ADDR, IP_ADDR_AS_HEX) +dnl +dnl Checks the implementation of conntrack original direction tuple matching +dnl with FTP ALGs in combination with NAT, with flow tables that implement +dnl the NATing before the first round of recirculation - that is, the first +dnl flow ct(nat, table=foo) then a subsequent flow will implement the +dnl commiting of NATed and other connections with ct(nat..),output:foo. +dnl +dnl IP_ADDR must specify the NAT address in standard "10.1.1.x" format, +dnl and IP_ADDR_AS_HEX must specify the same address as hex, eg 0x0a0101xx. +m4_define([CHECK_FTP_NAT_ORIG_TUPLE], [dnl + CHECK_FTP_NAT([orig tuple $1], [$2], [dnl +dnl track all IP traffic (includes nat and helper calls to non-NEW packets.) +table=0 ip, action=ct(nat,table=1) +dnl +dnl ARP +dnl +table=0 priority=100 arp arp_op=1 action=move:OXM_OF_ARP_TPA[[]]->NXM_NX_REG2[[]],resubmit(,8),goto_table:10 +table=0 priority=10 arp action=normal +table=0 priority=0 action=drop +dnl +dnl "ACL table" +dnl +dnl Allow all IP traffic with conntrack original direction IP source address +dnl '10.1.1.1'. This should allow also reply packets and related packets in +dnl both directions. +table=1 ip, ct_state=+trk-inv, ct_nw_src=10.1.1.1 action=goto_table:2 +dnl Drop everything else. +table=1 priority=0, action=drop +dnl +dnl "Conntrack commit table" +dnl +dnl Commit new outgoing FTP control connections. Must match on 'tcp' when +dnl setting 'alg=ftp'. +table=2 in_port=1 priority=100 ct_state=+new, tcp, tp_dst=21, action=ct(alg=ftp,commit,nat(src=$2)),2 +dnl Commit other new outgoing IP connections. +table=2 in_port=1 priority=20 ct_state=+new, ip, action=ct(commit,nat(src=$2)),2 +dnl Commit incoming new IP connections. 'nat' may be needed for related +dnl connections, and is harmless for connections that do not need it. +table=2 in_port=2 priority=10 ct_state=+new, ip, action=ct(commit,nat),1 +dnl Just forward all the rest. +table=2 priority=0 in_port=1 action=2 +table=2 priority=0 in_port=2 action=1 +dnl +dnl MAC resolution table for IP in reg2, stores mac in OXM_OF_PKT_REG0 +dnl +table=8,reg2=$3/0xffffffff,action=load:0x808888888888->OXM_OF_PKT_REG0[[]] +table=8,priority=0,action=load:0->OXM_OF_PKT_REG0[[]] +dnl ARP responder mac filled in at OXM_OF_PKT_REG0, or 0 for normal action. +dnl TPA IP in reg2. +dnl Swaps the fields of the ARP message to turn a query to a response. +table=10 priority=100 arp xreg0=0 action=normal +table=10 priority=10,arp,arp_op=1,action=load:2->OXM_OF_ARP_OP[[]],move:OXM_OF_ARP_SHA[[]]->OXM_OF_ARP_THA[[]],move:OXM_OF_PKT_REG0[[0..47]]->OXM_OF_ARP_SHA[[]],move:OXM_OF_ARP_SPA[[]]->OXM_OF_ARP_TPA[[]],move:NXM_NX_REG2[[]]->OXM_OF_ARP_SPA[[]],move:NXM_OF_ETH_SRC[[]]->NXM_OF_ETH_DST[[]],move:OXM_OF_PKT_REG0[[0..47]]->NXM_OF_ETH_SRC[[]],move:NXM_OF_IN_PORT[[]]->NXM_NX_REG3[[0..15]],load:0->NXM_OF_IN_PORT[[]],output:NXM_NX_REG3[[0..15]] +table=10 priority=0 action=drop + ]) +]) + +dnl Check that ct(nat,table=foo) works without TCP sequence adjustment with +dnl an ACL table based on matching on conntrack original direction tuple only. +CHECK_FTP_NAT_ORIG_TUPLE([], [10.1.1.9], [0x0a010109]) + +dnl Check that ct(nat,table=foo) works with TCP sequence adjustment with +dnl an ACL table based on matching on conntrack original direction tuple only. +CHECK_FTP_NAT_ORIG_TUPLE([seqadj], [10.1.1.240], [0x0a0101f0]) + AT_SETUP([conntrack - IPv6 HTTP with NAT]) CHECK_CONNTRACK() CHECK_CONNTRACK_NAT() @@ -2878,9 +2945,6 @@ NS_CHECK_EXEC([at_ns1], [ip -6 neigh add fc00::240 lladdr 80:88:88:88:88:88 dev dnl Allow any traffic from ns0->ns1. dnl Only allow nd, return traffic from ns1->ns0. AT_DATA([flows.txt], [dnl -dnl Allow other ICMPv6 both ways (without commit). -table=1 priority=100 in_port=1 icmp6, action=2 -table=1 priority=100 in_port=2 icmp6, action=1 dnl track all IPv6 traffic (this includes NAT & help to non-NEW packets.) table=0 priority=10 ip6, action=ct(nat,table=1) table=0 priority=0 action=drop @@ -2894,6 +2958,9 @@ table=1 in_port=2 ct_state=+new+rel tcp6 ipv6_dst=fc00::240 action=ct(commit,nat dnl Allow established TCPv6 connections both ways, enforce NATting table=1 in_port=1 ct_state=+est tcp6 ipv6_src=fc00::240 action=2 table=1 in_port=2 ct_state=+est tcp6 ipv6_dst=fc00::1 action=1 +dnl Allow other ICMPv6 both ways (without commit). +table=1 priority=100 in_port=1 icmp6, action=2 +table=1 priority=100 in_port=2 icmp6, action=1 dnl Drop everything else. table=1 priority=0, action=drop ]) @@ -2919,6 +2986,67 @@ tcp,orig=(src=fc00::2,dst=fc00::240,sport=,dport=),reply=(src= OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP + +AT_SETUP([conntrack - IPv6 FTP with NAT - orig tuple]) +AT_SKIP_IF([test $HAVE_FTP = no]) +CHECK_CONNTRACK() +CHECK_CONNTRACK_NAT() +OVS_TRAFFIC_VSWITCHD_START() + +ADD_NAMESPACES(at_ns0, at_ns1) + +ADD_VETH(p0, at_ns0, br0, "fc00::1/96") +NS_CHECK_EXEC([at_ns0], [ip link set dev p0 address 80:88:88:88:88:88]) +ADD_VETH(p1, at_ns1, br0, "fc00::2/96") +dnl Would be nice if NAT could translate neighbor discovery messages, too. +NS_CHECK_EXEC([at_ns1], [ip -6 neigh add fc00::240 lladdr 80:88:88:88:88:88 dev p1]) + +dnl Allow any traffic from ns0->ns1. +dnl Only allow nd, return traffic from ns1->ns0. +AT_DATA([flows.txt], [dnl +dnl track all IPv6 traffic (this includes NAT & help to non-NEW packets.) +table=0 priority=10 ip6, action=ct(nat,table=1) +table=0 priority=0 action=drop +dnl +dnl Table 1 +dnl +dnl Allow other ICMPv6 both ways (without commit). +table=1 priority=100 in_port=1 icmp6, action=2 +table=1 priority=100 in_port=2 icmp6, action=1 +dnl Allow new TCPv6 FTP control connections. +table=1 priority=10 in_port=1 ct_state=+new+trk-inv tcp6 ct_nw_proto=6 ct_ipv6_src=fc00::1 ct_tp_dst=21 action=ct(alg=ftp,commit,nat(src=fc00::240)),2 +dnl Allow related TCPv6 connections from port 2 to the NATted address. +table=1 priority=10 in_port=2 ct_state=+new+rel+trk-inv ipv6 ct_nw_proto=6 ct_ipv6_src=fc00::1 ct_tp_dst=21 action=ct(commit,nat),1 +dnl Allow established TCPv6 connections both ways, enforce NATting +table=1 priority=10 in_port=1 ct_state=+est+trk-inv ipv6 ct_nw_proto=6 ct_ipv6_src=fc00::1 ct_tp_dst=21 action=2 +table=1 priority=10 in_port=2 ct_state=+est+trk-inv ipv6 ct_nw_proto=6 ct_ipv6_src=fc00::1 ct_tp_dst=21 action=1 +dnl Drop everything else. +table=1 priority=0, action=drop +]) + +AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) + +dnl Linux seems to take a little time to get its IPv6 stack in order. Without +dnl waiting, we get occasional failures due to the following error: +dnl "connect: Cannot assign requested address" +OVS_WAIT_UNTIL([ip netns exec at_ns0 ping6 -c 1 fc00::2 >/dev/null]) + +NETNS_DAEMONIZE([at_ns1], [[$PYTHON $srcdir/test-l7.py ftp]], [ftp0.pid]) +OVS_WAIT_UNTIL([ip netns exec at_ns1 netstat -l | grep ftp]) + +dnl FTP requests from p0->p1 should work fine. +NS_CHECK_EXEC([at_ns0], [wget ftp://[[fc00::2]] -6 --no-passive-ftp -t 3 -T 1 --retry-connrefused -v --server-response --no-remove-listing -o wget0.log -d]) + +dnl Discards CLOSE_WAIT and CLOSING +AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fc00::2)], [0], [dnl +tcp,orig=(src=fc00::1,dst=fc00::2,sport=,dport=),reply=(src=fc00::2,dst=fc00::240,sport=,dport=),protoinfo=(state=),helper=ftp +tcp,orig=(src=fc00::2,dst=fc00::240,sport=,dport=),reply=(src=fc00::1,dst=fc00::2,sport=,dport=),protoinfo=(state=) +]) + +OVS_TRAFFIC_VSWITCHD_STOP +AT_CLEANUP + + AT_SETUP([conntrack - DNAT load balancing]) CHECK_CONNTRACK() CHECK_CONNTRACK_NAT()