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 * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) and 58c2ecf20Sopenharmony_ci * James Leu (jleu@mindspring.net). 68c2ecf20Sopenharmony_ci * Copyright (C) 2001 by various other people who didn't put their name here. 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <stdio.h> 108c2ecf20Sopenharmony_ci#include <unistd.h> 118c2ecf20Sopenharmony_ci#include <errno.h> 128c2ecf20Sopenharmony_ci#include <string.h> 138c2ecf20Sopenharmony_ci#include <sys/socket.h> 148c2ecf20Sopenharmony_ci#include <sys/wait.h> 158c2ecf20Sopenharmony_ci#include "etap.h" 168c2ecf20Sopenharmony_ci#include <os.h> 178c2ecf20Sopenharmony_ci#include <net_user.h> 188c2ecf20Sopenharmony_ci#include <um_malloc.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#define MAX_PACKET ETH_MAX_PACKET 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistatic int etap_user_init(void *data, void *dev) 238c2ecf20Sopenharmony_ci{ 248c2ecf20Sopenharmony_ci struct ethertap_data *pri = data; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci pri->dev = dev; 278c2ecf20Sopenharmony_ci return 0; 288c2ecf20Sopenharmony_ci} 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistruct addr_change { 318c2ecf20Sopenharmony_ci enum { ADD_ADDR, DEL_ADDR } what; 328c2ecf20Sopenharmony_ci unsigned char addr[4]; 338c2ecf20Sopenharmony_ci unsigned char netmask[4]; 348c2ecf20Sopenharmony_ci}; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic void etap_change(int op, unsigned char *addr, unsigned char *netmask, 378c2ecf20Sopenharmony_ci int fd) 388c2ecf20Sopenharmony_ci{ 398c2ecf20Sopenharmony_ci struct addr_change change; 408c2ecf20Sopenharmony_ci char *output; 418c2ecf20Sopenharmony_ci int n; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci change.what = op; 448c2ecf20Sopenharmony_ci memcpy(change.addr, addr, sizeof(change.addr)); 458c2ecf20Sopenharmony_ci memcpy(change.netmask, netmask, sizeof(change.netmask)); 468c2ecf20Sopenharmony_ci CATCH_EINTR(n = write(fd, &change, sizeof(change))); 478c2ecf20Sopenharmony_ci if (n != sizeof(change)) { 488c2ecf20Sopenharmony_ci printk(UM_KERN_ERR "etap_change - request failed, err = %d\n", 498c2ecf20Sopenharmony_ci errno); 508c2ecf20Sopenharmony_ci return; 518c2ecf20Sopenharmony_ci } 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci output = uml_kmalloc(UM_KERN_PAGE_SIZE, UM_GFP_KERNEL); 548c2ecf20Sopenharmony_ci if (output == NULL) 558c2ecf20Sopenharmony_ci printk(UM_KERN_ERR "etap_change : Failed to allocate output " 568c2ecf20Sopenharmony_ci "buffer\n"); 578c2ecf20Sopenharmony_ci read_output(fd, output, UM_KERN_PAGE_SIZE); 588c2ecf20Sopenharmony_ci if (output != NULL) { 598c2ecf20Sopenharmony_ci printk("%s", output); 608c2ecf20Sopenharmony_ci kfree(output); 618c2ecf20Sopenharmony_ci } 628c2ecf20Sopenharmony_ci} 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic void etap_open_addr(unsigned char *addr, unsigned char *netmask, 658c2ecf20Sopenharmony_ci void *arg) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci etap_change(ADD_ADDR, addr, netmask, *((int *) arg)); 688c2ecf20Sopenharmony_ci} 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic void etap_close_addr(unsigned char *addr, unsigned char *netmask, 718c2ecf20Sopenharmony_ci void *arg) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci etap_change(DEL_ADDR, addr, netmask, *((int *) arg)); 748c2ecf20Sopenharmony_ci} 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistruct etap_pre_exec_data { 778c2ecf20Sopenharmony_ci int control_remote; 788c2ecf20Sopenharmony_ci int control_me; 798c2ecf20Sopenharmony_ci int data_me; 808c2ecf20Sopenharmony_ci}; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic void etap_pre_exec(void *arg) 838c2ecf20Sopenharmony_ci{ 848c2ecf20Sopenharmony_ci struct etap_pre_exec_data *data = arg; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci dup2(data->control_remote, 1); 878c2ecf20Sopenharmony_ci close(data->data_me); 888c2ecf20Sopenharmony_ci close(data->control_me); 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistatic int etap_tramp(char *dev, char *gate, int control_me, 928c2ecf20Sopenharmony_ci int control_remote, int data_me, int data_remote) 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci struct etap_pre_exec_data pe_data; 958c2ecf20Sopenharmony_ci int pid, err, n; 968c2ecf20Sopenharmony_ci char version_buf[sizeof("nnnnn\0")]; 978c2ecf20Sopenharmony_ci char data_fd_buf[sizeof("nnnnnn\0")]; 988c2ecf20Sopenharmony_ci char gate_buf[sizeof("nnn.nnn.nnn.nnn\0")]; 998c2ecf20Sopenharmony_ci char *setup_args[] = { "uml_net", version_buf, "ethertap", dev, 1008c2ecf20Sopenharmony_ci data_fd_buf, gate_buf, NULL }; 1018c2ecf20Sopenharmony_ci char *nosetup_args[] = { "uml_net", version_buf, "ethertap", 1028c2ecf20Sopenharmony_ci dev, data_fd_buf, NULL }; 1038c2ecf20Sopenharmony_ci char **args, c; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci sprintf(data_fd_buf, "%d", data_remote); 1068c2ecf20Sopenharmony_ci sprintf(version_buf, "%d", UML_NET_VERSION); 1078c2ecf20Sopenharmony_ci if (gate != NULL) { 1088c2ecf20Sopenharmony_ci strncpy(gate_buf, gate, 15); 1098c2ecf20Sopenharmony_ci args = setup_args; 1108c2ecf20Sopenharmony_ci } 1118c2ecf20Sopenharmony_ci else args = nosetup_args; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci err = 0; 1148c2ecf20Sopenharmony_ci pe_data.control_remote = control_remote; 1158c2ecf20Sopenharmony_ci pe_data.control_me = control_me; 1168c2ecf20Sopenharmony_ci pe_data.data_me = data_me; 1178c2ecf20Sopenharmony_ci pid = run_helper(etap_pre_exec, &pe_data, args); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci if (pid < 0) 1208c2ecf20Sopenharmony_ci err = pid; 1218c2ecf20Sopenharmony_ci close(data_remote); 1228c2ecf20Sopenharmony_ci close(control_remote); 1238c2ecf20Sopenharmony_ci CATCH_EINTR(n = read(control_me, &c, sizeof(c))); 1248c2ecf20Sopenharmony_ci if (n != sizeof(c)) { 1258c2ecf20Sopenharmony_ci err = -errno; 1268c2ecf20Sopenharmony_ci printk(UM_KERN_ERR "etap_tramp : read of status failed, " 1278c2ecf20Sopenharmony_ci "err = %d\n", -err); 1288c2ecf20Sopenharmony_ci return err; 1298c2ecf20Sopenharmony_ci } 1308c2ecf20Sopenharmony_ci if (c != 1) { 1318c2ecf20Sopenharmony_ci printk(UM_KERN_ERR "etap_tramp : uml_net failed\n"); 1328c2ecf20Sopenharmony_ci err = helper_wait(pid); 1338c2ecf20Sopenharmony_ci } 1348c2ecf20Sopenharmony_ci return err; 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistatic int etap_open(void *data) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci struct ethertap_data *pri = data; 1408c2ecf20Sopenharmony_ci char *output; 1418c2ecf20Sopenharmony_ci int data_fds[2], control_fds[2], err, output_len; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci err = tap_open_common(pri->dev, pri->gate_addr); 1448c2ecf20Sopenharmony_ci if (err) 1458c2ecf20Sopenharmony_ci return err; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci err = socketpair(AF_UNIX, SOCK_DGRAM, 0, data_fds); 1488c2ecf20Sopenharmony_ci if (err) { 1498c2ecf20Sopenharmony_ci err = -errno; 1508c2ecf20Sopenharmony_ci printk(UM_KERN_ERR "etap_open - data socketpair failed - " 1518c2ecf20Sopenharmony_ci "err = %d\n", errno); 1528c2ecf20Sopenharmony_ci return err; 1538c2ecf20Sopenharmony_ci } 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci err = socketpair(AF_UNIX, SOCK_STREAM, 0, control_fds); 1568c2ecf20Sopenharmony_ci if (err) { 1578c2ecf20Sopenharmony_ci err = -errno; 1588c2ecf20Sopenharmony_ci printk(UM_KERN_ERR "etap_open - control socketpair failed - " 1598c2ecf20Sopenharmony_ci "err = %d\n", errno); 1608c2ecf20Sopenharmony_ci goto out_close_data; 1618c2ecf20Sopenharmony_ci } 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci err = etap_tramp(pri->dev_name, pri->gate_addr, control_fds[0], 1648c2ecf20Sopenharmony_ci control_fds[1], data_fds[0], data_fds[1]); 1658c2ecf20Sopenharmony_ci output_len = UM_KERN_PAGE_SIZE; 1668c2ecf20Sopenharmony_ci output = uml_kmalloc(output_len, UM_GFP_KERNEL); 1678c2ecf20Sopenharmony_ci read_output(control_fds[0], output, output_len); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci if (output == NULL) 1708c2ecf20Sopenharmony_ci printk(UM_KERN_ERR "etap_open : failed to allocate output " 1718c2ecf20Sopenharmony_ci "buffer\n"); 1728c2ecf20Sopenharmony_ci else { 1738c2ecf20Sopenharmony_ci printk("%s", output); 1748c2ecf20Sopenharmony_ci kfree(output); 1758c2ecf20Sopenharmony_ci } 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci if (err < 0) { 1788c2ecf20Sopenharmony_ci printk(UM_KERN_ERR "etap_tramp failed - err = %d\n", -err); 1798c2ecf20Sopenharmony_ci goto out_close_control; 1808c2ecf20Sopenharmony_ci } 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci pri->data_fd = data_fds[0]; 1838c2ecf20Sopenharmony_ci pri->control_fd = control_fds[0]; 1848c2ecf20Sopenharmony_ci iter_addresses(pri->dev, etap_open_addr, &pri->control_fd); 1858c2ecf20Sopenharmony_ci return data_fds[0]; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ciout_close_control: 1888c2ecf20Sopenharmony_ci close(control_fds[0]); 1898c2ecf20Sopenharmony_ci close(control_fds[1]); 1908c2ecf20Sopenharmony_ciout_close_data: 1918c2ecf20Sopenharmony_ci close(data_fds[0]); 1928c2ecf20Sopenharmony_ci close(data_fds[1]); 1938c2ecf20Sopenharmony_ci return err; 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cistatic void etap_close(int fd, void *data) 1978c2ecf20Sopenharmony_ci{ 1988c2ecf20Sopenharmony_ci struct ethertap_data *pri = data; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci iter_addresses(pri->dev, etap_close_addr, &pri->control_fd); 2018c2ecf20Sopenharmony_ci close(fd); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci if (shutdown(pri->data_fd, SHUT_RDWR) < 0) 2048c2ecf20Sopenharmony_ci printk(UM_KERN_ERR "etap_close - shutdown data socket failed, " 2058c2ecf20Sopenharmony_ci "errno = %d\n", errno); 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci if (shutdown(pri->control_fd, SHUT_RDWR) < 0) 2088c2ecf20Sopenharmony_ci printk(UM_KERN_ERR "etap_close - shutdown control socket " 2098c2ecf20Sopenharmony_ci "failed, errno = %d\n", errno); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci close(pri->data_fd); 2128c2ecf20Sopenharmony_ci pri->data_fd = -1; 2138c2ecf20Sopenharmony_ci close(pri->control_fd); 2148c2ecf20Sopenharmony_ci pri->control_fd = -1; 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_cistatic void etap_add_addr(unsigned char *addr, unsigned char *netmask, 2188c2ecf20Sopenharmony_ci void *data) 2198c2ecf20Sopenharmony_ci{ 2208c2ecf20Sopenharmony_ci struct ethertap_data *pri = data; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci tap_check_ips(pri->gate_addr, addr); 2238c2ecf20Sopenharmony_ci if (pri->control_fd == -1) 2248c2ecf20Sopenharmony_ci return; 2258c2ecf20Sopenharmony_ci etap_open_addr(addr, netmask, &pri->control_fd); 2268c2ecf20Sopenharmony_ci} 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_cistatic void etap_del_addr(unsigned char *addr, unsigned char *netmask, 2298c2ecf20Sopenharmony_ci void *data) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci struct ethertap_data *pri = data; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci if (pri->control_fd == -1) 2348c2ecf20Sopenharmony_ci return; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci etap_close_addr(addr, netmask, &pri->control_fd); 2378c2ecf20Sopenharmony_ci} 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ciconst struct net_user_info ethertap_user_info = { 2408c2ecf20Sopenharmony_ci .init = etap_user_init, 2418c2ecf20Sopenharmony_ci .open = etap_open, 2428c2ecf20Sopenharmony_ci .close = etap_close, 2438c2ecf20Sopenharmony_ci .remove = NULL, 2448c2ecf20Sopenharmony_ci .add_address = etap_add_addr, 2458c2ecf20Sopenharmony_ci .delete_address = etap_del_addr, 2468c2ecf20Sopenharmony_ci .mtu = ETH_MAX_PACKET, 2478c2ecf20Sopenharmony_ci .max_packet = ETH_MAX_PACKET + ETH_HEADER_ETHERTAP, 2488c2ecf20Sopenharmony_ci}; 249