From: Martin Belanger Date: Wed, 6 Oct 2021 18:39:56 +0000 (-0400) Subject: Add support for scoped IPv6 X-Git-Tag: v1.0-rc0~96^2 X-Git-Url: https://www.infradead.org/git/?a=commitdiff_plain;h=abdd8bec59c5049bf0f412e9e391918f84c54f74;p=users%2Fsagi%2Flibnvme.git Add support for scoped IPv6 --- diff --git a/src/nvme/fabrics.c b/src/nvme/fabrics.c index ebfc6356..319d66f7 100644 --- a/src/nvme/fabrics.c +++ b/src/nvme/fabrics.c @@ -23,6 +23,7 @@ #include #include #include +#include #ifdef CONFIG_SYSTEMD #include @@ -215,16 +216,117 @@ static int add_argument(char **argstr, const char *tok, const char *arg) return 0; } +static int inet4_pton(const char *src, uint16_t port, + struct sockaddr_storage *addr) +{ + struct sockaddr_in *addr4 = (struct sockaddr_in *)addr; + + if (strlen(src) > INET_ADDRSTRLEN) + return -EINVAL; + + if (inet_pton(AF_INET, src, &addr4->sin_addr.s_addr) <= 0) + return -EINVAL; + + addr4->sin_family = AF_INET; + addr4->sin_port = htons(port); + + return 0; +} + +static int inet6_pton(const char *src, uint16_t port, + struct sockaddr_storage *addr) +{ + int ret = -EINVAL; + struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)addr; + + if (strlen(src) > INET6_ADDRSTRLEN) + return -EINVAL; + + char *tmp = strdup(src); + if (!tmp) + nvme_msg(LOG_ERR, "cannot copy: %s\n", src); + + const char *scope = NULL; + char *p = strchr(tmp, SCOPE_DELIMITER); + if (p) { + *p = '\0'; + scope = src + (p - tmp) + 1; + } + + if (inet_pton(AF_INET6, tmp, &addr6->sin6_addr) != 1) + goto free_tmp; + + if (IN6_IS_ADDR_LINKLOCAL(&addr6->sin6_addr) && scope) { + addr6->sin6_scope_id = if_nametoindex(scope); + if (addr6->sin6_scope_id == 0) { + nvme_msg(LOG_ERR, + "can't find iface index for: %s (%m)\n", scope); + goto free_tmp; + } + } + + addr6->sin6_family = AF_INET6; + addr6->sin6_port = htons(port); + ret = 0; + +free_tmp: + free(tmp); + return ret; +} + +/** + * inet_pton_with_scope - convert an IPv4/IPv6 to socket address + * @af: address family, AF_INET, AF_INET6 or AF_UNSPEC for either + * @src: the start of the address string + * @addr: output socket address + * + * Return 0 on success, errno otherwise. + */ +static int inet_pton_with_scope(int af, const char *src, const char * trsvcid, + struct sockaddr_storage *addr) +{ + int ret = -EINVAL; + uint16_t port = 0; + + if (trsvcid) { + unsigned long long tmp = strtoull(trsvcid, NULL, 0); + port = (uint16_t)tmp; + if (tmp != port) { + nvme_msg(LOG_ERR, "trsvcid out of range: %s\n", trsvcid); + return -ERANGE; + } + } else { + port = 0; + } + + switch (af) { + case AF_INET: + ret = inet4_pton(src, port, addr); + break; + case AF_INET6: + ret = inet6_pton(src, port, addr); + break; + case AF_UNSPEC: + ret = inet4_pton(src, port, addr); + if (ret) + ret = inet6_pton(src, port, addr); + break; + default: + nvme_msg(LOG_ERR, "unexpected address family %d\n", af); + } + + return ret; +} + static bool traddr_is_hostname(nvme_ctrl_t c) { - char addrstr[NVMF_TRADDR_SIZE]; + struct sockaddr_storage addr; if (!c->traddr) return false; if (strcmp(c->transport, "tcp") && strcmp(c->transport, "rdma")) return false; - if (inet_pton(AF_INET, c->traddr, addrstr) > 0 || - inet_pton(AF_INET6, c->traddr, addrstr) > 0) + if (inet_pton_with_scope(AF_UNSPEC, c->traddr, c->trsvcid, &addr) == 0) return false; return true; }