Skip to content

Commit a240839

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 a240839

File tree

9 files changed

+334
-10
lines changed

9 files changed

+334
-10
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.git
12+
shallow = true

Makefile

+11
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ OBJS := \
101101
uart.o \
102102
main.o \
103103
aclint.o \
104+
slirp.o \
104105
$(OBJS_EXTRA)
105106

106107
deps := $(OBJS:%.o=.%.o.d)
@@ -113,6 +114,15 @@ $(GDBSTUB_LIB): mini-gdbstub/Makefile
113114
$(MAKE) -C $(dir $<)
114115
$(OBJS): $(GDBSTUB_LIB)
115116

117+
MINISLIRP_DIR := minislirp
118+
MINISLIRP_LIB := minislirp/src/libslirp.a
119+
LDFLAGS += $(MINISLIRP_LIB)
120+
$(MINISLIRP_DIR)/src/Makefile:
121+
git submodule update --init $(MINISLIRP_DIR)
122+
$(MINISLIRP_LIB): $(MINISLIRP_DIR)/src/Makefile
123+
$(MAKE) -C $(dir $<)
124+
$(OBJS): $(MINISLIRP_LIB)
125+
116126
$(BIN): $(OBJS)
117127
$(VECHO) " LD\t$@\n"
118128
$(Q)$(CC) -o $@ $^ $(LDFLAGS)
@@ -171,6 +181,7 @@ build-image:
171181
clean:
172182
$(Q)$(RM) $(BIN) $(OBJS) $(deps)
173183
$(Q)$(MAKE) -C mini-gdbstub clean
184+
$(Q)$(MAKE) -C minislirp/src clean
174185

175186
distclean: clean
176187
$(Q)$(RM) riscv-harts.dtsi

device.h

+2
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,8 @@ void virtio_net_write(hart_t *core,
120120
uint32_t value);
121121
void virtio_net_refresh_queue(virtio_net_state_t *vnet);
122122

123+
void virtio_net_recv_from_peer(void *peer);
124+
123125
bool virtio_net_init(virtio_net_state_t *vnet, const char *name);
124126
#endif /* SEMU_HAS(VIRTIONET) */
125127

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

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

0 commit comments

Comments
 (0)