X-Git-Url: https://tinc-vpn.org/git/browse?a=blobdiff_plain;f=src%2Ffd_device.c;h=1325ee5a1f577c96d74f72dcc668d368e79e12f5;hb=f71ab70351e8d5dfdc91c9808d142cf93d2a5d61;hp=afe59bc2744b52356b8258c52e42c670b7030362;hpb=f6e87ab476a0faf8b124ecaaa27f967d825e6457;p=tinc diff --git a/src/fd_device.c b/src/fd_device.c index afe59bc2..1325ee5a 100644 --- a/src/fd_device.c +++ b/src/fd_device.c @@ -1,9 +1,9 @@ /* fd_device.c -- Interaction with Android tun fd Copyright (C) 2001-2005 Ivo Timmermans, - 2001-2016 Guus Sliepen + 2001-2021 Guus Sliepen 2009 Grzegorz Dymarek - 2016 Pacien TRAN-GIRARD + 2016-2020 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 @@ -21,6 +21,10 @@ */ #include "system.h" + +#ifdef HAVE_SYS_UN_H +#include + #include "conf.h" #include "device.h" #include "ethernet.h" @@ -29,25 +33,135 @@ #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; +struct unix_socket_addr { + size_t size; + struct sockaddr_un addr; +}; + +static int read_fd(int socket) { + char iobuf; + struct iovec iov = {0}; + char cmsgbuf[CMSG_SPACE(sizeof(device_fd))]; + struct msghdr msg = {0}; + int ret; + struct cmsghdr *cmsgptr; + + iov.iov_base = &iobuf; + iov.iov_len = 1; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = cmsgbuf; + msg.msg_controllen = sizeof(cmsgbuf); + + if((ret = recvmsg(socket, &msg, 0)) < 1) { + logger(DEBUG_ALWAYS, LOG_ERR, "Could not read from unix socket (error %d)!", ret); + return -1; } - if(!get_config_int(lookup_config(config_tree, "Device"), &device_fd)) { - logger(DEBUG_ALWAYS, LOG_ERR, "Could not read fd from configuration!"); - return false; +#ifdef IP_RECVERR + + if(msg.msg_flags & (MSG_CTRUNC | MSG_OOB | MSG_ERRQUEUE)) { +#else + + if(msg.msg_flags & (MSG_CTRUNC | MSG_OOB)) { +#endif + logger(DEBUG_ALWAYS, LOG_ERR, "Error while receiving message (flags %d)!", msg.msg_flags); + return -1; } - return true; + cmsgptr = CMSG_FIRSTHDR(&msg); + + if(cmsgptr->cmsg_level != SOL_SOCKET) { + logger(DEBUG_ALWAYS, LOG_ERR, "Wrong CMSG level: %d, expected %d!", + cmsgptr->cmsg_level, SOL_SOCKET); + return -1; + } + + if(cmsgptr->cmsg_type != SCM_RIGHTS) { + logger(DEBUG_ALWAYS, LOG_ERR, "Wrong CMSG type: %d, expected %d!", + cmsgptr->cmsg_type, SCM_RIGHTS); + return -1; + } + + if(cmsgptr->cmsg_len != CMSG_LEN(sizeof(device_fd))) { + logger(DEBUG_ALWAYS, LOG_ERR, "Wrong CMSG data length: %lu, expected %lu!", + (unsigned long)cmsgptr->cmsg_len, (unsigned long)CMSG_LEN(sizeof(device_fd))); + return -1; + } + + return *(int *) CMSG_DATA(cmsgptr); +} + +static int receive_fd(struct unix_socket_addr socket_addr) { + int socketfd; + int ret; + int result; + + if((socketfd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) { + logger(DEBUG_ALWAYS, LOG_ERR, "Could not open stream socket (error %d)!", socketfd); + return -1; + } + + if((ret = connect(socketfd, (struct sockaddr *) &socket_addr.addr, socket_addr.size)) < 0) { + logger(DEBUG_ALWAYS, LOG_ERR, "Could not connect to Unix socket (error %d)!", ret); + result = -1; + goto end; + } + + result = read_fd(socketfd); + +end: + close(socketfd); + return result; +} + +static struct unix_socket_addr parse_socket_addr(const char *path) { + struct sockaddr_un socket_addr = { + .sun_family = AF_UNIX, + }; + size_t path_length; + + if(strlen(path) >= sizeof(socket_addr.sun_path)) { + logger(DEBUG_ALWAYS, LOG_ERR, "Unix socket path too long!"); + return (struct unix_socket_addr) { + 0 + }; + } + + strncpy(socket_addr.sun_path, path, sizeof(socket_addr.sun_path)); + + if(path[0] == '@') { + /* abstract namespace socket */ + socket_addr.sun_path[0] = '\0'; + path_length = strlen(path); + } else { + /* filesystem path with NUL terminator */ + path_length = strlen(path) + 1; + } + + return (struct unix_socket_addr) { + .size = offsetof(struct sockaddr_un, sun_path) + path_length, + .addr = socket_addr + }; } static bool setup_device(void) { - if(!check_config()) { + if(routing_mode == RMODE_SWITCH) { + logger(DEBUG_ALWAYS, LOG_ERR, "Switch mode not supported (requires unsupported TAP device)!"); return false; } + if(!get_config_string(lookup_config(config_tree, "Device"), &device)) { + logger(DEBUG_ALWAYS, LOG_ERR, "Could not read device from configuration!"); + return false; + } + + /* device is either directly a file descriptor or an unix socket to read it from */ + if(sscanf(device, "%d", &device_fd) != 1) { + logger(DEBUG_ALWAYS, LOG_INFO, "Receiving fd from Unix socket at %s.", device); + device_fd = receive_fd(parse_socket_addr(device)); + } + if(device_fd < 0) { logger(DEBUG_ALWAYS, LOG_ERR, "Could not open %s: %s!", device, strerror(errno)); return false; @@ -61,6 +175,10 @@ static bool setup_device(void) { static void close_device(void) { close(device_fd); device_fd = -1; + free(iface); + iface = NULL; + free(device); + device = NULL; } static inline uint16_t get_ip_ethertype(vpn_packet_t *packet) { @@ -123,3 +241,4 @@ const devops_t fd_devops = { .read = read_packet, .write = write_packet, }; +#endif