someip_rs/
header.rs

1//! SOME/IP header types and ID newtypes.
2
3use crate::error::{Result, SomeIpError};
4use crate::types::{MessageType, ReturnCode, PROTOCOL_VERSION};
5
6/// Size of the SOME/IP header in bytes.
7pub const HEADER_SIZE: usize = 16;
8
9/// Service ID - identifies a SOME/IP service.
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
11pub struct ServiceId(pub u16);
12
13/// Method ID - identifies a method within a service.
14/// Bit 15 indicates if this is an event (1) or method (0).
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
16pub struct MethodId(pub u16);
17
18/// Client ID - identifies the client making a request.
19#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
20pub struct ClientId(pub u16);
21
22/// Session ID - unique identifier for a request/response pair.
23#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
24pub struct SessionId(pub u16);
25
26impl MethodId {
27    /// Check if this method ID represents an event (bit 15 set).
28    pub fn is_event(&self) -> bool {
29        self.0 & 0x8000 != 0
30    }
31
32    /// Create a method ID for an event.
33    pub fn event(id: u16) -> Self {
34        Self(id | 0x8000)
35    }
36
37    /// Create a method ID for a regular method.
38    pub fn method(id: u16) -> Self {
39        Self(id & 0x7FFF)
40    }
41}
42
43impl std::fmt::Display for ServiceId {
44    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
45        write!(f, "0x{:04X}", self.0)
46    }
47}
48
49impl std::fmt::Display for MethodId {
50    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
51        write!(f, "0x{:04X}", self.0)
52    }
53}
54
55impl std::fmt::Display for ClientId {
56    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
57        write!(f, "0x{:04X}", self.0)
58    }
59}
60
61impl std::fmt::Display for SessionId {
62    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
63        write!(f, "0x{:04X}", self.0)
64    }
65}
66
67/// SOME/IP message header (16 bytes).
68///
69/// ```text
70/// +----------------+----------------+----------------+----------------+
71/// |           Message ID (32 bits)                                   |
72/// |   Service ID (16 bits)  |  Method ID (16 bits)                   |
73/// +----------------+----------------+----------------+----------------+
74/// |           Length (32 bits) - payload length + 8                  |
75/// +----------------+----------------+----------------+----------------+
76/// |           Request ID (32 bits)                                   |
77/// |   Client ID (16 bits)   |  Session ID (16 bits)                  |
78/// +----------------+----------------+----------------+----------------+
79/// | Protocol Ver | Interface Ver | Message Type | Return Code        |
80/// | (8 bits)     | (8 bits)      | (8 bits)     | (8 bits)           |
81/// +----------------+----------------+----------------+----------------+
82/// ```
83#[derive(Debug, Clone, PartialEq, Eq)]
84pub struct SomeIpHeader {
85    /// Service ID.
86    pub service_id: ServiceId,
87    /// Method ID.
88    pub method_id: MethodId,
89    /// Length of payload + 8 bytes (client_id, session_id, protocol_version, interface_version, message_type, return_code).
90    pub length: u32,
91    /// Client ID.
92    pub client_id: ClientId,
93    /// Session ID.
94    pub session_id: SessionId,
95    /// Protocol version (should be 0x01).
96    pub protocol_version: u8,
97    /// Interface version.
98    pub interface_version: u8,
99    /// Message type.
100    pub message_type: MessageType,
101    /// Return code.
102    pub return_code: ReturnCode,
103}
104
105impl SomeIpHeader {
106    /// Create a new header with the given service and method IDs.
107    pub fn new(service_id: ServiceId, method_id: MethodId) -> Self {
108        Self {
109            service_id,
110            method_id,
111            length: 8, // Minimum length (no payload)
112            client_id: ClientId::default(),
113            session_id: SessionId::default(),
114            protocol_version: PROTOCOL_VERSION,
115            interface_version: 1,
116            message_type: MessageType::Request,
117            return_code: ReturnCode::Ok,
118        }
119    }
120
121    /// Create a request header.
122    pub fn request(service_id: ServiceId, method_id: MethodId) -> Self {
123        let mut header = Self::new(service_id, method_id);
124        header.message_type = MessageType::Request;
125        header
126    }
127
128    /// Create a request-no-return header.
129    pub fn request_no_return(service_id: ServiceId, method_id: MethodId) -> Self {
130        let mut header = Self::new(service_id, method_id);
131        header.message_type = MessageType::RequestNoReturn;
132        header
133    }
134
135    /// Create a notification header.
136    pub fn notification(service_id: ServiceId, method_id: MethodId) -> Self {
137        let mut header = Self::new(service_id, method_id);
138        header.message_type = MessageType::Notification;
139        header
140    }
141
142    /// Create a response header from a request header.
143    pub fn response_from(request: &Self) -> Self {
144        Self {
145            service_id: request.service_id,
146            method_id: request.method_id,
147            length: 8,
148            client_id: request.client_id,
149            session_id: request.session_id,
150            protocol_version: PROTOCOL_VERSION,
151            interface_version: request.interface_version,
152            message_type: MessageType::Response,
153            return_code: ReturnCode::Ok,
154        }
155    }
156
157    /// Create an error response header from a request header.
158    pub fn error_from(request: &Self, return_code: ReturnCode) -> Self {
159        Self {
160            service_id: request.service_id,
161            method_id: request.method_id,
162            length: 8,
163            client_id: request.client_id,
164            session_id: request.session_id,
165            protocol_version: PROTOCOL_VERSION,
166            interface_version: request.interface_version,
167            message_type: MessageType::Error,
168            return_code,
169        }
170    }
171
172    /// Get the payload length (length field minus 8).
173    pub fn payload_length(&self) -> u32 {
174        self.length.saturating_sub(8)
175    }
176
177    /// Set the payload length (updates length field to payload_len + 8).
178    pub fn set_payload_length(&mut self, payload_len: u32) {
179        self.length = payload_len + 8;
180    }
181
182    /// Parse a header from bytes.
183    pub fn from_bytes(data: &[u8]) -> Result<Self> {
184        if data.len() < HEADER_SIZE {
185            return Err(SomeIpError::MessageTooShort {
186                expected: HEADER_SIZE,
187                actual: data.len(),
188            });
189        }
190
191        let service_id = ServiceId(u16::from_be_bytes([data[0], data[1]]));
192        let method_id = MethodId(u16::from_be_bytes([data[2], data[3]]));
193        let length = u32::from_be_bytes([data[4], data[5], data[6], data[7]]);
194        let client_id = ClientId(u16::from_be_bytes([data[8], data[9]]));
195        let session_id = SessionId(u16::from_be_bytes([data[10], data[11]]));
196        let protocol_version = data[12];
197        let interface_version = data[13];
198
199        if protocol_version != PROTOCOL_VERSION {
200            return Err(SomeIpError::WrongProtocolVersion(protocol_version));
201        }
202
203        let message_type = MessageType::from_u8(data[14])
204            .ok_or(SomeIpError::UnknownMessageType(data[14]))?;
205        let return_code =
206            ReturnCode::from_u8(data[15]).ok_or(SomeIpError::UnknownReturnCode(data[15]))?;
207
208        Ok(Self {
209            service_id,
210            method_id,
211            length,
212            client_id,
213            session_id,
214            protocol_version,
215            interface_version,
216            message_type,
217            return_code,
218        })
219    }
220
221    /// Serialize the header to bytes.
222    pub fn to_bytes(&self) -> [u8; HEADER_SIZE] {
223        let mut buf = [0u8; HEADER_SIZE];
224
225        buf[0..2].copy_from_slice(&self.service_id.0.to_be_bytes());
226        buf[2..4].copy_from_slice(&self.method_id.0.to_be_bytes());
227        buf[4..8].copy_from_slice(&self.length.to_be_bytes());
228        buf[8..10].copy_from_slice(&self.client_id.0.to_be_bytes());
229        buf[10..12].copy_from_slice(&self.session_id.0.to_be_bytes());
230        buf[12] = self.protocol_version;
231        buf[13] = self.interface_version;
232        buf[14] = self.message_type as u8;
233        buf[15] = self.return_code as u8;
234
235        buf
236    }
237
238    /// Get the message ID (service_id << 16 | method_id).
239    pub fn message_id(&self) -> u32 {
240        ((self.service_id.0 as u32) << 16) | (self.method_id.0 as u32)
241    }
242
243    /// Get the request ID (client_id << 16 | session_id).
244    pub fn request_id(&self) -> u32 {
245        ((self.client_id.0 as u32) << 16) | (self.session_id.0 as u32)
246    }
247}
248
249impl Default for SomeIpHeader {
250    fn default() -> Self {
251        Self::new(ServiceId(0), MethodId(0))
252    }
253}
254
255#[cfg(test)]
256mod tests {
257    use super::*;
258
259    #[test]
260    fn test_header_roundtrip() {
261        let header = SomeIpHeader {
262            service_id: ServiceId(0x1234),
263            method_id: MethodId(0x5678),
264            length: 16,
265            client_id: ClientId(0xABCD),
266            session_id: SessionId(0xEF01),
267            protocol_version: PROTOCOL_VERSION,
268            interface_version: 2,
269            message_type: MessageType::Request,
270            return_code: ReturnCode::Ok,
271        };
272
273        let bytes = header.to_bytes();
274        let parsed = SomeIpHeader::from_bytes(&bytes).unwrap();
275
276        assert_eq!(header, parsed);
277    }
278
279    #[test]
280    fn test_header_byte_order() {
281        let header = SomeIpHeader {
282            service_id: ServiceId(0x1234),
283            method_id: MethodId(0x5678),
284            length: 8,
285            client_id: ClientId(0x0000),
286            session_id: SessionId(0x0001),
287            protocol_version: PROTOCOL_VERSION,
288            interface_version: 1,
289            message_type: MessageType::Request,
290            return_code: ReturnCode::Ok,
291        };
292
293        let bytes = header.to_bytes();
294
295        // Check big-endian order
296        assert_eq!(bytes[0], 0x12); // Service ID high byte
297        assert_eq!(bytes[1], 0x34); // Service ID low byte
298        assert_eq!(bytes[2], 0x56); // Method ID high byte
299        assert_eq!(bytes[3], 0x78); // Method ID low byte
300    }
301
302    #[test]
303    fn test_method_id_event() {
304        let event = MethodId::event(0x1234);
305        assert!(event.is_event());
306        assert_eq!(event.0, 0x9234);
307
308        let method = MethodId::method(0x9234);
309        assert!(!method.is_event());
310        assert_eq!(method.0, 0x1234);
311    }
312
313    #[test]
314    fn test_response_from() {
315        let request = SomeIpHeader {
316            service_id: ServiceId(0x1234),
317            method_id: MethodId(0x0001),
318            length: 20,
319            client_id: ClientId(0x0100),
320            session_id: SessionId(0x0001),
321            protocol_version: PROTOCOL_VERSION,
322            interface_version: 1,
323            message_type: MessageType::Request,
324            return_code: ReturnCode::Ok,
325        };
326
327        let response = SomeIpHeader::response_from(&request);
328
329        assert_eq!(response.service_id, request.service_id);
330        assert_eq!(response.method_id, request.method_id);
331        assert_eq!(response.client_id, request.client_id);
332        assert_eq!(response.session_id, request.session_id);
333        assert_eq!(response.message_type, MessageType::Response);
334    }
335
336    #[test]
337    fn test_parse_too_short() {
338        let data = [0u8; 10];
339        let result = SomeIpHeader::from_bytes(&data);
340        assert!(matches!(result, Err(SomeIpError::MessageTooShort { .. })));
341    }
342
343    #[test]
344    fn test_parse_wrong_protocol_version() {
345        let mut header = SomeIpHeader::default();
346        header.protocol_version = 0x02;
347        let mut bytes = header.to_bytes();
348        bytes[12] = 0x02; // Wrong protocol version
349
350        let result = SomeIpHeader::from_bytes(&bytes);
351        assert!(matches!(result, Err(SomeIpError::WrongProtocolVersion(0x02))));
352    }
353}