OpenVPN 3 Core Library
Loading...
Searching...
No Matches
oob_probe.hpp
Go to the documentation of this file.
1// OpenVPN -- An application to securely tunnel IP networks
2// over a single port, with support for SSL/TLS-based
3// session authentication and key exchange,
4// packet encryption, packet authentication, and
5// packet compression.
6//
7// Copyright (C) 2026- OpenVPN Inc.
8//
9// SPDX-License-Identifier: MPL-2.0 OR AGPL-3.0-only WITH openvpn3-openssl-exception
10//
11
31#ifndef OPENVPN_SSL_OOB_PROBE_H
32#define OPENVPN_SSL_OOB_PROBE_H
33
34#include <cstdint>
35#include <cstring>
36#include <optional>
37
40#include <openvpn/ssl/psid.hpp>
41
42namespace openvpn::oob {
43
44// big-endian integer read/write on a Buffer (shared helpers)
51
53enum : std::uint16_t
54{
57};
58
60enum : std::uint16_t
61{
63 TLV_TYPE_MASK = 0x7fff,
66};
67
75enum : std::uint32_t
76{
78};
79
80// ---- message + TLV headers ----------------------------------------------------
81
83inline bool msg_write_header(Buffer &buf, std::uint16_t msg_type)
84{
85 return write_u16(buf, msg_type);
86}
87
94inline bool msg_read_header(Buffer &buf, std::uint16_t expected_msg_type)
95{
96 std::uint16_t msg_type;
97 return read_u16(buf, msg_type) && msg_type == expected_msg_type;
98}
99
102{
103 std::uint16_t type;
104 bool optional;
105 std::uint16_t value_len;
106};
107
109inline bool tlv_write_header(Buffer &buf, std::uint16_t type, bool optional, std::uint16_t value_len)
110{
111 std::uint16_t field = type & TLV_TYPE_MASK;
112 if (optional)
113 field |= TLV_OPTIONAL_FLAG;
114 return write_u16(buf, field) && write_u16(buf, value_len);
115}
116
121inline std::optional<TlvHeader> tlv_read_header(Buffer &buf)
122{
123 std::uint16_t field, value_len;
124 if (!read_u16(buf, field) || !read_u16(buf, value_len))
125 return std::nullopt;
126 return TlvHeader{.type = static_cast<std::uint16_t>(field & TLV_TYPE_MASK),
127 .optional = (field & TLV_OPTIONAL_FLAG) != 0,
128 .value_len = value_len};
129}
130
137inline bool skip_trailing(Buffer &buf, std::uint16_t value_len, std::size_t consumed)
138{
139 if (value_len < consumed)
140 return false;
141 const std::size_t rest = value_len - consumed;
142 if (buf.size() < rest)
143 return false;
144 buf.advance(rest);
145 return true;
146}
147
157inline std::optional<std::uint16_t> find_tlv(Buffer &buf, std::uint16_t wanted_type)
158{
159 while (buf.size() >= 4) // a TLV header is 4 bytes
160 {
161 const auto hdr = tlv_read_header(buf);
162 if (!hdr)
163 return std::nullopt;
164 if (hdr->type == wanted_type)
165 return hdr->value_len;
166 // Only an optional TLV may be ignored when we do not understand it. A
167 // mandatory one carries something the sender requires us to act on, so
168 // the message as a whole is not ours to interpret.
169 if (!hdr->optional)
170 return std::nullopt;
171 if (buf.size() < hdr->value_len)
172 return std::nullopt;
173 buf.advance(hdr->value_len); // optional and unknown: skip its value
174 }
175 return std::nullopt;
176}
177
178// ---- probe_parameter / probe_reply TLVs -----------------------------------------
179
182{
185 static constexpr std::size_t WIRE_LEN = 12;
186
187 std::uint64_t timestamp = 0;
188 std::uint32_t flags = 0;
189
191 bool write(Buffer &buf) const
192 {
194 && write_u64(buf, timestamp)
195 && write_u32(buf, flags);
196 }
197
203 static std::optional<ProbeParameter> read(Buffer &buf, std::uint16_t value_len)
204 {
205 if (value_len < WIRE_LEN)
206 return std::nullopt;
208 if (!read_u64(buf, p.timestamp) || !read_u32(buf, p.flags))
209 return std::nullopt;
210 if (!skip_trailing(buf, value_len, WIRE_LEN))
211 return std::nullopt;
212 return p;
213 }
214};
215
218{
221 static constexpr std::size_t WIRE_LEN = 20;
222
224 std::uint16_t priority = 0;
225 std::uint16_t weight = 0;
226 std::uint16_t max_latency_diff = 0;
227 std::uint16_t connect_lifetime = 0;
228 std::uint32_t flags = 0;
229
231 bool write(Buffer &buf) const
232 {
233 if (!tlv_write_header(buf, TLV_PROBE_REPLY, false, WIRE_LEN))
234 return false;
236 return false;
238 return write_u16(buf, priority)
239 && write_u16(buf, weight)
240 && write_u16(buf, max_latency_diff)
241 && write_u16(buf, connect_lifetime)
242 && write_u32(buf, flags);
243 }
244
246 static std::optional<ProbeReply> read(Buffer &buf, std::uint16_t value_len)
247 {
248 if (value_len < WIRE_LEN)
249 return std::nullopt;
250 if (buf.size() < ProtoSessionID::SIZE)
251 return std::nullopt;
252 ProbeReply r;
253 r.peer_session_id.read(buf);
254 if (!read_u16(buf, r.priority) || !read_u16(buf, r.weight) || !read_u16(buf, r.max_latency_diff)
255 || !read_u16(buf, r.connect_lifetime) || !read_u32(buf, r.flags))
256 return std::nullopt;
257 if (!skip_trailing(buf, value_len, WIRE_LEN))
258 return std::nullopt;
259 return r;
260 }
261};
262
263// ---- full SERVER_PROBE / PROBE_REPLY messages ---------------------------------
264
266inline bool server_probe_write(Buffer &buf, const ProbeParameter &param)
267{
268 return msg_write_header(buf, MSG_SERVER_PROBE) && param.write(buf);
269}
270
277inline std::optional<ProbeParameter> server_probe_read(Buffer &buf)
278{
280 return std::nullopt;
281 const auto value_len = find_tlv(buf, TLV_PROBE_PARAMETER);
282 if (!value_len)
283 return std::nullopt;
284 return ProbeParameter::read(buf, *value_len);
285}
286
288inline bool client_reply_write(Buffer &buf, const ProbeReply &reply)
289{
290 return msg_write_header(buf, MSG_PROBE_REPLY) && reply.write(buf);
291}
292
298inline std::optional<ProbeReply> client_reply_read(Buffer &buf)
299{
301 return std::nullopt;
302 const auto value_len = find_tlv(buf, TLV_PROBE_REPLY);
303 if (!value_len)
304 return std::nullopt;
305 return ProbeReply::read(buf, *value_len);
306}
307
308} // namespace openvpn::oob
309
310#endif
size_t size() const
Returns the size of the buffer in T objects.
Definition buffer.hpp:1241
void advance(const size_t delta)
Advances the buffer by the specified delta.
Definition buffer.hpp:1276
size_t remaining(const size_t tailroom=0) const
Return the number of additional T objects that can be added before capacity is reached (without consi...
Definition buffer.hpp:1466
void write(Buffer &buf) const
Definition psid.hpp:65
void read(BufType &buf)
Definition psid.hpp:59
designed to represent an optional reference to an object of type T
bool read_u32(Buffer &buf, std::uint32_t &out)
Read a big-endian uint32 from buf into out; false if fewer than 4 bytes.
Definition bufbe.hpp:64
bool read_u16(Buffer &buf, std::uint16_t &out)
Read a big-endian uint16 from buf into out; false if fewer than 2 bytes.
Definition bufbe.hpp:53
bool write_u16(Buffer &buf, std::uint16_t v)
Append a big-endian uint16 to buf; false if there is no room.
Definition bufbe.hpp:26
bool write_u64(Buffer &buf, std::uint64_t v)
Append a big-endian uint64 to buf; false if there is no room.
Definition bufbe.hpp:46
bool read_u64(Buffer &buf, std::uint64_t &out)
Read a big-endian uint64 from buf into out; false if fewer than 8 bytes.
Definition bufbe.hpp:75
bool write_u32(Buffer &buf, std::uint32_t v)
Append a big-endian uint32 to buf; false if there is no room.
Definition bufbe.hpp:36
@ REPLY_FLAG_RESEND_WKC
Definition oob_probe.hpp:77
bool server_probe_write(Buffer &buf, const ProbeParameter &param)
Write a complete SERVER_PROBE (message header + probe_parameter TLV). Client.
bool msg_read_header(Buffer &buf, std::uint16_t expected_msg_type)
Definition oob_probe.hpp:94
bool client_reply_write(Buffer &buf, const ProbeReply &reply)
Write a complete PROBE_REPLY (message header + probe_reply TLV). Server.
std::optional< ProbeReply > client_reply_read(Buffer &buf)
std::optional< TlvHeader > tlv_read_header(Buffer &buf)
bool skip_trailing(Buffer &buf, std::uint16_t value_len, std::size_t consumed)
std::optional< std::uint16_t > find_tlv(Buffer &buf, std::uint16_t wanted_type)
std::optional< ProbeParameter > server_probe_read(Buffer &buf)
bool msg_write_header(Buffer &buf, std::uint16_t msg_type)
Write an OOB message-type header (the 16-bit type preceding the TLVs).
Definition oob_probe.hpp:83
bool tlv_write_header(Buffer &buf, std::uint16_t type, bool optional, std::uint16_t value_len)
Write a TLV header (type + optional flag + value length) to buf.
probe parameter TLV (sent by the client in a SERVER_PROBE).
static constexpr std::size_t WIRE_LEN
static std::optional< ProbeParameter > read(Buffer &buf, std::uint16_t value_len)
bool write(Buffer &buf) const
Append this parameter as a complete TLV (header + value) to buf.
std::uint32_t flags
client capability flags, currently must be 0
std::uint64_t timestamp
client clock as a UNIX timestamp
probe reply TLV (sent by the server in a PROBE_REPLY).
ProtoSessionID peer_session_id
echoes the session id of the request
std::uint16_t weight
DNS-SRV style weight.
std::uint16_t connect_lifetime
seconds the reply is valid as a handshake shortcut
std::uint32_t flags
server behaviour flags (REPLY_FLAG_*)
std::uint16_t priority
DNS-SRV style priority (lower preferred)
std::uint16_t max_latency_diff
candidate-band margin (ms); 0 = client default
static std::optional< ProbeReply > read(Buffer &buf, std::uint16_t value_len)
Read a probe_reply TLV value of value_len bytes; see ProbeParameter::read().
bool write(Buffer &buf) const
Append this reply as a complete TLV (header + value) to buf.
static constexpr std::size_t WIRE_LEN
A decoded TLV header.
std::uint16_t value_len
the declared value length
bool optional
the optional flag
std::uint16_t type
the 15-bit TLV type