Skip to content

Commit 15f6f8a

Browse files
committed
Refactor network device handling for flexible client support
Separate network client handling to support various network backends, such as Slirp, instead of hard-coding the TAP device. The '-netdev' flag can now be used to specify the network client type, defaulting to TAP if unspecified. Additionally, packet transfer functions are now tailored to each client device, enabling more modular and extensible network client development.
1 parent a0ad61b commit 15f6f8a

File tree

6 files changed

+213
-46
lines changed

6 files changed

+213
-46
lines changed

Makefile

+3-1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ ifeq ($(call has, VIRTIOBLK), 1)
3131
endif
3232
endif
3333

34+
NETDEV ?= tap
3435
# virtio-net
3536
ENABLE_VIRTIONET ?= 1
3637
ifneq ($(UNAME_S),Linux)
@@ -39,6 +40,7 @@ endif
3940
$(call set-feature, VIRTIONET)
4041
ifeq ($(call has, VIRTIONET), 1)
4142
OBJS_EXTRA += virtio-net.o
43+
OBJS_EXTRA += netdev.o
4244
endif
4345

4446
BIN = semu
@@ -96,7 +98,7 @@ ext4.img:
9698

9799
check: $(BIN) minimal.dtb $(KERNEL_DATA) $(INITRD_DATA) $(DISKIMG_FILE)
98100
@$(call notice, Ready to launch Linux kernel. Please be patient.)
99-
$(Q)./$(BIN) -k $(KERNEL_DATA) -c $(SMP) -b minimal.dtb -i $(INITRD_DATA) $(OPTS)
101+
$(Q)./$(BIN) -k $(KERNEL_DATA) -c $(SMP) -b minimal.dtb -i $(INITRD_DATA) -n $(NETDEV) $(OPTS)
100102

101103
build-image:
102104
scripts/build-image.sh

device.h

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

3+
#include "netdev.h"
34
#include "riscv.h"
45
#include "virtio.h"
56

@@ -101,7 +102,7 @@ typedef struct {
101102
uint32_t Status;
102103
uint32_t InterruptStatus;
103104
/* supplied by environment */
104-
int tap_fd;
105+
netdev_t peer;
105106
uint32_t *ram;
106107
/* implementation-specific */
107108
void *priv;
@@ -119,7 +120,7 @@ void virtio_net_write(hart_t *core,
119120
uint32_t value);
120121
void virtio_net_refresh_queue(virtio_net_state_t *vnet);
121122

122-
bool virtio_net_init(virtio_net_state_t *vnet);
123+
bool virtio_net_init(virtio_net_state_t *vnet, const char *name);
123124
#endif /* SEMU_HAS(VIRTIONET) */
124125

125126
/* VirtIO-Block */

main.c

+15-5
Original file line numberDiff line numberDiff line change
@@ -440,19 +440,22 @@ static void handle_options(int argc,
440440
char **dtb_file,
441441
char **initrd_file,
442442
char **disk_file,
443+
char **net_dev,
443444
int *hart_count)
444445
{
445-
*kernel_file = *dtb_file = *initrd_file = *disk_file = NULL;
446+
*kernel_file = *dtb_file = *initrd_file = *disk_file = *net_dev = NULL;
446447

447448
int optidx = 0;
448449
struct option opts[] = {
449450
{"kernel", 1, NULL, 'k'}, {"dtb", 1, NULL, 'b'},
450451
{"initrd", 1, NULL, 'i'}, {"disk", 1, NULL, 'd'},
451-
{"smp", 1, NULL, 'c'}, {"help", 0, NULL, 'h'},
452+
{"netdev", 1, NULL, 'n'}, {"smp", 1, NULL, 'c'},
453+
{"help", 0, NULL, 'h'},
452454
};
453455

454456
int c;
455-
while ((c = getopt_long(argc, argv, "k:b:i:d:c:h", opts, &optidx)) != -1) {
457+
while ((c = getopt_long(argc, argv, "k:b:i:d:n:c:h", opts, &optidx)) !=
458+
-1) {
456459
switch (c) {
457460
case 'k':
458461
*kernel_file = optarg;
@@ -466,6 +469,9 @@ static void handle_options(int argc,
466469
case 'd':
467470
*disk_file = optarg;
468471
break;
472+
case 'n':
473+
*net_dev = optarg;
474+
break;
469475
case 'c':
470476
*hart_count = atoi(optarg);
471477
break;
@@ -487,6 +493,9 @@ static void handle_options(int argc,
487493

488494
if (!*dtb_file)
489495
*dtb_file = "minimal.dtb";
496+
497+
if (!*net_dev)
498+
*net_dev = "tap";
490499
}
491500

492501

@@ -509,9 +518,10 @@ static int semu_start(int argc, char **argv)
509518
char *dtb_file;
510519
char *initrd_file;
511520
char *disk_file;
521+
char *netdev;
512522
int hart_count = 1;
513523
handle_options(argc, argv, &kernel_file, &dtb_file, &initrd_file,
514-
&disk_file, &hart_count);
524+
&disk_file, &netdev, &hart_count);
515525

516526
/* Initialize the emulator */
517527
emu_state_t emu;
@@ -573,7 +583,7 @@ static int semu_start(int argc, char **argv)
573583
emu.uart.in_fd = 0, emu.uart.out_fd = 1;
574584
capture_keyboard_input(); /* set up uart */
575585
#if SEMU_HAS(VIRTIONET)
576-
if (!virtio_net_init(&(emu.vnet)))
586+
if (!virtio_net_init(&(emu.vnet), netdev))
577587
fprintf(stderr, "No virtio-net functioned\n");
578588
emu.vnet.ram = emu.ram;
579589
#endif

netdev.c

+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
#include <assert.h>
2+
#include <errno.h>
3+
#include <fcntl.h>
4+
#include <linux/if.h>
5+
#include <linux/if_tun.h>
6+
#include <stdbool.h>
7+
#include <stdio.h>
8+
#include <string.h>
9+
#include <sys/ioctl.h>
10+
11+
#include "netdev.h"
12+
13+
static int net_init_tap();
14+
static int net_init_slirp();
15+
16+
static int (*const net_init_fun[NET_DEV_DRIVER_MAX])(const netdev_t *) = {
17+
[NET_DEV_DRIVER_TAP] = net_init_tap,
18+
[NET_DEV_DRIVER_USER] = net_init_slirp,
19+
};
20+
21+
static const char *netdev_driver_lookup[] = {
22+
[NET_DEV_DRIVER_TAP] = "tap",
23+
[NET_DEV_DRIVER_USER] = "user",
24+
};
25+
26+
static int find_net_dev_idx(const char *net_type, const char **netlookup)
27+
{
28+
int i;
29+
if (!net_type)
30+
return -1;
31+
for (i = 0; i < NET_DEV_DRIVER_MAX; i++) {
32+
if (!strcmp(net_type, netlookup[i])) {
33+
return i;
34+
}
35+
}
36+
return -1;
37+
}
38+
39+
static int net_init_tap(netdev_t *netdev)
40+
{
41+
net_tap_options *tap;
42+
tap = &netdev->op.tap;
43+
tap->tap_fd = open("/dev/net/tun", O_RDWR);
44+
if (tap->tap_fd < 0) {
45+
fprintf(stderr, "failed to open TAP device: %s\n", strerror(errno));
46+
return false;
47+
}
48+
49+
/* Specify persistent tap device */
50+
struct ifreq ifreq = {.ifr_flags = IFF_TAP | IFF_NO_PI};
51+
strncpy(ifreq.ifr_name, "tap%d", sizeof(ifreq.ifr_name));
52+
if (ioctl(tap->tap_fd, TUNSETIFF, &ifreq) < 0) {
53+
fprintf(stderr, "failed to allocate TAP device: %s\n", strerror(errno));
54+
return false;
55+
}
56+
57+
fprintf(stderr, "allocated TAP interface: %s\n", ifreq.ifr_name);
58+
assert(fcntl(tap->tap_fd, F_SETFL,
59+
fcntl(tap->tap_fd, F_GETFL, 0) | O_NONBLOCK) >= 0);
60+
return 0;
61+
}
62+
63+
static int net_init_slirp(netdev_t *netdev)
64+
{
65+
// TBD: create slirp dev
66+
return 0;
67+
}
68+
69+
int netdev_init(netdev_t *netdev, const char *net_type)
70+
{
71+
int dev_idx = find_net_dev_idx(net_type, netdev_driver_lookup);
72+
if (dev_idx == -1)
73+
return false;
74+
else
75+
netdev->type = dev_idx;
76+
77+
net_init_fun[netdev->type](netdev);
78+
79+
return true;
80+
}

netdev.h

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#pragma once
2+
3+
typedef enum {
4+
NET_DEV_DRIVER_TAP,
5+
NET_DEV_DRIVER_USER,
6+
NET_DEV_DRIVER_MAX,
7+
} net_dev_driver_t;
8+
9+
typedef struct {
10+
int tap_fd;
11+
} net_tap_options;
12+
13+
typedef struct {
14+
/* TODO: Implement user option */
15+
int temp;
16+
} net_user_options;
17+
18+
typedef struct {
19+
char *name;
20+
net_dev_driver_t type;
21+
union {
22+
net_tap_options tap;
23+
net_user_options user;
24+
} op;
25+
} netdev_t;
26+
27+
int netdev_init(netdev_t *nedtev, const char *net_type);

virtio-net.c

+85-38
Original file line numberDiff line numberDiff line change
@@ -62,11 +62,11 @@ static void virtio_net_update_status(virtio_net_state_t *vnet, uint32_t status)
6262
return;
6363

6464
/* Reset */
65-
int tap_fd = vnet->tap_fd;
65+
netdev_t peer = vnet->peer;
6666
uint32_t *ram = vnet->ram;
6767
void *priv = vnet->priv;
6868
memset(vnet, 0, sizeof(*vnet));
69-
vnet->tap_fd = tap_fd, vnet->ram = ram;
69+
vnet->peer = peer, vnet->ram = ram;
7070
vnet->priv = priv;
7171
}
7272

@@ -113,6 +113,64 @@ static bool vnet_iovec_read(struct iovec **vecs,
113113
return n && !*nvecs;
114114
}
115115

116+
static ssize_t handle_read(netdev_t *netdev,
117+
virtio_net_queue_t *queue,
118+
struct iovec *iovs_cursor,
119+
size_t niovs)
120+
{
121+
ssize_t plen = 0;
122+
switch (netdev->type) {
123+
case NET_DEV_DRIVER_TAP:
124+
plen = readv(netdev->op.tap.tap_fd, iovs_cursor, niovs);
125+
if (plen < 0 && (errno == EWOULDBLOCK || errno == EAGAIN)) {
126+
queue->fd_ready = false;
127+
return -1;
128+
}
129+
if (plen < 0) {
130+
plen = 0;
131+
fprintf(stderr, "[VNET] could not read packet: %s\n",
132+
strerror(errno));
133+
}
134+
break;
135+
case NET_DEV_DRIVER_USER:
136+
/* TODO: handle read */
137+
break;
138+
default:
139+
break;
140+
}
141+
142+
return plen;
143+
}
144+
145+
static ssize_t handle_write(netdev_t *netdev,
146+
virtio_net_queue_t *queue,
147+
struct iovec *iovs_cursor,
148+
size_t niovs)
149+
{
150+
ssize_t plen = 0;
151+
switch (netdev->type) {
152+
case NET_DEV_DRIVER_TAP:
153+
plen = writev(netdev->op.tap.tap_fd, iovs_cursor, niovs);
154+
if (plen < 0 && (errno == EWOULDBLOCK || errno == EAGAIN)) {
155+
queue->fd_ready = false;
156+
return -1;
157+
}
158+
if (plen < 0) {
159+
plen = 0;
160+
fprintf(stderr, "[VNET] could not write packet: %s\n",
161+
strerror(errno));
162+
}
163+
break;
164+
case NET_DEV_DRIVER_USER:
165+
/* TODO: handle slirp_input */
166+
break;
167+
default:
168+
break;
169+
}
170+
171+
return plen;
172+
}
173+
116174
/* Require existing 'desc_idx' to use as iteration variable, and input
117175
* 'buffer_idx'.
118176
*/
@@ -186,18 +244,10 @@ static bool vnet_iovec_read(struct iovec **vecs,
186244
virtio_header, sizeof(virtio_header)); \
187245
} \
188246
\
189-
ssize_t plen = \
190-
VERB##v(vnet->tap_fd, buffer_iovs_cursor, buffer_niovs); \
191-
if (plen < 0 && (errno == EWOULDBLOCK || errno == EAGAIN)) { \
192-
queue->fd_ready = false; \
247+
ssize_t plen = handle_##VERB(&vnet->peer, queue, \
248+
buffer_iovs_cursor, buffer_niovs); \
249+
if (plen < 0) \
193250
break; \
194-
} \
195-
if (plen < 0) { \
196-
plen = 0; \
197-
fprintf(stderr, "[VNET] could not " #VERB " packet: %s\n", \
198-
strerror(errno)); \
199-
} \
200-
\
201251
/* consume from available queue, write to used queue */ \
202252
queue->last_avail++; \
203253
ram[queue->QueueUsed + 1 + (new_used % queue->QueueNum) * 2] = \
@@ -223,15 +273,25 @@ void virtio_net_refresh_queue(virtio_net_state_t *vnet)
223273
(vnet->Status & VIRTIO_STATUS__DEVICE_NEEDS_RESET))
224274
return;
225275

226-
struct pollfd pfd = {vnet->tap_fd, POLLIN | POLLOUT, 0};
227-
poll(&pfd, 1, 0);
228-
if (pfd.revents & POLLIN) {
229-
vnet->queues[VNET_QUEUE_RX].fd_ready = true;
230-
virtio_net_try_rx(vnet);
231-
}
232-
if (pfd.revents & POLLOUT) {
233-
vnet->queues[VNET_QUEUE_TX].fd_ready = true;
234-
virtio_net_try_tx(vnet);
276+
net_dev_driver_t dev_type = vnet->peer.type;
277+
switch (dev_type) {
278+
case NET_DEV_DRIVER_TAP:
279+
struct pollfd pfd = {vnet->peer.op.tap.tap_fd, POLLIN | POLLOUT, 0};
280+
poll(&pfd, 1, 0);
281+
if (pfd.revents & POLLIN) {
282+
vnet->queues[VNET_QUEUE_RX].fd_ready = true;
283+
virtio_net_try_rx(vnet);
284+
}
285+
if (pfd.revents & POLLOUT) {
286+
vnet->queues[VNET_QUEUE_TX].fd_ready = true;
287+
virtio_net_try_tx(vnet);
288+
}
289+
break;
290+
case NET_DEV_DRIVER_USER:
291+
/* TODO: handle slirp input/output */
292+
break;
293+
default:
294+
break;
235295
}
236296
}
237297

@@ -433,7 +493,7 @@ void virtio_net_write(hart_t *vm,
433493
}
434494
}
435495

436-
bool virtio_net_init(virtio_net_state_t *vnet)
496+
bool virtio_net_init(virtio_net_state_t *vnet, const char *name)
437497
{
438498
if (vnet_dev_cnt >= VNET_DEV_CNT_MAX) {
439499
fprintf(stderr,
@@ -444,23 +504,10 @@ bool virtio_net_init(virtio_net_state_t *vnet)
444504
/* Allocate memory for the private member */
445505
vnet->priv = &vnet_configs[vnet_dev_cnt++];
446506

447-
vnet->tap_fd = open("/dev/net/tun", O_RDWR);
448-
if (vnet->tap_fd < 0) {
449-
fprintf(stderr, "failed to open TAP device: %s\n", strerror(errno));
507+
if (!netdev_init(&vnet->peer, name)) {
508+
fprintf(stderr, "Fail to init net device %s\n", name);
450509
return false;
451510
}
452511

453-
/* Specify persistent tap device */
454-
struct ifreq ifreq = {.ifr_flags = IFF_TAP | IFF_NO_PI};
455-
strncpy(ifreq.ifr_name, TAP_INTERFACE, sizeof(ifreq.ifr_name));
456-
if (ioctl(vnet->tap_fd, TUNSETIFF, &ifreq) < 0) {
457-
fprintf(stderr, "failed to allocate TAP device: %s\n", strerror(errno));
458-
return false;
459-
}
460-
461-
fprintf(stderr, "allocated TAP interface: %s\n", ifreq.ifr_name);
462-
assert(fcntl(vnet->tap_fd, F_SETFL,
463-
fcntl(vnet->tap_fd, F_GETFL, 0) | O_NONBLOCK) >= 0);
464-
465512
return true;
466513
}

0 commit comments

Comments
 (0)