From patchwork Tue Nov 22 17:54:05 2022
Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
X-Patchwork-Submitter: Lorenzo Bianconi
X-Patchwork-Id: 1707958
Return-Path:
X-Original-To: incoming@patchwork.ozlabs.org
Delivered-To: patchwork-incoming@legolas.ozlabs.org
Authentication-Results: legolas.ozlabs.org;
spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org
(client-ip=2605:bc80:3010::133; helo=smtp2.osuosl.org;
envelope-from=ovs-dev-bounces@openvswitch.org; receiver=)
Authentication-Results: legolas.ozlabs.org;
dkim=fail reason="signature verification failed" (1024-bit key;
unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256
header.s=mimecast20190719 header.b=BXNxI59K;
dkim-atps=neutral
Received: from smtp2.osuosl.org (smtp2.osuosl.org [IPv6:2605:bc80:3010::133])
(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)
key-exchange X25519 server-signature ECDSA (P-384) server-digest SHA384)
(No client certificate requested)
by legolas.ozlabs.org (Postfix) with ESMTPS id 4NGsMj121hz23nq
for ; Wed, 23 Nov 2022 04:54:25 +1100 (AEDT)
Received: from localhost (localhost [127.0.0.1])
by smtp2.osuosl.org (Postfix) with ESMTP id 392CD40B8A;
Tue, 22 Nov 2022 17:54:23 +0000 (UTC)
DKIM-Filter: OpenDKIM Filter v2.11.0 smtp2.osuosl.org 392CD40B8A
Authentication-Results: smtp2.osuosl.org;
dkim=fail reason="signature verification failed" (1024-bit key)
header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256
header.s=mimecast20190719 header.b=BXNxI59K
X-Virus-Scanned: amavisd-new at osuosl.org
Received: from smtp2.osuosl.org ([127.0.0.1])
by localhost (smtp2.osuosl.org [127.0.0.1]) (amavisd-new, port 10024)
with ESMTP id Fi-YO4F34qC4; Tue, 22 Nov 2022 17:54:21 +0000 (UTC)
Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [140.211.9.56])
by smtp2.osuosl.org (Postfix) with ESMTPS id EE510400CB;
Tue, 22 Nov 2022 17:54:19 +0000 (UTC)
DKIM-Filter: OpenDKIM Filter v2.11.0 smtp2.osuosl.org EE510400CB
Received: from lf-lists.osuosl.org (localhost [127.0.0.1])
by lists.linuxfoundation.org (Postfix) with ESMTP id BFED3C0033;
Tue, 22 Nov 2022 17:54:19 +0000 (UTC)
X-Original-To: ovs-dev@openvswitch.org
Delivered-To: ovs-dev@lists.linuxfoundation.org
Received: from smtp3.osuosl.org (smtp3.osuosl.org [140.211.166.136])
by lists.linuxfoundation.org (Postfix) with ESMTP id 17822C002D
for ; Tue, 22 Nov 2022 17:54:19 +0000 (UTC)
Received: from localhost (localhost [127.0.0.1])
by smtp3.osuosl.org (Postfix) with ESMTP id E544D60FF4
for ; Tue, 22 Nov 2022 17:54:18 +0000 (UTC)
DKIM-Filter: OpenDKIM Filter v2.11.0 smtp3.osuosl.org E544D60FF4
Authentication-Results: smtp3.osuosl.org;
dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com
header.a=rsa-sha256 header.s=mimecast20190719 header.b=BXNxI59K
X-Virus-Scanned: amavisd-new at osuosl.org
Received: from smtp3.osuosl.org ([127.0.0.1])
by localhost (smtp3.osuosl.org [127.0.0.1]) (amavisd-new, port 10024)
with ESMTP id JOJLCPZ5ycF5 for ;
Tue, 22 Nov 2022 17:54:16 +0000 (UTC)
X-Greylist: domain auto-whitelisted by SQLgrey-1.8.0
DKIM-Filter: OpenDKIM Filter v2.11.0 smtp3.osuosl.org 4A66260F7A
Received: from us-smtp-delivery-124.mimecast.com
(us-smtp-delivery-124.mimecast.com [170.10.133.124])
by smtp3.osuosl.org (Postfix) with ESMTPS id 4A66260F7A
for ; Tue, 22 Nov 2022 17:54:16 +0000 (UTC)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com;
s=mimecast20190719; t=1669139655;
h=from:from:reply-to:subject:subject:date:date:message-id:message-id:
to:to:cc:mime-version:mime-version:content-type:content-type:
content-transfer-encoding:content-transfer-encoding;
bh=8CByq8xz8qIkOdnTCPdpVON4Ess5kO6iRcL32g0DVqc=;
b=BXNxI59Kwrv8M57SzN9mQ7laxUflJ3BbrmKhfAfaSncJ0QlCTmkWHAscgLNpbI4F0W1RjO
YY4BbU+9b273v+I8Lm2YcUsTFifPy5oO2gs3tHl4pLT4+iYnfwoYWo4FyfMpbZLt6RnSjK
0MKMRqlKxHA5gLWoB/CQOgsaQA357RU=
Received: from mail-wm1-f71.google.com (mail-wm1-f71.google.com
[209.85.128.71]) by relay.mimecast.com with ESMTP with STARTTLS
(version=TLSv1.3, cipher=TLS_AES_128_GCM_SHA256) id
us-mta-582-sRFUwaq1OJ-7B3IjF2uZqQ-1; Tue, 22 Nov 2022 12:54:13 -0500
X-MC-Unique: sRFUwaq1OJ-7B3IjF2uZqQ-1
Received: by mail-wm1-f71.google.com with SMTP id
j2-20020a05600c1c0200b003cf7397fc9bso8398220wms.5
for ; Tue, 22 Nov 2022 09:54:13 -0800 (PST)
X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
d=1e100.net; s=20210112;
h=content-transfer-encoding:mime-version:message-id:date:subject:to
:from:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to;
bh=8CByq8xz8qIkOdnTCPdpVON4Ess5kO6iRcL32g0DVqc=;
b=og4sox7bPyUXnMmddO9Yn95sXKON3xRQXD6rMm8RKcuUpVTxfYRfHOfXjJ/IfVMy9v
lDoutHOuqRXfXxUbu6RaxWiC1knQuOu4GAw34Ys62jnmNEyBzsmwWSShUDK5f1k7/Tqm
/yU7w+EPxfkg63oYXtzlvyuP4fummwfXxQRBMcbexpPRfal4JzWTkPWFQdG2q3ynueuY
ZjJsk7Nyqxv72Umkz+h12HNExoPau5Yp+kqLGL0yODsJjmKVmPA1CWcdb8cCjzzDt0M5
c+go4MsriLG2CiS/to7+YoBGLD65abAEqr7LdwvF0fMbO3oERcGLlvqWCxWm62BL8Ktm
Oqxw==
X-Gm-Message-State: ANoB5pkVoneRDt+An9B+Ed4JN4BLYgm6I1buxF6bTfzbeH62zpQHAKvl
Q5aVggmcGmsWV0nok5C4CqtXPmdzenNMA9qa58R1dG/460J2D6+qv5QNyT+IxEwTGyss7ghLeh5
qASxLADQnaPfhqBYk4xnlSzIjEqoibOgTof8NO5oU8xXy3OVoqANriAtvcGt8yC2AoOEtrL3hP8
Y/hFS1
X-Received: by 2002:a05:600c:47cf:b0:3cf:8051:47ae with SMTP id
l15-20020a05600c47cf00b003cf805147aemr7346838wmo.24.1669139651371;
Tue, 22 Nov 2022 09:54:11 -0800 (PST)
X-Google-Smtp-Source:
AA0mqf6P+CN1QwuKF4egKM2W/nfkWjPJ8l5cgTlr0fxB5qSyZr3ty96L0IvITwa+r0ipJ73CNAFPKg==
X-Received: by 2002:a05:600c:47cf:b0:3cf:8051:47ae with SMTP id
l15-20020a05600c47cf00b003cf805147aemr7346779wmo.24.1669139650301;
Tue, 22 Nov 2022 09:54:10 -0800 (PST)
Received: from localhost (net-188-216-77-84.cust.vodafonedsl.it.
[188.216.77.84]) by smtp.gmail.com with ESMTPSA id
v17-20020a5d5911000000b00241da0e018dsm5189068wrd.29.2022.11.22.09.54.09
for
(version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);
Tue, 22 Nov 2022 09:54:09 -0800 (PST)
From: Lorenzo Bianconi
To: ovs-dev@openvswitch.org
Date: Tue, 22 Nov 2022 18:54:05 +0100
Message-Id:
<61c9bb288444c044a1e3945860cd72a2e5936f49.1669139578.git.lorenzo.bianconi@redhat.com>
X-Mailer: git-send-email 2.38.1
MIME-Version: 1.0
X-Mimecast-Spam-Score: 0
X-Mimecast-Originator: redhat.com
Subject: [ovs-dev] [PATCH ovn] Add IPv6 support for lb health-check
X-BeenThere: ovs-dev@openvswitch.org
X-Mailman-Version: 2.1.15
Precedence: list
List-Id:
List-Unsubscribe: ,
List-Archive:
List-Post:
List-Help:
List-Subscribe: ,
Errors-To: ovs-dev-bounces@openvswitch.org
Sender: "dev"
Add Similar to IPv4 counterpart, introduce IPv6 load-balancer health
check support.
Reported-at: https://bugzilla.redhat.com/show_bug.cgi?id=2136094
Signed-off-by: Lorenzo Bianconi
---
controller/pinctrl.c | 213 ++++++++++++++++++++++++-------------
northd/northd.c | 77 ++++++++++----
northd/ovn-northd.8.xml | 17 +++
ovn-nb.xml | 8 +-
tests/ovn.at | 201 ++++++++++++++++++++++++++++++++++-
tests/system-ovn.at | 230 +++++++++++++++++++++++++++++++++++++++-
6 files changed, 647 insertions(+), 99 deletions(-)
diff --git a/controller/pinctrl.c b/controller/pinctrl.c
index f44775c7e..89c2f207b 100644
--- a/controller/pinctrl.c
+++ b/controller/pinctrl.c
@@ -6676,7 +6676,7 @@ sync_svc_monitors(struct ovsdb_idl_txn *ovnsb_idl_txn,
ovs_be32 ip4;
if (ip_parse(sb_svc_mon->ip, &ip4)) {
ip_addr = in6_addr_mapped_ipv4(ip4);
- } else {
+ } else if (!ipv6_parse(sb_svc_mon->ip, &ip_addr)) {
continue;
}
@@ -6689,16 +6689,27 @@ sync_svc_monitors(struct ovsdb_idl_txn *ovnsb_idl_txn,
continue;
}
- for (size_t j = 0; j < laddrs.n_ipv4_addrs; j++) {
- if (ip4 == laddrs.ipv4_addrs[j].addr) {
- ea = laddrs.ea;
- mac_found = true;
- break;
+ if (IN6_IS_ADDR_V4MAPPED(&ip_addr)) {
+ for (size_t j = 0; j < laddrs.n_ipv4_addrs; j++) {
+ if (ip4 == laddrs.ipv4_addrs[j].addr) {
+ ea = laddrs.ea;
+ mac_found = true;
+ break;
+ }
+ }
+ } else {
+ for (size_t j = 0; j < laddrs.n_ipv6_addrs; j++) {
+ if (!memcmp(&ip_addr, &laddrs.ipv6_addrs[j].addr,
+ sizeof(ovs_be32[4]))) {
+ ea = laddrs.ea;
+ mac_found = true;
+ break;
+ }
}
}
- if (!mac_found && !laddrs.n_ipv4_addrs) {
- /* IPv4 address(es) are not configured. Use the first mac. */
+ if (!mac_found && !laddrs.n_ipv4_addrs && !laddrs.n_ipv6_addrs) {
+ /* IP address(es) are not configured. Use the first mac. */
ea = laddrs.ea;
mac_found = true;
}
@@ -6732,7 +6743,7 @@ sync_svc_monitors(struct ovsdb_idl_txn *ovnsb_idl_txn,
svc_mon->port_key = port_key;
svc_mon->proto_port = sb_svc_mon->port;
svc_mon->ip = ip_addr;
- svc_mon->is_ip6 = false;
+ svc_mon->is_ip6 = !IN6_IS_ADDR_V4MAPPED(&ip_addr);
svc_mon->state = SVC_MON_S_INIT;
svc_mon->status = SVC_MON_ST_UNKNOWN;
svc_mon->protocol = protocol;
@@ -7500,26 +7511,30 @@ svc_monitor_send_tcp_health_check__(struct rconn *swconn,
ovs_be32 tcp_ack,
ovs_be16 tcp_src)
{
- if (svc_mon->is_ip6) {
- return;
- }
-
/* Compose a TCP-SYN packet. */
uint64_t packet_stub[128 / 8];
struct dp_packet packet;
+ dp_packet_use_stub(&packet, packet_stub, sizeof packet_stub);
struct eth_addr eth_src;
eth_addr_from_string(svc_mon->sb_svc_mon->src_mac, ð_src);
- ovs_be32 ip4_src;
- ip_parse(svc_mon->sb_svc_mon->src_ip, &ip4_src);
-
- dp_packet_use_stub(&packet, packet_stub, sizeof packet_stub);
- pinctrl_compose_ipv4(&packet, eth_src, svc_mon->ea,
- ip4_src, in6_addr_get_mapped_ipv4(&svc_mon->ip),
- IPPROTO_TCP, 63, TCP_HEADER_LEN);
+ if (svc_mon->is_ip6) {
+ struct in6_addr ip6_src;
+ ipv6_parse(svc_mon->sb_svc_mon->src_ip, &ip6_src);
+ pinctrl_compose_ipv6(&packet, eth_src, svc_mon->ea,
+ &ip6_src, &svc_mon->ip, IPPROTO_TCP,
+ 63, TCP_HEADER_LEN);
+ } else {
+ ovs_be32 ip4_src;
+ ip_parse(svc_mon->sb_svc_mon->src_ip, &ip4_src);
+ pinctrl_compose_ipv4(&packet, eth_src, svc_mon->ea,
+ ip4_src, in6_addr_get_mapped_ipv4(&svc_mon->ip),
+ IPPROTO_TCP, 63, TCP_HEADER_LEN);
+ }
struct tcp_header *th = dp_packet_l4(&packet);
dp_packet_set_l4(&packet, th);
+ th->tcp_csum = 0;
th->tcp_dst = htons(svc_mon->proto_port);
th->tcp_src = tcp_src;
@@ -7530,7 +7545,11 @@ svc_monitor_send_tcp_health_check__(struct rconn *swconn,
th->tcp_winsz = htons(65160);
uint32_t csum;
- csum = packet_csum_pseudoheader(dp_packet_l3(&packet));
+ if (svc_mon->is_ip6) {
+ csum = packet_csum_pseudoheader6(dp_packet_l3(&packet));
+ } else {
+ csum = packet_csum_pseudoheader(dp_packet_l3(&packet));
+ }
csum = csum_continue(csum, th, dp_packet_size(&packet) -
((const unsigned char *)th -
(const unsigned char *)dp_packet_eth(&packet)));
@@ -7565,21 +7584,26 @@ svc_monitor_send_udp_health_check(struct rconn *swconn,
struct svc_monitor *svc_mon,
ovs_be16 udp_src)
{
- if (svc_mon->is_ip6) {
- return;
- }
-
struct eth_addr eth_src;
eth_addr_from_string(svc_mon->sb_svc_mon->src_mac, ð_src);
- ovs_be32 ip4_src;
- ip_parse(svc_mon->sb_svc_mon->src_ip, &ip4_src);
uint64_t packet_stub[128 / 8];
struct dp_packet packet;
dp_packet_use_stub(&packet, packet_stub, sizeof packet_stub);
- pinctrl_compose_ipv4(&packet, eth_src, svc_mon->ea,
- ip4_src, in6_addr_get_mapped_ipv4(&svc_mon->ip),
- IPPROTO_UDP, 63, UDP_HEADER_LEN + 8);
+
+ if (svc_mon->is_ip6) {
+ struct in6_addr ip6_src;
+ ipv6_parse(svc_mon->sb_svc_mon->src_ip, &ip6_src);
+ pinctrl_compose_ipv6(&packet, eth_src, svc_mon->ea,
+ &ip6_src, &svc_mon->ip, IPPROTO_UDP,
+ 63, UDP_HEADER_LEN + 8);
+ } else {
+ ovs_be32 ip4_src;
+ ip_parse(svc_mon->sb_svc_mon->src_ip, &ip4_src);
+ pinctrl_compose_ipv4(&packet, eth_src, svc_mon->ea,
+ ip4_src, in6_addr_get_mapped_ipv4(&svc_mon->ip),
+ IPPROTO_UDP, 63, UDP_HEADER_LEN + 8);
+ }
struct udp_header *uh = dp_packet_l4(&packet);
dp_packet_set_l4(&packet, uh);
@@ -7587,6 +7611,16 @@ svc_monitor_send_udp_health_check(struct rconn *swconn,
uh->udp_src = udp_src;
uh->udp_len = htons(UDP_HEADER_LEN + 8);
uh->udp_csum = 0;
+ if (svc_mon->is_ip6) {
+ uint32_t csum = packet_csum_pseudoheader6(dp_packet_l3(&packet));
+ csum = csum_continue(csum, uh, dp_packet_size(&packet) -
+ ((const unsigned char *) uh -
+ (const unsigned char *) dp_packet_eth(&packet)));
+ uh->udp_csum = csum_finish(csum);
+ if (!uh->udp_csum) {
+ uh->udp_csum = htons(0xffff);
+ }
+ }
uint64_t ofpacts_stub[4096 / 8];
struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(ofpacts_stub);
@@ -7649,6 +7683,7 @@ svc_monitors_run(struct rconn *swconn,
long long int current_time = time_msec();
long long int next_run_time = LLONG_MAX;
enum svc_monitor_status old_status = svc_mon->status;
+
switch (svc_mon->state) {
case SVC_MON_S_INIT:
svc_monitor_send_health_check(swconn, svc_mon);
@@ -7779,32 +7814,38 @@ pinctrl_handle_svc_check(struct rconn *swconn, const struct flow *ip_flow,
uint32_t port_key = md->flow.regs[MFF_LOG_INPORT - MFF_REG0];
struct in6_addr ip_addr;
struct eth_header *in_eth = dp_packet_data(pkt_in);
- struct ip_header *in_ip = dp_packet_l3(pkt_in);
+ uint8_t ip_proto;
- if (in_ip->ip_proto != IPPROTO_TCP && in_ip->ip_proto != IPPROTO_ICMP) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
- VLOG_WARN_RL(&rl,
- "handle service check: Unsupported protocol - [%x]",
- in_ip->ip_proto);
- return;
+ if (in_eth->eth_type == htons(ETH_TYPE_IP)) {
+ struct ip_header *in_ip = dp_packet_l3(pkt_in);
+ uint16_t in_ip_len = ntohs(in_ip->ip_tot_len);
+ if (in_ip_len < IP_HEADER_LEN) {
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+ VLOG_WARN_RL(&rl,
+ "IP packet with invalid length (%u)",
+ in_ip_len);
+ return;
+ }
+
+ ip_addr = in6_addr_mapped_ipv4(ip_flow->nw_src);
+ ip_proto = in_ip->ip_proto;
+ } else {
+ struct ovs_16aligned_ip6_hdr *in_ip = dp_packet_l3(pkt_in);
+ ip_addr = ip_flow->ipv6_src;
+ ip_proto = in_ip->ip6_nxt;
}
- uint16_t in_ip_len = ntohs(in_ip->ip_tot_len);
- if (in_ip_len < IP_HEADER_LEN) {
+ if (ip_proto != IPPROTO_TCP && ip_proto != IPPROTO_ICMP &&
+ ip_proto != IPPROTO_ICMPV6) {
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
VLOG_WARN_RL(&rl,
- "IP packet with invalid length (%u)",
- in_ip_len);
+ "handle service check: Unsupported protocol - [%x]",
+ ip_proto);
return;
}
- if (in_eth->eth_type == htons(ETH_TYPE_IP)) {
- ip_addr = in6_addr_mapped_ipv4(ip_flow->nw_src);
- } else {
- ip_addr = ip_flow->ipv6_dst;
- }
- if (in_ip->ip_proto == IPPROTO_TCP) {
+ if (ip_proto == IPPROTO_TCP) {
uint32_t hash =
hash_bytes(&ip_addr, sizeof ip_addr,
hash_3words(dp_key, port_key, ntohs(ip_flow->tp_src)));
@@ -7821,44 +7862,68 @@ pinctrl_handle_svc_check(struct rconn *swconn, const struct flow *ip_flow,
}
pinctrl_handle_tcp_svc_check(swconn, pkt_in, svc_mon);
} else {
- /* It's ICMP packet. */
- struct icmp_header *ih = dp_packet_l4(pkt_in);
- if (!ih) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
- VLOG_WARN_RL(&rl, "ICMPv4 packet with invalid header");
- return;
- }
-
- if (ih->icmp_type != ICMP4_DST_UNREACH || ih->icmp_code != 3) {
- return;
- }
-
+ struct udp_header *orig_uh;
const char *end =
(char *)dp_packet_l4(pkt_in) + dp_packet_l4_size(pkt_in);
- const struct ip_header *orig_ip_hr =
- dp_packet_get_icmp_payload(pkt_in);
- if (!orig_ip_hr) {
+ void *l4h = dp_packet_l4(pkt_in);
+ if (!l4h) {
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
- VLOG_WARN_RL(&rl, "Original IP datagram not present in "
- "ICMP packet");
+ VLOG_WARN_RL(&rl, "ICMP packet with invalid header");
return;
}
- if (ntohs(orig_ip_hr->ip_tot_len) !=
- (IP_HEADER_LEN + UDP_HEADER_LEN + 8)) {
+ const void *in_ip = dp_packet_get_icmp_payload(pkt_in);
+ if (!in_ip) {
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
- VLOG_WARN_RL(&rl, "Invalid original IP datagram length present "
- "in ICMP packet");
+ VLOG_WARN_RL(&rl, "Original IP datagram not present in "
+ "ICMP packet");
return;
}
- struct udp_header *orig_uh = (struct udp_header *) (orig_ip_hr + 1);
- if ((char *)orig_uh >= end) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
- VLOG_WARN_RL(&rl, "Invalid UDP header in the original "
- "IP datagram");
- return;
+ if (in_eth->eth_type == htons(ETH_TYPE_IP)) {
+ struct icmp_header *ih = l4h;
+ /* It's ICMP packet. */
+ if (ih->icmp_type != ICMP4_DST_UNREACH || ih->icmp_code != 3) {
+ return;
+ }
+
+ const struct ip_header *orig_ip_hr = in_ip;
+ if (ntohs(orig_ip_hr->ip_tot_len) !=
+ (IP_HEADER_LEN + UDP_HEADER_LEN + 8)) {
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+ VLOG_WARN_RL(&rl, "Invalid original IP datagram length "
+ "present in ICMP packet");
+ return;
+ }
+
+ orig_uh = (struct udp_header *) (orig_ip_hr + 1);
+ if ((char *) orig_uh >= end) {
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+ VLOG_WARN_RL(&rl, "Invalid UDP header in the original "
+ "IP datagram");
+ return;
+ }
+ } else {
+ struct icmp6_header *ih6 = l4h;
+ if (ih6->icmp6_type != 1 || ih6->icmp6_code != 4) {
+ return;
+ }
+
+ const struct ovs_16aligned_ip6_hdr *ip6_hdr = in_ip;
+ if (ntohs(ip6_hdr->ip6_plen) != UDP_HEADER_LEN + 8) {
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+ VLOG_WARN_RL(&rl, "Invalid original IP datagram length "
+ "present in ICMP packet");
+ }
+
+ orig_uh = (struct udp_header *) (ip6_hdr + 1);
+ if ((char *) orig_uh >= end) {
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+ VLOG_WARN_RL(&rl, "Invalid UDP header in the original "
+ "IP datagram");
+ return;
+ }
}
uint32_t hash =
diff --git a/northd/northd.c b/northd/northd.c
index 00ff8f933..68c7655ba 100644
--- a/northd/northd.c
+++ b/northd/northd.c
@@ -3751,8 +3751,15 @@ ovn_lb_svc_create(struct ovsdb_idl_txn *ovnsb_txn, struct ovn_northd_lb *lb,
struct ovn_port *op = NULL;
char *svc_mon_src_ip = NULL;
+
+ bool ipv6 = !IN6_IS_ADDR_V4MAPPED(&lb_vip->vip);
+ struct ds key = DS_EMPTY_INITIALIZER;
+ ds_put_format(&key, "%s%s%s",
+ ipv6 ? "[" : "", backend->ip_str,
+ ipv6 ? "]" : "");
+
const char *s = smap_get(&lb->nlb->ip_port_mappings,
- backend->ip_str);
+ ds_cstr(&key));
if (s) {
char *port_name = xstrdup(s);
char *p = strstr(port_name, ":");
@@ -3760,10 +3767,21 @@ ovn_lb_svc_create(struct ovsdb_idl_txn *ovnsb_txn, struct ovn_northd_lb *lb,
*p = 0;
p++;
op = ovn_port_find(ports, port_name);
+ if (ipv6) {
+ p = strstr(p, "[");
+ if (p) {
+ p++;
+ }
+ char *q = strstr(p, "]");
+ if (q) {
+ *q = 0;
+ }
+ }
svc_mon_src_ip = xstrdup(p);
}
free(port_name);
}
+ ds_destroy(&key);
backend_nb->op = op;
backend_nb->svc_mon_src_ip = svc_mon_src_ip;
@@ -3841,8 +3859,10 @@ build_lb_vip_actions(struct ovn_lb_vip *lb_vip,
}
n_active_backends++;
- ds_put_format(action, "%s:%"PRIu16",",
- backend->ip_str, backend->port);
+ bool ipv6 = !IN6_IS_ADDR_V4MAPPED(&backend->ip);
+ ds_put_format(action, "%s%s%s:%"PRIu16",",
+ ipv6 ? "[" : "", backend->ip_str,
+ ipv6 ? "]" : "", backend->port);
}
if (!n_active_backends) {
@@ -8435,6 +8455,7 @@ build_lswitch_arp_nd_service_monitor(struct ovn_northd_lb *lb,
continue;
}
+ struct ovn_lb_vip *lb_vip = &lb->vips[i];
for (size_t j = 0; j < lb_vip_nb->n_backends; j++) {
struct ovn_northd_lb_backend *backend_nb =
&lb_vip_nb->backends_nb[j];
@@ -8443,22 +8464,42 @@ build_lswitch_arp_nd_service_monitor(struct ovn_northd_lb *lb,
}
ds_clear(match);
- ds_put_format(match, "arp.tpa == %s && arp.op == 1",
- backend_nb->svc_mon_src_ip);
ds_clear(actions);
- ds_put_format(actions,
- "eth.dst = eth.src; "
- "eth.src = %s; "
- "arp.op = 2; /* ARP reply */ "
- "arp.tha = arp.sha; "
- "arp.sha = %s; "
- "arp.tpa = arp.spa; "
- "arp.spa = %s; "
- "outport = inport; "
- "flags.loopback = 1; "
- "output;",
- svc_monitor_mac, svc_monitor_mac,
- backend_nb->svc_mon_src_ip);
+ if (IN6_IS_ADDR_V4MAPPED(&lb_vip->vip)) {
+ ds_put_format(match, "arp.tpa == %s && arp.op == 1",
+ backend_nb->svc_mon_src_ip);
+ ds_put_format(actions,
+ "eth.dst = eth.src; "
+ "eth.src = %s; "
+ "arp.op = 2; /* ARP reply */ "
+ "arp.tha = arp.sha; "
+ "arp.sha = %s; "
+ "arp.tpa = arp.spa; "
+ "arp.spa = %s; "
+ "outport = inport; "
+ "flags.loopback = 1; "
+ "output;",
+ svc_monitor_mac, svc_monitor_mac,
+ backend_nb->svc_mon_src_ip);
+ } else {
+ ds_put_format(match, "nd_ns && nd.target == %s",
+ backend_nb->svc_mon_src_ip);
+ ds_put_format(actions,
+ "nd_na { "
+ "eth.dst = eth.src; "
+ "eth.src = %s; "
+ "ip6.src = %s; "
+ "nd.target = %s; "
+ "nd.tll = %s; "
+ "outport = inport; "
+ "flags.loopback = 1; "
+ "output; "
+ "};",
+ svc_monitor_mac,
+ backend_nb->svc_mon_src_ip,
+ backend_nb->svc_mon_src_ip,
+ svc_monitor_mac);
+ }
ovn_lflow_add_with_hint(lflows,
backend_nb->op->od,
S_SWITCH_IN_ARP_ND_RSP, 110,
diff --git a/northd/ovn-northd.8.xml b/northd/ovn-northd.8.xml
index 4b712cec4..d9439ea78 100644
--- a/northd/ovn-northd.8.xml
+++ b/northd/ovn-northd.8.xml
@@ -1418,6 +1418,23 @@ output;
These flows are required if an ARP request is sent for the IP
SVC_MON_SRC_IP.
+
+
+ For IPv6 the similar flow is added with the following action
+
+
+
+nd_na {
+ eth.dst = eth.src;
+ eth.src = E;
+ ip6.src = A;
+ nd.target = A;
+ nd.tll = E;
+ outport = inport;
+ flags.loopback = 1;
+ output;
+};
+
diff --git a/ovn-nb.xml b/ovn-nb.xml
index 0a4340529..e9340131c 100644
--- a/ovn-nb.xml
+++ b/ovn-nb.xml
@@ -1793,9 +1793,8 @@
- OVN supports health checks for load balancer endpoints, for IPv4 load
- balancers only. When health checks are enabled, the load balancer uses
- only healthy endpoints.
+ OVN supports health checks for load balancer endpoints. When health
+ checks are enabled, the load balancer uses only healthy endpoints.
@@ -1941,8 +1940,7 @@
- Each row represents one load balancer health check. Health checks
- are supported for IPv4 load balancers only.
+ Each row represents one load balancer health check.
diff --git a/tests/ovn.at b/tests/ovn.at
index dde0f582b..73d7eeaa6 100644
--- a/tests/ovn.at
+++ b/tests/ovn.at
@@ -23625,7 +23625,7 @@ AT_CLEANUP
])
OVN_FOR_EACH_NORTHD([
-AT_SETUP([Load balancer health checks])
+AT_SETUP([Load balancer health checks - IPv4])
AT_KEYWORDS([lb])
ovn_start
@@ -23823,6 +23823,205 @@ OVN_CLEANUP([hv1], [hv2])
AT_CLEANUP
])
+OVN_FOR_EACH_NORTHD([
+AT_SETUP([Load balancer health checks - IPv6])
+AT_KEYWORDS([lb])
+ovn_start
+
+net_add n1
+
+sim_add hv1
+as hv1
+ovs-vsctl add-br br-phys
+ovn_attach n1 br-phys 192.168.0.1
+check ovs-vsctl -- add-port br-int hv1-vif1 -- \
+ set interface hv1-vif1 external-ids:iface-id=sw0-p1 \
+ options:tx_pcap=hv1/vif1-tx.pcap \
+ options:rxq_pcap=hv1/vif1-rx.pcap \
+ ofport-request=1
+check ovs-vsctl -- add-port br-int hv1-vif2 -- \
+ set interface hv1-vif2 external-ids:iface-id=sw0-p2 \
+ options:tx_pcap=hv1/vif2-tx.pcap \
+ options:rxq_pcap=hv1/vif2-rx.pcap \
+ ofport-request=2
+
+sim_add hv2
+as hv2
+check ovs-vsctl add-br br-phys
+ovn_attach n1 br-phys 192.168.0.2
+check ovs-vsctl -- add-port br-int hv2-vif1 -- \
+ set interface hv2-vif1 external-ids:iface-id=sw1-p1 \
+ options:tx_pcap=hv2/vif1-tx.pcap \
+ options:rxq_pcap=hv2/vif1-rx.pcap \
+ ofport-request=1
+
+check ovn-nbctl ls-add sw0
+
+check ovn-nbctl lsp-add sw0 sw0-p1
+check ovn-nbctl lsp-set-addresses sw0-p1 "50:54:00:00:00:03 2001::3"
+check ovn-nbctl lsp-set-port-security sw0-p1 "50:54:00:00:00:03 2001::3"
+
+# Create port group and ACLs for sw0 ports.
+check ovn-nbctl pg-add pg0_drop sw0-p1
+check ovn-nbctl acl-add pg0_drop from-lport 1001 "inport == @pg0_drop && ip" drop
+check ovn-nbctl acl-add pg0_drop to-lport 1001 "outport == @pg0_drop && ip" drop
+
+# Create the second logical switch with one port
+check ovn-nbctl ls-add sw1
+check ovn-nbctl lsp-add sw1 sw1-p1
+check ovn-nbctl lsp-set-addresses sw1-p1 "40:54:00:00:00:03 2002::3"
+check ovn-nbctl lsp-set-port-security sw1-p1 "40:54:00:00:00:03 2002::3"
+
+# Create port group and ACLs for sw1 ports.
+check ovn-nbctl pg-add pg1_drop sw1-p1
+check ovn-nbctl acl-add pg1_drop from-lport 1001 "inport == @pg1_drop && ip" drop
+check ovn-nbctl acl-add pg1_drop to-lport 1001 "outport == @pg1_drop && ip" drop
+
+check ovn-nbctl pg-add pg1 sw1-p1
+check ovn-nbctl acl-add pg1 from-lport 1002 "inport == @pg1 && ip6" allow-related
+check ovn-nbctl acl-add pg1 to-lport 1002 "outport == @pg1 && ip6 && ip6.src == ::/0 && icmp6" allow-related
+check ovn-nbctl acl-add pg1 to-lport 1002 "outport == @pg1 && ip6 && ip6.src == ::/0 && tcp && tcp.dst == 80" allow-related
+check ovn-nbctl acl-add pg1 to-lport 1002 "outport == @pg1 && ip6 && ip6.src == ::/0 && udp && udp.dst == 80" allow-related
+
+# Create a logical router and attach both logical switches
+check ovn-nbctl lr-add lr0
+check ovn-nbctl lrp-add lr0 lr0-sw0 00:00:00:00:ff:01 2001::1/64
+check ovn-nbctl lsp-add sw0 sw0-lr0
+check ovn-nbctl lsp-set-type sw0-lr0 router
+check ovn-nbctl lsp-set-addresses sw0-lr0 router
+check ovn-nbctl lsp-set-options sw0-lr0 router-port=lr0-sw0
+
+check ovn-nbctl lrp-add lr0 lr0-sw1 00:00:00:00:ff:02 2001::a/64
+check ovn-nbctl lsp-add sw1 sw1-lr0
+check ovn-nbctl lsp-set-type sw1-lr0 router
+check ovn-nbctl lsp-set-addresses sw1-lr0 router
+check ovn-nbctl lsp-set-options sw1-lr0 router-port=lr0-sw1
+
+check ovn-nbctl lb-add lb1 [[2001::a]]:80 [[2001::3]]:80,[[2002::3]]:80
+OVN_LB_ID=$(ovn-nbctl --bare --column _uuid find load_balancer name=lb1)
+check ovn-nbctl set load_balancer ${OVN_LB_ID} selection_fields="ip_dst,ip_src,tp_dst,tp_src"
+#
+check ovn-nbctl --wait=sb set load_balancer . ip_port_mappings:\"[[2001::3]]\"=\"sw0-p1:[[2001::2]]\"
+check ovn-nbctl --wait=sb set load_balancer . ip_port_mappings:\"[[2002::3]]\"=\"sw1-p1:[[2002::2]]\"
+
+AT_CHECK([ovn-nbctl --wait=sb \
+ -- --id=@hc create Load_Balancer_Health_Check vip="\[\[2001\:\:a\]\]\:80" \
+ options:failure_count=100 \
+ -- add Load_Balancer . health_check @hc | uuidfilt], [0], [<0>
+])
+
+check ovn-nbctl --wait=sb ls-lb-add sw0 lb1
+check ovn-nbctl --wait=sb ls-lb-add sw1 lb1
+check ovn-nbctl --wait=sb lr-lb-add lr0 lb1
+
+check ovn-nbctl ls-add public
+check ovn-nbctl lrp-add lr0 lr0-public 00:00:20:20:12:13 2003::1/64
+check ovn-nbctl lsp-add public public-lr0
+check ovn-nbctl lsp-set-type public-lr0 router
+check ovn-nbctl lsp-set-addresses public-lr0 router
+check ovn-nbctl lsp-set-options public-lr0 router-port=lr0-public
+
+# localnet port
+check ovn-nbctl lsp-add public ln-public
+check ovn-nbctl lsp-set-type ln-public localnet
+check ovn-nbctl lsp-set-addresses ln-public unknown
+check ovn-nbctl lsp-set-options ln-public network_name=public
+
+# schedule the gw router port to a chassis. Change the name of the chassis
+check ovn-nbctl --wait=hv lrp-set-gateway-chassis lr0-public hv1 20
+
+OVN_POPULATE_ARP
+wait_for_ports_up
+check ovn-nbctl --wait=hv sync
+
+wait_row_count Service_Monitor 2
+
+AT_CAPTURE_FILE([sbflows])
+OVS_WAIT_FOR_OUTPUT(
+ [ovn-sbctl dump-flows > sbflows
+ ovn-sbctl dump-flows sw0 | grep ct_lb_mark | grep priority=120 | sed 's/table=..//'], 0,
+ [dnl
+ (ls_in_pre_stateful ), priority=120 , match=(ip6.dst == 2001::a && tcp.dst == 80), action=(xxreg1 = 2001::a; reg2[[0..15]] = 80; ct_lb_mark;)
+ (ls_in_lb ), priority=120 , match=(ct.new && ip6.dst == 2001::a && tcp.dst == 80), action=(reg0[[1]] = 0; ct_lb_mark(backends=[[2001::3]]:80,[[2002::3]]:80; hash_fields="ip_dst,ip_src,tcp_dst,tcp_src");)
+])
+
+AT_CAPTURE_FILE([sbflows2])
+OVS_WAIT_FOR_OUTPUT(
+ [ovn-sbctl dump-flows > sbflows2
+ ovn-sbctl dump-flows lr0 | grep ct_lb_mark | grep priority=120 | sed 's/table=..//'], 0,
+ [ (lr_in_dnat ), priority=120 , match=(ct.new && ip6 && xxreg0 == 2001::a && tcp && reg9[[16..31]] == 80 && is_chassis_resident("cr-lr0-public")), action=(ct_lb_mark(backends=[[2001::3]]:80,[[2002::3]]:80; hash_fields="ip_dst,ip_src,tcp_dst,tcp_src");)
+])
+
+# get the svc monitor mac.
+svc_mon_src_mac=`ovn-nbctl get NB_Global . options:svc_monitor_mac | \
+sed s/":"//g | sed s/\"//g`
+
+OVS_WAIT_UNTIL(
+ [test 1 = `$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif1-tx.pcap | \
+grep "505400000003${svc_mon_src_mac}" | wc -l`]
+)
+
+OVS_WAIT_UNTIL(
+ [test 1 = `$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv2/vif1-tx.pcap | \
+grep "405400000003${svc_mon_src_mac}" | wc -l`]
+)
+
+check ovn-nbctl set load_balancer_health_check [[2001::a]]:80 options:failure_count=1
+wait_row_count Service_Monitor 2 status=offline
+
+OVS_WAIT_UNTIL(
+ [test 2 = `$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif1-tx.pcap | \
+grep "505400000003${svc_mon_src_mac}" | wc -l`]
+)
+
+OVS_WAIT_UNTIL(
+ [test 2 = `$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv2/vif1-tx.pcap | \
+grep "405400000003${svc_mon_src_mac}" | wc -l`]
+)
+
+AT_CAPTURE_FILE([sbflows3])
+ovn-sbctl dump-flows sw0 > sbflows3
+AT_CHECK(
+ [grep "ip6.dst == 2001::a && tcp.dst == 80" sbflows3 | grep priority=120 |\
+ sed 's/table=../table=??/'], [0], [dnl
+ table=??(ls_in_pre_stateful ), priority=120 , match=(ip6.dst == 2001::a && tcp.dst == 80), action=(xxreg1 = 2001::a; reg2[[0..15]] = 80; ct_lb_mark;)
+ table=??(ls_in_lb ), priority=120 , match=(ct.new && ip6.dst == 2001::a && tcp.dst == 80), action=(drop;)
+])
+
+AT_CAPTURE_FILE([sbflows4])
+ovn-sbctl dump-flows lr0 > sbflows4
+AT_CHECK([grep lr_in_dnat sbflows4 | grep priority=120 | sed 's/table=..//' | sort], [0], [dnl
+ (lr_in_dnat ), priority=120 , match=(ct.est && ip6 && xxreg0 == 2001::a && tcp && reg9[[16..31]] == 80 && ct_mark.natted == 1 && is_chassis_resident("cr-lr0-public")), action=(next;)
+ (lr_in_dnat ), priority=120 , match=(ct.new && ip6 && xxreg0 == 2001::a && tcp && reg9[[16..31]] == 80 && is_chassis_resident("cr-lr0-public")), action=(drop;)
+])
+
+# Delete sw0-p1
+check ovn-nbctl lsp-del sw0-p1
+
+wait_row_count Service_Monitor 1
+
+# Add back sw0-p1 but without any IP address.
+check ovn-nbctl lsp-add sw0 sw0-p1
+check ovn-nbctl lsp-set-addresses sw0-p1 "50:54:00:00:00:03" -- \
+ lsp-set-port-security sw0-p1 "50:54:00:00:00:03"
+
+wait_row_count Service_Monitor 2 status=offline
+
+check ovn-nbctl lsp-del sw0-p1
+check ovn-nbctl lsp-del sw1-p1
+wait_row_count Service_Monitor 0
+
+# Add back sw0-p1 but without any address set.
+check ovn-nbctl lsp-add sw0 sw0-p1
+
+wait_row_count Service_Monitor 1
+wait_row_count Service_Monitor 0 status=offline
+wait_row_count Service_Monitor 0 status=online
+
+OVN_CLEANUP([hv1], [hv2])
+AT_CLEANUP
+])
+
OVN_FOR_EACH_NORTHD([
AT_SETUP([SCTP Load balancer health checks])
AT_KEYWORDS([lb sctp])
diff --git a/tests/system-ovn.at b/tests/system-ovn.at
index cb3412717..358929522 100644
--- a/tests/system-ovn.at
+++ b/tests/system-ovn.at
@@ -4388,7 +4388,7 @@ AT_CLEANUP
])
OVN_FOR_EACH_NORTHD([
-AT_SETUP([Load balancer health checks])
+AT_SETUP([Load balancer health checks - IPv4])
AT_KEYWORDS([lb])
ovn_start
@@ -4615,6 +4615,234 @@ OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d
AT_CLEANUP
])
+OVN_FOR_EACH_NORTHD([
+AT_SETUP([Load balancer health checks - IPv6])
+AT_KEYWORDS([lb])
+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
+
+ovn-nbctl ls-add sw0
+
+ovn-nbctl lsp-add sw0 sw0-p1
+ovn-nbctl lsp-set-addresses sw0-p1 "50:54:00:00:00:03 2001::3"
+ovn-nbctl lsp-set-port-security sw0-p1 "50:54:00:00:00:03 2001::3"
+
+ovn-nbctl lsp-add sw0 sw0-p2
+ovn-nbctl lsp-set-addresses sw0-p2 "50:54:00:00:00:04 2001::4"
+ovn-nbctl lsp-set-port-security sw0-p2 "50:54:00:00:00:04 2001::4"
+
+# Create port group and ACLs for sw0 ports.
+ovn-nbctl pg-add pg0_drop sw0-p1 sw0-p2
+ovn-nbctl acl-add pg0_drop from-lport 1001 "inport == @pg0_drop && ip" drop
+ovn-nbctl acl-add pg0_drop to-lport 1001 "outport == @pg0_drop && ip" drop
+
+ovn-nbctl pg-add pg0 sw0-p1 sw0-p2
+ovn-nbctl acl-add pg0 from-lport 1002 "inport == @pg0 && ip6" allow-related
+ovn-nbctl acl-add pg0 to-lport 1002 "outport == @pg0 && ip6 && ip6.src == ::/0 && icmp6" allow-related
+ovn-nbctl acl-add pg0 to-lport 1002 "outport == @pg0 && ip6 && ip6.src == ::/0 && tcp && tcp.dst == 80" allow-related
+ovn-nbctl acl-add pg0 to-lport 1002 "outport == @pg0 && ip6 && ip6.src == ::/0 && udp && udp.dst == 80" allow-related
+
+# Create the second logical switch with one port
+ovn-nbctl ls-add sw1
+ovn-nbctl lsp-add sw1 sw1-p1
+ovn-nbctl lsp-set-addresses sw1-p1 "40:54:00:00:00:03 2002::3"
+ovn-nbctl lsp-set-port-security sw1-p1 "40:54:00:00:00:03 2002::3"
+
+# Create port group and ACLs for sw1 ports.
+ovn-nbctl pg-add pg1_drop sw1-p1
+ovn-nbctl acl-add pg1_drop from-lport 1001 "inport == @pg1_drop && ip" drop
+ovn-nbctl acl-add pg1_drop to-lport 1001 "outport == @pg1_drop && ip" drop
+
+ovn-nbctl pg-add pg1 sw1-p1
+ovn-nbctl acl-add pg1 from-lport 1002 "inport == @pg1 && ip6" allow-related
+ovn-nbctl acl-add pg1 to-lport 1002 "outport == @pg1 && ip6 && ip6.src == ::/0 && icmp6" allow-related
+ovn-nbctl acl-add pg1 to-lport 1002 "outport == @pg1 && ip6 && ip6.src == ::/0 && tcp && tcp.dst == 80" allow-related
+ovn-nbctl acl-add pg1 to-lport 1002 "outport == @pg1 && ip6 && ip6.src == ::/0 && udp && udp.dst == 80" allow-related
+
+# Create a logical router and attach both logical switches
+ovn-nbctl lr-add lr0
+ovn-nbctl lrp-add lr0 lr0-sw0 00:00:00:00:ff:01 2001::1/64
+ovn-nbctl lsp-add sw0 sw0-lr0
+ovn-nbctl lsp-set-type sw0-lr0 router
+ovn-nbctl lsp-set-addresses sw0-lr0 router
+ovn-nbctl lsp-set-options sw0-lr0 router-port=lr0-sw0
+
+ovn-nbctl lrp-add lr0 lr0-sw1 00:00:00:00:ff:02 2002::1/64
+ovn-nbctl lsp-add sw1 sw1-lr0
+ovn-nbctl lsp-set-type sw1-lr0 router
+ovn-nbctl lsp-set-addresses sw1-lr0 router
+ovn-nbctl lsp-set-options sw1-lr0 router-port=lr0-sw1
+
+ovn-nbctl --reject lb-add lb1 [[2001::a]]:80 [[2001::3]]:80,[[2002::3]]:80
+
+check ovn-nbctl --wait=sb set load_balancer . ip_port_mappings:\"[[2001::3]]\"=\"sw0-p1:[[2001::2]]\"
+check ovn-nbctl --wait=sb set load_balancer . ip_port_mappings:\"[[2002::3]]\"=\"sw1-p1:[[2002::2]]\"
+
+ovn-nbctl --wait=sb -- --id=@hc create \
+Load_Balancer_Health_Check vip="\[\[2001\:\:a\]\]\:80" -- add Load_Balancer . \
+health_check @hc
+
+ovn-nbctl --wait=sb ls-lb-add sw0 lb1
+ovn-nbctl --wait=sb ls-lb-add sw1 lb1
+ovn-nbctl --wait=sb lr-lb-add lr0 lb1
+
+OVN_POPULATE_ARP
+ovn-nbctl --wait=hv sync
+
+ADD_NAMESPACES(sw0-p1)
+ADD_VETH(sw0-p1, sw0-p1, br-int, "2001::3/64", "50:54:00:00:00:03", \
+ "2001::1")
+
+ADD_NAMESPACES(sw1-p1)
+ADD_VETH(sw1-p1, sw1-p1, br-int, "2002::3/64", "40:54:00:00:00:03", \
+ "2002::1")
+
+ADD_NAMESPACES(sw0-p2)
+ADD_VETH(sw0-p2, sw0-p2, br-int, "2001::4/64", "50:54:00:00:00:04", \
+ "2001::1")
+
+# Wait until all the services are set to offline.
+OVS_WAIT_UNTIL([test 2 = `ovn-sbctl --bare --columns status find \
+service_monitor | sed '/^$/d' | grep offline | wc -l`])
+
+# Start webservers in 'sw0-p1' and 'sw1-p1'.
+OVS_START_L7([sw0-p1], [http6])
+sw0_p1_pid_file=$(cat l7_pid_file)
+OVS_START_L7([sw1-p1], [http6])
+
+# Wait until the services are set to online.
+OVS_WAIT_UNTIL([test 2 = `ovn-sbctl --bare --columns status find \
+service_monitor | sed '/^$/d' | grep online | wc -l`])
+
+OVS_WAIT_UNTIL(
+ [ovn-sbctl dump-flows sw0 | grep ct_lb_mark | grep priority=120 | grep "ip6.dst == 2001::a" > lflows.txt
+ test 1 = `cat lflows.txt | grep "ct_lb_mark(backends=[\[2001::3\]]:80,[\[2002::3\]]:80)" | wc -l`]
+)
+
+# From sw0-p2 send traffic to vip - 2001::a
+for i in `seq 1 20`; do
+ echo Request $i
+ ovn-sbctl list service_monitor
+ NS_CHECK_EXEC([sw0-p2], [wget http://[[2001::a]] -t 5 -T 1 --retry-connrefused -v -o wget$i.log])
+done
+
+dnl Each server should have at least one connection.
+AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(2001::a) | grep -v fe80 | \
+sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl
+tcp,orig=(src=2001::4,dst=2001::a,sport=,dport=),reply=(src=2001::3,dst=2001::4,sport=,dport=),zone=,mark=2,protoinfo=(state=)
+tcp,orig=(src=2001::4,dst=2001::a,sport=,dport=),reply=(src=2002::3,dst=2001::4,sport=,dport=),zone=,mark=2,protoinfo=(state=)
+])
+
+# Stop webserver in sw0-p1
+kill `cat $sw0_p1_pid_file`
+
+# Wait until service_monitor for sw0-p1 is set to offline
+OVS_WAIT_UNTIL([test 1 = `ovn-sbctl --bare --columns status find \
+service_monitor logical_port=sw0-p1 | sed '/^$/d' | grep offline | wc -l`])
+
+OVS_WAIT_UNTIL(
+ [ovn-sbctl dump-flows sw0 | grep ct_lb_mark | grep priority=120 | grep "ip6.dst == 2001::a" > lflows.txt
+ test 1 = `cat lflows.txt | grep "ct_lb_mark(backends=[\[2002::3\]]:80)" | wc -l`]
+)
+
+ovs-appctl dpctl/flush-conntrack
+# From sw0-p2 send traffic to vip - 2001::a
+for i in `seq 1 20`; do
+ echo Request $i
+ NS_CHECK_EXEC([sw0-p2], [wget http://[[2001::a]] -t 5 -T 1 --retry-connrefused -v -o wget$i.log])
+done
+
+AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(2001::a) | grep -v fe80 | \
+sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl
+tcp,orig=(src=2001::4,dst=2001::a,sport=,dport=),reply=(src=2002::3,dst=2001::4,sport=,dport=),zone=,mark=2,protoinfo=(state=)
+])
+
+# trigger port binding release and check if status changed to offline
+ovs-vsctl remove interface ovs-sw1-p1 external_ids iface-id
+wait_row_count Service_Monitor 2
+wait_row_count Service_Monitor 2 status=offline
+
+ovs-vsctl set interface ovs-sw1-p1 external_ids:iface-id=sw1-p1
+wait_row_count Service_Monitor 2
+wait_row_count Service_Monitor 1 status=online
+
+# Create udp load balancer.
+#ovn-nbctl lb-add lb2 10.0.0.10:80 10.0.0.3:80,20.0.0.3:80 udp
+#lb_udp=`ovn-nbctl lb-list | grep udp | awk '{print $1}'`
+#
+#echo "lb udp uuid = $lb_udp"
+#
+#ovn-nbctl list load_balancer
+#
+#ovn-nbctl --wait=sb set load_balancer $lb_udp ip_port_mappings:10.0.0.3=sw0-p1:10.0.0.2
+#ovn-nbctl --wait=sb set load_balancer $lb_udp ip_port_mappings:20.0.0.3=sw1-p1:20.0.0.2
+#
+#ovn-nbctl --wait=sb -- --id=@hc create \
+#Load_Balancer_Health_Check vip="10.0.0.10\:80" -- add Load_Balancer $lb_udp \
+#health_check @hc
+#
+#ovn-nbctl --wait=sb ls-lb-add sw0 lb2
+#ovn-nbctl --wait=sb ls-lb-add sw1 lb2
+#ovn-nbctl --wait=sb lr-lb-add lr0 lb2
+#
+#sleep 10
+#
+#ovn-nbctl list load_balancer
+#echo "*******Next is health check*******"
+#ovn-nbctl list Load_Balancer_Health_Check
+#echo "********************"
+#ovn-sbctl list service_monitor
+#
+## Wait until udp service_monitor are set to offline
+#OVS_WAIT_UNTIL([test 2 = `ovn-sbctl --bare --columns status find \
+#service_monitor protocol=udp | sed '/^$/d' | grep offline | wc -l`])
+#
+## Stop webserver in sw1-p1
+#pid_file=$(cat l7_pid_file)
+#NS_CHECK_EXEC([sw1-p1], [kill $(cat $pid_file)])
+#
+#NS_CHECK_EXEC([sw0-p2], [tcpdump -c 1 -neei sw0-p2 ip[[33:1]]=0x14 > rst.pcap &])
+#OVS_WAIT_UNTIL([test 2 = `ovn-sbctl --bare --columns status find \
+#service_monitor protocol=tcp | sed '/^$/d' | grep offline | wc -l`])
+#NS_CHECK_EXEC([sw0-p2], [wget 10.0.0.10 -v -o wget$i.log],[4])
+#
+#OVS_WAIT_UNTIL([
+# n_reset=$(cat rst.pcap | wc -l)
+# test "${n_reset}" = "1"
+#])
+
+OVS_APP_EXIT_AND_WAIT([ovn-controller])
+
+as ovn-sb
+OVS_APP_EXIT_AND_WAIT([ovsdb-server])
+
+as ovn-nb
+OVS_APP_EXIT_AND_WAIT([ovsdb-server])
+
+as northd
+OVS_APP_EXIT_AND_WAIT([NORTHD_TYPE])
+
+as
+OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d
+/connection dropped.*/d
+/Service monitor not found.*/d"])
+
+AT_CLEANUP
+])
+
OVN_FOR_EACH_NORTHD([
AT_SETUP([Load Balancer LS hairpin IPv4])
AT_SKIP_IF([test $HAVE_NC = no])