Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update client and server to allow forwarding IPv6 packets within the tunnel #65

Open
wants to merge 20 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -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
Expand Down
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

flag changes should be reflected in manpages also

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
Expand Down
2 changes: 1 addition & 1 deletion src/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
72 changes: 72 additions & 0 deletions src/client.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
#include <fcntl.h>
#include <zlib.h>
#include <time.h>
#include <stdbool.h>


#ifdef WINDOWS32
#include "windows.h"
Expand Down Expand Up @@ -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;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use int instead (also in other changes)


void
client_init()
Expand Down Expand Up @@ -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;
}
2 changes: 1 addition & 1 deletion src/client.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is an internal function, can you move it so it doesn't need to be declared here?

#endif
16 changes: 16 additions & 0 deletions src/common.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

undo

*
*/

#include <time.h>
Expand Down Expand Up @@ -557,3 +559,17 @@ fd_set_close_on_exec(int fd)
}
#endif

bool
isV6AddrSet(struct in6_addr *ip6)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

follow naming convention. also, can this replaced by using the comparison function vs a constant?

{
int i;

for (i = 0; i < sizeof(ip6->s6_addr); i++) {
if (ip6->s6_addr[i] != 0) {
return true;
}
}

return false;
}

2 changes: 2 additions & 0 deletions src/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
extern const unsigned char raw_header[RAW_HDR_LEN];

#include <stdarg.h>
#include <stdbool.h>
#ifdef WINDOWS32
#include "windows.h"
#else
Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions src/iodine.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

undo

switch(choice) {
case '4':
nameserv_family = AF_INET;
Expand Down
110 changes: 100 additions & 10 deletions src/iodined.c
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

move these into the minimal scope

char *display_ip6_buffer = NULL;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i am not sold on calling the string version of an address display.

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;

Expand Down Expand Up @@ -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];
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use lowercase names, why len 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);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

duplicate identical assignment

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];
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use memcpy based on a header struct

}
inet_ntop(AF_INET6, &v6Addr, v6AddrP, INET6_ADDRSTRLEN);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

redundant?

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) {
Expand Down Expand Up @@ -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];
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wrong indent

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')) {
Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -2418,7 +2466,6 @@ main(int argc, char **argv)
debug = 0;
netmask = 27;
pidfile = NULL;

retval = 0;

#ifdef WINDOWS32
Expand All @@ -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) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

u lost its : ?

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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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.");
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

undo

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));
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we just refuse strings with netmask set instead?

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);
Expand Down Expand Up @@ -2666,19 +2739,36 @@ 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 */
return 1;
}
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;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

undo

free((void*) other_ip);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IPv6 address string leak

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");
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does it really? shouldn't some flag be changed?

}

free((void*) other_ip);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IPv6 address string leak

}

Expand Down
Loading