Skip to content

Commit 568bf26

Browse files
committed
Enable user-mode networking through SLIRP
This commit integrates 'minislirp' as submodule and enables support for the new 'user' network device, which uses SLIRP for user-mode networking. An internal pipe mechanism is introduced to monitor incoming data. The main loop polls the pipe for availabe data, and any incoming data is forwarded to the virtio-net device for processing. Additionally, this commit introduces special address used by SLIRP for network configuration [1]: - **10.0.2.0**: The SLIRP "on-line" configuration address. - **10.0.2.2**: An alias for the host running SLIRP. - **10.0.2.3**: An alias for the DNS address. - **10.0.2.15**: A recommended address for the PC running SLIRP. [1]: https://github.com/kost/slirp/blob/master/docs/slirp.doc
1 parent 161f30c commit 568bf26

File tree

10 files changed

+342
-11
lines changed

10 files changed

+342
-11
lines changed

.gitmodules

+4
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,7 @@
66
path = mini-gdbstub
77
url = https://github.com/RinHizakura/mini-gdbstub
88
shallow = true
9+
[submodule "minislirp"]
10+
path = minislirp
11+
url = https://github.com/edubart/minislirp
12+
shallow = true

Makefile

+13
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ $(call set-feature, VIRTIONET)
5151
ifeq ($(call has, VIRTIONET), 1)
5252
OBJS_EXTRA += virtio-net.o
5353
OBJS_EXTRA += netdev.o
54+
OBJS_EXTRA += slirp.o
5455
endif
5556

5657
# virtio-snd
@@ -113,6 +114,17 @@ $(GDBSTUB_LIB): mini-gdbstub/Makefile
113114
$(MAKE) -C $(dir $<)
114115
$(OBJS): $(GDBSTUB_LIB)
115116

117+
ifeq ($(call has, VIRTIONET), 1)
118+
MINISLIRP_DIR := minislirp
119+
MINISLIRP_LIB := minislirp/src/libslirp.a
120+
LDFLAGS += $(MINISLIRP_LIB)
121+
$(MINISLIRP_DIR)/src/Makefile:
122+
git submodule update --init $(MINISLIRP_DIR)
123+
$(MINISLIRP_LIB): $(MINISLIRP_DIR)/src/Makefile
124+
$(MAKE) -C $(dir $<)
125+
$(OBJS): $(MINISLIRP_LIB)
126+
endif
127+
116128
$(BIN): $(OBJS)
117129
$(VECHO) " LD\t$@\n"
118130
$(Q)$(CC) -o $@ $^ $(LDFLAGS)
@@ -171,6 +183,7 @@ build-image:
171183
clean:
172184
$(Q)$(RM) $(BIN) $(OBJS) $(deps)
173185
$(Q)$(MAKE) -C mini-gdbstub clean
186+
$(Q)$(MAKE) -C minislirp/src clean
174187

175188
distclean: clean
176189
$(Q)$(RM) riscv-harts.dtsi

device.h

+4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
#pragma once
22

3+
#if SEMU_HAS(VIRTIONET)
34
#include "netdev.h"
5+
#endif
46
#include "riscv.h"
57
#include "virtio.h"
68

@@ -120,6 +122,8 @@ void virtio_net_write(hart_t *core,
120122
uint32_t value);
121123
void virtio_net_refresh_queue(virtio_net_state_t *vnet);
122124

125+
void virtio_net_recv_from_peer(void *peer);
126+
123127
bool virtio_net_init(virtio_net_state_t *vnet, const char *name);
124128
#endif /* SEMU_HAS(VIRTIONET) */
125129

main.c

+32-3
Original file line numberDiff line numberDiff line change
@@ -761,9 +761,38 @@ static int semu_run(emu_state_t *emu)
761761

762762
/* Emulate */
763763
while (!emu->stopped) {
764-
ret = semu_step(emu);
765-
if (ret)
766-
return ret;
764+
#if SEMU_HAS(VIRTIONET)
765+
int i = 0;
766+
if (emu->vnet.peer.type == NETDEV_IMPL_user && boot_complete) {
767+
net_user_options_t *usr = (net_user_options_t *) emu->vnet.peer.op;
768+
769+
uint32_t timeout = -1;
770+
usr->pfd_len = 1;
771+
slirp_pollfds_fill_socket(usr->slirp, &timeout,
772+
semu_slirp_add_poll_socket, usr);
773+
774+
/* Poll the internal pipe for incoming data. If data is
775+
* available (POLL_IN), process it and forward it to the
776+
* virtio-net device.
777+
*/
778+
int pollout = poll(usr->pfd, usr->pfd_len, 1);
779+
if (usr->pfd[0].revents & POLLIN) {
780+
virtio_net_recv_from_peer(usr->peer);
781+
}
782+
slirp_pollfds_poll(usr->slirp, (pollout <= 0),
783+
semu_slirp_get_revents, usr);
784+
for (i = 0; i < SLIRP_POLL_INTERVAL; i++) {
785+
ret = semu_step(emu);
786+
if (ret)
787+
return ret;
788+
}
789+
} else
790+
#endif
791+
{
792+
ret = semu_step(emu);
793+
if (ret)
794+
return ret;
795+
}
767796
}
768797

769798
/* unreachable */

minislirp

Submodule minislirp added at 0bf4a41

netdev.c

+13-2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <string.h>
99
#include <sys/ioctl.h>
1010

11+
#include "device.h"
1112
#include "netdev.h"
1213

1314
static int net_init_tap();
@@ -55,9 +56,19 @@ static int net_init_tap(netdev_t *netdev)
5556
return 0;
5657
}
5758

58-
static int net_init_user(netdev_t *netdev UNUSED)
59+
static int net_init_user(netdev_t *netdev)
5960
{
60-
/* TODO: create slirp dev */
61+
net_user_options_t *usr = (net_user_options_t *) netdev->op;
62+
memset(usr, 0, sizeof(*usr));
63+
usr->peer = container_of(netdev, virtio_net_state_t, peer);
64+
if (pipe(usr->channel) < 0)
65+
return false;
66+
assert(fcntl(usr->channel[SLIRP_READ_SIDE], F_SETFL,
67+
fcntl(usr->channel[SLIRP_READ_SIDE], F_GETFL, 0) |
68+
O_NONBLOCK) >= 0);
69+
70+
net_slirp_init(usr);
71+
6172
return 0;
6273
}
6374

netdev.h

+31-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
#pragma once
22

3-
#include <stdbool.h>
3+
#include <poll.h>
4+
#include <unistd.h>
5+
6+
#include "minislirp/src/libslirp.h"
7+
#include "utils.h"
8+
49

510
/* clang-format off */
611
#define SUPPORTED_DEVICES \
@@ -18,10 +23,34 @@ typedef struct {
1823
int tap_fd;
1924
} net_tap_options_t;
2025

26+
/* SLIRP */
27+
#define SLIRP_POLL_INTERVAL 100000
28+
#define SLIRP_READ_SIDE 0
29+
#define SLIRP_WRITE_SIDE 1
2130
typedef struct {
22-
/* TODO: Implement user option */
31+
semu_timer_t timer;
32+
Slirp *slirp;
33+
SlirpTimerId id;
34+
void *cb_opaque;
35+
void (*cb)(void *opaque);
36+
int64_t expire_timer_msec;
37+
} slirp_timer;
38+
39+
typedef struct {
40+
Slirp *slirp;
41+
int channel[2];
42+
int pfd_len;
43+
int pfd_size;
44+
struct pollfd *pfd;
45+
slirp_timer *timer;
46+
void *peer;
2347
} net_user_options_t;
2448

49+
Slirp *slirp_create(net_user_options_t *usr, SlirpConfig *cfg);
50+
int net_slirp_init(net_user_options_t *usr);
51+
int semu_slirp_add_poll_socket(slirp_os_socket fd, int events, void *opaque);
52+
int semu_slirp_get_revents(int idx, void *opaque);
53+
2554
typedef struct {
2655
char *name;
2756
netdev_impl_t type;

slirp.c

+205
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
#include <stdio.h>
2+
#include <stdlib.h>
3+
4+
#include "netdev.h"
5+
6+
/* Slirp callback: invoked when Slirp wants to send a packet to the backend */
7+
static ssize_t net_slirp_send_packet(const void *buf, size_t len, void *opaque)
8+
{
9+
net_user_options_t *usr = (net_user_options_t *) opaque;
10+
11+
return write(usr->channel[SLIRP_WRITE_SIDE], buf, len);
12+
}
13+
14+
/* Slirp callback: reports an error from the guest (current unused) */
15+
static void net_slirp_guest_error(const char *msg UNUSED, void *opaque UNUSED)
16+
{
17+
// Unused
18+
}
19+
20+
/* Slirp callback: returns current time in nanoseconds for Slirp timers */
21+
static int64_t net_slirp_clock_get_ns(void *opaque UNUSED)
22+
{
23+
net_user_options_t *usr = (net_user_options_t *) opaque;
24+
25+
return semu_timer_get(&usr->timer->timer);
26+
}
27+
28+
/* Slirp callback: called when Slirp has finished initialization */
29+
static void net_slirp_init_completed(Slirp *slirp, void *opaque)
30+
{
31+
net_user_options_t *s = opaque;
32+
s->slirp = slirp;
33+
}
34+
35+
static void slirp_timer_init(slirp_timer *t, void (*cb)(void *opaque))
36+
{
37+
t->cb = cb;
38+
semu_timer_init(&t->timer, CLOCK_FREQ, 1);
39+
}
40+
41+
static void net_slirp_timer_cb(void *opaque)
42+
{
43+
slirp_timer *t = opaque;
44+
slirp_handle_timer(t->slirp, t->id, t->cb_opaque);
45+
}
46+
47+
/* Slirp callback: allocated and initializes a new timer object */
48+
static void *net_slirp_timer_new_opaque(SlirpTimerId id,
49+
void *cb_opaque,
50+
void *opaque)
51+
{
52+
net_user_options_t *usr = (net_user_options_t *) opaque;
53+
slirp_timer *t = malloc(sizeof(slirp_timer));
54+
usr->timer = t;
55+
t->slirp = usr->slirp;
56+
t->id = id;
57+
t->cb_opaque = cb_opaque;
58+
t->expire_timer_msec = -1;
59+
slirp_timer_init(t, net_slirp_timer_cb);
60+
61+
return t;
62+
}
63+
64+
/* Slirp callback: releases resources associated with a timer */
65+
static void net_slirp_timer_free(void *timer, void *opaque UNUSED)
66+
{
67+
if (timer)
68+
free(timer);
69+
}
70+
71+
/* Slirp callback: modifies the expiration time of an existing timer */
72+
static void net_slirp_timer_mod(void *timer,
73+
int64_t expire_time,
74+
void *opaque UNUSED)
75+
{
76+
slirp_timer *t = (slirp_timer *) timer;
77+
semu_timer_rebase(&t->timer, expire_time);
78+
}
79+
80+
/* Slirp callback: registers a pollable socket (unused in this backend) */
81+
static void net_slirp_register_poll_sock(int fd UNUSED, void *opaque UNUSED)
82+
{
83+
// Unused
84+
}
85+
86+
/* Slirp callback: unregisters a pollable socket (unused in this backend) */
87+
static void net_slirp_unregister_poll_sock(int fd UNUSED, void *opaque UNUSED)
88+
{
89+
// Unused
90+
}
91+
92+
/* Slirp callback: notifies backend of pending activity (unused) */
93+
static void net_slirp_notify(void *opaque UNUSED)
94+
{
95+
// Unused
96+
}
97+
98+
static const SlirpCb slirp_cb = {
99+
.send_packet = net_slirp_send_packet,
100+
.guest_error = net_slirp_guest_error,
101+
.clock_get_ns = net_slirp_clock_get_ns,
102+
.init_completed = net_slirp_init_completed,
103+
.timer_new_opaque = net_slirp_timer_new_opaque,
104+
.timer_free = net_slirp_timer_free,
105+
.timer_mod = net_slirp_timer_mod,
106+
.register_poll_socket = net_slirp_register_poll_sock,
107+
.unregister_poll_socket = net_slirp_unregister_poll_sock,
108+
.notify = net_slirp_notify,
109+
};
110+
111+
static int poll_to_slirp_poll(int events)
112+
{
113+
int ret = 0;
114+
if (events & POLLIN)
115+
ret |= SLIRP_POLL_IN;
116+
if (events & POLLOUT)
117+
ret |= SLIRP_POLL_OUT;
118+
if (events & POLLPRI)
119+
ret |= SLIRP_POLL_PRI;
120+
if (events & POLLERR)
121+
ret |= SLIRP_POLL_ERR;
122+
if (events & POLLHUP)
123+
ret |= SLIRP_POLL_HUP;
124+
return ret;
125+
}
126+
127+
int semu_slirp_get_revents(int idx, void *opaque)
128+
{
129+
net_user_options_t *usr = opaque;
130+
return poll_to_slirp_poll(usr->pfd[idx].revents);
131+
}
132+
133+
int semu_slirp_add_poll_socket(slirp_os_socket fd, int events, void *opaque)
134+
{
135+
net_user_options_t *usr = opaque;
136+
if (usr->pfd_len >= usr->pfd_size) {
137+
int newsize = usr->pfd_size + 16;
138+
struct pollfd *new = realloc(usr->pfd, newsize * sizeof(struct pollfd));
139+
if (new) {
140+
usr->pfd = new;
141+
usr->pfd_size = newsize;
142+
}
143+
}
144+
if (usr->pfd_len < usr->pfd_size) {
145+
int idx = usr->pfd_len++;
146+
usr->pfd[idx].fd = fd;
147+
148+
usr->pfd[idx].events = poll_to_slirp_poll(events);
149+
return idx;
150+
} else {
151+
return -1;
152+
}
153+
}
154+
155+
Slirp *slirp_create(net_user_options_t *usr, SlirpConfig *cfg)
156+
{
157+
/* Create a Slirp instance with special address. All
158+
* addresses of the form 10.0.2.xxx are special to
159+
* Slirp.
160+
*/
161+
cfg->version = SLIRP_CHECK_VERSION(4, 8, 0) ? 6
162+
: SLIRP_CHECK_VERSION(4, 7, 0) ? 4
163+
: 1;
164+
cfg->restricted = 0;
165+
cfg->in_enabled = 1;
166+
inet_pton(AF_INET, "10.0.2.0", &(cfg->vnetwork));
167+
inet_pton(AF_INET, "255.255.255.0", &(cfg->vnetmask));
168+
inet_pton(AF_INET, "10.0.2.2", &(cfg->vhost));
169+
cfg->in6_enabled = 1;
170+
inet_pton(AF_INET6, "fd00::", &cfg->vprefix_addr6);
171+
cfg->vhostname = "slirp";
172+
cfg->tftp_server_name = NULL;
173+
cfg->tftp_path = NULL;
174+
cfg->bootfile = NULL;
175+
inet_pton(AF_INET, "10.0.2.15", &(cfg->vdhcp_start));
176+
inet_pton(AF_INET, "10.0.2.3", &(cfg->vnameserver));
177+
inet_pton(AF_INET6, "fd00::3", &cfg->vnameserver6);
178+
cfg->vdnssearch = NULL;
179+
cfg->vdomainname = NULL;
180+
cfg->if_mtu = 1500;
181+
cfg->if_mru = 1500;
182+
cfg->outbound_addr = NULL;
183+
cfg->disable_host_loopback = 0;
184+
185+
return slirp_new(cfg, &slirp_cb, usr);
186+
}
187+
188+
int net_slirp_init(net_user_options_t *usr)
189+
{
190+
SlirpConfig cfg;
191+
usr->slirp = slirp_create(usr, &cfg);
192+
if (usr->slirp == NULL) {
193+
fprintf(stderr, "create slirp failed\n");
194+
}
195+
196+
usr->pfd = malloc(sizeof(struct pollfd));
197+
198+
/* Register the read end of the internal pipe (channel[SLIRP_READ_SIDE])
199+
* with slirp's poll system. This allows slirp to monitor it for incoming
200+
* data (POLL_IN) or hang-up event (POLL_HUP).
201+
*/
202+
semu_slirp_add_poll_socket(usr->channel[SLIRP_READ_SIDE],
203+
SLIRP_POLL_IN | SLIRP_POLL_HUP, usr);
204+
return 0;
205+
}

utils.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ static inline void list_del_init(struct list_head *node)
8787
})
8888
#else
8989
#define container_of(ptr, type, member) \
90-
((type *) ((char *) (ptr) - offsetof(type, member)))
90+
((type *) ((char *) (ptr) -offsetof(type, member)))
9191
#endif
9292

9393
#define list_entry(node, type, member) container_of(node, type, member)

0 commit comments

Comments
 (0)