On Sat, Jul 19, 2025 at 8:27 AM Sragdhara Datta Chaudhuri <sragdha.chau...@nutanix.com> wrote: > > The LB health monitoring functionality has been extended to support NFs. > Network_Function_Group has a list of Network_Functions, each of which has a > reference to network_Function_Health_Check that has the monitoring config. > There is a corresponding SB service_monitor maintaining the online/offline > status. When status changes, northd picks one of the “online” NFs and sets in > network_function_active field of NFG. The redirection rule in LS uses the > ports from this NF. > > Ovn-controller performs the health monitoring by sending ICMP echo request > with source IP and MAC from NB global options “svc_monitor_ip4” and > “svc_monitor_mac”, and destination IP and MAC from new NB global options > “svc_monitor_ip4_dst” and “svc_monitor_mac_dst”. The sequence number and id > are randomly generated and stored in service_mon. The NF VM forwards the > same packet out of the other port. When it comes out, ovn-controller matches > the sequence number and id with stored values and marks online if matched. > > In SB Service_Monitor table three new fields have been added: > type: to indicate “load-balancer” or “network-function” > mac: the destination MAC address for the monitor packets > logical_input_port: The LSP to which the probe packet would be sent > (taken from inport of Network_Function) > > Co-authored-by: Naveen Yerramneni <naveen.yerramn...@nutanix.com> > Co-authored-by: Karthik Chandrashekar <karthi...@nutanix.com> > Signed-off-by: Naveen Yerramneni <naveen.yerramn...@nutanix.com> > Signed-off-by: Karthik Chandrashekar <karthi...@nutanix.com> > Signed-off-by: Sragdhara Datta Chaudhuri <sragdha.chau...@nutanix.com> > Signed-off-by: Naveen Yerramneni <naveen.yerramn...@nutanix.com> > Signed-off-by: Karthik Chandrashekar <karthi...@nutanix.com> > Signed-off-by: Sragdhara Datta Chaudhuri <sragdha.chau...@nutanix.com>
This patch needs test cases. Please see ovn-northd.at which has test cases for service monitors. You can use the same approach and test out the behavior of ovn-northd. Also please add system tests in system-ovn.at to cover this feature. Thanks Numan > --- > controller/pinctrl.c | 252 +++++++++++++++++++++++++++++------- > northd/en-global-config.c | 75 +++++++++++ > northd/en-global-config.h | 12 +- > northd/en-northd.c | 4 + > northd/en-sync-sb.c | 16 ++- > northd/northd.c | 266 +++++++++++++++++++++++++++++++++----- > northd/northd.h | 6 +- > ovn-sb.ovsschema | 12 +- > ovn-sb.xml | 22 +++- > 9 files changed, 576 insertions(+), 89 deletions(-) > > diff --git a/controller/pinctrl.c b/controller/pinctrl.c > index d4f4da731..583e6b66c 100644 > --- a/controller/pinctrl.c > +++ b/controller/pinctrl.c > @@ -6927,8 +6927,17 @@ enum svc_monitor_status { > enum svc_monitor_protocol { > SVC_MON_PROTO_TCP, > SVC_MON_PROTO_UDP, > + SVC_MON_PROTO_ICMP, > }; > > +enum svc_monitor_type { > + /* load balancer */ > + SVC_MON_TYPE_LB, > + /* network function */ > + SVC_MON_TYPE_NF, > +}; > + > + > /* Service monitor health checks. */ > struct svc_monitor { > struct hmap_node hmap_node; > @@ -6941,6 +6950,7 @@ struct svc_monitor { > /* key */ > struct in6_addr ip; > uint32_t dp_key; > + uint32_t input_port_key; > uint32_t port_key; > uint32_t proto_port; /* tcp/udp port */ > > @@ -6973,6 +6983,7 @@ struct svc_monitor { > int n_failures; > > enum svc_monitor_protocol protocol; > + enum svc_monitor_type type; > enum svc_monitor_state state; > enum svc_monitor_status status; > struct dp_packet pkt; > @@ -6980,6 +6991,9 @@ struct svc_monitor { > uint32_t seq_no; > ovs_be16 tp_src; > > + ovs_be16 icmp_id; > + ovs_be16 icmp_seq_no; > + > bool delete; > }; > > @@ -7045,9 +7059,28 @@ sync_svc_monitors(struct ovsdb_idl_txn *ovnsb_idl_txn, > > const struct sbrec_service_monitor *sb_svc_mon; > SBREC_SERVICE_MONITOR_TABLE_FOR_EACH (sb_svc_mon, svc_mon_table) { > + enum svc_monitor_type mon_type; > + if (sb_svc_mon->type > + && !strcmp(sb_svc_mon->type, "network-function")) { > + mon_type = SVC_MON_TYPE_NF; > + } else { > + mon_type = SVC_MON_TYPE_LB; > + } > + > + enum svc_monitor_protocol protocol; > + if (!strcmp(sb_svc_mon->protocol, "udp")) { > + protocol = SVC_MON_PROTO_UDP; > + } else if (!strcmp(sb_svc_mon->protocol, "icmp")) { > + protocol = SVC_MON_PROTO_ICMP; > + } else { > + protocol = SVC_MON_PROTO_TCP; > + } > + > const struct sbrec_port_binding *pb > = lport_lookup_by_name(sbrec_port_binding_by_name, > sb_svc_mon->logical_port); > + const struct sbrec_port_binding *input_pb = NULL; > + > if (!pb) { > continue; > } > @@ -7067,39 +7100,65 @@ sync_svc_monitors(struct ovsdb_idl_txn *ovnsb_idl_txn, > > struct eth_addr ea; > bool mac_found = false; > - for (size_t i = 0; i < pb->n_mac && !mac_found; i++) { > - struct lport_addresses laddrs; > > - if (!extract_lsp_addresses(pb->mac[i], &laddrs)) { > + if (mon_type == SVC_MON_TYPE_NF) { > + if (protocol != SVC_MON_PROTO_ICMP) { > + continue; > + } > + input_pb = lport_lookup_by_name(sbrec_port_binding_by_name, > + sb_svc_mon->logical_input_port); > + if (!input_pb) { > + continue; > + } > + if (input_pb->chassis != our_chassis) { > + continue; > + } > + if (strcmp(sb_svc_mon->mac, "")) { > + if (eth_addr_from_string(sb_svc_mon->mac, &ea)) { > + mac_found = true; > + } > + } > + } else { > + if (protocol != SVC_MON_PROTO_TCP && > + protocol != SVC_MON_PROTO_UDP) { > continue; > } > > - if (is_ipv4) { > - for (size_t j = 0; j < laddrs.n_ipv4_addrs; j++) { > - if (ip4 == laddrs.ipv4_addrs[j].addr) { > - ea = laddrs.ea; > - mac_found = true; > - break; > - } > + for (size_t i = 0; i < pb->n_mac && !mac_found; i++) { > + struct lport_addresses laddrs; > + > + if (!extract_lsp_addresses(pb->mac[i], &laddrs)) { > + continue; > } > - } else { > - for (size_t j = 0; j < laddrs.n_ipv6_addrs; j++) { > - if (IN6_ARE_ADDR_EQUAL(&ip_addr, > - &laddrs.ipv6_addrs[j].addr)) { > - ea = laddrs.ea; > - mac_found = true; > - break; > + > + if (is_ipv4) { > + for (size_t j = 0; j < laddrs.n_ipv4_addrs; j++) { > + if (ip4 == laddrs.ipv4_addrs[j].addr) { > + ea = laddrs.ea; > + mac_found = true; > + break; > + } > + } > + } else { > + for (size_t j = 0; j < laddrs.n_ipv6_addrs; j++) { > + if (IN6_ARE_ADDR_EQUAL(&ip_addr, > + &laddrs.ipv6_addrs[j].addr)) { > + ea = laddrs.ea; > + mac_found = true; > + break; > + } > } > } > - } > > - if (!mac_found && !laddrs.n_ipv4_addrs && !laddrs.n_ipv6_addrs) { > - /* IP address(es) are not configured. Use the first mac. */ > - ea = laddrs.ea; > - mac_found = true; > - } > + if (!mac_found && !laddrs.n_ipv4_addrs && > + !laddrs.n_ipv6_addrs) { > + /* IP address(es) are not configured. Use the first mac. > */ > + ea = laddrs.ea; > + mac_found = true; > + } > > - destroy_lport_addresses(&laddrs); > + destroy_lport_addresses(&laddrs); > + } > } > > if (!mac_found) { > @@ -7108,23 +7167,18 @@ sync_svc_monitors(struct ovsdb_idl_txn *ovnsb_idl_txn, > > uint32_t dp_key = pb->datapath->tunnel_key; > uint32_t port_key = pb->tunnel_key; > + uint32_t input_port_key = input_pb ? input_pb->tunnel_key : > UINT32_MAX; > uint32_t hash = > hash_bytes(&ip_addr, sizeof ip_addr, > hash_3words(dp_key, port_key, sb_svc_mon->port)); > > - enum svc_monitor_protocol protocol; > - if (!sb_svc_mon->protocol || strcmp(sb_svc_mon->protocol, "udp")) { > - protocol = SVC_MON_PROTO_TCP; > - } else { > - protocol = SVC_MON_PROTO_UDP; > - } > - > svc_mon = pinctrl_find_svc_monitor(dp_key, port_key, &ip_addr, > sb_svc_mon->port, protocol, hash); > > 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; > @@ -7132,6 +7186,7 @@ sync_svc_monitors(struct ovsdb_idl_txn *ovnsb_idl_txn, > 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 = > @@ -8023,11 +8078,67 @@ svc_monitor_send_udp_health_check(struct rconn > *swconn, > ofpbuf_uninit(&ofpacts); > } > > + > +static void > +svc_monitor_send_icmp_health_check__(struct rconn *swconn, > + struct svc_monitor *svc_mon) > +{ > + uint64_t packet_stub[128 / 8]; > + struct dp_packet packet; > + dp_packet_use_stub(&packet, packet_stub, sizeof packet_stub); > + > + struct eth_addr eth_src; > + eth_addr_from_string(svc_mon->sb_svc_mon->src_mac, ð_src); > + > + ovs_be32 ip4_src; > + ip_parse(svc_mon->sb_svc_mon->src_ip, &ip4_src); > + pinctrl_compose_ipv4(&packet, eth_src, svc_mon->ea, ip4_src, > + in6_addr_get_mapped_ipv4(&svc_mon->ip), > + IPPROTO_ICMP, 255, ICMP_HEADER_LEN); > + > + struct icmp_header *ih = dp_packet_l4(&packet); > + ih->icmp_fields.echo.id = svc_mon->icmp_id; > + ih->icmp_fields.echo.seq = svc_mon->icmp_seq_no; > + > + uint8_t icmp_code = 0; > + packet_set_icmp(&packet, ICMP4_ECHO_REQUEST, icmp_code); > + > + ih->icmp_csum = 0; > + ih->icmp_csum = csum(ih, sizeof *ih); > + > + uint64_t ofpacts_stub[4096 / 8]; > + struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(ofpacts_stub); > + enum ofp_version version = rconn_get_version(swconn); > + put_load(svc_mon->dp_key, MFF_LOG_DATAPATH, 0, 64, &ofpacts); > + put_load(svc_mon->input_port_key, MFF_LOG_OUTPORT, 0, 32, &ofpacts); > + put_load(1, MFF_LOG_FLAGS, MLF_LOCAL_ONLY, 1, &ofpacts); > + struct ofpact_resubmit *resubmit = ofpact_put_RESUBMIT(&ofpacts); > + resubmit->in_port = OFPP_CONTROLLER; > + resubmit->table_id = OFTABLE_LOCAL_OUTPUT; > + > + struct ofputil_packet_out po = { > + .packet = dp_packet_data(&packet), > + .packet_len = dp_packet_size(&packet), > + .buffer_id = UINT32_MAX, > + .ofpacts = ofpacts.data, > + .ofpacts_len = ofpacts.size, > + }; > + match_set_in_port(&po.flow_metadata, OFPP_CONTROLLER); > + enum ofputil_protocol proto = ofputil_protocol_from_ofp_version(version); > + queue_msg(swconn, ofputil_encode_packet_out(&po, proto)); > + dp_packet_uninit(&packet); > + ofpbuf_uninit(&ofpacts); > +} > + > static void > svc_monitor_send_health_check(struct rconn *swconn, > struct svc_monitor *svc_mon) > { > - if (svc_mon->protocol == SVC_MON_PROTO_TCP) { > + if (svc_mon->protocol == SVC_MON_PROTO_ICMP) { > + svc_mon->icmp_id = (OVS_FORCE ovs_be16) random_uint16(); > + svc_mon->icmp_seq_no = (OVS_FORCE ovs_be16) random_uint16(); > + svc_monitor_send_icmp_health_check__(swconn, svc_mon); > + } else if (svc_mon->protocol == SVC_MON_PROTO_TCP) { > svc_mon->seq_no = random_uint32(); > svc_mon->tp_src = htons(get_random_src_port()); > svc_monitor_send_tcp_health_check__(swconn, svc_mon, > @@ -8068,13 +8179,14 @@ svc_monitors_run(struct rconn *swconn, > > case SVC_MON_S_WAITING: > if (current_time > svc_mon->wait_time) { > - if (svc_mon->protocol == SVC_MON_PROTO_TCP) { > - svc_mon->n_failures++; > - svc_mon->state = SVC_MON_S_OFFLINE; > - } else { > + if (svc_mon->protocol == SVC_MON_PROTO_UDP) { > svc_mon->n_success++; > svc_mon->state = SVC_MON_S_ONLINE; > + } else { > + svc_mon->n_failures++; > + svc_mon->state = SVC_MON_S_OFFLINE; > } > + > svc_mon->next_send_time = current_time + svc_mon->interval; > next_run_time = svc_mon->next_send_time; > } else { > @@ -8135,6 +8247,27 @@ svc_monitors_wait(long long int > svc_monitors_next_run_time) > } > } > > + > +static void > +pinctrl_handle_icmp_svc_check(struct dp_packet *pkt_in, > + struct svc_monitor *svc_mon) > +{ > + struct icmp_header *ih = dp_packet_l4(pkt_in); > + > + if (!ih) { > + return; > + } > + > + if ((ih->icmp_fields.echo.id != svc_mon->icmp_id) || > + (ih->icmp_fields.echo.seq != svc_mon->icmp_seq_no)) { > + return; > + } > + > + svc_mon->n_success++; > + svc_mon->state = SVC_MON_S_ONLINE; > + svc_mon->next_send_time = time_msec() + svc_mon->interval; > +} > + > static bool > pinctrl_handle_tcp_svc_check(struct rconn *swconn, > struct dp_packet *pkt_in, > @@ -8191,6 +8324,7 @@ pinctrl_handle_svc_check(struct rconn *swconn, const > struct flow *ip_flow, > uint32_t dp_key = ntohll(md->flow.metadata); > uint32_t port_key = md->flow.regs[MFF_LOG_INPORT - MFF_REG0]; > struct in6_addr ip_addr; > + struct in6_addr dst_ip_addr; > struct eth_header *in_eth = dp_packet_data(pkt_in); > uint8_t ip_proto; > > @@ -8206,10 +8340,12 @@ pinctrl_handle_svc_check(struct rconn *swconn, const > struct flow *ip_flow, > } > > ip_addr = in6_addr_mapped_ipv4(ip_flow->nw_src); > + dst_ip_addr = in6_addr_mapped_ipv4(ip_flow->nw_dst); > ip_proto = in_ip->ip_proto; > } else { > struct ovs_16aligned_ip6_hdr *in_ip = dp_packet_l3(pkt_in); > ip_addr = ip_flow->ipv6_src; > + dst_ip_addr = ip_flow->ipv6_dst; > ip_proto = in_ip->ip6_nxt; > } > > @@ -8222,7 +8358,6 @@ pinctrl_handle_svc_check(struct rconn *swconn, const > struct flow *ip_flow, > return; > } > > - > if (ip_proto == IPPROTO_TCP) { > uint32_t hash = > hash_bytes(&ip_addr, sizeof ip_addr, > @@ -8251,17 +8386,36 @@ pinctrl_handle_svc_check(struct rconn *swconn, const > struct flow *ip_flow, > return; > } > > - const void *in_ip = dp_packet_get_icmp_payload(pkt_in); > - if (!in_ip) { > - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); > - VLOG_WARN_RL(&rl, "Original IP datagram not present in " > - "ICMP packet"); > - return; > - } > - > if (in_eth->eth_type == htons(ETH_TYPE_IP)) { > struct icmp_header *ih = l4h; > /* It's ICMP packet. */ > + if (ih->icmp_type == ICMP4_ECHO_REQUEST && ih->icmp_code == 0) { > + uint32_t hash = hash_bytes(&dst_ip_addr, sizeof dst_ip_addr, > + hash_3words(dp_key, port_key, 0)); > + struct svc_monitor *svc_mon = > + pinctrl_find_svc_monitor(dp_key, port_key, &dst_ip_addr, > 0, > + SVC_MON_PROTO_ICMP, hash); > + if (!svc_mon) { > + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT( > + 1, 5); > + VLOG_WARN_RL(&rl, "handle service check: Service monitor > " > + "not found for ICMP request"); > + return; > + } > + if (svc_mon->type == SVC_MON_TYPE_NF) { > + pinctrl_handle_icmp_svc_check(pkt_in, svc_mon); > + } > + return; > + } > + > + const void *in_ip = dp_packet_get_icmp_payload(pkt_in); > + if (!in_ip) { > + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, > 5); > + VLOG_WARN_RL(&rl, "Original IP datagram not present in " > + "ICMP packet"); > + return; > + } > + > if (ih->icmp_type != ICMP4_DST_UNREACH || ih->icmp_code != 3) { > return; > } > @@ -8283,6 +8437,14 @@ pinctrl_handle_svc_check(struct rconn *swconn, const > struct flow *ip_flow, > return; > } > } else { > + const void *in_ip = dp_packet_get_icmp_payload(pkt_in); > + if (!in_ip) { > + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, > 5); > + VLOG_WARN_RL(&rl, "Original IP datagram not present in " > + "ICMP packet"); > + return; > + } > + > struct icmp6_header *ih6 = l4h; > if (ih6->icmp6_type != 1 || ih6->icmp6_code != 4) { > return; > diff --git a/northd/en-global-config.c b/northd/en-global-config.c > index e7d6a7e66..d250c6309 100644 > --- a/northd/en-global-config.c > +++ b/northd/en-global-config.c > @@ -20,6 +20,7 @@ > > /* OVS includes */ > #include "openvswitch/vlog.h" > +#include "socket-util.h" > > /* OVN includes */ > #include "debug.h" > @@ -63,6 +64,35 @@ en_global_config_init(struct engine_node *node OVS_UNUSED, > return data; > } > > +static void > +update_svc_monitor_addr(const char *new_ip4, const char **old_ip4_pptr) > +{ > + if (new_ip4) { > + struct sockaddr_storage svc_mon_addr; > + if (inet_parse_address(new_ip4, &svc_mon_addr)) { > + struct ds ip_s = DS_EMPTY_INITIALIZER; > + ss_format_address_nobracks(&svc_mon_addr, &ip_s); > + if ((*old_ip4_pptr == NULL) > + || strcmp(*old_ip4_pptr, ds_steal_cstr(&ip_s))) { > + if (*old_ip4_pptr) { > + free(CONST_CAST(void *, *old_ip4_pptr)); > + } > + *old_ip4_pptr = ds_steal_cstr(&ip_s); > + } > + } else { > + if (*old_ip4_pptr) { > + free(CONST_CAST(void *, *old_ip4_pptr)); > + *old_ip4_pptr = NULL; > + } > + } > + } else { > + if (*old_ip4_pptr) { > + free(CONST_CAST(void *, *old_ip4_pptr)); > + *old_ip4_pptr = NULL; > + } > + } > +} > + > enum engine_node_state > en_global_config_run(struct engine_node *node , void *data) > { > @@ -106,6 +136,27 @@ en_global_config_run(struct engine_node *node , void > *data) > } > } > > + const char *dst_monitor_mac = smap_get(&nb->options, > + "svc_monitor_mac_dst"); > + if (dst_monitor_mac) { > + if (eth_addr_from_string(dst_monitor_mac, > + &config_data->svc_monitor_mac_ea_dst)) { > + snprintf(config_data->svc_monitor_mac_dst, > + sizeof config_data->svc_monitor_mac_dst, > + ETH_ADDR_FMT, > + ETH_ADDR_ARGS(config_data->svc_monitor_mac_ea_dst)); > + } else { > + dst_monitor_mac = NULL; > + } > + } > + > + const char *monitor_ip4 = smap_get(&nb->options, "svc_monitor_ip4"); > + update_svc_monitor_addr(monitor_ip4, &config_data->svc_monitor_ip4); > + const char *monitor_ip4_dst = smap_get(&nb->options, > + "svc_monitor_ip4_dst"); > + update_svc_monitor_addr(monitor_ip4_dst, > + &config_data->svc_monitor_ip4_dst); > + > struct smap *options = &config_data->nb_options; > smap_destroy(options); > smap_clone(options, &nb->options); > @@ -121,6 +172,15 @@ en_global_config_run(struct engine_node *node , void > *data) > config_data->svc_monitor_mac); > } > > + if (!dst_monitor_mac) { > + eth_addr_random(&config_data->svc_monitor_mac_ea_dst); > + snprintf(config_data->svc_monitor_mac_dst, > + sizeof config_data->svc_monitor_mac_dst, ETH_ADDR_FMT, > + ETH_ADDR_ARGS(config_data->svc_monitor_mac_ea_dst)); > + smap_replace(options, "svc_monitor_mac_dst", > + config_data->svc_monitor_mac_dst); > + } > + > bool ic_vxlan_mode = false; > const struct nbrec_logical_switch *nbs; > NBREC_LOGICAL_SWITCH_TABLE_FOR_EACH (nbs, nbrec_ls_table) { > @@ -243,6 +303,21 @@ global_config_nb_global_handler(struct engine_node > *node, void *data) > return EN_UNHANDLED; > } > > + if (config_out_of_sync(&nb->options, &config_data->nb_options, > + "svc_monitor_mac_dst", true)) { > + return false; > + } > + > + if (config_out_of_sync(&nb->options, &config_data->nb_options, > + "svc_monitor_ip4", false)) { > + return false; > + } > + > + if (config_out_of_sync(&nb->options, &config_data->nb_options, > + "svc_monitor_ip4_dst", false)) { > + return false; > + } > + > /* Check if max_tunid has changed or not. */ > if (config_out_of_sync(&nb->options, &config_data->nb_options, > "max_tunid", true)) { > diff --git a/northd/en-global-config.h b/northd/en-global-config.h > index 55a1e420b..b76c0775a 100644 > --- a/northd/en-global-config.h > +++ b/northd/en-global-config.h > @@ -37,13 +37,19 @@ struct ed_type_global_config { > const struct nbrec_nb_global *nb_global; > const struct sbrec_sb_global *sb_global; > > - /* MAC allocated for service monitor usage. Just one mac is allocated > + /* MAC allocated for service monitor usage. Just one pair is allocated > * for this purpose and ovn-controller's on each chassis will make use > - * of this mac when sending out the packets to monitor the services > + * of this pair when sending out the packets to monitor the services > * defined in Service_Monitor Southbound table. Since these packets > - * are locally handled, having just one mac is good enough. */ > + * are locally handled, having just one pair is good enough. */ > char svc_monitor_mac[ETH_ADDR_STRLEN + 1]; > struct eth_addr svc_monitor_mac_ea; > + char svc_monitor_mac_dst[ETH_ADDR_STRLEN + 1]; > + struct eth_addr svc_monitor_mac_ea_dst; > + > + /* IP configured for LB and NF service monitor usage. */ > + const char *svc_monitor_ip4; > + const char *svc_monitor_ip4_dst; > > struct chassis_features features; > > diff --git a/northd/en-northd.c b/northd/en-northd.c > index 95491495f..0004a8ac0 100644 > --- a/northd/en-northd.c > +++ b/northd/en-northd.c > @@ -121,6 +121,10 @@ northd_get_input_data(struct engine_node *node, > input_data->sb_options = &global_config->sb_options; > input_data->svc_monitor_mac = global_config->svc_monitor_mac; > input_data->svc_monitor_mac_ea = global_config->svc_monitor_mac_ea; > + input_data->svc_monitor_mac_dst = global_config->svc_monitor_mac_dst; > + input_data->svc_monitor_mac_ea_dst = > global_config->svc_monitor_mac_ea_dst; > + input_data->svc_monitor_ip4 = global_config->svc_monitor_ip4; > + input_data->svc_monitor_ip4_dst = global_config->svc_monitor_ip4_dst; > input_data->features = &global_config->features; > input_data->vxlan_mode = global_config->vxlan_mode; > } > diff --git a/northd/en-sync-sb.c b/northd/en-sync-sb.c > index a111f14fd..c37c7bc04 100644 > --- a/northd/en-sync-sb.c > +++ b/northd/en-sync-sb.c > @@ -49,7 +49,8 @@ static void sync_addr_sets(struct ovsdb_idl_txn *ovnsb_txn, > const struct sbrec_address_set_table *, > const struct lr_stateful_table *, > const struct ovn_datapaths *, > - const char *svc_monitor_macp); > + const char *svc_monitor_macp, > + const char *svc_monitor_macp_dst); > static const struct sbrec_address_set *sb_address_set_lookup_by_name( > struct ovsdb_idl_index *, const char *name); > static void update_sb_addr_set(struct sorted_array *, > @@ -104,7 +105,8 @@ en_sync_to_sb_addr_set_run(struct engine_node *node, void > *data OVS_UNUSED) > nb_port_group_table, sb_address_set_table, > &lr_stateful_data->table, > &northd_data->lr_datapaths, > - global_config->svc_monitor_mac); > + global_config->svc_monitor_mac, > + global_config->svc_monitor_mac_dst); > > return EN_UPDATED; > } > @@ -464,7 +466,8 @@ sync_addr_sets(struct ovsdb_idl_txn *ovnsb_txn, > const struct sbrec_address_set_table *sb_address_set_table, > const struct lr_stateful_table *lr_statefuls, > const struct ovn_datapaths *lr_datapaths, > - const char *svc_monitor_macp) > + const char *svc_monitor_macp, > + const char *svc_monitor_macp_dst) > { > struct shash sb_address_sets = SHASH_INITIALIZER(&sb_address_sets); > > @@ -474,8 +477,11 @@ sync_addr_sets(struct ovsdb_idl_txn *ovnsb_txn, > shash_add(&sb_address_sets, sb_address_set->name, sb_address_set); > } > > - /* Service monitor MAC. */ > - struct sorted_array svc = sorted_array_create(&svc_monitor_macp, 1, > false); > + /* Service monitor MACs. */ > + const char *svc_macs[] = {svc_monitor_macp, svc_monitor_macp_dst}; > + size_t n_macs = sizeof(svc_macs) / sizeof(svc_macs[0]); > + struct sorted_array svc = sorted_array_create(svc_macs, n_macs, > + false); > sync_addr_set(ovnsb_txn, "svc_monitor_mac", &svc, &sb_address_sets); > sorted_array_destroy(&svc); > > diff --git a/northd/northd.c b/northd/northd.c > index 5dc7e0d87..137bed00e 100644 > --- a/northd/northd.c > +++ b/northd/northd.c > @@ -3721,7 +3721,9 @@ get_service_mon(const struct hmap *monitor_map, > static struct service_monitor_info * > create_or_get_service_mon(struct ovsdb_idl_txn *ovnsb_txn, > struct hmap *monitor_map, > - const char *ip, const char *logical_port, > + const char *type, const char *ip, > + const char *logical_port, > + const char *logical_input_port, > uint16_t service_port, const char *protocol, > const char *chassis_name) > { > @@ -3745,9 +3747,14 @@ create_or_get_service_mon(struct ovsdb_idl_txn > *ovnsb_txn, > > struct sbrec_service_monitor *sbrec_mon = > sbrec_service_monitor_insert(ovnsb_txn); > + sbrec_service_monitor_set_type(sbrec_mon, type); > sbrec_service_monitor_set_ip(sbrec_mon, ip); > sbrec_service_monitor_set_port(sbrec_mon, service_port); > sbrec_service_monitor_set_logical_port(sbrec_mon, logical_port); > + if (logical_input_port) { > + sbrec_service_monitor_set_logical_input_port(sbrec_mon, > + logical_input_port); > + } > sbrec_service_monitor_set_protocol(sbrec_mon, protocol); > if (chassis_name) { > sbrec_service_monitor_set_chassis_name(sbrec_mon, chassis_name); > @@ -3758,6 +3765,99 @@ create_or_get_service_mon(struct ovsdb_idl_txn > *ovnsb_txn, > return mon_info; > } > > +static void > +ovn_nf_svc_create(struct ovsdb_idl_txn *ovnsb_txn, > + struct hmap *monitor_map, > + struct sset *svc_monitor_lsps, > + struct hmap *ls_ports, > + const char *mac_src, const char *mac_dst, > + const char *ip_src, const char *ip_dst, > + const char *logical_port, const char *logical_input_port, > + const struct smap *health_check_options) > +{ > + if (!ip_src || !ip_dst || !mac_src || !mac_dst) { > + static struct vlog_rate_limit rl = > + VLOG_RATE_LIMIT_INIT(1, 1); > + VLOG_ERR_RL(&rl, "NetworkFunction: invalid service monitor > src_mac:%s " > + "dst_mac:%s src_ip:%s dst_ip:%s\n", > + mac_src, mac_dst, ip_src, ip_dst); > + return; > + } > + > + const char *ports[] = {logical_port, logical_input_port}; > + size_t n_ports = sizeof(ports) / sizeof(ports[0]); > + const char *chassis_name = NULL; > + bool port_up = true; > + > + for (int i = 0; i < n_ports; i++) { > + const char *port = ports[i]; > + sset_add(svc_monitor_lsps, port); > + struct ovn_port *op = ovn_port_find(ls_ports, port); > + if (op == NULL) { > + static struct vlog_rate_limit rl = > + VLOG_RATE_LIMIT_INIT(1, 1); > + VLOG_ERR_RL(&rl, "NetworkFunction: skip health check, port:%s " > + "not found\n", port); > + return; > + } > + > + if (op->sb && op->sb->chassis) { > + if (chassis_name == NULL) { > + chassis_name = op->sb->chassis->name; > + } else if (strcmp(chassis_name, op->sb->chassis->name)) { > + static struct vlog_rate_limit rl = > + VLOG_RATE_LIMIT_INIT(1, 1); > + VLOG_ERR_RL(&rl, "NetworkFunction: chassis mismatch " > + " chassis:%s port:%s\n", op->sb->chassis->name, port); > + } > + } > + port_up &= (op->sb->n_up && op->sb->up[0]); > + } > + > + > + struct service_monitor_info *mon_info = > + create_or_get_service_mon(ovnsb_txn, monitor_map, > + "network-function", ip_dst, > + logical_port, > + logical_input_port, > + 0, > + "icmp", > + chassis_name); > + ovs_assert(mon_info); > + sbrec_service_monitor_set_options( > + mon_info->sbrec_mon, health_check_options); > + > + if (!mon_info->sbrec_mon->src_mac || > + strcmp(mon_info->sbrec_mon->src_mac, mac_src)) { > + sbrec_service_monitor_set_src_mac(mon_info->sbrec_mon, > + mac_src); > + } > + > + if (!mon_info->sbrec_mon->mac || > + strcmp(mon_info->sbrec_mon->mac, mac_dst)) { > + sbrec_service_monitor_set_mac(mon_info->sbrec_mon, > + mac_dst); > + } > + > + if (!mon_info->sbrec_mon->src_ip || > + strcmp(mon_info->sbrec_mon->src_ip, ip_src)) { > + sbrec_service_monitor_set_src_ip(mon_info->sbrec_mon, ip_src); > + } > + > + if (!mon_info->sbrec_mon->ip || > + strcmp(mon_info->sbrec_mon->ip, ip_dst)) { > + sbrec_service_monitor_set_ip(mon_info->sbrec_mon, ip_dst); > + } > + > + if (!port_up > + && mon_info->sbrec_mon->status > + && !strcmp(mon_info->sbrec_mon->status, "online")) { > + sbrec_service_monitor_set_status(mon_info->sbrec_mon, > + "offline"); > + } > + mon_info->required = true; > +} > + > static void > ovn_lb_svc_create(struct ovsdb_idl_txn *ovnsb_txn, > const struct ovn_northd_lb *lb, > @@ -3804,8 +3904,10 @@ ovn_lb_svc_create(struct ovsdb_idl_txn *ovnsb_txn, > > struct service_monitor_info *mon_info = > create_or_get_service_mon(ovnsb_txn, monitor_map, > + "load-balancer", > backend->ip_str, > backend_nb->logical_port, > + NULL, > backend->port, > protocol, > chassis_name); > @@ -4042,12 +4144,16 @@ build_lb_datapaths(const struct hmap *lbs, const > struct hmap *lb_groups, > } > > static void > -build_lb_svcs( > +build_svcs( > struct ovsdb_idl_txn *ovnsb_txn, > const struct sbrec_service_monitor_table *sbrec_service_monitor_table, > const char *svc_monitor_mac, > const struct eth_addr *svc_monitor_mac_ea, > + const char *svc_monitor_mac_dst, > + const char *svc_monitor_ip4, > + const char *svc_monitor_ip4_dst, > struct hmap *ls_ports, struct hmap *lb_dps_map, > + const struct nbrec_network_function_table *nbrec_network_function_table, > struct sset *svc_monitor_lsps, > struct hmap *svc_monitor_map) > { > @@ -4070,6 +4176,21 @@ build_lb_svcs( > svc_monitor_lsps); > } > > + const struct nbrec_network_function *nbrec_nf; > + NBREC_NETWORK_FUNCTION_TABLE_FOR_EACH (nbrec_nf, > + nbrec_network_function_table) { > + if (nbrec_nf->health_check) { > + ovn_nf_svc_create(ovnsb_txn, > + svc_monitor_map, > + svc_monitor_lsps, > + ls_ports, > + svc_monitor_mac, svc_monitor_mac_dst, > + svc_monitor_ip4, svc_monitor_ip4_dst, > + nbrec_nf->outport->name, > nbrec_nf->inport->name, > + &nbrec_nf->health_check->options); > + } > + } > + > struct service_monitor_info *mon_info; > HMAP_FOR_EACH_SAFE (mon_info, hmap_node, svc_monitor_map) { > if (!mon_info->required) { > @@ -4138,18 +4259,9 @@ build_lb_count_dps(struct hmap *lb_dps_map, > */ > static void > build_lb_port_related_data( > - struct ovsdb_idl_txn *ovnsb_txn, > - const struct sbrec_service_monitor_table *sbrec_service_monitor_table, > - const char *svc_monitor_mac, > - const struct eth_addr *svc_monitor_mac_ea, > - struct ovn_datapaths *lr_datapaths, struct hmap *ls_ports, > - struct hmap *lb_dps_map, struct hmap *lb_group_dps_map, > - struct sset *svc_monitor_lsps, > - struct hmap *svc_monitor_map) > + struct ovn_datapaths *lr_datapaths, > + struct hmap *lb_dps_map, struct hmap *lb_group_dps_map) > { > - build_lb_svcs(ovnsb_txn, sbrec_service_monitor_table, svc_monitor_mac, > - svc_monitor_mac_ea, ls_ports, lb_dps_map, > - svc_monitor_lsps, svc_monitor_map); > build_lswitch_lbs_from_lrouter(lr_datapaths, lb_dps_map, > lb_group_dps_map); > } > > @@ -18048,16 +18160,6 @@ build_ls_stateful_flows(const struct > ls_stateful_record *ls_stateful_rec, > build_lb_hairpin(ls_stateful_rec, od, lflows, > ls_stateful_rec->lflow_ref); > } > > -static struct nbrec_network_function * > -network_function_get_active(const struct nbrec_network_function_group *nfg) > -{ > - /* Another patch adds the healthmon support. This is temporary. */ > - if (nfg->n_network_function == 0) { > - return NULL; > - } > - return nfg->network_function[0]; > -} > - > /* For packets received on tunnel and egressing towards a network-function > port > * commit the tunnel interface id in CT. This will be utilized when the > packet > * comes out of the other network-function interface of the service VM. The > @@ -18100,6 +18202,101 @@ build_lswitch_stateful_nf(struct ovn_port *op, > ds_cstr(match), ds_cstr(actions), lflow_ref); > } > > +static struct nbrec_network_function * > +network_function_get_active(const struct nbrec_network_function_group *nfg) > +{ > + return nfg->network_function_active; > +} > + > +static void > +network_function_update_active(const struct nbrec_network_function_group > *nfg, > + const struct hmap *svc_monitor_map, > + const char *svc_monitor_ip4_dst) > +{ > + if (!nfg->n_network_function) { > + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); > + VLOG_ERR_RL(&rl, "NetworkFunction: No network_function found in " > + "network_function_group %s", nfg->name); > + if (nfg->network_function_active) { > + nbrec_network_function_group_set_network_function_active(nfg, > + NULL); > + } > + return; > + } > + struct nbrec_network_function *nf_active = nfg->network_function[0]; > + struct nbrec_network_function *nf_active_prev = NULL; > + uint16_t best_score = 0; > + bool healthy_nf_available = false; > + if (nfg->network_function_active) { > + nf_active_prev = nfg->network_function_active; > + } > + > + for (int i = 0; i < nfg->n_network_function; i++) { > + struct nbrec_network_function *nf = nfg->network_function[i]; > + uint16_t curr_score = 0; > + if (nf->health_check == NULL) { > + VLOG_DBG("NetworkFunction: Health check is not configured for " > + "network_function %s", nf->name); > + /* Consider network_function as healthy if health_check is > + * not configured. */ > + curr_score += 3; > + healthy_nf_available = true; > + } else { > + struct service_monitor_info *mon_info = > + get_service_mon(svc_monitor_map, svc_monitor_ip4_dst, > + nf->outport->name, 0, "icmp"); > + if (mon_info == NULL) { > + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, > 1); > + VLOG_ERR_RL(&rl, "NetworkFunction: Service_monitor is not " > + "found for network_function:%s", nf->name); > + } else if (mon_info->sbrec_mon->status > + && !strcmp(mon_info->sbrec_mon->status, "online")) { > + curr_score += 3; > + healthy_nf_available = true; > + } > + } > + > + if (nf_active_prev && (nf == nf_active_prev)) { > + curr_score += 1; > + } > + > + if (curr_score > best_score) { > + nf_active = nf; > + best_score = curr_score; > + } > + } > + > + if (!healthy_nf_available) { > + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); > + VLOG_WARN_RL(&rl, "NetworkFunction: No healthy network_function > found " > + "in network_function_group %s, " > + "selected network_function %s as active", nfg->name, > + nf_active->name); > + } > + > + if (nf_active_prev != nf_active) { > + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); > + VLOG_INFO_RL(&rl, "NetworkFunction: Update active network_function > %s " > + "in network_function_group %s", > + nf_active->name, nfg->name); > + nbrec_network_function_group_set_network_function_active(nfg, > + nf_active); > + } > +} > + > +static void build_network_function_active( > + const struct nbrec_network_function_group_table *nbrec_nfg_table, > + struct hmap *svc_monitor_map, > + const char *svc_monitor_ip4_dst) > +{ > + const struct nbrec_network_function_group *nbrec_nfg; > + NBREC_NETWORK_FUNCTION_GROUP_TABLE_FOR_EACH (nbrec_nfg, > + nbrec_nfg_table) { > + network_function_update_active(nbrec_nfg, svc_monitor_map, > + svc_monitor_ip4_dst); > + } > +} > + > static void > consider_network_function( > struct lflow_table *lflows, > @@ -20031,18 +20228,25 @@ ovnnb_db_run(struct northd_input *input_data, > input_data->sbrec_ha_chassis_grp_by_name, > &data->ls_datapaths.datapaths, &data->lr_datapaths.datapaths, > &data->ls_ports, &data->lr_ports); > - build_lb_port_related_data(ovnsb_txn, > - input_data->sbrec_service_monitor_table, > - input_data->svc_monitor_mac, > - &input_data->svc_monitor_mac_ea, > - &data->lr_datapaths, &data->ls_ports, > + build_lb_port_related_data(&data->lr_datapaths, > &data->lb_datapaths_map, > - &data->lb_group_datapaths_map, > - &data->svc_monitor_lsps, > - &data->svc_monitor_map); > + &data->lb_group_datapaths_map); > + build_svcs(ovnsb_txn, input_data->sbrec_service_monitor_table, > + input_data->svc_monitor_mac, > + &input_data->svc_monitor_mac_ea, > + input_data->svc_monitor_mac_dst, > + input_data->svc_monitor_ip4, > + input_data->svc_monitor_ip4_dst, > + &data->ls_ports, &data->lb_datapaths_map, > + input_data->nbrec_network_function_table, > + &data->svc_monitor_lsps, &data->svc_monitor_map); > build_lb_count_dps(&data->lb_datapaths_map, > ods_size(&data->ls_datapaths), > ods_size(&data->lr_datapaths)); > + build_network_function_active( > + input_data->nbrec_network_function_group_table, > + &data->svc_monitor_map, > + input_data->svc_monitor_ip4_dst); > build_ipam(&data->ls_datapaths.datapaths, &data->ls_ports); > build_lrouter_groups(&data->lr_ports, &data->lr_datapaths); > build_ip_mcast(ovnsb_txn, input_data->sbrec_ip_multicast_table, > diff --git a/northd/northd.h b/northd/northd.h > index 95c7eb9f4..0a49e43b5 100644 > --- a/northd/northd.h > +++ b/northd/northd.h > @@ -68,6 +68,10 @@ struct northd_input { > const struct smap *sb_options; > const char *svc_monitor_mac; > struct eth_addr svc_monitor_mac_ea; > + const char *svc_monitor_mac_dst; > + struct eth_addr svc_monitor_mac_ea_dst; > + const char *svc_monitor_ip4; > + const char *svc_monitor_ip4_dst; > const struct chassis_features *features; > bool vxlan_mode; > > @@ -240,8 +244,8 @@ struct lflow_input { > const struct hmap *lb_datapaths_map; > const struct sset *bfd_ports; > const struct chassis_features *features; > - const struct hmap *svc_monitor_map; > bool ovn_internal_version_changed; > + const struct hmap *svc_monitor_map; > const char *svc_monitor_mac; > const struct sampling_app_table *sampling_apps; > struct group_ecmp_route_data *route_data; > diff --git a/ovn-sb.ovsschema b/ovn-sb.ovsschema > index 4c24f5b51..dd360a87a 100644 > --- a/ovn-sb.ovsschema > +++ b/ovn-sb.ovsschema > @@ -1,7 +1,7 @@ > { > "name": "OVN_Southbound", > - "version": "21.2.0", > - "cksum": "29145795 34859", > + "version": "21.3.0", > + "cksum": "4131927810 35186", > "tables": { > "SB_Global": { > "columns": { > @@ -505,14 +505,20 @@ > "isRoot": true}, > "Service_Monitor": { > "columns": { > + "type": {"type": {"key": { > + "type": "string", > + "enum": ["set", ["load-balancer", > + "network-function"]]}}}, > "ip": {"type": "string"}, > + "mac": {"type": "string"}, > "protocol": { > "type": {"key": {"type": "string", > - "enum": ["set", ["tcp", "udp"]]}, > + "enum": ["set", ["tcp", "udp", "icmp"]]}, > "min": 0, "max": 1}}, > "port": {"type": {"key": {"type": "integer", > "minInteger": 0, > "maxInteger": 65535}}}, > + "logical_input_port": {"type": "string"}, > "logical_port": {"type": "string"}, > "src_mac": {"type": "string"}, > "src_ip": {"type": "string"}, > diff --git a/ovn-sb.xml b/ovn-sb.xml > index db5faac66..fbe1a746e 100644 > --- a/ovn-sb.xml > +++ b/ovn-sb.xml > @@ -4980,10 +4980,20 @@ tcp.flags = RST; > service monitor. > </p> > > + <column name="type"> > + The type of the service. Supported values are "load-balancer" and > + "network-function". > + </column> > + > <column name="ip"> > + Destination IP used in monitor packets. For load-balancer this is the > IP of the service to be monitored. Only IPv4 is supported. > </column> > > + <column name="mac"> > + Destination MAC address used in monitor packets for network-function. > + </column> > + > <column name="protocol"> > The protocol of the service. > </column> > @@ -4992,10 +5002,20 @@ tcp.flags = RST; > The TCP or UDP port of the service. > </column> > > + <column name="logical_input_port"> > + This is applicable only for network-function type. The VIF of the > + logical port on which monitor packets have to be sent. The > + <code>ovn-controller</code> that binds this <code>logical_port</code> > + monitors the service by sending periodic monitor packets. > + </column> > + > <column name="logical_port"> > The VIF of the logical port on which the service is running. The > <code>ovn-controller</code> that binds this <code>logical_port</code> > - monitors the service by sending periodic monitor packets. > + monitors the service by sending periodic monitor packets. For > + load-balancer this is the port to which monitor packets are sent and > + from which response packets are received. For network-function this > + is the port from which the forwarded monitor packets are received. > </column> > > <column name="src_mac"> > -- > 2.39.3 > > _______________________________________________ > dev mailing list > d...@openvswitch.org > https://mail.openvswitch.org/mailman/listinfo/ovs-dev _______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev