xref: /qemu/net/slirp.c (revision 5a01e99f)
168ac40d2SMark McLoughlin /*
268ac40d2SMark McLoughlin  * QEMU System Emulator
368ac40d2SMark McLoughlin  *
468ac40d2SMark McLoughlin  * Copyright (c) 2003-2008 Fabrice Bellard
568ac40d2SMark McLoughlin  *
668ac40d2SMark McLoughlin  * Permission is hereby granted, free of charge, to any person obtaining a copy
768ac40d2SMark McLoughlin  * of this software and associated documentation files (the "Software"), to deal
868ac40d2SMark McLoughlin  * in the Software without restriction, including without limitation the rights
968ac40d2SMark McLoughlin  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1068ac40d2SMark McLoughlin  * copies of the Software, and to permit persons to whom the Software is
1168ac40d2SMark McLoughlin  * furnished to do so, subject to the following conditions:
1268ac40d2SMark McLoughlin  *
1368ac40d2SMark McLoughlin  * The above copyright notice and this permission notice shall be included in
1468ac40d2SMark McLoughlin  * all copies or substantial portions of the Software.
1568ac40d2SMark McLoughlin  *
1668ac40d2SMark McLoughlin  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1768ac40d2SMark McLoughlin  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1868ac40d2SMark McLoughlin  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
1968ac40d2SMark McLoughlin  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2068ac40d2SMark McLoughlin  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2168ac40d2SMark McLoughlin  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2268ac40d2SMark McLoughlin  * THE SOFTWARE.
2368ac40d2SMark McLoughlin  */
2468ac40d2SMark McLoughlin #include "net/slirp.h"
2568ac40d2SMark McLoughlin 
2668ac40d2SMark McLoughlin #include "config-host.h"
2768ac40d2SMark McLoughlin 
2868ac40d2SMark McLoughlin #include "net.h"
2968ac40d2SMark McLoughlin #include "monitor.h"
3068ac40d2SMark McLoughlin #include "sysemu.h"
3168ac40d2SMark McLoughlin #include "qemu_socket.h"
3268ac40d2SMark McLoughlin #include "slirp/libslirp.h"
3368ac40d2SMark McLoughlin 
3468ac40d2SMark McLoughlin static int get_str_sep(char *buf, int buf_size, const char **pp, int sep)
3568ac40d2SMark McLoughlin {
3668ac40d2SMark McLoughlin     const char *p, *p1;
3768ac40d2SMark McLoughlin     int len;
3868ac40d2SMark McLoughlin     p = *pp;
3968ac40d2SMark McLoughlin     p1 = strchr(p, sep);
4068ac40d2SMark McLoughlin     if (!p1)
4168ac40d2SMark McLoughlin         return -1;
4268ac40d2SMark McLoughlin     len = p1 - p;
4368ac40d2SMark McLoughlin     p1++;
4468ac40d2SMark McLoughlin     if (buf_size > 0) {
4568ac40d2SMark McLoughlin         if (len > buf_size - 1)
4668ac40d2SMark McLoughlin             len = buf_size - 1;
4768ac40d2SMark McLoughlin         memcpy(buf, p, len);
4868ac40d2SMark McLoughlin         buf[len] = '\0';
4968ac40d2SMark McLoughlin     }
5068ac40d2SMark McLoughlin     *pp = p1;
5168ac40d2SMark McLoughlin     return 0;
5268ac40d2SMark McLoughlin }
5368ac40d2SMark McLoughlin 
5468ac40d2SMark McLoughlin /* slirp network adapter */
5568ac40d2SMark McLoughlin 
5668ac40d2SMark McLoughlin #define SLIRP_CFG_HOSTFWD 1
5768ac40d2SMark McLoughlin #define SLIRP_CFG_LEGACY  2
5868ac40d2SMark McLoughlin 
5968ac40d2SMark McLoughlin struct slirp_config_str {
6068ac40d2SMark McLoughlin     struct slirp_config_str *next;
6168ac40d2SMark McLoughlin     int flags;
6268ac40d2SMark McLoughlin     char str[1024];
6368ac40d2SMark McLoughlin     int legacy_format;
6468ac40d2SMark McLoughlin };
6568ac40d2SMark McLoughlin 
6668ac40d2SMark McLoughlin typedef struct SlirpState {
67ce20b5beSMark McLoughlin     VLANClientState nc;
6868ac40d2SMark McLoughlin     QTAILQ_ENTRY(SlirpState) entry;
6968ac40d2SMark McLoughlin     Slirp *slirp;
7068ac40d2SMark McLoughlin #ifndef _WIN32
7168ac40d2SMark McLoughlin     char smb_dir[128];
7268ac40d2SMark McLoughlin #endif
7368ac40d2SMark McLoughlin } SlirpState;
7468ac40d2SMark McLoughlin 
7568ac40d2SMark McLoughlin static struct slirp_config_str *slirp_configs;
7668ac40d2SMark McLoughlin const char *legacy_tftp_prefix;
7768ac40d2SMark McLoughlin const char *legacy_bootp_filename;
7868ac40d2SMark McLoughlin static QTAILQ_HEAD(slirp_stacks, SlirpState) slirp_stacks =
7968ac40d2SMark McLoughlin     QTAILQ_HEAD_INITIALIZER(slirp_stacks);
8068ac40d2SMark McLoughlin 
8168ac40d2SMark McLoughlin static int slirp_hostfwd(SlirpState *s, const char *redir_str,
8268ac40d2SMark McLoughlin                          int legacy_format);
8368ac40d2SMark McLoughlin static int slirp_guestfwd(SlirpState *s, const char *config_str,
8468ac40d2SMark McLoughlin                           int legacy_format);
8568ac40d2SMark McLoughlin 
8668ac40d2SMark McLoughlin #ifndef _WIN32
8768ac40d2SMark McLoughlin static const char *legacy_smb_export;
8868ac40d2SMark McLoughlin 
8968ac40d2SMark McLoughlin static int slirp_smb(SlirpState *s, const char *exported_dir,
9068ac40d2SMark McLoughlin                      struct in_addr vserver_addr);
9168ac40d2SMark McLoughlin static void slirp_smb_cleanup(SlirpState *s);
9268ac40d2SMark McLoughlin #else
9368ac40d2SMark McLoughlin static inline void slirp_smb_cleanup(SlirpState *s) { }
9468ac40d2SMark McLoughlin #endif
9568ac40d2SMark McLoughlin 
9668ac40d2SMark McLoughlin int slirp_can_output(void *opaque)
9768ac40d2SMark McLoughlin {
9868ac40d2SMark McLoughlin     SlirpState *s = opaque;
9968ac40d2SMark McLoughlin 
100ce20b5beSMark McLoughlin     return qemu_can_send_packet(&s->nc);
10168ac40d2SMark McLoughlin }
10268ac40d2SMark McLoughlin 
10368ac40d2SMark McLoughlin void slirp_output(void *opaque, const uint8_t *pkt, int pkt_len)
10468ac40d2SMark McLoughlin {
10568ac40d2SMark McLoughlin     SlirpState *s = opaque;
10668ac40d2SMark McLoughlin 
107ce20b5beSMark McLoughlin     qemu_send_packet(&s->nc, pkt, pkt_len);
10868ac40d2SMark McLoughlin }
10968ac40d2SMark McLoughlin 
110ce20b5beSMark McLoughlin static ssize_t net_slirp_receive(VLANClientState *nc, const uint8_t *buf, size_t size)
11168ac40d2SMark McLoughlin {
112ce20b5beSMark McLoughlin     SlirpState *s = DO_UPCAST(SlirpState, nc, nc);
11368ac40d2SMark McLoughlin 
11468ac40d2SMark McLoughlin     slirp_input(s->slirp, buf, size);
11568ac40d2SMark McLoughlin 
11668ac40d2SMark McLoughlin     return size;
11768ac40d2SMark McLoughlin }
11868ac40d2SMark McLoughlin 
119ce20b5beSMark McLoughlin static void net_slirp_cleanup(VLANClientState *nc)
12068ac40d2SMark McLoughlin {
121ce20b5beSMark McLoughlin     SlirpState *s = DO_UPCAST(SlirpState, nc, nc);
12268ac40d2SMark McLoughlin 
12368ac40d2SMark McLoughlin     slirp_cleanup(s->slirp);
12468ac40d2SMark McLoughlin     slirp_smb_cleanup(s);
12568ac40d2SMark McLoughlin     QTAILQ_REMOVE(&slirp_stacks, s, entry);
12668ac40d2SMark McLoughlin }
12768ac40d2SMark McLoughlin 
128ce20b5beSMark McLoughlin static NetClientInfo net_slirp_info = {
129ce20b5beSMark McLoughlin     .type = NET_CLIENT_TYPE_SLIRP,
130ce20b5beSMark McLoughlin     .size = sizeof(SlirpState),
131ce20b5beSMark McLoughlin     .receive = net_slirp_receive,
132ce20b5beSMark McLoughlin     .cleanup = net_slirp_cleanup,
133ce20b5beSMark McLoughlin };
134ce20b5beSMark McLoughlin 
13568ac40d2SMark McLoughlin static int net_slirp_init(VLANState *vlan, const char *model,
13668ac40d2SMark McLoughlin                           const char *name, int restricted,
13768ac40d2SMark McLoughlin                           const char *vnetwork, const char *vhost,
13868ac40d2SMark McLoughlin                           const char *vhostname, const char *tftp_export,
13968ac40d2SMark McLoughlin                           const char *bootfile, const char *vdhcp_start,
14068ac40d2SMark McLoughlin                           const char *vnameserver, const char *smb_export,
14168ac40d2SMark McLoughlin                           const char *vsmbserver)
14268ac40d2SMark McLoughlin {
14368ac40d2SMark McLoughlin     /* default settings according to historic slirp */
14468ac40d2SMark McLoughlin     struct in_addr net  = { .s_addr = htonl(0x0a000200) }; /* 10.0.2.0 */
14568ac40d2SMark McLoughlin     struct in_addr mask = { .s_addr = htonl(0xffffff00) }; /* 255.255.255.0 */
14668ac40d2SMark McLoughlin     struct in_addr host = { .s_addr = htonl(0x0a000202) }; /* 10.0.2.2 */
14768ac40d2SMark McLoughlin     struct in_addr dhcp = { .s_addr = htonl(0x0a00020f) }; /* 10.0.2.15 */
14868ac40d2SMark McLoughlin     struct in_addr dns  = { .s_addr = htonl(0x0a000203) }; /* 10.0.2.3 */
14968ac40d2SMark McLoughlin #ifndef _WIN32
15068ac40d2SMark McLoughlin     struct in_addr smbsrv = { .s_addr = 0 };
15168ac40d2SMark McLoughlin #endif
152ce20b5beSMark McLoughlin     VLANClientState *nc;
15368ac40d2SMark McLoughlin     SlirpState *s;
15468ac40d2SMark McLoughlin     char buf[20];
15568ac40d2SMark McLoughlin     uint32_t addr;
15668ac40d2SMark McLoughlin     int shift;
15768ac40d2SMark McLoughlin     char *end;
15868ac40d2SMark McLoughlin     struct slirp_config_str *config;
15968ac40d2SMark McLoughlin 
16068ac40d2SMark McLoughlin     if (!tftp_export) {
16168ac40d2SMark McLoughlin         tftp_export = legacy_tftp_prefix;
16268ac40d2SMark McLoughlin     }
16368ac40d2SMark McLoughlin     if (!bootfile) {
16468ac40d2SMark McLoughlin         bootfile = legacy_bootp_filename;
16568ac40d2SMark McLoughlin     }
16668ac40d2SMark McLoughlin 
16768ac40d2SMark McLoughlin     if (vnetwork) {
16868ac40d2SMark McLoughlin         if (get_str_sep(buf, sizeof(buf), &vnetwork, '/') < 0) {
16968ac40d2SMark McLoughlin             if (!inet_aton(vnetwork, &net)) {
17068ac40d2SMark McLoughlin                 return -1;
17168ac40d2SMark McLoughlin             }
17268ac40d2SMark McLoughlin             addr = ntohl(net.s_addr);
17368ac40d2SMark McLoughlin             if (!(addr & 0x80000000)) {
17468ac40d2SMark McLoughlin                 mask.s_addr = htonl(0xff000000); /* class A */
17568ac40d2SMark McLoughlin             } else if ((addr & 0xfff00000) == 0xac100000) {
17668ac40d2SMark McLoughlin                 mask.s_addr = htonl(0xfff00000); /* priv. 172.16.0.0/12 */
17768ac40d2SMark McLoughlin             } else if ((addr & 0xc0000000) == 0x80000000) {
17868ac40d2SMark McLoughlin                 mask.s_addr = htonl(0xffff0000); /* class B */
17968ac40d2SMark McLoughlin             } else if ((addr & 0xffff0000) == 0xc0a80000) {
18068ac40d2SMark McLoughlin                 mask.s_addr = htonl(0xffff0000); /* priv. 192.168.0.0/16 */
18168ac40d2SMark McLoughlin             } else if ((addr & 0xffff0000) == 0xc6120000) {
18268ac40d2SMark McLoughlin                 mask.s_addr = htonl(0xfffe0000); /* tests 198.18.0.0/15 */
18368ac40d2SMark McLoughlin             } else if ((addr & 0xe0000000) == 0xe0000000) {
18468ac40d2SMark McLoughlin                 mask.s_addr = htonl(0xffffff00); /* class C */
18568ac40d2SMark McLoughlin             } else {
18668ac40d2SMark McLoughlin                 mask.s_addr = htonl(0xfffffff0); /* multicast/reserved */
18768ac40d2SMark McLoughlin             }
18868ac40d2SMark McLoughlin         } else {
18968ac40d2SMark McLoughlin             if (!inet_aton(buf, &net)) {
19068ac40d2SMark McLoughlin                 return -1;
19168ac40d2SMark McLoughlin             }
19268ac40d2SMark McLoughlin             shift = strtol(vnetwork, &end, 10);
19368ac40d2SMark McLoughlin             if (*end != '\0') {
19468ac40d2SMark McLoughlin                 if (!inet_aton(vnetwork, &mask)) {
19568ac40d2SMark McLoughlin                     return -1;
19668ac40d2SMark McLoughlin                 }
19768ac40d2SMark McLoughlin             } else if (shift < 4 || shift > 32) {
19868ac40d2SMark McLoughlin                 return -1;
19968ac40d2SMark McLoughlin             } else {
20068ac40d2SMark McLoughlin                 mask.s_addr = htonl(0xffffffff << (32 - shift));
20168ac40d2SMark McLoughlin             }
20268ac40d2SMark McLoughlin         }
20368ac40d2SMark McLoughlin         net.s_addr &= mask.s_addr;
20468ac40d2SMark McLoughlin         host.s_addr = net.s_addr | (htonl(0x0202) & ~mask.s_addr);
20568ac40d2SMark McLoughlin         dhcp.s_addr = net.s_addr | (htonl(0x020f) & ~mask.s_addr);
20668ac40d2SMark McLoughlin         dns.s_addr  = net.s_addr | (htonl(0x0203) & ~mask.s_addr);
20768ac40d2SMark McLoughlin     }
20868ac40d2SMark McLoughlin 
20968ac40d2SMark McLoughlin     if (vhost && !inet_aton(vhost, &host)) {
21068ac40d2SMark McLoughlin         return -1;
21168ac40d2SMark McLoughlin     }
21268ac40d2SMark McLoughlin     if ((host.s_addr & mask.s_addr) != net.s_addr) {
21368ac40d2SMark McLoughlin         return -1;
21468ac40d2SMark McLoughlin     }
21568ac40d2SMark McLoughlin 
21668ac40d2SMark McLoughlin     if (vdhcp_start && !inet_aton(vdhcp_start, &dhcp)) {
21768ac40d2SMark McLoughlin         return -1;
21868ac40d2SMark McLoughlin     }
21968ac40d2SMark McLoughlin     if ((dhcp.s_addr & mask.s_addr) != net.s_addr ||
22068ac40d2SMark McLoughlin         dhcp.s_addr == host.s_addr || dhcp.s_addr == dns.s_addr) {
22168ac40d2SMark McLoughlin         return -1;
22268ac40d2SMark McLoughlin     }
22368ac40d2SMark McLoughlin 
22468ac40d2SMark McLoughlin     if (vnameserver && !inet_aton(vnameserver, &dns)) {
22568ac40d2SMark McLoughlin         return -1;
22668ac40d2SMark McLoughlin     }
22768ac40d2SMark McLoughlin     if ((dns.s_addr & mask.s_addr) != net.s_addr ||
22868ac40d2SMark McLoughlin         dns.s_addr == host.s_addr) {
22968ac40d2SMark McLoughlin         return -1;
23068ac40d2SMark McLoughlin     }
23168ac40d2SMark McLoughlin 
23268ac40d2SMark McLoughlin #ifndef _WIN32
23368ac40d2SMark McLoughlin     if (vsmbserver && !inet_aton(vsmbserver, &smbsrv)) {
23468ac40d2SMark McLoughlin         return -1;
23568ac40d2SMark McLoughlin     }
23668ac40d2SMark McLoughlin #endif
23768ac40d2SMark McLoughlin 
238ce20b5beSMark McLoughlin     nc = qemu_new_net_client(&net_slirp_info, vlan, NULL, model, name);
239ce20b5beSMark McLoughlin 
240ce20b5beSMark McLoughlin     snprintf(nc->info_str, sizeof(nc->info_str),
241ce20b5beSMark McLoughlin              "net=%s, restricted=%c", inet_ntoa(net), restricted ? 'y' : 'n');
242ce20b5beSMark McLoughlin 
243ce20b5beSMark McLoughlin     s = DO_UPCAST(SlirpState, nc, nc);
244ce20b5beSMark McLoughlin 
24568ac40d2SMark McLoughlin     s->slirp = slirp_init(restricted, net, mask, host, vhostname,
24668ac40d2SMark McLoughlin                           tftp_export, bootfile, dhcp, dns, s);
24768ac40d2SMark McLoughlin     QTAILQ_INSERT_TAIL(&slirp_stacks, s, entry);
24868ac40d2SMark McLoughlin 
24968ac40d2SMark McLoughlin     for (config = slirp_configs; config; config = config->next) {
25068ac40d2SMark McLoughlin         if (config->flags & SLIRP_CFG_HOSTFWD) {
25168ac40d2SMark McLoughlin             if (slirp_hostfwd(s, config->str,
25268ac40d2SMark McLoughlin                               config->flags & SLIRP_CFG_LEGACY) < 0)
253ce20b5beSMark McLoughlin                 goto error;
25468ac40d2SMark McLoughlin         } else {
25568ac40d2SMark McLoughlin             if (slirp_guestfwd(s, config->str,
25668ac40d2SMark McLoughlin                                config->flags & SLIRP_CFG_LEGACY) < 0)
257ce20b5beSMark McLoughlin                 goto error;
25868ac40d2SMark McLoughlin         }
25968ac40d2SMark McLoughlin     }
26068ac40d2SMark McLoughlin #ifndef _WIN32
26168ac40d2SMark McLoughlin     if (!smb_export) {
26268ac40d2SMark McLoughlin         smb_export = legacy_smb_export;
26368ac40d2SMark McLoughlin     }
26468ac40d2SMark McLoughlin     if (smb_export) {
26568ac40d2SMark McLoughlin         if (slirp_smb(s, smb_export, smbsrv) < 0)
266ce20b5beSMark McLoughlin             goto error;
26768ac40d2SMark McLoughlin     }
26868ac40d2SMark McLoughlin #endif
26968ac40d2SMark McLoughlin 
27068ac40d2SMark McLoughlin     return 0;
271ce20b5beSMark McLoughlin 
272ce20b5beSMark McLoughlin error:
273ce20b5beSMark McLoughlin     qemu_del_vlan_client(nc);
274ce20b5beSMark McLoughlin     return -1;
27568ac40d2SMark McLoughlin }
27668ac40d2SMark McLoughlin 
27768ac40d2SMark McLoughlin static SlirpState *slirp_lookup(Monitor *mon, const char *vlan,
27868ac40d2SMark McLoughlin                                 const char *stack)
27968ac40d2SMark McLoughlin {
28068ac40d2SMark McLoughlin 
28168ac40d2SMark McLoughlin     if (vlan) {
282ce20b5beSMark McLoughlin         VLANClientState *nc;
283ce20b5beSMark McLoughlin         nc = qemu_find_vlan_client_by_name(mon, strtol(vlan, NULL, 0), stack);
284ce20b5beSMark McLoughlin         if (!nc) {
28568ac40d2SMark McLoughlin             return NULL;
28668ac40d2SMark McLoughlin         }
287ce20b5beSMark McLoughlin         if (strcmp(nc->model, "user")) {
28868ac40d2SMark McLoughlin             monitor_printf(mon, "invalid device specified\n");
28968ac40d2SMark McLoughlin             return NULL;
29068ac40d2SMark McLoughlin         }
291ce20b5beSMark McLoughlin         return DO_UPCAST(SlirpState, nc, nc);
29268ac40d2SMark McLoughlin     } else {
29368ac40d2SMark McLoughlin         if (QTAILQ_EMPTY(&slirp_stacks)) {
29468ac40d2SMark McLoughlin             monitor_printf(mon, "user mode network stack not in use\n");
29568ac40d2SMark McLoughlin             return NULL;
29668ac40d2SMark McLoughlin         }
29768ac40d2SMark McLoughlin         return QTAILQ_FIRST(&slirp_stacks);
29868ac40d2SMark McLoughlin     }
29968ac40d2SMark McLoughlin }
30068ac40d2SMark McLoughlin 
30168ac40d2SMark McLoughlin void net_slirp_hostfwd_remove(Monitor *mon, const QDict *qdict)
30268ac40d2SMark McLoughlin {
30368ac40d2SMark McLoughlin     struct in_addr host_addr = { .s_addr = INADDR_ANY };
30468ac40d2SMark McLoughlin     int host_port;
30568ac40d2SMark McLoughlin     char buf[256] = "";
30668ac40d2SMark McLoughlin     const char *src_str, *p;
30768ac40d2SMark McLoughlin     SlirpState *s;
30868ac40d2SMark McLoughlin     int is_udp = 0;
30968ac40d2SMark McLoughlin     int err;
31068ac40d2SMark McLoughlin     const char *arg1 = qdict_get_str(qdict, "arg1");
31168ac40d2SMark McLoughlin     const char *arg2 = qdict_get_try_str(qdict, "arg2");
31268ac40d2SMark McLoughlin     const char *arg3 = qdict_get_try_str(qdict, "arg3");
31368ac40d2SMark McLoughlin 
31468ac40d2SMark McLoughlin     if (arg2) {
31568ac40d2SMark McLoughlin         s = slirp_lookup(mon, arg1, arg2);
31668ac40d2SMark McLoughlin         src_str = arg3;
31768ac40d2SMark McLoughlin     } else {
31868ac40d2SMark McLoughlin         s = slirp_lookup(mon, NULL, NULL);
31968ac40d2SMark McLoughlin         src_str = arg1;
32068ac40d2SMark McLoughlin     }
32168ac40d2SMark McLoughlin     if (!s) {
32268ac40d2SMark McLoughlin         return;
32368ac40d2SMark McLoughlin     }
32468ac40d2SMark McLoughlin 
32568ac40d2SMark McLoughlin     if (!src_str || !src_str[0])
32668ac40d2SMark McLoughlin         goto fail_syntax;
32768ac40d2SMark McLoughlin 
32868ac40d2SMark McLoughlin     p = src_str;
32968ac40d2SMark McLoughlin     get_str_sep(buf, sizeof(buf), &p, ':');
33068ac40d2SMark McLoughlin 
33168ac40d2SMark McLoughlin     if (!strcmp(buf, "tcp") || buf[0] == '\0') {
33268ac40d2SMark McLoughlin         is_udp = 0;
33368ac40d2SMark McLoughlin     } else if (!strcmp(buf, "udp")) {
33468ac40d2SMark McLoughlin         is_udp = 1;
33568ac40d2SMark McLoughlin     } else {
33668ac40d2SMark McLoughlin         goto fail_syntax;
33768ac40d2SMark McLoughlin     }
33868ac40d2SMark McLoughlin 
33968ac40d2SMark McLoughlin     if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
34068ac40d2SMark McLoughlin         goto fail_syntax;
34168ac40d2SMark McLoughlin     }
34268ac40d2SMark McLoughlin     if (buf[0] != '\0' && !inet_aton(buf, &host_addr)) {
34368ac40d2SMark McLoughlin         goto fail_syntax;
34468ac40d2SMark McLoughlin     }
34568ac40d2SMark McLoughlin 
34668ac40d2SMark McLoughlin     host_port = atoi(p);
34768ac40d2SMark McLoughlin 
34868ac40d2SMark McLoughlin     err = slirp_remove_hostfwd(QTAILQ_FIRST(&slirp_stacks)->slirp, is_udp,
34968ac40d2SMark McLoughlin                                host_addr, host_port);
35068ac40d2SMark McLoughlin 
35168ac40d2SMark McLoughlin     monitor_printf(mon, "host forwarding rule for %s %s\n", src_str,
35268ac40d2SMark McLoughlin                    err ? "removed" : "not found");
35368ac40d2SMark McLoughlin     return;
35468ac40d2SMark McLoughlin 
35568ac40d2SMark McLoughlin  fail_syntax:
35668ac40d2SMark McLoughlin     monitor_printf(mon, "invalid format\n");
35768ac40d2SMark McLoughlin }
35868ac40d2SMark McLoughlin 
35968ac40d2SMark McLoughlin static int slirp_hostfwd(SlirpState *s, const char *redir_str,
36068ac40d2SMark McLoughlin                          int legacy_format)
36168ac40d2SMark McLoughlin {
36268ac40d2SMark McLoughlin     struct in_addr host_addr = { .s_addr = INADDR_ANY };
36368ac40d2SMark McLoughlin     struct in_addr guest_addr = { .s_addr = 0 };
36468ac40d2SMark McLoughlin     int host_port, guest_port;
36568ac40d2SMark McLoughlin     const char *p;
36668ac40d2SMark McLoughlin     char buf[256];
36768ac40d2SMark McLoughlin     int is_udp;
36868ac40d2SMark McLoughlin     char *end;
36968ac40d2SMark McLoughlin 
37068ac40d2SMark McLoughlin     p = redir_str;
37168ac40d2SMark McLoughlin     if (!p || get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
37268ac40d2SMark McLoughlin         goto fail_syntax;
37368ac40d2SMark McLoughlin     }
37468ac40d2SMark McLoughlin     if (!strcmp(buf, "tcp") || buf[0] == '\0') {
37568ac40d2SMark McLoughlin         is_udp = 0;
37668ac40d2SMark McLoughlin     } else if (!strcmp(buf, "udp")) {
37768ac40d2SMark McLoughlin         is_udp = 1;
37868ac40d2SMark McLoughlin     } else {
37968ac40d2SMark McLoughlin         goto fail_syntax;
38068ac40d2SMark McLoughlin     }
38168ac40d2SMark McLoughlin 
38268ac40d2SMark McLoughlin     if (!legacy_format) {
38368ac40d2SMark McLoughlin         if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
38468ac40d2SMark McLoughlin             goto fail_syntax;
38568ac40d2SMark McLoughlin         }
38668ac40d2SMark McLoughlin         if (buf[0] != '\0' && !inet_aton(buf, &host_addr)) {
38768ac40d2SMark McLoughlin             goto fail_syntax;
38868ac40d2SMark McLoughlin         }
38968ac40d2SMark McLoughlin     }
39068ac40d2SMark McLoughlin 
39168ac40d2SMark McLoughlin     if (get_str_sep(buf, sizeof(buf), &p, legacy_format ? ':' : '-') < 0) {
39268ac40d2SMark McLoughlin         goto fail_syntax;
39368ac40d2SMark McLoughlin     }
39468ac40d2SMark McLoughlin     host_port = strtol(buf, &end, 0);
39568ac40d2SMark McLoughlin     if (*end != '\0' || host_port < 1 || host_port > 65535) {
39668ac40d2SMark McLoughlin         goto fail_syntax;
39768ac40d2SMark McLoughlin     }
39868ac40d2SMark McLoughlin 
39968ac40d2SMark McLoughlin     if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
40068ac40d2SMark McLoughlin         goto fail_syntax;
40168ac40d2SMark McLoughlin     }
40268ac40d2SMark McLoughlin     if (buf[0] != '\0' && !inet_aton(buf, &guest_addr)) {
40368ac40d2SMark McLoughlin         goto fail_syntax;
40468ac40d2SMark McLoughlin     }
40568ac40d2SMark McLoughlin 
40668ac40d2SMark McLoughlin     guest_port = strtol(p, &end, 0);
40768ac40d2SMark McLoughlin     if (*end != '\0' || guest_port < 1 || guest_port > 65535) {
40868ac40d2SMark McLoughlin         goto fail_syntax;
40968ac40d2SMark McLoughlin     }
41068ac40d2SMark McLoughlin 
41168ac40d2SMark McLoughlin     if (slirp_add_hostfwd(s->slirp, is_udp, host_addr, host_port, guest_addr,
41268ac40d2SMark McLoughlin                           guest_port) < 0) {
41368ac40d2SMark McLoughlin         qemu_error("could not set up host forwarding rule '%s'\n",
41468ac40d2SMark McLoughlin                    redir_str);
41568ac40d2SMark McLoughlin         return -1;
41668ac40d2SMark McLoughlin     }
41768ac40d2SMark McLoughlin     return 0;
41868ac40d2SMark McLoughlin 
41968ac40d2SMark McLoughlin  fail_syntax:
42068ac40d2SMark McLoughlin     qemu_error("invalid host forwarding rule '%s'\n", redir_str);
42168ac40d2SMark McLoughlin     return -1;
42268ac40d2SMark McLoughlin }
42368ac40d2SMark McLoughlin 
42468ac40d2SMark McLoughlin void net_slirp_hostfwd_add(Monitor *mon, const QDict *qdict)
42568ac40d2SMark McLoughlin {
42668ac40d2SMark McLoughlin     const char *redir_str;
42768ac40d2SMark McLoughlin     SlirpState *s;
42868ac40d2SMark McLoughlin     const char *arg1 = qdict_get_str(qdict, "arg1");
42968ac40d2SMark McLoughlin     const char *arg2 = qdict_get_try_str(qdict, "arg2");
43068ac40d2SMark McLoughlin     const char *arg3 = qdict_get_try_str(qdict, "arg3");
43168ac40d2SMark McLoughlin 
43268ac40d2SMark McLoughlin     if (arg2) {
43368ac40d2SMark McLoughlin         s = slirp_lookup(mon, arg1, arg2);
43468ac40d2SMark McLoughlin         redir_str = arg3;
43568ac40d2SMark McLoughlin     } else {
43668ac40d2SMark McLoughlin         s = slirp_lookup(mon, NULL, NULL);
43768ac40d2SMark McLoughlin         redir_str = arg1;
43868ac40d2SMark McLoughlin     }
43968ac40d2SMark McLoughlin     if (s) {
44068ac40d2SMark McLoughlin         slirp_hostfwd(s, redir_str, 0);
44168ac40d2SMark McLoughlin     }
44268ac40d2SMark McLoughlin 
44368ac40d2SMark McLoughlin }
44468ac40d2SMark McLoughlin 
44568ac40d2SMark McLoughlin int net_slirp_redir(const char *redir_str)
44668ac40d2SMark McLoughlin {
44768ac40d2SMark McLoughlin     struct slirp_config_str *config;
44868ac40d2SMark McLoughlin 
44968ac40d2SMark McLoughlin     if (QTAILQ_EMPTY(&slirp_stacks)) {
45068ac40d2SMark McLoughlin         config = qemu_malloc(sizeof(*config));
45168ac40d2SMark McLoughlin         pstrcpy(config->str, sizeof(config->str), redir_str);
45268ac40d2SMark McLoughlin         config->flags = SLIRP_CFG_HOSTFWD | SLIRP_CFG_LEGACY;
45368ac40d2SMark McLoughlin         config->next = slirp_configs;
45468ac40d2SMark McLoughlin         slirp_configs = config;
45568ac40d2SMark McLoughlin         return 0;
45668ac40d2SMark McLoughlin     }
45768ac40d2SMark McLoughlin 
45868ac40d2SMark McLoughlin     return slirp_hostfwd(QTAILQ_FIRST(&slirp_stacks), redir_str, 1);
45968ac40d2SMark McLoughlin }
46068ac40d2SMark McLoughlin 
46168ac40d2SMark McLoughlin #ifndef _WIN32
46268ac40d2SMark McLoughlin 
46368ac40d2SMark McLoughlin /* automatic user mode samba server configuration */
46468ac40d2SMark McLoughlin static void slirp_smb_cleanup(SlirpState *s)
46568ac40d2SMark McLoughlin {
46668ac40d2SMark McLoughlin     char cmd[128];
467*5a01e99fSKirill A. Shutemov     int ret;
46868ac40d2SMark McLoughlin 
46968ac40d2SMark McLoughlin     if (s->smb_dir[0] != '\0') {
47068ac40d2SMark McLoughlin         snprintf(cmd, sizeof(cmd), "rm -rf %s", s->smb_dir);
471*5a01e99fSKirill A. Shutemov         ret = system(cmd);
472*5a01e99fSKirill A. Shutemov         if (!WIFEXITED(ret)) {
473*5a01e99fSKirill A. Shutemov             qemu_error("'%s' failed.\n", cmd);
474*5a01e99fSKirill A. Shutemov         } else if (WEXITSTATUS(ret)) {
475*5a01e99fSKirill A. Shutemov             qemu_error("'%s' failed. Error code: %d\n",
476*5a01e99fSKirill A. Shutemov                     cmd, WEXITSTATUS(ret));
477*5a01e99fSKirill A. Shutemov         }
47868ac40d2SMark McLoughlin         s->smb_dir[0] = '\0';
47968ac40d2SMark McLoughlin     }
48068ac40d2SMark McLoughlin }
48168ac40d2SMark McLoughlin 
48268ac40d2SMark McLoughlin static int slirp_smb(SlirpState* s, const char *exported_dir,
48368ac40d2SMark McLoughlin                      struct in_addr vserver_addr)
48468ac40d2SMark McLoughlin {
48568ac40d2SMark McLoughlin     static int instance;
48668ac40d2SMark McLoughlin     char smb_conf[128];
48768ac40d2SMark McLoughlin     char smb_cmdline[128];
48868ac40d2SMark McLoughlin     FILE *f;
48968ac40d2SMark McLoughlin 
49068ac40d2SMark McLoughlin     snprintf(s->smb_dir, sizeof(s->smb_dir), "/tmp/qemu-smb.%ld-%d",
49168ac40d2SMark McLoughlin              (long)getpid(), instance++);
49268ac40d2SMark McLoughlin     if (mkdir(s->smb_dir, 0700) < 0) {
49368ac40d2SMark McLoughlin         qemu_error("could not create samba server dir '%s'\n", s->smb_dir);
49468ac40d2SMark McLoughlin         return -1;
49568ac40d2SMark McLoughlin     }
49668ac40d2SMark McLoughlin     snprintf(smb_conf, sizeof(smb_conf), "%s/%s", s->smb_dir, "smb.conf");
49768ac40d2SMark McLoughlin 
49868ac40d2SMark McLoughlin     f = fopen(smb_conf, "w");
49968ac40d2SMark McLoughlin     if (!f) {
50068ac40d2SMark McLoughlin         slirp_smb_cleanup(s);
50168ac40d2SMark McLoughlin         qemu_error("could not create samba server configuration file '%s'\n",
50268ac40d2SMark McLoughlin                    smb_conf);
50368ac40d2SMark McLoughlin         return -1;
50468ac40d2SMark McLoughlin     }
50568ac40d2SMark McLoughlin     fprintf(f,
50668ac40d2SMark McLoughlin             "[global]\n"
50768ac40d2SMark McLoughlin             "private dir=%s\n"
50868ac40d2SMark McLoughlin             "smb ports=0\n"
50968ac40d2SMark McLoughlin             "socket address=127.0.0.1\n"
51068ac40d2SMark McLoughlin             "pid directory=%s\n"
51168ac40d2SMark McLoughlin             "lock directory=%s\n"
51268ac40d2SMark McLoughlin             "log file=%s/log.smbd\n"
51368ac40d2SMark McLoughlin             "smb passwd file=%s/smbpasswd\n"
51468ac40d2SMark McLoughlin             "security = share\n"
51568ac40d2SMark McLoughlin             "[qemu]\n"
51668ac40d2SMark McLoughlin             "path=%s\n"
51768ac40d2SMark McLoughlin             "read only=no\n"
51868ac40d2SMark McLoughlin             "guest ok=yes\n",
51968ac40d2SMark McLoughlin             s->smb_dir,
52068ac40d2SMark McLoughlin             s->smb_dir,
52168ac40d2SMark McLoughlin             s->smb_dir,
52268ac40d2SMark McLoughlin             s->smb_dir,
52368ac40d2SMark McLoughlin             s->smb_dir,
52468ac40d2SMark McLoughlin             exported_dir
52568ac40d2SMark McLoughlin             );
52668ac40d2SMark McLoughlin     fclose(f);
52768ac40d2SMark McLoughlin 
52868ac40d2SMark McLoughlin     snprintf(smb_cmdline, sizeof(smb_cmdline), "%s -s %s",
52968ac40d2SMark McLoughlin              SMBD_COMMAND, smb_conf);
53068ac40d2SMark McLoughlin 
53168ac40d2SMark McLoughlin     if (slirp_add_exec(s->slirp, 0, smb_cmdline, &vserver_addr, 139) < 0) {
53268ac40d2SMark McLoughlin         slirp_smb_cleanup(s);
53368ac40d2SMark McLoughlin         qemu_error("conflicting/invalid smbserver address\n");
53468ac40d2SMark McLoughlin         return -1;
53568ac40d2SMark McLoughlin     }
53668ac40d2SMark McLoughlin     return 0;
53768ac40d2SMark McLoughlin }
53868ac40d2SMark McLoughlin 
53968ac40d2SMark McLoughlin /* automatic user mode samba server configuration (legacy interface) */
54068ac40d2SMark McLoughlin int net_slirp_smb(const char *exported_dir)
54168ac40d2SMark McLoughlin {
54268ac40d2SMark McLoughlin     struct in_addr vserver_addr = { .s_addr = 0 };
54368ac40d2SMark McLoughlin 
54468ac40d2SMark McLoughlin     if (legacy_smb_export) {
54568ac40d2SMark McLoughlin         fprintf(stderr, "-smb given twice\n");
54668ac40d2SMark McLoughlin         return -1;
54768ac40d2SMark McLoughlin     }
54868ac40d2SMark McLoughlin     legacy_smb_export = exported_dir;
54968ac40d2SMark McLoughlin     if (!QTAILQ_EMPTY(&slirp_stacks)) {
55068ac40d2SMark McLoughlin         return slirp_smb(QTAILQ_FIRST(&slirp_stacks), exported_dir,
55168ac40d2SMark McLoughlin                          vserver_addr);
55268ac40d2SMark McLoughlin     }
55368ac40d2SMark McLoughlin     return 0;
55468ac40d2SMark McLoughlin }
55568ac40d2SMark McLoughlin 
55668ac40d2SMark McLoughlin #endif /* !defined(_WIN32) */
55768ac40d2SMark McLoughlin 
55868ac40d2SMark McLoughlin struct GuestFwd {
55968ac40d2SMark McLoughlin     CharDriverState *hd;
56068ac40d2SMark McLoughlin     struct in_addr server;
56168ac40d2SMark McLoughlin     int port;
56268ac40d2SMark McLoughlin     Slirp *slirp;
56368ac40d2SMark McLoughlin };
56468ac40d2SMark McLoughlin 
56568ac40d2SMark McLoughlin static int guestfwd_can_read(void *opaque)
56668ac40d2SMark McLoughlin {
56768ac40d2SMark McLoughlin     struct GuestFwd *fwd = opaque;
56868ac40d2SMark McLoughlin     return slirp_socket_can_recv(fwd->slirp, fwd->server, fwd->port);
56968ac40d2SMark McLoughlin }
57068ac40d2SMark McLoughlin 
57168ac40d2SMark McLoughlin static void guestfwd_read(void *opaque, const uint8_t *buf, int size)
57268ac40d2SMark McLoughlin {
57368ac40d2SMark McLoughlin     struct GuestFwd *fwd = opaque;
57468ac40d2SMark McLoughlin     slirp_socket_recv(fwd->slirp, fwd->server, fwd->port, buf, size);
57568ac40d2SMark McLoughlin }
57668ac40d2SMark McLoughlin 
57768ac40d2SMark McLoughlin static int slirp_guestfwd(SlirpState *s, const char *config_str,
57868ac40d2SMark McLoughlin                           int legacy_format)
57968ac40d2SMark McLoughlin {
58068ac40d2SMark McLoughlin     struct in_addr server = { .s_addr = 0 };
58168ac40d2SMark McLoughlin     struct GuestFwd *fwd;
58268ac40d2SMark McLoughlin     const char *p;
58368ac40d2SMark McLoughlin     char buf[128];
58468ac40d2SMark McLoughlin     char *end;
58568ac40d2SMark McLoughlin     int port;
58668ac40d2SMark McLoughlin 
58768ac40d2SMark McLoughlin     p = config_str;
58868ac40d2SMark McLoughlin     if (legacy_format) {
58968ac40d2SMark McLoughlin         if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
59068ac40d2SMark McLoughlin             goto fail_syntax;
59168ac40d2SMark McLoughlin         }
59268ac40d2SMark McLoughlin     } else {
59368ac40d2SMark McLoughlin         if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
59468ac40d2SMark McLoughlin             goto fail_syntax;
59568ac40d2SMark McLoughlin         }
59668ac40d2SMark McLoughlin         if (strcmp(buf, "tcp") && buf[0] != '\0') {
59768ac40d2SMark McLoughlin             goto fail_syntax;
59868ac40d2SMark McLoughlin         }
59968ac40d2SMark McLoughlin         if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
60068ac40d2SMark McLoughlin             goto fail_syntax;
60168ac40d2SMark McLoughlin         }
60268ac40d2SMark McLoughlin         if (buf[0] != '\0' && !inet_aton(buf, &server)) {
60368ac40d2SMark McLoughlin             goto fail_syntax;
60468ac40d2SMark McLoughlin         }
60568ac40d2SMark McLoughlin         if (get_str_sep(buf, sizeof(buf), &p, '-') < 0) {
60668ac40d2SMark McLoughlin             goto fail_syntax;
60768ac40d2SMark McLoughlin         }
60868ac40d2SMark McLoughlin     }
60968ac40d2SMark McLoughlin     port = strtol(buf, &end, 10);
61068ac40d2SMark McLoughlin     if (*end != '\0' || port < 1 || port > 65535) {
61168ac40d2SMark McLoughlin         goto fail_syntax;
61268ac40d2SMark McLoughlin     }
61368ac40d2SMark McLoughlin 
61468ac40d2SMark McLoughlin     fwd = qemu_malloc(sizeof(struct GuestFwd));
61568ac40d2SMark McLoughlin     snprintf(buf, sizeof(buf), "guestfwd.tcp:%d", port);
61668ac40d2SMark McLoughlin     fwd->hd = qemu_chr_open(buf, p, NULL);
61768ac40d2SMark McLoughlin     if (!fwd->hd) {
61868ac40d2SMark McLoughlin         qemu_error("could not open guest forwarding device '%s'\n", buf);
61968ac40d2SMark McLoughlin         qemu_free(fwd);
62068ac40d2SMark McLoughlin         return -1;
62168ac40d2SMark McLoughlin     }
62268ac40d2SMark McLoughlin 
62368ac40d2SMark McLoughlin     if (slirp_add_exec(s->slirp, 3, fwd->hd, &server, port) < 0) {
62468ac40d2SMark McLoughlin         qemu_error("conflicting/invalid host:port in guest forwarding "
62568ac40d2SMark McLoughlin                    "rule '%s'\n", config_str);
62668ac40d2SMark McLoughlin         qemu_free(fwd);
62768ac40d2SMark McLoughlin         return -1;
62868ac40d2SMark McLoughlin     }
62968ac40d2SMark McLoughlin     fwd->server = server;
63068ac40d2SMark McLoughlin     fwd->port = port;
63168ac40d2SMark McLoughlin     fwd->slirp = s->slirp;
63268ac40d2SMark McLoughlin 
63368ac40d2SMark McLoughlin     qemu_chr_add_handlers(fwd->hd, guestfwd_can_read, guestfwd_read,
63468ac40d2SMark McLoughlin                           NULL, fwd);
63568ac40d2SMark McLoughlin     return 0;
63668ac40d2SMark McLoughlin 
63768ac40d2SMark McLoughlin  fail_syntax:
63868ac40d2SMark McLoughlin     qemu_error("invalid guest forwarding rule '%s'\n", config_str);
63968ac40d2SMark McLoughlin     return -1;
64068ac40d2SMark McLoughlin }
64168ac40d2SMark McLoughlin 
64268ac40d2SMark McLoughlin void do_info_usernet(Monitor *mon)
64368ac40d2SMark McLoughlin {
64468ac40d2SMark McLoughlin     SlirpState *s;
64568ac40d2SMark McLoughlin 
64668ac40d2SMark McLoughlin     QTAILQ_FOREACH(s, &slirp_stacks, entry) {
647ce20b5beSMark McLoughlin         monitor_printf(mon, "VLAN %d (%s):\n",
648ce20b5beSMark McLoughlin                        s->nc.vlan ? s->nc.vlan->id : -1,
649ce20b5beSMark McLoughlin                        s->nc.name);
65068ac40d2SMark McLoughlin         slirp_connection_info(s->slirp, mon);
65168ac40d2SMark McLoughlin     }
65268ac40d2SMark McLoughlin }
65368ac40d2SMark McLoughlin 
65468ac40d2SMark McLoughlin static int net_init_slirp_configs(const char *name, const char *value, void *opaque)
65568ac40d2SMark McLoughlin {
65668ac40d2SMark McLoughlin     struct slirp_config_str *config;
65768ac40d2SMark McLoughlin 
65868ac40d2SMark McLoughlin     if (strcmp(name, "hostfwd") != 0 && strcmp(name, "guestfwd") != 0) {
65968ac40d2SMark McLoughlin         return 0;
66068ac40d2SMark McLoughlin     }
66168ac40d2SMark McLoughlin 
66268ac40d2SMark McLoughlin     config = qemu_mallocz(sizeof(*config));
66368ac40d2SMark McLoughlin 
66468ac40d2SMark McLoughlin     pstrcpy(config->str, sizeof(config->str), value);
66568ac40d2SMark McLoughlin 
66668ac40d2SMark McLoughlin     if (!strcmp(name, "hostfwd")) {
66768ac40d2SMark McLoughlin         config->flags = SLIRP_CFG_HOSTFWD;
66868ac40d2SMark McLoughlin     }
66968ac40d2SMark McLoughlin 
67068ac40d2SMark McLoughlin     config->next = slirp_configs;
67168ac40d2SMark McLoughlin     slirp_configs = config;
67268ac40d2SMark McLoughlin 
67368ac40d2SMark McLoughlin     return 0;
67468ac40d2SMark McLoughlin }
67568ac40d2SMark McLoughlin 
67668ac40d2SMark McLoughlin int net_init_slirp(QemuOpts *opts,
67768ac40d2SMark McLoughlin                    Monitor *mon,
67868ac40d2SMark McLoughlin                    const char *name,
67968ac40d2SMark McLoughlin                    VLANState *vlan)
68068ac40d2SMark McLoughlin {
68168ac40d2SMark McLoughlin     struct slirp_config_str *config;
68268ac40d2SMark McLoughlin     const char *vhost;
68368ac40d2SMark McLoughlin     const char *vhostname;
68468ac40d2SMark McLoughlin     const char *vdhcp_start;
68568ac40d2SMark McLoughlin     const char *vnamesrv;
68668ac40d2SMark McLoughlin     const char *tftp_export;
68768ac40d2SMark McLoughlin     const char *bootfile;
68868ac40d2SMark McLoughlin     const char *smb_export;
68968ac40d2SMark McLoughlin     const char *vsmbsrv;
69068ac40d2SMark McLoughlin     char *vnet = NULL;
69168ac40d2SMark McLoughlin     int restricted = 0;
69268ac40d2SMark McLoughlin     int ret;
69368ac40d2SMark McLoughlin 
69468ac40d2SMark McLoughlin     vhost       = qemu_opt_get(opts, "host");
69568ac40d2SMark McLoughlin     vhostname   = qemu_opt_get(opts, "hostname");
69668ac40d2SMark McLoughlin     vdhcp_start = qemu_opt_get(opts, "dhcpstart");
69768ac40d2SMark McLoughlin     vnamesrv    = qemu_opt_get(opts, "dns");
69868ac40d2SMark McLoughlin     tftp_export = qemu_opt_get(opts, "tftp");
69968ac40d2SMark McLoughlin     bootfile    = qemu_opt_get(opts, "bootfile");
70068ac40d2SMark McLoughlin     smb_export  = qemu_opt_get(opts, "smb");
70168ac40d2SMark McLoughlin     vsmbsrv     = qemu_opt_get(opts, "smbserver");
70268ac40d2SMark McLoughlin 
70368ac40d2SMark McLoughlin     if (qemu_opt_get(opts, "ip")) {
70468ac40d2SMark McLoughlin         const char *ip = qemu_opt_get(opts, "ip");
70568ac40d2SMark McLoughlin         int l = strlen(ip) + strlen("/24") + 1;
70668ac40d2SMark McLoughlin 
70768ac40d2SMark McLoughlin         vnet = qemu_malloc(l);
70868ac40d2SMark McLoughlin 
70968ac40d2SMark McLoughlin         /* emulate legacy ip= parameter */
71068ac40d2SMark McLoughlin         pstrcpy(vnet, l, ip);
71168ac40d2SMark McLoughlin         pstrcat(vnet, l, "/24");
71268ac40d2SMark McLoughlin     }
71368ac40d2SMark McLoughlin 
71468ac40d2SMark McLoughlin     if (qemu_opt_get(opts, "net")) {
71568ac40d2SMark McLoughlin         if (vnet) {
71668ac40d2SMark McLoughlin             qemu_free(vnet);
71768ac40d2SMark McLoughlin         }
71868ac40d2SMark McLoughlin         vnet = qemu_strdup(qemu_opt_get(opts, "net"));
71968ac40d2SMark McLoughlin     }
72068ac40d2SMark McLoughlin 
72168ac40d2SMark McLoughlin     if (qemu_opt_get(opts, "restrict") &&
72268ac40d2SMark McLoughlin         qemu_opt_get(opts, "restrict")[0] == 'y') {
72368ac40d2SMark McLoughlin         restricted = 1;
72468ac40d2SMark McLoughlin     }
72568ac40d2SMark McLoughlin 
72668ac40d2SMark McLoughlin     qemu_opt_foreach(opts, net_init_slirp_configs, NULL, 0);
72768ac40d2SMark McLoughlin 
72868ac40d2SMark McLoughlin     ret = net_slirp_init(vlan, "user", name, restricted, vnet, vhost,
72968ac40d2SMark McLoughlin                          vhostname, tftp_export, bootfile, vdhcp_start,
73068ac40d2SMark McLoughlin                          vnamesrv, smb_export, vsmbsrv);
73168ac40d2SMark McLoughlin 
73268ac40d2SMark McLoughlin     while (slirp_configs) {
73368ac40d2SMark McLoughlin         config = slirp_configs;
73468ac40d2SMark McLoughlin         slirp_configs = config->next;
73568ac40d2SMark McLoughlin         qemu_free(config);
73668ac40d2SMark McLoughlin     }
73768ac40d2SMark McLoughlin 
73868ac40d2SMark McLoughlin     if (ret != -1 && vlan) {
73968ac40d2SMark McLoughlin         vlan->nb_host_devs++;
74068ac40d2SMark McLoughlin     }
74168ac40d2SMark McLoughlin 
74268ac40d2SMark McLoughlin     qemu_free(vnet);
74368ac40d2SMark McLoughlin 
74468ac40d2SMark McLoughlin     return ret;
74568ac40d2SMark McLoughlin }
74668ac40d2SMark McLoughlin 
74768ac40d2SMark McLoughlin int net_slirp_parse_legacy(QemuOptsList *opts_list, const char *optarg, int *ret)
74868ac40d2SMark McLoughlin {
74968ac40d2SMark McLoughlin     if (strcmp(opts_list->name, "net") != 0 ||
75068ac40d2SMark McLoughlin         strncmp(optarg, "channel,", strlen("channel,")) != 0) {
75168ac40d2SMark McLoughlin         return 0;
75268ac40d2SMark McLoughlin     }
75368ac40d2SMark McLoughlin 
75468ac40d2SMark McLoughlin     /* handle legacy -net channel,port:chr */
75568ac40d2SMark McLoughlin     optarg += strlen("channel,");
75668ac40d2SMark McLoughlin 
75768ac40d2SMark McLoughlin     if (QTAILQ_EMPTY(&slirp_stacks)) {
75868ac40d2SMark McLoughlin         struct slirp_config_str *config;
75968ac40d2SMark McLoughlin 
76068ac40d2SMark McLoughlin         config = qemu_malloc(sizeof(*config));
76168ac40d2SMark McLoughlin         pstrcpy(config->str, sizeof(config->str), optarg);
76268ac40d2SMark McLoughlin         config->flags = SLIRP_CFG_LEGACY;
76368ac40d2SMark McLoughlin         config->next = slirp_configs;
76468ac40d2SMark McLoughlin         slirp_configs = config;
76568ac40d2SMark McLoughlin         *ret = 0;
76668ac40d2SMark McLoughlin     } else {
76768ac40d2SMark McLoughlin         *ret = slirp_guestfwd(QTAILQ_FIRST(&slirp_stacks), optarg, 1);
76868ac40d2SMark McLoughlin     }
76968ac40d2SMark McLoughlin 
77068ac40d2SMark McLoughlin     return 1;
77168ac40d2SMark McLoughlin }
77268ac40d2SMark McLoughlin 
773