Skip to content

Commit

Permalink
First commit
Browse files Browse the repository at this point in the history
  • Loading branch information
root4root committed Jan 26, 2018
1 parent 6b9ca99 commit 1777fb6
Show file tree
Hide file tree
Showing 7 changed files with 431 additions and 1 deletion.
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
first: main.c
gcc -o tuninetd main.c -lpthread -lpcap
45 changes: 44 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,44 @@
# tuninetd
# Tuninetd

**tuninetd** - is a simple daemon for tun/tap devices, similar to classic inetd by its logic, but for mentioned interfaceces, instead of a ports.

#### How it works:
First, you create and configure tun/tap device, then run **tuninetd**. It start listening on that interface, until network packet will be received.
Next, interface will be released and certain command is executed. From now on, daemon in monitoring state.
After N seconds of interface idle, tuninetd send "stop" command by path, that you define, and start listening interface by its own again.

Since, **tuninetd** based on **libpcap**, you can specify filter to trigging "start" event and monitoring iddle (i.e. cutoff unwanted traffic).
To test/debug filters rules - use tcpdump, because it built upon the same library.

**tuninetd** allows you deploy "VPN by demand" or any other "by demand" services, which is the main idea of the project.

#### Installation:
To build tuninetd, you need to have libpcap-dev library (Debian)<br/>
Download all files and:
```sh
# cd /folder/with/sourcefiles
# make
```

#### Usage:

tuninetd -i \<ifname> -c \<path> [-m \<iftype>] [-f <filter>] [-t \<ttl>] [-d]

**-i \<ifname>**: interface to use (tun or tap). Must be up and configured.<br/>
**-c \<path>**: will be executed with 'start' and 'stop' parameter.<br/>
**-m \<iftype>**: 'tun' or 'tap' mode. By default 'tun', should be set properly.<br/>
**-f \<filter>**: specify pcap filter, similar to tcpdump<br/>
**-t \<ttl>**: seconds of interface idle, before 'stop' command (default is 600).<br/>
**-d**: demonize process<br/>
**-h**: prints this help text

#### Example:
```sh
# tuninetd -i tun0 -c /test/runtunnel.sh -f "! host 1.2.3.4" -t 3600 -d
```

### License:
MIT
### Author:
Paul aka root4root \<root4root at gmail dot com><br/>
**Any suggestions will be appreciated.**
162 changes: 162 additions & 0 deletions main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
//Author: root4root@gmail.com

#include <fcntl.h>
#include <pthread.h>
#include <pcap.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <net/if.h>
#include <linux/if_tun.h>
#include <sys/ioctl.h>
#include <stdarg.h>
#include <syslog.h>
#include <unistd.h>

#define BUFSIZE 2000

int debug = 0;
int status = 0;
long ts = 0;
long curts = 0;

char progname[] = "tuninetd";

struct globcfg_t {
int isdaemon;
pid_t pid;
char *cmd_path;
char *cmd_path_start;
char *cmd_path_stop;
char *pcap_filter;
char *dev_name;
int dev_mode;
int ttl;
} globcfg;

#include "utils.c"
#include "tun.c"
#include "pcap.c"

int main(int argc, char *argv[])
{
int x, y, opt=0;

static const char *optString = "i:t:c:f:m:dh";

curts = time(NULL);

globcfg.isdaemon = 0;
globcfg.pid = 0;
globcfg.cmd_path = NULL;
globcfg.ttl = 600;
globcfg.dev_mode = IFF_TUN;

opt = getopt( argc, argv, optString);

while( opt != -1 ) {
switch( opt ) {
case 'i':
globcfg.dev_name = optarg;
break;
case 't':
globcfg.ttl = atoi(optarg);
break;
case 'c':
globcfg.cmd_path = optarg;

globcfg.cmd_path_start = malloc(strlen(optarg) + 23);
strcpy(globcfg.cmd_path_start, optarg);
strcat(globcfg.cmd_path_start, " start > /dev/null 2>&1");

globcfg.cmd_path_stop = malloc(strlen(optarg) + 22);
strcpy(globcfg.cmd_path_stop, optarg);
strcat(globcfg.cmd_path_stop, " stop > /dev/null 2>&1");
break;

case 'f':
globcfg.pcap_filter = optarg;
break;
case 'm':
if (strcmp("tap", optarg)== 0) {
globcfg.dev_mode = IFF_TAP;
}
break;
case 'd':
globcfg.isdaemon = 1;
break;
case 'h': /* намеренный проход в следующий case-блок */
case '?':
usage();
break;
default:
exit(1);
break;
}

opt = getopt( argc, argv, optString );
}

if (globcfg.dev_name == NULL) {
my_err("tun/tap device must be specified with proper type (-m by default tun).");
usage();
exit(1);
}

if (globcfg.cmd_path == NULL) {
my_err("Executable path must be specified");
usage();
exit(1);
}

if (globcfg.isdaemon == 1) {
globcfg.pid = fork();

if (globcfg.pid < 0) {
my_err("Can't fork process. Abort.");
exit(1);
}

if (globcfg.pid > 0) {
my_info("---");
my_info("Success! tuninetd has been started with pid: %i", globcfg.pid);
exit(0);
}

chdir("/");

setsid();

close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
}

pthread_t inc_x_thread;
pthread_t tun_x_thread;

pthread_attr_t attr;

pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

pthread_create(&inc_x_thread, &attr, inc_x, &x);
pthread_create(&tun_x_thread, &attr, tun_x, &y);

while (1) {
usleep(1000000);
curts = time(NULL);

if (ts != 0 && status == 1 && ((curts - ts) >= globcfg.ttl) ) {
status = 0;
my_info("Executing STOP command... Binding again to interface %s", globcfg.dev_name);

if (system(globcfg.cmd_path_stop) != 0) {
my_err("Warning! Executable command doesn't return 0 (%s)", globcfg.cmd_path_stop);
}

pthread_create(&tun_x_thread, &attr, tun_x, &y);
}
}
return 0;
}
35 changes: 35 additions & 0 deletions pcap.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//Author: root4root@gmail.com

void got_packet(u_char *args, const struct pcap_pkthdr *header, const u_char *packet)
{
ts = header->ts.tv_sec;
}


void *inc_x(void *x_void_ptr)
{
struct bpf_program filter;

char errbuf[PCAP_ERRBUF_SIZE];
pcap_t *handle;

handle = pcap_open_live(globcfg.dev_name, BUFSIZ, 0, 10, errbuf);

if (handle == NULL) {
my_err("Pcap: unable to open interface. %s", errbuf);
exit(1);
}

if (globcfg.pcap_filter != NULL) {
if (pcap_compile(handle, &filter, globcfg.pcap_filter, 0, PCAP_NETMASK_UNKNOWN) != 0) {
my_err("Wrong libpcap filter: \"%s\"", globcfg.pcap_filter);
exit(1);
}

pcap_setfilter(handle, &filter);
}

pcap_loop(handle, -1, got_packet, NULL);

pcap_close(handle);
}
46 changes: 46 additions & 0 deletions runtunnel.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#!/bin/bash

#This is just example of how executable script, which run from tuninetd, may looks like.
#Accepts 'start' or 'stop' parameter.
#Author: root4root@gmail.com

controlFile='/var/run/ssh-myvpn-tunnel-control'
remoteHost='1.2.3.4'

function up()
{
if [ ! -S $controlFile ]
then
/usr/bin/ssh -S $controlFile -M -f -w 0:0 $remoteHost ifconfig tun0 10.10.10.1/30 pointopoint 10.10.10.2
exit 0
else
echo 'Tunnel already up!'
exit 1
fi
}

function down()
{
if [ -S $controlFile ]
then
/usr/bin/ssh -S $controlFile -O exit $remoteHost
exit 0
else
echo 'Tunnel already down!'
exit 1
fi
}

case $1 in
'start')
up
;;
'stop' )
down
;;
*)
echo 'Usage: start|stop'
;;
esac

exit 1
81 changes: 81 additions & 0 deletions tun.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
//Author: root4root@gmail.com

int tun_alloc(char *dev, int flags)
{
struct ifreq ifr;
int fd, err;
char *clonedev = "/dev/net/tun";

if ( (fd = open(clonedev, O_RDWR)) < 0 ) {
my_err("Unable to open clonable device %s", clonedev);
return fd;
}

memset(&ifr, 0, sizeof(ifr));

ifr.ifr_flags = flags;

if (*dev) {
strncpy(ifr.ifr_name, dev, IFNAMSIZ);
}

if ( (err = ioctl(fd, TUNSETIFF, (void *)&ifr)) < 0 ) {
close(fd);
return err;
}

return fd;
}

int cread(int fd, char *buf, int n)
{
int nread;

if ((nread = read(fd, buf, n)) < 0) {
my_err("Error, while reading data. Abort.");
exit(1);
}

return nread;
}

void *tun_x(void *x_void_ptr)
{
int tap_fd;
int nread;
char buffer[BUFSIZE];


if ( (tap_fd = tun_alloc(globcfg.dev_name, globcfg.dev_mode | IFF_NO_PI)) < 0 ) {
my_err("Error connecting to tun/tap interface %s. Abort.", globcfg.dev_name);
exit(1);
}

while(1) {
nread = cread(tap_fd, buffer, BUFSIZE);

if (globcfg.pcap_filter != NULL) {
usleep(15000); //Wait for libpcap time out.
if ( (curts - ts) < 2 ) {
do_debug("Read %d bytes from the tap interface\n", nread);
break;
}
} else {
break;
}
}

ts = time(NULL);

status = 1;

close(tap_fd);

my_info("Executing START command...");

if (system(globcfg.cmd_path_start) != 0) {
my_err("Warning! Executable command doesn't return 0 (%s)", globcfg.cmd_path_start);
}

return 0;
}
Loading

0 comments on commit 1777fb6

Please sign in to comment.