From 1672dbd66b619f84cb86950298ad89df93e1dcba Mon Sep 17 00:00:00 2001 From: Etienne Dechamps Date: Sat, 3 Dec 2016 22:52:30 +0000 Subject: [PATCH 01/21] Fix crash on Windows when a socket is available for both write and read. Currently, if both write and read events fire at the same time on a socket, the Windows-specific event loop will call both the write and read callbacks, in that order. Problem is, the write callback could have deleted the io handle, which makes the next call to the write callback a use-after-free typically resulting in a hard crash. In practice, this issue is triggered quite easily by putting the computer to sleep, which basically freezes the tinc process. When the computer wakes up and the process resumes, all TCP connections are suddenly gone; as a result, the following sequence of events might appear in the logs: Metadata socket read error for node1 (1.2.3.4 port 655): (10054) An existing connection was forcibly closed by the remote host. Closing connection with node1 (1.2.3.4 port 655) Sending DEL_EDGE to everyone (BROADCAST): 13 4bf6 mynode node1 Sending 43 bytes of metadata to node2 (5.6.7.8 port 655) Could not send 10891 bytes of data to node2 (5.6.7.8 port 655): (10054) An existing connection was forcibly closed by the remote host.a Closing connection with node2 (5.6.7.8 port 655) In this example the crash occurs because the socket to node2 was signaled for reading *in addition* to writing, but since the connection was terminated, the attempt to call the read callback crashed the process. This commit fixes the problem by not even attempting to fire the write callback when the write event on the socket is signaled - instead, we just rely on the part of the event loop that simulates level-triggered write events. Arguably that's even cleaner and faster, because the code being removed was technically redundant - we have to go through that write check loop anyway. --- src/event.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/event.c b/src/event.c index 59b96e37..858e1d9d 100644 --- a/src/event.c +++ b/src/event.c @@ -357,10 +357,13 @@ bool event_loop(void) { WSANETWORKEVENTS network_events; if (WSAEnumNetworkEvents(io->fd, io->event, &network_events) != 0) return false; - if (network_events.lNetworkEvents & WRITE_EVENTS) - io->cb(io->data, IO_WRITE); if (network_events.lNetworkEvents & READ_EVENTS) io->cb(io->data, IO_READ); + /* + The fd might be available for write too. However, if we already fired the read callback, that + callback might have deleted the io (e.g. through terminate_connection()), so we can't fire the + write callback here. Instead, we loop back and let the writable io loop above handle it. + */ } } #endif From d21d97eaf5db9e848d8eee28784c4f54af85f43d Mon Sep 17 00:00:00 2001 From: Etienne Dechamps Date: Sat, 3 Dec 2016 23:13:46 +0000 Subject: [PATCH 02/21] On Windows, don't cancel I/O when disabling the device. I have observed cases where disable_device() can get stuck on the GetOverlappedResult() call, especially when the computer is waking up from sleep. This is problematic when combined with DeviceStandby=yes: other_side (1.2.3.4 port 655) didn't respond to PING in 5 seconds Closing connection with other_side (1.2.3.4 port 655) Disabling Windows tap device gdb reveals the following stack trace: #0 0x77c7dd3c in ?? () #1 0x7482aad0 in KERNELBASE!GetOverlappedResult () from C:\WINDOWS\SysWoW64\KernelBase.dll #2 0x0043c343 in disable_device () at mingw/device.c:244 #3 0x0040fcee in device_disable () at net_setup.c:759 #4 0x00405bb5 in check_reachability () at graph.c:292 #5 0x00405be2 in graph () at graph.c:301 #6 0x004088db in terminate_connection (c=0x4dea5c0, report=true) at net.c:108 #7 0x00408aed in timeout_handler (data=0x5af0c0 ) at net.c:168 #8 0x00403af8 in get_time_remaining (diff=0x2a8fd64) at event.c:239 #9 0x00403b6c in event_loop () at event.c:303 #10 0x00409904 in main_loop () at net.c:461 #11 0x00424a95 in main2 (argc=6, argv=0x2b42a60) at tincd.c:489 #12 0x00424788 in main (argc=6, argv=0x2b42a60) at tincd.c:416 This is with TAP-Win32 9.0.0.9. I suspect driver bugs related to sleep. In any case, this commit fixes the issue by cancelling I/O only when the entire tinc process is being gracefully shut down, as opposed to every time the device is disabled. Thankfully, the driver seems to be perfectly fine with this code issuing TAP_IOCTL_SET_MEDIA_STATUS ioctls while there are I/O operations inflight. --- src/mingw/device.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/mingw/device.c b/src/mingw/device.c index b0476155..dfdb9641 100644 --- a/src/mingw/device.c +++ b/src/mingw/device.c @@ -214,6 +214,9 @@ static bool setup_device(void) { logger(DEBUG_ALWAYS, LOG_INFO, "%s (%s) is a %s", device, iface, device_info); + device_read_overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + device_write_overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + return true; } @@ -226,9 +229,6 @@ static void enable_device(void) { /* We don't use the write event directly, but GetOverlappedResult() does, internally. */ - device_read_overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); - device_write_overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); - io_add_event(&device_read_io, device_handle_read, NULL, device_read_overlapped.hEvent); device_issue_read(); } @@ -237,6 +237,19 @@ static void disable_device(void) { logger(DEBUG_ALWAYS, LOG_INFO, "Disabling %s", device_info); io_del(&device_read_io); + + ULONG status = 0; + DWORD len; + DeviceIoControl(device_handle, TAP_IOCTL_SET_MEDIA_STATUS, &status, sizeof status, &status, sizeof status, &len, NULL); + + /* Note that we don't try to cancel ongoing I/O here - we just stop listening. + This is because some TAP-Win32 drivers don't seem to handle cancellation very well, + especially when combined with other events such as the computer going to sleep - cases + were observed where the GetOverlappedResult() would just block indefinitely and never + return in that case. */ +} + +static void close_device(void) { CancelIo(device_handle); /* According to MSDN, CancelIo() does not necessarily wait for the operation to complete. @@ -253,11 +266,6 @@ static void disable_device(void) { CloseHandle(device_read_overlapped.hEvent); CloseHandle(device_write_overlapped.hEvent); - ULONG status = 0; - DeviceIoControl(device_handle, TAP_IOCTL_SET_MEDIA_STATUS, &status, sizeof status, &status, sizeof status, &len, NULL); -} - -static void close_device(void) { CloseHandle(device_handle); device_handle = INVALID_HANDLE_VALUE; free(device); device = NULL; From 4a45a65fe2555efc5b6763a293d30251755e78a8 Mon Sep 17 00:00:00 2001 From: Guus Sliepen Date: Tue, 14 Feb 2017 20:51:43 +0100 Subject: [PATCH 03/21] Remove the description of the LocalDiscoveryAddress option from the manual. This option is no longer implemented. --- doc/tinc.texi | 4 ---- 1 file changed, 4 deletions(-) diff --git a/doc/tinc.texi b/doc/tinc.texi index cb50e74a..a6e34d60 100644 --- a/doc/tinc.texi +++ b/doc/tinc.texi @@ -1086,10 +1086,6 @@ which normally would prevent the peers from learning each other's LAN address. Currently, local discovery is implemented by sending some packets to the local address of the node during UDP discovery. This will not work with old nodes that don't transmit their local address. -@cindex LocalDiscoveryAddress -@item LocalDiscoveryAddress <@var{address}> -If this variable is specified, local discovery packets are sent to the given @var{address}. - @cindex Mode @item Mode = (router) This option selects the way packets are routed to other daemons. From 7a54fe5e884e98ade91af527c67f9c5df1452a50 Mon Sep 17 00:00:00 2001 From: Pacien TRAN-GIRARD Date: Mon, 27 Feb 2017 20:56:55 +0100 Subject: [PATCH 04/21] 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; From ef661316f163f2ecf6c75a3dd28e2cad51772c56 Mon Sep 17 00:00:00 2001 From: Roman Savelyev Date: Tue, 7 Mar 2017 19:07:19 +0100 Subject: [PATCH 05/21] Fix lost pointer trails in get_known_addresses(). --- src/net_socket.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/net_socket.c b/src/net_socket.c index 8259d9a5..36ea5fed 100644 --- a/src/net_socket.c +++ b/src/net_socket.c @@ -570,6 +570,7 @@ begin: // Find edges pointing to this node, and use them to build a list of unique, known addresses. static struct addrinfo *get_known_addresses(node_t *n) { struct addrinfo *ai = NULL; + struct addrinfo *oai = NULL; for splay_each(edge_t, e, n->edge_tree) { if(!e->reverse) @@ -585,16 +586,15 @@ static struct addrinfo *get_known_addresses(node_t *n) { if(found) continue; - struct addrinfo *nai = xzalloc(sizeof *nai); - if(ai) - ai->ai_next = nai; - ai = nai; + oai = ai; + ai = xzalloc(sizeof *ai); ai->ai_family = e->reverse->address.sa.sa_family; ai->ai_socktype = SOCK_STREAM; ai->ai_protocol = IPPROTO_TCP; ai->ai_addrlen = SALEN(e->reverse->address.sa); ai->ai_addr = xmalloc(ai->ai_addrlen); memcpy(ai->ai_addr, &e->reverse->address, ai->ai_addrlen); + ai->ai_next = oai; } return ai; From 3fc678a8df4d6c057ef9f2c602b86ed106651e68 Mon Sep 17 00:00:00 2001 From: Guus Sliepen Date: Tue, 7 Mar 2017 19:19:19 +0100 Subject: [PATCH 06/21] Use free_known_addresses() to free memory allocated by get_known_addresses(). We know what struct addrinfo looks like, but the standard says nothing about how it is allocated. So we cannot trust freeaddrinfo() to work correctly on the struct addrinfo list we allocated ourselves in get_known_addresses(). To make a distinction by allocations from the latter and from str2addrinfo(), we keep two pointers (*ai and *kai) in struct outgoing, and use the freeing function that is appropriate for each. --- src/net.h | 3 ++- src/net_socket.c | 21 ++++++++++++++++++--- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/net.h b/src/net.h index 70808695..69ca4887 100644 --- a/src/net.h +++ b/src/net.h @@ -126,8 +126,9 @@ typedef struct outgoing_t { int timeout; splay_tree_t *config_tree; struct config_t *cfg; - struct addrinfo *ai; + struct addrinfo *ai; // addresses from config files struct addrinfo *aip; + struct addrinfo *kai; // addresses known via other online nodes (use free_known_addresses()) timeout_t ev; } outgoing_t; diff --git a/src/net_socket.c b/src/net_socket.c index 36ea5fed..a1934e9f 100644 --- a/src/net_socket.c +++ b/src/net_socket.c @@ -441,13 +441,20 @@ static void handle_meta_io(void *data, int flags) { handle_meta_connection_data(c); } +static void free_known_addresses(struct addrinfo *ai) { + for(struct addrinfo *aip = ai, *next; aip; aip = next) { + next = aip->ai_next; + free(aip); + } +} + bool do_outgoing_connection(outgoing_t *outgoing) { char *address, *port, *space; struct addrinfo *proxyai = NULL; int result; begin: - if(!outgoing->ai) { + if(!outgoing->ai && !outgoing->kai) { if(!outgoing->cfg) { logger(DEBUG_CONNECTIONS, LOG_ERR, "Could not set up a meta connection to %s", outgoing->name); retry_outgoing(outgoing); @@ -477,6 +484,11 @@ begin: if(outgoing->ai) freeaddrinfo(outgoing->ai); outgoing->ai = NULL; + + if(outgoing->kai) + free_known_addresses(outgoing->kai); + outgoing->kai = NULL; + goto begin; } @@ -621,8 +633,8 @@ void setup_outgoing_connection(outgoing_t *outgoing) { if(!outgoing->cfg) { if(n) - outgoing->aip = outgoing->ai = get_known_addresses(n); - if(!outgoing->ai) { + outgoing->aip = outgoing->kai = get_known_addresses(n); + if(!outgoing->kai) { logger(DEBUG_ALWAYS, LOG_DEBUG, "No address known for %s", outgoing->name); goto remove; } @@ -777,6 +789,9 @@ static void free_outgoing(outgoing_t *outgoing) { if(outgoing->ai) freeaddrinfo(outgoing->ai); + if(outgoing->kai) + free_known_addresses(outgoing->kai); + if(outgoing->config_tree) exit_configuration(&outgoing->config_tree); From 26dc50599d6e38be1e7304602ffea0ba282c1091 Mon Sep 17 00:00:00 2001 From: Guus Sliepen Date: Mon, 20 Mar 2017 22:15:20 +0100 Subject: [PATCH 07/21] Add missing #defines used by fd_device.c. --- src/ethernet.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/ethernet.h b/src/ethernet.h index 2165c51e..0b4a1db0 100644 --- a/src/ethernet.h +++ b/src/ethernet.h @@ -25,6 +25,15 @@ #define ETH_ALEN 6 #endif +#ifndef ETH_HLEN +#define ETH_HLEN 14 +#endif + +#ifndef ETHER_TYPE_LEN +#define ETHER_TYPE_LEN 2 +#endif + + #ifndef ARPHRD_ETHER #define ARPHRD_ETHER 1 #endif From 2c333f0f45f445d0811a364817d03df0c8cf2d2f Mon Sep 17 00:00:00 2001 From: Guus Sliepen Date: Mon, 20 Mar 2017 22:19:36 +0100 Subject: [PATCH 08/21] Don't try to use kill() on Windows. --- src/tincctl.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/tincctl.c b/src/tincctl.c index 1f0246c0..5067ded5 100644 --- a/src/tincctl.c +++ b/src/tincctl.c @@ -1,6 +1,6 @@ /* tincctl.c -- Controlling a running tincd - Copyright (C) 2007-2016 Guus Sliepen + Copyright (C) 2007-2017 Guus Sliepen 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 @@ -721,6 +721,8 @@ bool connect_tincd(bool verbose) { } fclose(f); + +#ifndef HAVE_MINGW if ((pid == 0) || (kill(pid, 0) && (errno == ESRCH))) { fprintf(stderr, "Could not find tincd running at pid %d\n", pid); /* clean up the stale socket and pid file */ @@ -729,7 +731,6 @@ bool connect_tincd(bool verbose) { return false; } -#ifndef HAVE_MINGW struct sockaddr_un sa; sa.sun_family = AF_UNIX; strncpy(sa.sun_path, unixsocketname, sizeof sa.sun_path); From 3e643d5d7e5c7db35eacb3703d497c584e93cf18 Mon Sep 17 00:00:00 2001 From: "Vittorio Gambaletta (VittGam)" Date: Wed, 12 Oct 2016 13:52:17 +0200 Subject: [PATCH 09/21] route: Support ToS/DiffServ priority inheritance when routing IPv6 packets. Signed-off-by: Vittorio Gambaletta --- src/route.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/route.c b/src/route.c index 0c9f2aa1..c10fddb5 100644 --- a/src/route.c +++ b/src/route.c @@ -683,6 +683,9 @@ static void route_ipv6(node_t *source, vpn_packet_t *packet) { if(!do_decrement_ttl(source, packet)) return; + if(priorityinheritance) + packet->priority = ((DATA(packet)[14] & 0x0f) << 4) | (DATA(packet)[15] >> 4); + via = (subnet->owner->via == myself) ? subnet->owner->nexthop : subnet->owner->via; if(via == source) { @@ -954,8 +957,12 @@ static void route_mac(node_t *source, vpn_packet_t *packet) { uint16_t type = DATA(packet)[12] << 8 | DATA(packet)[13]; - if(priorityinheritance && type == ETH_P_IP && packet->len >= ether_size + ip_size) - packet->priority = DATA(packet)[15]; + if(priorityinheritance) { + if(type == ETH_P_IP && packet->len >= ether_size + ip_size) + packet->priority = DATA(packet)[15]; + else if(type == ETH_P_IPV6 && packet->len >= ether_size + ip6_size) + packet->priority = ((DATA(packet)[14] & 0x0f) << 4) | (DATA(packet)[15] >> 4); + } // Handle packets larger than PMTU From 5cbef906209eb5005f821af8f55a6f5d7e7d060c Mon Sep 17 00:00:00 2001 From: Guus Sliepen Date: Tue, 21 Mar 2017 21:21:23 +0100 Subject: [PATCH 10/21] Put script environment creation/deletion in functions. This makes environment handling safer, and also has a single place where we can add new environment variables that should be present for all scripts. --- src/graph.c | 23 +++++++---------- src/invitation.c | 20 +++++++-------- src/logger.c | 4 +-- src/logger.h | 4 +-- src/names.c | 5 +++- src/names.h | 3 ++- src/net_setup.c | 32 +++++++---------------- src/protocol_auth.c | 19 ++++++-------- src/script.c | 62 ++++++++++++++++++++++++++++++++++++++++----- src/script.h | 16 ++++++++++-- src/subnet.c | 35 +++++++++++-------------- src/tincctl.c | 5 +++- 12 files changed, 134 insertions(+), 94 deletions(-) diff --git a/src/graph.c b/src/graph.c index e570febb..1f1fdb3d 100644 --- a/src/graph.c +++ b/src/graph.c @@ -1,6 +1,6 @@ /* graph.c -- graph algorithms - Copyright (C) 2001-2013 Guus Sliepen , + Copyright (C) 2001-2017 Guus Sliepen , 2001-2005 Ivo Timmermans This program is free software; you can redistribute it and/or modify @@ -247,28 +247,23 @@ static void check_reachability(void) { char *name; char *address; char *port; - char *envp[8] = {NULL}; - xasprintf(&envp[0], "NETNAME=%s", netname ? : ""); - xasprintf(&envp[1], "DEVICE=%s", device ? : ""); - xasprintf(&envp[2], "INTERFACE=%s", iface ? : ""); - xasprintf(&envp[3], "NODE=%s", n->name); + environment_t env; + environment_init(&env); + environment_add(&env, "NODE=%s", n->name); sockaddr2str(&n->address, &address, &port); - xasprintf(&envp[4], "REMOTEADDRESS=%s", address); - xasprintf(&envp[5], "REMOTEPORT=%s", port); - xasprintf(&envp[6], "NAME=%s", myself->name); + environment_add(&env, "REMOTEADDRESS=%s", address); + environment_add(&env, "REMOTEPORT=%s", port); - execute_script(n->status.reachable ? "host-up" : "host-down", envp); + execute_script(n->status.reachable ? "host-up" : "host-down", &env); xasprintf(&name, n->status.reachable ? "hosts/%s-up" : "hosts/%s-down", n->name); - execute_script(name, envp); + execute_script(name, &env); free(name); free(address); free(port); - - for(int i = 0; i < 7; i++) - free(envp[i]); + environment_exit(&env); subnet_update(n, NULL, n->status.reachable); diff --git a/src/invitation.c b/src/invitation.c index 08afe785..ff93f9ee 100644 --- a/src/invitation.c +++ b/src/invitation.c @@ -1,6 +1,6 @@ /* invitation.c -- Create and accept invitations - Copyright (C) 2013-2015 Guus Sliepen + Copyright (C) 2013-2017 Guus Sliepen 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 @@ -239,7 +239,7 @@ int cmd_invite(int argc, char *argv[]) { return 1; } - char *myname = get_my_name(true); + myname = get_my_name(true); if(!myname) return 1; @@ -425,15 +425,13 @@ int cmd_invite(int argc, char *argv[]) { xasprintf(&url, "%s/%s%s", address, hash, cookie); // Call the inviation-created script - char *envp[6] = {}; - xasprintf(&envp[0], "NAME=%s", myname); - xasprintf(&envp[1], "NETNAME=%s", netname); - xasprintf(&envp[2], "NODE=%s", argv[1]); - xasprintf(&envp[3], "INVITATION_FILE=%s", filename); - xasprintf(&envp[4], "INVITATION_URL=%s", url); - execute_script("invitation-created", envp); - for(int i = 0; i < 6 && envp[i]; i++) - free(envp[i]); + environment_t env; + environment_init(&env); + environment_add(&env, "NODE=%s", argv[1]); + environment_add(&env, "INVITATION_FILE=%s", filename); + environment_add(&env, "INVITATION_URL=%s", url); + execute_script("invitation-created", &env); + environment_exit(&env); puts(url); free(url); diff --git a/src/logger.c b/src/logger.c index 6028e3d0..4075ea8c 100644 --- a/src/logger.c +++ b/src/logger.c @@ -1,6 +1,6 @@ /* logger.c -- logging code - Copyright (C) 2004-2015 Guus Sliepen + Copyright (C) 2004-2017 Guus Sliepen 2004-2005 Ivo Timmermans This program is free software; you can redistribute it and/or modify @@ -29,7 +29,7 @@ #include "process.h" #include "sptps.h" -debug_t debug_level = DEBUG_NOTHING; +int debug_level = DEBUG_NOTHING; static logmode_t logmode = LOGMODE_STDERR; static pid_t logpid; static FILE *logfile = NULL; diff --git a/src/logger.h b/src/logger.h index 252497bf..f4f46f9d 100644 --- a/src/logger.h +++ b/src/logger.h @@ -1,7 +1,7 @@ /* logger.h -- header file for logger.c Copyright (C) 1998-2005 Ivo Timmermans - 2000-2012 Guus Sliepen + 2000-2017 Guus Sliepen 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 @@ -67,7 +67,7 @@ enum { #include -extern debug_t debug_level; +extern int debug_level; extern bool logcontrol; extern int umbilical; extern void openlogger(const char *, logmode_t); diff --git a/src/names.c b/src/names.c index 6c518f26..0cae28af 100644 --- a/src/names.c +++ b/src/names.c @@ -1,7 +1,7 @@ /* names.c -- generate commonly used (file)names Copyright (C) 1998-2005 Ivo Timmermans - 2000-2015 Guus Sliepen + 2000-2017 Guus Sliepen 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 @@ -25,6 +25,7 @@ #include "xalloc.h" char *netname = NULL; +char *myname = NULL; char *confdir = NULL; /* base configuration directory */ char *confbase = NULL; /* base configuration directory for this instance of tinc */ bool confbase_given; @@ -137,6 +138,7 @@ void free_names(void) { free(logfilename); free(confbase); free(confdir); + free(myname); identname = NULL; netname = NULL; @@ -145,4 +147,5 @@ void free_names(void) { logfilename = NULL; confbase = NULL; confdir = NULL; + myname = NULL; } diff --git a/src/names.h b/src/names.h index a2395af7..e6b99c56 100644 --- a/src/names.h +++ b/src/names.h @@ -1,7 +1,7 @@ /* names.h -- header for names.c Copyright (C) 1998-2005 Ivo Timmermans - 2000-2015 Guus Sliepen + 2000-2017 Guus Sliepen 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 @@ -25,6 +25,7 @@ extern char *confdir; extern char *confbase; extern bool confbase_given; extern char *netname; +extern char *myname; extern char *identname; extern char *unixsocketname; extern char *logfilename; diff --git a/src/net_setup.c b/src/net_setup.c index f5fddd4e..6becfbb4 100644 --- a/src/net_setup.c +++ b/src/net_setup.c @@ -1,7 +1,7 @@ /* net_setup.c -- Setup. Copyright (C) 1998-2005 Ivo Timmermans, - 2000-2016 Guus Sliepen + 2000-2017 Guus Sliepen 2006 Scott Lamb 2010 Brandon Black @@ -48,7 +48,6 @@ #endif char *myport; -static char *myname; static io_t device_io; devops_t devops; bool device_standby = false; @@ -705,29 +704,17 @@ void device_enable(void) { /* Run tinc-up script to further initialize the tap interface */ - char *envp[5] = {NULL}; - xasprintf(&envp[0], "NETNAME=%s", netname ? : ""); - xasprintf(&envp[1], "DEVICE=%s", device ? : ""); - xasprintf(&envp[2], "INTERFACE=%s", iface ? : ""); - xasprintf(&envp[3], "NAME=%s", myname); - - execute_script("tinc-up", envp); - - for(int i = 0; i < 4; i++) - free(envp[i]); + environment_t env; + environment_init(&env); + execute_script("tinc-up", &env); + environment_exit(&env); } void device_disable(void) { - char *envp[5] = {NULL}; - xasprintf(&envp[0], "NETNAME=%s", netname ? : ""); - xasprintf(&envp[1], "DEVICE=%s", device ? : ""); - xasprintf(&envp[2], "INTERFACE=%s", iface ? : ""); - xasprintf(&envp[3], "NAME=%s", myname); - - execute_script("tinc-down", envp); - - for(int i = 0; i < 4; i++) - free(envp[i]); + environment_t env; + environment_init(&env); + execute_script("tinc-down", &env); + environment_exit(&env); if (devops.disable) devops.disable(); @@ -1150,7 +1137,6 @@ void close_network_connections(void) { exit_control(); - free(myname); free(scriptextension); free(scriptinterpreter); diff --git a/src/protocol_auth.c b/src/protocol_auth.c index d4705bde..31906bab 100644 --- a/src/protocol_auth.c +++ b/src/protocol_auth.c @@ -1,7 +1,7 @@ /* protocol_auth.c -- handle the meta-protocol, authentication Copyright (C) 1999-2005 Ivo Timmermans, - 2000-2016 Guus Sliepen + 2000-2017 Guus Sliepen 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 @@ -180,21 +180,18 @@ static bool finalize_invitation(connection_t *c, const char *data, uint16_t len) logger(DEBUG_CONNECTIONS, LOG_INFO, "Key succesfully received from %s (%s)", c->name, c->hostname); // Call invitation-accepted script - char *envp[7] = {NULL}; + environment_t env; char *address, *port; - xasprintf(&envp[0], "NETNAME=%s", netname ? : ""); - xasprintf(&envp[1], "DEVICE=%s", device ? : ""); - xasprintf(&envp[2], "INTERFACE=%s", iface ? : ""); - xasprintf(&envp[3], "NODE=%s", c->name); + environment_init(&env); + environment_add(&env, "NODE=%s", c->name); sockaddr2str(&c->address, &address, &port); - xasprintf(&envp[4], "REMOTEADDRESS=%s", address); - xasprintf(&envp[5], "NAME=%s", myself->name); + environment_add(&env, "REMOTEADDRESS=%s", address); + environment_add(&env, "NAME=%s", myself->name); - execute_script("invitation-accepted", envp); + execute_script("invitation-accepted", &env); - for(int i = 0; envp[i] && i < 7; i++) - free(envp[i]); + environment_exit(&env); sptps_send_record(&c->sptps, 2, data, 0); return true; diff --git a/src/script.c b/src/script.c index d4db8894..ceb6a1b4 100644 --- a/src/script.c +++ b/src/script.c @@ -1,7 +1,7 @@ /* script.c -- call an external script Copyright (C) 1999-2005 Ivo Timmermans, - 2000-2015 Guus Sliepen + 2000-2017 Guus Sliepen 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 @@ -21,6 +21,7 @@ #include "system.h" #include "conf.h" +#include "device.h" #include "logger.h" #include "names.h" #include "script.h" @@ -63,7 +64,56 @@ static void putenv(const char *p) {} static void unputenv(const char *p) {} #endif -bool execute_script(const char *name, char **envp) { +static const int min_env_size; + +int environment_add(environment_t *env, const char *format, ...) { + if(env->n >= env->size) { + env->size = env->n ? env->n * 2 : min_env_size; + env->entries = xrealloc(env->entries, env->size * sizeof *env->entries); + } + + if(format) { + va_list ap; + va_start(ap, format); + vasprintf(&env->entries[env->n], format, ap); + va_end(ap); + } else { + env->entries[env->n] = NULL; + } + + return env->n++; +} + +void environment_update(environment_t *env, int pos, const char *format, ...) { + free(env->entries[pos]); + va_list ap; + va_start(ap, format); + vasprintf(&env->entries[pos], format, ap); + va_end(ap); +} + +void environment_init(environment_t *env) { + env->n = 0; + env->size = min_env_size; + env->entries = 0; //xzalloc(env->size * sizeof *env->entries); + + if(netname) + environment_add(env, "NETNAME=%s", netname); + if(myname) + environment_add(env, "NAME=%s", myname); + if(device) + environment_add(env, "DEVICE=%s", device); + if(iface) + environment_add(env, "INTERFACE=%s", iface); +} + +void environment_exit(environment_t *env) { + for(int i = 0; i < env->n; i++) + free(env->entries[i]); + free(env->entries); +} + +bool execute_script(const char *name, environment_t *env) { char scriptname[PATH_MAX]; char *command; @@ -107,8 +157,8 @@ bool execute_script(const char *name, char **envp) { /* Set environment */ - for(int i = 0; envp[i]; i++) - putenv(envp[i]); + for(int i = 0; i < env->n; i++) + putenv(env->entries[i]); if(scriptinterpreter) xasprintf(&command, "%s \"%s\"", scriptinterpreter, scriptname); @@ -121,8 +171,8 @@ bool execute_script(const char *name, char **envp) { /* Unset environment */ - for(int i = 0; envp[i]; i++) - unputenv(envp[i]); + for(int i = 0; i < env->n; i++) + unputenv(env->entries[i]); if(status != -1) { #ifdef WEXITSTATUS diff --git a/src/script.h b/src/script.h index 446a3b95..2e26418d 100644 --- a/src/script.h +++ b/src/script.h @@ -1,7 +1,7 @@ /* script.h -- header file for script.c Copyright (C) 1999-2005 Ivo Timmermans, - 2000-2013 Guus Sliepen + 2000-2017 Guus Sliepen 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 @@ -21,6 +21,18 @@ #ifndef __TINC_SCRIPT_H__ #define __TINC_SCRIPT_H__ -extern bool execute_script(const char *, char **); +typedef struct environment { + int n; + int size; + char **entries; +} environment_t; + +extern int environment_add(environment_t *env, const char *format, ...); +extern int environment_placeholder(environment_t *env); +extern void environment_update(environment_t *env, int pos, const char *format, ...); +extern void environment_init(environment_t *env); +extern void environment_exit(environment_t *env); + +extern bool execute_script(const char *name, environment_t *env); #endif /* __TINC_SCRIPT_H__ */ diff --git a/src/subnet.c b/src/subnet.c index 090a62bf..3d055b68 100644 --- a/src/subnet.c +++ b/src/subnet.c @@ -1,6 +1,6 @@ /* subnet.c -- handle subnet lookups and lists - Copyright (C) 2000-2013 Guus Sliepen , + Copyright (C) 2000-2017 Guus Sliepen , 2000-2005 Ivo Timmermans This program is free software; you can redistribute it and/or modify @@ -206,22 +206,20 @@ void subnet_update(node_t *owner, subnet_t *subnet, bool up) { // Prepare environment variables to be passed to the script - char *envp[10] = {NULL}; - int n = 0; - xasprintf(&envp[n++], "NETNAME=%s", netname ? : ""); - xasprintf(&envp[n++], "DEVICE=%s", device ? : ""); - xasprintf(&envp[n++], "INTERFACE=%s", iface ? : ""); - xasprintf(&envp[n++], "NODE=%s", owner->name); + environment_t env; + environment_init(&env); + environment_add(&env, "NODE=%s", owner->name); if(owner != myself) { sockaddr2str(&owner->address, &address, &port); - xasprintf(&envp[n++], "REMOTEADDRESS=%s", address); - xasprintf(&envp[n++], "REMOTEPORT=%s", port); + environment_add(&env, "REMOTEADDRESS=%s", address); + environment_add(&env, "REMOTEPORT=%s", port); free(port); free(address); } - xasprintf(&envp[n++], "NAME=%s", myself->name); + int env_subnet = environment_add(&env, NULL); + int env_weight = environment_add(&env, NULL); name = up ? "subnet-up" : "subnet-down"; @@ -238,12 +236,10 @@ void subnet_update(node_t *owner, subnet_t *subnet, bool up) { weight = empty; // Prepare the SUBNET and WEIGHT variables - free(envp[n]); - free(envp[n + 1]); - xasprintf(&envp[n], "SUBNET=%s", netstr); - xasprintf(&envp[n + 1], "WEIGHT=%s", weight); + environment_update(&env, env_subnet, "SUBNET=%s", netstr); + environment_update(&env, env_weight, "WEIGHT=%s", weight); - execute_script(name, envp); + execute_script(name, &env); } } else { if(net2str(netstr, sizeof netstr, subnet)) { @@ -255,15 +251,14 @@ void subnet_update(node_t *owner, subnet_t *subnet, bool up) { weight = empty; // Prepare the SUBNET and WEIGHT variables - xasprintf(&envp[n], "SUBNET=%s", netstr); - xasprintf(&envp[n + 1], "WEIGHT=%s", weight); + environment_update(&env, env_subnet, "SUBNET=%s", netstr); + environment_update(&env, env_weight, "WEIGHT=%s", weight); - execute_script(name, envp); + execute_script(name, &env); } } - for(int i = 0; envp[i] && i < 9; i++) - free(envp[i]); + environment_exit(&env); } bool dump_subnets(connection_t *c) { diff --git a/src/tincctl.c b/src/tincctl.c index 5067ded5..2db9f235 100644 --- a/src/tincctl.c +++ b/src/tincctl.c @@ -74,6 +74,9 @@ bool netnamegiven = false; char *scriptinterpreter = NULL; char *scriptextension = ""; static char *prompt; +char *device = NULL; +char *iface = NULL; +int debug_level = -1; static struct option const long_options[] = { {"batch", no_argument, NULL, 'b'}, @@ -89,7 +92,7 @@ static struct option const long_options[] = { static void version(void) { printf("%s version %s (built %s %s, protocol %d.%d)\n", PACKAGE, BUILD_VERSION, BUILD_DATE, BUILD_TIME, PROT_MAJOR, PROT_MINOR); - printf("Copyright (C) 1998-2016 Ivo Timmermans, Guus Sliepen and others.\n" + printf("Copyright (C) 1998-2017 Ivo Timmermans, Guus Sliepen and others.\n" "See the AUTHORS file for a complete list.\n\n" "tinc comes with ABSOLUTELY NO WARRANTY. This is free software,\n" "and you are welcome to redistribute it under certain conditions;\n" From add75303e918af5e94ff545d969872799fac5cef Mon Sep 17 00:00:00 2001 From: Guus Sliepen Date: Tue, 21 Mar 2017 21:25:27 +0100 Subject: [PATCH 11/21] Add DEBUG environment variable for scripts. This contains the current debug level used by tinc. Scripts can use it to decide whether to log debugging information of their own. Closes #138 on GitHub. --- src/script.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/script.c b/src/script.c index ceb6a1b4..1ce59ea0 100644 --- a/src/script.c +++ b/src/script.c @@ -105,6 +105,8 @@ void environment_init(environment_t *env) { environment_add(env, "DEVICE=%s", device); if(iface) environment_add(env, "INTERFACE=%s", iface); + if(debug_level >= 0) + environment_add(env, "DEBUG=%d", debug_level); } void environment_exit(environment_t *env) { From ccb4fb6f7ac2efbb99d044aa072e75c035f504aa Mon Sep 17 00:00:00 2001 From: Guus Sliepen Date: Sun, 26 Mar 2017 16:46:03 +0200 Subject: [PATCH 12/21] Use unique ports for all tests. --- test/invite-tinc-up.test | 2 +- test/ns-ping.test | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/invite-tinc-up.test b/test/invite-tinc-up.test index bc5d45bf..8d637b94 100755 --- a/test/invite-tinc-up.test +++ b/test/invite-tinc-up.test @@ -8,7 +8,7 @@ $tinc $c1 < Date: Sun, 26 Mar 2017 16:46:31 +0200 Subject: [PATCH 13/21] Remove superfluous sleep command in invite-join test. --- test/invite-join.test | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/invite-join.test b/test/invite-join.test index c1bd1b8e..28de83c6 100755 --- a/test/invite-join.test +++ b/test/invite-join.test @@ -17,8 +17,6 @@ EOF # Generate an invitation and let another node join the VPN -sleep 1 - $tinc $c1 invite bar | $tinc $c2 join # Test equivalence of host config files From fd3ec60757a84b7551e03b3a48dd30f35015c448 Mon Sep 17 00:00:00 2001 From: Guus Sliepen Date: Sun, 26 Mar 2017 16:47:54 +0200 Subject: [PATCH 14/21] Add the invite-offline test. This tests generating an invitation on the server while no tinc daemon is running. --- test/Makefile.am | 1 + test/invite-offline.test | 50 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100755 test/invite-offline.test diff --git a/test/Makefile.am b/test/Makefile.am index df8e2c3b..0bd66c77 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -4,6 +4,7 @@ TESTS = \ executables.test \ import-export.test \ invite-join.test \ + invite-offline.test \ invite-tinc-up.test \ ns-ping.test \ ping.test \ diff --git a/test/invite-offline.test b/test/invite-offline.test new file mode 100755 index 00000000..8e027daa --- /dev/null +++ b/test/invite-offline.test @@ -0,0 +1,50 @@ +#!/bin/sh + +. ./testlib.sh + +# Initialize one node + +$tinc $c1 < Date: Sun, 26 Mar 2017 16:48:02 +0200 Subject: [PATCH 15/21] Update .gitignore. --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 46d528fd..15771158 100644 --- a/.gitignore +++ b/.gitignore @@ -33,6 +33,9 @@ /src/version_git.h /stamp-h1 /test-driver +/test/*.test.* +/test/*.log +/test/*.trs Makefile Makefile.in core* From 0af3dcf7a838dede699194c02444f1607644bb28 Mon Sep 17 00:00:00 2001 From: Guus Sliepen Date: Sun, 26 Mar 2017 17:43:33 +0200 Subject: [PATCH 16/21] Add the scripts test. This test whether all the scripts are run with the right information in the right order. --- test/Makefile.am | 1 + test/scripts.test | 110 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 111 insertions(+) create mode 100755 test/scripts.test diff --git a/test/Makefile.am b/test/Makefile.am index 0bd66c77..98f4a3bd 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -8,6 +8,7 @@ TESTS = \ invite-tinc-up.test \ ns-ping.test \ ping.test \ + scripts.test \ sptps-basic.test \ variables.test diff --git a/test/scripts.test b/test/scripts.test new file mode 100755 index 00000000..de69ad3a --- /dev/null +++ b/test/scripts.test @@ -0,0 +1,110 @@ +#!/bin/sh + +. ./testlib.sh + +# Initialize server node + +$tinc $c1 <$d1/$script << EOF +#!/bin/sh +echo $script \$NETNAME,\$NAME,\$DEVICE,\$IFACE,\$NODE,\$REMOTEADDRESS,\$REMOTEPORT,\$SUBNET,\$WEIGHT,\$INVITATION_FILE,\$INVITATION_URL,\$DEBUG >>$OUT +EOF +chmod u+x $d1/$script +done + +# Start server node + +$tinc -n netname $c1 start $r1 + +echo foo-started >>$OUT + +# Invite client node + +url=`$tinc -n netname2 $c1 invite bar` +file=`cd $d1/invitations; ls | grep -v ed25519_key.priv` +echo bar-invited >>$OUT +$tinc -n netname3 $c2 join $url +echo bar-joined >>$OUT + +# Start and stop client node + +$tinc $c2 << EOF +set DeviceType dummy +set Port 32760 +add Subnet 10.0.0.2 +add Subnet fec0::/64#5 +start $r2 +EOF + +sleep 1 + +echo bar-started >>$OUT + +$tinc $c1 debug 4 +$tinc $c2 stop + +sleep 1 + +echo bar-stopped >>$OUT + +$tinc $c1 debug 5 +$tinc $c2 start $r2 + +sleep 1 + +echo bar-started >>$OUT + +# Stop server node + +$tinc $c1 stop + +# Check if the script output is what is expected + +cat >$OUT.expected << EOF +tinc-up netname,foo,dummy,,,,,,,,,5 +subnet-up netname,foo,dummy,,foo,,,10.0.0.1,,,,5 +subnet-up netname,foo,dummy,,foo,,,fec0::/64,,,,5 +foo-started +invitation-created netname2,foo,,,bar,,,,,$d1/invitations/$file,$url, +bar-invited +invitation-accepted netname,foo,dummy,,bar,127.0.0.1,,,,,,5 +bar-joined +host-up netname,foo,dummy,,bar,127.0.0.1,32760,,,,,5 +hosts/bar-up netname,foo,dummy,,bar,127.0.0.1,32760,,,,,5 +subnet-up netname,foo,dummy,,bar,127.0.0.1,32760,10.0.0.2,,,,5 +subnet-up netname,foo,dummy,,bar,127.0.0.1,32760,fec0::/64,5,,,5 +bar-started +host-down netname,foo,dummy,,bar,127.0.0.1,32760,,,,,4 +hosts/bar-down netname,foo,dummy,,bar,127.0.0.1,32760,,,,,4 +subnet-down netname,foo,dummy,,bar,127.0.0.1,32760,10.0.0.2,,,,4 +subnet-down netname,foo,dummy,,bar,127.0.0.1,32760,fec0::/64,5,,,4 +bar-stopped +host-up netname,foo,dummy,,bar,127.0.0.1,32760,,,,,5 +hosts/bar-up netname,foo,dummy,,bar,127.0.0.1,32760,,,,,5 +subnet-up netname,foo,dummy,,bar,127.0.0.1,32760,10.0.0.2,,,,5 +subnet-up netname,foo,dummy,,bar,127.0.0.1,32760,fec0::/64,5,,,5 +bar-started +host-down netname,foo,dummy,,bar,127.0.0.1,32760,,,,,5 +hosts/bar-down netname,foo,dummy,,bar,127.0.0.1,32760,,,,,5 +subnet-down netname,foo,dummy,,bar,127.0.0.1,32760,10.0.0.2,,,,5 +subnet-down netname,foo,dummy,,bar,127.0.0.1,32760,fec0::/64,5,,,5 +subnet-down netname,foo,dummy,,foo,,,10.0.0.1,,,,5 +subnet-down netname,foo,dummy,,foo,,,fec0::/64,,,,5 +tinc-down netname,foo,dummy,,,,,,,,,5 +EOF + +cmp $OUT $OUT.expected From 3ab1893a4b6c2895075ac889cf06c511e2001a9e Mon Sep 17 00:00:00 2001 From: Guus Sliepen Date: Sun, 26 Mar 2017 17:54:37 +0200 Subject: [PATCH 17/21] Ensure proper logging in the invite-offline test. --- test/invite-offline.test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/invite-offline.test b/test/invite-offline.test index 8e027daa..b4354938 100755 --- a/test/invite-offline.test +++ b/test/invite-offline.test @@ -18,7 +18,7 @@ EOF invitation=`$tinc $c1 invite bar` -$tinc $c1 start +$tinc $c1 start $r1 $tinc $c2 join $invitation From d9a7f2d1054a7155b5f23855ba28dd98e0df73be Mon Sep 17 00:00:00 2001 From: Guus Sliepen Date: Wed, 29 Mar 2017 08:08:19 +0200 Subject: [PATCH 18/21] Use 127.0.0.1 instead of localhost to ensure tests are reproducible. --- test/scripts.test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/scripts.test b/test/scripts.test index de69ad3a..6fe616f8 100755 --- a/test/scripts.test +++ b/test/scripts.test @@ -8,7 +8,7 @@ $tinc $c1 < Date: Wed, 29 Mar 2017 08:08:56 +0200 Subject: [PATCH 19/21] Ensure sptps_keypair and sptps_test get build for make check. --- src/Makefile.am | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Makefile.am b/src/Makefile.am index 794e4202..8e2aa038 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,6 +1,7 @@ ## Produce this file with automake to get Makefile.in sbin_PROGRAMS = tincd tinc +check_PROGRAMS = sptps_test sptps_keypair EXTRA_PROGRAMS = sptps_test sptps_keypair CLEANFILES = version_git.h From 6011197be5cdb18aa79713990d6a1887b9261d12 Mon Sep 17 00:00:00 2001 From: Guus Sliepen Date: Sat, 8 Apr 2017 13:31:04 +0200 Subject: [PATCH 20/21] Use /dev/udp instead of /dev/ip on Solaris. # Conflicts: # src/solaris/device.c --- src/solaris/device.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/solaris/device.c b/src/solaris/device.c index fadae573..53d3d63c 100644 --- a/src/solaris/device.c +++ b/src/solaris/device.c @@ -41,6 +41,7 @@ #define DEFAULT_TUN_DEVICE "/dev/tun" #define DEFAULT_TAP_DEVICE "/dev/tap" +#define IP_DEVICE "/dev/udp" static enum { DEVICE_TYPE_TUN, @@ -84,8 +85,8 @@ static bool setup_device(void) { /* The following is black magic copied from OpenVPN. */ - if((ip_fd = open("/dev/ip", O_RDWR, 0)) < 0) { - logger(DEBUG_ALWAYS, LOG_ERR, "Could not open %s: %s\n", "/dev/ip", strerror(errno)); + if((ip_fd = open(IP_DEVICE, O_RDWR, 0)) < 0) { + logger(DEBUG_ALWAYS, LOG_ERR, "Could not open %s: %s\n", IP_DEVICE, strerror(errno)); return false; } @@ -203,7 +204,7 @@ static bool setup_device(void) { /* Push arp module to ip_fd */ if(ioctl(ip_fd, I_PUSH, "arp") < 0) { - logger(DEBUG_ALWAYS, LOG_ERR, "Could not push ARP module onto %s!", "/dev/ip"); + logger(DEBUG_ALWAYS, LOG_ERR, "Could not push ARP module onto %s!", IP_DEVICE); return false; } From 95f09569beb2e304e6a2112d20cee6fab88f3729 Mon Sep 17 00:00:00 2001 From: Guus Sliepen Date: Sat, 8 Apr 2017 13:34:40 +0200 Subject: [PATCH 21/21] Use getmsg()/putmsg() instead of read()/write() on Solaris. This fixes a problem where read() returns packets from the IP layer before fragmentation is done. # Conflicts: # src/solaris/device.c --- src/solaris/device.c | 37 +++++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/src/solaris/device.c b/src/solaris/device.c index 53d3d63c..89481c7b 100644 --- a/src/solaris/device.c +++ b/src/solaris/device.c @@ -24,6 +24,7 @@ #include #include +#include #include "../conf.h" #include "../device.h" @@ -296,11 +297,16 @@ static void close_device(void) { } static bool read_packet(vpn_packet_t *packet) { - int inlen; + int result; + struct strbuf sbuf; + int f = 0; switch(device_type) { case DEVICE_TYPE_TUN: - if((inlen = read(device_fd, DATA(packet) + 14, MTU - 14)) <= 0) { + sbuf.maxlen = MTU - 14; + sbuf.buf = (char *)DATA(packet) + 14; + + if((result = getmsg(device_fd, NULL, &sbuf, &f)) < 0) { logger(DEBUG_ALWAYS, LOG_ERR, "Error while reading from %s %s: %s", device_info, device, strerror(errno)); return false; } @@ -320,16 +326,19 @@ static bool read_packet(vpn_packet_t *packet) { } memset(DATA(packet), 0, 12); - packet->len = inlen + 14; + packet->len = sbuf.len + 14; break; case DEVICE_TYPE_TAP: - if((inlen = read(device_fd, DATA(packet), MTU)) <= 0) { - logger(DEBUG_ALWAYS, LOG_ERR, "Error while reading from %s %s: %s", device_info, device, strerror(errno)); + sbuf.maxlen = MTU; + sbuf.buf = (char *)DATA(packet); + + if((result = getmsg(device_fd, NULL, &sbuf, &f)) < 0) { + logger(LOG_ERR, "Error while reading from %s %s: %s", device_info, device, strerror(errno)); return false; } - packet->len = inlen + 14; + packet->len = sbuf.len; break; default: @@ -344,17 +353,25 @@ static bool read_packet(vpn_packet_t *packet) { static bool write_packet(vpn_packet_t *packet) { logger(DEBUG_TRAFFIC, LOG_DEBUG, "Writing packet of %d bytes to %s", packet->len, device_info); + struct strbuf sbuf; + switch(device_type) { case DEVICE_TYPE_TUN: - if(write(device_fd, DATA(packet) + 14, packet->len - 14) < 0) { - logger(DEBUG_ALWAYS, LOG_ERR, "Can't write to %s %s: %s", device_info, device, strerror(errno)); + sbuf.len = packet->len - 14; + sbuf.buf = (char *)DATA(packet) + 14; + + if(putmsg(device_fd, NULL, &sbuf, 0) < 0) { + logger(LOG_ERR, "Can't write to %s %s: %s", device_info, device, strerror(errno)); return false; } break; case DEVICE_TYPE_TAP: - if(write(device_fd, DATA(packet), packet->len) < 0) { - logger(DEBUG_ALWAYS, LOG_ERR, "Can't write to %s %s: %s", device_info, device, strerror(errno)); + sbuf.len = packet->len; + sbuf.buf = (char *)DATA(packet); + + if(putmsg(device_fd, NULL, &sbuf, 0) < 0) { + logger(LOG_ERR, "Can't write to %s %s: %s", device_info, device, strerror(errno)); return false; } break;