Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

tuntap driver imported from http://jungerl.sourceforge.net/.

  • Loading branch information...
commit 54694abc333483cbe15dc414e6b36b110f9ec71b 0 parents
Michael Santos authored
2  Makefile
... ... @@ -0,0 +1,2 @@
  1 +include ../../support/subdir.mk
  2 +
3  README
... ... @@ -0,0 +1,3 @@
  1 +
  2 +Copy of the tuntap Erlang driver from Jungerl, by Luke Gorrie.
  3 +
23 c_src/Makefile
... ... @@ -0,0 +1,23 @@
  1 +include ../../../support/include.mk
  2 +
  3 +CFLAGS += -I $(ERL_C_INCLUDE_DIR) -I../../../support
  4 +
  5 +TUN_DRV_SO = ../priv/tun_drv.so
  6 +TUNCTL = ../priv/tunctl
  7 +
  8 +all: $(TUN_DRV_SO) $(TUNCTL)
  9 +
  10 +$(TUN_DRV_SO): tun_drv.o
  11 + ld -G -o $@ $<
  12 +
  13 +tun_drv.o: tun_drv.c
  14 + $(CC) $(CFLAGS) -o $@ -c -fpic $(ERL_INCLUDE) $<
  15 +
  16 +$(TUNCTL): tunctl.c
  17 + $(CC) $(CFLAGS) -o $@ $<
  18 +
  19 +clean:
  20 + -rm $(TUN_DRV_SO) $(TUNCTL)
  21 +
  22 +.INTERMEDIATE: tun_drv.o
  23 +
247 c_src/tun_drv.c
... ... @@ -0,0 +1,247 @@
  1 +/* Tunnel device linked-in driver.
  2 + See /usr/src/linux/Documentation/networking/tuntap.txt
  3 +
  4 + Written by Luke Gorrie <luke@bluetail.com> in November 2001, and
  5 + updated in February 2003 to support TAP interfaces and the new
  6 + Erlang driver interface. */
  7 +
  8 +#include <stdio.h>
  9 +#include <fcntl.h>
  10 +#include <unistd.h>
  11 +#include <errno.h>
  12 +#include <sys/time.h>
  13 +#include <sys/types.h>
  14 +#include <net/if.h>
  15 +#include <sys/ioctl.h>
  16 +#include <assert.h>
  17 +
  18 +#include <linux/if_tun.h>
  19 +
  20 +#include "erl_driver.h"
  21 +
  22 +#define REPLY_ERROR 0
  23 +#define REPLY_OK 1
  24 +
  25 +#define REQUEST_GET_DEVICE 0
  26 +#define REQUEST_ACTIVE 2
  27 +
  28 +#define ACTIVE_FALSE 0
  29 +#define ACTIVE_TRUE 1
  30 +#define ACTIVE_ONCE 2
  31 +
  32 +/* Generous buffer size for reading single ethernet frames.
  33 +
  34 + FIXME: Do we need to worry about bigger packets, e.g. if we have a
  35 + giant MTU in 'tap' or receive pre-defragmented IP packets in
  36 + 'tun'? */
  37 +#define TUNNEL_BUF_SIZE 2048
  38 +
  39 +/*
  40 + *
  41 + * Helpers
  42 + *
  43 + */
  44 +
  45 +/* parse driver argument string: "tunnel_drv <tun|tap> [device_name]" */
  46 +static int parse_args(char *str, int *mode, char *dev_name, int max_name_len)
  47 +{
  48 + /* skip to start of first argument */
  49 + for (; *str != ' '; str++) { if (*str == '\0') return 0; }
  50 + for (; *str == ' '; str++) { if (*str == '\0') return 0; }
  51 +
  52 + /* looking at "tun" or "tap" - parse and move over */
  53 + if (strncmp(str, "tun", 3) == 0) {
  54 + *mode = IFF_TUN;
  55 + } else if (strncmp(str, "tap", 3) == 0) {
  56 + *mode = IFF_TAP;
  57 + } else {
  58 + return 0;
  59 + }
  60 + str += 3;
  61 + /* optional device name - skip any whitespace, then copy */
  62 + while (*str == ' ') str++;
  63 + strncpy(dev_name, str, max_name_len);
  64 +
  65 + return 1;
  66 +}
  67 +
  68 +/* make a tunnel interface */
  69 +static int make_if(int mode, char *dev) {
  70 + struct ifreq ifr;
  71 + int fd, err;
  72 +
  73 + if ((fd = open("/dev/net/tun", O_RDWR | O_NONBLOCK)) < 0) return -1;
  74 +
  75 + memset(&ifr, 0, sizeof(ifr));
  76 + ifr.ifr_flags = mode | IFF_NO_PI;
  77 + if( *dev )
  78 + strncpy(ifr.ifr_name, dev, IFNAMSIZ);
  79 +
  80 + if( (err = ioctl(fd, TUNSETIFF, (void *) &ifr)) < 0 ){
  81 + close(fd);
  82 + return err;
  83 + }
  84 + strcpy(dev, ifr.ifr_name);
  85 + return fd;
  86 +}
  87 +
  88 +
  89 +/*
  90 + *
  91 + * erl_driver interface
  92 + *
  93 + */
  94 +
  95 +struct tun_state {
  96 + ErlDrvPort port;
  97 + int fd;
  98 + char dev[IFNAMSIZ];
  99 + char buf[TUNNEL_BUF_SIZE];
  100 + int active;
  101 +};
  102 +
  103 +static int ctl_reply(int rep, char *buf, int len, char **rbuf, int rsize)
  104 +{
  105 + char* ptr;
  106 +
  107 + if ((len+1) > rsize) {
  108 + ptr = (char *)sys_alloc(len+1);
  109 + assert(ptr);
  110 + *rbuf = ptr;
  111 + }
  112 + else
  113 + ptr = *rbuf;
  114 + *ptr++ = rep;
  115 + memcpy(ptr, buf, len);
  116 + return len+1;
  117 +}
  118 +
  119 +static void set_input(struct tun_state *state, int flag)
  120 +{
  121 + driver_select(state->port, (ErlDrvEvent)state->fd, DO_READ, flag);
  122 +}
  123 +
  124 +static int tun_ctl(ErlDrvData data,
  125 + unsigned int cmd,
  126 + char *buf,
  127 + int len,
  128 + char **rbuf,
  129 + int rsize)
  130 +{
  131 + struct tun_state *state = (struct tun_state *)data;
  132 + int read_len;
  133 + switch (cmd) {
  134 + case REQUEST_GET_DEVICE:
  135 + return ctl_reply(REPLY_OK, state->dev, strlen(state->dev), rbuf, rsize);
  136 + case REQUEST_ACTIVE:
  137 + state->active = (int)*buf;
  138 + switch (state->active) {
  139 + case ACTIVE_FALSE:
  140 + set_input(state, 0);
  141 + break;
  142 + case ACTIVE_TRUE:
  143 + set_input(state, 1);
  144 + break;
  145 + case ACTIVE_ONCE:
  146 + /* optimization: try to read a packet immediately if it
  147 + exists, to avoid going back to select() */
  148 + read_len = read(state->fd, state->buf, TUNNEL_BUF_SIZE);
  149 + if (read_len > 0) {
  150 + /* got a packet */
  151 + driver_output(state->port, state->buf, read_len);
  152 + state->active = ACTIVE_FALSE;
  153 + set_input(state, 0);
  154 + } else {
  155 + /* no packet available yet */
  156 + set_input(state, 1);
  157 + }
  158 + break;
  159 + default:
  160 + assert(0);
  161 + }
  162 + return ctl_reply(REPLY_OK, "", 0, rbuf, rsize);
  163 + default:
  164 + return ctl_reply(REPLY_ERROR, "", 0, rbuf, rsize);
  165 + }
  166 +}
  167 +
  168 +static void tun_output(ErlDrvData data, char* buf, int len)
  169 +{
  170 + struct tun_state *state = (struct tun_state *)data;
  171 + if (write(state->fd, buf, len) < 0)
  172 + driver_failure_posix(state->port, errno);
  173 +}
  174 +
  175 +static void tun_input(ErlDrvData data, ErlDrvEvent nil)
  176 +{
  177 + struct tun_state *state = (struct tun_state *)data;
  178 + int len;
  179 +
  180 + len = read(state->fd, state->buf, TUNNEL_BUF_SIZE);
  181 + if (len > 0) {
  182 + driver_output(state->port, state->buf, len);
  183 + }
  184 + if (state->active == ACTIVE_ONCE) {
  185 + state->active = ACTIVE_FALSE;
  186 + set_input(state, 0);
  187 + }
  188 +}
  189 +
  190 +static void tun_stop(ErlDrvData data)
  191 +{
  192 + struct tun_state *state = (struct tun_state *)data;
  193 + set_input(state, 0);
  194 + close(state->fd);
  195 + sys_free(state);
  196 +}
  197 +
  198 +static ErlDrvData tun_start(ErlDrvPort port, char *args)
  199 +{
  200 + struct tun_state *state;
  201 + int fd;
  202 +
  203 + int mode;
  204 + char dev_name[IFNAMSIZ];
  205 +
  206 + state = (struct tun_state*) sys_alloc(sizeof(struct tun_state));
  207 + if (state == NULL) {
  208 + errno = ENOMEM; /* appropriate to set errno? */
  209 + return ERL_DRV_ERROR_ERRNO;
  210 + }
  211 +
  212 + if (!parse_args(args, &mode, state->dev, IFNAMSIZ - 1)) {
  213 + return ERL_DRV_ERROR_BADARG;
  214 + }
  215 +
  216 + fd = make_if(mode, state->dev);
  217 + if (fd < 0) {
  218 + return ERL_DRV_ERROR_GENERAL;
  219 + }
  220 + state->port = port;
  221 + state->fd = fd;
  222 + state->active = ACTIVE_FALSE;
  223 + set_input(state, 0);
  224 +
  225 + return (ErlDrvData)state;
  226 +}
  227 +
  228 +static int tun_init(void)
  229 +{
  230 + return 0;
  231 +}
  232 +
  233 +ErlDrvEntry tun_driver_entry;
  234 +
  235 +ErlDrvEntry *driver_init(void)
  236 +{
  237 + memset(&tun_driver_entry, 0, sizeof(tun_driver_entry));
  238 + tun_driver_entry.init = tun_init;
  239 + tun_driver_entry.start = tun_start;
  240 + tun_driver_entry.stop = tun_stop;
  241 + tun_driver_entry.ready_input = tun_input;
  242 + tun_driver_entry.control = tun_ctl;
  243 + tun_driver_entry.output = tun_output;
  244 + tun_driver_entry.driver_name = "tun_drv";
  245 + return &tun_driver_entry;
  246 +}
  247 +
112 c_src/tunctl.c
... ... @@ -0,0 +1,112 @@
  1 +/* Copyright 2002 Jeff Dike
  2 + * Licensed under the GPL
  3 + */
  4 +
  5 +#include <stdio.h>
  6 +#include <stdlib.h>
  7 +#include <string.h>
  8 +#include <errno.h>
  9 +#include <fcntl.h>
  10 +#include <unistd.h>
  11 +#include <pwd.h>
  12 +#include <net/if.h>
  13 +#include <sys/ioctl.h>
  14 +#include <linux/if_tun.h>
  15 +
  16 +static void Usage(char *name)
  17 +{
  18 + fprintf(stderr, "Create: %s [-b] [-u owner] [-t device-name] "
  19 + "[-f tun-clone-device]\n", name);
  20 + fprintf(stderr, "Delete: %s -d device-name [-f tun-clone-device]\n\n",
  21 + name);
  22 + fprintf(stderr, "The default tun clone device is /dev/net/tun - some systems"
  23 + " use\n/dev/misc/net/tun instead\n\n");
  24 + fprintf(stderr, "-b will result in brief output (just the device name)\n");
  25 + exit(1);
  26 +}
  27 +
  28 +int main(int argc, char **argv)
  29 +{
  30 + struct ifreq ifr;
  31 + struct passwd *pw;
  32 + long owner = geteuid();
  33 + int tap_fd, opt, delete = 0, brief = 0;
  34 + char *tun = "", *file = "/dev/net/tun", *name = argv[0], *end;
  35 +
  36 + while((opt = getopt(argc, argv, "bd:f:t:u:")) > 0){
  37 + switch(opt) {
  38 + case 'b':
  39 + brief = 1;
  40 + break;
  41 + case 'd':
  42 + delete = 1;
  43 + tun = optarg;
  44 + break;
  45 + case 'f':
  46 + file = optarg;
  47 + break;
  48 + case 'u':
  49 + pw = getpwnam(optarg);
  50 + if(pw != NULL){
  51 + owner = pw->pw_uid;
  52 + break;
  53 + }
  54 + owner = strtol(optarg, &end, 0);
  55 + if(*end != '\0'){
  56 + fprintf(stderr, "'%s' is neither a username nor a numeric uid.\n",
  57 + optarg);
  58 + Usage(name);
  59 + }
  60 + break;
  61 + case 't':
  62 + tun = optarg;
  63 + break;
  64 + case 'h':
  65 + default:
  66 + Usage(name);
  67 + }
  68 + }
  69 +
  70 + argv += optind;
  71 + argc -= optind;
  72 +
  73 + if(argc > 0)
  74 + Usage(name);
  75 +
  76 + if((tap_fd = open(file, O_RDWR)) < 0){
  77 + perror("opening tun device");
  78 + exit(1);
  79 + }
  80 +
  81 + memset(&ifr, 0, sizeof(ifr));
  82 +
  83 + ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
  84 + strncpy(ifr.ifr_name, tun, sizeof(ifr.ifr_name) - 1);
  85 + if(ioctl(tap_fd, TUNSETIFF, (void *) &ifr) < 0){
  86 + perror("TUNSETIFF");
  87 + exit(1);
  88 + }
  89 +
  90 + if(delete){
  91 + if(ioctl(tap_fd, TUNSETPERSIST, 0) < 0){
  92 + perror("TUNSETPERSIST");
  93 + exit(1);
  94 + }
  95 + printf("Set '%s' nonpersistent\n", ifr.ifr_name);
  96 + }
  97 + else {
  98 + if(ioctl(tap_fd, TUNSETPERSIST, 1) < 0){
  99 + perror("TUNSETPERSIST");
  100 + exit(1);
  101 + }
  102 + if(ioctl(tap_fd, TUNSETOWNER, owner) < 0){
  103 + perror("TUNSETPERSIST");
  104 + exit(1);
  105 + }
  106 + if(brief)
  107 + printf("%s\n", ifr.ifr_name);
  108 + else printf("Set '%s' persistent and owned by uid %ld\n", ifr.ifr_name,
  109 + owner);
  110 + }
  111 + return(0);
  112 +}
1  doc/short-desc
... ... @@ -0,0 +1 @@
  1 +Linux "Universal TUN/TAP device" driver
7 src/Makefile
... ... @@ -0,0 +1,7 @@
  1 +include ../../../support/include.mk
  2 +
  3 +all: $(ERL_OBJECTS)
  4 +
  5 +clean:
  6 + -rm $(ERL_OBJECTS)
  7 +
51 src/eth2udp.erl
... ... @@ -0,0 +1,51 @@
  1 +%%%----------------------------------------------------------------------
  2 +%%% File : eth2udp.erl
  3 +%%% Author : Luke Gorrie <luke@bluetail.com>
  4 +%%% Purpose : Ethernet-over-UDP tunnels
  5 +%%% Created : 18 Feb 2003 by Luke Gorrie <luke@bluetail.com>
  6 +%%%----------------------------------------------------------------------
  7 +
  8 +-module(eth2udp).
  9 +-author('luke@bluetail.com').
  10 +
  11 +-import(lists, [foreach/2]).
  12 +
  13 +-export([start_link/2, start_link/3]).
  14 +-export([init/3]).
  15 +-export([loop/3]).
  16 +
  17 +%% start_link(Port, [EndPoint]) -> pid()
  18 +%%
  19 +%% Port = integer() (UDP port for local endpoint)
  20 +%% EndPoint = {IP, Port} (UDP endpoint of remote tunnel)
  21 +start_link(Port, EPs) ->
  22 + start_link(Port, EPs, undefined).
  23 +start_link(Port, EPs, Dev) ->
  24 + spawn_link(?MODULE, init, [Port, EPs, Dev]).
  25 +
  26 +init(Port, EPs, Dev) ->
  27 + {ok, Socket} = gen_udp:open(Port, [binary]),
  28 + {ok, Tunnel} = init_tunnel(Dev),
  29 + tuntap:set_active(Tunnel, true),
  30 + loop(Tunnel, Socket, EPs).
  31 +
  32 +loop(Tunnel, Socket, EPs) ->
  33 + receive
  34 + {Tunnel, {data, Packet}} ->
  35 + io:format("Got ~p byte packet on tap~n", [size(Packet)]),
  36 + foreach(fun({IP, Port}) ->
  37 + gen_udp:send(Socket, IP, Port, Packet)
  38 + end,
  39 + EPs);
  40 + {udp, Socket, _, _, Packet} ->
  41 + io:format("Got ~p byte packet on UDP~n", [size(Packet)]),
  42 + tuntap:write(Tunnel, Packet)
  43 + end,
  44 + ?MODULE:loop(Tunnel, Socket, EPs).
  45 +
  46 +init_tunnel(Dev) ->
  47 + Tun = tuntap:open_tuntap(tap, Dev),
  48 + Name = tuntap:device_name(Tun),
  49 + io:format("Alive and kicking on ~p~n", [Name]),
  50 + {ok, Tun}.
  51 +
88 src/tuntap.erl
... ... @@ -0,0 +1,88 @@
  1 +%%%-------------------------------------------------------------------
  2 +%%% File : tuntap.erl
  3 +%%% Author : Luke Gorrie <luke@bluetail.com>
  4 +%%% Purpose : Linux "Universal TUN/TAP device" driver
  5 +%%% See /usr/src/linux/Documentation/networking/tuntap.txt
  6 +%%%
  7 +%%% Created : 10 Nov 2001 by Luke Gorrie <luke@bluetail.com>
  8 +%%%-------------------------------------------------------------------
  9 +
  10 +%% Note: if you want to be able to create tunnels as some user other
  11 +%% than root, you should chmod "/dev/net/tun" to be read/write by the
  12 +%% appropriate user.
  13 +
  14 +%% To actually communicate between your host machine and Erlang via a
  15 +%% tun/tap interface, you will probably want to assign it an IP
  16 +%% address and setup routing. For example (on Linux):
  17 +%%
  18 +%% ifconfig tun0 200.0.0.1 pointopoint 200.0.0.2
  19 +%%
  20 +%% Will configure the tun0 interface so that the host machine (Linux
  21 +%% itself) has the address 200.0.0.1 and that it believes Erlang has
  22 +%% the address 200.0.0.2 (i.e. it sets up routing of that address into
  23 +%% the tunnel).
  24 +%%
  25 +%% The exact setup varies from unix to unix. The point is that you
  26 +%% configure it exactly like a normal network interface: assign an
  27 +%% address for the local machine to use and a route for any addresses
  28 +%% you want to talk to "out there".
  29 +
  30 +-module(tuntap).
  31 +
  32 +-author('luke@bluetail.com').
  33 +
  34 +-export([init/0,
  35 + open_tun/0, open_tap/0, open_tuntap/2,
  36 + device_name/1, write/2, set_active/2]).
  37 +
  38 +-define(REPLY_ERROR, 0).
  39 +-define(REPLY_OK, 1).
  40 +
  41 +-define(REQUEST_GET_DEVICE, 0).
  42 +-define(REQUEST_WRITE, 1).
  43 +-define(REQUEST_ACTIVE, 2).
  44 +
  45 +-define(ACTIVE_FALSE, 0).
  46 +-define(ACTIVE_TRUE, 1).
  47 +-define(ACTIVE_ONCE, 2).
  48 +
  49 +%% Returns: ok
  50 +init() ->
  51 + erl_ddll:start(),
  52 + ok = erl_ddll:load_driver(code:priv_dir(tuntap), "tun_drv").
  53 +
  54 +%% Returns port()
  55 +open_tun() -> open_tuntap(tun, undefined).
  56 +open_tap() -> open_tuntap(tap, undefined).
  57 +
  58 +%% open_tuntap(tun|tap, undefined|string()) -> port()
  59 +open_tuntap(Type, Dev) ->
  60 + %% It seems to be okay to init() multiple times..
  61 + ok = init(),
  62 + TypeName = case Type of
  63 + tun -> "tun";
  64 + tap -> "tap"
  65 + end,
  66 + DevArg = if Dev == undefined -> "";
  67 + list(Dev) -> " " ++ Dev
  68 + end,
  69 + open_port({spawn, "tun_drv "++TypeName++DevArg}, [binary]).
  70 +
  71 +%% Returns: string() (e.g. "tun0" or "tap42")
  72 +device_name(Port) ->
  73 + [?REPLY_OK|Name] = erlang:port_control(Port, ?REQUEST_GET_DEVICE, []),
  74 + Name.
  75 +
  76 +%% Returns: ok
  77 +write(Port, Packet) ->
  78 + erlang:port_command(Port, Packet),
  79 + ok.
  80 +
  81 +set_active(Port, false) -> set_active1(Port, ?ACTIVE_FALSE);
  82 +set_active(Port, true) -> set_active1(Port, ?ACTIVE_TRUE);
  83 +set_active(Port, once) -> set_active1(Port, ?ACTIVE_ONCE).
  84 +
  85 +set_active1(Port, Arg) ->
  86 + [?REPLY_OK] = erlang:port_control(Port, ?REQUEST_ACTIVE, [Arg]),
  87 + ok.
  88 +

0 comments on commit 54694ab

Please sign in to comment.
Something went wrong with that request. Please try again.