Skip to content

Commit e8531c1

Browse files
authored
fix(proc): expose arp table for busybox arp (#480)
* busybox_arp Pipeline round: 6 Reviewer decision: PASS Reviewer summary: busybox_arp 闭环通过。我复核了 issue #13 行:rcore-os/linux-compatible-testsuit#13 中 busybox_arp 的测试命令为 `busybox arp 2>&1`,验证方式为 `grep -qF "HWtype"`;journal/passed_commits 仅显示 busybox_nice、busybox_iostat、busybox_ip 已 PASS,busybox_arp 仍在 backlog。当前 diff 只新增 busybox_arp 检查,没有搬运已 PASS 的 busybox_nice/iostat/ip 检查。Linux 源码级 /proc/net/arp 回归在本机通过;riscv64 qemu-user BusyBox arp 返回 rc=0 且输出包含 `[ether]`,确认 Developer 对当前 BusyBox 1.37 增加 fallback oracle 的解释成立。复跑 `cargo xtask starry test qemu --arch riscv64 --test-group normal --test-case bugfix`、`... --test-case busybox`、`cargo fmt --check`、`git diff --check`、`cargo xtask clippy --package starry-kernel` 均通过。未修改仓库正式文件,仅生成 `/tmp/bug-proc-net-arp-review` 和 target/QEMU 验证产物。 * fix(starry): render proc net arp from neighbors * test(starry): allow empty busybox arp output
1 parent 016737c commit e8531c1

17 files changed

Lines changed: 253 additions & 12 deletions

File tree

os/StarryOS/kernel/src/pseudofs/proc.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,39 @@ fn render_stat() -> String {
242242
buf
243243
}
244244

245+
fn render_proc_net_arp() -> String {
246+
let mut entries = axnet::arp_entries();
247+
entries.sort_by(|a, b| {
248+
a.device
249+
.cmp(&b.device)
250+
.then_with(|| a.ip_addr.cmp(&b.ip_addr))
251+
});
252+
253+
let mut buf = "IP address HW type Flags HW address Mask \
254+
Device\n"
255+
.to_string();
256+
for entry in entries {
257+
let ip = entry.ip_addr;
258+
let mac = entry.hw_addr;
259+
let ip_addr = format!("{}.{}.{}.{}", ip[0], ip[1], ip[2], ip[3]);
260+
let _ = writeln!(
261+
buf,
262+
"{:<16} 0x{:<8x} 0x{:<8x} {:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x} * {}",
263+
ip_addr,
264+
entry.hw_type,
265+
entry.flags,
266+
mac[0],
267+
mac[1],
268+
mac[2],
269+
mac[3],
270+
mac[4],
271+
mac[5],
272+
entry.device
273+
);
274+
}
275+
buf
276+
}
277+
245278
pub fn new_procfs() -> Filesystem {
246279
SimpleFs::new_with("proc".into(), 0x9fa0, builder)
247280
}
@@ -804,6 +837,17 @@ fn builder(fs: Arc<SimpleFs>) -> DirMaker {
804837
SimpleDir::new_maker(fs.clone(), Arc::new(sys))
805838
});
806839

840+
root.add("net", {
841+
let mut net = DirMapping::new();
842+
843+
net.add(
844+
"arp",
845+
SimpleFile::new_regular(fs.clone(), || Ok(render_proc_net_arp())),
846+
);
847+
848+
SimpleDir::new_maker(fs.clone(), Arc::new(net))
849+
});
850+
807851
root.add("dynamic_debug", {
808852
let mut dynamic_debug = DirMapping::new();
809853

os/arceos/modules/axnet-ng/src/device/ethernet.rs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use alloc::{
22
string::String,
33
sync::{Arc, Weak},
44
vec,
5+
vec::Vec,
56
};
67
use core::task::Waker;
78

@@ -20,7 +21,7 @@ use smoltcp::{
2021

2122
use crate::{
2223
consts::{ETHERNET_MAX_PENDING_PACKETS, STANDARD_MTU},
23-
device::Device,
24+
device::{ArpEntry, Device},
2425
};
2526

2627
const EMPTY_MAC: EthernetAddress = EthernetAddress([0; 6]);
@@ -489,6 +490,27 @@ impl Device for EthernetDevice {
489490
self.pending_neighbors.clear();
490491
}
491492

493+
fn arp_entries(&self, timestamp: Instant) -> Vec<ArpEntry> {
494+
self.neighbors
495+
.iter()
496+
.filter_map(|(ip_addr, neighbor)| {
497+
if neighbor.expires_at <= timestamp {
498+
return None;
499+
}
500+
let IpAddress::Ipv4(ip_addr) = ip_addr else {
501+
return None;
502+
};
503+
Some(ArpEntry {
504+
ip_addr: ip_addr.octets(),
505+
hw_type: 1,
506+
flags: 2,
507+
hw_addr: neighbor.hardware_address.0,
508+
device: self.name.clone(),
509+
})
510+
})
511+
.collect()
512+
}
513+
492514
fn register_waker(&self, waker: &Waker) {
493515
if self.inner.irq_num.is_some() {
494516
self.inner.poll_ready.register(waker);

os/arceos/modules/axnet-ng/src/device/mod.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use alloc::{string::String, vec::Vec};
12
use core::task::Waker;
23

34
use smoltcp::{
@@ -16,6 +17,15 @@ pub use loopback::*;
1617
#[cfg(feature = "vsock")]
1718
pub use vsock::*;
1819

20+
#[derive(Clone, Debug, Eq, PartialEq)]
21+
pub struct ArpEntry {
22+
pub ip_addr: [u8; 4],
23+
pub hw_type: u16,
24+
pub flags: u16,
25+
pub hw_addr: [u8; 6],
26+
pub device: String,
27+
}
28+
1929
pub trait Device: Send + Sync {
2030
fn name(&self) -> &str;
2131

@@ -34,5 +44,9 @@ pub trait Device: Send + Sync {
3444

3545
fn set_ipv4_addr(&mut self, _addr: Option<Ipv4Cidr>) {}
3646

47+
fn arp_entries(&self, _timestamp: Instant) -> Vec<ArpEntry> {
48+
Vec::new()
49+
}
50+
3751
fn register_waker(&self, waker: &Waker);
3852
}

os/arceos/modules/axnet-ng/src/lib.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ pub mod unix;
4040
pub mod vsock;
4141
mod wrapper;
4242

43-
use alloc::{borrow::ToOwned, boxed::Box};
43+
use alloc::{borrow::ToOwned, boxed::Box, vec::Vec};
4444
use core::{
4545
sync::atomic::{AtomicBool, Ordering},
4646
time::Duration,
@@ -51,7 +51,6 @@ use ax_sync::Mutex;
5151
use smoltcp::wire::{EthernetAddress, Ipv4Address, Ipv4Cidr};
5252
use spin::{Lazy, Once};
5353

54-
pub use self::socket::*;
5554
use self::{
5655
consts::{GATEWAY, IP, IP_PREFIX},
5756
device::{EthernetDevice, LoopbackDevice},
@@ -60,6 +59,7 @@ use self::{
6059
service::Service,
6160
wrapper::SocketSetWrapper,
6261
};
62+
pub use self::{device::ArpEntry, socket::*};
6363

6464
static LISTEN_TABLE: Lazy<ListenTable> = Lazy::new(ListenTable::new);
6565
static SOCKET_SET: Lazy<SocketSetWrapper> = Lazy::new(SocketSetWrapper::new);
@@ -192,6 +192,10 @@ pub fn poll_interfaces() {
192192
}
193193
}
194194

195+
pub fn arp_entries() -> Vec<ArpEntry> {
196+
get_service().arp_entries()
197+
}
198+
195199
fn dhcp_bootstrap() {
196200
for _ in 0..DHCP_BOOTSTRAP_ATTEMPTS {
197201
poll_interfaces();

os/arceos/modules/axnet-ng/src/router.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use smoltcp::{
1414
use crate::{
1515
LISTEN_TABLE,
1616
consts::{SOCKET_BUFFER_SIZE, STANDARD_MTU},
17-
device::Device,
17+
device::{ArpEntry, Device},
1818
};
1919

2020
#[derive(Debug)]
@@ -157,6 +157,14 @@ impl Router {
157157
self.devices[dev].send(next_hop, packet, timestamp)
158158
}
159159

160+
pub fn arp_entries(&self, timestamp: Instant) -> Vec<ArpEntry> {
161+
let mut entries = Vec::new();
162+
for device in &self.devices {
163+
entries.extend(device.arp_entries(timestamp));
164+
}
165+
entries
166+
}
167+
160168
pub fn dispatch(&mut self, timestamp: Instant) -> bool {
161169
let mut poll_next = false;
162170
while let Ok(((), packet)) = self.tx_buffer.dequeue() {

os/arceos/modules/axnet-ng/src/service.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use smoltcp::{
1717
},
1818
};
1919

20-
use crate::{SOCKET_SET, consts::STANDARD_MTU, router::Router};
20+
use crate::{SOCKET_SET, consts::STANDARD_MTU, device::ArpEntry, router::Router};
2121

2222
fn now() -> Instant {
2323
Instant::from_micros_const((wall_time_nanos() / NANOS_PER_MICROS) as i64)
@@ -323,6 +323,10 @@ impl Service {
323323
rule.src
324324
}
325325

326+
pub fn arp_entries(&self) -> Vec<ArpEntry> {
327+
self.router.arp_entries(now())
328+
}
329+
326330
pub fn device_mask_for(&self, endpoint: &IpListenEndpoint) -> u32 {
327331
match endpoint.addr {
328332
Some(addr) => self
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
cmake_minimum_required(VERSION 3.20)
2+
project(bug-proc-net-arp C)
3+
4+
set(CMAKE_C_STANDARD 11)
5+
set(CMAKE_C_STANDARD_REQUIRED ON)
6+
set(CMAKE_C_EXTENSIONS ON)
7+
8+
add_executable(bug-proc-net-arp src/main.c)
9+
target_compile_options(bug-proc-net-arp PRIVATE -Wall -Wextra -Werror)
10+
11+
install(TARGETS bug-proc-net-arp RUNTIME DESTINATION usr/bin)
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
#include <errno.h>
2+
#include <stdio.h>
3+
#include <string.h>
4+
5+
static int is_blank_line(const char *line)
6+
{
7+
while (*line != '\0') {
8+
if (*line != ' ' && *line != '\t' && *line != '\r' && *line != '\n') {
9+
return 0;
10+
}
11+
line++;
12+
}
13+
return 1;
14+
}
15+
16+
static int check_contains(const char *text, const char *needle)
17+
{
18+
if (strstr(text, needle) != NULL) {
19+
printf("PASS: /proc/net/arp contains %s\n", needle);
20+
return 0;
21+
}
22+
23+
printf("FAIL: /proc/net/arp missing %s\n", needle);
24+
return 1;
25+
}
26+
27+
static int check_arp_rows(char *text)
28+
{
29+
int failed = 0;
30+
int saw_header = 0;
31+
int row_count = 0;
32+
33+
for (char *line = text; line != NULL; ) {
34+
char *next = strchr(line, '\n');
35+
if (next != NULL) {
36+
*next = '\0';
37+
next++;
38+
}
39+
40+
if (is_blank_line(line)) {
41+
line = next;
42+
continue;
43+
}
44+
45+
if (!saw_header) {
46+
saw_header = 1;
47+
line = next;
48+
continue;
49+
}
50+
51+
char ip[32];
52+
char hw_type[32];
53+
char flags[32];
54+
char hw_addr[32];
55+
char mask[32];
56+
char device[32];
57+
char extra[32];
58+
int fields = sscanf(
59+
line,
60+
"%31s %31s %31s %31s %31s %31s %31s",
61+
ip,
62+
hw_type,
63+
flags,
64+
hw_addr,
65+
mask,
66+
device,
67+
extra);
68+
if (fields != 6) {
69+
printf("FAIL: malformed /proc/net/arp row: %s\n", line);
70+
failed++;
71+
line = next;
72+
continue;
73+
}
74+
75+
row_count++;
76+
if (strcmp(ip, "10.0.2.2") == 0
77+
&& strcmp(hw_addr, "52:54:00:12:34:56") == 0
78+
&& strcmp(device, "eth0") == 0) {
79+
printf("FAIL: /proc/net/arp exposes fixed QEMU gateway stub\n");
80+
failed++;
81+
}
82+
83+
line = next;
84+
}
85+
86+
printf("PASS: /proc/net/arp data rows checked: %d\n", row_count);
87+
return failed;
88+
}
89+
90+
int main(void)
91+
{
92+
char buf[512];
93+
FILE *fp = fopen("/proc/net/arp", "r");
94+
if (fp == NULL) {
95+
printf("FAIL: fopen /proc/net/arp: errno=%d %s\n", errno, strerror(errno));
96+
return 1;
97+
}
98+
99+
size_t nread = fread(buf, 1, sizeof(buf) - 1, fp);
100+
int saved_errno = errno;
101+
if (ferror(fp)) {
102+
printf("FAIL: fread /proc/net/arp: errno=%d %s\n", saved_errno, strerror(saved_errno));
103+
fclose(fp);
104+
return 1;
105+
}
106+
fclose(fp);
107+
108+
buf[nread] = '\0';
109+
printf("/proc/net/arp content:\n%s", buf);
110+
111+
int failed = 0;
112+
failed += check_contains(buf, "IP address");
113+
failed += check_contains(buf, "HW type");
114+
failed += check_contains(buf, "HW address");
115+
failed += check_contains(buf, "Device");
116+
failed += check_arp_rows(buf);
117+
118+
if (failed == 0) {
119+
printf("ALL TESTS PASSED\n");
120+
return 0;
121+
}
122+
123+
printf("SOME TESTS FAILED\n");
124+
return 1;
125+
}

test-suit/starryos/normal/qemu-smp1/bugfix/qemu-aarch64.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ test_commands = [
3232
"/usr/bin/clone3-badsize",
3333
"/usr/bin/bug-unaligned-cow-split",
3434
"/usr/bin/bug-proc-maps-lseek-refresh",
35+
"/usr/bin/bug-proc-net-arp",
3536
"/usr/bin/bug-proc-init-pid",
3637
"/usr/bin/bug-tcp-send-no-epoll-notify",
3738
]

test-suit/starryos/normal/qemu-smp1/bugfix/qemu-loongarch64.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ test_commands = [
3434
"/usr/bin/clone3-badsize",
3535
"/usr/bin/bug-unaligned-cow-split",
3636
"/usr/bin/bug-proc-maps-lseek-refresh",
37+
"/usr/bin/bug-proc-net-arp",
3738
"/usr/bin/bug-proc-init-pid",
3839
"/usr/bin/bug-tcp-send-no-epoll-notify",
3940
]

0 commit comments

Comments
 (0)