diff --git a/SOURCES/openvswitch-2.17.0.patch b/SOURCES/openvswitch-2.17.0.patch index 7720d72..9107b79 100644 --- a/SOURCES/openvswitch-2.17.0.patch +++ b/SOURCES/openvswitch-2.17.0.patch @@ -51131,10 +51131,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 +51469,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 +51478,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 +51497,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 +51524,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 +51539,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 +51551,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 +51580,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 +51645,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 +58371,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 +58550,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 +58559,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 +58568,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 +58611,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 +58620,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 +58629,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 +58645,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 +58667,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 +58689,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 +58702,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 +58711,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 +58720,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 +58777,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 +58885,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 +58918,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, ""); } @@ -66147,7 +66775,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 +67322,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 +67340,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 +67372,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 +67493,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 +67623,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 +67754,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 +67769,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 +67788,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 9e8720c..3d767cd 100644 --- a/SPECS/openvswitch2.17.spec +++ b/SPECS/openvswitch2.17.spec @@ -57,7 +57,7 @@ Summary: Open vSwitch Group: System Environment/Daemons daemon/database/utilities URL: http://www.openvswitch.org/ Version: 2.17.0 -Release: 65%{?dist} +Release: 66%{?dist} # Nearly all of openvswitch is ASL 2.0. The bugtool is LGPLv2+, and the # lib/sflow*.[ch] files are SISSL @@ -748,6 +748,15 @@ exit 0 %endif %changelog +* Tue Feb 21 2023 Open vSwitch CI - 2.17.0-66 +- Merging upstream branch-2.17 [RH git: 05aa9c16ae] + Commit list: + 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. + + * Mon Feb 13 2023 Open vSwitch CI - 2.17.0-65 - Merging upstream branch-2.17 [RH git: 2011158f64] Commit list: