Created
November 2, 2020 08:17
-
-
Save apankrat/9ef1c83c8c891ca6af4e6bc9303a0ba5 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright (c) 2005 Applied Networking Inc. All rights reserved. | |
* | |
* This program is free software; you can redistribute it and/or modify | |
* it under the terms set out in the LICENSE.tuncfg file, which is | |
* included in Hamachi Client distribution. | |
* | |
* This program is distributed in the hope that it will be useful, | |
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
* LICENSE.tuncfg file for more details. | |
* | |
* (BSD license) | |
*/ | |
/* | |
* Tuncfg is a tunneling device management and configuration daemon. | |
* | |
* The idea is that tuncfg encapsulates all root-level functionality | |
* normally required by a private networking software. Namely - | |
* | |
* * creation of tunneling devices; this requires an access to | |
* /dev/net/tun file, which _usually_ has 700 access mask | |
* | |
* * configuration of the tunneling device using ifconfig, which is | |
* always a root-level operation | |
* | |
* Tuncfg must be run with superuser privileges. It will daemonize and | |
* open a listening domain socket /var/run/tuncfg.sock. | |
* | |
* Upon accepting the connection on this socket, it will issue an open() | |
* call for /dev/net/tun file (thus instantiating the tunneling device) | |
* and pass obtained FD to the peer process. It will also query and pass | |
* the MAC address of the device to the peer process. | |
* | |
* It will then listen for ifconfig requests from the peer. The request | |
* contains just an IP address and mask. Tuncfg will lookup the tun | |
* device the peer owns and will assign IP/mask to it using system() | |
* call for ipconfig command. | |
* | |
* 'tuncfg' starts the daemon (it won't start twice, there's a check) | |
* 'tuncfg -d' starts the console version (useful for debugging) | |
* | |
*/ | |
#include <sys/types.h> | |
#include <sys/socket.h> | |
#include <sys/un.h> | |
#include <sys/ioctl.h> | |
#include <sys/stat.h> | |
#include <arpa/inet.h> | |
#include <linux/if.h> | |
#include <linux/if_tun.h> | |
#include <unistd.h> | |
#include <errno.h> | |
#include <stdio.h> | |
#include <fcntl.h> | |
#include <stdarg.h> | |
#include <stdlib.h> | |
/* | |
* | |
*/ | |
#define LOCK_PATH "/var/run/tuncfg.lock" | |
#define SOCK_PATH "/var/run/tuncfg.sock" | |
#define TUNTAP_URL "http://www.hamachi.cc/tuntap" | |
#define MAX_CLIENTS 64 | |
struct context | |
{ | |
int fd; | |
char dev[IFNAMSIZ]; | |
}; | |
/* | |
* | |
*/ | |
#define errorf(args...) { printf(args); exit(1); } | |
int send_with_fd(int fd, const void * buf, size_t len, int fd_to_send); | |
int main(int argc, char ** argv) | |
{ | |
struct sockaddr_un addr = { 0 }; | |
socklen_t alen = sizeof(addr); | |
struct stat st; | |
pid_t pid; | |
int fd, r, i; | |
struct context ctx[MAX_CLIENTS]; | |
int ctx_n = 0; | |
// check we are root | |
if (getuid() != 0) | |
errorf("tuncfg: must be run with superuser permissions\n"); | |
// lcok | |
fd = open(LOCK_PATH, O_CREAT); | |
if (fd < 0) | |
errorf("tuncfg: cannot open lock file %s -- %s\n", | |
LOCK_PATH, strerror(errno)); | |
if (flock(fd, LOCK_EX | LOCK_NB) < 0) | |
errorf("tuncfg: already running\n"); | |
// check there's /dev/net/tun | |
if (stat("/dev/net/tun", &st) < 0) | |
errorf("tuncfg: cannot stat() /dev/net/tun -- %s\n" | |
"tuncfg: visit %s for more information\n", | |
strerror(errno), TUNTAP_URL); | |
// open request socket | |
addr.sun_family = AF_UNIX; | |
strcpy(addr.sun_path, SOCK_PATH); | |
fd = socket(AF_UNIX, SOCK_STREAM, 0); | |
if (fd < 0) | |
errorf("tuncfg: cannot create domain socket -- %s\n", | |
strerror(errno)); | |
unlink(SOCK_PATH); | |
r = bind(fd, (void*)&addr, alen); | |
if (r < 0) | |
errorf("tuncfg: cannot bind domain socket to %s -- %s\n", | |
SOCK_PATH, strerror(errno)); | |
r = listen(fd, 2); | |
if (r < 0) | |
errorf("tuncfg: cannot listen on domain socket %s -- %s\n", | |
SOCK_PATH, strerror(errno)); | |
// make the socket world-accessible | |
if (stat(SOCK_PATH, &st) < 0) | |
errorf("tuncfg: cannot stat() %s -- %s\n", | |
SOCK_PATH, strerror(errno)); | |
if (chmod(SOCK_PATH, st.st_mode | 0777) < 0) | |
printf("tuncfg: failed to chmod +777 %s -- %s\n", | |
SOCK_PATH, strerror(errno)); | |
// daemonize | |
if (argc < 2 || strcmp(argv[1], "-d")) | |
{ | |
chdir("/"); | |
pid = fork(); | |
if (pid < 0) return 0; | |
if (pid > 0) exit(0); | |
if (setsid() < 0) | |
return 0;/* shouldn't happen */ | |
pid = fork(); | |
if (pid < 0) return 0; | |
if (pid > 0) exit(0); | |
close(0); | |
close(1); | |
close(2); | |
open("/dev/null", O_RDWR); | |
dup(0); | |
dup(0); | |
} | |
// loop | |
for (;;) | |
{ | |
fd_set fdr; | |
int fdm; | |
FD_ZERO(&fdr); | |
FD_SET(fd, &fdr); | |
fdm = fd; | |
for (i=0; i<ctx_n; i++) | |
{ | |
FD_SET(ctx[i].fd, &fdr); | |
if (ctx[i].fd > fdm) | |
fdm = ctx[i].fd; | |
} | |
do | |
{ | |
r = select(fdm+1, &fdr, NULL, NULL, NULL); | |
if (r < 0 && errno != EINTR) | |
exit(EXIT_FAILURE); | |
} | |
while (r < 0); | |
if (FD_ISSET(fd, &fdr)) | |
{ | |
struct context * p; | |
struct ifreq ifr; | |
char buf[4+6]; | |
int cli, dev = -1, tmp = -1; | |
cli = accept(fd, (void*)&addr, &alen); | |
printf("tuncfg: accept() %d %d\n", cli, errno); | |
if (cli < 0) | |
goto skip; | |
if (ctx_n == MAX_CLIENTS) | |
{ | |
printf("tuncfg: too many clients\n"); | |
r = (0x01 << 24); | |
goto done; | |
} | |
// open tap device | |
dev = open("/dev/net/tun", O_RDWR); | |
printf("tuncfg: open() %d %d\n", dev, errno); | |
if (dev < 0) | |
{ | |
r = (0x02 << 24) | errno; | |
goto done; | |
} | |
// bring it up | |
strcpy(ifr.ifr_name, "ham%d"); | |
ifr.ifr_flags = IFF_TAP | IFF_NO_PI; | |
if (ioctl(dev, TUNSETIFF, (ulong)&ifr) < 0) | |
{ | |
printf("tuncfg: ioctl() -1 %d\n", errno); | |
r = (0x03 << 24) | errno; | |
goto done; | |
} | |
printf("tuncfg: ioctl() 0 %s\n", ifr.ifr_name); | |
strcpy(ifr.ifr_name, "ham0"); | |
// query mac | |
tmp = socket(AF_INET, SOCK_DGRAM, 0); | |
if (tmp < 0) | |
{ | |
printf("tuncfg: socket(mac) %d\n", errno); | |
r = (0x04 << 24) | errno; | |
goto done; | |
} | |
if (ioctl(tmp, SIOCGIFHWADDR, (ulong)&ifr) < 0) | |
{ | |
printf("tuncfg: ioctl(mac) %d\n", errno); | |
r = (0x05 << 24) | errno; | |
goto done; | |
} | |
memcpy(buf+4, &ifr.ifr_hwaddr.sa_data, 6); | |
// remember | |
p = &ctx[ctx_n++]; | |
p->fd = cli; | |
strncpy(p->dev, ifr.ifr_name, sizeof(p->dev)); | |
r = 0; | |
done: | |
if (cli != -1) | |
{ | |
memcpy(buf, &r, sizeof(r)); | |
send_with_fd(cli, buf, sizeof(buf), dev); | |
} | |
if (tmp != -1) close(tmp); | |
if (dev != -1) close(dev); | |
if (r != 0) close(cli); | |
} | |
skip: | |
for (i=0; i<ctx_n; i++) | |
{ | |
unsigned long v[2]; | |
char cmd[256]; | |
if (! FD_ISSET(ctx[i].fd, &fdr)) | |
continue; | |
r = recv(ctx[i].fd, v, sizeof(v), 0); | |
printf("tuncfg: recv() %d %d\n", r, errno); | |
if (r == 0) | |
{ | |
/* client's gone */ | |
printf("tuncfg: removing %s\n", ctx[i].dev); | |
close(ctx[i].fd); | |
ctx[i--] = ctx[ctx_n--]; | |
continue; | |
} | |
if (r != sizeof(v)) | |
{ | |
r = (0x06 << 24); | |
goto ack; | |
} | |
/* v[0] = ham<n>, v[1] = ip, v[2] = mask */ | |
if ( (v[0] & 0xff000000) != 0x05000000 || | |
(v[1] & 0xff000000) != 0xff000000 ) | |
{ | |
printf("tuncfg: bad arg %08x %08x\n", | |
v[0], v[1]); | |
r = (0x07 << 24); | |
goto ack; | |
} | |
r = sprintf(cmd, | |
"ifconfig %s %u.%u.%u.%u ", ctx[i].dev, | |
(v[0] >> 24) & 0xff, (v[0] >> 16) & 0xff, | |
(v[0] >> 8) & 0xff, (v[0] >> 0) & 0xff); | |
r += sprintf(cmd + r, | |
"netmask %u.%u.%u.%u ", | |
(v[1] >> 24) & 0xff, (v[1] >> 16) & 0xff, | |
(v[1] >> 8) & 0xff, (v[1] >> 0) & 0xff); | |
v[0] |= ~v[1]; | |
r += sprintf(cmd + r, | |
"mtu 1200 broadcast %u.%u.%u.%u", | |
(v[0] >> 24) & 0xff, (v[0] >> 16) & 0xff, | |
(v[0] >> 8) & 0xff, (v[0] >> 0) & 0xff); | |
r = system(cmd); | |
printf("tuncfg: system(%s) %d %d\n", cmd, r, errno); | |
ack: | |
printf("tuncfg: config() %08x", r); | |
send_with_fd(ctx[i].fd, &r, sizeof(r), -1); | |
} | |
} | |
return 0; | |
} | |
int send_with_fd(int fd, const void * ptr, size_t len, int fd_to_send) | |
{ | |
struct msghdr msg = { 0 }; | |
struct iovec iov[1]; | |
struct cmsghdr * hdr; | |
char buf[CMSG_SPACE(sizeof(int))]; | |
if (fd_to_send != -1) | |
{ | |
msg.msg_control = buf; | |
msg.msg_controllen = sizeof(buf); | |
hdr = CMSG_FIRSTHDR(&msg); | |
hdr->cmsg_len = CMSG_LEN(sizeof(int)); | |
hdr->cmsg_level = SOL_SOCKET; | |
hdr->cmsg_type = SCM_RIGHTS; | |
*(int*)CMSG_DATA(hdr) = fd_to_send; | |
} | |
iov[0].iov_base = (void*)ptr; | |
iov[0].iov_len = len; | |
msg.msg_iov = iov; | |
msg.msg_iovlen = 1; | |
return sendmsg(fd, &msg, 0); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment