From 811826be429fd6fc5154d9b04ced1cd22bd66758 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Mon, 24 Oct 2011 09:11:50 +0200 Subject: [PATCH] client/server: IPv6 support Signed-off-by: Jens Axboe --- README | 20 +++++++----- client.c | 37 ++++++++++++++++------ fio.1 | 20 +++++++----- server.c | 93 ++++++++++++++++++++++++++++++++++++++++++++++++-------- server.h | 3 +- 5 files changed, 134 insertions(+), 39 deletions(-) diff --git a/README b/README index d41fc660..b2fdd0ba 100644 --- a/README +++ b/README @@ -331,28 +331,32 @@ To start the server, you would do: fio --server=args on that machine, where args defines what fio listens to. The arguments -are of the form 'type:hostname or IP:port'. 'type' is either 'ip' for -TCP/IP, or 'sock' for a local unix domain socket. 'hostname' is either -a hostname or IP address, and 'port' is the port to listen to (only valid -for TCP/IP, not a local socket). Some examples: +are of the form 'type,hostname or IP,port'. 'type' is either 'ip' (or ip4) +for TCP/IP v4, 'ip6' for TCP/IP v6, or 'sock' for a local unix domain socket. +'hostname' is either a hostname or IP address, and 'port' is the port to +listen to (only valid for TCP/IP, not a local socket). Some examples: 1) fio --server Start a fio server, listening on all interfaces on the default port (8765). -2) fio --server=ip:hostname:4444 +2) fio --server=ip:hostname,4444 Start a fio server, listening on IP belonging to hostname and on port 4444. -3) fio --server=:4444 +3) fio --server=ip6:::1,4444 + + Start a fio server, listening on IPv6 localhost ::1 and on port 4444. + +4) fio --server=,4444 Start a fio server, listening on all interfaces on port 4444. -4) fio --server=1.2.3.4 +5) fio --server=1.2.3.4 Start a fio server, listening on IP 1.2.3.4 on the default port. -5) fio --server=sock:/tmp/fio.sock +6) fio --server=sock:/tmp/fio.sock Start a fio server, listening on the local socket /tmp/fio.sock. diff --git a/client.c b/client.c index fda16b8e..a61bc803 100644 --- a/client.c +++ b/client.c @@ -29,8 +29,11 @@ struct fio_client { struct flist_head list; struct flist_head hash_list; struct flist_head arg_list; - struct sockaddr_in addr; - struct sockaddr_un addr_un; + union { + struct sockaddr_in addr; + struct sockaddr_in6 addr6; + struct sockaddr_un addr_un; + }; char *hostname; int port; int fd; @@ -44,6 +47,7 @@ struct fio_client { int disk_stats_shown; unsigned int jobs; int error; + int ipv6; struct flist_head eta_list; struct client_eta *eta_in_flight; @@ -204,7 +208,9 @@ int fio_client_add(const char *hostname, void **cookie) if (fio_server_parse_string(hostname, &client->hostname, &client->is_sock, &client->port, - &client->addr.sin_addr)) + &client->addr.sin_addr, + &client->addr6.sin6_addr, + &client->ipv6)) return -1; client->fd = -1; @@ -220,18 +226,31 @@ int fio_client_add(const char *hostname, void **cookie) static int fio_client_connect_ip(struct fio_client *client) { - int fd; - - client->addr.sin_family = AF_INET; - client->addr.sin_port = htons(client->port); + struct sockaddr *addr; + fio_socklen_t socklen; + int fd, domain; + + if (client->ipv6) { + client->addr6.sin6_family = AF_INET6; + client->addr6.sin6_port = htons(client->port); + domain = AF_INET6; + addr = (struct sockaddr *) &client->addr6; + socklen = sizeof(client->addr6); + } else { + client->addr.sin_family = AF_INET; + client->addr.sin_port = htons(client->port); + domain = AF_INET; + addr = (struct sockaddr *) &client->addr; + socklen = sizeof(client->addr); + } - fd = socket(AF_INET, SOCK_STREAM, 0); + fd = socket(domain, SOCK_STREAM, 0); if (fd < 0) { log_err("fio: socket: %s\n", strerror(errno)); return -1; } - if (connect(fd, (struct sockaddr *) &client->addr, sizeof(client->addr)) < 0) { + if (connect(fd, addr, socklen) < 0) { log_err("fio: connect: %s\n", strerror(errno)); log_err("fio: failed to connect to %s:%u\n", client->hostname, client->port); diff --git a/fio.1 b/fio.1 index d63d1f2d..3b111225 100644 --- a/fio.1 +++ b/fio.1 @@ -1221,28 +1221,32 @@ To start the server, you would do: \fBfio \-\-server=args\fR on that machine, where args defines what fio listens to. The arguments -are of the form 'type:hostname or IP:port'. 'type' is either 'ip' for -TCP/IP, or 'sock' for a local unix domain socket. 'hostname' is either -a hostname or IP address, and 'port' is the port to listen to (only valid -for TCP/IP, not a local socket). Some examples: +are of the form 'type:hostname or IP:port'. 'type' is either 'ip' (or ip4) +for TCP/IP v4, 'ip6' for TCP/IP v6, or 'sock' for a local unix domain socket. +'hostname' is either a hostname or IP address, and 'port' is the port to +listen to (only valid for TCP/IP, not a local socket). Some examples: 1) fio --server Start a fio server, listening on all interfaces on the default port (8765). -2) fio --server=ip:hostname:4444 +2) fio --server=ip:hostname,4444 Start a fio server, listening on IP belonging to hostname and on port 4444. -3) fio --server=:4444 +3) fio --server=ip6:::1,4444 + + Start a fio server, listening on IPv6 localhost ::1 and on port 4444. + +4) fio --server=,4444 Start a fio server, listening on all interfaces on port 4444. -4) fio --server=1.2.3.4 +5) fio --server=1.2.3.4 Start a fio server, listening on IP 1.2.3.4 on the default port. -5) fio --server=sock:/tmp/fio.sock +6) fio --server=sock:/tmp/fio.sock Start a fio server, listening on the local socket /tmp/fio.sock. diff --git a/server.c b/server.c index 4da8bf00..6a5bc6ce 100644 --- a/server.c +++ b/server.c @@ -32,7 +32,9 @@ static int server_fd = -1; static char *fio_server_arg; static char *bind_sock; static struct sockaddr_in saddr_in; +static struct sockaddr_in6 saddr_in6; static int first_cmd_check; +static int use_ipv6; static const char *fio_server_ops[FIO_NET_CMD_NR] = { "", @@ -814,9 +816,15 @@ int fio_server_log(const char *format, ...) static int fio_init_server_ip(void) { + struct sockaddr *addr; + fio_socklen_t socklen; int sk, opt; - sk = socket(AF_INET, SOCK_STREAM, 0); + if (use_ipv6) + sk = socket(AF_INET6, SOCK_STREAM, 0); + else + sk = socket(AF_INET, SOCK_STREAM, 0); + if (sk < 0) { log_err("fio: socket: %s\n", strerror(errno)); return -1; @@ -836,9 +844,17 @@ static int fio_init_server_ip(void) } #endif - saddr_in.sin_family = AF_INET; + if (use_ipv6) { + addr = (struct sockaddr *) &saddr_in6; + socklen = sizeof(saddr_in6); + saddr_in6.sin6_family = AF_INET6; + } else { + addr = (struct sockaddr *) &saddr_in; + socklen = sizeof(saddr_in); + saddr_in.sin_family = AF_INET; + } - if (bind(sk, (struct sockaddr *) &saddr_in, sizeof(saddr_in)) < 0) { + if (bind(sk, addr, socklen) < 0) { log_err("fio: bind: %s\n", strerror(errno)); close(sk); return -1; @@ -894,9 +910,27 @@ static int fio_init_server_connection(void) if (sk < 0) return sk; - if (!bind_sock) - sprintf(bind_str, "%s:%u", inet_ntoa(saddr_in.sin_addr), fio_net_port); - else + if (!bind_sock) { + char *p, port[16]; + const void *src; + int af; + + if (use_ipv6) { + af = AF_INET6; + src = &saddr_in6.sin6_addr; + } else { + af = AF_INET; + src = &saddr_in.sin_addr; + } + + p = (char *) inet_ntop(af, src, bind_str, sizeof(bind_str)); + + sprintf(port, ",%u", fio_net_port); + if (p) + strcat(p, port); + else + strcpy(bind_str, port); + } else strcpy(bind_str, bind_sock); log_info("fio: server listening on %s\n", bind_str); @@ -910,11 +944,13 @@ static int fio_init_server_connection(void) } int fio_server_parse_string(const char *str, char **ptr, int *is_sock, - int *port, struct in_addr *inp) + int *port, struct in_addr *inp, + struct in6_addr *inp6, int *ipv6) { *ptr = NULL; *is_sock = 0; *port = fio_net_port; + *ipv6 = 0; if (!strncmp(str, "sock:", 5)) { *ptr = strdup(str + 5); @@ -922,14 +958,19 @@ int fio_server_parse_string(const char *str, char **ptr, int *is_sock, } else { const char *host = str; char *portp; - int lport = 0; + int ret, lport = 0; /* * Is it ip::port */ if (!strncmp(host, "ip:", 3)) host += 3; - else if (host[0] == ':') { + else if (!strncmp(host, "ip4:", 4)) + host += 4; + else if (!strncmp(host, "ip6:", 4)) { + host += 4; + *ipv6 = 1; + } else if (host[0] == ':') { /* String is :port */ host++; lport = atoi(host); @@ -946,7 +987,7 @@ int fio_server_parse_string(const char *str, char **ptr, int *is_sock, * If no port seen yet, check if there's a last ':' at the end */ if (!lport) { - portp = strchr(host, ':'); + portp = strchr(host, ','); if (portp) { *portp = '\0'; portp++; @@ -961,22 +1002,46 @@ int fio_server_parse_string(const char *str, char **ptr, int *is_sock, if (lport) *port = lport; + if (!strlen(host)) + goto done; + *ptr = strdup(host); - if (inet_aton(host, inp) != 1) { + if (*ipv6) + ret = inet_pton(AF_INET6, host, inp6); + else + ret = inet_pton(AF_INET, host, inp); + + if (ret != 1) { struct hostent *hent; hent = gethostbyname(host); if (!hent) { + log_err("fio: failed to resolve <%s>\n", host); +err: free(*ptr); *ptr = NULL; return 1; } - memcpy(inp, hent->h_addr, 4); + if (*ipv6) { + if (hent->h_addrtype != AF_INET6) { + log_info("fio: falling back to IPv4\n"); + *ipv6 = 0; + } else + memcpy(inp6, hent->h_addr_list[0], 16); + } + if (!*ipv6) { + if (hent->h_addrtype != AF_INET) { + log_err("fio: lookup type mismatch\n"); + goto err; + } + memcpy(inp, hent->h_addr_list[0], 4); + } } } +done: if (*port == 0) *port = fio_net_port; @@ -1006,7 +1071,8 @@ static int fio_handle_server_arg(void) goto out; ret = fio_server_parse_string(fio_server_arg, &bind_sock, &is_sock, - &port, &saddr_in.sin_addr); + &port, &saddr_in.sin_addr, + &saddr_in6.sin6_addr, &use_ipv6); if (!is_sock && bind_sock) { free(bind_sock); @@ -1016,6 +1082,7 @@ static int fio_handle_server_arg(void) out: fio_net_port = port; saddr_in.sin_port = htons(port); + saddr_in6.sin6_port = htons(port); return ret; } diff --git a/server.h b/server.h index 99689d40..27da94f2 100644 --- a/server.h +++ b/server.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "stat.h" #include "os/os.h" @@ -109,7 +110,7 @@ extern int fio_server_log(const char *format, ...); extern int fio_net_send_cmd(int, uint16_t, const void *, off_t, uint64_t); extern int fio_net_send_simple_cmd(int, uint16_t, uint64_t, struct flist_head *); extern void fio_server_set_arg(const char *); -extern int fio_server_parse_string(const char *, char **, int *, int *, struct in_addr *); +extern int fio_server_parse_string(const char *, char **, int *, int *, struct in_addr *, struct in6_addr *, int *); extern const char *fio_server_op(unsigned int); extern void fio_server_got_signal(int); -- 2.25.1