Mark McLoughlin | 966ea5e | 2009-10-22 17:49:09 +0100 | [diff] [blame] | 1 | /* |
| 2 | * QEMU System Emulator |
| 3 | * |
| 4 | * Copyright (c) 2003-2008 Fabrice Bellard |
| 5 | * |
| 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
| 7 | * of this software and associated documentation files (the "Software"), to deal |
| 8 | * in the Software without restriction, including without limitation the rights |
| 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| 10 | * copies of the Software, and to permit persons to whom the Software is |
| 11 | * furnished to do so, subject to the following conditions: |
| 12 | * |
| 13 | * The above copyright notice and this permission notice shall be included in |
| 14 | * all copies or substantial portions of the Software. |
| 15 | * |
| 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| 19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
| 22 | * THE SOFTWARE. |
| 23 | */ |
| 24 | |
Peter Maydell | 2744d92 | 2016-01-29 17:50:00 +0000 | [diff] [blame] | 25 | #include "qemu/osdep.h" |
Markus Armbruster | da34e65 | 2016-03-14 09:01:28 +0100 | [diff] [blame] | 26 | #include "qapi/error.h" |
Paolo Bonzini | 1422e32 | 2012-10-24 08:43:34 +0200 | [diff] [blame] | 27 | #include "tap_int.h" |
Paolo Bonzini | 9c17d61 | 2012-12-17 18:20:04 +0100 | [diff] [blame] | 28 | #include "sysemu/sysemu.h" |
Veronia Bahaa | f348b6d | 2016-03-20 19:16:19 +0200 | [diff] [blame] | 29 | #include "qemu/cutils.h" |
Mark McLoughlin | 966ea5e | 2009-10-22 17:49:09 +0100 | [diff] [blame] | 30 | |
Mark McLoughlin | 966ea5e | 2009-10-22 17:49:09 +0100 | [diff] [blame] | 31 | #include <sys/ethernet.h> |
| 32 | #include <sys/sockio.h> |
| 33 | #include <netinet/arp.h> |
| 34 | #include <netinet/in.h> |
| 35 | #include <netinet/in_systm.h> |
| 36 | #include <netinet/ip.h> |
| 37 | #include <netinet/ip_icmp.h> // must come after ip.h |
| 38 | #include <netinet/udp.h> |
| 39 | #include <netinet/tcp.h> |
| 40 | #include <net/if.h> |
Mark McLoughlin | 966ea5e | 2009-10-22 17:49:09 +0100 | [diff] [blame] | 41 | #include <stropts.h> |
Paolo Bonzini | 1de7afc | 2012-12-17 18:20:00 +0100 | [diff] [blame] | 42 | #include "qemu/error-report.h" |
Mark McLoughlin | 966ea5e | 2009-10-22 17:49:09 +0100 | [diff] [blame] | 43 | |
| 44 | ssize_t tap_read_packet(int tapfd, uint8_t *buf, int maxlen) |
| 45 | { |
| 46 | struct strbuf sbuf; |
| 47 | int f = 0; |
| 48 | |
| 49 | sbuf.maxlen = maxlen; |
| 50 | sbuf.buf = (char *)buf; |
| 51 | |
| 52 | return getmsg(tapfd, NULL, &sbuf, &f) >= 0 ? sbuf.len : -1; |
| 53 | } |
| 54 | |
| 55 | #define TUNNEWPPA (('T'<<16) | 0x0001) |
| 56 | /* |
| 57 | * Allocate TAP device, returns opened fd. |
| 58 | * Stores dev name in the first arg(must be large enough). |
| 59 | */ |
Markus Armbruster | 576c6eb | 2015-05-15 13:59:01 +0200 | [diff] [blame] | 60 | static int tap_alloc(char *dev, size_t dev_size, Error **errp) |
Mark McLoughlin | 966ea5e | 2009-10-22 17:49:09 +0100 | [diff] [blame] | 61 | { |
Markus Armbruster | 576c6eb | 2015-05-15 13:59:01 +0200 | [diff] [blame] | 62 | /* FIXME leaks like a sieve on error paths */ |
| 63 | /* FIXME suspicious: many errors are reported, then ignored */ |
Mark McLoughlin | 966ea5e | 2009-10-22 17:49:09 +0100 | [diff] [blame] | 64 | int tap_fd, if_fd, ppa = -1; |
| 65 | static int ip_fd = 0; |
| 66 | char *ptr; |
| 67 | |
| 68 | static int arp_fd = 0; |
| 69 | int ip_muxid, arp_muxid; |
| 70 | struct strioctl strioc_if, strioc_ppa; |
Dong Xu Wang | 3a93113 | 2011-11-29 16:52:38 +0800 | [diff] [blame] | 71 | int link_type = I_PLINK; |
Mark McLoughlin | 966ea5e | 2009-10-22 17:49:09 +0100 | [diff] [blame] | 72 | struct lifreq ifr; |
| 73 | char actual_name[32] = ""; |
| 74 | |
| 75 | memset(&ifr, 0x0, sizeof(ifr)); |
| 76 | |
| 77 | if( *dev ){ |
| 78 | ptr = dev; |
| 79 | while( *ptr && !qemu_isdigit((int)*ptr) ) ptr++; |
| 80 | ppa = atoi(ptr); |
| 81 | } |
| 82 | |
| 83 | /* Check if IP device was opened */ |
| 84 | if( ip_fd ) |
| 85 | close(ip_fd); |
| 86 | |
| 87 | TFR(ip_fd = open("/dev/udp", O_RDWR, 0)); |
| 88 | if (ip_fd < 0) { |
Markus Armbruster | 576c6eb | 2015-05-15 13:59:01 +0200 | [diff] [blame] | 89 | error_setg(errp, "Can't open /dev/ip (actually /dev/udp)"); |
| 90 | return -1; |
Mark McLoughlin | 966ea5e | 2009-10-22 17:49:09 +0100 | [diff] [blame] | 91 | } |
| 92 | |
| 93 | TFR(tap_fd = open("/dev/tap", O_RDWR, 0)); |
| 94 | if (tap_fd < 0) { |
Markus Armbruster | 576c6eb | 2015-05-15 13:59:01 +0200 | [diff] [blame] | 95 | error_setg(errp, "Can't open /dev/tap"); |
| 96 | return -1; |
Mark McLoughlin | 966ea5e | 2009-10-22 17:49:09 +0100 | [diff] [blame] | 97 | } |
| 98 | |
| 99 | /* Assign a new PPA and get its unit number. */ |
| 100 | strioc_ppa.ic_cmd = TUNNEWPPA; |
| 101 | strioc_ppa.ic_timout = 0; |
| 102 | strioc_ppa.ic_len = sizeof(ppa); |
| 103 | strioc_ppa.ic_dp = (char *)&ppa; |
| 104 | if ((ppa = ioctl (tap_fd, I_STR, &strioc_ppa)) < 0) |
Markus Armbruster | 576c6eb | 2015-05-15 13:59:01 +0200 | [diff] [blame] | 105 | error_report("Can't assign new interface"); |
Mark McLoughlin | 966ea5e | 2009-10-22 17:49:09 +0100 | [diff] [blame] | 106 | |
| 107 | TFR(if_fd = open("/dev/tap", O_RDWR, 0)); |
| 108 | if (if_fd < 0) { |
Markus Armbruster | 576c6eb | 2015-05-15 13:59:01 +0200 | [diff] [blame] | 109 | error_setg(errp, "Can't open /dev/tap (2)"); |
| 110 | return -1; |
Mark McLoughlin | 966ea5e | 2009-10-22 17:49:09 +0100 | [diff] [blame] | 111 | } |
| 112 | if(ioctl(if_fd, I_PUSH, "ip") < 0){ |
Markus Armbruster | 576c6eb | 2015-05-15 13:59:01 +0200 | [diff] [blame] | 113 | error_setg(errp, "Can't push IP module"); |
| 114 | return -1; |
Mark McLoughlin | 966ea5e | 2009-10-22 17:49:09 +0100 | [diff] [blame] | 115 | } |
| 116 | |
| 117 | if (ioctl(if_fd, SIOCGLIFFLAGS, &ifr) < 0) |
Markus Armbruster | 576c6eb | 2015-05-15 13:59:01 +0200 | [diff] [blame] | 118 | error_report("Can't get flags"); |
Mark McLoughlin | 966ea5e | 2009-10-22 17:49:09 +0100 | [diff] [blame] | 119 | |
| 120 | snprintf (actual_name, 32, "tap%d", ppa); |
| 121 | pstrcpy(ifr.lifr_name, sizeof(ifr.lifr_name), actual_name); |
| 122 | |
| 123 | ifr.lifr_ppa = ppa; |
| 124 | /* Assign ppa according to the unit number returned by tun device */ |
| 125 | |
| 126 | if (ioctl (if_fd, SIOCSLIFNAME, &ifr) < 0) |
Markus Armbruster | 576c6eb | 2015-05-15 13:59:01 +0200 | [diff] [blame] | 127 | error_report("Can't set PPA %d", ppa); |
Mark McLoughlin | 966ea5e | 2009-10-22 17:49:09 +0100 | [diff] [blame] | 128 | if (ioctl(if_fd, SIOCGLIFFLAGS, &ifr) <0) |
Markus Armbruster | 576c6eb | 2015-05-15 13:59:01 +0200 | [diff] [blame] | 129 | error_report("Can't get flags"); |
Mark McLoughlin | 966ea5e | 2009-10-22 17:49:09 +0100 | [diff] [blame] | 130 | /* Push arp module to if_fd */ |
| 131 | if (ioctl (if_fd, I_PUSH, "arp") < 0) |
Markus Armbruster | 576c6eb | 2015-05-15 13:59:01 +0200 | [diff] [blame] | 132 | error_report("Can't push ARP module (2)"); |
Mark McLoughlin | 966ea5e | 2009-10-22 17:49:09 +0100 | [diff] [blame] | 133 | |
| 134 | /* Push arp module to ip_fd */ |
| 135 | if (ioctl (ip_fd, I_POP, NULL) < 0) |
Markus Armbruster | 576c6eb | 2015-05-15 13:59:01 +0200 | [diff] [blame] | 136 | error_report("I_POP failed"); |
Mark McLoughlin | 966ea5e | 2009-10-22 17:49:09 +0100 | [diff] [blame] | 137 | if (ioctl (ip_fd, I_PUSH, "arp") < 0) |
Markus Armbruster | 576c6eb | 2015-05-15 13:59:01 +0200 | [diff] [blame] | 138 | error_report("Can't push ARP module (3)"); |
Mark McLoughlin | 966ea5e | 2009-10-22 17:49:09 +0100 | [diff] [blame] | 139 | /* Open arp_fd */ |
| 140 | TFR(arp_fd = open ("/dev/tap", O_RDWR, 0)); |
| 141 | if (arp_fd < 0) |
Markus Armbruster | 576c6eb | 2015-05-15 13:59:01 +0200 | [diff] [blame] | 142 | error_report("Can't open %s", "/dev/tap"); |
Mark McLoughlin | 966ea5e | 2009-10-22 17:49:09 +0100 | [diff] [blame] | 143 | |
| 144 | /* Set ifname to arp */ |
| 145 | strioc_if.ic_cmd = SIOCSLIFNAME; |
| 146 | strioc_if.ic_timout = 0; |
| 147 | strioc_if.ic_len = sizeof(ifr); |
| 148 | strioc_if.ic_dp = (char *)𝔦 |
| 149 | if (ioctl(arp_fd, I_STR, &strioc_if) < 0){ |
Markus Armbruster | 576c6eb | 2015-05-15 13:59:01 +0200 | [diff] [blame] | 150 | error_report("Can't set ifname to arp"); |
Mark McLoughlin | 966ea5e | 2009-10-22 17:49:09 +0100 | [diff] [blame] | 151 | } |
| 152 | |
| 153 | if((ip_muxid = ioctl(ip_fd, I_LINK, if_fd)) < 0){ |
Markus Armbruster | 576c6eb | 2015-05-15 13:59:01 +0200 | [diff] [blame] | 154 | error_setg(errp, "Can't link TAP device to IP"); |
| 155 | return -1; |
Mark McLoughlin | 966ea5e | 2009-10-22 17:49:09 +0100 | [diff] [blame] | 156 | } |
| 157 | |
| 158 | if ((arp_muxid = ioctl (ip_fd, link_type, arp_fd)) < 0) |
Markus Armbruster | 576c6eb | 2015-05-15 13:59:01 +0200 | [diff] [blame] | 159 | error_report("Can't link TAP device to ARP"); |
Mark McLoughlin | 966ea5e | 2009-10-22 17:49:09 +0100 | [diff] [blame] | 160 | |
| 161 | close (if_fd); |
| 162 | |
| 163 | memset(&ifr, 0x0, sizeof(ifr)); |
| 164 | pstrcpy(ifr.lifr_name, sizeof(ifr.lifr_name), actual_name); |
| 165 | ifr.lifr_ip_muxid = ip_muxid; |
| 166 | ifr.lifr_arp_muxid = arp_muxid; |
| 167 | |
| 168 | if (ioctl (ip_fd, SIOCSLIFMUXID, &ifr) < 0) |
| 169 | { |
| 170 | ioctl (ip_fd, I_PUNLINK , arp_muxid); |
| 171 | ioctl (ip_fd, I_PUNLINK, ip_muxid); |
Markus Armbruster | 576c6eb | 2015-05-15 13:59:01 +0200 | [diff] [blame] | 172 | error_report("Can't set multiplexor id"); |
Mark McLoughlin | 966ea5e | 2009-10-22 17:49:09 +0100 | [diff] [blame] | 173 | } |
| 174 | |
| 175 | snprintf(dev, dev_size, "tap%d", ppa); |
| 176 | return tap_fd; |
| 177 | } |
| 178 | |
Jason Wang | 264986e | 2013-01-30 19:12:34 +0800 | [diff] [blame] | 179 | int tap_open(char *ifname, int ifname_size, int *vnet_hdr, |
Markus Armbruster | 468dd82 | 2015-05-15 13:58:58 +0200 | [diff] [blame] | 180 | int vnet_hdr_required, int mq_required, Error **errp) |
Mark McLoughlin | 966ea5e | 2009-10-22 17:49:09 +0100 | [diff] [blame] | 181 | { |
| 182 | char dev[10]=""; |
| 183 | int fd; |
Markus Armbruster | 576c6eb | 2015-05-15 13:59:01 +0200 | [diff] [blame] | 184 | |
| 185 | fd = tap_alloc(dev, sizeof(dev), errp); |
| 186 | if (fd < 0) { |
| 187 | return -1; |
Mark McLoughlin | 966ea5e | 2009-10-22 17:49:09 +0100 | [diff] [blame] | 188 | } |
| 189 | pstrcpy(ifname, ifname_size, dev); |
Mark McLoughlin | f5c5e38 | 2009-11-25 18:49:37 +0000 | [diff] [blame] | 190 | if (*vnet_hdr) { |
| 191 | /* Solaris doesn't have IFF_VNET_HDR */ |
| 192 | *vnet_hdr = 0; |
| 193 | |
| 194 | if (vnet_hdr_required && !*vnet_hdr) { |
Markus Armbruster | 576c6eb | 2015-05-15 13:59:01 +0200 | [diff] [blame] | 195 | error_setg(errp, "vnet_hdr=1 requested, but no kernel " |
| 196 | "support for IFF_VNET_HDR available"); |
Mark McLoughlin | f5c5e38 | 2009-11-25 18:49:37 +0000 | [diff] [blame] | 197 | close(fd); |
| 198 | return -1; |
| 199 | } |
| 200 | } |
Mark McLoughlin | 966ea5e | 2009-10-22 17:49:09 +0100 | [diff] [blame] | 201 | fcntl(fd, F_SETFL, O_NONBLOCK); |
| 202 | return fd; |
| 203 | } |
Mark McLoughlin | 15ac913 | 2009-10-22 17:49:13 +0100 | [diff] [blame] | 204 | |
Markus Armbruster | 80b832c | 2015-05-15 13:58:55 +0200 | [diff] [blame] | 205 | void tap_set_sndbuf(int fd, const NetdevTapOptions *tap, Error **errp) |
Mark McLoughlin | 15ac913 | 2009-10-22 17:49:13 +0100 | [diff] [blame] | 206 | { |
Mark McLoughlin | 15ac913 | 2009-10-22 17:49:13 +0100 | [diff] [blame] | 207 | } |
Mark McLoughlin | dc69004 | 2009-10-22 17:49:14 +0100 | [diff] [blame] | 208 | |
| 209 | int tap_probe_vnet_hdr(int fd) |
| 210 | { |
| 211 | return 0; |
| 212 | } |
Mark McLoughlin | 1faac1f | 2009-10-22 17:49:15 +0100 | [diff] [blame] | 213 | |
Mark McLoughlin | 9c28271 | 2009-10-22 17:49:16 +0100 | [diff] [blame] | 214 | int tap_probe_has_ufo(int fd) |
| 215 | { |
| 216 | return 0; |
| 217 | } |
| 218 | |
Michael S. Tsirkin | 445d892 | 2010-07-16 11:16:06 +0300 | [diff] [blame] | 219 | int tap_probe_vnet_hdr_len(int fd, int len) |
| 220 | { |
| 221 | return 0; |
| 222 | } |
| 223 | |
| 224 | void tap_fd_set_vnet_hdr_len(int fd, int len) |
| 225 | { |
| 226 | } |
| 227 | |
Michael S. Tsirkin | 4ee9b43 | 2015-06-18 16:52:23 +0200 | [diff] [blame] | 228 | int tap_fd_set_vnet_le(int fd, int is_le) |
| 229 | { |
| 230 | return -EINVAL; |
| 231 | } |
| 232 | |
| 233 | int tap_fd_set_vnet_be(int fd, int is_be) |
| 234 | { |
| 235 | return -EINVAL; |
| 236 | } |
| 237 | |
Mark McLoughlin | 1faac1f | 2009-10-22 17:49:15 +0100 | [diff] [blame] | 238 | void tap_fd_set_offload(int fd, int csum, int tso4, |
| 239 | int tso6, int ecn, int ufo) |
| 240 | { |
| 241 | } |
Jason Wang | 94fdc6d | 2013-01-30 19:12:31 +0800 | [diff] [blame] | 242 | |
| 243 | int tap_fd_enable(int fd) |
| 244 | { |
| 245 | return -1; |
| 246 | } |
| 247 | |
| 248 | int tap_fd_disable(int fd) |
| 249 | { |
| 250 | return -1; |
| 251 | } |
| 252 | |
Jason Wang | e5dc0b4 | 2013-01-30 19:12:33 +0800 | [diff] [blame] | 253 | int tap_fd_get_ifname(int fd, char *ifname) |
| 254 | { |
| 255 | return -1; |
| 256 | } |