#include "vknetd.h"
static ioinfo_t vknet_tap(const char *tapName, const char *bridgeName);
static int vknet_listener(const char *pathName);
static void vknet_acceptor(int net_fd);
static void *vknet_io(void *arg);
static int vknet_connect(const char *pathName);
static void vknet_monitor(int net_fd);
static void usage(void) __dead2;
static void writepid(void);
static void cleanup(int);
pthread_mutex_t BridgeMutex;
static int DebugOpt = 0;
static int SetAddrOpt = 0;
static const char *pidfile = "/var/run/vknetd.pid";
int SecureOpt = 1;
struct in_addr NetAddress;
struct in_addr NetMask;
int
main(int ac, char **av)
{
const char *pathName = "/var/run/vknet";
const char *tapName = "auto";
const char *bridgeName = NULL;
int net_fd;
int connectOpt = 0;
int c;
ioinfo_t tap_info;
pthread_t dummy_td;
while ((c = getopt(ac, av, "b:cdp:i:t:U")) != -1) {
switch (c) {
case 'U':
SecureOpt = 0;
break;
case 'b':
bridgeName = optarg;
break;
case 'd':
DebugOpt = 1;
break;
case 'p':
pathName = optarg;
break;
case 'i':
pidfile = optarg;
break;
case 't':
tapName = optarg;
break;
case 'c':
connectOpt = 1;
break;
default:
usage();
}
}
av += optind;
ac -= optind;
if (ac)
SetAddrOpt = 1;
signal(SIGPIPE, SIG_IGN);
if (connectOpt) {
net_fd = vknet_connect(pathName);
if (net_fd < 0) {
perror("connect");
exit(1);
}
vknet_monitor(net_fd);
exit(0);
}
if (SecureOpt || SetAddrOpt) {
char *str;
int masklen;
u_int32_t mask;
if (ac == 0 || strchr(av[0], '/') == NULL)
usage();
str = strdup(av[0]);
if (inet_pton(AF_INET, strtok(str, "/"), &NetAddress) <= 0)
usage();
masklen = strtoul(strtok(NULL, "/"), NULL, 10);
mask = (1 << (32 - masklen)) - 1;
NetMask.s_addr = htonl(~mask);
}
mac_init();
if ((tap_info = vknet_tap(tapName, bridgeName)) == NULL) {
perror("tap: ");
exit(1);
}
if ((net_fd = vknet_listener(pathName)) < 0) {
perror("listener: ");
exit(1);
}
if (DebugOpt == 0)
daemon(1, 0);
writepid();
signal(SIGINT, cleanup);
signal(SIGHUP, cleanup);
signal(SIGTERM, cleanup);
if (tap_info && tap_info->fd >= 0) {
int pid = getpid();
ioctl(tap_info->fd, FIOSETOWN, &pid);
}
pthread_mutex_init(&BridgeMutex, NULL);
pthread_create(&dummy_td, NULL, vknet_io, tap_info);
vknet_acceptor(net_fd);
exit(0);
}
#define TAPDEV_MINOR(x) ((int)((x) & 0xffff00ff))
static ioinfo_t
vknet_tap(const char *tapName, const char *bridgeName)
{
struct ifreq ifr;
struct ifaliasreq ifra;
struct stat st;
char *buf = NULL;
int tap_fd;
int tap_unit;
int s;
int flags;
ioinfo_t info;
if (strcmp(tapName, "auto") == 0) {
tap_fd = open("/dev/tap", O_RDWR);
} else if (strncmp(tapName, "tap", 3) == 0) {
asprintf(&buf, "/dev/%s", tapName);
tap_fd = open(buf, O_RDWR | O_NONBLOCK);
free(buf);
} else {
tap_fd = open(tapName, O_RDWR | O_NONBLOCK);
}
if (tap_fd < 0)
return(NULL);
if (fstat(tap_fd, &st) < 0) {
close(tap_fd);
return(NULL);
}
tap_unit = TAPDEV_MINOR(st.st_rdev);
printf("/dev/tap%d\n", tap_unit);
fflush(stdout);
fcntl(tap_fd, F_SETFL, 0);
bzero(&ifr, sizeof(ifr));
bzero(&ifra, sizeof(ifra));
snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "tap%d", tap_unit);
snprintf(ifra.ifra_name, sizeof(ifra.ifra_name), "tap%d", tap_unit);
s = socket(AF_INET, SOCK_DGRAM, 0);
if (SetAddrOpt) {
struct sockaddr_in *in;
in = (void *)&ifra.ifra_addr;
in->sin_family = AF_INET;
in->sin_len = sizeof(ifra.ifra_addr);
in->sin_addr = NetAddress;
in = (void *)&ifra.ifra_mask;
in->sin_family = AF_INET;
in->sin_len = sizeof(ifra.ifra_mask);
in->sin_addr = NetMask;
if (ioctl(s, SIOCAIFADDR, &ifra) < 0) {
perror("Unable to set address on tap interface");
exit(1);
}
}
flags = IFF_UP;
if (ioctl(s, SIOCGIFFLAGS, &ifr) >= 0) {
bzero(&ifr, sizeof(ifr));
snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "tap%d", tap_unit);
ifr.ifr_flags |= flags & 0xFFFF;
ifr.ifr_flagshigh |= flags >> 16;
if (ioctl(s, SIOCSIFFLAGS, &ifr) < 0) {
perror("Unable to set IFF_UP on tap interface");
exit(1);
}
}
if (bridgeName) {
struct ifbreq ifbr;
struct ifdrv ifd;
bzero(&ifr, sizeof(ifr));
snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", bridgeName);
if (ioctl(s, SIOCIFCREATE, &ifr) < 0) {
if (errno != EEXIST) {
perror("Unable to create bridge interface");
exit(1);
}
}
bzero(&ifbr, sizeof(ifbr));
snprintf(ifbr.ifbr_ifsname, sizeof(ifbr.ifbr_ifsname),
"tap%d", tap_unit);
bzero(&ifd, sizeof(ifd));
snprintf(ifd.ifd_name, sizeof(ifd.ifd_name), "%s", bridgeName);
ifd.ifd_cmd = BRDGADD;
ifd.ifd_len = sizeof(ifbr);
ifd.ifd_data = &ifbr;
if (ioctl(s, SIOCSDRVSPEC, &ifd) < 0) {
if (errno != EEXIST) {
perror("Unable to add tap ifc to bridge!");
exit(1);
}
}
}
close(s);
info = malloc(sizeof(*info));
bzero(info, sizeof(*info));
info->fd = tap_fd;
info->istap = 1;
return(info);
}
#undef TAPDEV_MINOR
static int
vknet_listener(const char *pathName)
{
struct sockaddr_un sunx;
int net_fd;
int len;
gid_t gid;
struct group *grp;
if ((grp = getgrnam("vknet")) == NULL) {
fprintf(stderr, "The 'vknet' group must exist\n");
exit(1);
}
gid = grp->gr_gid;
endgrent();
snprintf(sunx.sun_path, sizeof(sunx.sun_path), "%s", pathName);
len = offsetof(struct sockaddr_un, sun_path[strlen(sunx.sun_path)]);
++len;
sunx.sun_family = AF_UNIX;
sunx.sun_len = len;
net_fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
if (net_fd < 0)
return(-1);
remove(pathName);
if (bind(net_fd, (void *)&sunx, len) < 0) {
close(net_fd);
return(-1);
}
if (listen(net_fd, 1024) < 0) {
close(net_fd);
return(-1);
}
if (chown(pathName, (uid_t)-1, gid) < 0) {
close(net_fd);
return(-1);
}
if (chmod(pathName, 0660) < 0) {
close(net_fd);
return(-1);
}
return(net_fd);
}
static void
vknet_acceptor(int net_fd)
{
struct sockaddr_un sunx;
pthread_t dummy_td;
int sunx_len;
int rfd;
ioinfo_t info;
for (;;) {
sunx_len = sizeof(sunx);
rfd = accept(net_fd, (void *)&sunx, &sunx_len);
if (rfd < 0)
break;
info = malloc(sizeof(*info));
bzero(info, sizeof(*info));
info->fd = rfd;
info->istap = 0;
pthread_create(&dummy_td, NULL, vknet_io, info);
}
}
static void *
vknet_io(void *arg)
{
ioinfo_t info = arg;
bridge_t bridge;
u_int8_t *pkt;
int bytes;
pthread_detach(pthread_self());
pthread_mutex_lock(&BridgeMutex);
bridge = bridge_add(info);
pthread_mutex_unlock(&BridgeMutex);
pkt = malloc(MAXPKT);
while ((bytes = read(info->fd, pkt, MAXPKT)) > 0) {
pthread_mutex_lock(&BridgeMutex);
bridge_packet(bridge, pkt, bytes);
pthread_mutex_unlock(&BridgeMutex);
}
pthread_mutex_lock(&BridgeMutex);
bridge_del(bridge);
pthread_mutex_unlock(&BridgeMutex);
close(info->fd);
free(pkt);
pthread_exit(NULL);
}
static int
vknet_connect(const char *pathName)
{
struct sockaddr_un sunx;
int len;
int net_fd;
snprintf(sunx.sun_path, sizeof(sunx.sun_path), "%s", pathName);
len = offsetof(struct sockaddr_un, sun_path[strlen(sunx.sun_path)]);
++len;
sunx.sun_family = AF_UNIX;
sunx.sun_len = len;
net_fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
if (net_fd < 0)
return(-1);
if (connect(net_fd, (void *)&sunx, len) < 0) {
close(net_fd);
return(-1);
}
return(net_fd);
}
static void
vknet_monitor(int net_fd)
{
u_int8_t *pkt;
int bytes;
int i;
pkt = malloc(MAXPKT);
while ((bytes = read(net_fd, pkt, MAXPKT)) > 0) {
printf("%02x:%02x:%02x:%02x:%02x:%02x <- "
"%02x:%02x:%02x:%02x:%02x:%02x",
pkt[0], pkt[1], pkt[2], pkt[3], pkt[4], pkt[5],
pkt[6], pkt[7], pkt[8], pkt[9], pkt[10], pkt[11]);
for (i = 12; i < bytes; ++i) {
if (((i - 12) & 15) == 0) {
printf("\n\t");
}
printf(" %02x", pkt[i]);
}
printf("\n");
}
free(pkt);
}
static void
writepid(void)
{
FILE *pf;
if ((pf = fopen(pidfile, "w+")) == NULL)
errx(1, "Failed to create pidfile %s", pidfile);
if ((fprintf(pf, "%d\n", getpid())) < 1)
err(1, "fprintf");
fclose(pf);
}
static void
cleanup(int __unused sig)
{
if (pidfile)
unlink(pidfile);
}
static void
usage(void)
{
fprintf(stderr,
"usage: vknet [-cdU] [-b bridgeN] [-p socket_path]\n"
" [-i pidfile] [-t tapN] [address/cidrbits]\n"
"\n"
"address/cidrbits must be specified in default secure mode.\n");
exit(1);
}