From patchwork Thu Nov 2 16:13:56 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mark Michelson X-Patchwork-Id: 833410 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=openvswitch.org (client-ip=140.211.169.12; helo=mail.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Received: from mail.linuxfoundation.org (mail.linuxfoundation.org [140.211.169.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3ySVWz1ctHz9t34 for ; Fri, 3 Nov 2017 03:14:06 +1100 (AEDT) Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id 87D0EAE1; Thu, 2 Nov 2017 16:14:03 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@mail.linuxfoundation.org Received: from smtp1.linuxfoundation.org (smtp1.linux-foundation.org [172.17.192.35]) by mail.linuxfoundation.org (Postfix) with ESMTPS id 174A69F8 for ; Thu, 2 Nov 2017 16:14:02 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.7.6 Received: from mx1.redhat.com (mx1.redhat.com [209.132.183.28]) by smtp1.linuxfoundation.org (Postfix) with ESMTPS id 770A64CE for ; Thu, 2 Nov 2017 16:14:01 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id F119361D0A for ; Thu, 2 Nov 2017 16:14:00 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mx1.redhat.com F119361D0A Authentication-Results: ext-mx10.extmail.prod.ext.phx2.redhat.com; dmarc=none (p=none dis=none) header.from=redhat.com Authentication-Results: ext-mx10.extmail.prod.ext.phx2.redhat.com; spf=fail smtp.mailfrom=mmichels@redhat.com Received: from monae.redhat.com (ovpn-123-202.rdu2.redhat.com [10.10.123.202]) by smtp.corp.redhat.com (Postfix) with ESMTP id A664660BE5 for ; Thu, 2 Nov 2017 16:14:00 +0000 (UTC) From: Mark Michelson To: dev@openvswitch.org Date: Thu, 2 Nov 2017 11:13:56 -0500 Message-Id: <20171102161359.7344-2-mmichels@redhat.com> In-Reply-To: <20171102161359.7344-1-mmichels@redhat.com> References: <20171102161359.7344-1-mmichels@redhat.com> X-Scanned-By: MIMEDefang 2.79 on 10.5.11.12 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.39]); Thu, 02 Nov 2017 16:14:01 +0000 (UTC) X-Spam-Status: No, score=-5.0 required=5.0 tests=RCVD_IN_DNSWL_HI, RP_MATCHES_RCVD autolearn=disabled version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on smtp1.linux-foundation.org Subject: [ovs-dev] [PATCH v3 1/4] Add general-purpose IP/port parsing function. X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: ovs-dev-bounces@openvswitch.org Errors-To: ovs-dev-bounces@openvswitch.org OVS has functions for parsing IPv4 addresses, parsing IPv4 addresses with a port, and parsing IPv6 addresses. What is lacking though is a function that can take an IPv4 or IPv6 address, with or without a port. This commit adds ipv46_parse(), which breaks the given input string into its component parts and stores them in a sockaddr_storage structure. The function accepts flags that determine how it should behave if a port is present in the input string. Signed-off-by: Mark Michelson --- lib/packets.c | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/packets.h | 10 ++++++++ 2 files changed, 88 insertions(+) diff --git a/lib/packets.c b/lib/packets.c index 74d87eda8..51044df4c 100644 --- a/lib/packets.c +++ b/lib/packets.c @@ -22,6 +22,8 @@ #include #include #include +#include +#include #include "byte-order.h" #include "csum.h" #include "crc32c.h" @@ -656,6 +658,82 @@ ip_parse_cidr(const char *s, ovs_be32 *ip, unsigned int *plen) return error; } +/* Parses the string into an IPv4 or IPv6 address. + * The port flags act as follows: + * * PORT_OPTIONAL: A port may be present but is not required + * * PORT_REQUIRED: A port must be present + * * PORT_FORBIDDEN: A port must not be present + */ +char * OVS_WARN_UNUSED_RESULT +ipv46_parse(const char *s, enum port_flags flags, struct sockaddr_storage *ss) +{ + char *error = NULL; + + char *copy; + copy = xstrdup(s); + + char *addr; + char *port; + if (*copy == '[') { + char *end; + + addr = copy + 1; + end = strchr(addr, ']'); + if (!end) { + error = xasprintf("No closing bracket on address %s", s); + goto finish; + } + *end++ = '\0'; + if (*end == ':') { + port = end + 1; + } else { + port = NULL; + } + } else { + addr = copy; + port = strchr(copy, ':'); + if (port) { + if (strchr(port + 1, ':')) { + port = NULL; + } else { + *port++ = '\0'; + } + } + } + + if (port && !*port) { + error = xasprintf("Port is an empty string"); + goto finish; + } + + if (port && flags == PORT_FORBIDDEN) { + error = xasprintf("Port forbidden in address %s", s); + goto finish; + } else if (!port && flags == PORT_REQUIRED) { + error = xasprintf("Port required in address %s", s); + goto finish; + } + + struct addrinfo hints = { + .ai_flags = AI_NUMERICHOST | AI_NUMERICSERV, + .ai_family = AF_UNSPEC, + }; + struct addrinfo *res; + int status; + status = getaddrinfo(addr, port, &hints, &res); + if (status) { + error = xasprintf("Error parsing address %s: %s", + s, gai_strerror(status)); + goto finish; + } + memcpy(ss, res->ai_addr, res->ai_addrlen); + freeaddrinfo(res); + +finish: + free(copy); + return error; +} + /* Parses string 's', which must be an IPv6 address. Stores the IPv6 address * into '*ip'. Returns true if successful, otherwise false. */ bool diff --git a/lib/packets.h b/lib/packets.h index 057935cbf..c5915a0f8 100644 --- a/lib/packets.h +++ b/lib/packets.h @@ -1339,6 +1339,16 @@ struct in6_addr ipv6_create_mask(int mask); int ipv6_count_cidr_bits(const struct in6_addr *netmask); bool ipv6_is_cidr(const struct in6_addr *netmask); +enum port_flags { + PORT_OPTIONAL, + PORT_REQUIRED, + PORT_FORBIDDEN, +}; + +char *ipv46_parse(const char *s, enum port_flags flags, + struct sockaddr_storage *ss) + OVS_WARN_UNUSED_RESULT; + bool ipv6_parse(const char *s, struct in6_addr *ip); char *ipv6_parse_masked(const char *s, struct in6_addr *ipv6, struct in6_addr *mask); From patchwork Thu Nov 2 16:13:57 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mark Michelson X-Patchwork-Id: 833419 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=openvswitch.org (client-ip=140.211.169.12; helo=mail.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Received: from mail.linuxfoundation.org (mail.linuxfoundation.org [140.211.169.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3ySVYs0mGHz9t2r for ; Fri, 3 Nov 2017 03:15:45 +1100 (AEDT) Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id 6E20EB1D; Thu, 2 Nov 2017 16:14:05 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@mail.linuxfoundation.org Received: from smtp1.linuxfoundation.org (smtp1.linux-foundation.org [172.17.192.35]) by mail.linuxfoundation.org (Postfix) with ESMTPS id 6371594B for ; Thu, 2 Nov 2017 16:14:02 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.7.6 Received: from mx1.redhat.com (mx1.redhat.com [209.132.183.28]) by smtp1.linuxfoundation.org (Postfix) with ESMTPS id E83AB522 for ; Thu, 2 Nov 2017 16:14:01 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 694BCC04AC4E for ; Thu, 2 Nov 2017 16:14:01 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mx1.redhat.com 694BCC04AC4E Authentication-Results: ext-mx07.extmail.prod.ext.phx2.redhat.com; dmarc=none (p=none dis=none) header.from=redhat.com Authentication-Results: ext-mx07.extmail.prod.ext.phx2.redhat.com; spf=fail smtp.mailfrom=mmichels@redhat.com Received: from monae.redhat.com (ovpn-123-202.rdu2.redhat.com [10.10.123.202]) by smtp.corp.redhat.com (Postfix) with ESMTP id 1C3FF60CA0 for ; Thu, 2 Nov 2017 16:14:01 +0000 (UTC) From: Mark Michelson To: dev@openvswitch.org Date: Thu, 2 Nov 2017 11:13:57 -0500 Message-Id: <20171102161359.7344-3-mmichels@redhat.com> In-Reply-To: <20171102161359.7344-1-mmichels@redhat.com> References: <20171102161359.7344-1-mmichels@redhat.com> X-Scanned-By: MIMEDefang 2.79 on 10.5.11.12 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.31]); Thu, 02 Nov 2017 16:14:01 +0000 (UTC) X-Spam-Status: No, score=-5.0 required=5.0 tests=RCVD_IN_DNSWL_HI, RP_MATCHES_RCVD autolearn=disabled version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on smtp1.linux-foundation.org Subject: [ovs-dev] [PATCH v3 2/4] ovn: Allow ct_lb actions to take IPv6 address arguments. X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: ovs-dev-bounces@openvswitch.org Errors-To: ovs-dev-bounces@openvswitch.org The ct_lb action previously assumed that any address arguments were IPv4. This patch expands the parsing, formatting, and encoding of ct_lb to be amenable to IPv6 addresses as well. Signed-off-by: Mark Michelson --- include/ovn/actions.h | 6 +++- ovn/lib/actions.c | 98 ++++++++++++++++++++++++++++++++++++++++----------- tests/ovn.at | 8 ++++- 3 files changed, 90 insertions(+), 22 deletions(-) diff --git a/include/ovn/actions.h b/include/ovn/actions.h index 0a04af7aa..63885da3c 100644 --- a/include/ovn/actions.h +++ b/include/ovn/actions.h @@ -200,7 +200,11 @@ struct ovnact_ct_nat { }; struct ovnact_ct_lb_dst { - ovs_be32 ip; + int family; + union { + struct in6_addr ipv6; + ovs_be32 ipv4; + }; uint16_t port; }; diff --git a/ovn/lib/actions.c b/ovn/lib/actions.c index c9876436d..2aace1487 100644 --- a/ovn/lib/actions.c +++ b/ovn/lib/actions.c @@ -883,23 +883,62 @@ parse_ct_lb_action(struct action_context *ctx) if (lexer_match(ctx->lexer, LEX_T_LPAREN)) { while (!lexer_match(ctx->lexer, LEX_T_RPAREN)) { - if (ctx->lexer->token.type != LEX_T_INTEGER - || mf_subvalue_width(&ctx->lexer->token.value) > 32) { - free(dsts); - lexer_syntax_error(ctx->lexer, "expecting IPv4 address"); - return; - } + struct ovnact_ct_lb_dst dst; + if (lexer_match(ctx->lexer, LEX_T_LSQUARE)) { + /* IPv6 address and port */ + if (ctx->lexer->token.type != LEX_T_INTEGER + || ctx->lexer->token.format != LEX_F_IPV6) { + free(dsts); + lexer_syntax_error(ctx->lexer, "expecting IPv6 address"); + return; + } + dst.family = AF_INET6; + dst.ipv6 = ctx->lexer->token.value.ipv6; - /* Parse IP. */ - ovs_be32 ip = ctx->lexer->token.value.ipv4; - lexer_get(ctx->lexer); + lexer_get(ctx->lexer); + if (!lexer_match(ctx->lexer, LEX_T_RSQUARE)) { + free(dsts); + lexer_syntax_error(ctx->lexer, "no closing square " + "bracket"); + return; + } + dst.port = 0; + if (lexer_match(ctx->lexer, LEX_T_COLON) + && !action_parse_port(ctx, &dst.port)) { + free(dsts); + return; + } + } else { + if (ctx->lexer->token.type != LEX_T_INTEGER + || (ctx->lexer->token.format != LEX_F_IPV4 + && ctx->lexer->token.format != LEX_F_IPV6)) { + free(dsts); + lexer_syntax_error(ctx->lexer, "expecting IP address"); + return; + } - /* Parse optional port. */ - uint16_t port = 0; - if (lexer_match(ctx->lexer, LEX_T_COLON) - && !action_parse_port(ctx, &port)) { - free(dsts); - return; + /* Parse IP. */ + if (ctx->lexer->token.format == LEX_F_IPV4) { + dst.family = AF_INET; + dst.ipv4 = ctx->lexer->token.value.ipv4; + } else { + dst.family = AF_INET6; + dst.ipv6 = ctx->lexer->token.value.ipv6; + } + + lexer_get(ctx->lexer); + dst.port = 0; + if (lexer_match(ctx->lexer, LEX_T_COLON)) { + if (dst.family == AF_INET6) { + free(dsts); + lexer_syntax_error(ctx->lexer, "IPv6 address needs " + "square brackets if port is included"); + return; + } else if (!action_parse_port(ctx, &dst.port)) { + free(dsts); + return; + } + } } lexer_match(ctx->lexer, LEX_T_COMMA); @@ -907,7 +946,7 @@ parse_ct_lb_action(struct action_context *ctx) if (n_dsts >= allocated_dsts) { dsts = x2nrealloc(dsts, &allocated_dsts, sizeof *dsts); } - dsts[n_dsts++] = (struct ovnact_ct_lb_dst) { ip, port }; + dsts[n_dsts++] = dst; } } @@ -929,9 +968,19 @@ format_CT_LB(const struct ovnact_ct_lb *cl, struct ds *s) } const struct ovnact_ct_lb_dst *dst = &cl->dsts[i]; - ds_put_format(s, IP_FMT, IP_ARGS(dst->ip)); - if (dst->port) { - ds_put_format(s, ":%"PRIu16, dst->port); + if (dst->family == AF_INET) { + ds_put_format(s, IP_FMT, IP_ARGS(dst->ipv4)); + if (dst->port) { + ds_put_format(s, ":%"PRIu16, dst->port); + } + } else { + if (dst->port) { + ds_put_char(s, '['); + } + ipv6_format_addr(&dst->ipv6, s); + if (dst->port) { + ds_put_format(s, "]:%"PRIu16, dst->port); + } } } ds_put_char(s, ')'); @@ -991,8 +1040,17 @@ encode_CT_LB(const struct ovnact_ct_lb *cl, BUILD_ASSERT(MFF_LOG_DNAT_ZONE < MFF_REG0 + FLOW_N_REGS); for (size_t bucket_id = 0; bucket_id < cl->n_dsts; bucket_id++) { const struct ovnact_ct_lb_dst *dst = &cl->dsts[bucket_id]; + char ip_addr[INET6_ADDRSTRLEN]; + if (dst->family == AF_INET) { + inet_ntop(AF_INET, &dst->ipv4, ip_addr, sizeof ip_addr); + } else { + inet_ntop(AF_INET6, &dst->ipv6, ip_addr, sizeof ip_addr); + } ds_put_format(&ds, ",bucket=bucket_id=%"PRIuSIZE",weight:100,actions=" - "ct(nat(dst="IP_FMT, bucket_id, IP_ARGS(dst->ip)); + "ct(nat(dst=%s%s%s", bucket_id, + dst->family == AF_INET6 && dst->port ? "[" : "", + ip_addr, + dst->family == AF_INET6 && dst->port ? "]" : ""); if (dst->port) { ds_put_format(&ds, ":%"PRIu16, dst->port); } diff --git a/tests/ovn.at b/tests/ovn.at index 490841c63..67709b55b 100644 --- a/tests/ovn.at +++ b/tests/ovn.at @@ -825,13 +825,19 @@ ct_lb(192.168.1.2, 192.168.1.3, ); formats as ct_lb(192.168.1.2, 192.168.1.3); encodes as group:2 has prereqs ip +ct_lb(fd0f::2, fd0f::3, ); + formats as ct_lb(fd0f::2, fd0f::3); + encodes as group:3 + has prereqs ip ct_lb(192.168.1.2:); Syntax error at `)' expecting port number. ct_lb(192.168.1.2:123456); Syntax error at `123456' expecting port number. ct_lb(foo); - Syntax error at `foo' expecting IPv4 address. + Syntax error at `foo' expecting IP address. +ct_lb([192.168.1.2]); + Syntax error at `192.168.1.2' expecting IPv6 address. # ct_next ct_next; From patchwork Thu Nov 2 16:13:58 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mark Michelson X-Patchwork-Id: 833420 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=openvswitch.org (client-ip=140.211.169.12; helo=mail.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Received: from mail.linuxfoundation.org (mail.linuxfoundation.org [140.211.169.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3ySVZw5fV6z9t34 for ; Fri, 3 Nov 2017 03:16:40 +1100 (AEDT) Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id 8F236BDB; Thu, 2 Nov 2017 16:14:06 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@mail.linuxfoundation.org Received: from smtp1.linuxfoundation.org (smtp1.linux-foundation.org [172.17.192.35]) by mail.linuxfoundation.org (Postfix) with ESMTPS id CF0B09F8 for ; Thu, 2 Nov 2017 16:14:03 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.7.6 Received: from mx1.redhat.com (mx1.redhat.com [209.132.183.28]) by smtp1.linuxfoundation.org (Postfix) with ESMTPS id 9D1FD248 for ; Thu, 2 Nov 2017 16:14:02 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 27B096014F for ; Thu, 2 Nov 2017 16:14:02 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mx1.redhat.com 27B096014F Authentication-Results: ext-mx01.extmail.prod.ext.phx2.redhat.com; dmarc=none (p=none dis=none) header.from=redhat.com Authentication-Results: ext-mx01.extmail.prod.ext.phx2.redhat.com; spf=fail smtp.mailfrom=mmichels@redhat.com Received: from monae.redhat.com (ovpn-123-202.rdu2.redhat.com [10.10.123.202]) by smtp.corp.redhat.com (Postfix) with ESMTP id 9B61860BE5 for ; Thu, 2 Nov 2017 16:14:01 +0000 (UTC) From: Mark Michelson To: dev@openvswitch.org Date: Thu, 2 Nov 2017 11:13:58 -0500 Message-Id: <20171102161359.7344-4-mmichels@redhat.com> In-Reply-To: <20171102161359.7344-1-mmichels@redhat.com> References: <20171102161359.7344-1-mmichels@redhat.com> X-Scanned-By: MIMEDefang 2.79 on 10.5.11.12 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.25]); Thu, 02 Nov 2017 16:14:02 +0000 (UTC) X-Spam-Status: No, score=-5.0 required=5.0 tests=RCVD_IN_DNSWL_HI, RP_MATCHES_RCVD autolearn=disabled version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on smtp1.linux-foundation.org Subject: [ovs-dev] [PATCH v3 3/4] ovn: Allow northd to install IPv6 ct_lb logical flows. X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: ovs-dev-bounces@openvswitch.org Errors-To: ovs-dev-bounces@openvswitch.org For this commit, ovn-northd will now accept both IPv4 and IPv6 addresses in the northbound database for a load balancer VIP or destination addresses. For IPv4, the behavior remains the same. For IPv6, the following logical flows will be added to the southbound database: * An ND_NA response for incoming ND_NS requests for the load balancer VIP. * A ct_lb flow with the configured IPv6 addresses. The ovn-northd manpage has been updated to indicate what flows are added for load balancers with IPv6 VIPs. Signed-off-by: Mark Michelson --- ovn/northd/ovn-northd.8.xml | 68 ++++++++++------- ovn/northd/ovn-northd.c | 182 ++++++++++++++++++++++++++------------------ ovn/ovn-nb.xml | 22 +++--- 3 files changed, 163 insertions(+), 109 deletions(-) diff --git a/ovn/northd/ovn-northd.8.xml b/ovn/northd/ovn-northd.8.xml index e9799e18a..b31106521 100644 --- a/ovn/northd/ovn-northd.8.xml +++ b/ovn/northd/ovn-northd.8.xml @@ -243,8 +243,10 @@ balancing rules with virtual IP addresses (and ports) are configured in OVN_Northbound database for a logical switch datapath, a priority-100 flow is added for each configured virtual IP address - VIP with a match ip && ip4.dst == VIP - that sets an action reg0[0] = 1; next; to act as a + VIP. For IPv4 VIPs, the match is ip + && ip4.dst == VIP. For IPv6 VIPs, + the match is ip && ip6.dst == VIP. The + flow sets an action reg0[0] = 1; next; to act as a hint for table Pre-stateful to send IP packets to the connection tracker for packet de-fragmentation before eventually advancing to ingress table LB. @@ -383,21 +385,29 @@
  • For all the configured load balancing rules for a switch in OVN_Northbound database that includes a L4 port - PORT of protocol P and IPv4 address - VIP, a priority-120 flow that matches on - ct.new && ip && ip4.dst == VIP - && P && P.dst == PORT - with an action of ct_lb(args), - where args contains comma separated IPv4 addresses (and - optional port numbers) to load balance to. + PORT of protocol P and IP address + VIP, a priority-120 flow is added. For IPv4 VIPs + , the flow matches ct.new && ip && + ip4.dst == VIP && P && + P.dst == PORT. For IPv6 VIPs, + the flow matches ct.new && ip && ip6.dst == + VIP && P && P.dst == + PORT. The flow's action is ct_lb(args) + , where args contains comma separated IP addresses + (and optional port numbers) to load balance to. The address family of + the IP addresses of args is the same as the address family + of VIP
  • For all the configured load balancing rules for a switch in OVN_Northbound database that includes just an IP address - VIP to match on, a priority-110 flow that matches on - ct.new && ip && ip4.dst == VIP - with an action of ct_lb(args), where - args contains comma separated IPv4 addresses. + VIP to match on, OVN adds a priority-110 flow. For IPv4 + VIPs, the flow matches ct.new && ip && + ip4.dst == VIP. For IPv6 VIPs, + the flow matches ct.new && ip && ip6.dst == + VIP. The action on this flow is + ct_lb(args), where args contains comma + separated IP addresses of the same address family as VIP.
  • A priority-100 flow commits packets to connection tracker using @@ -1113,7 +1123,7 @@ output;

    These flows reply to ARP requests for the virtual IP addresses configured in the router for DNAT or load balancing. For a - configured DNAT IP address or a load balancer VIP A, + configured DNAT IP address or a load balancer IPv4 VIP A, for each router port P with Ethernet address E, a priority-90 flow matches inport == P && arp.op == 1 && @@ -1190,13 +1200,13 @@ arp.sha = external_mac;

    Reply to IPv6 Neighbor Solicitations. These flows reply to Neighbor Solicitation requests for the router's own IPv6 - address and populate the logical router's mac binding table. - For each router port P that owns IPv6 address - A, solicited node address S, and - Ethernet address E, a priority-90 flow matches - inport == P && nd_ns && - ip6.dst == {A, E} && nd.target - == A with the following actions: + address and load balancing IPv6 VIPs and populate the logical + router's mac binding table. For each router port P that + owns IPv6 address or has load balancing VIP A, solicited + node address S, and Ethernet address E, a + priority-90 flow matches inport == P && + nd_ns && ip6.dst == {A, E} && + nd.target == A with the following actions:

    @@ -1364,10 +1374,12 @@ icmp4 {
           to the next table.  If load balancing rules with virtual IP addresses
           (and ports) are configured in OVN_Northbound database for a
           Gateway router, a priority-100 flow is added for each configured virtual
    -      IP address VIP with a match ip &&
    -      ip4.dst == VIP that sets an action
    -      ct_next; to send IP packets to the connection tracker for
    -      packet de-fragmentation and tracking before sending it to the next table.
    +      IP address VIP. For IPv4 VIPs the flow matches
    +      ip && ip4.dst == VIP.  For IPv6
    +      VIPs, the flow matches ip && ip6.dst ==
    +      VIP.  The flow uses the action ct_next;
    +      to send IP packets to the connection tracker for packet de-fragmentation
    +      and tracking before sending it to the next table.
         

    Ingress Table 3: UNSNAT

    @@ -1464,7 +1476,8 @@ icmp4 {

    Following load balancing DNAT flows are added for Gateway router or Router with gateway port. These flows are programmed only on the - redirect-chassis. + redirect-chassis. These flows do not get programmed for + load balancers with IPv6 VIPs.

      @@ -1910,7 +1923,8 @@ arp { router gateway port with an action ct_dnat;. If the backend IPv4 address B is also configured with L4 port PORT of protocol P, then the - match also includes P.src == PORT. + match also includes P.src == PORT. These + flows are not added for load balancers with IPv6 VIPs.

      diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c index 268bd60d6..6732ad003 100644 --- a/ovn/northd/ovn-northd.c +++ b/ovn/northd/ovn-northd.c @@ -1562,11 +1562,11 @@ join_logical_ports(struct northd_context *ctx, static void ip_address_and_port_from_lb_key(const char *key, char **ip_address, - uint16_t *port); + uint16_t *port, int *addr_family); static void get_router_load_balancer_ips(const struct ovn_datapath *od, - struct sset *all_ips) + struct sset *all_ips, int *addr_family) { if (!od->nbr) { return; @@ -1582,7 +1582,8 @@ get_router_load_balancer_ips(const struct ovn_datapath *od, char *ip_address = NULL; uint16_t port; - ip_address_and_port_from_lb_key(node->key, &ip_address, &port); + ip_address_and_port_from_lb_key(node->key, &ip_address, &port, + addr_family); if (!ip_address) { continue; } @@ -1659,7 +1660,8 @@ get_nat_addresses(const struct ovn_port *op, size_t *n) /* A set to hold all load-balancer vips. */ struct sset all_ips = SSET_INITIALIZER(&all_ips); - get_router_load_balancer_ips(op->od, &all_ips); + int addr_family; + get_router_load_balancer_ips(op->od, &all_ips, &addr_family); const char *ip_address; SSET_FOR_EACH (ip_address, &all_ips) { @@ -2902,44 +2904,33 @@ build_pre_acls(struct ovn_datapath *od, struct hmap *lflows) * 'ip_address'. */ static void ip_address_and_port_from_lb_key(const char *key, char **ip_address, - uint16_t *port) + uint16_t *port, int *addr_family) { - char *ip_str, *start, *next; - *ip_address = NULL; - *port = 0; + struct sockaddr_storage ss; + char ip_addr_buf[INET6_ADDRSTRLEN]; + char *error; - next = start = xstrdup(key); - ip_str = strsep(&next, ":"); - if (!ip_str || !ip_str[0]) { - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); - VLOG_WARN_RL(&rl, "bad ip address for load balancer key %s", key); - free(start); - return; - } - - ovs_be32 ip, mask; - char *error = ip_parse_masked(ip_str, &ip, &mask); - if (error || mask != OVS_BE32_MAX) { + error = ipv46_parse(key, PORT_OPTIONAL, &ss); + if (error) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); - VLOG_WARN_RL(&rl, "bad ip address for load balancer key %s", key); - free(start); + VLOG_WARN_RL(&rl, "bad ip address or port for load balancer key %s", + key); free(error); return; } - int l4_port = 0; - if (next && next[0]) { - if (!str_to_int(next, 0, &l4_port) || l4_port < 0 || l4_port > 65535) { - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); - VLOG_WARN_RL(&rl, "bad ip port for load balancer key %s", key); - free(start); - return; - } + if (ss.ss_family == AF_INET) { + struct sockaddr_in *sin = ALIGNED_CAST(struct sockaddr_in *, &ss); + *port = sin->sin_port == 0 ? 0 : ntohs(sin->sin_port); + inet_ntop(AF_INET, &sin->sin_addr, ip_addr_buf, sizeof ip_addr_buf); + } else { + struct sockaddr_in6 *sin6 = ALIGNED_CAST(struct sockaddr_in6 *, &ss); + *port = sin6->sin6_port == 0 ? 0 : ntohs(sin6->sin6_port); + inet_ntop(AF_INET6, &sin6->sin6_addr, ip_addr_buf, sizeof ip_addr_buf); } - *port = l4_port; - *ip_address = strdup(ip_str); - free(start); + *ip_address = xstrdup(ip_addr_buf); + *addr_family = ss.ss_family; } /* @@ -2967,6 +2958,7 @@ build_pre_lb(struct ovn_datapath *od, struct hmap *lflows) struct sset all_ips = SSET_INITIALIZER(&all_ips); bool vip_configured = false; + int addr_family = AF_INET; for (int i = 0; i < od->nbs->n_load_balancer; i++) { struct nbrec_load_balancer *lb = od->nbs->load_balancer[i]; struct smap *vips = &lb->vips; @@ -2978,7 +2970,8 @@ build_pre_lb(struct ovn_datapath *od, struct hmap *lflows) /* node->key contains IP:port or just IP. */ char *ip_address = NULL; uint16_t port; - ip_address_and_port_from_lb_key(node->key, &ip_address, &port); + ip_address_and_port_from_lb_key(node->key, &ip_address, &port, + &addr_family); if (!ip_address) { continue; } @@ -3000,7 +2993,13 @@ build_pre_lb(struct ovn_datapath *od, struct hmap *lflows) * packet to conntrack for defragmentation. */ const char *ip_address; SSET_FOR_EACH(ip_address, &all_ips) { - char *match = xasprintf("ip && ip4.dst == %s", ip_address); + char *match; + + if (addr_family == AF_INET) { + match = xasprintf("ip && ip4.dst == %s", ip_address); + } else { + match = xasprintf("ip && ip6.dst == %s", ip_address); + } ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_LB, 100, match, REGBIT_CONNTRACK_DEFRAG" = 1; next;"); free(match); @@ -3458,10 +3457,12 @@ build_stateful(struct ovn_datapath *od, struct hmap *lflows) SMAP_FOR_EACH (node, vips) { uint16_t port = 0; + int addr_family; /* node->key contains IP:port or just IP. */ char *ip_address = NULL; - ip_address_and_port_from_lb_key(node->key, &ip_address, &port); + ip_address_and_port_from_lb_key(node->key, &ip_address, &port, + &addr_family); if (!ip_address) { continue; } @@ -3469,7 +3470,11 @@ build_stateful(struct ovn_datapath *od, struct hmap *lflows) /* New connections in Ingress table. */ char *action = xasprintf("ct_lb(%s);", node->value); struct ds match = DS_EMPTY_INITIALIZER; - ds_put_format(&match, "ct.new && ip4.dst == %s", ip_address); + if (addr_family == AF_INET) { + ds_put_format(&match, "ct.new && ip4.dst == %s", ip_address); + } else { + ds_put_format(&match, "ct.new && ip6.dst == %s", ip_address); + } if (port) { if (lb->protocol && !strcmp(lb->protocol, "udp")) { ds_put_format(&match, " && udp.dst == %d", port); @@ -4352,7 +4357,7 @@ static void add_router_lb_flow(struct hmap *lflows, struct ovn_datapath *od, struct ds *match, struct ds *actions, int priority, const char *lb_force_snat_ip, char *backend_ips, - bool is_udp) + bool is_udp, int addr_family) { /* A match and actions for new connections. */ char *new_match = xasprintf("ct.new && %s", ds_cstr(match)); @@ -4380,7 +4385,8 @@ add_router_lb_flow(struct hmap *lflows, struct ovn_datapath *od, free(new_match); free(est_match); - if (!od->l3dgw_port || !od->l3redirect_port || !backend_ips) { + if (!od->l3dgw_port || !od->l3redirect_port || !backend_ips + || addr_family != AF_INET) { return; } @@ -4397,7 +4403,9 @@ add_router_lb_flow(struct hmap *lflows, struct ovn_datapath *od, while (ip_str && ip_str[0]) { char *ip_address = NULL; uint16_t port = 0; - ip_address_and_port_from_lb_key(ip_str, &ip_address, &port); + int addr_family; + ip_address_and_port_from_lb_key(ip_str, &ip_address, &port, + &addr_family); if (!ip_address) { break; } @@ -4635,36 +4643,55 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, /* A set to hold all load-balancer vips that need ARP responses. */ struct sset all_ips = SSET_INITIALIZER(&all_ips); - get_router_load_balancer_ips(op->od, &all_ips); + int addr_family; + get_router_load_balancer_ips(op->od, &all_ips, &addr_family); const char *ip_address; SSET_FOR_EACH(ip_address, &all_ips) { - ovs_be32 ip; - if (!ip_parse(ip_address, &ip) || !ip) { - continue; - } - ds_clear(&match); - ds_put_format(&match, - "inport == %s && arp.tpa == "IP_FMT" && arp.op == 1", - op->json_key, IP_ARGS(ip)); + if (addr_family == AF_INET) { + ds_put_format(&match, + "inport == %s && arp.tpa == %s && arp.op == 1", + op->json_key, ip_address); + } else { + ds_put_format(&match, + "inport == %s && nd_ns && nd.target == %s", + op->json_key, ip_address); + } ds_clear(&actions); - ds_put_format(&actions, + if (addr_family == AF_INET) { + 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 = "IP_FMT"; " + "arp.spa = %s; " "outport = %s; " "flags.loopback = 1; " "output;", op->lrp_networks.ea_s, op->lrp_networks.ea_s, - IP_ARGS(ip), + ip_address, op->json_key); + } else { + ds_put_format(&actions, + "nd_na { " + "eth.src = %s; " + "ip6.src = %s; " + "nd.target = %s; " + "nd.tll = %s; " + "outport = inport; " + "flags.loopback = 1; " + "output; " + "};", + op->lrp_networks.ea_s, + ip_address, + ip_address, + op->lrp_networks.ea_s); + } ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 90, ds_cstr(&match), ds_cstr(&actions)); } @@ -5328,16 +5355,36 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, SMAP_FOR_EACH (node, vips) { uint16_t port = 0; + int addr_family; /* node->key contains IP:port or just IP. */ char *ip_address = NULL; - ip_address_and_port_from_lb_key(node->key, &ip_address, &port); + ip_address_and_port_from_lb_key(node->key, &ip_address, &port, + &addr_family); if (!ip_address) { continue; } if (!sset_contains(&all_ips, ip_address)) { sset_add(&all_ips, ip_address); + /* If there are any load balancing rules, we should send + * the packet to conntrack for defragmentation and + * tracking. This helps with two things. + * + * 1. With tracking, we can send only new connections to + * pick a DNAT ip address from a group. + * 2. If there are L4 ports in load balancing rules, we + * need the defragmentation to match on L4 ports. */ + ds_clear(&match); + if (addr_family == AF_INET) { + ds_put_format(&match, "ip && ip4.dst == %s", + ip_address); + } else { + ds_put_format(&match, "ip && ip6.dst == %s", + ip_address); + } + ovn_lflow_add(lflows, od, S_ROUTER_IN_DEFRAG, + 100, ds_cstr(&match), "ct_next;"); } /* Higher priority rules are added for load-balancing in DNAT @@ -5349,8 +5396,13 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, ds_put_format(&actions, "ct_lb(%s);", node->value); ds_clear(&match); - ds_put_format(&match, "ip && ip4.dst == %s", - ip_address); + if (addr_family == AF_INET) { + ds_put_format(&match, "ip && ip4.dst == %s", + ip_address); + } else { + ds_put_format(&match, "ip && ip6.dst == %s", + ip_address); + } free(ip_address); int prio = 110; @@ -5372,26 +5424,10 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, od->l3redirect_port->json_key); } add_router_lb_flow(lflows, od, &match, &actions, prio, - lb_force_snat_ip, node->value, is_udp); + lb_force_snat_ip, node->value, is_udp, + addr_family); } } - - /* If there are any load balancing rules, we should send the - * packet to conntrack for defragmentation and tracking. This helps - * with two things. - * - * 1. With tracking, we can send only new connections to pick a - * DNAT ip address from a group. - * 2. If there are L4 ports in load balancing rules, we need the - * defragmentation to match on L4 ports. */ - const char *ip_address; - SSET_FOR_EACH(ip_address, &all_ips) { - ds_clear(&match); - ds_put_format(&match, "ip && ip4.dst == %s", ip_address); - ovn_lflow_add(lflows, od, S_ROUTER_IN_DEFRAG, - 100, ds_cstr(&match), "ct_next;"); - } - sset_destroy(&all_ips); } diff --git a/ovn/ovn-nb.xml b/ovn/ovn-nb.xml index 9869d7ed7..a6afc9583 100644 --- a/ovn/ovn-nb.xml +++ b/ovn/ovn-nb.xml @@ -113,8 +113,8 @@ - Load balance a virtual ipv4 address to a set of logical port endpoint - ipv4 addresses. + Load balance a virtual ip address to a set of logical port endpoint + ip addresses. @@ -925,16 +925,20 @@

      - A map of virtual IPv4 addresses (and an optional port number with + A map of virtual IP addresses (and an optional port number with : as a separator) associated with this load balancer and - their corresponding endpoint IPv4 addresses (and optional port numbers + their corresponding endpoint IP addresses (and optional port numbers with : as separators) separated by commas. If the destination IP address (and port number) of a packet leaving a - container or a VM matches the virtual IPv4 address (and port number) + container or a VM matches the virtual IP address (and port number) provided here as a key, then OVN will statefully replace the - destination IP address by one of the provided IPv4 address (and port - number) in this map as a value. Examples for keys are "192.168.1.4" - and "172.16.1.8:80". Examples for value are "10.0.0.1, 10.0.0.2" and + destination IP address by one of the provided IP address (and port + number) in this map as a value. IPv4 and IPv6 addresses are supported + for load balancing; however a VIP of one address family may not be + mapped to a destination IP address of a different family. If + specifying an IPv6 address with a port, the address portion must be + enclosed in square brackets. Examples for keys are "192.168.1.4" and + "[fd0f::1]:8800". Examples for value are "10.0.0.1, 10.0.0.2" and "20.0.0.10:8800, 20.0.0.11:8800".

      @@ -1116,7 +1120,7 @@ - Load balance a virtual ipv4 address to a set of logical port ipv4 + Load balance a virtual ip address to a set of logical port ip addresses. Load balancer rules only work on the Gateway routers. From patchwork Thu Nov 2 16:13:59 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mark Michelson X-Patchwork-Id: 833421 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=openvswitch.org (client-ip=140.211.169.12; helo=mail.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Received: from mail.linuxfoundation.org (mail.linuxfoundation.org [140.211.169.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3ySVbr663zz9t34 for ; Fri, 3 Nov 2017 03:17:28 +1100 (AEDT) Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id 9AED5BE9; Thu, 2 Nov 2017 16:14:07 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@mail.linuxfoundation.org Received: from smtp1.linuxfoundation.org (smtp1.linux-foundation.org [172.17.192.35]) by mail.linuxfoundation.org (Postfix) with ESMTPS id 7887BBC2 for ; Thu, 2 Nov 2017 16:14:05 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.7.6 Received: from mx1.redhat.com (mx1.redhat.com [209.132.183.28]) by smtp1.linuxfoundation.org (Postfix) with ESMTPS id 803F14CE for ; Thu, 2 Nov 2017 16:14:03 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id EAA4861D1F for ; Thu, 2 Nov 2017 16:14:02 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mx1.redhat.com EAA4861D1F Authentication-Results: ext-mx10.extmail.prod.ext.phx2.redhat.com; dmarc=none (p=none dis=none) header.from=redhat.com Authentication-Results: ext-mx10.extmail.prod.ext.phx2.redhat.com; spf=fail smtp.mailfrom=mmichels@redhat.com Received: from monae.redhat.com (ovpn-123-202.rdu2.redhat.com [10.10.123.202]) by smtp.corp.redhat.com (Postfix) with ESMTP id 4E54760BE5 for ; Thu, 2 Nov 2017 16:14:02 +0000 (UTC) From: Mark Michelson To: dev@openvswitch.org Date: Thu, 2 Nov 2017 11:13:59 -0500 Message-Id: <20171102161359.7344-5-mmichels@redhat.com> In-Reply-To: <20171102161359.7344-1-mmichels@redhat.com> References: <20171102161359.7344-1-mmichels@redhat.com> X-Scanned-By: MIMEDefang 2.79 on 10.5.11.12 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.39]); Thu, 02 Nov 2017 16:14:03 +0000 (UTC) X-Spam-Status: No, score=-5.0 required=5.0 tests=RCVD_IN_DNSWL_HI, RP_MATCHES_RCVD autolearn=disabled version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on smtp1.linux-foundation.org Subject: [ovs-dev] [PATCH v3 4/4] ovn: Add IPv6 capability to ovn-nbctl lb-add X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: ovs-dev-bounces@openvswitch.org Errors-To: ovs-dev-bounces@openvswitch.org ovn-nbctl will now accept IPv6 addresses for load balancer VIPs and desetination addresses. In addition, the ovn-nbctl lb-list, lr-lb-list, and ls-lb-list have been modified to be able to fit IPv6 addresses on screen. Signed-off-by: Mark Michelson --- ovn/utilities/ovn-nbctl.8.xml | 14 +- ovn/utilities/ovn-nbctl.c | 175 ++++++++++++++----- tests/ovn-nbctl.at | 379 +++++++++++++++++++++++++++++++++++------- 3 files changed, 459 insertions(+), 109 deletions(-) diff --git a/ovn/utilities/ovn-nbctl.8.xml b/ovn/utilities/ovn-nbctl.8.xml index a20828088..3688d35b3 100644 --- a/ovn/utilities/ovn-nbctl.8.xml +++ b/ovn/utilities/ovn-nbctl.8.xml @@ -600,13 +600,15 @@ Creates a new load balancer named lb with the provided vip and ips or adds the vip to an existing lb. vip should be a - virtual IPv4 address (or an IPv4 address and a port number with + virtual IP address (or an IP address and a port number with : as a separator). Examples for vip are - 192.168.1.4 and 192.168.1.5:8080. - ips should be comma separated IPv4 endpoints (or comma - separated IPv4 addresses and port numbers with : as a - separator). Examples for ips are 10.0.0.1,10.0.0.2 - or 20.0.0.10:8800,20.0.0.11:8800. + 192.168.1.4, fd0f::1, and + 192.168.1.5:8080. ips should be comma + separated IP endpoints (or comma separated IP addresses and port + numbers with : as a separator). ips must + be the same address family as vip. Examples for + ips are 10.0.0.1,10.0.0.2or + [fdef::1]:8800,[fdef::2]:8800.

      diff --git a/ovn/utilities/ovn-nbctl.c b/ovn/utilities/ovn-nbctl.c index 77889961f..34904cf5b 100644 --- a/ovn/utilities/ovn-nbctl.c +++ b/ovn/utilities/ovn-nbctl.c @@ -1553,40 +1553,76 @@ nbctl_lb_add(struct ctl_context *ctx) } } - ovs_be32 ipv4 = 0; - ovs_be16 port = 0; - char *error = ip_parse_port(lb_vip, &ipv4, &port); + struct sockaddr_storage ss_vip; + char *error; + error = ipv46_parse(lb_vip, PORT_OPTIONAL, &ss_vip); if (error) { free(error); - if (!ip_parse(lb_vip, &ipv4)) { - ctl_fatal("%s: should be an IPv4 address (or an IPv4 address " - "and a port number with : as a separator).", lb_vip); + ctl_fatal("%s: should be an IP address (or an IP address " + "and a port number with : as a separator).", lb_vip); + } + + char lb_vip_normalized[INET6_ADDRSTRLEN + 8]; + char normalized_ip[INET6_ADDRSTRLEN]; + if (ss_vip.ss_family == AF_INET) { + struct sockaddr_in *sin = ALIGNED_CAST(struct sockaddr_in *, &ss_vip); + inet_ntop(AF_INET, &sin->sin_addr, normalized_ip, + sizeof normalized_ip); + if (sin->sin_port) { + is_vip_with_port = true; + snprintf(lb_vip_normalized, sizeof lb_vip_normalized, "%s:%d", + normalized_ip, ntohs(sin->sin_port)); + } else { + is_vip_with_port = false; + ovs_strlcpy(lb_vip_normalized, normalized_ip, + sizeof lb_vip_normalized); } - - if (is_update_proto) { - ctl_fatal("Protocol is unnecessary when no port of vip " - "is given."); + } else { + struct sockaddr_in6 *sin6 = ALIGNED_CAST(struct sockaddr_in6 *, + &ss_vip); + inet_ntop(AF_INET6, &sin6->sin6_addr, normalized_ip, + sizeof normalized_ip); + if (sin6->sin6_port) { + is_vip_with_port = true; + snprintf(lb_vip_normalized, sizeof lb_vip_normalized, "[%s]:%d", + normalized_ip, ntohs(sin6->sin6_port)); + } else { + is_vip_with_port = false; + ovs_strlcpy(lb_vip_normalized, normalized_ip, + sizeof lb_vip_normalized); } - is_vip_with_port = false; + } + + if (!is_vip_with_port && is_update_proto) { + ctl_fatal("Protocol is unnecessary when no port of vip " + "is given."); } char *token = NULL, *save_ptr = NULL; struct ds lb_ips_new = DS_EMPTY_INITIALIZER; for (token = strtok_r(lb_ips, ",", &save_ptr); token != NULL; token = strtok_r(NULL, ",", &save_ptr)) { - if (is_vip_with_port) { - error = ip_parse_port(token, &ipv4, &port); - if (error) { - free(error); - ds_destroy(&lb_ips_new); - ctl_fatal("%s: should be an IPv4 address and a port " + struct sockaddr_storage ss_dst; + + error = ipv46_parse(token, is_vip_with_port + ? PORT_REQUIRED + : PORT_FORBIDDEN, + &ss_dst); + + if (error) { + free(error); + if (is_vip_with_port) { + ctl_fatal("%s: should be an IP address and a port " "number with : as a separator.", token); + } else { + ctl_fatal("%s: should be an IP address.", token); } - } else { - if (!ip_parse(token, &ipv4)) { - ds_destroy(&lb_ips_new); - ctl_fatal("%s: should be an IPv4 address.", token); - } + } + + if (ss_vip.ss_family != ss_dst.ss_family) { + ds_destroy(&lb_ips_new); + ctl_fatal("%s: IP address family is different from VIP %s.", + token, lb_vip_normalized); } ds_put_format(&lb_ips_new, "%s%s", lb_ips_new.length ? "," : "", token); @@ -1596,19 +1632,19 @@ nbctl_lb_add(struct ctl_context *ctx) if (!add_duplicate) { lb = lb_by_name_or_uuid(ctx, lb_name, false); if (lb) { - if (smap_get(&lb->vips, lb_vip)) { + if (smap_get(&lb->vips, lb_vip_normalized)) { if (!may_exist) { ds_destroy(&lb_ips_new); ctl_fatal("%s: a load balancer with this vip (%s) " - "already exists", lb_name, lb_vip); + "already exists", lb_name, lb_vip_normalized); } /* Update the vips. */ smap_replace(CONST_CAST(struct smap *, &lb->vips), - lb_vip, ds_cstr(&lb_ips_new)); + lb_vip_normalized, ds_cstr(&lb_ips_new)); } else { /* Add the new vips. */ smap_add(CONST_CAST(struct smap *, &lb->vips), - lb_vip, ds_cstr(&lb_ips_new)); + lb_vip_normalized, ds_cstr(&lb_ips_new)); } /* Update the load balancer. */ @@ -1628,7 +1664,7 @@ nbctl_lb_add(struct ctl_context *ctx) nbrec_load_balancer_set_name(lb, lb_name); nbrec_load_balancer_set_protocol(lb, lb_proto); smap_add(CONST_CAST(struct smap *, &lb->vips), - lb_vip, ds_cstr(&lb_ips_new)); + lb_vip_normalized, ds_cstr(&lb_ips_new)); nbrec_load_balancer_set_vips(lb, &lb->vips); ds_destroy(&lb_ips_new); } @@ -1670,32 +1706,49 @@ nbctl_lb_del(struct ctl_context *ctx) static void lb_info_add_smap(const struct nbrec_load_balancer *lb, - struct smap *lbs) + struct smap *lbs, int vip_width) { struct ds key = DS_EMPTY_INITIALIZER; struct ds val = DS_EMPTY_INITIALIZER; char *error, *protocol; - ovs_be32 ipv4 = 0; - ovs_be16 port = 0; const struct smap_node **nodes = smap_sort(&lb->vips); if (nodes) { for (int i = 0; i < smap_count(&lb->vips); i++) { const struct smap_node *node = nodes[i]; protocol = lb->protocol; - error = ip_parse_port(node->key, &ipv4, &port); + + struct sockaddr_storage ss; + error = ipv46_parse(node->key, PORT_OPTIONAL, &ss); if (error) { + VLOG_WARN("%s", error); free(error); - protocol = "tcp/udp"; + continue; + } + + if (ss.ss_family == AF_INET) { + struct sockaddr_in *sin = ALIGNED_CAST(struct sockaddr_in *, + &ss); + if (!sin->sin_port) { + protocol = "tcp/udp"; + } + } else { + struct sockaddr_in6 *sin6 = ALIGNED_CAST(struct sockaddr_in6 *, + &ss); + if (!sin6->sin6_port) { + protocol = "tcp/udp"; + } } i == 0 ? ds_put_format(&val, - UUID_FMT " %-20.16s%-11.7s%-25.21s%s", + UUID_FMT " %-20.16s%-11.7s%-*.*s%s", UUID_ARGS(&lb->header_.uuid), lb->name, protocol, + vip_width + 4, vip_width, node->key, node->value) - : ds_put_format(&val, "\n%60s%-11.7s%-25.21s%s", + : ds_put_format(&val, "\n%60s%-11.7s%-*.*s%s", "", protocol, + vip_width + 4, vip_width, node->key, node->value); } @@ -1709,12 +1762,12 @@ lb_info_add_smap(const struct nbrec_load_balancer *lb, } static void -lb_info_print(struct ctl_context *ctx, struct smap *lbs) +lb_info_print(struct ctl_context *ctx, struct smap *lbs, int vip_width) { const struct smap_node **nodes = smap_sort(lbs); if (nodes) { - ds_put_format(&ctx->output, "%-40.36s%-20.16s%-11.7s%-25.21s%s\n", - "UUID", "LB", "PROTO", "VIP", "IPs"); + ds_put_format(&ctx->output, "%-40.36s%-20.16s%-11.7s%-*.*s%s\n", + "UUID", "LB", "PROTO", vip_width + 4, vip_width, "VIP", "IPs"); for (size_t i = 0; i < smap_count(lbs); i++) { const struct smap_node *node = nodes[i]; ds_put_format(&ctx->output, "%s\n", node->value); @@ -1724,21 +1777,45 @@ lb_info_print(struct ctl_context *ctx, struct smap *lbs) } } +static int +lb_get_max_vip_length(const struct nbrec_load_balancer *lb, int vip_width) +{ + const struct smap_node *node; + int max_length = vip_width; + + SMAP_FOR_EACH (node, &lb->vips) { + size_t keylen = strlen(node->key); + if (max_length < keylen) { + max_length = keylen; + } + } + + return max_length; +} + static void lb_info_list_all(struct ctl_context *ctx, const char *lb_name, bool lb_check) { const struct nbrec_load_balancer *lb; struct smap lbs = SMAP_INITIALIZER(&lbs); + int vip_width = 0; + + NBREC_LOAD_BALANCER_FOR_EACH (lb, ctx->idl) { + if (lb_check && strcmp(lb->name, lb_name)) { + continue; + } + vip_width = lb_get_max_vip_length(lb, vip_width); + } NBREC_LOAD_BALANCER_FOR_EACH(lb, ctx->idl) { if (lb_check && strcmp(lb->name, lb_name)) { continue; } - lb_info_add_smap(lb, &lbs); + lb_info_add_smap(lb, &lbs, vip_width); } - lb_info_print(ctx, &lbs); + lb_info_print(ctx, &lbs, vip_width); smap_destroy(&lbs); } @@ -1837,15 +1914,21 @@ nbctl_lr_lb_list(struct ctl_context *ctx) const char *lr_name = ctx->argv[1]; const struct nbrec_logical_router *lr; struct smap lbs = SMAP_INITIALIZER(&lbs); + int vip_width = 0; lr = lr_by_name_or_uuid(ctx, lr_name, true); for (int i = 0; i < lr->n_load_balancer; i++) { const struct nbrec_load_balancer *lb = lr->load_balancer[i]; - lb_info_add_smap(lb, &lbs); + vip_width = lb_get_max_vip_length(lb, vip_width); + } + for (int i = 0; i < lr->n_load_balancer; i++) { + const struct nbrec_load_balancer *lb + = lr->load_balancer[i]; + lb_info_add_smap(lb, &lbs, vip_width); } - lb_info_print(ctx, &lbs); + lb_info_print(ctx, &lbs, vip_width); smap_destroy(&lbs); } @@ -1934,15 +2017,21 @@ nbctl_ls_lb_list(struct ctl_context *ctx) const char *ls_name = ctx->argv[1]; const struct nbrec_logical_switch *ls; struct smap lbs = SMAP_INITIALIZER(&lbs); + int vip_width = 0; ls = ls_by_name_or_uuid(ctx, ls_name, true); for (int i = 0; i < ls->n_load_balancer; i++) { const struct nbrec_load_balancer *lb = ls->load_balancer[i]; - lb_info_add_smap(lb, &lbs); + vip_width = lb_get_max_vip_length(lb, vip_width); + } + for (int i = 0; i < ls->n_load_balancer; i++) { + const struct nbrec_load_balancer *lb + = ls->load_balancer[i]; + lb_info_add_smap(lb, &lbs, vip_width); } - lb_info_print(ctx, &lbs); + lb_info_print(ctx, &lbs, vip_width); smap_destroy(&lbs); } diff --git a/tests/ovn-nbctl.at b/tests/ovn-nbctl.at index e1c4b4473..26b3ee9d8 100644 --- a/tests/ovn-nbctl.at +++ b/tests/ovn-nbctl.at @@ -399,47 +399,43 @@ OVN_NBCTL_TEST_START dnl Add two LBs. AT_CHECK([ovn-nbctl lb-add lb0 30.0.0.10:80a 192.168.10.10:80,192.168.10.20:80 tcp], [1], [], -[ovn-nbctl: 30.0.0.10:80a: should be an IPv4 address (or an IPv4 address and a port number with : as a separator). +[ovn-nbctl: 30.0.0.10:80a: should be an IP address (or an IP address and a port number with : as a separator). ]) AT_CHECK([ovn-nbctl lb-add lb0 30.0.0.10:a80 192.168.10.10:80,192.168.10.20:80 tcp], [1], [], -[ovn-nbctl: 30.0.0.10:a80: should be an IPv4 address (or an IPv4 address and a port number with : as a separator). +[ovn-nbctl: 30.0.0.10:a80: should be an IP address (or an IP address and a port number with : as a separator). ]) AT_CHECK([ovn-nbctl lb-add lb0 30.0.0.10: 192.168.10.10:80,192.168.10.20:80 tcp], [1], [], -[ovn-nbctl: 30.0.0.10:: should be an IPv4 address (or an IPv4 address and a port number with : as a separator). +[ovn-nbctl: 30.0.0.10:: should be an IP address (or an IP address and a port number with : as a separator). ]) AT_CHECK([ovn-nbctl lb-add lb0 30.0.0.10:80 192.168.10.10:80,192.168.10.20 tcp], [1], [], -[ovn-nbctl: 192.168.10.20: should be an IPv4 address and a port number with : as a separator. +[ovn-nbctl: 192.168.10.20: should be an IP address and a port number with : as a separator. ]) AT_CHECK([ovn-nbctl lb-add lb0 30.0.0.1a 192.168.10.10:80,192.168.10.20:80], [1], [], -[ovn-nbctl: 30.0.0.1a: should be an IPv4 address (or an IPv4 address and a port number with : as a separator). +[ovn-nbctl: 30.0.0.1a: should be an IP address (or an IP address and a port number with : as a separator). ]) AT_CHECK([ovn-nbctl lb-add lb0 30.0.0 192.168.10.10:80,192.168.10.20:80], [1], [], -[ovn-nbctl: 30.0.0: should be an IPv4 address (or an IPv4 address and a port number with : as a separator). +[ovn-nbctl: 192.168.10.10:80: should be an IP address. ]) AT_CHECK([ovn-nbctl lb-add lb0 30.0.0.10 192.168.10.10,192.168.10.20:80], [1], [], -[ovn-nbctl: 192.168.10.20:80: should be an IPv4 address. +[ovn-nbctl: 192.168.10.20:80: should be an IP address. ]) AT_CHECK([ovn-nbctl lb-add lb0 30.0.0.10 192.168.10.10:a80], [1], [], -[ovn-nbctl: 192.168.10.10:a80: should be an IPv4 address. +[ovn-nbctl: 192.168.10.10:a80: should be an IP address. ]) AT_CHECK([ovn-nbctl lb-add lb0 30.0.0.10 192.168.10.10:], [1], [], -[ovn-nbctl: 192.168.10.10:: should be an IPv4 address. +[ovn-nbctl: 192.168.10.10:: should be an IP address. ]) AT_CHECK([ovn-nbctl lb-add lb0 30.0.0.10 192.168.10.1a], [1], [], -[ovn-nbctl: 192.168.10.1a: should be an IPv4 address. -]) - -AT_CHECK([ovn-nbctl lb-add lb0 30.0.0.10 192.168.10], [1], [], -[ovn-nbctl: 192.168.10: should be an IPv4 address. +[ovn-nbctl: 192.168.10.1a: should be an IP address. ]) AT_CHECK([ovn-nbctl lb-add lb0 30.0.0.10 192.168.10.10 tcp], [1], [], @@ -454,9 +450,9 @@ dnl Add ips to lb AT_CHECK([ovn-nbctl lb-add lb0 30.0.0.10:80 ,,,192.168.10.10:80,,,,,]) AT_CHECK([ovn-nbctl lb-add lb1 30.0.0.10:80 ,,,192.168.10.10:80,,,,192.168.10.20:80,,,,]) AT_CHECK([ovn-nbctl lb-list | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl -UUID LB PROTO VIP IPs -<0> lb0 tcp 30.0.0.10:80 192.168.10.10:80 -<1> lb1 tcp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80 +UUID LB PROTO VIP IPs +<0> lb0 tcp 30.0.0.10:80 192.168.10.10:80 +<1> lb1 tcp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80 ]) AT_CHECK([ovn-nbctl lb-del lb0]) AT_CHECK([ovn-nbctl lb-del lb1]) @@ -464,51 +460,51 @@ AT_CHECK([ovn-nbctl lb-del lb1]) AT_CHECK([ovn-nbctl lb-add lb0 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80]) AT_CHECK([ovn-nbctl lb-add lb1 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80 tcp]) AT_CHECK([ovn-nbctl lb-list | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl -UUID LB PROTO VIP IPs -<0> lb0 tcp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80 -<1> lb1 tcp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80 +UUID LB PROTO VIP IPs +<0> lb0 tcp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80 +<1> lb1 tcp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80 ]) dnl Update the VIP of the lb1. AT_CHECK([ovn-nbctl --may-exist lb-add lb1 30.0.0.10:80 192.168.10.10:80,192.168.10.20:8080]) AT_CHECK([ovn-nbctl lb-list | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl -UUID LB PROTO VIP IPs -<0> lb0 tcp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80 -<1> lb1 tcp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:8080 +UUID LB PROTO VIP IPs +<0> lb0 tcp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80 +<1> lb1 tcp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:8080 ]) AT_CHECK([ovn-nbctl --may-exist lb-add lb1 30.0.0.10:80 192.168.10.10:80,192.168.10.20:8080 udp]) AT_CHECK([ovn-nbctl lb-list | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl -UUID LB PROTO VIP IPs -<0> lb0 tcp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80 -<1> lb1 udp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:8080 +UUID LB PROTO VIP IPs +<0> lb0 tcp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80 +<1> lb1 udp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:8080 ]) dnl Config lb1 with another VIP. AT_CHECK([ovn-nbctl lb-add lb1 30.0.0.20:80 192.168.10.10:80 udp]) AT_CHECK([ovn-nbctl lb-list | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl -UUID LB PROTO VIP IPs -<0> lb0 tcp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80 -<1> lb1 udp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:8080 - udp 30.0.0.20:80 192.168.10.10:80 +UUID LB PROTO VIP IPs +<0> lb0 tcp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80 +<1> lb1 udp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:8080 + udp 30.0.0.20:80 192.168.10.10:80 ]) AT_CHECK([ovn-nbctl lb-del lb1 30.0.0.20:80]) AT_CHECK([ovn-nbctl lb-list | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl -UUID LB PROTO VIP IPs -<0> lb0 tcp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80 -<1> lb1 udp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:8080 +UUID LB PROTO VIP IPs +<0> lb0 tcp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80 +<1> lb1 udp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:8080 ]) dnl Add LBs whose vip is just an IP address. AT_CHECK([ovn-nbctl lb-add lb2 30.0.0.30 192.168.10.10]) AT_CHECK([ovn-nbctl lb-add lb3 30.0.0.30 192.168.10.10]) AT_CHECK([ovn-nbctl lb-list | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl -UUID LB PROTO VIP IPs -<0> lb0 tcp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80 -<1> lb1 udp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:8080 -<2> lb2 tcp/udp 30.0.0.30 192.168.10.10 -<3> lb3 tcp/udp 30.0.0.30 192.168.10.10 +UUID LB PROTO VIP IPs +<0> lb0 tcp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80 +<1> lb1 udp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:8080 +<2> lb2 tcp/udp 30.0.0.30 192.168.10.10 +<3> lb3 tcp/udp 30.0.0.30 192.168.10.10 ]) AT_CHECK([ovn-nbctl lb-del lb2 30.0.0.30]) AT_CHECK([ovn-nbctl lb-del lb3 30.0.0.30]) @@ -516,11 +512,11 @@ AT_CHECK([ovn-nbctl lb-del lb3 30.0.0.30]) AT_CHECK([ovn-nbctl lb-add lb2 30.0.0.10:8080 192.168.10.10:80,192.168.10.20:80 tcp]) AT_CHECK([ovn-nbctl --add-duplicate lb-add lb2 30.0.0.10:8080 192.168.10.10:80,192.168.10.20:80 tcp]) AT_CHECK([ovn-nbctl lb-list | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl -UUID LB PROTO VIP IPs -<0> lb0 tcp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80 -<1> lb1 udp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:8080 -<2> lb2 tcp 30.0.0.10:8080 192.168.10.10:80,192.168.10.20:80 -<3> lb2 tcp 30.0.0.10:8080 192.168.10.10:80,192.168.10.20:80 +UUID LB PROTO VIP IPs +<0> lb0 tcp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80 +<1> lb1 udp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:8080 +<2> lb2 tcp 30.0.0.10:8080 192.168.10.10:80,192.168.10.20:80 +<3> lb2 tcp 30.0.0.10:8080 192.168.10.10:80,192.168.10.20:80 ]) dnl If there are multiple load balancers with the same name, use a UUID to update/delete. @@ -538,9 +534,9 @@ AT_CHECK([ovn-nbctl --may-exist lb-add lb1 30.0.0.10:9090 192.168.10.10:8080,192 AT_CHECK([ovn-nbctl lb-del lb0 30.0.0.10:80]) AT_CHECK([ovn-nbctl lb-del lb1]) AT_CHECK([ovn-nbctl lb-list | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl -UUID LB PROTO VIP IPs -<0> lb2 tcp 30.0.0.10:8080 192.168.10.10:80,192.168.10.20:80 -<1> lb2 tcp 30.0.0.10:8080 192.168.10.10:80,192.168.10.20:80 +UUID LB PROTO VIP IPs +<0> lb2 tcp 30.0.0.10:8080 192.168.10.10:80,192.168.10.20:80 +<1> lb2 tcp 30.0.0.10:8080 192.168.10.10:80,192.168.10.20:80 ]) dnl Add load balancer to logical switch. @@ -557,17 +553,17 @@ AT_CHECK([ovn-nbctl ls-lb-add ls0 lb2], [1], [], AT_CHECK([ovn-nbctl ls-lb-add ls0 lb3]) AT_CHECK([ovn-nbctl ls-lb-list ls0 | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl -UUID LB PROTO VIP IPs -<0> lb0 tcp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80 -<1> lb1 udp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80 -<2> lb3 tcp/udp 30.0.0.10 192.168.10.10,192.168.10.20 +UUID LB PROTO VIP IPs +<0> lb0 tcp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80 +<1> lb1 udp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80 +<2> lb3 tcp/udp 30.0.0.10 192.168.10.10,192.168.10.20 ]) AT_CHECK([ovn-nbctl ls-lb-del ls0 lb0]) AT_CHECK([ovn-nbctl ls-lb-list ls0 | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl -UUID LB PROTO VIP IPs -<0> lb1 udp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80 -<1> lb3 tcp/udp 30.0.0.10 192.168.10.10,192.168.10.20 +UUID LB PROTO VIP IPs +<0> lb1 udp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80 +<1> lb3 tcp/udp 30.0.0.10 192.168.10.10,192.168.10.20 ]) AT_CHECK([ovn-nbctl ls-lb-del ls0 lb1]) @@ -593,17 +589,17 @@ AT_CHECK([ovn-nbctl lr-lb-add lr0 lb2], [1], [], AT_CHECK([ovn-nbctl lr-lb-add lr0 lb3]) AT_CHECK([ovn-nbctl lr-lb-list lr0 | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl -UUID LB PROTO VIP IPs -<0> lb0 tcp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80 -<1> lb1 udp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80 -<2> lb3 tcp/udp 30.0.0.10 192.168.10.10,192.168.10.20 +UUID LB PROTO VIP IPs +<0> lb0 tcp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80 +<1> lb1 udp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80 +<2> lb3 tcp/udp 30.0.0.10 192.168.10.10,192.168.10.20 ]) AT_CHECK([ovn-nbctl lr-lb-del lr0 lb0]) AT_CHECK([ovn-nbctl lr-lb-list lr0 | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl -UUID LB PROTO VIP IPs -<0> lb1 udp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80 -<1> lb3 tcp/udp 30.0.0.10 192.168.10.10,192.168.10.20 +UUID LB PROTO VIP IPs +<0> lb1 udp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80 +<1> lb3 tcp/udp 30.0.0.10 192.168.10.10,192.168.10.20 ]) AT_CHECK([ovn-nbctl lr-lb-del lr0 lb1]) @@ -620,7 +616,270 @@ AT_CHECK([ovn-nbctl lr-lb-list lr0 | ${PERL} $srcdir/uuidfilt.pl], [0], []) OVN_NBCTL_TEST_STOP AT_CLEANUP +dnl +dnl --------------------------------------------------------------------- + +AT_SETUP([ovn-nbctl - LBs IPv6]) +OVN_NBCTL_TEST_START + +dnl A bunch of commands that should fail +AT_CHECK([ovn-nbctl lb-add lb0 [[ae0f::10]]:80a [[fd0f::10]]:80,[[fd0f::20]]:80 tcp], [1], [], +[ovn-nbctl: [[ae0f::10]]:80a: should be an IP address (or an IP address and a port number with : as a separator). +]) + + +AT_CHECK([ovn-nbctl lb-add lb0 [[ae0f::10]]:a80 [[fd0f::10]]:80,[[fd0f::20]]:80 tcp], [1], [], +[ovn-nbctl: [[ae0f::10]]:a80: should be an IP address (or an IP address and a port number with : as a separator). +]) + + +AT_CHECK([ovn-nbctl lb-add lb0 [[ae0f::10]]: [[fd0f::10]]:80,[[fd0f::20]]:80 tcp], [1], [], +[ovn-nbctl: [[ae0f::10]]:: should be an IP address (or an IP address and a port number with : as a separator). +]) + + +AT_CHECK([ovn-nbctl lb-add lb0 [[ae0f::10]]:80 [[fd0f::10]]:80,fd0f::20 tcp], [1], [], +[ovn-nbctl: fd0f::20: should be an IP address and a port number with : as a separator. +]) + + +AT_CHECK([ovn-nbctl lb-add lb0 ae0f::10fff [[fd0f::10]]:80,fd0f::20 tcp], [1], [], +[ovn-nbctl: ae0f::10fff: should be an IP address (or an IP address and a port number with : as a separator). +]) + + +AT_CHECK([ovn-nbctl lb-add lb0 ae0f::10 [[fd0f::10]]:80,[[fd0f::20]]:80], [1], [], +[ovn-nbctl: [[fd0f::10]]:80: should be an IP address. +]) + + +AT_CHECK([ovn-nbctl lb-add lb0 ae0f::10 fd0f::10,[[fd0f::20]]:80], [1], [], +[ovn-nbctl: [[fd0f::20]]:80: should be an IP address. +]) + + +AT_CHECK([ovn-nbctl lb-add lb0 ae0f::10 [[fd0f::10]]:a80], [1], [], +[ovn-nbctl: [[fd0f::10]]:a80: should be an IP address. +]) + + +AT_CHECK([ovn-nbctl lb-add lb0 ae0f::10 [[fd0f::10]]:], [1], [], +[ovn-nbctl: [[fd0f::10]]:: should be an IP address. +]) + + +AT_CHECK([ovn-nbctl lb-add lb0 ae0f::10 fd0f::1001a], [1], [], +[ovn-nbctl: fd0f::1001a: should be an IP address. +]) + + +AT_CHECK([ovn-nbctl lb-add lb0 ae0f::10 fd0f::10 tcp], [1], [], +[ovn-nbctl: Protocol is unnecessary when no port of vip is given. +]) + + +AT_CHECK([ovn-nbctl lb-add lb0 ae0f::10 [[fd0f::10]]:900 tcp], [1], [], +[ovn-nbctl: Protocol is unnecessary when no port of vip is given. +]) + +AT_CHECK([ovn-nbctl lb-add lb0 ae0f::10 192.168.10.10], [1], [], +[ovn-nbctl: 192.168.10.10: IP address family is different from VIP ae0f::10. +]) + +AT_CHECK([ovn-nbctl lb-add lb0 ae0f::10 192.168.10.10], [1], [], +[ovn-nbctl: 192.168.10.10: IP address family is different from VIP ae0f::10. +]) + +AT_CHECK([ovn-nbctl lb-add lb0 [[ae0f::10]]:80 192.168.10.10:80], [1], [], +[ovn-nbctl: 192.168.10.10:80: IP address family is different from VIP [[ae0f::10]]:80. +]) + +AT_CHECK([ovn-nbctl lb-add lb0 30.0.0.10 ae0f::10], [1], [], +[ovn-nbctl: ae0f::10: IP address family is different from VIP 30.0.0.10. +]) + +AT_CHECK([ovn-nbctl lb-add lb0 30.0.0.10:80 [[ae0f::10]]:80], [1], [], +[ovn-nbctl: [[ae0f::10]]:80: IP address family is different from VIP 30.0.0.10:80. +]) + +AT_CHECK([ovn-nbctl lb-add lb0 ae0f::10 fd0f::10]) +AT_CHECK([ovn-nbctl lb-add lb0 ae0f:0000:0000:0000:0000:0000:0000:0010 fd0f::20], +[1], [], [ovn-nbctl: lb0: a load balancer with this vip (ae0f::10) already exists +]) + +AT_CHECK([ovn-nbctl lb-del lb0]) + +dnl Add ips to lb +AT_CHECK([ovn-nbctl lb-add lb0 [[ae0f::10]]:80 ,,,[[fd0f::10]]:80,,,,,]) +AT_CHECK([ovn-nbctl lb-add lb1 [[ae0f::10]]:80 ,,,[[fd0f::10]]:80,,,,[[fd0f::20]]:80,,,,]) +AT_CHECK([ovn-nbctl lb-list | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl +UUID LB PROTO VIP IPs +<0> lb0 tcp [[ae0f::10]]:80 [[fd0f::10]]:80 +<1> lb1 tcp [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:80 +]) +AT_CHECK([ovn-nbctl lb-del lb0]) +AT_CHECK([ovn-nbctl lb-del lb1]) + + +AT_CHECK([ovn-nbctl lb-add lb0 [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:80]) +AT_CHECK([ovn-nbctl lb-add lb1 [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:80 tcp]) +AT_CHECK([ovn-nbctl lb-list | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl +UUID LB PROTO VIP IPs +<0> lb0 tcp [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:80 +<1> lb1 tcp [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:80 +]) + +dnl Update the VIP of the lb1. +AT_CHECK([ovn-nbctl --may-exist lb-add lb1 [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:8080]) +AT_CHECK([ovn-nbctl lb-list | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl +UUID LB PROTO VIP IPs +<0> lb0 tcp [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:80 +<1> lb1 tcp [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:8080 +]) + +AT_CHECK([ovn-nbctl --may-exist lb-add lb1 [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:8080 udp]) +AT_CHECK([ovn-nbctl lb-list | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl +UUID LB PROTO VIP IPs +<0> lb0 tcp [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:80 +<1> lb1 udp [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:8080 +]) + +dnl Config lb1 with another VIP. +AT_CHECK([ovn-nbctl lb-add lb1 [[ae0f::20]]:80 [[fd0f::10]]:80 udp]) +AT_CHECK([ovn-nbctl lb-list | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl +UUID LB PROTO VIP IPs +<0> lb0 tcp [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:80 +<1> lb1 udp [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:8080 + udp [[ae0f::20]]:80 [[fd0f::10]]:80 +]) + +AT_CHECK([ovn-nbctl lb-del lb1 [[ae0f::20]]:80]) +AT_CHECK([ovn-nbctl lb-list | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl +UUID LB PROTO VIP IPs +<0> lb0 tcp [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:80 +<1> lb1 udp [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:8080 +]) +dnl Add LBs whose vip is just an IP address. +AT_CHECK([ovn-nbctl lb-add lb2 ae0f::30 fd0f::10]) +AT_CHECK([ovn-nbctl lb-add lb3 ae0f::30 fd0f::10]) +AT_CHECK([ovn-nbctl lb-list | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl +UUID LB PROTO VIP IPs +<0> lb0 tcp [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:80 +<1> lb1 udp [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:8080 +<2> lb2 tcp/udp ae0f::30 fd0f::10 +<3> lb3 tcp/udp ae0f::30 fd0f::10 +]) +AT_CHECK([ovn-nbctl lb-del lb2 ae0f::30]) +AT_CHECK([ovn-nbctl lb-del lb3 ae0f::30]) + +AT_CHECK([ovn-nbctl lb-add lb2 [[ae0f::10]]:8080 [[fd0f::10]]:80,[[fd0f::20]]:80 tcp]) +AT_CHECK([ovn-nbctl --add-duplicate lb-add lb2 [[ae0f::10]]:8080 [[fd0f::10]]:80,[[fd0f::20]]:80 tcp]) +AT_CHECK([ovn-nbctl lb-list | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl +UUID LB PROTO VIP IPs +<0> lb0 tcp [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:80 +<1> lb1 udp [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:8080 +<2> lb2 tcp [[ae0f::10]]:8080 [[fd0f::10]]:80,[[fd0f::20]]:80 +<3> lb2 tcp [[ae0f::10]]:8080 [[fd0f::10]]:80,[[fd0f::20]]:80 +]) + +dnl If there are multiple load balancers with the same name, use a UUID to update/delete. +AT_CHECK([ovn-nbctl lb-add lb2 [[ae0f::10]]:8080 [[fd0f::10]]:80,[[fd0f::20]]:80 tcp], [1], [], +[ovn-nbctl: Multiple load balancers named 'lb2'. Use a UUID. +]) + +AT_CHECK([ovn-nbctl lb-del lb2], [1], [], +[ovn-nbctl: Multiple load balancers named 'lb2'. Use a UUID. +]) + +AT_CHECK([ovn-nbctl --may-exist lb-add lb1 [[ae0f::10]]:80 [[fd0f::10]]:8080,[[fd0f::20]]:8080 udp]) +AT_CHECK([ovn-nbctl --may-exist lb-add lb1 [[ae0f::10]]:8080 [[fd0f::10]]:8080,[[fd0f::20]]:8080 udp]) +AT_CHECK([ovn-nbctl --may-exist lb-add lb1 [[ae0f::10]]:9090 [[fd0f::10]]:8080,[[fd0f::20]]:8080 udp]) +AT_CHECK([ovn-nbctl lb-del lb0 [[ae0f::10]]:80]) +AT_CHECK([ovn-nbctl lb-del lb1]) +AT_CHECK([ovn-nbctl lb-list | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl +UUID LB PROTO VIP IPs +<0> lb2 tcp [[ae0f::10]]:8080 [[fd0f::10]]:80,[[fd0f::20]]:80 +<1> lb2 tcp [[ae0f::10]]:8080 [[fd0f::10]]:80,[[fd0f::20]]:80 +]) + +dnl Add load balancer to logical switch. +AT_CHECK([ovn-nbctl ls-add ls0]) +AT_CHECK([ovn-nbctl lb-add lb0 [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:80]) +AT_CHECK([ovn-nbctl lb-add lb1 [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:80 udp]) +AT_CHECK([ovn-nbctl lb-add lb3 ae0f::10 fd0f::10,fd0f::20]) +AT_CHECK([ovn-nbctl ls-lb-add ls0 lb0]) +AT_CHECK([ovn-nbctl ls-lb-add ls0 lb1]) +AT_CHECK([ovn-nbctl --may-exist ls-lb-add ls0 lb1]) +AT_CHECK([ovn-nbctl ls-lb-add ls0 lb2], [1], [], +[ovn-nbctl: Multiple load balancers named 'lb2'. Use a UUID. +]) +AT_CHECK([ovn-nbctl ls-lb-add ls0 lb3]) + +AT_CHECK([ovn-nbctl ls-lb-list ls0 | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl +UUID LB PROTO VIP IPs +<0> lb0 tcp [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:80 +<1> lb1 udp [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:80 +<2> lb3 tcp/udp ae0f::10 fd0f::10,fd0f::20 +]) + +AT_CHECK([ovn-nbctl ls-lb-del ls0 lb0]) +AT_CHECK([ovn-nbctl ls-lb-list ls0 | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl +UUID LB PROTO VIP IPs +<0> lb1 udp [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:80 +<1> lb3 tcp/udp ae0f::10 fd0f::10,fd0f::20 +]) + +AT_CHECK([ovn-nbctl ls-lb-del ls0 lb1]) +AT_CHECK([ovn-nbctl ls-lb-del ls0 lb3]) +AT_CHECK([ovn-nbctl ls-lb-list ls0 | ${PERL} $srcdir/uuidfilt.pl], [0], []) +AT_CHECK([ovn-nbctl --if-exists ls-lb-del ls0 lb1]) + +dnl Remove all load balancers from logical switch. +AT_CHECK([ovn-nbctl ls-lb-add ls0 lb0]) +AT_CHECK([ovn-nbctl ls-lb-add ls0 lb1]) +AT_CHECK([ovn-nbctl ls-lb-add ls0 lb3]) +AT_CHECK([ovn-nbctl ls-lb-del ls0]) +AT_CHECK([ovn-nbctl ls-lb-list ls0 | ${PERL} $srcdir/uuidfilt.pl], [0], []) + +dnl Add load balancer to logical router. +AT_CHECK([ovn-nbctl lr-add lr0]) +AT_CHECK([ovn-nbctl lr-lb-add lr0 lb0]) +AT_CHECK([ovn-nbctl lr-lb-add lr0 lb1]) +AT_CHECK([ovn-nbctl --may-exist lr-lb-add lr0 lb1]) +AT_CHECK([ovn-nbctl lr-lb-add lr0 lb2], [1], [], +[ovn-nbctl: Multiple load balancers named 'lb2'. Use a UUID. +]) +AT_CHECK([ovn-nbctl lr-lb-add lr0 lb3]) + +AT_CHECK([ovn-nbctl lr-lb-list lr0 | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl +UUID LB PROTO VIP IPs +<0> lb0 tcp [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:80 +<1> lb1 udp [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:80 +<2> lb3 tcp/udp ae0f::10 fd0f::10,fd0f::20 +]) + +AT_CHECK([ovn-nbctl lr-lb-del lr0 lb0]) +AT_CHECK([ovn-nbctl lr-lb-list lr0 | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl +UUID LB PROTO VIP IPs +<0> lb1 udp [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:80 +<1> lb3 tcp/udp ae0f::10 fd0f::10,fd0f::20 +]) + +AT_CHECK([ovn-nbctl lr-lb-del lr0 lb1]) +AT_CHECK([ovn-nbctl lr-lb-del lr0 lb3]) +AT_CHECK([ovn-nbctl lr-lb-list lr0 | ${PERL} $srcdir/uuidfilt.pl], [0], []) +AT_CHECK([ovn-nbctl --if-exists lr-lb-del lr0 lb1]) + +dnl Remove all load balancers from logical router. +AT_CHECK([ovn-nbctl lr-lb-add lr0 lb0]) +AT_CHECK([ovn-nbctl lr-lb-add lr0 lb1]) +AT_CHECK([ovn-nbctl lr-lb-add lr0 lb3]) +AT_CHECK([ovn-nbctl lr-lb-del lr0]) +AT_CHECK([ovn-nbctl lr-lb-list lr0 | ${PERL} $srcdir/uuidfilt.pl], [0], []) + +OVN_NBCTL_TEST_STOP +AT_CLEANUP dnl --------------------------------------------------------------------- AT_SETUP([ovn-nbctl - basic logical router commands])