Skip to content

Commit

Permalink
datalink(linux): add feature to pass the fd (socket) to ::channel()
Browse files Browse the repository at this point in the history
This can be useful if the caller want to manipulate the
socket differently at creating. For example setting a
BPF filter on the socket before passing it in the channel:

```
datalink::linux::channel(interface, config, socket_fd)
```

Signed-off-by: Martin Andre <martin.andre@tessares.net>
  • Loading branch information
Martichou committed Jan 13, 2024
1 parent 87f362d commit 189e5c9
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 39 deletions.
14 changes: 12 additions & 2 deletions pnet_datalink/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,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 @@ -167,6 +170,7 @@ impl Default for Config {
bpf_fd_attempts: 1000,
linux_fanout: None,
promiscuous: true,
socket_fd: None,
}
}
}
Expand All @@ -183,8 +187,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

0 comments on commit 189e5c9

Please sign in to comment.