Skip to content

Commit 5436228

Browse files
authored
Add multicast SSM join and leave for IPv4 (IGMP)
1 parent 5272921 commit 5436228

File tree

5 files changed

+95
-3
lines changed

5 files changed

+95
-3
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ rustdoc-args = ["--cfg", "docsrs"]
3333
features = ["all"]
3434

3535
[target."cfg(unix)".dependencies]
36-
libc = "0.2.114"
36+
libc = "0.2.124"
3737

3838
[target."cfg(windows)".dependencies]
3939
winapi = { version = "0.3.9", features = ["handleapi", "ws2ipdef", "ws2tcpip"] }

src/socket.rs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1184,6 +1184,72 @@ impl Socket {
11841184
}
11851185
}
11861186

1187+
/// Join a multicast SSM channel using `IP_ADD_SOURCE_MEMBERSHIP` option on this socket.
1188+
///
1189+
/// This function specifies a new multicast channel for this socket to join.
1190+
/// The group must be a valid SSM group address, the source must be the address of the sender
1191+
/// and `interface` is the address of the local interface with which the system should join the
1192+
/// multicast group. If it's [`Ipv4Addr::UNSPECIFIED`] (`INADDR_ANY`) then
1193+
/// an appropriate interface is chosen by the system.
1194+
#[cfg(not(any(
1195+
target_os = "haiku",
1196+
target_os = "netbsd",
1197+
target_os = "redox",
1198+
target_os = "fuchsia",
1199+
)))]
1200+
pub fn join_ssm_v4(
1201+
&self,
1202+
source: &Ipv4Addr,
1203+
group: &Ipv4Addr,
1204+
interface: &Ipv4Addr,
1205+
) -> io::Result<()> {
1206+
let mreqs = sys::IpMreqSource {
1207+
imr_multiaddr: sys::to_in_addr(group),
1208+
imr_interface: sys::to_in_addr(interface),
1209+
imr_sourceaddr: sys::to_in_addr(source),
1210+
};
1211+
unsafe {
1212+
setsockopt(
1213+
self.as_raw(),
1214+
sys::IPPROTO_IP,
1215+
sys::IP_ADD_SOURCE_MEMBERSHIP,
1216+
mreqs,
1217+
)
1218+
}
1219+
}
1220+
1221+
/// Leave a multicast group using `IP_DROP_SOURCE_MEMBERSHIP` option on this socket.
1222+
///
1223+
/// For more information about this option, see [`join_ssm_v4`].
1224+
///
1225+
/// [`join_ssm_v4`]: Socket::join_ssm_v4
1226+
#[cfg(not(any(
1227+
target_os = "haiku",
1228+
target_os = "netbsd",
1229+
target_os = "redox",
1230+
target_os = "fuchsia",
1231+
)))]
1232+
pub fn leave_ssm_v4(
1233+
&self,
1234+
source: &Ipv4Addr,
1235+
group: &Ipv4Addr,
1236+
interface: &Ipv4Addr,
1237+
) -> io::Result<()> {
1238+
let mreqs = sys::IpMreqSource {
1239+
imr_multiaddr: sys::to_in_addr(group),
1240+
imr_interface: sys::to_in_addr(interface),
1241+
imr_sourceaddr: sys::to_in_addr(source),
1242+
};
1243+
unsafe {
1244+
setsockopt(
1245+
self.as_raw(),
1246+
sys::IPPROTO_IP,
1247+
sys::IP_DROP_SOURCE_MEMBERSHIP,
1248+
mreqs,
1249+
)
1250+
}
1251+
}
1252+
11871253
/// Get the value of the `IP_MULTICAST_IF` option for this socket.
11881254
///
11891255
/// For more information about this option, see [`set_multicast_if_v4`].

src/sys/unix.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,15 @@ pub(crate) use libc::{
9595
IP_TTL, MSG_OOB, MSG_PEEK, SOL_SOCKET, SO_BROADCAST, SO_ERROR, SO_KEEPALIVE, SO_RCVBUF,
9696
SO_RCVTIMEO, SO_REUSEADDR, SO_SNDBUF, SO_SNDTIMEO, SO_TYPE, TCP_NODELAY,
9797
};
98+
#[cfg(not(any(
99+
target_os = "haiku",
100+
target_os = "netbsd",
101+
target_os = "redox",
102+
target_os = "fuchsia",
103+
)))]
104+
pub(crate) use libc::{
105+
ip_mreq_source as IpMreqSource, IP_ADD_SOURCE_MEMBERSHIP, IP_DROP_SOURCE_MEMBERSHIP,
106+
};
98107
#[cfg(not(any(
99108
target_os = "dragonfly",
100109
target_os = "freebsd",

src/sys/windows.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,9 @@ pub(crate) use winapi::shared::ws2ipdef::IP_HDRINCL;
7171
pub(crate) use winapi::shared::ws2ipdef::{
7272
IPV6_ADD_MEMBERSHIP, IPV6_DROP_MEMBERSHIP, IPV6_MREQ as Ipv6Mreq, IPV6_MULTICAST_HOPS,
7373
IPV6_MULTICAST_IF, IPV6_MULTICAST_LOOP, IPV6_UNICAST_HOPS, IPV6_V6ONLY, IP_ADD_MEMBERSHIP,
74-
IP_DROP_MEMBERSHIP, IP_MREQ as IpMreq, IP_MULTICAST_IF, IP_MULTICAST_LOOP, IP_MULTICAST_TTL,
75-
IP_TOS, IP_TTL,
74+
IP_ADD_SOURCE_MEMBERSHIP, IP_DROP_MEMBERSHIP, IP_DROP_SOURCE_MEMBERSHIP, IP_MREQ as IpMreq,
75+
IP_MREQ_SOURCE as IpMreqSource, IP_MULTICAST_IF, IP_MULTICAST_LOOP, IP_MULTICAST_TTL, IP_TOS,
76+
IP_TTL,
7677
};
7778
pub(crate) use winapi::um::winsock2::{linger, MSG_OOB, MSG_PEEK};
7879
pub(crate) const IPPROTO_IPV6: c_int = winapi::shared::ws2def::IPPROTO_IPV6 as c_int;

tests/socket.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1219,6 +1219,22 @@ fn join_leave_multicast_v4_n() {
12191219
.expect("leave multicast group");
12201220
}
12211221

1222+
#[test]
1223+
#[cfg(not(any(
1224+
target_os = "haiku",
1225+
target_os = "netbsd",
1226+
target_os = "redox",
1227+
target_os = "fuchsia",
1228+
)))]
1229+
fn join_leave_ssm_v4() {
1230+
let socket = Socket::new(Domain::IPV4, Type::DGRAM, None).unwrap();
1231+
let g = Ipv4Addr::new(232, 123, 52, 36);
1232+
let s = Ipv4Addr::new(62, 40, 109, 31);
1233+
let interface = Ipv4Addr::new(0, 0, 0, 0);
1234+
let () = socket.join_ssm_v4(&s, &g, &interface).expect("Joined SSM");
1235+
let () = socket.leave_ssm_v4(&s, &g, &interface).expect("Left SSM");
1236+
}
1237+
12221238
#[test]
12231239
#[cfg(all(feature = "all", not(target_os = "redox")))]
12241240
fn header_included() {

0 commit comments

Comments
 (0)