18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <stdio.h> 78c2ecf20Sopenharmony_ci#include <unistd.h> 88c2ecf20Sopenharmony_ci#include <errno.h> 98c2ecf20Sopenharmony_ci#include <string.h> 108c2ecf20Sopenharmony_ci#include <linux/if_tun.h> 118c2ecf20Sopenharmony_ci#include <net/if.h> 128c2ecf20Sopenharmony_ci#include <sys/ioctl.h> 138c2ecf20Sopenharmony_ci#include <sys/socket.h> 148c2ecf20Sopenharmony_ci#include <sys/wait.h> 158c2ecf20Sopenharmony_ci#include <sys/uio.h> 168c2ecf20Sopenharmony_ci#include <kern_util.h> 178c2ecf20Sopenharmony_ci#include <os.h> 188c2ecf20Sopenharmony_ci#include "tuntap.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistatic int tuntap_user_init(void *data, void *dev) 218c2ecf20Sopenharmony_ci{ 228c2ecf20Sopenharmony_ci struct tuntap_data *pri = data; 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci pri->dev = dev; 258c2ecf20Sopenharmony_ci return 0; 268c2ecf20Sopenharmony_ci} 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistatic void tuntap_add_addr(unsigned char *addr, unsigned char *netmask, 298c2ecf20Sopenharmony_ci void *data) 308c2ecf20Sopenharmony_ci{ 318c2ecf20Sopenharmony_ci struct tuntap_data *pri = data; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci tap_check_ips(pri->gate_addr, addr); 348c2ecf20Sopenharmony_ci if ((pri->fd == -1) || pri->fixed_config) 358c2ecf20Sopenharmony_ci return; 368c2ecf20Sopenharmony_ci open_addr(addr, netmask, pri->dev_name); 378c2ecf20Sopenharmony_ci} 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistatic void tuntap_del_addr(unsigned char *addr, unsigned char *netmask, 408c2ecf20Sopenharmony_ci void *data) 418c2ecf20Sopenharmony_ci{ 428c2ecf20Sopenharmony_ci struct tuntap_data *pri = data; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci if ((pri->fd == -1) || pri->fixed_config) 458c2ecf20Sopenharmony_ci return; 468c2ecf20Sopenharmony_ci close_addr(addr, netmask, pri->dev_name); 478c2ecf20Sopenharmony_ci} 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistruct tuntap_pre_exec_data { 508c2ecf20Sopenharmony_ci int stdout_fd; 518c2ecf20Sopenharmony_ci int close_me; 528c2ecf20Sopenharmony_ci}; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistatic void tuntap_pre_exec(void *arg) 558c2ecf20Sopenharmony_ci{ 568c2ecf20Sopenharmony_ci struct tuntap_pre_exec_data *data = arg; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci dup2(data->stdout_fd, 1); 598c2ecf20Sopenharmony_ci close(data->close_me); 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic int tuntap_open_tramp(char *gate, int *fd_out, int me, int remote, 638c2ecf20Sopenharmony_ci char *buffer, int buffer_len, int *used_out) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci struct tuntap_pre_exec_data data; 668c2ecf20Sopenharmony_ci char version_buf[sizeof("nnnnn\0")]; 678c2ecf20Sopenharmony_ci char *argv[] = { "uml_net", version_buf, "tuntap", "up", gate, 688c2ecf20Sopenharmony_ci NULL }; 698c2ecf20Sopenharmony_ci char buf[CMSG_SPACE(sizeof(*fd_out))]; 708c2ecf20Sopenharmony_ci struct msghdr msg; 718c2ecf20Sopenharmony_ci struct cmsghdr *cmsg; 728c2ecf20Sopenharmony_ci struct iovec iov; 738c2ecf20Sopenharmony_ci int pid, n, err; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci sprintf(version_buf, "%d", UML_NET_VERSION); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci data.stdout_fd = remote; 788c2ecf20Sopenharmony_ci data.close_me = me; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci pid = run_helper(tuntap_pre_exec, &data, argv); 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci if (pid < 0) 838c2ecf20Sopenharmony_ci return pid; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci close(remote); 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci msg.msg_name = NULL; 888c2ecf20Sopenharmony_ci msg.msg_namelen = 0; 898c2ecf20Sopenharmony_ci if (buffer != NULL) { 908c2ecf20Sopenharmony_ci iov = ((struct iovec) { buffer, buffer_len }); 918c2ecf20Sopenharmony_ci msg.msg_iov = &iov; 928c2ecf20Sopenharmony_ci msg.msg_iovlen = 1; 938c2ecf20Sopenharmony_ci } 948c2ecf20Sopenharmony_ci else { 958c2ecf20Sopenharmony_ci msg.msg_iov = NULL; 968c2ecf20Sopenharmony_ci msg.msg_iovlen = 0; 978c2ecf20Sopenharmony_ci } 988c2ecf20Sopenharmony_ci msg.msg_control = buf; 998c2ecf20Sopenharmony_ci msg.msg_controllen = sizeof(buf); 1008c2ecf20Sopenharmony_ci msg.msg_flags = 0; 1018c2ecf20Sopenharmony_ci n = recvmsg(me, &msg, 0); 1028c2ecf20Sopenharmony_ci *used_out = n; 1038c2ecf20Sopenharmony_ci if (n < 0) { 1048c2ecf20Sopenharmony_ci err = -errno; 1058c2ecf20Sopenharmony_ci printk(UM_KERN_ERR "tuntap_open_tramp : recvmsg failed - " 1068c2ecf20Sopenharmony_ci "errno = %d\n", errno); 1078c2ecf20Sopenharmony_ci return err; 1088c2ecf20Sopenharmony_ci } 1098c2ecf20Sopenharmony_ci helper_wait(pid); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci cmsg = CMSG_FIRSTHDR(&msg); 1128c2ecf20Sopenharmony_ci if (cmsg == NULL) { 1138c2ecf20Sopenharmony_ci printk(UM_KERN_ERR "tuntap_open_tramp : didn't receive a " 1148c2ecf20Sopenharmony_ci "message\n"); 1158c2ecf20Sopenharmony_ci return -EINVAL; 1168c2ecf20Sopenharmony_ci } 1178c2ecf20Sopenharmony_ci if ((cmsg->cmsg_level != SOL_SOCKET) || 1188c2ecf20Sopenharmony_ci (cmsg->cmsg_type != SCM_RIGHTS)) { 1198c2ecf20Sopenharmony_ci printk(UM_KERN_ERR "tuntap_open_tramp : didn't receive a " 1208c2ecf20Sopenharmony_ci "descriptor\n"); 1218c2ecf20Sopenharmony_ci return -EINVAL; 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci *fd_out = ((int *) CMSG_DATA(cmsg))[0]; 1248c2ecf20Sopenharmony_ci os_set_exec_close(*fd_out); 1258c2ecf20Sopenharmony_ci return 0; 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic int tuntap_open(void *data) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci struct ifreq ifr; 1318c2ecf20Sopenharmony_ci struct tuntap_data *pri = data; 1328c2ecf20Sopenharmony_ci char *output, *buffer; 1338c2ecf20Sopenharmony_ci int err, fds[2], len, used; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci err = tap_open_common(pri->dev, pri->gate_addr); 1368c2ecf20Sopenharmony_ci if (err < 0) 1378c2ecf20Sopenharmony_ci return err; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci if (pri->fixed_config) { 1408c2ecf20Sopenharmony_ci pri->fd = os_open_file("/dev/net/tun", 1418c2ecf20Sopenharmony_ci of_cloexec(of_rdwr(OPENFLAGS())), 0); 1428c2ecf20Sopenharmony_ci if (pri->fd < 0) { 1438c2ecf20Sopenharmony_ci printk(UM_KERN_ERR "Failed to open /dev/net/tun, " 1448c2ecf20Sopenharmony_ci "err = %d\n", -pri->fd); 1458c2ecf20Sopenharmony_ci return pri->fd; 1468c2ecf20Sopenharmony_ci } 1478c2ecf20Sopenharmony_ci memset(&ifr, 0, sizeof(ifr)); 1488c2ecf20Sopenharmony_ci ifr.ifr_flags = IFF_TAP | IFF_NO_PI; 1498c2ecf20Sopenharmony_ci strlcpy(ifr.ifr_name, pri->dev_name, sizeof(ifr.ifr_name)); 1508c2ecf20Sopenharmony_ci if (ioctl(pri->fd, TUNSETIFF, &ifr) < 0) { 1518c2ecf20Sopenharmony_ci err = -errno; 1528c2ecf20Sopenharmony_ci printk(UM_KERN_ERR "TUNSETIFF failed, errno = %d\n", 1538c2ecf20Sopenharmony_ci errno); 1548c2ecf20Sopenharmony_ci close(pri->fd); 1558c2ecf20Sopenharmony_ci return err; 1568c2ecf20Sopenharmony_ci } 1578c2ecf20Sopenharmony_ci } 1588c2ecf20Sopenharmony_ci else { 1598c2ecf20Sopenharmony_ci err = socketpair(AF_UNIX, SOCK_DGRAM, 0, fds); 1608c2ecf20Sopenharmony_ci if (err) { 1618c2ecf20Sopenharmony_ci err = -errno; 1628c2ecf20Sopenharmony_ci printk(UM_KERN_ERR "tuntap_open : socketpair failed - " 1638c2ecf20Sopenharmony_ci "errno = %d\n", errno); 1648c2ecf20Sopenharmony_ci return err; 1658c2ecf20Sopenharmony_ci } 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci buffer = get_output_buffer(&len); 1688c2ecf20Sopenharmony_ci if (buffer != NULL) 1698c2ecf20Sopenharmony_ci len--; 1708c2ecf20Sopenharmony_ci used = 0; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci err = tuntap_open_tramp(pri->gate_addr, &pri->fd, fds[0], 1738c2ecf20Sopenharmony_ci fds[1], buffer, len, &used); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci output = buffer; 1768c2ecf20Sopenharmony_ci if (err < 0) { 1778c2ecf20Sopenharmony_ci printk("%s", output); 1788c2ecf20Sopenharmony_ci free_output_buffer(buffer); 1798c2ecf20Sopenharmony_ci printk(UM_KERN_ERR "tuntap_open_tramp failed - " 1808c2ecf20Sopenharmony_ci "err = %d\n", -err); 1818c2ecf20Sopenharmony_ci return err; 1828c2ecf20Sopenharmony_ci } 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci pri->dev_name = uml_strdup(buffer); 1858c2ecf20Sopenharmony_ci output += IFNAMSIZ; 1868c2ecf20Sopenharmony_ci printk("%s", output); 1878c2ecf20Sopenharmony_ci free_output_buffer(buffer); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci close(fds[0]); 1908c2ecf20Sopenharmony_ci iter_addresses(pri->dev, open_addr, pri->dev_name); 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci return pri->fd; 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cistatic void tuntap_close(int fd, void *data) 1978c2ecf20Sopenharmony_ci{ 1988c2ecf20Sopenharmony_ci struct tuntap_data *pri = data; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci if (!pri->fixed_config) 2018c2ecf20Sopenharmony_ci iter_addresses(pri->dev, close_addr, pri->dev_name); 2028c2ecf20Sopenharmony_ci close(fd); 2038c2ecf20Sopenharmony_ci pri->fd = -1; 2048c2ecf20Sopenharmony_ci} 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ciconst struct net_user_info tuntap_user_info = { 2078c2ecf20Sopenharmony_ci .init = tuntap_user_init, 2088c2ecf20Sopenharmony_ci .open = tuntap_open, 2098c2ecf20Sopenharmony_ci .close = tuntap_close, 2108c2ecf20Sopenharmony_ci .remove = NULL, 2118c2ecf20Sopenharmony_ci .add_address = tuntap_add_addr, 2128c2ecf20Sopenharmony_ci .delete_address = tuntap_del_addr, 2138c2ecf20Sopenharmony_ci .mtu = ETH_MAX_PACKET, 2148c2ecf20Sopenharmony_ci .max_packet = ETH_MAX_PACKET + ETH_HEADER_OTHER, 2158c2ecf20Sopenharmony_ci}; 216