Message ID | 7885b0f383870557c415ced70efdbe6dc168beb3.1676027700.git.lorenzo.bianconi@redhat.com |
---|---|
State | Changes Requested |
Headers | show |
Series | [ovs-dev,v3] Add IPv6 support for lb health-check | expand |
Context | Check | Description |
---|---|---|
ovsrobot/apply-robot | success | apply and check: success |
ovsrobot/github-robot-_Build_and_Test | success | github build: passed |
ovsrobot/github-robot-_ovn-kubernetes | success | github build: passed |
On Fri, Feb 10, 2023 at 12:18 PM Lorenzo Bianconi < lorenzo.bianconi@redhat.com> wrote: > Add Similar to IPv4 counterpart, introduce IPv6 load-balancer health > check support. > > Reported-at: https://bugzilla.redhat.com/show_bug.cgi?id=2136094 > Acked-by: Mark Michelson <mmichels@redhat.com> > Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com> > --- > Changes since v2: > - cosmetics > > Changes since v1: > - fix potential crash in ovn-northd > - improve documentation > --- > controller/pinctrl.c | 216 ++++++++++++++++++++++++------------- > northd/northd.c | 74 +++++++++---- > northd/ovn-northd.8.xml | 17 +++ > ovn-nb.xml | 21 ++-- > tests/ovn.at | 201 ++++++++++++++++++++++++++++++++++- > tests/system-ovn.at | 230 +++++++++++++++++++++++++++++++++++++++- > 6 files changed, 656 insertions(+), 103 deletions(-) > > diff --git a/controller/pinctrl.c b/controller/pinctrl.c > index ffceb7e5f..f8ebc3a9e 100644 > --- a/controller/pinctrl.c > +++ b/controller/pinctrl.c > @@ -6736,9 +6736,10 @@ sync_svc_monitors(struct ovsdb_idl_txn > *ovnsb_idl_txn, > > struct in6_addr ip_addr; > ovs_be32 ip4; > - if (ip_parse(sb_svc_mon->ip, &ip4)) { > + bool is_ipv4 = ip_parse(sb_svc_mon->ip, &ip4); > + if (is_ipv4) { > ip_addr = in6_addr_mapped_ipv4(ip4); > - } else { > + } else if (!ipv6_parse(sb_svc_mon->ip, &ip_addr)) { > continue; > } > > @@ -6751,16 +6752,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 (is_ipv4) { > + for (size_t j = 0; j < laddrs.n_ipv4_addrs; j++) { > + if (ip4 == laddrs.ipv4_addrs[j].addr) { > + ea = laddrs.ea; > + mac_found = true; > + break; > + } > + } > + } else { > + for (size_t j = 0; j < laddrs.n_ipv6_addrs; j++) { > + if (IN6_ARE_ADDR_EQUAL(&ip_addr, > + &laddrs.ipv6_addrs[j].addr)) { > + ea = laddrs.ea; > + mac_found = true; > + break; > + } > } > } > > - if (!mac_found && !laddrs.n_ipv4_addrs) { > - /* 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; > } > @@ -6794,7 +6806,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 = !is_ipv4; > svc_mon->state = SVC_MON_S_INIT; > svc_mon->status = SVC_MON_ST_UNKNOWN; > svc_mon->protocol = protocol; > @@ -7562,26 +7574,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; > > @@ -7592,7 +7608,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))); > @@ -7627,21 +7647,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); > @@ -7649,6 +7674,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); > @@ -7711,6 +7746,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); > @@ -7841,32 +7877,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))); > @@ -7883,44 +7925,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 77e105b86..51a9d92cb 100644 > --- a/northd/northd.c > +++ b/northd/northd.c > @@ -3809,8 +3809,13 @@ 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, ipv6 ? "[%s]" : "%s", backend->ip_str); > + > 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, ":"); > @@ -3818,10 +3823,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); > - svc_mon_src_ip = xstrdup(p); > + if (ipv6) { > + char *t, *q = strstr(p, "["); > + p = NULL; > + if (q && (t = strstr(q + 1, "]"))) { > + p = q + 1; > + *t = 0; > + } > + } > + if (p) { > + 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; > @@ -3902,7 +3918,8 @@ build_lb_vip_actions(struct ovn_lb_vip *lb_vip, > } > > n_active_backends++; > - ds_put_format(action, "%s:%"PRIu16",", > + bool ipv6 = !IN6_IS_ADDR_V4MAPPED(&backend->ip); > + ds_put_format(action, ipv6 ? "[%s]:%"PRIu16"," : > "%s:%"PRIu16",", > backend->ip_str, backend->port); > } > ds_chomp(action, ','); > @@ -8752,6 +8769,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]; > @@ -8760,22 +8778,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 3d7a92ea8..2eab2c4ae 100644 > --- a/northd/ovn-northd.8.xml > +++ b/northd/ovn-northd.8.xml > @@ -1469,6 +1469,23 @@ output; > These flows are required if an ARP request is sent for the IP > <var>SVC_MON_SRC_IP</var>. > </p> > + > + <p> > + For IPv6 the similar flow is added with the following action > + </p> > + > + <pre> > +nd_na { > + eth.dst = eth.src; > + eth.src = <var>E</var>; > + ip6.src = <var>A</var>; > + nd.target = <var>A</var>; > + nd.tll = <var>E</var>; > + outport = inport; > + flags.loopback = 1; > + output; > +}; > + </pre> > </li> > > <li> > diff --git a/ovn-nb.xml b/ovn-nb.xml > index 4b52b9953..8d56d0c6e 100644 > --- a/ovn-nb.xml > +++ b/ovn-nb.xml > @@ -1847,9 +1847,8 @@ > > <group title="Health Checks"> > <p> > - 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. > </p> > > <p> > @@ -1861,7 +1860,7 @@ > column="health_check"/> a reference to a <ref > table="Load_Balancer_Health_Check"/> row whose <ref > table="Load_Balancer_Health_Check" column="vip"/> is set to > - <code>10.0.0.10</code>. > + <code>10.0.0.10</code>. The same approach can be used for IPv6 as > well. > </p> > > <column name="health_check"> > @@ -1872,8 +1871,10 @@ > <p> > Maps from endpoint IP to a colon-separated pair of logical port > name > and source IP, > - e.g. <code><var>port_name</var>:<var>sourc_ip</var></code>. > Health > - checks are sent to this port with the specified source IP. > + e.g. <code><var>port_name</var>:<var>sourc_ip</var></code> for > IPv4. > + Health checks are sent to this port with the specified source > IP. > + For IPv6 square brackets must be used around IP address, e.g: > + <code><var>port_name</var>:<var>[sourc_ip]</var></code> > </p> > > <p> > @@ -1882,6 +1883,11 @@ > <code>20.0.0.4</code>=<code>sw1-p1:20.0.0.2</code>, if the > values > given were suitable ports and IP addresses. > </p> > + > + <p> > + For IPv6 IP to port mappings might be defined as > + <code>[2001::1]</code>=<code>sw0-p1:[2002::1]</code>. > + </p> > </column> > </group> > > @@ -2055,8 +2061,7 @@ or > > <table name="Load_Balancer_Health_Check" title="load balancer"> > <p> > - 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. > </p> > > <column name="vip"> > diff --git a/tests/ovn.at b/tests/ovn.at > index e9b8bc677..8854030a4 100644 > --- a/tests/ovn.at > +++ b/tests/ovn.at > @@ -24243,7 +24243,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 > > @@ -24441,6 +24441,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=(reg0[[2]] == 1 && 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 && !ct.rel && > 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=(reg0[[2]] == 1 && > 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 && !ct.rel && 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 && !ct.rel && 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 2ece0f571..7fa899edd 100644 > --- a/tests/system-ovn.at > +++ b/tests/system-ovn.at > @@ -4378,7 +4378,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 > > @@ -4603,6 +4603,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=<cleared>/'], [0], [dnl > > +tcp,orig=(src=2001::4,dst=2001::a,sport=<cleared>,dport=<cleared>),reply=(src=2001::3,dst=2001::4,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=2,protoinfo=(state=<cleared>) > > +tcp,orig=(src=2001::4,dst=2001::a,sport=<cleared>,dport=<cleared>),reply=(src=2002::3,dst=2001::4,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=2,protoinfo=(state=<cleared>) > +]) > + > +# 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=<cleared>/'], [0], [dnl > > +tcp,orig=(src=2001::4,dst=2001::a,sport=<cleared>,dport=<cleared>),reply=(src=2002::3,dst=2001::4,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=2,protoinfo=(state=<cleared>) > +]) > + > +# 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]) > -- > 2.39.1 > > Looks good to me, thanks. Reviewed-by: Ales Musil <amusil@redhat.com>
On 2/10/23 12:18, Lorenzo Bianconi wrote: > Add Similar to IPv4 counterpart, introduce IPv6 load-balancer health > check support. > > Reported-at: https://bugzilla.redhat.com/show_bug.cgi?id=2136094 > Acked-by: Mark Michelson <mmichels@redhat.com> > Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com> > --- > Changes since v2: > - cosmetics > > Changes since v1: > - fix potential crash in ovn-northd > - improve documentation > --- Hi Lorenzo, Thanks for this revision; I have a few comments below. > controller/pinctrl.c | 216 ++++++++++++++++++++++++------------- > northd/northd.c | 74 +++++++++---- > northd/ovn-northd.8.xml | 17 +++ > ovn-nb.xml | 21 ++-- > tests/ovn.at | 201 ++++++++++++++++++++++++++++++++++- > tests/system-ovn.at | 230 +++++++++++++++++++++++++++++++++++++++- > 6 files changed, 656 insertions(+), 103 deletions(-) > > diff --git a/controller/pinctrl.c b/controller/pinctrl.c > index ffceb7e5f..f8ebc3a9e 100644 > --- a/controller/pinctrl.c > +++ b/controller/pinctrl.c > @@ -6736,9 +6736,10 @@ sync_svc_monitors(struct ovsdb_idl_txn *ovnsb_idl_txn, > > struct in6_addr ip_addr; > ovs_be32 ip4; > - if (ip_parse(sb_svc_mon->ip, &ip4)) { > + bool is_ipv4 = ip_parse(sb_svc_mon->ip, &ip4); > + if (is_ipv4) { > ip_addr = in6_addr_mapped_ipv4(ip4); > - } else { > + } else if (!ipv6_parse(sb_svc_mon->ip, &ip_addr)) { > continue; > } > > @@ -6751,16 +6752,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 (is_ipv4) { > + for (size_t j = 0; j < laddrs.n_ipv4_addrs; j++) { > + if (ip4 == laddrs.ipv4_addrs[j].addr) { > + ea = laddrs.ea; > + mac_found = true; > + break; > + } > + } > + } else { > + for (size_t j = 0; j < laddrs.n_ipv6_addrs; j++) { > + if (IN6_ARE_ADDR_EQUAL(&ip_addr, > + &laddrs.ipv6_addrs[j].addr)) { > + ea = laddrs.ea; > + mac_found = true; > + break; > + } > } > } > > - if (!mac_found && !laddrs.n_ipv4_addrs) { > - /* 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; > } > @@ -6794,7 +6806,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 = !is_ipv4; > svc_mon->state = SVC_MON_S_INIT; > svc_mon->status = SVC_MON_ST_UNKNOWN; > svc_mon->protocol = protocol; > @@ -7562,26 +7574,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; > > @@ -7592,7 +7608,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))); > @@ -7627,21 +7647,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); > @@ -7649,6 +7674,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); > @@ -7711,6 +7746,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); > @@ -7841,32 +7877,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))); > @@ -7883,44 +7925,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 77e105b86..51a9d92cb 100644 > --- a/northd/northd.c > +++ b/northd/northd.c > @@ -3809,8 +3809,13 @@ 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, ipv6 ? "[%s]" : "%s", backend->ip_str); > + > 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, ":"); > @@ -3818,10 +3823,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); > - svc_mon_src_ip = xstrdup(p); > + if (ipv6) { > + char *t, *q = strstr(p, "["); > + p = NULL; > + if (q && (t = strstr(q + 1, "]"))) { > + p = q + 1; > + *t = 0; > + } > + } > + if (p) { > + svc_mon_src_ip = xstrdup(p); > + } I think all this custom parsing can be re-written as: static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); struct sockaddr_storage svc_mon_src_addr; if (!inet_parse_address(p, &svc_mon_src_addr)) { VLOG_WARN_RL(&rl, "Invalid svc mon src IP %s", p); } else { struct ds src_ip_s = DS_EMPTY_INITIALIZER; ss_format_address_nobracks(&svc_mon_src_addr, &src_ip_s); svc_mon_src_ip = ds_steal_cstr(&src_ip_s); } Or similar. What do you think? > } > free(port_name); > } > + ds_destroy(&key); > > backend_nb->op = op; > backend_nb->svc_mon_src_ip = svc_mon_src_ip; > @@ -3902,7 +3918,8 @@ build_lb_vip_actions(struct ovn_lb_vip *lb_vip, > } > > n_active_backends++; > - ds_put_format(action, "%s:%"PRIu16",", > + bool ipv6 = !IN6_IS_ADDR_V4MAPPED(&backend->ip); > + ds_put_format(action, ipv6 ? "[%s]:%"PRIu16"," : "%s:%"PRIu16",", > backend->ip_str, backend->port); > } > ds_chomp(action, ','); > @@ -8752,6 +8769,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]; > @@ -8760,22 +8778,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 3d7a92ea8..2eab2c4ae 100644 > --- a/northd/ovn-northd.8.xml > +++ b/northd/ovn-northd.8.xml > @@ -1469,6 +1469,23 @@ output; > These flows are required if an ARP request is sent for the IP > <var>SVC_MON_SRC_IP</var>. > </p> > + > + <p> > + For IPv6 the similar flow is added with the following action > + </p> > + > + <pre> > +nd_na { > + eth.dst = eth.src; > + eth.src = <var>E</var>; > + ip6.src = <var>A</var>; > + nd.target = <var>A</var>; > + nd.tll = <var>E</var>; > + outport = inport; > + flags.loopback = 1; > + output; > +}; > + </pre> > </li> > > <li> > diff --git a/ovn-nb.xml b/ovn-nb.xml > index 4b52b9953..8d56d0c6e 100644 > --- a/ovn-nb.xml > +++ b/ovn-nb.xml > @@ -1847,9 +1847,8 @@ > > <group title="Health Checks"> > <p> > - 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. > </p> > > <p> > @@ -1861,7 +1860,7 @@ > column="health_check"/> a reference to a <ref > table="Load_Balancer_Health_Check"/> row whose <ref > table="Load_Balancer_Health_Check" column="vip"/> is set to > - <code>10.0.0.10</code>. > + <code>10.0.0.10</code>. The same approach can be used for IPv6 as well. > </p> > > <column name="health_check"> > @@ -1872,8 +1871,10 @@ > <p> > Maps from endpoint IP to a colon-separated pair of logical port name > and source IP, > - e.g. <code><var>port_name</var>:<var>sourc_ip</var></code>. Health > - checks are sent to this port with the specified source IP. > + e.g. <code><var>port_name</var>:<var>sourc_ip</var></code> for IPv4. > + Health checks are sent to this port with the specified source IP. > + For IPv6 square brackets must be used around IP address, e.g: > + <code><var>port_name</var>:<var>[sourc_ip]</var></code> > </p> > > <p> > @@ -1882,6 +1883,11 @@ > <code>20.0.0.4</code>=<code>sw1-p1:20.0.0.2</code>, if the values > given were suitable ports and IP addresses. > </p> > + > + <p> > + For IPv6 IP to port mappings might be defined as > + <code>[2001::1]</code>=<code>sw0-p1:[2002::1]</code>. > + </p> > </column> > </group> > > @@ -2055,8 +2061,7 @@ or > > <table name="Load_Balancer_Health_Check" title="load balancer"> > <p> > - 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. > </p> > > <column name="vip"> > diff --git a/tests/ovn.at b/tests/ovn.at > index e9b8bc677..8854030a4 100644 > --- a/tests/ovn.at > +++ b/tests/ovn.at > @@ -24243,7 +24243,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 > > @@ -24441,6 +24441,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=(reg0[[2]] == 1 && 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 && !ct.rel && 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=(reg0[[2]] == 1 && 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 && !ct.rel && 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 && !ct.rel && 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 2ece0f571..7fa899edd 100644 > --- a/tests/system-ovn.at > +++ b/tests/system-ovn.at > @@ -4378,7 +4378,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 > > @@ -4603,6 +4603,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 Most of the ovn-nbctl calls above need a "check" prefix. > + > +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 > + No need for the --wait=sb for all of the above. We do a --wait=hv just below. Also, please add "check ovn-nbctl" where applicable. > +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=<cleared>/'], [0], [dnl > +tcp,orig=(src=2001::4,dst=2001::a,sport=<cleared>,dport=<cleared>),reply=(src=2001::3,dst=2001::4,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=2,protoinfo=(state=<cleared>) > +tcp,orig=(src=2001::4,dst=2001::a,sport=<cleared>,dport=<cleared>),reply=(src=2002::3,dst=2001::4,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=2,protoinfo=(state=<cleared>) > +]) > + > +# 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=<cleared>/'], [0], [dnl > +tcp,orig=(src=2001::4,dst=2001::a,sport=<cleared>,dport=<cleared>),reply=(src=2002::3,dst=2001::4,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=2,protoinfo=(state=<cleared>) > +]) > + > +# 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" > +#]) The UDP part is commented out. We should either add a test or remove it all together. > + > +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]) Thanks, Dumitru
> On 2/10/23 12:18, Lorenzo Bianconi wrote: > > Add Similar to IPv4 counterpart, introduce IPv6 load-balancer health > > check support. > > > > Reported-at: https://bugzilla.redhat.com/show_bug.cgi?id=2136094 > > Acked-by: Mark Michelson <mmichels@redhat.com> > > Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com> > > --- > > Changes since v2: > > - cosmetics > > > > Changes since v1: > > - fix potential crash in ovn-northd > > - improve documentation > > --- > > Hi Lorenzo, > > Thanks for this revision; I have a few comments below. Hi Dumitru, thx for the review. > > > controller/pinctrl.c | 216 ++++++++++++++++++++++++------------- > > northd/northd.c | 74 +++++++++---- > > northd/ovn-northd.8.xml | 17 +++ > > ovn-nb.xml | 21 ++-- > > tests/ovn.at | 201 ++++++++++++++++++++++++++++++++++- > > tests/system-ovn.at | 230 +++++++++++++++++++++++++++++++++++++++- > > 6 files changed, 656 insertions(+), 103 deletions(-) > > > > diff --git a/controller/pinctrl.c b/controller/pinctrl.c > > index ffceb7e5f..f8ebc3a9e 100644 > > --- a/controller/pinctrl.c > > +++ b/controller/pinctrl.c > > @@ -6736,9 +6736,10 @@ sync_svc_monitors(struct ovsdb_idl_txn *ovnsb_idl_txn, > > > > struct in6_addr ip_addr; > > ovs_be32 ip4; > > - if (ip_parse(sb_svc_mon->ip, &ip4)) { > > + bool is_ipv4 = ip_parse(sb_svc_mon->ip, &ip4); > > + if (is_ipv4) { > > ip_addr = in6_addr_mapped_ipv4(ip4); > > - } else { > > + } else if (!ipv6_parse(sb_svc_mon->ip, &ip_addr)) { > > continue; > > } > > > > @@ -6751,16 +6752,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 (is_ipv4) { > > + for (size_t j = 0; j < laddrs.n_ipv4_addrs; j++) { > > + if (ip4 == laddrs.ipv4_addrs[j].addr) { > > + ea = laddrs.ea; > > + mac_found = true; > > + break; > > + } > > + } > > + } else { > > + for (size_t j = 0; j < laddrs.n_ipv6_addrs; j++) { > > + if (IN6_ARE_ADDR_EQUAL(&ip_addr, > > + &laddrs.ipv6_addrs[j].addr)) { > > + ea = laddrs.ea; > > + mac_found = true; > > + break; > > + } > > } > > } > > > > - if (!mac_found && !laddrs.n_ipv4_addrs) { > > - /* 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; > > } > > @@ -6794,7 +6806,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 = !is_ipv4; > > svc_mon->state = SVC_MON_S_INIT; > > svc_mon->status = SVC_MON_ST_UNKNOWN; > > svc_mon->protocol = protocol; > > @@ -7562,26 +7574,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; > > > > @@ -7592,7 +7608,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))); > > @@ -7627,21 +7647,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); > > @@ -7649,6 +7674,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); > > @@ -7711,6 +7746,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); > > @@ -7841,32 +7877,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))); > > @@ -7883,44 +7925,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 77e105b86..51a9d92cb 100644 > > --- a/northd/northd.c > > +++ b/northd/northd.c > > @@ -3809,8 +3809,13 @@ 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, ipv6 ? "[%s]" : "%s", backend->ip_str); > > + > > 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, ":"); > > @@ -3818,10 +3823,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); > > - svc_mon_src_ip = xstrdup(p); > > + if (ipv6) { > > + char *t, *q = strstr(p, "["); > > + p = NULL; > > + if (q && (t = strstr(q + 1, "]"))) { > > + p = q + 1; > > + *t = 0; > > + } > > + } > > + if (p) { > > + svc_mon_src_ip = xstrdup(p); > > + } > > I think all this custom parsing can be re-written as: > > > static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); > struct sockaddr_storage svc_mon_src_addr; > if (!inet_parse_address(p, &svc_mon_src_addr)) { > VLOG_WARN_RL(&rl, "Invalid svc mon src IP %s", p); > } else { > struct ds src_ip_s = DS_EMPTY_INITIALIZER; > ss_format_address_nobracks(&svc_mon_src_addr, &src_ip_s); > svc_mon_src_ip = ds_steal_cstr(&src_ip_s); > } > > Or similar. What do you think? ack, I will fix it. > > > } > > free(port_name); > > } > > + ds_destroy(&key); > > > > backend_nb->op = op; > > backend_nb->svc_mon_src_ip = svc_mon_src_ip; > > @@ -3902,7 +3918,8 @@ build_lb_vip_actions(struct ovn_lb_vip *lb_vip, > > } > > > > n_active_backends++; > > - ds_put_format(action, "%s:%"PRIu16",", > > + bool ipv6 = !IN6_IS_ADDR_V4MAPPED(&backend->ip); > > + ds_put_format(action, ipv6 ? "[%s]:%"PRIu16"," : "%s:%"PRIu16",", > > backend->ip_str, backend->port); > > } > > ds_chomp(action, ','); > > @@ -8752,6 +8769,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]; > > @@ -8760,22 +8778,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 3d7a92ea8..2eab2c4ae 100644 > > --- a/northd/ovn-northd.8.xml > > +++ b/northd/ovn-northd.8.xml > > @@ -1469,6 +1469,23 @@ output; > > These flows are required if an ARP request is sent for the IP > > <var>SVC_MON_SRC_IP</var>. > > </p> > > + > > + <p> > > + For IPv6 the similar flow is added with the following action > > + </p> > > + > > + <pre> > > +nd_na { > > + eth.dst = eth.src; > > + eth.src = <var>E</var>; > > + ip6.src = <var>A</var>; > > + nd.target = <var>A</var>; > > + nd.tll = <var>E</var>; > > + outport = inport; > > + flags.loopback = 1; > > + output; > > +}; > > + </pre> > > </li> > > > > <li> > > diff --git a/ovn-nb.xml b/ovn-nb.xml > > index 4b52b9953..8d56d0c6e 100644 > > --- a/ovn-nb.xml > > +++ b/ovn-nb.xml > > @@ -1847,9 +1847,8 @@ > > > > <group title="Health Checks"> > > <p> > > - 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. > > </p> > > > > <p> > > @@ -1861,7 +1860,7 @@ > > column="health_check"/> a reference to a <ref > > table="Load_Balancer_Health_Check"/> row whose <ref > > table="Load_Balancer_Health_Check" column="vip"/> is set to > > - <code>10.0.0.10</code>. > > + <code>10.0.0.10</code>. The same approach can be used for IPv6 as well. > > </p> > > > > <column name="health_check"> > > @@ -1872,8 +1871,10 @@ > > <p> > > Maps from endpoint IP to a colon-separated pair of logical port name > > and source IP, > > - e.g. <code><var>port_name</var>:<var>sourc_ip</var></code>. Health > > - checks are sent to this port with the specified source IP. > > + e.g. <code><var>port_name</var>:<var>sourc_ip</var></code> for IPv4. > > + Health checks are sent to this port with the specified source IP. > > + For IPv6 square brackets must be used around IP address, e.g: > > + <code><var>port_name</var>:<var>[sourc_ip]</var></code> > > </p> > > > > <p> > > @@ -1882,6 +1883,11 @@ > > <code>20.0.0.4</code>=<code>sw1-p1:20.0.0.2</code>, if the values > > given were suitable ports and IP addresses. > > </p> > > + > > + <p> > > + For IPv6 IP to port mappings might be defined as > > + <code>[2001::1]</code>=<code>sw0-p1:[2002::1]</code>. > > + </p> > > </column> > > </group> > > > > @@ -2055,8 +2061,7 @@ or > > > > <table name="Load_Balancer_Health_Check" title="load balancer"> > > <p> > > - 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. > > </p> > > > > <column name="vip"> > > diff --git a/tests/ovn.at b/tests/ovn.at > > index e9b8bc677..8854030a4 100644 > > --- a/tests/ovn.at > > +++ b/tests/ovn.at > > @@ -24243,7 +24243,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 > > > > @@ -24441,6 +24441,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=(reg0[[2]] == 1 && 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 && !ct.rel && 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=(reg0[[2]] == 1 && 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 && !ct.rel && 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 && !ct.rel && 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 2ece0f571..7fa899edd 100644 > > --- a/tests/system-ovn.at > > +++ b/tests/system-ovn.at > > @@ -4378,7 +4378,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 > > > > @@ -4603,6 +4603,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 > > Most of the ovn-nbctl calls above need a "check" prefix. ack, I will fix it. > > > + > > +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 > > + > > No need for the --wait=sb for all of the above. We do a --wait=hv just > below. Also, please add "check ovn-nbctl" where applicable. ack, I will fix it. > > > +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=<cleared>/'], [0], [dnl > > +tcp,orig=(src=2001::4,dst=2001::a,sport=<cleared>,dport=<cleared>),reply=(src=2001::3,dst=2001::4,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=2,protoinfo=(state=<cleared>) > > +tcp,orig=(src=2001::4,dst=2001::a,sport=<cleared>,dport=<cleared>),reply=(src=2002::3,dst=2001::4,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=2,protoinfo=(state=<cleared>) > > +]) > > + > > +# 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=<cleared>/'], [0], [dnl > > +tcp,orig=(src=2001::4,dst=2001::a,sport=<cleared>,dport=<cleared>),reply=(src=2002::3,dst=2001::4,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=2,protoinfo=(state=<cleared>) > > +]) > > + > > +# 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" > > +#]) > > The UDP part is commented out. We should either add a test or remove it > all together. this is a leftover, I will fix it. Regards, Lorenzo > > > + > > +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]) > > Thanks, > Dumitru >
diff --git a/controller/pinctrl.c b/controller/pinctrl.c index ffceb7e5f..f8ebc3a9e 100644 --- a/controller/pinctrl.c +++ b/controller/pinctrl.c @@ -6736,9 +6736,10 @@ sync_svc_monitors(struct ovsdb_idl_txn *ovnsb_idl_txn, struct in6_addr ip_addr; ovs_be32 ip4; - if (ip_parse(sb_svc_mon->ip, &ip4)) { + bool is_ipv4 = ip_parse(sb_svc_mon->ip, &ip4); + if (is_ipv4) { ip_addr = in6_addr_mapped_ipv4(ip4); - } else { + } else if (!ipv6_parse(sb_svc_mon->ip, &ip_addr)) { continue; } @@ -6751,16 +6752,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 (is_ipv4) { + for (size_t j = 0; j < laddrs.n_ipv4_addrs; j++) { + if (ip4 == laddrs.ipv4_addrs[j].addr) { + ea = laddrs.ea; + mac_found = true; + break; + } + } + } else { + for (size_t j = 0; j < laddrs.n_ipv6_addrs; j++) { + if (IN6_ARE_ADDR_EQUAL(&ip_addr, + &laddrs.ipv6_addrs[j].addr)) { + ea = laddrs.ea; + mac_found = true; + break; + } } } - if (!mac_found && !laddrs.n_ipv4_addrs) { - /* 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; } @@ -6794,7 +6806,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 = !is_ipv4; svc_mon->state = SVC_MON_S_INIT; svc_mon->status = SVC_MON_ST_UNKNOWN; svc_mon->protocol = protocol; @@ -7562,26 +7574,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; @@ -7592,7 +7608,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))); @@ -7627,21 +7647,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); @@ -7649,6 +7674,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); @@ -7711,6 +7746,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); @@ -7841,32 +7877,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))); @@ -7883,44 +7925,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 77e105b86..51a9d92cb 100644 --- a/northd/northd.c +++ b/northd/northd.c @@ -3809,8 +3809,13 @@ 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, ipv6 ? "[%s]" : "%s", backend->ip_str); + 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, ":"); @@ -3818,10 +3823,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); - svc_mon_src_ip = xstrdup(p); + if (ipv6) { + char *t, *q = strstr(p, "["); + p = NULL; + if (q && (t = strstr(q + 1, "]"))) { + p = q + 1; + *t = 0; + } + } + if (p) { + 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; @@ -3902,7 +3918,8 @@ build_lb_vip_actions(struct ovn_lb_vip *lb_vip, } n_active_backends++; - ds_put_format(action, "%s:%"PRIu16",", + bool ipv6 = !IN6_IS_ADDR_V4MAPPED(&backend->ip); + ds_put_format(action, ipv6 ? "[%s]:%"PRIu16"," : "%s:%"PRIu16",", backend->ip_str, backend->port); } ds_chomp(action, ','); @@ -8752,6 +8769,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]; @@ -8760,22 +8778,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 3d7a92ea8..2eab2c4ae 100644 --- a/northd/ovn-northd.8.xml +++ b/northd/ovn-northd.8.xml @@ -1469,6 +1469,23 @@ output; These flows are required if an ARP request is sent for the IP <var>SVC_MON_SRC_IP</var>. </p> + + <p> + For IPv6 the similar flow is added with the following action + </p> + + <pre> +nd_na { + eth.dst = eth.src; + eth.src = <var>E</var>; + ip6.src = <var>A</var>; + nd.target = <var>A</var>; + nd.tll = <var>E</var>; + outport = inport; + flags.loopback = 1; + output; +}; + </pre> </li> <li> diff --git a/ovn-nb.xml b/ovn-nb.xml index 4b52b9953..8d56d0c6e 100644 --- a/ovn-nb.xml +++ b/ovn-nb.xml @@ -1847,9 +1847,8 @@ <group title="Health Checks"> <p> - 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. </p> <p> @@ -1861,7 +1860,7 @@ column="health_check"/> a reference to a <ref table="Load_Balancer_Health_Check"/> row whose <ref table="Load_Balancer_Health_Check" column="vip"/> is set to - <code>10.0.0.10</code>. + <code>10.0.0.10</code>. The same approach can be used for IPv6 as well. </p> <column name="health_check"> @@ -1872,8 +1871,10 @@ <p> Maps from endpoint IP to a colon-separated pair of logical port name and source IP, - e.g. <code><var>port_name</var>:<var>sourc_ip</var></code>. Health - checks are sent to this port with the specified source IP. + e.g. <code><var>port_name</var>:<var>sourc_ip</var></code> for IPv4. + Health checks are sent to this port with the specified source IP. + For IPv6 square brackets must be used around IP address, e.g: + <code><var>port_name</var>:<var>[sourc_ip]</var></code> </p> <p> @@ -1882,6 +1883,11 @@ <code>20.0.0.4</code>=<code>sw1-p1:20.0.0.2</code>, if the values given were suitable ports and IP addresses. </p> + + <p> + For IPv6 IP to port mappings might be defined as + <code>[2001::1]</code>=<code>sw0-p1:[2002::1]</code>. + </p> </column> </group> @@ -2055,8 +2061,7 @@ or <table name="Load_Balancer_Health_Check" title="load balancer"> <p> - 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. </p> <column name="vip"> diff --git a/tests/ovn.at b/tests/ovn.at index e9b8bc677..8854030a4 100644 --- a/tests/ovn.at +++ b/tests/ovn.at @@ -24243,7 +24243,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 @@ -24441,6 +24441,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=(reg0[[2]] == 1 && 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 && !ct.rel && 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=(reg0[[2]] == 1 && 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 && !ct.rel && 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 && !ct.rel && 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 2ece0f571..7fa899edd 100644 --- a/tests/system-ovn.at +++ b/tests/system-ovn.at @@ -4378,7 +4378,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 @@ -4603,6 +4603,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=<cleared>/'], [0], [dnl +tcp,orig=(src=2001::4,dst=2001::a,sport=<cleared>,dport=<cleared>),reply=(src=2001::3,dst=2001::4,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=2,protoinfo=(state=<cleared>) +tcp,orig=(src=2001::4,dst=2001::a,sport=<cleared>,dport=<cleared>),reply=(src=2002::3,dst=2001::4,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=2,protoinfo=(state=<cleared>) +]) + +# 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=<cleared>/'], [0], [dnl +tcp,orig=(src=2001::4,dst=2001::a,sport=<cleared>,dport=<cleared>),reply=(src=2002::3,dst=2001::4,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=2,protoinfo=(state=<cleared>) +]) + +# 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])