diff --git a/kernel/libs/aster-bigtcp/src/iface/iface.rs b/kernel/libs/aster-bigtcp/src/iface/iface.rs index 891ef25eb..b5bb48dcd 100644 --- a/kernel/libs/aster-bigtcp/src/iface/iface.rs +++ b/kernel/libs/aster-bigtcp/src/iface/iface.rs @@ -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 dyn Iface { self.common().prefix_len() } + /// Gets the broadcast address of the iface, if any. + pub fn broadcast_addr(&self) -> Option { + 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() diff --git a/kernel/src/net/iface/broadcast.rs b/kernel/src/net/iface/broadcast.rs new file mode 100644 index 000000000..5207cf1d7 --- /dev/null +++ b/kernel/src/net/iface/broadcast.rs @@ -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> = 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) +} diff --git a/kernel/src/net/iface/init.rs b/kernel/src/net/iface/init.rs index ab7d29ab1..de6f29329 100644 --- a/kernel/src/net/iface/init.rs +++ b/kernel/src/net/iface/init.rs @@ -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>> = Once::new(); @@ -51,6 +54,8 @@ pub fn init() { aster_network::register_send_callback(VIRTIO_DEVICE_NAME, callback); } + broadcast::init(); + poll_ifaces(); } diff --git a/kernel/src/net/iface/mod.rs b/kernel/src/net/iface/mod.rs index e30be2c6c..b86487caf 100644 --- a/kernel/src/net/iface/mod.rs +++ b/kernel/src/net/iface/mod.rs @@ -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; diff --git a/kernel/src/net/socket/ip/datagram/mod.rs b/kernel/src/net/socket/ip/datagram/mod.rs index 7af8d6ed7..229c924ac 100644 --- a/kernel/src/net/socket/ip/datagram/mod.rs +++ b/kernel/src/net/socket/ip/datagram/mod.rs @@ -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"); diff --git a/kernel/src/net/socket/options/mod.rs b/kernel/src/net/socket/options/mod.rs index 1d8d7ce5c..323c7c029 100644 --- a/kernel/src/net/socket/options/mod.rs +++ b/kernel/src/net/socket/options/mod.rs @@ -15,6 +15,7 @@ pub trait SocketOption: Any + Send + Sync + Debug { impl_socket_options!( pub struct ReuseAddr(bool); pub struct Error(Option); + pub struct Broadcast(bool); pub struct SendBuf(u32); pub struct RecvBuf(u32); pub struct KeepAlive(bool); diff --git a/kernel/src/net/socket/util/options.rs b/kernel/src/net/socket/util/options.rs index 711d2f98f..84e7953e8 100644 --- a/kernel/src/net/socket/util/options.rs +++ b/kernel/src/net/socket/util/options.rs @@ -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 { diff --git a/kernel/src/util/net/options/socket.rs b/kernel/src/util/net/options/socket.rs index aa56268ad..5eb84f03d 100644 --- a/kernel/src/util/net/options/socket.rs +++ b/kernel/src/util/net/options/socket.rs @@ -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> { 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> { 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);