diff --git a/LICENSE b/LICENSE index 80845bae..224e7459 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2006-2020 iodine authors +Copyright (c) 2006-2021 iodine authors Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice diff --git a/README.md b/README.md index 179872d0..67875047 100644 --- a/README.md +++ b/README.md @@ -123,7 +123,7 @@ end of the tunnel. In this case, `ping 192.168.99.1` from the iodine client, and ### MISC. INFO #### IPv6 -The data inside the tunnel is IPv4 only. +The data inside the tunnel may be IPv4 or IPv6. The server listens to both IPv4 and IPv6 for incoming requests by default. Use options `-4` or `-6` to only listen on one protocol. Raw mode will be @@ -141,6 +141,14 @@ to your DNS setup. Extending the example above would look like this: t1ns IN A 10.15.213.99 t1ns IN AAAA 2001:db8::1001:99 +On the server, specify -S followed by an IPv6 address that will be the server end +of the IPv6 pool to allocate to clients. The server only supports a /64 subnet +mask, which is assumed and can be omitted. The first 64 bits are the network from +which IPv6 addresses are allocated from. + +The client will automatically check for IPv6 capability on the server and +assign the allocated address to its tunnel interface. No flags are needed. + #### Routing It is possible to route all traffic through the DNS tunnel. To do this, first add a host route to the nameserver used by iodine over the wired/wireless diff --git a/src/Makefile b/src/Makefile index c3f8c28c..df59a565 100644 --- a/src/Makefile +++ b/src/Makefile @@ -9,7 +9,7 @@ ARCH = `uname -m` HEAD_COMMIT = `git rev-parse --short HEAD` LIBPATH = -L. -LDFLAGS += -lz `sh osflags $(TARGETOS) link` $(LIBPATH) +LDFLAGS += -lz `sh osflags $(TARGETOS) link` $(LIBPATH) -lm CFLAGS += -std=c99 -c -g -Wall -D$(OS) -pedantic `sh osflags $(TARGETOS) cflags` -DGITREVISION=\"$(HEAD_COMMIT)\" CFLAGS += -Wstrict-prototypes -Wtype-limits -Wmissing-declarations -Wmissing-prototypes diff --git a/src/client.c b/src/client.c index 5f3eb781..39ee20b6 100644 --- a/src/client.c +++ b/src/client.c @@ -28,6 +28,8 @@ #include #include #include +#include + #ifdef WINDOWS32 #include "windows.h" @@ -101,6 +103,7 @@ static time_t lastdownstreamtime; static long send_query_sendcnt = -1; static long send_query_recvcnt = 0; static int hostname_maxlen = 0xFF; +static bool use_v6 = false; void client_init() @@ -2414,8 +2417,77 @@ client_handshake(int dns_fd, int raw_mode, int autodetect_frag_size, int fragsiz handshake_set_fragsize(dns_fd, fragsize); if (!running) return -1; + + handshake_check_v6(dns_fd); + if (!running) + return -1; } return 0; } +static +void send_v6_probe(int dns_fd) +{ + char data[4096]; + + data[0] = userid; + + send_packet(dns_fd, 'g', data, sizeof(data)); +} + +int +handshake_check_v6(int dns_fd) +{ + char in[4096]; + char server6[INET6_ADDRSTRLEN]; + char client6[INET6_ADDRSTRLEN]; + int i; + int read; + int netmask6 = 0; + int length_recieved; + + fprintf(stderr, "Autoprobing server IPV6 tunnel support\n"); + + for (i = 0; running && i < 5; i++) { + + send_v6_probe(dns_fd); + + read = handshake_waitdns(dns_fd, in, sizeof(in), 'g', 'G', i+1); + + if (read > 0) { + + /* + * including a terminating dash to allow for future IPv6 options, e.g. + * netmask. Currently assumes /64. MTU is taken from the IPv4 handshake. + * A future IPv6-only implementation would need to pass mtu + * in the IPV6 handshake. + */ + + if (sscanf(in, "%512[^-]-%512[^-]-%d", server6, client6, &netmask6) == 3) { + + fprintf(stderr, "Server tunnel IPv6 is %s\n", server6); + fprintf(stderr, "Local tunnel IPv6 is %s\n", client6); + + length_recieved = strlen(client6); + if (length_recieved > 2) { + if (tun_setip6(client6, server6, netmask6) == 0) { + + use_v6 = true; + return 0; + + } else { + errx(4, "Failed to set IPv6 tunnel address"); + } + } else { + fprintf(stderr, "Received bad IPv6 tunnel handshake\n"); + } + } + } + + fprintf(stderr, "Retrying IPv6 tunnel handshake...\n"); + } + if (!running) + return -1; + return 0; +} diff --git a/src/client.h b/src/client.h index 3dab6fb7..aa0ea3b6 100644 --- a/src/client.h +++ b/src/client.h @@ -37,5 +37,5 @@ void client_set_hostname_maxlen(int i); int client_handshake(int dns_fd, int raw_mode, int autodetect_frag_size, int fragsize); int client_tunnel(int tun_fd, int dns_fd); - +int handshake_check_v6(int tun_fd); #endif diff --git a/src/common.c b/src/common.c index 37ae48dc..3eaff218 100644 --- a/src/common.c +++ b/src/common.c @@ -13,6 +13,8 @@ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * */ #include @@ -557,3 +559,17 @@ fd_set_close_on_exec(int fd) } #endif +bool +isV6AddrSet(struct in6_addr *ip6) +{ + int i; + + for (i = 0; i < sizeof(ip6->s6_addr); i++) { + if (ip6->s6_addr[i] != 0) { + return true; + } + } + + return false; +} + diff --git a/src/common.h b/src/common.h index 0f990c13..15265323 100644 --- a/src/common.h +++ b/src/common.h @@ -33,6 +33,7 @@ extern const unsigned char raw_header[RAW_HDR_LEN]; #include +#include #ifdef WINDOWS32 #include "windows.h" #else @@ -129,6 +130,7 @@ void read_password(char*, size_t); int check_topdomain(char *, int, char **); int query_datalen(const char *qname, const char *topdomain); +bool isV6AddrSet(struct in6_addr *); #if defined(WINDOWS32) || defined(ANDROID) #ifndef ANDROID diff --git a/src/iodine.c b/src/iodine.c index fbb64812..8e1a42f8 100644 --- a/src/iodine.c +++ b/src/iodine.c @@ -193,6 +193,7 @@ int main(int argc, char **argv) #endif while ((choice = getopt(argc, argv, "46vfhru:t:d:R:P:m:M:F:T:O:L:I:")) != -1) { + switch(choice) { case '4': nameserv_family = AF_INET; diff --git a/src/iodined.c b/src/iodined.c index f9555161..9add3ad6 100644 --- a/src/iodined.c +++ b/src/iodined.c @@ -88,10 +88,16 @@ static int created_users; static int check_ip; static int my_mtu; static in_addr_t my_ip; + +char display_ip6[INET6_ADDRSTRLEN]; +char *display_ip6_buffer = NULL; +char *ip6_netmask_buffer = NULL; + +static struct in6_addr my_ip6; static int netmask; +static int ip6_netmask = 64; static in_addr_t ns_ip; - static int bind_port; static int debug; @@ -649,17 +655,32 @@ static int tunnel_tun(int tun_fd, struct dnsfd *dns_fds) char in[64*1024]; int userid; int read; - + int ip_version; + int c; + struct in6_addr v6Addr; + char v6AddrP[16]; if ((read = read_tun(tun_fd, in, sizeof(in))) <= 0) return 0; /* find target ip in packet, in is padded with 4 bytes TUN header */ header = (struct ip*) (in + 4); - userid = find_user_by_ip(header->ip_dst.s_addr); + ip_version = get_ipversion(in[4]); + + if (ip_version == 4) { /* IPv4 */ + header = (struct ip*) (in + 4); + userid = find_user_by_ip(header->ip_dst.s_addr); + } else { /* IPv6 */ + for (c = 0; c < 16; c++) { + v6Addr.s6_addr[c] = in[c + 28]; + } + inet_ntop(AF_INET6, &v6Addr, v6AddrP, INET6_ADDRSTRLEN); + userid = find_user_by_ip6(&v6Addr); + } if (userid < 0) return 0; outlen = sizeof(out); + compress2((uint8_t*)out, &outlen, (uint8_t*)in, read, 9); if (users[userid].conn == CONN_DNS_NULL) { @@ -1289,6 +1310,32 @@ handle_null_request(int tun_fd, int dns_fd, struct dnsfd *dns_fds, struct query !users[userid].lazy) send_chunk_or_dataless(dns_fd, userid, &users[userid].q); + /* IPv6 tunnel address probe */ + } else if (in[0] == 'G' || in[0] == 'g') { + char client_ip6[INET6_ADDRSTRLEN]; + char display_my_ip6[INET6_ADDRSTRLEN]; + + read = unpack_data(unpacked, sizeof(unpacked), &(in[1]), domain_len - 1, &base32_ops); + if (read < 1) { + write_dns(dns_fd, q, "BADLEN", 6, 'T'); + return; + } + + /* Ping packet, store userid */ + userid = unpacked[0]; + if (check_authenticated_user_and_ip(userid, q) != 0) { + write_dns(dns_fd, q, "BADIP", 5, 'T'); + return; /* illegal id */ + } + + inet_ntop(AF_INET6, &users[userid].tun_ip6, client_ip6, INET6_ADDRSTRLEN); + inet_ntop(AF_INET6, &my_ip6, display_my_ip6, INET6_ADDRSTRLEN); + read = snprintf(out, sizeof(out), "%s-%s-%d-", + display_my_ip6, client_ip6, ip6_netmask); + + write_dns(dns_fd, q, out, read, users[userid].downenc); + return; + } else if ((in[0] >= '0' && in[0] <= '9') || (in[0] >= 'a' && in[0] <= 'f') || (in[0] >= 'A' && in[0] <= 'F')) { @@ -2277,7 +2324,7 @@ static void print_usage(FILE *stream) "Usage: %s [-46cDfsv] [-u user] [-t chrootdir] [-d device] [-m mtu]\n" " [-z context] [-l ipv4 listen address] [-L ipv6 listen address]\n" " [-p port] [-n auto|external_ip] [-b dnsport] [-P password]\n" - " [-F pidfile] [-i max idle time] tunnel_ip[/netmask] topdomain\n", + " [-F pidfile] [-S ipv6 tunnel address] [-i max idle time] tunnel_ip[/netmask] topdomain\n", __progname); } @@ -2319,6 +2366,7 @@ static void help(FILE *stream) " -b port to forward normal DNS queries to (on localhost)\n" " -P password used for authentication (max 32 chars will be used)\n" " -F pidfile to write pid to a file\n" + " -S IPv6 server address within the tunnel. Netmask fixed at /64\n" " -i maximum idle time before shutting down\n\n" "tunnel_ip is the IP number of the local tunnel interface.\n" " /netmask sets the size of the tunnel network.\n" @@ -2418,7 +2466,6 @@ main(int argc, char **argv) debug = 0; netmask = 27; pidfile = NULL; - retval = 0; #ifdef WINDOWS32 @@ -2436,7 +2483,8 @@ main(int argc, char **argv) srand(time(NULL)); fw_query_init(); - while ((choice = getopt(argc, argv, "46vcsfhDu:t:d:m:l:L:p:n:b:P:z:F:i:")) != -1) { + while ((choice = getopt(argc, argv, "46vcsfhDuS:t:d:m:l:L:p:n:b:P:z:F:i:")) != -1) { + switch(choice) { case '4': addrfamily = AF_INET; @@ -2507,6 +2555,9 @@ main(int argc, char **argv) /* XXX: find better way of cleaning up ps(1) */ memset(optarg, 0, strlen(optarg)); break; + case 'S': + display_ip6_buffer = optarg; + break; case 'z': context = optarg; break; @@ -2534,10 +2585,32 @@ main(int argc, char **argv) my_ip = inet_addr(argv[0]); if (my_ip == INADDR_NONE) { - warnx("Bad IP address to use inside tunnel."); + warnx("Bad IP address to use inside tunnel."); usage(); } + + if (display_ip6_buffer != NULL) { + + ip6_netmask_buffer = strchr(display_ip6_buffer, '/'); + + if (ip6_netmask_buffer != NULL) { + if (atoi(ip6_netmask_buffer+1) != ip6_netmask) { + warnx("IPv6 address must be a 64-bit mask."); + usage(); + } + /* remove masklen */ + memcpy(display_ip6, display_ip6_buffer, strlen(display_ip6_buffer) - strlen(ip6_netmask_buffer)); + display_ip6[strlen(display_ip6)+1] = '\0'; + } + + /* IPV6 address sanity check */ + if (inet_pton(AF_INET6, display_ip6, &my_ip6) != 1) { + warnx("Bad IPv6 address to use inside tunnel."); + usage(); + } + } + topdomain = strdup(argv[1]); if (check_topdomain(topdomain, 1, &errormsg)) { warnx("Invalid topdomain: %s", errormsg); @@ -2666,7 +2739,7 @@ main(int argc, char **argv) dns_fds.v4fd = -1; dns_fds.v6fd = -1; - created_users = init_users(my_ip, netmask); + created_users = init_users(my_ip, netmask, my_ip6, ip6_netmask); if ((tun_fd = open_tun(device)) == -1) { /* nothing to clean up, just return */ @@ -2674,11 +2747,28 @@ main(int argc, char **argv) } if (!skipipconfig) { const char *other_ip = users_get_first_ip(); + const char *display_other_ip6 = users_get_first_ip6(); + + if (tun_setip(argv[0], other_ip, netmask) != 0 || tun_setmtu(mtu) != 0) { - retval = 1; + retval = 1; free((void*) other_ip); - goto cleanup; + goto cleanup; + + } + + if (display_ip6_buffer != NULL) { + if (tun_setip6(display_ip6, display_other_ip6, ip6_netmask) != 0 ) { + retval = 1; + goto cleanup; + } + } + + if ((mtu < 1280) && (sizeof(display_ip6)) != 0) { + warnx("Interface mtu of %d below the 1280 threshold needed for IPv6 tunneling.\n", mtu); + warnx("Proceeding without IPv6 tunneling\n"); } + free((void*) other_ip); } diff --git a/src/tun.c b/src/tun.c index 4c78895a..ec5c909b 100644 --- a/src/tun.c +++ b/src/tun.c @@ -35,6 +35,11 @@ #include #endif +#if defined FREEBSD || defined NETBSD +#include +#include +#endif + #ifndef IFCONFIGPATH #define IFCONFIGPATH "PATH=/sbin:/bin " #endif @@ -82,6 +87,7 @@ static char if_name[250]; #include #include + int open_tun(const char *tun_device) { @@ -452,7 +458,15 @@ open_tun(const char *tun_device) snprintf(tun_name, sizeof(tun_name), "/dev/tun%d", i); if ((tun_fd = open(tun_name, O_RDWR)) >= 0) { - fprintf(stderr, "Opened %s\n", tun_name); +#if defined FREEBSD || defined NETBSD + /* FreeBSD requires a packet header for + * IPv6 traffic + */ + if (ioctl(tun_fd, TUNSIFHEAD, &(int){1}) != 0) { + fprintf(stderr, "Not able to enable TUNSIFHEAD\n"); + break; + } +#endif /* LINUX */ snprintf(if_name, sizeof(if_name), "tun%d", i); fd_set_close_on_exec(tun_fd); return tun_fd; @@ -530,9 +544,12 @@ read_tun(int tun_fd, char *buf, size_t len) static int tun_uses_header(void) { -#if defined (FREEBSD) || defined (NETBSD) - /* FreeBSD/NetBSD has no header */ - return 0; +#if defined FREEBSD || defined NETBSD || defined OPENBSD + /* To enable IPv6 in FreeBSD tunnels, tunnel + * headers now enabled for that platform + */ + return 1; + #elif defined (DARWIN) /* Darwin tun has no header, Darwin utun does */ return !strncmp(if_name, "utun", 4); @@ -544,24 +561,83 @@ tun_uses_header(void) int write_tun(int tun_fd, char *data, size_t len) { + + int ip_version = 0; + if (!tun_uses_header()) { data += 4; len -= 4; } else { + + ip_version = get_ipversion(data[4]); + + if (ip_version < 0) { + return 1; /* Cannot read IP version number from packet */ + } + #ifdef LINUX + + if (ip_version == 4) { // Linux prefixes with 32 bits ethertype // 0x0800 for IPv4, 0x86DD for IPv6 - data[0] = 0x00; - data[1] = 0x00; - data[2] = 0x08; - data[3] = 0x00; -#else /* OPENBSD and DARWIN(utun) */ + data[0] = 0x00; + data[1] = 0x00; + data[2] = 0x08; + data[3] = 0x00; + } else { /* IPV6 */ + data[0] = 0x00; + data[1] = 0x00; + data[2] = 0x86; + data[3] = 0xDD; + } +#elif defined (FREEBSD) || defined (OPENBSD) + + // BSDs prefix with 32 bits address family + // AF_INET for IPv4, AF_INET6 for IPv6 + if (ip_version == 4) { + data[0] = 0x00; + data[1] = 0x00; + data[2] = 0x00; + data[3] = 0x02; + } else { /* IPV6 */ + data[0] = 0x00; + data[1] = 0x00; + data[2] = 0x00; + data[3] = 0x1C; + + } + +#elif defined NETBSD + + // BSDs prefix with 32 bits address family + // AF_INET for IPv4, AF_INET6 for IPv6 + if (ip_version == 4) { + data[0] = 0x00; + data[1] = 0x00; + data[2] = 0x00; + data[3] = 0x02; + } else { /* IPV6 */ + data[0] = 0x00; + data[1] = 0x00; + data[2] = 0x00; + data[3] = 0x18; + + } +#else /* DARWIN(utun) and all others */ + // BSDs prefix with 32 bits address family // AF_INET for IPv4, AF_INET6 for IPv6 - data[0] = 0x00; - data[1] = 0x00; - data[2] = 0x00; - data[3] = 0x02; + if (ip_version == 4) { + data[0] = 0x00; + data[1] = 0x00; + data[2] = 0x00; + data[3] = 0x02; + } else { /* IPV6 */ + data[0] = 0x00; + data[1] = 0x00; + data[2] = 0x00; + data[3] = 0x1E; + } #endif } @@ -630,6 +706,7 @@ tun_setip(const char *ip, const char *other_ip, int netbits) # else display_ip = ip; # endif + snprintf(cmdline, sizeof(cmdline), IFCONFIGPATH "ifconfig %s %s %s netmask %s", if_name, @@ -687,6 +764,90 @@ tun_setip(const char *ip, const char *other_ip, int netbits) if_name, ip, inet_ntoa(net)); return system(cmdline); #endif + +} + +int +tun_setip6(char *display_ip6, const char *display_other_ip6, int netbits6) +{ + int v6_r; + struct in6_addr ip6; + char v6_cmdline[512]; + if (inet_pton(AF_INET6, display_ip6, &ip6) < 1){ + warnx("Error in IPv6 address"); + } + +#ifdef WINDOWS32 + /* + DWORD status; + DWORD ipdata[3]; + struct in_addr addr; + DWORD len; + */ +#endif + + if (netbits6 > 0) { + + fprintf(stderr, "Setting IPv6 of %s to %s\n", if_name, display_ip6); + +#if defined LINUX + snprintf(v6_cmdline, sizeof(v6_cmdline), + IFCONFIGPATH "ifconfig %s inet6 add %s/%d", + if_name, + display_ip6, netbits6); +#else + snprintf(v6_cmdline, sizeof(v6_cmdline), + IFCONFIGPATH "ifconfig %s inet6 %s/%d", + if_name, + display_ip6, netbits6); +#endif + + v6_r = system(v6_cmdline); + + if (v6_r != 0) { + return v6_r; + } else { + return 0; + } + } + +#ifdef WINDOWS32 /* WINDOWS32 */ + + /* Set device as connected */ + fprintf(stderr, "Enabling interface '%s'\n", if_name); + status = 1; + r = DeviceIoControl(dev_handle, TAP_IOCTL_SET_MEDIA_STATUS, &status, + sizeof(status), &status, sizeof(status), &len, NULL); + if (!r) { + fprintf(stderr, "Failed to enable interface\n"); + return -1; + } + + if (inet_aton(ip, &addr)) { + ipdata[0] = (DWORD) addr.s_addr; /* local ip addr */ + ipdata[1] = net.s_addr & ipdata[0]; /* network addr */ + ipdata[2] = (DWORD) net.s_addr; /* netmask */ + } else { + return -1; + } + + /* Tell ip/networkaddr/netmask to device for arp use */ + r = DeviceIoControl(dev_handle, TAP_IOCTL_CONFIG_TUN, &ipdata, + sizeof(ipdata), &ipdata, sizeof(ipdata), &len, NULL); + if (!r) { + fprintf(stderr, "Failed to set interface in TUN mode\n"); + return -1; + } + + /* use netsh to set ip address */ + fprintf(stderr, "Setting IP of interface '%s' to %s (can take a few seconds)...\n", if_name, ip); + snprintf(cmdline, sizeof(cmdline), "netsh interface ip set address \"%s\" static %s %s", + if_name, ip, inet_ntoa(net)); + return system(cmdline); + + +#endif + return 0; } int @@ -714,3 +875,18 @@ tun_setmtu(const unsigned mtu) #endif } +int get_ipversion(char first_byte) +{ + + int v; + + v = first_byte & 0xf0; + + if (v == 64) { + return 4; + } else if (v == 96) { + return 6; + } else { + return -1; + } +} diff --git a/src/tun.h b/src/tun.h index 8982a9f8..43049ea8 100644 --- a/src/tun.h +++ b/src/tun.h @@ -23,6 +23,8 @@ void close_tun(int); int write_tun(int, char *, size_t); ssize_t read_tun(int, char *, size_t); int tun_setip(const char *, const char *, int); +int tun_setip6(char *, const char *, int); int tun_setmtu(const unsigned); +int get_ipversion(char); #endif /* _TUN_H_ */ diff --git a/src/user.c b/src/user.c index b0ecdbaf..8978165d 100644 --- a/src/user.c +++ b/src/user.c @@ -23,6 +23,7 @@ #include #include #include +#include #ifdef WINDOWS32 #include @@ -37,10 +38,12 @@ struct tun_user *users; unsigned usercount; -int init_users(in_addr_t my_ip, int netbits) +int init_users(in_addr_t my_ip, int netbits, struct in6_addr my_ip6, int netbits6) { int i; + int i6; int skip = 0; + int skip6 = 0; char newip[16]; int maxusers; @@ -48,7 +51,13 @@ int init_users(in_addr_t my_ip, int netbits) in_addr_t netmask = 0; struct in_addr net; struct in_addr ipstart; + struct in6_addr ip6start; + unsigned ip6_netmask[16]; + bool ip6_enabled; + + ip6_enabled = isV6AddrSet(&my_ip6); + for (i = 0; i < netbits; i++) { netmask = (netmask << 1) | 1; } @@ -56,22 +65,77 @@ int init_users(in_addr_t my_ip, int netbits) net.s_addr = htonl(netmask); ipstart.s_addr = my_ip & net.s_addr; + /* Covert IPv6 netbits to IPv6 netmask and work + * out the network address from my IP address. Start + * assigning IPv6 address from the network address + 1 + */ + if (ip6_enabled == true) { + for (i6 = 0; i6 < netbits6 / 8; i6++) { + ip6_netmask[i6] |= 0xFF; + } + + ip6_netmask[netbits6 / 8] = pow(2, (netbits6 % 8 )) - 1; + ip6_netmask[netbits6 / 8] <<= (8-(netbits6 % 8)); + + for (i6 = 0; i6 < 16; i6++) { + ip6start.s6_addr[i6] = my_ip6.s6_addr[i6] & ip6_netmask[i6]; + } + } maxusers = (1 << (32-netbits)) - 3; /* 3: Net addr, broadcast addr, iodined addr */ usercount = MIN(maxusers, USERS); users = calloc(usercount, sizeof(struct tun_user)); + /* + * IPv6 note: Current behavior is to populate the users structure + * with the IPv4 addresses that are expected to be used. + * In the future with IPv6-only tunnel transport, we should not be + * populating a /64 (or whatever mask) in the users structure + * and should shift to an on-demand scheme. For now + * we expect dual-stack and pre-allocate IPv6 addresses into the + * users struct as we do with IPv4. + */ for (i = 0; i < usercount; i++) { in_addr_t ip; users[i].id = i; + snprintf(newip, sizeof(newip), "0.0.0.%d", i + skip + 1); + ip = ipstart.s_addr + inet_addr(newip); if (ip == my_ip && skip == 0) { /* This IP was taken by iodined */ skip++; snprintf(newip, sizeof(newip), "0.0.0.%d", i + skip + 1); ip = ipstart.s_addr + inet_addr(newip); + } users[i].tun_ip = ip; + + if (ip6_enabled == true) { + struct in6_addr temp_ip6; + /* + * start assigning host addresses from the network address + 1 + * unless that is my_ip, in which case, use the following address. + */ + memcpy(temp_ip6.s6_addr, ip6start.s6_addr, sizeof(ip6start.s6_addr)); + temp_ip6.s6_addr[15] = ip6start.s6_addr[15] + skip + 1 + i; + + if (v6AddressesEqual(&temp_ip6, &my_ip6) == true && + skip6 == 0) { + /* This IPv6 was taken by iodined */ + skip6++; + + /* + * We expect to start assigning addresses at the network address + 1 and + * to not worry about assigning more than 254 host addresses. If we did, we have to + * iterate through lower order bytes of ip6. This plus a few other corner cases + * is why we enourage/force/assume the user to specify a /64 V6 address + */ + + temp_ip6.s6_addr[15] = ip6start.s6_addr[15] + skip + 1 + i; + + } + memcpy(users[i].tun_ip6.s6_addr, temp_ip6.s6_addr, sizeof(temp_ip6.s6_addr)); + } net.s_addr = ip; users[i].disabled = 0; users[i].authenticated = 0; @@ -91,6 +155,50 @@ const char *users_get_first_ip(void) return strdup(inet_ntoa(ip)); } +const char *users_get_first_ip6(void) +{ + struct in6_addr ip6; + char display_ip6[INET6_ADDRSTRLEN]; + + memcpy(&ip6, &users[0].tun_ip6, sizeof(struct in6_addr)); + + inet_ntop(AF_INET6, &ip6, display_ip6, INET6_ADDRSTRLEN); + return strdup(display_ip6); +} + + +int find_user_by_ip6(struct in6_addr *v6Addr) +{ + int i; + char v6AddrOut[32]; + + inet_ntop(AF_INET6, v6Addr, v6AddrOut, INET6_ADDRSTRLEN); + + for (i = 0; i < usercount; i++) { + if (users[i].active && + users[i].authenticated && + !users[i].disabled && + users[i].last_pkt + 60 > time(NULL) && + v6AddressesEqual(v6Addr, &users[i].tun_ip6) == true) { + return i; + } + } + return -1; +} + +bool v6AddressesEqual(struct in6_addr *v6Struct1, struct in6_addr *v6Struct2) +{ + int i; + + for (i = 0; i < 16; i++) { + if (v6Struct1->s6_addr[i] != v6Struct2->s6_addr[i]) { + return false; + } + } + return true; +} + + int find_user_by_ip(uint32_t ip) { int ret; diff --git a/src/user.h b/src/user.h index b2ba7048..4d1d2ad6 100644 --- a/src/user.h +++ b/src/user.h @@ -45,6 +45,7 @@ struct tun_user { int seed; in_addr_t tun_ip; struct sockaddr_storage host; + struct in6_addr tun_ip6; socklen_t hostlen; struct query q; struct query q_sendrealsoon; @@ -80,12 +81,15 @@ struct tun_user { extern struct tun_user *users; -int init_users(in_addr_t, int); +int init_users(in_addr_t, int, struct in6_addr, int); const char* users_get_first_ip(void); +const char* users_get_first_ip6(void); int find_user_by_ip(uint32_t); +int find_user_by_ip6(struct in6_addr *v6Addr); int all_users_waiting_to_send(void); int find_available_user(void); void user_switch_codec(int userid, const struct encoder *enc); void user_set_conn_type(int userid, enum connection c); +bool v6AddressesEqual(struct in6_addr *, struct in6_addr *); #endif