Add IP options to UDP socket

This commit is contained in:
jiangjianfeng 2025-10-31 09:31:55 +00:00 committed by Ruihan Li
parent e796c41546
commit 1375346214
3 changed files with 74 additions and 21 deletions

View File

@ -12,6 +12,7 @@ use crate::{
fs::utils::Inode,
match_sock_option_mut,
net::socket::{
ip::options::{IpOptionSet, SetIpLevelOption},
new_pseudo_inode,
options::{Error as SocketError, SocketOption},
private::SocketPrivate,
@ -44,13 +45,15 @@ pub struct DatagramSocket {
#[derive(Debug, Clone)]
struct OptionSet {
socket: SocketOptionSet,
ip: IpOptionSet,
// TODO: UDP option set
}
impl OptionSet {
fn new() -> Self {
let socket = SocketOptionSet::new_udp();
OptionSet { socket }
let ip = IpOptionSet::new_udp();
OptionSet { socket, ip }
}
}
@ -228,33 +231,47 @@ impl Socket for DatagramSocket {
});
let inner = self.inner.read();
self.options.read().socket.get_option(option, &*inner)
let options = self.options.read();
// Deal with socket-level options
match options.socket.get_option(option, &*inner) {
Err(err) if err.error() == Errno::ENOPROTOOPT => (),
res => return res,
}
// Deal with IP-level options
options.ip.get_option(option)
}
fn set_option(&self, option: &dyn SocketOption) -> Result<()> {
let inner = self.inner.read();
let mut options = self.options.write();
match options.socket.set_option(option, &*inner) {
Err(e) => Err(e),
Ok(need_iface_poll) => {
let iface_to_poll = need_iface_poll
.then(|| match &*inner {
Inner::Unbound(_) => None,
Inner::Bound(bound_datagram) => Some(bound_datagram.iface().clone()),
})
.flatten();
drop(inner);
drop(options);
if let Some(iface) = iface_to_poll {
iface.poll();
}
Ok(())
// Deal with socket-level options
let need_iface_poll = match options.socket.set_option(option, &*inner) {
Err(err) if err.error() == Errno::ENOPROTOOPT => {
// Deal with IP-level options
options.ip.set_option(option, &*inner)?
}
Err(err) => return Err(err),
Ok(need_iface_poll) => need_iface_poll,
};
let iface_to_poll = need_iface_poll
.then(|| match &*inner {
Inner::Unbound(_) => None,
Inner::Bound(bound_datagram) => Some(bound_datagram.iface().clone()),
})
.flatten();
drop(inner);
drop(options);
if let Some(iface) = iface_to_poll {
iface.poll();
}
Ok(())
}
fn pseudo_inode(&self) -> &Arc<dyn Inode> {
@ -277,3 +294,12 @@ impl SetSocketLevelOption for Inner<UnboundDatagram, BoundDatagram> {
bound.bound_port().set_can_reuse(reuse_addr);
}
}
impl SetIpLevelOption for Inner<UnboundDatagram, BoundDatagram> {
fn set_hdrincl(&self, _hdrincl: bool) -> Result<()> {
return_errno_with_message!(
Errno::ENOPROTOOPT,
"IP_HDRINCL cannot be set on UDP sockets"
);
}
}

View File

@ -31,6 +31,14 @@ impl IpOptionSet {
}
}
pub(super) const fn new_udp() -> Self {
Self {
tos: 0,
ttl: IpTtl(None),
hdrincl: false,
}
}
pub(super) fn get_option(&self, option: &mut dyn SocketOption) -> Result<()> {
match_sock_option_mut!(option, {
ip_tos: Tos => {

View File

@ -13,6 +13,7 @@ int sk_unbound;
int sk_listen;
int sk_connected;
int sk_accepted;
int sk_udp;
struct sockaddr_in listen_addr;
#define LISTEN_PORT htons(0x1242)
@ -35,6 +36,8 @@ FN_SETUP(general)
sizeof(listen_addr)));
sk_accepted = CHECK(accept(sk_listen, NULL, NULL));
sk_udp = CHECK(socket(AF_INET, SOCK_DGRAM, 0));
}
END_SETUP()
@ -287,13 +290,18 @@ FN_TEST(ip_tos)
// 1. Check default value
TEST_RES(getsockopt(sk_unbound, IPPROTO_IP, IP_TOS, &tos, &tos_len),
tos == 0 && tos_len == 4);
TEST_RES(getsockopt(sk_udp, IPPROTO_IP, IP_TOS, &tos, &tos_len),
tos == 0 && tos_len == 4);
// 2. Set and get value
tos = 0x10;
CHECK(setsockopt(sk_unbound, IPPROTO_IP, IP_TOS, &tos, tos_len));
CHECK(setsockopt(sk_udp, IPPROTO_IP, IP_TOS, &tos, tos_len));
tos = 0;
TEST_RES(getsockopt(sk_unbound, IPPROTO_IP, IP_TOS, &tos, &tos_len),
tos == 0x10 && tos_len == 4);
TEST_RES(getsockopt(sk_udp, IPPROTO_IP, IP_TOS, &tos, &tos_len),
tos == 0x10 && tos_len == 4);
tos = 0x123;
CHECK(setsockopt(sk_unbound, IPPROTO_IP, IP_TOS, &tos, tos_len));
@ -317,6 +325,8 @@ FN_TEST(ip_ttl)
// 1. Check default value
TEST_RES(getsockopt(sk_unbound, IPPROTO_IP, IP_TTL, &ttl, &ttl_len),
ttl == 64 && ttl_len == 4);
TEST_RES(getsockopt(sk_udp, IPPROTO_IP, IP_TTL, &ttl, &ttl_len),
ttl == 64 && ttl_len == 4);
// 2. Set and get value
ttl = 0x0;
@ -329,10 +339,13 @@ FN_TEST(ip_ttl)
ttl = 0x10;
CHECK(setsockopt(sk_unbound, IPPROTO_IP, IP_TTL, &ttl, ttl_len));
CHECK(setsockopt(sk_udp, IPPROTO_IP, IP_TTL, &ttl, ttl_len));
ttl = 0;
TEST_RES(getsockopt(sk_unbound, IPPROTO_IP, IP_TTL, &ttl, &ttl_len),
ttl == 0x10 && ttl_len == 4);
TEST_RES(getsockopt(sk_udp, IPPROTO_IP, IP_TTL, &ttl, &ttl_len),
ttl == 0x10 && ttl_len == 4);
ttl = -1;
CHECK(setsockopt(sk_unbound, IPPROTO_IP, IP_TTL, &ttl, ttl_len));
@ -352,11 +365,17 @@ FN_TEST(ip_hdrincl)
TEST_RES(getsockopt(sk_unbound, IPPROTO_IP, IP_HDRINCL, &hdrincl,
&hdrincl_len),
hdrincl == 0 && hdrincl_len == 4);
TEST_RES(getsockopt(sk_udp, IPPROTO_IP, IP_HDRINCL, &hdrincl,
&hdrincl_len),
hdrincl == 0 && hdrincl_len == 4);
// 2. Set value
// 2. Set and get value
hdrincl = 0x10;
TEST_ERRNO(setsockopt(sk_unbound, IPPROTO_IP, IP_HDRINCL, &hdrincl,
hdrincl_len),
ENOPROTOOPT);
TEST_ERRNO(setsockopt(sk_udp, IPPROTO_IP, IP_HDRINCL, &hdrincl,
hdrincl_len),
ENOPROTOOPT);
}
END_TEST()