openvpn_parser/
openvpn.rs

1use nom::bytes::streaming::take;
2use nom::combinator::{cond, map, map_parser, rest};
3use nom::error::{make_error, ErrorKind};
4use nom::multi::count;
5use nom::number::streaming::{be_u16, be_u32, be_u64, be_u8};
6use nom::{Err, IResult};
7use rusticata_macros::newtype_enum;
8
9/// OpenVPN packet
10#[derive(Debug, PartialEq)]
11pub struct OpenVPNPacket<'a> {
12    pub hdr: OpenVPNHdr,
13    pub msg: Payload<'a>,
14}
15
16/// OpenVPN packet header
17///
18/// TCP and UDP differ only by the presence of the `plen` field.
19#[derive(Debug, PartialEq)]
20pub struct OpenVPNHdr {
21    /// Packet length, TCP only
22    pub plen: Option<u16>,
23    pub opcode: Opcode,
24    pub key: u8,
25}
26
27#[derive(Copy, Clone, PartialEq, Eq)]
28pub struct Opcode(pub u8);
29
30newtype_enum! {
31impl debug Opcode {
32    P_CONTROL_HARD_RESET_CLIENT_V1 = 0x1,
33    P_CONTROL_HARD_RESET_SERVER_V1 = 0x2,
34    P_CONTROL_SOFT_RESET_V1 = 0x3,
35    P_CONTROL_V1 = 0x4,
36    P_ACK_V1 = 0x5,
37    P_DATA_V1 = 0x6,
38    P_CONTROL_HARD_RESET_CLIENT_V2 = 0x7,
39    P_CONTROL_HARD_RESET_SERVER_V2 = 0x8,
40    P_DATA_V2 = 0x9,
41}
42}
43
44/// Payload for OpenVPN data
45#[derive(Debug, PartialEq)]
46pub enum Payload<'a> {
47    Control(PControl<'a>),
48    Ack(PAck<'a>),
49    Data(PData<'a>),
50}
51
52/// Payload for P_CONTROL messages
53#[derive(Debug, PartialEq)]
54pub struct PControl<'a> {
55    pub session_id: u64,
56    /// 16 or 20 bytes
57    pub hmac: &'a [u8],
58    /// replay protection, 4 or 8 bytes (see `net_time`)
59    pub packet_id: u32,
60    /// Optional part of replay protection
61    pub net_time: u32,
62    pub msg_ar_len: u8,
63    pub msg_ar: Option<Vec<u32>>,
64    pub msg_packet_id: u32,
65    pub remote_session_id: Option<u64>,
66    pub payload: &'a [u8],
67}
68
69/// Payload for P_ACK messages
70#[derive(Debug, PartialEq)]
71pub struct PAck<'a> {
72    pub session_id: u64,
73    /// 16 or 20 bytes
74    pub hmac: &'a [u8],
75    /// replay protection, 4 or 8 bytes (see `net_time`)
76    pub packet_id: u32,
77    /// Optional part of replay protection
78    pub net_time: u32,
79    pub msg_ar_len: u8,
80    pub msg_ar: Option<Vec<u32>>,
81    pub remote_session_id: Option<u64>,
82}
83
84/// Payload for P_DATA messages
85///
86/// Since the payload can be encrypted, do not parse data
87#[derive(Debug, PartialEq)]
88pub struct PData<'a> {
89    pub contents: &'a [u8],
90}
91
92/// Parse an OpenVPN packet in TCP
93pub fn parse_openvpn_tcp(i: &[u8]) -> IResult<&[u8], OpenVPNPacket> {
94    let (i, hdr) = parse_openvpn_header_tcp(i)?;
95    // length includes header (minus plen field)
96    // substract 1 (opcode + key)
97    let plen = match hdr.plen {
98        Some(plen) if plen >= 2 => plen,
99        _ => return Err(Err::Error(make_error(i, ErrorKind::LengthValue))),
100    };
101    let (i, msg) = map_parser(take(plen - 1), parse_openvpn_msg_payload(hdr.opcode))(i)?;
102    Ok((i, OpenVPNPacket { hdr, msg }))
103}
104
105/// Parse an OpenVPN packet in UDP
106///
107/// Note that this will consume the entire buffer
108pub fn parse_openvpn_udp(i: &[u8]) -> IResult<&[u8], OpenVPNPacket> {
109    let (i, hdr) = parse_openvpn_header_udp(i)?;
110    let (i, msg) = parse_openvpn_msg_payload(hdr.opcode)(i)?;
111    Ok((i, OpenVPNPacket { hdr, msg }))
112}
113
114pub fn parse_openvpn_header_tcp(i: &[u8]) -> IResult<&[u8], OpenVPNHdr> {
115    let (i, plen) = be_u16(i)?;
116    let (i, opcode_and_key) = be_u8(i)?;
117    // take 5 bits for opcode and 3 for key
118    let opcode = Opcode(opcode_and_key >> 3);
119    let key = opcode_and_key & 0b111;
120    let hdr = OpenVPNHdr {
121        plen: Some(plen),
122        opcode,
123        key,
124    };
125    Ok((i, hdr))
126}
127
128pub fn parse_openvpn_header_udp(i: &[u8]) -> IResult<&[u8], OpenVPNHdr> {
129    let (i, opcode_and_key) = be_u8(i)?;
130    // take 5 bits for opcode and 3 for key
131    let opcode = Opcode(opcode_and_key >> 3);
132    let key = opcode_and_key & 0b111;
133    let hdr = OpenVPNHdr {
134        plen: None,
135        opcode,
136        key,
137    };
138    Ok((i, hdr))
139}
140
141pub fn parse_openvpn_msg_payload(msg_type: Opcode) -> impl FnMut(&[u8]) -> IResult<&[u8], Payload> {
142    move |i| match msg_type {
143        Opcode::P_CONTROL_HARD_RESET_CLIENT_V1
144        | Opcode::P_CONTROL_HARD_RESET_SERVER_V1
145        | Opcode::P_CONTROL_SOFT_RESET_V1
146        | Opcode::P_CONTROL_V1
147        | Opcode::P_CONTROL_HARD_RESET_CLIENT_V2
148        | Opcode::P_CONTROL_HARD_RESET_SERVER_V2 => {
149            map(parse_openvpn_msg_pcontrol, Payload::Control)(i)
150        }
151        Opcode::P_ACK_V1 => map(parse_openvpn_msg_pack, Payload::Ack)(i),
152        Opcode::P_DATA_V1 | Opcode::P_DATA_V2 => {
153            map(rest, |x| Payload::Data(PData { contents: x }))(i)
154        }
155        _ => Err(::nom::Err::Error(make_error(i, ErrorKind::Tag))),
156    }
157}
158
159pub fn parse_openvpn_msg_pcontrol(i: &[u8]) -> IResult<&[u8], PControl> {
160    let (i, session_id) = be_u64(i)?;
161    let (i, hmac) = take(20usize)(i)?;
162    let (i, packet_id) = be_u32(i)?;
163    let (i, net_time) = be_u32(i)?;
164    let (i, msg_ar_len) = be_u8(i)?;
165    let (i, msg_ar) = cond(msg_ar_len > 0, count(be_u32, msg_ar_len as usize))(i)?;
166    let (i, remote_session_id) = cond(msg_ar_len > 0, be_u64)(i)?;
167    let (i, msg_packet_id) = be_u32(i)?;
168    let (i, payload) = rest(i)?;
169    let pcontrol = PControl {
170        session_id,
171        hmac,
172        packet_id,
173        net_time,
174        msg_ar_len,
175        msg_ar,
176        remote_session_id,
177        msg_packet_id,
178        payload,
179    };
180    Ok((i, pcontrol))
181}
182
183pub fn parse_openvpn_msg_pack(i: &[u8]) -> IResult<&[u8], PAck> {
184    let (i, session_id) = be_u64(i)?;
185    let (i, hmac) = take(20usize)(i)?;
186    let (i, packet_id) = be_u32(i)?;
187    let (i, net_time) = be_u32(i)?;
188    let (i, msg_ar_len) = be_u8(i)?;
189    let (i, msg_ar) = cond(msg_ar_len > 0, count(be_u32, msg_ar_len as usize))(i)?;
190    let (i, remote_session_id) = cond(msg_ar_len > 0, be_u64)(i)?;
191    let pack = PAck {
192        session_id,
193        hmac,
194        packet_id,
195        net_time,
196        msg_ar_len,
197        msg_ar,
198        remote_session_id,
199    };
200    Ok((i, pack))
201}