Decoding network packets

Anyone know if this is still the case today? Looks likee rustc-serialize was deprecated in favor of serde now. Basically, I am kinda looking for something like the equivalent of a gopacket for rust, and the 'correct' way to do something like map a byte buffer to a struct.

Well in the meantime I have made pktparse if it may be of any use to you ...

1 Like

Thanks for this! If I use something like pcap, I am able to open my file and then using your pkparse I was able to split the header off from the payload. I think I should just be doing something with the nom crate, but I can't quite follow how this is supposed to be done in rust. Even after I have the u8 slice from pktparse, when I attempt to read the specific offset I am not getting the expected results.

Have you seen GitHub - sharksforarms/deku: Declarative binary reading and writing: bit-level, symmetric, serialization/deserialization?

If you want something more barebones I'd suggest you the bytes crate: bytes - Rust

It's very easy to use and it has the advantage that you can have multiple Bytes structs pointing to the same underlying buffer (think of owned slices).

Here's an example:

use bytes::{Buf, BufMut, Bytes, BytesMut};

//
// OSPFv3 LSA header.
//
// Encoding format:
//
//  0                   1                   2                   3
//  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |           LS Age              |           LS Type             |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |                       Link State ID                           |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |                    Advertising Router                         |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |                    LS Sequence Number                         |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |        LS Checksum            |             Length            |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[derive(Deserialize, Serialize)]
pub struct LsaHdr {
    pub age: u16,
    pub lsa_type: LsaType,
    pub lsa_id: Ipv4Addr,
    pub adv_rtr: Ipv4Addr,
    pub seq_no: u32,
    pub cksum: u16,
    pub length: u16,
}

impl LsaHdr {
    fn decode(buf: &mut Bytes) -> DecodeResult<Self> {
        let age = buf.get_u16();
        if age > LSA_MAX_AGE {
            return Err(DecodeError::InvalidLsaAge(age));
        }

        let lsa_type = LsaType(buf.get_u16());
        let lsa_id = buf.get_ipv4();
        let adv_rtr = buf.get_ipv4();
        let seq_no = buf.get_u32();
        if seq_no == LSA_RESERVED_SEQ_NO {
            return Err(DecodeError::InvalidLsaSeqNo(seq_no));
        }
        let cksum = buf.get_u16();
        let length = buf.get_u16();

        Ok(LsaHdr {
            age,
            lsa_type,
            lsa_id,
            adv_rtr,
            seq_no,
            cksum,
            length,
        })
    }

    fn encode(&self, buf: &mut BytesMut) {
        buf.put_u16(self.age);
        buf.put_u16(self.lsa_type.0);
        buf.put_ipv4(&self.lsa_id);
        buf.put_ipv4(&self.adv_rtr);
        buf.put_u32(self.seq_no);
        buf.put_u16(self.cksum);
        buf.put_u16(self.length);
    }
}

Oh, thanks very much for this, super helpful. So I think I can implement the decode for my specific transport header with something like this. If I went this route, would you suggest to use something like the pcap crate and then take the packet payload and read it into this struct?

use serde::{Serialize, Deserialize};

//  0                  1                   2                     3 
//  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |      Version  |  (Reserved)   |      Message Protocol ID      |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |                         Channel ID                            |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |                         Session ID                            |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |       Payload Length          |        Message Count          |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |                         Stream Offset                         |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |                      Stream Offset (cont.)                    |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |                 First Message Sequence Number                 |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |               First Message Sequence Number (cont.)           |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |                         Send Time                             |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |                      Send Time (cont.)                        |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |        Message Length         |          (Msg Data)           | B 41-X
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |        Message Length         |          (Msg Data)           | B X-Y
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[derive(Serialize, Deserialize)]
struct transportHeader { 
    version: u8,
    reserved: u8,
    message_protocol_id: u16,
    channel_id: u32,
    session_id: u32,
    payload_length: u16,
    message_count: u16,
    stream_offset: u64,
    first_message_sequence: u64,
    send_time: u64,
}

impl transportHeader {
    fn decode(buf: &mut Bytes) -> DecodeResult<Self> {
        let version = buf.get_u8();
        let reserved = buf.get_u8();
        let message_protocol_id = buf.get_u16();
        let channel_id = buf.get_u32();
        let session_id = buf.get_u32();
        let payload_length = buf.get_u16();
        let message_count = buf.get_u16();
        let stream_offset = buf.get_u64();
        let first_message_sequence = buf.get_u64();
        let send_time = buf.get_u64();

        Ok(transportHeader{
            version,
            reserved,
            message_protocol_id,
            channel_id,
            session_id,
            payload_length,
            message_count,
            stream_offset,
            first_message_sequence,
            send_time,
        })
    }

Where do you get DecodeResult?

I get:

  --> src/main.rs:54:35
   |
54 |     fn decode(buf: &mut Bytes) -> DecodeResult<Self> {
   |                                   ^^^^^^^^^^^^ not found in this scope