net: raw packet tx capture
This commit is contained in:
parent
b8078561bf
commit
3f62374431
@ -65,7 +65,7 @@ pub fn handle(packet: L2Packet) {
|
||||
let frame = packet.ethernet_frame();
|
||||
let ty = EtherType::from_network_order(frame.ethertype);
|
||||
|
||||
RawSocket::packet_received(packet.clone());
|
||||
RawSocket::handle_rx(packet.clone());
|
||||
|
||||
match ty {
|
||||
EtherType::ARP => l3::arp::handle_packet(packet),
|
||||
|
@ -16,7 +16,10 @@ use yggdrasil_abi::{
|
||||
net::{link::LinkState, protocols::EthernetFrame, MacAddress},
|
||||
};
|
||||
|
||||
use crate::l3::{arp::ArpTable, Route};
|
||||
use crate::{
|
||||
l3::{arp::ArpTable, Route},
|
||||
TxPacketBuilder,
|
||||
};
|
||||
|
||||
pub trait NetworkDevice: Sync + Send {
|
||||
fn allocate_transmit_buffer(&self, len: usize) -> Result<DmaBuffer<[MaybeUninit<u8>]>, Error>;
|
||||
@ -109,18 +112,10 @@ impl NetworkInterface {
|
||||
}
|
||||
|
||||
pub fn send_l2(&self, l2_frame: &EthernetFrame, l2_data: &[u8]) -> Result<(), Error> {
|
||||
let l2_offset = self.device.packet_prefix_size();
|
||||
let l2_data_offset = l2_offset + size_of::<EthernetFrame>();
|
||||
|
||||
let packet = self
|
||||
.device
|
||||
.allocate_transmit_buffer(l2_data_offset + l2_data.len())?;
|
||||
let mut packet = unsafe { DmaBuffer::assume_init_slice(packet) };
|
||||
|
||||
packet[l2_offset..l2_data_offset].copy_from_slice(bytemuck::bytes_of(l2_frame));
|
||||
packet[l2_data_offset..l2_data_offset + l2_data.len()].copy_from_slice(l2_data);
|
||||
|
||||
self.device.transmit_buffer(packet)
|
||||
let mut builder = TxPacketBuilder::new(self, size_of::<EthernetFrame>() + l2_data.len())?;
|
||||
builder.push_bytes(bytemuck::bytes_of(l2_frame))?;
|
||||
builder.push_bytes(l2_data)?;
|
||||
builder.transmit(None)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,7 @@ use yggdrasil_abi::{
|
||||
},
|
||||
};
|
||||
|
||||
use crate::{interface::NetworkInterface, l4, PacketBuilder};
|
||||
use crate::{interface::NetworkInterface, l4, TxPacketBuilder};
|
||||
|
||||
pub mod arp;
|
||||
pub mod ip;
|
||||
@ -54,6 +54,7 @@ pub struct Route {
|
||||
pub subnet: SubnetAddr,
|
||||
pub interface: u32,
|
||||
pub gateway: Option<IpAddr>,
|
||||
pub priority: u32,
|
||||
}
|
||||
|
||||
pub struct L4ResolvedPacket<'a, 'i> {
|
||||
@ -114,12 +115,20 @@ impl Route {
|
||||
}
|
||||
|
||||
let routes = ROUTES.read();
|
||||
let mut best: Option<(&Route, IpAddr)> = None;
|
||||
for route in routes.iter() {
|
||||
if route.subnet.contains(&address) {
|
||||
return Some((route.interface, route.gateway, address));
|
||||
if route.subnet.contains(&address) || route.subnet.base() == address {
|
||||
if let Some((prev_best, _)) = best.as_ref() {
|
||||
if route.priority < prev_best.priority {
|
||||
best = Some((route, address));
|
||||
}
|
||||
} else {
|
||||
best = Some((route, address));
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
let (best, address) = best?;
|
||||
Some((best.interface, best.gateway, address))
|
||||
}
|
||||
|
||||
pub fn insert(route: Self) -> Result<(), Error> {
|
||||
@ -220,8 +229,8 @@ pub async fn send_l4_ip_resolved(packet: &L4ResolvedPacket<'_, '_>) -> Result<()
|
||||
|
||||
let l3_frame = packet.make_l3_frame()?;
|
||||
|
||||
let mut builder = PacketBuilder::new(
|
||||
packet.interface.device.as_ref(),
|
||||
let mut builder = TxPacketBuilder::new(
|
||||
packet.interface,
|
||||
size_of::<EthernetFrame>() + size_of::<Ipv4Frame>() + packet.total_l4_len(),
|
||||
)?;
|
||||
builder.push(&EthernetFrame {
|
||||
@ -233,9 +242,7 @@ pub async fn send_l4_ip_resolved(packet: &L4ResolvedPacket<'_, '_>) -> Result<()
|
||||
builder.push_bytes(packet.l4_frame)?;
|
||||
builder.push_bytes(packet.l4_options)?;
|
||||
builder.push_bytes(packet.l4_data)?;
|
||||
|
||||
let (sent_packet, _len) = builder.finish();
|
||||
packet.interface.device.transmit_buffer(sent_packet)
|
||||
builder.transmit(None)
|
||||
}
|
||||
|
||||
pub async fn send_l4_ip(packet: &L4UnresolvedPacket<'_>) -> Result<(), Error> {
|
||||
|
@ -8,10 +8,14 @@ use yggdrasil_abi::{
|
||||
net::{
|
||||
protocols::{IcmpV4Frame, InetChecksum, IpProtocol},
|
||||
types::NetValueImpl,
|
||||
SubnetAddr, SubnetV4Addr,
|
||||
},
|
||||
};
|
||||
|
||||
use crate::{l3, L3Packet};
|
||||
use crate::{
|
||||
l3::{self, Route},
|
||||
L3Packet,
|
||||
};
|
||||
|
||||
async fn send_v4_reply(
|
||||
destination_ip: Ipv4Addr,
|
||||
@ -64,6 +68,20 @@ async fn handle_v4(source_address: Ipv4Addr, l3_packet: L3Packet) -> Result<(),
|
||||
|
||||
match (icmp_frame.ty, icmp_frame.code) {
|
||||
(8, 0) => send_v4_reply(source_address, icmp_frame, icmp_data).await,
|
||||
// ICMP redirect to host
|
||||
(5, 1) => {
|
||||
// TODO don't just accept whatever gateway is supplied, it's a MitM asking to happen
|
||||
let gateway = Ipv4Addr::from(u32::from_network_order(icmp_frame.rest));
|
||||
log::warn!("ICMP redirect to host: {gateway}");
|
||||
Route::insert(Route {
|
||||
priority: 5,
|
||||
interface: l3_packet.interface_id,
|
||||
gateway: Some(IpAddr::V4(gateway)),
|
||||
subnet: SubnetAddr::V4(SubnetV4Addr::from_address_mask(gateway, 32)),
|
||||
})
|
||||
.ok();
|
||||
Ok(())
|
||||
}
|
||||
(0, 0) => Ok(()),
|
||||
_ => {
|
||||
log::debug!(
|
||||
|
@ -9,10 +9,11 @@ use core::mem::size_of;
|
||||
use alloc::sync::Arc;
|
||||
use bytemuck::Pod;
|
||||
use ethernet::L2Packet;
|
||||
use interface::NetworkDevice;
|
||||
use interface::{NetworkDevice, NetworkInterface};
|
||||
use l3::L3Packet;
|
||||
use libk::{dma::DmaBuffer, task::runtime};
|
||||
use libk_util::queue::UnboundedMpmcQueue;
|
||||
use socket::RawSocket;
|
||||
use yggdrasil_abi::{error::Error, net::protocols::EthernetFrame};
|
||||
|
||||
pub mod ethernet;
|
||||
@ -26,28 +27,29 @@ pub mod util;
|
||||
|
||||
pub use interface::register_interface;
|
||||
|
||||
pub struct Packet {
|
||||
// TODO info about "received" interface
|
||||
pub struct RxPacket {
|
||||
buffer: DmaBuffer<[u8]>,
|
||||
offset: usize,
|
||||
iface: u32,
|
||||
}
|
||||
|
||||
pub struct PacketBuilder {
|
||||
pub struct TxPacketBuilder<'a> {
|
||||
nic: &'a NetworkInterface,
|
||||
data: DmaBuffer<[u8]>,
|
||||
pos: usize,
|
||||
len: usize,
|
||||
l2_offset: usize,
|
||||
}
|
||||
|
||||
impl PacketBuilder {
|
||||
pub fn new(nic: &dyn NetworkDevice, l2_size: usize) -> Result<Self, Error> {
|
||||
let l2_offset = nic.packet_prefix_size();
|
||||
let data = nic.allocate_transmit_buffer(l2_offset + l2_size)?;
|
||||
impl<'a> TxPacketBuilder<'a> {
|
||||
pub fn new(nic: &'a NetworkInterface, l2_size: usize) -> Result<Self, Error> {
|
||||
let l2_offset = nic.device.packet_prefix_size();
|
||||
let data = nic.device.allocate_transmit_buffer(l2_offset + l2_size)?;
|
||||
let data = unsafe { DmaBuffer::assume_init_slice(data) };
|
||||
Ok(Self {
|
||||
nic,
|
||||
data,
|
||||
pos: l2_offset,
|
||||
len: l2_offset,
|
||||
l2_offset,
|
||||
})
|
||||
}
|
||||
|
||||
@ -65,12 +67,20 @@ impl PacketBuilder {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn finish(self) -> (DmaBuffer<[u8]>, usize) {
|
||||
(self.data, self.len)
|
||||
fn finish(self) -> (DmaBuffer<[u8]>, usize) {
|
||||
(self.data, self.pos)
|
||||
}
|
||||
|
||||
pub fn transmit(self, mute_raw: Option<u32>) -> Result<(), Error> {
|
||||
let nic = self.nic;
|
||||
let (data, len) = self.finish();
|
||||
// Also transmit to raw
|
||||
RawSocket::handle_tx(nic.id, &data, len, mute_raw);
|
||||
nic.device.transmit_buffer(data)
|
||||
}
|
||||
}
|
||||
|
||||
impl Packet {
|
||||
impl RxPacket {
|
||||
#[inline]
|
||||
pub fn new(buffer: DmaBuffer<[u8]>, offset: usize, iface: u32) -> Self {
|
||||
Self {
|
||||
@ -81,11 +91,11 @@ impl Packet {
|
||||
}
|
||||
}
|
||||
|
||||
static PACKET_QUEUE: UnboundedMpmcQueue<Packet> = UnboundedMpmcQueue::new();
|
||||
static PACKET_QUEUE: UnboundedMpmcQueue<RxPacket> = UnboundedMpmcQueue::new();
|
||||
static ACCEPT_QUEUE: UnboundedMpmcQueue<L3Packet> = UnboundedMpmcQueue::new();
|
||||
|
||||
#[inline]
|
||||
pub fn receive_packet(packet: Packet) -> Result<(), Error> {
|
||||
pub fn receive_packet(packet: RxPacket) -> Result<(), Error> {
|
||||
PACKET_QUEUE.push_back(packet);
|
||||
Ok(())
|
||||
}
|
||||
|
@ -276,6 +276,7 @@ fn add_route(
|
||||
interface: interface.id,
|
||||
gateway,
|
||||
subnet,
|
||||
priority: 10,
|
||||
};
|
||||
Route::insert(route).map_err(|_| "Could not insert route")?;
|
||||
Ok(())
|
||||
|
@ -13,6 +13,7 @@ use libk::{
|
||||
task::runtime::maybe_timeout,
|
||||
vfs::{FileReadiness, Socket},
|
||||
};
|
||||
use libk_mm::PageBox;
|
||||
use libk_util::{queue::BoundedMpmcQueue, sync::spin_rwlock::IrqSafeRwLock};
|
||||
use yggdrasil_abi::{
|
||||
net::{
|
||||
@ -22,12 +23,17 @@ use yggdrasil_abi::{
|
||||
option::OptionValue,
|
||||
};
|
||||
|
||||
use crate::{ethernet::L2Packet, interface::NetworkInterface};
|
||||
use crate::{ethernet::L2Packet, interface::NetworkInterface, TxPacketBuilder};
|
||||
|
||||
enum RawPacket {
|
||||
Ingress(L2Packet),
|
||||
Egress(Arc<PageBox<[u8]>>),
|
||||
}
|
||||
|
||||
pub struct RawSocket {
|
||||
id: u32,
|
||||
bound: IrqSafeRwLock<Option<u32>>,
|
||||
receive_queue: BoundedMpmcQueue<L2Packet>,
|
||||
receive_queue: BoundedMpmcQueue<RawPacket>,
|
||||
}
|
||||
|
||||
static RAW_SOCKET_ID: AtomicU32 = AtomicU32::new(0);
|
||||
@ -48,31 +54,64 @@ impl RawSocket {
|
||||
socket
|
||||
}
|
||||
|
||||
fn bound_packet_received(&self, packet: L2Packet) {
|
||||
fn bound_packet_received(&self, packet: RawPacket) {
|
||||
// TODO do something with the dropped packet?
|
||||
self.receive_queue.try_push_back(packet).ok();
|
||||
}
|
||||
|
||||
pub fn packet_received(packet: L2Packet) {
|
||||
pub fn handle_rx(packet: L2Packet) {
|
||||
let bound_sockets = BOUND_RAW_SOCKETS.read();
|
||||
let raw_sockets = RAW_SOCKETS.read();
|
||||
|
||||
if let Some(ids) = bound_sockets.get(&packet.interface_id) {
|
||||
for id in ids {
|
||||
let socket = raw_sockets.get(id).unwrap();
|
||||
socket.bound_packet_received(packet.clone());
|
||||
socket.bound_packet_received(RawPacket::Ingress(packet.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn packet_to_user(packet: L2Packet, buffer: &mut [u8]) -> Result<usize, Error> {
|
||||
let full_len = packet.data.len();
|
||||
let len = full_len - packet.l2_offset;
|
||||
if buffer.len() < len {
|
||||
return Err(Error::BufferTooSmall);
|
||||
pub fn handle_tx(iface: u32, packet: &DmaBuffer<[u8]>, len: usize, except: Option<u32>) {
|
||||
let bound_sockets = BOUND_RAW_SOCKETS.read();
|
||||
let raw_sockets = RAW_SOCKETS.read();
|
||||
|
||||
if let Some(ids) = bound_sockets.get(&iface) {
|
||||
let Ok(egress) = PageBox::from_slice(&packet[..len]).map(Arc::new) else {
|
||||
return;
|
||||
};
|
||||
|
||||
for id in ids {
|
||||
if except.map_or(false, |i| i == *id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let socket = raw_sockets.get(id).unwrap();
|
||||
socket.bound_packet_received(RawPacket::Egress(egress.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn packet_to_user(packet: RawPacket, buffer: &mut [u8]) -> Result<usize, Error> {
|
||||
match packet {
|
||||
RawPacket::Egress(egress) => {
|
||||
// TODO l2offset in egress?
|
||||
let len = egress.len();
|
||||
if buffer.len() < len {
|
||||
return Err(Error::BufferTooSmall);
|
||||
}
|
||||
buffer[..len].copy_from_slice(&egress[..]);
|
||||
Ok(len)
|
||||
}
|
||||
RawPacket::Ingress(ingress) => {
|
||||
let full_len = ingress.data.len();
|
||||
let len = full_len - ingress.l2_offset;
|
||||
if buffer.len() < len {
|
||||
return Err(Error::BufferTooSmall);
|
||||
}
|
||||
buffer[..len].copy_from_slice(&ingress.data[ingress.l2_offset..full_len]);
|
||||
Ok(len)
|
||||
}
|
||||
}
|
||||
buffer[..len].copy_from_slice(&packet.data[packet.l2_offset..full_len]);
|
||||
Ok(len)
|
||||
}
|
||||
}
|
||||
|
||||
@ -167,16 +206,14 @@ impl Socket for RawSocket {
|
||||
// TODO cap by MTU?
|
||||
let bound = self.bound.read().ok_or(Error::InvalidOperation)?;
|
||||
let interface = NetworkInterface::get(bound)?;
|
||||
let l2_offset = interface.device.packet_prefix_size();
|
||||
if message.payload.len() > 4096 - l2_offset {
|
||||
// let l2_offset = interface.device.packet_prefix_size();
|
||||
if message.payload.len() > 1024 {
|
||||
return Err(Error::InvalidArgument);
|
||||
}
|
||||
let packet = interface
|
||||
.device
|
||||
.allocate_transmit_buffer(l2_offset + message.payload.len())?;
|
||||
let mut packet = unsafe { DmaBuffer::assume_init_slice(packet) };
|
||||
packet[l2_offset..l2_offset + message.payload.len()].copy_from_slice(message.payload);
|
||||
interface.device.transmit_buffer(packet)?;
|
||||
let mut builder = TxPacketBuilder::new(&*interface, message.payload.len())?;
|
||||
builder.push_bytes(message.payload)?;
|
||||
// false to prevent loopback
|
||||
builder.transmit(Some(self.id))?;
|
||||
Ok(message.payload.len())
|
||||
}
|
||||
async fn send_message(
|
||||
|
@ -21,7 +21,7 @@ use regs::{Regs, ICR};
|
||||
use tock_registers::interfaces::Writeable;
|
||||
use ygg_driver_net_core::{
|
||||
interface::{NetworkDevice, NetworkInterfaceType},
|
||||
register_interface, Packet,
|
||||
register_interface, RxPacket,
|
||||
};
|
||||
use ygg_driver_pci::{
|
||||
device::{PciDeviceInfo, PreferredInterruptMode},
|
||||
@ -322,7 +322,7 @@ impl InterruptHandler for Igbe {
|
||||
let nic = *self.nic.get();
|
||||
let head = regs.rx_queue_head();
|
||||
let tail = rx.handle_rx(&*self.dma, head, |packet, _| {
|
||||
let packet = Packet::new(packet, 0, nic);
|
||||
let packet = RxPacket::new(packet, 0, nic);
|
||||
ygg_driver_net_core::receive_packet(packet).ok();
|
||||
});
|
||||
regs.set_rx_queue_tail(tail);
|
||||
|
@ -13,7 +13,7 @@ use libk::dma::{DmaBuffer, DummyDmaAllocator};
|
||||
use libk_util::OneTimeInit;
|
||||
use ygg_driver_net_core::{
|
||||
interface::{NetworkDevice, NetworkInterfaceType},
|
||||
Packet,
|
||||
RxPacket,
|
||||
};
|
||||
use yggdrasil_abi::{error::Error, net::MacAddress};
|
||||
|
||||
@ -27,7 +27,7 @@ impl NetworkDevice for LoopbackDevice {
|
||||
}
|
||||
|
||||
fn transmit_buffer(&self, buffer: DmaBuffer<[u8]>) -> Result<(), Error> {
|
||||
let packet = Packet::new(buffer, 0, *LOOPBACK_ID.get());
|
||||
let packet = RxPacket::new(buffer, 0, *LOOPBACK_ID.get());
|
||||
ygg_driver_net_core::receive_packet(packet)
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,7 @@ use tock_registers::{
|
||||
};
|
||||
use ygg_driver_net_core::{
|
||||
interface::{NetworkDevice, NetworkInterfaceType},
|
||||
Packet,
|
||||
RxPacket,
|
||||
};
|
||||
use ygg_driver_pci::device::PciDeviceInfo;
|
||||
use yggdrasil_abi::net::MacAddress;
|
||||
@ -325,7 +325,7 @@ impl InterruptHandler for Rtl8139 {
|
||||
packet_buf.copy_from_slice(&rx.buffer[rx_pos + 4..rx_pos + rx_len + 4]);
|
||||
let packet_buf = unsafe { DmaBuffer::assume_init_slice(packet_buf) };
|
||||
// let packet_buf = unsafe { packet_buf.assume_init_slice() };
|
||||
let packet = Packet::new(packet_buf, 0, nic);
|
||||
let packet = RxPacket::new(packet_buf, 0, nic);
|
||||
ygg_driver_net_core::receive_packet(packet).ok();
|
||||
}
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ use tock_registers::{
|
||||
};
|
||||
use ygg_driver_net_core::{
|
||||
interface::{NetworkDevice, NetworkInterfaceType},
|
||||
Packet,
|
||||
RxPacket,
|
||||
};
|
||||
use ygg_driver_pci::device::{PciDeviceInfo, PreferredInterruptMode};
|
||||
use yggdrasil_abi::{
|
||||
@ -579,7 +579,7 @@ impl InterruptHandler for Rtl8168 {
|
||||
let count = rx
|
||||
.consume(&*self.dma, |buffer| {
|
||||
// TODO add packet len hint to packets
|
||||
let packet = Packet::new(buffer, 0, nic);
|
||||
let packet = RxPacket::new(buffer, 0, nic);
|
||||
ygg_driver_net_core::receive_packet(packet).ok();
|
||||
})
|
||||
.unwrap_or(0);
|
||||
|
@ -19,7 +19,7 @@ use libk_util::{
|
||||
};
|
||||
use ygg_driver_net_core::{
|
||||
interface::{NetworkDevice, NetworkInterfaceType},
|
||||
Packet,
|
||||
RxPacket,
|
||||
};
|
||||
use ygg_driver_pci::{
|
||||
device::{PciDeviceInfo, PreferredInterruptMode},
|
||||
@ -130,7 +130,7 @@ impl<T: Transport + 'static> VirtioNet<T> {
|
||||
pending_packets.insert(token, buffer);
|
||||
|
||||
let packet = unsafe { DmaBuffer::assume_init_slice(packet) };
|
||||
let packet = Packet::new(packet, size_of::<VirtioPacketHeader>(), interface_id);
|
||||
let packet = RxPacket::new(packet, size_of::<VirtioPacketHeader>(), interface_id);
|
||||
ygg_driver_net_core::receive_packet(packet).unwrap();
|
||||
count += 1
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
step_trait,
|
||||
const_trait_impl,
|
||||
maybe_uninit_as_bytes,
|
||||
maybe_uninit_write_slice,
|
||||
negative_impls
|
||||
)]
|
||||
#![no_std]
|
||||
@ -123,6 +124,15 @@ impl<T> PageBox<T, GlobalPhysicalAllocator> {
|
||||
pub unsafe fn from_physical_raw(address: PhysicalAddress) -> PageBox<T> {
|
||||
PageBox::from_physical_raw_in(address)
|
||||
}
|
||||
|
||||
pub fn from_slice(slice: &[T]) -> Result<PageBox<[T]>, Error>
|
||||
where
|
||||
T: Copy,
|
||||
{
|
||||
let mut uninit = Self::new_uninit_slice(slice.len())?;
|
||||
MaybeUninit::copy_from_slice(&mut uninit[..], slice);
|
||||
Ok(unsafe { uninit.assume_init_slice() })
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, A: PhysicalMemoryAllocator<Address = PhysicalAddress>> PageBox<T, A> {
|
||||
|
@ -247,9 +247,6 @@ impl AArch64 {
|
||||
Cpu::init_local(None, per_cpu);
|
||||
|
||||
if is_bsp {
|
||||
// Will register drivers
|
||||
call_init_array();
|
||||
|
||||
atomic::compiler_fence(Ordering::SeqCst);
|
||||
|
||||
debug::init();
|
||||
@ -258,6 +255,9 @@ impl AArch64 {
|
||||
let bootargs = dt.chosen_bootargs().unwrap_or("");
|
||||
config::parse_boot_arguments(bootargs);
|
||||
|
||||
// Will register drivers
|
||||
call_init_array();
|
||||
|
||||
// Create device tree sysfs nodes
|
||||
device_tree::util::create_sysfs_nodes(dt);
|
||||
|
||||
|
@ -64,6 +64,11 @@ impl SubnetV4Addr {
|
||||
(address & self.mask) == root
|
||||
}
|
||||
|
||||
/// Returns the base address of the subnet
|
||||
pub fn base(&self) -> Ipv4Addr {
|
||||
self.root
|
||||
}
|
||||
|
||||
fn make_root_mask(bits: u8) -> u32 {
|
||||
!((1 << (32 - bits)) - 1)
|
||||
}
|
||||
@ -106,6 +111,13 @@ impl SubnetAddr {
|
||||
(_, _) => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the base address of this subnet
|
||||
pub fn base(&self) -> IpAddr {
|
||||
match self {
|
||||
Self::V4(v4) => IpAddr::V4(v4.base()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for SubnetAddr {
|
||||
|
@ -44,3 +44,7 @@ path = "src/dnsq.rs"
|
||||
[[bin]]
|
||||
name = "ping"
|
||||
path = "src/ping.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "ncap"
|
||||
path = "src/ncap.rs"
|
||||
|
176
userspace/netutils/src/ncap.rs
Normal file
176
userspace/netutils/src/ncap.rs
Normal file
@ -0,0 +1,176 @@
|
||||
#![feature(yggdrasil_os, rustc_private)]
|
||||
use std::{
|
||||
fs::File,
|
||||
io::{self, BufWriter, Write},
|
||||
os::yggdrasil::{
|
||||
io::net::raw_socket::RawSocket,
|
||||
signal::{set_signal_handler, SignalHandler},
|
||||
},
|
||||
path::{Path, PathBuf},
|
||||
process::ExitCode,
|
||||
};
|
||||
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
use clap::Parser;
|
||||
use ::yggdrasil_abi::net::protocols::EthernetFrame;
|
||||
use runtime::abi as yggdrasil_abi;
|
||||
use yggdrasil_abi::process::Signal;
|
||||
|
||||
#[derive(Debug, Clone, Copy, Pod, Zeroable)]
|
||||
#[repr(C)]
|
||||
struct PcapFileHeader {
|
||||
magic: u32,
|
||||
version_major: u16,
|
||||
version_minor: u16,
|
||||
_0: [u32; 2],
|
||||
snap_len: u32,
|
||||
link_type: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Pod, Zeroable)]
|
||||
#[repr(C)]
|
||||
struct PcapRecordHeader {
|
||||
ts_seconds: u32,
|
||||
ts_nanoseconds: u32,
|
||||
captured_len: u32,
|
||||
original_len: u32,
|
||||
}
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
struct Args {
|
||||
#[clap(
|
||||
short,
|
||||
help = "Write captured packets to a PCAP file (Default: write to stdout)"
|
||||
)]
|
||||
output: Option<PathBuf>,
|
||||
#[clap(
|
||||
short,
|
||||
help = "When writing PCAP output to a file, also print packet information to stdout"
|
||||
)]
|
||||
noisy: bool,
|
||||
#[clap(help = "Interface to capture from")]
|
||||
interface: String,
|
||||
}
|
||||
|
||||
struct PcapFile {
|
||||
writer: BufWriter<File>,
|
||||
header_written: bool,
|
||||
}
|
||||
|
||||
impl PcapFile {
|
||||
const MAGIC: u32 = 0xA1B2C3D4;
|
||||
const VERSION_MAJOR: u16 = 2;
|
||||
const VERSION_MINOR: u16 = 0;
|
||||
|
||||
fn open<P: AsRef<Path>>(path: P) -> io::Result<Self> {
|
||||
File::create(path)
|
||||
.map(BufWriter::new)
|
||||
.map(Self::from_writer)
|
||||
}
|
||||
|
||||
fn from_writer(writer: BufWriter<File>) -> Self {
|
||||
Self {
|
||||
writer,
|
||||
header_written: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn write(&mut self, payload: &[u8]) -> io::Result<()> {
|
||||
if !self.header_written {
|
||||
// Write the file header
|
||||
let header = PcapFileHeader {
|
||||
magic: Self::MAGIC.to_be(),
|
||||
version_minor: Self::VERSION_MINOR.to_be(),
|
||||
version_major: Self::VERSION_MAJOR.to_be(),
|
||||
_0: [0; 2],
|
||||
snap_len: 4096u32.to_be(),
|
||||
link_type: 0x1u32.to_be(),
|
||||
};
|
||||
self.writer.write_all(bytemuck::bytes_of(&header))?;
|
||||
self.header_written = true;
|
||||
}
|
||||
|
||||
let record = PcapRecordHeader {
|
||||
ts_seconds: 0,
|
||||
ts_nanoseconds: 0,
|
||||
captured_len: (payload.len() as u32).to_be(),
|
||||
original_len: (payload.len() as u32).to_be(),
|
||||
};
|
||||
self.writer.write_all(bytemuck::bytes_of(&record))?;
|
||||
self.writer.write_all(payload)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
enum Output {
|
||||
File(PcapFile),
|
||||
Stdout,
|
||||
}
|
||||
|
||||
impl Output {
|
||||
fn open(path: Option<&Path>) -> io::Result<Self> {
|
||||
match path {
|
||||
Some(path) => PcapFile::open(path).map(Self::File),
|
||||
None => Ok(Self::Stdout),
|
||||
}
|
||||
}
|
||||
|
||||
fn write(&mut self, noisy: bool, payload: &[u8]) -> io::Result<()> {
|
||||
match self {
|
||||
Self::File(file) => {
|
||||
if noisy {
|
||||
describe_packet(payload);
|
||||
}
|
||||
|
||||
file.write(payload)
|
||||
}
|
||||
Self::Stdout => {
|
||||
describe_packet(payload);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn describe_packet(payload: &[u8]) {
|
||||
if payload.len() < size_of::<EthernetFrame>() {
|
||||
eprintln!("Runt packet of {} bytes", payload.len());
|
||||
return;
|
||||
}
|
||||
let ethernet: &EthernetFrame = bytemuck::from_bytes(&payload[..size_of::<EthernetFrame>()]);
|
||||
println!(
|
||||
"{} > {}, length {}",
|
||||
ethernet.source_mac,
|
||||
ethernet.destination_mac,
|
||||
payload.len() - size_of::<EthernetFrame>()
|
||||
);
|
||||
}
|
||||
|
||||
fn run(args: &Args) -> io::Result<()> {
|
||||
let mut output = Output::open(args.output.as_deref())?;
|
||||
let socket = RawSocket::bind(args.interface.as_ref())?;
|
||||
let mut buffer = [0; 4096];
|
||||
|
||||
// TODO packet timestamps
|
||||
loop {
|
||||
let len = socket.recv(&mut buffer)?;
|
||||
output.write(args.noisy, &buffer[..len])?;
|
||||
}
|
||||
}
|
||||
|
||||
fn sigint_handler(_: Signal) {
|
||||
debug_trace!("SIGINT handler!")
|
||||
}
|
||||
|
||||
fn main() -> ExitCode {
|
||||
let _ = set_signal_handler(Signal::Interrupted, SignalHandler::Function(sigint_handler));
|
||||
|
||||
let args = Args::parse();
|
||||
match run(&args) {
|
||||
Ok(()) => ExitCode::SUCCESS,
|
||||
Err(error) => {
|
||||
eprintln!("{error}");
|
||||
ExitCode::FAILURE
|
||||
}
|
||||
}
|
||||
}
|
@ -54,6 +54,7 @@ const PROGRAMS: &[(&str, &str)] = &[
|
||||
("http", "bin/http"),
|
||||
("dnsq", "bin/dnsq"),
|
||||
("ping", "bin/ping"),
|
||||
("ncap", "sbin/ncap"),
|
||||
// colors
|
||||
("colors", "bin/colors"),
|
||||
("term", "bin/term"),
|
||||
|
Loading…
x
Reference in New Issue
Block a user