Statistics
| Branch: | Revision:

root / net / tap.c @ 8af8ce4d

History | View | Annotate | Download (12 kB)

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 "net/tap.h"
27

    
28
#include "config-host.h"
29

    
30
#include <signal.h>
31
#include <sys/ioctl.h>
32
#include <sys/stat.h>
33
#include <sys/wait.h>
34
#include <sys/socket.h>
35
#include <net/if.h>
36

    
37
#include "net.h"
38
#include "sysemu.h"
39
#include "qemu-char.h"
40
#include "qemu-common.h"
41
#include "qemu-error.h"
42

    
43
#include "net/tap-linux.h"
44

    
45
/* Maximum GSO packet size (64k) plus plenty of room for
46
 * the ethernet and virtio_net headers
47
 */
48
#define TAP_BUFSIZE (4096 + 65536)
49

    
50
typedef struct TAPState {
51
    VLANClientState nc;
52
    int fd;
53
    char down_script[1024];
54
    char down_script_arg[128];
55
    uint8_t buf[TAP_BUFSIZE];
56
    Notifier exit_notifier;
57
    unsigned int read_poll : 1;
58
    unsigned int write_poll : 1;
59
    unsigned int has_vnet_hdr : 1;
60
    unsigned int using_vnet_hdr : 1;
61
    unsigned int has_ufo: 1;
62
} TAPState;
63

    
64
static int launch_script(const char *setup_script, const char *ifname, int fd);
65

    
66
static int tap_can_send(void *opaque);
67
static void tap_send(void *opaque);
68
static void tap_writable(void *opaque);
69

    
70
static void tap_update_fd_handler(TAPState *s)
71
{
72
    qemu_set_fd_handler2(s->fd,
73
                         s->read_poll  ? tap_can_send : NULL,
74
                         s->read_poll  ? tap_send     : NULL,
75
                         s->write_poll ? tap_writable : NULL,
76
                         s);
77
}
78

    
79
static void tap_read_poll(TAPState *s, int enable)
80
{
81
    s->read_poll = !!enable;
82
    tap_update_fd_handler(s);
83
}
84

    
85
static void tap_write_poll(TAPState *s, int enable)
86
{
87
    s->write_poll = !!enable;
88
    tap_update_fd_handler(s);
89
}
90

    
91
static void tap_writable(void *opaque)
92
{
93
    TAPState *s = opaque;
94

    
95
    tap_write_poll(s, 0);
96

    
97
    qemu_flush_queued_packets(&s->nc);
98
}
99

    
100
static ssize_t tap_write_packet(TAPState *s, const struct iovec *iov, int iovcnt)
101
{
102
    ssize_t len;
103

    
104
    do {
105
        len = writev(s->fd, iov, iovcnt);
106
    } while (len == -1 && errno == EINTR);
107

    
108
    if (len == -1 && errno == EAGAIN) {
109
        tap_write_poll(s, 1);
110
        return 0;
111
    }
112

    
113
    return len;
114
}
115

    
116
static ssize_t tap_receive_iov(VLANClientState *nc, const struct iovec *iov,
117
                               int iovcnt)
118
{
119
    TAPState *s = DO_UPCAST(TAPState, nc, nc);
120
    const struct iovec *iovp = iov;
121
    struct iovec iov_copy[iovcnt + 1];
122
    struct virtio_net_hdr hdr = { 0, };
123

    
124
    if (s->has_vnet_hdr && !s->using_vnet_hdr) {
125
        iov_copy[0].iov_base = &hdr;
126
        iov_copy[0].iov_len =  sizeof(hdr);
127
        memcpy(&iov_copy[1], iov, iovcnt * sizeof(*iov));
128
        iovp = iov_copy;
129
        iovcnt++;
130
    }
131

    
132
    return tap_write_packet(s, iovp, iovcnt);
133
}
134

    
135
static ssize_t tap_receive_raw(VLANClientState *nc, const uint8_t *buf, size_t size)
136
{
137
    TAPState *s = DO_UPCAST(TAPState, nc, nc);
138
    struct iovec iov[2];
139
    int iovcnt = 0;
140
    struct virtio_net_hdr hdr = { 0, };
141

    
142
    if (s->has_vnet_hdr) {
143
        iov[iovcnt].iov_base = &hdr;
144
        iov[iovcnt].iov_len  = sizeof(hdr);
145
        iovcnt++;
146
    }
147

    
148
    iov[iovcnt].iov_base = (char *)buf;
149
    iov[iovcnt].iov_len  = size;
150
    iovcnt++;
151

    
152
    return tap_write_packet(s, iov, iovcnt);
153
}
154

    
155
static ssize_t tap_receive(VLANClientState *nc, const uint8_t *buf, size_t size)
156
{
157
    TAPState *s = DO_UPCAST(TAPState, nc, nc);
158
    struct iovec iov[1];
159

    
160
    if (s->has_vnet_hdr && !s->using_vnet_hdr) {
161
        return tap_receive_raw(nc, buf, size);
162
    }
163

    
164
    iov[0].iov_base = (char *)buf;
165
    iov[0].iov_len  = size;
166

    
167
    return tap_write_packet(s, iov, 1);
168
}
169

    
170
static int tap_can_send(void *opaque)
171
{
172
    TAPState *s = opaque;
173

    
174
    return qemu_can_send_packet(&s->nc);
175
}
176

    
177
#ifndef __sun__
178
ssize_t tap_read_packet(int tapfd, uint8_t *buf, int maxlen)
179
{
180
    return read(tapfd, buf, maxlen);
181
}
182
#endif
183

    
184
static void tap_send_completed(VLANClientState *nc, ssize_t len)
185
{
186
    TAPState *s = DO_UPCAST(TAPState, nc, nc);
187
    tap_read_poll(s, 1);
188
}
189

    
190
static void tap_send(void *opaque)
191
{
192
    TAPState *s = opaque;
193
    int size;
194

    
195
    do {
196
        uint8_t *buf = s->buf;
197

    
198
        size = tap_read_packet(s->fd, s->buf, sizeof(s->buf));
199
        if (size <= 0) {
200
            break;
201
        }
202

    
203
        if (s->has_vnet_hdr && !s->using_vnet_hdr) {
204
            buf  += sizeof(struct virtio_net_hdr);
205
            size -= sizeof(struct virtio_net_hdr);
206
        }
207

    
208
        size = qemu_send_packet_async(&s->nc, buf, size, tap_send_completed);
209
        if (size == 0) {
210
            tap_read_poll(s, 0);
211
        }
212
    } while (size > 0 && qemu_can_send_packet(&s->nc));
213
}
214

    
215
int tap_has_ufo(VLANClientState *nc)
216
{
217
    TAPState *s = DO_UPCAST(TAPState, nc, nc);
218

    
219
    assert(nc->info->type == NET_CLIENT_TYPE_TAP);
220

    
221
    return s->has_ufo;
222
}
223

    
224
int tap_has_vnet_hdr(VLANClientState *nc)
225
{
226
    TAPState *s = DO_UPCAST(TAPState, nc, nc);
227

    
228
    assert(nc->info->type == NET_CLIENT_TYPE_TAP);
229

    
230
    return s->has_vnet_hdr;
231
}
232

    
233
void tap_using_vnet_hdr(VLANClientState *nc, int using_vnet_hdr)
234
{
235
    TAPState *s = DO_UPCAST(TAPState, nc, nc);
236

    
237
    using_vnet_hdr = using_vnet_hdr != 0;
238

    
239
    assert(nc->info->type == NET_CLIENT_TYPE_TAP);
240
    assert(s->has_vnet_hdr == using_vnet_hdr);
241

    
242
    s->using_vnet_hdr = using_vnet_hdr;
243
}
244

    
245
void tap_set_offload(VLANClientState *nc, int csum, int tso4,
246
                     int tso6, int ecn, int ufo)
247
{
248
    TAPState *s = DO_UPCAST(TAPState, nc, nc);
249

    
250
    return tap_fd_set_offload(s->fd, csum, tso4, tso6, ecn, ufo);
251
}
252

    
253
static void tap_cleanup(VLANClientState *nc)
254
{
255
    TAPState *s = DO_UPCAST(TAPState, nc, nc);
256

    
257
    qemu_purge_queued_packets(nc);
258

    
259
    if (s->down_script[0])
260
        launch_script(s->down_script, s->down_script_arg, s->fd);
261

    
262
    tap_read_poll(s, 0);
263
    tap_write_poll(s, 0);
264
    close(s->fd);
265
    exit_notifier_remove(&s->exit_notifier);
266
}
267

    
268
/* Instead of exiting gracefully, we're exiting because someone called
269
 * exit(), make sure to invoke down script at least
270
 */
271
static void tap_cleanup_at_exit(Notifier *notifier)
272
{
273
    TAPState *s = container_of(notifier, TAPState, exit_notifier);
274

    
275
    if (s->down_script[0]) {
276
        launch_script(s->down_script, s->down_script_arg, s->fd);
277
    }
278
}
279

    
280
static void tap_poll(VLANClientState *nc, bool enable)
281
{
282
    TAPState *s = DO_UPCAST(TAPState, nc, nc);
283
    tap_read_poll(s, enable);
284
    tap_write_poll(s, enable);
285
}
286

    
287
/* fd support */
288

    
289
static NetClientInfo net_tap_info = {
290
    .type = NET_CLIENT_TYPE_TAP,
291
    .size = sizeof(TAPState),
292
    .receive = tap_receive,
293
    .receive_raw = tap_receive_raw,
294
    .receive_iov = tap_receive_iov,
295
    .poll = tap_poll,
296
    .cleanup = tap_cleanup,
297
};
298

    
299
static TAPState *net_tap_fd_init(VLANState *vlan,
300
                                 const char *model,
301
                                 const char *name,
302
                                 int fd,
303
                                 int vnet_hdr)
304
{
305
    VLANClientState *nc;
306
    TAPState *s;
307

    
308
    nc = qemu_new_net_client(&net_tap_info, vlan, NULL, model, name);
309

    
310
    s = DO_UPCAST(TAPState, nc, nc);
311

    
312
    s->fd = fd;
313
    s->has_vnet_hdr = vnet_hdr != 0;
314
    s->using_vnet_hdr = 0;
315
    s->has_ufo = tap_probe_has_ufo(s->fd);
316
    s->exit_notifier.notify = tap_cleanup_at_exit;
317
    exit_notifier_add(&s->exit_notifier);
318
    tap_set_offload(&s->nc, 0, 0, 0, 0, 0);
319
    tap_read_poll(s, 1);
320
    return s;
321
}
322

    
323
static int launch_script(const char *setup_script, const char *ifname, int fd)
324
{
325
    sigset_t oldmask, mask;
326
    int pid, status;
327
    char *args[3];
328
    char **parg;
329

    
330
    sigemptyset(&mask);
331
    sigaddset(&mask, SIGCHLD);
332
    sigprocmask(SIG_BLOCK, &mask, &oldmask);
333

    
334
    /* try to launch network script */
335
    pid = fork();
336
    if (pid == 0) {
337
        int open_max = sysconf(_SC_OPEN_MAX), i;
338

    
339
        for (i = 0; i < open_max; i++) {
340
            if (i != STDIN_FILENO &&
341
                i != STDOUT_FILENO &&
342
                i != STDERR_FILENO &&
343
                i != fd) {
344
                close(i);
345
            }
346
        }
347
        parg = args;
348
        *parg++ = (char *)setup_script;
349
        *parg++ = (char *)ifname;
350
        *parg++ = NULL;
351
        execv(setup_script, args);
352
        _exit(1);
353
    } else if (pid > 0) {
354
        while (waitpid(pid, &status, 0) != pid) {
355
            /* loop */
356
        }
357
        sigprocmask(SIG_SETMASK, &oldmask, NULL);
358

    
359
        if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
360
            return 0;
361
        }
362
    }
363
    fprintf(stderr, "%s: could not launch network script\n", setup_script);
364
    return -1;
365
}
366

    
367
static int net_tap_init(QemuOpts *opts, int *vnet_hdr)
368
{
369
    int fd, vnet_hdr_required;
370
    char ifname[128] = {0,};
371
    const char *setup_script;
372

    
373
    if (qemu_opt_get(opts, "ifname")) {
374
        pstrcpy(ifname, sizeof(ifname), qemu_opt_get(opts, "ifname"));
375
    }
376

    
377
    *vnet_hdr = qemu_opt_get_bool(opts, "vnet_hdr", 1);
378
    if (qemu_opt_get(opts, "vnet_hdr")) {
379
        vnet_hdr_required = *vnet_hdr;
380
    } else {
381
        vnet_hdr_required = 0;
382
    }
383

    
384
    TFR(fd = tap_open(ifname, sizeof(ifname), vnet_hdr, vnet_hdr_required));
385
    if (fd < 0) {
386
        return -1;
387
    }
388

    
389
    setup_script = qemu_opt_get(opts, "script");
390
    if (setup_script &&
391
        setup_script[0] != '\0' &&
392
        strcmp(setup_script, "no") != 0 &&
393
        launch_script(setup_script, ifname, fd)) {
394
        close(fd);
395
        return -1;
396
    }
397

    
398
    qemu_opt_set(opts, "ifname", ifname);
399

    
400
    return fd;
401
}
402

    
403
int net_init_tap(QemuOpts *opts, Monitor *mon, const char *name, VLANState *vlan)
404
{
405
    TAPState *s;
406
    int fd, vnet_hdr = 0;
407

    
408
    if (qemu_opt_get(opts, "fd")) {
409
        if (qemu_opt_get(opts, "ifname") ||
410
            qemu_opt_get(opts, "script") ||
411
            qemu_opt_get(opts, "downscript") ||
412
            qemu_opt_get(opts, "vnet_hdr")) {
413
            error_report("ifname=, script=, downscript= and vnet_hdr= is invalid with fd=");
414
            return -1;
415
        }
416

    
417
        fd = net_handle_fd_param(mon, qemu_opt_get(opts, "fd"));
418
        if (fd == -1) {
419
            return -1;
420
        }
421

    
422
        fcntl(fd, F_SETFL, O_NONBLOCK);
423

    
424
        vnet_hdr = tap_probe_vnet_hdr(fd);
425
    } else {
426
        if (!qemu_opt_get(opts, "script")) {
427
            qemu_opt_set(opts, "script", DEFAULT_NETWORK_SCRIPT);
428
        }
429

    
430
        if (!qemu_opt_get(opts, "downscript")) {
431
            qemu_opt_set(opts, "downscript", DEFAULT_NETWORK_DOWN_SCRIPT);
432
        }
433

    
434
        fd = net_tap_init(opts, &vnet_hdr);
435
        if (fd == -1) {
436
            return -1;
437
        }
438
    }
439

    
440
    s = net_tap_fd_init(vlan, "tap", name, fd, vnet_hdr);
441
    if (!s) {
442
        close(fd);
443
        return -1;
444
    }
445

    
446
    if (tap_set_sndbuf(s->fd, opts) < 0) {
447
        return -1;
448
    }
449

    
450
    if (qemu_opt_get(opts, "fd")) {
451
        snprintf(s->nc.info_str, sizeof(s->nc.info_str), "fd=%d", fd);
452
    } else {
453
        const char *ifname, *script, *downscript;
454

    
455
        ifname     = qemu_opt_get(opts, "ifname");
456
        script     = qemu_opt_get(opts, "script");
457
        downscript = qemu_opt_get(opts, "downscript");
458

    
459
        snprintf(s->nc.info_str, sizeof(s->nc.info_str),
460
                 "ifname=%s,script=%s,downscript=%s",
461
                 ifname, script, downscript);
462

    
463
        if (strcmp(downscript, "no") != 0) {
464
            snprintf(s->down_script, sizeof(s->down_script), "%s", downscript);
465
            snprintf(s->down_script_arg, sizeof(s->down_script_arg), "%s", ifname);
466
        }
467
    }
468

    
469
    return 0;
470
}