From 5acaff165bea7f203c75dda8f9a6a2c42c505305 Mon Sep 17 00:00:00 2001 From: Open vSwitch CI Date: Apr 05 2023 12:52:19 +0000 Subject: Import openvswitch3.1-3.1.0-18 from Fast DataPath --- diff --git a/SOURCES/openvswitch-3.1.0.patch b/SOURCES/openvswitch-3.1.0.patch index e69de29..c77b11f 100644 --- a/SOURCES/openvswitch-3.1.0.patch +++ b/SOURCES/openvswitch-3.1.0.patch @@ -0,0 +1,2091 @@ +diff --git a/Documentation/ref/ovs-actions.7.rst b/Documentation/ref/ovs-actions.7.rst +index b59b7634f..d13895655 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/NEWS b/NEWS +index 37a01dea5..58a52120b 100644 +--- a/NEWS ++++ b/NEWS +@@ -1,3 +1,6 @@ ++v3.1.1 - xx xxx xxxx ++-------------------- ++ + v3.1.0 - 16 Feb 2023 + -------------------- + - ovs-vswitchd now detects changes in CPU affinity and adjusts the number +diff --git a/configure.ac b/configure.ac +index 9bf896c01..dea3b6f0b 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -13,7 +13,7 @@ + # limitations under the License. + + AC_PREREQ(2.63) +-AC_INIT(openvswitch, 3.1.0, bugs@openvswitch.org) ++AC_INIT(openvswitch, 3.1.1, bugs@openvswitch.org) + AC_CONFIG_SRCDIR([vswitchd/ovs-vswitchd.c]) + AC_CONFIG_MACRO_DIR([m4]) + AC_CONFIG_AUX_DIR([build-aux]) +diff --git a/debian/changelog b/debian/changelog +index a5ad222c4..83cc8e010 100644 +--- a/debian/changelog ++++ b/debian/changelog +@@ -1,3 +1,9 @@ ++openvswitch (3.1.1-1) unstable; urgency=low ++ [ Open vSwitch team ] ++ * New upstream version ++ ++ -- Open vSwitch team Thu, 16 Feb 2023 13:52:24 +0100 ++ + openvswitch (3.1.0-1) unstable; urgency=low + + * New upstream version +diff --git a/include/openvswitch/meta-flow.h b/include/openvswitch/meta-flow.h +index 045dce8f5..3b0220aaa 100644 +--- a/include/openvswitch/meta-flow.h ++++ b/include/openvswitch/meta-flow.h +@@ -2366,6 +2366,10 @@ void mf_format_subvalue(const union mf_subvalue *subvalue, struct ds *s); + void field_array_set(enum mf_field_id id, const union mf_value *, + struct field_array *); + ++/* Mask the required l3 prerequisites if a 'set' action occurs. */ ++void mf_set_mask_l3_prereqs(const struct mf_field *, const struct flow *, ++ struct flow_wildcards *); ++ + #ifdef __cplusplus + } + #endif +diff --git a/lib/classifier.c b/lib/classifier.c +index 0a89626cc..18dbfc83a 100644 +--- a/lib/classifier.c ++++ b/lib/classifier.c +@@ -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/conntrack-tp.h b/lib/conntrack-tp.h +index 4d411d19f..7ece2eae2 100644 +--- a/lib/conntrack-tp.h ++++ b/lib/conntrack-tp.h +@@ -17,8 +17,15 @@ + #ifndef CONNTRACK_TP_H + #define CONNTRACK_TP_H 1 + ++#include ++ + #define CT_DPIF_NETDEV_TP_MIN 30 ++ + enum ct_timeout; ++struct conn; ++struct conntrack; ++struct timeout_policy; ++ + void timeout_policy_init(struct conntrack *ct); + int timeout_policy_update(struct conntrack *ct, struct timeout_policy *tp); + int timeout_policy_delete(struct conntrack *ct, uint32_t tp_id); +diff --git a/lib/conntrack.c b/lib/conntrack.c +index 524670e45..8cf7779c6 100644 +--- a/lib/conntrack.c ++++ b/lib/conntrack.c +@@ -1512,12 +1512,12 @@ conntrack_clean(struct conntrack *ct, long long now) + clean_end = n_conn_limit / 64; + + for (i = ct->next_sweep; i < N_EXP_LISTS; i++) { +- count += ct_sweep(ct, &ct->exp_lists[i], now); +- + if (count > clean_end) { + next_wakeup = 0; + break; + } ++ ++ count += ct_sweep(ct, &ct->exp_lists[i], now); + } + + ct->next_sweep = (i < N_EXP_LISTS) ? i : 0; +diff --git a/lib/db-ctl-base.c b/lib/db-ctl-base.c +index 134496ef3..5d2635946 100644 +--- a/lib/db-ctl-base.c ++++ b/lib/db-ctl-base.c +@@ -1492,7 +1492,7 @@ cmd_add(struct ctl_context *ctx) + const struct ovsdb_idl_column *column; + const struct ovsdb_idl_row *row; + const struct ovsdb_type *type; +- struct ovsdb_datum new; ++ struct ovsdb_datum old; + int i; + + ctx->error = get_table(table_name, &table); +@@ -1516,13 +1516,7 @@ cmd_add(struct ctl_context *ctx) + } + + type = &column->type; +- +- if (ctx->last_command) { +- ovsdb_datum_init_empty(&new); +- } else { +- ovsdb_datum_clone(&new, ovsdb_idl_read(row, column)); +- } +- ++ ovsdb_datum_clone(&old, ovsdb_idl_read(row, column)); + for (i = 4; i < ctx->argc; i++) { + struct ovsdb_type add_type; + struct ovsdb_datum add; +@@ -1533,41 +1527,23 @@ cmd_add(struct ctl_context *ctx) + ctx->error = ovsdb_datum_from_string(&add, &add_type, ctx->argv[i], + ctx->symtab); + if (ctx->error) { +- ovsdb_datum_destroy(&new, &column->type); ++ ovsdb_datum_destroy(&old, &column->type); + return; + } +- ovsdb_datum_union(&new, &add, type); ++ ovsdb_datum_union(&old, &add, type); + ovsdb_datum_destroy(&add, type); + } +- +- if (!ctx->last_command && new.n > type->n_max) { ++ if (old.n > type->n_max) { + ctl_error(ctx, "\"add\" operation would put %u %s in column %s of " + "table %s but the maximum number is %u", +- new.n, ++ old.n, + type->value.type == OVSDB_TYPE_VOID ? "values" : "pairs", + column->name, table->name, type->n_max); +- ovsdb_datum_destroy(&new, &column->type); ++ ovsdb_datum_destroy(&old, &column->type); + return; + } +- +- if (ctx->last_command) { +- /* Partial updates can only be made one by one. */ +- for (i = 0; i < new.n; i++) { +- struct ovsdb_datum *datum = xmalloc(sizeof *datum); +- +- ovsdb_datum_init_empty(datum); +- ovsdb_datum_add_from_index_unsafe(datum, &new, i, type); +- if (ovsdb_type_is_map(type)) { +- ovsdb_idl_txn_write_partial_map(row, column, datum); +- } else { +- ovsdb_idl_txn_write_partial_set(row, column, datum); +- } +- } +- ovsdb_datum_destroy(&new, &column->type); +- } else { +- ovsdb_idl_txn_verify(row, column); +- ovsdb_idl_txn_write(row, column, &new); +- } ++ ovsdb_idl_txn_verify(row, column); ++ ovsdb_idl_txn_write(row, column, &old); + + invalidate_cache(ctx); + } +diff --git a/lib/dpctl.c b/lib/dpctl.c +index d12d9b8a5..970373389 100644 +--- a/lib/dpctl.c ++++ b/lib/dpctl.c +@@ -1713,10 +1713,16 @@ dpctl_flush_conntrack(int argc, const char *argv[], + uint16_t zone, *pzone = NULL; + int error; + int args = argc - 1; ++ int zone_pos = 1; ++ ++ if (dp_arg_exists(argc, argv)) { ++ args--; ++ zone_pos = 2; ++ } + + /* Parse zone. */ +- if (args && !strncmp(argv[1], "zone=", 5)) { +- if (!ovs_scan(argv[1], "zone=%"SCNu16, &zone)) { ++ if (args && !strncmp(argv[zone_pos], "zone=", 5)) { ++ if (!ovs_scan(argv[zone_pos], "zone=%"SCNu16, &zone)) { + ds_put_cstr(&ds, "failed to parse zone"); + error = EINVAL; + goto error; +@@ -1744,7 +1750,7 @@ dpctl_flush_conntrack(int argc, const char *argv[], + } + + /* Report error if there is more than one unparsed argument. */ +- if (args > 1) { ++ if (args > 0) { + ds_put_cstr(&ds, "invalid arguments"); + error = EINVAL; + goto error; +diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c +index c9f7179c3..aed2c8fbb 100644 +--- a/lib/dpif-netdev.c ++++ b/lib/dpif-netdev.c +@@ -9616,6 +9616,7 @@ dpif_netdev_bond_stats_get(struct dpif *dpif, uint32_t bond_id, + const struct dpif_class dpif_netdev_class = { + "netdev", + true, /* cleanup_required */ ++ true, /* synced_dp_layers */ + dpif_netdev_init, + dpif_netdev_enumerate, + dpif_netdev_port_open_type, +diff --git a/lib/dpif-netlink.c b/lib/dpif-netlink.c +index 026b0daa8..2f829f589 100644 +--- a/lib/dpif-netlink.c ++++ b/lib/dpif-netlink.c +@@ -2582,7 +2582,7 @@ dpif_netlink_calculate_n_handlers(void) + n_handlers = MIN(next_prime_num, total_cores); + } + +- return n_handlers; ++ return MAX(n_handlers, 1); + } + + static int +@@ -4515,6 +4515,7 @@ dpif_netlink_cache_set_size(struct dpif *dpif_, uint32_t level, uint32_t size) + const struct dpif_class dpif_netlink_class = { + "system", + false, /* cleanup_required */ ++ false, /* synced_dp_layers */ + NULL, /* init */ + dpif_netlink_enumerate, + NULL, +diff --git a/lib/dpif-provider.h b/lib/dpif-provider.h +index 12477a24f..b8ead8a02 100644 +--- a/lib/dpif-provider.h ++++ b/lib/dpif-provider.h +@@ -127,6 +127,14 @@ struct dpif_class { + * datapaths that can not exist without it (e.g. netdev datapath). */ + bool cleanup_required; + ++ /* If 'true' the specific dpif implementation synchronizes the various ++ * datapath implementation layers, i.e., the dpif's layer in combination ++ * with the underlying netdev offload layers. For example, dpif-netlink ++ * does not sync its kernel flows with the tc ones, i.e., only one gets ++ * installed. On the other hand, dpif-netdev installs both flows, ++ * internally keeps track of both, and represents them as one. */ ++ bool synced_dp_layers; ++ + /* Called when the dpif provider is registered, typically at program + * startup. Returning an error from this function will prevent any + * datapath with this class from being created. +diff --git a/lib/dpif.c b/lib/dpif.c +index fe4db83fb..3305401fe 100644 +--- a/lib/dpif.c ++++ b/lib/dpif.c +@@ -2109,3 +2109,9 @@ dpif_cache_set_size(struct dpif *dpif, uint32_t level, uint32_t size) + ? dpif->dpif_class->cache_set_size(dpif, level, size) + : EOPNOTSUPP; + } ++ ++bool ++dpif_synced_dp_layers(struct dpif *dpif) ++{ ++ return dpif->dpif_class->synced_dp_layers; ++} +diff --git a/lib/dpif.h b/lib/dpif.h +index 6cb4dae6d..129cbf6a1 100644 +--- a/lib/dpif.h ++++ b/lib/dpif.h +@@ -939,6 +939,7 @@ int dpif_get_pmds_for_port(const struct dpif * dpif, odp_port_t port_no, + char *dpif_get_dp_version(const struct dpif *); + bool dpif_supports_tnl_push_pop(const struct dpif *); + bool dpif_supports_explicit_drop_action(const struct dpif *); ++bool dpif_synced_dp_layers(struct dpif *); + + /* Log functions. */ + struct vlog_module; +diff --git a/lib/meta-flow.c b/lib/meta-flow.c +index c576ae620..474344194 100644 +--- a/lib/meta-flow.c ++++ b/lib/meta-flow.c +@@ -3676,3 +3676,28 @@ mf_bitmap_not(struct mf_bitmap x) + bitmap_not(x.bm, MFF_N_IDS); + return x; + } ++ ++void ++mf_set_mask_l3_prereqs(const struct mf_field *mf, const struct flow *fl, ++ struct flow_wildcards *wc) ++{ ++ if (is_ip_any(fl) && ++ ((mf->id == MFF_IPV4_SRC) || ++ (mf->id == MFF_IPV4_DST) || ++ (mf->id == MFF_IPV6_SRC) || ++ (mf->id == MFF_IPV6_DST) || ++ (mf->id == MFF_IPV6_LABEL) || ++ (mf->id == MFF_IP_DSCP) || ++ (mf->id == MFF_IP_ECN) || ++ (mf->id == MFF_IP_TTL))) { ++ WC_MASK_FIELD(wc, nw_proto); ++ } else if ((fl->dl_type == htons(ETH_TYPE_ARP)) && ++ ((mf->id == MFF_ARP_OP) || ++ (mf->id == MFF_ARP_SHA) || ++ (mf->id == MFF_ARP_THA) || ++ (mf->id == MFF_ARP_SPA) || ++ (mf->id == MFF_ARP_TPA))) { ++ /* mask only the lower 8 bits. */ ++ wc->masks.nw_proto = 0xff; ++ } ++} +diff --git a/lib/netdev-offload-tc.c b/lib/netdev-offload-tc.c +index 4c78c4816..c38247423 100644 +--- a/lib/netdev-offload-tc.c ++++ b/lib/netdev-offload-tc.c +@@ -276,8 +276,9 @@ del_filter_and_ufid_mapping(struct tcf_id *id, const ovs_u128 *ufid, + } + + err = tc_del_flower_filter(id); +- if (!err) { ++ if (!err || err == ENODEV) { + del_ufid_tc_mapping(ufid); ++ return 0; + } + return err; + } +@@ -871,7 +872,7 @@ parse_tc_flower_to_actions__(struct tc_flower *flower, struct ofpbuf *buf, + outport = + netdev_ifindex_to_odp_port(action->out.ifindex_out); + if (!outport) { +- return ENOENT; ++ return -ENOENT; + } + } + nl_msg_put_u32(buf, OVS_ACTION_ATTR_OUTPUT, odp_to_u32(outport)); +@@ -964,7 +965,7 @@ parse_tc_flower_to_actions__(struct tc_flower *flower, struct ofpbuf *buf, + uint32_t meter_id; + + if (police_idx_lookup(action->police.index, &meter_id)) { +- return ENOENT; ++ return -ENOENT; + } + nl_msg_put_u32(buf, OVS_ACTION_ATTR_METER, meter_id); + } +@@ -983,6 +984,9 @@ parse_tc_flower_to_actions__(struct tc_flower *flower, struct ofpbuf *buf, + buf, OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER); + i = parse_tc_flower_to_actions__(flower, buf, i + 1, + action->police.result_jump); ++ if (i < 0) { ++ return i; ++ } + nl_msg_end_nested(buf, act_offset); + + act_offset = nl_msg_start_nested( +@@ -994,6 +998,9 @@ parse_tc_flower_to_actions__(struct tc_flower *flower, struct ofpbuf *buf, + } + if (jump != 0) { + i = parse_tc_flower_to_actions__(flower, buf, i, jump); ++ if (i < 0) { ++ return i; ++ } + } + nl_msg_end_nested(buf, act_offset); + +@@ -1013,11 +1020,11 @@ parse_tc_flower_to_actions__(struct tc_flower *flower, struct ofpbuf *buf, + return i; + } + +-static void ++static int + parse_tc_flower_to_actions(struct tc_flower *flower, + struct ofpbuf *buf) + { +- parse_tc_flower_to_actions__(flower, buf, 0, 0); ++ return parse_tc_flower_to_actions__(flower, buf, 0, 0); + } + + static int +@@ -1030,9 +1037,10 @@ parse_tc_flower_to_match(const struct netdev *netdev, + struct ofpbuf *buf, + bool terse) + { +- size_t act_off; + struct tc_flower_key *key = &flower->key; + struct tc_flower_key *mask = &flower->mask; ++ size_t act_off; ++ int err; + + if (terse) { + return parse_tc_flower_terse_to_match(flower, match, stats, attrs); +@@ -1229,7 +1237,10 @@ parse_tc_flower_to_match(const struct netdev *netdev, + } + + act_off = nl_msg_start_nested(buf, OVS_FLOW_ATTR_ACTIONS); +- parse_tc_flower_to_actions(flower, buf); ++ err = parse_tc_flower_to_actions(flower, buf); ++ if (err < 0) { ++ return -err; ++ } + nl_msg_end_nested(buf, act_off); + + *actions = ofpbuf_at_assert(buf, act_off, sizeof(struct nlattr)); +@@ -1272,8 +1283,8 @@ netdev_tc_flow_dump_next(struct netdev_flow_dump *dump, + continue; + } + +- if (flower.act_cookie.len) { +- *ufid = *((ovs_u128 *) flower.act_cookie.data); ++ if (flower.act_cookie.len >= sizeof *ufid) { ++ *ufid = get_32aligned_u128(flower.act_cookie.data); + } else if (!find_ufid(netdev, &id, ufid)) { + continue; + } +@@ -2490,15 +2501,23 @@ netdev_tc_flow_get(struct netdev *netdev, + + err = tc_get_flower(&id, &flower); + if (err) { +- VLOG_ERR_RL(&error_rl, "flow get failed (dev %s prio %d handle %d): %s", ++ VLOG_ERR_RL(&error_rl, ++ "flow get failed (dev %s prio %d handle %d): %s", + netdev_get_name(netdev), id.prio, id.handle, + ovs_strerror(err)); + return err; + } + + in_port = netdev_ifindex_to_odp_port(id.ifindex); +- parse_tc_flower_to_match(netdev, &flower, match, actions, +- stats, attrs, buf, false); ++ err = parse_tc_flower_to_match(netdev, &flower, match, actions, ++ stats, attrs, buf, false); ++ if (err) { ++ VLOG_ERR_RL(&error_rl, ++ "flow get parse failed (dev %s prio %d handle %d): %s", ++ netdev_get_name(netdev), id.prio, id.handle, ++ ovs_strerror(err)); ++ return err; ++ } + + if (stats) { + struct dpif_flow_stats adjust_stats; +diff --git a/lib/netdev-windows.c b/lib/netdev-windows.c +index 4ad45ffa1..3fad501e3 100644 +--- a/lib/netdev-windows.c ++++ b/lib/netdev-windows.c +@@ -156,6 +156,7 @@ netdev_windows_system_construct(struct netdev *netdev_) + struct netdev_windows_netdev_info info; + struct ofpbuf *buf; + int ret; ++ const char *type = NULL; + + /* Query the attributes and runtime status of the netdev. */ + ret = query_netdev(netdev_get_name(&netdev->up), &info, &buf); +@@ -167,6 +168,16 @@ netdev_windows_system_construct(struct netdev *netdev_) + } + ofpbuf_delete(buf); + ++ /* Don't create netdev if ovs-type is "internal" ++ * but the type of netdev->up is "system". */ ++ type = netdev_get_type(&netdev->up); ++ if (type && !strcmp(type, "system") && ++ (info.ovs_type == OVS_VPORT_TYPE_INTERNAL)) { ++ VLOG_DBG("construct device %s, ovs_type: %u failed", ++ netdev_get_name(&netdev->up), info.ovs_type); ++ return 1; ++ } ++ + netdev->change_seq = 1; + netdev->dev_type = info.ovs_type; + netdev->port_no = info.port_no; +diff --git a/lib/ovs-thread.c b/lib/ovs-thread.c +index 2d382f1e8..ac5d2c3d0 100644 +--- a/lib/ovs-thread.c ++++ b/lib/ovs-thread.c +@@ -674,7 +674,7 @@ count_cpu_cores(void) + static int cpu_cores; + + ovs_mutex_lock(&cpu_cores_mutex); +- if (now - last_updated >= COUNT_CPU_UPDATE_TIME_MS) { ++ if (!last_updated || now - last_updated >= COUNT_CPU_UPDATE_TIME_MS) { + last_updated = now; + cpu_cores = count_cpu_cores__(); + } +diff --git a/ofproto/ofproto-dpif-ipfix.c b/ofproto/ofproto-dpif-ipfix.c +index 742eed399..f13478a88 100644 +--- a/ofproto/ofproto-dpif-ipfix.c ++++ b/ofproto/ofproto-dpif-ipfix.c +@@ -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); +@@ -927,7 +979,7 @@ static void + dpif_ipfix_bridge_exporter_set_options( + struct dpif_ipfix_bridge_exporter *exporter, + const struct ofproto_ipfix_bridge_exporter_options *options, +- bool *options_changed) ++ bool *options_changed) OVS_REQUIRES(mutex) + { + if (!options || sset_is_empty(&options->targets)) { + /* No point in doing any work if there are no targets. */ +@@ -1003,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); +@@ -1011,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); +@@ -1020,7 +1074,7 @@ static bool + dpif_ipfix_flow_exporter_set_options( + struct dpif_ipfix_flow_exporter *exporter, + const struct ofproto_ipfix_flow_exporter_options *options, +- bool *options_changed) ++ bool *options_changed) OVS_REQUIRES(mutex) + { + if (sset_is_empty(&options->targets)) { + /* No point in doing any work if there are no targets. */ +@@ -1071,6 +1125,7 @@ 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); +@@ -2000,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; +@@ -2811,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) OVS_REQUIRES(mutex) + { + struct ipfix_flow_cache_entry *entry; + uint64_t max_flow_start_timestamp_usec; +- bool template_msg_sent = false; + enum ipfix_flow_end_reason flow_end_reason; + + if (ovs_list_is_empty(&exporter->cache_flow_start_timestamp_list)) { +@@ -2844,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); +@@ -2883,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-upcall.c b/ofproto/ofproto-dpif-upcall.c +index e05ffe312..89df92242 100644 +--- a/ofproto/ofproto-dpif-upcall.c ++++ b/ofproto/ofproto-dpif-upcall.c +@@ -47,17 +47,20 @@ + + #define UPCALL_MAX_BATCH 64 + #define REVALIDATE_MAX_BATCH 50 ++#define UINT64_THREE_QUARTERS (UINT64_MAX / 4 * 3) + + VLOG_DEFINE_THIS_MODULE(ofproto_dpif_upcall); + + COVERAGE_DEFINE(dumped_duplicate_flow); + COVERAGE_DEFINE(dumped_new_flow); + COVERAGE_DEFINE(handler_duplicate_upcall); +-COVERAGE_DEFINE(upcall_ukey_contention); +-COVERAGE_DEFINE(upcall_ukey_replace); + COVERAGE_DEFINE(revalidate_missed_dp_flow); ++COVERAGE_DEFINE(ukey_dp_change); ++COVERAGE_DEFINE(ukey_invalid_stat_reset); + COVERAGE_DEFINE(upcall_flow_limit_hit); + COVERAGE_DEFINE(upcall_flow_limit_kill); ++COVERAGE_DEFINE(upcall_ukey_contention); ++COVERAGE_DEFINE(upcall_ukey_replace); + + /* A thread that reads upcalls from dpif, forwards each upcall's packet, + * and possibly sets up a kernel flow as a cache. */ +@@ -287,6 +290,7 @@ struct udpif_key { + + struct ovs_mutex mutex; /* Guards the following. */ + struct dpif_flow_stats stats OVS_GUARDED; /* Last known stats.*/ ++ const char *dp_layer OVS_GUARDED; /* Last known dp_layer. */ + long long int created OVS_GUARDED; /* Estimate of creation time. */ + uint64_t dump_seq OVS_GUARDED; /* Tracks udpif->dump_seq. */ + uint64_t reval_seq OVS_GUARDED; /* Tracks udpif->reval_seq. */ +@@ -780,6 +784,17 @@ udpif_get_n_flows(struct udpif *udpif) + atomic_store_relaxed(&udpif->n_flows_timestamp, now); + dpif_get_dp_stats(udpif->dpif, &stats); + flow_count = stats.n_flows; ++ ++ if (!dpif_synced_dp_layers(udpif->dpif)) { ++ /* If the dpif layer does not sync the flows, we need to include ++ * the hardware offloaded flows separately. */ ++ uint64_t hw_flows; ++ ++ if (!dpif_get_n_offloaded_flows(udpif->dpif, &hw_flows)) { ++ flow_count += hw_flows; ++ } ++ } ++ + atomic_store_relaxed(&udpif->n_flows, flow_count); + ovs_mutex_unlock(&udpif->n_flows_mutex); + } else { +@@ -1766,6 +1781,7 @@ ukey_create__(const struct nlattr *key, size_t key_len, + ukey->created = ukey->flow_time = time_msec(); + memset(&ukey->stats, 0, sizeof ukey->stats); + ukey->stats.used = used; ++ ukey->dp_layer = NULL; + ukey->xcache = NULL; + + ukey->offloaded = false; +@@ -2095,10 +2111,12 @@ ukey_delete(struct umap *umap, struct udpif_key *ukey) + } + + static bool +-should_revalidate(const struct udpif *udpif, uint64_t packets, +- long long int used) ++should_revalidate(const struct udpif *udpif, const struct udpif_key *ukey, ++ uint64_t packets) ++ OVS_REQUIRES(ukey->mutex) + { + long long int metric, now, duration; ++ long long int used = ukey->stats.used; + + if (!used) { + /* Always revalidate the first time a flow is dumped. */ +@@ -2125,8 +2143,12 @@ should_revalidate(const struct udpif *udpif, uint64_t packets, + duration = now - used; + metric = duration / packets; + +- if (metric < 1000 / ofproto_min_revalidate_pps) { +- /* The flow is receiving more than min-revalidate-pps, so keep it. */ ++ if (metric < 1000 / ofproto_min_revalidate_pps || ++ (ukey->offloaded && duration < ofproto_offloaded_stats_delay)) { ++ /* The flow is receiving more than min-revalidate-pps, so keep it. ++ * Or it's a hardware offloaded flow that might take up to X seconds ++ * to update its statistics. Until we are sure the statistics had a ++ * chance to be updated, also keep it. */ + return true; + } + return false; +@@ -2324,7 +2346,7 @@ static enum reval_result + revalidate_ukey(struct udpif *udpif, struct udpif_key *ukey, + const struct dpif_flow_stats *stats, + struct ofpbuf *odp_actions, uint64_t reval_seq, +- struct recirc_refs *recircs, bool offloaded) ++ struct recirc_refs *recircs) + OVS_REQUIRES(ukey->mutex) + { + bool need_revalidate = ukey->reval_seq != reval_seq; +@@ -2342,8 +2364,15 @@ revalidate_ukey(struct udpif *udpif, struct udpif_key *ukey, + ? stats->n_bytes - ukey->stats.n_bytes + : 0); + ++ if (stats->n_packets < ukey->stats.n_packets && ++ ukey->stats.n_packets < UINT64_THREE_QUARTERS) { ++ /* Report cases where the packet counter is lower than the previous ++ * instance, but exclude the potential wrapping of an uint64_t. */ ++ COVERAGE_INC(ukey_invalid_stat_reset); ++ } ++ + if (need_revalidate) { +- if (should_revalidate(udpif, push.n_packets, ukey->stats.used)) { ++ if (should_revalidate(udpif, ukey, push.n_packets)) { + if (!ukey->xcache) { + ukey->xcache = xlate_cache_new(); + } else { +@@ -2359,7 +2388,7 @@ revalidate_ukey(struct udpif *udpif, struct udpif_key *ukey, + + /* Stats for deleted flows will be attributed upon flow deletion. Skip. */ + if (result != UKEY_DELETE) { +- xlate_push_stats(ukey->xcache, &push, offloaded); ++ xlate_push_stats(ukey->xcache, &push, ukey->offloaded); + ukey->stats = *stats; + ukey->reval_seq = reval_seq; + } +@@ -2455,6 +2484,15 @@ push_dp_ops(struct udpif *udpif, struct ukey_op *ops, size_t n_ops) + push->tcp_flags = stats->tcp_flags | op->ukey->stats.tcp_flags; + push->n_packets = stats->n_packets - op->ukey->stats.n_packets; + push->n_bytes = stats->n_bytes - op->ukey->stats.n_bytes; ++ ++ if (stats->n_packets < op->ukey->stats.n_packets && ++ op->ukey->stats.n_packets < UINT64_THREE_QUARTERS) { ++ /* Report cases where the packet counter is lower than the ++ * previous instance, but exclude the potential wrapping of an ++ * uint64_t. */ ++ COVERAGE_INC(ukey_invalid_stat_reset); ++ } ++ + ovs_mutex_unlock(&op->ukey->mutex); + } else { + push = stats; +@@ -2759,6 +2797,22 @@ revalidate(struct revalidator *revalidator) + continue; + } + ++ ukey->offloaded = f->attrs.offloaded; ++ if (!ukey->dp_layer ++ || (!dpif_synced_dp_layers(udpif->dpif) ++ && strcmp(ukey->dp_layer, f->attrs.dp_layer))) { ++ ++ if (ukey->dp_layer) { ++ /* The dp_layer has changed this is probably due to an ++ * earlier revalidate cycle moving it to/from hw offload. ++ * In this case we should reset the ukey stored statistics, ++ * as they are from the deleted DP flow. */ ++ COVERAGE_INC(ukey_dp_change); ++ memset(&ukey->stats, 0, sizeof ukey->stats); ++ } ++ ukey->dp_layer = f->attrs.dp_layer; ++ } ++ + already_dumped = ukey->dump_seq == dump_seq; + if (already_dumped) { + /* The flow has already been handled during this flow dump +@@ -2790,8 +2844,7 @@ revalidate(struct revalidator *revalidator) + result = UKEY_DELETE; + } else { + result = revalidate_ukey(udpif, ukey, &stats, &odp_actions, +- reval_seq, &recircs, +- f->attrs.offloaded); ++ reval_seq, &recircs); + } + ukey->dump_seq = dump_seq; + +@@ -2876,7 +2929,7 @@ revalidator_sweep__(struct revalidator *revalidator, bool purge) + COVERAGE_INC(revalidate_missed_dp_flow); + memcpy(&stats, &ukey->stats, sizeof stats); + result = revalidate_ukey(udpif, ukey, &stats, &odp_actions, +- reval_seq, &recircs, false); ++ reval_seq, &recircs); + } + if (result != UKEY_KEEP) { + /* Clears 'recircs' if filled by revalidate_ukey(). */ +diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c +index a9cf3cbee..cffd733c5 100644 +--- a/ofproto/ofproto-dpif-xlate.c ++++ b/ofproto/ofproto-dpif-xlate.c +@@ -5211,6 +5211,7 @@ compose_dec_ttl(struct xlate_ctx *ctx, struct ofpact_cnt_ids *ids) + } + + ctx->wc->masks.nw_ttl = 0xff; ++ WC_MASK_FIELD(ctx->wc, nw_proto); + if (flow->nw_ttl > 1) { + flow->nw_ttl--; + return false; +@@ -7128,6 +7129,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len, + case OFPACT_SET_IPV4_SRC: + if (flow->dl_type == htons(ETH_TYPE_IP)) { + memset(&wc->masks.nw_src, 0xff, sizeof wc->masks.nw_src); ++ WC_MASK_FIELD(wc, nw_proto); + flow->nw_src = ofpact_get_SET_IPV4_SRC(a)->ipv4; + } + break; +@@ -7135,12 +7137,14 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len, + case OFPACT_SET_IPV4_DST: + if (flow->dl_type == htons(ETH_TYPE_IP)) { + memset(&wc->masks.nw_dst, 0xff, sizeof wc->masks.nw_dst); ++ WC_MASK_FIELD(wc, nw_proto); + flow->nw_dst = ofpact_get_SET_IPV4_DST(a)->ipv4; + } + break; + + case OFPACT_SET_IP_DSCP: + if (is_ip_any(flow)) { ++ WC_MASK_FIELD(wc, nw_proto); + wc->masks.nw_tos |= IP_DSCP_MASK; + flow->nw_tos &= ~IP_DSCP_MASK; + flow->nw_tos |= ofpact_get_SET_IP_DSCP(a)->dscp; +@@ -7149,6 +7153,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len, + + case OFPACT_SET_IP_ECN: + if (is_ip_any(flow)) { ++ WC_MASK_FIELD(wc, nw_proto); + wc->masks.nw_tos |= IP_ECN_MASK; + flow->nw_tos &= ~IP_ECN_MASK; + flow->nw_tos |= ofpact_get_SET_IP_ECN(a)->ecn; +@@ -7157,6 +7162,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len, + + case OFPACT_SET_IP_TTL: + if (is_ip_any(flow)) { ++ WC_MASK_FIELD(wc, nw_proto); + wc->masks.nw_ttl = 0xff; + flow->nw_ttl = ofpact_get_SET_IP_TTL(a)->ttl; + } +@@ -7224,6 +7230,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len, + + /* Set the field only if the packet actually has it. */ + if (mf_are_prereqs_ok(mf, flow, wc)) { ++ mf_set_mask_l3_prereqs(mf, flow, wc); + mf_mask_field_masked(mf, ofpact_set_field_mask(set_field), wc); + mf_set_flow_value_masked(mf, set_field->value, + ofpact_set_field_mask(set_field), +@@ -7280,6 +7287,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len, + + case OFPACT_DEC_TTL: + wc->masks.nw_ttl = 0xff; ++ WC_MASK_FIELD(wc, nw_proto); + if (compose_dec_ttl(ctx, ofpact_get_DEC_TTL(a))) { + return; + } +diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c +index f87e27a8c..fad7342b0 100644 +--- a/ofproto/ofproto-dpif.c ++++ b/ofproto/ofproto-dpif.c +@@ -714,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 +@@ -729,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; +@@ -792,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); +diff --git a/ofproto/ofproto-provider.h b/ofproto/ofproto-provider.h +index a84ddc1d0..143ded690 100644 +--- a/ofproto/ofproto-provider.h ++++ b/ofproto/ofproto-provider.h +@@ -541,6 +541,11 @@ extern unsigned ofproto_max_revalidator; + * duration exceeds half of max-revalidator config variable. */ + extern unsigned ofproto_min_revalidate_pps; + ++/* Worst case delay (in ms) it might take before statistics of offloaded flows ++ * are updated. Offloaded flows younger than this delay will always be ++ * revalidated regardless of ofproto_min_revalidate_pps. */ ++extern unsigned ofproto_offloaded_stats_delay; ++ + /* Number of upcall handler and revalidator threads. Only affects the + * ofproto-dpif implementation. */ + extern uint32_t n_handlers, n_revalidators; +diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c +index 17f636ed9..5fc1a7409 100644 +--- a/ofproto/ofproto.c ++++ b/ofproto/ofproto.c +@@ -311,6 +311,7 @@ unsigned ofproto_flow_limit = OFPROTO_FLOW_LIMIT_DEFAULT; + unsigned ofproto_max_idle = OFPROTO_MAX_IDLE_DEFAULT; + unsigned ofproto_max_revalidator = OFPROTO_MAX_REVALIDATOR_DEFAULT; + unsigned ofproto_min_revalidate_pps = OFPROTO_MIN_REVALIDATE_PPS_DEFAULT; ++unsigned ofproto_offloaded_stats_delay = OFPROTO_OFFLOADED_STATS_DELAY; + + uint32_t n_handlers, n_revalidators; + +@@ -727,6 +728,15 @@ ofproto_set_min_revalidate_pps(unsigned min_revalidate_pps) + ofproto_min_revalidate_pps = min_revalidate_pps ? min_revalidate_pps : 1; + } + ++/* Set worst case delay (in ms) it might take before statistics of offloaded ++ * flows are updated. Offloaded flows younger than this delay will always be ++ * revalidated regardless of ofproto_min_revalidate_pps. */ ++void ++ofproto_set_offloaded_stats_delay(unsigned offloaded_stats_delay) ++{ ++ ofproto_offloaded_stats_delay = offloaded_stats_delay; ++} ++ + /* If forward_bpdu is true, the NORMAL action will forward frames with + * reserved (e.g. STP) destination Ethernet addresses. if forward_bpdu is false, + * the NORMAL action will drop these frames. */ +diff --git a/ofproto/ofproto.h b/ofproto/ofproto.h +index 4e15167ab..fa7973ac7 100644 +--- a/ofproto/ofproto.h ++++ b/ofproto/ofproto.h +@@ -311,6 +311,7 @@ int ofproto_port_dump_done(struct ofproto_port_dump *); + #define OFPROTO_MAX_IDLE_DEFAULT 10000 /* ms */ + #define OFPROTO_MAX_REVALIDATOR_DEFAULT 500 /* ms */ + #define OFPROTO_MIN_REVALIDATE_PPS_DEFAULT 5 ++#define OFPROTO_OFFLOADED_STATS_DELAY 2000 /* ms */ + + const char *ofproto_port_open_type(const struct ofproto *, + const char *port_type); +@@ -340,6 +341,7 @@ void ofproto_set_flow_limit(unsigned limit); + void ofproto_set_max_idle(unsigned max_idle); + void ofproto_set_max_revalidator(unsigned max_revalidator); + void ofproto_set_min_revalidate_pps(unsigned min_revalidate_pps); ++void ofproto_set_offloaded_stats_delay(unsigned offloaded_stats_delay); + void ofproto_set_forward_bpdu(struct ofproto *, bool forward_bpdu); + void ofproto_set_mac_table_config(struct ofproto *, unsigned idle_time, + size_t max_entries); +diff --git a/tests/classifier.at b/tests/classifier.at +index f652b5983..de2705653 100644 +--- a/tests/classifier.at ++++ b/tests/classifier.at +@@ -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 +diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at +index fa6111c1e..6b58cabec 100644 +--- a/tests/ofproto-dpif.at ++++ b/tests/ofproto-dpif.at +@@ -849,7 +849,7 @@ table=2 ip actions=set_field:192.168.3.91->ip_src,output(11) + AT_CHECK([ovs-ofctl -O OpenFlow12 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=1,nw_tos=0,nw_ttl=128,nw_frag=no,icmp_type=8,icmp_code=0'], [0], [stdout]) + AT_CHECK([tail -2 stdout], [0], +- [Megaflow: recirc_id=0,eth,ip,in_port=1,nw_src=192.168.0.1,nw_frag=no ++ [Megaflow: recirc_id=0,eth,icmp,in_port=1,nw_src=192.168.0.1,nw_frag=no + Datapath actions: 10,set(ipv4(src=192.168.3.91)),11,set(ipv4(src=192.168.3.90)),13 + ]) + OVS_VSWITCHD_STOP +@@ -912,7 +912,7 @@ AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1,dl_src=50:54:00:00:00:05,dl_ds + # Must match on the source address to be able to restore it's value for + # the second bucket + AT_CHECK([tail -2 stdout], [0], +- [Megaflow: recirc_id=0,eth,ip,in_port=1,nw_src=192.168.0.1,nw_frag=no ++ [Megaflow: recirc_id=0,eth,icmp,in_port=1,nw_src=192.168.0.1,nw_frag=no + Datapath actions: set(ipv4(src=192.168.3.90)),10,set(ipv4(src=192.168.0.1)),11 + ]) + OVS_VSWITCHD_STOP +@@ -944,7 +944,7 @@ done + AT_CHECK([ovs-appctl dpctl/dump-flows | sed 's/dp_hash(.*\/0xf)/dp_hash(0xXXXX\/0xf)/' | sed 's/packets.*actions:/actions:/' | strip_ufid | strip_used | sort], [0], [dnl + flow-dump from the main thread: + recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), actions:hash(sym_l4(0)),recirc(0x1) +-recirc_id(0x1),dp_hash(0xXXXX/0xf),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(src=192.168.0.1,frag=no), actions:set(ipv4(src=192.168.3.90)),10,set(ipv4(src=192.168.0.1)),10 ++recirc_id(0x1),dp_hash(0xXXXX/0xf),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(src=192.168.0.1,proto=1,frag=no), actions:set(ipv4(src=192.168.3.90)),10,set(ipv4(src=192.168.0.1)),10 + ]) + + OVS_VSWITCHD_STOP +@@ -959,7 +959,7 @@ AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1,dl_src=50:54:00:00:00:05,dl_ds + # Must match on the source address to be able to restore it's value for + # the third bucket + AT_CHECK([tail -2 stdout], [0], +- [Megaflow: recirc_id=0,eth,ip,in_port=1,nw_src=192.168.0.1,nw_frag=no ++ [Megaflow: recirc_id=0,eth,icmp,in_port=1,nw_src=192.168.0.1,nw_frag=no + Datapath actions: set(ipv4(src=192.168.3.90)),10,set(ipv4(src=192.168.0.1)),11 + ]) + OVS_VSWITCHD_STOP +@@ -1536,17 +1536,17 @@ AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) + AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=111,tos=0,ttl=2,frag=no)' -generate], [0], [stdout]) + AT_CHECK([tail -4 stdout], [0], [ + Final flow: ip,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=111,nw_tos=0,nw_ecn=0,nw_ttl=1,nw_frag=no +-Megaflow: recirc_id=0,eth,ip,in_port=1,nw_ttl=2,nw_frag=no ++Megaflow: recirc_id=0,eth,ip,in_port=1,nw_proto=111,nw_ttl=2,nw_frag=no + Datapath actions: set(ipv4(ttl=1)),2,userspace(pid=0,controller(reason=2,dont_send=0,continuation=0,recirc_id=1,rule_cookie=0,controller_id=0,max_len=65535)),4 + ]) + AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=111,tos=0,ttl=3,frag=no)'], [0], [stdout]) + AT_CHECK([tail -2 stdout], [0], +- [Megaflow: recirc_id=0,eth,ip,in_port=1,nw_ttl=3,nw_frag=no ++ [Megaflow: recirc_id=0,eth,ip,in_port=1,nw_proto=111,nw_ttl=3,nw_frag=no + Datapath actions: set(ipv4(ttl=2)),2,set(ipv4(ttl=1)),3,4 + ]) + AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=10,tclass=0x70,hlimit=128,frag=no)'], [0], [stdout]) + AT_CHECK([tail -2 stdout], [0], +- [Megaflow: recirc_id=0,eth,ipv6,in_port=1,nw_ttl=128,nw_frag=no ++ [Megaflow: recirc_id=0,eth,ipv6,in_port=1,nw_proto=10,nw_ttl=128,nw_frag=no + Datapath actions: set(ipv6(hlimit=127)),2,set(ipv6(hlimit=126)),3,4 + ]) + +@@ -1656,7 +1656,7 @@ AT_CHECK([ovs-vsctl -- \ + --id=@q2 create Queue dscp=2], [0], [ignore]) + AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(9),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=1.1.1.1,dst=2.2.2.2,proto=1,tos=0xff,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout]) + AT_CHECK([tail -2 stdout], [0], +- [Megaflow: recirc_id=0,skb_priority=0,eth,ip,in_port=9,nw_tos=252,nw_frag=no ++ [Megaflow: recirc_id=0,skb_priority=0,eth,icmp,in_port=9,nw_tos=252,nw_frag=no + Datapath actions: dnl + 100,dnl + set(ipv4(tos=0x4/0xfc)),set(skb_priority(0x1)),1,dnl +@@ -11884,7 +11884,7 @@ ovs-ofctl dump-flows br0 + + AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.10.10.2,dst=10.10.10.1,proto=1,tos=1,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout]) + AT_CHECK([tail -3 stdout], [0], [dnl +-Megaflow: recirc_id=0,eth,ip,reg0=0/0x1,in_port=1,nw_src=10.10.10.2,nw_frag=no ++Megaflow: recirc_id=0,eth,icmp,reg0=0/0x1,in_port=1,nw_src=10.10.10.2,nw_frag=no + Datapath actions: drop + Translation failed (Recursion too deep), packet is dropped. + ]) +diff --git a/tests/ofproto.at b/tests/ofproto.at +index a666bebca..2fa8486a8 100644 +--- a/tests/ofproto.at ++++ b/tests/ofproto.at +@@ -6538,3 +6538,185 @@ verify_deleted + + OVS_VSWITCHD_STOP(["/nw_dst,output=2 ++table=0 in_port=1 priority=83,ip,nw_dst=192.168.1.15,actions=set_field:192.168.21.26->nw_src,output=2 ++table=0 in_port=1 priority=82,ip,nw_dst=192.168.1.14,actions=set_field:0x40->nw_tos,output=2 ++table=0 in_port=1 priority=0,actions=drop ++]) ++AT_CHECK([ovs-ofctl del-flows br0]) ++AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) ++ ++dnl send a proto 0 packet to try and poison the DP flow path ++AT_CHECK([ovs-appctl netdev-dummy/receive p1 \ ++ '5054000000075054000000050800450000548de140004000289fc0a801c4c0a8011408003bf60002001bbf080a640000000032ad010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637']) ++ ++AT_CHECK([ovs-appctl dpctl/dump-flows], [0], [dnl ++flow-dump from the main thread: ++recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(dst=192.168.1.20,proto=0,frag=no), packets:0, bytes:0, used:never, actions:2 ++]) ++ ++dnl Send ICMP for mod nw_src and mod nw_dst ++AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=192.168.1.1,dst=192.168.1.21,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) ++AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=192.168.1.1,dst=192.168.1.20,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) ++ ++dnl send ICMP that will dec TTL ++AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=192.168.1.1,dst=192.168.1.10,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) ++ ++dnl send ICMP that will mod TTL ++AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=192.168.1.1,dst=192.168.1.19,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) ++ ++dnl send ICMP that will mod ECN ++AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=192.168.1.1,dst=192.168.1.18,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) ++ ++dnl send ICMP that will mod TOS ++AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=192.168.1.1,dst=192.168.1.17,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) ++ ++dnl send ICMP that will set DST ++AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=192.168.1.1,dst=192.168.1.16,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) ++ ++dnl send ICMP that will set SRC ++AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=192.168.1.1,dst=192.168.1.15,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) ++ ++dnl send ICMP that will set TOS ++AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=192.168.1.1,dst=192.168.1.14,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) ++ ++AT_CHECK([ovs-appctl dpctl/dump-flows | sort], [0], [dnl ++flow-dump from the main thread: ++recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(dst=192.168.1.10,proto=1,ttl=64,frag=no), packets:0, bytes:0, used:never, actions:set(ipv4(ttl=63)),2 ++recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(dst=192.168.1.14,proto=1,tos=0/0xfc,frag=no), packets:0, bytes:0, used:never, actions:set(ipv4(tos=0x40/0xfc)),2 ++recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(dst=192.168.1.16,proto=1,frag=no), packets:0, bytes:0, used:never, actions:set(ipv4(dst=192.168.20.26)),2 ++recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(dst=192.168.1.17,proto=1,tos=0/0xfc,frag=no), packets:0, bytes:0, used:never, actions:set(ipv4(tos=0x40/0xfc)),2 ++recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(dst=192.168.1.18,proto=1,tos=0/0x3,frag=no), packets:0, bytes:0, used:never, actions:set(ipv4(tos=0x2/0x3)),2 ++recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(dst=192.168.1.19,proto=1,ttl=64,frag=no), packets:0, bytes:0, used:never, actions:set(ipv4(ttl=8)),2 ++recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(dst=192.168.1.20,proto=0,frag=no), packets:0, bytes:0, used:never, actions:2 ++recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(dst=192.168.1.20,proto=1,frag=no), packets:0, bytes:0, used:never, actions:set(ipv4(dst=192.168.20.20)),2 ++recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(src=192.168.1.1,dst=192.168.1.15,proto=1,frag=no), packets:0, bytes:0, used:never, actions:set(ipv4(src=192.168.21.26)),2 ++recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(src=192.168.1.1,dst=192.168.1.21,proto=1,frag=no), packets:0, bytes:0, used:never, actions:set(ipv4(src=192.168.20.21)),2 ++]) ++ ++OVS_VSWITCHD_STOP ++AT_CLEANUP ++ ++AT_SETUP([ofproto - implicit mask of ipv6 proto with HOPOPT field]) ++OVS_VSWITCHD_START ++add_of_ports br0 1 2 ++ ++AT_DATA([flows.txt], [dnl ++table=0 in_port=1 priority=77,ip6,ipv6_dst=111:db8::3,actions=dec_ttl,output=2 ++table=0 in_port=1 priority=76,ip6,ipv6_dst=111:db8::4,actions=mod_nw_ttl:8,output=2 ++table=0 in_port=1 priority=75,ip6,ipv6_dst=111:db8::5,actions=mod_nw_ecn:2,output=2 ++table=0 in_port=1 priority=74,ip6,ipv6_dst=111:db8::6,actions=mod_nw_tos:0x40,output=2 ++table=0 in_port=1 priority=73,ip6,ipv6_dst=111:db8::7,actions=set_field:2112:db8::2->ipv6_dst,output=2 ++table=0 in_port=1 priority=72,ip6,ipv6_dst=111:db8::8,actions=set_field:2112:db8::3->ipv6_src,output=2 ++table=0 in_port=1 priority=72,ip6,ipv6_dst=111:db8::9,actions=set_field:44->ipv6_label,output=2 ++table=0 in_port=1 priority=0,actions=drop ++]) ++AT_CHECK([ovs-ofctl del-flows br0]) ++AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) ++ ++dnl send a proto 0 packet to try and poison the DP flow path ++AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x86dd),ipv6(src=2001:db8::1,dst=111:db8::3,proto=0,tclass=0,hlimit=64,frag=no)']) ++ ++AT_CHECK([ovs-appctl dpctl/dump-flows], [0], [dnl ++flow-dump from the main thread: ++recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x86dd),ipv6(dst=111:db8::3,proto=0,hlimit=0,frag=no), packets:0, bytes:0, used:never, actions:userspace(pid=0,controller(reason=2,dont_send=0,continuation=0,recirc_id=1,rule_cookie=0,controller_id=0,max_len=65535)) ++]) ++ ++dnl Send ICMP for mod nw_src and mod nw_dst ++AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x86dd),ipv6(src=2001:db8::1,dst=111:db8::3,proto=1,tclass=0,hlimit=64,frag=no),icmpv6(type=0,code=8)']) ++AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x86dd),ipv6(src=2001:db8::1,dst=111:db8::4,proto=1,tclass=0,hlimit=64,frag=no),icmpv6(type=0,code=8)']) ++ ++dnl send ICMP that will dec TTL ++AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x86dd),ipv6(src=2001:db8::1,dst=111:db8::5,proto=1,tclass=0,hlimit=64,frag=no),icmpv6(type=0,code=8)']) ++ ++dnl send ICMP that will mod TTL ++AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x86dd),ipv6(src=2001:db8::1,dst=111:db8::6,proto=1,tclass=0,hlimit=64,frag=no),icmpv6(type=0,code=8)']) ++ ++dnl send ICMP that will mod ECN ++AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x86dd),ipv6(src=2001:db8::1,dst=111:db8::7,proto=1,tclass=0,hlimit=64,frag=no),icmpv6(type=0,code=8)']) ++ ++dnl send ICMP that will mod TOS ++AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x86dd),ipv6(src=2001:db8::1,dst=111:db8::8,proto=1,tclass=0,hlimit=64,frag=no),icmpv6(type=0,code=8)']) ++ ++dnl send ICMP that will set LABEL ++AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x86dd),ipv6(src=2001:db8::1,dst=111:db8::9,proto=1,tclass=0,hlimit=64,frag=no),icmpv6(type=0,code=8)']) ++ ++AT_CHECK([ovs-appctl dpctl/dump-flows | sort], [0], [dnl ++flow-dump from the main thread: ++recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x86dd),ipv6(dst=111:db8::3,proto=0,hlimit=0,frag=no), packets:0, bytes:0, used:never, actions:userspace(pid=0,controller(reason=2,dont_send=0,continuation=0,recirc_id=1,rule_cookie=0,controller_id=0,max_len=65535)) ++recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x86dd),ipv6(dst=111:db8::3,proto=1,hlimit=64,frag=no), packets:0, bytes:0, used:never, actions:set(ipv6(hlimit=63)),2 ++recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x86dd),ipv6(dst=111:db8::4,proto=1,hlimit=64,frag=no), packets:0, bytes:0, used:never, actions:set(ipv6(hlimit=8)),2 ++recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x86dd),ipv6(dst=111:db8::5,proto=1,tclass=0/0x3,frag=no), packets:0, bytes:0, used:never, actions:set(ipv6(tclass=0x2/0x3)),2 ++recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x86dd),ipv6(dst=111:db8::6,proto=1,tclass=0/0xfc,frag=no), packets:0, bytes:0, used:never, actions:set(ipv6(tclass=0x40/0xfc)),2 ++recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x86dd),ipv6(dst=111:db8::7,proto=1,frag=no), packets:0, bytes:0, used:never, actions:set(ipv6(dst=2112:db8::2)),2 ++recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x86dd),ipv6(dst=111:db8::9,label=0,proto=1,frag=no), packets:0, bytes:0, used:never, actions:set(ipv6(label=0x2c)),2 ++recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x86dd),ipv6(src=2001:db8::1,dst=111:db8::8,proto=1,frag=no), packets:0, bytes:0, used:never, actions:set(ipv6(src=2112:db8::3)),2 ++]) ++ ++OVS_VSWITCHD_STOP ++AT_CLEANUP ++ ++AT_SETUP([ofproto - implicit mask of ARP OPer field]) ++OVS_VSWITCHD_START ++add_of_ports br0 1 2 ++ ++AT_DATA([flows.txt], [dnl ++table=0 in_port=1 priority=77,arp,arp_sha=00:01:02:03:04:06,actions=set_field:0x1->arp_op,2 ++table=0 in_port=1 priority=76,arp,arp_sha=00:01:02:03:04:07,actions=set_field:00:02:03:04:05:06->arp_sha,2 ++table=0 in_port=1 priority=75,arp,arp_sha=00:01:02:03:04:08,actions=set_field:ff:00:00:00:00:ff->arp_tha,2 ++table=0 in_port=1 priority=74,arp,arp_sha=00:01:02:03:04:09,actions=set_field:172.31.110.26->arp_spa,2 ++table=0 in_port=1 priority=73,arp,arp_sha=00:01:02:03:04:0a,actions=set_field:172.31.110.10->arp_tpa,2 ++table=0 in_port=1 priority=1,actions=drop ++]) ++ ++AT_CHECK([ovs-ofctl del-flows br0]) ++AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) ++ ++dnl Send op == 0 packet ++AT_CHECK([ovs-appctl netdev-dummy/receive p1 \ ++ 'ffffffffffffaa55aa550000080600010800060400000001020304070c0a00010000000000000c0a0002']) ++ ++AT_CHECK([ovs-appctl dpctl/dump-flows], [0], [dnl ++flow-dump from the main thread: ++recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0806),arp(op=0,sha=00:01:02:03:04:07), packets:0, bytes:0, used:never, actions:2 ++]) ++ ++dnl Send op 2 -> set op ++AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0806),arp(sip=172.31.110.1,tip=172.31.110.25,op=2,sha=00:01:02:03:04:06,tha=ff:ff:ff:ff:ff:ff)']) ++ ++dnl Send op 1 -> set SHA ++AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0806),arp(sip=172.31.110.1,tip=172.31.110.25,op=1,sha=00:01:02:03:04:07,tha=ff:ff:ff:ff:ff:ff)']) ++ ++dnl Send op 1 -> set THA ++AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0806),arp(sip=172.31.110.1,tip=172.31.110.25,op=1,sha=00:01:02:03:04:08,tha=ff:ff:ff:ff:ff:ff)']) ++ ++dnl Send op 1 -> set SIP ++AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0806),arp(sip=172.31.110.1,tip=172.31.110.25,op=1,sha=00:01:02:03:04:09,tha=ff:ff:ff:ff:ff:ff)']) ++ ++dnl Send op 1 -> set TIP ++AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0806),arp(sip=172.31.110.1,tip=172.31.110.25,op=1,sha=00:01:02:03:04:0a,tha=ff:ff:ff:ff:ff:ff)']) ++ ++AT_CHECK([ovs-appctl dpctl/dump-flows | sort], [0], [dnl ++flow-dump from the main thread: ++recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0806),arp(op=0,sha=00:01:02:03:04:07), packets:0, bytes:0, used:never, actions:2 ++recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0806),arp(op=1,sha=00:01:02:03:04:07), packets:0, bytes:0, used:never, actions:userspace(pid=0,slow_path(action)) ++recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0806),arp(op=1,sha=00:01:02:03:04:08,tha=ff:ff:ff:ff:ff:ff), packets:0, bytes:0, used:never, actions:userspace(pid=0,slow_path(action)) ++recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0806),arp(op=2,sha=00:01:02:03:04:06), packets:0, bytes:0, used:never, actions:userspace(pid=0,slow_path(action)) ++recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0806),arp(sip=172.31.110.1,op=1,sha=00:01:02:03:04:09), packets:0, bytes:0, used:never, actions:userspace(pid=0,slow_path(action)) ++recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0806),arp(tip=172.31.110.25,op=1,sha=00:01:02:03:04:0a), packets:0, bytes:0, used:never, actions:userspace(pid=0,slow_path(action)) ++]) ++ ++OVS_VSWITCHD_STOP ++AT_CLEANUP +diff --git a/tests/ovs-vsctl.at b/tests/ovs-vsctl.at +index a92156f00..a368bff6e 100644 +--- a/tests/ovs-vsctl.at ++++ b/tests/ovs-vsctl.at +@@ -425,6 +425,7 @@ AT_CHECK([RUN_OVS_VSCTL_ONELINE( + [add-port a a1], + [add-bond a bond0 a2 a3], + [br-set-external-id a key0 value0], ++ [add Bridge a external_ids key0=value1], + [set port a1 external-ids:key1=value1], + [set interface a2 external-ids:key2=value2], + [set interface a2 external-ids:key3=value3], +@@ -446,6 +447,7 @@ AT_CHECK([RUN_OVS_VSCTL_ONELINE( + + + ++ + key0=value0 + value0 + +@@ -1071,13 +1073,9 @@ AT_CHECK([RUN_OVS_VSCTL([set controller br1 'connection-mode=xyz'])], + AT_CHECK([RUN_OVS_VSCTL([set controller br1 connection-mode:x=y])], + [1], [], [ovs-vsctl: cannot specify key to set for non-map column connection_mode + ]) +-AT_CHECK([RUN_OVS_VSCTL([add bridge br1 datapath_id x y -- show])], ++AT_CHECK([RUN_OVS_VSCTL([add bridge br1 datapath_id x y])], + [1], [], [ovs-vsctl: "add" operation would put 2 values in column datapath_id of table Bridge but the maximum number is 1 + ]) +-AT_CHECK([RUN_OVS_VSCTL([add bridge br1 datapath_id x y])], [1], [], [stderr]) +-AT_CHECK([sed "/^.*|WARN|.*/d" < stderr], [0], [dnl +-ovs-vsctl: transaction error: {"details":"set must have 0 to 1 members but 2 are present","error":"syntax error","syntax":"[[\"set\",[\"x\",\"y\"]]]"} +-]) + AT_CHECK([RUN_OVS_VSCTL([remove netflow `cat netflow-uuid` targets '"1.2.3.4:567"'])], + [1], [], [ovs-vsctl: "remove" operation would put 0 values in column targets of table NetFlow but the minimum number is 1 + ]) +diff --git a/tests/packet-type-aware.at b/tests/packet-type-aware.at +index 3b5c66fe5..d63528e69 100644 +--- a/tests/packet-type-aware.at ++++ b/tests/packet-type-aware.at +@@ -1021,7 +1021,7 @@ AT_CHECK([ + ], [0], [flow-dump from the main thread: + recirc_id(0),in_port(p0),packet_type(ns=0,id=0),eth(src=aa:bb:cc:00:00:02,dst=aa:bb:cc:00:00:01),eth_type(0x0800),ipv4(dst=20.0.0.1,proto=47,frag=no), packets:3, bytes:378, used:0.0s, actions:tnl_pop(gre_sys) + tunnel(src=20.0.0.2,dst=20.0.0.1,flags(-df-csum)),recirc_id(0),in_port(gre_sys),packet_type(ns=1,id=0x8847),eth_type(0x8847),mpls(label=999/0x0,tc=0/0,ttl=64/0x0,bos=1/1), packets:3, bytes:264, used:0.0s, actions:push_eth(src=00:00:00:00:00:00,dst=00:00:00:00:00:00),pop_mpls(eth_type=0x800),recirc(0x1) +-tunnel(src=20.0.0.2,dst=20.0.0.1,flags(-df-csum)),recirc_id(0x1),in_port(gre_sys),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(ttl=64,frag=no), packets:3, bytes:294, used:0.0s, actions:set(ipv4(ttl=63)),int-br ++tunnel(src=20.0.0.2,dst=20.0.0.1,flags(-df-csum)),recirc_id(0x1),in_port(gre_sys),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(proto=1,ttl=64,frag=no), packets:3, bytes:294, used:0.0s, actions:set(ipv4(ttl=63)),int-br + ]) + + ovs-appctl time/warp 1000 +diff --git a/tests/system-interface.at b/tests/system-interface.at +index 784bada12..3bf339582 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-offloads-traffic.at b/tests/system-offloads-traffic.at +index d36da0580..8dd3bdf88 100644 +--- a/tests/system-offloads-traffic.at ++++ b/tests/system-offloads-traffic.at +@@ -742,3 +742,118 @@ recirc_id(),in_port(3),eth_type(0x0800),ipv4(frag=no), packets:29, bytes + + OVS_TRAFFIC_VSWITCHD_STOP + AT_CLEANUP ++ ++ ++AT_SETUP([offloads - offload flow to none-offload]) ++OVS_TRAFFIC_VSWITCHD_START([], [], [-- set Open_vSwitch . other_config:hw-offload=true]) ++ ++ADD_NAMESPACES(at_ns0, at_ns1) ++ ++ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") ++ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") ++ ++AT_DATA([flows.txt], [dnl ++add in_port=ovs-p0,actions=ovs-p1 ++add in_port=ovs-p1,actions=ovs-p0 ++]) ++AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) ++ ++NS_CHECK_EXEC([at_ns0], [ping -q -c 10 -i 0.1 -w 2 10.1.1.2 | FORMAT_PING], [0], [dnl ++10 packets transmitted, 10 received, 0% packet loss, time 0ms ++]) ++ ++AT_CHECK([ovs-appctl dpctl/dump-flows type=tc | grep "eth_type(0x0800)" | sort | strip_recirc | strip_used], [0], [dnl ++recirc_id(),in_port(2),eth(),eth_type(0x0800),ipv4(frag=no), packets:9, bytes:756, used:0.0s, actions:3 ++recirc_id(),in_port(3),eth(),eth_type(0x0800),ipv4(frag=no), packets:9, bytes:756, used:0.0s, actions:2 ++]) ++ ++dnl Here we use an output action with truncate, which will force a kernel flow. ++AT_DATA([flows2.txt], [dnl ++modify in_port=ovs-p0,actions=output(port=ovs-p1, max_len=128) ++modify in_port=ovs-p1,actions=output(port=ovs-p0, max_len=128) ++]) ++AT_CHECK([ovs-ofctl add-flows br0 flows2.txt]) ++AT_CHECK([ovs-appctl revalidator/wait], [0]) ++ ++NS_CHECK_EXEC([at_ns0], [ping -q -c 10 -i 0.1 -w 2 10.1.1.2 | FORMAT_PING], [0], [dnl ++10 packets transmitted, 10 received, 0% packet loss, time 0ms ++]) ++ ++AT_CHECK([ovs-appctl dpctl/dump-flows type=ovs | grep "eth_type(0x0800)" | sort | strip_recirc | strip_used], [0], [dnl ++recirc_id(),in_port(2),eth(),eth_type(0x0800),ipv4(frag=no), packets:10, bytes:980, used:0.0s, actions:trunc(128),3 ++recirc_id(),in_port(3),eth(),eth_type(0x0800),ipv4(frag=no), packets:10, bytes:980, used:0.0s, actions:trunc(128),2 ++]) ++ ++AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) ++AT_CHECK([ovs-appctl revalidator/wait], [0]) ++ ++NS_CHECK_EXEC([at_ns0], [ping -q -c 10 -i 0.1 -w 2 10.1.1.2 | FORMAT_PING], [0], [dnl ++10 packets transmitted, 10 received, 0% packet loss, time 0ms ++]) ++ ++AT_CHECK([ovs-appctl dpctl/dump-flows type=tc | grep "eth_type(0x0800)" | sort | strip_recirc | strip_used], [0], [dnl ++recirc_id(),in_port(2),eth(),eth_type(0x0800),ipv4(frag=no), packets:10, bytes:840, used:0.0s, actions:3 ++recirc_id(),in_port(3),eth(),eth_type(0x0800),ipv4(frag=no), packets:10, bytes:840, used:0.0s, actions:2 ++]) ++ ++AT_CHECK([ovs-appctl coverage/read-counter ukey_invalid_stat_reset], [0], [dnl ++0 ++]) ++ ++OVS_TRAFFIC_VSWITCHD_STOP ++AT_CLEANUP ++ ++AT_SETUP([offloads - delete ufid mapping if device not exist - offloads enabled]) ++OVS_TRAFFIC_VSWITCHD_START([], [], [-- set Open_vSwitch . other_config:hw-offload=true]) ++ ++AT_CHECK([ovs-ofctl add-flow br0 "actions=normal"]) ++ ++ADD_NAMESPACES(at_ns0, at_ns1, at_ns2) ++ ++dnl Disable IPv6 to skip unexpected flow ++AT_CHECK([sysctl -w net.ipv6.conf.br0.disable_ipv6=1], [0], [ignore]) ++NS_CHECK_EXEC([at_ns0], [sysctl -w net.ipv6.conf.all.disable_ipv6=1], [0], [ignore]) ++NS_CHECK_EXEC([at_ns1], [sysctl -w net.ipv6.conf.all.disable_ipv6=1], [0], [ignore]) ++NS_CHECK_EXEC([at_ns2], [sysctl -w net.ipv6.conf.all.disable_ipv6=1], [0], [ignore]) ++ ++ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24", "aa:1a:54:e9:c5:56") ++ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") ++ ++NS_CHECK_EXEC([at_ns0], [ping -q -c 2 -i 0.2 10.1.1.2 | FORMAT_PING], [0], [dnl ++2 packets transmitted, 2 received, 0% packet loss, time 0ms ++]) ++ ++dnl Delete and add interface ovs-p0/p0 ++AT_CHECK([ip link del dev ovs-p0]) ++AT_CHECK([ip link add p0 type veth peer name ovs-p0 || return 77]) ++AT_CHECK([ip link set p0 netns at_ns0]) ++AT_CHECK([ip link set dev ovs-p0 up]) ++NS_CHECK_EXEC([at_ns0], [ip addr add dev p0 "10.1.1.1/24"]) ++NS_CHECK_EXEC([at_ns0], [ip link set dev p0 up]) ++NS_CHECK_EXEC([at_ns0], [ip link set dev p0 address "aa:1a:54:e9:c5:56"]) ++ ++AT_CHECK([ovs-appctl revalidator/purge], [0]) ++ ++dnl Generate flows to trigger the hmap expand once ++ADD_VETH(p2, at_ns2, br0, "10.1.1.3/24") ++NS_CHECK_EXEC([at_ns0], [ping -q -c 2 -i 0.2 10.1.1.2 | FORMAT_PING], [0], [dnl ++2 packets transmitted, 2 received, 0% packet loss, time 0ms ++]) ++NS_CHECK_EXEC([at_ns0], [ping -q -c 2 -i 0.2 10.1.1.3 | FORMAT_PING], [0], [dnl ++2 packets transmitted, 2 received, 0% packet loss, time 0ms ++]) ++ ++AT_CHECK([ovs-appctl revalidator/purge], [0]) ++dnl Fix purge fail occasionally ++AT_CHECK([ovs-appctl revalidator/purge], [0]) ++ ++AT_CHECK([test $(ovs-appctl dpctl/dump-flows | grep -c "eth_type(0x0800)") -eq 0], [0], [ignore]) ++ ++OVS_TRAFFIC_VSWITCHD_STOP(["/could not open network device ovs-p0/d ++/on nonexistent port/d ++/failed to flow_get/d ++/Failed to acquire udpif_key/d ++/No such device/d ++/failed to offload flow/d ++"]) ++AT_CLEANUP +diff --git a/tests/system-traffic.at b/tests/system-traffic.at +index 221d96aef..6678911b4 100644 +--- a/tests/system-traffic.at ++++ b/tests/system-traffic.at +@@ -2360,8 +2360,10 @@ priority=100,in_port=2,icmp,action=ct(zone=5,commit),1 + ]) + + AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) ++dp=$(ovs-appctl dpctl/dump-dps) + + m4_foreach([FLUSH_CMD], [[ovs-appctl dpctl/flush-conntrack], ++ [ovs-appctl dpctl/flush-conntrack $dp], + [ovs-ofctl ct-flush br0]], [ + AS_BOX([Testing with FLUSH_CMD]) + +@@ -2504,8 +2506,48 @@ udp,orig=(src=10.1.1.2,dst=10.1.1.1,sport=2,dport=1),reply=(src=10.1.1.1,dst=10. + AT_CHECK([FLUSH_CMD zone=5 '' 'ct_nw_src=10.1.1.1']) + + AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "10\.1\.1\.1"], [1]) ++ ++dnl Test UDP from port 1 and 2, flush without arguments ++AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=1 packet=50540000000a50540000000908004500001c000000000011a4cd0a0101010a0101020001000200080000 actions=resubmit(,0)"]) ++AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=2 packet=50540000000a50540000000908004500001c000000000011a4cd0a0101020a0101010002000100080000 actions=resubmit(,0)"]) ++ ++ ++AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "10\.1\.1\.1" | sort], [0], [dnl ++udp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=1,dport=2),reply=(src=10.1.1.2,dst=10.1.1.1,sport=2,dport=1) ++udp,orig=(src=10.1.1.2,dst=10.1.1.1,sport=2,dport=1),reply=(src=10.1.1.1,dst=10.1.1.2,sport=1,dport=2),zone=5 + ]) + ++AT_CHECK([FLUSH_CMD]) ++ ++AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "10\.1\.1\.1"], [1]) ++]) ++ ++dnl Test flush with invalid arguments ++ ++AT_CHECK([ovs-appctl dpctl/flush-conntrack zone=invalid 'ct_nw_src=10.1.1.1' 'ct_nw_dst=10.1.1.1'], [2], [ignore], [stderr]) ++AT_CHECK([grep -q "failed to parse zone" stderr]) ++ ++AT_CHECK([ovs-appctl dpctl/flush-conntrack zone=1 'ct_nw_src=10.1.1.1,invalid=invalid' 'ct_nw_dst=10.1.1.1'], [2], [ignore], [stderr]) ++AT_CHECK([grep -q "invalid conntrack tuple field: invalid" stderr]) ++ ++AT_CHECK([ovs-appctl dpctl/flush-conntrack zone=1 'ct_nw_src=invalid' 'ct_nw_dst=10.1.1.1'], [2], [ignore], [stderr]) ++AT_CHECK([grep -q "failed to parse field ct_nw_src" stderr]) ++ ++AT_CHECK([ovs-appctl dpctl/flush-conntrack zone=1 'ct_nw_src=10.1.1.1' 'ct_nw_dst=10.1.1.1' invalid], [2], [ignore], [stderr]) ++AT_CHECK([grep -q "invalid arguments" stderr]) ++ ++AT_CHECK([ovs-appctl dpctl/flush-conntrack $dp zone=1 'ct_nw_src=10.1.1.1' 'ct_nw_dst=10.1.1.1' invalid], [2], [ignore], [stderr]) ++AT_CHECK([grep -q "command takes at most 4 arguments" stderr]) ++ ++AT_CHECK([ovs-appctl dpctl/flush-conntrack $dp 'ct_nw_src=10.1.1.1' 'ct_nw_dst=10.1.1.1' invalid], [2], [ignore], [stderr]) ++AT_CHECK([grep -q "invalid arguments" stderr]) ++ ++AT_CHECK([ovs-ofctl ct-flush br0 zone=1 'ct_nw_src=10.1.1.1' 'ct_nw_dst=10.1.1.1' invalid], [1], [ignore], [stderr]) ++AT_CHECK([grep -q "command takes at most 4 arguments" stderr]) ++ ++AT_CHECK([ovs-ofctl ct-flush br0 'ct_nw_src=10.1.1.1' 'ct_nw_dst=10.1.1.1' invalid], [1], [ignore], [stderr]) ++AT_CHECK([grep -q "Invalid arguments" stderr]) ++ + OVS_TRAFFIC_VSWITCHD_STOP + AT_CLEANUP + +@@ -7220,7 +7262,7 @@ 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 +-OVS_DAEMONIZE([tcpdump -l -U -i ovs-server -w server.pcap 2>tcpdump0_err], [tcpdump0.pid]) ++NETNS_DAEMONIZE([server], [tcpdump -l -U -i server -w server.pcap 2>tcpdump0_err], [tcpdump0.pid]) + OVS_WAIT_UNTIL([grep "listening" tcpdump0_err]) + + dnl Send UDP client->server +@@ -7262,7 +7304,7 @@ dnl Check the ICMP error in reply direction + AT_CHECK([ovs-appctl dpctl/flush-conntrack zone=42]) + + rm client.pcap +-OVS_DAEMONIZE([tcpdump -l -U -i ovs-client -w client.pcap 2>tcpdump1_err], [tcpdump1.pid]) ++NETNS_DAEMONIZE([client], [tcpdump -l -U -i client -w client.pcap 2>tcpdump1_err], [tcpdump1.pid]) + OVS_WAIT_UNTIL([grep "listening" tcpdump1_err]) + + dnl Send UDP client->server +diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c +index eabec18a3..3ce4e82ec 100644 +--- a/utilities/ovs-ofctl.c ++++ b/utilities/ovs-ofctl.c +@@ -3089,6 +3089,10 @@ ofctl_ct_flush(struct ovs_cmdl_context *ctx) + args--; + } + ++ if (args > 0) { ++ ovs_fatal(0, "Invalid arguments"); ++ } ++ + open_vconn(ctx->argv[1], &vconn); + enum ofp_version version = vconn_get_version(vconn); + struct ofpbuf *msg = ofp_ct_match_encode(&match, pzone, version); +diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c +index bfb2adef1..0deca14b9 100644 +--- a/vswitchd/bridge.c ++++ b/vswitchd/bridge.c +@@ -832,6 +832,9 @@ bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg) + ofproto_set_min_revalidate_pps( + smap_get_uint(&ovs_cfg->other_config, "min-revalidate-pps", + OFPROTO_MIN_REVALIDATE_PPS_DEFAULT)); ++ ofproto_set_offloaded_stats_delay( ++ smap_get_uint(&ovs_cfg->other_config, "offloaded-stats-delay", ++ OFPROTO_OFFLOADED_STATS_DELAY)); + ofproto_set_vlan_limit(smap_get_int(&ovs_cfg->other_config, "vlan-limit", + LEGACY_MAX_VLAN_HEADERS)); + ofproto_set_bundle_idle_timeout(smap_get_uint(&ovs_cfg->other_config, +diff --git a/vswitchd/vswitch.xml b/vswitchd/vswitch.xml +index 2b57fc0e3..1204805b1 100644 +--- a/vswitchd/vswitch.xml ++++ b/vswitchd/vswitch.xml +@@ -215,6 +215,19 @@ +

+ + ++ ++

++ Set worst case delay (in ms) it might take before statistics of ++ offloaded flows are updated. Offloaded flows younger than this ++ delay will always be revalidated regardless of ++ . ++

++

++ The default is 2000. ++

++
++ + +

+@@ -6296,6 +6309,12 @@ ovs-vsctl add-port br0 p0 -- set Interface p0 type=patch options:peer=p1 \ + translated to an ephemeral port. If there is no collision, no SNAT + is performed. + ++ ++ True if the datapath supports CT flush OpenFlow Nicira extension ++ called NXT_CT_FLUSH. The NXT_CT_FLUSH ++ extensions allows to flush CT entries based on specified parameters. ++ + + + +Submodule dpdk 9dae7a15a..3812e23f6: +diff --git a/dpdk/drivers/net/i40e/i40e_ethdev.c b/dpdk/drivers/net/i40e/i40e_ethdev.c +index 7726a89d99..a982e42264 100644 +--- a/dpdk/drivers/net/i40e/i40e_ethdev.c ++++ b/dpdk/drivers/net/i40e/i40e_ethdev.c +@@ -387,7 +387,6 @@ static int i40e_set_default_mac_addr(struct rte_eth_dev *dev, + struct rte_ether_addr *mac_addr); + + static int i40e_dev_mtu_set(struct rte_eth_dev *dev, uint16_t mtu); +-static void i40e_set_mac_max_frame(struct rte_eth_dev *dev, uint16_t size); + + static int i40e_ethertype_filter_convert( + const struct rte_eth_ethertype_filter *input, +@@ -1711,6 +1710,11 @@ eth_i40e_dev_init(struct rte_eth_dev *dev, void *init_params __rte_unused) + */ + i40e_add_tx_flow_control_drop_filter(pf); + ++ /* Set the max frame size to 0x2600 by default, ++ * in case other drivers changed the default value. ++ */ ++ i40e_aq_set_mac_config(hw, I40E_FRAME_SIZE_MAX, TRUE, false, 0, NULL); ++ + /* initialize RSS rule list */ + TAILQ_INIT(&pf->rss_config_list); + +@@ -2328,7 +2332,6 @@ i40e_dev_start(struct rte_eth_dev *dev) + uint32_t intr_vector = 0; + struct i40e_vsi *vsi; + uint16_t nb_rxq, nb_txq; +- uint16_t max_frame_size; + + hw->adapter_stopped = 0; + +@@ -2467,9 +2470,6 @@ i40e_dev_start(struct rte_eth_dev *dev) + "please call hierarchy_commit() " + "before starting the port"); + +- max_frame_size = dev->data->mtu + I40E_ETH_OVERHEAD; +- i40e_set_mac_max_frame(dev, max_frame_size); +- + return I40E_SUCCESS; + + tx_err: +@@ -2809,9 +2809,6 @@ i40e_dev_set_link_down(struct rte_eth_dev *dev) + return i40e_phy_conf_link(hw, abilities, speed, false); + } + +-#define CHECK_INTERVAL 100 /* 100ms */ +-#define MAX_REPEAT_TIME 10 /* 1s (10 * 100ms) in total */ +- + static __rte_always_inline void + update_link_reg(struct i40e_hw *hw, struct rte_eth_link *link) + { +@@ -2878,6 +2875,8 @@ static __rte_always_inline void + update_link_aq(struct i40e_hw *hw, struct rte_eth_link *link, + bool enable_lse, int wait_to_complete) + { ++#define CHECK_INTERVAL 100 /* 100ms */ ++#define MAX_REPEAT_TIME 10 /* 1s (10 * 100ms) in total */ + uint32_t rep_cnt = MAX_REPEAT_TIME; + struct i40e_link_status link_status; + int status; +@@ -6738,7 +6737,6 @@ i40e_dev_handle_aq_msg(struct rte_eth_dev *dev) + if (!ret) + rte_eth_dev_callback_process(dev, + RTE_ETH_EVENT_INTR_LSC, NULL); +- + break; + default: + PMD_DRV_LOG(DEBUG, "Request %u is not supported yet", +@@ -12123,40 +12121,6 @@ i40e_cloud_filter_qinq_create(struct i40e_pf *pf) + return ret; + } + +-static void +-i40e_set_mac_max_frame(struct rte_eth_dev *dev, uint16_t size) +-{ +- struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private); +- uint32_t rep_cnt = MAX_REPEAT_TIME; +- struct rte_eth_link link; +- enum i40e_status_code status; +- bool can_be_set = true; +- +- /* +- * I40E_MEDIA_TYPE_BASET link up can be ignored +- * I40E_MEDIA_TYPE_BASET link down that hw->phy.media_type +- * is I40E_MEDIA_TYPE_UNKNOWN +- */ +- if (hw->phy.media_type != I40E_MEDIA_TYPE_BASET && +- hw->phy.media_type != I40E_MEDIA_TYPE_UNKNOWN) { +- do { +- update_link_reg(hw, &link); +- if (link.link_status) +- break; +- rte_delay_ms(CHECK_INTERVAL); +- } while (--rep_cnt); +- can_be_set = !!link.link_status; +- } +- +- if (can_be_set) { +- status = i40e_aq_set_mac_config(hw, size, TRUE, 0, false, NULL); +- if (status != I40E_SUCCESS) +- PMD_DRV_LOG(ERR, "Failed to set max frame size at port level"); +- } else { +- PMD_DRV_LOG(ERR, "Set max frame size at port level not applicable on link down"); +- } +-} +- + RTE_LOG_REGISTER_SUFFIX(i40e_logtype_init, init, NOTICE); + RTE_LOG_REGISTER_SUFFIX(i40e_logtype_driver, driver, NOTICE); + #ifdef RTE_ETHDEV_DEBUG_RX diff --git a/SPECS/openvswitch3.1.spec b/SPECS/openvswitch3.1.spec index bbe23f5..6a2445f 100644 --- a/SPECS/openvswitch3.1.spec +++ b/SPECS/openvswitch3.1.spec @@ -63,7 +63,7 @@ Summary: Open vSwitch Group: System Environment/Daemons daemon/database/utilities URL: http://www.openvswitch.org/ Version: 3.1.0 -Release: 2%{?dist} +Release: 18%{?dist} # Nearly all of openvswitch is ASL 2.0. The bugtool is LGPLv2+, and the # lib/sflow*.[ch] files are SISSL @@ -757,6 +757,129 @@ exit 0 %endif %changelog +* Wed Apr 05 2023 Open vSwitch CI - 3.1.0-18 +- Merging upstream branch-3.1 [RH git: 3607c5dbe6] + Commit list: + 8019b5896e conntrack-tp: Fix clang warning. + + +* Tue Apr 04 2023 Timothy Redaelli - 3.1.0-17 +- ofproto-dpif-xlate: Always mask ip proto field. [RH git: 4868ac0e85] (#2134873) + The ofproto layer currently treats nw_proto field as overloaded to mean + both that a proper nw layer exists, as well as the value contained in + the header for the nw proto. However, this is incorrect behavior as + relevant standards permit that any value, including '0' should be treated + as a valid value. + + Because of this overload, when the ofproto layer builds action list for + a packet with nw_proto of 0, it won't build the complete action list that + we expect to be built for the packet. That will cause a bad behavior + where all packets passing the datapath will fall into an incomplete + action set. + + The fix here is to unwildcard nw_proto, allowing us to preserve setting + actions for protocols which we know have support for the actions we + program. This means that a traffic which contains nw_proto == 0 cannot + cause connectivity breakage with other traffic on the link. + + Reported-by: David Marchand + Reported-at: https://bugzilla.redhat.com/show_bug.cgi?id=2134873 + Acked-by: Ilya Maximets + Signed-off-by: Aaron Conole + + +* Mon Apr 03 2023 Open vSwitch CI - 3.1.0-16 +- Merging upstream branch-3.1 [RH git: a911cf94ce] + Commit list: + b184a68a20 netdev-offload-tc: Del ufid mapping if device not exist. + + +* Thu Mar 30 2023 Open vSwitch CI - 3.1.0-15 +- Merging upstream branch-3.1 [RH git: e6b73e0565] + Commit list: + 037e2d916 db-ctl-base: Partially revert b8bf410a5. + + +* Wed Mar 29 2023 Open vSwitch CI - 3.1.0-14 +- Merging upstream branch-3.1 [RH git: 407a020d7a] + Commit list: + fee5ea720 netdev-tc-offloads: Fix misaligned 8 byte read. + + +* Wed Mar 29 2023 Open vSwitch CI - 3.1.0-13 +- Merging dpdk submodule [RH git: 541c8eefbd] + Commit list: + 3812e23f6b net/i40e: revert link status check on device start (#2173794) + + +* Mon Mar 27 2023 Open vSwitch CI - 3.1.0-12 +- Merging upstream branch-3.1 [RH git: fbc56ee622] + Commit list: + fff04b838 ovs-thread: Fix cpus not read for the first 10s. + b2b467b3a dpif-netlink: Always create at least 1 handler. + + +* Wed Mar 22 2023 Open vSwitch CI - 3.1.0-11 +- Merging upstream branch-3.1 [RH git: 28d55c3fb3] + Commit list: + 0f30961e0 netdev-offload-tc: Fix parse_tc_flower_to_actions() reporting errors. + + +* Wed Mar 15 2023 Open vSwitch CI - 3.1.0-10 +- Merging upstream branch-3.1 [RH git: a9561af3dc] + Commit list: + 3f50354a9 vswitch: Add missing documentation for "ct_flush" capability. + 504f8c859 dpctl: Fix flush-conntrack with datapath as argument. + 24b47072f ofproto-dpif-upcall: Wait for valid hw flow stats before applying min-revalidate-pps. + + +* Tue Mar 14 2023 Open vSwitch CI - 3.1.0-9 +- Merging upstream branch-3.1 [RH git: 398009fd5f] + Commit list: + 9f9b4de38 system-traffic: Fix conntrack test cases which are failing with af_xdp. + + +* Tue Mar 07 2023 Open vSwitch CI - 3.1.0-8 +- Merging upstream branch-3.1 [RH git: d17880bde7] + Commit list: + dcdff1f42 netdev-windows: Add checking when creating netdev with system type on Windows + 39a7a18c3 ofproto-dpif-upcall: Include hardware offloaded flows in total flows. + f7378bc7f ofproto-dpif-upcall: Reset ukey's last stats value if the datapath changed. + + +* Tue Feb 28 2023 Open vSwitch CI - 3.1.0-7 +- Merging upstream branch-3.1 [RH git: 54a2eb1fda] + Commit list: + 694c7b4e0 classifier: Fix missing masks on a final stage with ports trie. + + +* Mon Feb 27 2023 Open vSwitch CI - 3.1.0-6 +- Merging upstream branch-3.1 [RH git: 29cb42e1cd] + Commit list: + 87249e136 ofproto: Fix re-creation of tunnel backing interfaces on restart. + + +* Thu Feb 23 2023 Timothy Redaelli - 3.1.0-5 +- redhat: use git rev-parse instead of git name-rev [RH git: 1b47f9b3ae] + Currenty, git name-rev --name-only HEAD is used in order to get the + current branch, but it doesn't work correctly if a tag is created so use + git rev-parse --symbolic-full-name --abbrev-ref instead. + + +* Tue Feb 21 2023 Open vSwitch CI - 3.1.0-4 +- Merging upstream branch-3.1 [RH git: 7992626cb1] + Commit list: + cb4eecd01 conntrack: Fix conntrack_clean may access the same exp_list each time. + 372e1a835 ovs-actions: Correct typo in ovs-actions man page. + + +* Tue Feb 21 2023 Open vSwitch CI - 3.1.0-3 +- Merging upstream branch-3.1 [RH git: 1cdfcbc3be] + Commit list: + 9c34fc2f6 ofproto-ipfix: Use per-domain template timeouts. + b72a7f925 Prepare for 3.1.1. + + * Mon Feb 20 2023 Timothy Redaelli - 3.1.0-2 - redhat: add a workaround for meson [RH git: 4b77c5096a] Currently, fast-datapath-rhel-8 is aligned to RHEL 8.0, with an