From patchwork Wed Sep 6 21:08:36 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mark Michelson X-Patchwork-Id: 810802 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 3xnbmz2HyVz9t2r for ; Thu, 7 Sep 2017 07:09:23 +1000 (AEST) Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id 5C13EB78; Wed, 6 Sep 2017 21:08:45 +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 F0670B4C for ; Wed, 6 Sep 2017 21:08:42 +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 981F048E for ; Wed, 6 Sep 2017 21:08:42 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.phx2.redhat.com [10.5.11.14]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 0B5048535D for ; Wed, 6 Sep 2017 21:08:42 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mx1.redhat.com 0B5048535D 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-121-224.rdu2.redhat.com [10.10.121.224]) by smtp.corp.redhat.com (Postfix) with ESMTP id 75E92179D3 for ; Wed, 6 Sep 2017 21:08:41 +0000 (UTC) From: Mark Michelson To: dev@openvswitch.org Date: Wed, 6 Sep 2017 16:08:36 -0500 Message-Id: <20170906210839.26091-2-mmichels@redhat.com> In-Reply-To: <20170906210839.26091-1-mmichels@redhat.com> References: <20170906210839.26091-1-mmichels@redhat.com> X-Scanned-By: MIMEDefang 2.79 on 10.5.11.14 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.25]); Wed, 06 Sep 2017 21:08:42 +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 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 705d0b270..866a3335a 100644 --- a/lib/packets.h +++ b/lib/packets.h @@ -1325,6 +1325,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 Wed Sep 6 21:08:37 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mark Michelson X-Patchwork-Id: 810803 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 3xnbnp3hzcz9t2v for ; Thu, 7 Sep 2017 07:10:06 +1000 (AEST) Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id 5102DB7C; Wed, 6 Sep 2017 21:08:46 +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 EB36BB4A for ; Wed, 6 Sep 2017 21:08:43 +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 8053F48E for ; Wed, 6 Sep 2017 21:08:43 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.phx2.redhat.com [10.5.11.14]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id E4629883A0 for ; Wed, 6 Sep 2017 21:08:42 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mx1.redhat.com E4629883A0 Authentication-Results: ext-mx02.extmail.prod.ext.phx2.redhat.com; dmarc=none (p=none dis=none) header.from=redhat.com Authentication-Results: ext-mx02.extmail.prod.ext.phx2.redhat.com; spf=fail smtp.mailfrom=mmichels@redhat.com Received: from monae.redhat.com (ovpn-121-224.rdu2.redhat.com [10.10.121.224]) by smtp.corp.redhat.com (Postfix) with ESMTP id 61069179D3 for ; Wed, 6 Sep 2017 21:08:42 +0000 (UTC) From: Mark Michelson To: dev@openvswitch.org Date: Wed, 6 Sep 2017 16:08:37 -0500 Message-Id: <20170906210839.26091-3-mmichels@redhat.com> In-Reply-To: <20170906210839.26091-1-mmichels@redhat.com> References: <20170906210839.26091-1-mmichels@redhat.com> X-Scanned-By: MIMEDefang 2.79 on 10.5.11.14 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.26]); Wed, 06 Sep 2017 21:08:43 +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 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 | 4 ++- ovn/lib/actions.c | 99 ++++++++++++++++++++++++++++++++++++++++----------- tests/ovn.at | 8 ++++- 3 files changed, 89 insertions(+), 22 deletions(-) diff --git a/include/ovn/actions.h b/include/ovn/actions.h index 0a04af7aa..ec0063efc 100644 --- a/include/ovn/actions.h +++ b/include/ovn/actions.h @@ -200,7 +200,9 @@ struct ovnact_ct_nat { }; struct ovnact_ct_lb_dst { - ovs_be32 ip; + int family; + struct in6_addr ipv6; + ovs_be32 ipv4; uint16_t port; }; diff --git a/ovn/lib/actions.c b/ovn/lib/actions.c index d0d73b69c..8d60529cb 100644 --- a/ovn/lib/actions.c +++ b/ovn/lib/actions.c @@ -883,23 +883,63 @@ 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. */ + struct ovnact_ct_lb_dst dst; + 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 +947,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 +969,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 +1041,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 fb9fc7352..9118f2839 100644 --- a/tests/ovn.at +++ b/tests/ovn.at @@ -793,13 +793,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 Wed Sep 6 21:08:38 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mark Michelson X-Patchwork-Id: 810804 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 3xnbpL5gzlz9t3F for ; Thu, 7 Sep 2017 07:10:34 +1000 (AEST) Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id 2E05BB9B; Wed, 6 Sep 2017 21:08:47 +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 1A878B4C for ; Wed, 6 Sep 2017 21:08:45 +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 672A9488 for ; Wed, 6 Sep 2017 21:08:44 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.phx2.redhat.com [10.5.11.14]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id D1F72859F7 for ; Wed, 6 Sep 2017 21:08:43 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mx1.redhat.com D1F72859F7 Authentication-Results: ext-mx02.extmail.prod.ext.phx2.redhat.com; dmarc=none (p=none dis=none) header.from=redhat.com Authentication-Results: ext-mx02.extmail.prod.ext.phx2.redhat.com; spf=fail smtp.mailfrom=mmichels@redhat.com Received: from monae.redhat.com (ovpn-121-224.rdu2.redhat.com [10.10.121.224]) by smtp.corp.redhat.com (Postfix) with ESMTP id 4D913179D3 for ; Wed, 6 Sep 2017 21:08:43 +0000 (UTC) From: Mark Michelson To: dev@openvswitch.org Date: Wed, 6 Sep 2017 16:08:38 -0500 Message-Id: <20170906210839.26091-4-mmichels@redhat.com> In-Reply-To: <20170906210839.26091-1-mmichels@redhat.com> References: <20170906210839.26091-1-mmichels@redhat.com> X-Scanned-By: MIMEDefang 2.79 on 10.5.11.14 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.26]); Wed, 06 Sep 2017 21:08:43 +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 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. Signed-off-by: Mark Michelson --- ovn/northd/ovn-northd.c | 170 ++++++++++++++++++++++++++++-------------------- 1 file changed, 101 insertions(+), 69 deletions(-) diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c index 49e4ac338..c8b4e4d4f 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) { @@ -2889,44 +2891,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; } /* @@ -2954,6 +2945,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; @@ -2965,7 +2957,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; } @@ -2987,7 +2980,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); @@ -3445,10 +3444,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; } @@ -3456,7 +3457,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); @@ -4564,36 +4569,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)); } @@ -5257,16 +5281,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 @@ -5278,8 +5322,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); if (port) { @@ -5298,23 +5347,6 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, } } } - - /* 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); } From patchwork Wed Sep 6 21:08:39 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mark Michelson X-Patchwork-Id: 810805 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 3xnbqk4CHYz9t2r for ; Thu, 7 Sep 2017 07:11:46 +1000 (AEST) Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id 66250BAD; Wed, 6 Sep 2017 21:08:49 +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 834D3B9F for ; Wed, 6 Sep 2017 21:08:47 +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 8A3AE48F for ; Wed, 6 Sep 2017 21:08:45 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.phx2.redhat.com [10.5.11.14]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 14BF65F7AE for ; Wed, 6 Sep 2017 21:08:45 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mx1.redhat.com 14BF65F7AE 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-121-224.rdu2.redhat.com [10.10.121.224]) by smtp.corp.redhat.com (Postfix) with ESMTP id 3A63D17C6F for ; Wed, 6 Sep 2017 21:08:44 +0000 (UTC) From: Mark Michelson To: dev@openvswitch.org Date: Wed, 6 Sep 2017 16:08:39 -0500 Message-Id: <20170906210839.26091-5-mmichels@redhat.com> In-Reply-To: <20170906210839.26091-1-mmichels@redhat.com> References: <20170906210839.26091-1-mmichels@redhat.com> X-Scanned-By: MIMEDefang 2.79 on 10.5.11.14 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.39]); Wed, 06 Sep 2017 21:08:45 +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 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.c | 175 +++++++++++++++------ tests/ovn-nbctl.at | 379 ++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 451 insertions(+), 103 deletions(-) diff --git a/ovn/utilities/ovn-nbctl.c b/ovn/utilities/ovn-nbctl.c index 46ede4e50..93e1bfc81 100644 --- a/ovn/utilities/ovn-nbctl.c +++ b/ovn/utilities/ovn-nbctl.c @@ -1548,40 +1548,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); @@ -1591,19 +1627,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. */ @@ -1623,7 +1659,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); } @@ -1665,32 +1701,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); } @@ -1704,12 +1757,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); @@ -1719,21 +1772,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); } @@ -1832,15 +1909,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); } @@ -1929,15 +2012,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 354b8df96..3f39af2a3 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])