Skip to content

Commit 06ad93d

Browse files
authored
Add setter and getter for multicast_ifv4 and multicast_ifv6 (#1291)
* Add setter and getter for `multicast_ifv4` and `multicast_ifv6` * Add `set_ip_multicast_if_with_ifindex` and fix naming * make all ifindex to `u32`
1 parent 9abd5fa commit 06ad93d

File tree

4 files changed

+240
-12
lines changed

4 files changed

+240
-12
lines changed

src/backend/libc/net/sockopt.rs

Lines changed: 53 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,44 @@ pub(crate) fn ipv6_v6only(fd: BorrowedFd<'_>) -> io::Result<bool> {
480480
getsockopt(fd, c::IPPROTO_IPV6, c::IPV6_V6ONLY).map(to_bool)
481481
}
482482

483+
#[inline]
484+
pub(crate) fn set_ip_multicast_if(fd: BorrowedFd<'_>, value: &Ipv4Addr) -> io::Result<()> {
485+
setsockopt(fd, c::IPPROTO_IP, c::IP_MULTICAST_IF, to_imr_addr(value))
486+
}
487+
488+
#[inline]
489+
pub(crate) fn ip_multicast_if(fd: BorrowedFd<'_>) -> io::Result<Ipv4Addr> {
490+
getsockopt(fd, c::IPPROTO_IP, c::IP_MULTICAST_IF).map(from_in_addr)
491+
}
492+
493+
#[cfg(any(
494+
apple,
495+
freebsdlike,
496+
linux_like,
497+
target_os = "fuchsia",
498+
target_os = "openbsd"
499+
))]
500+
#[inline]
501+
pub(crate) fn set_ip_multicast_if_with_ifindex(
502+
fd: BorrowedFd<'_>,
503+
multiaddr: &Ipv4Addr,
504+
address: &Ipv4Addr,
505+
ifindex: u32,
506+
) -> io::Result<()> {
507+
let mreqn = to_ip_mreqn(multiaddr, address, ifindex as i32);
508+
setsockopt(fd, c::IPPROTO_IP, c::IP_MULTICAST_IF, mreqn)
509+
}
510+
511+
#[inline]
512+
pub(crate) fn set_ipv6_multicast_if(fd: BorrowedFd<'_>, value: u32) -> io::Result<()> {
513+
setsockopt(fd, c::IPPROTO_IPV6, c::IPV6_MULTICAST_IF, value as c::c_int)
514+
}
515+
516+
#[inline]
517+
pub(crate) fn ipv6_multicast_if(fd: BorrowedFd<'_>) -> io::Result<u32> {
518+
getsockopt(fd, c::IPPROTO_IPV6, c::IPV6_MULTICAST_IF)
519+
}
520+
483521
#[inline]
484522
pub(crate) fn set_ip_multicast_loop(fd: BorrowedFd<'_>, multicast_loop: bool) -> io::Result<()> {
485523
setsockopt(
@@ -552,9 +590,9 @@ pub(crate) fn set_ip_add_membership_with_ifindex(
552590
fd: BorrowedFd<'_>,
553591
multiaddr: &Ipv4Addr,
554592
address: &Ipv4Addr,
555-
ifindex: i32,
593+
ifindex: u32,
556594
) -> io::Result<()> {
557-
let mreqn = to_ip_mreqn(multiaddr, address, ifindex);
595+
let mreqn = to_ip_mreqn(multiaddr, address, ifindex as i32);
558596
setsockopt(fd, c::IPPROTO_IP, c::IP_ADD_MEMBERSHIP, mreqn)
559597
}
560598

@@ -631,9 +669,9 @@ pub(crate) fn set_ip_drop_membership_with_ifindex(
631669
fd: BorrowedFd<'_>,
632670
multiaddr: &Ipv4Addr,
633671
address: &Ipv4Addr,
634-
ifindex: i32,
672+
ifindex: u32,
635673
) -> io::Result<()> {
636-
let mreqn = to_ip_mreqn(multiaddr, address, ifindex);
674+
let mreqn = to_ip_mreqn(multiaddr, address, ifindex as i32);
637675
setsockopt(fd, c::IPPROTO_IP, c::IP_DROP_MEMBERSHIP, mreqn)
638676
}
639677

@@ -1150,6 +1188,17 @@ fn to_ip_mreq(multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> c::ip_mreq {
11501188
}
11511189
}
11521190

1191+
#[cfg(not(windows))]
1192+
#[inline]
1193+
fn from_in_addr(in_addr: c::in_addr) -> Ipv4Addr {
1194+
Ipv4Addr::from(in_addr.s_addr.to_ne_bytes())
1195+
}
1196+
1197+
#[cfg(windows)]
1198+
fn from_in_addr(in_addr: c::in_addr) -> Ipv4Addr {
1199+
Ipv4Addr::from(unsafe { in_addr.S_un.S_addr.to_ne_bytes() })
1200+
}
1201+
11531202
#[cfg(any(
11541203
apple,
11551204
freebsdlike,

src/backend/linux_raw/net/sockopt.rs

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,12 @@ use alloc::borrow::ToOwned;
2424
use alloc::string::String;
2525
use core::mem::MaybeUninit;
2626
use core::time::Duration;
27-
use linux_raw_sys::general::{__kernel_old_timeval, __kernel_sock_timeval};
2827
#[cfg(target_os = "linux")]
2928
use linux_raw_sys::xdp::{xdp_mmap_offsets, xdp_statistics, xdp_statistics_v1};
29+
use linux_raw_sys::{
30+
general::{__kernel_old_timeval, __kernel_sock_timeval},
31+
net::{IPV6_MULTICAST_IF, IP_MULTICAST_IF},
32+
};
3033
#[cfg(target_arch = "x86")]
3134
use {
3235
crate::backend::conv::{slice_just_addr, x86_sys},
@@ -445,6 +448,37 @@ pub(crate) fn ipv6_v6only(fd: BorrowedFd<'_>) -> io::Result<bool> {
445448
getsockopt(fd, c::IPPROTO_IPV6, c::IPV6_V6ONLY).map(to_bool)
446449
}
447450

451+
#[inline]
452+
pub(crate) fn set_ip_multicast_if_with_ifindex(
453+
fd: BorrowedFd<'_>,
454+
multiaddr: &Ipv4Addr,
455+
address: &Ipv4Addr,
456+
ifindex: u32,
457+
) -> io::Result<()> {
458+
let mreqn = to_ip_mreqn(multiaddr, address, ifindex as i32);
459+
setsockopt(fd, c::IPPROTO_IP, IP_MULTICAST_IF, mreqn)
460+
}
461+
462+
#[inline]
463+
pub(crate) fn set_ip_multicast_if(fd: BorrowedFd<'_>, value: &Ipv4Addr) -> io::Result<()> {
464+
setsockopt(fd, c::IPPROTO_IP, IP_MULTICAST_IF, to_imr_addr(value))
465+
}
466+
467+
#[inline]
468+
pub(crate) fn ip_multicast_if(fd: BorrowedFd<'_>) -> io::Result<Ipv4Addr> {
469+
getsockopt(fd, c::IPPROTO_IP, IP_MULTICAST_IF).map(from_in_addr)
470+
}
471+
472+
#[inline]
473+
pub(crate) fn set_ipv6_multicast_if(fd: BorrowedFd<'_>, value: u32) -> io::Result<()> {
474+
setsockopt(fd, c::IPPROTO_IPV6, IPV6_MULTICAST_IF, value as c::c_int)
475+
}
476+
477+
#[inline]
478+
pub(crate) fn ipv6_multicast_if(fd: BorrowedFd<'_>) -> io::Result<u32> {
479+
getsockopt(fd, c::IPPROTO_IPV6, IPV6_MULTICAST_IF)
480+
}
481+
448482
#[inline]
449483
pub(crate) fn set_ip_multicast_loop(fd: BorrowedFd<'_>, multicast_loop: bool) -> io::Result<()> {
450484
setsockopt(
@@ -510,9 +544,9 @@ pub(crate) fn set_ip_add_membership_with_ifindex(
510544
fd: BorrowedFd<'_>,
511545
multiaddr: &Ipv4Addr,
512546
address: &Ipv4Addr,
513-
ifindex: i32,
547+
ifindex: u32,
514548
) -> io::Result<()> {
515-
let mreqn = to_ip_mreqn(multiaddr, address, ifindex);
549+
let mreqn = to_ip_mreqn(multiaddr, address, ifindex as i32);
516550
setsockopt(fd, c::IPPROTO_IP, c::IP_ADD_MEMBERSHIP, mreqn)
517551
}
518552

@@ -563,9 +597,9 @@ pub(crate) fn set_ip_drop_membership_with_ifindex(
563597
fd: BorrowedFd<'_>,
564598
multiaddr: &Ipv4Addr,
565599
address: &Ipv4Addr,
566-
ifindex: i32,
600+
ifindex: u32,
567601
) -> io::Result<()> {
568-
let mreqn = to_ip_mreqn(multiaddr, address, ifindex);
602+
let mreqn = to_ip_mreqn(multiaddr, address, ifindex as i32);
569603
setsockopt(fd, c::IPPROTO_IP, c::IP_DROP_MEMBERSHIP, mreqn)
570604
}
571605

@@ -976,6 +1010,11 @@ pub(crate) fn xdp_options(fd: BorrowedFd<'_>) -> io::Result<XdpOptionsFlags> {
9761010
getsockopt(fd, c::SOL_XDP, c::XDP_OPTIONS)
9771011
}
9781012

1013+
#[inline]
1014+
fn from_in_addr(in_addr: c::in_addr) -> Ipv4Addr {
1015+
Ipv4Addr::from(in_addr.s_addr.to_ne_bytes())
1016+
}
1017+
9791018
#[inline]
9801019
fn to_ip_mreq(multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> c::ip_mreq {
9811020
c::ip_mreq {

src/net/sockopt.rs

Lines changed: 73 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -647,6 +647,77 @@ pub fn ipv6_v6only<Fd: AsFd>(fd: Fd) -> io::Result<bool> {
647647
backend::net::sockopt::ipv6_v6only(fd.as_fd())
648648
}
649649

650+
/// `setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, value)`
651+
///
652+
/// See the [module-level documentation] for more.
653+
///
654+
/// [module-level documentation]: self#references-for-get_ip_-and-set_ip_-functions
655+
#[inline]
656+
#[doc(alias = "IP_MULTICAST_IF")]
657+
pub fn set_ip_multicast_if<Fd: AsFd>(fd: Fd, value: &Ipv4Addr) -> io::Result<()> {
658+
backend::net::sockopt::set_ip_multicast_if(fd.as_fd(), value)
659+
}
660+
661+
/// `setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, multiaddr, address,
662+
/// ifindex)`
663+
///
664+
/// This is similar to [`set_ip_multicast_if`] but additionally allows an
665+
/// `ifindex` value to be given.
666+
///
667+
/// See the [module-level documentation] for more.
668+
///
669+
/// [module-level documentation]: self#references-for-get_ip_-and-set_ip_-functions
670+
#[cfg(any(
671+
apple,
672+
freebsdlike,
673+
linux_like,
674+
target_os = "fuchsia",
675+
target_os = "openbsd"
676+
))]
677+
#[inline]
678+
#[doc(alias = "IP_MULTICAST_IF")]
679+
pub fn set_ip_multicast_if_with_ifindex<Fd: AsFd>(
680+
fd: Fd,
681+
multiaddr: &Ipv4Addr,
682+
address: &Ipv4Addr,
683+
ifindex: u32,
684+
) -> io::Result<()> {
685+
backend::net::sockopt::set_ip_multicast_if_with_ifindex(fd.as_fd(), multiaddr, address, ifindex)
686+
}
687+
688+
/// `getsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF)`
689+
///
690+
/// See the [module-level documentation] for more.
691+
///
692+
/// [module-level documentation]: self#references-for-get_ip_-and-set_ip_-functions
693+
#[inline]
694+
#[doc(alias = "IP_MULTICAST_IF")]
695+
pub fn ip_multicast_if<Fd: AsFd>(fd: Fd) -> io::Result<Ipv4Addr> {
696+
backend::net::sockopt::ip_multicast_if(fd.as_fd())
697+
}
698+
699+
/// `setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, value)`
700+
///
701+
/// See the [module-level documentation] for more.
702+
///
703+
/// [module-level documentation]: self#references-for-get_ip_-and-set_ip_-functions
704+
#[inline]
705+
#[doc(alias = "IPV6_MULTICAST_IF")]
706+
pub fn set_ipv6_multicast_if<Fd: AsFd>(fd: Fd, value: u32) -> io::Result<()> {
707+
backend::net::sockopt::set_ipv6_multicast_if(fd.as_fd(), value)
708+
}
709+
710+
/// `getsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF)`
711+
///
712+
/// See the [module-level documentation] for more.
713+
///
714+
/// [module-level documentation]: self#references-for-get_ip_-and-set_ip_-functions
715+
#[inline]
716+
#[doc(alias = "IPV6_MULTICAST_IF")]
717+
pub fn ipv6_multicast_if<Fd: AsFd>(fd: Fd) -> io::Result<u32> {
718+
backend::net::sockopt::ipv6_multicast_if(fd.as_fd())
719+
}
720+
650721
/// `setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, value)`
651722
///
652723
/// See the [module-level documentation] for more.
@@ -798,7 +869,7 @@ pub fn set_ip_add_membership_with_ifindex<Fd: AsFd>(
798869
fd: Fd,
799870
multiaddr: &Ipv4Addr,
800871
address: &Ipv4Addr,
801-
ifindex: i32,
872+
ifindex: u32,
802873
) -> io::Result<()> {
803874
backend::net::sockopt::set_ip_add_membership_with_ifindex(
804875
fd.as_fd(),
@@ -907,7 +978,7 @@ pub fn set_ip_drop_membership_with_ifindex<Fd: AsFd>(
907978
fd: Fd,
908979
multiaddr: &Ipv4Addr,
909980
address: &Ipv4Addr,
910-
ifindex: i32,
981+
ifindex: u32,
911982
) -> io::Result<()> {
912983
backend::net::sockopt::set_ip_drop_membership_with_ifindex(
913984
fd.as_fd(),

tests/net/sockopt.rs

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use rustix::io;
1010
))]
1111
use rustix::net::ipproto;
1212
use rustix::net::{sockopt, AddressFamily, SocketType};
13-
use std::time::Duration;
13+
use std::{net::Ipv4Addr, time::Duration};
1414

1515
// Test `socket` socket options.
1616
fn test_sockopts_socket(s: &OwnedFd) {
@@ -486,3 +486,72 @@ fn test_sockopts_ipv6() {
486486

487487
test_sockopts_tcp(&s);
488488
}
489+
490+
#[test]
491+
fn test_sockopts_multicast_ifv4() {
492+
crate::init();
493+
494+
let s = rustix::net::socket(AddressFamily::INET, SocketType::DGRAM, None).unwrap();
495+
496+
// Set a ipv4 interface
497+
match sockopt::set_ip_multicast_if(&s, &Ipv4Addr::LOCALHOST) {
498+
Ok(_) => {
499+
assert_eq!(sockopt::ip_multicast_if(&s).unwrap(), Ipv4Addr::LOCALHOST);
500+
}
501+
Err(e) if e.to_string().contains("Protocol not available") => {
502+
// Skip test on unsupported platforms
503+
}
504+
Err(e) => panic!("{e}"),
505+
}
506+
}
507+
508+
#[cfg(linux_kernel)]
509+
#[test]
510+
fn test_sockopts_multicast_if_with_ifindex() {
511+
crate::init();
512+
513+
let s = rustix::net::socket(AddressFamily::INET, SocketType::DGRAM, None).unwrap();
514+
515+
let fd = rustix::net::socket_with(
516+
AddressFamily::INET,
517+
SocketType::DGRAM,
518+
rustix::net::SocketFlags::CLOEXEC,
519+
None,
520+
)
521+
.unwrap();
522+
let index = rustix::net::netdevice::name_to_index(&fd, "lo").unwrap();
523+
524+
// Set a ipv4 interface
525+
match sockopt::set_ip_multicast_if_with_ifindex(
526+
&s,
527+
&Ipv4Addr::new(224, 254, 0, 0),
528+
&Ipv4Addr::UNSPECIFIED,
529+
index,
530+
) {
531+
Ok(_) => {
532+
assert_eq!(sockopt::ip_multicast_if(&s).unwrap(), Ipv4Addr::UNSPECIFIED);
533+
}
534+
Err(e) if e.to_string().contains("Protocol not available") => {
535+
// Skip test on unsupported platforms
536+
}
537+
Err(e) => panic!("{e}"),
538+
}
539+
}
540+
541+
#[test]
542+
fn test_sockopts_multicast_ifv6() {
543+
crate::init();
544+
545+
let s = rustix::net::socket(AddressFamily::INET6, SocketType::DGRAM, None).unwrap();
546+
547+
// Set a ipv6 interface
548+
match sockopt::set_ipv6_multicast_if(&s, 1) {
549+
Ok(_) => {
550+
assert_eq!(sockopt::ipv6_multicast_if(&s).unwrap(), 1);
551+
}
552+
Err(e) if e.to_string().contains("Protocol not available") => {
553+
// Skip test on unsupported platforms
554+
}
555+
Err(e) => panic!("{e}"),
556+
}
557+
}

0 commit comments

Comments
 (0)