/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ /*** This file is part of systemd. Copyright 2014 Susant Sahani <susant@redhat.com> systemd is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. systemd 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ #include <sys/ioctl.h> #include <net/if.h> #include <linux/if_tun.h> #include "networkd-netdev-tuntap.h" #define TUN_DEV "/dev/net/tun" static int netdev_fill_tuntap_message(NetDev *netdev, struct ifreq *ifr) { TunTap *t; assert(netdev); assert(netdev->ifname); assert(ifr); if (netdev->kind == NETDEV_KIND_TAP) { t = TAP(netdev); ifr->ifr_flags |= IFF_TAP; } else { t = TUN(netdev); ifr->ifr_flags |= IFF_TUN; } if (!t->packet_info) ifr->ifr_flags |= IFF_NO_PI; if (t->one_queue) ifr->ifr_flags |= IFF_ONE_QUEUE; if (t->multi_queue) ifr->ifr_flags |= IFF_MULTI_QUEUE; strncpy(ifr->ifr_name, netdev->ifname, IFNAMSIZ-1); return 0; } static int netdev_tuntap_add(NetDev *netdev, struct ifreq *ifr) { _cleanup_close_ int fd; TunTap *t = NULL; const char *user; const char *group; uid_t uid; gid_t gid; int r = 0; assert(netdev); assert(ifr); fd = open(TUN_DEV, O_RDWR); if (fd < 0) { log_error_netdev(netdev, "Failed to open tun dev: %s", strerror(-r)); return r; } r = ioctl(fd, TUNSETIFF, ifr); if (r < 0) { log_error_netdev(netdev, "TUNSETIFF failed on tun dev: %s", strerror(-r)); return r; } if (netdev->kind == NETDEV_KIND_TAP) t = TAP(netdev); else t = TUN(netdev); assert(t); if(t->user_name) { user = t->user_name; r = get_user_creds(&user, &uid, NULL, NULL, NULL); if (r < 0) { log_error("Cannot resolve user name %s: %s", t->user_name, strerror(-r)); return 0; } r = ioctl(fd, TUNSETOWNER, uid); if ( r < 0) { log_error_netdev(netdev, "TUNSETOWNER failed on tun dev: %s", strerror(-r)); } } if(t->group_name) { group = t->group_name; r = get_group_creds(&group, &gid); if (r < 0) { log_error("Cannot resolve group name %s: %s", t->group_name, strerror(-r)); return 0; } r = ioctl(fd, TUNSETGROUP, gid); if( r < 0) { log_error_netdev(netdev, "TUNSETGROUP failed on tun dev: %s", strerror(-r)); return r; } } r = ioctl(fd, TUNSETPERSIST, 1); if (r < 0) { log_error_netdev(netdev, "TUNSETPERSIST failed on tun dev: %s", strerror(-r)); return r; } return r; } static int netdev_create_tuntap(NetDev *netdev) { struct ifreq ifr = {}; int r; r = netdev_fill_tuntap_message(netdev, &ifr); if(r < 0) return r; return netdev_tuntap_add(netdev, &ifr); } static void tuntap_done(NetDev *netdev) { TunTap *t = NULL; assert(netdev); if (netdev->kind == NETDEV_KIND_TUN) t = TUN(netdev); else t = TAP(netdev); assert(t); free(t->user_name); t->user_name = NULL; free(t->group_name); t->group_name = NULL; } const NetDevVTable tun_vtable = { .object_size = sizeof(TunTap), .sections = "Match\0NetDev\0Tun\0", .done = tuntap_done, .create = netdev_create_tuntap, .create_type = NETDEV_CREATE_INDEPENDENT, }; const NetDevVTable tap_vtable = { .object_size = sizeof(TunTap), .sections = "Match\0NetDev\0Tap\0", .done = tuntap_done, .create = netdev_create_tuntap, .create_type = NETDEV_CREATE_INDEPENDENT, };