Skip to content

Commit 2c42b30

Browse files
bors[bot]isomer
andauthored
Merge #1222
1222: Add Ipv{4,6}PacketInfo support to ControlMessage for send{m,}msg. r=asomers a=isomer This adds Ipv4PacketInfo and Ipv6PacketInfo to ControlMessage, allowing these to be used with sendmsg/sendmmsg. Co-authored-by: Perry Lorier <perryl@google.com>
2 parents ea099dd + 78347d1 commit 2c42b30

File tree

3 files changed

+150
-0
lines changed

3 files changed

+150
-0
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
3333
(#[1233](https://github.com/nix-rust/nix/pull/1233))
3434
- Added `EventFilter` bitflags for `EV_DISPATCH` and `EV_RECEIPT` on OpenBSD.
3535
(#[1252](https://github.com/nix-rust/nix/pull/1252))
36+
- Added support for `Ipv4PacketInfo` and `Ipv6PacketInfo` to `ControlMessage`.
37+
(#[1222](https://github.com/nix-rust/nix/pull/1222))
3638

3739
### Changed
3840
- Changed `fallocate` return type from `c_int` to `()` (#[1201](https://github.com/nix-rust/nix/pull/1201))

src/sys/socket/mod.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -717,6 +717,25 @@ pub enum ControlMessage<'a> {
717717
/// following one by one, and the last, possibly smaller one.
718718
#[cfg(target_os = "linux")]
719719
UdpGsoSegments(&'a u16),
720+
721+
/// Configure the sending addressing and interface for v4
722+
///
723+
/// For further information, please refer to the
724+
/// [`ip(7)`](http://man7.org/linux/man-pages/man7/ip.7.html) man page.
725+
#[cfg(any(target_os = "linux",
726+
target_os = "macos",
727+
target_os = "netbsd"))]
728+
Ipv4PacketInfo(&'a libc::in_pktinfo),
729+
730+
/// Configure the sending addressing and interface for v6
731+
///
732+
/// For further information, please refer to the
733+
/// [`ipv6(7)`](http://man7.org/linux/man-pages/man7/ipv6.7.html) man page.
734+
#[cfg(any(target_os = "linux",
735+
target_os = "macos",
736+
target_os = "netbsd",
737+
target_os = "freebsd"))]
738+
Ipv6PacketInfo(&'a libc::in6_pktinfo),
720739
}
721740

722741
// An opaque structure used to prevent cmsghdr from being a public type
@@ -798,6 +817,12 @@ impl<'a> ControlMessage<'a> {
798817
ControlMessage::UdpGsoSegments(gso_size) => {
799818
gso_size as *const _ as *const u8
800819
},
820+
#[cfg(any(target_os = "linux", target_os = "macos",
821+
target_os = "netbsd"))]
822+
ControlMessage::Ipv4PacketInfo(info) => info as *const _ as *const u8,
823+
#[cfg(any(target_os = "linux", target_os = "macos",
824+
target_os = "netbsd", target_os = "freebsd"))]
825+
ControlMessage::Ipv6PacketInfo(info) => info as *const _ as *const u8,
801826
};
802827
unsafe {
803828
ptr::copy_nonoverlapping(
@@ -838,6 +863,12 @@ impl<'a> ControlMessage<'a> {
838863
ControlMessage::UdpGsoSegments(gso_size) => {
839864
mem::size_of_val(gso_size)
840865
},
866+
#[cfg(any(target_os = "linux", target_os = "macos",
867+
target_os = "netbsd"))]
868+
ControlMessage::Ipv4PacketInfo(info) => mem::size_of_val(info),
869+
#[cfg(any(target_os = "linux", target_os = "macos",
870+
target_os = "netbsd", target_os = "freebsd"))]
871+
ControlMessage::Ipv6PacketInfo(info) => mem::size_of_val(info),
841872
}
842873
}
843874

@@ -854,6 +885,12 @@ impl<'a> ControlMessage<'a> {
854885
ControlMessage::AlgSetAeadAssoclen(_) => libc::SOL_ALG,
855886
#[cfg(target_os = "linux")]
856887
ControlMessage::UdpGsoSegments(_) => libc::SOL_UDP,
888+
#[cfg(any(target_os = "linux", target_os = "macos",
889+
target_os = "netbsd"))]
890+
ControlMessage::Ipv4PacketInfo(_) => libc::IPPROTO_IP,
891+
#[cfg(any(target_os = "linux", target_os = "macos",
892+
target_os = "netbsd", target_os = "freebsd"))]
893+
ControlMessage::Ipv6PacketInfo(_) => libc::IPPROTO_IPV6,
857894
}
858895
}
859896

@@ -881,6 +918,12 @@ impl<'a> ControlMessage<'a> {
881918
ControlMessage::UdpGsoSegments(_) => {
882919
libc::UDP_SEGMENT
883920
},
921+
#[cfg(any(target_os = "linux", target_os = "macos",
922+
target_os = "netbsd"))]
923+
ControlMessage::Ipv4PacketInfo(_) => libc::IP_PKTINFO,
924+
#[cfg(any(target_os = "linux", target_os = "macos",
925+
target_os = "netbsd", target_os = "freebsd"))]
926+
ControlMessage::Ipv6PacketInfo(_) => libc::IPV6_PKTINFO,
884927
}
885928
}
886929

test/sys/test_socket.rs

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -666,6 +666,111 @@ pub fn test_af_alg_aead() {
666666
assert_eq!(decrypted[(assoc_size as usize)..(payload_len + (assoc_size as usize))], payload[(assoc_size as usize)..payload_len + (assoc_size as usize)]);
667667
}
668668

669+
// Verify `ControlMessage::Ipv4PacketInfo` for `sendmsg`.
670+
// This creates a (udp) socket bound to localhost, then sends a message to
671+
// itself but uses Ipv4PacketInfo to force the source address to be localhost.
672+
//
673+
// This would be a more interesting test if we could assume that the test host
674+
// has more than one IP address (since we could select a different address to
675+
// test from).
676+
#[cfg(any(target_os = "linux",
677+
target_os = "macos",
678+
target_os = "netbsd"))]
679+
#[test]
680+
pub fn test_sendmsg_ipv4packetinfo() {
681+
use nix::sys::uio::IoVec;
682+
use nix::sys::socket::{socket, sendmsg, bind,
683+
AddressFamily, SockType, SockFlag, SockAddr,
684+
ControlMessage, MsgFlags};
685+
686+
let sock = socket(AddressFamily::Inet,
687+
SockType::Datagram,
688+
SockFlag::empty(),
689+
None)
690+
.expect("socket failed");
691+
692+
let std_sa = SocketAddr::from_str("127.0.0.1:4000").unwrap();
693+
let inet_addr = InetAddr::from_std(&std_sa);
694+
let sock_addr = SockAddr::new_inet(inet_addr);
695+
696+
bind(sock, &sock_addr).expect("bind failed");
697+
698+
let slice = [1u8, 2, 3, 4, 5, 6, 7, 8];
699+
let iov = [IoVec::from_slice(&slice)];
700+
701+
if let InetAddr::V4(sin) = inet_addr {
702+
let pi = libc::in_pktinfo {
703+
ipi_ifindex: 0, /* Unspecified interface */
704+
ipi_addr: libc::in_addr { s_addr: 0 },
705+
ipi_spec_dst: sin.sin_addr,
706+
};
707+
708+
let cmsg = [ControlMessage::Ipv4PacketInfo(&pi)];
709+
710+
sendmsg(sock, &iov, &cmsg, MsgFlags::empty(), Some(&sock_addr))
711+
.expect("sendmsg");
712+
} else {
713+
panic!("No IPv4 addresses available for testing?");
714+
}
715+
}
716+
717+
// Verify `ControlMessage::Ipv6PacketInfo` for `sendmsg`.
718+
// This creates a (udp) socket bound to ip6-localhost, then sends a message to
719+
// itself but uses Ipv6PacketInfo to force the source address to be
720+
// ip6-localhost.
721+
//
722+
// This would be a more interesting test if we could assume that the test host
723+
// has more than one IP address (since we could select a different address to
724+
// test from).
725+
#[cfg(any(target_os = "linux",
726+
target_os = "macos",
727+
target_os = "netbsd",
728+
target_os = "freebsd"))]
729+
#[test]
730+
pub fn test_sendmsg_ipv6packetinfo() {
731+
use nix::Error;
732+
use nix::errno::Errno;
733+
use nix::sys::uio::IoVec;
734+
use nix::sys::socket::{socket, sendmsg, bind,
735+
AddressFamily, SockType, SockFlag, SockAddr,
736+
ControlMessage, MsgFlags};
737+
738+
let sock = socket(AddressFamily::Inet6,
739+
SockType::Datagram,
740+
SockFlag::empty(),
741+
None)
742+
.expect("socket failed");
743+
744+
let std_sa = SocketAddr::from_str("[::1]:6000").unwrap();
745+
let inet_addr = InetAddr::from_std(&std_sa);
746+
let sock_addr = SockAddr::new_inet(inet_addr);
747+
748+
match bind(sock, &sock_addr) {
749+
Err(Error::Sys(Errno::EADDRNOTAVAIL)) => {
750+
println!("IPv6 not available, skipping test.");
751+
return;
752+
},
753+
_ => (),
754+
}
755+
756+
let slice = [1u8, 2, 3, 4, 5, 6, 7, 8];
757+
let iov = [IoVec::from_slice(&slice)];
758+
759+
if let InetAddr::V6(sin) = inet_addr {
760+
let pi = libc::in6_pktinfo {
761+
ipi6_ifindex: 0, /* Unspecified interface */
762+
ipi6_addr: sin.sin6_addr,
763+
};
764+
765+
let cmsg = [ControlMessage::Ipv6PacketInfo(&pi)];
766+
767+
sendmsg(sock, &iov, &cmsg, MsgFlags::empty(), Some(&sock_addr))
768+
.expect("sendmsg");
769+
} else {
770+
println!("No IPv6 addresses available for testing: skipping testing Ipv6PacketInfo");
771+
}
772+
}
773+
669774
/// Tests that passing multiple fds using a single `ControlMessage` works.
670775
// Disable the test on emulated platforms due to a bug in QEMU versions <
671776
// 2.12.0. https://bugs.launchpad.net/qemu/+bug/1701808

0 commit comments

Comments
 (0)