summaryrefslogtreecommitdiff
path: root/slirp4netns.c
diff options
context:
space:
mode:
authorReinhard Tartler <siretart@tauware.de>2019-03-30 10:53:01 -0400
committerReinhard Tartler <siretart@tauware.de>2019-03-30 10:53:01 -0400
commit3925631f27136b690f806464227497af42cb529d (patch)
treec9d6d8eaba72f372ef68fee2d999eda9328e0a41 /slirp4netns.c
parent2495596df437ed78585cb469e63bec16318c3645 (diff)
New upstream version 0.3.0
Diffstat (limited to 'slirp4netns.c')
-rw-r--r--slirp4netns.c352
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;
}