bbaaef
From b06fa16c0efeacc453f96e5640124acdf980bae7 Mon Sep 17 00:00:00 2001
bbaaef
From: Numan Siddique <numans@ovn.org>
bbaaef
Date: Tue, 22 Oct 2019 12:28:36 +0530
bbaaef
Subject: [PATCH 11/12] Send service monitor health checks
bbaaef
bbaaef
ovn-controller will periodically sends out the service monitor packets
bbaaef
for the services configured in the SB DB Service_Monitor table. This
bbaaef
patch makes use of the action - handle_svc_check to handle the service
bbaaef
monitor reply packets from the service.
bbaaef
bbaaef
This patch supports IPv4 TCP and UDP service monitoring. For TCP services,
bbaaef
it sends out a TCP SYN packet and expects TCP ACK packet in response.
bbaaef
If the response is received on time, the status of the service is set to
bbaaef
"online", otherwise it is set to "offline".
bbaaef
bbaaef
For UDP services, it sends out a empty UDP packet and doesn't expect any
bbaaef
reply. In case the service is down, the host running the service, sends out
bbaaef
ICMP type 3 code 4 (destination unreachable) packet. If ovn-controller receives this
bbaaef
ICMP packet, it sets the status of the service to "offline".
bbaaef
bbaaef
Right now only IPv4 service monitoring is supported. An upcoming patch will add
bbaaef
the support for IPv6.
bbaaef
bbaaef
Change-Id: Ia8457c020b6d6cea15b1e5a73989a54430d8d9de
bbaaef
Acked-by: Mark Michelson <mmichels@redhat.com>
bbaaef
Signed-off-by: Numan Siddique <numans@ovn.org>
bbaaef
---
bbaaef
 ovn/controller/ovn-controller.c |   2 +
bbaaef
 ovn/controller/pinctrl.c        | 775 ++++++++++++++++++++++++++++++--
bbaaef
 ovn/controller/pinctrl.h        |   2 +
bbaaef
 ovn/northd/ovn-northd.8.xml     |  10 +
bbaaef
 ovn/northd/ovn-northd.c         |  18 +
bbaaef
 tests/ovn.at                    | 119 +++++
bbaaef
 tests/system-common-macros.at   |   1 +
bbaaef
 tests/system-ovn.at             | 180 ++++++++
bbaaef
 8 files changed, 1081 insertions(+), 26 deletions(-)
bbaaef
bbaaef
diff --git a/ovn/controller/ovn-controller.c b/ovn/controller/ovn-controller.c
bbaaef
index cb3d63cbb..f2378ab99 100644
bbaaef
--- a/ovn/controller/ovn-controller.c
bbaaef
+++ b/ovn/controller/ovn-controller.c
bbaaef
@@ -2046,6 +2046,8 @@ main(int argc, char *argv[])
bbaaef
                                 sbrec_dns_table_get(ovnsb_idl_loop.idl),
bbaaef
                                 sbrec_controller_event_table_get(
bbaaef
                                     ovnsb_idl_loop.idl),
bbaaef
+                                sbrec_service_monitor_table_get(
bbaaef
+                                    ovnsb_idl_loop.idl),
bbaaef
                                 br_int, chassis,
bbaaef
                                 &ed_runtime_data.local_datapaths,
bbaaef
                                 &ed_runtime_data.active_tunnels);
bbaaef
diff --git a/ovn/controller/pinctrl.c b/ovn/controller/pinctrl.c
bbaaef
index c6dd69fb3..ae1b8e1ca 100644
bbaaef
--- a/ovn/controller/pinctrl.c
bbaaef
+++ b/ovn/controller/pinctrl.c
bbaaef
@@ -38,6 +38,7 @@
bbaaef
 #include "openvswitch/ofp-switch.h"
bbaaef
 #include "openvswitch/ofp-util.h"
bbaaef
 #include "openvswitch/vlog.h"
bbaaef
+#include "lib/random.h"
bbaaef
 
bbaaef
 #include "lib/dhcp.h"
bbaaef
 #include "ovn-controller.h"
bbaaef
@@ -284,6 +285,22 @@ static void run_put_vport_bindings(
bbaaef
 static void wait_put_vport_bindings(struct ovsdb_idl_txn *ovnsb_idl_txn);
bbaaef
 static void pinctrl_handle_bind_vport(const struct flow *md,
bbaaef
                                       struct ofpbuf *userdata);
bbaaef
+static void pinctrl_handle_svc_check(struct rconn *swconn,
bbaaef
+                                     const struct flow *ip_flow,
bbaaef
+                                     struct dp_packet *pkt_in,
bbaaef
+                                     const struct match *md);
bbaaef
+static void init_svc_monitors(void);
bbaaef
+static void destroy_svc_monitors(void);
bbaaef
+static void sync_svc_monitors(
bbaaef
+    struct ovsdb_idl_txn *ovnsb_idl_txn,
bbaaef
+    const struct sbrec_service_monitor_table *svc_mon_table,
bbaaef
+    struct ovsdb_idl_index *sbrec_port_binding_by_name,
bbaaef
+    const struct sbrec_chassis *our_chassis)
bbaaef
+    OVS_REQUIRES(pinctrl_mutex);
bbaaef
+static void svc_monitors_run(struct rconn *swconn,
bbaaef
+                             long long int *svc_monitors_next_run_time)
bbaaef
+    OVS_REQUIRES(pinctrl_mutex);
bbaaef
+static void svc_monitors_wait(long long int svc_monitors_next_run_time);
bbaaef
 
bbaaef
 COVERAGE_DEFINE(pinctrl_drop_put_mac_binding);
bbaaef
 COVERAGE_DEFINE(pinctrl_drop_buffered_packets_map);
bbaaef
@@ -446,6 +463,7 @@ pinctrl_init(void)
bbaaef
     init_event_table();
bbaaef
     ip_mcast_snoop_init();
bbaaef
     init_put_vport_bindings();
bbaaef
+    init_svc_monitors();
bbaaef
     pinctrl.br_int_name = NULL;
bbaaef
     pinctrl_handler_seq = seq_create();
bbaaef
     pinctrl_main_seq = seq_create();
bbaaef
@@ -1983,6 +2001,13 @@ process_packet_in(struct rconn *swconn, const struct ofp_header *msg)
bbaaef
         ovs_mutex_unlock(&pinctrl_mutex);
bbaaef
         break;
bbaaef
 
bbaaef
+    case ACTION_OPCODE_HANDLE_SVC_CHECK:
bbaaef
+        ovs_mutex_lock(&pinctrl_mutex);
bbaaef
+        pinctrl_handle_svc_check(swconn, &headers, &packet,
bbaaef
+                                 &pin.flow_metadata);
bbaaef
+        ovs_mutex_unlock(&pinctrl_mutex);
bbaaef
+        break;
bbaaef
+
bbaaef
     default:
bbaaef
         VLOG_WARN_RL(&rl, "unrecognized packet-in opcode %"PRIu32,
bbaaef
                      ntohl(ah->opcode));
bbaaef
@@ -2052,6 +2077,7 @@ pinctrl_handler(void *arg_)
bbaaef
     static long long int send_garp_time = LLONG_MAX;
bbaaef
     /* Next multicast query (IGMP) in ms. */
bbaaef
     static long long int send_mcast_query_time = LLONG_MAX;
bbaaef
+    static long long int svc_monitors_next_run_time = LLONG_MAX;
bbaaef
 
bbaaef
     swconn = rconn_create(5, 0, DSCP_DEFAULT, 1 << OFP13_VERSION);
bbaaef
 
bbaaef
@@ -2112,11 +2138,16 @@ pinctrl_handler(void *arg_)
bbaaef
             }
bbaaef
         }
bbaaef
 
bbaaef
+        ovs_mutex_lock(&pinctrl_mutex);
bbaaef
+        svc_monitors_run(swconn, &svc_monitors_next_run_time);
bbaaef
+        ovs_mutex_unlock(&pinctrl_mutex);
bbaaef
+
bbaaef
         rconn_run_wait(swconn);
bbaaef
         rconn_recv_wait(swconn);
bbaaef
         send_garp_wait(send_garp_time);
bbaaef
         ipv6_ra_wait(send_ipv6_ra_time);
bbaaef
         ip_mcast_querier_wait(send_mcast_query_time);
bbaaef
+        svc_monitors_wait(svc_monitors_next_run_time);
bbaaef
 
bbaaef
         new_seq = seq_read(pinctrl_handler_seq);
bbaaef
         seq_wait(pinctrl_handler_seq, new_seq);
bbaaef
@@ -2142,6 +2173,7 @@ pinctrl_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
bbaaef
             struct ovsdb_idl_index *sbrec_ip_multicast_opts,
bbaaef
             const struct sbrec_dns_table *dns_table,
bbaaef
             const struct sbrec_controller_event_table *ce_table,
bbaaef
+            const struct sbrec_service_monitor_table *svc_mon_table,
bbaaef
             const struct ovsrec_bridge *br_int,
bbaaef
             const struct sbrec_chassis *chassis,
bbaaef
             const struct hmap *local_datapaths,
bbaaef
@@ -2178,6 +2210,8 @@ pinctrl_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
bbaaef
     run_buffered_binding(sbrec_port_binding_by_datapath,
bbaaef
                          sbrec_mac_binding_by_lport_ip,
bbaaef
                          local_datapaths);
bbaaef
+    sync_svc_monitors(ovnsb_idl_txn, svc_mon_table, sbrec_port_binding_by_name,
bbaaef
+                      chassis);
bbaaef
     ovs_mutex_unlock(&pinctrl_mutex);
bbaaef
 }
bbaaef
 
bbaaef
@@ -2726,6 +2760,7 @@ pinctrl_destroy(void)
bbaaef
     destroy_put_vport_bindings();
bbaaef
     destroy_dns_cache();
bbaaef
     ip_mcast_snoop_destroy();
bbaaef
+    destroy_svc_monitors();
bbaaef
     seq_destroy(pinctrl_main_seq);
bbaaef
     seq_destroy(pinctrl_handler_seq);
bbaaef
 }
bbaaef
@@ -3158,6 +3193,36 @@ send_garp(struct rconn *swconn, struct garp_data *garp,
bbaaef
     return garp->announce_time;
bbaaef
 }
bbaaef
 
bbaaef
+static void
bbaaef
+pinctrl_compose_ipv4(struct dp_packet *packet, struct eth_addr eth_src,
bbaaef
+                     struct eth_addr eth_dst, ovs_be32 ipv4_src,
bbaaef
+                     ovs_be32 ipv4_dst, uint8_t ip_proto, uint8_t ttl,
bbaaef
+                     uint16_t ip_payload_len)
bbaaef
+{
bbaaef
+    dp_packet_clear(packet);
bbaaef
+    packet->packet_type = htonl(PT_ETH);
bbaaef
+
bbaaef
+    struct eth_header *eh = dp_packet_put_zeros(packet, sizeof *eh);
bbaaef
+    eh->eth_dst = eth_dst;
bbaaef
+    eh->eth_src = eth_src;
bbaaef
+
bbaaef
+    struct ip_header *nh = dp_packet_put_zeros(packet, sizeof *nh);
bbaaef
+
bbaaef
+    eh->eth_type = htons(ETH_TYPE_IP);
bbaaef
+    dp_packet_set_l3(packet, nh);
bbaaef
+    nh->ip_ihl_ver = IP_IHL_VER(5, 4);
bbaaef
+    nh->ip_tot_len = htons(sizeof(struct ip_header) + ip_payload_len);
bbaaef
+    nh->ip_tos = IP_DSCP_CS6;
bbaaef
+    nh->ip_proto = ip_proto;
bbaaef
+    nh->ip_frag_off = htons(IP_DF);
bbaaef
+
bbaaef
+    /* Setting tos and ttl to 0 and 1 respectively. */
bbaaef
+    packet_set_ipv4(packet, ipv4_src, ipv4_dst, 0, ttl);
bbaaef
+
bbaaef
+    nh->ip_csum = 0;
bbaaef
+    nh->ip_csum = csum(nh, sizeof *nh);
bbaaef
+}
bbaaef
+
bbaaef
 /*
bbaaef
  * Multicast snooping configuration.
bbaaef
  */
bbaaef
@@ -3775,32 +3840,11 @@ ip_mcast_querier_send(struct rconn *swconn, struct ip_mcast_snoop *ip_ms,
bbaaef
     struct dp_packet packet;
bbaaef
 
bbaaef
     dp_packet_use_stub(&packet, packet_stub, sizeof packet_stub);
bbaaef
-
bbaaef
-    uint8_t ip_tos = 0;
bbaaef
-    uint8_t igmp_ttl = 1;
bbaaef
-
bbaaef
-    dp_packet_clear(&packet);
bbaaef
-    packet.packet_type = htonl(PT_ETH);
bbaaef
-
bbaaef
-    struct eth_header *eh = dp_packet_put_zeros(&packet, sizeof *eh);
bbaaef
-    eh->eth_dst = ip_ms->cfg.query_eth_dst;
bbaaef
-    eh->eth_src = ip_ms->cfg.query_eth_src;
bbaaef
-
bbaaef
-    struct ip_header *nh = dp_packet_put_zeros(&packet, sizeof *nh);
bbaaef
-
bbaaef
-    eh->eth_type = htons(ETH_TYPE_IP);
bbaaef
-    dp_packet_set_l3(&packet, nh);
bbaaef
-    nh->ip_ihl_ver = IP_IHL_VER(5, 4);
bbaaef
-    nh->ip_tot_len = htons(sizeof(struct ip_header) +
bbaaef
-                            sizeof(struct igmpv3_query_header));
bbaaef
-    nh->ip_tos = IP_DSCP_CS6;
bbaaef
-    nh->ip_proto = IPPROTO_IGMP;
bbaaef
-    nh->ip_frag_off = htons(IP_DF);
bbaaef
-    packet_set_ipv4(&packet, ip_ms->cfg.query_ipv4_src,
bbaaef
-                    ip_ms->cfg.query_ipv4_dst, ip_tos, igmp_ttl);
bbaaef
-
bbaaef
-    nh->ip_csum = 0;
bbaaef
-    nh->ip_csum = csum(nh, sizeof *nh);
bbaaef
+    pinctrl_compose_ipv4(&packet, ip_ms->cfg.query_eth_src,
bbaaef
+                         ip_ms->cfg.query_eth_dst,
bbaaef
+                         ip_ms->cfg.query_ipv4_src,
bbaaef
+                         ip_ms->cfg.query_ipv4_dst,
bbaaef
+                         IPPROTO_IGMP, 1, sizeof(struct igmpv3_query_header));
bbaaef
 
bbaaef
     struct igmpv3_query_header *igh =
bbaaef
         dp_packet_put_zeros(&packet, sizeof *igh);
bbaaef
@@ -4693,3 +4737,682 @@ pinctrl_handle_bind_vport(
bbaaef
 
bbaaef
     notify_pinctrl_main();
bbaaef
 }
bbaaef
+
bbaaef
+enum svc_monitor_state {
bbaaef
+    SVC_MON_S_INIT,
bbaaef
+    SVC_MON_S_WAITING,
bbaaef
+    SVC_MON_S_ONLINE,
bbaaef
+    SVC_MON_S_OFFLINE,
bbaaef
+};
bbaaef
+
bbaaef
+enum svc_monitor_status {
bbaaef
+    SVC_MON_ST_UNKNOWN,
bbaaef
+    SVC_MON_ST_OFFLINE,
bbaaef
+    SVC_MON_ST_ONLINE,
bbaaef
+};
bbaaef
+
bbaaef
+enum svc_monitor_protocol {
bbaaef
+    SVC_MON_PROTO_TCP,
bbaaef
+    SVC_MON_PROTO_UDP,
bbaaef
+};
bbaaef
+
bbaaef
+/* Service monitor health checks. */
bbaaef
+struct svc_monitor {
bbaaef
+    struct hmap_node hmap_node;
bbaaef
+    struct ovs_list list_node;
bbaaef
+
bbaaef
+    /* Should be accessed only with in the main ovn-controller
bbaaef
+     * thread. */
bbaaef
+    const struct sbrec_service_monitor *sb_svc_mon;
bbaaef
+
bbaaef
+    /* key */
bbaaef
+    struct in6_addr ip;
bbaaef
+    uint32_t dp_key;
bbaaef
+    uint32_t port_key;
bbaaef
+    uint32_t proto_port; /* tcp/udp port */
bbaaef
+
bbaaef
+    struct eth_addr ea;
bbaaef
+    long long int timestamp;
bbaaef
+    bool is_ip6;
bbaaef
+
bbaaef
+    long long int wait_time;
bbaaef
+    long long int next_send_time;
bbaaef
+
bbaaef
+    struct smap options;
bbaaef
+    /* The interval, in milli seconds, between service monitor checks. */
bbaaef
+    int interval;
bbaaef
+
bbaaef
+    /* The time, in milli seconds, after which the service monitor check
bbaaef
+     * times out. */
bbaaef
+    int svc_timeout;
bbaaef
+
bbaaef
+    /* The number of successful checks after which the service is
bbaaef
+     * considered online. */
bbaaef
+    int success_count;
bbaaef
+    int n_success;
bbaaef
+
bbaaef
+    /* The number of failure checks after which the service is
bbaaef
+     * considered offline. */
bbaaef
+    int failure_count;
bbaaef
+    int n_failures;
bbaaef
+
bbaaef
+    enum svc_monitor_protocol protocol;
bbaaef
+    enum svc_monitor_state state;
bbaaef
+    enum svc_monitor_status status;
bbaaef
+    struct dp_packet pkt;
bbaaef
+
bbaaef
+    uint32_t seq_no;
bbaaef
+    ovs_be16 tp_src;
bbaaef
+
bbaaef
+    bool delete;
bbaaef
+};
bbaaef
+
bbaaef
+static struct hmap svc_monitors_map;
bbaaef
+static struct ovs_list svc_monitors;
bbaaef
+
bbaaef
+static void
bbaaef
+init_svc_monitors(void)
bbaaef
+{
bbaaef
+    hmap_init(&svc_monitors_map);
bbaaef
+    ovs_list_init(&svc_monitors);
bbaaef
+}
bbaaef
+
bbaaef
+static void
bbaaef
+destroy_svc_monitors(void)
bbaaef
+{
bbaaef
+    struct svc_monitor *svc;
bbaaef
+    HMAP_FOR_EACH_POP (svc, hmap_node, &svc_monitors_map) {
bbaaef
+
bbaaef
+    }
bbaaef
+
bbaaef
+    hmap_destroy(&svc_monitors_map);
bbaaef
+
bbaaef
+    LIST_FOR_EACH_POP (svc, list_node, &svc_monitors) {
bbaaef
+        smap_destroy(&svc->options);
bbaaef
+        free(svc);
bbaaef
+    }
bbaaef
+}
bbaaef
+
bbaaef
+
bbaaef
+static struct svc_monitor *
bbaaef
+pinctrl_find_svc_monitor(uint32_t dp_key, uint32_t port_key,
bbaaef
+                         const struct in6_addr *ip_key, uint32_t port,
bbaaef
+                         enum svc_monitor_protocol protocol,
bbaaef
+                         uint32_t hash)
bbaaef
+{
bbaaef
+    struct svc_monitor *svc;
bbaaef
+    HMAP_FOR_EACH_WITH_HASH (svc, hmap_node, hash, &svc_monitors_map) {
bbaaef
+        if (svc->dp_key == dp_key
bbaaef
+            && svc->port_key == port_key
bbaaef
+            && svc->proto_port == port
bbaaef
+            && IN6_ARE_ADDR_EQUAL(&svc->ip, ip_key)
bbaaef
+            && svc->protocol == protocol) {
bbaaef
+            return svc;
bbaaef
+        }
bbaaef
+    }
bbaaef
+    return NULL;
bbaaef
+}
bbaaef
+
bbaaef
+static void
bbaaef
+sync_svc_monitors(struct ovsdb_idl_txn *ovnsb_idl_txn,
bbaaef
+                  const struct sbrec_service_monitor_table *svc_mon_table,
bbaaef
+                  struct ovsdb_idl_index *sbrec_port_binding_by_name,
bbaaef
+                  const struct sbrec_chassis *our_chassis)
bbaaef
+    OVS_REQUIRES(pinctrl_mutex)
bbaaef
+{
bbaaef
+    bool changed = false;
bbaaef
+    struct svc_monitor *svc_mon;
bbaaef
+
bbaaef
+    LIST_FOR_EACH (svc_mon, list_node, &svc_monitors) {
bbaaef
+        svc_mon->delete = true;
bbaaef
+    }
bbaaef
+
bbaaef
+    const struct sbrec_service_monitor *sb_svc_mon;
bbaaef
+    SBREC_SERVICE_MONITOR_TABLE_FOR_EACH (sb_svc_mon, svc_mon_table) {
bbaaef
+        const struct sbrec_port_binding *pb
bbaaef
+            = lport_lookup_by_name(sbrec_port_binding_by_name,
bbaaef
+                                   sb_svc_mon->logical_port);
bbaaef
+        if (!pb) {
bbaaef
+            continue;
bbaaef
+        }
bbaaef
+
bbaaef
+        if (pb->chassis != our_chassis) {
bbaaef
+            continue;
bbaaef
+        }
bbaaef
+
bbaaef
+        struct in6_addr ip_addr;
bbaaef
+        ovs_be32 ip4;
bbaaef
+        if (ip_parse(sb_svc_mon->ip, &ip4)) {
bbaaef
+            ip_addr = in6_addr_mapped_ipv4(ip4);
bbaaef
+        } else {
bbaaef
+            continue;
bbaaef
+        }
bbaaef
+
bbaaef
+        struct eth_addr ea;
bbaaef
+        bool mac_found = false;
bbaaef
+        for (size_t i = 0; i < pb->n_mac; i++) {
bbaaef
+            struct lport_addresses laddrs;
bbaaef
+            if (!extract_lsp_addresses(pb->mac[i], &laddrs)) {
bbaaef
+                continue;
bbaaef
+            }
bbaaef
+
bbaaef
+            for (size_t j = 0; j < laddrs.n_ipv4_addrs; j++) {
bbaaef
+                if (ip4 == laddrs.ipv4_addrs[j].addr) {
bbaaef
+                    ea = laddrs.ea;
bbaaef
+                    mac_found = true;
bbaaef
+                    break;
bbaaef
+                }
bbaaef
+            }
bbaaef
+
bbaaef
+            if (mac_found) {
bbaaef
+                break;
bbaaef
+            }
bbaaef
+        }
bbaaef
+
bbaaef
+        if (!mac_found) {
bbaaef
+            continue;
bbaaef
+        }
bbaaef
+
bbaaef
+        uint32_t dp_key = pb->datapath->tunnel_key;
bbaaef
+        uint32_t port_key = pb->tunnel_key;
bbaaef
+        uint32_t hash =
bbaaef
+            hash_bytes(&ip_addr, sizeof ip_addr,
bbaaef
+                       hash_3words(dp_key, port_key, sb_svc_mon->port));
bbaaef
+
bbaaef
+        enum svc_monitor_protocol protocol;
bbaaef
+        if (!sb_svc_mon->protocol || strcmp(sb_svc_mon->protocol, "udp")) {
bbaaef
+            protocol = SVC_MON_PROTO_TCP;
bbaaef
+        } else {
bbaaef
+            protocol = SVC_MON_PROTO_UDP;
bbaaef
+        }
bbaaef
+
bbaaef
+        svc_mon = pinctrl_find_svc_monitor(dp_key, port_key, &ip_addr,
bbaaef
+                                           sb_svc_mon->port, protocol, hash);
bbaaef
+
bbaaef
+        if (!svc_mon) {
bbaaef
+            svc_mon = xmalloc(sizeof *svc_mon);
bbaaef
+            svc_mon->dp_key = dp_key;
bbaaef
+            svc_mon->port_key = port_key;
bbaaef
+            svc_mon->proto_port = sb_svc_mon->port;
bbaaef
+            svc_mon->ip = ip_addr;
bbaaef
+            svc_mon->is_ip6 = false;
bbaaef
+            svc_mon->state = SVC_MON_S_INIT;
bbaaef
+            svc_mon->status = SVC_MON_ST_UNKNOWN;
bbaaef
+            svc_mon->protocol = protocol;
bbaaef
+
bbaaef
+            smap_init(&svc_mon->options);
bbaaef
+            svc_mon->interval =
bbaaef
+                smap_get_int(&svc_mon->options, "interval", 5) * 1000;
bbaaef
+            svc_mon->svc_timeout =
bbaaef
+                smap_get_int(&svc_mon->options, "timeout", 3) * 1000;
bbaaef
+            svc_mon->success_count =
bbaaef
+                smap_get_int(&svc_mon->options, "success_count", 1);
bbaaef
+            svc_mon->failure_count =
bbaaef
+                smap_get_int(&svc_mon->options, "failure_count", 1);
bbaaef
+            svc_mon->n_success = 0;
bbaaef
+            svc_mon->n_failures = 0;
bbaaef
+
bbaaef
+            hmap_insert(&svc_monitors_map, &svc_mon->hmap_node, hash);
bbaaef
+            ovs_list_push_back(&svc_monitors, &svc_mon->list_node);
bbaaef
+            changed = true;
bbaaef
+        }
bbaaef
+
bbaaef
+        svc_mon->sb_svc_mon = sb_svc_mon;
bbaaef
+        svc_mon->ea = ea;
bbaaef
+        if (!smap_equal(&svc_mon->options, &sb_svc_mon->options)) {
bbaaef
+            smap_destroy(&svc_mon->options);
bbaaef
+            smap_clone(&svc_mon->options, &sb_svc_mon->options);
bbaaef
+            svc_mon->interval =
bbaaef
+                smap_get_int(&svc_mon->options, "interval", 5) * 1000;
bbaaef
+            svc_mon->svc_timeout =
bbaaef
+                smap_get_int(&svc_mon->options, "timeout", 3) * 1000;
bbaaef
+            svc_mon->success_count =
bbaaef
+                smap_get_int(&svc_mon->options, "success_count", 1);
bbaaef
+            svc_mon->failure_count =
bbaaef
+                smap_get_int(&svc_mon->options, "failure_count", 1);
bbaaef
+            changed = true;
bbaaef
+        }
bbaaef
+
bbaaef
+        svc_mon->delete = false;
bbaaef
+    }
bbaaef
+
bbaaef
+    struct svc_monitor *next;
bbaaef
+    LIST_FOR_EACH_SAFE (svc_mon, next, list_node, &svc_monitors) {
bbaaef
+        if (svc_mon->delete) {
bbaaef
+            hmap_remove(&svc_monitors_map, &svc_mon->hmap_node);
bbaaef
+            ovs_list_remove(&svc_mon->list_node);
bbaaef
+            smap_destroy(&svc_mon->options);
bbaaef
+            free(svc_mon);
bbaaef
+            changed = true;
bbaaef
+        } else if (ovnsb_idl_txn) {
bbaaef
+            /* Update the status of the service monitor. */
bbaaef
+            if (svc_mon->status != SVC_MON_ST_UNKNOWN) {
bbaaef
+                if (svc_mon->status == SVC_MON_ST_ONLINE) {
bbaaef
+                    sbrec_service_monitor_set_status(svc_mon->sb_svc_mon,
bbaaef
+                                                     "online");
bbaaef
+                } else {
bbaaef
+                    sbrec_service_monitor_set_status(svc_mon->sb_svc_mon,
bbaaef
+                                                     "offline");
bbaaef
+                }
bbaaef
+            }
bbaaef
+        }
bbaaef
+    }
bbaaef
+
bbaaef
+    if (changed) {
bbaaef
+        notify_pinctrl_handler();
bbaaef
+    }
bbaaef
+
bbaaef
+}
bbaaef
+
bbaaef
+static uint16_t
bbaaef
+get_random_src_port(void)
bbaaef
+{
bbaaef
+    uint16_t random_src_port = random_uint16();
bbaaef
+    while (random_src_port < 1024) {
bbaaef
+        random_src_port = random_uint16();
bbaaef
+    }
bbaaef
+
bbaaef
+    return random_src_port;
bbaaef
+}
bbaaef
+
bbaaef
+static void
bbaaef
+svc_monitor_send_tcp_health_check__(struct rconn *swconn,
bbaaef
+                                    struct svc_monitor *svc_mon,
bbaaef
+                                    uint16_t ctl_flags,
bbaaef
+                                    ovs_be32 tcp_seq,
bbaaef
+                                    ovs_be32 tcp_ack,
bbaaef
+                                    ovs_be16 tcp_src)
bbaaef
+{
bbaaef
+    if (svc_mon->is_ip6) {
bbaaef
+        return;
bbaaef
+    }
bbaaef
+
bbaaef
+    /* Compose a TCP-SYN packet. */
bbaaef
+    uint64_t packet_stub[128 / 8];
bbaaef
+    struct dp_packet packet;
bbaaef
+
bbaaef
+    struct eth_addr eth_src;
bbaaef
+    eth_addr_from_string(svc_mon->sb_svc_mon->src_mac, &eth_src);
bbaaef
+    ovs_be32 ip4_src;
bbaaef
+    ip_parse(svc_mon->sb_svc_mon->src_ip, &ip4_src);
bbaaef
+
bbaaef
+    dp_packet_use_stub(&packet, packet_stub, sizeof packet_stub);
bbaaef
+    pinctrl_compose_ipv4(&packet, eth_src, svc_mon->ea,
bbaaef
+                         ip4_src, in6_addr_get_mapped_ipv4(&svc_mon->ip),
bbaaef
+                         IPPROTO_TCP, 63, TCP_HEADER_LEN);
bbaaef
+
bbaaef
+    struct tcp_header *th = dp_packet_put_zeros(&packet, sizeof *th);
bbaaef
+    dp_packet_set_l4(&packet, th);
bbaaef
+    th->tcp_dst = htons(svc_mon->proto_port);
bbaaef
+    th->tcp_src = tcp_src;
bbaaef
+
bbaaef
+    th->tcp_ctl = htons((5 << 12) | ctl_flags);
bbaaef
+    put_16aligned_be32(&th->tcp_seq, tcp_seq);
bbaaef
+    put_16aligned_be32(&th->tcp_ack, tcp_ack);
bbaaef
+
bbaaef
+    th->tcp_winsz = htons(65160);
bbaaef
+
bbaaef
+    uint32_t csum;
bbaaef
+    csum = packet_csum_pseudoheader(dp_packet_l3(&packet));
bbaaef
+    csum = csum_continue(csum, th, dp_packet_size(&packet) -
bbaaef
+                         ((const unsigned char *)th -
bbaaef
+                         (const unsigned char *)dp_packet_eth(&packet)));
bbaaef
+    th->tcp_csum = csum_finish(csum);
bbaaef
+
bbaaef
+    uint64_t ofpacts_stub[4096 / 8];
bbaaef
+    struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(ofpacts_stub);
bbaaef
+    enum ofp_version version = rconn_get_version(swconn);
bbaaef
+    put_load(svc_mon->dp_key, MFF_LOG_DATAPATH, 0, 64, &ofpacts);
bbaaef
+    put_load(svc_mon->port_key, MFF_LOG_OUTPORT, 0, 32, &ofpacts);
bbaaef
+    put_load(1, MFF_LOG_FLAGS, MLF_LOCAL_ONLY, 1, &ofpacts);
bbaaef
+    struct ofpact_resubmit *resubmit = ofpact_put_RESUBMIT(&ofpacts);
bbaaef
+    resubmit->in_port = OFPP_CONTROLLER;
bbaaef
+    resubmit->table_id = OFTABLE_LOCAL_OUTPUT;
bbaaef
+
bbaaef
+    struct ofputil_packet_out po = {
bbaaef
+        .packet = dp_packet_data(&packet),
bbaaef
+        .packet_len = dp_packet_size(&packet),
bbaaef
+        .buffer_id = UINT32_MAX,
bbaaef
+        .ofpacts = ofpacts.data,
bbaaef
+        .ofpacts_len = ofpacts.size,
bbaaef
+    };
bbaaef
+    match_set_in_port(&po.flow_metadata, OFPP_CONTROLLER);
bbaaef
+    enum ofputil_protocol proto = ofputil_protocol_from_ofp_version(version);
bbaaef
+    queue_msg(swconn, ofputil_encode_packet_out(&po, proto));
bbaaef
+    dp_packet_uninit(&packet);
bbaaef
+    ofpbuf_uninit(&ofpacts);
bbaaef
+}
bbaaef
+
bbaaef
+static void
bbaaef
+svc_monitor_send_udp_health_check(struct rconn *swconn,
bbaaef
+                                  struct svc_monitor *svc_mon,
bbaaef
+                                  ovs_be16 udp_src)
bbaaef
+{
bbaaef
+    if (svc_mon->is_ip6) {
bbaaef
+        return;
bbaaef
+    }
bbaaef
+
bbaaef
+    struct eth_addr eth_src;
bbaaef
+    eth_addr_from_string(svc_mon->sb_svc_mon->src_mac, &eth_src);
bbaaef
+    ovs_be32 ip4_src;
bbaaef
+    ip_parse(svc_mon->sb_svc_mon->src_ip, &ip4_src);
bbaaef
+
bbaaef
+    uint64_t packet_stub[128 / 8];
bbaaef
+    struct dp_packet packet;
bbaaef
+    dp_packet_use_stub(&packet, packet_stub, sizeof packet_stub);
bbaaef
+    pinctrl_compose_ipv4(&packet, eth_src, svc_mon->ea,
bbaaef
+                         ip4_src, in6_addr_get_mapped_ipv4(&svc_mon->ip),
bbaaef
+                         IPPROTO_UDP, 63, UDP_HEADER_LEN + 8);
bbaaef
+
bbaaef
+    struct udp_header *uh = dp_packet_put_zeros(&packet, sizeof *uh);
bbaaef
+    dp_packet_set_l4(&packet, uh);
bbaaef
+    uh->udp_dst = htons(svc_mon->proto_port);
bbaaef
+    uh->udp_src = udp_src;
bbaaef
+    uh->udp_len = htons(UDP_HEADER_LEN + 8);
bbaaef
+    uh->udp_csum = 0;
bbaaef
+    dp_packet_put_zeros(&packet, 8);
bbaaef
+
bbaaef
+    uint64_t ofpacts_stub[4096 / 8];
bbaaef
+    struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(ofpacts_stub);
bbaaef
+    enum ofp_version version = rconn_get_version(swconn);
bbaaef
+    put_load(svc_mon->dp_key, MFF_LOG_DATAPATH, 0, 64, &ofpacts);
bbaaef
+    put_load(svc_mon->port_key, MFF_LOG_OUTPORT, 0, 32, &ofpacts);
bbaaef
+    put_load(1, MFF_LOG_FLAGS, MLF_LOCAL_ONLY, 1, &ofpacts);
bbaaef
+    struct ofpact_resubmit *resubmit = ofpact_put_RESUBMIT(&ofpacts);
bbaaef
+    resubmit->in_port = OFPP_CONTROLLER;
bbaaef
+    resubmit->table_id = OFTABLE_LOCAL_OUTPUT;
bbaaef
+
bbaaef
+    struct ofputil_packet_out po = {
bbaaef
+        .packet = dp_packet_data(&packet),
bbaaef
+        .packet_len = dp_packet_size(&packet),
bbaaef
+        .buffer_id = UINT32_MAX,
bbaaef
+        .ofpacts = ofpacts.data,
bbaaef
+        .ofpacts_len = ofpacts.size,
bbaaef
+    };
bbaaef
+    match_set_in_port(&po.flow_metadata, OFPP_CONTROLLER);
bbaaef
+    enum ofputil_protocol proto = ofputil_protocol_from_ofp_version(version);
bbaaef
+    queue_msg(swconn, ofputil_encode_packet_out(&po, proto));
bbaaef
+    dp_packet_uninit(&packet);
bbaaef
+    ofpbuf_uninit(&ofpacts);
bbaaef
+}
bbaaef
+
bbaaef
+static void
bbaaef
+svc_monitor_send_health_check(struct rconn *swconn,
bbaaef
+                              struct svc_monitor *svc_mon)
bbaaef
+{
bbaaef
+    if (svc_mon->protocol == SVC_MON_PROTO_TCP) {
bbaaef
+        svc_mon->seq_no = random_uint32();
bbaaef
+        svc_mon->tp_src = htons(get_random_src_port());
bbaaef
+        svc_monitor_send_tcp_health_check__(swconn, svc_mon,
bbaaef
+                                            TCP_SYN,
bbaaef
+                                            htonl(svc_mon->seq_no), htonl(0),
bbaaef
+                                            svc_mon->tp_src);
bbaaef
+    } else {
bbaaef
+        if (!svc_mon->tp_src) {
bbaaef
+            svc_mon->tp_src = htons(get_random_src_port());
bbaaef
+        }
bbaaef
+        svc_monitor_send_udp_health_check(swconn, svc_mon, svc_mon->tp_src);
bbaaef
+    }
bbaaef
+
bbaaef
+    svc_mon->wait_time = time_msec() + svc_mon->svc_timeout;
bbaaef
+    svc_mon->state = SVC_MON_S_WAITING;
bbaaef
+}
bbaaef
+
bbaaef
+static void
bbaaef
+svc_monitors_run(struct rconn *swconn,
bbaaef
+                 long long int *svc_monitors_next_run_time)
bbaaef
+    OVS_REQUIRES(pinctrl_mutex)
bbaaef
+{
bbaaef
+    *svc_monitors_next_run_time = LLONG_MAX;
bbaaef
+    struct svc_monitor *svc_mon;
bbaaef
+    LIST_FOR_EACH (svc_mon, list_node, &svc_monitors) {
bbaaef
+        char ip_[INET6_ADDRSTRLEN + 1];
bbaaef
+        memset(ip_, 0, INET6_ADDRSTRLEN + 1);
bbaaef
+        ipv6_string_mapped(ip_, &svc_mon->ip);
bbaaef
+
bbaaef
+        long long int current_time = time_msec();
bbaaef
+        long long int next_run_time = LLONG_MAX;
bbaaef
+        enum svc_monitor_status old_status = svc_mon->status;
bbaaef
+        switch (svc_mon->state) {
bbaaef
+        case SVC_MON_S_INIT:
bbaaef
+            svc_monitor_send_health_check(swconn, svc_mon);
bbaaef
+            next_run_time = svc_mon->wait_time;
bbaaef
+            break;
bbaaef
+
bbaaef
+        case SVC_MON_S_WAITING:
bbaaef
+            if (current_time > svc_mon->wait_time) {
bbaaef
+                if (svc_mon->protocol ==  SVC_MON_PROTO_TCP) {
bbaaef
+                    svc_mon->n_failures++;
bbaaef
+                    svc_mon->state = SVC_MON_S_OFFLINE;
bbaaef
+                } else {
bbaaef
+                    svc_mon->n_success++;
bbaaef
+                    svc_mon->state = SVC_MON_S_ONLINE;
bbaaef
+                }
bbaaef
+                svc_mon->next_send_time = current_time + svc_mon->interval;
bbaaef
+                next_run_time = svc_mon->next_send_time;
bbaaef
+            } else {
bbaaef
+                next_run_time = svc_mon->wait_time - current_time;
bbaaef
+                next_run_time = svc_mon->wait_time;
bbaaef
+            }
bbaaef
+            break;
bbaaef
+
bbaaef
+        case SVC_MON_S_ONLINE:
bbaaef
+            if (svc_mon->n_success >= svc_mon->success_count) {
bbaaef
+                svc_mon->status = SVC_MON_ST_ONLINE;
bbaaef
+                svc_mon->n_success = 0;
bbaaef
+            }
bbaaef
+            if (current_time >= svc_mon->next_send_time) {
bbaaef
+                svc_monitor_send_health_check(swconn, svc_mon);
bbaaef
+                next_run_time = svc_mon->wait_time;
bbaaef
+            } else {
bbaaef
+                next_run_time = svc_mon->next_send_time;
bbaaef
+            }
bbaaef
+            break;
bbaaef
+
bbaaef
+        case SVC_MON_S_OFFLINE:
bbaaef
+            if (svc_mon->n_failures >= svc_mon->failure_count) {
bbaaef
+                svc_mon->status = SVC_MON_ST_OFFLINE;
bbaaef
+                svc_mon->n_failures = 0;
bbaaef
+            }
bbaaef
+
bbaaef
+            if (current_time >= svc_mon->next_send_time) {
bbaaef
+                svc_monitor_send_health_check(swconn, svc_mon);
bbaaef
+                next_run_time = svc_mon->wait_time;
bbaaef
+            } else {
bbaaef
+                next_run_time = svc_mon->next_send_time;
bbaaef
+            }
bbaaef
+            break;
bbaaef
+
bbaaef
+        default:
bbaaef
+            OVS_NOT_REACHED();
bbaaef
+        }
bbaaef
+
bbaaef
+        if (*svc_monitors_next_run_time > next_run_time) {
bbaaef
+            *svc_monitors_next_run_time = next_run_time;
bbaaef
+        }
bbaaef
+
bbaaef
+        if (old_status != svc_mon->status) {
bbaaef
+            /* Notify the main thread to update the status in the SB DB. */
bbaaef
+            notify_pinctrl_main();
bbaaef
+        }
bbaaef
+    }
bbaaef
+}
bbaaef
+
bbaaef
+static void
bbaaef
+svc_monitors_wait(long long int svc_monitors_next_run_time)
bbaaef
+{
bbaaef
+    if (!ovs_list_is_empty(&svc_monitors)) {
bbaaef
+        poll_timer_wait_until(svc_monitors_next_run_time);
bbaaef
+    }
bbaaef
+}
bbaaef
+
bbaaef
+static bool
bbaaef
+pinctrl_handle_tcp_svc_check(struct rconn *swconn,
bbaaef
+                             struct dp_packet *pkt_in,
bbaaef
+                             struct svc_monitor *svc_mon)
bbaaef
+{
bbaaef
+    struct tcp_header *th = dp_packet_l4(pkt_in);
bbaaef
+
bbaaef
+    if (!th) {
bbaaef
+        return false;
bbaaef
+    }
bbaaef
+
bbaaef
+    uint32_t tcp_seq = ntohl(get_16aligned_be32(&th->tcp_seq));
bbaaef
+    uint32_t tcp_ack = ntohl(get_16aligned_be32(&th->tcp_ack));
bbaaef
+
bbaaef
+    if (th->tcp_dst != svc_mon->tp_src) {
bbaaef
+       return false;
bbaaef
+    }
bbaaef
+
bbaaef
+    if (tcp_ack != (svc_mon->seq_no + 1)) {
bbaaef
+        return false;
bbaaef
+    }
bbaaef
+
bbaaef
+    /* Check for SYN flag and Ack flag. */
bbaaef
+    if ((TCP_FLAGS(th->tcp_ctl) & (TCP_SYN | TCP_ACK))
bbaaef
+         == (TCP_SYN | TCP_ACK)) {
bbaaef
+        svc_mon->n_success++;
bbaaef
+        svc_mon->state = SVC_MON_S_ONLINE;
bbaaef
+
bbaaef
+        /* Send RST-ACK packet. */
bbaaef
+        svc_monitor_send_tcp_health_check__(swconn, svc_mon, TCP_RST | TCP_ACK,
bbaaef
+                                            htonl(tcp_ack + 1),
bbaaef
+                                            htonl(tcp_seq + 1), th->tcp_dst);
bbaaef
+        /* Calculate next_send_time. */
bbaaef
+        svc_mon->next_send_time = time_msec() + svc_mon->interval;
bbaaef
+        return true;
bbaaef
+    }
bbaaef
+
bbaaef
+    /* Check if RST flag is set. */
bbaaef
+    if (TCP_FLAGS(th->tcp_ctl) & TCP_RST) {
bbaaef
+        svc_mon->n_failures++;
bbaaef
+        svc_mon->state = SVC_MON_S_OFFLINE;
bbaaef
+
bbaaef
+        /* Calculate next_send_time. */
bbaaef
+        svc_mon->next_send_time = time_msec() + svc_mon->interval;
bbaaef
+        return false;
bbaaef
+    }
bbaaef
+
bbaaef
+    return false;
bbaaef
+}
bbaaef
+
bbaaef
+static void
bbaaef
+pinctrl_handle_svc_check(struct rconn *swconn, const struct flow *ip_flow,
bbaaef
+                         struct dp_packet *pkt_in, const struct match *md)
bbaaef
+{
bbaaef
+    uint32_t dp_key = ntohll(md->flow.metadata);
bbaaef
+    uint32_t port_key = md->flow.regs[MFF_LOG_INPORT - MFF_REG0];
bbaaef
+    struct in6_addr ip_addr;
bbaaef
+    struct eth_header *in_eth = dp_packet_data(pkt_in);
bbaaef
+    struct ip_header *in_ip = dp_packet_l3(pkt_in);
bbaaef
+
bbaaef
+    if (in_ip->ip_proto != IPPROTO_TCP && in_ip->ip_proto != IPPROTO_ICMP) {
bbaaef
+        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
bbaaef
+        VLOG_WARN_RL(&rl,
bbaaef
+                     "handle service check: Unsupported protocol - [%x]",
bbaaef
+                     in_ip->ip_proto);
bbaaef
+        return;
bbaaef
+    }
bbaaef
+
bbaaef
+    uint16_t in_ip_len = ntohs(in_ip->ip_tot_len);
bbaaef
+    if (in_ip_len < IP_HEADER_LEN) {
bbaaef
+        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
bbaaef
+        VLOG_WARN_RL(&rl,
bbaaef
+                     "IP packet with invalid length (%u)",
bbaaef
+                     in_ip_len);
bbaaef
+        return;
bbaaef
+    }
bbaaef
+
bbaaef
+    if (in_eth->eth_type == htons(ETH_TYPE_IP)) {
bbaaef
+        ip_addr = in6_addr_mapped_ipv4(ip_flow->nw_src);
bbaaef
+    } else {
bbaaef
+        ip_addr = ip_flow->ipv6_dst;
bbaaef
+    }
bbaaef
+
bbaaef
+    if (in_ip->ip_proto == IPPROTO_TCP) {
bbaaef
+        uint32_t hash =
bbaaef
+            hash_bytes(&ip_addr, sizeof ip_addr,
bbaaef
+                       hash_3words(dp_key, port_key, ntohs(ip_flow->tp_src)));
bbaaef
+
bbaaef
+        struct svc_monitor *svc_mon =
bbaaef
+            pinctrl_find_svc_monitor(dp_key, port_key, &ip_addr,
bbaaef
+                                     ntohs(ip_flow->tp_src),
bbaaef
+                                     SVC_MON_PROTO_TCP, hash);
bbaaef
+        if (!svc_mon) {
bbaaef
+            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
bbaaef
+            VLOG_WARN_RL(&rl, "handle service check: Service monitor "
bbaaef
+                         "not found");
bbaaef
+            return;
bbaaef
+        }
bbaaef
+        pinctrl_handle_tcp_svc_check(swconn, pkt_in, svc_mon);
bbaaef
+    } else {
bbaaef
+        /* It's ICMP packet. */
bbaaef
+        struct icmp_header *ih = dp_packet_l4(pkt_in);
bbaaef
+        if (!ih) {
bbaaef
+            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
bbaaef
+            VLOG_WARN_RL(&rl, "ICMPv4 packet with invalid header");
bbaaef
+            return;
bbaaef
+        }
bbaaef
+
bbaaef
+        if (ih->icmp_type != ICMP4_DST_UNREACH || ih->icmp_code != 3) {
bbaaef
+            return;
bbaaef
+        }
bbaaef
+
bbaaef
+        const char *end =
bbaaef
+            (char *)dp_packet_l4(pkt_in) + dp_packet_l4_size(pkt_in);
bbaaef
+
bbaaef
+        const struct ip_header *orig_ip_hr =
bbaaef
+            dp_packet_get_icmp_payload(pkt_in);
bbaaef
+        if (!orig_ip_hr) {
bbaaef
+            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
bbaaef
+            VLOG_WARN_RL(&rl, "Original IP datagram not present in "
bbaaef
+                         "ICMP packet");
bbaaef
+            return;
bbaaef
+        }
bbaaef
+
bbaaef
+        if (ntohs(orig_ip_hr->ip_tot_len) !=
bbaaef
+            (IP_HEADER_LEN + UDP_HEADER_LEN + 8)) {
bbaaef
+            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
bbaaef
+            VLOG_WARN_RL(&rl, "Invalid original IP datagram length present "
bbaaef
+                         "in ICMP packet");
bbaaef
+            return;
bbaaef
+        }
bbaaef
+
bbaaef
+        struct udp_header *orig_uh = (struct udp_header *) (orig_ip_hr + 1);
bbaaef
+        if ((char *)orig_uh >= end) {
bbaaef
+            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
bbaaef
+            VLOG_WARN_RL(&rl, "Invalid UDP header in the original "
bbaaef
+                         "IP datagram");
bbaaef
+            return;
bbaaef
+        }
bbaaef
+
bbaaef
+        uint32_t hash =
bbaaef
+            hash_bytes(&ip_addr, sizeof ip_addr,
bbaaef
+                       hash_3words(dp_key, port_key, ntohs(orig_uh->udp_dst)));
bbaaef
+
bbaaef
+        struct svc_monitor *svc_mon =
bbaaef
+            pinctrl_find_svc_monitor(dp_key, port_key, &ip_addr,
bbaaef
+                                     ntohs(orig_uh->udp_dst),
bbaaef
+                                     SVC_MON_PROTO_UDP, hash);
bbaaef
+        if (!svc_mon) {
bbaaef
+            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
bbaaef
+            VLOG_WARN_RL(&rl, "handle service check: Service monitor not "
bbaaef
+                         "found for ICMP packet");
bbaaef
+            return;
bbaaef
+        }
bbaaef
+
bbaaef
+        if (orig_uh->udp_src != svc_mon->tp_src) {
bbaaef
+            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
bbaaef
+            VLOG_WARN_RL(&rl, "handle service check: UDP src port doesn't "
bbaaef
+                         "match in the Original IP datagram of ICMP packet");
bbaaef
+            return;
bbaaef
+        }
bbaaef
+
bbaaef
+        /* The UDP service monitor is down. */
bbaaef
+        svc_mon->n_failures++;
bbaaef
+        svc_mon->state = SVC_MON_S_OFFLINE;
bbaaef
+
bbaaef
+        /* Calculate next_send_time. */
bbaaef
+        svc_mon->next_send_time = time_msec() + svc_mon->interval;
bbaaef
+    }
bbaaef
+}
bbaaef
diff --git a/ovn/controller/pinctrl.h b/ovn/controller/pinctrl.h
bbaaef
index fcfce6bcf..208272442 100644
bbaaef
--- a/ovn/controller/pinctrl.h
bbaaef
+++ b/ovn/controller/pinctrl.h
bbaaef
@@ -30,6 +30,7 @@ struct ovsrec_bridge;
bbaaef
 struct sbrec_chassis;
bbaaef
 struct sbrec_dns_table;
bbaaef
 struct sbrec_controller_event_table;
bbaaef
+struct sbrec_service_monitor_table;
bbaaef
 
bbaaef
 void pinctrl_init(void);
bbaaef
 void pinctrl_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
bbaaef
@@ -42,6 +43,7 @@ void pinctrl_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
bbaaef
                  struct ovsdb_idl_index *sbrec_ip_multicast_opts,
bbaaef
                  const struct sbrec_dns_table *,
bbaaef
                  const struct sbrec_controller_event_table *,
bbaaef
+                 const struct sbrec_service_monitor_table *,
bbaaef
                  const struct ovsrec_bridge *, const struct sbrec_chassis *,
bbaaef
                  const struct hmap *local_datapaths,
bbaaef
                  const struct sset *active_tunnels);
bbaaef
diff --git a/ovn/northd/ovn-northd.8.xml b/ovn/northd/ovn-northd.8.xml
bbaaef
index 9515c7790..956a10362 100644
bbaaef
--- a/ovn/northd/ovn-northd.8.xml
bbaaef
+++ b/ovn/northd/ovn-northd.8.xml
bbaaef
@@ -1009,6 +1009,16 @@ output;
bbaaef
     

bbaaef
 
bbaaef
     
    bbaaef
    +      
  • bbaaef
    +        A priorirty-110 flow with the match
    bbaaef
    +        eth.src == E for all logical switch
    bbaaef
    +        datapaths and applies the action handle_svc_check(inport).
    bbaaef
    +        Where E is the service monitor mac defined in the
    bbaaef
    +        
    bbaaef
    +        db="OVN_Northbound"/> colum of 
    bbaaef
    +        db="OVN_Northbound"/> table.
    bbaaef
    +      
    bbaaef
    +
    bbaaef
           
  • bbaaef
             A priority-100 flow that punts all IGMP packets to
    bbaaef
             ovn-controller if IGMP snooping is enabled on the
    bbaaef
    diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
    bbaaef
    index 1ded5dd9b..ec8f9a70f 100644
    bbaaef
    --- a/ovn/northd/ovn-northd.c
    bbaaef
    +++ b/ovn/northd/ovn-northd.c
    bbaaef
    @@ -6182,6 +6182,7 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
    bbaaef
             }
    bbaaef
         }
    bbaaef
     
    bbaaef
    +    char *svc_check_match = xasprintf("eth.dst == %s", svc_monitor_mac);
    bbaaef
         /* Ingress table 17: Destination lookup, broadcast and multicast handling
    bbaaef
          * (priority 70 - 100). */
    bbaaef
         HMAP_FOR_EACH (od, key_node, datapaths) {
    bbaaef
    @@ -6189,6 +6190,9 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
    bbaaef
                 continue;
    bbaaef
             }
    bbaaef
     
    bbaaef
    +        ovn_lflow_add(lflows, od, S_SWITCH_IN_L2_LKUP, 110, svc_check_match,
    bbaaef
    +                      "handle_svc_check(inport);");
    bbaaef
    +
    bbaaef
             struct mcast_switch_info *mcast_sw_info = &od->mcast_info.sw;
    bbaaef
     
    bbaaef
             if (mcast_sw_info->enabled) {
    bbaaef
    @@ -6248,6 +6252,7 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
    bbaaef
             ovn_lflow_add(lflows, od, S_SWITCH_IN_L2_LKUP, 70, "eth.mcast",
    bbaaef
                           "outport = \""MC_FLOOD"\"; output;");
    bbaaef
         }
    bbaaef
    +    free(svc_check_match);
    bbaaef
     
    bbaaef
         /* Ingress table 17: Add IP multicast flows learnt from IGMP
    bbaaef
          * (priority 90). */
    bbaaef
    @@ -10521,6 +10526,11 @@ static const char *rbac_mac_binding_auth[] =
    bbaaef
     static const char *rbac_mac_binding_update[] =
    bbaaef
         {"logical_port", "ip", "mac", "datapath"};
    bbaaef
     
    bbaaef
    +static const char *rbac_svc_monitor_auth[] =
    bbaaef
    +    {""};
    bbaaef
    +static const char *rbac_svc_monitor_auth_update[] =
    bbaaef
    +    {"status"};
    bbaaef
    +
    bbaaef
     static struct rbac_perm_cfg {
    bbaaef
         const char *table;
    bbaaef
         const char **auth;
    bbaaef
    @@ -10562,6 +10572,14 @@ static struct rbac_perm_cfg {
    bbaaef
             .update = rbac_mac_binding_update,
    bbaaef
             .n_update = ARRAY_SIZE(rbac_mac_binding_update),
    bbaaef
             .row = NULL
    bbaaef
    +    },{
    bbaaef
    +        .table = "Service_Monitor",
    bbaaef
    +        .auth = rbac_svc_monitor_auth,
    bbaaef
    +        .n_auth = ARRAY_SIZE(rbac_svc_monitor_auth),
    bbaaef
    +        .insdel = false,
    bbaaef
    +        .update = rbac_svc_monitor_auth_update,
    bbaaef
    +        .n_update = ARRAY_SIZE(rbac_svc_monitor_auth_update),
    bbaaef
    +        .row = NULL
    bbaaef
         },{
    bbaaef
             .table = NULL,
    bbaaef
             .auth = NULL,
    bbaaef
    diff --git a/tests/ovn.at b/tests/ovn.at
    bbaaef
    index f68ddcd33..e2565f274 100644
    bbaaef
    --- a/tests/ovn.at
    bbaaef
    +++ b/tests/ovn.at
    bbaaef
    @@ -16932,5 +16932,124 @@ as hv4 ovs-ofctl show br-phys
    bbaaef
     as hv4 ovs-appctl fdb/show br-phys
    bbaaef
     
    bbaaef
     OVN_CLEANUP([hv1],[hv2],[hv3],[hv4])
    bbaaef
    +AT_CLEANUP
    bbaaef
    +
    bbaaef
    +AT_SETUP([ovn -- Load balancer health checks])
    bbaaef
    +AT_KEYWORDS([lb])
    bbaaef
    +ovn_start
    bbaaef
    +
    bbaaef
    +net_add n1
    bbaaef
    +
    bbaaef
    +sim_add hv1
    bbaaef
    +as hv1
    bbaaef
    +ovs-vsctl add-br br-phys
    bbaaef
    +ovn_attach n1 br-phys 192.168.0.1
    bbaaef
    +ovs-vsctl -- add-port br-int hv1-vif1 -- \
    bbaaef
    +    set interface hv1-vif1 external-ids:iface-id=sw0-p1 \
    bbaaef
    +    options:tx_pcap=hv1/vif1-tx.pcap \
    bbaaef
    +    options:rxq_pcap=hv1/vif1-rx.pcap \
    bbaaef
    +    ofport-request=1
    bbaaef
    +ovs-vsctl -- add-port br-int hv1-vif2 -- \
    bbaaef
    +    set interface hv1-vif2 external-ids:iface-id=sw0-p2 \
    bbaaef
    +    options:tx_pcap=hv1/vif2-tx.pcap \
    bbaaef
    +    options:rxq_pcap=hv1/vif2-rx.pcap \
    bbaaef
    +    ofport-request=2
    bbaaef
    +
    bbaaef
    +sim_add hv2
    bbaaef
    +as hv2
    bbaaef
    +ovs-vsctl add-br br-phys
    bbaaef
    +ovn_attach n1 br-phys 192.168.0.2
    bbaaef
    +ovs-vsctl -- add-port br-int hv2-vif1 -- \
    bbaaef
    +    set interface hv2-vif1 external-ids:iface-id=sw1-p1 \
    bbaaef
    +    options:tx_pcap=hv2/vif1-tx.pcap \
    bbaaef
    +    options:rxq_pcap=hv2/vif1-rx.pcap \
    bbaaef
    +    ofport-request=1
    bbaaef
    +
    bbaaef
    +ovn-nbctl ls-add sw0
    bbaaef
    +
    bbaaef
    +ovn-nbctl lsp-add sw0 sw0-p1
    bbaaef
    +ovn-nbctl lsp-set-addresses sw0-p1 "50:54:00:00:00:03 10.0.0.3"
    bbaaef
    +ovn-nbctl lsp-set-port-security sw0-p1 "50:54:00:00:00:03 10.0.0.3"
    bbaaef
    +
    bbaaef
    +ovn-nbctl lsp-set-addresses sw0-p2 "50:54:00:00:00:04 10.0.0.4"
    bbaaef
    +ovn-nbctl lsp-set-port-security sw0-p2 "50:54:00:00:00:04 10.0.0.4"
    bbaaef
    +
    bbaaef
    +# Create the second logical switch with one port
    bbaaef
    +ovn-nbctl ls-add sw1
    bbaaef
    +ovn-nbctl lsp-add sw1 sw1-p1
    bbaaef
    +ovn-nbctl lsp-set-addresses sw1-p1 "40:54:00:00:00:03 20.0.0.3"
    bbaaef
    +ovn-nbctl lsp-set-port-security sw1-p1 "40:54:00:00:00:03 20.0.0.3"
    bbaaef
     
    bbaaef
    +# Create a logical router and attach both logical switches
    bbaaef
    +ovn-nbctl lr-add lr0
    bbaaef
    +ovn-nbctl lrp-add lr0 lr0-sw0 00:00:00:00:ff:01 10.0.0.1/24
    bbaaef
    +ovn-nbctl lsp-add sw0 sw0-lr0
    bbaaef
    +ovn-nbctl lsp-set-type sw0-lr0 router
    bbaaef
    +ovn-nbctl lsp-set-addresses sw0-lr0 router
    bbaaef
    +ovn-nbctl lsp-set-options sw0-lr0 router-port=lr0-sw0
    bbaaef
    +
    bbaaef
    +ovn-nbctl lrp-add lr0 lr0-sw1 00:00:00:00:ff:02 20.0.0.1/24
    bbaaef
    +ovn-nbctl lsp-add sw1 sw1-lr0
    bbaaef
    +ovn-nbctl lsp-set-type sw1-lr0 router
    bbaaef
    +ovn-nbctl lsp-set-addresses sw1-lr0 router
    bbaaef
    +ovn-nbctl lsp-set-options sw1-lr0 router-port=lr0-sw1
    bbaaef
    +
    bbaaef
    +ovn-nbctl lb-add lb1 10.0.0.10:80 10.0.0.3:80,20.0.0.3:80
    bbaaef
    +
    bbaaef
    +ovn-nbctl --wait=sb set load_balancer . ip_port_mappings:10.0.0.3=sw0-p1:10.0.0.2
    bbaaef
    +ovn-nbctl --wait=sb set load_balancer . ip_port_mappings:20.0.0.3=sw1-p1:20.0.0.2
    bbaaef
    +
    bbaaef
    +ovn-nbctl --wait=sb -- --id=@hc create \
    bbaaef
    +Load_Balancer_Health_Check vip="10.0.0.10\:80" -- add Load_Balancer . \
    bbaaef
    +health_check @hc
    bbaaef
    +
    bbaaef
    +ovn-nbctl --wait=sb ls-lb-add sw0 lb1
    bbaaef
    +ovn-nbctl --wait=sb ls-lb-add sw1 lb1
    bbaaef
    +ovn-nbctl --wait=sb lr-lb-add lr0 lb1
    bbaaef
    +
    bbaaef
    +OVN_POPULATE_ARP
    bbaaef
    +ovn-nbctl --wait=hv sync
    bbaaef
    +
    bbaaef
    +OVS_WAIT_UNTIL([test 2 = `ovn-sbctl --bare --columns _uuid find \
    bbaaef
    +service_monitor | sed '/^$/d' | wc -l`])
    bbaaef
    +
    bbaaef
    +ovn-sbctl dump-flows sw0 | grep ct_lb | grep priority=120 > lflows.txt
    bbaaef
    +AT_CHECK([cat lflows.txt], [0], [dnl
    bbaaef
    +  table=10(ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(ct_lb(10.0.0.3:80,20.0.0.3:80);)
    bbaaef
    +])
    bbaaef
    +
    bbaaef
    +# get the svc monitor mac.
    bbaaef
    +svc_mon_src_mac=`ovn-nbctl get NB_Global . options:svc_monitor_mac | \
    bbaaef
    +sed s/":"//g | sed s/\"//g`
    bbaaef
    +
    bbaaef
    +OVS_WAIT_UNTIL(
    bbaaef
    +    [test 1 = `$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif1-tx.pcap | \
    bbaaef
    +grep "505400000003${svc_mon_src_mac}" | wc -l`]
    bbaaef
    +)
    bbaaef
    +
    bbaaef
    +OVS_WAIT_UNTIL(
    bbaaef
    +    [test 1 = `$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv2/vif1-tx.pcap | \
    bbaaef
    +grep "405400000003${svc_mon_src_mac}" | wc -l`]
    bbaaef
    +)
    bbaaef
    +
    bbaaef
    +OVS_WAIT_UNTIL([test 2 = `ovn-sbctl --bare --columns status find \
    bbaaef
    +service_monitor | grep offline | wc -l`])
    bbaaef
    +
    bbaaef
    +OVS_WAIT_UNTIL(
    bbaaef
    +    [test 2 = `$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif1-tx.pcap | \
    bbaaef
    +grep "505400000003${svc_mon_src_mac}" | wc -l`]
    bbaaef
    +)
    bbaaef
    +
    bbaaef
    +OVS_WAIT_UNTIL(
    bbaaef
    +    [test 2 = `$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv2/vif1-tx.pcap | \
    bbaaef
    +grep "405400000003${svc_mon_src_mac}" | wc -l`]
    bbaaef
    +)
    bbaaef
    +
    bbaaef
    +ovn-sbctl dump-flows sw0 | grep "ip4.dst == 10.0.0.10 && tcp.dst == 80" \
    bbaaef
    +| grep priority=120 > lflows.txt
    bbaaef
    +AT_CHECK([cat lflows.txt], [0], [dnl
    bbaaef
    +  table=10(ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(drop;)
    bbaaef
    +])
    bbaaef
    +
    bbaaef
    +OVN_CLEANUP([hv1], [hv2])
    bbaaef
     AT_CLEANUP
    bbaaef
    diff --git a/tests/system-common-macros.at b/tests/system-common-macros.at
    bbaaef
    index 64bf5ec63..c8fa6f03f 100644
    bbaaef
    --- a/tests/system-common-macros.at
    bbaaef
    +++ b/tests/system-common-macros.at
    bbaaef
    @@ -267,6 +267,7 @@ m4_define([OVS_CHECK_FIREWALL],
    bbaaef
     #
    bbaaef
     m4_define([OVS_START_L7],
    bbaaef
        [PIDFILE=$(mktemp $2XXX.pid)
    bbaaef
    +    echo $PIDFILE > l7_pid_file
    bbaaef
         NETNS_DAEMONIZE([$1], [[$PYTHON $srcdir/test-l7.py $2]], [$PIDFILE])
    bbaaef
     
    bbaaef
         dnl netstat doesn't print http over IPv6 as "http6"; drop the number.
    bbaaef
    diff --git a/tests/system-ovn.at b/tests/system-ovn.at
    bbaaef
    index b3f90aae2..7d1c65d85 100644
    bbaaef
    --- a/tests/system-ovn.at
    bbaaef
    +++ b/tests/system-ovn.at
    bbaaef
    @@ -2523,3 +2523,183 @@ as
    bbaaef
     OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d
    bbaaef
     /connection dropped.*/d"])
    bbaaef
     AT_CLEANUP
    bbaaef
    +
    bbaaef
    +AT_SETUP([ovn -- Load balancer health checks])
    bbaaef
    +AT_KEYWORDS([lb])
    bbaaef
    +ovn_start
    bbaaef
    +
    bbaaef
    +OVS_TRAFFIC_VSWITCHD_START()
    bbaaef
    +ADD_BR([br-int])
    bbaaef
    +
    bbaaef
    +# Set external-ids in br-int needed for ovn-controller
    bbaaef
    +ovs-vsctl \
    bbaaef
    +        -- set Open_vSwitch . external-ids:system-id=hv1 \
    bbaaef
    +        -- set Open_vSwitch . external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \
    bbaaef
    +        -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \
    bbaaef
    +        -- set Open_vSwitch . external-ids:ovn-encap-ip=169.0.0.1 \
    bbaaef
    +        -- set bridge br-int fail-mode=secure other-config:disable-in-band=true
    bbaaef
    +
    bbaaef
    +# Start ovn-controller
    bbaaef
    +start_daemon ovn-controller
    bbaaef
    +
    bbaaef
    +ovn-nbctl ls-add sw0
    bbaaef
    +
    bbaaef
    +ovn-nbctl lsp-add sw0 sw0-p1
    bbaaef
    +ovn-nbctl lsp-set-addresses sw0-p1 "50:54:00:00:00:03 10.0.0.3"
    bbaaef
    +ovn-nbctl lsp-set-port-security sw0-p1 "50:54:00:00:00:03 10.0.0.3"
    bbaaef
    +
    bbaaef
    +ovn-nbctl lsp-add sw0 sw0-p2
    bbaaef
    +ovn-nbctl lsp-set-addresses sw0-p2 "50:54:00:00:00:04 10.0.0.4"
    bbaaef
    +ovn-nbctl lsp-set-port-security sw0-p2 "50:54:00:00:00:04 10.0.0.4"
    bbaaef
    +
    bbaaef
    +# Create the second logical switch with one port
    bbaaef
    +ovn-nbctl ls-add sw1
    bbaaef
    +ovn-nbctl lsp-add sw1 sw1-p1
    bbaaef
    +ovn-nbctl lsp-set-addresses sw1-p1 "40:54:00:00:00:03 20.0.0.3"
    bbaaef
    +ovn-nbctl lsp-set-port-security sw1-p1 "40:54:00:00:00:03 20.0.0.3"
    bbaaef
    +
    bbaaef
    +# Create a logical router and attach both logical switches
    bbaaef
    +ovn-nbctl lr-add lr0
    bbaaef
    +ovn-nbctl lrp-add lr0 lr0-sw0 00:00:00:00:ff:01 10.0.0.1/24
    bbaaef
    +ovn-nbctl lsp-add sw0 sw0-lr0
    bbaaef
    +ovn-nbctl lsp-set-type sw0-lr0 router
    bbaaef
    +ovn-nbctl lsp-set-addresses sw0-lr0 router
    bbaaef
    +ovn-nbctl lsp-set-options sw0-lr0 router-port=lr0-sw0
    bbaaef
    +
    bbaaef
    +ovn-nbctl lrp-add lr0 lr0-sw1 00:00:00:00:ff:02 20.0.0.1/24
    bbaaef
    +ovn-nbctl lsp-add sw1 sw1-lr0
    bbaaef
    +ovn-nbctl lsp-set-type sw1-lr0 router
    bbaaef
    +ovn-nbctl lsp-set-addresses sw1-lr0 router
    bbaaef
    +ovn-nbctl lsp-set-options sw1-lr0 router-port=lr0-sw1
    bbaaef
    +
    bbaaef
    +ovn-nbctl lb-add lb1 10.0.0.10:80 10.0.0.3:80,20.0.0.3:80
    bbaaef
    +
    bbaaef
    +ovn-nbctl --wait=sb set load_balancer . ip_port_mappings:10.0.0.3=sw0-p1:10.0.0.2
    bbaaef
    +ovn-nbctl --wait=sb set load_balancer . ip_port_mappings:20.0.0.3=sw1-p1:20.0.0.2
    bbaaef
    +
    bbaaef
    +ovn-nbctl --wait=sb -- --id=@hc create \
    bbaaef
    +Load_Balancer_Health_Check vip="10.0.0.10\:80" -- add Load_Balancer . \
    bbaaef
    +health_check @hc
    bbaaef
    +
    bbaaef
    +ovn-nbctl --wait=sb ls-lb-add sw0 lb1
    bbaaef
    +ovn-nbctl --wait=sb ls-lb-add sw1 lb1
    bbaaef
    +ovn-nbctl --wait=sb lr-lb-add lr0 lb1
    bbaaef
    +
    bbaaef
    +OVN_POPULATE_ARP
    bbaaef
    +ovn-nbctl --wait=hv sync
    bbaaef
    +
    bbaaef
    +ADD_NAMESPACES(sw0-p1)
    bbaaef
    +ADD_VETH(sw0-p1, sw0-p1, br-int, "10.0.0.3/24", "50:54:00:00:00:03", \
    bbaaef
    +         "10.0.0.1")
    bbaaef
    +
    bbaaef
    +ADD_NAMESPACES(sw1-p1)
    bbaaef
    +ADD_VETH(sw1-p1, sw1-p1, br-int, "20.0.0.3/24", "40:54:00:00:00:03", \
    bbaaef
    +         "20.0.0.1")
    bbaaef
    +
    bbaaef
    +ADD_NAMESPACES(sw0-p2)
    bbaaef
    +ADD_VETH(sw0-p2, sw0-p2, br-int, "10.0.0.4/24", "50:54:00:00:00:04", \
    bbaaef
    +         "10.0.0.1")
    bbaaef
    +
    bbaaef
    +# Wait until all the services are set to offline.
    bbaaef
    +OVS_WAIT_UNTIL([test 2 = `ovn-sbctl --bare --columns status find \
    bbaaef
    +service_monitor | sed '/^$/d' | grep offline | wc -l`])
    bbaaef
    +
    bbaaef
    +# Start webservers in 'sw0-p1' and 'sw1-p1'.
    bbaaef
    +OVS_START_L7([sw0-p1], [http])
    bbaaef
    +sw0_p1_pid_file=`cat l7_pid_file`
    bbaaef
    +OVS_START_L7([sw1-p1], [http])
    bbaaef
    +
    bbaaef
    +# Wait until the services are set to online.
    bbaaef
    +OVS_WAIT_UNTIL([test 2 = `ovn-sbctl --bare --columns status find \
    bbaaef
    +service_monitor | sed '/^$/d' | grep online | wc -l`])
    bbaaef
    +
    bbaaef
    +OVS_WAIT_UNTIL(
    bbaaef
    +    [ovn-sbctl dump-flows sw0 | grep ct_lb | grep priority=120 | grep "ip4.dst == 10.0.0.10" > lflows.txt
    bbaaef
    +     test 1 = `cat lflows.txt | grep "ct_lb(10.0.0.3:80,20.0.0.3:80)" | wc -l`]
    bbaaef
    +)
    bbaaef
    +
    bbaaef
    +# From sw0-p2 send traffic to vip - 10.0.0.10
    bbaaef
    +for i in `seq 1 20`; do
    bbaaef
    +    echo Request $i
    bbaaef
    +    ovn-sbctl list service_monitor
    bbaaef
    +    NS_CHECK_EXEC([sw0-p2], [wget 10.0.0.10 -t 5 -T 1 --retry-connrefused -v -o wget$i.log])
    bbaaef
    +done
    bbaaef
    +
    bbaaef
    +dnl Each server should have at least one connection.
    bbaaef
    +AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.0.0.10) | \
    bbaaef
    +sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
    bbaaef
    +tcp,orig=(src=10.0.0.4,dst=10.0.0.10,sport=<cleared>,dport=<cleared>),reply=(src=10.0.0.3,dst=10.0.0.4,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
    bbaaef
    +tcp,orig=(src=10.0.0.4,dst=10.0.0.10,sport=<cleared>,dport=<cleared>),reply=(src=20.0.0.3,dst=10.0.0.4,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
    bbaaef
    +])
    bbaaef
    +
    bbaaef
    +# Stop webserer in sw0-p1
    bbaaef
    +kill `cat $sw0_p1_pid_file`
    bbaaef
    +
    bbaaef
    +# Wait until service_monitor for sw0-p1 is set to offline
    bbaaef
    +OVS_WAIT_UNTIL([test 1 = `ovn-sbctl --bare --columns status find \
    bbaaef
    +service_monitor logical_port=sw0-p1 | sed '/^$/d' | grep offline | wc -l`])
    bbaaef
    +
    bbaaef
    +OVS_WAIT_UNTIL(
    bbaaef
    +    [ovn-sbctl dump-flows sw0 | grep ct_lb | grep priority=120 | grep "ip4.dst == 10.0.0.10" > lflows.txt
    bbaaef
    +     test 1 = `cat lflows.txt | grep "ct_lb(20.0.0.3:80)" | wc -l`]
    bbaaef
    +)
    bbaaef
    +
    bbaaef
    +ovs-appctl dpctl/flush-conntrack
    bbaaef
    +# From sw0-p2 send traffic to vip - 10.0.0.10
    bbaaef
    +for i in `seq 1 20`; do
    bbaaef
    +    echo Request $i
    bbaaef
    +    NS_CHECK_EXEC([sw0-p2], [wget 10.0.0.10 -t 5 -T 1 --retry-connrefused -v -o wget$i.log])
    bbaaef
    +done
    bbaaef
    +
    bbaaef
    +AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.0.0.10) | \
    bbaaef
    +sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
    bbaaef
    +tcp,orig=(src=10.0.0.4,dst=10.0.0.10,sport=<cleared>,dport=<cleared>),reply=(src=20.0.0.3,dst=10.0.0.4,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
    bbaaef
    +])
    bbaaef
    +
    bbaaef
    +# Create udp load balancer.
    bbaaef
    +ovn-nbctl lb-add lb2 10.0.0.10:80 10.0.0.3:80,20.0.0.3:80 udp
    bbaaef
    +lb_udp=`ovn-nbctl lb-list | grep udp | awk '{print $1}'`
    bbaaef
    +
    bbaaef
    +echo "lb udp uuid = $lb_udp"
    bbaaef
    +
    bbaaef
    +ovn-nbctl list load_balancer
    bbaaef
    +
    bbaaef
    +ovn-nbctl --wait=sb set load_balancer $lb_udp ip_port_mappings:10.0.0.3=sw0-p1:10.0.0.2
    bbaaef
    +ovn-nbctl --wait=sb set load_balancer $lb_udp ip_port_mappings:20.0.0.3=sw1-p1:20.0.0.2
    bbaaef
    +
    bbaaef
    +ovn-nbctl --wait=sb -- --id=@hc create \
    bbaaef
    +Load_Balancer_Health_Check vip="10.0.0.10\:80" -- add Load_Balancer $lb_udp \
    bbaaef
    +health_check @hc
    bbaaef
    +
    bbaaef
    +ovn-nbctl --wait=sb ls-lb-add sw0 lb2
    bbaaef
    +ovn-nbctl --wait=sb ls-lb-add sw1 lb2
    bbaaef
    +ovn-nbctl --wait=sb lr-lb-add lr0 lb2
    bbaaef
    +
    bbaaef
    +sleep 10
    bbaaef
    +
    bbaaef
    +ovn-nbctl list load_balancer
    bbaaef
    +echo "*******Next is health check*******"
    bbaaef
    +ovn-nbctl list Load_Balancer_Health_Check
    bbaaef
    +echo "********************"
    bbaaef
    +ovn-sbctl list service_monitor
    bbaaef
    +
    bbaaef
    +# Wait until udp service_monitor are set to offline
    bbaaef
    +OVS_WAIT_UNTIL([test 2 = `ovn-sbctl --bare --columns status find \
    bbaaef
    +service_monitor protocol=udp | sed '/^$/d' | grep offline | wc -l`])
    bbaaef
    +
    bbaaef
    +OVS_APP_EXIT_AND_WAIT([ovn-controller])
    bbaaef
    +
    bbaaef
    +as ovn-sb
    bbaaef
    +OVS_APP_EXIT_AND_WAIT([ovsdb-server])
    bbaaef
    +
    bbaaef
    +as ovn-nb
    bbaaef
    +OVS_APP_EXIT_AND_WAIT([ovsdb-server])
    bbaaef
    +
    bbaaef
    +as northd
    bbaaef
    +OVS_APP_EXIT_AND_WAIT([ovn-northd])
    bbaaef
    +
    bbaaef
    +as
    bbaaef
    +OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d
    bbaaef
    +/connection dropped.*/d"])
    bbaaef
    +
    bbaaef
    +AT_CLEANUP
    bbaaef
    -- 
    bbaaef
    2.23.0
    bbaaef