Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add multicast SSM join and leave for IPv4 (IGMP) #298

Merged
merged 6 commits into from
Apr 19, 2022
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ rustdoc-args = ["--cfg", "docsrs"]
features = ["all"]

[target."cfg(unix)".dependencies]
libc = "0.2.114"
libc = "0.2.124"

[target."cfg(windows)".dependencies]
winapi = { version = "0.3.9", features = ["handleapi", "ws2ipdef", "ws2tcpip"] }
Expand Down
56 changes: 56 additions & 0 deletions src/socket.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1184,6 +1184,62 @@ impl Socket {
}
}

/// Join a multicast SSM channel using `IP_ADD_SOURCE_MEMBERSHIP` option on this socket.
///
/// This function specifies a new multicast channel for this socket to join.
/// The g must be a valid SSM group address, the s must be the address of the sender
Thomasdezeeuw marked this conversation as resolved.
Show resolved Hide resolved
/// and `interface` is the address of the local interface with which the system should join the
/// multicast group. If it's [`Ipv4Addr::UNSPECIFIED`] (`INADDR_ANY`) then
/// an appropriate interface is chosen by the system.
#[cfg(not(any(
target_os = "haiku",
target_os = "netbsd",
target_os = "redox",
target_os = "fuchsia",
)))]
pub fn join_ssm_v4(&self, s: &Ipv4Addr, g: &Ipv4Addr, interface: &Ipv4Addr) -> io::Result<()> {
Thomasdezeeuw marked this conversation as resolved.
Show resolved Hide resolved
let mreqs = sys::IpMreqSource {
imr_multiaddr: sys::to_in_addr(g),
imr_interface: sys::to_in_addr(interface),
imr_sourceaddr: sys::to_in_addr(s),
};
unsafe {
setsockopt(
self.as_raw(),
sys::IPPROTO_IP,
sys::IP_ADD_SOURCE_MEMBERSHIP,
mreqs,
)
}
}

/// Leave a multicast group using `IP_DROP_SOURCE_MEMBERSHIP` option on this socket.
///
/// For more information about this option, see [`join_ssm_v4`].
///
/// [`join_ssm_v4`]: Socket::join_ssm_v4
#[cfg(not(any(
target_os = "haiku",
target_os = "netbsd",
target_os = "redox",
target_os = "fuchsia",
)))]
pub fn leave_ssm_v4(&self, s: &Ipv4Addr, g: &Ipv4Addr, interface: &Ipv4Addr) -> io::Result<()> {
let mreqs = sys::IpMreqSource {
imr_multiaddr: sys::to_in_addr(g),
imr_interface: sys::to_in_addr(interface),
imr_sourceaddr: sys::to_in_addr(s),
};
unsafe {
setsockopt(
self.as_raw(),
sys::IPPROTO_IP,
sys::IP_DROP_SOURCE_MEMBERSHIP,
mreqs,
)
}
}

/// Get the value of the `IP_MULTICAST_IF` option for this socket.
///
/// For more information about this option, see [`set_multicast_if_v4`].
Expand Down
9 changes: 9 additions & 0 deletions src/sys/unix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,15 @@ pub(crate) use libc::{
IP_TTL, MSG_OOB, MSG_PEEK, SOL_SOCKET, SO_BROADCAST, SO_ERROR, SO_KEEPALIVE, SO_RCVBUF,
SO_RCVTIMEO, SO_REUSEADDR, SO_SNDBUF, SO_SNDTIMEO, SO_TYPE, TCP_NODELAY,
};
#[cfg(not(any(
target_os = "haiku",
target_os = "netbsd",
target_os = "redox",
target_os = "fuchsia",
)))]
pub(crate) use libc::{
ip_mreq_source as IpMreqSource, IP_ADD_SOURCE_MEMBERSHIP, IP_DROP_SOURCE_MEMBERSHIP,
};
#[cfg(not(any(
target_os = "dragonfly",
target_os = "freebsd",
Expand Down
5 changes: 3 additions & 2 deletions src/sys/windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,9 @@ pub(crate) use winapi::shared::ws2ipdef::IP_HDRINCL;
pub(crate) use winapi::shared::ws2ipdef::{
IPV6_ADD_MEMBERSHIP, IPV6_DROP_MEMBERSHIP, IPV6_MREQ as Ipv6Mreq, IPV6_MULTICAST_HOPS,
IPV6_MULTICAST_IF, IPV6_MULTICAST_LOOP, IPV6_UNICAST_HOPS, IPV6_V6ONLY, IP_ADD_MEMBERSHIP,
IP_DROP_MEMBERSHIP, IP_MREQ as IpMreq, IP_MULTICAST_IF, IP_MULTICAST_LOOP, IP_MULTICAST_TTL,
IP_TOS, IP_TTL,
IP_ADD_SOURCE_MEMBERSHIP, IP_DROP_MEMBERSHIP, IP_DROP_SOURCE_MEMBERSHIP, IP_MREQ as IpMreq,
IP_MREQ_SOURCE as IpMreqSource, IP_MULTICAST_IF, IP_MULTICAST_LOOP, IP_MULTICAST_TTL, IP_TOS,
IP_TTL,
};
pub(crate) use winapi::um::winsock2::{linger, MSG_OOB, MSG_PEEK};
pub(crate) const IPPROTO_IPV6: c_int = winapi::shared::ws2def::IPPROTO_IPV6 as c_int;
Expand Down
16 changes: 16 additions & 0 deletions tests/socket.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1219,6 +1219,22 @@ fn join_leave_multicast_v4_n() {
.expect("leave multicast group");
}

#[test]
#[cfg(not(any(
target_os = "haiku",
target_os = "netbsd",
target_os = "redox",
target_os = "fuchsia",
)))]
fn join_leave_ssm_v4() {
let socket = Socket::new(Domain::IPV4, Type::DGRAM, None).unwrap();
let g = Ipv4Addr::new(232, 123, 52, 36);
let s = Ipv4Addr::new(62, 40, 109, 31);
let interface = Ipv4Addr::new(0, 0, 0, 0);
let () = socket.join_ssm_v4(&s, &g, &interface).expect("Joined SSM");
let () = socket.leave_ssm_v4(&s, &g, &interface).expect("Left SSM");
}

#[test]
#[cfg(all(feature = "all", not(target_os = "redox")))]
fn header_included() {
Expand Down