From patchwork Fri Apr 13 17:26:50 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ben Pfaff X-Patchwork-Id: 898038 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=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=ovn.org 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 40N4XG3m3bz9s0t for ; Sat, 14 Apr 2018 03:29:34 +1000 (AEST) Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id 83BB9DEB; Fri, 13 Apr 2018 17:27:14 +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 3AECADA6 for ; Fri, 13 Apr 2018 17:27:11 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.7.6 Received: from relay1-d.mail.gandi.net (relay1-d.mail.gandi.net [217.70.183.193]) by smtp1.linuxfoundation.org (Postfix) with ESMTPS id 7DDA3672 for ; Fri, 13 Apr 2018 17:27:09 +0000 (UTC) X-Originating-IP: 208.91.3.26 Received: from sigabrt.benpfaff.org (unknown [208.91.3.26]) (Authenticated sender: blp@ovn.org) by relay1-d.mail.gandi.net (Postfix) with ESMTPSA id 03006240012; Fri, 13 Apr 2018 19:27:07 +0200 (CEST) From: Ben Pfaff To: dev@openvswitch.org Date: Fri, 13 Apr 2018 10:26:50 -0700 Message-Id: <20180413172655.31638-6-blp@ovn.org> X-Mailer: git-send-email 2.16.1 In-Reply-To: <20180413172655.31638-1-blp@ovn.org> References: <20180413172655.31638-1-blp@ovn.org> X-Spam-Status: No, score=-2.6 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_LOW autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on smtp1.linux-foundation.org Cc: Ben Pfaff Subject: [ovs-dev] [PATCH 06/11] Make : parsing uniform treewide. 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 I didn't realize until now that the tree had two different ways of parsing strings in the form : and :. There are the long-standing inet_parse_active() and inet_parse_passive() functions, and more recently the ipv46_parse() function. This commit eliminates the latter and changes the code to use the former. The two implementations interpreted some input differently. In particular, the older functions required IPv6 addresses to be [bracketed], but the newer ones did not. I'd prefer the brackets to be required, because of ambiguous cases like "::1:2:3:4:80" (is :80 part of the IPv6 address or a port number?) but for compatibility this patch changes the merged code to use the more liberal interpretation. Signed-off-by: Ben Pfaff --- lib/packets.c | 76 ------------------------- lib/packets.h | 10 ---- lib/socket-util.c | 140 +++++++++++++++++++++++++++------------------- lib/socket-util.h | 3 +- ovn/utilities/ovn-nbctl.c | 43 +++----------- ovsdb/raft.c | 5 +- tests/ovn-nbctl.at | 56 +++++++++---------- 7 files changed, 125 insertions(+), 208 deletions(-) diff --git a/lib/packets.c b/lib/packets.c index 8ebae8cc8fe8..38bfb6015b9e 100644 --- a/lib/packets.c +++ b/lib/packets.c @@ -652,82 +652,6 @@ 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 9a71aa3abbdb..b2bf70697e90 100644 --- a/lib/packets.h +++ b/lib/packets.h @@ -1362,16 +1362,6 @@ 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); diff --git a/lib/socket-util.c b/lib/socket-util.c index 223e3780ba5f..0964a015e3f9 100644 --- a/lib/socket-util.c +++ b/lib/socket-util.c @@ -333,39 +333,89 @@ guess_netmask(ovs_be32 ip_) : htonl(0)); /* ??? */ } -/* This is like strsep() except: - * - * - The separator string is ":". - * - * - Square brackets [] quote ":" separators and are removed from the - * tokens. */ -char * -inet_parse_token(char **pp) +static char * +unbracket(char *s) +{ + if (*s == '[') { + s++; + + char *end = strchr(s, '\0'); + if (end[-1] == ']') { + end[-1] = '\0'; + } + } + return s; +} + +/* 'host_index' is 0 if the host precedes the port within 's', 1 otherwise. */ +static void +inet_parse_tokens__(char *s, int host_index, char **hostp, char **portp) { - char *p = *pp; - - if (p == NULL) { - return NULL; - } else if (*p == '\0') { - *pp = NULL; - return p; - } else if (*p == '[') { - char *start = p + 1; - char *end = start + strcspn(start, "]"); - *pp = (*end == '\0' ? NULL - : end[1] == ':' ? end + 2 - : end + 1); - *end = '\0'; - return start; + char *colon = NULL; + bool in_brackets = false; + int n_colons = 0; + for (char *p = s; *p; p++) { + if (*p == '[') { + in_brackets = true; + } else if (*p == ']') { + in_brackets = false; + } else if (*p == ':' && !in_brackets) { + n_colons++; + colon = p; + } + } + + *hostp = *portp = NULL; + if (n_colons > 1) { + *hostp = s; } else { - char *start = p; - char *end = start + strcspn(start, ":"); - *pp = *end == '\0' ? NULL : end + 1; - *end = '\0'; - return start; + char **tokens[2]; + tokens[host_index] = hostp; + tokens[!host_index] = portp; + + if (colon) { + *colon = '\0'; + *tokens[1] = unbracket(colon + 1); + } + *tokens[0] = unbracket(s); } } +/* Parses 's', a string in the form "[:]", into its (required) host + * and (optional) port components, and stores pointers to them in '*hostp' and + * '*portp' respectively. Always sets '*hostp' nonnull, although possibly to + * an empty string empty string. Can set '*portp' to the null string. + * + * Supports both IPv4 and IPv6. IPv6 addresses may be quoted with square + * brackets. Resolves ambiguous cases that might represent an IPv6 address or + * an IPv6 address and a port as representing just a host, e.g. "::1:2:3:4:80" + * is a host but "[::1:2:3:4]:80" is a host and a port. + * + * Modifies 's' and points '*hostp' and '*portp' (if nonnull) into it. + */ +void +inet_parse_host_port_tokens(char *s, char **hostp, char **portp) +{ + inet_parse_tokens__(s, 0, hostp, portp); +} + +/* Parses 's', a string in the form "[:]", into its port and host + * components, and stores pointers to them in '*portp' and '*hostp' + * respectively. Both '*portp' and '*hostp' can end up null. + * + * Supports both IPv4 and IPv6. IPv6 addresses may be quoted with square + * brackets. Resolves ambiguous cases that might represent an IPv6 address or + * an IPv6 address and a port as representing just a host, e.g. "::1:2:3:4:80" + * is a host but "[::1:2:3:4]:80" is a host and a port. + * + * Modifies 's' and points '*hostp' and '*portp' (if nonnull) into it. + */ +void +inet_parse_port_host_tokens(char *s, char **portp, char **hostp) +{ + inet_parse_tokens__(s, 1, hostp, portp); +} + static bool parse_sockaddr_components(struct sockaddr_storage *ss, char *host_s, @@ -441,23 +491,16 @@ inet_parse_active(const char *target_, int default_port, struct sockaddr_storage *ss) { char *target = xstrdup(target_); - const char *port; - char *host; - char *p; + char *port, *host; bool ok; - p = target; - host = inet_parse_token(&p); - port = inet_parse_token(&p); + inet_parse_host_port_tokens(target, &host, &port); if (!host) { VLOG_ERR("%s: host must be specified", target_); ok = false; } else if (!port && default_port < 0) { VLOG_ERR("%s: port must be specified", target_); ok = false; - } else if (p && p[strspn(p, " \t\r\n")] != '\0') { - VLOG_ERR("%s: unexpected characters follow host and port", target_); - ok = false; } else { ok = parse_sockaddr_components(ss, host, port, default_port, target_); } @@ -571,20 +614,13 @@ inet_parse_passive(const char *target_, int default_port, struct sockaddr_storage *ss) { char *target = xstrdup(target_); - const char *port; - char *host; - char *p; + char *port, *host; bool ok; - p = target; - port = inet_parse_token(&p); - host = inet_parse_token(&p); + inet_parse_port_host_tokens(target, &port, &host); if (!port && default_port < 0) { VLOG_ERR("%s: port must be specified", target_); ok = false; - } else if (p && p[strspn(p, " \t\r\n")] != '\0') { - VLOG_ERR("%s: unexpected characters follow port and host", target_); - ok = false; } else { ok = parse_sockaddr_components(ss, host, port, default_port, target_); } @@ -707,16 +743,8 @@ bool inet_parse_address(const char *target_, struct sockaddr_storage *ss) { char *target = xstrdup(target_); - char *p = target; - char *host = inet_parse_token(&p); - bool ok = false; - if (!host) { - VLOG_ERR("%s: host must be specified", target_); - } else if (p && p[strspn(p, " \t\r\n")] != '\0') { - VLOG_ERR("%s: unexpected characters follow host", target_); - } else { - ok = parse_sockaddr_components(ss, host, NULL, 0, target_); - } + char *host = unbracket(target); + bool ok = parse_sockaddr_components(ss, host, NULL, 0, target_); if (!ok) { memset(ss, 0, sizeof *ss); } diff --git a/lib/socket-util.h b/lib/socket-util.h index d927d67a0e1b..239d3f22041c 100644 --- a/lib/socket-util.h +++ b/lib/socket-util.h @@ -46,7 +46,8 @@ int check_connection_completion(int fd); void drain_fd(int fd, size_t n_packets); ovs_be32 guess_netmask(ovs_be32 ip); -char *inet_parse_token(char **); +void inet_parse_host_port_tokens(char *s, char **hostp, char **portp); +void inet_parse_port_host_tokens(char *s, char **portp, char **hostp); bool inet_parse_active(const char *target, int default_port, struct sockaddr_storage *ssp); int inet_open_active(int style, const char *target, int default_port, diff --git a/ovn/utilities/ovn-nbctl.c b/ovn/utilities/ovn-nbctl.c index 7efc5065c962..16698de1f049 100644 --- a/ovn/utilities/ovn-nbctl.c +++ b/ovn/utilities/ovn-nbctl.c @@ -1798,10 +1798,7 @@ nbctl_lb_add(struct ctl_context *ctx) } struct sockaddr_storage ss_vip; - char *error; - error = ipv46_parse(lb_vip, PORT_OPTIONAL, &ss_vip); - if (error) { - free(error); + if (!inet_parse_active(lb_vip, 0, &ss_vip)) { ctl_fatal("%s: should be an IP address (or an IP address " "and a port number with : as a separator).", lb_vip); } @@ -1848,17 +1845,13 @@ nbctl_lb_add(struct ctl_context *ctx) token != NULL; token = strtok_r(NULL, ",", &save_ptr)) { 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) { + if (is_vip_with_port) { + if (!inet_parse_active(token, -1, &ss_dst)) { ctl_fatal("%s: should be an IP address and a port " - "number with : as a separator.", token); - } else { + "number with : as a separator.", token); + } + } else { + if (!inet_parse_address(token, &ss_dst)) { ctl_fatal("%s: should be an IP address.", token); } } @@ -1954,36 +1947,18 @@ lb_info_add_smap(const struct nbrec_load_balancer *lb, { struct ds key = DS_EMPTY_INITIALIZER; struct ds val = DS_EMPTY_INITIALIZER; - char *error, *protocol; 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; struct sockaddr_storage ss; - error = ipv46_parse(node->key, PORT_OPTIONAL, &ss); - if (error) { - VLOG_WARN("%s", error); - free(error); + if (!inet_parse_active(node->key, 0, &ss)) { 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"; - } - } - + char *protocol = ss_get_port(&ss) ? lb->protocol : "tcp/udp"; i == 0 ? ds_put_format(&val, UUID_FMT " %-20.16s%-11.7s%-*.*s%s", UUID_ARGS(&lb->header_.uuid), diff --git a/ovsdb/raft.c b/ovsdb/raft.c index edb22f2e7b60..c0c1e98977b9 100644 --- a/ovsdb/raft.c +++ b/ovsdb/raft.c @@ -335,9 +335,8 @@ raft_make_address_passive(const char *address_) return xasprintf("p%s", address_); } else { char *address = xstrdup(address_); - char *p = strchr(address, ':') + 1; - char *host = inet_parse_token(&p); - char *port = inet_parse_token(&p); + char *host, *port; + inet_parse_host_port_tokens(strchr(address, ':') + 1, &host, &port); struct ds paddr = DS_EMPTY_INITIALIZER; ds_put_format(&paddr, "p%.3s:%s:", address, port); diff --git a/tests/ovn-nbctl.at b/tests/ovn-nbctl.at index f07b47fd20ea..514e7e7d2d49 100644 --- a/tests/ovn-nbctl.at +++ b/tests/ovn-nbctl.at @@ -460,46 +460,46 @@ AT_SETUP([ovn-nbctl - LBs]) 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], [], +AT_CHECK([ovn-nbctl -vsocket_util:off 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 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], [], +AT_CHECK([ovn-nbctl -vsocket_util:off 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 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 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], [], +AT_CHECK([ovn-nbctl -vsocket_util:off 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 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], [], +AT_CHECK([ovn-nbctl -vsocket_util:off 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 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: 192.168.10.10:80: should be an IP address. +AT_CHECK([ovn-nbctl -vsocket_util:off 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 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,192.168.10.20:80], [1], [], +AT_CHECK([ovn-nbctl -vsocket_util:off 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 IP address. ]) -AT_CHECK([ovn-nbctl lb-add lb0 30.0.0.10 192.168.10.10:a80], [1], [], +AT_CHECK([ovn-nbctl -vsocket_util:off lb-add lb0 30.0.0.10 192.168.10.10:a80], [1], [], [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], [], +AT_CHECK([ovn-nbctl -vsocket_util:off lb-add lb0 30.0.0.10 192.168.10.10:], [1], [], [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], [], +AT_CHECK([ovn-nbctl -vsocket_util:off lb-add lb0 30.0.0.10 192.168.10.1a], [1], [], [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:80,192.168.10.20:80 tcp], [1], [], +[ovn-nbctl: Protocol is unnecessary when no port of vip is given. +]) + AT_CHECK([ovn-nbctl lb-add lb0 30.0.0.10 192.168.10.10 tcp], [1], [], [ovn-nbctl: Protocol is unnecessary when no port of vip is given. ]) @@ -685,56 +685,56 @@ 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], [], +AT_CHECK([ovn-nbctl -vsocket_util:off 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], [], +AT_CHECK([ovn-nbctl -vsocket_util:off 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], [], +AT_CHECK([ovn-nbctl -vsocket_util:off 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], [], +AT_CHECK([ovn-nbctl -vsocket_util:off 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], [], +AT_CHECK([ovn-nbctl -vsocket_util:off 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], [], +AT_CHECK([ovn-nbctl -vsocket_util:off 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], [], +AT_CHECK([ovn-nbctl -vsocket_util:off 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], [], +AT_CHECK([ovn-nbctl -vsocket_util:off 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], [], +AT_CHECK([ovn-nbctl -vsocket_util:off lb-add lb0 ae0f::10 fd0f::1001a], [1], [], [ovn-nbctl: fd0f::1001a: should be an IP address. ]) +AT_CHECK([ovn-nbctl -vsocket_util:off lb-add lb0 [[ae0f::10]]: [[fd0f::10]]:80,[[fd0f::20]]:80 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 tcp], [1], [], [ovn-nbctl: Protocol is unnecessary when no port of vip is given. ])