libnet/
pf_key.rs

1// Copyright 2024 Oxide Computer Company
2
3// This file implements the PF_KEY protocol as described in RFC 2367.
4
5use 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
17/// The PF_KEY protocol family.
18const PF_KEY: i32 = 27;
19/// The PF_KEY protocol version.
20const PF_KEY_V2: u8 = 2;
21/// Maximum size of a StrAuth key.
22const MAX_STR_AUTH_KEY_SIZE: usize = 80;
23
24/// PF_KEY message types.
25#[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/// PF_KEY security association types.
47#[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/// PF_KEY security association extension types.
61#[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    //TODO AddressProxy = 7,
71    //TODO KeyAuth = 8,
72    //TODO KeyEncrypt = 9,
73    //TODO IdentitySrc = 10,
74    //TODO IdentityDst = 11,
75    //TODO Sensitivity = 12,
76    //TODO Proposal = 13,
77    //TODO SupportedAuth = 14,
78    //TODO SupportedEncrypt = 15,
79    //TODO SpiRange = 16,
80    //TODO Ereg = 17,
81    //TODO Eprop = 18,
82    //TODO KmCookie = 19,
83    //TODO AddressNattLoc = 20,
84    //TODO AddressNattRem = 21,
85    //TODO AddressInnerDst = 22,
86    //TODO Pair = 23,
87    //TODO ReplayValue = 24,
88    //TODO Edump = 25,
89    //TODO LifetimeIdle = 26,
90    //TODO OuterSens = 27,
91    StrAuth = 28,
92}
93
94/// PF_KEY security association authentication types.
95#[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/// PF_KEY security association encryption types.
108#[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/// PF_KEY security association states.
126#[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/// A PF_KEY security association header.
136#[derive(Debug)]
137#[repr(C, packed)]
138pub struct Header {
139    /// Protocol version. Always PF_KEY_2.
140    pub version: u8,
141    /// The message type.
142    pub typ: MessageType,
143    /// Error returned by OS, if any.
144    pub errno: u8,
145    /// Security association type.
146    pub sa_typ: SaType,
147    /// Length of the message in 8-byte units.
148    pub len: u16,
149    /// Reserved when going to the kernel, diagnostic code when coming from the
150    /// kernel.
151    pub reserved: u16,
152    /// Sequence id for this message.
153    pub seq: u32,
154    /// Process id of the sender.
155    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/// The extension enumeration contains all PF_KEY extensions supported by this
174/// module.
175#[derive(Debug)]
176pub enum Extension {
177    Association(Association),
178    Lifetime(Lifetime),
179    Address(Address),
180    StrAuth(StrAuth),
181}
182
183/// Basic information about a security association.
184#[derive(Clone, Copy, Debug)]
185#[repr(C, packed)]
186pub struct Association {
187    /// Length of this extension in 8-byte units.
188    pub len: u16,
189    /// The type of this extension.
190    pub typ: SaExtType,
191    /// Security parameters index.
192    pub spi: u32,
193    /// Replay window size.
194    pub replay: u8,
195    /// State of the association.
196    pub state: SaState,
197    /// Authentication type.
198    pub auth: SaAuthType,
199    /// Encryption type.
200    pub encrypt: SaEncryptType,
201    /// Optional flags.
202    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, // This is not for IPsec
211            replay: 0,
212            state: SaState::Mature,
213            auth: SaAuthType::Md5,
214            encrypt: SaEncryptType::None,
215            flags: 0,
216        }
217    }
218}
219
220/// Lifetime information for a security association.
221#[derive(Debug)]
222#[repr(C, packed)]
223pub struct Lifetime {
224    /// Length of this extension in 8-byte units.
225    pub len: u16,
226    /// The type of this extension.
227    pub typ: SaExtType,
228    /// How many allocations this lifetime lasts for.
229    pub alloc: u32,
230    /// How many bytes this lifetime lasts for.
231    pub bytes: u64,
232    /// How long after creation this lifetime expires in seconds.
233    pub addtime: u64,
234    /// How long after first use this lifetime expires in seconds.
235    pub usetime: u64,
236}
237
238impl Lifetime {
239    /// Create a hard lifetime extension.
240    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, // no allocation limit
245            bytes: 0, // no byte limit
246            addtime: addtime.as_secs(),
247            usetime: 0,
248        }
249    }
250    /// Create a soft lifetime extension.
251    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, // no allocation limit
256            bytes: 0, // no byte limit
257            addtime: addtime.as_secs(),
258            usetime: 0,
259        }
260    }
261}
262
263/// Address information for a security association.
264#[repr(C, packed)]
265pub struct Address {
266    /// Length of this extension in 8-byte units.
267    pub len: u16,
268    /// The type of this extension.
269    pub typ: SaExtType,
270    /// Protocol family identifier for this address.
271    pub proto: u8,
272    /// Prefix length associated with the address.
273    pub prefix_len: u8,
274    /// Reserved bits.
275    pub reserved: u16,
276    /// Address and port the security association binds to.
277    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    /// Create a new source address extension.
303    pub fn src(sockaddr: SockAddr, proto: u8) -> Self {
304        Self::new(sockaddr, proto, SaExtType::AddressSrc)
305    }
306
307    /// Create a new destination address extension.
308    pub fn dst(sockaddr: SockAddr, proto: u8) -> Self {
309        Self::new(sockaddr, proto, SaExtType::AddressDst)
310    }
311
312    /// Create a new address extension.
313    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    /// Get the socket address.
325    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/// String authentication information for this security association.
333#[repr(C, packed)]
334pub struct StrAuth {
335    /// Length of this extension in 8-byte units.
336    pub len: u16,
337    /// The type of this extension.
338    pub typ: SaExtType,
339    /// Length of the key in bits.
340    pub bits: u16,
341    /// Reserved.
342    pub reserved: u16,
343    /// Key data.
344    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    /// Create a new string authentication extension for a given key.
367    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    /// Return the key in string form.
380    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/// A packet to add a TCP-MD5 security association.
390#[repr(C, packed)]
391pub struct TcpMd5AddKeyRequest {
392    /// Packet header.
393    pub header: Header,
394    /// Association info.
395    pub association: Association,
396    /// Lifetime info.
397    pub lifetime: Lifetime,
398    /// Source socket address to bind to.
399    pub src: Address,
400    /// Destination socket address to bind to.
401    pub dst: Address,
402    /// String-based key.
403    pub key: StrAuth,
404}
405
406impl TcpMd5AddKeyRequest {
407    /// Create a new TCP-MD5 add key request.
408    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/// A packet to update a TCP-MD5 security association.
430#[repr(C, packed)]
431pub struct TcpMd5UpdateKeyRequest {
432    /// Packet header.
433    pub header: Header,
434    /// Association info.
435    pub association: Association,
436    /// Lifetime info.
437    pub lifetime: Lifetime,
438    /// Source socket address to bind to.
439    pub src: Address,
440    /// Destination socket address to bind to.
441    pub dst: Address,
442}
443
444impl TcpMd5UpdateKeyRequest {
445    /// Create a new TCP-MD5 update key request.
446    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/// A packet to delete a TCP-MD5 security association.
462#[repr(C, packed)]
463pub struct TcpMd5DeleteKeyRequest {
464    /// Packet header.
465    pub header: Header,
466    /// Association info.
467    pub association: Association,
468    /// Source socket address to unbind.
469    pub src: Address,
470    /// Destination socket address to unbind.
471    pub dst: Address,
472}
473
474impl TcpMd5DeleteKeyRequest {
475    /// Create a new TCP-MD5 delete key request.
476    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/// A packet to request info about a TCP-MD5 security association.
491#[repr(C, packed)]
492pub struct TcpMd5GetKeyRequest {
493    /// Packet header.
494    pub header: Header,
495    /// Association info.
496    pub association: Association,
497    /// Source socket address predicate.
498    pub src: Address,
499    /// Destination socket address predicate.
500    pub dst: Address,
501}
502
503impl TcpMd5GetKeyRequest {
504    /// Create a new TCP-MD5 get key request.
505    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/// Response information returned from kernel from a key association request.
520#[derive(Debug)]
521pub struct GetAssociationResponse {
522    pub header: Header,
523    pub extensions: Vec<Extension>,
524}
525
526/// Add a TCP-MD5 security association for the provided source and destination
527/// address with `authstring` as the key that is valid for `valid_time` after
528/// creation. If update is true, this is treated as an update to an existing
529/// association, otherwise a new association is created.
530pub 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
579/// Update a TCP-MD5 security association for the provided source and
580/// destination. This function is primarily for updating the lifetime
581/// of the security association.
582pub 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
630/// Get info on a TCP-MD5 security association for the provided source and
631/// destination address with
632pub 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
676/// Delete the TCP-MD5 security association for the provided source and
677/// destination.
678pub 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/// Errors that can be returned from PF_KEY operations.
723#[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}