On Thu, Mar 19, 2026 at 11:02 AM Dumitru Ceara via dev <
[email protected]> wrote:
> It doesn't really make sense to use the router port MAC as service
> monitor source otherwise. Worse, it may even break existing use cases
> when the load balancer is only applied to logical switches and not to
> the routers connected to them.
>
> In order for the change to work properly we also had to fallback to
> recompute in all cases when load balancers with health check
> configuration are added/updated/deleted (through load balancer groups
> too). This was currently not happening but didn't really cause issues
> before ceea1d7cccad ("northd: Allow LB health checks from router IPs.").
>
> This also uncovered a bug in pinctrl, where we don't resync the
> in-memory version of the service monitor if the SB contents change
> (e.g., the source mac address). The patch fixes that part too as it
> wasn't especially visible until now when we change the source mac of the
> monitor if it gets applied to a router and uses the router IP.
>
> Fixes: a0a5dd8ce56f ("controller: Store src_mac, src_ip in svc_monitor
> struct.")
> Fixes: ceea1d7cccad ("northd: Allow LB health checks from router IPs.")
> Reported-at: https://redhat.atlassian.net/browse/FDP-3453
> Signed-off-by: Dumitru Ceara <[email protected]>
> ---
>
Hi Dumitru,
thank you for the v2. Just one thing, let's split the patch into two
each fixing one issue. With that done:
Acked-by: Ales Musil <[email protected]>
> V2:
> - Addressed Mark's comment:
> - renamed tests to show the difference between system/non-system tests
> - added mac check to the system test
> ---
> controller/pinctrl.c | 11 +++---
> northd/en-lb-data.c | 20 +++++++----
> northd/lb.c | 13 ++++++-
> northd/lb.h | 8 +++--
> northd/northd.c | 8 +++--
> tests/ovn-northd.at | 78 +++++++++++++++++++++++++++++++++++++++++
> tests/system-ovn.at | 82 ++++++++++++++++++++++++++++++++++++++++++++
> 7 files changed, 202 insertions(+), 18 deletions(-)
>
> diff --git a/controller/pinctrl.c b/controller/pinctrl.c
> index 35c42942f3..18b7b0df2e 100644
> --- a/controller/pinctrl.c
> +++ b/controller/pinctrl.c
> @@ -7197,16 +7197,13 @@ sync_svc_monitors(struct ovsdb_idl_txn
> *ovnsb_idl_txn,
> if (!svc_mon) {
> svc_mon = xmalloc(sizeof *svc_mon);
> svc_mon->dp_key = dp_key;
> - svc_mon->input_port_key = input_port_key;
> svc_mon->port_key = port_key;
> svc_mon->proto_port = sb_svc_mon->port;
> svc_mon->ip = ip_addr;
> - svc_mon->src_ip = ip_addr_src;
> svc_mon->is_ip6 = !is_ipv4;
> svc_mon->state = SVC_MON_S_INIT;
> svc_mon->status = SVC_MON_ST_UNKNOWN;
> svc_mon->protocol = protocol;
> - svc_mon->type = mon_type;
>
> smap_init(&svc_mon->options);
> svc_mon->interval =
> @@ -7220,15 +7217,19 @@ sync_svc_monitors(struct ovsdb_idl_txn
> *ovnsb_idl_txn,
> svc_mon->n_success = 0;
> svc_mon->n_failures = 0;
>
> - eth_addr_from_string(sb_svc_mon->src_mac, &svc_mon->src_mac);
> -
> hmap_insert(&svc_monitors_map, &svc_mon->hmap_node, hash);
> ovs_list_push_back(&svc_monitors, &svc_mon->list_node);
> changed = true;
> }
>
> svc_mon->sb_svc_mon = sb_svc_mon;
> +
> + svc_mon->input_port_key = input_port_key;
> + svc_mon->src_ip = ip_addr_src;
> + svc_mon->type = mon_type;
> + eth_addr_from_string(sb_svc_mon->src_mac, &svc_mon->src_mac);
> svc_mon->ea = ea;
> +
> if (!smap_equal(&svc_mon->options, &sb_svc_mon->options)) {
> smap_destroy(&svc_mon->options);
> smap_clone(&svc_mon->options, &sb_svc_mon->options);
> diff --git a/northd/en-lb-data.c b/northd/en-lb-data.c
> index a64c06bfda..04ef20bcc6 100644
> --- a/northd/en-lb-data.c
> +++ b/northd/en-lb-data.c
> @@ -268,6 +268,7 @@ lb_data_load_balancer_group_handler(struct engine_node
> *node, void *data)
> hmapx_add(&clbg->assoc_lbs, lb_group->lbs[i]);
> }
>
> + trk_lb_data->has_health_checks |= lb_group->has_health_checks;
> trk_lb_data->has_routable_lb |= lb_group->has_routable_lb;
> continue;
> }
> @@ -283,6 +284,7 @@ lb_data_load_balancer_group_handler(struct engine_node
> *node, void *data)
> if (nbrec_load_balancer_group_is_deleted(tracked_lb_group)) {
> hmap_remove(&lb_data->lbgrps, &lb_group->hmap_node);
> add_deleted_lbgrp_to_tracked_data(lb_group, trk_lb_data);
> + trk_lb_data->has_health_checks |= lb_group->has_health_checks;
> trk_lb_data->has_routable_lb |= lb_group->has_routable_lb;
> } else {
> /* Determine the lbs which are added or deleted for this
> @@ -299,6 +301,7 @@ lb_data_load_balancer_group_handler(struct engine_node
> *node, void *data)
> build_lrouter_lb_ips(lb_group->lb_ips, lb_group->lbs[i]);
> }
>
> + trk_lb_data->has_health_checks |= lb_group->has_health_checks;
> trk_lb_data->has_routable_lb |= lb_group->has_routable_lb;
> struct crupdated_lbgrp *clbg =
> add_crupdated_lbgrp_to_tracked_data(lb_group,
> trk_lb_data);
> @@ -690,10 +693,12 @@ handle_od_lb_changes(struct nbrec_load_balancer
> **nbrec_lbs,
> /* Add this lb to the tracked data. */
> uuidset_insert(&codlb->assoc_lbs, lb_uuid);
>
> + struct ovn_northd_lb *lb = ovn_northd_lb_find(&lb_data->lbs,
> + lb_uuid);
> + ovs_assert(lb);
> +
> + trk_lb_data->has_health_checks |= lb->health_checks;
> if (!trk_lb_data->has_routable_lb) {
> - struct ovn_northd_lb *lb =
> ovn_northd_lb_find(&lb_data->lbs,
> - lb_uuid);
> - ovs_assert(lb);
> trk_lb_data->has_routable_lb |= lb->routable;
> trk_lb_data->has_distributed_lb |= lb->is_distributed;
> }
> @@ -730,10 +735,12 @@ handle_od_lbgrp_changes(struct
> nbrec_load_balancer_group **nbrec_lbgrps,
> /* Add this lb group to the tracked data. */
> uuidset_insert(&codlb->assoc_lbgrps, lbgrp_uuid);
>
> + struct ovn_lb_group *lbgrp =
> + ovn_lb_group_find(&lb_data->lbgrps, lbgrp_uuid);
> + ovs_assert(lbgrp);
> +
> + trk_lb_data->has_health_checks |= lbgrp->has_health_checks;
> if (!trk_lb_data->has_routable_lb) {
> - struct ovn_lb_group *lbgrp =
> - ovn_lb_group_find(&lb_data->lbgrps, lbgrp_uuid);
> - ovs_assert(lbgrp);
> trk_lb_data->has_routable_lb |= lbgrp->has_routable_lb;
> }
> }
> @@ -755,6 +762,7 @@ destroy_tracked_data(struct ed_type_lb_data *lb_data)
> lb_data->tracked_lb_data.has_dissassoc_lbs_from_lbgrps = false;
> lb_data->tracked_lb_data.has_dissassoc_lbs_from_od = false;
> lb_data->tracked_lb_data.has_dissassoc_lbgrps_from_od = false;
> + lb_data->tracked_lb_data.has_health_checks = false;
> lb_data->tracked_lb_data.has_routable_lb = false;
>
> struct hmapx_node *node;
> diff --git a/northd/lb.c b/northd/lb.c
> index 83c1bf97e1..2f683cef1b 100644
> --- a/northd/lb.c
> +++ b/northd/lb.c
> @@ -464,7 +464,8 @@ ovn_northd_lb_init(struct ovn_northd_lb *lb,
> * considered. If no matching LRP is found, the 'svc_mon_lrp' is set to
> * NULL. */
> void
> -ovn_northd_lb_backend_set_mon_port(const struct ovn_port *backend_op,
> +ovn_northd_lb_backend_set_mon_port(const struct ovn_lb_datapaths *lb_dps,
> + const struct ovn_port *backend_op,
> struct ovn_northd_lb_backend
> *backend_nb)
> {
> if (backend_op && !backend_nb->remote_backend) {
> @@ -473,6 +474,14 @@ ovn_northd_lb_backend_set_mon_port(const struct
> ovn_port *backend_op,
> if (!svc_mon_op->peer) {
> continue;
> }
> +
> + /* Skip routers this load balancer is not applied onto. */
> + const struct ovn_datapath *rtr_od = svc_mon_op->peer->od;
> + if (!dynamic_bitmap_is_set(&lb_dps->nb_lr_map,
> + rtr_od->sdp->index)) {
> + continue;
> + }
> +
> const char *lrp_ip = lrp_find_member_ip(
> svc_mon_op->peer,
> backend_nb->svc_mon_src_ip);
> @@ -564,6 +573,7 @@ ovn_lb_group_init(struct ovn_lb_group *lb_group,
> const struct uuid *lb_uuid =
> &nbrec_lb_group->load_balancer[i]->header_.uuid;
> lb_group->lbs[i] = ovn_northd_lb_find(lbs, lb_uuid);
> + lb_group->has_health_checks |= lb_group->lbs[i]->health_checks;
> lb_group->has_routable_lb |= lb_group->lbs[i]->routable;
> }
> }
> @@ -586,6 +596,7 @@ ovn_lb_group_cleanup(struct ovn_lb_group *lb_group)
> {
> ovn_lb_ip_set_destroy(lb_group->lb_ips);
> lb_group->lb_ips = NULL;
> + lb_group->has_health_checks = false;
> lb_group->has_routable_lb = false;
> free(lb_group->lbs);
> }
> diff --git a/northd/lb.h b/northd/lb.h
> index c1f0c95da1..97d2c4c366 100644
> --- a/northd/lb.h
> +++ b/northd/lb.h
> @@ -108,9 +108,6 @@ struct ovn_northd_lb_backend {
> struct ovn_port *svc_mon_lrp;
> };
>
> -void ovn_northd_lb_backend_set_mon_port(const struct ovn_port *,
> - struct ovn_northd_lb_backend *);
> -
> struct ovn_northd_lb *ovn_northd_lb_create(const struct
> nbrec_load_balancer *);
> struct ovn_northd_lb *ovn_northd_lb_find(const struct hmap *,
> const struct uuid *);
> @@ -136,6 +133,7 @@ struct ovn_lb_group {
> size_t n_lbs;
> struct ovn_northd_lb **lbs;
> struct ovn_lb_ip_set *lb_ips;
> + bool has_health_checks;
> bool has_routable_lb;
> };
>
> @@ -203,6 +201,10 @@ void ovn_lb_datapaths_add_ls(struct ovn_lb_datapaths
> *, size_t n,
> struct ovn_datapath **,
> size_t n_ls_datapaths);
>
> +void ovn_northd_lb_backend_set_mon_port(const struct ovn_lb_datapaths *,
> + const struct ovn_port *,
> + struct ovn_northd_lb_backend *);
> +
> struct ovn_lb_group_datapaths {
> struct hmap_node hmap_node;
>
> diff --git a/northd/northd.c b/northd/northd.c
> index 734efea657..c607c4c08d 100644
> --- a/northd/northd.c
> +++ b/northd/northd.c
> @@ -3332,13 +3332,15 @@ ovn_nf_svc_create(struct ovsdb_idl_txn *ovnsb_txn,
>
> static void
> ovn_lb_svc_create(struct ovsdb_idl_txn *ovnsb_txn,
> - const struct ovn_northd_lb *lb,
> + const struct ovn_lb_datapaths *lb_dps,
> const struct svc_monitor_addresses
> *svc_global_addresses,
> struct hmap *ls_ports,
> struct sset *svc_monitor_lsps,
> struct hmap *local_svc_monitors_map,
> struct hmap *ic_learned_svc_monitors_map)
> {
> + const struct ovn_northd_lb *lb = lb_dps->lb;
> +
> if (lb->template) {
> return;
> }
> @@ -3366,7 +3368,7 @@ ovn_lb_svc_create(struct ovsdb_idl_txn *ovnsb_txn,
> continue;
> }
>
> - ovn_northd_lb_backend_set_mon_port(op, backend_nb);
> + ovn_northd_lb_backend_set_mon_port(lb_dps, op, backend_nb);
>
> /* If the service monitor is backed by a real port, use its
> MAC
> * address instead of the default service check MAC. */
> @@ -3787,7 +3789,7 @@ build_svc_monitors_data(
>
> struct ovn_lb_datapaths *lb_dps;
> HMAP_FOR_EACH (lb_dps, hmap_node, lb_dps_map) {
> - ovn_lb_svc_create(ovnsb_txn, lb_dps->lb,
> + ovn_lb_svc_create(ovnsb_txn, lb_dps,
> svc_global_addresses,
> ls_ports,
> svc_monitor_lsps,
> diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
> index 60258a6f25..01ae91a963 100644
> --- a/tests/ovn-northd.at
> +++ b/tests/ovn-northd.at
> @@ -20170,3 +20170,81 @@ AT_CHECK([ovn-sbctl get Port_Binding $lsp_pb_uuid
> external_ids:name],
> OVN_CLEANUP_NORTHD
> AT_CLEANUP
> ])
> +
> +OVN_FOR_EACH_NORTHD_NO_HV([
> +AT_SETUP([Load balancer health checks - service monitor source MAC - SB
> syncing])
> +ovn_start
> +
> +global_svc_mon_mac="11:11:11:11:11:11"
> +router_port_mac="00:00:00:00:00:01"
> +check ovn-nbctl set NB_Global .
> options:svc_monitor_mac="$global_svc_mon_mac"
> +
> +check ovn-nbctl \
> + -- ls-add ls \
> + -- lsp-add ls lsp1 \
> + -- lsp-add ls lsp2 \
> + -- lr-add lr \
> + -- lrp-add lr lr-ls $router_port_mac 192.168.1.254/24 \
> + -- lsp-add-router-port ls ls-lr lr-ls
> +
> +check ovn-nbctl \
> + -- lb-add lb0 192.168.5.1:1 192.168.1.1:1,192.168.1.2:1 udp \
> + -- lb-add lb1 192.168.5.1:2 192.168.1.1:2,192.168.1.2:2 udp
> +lb0=$(fetch_column nb:Load_Balancer _uuid name=lb0)
> +lb1=$(fetch_column nb:Load_Balancer _uuid name=lb1)
> +
> +check ovn-nbctl
> \
> + -- set load_balancer $lb0
> ip_port_mappings:192.168.1.1=lsp1:192.168.1.254 \
> + -- set load_balancer $lb1
> ip_port_mappings:192.168.1.1=lsp1:192.168.1.254
> +
> +check_uuid ovn-nbctl --id=@hc create Load_Balancer_Health_Check
> vip="192.168.5.1\:1" \
> + -- add Load_Balancer $lb0 health_check @hc
> +check_uuid ovn-nbctl --id=@hc create Load_Balancer_Health_Check
> vip="192.168.5.1\:2" \
> + -- add Load_Balancer $lb1 health_check @hc
> +
> +dnl Add LB0 and LB1 to the switch (LB1 via load balancer group).
> +check ovn-nbctl ls-lb-add ls lb0
> +lbg=$(ovn-nbctl --wait=sb create load_balancer_group name=lbg)
> +check ovn-nbctl add load_balancer_group lbg load_balancer $lb1
> +check ovn-nbctl add logical_switch ls load_balancer_group $lbg
> +
> +check ovn-nbctl --wait=sb sync
> +check_row_count sb:Service_Monitor 2
> +
> +AS_BOX([LB0 on switch, LB1 on switch])
> +check_column "$global_svc_mon_mac" sb:Service_Monitor src_mac port=1
> +check_column "$global_svc_mon_mac" sb:Service_Monitor src_mac port=2
> +
> +AS_BOX([LB0 on switch + router, LB1 on switch])
> +check ovn-nbctl --wait=sb lr-lb-add lr lb0
> +check_column "$router_port_mac" sb:Service_Monitor src_mac port=1
> +check_column "$global_svc_mon_mac" sb:Service_Monitor src_mac port=2
> +
> +AS_BOX([LB0 on switch + router, LB1 on switch + router])
> +check ovn-nbctl --wait=sb add logical_router lr load_balancer_group $lbg
> +check_column "$router_port_mac" sb:Service_Monitor src_mac port=1
> +check_column "$router_port_mac" sb:Service_Monitor src_mac port=2
> +
> +AS_BOX([LB0 on switch + router, LB1 nowhere])
> +check ovn-nbctl --wait=sb remove load_balancer_group lbg load_balancer
> $lb1
> +check_column "$router_port_mac" sb:Service_Monitor src_mac port=1
> +check_column "$global_svc_mon_mac" sb:Service_Monitor src_mac port=2
> +
> +AS_BOX([LB0 on switch + router, LB1 on switch + router, readd])
> +check ovn-nbctl --wait=sb add load_balancer_group lbg load_balancer $lb1
> +check_column "$router_port_mac" sb:Service_Monitor src_mac port=1
> +check_column "$router_port_mac" sb:Service_Monitor src_mac port=2
> +
> +AS_BOX([LB0 on switch + router, LB1 on switch, remove])
> +check ovn-nbctl --wait=sb remove logical_router lr load_balancer_group
> $lbg
> +check_column "$router_port_mac" sb:Service_Monitor src_mac port=1
> +check_column "$global_svc_mon_mac" sb:Service_Monitor src_mac port=2
> +
> +AS_BOX([LB0 on switch, LB1 on switch, remove])
> +check ovn-nbctl --wait=sb lr-lb-del lr lb0
> +check_column "$global_svc_mon_mac" sb:Service_Monitor src_mac port=1
> +check_column "$global_svc_mon_mac" sb:Service_Monitor src_mac port=2
> +
> +OVN_CLEANUP_NORTHD
> +AT_CLEANUP
> +])
> diff --git a/tests/system-ovn.at b/tests/system-ovn.at
> index 7edb8a45b0..9e58f39c8b 100644
> --- a/tests/system-ovn.at
> +++ b/tests/system-ovn.at
> @@ -21496,3 +21496,85 @@ OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port
> patch-.*/d
> /connection dropped.*/d"])
> AT_CLEANUP
> ])
> +
> +OVN_FOR_EACH_NORTHD([
> +AT_SETUP([Load balancer health checks - service monitor source MAC
> matching])
> +AT_SKIP_IF([test $HAVE_NC = no])
> +AT_SKIP_IF([test $HAVE_TCPDUMP = no])
> +
> +ovn_start
> +OVS_TRAFFIC_VSWITCHD_START()
> +ADD_BR([br-int])
> +
> +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_daemon ovn-controller
> +
> +global_svc_mon_mac="11:11:11:11:11:11"
> +router_port_mac="00:00:00:00:00:01"
> +lsp_mac="00:00:00:01:01:01"
> +check ovn-nbctl set NB_Global .
> options:svc_monitor_mac="$global_svc_mon_mac"
> +
> +check ovn-nbctl \
> + -- ls-add ls \
> + -- lsp-add ls lsp \
> + -- lsp-set-addresses lsp "$lsp_mac 192.168.1.1" \
> + -- lr-add lr \
> + -- lrp-add lr lr-ls $router_port_mac 192.168.1.254/24 \
> + -- lsp-add-router-port ls ls-lr lr-ls
> +
> +check ovn-nbctl lb-add lb 192.168.5.1:42 192.168.1.1:42 udp
> +lb=$(fetch_column nb:Load_Balancer _uuid name=lb)
> +
> +lbhc=$(ovn-nbctl \
> + --id=@hc create Load_Balancer_Health_Check vip="192.168.5.1\:42" \
> + -- add Load_Balancer $lb health_check @hc)
> +check ovn-nbctl set Load_Balancer_Health_Check $lbhc \
> + options:interval=1 options:timeout=1 \
> + options:success_count=2 \
> + options:failure_count=2
> +check ovn-nbctl --wait=sb set load_balancer $lb \
> + ip_port_mappings:192.168.1.1=lsp:192.168.1.254
> +
> +check ovn-nbctl set Logical_Router lr options:chassis="hv1"
> +check ovn-nbctl ls-lb-add ls lb
> +
> +ADD_NAMESPACES(lsp)
> +ADD_VETH(lsp, lsp, br-int, "192.168.1.1/24", "$lsp_mac",
> + "192.168.1.254")
> +
> +wait_for_ports_up
> +check ovn-nbctl --wait=hv sync
> +
> +dnl Check mac address of probe requests and replies, they should use the
> +dnl global svc_monitor_mac.
> +NETNS_START_TCPDUMP([lsp], [-nne -i lsp -Q in 'udp'], [lsp_in])
> +NETNS_START_TCPDUMP([lsp], [-nne -i lsp -Q out 'udp || icmp'], [lsp_out])
> +
> +dnl Start a backend server, the monitor should change status to "online".
> +NETNS_DAEMONIZE([lsp], [nc -l -u 42], [nc.pid])
> +wait_row_count Service_Monitor 1 status=online
> +
> +dnl Kill the backend server, the monitor should change status to
> "offline".
> +check kill -9 `cat nc.pid`
> +wait_row_count Service_Monitor 1 status=offline
> +
> +OVS_WAIT_UNTIL([grep -q "$global_svc_mon_mac > $lsp_mac" lsp_in.tcpdump])
> +AT_CHECK([grep -v "$global_svc_mon_mac > $lsp_mac" lsp_in.tcpdump], [1])
> +
> +OVS_WAIT_UNTIL([grep -q "$lsp_mac > $global_svc_mon_mac" lsp_out.tcpdump])
> +AT_CHECK([grep -v "$lsp_mac > $global_svc_mon_mac" lsp_out.tcpdump], [1])
> +
> +OVN_CLEANUP_CONTROLLER([hv1])
> +OVN_CLEANUP_NORTHD
> +
> +as
> +OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d
> +/connection dropped.*/d"])
> +AT_CLEANUP
> +])
> --
> 2.53.0
>
> _______________________________________________
> dev mailing list
> [email protected]
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>
>
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev