Skip to main content

corevpn_protocol/
opcode.rs

1//! OpenVPN Protocol Opcodes
2//!
3//! Defines the packet types used in the OpenVPN protocol.
4
5use crate::{ProtocolError, Result};
6
7/// OpenVPN packet opcode (high 5 bits of first byte)
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9#[repr(u8)]
10pub enum OpCode {
11    /// Control channel packet with reliability layer (P_CONTROL_V1)
12    ControlV1 = 4,
13
14    /// Acknowledgment packet (P_ACK_V1)
15    AckV1 = 5,
16
17    /// Data channel packet with current key (P_DATA_V1)
18    DataV1 = 6,
19
20    /// Hard reset from client v2 (P_CONTROL_HARD_RESET_CLIENT_V2)
21    HardResetClientV2 = 7,
22
23    /// Hard reset from server v2 (P_CONTROL_HARD_RESET_SERVER_V2)
24    HardResetServerV2 = 8,
25
26    /// Soft reset v1 (P_CONTROL_SOFT_RESET_V1)
27    SoftResetV1 = 3,
28
29    /// Data channel v2 with peer-id (P_DATA_V2)
30    DataV2 = 9,
31
32    /// Hard reset from client v3 (P_CONTROL_HARD_RESET_CLIENT_V3) - with tls-crypt-v2
33    HardResetClientV3 = 10,
34
35    /// Control channel with tls-crypt (P_CONTROL_WKC_V1)
36    ControlWkcV1 = 11,
37}
38
39impl OpCode {
40    /// Parse opcode from raw byte (high 5 bits)
41    pub fn from_byte(byte: u8) -> Result<Self> {
42        let opcode = byte >> 3;
43        match opcode {
44            3 => Ok(OpCode::SoftResetV1),
45            4 => Ok(OpCode::ControlV1),
46            5 => Ok(OpCode::AckV1),
47            6 => Ok(OpCode::DataV1),
48            7 => Ok(OpCode::HardResetClientV2),
49            8 => Ok(OpCode::HardResetServerV2),
50            9 => Ok(OpCode::DataV2),
51            10 => Ok(OpCode::HardResetClientV3),
52            11 => Ok(OpCode::ControlWkcV1),
53            _ => Err(ProtocolError::UnknownOpcode(opcode)),
54        }
55    }
56
57    /// Convert opcode to byte (shifted to high 5 bits)
58    pub fn to_byte(self, key_id: KeyId) -> u8 {
59        ((self as u8) << 3) | (key_id.0 & 0x07)
60    }
61
62    /// Check if this is a control channel opcode
63    pub fn is_control(&self) -> bool {
64        matches!(
65            self,
66            OpCode::ControlV1
67                | OpCode::AckV1
68                | OpCode::HardResetClientV2
69                | OpCode::HardResetServerV2
70                | OpCode::SoftResetV1
71                | OpCode::HardResetClientV3
72                | OpCode::ControlWkcV1
73        )
74    }
75
76    /// Check if this is a data channel opcode
77    pub fn is_data(&self) -> bool {
78        matches!(self, OpCode::DataV1 | OpCode::DataV2)
79    }
80
81    /// Check if this is a hard reset opcode
82    pub fn is_hard_reset(&self) -> bool {
83        matches!(
84            self,
85            OpCode::HardResetClientV2 | OpCode::HardResetServerV2 | OpCode::HardResetClientV3
86        )
87    }
88}
89
90impl std::fmt::Display for OpCode {
91    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
92        match self {
93            OpCode::ControlV1 => write!(f, "P_CONTROL_V1"),
94            OpCode::AckV1 => write!(f, "P_ACK_V1"),
95            OpCode::DataV1 => write!(f, "P_DATA_V1"),
96            OpCode::HardResetClientV2 => write!(f, "P_CONTROL_HARD_RESET_CLIENT_V2"),
97            OpCode::HardResetServerV2 => write!(f, "P_CONTROL_HARD_RESET_SERVER_V2"),
98            OpCode::SoftResetV1 => write!(f, "P_CONTROL_SOFT_RESET_V1"),
99            OpCode::DataV2 => write!(f, "P_DATA_V2"),
100            OpCode::HardResetClientV3 => write!(f, "P_CONTROL_HARD_RESET_CLIENT_V3"),
101            OpCode::ControlWkcV1 => write!(f, "P_CONTROL_WKC_V1"),
102        }
103    }
104}
105
106/// Key ID (low 3 bits of first byte)
107///
108/// Used to identify which key to use for data channel encryption.
109/// Allows key renegotiation without interrupting traffic.
110#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
111pub struct KeyId(pub u8);
112
113impl KeyId {
114    /// Create a new key ID
115    pub fn new(id: u8) -> Self {
116        Self(id & 0x07)
117    }
118
119    /// Parse key ID from raw byte (low 3 bits)
120    pub fn from_byte(byte: u8) -> Self {
121        Self(byte & 0x07)
122    }
123
124    /// Get the next key ID (wraps around)
125    pub fn next(&self) -> Self {
126        Self((self.0 + 1) & 0x07)
127    }
128}
129
130#[cfg(test)]
131mod tests {
132    use super::*;
133
134    #[test]
135    fn test_opcode_roundtrip() {
136        let key_id = KeyId::new(3);
137
138        for opcode in [
139            OpCode::ControlV1,
140            OpCode::AckV1,
141            OpCode::DataV1,
142            OpCode::HardResetClientV2,
143            OpCode::HardResetServerV2,
144            OpCode::DataV2,
145        ] {
146            let byte = opcode.to_byte(key_id);
147            let parsed = OpCode::from_byte(byte).unwrap();
148            let parsed_key_id = KeyId::from_byte(byte);
149
150            assert_eq!(opcode, parsed);
151            assert_eq!(key_id, parsed_key_id);
152        }
153    }
154
155    #[test]
156    fn test_key_id_wrap() {
157        let key_id = KeyId::new(7);
158        assert_eq!(key_id.next(), KeyId::new(0));
159    }
160}