xref: /openbmc/qemu/net/tap-linux.c (revision 8e6fe6b8)
1 /*
2  * QEMU System Emulator
3  *
4  * Copyright (c) 2003-2008 Fabrice Bellard
5  * Copyright (c) 2009 Red Hat, Inc.
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a copy
8  * of this software and associated documentation files (the "Software"), to deal
9  * in the Software without restriction, including without limitation the rights
10  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11  * copies of the Software, and to permit persons to whom the Software is
12  * furnished to do so, subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included in
15  * all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23  * THE SOFTWARE.
24  */
25 
26 #include "qemu/osdep.h"
27 #include "qemu-common.h"
28 #include "tap_int.h"
29 #include "tap-linux.h"
30 #include "net/tap.h"
31 
32 #include <net/if.h>
33 #include <sys/ioctl.h>
34 
35 #include "sysemu/sysemu.h"
36 #include "qapi/error.h"
37 #include "qemu/error-report.h"
38 #include "qemu/cutils.h"
39 
40 #define PATH_NET_TUN "/dev/net/tun"
41 
42 int tap_open(char *ifname, int ifname_size, int *vnet_hdr,
43              int vnet_hdr_required, int mq_required, Error **errp)
44 {
45     struct ifreq ifr;
46     int fd, ret;
47     int len = sizeof(struct virtio_net_hdr);
48     unsigned int features;
49 
50     TFR(fd = open(PATH_NET_TUN, O_RDWR));
51     if (fd < 0) {
52         error_setg_errno(errp, errno, "could not open %s", PATH_NET_TUN);
53         return -1;
54     }
55     memset(&ifr, 0, sizeof(ifr));
56     ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
57 
58     if (ioctl(fd, TUNGETFEATURES, &features) == -1) {
59         warn_report("TUNGETFEATURES failed: %s", strerror(errno));
60         features = 0;
61     }
62 
63     if (features & IFF_ONE_QUEUE) {
64         ifr.ifr_flags |= IFF_ONE_QUEUE;
65     }
66 
67     if (*vnet_hdr) {
68         if (features & IFF_VNET_HDR) {
69             *vnet_hdr = 1;
70             ifr.ifr_flags |= IFF_VNET_HDR;
71         } else {
72             *vnet_hdr = 0;
73         }
74 
75         if (vnet_hdr_required && !*vnet_hdr) {
76             error_setg(errp, "vnet_hdr=1 requested, but no kernel "
77                        "support for IFF_VNET_HDR available");
78             close(fd);
79             return -1;
80         }
81         /*
82          * Make sure vnet header size has the default value: for a persistent
83          * tap it might have been modified e.g. by another instance of qemu.
84          * Ignore errors since old kernels do not support this ioctl: in this
85          * case the header size implicitly has the correct value.
86          */
87         ioctl(fd, TUNSETVNETHDRSZ, &len);
88     }
89 
90     if (mq_required) {
91         if (!(features & IFF_MULTI_QUEUE)) {
92             error_setg(errp, "multiqueue required, but no kernel "
93                        "support for IFF_MULTI_QUEUE available");
94             close(fd);
95             return -1;
96         } else {
97             ifr.ifr_flags |= IFF_MULTI_QUEUE;
98         }
99     }
100 
101     if (ifname[0] != '\0')
102         pstrcpy(ifr.ifr_name, IFNAMSIZ, ifname);
103     else
104         pstrcpy(ifr.ifr_name, IFNAMSIZ, "tap%d");
105     ret = ioctl(fd, TUNSETIFF, (void *) &ifr);
106     if (ret != 0) {
107         if (ifname[0] != '\0') {
108             error_setg_errno(errp, errno, "could not configure %s (%s)",
109                              PATH_NET_TUN, ifr.ifr_name);
110         } else {
111             error_setg_errno(errp, errno, "could not configure %s",
112                              PATH_NET_TUN);
113         }
114         close(fd);
115         return -1;
116     }
117     pstrcpy(ifname, ifname_size, ifr.ifr_name);
118     fcntl(fd, F_SETFL, O_NONBLOCK);
119     return fd;
120 }
121 
122 /* sndbuf implements a kind of flow control for tap.
123  * Unfortunately when it's enabled, and packets are sent
124  * to other guests on the same host, the receiver
125  * can lock up the transmitter indefinitely.
126  *
127  * To avoid packet loss, sndbuf should be set to a value lower than the tx
128  * queue capacity of any destination network interface.
129  * Ethernet NICs generally have txqueuelen=1000, so 1Mb is
130  * a good value, given a 1500 byte MTU.
131  */
132 #define TAP_DEFAULT_SNDBUF 0
133 
134 void tap_set_sndbuf(int fd, const NetdevTapOptions *tap, Error **errp)
135 {
136     int sndbuf;
137 
138     sndbuf = !tap->has_sndbuf       ? TAP_DEFAULT_SNDBUF :
139              tap->sndbuf > INT_MAX  ? INT_MAX :
140              tap->sndbuf;
141 
142     if (!sndbuf) {
143         sndbuf = INT_MAX;
144     }
145 
146     if (ioctl(fd, TUNSETSNDBUF, &sndbuf) == -1 && tap->has_sndbuf) {
147         error_setg_errno(errp, errno, "TUNSETSNDBUF ioctl failed");
148     }
149 }
150 
151 int tap_probe_vnet_hdr(int fd)
152 {
153     struct ifreq ifr;
154 
155     if (ioctl(fd, TUNGETIFF, &ifr) != 0) {
156         error_report("TUNGETIFF ioctl() failed: %s", strerror(errno));
157         return 0;
158     }
159 
160     return ifr.ifr_flags & IFF_VNET_HDR;
161 }
162 
163 int tap_probe_has_ufo(int fd)
164 {
165     unsigned offload;
166 
167     offload = TUN_F_CSUM | TUN_F_UFO;
168 
169     if (ioctl(fd, TUNSETOFFLOAD, offload) < 0)
170         return 0;
171 
172     return 1;
173 }
174 
175 /* Verify that we can assign given length */
176 int tap_probe_vnet_hdr_len(int fd, int len)
177 {
178     int orig;
179     if (ioctl(fd, TUNGETVNETHDRSZ, &orig) == -1) {
180         return 0;
181     }
182     if (ioctl(fd, TUNSETVNETHDRSZ, &len) == -1) {
183         return 0;
184     }
185     /* Restore original length: we can't handle failure. */
186     if (ioctl(fd, TUNSETVNETHDRSZ, &orig) == -1) {
187         fprintf(stderr, "TUNGETVNETHDRSZ ioctl() failed: %s. Exiting.\n",
188                 strerror(errno));
189         abort();
190         return -errno;
191     }
192     return 1;
193 }
194 
195 void tap_fd_set_vnet_hdr_len(int fd, int len)
196 {
197     if (ioctl(fd, TUNSETVNETHDRSZ, &len) == -1) {
198         fprintf(stderr, "TUNSETVNETHDRSZ ioctl() failed: %s. Exiting.\n",
199                 strerror(errno));
200         abort();
201     }
202 }
203 
204 int tap_fd_set_vnet_le(int fd, int is_le)
205 {
206     int arg = is_le ? 1 : 0;
207 
208     if (!ioctl(fd, TUNSETVNETLE, &arg)) {
209         return 0;
210     }
211 
212     /* Check if our kernel supports TUNSETVNETLE */
213     if (errno == EINVAL) {
214         return -errno;
215     }
216 
217     error_report("TUNSETVNETLE ioctl() failed: %s.", strerror(errno));
218     abort();
219 }
220 
221 int tap_fd_set_vnet_be(int fd, int is_be)
222 {
223     int arg = is_be ? 1 : 0;
224 
225     if (!ioctl(fd, TUNSETVNETBE, &arg)) {
226         return 0;
227     }
228 
229     /* Check if our kernel supports TUNSETVNETBE */
230     if (errno == EINVAL) {
231         return -errno;
232     }
233 
234     error_report("TUNSETVNETBE ioctl() failed: %s.", strerror(errno));
235     abort();
236 }
237 
238 void tap_fd_set_offload(int fd, int csum, int tso4,
239                         int tso6, int ecn, int ufo)
240 {
241     unsigned int offload = 0;
242 
243     /* Check if our kernel supports TUNSETOFFLOAD */
244     if (ioctl(fd, TUNSETOFFLOAD, 0) != 0 && errno == EINVAL) {
245         return;
246     }
247 
248     if (csum) {
249         offload |= TUN_F_CSUM;
250         if (tso4)
251             offload |= TUN_F_TSO4;
252         if (tso6)
253             offload |= TUN_F_TSO6;
254         if ((tso4 || tso6) && ecn)
255             offload |= TUN_F_TSO_ECN;
256         if (ufo)
257             offload |= TUN_F_UFO;
258     }
259 
260     if (ioctl(fd, TUNSETOFFLOAD, offload) != 0) {
261         offload &= ~TUN_F_UFO;
262         if (ioctl(fd, TUNSETOFFLOAD, offload) != 0) {
263             fprintf(stderr, "TUNSETOFFLOAD ioctl() failed: %s\n",
264                     strerror(errno));
265         }
266     }
267 }
268 
269 /* Enable a specific queue of tap. */
270 int tap_fd_enable(int fd)
271 {
272     struct ifreq ifr;
273     int ret;
274 
275     memset(&ifr, 0, sizeof(ifr));
276 
277     ifr.ifr_flags = IFF_ATTACH_QUEUE;
278     ret = ioctl(fd, TUNSETQUEUE, (void *) &ifr);
279 
280     if (ret != 0) {
281         error_report("could not enable queue");
282     }
283 
284     return ret;
285 }
286 
287 /* Disable a specific queue of tap/ */
288 int tap_fd_disable(int fd)
289 {
290     struct ifreq ifr;
291     int ret;
292 
293     memset(&ifr, 0, sizeof(ifr));
294 
295     ifr.ifr_flags = IFF_DETACH_QUEUE;
296     ret = ioctl(fd, TUNSETQUEUE, (void *) &ifr);
297 
298     if (ret != 0) {
299         error_report("could not disable queue");
300     }
301 
302     return ret;
303 }
304 
305 int tap_fd_get_ifname(int fd, char *ifname)
306 {
307     struct ifreq ifr;
308 
309     if (ioctl(fd, TUNGETIFF, &ifr) != 0) {
310         error_report("TUNGETIFF ioctl() failed: %s",
311                      strerror(errno));
312         return -1;
313     }
314 
315     pstrcpy(ifname, sizeof(ifr.ifr_name), ifr.ifr_name);
316     return 0;
317 }
318