Support SO_BROADCAST for UDP sockets
This commit is contained in:
parent
66b3dcc388
commit
188b20eb99
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Reference in New Issue