From 7a54fe5e884e98ade91af527c67f9c5df1452a50 Mon Sep 17 00:00:00 2001 From: Pacien TRAN-GIRARD Date: Mon, 27 Feb 2017 20:56:55 +0100 Subject: [PATCH] Add fd_device --- doc/tinc.conf.5.in | 4 ++ doc/tinc.texi | 6 +++ src/Makefile.am | 1 + src/device.h | 1 + src/ethernet.h | 4 ++ src/fd_device.c | 123 +++++++++++++++++++++++++++++++++++++++++++++ src/net_setup.c | 2 + 7 files changed, 141 insertions(+) create mode 100644 src/fd_device.c diff --git a/doc/tinc.conf.5.in b/doc/tinc.conf.5.in index 783c299f..9365184b 100644 --- a/doc/tinc.conf.5.in +++ b/doc/tinc.conf.5.in @@ -234,6 +234,10 @@ Do NOT connect multiple .Nm tinc daemons to the same multicast address, this will very likely cause routing loops. Also note that this can cause decrypted VPN packets to be sent out on a real network if misconfigured. +.It fd +Use a file descriptor. +All packets are read from this interface. +Packets received for the local node are written to it. .It uml Pq not compiled in by default Create a UNIX socket with the filename specified by .Va Device , diff --git a/doc/tinc.texi b/doc/tinc.texi index a6e34d60..29e2bdc9 100644 --- a/doc/tinc.texi +++ b/doc/tinc.texi @@ -958,6 +958,12 @@ This can be used to connect to UML, QEMU or KVM instances listening on the same Do NOT connect multiple tinc daemons to the same multicast address, this will very likely cause routing loops. Also note that this can cause decrypted VPN packets to be sent out on a real network if misconfigured. +@cindex fd +@item fd +Use a file descriptor. +All packets are read from this interface. +Packets received for the local node are written to it. + @cindex UML @item uml (not compiled in by default) Create a UNIX socket with the filename specified by diff --git a/src/Makefile.am b/src/Makefile.am index 2a1dbeba..794e4202 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -59,6 +59,7 @@ tincd_SOURCES = \ edge.c edge.h \ ethernet.h \ event.c event.h \ + fd_device.c \ graph.c graph.h \ hash.c hash.h \ have.h \ diff --git a/src/device.h b/src/device.h index 8046a255..fa27df3d 100644 --- a/src/device.h +++ b/src/device.h @@ -40,6 +40,7 @@ extern const devops_t os_devops; extern const devops_t dummy_devops; extern const devops_t raw_socket_devops; extern const devops_t multicast_devops; +extern const devops_t fd_devops; extern const devops_t uml_devops; extern const devops_t vde_devops; extern devops_t devops; diff --git a/src/ethernet.h b/src/ethernet.h index 085e96ad..2165c51e 100644 --- a/src/ethernet.h +++ b/src/ethernet.h @@ -45,6 +45,10 @@ #define ETH_P_8021Q 0x8100 #endif +#ifndef ETH_P_MAX +#define ETH_P_MAX 0xFFFF +#endif + #ifndef HAVE_STRUCT_ETHER_HEADER struct ether_header { uint8_t ether_dhost[ETH_ALEN]; diff --git a/src/fd_device.c b/src/fd_device.c new file mode 100644 index 00000000..67e0cb78 --- /dev/null +++ b/src/fd_device.c @@ -0,0 +1,123 @@ +/* + fd_device.c -- Interaction with Android tun fd + Copyright (C) 2001-2005 Ivo Timmermans, + 2001-2016 Guus Sliepen + 2009 Grzegorz Dymarek + 2016 Pacien TRAN-GIRARD + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "system.h" +#include "conf.h" +#include "device.h" +#include "ethernet.h" +#include "logger.h" +#include "net.h" +#include "route.h" +#include "utils.h" + +static inline bool check_config(void) { + if(routing_mode == RMODE_SWITCH) { + logger(DEBUG_ALWAYS, LOG_ERR, "Switch mode not supported (requires unsupported TAP device)!"); + return false; + } + + if(!get_config_int(lookup_config(config_tree, "Device"), &device_fd)) { + logger(DEBUG_ALWAYS, LOG_ERR, "Could not read fd from configuration!"); + return false; + } + + return true; +} + +static bool setup_device(void) { + if(!check_config()) { + return false; + } + + if(device_fd < 0) { + logger(DEBUG_ALWAYS, LOG_ERR, "Could not open %s: %s!", device, strerror(errno)); + return false; + } + + logger(DEBUG_ALWAYS, LOG_INFO, "fd/%d adapter set up.", device_fd); + + return true; +} + +static void close_device(void) { + close(device_fd); + device_fd = -1; +} + +static inline uint16_t get_ip_ethertype(vpn_packet_t *packet) { + switch (DATA(packet)[ETH_HLEN] >> 4) { + case 4: + return ETH_P_IP; + + case 6: + return ETH_P_IPV6; + + default: + return ETH_P_MAX; + } +} + +static inline void set_etherheader(vpn_packet_t *packet, uint16_t ethertype) { + memset(DATA(packet), 0, ETH_HLEN - ETHER_TYPE_LEN); + + DATA(packet)[ETH_HLEN - ETHER_TYPE_LEN] = (ethertype >> 8) & 0xFF; + DATA(packet)[ETH_HLEN - ETHER_TYPE_LEN + 1] = ethertype & 0xFF; +} + +static bool read_packet(vpn_packet_t *packet) { + int lenin = read(device_fd, DATA(packet) + ETH_HLEN, MTU - ETH_HLEN); + if(lenin <= 0) { + logger(DEBUG_ALWAYS, LOG_ERR, "Error while reading from fd/%d: %s!", device_fd, strerror(errno)); + return false; + } + + uint16_t ethertype = get_ip_ethertype(packet); + if(ethertype == ETH_P_MAX) { + logger(DEBUG_TRAFFIC, LOG_ERR, "Unknown IP version while reading packet from fd/%d!", device_fd); + return false; + } + + set_etherheader(packet, ethertype); + packet->len = lenin + ETH_HLEN; + + logger(DEBUG_TRAFFIC, LOG_DEBUG, "Read packet of %d bytes from fd/%d.", packet->len, device_fd); + + return true; +} + +static bool write_packet(vpn_packet_t *packet) { + logger(DEBUG_TRAFFIC, LOG_DEBUG, "Writing packet of %d bytes to fd/%d.", packet->len, device_fd); + + if(write(device_fd, DATA(packet) + ETH_HLEN, packet->len - ETH_HLEN) < 0) { + logger(DEBUG_ALWAYS, LOG_ERR, "Error while writing to fd/%d: %s!", device_fd, strerror(errno)); + return false; + } + + return true; +} + +const devops_t fd_devops = { + .setup = setup_device, + .close = close_device, + .read = read_packet, + .write = write_packet, +}; diff --git a/src/net_setup.c b/src/net_setup.c index 9293c118..f5fddd4e 100644 --- a/src/net_setup.c +++ b/src/net_setup.c @@ -929,6 +929,8 @@ static bool setup_myself(void) { devops = raw_socket_devops; else if(!strcasecmp(type, "multicast")) devops = multicast_devops; + else if(!strcasecmp(type, "fd")) + devops = fd_devops; #ifdef ENABLE_UML else if(!strcasecmp(type, "uml")) devops = uml_devops;