someip_sd_wire/
options.rs

1/// Option types for SOME/IP-SD
2///
3/// This module provides zero-copy wrappers around various option types
4/// used in SOME/IP Service Discovery messages. Options provide additional
5/// information like endpoint addresses, load balancing parameters, and
6/// configuration strings.
7
8use crate::error::Error;
9use crate::field;
10use byteorder::{ByteOrder, NetworkEndian};
11
12/// Result type alias using the crate's Error type.
13pub type Result<T> = core::result::Result<T, Error>;
14
15/// Option type enumeration for SOME/IP-SD options.
16///
17/// Defines the type field in option headers which determines how to
18/// interpret the option payload.
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20#[repr(u8)]
21pub enum OptionType {
22    /// Configuration option (0x01) - DNS-SD TXT record style key=value pairs
23    Configuration = 0x01,
24    /// Load balancing option (0x02) - Priority and weight for load balancing
25    LoadBalancing = 0x02,
26    /// IPv4 endpoint option (0x04) - IPv4 address and port
27    IPv4Endpoint = 0x04,
28    /// IPv6 endpoint option (0x06) - IPv6 address and port
29    IPv6Endpoint = 0x06,
30    /// IPv4 multicast option (0x14) - IPv4 multicast address and port
31    IPv4Multicast = 0x14,
32    /// IPv6 multicast option (0x16) - IPv6 multicast address and port
33    IPv6Multicast = 0x16,
34    /// IPv4 SD endpoint option (0x24) - IPv4 address and port for SD messages
35    IPv4SdEndpoint = 0x24,
36    /// IPv6 SD endpoint option (0x26) - IPv6 address and port for SD messages
37    IPv6SdEndpoint = 0x26,
38}
39
40impl OptionType {
41    /// Convert a u8 value to an OptionType.
42    ///
43    /// # Parameters
44    /// * `value` - The byte value to convert
45    ///
46    /// # Returns
47    /// * `Some(OptionType)` if value matches a known option type
48    /// * `None` if value is not a valid option type
49    pub fn from_u8(value: u8) -> Option<Self> {
50        match value {
51            0x01 => Some(OptionType::Configuration),
52            0x02 => Some(OptionType::LoadBalancing),
53            0x04 => Some(OptionType::IPv4Endpoint),
54            0x06 => Some(OptionType::IPv6Endpoint),
55            0x14 => Some(OptionType::IPv4Multicast),
56            0x16 => Some(OptionType::IPv6Multicast),
57            0x24 => Some(OptionType::IPv4SdEndpoint),
58            0x26 => Some(OptionType::IPv6SdEndpoint),
59            _ => None,
60        }
61    }
62
63    /// Convert the OptionType to its u8 representation.
64    ///
65    /// # Returns
66    /// The byte value of this option type
67    pub fn as_u8(&self) -> u8 {
68        *self as u8
69    }
70}
71
72/// Transport protocol enumeration.
73///
74/// Based on IANA protocol numbers for IP protocols.
75/// Used in endpoint options to specify TCP or UDP.
76#[derive(Debug, Clone, Copy, PartialEq, Eq)]
77#[repr(u8)]
78pub enum TransportProtocol {
79    /// TCP protocol (0x06)
80    TCP = 0x06,
81    /// UDP protocol (0x11)
82    UDP = 0x11,
83}
84
85impl TransportProtocol {
86    /// Convert a u8 value to a TransportProtocol.
87    ///
88    /// # Parameters
89    /// * `value` - The byte value to convert (IANA protocol number)
90    ///
91    /// # Returns
92    /// * `Some(TransportProtocol)` if value is 0x06 (TCP) or 0x11 (UDP)
93    /// * `None` if value is not a supported protocol
94    pub fn from_u8(value: u8) -> Option<Self> {
95        match value {
96            0x06 => Some(TransportProtocol::TCP),
97            0x11 => Some(TransportProtocol::UDP),
98            _ => None,
99        }
100    }
101
102    /// Convert the TransportProtocol to its u8 representation.
103    ///
104    /// # Returns
105    /// The IANA protocol number (0x06 for TCP, 0x11 for UDP)
106    pub fn as_u8(&self) -> u8 {
107        *self as u8
108    }
109}
110
111/// 1-bit discardable flag + 7 reserved bits packed into a u8.
112///
113/// The discardable flag indicates whether an option can be safely ignored
114/// by receivers that don't understand it. The remaining 7 bits are reserved
115/// and should be set to 0.
116#[derive(Debug, Clone, Copy, PartialEq, Eq)]
117pub struct DiscardableFlag(u8);
118
119impl DiscardableFlag {
120    /// Create a new DiscardableFlag with all bits set to 0.
121    ///
122    /// # Returns
123    /// A DiscardableFlag with discardable=false and reserved=0
124    pub fn new() -> Self {
125        DiscardableFlag(0)
126    }
127
128    /// Create a DiscardableFlag from a boolean value.
129    ///
130    /// # Parameters
131    /// * `discardable` - True to set the discardable bit, false to clear it
132    ///
133    /// # Returns
134    /// A DiscardableFlag with the specified discardable bit and reserved=0
135    pub fn from_bool(discardable: bool) -> Self {
136        DiscardableFlag(if discardable { 0x80 } else { 0x00 })
137    }
138
139    /// Check if the discardable bit is set.
140    ///
141    /// # Returns
142    /// True if the option can be discarded, false otherwise
143    pub fn is_discardable(&self) -> bool {
144        (self.0 & 0x80) != 0
145    }
146
147    /// Set or clear the discardable bit.
148    ///
149    /// # Parameters
150    /// * `discardable` - True to set the bit, false to clear it
151    pub fn set_discardable(&mut self, discardable: bool) {
152        if discardable {
153            self.0 |= 0x80;
154        } else {
155            self.0 &= 0x7F;
156        }
157    }
158
159    /// Get the 7-bit reserved field value.
160    ///
161    /// # Returns
162    /// The lower 7 bits (should be 0 in well-formed packets)
163    pub fn reserved(&self) -> u8 {
164        self.0 & 0x7F
165    }
166
167    /// Convert to the u8 wire format representation.
168    ///
169    /// # Returns
170    /// The packed byte with discardable bit (MSB) and reserved bits
171    pub fn as_u8(&self) -> u8 {
172        self.0
173    }
174
175    /// Create a DiscardableFlag from a u8 value.
176    ///
177    /// # Parameters
178    /// * `value` - The byte value (bit 7 = discardable, bits 6-0 = reserved)
179    ///
180    /// # Returns
181    /// A DiscardableFlag with the specified bit pattern
182    pub fn from_u8(value: u8) -> Self {
183        DiscardableFlag(value)
184    }
185}
186
187/// Zero-copy wrapper around Option header (4 bytes).
188///
189/// All SOME/IP-SD options start with this 4-byte header containing
190/// the length, type, and discardable flag.
191///
192/// Wire format (4 bytes):
193/// ```text
194/// 0               1               2               3
195/// 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
196/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
197/// |           Length              |     Type      |D|  Reserved   |
198/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
199/// ```
200#[derive(Debug, Clone, Copy)]
201pub struct OptionHeader<T: AsRef<[u8]>> {
202    buffer: T,
203}
204
205impl<T: AsRef<[u8]>> OptionHeader<T> {
206    /// Option header wire format size in bytes.
207    pub const LENGTH: usize = 4;
208
209    /// Create an OptionHeader without validation.
210    ///
211    /// # Parameters
212    /// * `buffer` - The buffer containing the 4-byte header
213    ///
214    /// # Safety
215    /// This does not validate buffer length. Use `new_checked` for validation.
216    pub fn new_unchecked(buffer: T) -> Self {
217        OptionHeader { buffer }
218    }
219
220    /// Create an OptionHeader from a buffer with length validation.
221    ///
222    /// # Parameters
223    /// * `buffer` - The buffer containing the 4-byte header
224    ///
225    /// # Returns
226    /// * `Ok(OptionHeader)` if buffer is at least 4 bytes
227    /// * `Err(Error)` if buffer is too short
228    pub fn new_checked(buffer: T) -> Result<Self> {
229        let header = Self::new_unchecked(buffer);
230        header.check_len()?;
231        Ok(header)
232    }
233
234    /// Validate that the buffer is at least 4 bytes long.
235    ///
236    /// # Returns
237    /// * `Ok(())` if buffer meets minimum length requirement
238    /// * `Err(Error)` if buffer is too short
239    pub fn check_len(&self) -> Result<()> {
240        if self.buffer.as_ref().len() < Self::LENGTH {
241            return Err(Error::BufferTooShort);
242        }
243        Ok(())
244    }
245
246    /// Validate the option type field contains a known option type.
247    ///
248    /// # Returns
249    /// * `Ok(())` if option type is valid
250    /// * `Err(Error::InvalidOptionType)` if option type is unknown
251    pub fn check_option_type(&self) -> Result<()> {
252        let type_val = self.option_type();
253        OptionType::from_u8(type_val)
254            .map(|_| ())
255            .ok_or(Error::InvalidOptionType(type_val))
256    }
257
258    /// Get the Length field (2 bytes at offset 0-1, network byte order).
259    ///
260    /// # Returns
261    /// Length of the option data (excluding the 4-byte header itself)
262    pub fn length(&self) -> u16 {
263        NetworkEndian::read_u16(&self.buffer.as_ref()[field::option_header::LENGTH])
264    }
265
266    /// Get the Type field (1 byte at offset 2).
267    ///
268    /// # Returns
269    /// Option type value (use OptionType::from_u8 to parse)
270    pub fn option_type(&self) -> u8 {
271        self.buffer.as_ref()[field::option_header::TYPE.start]
272    }
273
274    /// Get the Discardable flag and reserved bits (1 byte at offset 3).
275    ///
276    /// # Returns
277    /// DiscardableFlag containing the discardable bit and reserved bits
278    pub fn discardable_flag(&self) -> DiscardableFlag {
279        DiscardableFlag::from_u8(self.buffer.as_ref()[field::option_header::DISCARDABLE_FLAG_AND_RESERVED.start])
280    }
281}
282
283impl<T: AsRef<[u8]> + AsMut<[u8]>> OptionHeader<T> {
284    /// Set the Length field (2 bytes at offset 0-1, network byte order).
285    ///
286    /// # Parameters
287    /// * `value` - Length of option data (excluding the 4-byte header)
288    pub fn set_length(&mut self, value: u16) {
289        NetworkEndian::write_u16(&mut self.buffer.as_mut()[field::option_header::LENGTH], value);
290    }
291
292    /// Set the Type field (1 byte at offset 2).
293    ///
294    /// # Parameters
295    /// * `value` - Option type value (use OptionType::as_u8 for enum values)
296    pub fn set_option_type(&mut self, value: u8) {
297        self.buffer.as_mut()[field::option_header::TYPE.start] = value;
298    }
299
300    /// Set the Discardable flag and reserved bits (1 byte at offset 3).
301    ///
302    /// # Parameters
303    /// * `value` - DiscardableFlag with the desired bit pattern
304    pub fn set_discardable_flag(&mut self, value: DiscardableFlag) {
305        self.buffer.as_mut()[field::option_header::DISCARDABLE_FLAG_AND_RESERVED.start] = value.as_u8();
306    }
307}
308
309/// Zero-copy wrapper around IPv4 Endpoint Option (12 bytes total: 4 header + 8 data).
310///
311/// IPv4 endpoint options convey IPv4 address, port, and transport protocol
312/// for service endpoints.
313///
314/// Wire format (12 bytes):
315/// ```text
316/// 0               1               2               3
317/// 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
318/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
319/// |           Length              |     Type      |D|  Reserved   |
320/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
321/// |                       IPv4 Address                            |
322/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
323/// |    Reserved   |   Protocol    |             Port              |
324/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
325/// ```
326#[derive(Debug, Clone, Copy)]
327pub struct IPv4EndpointOption<T: AsRef<[u8]>> {
328    buffer: T,
329}
330
331impl<T: AsRef<[u8]>> IPv4EndpointOption<T> {
332    /// IPv4 endpoint option wire format size in bytes (4 header + 8 data).
333    pub const LENGTH: usize = 12;
334
335    /// Create an IPv4EndpointOption without validation.
336    ///
337    /// # Parameters
338    /// * `buffer` - The buffer containing the 12-byte option
339    ///
340    /// # Safety
341    /// This does not validate buffer length. Use `new_checked` for validation.
342    pub fn new_unchecked(buffer: T) -> Self {
343        IPv4EndpointOption { buffer }
344    }
345
346    /// Create an IPv4EndpointOption from a buffer with length validation.
347    ///
348    /// # Parameters
349    /// * `buffer` - The buffer containing the 12-byte option
350    ///
351    /// # Returns
352    /// * `Ok(IPv4EndpointOption)` if buffer is at least 12 bytes
353    /// * `Err(Error)` if buffer is too short
354    pub fn new_checked(buffer: T) -> Result<Self> {
355        let option = Self::new_unchecked(buffer);
356        option.check_len()?;
357        Ok(option)
358    }
359
360    /// Validate that the buffer is at least 12 bytes long.
361    ///
362    /// # Returns
363    /// * `Ok(())` if buffer meets minimum length requirement
364    /// * `Err(Error)` if buffer is too short
365    pub fn check_len(&self) -> Result<()> {
366        if self.buffer.as_ref().len() < Self::LENGTH {
367            return Err(Error::BufferTooShort);
368        }
369        Ok(())
370    }
371
372    /// Get a view of the option header (first 4 bytes).
373    ///
374    /// # Returns
375    /// OptionHeader wrapper around the header bytes
376    pub fn header(&self) -> OptionHeader<&[u8]> {
377        OptionHeader::new_unchecked(&self.buffer.as_ref()[..4])
378    }
379
380    /// Get the IPv4 address (4 bytes at offset 4-7).
381    ///
382    /// # Returns
383    /// The IPv4 address as a 4-byte array in network byte order
384    pub fn ipv4_address(&self) -> [u8; 4] {
385        let bytes = &self.buffer.as_ref()[4..];
386        [bytes[0], bytes[1], bytes[2], bytes[3]]
387    }
388
389    /// Get the transport protocol (1 byte at offset 9).
390    ///
391    /// # Returns
392    /// Protocol value (0x06=TCP, 0x11=UDP)
393    pub fn transport_protocol(&self) -> u8 {
394        self.buffer.as_ref()[4 + field::ipv4_endpoint_option::TRANSPORT_PROTOCOL.start]
395    }
396
397    /// Validate the transport protocol field.
398    ///
399    /// # Returns
400    /// * `Ok(())` if protocol is TCP (0x06) or UDP (0x11)
401    /// * `Err(Error::InvalidProtocol)` if protocol is unknown
402    pub fn check_protocol(&self) -> Result<()> {
403        let proto = self.transport_protocol();
404        TransportProtocol::from_u8(proto)
405            .map(|_| ())
406            .ok_or(Error::InvalidProtocol(proto))
407    }
408
409    /// Get the port number (2 bytes at offset 10-11, network byte order).
410    ///
411    /// # Returns
412    /// The port number
413    pub fn port(&self) -> u16 {
414        NetworkEndian::read_u16(&self.buffer.as_ref()[4 + field::ipv4_endpoint_option::PORT.start..])
415    }
416}
417
418impl<T: AsRef<[u8]> + AsMut<[u8]>> IPv4EndpointOption<T> {
419    /// Set the IPv4 address (4 bytes at offset 4-7).
420    ///
421    /// # Parameters
422    /// * `addr` - The IPv4 address as a 4-byte array in network byte order
423    pub fn set_ipv4_address(&mut self, addr: [u8; 4]) {
424        self.buffer.as_mut()[4..8].copy_from_slice(&addr);
425    }
426
427    /// Set the transport protocol (1 byte at offset 9).
428    ///
429    /// # Parameters
430    /// * `proto` - Protocol value (0x06=TCP, 0x11=UDP)
431    pub fn set_transport_protocol(&mut self, proto: u8) {
432        self.buffer.as_mut()[4 + field::ipv4_endpoint_option::TRANSPORT_PROTOCOL.start] = proto;
433    }
434
435    /// Set the port number (2 bytes at offset 10-11, network byte order).
436    ///
437    /// # Parameters
438    /// * `port` - The port number
439    pub fn set_port(&mut self, port: u16) {
440        NetworkEndian::write_u16(&mut self.buffer.as_mut()[4 + field::ipv4_endpoint_option::PORT.start..], port);
441    }
442}
443
444/// Zero-copy wrapper around IPv6 Endpoint Option (24 bytes total: 4 header + 20 data).
445///
446/// IPv6 endpoint options convey IPv6 address, port, and transport protocol
447/// for service endpoints.
448///
449/// Wire format (24 bytes):
450/// ```text
451/// 0               1               2               3
452/// 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
453/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
454/// |           Length              |     Type      |D|  Reserved   |
455/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
456/// |                                                               |
457/// |                       IPv6 Address (16 bytes)                 |
458/// |                                                               |
459/// |                                                               |
460/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
461/// |    Reserved   |   Protocol    |             Port              |
462/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
463/// ```
464#[derive(Debug, Clone, Copy)]
465pub struct IPv6EndpointOption<T: AsRef<[u8]>> {
466    buffer: T,
467}
468
469impl<T: AsRef<[u8]>> IPv6EndpointOption<T> {
470    /// IPv6 endpoint option wire format size in bytes (4 header + 20 data).
471    pub const LENGTH: usize = 24;
472
473    /// Create an IPv6EndpointOption without validation.
474    ///
475    /// # Parameters
476    /// * `buffer` - The buffer containing the 24-byte option
477    ///
478    /// # Safety
479    /// This does not validate buffer length. Use `new_checked` for validation.
480    pub fn new_unchecked(buffer: T) -> Self {
481        IPv6EndpointOption { buffer }
482    }
483
484    /// Create an IPv6EndpointOption from a buffer with length validation.
485    ///
486    /// # Parameters
487    /// * `buffer` - The buffer containing the 24-byte option
488    ///
489    /// # Returns
490    /// * `Ok(IPv6EndpointOption)` if buffer is at least 24 bytes
491    /// * `Err(Error)` if buffer is too short
492    pub fn new_checked(buffer: T) -> Result<Self> {
493        let option = Self::new_unchecked(buffer);
494        option.check_len()?;
495        Ok(option)
496    }
497
498    /// Validate that the buffer is at least 24 bytes long.
499    ///
500    /// # Returns
501    /// * `Ok(())` if buffer meets minimum length requirement
502    /// * `Err(Error)` if buffer is too short
503    pub fn check_len(&self) -> Result<()> {
504        if self.buffer.as_ref().len() < Self::LENGTH {
505            return Err(Error::BufferTooShort);
506        }
507        Ok(())
508    }
509
510    /// Get a view of the option header (first 4 bytes).
511    ///
512    /// # Returns
513    /// OptionHeader wrapper around the header bytes
514    pub fn header(&self) -> OptionHeader<&[u8]> {
515        OptionHeader::new_unchecked(&self.buffer.as_ref()[..4])
516    }
517
518    /// Get the IPv6 address (16 bytes at offset 4-19).
519    ///
520    /// # Returns
521    /// The IPv6 address as a 16-byte array in network byte order
522    pub fn ipv6_address(&self) -> [u8; 16] {
523        let bytes = &self.buffer.as_ref()[4..];
524        let mut addr = [0u8; 16];
525        addr.copy_from_slice(&bytes[0..16]);
526        addr
527    }
528
529    /// Get the transport protocol (1 byte at offset 21).
530    ///
531    /// # Returns
532    /// Protocol value (0x06=TCP, 0x11=UDP)
533    pub fn transport_protocol(&self) -> u8 {
534        self.buffer.as_ref()[4 + field::ipv6_endpoint_option::TRANSPORT_PROTOCOL.start]
535    }
536
537    /// Validate the transport protocol field.
538    ///
539    /// # Returns
540    /// * `Ok(())` if protocol is TCP (0x06) or UDP (0x11)
541    /// * `Err(Error::InvalidProtocol)` if protocol is unknown
542    pub fn check_protocol(&self) -> Result<()> {
543        let proto = self.transport_protocol();
544        TransportProtocol::from_u8(proto)
545            .map(|_| ())
546            .ok_or(Error::InvalidProtocol(proto))
547    }
548
549    /// Get the port number (2 bytes at offset 22-23, network byte order).
550    ///
551    /// # Returns
552    /// The port number
553    pub fn port(&self) -> u16 {
554        NetworkEndian::read_u16(&self.buffer.as_ref()[4 + field::ipv6_endpoint_option::PORT.start..])
555    }
556}
557
558impl<T: AsRef<[u8]> + AsMut<[u8]>> IPv6EndpointOption<T> {
559    /// Set the IPv6 address (16 bytes at offset 4-19).
560    ///
561    /// # Parameters
562    /// * `addr` - The IPv6 address as a 16-byte array in network byte order
563    pub fn set_ipv6_address(&mut self, addr: [u8; 16]) {
564        self.buffer.as_mut()[4..20].copy_from_slice(&addr);
565    }
566
567    /// Set the transport protocol (1 byte at offset 21).
568    ///
569    /// # Parameters
570    /// * `proto` - Protocol value (0x06=TCP, 0x11=UDP)
571    pub fn set_transport_protocol(&mut self, proto: u8) {
572        self.buffer.as_mut()[4 + field::ipv6_endpoint_option::TRANSPORT_PROTOCOL.start] = proto;
573    }
574
575    /// Set the port number (2 bytes at offset 22-23, network byte order).
576    ///
577    /// # Parameters
578    /// * `port` - The port number
579    pub fn set_port(&mut self, port: u16) {
580        NetworkEndian::write_u16(&mut self.buffer.as_mut()[4 + field::ipv6_endpoint_option::PORT.start..], port);
581    }
582}
583
584/// Zero-copy wrapper around Load Balancing Option (8 bytes total: 4 header + 4 data).
585///
586/// Load balancing options provide priority and weight values for server selection.
587///
588/// Wire format (8 bytes):
589/// ```text
590/// 0               1               2               3
591/// 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
592/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
593/// |           Length              |     Type      |D|  Reserved   |
594/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
595/// |            Priority           |            Weight             |
596/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
597/// ```
598#[derive(Debug, Clone, Copy)]
599pub struct LoadBalancingOption<T: AsRef<[u8]>> {
600    buffer: T,
601}
602
603impl<T: AsRef<[u8]>> LoadBalancingOption<T> {
604    /// Load balancing option wire format size in bytes (4 header + 4 data).
605    pub const LENGTH: usize = 8;
606
607    /// Create a LoadBalancingOption without validation.
608    ///
609    /// # Parameters
610    /// * `buffer` - The buffer containing the 8-byte option
611    ///
612    /// # Safety
613    /// This does not validate buffer length. Use `new_checked` for validation.
614    pub fn new_unchecked(buffer: T) -> Self {
615        LoadBalancingOption { buffer }
616    }
617
618    /// Create a LoadBalancingOption from a buffer with length validation.
619    ///
620    /// # Parameters
621    /// * `buffer` - The buffer containing the 8-byte option
622    ///
623    /// # Returns
624    /// * `Ok(LoadBalancingOption)` if buffer is at least 8 bytes
625    /// * `Err(Error)` if buffer is too short
626    pub fn new_checked(buffer: T) -> Result<Self> {
627        let option = Self::new_unchecked(buffer);
628        option.check_len()?;
629        Ok(option)
630    }
631
632    /// Validate that the buffer is at least 8 bytes long.
633    ///
634    /// # Returns
635    /// * `Ok(())` if buffer meets minimum length requirement
636    /// * `Err(Error)` if buffer is too short
637    pub fn check_len(&self) -> Result<()> {
638        if self.buffer.as_ref().len() < Self::LENGTH {
639            return Err(Error::BufferTooShort);
640        }
641        Ok(())
642    }
643
644    /// Get a view of the option header (first 4 bytes).
645    ///
646    /// # Returns
647    /// OptionHeader wrapper around the header bytes
648    pub fn header(&self) -> OptionHeader<&[u8]> {
649        OptionHeader::new_unchecked(&self.buffer.as_ref()[..4])
650    }
651
652    /// Get the priority value (2 bytes at offset 4-5, network byte order).
653    ///
654    /// # Returns
655    /// Priority value (lower is higher priority)
656    pub fn priority(&self) -> u16 {
657        NetworkEndian::read_u16(&self.buffer.as_ref()[4 + field::load_balancing_option::PRIORITY.start..])
658    }
659
660    /// Get the weight value (2 bytes at offset 6-7, network byte order).
661    ///
662    /// # Returns
663    /// Weight value for load distribution
664    pub fn weight(&self) -> u16 {
665        NetworkEndian::read_u16(&self.buffer.as_ref()[4 + field::load_balancing_option::WEIGHT.start..])
666    }
667}
668
669impl<T: AsRef<[u8]> + AsMut<[u8]>> LoadBalancingOption<T> {
670    /// Set the priority value (2 bytes at offset 4-5, network byte order).
671    ///
672    /// # Parameters
673    /// * `priority` - Priority value (lower is higher priority)
674    pub fn set_priority(&mut self, priority: u16) {
675        NetworkEndian::write_u16(&mut self.buffer.as_mut()[4 + field::load_balancing_option::PRIORITY.start..], priority);
676    }
677
678    /// Set the weight value (2 bytes at offset 6-7, network byte order).
679    ///
680    /// # Parameters
681    /// * `weight` - Weight value for load distribution
682    pub fn set_weight(&mut self, weight: u16) {
683        NetworkEndian::write_u16(&mut self.buffer.as_mut()[4 + field::load_balancing_option::WEIGHT.start..], weight);
684    }
685}
686
687/// High-level representation of an IPv4 Endpoint Option.
688///
689/// This provides a builder-style API for constructing and parsing IPv4 endpoint options
690/// without manually managing byte arrays.
691#[derive(Debug, Clone, Copy, PartialEq, Eq)]
692pub struct IPv4EndpointOptionRepr {
693    /// IPv4 address (4 bytes)
694    pub ipv4_address: [u8; 4],
695    /// Transport protocol (TCP=0x06, UDP=0x11)
696    pub protocol: TransportProtocol,
697    /// Port number
698    pub port: u16,
699}
700
701impl IPv4EndpointOptionRepr {
702    /// Parse an IPv4EndpointOption into a high-level representation.
703    ///
704    /// # Parameters
705    /// * `option` - The IPv4EndpointOption to parse
706    ///
707    /// # Returns
708    /// IPv4EndpointOptionRepr with all fields populated
709    ///
710    /// # Errors
711    /// Returns Error::InvalidProtocol if protocol is not TCP or UDP
712    pub fn parse<T: AsRef<[u8]>>(option: &IPv4EndpointOption<T>) -> Result<Self> {
713        option.check_protocol()?;
714        
715        let protocol = TransportProtocol::from_u8(option.transport_protocol())
716            .ok_or(Error::InvalidProtocol(option.transport_protocol()))?;
717
718        Ok(IPv4EndpointOptionRepr {
719            ipv4_address: option.ipv4_address(),
720            protocol,
721            port: option.port(),
722        })
723    }
724
725    /// Emit this representation into a buffer.
726    ///
727    /// # Parameters
728    /// * `buffer` - 12-byte buffer to write the option into
729    ///
730    /// # Returns
731    /// Number of bytes written (always 12)
732    pub fn emit(&self, buffer: &mut [u8]) -> usize {
733        let mut header = OptionHeader::new_unchecked(&mut buffer[..4]);
734        header.set_length(9);
735        header.set_option_type(OptionType::IPv4Endpoint.as_u8());
736        
737        let mut option = IPv4EndpointOption::new_unchecked(buffer);
738        option.set_ipv4_address(self.ipv4_address);
739        option.set_transport_protocol(self.protocol.as_u8());
740        option.set_port(self.port);
741        
742        Self::buffer_len()
743    }
744
745    /// Get the wire format size of this option (always 12 bytes: 4 header + 8 payload).
746    pub const fn buffer_len() -> usize {
747        12
748    }
749}
750
751/// High-level representation of an IPv6 Endpoint Option.
752///
753/// This provides a builder-style API for constructing and parsing IPv6 endpoint options
754/// without manually managing byte arrays.
755#[derive(Debug, Clone, Copy, PartialEq, Eq)]
756pub struct IPv6EndpointOptionRepr {
757    /// IPv6 address (16 bytes)
758    pub ipv6_address: [u8; 16],
759    /// Transport protocol (TCP=0x06, UDP=0x11)
760    pub protocol: TransportProtocol,
761    /// Port number
762    pub port: u16,
763}
764
765impl IPv6EndpointOptionRepr {
766    /// Parse an IPv6EndpointOption into a high-level representation.
767    ///
768    /// # Parameters
769    /// * `option` - The IPv6EndpointOption to parse
770    ///
771    /// # Returns
772    /// IPv6EndpointOptionRepr with all fields populated
773    ///
774    /// # Errors
775    /// Returns Error::InvalidProtocol if protocol is not TCP or UDP
776    pub fn parse<T: AsRef<[u8]>>(option: &IPv6EndpointOption<T>) -> Result<Self> {
777        option.check_protocol()?;
778        
779        let protocol = TransportProtocol::from_u8(option.transport_protocol())
780            .ok_or(Error::InvalidProtocol(option.transport_protocol()))?;
781
782        Ok(IPv6EndpointOptionRepr {
783            ipv6_address: option.ipv6_address(),
784            protocol,
785            port: option.port(),
786        })
787    }
788
789    /// Emit this representation into a buffer.
790    ///
791    /// # Parameters
792    /// * `buffer` - 24-byte buffer to write the option into
793    ///
794    /// # Returns
795    /// Number of bytes written (always 24)
796    pub fn emit(&self, buffer: &mut [u8]) -> usize {
797        let mut header = OptionHeader::new_unchecked(&mut buffer[..4]);
798        header.set_length(21);
799        header.set_option_type(OptionType::IPv6Endpoint.as_u8());
800        
801        let mut option = IPv6EndpointOption::new_unchecked(buffer);
802        option.set_ipv6_address(self.ipv6_address);
803        option.set_transport_protocol(self.protocol.as_u8());
804        option.set_port(self.port);
805        
806        Self::buffer_len()
807    }
808
809    /// Get the wire format size of this option (always 24 bytes: 4 header + 20 payload).
810    pub const fn buffer_len() -> usize {
811        24
812    }
813}
814
815/// High-level representation of a Load Balancing Option.
816///
817/// This provides a builder-style API for constructing and parsing load balancing options
818/// without manually managing byte arrays.
819#[derive(Debug, Clone, Copy, PartialEq, Eq)]
820pub struct LoadBalancingOptionRepr {
821    /// Priority value (lower = higher priority)
822    pub priority: u16,
823    /// Weight for load distribution
824    pub weight: u16,
825}
826
827impl LoadBalancingOptionRepr {
828    /// Parse a LoadBalancingOption into a high-level representation.
829    ///
830    /// # Parameters
831    /// * `option` - The LoadBalancingOption to parse
832    ///
833    /// # Returns
834    /// LoadBalancingOptionRepr with all fields populated
835    pub fn parse<T: AsRef<[u8]>>(option: &LoadBalancingOption<T>) -> Self {
836        LoadBalancingOptionRepr {
837            priority: option.priority(),
838            weight: option.weight(),
839        }
840    }
841
842    /// Emit this representation into a buffer.
843    ///
844    /// # Parameters
845    /// * `buffer` - 9-byte buffer to write the option into
846    ///
847    /// # Returns
848    /// Number of bytes written (always 9)
849    pub fn emit(&self, buffer: &mut [u8]) -> usize {
850        let mut header = OptionHeader::new_unchecked(&mut buffer[..4]);
851        header.set_length(5);
852        header.set_option_type(OptionType::LoadBalancing.as_u8());
853        
854        let mut option = LoadBalancingOption::new_unchecked(buffer);
855        option.set_priority(self.priority);
856        option.set_weight(self.weight);
857        
858        Self::buffer_len()
859    }
860
861    /// Get the wire format size of this option (always 9 bytes: 4 header + 5 payload).
862    pub const fn buffer_len() -> usize {
863        9
864    }
865}
866
867#[cfg(test)]
868mod tests {
869    use super::*;
870
871    #[test]
872    fn test_option_header() {
873        let mut buffer = [0u8; 4];
874        let mut header = OptionHeader::new_unchecked(&mut buffer[..]);
875        
876        header.set_length(8);
877        header.set_option_type(OptionType::Configuration.as_u8());
878        header.set_discardable_flag(DiscardableFlag::from_bool(true));
879        
880        assert_eq!(header.length(), 8);
881        assert_eq!(header.option_type(), 0x01);
882        assert!(header.discardable_flag().is_discardable());
883    }
884
885    #[test]
886    fn test_ipv4_endpoint_option() {
887        let mut buffer = [0u8; 12];
888        let mut option = IPv4EndpointOption::new_unchecked(&mut buffer[..]);
889        
890        option.set_ipv4_address([192, 168, 1, 1]);
891        option.set_transport_protocol(TransportProtocol::UDP.as_u8());
892        option.set_port(30490);
893        
894        assert_eq!(option.ipv4_address(), [192, 168, 1, 1]);
895        assert_eq!(option.transport_protocol(), 0x11);
896        assert_eq!(option.port(), 30490);
897    }
898
899    #[test]
900    fn test_ipv6_endpoint_option() {
901        let mut buffer = [0u8; 24];
902        let mut option = IPv6EndpointOption::new_unchecked(&mut buffer[..]);
903        
904        let addr = [0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1];
905        option.set_ipv6_address(addr);
906        option.set_transport_protocol(TransportProtocol::TCP.as_u8());
907        option.set_port(30490);
908        
909        assert_eq!(option.ipv6_address(), addr);
910        assert_eq!(option.transport_protocol(), 0x06);
911        assert_eq!(option.port(), 30490);
912    }
913
914    #[test]
915    fn test_load_balancing_option() {
916        let mut buffer = [0u8; 8];
917        let mut option = LoadBalancingOption::new_unchecked(&mut buffer[..]);
918        
919        option.set_priority(100);
920        option.set_weight(50);
921        
922        assert_eq!(option.priority(), 100);
923        assert_eq!(option.weight(), 50);
924    }
925
926    #[test]
927    fn test_discardable_flag() {
928        let mut flag = DiscardableFlag::new();
929        assert!(!flag.is_discardable());
930        assert_eq!(flag.reserved(), 0x00);
931
932        flag.set_discardable(true);
933        assert!(flag.is_discardable());
934        assert_eq!(flag.as_u8(), 0x80);
935
936        let flag2 = DiscardableFlag::from_bool(true);
937        assert!(flag2.is_discardable());
938    }
939
940    #[test]
941    fn test_option_header_type_validation() {
942        // Valid option types
943        let mut buffer = [0u8; 4];
944        buffer[2] = 0x01; // Configuration
945        let header = OptionHeader::new_unchecked(&buffer[..]);
946        assert!(header.check_option_type().is_ok());
947
948        buffer[2] = 0x04; // IPv4Endpoint
949        let header = OptionHeader::new_unchecked(&buffer[..]);
950        assert!(header.check_option_type().is_ok());
951
952        buffer[2] = 0x24; // IPv4SdEndpoint
953        let header = OptionHeader::new_unchecked(&buffer[..]);
954        assert!(header.check_option_type().is_ok());
955
956        // Invalid option types
957        buffer[2] = 0xFF; // Unknown type
958        let header = OptionHeader::new_unchecked(&buffer[..]);
959        assert_eq!(header.check_option_type(), Err(Error::InvalidOptionType(0xFF)));
960
961        buffer[2] = 0x03; // Not a valid option type
962        let header = OptionHeader::new_unchecked(&buffer[..]);
963        assert_eq!(header.check_option_type(), Err(Error::InvalidOptionType(0x03)));
964
965        buffer[2] = 0x99; // Random invalid type
966        let header = OptionHeader::new_unchecked(&buffer[..]);
967        assert_eq!(header.check_option_type(), Err(Error::InvalidOptionType(0x99)));
968    }
969
970    #[test]
971    fn test_ipv4_endpoint_protocol_validation() {
972        // Valid protocols
973        let mut buffer = [0u8; 12];
974        buffer[9] = 0x06; // TCP
975        let option = IPv4EndpointOption::new_unchecked(&buffer[..]);
976        assert!(option.check_protocol().is_ok());
977
978        buffer[9] = 0x11; // UDP
979        let option = IPv4EndpointOption::new_unchecked(&buffer[..]);
980        assert!(option.check_protocol().is_ok());
981
982        // Invalid protocols
983        buffer[9] = 0x01; // ICMP (not supported)
984        let option = IPv4EndpointOption::new_unchecked(&buffer[..]);
985        assert_eq!(option.check_protocol(), Err(Error::InvalidProtocol(0x01)));
986
987        buffer[9] = 0xFF; // Unknown protocol
988        let option = IPv4EndpointOption::new_unchecked(&buffer[..]);
989        assert_eq!(option.check_protocol(), Err(Error::InvalidProtocol(0xFF)));
990
991        buffer[9] = 0x00; // Reserved
992        let option = IPv4EndpointOption::new_unchecked(&buffer[..]);
993        assert_eq!(option.check_protocol(), Err(Error::InvalidProtocol(0x00)));
994    }
995
996    #[test]
997    fn test_ipv6_endpoint_protocol_validation() {
998        // Valid protocols
999        let mut buffer = [0u8; 24];
1000        buffer[21] = 0x06; // TCP
1001        let option = IPv6EndpointOption::new_unchecked(&buffer[..]);
1002        assert!(option.check_protocol().is_ok());
1003
1004        buffer[21] = 0x11; // UDP
1005        let option = IPv6EndpointOption::new_unchecked(&buffer[..]);
1006        assert!(option.check_protocol().is_ok());
1007
1008        // Invalid protocols
1009        buffer[21] = 0x02; // IGMP (not supported)
1010        let option = IPv6EndpointOption::new_unchecked(&buffer[..]);
1011        assert_eq!(option.check_protocol(), Err(Error::InvalidProtocol(0x02)));
1012
1013        buffer[21] = 0xFF; // Unknown protocol
1014        let option = IPv6EndpointOption::new_unchecked(&buffer[..]);
1015        assert_eq!(option.check_protocol(), Err(Error::InvalidProtocol(0xFF)));
1016
1017        buffer[21] = 0x3A; // IPv6-ICMP (not supported in this context)
1018        let option = IPv6EndpointOption::new_unchecked(&buffer[..]);
1019        assert_eq!(option.check_protocol(), Err(Error::InvalidProtocol(0x3A)));
1020    }
1021}