Access slice of TCP Header in Etherparse

Looking for some help with using etherparse for TCP.
I would like to use etherparse to parse a TCP packet and I am having difficulty understanding how to access the TcpHeader after parsing the packet using the following:

match SlicedPacket::from_ethernet(&packet) {
    Err(value) => println!("Err {:?}", value),
    Ok(value) => {
        println!("link: {:?}", value.link);
        println!("vlan: {:?}", value.vlan);
        println!("ip: {:?}", value.ip);
        println!("transport: {:?}", value.transport);
    }
}  

When trying to use value.transport as a slice I get the following error: "expected slice [u8], found enum Option"

I am unsure how to access the TcpHeader that is part of the " Option TransportHeader ". Any help would be appreciated, thanks!

Well, what do you want to do when the transport is None (couldn't be parsed)?

It sounds like maybe you're only interested in TCP packets?

match SlicedPacket::from_ethernet(&packet) {
    Err(value) => println!("Err {:?}", value),
    Ok(value) => {
        println!("link: {:?}", value.link);
        println!("vlan: {:?}", value.vlan);
        println!("ip: {:?}", value.ip);
        match value.transport {
            Some(TransportSlice::Tcp(tcp_header)) => {
                // Do TCP stuff
            }
            _ => { /* ignore the rest */ }
        }
    }
}  
1 Like

I'm only interested in TCP packets, so passing them is fine.

I'm only interested in getting the TCP data field. That's why I am trying to access the TCP slice. Could you clarify the TransportSlice? I am not sure how that fits into my current understanding

On mobile now, but I just looked at the crate docs and the field is an option of TransportSlice, which is an enum including Tcp(TcpHeader) and others.

I suggest making a type of your own with the fields of SlicePacket, but with transport replaced with TcpHeader. Then write a function to go from SlicePacket to an option of your type (or implement TryFrom).

If you're not sure what I mean and no one else supplies an example, I can do so later.

I'm still pretty new to rust so I would appreciate seeing your implementation.

If I could access the TCP header vector I would just slice the fields, but it seems like that is not enough?

struct SlicedTcp<'a> {
    pub link: Option<LinkSlice<'a>>,
    pub vlan: Option<VlanSlice<'a>>,
    pub ip: Option<InternetSlice<'a>>,
    pub tcp_header: TcpHeaderSlice<'a>,
}

impl<'a> TryFrom<SlicedPacket<'a>> for SlicedTcp<'a> {
    type Error = ();
    fn try_from(sp: SlicedPacket<'a>) -> Result<Self, Self::Error> {
        match sp {
            SlicedPacket {
                link,
                vlan,
                ip,
                transport: Some(
                    TransportSlice::Tcp(tcp_header)
                ),
                ..
            } => Ok(Self { link, vlan, ip, tcp_header }),
            _ => Err(()),
        }
    }
}

impl<'a> SlicedTcp<'a> {
    fn new(sp: SlicedPacket<'a>) -> Option<Self> {
        sp.try_into().ok()
    }
}

(I just left the other SlicedPacket fields as Option, but you could require them to be Some too if that's what you want.)

But there might not be a TCP header vector.

Rust won't let you access something that's not there; it doesn't just hand out Nil values for uninitialized variables and fields like some languages. You have to explicitly deal with the possibility of an Option being None, and more generally with an enum being exactly one variant.

With that in mind we can look at SlicedPacket and see that it might not have a TransportSlice at all, and look at TransportSlice to see it could be something that's not a TcpHeaderSlice.

This really helped, thanks again!

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.