client/server: IPv6 support
authorJens Axboe <axboe@kernel.dk>
Mon, 24 Oct 2011 07:11:50 +0000 (09:11 +0200)
committerJens Axboe <axboe@kernel.dk>
Mon, 24 Oct 2011 07:11:50 +0000 (09:11 +0200)
Signed-off-by: Jens Axboe <axboe@kernel.dk>
README
client.c
fio.1
server.c
server.h

diff --git a/README b/README
index d41fc6601c97adf9e788d8551df20087728f61f0..b2fdd0ba9c0319185a3e5cbda4de71b632334a0c 100644 (file)
--- 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.
 
index fda16b8e5e6c62233e6ff6f5e5abf763aee8a35e..a61bc803b4267708a7f1f8affe160f9154256ac5 100644 (file)
--- 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 d63d1f2db56275c82f5216dcdba0128c5e45dd50..3b1112256f77595fd6c696ca54bb2d83be051738 100644 (file)
--- 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.
 
index 4da8bf0040397d5173987b3b993e5a794f1959fc..6a5bc6ce937fcc24e0d6d135be6ac73335acb437 100644 (file)
--- 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:<ip or host>: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;
 }
 
index 99689d403d9c8c83e103776f01e0f80a5dd822b2..27da94f23c69e00ed089af87de833b84a612f9f2 100644 (file)
--- a/server.h
+++ b/server.h
@@ -4,6 +4,7 @@
 #include <inttypes.h>
 #include <string.h>
 #include <sys/time.h>
+#include <netinet/in.h>
 
 #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);