From c6e1675108df5e3fc9f4e48d0909416753023d3e Mon Sep 17 00:00:00 2001 From: sr Date: Tue, 15 Jan 2013 19:02:07 +0100 Subject: [KERNEL/CLIENT] Add IPv6 support --- src/client/client.c | 102 +++++++++++++++++++++++++++++++++++++++++++------- src/kernel/net.c | 90 +++++++++++++++++++++++++++++++------------- src/server/saveload.c | 1 - 3 files changed, 151 insertions(+), 42 deletions(-) diff --git a/src/client/client.c b/src/client/client.c index 397ac38..bd28d51 100644 --- a/src/client/client.c +++ b/src/client/client.c @@ -59,26 +59,101 @@ void dnbd3_print_version() exit(EXIT_SUCCESS); } -static void dnbd3_get_ip(char *hostname, uint8_t *target, uint8_t *addrtype) +/** + * Parse IPv4 or IPv6 address in string representation to a suitable format usable by the BSD socket library + * @string eg. "1.2.3.4" or "2a01::10:5", optially with port appended, eg "1.2.3.4:6666" or "[2a01::10:5]:6666" + * @af will contain either AF_INET or AF_INET6 + * @addr will contain the address in network representation + * @port will contain the port in network representation, defaulting to #define PORT if none was given + * returns 1 on success, 0 in failure. contents of af, addr and port are undefined in the latter case + * !! Contents of @string might be modified by this function !! + */ +static char parse_address(char *string, dnbd3_host_t *host) { - struct hostent *host; + struct in_addr v4; + struct in6_addr v6; + + // Try IPv4 without port + if (1 == inet_pton(AF_INET, string, &v4)) + { + host->type = AF_INET; + memcpy(host->addr, &v4, 4); + host->port = htons(PORT); + return 1; + } + // Try IPv6 without port + if (1 == inet_pton(AF_INET6, string, &v6)) + { + host->type = AF_INET6; + memcpy(host->addr, &v6, 16); + host->port = htons(PORT); + return 1; + } + + // Scan for port + char *portpos = NULL, *ptr = string; + while (*ptr) + { + if (*ptr == ':') + portpos = ptr; + ++ptr; + } + if (portpos == NULL) + return 0; // No port in string + // Consider IP being surrounded by [ ] + if (*string == '[' && *(portpos - 1) == ']') + { + ++string; + *(portpos - 1) = '\0'; + } + *portpos++ = '\0'; + int p = atoi(portpos); + if (p < 1 || p > 65535) + return 0; // Invalid port + host->port = htons((uint16_t)p); + + // Try IPv4 with port + if (1 == inet_pton(AF_INET, string, &v4)) + { + host->type = AF_INET; + memcpy(host->addr, &v4, 4); + return 1; + } + // Try IPv6 with port + if (1 == inet_pton(AF_INET6, string, &v6)) + { + host->type = AF_INET6; + memcpy(host->addr, &v6, 16); + return 1; + } + + // FAIL + return 0; +} - if ((host = gethostbyname(hostname)) == NULL) +static void dnbd3_get_ip(char *hostname, dnbd3_host_t *host) +{ + if (parse_address(hostname, host)) + return; + // TODO: Parse port too for host names + struct hostent *hent; + if ((hent = gethostbyname(hostname)) == NULL) { printf("FATAL: Unknown host '%s'\n", hostname); exit(EXIT_FAILURE); } - *addrtype = (uint8_t)host->h_addrtype; - if (host->h_addrtype == AF_INET) - memcpy(target, host->h_addr, 4); - else if (host->h_addrtype == AF_INET6) - memcpy(target, host->h_addr, 16); + host->type = (uint8_t)hent->h_addrtype; + if (hent->h_addrtype == AF_INET) + memcpy(host->addr, hent->h_addr, 4); + else if (hent->h_addrtype == AF_INET6) + memcpy(host->addr, hent->h_addr, 16); else { - printf("FATAL: Unknown address type: %d\n", host->h_addrtype); + printf("FATAL: Unknown address type: %d\n", hent->h_addrtype); exit(EXIT_FAILURE); } + host->port = htons(PORT); } int main(int argc, char *argv[]) @@ -125,8 +200,7 @@ int main(int argc, char *argv[]) _config_file_name = strdup(optarg); break; case 'h': - dnbd3_get_ip(optarg, msg.host.addr, &msg.host.type); - printf("Host set to %s (type %d)\n", inet_ntoa(*(struct in_addr *)msg.host.addr), (int)msg.host.type); + dnbd3_get_ip(optarg, &msg.host); break; case 'i': msg.imgname = strdup(optarg); @@ -147,7 +221,7 @@ int main(int argc, char *argv[]) close_dev = 1; break; case 's': - dnbd3_get_ip(optarg, msg.host.addr, &msg.host.type); + dnbd3_get_ip(optarg, &msg.host); switch_host = 1; break; case 'H': @@ -215,7 +289,7 @@ int main(int argc, char *argv[]) exit(EXIT_SUCCESS); } - // use configuration file if exist + // use configuration file if existent GKeyFile *gkf; int i = 0; size_t j = 0; @@ -229,7 +303,7 @@ int main(int argc, char *argv[]) for (i = 0; i < j; i++) { - dnbd3_get_ip(g_key_file_get_string(gkf, groups[i], "server", NULL), msg.host.addr, &msg.host.type); + dnbd3_get_ip(g_key_file_get_string(gkf, groups[i], "server", NULL), &msg.host); msg.imgname = g_key_file_get_string(gkf, groups[i], "name", NULL); msg.rid = g_key_file_get_integer(gkf, groups[i], "rid", NULL); dev = g_key_file_get_string(gkf, groups[i], "device", NULL); diff --git a/src/kernel/net.c b/src/kernel/net.c index f225189..0217d26 100644 --- a/src/kernel/net.c +++ b/src/kernel/net.c @@ -67,13 +67,13 @@ #else // Silent -#define debug_dev(x) while(0) +#define debug_dev(x) do { } while(0) #define error_dev(x) goto error -#define debug_dev_va(x, ...) while(0) +#define debug_dev_va(x, ...) do { } while(0) #define error_dev_va(x, ...) goto error -#define debug_alt(x) while(0) +#define debug_alt(x) do { } while(0) #define error_alt(x) goto error -#define debug_alt_va(x, ...) while(0) +#define debug_alt_va(x, ...) do { } while(0) #define error_alt_va(x, ...) goto error #endif @@ -119,9 +119,7 @@ static inline dnbd3_server_t *get_free_alt_server(dnbd3_device_t *const dev) int dnbd3_net_connect(dnbd3_device_t *dev) { - struct sockaddr_in sin; struct request *req1 = NULL; - struct timeval timeout; char get_servers = 0, set_client = 0; @@ -155,10 +153,10 @@ int dnbd3_net_connect(dnbd3_device_t *dev) if (dev->sock) error_dev("ERROR: Already connected."); - if (dev->cur_server.host.type != AF_INET) - error_dev("ERROR: IPv6 not implemented."); - else - debug_dev("INFO: Connecting..."); + if (dev->cur_server.host.type != AF_INET && dev->cur_server.host.type != AF_INET6) + error_dev_va("ERROR: Unknown address type %d", (int)dev->cur_server.host.type); + + debug_dev("INFO: Connecting..."); if (dev->better_sock == NULL) { @@ -171,16 +169,33 @@ int dnbd3_net_connect(dnbd3_device_t *dev) char *name; int mlen; init_msghdr(msg); - if (sock_create_kern(AF_INET, SOCK_STREAM, IPPROTO_TCP, &dev->sock) < 0) - error_dev("ERROR: Couldn't create socket."); + + if (sock_create_kern(dev->cur_server.host.type, SOCK_STREAM, IPPROTO_TCP, &dev->sock) < 0) + error_dev("ERROR: Couldn't create socket (v6)."); + kernel_setsockopt(dev->sock, SOL_SOCKET, SO_SNDTIMEO, (char *) &timeout, sizeof(timeout)); kernel_setsockopt(dev->sock, SOL_SOCKET, SO_RCVTIMEO, (char *) &timeout, sizeof(timeout)); dev->sock->sk->sk_allocation = GFP_NOIO; - sin.sin_family = AF_INET; - memcpy(&(sin.sin_addr.s_addr), dev->cur_server.host.addr, 4); - sin.sin_port = dev->cur_server.host.port; - if (kernel_connect(dev->sock, (struct sockaddr *) &sin, sizeof(sin), 0) != 0) - error_dev("FATAL: Connection to host failed."); + if (dev->cur_server.host.type == AF_INET) + { + struct sockaddr_in sin; + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + memcpy(&(sin.sin_addr), dev->cur_server.host.addr, 4); + sin.sin_port = dev->cur_server.host.port; + if (kernel_connect(dev->sock, (struct sockaddr *) &sin, sizeof(sin), 0) != 0) + error_dev("FATAL: Connection to host failed. (v4)"); + } + else + { + struct sockaddr_in6 sin; + memset(&sin, 0, sizeof(sin)); + sin.sin6_family = AF_INET6; + memcpy(&(sin.sin6_addr), dev->cur_server.host.addr, 16); + sin.sin6_port = dev->cur_server.host.port; + if (kernel_connect(dev->sock, (struct sockaddr *) &sin, sizeof(sin), 0) != 0) + error_dev("FATAL: Connection to host failed. (v6)"); + } // Request filesize dnbd3_request.magic = dnbd3_packet_magic; dnbd3_request.cmd = CMD_SELECT_IMAGE; @@ -395,7 +410,8 @@ void dnbd3_net_heartbeat(unsigned long arg) int dnbd3_net_discover(void *data) { dnbd3_device_t *dev = data; - struct sockaddr_in sin; + struct sockaddr_in sin4; + struct sockaddr_in6 sin6; struct socket *sock, *best_sock = NULL; dnbd3_request_t dnbd3_request; @@ -421,6 +437,9 @@ int dnbd3_net_discover(void *data) timeout.tv_sec = SOCKET_TIMEOUT_CLIENT_DISCOVERY; timeout.tv_usec = 0; + memset(&sin4, 0, sizeof(sin4)); + memset(&sin6, 0, sizeof(sin6)); + init_msghdr(msg); buf = kmalloc(4096, GFP_KERNEL); @@ -454,7 +473,7 @@ int dnbd3_net_discover(void *data) spin_lock_irqsave(&dev->blk_lock, irqflags); for (i = 0; i < dev->new_servers_num; ++i) { - if (dev->new_servers[i].host.type != AF_INET) // Invalid entry.. (Add IPv6 someday) + if (dev->new_servers[i].host.type != AF_INET && dev->new_servers[i].host.type != AF_INET6) // Invalid entry? continue; alt_server = get_existing_server(&dev->new_servers[i], dev); if (alt_server != NULL) // Server already known @@ -462,8 +481,11 @@ int dnbd3_net_discover(void *data) if (dev->new_servers[i].failures == 1) { // REMOVE request + if (alt_server->host.type == AF_INET) + debug_dev_va("Removing alt server %pI4", alt_server->host.addr); + else + debug_dev_va("Removing alt server %pI6", alt_server->host.addr); alt_server->host.type = 0; - debug_dev_va("Removing alt server %pI4", alt_server->host.addr); continue; } // ADD, so just reset fail counter @@ -477,7 +499,10 @@ int dnbd3_net_discover(void *data) continue; // Add new server entry alt_server->host = dev->new_servers[i].host; - debug_dev_va("Adding alt server %pI4", alt_server->host.addr); + if (alt_server->host.type == AF_INET) + debug_dev_va("Adding alt server %pI4", alt_server->host.addr); + else + debug_dev_va("Adding alt server %pI6", alt_server->host.addr); alt_server->rtts[0] = alt_server->rtts[1] = alt_server->rtts[2] = alt_server->rtts[3] = RTT_UNREACHABLE; @@ -499,7 +524,7 @@ int dnbd3_net_discover(void *data) continue; // Initialize socket and connect - if (sock_create_kern(AF_INET, SOCK_STREAM, IPPROTO_TCP, &sock) < 0) + if (sock_create_kern(dev->alt_servers[i].host.type, SOCK_STREAM, IPPROTO_TCP, &sock) < 0) { debug_alt("ERROR: Couldn't create socket (discover)."); sock = NULL; @@ -508,11 +533,22 @@ int dnbd3_net_discover(void *data) kernel_setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (char *) &timeout, sizeof(timeout)); kernel_setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char *) &timeout, sizeof(timeout)); sock->sk->sk_allocation = GFP_NOIO; - sin.sin_family = AF_INET; // add IPv6..... - memcpy(&sin.sin_addr.s_addr, dev->alt_servers[i].host.addr, 4); - sin.sin_port = dev->alt_servers[i].host.port; - if (kernel_connect(sock, (struct sockaddr *) &sin, sizeof(sin), 0) < 0) - goto error; + if (dev->alt_servers[i].host.type == AF_INET) + { + sin4.sin_family = AF_INET; + memcpy(&sin4.sin_addr, dev->alt_servers[i].host.addr, 4); + sin4.sin_port = dev->alt_servers[i].host.port; + if (kernel_connect(sock, (struct sockaddr *) &sin4, sizeof(sin4), 0) < 0) + goto error; + } + else + { + sin6.sin6_family = AF_INET6; + memcpy(&sin6.sin6_addr, dev->alt_servers[i].host.addr, 16); + sin6.sin6_port = dev->alt_servers[i].host.port; + if (kernel_connect(sock, (struct sockaddr *) &sin6, sizeof(sin6), 0) < 0) + goto error; + } // Request filesize dnbd3_request.cmd = CMD_SELECT_IMAGE; diff --git a/src/server/saveload.c b/src/server/saveload.c index c3b0ff3..e888f50 100644 --- a/src/server/saveload.c +++ b/src/server/saveload.c @@ -36,7 +36,6 @@ static GKeyFile *_config_handle = NULL; static dnbd3_image_t *prepare_image(char *image_name, int rid, char *image_file, char *cache_file); -//static char* get_local_image_name(char *global_name); void dnbd3_load_config() -- cgit v1.2.3-55-g7522