Introduce the capability to actively refresh mac_binding entries available in OFTABLE_MAC_BINDING table when they are inactive for more than the configured threshold. Add the OFTABLE_MAC_BINDING table stats monitoring. This feature avoids possible unnecessary mac_entry removals if the destination is still alive but inactive for more than the configured value.
Acked-by: Mark Michelson <mmich...@redhat.com> Reported-at: https://issues.redhat.com/browse/FDP-1135 Signed-off-by: Lorenzo Bianconi <lorenzo.bianc...@redhat.com> --- controller/mac-cache.c | 111 +++++++++++++++++- controller/mac-cache.h | 26 ++++- controller/ovn-controller.c | 40 +++++-- controller/pinctrl.c | 8 +- controller/pinctrl.h | 7 ++ controller/statctrl.c | 25 +++- tests/ovn.at | 221 ++++++++++++++++++++++++++++++++++++ tests/system-ovn.at | 108 ++++++++++++++++++ 8 files changed, 521 insertions(+), 25 deletions(-) diff --git a/controller/mac-cache.c b/controller/mac-cache.c index 9d05c1950..5c4e10810 100644 --- a/controller/mac-cache.c +++ b/controller/mac-cache.c @@ -16,6 +16,7 @@ #include <config.h> #include <stdbool.h> +#include "lflow.h" #include "lib/mac-binding-index.h" #include "local_data.h" #include "lport.h" @@ -24,6 +25,7 @@ #include "openvswitch/vlog.h" #include "ovn/logical-fields.h" #include "ovn-sb-idl.h" +#include "pinctrl.h" VLOG_DEFINE_THIS_MODULE(mac_cache); @@ -160,17 +162,25 @@ mac_cache_thresholds_clear(struct mac_cache_data *data) /* MAC binding. */ void mac_binding_add(struct hmap *map, struct mac_binding_data mb_data, - const struct sbrec_mac_binding *smb, long long timestamp) + const struct sbrec_mac_binding *smb, + const struct sbrec_port_binding *pb, + long long timestamp) { struct mac_binding *mb = mac_binding_find(map, &mb_data); if (!mb) { - mb = xmalloc(sizeof *mb); + mb = xzalloc(sizeof *mb); hmap_insert(map, &mb->hmap_node, mac_binding_data_hash(&mb_data)); } mb->data = mb_data; mb->sbrec = smb; mb->timestamp = timestamp; + if (pb) { + destroy_lport_addresses(&mb->laddr); + if (extract_lsp_addresses(pb->mac[0], &mb->laddr)) { + mb->tunnel_key = pb->tunnel_key; + } + } mac_binding_update_log("Added", &mb_data, false, NULL, 0, 0); } @@ -179,6 +189,7 @@ mac_binding_remove(struct hmap *map, struct mac_binding *mb) { mac_binding_update_log("Removed", &mb->data, false, NULL, 0, 0); hmap_remove(map, &mb->hmap_node); + destroy_lport_addresses(&mb->laddr); free(mb); } @@ -237,6 +248,7 @@ mac_bindings_clear(struct hmap *map) { struct mac_binding *mb; HMAP_FOR_EACH_POP (mb, hmap_node, map) { + destroy_lport_addresses(&mb->laddr); free(mb); } } @@ -399,7 +411,8 @@ mac_binding_update_log(const char *action, } void -mac_binding_stats_run(struct ovs_list *stats_list, uint64_t *req_delay, +mac_binding_stats_run(struct rconn *swconn OVS_UNUSED, + struct ovs_list *stats_list, uint64_t *req_delay, void *data) { struct mac_cache_data *cache_data = data; @@ -495,8 +508,9 @@ fdb_update_log(const char *action, } void -fdb_stats_run(struct ovs_list *stats_list, uint64_t *req_delay, - void *data) +fdb_stats_run(struct rconn *swconn OVS_UNUSED, + struct ovs_list *stats_list, + uint64_t *req_delay, void *data) { struct mac_cache_data *cache_data = data; long long timewall_now = time_wall_msec(); @@ -847,3 +861,90 @@ buffered_packets_db_lookup(struct buffered_packets *bp, struct ds *ip, eth_addr_from_string(smb->mac, mac); } + +void +mac_binding_probe_stats_process_flow_stats( + struct ovs_list *stats_list, + struct ofputil_flow_stats *ofp_stats) +{ + struct mac_cache_stats *stats = xmalloc(sizeof *stats); + + stats->idle_age_ms = ofp_stats->idle_age * 1000; + stats->data.mb = (struct mac_binding_data) { + .cookie = ntohll(ofp_stats->cookie), + /* The port_key must be zero to match mac_binding_data_from_sbrec. */ + .port_key = 0, + .dp_key = ntohll(ofp_stats->match.flow.metadata), + }; + + if (ofp_stats->match.flow.regs[0]) { + stats->data.mb.ip = + in6_addr_mapped_ipv4(htonl(ofp_stats->match.flow.regs[0])); + } else { + ovs_be128 ip6 = hton128(flow_get_xxreg(&ofp_stats->match.flow, 1)); + memcpy(&stats->data.mb.ip, &ip6, sizeof stats->data.mb.ip); + } + + ovs_list_push_back(stats_list, &stats->list_node); +} + +void +mac_binding_probe_stats_run( + struct rconn *swconn, + struct ovs_list *stats_list, + uint64_t *req_delay, void *data) +{ + long long timewall_now = time_wall_msec(); + struct mac_cache_data *cache_data = data; + + struct mac_cache_stats *stats; + LIST_FOR_EACH_POP (stats, list_node, stats_list) { + struct mac_binding *mb = mac_binding_find(&cache_data->mac_bindings, + &stats->data.mb); + if (!mb) { + mac_binding_update_log("Probe: not found in the cache:", + &stats->data.mb, false, NULL, 0, 0); + free(stats); + continue; + } + + struct mac_cache_threshold *threshold = + mac_cache_threshold_find(cache_data, mb->data.dp_key); + uint64_t since_updated_ms = timewall_now - mb->sbrec->timestamp; + const struct sbrec_mac_binding *sbrec = mb->sbrec; + mac_binding_update_log("Probe: trying to refresh", &mb->data, true, + threshold, stats->idle_age_ms, + since_updated_ms); + + if (stats->idle_age_ms > threshold->value) { + free(stats); + continue; + } + + if (since_updated_ms < threshold->value / 2) { + free(stats); + continue; + } + + if (!mb->laddr.n_ipv4_addrs && !mb->laddr.n_ipv6_addrs) { + free(stats); + continue; + } + + struct in6_addr local = mb->laddr.n_ipv4_addrs + ? in6_addr_mapped_ipv4(mb->laddr.ipv4_addrs[0].addr) + : mb->laddr.ipv6_addrs[0].addr; + send_self_originated_neigh_packet(swconn, + sbrec->datapath->tunnel_key, + mb->tunnel_key, mb->laddr.ea, + &local, &mb->data.ip, + OFTABLE_LOCAL_OUTPUT); + free(stats); + } + + mac_cache_update_req_delay(&cache_data->thresholds, req_delay); + *req_delay = *req_delay / 2; + if (*req_delay) { + VLOG_DBG("MAC probe binding statistics delay: %"PRIu64, *req_delay); + } +} diff --git a/controller/mac-cache.h b/controller/mac-cache.h index 40d7b9c25..eb2ad7b55 100644 --- a/controller/mac-cache.h +++ b/controller/mac-cache.h @@ -70,6 +70,10 @@ struct mac_binding { const struct sbrec_mac_binding *sbrec; /* User specified timestamp (in ms) */ long long timestamp; + /* SB MAC binding logical port address. */ + struct lport_addresses laddr; + /* SB Port binding tunnel key. */ + int64_t tunnel_key; }; struct fdb_data { @@ -147,7 +151,9 @@ mac_binding_data_init(struct mac_binding_data *data, } void mac_binding_add(struct hmap *map, struct mac_binding_data mb_data, - const struct sbrec_mac_binding *, long long timestamp); + const struct sbrec_mac_binding *smb, + const struct sbrec_port_binding *pb, + long long timestamp); void mac_binding_remove(struct hmap *map, struct mac_binding *mb); @@ -180,15 +186,17 @@ void mac_binding_stats_process_flow_stats(struct ovs_list *stats_list, struct ofputil_flow_stats *ofp_stats); -void mac_binding_stats_run(struct ovs_list *stats_list, uint64_t *req_delay, - void *data); +void mac_binding_stats_run(struct rconn *swconn OVS_UNUSED, + struct ovs_list *stats_list, + uint64_t *req_delay, void *data); /* FDB stat processing. */ void fdb_stats_process_flow_stats(struct ovs_list *stats_list, struct ofputil_flow_stats *ofp_stats); -void fdb_stats_run(struct ovs_list *stats_list, uint64_t *req_delay, - void *data); +void fdb_stats_run(struct rconn *swconn OVS_UNUSED, + struct ovs_list *stats_list, + uint64_t *req_delay, void *data); void mac_cache_stats_destroy(struct ovs_list *stats_list); @@ -221,4 +229,12 @@ bool buffered_packets_ctx_is_ready_to_send(struct buffered_packets_ctx *ctx); bool buffered_packets_ctx_has_packets(struct buffered_packets_ctx *ctx); +void mac_binding_probe_stats_process_flow_stats( + struct ovs_list *stats_list, + struct ofputil_flow_stats *ofp_stats); + +void mac_binding_probe_stats_run(struct rconn *swconn, + struct ovs_list *stats_list, + uint64_t *req_delay, void *data); + #endif /* controller/mac-cache.h */ diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c index 081411cba..5749f316b 100644 --- a/controller/ovn-controller.c +++ b/controller/ovn-controller.c @@ -3054,14 +3054,17 @@ en_lb_data_cleanup(void *data) static void mac_binding_add_sb(struct mac_cache_data *data, - const struct sbrec_mac_binding *smb) + const struct sbrec_mac_binding *smb, + struct ovsdb_idl_index *sbrec_port_binding_by_name) { struct mac_binding_data mb_data; if (!mac_binding_data_from_sbrec(&mb_data, smb)) { return; } - mac_binding_add(&data->mac_bindings, mb_data, smb, 0); + const struct sbrec_port_binding *pb = lport_lookup_by_name( + sbrec_port_binding_by_name, smb->logical_port); + mac_binding_add(&data->mac_bindings, mb_data, smb, pb, 0); } static void @@ -3113,7 +3116,8 @@ fdb_remove_sb(struct mac_cache_data *data, const struct sbrec_fdb *sfdb) static void mac_cache_mb_handle_for_datapath(struct mac_cache_data *data, const struct sbrec_datapath_binding *dp, - struct ovsdb_idl_index *sbrec_mb_by_dp) + struct ovsdb_idl_index *sbrec_mb_by_dp, + struct ovsdb_idl_index *sbrec_pb_by_name) { bool has_threshold = mac_cache_threshold_find(data, dp->tunnel_key); @@ -3124,7 +3128,7 @@ mac_cache_mb_handle_for_datapath(struct mac_cache_data *data, const struct sbrec_mac_binding *mb; SBREC_MAC_BINDING_FOR_EACH_EQUAL (mb, mb_index_row, sbrec_mb_by_dp) { if (has_threshold) { - mac_binding_add_sb(data, mb); + mac_binding_add_sb(data, mb, sbrec_pb_by_name); } else { mac_binding_remove_sb(data, mb); } @@ -3185,6 +3189,10 @@ en_mac_cache_run(struct engine_node *node, void *data) engine_ovsdb_node_get_index( engine_get_input("SB_fdb", node), "dp_key"); + struct ovsdb_idl_index *sbrec_port_binding_by_name = + engine_ovsdb_node_get_index( + engine_get_input("SB_port_binding", node), + "name"); mac_cache_thresholds_clear(cache_data); mac_bindings_clear(&cache_data->mac_bindings); @@ -3199,7 +3207,8 @@ en_mac_cache_run(struct engine_node *node, void *data) mac_cache_threshold_add(cache_data, sbrec_dp); mac_cache_mb_handle_for_datapath(cache_data, sbrec_dp, - sbrec_mb_by_dp); + sbrec_mb_by_dp, + sbrec_port_binding_by_name); mac_cache_fdb_handle_for_datapath(cache_data, sbrec_dp, sbrec_fdb_by_dp_key); } @@ -3216,6 +3225,10 @@ mac_cache_sb_mac_binding_handler(struct engine_node *node, void *data) const struct sbrec_mac_binding_table *mb_table = EN_OVSDB_GET(engine_get_input("SB_mac_binding", node)); size_t previous_size = hmap_count(&cache_data->mac_bindings); + struct ovsdb_idl_index *sbrec_port_binding_by_name = + engine_ovsdb_node_get_index( + engine_get_input("SB_port_binding", node), + "name"); const struct sbrec_mac_binding *sbrec_mb; SBREC_MAC_BINDING_TABLE_FOR_EACH_TRACKED (sbrec_mb, mb_table) { @@ -3231,7 +3244,8 @@ mac_cache_sb_mac_binding_handler(struct engine_node *node, void *data) if (mac_cache_threshold_find(cache_data, sbrec_mb->datapath->tunnel_key)) { - mac_binding_add_sb(cache_data, sbrec_mb); + mac_binding_add_sb(cache_data, sbrec_mb, + sbrec_port_binding_by_name); } } @@ -3292,6 +3306,10 @@ mac_cache_runtime_data_handler(struct engine_node *node, void *data OVS_UNUSED) engine_ovsdb_node_get_index( engine_get_input("SB_fdb", node), "dp_key"); + struct ovsdb_idl_index *sbrec_port_binding_by_name = + engine_ovsdb_node_get_index( + engine_get_input("SB_port_binding", node), + "name"); /* There are no tracked data. Fall back to full recompute. */ if (!rt_data->tracked) { @@ -3312,7 +3330,8 @@ mac_cache_runtime_data_handler(struct engine_node *node, void *data OVS_UNUSED) HMAP_FOR_EACH (tdp, node, &rt_data->tracked_dp_bindings) { mac_cache_mb_handle_for_datapath(cache_data, tdp->dp, - sbrec_mb_by_dp); + sbrec_mb_by_dp, + sbrec_port_binding_by_name); mac_cache_fdb_handle_for_datapath(cache_data, tdp->dp, sbrec_fdb_by_dp_key); @@ -3342,6 +3361,10 @@ mac_cache_sb_datapath_binding_handler(struct engine_node *node, void *data) engine_ovsdb_node_get_index( engine_get_input("SB_fdb", node), "dp_key"); + struct ovsdb_idl_index *sbrec_port_binding_by_name = + engine_ovsdb_node_get_index( + engine_get_input("SB_port_binding", node), + "name"); size_t previous_mb_size = hmap_count(&cache_data->mac_bindings); size_t previous_fdb_size = hmap_count(&cache_data->fdbs); @@ -3365,7 +3388,8 @@ mac_cache_sb_datapath_binding_handler(struct engine_node *node, void *data) SBREC_DATAPATH_BINDING_TABLE_FOR_EACH_TRACKED (sbrec_dp, dp_table) { mac_cache_mb_handle_for_datapath(cache_data, sbrec_dp, - sbrec_mb_by_dp); + sbrec_mb_by_dp, + sbrec_port_binding_by_name); mac_cache_fdb_handle_for_datapath(cache_data, sbrec_dp, sbrec_fdb_by_dp_key); diff --git a/controller/pinctrl.c b/controller/pinctrl.c index 47c4bf78b..1957ff771 100644 --- a/controller/pinctrl.c +++ b/controller/pinctrl.c @@ -4718,7 +4718,7 @@ pinctrl_handle_put_mac_binding(const struct flow *md, ? random_range(MAX_MAC_BINDING_DELAY_MSEC) + 1 : 0; long long timestamp = time_msec() + delay; - mac_binding_add(&put_mac_bindings, mb_data, NULL, timestamp); + mac_binding_add(&put_mac_bindings, mb_data, NULL, NULL, timestamp); /* We can send the buffered packet once the main ovn-controller * thread calls pinctrl_run() and it writes the mac_bindings stored @@ -4924,7 +4924,7 @@ run_buffered_binding(const struct sbrec_mac_binding_table *mac_binding_table, continue; } - mac_binding_add(&recent_mbs, mb_data, smb, 0); + mac_binding_add(&recent_mbs, mb_data, smb, pb, 0); const char *redirect_port = smap_get(&pb->options, "chassis-redirect-port"); @@ -4941,7 +4941,7 @@ run_buffered_binding(const struct sbrec_mac_binding_table *mac_binding_table, /* Add the same entry also for chassisredirect port as the buffered * traffic might be buffered on the cr port. */ mb_data.port_key = pb->tunnel_key; - mac_binding_add(&recent_mbs, mb_data, smb, 0); + mac_binding_add(&recent_mbs, mb_data, smb, NULL, 0); } buffered_packets_ctx_run(&buffered_packets_ctx, &recent_mbs, @@ -5303,7 +5303,7 @@ send_garp_rarp_delete(const char *lport) notify_pinctrl_handler(); } -static void +void send_self_originated_neigh_packet(struct rconn *swconn, uint32_t dp_key, uint32_t port_key, struct eth_addr eth, diff --git a/controller/pinctrl.h b/controller/pinctrl.h index 8459f4f53..e33d7cbf5 100644 --- a/controller/pinctrl.h +++ b/controller/pinctrl.h @@ -31,6 +31,7 @@ struct ovsdb_idl_index; struct ovsdb_idl_txn; struct ovsrec_bridge; struct ovsrec_open_vswitch_table; +struct rconn; struct sbrec_chassis; struct sbrec_dns_table; struct sbrec_controller_event_table; @@ -77,4 +78,10 @@ struct activated_port { void tag_port_as_activated_in_engine(struct activated_port *ap); struct ovs_list *get_ports_to_activate_in_engine(void); bool pinctrl_is_port_activated(int64_t dp_key, int64_t port_key); +void send_self_originated_neigh_packet(struct rconn *swconn, + uint32_t dp_key, uint32_t port_key, + struct eth_addr eth, + struct in6_addr *local, + struct in6_addr *target, + uint8_t table_id); #endif /* controller/pinctrl.h */ diff --git a/controller/statctrl.c b/controller/statctrl.c index d3c70ccba..6052d2884 100644 --- a/controller/statctrl.c +++ b/controller/statctrl.c @@ -39,6 +39,7 @@ VLOG_DEFINE_THIS_MODULE(statctrl); enum stat_type { STATS_MAC_BINDING = 0, STATS_FDB, + STATS_MAC_BINDING_PROBE, STATS_MAX, }; @@ -62,7 +63,8 @@ struct stats_node { struct ofputil_flow_stats *ofp_stats); /* Function to process the parsed stats. * This function runs in main thread locked behind mutex. */ - void (*run)(struct ovs_list *stats_list, uint64_t *req_delay, void *data); + void (*run)(struct rconn *swconn, struct ovs_list *stats_list, + uint64_t *req_delay, void *data); }; #define STATS_NODE(NAME, REQUEST, DESTROY, PROCESS, RUN) \ @@ -145,6 +147,18 @@ statctrl_init(void) STATS_NODE(FDB, fdb_request, mac_cache_stats_destroy, fdb_stats_process_flow_stats, fdb_stats_run); + struct ofputil_flow_stats_request mac_binding_probe_request = { + .cookie = htonll(0), + .cookie_mask = htonll(0), + .out_port = OFPP_ANY, + .out_group = OFPG_ANY, + .table_id = OFTABLE_MAC_BINDING, + }; + STATS_NODE(MAC_BINDING_PROBE, mac_binding_probe_request, + mac_cache_stats_destroy, + mac_binding_probe_stats_process_flow_stats, + mac_binding_probe_stats_run); + statctrl_ctx.thread = ovs_thread_create("ovn_statctrl", statctrl_thread_handler, &statctrl_ctx); @@ -158,7 +172,11 @@ statctrl_run(struct ovsdb_idl_txn *ovnsb_idl_txn, return; } - void *node_data[STATS_MAX] = {mac_cache_data, mac_cache_data}; + void *node_data[STATS_MAX] = { + mac_cache_data, + mac_cache_data, + mac_cache_data + }; bool schedule_updated = false; long long now = time_msec(); @@ -168,7 +186,8 @@ statctrl_run(struct ovsdb_idl_txn *ovnsb_idl_txn, struct stats_node *node = &statctrl_ctx.nodes[i]; uint64_t prev_delay = node->request_delay; - node->run(&node->stats_list, &node->request_delay, node_data[i]); + node->run(statctrl_ctx.swconn, &node->stats_list, + &node->request_delay, node_data[i]); schedule_updated |= statctrl_update_next_request_timestamp(node, now, prev_delay); diff --git a/tests/ovn.at b/tests/ovn.at index 509a0c6ce..5d9bc9310 100644 --- a/tests/ovn.at +++ b/tests/ovn.at @@ -36079,6 +36079,227 @@ OVN_CLEANUP([hv1]) AT_CLEANUP ]) +OVN_FOR_EACH_NORTHD([ +AT_SETUP([MAC binding aging - probing]) +AT_SKIP_IF([test $HAVE_SCAPY = no]) +ovn_start + +send_udp() { + local hv=$1 dev=$2 hdst=$3 hsrc=$4 idst=$5 isrc=$6 + local packet=$(fmt_pkt "Ether(dst='${hdst}', src='${hsrc}')/ \ + IP(dst='${idst}', src='${isrc}')/UDP()") + as $hv ovs-appctl netdev-dummy/receive $dev $packet +} + +dump_arp() { + local op=$1 eth_src=$2 eth_dst=$3 spa=$4 tpa=$5 hwdst=$6 + + local packet=$(fmt_pkt "Ether(dst='${eth_dst}', src='${eth_src}')/ \ + ARP(op=$op, hwsrc='${eth_src}', hwdst='${hwdst}', \ + psrc='${spa}', pdst='${tpa}')") + echo $packet +} + +aging_th=5 +net_add n1 +sim_add hv1 +as hv1 +check ovs-vsctl add-br br-phys +ovn_attach n1 br-phys 192.168.0.1 +ovn-appctl -t ovn-controller vlog/set mac_cache:file:dbg pinctrl:file:dbg + +check ovn-nbctl \ + -- ls-add ls1 \ + -- ls-add ls2 \ + -- lr-add lr \ + -- set logical_router lr options:mac_binding_age_threshold=$aging_th \ + -- lrp-add lr lr-ls1 00:00:00:00:10:00 192.168.10.1/24 \ + -- lrp-add lr lr-ls2 00:00:00:00:20:00 192.168.20.1/24 \ + -- lsp-add ls1 ls1-lr \ + -- lsp-set-type ls1-lr router \ + -- lsp-set-addresses ls1-lr router \ + -- lsp-set-options ls1-lr router-port=lr-ls1 \ + -- lsp-add ls1 vif1 \ + -- lsp-set-addresses vif1 "unknown" \ + -- lsp-add ls2 ls2-lr \ + -- lsp-set-type ls2-lr router \ + -- lsp-set-addresses ls2-lr router \ + -- lsp-set-options ls2-lr router-port=lr-ls2 \ + -- lsp-add ls2 vif2 \ + -- lsp-set-addresses vif2 "unknown" + +check ovs-vsctl \ + -- add-port br-int vif1 \ + -- set interface vif1 external-ids:iface-id=vif1 \ + options:tx_pcap=hv1/vif1-tx.pcap options:rxq_pcap=hv1/vif1-rx.pcap \ + -- add-port br-int vif2 \ + -- set interface vif2 external-ids:iface-id=vif2 \ + options:tx_pcap=hv1/vif2-tx.pcap options:rxq_pcap=hv1/vif2-rx.pcap + +OVN_POPULATE_ARP +wait_for_ports_up +check ovn-nbctl --wait=hv sync + +# Wait for pinctrl thread to be connected. +OVS_WAIT_UNTIL([grep pinctrl hv1/ovn-controller.log | grep -q connected]) + +send_garp hv1 vif1 2 00:00:00:00:10:1a ff:ff:ff:ff:ff:ff 192.168.10.100 192.168.10.100 +wait_row_count mac_binding 1 ip="192.168.10.100" logical_port="lr-ls1" + +send_garp hv1 vif2 2 00:00:00:00:10:1b ff:ff:ff:ff:ff:ff 192.168.20.100 192.168.20.100 +wait_row_count mac_binding 1 ip="192.168.20.100" logical_port="lr-ls2" + +# Send UDP traffic to create entries in OFTABLE_MAC_BINDING. +# Please note we are using a different src mac address with respect to the GARP +# one. +send_udp hv1 vif1 00:00:00:00:10:00 00:00:00:00:10:2a 192.168.20.100 192.168.10.100 +send_udp hv1 vif2 00:00:00:00:20:00 00:00:00:00:10:2b 192.168.10.100 192.168.20.100 + +OVS_WAIT_UNTIL([$(ovs-ofctl dump-flows br-int table=OFTABLE_MAC_BINDING | \ + sed 's/reg15=0x.,metadata=0x./reg15=<cleared>,metadata=<cleared>/g' | \ + grep -q "reg0=0xc0a80a64,reg15=<cleared>,metadata=<cleared> actions=mod_dl_dst:00:00:00:00:10:1a")]) +OVS_WAIT_UNTIL([$(ovs-ofctl dump-flows br-int table=OFTABLE_MAC_BINDING | \ + sed 's/reg15=0x.,metadata=0x./reg15=<cleared>,metadata=<cleared>/g' | \ + grep -q "reg0=0xc0a81464,reg15=<cleared>,metadata=<cleared> actions=mod_dl_dst:00:00:00:00:10:1b")]) + +ts0=$(fetch_column Mac_Binding timestamp ip=192.168.10.100) +# Wait until entries in OFTABLE_MAC_CACHE_USE are stale. +OVS_WAIT_UNTIL([test $(ovs-ofctl dump-flows br-int table=OFTABLE_MAC_CACHE_USE | \ + awk '/nw_src=192.168.10.100/{print substr($6,10,1)}') -ge ${aging_th}]) + +# Send UDP traffic to refresh entries in OFTABLE_MAC_BINDING. +send_udp hv1 vif1 00:00:00:00:10:00 00:00:00:00:10:2a 192.168.20.100 192.168.10.100 +send_udp hv1 vif2 00:00:00:00:20:00 00:00:00:00:10:2b 192.168.10.100 192.168.20.100 +# Wait for ARP requests to be generated. +dump_arp 1 00:00:00:00:20:00 ff:ff:ff:ff:ff:ff 192.168.20.1 192.168.20.100 00:00:00:00:00:00 > expected1 +dump_arp 1 00:00:00:00:10:00 ff:ff:ff:ff:ff:ff 192.168.10.1 192.168.10.100 00:00:00:00:00:00 > expected2 + +OVN_CHECK_PACKETS_CONTAIN([hv1/vif1-tx.pcap], [expected2]) +# Check MAC_Binding timestamp is updated receiving the ARP replay. +send_garp hv1 vif1 2 00:00:00:00:10:1a 00:00:00:00:10:00 192.168.10.100 192.168.10.1 +OVS_WAIT_UNTIL([test $(fetch_column Mac_Binding timestamp ip=192.168.10.100) -gt $ts0]) + +# Refresh OFTABLE_MAC_BINDING entires. +send_udp hv1 vif1 00:00:00:00:10:00 00:00:00:00:10:2a 192.168.20.100 192.168.10.100 +send_udp hv1 vif2 00:00:00:00:20:00 00:00:00:00:10:2b 192.168.10.100 192.168.20.100 +# Check OVN tries to refresh MAC Binding entries. +dump_arp 1 00:00:00:00:10:00 ff:ff:ff:ff:ff:ff 192.168.10.1 192.168.10.100 00:00:00:00:00:00 >> expected2 +wait_row_count mac_binding 0 ip="192.168.10.100" logical_port="lr-ls1" +OVN_CHECK_PACKETS_CONTAIN([hv1/vif1-tx.pcap], [expected2]) + +dump_arp 1 00:00:00:00:20:00 ff:ff:ff:ff:ff:ff 192.168.20.1 192.168.20.100 00:00:00:00:00:00 >> expected1 +wait_row_count mac_binding 0 ip="192.168.20.100" logical_port="lr-ls2" +OVN_CHECK_PACKETS_CONTAIN([hv1/vif2-tx.pcap], [expected1]) + +OVN_CLEANUP([hv1]) +AT_CLEANUP +]) + +OVN_FOR_EACH_NORTHD([ +AT_SETUP([MAC binding aging - probing GW router Dynamic Neigh]) +AT_SKIP_IF([test $HAVE_SCAPY = no]) +ovn_start + +send_imcp_echo_req() { + local hv=$1 dev=$2 hdst=$3 hsrc=$4 idst=$5 isrc=$6 + local packet=$(fmt_pkt "Ether(dst='${hdst}', src='${hsrc}')/ \ + IP(dst='${idst}', src='${isrc}')/ICMP()") + as $hv ovs-appctl netdev-dummy/receive $dev $packet +} + +dump_icmp() { + local hdst=$1 hsrc=$2 idst=$3 isrc=$4 ttl=$5 type=$6 chksum=$7 + local packet=$(fmt_pkt "Ether(dst='${hdst}', src='${hsrc}')/ \ + IP(dst='${idst}', src='${isrc}', ttl=${ttl})/\ + ICMP(type=${type}, chksum=${chksum})") + + echo $packet +} + +aging_th=5 +net_add n1 +sim_add hv1 +as hv1 +check ovs-vsctl add-br br-phys +ovn_attach n1 br-phys 192.168.0.1 +ovn-appctl -t ovn-controller vlog/set mac_cache:file:dbg pinctrl:file:dbg + +check ovn-nbctl \ + -- ls-add public \ + -- ls-add join \ + -- lr-add lr \ + -- lr-add gw \ + -- set Logical_Router gw options:chassis="hv1" \ + -- set logical_router gw options:mac_binding_age_threshold=$aging_th \ + -- set logical_router gw options:dynamic_neigh_routers=true \ + -- lrp-add gw gw-public 00:00:00:00:10:00 192.168.10.1/24 \ + -- lrp-add gw gw-join 00:00:00:00:20:00 192.168.20.1/24 \ + -- lsp-add public public-gw \ + -- lsp-set-type public-gw router \ + -- lsp-set-addresses public-gw router \ + -- lsp-set-options public-gw router-port=gw-public \ + -- lsp-add public public \ + -- lsp-set-addresses public "unknown" \ + -- lsp-add join join-gw \ + -- lsp-set-type join-gw router \ + -- lsp-set-addresses join-gw router \ + -- lsp-set-options join-gw router-port=gw-join \ + -- lrp-add lr lr-join 00:00:00:00:30:00 192.168.20.2/24 \ + -- lsp-add join join-lr \ + -- lsp-set-type join-lr router \ + -- lsp-set-addresses join-lr router \ + -- lsp-set-options join-lr router-port=lr-join \ + -- lr-route-add lr 0.0.0.0/0 192.168.20.1 + +check ovs-vsctl \ + -- add-port br-int public \ + -- set interface public external-ids:iface-id=public \ + options:tx_pcap=hv1/public-tx.pcap options:rxq_pcap=hv1/public-rx.pcap + +OVN_POPULATE_ARP +wait_for_ports_up +check ovn-nbctl --wait=hv sync + +dnl Wait for pinctrl thread to be connected. +OVS_WAIT_UNTIL([grep pinctrl hv1/ovn-controller.log | grep -q connected]) + +# Send a GARP for an external device to populate MAC Binding table. +send_garp hv1 public 2 00:00:00:00:10:1a ff:ff:ff:ff:ff:ff 192.168.10.100 192.168.10.100 +wait_row_count mac_binding 1 ip="192.168.10.100" logical_port="gw-public" + +# Send ICMP echo request to create entries in OFTABLE_MAC_BINDING. +send_imcp_echo_req hv1 public 00:00:00:00:10:00 00:00:00:00:10:1a 192.168.20.2 192.168.10.100 +dump_icmp 00:00:00:00:10:1a 00:00:00:00:10:00 192.168.10.100 192.168.20.2 253 0 0 > expected +wait_row_count mac_binding 1 ip="192.168.20.2" logical_port="gw-join" +ts0=$(fetch_column Mac_Binding timestamp ip=192.168.20.2) +OVN_CHECK_PACKETS_CONTAIN([hv1/public-tx.pcap], [expected]) +n_arp=$(ovs-ofctl dump-flows br-int table=OFTABLE_MAC_CACHE_USE |awk '/arp_spa=192.168.20.2/{print substr($4,11,1)}') + +OVS_WAIT_UNTIL([$(ovs-ofctl dump-flows br-int table=OFTABLE_MAC_BINDING | \ + sed 's/reg15=0x.,metadata=0x./reg15=<cleared>,metadata=<cleared>/g' | \ + grep -q "reg0=0xc0a80a64,reg15=<cleared>,metadata=<cleared> actions=mod_dl_dst:00:00:00:00:10:1a")]) +OVS_WAIT_UNTIL([$(ovs-ofctl dump-flows br-int table=OFTABLE_MAC_BINDING | \ + sed 's/reg15=0x.,metadata=0x./reg15=<cleared>,metadata=<cleared>/g' | \ + grep -q "reg0=0xc0a81402,reg15=<cleared>,metadata=<cleared> actions=mod_dl_dst:00:00:00:00:30:00")]) + +# Check GW router does not send any ARP request in this case. +wait_row_count mac_binding 1 ip="192.168.20.2" logical_port="gw-join" +AT_CHECK([test "$(ovs-ofctl dump-flows br-int table=OFTABLE_MAC_CACHE_USE |awk '/arp_spa=192.168.20.2/{print substr($4,11,1)}')" = "$n_arp"]) + +# Now drop ICMP echo reply in order to force OVN to arp the mac binding entry +# for the distributed router lr. +# Wait until entries in OFTABLE_MAC_CACHE_USE are stale. +OVS_WAIT_UNTIL([test $(ovs-ofctl dump-flows br-int table=OFTABLE_MAC_CACHE_USE | \ + awk '/nw_src=192.168.10.100/{print substr($6,10,1)}') -ge ${aging_th}]) +n_arp=$(ovs-ofctl dump-flows br-int table=OFTABLE_MAC_CACHE_USE |awk '/arp_spa=192.168.20.2/{print substr($4,11,1)}') +check ovn-nbctl --wait=hv acl-add join from-lport 1000 'inport == "join-lr" && icmp' drop +send_imcp_echo_req hv1 public 00:00:00:00:10:00 00:00:00:00:10:1a 192.168.20.2 192.168.10.100 +OVS_WAIT_UNTIL([test "$(ovs-ofctl dump-flows br-int table=OFTABLE_MAC_CACHE_USE |awk '/arp_spa=192.168.20.2/{print substr($4,11,1)}')" = "$((n_arp+2))"]) + +OVN_CLEANUP([hv1]) +AT_CLEANUP +]) + OVN_FOR_EACH_NORTHD([ AT_SETUP([router port type update and then remove]) ovn_start diff --git a/tests/system-ovn.at b/tests/system-ovn.at index 9982da7fe..d5858bec7 100644 --- a/tests/system-ovn.at +++ b/tests/system-ovn.at @@ -17240,3 +17240,111 @@ OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d /connection dropped.*/d"]) AT_CLEANUP ]) + +OVN_FOR_EACH_NORTHD([ +AT_SETUP([Mac binding aging - Probing]) +AT_KEYWORDS([mac_binding_probing]) +ovn_start +OVS_TRAFFIC_VSWITCHD_START() + +ADD_BR([br-int]) +ADD_BR([br-ext]) +ADD_BR([br-gw]) + +check ovs-ofctl add-flow br-ext action=normal +check ovs-ofctl add-flow br-gw action=normal +# Set external-ids in br-int needed for ovn-controller +check ovs-vsctl \ + -- set Open_vSwitch . external-ids:system-id=hv1 \ + -- set Open_vSwitch . external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \ + -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \ + -- set Open_vSwitch . external-ids:ovn-encap-ip=169.0.0.1 \ + -- set bridge br-int fail-mode=secure other-config:disable-in-band=true + +# Start ovn-controller +start_daemon ovn-controller + +check ovn-appctl -t ovn-controller vlog/set mac_cache:file:dbg pinctrl:file:dbg +check ovn-nbctl lr-add lr +check ovn-nbctl set logical_router lr options:chassis=hv1 +check ovn-nbctl set logical_router lr options:mac_binding_age_threshold=10 + +check ovn-nbctl ls-add sw +check ovn-nbctl ls-add public + +check ovn-nbctl lrp-add lr rp-sw 00:00:01:01:02:03 192.168.1.1/24 +check ovn-nbctl lrp-add lr rp-public 00:00:02:01:02:03 172.16.1.1/24 + +check ovn-nbctl lsp-add sw sw-rp -- set Logical_Switch_Port sw-rp \ + type=router options:router-port=rp-sw \ + -- lsp-set-addresses sw-rp router + +check ovn-nbctl lsp-add public public-rp -- set Logical_Switch_Port public-rp \ + type=router options:router-port=rp-public \ + -- lsp-set-addresses public-rp router + +ADD_NAMESPACES(alice) +ADD_VETH(alice, alice, br-int, "192.168.1.2/24", "f0:00:00:01:02:03", "192.168.1.1") +check ovn-nbctl lsp-add sw alice -- lsp-set-addresses alice "f0:00:00:01:02:03 192.168.1.2" + +check ovs-vsctl set Open_vSwitch . external-ids:ovn-bridge-mappings=phynet:br-ext +check ovn-nbctl lsp-add public public \ + -- lsp-set-addresses public unknown \ + -- lsp-set-type public localnet \ + -- lsp-set-options public network_name=phynet + +ADD_NAMESPACES(gw) +ADD_VETH(gw0, gw, br-ext, "172.16.1.2/24", "f0:00:00:01:02:04", "172.16.1.1") +ADD_VETH(gw1, gw, br-gw, "172.16.2.2/24", "f0:00:00:01:03:04") + +NS_CHECK_EXEC([gw], [sysctl -w net.ipv4.conf.all.forwarding=1],[0], [dnl +net.ipv4.conf.all.forwarding = 1 +]) + +ADD_NAMESPACES(bob) +ADD_VETH(bob, bob, br-gw, "172.16.2.10/24", "f0:00:00:01:02:06", "172.16.2.2") + +check ovn-nbctl lr-route-add lr 0.0.0.0/0 172.16.1.2 +check ovn-nbctl --wait=hv sync + +NS_CHECK_EXEC([alice], [ping -q -c 3 -i 0.3 -w 2 172.16.2.10 | FORMAT_PING], \ +[0], [dnl +3 packets transmitted, 3 received, 0% packet loss, time 0ms +]) +check_row_count mac_binding 1 mac=\"f0:00:00:01:02:04\" +mac_binding_uuid=$(fetch_column mac_binding _uuid logical_port=rp-public ip=172.16.1.2) + +NETNS_START_TCPDUMP([gw], [-n -c 2 -i gw0 arp[[6:2]] == 0x1 and arp[[24:4]] == 0xac100102], [gw]) +NS_CHECK_EXEC([alice], [ping -q -c 20 -i 0.5 172.16.2.10 | FORMAT_PING], \ +[0], [dnl +20 packets transmitted, 20 received, 0% packet loss, time 0ms +]) + +# Check mac binding entry is still active. +check_row_count mac_binding 1 mac=\"f0:00:00:01:02:04\" +OVS_WAIT_UNTIL([ + n_arp=$(cat gw.tcpdump | wc -l) + test ${n_arp} -ge 2 +]) + +AT_CHECK([test "$(fetch_column mac_binding _uuid logical_port=rp-public ip=172.16.1.2)" = "$mac_binding_uuid"]) + +# Wait for the mac binding entry to expire. +wait_row_count MAC_Binding 0 + +OVS_APP_EXIT_AND_WAIT([ovn-controller]) + +as ovn-sb +OVS_APP_EXIT_AND_WAIT([ovsdb-server]) + +as ovn-nb +OVS_APP_EXIT_AND_WAIT([ovsdb-server]) + +as northd +OVS_APP_EXIT_AND_WAIT([ovn-northd]) + +as +OVS_TRAFFIC_VSWITCHD_STOP(["/.*error receiving.*/d +/.*terminating with signal 15.*/d"]) +AT_CLEANUP +]) -- 2.48.1 _______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev