genode/repos/ports/src/app/openvpn/tun_genode.cc
Josef Söntgen 9f886e1d94 ports: add openvpn-2.3.4
The port was succesfully tested a echo test and lighttpd. DHCP over
OpenVPN is not tested and probably will not work out of the box.
Therefore, the ip address etc. need to be specified manually.

For now, only ethernet bridging (using a TAP device) is supported.

Fixes #1235.
2014-08-26 11:00:35 +02:00

304 lines
7.2 KiB
C++

/**
* \brief TUN/TAP to Nic_session interface
* \author Josef Soentgen
* \date 2014-06-05
*/
/*
* Copyright (C) 2014 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
/* Genode includes */
#include <base/printf.h>
#include <base/snprintf.h>
#include <cap_session/connection.h>
#include <nic_session/rpc_object.h>
#include <os/server.h>
#include <root/component.h>
#include <util/string.h>
/* local includes */
#include "tuntap.h"
/* OpenVPN includes */
extern "C" {
#include "config.h"
#include "syshead.h"
#include "socket.h"
#include "tun.h"
}
static bool verbose = false;
#define PDBGV(...) if (verbose) PDBG(__VA_ARGS__)
#define TRACE do { PDBGV("%s: called", __func__); } while (0)
extern Tuntap_device *tuntap_dev();
static in_addr_t gen_broadcast_addr(in_addr_t local, in_addr_t netmask) {
return local | ~netmask; }
extern "C" void open_tun(char const *dev, char const *dev_type,
char const *dev_node, struct tuntap *tt)
{
/* start with a failed attempt to open tun/tap device */
tt->fd = -1;
if (tt->ipv6) {
PERR("IPv6 is currently not supported!");
return;
}
if (tt->type == DEV_TYPE_NULL) {
PERR("null device not supported");
return;
}
char name[256];
Genode::snprintf(name, sizeof (name), "/dev/%s", dev);
tt->actual_name = string_alloc(name, NULL);
tt->fd = tuntap_dev()->fd();
PDBGV("tt->fd:%d", tuntap_dev()->fd());
}
extern "C" void close_tun(struct tuntap *tt)
{
free(tt->actual_name);
free(tt);
}
extern "C" int write_tun(struct tuntap *tt, uint8_t *buf, int len)
{
PDBGV("tt->fd:%d buf:0x%p len: %d", tt->fd, buf, len);
if (len <= 0)
return -1;
switch (tt->type) {
case DEV_TYPE_TAP:
return tuntap_dev()->write(reinterpret_cast<char const*>(buf), len);
break;
case DEV_TYPE_TUN:
break;
}
return -1;
}
extern "C" int read_tun(struct tuntap *tt, uint8_t *buf, int len)
{
PDBGV("tt->fd:%d buf:0x%p len: %d", tt->fd, buf, len);
if (len <= 0)
return -1;
{
/* read from fd to prevent select() from triggering more than once */
char tmp[1];
::read(tt->fd, tmp, sizeof (tmp));
}
switch (tt->type) {
case DEV_TYPE_TAP:
return tuntap_dev()->read(reinterpret_cast<char*>(buf), len);
break;
case DEV_TYPE_TUN:
break;
}
return -1;
}
extern "C" void tuncfg(char const *dev, char const *dev_type,
char const *dev_node, int persist_mode,
char const *username, char const *groupname,
struct tuntap_options const *options)
{
PDBGV("dev:'%s' dev_type:'%s' dev_node:'%s' persist_mode:%d"
"username:'%s' groupname:'%s' options:0x%p",
dev, dev_type, dev_node, persist_mode, username, groupname, options);
}
extern "C" char const *guess_tuntap_dev(char const *dev, char const *dev_type,
char const *dev_node, struct gc_arena *gc)
{
return dev;
}
extern "C" struct tuntap *init_tun(char const *dev, char const *dev_type,
int topology, char const *ifconfig_local_parm,
char const *ifconfig_remote_netmask_parm,
char const *ifconfig_ipv6_local_parm,
int ifconfig_ipv6_netbits_parm,
char const *ifconfig_ipv6_remote_parm,
in_addr_t local_public, in_addr_t remote_public,
bool const strict_warn, struct env_set *es)
{
PDBGV("dev:'%s' dev_type:'%s' topology:%d ifconfig_local_parm:'%s'"
"ifconfig_remote_netmask_parm:'%s' es:0x%p", dev, dev_type,
topology, ifconfig_local_parm, ifconfig_remote_netmask_parm, es);
struct tuntap *tt;
ALLOC_OBJ(tt, struct tuntap);
Genode::memset(tt, 0, sizeof (struct tuntap));
tt->fd = -1;
tt->ipv6 = false;
tt->type = dev_type_enum(dev, dev_type);
tt->topology = topology;
if (ifconfig_local_parm && ifconfig_remote_netmask_parm) {
bool tun = is_tun_p2p(tt);
tt->local = getaddr(GETADDR_RESOLVE | GETADDR_HOST_ORDER |
GETADDR_FATAL_ON_SIGNAL | GETADDR_FATAL,
ifconfig_local_parm, 0, NULL, NULL);
tt->remote_netmask = getaddr((tun ? GETADDR_RESOLVE : 0) |
GETADDR_HOST_ORDER | GETADDR_FATAL_ON_SIGNAL |
GETADDR_FATAL, ifconfig_remote_netmask_parm,
0, NULL, NULL);
if (!tun) {
tt->broadcast = gen_broadcast_addr(tt->local, tt->remote_netmask);
}
tt->did_ifconfig_setup = true;
}
return tt;
}
extern "C" void init_tun_post(struct tuntap *tt, struct frame const *frame,
struct tuntap_options const *options)
{
TRACE;
}
extern "C" void do_ifconfig(struct tuntap *tt, char const *actual_name,
int tun_mtu, struct env_set const *es)
{
TRACE;
/**
* After OpenVPN has received a PUSH_REPLY it will configure
* the TUN/TAP device by calling this function. At this point
* it is save to actually announce the Nic_session. Therefore,
* we release the lock.
*/
tuntap_dev()->up();
}
extern "C" bool is_dev_type(char const *dev, char const *dev_type,
char const *match_type)
{
if (!dev)
return false;
if (dev_type)
return !Genode::strcmp(dev_type, match_type);
else
return !Genode::strcmp(dev, match_type, Genode::strlen(match_type));
}
extern "C" int dev_type_enum(char const *dev, char const *dev_type)
{
if (is_dev_type(dev, dev_type, "tap"))
return DEV_TYPE_TAP;
if (is_dev_type(dev, dev_type, "tun"))
return DEV_TYPE_TUN;
if (is_dev_type(dev, dev_type, "null"))
return DEV_TYPE_NULL;
return DEV_TYPE_UNDEF;
}
extern "C" char const *dev_type_string(char const *dev, char const *dev_type)
{
switch (dev_type_enum(dev, dev_type)) {
case DEV_TYPE_TAP:
return "tap";
case DEV_TYPE_TUN:
return "tun";
case DEV_TYPE_NULL:
return "null";
default:
return "[unknown-dev-type]";
}
}
extern "C" char const *ifconfig_options_string(struct tuntap const* tt,
bool remote, bool disable,
struct gc_arena *gc)
{
TRACE;
return 0;
}
extern "C" bool is_tun_p2p(struct tuntap const *tt)
{
bool tun = false;
if (tt->type == DEV_TYPE_TAP ||
(tt->type == DEV_TYPE_TUN && tt->topology == TOP_SUBNET))
tun = false;
else if (tt->type == DEV_TYPE_TUN)
tun = true;
else
PERR("problem with tun vs. tap setting");
return tun;
}
extern "C" void check_subnet_conflict(const in_addr_t, const in_addr_t,
char const *) { TRACE; }
extern "C" void warn_on_use_of_common_subnets(void) { TRACE; }
extern "C" char const *tun_stat(struct tuntap const *tt, unsigned rwflags,
struct gc_arena *gc)
{
struct buffer out = alloc_buf_gc(64, gc);
if (tt) {
if (rwflags & EVENT_READ) {
buf_printf(&out, "T%s", (tt->rwflags_debug & EVENT_READ) ? "R" : "r");
}
if (rwflags & EVENT_WRITE) {
buf_printf(&out, "T%s", (tt->rwflags_debug & EVENT_WRITE) ? "W" : "w");
}
}
else
buf_printf(&out, "T?");
return buf_str(&out);
}