mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-09 05:16:47 +00:00
Add ip level socket option
This commit is contained in:
parent
ffc7e3612d
commit
388eec449a
@ -3,6 +3,7 @@
|
|||||||
mod addr;
|
mod addr;
|
||||||
mod common;
|
mod common;
|
||||||
pub mod datagram;
|
pub mod datagram;
|
||||||
|
pub mod options;
|
||||||
pub mod stream;
|
pub mod stream;
|
||||||
|
|
||||||
use addr::UNSPECIFIED_LOCAL_ENDPOINT;
|
use addr::UNSPECIFIED_LOCAL_ENDPOINT;
|
||||||
|
108
kernel/src/net/socket/ip/options.rs
Normal file
108
kernel/src/net/socket/ip/options.rs
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
use core::num::NonZeroU8;
|
||||||
|
|
||||||
|
use aster_bigtcp::socket::NeedIfacePoll;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
impl_socket_options, match_sock_option_mut, match_sock_option_ref,
|
||||||
|
net::socket::options::SocketOption, prelude::*,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// IP-level socket options.
|
||||||
|
#[derive(Debug, Clone, Copy, CopyGetters, Setters)]
|
||||||
|
#[get_copy = "pub"]
|
||||||
|
#[set = "pub"]
|
||||||
|
pub(super) struct IpOptionSet {
|
||||||
|
tos: u8,
|
||||||
|
ttl: IpTtl,
|
||||||
|
hdrincl: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
const DEFAULT_TTL: u8 = 64;
|
||||||
|
pub(super) const INET_ECN_MASK: u8 = 3;
|
||||||
|
|
||||||
|
impl IpOptionSet {
|
||||||
|
pub(super) const fn new_tcp() -> 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 => {
|
||||||
|
let tos = self.tos();
|
||||||
|
ip_tos.set(tos as _);
|
||||||
|
},
|
||||||
|
ip_ttl: Ttl => {
|
||||||
|
let ttl = self.ttl();
|
||||||
|
ip_ttl.set(ttl);
|
||||||
|
},
|
||||||
|
ip_hdrincl: Hdrincl => {
|
||||||
|
let hdrincl = self.hdrincl();
|
||||||
|
ip_hdrincl.set(hdrincl);
|
||||||
|
},
|
||||||
|
_ => return_errno_with_message!(Errno::ENOPROTOOPT, "the socket option is unknown")
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn set_option(
|
||||||
|
&mut self,
|
||||||
|
option: &dyn SocketOption,
|
||||||
|
socket: &mut dyn SetIpLevelOption,
|
||||||
|
) -> Result<NeedIfacePoll> {
|
||||||
|
match_sock_option_ref!(option, {
|
||||||
|
ip_tos: Tos => {
|
||||||
|
let old_value = self.tos();
|
||||||
|
let mut val = *ip_tos.get().unwrap() as u8;
|
||||||
|
val &= !INET_ECN_MASK;
|
||||||
|
val |= old_value & INET_ECN_MASK;
|
||||||
|
self.set_tos(val);
|
||||||
|
},
|
||||||
|
ip_ttl: Ttl => {
|
||||||
|
let ttl = ip_ttl.get().unwrap();
|
||||||
|
self.set_ttl(*ttl);
|
||||||
|
},
|
||||||
|
ip_hdrincl: Hdrincl => {
|
||||||
|
let hdrincl = ip_hdrincl.get().unwrap();
|
||||||
|
socket.set_hdrincl(*hdrincl)?;
|
||||||
|
self.set_hdrincl(*hdrincl);
|
||||||
|
},
|
||||||
|
_ => return_errno_with_message!(Errno::ENOPROTOOPT, "the socket option to be set is unknown")
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(NeedIfacePoll::FALSE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_socket_options!(
|
||||||
|
pub struct Tos(i32);
|
||||||
|
pub struct Ttl(IpTtl);
|
||||||
|
pub struct Hdrincl(bool);
|
||||||
|
);
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct IpTtl(Option<NonZeroU8>);
|
||||||
|
|
||||||
|
impl IpTtl {
|
||||||
|
pub const fn new(val: Option<NonZeroU8>) -> Self {
|
||||||
|
Self(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn get(&self) -> u8 {
|
||||||
|
if let Some(val) = self.0 {
|
||||||
|
val.get()
|
||||||
|
} else {
|
||||||
|
DEFAULT_TTL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait SetIpLevelOption {
|
||||||
|
fn set_hdrincl(&self, _hdrincl: bool) -> Result<()>;
|
||||||
|
}
|
@ -18,7 +18,10 @@ use ostd::sync::{PreemptDisabled, RwLockReadGuard, RwLockWriteGuard};
|
|||||||
use takeable::Takeable;
|
use takeable::Takeable;
|
||||||
use util::{Retrans, TcpOptionSet};
|
use util::{Retrans, TcpOptionSet};
|
||||||
|
|
||||||
use super::UNSPECIFIED_LOCAL_ENDPOINT;
|
use super::{
|
||||||
|
options::{IpOptionSet, SetIpLevelOption},
|
||||||
|
UNSPECIFIED_LOCAL_ENDPOINT,
|
||||||
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
events::IoEvents,
|
events::IoEvents,
|
||||||
fs::file_handle::FileLike,
|
fs::file_handle::FileLike,
|
||||||
@ -75,14 +78,16 @@ enum State {
|
|||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct OptionSet {
|
struct OptionSet {
|
||||||
socket: SocketOptionSet,
|
socket: SocketOptionSet,
|
||||||
|
ip: IpOptionSet,
|
||||||
tcp: TcpOptionSet,
|
tcp: TcpOptionSet,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OptionSet {
|
impl OptionSet {
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
let socket = SocketOptionSet::new_tcp();
|
let socket = SocketOptionSet::new_tcp();
|
||||||
|
let ip = IpOptionSet::new_tcp();
|
||||||
let tcp = TcpOptionSet::new();
|
let tcp = TcpOptionSet::new();
|
||||||
OptionSet { socket, tcp }
|
OptionSet { socket, ip, tcp }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn raw(&self) -> RawTcpOption {
|
fn raw(&self) -> RawTcpOption {
|
||||||
@ -582,11 +587,19 @@ impl Socket for StreamSocket {
|
|||||||
|
|
||||||
let options = self.options.read();
|
let options = self.options.read();
|
||||||
|
|
||||||
|
// Deal with socket-level options
|
||||||
match options.socket.get_option(option) {
|
match options.socket.get_option(option) {
|
||||||
Err(err) if err.error() == Errno::ENOPROTOOPT => (),
|
Err(err) if err.error() == Errno::ENOPROTOOPT => (),
|
||||||
res => return res,
|
res => return res,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Deal with IP-level options
|
||||||
|
match options.ip.get_option(option) {
|
||||||
|
Err(err) if err.error() == Errno::ENOPROTOOPT => (),
|
||||||
|
res => return res,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deal with TCP-level options
|
||||||
// FIXME: Here we only return the previously set values, without actually
|
// FIXME: Here we only return the previously set values, without actually
|
||||||
// asking the underlying sockets for the real, effective values.
|
// asking the underlying sockets for the real, effective values.
|
||||||
match_sock_option_mut!(option, {
|
match_sock_option_mut!(option, {
|
||||||
@ -648,9 +661,18 @@ impl Socket for StreamSocket {
|
|||||||
let mut options = self.options.write();
|
let mut options = self.options.write();
|
||||||
let mut state = self.write_updated_state();
|
let mut state = self.write_updated_state();
|
||||||
|
|
||||||
|
// Deal with socket-level options
|
||||||
let need_iface_poll = match options.socket.set_option(option, state.as_mut()) {
|
let need_iface_poll = match options.socket.set_option(option, state.as_mut()) {
|
||||||
Err(err) if err.error() == Errno::ENOPROTOOPT => {
|
Err(err) if err.error() == Errno::ENOPROTOOPT => {
|
||||||
do_tcp_setsockopt(option, &mut options, state.as_mut())?
|
// Deal with IP-level options
|
||||||
|
match options.ip.set_option(option, state.as_mut()) {
|
||||||
|
Err(err) if err.error() == Errno::ENOPROTOOPT => {
|
||||||
|
// Deal with TCP-level options
|
||||||
|
do_tcp_setsockopt(option, &mut options, state.as_mut())?
|
||||||
|
}
|
||||||
|
Err(err) => return Err(err),
|
||||||
|
Ok(need_iface_poll) => need_iface_poll,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Err(err) => return Err(err),
|
Err(err) => return Err(err),
|
||||||
Ok(need_iface_poll) => need_iface_poll,
|
Ok(need_iface_poll) => need_iface_poll,
|
||||||
@ -790,6 +812,15 @@ impl SetSocketLevelOption for State {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl SetIpLevelOption for State {
|
||||||
|
fn set_hdrincl(&self, _hdrincl: bool) -> Result<()> {
|
||||||
|
return_errno_with_message!(
|
||||||
|
Errno::ENOPROTOOPT,
|
||||||
|
"IP_HDRINCL cannot be set on TCP sockets"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Drop for StreamSocket {
|
impl Drop for StreamSocket {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
let state = self.state.get_mut().take();
|
let state = self.state.get_mut().take();
|
||||||
|
81
kernel/src/util/net/options/ip.rs
Normal file
81
kernel/src/util/net/options/ip.rs
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
use int_to_c_enum::TryFromInt;
|
||||||
|
|
||||||
|
use super::RawSocketOption;
|
||||||
|
use crate::{
|
||||||
|
impl_raw_socket_option,
|
||||||
|
net::socket::ip::options::{Hdrincl, Tos, Ttl},
|
||||||
|
prelude::*,
|
||||||
|
util::net::options::SocketOption,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Socket options for IP socket.
|
||||||
|
///
|
||||||
|
/// The raw definitions can be found at:
|
||||||
|
/// https://elixir.bootlin.com/linux/v6.0.19/source/include/uapi/linux/in.h#L94
|
||||||
|
#[repr(i32)]
|
||||||
|
#[derive(Debug, Clone, Copy, TryFromInt)]
|
||||||
|
#[expect(non_camel_case_types)]
|
||||||
|
#[expect(clippy::upper_case_acronyms)]
|
||||||
|
pub enum CIpOptionName {
|
||||||
|
TOS = 1,
|
||||||
|
TTL = 2,
|
||||||
|
HDRINCL = 3,
|
||||||
|
OPTIONS = 4,
|
||||||
|
ROUTER_ALERT = 5,
|
||||||
|
RECVOPTS = 6,
|
||||||
|
RETOPTS = 7,
|
||||||
|
PKTINFO = 8,
|
||||||
|
PKTOPTIONS = 9,
|
||||||
|
MTU_DISCOVER = 10,
|
||||||
|
RECVERR = 11,
|
||||||
|
RECVTTL = 12,
|
||||||
|
RECVTOS = 13,
|
||||||
|
MTU = 14,
|
||||||
|
FREEBIND = 15,
|
||||||
|
IPSEC_POLICY = 16,
|
||||||
|
XFRM_POLICY = 17,
|
||||||
|
PASSSEC = 18,
|
||||||
|
TRANSPARENT = 19,
|
||||||
|
ORIGDSTADDR = 20,
|
||||||
|
MINTTL = 21,
|
||||||
|
NODEFRAG = 22,
|
||||||
|
CHECKSUM = 23,
|
||||||
|
BIND_ADDRESS_NO_PORT = 24,
|
||||||
|
RECVFRAGSIZE = 25,
|
||||||
|
RECVERR_RFC4884 = 26,
|
||||||
|
MULTICAST_IF = 32,
|
||||||
|
MULTICAST_TTL = 33,
|
||||||
|
MULTICAST_LOOP = 34,
|
||||||
|
ADD_MEMBERSHIP = 35,
|
||||||
|
DROP_MEMBERSHIP = 36,
|
||||||
|
UNBLOCK_SOURCE = 37,
|
||||||
|
BLOCK_SOURCE = 38,
|
||||||
|
ADD_SOURCE_MEMBERSHIP = 39,
|
||||||
|
DROP_SOURCE_MEMBERSHIP = 40,
|
||||||
|
MSFILTER = 41,
|
||||||
|
MCAST_JOIN_GROUP = 42,
|
||||||
|
MCAST_BLOCK_SOURCE = 43,
|
||||||
|
MCAST_UNBLOCK_SOURCE = 44,
|
||||||
|
MCAST_LEAVE_GROUP = 45,
|
||||||
|
MCAST_JOIN_SOURCE_GROUP = 46,
|
||||||
|
MCAST_LEAVE_SOURCE_GROUP = 47,
|
||||||
|
MCAST_MSFILTER = 48,
|
||||||
|
MULTICAST_ALL = 49,
|
||||||
|
UNICAST_IF = 50,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_ip_option(name: i32) -> Result<Box<dyn RawSocketOption>> {
|
||||||
|
let name = CIpOptionName::try_from(name).map_err(|_| Errno::ENOPROTOOPT)?;
|
||||||
|
match name {
|
||||||
|
CIpOptionName::TOS => Ok(Box::new(Tos::new())),
|
||||||
|
CIpOptionName::TTL => Ok(Box::new(Ttl::new())),
|
||||||
|
CIpOptionName::HDRINCL => Ok(Box::new(Hdrincl::new())),
|
||||||
|
_ => return_errno_with_message!(Errno::ENOPROTOOPT, "unsupported ip level option"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_raw_socket_option!(Ttl);
|
||||||
|
impl_raw_socket_option!(Tos);
|
||||||
|
impl_raw_socket_option!(Hdrincl);
|
@ -51,8 +51,11 @@
|
|||||||
//! At the syscall level, the interface is unified for all options and does not need to be modified.
|
//! At the syscall level, the interface is unified for all options and does not need to be modified.
|
||||||
//!
|
//!
|
||||||
|
|
||||||
|
use ip::new_ip_option;
|
||||||
|
|
||||||
use crate::{net::socket::options::SocketOption, prelude::*};
|
use crate::{net::socket::options::SocketOption, prelude::*};
|
||||||
|
|
||||||
|
mod ip;
|
||||||
mod socket;
|
mod socket;
|
||||||
mod tcp;
|
mod tcp;
|
||||||
mod utils;
|
mod utils;
|
||||||
@ -133,6 +136,7 @@ pub fn new_raw_socket_option(
|
|||||||
) -> Result<Box<dyn RawSocketOption>> {
|
) -> Result<Box<dyn RawSocketOption>> {
|
||||||
match level {
|
match level {
|
||||||
CSocketOptionLevel::SOL_SOCKET => new_socket_option(name),
|
CSocketOptionLevel::SOL_SOCKET => new_socket_option(name),
|
||||||
|
CSocketOptionLevel::SOL_IP => new_ip_option(name),
|
||||||
CSocketOptionLevel::SOL_TCP => new_tcp_option(name),
|
CSocketOptionLevel::SOL_TCP => new_tcp_option(name),
|
||||||
_ => return_errno_with_message!(Errno::EOPNOTSUPP, "unsupported option level"),
|
_ => return_errno_with_message!(Errno::EOPNOTSUPP, "unsupported option level"),
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
// SPDX-License-Identifier: MPL-2.0
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
use core::time::Duration;
|
use core::{num::NonZeroU8, time::Duration};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
current_userspace,
|
current_userspace,
|
||||||
net::socket::{ip::stream::CongestionControl, LingerOption},
|
net::socket::{
|
||||||
|
ip::{options::IpTtl, stream::CongestionControl},
|
||||||
|
LingerOption,
|
||||||
|
},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -94,6 +97,27 @@ impl WriteToUser for u8 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ReadFromUser for IpTtl {
|
||||||
|
fn read_from_user(addr: Vaddr, max_len: u32) -> Result<Self> {
|
||||||
|
let val = i32::read_from_user(addr, max_len)?;
|
||||||
|
|
||||||
|
let ttl_value = match val {
|
||||||
|
-1 => None,
|
||||||
|
1..255 => Some(NonZeroU8::new(val as u8).unwrap()),
|
||||||
|
_ => return_errno_with_message!(Errno::EINVAL, "invalid ttl value"),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(IpTtl::new(ttl_value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WriteToUser for IpTtl {
|
||||||
|
fn write_to_user(&self, addr: Vaddr, max_len: u32) -> Result<usize> {
|
||||||
|
let val = self.get() as i32;
|
||||||
|
val.write_to_user(addr, max_len)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl WriteToUser for Option<Error> {
|
impl WriteToUser for Option<Error> {
|
||||||
fn write_to_user(&self, addr: Vaddr, max_len: u32) -> Result<usize> {
|
fn write_to_user(&self, addr: Vaddr, max_len: u32) -> Result<usize> {
|
||||||
let write_len = core::mem::size_of::<i32>();
|
let write_len = core::mem::size_of::<i32>();
|
||||||
|
@ -55,6 +55,10 @@ FN_TEST(invalid_socket_option)
|
|||||||
TEST_ERRNO(getsockopt(sk_connected, IPPROTO_TCP, INVALID_TCP_OPTION,
|
TEST_ERRNO(getsockopt(sk_connected, IPPROTO_TCP, INVALID_TCP_OPTION,
|
||||||
&res, &res_len),
|
&res, &res_len),
|
||||||
ENOPROTOOPT);
|
ENOPROTOOPT);
|
||||||
|
#define INVALID_IP_OPTION 99999
|
||||||
|
TEST_ERRNO(getsockopt(sk_connected, IPPROTO_IP, INVALID_IP_OPTION, &res,
|
||||||
|
&res_len),
|
||||||
|
ENOPROTOOPT);
|
||||||
}
|
}
|
||||||
END_TEST()
|
END_TEST()
|
||||||
|
|
||||||
@ -274,3 +278,85 @@ FN_TEST(keepidle)
|
|||||||
keepidle == 200);
|
keepidle == 200);
|
||||||
}
|
}
|
||||||
END_TEST()
|
END_TEST()
|
||||||
|
|
||||||
|
FN_TEST(ip_tos)
|
||||||
|
{
|
||||||
|
int tos;
|
||||||
|
socklen_t tos_len = sizeof(tos);
|
||||||
|
|
||||||
|
// 1. Check default value
|
||||||
|
TEST_RES(getsockopt(sk_unbound, 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));
|
||||||
|
tos = 0;
|
||||||
|
TEST_RES(getsockopt(sk_unbound, 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));
|
||||||
|
tos = 0;
|
||||||
|
TEST_RES(getsockopt(sk_unbound, IPPROTO_IP, IP_TOS, &tos, &tos_len),
|
||||||
|
tos == 32 && tos_len == 4);
|
||||||
|
|
||||||
|
tos = 0x1111;
|
||||||
|
CHECK(setsockopt(sk_unbound, IPPROTO_IP, IP_TOS, &tos, tos_len));
|
||||||
|
tos = 0;
|
||||||
|
TEST_RES(getsockopt(sk_unbound, IPPROTO_IP, IP_TOS, &tos, &tos_len),
|
||||||
|
tos == 16 && tos_len == 4);
|
||||||
|
}
|
||||||
|
END_TEST()
|
||||||
|
|
||||||
|
FN_TEST(ip_ttl)
|
||||||
|
{
|
||||||
|
int ttl;
|
||||||
|
socklen_t ttl_len = sizeof(ttl);
|
||||||
|
|
||||||
|
// 1. Check default value
|
||||||
|
TEST_RES(getsockopt(sk_unbound, IPPROTO_IP, IP_TTL, &ttl, &ttl_len),
|
||||||
|
ttl == 64 && ttl_len == 4);
|
||||||
|
|
||||||
|
// 2. Set and get value
|
||||||
|
ttl = 0x0;
|
||||||
|
TEST_ERRNO(setsockopt(sk_unbound, IPPROTO_IP, IP_TTL, &ttl, ttl_len),
|
||||||
|
EINVAL);
|
||||||
|
|
||||||
|
ttl = 0x100;
|
||||||
|
TEST_ERRNO(setsockopt(sk_unbound, IPPROTO_IP, IP_TTL, &ttl, ttl_len),
|
||||||
|
EINVAL);
|
||||||
|
|
||||||
|
ttl = 0x10;
|
||||||
|
CHECK(setsockopt(sk_unbound, 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);
|
||||||
|
|
||||||
|
ttl = -1;
|
||||||
|
CHECK(setsockopt(sk_unbound, IPPROTO_IP, IP_TTL, &ttl, ttl_len));
|
||||||
|
|
||||||
|
ttl = 0;
|
||||||
|
TEST_RES(getsockopt(sk_unbound, IPPROTO_IP, IP_TTL, &ttl, &ttl_len),
|
||||||
|
ttl == 64 && ttl_len == 4);
|
||||||
|
}
|
||||||
|
END_TEST()
|
||||||
|
|
||||||
|
FN_TEST(ip_hdrincl)
|
||||||
|
{
|
||||||
|
int hdrincl;
|
||||||
|
socklen_t hdrincl_len = sizeof(hdrincl);
|
||||||
|
|
||||||
|
// 1. Check default value
|
||||||
|
TEST_RES(getsockopt(sk_unbound, IPPROTO_IP, IP_HDRINCL, &hdrincl,
|
||||||
|
&hdrincl_len),
|
||||||
|
hdrincl == 0 && hdrincl_len == 4);
|
||||||
|
|
||||||
|
// 2. Set value
|
||||||
|
hdrincl = 0x10;
|
||||||
|
TEST_ERRNO(setsockopt(sk_unbound, IPPROTO_IP, IP_HDRINCL, &hdrincl,
|
||||||
|
hdrincl_len),
|
||||||
|
ENOPROTOOPT);
|
||||||
|
}
|
||||||
|
END_TEST()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user