async-arp 0.4.0

An async ARP client library for probing hosts and sending advanced ARP requests.
Documentation
use std::sync::Arc;

use afpacket::tokio::RawPacketStream;
use pnet::packet::arp::{Arp, ArpOperations, ArpPacket};
use pnet::packet::ethernet::{EtherTypes, EthernetPacket};
use pnet::packet::{FromPacket, Packet};
use tokio::io::AsyncReadExt;

use crate::caching::ArpCache;
use crate::constants::ETH_PACK_LEN;
use crate::error::{Error, Result};

pub(super) struct Listener {
    stream: RawPacketStream,
    cache: Arc<ArpCache>,
}

impl Listener {
    pub(super) fn new(stream: RawPacketStream, cache: Arc<ArpCache>) -> Self {
        Self { stream, cache }
    }

    pub(super) async fn listen(&mut self) -> Result<()> {
        let mut buf = [0; ETH_PACK_LEN];
        while let Ok(read_bytes) = self.stream.read(&mut buf).await {
            if let Ok(arp) = parse_arp_packet(&buf[..read_bytes]) {
                if arp.operation == ArpOperations::Reply {
                    self.cache.cache(arp).await;
                }
            }
        }
        Err(Error::Opaque(
            "error while reading the interface traffic".into(),
        ))
    }
}

pub(super) fn parse_arp_packet(bytes: &[u8]) -> Result<Arp> {
    let ethernet_packet =
        EthernetPacket::new(bytes).ok_or(Error::Opaque("failed to parse Ethernet frame".into()))?;
    if ethernet_packet.get_ethertype() == EtherTypes::Arp {
        Ok(ArpPacket::new(ethernet_packet.payload())
            .ok_or(Error::Opaque("failed to parse ARP packet".into()))?
            .from_packet())
    } else {
        Err(Error::Opaque("not an ARP packet".into()))
    }
}