1use libc::IPPROTO_TCP;
6use num_enum::{IntoPrimitive, TryFromPrimitive};
7use socket2::{Domain, Protocol, SockAddr, Socket, Type};
8use std::io::{Read, Write};
9use std::net::SocketAddr;
10use std::{mem::size_of, time::Duration};
11use winnow::binary::{le_u16, le_u32, le_u64, le_u8};
12use winnow::combinator::repeat;
13use winnow::error::{ContextError, ErrMode};
14use winnow::token::take;
15use winnow::{ModalResult, Parser};
16
17const PF_KEY: i32 = 27;
19const PF_KEY_V2: u8 = 2;
21const MAX_STR_AUTH_KEY_SIZE: usize = 80;
23
24#[derive(Debug, IntoPrimitive, TryFromPrimitive, Copy, Clone)]
26#[repr(u8)]
27pub enum MessageType {
28 Reserved = 0,
29 GetSpi = 1,
30 Update = 2,
31 Add = 3,
32 Delete = 4,
33 Get = 5,
34 Acquire = 6,
35 Register = 7,
36 Expire = 8,
37 Flush = 9,
38 Dump = 10,
39 Promisc = 11,
40 InverseAcquire = 12,
41 UpdatePair = 13,
42 DelPair = 14,
43 DelPairState = 15,
44}
45
46#[derive(Debug, IntoPrimitive, TryFromPrimitive, Copy, Clone)]
48#[repr(u8)]
49pub enum SaType {
50 Unspec = 0,
51 Ah = 2,
52 Esp = 3,
53 TcpSig = 4,
54 Rsvp = 5,
55 OspvV2 = 6,
56 RipV2 = 7,
57 Mip = 8,
58}
59
60#[derive(Debug, IntoPrimitive, TryFromPrimitive, Copy, Clone)]
62#[repr(u16)]
63pub enum SaExtType {
64 Sa = 1,
65 LifetimeCurrent = 2,
66 LifetimeHard = 3,
67 LifetimeSoft = 4,
68 AddressSrc = 5,
69 AddressDst = 6,
70 StrAuth = 28,
92}
93
94#[derive(Debug, IntoPrimitive, TryFromPrimitive, Copy, Clone)]
96#[repr(u8)]
97pub enum SaAuthType {
98 None = 0,
99 Md5 = 1,
100 Md5Hmac = 2,
101 Sha1Hmac = 3,
102 Sha256Hmac = 5,
103 Sha384Hmac = 6,
104 Sha512Hmac = 7,
105}
106
107#[derive(Debug, IntoPrimitive, TryFromPrimitive, Copy, Clone)]
109#[repr(u8)]
110pub enum SaEncryptType {
111 None = 0,
112 DesCbc = 2,
113 DesCbc3 = 3,
114 Blowfish = 7,
115 Null = 11,
116 Aes = 12,
117 AesCcm8 = 14,
118 AesCcm12 = 15,
119 AesCcm16 = 16,
120 AesGcm8 = 18,
121 AesGcm12 = 19,
122 AesGcm16 = 20,
123}
124
125#[derive(Debug, IntoPrimitive, TryFromPrimitive, Copy, Clone)]
127#[repr(u8)]
128pub enum SaState {
129 Larval = 0,
130 Mature = 1,
131 Dying = 2,
132 Dead = 3,
133}
134
135#[derive(Debug)]
137#[repr(C, packed)]
138pub struct Header {
139 pub version: u8,
141 pub typ: MessageType,
143 pub errno: u8,
145 pub sa_typ: SaType,
147 pub len: u16,
149 pub reserved: u16,
152 pub seq: u32,
154 pub pid: u32,
156}
157
158impl Header {
159 pub fn new(typ: MessageType, sa_typ: SaType, len: usize) -> Self {
160 Header {
161 version: PF_KEY_V2,
162 typ,
163 errno: 0,
164 sa_typ,
165 len: u16::try_from(len).unwrap() >> 3,
166 reserved: 0,
167 seq: rand::random(),
168 pid: std::process::id(),
169 }
170 }
171}
172
173#[derive(Debug)]
176pub enum Extension {
177 Association(Association),
178 Lifetime(Lifetime),
179 Address(Address),
180 StrAuth(StrAuth),
181}
182
183#[derive(Clone, Copy, Debug)]
185#[repr(C, packed)]
186pub struct Association {
187 pub len: u16,
189 pub typ: SaExtType,
191 pub spi: u32,
193 pub replay: u8,
195 pub state: SaState,
197 pub auth: SaAuthType,
199 pub encrypt: SaEncryptType,
201 pub flags: u32,
203}
204
205impl Default for Association {
206 fn default() -> Self {
207 Association {
208 len: u16::try_from(size_of::<Association>()).unwrap() >> 3,
209 typ: SaExtType::Sa,
210 spi: 0, replay: 0,
212 state: SaState::Mature,
213 auth: SaAuthType::Md5,
214 encrypt: SaEncryptType::None,
215 flags: 0,
216 }
217 }
218}
219
220#[derive(Debug)]
222#[repr(C, packed)]
223pub struct Lifetime {
224 pub len: u16,
226 pub typ: SaExtType,
228 pub alloc: u32,
230 pub bytes: u64,
232 pub addtime: u64,
234 pub usetime: u64,
236}
237
238impl Lifetime {
239 pub fn hard(addtime: Duration) -> Self {
241 Lifetime {
242 len: u16::try_from(size_of::<Lifetime>()).unwrap() >> 3,
243 typ: SaExtType::LifetimeHard,
244 alloc: 0, bytes: 0, addtime: addtime.as_secs(),
247 usetime: 0,
248 }
249 }
250 pub fn soft(addtime: Duration) -> Self {
252 Lifetime {
253 len: u16::try_from(size_of::<Lifetime>()).unwrap() >> 3,
254 typ: SaExtType::LifetimeSoft,
255 alloc: 0, bytes: 0, addtime: addtime.as_secs(),
258 usetime: 0,
259 }
260 }
261}
262
263#[repr(C, packed)]
265pub struct Address {
266 pub len: u16,
268 pub typ: SaExtType,
270 pub proto: u8,
272 pub prefix_len: u8,
274 pub reserved: u16,
276 pub sockaddr: SockAddr,
278}
279
280impl std::fmt::Debug for Address {
281 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
282 let s = unsafe { (self as *const Address).read_unaligned() };
283 let len = s.len;
284 let typ = s.typ;
285 let proto = s.proto;
286 let plen = s.prefix_len;
287 let res = s.reserved;
288 let sa = s.sockaddr;
289 let sa = sa.as_socket();
290 f.debug_struct("Address")
291 .field("len", &len)
292 .field("typ", &typ)
293 .field("proto", &proto)
294 .field("prefix_len", &plen)
295 .field("reserved", &res)
296 .field("sockaddr", &sa)
297 .finish()
298 }
299}
300
301impl Address {
302 pub fn src(sockaddr: SockAddr, proto: u8) -> Self {
304 Self::new(sockaddr, proto, SaExtType::AddressSrc)
305 }
306
307 pub fn dst(sockaddr: SockAddr, proto: u8) -> Self {
309 Self::new(sockaddr, proto, SaExtType::AddressDst)
310 }
311
312 pub fn new(sockaddr: SockAddr, proto: u8, typ: SaExtType) -> Self {
314 Address {
315 len: u16::try_from(size_of::<Address>()).unwrap() >> 3,
316 typ,
317 proto,
318 prefix_len: 0,
319 reserved: 0,
320 sockaddr,
321 }
322 }
323
324 pub fn get_sockaddr(&self) -> Option<SocketAddr> {
326 let s = unsafe { (self as *const Address).read_unaligned() };
327 let sa = s.sockaddr;
328 sa.as_socket()
329 }
330}
331
332#[repr(C, packed)]
334pub struct StrAuth {
335 pub len: u16,
337 pub typ: SaExtType,
339 pub bits: u16,
341 pub reserved: u16,
343 pub data: [u8; MAX_STR_AUTH_KEY_SIZE],
345}
346
347impl std::fmt::Debug for StrAuth {
348 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
349 let s = unsafe { (self as *const StrAuth).read_unaligned() };
350 let len = s.len;
351 let typ = s.typ;
352 let bits = s.bits;
353 let res = s.reserved;
354 let key = s.key();
355 f.debug_struct("StrAuth")
356 .field("len", &len)
357 .field("typ", &typ)
358 .field("bits", &bits)
359 .field("reserved", &res)
360 .field("data", &key)
361 .finish()
362 }
363}
364
365impl StrAuth {
366 pub fn new(authstring: &str) -> Self {
368 let mut key = StrAuth {
369 len: u16::try_from(size_of::<StrAuth>()).unwrap() >> 3,
370 typ: SaExtType::StrAuth,
371 bits: u16::try_from(authstring.len() << 3).unwrap(),
372 reserved: 0,
373 data: [0; MAX_STR_AUTH_KEY_SIZE],
374 };
375 key.data[..authstring.len()].copy_from_slice(authstring.as_bytes());
376 key
377 }
378
379 pub fn key(&self) -> String {
381 let s = unsafe { (self as *const StrAuth).read_unaligned() };
382 let bits = s.bits;
383 let bytelen = (bits >> 3) as usize;
384 let data = s.data;
385 String::from_utf8_lossy(&data[..bytelen]).to_string()
386 }
387}
388
389#[repr(C, packed)]
391pub struct TcpMd5AddKeyRequest {
392 pub header: Header,
394 pub association: Association,
396 pub lifetime: Lifetime,
398 pub src: Address,
400 pub dst: Address,
402 pub key: StrAuth,
404}
405
406impl TcpMd5AddKeyRequest {
407 pub fn new(
409 src: SockAddr,
410 dst: SockAddr,
411 authstring: &str,
412 valid_time: Duration,
413 ) -> Self {
414 Self {
415 header: Header::new(
416 MessageType::Add,
417 SaType::TcpSig,
418 size_of::<Self>(),
419 ),
420 association: Association::default(),
421 lifetime: Lifetime::hard(valid_time),
422 src: Address::src(src, IPPROTO_TCP as u8),
423 dst: Address::dst(dst, IPPROTO_TCP as u8),
424 key: StrAuth::new(authstring),
425 }
426 }
427}
428
429#[repr(C, packed)]
431pub struct TcpMd5UpdateKeyRequest {
432 pub header: Header,
434 pub association: Association,
436 pub lifetime: Lifetime,
438 pub src: Address,
440 pub dst: Address,
442}
443
444impl TcpMd5UpdateKeyRequest {
445 pub fn new(src: SockAddr, dst: SockAddr, valid_time: Duration) -> Self {
447 Self {
448 header: Header::new(
449 MessageType::Update,
450 SaType::TcpSig,
451 size_of::<Self>(),
452 ),
453 association: Association::default(),
454 lifetime: Lifetime::hard(valid_time),
455 src: Address::src(src, IPPROTO_TCP as u8),
456 dst: Address::dst(dst, IPPROTO_TCP as u8),
457 }
458 }
459}
460
461#[repr(C, packed)]
463pub struct TcpMd5DeleteKeyRequest {
464 pub header: Header,
466 pub association: Association,
468 pub src: Address,
470 pub dst: Address,
472}
473
474impl TcpMd5DeleteKeyRequest {
475 pub fn new(src: SockAddr, dst: SockAddr) -> Self {
477 Self {
478 header: Header::new(
479 MessageType::Delete,
480 SaType::TcpSig,
481 size_of::<Self>(),
482 ),
483 association: Association::default(),
484 src: Address::src(src, IPPROTO_TCP as u8),
485 dst: Address::dst(dst, IPPROTO_TCP as u8),
486 }
487 }
488}
489
490#[repr(C, packed)]
492pub struct TcpMd5GetKeyRequest {
493 pub header: Header,
495 pub association: Association,
497 pub src: Address,
499 pub dst: Address,
501}
502
503impl TcpMd5GetKeyRequest {
504 pub fn new(src: SockAddr, dst: SockAddr) -> Self {
506 Self {
507 header: Header::new(
508 MessageType::Get,
509 SaType::TcpSig,
510 size_of::<Self>(),
511 ),
512 association: Association::default(),
513 src: Address::src(src, IPPROTO_TCP as u8),
514 dst: Address::dst(dst, IPPROTO_TCP as u8),
515 }
516 }
517}
518
519#[derive(Debug)]
521pub struct GetAssociationResponse {
522 pub header: Header,
523 pub extensions: Vec<Extension>,
524}
525
526pub fn tcp_md5_key_add(
531 src: SockAddr,
532 dst: SockAddr,
533 authstring: &str,
534 valid_time: Duration,
535) -> Result<(), Error> {
536 let msg = TcpMd5AddKeyRequest::new(src, dst, authstring, valid_time);
537 let mut sock = Socket::new(
538 Domain::from(PF_KEY),
539 Type::RAW,
540 Some(Protocol::from(i32::from(PF_KEY_V2))),
541 )?;
542 let data = unsafe {
543 std::slice::from_raw_parts(
544 (&msg as *const TcpMd5AddKeyRequest) as *const u8,
545 size_of::<TcpMd5AddKeyRequest>(),
546 )
547 };
548 let n = sock.write(data)?;
549 if n != data.len() {
550 return Err(std::io::Error::new(
551 std::io::ErrorKind::UnexpectedEof,
552 format!("short write {} != {}", n, data.len()),
553 )
554 .into());
555 }
556
557 let mut buf = [0u8; 1024];
558 let _n = sock.read(&mut buf)?;
559 let response = unsafe { &*(buf.as_ptr() as *const Header) };
560
561 if response.errno != 0 {
562 if response.seq != msg.header.seq {
563 return Err(Error::PfKeySequenceMismatch {
564 expected: msg.header.seq,
565 received: response.seq,
566 });
567 }
568 return Err(Error::PfKey {
569 errno: response.errno,
570 typ: response.typ,
571 sa_typ: response.sa_typ,
572 diagnostic: response.reserved,
573 });
574 }
575
576 Ok(())
577}
578
579pub fn tcp_md5_key_update(
583 src: SockAddr,
584 dst: SockAddr,
585 valid_time: Duration,
586) -> Result<(), Error> {
587 let msg = TcpMd5UpdateKeyRequest::new(src, dst, valid_time);
588 let mut sock = Socket::new(
589 Domain::from(PF_KEY),
590 Type::RAW,
591 Some(Protocol::from(i32::from(PF_KEY_V2))),
592 )?;
593 let data = unsafe {
594 std::slice::from_raw_parts(
595 (&msg as *const TcpMd5UpdateKeyRequest) as *const u8,
596 size_of::<TcpMd5UpdateKeyRequest>(),
597 )
598 };
599 let n = sock.write(data)?;
600 if n != data.len() {
601 return Err(std::io::Error::new(
602 std::io::ErrorKind::UnexpectedEof,
603 format!("short write {} != {}", n, data.len()),
604 )
605 .into());
606 }
607
608 let mut buf = [0u8; 1024];
609 let _n = sock.read(&mut buf)?;
610 let response = unsafe { &*(buf.as_ptr() as *const Header) };
611
612 if response.errno != 0 {
613 if response.seq != msg.header.seq {
614 return Err(Error::PfKeySequenceMismatch {
615 expected: msg.header.seq,
616 received: response.seq,
617 });
618 }
619 return Err(Error::PfKey {
620 errno: response.errno,
621 typ: response.typ,
622 sa_typ: response.sa_typ,
623 diagnostic: response.reserved,
624 });
625 }
626
627 Ok(())
628}
629
630pub fn tcp_md5_key_get(
633 src: SockAddr,
634 dst: SockAddr,
635) -> Result<GetAssociationResponse, Error> {
636 let msg = TcpMd5GetKeyRequest::new(src, dst);
637 let mut sock = Socket::new(
638 Domain::from(PF_KEY),
639 Type::RAW,
640 Some(Protocol::from(i32::from(PF_KEY_V2))),
641 )?;
642 let data = unsafe {
643 std::slice::from_raw_parts(
644 (&msg as *const TcpMd5GetKeyRequest) as *const u8,
645 size_of::<TcpMd5GetKeyRequest>(),
646 )
647 };
648 let n = sock.write(data)?;
649 if n != data.len() {
650 return Err(std::io::Error::new(
651 std::io::ErrorKind::UnexpectedEof,
652 format!("short write {} != {}", n, data.len()),
653 )
654 .into());
655 }
656
657 let mut buf = [0u8; 1024];
658 let _n = sock.read(&mut buf)?;
659 let response = unsafe { &*(buf.as_ptr() as *const Header) };
660
661 if response.errno != 0 {
662 return Err(Error::PfKey {
663 errno: response.errno,
664 typ: response.typ,
665 sa_typ: response.sa_typ,
666 diagnostic: response.reserved,
667 });
668 }
669
670 let cursor = &mut buf.as_slice();
671 parse::association_response
672 .parse_next(cursor)
673 .map_err(|e| Error::PfKeyParse(format!("{e:?}")))
674}
675
676pub fn tcp_md5_key_remove(src: SockAddr, dst: SockAddr) -> Result<(), Error> {
679 let msg = TcpMd5DeleteKeyRequest::new(src, dst);
680 let mut sock = Socket::new(
681 Domain::from(PF_KEY),
682 Type::RAW,
683 Some(Protocol::from(i32::from(PF_KEY_V2))),
684 )?;
685 let data = unsafe {
686 std::slice::from_raw_parts(
687 (&msg as *const TcpMd5DeleteKeyRequest) as *const u8,
688 size_of::<TcpMd5DeleteKeyRequest>(),
689 )
690 };
691 let n = sock.write(data)?;
692 if n != data.len() {
693 return Err(std::io::Error::new(
694 std::io::ErrorKind::UnexpectedEof,
695 format!("short write {} != {}", n, data.len()),
696 )
697 .into());
698 }
699
700 let mut buf = [0u8; 1024];
701 let _n = sock.read(&mut buf)?;
702 let response = unsafe { &*(buf.as_ptr() as *const Header) };
703
704 if response.errno != 0 {
705 if response.seq != msg.header.seq {
706 return Err(Error::PfKeySequenceMismatch {
707 expected: msg.header.seq,
708 received: response.seq,
709 });
710 }
711 return Err(Error::PfKey {
712 errno: response.errno,
713 typ: response.typ,
714 sa_typ: response.sa_typ,
715 diagnostic: response.reserved,
716 });
717 }
718
719 Ok(())
720}
721
722#[derive(thiserror::Error, Debug)]
724pub enum Error {
725 #[error("io error {0}")]
726 Io(#[from] std::io::Error),
727
728 #[error("pfkey {typ:?}/{sa_typ:?} {errno}/{diagnostic}")]
729 PfKey {
730 typ: MessageType,
731 sa_typ: SaType,
732 errno: u8,
733 diagnostic: u16,
734 },
735
736 #[error("pfkey parse {0}")]
737 PfKeyParse(String),
738
739 #[error("pfkey sequence mismatch {expected} {received}")]
740 PfKeySequenceMismatch { expected: u32, received: u32 },
741}
742
743mod parse {
744 use super::*;
745
746 pub fn association_response(
747 buf: &mut &[u8],
748 ) -> ModalResult<GetAssociationResponse> {
749 Ok(GetAssociationResponse {
750 header: header.parse_next(buf)?,
751 extensions: repeat(0.., extension).parse_next(buf)?,
752 })
753 }
754
755 pub fn header(buf: &mut &[u8]) -> ModalResult<Header> {
756 Ok(Header {
757 version: le_u8.parse_next(buf)?,
758 typ: message_type.parse_next(buf)?,
759 errno: le_u8.parse_next(buf)?,
760 sa_typ: sa_type.parse_next(buf)?,
761 len: le_u16.parse_next(buf)?,
762 reserved: le_u16.parse_next(buf)?,
763 seq: le_u32.parse_next(buf)?,
764 pid: le_u32.parse_next(buf)?,
765 })
766 }
767
768 pub fn extension(buf: &mut &[u8]) -> ModalResult<Extension> {
769 let len = le_u16.parse_next(buf)?;
770 let typ = sa_ext_type.parse_next(buf)?;
771 Ok(match typ {
772 SaExtType::Sa => {
773 Extension::Association(association(len).parse_next(buf)?)
774 }
775 SaExtType::LifetimeCurrent
776 | SaExtType::LifetimeHard
777 | SaExtType::LifetimeSoft => {
778 Extension::Lifetime(lifetime(len, typ).parse_next(buf)?)
779 }
780 SaExtType::AddressSrc | SaExtType::AddressDst => {
781 Extension::Address(address(len, typ).parse_next(buf)?)
782 }
783 SaExtType::StrAuth => {
784 Extension::StrAuth(str_auth(len).parse_next(buf)?)
785 }
786 })
787 }
788
789 pub fn association(
790 len: u16,
791 ) -> impl FnMut(&mut &[u8]) -> ModalResult<Association> {
792 move |buf: &mut &[u8]| -> ModalResult<Association> {
793 Ok(Association {
794 len,
795 typ: SaExtType::Sa,
796 spi: le_u32.parse_next(buf)?,
797 replay: le_u8.parse_next(buf)?,
798 state: sa_state.parse_next(buf)?,
799 auth: sa_auth_type.parse_next(buf)?,
800 encrypt: sa_encrypt_type.parse_next(buf)?,
801 flags: le_u32.parse_next(buf)?,
802 })
803 }
804 }
805
806 pub fn lifetime(
807 len: u16,
808 typ: SaExtType,
809 ) -> impl FnMut(&mut &[u8]) -> ModalResult<Lifetime> {
810 move |buf: &mut &[u8]| -> ModalResult<Lifetime> {
811 Ok(Lifetime {
812 len,
813 typ,
814 alloc: le_u32.parse_next(buf)?,
815 bytes: le_u64.parse_next(buf)?,
816 addtime: le_u64.parse_next(buf)?,
817 usetime: le_u64.parse_next(buf)?,
818 })
819 }
820 }
821
822 pub fn address(
823 len: u16,
824 typ: SaExtType,
825 ) -> impl FnMut(&mut &[u8]) -> ModalResult<Address> {
826 move |buf: &mut &[u8]| -> ModalResult<Address> {
827 let sockaddr_len = ((len as usize) << 3)
828 - (size_of::<Address>() - size_of::<SockAddr>());
829 Ok(Address {
830 len,
831 typ,
832 proto: le_u8.parse_next(buf)?,
833 prefix_len: le_u8.parse_next(buf)?,
834 reserved: le_u16.parse_next(buf)?,
835 sockaddr: unsafe {
836 let x = take(sockaddr_len).parse_next(buf)?;
837 let mut buf = [0; size_of::<SockAddr>()];
838 buf[0..sockaddr_len].copy_from_slice(x);
839 (buf.as_ptr() as *const SockAddr).read_unaligned()
840 },
841 })
842 }
843 }
844
845 pub fn str_auth(
846 len: u16,
847 ) -> impl FnMut(&mut &[u8]) -> ModalResult<StrAuth> {
848 let data_len = ((len as usize) << 3)
849 - (size_of::<StrAuth>() - MAX_STR_AUTH_KEY_SIZE);
850 move |buf: &mut &[u8]| -> ModalResult<StrAuth> {
851 Ok(StrAuth {
852 len,
853 typ: SaExtType::StrAuth,
854 bits: le_u16.parse_next(buf)?,
855 reserved: le_u16.parse_next(buf)?,
856 data: {
857 let x = take(data_len).parse_next(buf)?;
858 let mut buf = [0; MAX_STR_AUTH_KEY_SIZE];
859 buf[0..data_len].copy_from_slice(x);
860 buf
861 },
862 })
863 }
864 }
865
866 pub fn message_type(buf: &mut &[u8]) -> ModalResult<MessageType> {
867 let value = le_u8.parse_next(buf)?;
868 MessageType::try_from_primitive(value)
869 .map_err(|_| ErrMode::Backtrack(ContextError::new()))
870 }
871
872 pub fn sa_type(buf: &mut &[u8]) -> ModalResult<SaType> {
873 let value = le_u8.parse_next(buf)?;
874 SaType::try_from_primitive(value)
875 .map_err(|_| ErrMode::Backtrack(ContextError::new()))
876 }
877
878 fn sa_ext_type(buf: &mut &[u8]) -> ModalResult<SaExtType> {
879 let value = le_u16.parse_next(buf)?;
880 SaExtType::try_from_primitive(value)
881 .map_err(|_| ErrMode::Backtrack(ContextError::new()))
882 }
883
884 fn sa_auth_type(buf: &mut &[u8]) -> ModalResult<SaAuthType> {
885 let value = le_u8.parse_next(buf)?;
886 SaAuthType::try_from_primitive(value)
887 .map_err(|_| ErrMode::Backtrack(ContextError::new()))
888 }
889
890 fn sa_encrypt_type(buf: &mut &[u8]) -> ModalResult<SaEncryptType> {
891 let value = le_u8.parse_next(buf)?;
892 SaEncryptType::try_from_primitive(value)
893 .map_err(|_| ErrMode::Backtrack(ContextError::new()))
894 }
895
896 fn sa_state(buf: &mut &[u8]) -> ModalResult<SaState> {
897 let value = le_u8.parse_next(buf)?;
898 SaState::try_from_primitive(value)
899 .map_err(|_| ErrMode::Backtrack(ContextError::new()))
900 }
901}