/* 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_storage 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_storage 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 [address] \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.ss_family = AF_INET; (*(struct sockaddr_in*)&c).sin_port = htons((u16)atoi(argv[argc-1])); if (argc == 4){ res = inet_pton(AF_INET6, argv[2], ((struct sockaddr_in6*)&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); } memcpy(&((struct sockaddr_in*)&c)->sin_addr.s_addr, &psz, 4); } else { c.ss_family = AF_INET6; } } 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(c.ss_family, 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, &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, &c); switch (c.ss_family) { case AF_INET: for (i=0; i < ccnt; i++) if (((struct sockaddr_in*)&c)->sin_port == ((struct sockaddr_in*)&peer[i].a)->sin_port && ((struct sockaddr_in*)&c)->sin_addr.s_addr == ((struct sockaddr_in*)&peer[i].a)->sin_addr.s_addr) break; break; case AF_INET6: for (i=0; i < ccnt; i++) if (((struct sockaddr_in6*)&c)->sin6_port == ((struct sockaddr_in6*)&peer[i].a)->sin6_port && 0 == memcmp(((struct sockaddr_in6*)&c)->sin6_addr.s6_addr, ((struct sockaddr_in6*)&peer[i].a)->sin6_addr.s6_addr, 16)) break; 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; }