xref: /openbmc/qemu/net/slirp.c (revision 93b91c59)
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 "net/slirp.h"
25 
26 #include "config-host.h"
27 
28 #ifndef _WIN32
29 #include <pwd.h>
30 #include <sys/wait.h>
31 #endif
32 #include "net.h"
33 #include "net/hub.h"
34 #include "monitor.h"
35 #include "qemu_socket.h"
36 #include "slirp/libslirp.h"
37 
38 static int get_str_sep(char *buf, int buf_size, const char **pp, int sep)
39 {
40     const char *p, *p1;
41     int len;
42     p = *pp;
43     p1 = strchr(p, sep);
44     if (!p1)
45         return -1;
46     len = p1 - p;
47     p1++;
48     if (buf_size > 0) {
49         if (len > buf_size - 1)
50             len = buf_size - 1;
51         memcpy(buf, p, len);
52         buf[len] = '\0';
53     }
54     *pp = p1;
55     return 0;
56 }
57 
58 /* slirp network adapter */
59 
60 #define SLIRP_CFG_HOSTFWD 1
61 #define SLIRP_CFG_LEGACY  2
62 
63 struct slirp_config_str {
64     struct slirp_config_str *next;
65     int flags;
66     char str[1024];
67     int legacy_format;
68 };
69 
70 typedef struct SlirpState {
71     NetClientState nc;
72     QTAILQ_ENTRY(SlirpState) entry;
73     Slirp *slirp;
74 #ifndef _WIN32
75     char smb_dir[128];
76 #endif
77 } SlirpState;
78 
79 static struct slirp_config_str *slirp_configs;
80 const char *legacy_tftp_prefix;
81 const char *legacy_bootp_filename;
82 static QTAILQ_HEAD(slirp_stacks, SlirpState) slirp_stacks =
83     QTAILQ_HEAD_INITIALIZER(slirp_stacks);
84 
85 static int slirp_hostfwd(SlirpState *s, const char *redir_str,
86                          int legacy_format);
87 static int slirp_guestfwd(SlirpState *s, const char *config_str,
88                           int legacy_format);
89 
90 #ifndef _WIN32
91 static const char *legacy_smb_export;
92 
93 static int slirp_smb(SlirpState *s, const char *exported_dir,
94                      struct in_addr vserver_addr);
95 static void slirp_smb_cleanup(SlirpState *s);
96 #else
97 static inline void slirp_smb_cleanup(SlirpState *s) { }
98 #endif
99 
100 void slirp_output(void *opaque, const uint8_t *pkt, int pkt_len)
101 {
102     SlirpState *s = opaque;
103 
104     qemu_send_packet(&s->nc, pkt, pkt_len);
105 }
106 
107 static ssize_t net_slirp_receive(NetClientState *nc, const uint8_t *buf, size_t size)
108 {
109     SlirpState *s = DO_UPCAST(SlirpState, nc, nc);
110 
111     slirp_input(s->slirp, buf, size);
112 
113     return size;
114 }
115 
116 static void net_slirp_cleanup(NetClientState *nc)
117 {
118     SlirpState *s = DO_UPCAST(SlirpState, nc, nc);
119 
120     slirp_cleanup(s->slirp);
121     slirp_smb_cleanup(s);
122     QTAILQ_REMOVE(&slirp_stacks, s, entry);
123 }
124 
125 static NetClientInfo net_slirp_info = {
126     .type = NET_CLIENT_OPTIONS_KIND_USER,
127     .size = sizeof(SlirpState),
128     .receive = net_slirp_receive,
129     .cleanup = net_slirp_cleanup,
130 };
131 
132 static int net_slirp_init(NetClientState *peer, const char *model,
133                           const char *name, int restricted,
134                           const char *vnetwork, const char *vhost,
135                           const char *vhostname, const char *tftp_export,
136                           const char *bootfile, const char *vdhcp_start,
137                           const char *vnameserver, const char *smb_export,
138                           const char *vsmbserver)
139 {
140     /* default settings according to historic slirp */
141     struct in_addr net  = { .s_addr = htonl(0x0a000200) }; /* 10.0.2.0 */
142     struct in_addr mask = { .s_addr = htonl(0xffffff00) }; /* 255.255.255.0 */
143     struct in_addr host = { .s_addr = htonl(0x0a000202) }; /* 10.0.2.2 */
144     struct in_addr dhcp = { .s_addr = htonl(0x0a00020f) }; /* 10.0.2.15 */
145     struct in_addr dns  = { .s_addr = htonl(0x0a000203) }; /* 10.0.2.3 */
146 #ifndef _WIN32
147     struct in_addr smbsrv = { .s_addr = 0 };
148 #endif
149     NetClientState *nc;
150     SlirpState *s;
151     char buf[20];
152     uint32_t addr;
153     int shift;
154     char *end;
155     struct slirp_config_str *config;
156 
157     if (!tftp_export) {
158         tftp_export = legacy_tftp_prefix;
159     }
160     if (!bootfile) {
161         bootfile = legacy_bootp_filename;
162     }
163 
164     if (vnetwork) {
165         if (get_str_sep(buf, sizeof(buf), &vnetwork, '/') < 0) {
166             if (!inet_aton(vnetwork, &net)) {
167                 return -1;
168             }
169             addr = ntohl(net.s_addr);
170             if (!(addr & 0x80000000)) {
171                 mask.s_addr = htonl(0xff000000); /* class A */
172             } else if ((addr & 0xfff00000) == 0xac100000) {
173                 mask.s_addr = htonl(0xfff00000); /* priv. 172.16.0.0/12 */
174             } else if ((addr & 0xc0000000) == 0x80000000) {
175                 mask.s_addr = htonl(0xffff0000); /* class B */
176             } else if ((addr & 0xffff0000) == 0xc0a80000) {
177                 mask.s_addr = htonl(0xffff0000); /* priv. 192.168.0.0/16 */
178             } else if ((addr & 0xffff0000) == 0xc6120000) {
179                 mask.s_addr = htonl(0xfffe0000); /* tests 198.18.0.0/15 */
180             } else if ((addr & 0xe0000000) == 0xe0000000) {
181                 mask.s_addr = htonl(0xffffff00); /* class C */
182             } else {
183                 mask.s_addr = htonl(0xfffffff0); /* multicast/reserved */
184             }
185         } else {
186             if (!inet_aton(buf, &net)) {
187                 return -1;
188             }
189             shift = strtol(vnetwork, &end, 10);
190             if (*end != '\0') {
191                 if (!inet_aton(vnetwork, &mask)) {
192                     return -1;
193                 }
194             } else if (shift < 4 || shift > 32) {
195                 return -1;
196             } else {
197                 mask.s_addr = htonl(0xffffffff << (32 - shift));
198             }
199         }
200         net.s_addr &= mask.s_addr;
201         host.s_addr = net.s_addr | (htonl(0x0202) & ~mask.s_addr);
202         dhcp.s_addr = net.s_addr | (htonl(0x020f) & ~mask.s_addr);
203         dns.s_addr  = net.s_addr | (htonl(0x0203) & ~mask.s_addr);
204     }
205 
206     if (vhost && !inet_aton(vhost, &host)) {
207         return -1;
208     }
209     if ((host.s_addr & mask.s_addr) != net.s_addr) {
210         return -1;
211     }
212 
213     if (vdhcp_start && !inet_aton(vdhcp_start, &dhcp)) {
214         return -1;
215     }
216     if ((dhcp.s_addr & mask.s_addr) != net.s_addr ||
217         dhcp.s_addr == host.s_addr || dhcp.s_addr == dns.s_addr) {
218         return -1;
219     }
220 
221     if (vnameserver && !inet_aton(vnameserver, &dns)) {
222         return -1;
223     }
224     if ((dns.s_addr & mask.s_addr) != net.s_addr ||
225         dns.s_addr == host.s_addr) {
226         return -1;
227     }
228 
229 #ifndef _WIN32
230     if (vsmbserver && !inet_aton(vsmbserver, &smbsrv)) {
231         return -1;
232     }
233 #endif
234 
235     nc = qemu_new_net_client(&net_slirp_info, peer, model, name);
236 
237     snprintf(nc->info_str, sizeof(nc->info_str),
238              "net=%s,restrict=%s", inet_ntoa(net),
239              restricted ? "on" : "off");
240 
241     s = DO_UPCAST(SlirpState, nc, nc);
242 
243     s->slirp = slirp_init(restricted, net, mask, host, vhostname,
244                           tftp_export, bootfile, dhcp, dns, s);
245     QTAILQ_INSERT_TAIL(&slirp_stacks, s, entry);
246 
247     for (config = slirp_configs; config; config = config->next) {
248         if (config->flags & SLIRP_CFG_HOSTFWD) {
249             if (slirp_hostfwd(s, config->str,
250                               config->flags & SLIRP_CFG_LEGACY) < 0)
251                 goto error;
252         } else {
253             if (slirp_guestfwd(s, config->str,
254                                config->flags & SLIRP_CFG_LEGACY) < 0)
255                 goto error;
256         }
257     }
258 #ifndef _WIN32
259     if (!smb_export) {
260         smb_export = legacy_smb_export;
261     }
262     if (smb_export) {
263         if (slirp_smb(s, smb_export, smbsrv) < 0)
264             goto error;
265     }
266 #endif
267 
268     return 0;
269 
270 error:
271     qemu_del_net_client(nc);
272     return -1;
273 }
274 
275 static SlirpState *slirp_lookup(Monitor *mon, const char *vlan,
276                                 const char *stack)
277 {
278 
279     if (vlan) {
280         NetClientState *nc;
281         nc = net_hub_find_client_by_name(strtol(vlan, NULL, 0), stack);
282         if (!nc) {
283             return NULL;
284         }
285         if (strcmp(nc->model, "user")) {
286             monitor_printf(mon, "invalid device specified\n");
287             return NULL;
288         }
289         return DO_UPCAST(SlirpState, nc, nc);
290     } else {
291         if (QTAILQ_EMPTY(&slirp_stacks)) {
292             monitor_printf(mon, "user mode network stack not in use\n");
293             return NULL;
294         }
295         return QTAILQ_FIRST(&slirp_stacks);
296     }
297 }
298 
299 void net_slirp_hostfwd_remove(Monitor *mon, const QDict *qdict)
300 {
301     struct in_addr host_addr = { .s_addr = INADDR_ANY };
302     int host_port;
303     char buf[256];
304     const char *src_str, *p;
305     SlirpState *s;
306     int is_udp = 0;
307     int err;
308     const char *arg1 = qdict_get_str(qdict, "arg1");
309     const char *arg2 = qdict_get_try_str(qdict, "arg2");
310     const char *arg3 = qdict_get_try_str(qdict, "arg3");
311 
312     if (arg2) {
313         s = slirp_lookup(mon, arg1, arg2);
314         src_str = arg3;
315     } else {
316         s = slirp_lookup(mon, NULL, NULL);
317         src_str = arg1;
318     }
319     if (!s) {
320         return;
321     }
322 
323     p = src_str;
324     if (!p || get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
325         goto fail_syntax;
326     }
327 
328     if (!strcmp(buf, "tcp") || buf[0] == '\0') {
329         is_udp = 0;
330     } else if (!strcmp(buf, "udp")) {
331         is_udp = 1;
332     } else {
333         goto fail_syntax;
334     }
335 
336     if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
337         goto fail_syntax;
338     }
339     if (buf[0] != '\0' && !inet_aton(buf, &host_addr)) {
340         goto fail_syntax;
341     }
342 
343     host_port = atoi(p);
344 
345     err = slirp_remove_hostfwd(QTAILQ_FIRST(&slirp_stacks)->slirp, is_udp,
346                                host_addr, host_port);
347 
348     monitor_printf(mon, "host forwarding rule for %s %s\n", src_str,
349                    err ? "not found" : "removed");
350     return;
351 
352  fail_syntax:
353     monitor_printf(mon, "invalid format\n");
354 }
355 
356 static int slirp_hostfwd(SlirpState *s, const char *redir_str,
357                          int legacy_format)
358 {
359     struct in_addr host_addr = { .s_addr = INADDR_ANY };
360     struct in_addr guest_addr = { .s_addr = 0 };
361     int host_port, guest_port;
362     const char *p;
363     char buf[256];
364     int is_udp;
365     char *end;
366 
367     p = redir_str;
368     if (!p || get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
369         goto fail_syntax;
370     }
371     if (!strcmp(buf, "tcp") || buf[0] == '\0') {
372         is_udp = 0;
373     } else if (!strcmp(buf, "udp")) {
374         is_udp = 1;
375     } else {
376         goto fail_syntax;
377     }
378 
379     if (!legacy_format) {
380         if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
381             goto fail_syntax;
382         }
383         if (buf[0] != '\0' && !inet_aton(buf, &host_addr)) {
384             goto fail_syntax;
385         }
386     }
387 
388     if (get_str_sep(buf, sizeof(buf), &p, legacy_format ? ':' : '-') < 0) {
389         goto fail_syntax;
390     }
391     host_port = strtol(buf, &end, 0);
392     if (*end != '\0' || host_port < 1 || host_port > 65535) {
393         goto fail_syntax;
394     }
395 
396     if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
397         goto fail_syntax;
398     }
399     if (buf[0] != '\0' && !inet_aton(buf, &guest_addr)) {
400         goto fail_syntax;
401     }
402 
403     guest_port = strtol(p, &end, 0);
404     if (*end != '\0' || guest_port < 1 || guest_port > 65535) {
405         goto fail_syntax;
406     }
407 
408     if (slirp_add_hostfwd(s->slirp, is_udp, host_addr, host_port, guest_addr,
409                           guest_port) < 0) {
410         error_report("could not set up host forwarding rule '%s'",
411                      redir_str);
412         return -1;
413     }
414     return 0;
415 
416  fail_syntax:
417     error_report("invalid host forwarding rule '%s'", redir_str);
418     return -1;
419 }
420 
421 void net_slirp_hostfwd_add(Monitor *mon, const QDict *qdict)
422 {
423     const char *redir_str;
424     SlirpState *s;
425     const char *arg1 = qdict_get_str(qdict, "arg1");
426     const char *arg2 = qdict_get_try_str(qdict, "arg2");
427     const char *arg3 = qdict_get_try_str(qdict, "arg3");
428 
429     if (arg2) {
430         s = slirp_lookup(mon, arg1, arg2);
431         redir_str = arg3;
432     } else {
433         s = slirp_lookup(mon, NULL, NULL);
434         redir_str = arg1;
435     }
436     if (s) {
437         slirp_hostfwd(s, redir_str, 0);
438     }
439 
440 }
441 
442 int net_slirp_redir(const char *redir_str)
443 {
444     struct slirp_config_str *config;
445 
446     if (QTAILQ_EMPTY(&slirp_stacks)) {
447         config = g_malloc(sizeof(*config));
448         pstrcpy(config->str, sizeof(config->str), redir_str);
449         config->flags = SLIRP_CFG_HOSTFWD | SLIRP_CFG_LEGACY;
450         config->next = slirp_configs;
451         slirp_configs = config;
452         return 0;
453     }
454 
455     return slirp_hostfwd(QTAILQ_FIRST(&slirp_stacks), redir_str, 1);
456 }
457 
458 #ifndef _WIN32
459 
460 /* automatic user mode samba server configuration */
461 static void slirp_smb_cleanup(SlirpState *s)
462 {
463     char cmd[128];
464     int ret;
465 
466     if (s->smb_dir[0] != '\0') {
467         snprintf(cmd, sizeof(cmd), "rm -rf %s", s->smb_dir);
468         ret = system(cmd);
469         if (ret == -1 || !WIFEXITED(ret)) {
470             error_report("'%s' failed.", cmd);
471         } else if (WEXITSTATUS(ret)) {
472             error_report("'%s' failed. Error code: %d",
473                          cmd, WEXITSTATUS(ret));
474         }
475         s->smb_dir[0] = '\0';
476     }
477 }
478 
479 static int slirp_smb(SlirpState* s, const char *exported_dir,
480                      struct in_addr vserver_addr)
481 {
482     static int instance;
483     char smb_conf[128];
484     char smb_cmdline[128];
485     struct passwd *passwd;
486     FILE *f;
487 
488     passwd = getpwuid(geteuid());
489     if (!passwd) {
490         error_report("failed to retrieve user name");
491         return -1;
492     }
493 
494     if (access(CONFIG_SMBD_COMMAND, F_OK)) {
495         error_report("could not find '%s', please install it",
496                      CONFIG_SMBD_COMMAND);
497         return -1;
498     }
499 
500     if (access(exported_dir, R_OK | X_OK)) {
501         error_report("error accessing shared directory '%s': %s",
502                      exported_dir, strerror(errno));
503         return -1;
504     }
505 
506     snprintf(s->smb_dir, sizeof(s->smb_dir), "/tmp/qemu-smb.%ld-%d",
507              (long)getpid(), instance++);
508     if (mkdir(s->smb_dir, 0700) < 0) {
509         error_report("could not create samba server dir '%s'", s->smb_dir);
510         return -1;
511     }
512     snprintf(smb_conf, sizeof(smb_conf), "%s/%s", s->smb_dir, "smb.conf");
513 
514     f = fopen(smb_conf, "w");
515     if (!f) {
516         slirp_smb_cleanup(s);
517         error_report("could not create samba server configuration file '%s'",
518                      smb_conf);
519         return -1;
520     }
521     fprintf(f,
522             "[global]\n"
523             "private dir=%s\n"
524             "socket address=127.0.0.1\n"
525             "pid directory=%s\n"
526             "lock directory=%s\n"
527             "state directory=%s\n"
528             "log file=%s/log.smbd\n"
529             "smb passwd file=%s/smbpasswd\n"
530             "security = share\n"
531             "[qemu]\n"
532             "path=%s\n"
533             "read only=no\n"
534             "guest ok=yes\n"
535             "force user=%s\n",
536             s->smb_dir,
537             s->smb_dir,
538             s->smb_dir,
539             s->smb_dir,
540             s->smb_dir,
541             s->smb_dir,
542             exported_dir,
543             passwd->pw_name
544             );
545     fclose(f);
546 
547     snprintf(smb_cmdline, sizeof(smb_cmdline), "%s -s %s",
548              CONFIG_SMBD_COMMAND, smb_conf);
549 
550     if (slirp_add_exec(s->slirp, 0, smb_cmdline, &vserver_addr, 139) < 0) {
551         slirp_smb_cleanup(s);
552         error_report("conflicting/invalid smbserver address");
553         return -1;
554     }
555     return 0;
556 }
557 
558 /* automatic user mode samba server configuration (legacy interface) */
559 int net_slirp_smb(const char *exported_dir)
560 {
561     struct in_addr vserver_addr = { .s_addr = 0 };
562 
563     if (legacy_smb_export) {
564         fprintf(stderr, "-smb given twice\n");
565         return -1;
566     }
567     legacy_smb_export = exported_dir;
568     if (!QTAILQ_EMPTY(&slirp_stacks)) {
569         return slirp_smb(QTAILQ_FIRST(&slirp_stacks), exported_dir,
570                          vserver_addr);
571     }
572     return 0;
573 }
574 
575 #endif /* !defined(_WIN32) */
576 
577 struct GuestFwd {
578     CharDriverState *hd;
579     struct in_addr server;
580     int port;
581     Slirp *slirp;
582 };
583 
584 static int guestfwd_can_read(void *opaque)
585 {
586     struct GuestFwd *fwd = opaque;
587     return slirp_socket_can_recv(fwd->slirp, fwd->server, fwd->port);
588 }
589 
590 static void guestfwd_read(void *opaque, const uint8_t *buf, int size)
591 {
592     struct GuestFwd *fwd = opaque;
593     slirp_socket_recv(fwd->slirp, fwd->server, fwd->port, buf, size);
594 }
595 
596 static int slirp_guestfwd(SlirpState *s, const char *config_str,
597                           int legacy_format)
598 {
599     struct in_addr server = { .s_addr = 0 };
600     struct GuestFwd *fwd;
601     const char *p;
602     char buf[128];
603     char *end;
604     int port;
605 
606     p = config_str;
607     if (legacy_format) {
608         if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
609             goto fail_syntax;
610         }
611     } else {
612         if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
613             goto fail_syntax;
614         }
615         if (strcmp(buf, "tcp") && buf[0] != '\0') {
616             goto fail_syntax;
617         }
618         if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
619             goto fail_syntax;
620         }
621         if (buf[0] != '\0' && !inet_aton(buf, &server)) {
622             goto fail_syntax;
623         }
624         if (get_str_sep(buf, sizeof(buf), &p, '-') < 0) {
625             goto fail_syntax;
626         }
627     }
628     port = strtol(buf, &end, 10);
629     if (*end != '\0' || port < 1 || port > 65535) {
630         goto fail_syntax;
631     }
632 
633     fwd = g_malloc(sizeof(struct GuestFwd));
634     snprintf(buf, sizeof(buf), "guestfwd.tcp.%d", port);
635 
636     if ((strlen(p) > 4) && !strncmp(p, "cmd:", 4)) {
637         if (slirp_add_exec(s->slirp, 0, &p[4], &server, port) < 0) {
638             error_report("conflicting/invalid host:port in guest forwarding "
639                          "rule '%s'", config_str);
640             g_free(fwd);
641             return -1;
642         }
643     } else {
644         fwd->hd = qemu_chr_new(buf, p, NULL);
645         if (!fwd->hd) {
646             error_report("could not open guest forwarding device '%s'", buf);
647             g_free(fwd);
648             return -1;
649         }
650 
651         if (slirp_add_exec(s->slirp, 3, fwd->hd, &server, port) < 0) {
652             error_report("conflicting/invalid host:port in guest forwarding "
653                          "rule '%s'", config_str);
654             g_free(fwd);
655             return -1;
656         }
657         fwd->server = server;
658         fwd->port = port;
659         fwd->slirp = s->slirp;
660 
661         qemu_chr_add_handlers(fwd->hd, guestfwd_can_read, guestfwd_read,
662                               NULL, fwd);
663     }
664     return 0;
665 
666  fail_syntax:
667     error_report("invalid guest forwarding rule '%s'", config_str);
668     return -1;
669 }
670 
671 void do_info_usernet(Monitor *mon)
672 {
673     SlirpState *s;
674 
675     QTAILQ_FOREACH(s, &slirp_stacks, entry) {
676         int id;
677         bool got_vlan_id = net_hub_id_for_client(&s->nc, &id) == 0;
678         monitor_printf(mon, "VLAN %d (%s):\n",
679                        got_vlan_id ? id : -1,
680                        s->nc.name);
681         slirp_connection_info(s->slirp, mon);
682     }
683 }
684 
685 static void
686 net_init_slirp_configs(const StringList *fwd, int flags)
687 {
688     while (fwd) {
689         struct slirp_config_str *config;
690 
691         config = g_malloc0(sizeof(*config));
692         pstrcpy(config->str, sizeof(config->str), fwd->value->str);
693         config->flags = flags;
694         config->next = slirp_configs;
695         slirp_configs = config;
696 
697         fwd = fwd->next;
698     }
699 }
700 
701 int net_init_slirp(const NetClientOptions *opts, const char *name,
702                    NetClientState *peer)
703 {
704     struct slirp_config_str *config;
705     char *vnet;
706     int ret;
707     const NetdevUserOptions *user;
708 
709     assert(opts->kind == NET_CLIENT_OPTIONS_KIND_USER);
710     user = opts->user;
711 
712     vnet = user->has_net ? g_strdup(user->net) :
713            user->has_ip  ? g_strdup_printf("%s/24", user->ip) :
714            NULL;
715 
716     /* all optional fields are initialized to "all bits zero" */
717 
718     net_init_slirp_configs(user->hostfwd, SLIRP_CFG_HOSTFWD);
719     net_init_slirp_configs(user->guestfwd, 0);
720 
721     ret = net_slirp_init(peer, "user", name, user->q_restrict, vnet,
722                          user->host, user->hostname, user->tftp,
723                          user->bootfile, user->dhcpstart, user->dns, user->smb,
724                          user->smbserver);
725 
726     while (slirp_configs) {
727         config = slirp_configs;
728         slirp_configs = config->next;
729         g_free(config);
730     }
731 
732     g_free(vnet);
733 
734     return ret;
735 }
736 
737 int net_slirp_parse_legacy(QemuOptsList *opts_list, const char *optarg, int *ret)
738 {
739     if (strcmp(opts_list->name, "net") != 0 ||
740         strncmp(optarg, "channel,", strlen("channel,")) != 0) {
741         return 0;
742     }
743 
744     /* handle legacy -net channel,port:chr */
745     optarg += strlen("channel,");
746 
747     if (QTAILQ_EMPTY(&slirp_stacks)) {
748         struct slirp_config_str *config;
749 
750         config = g_malloc(sizeof(*config));
751         pstrcpy(config->str, sizeof(config->str), optarg);
752         config->flags = SLIRP_CFG_LEGACY;
753         config->next = slirp_configs;
754         slirp_configs = config;
755         *ret = 0;
756     } else {
757         *ret = slirp_guestfwd(QTAILQ_FIRST(&slirp_stacks), optarg, 1);
758     }
759 
760     return 1;
761 }
762 
763