162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <stdio.h> 762306a36Sopenharmony_ci#include <unistd.h> 862306a36Sopenharmony_ci#include <stdarg.h> 962306a36Sopenharmony_ci#include <errno.h> 1062306a36Sopenharmony_ci#include <stddef.h> 1162306a36Sopenharmony_ci#include <string.h> 1262306a36Sopenharmony_ci#include <sys/socket.h> 1362306a36Sopenharmony_ci#include <sys/wait.h> 1462306a36Sopenharmony_ci#include <net_user.h> 1562306a36Sopenharmony_ci#include <os.h> 1662306a36Sopenharmony_ci#include <um_malloc.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ciint tap_open_common(void *dev, char *gate_addr) 1962306a36Sopenharmony_ci{ 2062306a36Sopenharmony_ci int tap_addr[4]; 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci if (gate_addr == NULL) 2362306a36Sopenharmony_ci return 0; 2462306a36Sopenharmony_ci if (sscanf(gate_addr, "%d.%d.%d.%d", &tap_addr[0], 2562306a36Sopenharmony_ci &tap_addr[1], &tap_addr[2], &tap_addr[3]) != 4) { 2662306a36Sopenharmony_ci printk(UM_KERN_ERR "Invalid tap IP address - '%s'\n", 2762306a36Sopenharmony_ci gate_addr); 2862306a36Sopenharmony_ci return -EINVAL; 2962306a36Sopenharmony_ci } 3062306a36Sopenharmony_ci return 0; 3162306a36Sopenharmony_ci} 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_civoid tap_check_ips(char *gate_addr, unsigned char *eth_addr) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci int tap_addr[4]; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci if ((gate_addr != NULL) && 3862306a36Sopenharmony_ci (sscanf(gate_addr, "%d.%d.%d.%d", &tap_addr[0], 3962306a36Sopenharmony_ci &tap_addr[1], &tap_addr[2], &tap_addr[3]) == 4) && 4062306a36Sopenharmony_ci (eth_addr[0] == tap_addr[0]) && 4162306a36Sopenharmony_ci (eth_addr[1] == tap_addr[1]) && 4262306a36Sopenharmony_ci (eth_addr[2] == tap_addr[2]) && 4362306a36Sopenharmony_ci (eth_addr[3] == tap_addr[3])) { 4462306a36Sopenharmony_ci printk(UM_KERN_ERR "The tap IP address and the UML eth IP " 4562306a36Sopenharmony_ci "address must be different\n"); 4662306a36Sopenharmony_ci } 4762306a36Sopenharmony_ci} 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci/* Do reliable error handling as this fails frequently enough. */ 5062306a36Sopenharmony_civoid read_output(int fd, char *output, int len) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci int remain, ret, expected; 5362306a36Sopenharmony_ci char c; 5462306a36Sopenharmony_ci char *str; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci if (output == NULL) { 5762306a36Sopenharmony_ci output = &c; 5862306a36Sopenharmony_ci len = sizeof(c); 5962306a36Sopenharmony_ci } 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci *output = '\0'; 6262306a36Sopenharmony_ci ret = read(fd, &remain, sizeof(remain)); 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci if (ret != sizeof(remain)) { 6562306a36Sopenharmony_ci if (ret < 0) 6662306a36Sopenharmony_ci ret = -errno; 6762306a36Sopenharmony_ci expected = sizeof(remain); 6862306a36Sopenharmony_ci str = "length"; 6962306a36Sopenharmony_ci goto err; 7062306a36Sopenharmony_ci } 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci while (remain != 0) { 7362306a36Sopenharmony_ci expected = (remain < len) ? remain : len; 7462306a36Sopenharmony_ci ret = read(fd, output, expected); 7562306a36Sopenharmony_ci if (ret != expected) { 7662306a36Sopenharmony_ci if (ret < 0) 7762306a36Sopenharmony_ci ret = -errno; 7862306a36Sopenharmony_ci str = "data"; 7962306a36Sopenharmony_ci goto err; 8062306a36Sopenharmony_ci } 8162306a36Sopenharmony_ci remain -= ret; 8262306a36Sopenharmony_ci } 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci return; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cierr: 8762306a36Sopenharmony_ci if (ret < 0) 8862306a36Sopenharmony_ci printk(UM_KERN_ERR "read_output - read of %s failed, " 8962306a36Sopenharmony_ci "errno = %d\n", str, -ret); 9062306a36Sopenharmony_ci else 9162306a36Sopenharmony_ci printk(UM_KERN_ERR "read_output - read of %s failed, read only " 9262306a36Sopenharmony_ci "%d of %d bytes\n", str, ret, expected); 9362306a36Sopenharmony_ci} 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ciint net_read(int fd, void *buf, int len) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci int n; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci n = read(fd, buf, len); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci if ((n < 0) && (errno == EAGAIN)) 10262306a36Sopenharmony_ci return 0; 10362306a36Sopenharmony_ci else if (n == 0) 10462306a36Sopenharmony_ci return -ENOTCONN; 10562306a36Sopenharmony_ci return n; 10662306a36Sopenharmony_ci} 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ciint net_recvfrom(int fd, void *buf, int len) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci int n; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci CATCH_EINTR(n = recvfrom(fd, buf, len, 0, NULL, NULL)); 11362306a36Sopenharmony_ci if (n < 0) { 11462306a36Sopenharmony_ci if (errno == EAGAIN) 11562306a36Sopenharmony_ci return 0; 11662306a36Sopenharmony_ci return -errno; 11762306a36Sopenharmony_ci } 11862306a36Sopenharmony_ci else if (n == 0) 11962306a36Sopenharmony_ci return -ENOTCONN; 12062306a36Sopenharmony_ci return n; 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ciint net_write(int fd, void *buf, int len) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci int n; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci n = write(fd, buf, len); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci if ((n < 0) && (errno == EAGAIN)) 13062306a36Sopenharmony_ci return 0; 13162306a36Sopenharmony_ci else if (n == 0) 13262306a36Sopenharmony_ci return -ENOTCONN; 13362306a36Sopenharmony_ci return n; 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ciint net_send(int fd, void *buf, int len) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci int n; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci CATCH_EINTR(n = send(fd, buf, len, 0)); 14162306a36Sopenharmony_ci if (n < 0) { 14262306a36Sopenharmony_ci if (errno == EAGAIN) 14362306a36Sopenharmony_ci return 0; 14462306a36Sopenharmony_ci return -errno; 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci else if (n == 0) 14762306a36Sopenharmony_ci return -ENOTCONN; 14862306a36Sopenharmony_ci return n; 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ciint net_sendto(int fd, void *buf, int len, void *to, int sock_len) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci int n; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci CATCH_EINTR(n = sendto(fd, buf, len, 0, (struct sockaddr *) to, 15662306a36Sopenharmony_ci sock_len)); 15762306a36Sopenharmony_ci if (n < 0) { 15862306a36Sopenharmony_ci if (errno == EAGAIN) 15962306a36Sopenharmony_ci return 0; 16062306a36Sopenharmony_ci return -errno; 16162306a36Sopenharmony_ci } 16262306a36Sopenharmony_ci else if (n == 0) 16362306a36Sopenharmony_ci return -ENOTCONN; 16462306a36Sopenharmony_ci return n; 16562306a36Sopenharmony_ci} 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cistruct change_pre_exec_data { 16862306a36Sopenharmony_ci int close_me; 16962306a36Sopenharmony_ci int stdout_fd; 17062306a36Sopenharmony_ci}; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cistatic void change_pre_exec(void *arg) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci struct change_pre_exec_data *data = arg; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci close(data->close_me); 17762306a36Sopenharmony_ci dup2(data->stdout_fd, 1); 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_cistatic int change_tramp(char **argv, char *output, int output_len) 18162306a36Sopenharmony_ci{ 18262306a36Sopenharmony_ci int pid, fds[2], err; 18362306a36Sopenharmony_ci struct change_pre_exec_data pe_data; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci err = os_pipe(fds, 1, 0); 18662306a36Sopenharmony_ci if (err < 0) { 18762306a36Sopenharmony_ci printk(UM_KERN_ERR "change_tramp - pipe failed, err = %d\n", 18862306a36Sopenharmony_ci -err); 18962306a36Sopenharmony_ci return err; 19062306a36Sopenharmony_ci } 19162306a36Sopenharmony_ci pe_data.close_me = fds[0]; 19262306a36Sopenharmony_ci pe_data.stdout_fd = fds[1]; 19362306a36Sopenharmony_ci pid = run_helper(change_pre_exec, &pe_data, argv); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci if (pid > 0) /* Avoid hang as we won't get data in failure case. */ 19662306a36Sopenharmony_ci read_output(fds[0], output, output_len); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci close(fds[0]); 19962306a36Sopenharmony_ci close(fds[1]); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci if (pid > 0) 20262306a36Sopenharmony_ci helper_wait(pid); 20362306a36Sopenharmony_ci return pid; 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cistatic void change(char *dev, char *what, unsigned char *addr, 20762306a36Sopenharmony_ci unsigned char *netmask) 20862306a36Sopenharmony_ci{ 20962306a36Sopenharmony_ci char addr_buf[sizeof("255.255.255.255\0")]; 21062306a36Sopenharmony_ci char netmask_buf[sizeof("255.255.255.255\0")]; 21162306a36Sopenharmony_ci char version[sizeof("nnnnn\0")]; 21262306a36Sopenharmony_ci char *argv[] = { "uml_net", version, what, dev, addr_buf, 21362306a36Sopenharmony_ci netmask_buf, NULL }; 21462306a36Sopenharmony_ci char *output; 21562306a36Sopenharmony_ci int output_len, pid; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci sprintf(version, "%d", UML_NET_VERSION); 21862306a36Sopenharmony_ci sprintf(addr_buf, "%d.%d.%d.%d", addr[0], addr[1], addr[2], addr[3]); 21962306a36Sopenharmony_ci sprintf(netmask_buf, "%d.%d.%d.%d", netmask[0], netmask[1], 22062306a36Sopenharmony_ci netmask[2], netmask[3]); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci output_len = UM_KERN_PAGE_SIZE; 22362306a36Sopenharmony_ci output = uml_kmalloc(output_len, UM_GFP_KERNEL); 22462306a36Sopenharmony_ci if (output == NULL) 22562306a36Sopenharmony_ci printk(UM_KERN_ERR "change : failed to allocate output " 22662306a36Sopenharmony_ci "buffer\n"); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci pid = change_tramp(argv, output, output_len); 22962306a36Sopenharmony_ci if (pid < 0) { 23062306a36Sopenharmony_ci kfree(output); 23162306a36Sopenharmony_ci return; 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci if (output != NULL) { 23562306a36Sopenharmony_ci printk("%s", output); 23662306a36Sopenharmony_ci kfree(output); 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci} 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_civoid open_addr(unsigned char *addr, unsigned char *netmask, void *arg) 24162306a36Sopenharmony_ci{ 24262306a36Sopenharmony_ci change(arg, "add", addr, netmask); 24362306a36Sopenharmony_ci} 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_civoid close_addr(unsigned char *addr, unsigned char *netmask, void *arg) 24662306a36Sopenharmony_ci{ 24762306a36Sopenharmony_ci change(arg, "del", addr, netmask); 24862306a36Sopenharmony_ci} 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_cichar *split_if_spec(char *str, ...) 25162306a36Sopenharmony_ci{ 25262306a36Sopenharmony_ci char **arg, *end, *ret = NULL; 25362306a36Sopenharmony_ci va_list ap; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci va_start(ap, str); 25662306a36Sopenharmony_ci while ((arg = va_arg(ap, char **)) != NULL) { 25762306a36Sopenharmony_ci if (*str == '\0') 25862306a36Sopenharmony_ci goto out; 25962306a36Sopenharmony_ci end = strchr(str, ','); 26062306a36Sopenharmony_ci if (end != str) 26162306a36Sopenharmony_ci *arg = str; 26262306a36Sopenharmony_ci if (end == NULL) 26362306a36Sopenharmony_ci goto out; 26462306a36Sopenharmony_ci *end++ = '\0'; 26562306a36Sopenharmony_ci str = end; 26662306a36Sopenharmony_ci } 26762306a36Sopenharmony_ci ret = str; 26862306a36Sopenharmony_ciout: 26962306a36Sopenharmony_ci va_end(ap); 27062306a36Sopenharmony_ci return ret; 27162306a36Sopenharmony_ci} 272