diff --git a/src/Makefile b/src/Makefile index c3f8c28..df59a56 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 a756c75..8261b68 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() @@ -1374,7 +1377,7 @@ handshake_version(int dns_fd, int *seed) } static int -handshake_login(int dns_fd, int seed, int forward_v6) +handshake_login(int dns_fd, int seed) { char in[4096]; char login[16]; @@ -1405,7 +1408,7 @@ handshake_login(int dns_fd, int seed, int forward_v6) server[64] = 0; client[64] = 0; - if (tun_setip(client, server, netmask, forward_v6) == 0 && + if (tun_setip(client, server, netmask) == 0 && tun_setmtu(mtu) == 0) { fprintf(stderr, "Server tunnel IP is %s\n", server); @@ -2326,7 +2329,7 @@ handshake_set_fragsize(int dns_fd, int fragsize) } int -client_handshake(int dns_fd, int raw_mode, int autodetect_frag_size, int fragsize, int forward_v6) +client_handshake(int dns_fd, int raw_mode, int autodetect_frag_size, int fragsize) { int seed; int upcodec; @@ -2349,7 +2352,7 @@ client_handshake(int dns_fd, int raw_mode, int autodetect_frag_size, int fragsiz return r; } - r = handshake_login(dns_fd, seed, forward_v6); + r = handshake_login(dns_fd, seed); if (r) { return r; } @@ -2414,8 +2417,72 @@ 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[1024]; + char client6[1024]; + int i; + int read; + int netmask6 = 0; + + 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); + + 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 ac61a83..aa0ea3b 100644 --- a/src/client.h +++ b/src/client.h @@ -35,7 +35,7 @@ void client_set_lazymode(int lazy_mode); void client_set_hostname_maxlen(int i); int client_handshake(int dns_fd, int raw_mode, int autodetect_frag_size, - int fragsize, int forward_v6); + 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 37ae48d..3eaff21 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 0f990c1..1526532 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 3b9b76d..8e1a42f 100644 --- a/src/iodine.c +++ b/src/iodine.c @@ -72,7 +72,7 @@ static void help(FILE *stream, bool verbose) { fprintf(stream, "iodine IP over DNS tunneling client\n\n" - "Usage: %s [-46fhrvS] [-u user] [-t chrootdir] [-d device] [-P password]\n" + "Usage: %s [-46fhrv] [-u user] [-t chrootdir] [-d device] [-P password]\n" " [-m maxfragsize] [-M maxlen] [-T type] [-O enc] [-L 0|1] [-I sec]\n" " [-z context] [-F pidfile] [nameserver] topdomain\n", __progname); @@ -100,7 +100,6 @@ static void help(FILE *stream, bool verbose) " -t dir to chroot to directory dir\n" " -d device to set tunnel device name\n" " -z context, to apply specified SELinux context after initialization\n" - " -S enable forwarding of IPv6 packets within the tunnel\n" " -F pidfile to write pid to a file\n\n" "nameserver is the IP number/hostname of the relaying nameserver. If absent,\n" " /etc/resolv.conf is used\n" @@ -153,7 +152,6 @@ int main(int argc, char **argv) struct sockaddr_storage nameservaddr; int nameservaddr_len; int nameserv_family; - int forward_v6; nameserv_host = NULL; topdomain = NULL; @@ -178,7 +176,6 @@ int main(int argc, char **argv) selecttimeout = 4; hostname_maxlen = 0xFF; nameserv_family = AF_UNSPEC; - forward_v6 = 0; #ifdef WINDOWS32 WSAStartup(req_version, &wsa_data); @@ -195,7 +192,8 @@ int main(int argc, char **argv) __progname++; #endif - while ((choice = getopt(argc, argv, "t:d:R:P:m:M:z:F:T:O:L:I:46vfhruS")) != -1) { + 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; @@ -220,9 +218,6 @@ int main(int argc, char **argv) case 'u': username = optarg; break; - case 'S': - forward_v6 = 1; - break; case 't': newroot = optarg; break; @@ -372,7 +367,7 @@ int main(int argc, char **argv) fprintf(stderr, "Sending DNS queries for %s to %s\n", topdomain, format_addr(&nameservaddr, nameservaddr_len)); - if (client_handshake(dns_fd, raw_mode, autodetect_frag_size, max_downstream_frag_size, forward_v6)) { + if (client_handshake(dns_fd, raw_mode, autodetect_frag_size, max_downstream_frag_size)) { retval = 1; goto cleanup2; } diff --git a/src/iodined.c b/src/iodined.c index 9109571..cb6f803 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; +char *ip6_netmask_buffer; + +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; @@ -658,9 +664,9 @@ static int tunnel_tun(int tun_fd, struct dnsfd *dns_fds) /* find target ip in packet, in is padded with 4 bytes TUN header */ header = (struct ip*) (in + 4); - ip_version = in[4] & 0xf0; + ip_version = get_ipversion(in[4]); - if (ip_version == 64) { /* IPv4 */ + if (ip_version == 4) { /* IPv4 */ header = (struct ip*) (in + 4); userid = find_user_by_ip(header->ip_dst.s_addr); } else { /* IPv6 */ @@ -674,6 +680,7 @@ static int tunnel_tun(int tun_fd, struct dnsfd *dns_fds) return 0; outlen = sizeof(out); + compress2((uint8_t*)out, &outlen, (uint8_t*)in, read, 9); if (users[userid].conn == CONN_DNS_NULL) { @@ -1303,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')) { @@ -2288,10 +2321,10 @@ write_dns(int fd, struct query *q, const char *data, int datalen, char downenc) static void print_usage(FILE *stream) { fprintf(stream, - "Usage: %s [-46cDfsvS] [-u user] [-t chrootdir] [-d device] [-m mtu]\n" + "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); } @@ -2333,7 +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 enable forwarding of IPv6 packets within the tunnel\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" @@ -2405,7 +2438,6 @@ main(int argc, char **argv) int dns4addr_len; struct sockaddr_storage dns6addr; int dns6addr_len; - int forward_v6; #ifdef HAVE_SYSTEMD int nb_fds; #endif @@ -2434,7 +2466,6 @@ main(int argc, char **argv) debug = 0; netmask = 27; pidfile = NULL; - forward_v6 = 0; retval = 0; #ifdef WINDOWS32 @@ -2452,7 +2483,8 @@ main(int argc, char **argv) srand(time(NULL)); fw_query_init(); - while ((choice = getopt(argc, argv, "t:d:m:l:L:p:n:b:P:z:F:i:46vcsSfhDu")) != -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; @@ -2524,7 +2556,7 @@ main(int argc, char **argv) memset(optarg, 0, strlen(optarg)); break; case 'S': - forward_v6 = 1; + display_ip6_buffer = optarg; break; case 'z': context = optarg; @@ -2553,10 +2585,28 @@ 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(); } + + ip6_netmask_buffer = strchr(display_ip6_buffer, '/'); + if (ip6_netmask_buffer) { + 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); @@ -2685,7 +2735,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 */ @@ -2693,16 +2743,26 @@ main(int argc, char **argv) } if (!skipipconfig) { const char *other_ip = users_get_first_ip(); - if (tun_setip(argv[0], other_ip, netmask, forward_v6) != 0 || tun_setmtu(mtu) != 0) { + const char *display_other_ip6 = users_get_first_ip6(); + + + if (tun_setip(argv[0], other_ip, netmask) != 0 || tun_setmtu(mtu) != 0) { retval = 1; free((void*) other_ip); goto cleanup; - + } - if ((mtu < 1280) && (forward_v6)) { - warnx("Interface mtu of %d below the 1280 threshold needed for IPv6 tunneling.\n", mtu); - warnx("Proceeding without IPv6 tunneling\n"); - } + + 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 0e658b5..ec5c909 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,41 +561,82 @@ 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 { - int i = data[4] & 0xf0; + + ip_version = get_ipversion(data[4]); + + if (ip_version < 0) { + return 1; /* Cannot read IP version number from packet */ + } + #ifdef LINUX - - if (i == 64) { - // Look at the fifth bype + + 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 { /* 96 for IPV6 */ + } else { /* IPV6 */ data[0] = 0x00; data[1] = 0x00; data[2] = 0x86; data[3] = 0xDD; - } -#else /* OPENBSD and DARWIN(utun) */ + } +#elif defined (FREEBSD) || defined (OPENBSD) + // BSDs prefix with 32 bits address family // AF_INET for IPv4, AF_INET6 for IPv6 - if (i == 64) { + if (ip_version == 4) { data[0] = 0x00; data[1] = 0x00; data[2] = 0x00; data[3] = 0x02; - } else { /* 96 for IPV6 */ + } 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 + 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 } @@ -610,14 +668,12 @@ read_tun(int tun_fd, char *buf, size_t len) #endif int -tun_setip(const char *ip, const char *other_ip, int netbits, int forward_v6) +tun_setip(const char *ip, const char *other_ip, int netbits) { char cmdline[512]; - char v6_cmdline[512]; int netmask; struct in_addr net; int i; - int v6_r; #ifndef LINUX int r; #endif @@ -650,20 +706,7 @@ tun_setip(const char *ip, const char *other_ip, int netbits, int forward_v6) # else display_ip = ip; # endif - if (forward_v6) { - fprintf(stderr, "Setting IPv6 of %s to ::%s\n", if_name, ip); - snprintf(v6_cmdline, sizeof(cmdline), - IFCONFIGPATH "ifconfig %s inet6 add ::%s/64", - if_name, - display_ip); - - v6_r = system(v6_cmdline); - - if (v6_r != 0) { - return v6_r; - } - } snprintf(cmdline, sizeof(cmdline), IFCONFIGPATH "ifconfig %s %s %s netmask %s", if_name, @@ -722,14 +765,89 @@ tun_setip(const char *ip, const char *other_ip, int netbits, int forward_v6) return system(cmdline); #endif - if (forward_v6) { - snprintf(cmdline, sizeof(cmdline), - IFCONFIGPATH "ifconfig %s inet6 add ::%s/64", - if_name, - ip); +} - fprintf(stderr, "Setting IP of %s to %s\n", if_name, ip); +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 @@ -757,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 a57c007..43049ea 100644 --- a/src/tun.h +++ b/src/tun.h @@ -22,7 +22,9 @@ int open_tun(const char *); 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); +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 253694e..8978165 100644 --- a/src/user.c +++ b/src/user.c @@ -23,6 +23,7 @@ #include #include #include +#include #ifdef WINDOWS32 #include @@ -37,25 +38,26 @@ 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]; - char ip6Tmp[16]; - char ip6Tmp2[18]; int maxusers; in_addr_t netmask = 0; struct in_addr net; struct in_addr ipstart; - - /* For IPv6, we take the IPv4 address and simply prepend :: - * and use a 64-bit mask. Reduces the need to parse - * netmasks. - */ + 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; } @@ -63,17 +65,39 @@ 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; - memset(ip6Tmp,0,strlen(ip6Tmp)); - memset(ip6Tmp2,0,strlen(ip6Tmp2)); - snprintf(newip, sizeof(newip), "0.0.0.%d", i + skip + 1); ip = ipstart.s_addr + inet_addr(newip); @@ -82,15 +106,36 @@ int init_users(in_addr_t my_ip, int netbits) skip++; snprintf(newip, sizeof(newip), "0.0.0.%d", i + skip + 1); ip = ipstart.s_addr + inet_addr(newip); - } users[i].tun_ip = ip; - inet_ntop(AF_INET, &ip, ip6Tmp, INET_ADDRSTRLEN); - snprintf(ip6Tmp2, sizeof(ip6Tmp2), "::%s", ip6Tmp); - inet_pton(AF_INET6, ip6Tmp2, &users[i].tun_ip6); - + 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; @@ -110,6 +155,18 @@ 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; @@ -122,23 +179,23 @@ int find_user_by_ip6(struct in6_addr *v6Addr) users[i].authenticated && !users[i].disabled && users[i].last_pkt + 60 > time(NULL) && - (areV6AddressesEqual(v6Addr, &users[i].tun_ip6) == 0) ) { + v6AddressesEqual(v6Addr, &users[i].tun_ip6) == true) { return i; - } + } } return -1; } -int areV6AddressesEqual(struct in6_addr *v6Struct1, struct in6_addr *v6Struct2) +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 -1; + return false; } } - return 0; + return true; } diff --git a/src/user.h b/src/user.h index bf160a6..4d1d2ad 100644 --- a/src/user.h +++ b/src/user.h @@ -81,14 +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); -int areV6AddressesEqual(struct in6_addr *, struct in6_addr *); +bool v6AddressesEqual(struct in6_addr *, struct in6_addr *); #endif