1/* 2 * Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) 3 * Licensed under the GPL 4 */ 5 6#include <stdio.h> 7#include <unistd.h> 8#include <errno.h> 9#include <string.h> 10#include <linux/if_tun.h> 11#include <net/if.h> 12#include <sys/ioctl.h> 13#include <sys/socket.h> 14#include <sys/wait.h> 15#include <sys/uio.h> 16#include <kern_util.h> 17#include <os.h> 18#include "tuntap.h" 19 20static int tuntap_user_init(void *data, void *dev) 21{ 22 struct tuntap_data *pri = data; 23 24 pri->dev = dev; 25 return 0; 26} 27 28static void tuntap_add_addr(unsigned char *addr, unsigned char *netmask, 29 void *data) 30{ 31 struct tuntap_data *pri = data; 32 33 tap_check_ips(pri->gate_addr, addr); 34 if ((pri->fd == -1) || pri->fixed_config) 35 return; 36 open_addr(addr, netmask, pri->dev_name); 37} 38 39static void tuntap_del_addr(unsigned char *addr, unsigned char *netmask, 40 void *data) 41{ 42 struct tuntap_data *pri = data; 43 44 if ((pri->fd == -1) || pri->fixed_config) 45 return; 46 close_addr(addr, netmask, pri->dev_name); 47} 48 49struct tuntap_pre_exec_data { 50 int stdout_fd; 51 int close_me; 52}; 53 54static void tuntap_pre_exec(void *arg) 55{ 56 struct tuntap_pre_exec_data *data = arg; 57 58 dup2(data->stdout_fd, 1); 59 close(data->close_me); 60} 61 62static int tuntap_open_tramp(char *gate, int *fd_out, int me, int remote, 63 char *buffer, int buffer_len, int *used_out) 64{ 65 struct tuntap_pre_exec_data data; 66 char version_buf[sizeof("nnnnn\0")]; 67 char *argv[] = { "uml_net", version_buf, "tuntap", "up", gate, 68 NULL }; 69 char buf[CMSG_SPACE(sizeof(*fd_out))]; 70 struct msghdr msg; 71 struct cmsghdr *cmsg; 72 struct iovec iov; 73 int pid, n, err; 74 75 sprintf(version_buf, "%d", UML_NET_VERSION); 76 77 data.stdout_fd = remote; 78 data.close_me = me; 79 80 pid = run_helper(tuntap_pre_exec, &data, argv); 81 82 if (pid < 0) 83 return -pid; 84 85 close(remote); 86 87 msg.msg_name = NULL; 88 msg.msg_namelen = 0; 89 if (buffer != NULL) { 90 iov = ((struct iovec) { buffer, buffer_len }); 91 msg.msg_iov = &iov; 92 msg.msg_iovlen = 1; 93 } 94 else { 95 msg.msg_iov = NULL; 96 msg.msg_iovlen = 0; 97 } 98 msg.msg_control = buf; 99 msg.msg_controllen = sizeof(buf); 100 msg.msg_flags = 0; 101 n = recvmsg(me, &msg, 0); 102 *used_out = n; 103 if (n < 0) { 104 err = -errno; 105 printk(UM_KERN_ERR "tuntap_open_tramp : recvmsg failed - " 106 "errno = %d\n", errno); 107 return err; 108 } 109 helper_wait(pid); 110 111 cmsg = CMSG_FIRSTHDR(&msg); 112 if (cmsg == NULL) { 113 printk(UM_KERN_ERR "tuntap_open_tramp : didn't receive a " 114 "message\n"); 115 return -EINVAL; 116 } 117 if ((cmsg->cmsg_level != SOL_SOCKET) || 118 (cmsg->cmsg_type != SCM_RIGHTS)) { 119 printk(UM_KERN_ERR "tuntap_open_tramp : didn't receive a " 120 "descriptor\n"); 121 return -EINVAL; 122 } 123 *fd_out = ((int *) CMSG_DATA(cmsg))[0]; 124 os_set_exec_close(*fd_out); 125 return 0; 126} 127 128static int tuntap_open(void *data) 129{ 130 struct ifreq ifr; 131 struct tuntap_data *pri = data; 132 char *output, *buffer; 133 int err, fds[2], len, used; 134 135 err = tap_open_common(pri->dev, pri->gate_addr); 136 if (err < 0) 137 return err; 138 139 if (pri->fixed_config) { 140 pri->fd = os_open_file("/dev/net/tun", 141 of_cloexec(of_rdwr(OPENFLAGS())), 0); 142 if (pri->fd < 0) { 143 printk(UM_KERN_ERR "Failed to open /dev/net/tun, " 144 "err = %d\n", -pri->fd); 145 return pri->fd; 146 } 147 memset(&ifr, 0, sizeof(ifr)); 148 ifr.ifr_flags = IFF_TAP | IFF_NO_PI; 149 strlcpy(ifr.ifr_name, pri->dev_name, sizeof(ifr.ifr_name)); 150 if (ioctl(pri->fd, TUNSETIFF, &ifr) < 0) { 151 err = -errno; 152 printk(UM_KERN_ERR "TUNSETIFF failed, errno = %d\n", 153 errno); 154 close(pri->fd); 155 return err; 156 } 157 } 158 else { 159 err = socketpair(AF_UNIX, SOCK_DGRAM, 0, fds); 160 if (err) { 161 err = -errno; 162 printk(UM_KERN_ERR "tuntap_open : socketpair failed - " 163 "errno = %d\n", errno); 164 return err; 165 } 166 167 buffer = get_output_buffer(&len); 168 if (buffer != NULL) 169 len--; 170 used = 0; 171 172 err = tuntap_open_tramp(pri->gate_addr, &pri->fd, fds[0], 173 fds[1], buffer, len, &used); 174 175 output = buffer; 176 if (err < 0) { 177 printk("%s", output); 178 free_output_buffer(buffer); 179 printk(UM_KERN_ERR "tuntap_open_tramp failed - " 180 "err = %d\n", -err); 181 return err; 182 } 183 184 pri->dev_name = uml_strdup(buffer); 185 output += IFNAMSIZ; 186 printk("%s", output); 187 free_output_buffer(buffer); 188 189 close(fds[0]); 190 iter_addresses(pri->dev, open_addr, pri->dev_name); 191 } 192 193 return pri->fd; 194} 195 196static void tuntap_close(int fd, void *data) 197{ 198 struct tuntap_data *pri = data; 199 200 if (!pri->fixed_config) 201 iter_addresses(pri->dev, close_addr, pri->dev_name); 202 close(fd); 203 pri->fd = -1; 204} 205 206const struct net_user_info tuntap_user_info = { 207 .init = tuntap_user_init, 208 .open = tuntap_open, 209 .close = tuntap_close, 210 .remove = NULL, 211 .add_address = tuntap_add_addr, 212 .delete_address = tuntap_del_addr, 213 .mtu = ETH_MAX_PACKET, 214 .max_packet = ETH_MAX_PACKET + ETH_HEADER_OTHER, 215}; 216