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

datalink(linux): add feature to pass the fd (socket) to ::channel() #584

Merged
merged 1 commit into from
May 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
14 changes: 12 additions & 2 deletions pnet_datalink/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,9 @@ pub struct Config {
pub linux_fanout: Option<FanoutOption>,

pub promiscuous: bool,

/// Linux only: The socket's file descriptor that pnet will use
pub socket_fd: Option<i32>,
}

impl Default for Config {
Expand All @@ -169,6 +172,7 @@ impl Default for Config {
bpf_fd_attempts: 1000,
linux_fanout: None,
promiscuous: true,
socket_fd: None,
}
}
}
Expand All @@ -185,8 +189,14 @@ impl Default for Config {
/// When matching on the returned channel, make sure to include a catch-all so that code doesn't
/// break when new channel types are added.
#[inline]
pub fn channel(network_interface: &NetworkInterface, configuration: Config) -> io::Result<Channel> {
backend::channel(network_interface, (&configuration).into())
pub fn channel(
network_interface: &NetworkInterface,
configuration: Config,
) -> io::Result<Channel> {
backend::channel(
network_interface,
(&configuration).into()
)
}

/// Trait to enable sending `$packet` packets.
Expand Down
32 changes: 21 additions & 11 deletions pnet_datalink/src/linux.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ pub struct Config {

/// Promiscuous mode.
pub promiscuous: bool,

pub socket_fd: Option<i32>,
}

impl<'a> From<&'a super::Config> for Config {
Expand All @@ -77,6 +79,7 @@ impl<'a> From<&'a super::Config> for Config {
write_timeout: config.write_timeout,
fanout: config.linux_fanout,
promiscuous: config.promiscuous,
socket_fd: config.socket_fd,
}
}
}
Expand All @@ -91,25 +94,32 @@ impl Default for Config {
channel_type: super::ChannelType::Layer2,
fanout: None,
promiscuous: true,
socket_fd: None,
}
}
}

/// Create a data link channel using the Linux's `AF_PACKET` socket type.
#[inline]
pub fn channel(network_interface: &NetworkInterface, config: Config) -> io::Result<super::Channel> {
let eth_p_all = 0x0003;
let (typ, proto) = match config.channel_type {
super::ChannelType::Layer2 => (libc::SOCK_RAW, eth_p_all),
super::ChannelType::Layer3(proto) => (libc::SOCK_DGRAM, proto),
pub fn channel(
network_interface: &NetworkInterface,
config: Config,
) -> io::Result<super::Channel> {
let (_typ, proto) = match config.channel_type {
super::ChannelType::Layer2 => (libc::SOCK_RAW, libc::ETH_P_ALL),
super::ChannelType::Layer3(proto) => (libc::SOCK_DGRAM, proto as i32),
};
let socket = unsafe { libc::socket(libc::AF_PACKET, typ, proto.to_be() as i32) };
if socket == -1 {
return Err(io::Error::last_os_error());
}
let mut addr: libc::sockaddr_storage = unsafe { mem::zeroed() };
let len = network_addr_to_sockaddr(network_interface, &mut addr, proto as i32);

let socket = match config.socket_fd {
Some(sock) => sock,
None => match unsafe { libc::socket(libc::AF_PACKET, _typ, proto.to_be()) } {
-1 => return Err(io::Error::last_os_error()),
fd => fd
}
};

let mut addr: libc::sockaddr_storage = unsafe { mem::zeroed() };
let len = network_addr_to_sockaddr(network_interface, &mut addr, proto);
let send_addr = (&addr as *const libc::sockaddr_storage) as *const libc::sockaddr;

// Bind to interface
Expand Down
1 change: 0 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@

#![deny(missing_docs)]
#![deny(warnings)]

#![cfg_attr(not(feature = "std"), no_std)]
#![cfg_attr(feature = "nightly", feature(custom_attribute, plugin))]
#![cfg_attr(feature = "nightly", plugin(pnet_macros_plugin))]
Expand Down
92 changes: 67 additions & 25 deletions src/pnettest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@ use crate::packet::ipv6::MutableIpv6Packet;
use crate::packet::udp;
use crate::packet::udp::{MutableUdpPacket, UdpPacket};
use crate::packet::Packet;
use std::iter::Iterator;
use pnet_base::core_net::{IpAddr, Ipv4Addr, Ipv6Addr};
use std::sync::mpsc::channel;
use std::thread;
use crate::transport::TransportProtocol::{Ipv4, Ipv6};
use crate::transport::{
ipv4_packet_iter, transport_channel, udp_packet_iter, TransportChannelType, TransportProtocol,
};
use pnet_base::core_net::{IpAddr, Ipv4Addr, Ipv6Addr};
use std::iter::Iterator;
use std::sync::mpsc::channel;
use std::thread;

const IPV4_HEADER_LEN: usize = 20;
const IPV6_HEADER_LEN: usize = 40;
Expand All @@ -51,7 +51,8 @@ fn ipv6_destination() -> Ipv6Addr {
const TEST_PROTO: IpNextHeaderProtocol = IpNextHeaderProtocols::Test1;

fn build_ipv4_header(packet: &mut [u8], offset: usize) {
let mut ip_header = MutableIpv4Packet::new(&mut packet[offset..]).expect("could not create MutableIpv4Packet");
let mut ip_header =
MutableIpv4Packet::new(&mut packet[offset..]).expect("could not create MutableIpv4Packet");

let total_len = (IPV4_HEADER_LEN + UDP_HEADER_LEN + TEST_DATA_LEN) as u16;

Expand All @@ -67,7 +68,8 @@ fn build_ipv4_header(packet: &mut [u8], offset: usize) {
}

fn build_ipv6_header(packet: &mut [u8], offset: usize) {
let mut ip_header = MutableIpv6Packet::new(&mut packet[offset..]).expect("could not create MutableIpv6Packet");
let mut ip_header =
MutableIpv6Packet::new(&mut packet[offset..]).expect("could not create MutableIpv6Packet");

ip_header.set_version(6);
ip_header.set_payload_length((UDP_HEADER_LEN + TEST_DATA_LEN) as u16);
Expand All @@ -78,7 +80,8 @@ fn build_ipv6_header(packet: &mut [u8], offset: usize) {
}

fn build_udp_header(packet: &mut [u8], offset: usize) {
let mut udp_header = MutableUdpPacket::new(&mut packet[offset..]).expect("could not create MutableUdpPacket");
let mut udp_header =
MutableUdpPacket::new(&mut packet[offset..]).expect("could not create MutableUdpPacket");

udp_header.set_source(1234); // Arbitrary port number
udp_header.set_destination(1234);
Expand Down Expand Up @@ -126,8 +129,14 @@ fn build_udp4_packet(
};

let slice = &mut packet[(start + IPV4_HEADER_LEN as usize)..];
let checksum = udp::ipv4_checksum(&UdpPacket::new(slice).expect("could not create UdpPacket"), &source, &dest);
MutableUdpPacket::new(slice).expect("could not create MutableUdpPacket").set_checksum(checksum);
let checksum = udp::ipv4_checksum(
&UdpPacket::new(slice).expect("could not create UdpPacket"),
&source,
&dest,
);
MutableUdpPacket::new(slice)
.expect("could not create MutableUdpPacket")
.set_checksum(checksum);
}

fn build_udp6_packet(packet: &mut [u8], start: usize, msg: &str) {
Expand All @@ -148,7 +157,9 @@ fn build_udp6_packet(packet: &mut [u8], start: usize, msg: &str) {
&ipv6_source(),
&ipv6_destination(),
);
MutableUdpPacket::new(slice).expect("could not create MutableUdpPacket").set_checksum(checksum);
MutableUdpPacket::new(slice)
.expect("could not create MutableUdpPacket")
.set_checksum(checksum);
}

// OSes have a nasty habit of tweaking IP fields, so we only check
Expand Down Expand Up @@ -204,7 +215,8 @@ fn layer4(ip: IpAddr, header_len: usize) {
assert_eq!(addr, ip);
assert_eq!(
header,
UdpPacket::new(&packet[header_len..packet_len]).expect("could not create UdpPacket")
UdpPacket::new(&packet[header_len..packet_len])
.expect("could not create UdpPacket")
);
break;
}
Expand All @@ -215,7 +227,8 @@ fn layer4(ip: IpAddr, header_len: usize) {
}
});

rx.recv().expect("failed to receive message through channel");
rx.recv()
.expect("failed to receive message through channel");
match ttx.send_to(udp, ip) {
Ok(res) => assert_eq!(res as usize, UDP_HEADER_LEN + TEST_DATA_LEN),
Err(e) => panic!("layer4_test failed: {}", e),
Expand Down Expand Up @@ -281,7 +294,8 @@ fn layer3_ipv4() {
.expect("could not create UdpPacket");
assert_eq!(
udp_header,
UdpPacket::new(&packet[IPV4_HEADER_LEN..]).expect("could not create UdpPacket")
UdpPacket::new(&packet[IPV4_HEADER_LEN..])
.expect("could not create UdpPacket")
);

assert_eq!(
Expand All @@ -297,8 +311,12 @@ fn layer3_ipv4() {
}
});

rx.recv().expect("unable to receive message through channel");
match ttx.send_to(Ipv4Packet::new(&packet[..]).expect("could not create Ipv4Packet"), send_addr) {
rx.recv()
.expect("unable to receive message through channel");
match ttx.send_to(
Ipv4Packet::new(&packet[..]).expect("could not create Ipv4Packet"),
send_addr,
) {
Ok(res) => assert_eq!(res as usize, packet.len()),
Err(e) => panic!("layer3_ipv4_test failed: {}", e),
}
Expand Down Expand Up @@ -355,9 +373,18 @@ fn layer2() {
let mut packet = [0u8; ETHERNET_HEADER_LEN + IPV4_HEADER_LEN + UDP_HEADER_LEN + TEST_DATA_LEN];

{
let mut ethernet_header = MutableEthernetPacket::new(&mut packet[..]).expect("could not create MutableEthernetPacket");
ethernet_header.set_source(interface.mac.expect("could not find mac address for test interface"));
ethernet_header.set_destination(interface.mac.expect("could not find mac address for test interface"));
let mut ethernet_header = MutableEthernetPacket::new(&mut packet[..])
.expect("could not create MutableEthernetPacket");
ethernet_header.set_source(
interface
.mac
.expect("could not find mac address for test interface"),
);
ethernet_header.set_destination(
interface
.mac
.expect("could not find mac address for test interface"),
);
ethernet_header.set_ethertype(EtherTypes::Ipv4);
}

Expand Down Expand Up @@ -394,8 +421,12 @@ fn layer2() {
if i == 10_000 {
panic!("layer2: did not find matching packet after 10_000 iterations");
}
if EthernetPacket::new(&packet[..]).expect("failed to create EthernetPacket").payload()
== EthernetPacket::new(eh).expect("failed to create EthernetPacket").payload()
if EthernetPacket::new(&packet[..])
.expect("failed to create EthernetPacket")
.payload()
== EthernetPacket::new(eh)
.expect("failed to create EthernetPacket")
.payload()
{
return;
}
Expand All @@ -408,7 +439,8 @@ fn layer2() {
}
});

rx.recv().expect("failed to receive message through channel");
rx.recv()
.expect("failed to receive message through channel");
match dltx.send_to(&packet[..], None) {
Some(Ok(())) => (),
Some(Err(e)) => panic!("layer2_test failed: {}", e),
Expand All @@ -434,9 +466,18 @@ fn layer2_timeouts() {
let mut packet = [0u8; ETHERNET_HEADER_LEN + IPV4_HEADER_LEN + UDP_HEADER_LEN + TEST_DATA_LEN];

{
let mut ethernet_header = MutableEthernetPacket::new(&mut packet[..]).expect("failed to create MutableEthernetPacket");
ethernet_header.set_source(interface.mac.expect("missing mac address for test interface"));
ethernet_header.set_destination(interface.mac.expect("missing mac address for test interface"));
let mut ethernet_header = MutableEthernetPacket::new(&mut packet[..])
.expect("failed to create MutableEthernetPacket");
ethernet_header.set_source(
interface
.mac
.expect("missing mac address for test interface"),
);
ethernet_header.set_destination(
interface
.mac
.expect("missing mac address for test interface"),
);
ethernet_header.set_ethertype(EtherTypes::Ipv4);
}

Expand Down Expand Up @@ -487,7 +528,8 @@ fn layer2_timeouts() {
}
}
});
rx.recv().expect("failed to receive message through channel");
rx.recv()
.expect("failed to receive message through channel");

// Wait a while
thread::sleep(Duration::from_millis(1000));
Expand Down