Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 787a385

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 787a385

File tree

9 files changed

+341
-10
lines changed

9 files changed

+341
-10
lines changed

‎.gitmodules

Lines changed: 4 additions & 0 deletions
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

Lines changed: 13 additions & 0 deletions
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

Lines changed: 4 additions & 0 deletions
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

Lines changed: 32 additions & 3 deletions
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

Lines changed: 13 additions & 2 deletions
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 *netdevUNUSED)
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

Lines changed: 31 additions & 2 deletions
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

Lines changed: 205 additions & 0 deletions
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+
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /