fn test_constructors()

in src/dumbo/src/pdu/tcp.rs [635:809]


    fn test_constructors() {
        let mut a = [1u8; 1460];
        let b = [2u8; 1000];
        let c = [3u8; 2000];

        let src_addr = Ipv4Addr::new(10, 1, 2, 3);
        let dst_addr = Ipv4Addr::new(192, 168, 44, 77);
        let src_port = 1234;
        let dst_port = 5678;
        let seq_number = 11_111_222;
        let ack_number = 34_566_543;
        let flags_after_ns = Flags::SYN | Flags::RST;
        let window_size = 19999;
        let mss_left = 1460;
        let mss_option = Some(mss_left);
        let payload = Some((b.as_ref(), b.len()));

        let header_len = OPTIONS_OFFSET + OPTION_LEN_MSS;

        let segment_len = {
            let mut segment = TcpSegment::write_segment(
                a.as_mut(),
                src_port,
                dst_port,
                seq_number,
                ack_number,
                flags_after_ns,
                window_size,
                mss_option,
                mss_left,
                payload,
                Some((src_addr, dst_addr)),
            )
            .unwrap();

            assert_eq!(segment.source_port(), src_port);
            assert_eq!(segment.destination_port(), dst_port);
            assert_eq!(segment.sequence_number(), seq_number);
            assert_eq!(segment.ack_number(), ack_number);
            assert_eq!(segment.header_len_rsvd_ns(), (header_len, 0, false));
            assert_eq!(segment.flags_after_ns(), flags_after_ns);
            assert_eq!(segment.window_size(), window_size);

            let checksum = segment.checksum();
            segment.set_checksum(0);
            let computed_checksum = segment.compute_checksum(src_addr, dst_addr);
            assert_eq!(checksum, computed_checksum);

            segment.set_checksum(checksum);
            assert_eq!(segment.compute_checksum(src_addr, dst_addr), 0);

            assert_eq!(segment.urgent_pointer(), 0);

            {
                let options = segment.options_unchecked(header_len);
                assert_eq!(options.len(), OPTION_LEN_MSS);
                assert_eq!(options[0], OPTION_KIND_MSS);
                assert_eq!(options[1], OPTION_LEN_MSS as u8);
                assert_eq!(options.ntohs_unchecked(2), mss_left);
            }

            // Payload was smaller than mss_left after options.
            assert_eq!(segment.len(), header_len + b.len());
            segment.len()
            // Mutable borrow of a goes out of scope.
        };

        {
            let segment =
                TcpSegment::from_bytes(&a[..segment_len], Some((src_addr, dst_addr))).unwrap();
            assert_eq!(
                segment.parse_mss_option_unchecked(header_len),
                Ok(Some(NonZeroU16::new(mss_left as u16).unwrap()))
            );
        }

        // Let's quickly see what happens when the payload buf is larger than our mutable slice.
        {
            let segment_len = TcpSegment::write_segment(
                a.as_mut(),
                src_port,
                dst_port,
                seq_number,
                ack_number,
                flags_after_ns,
                window_size,
                mss_option,
                mss_left,
                Some((c.as_ref(), c.len())),
                Some((src_addr, dst_addr)),
            )
            .unwrap()
            .len();

            assert_eq!(segment_len, mss_left as usize);
        }

        // Now let's test the error value for from_bytes().

        // Using a helper function here instead of a closure because it's hard (impossible?) to
        // specify lifetime bounds for closure arguments.
        fn p(buf: &mut [u8]) -> TcpSegment<&mut [u8]> {
            TcpSegment::from_bytes_unchecked(buf)
        }

        // Just a helper closure.
        let look_for_error = |buf: &[u8], err: Error| {
            assert_eq!(
                TcpSegment::from_bytes(buf, Some((src_addr, dst_addr))).unwrap_err(),
                err
            );
        };

        // Header length too short.
        p(a.as_mut()).set_header_len_rsvd_ns(OPTIONS_OFFSET.checked_sub(1).unwrap(), false);
        look_for_error(a.as_ref(), Error::HeaderLen);

        // Header length too large.
        p(a.as_mut()).set_header_len_rsvd_ns(MAX_HEADER_LEN.checked_add(4).unwrap(), false);
        look_for_error(a.as_ref(), Error::HeaderLen);

        // The previously set checksum should be valid.
        assert_eq!(
            p(a.as_mut())
                .set_header_len_rsvd_ns(header_len, false)
                .compute_checksum(src_addr, dst_addr),
            0
        );

        // Let's make it invalid.
        let checksum = p(a.as_mut()).checksum();
        p(a.as_mut()).set_checksum(checksum.wrapping_add(1));
        look_for_error(a.as_ref(), Error::Checksum);

        // Now we use a very small buffer.
        let mut small_buf = [0u8; 1];
        look_for_error(small_buf.as_ref(), Error::SliceTooShort);

        assert_eq!(
            TcpSegment::write_segment(
                small_buf.as_mut(),
                src_port,
                dst_port,
                seq_number,
                ack_number,
                flags_after_ns,
                window_size,
                mss_option,
                mss_left,
                payload,
                Some((src_addr, dst_addr)),
            )
            .unwrap_err(),
            Error::SliceTooShort
        );

        // Make sure we get the proper error for an insufficient value of mss_remaining.
        assert_eq!(
            TcpSegment::write_segment(
                small_buf.as_mut(),
                src_port,
                dst_port,
                seq_number,
                ack_number,
                flags_after_ns,
                window_size,
                mss_option,
                0,
                payload,
                Some((src_addr, dst_addr)),
            )
            .unwrap_err(),
            Error::MssRemaining
        );
    }