diff --git a/kmod/src/client.c b/kmod/src/client.c index cffd2bfac..25ea716f9 100644 --- a/kmod/src/client.c +++ b/kmod/src/client.c @@ -479,7 +479,7 @@ static void scoutfs_client_connect_worker(struct work_struct *work) struct scoutfs_sb_info *sbi = SCOUTFS_SB(sb); struct scoutfs_mount_options opts; struct scoutfs_net_greeting greet; - struct sockaddr_in sin; + struct sockaddr_storage sin; bool am_quorum; int ret; diff --git a/kmod/src/fence.c b/kmod/src/fence.c index 3669dc2d8..59b8ef10a 100644 --- a/kmod/src/fence.c +++ b/kmod/src/fence.c @@ -25,6 +25,7 @@ #include "sysfs.h" #include "server.h" #include "fence.h" +#include "net.h" /* * Fencing ensures that a given mount can no longer write to the @@ -79,7 +80,7 @@ struct pending_fence { struct timer_list timer; ktime_t start_kt; - __be32 ipv4_addr; + union scoutfs_inet_addr addr; bool fenced; bool error; int reason; @@ -171,14 +172,19 @@ static ssize_t error_store(struct kobject *kobj, struct kobj_attribute *attr, co } SCOUTFS_ATTR_RW(error); -static ssize_t ipv4_addr_show(struct kobject *kobj, +static ssize_t inet_addr_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { DECLARE_FENCE_FROM_KOBJ(fence, kobj); + struct sockaddr_storage sin; - return snprintf(buf, PAGE_SIZE, "%pI4", &fence->ipv4_addr); + memset(&sin, 0, sizeof(struct sockaddr_storage)); + + scoutfs_addr_to_sin(&sin, &fence->addr); + + return snprintf(buf, PAGE_SIZE, "%pISc", SIN_ARG(&sin)); } -SCOUTFS_ATTR_RO(ipv4_addr); +SCOUTFS_ATTR_RO(inet_addr); static ssize_t reason_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) @@ -212,7 +218,7 @@ static struct attribute *fence_attrs[] = { SCOUTFS_ATTR_PTR(elapsed_secs), SCOUTFS_ATTR_PTR(fenced), SCOUTFS_ATTR_PTR(error), - SCOUTFS_ATTR_PTR(ipv4_addr), + SCOUTFS_ATTR_PTR(inet_addr), SCOUTFS_ATTR_PTR(reason), SCOUTFS_ATTR_PTR(rid), NULL, @@ -232,7 +238,7 @@ static void fence_timeout(struct timer_list *timer) wake_up(&fi->waitq); } -int scoutfs_fence_start(struct super_block *sb, u64 rid, __be32 ipv4_addr, int reason) +int scoutfs_fence_start(struct super_block *sb, u64 rid, union scoutfs_inet_addr *addr, int reason) { DECLARE_FENCE_INFO(sb, fi); struct pending_fence *fence; @@ -248,7 +254,7 @@ int scoutfs_fence_start(struct super_block *sb, u64 rid, __be32 ipv4_addr, int r scoutfs_sysfs_init_attrs(sb, &fence->ssa); fence->start_kt = ktime_get(); - fence->ipv4_addr = ipv4_addr; + memcpy(&fence->addr, addr, sizeof(union scoutfs_inet_addr)); fence->fenced = false; fence->error = false; fence->reason = reason; diff --git a/kmod/src/fence.h b/kmod/src/fence.h index f9139001f..40d51943e 100644 --- a/kmod/src/fence.h +++ b/kmod/src/fence.h @@ -7,7 +7,7 @@ enum { SCOUTFS_FENCE_QUORUM_BLOCK_LEADER, }; -int scoutfs_fence_start(struct super_block *sb, u64 rid, __be32 ipv4_addr, int reason); +int scoutfs_fence_start(struct super_block *sb, u64 rid, union scoutfs_inet_addr *addr, int reason); int scoutfs_fence_next(struct super_block *sb, u64 *rid, int *reason, bool *error); int scoutfs_fence_reason_pending(struct super_block *sb, int reason); int scoutfs_fence_free(struct super_block *sb, u64 rid); diff --git a/kmod/src/kernelcompat.h b/kmod/src/kernelcompat.h index 5564a12a2..59fff2fa6 100644 --- a/kmod/src/kernelcompat.h +++ b/kmod/src/kernelcompat.h @@ -195,9 +195,11 @@ struct kc_shrinker_wrapper { #include static inline int kc_kernel_getsockname(struct socket *sock, struct sockaddr *addr) { - int addrlen = sizeof(struct sockaddr_in); + int addrlen = sizeof(struct sockaddr_storage); int ret = kernel_getsockname(sock, addr, &addrlen); - if (ret == 0 && addrlen != sizeof(struct sockaddr_in)) + if (ret == 0 && (!( + (addrlen == sizeof(struct sockaddr_in)) || + (addrlen == sizeof(struct sockaddr_in6))))) return -EAFNOSUPPORT; else if (ret < 0) return ret; @@ -206,9 +208,11 @@ static inline int kc_kernel_getsockname(struct socket *sock, struct sockaddr *ad } static inline int kc_kernel_getpeername(struct socket *sock, struct sockaddr *addr) { - int addrlen = sizeof(struct sockaddr_in); + int addrlen = sizeof(struct sockaddr_storage); int ret = kernel_getpeername(sock, addr, &addrlen); - if (ret == 0 && addrlen != sizeof(struct sockaddr_in)) + if (ret == 0 && (!( + (addrlen == sizeof(struct sockaddr_in)) || + (addrlen == sizeof(struct sockaddr_in6))))) return -EAFNOSUPPORT; else if (ret < 0) return ret; diff --git a/kmod/src/net.c b/kmod/src/net.c index 4c22e8eac..c5cd3f978 100644 --- a/kmod/src/net.c +++ b/kmod/src/net.c @@ -1218,7 +1218,8 @@ static void scoutfs_net_connect_worker(struct work_struct *work) trace_scoutfs_net_connect_work_enter(sb, 0, 0); - ret = kc_sock_create_kern(AF_INET, SOCK_STREAM, IPPROTO_TCP, &sock); + ret = kc_sock_create_kern(conn->connect_sin.ss_family, + SOCK_STREAM, IPPROTO_TCP, &sock); if (ret) goto out; @@ -1239,7 +1240,9 @@ static void scoutfs_net_connect_worker(struct work_struct *work) trace_scoutfs_conn_connect_start(conn); ret = kernel_connect(sock, (struct sockaddr *)&conn->connect_sin, - sizeof(struct sockaddr_in), 0); + conn->connect_sin.ss_family == AF_INET ? + sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6), + 0); if (ret) goto out; @@ -1281,6 +1284,13 @@ static bool empty_accepted_list(struct scoutfs_net_connection *conn) return empty; } +/* + * sockaddr_storage wraps both _in and _in6, which have _port always + * __be16 at the same offset, and we only need to test whether it's + * zero. + */ +#define sockaddr_port_is_nonzero(sin) ((sin).__data[0] || (sin).__data[1]) + /* * Safely shut down an active connection. This can be triggered by * errors in workers or by an external call to free the connection. The @@ -1304,7 +1314,7 @@ static void scoutfs_net_shutdown_worker(struct work_struct *work) trace_scoutfs_conn_shutdown_start(conn); /* connected and accepted conns print a message */ - if (conn->peername.sin_port != 0) + if (sockaddr_port_is_nonzero(conn->peername)) scoutfs_info(sb, "%s "SIN_FMT" -> "SIN_FMT, conn->listening_conn ? "server closing" : "client disconnected", @@ -1434,6 +1444,7 @@ static void scoutfs_net_reconn_free_worker(struct work_struct *work) DEFINE_CONN_FROM_WORK(conn, work, reconn_free_dwork.work); struct super_block *sb = conn->sb; struct scoutfs_net_connection *acc; + union scoutfs_inet_addr addr; unsigned long now = jiffies; unsigned long deadline = 0; bool requeue = false; @@ -1454,8 +1465,9 @@ static void scoutfs_net_reconn_free_worker(struct work_struct *work) if (!test_conn_fl(conn, shutting_down)) { scoutfs_info(sb, "client "SIN_FMT" reconnect timed out, fencing", SIN_ARG(&acc->last_peername)); + scoutfs_sin_to_addr(&addr, &acc->last_peername); ret = scoutfs_fence_start(sb, acc->rid, - acc->last_peername.sin_addr.s_addr, + &addr, SCOUTFS_FENCE_CLIENT_RECONNECT); if (ret) { scoutfs_err(sb, "client fence returned err %d, shutting down server", @@ -1538,9 +1550,9 @@ scoutfs_net_alloc_conn(struct super_block *sb, conn->req_funcs = req_funcs; spin_lock_init(&conn->lock); init_waitqueue_head(&conn->waitq); - conn->sockname.sin_family = AF_INET; - conn->peername.sin_family = AF_INET; - conn->last_peername.sin_family = AF_INET; + conn->sockname.ss_family = AF_UNSPEC; + conn->peername.ss_family = AF_UNSPEC; + conn->last_peername.ss_family = AF_UNSPEC; INIT_LIST_HEAD(&conn->accepted_head); INIT_LIST_HEAD(&conn->accepted_list); conn->next_send_seq = 1; @@ -1619,7 +1631,7 @@ void scoutfs_net_free_conn(struct super_block *sb, */ int scoutfs_net_bind(struct super_block *sb, struct scoutfs_net_connection *conn, - struct sockaddr_in *sin) + struct sockaddr_storage *sin) { struct socket *sock = NULL; int addrlen; @@ -1630,7 +1642,7 @@ int scoutfs_net_bind(struct super_block *sb, if (WARN_ON_ONCE(conn->sock)) return -EINVAL; - ret = kc_sock_create_kern(AF_INET, SOCK_STREAM, IPPROTO_TCP, &sock); + ret = kc_sock_create_kern(sin->ss_family, SOCK_STREAM, IPPROTO_TCP, &sock); if (ret) goto out; @@ -1642,7 +1654,7 @@ int scoutfs_net_bind(struct super_block *sb, if (ret) goto out; - addrlen = sizeof(struct sockaddr_in); + addrlen = sin->ss_family == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6); ret = kernel_bind(sock, (struct sockaddr *)sin, addrlen); if (ret) goto out; @@ -1658,7 +1670,7 @@ int scoutfs_net_bind(struct super_block *sb, ret = 0; conn->sock = sock; - *sin = conn->sockname; + sin = (struct sockaddr_storage *)&conn->sockname; out: if (ret < 0 && sock) @@ -1693,7 +1705,7 @@ static bool connect_result(struct scoutfs_net_connection *conn, int *error) done = true; *error = 0; } else if (test_conn_fl(conn, shutting_down) || - conn->connect_sin.sin_family == 0) { + conn->connect_sin.ss_family == AF_UNSPEC) { done = true; *error = -ESHUTDOWN; } @@ -1714,7 +1726,7 @@ static bool connect_result(struct scoutfs_net_connection *conn, int *error) */ int scoutfs_net_connect(struct super_block *sb, struct scoutfs_net_connection *conn, - struct sockaddr_in *sin, unsigned long timeout_ms) + struct sockaddr_storage *sin, unsigned long timeout_ms) { int ret = 0; diff --git a/kmod/src/net.h b/kmod/src/net.h index a2bdd690e..2e151ac75 100644 --- a/kmod/src/net.h +++ b/kmod/src/net.h @@ -49,15 +49,15 @@ struct scoutfs_net_connection { unsigned long flags; /* CONN_FL_* bitmask */ unsigned long reconn_deadline; - struct sockaddr_in connect_sin; + struct sockaddr_storage connect_sin; unsigned long connect_timeout_ms; struct socket *sock; u64 rid; u64 greeting_id; - struct sockaddr_in sockname; - struct sockaddr_in peername; - struct sockaddr_in last_peername; + struct sockaddr_storage sockname; + struct sockaddr_storage peername; + struct sockaddr_storage last_peername; struct list_head accepted_head; struct scoutfs_net_connection *listening_conn; @@ -99,27 +99,44 @@ enum conn_flags { CONN_FL_reconn_freeing = (1UL << 6), /* waiting done, setter frees */ }; -#define SIN_FMT "%pIS:%u" -#define SIN_ARG(sin) sin, be16_to_cpu((sin)->sin_port) +#define SIN_FMT "%pISpc" +#define SIN_ARG(sin) sin -static inline void scoutfs_addr_to_sin(struct sockaddr_in *sin, +static inline void scoutfs_addr_to_sin(struct sockaddr_storage *sin, union scoutfs_inet_addr *addr) { - BUG_ON(addr->v4.family != cpu_to_le16(SCOUTFS_AF_IPV4)); - - sin->sin_family = AF_INET; - sin->sin_addr.s_addr = cpu_to_be32(le32_to_cpu(addr->v4.addr)); - sin->sin_port = cpu_to_be16(le16_to_cpu(addr->v4.port)); + if (addr->v4.family == cpu_to_le16(SCOUTFS_AF_IPV4)) { + struct sockaddr_in *sin4 = (struct sockaddr_in *)sin; + memset(sin, 0, sizeof(struct sockaddr_storage)); + sin4->sin_family = AF_INET; + sin4->sin_addr.s_addr = cpu_to_be32(le32_to_cpu(addr->v4.addr)); + sin4->sin_port = cpu_to_be16(le16_to_cpu(addr->v4.port)); + } else if (addr->v6.family == cpu_to_le16(SCOUTFS_AF_IPV6)) { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sin; + memset(sin, 0, sizeof(struct sockaddr_storage)); + sin6->sin6_family = AF_INET6; + memcpy(&sin6->sin6_addr.in6_u.u6_addr8, &addr->v6.addr, 16); + sin6->sin6_port = cpu_to_be16(le16_to_cpu(addr->v6.port)); + } else + BUG(); } -static inline void scoutfs_sin_to_addr(union scoutfs_inet_addr *addr, struct sockaddr_in *sin) +static inline void scoutfs_sin_to_addr(union scoutfs_inet_addr *addr, struct sockaddr_storage *sin) { - BUG_ON(sin->sin_family != AF_INET); - - memset(addr, 0, sizeof(union scoutfs_inet_addr)); - addr->v4.family = cpu_to_le16(SCOUTFS_AF_IPV4); - addr->v4.addr = be32_to_le32(sin->sin_addr.s_addr); - addr->v4.port = be16_to_le16(sin->sin_port); + if (sin->ss_family == AF_INET) { + struct sockaddr_in *sin4 = (struct sockaddr_in *)sin; + memset(addr, 0, sizeof(union scoutfs_inet_addr)); + addr->v4.family = cpu_to_le16(SCOUTFS_AF_IPV4); + addr->v4.addr = be32_to_le32(sin4->sin_addr.s_addr); + addr->v4.port = be16_to_le16(sin4->sin_port); + } else if (sin->ss_family == AF_INET6) { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sin; + memset(addr, 0, sizeof(union scoutfs_inet_addr)); + addr->v6.family = cpu_to_le16(SCOUTFS_AF_IPV6); + memcpy(&addr->v6.addr, &sin6->sin6_addr.in6_u.u6_addr8, 16); + addr->v6.port = be16_to_le16(sin6->sin6_port); + } else + BUG(); } struct scoutfs_net_connection * @@ -130,10 +147,10 @@ scoutfs_net_alloc_conn(struct super_block *sb, u64 scoutfs_net_client_rid(struct scoutfs_net_connection *conn); int scoutfs_net_connect(struct super_block *sb, struct scoutfs_net_connection *conn, - struct sockaddr_in *sin, unsigned long timeout_ms); + struct sockaddr_storage *sin, unsigned long timeout_ms); int scoutfs_net_bind(struct super_block *sb, struct scoutfs_net_connection *conn, - struct sockaddr_in *sin); + struct sockaddr_storage *sin); void scoutfs_net_listen(struct super_block *sb, struct scoutfs_net_connection *conn); int scoutfs_net_submit_request(struct super_block *sb, diff --git a/kmod/src/quorum.c b/kmod/src/quorum.c index 242804ed9..198b4300e 100644 --- a/kmod/src/quorum.c +++ b/kmod/src/quorum.c @@ -145,14 +145,26 @@ struct quorum_info { #define DECLARE_QUORUM_INFO_KOBJ(kobj, name) \ DECLARE_QUORUM_INFO(SCOUTFS_SYSFS_ATTRS_SB(kobj), name) -static bool quorum_slot_present(struct scoutfs_quorum_config *qconf, int i) +static bool quorum_slot_ipv4(struct scoutfs_quorum_config *qconf, int i) { BUG_ON(i < 0 || i > SCOUTFS_QUORUM_MAX_SLOTS); return qconf->slots[i].addr.v4.family == cpu_to_le16(SCOUTFS_AF_IPV4); } -static void quorum_slot_sin(struct scoutfs_quorum_config *qconf, int i, struct sockaddr_in *sin) +static bool quorum_slot_ipv6(struct scoutfs_quorum_config *qconf, int i) +{ + BUG_ON(i < 0 || i > SCOUTFS_QUORUM_MAX_SLOTS); + + return qconf->slots[i].addr.v6.family == cpu_to_le16(SCOUTFS_AF_IPV6); +} + +static bool quorum_slot_present(struct scoutfs_quorum_config *qconf, int i) +{ + return quorum_slot_ipv4(qconf, i) || quorum_slot_ipv6(qconf, i); +} + +static void quorum_slot_sin(struct scoutfs_quorum_config *qconf, int i, struct sockaddr_storage *sin) { BUG_ON(i < 0 || i >= SCOUTFS_QUORUM_MAX_SLOTS); @@ -179,11 +191,18 @@ static int create_socket(struct super_block *sb) { DECLARE_QUORUM_INFO(sb, qinf); struct socket *sock = NULL; - struct sockaddr_in sin; + struct sockaddr_storage sin; + struct scoutfs_quorum_slot slot = qinf->qconf.slots[qinf->our_quorum_slot_nr]; int addrlen; int ret; - ret = kc_sock_create_kern(PF_INET, SOCK_DGRAM, IPPROTO_UDP, &sock); + if (le16_to_cpu(slot.addr.v4.family) == SCOUTFS_AF_IPV4) + ret = kc_sock_create_kern(PF_INET, SOCK_DGRAM, IPPROTO_UDP, &sock); + else if (le16_to_cpu(slot.addr.v6.family) == SCOUTFS_AF_IPV6) + ret = kc_sock_create_kern(PF_INET6, SOCK_DGRAM, IPPROTO_UDP, &sock); + else + BUG(); + if (ret) { scoutfs_err(sb, "quorum couldn't create udp socket: %d", ret); goto out; @@ -192,9 +211,9 @@ static int create_socket(struct super_block *sb) /* rather fail and retry than block waiting for free */ sock->sk->sk_allocation = GFP_ATOMIC; + addrlen = (le16_to_cpu(slot.addr.v4.family) == SCOUTFS_AF_IPV4) ? + sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6); quorum_slot_sin(&qinf->qconf, qinf->our_quorum_slot_nr, &sin); - - addrlen = sizeof(sin); ret = kernel_bind(sock, (struct sockaddr *)&sin, addrlen); if (ret) { scoutfs_err(sb, "quorum failed to bind udp socket to "SIN_FMT": %d", @@ -241,7 +260,7 @@ static int send_msg_members(struct super_block *sb, int type, u64 term, int only .iov_base = &qmes, .iov_len = sizeof(qmes), }; - struct sockaddr_in sin; + struct sockaddr_storage sin; struct msghdr mh = { .msg_flags = MSG_DONTWAIT | MSG_NOSIGNAL, .msg_name = &sin, @@ -542,10 +561,11 @@ int scoutfs_quorum_fence_leaders(struct super_block *sb, struct scoutfs_quorum_c u64 term) { #define NR_OLD 2 - struct scoutfs_quorum_block_event old[SCOUTFS_QUORUM_MAX_SLOTS][NR_OLD] = {{{0,}}}; + struct scoutfs_quorum_block_event (*old)[NR_OLD]; struct scoutfs_sb_info *sbi = SCOUTFS_SB(sb); struct scoutfs_quorum_block blk; - struct sockaddr_in sin; + struct sockaddr_storage sin; + union scoutfs_inet_addr addr; const __le64 lefsid = cpu_to_le64(sbi->fsid); const u64 rid = sbi->rid; bool fence_started = false; @@ -558,13 +578,20 @@ int scoutfs_quorum_fence_leaders(struct super_block *sb, struct scoutfs_quorum_c BUILD_BUG_ON(SCOUTFS_QUORUM_BLOCKS < SCOUTFS_QUORUM_MAX_SLOTS); + old = kmalloc(NR_OLD * SCOUTFS_QUORUM_MAX_SLOTS * sizeof(struct scoutfs_quorum_block_event), GFP_KERNEL); + if (!old) { + ret = -ENOMEM; + goto out; + } + memset(old, 0, NR_OLD * SCOUTFS_QUORUM_MAX_SLOTS * sizeof(struct scoutfs_quorum_block_event)); + for (i = 0; i < SCOUTFS_QUORUM_MAX_SLOTS; i++) { if (!quorum_slot_present(qconf, i)) continue; ret = read_quorum_block(sb, SCOUTFS_QUORUM_BLKNO + i, &blk, false); if (ret < 0) - goto out; + goto out_free; /* elected leader still running */ if (le64_to_cpu(blk.events[SCOUTFS_QUORUM_EVENT_ELECT].term) > @@ -598,14 +625,17 @@ int scoutfs_quorum_fence_leaders(struct super_block *sb, struct scoutfs_quorum_c scoutfs_info(sb, "fencing previous leader "SCSBF" at term %llu in slot %u with address "SIN_FMT, SCSB_LEFR_ARGS(lefsid, fence_rid), le64_to_cpu(old[i][j].term), i, SIN_ARG(&sin)); - ret = scoutfs_fence_start(sb, le64_to_cpu(fence_rid), sin.sin_addr.s_addr, + scoutfs_sin_to_addr(&addr, &sin); + ret = scoutfs_fence_start(sb, le64_to_cpu(fence_rid), &addr, SCOUTFS_FENCE_QUORUM_BLOCK_LEADER); if (ret < 0) - goto out; + goto out_free; fence_started = true; } } +out_free: + kfree(old); out: err = scoutfs_fence_wait_fenced(sb, msecs_to_jiffies(SCOUTFS_QUORUM_FENCE_TO_MS)); if (ret == 0) @@ -708,7 +738,7 @@ static void scoutfs_quorum_worker(struct work_struct *work) struct quorum_info *qinf = container_of(work, struct quorum_info, work); struct scoutfs_mount_options opts; struct super_block *sb = qinf->sb; - struct sockaddr_in unused; + struct sockaddr_storage unused; struct quorum_host_msg msg; struct quorum_status qst = {0,}; struct hb_recording hbr; @@ -990,7 +1020,7 @@ static void scoutfs_quorum_worker(struct work_struct *work) * leader with the greatest elected term. If we get it wrong the * connection will timeout and the client will try again. */ -int scoutfs_quorum_server_sin(struct super_block *sb, struct sockaddr_in *sin) +int scoutfs_quorum_server_sin(struct super_block *sb, struct sockaddr_storage *sin) { struct scoutfs_super_block *super = NULL; struct scoutfs_quorum_block blk; @@ -1049,7 +1079,7 @@ u8 scoutfs_quorum_votes_needed(struct super_block *sb) return qinf->votes_needed; } -void scoutfs_quorum_slot_sin(struct scoutfs_quorum_config *qconf, int i, struct sockaddr_in *sin) +void scoutfs_quorum_slot_sin(struct scoutfs_quorum_config *qconf, int i, struct sockaddr_storage *sin) { return quorum_slot_sin(qconf, i, sin); } @@ -1208,8 +1238,12 @@ static int verify_quorum_slots(struct super_block *sb, struct quorum_info *qinf, struct scoutfs_quorum_config *qconf) { char slots[(SCOUTFS_QUORUM_MAX_SLOTS * 3) + 1]; - struct sockaddr_in other; - struct sockaddr_in sin; + struct sockaddr_storage other; + struct sockaddr_storage sin; + struct sockaddr_in *sin4; + struct sockaddr_in *other4; + struct sockaddr_in6 *sin6; + struct sockaddr_in6 *other6; int found = 0; int ret; int i; @@ -1220,35 +1254,78 @@ static int verify_quorum_slots(struct super_block *sb, struct quorum_info *qinf, if (!quorum_slot_present(qconf, i)) continue; - scoutfs_quorum_slot_sin(qconf, i, &sin); + if (quorum_slot_ipv4(qconf, i)) { + scoutfs_quorum_slot_sin(qconf, i, &sin); + sin4 = (struct sockaddr_in *)&sin; - if (!valid_ipv4_unicast(sin.sin_addr.s_addr)) { - scoutfs_err(sb, "quorum slot #%d has invalid ipv4 unicast address: "SIN_FMT, - i, SIN_ARG(&sin)); - return -EINVAL; - } + if (!valid_ipv4_unicast(sin4->sin_addr.s_addr)) { + scoutfs_err(sb, "quorum slot #%d has invalid ipv4 unicast address: "SIN_FMT, + i, SIN_ARG(&sin)); + return -EINVAL; + } - if (!valid_ipv4_port(sin.sin_port)) { - scoutfs_err(sb, "quorum slot #%d has invalid ipv4 port number:"SIN_FMT, - i, SIN_ARG(&sin)); - return -EINVAL; - } + if (!valid_ipv4_port(sin4->sin_port)) { + scoutfs_err(sb, "quorum slot #%d has invalid ipv4 port number:"SIN_FMT, + i, SIN_ARG(&sin)); + return -EINVAL; + } - for (j = i + 1; j < SCOUTFS_QUORUM_MAX_SLOTS; j++) { - if (!quorum_slot_present(qconf, j)) - continue; + for (j = i + 1; j < SCOUTFS_QUORUM_MAX_SLOTS; j++) { + if (!quorum_slot_ipv4(qconf, j)) + continue; + + scoutfs_quorum_slot_sin(qconf, j, &other); + other4 = (struct sockaddr_in *)&other; + + if (sin4->sin_addr.s_addr == other4->sin_addr.s_addr && + sin4->sin_port == other4->sin_port) { + scoutfs_err(sb, "quorum slots #%u and #%u have the same address: "SIN_FMT, + i, j, SIN_ARG(&sin)); + return -EINVAL; + } + } - scoutfs_quorum_slot_sin(qconf, j, &other); + found++; + } else if (quorum_slot_ipv6(qconf, i)) { + quorum_slot_sin(qconf, i, &sin); + sin6 = (struct sockaddr_in6 *)&sin; - if (sin.sin_addr.s_addr == other.sin_addr.s_addr && - sin.sin_port == other.sin_port) { - scoutfs_err(sb, "quorum slots #%u and #%u have the same address: "SIN_FMT, - i, j, SIN_ARG(&sin)); + if ((sin6->sin6_addr.in6_u.u6_addr32[0] == 0) && (sin6->sin6_addr.in6_u.u6_addr32[1] == 0) && + (sin6->sin6_addr.in6_u.u6_addr32[2] == 0) && (sin6->sin6_addr.in6_u.u6_addr32[3] == 0)) { + scoutfs_err(sb, "quorum slot #%d has unspecified ipv6 address:"SIN_FMT, + i, SIN_ARG(&sin)); return -EINVAL; } - } - found++; + if (sin6->sin6_addr.in6_u.u6_addr8[0] == 0xff) { + scoutfs_err(sb, "quorum slot #%d has multicast ipv6 address:"SIN_FMT, + i, SIN_ARG(&sin)); + return -EINVAL; + } + + if (!valid_ipv4_port(sin6->sin6_port)) { + scoutfs_err(sb, "quorum slot #%d has invalid ipv6 port number:"SIN_FMT, + i, SIN_ARG(&sin)); + return -EINVAL; + } + + for (j = i + 1; j < SCOUTFS_QUORUM_MAX_SLOTS; j++) { + if (!quorum_slot_ipv6(qconf, j)) + continue; + + quorum_slot_sin(qconf, j, &other); + other6 = (struct sockaddr_in6 *)&other; + + if ((ipv6_addr_equal(&sin6->sin6_addr, &other6->sin6_addr)) && + (sin6->sin6_port == other6->sin6_port)) { + scoutfs_err(sb, "quorum slots #%u and #%u have the same address: "SIN_FMT, + i, j, SIN_ARG(&sin)); + return -EINVAL; + } + } + + found++; + } } if (found == 0) { diff --git a/kmod/src/quorum.h b/kmod/src/quorum.h index 500a5d16d..4e29bcee2 100644 --- a/kmod/src/quorum.h +++ b/kmod/src/quorum.h @@ -1,11 +1,11 @@ #ifndef _SCOUTFS_QUORUM_H_ #define _SCOUTFS_QUORUM_H_ -int scoutfs_quorum_server_sin(struct super_block *sb, struct sockaddr_in *sin); +int scoutfs_quorum_server_sin(struct super_block *sb, struct sockaddr_storage *sin); u8 scoutfs_quorum_votes_needed(struct super_block *sb); void scoutfs_quorum_slot_sin(struct scoutfs_quorum_config *qconf, int i, - struct sockaddr_in *sin); + struct sockaddr_storage *sin); int scoutfs_quorum_fence_leaders(struct super_block *sb, struct scoutfs_quorum_config *qconf, u64 term); diff --git a/kmod/src/scoutfs_trace.h b/kmod/src/scoutfs_trace.h index c71573529..5a969faae 100644 --- a/kmod/src/scoutfs_trace.h +++ b/kmod/src/scoutfs_trace.h @@ -1355,35 +1355,37 @@ DEFINE_EVENT(scoutfs_lock_class, scoutfs_lock_shrink, ); DECLARE_EVENT_CLASS(scoutfs_net_class, - TP_PROTO(struct super_block *sb, struct sockaddr_in *name, - struct sockaddr_in *peer, struct scoutfs_net_header *nh), + TP_PROTO(struct super_block *sb, struct sockaddr_storage *name, + struct sockaddr_storage *peer, struct scoutfs_net_header *nh), TP_ARGS(sb, name, peer, nh), TP_STRUCT__entry( SCSB_TRACE_FIELDS - si4_trace_define(name) - si4_trace_define(peer) + __field_struct(struct sockaddr_storage, name) + __field_struct(struct sockaddr_storage, peer) snh_trace_define(nh) ), TP_fast_assign( SCSB_TRACE_ASSIGN(sb); - si4_trace_assign(name, name); - si4_trace_assign(peer, peer); + memcpy(&__entry->name, name, sizeof(struct sockaddr_storage)); + memcpy(&__entry->peer, peer, sizeof(struct sockaddr_storage)); snh_trace_assign(nh, nh); ), - TP_printk(SCSBF" name "SI4_FMT" peer "SI4_FMT" nh "SNH_FMT, - SCSB_TRACE_ARGS, si4_trace_args(name), si4_trace_args(peer), + TP_printk(SCSBF" name "SIN_FMT" peer "SIN_FMT" nh "SNH_FMT, + SCSB_TRACE_ARGS, + &__entry->name, + &__entry->peer, snh_trace_args(nh)) ); DEFINE_EVENT(scoutfs_net_class, scoutfs_net_send_message, - TP_PROTO(struct super_block *sb, struct sockaddr_in *name, - struct sockaddr_in *peer, struct scoutfs_net_header *nh), + TP_PROTO(struct super_block *sb, struct sockaddr_storage *name, + struct sockaddr_storage *peer, struct scoutfs_net_header *nh), TP_ARGS(sb, name, peer, nh) ); DEFINE_EVENT(scoutfs_net_class, scoutfs_net_recv_message, - TP_PROTO(struct super_block *sb, struct sockaddr_in *name, - struct sockaddr_in *peer, struct scoutfs_net_header *nh), + TP_PROTO(struct super_block *sb, struct sockaddr_storage *name, + struct sockaddr_storage *peer, struct scoutfs_net_header *nh), TP_ARGS(sb, name, peer, nh) ); @@ -1416,8 +1418,8 @@ DECLARE_EVENT_CLASS(scoutfs_net_conn_class, __field(void *, sock) __field(__u64, c_rid) __field(__u64, greeting_id) - si4_trace_define(sockname) - si4_trace_define(peername) + __field_struct(struct sockaddr_storage, sockname) + __field_struct(struct sockaddr_storage, peername) __field(unsigned char, e_accepted_head) __field(void *, listening_conn) __field(unsigned char, e_accepted_list) @@ -1435,8 +1437,8 @@ DECLARE_EVENT_CLASS(scoutfs_net_conn_class, __entry->sock = conn->sock; __entry->c_rid = conn->rid; __entry->greeting_id = conn->greeting_id; - si4_trace_assign(sockname, &conn->sockname); - si4_trace_assign(peername, &conn->peername); + memcpy(&__entry->sockname, &conn->sockname, sizeof(struct sockaddr_storage)); + memcpy(&__entry->peername, &conn->peername, sizeof(struct sockaddr_storage)); __entry->e_accepted_head = !!list_empty(&conn->accepted_head); __entry->listening_conn = conn->listening_conn; __entry->e_accepted_list = !!list_empty(&conn->accepted_list); @@ -1446,7 +1448,7 @@ DECLARE_EVENT_CLASS(scoutfs_net_conn_class, __entry->e_resend_queue = !!list_empty(&conn->resend_queue); __entry->recv_seq = atomic64_read(&conn->recv_seq); ), - TP_printk(SCSBF" flags %s rc_dl %lu cto %lu sk %p rid %llu grid %llu sn "SI4_FMT" pn "SI4_FMT" eah %u lc %p eal %u nss %llu nsi %llu esq %u erq %u rs %llu", + TP_printk(SCSBF" flags %s rc_dl %lu cto %lu sk %p rid %llu grid %llu sn "SIN_FMT" pn "SIN_FMT" eah %u lc %p eal %u nss %llu nsi %llu esq %u erq %u rs %llu", SCSB_TRACE_ARGS, print_conn_flags(__entry->flags), __entry->reconn_deadline, @@ -1454,8 +1456,8 @@ DECLARE_EVENT_CLASS(scoutfs_net_conn_class, __entry->sock, __entry->c_rid, __entry->greeting_id, - si4_trace_args(sockname), - si4_trace_args(peername), + &__entry->sockname, + &__entry->peername, __entry->e_accepted_head, __entry->listening_conn, __entry->e_accepted_list, diff --git a/kmod/src/server.c b/kmod/src/server.c index 7f979df72..63ac5ea45 100644 --- a/kmod/src/server.c +++ b/kmod/src/server.c @@ -3553,7 +3553,7 @@ static bool invalid_mounted_client_item(struct scoutfs_btree_item_ref *iref) * it's acceptable to see -EEXIST. */ static int insert_mounted_client(struct super_block *sb, u64 rid, u64 gr_flags, - struct sockaddr_in *sin) + struct sockaddr_storage *sin) { DECLARE_SERVER_INFO(sb, server); struct scoutfs_super_block *super = DIRTY_SUPER_SB(sb); @@ -4306,7 +4306,7 @@ static void fence_pending_recov_worker(struct work_struct *work) break; } - ret = scoutfs_fence_start(sb, rid, le32_to_be32(addr.v4.addr), + ret = scoutfs_fence_start(sb, rid, &addr, SCOUTFS_FENCE_CLIENT_RECOVERY); if (ret < 0) { scoutfs_err(sb, "fence returned err %d, shutting down server", ret); @@ -4457,7 +4457,7 @@ static void scoutfs_server_worker(struct work_struct *work) struct scoutfs_net_connection *conn = NULL; struct scoutfs_mount_options opts; DECLARE_WAIT_QUEUE_HEAD(waitq); - struct sockaddr_in sin; + struct sockaddr_storage sin; bool alloc_init = false; u64 max_seq; int ret; @@ -4466,7 +4466,7 @@ static void scoutfs_server_worker(struct work_struct *work) scoutfs_options_read(sb, &opts); scoutfs_quorum_slot_sin(&server->qconf, opts.quorum_slot_nr, &sin); - scoutfs_info(sb, "server starting at "SIN_FMT, SIN_ARG(&sin)); + scoutfs_info(sb, "server starting at "SIN_FMT, &sin); scoutfs_block_writer_init(sb, &server->wri); server->finalize_sent_seq = 0; diff --git a/kmod/src/server.h b/kmod/src/server.h index f795ea0fb..d1f01e88b 100644 --- a/kmod/src/server.h +++ b/kmod/src/server.h @@ -1,27 +1,6 @@ #ifndef _SCOUTFS_SERVER_H_ #define _SCOUTFS_SERVER_H_ -#define SI4_FMT "%u.%u.%u.%u:%u" - -#define si4_trace_define(name) \ - __field(__u32, name##_addr) \ - __field(__u16, name##_port) - -#define si4_trace_assign(name, sin) \ -do { \ - __typeof__(sin) _sin = (sin); \ - \ - __entry->name##_addr = be32_to_cpu(_sin->sin_addr.s_addr); \ - __entry->name##_port = be16_to_cpu(_sin->sin_port); \ -} while(0) - -#define si4_trace_args(name) \ - (__entry->name##_addr >> 24), \ - (__entry->name##_addr >> 16) & 255, \ - (__entry->name##_addr >> 8) & 255, \ - __entry->name##_addr & 255, \ - __entry->name##_port - #define SNH_FMT \ "seq %llu recv_seq %llu id %llu data_len %u cmd %u flags 0x%x error %u" #define SNH_ARG(nh) \ diff --git a/tests/run-tests.sh b/tests/run-tests.sh index f6c506ec6..2ac4421a0 100755 --- a/tests/run-tests.sh +++ b/tests/run-tests.sh @@ -383,7 +383,7 @@ fi quo="" if [ -n "$T_MKFS" ]; then for i in $(seq -0 $((T_QUORUM - 1))); do - quo="$quo -Q $i,127.0.0.1,$((T_TEST_PORT + i))" + quo="$quo -Q $i,::1,$((T_TEST_PORT + i))" done msg "making new filesystem with $T_QUORUM quorum members" diff --git a/utils/src/parse.c b/utils/src/parse.c index 3167a2ff9..1d9d54290 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 c17eb4251..c5eb7cb6a 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)); - - printf(" rid %016llx ipv4_addr %s flags 0x%x\n", - le64_to_cpu(key->skmc_rid), inet_ntoa(in), mcv->flags); + 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); + } 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; - - len = snprintf(NULL, 0, "%s:%u", quad, le16_to_cpu(ia->v4.port)); - if (len < 1 || len > 22) + 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++; /* null */ + str = malloc(len); + if (!str) + return NULL; + + 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; - - len++; /* null */ - str = malloc(len); - if (!str) - return NULL; - - snprintf(str, len, "%s:%u", quad, le16_to_cpu(ia->v4.port)); 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 935114abf..b2b13565f 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)) - 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; + 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)); + + 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; + } } }