Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions api/axfeat/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ fs-times = ["fs", "axfs/times"]
# Networking
net = ["alloc", "paging", "axdriver/virtio-net", "dep:axnet", "axruntime/net"]
vsock = ["net", "axdriver/virtio-socket", "axruntime/vsock", "axnet/vsock"]
netlink = ["net", "axnet/netlink"]

# Display
display = [
Expand Down
29 changes: 8 additions & 21 deletions modules/axfs/src/highlevel/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -219,16 +219,13 @@ impl OpenOptions {
}

pub fn open_loc(&self, loc: Location) -> VfsResult<OpenResult> {
if !self.is_valid() {
return Err(VfsError::InvalidInput);
}
self.check_options()?;

self._open(loc)
}

pub fn open(&self, context: &FsContext, path: impl AsRef<Path>) -> VfsResult<OpenResult> {
if !self.is_valid() {
return Err(VfsError::InvalidInput);
}
self.check_options()?;

let loc = match context.resolve_parent(path.as_ref()) {
Ok((parent, name)) => {
Expand Down Expand Up @@ -273,24 +270,14 @@ impl OpenOptions {
})
}

pub(crate) fn is_valid(&self) -> bool {
pub(crate) fn check_options(&self) -> VfsResult<()> {
if !self.read && !self.write && !self.append {
return true;
return Err(VfsError::InvalidInput);
}
match (self.write, self.append) {
(true, false) => {}
(false, false) => {
if self.truncate || self.create || self.create_new {
return false;
}
}
(_, true) => {
if self.truncate && !self.create_new {
return false;
}
}
if self.create_new && !self.create {
return Err(VfsError::AlreadyExists);
Copy link

Copilot AI Mar 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

create_new without create currently returns VfsError::AlreadyExists, which implies the path exists even though this is just an invalid/meaningless flag combination (POSIX treats O_EXCL as only meaningful with O_CREAT). Returning InvalidInput (or implicitly enabling create when create_new is set) would be more accurate.

Suggested change
return Err(VfsError::AlreadyExists);
return Err(VfsError::InvalidInput);

Copilot uses AI. Check for mistakes.
}
true
Ok(())
}
}

Expand Down
10 changes: 9 additions & 1 deletion modules/axnet/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ documentation = "https://arceos-org.github.io/arceos/axnet/index.html"

[features]
vsock = ["axdriver/vsock"]
netlink = []

[dependencies]
async-channel = { version = "2.5", default-features = false }
Expand All @@ -25,13 +26,20 @@ axio = { workspace = true }
axpoll = { workspace = true }
axsync = { workspace = true }
axtask = { workspace = true }
bitflags = "2.9.1"
bitflags = { version = "2.9.1", features = ["bytemuck"] }
bytemuck = { version = "1.23", features = ["derive"] }
cfg-if = { workspace = true }
enum_dispatch = { workspace = true }
event-listener = { version = "5.4", default-features = false }
hashbrown = "0.16"
lazy_static = { workspace = true }
log = { workspace = true }
memory_addr = { workspace = true }
num_enum = { version = "0.7", default-features = false }
rand = { version = "0.9", default-features = false, features = [
"alloc",
"small_rng",
] }
ringbuf = { version = "0.4.8", default-features = false, features = ["alloc"] }
spin = { workspace = true }

Expand Down
2 changes: 2 additions & 0 deletions modules/axnet/src/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,5 @@ pub const LISTEN_QUEUE_SIZE: usize = 512;

pub const SOCKET_BUFFER_SIZE: usize = 64;
pub const ETHERNET_MAX_PENDING_PACKETS: usize = 32;

pub const NETLINK_DEFAULT_BUF_SIZE: usize = 65536;
32 changes: 29 additions & 3 deletions modules/axnet/src/device/ethernet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ use smoltcp::{
time::{Duration, Instant},
wire::{
ArpOperation, ArpPacket, ArpRepr, EthernetAddress, EthernetFrame, EthernetProtocol,
EthernetRepr, IpAddress, Ipv4Cidr,
EthernetRepr, IpAddress, Ipv4Address, Ipv4Cidr,
},
};

use crate::{
consts::{ETHERNET_MAX_PENDING_PACKETS, STANDARD_MTU},
device::Device,
device::{Device, DeviceFlags, DeviceType},
};

const EMPTY_MAC: EthernetAddress = EthernetAddress([0; 6]);
Expand All @@ -26,6 +26,7 @@ struct Neighbor {
}

pub struct EthernetDevice {
index: u32,
name: String,
inner: AxNetDevice,
neighbors: HashMap<IpAddress, Option<Neighbor>>,
Expand All @@ -36,7 +37,7 @@ pub struct EthernetDevice {
impl EthernetDevice {
const NEIGHBOR_TTL: Duration = Duration::from_secs(60);

pub fn new(name: String, inner: AxNetDevice, ip: Ipv4Cidr) -> Self {
pub fn new(index: u32, name: String, inner: AxNetDevice, ip: Ipv4Cidr) -> Self {
let pending_packets = PacketBuffer::new(
vec![PacketMetadata::EMPTY; ETHERNET_MAX_PENDING_PACKETS],
vec![
Expand All @@ -46,6 +47,7 @@ impl EthernetDevice {
],
);
Self {
index,
name,
inner,
neighbors: HashMap::new(),
Expand Down Expand Up @@ -261,6 +263,30 @@ impl Device for EthernetDevice {
&self.name
}

fn get_type(&self) -> DeviceType {
DeviceType::ETHER
}

fn get_flags(&self) -> DeviceFlags {
DeviceFlags::UP
| DeviceFlags::BROADCAST
| DeviceFlags::RUNNING
| DeviceFlags::LOWER_UP
| DeviceFlags::MULTICAST
}

fn get_index(&self) -> u32 {
self.index
}

fn ipv4_addr(&self) -> Option<Ipv4Address> {
Some(self.ip.address())
}

fn prefix_len(&self) -> Option<u8> {
Some(self.ip.prefix_len())
}

fn recv(&mut self, buffer: &mut PacketBuffer<()>, timestamp: Instant) -> bool {
loop {
let rx_buf = match self.inner.receive() {
Expand Down
28 changes: 25 additions & 3 deletions modules/axnet/src/device/loopback.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use alloc::vec;
use core::task::Waker;
use core::{net::Ipv4Addr, task::Waker};

use axpoll::PollSet;
use smoltcp::{
Expand All @@ -10,20 +10,22 @@ use smoltcp::{

use crate::{
consts::{SOCKET_BUFFER_SIZE, STANDARD_MTU},
device::Device,
device::{Device, DeviceFlags, DeviceType},
};

pub struct LoopbackDevice {
index: u32,
buffer: PacketBuffer<'static, ()>,
poll: PollSet,
}
impl LoopbackDevice {
pub fn new() -> Self {
pub fn new(index: u32) -> Self {
let buffer = PacketBuffer::new(
vec![PacketMetadata::EMPTY; SOCKET_BUFFER_SIZE],
vec![0u8; STANDARD_MTU * SOCKET_BUFFER_SIZE],
);
Self {
index,
buffer,
poll: PollSet::new(),
}
Expand All @@ -35,6 +37,26 @@ impl Device for LoopbackDevice {
"lo"
}

fn get_type(&self) -> DeviceType {
DeviceType::LOOPBACK
}

fn get_flags(&self) -> DeviceFlags {
DeviceFlags::UP | DeviceFlags::LOOPBACK | DeviceFlags::RUNNING
}

fn get_index(&self) -> u32 {
self.index
}

fn ipv4_addr(&self) -> Option<Ipv4Addr> {
Some(Ipv4Addr::new(127, 0, 0, 1))
}
Comment on lines +52 to +54
Copy link

Copilot AI Mar 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Device::ipv4_addr is defined to return smoltcp::wire::Ipv4Address (see device/mod.rs), but LoopbackDevice implements it as core::net::Ipv4Addr, which will not satisfy the trait and should fail to compile. Use the same Ipv4Address type as the trait (or change the trait to consistently use core::net::Ipv4Addr).

Copilot uses AI. Check for mistakes.

fn prefix_len(&self) -> Option<u8> {
Some(8)
}

fn recv(&mut self, buffer: &mut PacketBuffer<()>, _timestamp: Instant) -> bool {
self.buffer.dequeue().ok().is_some_and(|(_, rx_buf)| {
buffer
Expand Down
96 changes: 95 additions & 1 deletion modules/axnet/src/device/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
use core::task::Waker;

use smoltcp::{storage::PacketBuffer, time::Instant, wire::IpAddress};
use bitflags::bitflags;
use num_enum::TryFromPrimitive;
use smoltcp::{
storage::PacketBuffer,
time::Instant,
wire::{IpAddress, Ipv4Address},
};

mod ethernet;
mod loopback;
Expand All @@ -15,6 +21,16 @@ pub use vsock::*;
pub trait Device: Send + Sync {
fn name(&self) -> &str;

fn get_type(&self) -> DeviceType;

fn get_flags(&self) -> DeviceFlags;

fn get_index(&self) -> u32;

fn ipv4_addr(&self) -> Option<Ipv4Address>;

fn prefix_len(&self) -> Option<u8>;

fn recv(&mut self, buffer: &mut PacketBuffer<()>, timestamp: Instant) -> bool;
/// Sends a packet to the next hop.
///
Expand All @@ -25,3 +41,81 @@ pub trait Device: Send + Sync {

fn register_waker(&self, waker: &Waker);
}

/// Device type.
///
/// Reference: <https://elixir.bootlin.com/linux/v6.0.18/source/include/uapi/linux/if_arp.h#L30>
#[repr(u16)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive)]
#[allow(dead_code)]
pub enum DeviceType {
// Arp protocol hardware identifiers
/// from KA9Q: NET/ROM pseudo
NETROM = 0,
/// Ethernet 10Mbps
ETHER = 1,
/// Experimental Ethernet
EETHER = 2,

// Dummy types for non ARP hardware
/// IPIP tunnel
TUNNEL = 768,
/// IP6IP6 tunnel
TUNNEL6 = 769,
/// Frame Relay Access Device
FRAD = 770,
/// SKIP vif
SKIP = 771,
/// Loopback device
LOOPBACK = 772,
/// Localtalk device
LOCALTALK = 773,
// TODO: This enum is not exhaustive
}

bitflags! {
/// Device flags.
///
/// Reference: <https://elixir.bootlin.com/linux/v6.0.18/source/include/uapi/linux/if.h#L82>
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct DeviceFlags: u32 {
/// Device is up
const UP = 1<<0;
/// Broadcast address valid
const BROADCAST = 1<<1;
/// Turn on debugging
const DEBUG = 1<<2;
/// Loopback net
const LOOPBACK = 1<<3;
/// Device is has p-p link
const POINTOPOINT = 1<<4;
/// Avoid use of trailers
const NOTRAILERS = 1<<5;
/// Device RFC2863 OPER_UP
const RUNNING = 1<<6;
/// No ARP protocol
const NOARP = 1<<7;
/// Receive all packets
const PROMISC = 1<<8;
/// Receive all multicast packets
const ALLMULTI = 1<<9;
/// Master of a load balancer
const MASTER = 1<<10;
/// Slave of a load balancer
const SLAVE = 1<<11;
/// Supports multicast
const MULTICAST = 1<<12;
/// Can set media type
const PORTSEL = 1<<13;
/// Auto media select active
const AUTOMEDIA = 1<<14;
/// Dialup device with changing addresses
const DYNAMIC = 1<<15;
/// Driver signals L1 up
const LOWER_UP = 1<<16;
/// Driver signals dormant
const DORMANT = 1<<17;
/// Echo sent packets
const ECHO = 1<<18;
}
}
11 changes: 10 additions & 1 deletion modules/axnet/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
//! [smoltcp]: https://github.com/smoltcp-rs/smoltcp

#![no_std]
#![feature(associated_type_defaults)]

#[macro_use]
extern crate log;
Expand All @@ -32,9 +33,13 @@ pub mod udp;
pub mod unix;
#[cfg(feature = "vsock")]
pub mod vsock;
#[cfg(feature = "netlink")]
pub mod netlink;

mod wrapper;

use alloc::{borrow::ToOwned, boxed::Box};
use core::sync::atomic::{AtomicU32, Ordering};

use axdriver::{AxDeviceContainer, prelude::*};
use axsync::Mutex;
Expand All @@ -56,6 +61,8 @@ static SOCKET_SET: Lazy<SocketSetWrapper> = Lazy::new(SocketSetWrapper::new);

static SERVICE: Once<Mutex<Service>> = Once::new();

static DEVICE_INDEX_COUNTER: AtomicU32 = AtomicU32::new(1);

fn get_service() -> axsync::MutexGuard<'static, Service> {
SERVICE
.get()
Expand All @@ -68,7 +75,8 @@ pub fn init_network(mut net_devs: AxDeviceContainer<AxNetDevice>) {
info!("Initialize network subsystem...");

let mut router = Router::new();
let lo_dev = router.add_device(Box::new(LoopbackDevice::new()));
let index = DEVICE_INDEX_COUNTER.fetch_add(1, Ordering::Relaxed);
let lo_dev = router.add_device(Box::new(LoopbackDevice::new(index)));

let lo_ip = Ipv4Cidr::new(Ipv4Address::new(127, 0, 0, 1), 8);
router.add_rule(Rule::new(
Expand All @@ -85,6 +93,7 @@ pub fn init_network(mut net_devs: AxDeviceContainer<AxNetDevice>) {
let eth0_ip = Ipv4Cidr::new(IP.parse().expect("Invalid IPv4 address"), IP_PREFIX);

let eth0_dev = router.add_device(Box::new(EthernetDevice::new(
DEVICE_INDEX_COUNTER.fetch_add(1, Ordering::Relaxed),
"eth0".to_owned(),
dev,
eth0_ip,
Expand Down
Loading
Loading