Support SO_BROADCAST for UDP sockets

This commit is contained in:
jiangjianfeng 2025-11-11 03:42:24 +00:00 committed by Ruihan Li
parent 66b3dcc388
commit 188b20eb99
8 changed files with 105 additions and 17 deletions

View File

@ -2,7 +2,7 @@
use alloc::sync::Arc;
use smoltcp::wire::Ipv4Address;
use smoltcp::wire::{Ipv4Address, Ipv4Cidr};
use super::{port::BindPortConfig, BoundPort, InterfaceFlags, InterfaceType};
use crate::{errors::BindError, ext::Ext};
@ -78,6 +78,17 @@ impl<E: Ext> dyn Iface<E> {
self.common().prefix_len()
}
/// Gets the broadcast address of the iface, if any.
pub fn broadcast_addr(&self) -> Option<Ipv4Address> {
let cidr = {
let common = self.common();
let ipv4_addr = common.ipv4_addr()?;
let prefix_len = common.prefix_len()?;
Ipv4Cidr::new(ipv4_addr, prefix_len)
};
cidr.broadcast()
}
/// Returns a reference to the associated [`ScheduleNextPoll`].
pub fn sched_poll(&self) -> &E::ScheduleNextPoll {
self.common().sched_poll()

View File

@ -0,0 +1,37 @@
// SPDX-License-Identifier: MPL-2.0
use core::net::Ipv4Addr;
use aster_bigtcp::wire::{IpAddress, IpEndpoint};
use spin::Once;
use crate::{net::iface::iter_all_ifaces, prelude::*};
/// All known broadcast addresses.
// FIXME: This information should be maintained in the routing table,
// since a broadcast address might change if an interface's IP
// or netmask changes, or if an interface is added/removed.
static BROADCAST_ADDRS: Once<BTreeSet<Ipv4Addr>> = Once::new();
pub(super) fn init() {
BROADCAST_ADDRS.call_once(|| {
let mut broadcast_addrs = BTreeSet::new();
// 255.255.255.255 is always included.
broadcast_addrs.insert(Ipv4Addr::BROADCAST);
for iface in iter_all_ifaces() {
let Some(broadcast_addr) = iface.broadcast_addr() else {
continue;
};
broadcast_addrs.insert(broadcast_addr);
}
broadcast_addrs
});
}
/// Determines if a given IP endpoint's address is a known broadcast address.
pub fn is_broadcast_endpoint(endpoint: &IpEndpoint) -> bool {
let IpAddress::Ipv4(ipv4_addr) = &endpoint.addr;
BROADCAST_ADDRS.get().unwrap().contains(ipv4_addr)
}

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: MPL-2.0
use alloc::{borrow::ToOwned, sync::Arc};
use alloc::borrow::ToOwned;
use core::slice::Iter;
use aster_bigtcp::{
@ -11,7 +11,10 @@ use aster_softirq::BottomHalfDisabled;
use spin::Once;
use super::{poll::poll_ifaces, Iface};
use crate::{net::iface::sched::PollScheduler, prelude::*};
use crate::{
net::iface::{broadcast, sched::PollScheduler},
prelude::*,
};
static IFACES: Once<Vec<Arc<Iface>>> = Once::new();
@ -51,6 +54,8 @@ pub fn init() {
aster_network::register_send_callback(VIRTIO_DEVICE_NAME, callback);
}
broadcast::init();
poll_ifaces();
}

View File

@ -1,10 +1,12 @@
// SPDX-License-Identifier: MPL-2.0
mod broadcast;
mod ext;
mod init;
mod poll;
mod sched;
pub use broadcast::is_broadcast_endpoint;
pub use init::{init, iter_all_ifaces, loopback_iface, virtio_iface};
pub(super) use poll::init_in_first_kthread;

View File

@ -11,17 +11,20 @@ use crate::{
events::IoEvents,
fs::utils::Inode,
match_sock_option_mut,
net::socket::{
ip::options::{IpOptionSet, SetIpLevelOption},
new_pseudo_inode,
options::{Error as SocketError, SocketOption},
private::SocketPrivate,
util::{
datagram_common::{select_remote_and_bind, Bound, Inner},
options::{GetSocketLevelOption, SetSocketLevelOption, SocketOptionSet},
MessageHeader, SendRecvFlags, SocketAddr,
net::{
iface::is_broadcast_endpoint,
socket::{
ip::options::{IpOptionSet, SetIpLevelOption},
new_pseudo_inode,
options::{Error as SocketError, SocketOption},
private::SocketPrivate,
util::{
datagram_common::{select_remote_and_bind, Bound, Inner},
options::{GetSocketLevelOption, SetSocketLevelOption, SocketOptionSet},
MessageHeader, SendRecvFlags, SocketAddr,
},
Socket,
},
Socket,
},
prelude::*,
process::signal::{PollHandle, Pollable, Pollee},
@ -147,6 +150,13 @@ impl Socket for DatagramSocket {
fn connect(&self, socket_addr: SocketAddr) -> Result<()> {
let endpoint = socket_addr.try_into()?;
let can_broadcast = self.options.read().socket.broadcast();
if !can_broadcast && is_broadcast_endpoint(&endpoint) {
return_errno_with_message!(
Errno::EACCES,
"connecting to a broadcast address without SO_BROADCAST is not allowed"
);
}
self.inner.write().connect(&endpoint, &self.pollee)
}
@ -191,6 +201,16 @@ impl Socket for DatagramSocket {
None => None,
};
if let Some(endpoint) = endpoint.as_ref() {
let can_broadcast = self.options.read().socket.broadcast();
if !can_broadcast && is_broadcast_endpoint(endpoint) {
return_errno_with_message!(
Errno::EACCES,
"sending to a broadcast address without SO_BROADCAST is not allowed"
);
}
}
if !control_messages.is_empty() {
// TODO: Support sending control message
warn!("sending control message is not supported");

View File

@ -15,6 +15,7 @@ pub trait SocketOption: Any + Send + Sync + Debug {
impl_socket_options!(
pub struct ReuseAddr(bool);
pub struct Error(Option<crate::error::Error>);
pub struct Broadcast(bool);
pub struct SendBuf(u32);
pub struct RecvBuf(u32);
pub struct KeepAlive(bool);

View File

@ -11,8 +11,8 @@ use crate::{
match_sock_option_mut, match_sock_option_ref,
net::socket::{
options::{
AcceptConn, KeepAlive, Linger, PassCred, PeerCred, PeerGroups, Priority, RecvBuf,
RecvBufForce, ReuseAddr, ReusePort, SendBuf, SendBufForce, SocketOption,
AcceptConn, Broadcast, KeepAlive, Linger, PassCred, PeerCred, PeerGroups, Priority,
RecvBuf, RecvBufForce, ReuseAddr, ReusePort, SendBuf, SendBufForce, SocketOption,
},
unix::{CUserCred, UNIX_DATAGRAM_DEFAULT_BUF_SIZE, UNIX_STREAM_DEFAULT_BUF_SIZE},
},
@ -25,6 +25,7 @@ use crate::{
#[set = "pub"]
pub struct SocketOptionSet {
reuse_addr: bool,
broadcast: bool,
send_buf: u32,
recv_buf: u32,
keep_alive: bool,
@ -38,6 +39,7 @@ impl Default for SocketOptionSet {
fn default() -> Self {
Self {
reuse_addr: false,
broadcast: false,
send_buf: MIN_SENDBUF,
recv_buf: MIN_RECVBUF,
keep_alive: false,
@ -101,6 +103,10 @@ impl SocketOptionSet {
let reuse_addr = self.reuse_addr();
socket_reuse_addr.set(reuse_addr);
},
socket_broadcast: Broadcast => {
let broadcast = self.broadcast();
socket_broadcast.set(broadcast);
},
socket_send_buf: SendBuf => {
let send_buf = self.send_buf();
socket_send_buf.set(send_buf);
@ -169,6 +175,10 @@ impl SocketOptionSet {
self.set_reuse_addr(*reuse_addr);
socket.set_reuse_addr(*reuse_addr);
},
socket_broadcast: Broadcast => {
let broadcast = socket_broadcast.get().unwrap();
self.set_broadcast(*broadcast);
},
socket_send_buf: SendBuf => {
let send_buf = socket_send_buf.get().unwrap();
if *send_buf <= MIN_SENDBUF {

View File

@ -4,8 +4,8 @@ use super::RawSocketOption;
use crate::{
current_userspace, impl_raw_sock_option_get_only, impl_raw_socket_option,
net::socket::options::{
AcceptConn, Error, KeepAlive, Linger, PassCred, PeerCred, PeerGroups, Priority, RecvBuf,
RecvBufForce, ReuseAddr, ReusePort, SendBuf, SendBufForce, SocketOption,
AcceptConn, Broadcast, Error, KeepAlive, Linger, PassCred, PeerCred, PeerGroups, Priority,
RecvBuf, RecvBufForce, ReuseAddr, ReusePort, SendBuf, SendBufForce, SocketOption,
},
prelude::*,
process::Gid,
@ -52,6 +52,7 @@ pub fn new_socket_option(name: i32) -> Result<Box<dyn RawSocketOption>> {
match name {
CSocketOptionName::REUSEADDR => Ok(Box::new(ReuseAddr::new())),
CSocketOptionName::ERROR => Ok(Box::new(Error::new())),
CSocketOptionName::BROADCAST => Ok(Box::new(Broadcast::new())),
CSocketOptionName::SNDBUF => Ok(Box::new(SendBuf::new())),
CSocketOptionName::RCVBUF => Ok(Box::new(RecvBuf::new())),
CSocketOptionName::KEEPALIVE => Ok(Box::new(KeepAlive::new())),
@ -70,6 +71,7 @@ pub fn new_socket_option(name: i32) -> Result<Box<dyn RawSocketOption>> {
impl_raw_socket_option!(ReuseAddr);
impl_raw_sock_option_get_only!(Error);
impl_raw_socket_option!(Broadcast);
impl_raw_socket_option!(SendBuf);
impl_raw_socket_option!(RecvBuf);
impl_raw_socket_option!(KeepAlive);