diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..a41d7c8
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,2 @@
+first: main.c
+ gcc -o tuninetd main.c -lpthread -lpcap
diff --git a/README.md b/README.md
index 3df1379..d21867f 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,44 @@
-# tuninetd
\ No newline at end of file
+# 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)
+Download all files and:
+```sh
+# cd /folder/with/sourcefiles
+# make
+```
+
+#### Usage:
+
+tuninetd -i \ -c \ [-m \] [-f ] [-t \] [-d]
+
+**-i \**: interface to use (tun or tap). Must be up and configured.
+**-c \**: will be executed with 'start' and 'stop' parameter.
+**-m \**: 'tun' or 'tap' mode. By default 'tun', should be set properly.
+**-f \**: specify pcap filter, similar to tcpdump
+**-t \**: seconds of interface idle, before 'stop' command (default is 600).
+**-d**: demonize process
+**-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 \
+**Any suggestions will be appreciated.**
diff --git a/main.c b/main.c
new file mode 100755
index 0000000..399fd6a
--- /dev/null
+++ b/main.c
@@ -0,0 +1,162 @@
+//Author: root4root@gmail.com
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#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;
+}
diff --git a/pcap.c b/pcap.c
new file mode 100755
index 0000000..7e44bef
--- /dev/null
+++ b/pcap.c
@@ -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);
+}
diff --git a/runtunnel.sh b/runtunnel.sh
new file mode 100755
index 0000000..288fe81
--- /dev/null
+++ b/runtunnel.sh
@@ -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
diff --git a/tun.c b/tun.c
new file mode 100755
index 0000000..7d7d051
--- /dev/null
+++ b/tun.c
@@ -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;
+}
diff --git a/utils.c b/utils.c
new file mode 100755
index 0000000..cdf85a7
--- /dev/null
+++ b/utils.c
@@ -0,0 +1,61 @@
+//Author: root4root@gmail.com
+
+void do_debug(char *msg, ...)
+{
+ if(debug) {
+ va_list argp;
+ va_start(argp, msg);
+ vfprintf(stderr, msg, argp);
+ va_end(argp);
+ }
+}
+
+void my_err(char *msg, ...)
+{
+ va_list argp;
+ va_start(argp, msg);
+
+ if (globcfg.isdaemon == 0) {
+ vfprintf(stderr, msg, argp);
+ vfprintf(stderr, "\n", NULL);
+ } else {
+ openlog("tuninetd", 0, LOG_USER);
+ vsyslog(LOG_ERR, msg, argp);
+ closelog();
+ }
+
+ va_end(argp);
+}
+
+void my_info(char *msg, ...)
+{
+ va_list argp;
+ va_start(argp, msg);
+
+ if (globcfg.isdaemon == 0) {
+ vfprintf(stderr, msg, argp);
+ vfprintf(stderr, "\n", NULL);
+ } else {
+ openlog("tuninetd", 0, LOG_USER);
+ vsyslog(LOG_INFO, msg, argp);
+ closelog();
+ }
+
+ va_end(argp);
+}
+
+void usage(void) {
+ fprintf(stderr, "\nUsage:\n\n");
+ fprintf(stderr, "%s -i -c [-m ] [-f ] [-t ] [-d]\n", progname);
+ fprintf(stderr, "\n\n");
+ fprintf(stderr, "-i : interface to use (tun or tap). Must be up and configured.\n");
+ fprintf(stderr, "-c : will be executed with 'start' and 'stop' parameter.\n");
+ fprintf(stderr, "-m : 'tun' or 'tap' mode. By default 'tun', should be set properly. \n");
+ fprintf(stderr, "-f : specify pcap filter, similar to tcpdump\n");
+ fprintf(stderr, "-t : seconds of interface idle, before 'stop' command (default is 600).\n");
+ fprintf(stderr, "-d: demonize process\n");
+ fprintf(stderr, "-h: prints this help text\n\n");
+ fprintf(stderr, "\nExample:\n\n");
+ fprintf(stderr, "tuninetd -i tun0 -c /test/runtunnel.sh -f \"! host 1.2.3.4\" -t 3600 -d\n\n");
+ exit(1);
+}