Add support for health check monitoring on logical switch ports via new NB table Logical_Switch_Port_Health_Check. Supports ICMP/TCP/UDP probes with configurable source IP and options.
- Extend service monitor for LSP monitoring type - Add ICMP probe handling with sequence validation - Generate necessary ARP/ND responses for health checks Signed-off-by: Alexandra Rukomoinikova <[email protected]> --- controller/pinctrl.c | 88 +++++++---- northd/en-northd.c | 2 + northd/inc-proc-northd.c | 5 +- northd/northd.c | 310 ++++++++++++++++++++++++++++++++------- northd/northd.h | 3 + tests/ovn-northd.at | 134 +++++++++++++++++ tests/system-ovn.at | 69 +++++++++ 7 files changed, 525 insertions(+), 86 deletions(-) diff --git a/controller/pinctrl.c b/controller/pinctrl.c index 2fb2dcded..3faba9b8e 100644 --- a/controller/pinctrl.c +++ b/controller/pinctrl.c @@ -6857,6 +6857,8 @@ enum svc_monitor_type { SVC_MON_TYPE_LB, /* network function */ SVC_MON_TYPE_NF, + /* logical switch port */ + SVC_MON_TYPE_LSP, }; /* Service monitor health checks. */ @@ -6981,20 +6983,26 @@ 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")) { + enum svc_monitor_protocol protocol; + + if (sb_svc_mon->type && + !strcmp(sb_svc_mon->type, "network-function")) { mon_type = SVC_MON_TYPE_NF; + } else if (sb_svc_mon->type && + !strcmp(sb_svc_mon->type, "logical-switch-port")) { + mon_type = SVC_MON_TYPE_LSP; } else { mon_type = SVC_MON_TYPE_LB; } - enum svc_monitor_protocol protocol; if (!strcmp(sb_svc_mon->protocol, "udp")) { - protocol = SVC_MON_PROTO_UDP; + protocol = (mon_type == SVC_MON_TYPE_NF) ? + SVC_MON_PROTO_ICMP : SVC_MON_PROTO_UDP; } else if (!strcmp(sb_svc_mon->protocol, "icmp")) { protocol = SVC_MON_PROTO_ICMP; } else { - protocol = SVC_MON_PROTO_TCP; + protocol = (mon_type == SVC_MON_TYPE_NF) ? + SVC_MON_PROTO_ICMP : SVC_MON_PROTO_TCP; } const struct sbrec_port_binding *pb @@ -7031,9 +7039,6 @@ sync_svc_monitors(struct ovsdb_idl_txn *ovnsb_idl_txn, bool mac_found = false; 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) { @@ -7048,11 +7053,6 @@ sync_svc_monitors(struct ovsdb_idl_txn *ovnsb_idl_txn, } } } else { - if (protocol != SVC_MON_PROTO_TCP && - protocol != SVC_MON_PROTO_UDP) { - continue; - } - for (size_t i = 0; i < pb->n_mac && !mac_found; i++) { struct lport_addresses laddrs; @@ -8011,6 +8011,7 @@ static void svc_monitor_send_icmp_health_check__(struct rconn *swconn, struct svc_monitor *svc_mon) { + bool svc_mon_nf = (svc_mon->type == SVC_MON_TYPE_NF) ? true : false; uint64_t packet_stub[128 / 8]; struct dp_packet packet; dp_packet_use_stub(&packet, packet_stub, sizeof packet_stub); @@ -8057,7 +8058,8 @@ svc_monitor_send_icmp_health_check__(struct rconn *swconn, 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(svc_mon_nf ? svc_mon->input_port_key : svc_mon->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; @@ -8335,6 +8337,7 @@ pinctrl_handle_svc_check(struct rconn *swconn, const struct flow *ip_flow, "not found"); return; } + pinctrl_handle_tcp_svc_check(swconn, pkt_in, svc_mon); } else { const char *end = @@ -8347,48 +8350,69 @@ pinctrl_handle_svc_check(struct rconn *swconn, const struct flow *ip_flow, return; } - /* Handle ICMP ECHO REQUEST probes for Network Function services */ + /* Handle ICMP ECHO REQUEST probes for Network Function and + * Logical Switch Port services */ 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, + if ((ih->icmp_type == ICMP4_ECHO_REQUEST || + ih->icmp_type == ICMP4_ECHO_REPLY) && ih->icmp_code == 0) { + uint32_t hash = + hash_bytes(&ip_addr, sizeof ip_addr, + hash_3words(dp_key, port_key, + ntohs(ip_flow->tp_src))); + + struct svc_monitor *svc_mon = + pinctrl_find_svc_monitor(dp_key, port_key, + ih->icmp_type == + ICMP4_ECHO_REQUEST ? + &dst_ip_addr : &ip_addr, 0, SVC_MON_PROTO_ICMP, hash); if (!svc_mon) { - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT( - 1, 5); + 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); - } + + /* Type validation done during creation - + * asserts on unsupported types. */ + ovs_assert(svc_mon->type != SVC_MON_TYPE_NF || + svc_mon->type != SVC_MON_TYPE_LSP); + + pinctrl_handle_icmp_svc_check(pkt_in, svc_mon); + return; } } else if (in_eth->eth_type == htons(ETH_TYPE_IPV6)) { struct icmp6_data_header *ih6 = l4h; /* It's ICMPv6 packet. */ - if (ih6->icmp6_base.icmp6_type == ICMP6_ECHO_REQUEST && + if ((ih6->icmp6_base.icmp6_type == ICMP6_ECHO_REQUEST || + ih6->icmp6_base.icmp6_type == ICMP6_ECHO_REPLY) && ih6->icmp6_base.icmp6_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, + pinctrl_find_svc_monitor(dp_key, port_key, + ih6->icmp6_base.icmp6_type == + ICMP6_ECHO_REQUEST ? + &dst_ip_addr : &ip_addr, 0, SVC_MON_PROTO_ICMP, hash); if (!svc_mon) { - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT( - 1, 5); + static struct vlog_rate_limit rl = + VLOG_RATE_LIMIT_INIT(1, 5); VLOG_WARN_RL(&rl, "handle service check: Service monitor " "not found for ICMPv6 request"); return; } - if (svc_mon->type == SVC_MON_TYPE_NF) { - pinctrl_handle_icmp_svc_check(pkt_in, svc_mon); - } + + /* Type validation done during creation + * - asserts on unsupported types. */ + ovs_assert(svc_mon->type != SVC_MON_TYPE_NF || + svc_mon->type != SVC_MON_TYPE_LSP); + + pinctrl_handle_icmp_svc_check(pkt_in, svc_mon); return; } } diff --git a/northd/en-northd.c b/northd/en-northd.c index 6815e6e39..9b37f3eee 100644 --- a/northd/en-northd.c +++ b/northd/en-northd.c @@ -82,6 +82,8 @@ northd_get_input_data(struct engine_node *node, EN_OVSDB_GET(engine_get_input("NB_network_function", node)); input_data->nbrec_network_function_group_table = EN_OVSDB_GET(engine_get_input("NB_network_function_group", node)); + input_data->nbrec_lsp_hc_table = EN_OVSDB_GET(engine_get_input( + "NB_logical_switch_port_health_check", node)); input_data->sbrec_port_binding_table = EN_OVSDB_GET(engine_get_input("SB_port_binding", node)); diff --git a/northd/inc-proc-northd.c b/northd/inc-proc-northd.c index 94199de12..5c41fd3ac 100644 --- a/northd/inc-proc-northd.c +++ b/northd/inc-proc-northd.c @@ -74,7 +74,8 @@ static unixctl_cb_func chassis_features_list; NB_NODE(chassis_template_var) \ NB_NODE(sampling_app) \ NB_NODE(network_function) \ - NB_NODE(network_function_group) + NB_NODE(network_function_group) \ + NB_NODE(logical_switch_port_health_check) enum nb_engine_node { #define NB_NODE(NAME) NB_##NAME, @@ -254,6 +255,8 @@ void inc_proc_northd_init(struct ovsdb_idl_loop *nb, engine_add_input(&en_northd, &en_nb_chassis_template_var, NULL); engine_add_input(&en_northd, &en_nb_network_function, NULL); engine_add_input(&en_northd, &en_nb_network_function_group, NULL); + engine_add_input(&en_northd, &en_nb_logical_switch_port_health_check, + NULL); engine_add_input(&en_northd, &en_sb_chassis, NULL); engine_add_input(&en_northd, &en_sb_mirror, NULL); diff --git a/northd/northd.c b/northd/northd.c index cdf12ec86..a9c700bb2 100644 --- a/northd/northd.c +++ b/northd/northd.c @@ -3027,14 +3027,14 @@ get_service_mon(const struct hmap *local_svc_monitors_map, return NULL; } -static void +static inline void set_service_mon_options(const struct sbrec_service_monitor *sbrec_mon, - const struct smap *nb_hc_options, + const struct smap *options, const char *target_az_name) { struct smap sb_svc_options = SMAP_INITIALIZER(&sb_svc_options); - smap_clone(&sb_svc_options, nb_hc_options); + smap_clone(&sb_svc_options, options); if (target_az_name) { smap_add(&sb_svc_options, "az-name", target_az_name); } @@ -3042,6 +3042,40 @@ set_service_mon_options(const struct sbrec_service_monitor *sbrec_mon, smap_destroy(&sb_svc_options); } +static inline void +configure_service_mon_rec(const struct sbrec_service_monitor *sbrec_mon, + const char *src_ip, + const char *target_az_name, + const char *svc_monitor_mac, + const struct eth_addr *svc_monitor_mac_ea, + const struct smap *options) +{ + set_service_mon_options(sbrec_mon, options, target_az_name); + + struct eth_addr ea; + if (!sbrec_mon->src_mac || + !eth_addr_from_string(sbrec_mon->src_mac, &ea) || + !eth_addr_equals(ea, *svc_monitor_mac_ea)) { + sbrec_service_monitor_set_src_mac(sbrec_mon, svc_monitor_mac); + } + + if (!sbrec_mon->src_ip || + strcmp(sbrec_mon->src_ip, src_ip)) { + sbrec_service_monitor_set_src_ip(sbrec_mon, src_ip); + } +} + +static inline void +update_status_to_service_mon(const struct sbrec_service_monitor *sbrec_mon, + const struct ovn_port *op, + bool remote_backend) +{ + if (!remote_backend && (!op->sb->n_up || !op->sb->up[0]) && + sbrec_mon->status && !strcmp(sbrec_mon->status, "online")) { + sbrec_service_monitor_set_status(sbrec_mon, "offline"); + } +} + static struct service_monitor_info * create_or_get_service_mon(struct ovsdb_idl_txn *ovnsb_txn, struct hmap *local_svc_monitors_map, @@ -3055,7 +3089,8 @@ create_or_get_service_mon(struct ovsdb_idl_txn *ovnsb_txn, struct service_monitor_info *mon_info = get_service_mon(local_svc_monitors_map, ic_learned_svc_monitors_map, - ip, logical_port, service_port, protocol); + ip, logical_port, + service_port, protocol); if (mon_info) { if (chassis_name && strcmp(mon_info->sbrec_mon->chassis_name, @@ -3191,6 +3226,33 @@ ovn_nf_svc_create(struct ovsdb_idl_txn *ovnsb_txn, mon_info->required = true; } +static inline bool +check_svc_port_available(const char *logical_port, + const bool remote_backend, + struct hmap *ls_ports, + struct sset *svc_monitor_lsps, + struct ovn_port **op, + char **chassis_name) +{ + sset_add(svc_monitor_lsps, logical_port); + + struct ovn_port *op_p = ovn_port_find(ls_ports, logical_port); + + if (!remote_backend && + (!op_p || !lsp_is_enabled(op_p->nbsp))) { + return false; + } + + if (!remote_backend && + op_p->sb && op_p->sb->chassis) { + *chassis_name = op_p->sb->chassis->name; + } + + *op = op_p; + + return true; +} + static void ovn_lb_svc_create(struct ovsdb_idl_txn *ovnsb_txn, const struct ovn_northd_lb *lb, @@ -3214,30 +3276,26 @@ ovn_lb_svc_create(struct ovsdb_idl_txn *ovnsb_txn, vector_get_ptr(&lb_vip->backends, j); struct ovn_northd_lb_backend *backend_nb = &lb_vip_nb->backends_nb[j]; + const char *protocol = lb->nlb->protocol; + struct ovn_port *op = NULL; + char *chassis_name = NULL; if (!backend_nb->health_check) { continue; } - sset_add(svc_monitor_lsps, backend_nb->logical_port); - struct ovn_port *op = ovn_port_find(ls_ports, - backend_nb->logical_port); - - if (!backend_nb->remote_backend && - (!op || !lsp_is_enabled(op->nbsp))) { - continue; + if (!check_svc_port_available(backend_nb->logical_port, + backend_nb->remote_backend, + ls_ports, + svc_monitor_lsps, + &op, &chassis_name)) { + continue; } - const char *protocol = lb->nlb->protocol; if (!protocol || !protocol[0]) { protocol = "tcp"; } - const char *chassis_name = NULL; - if (!backend_nb->remote_backend && op->sb->chassis) { - chassis_name = op->sb->chassis->name; - } - struct service_monitor_info *mon_info = create_or_get_service_mon(ovnsb_txn, local_svc_monitors_map, @@ -3251,38 +3309,115 @@ ovn_lb_svc_create(struct ovsdb_idl_txn *ovnsb_txn, chassis_name, backend_nb->remote_backend); ovs_assert(mon_info); - set_service_mon_options(mon_info->sbrec_mon, - &lb_vip_nb->lb_health_check->options, - backend_nb->az_name); - struct eth_addr ea; - if (!mon_info->sbrec_mon->src_mac || - !eth_addr_from_string(mon_info->sbrec_mon->src_mac, &ea) || - !eth_addr_equals(ea, *svc_monitor_mac_ea)) { - sbrec_service_monitor_set_src_mac(mon_info->sbrec_mon, - svc_monitor_mac); - } - if (!mon_info->sbrec_mon->src_ip || - strcmp(mon_info->sbrec_mon->src_ip, - backend_nb->svc_mon_src_ip)) { - sbrec_service_monitor_set_src_ip( - mon_info->sbrec_mon, - backend_nb->svc_mon_src_ip); - } + configure_service_mon_rec(mon_info->sbrec_mon, + backend_nb->svc_mon_src_ip, + backend_nb->az_name, + svc_monitor_mac, + svc_monitor_mac_ea, + &lb_vip_nb->lb_health_check->options); - if (!backend_nb->remote_backend && - (!op->sb->n_up || !op->sb->up[0]) - && mon_info->sbrec_mon->status - && !strcmp(mon_info->sbrec_mon->status, "online")) { - sbrec_service_monitor_set_status(mon_info->sbrec_mon, - "offline"); - } + update_status_to_service_mon(mon_info->sbrec_mon, + op, backend_nb->remote_backend); mon_info->required = true; } } } +static void +ovn_lsp_svc_monitor_add_address(struct ovsdb_idl_txn *ovnsb_txn, + struct nbrec_logical_switch_port_health_check *lsp_hc, + const struct ovn_port *op, + const char *address, + const char *svc_monitor_mac, + const struct eth_addr *svc_monitor_mac_ea, + struct hmap *local_svc_monitors_map) +{ + struct service_monitor_info *mon_info = + create_or_get_service_mon(ovnsb_txn, + local_svc_monitors_map, + NULL, + "logical-switch-port", + address, + op->key, + NULL, + lsp_hc->port, + lsp_hc->protocol, + (op->sb && op->sb->chassis) ? + op->sb->chassis->name : NULL, + false); + + ovs_assert(mon_info); + + configure_service_mon_rec(mon_info->sbrec_mon, + lsp_hc->src_ip, + NULL, + svc_monitor_mac, + svc_monitor_mac_ea, + &lsp_hc->options); + + update_status_to_service_mon(mon_info->sbrec_mon, op, false); + + mon_info->required = true; +} + +static void +ovn_lsp_svc_create_record(struct ovsdb_idl_txn *ovnsb_txn, + struct nbrec_logical_switch_port_health_check *lsp_hc, + const struct ovn_port *op, + const char *svc_monitor_mac, + const struct eth_addr *svc_monitor_mac_ea, + struct hmap *local_monitor_map) +{ + if (!lsp_hc->n_addresses) { + for (size_t i = 0; i < op->n_lsp_addrs; i++) { + for (size_t j = 0; j < op->lsp_addrs[i].n_ipv4_addrs; j++) { + ovn_lsp_svc_monitor_add_address( + ovnsb_txn, lsp_hc, op, + op->lsp_addrs[i].ipv4_addrs[j].addr_s, + svc_monitor_mac, svc_monitor_mac_ea, + local_monitor_map); + } + for (size_t j = 0; j < op->lsp_addrs[i].n_ipv6_addrs; j++) { + ovn_lsp_svc_monitor_add_address( + ovnsb_txn, lsp_hc, op, + op->lsp_addrs[i].ipv6_addrs[j].addr_s, + svc_monitor_mac, svc_monitor_mac_ea, + local_monitor_map); + } + } + } else { + for (size_t j = 0; j < lsp_hc->n_addresses; j++) { + ovn_lsp_svc_monitor_add_address( + ovnsb_txn, lsp_hc, op, + lsp_hc->addresses[j], + svc_monitor_mac, svc_monitor_mac_ea, + local_monitor_map); + } + } +} + +static void +ovn_lsp_svc_monitors_process_port(struct ovsdb_idl_txn *ovnsb_txn, + const struct ovn_port *op, + const char *svc_monitor_mac, + const struct eth_addr *svc_monitor_mac_ea, + struct hmap *local_monitor_map, + struct sset *svc_monitor_lsps) +{ + sset_add(svc_monitor_lsps, op->key); + + for (size_t i = 0; i < op->nbsp->n_health_checks; i++) { + ovn_lsp_svc_create_record(ovnsb_txn, + op->nbsp->health_checks[i], + op, + svc_monitor_mac, + svc_monitor_mac_ea, + local_monitor_map); + } +} + static bool build_lb_vip_actions(const struct ovn_northd_lb *lb, const struct ovn_lb_vip *lb_vip, @@ -3491,7 +3626,7 @@ build_lb_datapaths(const struct hmap *lbs, const struct hmap *lb_groups, } static void -build_svcs( +build_svc_monitors_data( struct ovsdb_idl_txn *ovnsb_txn, struct ovsdb_idl_index *sbrec_service_monitor_by_learned_type, const char *svc_monitor_mac, @@ -3503,7 +3638,8 @@ build_svcs( const struct nbrec_network_function_table *nbrec_network_function_table, struct sset *svc_monitor_lsps, struct hmap *local_svc_monitors_map, - struct hmap *ic_learned_svc_monitors_map) + struct hmap *ic_learned_svc_monitors_map, + struct hmapx *monitored_ports_map) { const struct sbrec_service_monitor *sbrec_mon; struct sbrec_service_monitor *key = @@ -3512,6 +3648,11 @@ build_svcs( sbrec_service_monitor_index_set_ic_learned(key, false); + /* Hash only the IP, port, and logical port. + * This is necessary because two service monitors with the same IP and + * logical port will have different `port` values + * (e.g., an ICMP monitor defaults its port to 0). + */ SBREC_SERVICE_MONITOR_FOR_EACH_EQUAL (sbrec_mon, key, sbrec_service_monitor_by_learned_type) { uint32_t hash = sbrec_mon->port; @@ -3553,6 +3694,18 @@ build_svcs( } } + struct hmapx_node *hmapx_node; + const struct ovn_port *op; + HMAPX_FOR_EACH (hmapx_node, monitored_ports_map) { + op = hmapx_node->data; + ovn_lsp_svc_monitors_process_port(ovnsb_txn, + op, + svc_monitor_mac, + svc_monitor_mac_ea, + local_svc_monitors_map, + svc_monitor_lsps); + } + struct service_monitor_info *mon_info; HMAP_FOR_EACH_SAFE (mon_info, hmap_node, local_svc_monitors_map) { if (!mon_info->required) { @@ -4017,6 +4170,16 @@ ovn_port_allocate_key(struct ovn_port *op) return true; } +static inline void +add_monitored_port(struct ovn_port *op, struct hmapx *monitored_ports_map) +{ + if (op->nbsp && + op->nbsp->n_health_checks && + lsp_is_enabled(op->nbsp)) { + hmapx_add(monitored_ports_map, op); + } +} + /* Updates the southbound Port_Binding table so that it contains the logical * switch ports specified by the northbound database. * @@ -4033,7 +4196,8 @@ build_ports(struct ovsdb_idl_txn *ovnsb_txn, struct ovsdb_idl_index *sbrec_chassis_by_hostname, struct ovsdb_idl_index *sbrec_ha_chassis_grp_by_name, struct hmap *ls_datapaths, struct hmap *lr_datapaths, - struct hmap *ls_ports, struct hmap *lr_ports) + struct hmap *ls_ports, struct hmap *lr_ports, + struct hmapx *monitored_ports_map) { struct ovs_list sb_only, nb_only, both; /* XXX: Add tag_alloc_table and queue_id_bitmap as part of northd_data @@ -4109,6 +4273,7 @@ build_ports(struct ovsdb_idl_txn *ovnsb_txn, &active_ha_chassis_grps); op->od->is_transit_router |= is_transit_router_port(op); ovs_list_remove(&op->list); + add_monitored_port(op, monitored_ports_map); } /* Add southbound record for each unmatched northbound record. */ @@ -4122,6 +4287,7 @@ build_ports(struct ovsdb_idl_txn *ovnsb_txn, &active_ha_chassis_grps); sbrec_port_binding_set_logical_port(op->sb, op->key); op->od->is_transit_router |= is_transit_router_port(op); + add_monitored_port(op, monitored_ports_map); ovs_list_remove(&op->list); } @@ -15250,6 +15416,34 @@ build_arp_resolve_flows_for_lsp( } } +static void +build_arp_nd_lflow_for_lsp_svc_hc(struct ovn_port *op, + const char *svc_monitor_mac, + struct lflow_table *lflows, + struct ds *match, + struct ds *actions) +{ + const struct nbrec_logical_switch_port *nbsp = op->nbsp; + for (size_t i = 0; i < nbsp->n_health_checks; i++) { + struct nbrec_logical_switch_port_health_check *lsp_hc = + nbsp->health_checks[i]; + ds_clear(match); + ds_clear(actions); + + bool is_ipv4 = strchr(lsp_hc->src_ip, '.') ? true : false; + + build_arp_nd_service_monitor_lflow(svc_monitor_mac, + lsp_hc->src_ip, actions, match, is_ipv4); + + ovn_lflow_add_with_hint(lflows, + op->od, + S_SWITCH_IN_ARP_ND_RSP, 110, + ds_cstr(match), ds_cstr(actions), + &op->nbsp->header_, + op->lflow_ref); + } +} + #define ICMP4_NEED_FRAG_FORMAT \ "icmp4_error {" \ "%s" \ @@ -18668,6 +18862,7 @@ build_lswitch_and_lrouter_iterate_by_lsp(struct ovn_port *op, const struct shash *meter_groups, struct ds *match, struct ds *actions, + const char *svc_monitor_mac, struct lflow_table *lflows) { ovs_assert(op->nbsp); @@ -18691,6 +18886,10 @@ build_lswitch_and_lrouter_iterate_by_lsp(struct ovn_port *op, /* Build Logical Router Flows. */ build_arp_resolve_flows_for_lsp(op, lflows, lr_ports, match, actions); + if (op->nbsp->n_health_checks) { + build_arp_nd_lflow_for_lsp_svc_hc(op, svc_monitor_mac, lflows, + match, actions); + } } /* Helper function to combine all lflow generation which is iterated by logical @@ -18794,11 +18993,8 @@ build_lflows_thread(void *arg) return NULL; } build_lswitch_and_lrouter_iterate_by_lsp(op, lsi->ls_ports, - lsi->lr_ports, - lsi->meter_groups, - &lsi->match, - &lsi->actions, - lsi->lflows); + lsi->lr_ports, lsi->meter_groups, &lsi->match, + &lsi->actions, lsi->svc_monitor_mac, lsi->lflows); build_lbnat_lflows_iterate_by_lsp( op, lsi->lr_stateful_table, &lsi->match, &lsi->actions, lsi->lflows); @@ -19065,6 +19261,7 @@ build_lswitch_and_lrouter_flows( lsi.meter_groups, &lsi.match, &lsi.actions, + lsi.svc_monitor_mac, lsi.lflows); build_lbnat_lflows_iterate_by_lsp(op, lsi.lr_stateful_table, &lsi.match, @@ -19376,6 +19573,7 @@ lflow_handle_northd_port_changes(struct ovsdb_idl_txn *ovnsb_txn, lflow_input->lr_ports, lflow_input->meter_groups, &match, &actions, + lflow_input->svc_monitor_mac, lflows); /* Sync the new flows to SB. */ bool handled = lflow_ref_sync_lflows( @@ -19435,7 +19633,9 @@ lflow_handle_northd_port_changes(struct ovsdb_idl_txn *ovnsb_txn, build_lswitch_and_lrouter_iterate_by_lsp(op, lflow_input->ls_ports, lflow_input->lr_ports, lflow_input->meter_groups, - &match, &actions, lflows); + &match, &actions, + lflow_input->svc_monitor_mac, + lflows); /* Sync the newly added flows to SB. */ bool handled = lflow_ref_sync_lflows( @@ -20101,6 +20301,7 @@ northd_init(struct northd_data *data) hmap_init(&data->lb_group_datapaths_map); sset_init(&data->svc_monitor_lsps); hmap_init(&data->local_svc_monitors_map); + hmapx_init(&data->monitored_ports_map); init_northd_tracked_data(data); } @@ -20186,6 +20387,7 @@ northd_destroy(struct northd_data *data) &data->ls_ports, &data->lr_ports); sset_destroy(&data->svc_monitor_lsps); + hmapx_destroy(&data->monitored_ports_map); destroy_northd_tracked_data(data); } @@ -20291,11 +20493,12 @@ ovnnb_db_run(struct northd_input *input_data, input_data->sbrec_chassis_by_hostname, input_data->sbrec_ha_chassis_grp_by_name, &data->ls_datapaths.datapaths, &data->lr_datapaths.datapaths, - &data->ls_ports, &data->lr_ports); + &data->ls_ports, &data->lr_ports, + &data->monitored_ports_map); build_lb_port_related_data(&data->lr_datapaths, &data->ls_datapaths, &data->lb_datapaths_map, &data->lb_group_datapaths_map); - build_svcs(ovnsb_txn, + build_svc_monitors_data(ovnsb_txn, input_data->sbrec_service_monitor_by_learned_type, input_data->svc_monitor_mac, &input_data->svc_monitor_mac_ea, @@ -20305,7 +20508,8 @@ ovnnb_db_run(struct northd_input *input_data, &data->ls_ports, &data->lb_datapaths_map, input_data->nbrec_network_function_table, &data->svc_monitor_lsps, &data->local_svc_monitors_map, - input_data->ic_learned_svc_monitors_map); + input_data->ic_learned_svc_monitors_map, + &data->monitored_ports_map); build_lb_count_dps(&data->lb_datapaths_map); build_network_function_active( input_data->nbrec_network_function_group_table, diff --git a/northd/northd.h b/northd/northd.h index 32134d36e..e16f5cbfe 100644 --- a/northd/northd.h +++ b/northd/northd.h @@ -41,6 +41,8 @@ struct northd_input { const struct nbrec_network_function_table *nbrec_network_function_table; const struct nbrec_network_function_group_table *nbrec_network_function_group_table; + const struct nbrec_logical_switch_port_health_check_table + *nbrec_lsp_hc_table; /* Southbound table references */ const struct sbrec_port_binding_table *sbrec_port_binding_table; @@ -202,6 +204,7 @@ struct northd_data { struct hmap lb_group_datapaths_map; struct sset svc_monitor_lsps; struct hmap local_svc_monitors_map; + struct hmapx monitored_ports_map; /* Change tracking data. */ struct northd_tracked_data trk_data; diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at index 448bc66ae..409dcb7d5 100644 --- a/tests/ovn-northd.at +++ b/tests/ovn-northd.at @@ -18558,3 +18558,137 @@ check_row_count Advertised_MAC_Binding 0 AT_CLEANUP ]) + +OVN_FOR_EACH_NORTHD_NO_HV([ +AT_SETUP([Logical Switch Port Health Check - lflow/service monitor synchronization]) +ovn_start + +check ovn-nbctl ls-add ls1 +check ovn-nbctl lsp-add ls1 lport1 -- \ + lsp-set-addresses lport1 "f0:00:0f:01:02:04 192.168.0.10" "f0:00:0f:01:02:06 192.168.0.11" + +check ovn-nbctl lsp-add ls1 lport2 -- \ + lsp-set-addresses lport2 "f0:00:0f:01:02:08 192.168.0.12" + +check ovn-nbctl set NB_Global . options:svc_monitor_mac="11:11:11:11:11:11" + +# ??reate a service monitor for all addresses on one of lsp. +check ovn-nbctl lsp-hc-add lport1 icmp 192.168.0.255 +check_row_count nb:Logical_Switch_Port_Health_Check 1 +lport1_uuid1=$(fetch_column nb:Logical_Switch_Port_Health_Check _uuid protocol=icmp) + +# Check lflow and service monitor synchronization +AT_CHECK([ovn-sbctl lflow-list ls1 | grep 'ls_in_arp_rsp'| grep "priority=110" | ovn_strip_lflows], [0], [dnl + table=??(ls_in_arp_rsp ), priority=110 , match=(arp.tpa == 192.168.0.255 && arp.op == 1), action=(eth.dst = eth.src; eth.src = 11:11:11:11:11:11; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; arp.sha = 11:11:11:11:11:11; arp.tpa = arp.spa; arp.spa = 192.168.0.255; outport = inport; flags.loopback = 1; output;) +]) + +check_row_count sb:Service_Monitor 2 +check_column "false false" sb:Service_Monitor ic_learned logical_port=lport1 +check_column "false false" sb:Service_Monitor remote logical_port=lport1 +check_column "192.168.0.10 192.168.0.11" sb:Service_Monitor ip logical_port=lport1 +check_column "icmp icmp" sb:Service_Monitor protocol logical_port=lport1 +check_column "192.168.0.255 192.168.0.255" sb:Service_Monitor src_ip logical_port=lport1 +check_column "0 0" sb:Service_Monitor port logical_port=lport1 +check_column "logical-switch-port logical-switch-port" sb:Service_Monitor type logical_port=lport1 +check_column "11:11:11:11:11:11 11:11:11:11:11:11" sb:Service_Monitor src_mac logical_port=lport1 + +# ??reate one more service monitor for all addresses on one of lsp. +check ovn-nbctl lsp-hc-add lport1 tcp 192.168.0.254 80 +check_row_count nb:Logical_Switch_Port_Health_Check 2 +lport1_uuid2=$(fetch_column nb:Logical_Switch_Port_Health_Check _uuid protocol=tcp) + +# Check that 2 records (2 addresses) have been created for each protocol. +check_row_count sb:Service_Monitor 4 + +AT_CHECK([ovn-sbctl lflow-list ls1 | grep 'ls_in_arp_rsp'| grep "priority=110" | ovn_strip_lflows], [0], [dnl + table=??(ls_in_arp_rsp ), priority=110 , match=(arp.tpa == 192.168.0.254 && arp.op == 1), action=(eth.dst = eth.src; eth.src = 11:11:11:11:11:11; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; arp.sha = 11:11:11:11:11:11; arp.tpa = arp.spa; arp.spa = 192.168.0.254; outport = inport; flags.loopback = 1; output;) + table=??(ls_in_arp_rsp ), priority=110 , match=(arp.tpa == 192.168.0.255 && arp.op == 1), action=(eth.dst = eth.src; eth.src = 11:11:11:11:11:11; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; arp.sha = 11:11:11:11:11:11; arp.tpa = arp.spa; arp.spa = 192.168.0.255; outport = inport; flags.loopback = 1; output;) +]) + +# Change addresses on lport - addresses for service monitors should be changed +check ovn-nbctl lsp-set-addresses lport1 "f0:00:0f:01:02:04 192.168.0.20" + +check_row_count sb:Service_Monitor 2 +check_column "false false" sb:Service_Monitor ic_learned logical_port=lport1 +check_column "false false " sb:Service_Monitor remote logical_port=lport1 +check_column "192.168.0.20 192.168.0.20" sb:Service_Monitor ip logical_port=lport1 +check_column "icmp tcp" sb:Service_Monitor protocol logical_port=lport1 +check_column "192.168.0.255 192.168.0.254" sb:Service_Monitor src_ip logical_port=lport1 +check_column "0 80" sb:Service_Monitor port logical_port=lport1 +check_column "logical-switch-port logical-switch-port" sb:Service_Monitor type logical_port=lport1 +check_column "11:11:11:11:11:11 11:11:11:11:11:11" sb:Service_Monitor src_mac logical_port=lport1 + +# Check options propogations +hc_lport1_uuid=$(fetch_column nb:logical_switch_port_health_check _uuid src_ip="192.168.0.255") + +check ovn-nbctl set logical_switch_port_health_check $hc_lport1_uuid options:interval=3 +check ovn-nbctl set logical_switch_port_health_check $hc_lport1_uuid options:timeout=30 +check ovn-nbctl set logical_switch_port_health_check $hc_lport1_uuid options:success_count=1 +check ovn-nbctl set logical_switch_port_health_check $hc_lport1_uuid options:failure_count=2 + + +sm_lport1_uuid=$(fetch_column sb:service_monitor _uuid protocol=icmp) + +AT_CHECK([ovn-sbctl get Service_Monitor $sm_lport1_uuid options:interval], +[0], ["3" +]) +AT_CHECK([ovn-sbctl get Service_Monitor $sm_lport1_uuid options:failure_count], +[0], ["2" +]) +AT_CHECK([ovn-sbctl get Service_Monitor $sm_lport1_uuid options:success_count], +[0], ["1" +]) +AT_CHECK([ovn-sbctl get Service_Monitor $sm_lport1_uuid options:timeout], +[0], ["30" +]) + +ovn-nbctl list logical_switch_port + +check ovn-nbctl lsp-hc-del lport1 + +check_row_count nb:Logical_Switch_Port_Health_Check 0 + +# Change the service monitor to monitor only one address. +check ovn-nbctl lsp-hc-add lport1 icmp 192.168.0.255 192.168.0.20 +check_row_count nb:Logical_Switch_Port_Health_Check 1 +lport1_uuid3=$(fetch_column nb:Logical_Switch_Port_Health_Check _uuid protocol=icmp) + +check_row_count sb:Service_Monitor 1 + +AT_CHECK([ovn-sbctl lflow-list ls1 | grep 'ls_in_arp_rsp'| grep "priority=110" | ovn_strip_lflows], [0], [dnl + table=??(ls_in_arp_rsp ), priority=110 , match=(arp.tpa == 192.168.0.255 && arp.op == 1), action=(eth.dst = eth.src; eth.src = 11:11:11:11:11:11; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; arp.sha = 11:11:11:11:11:11; arp.tpa = arp.spa; arp.spa = 192.168.0.255; outport = inport; flags.loopback = 1; output;) +]) + +check_column "false" sb:Service_Monitor ic_learned logical_port=lport1 +check_column "false" sb:Service_Monitor remote logical_port=lport1 +check_column "192.168.0.20" sb:Service_Monitor ip logical_port=lport1 +check_column "icmp" sb:Service_Monitor protocol logical_port=lport1 +check_column "192.168.0.255" sb:Service_Monitor src_ip logical_port=lport1 +check_column "0" sb:Service_Monitor port logical_port=lport1 +check_column "logical-switch-port" sb:Service_Monitor type logical_port=lport1 +check_column "11:11:11:11:11:11" sb:Service_Monitor src_mac logical_port=lport1 + +# Create one more monitor +check ovn-nbctl lsp-hc-add lport2 icmp 192.168.0.255 192.168.0.12 +lport1_uuid4=$(fetch_column nb:Logical_Switch_Port_Health_Check _uuid addresses="192.168.0.12") +check_row_count nb:Logical_Switch_Port_Health_Check 2 + +check_row_count sb:Service_Monitor 2 + +# One record for arp replay +AT_CHECK([ovn-sbctl lflow-list ls1 | grep 'ls_in_arp_rsp'| grep "priority=110" | ovn_strip_lflows], [0], [dnl + table=??(ls_in_arp_rsp ), priority=110 , match=(arp.tpa == 192.168.0.255 && arp.op == 1), action=(eth.dst = eth.src; eth.src = 11:11:11:11:11:11; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; arp.sha = 11:11:11:11:11:11; arp.tpa = arp.spa; arp.spa = 192.168.0.255; outport = inport; flags.loopback = 1; output;) +]) + +ovn-nbctl list logical_switch_port_health_check + +check ovn-nbctl lsp-hc-del lport1 +check ovn-nbctl lsp-hc-del lport2 +check_row_count nb:Logical_Switch_Port_Health_Check 0 +check_row_count sb:Service_Monitor 0 + +AT_CHECK([ovn-sbctl lflow-list ls1 | grep 'ls_in_arp_rsp'| grep "priority=110" | ovn_strip_lflows], [0], []) + +AT_CLEANUP +]) + diff --git a/tests/system-ovn.at b/tests/system-ovn.at index 76f73d96e..6dd12787a 100644 --- a/tests/system-ovn.at +++ b/tests/system-ovn.at @@ -19283,3 +19283,72 @@ OVS_TRAFFIC_VSWITCHD_STOP(["/.*error receiving.*/d /.*terminating with signal 15.*/d"]) AT_CLEANUP ]) + +OVN_FOR_EACH_NORTHD_NO_HV([ +AT_SETUP([Logical Switch Port Health Check]) + +ovn_start +OVS_TRAFFIC_VSWITCHD_START() +ADD_BR([br-int]) + +# Set external-ids in br-int needed for ovn-controller +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-nbctl ls-add ls1 + +ADD_NAMESPACES(lport) +ADD_VETH(lport, lport, br-int, "192.168.0.10", "f0:00:0f:01:02:04", \ + "192.168.0.1") +NS_EXEC([lport], [ip r del default via 192.168.0.1 dev lport]) +NS_EXEC([lport], [ip r add default dev lport]) + +check ovn-nbctl lsp-add ls1 lport -- \ + lsp-set-addresses lport "f0:00:0f:01:02:04 192.168.0.10" + +check ovn-nbctl --wait=hv sync + +check ovn-nbctl lsp-hc-add lport icmp 192.168.0.255 +lport1_uuid1=$(fetch_column nb:Logical_Switch_Port_Health_Check _uuid protocol=icmp) + +check_row_count sb:Service_Monitor 1 + +NETNS_START_TCPDUMP([lport], [-n -c 2 -i lport], [lport]) +OVS_WAIT_UNTIL([ + total_queries=`grep "ICMP" -c lport.tcpdump` + test "${total_queries}" = "2" +]) + +# Wait until the services are set to online. +wait_row_count Service_Monitor 1 status=online + +# Create one more health check on logical switch port +NETNS_DAEMONIZE([lport], [nc -l -k 192.168.0.10 4041], [lport_tcp.pid]) + +check ovn-nbctl lsp-hc-add lport tcp 192.168.0.255 4041 +lport1_uuid2=$(fetch_column nb:Logical_Switch_Port_Health_Check _uuid protocol=tcp) + +check_row_count sb:Service_Monitor 2 + +# Wait until the services are set to online. +wait_row_count Service_Monitor 2 status=online + +NETNS_DAEMONIZE([lport], [nc -ulp 4042], [lport_udp.pid]) + +check ovn-nbctl lsp-hc-add lport udp 192.168.0.255 4042 +lport1_uuid3=$(fetch_column nb:Logical_Switch_Port_Health_Check _uuid protocol=udp) + +check_row_count sb:Service_Monitor 3 +# Wait until the services are set to online. +wait_row_count Service_Monitor 3 status=online + +AT_CLEANUP +]) + -- 2.48.1
_______________________________________________ dev mailing list [email protected] https://mail.openvswitch.org/mailman/listinfo/ovs-dev
