Skip to content

Commit a6d6587

Browse files
Add support for most DCCP socket options
Not all socket options are added. Most notably DCCP_SOCKOPT_QPOLICY_ID could not be added since some constants needed for it aren't available in the libc crate. Namely DCCPQ_POLICY_SIMPLE, DCCPQ_POLICY_PRIO and DCCP_SCM_PRIORITY. Co-authored-by: Thomas de Zeeuw <[email protected]>
1 parent 017455c commit a6d6587

File tree

3 files changed

+305
-0
lines changed

3 files changed

+305
-0
lines changed

src/lib.rs

+12
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,13 @@ impl Type {
257257
/// Used for protocols such as UDP.
258258
pub const DGRAM: Type = Type(sys::SOCK_DGRAM);
259259

260+
/// Type corresponding to `SOCK_DCCP`.
261+
///
262+
/// Used for the DCCP protocol.
263+
#[cfg(all(feature = "all", target_os = "linux"))]
264+
#[cfg_attr(docsrs, doc(cfg(feature = "all", target_os = "linux")))]
265+
pub const DCCP: Type = Type(sys::SOCK_DCCP);
266+
260267
/// Type corresponding to `SOCK_SEQPACKET`.
261268
#[cfg(feature = "all")]
262269
#[cfg_attr(docsrs, doc(cfg(feature = "all")))]
@@ -307,6 +314,11 @@ impl Protocol {
307314
/// Protocol corresponding to `MPTCP`.
308315
pub const MPTCP: Protocol = Protocol(sys::IPPROTO_MPTCP);
309316

317+
/// Protocol corresponding to `DCCP`.
318+
#[cfg(all(feature = "all", target_os = "linux"))]
319+
#[cfg_attr(docsrs, doc(cfg(feature = "all", target_os = "linux")))]
320+
pub const DCCP: Protocol = Protocol(sys::IPPROTO_DCCP);
321+
310322
#[cfg(all(feature = "all", any(target_os = "freebsd", target_os = "linux")))]
311323
/// Protocol corresponding to `SCTP`.
312324
pub const SCTP: Protocol = Protocol(sys::IPPROTO_SCTP);

src/sys/unix.rs

+260
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,16 @@ pub(crate) use libc::c_int;
5757
// Used in `Domain`.
5858
pub(crate) use libc::{AF_INET, AF_INET6, AF_UNIX};
5959
// Used in `Type`.
60+
#[cfg(all(feature = "all", target_os = "linux"))]
61+
pub(crate) use libc::SOCK_DCCP;
6062
#[cfg(all(feature = "all", not(target_os = "redox")))]
6163
pub(crate) use libc::SOCK_RAW;
6264
#[cfg(feature = "all")]
6365
pub(crate) use libc::SOCK_SEQPACKET;
6466
pub(crate) use libc::{SOCK_DGRAM, SOCK_STREAM};
6567
// Used in `Protocol`.
68+
#[cfg(all(feature = "all", target_os = "linux"))]
69+
pub(crate) use libc::IPPROTO_DCCP;
6670
#[cfg(target_os = "linux")]
6771
pub(crate) use libc::IPPROTO_MPTCP;
6872
#[cfg(all(feature = "all", any(target_os = "freebsd", target_os = "linux")))]
@@ -382,6 +386,8 @@ impl_debug!(
382386
Type,
383387
libc::SOCK_STREAM,
384388
libc::SOCK_DGRAM,
389+
#[cfg(all(feature = "all", target_os = "linux"))]
390+
libc::SOCK_DCCP,
385391
#[cfg(not(target_os = "redox"))]
386392
libc::SOCK_RAW,
387393
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
@@ -419,6 +425,8 @@ impl_debug!(
419425
libc::IPPROTO_UDP,
420426
#[cfg(target_os = "linux")]
421427
libc::IPPROTO_MPTCP,
428+
#[cfg(all(feature = "all", target_os = "linux"))]
429+
libc::IPPROTO_DCCP,
422430
#[cfg(all(feature = "all", any(target_os = "freebsd", target_os = "linux")))]
423431
libc::IPPROTO_SCTP,
424432
);
@@ -2213,6 +2221,258 @@ impl crate::Socket {
22132221
))
22142222
.map(|_| ())
22152223
}
2224+
2225+
/// Set value for the `DCCP_SOCKOPT_SERVICE` option on this socket.
2226+
///
2227+
/// Sets the DCCP service. The specification mandates use of service codes.
2228+
/// If this socket option is not set, the socket will fall back to 0 (which
2229+
/// means that no meaningful service code is present). On active sockets
2230+
/// this is set before [`connect`]. On passive sockets up to 32 service
2231+
/// codes can be set before calling [`bind`]
2232+
///
2233+
/// [`connect`]: crate::Socket::connect
2234+
/// [`bind`]: crate::Socket::bind
2235+
#[cfg(all(feature = "all", target_os = "linux"))]
2236+
#[cfg_attr(docsrs, doc(cfg(feature = "all", target_os = "linux")))]
2237+
pub fn set_dccp_service(&self, code: u32) -> io::Result<()> {
2238+
unsafe {
2239+
setsockopt(
2240+
self.as_raw(),
2241+
libc::SOL_DCCP,
2242+
libc::DCCP_SOCKOPT_SERVICE,
2243+
code,
2244+
)
2245+
}
2246+
}
2247+
2248+
/// Get the value of the `DCCP_SOCKOPT_SERVICE` option on this socket.
2249+
///
2250+
/// For more information about this option see [`set_dccp_service`]
2251+
///
2252+
/// [`set_dccp_service`]: crate::Socket::set_dccp_service
2253+
#[cfg(all(feature = "all", target_os = "linux"))]
2254+
#[cfg_attr(docsrs, doc(cfg(feature = "all", target_os = "linux")))]
2255+
pub fn dccp_service(&self) -> io::Result<u32> {
2256+
unsafe { getsockopt(self.as_raw(), libc::SOL_DCCP, libc::DCCP_SOCKOPT_SERVICE) }
2257+
}
2258+
2259+
/// Set value for the `DCCP_SOCKOPT_CCID` option on this socket.
2260+
///
2261+
/// This option sets both the TX and RX CCIDs at the same time.
2262+
#[cfg(all(feature = "all", target_os = "linux"))]
2263+
#[cfg_attr(docsrs, doc(cfg(feature = "all", target_os = "linux")))]
2264+
pub fn set_dccp_ccid(&self, ccid: u8) -> io::Result<()> {
2265+
unsafe { setsockopt(self.as_raw(), libc::SOL_DCCP, libc::DCCP_SOCKOPT_CCID, ccid) }
2266+
}
2267+
2268+
/// Get the value of the `DCCP_SOCKOPT_TX_CCID` option on this socket.
2269+
///
2270+
/// For more information about this option see [`set_dccp_ccid`].
2271+
///
2272+
/// [`set_dccp_ccid`]: crate::Socket::set_dccp_ccid
2273+
#[cfg(all(feature = "all", target_os = "linux"))]
2274+
#[cfg_attr(docsrs, doc(cfg(feature = "all", target_os = "linux")))]
2275+
pub fn dccp_tx_ccid(&self) -> io::Result<u32> {
2276+
unsafe { getsockopt(self.as_raw(), libc::SOL_DCCP, libc::DCCP_SOCKOPT_TX_CCID) }
2277+
}
2278+
2279+
/// Get the value of the `DCCP_SOCKOPT_RX_CCID` option on this socket.
2280+
///
2281+
/// For more information about this option see [`set_dccp_ccid`].
2282+
///
2283+
/// [`set_dccp_ccid`]: crate::Socket::set_dccp_ccid
2284+
#[cfg(all(feature = "all", target_os = "linux"))]
2285+
#[cfg_attr(docsrs, doc(cfg(feature = "all", target_os = "linux")))]
2286+
pub fn dccp_xx_ccid(&self) -> io::Result<u32> {
2287+
unsafe { getsockopt(self.as_raw(), libc::SOL_DCCP, libc::DCCP_SOCKOPT_RX_CCID) }
2288+
}
2289+
2290+
/// Set value for the `DCCP_SOCKOPT_SERVER_TIMEWAIT` option on this socket.
2291+
///
2292+
/// Enables a listening socket to hold timewait state when closing the
2293+
/// connection. This option must be set after `accept` returns.
2294+
#[cfg(all(feature = "all", target_os = "linux"))]
2295+
#[cfg_attr(docsrs, doc(cfg(feature = "all", target_os = "linux")))]
2296+
pub fn set_dccp_server_timewait(&self, hold_timewait: bool) -> io::Result<()> {
2297+
unsafe {
2298+
setsockopt(
2299+
self.as_raw(),
2300+
libc::SOL_DCCP,
2301+
libc::DCCP_SOCKOPT_SERVER_TIMEWAIT,
2302+
hold_timewait as c_int,
2303+
)
2304+
}
2305+
}
2306+
2307+
/// Get the value of the `DCCP_SOCKOPT_SERVER_TIMEWAIT` option on this socket.
2308+
///
2309+
/// For more information see [`set_dccp_server_timewait`]
2310+
///
2311+
/// [`set_dccp_server_timewait`]: crate::Socket::set_dccp_server_timewait
2312+
#[cfg(all(feature = "all", target_os = "linux"))]
2313+
#[cfg_attr(docsrs, doc(cfg(feature = "all", target_os = "linux")))]
2314+
pub fn dccp_server_timewait(&self) -> io::Result<bool> {
2315+
unsafe {
2316+
getsockopt(
2317+
self.as_raw(),
2318+
libc::SOL_DCCP,
2319+
libc::DCCP_SOCKOPT_SERVER_TIMEWAIT,
2320+
)
2321+
}
2322+
}
2323+
2324+
/// Set value for the `DCCP_SOCKOPT_SEND_CSCOV` option on this socket.
2325+
///
2326+
/// Both this option and `DCCP_SOCKOPT_RECV_CSCOV` are used for setting the
2327+
/// partial checksum coverage. The default is that checksums always cover
2328+
/// the entire packet and that only fully covered application data is
2329+
/// accepted by the receiver. Hence, when using this feature on the sender,
2330+
/// it must be enabled at the receiver too, with suitable choice of CsCov.
2331+
#[cfg(all(feature = "all", target_os = "linux"))]
2332+
#[cfg_attr(docsrs, doc(cfg(feature = "all", target_os = "linux")))]
2333+
pub fn set_dccp_send_cscov(&self, level: u32) -> io::Result<()> {
2334+
unsafe {
2335+
setsockopt(
2336+
self.as_raw(),
2337+
libc::SOL_DCCP,
2338+
libc::DCCP_SOCKOPT_SEND_CSCOV,
2339+
level,
2340+
)
2341+
}
2342+
}
2343+
2344+
/// Get the value of the `DCCP_SOCKOPT_SEND_CSCOV` option on this socket.
2345+
///
2346+
/// For more information on this option see [`set_dccp_send_cscov`].
2347+
///
2348+
/// [`set_dccp_send_cscov`]: crate::Socket::set_dccp_send_cscov
2349+
#[cfg(all(feature = "all", target_os = "linux"))]
2350+
#[cfg_attr(docsrs, doc(cfg(feature = "all", target_os = "linux")))]
2351+
pub fn dccp_send_cscov(&self) -> io::Result<u32> {
2352+
unsafe { getsockopt(self.as_raw(), libc::SOL_DCCP, libc::DCCP_SOCKOPT_SEND_CSCOV) }
2353+
}
2354+
2355+
/// Set the value of the `DCCP_SOCKOPT_RECV_CSCOV` option on this socket.
2356+
///
2357+
/// This option is only useful when combined with [`set_dccp_send_cscov`].
2358+
///
2359+
/// [`set_dccp_send_cscov`]: crate::Socket::set_dccp_send_cscov
2360+
#[cfg(all(feature = "all", target_os = "linux"))]
2361+
#[cfg_attr(docsrs, doc(cfg(feature = "all", target_os = "linux")))]
2362+
pub fn set_dccp_recv_cscov(&self, level: u32) -> io::Result<()> {
2363+
unsafe {
2364+
setsockopt(
2365+
self.as_raw(),
2366+
libc::SOL_DCCP,
2367+
libc::DCCP_SOCKOPT_RECV_CSCOV,
2368+
level,
2369+
)
2370+
}
2371+
}
2372+
2373+
/// Get the value of the `DCCP_SOCKOPT_RECV_CSCOV` option on this socket.
2374+
///
2375+
/// For more information on this option see [`set_dccp_recv_cscov`].
2376+
///
2377+
/// [`set_dccp_recv_cscov`]: crate::Socket::set_dccp_recv_cscov
2378+
#[cfg(all(feature = "all", target_os = "linux"))]
2379+
#[cfg_attr(docsrs, doc(cfg(feature = "all", target_os = "linux")))]
2380+
pub fn dccp_recv_cscov(&self) -> io::Result<u32> {
2381+
unsafe { getsockopt(self.as_raw(), libc::SOL_DCCP, libc::DCCP_SOCKOPT_RECV_CSCOV) }
2382+
}
2383+
2384+
/// Set value for the `DCCP_SOCKOPT_QPOLICY_TXQLEN` option on this socket.
2385+
///
2386+
/// This option sets the maximum length of the output queue. A zero value is
2387+
/// interpreted as unbounded queue length.
2388+
#[cfg(all(feature = "all", target_os = "linux"))]
2389+
#[cfg_attr(docsrs, doc(cfg(feature = "all", target_os = "linux")))]
2390+
pub fn set_dccp_qpolicy_txqlen(&self, length: u32) -> io::Result<()> {
2391+
unsafe {
2392+
setsockopt(
2393+
self.as_raw(),
2394+
libc::SOL_DCCP,
2395+
libc::DCCP_SOCKOPT_QPOLICY_TXQLEN,
2396+
length,
2397+
)
2398+
}
2399+
}
2400+
2401+
/// Get the value of the `DCCP_SOCKOPT_QPOLICY_TXQLEN` on this socket.
2402+
///
2403+
/// For more information on this option see [`set_dccp_qpolicy_txqlen`].
2404+
///
2405+
/// [`set_dccp_qpolicy_txqlen`]: crate::Socket::set_dccp_qpolicy_txqlen
2406+
#[cfg(all(feature = "all", target_os = "linux"))]
2407+
#[cfg_attr(docsrs, doc(cfg(feature = "all", target_os = "linux")))]
2408+
pub fn dccp_qpolicy_txqlen(&self) -> io::Result<u32> {
2409+
unsafe {
2410+
getsockopt(
2411+
self.as_raw(),
2412+
libc::SOL_DCCP,
2413+
libc::DCCP_SOCKOPT_QPOLICY_TXQLEN,
2414+
)
2415+
}
2416+
}
2417+
2418+
/// Get the value of the `DCCP_SOCKOPT_AVAILABLE_CCIDS` option on this socket.
2419+
///
2420+
/// Returns the list of CCIDs supported by the endpoint.
2421+
///
2422+
/// The parameter `N` is used to get the maximum number of supported
2423+
/// endpoints. The [documentation] recommends a minimum of four at the time
2424+
/// of writing.
2425+
///
2426+
/// [documentation]: https://www.kernel.org/doc/html/latest/networking/dccp.html
2427+
#[cfg(all(feature = "all", target_os = "linux"))]
2428+
#[cfg_attr(docsrs, doc(cfg(feature = "all", target_os = "linux")))]
2429+
pub fn dccp_available_ccids<const N: usize>(&self) -> io::Result<CcidEndpoints<N>> {
2430+
let mut endpoints = [0; N];
2431+
let mut length = endpoints.len() as libc::socklen_t;
2432+
syscall!(getsockopt(
2433+
self.as_raw(),
2434+
libc::SOL_DCCP,
2435+
libc::DCCP_SOCKOPT_AVAILABLE_CCIDS,
2436+
endpoints.as_mut_ptr().cast(),
2437+
&mut length,
2438+
))?;
2439+
Ok(CcidEndpoints { endpoints, length })
2440+
}
2441+
2442+
/// Get the value of the `DCCP_SOCKOPT_GET_CUR_MPS` option on this socket.
2443+
///
2444+
/// This option retrieves the current maximum packet size (application
2445+
/// payload size) in bytes.
2446+
#[cfg(all(feature = "all", target_os = "linux"))]
2447+
#[cfg_attr(docsrs, doc(cfg(feature = "all", target_os = "linux")))]
2448+
pub fn dccp_cur_mps(&self) -> io::Result<u32> {
2449+
unsafe {
2450+
getsockopt(
2451+
self.as_raw(),
2452+
libc::SOL_DCCP,
2453+
libc::DCCP_SOCKOPT_GET_CUR_MPS,
2454+
)
2455+
}
2456+
}
2457+
}
2458+
2459+
/// See [`Socket::dccp_available_ccids`].
2460+
#[cfg(all(feature = "all", target_os = "linux"))]
2461+
#[cfg_attr(docsrs, doc(cfg(feature = "all", target_os = "linux")))]
2462+
#[derive(Debug)]
2463+
pub struct CcidEndpoints<const N: usize> {
2464+
endpoints: [u8; N],
2465+
length: u32,
2466+
}
2467+
2468+
#[cfg(all(feature = "all", target_os = "linux"))]
2469+
#[cfg_attr(docsrs, doc(cfg(feature = "all", target_os = "linux")))]
2470+
impl<const N: usize> std::ops::Deref for CcidEndpoints<N> {
2471+
type Target = [u8];
2472+
2473+
fn deref(&self) -> &[u8] {
2474+
&self.endpoints[0..self.length as usize]
2475+
}
22162476
}
22172477

22182478
#[cfg_attr(docsrs, doc(cfg(unix)))]

tests/socket.rs

+33
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,8 @@ fn protocol_fmt_debug() {
108108
(Protocol::UDP, "IPPROTO_UDP"),
109109
#[cfg(target_os = "linux")]
110110
(Protocol::MPTCP, "IPPROTO_MPTCP"),
111+
#[cfg(all(feature = "all", target_os = "linux"))]
112+
(Protocol::DCCP, "IPPROTO_DCCP"),
111113
#[cfg(all(feature = "all", any(target_os = "freebsd", target_os = "linux")))]
112114
(Protocol::SCTP, "IPPROTO_SCTP"),
113115
(500.into(), "500"),
@@ -1387,3 +1389,34 @@ fn tcp_congestion() {
13871389
new_tcp_ca,
13881390
);
13891391
}
1392+
1393+
#[test]
1394+
#[ignore = "DCCP support is not enabled in all kernels of majors Linux distros"]
1395+
#[cfg(all(feature = "all", target_os = "linux"))]
1396+
fn dccp() {
1397+
let listener = Socket::new(Domain::IPV4, Type::DCCP, Some(Protocol::DCCP)).unwrap();
1398+
let addr = "127.0.0.1:0".parse::<SocketAddr>().unwrap().into();
1399+
listener.set_dccp_service(45).unwrap();
1400+
assert!(listener.dccp_service().unwrap() == 45);
1401+
assert!(listener.dccp_cur_mps().unwrap() > 0);
1402+
assert!(listener.dccp_available_ccids::<4>().unwrap().len() >= 3);
1403+
assert!(
1404+
listener.dccp_send_cscov().unwrap() == 0,
1405+
"sender cscov should be zero by default"
1406+
);
1407+
listener.set_dccp_ccid(2).unwrap();
1408+
listener.set_dccp_qpolicy_txqlen(6).unwrap();
1409+
assert!(listener.dccp_qpolicy_txqlen().unwrap() == 6);
1410+
listener.bind(&addr).unwrap();
1411+
listener.listen(10).unwrap();
1412+
1413+
let mut client = Socket::new(Domain::IPV4, Type::DCCP, Some(Protocol::DCCP)).unwrap();
1414+
client.set_dccp_service(45).unwrap();
1415+
client.connect(&addr).unwrap();
1416+
1417+
let (mut accepted, _) = listener.accept().unwrap();
1418+
let msg = "Hello World!";
1419+
assert!(client.write(msg.as_bytes()).unwrap() == msg.len());
1420+
let mut recv_buf = [0_u8; 64];
1421+
assert!(accepted.read(&mut recv_buf).unwrap() == msg.len());
1422+
}

0 commit comments

Comments
 (0)