From 3ca0bef6ad3843b67fb81eb525d65855901b9b9b Mon Sep 17 00:00:00 2001 From: Auke Kok Date: Tue, 16 Sep 2025 11:02:22 -0700 Subject: [PATCH] Add ipv6 support to scoutfs userspace utility. This change adds ipv6 support to various scoutfs sub-commands, allowing users to mkfs, print and change-quorum-config using ipv6 addresses, and modifies the outputs. Any ipv6 address/port is displayed as [::1]:5000 to comply with the related RFC's. Input strings remain consistent as the quorum config input value is comma-separated already, not posing any issues. Signed-off-by: Auke Kok --- utils/src/parse.c | 33 ++++++++++++++-------- utils/src/print.c | 57 ++++++++++++++++++++++++++----------- utils/src/quorum.c | 70 ++++++++++++++++++++++++++-------------------- 3 files changed, 102 insertions(+), 58 deletions(-) diff --git a/utils/src/parse.c b/utils/src/parse.c index 3167a2ff..1d9d5429 100644 --- a/utils/src/parse.c +++ b/utils/src/parse.c @@ -160,15 +160,16 @@ int parse_timespec(char *str, struct timespec *ts) * Parse a quorum slot specification string "NR,ADDR,PORT" into its * component parts. We use sscanf to both parse the leading NR and * trailing PORT integers, and to pull out the inner ADDR string which - * is then parsed to make sure that it's a valid unicast ipv4 address. + * is then parsed to make sure that it's a valid unicast ip address. * We require that all components be specified, and sccanf will check * this by the number of matches it returns. */ int parse_quorum_slot(struct scoutfs_quorum_slot *slot, char *arg) { -#define ADDR_CHARS 45 /* max ipv6 */ - char addr[ADDR_CHARS + 1] = {'\0',}; +#define ADDR_CHARS 45 /* (INET6_ADDRSTRLEN - 1) */ + char addr[INET6_ADDRSTRLEN] = {'\0',}; struct in_addr in; + struct in6_addr in6; int port; int parsed; int nr; @@ -206,15 +207,25 @@ int parse_quorum_slot(struct scoutfs_quorum_slot *slot, char *arg) return -EINVAL; } - if (inet_aton(addr, &in) == 0 || htonl(in.s_addr) == 0 || - htonl(in.s_addr) == UINT_MAX) { - printf("invalid ipv4 address '%s' in quorum slot '%s'\n", - addr, arg); - return -EINVAL; + if (inet_pton(AF_INET, addr, &in) == 1) { + if (htonl(in.s_addr) == 0 || htonl(in.s_addr) == UINT_MAX) { + printf("invalid ipv4 address '%s' in quorum slot '%s'\n", + addr, arg); + return -EINVAL; + } + slot->addr.v4.family = cpu_to_le16(SCOUTFS_AF_IPV4); + slot->addr.v4.addr = cpu_to_le32(htonl(in.s_addr)); + slot->addr.v4.port = cpu_to_le16(port); + } else if (inet_pton(AF_INET6, addr, &in6) == 1) { + if (IN6_IS_ADDR_UNSPECIFIED(&in6) || IN6_IS_ADDR_MULTICAST(&in6)) { + printf("invalid ipv6 address '%s' in quorum slot '%s'\n", + addr, arg); + return -EINVAL; + } + slot->addr.v6.family = cpu_to_le16(SCOUTFS_AF_IPV6); + memcpy(slot->addr.v6.addr, &in6, 16); + slot->addr.v6.port = cpu_to_le16(port); } - slot->addr.v4.family = cpu_to_le16(SCOUTFS_AF_IPV4); - slot->addr.v4.addr = cpu_to_le32(htonl(in.s_addr)); - slot->addr.v4.port = cpu_to_le16(port); return nr; } diff --git a/utils/src/print.c b/utils/src/print.c index c17eb425..c5eb7cb6 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -28,6 +28,7 @@ #include "srch.h" #include "leaf_item_hash.h" #include "dev.h" +#include "quorum.h" static void print_block_header(struct scoutfs_block_header *hdr, int size) { @@ -400,12 +401,20 @@ static int print_mounted_client_entry(struct scoutfs_key *key, u64 seq, u8 flags { struct scoutfs_mounted_client_btree_val *mcv = val; struct in_addr in; + char ip6addr[INET6_ADDRSTRLEN]; memset(&in, 0, sizeof(in)); - in.s_addr = htonl(le32_to_cpu(mcv->addr.v4.addr)); + if (mcv->addr.v4.family == cpu_to_le16(SCOUTFS_AF_IPV4)) { + in.s_addr = htonl(le32_to_cpu(mcv->addr.v4.addr)); - printf(" rid %016llx ipv4_addr %s flags 0x%x\n", - le64_to_cpu(key->skmc_rid), inet_ntoa(in), mcv->flags); + printf(" rid %016llx ipv4_addr %s flags 0x%x\n", + le64_to_cpu(key->skmc_rid), inet_ntoa(in), mcv->flags); + } else if (mcv->addr.v6.family == cpu_to_le16(SCOUTFS_AF_IPV6)) { + printf(" rid %016llx ipv6_addr %s flags 0x%x\n", + le64_to_cpu(key->skmc_rid), + inet_ntop(AF_INET, mcv->addr.v6.addr, ip6addr, INET6_ADDRSTRLEN), + mcv->flags); + } return 0; } @@ -891,26 +900,40 @@ static int print_btree_leaf_items(int fd, struct scoutfs_super_block *super, static char *alloc_addr_str(union scoutfs_inet_addr *ia) { struct in_addr addr; + char ip6addr[INET6_ADDRSTRLEN]; char *quad; char *str; int len; - memset(&addr, 0, sizeof(addr)); - addr.s_addr = htonl(le32_to_cpu(ia->v4.addr)); - quad = inet_ntoa(addr); - if (quad == NULL) - return NULL; + if (le16_to_cpu(ia->v4.family) == SCOUTFS_AF_IPV4) { + memset(&addr, 0, sizeof(addr)); + addr.s_addr = htonl(le32_to_cpu(ia->v4.addr)); + quad = inet_ntoa(addr); + if (quad == NULL) + return NULL; - len = snprintf(NULL, 0, "%s:%u", quad, le16_to_cpu(ia->v4.port)); - if (len < 1 || len > 22) - return NULL; + len = snprintf(NULL, 0, "%s:%u", quad, le16_to_cpu(ia->v4.port)); + if (len < 1 || len > 22) + return NULL; - len++; /* null */ - str = malloc(len); - if (!str) - return NULL; + len++; /* null */ + str = malloc(len); + if (!str) + return NULL; - snprintf(str, len, "%s:%u", quad, le16_to_cpu(ia->v4.port)); + snprintf(str, len, "%s:%u", quad, le16_to_cpu(ia->v4.port)); + } else if (le16_to_cpu(ia->v6.family) == SCOUTFS_AF_IPV6) { + if (inet_ntop(AF_INET6, ia->v6.addr, ip6addr, INET6_ADDRSTRLEN) == NULL) + return NULL; + + len = strlen(ip6addr) + 9; /* "[]:\0" (4) plus max strlen(u16) (5) */ + str = malloc(len); + if (!str) + return NULL; + + snprintf(str, len, "[%s]:%u", ip6addr, le16_to_cpu(ia->v6.port)); + } else + return NULL; return str; } @@ -1026,7 +1049,7 @@ static void print_super_block(struct scoutfs_super_block *super, u64 blkno) printf(" quorum config version %llu\n", le64_to_cpu(super->qconf.version)); for (i = 0; i < array_size(super->qconf.slots); i++) { - if (super->qconf.slots[i].addr.v4.family != cpu_to_le16(SCOUTFS_AF_IPV4)) + if (!quorum_slot_present(super, i)) continue; addr = alloc_addr_str(&super->qconf.slots[i].addr); diff --git a/utils/src/quorum.c b/utils/src/quorum.c index 935114ab..b2b13565 100644 --- a/utils/src/quorum.c +++ b/utils/src/quorum.c @@ -10,7 +10,8 @@ bool quorum_slot_present(struct scoutfs_super_block *super, int i) { - return super->qconf.slots[i].addr.v4.family == cpu_to_le16(SCOUTFS_AF_IPV4); + return ((super->qconf.slots[i].addr.v4.family == cpu_to_le16(SCOUTFS_AF_IPV4)) || + (super->qconf.slots[i].addr.v6.family == cpu_to_le16(SCOUTFS_AF_IPV6))); } bool valid_quorum_slots(struct scoutfs_quorum_slot *slots) @@ -18,35 +19,40 @@ bool valid_quorum_slots(struct scoutfs_quorum_slot *slots) struct in_addr in; bool valid = true; char *addr; + char ip6addr[INET6_ADDRSTRLEN]; int i; int j; for (i = 0; i < SCOUTFS_QUORUM_MAX_SLOTS; i++) { - if (slots[i].addr.v4.family == cpu_to_le16(SCOUTFS_AF_NONE)) - continue; - - if (slots[i].addr.v4.family != cpu_to_le16(SCOUTFS_AF_IPV4)) { + if (slots[i].addr.v4.family == cpu_to_le16(SCOUTFS_AF_IPV4)) { + for (j = i + 1; j < SCOUTFS_QUORUM_MAX_SLOTS; j++) { + if (slots[i].addr.v4.addr == slots[j].addr.v4.addr && + slots[i].addr.v4.port == slots[j].addr.v4.port) { + in.s_addr = + htonl(le32_to_cpu(slots[i].addr.v4.addr)); + addr = inet_ntoa(in); + fprintf(stderr, "quorum slot nr %u and %u have the same address %s:%u\n", + i, j, addr, + le16_to_cpu(slots[i].addr.v4.port)); + valid = false; + } + } + } else if (slots[i].addr.v6.family == cpu_to_le16(SCOUTFS_AF_IPV6)) { + for (j = i + 1; j < SCOUTFS_QUORUM_MAX_SLOTS; j++) { + if ((IN6_ARE_ADDR_EQUAL(slots[i].addr.v6.addr, slots[j].addr.v6.addr)) && + (slots[i].addr.v6.port == slots[j].addr.v6.port)) { + fprintf(stderr, "quorum slot nr %u and %u have the same address [%s]:%u\n", + i, j, + inet_ntop(AF_INET6, slots[i].addr.v6.addr, ip6addr, INET6_ADDRSTRLEN), + le16_to_cpu(slots[i].addr.v6.port)); + valid = false; + } + } + } else if (slots[i].addr.v6.family != cpu_to_le16(SCOUTFS_AF_NONE)) { fprintf(stderr, "quorum slot nr %u has invalid family %u\n", i, le16_to_cpu(slots[i].addr.v4.family)); valid = false; } - - for (j = i + 1; j < SCOUTFS_QUORUM_MAX_SLOTS; j++) { - if (slots[i].addr.v4.family != cpu_to_le16(SCOUTFS_AF_IPV4)) - continue; - - if (slots[i].addr.v4.addr == slots[j].addr.v4.addr && - slots[i].addr.v4.port == slots[j].addr.v4.port) { - - in.s_addr = - htonl(le32_to_cpu(slots[i].addr.v4.addr)); - addr = inet_ntoa(in); - fprintf(stderr, "quorum slot nr %u and %u have the same address %s:%u\n", - i, j, addr, - le16_to_cpu(slots[i].addr.v4.port)); - valid = false; - } - } } return valid; @@ -61,19 +67,23 @@ void print_quorum_slots(struct scoutfs_quorum_slot *slots, int nr, char *indent) { struct scoutfs_quorum_slot *sl; struct in_addr in; + char ip6addr[INET6_ADDRSTRLEN]; bool first = true; int i; for (i = 0, sl = slots; i < SCOUTFS_QUORUM_MAX_SLOTS; i++, sl++) { + if (sl->addr.v4.family == cpu_to_le16(SCOUTFS_AF_IPV4)) { + in.s_addr = htonl(le32_to_cpu(sl->addr.v4.addr)); + printf("%s%u: %s:%u\n", first ? "" : indent, + i, inet_ntoa(in), le16_to_cpu(sl->addr.v4.port)); - if (sl->addr.v4.family != cpu_to_le16(SCOUTFS_AF_IPV4)) - continue; - - in.s_addr = htonl(le32_to_cpu(sl->addr.v4.addr)); - printf("%s%u: %s:%u\n", first ? "" : indent, - i, inet_ntoa(in), le16_to_cpu(sl->addr.v4.port)); - - first = false; + first = false; + } else if (sl->addr.v6.family == cpu_to_le16(SCOUTFS_AF_IPV6)) { + printf("%s%u: [%s]:%u\n", first ? "" : indent, i, + inet_ntop(AF_INET6, sl->addr.v6.addr, ip6addr, INET6_ADDRSTRLEN), + le16_to_cpu(sl->addr.v6.port)); + first = false; + } } }