//! K433 request decoder and K434 fixed-address reply builder; no ARP cache. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Error { ShortPacket, WrongHardwareType, WrongProtocolType, WrongHardwareLength, WrongProtocolLength, WrongOpcode, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Request { pub sender_mac: [u8; 6], pub sender_ipv4: [u8; 4], pub target_mac: [u8; 6], pub target_ipv4: [u8; 4], } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum ReplyError { WrongTargetIpv4, InvalidOwnMac, InvalidSenderMac, } pub fn parse_request(payload: &[u8]) -> Result { if payload.len() < 28 { return Err(Error::ShortPacket); } if u16::from_be_bytes([payload[0], payload[1]]) != 1 { return Err(Error::WrongHardwareType); } if u16::from_be_bytes([payload[2], payload[3]]) != 0x0800 { return Err(Error::WrongProtocolType); } if payload[4] != 6 { return Err(Error::WrongHardwareLength); } if payload[5] != 4 { return Err(Error::WrongProtocolLength); } if u16::from_be_bytes([payload[6], payload[7]]) != 1 { return Err(Error::WrongOpcode); } Ok(Request { sender_mac: payload[8..14].try_into().unwrap(), sender_ipv4: payload[14..18].try_into().unwrap(), target_mac: payload[18..24].try_into().unwrap(), target_ipv4: payload[24..28].try_into().unwrap(), }) } /// K434 fixed-size Ethernet/IPv4 ARP reply for one locally owned IPv4. The /// caller must still submit the bytes through a TX queue and observe used. pub fn build_reply( request: Request, own_mac: [u8; 6], own_ipv4: [u8; 4], ) -> Result<[u8; 60], ReplyError> { if request.target_ipv4 != own_ipv4 { return Err(ReplyError::WrongTargetIpv4); } if crate::net_mac::validate(crate::net_mac::VIRTIO_NET_F_MAC, own_mac).is_err() { return Err(ReplyError::InvalidOwnMac); } if crate::net_mac::validate(crate::net_mac::VIRTIO_NET_F_MAC, request.sender_mac).is_err() { return Err(ReplyError::InvalidSenderMac); } let mut frame = [0u8; 60]; frame[..6].copy_from_slice(&request.sender_mac); frame[6..12].copy_from_slice(&own_mac); frame[12..14].copy_from_slice(&0x0806u16.to_be_bytes()); frame[14..16].copy_from_slice(&1u16.to_be_bytes()); frame[16..18].copy_from_slice(&0x0800u16.to_be_bytes()); frame[18] = 6; frame[19] = 4; frame[20..22].copy_from_slice(&2u16.to_be_bytes()); frame[22..28].copy_from_slice(&own_mac); frame[28..32].copy_from_slice(&own_ipv4); frame[32..38].copy_from_slice(&request.sender_mac); frame[38..42].copy_from_slice(&request.sender_ipv4); Ok(frame) } #[cfg(test)] mod tests { use super::*; fn known() -> [u8; 28] { [ 0, 1, 0x08, 0, 6, 4, 0, 1, 0x02, 0, 0, 0, 0, 1, 192, 0, 2, 1, 0, 0, 0, 0, 0, 0, 192, 0, 2, 2, ] } #[test] fn k433_arp_request_decodes_exact_addresses_and_ignores_ethernet_padding() { let expected = Request { sender_mac: [0x02, 0, 0, 0, 0, 1], sender_ipv4: [192, 0, 2, 1], target_mac: [0; 6], target_ipv4: [192, 0, 2, 2], }; assert_eq!(parse_request(&known()), Ok(expected)); let mut padded = [0xa5; 46]; padded[..28].copy_from_slice(&known()); assert_eq!(parse_request(&padded), Ok(expected)); } #[test] fn k433_arp_request_rejects_short_and_wrong_length_fields() { let mut packet = known(); for length in [0, 7, 8, 27] { assert_eq!(parse_request(&packet[..length]), Err(Error::ShortPacket)); } for (offset, value, error) in [ (0, 2, Error::WrongHardwareType), (3, 6, Error::WrongProtocolType), (4, 5, Error::WrongHardwareLength), (5, 5, Error::WrongProtocolLength), (7, 2, Error::WrongOpcode), ] { let old = packet[offset]; packet[offset] = value; assert_eq!(parse_request(&packet), Err(error)); packet[offset] = old; } } #[test] fn k434_arp_reply_swaps_sender_target_addresses_and_sets_opcode_two() { let request = parse_request(&known()).unwrap(); let own_mac = [0x52, 0x54, 0, 0x12, 0x34, 0x56]; let reply = build_reply(request, own_mac, [192, 0, 2, 2]).unwrap(); assert_eq!(&reply[..6], &request.sender_mac); assert_eq!(&reply[6..12], &own_mac); assert_eq!(&reply[12..22], &[0x08, 0x06, 0, 1, 0x08, 0, 6, 4, 0, 2]); assert_eq!(&reply[22..28], &own_mac); assert_eq!(&reply[28..32], &[192, 0, 2, 2]); assert_eq!(&reply[32..38], &request.sender_mac); assert_eq!(&reply[38..42], &request.sender_ipv4); assert_eq!(&reply[42..], &[0; 18]); } #[test] fn k434_arp_reply_rejects_wrong_target_and_invalid_mac_before_tx() { let request = parse_request(&known()).unwrap(); let own = [0x52, 0x54, 0, 0x12, 0x34, 0x56]; assert_eq!( build_reply(request, own, [192, 0, 2, 3]), Err(ReplyError::WrongTargetIpv4) ); assert_eq!( build_reply(request, [0; 6], [192, 0, 2, 2]), Err(ReplyError::InvalidOwnMac) ); let mut invalid = request; invalid.sender_mac = [0xff; 6]; assert_eq!( build_reply(invalid, own, [192, 0, 2, 2]), Err(ReplyError::InvalidSenderMac) ); } }