diff options
author | Reinhard Tartler <siretart@tauware.de> | 2019-03-30 10:53:01 -0400 |
---|---|---|
committer | Reinhard Tartler <siretart@tauware.de> | 2019-03-30 10:53:01 -0400 |
commit | 3925631f27136b690f806464227497af42cb529d (patch) | |
tree | c9d6d8eaba72f372ef68fee2d999eda9328e0a41 /slirp4netns.c | |
parent | 2495596df437ed78585cb469e63bec16318c3645 (diff) |
New upstream version 0.3.0
Diffstat (limited to 'slirp4netns.c')
-rw-r--r-- | slirp4netns.c | 352 |
1 files changed, 304 insertions, 48 deletions
diff --git a/slirp4netns.c b/slirp4netns.c index d111a73..b4d580a 100644 --- a/slirp4netns.c +++ b/slirp4netns.c @@ -1,66 +1,293 @@ #define _GNU_SOURCE -#include <assert.h> -#include <stdio.h> +#include <errno.h> #include <signal.h> -#include <arpa/inet.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include <glib.h> -#include "qemu/slirp/slirp.h" -#include "libslirp.h" +#include "qemu/slirp/src/libslirp.h" +#include "api.h" +#include "slirp4netns.h" +/* opaque for SlirpCb */ struct libslirp_data { int tapfd; + GSList *timers; +}; + +/* implements SlirpCb.send_packet */ +static ssize_t libslirp_send_packet(const void *pkt, size_t pkt_len, void *opaque) +{ + struct libslirp_data *data = (struct libslirp_data *)opaque; + return write(data->tapfd, pkt, pkt_len); +} + +/* implements SlirpCb.guest_error */ +static void libslirp_guest_error(const char *msg, void *opaque) +{ + fprintf(stderr, "libslirp: %s\n", msg); +} + +/* implements SlirpCb.clock_get_ns */ +static int64_t libslirp_clock_get_ns(void *opaque) +{ + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return ts.tv_sec * 1000000000LL + ts.tv_nsec; +} + +/* timer for SlirpCb */ +struct timer { + SlirpTimerCb cb; + void *cb_opaque; + int64_t expire_timer_msec; }; -void slirp_output(void *opaque, const uint8_t * pkt, int pkt_len) +/* implements SlirpCb.timer_new */ +static void *libslirp_timer_new(SlirpTimerCb cb, void *cb_opaque, void *opaque) { struct libslirp_data *data = (struct libslirp_data *)opaque; - int rc; - if ((rc = write(data->tapfd, pkt, pkt_len)) < 0) { - perror("slirp_output: write"); + struct timer *t = g_malloc0(sizeof(*t)); + t->cb = cb; + t->cb_opaque = cb_opaque; + t->expire_timer_msec = -1; + data->timers = g_slist_append(data->timers, t); + return t; +} + +/* implements SlirpCb.timer_free */ +static void libslirp_timer_free(void *timer, void *opaque) +{ + struct libslirp_data *data = (struct libslirp_data *)opaque; + data->timers = g_slist_remove(data->timers, timer); + g_free(timer); +} + +/* implements SlirpCb.timer_mod */ +static void libslirp_timer_mod(void *timer, int64_t expire_timer_msec, void *opaque) +{ + struct timer *t = (struct timer *)timer; + t->expire_timer_msec = expire_timer_msec; +} + +/* implements SlirpCb.register_poll_fd */ +static void libslirp_register_poll_fd(int fd, void *opaque) +{ + /* + * NOP + * + * This is NOP on QEMU upstream on Linux as well, see: + * * qemu/net/slirp.c: net_slirp_register_poll_fd (calls qemu_fd_register) + * * qemu/stubs/fd-register.c: qemu_fd_register (NOP on Linux) + * + * See also: + * * qemu/util/main-loop.c: qemu_fd_register (Win32 only) + */ +} + +/* implements SlirpCb.unregister_poll_fd */ +static void libslirp_unregister_poll_fd(int fd, void *opaque) +{ + /* + * NOP + * + * This is NOP on QEMU upstream as well, see: + * * qemu/net/slirp.c: net_slirp_unregister_poll_fd (NOP) + */ +} + +/* implements SlirpCb.notify */ +static void libslirp_notify(void *opaque) +{ + /* + * NOP + * + * This can be NOP on QEMU upstream as well, see: + * * qemu/net/slirp.c: net_slirp_notify (calls qemu_notify_event) + * * qemu/stubs/notify-event.c: qemu_notify_event (NOP) + * + * See also: + * * qemu/util/main-loop.c: qemu_notify_event (NOP if !qemu_aio_context) + */ +} + +static int libslirp_poll_to_gio(int events) +{ + int ret = 0; + if (events & SLIRP_POLL_IN) { + ret |= G_IO_IN; + } + if (events & SLIRP_POLL_OUT) { + ret |= G_IO_OUT; } - assert(rc == pkt_len); + if (events & SLIRP_POLL_PRI) { + ret |= G_IO_PRI; + } + if (events & SLIRP_POLL_ERR) { + ret |= G_IO_ERR; + } + if (events & SLIRP_POLL_HUP) { + ret |= G_IO_HUP; + } + return ret; } -Slirp *create_slirp(void *opaque, unsigned int mtu, bool ip6_enabled) +/* + * implements SlirpAddPollCb used in slirp_pollfds_fill. + * originally from qemu/net/slirp.c:net_slirp_add_poll + */ +static int libslirp_add_poll(int fd, int events, void *opaque) +{ + GArray *pollfds = opaque; + GPollFD pfd = { + .fd = fd, + .events = libslirp_poll_to_gio(events), + }; + int idx = pollfds->len; + g_array_append_val(pollfds, pfd); + return idx; +} + +static int libslirp_gio_to_poll(int events) +{ + int ret = 0; + if (events & G_IO_IN) { + ret |= SLIRP_POLL_IN; + } + if (events & G_IO_OUT) { + ret |= SLIRP_POLL_OUT; + } + if (events & G_IO_PRI) { + ret |= SLIRP_POLL_PRI; + } + if (events & G_IO_ERR) { + ret |= SLIRP_POLL_ERR; + } + if (events & G_IO_HUP) { + ret |= SLIRP_POLL_HUP; + } + return ret; +} + +/* + * implements SlirpGetREventsCB used in slirp_pollfds_poll + * originally from qemu/net/slirp.c:net_slirp_get_revents + */ +static int libslirp_get_revents(int idx, void *opaque) +{ + GArray *pollfds = opaque; + return libslirp_gio_to_poll(g_array_index(pollfds, GPollFD, idx).revents); +} + +/* + * updates timeout_msec for data->timers + * originally from https://github.com/rd235/libslirp/blob/d2b7032e29f3ba98e17414b32c9effffc90f2bb0/src/qemu2libslirp.c#L66 + */ +static void update_ra_timeout(uint32_t * timeout_msec, struct libslirp_data *data) +{ + int64_t now_msec = libslirp_clock_get_ns(data) / 1000000; + GSList *f; + for (f = data->timers; f != NULL; f = f->next) { + struct timer *t = f->data; + if (t->expire_timer_msec != -1) { + int64_t diff = t->expire_timer_msec - now_msec; + if (diff < 0) + diff = 0; + if (diff < *timeout_msec) + *timeout_msec = diff; + } + } +} + +/* + * calls SlirpTimerCb if timed out + * originally from https://github.com/rd235/libslirp/blob/d2b7032e29f3ba98e17414b32c9effffc90f2bb0/src/qemu2libslirp.c#L78 + */ +static void check_ra_timeout(struct libslirp_data *data) +{ + int64_t now_msec = libslirp_clock_get_ns(data) / 1000000; + GSList *f; + for (f = data->timers; f != NULL; f = f->next) { + struct timer *t = f->data; + if (t->expire_timer_msec != -1) { + int64_t diff = t->expire_timer_msec - now_msec; + if (diff <= 0) { + t->expire_timer_msec = -1; + t->cb(t->cb_opaque); + } + } + } +} + +static const SlirpCb libslirp_cb = { + .send_packet = libslirp_send_packet, + .guest_error = libslirp_guest_error, + .clock_get_ns = libslirp_clock_get_ns, + .timer_new = libslirp_timer_new, + .timer_free = libslirp_timer_free, + .timer_mod = libslirp_timer_mod, + .register_poll_fd = libslirp_register_poll_fd, + .unregister_poll_fd = libslirp_unregister_poll_fd, + .notify = libslirp_notify, +}; + +Slirp *create_slirp(void *opaque, struct slirp4netns_config *s4nn) { Slirp *slirp = NULL; - struct in_addr vnetwork, vnetmask, vhost, vdhcp_start, vnameserver; - struct in6_addr vhost6, vprefix_addr6, vnameserver6; - int vprefix_len = 64; - inet_pton(AF_INET, "10.0.2.0", &vnetwork); - inet_pton(AF_INET, "255.255.255.0", &vnetmask); - inet_pton(AF_INET, "10.0.2.2", &vhost); - inet_pton(AF_INET, "10.0.2.3", &vnameserver); - inet_pton(AF_INET, "10.0.2.15", &vdhcp_start); - inet_pton(AF_INET6, "fd00::2", &vhost6); - inet_pton(AF_INET6, "fd00::", &vprefix_addr6); - inet_pton(AF_INET6, "fd00::3", &vnameserver6); - slirp = slirp_init(0 /* restricted */ , 1 /* is_enabled */ , - vnetwork, vnetmask, vhost, (int)ip6_enabled, vprefix_addr6, vprefix_len, vhost6, - NULL /* vhostname */ , NULL /* bootfile */ , vdhcp_start, - vnameserver, vnameserver6, NULL /* vdnssearch */ , NULL /* vdomainname */ , - mtu /* if_mtu */ , mtu /* if_mru */ , - opaque); + SlirpConfig cfg; + memset(&cfg, 0, sizeof(cfg)); + cfg.restricted = 0; + cfg.in_enabled = 1; + cfg.vnetwork = s4nn->vnetwork; + cfg.vnetmask = s4nn->vnetmask; + cfg.vhost = s4nn->vhost; + cfg.in6_enabled = (int)(s4nn->enable_ipv6); + inet_pton(AF_INET6, "fd00::", &cfg.vprefix_addr6); + cfg.vprefix_len = 64; + inet_pton(AF_INET6, "fd00::2", &cfg.vhost6); + cfg.vhostname = NULL; + cfg.tftp_server_name = NULL; + cfg.tftp_path = NULL; + cfg.bootfile = NULL; + cfg.vdhcp_start = s4nn->vdhcp_start; + cfg.vnameserver = s4nn->vnameserver; + inet_pton(AF_INET6, "fd00::3", &cfg.vnameserver6); + cfg.vdnssearch = NULL; + cfg.vdomainname = NULL; + cfg.if_mtu = s4nn->mtu; + cfg.if_mru = s4nn->mtu; + cfg.disable_host_loopback = s4nn->disable_host_loopback; + slirp = slirp_initx(&cfg, &libslirp_cb, opaque); if (slirp == NULL) { - fprintf(stderr, "slirp_init failed\n"); + fprintf(stderr, "slirp_initx failed\n"); } return slirp; } #define ETH_BUF_SIZE (65536) -int do_slirp(int tapfd, int exitfd, unsigned int mtu, bool ip6_enabled) +int do_slirp(int tapfd, int exitfd, const char *api_socket, struct slirp4netns_config *cfg) { int ret = -1; Slirp *slirp = NULL; uint8_t *buf = NULL; - struct libslirp_data opaque = {.tapfd = tapfd }; - GArray pollfds = { 0 }; + struct libslirp_data opaque = {.tapfd = tapfd,.timers = NULL }; + int apifd = -1; + struct api_ctx *apictx = NULL; + GArray *pollfds = g_array_new(FALSE, FALSE, sizeof(GPollFD)); + int pollfds_exitfd_idx = -1; + int pollfds_apifd_idx = -1; size_t n_fds = 1; - struct pollfd tap_pollfd = { tapfd, POLLIN | POLLHUP, 0 }; - struct pollfd exit_pollfd = { exitfd, POLLHUP, 0 }; + GPollFD tap_pollfd = {.fd = tapfd,.events = G_IO_IN | G_IO_HUP,.revents = 0 + }; + GPollFD exit_pollfd = {.fd = exitfd,.events = G_IO_HUP,.revents = 0 + }; + GPollFD api_pollfd = {.fd = -1,.events = G_IO_IN | G_IO_HUP,.revents = 0 + }; - slirp = create_slirp((void *)&opaque, mtu, ip6_enabled); + slirp = create_slirp((void *)&opaque, cfg); if (slirp == NULL) { fprintf(stderr, "create_slirp failed\n"); goto err; @@ -69,26 +296,42 @@ int do_slirp(int tapfd, int exitfd, unsigned int mtu, bool ip6_enabled) if (buf == NULL) { goto err; } - g_array_append_val(&pollfds, tap_pollfd); + g_array_append_val(pollfds, tap_pollfd); if (exitfd >= 0) { n_fds++; - g_array_append_val(&pollfds, exit_pollfd); + g_array_append_val(pollfds, exit_pollfd); + pollfds_exitfd_idx = n_fds - 1; + } + if (api_socket != NULL) { + if ((apifd = api_bindlisten(api_socket)) < 0) { + goto err; + } + if ((apictx = api_ctx_alloc(cfg)) == NULL) { + fprintf(stderr, "api_ctx_alloc failed\n"); + goto err; + } + api_pollfd.fd = apifd; + n_fds++; + g_array_append_val(pollfds, api_pollfd); + pollfds_apifd_idx = n_fds - 1; } signal(SIGPIPE, SIG_IGN); while (1) { int pollout; - uint32_t timeout = -1; - pollfds.len = n_fds; - slirp_pollfds_fill(&pollfds, &timeout); - update_ra_timeout(&timeout); - do - pollout = poll(pollfds.pfd, pollfds.len, timeout); - while (pollout < 0 && errno == EINTR); + GPollFD *pollfds_data; + uint32_t timeout = -1; /* msec */ + g_array_set_size(pollfds, n_fds); + slirp_pollfds_fill(slirp, &timeout, libslirp_add_poll, pollfds); + update_ra_timeout(&timeout, &opaque); + pollfds_data = (GPollFD *)pollfds->data; + do { + pollout = g_poll(pollfds_data, pollfds->len, timeout); + } while (pollout < 0 && errno == EINTR); if (pollout < 0) { goto err; } - if (pollfds.pfd[0].revents) { + if (pollfds_data[0].revents) { ssize_t rc = read(tapfd, buf, ETH_BUF_SIZE); if (rc < 0) { perror("do_slirp: read"); @@ -100,13 +343,21 @@ int do_slirp(int tapfd, int exitfd, unsigned int mtu, bool ip6_enabled) } /* The exitfd is closed. */ - if (exitfd >= 0 && pollfds.pfd[1].revents) { + if (pollfds_exitfd_idx >= 0 && pollfds_data[pollfds_exitfd_idx].revents) { fprintf(stderr, "exitfd event\n"); goto success; } - slirp_pollfds_poll(&pollfds, (pollout <= 0)); - check_ra_timeout(); + if (pollfds_apifd_idx >= 0 && pollfds_data[pollfds_apifd_idx].revents) { + int rc; + fprintf(stderr, "apifd event\n"); + if ((rc = api_handler(slirp, apifd, apictx)) < 0) { + fprintf(stderr, "api_handler: rc=%d\n", rc); + } + } + + slirp_pollfds_poll(slirp, (pollout <= 0), libslirp_get_revents, pollfds); + check_ra_timeout(&opaque); } success: ret = 0; @@ -115,5 +366,10 @@ int do_slirp(int tapfd, int exitfd, unsigned int mtu, bool ip6_enabled) if (buf != NULL) { free(buf); } + if (apictx != NULL) { + api_ctx_free(apictx); + unlink(api_socket); + } + g_array_free(pollfds, TRUE); return ret; } |