summary refs log tree commit diff
path: root/fatvpnd.c
diff options
context:
space:
mode:
Diffstat (limited to 'fatvpnd.c')
-rw-r--r--fatvpnd.c228
1 files changed, 228 insertions, 0 deletions
diff --git a/fatvpnd.c b/fatvpnd.c
new file mode 100644
index 0000000..767f305
--- /dev/null
+++ b/fatvpnd.c
@@ -0,0 +1,228 @@
+/* fatvpn daemon by eeeee
+ * Public Domain */
+
+#include "config.h"
+#include "common.h"
+
+#define LOGFILE "/tmp/fatvpnd.log"
+#define MAX_PEERS 16
+#define TIMEOUT_D  180 /* timeout (no packets) in seconds for disconnect */
+
+struct {
+	struct sockaddr_in6 a;
+	u32 lptime; /* time of last packet */
+	u8 mac[6];  /* internal mac */
+} peer[MAX_PEERS];
+
+u32 ccnt;
+
+int main(int argc, char *argv[]){
+	u8  *pkt;
+	s32 res, tap, fdmax, skt, i, psz;
+	struct sockaddr_in6 c;
+	struct timespec     t;
+	struct termios      attr;
+	struct ifreq        ifr;
+	fd_set rset;
+
+	/* arguments */
+	if (argc != 3 && argc != 4){
+		fprintf(stderr, "Using:\n\t"
+			"%s <tap name> [address] <port>\n\n"
+		        "Example:\n\t"
+			"%s tap0 2222\n\t%s tap0 127.0.0.1 2222\n\n",
+		        argv[0], argv[0], argv[0]);
+		exit(1);
+	}
+
+	memset(&c, 0, sizeof(c));
+	c.sin6_family = AF_INET6;
+	c.sin6_port = htons((u16)atoi(argv[argc-1]));
+	if (argc == 4){
+		res = inet_pton(AF_INET6, argv[2], c.sin6_addr.s6_addr);
+		if (res != 1){
+			res = inet_pton(AF_INET, argv[2], &psz);
+			if (res != 1){
+				fprintf(stderr, "wrong addr: %s\n",argv[2]);
+				exit(1);
+			}
+			c.sin6_addr.s6_addr[10] = 0xFF;
+			c.sin6_addr.s6_addr[11] = 0xFF;
+			memcpy(&c.sin6_addr.s6_addr[12], &psz, 4);
+		}
+	}
+
+	if (strlen(argv[1]) + 1 > IFNAMSIZ)
+		fputs("tap name is too long\n", stderr), exit(1);
+
+	if (password[0] == 0){
+		/* TODO: test std streams redirected to file */
+		/* get term attrs */
+		res = tcgetattr(STDOUT_FILENO, &attr);
+		ERRDIE(res, "tcgetattr");
+
+		/* echo off */
+		attr.c_lflag &= ~ECHO;
+		res = tcsetattr(STDOUT_FILENO, TCSANOW, &attr);
+		ERRDIE(res, "tcsetattr");
+
+		/* get password */
+		fputs("Password: ", stdout), fflush(stdout);
+		pkt = fgets(password, PASSWORD_BUF_SZ, stdin);
+		fputs("\n", stdout);
+		if (pkt == NULL)
+			fputs("can't read password\n", stderr), exit(1);
+
+		/* echo on */
+		attr.c_lflag |= ECHO;
+		res = tcsetattr(STDOUT_FILENO, TCSANOW, &attr);
+		ERRDIE(res, "tcsetattr");
+	}
+
+	/* password without '\r\n' */
+	res = 0;
+	while (password[res] != '\0'
+	    && password[res] != '\r'
+	    && password[res] != '\n') res++;
+
+	/* hash password */
+	sponge256_hash32(key, password, res);
+	memset(password, 0, PASSWORD_BUF_SZ);
+
+	/* feed prng with key and current time */
+	duplex257_prng_feed(prng_state, ((u32*)key)[0]);
+	duplex257_prng_feed(prng_state, ((u32*)key)[1]);
+	duplex257_prng_feed(prng_state, ((u32*)key)[2]);
+	duplex257_prng_feed(prng_state, ((u32*)key)[3]);
+	duplex257_prng_feed(prng_state, ((u32*)key)[4]);
+	duplex257_prng_feed(prng_state, ((u32*)key)[5]);
+	duplex257_prng_feed(prng_state, ((u32*)key)[6]);
+	duplex257_prng_feed(prng_state, ((u32*)key)[7]);
+	clock_gettime(CLOCK_MONOTONIC, &t);
+	duplex257_prng_feed(prng_state, (u32)t.tv_sec);
+	duplex257_prng_feed(prng_state, (u32)t.tv_nsec);
+
+	/* daemonize: redirect std streams */
+	res = open("/dev/null", O_RDONLY); ERRDIE(res, "open(/dev/null)");
+	close(STDIN_FILENO), dup(res), close(res);
+	res = open(LOGFILE, O_CREAT|O_WRONLY|O_TRUNC, S_IRUSR | S_IWUSR);
+	ERRDIE(res, "open(logfile)");
+	close(STDOUT_FILENO);
+	close(STDERR_FILENO);
+	dup(res), dup(res), close(res);
+
+	/* daemonize: fork 1 */
+	res = fork(); ERRDIE(res, "fork 1");
+	if (res != 0) exit(0);
+
+	/* daemonize: become session leader */
+	setsid();
+
+	/* daemonize: fork 2 */
+	res = fork(); ERRDIE(res, "fork 2");
+	if (res != 0) exit(0); /* kill session leader */
+
+	/* daemonized! */
+
+	/* cur pkt buf */
+	pkt = malloc(MAX_PKT_SZ);
+	if (pkt == NULL) fputs("pkt = malloc() failed\n",stderr), exit(1);
+
+	/* socket, setsockopt */
+	skt = socket(PF_INET6, SOCK_DGRAM, 0); ERRDIE(skt, "socket");
+	res = 1;
+	res = setsockopt(skt, SOL_SOCKET, SO_REUSEADDR, &res, sizeof(res));
+	ERRDIE(res, "SO_REUSEADDR");
+
+	/* bind */
+	res = bind(skt, (struct sockaddr*) &c, sizeof(c));
+	ERRDIE(res, "bind");
+
+	/* tap */
+	tap = open("/dev/net/tun", O_RDWR);
+	ERRDIE(tap, "open(/dev/net/tun)");
+
+	memset(&ifr, 0, sizeof(ifr));
+	ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
+	strncpy(ifr.ifr_name, argv[1], IFNAMSIZ);
+	res = ioctl(tap, TUNSETIFF, &ifr);
+	ERRDIE(res, "TUNSETIFF");
+
+	fdmax = (tap > skt) ? tap+1 : skt+1;
+
+inf_loop:
+	/* "disconnect" peers with timeout */
+	clock_gettime(CLOCK_MONOTONIC, &t);
+	for (i=0; i < ccnt; i++){
+		if (t.tv_sec - peer[i].lptime < TIMEOUT_D) continue;
+		if (i != ccnt-1)
+			memcpy(&peer[i], &peer[ccnt-1], sizeof(peer[0]));
+
+		ccnt--, i--;
+		memset(&peer[ccnt], 0, sizeof(peer[0]));
+	}
+
+	/* select */
+	FD_ZERO(&rset);
+	FD_SET(skt, &rset);
+	FD_SET(tap, &rset);
+
+	select(fdmax, &rset, NULL, NULL, NULL);
+
+	/* read packet from tap */
+	if (FD_ISSET(tap, &rset)){
+		psz = read(tap, pkt, MAX_PKT_SZ);
+		ERRDIE(psz, "read(tap)");
+
+		for (i=0; i < ccnt; i++){
+			if (0 == memcmp(peer[i].mac, pkt, 6)){
+				send_pkt(skt, pkt, psz,
+			             (struct sockaddr_storage*) &peer[i].a);
+				break;
+			}
+		}
+	}
+
+	/* recv packet from client */
+	if (0 == FD_ISSET(skt, &rset)) goto inf_loop;
+
+	psz = recv_pkt(skt, pkt, MAX_PKT_SZ, (struct sockaddr_storage*) &c);
+
+	for (i=0; i < ccnt; i++)
+		if (c.sin6_port == peer[i].a.sin6_port &&
+		    0 == memcmp(c.sin6_addr.s6_addr,
+		        peer[i].a.sin6_addr.s6_addr, 16)) break;
+
+	if (i == ccnt){
+		/* client not connected */
+		if (psz <= 14) /* ethernet header len == 14 */
+			goto inf_loop;
+
+		if (ccnt == MAX_PEERS) goto inf_loop;
+
+		/* add peer to array */
+		memcpy(&peer[i].a, &c, sizeof(c));
+		memcpy(&peer[i].mac, &pkt[6], 6);
+		ccnt++;
+	}
+
+	/* client connected */
+	clock_gettime(CLOCK_MONOTONIC, &t);
+	duplex257_prng_feed(prng_state, t.tv_nsec);
+	peer[i].lptime = t.tv_sec;
+
+	/* if connected peer send small packet, it's a ping packet for NAT
+	 * keepalive, all i need to do is update lptime */
+	if (psz <= 14) goto inf_loop;
+
+	i = write(tap, pkt, psz);
+	if (i == -1) perror("write(tap)");
+	if (i < psz)
+		fprintf(stderr, "i < psz, i=%d, psz=%d\n", i, psz), exit(1);
+
+	goto inf_loop;
+
+	return 0;
+}
+
+