]> git.proxmox.com Git - mirror_qemu.git/blame - net/slirp.c
qapi: net: Add query-netdev command
[mirror_qemu.git] / net / slirp.c
CommitLineData
68ac40d2
MM
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 */
452fcdbc 24
2744d920 25#include "qemu/osdep.h"
2addc8fb 26#include "qemu/log.h"
68ac40d2
MM
27#include "net/slirp.h"
28
68ac40d2 29
28b150bf 30#ifndef _WIN32
1cb1c5d1 31#include <pwd.h>
28b150bf
BS
32#include <sys/wait.h>
33#endif
1422e32d 34#include "net/net.h"
a245fc18
PB
35#include "clients.h"
36#include "hub.h"
83c9089e 37#include "monitor/monitor.h"
d49b6836 38#include "qemu/error-report.h"
1de7afc9 39#include "qemu/sockets.h"
675b9b53 40#include <libslirp.h>
4d43a603 41#include "chardev/char-fe.h"
f6c2e66a 42#include "sysemu/sysemu.h"
f348b6d1 43#include "qemu/cutils.h"
32a6ebec 44#include "qapi/error.h"
452fcdbc 45#include "qapi/qmp/qdict.h"
e05ae1d9 46#include "util.h"
d8903441
MAL
47#include "migration/register.h"
48#include "migration/qemu-file-types.h"
68ac40d2
MM
49
50static int get_str_sep(char *buf, int buf_size, const char **pp, int sep)
51{
52 const char *p, *p1;
53 int len;
54 p = *pp;
55 p1 = strchr(p, sep);
56 if (!p1)
57 return -1;
58 len = p1 - p;
59 p1++;
60 if (buf_size > 0) {
61 if (len > buf_size - 1)
62 len = buf_size - 1;
63 memcpy(buf, p, len);
64 buf[len] = '\0';
65 }
66 *pp = p1;
67 return 0;
68}
69
70/* slirp network adapter */
71
72#define SLIRP_CFG_HOSTFWD 1
68ac40d2
MM
73
74struct slirp_config_str {
75 struct slirp_config_str *next;
76 int flags;
77 char str[1024];
68ac40d2
MM
78};
79
8d45a3b9
MAL
80struct GuestFwd {
81 CharBackend hd;
82 struct in_addr server;
83 int port;
84 Slirp *slirp;
85};
86
68ac40d2 87typedef struct SlirpState {
4e68f7a0 88 NetClientState nc;
68ac40d2 89 QTAILQ_ENTRY(SlirpState) entry;
68ac40d2 90 Slirp *slirp;
1ab67b98 91 Notifier poll_notifier;
f6c2e66a 92 Notifier exit_notifier;
68ac40d2 93#ifndef _WIN32
f95cc8b6 94 gchar *smb_dir;
68ac40d2 95#endif
8d45a3b9 96 GSList *fwd;
68ac40d2
MM
97} SlirpState;
98
99static struct slirp_config_str *slirp_configs;
b58deb34 100static QTAILQ_HEAD(, SlirpState) slirp_stacks =
68ac40d2
MM
101 QTAILQ_HEAD_INITIALIZER(slirp_stacks);
102
d18572dd
TH
103static int slirp_hostfwd(SlirpState *s, const char *redir_str, Error **errp);
104static int slirp_guestfwd(SlirpState *s, const char *config_str, Error **errp);
68ac40d2
MM
105
106#ifndef _WIN32
68ac40d2 107static int slirp_smb(SlirpState *s, const char *exported_dir,
5c843af2 108 struct in_addr vserver_addr, Error **errp);
68ac40d2
MM
109static void slirp_smb_cleanup(SlirpState *s);
110#else
111static inline void slirp_smb_cleanup(SlirpState *s) { }
112#endif
113
625a526b
MAL
114static ssize_t net_slirp_send_packet(const void *pkt, size_t pkt_len,
115 void *opaque)
68ac40d2
MM
116{
117 SlirpState *s = opaque;
118
625a526b 119 return qemu_send_packet(&s->nc, pkt, pkt_len);
68ac40d2
MM
120}
121
4e68f7a0 122static ssize_t net_slirp_receive(NetClientState *nc, const uint8_t *buf, size_t size)
68ac40d2 123{
ce20b5be 124 SlirpState *s = DO_UPCAST(SlirpState, nc, nc);
68ac40d2
MM
125
126 slirp_input(s->slirp, buf, size);
127
128 return size;
129}
130
f6c2e66a
PB
131static void slirp_smb_exit(Notifier *n, void *data)
132{
133 SlirpState *s = container_of(n, SlirpState, exit_notifier);
134 slirp_smb_cleanup(s);
135}
136
8d45a3b9
MAL
137static void slirp_free_fwd(gpointer data)
138{
139 struct GuestFwd *fwd = data;
140
141 qemu_chr_fe_deinit(&fwd->hd, true);
142 g_free(data);
143}
144
4e68f7a0 145static void net_slirp_cleanup(NetClientState *nc)
68ac40d2 146{
ce20b5be 147 SlirpState *s = DO_UPCAST(SlirpState, nc, nc);
68ac40d2 148
8d45a3b9 149 g_slist_free_full(s->fwd, slirp_free_fwd);
1ab67b98 150 main_loop_poll_remove_notifier(&s->poll_notifier);
d8903441 151 unregister_savevm(NULL, "slirp", s->slirp);
68ac40d2 152 slirp_cleanup(s->slirp);
67f3280c
MAL
153 if (s->exit_notifier.notify) {
154 qemu_remove_exit_notifier(&s->exit_notifier);
155 }
68ac40d2
MM
156 slirp_smb_cleanup(s);
157 QTAILQ_REMOVE(&slirp_stacks, s, entry);
68ac40d2
MM
158}
159
ce20b5be 160static NetClientInfo net_slirp_info = {
f394b2e2 161 .type = NET_CLIENT_DRIVER_USER,
ce20b5be
MM
162 .size = sizeof(SlirpState),
163 .receive = net_slirp_receive,
164 .cleanup = net_slirp_cleanup,
165};
166
3e0fad3a 167static void net_slirp_guest_error(const char *msg, void *opaque)
2addc8fb
MAL
168{
169 qemu_log_mask(LOG_GUEST_ERROR, "%s", msg);
170}
171
3e0fad3a 172static int64_t net_slirp_clock_get_ns(void *opaque)
e6dbff3f
MAL
173{
174 return qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
175}
176
3e0fad3a
MAL
177static void *net_slirp_timer_new(SlirpTimerCb cb,
178 void *cb_opaque, void *opaque)
07abf6d4
MAL
179{
180 return timer_new_full(NULL, QEMU_CLOCK_VIRTUAL,
181 SCALE_MS, QEMU_TIMER_ATTR_EXTERNAL,
3e0fad3a 182 cb, cb_opaque);
07abf6d4
MAL
183}
184
3e0fad3a 185static void net_slirp_timer_free(void *timer, void *opaque)
07abf6d4 186{
07abf6d4
MAL
187 timer_free(timer);
188}
189
3e0fad3a
MAL
190static void net_slirp_timer_mod(void *timer, int64_t expire_timer,
191 void *opaque)
07abf6d4
MAL
192{
193 timer_mod(timer, expire_timer);
194}
195
3e0fad3a 196static void net_slirp_register_poll_fd(int fd, void *opaque)
848c7092
MAL
197{
198 qemu_fd_register(fd);
199}
200
3e0fad3a 201static void net_slirp_unregister_poll_fd(int fd, void *opaque)
f6e5aa36
MAL
202{
203 /* no qemu_fd_unregister */
204}
205
3e0fad3a
MAL
206static void net_slirp_notify(void *opaque)
207{
208 qemu_notify_event();
209}
210
d846b927 211static const SlirpCb slirp_cb = {
625a526b 212 .send_packet = net_slirp_send_packet,
2addc8fb 213 .guest_error = net_slirp_guest_error,
e6dbff3f 214 .clock_get_ns = net_slirp_clock_get_ns,
07abf6d4
MAL
215 .timer_new = net_slirp_timer_new,
216 .timer_free = net_slirp_timer_free,
217 .timer_mod = net_slirp_timer_mod,
848c7092 218 .register_poll_fd = net_slirp_register_poll_fd,
f6e5aa36 219 .unregister_poll_fd = net_slirp_unregister_poll_fd,
3e0fad3a 220 .notify = net_slirp_notify,
d846b927
MAL
221};
222
deaeb3f7
MAL
223static int slirp_poll_to_gio(int events)
224{
225 int ret = 0;
226
227 if (events & SLIRP_POLL_IN) {
228 ret |= G_IO_IN;
229 }
230 if (events & SLIRP_POLL_OUT) {
231 ret |= G_IO_OUT;
232 }
233 if (events & SLIRP_POLL_PRI) {
234 ret |= G_IO_PRI;
235 }
236 if (events & SLIRP_POLL_ERR) {
237 ret |= G_IO_ERR;
238 }
239 if (events & SLIRP_POLL_HUP) {
240 ret |= G_IO_HUP;
241 }
242
243 return ret;
244}
245
246static int net_slirp_add_poll(int fd, int events, void *opaque)
247{
248 GArray *pollfds = opaque;
249 GPollFD pfd = {
250 .fd = fd,
251 .events = slirp_poll_to_gio(events),
252 };
253 int idx = pollfds->len;
254 g_array_append_val(pollfds, pfd);
255 return idx;
256}
257
258static int slirp_gio_to_poll(int events)
259{
260 int ret = 0;
261
262 if (events & G_IO_IN) {
263 ret |= SLIRP_POLL_IN;
264 }
265 if (events & G_IO_OUT) {
266 ret |= SLIRP_POLL_OUT;
267 }
268 if (events & G_IO_PRI) {
269 ret |= SLIRP_POLL_PRI;
270 }
271 if (events & G_IO_ERR) {
272 ret |= SLIRP_POLL_ERR;
273 }
274 if (events & G_IO_HUP) {
275 ret |= SLIRP_POLL_HUP;
276 }
277
278 return ret;
279}
280
281static int net_slirp_get_revents(int idx, void *opaque)
282{
283 GArray *pollfds = opaque;
284
285 return slirp_gio_to_poll(g_array_index(pollfds, GPollFD, idx).revents);
286}
287
1ab67b98
MAL
288static void net_slirp_poll_notify(Notifier *notifier, void *data)
289{
290 MainLoopPoll *poll = data;
291 SlirpState *s = container_of(notifier, SlirpState, poll_notifier);
292
293 switch (poll->state) {
294 case MAIN_LOOP_POLL_FILL:
deaeb3f7
MAL
295 slirp_pollfds_fill(s->slirp, &poll->timeout,
296 net_slirp_add_poll, poll->pollfds);
1ab67b98
MAL
297 break;
298 case MAIN_LOOP_POLL_OK:
299 case MAIN_LOOP_POLL_ERR:
deaeb3f7
MAL
300 slirp_pollfds_poll(s->slirp, poll->state == MAIN_LOOP_POLL_ERR,
301 net_slirp_get_revents, poll->pollfds);
1ab67b98
MAL
302 break;
303 default:
304 g_assert_not_reached();
305 }
306}
307
d8903441
MAL
308static ssize_t
309net_slirp_stream_read(void *buf, size_t size, void *opaque)
310{
311 QEMUFile *f = opaque;
312
313 return qemu_get_buffer(f, buf, size);
314}
315
316static ssize_t
317net_slirp_stream_write(const void *buf, size_t size, void *opaque)
318{
319 QEMUFile *f = opaque;
320
321 qemu_put_buffer(f, buf, size);
322 if (qemu_file_get_error(f)) {
323 return -1;
324 }
325
326 return size;
327}
328
329static int net_slirp_state_load(QEMUFile *f, void *opaque, int version_id)
330{
331 Slirp *slirp = opaque;
332
333 return slirp_state_load(slirp, version_id, net_slirp_stream_read, f);
334}
335
336static void net_slirp_state_save(QEMUFile *f, void *opaque)
337{
338 Slirp *slirp = opaque;
339
340 slirp_state_save(slirp, net_slirp_stream_write, f);
341}
342
343static SaveVMHandlers savevm_slirp_state = {
344 .save_state = net_slirp_state_save,
345 .load_state = net_slirp_state_load,
346};
347
4e68f7a0 348static int net_slirp_init(NetClientState *peer, const char *model,
68ac40d2 349 const char *name, int restricted,
0b11c036
ST
350 bool ipv4, const char *vnetwork, const char *vhost,
351 bool ipv6, const char *vprefix6, int vprefix6_len,
7aac531e 352 const char *vhost6,
68ac40d2
MM
353 const char *vhostname, const char *tftp_export,
354 const char *bootfile, const char *vdhcp_start,
7aac531e
YB
355 const char *vnameserver, const char *vnameserver6,
356 const char *smb_export, const char *vsmbserver,
f18d1375 357 const char **dnssearch, const char *vdomainname,
0fca92b9 358 const char *tftp_server_name,
f18d1375 359 Error **errp)
68ac40d2
MM
360{
361 /* default settings according to historic slirp */
362 struct in_addr net = { .s_addr = htonl(0x0a000200) }; /* 10.0.2.0 */
363 struct in_addr mask = { .s_addr = htonl(0xffffff00) }; /* 255.255.255.0 */
364 struct in_addr host = { .s_addr = htonl(0x0a000202) }; /* 10.0.2.2 */
365 struct in_addr dhcp = { .s_addr = htonl(0x0a00020f) }; /* 10.0.2.15 */
366 struct in_addr dns = { .s_addr = htonl(0x0a000203) }; /* 10.0.2.3 */
7aac531e
YB
367 struct in6_addr ip6_prefix;
368 struct in6_addr ip6_host;
369 struct in6_addr ip6_dns;
68ac40d2
MM
370#ifndef _WIN32
371 struct in_addr smbsrv = { .s_addr = 0 };
372#endif
4e68f7a0 373 NetClientState *nc;
68ac40d2
MM
374 SlirpState *s;
375 char buf[20];
376 uint32_t addr;
377 int shift;
378 char *end;
379 struct slirp_config_str *config;
d32ad10a
AK
380 NetdevUserOptions *stored;
381 StringList **stored_hostfwd;
382 StringList **stored_guestfwd;
68ac40d2 383
0b11c036 384 if (!ipv4 && (vnetwork || vhost || vnameserver)) {
5c843af2 385 error_setg(errp, "IPv4 disabled but netmask/host/dns provided");
0b11c036
ST
386 return -1;
387 }
388
389 if (!ipv6 && (vprefix6 || vhost6 || vnameserver6)) {
5c843af2 390 error_setg(errp, "IPv6 disabled but prefix/host6/dns6 provided");
0b11c036
ST
391 return -1;
392 }
393
394 if (!ipv4 && !ipv6) {
395 /* It doesn't make sense to disable both */
5c843af2 396 error_setg(errp, "IPv4 and IPv6 disabled");
0b11c036
ST
397 return -1;
398 }
399
68ac40d2
MM
400 if (vnetwork) {
401 if (get_str_sep(buf, sizeof(buf), &vnetwork, '/') < 0) {
402 if (!inet_aton(vnetwork, &net)) {
5c843af2 403 error_setg(errp, "Failed to parse netmask");
68ac40d2
MM
404 return -1;
405 }
406 addr = ntohl(net.s_addr);
407 if (!(addr & 0x80000000)) {
408 mask.s_addr = htonl(0xff000000); /* class A */
409 } else if ((addr & 0xfff00000) == 0xac100000) {
410 mask.s_addr = htonl(0xfff00000); /* priv. 172.16.0.0/12 */
411 } else if ((addr & 0xc0000000) == 0x80000000) {
412 mask.s_addr = htonl(0xffff0000); /* class B */
413 } else if ((addr & 0xffff0000) == 0xc0a80000) {
414 mask.s_addr = htonl(0xffff0000); /* priv. 192.168.0.0/16 */
415 } else if ((addr & 0xffff0000) == 0xc6120000) {
416 mask.s_addr = htonl(0xfffe0000); /* tests 198.18.0.0/15 */
417 } else if ((addr & 0xe0000000) == 0xe0000000) {
418 mask.s_addr = htonl(0xffffff00); /* class C */
419 } else {
420 mask.s_addr = htonl(0xfffffff0); /* multicast/reserved */
421 }
422 } else {
423 if (!inet_aton(buf, &net)) {
5c843af2 424 error_setg(errp, "Failed to parse netmask");
68ac40d2
MM
425 return -1;
426 }
427 shift = strtol(vnetwork, &end, 10);
428 if (*end != '\0') {
429 if (!inet_aton(vnetwork, &mask)) {
5c843af2
HP
430 error_setg(errp,
431 "Failed to parse netmask (trailing chars)");
68ac40d2
MM
432 return -1;
433 }
434 } else if (shift < 4 || shift > 32) {
5c843af2
HP
435 error_setg(errp,
436 "Invalid netmask provided (must be in range 4-32)");
68ac40d2
MM
437 return -1;
438 } else {
439 mask.s_addr = htonl(0xffffffff << (32 - shift));
440 }
441 }
442 net.s_addr &= mask.s_addr;
443 host.s_addr = net.s_addr | (htonl(0x0202) & ~mask.s_addr);
444 dhcp.s_addr = net.s_addr | (htonl(0x020f) & ~mask.s_addr);
445 dns.s_addr = net.s_addr | (htonl(0x0203) & ~mask.s_addr);
446 }
447
448 if (vhost && !inet_aton(vhost, &host)) {
5c843af2 449 error_setg(errp, "Failed to parse host");
68ac40d2
MM
450 return -1;
451 }
452 if ((host.s_addr & mask.s_addr) != net.s_addr) {
5c843af2 453 error_setg(errp, "Host doesn't belong to network");
68ac40d2
MM
454 return -1;
455 }
456
68756ba8 457 if (vnameserver && !inet_aton(vnameserver, &dns)) {
5c843af2 458 error_setg(errp, "Failed to parse DNS");
68ac40d2
MM
459 return -1;
460 }
120b721f 461 if (restricted && (dns.s_addr & mask.s_addr) != net.s_addr) {
5c843af2
HP
462 error_setg(errp, "DNS doesn't belong to network");
463 return -1;
464 }
465 if (dns.s_addr == host.s_addr) {
466 error_setg(errp, "DNS must be different from host");
68ac40d2
MM
467 return -1;
468 }
469
68756ba8 470 if (vdhcp_start && !inet_aton(vdhcp_start, &dhcp)) {
5c843af2 471 error_setg(errp, "Failed to parse DHCP start address");
68ac40d2
MM
472 return -1;
473 }
5c843af2
HP
474 if ((dhcp.s_addr & mask.s_addr) != net.s_addr) {
475 error_setg(errp, "DHCP doesn't belong to network");
476 return -1;
477 }
478 if (dhcp.s_addr == host.s_addr || dhcp.s_addr == dns.s_addr) {
0c373c01 479 error_setg(errp, "DHCP must be different from host and DNS");
68ac40d2
MM
480 return -1;
481 }
482
483#ifndef _WIN32
484 if (vsmbserver && !inet_aton(vsmbserver, &smbsrv)) {
5c843af2 485 error_setg(errp, "Failed to parse SMB address");
68ac40d2
MM
486 return -1;
487 }
488#endif
489
7aac531e
YB
490 if (!vprefix6) {
491 vprefix6 = "fec0::";
492 }
493 if (!inet_pton(AF_INET6, vprefix6, &ip6_prefix)) {
5c843af2 494 error_setg(errp, "Failed to parse IPv6 prefix");
7aac531e
YB
495 return -1;
496 }
7aac531e
YB
497
498 if (!vprefix6_len) {
499 vprefix6_len = 64;
500 }
501 if (vprefix6_len < 0 || vprefix6_len > 126) {
5c843af2 502 error_setg(errp,
178a0a5d
SG
503 "Invalid IPv6 prefix provided "
504 "(IPv6 prefix length must be between 0 and 126)");
7aac531e
YB
505 return -1;
506 }
507
508 if (vhost6) {
7aac531e 509 if (!inet_pton(AF_INET6, vhost6, &ip6_host)) {
5c843af2 510 error_setg(errp, "Failed to parse IPv6 host");
7aac531e
YB
511 return -1;
512 }
513 if (!in6_equal_net(&ip6_prefix, &ip6_host, vprefix6_len)) {
5c843af2 514 error_setg(errp, "IPv6 Host doesn't belong to network");
7aac531e
YB
515 return -1;
516 }
7aac531e
YB
517 } else {
518 ip6_host = ip6_prefix;
519 ip6_host.s6_addr[15] |= 2;
520 }
521
522 if (vnameserver6) {
7aac531e 523 if (!inet_pton(AF_INET6, vnameserver6, &ip6_dns)) {
5c843af2 524 error_setg(errp, "Failed to parse IPv6 DNS");
7aac531e
YB
525 return -1;
526 }
120b721f 527 if (restricted && !in6_equal_net(&ip6_prefix, &ip6_dns, vprefix6_len)) {
5c843af2 528 error_setg(errp, "IPv6 DNS doesn't belong to network");
7aac531e
YB
529 return -1;
530 }
7aac531e
YB
531 } else {
532 ip6_dns = ip6_prefix;
533 ip6_dns.s6_addr[15] |= 3;
534 }
535
f18d1375
BD
536 if (vdomainname && !*vdomainname) {
537 error_setg(errp, "'domainname' parameter cannot be empty");
538 return -1;
539 }
540
6e157a03
FZ
541 if (vdomainname && strlen(vdomainname) > 255) {
542 error_setg(errp, "'domainname' parameter cannot exceed 255 bytes");
543 return -1;
544 }
545
546 if (vhostname && strlen(vhostname) > 255) {
547 error_setg(errp, "'vhostname' parameter cannot exceed 255 bytes");
548 return -1;
549 }
7aac531e 550
0fca92b9
FZ
551 if (tftp_server_name && strlen(tftp_server_name) > 255) {
552 error_setg(errp, "'tftp-server-name' parameter cannot exceed 255 bytes");
553 return -1;
554 }
555
ab5f3f84 556 nc = qemu_new_net_client(&net_slirp_info, peer, model, name);
ce20b5be 557
d32ad10a
AK
558 /* Store startup parameters */
559 nc->stored_config = g_new0(NetdevInfo, 1);
560 nc->stored_config->type = NET_BACKEND_USER;
561 stored = &nc->stored_config->u.user;
562
563 if (vhostname) {
564 stored->has_hostname = true;
565 stored->hostname = g_strdup(vhostname);
566 }
567
568 stored->has_q_restrict = true;
569 stored->q_restrict = restricted;
570
571 stored->has_ipv4 = true;
572 stored->ipv4 = ipv4;
573
574 stored->has_ipv6 = true;
575 stored->ipv6 = ipv6;
576
577 if (ipv4) {
578 uint8_t *net_bytes = (uint8_t *)&net;
579 uint8_t *mask_bytes = (uint8_t *)&mask;
580
581 stored->has_net = true;
582 stored->net = g_strdup_printf("%d.%d.%d.%d/%d.%d.%d.%d",
583 net_bytes[0], net_bytes[1],
584 net_bytes[2], net_bytes[3],
585 mask_bytes[0], mask_bytes[1],
586 mask_bytes[2], mask_bytes[3]);
587
588 stored->has_host = true;
589 stored->host = g_strdup(inet_ntoa(host));
590 }
591
592 if (tftp_export) {
593 stored->has_tftp = true;
594 stored->tftp = g_strdup(tftp_export);
595 }
596
597 if (bootfile) {
598 stored->has_bootfile = true;
599 stored->bootfile = g_strdup(bootfile);
600 }
601
602 if (vdhcp_start) {
603 stored->has_dhcpstart = true;
604 stored->dhcpstart = g_strdup(vdhcp_start);
605 }
606
607 if (ipv4) {
608 stored->has_dns = true;
609 stored->dns = g_strdup(inet_ntoa(dns));
610 }
611
612 if (dnssearch) {
613 stored->has_dnssearch = true;
614 StringList **stored_list = &stored->dnssearch;
615
616 for (int i = 0; dnssearch[i]; i++) {
617 String *element = g_new0(String, 1);
618
619 element->str = g_strdup(dnssearch[i]);
620 QAPI_LIST_APPEND(stored_list, element);
621 }
622 }
623
624 if (vdomainname) {
625 stored->has_domainname = true;
626 stored->domainname = g_strdup(vdomainname);
627 }
628
629 if (ipv6) {
630 char addrstr[INET6_ADDRSTRLEN];
631 const char *res;
632
633 stored->has_ipv6_prefix = true;
634 stored->ipv6_prefix = g_strdup(vprefix6);
635
636 stored->has_ipv6_prefixlen = true;
637 stored->ipv6_prefixlen = vprefix6_len;
638
639 res = inet_ntop(AF_INET6, &ip6_host,
640 addrstr, sizeof(addrstr));
641
642 stored->has_ipv6_host = true;
643 stored->ipv6_host = g_strdup(res);
644
645 res = inet_ntop(AF_INET6, &ip6_dns,
646 addrstr, sizeof(addrstr));
647
648 stored->has_ipv6_dns = true;
649 stored->ipv6_dns = g_strdup(res);
650 }
651
652 if (smb_export) {
653 stored->has_smb = true;
654 stored->smb = g_strdup(smb_export);
655 }
656
657 if (vsmbserver) {
658 stored->has_smbserver = true;
659 stored->smbserver = g_strdup(vsmbserver);
660 }
661
662 if (tftp_server_name) {
663 stored->has_tftp_server_name = true;
664 stored->tftp_server_name = g_strdup(tftp_server_name);
665 }
666
ce20b5be 667 snprintf(nc->info_str, sizeof(nc->info_str),
c54ed5bc
JK
668 "net=%s,restrict=%s", inet_ntoa(net),
669 restricted ? "on" : "off");
ce20b5be
MM
670
671 s = DO_UPCAST(SlirpState, nc, nc);
672
0b11c036
ST
673 s->slirp = slirp_init(restricted, ipv4, net, mask, host,
674 ipv6, ip6_prefix, vprefix6_len, ip6_host,
0fca92b9
FZ
675 vhostname, tftp_server_name,
676 tftp_export, bootfile, dhcp,
62c1d2c4 677 dns, ip6_dns, dnssearch, vdomainname,
d846b927 678 &slirp_cb, s);
68ac40d2
MM
679 QTAILQ_INSERT_TAIL(&slirp_stacks, s, entry);
680
d8903441
MAL
681 /*
682 * Make sure the current bitstream version of slirp is 4, to avoid
683 * QEMU migration incompatibilities, if upstream slirp bumped the
684 * version.
685 *
686 * FIXME: use bitfields of features? teach libslirp to save with
687 * specific version?
688 */
689 g_assert(slirp_state_version() == 4);
ce62df53 690 register_savevm_live("slirp", 0, slirp_state_version(),
d8903441
MAL
691 &savevm_slirp_state, s->slirp);
692
1ab67b98
MAL
693 s->poll_notifier.notify = net_slirp_poll_notify;
694 main_loop_poll_add_notifier(&s->poll_notifier);
695
d32ad10a
AK
696 stored_hostfwd = &stored->hostfwd;
697 stored_guestfwd = &stored->guestfwd;
698
68ac40d2 699 for (config = slirp_configs; config; config = config->next) {
d32ad10a
AK
700 String *element = g_new0(String, 1);
701
702 element->str = g_strdup(config->str);
68ac40d2 703 if (config->flags & SLIRP_CFG_HOSTFWD) {
d18572dd 704 if (slirp_hostfwd(s, config->str, errp) < 0) {
ce20b5be 705 goto error;
5c843af2 706 }
d32ad10a
AK
707 stored->has_hostfwd = true;
708 QAPI_LIST_APPEND(stored_hostfwd, element);
68ac40d2 709 } else {
d18572dd 710 if (slirp_guestfwd(s, config->str, errp) < 0) {
ce20b5be 711 goto error;
5c843af2 712 }
d32ad10a
AK
713 stored->has_guestfwd = true;
714 QAPI_LIST_APPEND(stored_guestfwd, element);
68ac40d2
MM
715 }
716 }
717#ifndef _WIN32
68ac40d2 718 if (smb_export) {
5c843af2 719 if (slirp_smb(s, smb_export, smbsrv, errp) < 0) {
ce20b5be 720 goto error;
5c843af2 721 }
68ac40d2
MM
722 }
723#endif
724
f6c2e66a
PB
725 s->exit_notifier.notify = slirp_smb_exit;
726 qemu_add_exit_notifier(&s->exit_notifier);
68ac40d2 727 return 0;
ce20b5be
MM
728
729error:
b20c6b9e 730 qemu_del_net_client(nc);
ce20b5be 731 return -1;
68ac40d2
MM
732}
733
b4983c57 734static SlirpState *slirp_lookup(Monitor *mon, const char *id)
68ac40d2 735{
b4983c57
TH
736 if (id) {
737 NetClientState *nc = qemu_find_netdev(id);
738 if (!nc) {
739 monitor_printf(mon, "unrecognized netdev id '%s'\n", id);
740 return NULL;
68ac40d2 741 }
ce20b5be 742 if (strcmp(nc->model, "user")) {
68ac40d2
MM
743 monitor_printf(mon, "invalid device specified\n");
744 return NULL;
745 }
ce20b5be 746 return DO_UPCAST(SlirpState, nc, nc);
68ac40d2
MM
747 } else {
748 if (QTAILQ_EMPTY(&slirp_stacks)) {
749 monitor_printf(mon, "user mode network stack not in use\n");
750 return NULL;
751 }
752 return QTAILQ_FIRST(&slirp_stacks);
753 }
754}
755
3e5a50d6 756void hmp_hostfwd_remove(Monitor *mon, const QDict *qdict)
68ac40d2
MM
757{
758 struct in_addr host_addr = { .s_addr = INADDR_ANY };
759 int host_port;
e30e5eb6 760 char buf[256];
68ac40d2
MM
761 const char *src_str, *p;
762 SlirpState *s;
763 int is_udp = 0;
764 int err;
765 const char *arg1 = qdict_get_str(qdict, "arg1");
766 const char *arg2 = qdict_get_try_str(qdict, "arg2");
68ac40d2 767
b4983c57
TH
768 if (arg2) {
769 s = slirp_lookup(mon, arg1);
93653066 770 src_str = arg2;
68ac40d2 771 } else {
b4983c57 772 s = slirp_lookup(mon, NULL);
68ac40d2
MM
773 src_str = arg1;
774 }
775 if (!s) {
776 return;
777 }
778
68ac40d2 779 p = src_str;
e30e5eb6
MA
780 if (!p || get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
781 goto fail_syntax;
782 }
68ac40d2
MM
783
784 if (!strcmp(buf, "tcp") || buf[0] == '\0') {
785 is_udp = 0;
786 } else if (!strcmp(buf, "udp")) {
787 is_udp = 1;
788 } else {
789 goto fail_syntax;
790 }
791
792 if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
793 goto fail_syntax;
794 }
795 if (buf[0] != '\0' && !inet_aton(buf, &host_addr)) {
796 goto fail_syntax;
797 }
798
1fb3f7f2
NA
799 if (qemu_strtoi(p, NULL, 10, &host_port)) {
800 goto fail_syntax;
801 }
68ac40d2 802
70381662 803 err = slirp_remove_hostfwd(s->slirp, is_udp, host_addr, host_port);
68ac40d2
MM
804
805 monitor_printf(mon, "host forwarding rule for %s %s\n", src_str,
b15ba6c9 806 err ? "not found" : "removed");
68ac40d2
MM
807 return;
808
809 fail_syntax:
810 monitor_printf(mon, "invalid format\n");
811}
812
d18572dd 813static int slirp_hostfwd(SlirpState *s, const char *redir_str, Error **errp)
68ac40d2
MM
814{
815 struct in_addr host_addr = { .s_addr = INADDR_ANY };
816 struct in_addr guest_addr = { .s_addr = 0 };
817 int host_port, guest_port;
818 const char *p;
819 char buf[256];
820 int is_udp;
821 char *end;
0e7e4fb0 822 const char *fail_reason = "Unknown reason";
68ac40d2
MM
823
824 p = redir_str;
825 if (!p || get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
0e7e4fb0 826 fail_reason = "No : separators";
68ac40d2
MM
827 goto fail_syntax;
828 }
829 if (!strcmp(buf, "tcp") || buf[0] == '\0') {
830 is_udp = 0;
831 } else if (!strcmp(buf, "udp")) {
832 is_udp = 1;
833 } else {
0e7e4fb0 834 fail_reason = "Bad protocol name";
68ac40d2
MM
835 goto fail_syntax;
836 }
837
d18572dd
TH
838 if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
839 fail_reason = "Missing : separator";
840 goto fail_syntax;
841 }
842 if (buf[0] != '\0' && !inet_aton(buf, &host_addr)) {
843 fail_reason = "Bad host address";
844 goto fail_syntax;
68ac40d2
MM
845 }
846
d18572dd 847 if (get_str_sep(buf, sizeof(buf), &p, '-') < 0) {
0e7e4fb0 848 fail_reason = "Bad host port separator";
68ac40d2
MM
849 goto fail_syntax;
850 }
851 host_port = strtol(buf, &end, 0);
0bed71ed 852 if (*end != '\0' || host_port < 0 || host_port > 65535) {
0e7e4fb0 853 fail_reason = "Bad host port";
68ac40d2
MM
854 goto fail_syntax;
855 }
856
857 if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
0e7e4fb0 858 fail_reason = "Missing guest address";
68ac40d2
MM
859 goto fail_syntax;
860 }
861 if (buf[0] != '\0' && !inet_aton(buf, &guest_addr)) {
0e7e4fb0 862 fail_reason = "Bad guest address";
68ac40d2
MM
863 goto fail_syntax;
864 }
865
866 guest_port = strtol(p, &end, 0);
867 if (*end != '\0' || guest_port < 1 || guest_port > 65535) {
0e7e4fb0 868 fail_reason = "Bad guest port";
68ac40d2
MM
869 goto fail_syntax;
870 }
871
872 if (slirp_add_hostfwd(s->slirp, is_udp, host_addr, host_port, guest_addr,
873 guest_port) < 0) {
5c843af2
HP
874 error_setg(errp, "Could not set up host forwarding rule '%s'",
875 redir_str);
68ac40d2
MM
876 return -1;
877 }
878 return 0;
879
880 fail_syntax:
0e7e4fb0
DDAG
881 error_setg(errp, "Invalid host forwarding rule '%s' (%s)", redir_str,
882 fail_reason);
68ac40d2
MM
883 return -1;
884}
885
3e5a50d6 886void hmp_hostfwd_add(Monitor *mon, const QDict *qdict)
68ac40d2
MM
887{
888 const char *redir_str;
889 SlirpState *s;
890 const char *arg1 = qdict_get_str(qdict, "arg1");
891 const char *arg2 = qdict_get_try_str(qdict, "arg2");
68ac40d2 892
b4983c57
TH
893 if (arg2) {
894 s = slirp_lookup(mon, arg1);
93653066 895 redir_str = arg2;
68ac40d2 896 } else {
b4983c57 897 s = slirp_lookup(mon, NULL);
68ac40d2
MM
898 redir_str = arg1;
899 }
900 if (s) {
5c843af2 901 Error *err = NULL;
d18572dd 902 if (slirp_hostfwd(s, redir_str, &err) < 0) {
5c843af2
HP
903 error_report_err(err);
904 }
68ac40d2
MM
905 }
906
907}
908
68ac40d2
MM
909#ifndef _WIN32
910
911/* automatic user mode samba server configuration */
912static void slirp_smb_cleanup(SlirpState *s)
913{
5a01e99f 914 int ret;
68ac40d2 915
f95cc8b6
DDAG
916 if (s->smb_dir) {
917 gchar *cmd = g_strdup_printf("rm -rf %s", s->smb_dir);
5a01e99f 918 ret = system(cmd);
24ac07de 919 if (ret == -1 || !WIFEXITED(ret)) {
1ecda02b 920 error_report("'%s' failed.", cmd);
5a01e99f 921 } else if (WEXITSTATUS(ret)) {
1ecda02b
MA
922 error_report("'%s' failed. Error code: %d",
923 cmd, WEXITSTATUS(ret));
5a01e99f 924 }
f95cc8b6
DDAG
925 g_free(cmd);
926 g_free(s->smb_dir);
927 s->smb_dir = NULL;
68ac40d2
MM
928 }
929}
930
931static int slirp_smb(SlirpState* s, const char *exported_dir,
5c843af2 932 struct in_addr vserver_addr, Error **errp)
68ac40d2 933{
f95cc8b6
DDAG
934 char *smb_conf;
935 char *smb_cmdline;
1cb1c5d1 936 struct passwd *passwd;
68ac40d2
MM
937 FILE *f;
938
1cb1c5d1
JK
939 passwd = getpwuid(geteuid());
940 if (!passwd) {
5c843af2 941 error_setg(errp, "Failed to retrieve user name");
1cb1c5d1
JK
942 return -1;
943 }
944
927d811b 945 if (access(CONFIG_SMBD_COMMAND, F_OK)) {
5c843af2
HP
946 error_setg(errp, "Could not find '%s', please install it",
947 CONFIG_SMBD_COMMAND);
927d811b
DH
948 return -1;
949 }
950
951 if (access(exported_dir, R_OK | X_OK)) {
5c843af2
HP
952 error_setg(errp, "Error accessing shared directory '%s': %s",
953 exported_dir, strerror(errno));
927d811b
DH
954 return -1;
955 }
956
f95cc8b6
DDAG
957 s->smb_dir = g_dir_make_tmp("qemu-smb.XXXXXX", NULL);
958 if (!s->smb_dir) {
5c843af2 959 error_setg(errp, "Could not create samba server dir");
68ac40d2
MM
960 return -1;
961 }
f95cc8b6 962 smb_conf = g_strdup_printf("%s/%s", s->smb_dir, "smb.conf");
68ac40d2
MM
963
964 f = fopen(smb_conf, "w");
965 if (!f) {
966 slirp_smb_cleanup(s);
5c843af2
HP
967 error_setg(errp,
968 "Could not create samba server configuration file '%s'",
969 smb_conf);
f95cc8b6 970 g_free(smb_conf);
68ac40d2
MM
971 return -1;
972 }
973 fprintf(f,
974 "[global]\n"
975 "private dir=%s\n"
7912d04b
PW
976 "interfaces=127.0.0.1\n"
977 "bind interfaces only=yes\n"
68ac40d2
MM
978 "pid directory=%s\n"
979 "lock directory=%s\n"
276eda57 980 "state directory=%s\n"
7912d04b 981 "cache directory=%s\n"
b87b8a8b 982 "ncalrpc dir=%s/ncalrpc\n"
68ac40d2
MM
983 "log file=%s/log.smbd\n"
984 "smb passwd file=%s/smbpasswd\n"
c2804ee6
MB
985 "security = user\n"
986 "map to guest = Bad User\n"
7912d04b
PW
987 "load printers = no\n"
988 "printing = bsd\n"
989 "disable spoolss = yes\n"
990 "usershare max shares = 0\n"
68ac40d2
MM
991 "[qemu]\n"
992 "path=%s\n"
993 "read only=no\n"
1cb1c5d1
JK
994 "guest ok=yes\n"
995 "force user=%s\n",
68ac40d2
MM
996 s->smb_dir,
997 s->smb_dir,
998 s->smb_dir,
999 s->smb_dir,
1000 s->smb_dir,
276eda57 1001 s->smb_dir,
b87b8a8b 1002 s->smb_dir,
7912d04b 1003 s->smb_dir,
1cb1c5d1
JK
1004 exported_dir,
1005 passwd->pw_name
68ac40d2
MM
1006 );
1007 fclose(f);
1008
f95cc8b6 1009 smb_cmdline = g_strdup_printf("%s -l %s -s %s",
44d8d2b2 1010 CONFIG_SMBD_COMMAND, s->smb_dir, smb_conf);
f95cc8b6 1011 g_free(smb_conf);
68ac40d2 1012
44b4ff24
MAL
1013 if (slirp_add_exec(s->slirp, smb_cmdline, &vserver_addr, 139) < 0 ||
1014 slirp_add_exec(s->slirp, smb_cmdline, &vserver_addr, 445) < 0) {
68ac40d2 1015 slirp_smb_cleanup(s);
f95cc8b6 1016 g_free(smb_cmdline);
5c843af2 1017 error_setg(errp, "Conflicting/invalid smbserver address");
68ac40d2
MM
1018 return -1;
1019 }
f95cc8b6 1020 g_free(smb_cmdline);
68ac40d2
MM
1021 return 0;
1022}
1023
68ac40d2
MM
1024#endif /* !defined(_WIN32) */
1025
68ac40d2
MM
1026static int guestfwd_can_read(void *opaque)
1027{
1028 struct GuestFwd *fwd = opaque;
1029 return slirp_socket_can_recv(fwd->slirp, fwd->server, fwd->port);
1030}
1031
1032static void guestfwd_read(void *opaque, const uint8_t *buf, int size)
1033{
1034 struct GuestFwd *fwd = opaque;
1035 slirp_socket_recv(fwd->slirp, fwd->server, fwd->port, buf, size);
1036}
1037
625a526b 1038static ssize_t guestfwd_write(const void *buf, size_t len, void *chr)
44b4ff24
MAL
1039{
1040 return qemu_chr_fe_write_all(chr, buf, len);
1041}
1042
d18572dd 1043static int slirp_guestfwd(SlirpState *s, const char *config_str, Error **errp)
68ac40d2 1044{
ffe02f55 1045 /* TODO: IPv6 */
68ac40d2
MM
1046 struct in_addr server = { .s_addr = 0 };
1047 struct GuestFwd *fwd;
1048 const char *p;
1049 char buf[128];
1050 char *end;
1051 int port;
1052
1053 p = config_str;
d18572dd
TH
1054 if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
1055 goto fail_syntax;
1056 }
1057 if (strcmp(buf, "tcp") && buf[0] != '\0') {
1058 goto fail_syntax;
1059 }
1060 if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
1061 goto fail_syntax;
1062 }
1063 if (buf[0] != '\0' && !inet_aton(buf, &server)) {
1064 goto fail_syntax;
1065 }
1066 if (get_str_sep(buf, sizeof(buf), &p, '-') < 0) {
1067 goto fail_syntax;
68ac40d2
MM
1068 }
1069 port = strtol(buf, &end, 10);
1070 if (*end != '\0' || port < 1 || port > 65535) {
1071 goto fail_syntax;
1072 }
1073
a9899996 1074 snprintf(buf, sizeof(buf), "guestfwd.tcp.%d", port);
68ac40d2 1075
3624730a 1076 if (g_str_has_prefix(p, "cmd:")) {
44b4ff24 1077 if (slirp_add_exec(s->slirp, &p[4], &server, port) < 0) {
5c843af2
HP
1078 error_setg(errp, "Conflicting/invalid host:port in guest "
1079 "forwarding rule '%s'", config_str);
b412eb61
AG
1080 return -1;
1081 }
1082 } else {
32a6ebec 1083 Error *err = NULL;
95e30b2a
MAL
1084 /*
1085 * FIXME: sure we want to support implicit
1086 * muxed monitors here?
1087 */
4ad6f6cb 1088 Chardev *chr = qemu_chr_new_mux_mon(buf, p, NULL);
32a6ebec
MAL
1089
1090 if (!chr) {
5c843af2
HP
1091 error_setg(errp, "Could not open guest forwarding device '%s'",
1092 buf);
32a6ebec
MAL
1093 return -1;
1094 }
1095
1096 fwd = g_new(struct GuestFwd, 1);
1097 qemu_chr_fe_init(&fwd->hd, chr, &err);
1098 if (err) {
5c843af2 1099 error_propagate(errp, err);
8e207c32 1100 object_unparent(OBJECT(chr));
b412eb61
AG
1101 g_free(fwd);
1102 return -1;
1103 }
68ac40d2 1104
44b4ff24
MAL
1105 if (slirp_add_guestfwd(s->slirp, guestfwd_write, &fwd->hd,
1106 &server, port) < 0) {
5c843af2
HP
1107 error_setg(errp, "Conflicting/invalid host:port in guest "
1108 "forwarding rule '%s'", config_str);
8e207c32 1109 qemu_chr_fe_deinit(&fwd->hd, true);
b412eb61
AG
1110 g_free(fwd);
1111 return -1;
1112 }
1113 fwd->server = server;
1114 fwd->port = port;
1115 fwd->slirp = s->slirp;
1116
5345fdb4 1117 qemu_chr_fe_set_handlers(&fwd->hd, guestfwd_can_read, guestfwd_read,
81517ba3 1118 NULL, NULL, fwd, NULL, true);
8d45a3b9 1119 s->fwd = g_slist_append(s->fwd, fwd);
b412eb61 1120 }
68ac40d2
MM
1121 return 0;
1122
1123 fail_syntax:
5c843af2 1124 error_setg(errp, "Invalid guest forwarding rule '%s'", config_str);
68ac40d2
MM
1125 return -1;
1126}
1127
1ce6be24 1128void hmp_info_usernet(Monitor *mon, const QDict *qdict)
68ac40d2
MM
1129{
1130 SlirpState *s;
1131
1132 QTAILQ_FOREACH(s, &slirp_stacks, entry) {
90d87a33 1133 int id;
442da403 1134 bool got_hub_id = net_hub_id_for_client(&s->nc, &id) == 0;
b7f43bf2
MAL
1135 char *info = slirp_connection_info(s->slirp);
1136 monitor_printf(mon, "Hub %d (%s):\n%s",
442da403 1137 got_hub_id ? id : -1,
b7f43bf2
MAL
1138 s->nc.name, info);
1139 g_free(info);
68ac40d2
MM
1140 }
1141}
1142
094f15c5
LE
1143static void
1144net_init_slirp_configs(const StringList *fwd, int flags)
68ac40d2 1145{
094f15c5
LE
1146 while (fwd) {
1147 struct slirp_config_str *config;
68ac40d2 1148
094f15c5
LE
1149 config = g_malloc0(sizeof(*config));
1150 pstrcpy(config->str, sizeof(config->str), fwd->value->str);
1151 config->flags = flags;
1152 config->next = slirp_configs;
1153 slirp_configs = config;
68ac40d2 1154
094f15c5 1155 fwd = fwd->next;
68ac40d2 1156 }
68ac40d2
MM
1157}
1158
63d2960b
KS
1159static const char **slirp_dnssearch(const StringList *dnsname)
1160{
1161 const StringList *c = dnsname;
1162 size_t i = 0, num_opts = 0;
1163 const char **ret;
1164
1165 while (c) {
1166 num_opts++;
1167 c = c->next;
1168 }
1169
1170 if (num_opts == 0) {
1171 return NULL;
1172 }
1173
1174 ret = g_malloc((num_opts + 1) * sizeof(*ret));
1175 c = dnsname;
1176 while (c) {
1177 ret[i++] = c->value->str;
1178 c = c->next;
1179 }
1180 ret[i] = NULL;
1181 return ret;
1182}
1183
cebea510 1184int net_init_slirp(const Netdev *netdev, const char *name,
a30ecde6 1185 NetClientState *peer, Error **errp)
68ac40d2
MM
1186{
1187 struct slirp_config_str *config;
094f15c5 1188 char *vnet;
68ac40d2 1189 int ret;
094f15c5 1190 const NetdevUserOptions *user;
63d2960b 1191 const char **dnssearch;
0b11c036 1192 bool ipv4 = true, ipv6 = true;
68ac40d2 1193
f394b2e2
EB
1194 assert(netdev->type == NET_CLIENT_DRIVER_USER);
1195 user = &netdev->u.user;
68ac40d2 1196
0b11c036
ST
1197 if ((user->has_ipv6 && user->ipv6 && !user->has_ipv4) ||
1198 (user->has_ipv4 && !user->ipv4)) {
1199 ipv4 = 0;
1200 }
1201 if ((user->has_ipv4 && user->ipv4 && !user->has_ipv6) ||
1202 (user->has_ipv6 && !user->ipv6)) {
1203 ipv6 = 0;
1204 }
1205
094f15c5
LE
1206 vnet = user->has_net ? g_strdup(user->net) :
1207 user->has_ip ? g_strdup_printf("%s/24", user->ip) :
1208 NULL;
68ac40d2 1209
63d2960b
KS
1210 dnssearch = slirp_dnssearch(user->dnssearch);
1211
094f15c5 1212 /* all optional fields are initialized to "all bits zero" */
68ac40d2 1213
094f15c5
LE
1214 net_init_slirp_configs(user->hostfwd, SLIRP_CFG_HOSTFWD);
1215 net_init_slirp_configs(user->guestfwd, 0);
68ac40d2 1216
0b11c036
ST
1217 ret = net_slirp_init(peer, "user", name, user->q_restrict,
1218 ipv4, vnet, user->host,
1219 ipv6, user->ipv6_prefix, user->ipv6_prefixlen,
d8eb3864 1220 user->ipv6_host, user->hostname, user->tftp,
7aac531e 1221 user->bootfile, user->dhcpstart,
d8eb3864 1222 user->dns, user->ipv6_dns, user->smb,
0fca92b9
FZ
1223 user->smbserver, dnssearch, user->domainname,
1224 user->tftp_server_name, errp);
68ac40d2
MM
1225
1226 while (slirp_configs) {
1227 config = slirp_configs;
1228 slirp_configs = config->next;
7267c094 1229 g_free(config);
68ac40d2
MM
1230 }
1231
7267c094 1232 g_free(vnet);
63d2960b 1233 g_free(dnssearch);
68ac40d2
MM
1234
1235 return ret;
1236}