]> git.proxmox.com Git - mirror_qemu.git/blobdiff - net/slirp.c
spelling: sytem => system
[mirror_qemu.git] / net / slirp.c
index 78ba96b63f7228e427992625f160fc08cb96c49b..ad3a838e0b46d21112184f695607d35c64afe49f 100644 (file)
 #include "net/slirp.h"
 
 
-#ifndef _WIN32
+#if defined(CONFIG_SLIRP_SMBD)
 #include <pwd.h>
 #include <sys/wait.h>
 #endif
+#include "net/eth.h"
 #include "net/net.h"
 #include "clients.h"
 #include "hub.h"
 #include "monitor/monitor.h"
 #include "qemu/error-report.h"
 #include "qemu/sockets.h"
-#include "slirp/libslirp.h"
+#include <libslirp.h>
 #include "chardev/char-fe.h"
 #include "sysemu/sysemu.h"
 #include "qemu/cutils.h"
 #include "qapi/error.h"
 #include "qapi/qmp/qdict.h"
 #include "util.h"
+#include "migration/register.h"
+#include "migration/qemu-file-types.h"
 
 static int get_str_sep(char *buf, int buf_size, const char **pp, int sep)
 {
@@ -86,8 +89,9 @@ typedef struct SlirpState {
     NetClientState nc;
     QTAILQ_ENTRY(SlirpState) entry;
     Slirp *slirp;
+    Notifier poll_notifier;
     Notifier exit_notifier;
-#ifndef _WIN32
+#if defined(CONFIG_SLIRP_SMBD)
     gchar *smb_dir;
 #endif
     GSList *fwd;
@@ -100,7 +104,7 @@ static QTAILQ_HEAD(, SlirpState) slirp_stacks =
 static int slirp_hostfwd(SlirpState *s, const char *redir_str, Error **errp);
 static int slirp_guestfwd(SlirpState *s, const char *config_str, Error **errp);
 
-#ifndef _WIN32
+#if defined(CONFIG_SLIRP_SMBD)
 static int slirp_smb(SlirpState *s, const char *exported_dir,
                      struct in_addr vserver_addr, Error **errp);
 static void slirp_smb_cleanup(SlirpState *s);
@@ -108,11 +112,21 @@ static void slirp_smb_cleanup(SlirpState *s);
 static inline void slirp_smb_cleanup(SlirpState *s) { }
 #endif
 
-static void net_slirp_output(void *opaque, const uint8_t *pkt, int pkt_len)
+static ssize_t net_slirp_send_packet(const void *pkt, size_t pkt_len,
+                                     void *opaque)
 {
     SlirpState *s = opaque;
+    uint8_t min_pkt[ETH_ZLEN];
+    size_t min_pktsz = sizeof(min_pkt);
 
-    qemu_send_packet(&s->nc, pkt, pkt_len);
+    if (net_peer_needs_padding(&s->nc)) {
+        if (eth_pad_short_frame(min_pkt, &min_pktsz, pkt, pkt_len)) {
+            pkt = min_pkt;
+            pkt_len = min_pktsz;
+        }
+    }
+
+    return qemu_send_packet(&s->nc, pkt, pkt_len);
 }
 
 static ssize_t net_slirp_receive(NetClientState *nc, const uint8_t *buf, size_t size)
@@ -143,6 +157,8 @@ static void net_slirp_cleanup(NetClientState *nc)
     SlirpState *s = DO_UPCAST(SlirpState, nc, nc);
 
     g_slist_free_full(s->fwd, slirp_free_fwd);
+    main_loop_poll_remove_notifier(&s->poll_notifier);
+    unregister_savevm(NULL, "slirp", s->slirp);
     slirp_cleanup(s->slirp);
     if (s->exit_notifier.notify) {
         qemu_remove_exit_notifier(&s->exit_notifier);
@@ -158,46 +174,52 @@ static NetClientInfo net_slirp_info = {
     .cleanup = net_slirp_cleanup,
 };
 
-static void net_slirp_guest_error(const char *msg)
+static void net_slirp_guest_error(const char *msg, void *opaque)
 {
     qemu_log_mask(LOG_GUEST_ERROR, "%s", msg);
 }
 
-static int64_t net_slirp_clock_get_ns(void)
+static int64_t net_slirp_clock_get_ns(void *opaque)
 {
     return qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
 }
 
-static void *net_slirp_timer_new(SlirpTimerCb cb, void *opaque)
+static void *net_slirp_timer_new(SlirpTimerCb cb,
+                                 void *cb_opaque, void *opaque)
 {
     return timer_new_full(NULL, QEMU_CLOCK_VIRTUAL,
                           SCALE_MS, QEMU_TIMER_ATTR_EXTERNAL,
-                          cb, opaque);
+                          cb, cb_opaque);
 }
 
-static void net_slirp_timer_free(void *timer)
+static void net_slirp_timer_free(void *timer, void *opaque)
 {
-    timer_del(timer);
     timer_free(timer);
 }
 
-static void net_slirp_timer_mod(void *timer, int64_t expire_timer)
+static void net_slirp_timer_mod(void *timer, int64_t expire_timer,
+                                void *opaque)
 {
     timer_mod(timer, expire_timer);
 }
 
-static void net_slirp_register_poll_fd(int fd)
+static void net_slirp_register_poll_fd(int fd, void *opaque)
 {
     qemu_fd_register(fd);
 }
 
-static void net_slirp_unregister_poll_fd(int fd)
+static void net_slirp_unregister_poll_fd(int fd, void *opaque)
 {
     /* no qemu_fd_unregister */
 }
 
+static void net_slirp_notify(void *opaque)
+{
+    qemu_notify_event();
+}
+
 static const SlirpCb slirp_cb = {
-    .output = net_slirp_output,
+    .send_packet = net_slirp_send_packet,
     .guest_error = net_slirp_guest_error,
     .clock_get_ns = net_slirp_clock_get_ns,
     .timer_new = net_slirp_timer_new,
@@ -205,6 +227,132 @@ static const SlirpCb slirp_cb = {
     .timer_mod = net_slirp_timer_mod,
     .register_poll_fd = net_slirp_register_poll_fd,
     .unregister_poll_fd = net_slirp_unregister_poll_fd,
+    .notify = net_slirp_notify,
+};
+
+static int slirp_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;
+    }
+    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;
+}
+
+static int net_slirp_add_poll(int fd, int events, void *opaque)
+{
+    GArray *pollfds = opaque;
+    GPollFD pfd = {
+        .fd = fd,
+        .events = slirp_poll_to_gio(events),
+    };
+    int idx = pollfds->len;
+    g_array_append_val(pollfds, pfd);
+    return idx;
+}
+
+static int slirp_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;
+}
+
+static int net_slirp_get_revents(int idx, void *opaque)
+{
+    GArray *pollfds = opaque;
+
+    return slirp_gio_to_poll(g_array_index(pollfds, GPollFD, idx).revents);
+}
+
+static void net_slirp_poll_notify(Notifier *notifier, void *data)
+{
+    MainLoopPoll *poll = data;
+    SlirpState *s = container_of(notifier, SlirpState, poll_notifier);
+
+    switch (poll->state) {
+    case MAIN_LOOP_POLL_FILL:
+        slirp_pollfds_fill(s->slirp, &poll->timeout,
+                           net_slirp_add_poll, poll->pollfds);
+        break;
+    case MAIN_LOOP_POLL_OK:
+    case MAIN_LOOP_POLL_ERR:
+        slirp_pollfds_poll(s->slirp, poll->state == MAIN_LOOP_POLL_ERR,
+                           net_slirp_get_revents, poll->pollfds);
+        break;
+    default:
+        g_assert_not_reached();
+    }
+}
+
+static ssize_t
+net_slirp_stream_read(void *buf, size_t size, void *opaque)
+{
+    QEMUFile *f = opaque;
+
+    return qemu_get_buffer(f, buf, size);
+}
+
+static ssize_t
+net_slirp_stream_write(const void *buf, size_t size, void *opaque)
+{
+    QEMUFile *f = opaque;
+
+    qemu_put_buffer(f, buf, size);
+    if (qemu_file_get_error(f)) {
+        return -1;
+    }
+
+    return size;
+}
+
+static int net_slirp_state_load(QEMUFile *f, void *opaque, int version_id)
+{
+    Slirp *slirp = opaque;
+
+    return slirp_state_load(slirp, version_id, net_slirp_stream_read, f);
+}
+
+static void net_slirp_state_save(QEMUFile *f, void *opaque)
+{
+    Slirp *slirp = opaque;
+
+    slirp_state_save(slirp, net_slirp_stream_write, f);
+}
+
+static SaveVMHandlers savevm_slirp_state = {
+    .save_state = net_slirp_state_save,
+    .load_state = net_slirp_state_load,
 };
 
 static int net_slirp_init(NetClientState *peer, const char *model,
@@ -229,7 +377,7 @@ static int net_slirp_init(NetClientState *peer, const char *model,
     struct in6_addr ip6_prefix;
     struct in6_addr ip6_host;
     struct in6_addr ip6_dns;
-#ifndef _WIN32
+#if defined(CONFIG_SLIRP_SMBD)
     struct in_addr smbsrv = { .s_addr = 0 };
 #endif
     NetClientState *nc;
@@ -317,7 +465,7 @@ static int net_slirp_init(NetClientState *peer, const char *model,
         error_setg(errp, "Failed to parse DNS");
         return -1;
     }
-    if ((dns.s_addr & mask.s_addr) != net.s_addr) {
+    if (restricted && (dns.s_addr & mask.s_addr) != net.s_addr) {
         error_setg(errp, "DNS doesn't belong to network");
         return -1;
     }
@@ -335,11 +483,11 @@ static int net_slirp_init(NetClientState *peer, const char *model,
         return -1;
     }
     if (dhcp.s_addr == host.s_addr || dhcp.s_addr == dns.s_addr) {
-        error_setg(errp, "DNS must be different from host and DNS");
+        error_setg(errp, "DHCP must be different from host and DNS");
         return -1;
     }
 
-#ifndef _WIN32
+#if defined(CONFIG_SLIRP_SMBD)
     if (vsmbserver && !inet_aton(vsmbserver, &smbsrv)) {
         error_setg(errp, "Failed to parse SMB address");
         return -1;
@@ -359,7 +507,8 @@ static int net_slirp_init(NetClientState *peer, const char *model,
     }
     if (vprefix6_len < 0 || vprefix6_len > 126) {
         error_setg(errp,
-                   "Invalid prefix provided (prefix len must be in range 0-126");
+                   "Invalid IPv6 prefix provided "
+                   "(IPv6 prefix length must be between 0 and 126)");
         return -1;
     }
 
@@ -382,7 +531,7 @@ static int net_slirp_init(NetClientState *peer, const char *model,
             error_setg(errp, "Failed to parse IPv6 DNS");
             return -1;
         }
-        if (!in6_equal_net(&ip6_prefix, &ip6_dns, vprefix6_len)) {
+        if (restricted && !in6_equal_net(&ip6_prefix, &ip6_dns, vprefix6_len)) {
             error_setg(errp, "IPv6 DNS doesn't belong to network");
             return -1;
         }
@@ -427,6 +576,21 @@ static int net_slirp_init(NetClientState *peer, const char *model,
                           &slirp_cb, s);
     QTAILQ_INSERT_TAIL(&slirp_stacks, s, entry);
 
+    /*
+     * Make sure the current bitstream version of slirp is 4, to avoid
+     * QEMU migration incompatibilities, if upstream slirp bumped the
+     * version.
+     *
+     * FIXME: use bitfields of features? teach libslirp to save with
+     * specific version?
+     */
+    g_assert(slirp_state_version() == 4);
+    register_savevm_live("slirp", 0, slirp_state_version(),
+                         &savevm_slirp_state, s->slirp);
+
+    s->poll_notifier.notify = net_slirp_poll_notify;
+    main_loop_poll_add_notifier(&s->poll_notifier);
+
     for (config = slirp_configs; config; config = config->next) {
         if (config->flags & SLIRP_CFG_HOSTFWD) {
             if (slirp_hostfwd(s, config->str, errp) < 0) {
@@ -438,7 +602,7 @@ static int net_slirp_init(NetClientState *peer, const char *model,
             }
         }
     }
-#ifndef _WIN32
+#if defined(CONFIG_SLIRP_SMBD)
     if (smb_export) {
         if (slirp_smb(s, smb_export, smbsrv, errp) < 0) {
             goto error;
@@ -455,25 +619,13 @@ error:
     return -1;
 }
 
-static SlirpState *slirp_lookup(Monitor *mon, const char *hub_id,
-                                const char *name)
+static SlirpState *slirp_lookup(Monitor *mon, const char *id)
 {
-    if (name) {
-        NetClientState *nc;
-        if (hub_id) {
-            nc = net_hub_find_client_by_name(strtol(hub_id, NULL, 0), name);
-            if (!nc) {
-                monitor_printf(mon, "unrecognized (hub-id, stackname) pair\n");
-                return NULL;
-            }
-            warn_report("Using 'hub-id' is deprecated, specify the netdev id "
-                        "directly instead");
-        } else {
-            nc = qemu_find_netdev(name);
-            if (!nc) {
-                monitor_printf(mon, "unrecognized netdev id '%s'\n", name);
-                return NULL;
-            }
+    if (id) {
+        NetClientState *nc = qemu_find_netdev(id);
+        if (!nc) {
+            monitor_printf(mon, "unrecognized netdev id '%s'\n", id);
+            return NULL;
         }
         if (strcmp(nc->model, "user")) {
             monitor_printf(mon, "invalid device specified\n");
@@ -500,16 +652,12 @@ void hmp_hostfwd_remove(Monitor *mon, const QDict *qdict)
     int err;
     const char *arg1 = qdict_get_str(qdict, "arg1");
     const char *arg2 = qdict_get_try_str(qdict, "arg2");
-    const char *arg3 = qdict_get_try_str(qdict, "arg3");
 
-    if (arg3) {
-        s = slirp_lookup(mon, arg1, arg2);
-        src_str = arg3;
-    } else if (arg2) {
-        s = slirp_lookup(mon, NULL, arg1);
+    if (arg2) {
+        s = slirp_lookup(mon, arg1);
         src_str = arg2;
     } else {
-        s = slirp_lookup(mon, NULL, NULL);
+        s = slirp_lookup(mon, NULL);
         src_str = arg1;
     }
     if (!s) {
@@ -629,16 +777,12 @@ void hmp_hostfwd_add(Monitor *mon, const QDict *qdict)
     SlirpState *s;
     const char *arg1 = qdict_get_str(qdict, "arg1");
     const char *arg2 = qdict_get_try_str(qdict, "arg2");
-    const char *arg3 = qdict_get_try_str(qdict, "arg3");
 
-    if (arg3) {
-        s = slirp_lookup(mon, arg1, arg2);
-        redir_str = arg3;
-    } else if (arg2) {
-        s = slirp_lookup(mon, NULL, arg1);
+    if (arg2) {
+        s = slirp_lookup(mon, arg1);
         redir_str = arg2;
     } else {
-        s = slirp_lookup(mon, NULL, NULL);
+        s = slirp_lookup(mon, NULL);
         redir_str = arg1;
     }
     if (s) {
@@ -650,7 +794,7 @@ void hmp_hostfwd_add(Monitor *mon, const QDict *qdict)
 
 }
 
-#ifndef _WIN32
+#if defined(CONFIG_SLIRP_SMBD)
 
 /* automatic user mode samba server configuration */
 static void slirp_smb_cleanup(SlirpState *s)
@@ -765,7 +909,7 @@ static int slirp_smb(SlirpState* s, const char *exported_dir,
     return 0;
 }
 
-#endif /* !defined(_WIN32) */
+#endif /* defined(CONFIG_SLIRP_SMBD) */
 
 static int guestfwd_can_read(void *opaque)
 {
@@ -779,13 +923,14 @@ static void guestfwd_read(void *opaque, const uint8_t *buf, int size)
     slirp_socket_recv(fwd->slirp, fwd->server, fwd->port, buf, size);
 }
 
-static int guestfwd_write(const void *buf, size_t len, void *chr)
+static ssize_t guestfwd_write(const void *buf, size_t len, void *chr)
 {
     return qemu_chr_fe_write_all(chr, buf, len);
 }
 
 static int slirp_guestfwd(SlirpState *s, const char *config_str, Error **errp)
 {
+    /* TODO: IPv6 */
     struct in_addr server = { .s_addr = 0 };
     struct GuestFwd *fwd;
     const char *p;
@@ -828,7 +973,7 @@ static int slirp_guestfwd(SlirpState *s, const char *config_str, Error **errp)
          * FIXME: sure we want to support implicit
          * muxed monitors here?
          */
-        Chardev *chr = qemu_chr_new_mux_mon(buf, p);
+        Chardev *chr = qemu_chr_new_mux_mon(buf, p, NULL);
 
         if (!chr) {
             error_setg(errp, "Could not open guest forwarding device '%s'",