From 321149d6b3f9596cd4c154fa5cda1eec5a35a3dd Mon Sep 17 00:00:00 2001 From: Open vSwitch CI Date: Mar 02 2023 06:46:48 +0000 Subject: Import openvswitch2.17-2.17.0-79 from Fast DataPath --- diff --git a/SOURCES/openvswitch-2.17.0.patch b/SOURCES/openvswitch-2.17.0.patch index 7720d72..c5bcbb1 100644 --- a/SOURCES/openvswitch-2.17.0.patch +++ b/SOURCES/openvswitch-2.17.0.patch @@ -233,6 +233,19 @@ index c4300cd53e..a297aadac8 100644 (https://git.kernel.org/pub/scm/devel/sparse/sparse.git/). - GNU make. +diff --git a/Documentation/ref/ovs-actions.7.rst b/Documentation/ref/ovs-actions.7.rst +index b59b7634fa..d138956556 100644 +--- a/Documentation/ref/ovs-actions.7.rst ++++ b/Documentation/ref/ovs-actions.7.rst +@@ -1380,7 +1380,7 @@ The ``delete_field`` action + | ``delete_field:``\ *field* + + The ``delete_field`` action deletes a *field* in the syntax described under +-`Field Specifications`_ above. Currently, only the ``tun_metadta`` fields are ++`Field Specifications`_ above. Currently, only the ``tun_metadata`` fields are + supported. + + This action was added in Open vSwitch 2.14. diff --git a/Documentation/topics/dpdk/phy.rst b/Documentation/topics/dpdk/phy.rst index 937f4c40e5..90c8691e32 100644 --- a/Documentation/topics/dpdk/phy.rst @@ -51026,7 +51039,7 @@ index cc43e70e31..c3742f3de2 100644 VLOG_INFO("%s: Received no CCM from RMP %"PRIu64" in the last" " %lldms", cfm->name, rmp->mpid, diff --git a/lib/classifier.c b/lib/classifier.c -index c4790ee6ba..0a89626cc3 100644 +index c4790ee6ba..18dbfc83ad 100644 --- a/lib/classifier.c +++ b/lib/classifier.c @@ -916,9 +916,9 @@ free_conjunctive_matches(struct hmap *matches, @@ -51041,6 +51054,62 @@ index c4790ee6ba..0a89626cc3 100644 if (!(cm >= cm_stubs && cm < &cm_stubs[n_cm_stubs])) { free(cm); } +@@ -1695,6 +1695,8 @@ find_match_wc(const struct cls_subtable *subtable, ovs_version_t version, + const struct cls_match *rule = NULL; + struct flowmap stages_map = FLOWMAP_EMPTY_INITIALIZER; + unsigned int mask_offset = 0; ++ bool adjust_ports_mask = false; ++ ovs_be32 ports_mask; + int i; + + /* Try to finish early by checking fields in segments. */ +@@ -1722,6 +1724,9 @@ find_match_wc(const struct cls_subtable *subtable, ovs_version_t version, + subtable->index_maps[i], flow, wc)) { + goto no_match; + } ++ /* Accumulate the map used so far. */ ++ stages_map = flowmap_or(stages_map, subtable->index_maps[i]); ++ + hash = flow_hash_in_minimask_range(flow, &subtable->mask, + subtable->index_maps[i], + &mask_offset, &basis); +@@ -1731,14 +1736,16 @@ find_match_wc(const struct cls_subtable *subtable, ovs_version_t version, + * unwildcarding all the ports bits, use the ports trie to figure out a + * smaller set of bits to unwildcard. */ + unsigned int mbits; +- ovs_be32 value, plens, mask; ++ ovs_be32 value, plens; + +- mask = miniflow_get_ports(&subtable->mask.masks); +- value = ((OVS_FORCE ovs_be32 *)flow)[TP_PORTS_OFS32] & mask; ++ ports_mask = miniflow_get_ports(&subtable->mask.masks); ++ value = ((OVS_FORCE ovs_be32 *) flow)[TP_PORTS_OFS32] & ports_mask; + mbits = trie_lookup_value(&subtable->ports_trie, &value, &plens, 32); + +- ((OVS_FORCE ovs_be32 *)&wc->masks)[TP_PORTS_OFS32] |= +- mask & be32_prefix_mask(mbits); ++ ports_mask &= be32_prefix_mask(mbits); ++ ports_mask |= ((OVS_FORCE ovs_be32 *) &wc->masks)[TP_PORTS_OFS32]; ++ ++ adjust_ports_mask = true; + + goto no_match; + } +@@ -1751,6 +1758,14 @@ no_match: + /* Unwildcard the bits in stages so far, as they were used in determining + * there is no match. */ + flow_wildcards_fold_minimask_in_map(wc, &subtable->mask, stages_map); ++ if (adjust_ports_mask) { ++ /* This has to be done after updating flow wildcards to overwrite ++ * the ports mask back. We can't simply disable the corresponding bit ++ * in the stages map, because it has 64-bit resolution, i.e. one ++ * bit covers not only tp_src/dst, but also ct_tp_src/dst, which are ++ * not covered by the trie. */ ++ ((OVS_FORCE ovs_be32 *) &wc->masks)[TP_PORTS_OFS32] = ports_mask; ++ } + return NULL; + } + diff --git a/lib/cmap.c b/lib/cmap.c index c9eef3f4ae..8ca893b0b2 100644 --- a/lib/cmap.c @@ -51131,10 +51200,328 @@ index d3b4601858..7f3e63c384 100644 static bool diff --git a/lib/conntrack.c b/lib/conntrack.c -index 33a1a92953..0103fb5396 100644 +index 33a1a92953..fff8e77db1 100644 --- a/lib/conntrack.c +++ b/lib/conntrack.c -@@ -1526,14 +1526,14 @@ set_label(struct dp_packet *pkt, struct conn *conn, +@@ -723,109 +723,59 @@ handle_alg_ctl(struct conntrack *ct, const struct conn_lookup_ctx *ctx, + } + + static void +-pat_packet(struct dp_packet *pkt, const struct conn *conn) ++pat_packet(struct dp_packet *pkt, const struct conn_key *key) + { +- if (conn->nat_action & NAT_ACTION_SRC) { +- if (conn->key.nw_proto == IPPROTO_TCP) { +- struct tcp_header *th = dp_packet_l4(pkt); +- packet_set_tcp_port(pkt, conn->rev_key.dst.port, th->tcp_dst); +- } else if (conn->key.nw_proto == IPPROTO_UDP) { +- struct udp_header *uh = dp_packet_l4(pkt); +- packet_set_udp_port(pkt, conn->rev_key.dst.port, uh->udp_dst); +- } +- } else if (conn->nat_action & NAT_ACTION_DST) { +- if (conn->key.nw_proto == IPPROTO_TCP) { +- packet_set_tcp_port(pkt, conn->rev_key.dst.port, +- conn->rev_key.src.port); +- } else if (conn->key.nw_proto == IPPROTO_UDP) { +- packet_set_udp_port(pkt, conn->rev_key.dst.port, +- conn->rev_key.src.port); +- } ++ if (key->nw_proto == IPPROTO_TCP) { ++ packet_set_tcp_port(pkt, key->dst.port, key->src.port); ++ } else if (key->nw_proto == IPPROTO_UDP) { ++ packet_set_udp_port(pkt, key->dst.port, key->src.port); + } + } + +-static void +-nat_packet(struct dp_packet *pkt, const struct conn *conn, bool related) ++static uint16_t ++nat_action_reverse(uint16_t nat_action) + { +- if (conn->nat_action & NAT_ACTION_SRC) { +- pkt->md.ct_state |= CS_SRC_NAT; +- if (conn->key.dl_type == htons(ETH_TYPE_IP)) { +- struct ip_header *nh = dp_packet_l3(pkt); +- packet_set_ipv4_addr(pkt, &nh->ip_src, +- conn->rev_key.dst.addr.ipv4); +- } else { +- struct ovs_16aligned_ip6_hdr *nh6 = dp_packet_l3(pkt); +- packet_set_ipv6_addr(pkt, conn->key.nw_proto, +- nh6->ip6_src.be32, +- &conn->rev_key.dst.addr.ipv6, true); +- } +- if (!related) { +- pat_packet(pkt, conn); +- } +- } else if (conn->nat_action & NAT_ACTION_DST) { +- pkt->md.ct_state |= CS_DST_NAT; +- if (conn->key.dl_type == htons(ETH_TYPE_IP)) { +- struct ip_header *nh = dp_packet_l3(pkt); +- packet_set_ipv4_addr(pkt, &nh->ip_dst, +- conn->rev_key.src.addr.ipv4); +- } else { +- struct ovs_16aligned_ip6_hdr *nh6 = dp_packet_l3(pkt); +- packet_set_ipv6_addr(pkt, conn->key.nw_proto, +- nh6->ip6_dst.be32, +- &conn->rev_key.src.addr.ipv6, true); +- } +- if (!related) { +- pat_packet(pkt, conn); +- } ++ if (nat_action & NAT_ACTION_SRC) { ++ nat_action ^= NAT_ACTION_SRC; ++ nat_action |= NAT_ACTION_DST; ++ } else if (nat_action & NAT_ACTION_DST) { ++ nat_action ^= NAT_ACTION_DST; ++ nat_action |= NAT_ACTION_SRC; + } ++ return nat_action; + } + + static void +-un_pat_packet(struct dp_packet *pkt, const struct conn *conn) ++nat_packet_ipv4(struct dp_packet *pkt, const struct conn_key *key, ++ uint16_t nat_action) + { +- if (conn->nat_action & NAT_ACTION_SRC) { +- if (conn->key.nw_proto == IPPROTO_TCP) { +- struct tcp_header *th = dp_packet_l4(pkt); +- packet_set_tcp_port(pkt, th->tcp_src, conn->key.src.port); +- } else if (conn->key.nw_proto == IPPROTO_UDP) { +- struct udp_header *uh = dp_packet_l4(pkt); +- packet_set_udp_port(pkt, uh->udp_src, conn->key.src.port); +- } +- } else if (conn->nat_action & NAT_ACTION_DST) { +- if (conn->key.nw_proto == IPPROTO_TCP) { +- packet_set_tcp_port(pkt, conn->key.dst.port, conn->key.src.port); +- } else if (conn->key.nw_proto == IPPROTO_UDP) { +- packet_set_udp_port(pkt, conn->key.dst.port, conn->key.src.port); +- } ++ struct ip_header *nh = dp_packet_l3(pkt); ++ ++ if (nat_action & NAT_ACTION_SRC) { ++ packet_set_ipv4_addr(pkt, &nh->ip_src, key->dst.addr.ipv4); ++ } else if (nat_action & NAT_ACTION_DST) { ++ packet_set_ipv4_addr(pkt, &nh->ip_dst, key->src.addr.ipv4); + } + } + + static void +-reverse_pat_packet(struct dp_packet *pkt, const struct conn *conn) ++nat_packet_ipv6(struct dp_packet *pkt, const struct conn_key *key, ++ uint16_t nat_action) + { +- if (conn->nat_action & NAT_ACTION_SRC) { +- if (conn->key.nw_proto == IPPROTO_TCP) { +- struct tcp_header *th_in = dp_packet_l4(pkt); +- packet_set_tcp_port(pkt, conn->key.src.port, +- th_in->tcp_dst); +- } else if (conn->key.nw_proto == IPPROTO_UDP) { +- struct udp_header *uh_in = dp_packet_l4(pkt); +- packet_set_udp_port(pkt, conn->key.src.port, +- uh_in->udp_dst); +- } +- } else if (conn->nat_action & NAT_ACTION_DST) { +- if (conn->key.nw_proto == IPPROTO_TCP) { +- packet_set_tcp_port(pkt, conn->key.src.port, +- conn->key.dst.port); +- } else if (conn->key.nw_proto == IPPROTO_UDP) { +- packet_set_udp_port(pkt, conn->key.src.port, +- conn->key.dst.port); +- } ++ struct ovs_16aligned_ip6_hdr *nh6 = dp_packet_l3(pkt); ++ ++ if (nat_action & NAT_ACTION_SRC) { ++ packet_set_ipv6_addr(pkt, key->nw_proto, nh6->ip6_src.be32, ++ &key->dst.addr.ipv6, true); ++ } else if (nat_action & NAT_ACTION_DST) { ++ packet_set_ipv6_addr(pkt, key->nw_proto, nh6->ip6_dst.be32, ++ &key->src.addr.ipv6, true); + } + } + + static void +-reverse_nat_packet(struct dp_packet *pkt, const struct conn *conn) ++nat_inner_packet(struct dp_packet *pkt, struct conn_key *key, ++ uint16_t nat_action) + { + char *tail = dp_packet_tail(pkt); + uint16_t pad = dp_packet_l2_pad_size(pkt); +@@ -834,98 +784,77 @@ reverse_nat_packet(struct dp_packet *pkt, const struct conn *conn) + uint16_t orig_l3_ofs = pkt->l3_ofs; + uint16_t orig_l4_ofs = pkt->l4_ofs; + +- if (conn->key.dl_type == htons(ETH_TYPE_IP)) { +- struct ip_header *nh = dp_packet_l3(pkt); +- struct icmp_header *icmp = dp_packet_l4(pkt); +- struct ip_header *inner_l3 = (struct ip_header *) (icmp + 1); +- /* This call is already verified to succeed during the code path from +- * 'conn_key_extract()' which calls 'extract_l4_icmp()'. */ +- extract_l3_ipv4(&inner_key, inner_l3, tail - ((char *)inner_l3) - pad, ++ void *l3 = dp_packet_l3(pkt); ++ void *l4 = dp_packet_l4(pkt); ++ void *inner_l3; ++ /* These calls are already verified to succeed during the code path from ++ * 'conn_key_extract()' which calls ++ * 'extract_l4_icmp()'/'extract_l4_icmp6()'. */ ++ if (key->dl_type == htons(ETH_TYPE_IP)) { ++ inner_l3 = (char *) l4 + sizeof(struct icmp_header); ++ extract_l3_ipv4(&inner_key, inner_l3, tail - ((char *) inner_l3) - pad, + &inner_l4, false); +- pkt->l3_ofs += (char *) inner_l3 - (char *) nh; +- pkt->l4_ofs += inner_l4 - (char *) icmp; ++ } else { ++ inner_l3 = (char *) l4 + sizeof(struct icmp6_data_header); ++ extract_l3_ipv6(&inner_key, inner_l3, tail - ((char *) inner_l3) - pad, ++ &inner_l4); ++ } ++ pkt->l3_ofs += (char *) inner_l3 - (char *) l3; ++ pkt->l4_ofs += inner_l4 - (char *) l4; + +- if (conn->nat_action & NAT_ACTION_SRC) { +- packet_set_ipv4_addr(pkt, &inner_l3->ip_src, +- conn->key.src.addr.ipv4); +- } else if (conn->nat_action & NAT_ACTION_DST) { +- packet_set_ipv4_addr(pkt, &inner_l3->ip_dst, +- conn->key.dst.addr.ipv4); +- } ++ /* Reverse the key for inner packet. */ ++ struct conn_key rev_key = *key; ++ conn_key_reverse(&rev_key); ++ ++ pat_packet(pkt, &rev_key); ++ ++ if (key->dl_type == htons(ETH_TYPE_IP)) { ++ nat_packet_ipv4(pkt, &rev_key, nat_action); + +- reverse_pat_packet(pkt, conn); ++ struct icmp_header *icmp = (struct icmp_header *) l4; + icmp->icmp_csum = 0; + icmp->icmp_csum = csum(icmp, tail - (char *) icmp - pad); + } else { +- struct ovs_16aligned_ip6_hdr *nh6 = dp_packet_l3(pkt); +- struct icmp6_data_header *icmp6 = dp_packet_l4(pkt); +- struct ovs_16aligned_ip6_hdr *inner_l3_6 = +- (struct ovs_16aligned_ip6_hdr *) (icmp6 + 1); +- /* This call is already verified to succeed during the code path from +- * 'conn_key_extract()' which calls 'extract_l4_icmp6()'. */ +- extract_l3_ipv6(&inner_key, inner_l3_6, +- tail - ((char *)inner_l3_6) - pad, +- &inner_l4); +- pkt->l3_ofs += (char *) inner_l3_6 - (char *) nh6; +- pkt->l4_ofs += inner_l4 - (char *) icmp6; +- +- if (conn->nat_action & NAT_ACTION_SRC) { +- packet_set_ipv6_addr(pkt, conn->key.nw_proto, +- inner_l3_6->ip6_src.be32, +- &conn->key.src.addr.ipv6, true); +- } else if (conn->nat_action & NAT_ACTION_DST) { +- packet_set_ipv6_addr(pkt, conn->key.nw_proto, +- inner_l3_6->ip6_dst.be32, +- &conn->key.dst.addr.ipv6, true); +- } +- reverse_pat_packet(pkt, conn); ++ nat_packet_ipv6(pkt, &rev_key, nat_action); ++ ++ struct icmp6_data_header *icmp6 = (struct icmp6_data_header *) l4; + icmp6->icmp6_base.icmp6_cksum = 0; +- icmp6->icmp6_base.icmp6_cksum = packet_csum_upperlayer6(nh6, icmp6, +- IPPROTO_ICMPV6, tail - (char *) icmp6 - pad); ++ icmp6->icmp6_base.icmp6_cksum = ++ packet_csum_upperlayer6(l3, icmp6, IPPROTO_ICMPV6, ++ tail - (char *) icmp6 - pad); + } ++ + pkt->l3_ofs = orig_l3_ofs; + pkt->l4_ofs = orig_l4_ofs; + } + + static void +-un_nat_packet(struct dp_packet *pkt, const struct conn *conn, +- bool related) ++nat_packet(struct dp_packet *pkt, struct conn *conn, bool reply, bool related) + { +- if (conn->nat_action & NAT_ACTION_SRC) { +- pkt->md.ct_state |= CS_DST_NAT; +- if (conn->key.dl_type == htons(ETH_TYPE_IP)) { +- struct ip_header *nh = dp_packet_l3(pkt); +- packet_set_ipv4_addr(pkt, &nh->ip_dst, +- conn->key.src.addr.ipv4); +- } else { +- struct ovs_16aligned_ip6_hdr *nh6 = dp_packet_l3(pkt); +- packet_set_ipv6_addr(pkt, conn->key.nw_proto, +- nh6->ip6_dst.be32, +- &conn->key.src.addr.ipv6, true); +- } ++ struct conn_key *key = reply ? &conn->key : &conn->rev_key; ++ uint16_t nat_action = reply ? nat_action_reverse(conn->nat_action) ++ : conn->nat_action; + +- if (OVS_UNLIKELY(related)) { +- reverse_nat_packet(pkt, conn); +- } else { +- un_pat_packet(pkt, conn); +- } +- } else if (conn->nat_action & NAT_ACTION_DST) { ++ /* Update ct_state. */ ++ if (nat_action & NAT_ACTION_SRC) { + pkt->md.ct_state |= CS_SRC_NAT; +- if (conn->key.dl_type == htons(ETH_TYPE_IP)) { +- struct ip_header *nh = dp_packet_l3(pkt); +- packet_set_ipv4_addr(pkt, &nh->ip_src, +- conn->key.dst.addr.ipv4); +- } else { +- struct ovs_16aligned_ip6_hdr *nh6 = dp_packet_l3(pkt); +- packet_set_ipv6_addr(pkt, conn->key.nw_proto, +- nh6->ip6_src.be32, +- &conn->key.dst.addr.ipv6, true); +- } ++ } else if (nat_action & NAT_ACTION_DST) { ++ pkt->md.ct_state |= CS_DST_NAT; ++ } ++ ++ /* Reverse the key for outer header. */ ++ if (key->dl_type == htons(ETH_TYPE_IP)) { ++ nat_packet_ipv4(pkt, key, nat_action); ++ } else { ++ nat_packet_ipv6(pkt, key, nat_action); ++ } + ++ if (nat_action & NAT_ACTION_SRC || nat_action & NAT_ACTION_DST) { + if (OVS_UNLIKELY(related)) { +- reverse_nat_packet(pkt, conn); ++ nat_action = nat_action_reverse(nat_action); ++ nat_inner_packet(pkt, key, nat_action); + } else { +- un_pat_packet(pkt, conn); ++ pat_packet(pkt, key); + } + } + } +@@ -1044,7 +973,7 @@ conn_not_found(struct conntrack *ct, struct dp_packet *pkt, + memcpy(nc, nat_conn, sizeof *nc); + } + +- nat_packet(pkt, nc, ctx->icmp_related); ++ nat_packet(pkt, nc, false, ctx->icmp_related); + memcpy(&nat_conn->key, &nc->rev_key, sizeof nat_conn->key); + memcpy(&nat_conn->rev_key, &nc->key, sizeof nat_conn->rev_key); + nat_conn->conn_type = CT_CONN_TYPE_UN_NAT; +@@ -1148,11 +1077,8 @@ handle_nat(struct dp_packet *pkt, struct conn *conn, + if (pkt->md.ct_state & (CS_SRC_NAT | CS_DST_NAT)) { + pkt->md.ct_state &= ~(CS_SRC_NAT | CS_DST_NAT); + } +- if (reply) { +- un_nat_packet(pkt, conn, related); +- } else { +- nat_packet(pkt, conn, related); +- } ++ ++ nat_packet(pkt, conn, reply, related); + } + } + +@@ -1526,14 +1452,14 @@ set_label(struct dp_packet *pkt, struct conn *conn, static long long ct_sweep(struct conntrack *ct, long long now, size_t limit) { @@ -51151,7 +51538,7 @@ index 33a1a92953..0103fb5396 100644 ovs_mutex_lock(&conn->lock); if (now < conn->expiration || count >= limit) { min_expiration = MIN(min_expiration, conn->expiration); -@@ -2242,7 +2242,7 @@ nat_range_hash(const struct conn *conn, uint32_t basis, +@@ -2242,7 +2168,7 @@ nat_range_hash(const struct conn *conn, uint32_t basis, hash = ct_addr_hash_add(hash, &nat_info->min_addr); hash = ct_addr_hash_add(hash, &nat_info->max_addr); hash = hash_add(hash, @@ -51160,7 +51547,7 @@ index 33a1a92953..0103fb5396 100644 | nat_info->min_port); hash = ct_endpoint_hash_add(hash, &conn->key.src); hash = ct_endpoint_hash_add(hash, &conn->key.dst); -@@ -2265,8 +2265,16 @@ set_sport_range(const struct nat_action_info_t *ni, const struct conn_key *k, +@@ -2265,8 +2191,16 @@ set_sport_range(const struct nat_action_info_t *ni, const struct conn_key *k, if (((ni->nat_action & NAT_ACTION_SNAT_ALL) == NAT_ACTION_SRC) || ((ni->nat_action & NAT_ACTION_DST))) { *curr = ntohs(k->src.port); @@ -51179,7 +51566,7 @@ index 33a1a92953..0103fb5396 100644 } else { *min = ni->min_port; *max = ni->max_port; -@@ -2389,6 +2397,26 @@ next_addr_in_range_guarded(union ct_addr *curr, union ct_addr *min, +@@ -2389,6 +2323,26 @@ next_addr_in_range_guarded(union ct_addr *curr, union ct_addr *min, return exhausted; } @@ -51206,7 +51593,7 @@ index 33a1a92953..0103fb5396 100644 /* This function tries to get a unique tuple. * Every iteration checks that the reverse tuple doesn't * collide with any existing one. -@@ -2403,9 +2431,11 @@ next_addr_in_range_guarded(union ct_addr *curr, union ct_addr *min, +@@ -2403,9 +2357,11 @@ next_addr_in_range_guarded(union ct_addr *curr, union ct_addr *min, * * In case of DNAT: * - For each dst IP address in the range (if any). @@ -51221,7 +51608,7 @@ index 33a1a92953..0103fb5396 100644 * * If none can be found, return exhaustion to the caller. */ static bool -@@ -2436,6 +2466,11 @@ nat_get_unique_tuple(struct conntrack *ct, const struct conn *conn, +@@ -2436,6 +2392,11 @@ nat_get_unique_tuple(struct conntrack *ct, const struct conn *conn, set_dport_range(nat_info, &conn->key, hash, &curr_dport, &min_dport, &max_dport); @@ -51233,7 +51620,7 @@ index 33a1a92953..0103fb5396 100644 another_round: store_addr_to_key(&curr_addr, &nat_conn->rev_key, nat_info->nat_action); -@@ -2449,15 +2484,19 @@ another_round: +@@ -2449,15 +2410,19 @@ another_round: goto next_addr; } @@ -51262,7 +51649,7 @@ index 33a1a92953..0103fb5396 100644 } /* Check if next IP is in range and respin. Otherwise, notify -@@ -2857,8 +2896,8 @@ expectation_clean(struct conntrack *ct, const struct conn_key *parent_key) +@@ -2857,8 +2822,8 @@ expectation_clean(struct conntrack *ct, const struct conn_key *parent_key) { ovs_rwlock_wrlock(&ct->resources_lock); @@ -51327,6 +51714,44 @@ index d344514343..1afcc65adb 100644 return; } +diff --git a/lib/dpctl.c b/lib/dpctl.c +index 29041fa3e3..742fbce2d9 100644 +--- a/lib/dpctl.c ++++ b/lib/dpctl.c +@@ -1727,26 +1727,23 @@ dpctl_flush_conntrack(int argc, const char *argv[], + + /* Report error if there are more than one unparsed argument. */ + if (args > 1) { +- ds_put_cstr(&ds, "invalid arguments"); + error = EINVAL; +- goto error; ++ dpctl_error(dpctl_p, error, "invalid arguments: %s", ds_cstr(&ds)); ++ goto out; + } + + error = opt_dpif_open(argc, argv, dpctl_p, 4, &dpif); + if (error) { +- return error; ++ goto out; + } + + error = ct_dpif_flush(dpif, pzone, ptuple); +- if (!error) { +- dpif_close(dpif); +- return 0; +- } else { +- ds_put_cstr(&ds, "failed to flush conntrack"); ++ if (error) { ++ dpctl_error(dpctl_p, error, "failed to flush conntrack: %s", ++ ds_cstr(&ds)); + } + +-error: +- dpctl_error(dpctl_p, error, "%s", ds_cstr(&ds)); ++out: + ds_destroy(&ds); + dpif_close(dpif); + return error; diff --git a/lib/dpif-netdev-avx512.c b/lib/dpif-netdev-avx512.c index b7131ba3f1..82a4138184 100644 --- a/lib/dpif-netdev-avx512.c @@ -58015,16 +58440,165 @@ index ed58de17de..aad9f9c77a 100644 free(nf_flow); } diff --git a/ofproto/ofproto-dpif-ipfix.c b/ofproto/ofproto-dpif-ipfix.c -index 9280e008ea..742eed3998 100644 +index 9280e008ea..f13478a884 100644 --- a/ofproto/ofproto-dpif-ipfix.c +++ b/ofproto/ofproto-dpif-ipfix.c -@@ -926,17 +926,21 @@ dpif_ipfix_bridge_exporter_destroy(struct dpif_ipfix_bridge_exporter *exporter) +@@ -124,11 +124,18 @@ struct dpif_ipfix_port { + uint32_t ifindex; + }; + ++struct dpif_ipfix_domain { ++ struct hmap_node hmap_node; /* In struct dpif_ipfix_exporter's domains. */ ++ time_t last_template_set_time; ++}; ++ + struct dpif_ipfix_exporter { + uint32_t exporter_id; /* Exporting Process identifier */ +- struct collectors *collectors; + uint32_t seq_number; +- time_t last_template_set_time; ++ struct collectors *collectors; ++ struct hmap domains; /* Contains struct dpif_ipfix_domain indexed by ++ observation domain id. */ ++ time_t last_stats_sent_time; + struct hmap cache_flow_key_map; /* ipfix_flow_cache_entry. */ + struct ovs_list cache_flow_start_timestamp_list; /* ipfix_flow_cache_entry. */ + uint32_t cache_active_timeout; /* In seconds. */ +@@ -617,6 +624,9 @@ static void get_export_time_now(uint64_t *, uint32_t *); + + static void dpif_ipfix_cache_expire_now(struct dpif_ipfix_exporter *, bool); + ++static void dpif_ipfix_exporter_del_domain(struct dpif_ipfix_exporter *, ++ struct dpif_ipfix_domain *); ++ + static bool + ofproto_ipfix_bridge_exporter_options_equal( + const struct ofproto_ipfix_bridge_exporter_options *a, +@@ -697,13 +707,14 @@ dpif_ipfix_exporter_init(struct dpif_ipfix_exporter *exporter) + exporter->exporter_id = ++exporter_total_count; + exporter->collectors = NULL; + exporter->seq_number = 1; +- exporter->last_template_set_time = 0; ++ exporter->last_stats_sent_time = 0; + hmap_init(&exporter->cache_flow_key_map); + ovs_list_init(&exporter->cache_flow_start_timestamp_list); + exporter->cache_active_timeout = 0; + exporter->cache_max_flows = 0; + exporter->virtual_obs_id = NULL; + exporter->virtual_obs_len = 0; ++ hmap_init(&exporter->domains); + + memset(&exporter->ipfix_global_stats, 0, + sizeof(struct dpif_ipfix_global_stats)); +@@ -711,6 +722,7 @@ dpif_ipfix_exporter_init(struct dpif_ipfix_exporter *exporter) + + static void + dpif_ipfix_exporter_clear(struct dpif_ipfix_exporter *exporter) ++ OVS_REQUIRES(mutex) + { + /* Flush the cache with flow end reason "forced end." */ + dpif_ipfix_cache_expire_now(exporter, true); +@@ -719,22 +731,29 @@ dpif_ipfix_exporter_clear(struct dpif_ipfix_exporter *exporter) + exporter->exporter_id = 0; + exporter->collectors = NULL; + exporter->seq_number = 1; +- exporter->last_template_set_time = 0; ++ exporter->last_stats_sent_time = 0; + exporter->cache_active_timeout = 0; + exporter->cache_max_flows = 0; + free(exporter->virtual_obs_id); + exporter->virtual_obs_id = NULL; + exporter->virtual_obs_len = 0; + ++ struct dpif_ipfix_domain *dom; ++ HMAP_FOR_EACH_SAFE (dom, hmap_node, &exporter->domains) { ++ dpif_ipfix_exporter_del_domain(exporter, dom); ++ } ++ + memset(&exporter->ipfix_global_stats, 0, + sizeof(struct dpif_ipfix_global_stats)); + } + + static void + dpif_ipfix_exporter_destroy(struct dpif_ipfix_exporter *exporter) ++ OVS_REQUIRES(mutex) + { + dpif_ipfix_exporter_clear(exporter); + hmap_destroy(&exporter->cache_flow_key_map); ++ hmap_destroy(&exporter->domains); + } + + static bool +@@ -742,7 +761,7 @@ dpif_ipfix_exporter_set_options(struct dpif_ipfix_exporter *exporter, + const struct sset *targets, + const uint32_t cache_active_timeout, + const uint32_t cache_max_flows, +- const char *virtual_obs_id) ++ const char *virtual_obs_id) OVS_REQUIRES(mutex) + { + size_t virtual_obs_len; + collectors_destroy(exporter->collectors); +@@ -769,6 +788,37 @@ dpif_ipfix_exporter_set_options(struct dpif_ipfix_exporter *exporter, + return true; + } + ++static struct dpif_ipfix_domain * ++dpif_ipfix_exporter_find_domain(const struct dpif_ipfix_exporter *exporter, ++ uint32_t domain_id) OVS_REQUIRES(mutex) ++{ ++ struct dpif_ipfix_domain *dom; ++ HMAP_FOR_EACH_WITH_HASH (dom, hmap_node, hash_int(domain_id, 0), ++ &exporter->domains) { ++ return dom; ++ } ++ return NULL; ++} ++ ++static struct dpif_ipfix_domain * ++dpif_ipfix_exporter_insert_domain(struct dpif_ipfix_exporter *exporter, ++ const uint32_t domain_id) OVS_REQUIRES(mutex) ++{ ++ struct dpif_ipfix_domain *dom = xmalloc(sizeof *dom); ++ dom->last_template_set_time = 0; ++ hmap_insert(&exporter->domains, &dom->hmap_node, hash_int(domain_id, 0)); ++ return dom; ++} ++ ++static void ++dpif_ipfix_exporter_del_domain(struct dpif_ipfix_exporter *exporter, ++ struct dpif_ipfix_domain *dom) ++ OVS_REQUIRES(mutex) ++{ ++ hmap_remove(&exporter->domains, &dom->hmap_node); ++ free(dom); ++} ++ + static struct dpif_ipfix_port * + dpif_ipfix_find_port(const struct dpif_ipfix *di, + odp_port_t odp_port) OVS_REQUIRES(mutex) +@@ -909,6 +959,7 @@ dpif_ipfix_bridge_exporter_init(struct dpif_ipfix_bridge_exporter *exporter) + + static void + dpif_ipfix_bridge_exporter_clear(struct dpif_ipfix_bridge_exporter *exporter) ++ OVS_REQUIRES(mutex) + { + dpif_ipfix_exporter_clear(&exporter->exporter); + ofproto_ipfix_bridge_exporter_options_destroy(exporter->options); +@@ -918,6 +969,7 @@ dpif_ipfix_bridge_exporter_clear(struct dpif_ipfix_bridge_exporter *exporter) + + static void + dpif_ipfix_bridge_exporter_destroy(struct dpif_ipfix_bridge_exporter *exporter) ++ OVS_REQUIRES(mutex) + { + dpif_ipfix_bridge_exporter_clear(exporter); + dpif_ipfix_exporter_destroy(&exporter->exporter); +@@ -926,17 +978,21 @@ dpif_ipfix_bridge_exporter_destroy(struct dpif_ipfix_bridge_exporter *exporter) static void dpif_ipfix_bridge_exporter_set_options( struct dpif_ipfix_bridge_exporter *exporter, - const struct ofproto_ipfix_bridge_exporter_options *options) + const struct ofproto_ipfix_bridge_exporter_options *options, -+ bool *options_changed) ++ bool *options_changed) OVS_REQUIRES(mutex) { - bool options_changed; - @@ -58045,7 +58619,7 @@ index 9280e008ea..742eed3998 100644 !exporter->options || !ofproto_ipfix_bridge_exporter_options_equal( options, exporter->options)); -@@ -945,7 +949,7 @@ dpif_ipfix_bridge_exporter_set_options( +@@ -945,7 +1001,7 @@ dpif_ipfix_bridge_exporter_set_options( * shortchanged in collectors (which indicates that opening one or * more of the configured collectors failed, so that we should * retry). */ @@ -58054,7 +58628,7 @@ index 9280e008ea..742eed3998 100644 || collectors_count(exporter->exporter.collectors) < sset_count(&options->targets)) { if (!dpif_ipfix_exporter_set_options( -@@ -957,7 +961,7 @@ dpif_ipfix_bridge_exporter_set_options( +@@ -957,7 +1013,7 @@ dpif_ipfix_bridge_exporter_set_options( } /* Avoid reconfiguring if options didn't change. */ @@ -58063,13 +58637,29 @@ index 9280e008ea..742eed3998 100644 return; } -@@ -1015,17 +1019,21 @@ dpif_ipfix_flow_exporter_destroy(struct dpif_ipfix_flow_exporter *exporter) +@@ -999,6 +1055,7 @@ dpif_ipfix_flow_exporter_init(struct dpif_ipfix_flow_exporter *exporter) + + static void + dpif_ipfix_flow_exporter_clear(struct dpif_ipfix_flow_exporter *exporter) ++ OVS_REQUIRES(mutex) + { + dpif_ipfix_exporter_clear(&exporter->exporter); + ofproto_ipfix_flow_exporter_options_destroy(exporter->options); +@@ -1007,6 +1064,7 @@ dpif_ipfix_flow_exporter_clear(struct dpif_ipfix_flow_exporter *exporter) + + static void + dpif_ipfix_flow_exporter_destroy(struct dpif_ipfix_flow_exporter *exporter) ++ OVS_REQUIRES(mutex) + { + dpif_ipfix_flow_exporter_clear(exporter); + dpif_ipfix_exporter_destroy(&exporter->exporter); +@@ -1015,17 +1073,21 @@ dpif_ipfix_flow_exporter_destroy(struct dpif_ipfix_flow_exporter *exporter) static bool dpif_ipfix_flow_exporter_set_options( struct dpif_ipfix_flow_exporter *exporter, - const struct ofproto_ipfix_flow_exporter_options *options) + const struct ofproto_ipfix_flow_exporter_options *options, -+ bool *options_changed) ++ bool *options_changed) OVS_REQUIRES(mutex) { - bool options_changed; - @@ -58090,7 +58680,7 @@ index 9280e008ea..742eed3998 100644 !exporter->options || !ofproto_ipfix_flow_exporter_options_equal( options, exporter->options)); -@@ -1034,7 +1042,7 @@ dpif_ipfix_flow_exporter_set_options( +@@ -1034,7 +1096,7 @@ dpif_ipfix_flow_exporter_set_options( * shortchanged in collectors (which indicates that opening one or * more of the configured collectors failed, so that we should * retry). */ @@ -58099,7 +58689,7 @@ index 9280e008ea..742eed3998 100644 || collectors_count(exporter->exporter.collectors) < sset_count(&options->targets)) { if (!dpif_ipfix_exporter_set_options( -@@ -1046,7 +1054,7 @@ dpif_ipfix_flow_exporter_set_options( +@@ -1046,7 +1108,7 @@ dpif_ipfix_flow_exporter_set_options( } /* Avoid reconfiguring if options didn't change. */ @@ -58108,7 +58698,14 @@ index 9280e008ea..742eed3998 100644 return true; } -@@ -1069,7 +1077,7 @@ remove_flow_exporter(struct dpif_ipfix *di, +@@ -1063,13 +1125,14 @@ dpif_ipfix_flow_exporter_set_options( + static void + remove_flow_exporter(struct dpif_ipfix *di, + struct dpif_ipfix_flow_exporter_map_node *node) ++ OVS_REQUIRES(mutex) + { + hmap_remove(&di->flow_exporter_map, &node->node); + dpif_ipfix_flow_exporter_destroy(&node->exporter); free(node); } @@ -58117,7 +58714,7 @@ index 9280e008ea..742eed3998 100644 dpif_ipfix_set_options( struct dpif_ipfix *di, const struct ofproto_ipfix_bridge_exporter_options *bridge_exporter_options, -@@ -1077,16 +1085,19 @@ dpif_ipfix_set_options( +@@ -1077,16 +1140,19 @@ dpif_ipfix_set_options( size_t n_flow_exporters_options) OVS_EXCLUDED(mutex) { int i; @@ -58139,7 +58736,7 @@ index 9280e008ea..742eed3998 100644 for (i = 0; i < n_flow_exporters_options; i++) { node = dpif_ipfix_find_flow_exporter_map_node( di, options->collector_set_id); -@@ -1095,15 +1106,19 @@ dpif_ipfix_set_options( +@@ -1095,15 +1161,19 @@ dpif_ipfix_set_options( dpif_ipfix_flow_exporter_init(&node->exporter); hmap_insert(&di->flow_exporter_map, &node->node, hash_int(options->collector_set_id, 0)); @@ -58161,7 +58758,7 @@ index 9280e008ea..742eed3998 100644 /* This is slow but doesn't take any extra memory, and * this table is not supposed to contain many rows anyway. */ options = (struct ofproto_ipfix_flow_exporter_options *) -@@ -1117,10 +1132,12 @@ dpif_ipfix_set_options( +@@ -1117,10 +1187,12 @@ dpif_ipfix_set_options( } if (i == n_flow_exporters_options) { /* Not found. */ remove_flow_exporter(di, node); @@ -58174,7 +58771,7 @@ index 9280e008ea..742eed3998 100644 } struct dpif_ipfix * -@@ -1215,7 +1232,7 @@ static void +@@ -1215,7 +1287,7 @@ static void dpif_ipfix_clear(struct dpif_ipfix *di) OVS_REQUIRES(mutex) { struct dpif_ipfix_flow_exporter_map_node *exp_node; @@ -58183,7 +58780,7 @@ index 9280e008ea..742eed3998 100644 dpif_ipfix_bridge_exporter_clear(&di->bridge_exporter); -@@ -1224,7 +1241,7 @@ dpif_ipfix_clear(struct dpif_ipfix *di) OVS_REQUIRES(mutex) +@@ -1224,7 +1296,7 @@ dpif_ipfix_clear(struct dpif_ipfix *di) OVS_REQUIRES(mutex) free(exp_node); } @@ -58192,16 +58789,55 @@ index 9280e008ea..742eed3998 100644 dpif_ipfix_del_port__(di, dip); } } -@@ -2799,7 +2816,7 @@ dpif_ipfix_cache_expire(struct dpif_ipfix_exporter *exporter, +@@ -1983,6 +2055,7 @@ static void + ipfix_cache_update(struct dpif_ipfix_exporter *exporter, + struct ipfix_flow_cache_entry *entry, + enum ipfix_sampled_packet_type sampled_pkt_type) ++ OVS_REQUIRES(mutex) + { + struct ipfix_flow_cache_entry *old_entry; + size_t current_flows = 0; +@@ -2794,14 +2867,36 @@ dpif_ipfix_flow_sample(struct dpif_ipfix *di, const struct dp_packet *packet, + ovs_mutex_unlock(&mutex); + } + ++static bool ++dpif_ipfix_should_send_template(struct dpif_ipfix_exporter *exporter, ++ const uint32_t observation_domain_id, ++ const uint32_t export_time_sec) ++ OVS_REQUIRES(mutex) ++{ ++ struct dpif_ipfix_domain *domain; ++ domain = dpif_ipfix_exporter_find_domain(exporter, ++ observation_domain_id); ++ if (!domain) { ++ /* First time we see this obs_domain_id. */ ++ domain = dpif_ipfix_exporter_insert_domain(exporter, ++ observation_domain_id); ++ } ++ ++ if ((domain->last_template_set_time + IPFIX_TEMPLATE_INTERVAL) ++ <= export_time_sec) { ++ domain->last_template_set_time = export_time_sec; ++ return true; ++ } ++ return false; ++} ++ + static void + dpif_ipfix_cache_expire(struct dpif_ipfix_exporter *exporter, bool forced_end, const uint64_t export_time_usec, - const uint32_t export_time_sec) +- const uint32_t export_time_sec) ++ const uint32_t export_time_sec) OVS_REQUIRES(mutex) { - struct ipfix_flow_cache_entry *entry, *next_entry; + struct ipfix_flow_cache_entry *entry; uint64_t max_flow_start_timestamp_usec; - bool template_msg_sent = false; +- bool template_msg_sent = false; enum ipfix_flow_end_reason flow_end_reason; -@@ -2811,7 +2828,7 @@ dpif_ipfix_cache_expire(struct dpif_ipfix_exporter *exporter, + + if (ovs_list_is_empty(&exporter->cache_flow_start_timestamp_list)) { +@@ -2811,7 +2906,7 @@ dpif_ipfix_cache_expire(struct dpif_ipfix_exporter *exporter, max_flow_start_timestamp_usec = export_time_usec - 1000000LL * exporter->cache_active_timeout; @@ -58210,6 +58846,58 @@ index 9280e008ea..742eed3998 100644 &exporter->cache_flow_start_timestamp_list) { if (forced_end) { flow_end_reason = FORCED_END; +@@ -2827,25 +2922,28 @@ dpif_ipfix_cache_expire(struct dpif_ipfix_exporter *exporter, + break; + } + +- ovs_list_remove(&entry->cache_flow_start_timestamp_list_node); +- hmap_remove(&exporter->cache_flow_key_map, +- &entry->flow_key_map_node); ++ /* XXX: Make frequency of the (Options) Template and Exporter Process ++ * Statistics transmission configurable. ++ * Cf. IETF RFC 5101 Section 4.3. and 10.3.6. */ ++ if ((exporter->last_stats_sent_time + IPFIX_TEMPLATE_INTERVAL) ++ <= export_time_sec) { ++ exporter->last_stats_sent_time = export_time_sec; ++ ipfix_send_exporter_data_msg(exporter, export_time_sec); ++ } + +- /* XXX: Make frequency of the (Options) Template and Exporter Process +- * Statistics transmission configurable. +- * Cf. IETF RFC 5101 Section 4.3. and 10.3.6. */ +- if (!template_msg_sent +- && (exporter->last_template_set_time + IPFIX_TEMPLATE_INTERVAL) +- <= export_time_sec) { ++ if (dpif_ipfix_should_send_template(exporter, ++ entry->flow_key.obs_domain_id, ++ export_time_sec)) { ++ VLOG_DBG("Sending templates for ObservationDomainID %"PRIu32, ++ entry->flow_key.obs_domain_id); + ipfix_send_template_msgs(exporter, export_time_sec, + entry->flow_key.obs_domain_id); +- exporter->last_template_set_time = export_time_sec; +- template_msg_sent = true; +- +- /* Send Exporter Process Statistics. */ +- ipfix_send_exporter_data_msg(exporter, export_time_sec); + } + ++ ovs_list_remove(&entry->cache_flow_start_timestamp_list_node); ++ hmap_remove(&exporter->cache_flow_key_map, ++ &entry->flow_key_map_node); ++ + /* XXX: Group multiple data records for the same obs domain id + * into the same message. */ + ipfix_send_data_msg(exporter, export_time_sec, entry, flow_end_reason); +@@ -2866,7 +2964,7 @@ get_export_time_now(uint64_t *export_time_usec, uint32_t *export_time_sec) + + static void + dpif_ipfix_cache_expire_now(struct dpif_ipfix_exporter *exporter, +- bool forced_end) ++ bool forced_end) OVS_REQUIRES(mutex) + { + uint64_t export_time_usec; + uint32_t export_time_sec; diff --git a/ofproto/ofproto-dpif-ipfix.h b/ofproto/ofproto-dpif-ipfix.h index 1f42cd5275..75c0ab81ac 100644 --- a/ofproto/ofproto-dpif-ipfix.h @@ -58266,7 +58954,7 @@ index 78a54c715d..109940ad2a 100644 oftrace_node_destroy(node); } diff --git a/ofproto/ofproto-dpif-upcall.c b/ofproto/ofproto-dpif-upcall.c -index 57f94df544..adb53f8031 100644 +index 57f94df544..59126b9014 100644 --- a/ofproto/ofproto-dpif-upcall.c +++ b/ofproto/ofproto-dpif-upcall.c @@ -362,6 +362,10 @@ static void upcall_unixctl_dump_wait(struct unixctl_conn *conn, int argc, @@ -58299,6 +58987,15 @@ index 57f94df544..adb53f8031 100644 ovsrcu_postpone(ukey_delete__, old_ukey); transition_ukey(old_ukey, UKEY_DELETED); transition_ukey(new_ukey, UKEY_VISIBLE); +@@ -2853,7 +2862,7 @@ revalidator_sweep__(struct revalidator *revalidator, bool purge) + } else { + struct dpif_flow_stats stats; + COVERAGE_INC(revalidate_missed_dp_flow); +- memset(&stats, 0, sizeof stats); ++ memcpy(&stats, &ukey->stats, sizeof stats); + result = revalidate_ukey(udpif, ukey, &stats, &odp_actions, + reval_seq, &recircs, false); + } @@ -3099,6 +3108,31 @@ upcall_unixctl_purge(struct unixctl_conn *conn, int argc OVS_UNUSED, unixctl_command_reply(conn, ""); } @@ -58771,7 +59468,7 @@ index 851088d794..2ba90e999c 100644 void xlate_bundle_set(struct ofproto_dpif *, struct ofbundle *, const char *name, enum port_vlan_mode, diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c -index 8143dd965f..f9562dee87 100644 +index 8143dd965f..b3e575bcd0 100644 --- a/ofproto/ofproto-dpif.c +++ b/ofproto/ofproto-dpif.c @@ -215,10 +215,6 @@ struct shash all_dpif_backers = SHASH_INITIALIZER(&all_dpif_backers); @@ -58785,7 +59482,64 @@ index 8143dd965f..f9562dee87 100644 static bool ofproto_use_tnl_push_pop = true; static void ofproto_unixctl_init(void); static void ct_zone_config_init(struct dpif_backer *backer); -@@ -1663,7 +1659,7 @@ static int +@@ -718,12 +714,6 @@ close_dpif_backer(struct dpif_backer *backer, bool del) + free(backer); + } + +-/* Datapath port slated for removal from datapath. */ +-struct odp_garbage { +- struct ovs_list list_node; +- odp_port_t odp_port; +-}; +- + static void check_support(struct dpif_backer *backer); + + static int +@@ -733,8 +723,6 @@ open_dpif_backer(const char *type, struct dpif_backer **backerp) + struct dpif_port_dump port_dump; + struct dpif_port port; + struct shash_node *node; +- struct ovs_list garbage_list; +- struct odp_garbage *garbage; + + struct sset names; + char *backer_name; +@@ -796,25 +784,23 @@ open_dpif_backer(const char *type, struct dpif_backer **backerp) + dpif_flow_flush(backer->dpif); + } + +- /* Loop through the ports already on the datapath and remove any +- * that we don't need anymore. */ +- ovs_list_init(&garbage_list); ++ /* Loop through the ports already on the datapath and find ones that are ++ * not on the initial OpenFlow ports list. These are stale ports, that we ++ * do not need anymore, or tunnel backing interfaces, that do not generally ++ * match the name of OpenFlow tunnel ports, or both. Add all of them to ++ * the list of tunnel backers. type_run() will garbage collect those that ++ * are not active tunnel backing interfaces during revalidation. */ + dpif_port_dump_start(&port_dump, backer->dpif); + while (dpif_port_dump_next(&port_dump, &port)) { + node = shash_find(&init_ofp_ports, port.name); + if (!node && strcmp(port.name, dpif_base_name(backer->dpif))) { +- garbage = xmalloc(sizeof *garbage); +- garbage->odp_port = port.port_no; +- ovs_list_push_front(&garbage_list, &garbage->list_node); ++ simap_put(&backer->tnl_backers, port.name, ++ odp_to_u32(port.port_no)); ++ backer->need_revalidate = REV_RECONFIGURE; + } + } + dpif_port_dump_done(&port_dump); + +- LIST_FOR_EACH_POP (garbage, list_node, &garbage_list) { +- dpif_port_del(backer->dpif, garbage->odp_port, false); +- free(garbage); +- } +- + shash_add(&all_dpif_backers, type, backer); + + check_support(backer); +@@ -1663,7 +1649,7 @@ static int construct(struct ofproto *ofproto_) { struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); @@ -58794,7 +59548,7 @@ index 8143dd965f..f9562dee87 100644 int error; /* Tunnel module can get used right after the udpif threads are running. */ -@@ -1701,7 +1697,7 @@ construct(struct ofproto *ofproto_) +@@ -1701,7 +1687,7 @@ construct(struct ofproto *ofproto_) ofproto->ams_seqno = seq_read(ofproto->ams_seq); @@ -58803,7 +59557,7 @@ index 8143dd965f..f9562dee87 100644 struct iface_hint *iface_hint = node->data; if (!strcmp(iface_hint->br_name, ofproto->up.name)) { -@@ -1720,9 +1716,6 @@ construct(struct ofproto *ofproto_) +@@ -1720,9 +1706,6 @@ construct(struct ofproto *ofproto_) hmap_insert(&all_ofproto_dpifs_by_name, &ofproto->all_ofproto_dpifs_by_name_node, hash_string(ofproto->up.name, 0)); @@ -58813,7 +59567,7 @@ index 8143dd965f..f9562dee87 100644 memset(&ofproto->stats, 0, sizeof ofproto->stats); ofproto_init_tables(ofproto_, N_TABLES); -@@ -1820,8 +1813,6 @@ destruct(struct ofproto *ofproto_, bool del) +@@ -1820,8 +1803,6 @@ destruct(struct ofproto *ofproto_, bool del) hmap_remove(&all_ofproto_dpifs_by_name, &ofproto->all_ofproto_dpifs_by_name_node); @@ -58822,7 +59576,7 @@ index 8143dd965f..f9562dee87 100644 OFPROTO_FOR_EACH_TABLE (table, &ofproto->up) { CLS_FOR_EACH (rule, up.cr, &table->cls) { -@@ -1857,6 +1848,8 @@ destruct(struct ofproto *ofproto_, bool del) +@@ -1857,6 +1838,8 @@ destruct(struct ofproto *ofproto_, bool del) seq_destroy(ofproto->ams_seq); @@ -58831,7 +59585,7 @@ index 8143dd965f..f9562dee87 100644 close_dpif_backer(ofproto->backer, del); } -@@ -1945,7 +1938,7 @@ run(struct ofproto *ofproto_) +@@ -1945,7 +1928,7 @@ run(struct ofproto *ofproto_) new_dump_seq = seq_read(udpif_dump_seq(ofproto->backer->udpif)); if (ofproto->dump_seq != new_dump_seq) { @@ -58840,7 +59594,7 @@ index 8143dd965f..f9562dee87 100644 long long now = time_msec(); /* We know stats are relatively fresh, so now is a good time to do some -@@ -1955,7 +1948,7 @@ run(struct ofproto *ofproto_) +@@ -1955,7 +1938,7 @@ run(struct ofproto *ofproto_) /* Expire OpenFlow flows whose idle_timeout or hard_timeout * has passed. */ ovs_mutex_lock(&ofproto_mutex); @@ -58849,7 +59603,7 @@ index 8143dd965f..f9562dee87 100644 &ofproto->up.expirable) { rule_expire(rule_dpif_cast(rule), now); } -@@ -2346,6 +2339,7 @@ set_ipfix( +@@ -2346,6 +2329,7 @@ set_ipfix( struct dpif_ipfix *di = ofproto->ipfix; bool has_options = bridge_exporter_options || flow_exporters_options; bool new_di = false; @@ -58857,7 +59611,7 @@ index 8143dd965f..f9562dee87 100644 if (has_options && !di) { di = ofproto->ipfix = dpif_ipfix_create(); -@@ -2355,7 +2349,7 @@ set_ipfix( +@@ -2355,7 +2339,7 @@ set_ipfix( if (di) { /* Call set_options in any case to cleanly flush the flow * caches in the last exporters that are to be destroyed. */ @@ -58866,7 +59620,7 @@ index 8143dd965f..f9562dee87 100644 di, bridge_exporter_options, flow_exporters_options, n_flow_exporters_options); -@@ -2371,6 +2365,10 @@ set_ipfix( +@@ -2371,6 +2355,10 @@ set_ipfix( dpif_ipfix_unref(di); ofproto->ipfix = NULL; } @@ -58877,7 +59631,7 @@ index 8143dd965f..f9562dee87 100644 } return 0; -@@ -2493,11 +2491,11 @@ set_lldp(struct ofport *ofport_, +@@ -2493,11 +2481,11 @@ set_lldp(struct ofport *ofport_, { struct ofport_dpif *ofport = ofport_dpif_cast(ofport_); struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofport->up.ofproto); @@ -58891,7 +59645,7 @@ index 8143dd965f..f9562dee87 100644 ofport->lldp = lldp_create(ofport->up.netdev, ofport_->mtu, cfg); } -@@ -2509,6 +2507,9 @@ set_lldp(struct ofport *ofport_, +@@ -2509,6 +2497,9 @@ set_lldp(struct ofport *ofport_, } else if (ofport->lldp) { lldp_unref(ofport->lldp); ofport->lldp = NULL; @@ -58901,7 +59655,7 @@ index 8143dd965f..f9562dee87 100644 ofproto->backer->need_revalidate = REV_RECONFIGURE; } -@@ -3106,11 +3107,11 @@ bundle_flush_macs(struct ofbundle *bundle, bool all_ofprotos) +@@ -3106,11 +3097,11 @@ bundle_flush_macs(struct ofbundle *bundle, bool all_ofprotos) { struct ofproto_dpif *ofproto = bundle->ofproto; struct mac_learning *ml = ofproto->ml; @@ -58915,7 +59669,7 @@ index 8143dd965f..f9562dee87 100644 if (mac_entry_get_port(ml, mac) == bundle) { if (all_ofprotos) { struct ofproto_dpif *o; -@@ -3141,13 +3142,13 @@ bundle_move(struct ofbundle *old, struct ofbundle *new) +@@ -3141,13 +3132,13 @@ bundle_move(struct ofbundle *old, struct ofbundle *new) { struct ofproto_dpif *ofproto = old->ofproto; struct mac_learning *ml = ofproto->ml; @@ -58931,7 +59685,7 @@ index 8143dd965f..f9562dee87 100644 if (mac_entry_get_port(ml, mac) == old) { mac_entry_set_port(ml, mac, new); } -@@ -3244,7 +3245,7 @@ static void +@@ -3244,7 +3235,7 @@ static void bundle_destroy(struct ofbundle *bundle) { struct ofproto_dpif *ofproto; @@ -58940,7 +59694,7 @@ index 8143dd965f..f9562dee87 100644 if (!bundle) { return; -@@ -3257,7 +3258,7 @@ bundle_destroy(struct ofbundle *bundle) +@@ -3257,7 +3248,7 @@ bundle_destroy(struct ofbundle *bundle) xlate_bundle_remove(bundle); xlate_txn_commit(); @@ -58949,7 +59703,7 @@ index 8143dd965f..f9562dee87 100644 bundle_del_port(port); } -@@ -3347,9 +3348,7 @@ bundle_set(struct ofproto *ofproto_, void *aux, +@@ -3347,9 +3338,7 @@ bundle_set(struct ofproto *ofproto_, void *aux, } } if (!ok || ovs_list_size(&bundle->ports) != s->n_members) { @@ -58960,7 +59714,7 @@ index 8143dd965f..f9562dee87 100644 for (i = 0; i < s->n_members; i++) { if (s->members[i] == port->up.ofp_port) { goto found; -@@ -3963,6 +3962,10 @@ port_add(struct ofproto *ofproto_, struct netdev *netdev) +@@ -3963,6 +3952,10 @@ port_add(struct ofproto *ofproto_, struct netdev *netdev) simap_put(&ofproto->backer->tnl_backers, dp_port_name, odp_to_u32(port_no)); } @@ -58971,7 +59725,7 @@ index 8143dd965f..f9562dee87 100644 } if (netdev_get_tunnel_config(netdev)) { -@@ -4471,12 +4474,14 @@ rule_dpif_lookup_from_table(struct ofproto_dpif *ofproto, +@@ -4471,12 +4464,14 @@ rule_dpif_lookup_from_table(struct ofproto_dpif *ofproto, atomic_add_relaxed(&tbl->n_matched, stats->n_packets, &orig); } if (xcache) { @@ -58991,7 +59745,7 @@ index 8143dd965f..f9562dee87 100644 } return rule; } -@@ -4507,12 +4512,14 @@ rule_dpif_lookup_from_table(struct ofproto_dpif *ofproto, +@@ -4507,12 +4502,14 @@ rule_dpif_lookup_from_table(struct ofproto_dpif *ofproto, stats->n_packets, &orig); } if (xcache) { @@ -59011,7 +59765,7 @@ index 8143dd965f..f9562dee87 100644 } if (rule) { goto out; /* Match. */ -@@ -5550,9 +5557,9 @@ ct_zone_timeout_policy_sweep(struct dpif_backer *backer) +@@ -5550,9 +5547,9 @@ ct_zone_timeout_policy_sweep(struct dpif_backer *backer) { if (!ovs_list_is_empty(&backer->ct_tp_kill_list) && time_msec() >= timeout_policy_cleanup_timer) { @@ -59023,7 +59777,7 @@ index 8143dd965f..f9562dee87 100644 if (!ct_dpif_del_timeout_policy(backer->dpif, ct_tp->tp_id)) { ovs_list_remove(&ct_tp->list_node); ct_timeout_policy_destroy(ct_tp, backer->tp_ids); -@@ -5594,6 +5601,7 @@ ct_set_zone_timeout_policy(const char *datapath_type, uint16_t zone_id, +@@ -5594,6 +5591,7 @@ ct_set_zone_timeout_policy(const char *datapath_type, uint16_t zone_id, ct_timeout_policy_unref(backer, ct_zone->ct_tp); ct_zone->ct_tp = ct_tp; ct_tp->ref_count++; @@ -59031,7 +59785,7 @@ index 8143dd965f..f9562dee87 100644 } } else { struct ct_zone *new_ct_zone = ct_zone_alloc(zone_id); -@@ -5601,6 +5609,7 @@ ct_set_zone_timeout_policy(const char *datapath_type, uint16_t zone_id, +@@ -5601,6 +5599,7 @@ ct_set_zone_timeout_policy(const char *datapath_type, uint16_t zone_id, cmap_insert(&backer->ct_zones, &new_ct_zone->node, hash_int(zone_id, 0)); ct_tp->ref_count++; @@ -59039,7 +59793,7 @@ index 8143dd965f..f9562dee87 100644 } } -@@ -5617,6 +5626,7 @@ ct_del_zone_timeout_policy(const char *datapath_type, uint16_t zone_id) +@@ -5617,6 +5616,7 @@ ct_del_zone_timeout_policy(const char *datapath_type, uint16_t zone_id) if (ct_zone) { ct_timeout_policy_unref(backer, ct_zone->ct_tp); ct_zone_remove_and_destroy(backer, ct_zone); @@ -59047,7 +59801,7 @@ index 8143dd965f..f9562dee87 100644 } } -@@ -5818,15 +5828,7 @@ ofproto_dpif_lookup_by_name(const char *name) +@@ -5818,15 +5818,7 @@ ofproto_dpif_lookup_by_name(const char *name) struct ofproto_dpif * ofproto_dpif_lookup_by_uuid(const struct uuid *uuid) { @@ -61714,10 +62468,105 @@ index 2bef06f39c..922185d61d 100644 OVS_VSWITCHD_STOP AT_CLEANUP diff --git a/tests/classifier.at b/tests/classifier.at -index cdcd72c156..f652b59837 100644 +index cdcd72c156..de2705653e 100644 --- a/tests/classifier.at +++ b/tests/classifier.at -@@ -129,6 +129,31 @@ Datapath actions: 3 +@@ -65,6 +65,94 @@ Datapath actions: 2 + OVS_VSWITCHD_STOP + AT_CLEANUP + ++AT_SETUP([flow classifier - lookup segmentation - final stage]) ++OVS_VSWITCHD_START ++add_of_ports br0 1 2 3 ++AT_DATA([flows.txt], [dnl ++table=0 in_port=1 priority=33,tcp,tp_dst=80,tcp_flags=+psh,action=output(2) ++table=0 in_port=1 priority=0,ip,action=drop ++table=0 in_port=2 priority=16,icmp6,nw_ttl=255,icmp_type=135,icmp_code=0,nd_target=1000::1 ,action=output(1) ++table=0 in_port=2 priority=0,ip,action=drop ++table=0 in_port=3 action=resubmit(,1) ++table=1 in_port=3 priority=45,ct_state=+trk+rpl,ct_nw_proto=6,ct_tp_src=3/0x1,tcp,tp_dst=80,tcp_flags=+psh,action=output(2) ++table=1 in_port=3 priority=10,ip,action=drop ++]) ++AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) ++ ++AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=6,nw_tos=0,nw_ttl=128,tp_src=8,tp_dst=80,tcp_flags=syn'], [0], [stdout]) ++AT_CHECK([tail -2 stdout], [0], ++ [Megaflow: recirc_id=0,eth,tcp,in_port=1,nw_frag=no,tp_dst=80,tcp_flags=-psh ++Datapath actions: drop ++]) ++AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=6,nw_tos=0,nw_ttl=128,tp_src=8,tp_dst=80,tcp_flags=syn|ack'], [0], [stdout]) ++AT_CHECK([tail -2 stdout], [0], ++ [Megaflow: recirc_id=0,eth,tcp,in_port=1,nw_frag=no,tp_dst=80,tcp_flags=-psh ++Datapath actions: drop ++]) ++AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=6,nw_tos=0,nw_ttl=128,tp_src=8,tp_dst=80,tcp_flags=ack|psh'], [0], [stdout]) ++AT_CHECK([tail -2 stdout], [0], ++ [Megaflow: recirc_id=0,eth,tcp,in_port=1,nw_frag=no,tp_dst=80,tcp_flags=+psh ++Datapath actions: 2 ++]) ++AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=6,nw_tos=0,nw_ttl=128,tp_src=8,tp_dst=80'], [0], [stdout]) ++AT_CHECK([tail -2 stdout], [0], ++ [Megaflow: recirc_id=0,eth,tcp,in_port=1,nw_frag=no,tp_dst=80,tcp_flags=-psh ++Datapath actions: drop ++]) ++AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=6,nw_tos=0,nw_ttl=128,tp_src=8,tp_dst=79'], [0], [stdout]) ++AT_CHECK([tail -2 stdout], [0], ++ [Megaflow: recirc_id=0,eth,tcp,in_port=1,nw_frag=no,tp_dst=0x40/0xfff0,tcp_flags=-psh ++Datapath actions: drop ++]) ++ ++dnl Having both the port and the tcp flags in the resulting megaflow below ++dnl is redundant, but that is how ports trie logic is implemented. ++AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=6,nw_tos=0,nw_ttl=128,tp_src=8,tp_dst=81'], [0], [stdout]) ++AT_CHECK([tail -2 stdout], [0], ++ [Megaflow: recirc_id=0,eth,tcp,in_port=1,nw_frag=no,tp_dst=81,tcp_flags=-psh ++Datapath actions: drop ++]) ++ ++dnl nd_target is redundant in the megaflow below and it is also not relevant ++dnl for an icmp reply. Datapath may discard that match, but it is OK as long ++dnl as we have prerequisites (icmp_type) in the match as well. ++AT_CHECK([ovs-appctl ofproto/trace br0 "in_port=2,eth_src=f6:d2:b0:19:5e:7b,eth_dst=d2:49:19:91:78:fe,dl_type=0x86dd,ipv6_src=1000::3,ipv6_dst=1000::4,nw_proto=58,nw_ttl=255,icmpv6_type=128,icmpv6_code=0"], [0], [stdout]) ++AT_CHECK([tail -2 stdout], [0], ++ [Megaflow: recirc_id=0,eth,icmp6,in_port=2,nw_ttl=255,nw_frag=no,icmp_type=0x80/0xfc,nd_target=:: ++Datapath actions: drop ++]) ++ ++AT_CHECK([ovs-appctl ofproto/trace br0 "in_port=2,eth_src=f6:d2:b0:19:5e:7b,eth_dst=d2:49:19:91:78:fe,dl_type=0x86dd,ipv6_src=1000::3,ipv6_dst=1000::4,nw_proto=58,nw_ttl=255,icmpv6_type=135,icmpv6_code=0"], [0], [stdout]) ++AT_CHECK([tail -2 stdout], [0], ++ [Megaflow: recirc_id=0,eth,icmp6,in_port=2,nw_ttl=255,nw_frag=no,icmp_type=0x87/0xff,icmp_code=0x0/0xff,nd_target=:: ++Datapath actions: drop ++]) ++AT_CHECK([ovs-appctl ofproto/trace br0 "in_port=2,eth_src=f6:d2:b0:19:5e:7b,eth_dst=d2:49:19:91:78:fe,dl_type=0x86dd,ipv6_src=1000::3,ipv6_dst=1000::4,nw_proto=58,nw_ttl=255,icmpv6_type=135,icmpv6_code=0,nd_target=1000::1"], [0], [stdout]) ++AT_CHECK([tail -2 stdout], [0], ++ [Megaflow: recirc_id=0,eth,icmp6,in_port=2,nw_ttl=255,nw_frag=no,icmp_type=0x87/0xff,icmp_code=0x0/0xff,nd_target=1000::1 ++Datapath actions: 1 ++]) ++AT_CHECK([ovs-appctl ofproto/trace br0 "in_port=2,eth_src=f6:d2:b0:19:5e:7b,eth_dst=d2:49:19:91:78:fe,dl_type=0x86dd,ipv6_src=1000::3,ipv6_dst=1000::4,nw_proto=58,nw_ttl=255,icmpv6_type=135,icmpv6_code=0,nd_target=1000::2"], [0], [stdout]) ++AT_CHECK([tail -2 stdout], [0], ++ [Megaflow: recirc_id=0,eth,icmp6,in_port=2,nw_ttl=255,nw_frag=no,icmp_type=0x87/0xff,icmp_code=0x0/0xff,nd_target=1000::2 ++Datapath actions: drop ++]) ++ ++dnl Check that ports' mask doesn't affect ct ports. ++AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=3,ct_state=trk|rpl,ct_nw_proto=6,ct_tp_src=3,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=6,nw_tos=0,nw_ttl=128,tp_src=8,tp_dst=80,tcp_flags=psh'], [0], [stdout]) ++AT_CHECK([tail -2 stdout], [0], ++ [Megaflow: recirc_id=0,ct_state=+rpl+trk,ct_nw_proto=6,ct_tp_src=0x1/0x1,eth,tcp,in_port=3,nw_frag=no,tp_dst=80,tcp_flags=+psh ++Datapath actions: 2 ++]) ++AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=3,ct_state=trk|rpl,ct_nw_proto=6,ct_tp_src=3,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=6,nw_tos=0,nw_ttl=128,tp_src=8,tp_dst=79,tcp_flags=psh'], [0], [stdout]) ++AT_CHECK([tail -2 stdout], [0], ++ [Megaflow: recirc_id=0,ct_state=+rpl+trk,ct_nw_proto=6,ct_tp_src=0x1/0x1,eth,tcp,in_port=3,nw_frag=no,tp_dst=0x40/0xfff0,tcp_flags=+psh ++Datapath actions: drop ++]) ++ ++OVS_VSWITCHD_STOP ++AT_CLEANUP ++ + AT_BANNER([flow classifier prefix lookup]) + AT_SETUP([flow classifier - prefix lookup]) + OVS_VSWITCHD_START +@@ -129,6 +217,31 @@ Datapath actions: 3 OVS_VSWITCHD_STOP(["/'prefixes' with incompatible field: ipv6_label/d"]) AT_CLEANUP @@ -66007,6 +66856,73 @@ index c3ee6990ca..7d2715c4a7 100644 AT_CHECK([ovs-appctl dpif-netdev/miniflow-parser-set autovalidator], [0], [dnl Miniflow extract implementation set to autovalidator. ]) +diff --git a/tests/system-interface.at b/tests/system-interface.at +index 784bada12c..3bf339582d 100644 +--- a/tests/system-interface.at ++++ b/tests/system-interface.at +@@ -63,3 +63,62 @@ AT_CHECK([ + [stdout], [Device "br-p1" does not exist.] + ) + AT_CLEANUP ++ ++AT_SETUP([interface - datapath ports garbage collection]) ++OVS_CHECK_GENEVE() ++OVS_TRAFFIC_VSWITCHD_START() ++ ++dnl Not relevant for userspace datapath. ++AT_SKIP_IF([! ovs-appctl dpctl/show | grep -q ovs-system]) ++ ++AT_CHECK([ovs-vsctl add-port br0 tunnel_port dnl ++ -- set Interface tunnel_port dnl ++ type=geneve options:remote_ip=flow options:key=123]) ++ ++AT_CHECK([ip link add ovs-veth0 type veth peer name ovs-veth1]) ++on_exit 'ip link del ovs-veth0' ++ ++AT_CHECK([ovs-vsctl add-port br0 ovs-veth0]) ++ ++OVS_WAIT_UNTIL([ip link show | grep -q " genev_sys_[[0-9]]*: .* ovs-system "]) ++ ++dnl Store the output of ip link for geneve port to compare ifindex later. ++AT_CHECK([ip link show | grep " genev_sys_[[0-9]]*: .* ovs-system " > geneve.0]) ++ ++AT_CHECK([ovs-appctl dpctl/show | grep port], [0], [dnl ++ port 0: ovs-system (internal) ++ port 1: br0 (internal) ++ port 2: genev_sys_6081 (geneve: packet_type=ptap) ++ port 3: ovs-veth0 ++]) ++ ++OVS_APP_EXIT_AND_WAIT_BY_TARGET([ovs-vswitchd], [ovs-vswitchd.pid]) ++ ++dnl Check that geneve backing interface is still in the datapath. ++AT_CHECK([ip link show | grep " genev_sys_[[0-9]]*: .* ovs-system " | diff -u - geneve.0]) ++ ++dnl Remove the veth port from the database while ovs-vswitchd is down. ++AT_CHECK([ovs-vsctl --no-wait del-port ovs-veth0]) ++ ++dnl Check that it is still tied to the OVS datapath. ++AT_CHECK([ip link show ovs-veth0 | grep -q ovs-system]) ++ ++dnl Bring ovs-vswitchd back up. ++AT_CHECK([ovs-vswitchd --detach --no-chdir --pidfile --log-file -vdpif:dbg], ++ [0], [], [stderr]) ++ ++dnl Wait for the veth port to be removed from the datapath. ++OVS_WAIT_WHILE([ip link show ovs-veth0 | grep -q ovs-system]) ++ ++AT_CHECK([ovs-appctl dpctl/show | grep port], [0], [dnl ++ port 0: ovs-system (internal) ++ port 1: br0 (internal) ++ port 2: genev_sys_6081 (geneve: packet_type=ptap) ++]) ++ ++dnl Check that geneve backing interface is still in the datapath and it wasn't ++dnl re-created, i.e. the ifindex is the same. ++AT_CHECK([ip link show | grep " genev_sys_[[0-9]]*: .* ovs-system " | diff -u - geneve.0]) ++ ++OVS_TRAFFIC_VSWITCHD_STOP ++AT_CLEANUP diff --git a/tests/system-kmod-macros.at b/tests/system-kmod-macros.at index 86d633ac4f..f0aaae63eb 100644 --- a/tests/system-kmod-macros.at @@ -66147,7 +67063,7 @@ index 1714273e35..270956d13f 100644 dnl Delete ip address. AT_CHECK([ip addr del 10.0.0.17/24 dev p1-route], [0], [stdout]) diff --git a/tests/system-traffic.at b/tests/system-traffic.at -index f22d86e466..d4c34c1291 100644 +index f22d86e466..4a37641b2f 100644 --- a/tests/system-traffic.at +++ b/tests/system-traffic.at @@ -192,6 +192,46 @@ NS_CHECK_EXEC([at_ns0], [ping6 -s 3200 -q -c 3 -i 0.3 -w 2 fc00:1::2 | FORMAT_PI @@ -66694,7 +67610,7 @@ index f22d86e466..d4c34c1291 100644 OVS_TRAFFIC_VSWITCHD_STOP -@@ -1933,15 +2148,51 @@ dnl p1(at_ns1) interface +@@ -1933,14 +2148,50 @@ dnl p1(at_ns1) interface NS_CHECK_EXEC([at_ns0], [$PYTHON3 $srcdir/sendpkt.py p0 00 00 00 00 00 02 00 00 00 00 00 01 88 47 00 00 21 40 36 b1 ee 7c 01 02 36 b1 ee 7c 01 03 08 00 45 00 00 54 03 44 40 00 40 01 21 61 0a 01 01 01 0a 01 01 02 08 00 ef ac 7c e4 00 03 5b 2c 1f 61 00 00 00 00 50 0b 02 00 00 00 00 00 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 > /dev/null]) dnl Check the expected decapsulated on the egress interface @@ -66712,8 +67628,8 @@ index f22d86e466..d4c34c1291 100644 +OVS_WAIT_UNTIL([cat p1.pcap | grep -E "0x0040: *1617 *1819 *1a1b *1c1d *1e1f *2021 *2223 *2425" 2>&1 1>/dev/null]) +OVS_WAIT_UNTIL([cat p1.pcap | grep -E "0x0050: *2627 *2829 *2a2b *2c2d *2e2f *3031 *3233 *3435" 2>&1 1>/dev/null]) +OVS_WAIT_UNTIL([cat p1.pcap | grep -E "0x0060: *3637" 2>&1 1>/dev/null]) - - ++ ++ +OVS_TRAFFIC_VSWITCHD_STOP +AT_CLEANUP + @@ -66744,15 +67660,14 @@ index f22d86e466..d4c34c1291 100644 +dnl Wait for qdiscs to be applied. +OVS_WAIT_UNTIL([tc qdisc show dev ovs-p0 | grep -q htb]) +OVS_WAIT_UNTIL([tc qdisc show dev ovs-p1 | grep -q htb]) -+ + +dnl Check the configuration. +m4_define([HTB_CONF], [rate 2Mbit ceil 3Mbit burst 375000b cburst 375000b]) +AT_CHECK([tc class show dev ovs-p0 | grep -q 'class htb .* HTB_CONF']) +AT_CHECK([tc class show dev ovs-p1 | grep -q 'class htb .* HTB_CONF']) -+ + OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP - @@ -1985,9 +2236,9 @@ OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) dnl Check this output. We only see the latter two packets, not the first. AT_CHECK([cat ofctl_monitor.log], [0], [dnl @@ -66866,7 +67781,7 @@ index f22d86e466..d4c34c1291 100644 echo Request $i NS_CHECK_EXEC([at_ns1], [wget 10.1.1.64 -t 5 -T 1 --retry-connrefused -v -o wget$i.log]) done -@@ -6743,6 +7008,132 @@ AT_CHECK([ovs-ofctl dump-flows br0 | grep table=2, | OFPROTO_CLEAR_DURATION_IDLE +@@ -6743,6 +7008,241 @@ AT_CHECK([ovs-ofctl dump-flows br0 | grep table=2, | OFPROTO_CLEAR_DURATION_IDLE OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP @@ -66996,10 +67911,119 @@ index f22d86e466..d4c34c1291 100644 +OVS_TRAFFIC_VSWITCHD_STOP +AT_CLEANUP + ++AT_SETUP([conntrack - ICMP from different source related with NAT]) ++AT_SKIP_IF([test $HAVE_NC = no]) ++AT_SKIP_IF([test $HAVE_TCPDUMP = no]) ++CHECK_CONNTRACK() ++CHECK_CONNTRACK_NAT() ++OVS_TRAFFIC_VSWITCHD_START() ++ ++ADD_NAMESPACES(client, server) ++ ++ADD_VETH(client, client, br0, "192.168.20.10/24", "00:00:00:00:20:10") ++ADD_VETH(server, server, br0, "192.168.10.20/24", "00:00:00:00:10:20") ++ ++dnl Send traffic from client to CT, do DNAT if the traffic is new otherwise send it to server ++AT_DATA([flows.txt], [dnl ++table=0,ip,actions=ct(table=1,zone=42,nat) ++table=1,in_port=ovs-client,ip,ct_state=+trk+new,actions=ct(commit,table=2,zone=42,nat(dst=192.168.10.20) ++table=1,icmp,ct_state=+trk+rel-rpl,actions=ct(commit,table=2,zone=42,nat) ++table=1,ip,actions=resubmit(,2) ++table=2,in_port=ovs-client,ip,ct_state=+trk+new,actions=output:ovs-server ++table=2,in_port=ovs-client,icmp,ct_state=+trk+rel,actions=output:ovs-server ++table=2,in_port=ovs-server,icmp,ct_state=+trk+rel,actions=output:ovs-client ++table=2,in_port=ovs-server,ip,ct_state=+trk+rpl,actions=output:ovs-client ++]) ++ ++AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) ++ ++rm server.pcap ++tcpdump -l -U -i ovs-server -w server.pcap 2>tcpdump0_err & ++on_exit "kill $!" ++OVS_WAIT_UNTIL([grep "listening" tcpdump0_err]) ++ ++dnl Send UDP client->server ++AT_CHECK([ovs-ofctl packet-out br0 "in_port=ovs-client,\ ++packet=00000000102000000000201008004500001C000040000A11C762C0A8140AC0A814140001000200080000,actions=resubmit(,0)"]) ++dnl Send UDP response server->client ++AT_CHECK([ovs-ofctl packet-out br0 "in_port=ovs-server,\ ++packet=00000000201000000000102008004500001C000040000A11D162C0A80A14C0A8140A0002000100080000,actions=resubmit(,0)"]) ++dnl Fake router sending ICMP need frag router->server ++AT_CHECK([ovs-ofctl packet-out br0 "in_port=ovs-client,\ ++packet=000000001020000000002000080045000038011F0000FF011140C0A81401C0A814140304F778000005784500001C000040000A11C762C0A81414C0A8140A0002000100080000,\ ++actions=resubmit(,0)" ++]) ++ ++AT_CHECK([ovs-appctl revalidator/purge], [0]) ++AT_CHECK([ovs-ofctl -O OpenFlow15 dump-flows br0 | ofctl_strip | sort ], [0], [dnl ++ n_packets=3, n_bytes=154, reset_counts ip actions=ct(table=1,zone=42,nat) ++ table=1, n_packets=1, n_bytes=42, reset_counts ct_state=+new+trk,ip,in_port=1 actions=ct(commit,table=2,zone=42,nat(dst=192.168.10.20)) ++ table=1, n_packets=1, n_bytes=42, reset_counts ip actions=resubmit(,2) ++ table=1, n_packets=1, n_bytes=70, reset_counts ct_state=+rel-rpl+trk,icmp actions=ct(commit,table=2,zone=42,nat) ++ table=2, n_packets=1, n_bytes=42, reset_counts ct_state=+new+trk,ip,in_port=1 actions=output:2 ++ table=2, n_packets=1, n_bytes=42, reset_counts ct_state=+rpl+trk,ip,in_port=2 actions=output:1 ++ table=2, n_packets=1, n_bytes=70, reset_counts ct_state=+rel+trk,icmp,in_port=1 actions=output:2 ++ table=2, reset_counts ct_state=+rel+trk,icmp,in_port=2 actions=output:1 ++OFPST_FLOW reply (OF1.5): ++]) ++ ++AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "192.168.20.10"], [0], [dnl ++udp,orig=(src=192.168.20.10,dst=192.168.20.20,sport=1,dport=2),reply=(src=192.168.10.20,dst=192.168.20.10,sport=2,dport=1),zone=42 ++]) ++ ++OVS_WAIT_UNTIL([ovs-pcap server.pcap | grep 000000001020000000002000]) ++ ++AT_CHECK([ovs-pcap server.pcap | grep 000000001020000000002000], [0], [dnl ++000000001020000000002000080045000038011f0000ff011b40c0a81401c0a80a140304f778000005784500001c000040000a11d162c0a80a14c0a8140a0002000100080000 ++]) ++ ++dnl Check the ICMP error in reply direction ++AT_CHECK([ovs-appctl dpctl/flush-conntrack zone=42]) ++ ++rm client.pcap ++tcpdump -l -U -i ovs-client -w client.pcap 2>tcpdump1_err & ++on_exit "kill $!" ++OVS_WAIT_UNTIL([grep "listening" tcpdump1_err]) ++ ++dnl Send UDP client->server ++AT_CHECK([ovs-ofctl packet-out br0 "in_port=ovs-client,\ ++packet=00000000102000000000201008004500001C000040000A11C762C0A8140AC0A814140001000200080000,actions=resubmit(,0)"]) ++dnl Fake router sending ICMP need frag router->client ++AT_CHECK([ovs-ofctl packet-out br0 "in_port=ovs-server,\ ++packet=000000002010000000002000080045000038011F0000FF01114AC0A81401C0A8140A0304F778000005784500001C000040000A11D162C0A8140AC0A80A140001000200080000,\ ++actions=resubmit(,0)" ++]) ++ ++AT_CHECK([ovs-appctl revalidator/purge], [0]) ++AT_CHECK([ovs-ofctl -O OpenFlow15 dump-flows br0 | ofctl_strip | sort ], [0], [dnl ++ n_packets=5, n_bytes=266, reset_counts ip actions=ct(table=1,zone=42,nat) ++ table=1, n_packets=1, n_bytes=70, reset_counts ct_state=+rel-rpl+trk,icmp actions=ct(commit,table=2,zone=42,nat) ++ table=1, n_packets=2, n_bytes=112, reset_counts ip actions=resubmit(,2) ++ table=1, n_packets=2, n_bytes=84, reset_counts ct_state=+new+trk,ip,in_port=1 actions=ct(commit,table=2,zone=42,nat(dst=192.168.10.20)) ++ table=2, n_packets=1, n_bytes=42, reset_counts ct_state=+rpl+trk,ip,in_port=2 actions=output:1 ++ table=2, n_packets=1, n_bytes=70, reset_counts ct_state=+rel+trk,icmp,in_port=1 actions=output:2 ++ table=2, n_packets=1, n_bytes=70, reset_counts ct_state=+rel+trk,icmp,in_port=2 actions=output:1 ++ table=2, n_packets=2, n_bytes=84, reset_counts ct_state=+new+trk,ip,in_port=1 actions=output:2 ++OFPST_FLOW reply (OF1.5): ++]) ++ ++AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "192.168.20.10"], [0], [dnl ++udp,orig=(src=192.168.20.10,dst=192.168.20.20,sport=1,dport=2),reply=(src=192.168.10.20,dst=192.168.20.10,sport=2,dport=1),zone=42 ++]) ++ ++OVS_WAIT_UNTIL([ovs-pcap client.pcap | grep 000000002010000000002000]) ++ ++AT_CHECK([ovs-pcap client.pcap | grep 000000002010000000002000], [0], [dnl ++000000002010000000002000080045000038011f0000ff011137c0a81414c0a8140a0304f778000005784500001c000040000a11c762c0a8140ac0a814140001000200080000 ++]) ++ ++OVS_TRAFFIC_VSWITCHD_STOP ++AT_CLEANUP ++ AT_BANNER([802.1ad]) AT_SETUP([802.1ad - vlan_limit]) -@@ -7007,12 +7398,12 @@ dnl p1(at_ns1) interface +@@ -7007,12 +7507,12 @@ dnl p1(at_ns1) interface NS_CHECK_EXEC([at_ns0], [$PYTHON3 $srcdir/sendpkt.py p0 f2 00 00 00 00 02 f2 00 00 00 00 01 08 00 45 00 00 28 00 01 00 00 40 06 b0 13 c0 a8 00 0a 0a 00 00 0a 04 00 08 00 00 00 00 c8 00 00 00 00 50 02 20 00 b8 5e 00 00 > /dev/null]) dnl Check the expected nsh encapsulated packet on the egress interface @@ -67018,7 +68042,7 @@ index f22d86e466..d4c34c1291 100644 OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP -@@ -7039,10 +7430,10 @@ dnl p1(at_ns1) interface +@@ -7039,10 +7539,10 @@ dnl p1(at_ns1) interface NS_CHECK_EXEC([at_ns0], [$PYTHON3 $srcdir/sendpkt.py p0 f2 ff 00 00 00 02 f2 ff 00 00 00 01 89 4f 02 06 01 03 00 00 64 03 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 f2 00 00 00 00 02 f2 00 00 00 00 01 08 00 45 00 00 28 00 01 00 00 40 06 b0 13 c0 a8 00 0a 0a 00 00 0a 04 00 08 00 00 00 00 c8 00 00 00 00 50 02 20 00 b8 5e 00 00 > /dev/null]) dnl Check the expected de-capsulated TCP packet on the egress interface @@ -67033,7 +68057,7 @@ index f22d86e466..d4c34c1291 100644 OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP -@@ -7072,12 +7463,12 @@ dnl p1(at_ns1) interface +@@ -7072,12 +7572,12 @@ dnl p1(at_ns1) interface NS_CHECK_EXEC([at_ns0], [$PYTHON3 $srcdir/sendpkt.py p0 f2 ff 00 00 00 02 f2 ff 00 00 00 01 89 4f 02 06 01 03 00 01 00 03 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 f2 00 00 00 00 02 f2 00 00 00 00 01 08 00 45 00 00 28 00 01 00 00 40 06 b0 13 c0 a8 00 0a 0a 00 00 0a 04 00 08 00 00 00 00 c8 00 00 00 00 50 02 20 00 b8 5e 00 00 > /dev/null]) dnl Check the expected NSH packet with new fields in the header @@ -67052,7 +68076,7 @@ index f22d86e466..d4c34c1291 100644 OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP -@@ -7106,23 +7497,23 @@ dnl First send packet from at_ns0 --> OVS with SPI=0x100 and SI=2 +@@ -7106,23 +7606,23 @@ dnl First send packet from at_ns0 --> OVS with SPI=0x100 and SI=2 NS_CHECK_EXEC([at_ns0], [$PYTHON3 $srcdir/sendpkt.py p0 f2 ff 00 00 00 02 f2 ff 00 00 00 01 89 4f 02 06 01 03 00 01 00 02 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 f2 00 00 00 00 02 f2 00 00 00 00 01 08 00 45 00 00 28 00 01 00 00 40 06 b0 13 c0 a8 00 0a 0a 00 00 0a 04 00 08 00 00 00 00 c8 00 00 00 00 50 02 20 00 b8 5e 00 00 > /dev/null]) dnl Check for the above packet on p1 interface diff --git a/SPECS/openvswitch2.17.spec b/SPECS/openvswitch2.17.spec index da9a73b..8f789bb 100644 --- a/SPECS/openvswitch2.17.spec +++ b/SPECS/openvswitch2.17.spec @@ -63,7 +63,7 @@ Summary: Open vSwitch Group: System Environment/Daemons daemon/database/utilities URL: http://www.openvswitch.org/ Version: 2.17.0 -Release: 78%{?dist} +Release: 79%{?dist} # Nearly all of openvswitch is ASL 2.0. The bugtool is LGPLv2+, and the # lib/sflow*.[ch] files are SISSL @@ -749,6 +749,18 @@ exit 0 %endif %changelog +* Thu Mar 02 2023 Open vSwitch CI - 2.17.0-79 +- Merging upstream branch-2.17 [RH git: 3da76b1dd5] + Commit list: + 132fa24b65 classifier: Fix missing masks on a final stage with ports trie. + 8661abd4c4 ofproto: Fix re-creation of tunnel backing interfaces on restart. + 638441e981 ovs-actions: Correct typo in ovs-actions man page. + 3c4bd63bca ofproto-ipfix: Use per-domain template timeouts. + d2583ccb74 ofproto-dpif-upcall: Use last known stats ukey stats on revalidate missed dp flows. + 705190d88e conntrack: Properly unNAT inner header of related traffic. (#2137754) + d87b6180ec dpctl: Fix memory leak in flush conntrack. + + * Tue Feb 21 2023 Timothy Redaelli - 2.17.0-78 - redhat: add a workaround for meson [RH git: 39c6e2a48b] Currently, fast-datapath-rhel-8 is aligned to RHEL 8.0, with an