/* SPDX-License-Identifier: GPL-2.0-or-later * Copyright Red Hat * Author: David Gibson * * inany.c - Types and helpers for handling addresses which could be * IPv6 or IPv4 (encoded as IPv4-mapped IPv6 addresses) */ #include #include #include #include #include #include #include "util.h" #include "ip.h" #include "siphash.h" #include "inany.h" #include "fwd.h" const union inany_addr inany_loopback4 = INANY_INIT4(IN4ADDR_LOOPBACK_INIT); const union inany_addr inany_any4 = INANY_INIT4(IN4ADDR_ANY_INIT); /** inany_ntop - Convert an IPv[46] address to text format * @src: IPv[46] address * @dst: output buffer, minimum INANY_ADDRSTRLEN bytes * @size: size of buffer at @dst * * Return: on success, a non-null pointer to @dst, NULL on failure */ const char *inany_ntop(const union inany_addr *src, char *dst, socklen_t size) { const struct in_addr *v4 = inany_v4(src); if (v4) return inet_ntop(AF_INET, v4, dst, size); return inet_ntop(AF_INET6, &src->a6, dst, size); } /** inany_pton - Parse an IPv[46] address from text format * @src: IPv[46] address * @dst: output buffer, filled with parsed address * * Return: on success, 1, if no parseable address is found, 0 */ int inany_pton(const char *src, union inany_addr *dst) { if (inet_pton(AF_INET, src, &dst->v4mapped.a4)) { memset(&dst->v4mapped.zero, 0, sizeof(dst->v4mapped.zero)); memset(&dst->v4mapped.one, 0xff, sizeof(dst->v4mapped.one)); return 1; } if (inet_pton(AF_INET6, src, &dst->a6)) return 1; return 0; } /** inany_prefix_pton - Parse an IPv[46] address with prefix length adjustment * @src: IPv[46] address string * @dst: output buffer, filled with parsed address * @prefix_len: pointer to prefix length * * Return: AF_INET for IPv4, AF_INET6 for IPv6, -1 on error */ int inany_prefix_pton(char *src, union inany_addr *dst, int *prefix_len) { bool mapped = false; struct in6_addr a6; struct in_addr a4; char *slash; char *end; int af; *prefix_len = 0; /* Check for presence of /prefix_len suffix */ slash = strchr(src, '/'); if (slash) *slash = '\0'; /* Read address */ if (inet_pton(AF_INET, src, &a4)) { inany_from_af(dst, AF_INET, &a4); af = AF_INET; } else if (inet_pton(AF_INET6, src, &a6)) { inany_from_af(dst, AF_INET6, &a6); af = AF_INET6; if (inany_v4(dst)) mapped = true; } else { memset(dst, 0, sizeof(*dst)); return -1; } if (!slash) return mapped ? AF_INET : af; /* Read prefix_len - /0 is not allowed */ errno = 0; *prefix_len = strtoul(slash + 1, &end, 10); if (errno || *end || *prefix_len == 0) return -1; if (mapped) { /* IPv4-mapped: prefix already in IPv6 format, must be 96-128 */ if (*prefix_len < 96 || *prefix_len > 128) return -1; return AF_INET; } if (af == AF_INET) { /* Native IPv4: convert to IPv6 format */ if (*prefix_len > 32) return -1; *prefix_len += 96; return AF_INET; } /* Native IPv6: keep as-is */ if (*prefix_len > 128) return -1; return AF_INET6; }