xref: /openbmc/qemu/net/slirp.c (revision b917da4c)
1 /*
2  * QEMU System Emulator
3  *
4  * Copyright (c) 2003-2008 Fabrice Bellard
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  */
24 #include "qemu/osdep.h"
25 #include "net/slirp.h"
26 
27 
28 #ifndef _WIN32
29 #include <pwd.h>
30 #include <sys/wait.h>
31 #endif
32 #include "net/net.h"
33 #include "clients.h"
34 #include "hub.h"
35 #include "monitor/monitor.h"
36 #include "qemu/error-report.h"
37 #include "qemu/sockets.h"
38 #include "slirp/libslirp.h"
39 #include "slirp/ip6.h"
40 #include "sysemu/char.h"
41 
42 static int get_str_sep(char *buf, int buf_size, const char **pp, int sep)
43 {
44     const char *p, *p1;
45     int len;
46     p = *pp;
47     p1 = strchr(p, sep);
48     if (!p1)
49         return -1;
50     len = p1 - p;
51     p1++;
52     if (buf_size > 0) {
53         if (len > buf_size - 1)
54             len = buf_size - 1;
55         memcpy(buf, p, len);
56         buf[len] = '\0';
57     }
58     *pp = p1;
59     return 0;
60 }
61 
62 /* slirp network adapter */
63 
64 #define SLIRP_CFG_HOSTFWD 1
65 #define SLIRP_CFG_LEGACY  2
66 
67 struct slirp_config_str {
68     struct slirp_config_str *next;
69     int flags;
70     char str[1024];
71     int legacy_format;
72 };
73 
74 typedef struct SlirpState {
75     NetClientState nc;
76     QTAILQ_ENTRY(SlirpState) entry;
77     Slirp *slirp;
78 #ifndef _WIN32
79     char smb_dir[128];
80 #endif
81 } SlirpState;
82 
83 static struct slirp_config_str *slirp_configs;
84 const char *legacy_tftp_prefix;
85 const char *legacy_bootp_filename;
86 static QTAILQ_HEAD(slirp_stacks, SlirpState) slirp_stacks =
87     QTAILQ_HEAD_INITIALIZER(slirp_stacks);
88 
89 static int slirp_hostfwd(SlirpState *s, const char *redir_str,
90                          int legacy_format);
91 static int slirp_guestfwd(SlirpState *s, const char *config_str,
92                           int legacy_format);
93 
94 #ifndef _WIN32
95 static const char *legacy_smb_export;
96 
97 static int slirp_smb(SlirpState *s, const char *exported_dir,
98                      struct in_addr vserver_addr);
99 static void slirp_smb_cleanup(SlirpState *s);
100 #else
101 static inline void slirp_smb_cleanup(SlirpState *s) { }
102 #endif
103 
104 void slirp_output(void *opaque, const uint8_t *pkt, int pkt_len)
105 {
106     SlirpState *s = opaque;
107 
108     qemu_send_packet(&s->nc, pkt, pkt_len);
109 }
110 
111 static ssize_t net_slirp_receive(NetClientState *nc, const uint8_t *buf, size_t size)
112 {
113     SlirpState *s = DO_UPCAST(SlirpState, nc, nc);
114 
115     slirp_input(s->slirp, buf, size);
116 
117     return size;
118 }
119 
120 static void net_slirp_cleanup(NetClientState *nc)
121 {
122     SlirpState *s = DO_UPCAST(SlirpState, nc, nc);
123 
124     slirp_cleanup(s->slirp);
125     slirp_smb_cleanup(s);
126     QTAILQ_REMOVE(&slirp_stacks, s, entry);
127 }
128 
129 static NetClientInfo net_slirp_info = {
130     .type = NET_CLIENT_OPTIONS_KIND_USER,
131     .size = sizeof(SlirpState),
132     .receive = net_slirp_receive,
133     .cleanup = net_slirp_cleanup,
134 };
135 
136 static int net_slirp_init(NetClientState *peer, const char *model,
137                           const char *name, int restricted,
138                           const char *vnetwork, const char *vhost,
139                           const char *vprefix6, int vprefix6_len,
140                           const char *vhost6,
141                           const char *vhostname, const char *tftp_export,
142                           const char *bootfile, const char *vdhcp_start,
143                           const char *vnameserver, const char *vnameserver6,
144                           const char *smb_export, const char *vsmbserver,
145                           const char **dnssearch)
146 {
147     /* default settings according to historic slirp */
148     struct in_addr net  = { .s_addr = htonl(0x0a000200) }; /* 10.0.2.0 */
149     struct in_addr mask = { .s_addr = htonl(0xffffff00) }; /* 255.255.255.0 */
150     struct in_addr host = { .s_addr = htonl(0x0a000202) }; /* 10.0.2.2 */
151     struct in_addr dhcp = { .s_addr = htonl(0x0a00020f) }; /* 10.0.2.15 */
152     struct in_addr dns  = { .s_addr = htonl(0x0a000203) }; /* 10.0.2.3 */
153     struct in6_addr ip6_prefix;
154     struct in6_addr ip6_host;
155     struct in6_addr ip6_dns;
156 #ifndef _WIN32
157     struct in_addr smbsrv = { .s_addr = 0 };
158 #endif
159     NetClientState *nc;
160     SlirpState *s;
161     char buf[20];
162     uint32_t addr;
163     int shift;
164     char *end;
165     struct slirp_config_str *config;
166 
167     if (!tftp_export) {
168         tftp_export = legacy_tftp_prefix;
169     }
170     if (!bootfile) {
171         bootfile = legacy_bootp_filename;
172     }
173 
174     if (vnetwork) {
175         if (get_str_sep(buf, sizeof(buf), &vnetwork, '/') < 0) {
176             if (!inet_aton(vnetwork, &net)) {
177                 return -1;
178             }
179             addr = ntohl(net.s_addr);
180             if (!(addr & 0x80000000)) {
181                 mask.s_addr = htonl(0xff000000); /* class A */
182             } else if ((addr & 0xfff00000) == 0xac100000) {
183                 mask.s_addr = htonl(0xfff00000); /* priv. 172.16.0.0/12 */
184             } else if ((addr & 0xc0000000) == 0x80000000) {
185                 mask.s_addr = htonl(0xffff0000); /* class B */
186             } else if ((addr & 0xffff0000) == 0xc0a80000) {
187                 mask.s_addr = htonl(0xffff0000); /* priv. 192.168.0.0/16 */
188             } else if ((addr & 0xffff0000) == 0xc6120000) {
189                 mask.s_addr = htonl(0xfffe0000); /* tests 198.18.0.0/15 */
190             } else if ((addr & 0xe0000000) == 0xe0000000) {
191                 mask.s_addr = htonl(0xffffff00); /* class C */
192             } else {
193                 mask.s_addr = htonl(0xfffffff0); /* multicast/reserved */
194             }
195         } else {
196             if (!inet_aton(buf, &net)) {
197                 return -1;
198             }
199             shift = strtol(vnetwork, &end, 10);
200             if (*end != '\0') {
201                 if (!inet_aton(vnetwork, &mask)) {
202                     return -1;
203                 }
204             } else if (shift < 4 || shift > 32) {
205                 return -1;
206             } else {
207                 mask.s_addr = htonl(0xffffffff << (32 - shift));
208             }
209         }
210         net.s_addr &= mask.s_addr;
211         host.s_addr = net.s_addr | (htonl(0x0202) & ~mask.s_addr);
212         dhcp.s_addr = net.s_addr | (htonl(0x020f) & ~mask.s_addr);
213         dns.s_addr  = net.s_addr | (htonl(0x0203) & ~mask.s_addr);
214     }
215 
216     if (vhost && !inet_aton(vhost, &host)) {
217         return -1;
218     }
219     if ((host.s_addr & mask.s_addr) != net.s_addr) {
220         return -1;
221     }
222 
223     if (vnameserver && !inet_aton(vnameserver, &dns)) {
224         return -1;
225     }
226     if ((dns.s_addr & mask.s_addr) != net.s_addr ||
227         dns.s_addr == host.s_addr) {
228         return -1;
229     }
230 
231     if (vdhcp_start && !inet_aton(vdhcp_start, &dhcp)) {
232         return -1;
233     }
234     if ((dhcp.s_addr & mask.s_addr) != net.s_addr ||
235         dhcp.s_addr == host.s_addr || dhcp.s_addr == dns.s_addr) {
236         return -1;
237     }
238 
239 #ifndef _WIN32
240     if (vsmbserver && !inet_aton(vsmbserver, &smbsrv)) {
241         return -1;
242     }
243 #endif
244 
245 #if defined(_WIN32) && (_WIN32_WINNT < 0x0600)
246     /* No inet_pton helper before Vista... */
247     if (vprefix6) {
248         /* Unsupported */
249         return -1;
250     }
251     memset(&ip6_prefix, 0, sizeof(ip6_prefix));
252     ip6_prefix.s6_addr[0] = 0xfe;
253     ip6_prefix.s6_addr[1] = 0xc0;
254 #else
255     if (!vprefix6) {
256         vprefix6 = "fec0::";
257     }
258     if (!inet_pton(AF_INET6, vprefix6, &ip6_prefix)) {
259         return -1;
260     }
261 #endif
262 
263     if (!vprefix6_len) {
264         vprefix6_len = 64;
265     }
266     if (vprefix6_len < 0 || vprefix6_len > 126) {
267         return -1;
268     }
269 
270     if (vhost6) {
271 #if defined(_WIN32) && (_WIN32_WINNT < 0x0600)
272         return -1;
273 #else
274         if (!inet_pton(AF_INET6, vhost6, &ip6_host)) {
275             return -1;
276         }
277         if (!in6_equal_net(&ip6_prefix, &ip6_host, vprefix6_len)) {
278             return -1;
279         }
280 #endif
281     } else {
282         ip6_host = ip6_prefix;
283         ip6_host.s6_addr[15] |= 2;
284     }
285 
286     if (vnameserver6) {
287 #if defined(_WIN32) && (_WIN32_WINNT < 0x0600)
288         return -1;
289 #else
290         if (!inet_pton(AF_INET6, vnameserver6, &ip6_dns)) {
291             return -1;
292         }
293         if (!in6_equal_net(&ip6_prefix, &ip6_dns, vprefix6_len)) {
294             return -1;
295         }
296 #endif
297     } else {
298         ip6_dns = ip6_prefix;
299         ip6_dns.s6_addr[15] |= 3;
300     }
301 
302 
303     nc = qemu_new_net_client(&net_slirp_info, peer, model, name);
304 
305     snprintf(nc->info_str, sizeof(nc->info_str),
306              "net=%s,restrict=%s", inet_ntoa(net),
307              restricted ? "on" : "off");
308 
309     s = DO_UPCAST(SlirpState, nc, nc);
310 
311     s->slirp = slirp_init(restricted, net, mask, host,
312                           ip6_prefix, vprefix6_len, ip6_host,
313                           vhostname, tftp_export, bootfile, dhcp,
314                           dns, ip6_dns, dnssearch, s);
315     QTAILQ_INSERT_TAIL(&slirp_stacks, s, entry);
316 
317     for (config = slirp_configs; config; config = config->next) {
318         if (config->flags & SLIRP_CFG_HOSTFWD) {
319             if (slirp_hostfwd(s, config->str,
320                               config->flags & SLIRP_CFG_LEGACY) < 0)
321                 goto error;
322         } else {
323             if (slirp_guestfwd(s, config->str,
324                                config->flags & SLIRP_CFG_LEGACY) < 0)
325                 goto error;
326         }
327     }
328 #ifndef _WIN32
329     if (!smb_export) {
330         smb_export = legacy_smb_export;
331     }
332     if (smb_export) {
333         if (slirp_smb(s, smb_export, smbsrv) < 0)
334             goto error;
335     }
336 #endif
337 
338     return 0;
339 
340 error:
341     qemu_del_net_client(nc);
342     return -1;
343 }
344 
345 static SlirpState *slirp_lookup(Monitor *mon, const char *vlan,
346                                 const char *stack)
347 {
348 
349     if (vlan) {
350         NetClientState *nc;
351         nc = net_hub_find_client_by_name(strtol(vlan, NULL, 0), stack);
352         if (!nc) {
353             monitor_printf(mon, "unrecognized (vlan-id, stackname) pair\n");
354             return NULL;
355         }
356         if (strcmp(nc->model, "user")) {
357             monitor_printf(mon, "invalid device specified\n");
358             return NULL;
359         }
360         return DO_UPCAST(SlirpState, nc, nc);
361     } else {
362         if (QTAILQ_EMPTY(&slirp_stacks)) {
363             monitor_printf(mon, "user mode network stack not in use\n");
364             return NULL;
365         }
366         return QTAILQ_FIRST(&slirp_stacks);
367     }
368 }
369 
370 void hmp_hostfwd_remove(Monitor *mon, const QDict *qdict)
371 {
372     struct in_addr host_addr = { .s_addr = INADDR_ANY };
373     int host_port;
374     char buf[256];
375     const char *src_str, *p;
376     SlirpState *s;
377     int is_udp = 0;
378     int err;
379     const char *arg1 = qdict_get_str(qdict, "arg1");
380     const char *arg2 = qdict_get_try_str(qdict, "arg2");
381     const char *arg3 = qdict_get_try_str(qdict, "arg3");
382 
383     if (arg2) {
384         s = slirp_lookup(mon, arg1, arg2);
385         src_str = arg3;
386     } else {
387         s = slirp_lookup(mon, NULL, NULL);
388         src_str = arg1;
389     }
390     if (!s) {
391         return;
392     }
393 
394     p = src_str;
395     if (!p || get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
396         goto fail_syntax;
397     }
398 
399     if (!strcmp(buf, "tcp") || buf[0] == '\0') {
400         is_udp = 0;
401     } else if (!strcmp(buf, "udp")) {
402         is_udp = 1;
403     } else {
404         goto fail_syntax;
405     }
406 
407     if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
408         goto fail_syntax;
409     }
410     if (buf[0] != '\0' && !inet_aton(buf, &host_addr)) {
411         goto fail_syntax;
412     }
413 
414     host_port = atoi(p);
415 
416     err = slirp_remove_hostfwd(s->slirp, is_udp, host_addr, host_port);
417 
418     monitor_printf(mon, "host forwarding rule for %s %s\n", src_str,
419                    err ? "not found" : "removed");
420     return;
421 
422  fail_syntax:
423     monitor_printf(mon, "invalid format\n");
424 }
425 
426 static int slirp_hostfwd(SlirpState *s, const char *redir_str,
427                          int legacy_format)
428 {
429     struct in_addr host_addr = { .s_addr = INADDR_ANY };
430     struct in_addr guest_addr = { .s_addr = 0 };
431     int host_port, guest_port;
432     const char *p;
433     char buf[256];
434     int is_udp;
435     char *end;
436 
437     p = redir_str;
438     if (!p || get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
439         goto fail_syntax;
440     }
441     if (!strcmp(buf, "tcp") || buf[0] == '\0') {
442         is_udp = 0;
443     } else if (!strcmp(buf, "udp")) {
444         is_udp = 1;
445     } else {
446         goto fail_syntax;
447     }
448 
449     if (!legacy_format) {
450         if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
451             goto fail_syntax;
452         }
453         if (buf[0] != '\0' && !inet_aton(buf, &host_addr)) {
454             goto fail_syntax;
455         }
456     }
457 
458     if (get_str_sep(buf, sizeof(buf), &p, legacy_format ? ':' : '-') < 0) {
459         goto fail_syntax;
460     }
461     host_port = strtol(buf, &end, 0);
462     if (*end != '\0' || host_port < 1 || host_port > 65535) {
463         goto fail_syntax;
464     }
465 
466     if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
467         goto fail_syntax;
468     }
469     if (buf[0] != '\0' && !inet_aton(buf, &guest_addr)) {
470         goto fail_syntax;
471     }
472 
473     guest_port = strtol(p, &end, 0);
474     if (*end != '\0' || guest_port < 1 || guest_port > 65535) {
475         goto fail_syntax;
476     }
477 
478     if (slirp_add_hostfwd(s->slirp, is_udp, host_addr, host_port, guest_addr,
479                           guest_port) < 0) {
480         error_report("could not set up host forwarding rule '%s'",
481                      redir_str);
482         return -1;
483     }
484     return 0;
485 
486  fail_syntax:
487     error_report("invalid host forwarding rule '%s'", redir_str);
488     return -1;
489 }
490 
491 void hmp_hostfwd_add(Monitor *mon, const QDict *qdict)
492 {
493     const char *redir_str;
494     SlirpState *s;
495     const char *arg1 = qdict_get_str(qdict, "arg1");
496     const char *arg2 = qdict_get_try_str(qdict, "arg2");
497     const char *arg3 = qdict_get_try_str(qdict, "arg3");
498 
499     if (arg2) {
500         s = slirp_lookup(mon, arg1, arg2);
501         redir_str = arg3;
502     } else {
503         s = slirp_lookup(mon, NULL, NULL);
504         redir_str = arg1;
505     }
506     if (s) {
507         slirp_hostfwd(s, redir_str, 0);
508     }
509 
510 }
511 
512 int net_slirp_redir(const char *redir_str)
513 {
514     struct slirp_config_str *config;
515 
516     if (QTAILQ_EMPTY(&slirp_stacks)) {
517         config = g_malloc(sizeof(*config));
518         pstrcpy(config->str, sizeof(config->str), redir_str);
519         config->flags = SLIRP_CFG_HOSTFWD | SLIRP_CFG_LEGACY;
520         config->next = slirp_configs;
521         slirp_configs = config;
522         return 0;
523     }
524 
525     return slirp_hostfwd(QTAILQ_FIRST(&slirp_stacks), redir_str, 1);
526 }
527 
528 #ifndef _WIN32
529 
530 /* automatic user mode samba server configuration */
531 static void slirp_smb_cleanup(SlirpState *s)
532 {
533     char cmd[128];
534     int ret;
535 
536     if (s->smb_dir[0] != '\0') {
537         snprintf(cmd, sizeof(cmd), "rm -rf %s", s->smb_dir);
538         ret = system(cmd);
539         if (ret == -1 || !WIFEXITED(ret)) {
540             error_report("'%s' failed.", cmd);
541         } else if (WEXITSTATUS(ret)) {
542             error_report("'%s' failed. Error code: %d",
543                          cmd, WEXITSTATUS(ret));
544         }
545         s->smb_dir[0] = '\0';
546     }
547 }
548 
549 static int slirp_smb(SlirpState* s, const char *exported_dir,
550                      struct in_addr vserver_addr)
551 {
552     char smb_conf[128];
553     char smb_cmdline[128];
554     struct passwd *passwd;
555     FILE *f;
556 
557     passwd = getpwuid(geteuid());
558     if (!passwd) {
559         error_report("failed to retrieve user name");
560         return -1;
561     }
562 
563     if (access(CONFIG_SMBD_COMMAND, F_OK)) {
564         error_report("could not find '%s', please install it",
565                      CONFIG_SMBD_COMMAND);
566         return -1;
567     }
568 
569     if (access(exported_dir, R_OK | X_OK)) {
570         error_report("error accessing shared directory '%s': %s",
571                      exported_dir, strerror(errno));
572         return -1;
573     }
574 
575     snprintf(s->smb_dir, sizeof(s->smb_dir), "/tmp/qemu-smb.XXXXXX");
576     if (!mkdtemp(s->smb_dir)) {
577         error_report("could not create samba server dir '%s'", s->smb_dir);
578         s->smb_dir[0] = 0;
579         return -1;
580     }
581     snprintf(smb_conf, sizeof(smb_conf), "%s/%s", s->smb_dir, "smb.conf");
582 
583     f = fopen(smb_conf, "w");
584     if (!f) {
585         slirp_smb_cleanup(s);
586         error_report("could not create samba server configuration file '%s'",
587                      smb_conf);
588         return -1;
589     }
590     fprintf(f,
591             "[global]\n"
592             "private dir=%s\n"
593             "interfaces=127.0.0.1\n"
594             "bind interfaces only=yes\n"
595             "pid directory=%s\n"
596             "lock directory=%s\n"
597             "state directory=%s\n"
598             "cache directory=%s\n"
599             "ncalrpc dir=%s/ncalrpc\n"
600             "log file=%s/log.smbd\n"
601             "smb passwd file=%s/smbpasswd\n"
602             "security = user\n"
603             "map to guest = Bad User\n"
604             "load printers = no\n"
605             "printing = bsd\n"
606             "disable spoolss = yes\n"
607             "usershare max shares = 0\n"
608             "[qemu]\n"
609             "path=%s\n"
610             "read only=no\n"
611             "guest ok=yes\n"
612             "force user=%s\n",
613             s->smb_dir,
614             s->smb_dir,
615             s->smb_dir,
616             s->smb_dir,
617             s->smb_dir,
618             s->smb_dir,
619             s->smb_dir,
620             s->smb_dir,
621             exported_dir,
622             passwd->pw_name
623             );
624     fclose(f);
625 
626     snprintf(smb_cmdline, sizeof(smb_cmdline), "%s -l %s -s %s",
627              CONFIG_SMBD_COMMAND, s->smb_dir, smb_conf);
628 
629     if (slirp_add_exec(s->slirp, 0, smb_cmdline, &vserver_addr, 139) < 0 ||
630         slirp_add_exec(s->slirp, 0, smb_cmdline, &vserver_addr, 445) < 0) {
631         slirp_smb_cleanup(s);
632         error_report("conflicting/invalid smbserver address");
633         return -1;
634     }
635     return 0;
636 }
637 
638 /* automatic user mode samba server configuration (legacy interface) */
639 int net_slirp_smb(const char *exported_dir)
640 {
641     struct in_addr vserver_addr = { .s_addr = 0 };
642 
643     if (legacy_smb_export) {
644         fprintf(stderr, "-smb given twice\n");
645         return -1;
646     }
647     legacy_smb_export = exported_dir;
648     if (!QTAILQ_EMPTY(&slirp_stacks)) {
649         return slirp_smb(QTAILQ_FIRST(&slirp_stacks), exported_dir,
650                          vserver_addr);
651     }
652     return 0;
653 }
654 
655 #endif /* !defined(_WIN32) */
656 
657 struct GuestFwd {
658     CharDriverState *hd;
659     struct in_addr server;
660     int port;
661     Slirp *slirp;
662 };
663 
664 static int guestfwd_can_read(void *opaque)
665 {
666     struct GuestFwd *fwd = opaque;
667     return slirp_socket_can_recv(fwd->slirp, fwd->server, fwd->port);
668 }
669 
670 static void guestfwd_read(void *opaque, const uint8_t *buf, int size)
671 {
672     struct GuestFwd *fwd = opaque;
673     slirp_socket_recv(fwd->slirp, fwd->server, fwd->port, buf, size);
674 }
675 
676 static int slirp_guestfwd(SlirpState *s, const char *config_str,
677                           int legacy_format)
678 {
679     struct in_addr server = { .s_addr = 0 };
680     struct GuestFwd *fwd;
681     const char *p;
682     char buf[128];
683     char *end;
684     int port;
685 
686     p = config_str;
687     if (legacy_format) {
688         if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
689             goto fail_syntax;
690         }
691     } else {
692         if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
693             goto fail_syntax;
694         }
695         if (strcmp(buf, "tcp") && buf[0] != '\0') {
696             goto fail_syntax;
697         }
698         if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
699             goto fail_syntax;
700         }
701         if (buf[0] != '\0' && !inet_aton(buf, &server)) {
702             goto fail_syntax;
703         }
704         if (get_str_sep(buf, sizeof(buf), &p, '-') < 0) {
705             goto fail_syntax;
706         }
707     }
708     port = strtol(buf, &end, 10);
709     if (*end != '\0' || port < 1 || port > 65535) {
710         goto fail_syntax;
711     }
712 
713     snprintf(buf, sizeof(buf), "guestfwd.tcp.%d", port);
714 
715     if ((strlen(p) > 4) && !strncmp(p, "cmd:", 4)) {
716         if (slirp_add_exec(s->slirp, 0, &p[4], &server, port) < 0) {
717             error_report("conflicting/invalid host:port in guest forwarding "
718                          "rule '%s'", config_str);
719             return -1;
720         }
721     } else {
722         fwd = g_new(struct GuestFwd, 1);
723         fwd->hd = qemu_chr_new(buf, p, NULL);
724         if (!fwd->hd) {
725             error_report("could not open guest forwarding device '%s'", buf);
726             g_free(fwd);
727             return -1;
728         }
729 
730         if (slirp_add_exec(s->slirp, 3, fwd->hd, &server, port) < 0) {
731             error_report("conflicting/invalid host:port in guest forwarding "
732                          "rule '%s'", config_str);
733             g_free(fwd);
734             return -1;
735         }
736         fwd->server = server;
737         fwd->port = port;
738         fwd->slirp = s->slirp;
739 
740         qemu_chr_fe_claim_no_fail(fwd->hd);
741         qemu_chr_add_handlers(fwd->hd, guestfwd_can_read, guestfwd_read,
742                               NULL, fwd);
743     }
744     return 0;
745 
746  fail_syntax:
747     error_report("invalid guest forwarding rule '%s'", config_str);
748     return -1;
749 }
750 
751 void hmp_info_usernet(Monitor *mon, const QDict *qdict)
752 {
753     SlirpState *s;
754 
755     QTAILQ_FOREACH(s, &slirp_stacks, entry) {
756         int id;
757         bool got_vlan_id = net_hub_id_for_client(&s->nc, &id) == 0;
758         monitor_printf(mon, "VLAN %d (%s):\n",
759                        got_vlan_id ? id : -1,
760                        s->nc.name);
761         slirp_connection_info(s->slirp, mon);
762     }
763 }
764 
765 static void
766 net_init_slirp_configs(const StringList *fwd, int flags)
767 {
768     while (fwd) {
769         struct slirp_config_str *config;
770 
771         config = g_malloc0(sizeof(*config));
772         pstrcpy(config->str, sizeof(config->str), fwd->value->str);
773         config->flags = flags;
774         config->next = slirp_configs;
775         slirp_configs = config;
776 
777         fwd = fwd->next;
778     }
779 }
780 
781 static const char **slirp_dnssearch(const StringList *dnsname)
782 {
783     const StringList *c = dnsname;
784     size_t i = 0, num_opts = 0;
785     const char **ret;
786 
787     while (c) {
788         num_opts++;
789         c = c->next;
790     }
791 
792     if (num_opts == 0) {
793         return NULL;
794     }
795 
796     ret = g_malloc((num_opts + 1) * sizeof(*ret));
797     c = dnsname;
798     while (c) {
799         ret[i++] = c->value->str;
800         c = c->next;
801     }
802     ret[i] = NULL;
803     return ret;
804 }
805 
806 int net_init_slirp(const NetClientOptions *opts, const char *name,
807                    NetClientState *peer, Error **errp)
808 {
809     /* FIXME error_setg(errp, ...) on failure */
810     struct slirp_config_str *config;
811     char *vnet;
812     int ret;
813     const NetdevUserOptions *user;
814     const char **dnssearch;
815 
816     assert(opts->type == NET_CLIENT_OPTIONS_KIND_USER);
817     user = opts->u.user;
818 
819     vnet = user->has_net ? g_strdup(user->net) :
820            user->has_ip  ? g_strdup_printf("%s/24", user->ip) :
821            NULL;
822 
823     dnssearch = slirp_dnssearch(user->dnssearch);
824 
825     /* all optional fields are initialized to "all bits zero" */
826 
827     net_init_slirp_configs(user->hostfwd, SLIRP_CFG_HOSTFWD);
828     net_init_slirp_configs(user->guestfwd, 0);
829 
830     ret = net_slirp_init(peer, "user", name, user->q_restrict, vnet,
831                          user->host, user->ip6_prefix, user->ip6_prefixlen,
832                          user->ip6_host, user->hostname, user->tftp,
833                          user->bootfile, user->dhcpstart,
834                          user->dns, user->ip6_dns, user->smb,
835                          user->smbserver, dnssearch);
836 
837     while (slirp_configs) {
838         config = slirp_configs;
839         slirp_configs = config->next;
840         g_free(config);
841     }
842 
843     g_free(vnet);
844     g_free(dnssearch);
845 
846     return ret;
847 }
848 
849 int net_slirp_parse_legacy(QemuOptsList *opts_list, const char *optarg, int *ret)
850 {
851     if (strcmp(opts_list->name, "net") != 0 ||
852         strncmp(optarg, "channel,", strlen("channel,")) != 0) {
853         return 0;
854     }
855 
856     error_report("The '-net channel' option is deprecated. "
857                  "Please use '-netdev user,guestfwd=...' instead.");
858 
859     /* handle legacy -net channel,port:chr */
860     optarg += strlen("channel,");
861 
862     if (QTAILQ_EMPTY(&slirp_stacks)) {
863         struct slirp_config_str *config;
864 
865         config = g_malloc(sizeof(*config));
866         pstrcpy(config->str, sizeof(config->str), optarg);
867         config->flags = SLIRP_CFG_LEGACY;
868         config->next = slirp_configs;
869         slirp_configs = config;
870         *ret = 0;
871     } else {
872         *ret = slirp_guestfwd(QTAILQ_FIRST(&slirp_stacks), optarg, 1);
873     }
874 
875     return 1;
876 }
877 
878