crafter 0.3.1

Packet-level network interaction for Rust tools and agents.
Documentation
//! UDP and TCP protocol implementations.

pub(crate) mod common;
mod tcp;
mod udp;

pub(crate) use self::tcp::append_tcp_packet_with_registry;
pub use self::tcp::{
    effective_mss, effective_mss_ipv4, effective_mss_ipv6, has_fin, has_syn, max_tcp_payload,
    option_budget, remaining_option_budget, sequence_space_len, tcp_header_len,
    tcp_option_kind_class, tcp_option_kind_is_assigned, tcp_option_kind_is_experimental,
    tcp_option_kind_name, valid_window_scale, Tcp, TcpExtendedDataOffset, TcpOption,
    TcpOptionBudget, TcpOptionIter, TcpOptionKindClass, TcpSackBlock, TcpSynOptions,
    IPV4_HEADER_LEN_FOR_MSS, IPV6_HEADER_LEN_FOR_MSS, IPV6_MINIMUM_MTU, MPTCP_SUBTYPE_ADD_ADDR,
    MPTCP_SUBTYPE_DSS, MPTCP_SUBTYPE_MP_CAPABLE, MPTCP_SUBTYPE_MP_EXPERIMENTAL,
    MPTCP_SUBTYPE_MP_FAIL, MPTCP_SUBTYPE_MP_FASTCLOSE, MPTCP_SUBTYPE_MP_JOIN,
    MPTCP_SUBTYPE_MP_PRIO, MPTCP_SUBTYPE_REMOVE_ADDR, MPTCP_SUBTYPE_TCPRST,
    MPTCP_TCPRST_REASON_ADMINISTRATIVELY_PROHIBITED, MPTCP_TCPRST_REASON_LACK_OF_RESOURCES,
    MPTCP_TCPRST_REASON_MIDDLEBOX_INTERFERENCE, MPTCP_TCPRST_REASON_MPTCP_SPECIFIC,
    MPTCP_TCPRST_REASON_TOO_MUCH_OUTSTANDING_DATA, MPTCP_TCPRST_REASON_UNACCEPTABLE_PERFORMANCE,
    MPTCP_TCPRST_REASON_UNSPECIFIED, TCP_DEFAULT_IPV4_MSS, TCP_EDO_HEADER_AND_SEGMENT_LEN,
    TCP_EDO_HEADER_LEN, TCP_EDO_REQUEST_LEN, TCP_FIXED_HEADER_LEN, TCP_FLAG_ACK, TCP_FLAG_AE,
    TCP_FLAG_CWR, TCP_FLAG_ECE, TCP_FLAG_FIN, TCP_FLAG_NS, TCP_FLAG_PSH, TCP_FLAG_RST,
    TCP_FLAG_SYN, TCP_FLAG_URG, TCP_MAX_OPTION_BYTES, TCP_OPTION_ACCURATE_ECN_MIN_LEN,
    TCP_OPTION_ACCURATE_ECN_ORDER_0, TCP_OPTION_ACCURATE_ECN_ORDER_1, TCP_OPTION_EDO,
    TCP_OPTION_EOL, TCP_OPTION_EXPERIMENTAL_1, TCP_OPTION_EXPERIMENTAL_2,
    TCP_OPTION_EXPERIMENTAL_MIN_LEN, TCP_OPTION_FAST_OPEN, TCP_OPTION_MD5_SIGNATURE,
    TCP_OPTION_MPTCP, TCP_OPTION_MSS, TCP_OPTION_NOP, TCP_OPTION_SACK, TCP_OPTION_SACK_PERMITTED,
    TCP_OPTION_TCP_AUTHENTICATION, TCP_OPTION_TCP_AUTHENTICATION_MIN_LEN, TCP_OPTION_TCP_ENO,
    TCP_OPTION_TCP_ENO_MIN_LEN, TCP_OPTION_TIMESTAMP, TCP_OPTION_USER_TIMEOUT,
    TCP_OPTION_USER_TIMEOUT_LEN, TCP_OPTION_WINDOW_SCALE, TCP_WINDOW_SCALE_MAX_SHIFT,
};
pub(crate) use self::udp::append_udp_packet_with_registry;
pub use self::udp::{
    udp_option_kind_class, udp_option_kind_is_unsafe, udp_option_kind_is_unsupported, Udp,
    UdpChecksumStatus, UdpOption, UdpOptionIter, UdpOptionKindClass, UdpOptionStatus, UdpOptions,
    UDP_HEADER_LEN, UDP_OPTION_APC, UDP_OPTION_AUTH, UDP_OPTION_EOL, UDP_OPTION_EXP,
    UDP_OPTION_FRAG, UDP_OPTION_MDS, UDP_OPTION_MRDS, UDP_OPTION_NOP, UDP_OPTION_REQ,
    UDP_OPTION_RES, UDP_OPTION_RESERVED_SAFE_END, UDP_OPTION_RESERVED_SAFE_START,
    UDP_OPTION_RESERVED_UNSAFE, UDP_OPTION_TIME, UDP_OPTION_UCMP, UDP_OPTION_UENC, UDP_OPTION_UEXP,
    UDP_OPTION_UNASSIGNED_SAFE_END, UDP_OPTION_UNASSIGNED_SAFE_START,
    UDP_OPTION_UNASSIGNED_UNSAFE_END, UDP_OPTION_UNASSIGNED_UNSAFE_START,
};

#[cfg(test)]
mod transport_checksums {
    use super::{Tcp, Udp};
    use crate::checksum::ipv6_pseudo_header_checksum;
    use crate::{
        Layer, LayerContext, Packet, Raw, TransportChecksumContext, IPPROTO_TCP, IPPROTO_UDP,
    };
    use core::any::Any;
    use core::net::Ipv6Addr;

    #[derive(Debug, Clone)]
    struct Ipv6PseudoHeader {
        source: Ipv6Addr,
        destination: Ipv6Addr,
    }

    impl Ipv6PseudoHeader {
        fn new() -> Self {
            Self {
                source: Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1),
                destination: Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 2),
            }
        }
    }

    impl Layer for Ipv6PseudoHeader {
        fn name(&self) -> &'static str {
            "Ipv6PseudoHeader"
        }

        fn encoded_len(&self) -> usize {
            0
        }

        fn compile(&self, _ctx: &LayerContext<'_>, _out: &mut Vec<u8>) -> crate::Result<()> {
            Ok(())
        }

        fn transport_checksum_context(
            &self,
            transport_protocol: u8,
        ) -> Option<TransportChecksumContext> {
            Some(TransportChecksumContext::Ipv6 {
                source: self.source,
                destination: self.destination,
                next_header: transport_protocol,
            })
        }

        fn clone_layer(&self) -> Box<dyn Layer> {
            Box::new(self.clone())
        }

        fn as_any(&self) -> &dyn Any {
            self
        }

        fn as_any_mut(&mut self) -> &mut dyn Any {
            self
        }

        fn into_any(self: Box<Self>) -> Box<dyn Any> {
            self
        }
    }

    #[test]
    fn udp_uses_ipv6_checksum_context_when_previous_layer_provides_it() {
        let pseudo = Ipv6PseudoHeader::new();
        let source = pseudo.source;
        let destination = pseudo.destination;
        let bytes = (Packet::from_layer(pseudo)
            / Udp::new().sport(1).dport(2)
            / Raw::from_bytes([1, 2, 3]))
        .compile()
        .unwrap();

        let mut udp = bytes.as_bytes().to_vec();
        udp[6] = 0;
        udp[7] = 0;
        assert_eq!(
            u16::from_be_bytes([bytes.as_bytes()[6], bytes.as_bytes()[7]]),
            ipv6_pseudo_header_checksum(source, destination, IPPROTO_UDP, &udp)
        );
    }

    #[test]
    fn tcp_uses_ipv6_checksum_context_when_previous_layer_provides_it() {
        let pseudo = Ipv6PseudoHeader::new();
        let source = pseudo.source;
        let destination = pseudo.destination;
        let bytes = (Packet::from_layer(pseudo)
            / Tcp::new().sport(10).dport(20)
            / Raw::from_bytes([1, 2, 3]))
        .compile()
        .unwrap();

        let mut tcp = bytes.as_bytes().to_vec();
        tcp[16] = 0;
        tcp[17] = 0;
        assert_eq!(
            u16::from_be_bytes([bytes.as_bytes()[16], bytes.as_bytes()[17]]),
            ipv6_pseudo_header_checksum(source, destination, IPPROTO_TCP, &tcp)
        );
    }

    #[test]
    fn transport_checksums_are_zero_without_network_context() {
        let udp = (Udp::new().sport(1).dport(2) / Raw::from("abc"))
            .compile()
            .unwrap();
        let tcp = (Tcp::new().sport(1).dport(2) / Raw::from("abc"))
            .compile()
            .unwrap();

        assert_eq!(&udp.as_bytes()[6..8], &[0, 0]);
        assert_eq!(&tcp.as_bytes()[16..18], &[0, 0]);
    }
}