@@ -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;
}
}
@@ -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));
@@ -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);
@@ -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,
@@ -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;
@@ -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
+])
+
@@ -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
+])
+
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 <arukomoinikova@k2.cloud> --- 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(-)