Skip to content

Commit 1777fb6

Browse files
committed
First commit
1 parent 6b9ca99 commit 1777fb6

File tree

7 files changed

+431
-1
lines changed

7 files changed

+431
-1
lines changed

Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
first: main.c
2+
gcc -o tuninetd main.c -lpthread -lpcap

README.md

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,44 @@
1-
# tuninetd
1+
# Tuninetd
2+
3+
**tuninetd** - is a simple daemon for tun/tap devices, similar to classic inetd by its logic, but for mentioned interfaceces, instead of a ports.
4+
5+
#### How it works:
6+
First, you create and configure tun/tap device, then run **tuninetd**. It start listening on that interface, until network packet will be received.
7+
Next, interface will be released and certain command is executed. From now on, daemon in monitoring state.
8+
After N seconds of interface idle, tuninetd send "stop" command by path, that you define, and start listening interface by its own again.
9+
10+
Since, **tuninetd** based on **libpcap**, you can specify filter to trigging "start" event and monitoring iddle (i.e. cutoff unwanted traffic).
11+
To test/debug filters rules - use tcpdump, because it built upon the same library.
12+
13+
**tuninetd** allows you deploy "VPN by demand" or any other "by demand" services, which is the main idea of the project.
14+
15+
#### Installation:
16+
To build tuninetd, you need to have libpcap-dev library (Debian)<br/>
17+
Download all files and:
18+
```sh
19+
# cd /folder/with/sourcefiles
20+
# make
21+
```
22+
23+
#### Usage:
24+
25+
tuninetd -i \<ifname> -c \<path> [-m \<iftype>] [-f <filter>] [-t \<ttl>] [-d]
26+
27+
**-i \<ifname>**: interface to use (tun or tap). Must be up and configured.<br/>
28+
**-c \<path>**: will be executed with 'start' and 'stop' parameter.<br/>
29+
**-m \<iftype>**: 'tun' or 'tap' mode. By default 'tun', should be set properly.<br/>
30+
**-f \<filter>**: specify pcap filter, similar to tcpdump<br/>
31+
**-t \<ttl>**: seconds of interface idle, before 'stop' command (default is 600).<br/>
32+
**-d**: demonize process<br/>
33+
**-h**: prints this help text
34+
35+
#### Example:
36+
```sh
37+
# tuninetd -i tun0 -c /test/runtunnel.sh -f "! host 1.2.3.4" -t 3600 -d
38+
```
39+
40+
### License:
41+
MIT
42+
### Author:
43+
Paul aka root4root \<root4root at gmail dot com><br/>
44+
**Any suggestions will be appreciated.**

main.c

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
//Author: root4root@gmail.com
2+
3+
#include <fcntl.h>
4+
#include <pthread.h>
5+
#include <pcap.h>
6+
#include <stdio.h>
7+
#include <stdlib.h>
8+
#include <string.h>
9+
#include <net/if.h>
10+
#include <linux/if_tun.h>
11+
#include <sys/ioctl.h>
12+
#include <stdarg.h>
13+
#include <syslog.h>
14+
#include <unistd.h>
15+
16+
#define BUFSIZE 2000
17+
18+
int debug = 0;
19+
int status = 0;
20+
long ts = 0;
21+
long curts = 0;
22+
23+
char progname[] = "tuninetd";
24+
25+
struct globcfg_t {
26+
int isdaemon;
27+
pid_t pid;
28+
char *cmd_path;
29+
char *cmd_path_start;
30+
char *cmd_path_stop;
31+
char *pcap_filter;
32+
char *dev_name;
33+
int dev_mode;
34+
int ttl;
35+
} globcfg;
36+
37+
#include "utils.c"
38+
#include "tun.c"
39+
#include "pcap.c"
40+
41+
int main(int argc, char *argv[])
42+
{
43+
int x, y, opt=0;
44+
45+
static const char *optString = "i:t:c:f:m:dh";
46+
47+
curts = time(NULL);
48+
49+
globcfg.isdaemon = 0;
50+
globcfg.pid = 0;
51+
globcfg.cmd_path = NULL;
52+
globcfg.ttl = 600;
53+
globcfg.dev_mode = IFF_TUN;
54+
55+
opt = getopt( argc, argv, optString);
56+
57+
while( opt != -1 ) {
58+
switch( opt ) {
59+
case 'i':
60+
globcfg.dev_name = optarg;
61+
break;
62+
case 't':
63+
globcfg.ttl = atoi(optarg);
64+
break;
65+
case 'c':
66+
globcfg.cmd_path = optarg;
67+
68+
globcfg.cmd_path_start = malloc(strlen(optarg) + 23);
69+
strcpy(globcfg.cmd_path_start, optarg);
70+
strcat(globcfg.cmd_path_start, " start > /dev/null 2>&1");
71+
72+
globcfg.cmd_path_stop = malloc(strlen(optarg) + 22);
73+
strcpy(globcfg.cmd_path_stop, optarg);
74+
strcat(globcfg.cmd_path_stop, " stop > /dev/null 2>&1");
75+
break;
76+
77+
case 'f':
78+
globcfg.pcap_filter = optarg;
79+
break;
80+
case 'm':
81+
if (strcmp("tap", optarg)== 0) {
82+
globcfg.dev_mode = IFF_TAP;
83+
}
84+
break;
85+
case 'd':
86+
globcfg.isdaemon = 1;
87+
break;
88+
case 'h': /* намеренный проход в следующий case-блок */
89+
case '?':
90+
usage();
91+
break;
92+
default:
93+
exit(1);
94+
break;
95+
}
96+
97+
opt = getopt( argc, argv, optString );
98+
}
99+
100+
if (globcfg.dev_name == NULL) {
101+
my_err("tun/tap device must be specified with proper type (-m by default tun).");
102+
usage();
103+
exit(1);
104+
}
105+
106+
if (globcfg.cmd_path == NULL) {
107+
my_err("Executable path must be specified");
108+
usage();
109+
exit(1);
110+
}
111+
112+
if (globcfg.isdaemon == 1) {
113+
globcfg.pid = fork();
114+
115+
if (globcfg.pid < 0) {
116+
my_err("Can't fork process. Abort.");
117+
exit(1);
118+
}
119+
120+
if (globcfg.pid > 0) {
121+
my_info("---");
122+
my_info("Success! tuninetd has been started with pid: %i", globcfg.pid);
123+
exit(0);
124+
}
125+
126+
chdir("/");
127+
128+
setsid();
129+
130+
close(STDIN_FILENO);
131+
close(STDOUT_FILENO);
132+
close(STDERR_FILENO);
133+
}
134+
135+
pthread_t inc_x_thread;
136+
pthread_t tun_x_thread;
137+
138+
pthread_attr_t attr;
139+
140+
pthread_attr_init(&attr);
141+
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
142+
143+
pthread_create(&inc_x_thread, &attr, inc_x, &x);
144+
pthread_create(&tun_x_thread, &attr, tun_x, &y);
145+
146+
while (1) {
147+
usleep(1000000);
148+
curts = time(NULL);
149+
150+
if (ts != 0 && status == 1 && ((curts - ts) >= globcfg.ttl) ) {
151+
status = 0;
152+
my_info("Executing STOP command... Binding again to interface %s", globcfg.dev_name);
153+
154+
if (system(globcfg.cmd_path_stop) != 0) {
155+
my_err("Warning! Executable command doesn't return 0 (%s)", globcfg.cmd_path_stop);
156+
}
157+
158+
pthread_create(&tun_x_thread, &attr, tun_x, &y);
159+
}
160+
}
161+
return 0;
162+
}

pcap.c

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
//Author: root4root@gmail.com
2+
3+
void got_packet(u_char *args, const struct pcap_pkthdr *header, const u_char *packet)
4+
{
5+
ts = header->ts.tv_sec;
6+
}
7+
8+
9+
void *inc_x(void *x_void_ptr)
10+
{
11+
struct bpf_program filter;
12+
13+
char errbuf[PCAP_ERRBUF_SIZE];
14+
pcap_t *handle;
15+
16+
handle = pcap_open_live(globcfg.dev_name, BUFSIZ, 0, 10, errbuf);
17+
18+
if (handle == NULL) {
19+
my_err("Pcap: unable to open interface. %s", errbuf);
20+
exit(1);
21+
}
22+
23+
if (globcfg.pcap_filter != NULL) {
24+
if (pcap_compile(handle, &filter, globcfg.pcap_filter, 0, PCAP_NETMASK_UNKNOWN) != 0) {
25+
my_err("Wrong libpcap filter: \"%s\"", globcfg.pcap_filter);
26+
exit(1);
27+
}
28+
29+
pcap_setfilter(handle, &filter);
30+
}
31+
32+
pcap_loop(handle, -1, got_packet, NULL);
33+
34+
pcap_close(handle);
35+
}

runtunnel.sh

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#!/bin/bash
2+
3+
#This is just example of how executable script, which run from tuninetd, may looks like.
4+
#Accepts 'start' or 'stop' parameter.
5+
#Author: root4root@gmail.com
6+
7+
controlFile='/var/run/ssh-myvpn-tunnel-control'
8+
remoteHost='1.2.3.4'
9+
10+
function up()
11+
{
12+
if [ ! -S $controlFile ]
13+
then
14+
/usr/bin/ssh -S $controlFile -M -f -w 0:0 $remoteHost ifconfig tun0 10.10.10.1/30 pointopoint 10.10.10.2
15+
exit 0
16+
else
17+
echo 'Tunnel already up!'
18+
exit 1
19+
fi
20+
}
21+
22+
function down()
23+
{
24+
if [ -S $controlFile ]
25+
then
26+
/usr/bin/ssh -S $controlFile -O exit $remoteHost
27+
exit 0
28+
else
29+
echo 'Tunnel already down!'
30+
exit 1
31+
fi
32+
}
33+
34+
case $1 in
35+
'start')
36+
up
37+
;;
38+
'stop' )
39+
down
40+
;;
41+
*)
42+
echo 'Usage: start|stop'
43+
;;
44+
esac
45+
46+
exit 1

tun.c

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
//Author: root4root@gmail.com
2+
3+
int tun_alloc(char *dev, int flags)
4+
{
5+
struct ifreq ifr;
6+
int fd, err;
7+
char *clonedev = "/dev/net/tun";
8+
9+
if ( (fd = open(clonedev, O_RDWR)) < 0 ) {
10+
my_err("Unable to open clonable device %s", clonedev);
11+
return fd;
12+
}
13+
14+
memset(&ifr, 0, sizeof(ifr));
15+
16+
ifr.ifr_flags = flags;
17+
18+
if (*dev) {
19+
strncpy(ifr.ifr_name, dev, IFNAMSIZ);
20+
}
21+
22+
if ( (err = ioctl(fd, TUNSETIFF, (void *)&ifr)) < 0 ) {
23+
close(fd);
24+
return err;
25+
}
26+
27+
return fd;
28+
}
29+
30+
int cread(int fd, char *buf, int n)
31+
{
32+
int nread;
33+
34+
if ((nread = read(fd, buf, n)) < 0) {
35+
my_err("Error, while reading data. Abort.");
36+
exit(1);
37+
}
38+
39+
return nread;
40+
}
41+
42+
void *tun_x(void *x_void_ptr)
43+
{
44+
int tap_fd;
45+
int nread;
46+
char buffer[BUFSIZE];
47+
48+
49+
if ( (tap_fd = tun_alloc(globcfg.dev_name, globcfg.dev_mode | IFF_NO_PI)) < 0 ) {
50+
my_err("Error connecting to tun/tap interface %s. Abort.", globcfg.dev_name);
51+
exit(1);
52+
}
53+
54+
while(1) {
55+
nread = cread(tap_fd, buffer, BUFSIZE);
56+
57+
if (globcfg.pcap_filter != NULL) {
58+
usleep(15000); //Wait for libpcap time out.
59+
if ( (curts - ts) < 2 ) {
60+
do_debug("Read %d bytes from the tap interface\n", nread);
61+
break;
62+
}
63+
} else {
64+
break;
65+
}
66+
}
67+
68+
ts = time(NULL);
69+
70+
status = 1;
71+
72+
close(tap_fd);
73+
74+
my_info("Executing START command...");
75+
76+
if (system(globcfg.cmd_path_start) != 0) {
77+
my_err("Warning! Executable command doesn't return 0 (%s)", globcfg.cmd_path_start);
78+
}
79+
80+
return 0;
81+
}

0 commit comments

Comments
 (0)