From b4709429ed88563982412a5a027b92143c37e268 Mon Sep 17 00:00:00 2001 From: Nakidai Date: Thu, 31 Jul 2025 17:12:27 +0300 Subject: Add files --- fatvpnd.c | 228 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 228 insertions(+) create mode 100644 fatvpnd.c (limited to 'fatvpnd.c') 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 [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.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; +} + + -- cgit 1.4.1