Skip to content

Commit 333254e

Browse files
authored
Merge pull request #64 from chiangkd/dev
Consolidate network devices
2 parents a0ad61b + aa3a224 commit 333254e

File tree

8 files changed

+317
-46
lines changed

8 files changed

+317
-46
lines changed

.ci/test-netdev.sh

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
#!/usr/bin/env bash
2+
3+
function cleanup {
4+
sleep 1
5+
pkill -9 semu
6+
}
7+
8+
function ASSERT {
9+
$*
10+
local RES=$?
11+
if [ $RES -ne 0 ]; then
12+
echo 'Assert failed: "' $* '"'
13+
exit $RES
14+
fi
15+
}
16+
17+
cleanup
18+
19+
# macOS needs more time to boot compared to Linux, so the timeout is set to
20+
# 600 seconds for macOS to handle the longer startup. For Linux, 90 seconds
21+
# is sufficient due to its faster boot process.
22+
UNAME_S=$(uname -s)
23+
if [[ ${UNAME_S} == "Darwin" ]]; then
24+
TIMEOUT=600
25+
else # Linux
26+
TIMEOUT=30
27+
fi
28+
29+
function TEST_NETDEV {
30+
local NETDEV=$1
31+
local CMD_PREFIX=""
32+
33+
if [ $NETDEV == tap ]; then
34+
CMD_PREFIX="sudo "
35+
fi
36+
37+
ASSERT expect <<DONE
38+
set timeout ${TIMEOUT}
39+
spawn ${CMD_PREFIX}make check NETDEV=${NETDEV}
40+
expect "buildroot login:" { send "root\n" } timeout { exit 1 }
41+
expect "# " { send "uname -a\n" } timeout { exit 2 }
42+
if { "$NETDEV" == "tap" } {
43+
exec sudo ip addr add 192.168.10.1/24 dev tap0
44+
exec sudo ip link set tap0 up
45+
expect "riscv32 GNU/Linux" { send "ip l set eth0 up\n" } timeout { exit 3 }
46+
expect "# " { send "ip a add 192.168.10.2/24 dev eth0\n" }
47+
expect "# " { send "ping -c 3 192.168.10.1\n" }
48+
expect "3 packets transmitted, 3 packets received, 0% packet loss" { } timeout { exit 4 }
49+
} elseif { "$NETDEV" == "user" } {
50+
# Test slirp configuration
51+
expect "riscv32 GNU/Linux" { send "\x01"; send "x" } timeout { exit 3 }
52+
}
53+
DONE
54+
}
55+
56+
# Network devices
57+
NETWORK_DEVICES=(tap user)
58+
59+
for NETDEV in "${NETWORK_DEVICES[@]}"; do
60+
cleanup
61+
echo "Test network device: $NETDEV"
62+
TEST_NETDEV $NETDEV
63+
done
64+
65+
ret=$?
66+
cleanup
67+
68+
MESSAGES=("OK!" \
69+
"Fail to boot" \
70+
"Fail to login" \
71+
"Fail to run commands" \
72+
"Fail to transfer packet" \
73+
)
74+
75+
COLOR_G='\e[32;01m' # Green
76+
COLOR_N='\e[0m'
77+
printf "\n[ ${COLOR_G}${MESSAGES[$ret]}${COLOR_N} ]\n"
78+
79+
exit ${ret}

.github/workflows/main.yml

+4
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ jobs:
1616
- name: automated test
1717
run: .ci/autorun.sh
1818
shell: bash
19+
- name: netdev test
20+
run: .ci/test-netdev.sh
21+
shell: bash
22+
if: ${{ success() }}
1923

2024
semu-macOS:
2125
runs-on: macos-latest

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

+90
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
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 <stdio.h>
7+
#include <stdlib.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_user();
15+
16+
static const char *netdev_impl_lookup[] = {
17+
#define _(dev) [NETDEV_IMPL_##dev] = #dev,
18+
SUPPORTED_DEVICES
19+
#undef _
20+
NULL,
21+
};
22+
23+
static int find_net_dev_idx(const char *net_type, const char **netlookup)
24+
{
25+
if (!net_type)
26+
return -1;
27+
28+
for (int i = 0; netlookup[i]; i++) {
29+
if (!strcmp(net_type, netlookup[i]))
30+
return i;
31+
}
32+
return -1;
33+
}
34+
35+
static int net_init_tap(netdev_t *netdev)
36+
{
37+
net_tap_options_t *tap = (net_tap_options_t *) netdev->op;
38+
tap->tap_fd = open("/dev/net/tun", O_RDWR);
39+
if (tap->tap_fd < 0) {
40+
fprintf(stderr, "failed to open TAP device: %s\n", strerror(errno));
41+
return false;
42+
}
43+
44+
/* Specify persistent tap device */
45+
struct ifreq ifreq = {.ifr_flags = IFF_TAP | IFF_NO_PI};
46+
strncpy(ifreq.ifr_name, "tap%d", sizeof(ifreq.ifr_name));
47+
if (ioctl(tap->tap_fd, TUNSETIFF, &ifreq) < 0) {
48+
fprintf(stderr, "failed to allocate TAP device: %s\n", strerror(errno));
49+
return false;
50+
}
51+
52+
fprintf(stderr, "allocated TAP interface: %s\n", ifreq.ifr_name);
53+
assert(fcntl(tap->tap_fd, F_SETFL,
54+
fcntl(tap->tap_fd, F_GETFL, 0) | O_NONBLOCK) >= 0);
55+
return 0;
56+
}
57+
58+
static int net_init_user(netdev_t *netdev)
59+
{
60+
/* TODO: create slirp dev */
61+
return 0;
62+
}
63+
64+
bool netdev_init(netdev_t *netdev, const char *net_type)
65+
{
66+
int dev_idx = find_net_dev_idx(net_type, netdev_impl_lookup);
67+
if (dev_idx == -1)
68+
return false;
69+
netdev->type = dev_idx;
70+
71+
switch (dev_idx) {
72+
#define _(dev) \
73+
case NETDEV_IMPL_##dev: \
74+
netdev->op = malloc(sizeof(net_##dev##_options_t)); \
75+
if (!netdev->op) { \
76+
fprintf(stderr, "Failed to allocate memory for %s device\n", \
77+
#dev); \
78+
return false; \
79+
} \
80+
net_init_##dev(netdev); \
81+
break;
82+
SUPPORTED_DEVICES
83+
#undef _
84+
default:
85+
fprintf(stderr, "unknown network device\n");
86+
break;
87+
}
88+
89+
return true;
90+
}

netdev.h

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#pragma once
2+
3+
#include <stdbool.h>
4+
5+
/* clang-format off */
6+
#define SUPPORTED_DEVICES \
7+
_(tap) \
8+
_(user)
9+
/* clang-format on */
10+
11+
typedef enum {
12+
#define _(dev) NETDEV_IMPL_##dev,
13+
SUPPORTED_DEVICES
14+
#undef _
15+
} netdev_impl_t;
16+
17+
typedef struct {
18+
int tap_fd;
19+
} net_tap_options_t;
20+
21+
typedef struct {
22+
/* TODO: Implement user option */
23+
} net_user_options_t;
24+
25+
typedef struct {
26+
char *name;
27+
netdev_impl_t type;
28+
void *op;
29+
} netdev_t;
30+
31+
bool netdev_init(netdev_t *nedtev, const char *net_type);

0 commit comments

Comments
 (0)