Skip to main content

rust_ethernet_ip_protocol/
encap.rs

1//! EtherNet/IP encapsulation command and header codecs.
2
3use bytes::{Buf, BufMut, BytesMut};
4
5use crate::{Decode, Encode, ProtocolError, Result};
6
7/// Register Session command code.
8pub const REGISTER_SESSION: u16 = 0x0065;
9/// Unregister Session command code.
10pub const UNREGISTER_SESSION: u16 = 0x0066;
11/// SendRRData command code.
12pub const SEND_RR_DATA: u16 = 0x006F;
13#[allow(dead_code)]
14/// SendUnitData command code.
15pub const SEND_UNIT_DATA: u16 = 0x0070;
16
17/// The fixed 24-byte EtherNet/IP encapsulation header.
18#[derive(Debug, Clone, PartialEq, Eq)]
19pub struct EncapsulationHeader {
20    /// Encapsulation command code.
21    pub command: u16,
22    /// Payload length following the header.
23    pub length: u16,
24    /// Session handle assigned by the target.
25    pub session_handle: u32,
26    /// Encapsulation status code.
27    pub status: u32,
28    /// Caller-controlled correlation bytes echoed by the target.
29    pub sender_context: [u8; 8],
30    /// Encapsulation options; currently required to be zero.
31    pub options: u32,
32}
33
34impl EncapsulationHeader {
35    /// Creates a header with zero status, context, and options.
36    pub fn new(command: u16, length: u16, session_handle: u32) -> Self {
37        Self {
38            command,
39            length,
40            session_handle,
41            status: 0,
42            sender_context: [0; 8],
43            options: 0,
44        }
45    }
46
47    /// Creates a SendRRData header with the library's default sender context.
48    pub fn send_rr_data(length: u16, session_handle: u32) -> Self {
49        Self {
50            sender_context: [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08],
51            ..Self::new(SEND_RR_DATA, length, session_handle)
52        }
53    }
54
55    /// Creates a SendRRData header with an explicit correlation context.
56    pub fn send_rr_data_with_context(
57        length: u16,
58        session_handle: u32,
59        sender_context: [u8; 8],
60    ) -> Self {
61        Self {
62            sender_context,
63            ..Self::new(SEND_RR_DATA, length, session_handle)
64        }
65    }
66}
67
68impl Encode for EncapsulationHeader {
69    fn encode(&self, buf: &mut BytesMut) {
70        buf.put_u16_le(self.command);
71        buf.put_u16_le(self.length);
72        buf.put_u32_le(self.session_handle);
73        buf.put_u32_le(self.status);
74        buf.put_slice(&self.sender_context);
75        buf.put_u32_le(self.options);
76    }
77}
78
79impl Decode for EncapsulationHeader {
80    fn decode(buf: &mut impl Buf) -> Result<Self> {
81        if buf.remaining() < 24 {
82            return Err(ProtocolError::new(
83                "Encapsulation header too short".to_string(),
84            ));
85        }
86
87        let command = buf.get_u16_le();
88        let length = buf.get_u16_le();
89        let session_handle = buf.get_u32_le();
90        let status = buf.get_u32_le();
91        let mut sender_context = [0u8; 8];
92        buf.copy_to_slice(&mut sender_context);
93        let options = buf.get_u32_le();
94
95        Ok(Self {
96            command,
97            length,
98            session_handle,
99            status,
100            sender_context,
101            options,
102        })
103    }
104}