[Openvpn-devel] [L] Change in openvpn[master]: oob: Add SERVER_PROBE parsing and the probe-reply decision

"stipa \(Code Review\) via Openvpn-devel" <[email protected]> Wed, 29 Jul 2026 12:22:54 +0000
Newsgroups net.sourceforge.lists.openvpn-devel
Message-ID <3afb905ddca2801d9026d528e5988459a233daea-EmailReplacePatchSet-HTML@gerrit.openvpn.net>
--===============4548603142499845786==
Content-Transfer-Encoding: 8bit
Content-Disposition: inline
Content-Type: multipart/alternative; boundary="vyKnPnI6aSw="; charset=UTF-8

--vyKnPnI6aSw=
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: quoted-printable

Attention is currently required from: flichtenheld, plaisthos=2E

Hello fli=
chtenheld, plaisthos, 

I'd like you to reexamine a change=2E Please visit
=

    http://gerrit=2Eopenvpn=2Enet/c/openvpn/+/1742?usp=3Demail

to look at=
 the new patch set (#10)=2E


Change subject: oob: Add SERVER_PROBE parsing=
 and the probe-reply decision
=2E=2E=2E=2E=2E=2E=2E=2E=2E=2E=2E=2E=2E=2E=2E=
=2E=2E=2E=2E=2E=2E=2E=2E=2E=2E=2E=2E=2E=2E=2E=2E=2E=2E=2E=2E=2E=2E=2E=2E=2E=
=2E=2E=2E=2E=2E=2E=2E=2E=2E=2E=2E=2E=2E=2E=2E=2E=2E=2E=2E=2E=2E=2E=2E=2E=2E=
=2E=2E=2E=2E=2E

oob: Add SERVER_PROBE parsing and the probe-reply decision=


Add the message-level helpers the server needs to answer a probe:

  - oo=
b_server_probe_write() writes a SERVER_PROBE (message-type header +
    pro=
be_parameter TLV); the client uses it later=2E
  - oob_server_probe_read() =
scans the TLV payload of a received OOB
    SERVER_PROBE for the probe_para=
meter TLV, skipping other/future TLV
    types and rejecting malformed or t=
runcated input=2E
  - oob_timestamp_in_window() performs the cheap replay/s=
kew check on the
    probe timestamp described in the wire protocol specifi=
cation=2E
  - oob_build_probe_reply() composes the two: given a received SE=
RVER_PROBE
    it scans for the probe_parameter, drops the probe if it is m=
issing or
    its timestamp is outside the acceptable window, and otherwise=
 populates
    the probe_reply (echoing the peer's session id) to send back=
=2E

A probe whose timestamp falls outside the window is dropped without a =
reply,
which is what the spec allows: the timestamp is there so a server ma=
y silently
drop requests outside an acceptable window and reduce the replay=
 attack surface=2E
The window itself is the caller's choice; the server pat=
h passes --hand-window in
a follow-up=2E

These are pure functions exercise=
d by unit tests; the server packet path
calls oob_build_probe_reply() in a =
follow-up, which performs the actual send=2E

Change-Id: Iafa9efc1c156974ad=
9f5752e9de810a8c2f68c30
Signed-off-by: Lev Stipakov <lev@openvpn=2Enet>
---=

M src/openvpn/oob=2Ec
M src/openvpn/oob=2Eh
M tests/unit_tests/openvpn/tes=
t_oob=2Ec
3 files changed, 289 insertions(+), 0 deletions(-)


  git pull s=
sh://gerrit=2Eopenvpn=2Enet:29418/openvpn refs/changes/42/1742/10

diff --g=
it a/src/openvpn/oob=2Ec b/src/openvpn/oob=2Ec
index c807171=2E=2E930b213 1=
00644
--- a/src/openvpn/oob=2Ec
+++ b/src/openvpn/oob=2Ec
@@ -81,3 +81,54 @=
@
     r->flags =3D buf_read_u32(buf, NULL);
     return true;
 }
+
+bool
+=
oob_server_probe_write(struct buffer *buf, const struct oob_probe_parameter=
 *param)
+{
+    return buf_write_u16(buf, OOB_MSG_SERVER_PROBE) && oob_pro=
be_parameter_write(buf, param);
+}
+
+bool
+oob_server_probe_read(struct bu=
ffer *payload, struct oob_probe_parameter *param)
+{
+    if (!ctrl_msg_rea=
d_header(payload, OOB_MSG_SERVER_PROBE))
+    {
+        return false;
+   =
 }
+
+    struct buffer value;
+    if (!ctrl_msg_find_tlv(payload, OOB_TLV=
_PROBE_PARAMETER, &value))
+    {
+        return false;
+    }
+
+    retu=
rn oob_probe_parameter_read(&value, param);
+}
+
+bool
+oob_timestamp_in_wi=
ndow(uint64_t probe_ts, uint64_t now, uint64_t window_secs)
+{
+    uint64_=
t diff =3D (now > probe_ts) ? (now - probe_ts) : (probe_ts - now);
+    ret=
urn diff <=3D window_secs;
+}
+
+bool
+oob_build_probe_reply(struct buffer =
*probe_payload, uint64_t now, uint64_t window_secs,
+                      =
const struct session_id *peer_sid, struct oob_probe_reply *reply)
+{
+    s=
truct oob_probe_parameter param;
+    if (!oob_server_probe_read(probe_payl=
oad, &param))
+    {
+        return false;
+    }
+
+    /* Drop replayed =
or implausibly-timed probes before doing any more work=2E */
+    if (!oob_=
timestamp_in_window(param=2Etimestamp, now, window_secs))
+    {
+        r=
eturn false;
+    }
+
+    /* the caller's advertised values are kept; only=
 the echo is ours to fill */
+    reply->peer_session_id =3D *peer_sid;
+  =
  return true;
+}
diff --git a/src/openvpn/oob=2Eh b/src/openvpn/oob=2Eh
in=
dex 0a9a4bb=2E=2E1afabbd 100644
--- a/src/openvpn/oob=2Eh
+++ b/src/openvpn=
/oob=2Eh
@@ -107,4 +107,58 @@
  */
 bool oob_probe_reply_read(struct buffer=
 *buf, struct oob_probe_reply *r);
 
+/**
+ * Write a complete SERVER_PROBE=
 message (message-type header + probe_parameter
+ * TLV) to buf=2E Sent by =
the client=2E
+ */
+bool oob_server_probe_write(struct buffer *buf, const s=
truct oob_probe_parameter *param);
+
+/**
+ * Read a received OOB SERVER_PR=
OBE: verify its message-type header, then scan
+ * for the probe_parameter =
TLV=2E TLV types other than probe_parameter are
+ * skipped=2E payload is c=
onsumed as it is read=2E
+ *
+ * @param payload  buffer positioned at the s=
tart of the OOB message payload
+ * @param param    filled with the parsed =
probe_parameter on success
+ * @return true if the header matched and a wel=
l-formed probe_parameter was
+ *         found, false otherwise
+ */
+bool =
oob_server_probe_read(struct buffer *payload, struct oob_probe_parameter *p=
aram);
+
+/**
+ * Check whether a probe timestamp is within an acceptable w=
indow around the
+ * current time=2E Used to cheaply drop replayed or impla=
usibly-timed probes
+ * before doing any further work (see the probe_parame=
ter timestamp rationale
+ * in the wire protocol specification)=2E
+ *
+ * =
@param probe_ts     timestamp from the probe_parameter (UNIX seconds)
+ * @=
param now          current time (UNIX seconds)
+ * @param window_secs  maxi=
mum allowed difference, in either direction
+ * @return true if |now - prob=
e_ts| <=3D window_secs
+ */
+bool oob_timestamp_in_window(uint64_t probe_ts=
, uint64_t now, uint64_t window_secs);
+
+/**
+ * Process the TLV payload o=
f a received SERVER_PROBE and decide whether to
+ * answer it=2E Combines o=
ob_server_probe_read() and oob_timestamp_in_window():
+ * the probe is drop=
ped (false returned) if it has no valid probe_parameter or
+ * its timestam=
p is outside the acceptable window=2E
+ *
+ * reply carries the values the =
server advertises (priority, weight,
+ * max_latency_diff, connect_lifetime=
, flags) in, and is deliberately not
+ * cleared: on success only the peer'=
s session id is filled in, leaving the
+ * reply ready to be wrapped and se=
nt=2E
+ *
+ * This is the transport-agnostic decision step; the caller perf=
orms the send=2E
+ *
+ * @param probe_payload  TLV payload of the received =
OOB SERVER_PROBE
+ * @param now            current time (UNIX seconds)
+ * =
@param window_secs    acceptable timestamp skew, in either direction
+ * @p=
aram peer_sid       session id of the requesting peer (echoed in the reply)=

+ * @param reply          in: the values to advertise; out: the reply to s=
end
+ * @return true if a reply should be sent, false to silently drop the =
probe
+ */
+bool oob_build_probe_reply(struct buffer *probe_payload, uint64=
_t now, uint64_t window_secs,
+                           const struct sess=
ion_id *peer_sid, struct oob_probe_reply *reply);
+
 #endif /* OOB_H */
dif=
f --git a/tests/unit_tests/openvpn/test_oob=2Ec b/tests/unit_tests/openvpn/=
test_oob=2Ec
index aa4eb23=2E=2E371cbc6 100644
--- a/tests/unit_tests/openv=
pn/test_oob=2Ec
+++ b/tests/unit_tests/openvpn/test_oob=2Ec
@@ -259,6 +259,=
181 @@
     gc_free(&gc);
 }
 
+/* A SERVER_PROBE carrying just a probe_par=
ameter is found by the scan=2E */
+static void
+test_server_probe_read_find=
s_parameter(void **state)
+{
+    struct gc_arena gc =3D gc_new();
+    str=
uct buffer buf =3D alloc_buf_gc(128, &gc);
+
+    const struct oob_probe_pa=
rameter in =3D {
+        =2Etimestamp =3D 0x1122334455667788ULL,
+        =
=2Eflags =3D 0,
+    };
+    assert_true(oob_server_probe_write(&buf, &in))=
;
+
+    struct oob_probe_parameter out =3D { 0 };
+    assert_true(oob_ser=
ver_probe_read(&buf, &out));
+    assert_true(in=2Etimestamp =3D=3D out=2Et=
imestamp);
+    assert_int_equal(in=2Eflags, out=2Eflags);
+
+    gc_free(&=
gc);
+}
+
+/* TLVs other than probe_parameter are skipped, so the scan find=
s the
+ * probe_parameter even when preceded by an unknown TLV=2E */
+stati=
c void
+test_server_probe_read_skips_unknown(void **state)
+{
+    struct g=
c_arena gc =3D gc_new();
+    struct buffer buf =3D alloc_buf_gc(128, &gc);=

+
+    /* SERVER_PROBE message header, then an unknown TLV (type 0x7ff) =
=2E=2E=2E */
+    assert_true(buf_write_u16(&buf, OOB_MSG_SERVER_PROBE));
+=
    assert_true(ctrl_msg_tlv_write_header(&buf, 0x7ff, false, 4));
+    ass=
ert_true(buf_write_u32(&buf, 0xcafef00d));
+    /* =2E=2E=2E followed by th=
e real probe_parameter */
+    const struct oob_probe_parameter in =3D { =
=2Etimestamp =3D 42, =2Eflags =3D 0 };
+    assert_true(oob_probe_parameter=
_write(&buf, &in));
+
+    struct oob_probe_parameter out =3D { 0 };
+    a=
ssert_true(oob_server_probe_read(&buf, &out));
+    assert_true(out=2Etimes=
tamp =3D=3D 42);
+
+    gc_free(&gc);
+}
+
+/* A payload with no probe_para=
meter must be rejected=2E */
+static void
+test_server_probe_read_missing(v=
oid **state)
+{
+    struct gc_arena gc =3D gc_new();
+    struct buffer bu=
f =3D alloc_buf_gc(128, &gc);
+
+    assert_true(buf_write_u16(&buf, OOB_MS=
G_SERVER_PROBE));
+    assert_true(ctrl_msg_tlv_write_header(&buf, 0x7ff, f=
alse, 4));
+    assert_true(buf_write_u32(&buf, 0));
+
+    struct oob_prob=
e_parameter out =3D { 0 };
+    assert_false(oob_server_probe_read(&buf, &o=
ut));
+
+    gc_free(&gc);
+}
+
+/* A TLV whose declared length runs past t=
he buffer must be rejected, not
+ * read out of bounds=2E */
+static void
+=
test_server_probe_read_truncated(void **state)
+{
+    struct gc_arena gc =
=3D gc_new();
+    struct buffer buf =3D alloc_buf_gc(128, &gc);
+
+    /* =
TLV header claims a 16-byte value but no value bytes follow */
+    assert_=
true(buf_write_u16(&buf, OOB_MSG_SERVER_PROBE));
+    assert_true(ctrl_msg_=
tlv_write_header(&buf, 0x7ff, false, 16));
+
+    struct oob_probe_paramete=
r out =3D { 0 };
+    assert_false(oob_server_probe_read(&buf, &out));
+
+ =
   gc_free(&gc);
+}
+
+/* A SERVER_PROBE reader rejects a payload carrying =
a different message type
+ * (here a PROBE_REPLY's), even if it contains a =
valid probe_parameter TLV=2E */
+static void
+test_server_probe_read_wrong_=
msg_type(void **state)
+{
+    struct gc_arena gc =3D gc_new();
+    struct=
 buffer buf =3D alloc_buf_gc(128, &gc);
+
+    assert_true(buf_write_u16(&b=
uf, OOB_MSG_PROBE_REPLY));
+    const struct oob_probe_parameter in =3D { =
=2Etimestamp =3D 42, =2Eflags =3D 0 };
+    assert_true(oob_probe_parameter=
_write(&buf, &in));
+
+    struct oob_probe_parameter out =3D { 0 };
+    a=
ssert_false(oob_server_probe_read(&buf, &out));
+
+    gc_free(&gc);
+}
+
+=
/* Timestamp window check accepts values within the window (either directio=
n)
+ * and rejects values outside it=2E */
+static void
+test_timestamp_in_=
window(void **state)
+{
+    const uint64_t now =3D 1000000;
+    const uin=
t64_t window =3D 30;
+
+    assert_true(oob_timestamp_in_window(now, now, w=
indow));
+    assert_true(oob_timestamp_in_window(now - window, now, window=
));      /* boundary, past */
+    assert_true(oob_timestamp_in_window(now =
+ window, now, window));      /* boundary, future */
+    assert_false(oob_=
timestamp_in_window(now - window - 1, now, window)); /* too old */
+    ass=
ert_false(oob_timestamp_in_window(now + window + 1, now, window)); /* too f=
ar ahead */
+}
+
+/* A valid, in-window SERVER_PROBE yields a reply that ec=
hoes the peer's
+ * session id and zeroes the remaining fields=2E */
+stati=
c void
+test_build_probe_reply_valid(void **state)
+{
+    struct gc_arena =
gc =3D gc_new();
+    struct buffer buf =3D alloc_buf_gc(128, &gc);
+
+    =
const uint64_t now =3D 1000000;
+    const struct oob_probe_parameter probe=
 =3D { =2Etimestamp =3D now, =2Eflags =3D 0 };
+    assert_true(oob_server_=
probe_write(&buf, &probe));
+
+    struct session_id peer;
+    memcpy(peer=
=2Eid, "PEER1234", SID_SIZE);
+
+    /* left as the caller set them: the fu=
nction only fills the session id */
+    struct oob_probe_reply reply =3D {=
 0 };
+    assert_true(oob_build_probe_reply(&buf, now, 30, &peer, &reply))=
;
+    assert_memory_equal(reply=2Epeer_session_id=2Eid, peer=2Eid, SID_SIZ=
E);
+    assert_int_equal(reply=2Epriority, 0);
+    assert_int_equal(reply=
=2Eweight, 0);
+    assert_int_equal(reply=2Econnect_lifetime, 0);
+    ass=
ert_int_equal(reply=2Eflags, 0);
+
+    gc_free(&gc);
+}
+
+/* A probe whos=
e timestamp is outside the window is dropped (no reply)=2E */
+static void
=
+test_build_probe_reply_stale(void **state)
+{
+    struct gc_arena gc =3D =
gc_new();
+    struct buffer buf =3D alloc_buf_gc(128, &gc);
+
+    const u=
int64_t now =3D 1000000;
+    const struct oob_probe_parameter probe =3D { =
=2Etimestamp =3D now - 1000, =2Eflags =3D 0 };
+    assert_true(oob_server_=
probe_write(&buf, &probe));
+
+    struct session_id peer =3D { 0 };
+    s=
truct oob_probe_reply reply =3D { 0 };
+    assert_false(oob_build_probe_re=
ply(&buf, now, 30, &peer, &reply));
+
+    gc_free(&gc);
+}
+
+/* A payload=
 without a probe_parameter is dropped (no reply)=2E */
+static void
+test_b=
uild_probe_reply_no_parameter(void **state)
+{
+    struct gc_arena gc =3D =
gc_new();
+    struct buffer buf =3D alloc_buf_gc(128, &gc);
+
+    assert_=
true(buf_write_u16(&buf, OOB_MSG_SERVER_PROBE));
+    assert_true(ctrl_msg_=
tlv_write_header(&buf, 0x7ff, false, 4));
+    assert_true(buf_write_u32(&b=
uf, 0));
+
+    struct session_id peer =3D { 0 };
+    struct oob_probe_rep=
ly reply =3D { 0 };
+    assert_false(oob_build_probe_reply(&buf, 1000000, =
30, &peer, &reply));
+
+    gc_free(&gc);
+}
+
 int
 main(void)
 {
@@ -271,=
6 +446,15 @@
         cmocka_unit_test(test_probe_parameter_too_short),
   =
      cmocka_unit_test(test_find_tlv_value_truncated),
         cmocka_unit=
_test(test_tlv_header_truncated),
+        cmocka_unit_test(test_server_pro=
be_read_finds_parameter),
+        cmocka_unit_test(test_server_probe_read_=
skips_unknown),
+        cmocka_unit_test(test_server_probe_read_missing),
=
+        cmocka_unit_test(test_server_probe_read_truncated),
+        cmock=
a_unit_test(test_server_probe_read_wrong_msg_type),
+        cmocka_unit_te=
st(test_timestamp_in_window),
+        cmocka_unit_test(test_build_probe_re=
ply_valid),
+        cmocka_unit_test(test_build_probe_reply_stale),
+     =
   cmocka_unit_test(test_build_probe_reply_no_parameter),
     };
 
     re=
turn cmocka_run_group_tests_name("oob tests", tests, NULL, NULL);

-- 
To v=
iew, visit http://gerrit=2Eopenvpn=2Enet/c/openvpn/+/1742?usp=3Demail
To un=
subscribe, or for help writing mail filters, visit http://gerrit=2Eopenvpn=
=2Enet/settings?usp=3Demail

Gerrit-MessageType: newpatchset
Gerrit-Project=
: openvpn
Gerrit-Branch: master
Gerrit-Change-Id: Iafa9efc1c156974ad9f5752e=
9de810a8c2f68c30
Gerrit-Change-Number: 1742
Gerrit-PatchSet: 10
Gerrit-Owne=
r: stipa <lstipakov@gmail=2Ecom>
Gerrit-Reviewer: flichtenheld <frank@licht=
enheld=2Ecom>
Gerrit-Reviewer: plaisthos <arne-openvpn@rfc2549=2Eorg>
Gerri=
t-CC: openvpn-devel <openvpn-devel@lists=2Esourceforge=2Enet>
Gerrit-Attent=
ion: plaisthos <arne-openvpn@rfc2549=2Eorg>
Gerrit-Attention: flichtenheld =
<frank@lichtenheld=2Ecom>

--vyKnPnI6aSw=
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable

<!DOCTYPE html><html><head><style></style></head><body><p> Attention is cur=
rently required from: flichtenheld, plaisthos=2E </p>
<p>stipa <strong>uplo=
aded patch set #10</strong> to this change=2E</p><p><a href=3D"http://gerri=
t=2Eopenvpn=2Enet/c/openvpn/+/1742?usp=3Demail">View Change</a></p><pre cla=
ss=3D"blocks" style=3D"font-family: monospace,monospace; white-space: pre-w=
rap;">oob: Add SERVER_PROBE parsing and the probe-reply decision<br><br>Add=
 the message-level helpers the server needs to answer a probe:<br><br>  - o=
ob_server_probe_write() writes a SERVER_PROBE (message-type header +<br>   =
 probe_parameter TLV); the client uses it later=2E<br>  - oob_server_probe_=
read() scans the TLV payload of a received OOB<br>    SERVER_PROBE for the =
probe_parameter TLV, skipping other/future TLV<br>    types and rejecting m=
alformed or truncated input=2E<br>  - oob_timestamp_in_window() performs th=
e cheap replay/skew check on the<br>    probe timestamp described in the wi=
re protocol specification=2E<br>  - oob_build_probe_reply() composes the tw=
o: given a received SERVER_PROBE<br>    it scans for the probe_parameter, d=
rops the probe if it is missing or<br>    its timestamp is outside the acce=
ptable window, and otherwise populates<br>    the probe_reply (echoing the =
peer&#39;s session id) to send back=2E<br><br>A probe whose timestamp falls=
 outside the window is dropped without a reply,<br>which is what the spec a=
llows: the timestamp is there so a server may silently<br>drop requests out=
side an acceptable window and reduce the replay attack surface=2E<br>The wi=
ndow itself is the caller&#39;s choice; the server path passes --hand-windo=
w in<br>a follow-up=2E<br><br>These are pure functions exercised by unit te=
sts; the server packet path<br>calls oob_build_probe_reply() in a follow-up=
, which performs the actual send=2E<br><br>Change-Id: Iafa9efc1c156974ad9f5=
752e9de810a8c2f68c30<br>Signed-off-by: Lev Stipakov &lt;lev@openvpn=2Enet&g=
t;<br>---<br>M src/openvpn/oob=2Ec<br>M src/openvpn/oob=2Eh<br>M tests/unit=
_tests/openvpn/test_oob=2Ec<br>3 files changed, 289 insertions(+), 0 deleti=
ons(-)<br><br></pre><pre class=3D"blocks" style=3D"font-family: monospace,m=
onospace; white-space: pre-wrap;">git pull ssh://gerrit=2Eopenvpn=2Enet:294=
18/openvpn refs/changes/42/1742/10</pre><pre style=3D"font-family: monospac=
e,monospace; white-space: pre-wrap;"><span>diff --git a/src/openvpn/oob=2Ec=
 b/src/openvpn/oob=2Ec</span><br><span>index c807171=2E=2E930b213 100644</s=
pan><br><span>--- a/src/openvpn/oob=2Ec</span><br><span>+++ b/src/openvpn/o=
ob=2Ec</span><br><span>@@ -81,3 +81,54 @@</span><br><span>     r-&gt;flags =
=3D buf_read_u32(buf, NULL);</span><br><span>     return true;</span><br><s=
pan> }</span><br><span style=3D"color: hsl(120, 100%, 40%);">+</span><br><s=
pan style=3D"color: hsl(120, 100%, 40%);">+bool</span><br><span style=3D"co=
lor: hsl(120, 100%, 40%);">+oob_server_probe_write(struct buffer *buf, cons=
t struct oob_probe_parameter *param)</span><br><span style=3D"color: hsl(12=
0, 100%, 40%);">+{</span><br><span style=3D"color: hsl(120, 100%, 40%);">+ =
   return buf_write_u16(buf, OOB_MSG_SERVER_PROBE) &amp;&amp; oob_probe_par=
ameter_write(buf, param);</span><br><span style=3D"color: hsl(120, 100%, 40=
%);">+}</span><br><span style=3D"color: hsl(120, 100%, 40%);">+</span><br><=
span style=3D"color: hsl(120, 100%, 40%);">+bool</span><br><span style=3D"c=
olor: hsl(120, 100%, 40%);">+oob_server_probe_read(struct buffer *payload, =
struct oob_probe_parameter *param)</span><br><span style=3D"color: hsl(120,=
 100%, 40%);">+{</span><br><span style=3D"color: hsl(120, 100%, 40%);">+   =
 if (!ctrl_msg_read_header(payload, OOB_MSG_SERVER_PROBE))</span><br><span =
style=3D"color: hsl(120, 100%, 40%);">+    {</span><br><span style=3D"color=
: hsl(120, 100%, 40%);">+        return false;</span><br><span style=3D"col=
or: hsl(120, 100%, 40%);">+    }</span><br><span style=3D"color: hsl(120, 1=
00%, 40%);">+</span><br><span style=3D"color: hsl(120, 100%, 40%);">+    st=
ruct buffer value;</span><br><span style=3D"color: hsl(120, 100%, 40%);">+ =
   if (!ctrl_msg_find_tlv(payload, OOB_TLV_PROBE_PARAMETER, &amp;value))</s=
pan><br><span style=3D"color: hsl(120, 100%, 40%);">+    {</span><br><span =
style=3D"color: hsl(120, 100%, 40%);">+        return false;</span><br><spa=
n style=3D"color: hsl(120, 100%, 40%);">+    }</span><br><span style=3D"col=
or: hsl(120, 100%, 40%);">+</span><br><span style=3D"color: hsl(120, 100%, =
40%);">+    return oob_probe_parameter_read(&amp;value, param);</span><br><=
span style=3D"color: hsl(120, 100%, 40%);">+}</span><br><span style=3D"colo=
r: hsl(120, 100%, 40%);">+</span><br><span style=3D"color: hsl(120, 100%, 4=
0%);">+bool</span><br><span style=3D"color: hsl(120, 100%, 40%);">+oob_time=
stamp_in_window(uint64_t probe_ts, uint64_t now, uint64_t window_secs)</spa=
n><br><span style=3D"color: hsl(120, 100%, 40%);">+{</span><br><span style=
=3D"color: hsl(120, 100%, 40%);">+    uint64_t diff =3D (now &gt; probe_ts)=
 ? (now - probe_ts) : (probe_ts - now);</span><br><span style=3D"color: hsl=
(120, 100%, 40%);">+    return diff &lt;=3D window_secs;</span><br><span st=
yle=3D"color: hsl(120, 100%, 40%);">+}</span><br><span style=3D"color: hsl(=
120, 100%, 40%);">+</span><br><span style=3D"color: hsl(120, 100%, 40%);">+=
bool</span><br><span style=3D"color: hsl(120, 100%, 40%);">+oob_build_probe=
_reply(struct buffer *probe_payload, uint64_t now, uint64_t window_secs,</s=
pan><br><span style=3D"color: hsl(120, 100%, 40%);">+                      =
const struct session_id *peer_sid, struct oob_probe_reply *reply)</span><br=
><span style=3D"color: hsl(120, 100%, 40%);">+{</span><br><span style=3D"co=
lor: hsl(120, 100%, 40%);">+    struct oob_probe_parameter param;</span><br=
><span style=3D"color: hsl(120, 100%, 40%);">+    if (!oob_server_probe_rea=
d(probe_payload, &amp;param))</span><br><span style=3D"color: hsl(120, 100%=
, 40%);">+    {</span><br><span style=3D"color: hsl(120, 100%, 40%);">+    =
    return false;</span><br><span style=3D"color: hsl(120, 100%, 40%);">+  =
  }</span><br><span style=3D"color: hsl(120, 100%, 40%);">+</span><br><span=
 style=3D"color: hsl(120, 100%, 40%);">+    /* Drop replayed or implausibly=
-timed probes before doing any more work=2E */</span><br><span style=3D"col=
or: hsl(120, 100%, 40%);">+    if (!oob_timestamp_in_window(param=2Etimesta=
mp, now, window_secs))</span><br><span style=3D"color: hsl(120, 100%, 40%);=
">+    {</span><br><span style=3D"color: hsl(120, 100%, 40%);">+        ret=
urn false;</span><br><span style=3D"color: hsl(120, 100%, 40%);">+    }</sp=
an><br><span style=3D"color: hsl(120, 100%, 40%);">+</span><br><span style=
=3D"color: hsl(120, 100%, 40%);">+    /* the caller&#39;s advertised values=
 are kept; only the echo is ours to fill */</span><br><span style=3D"color:=
 hsl(120, 100%, 40%);">+    reply-&gt;peer_session_id =3D *peer_sid;</span>=
<br><span style=3D"color: hsl(120, 100%, 40%);">+    return true;</span><br=
><span style=3D"color: hsl(120, 100%, 40%);">+}</span><br><span>diff --git =
a/src/openvpn/oob=2Eh b/src/openvpn/oob=2Eh</span><br><span>index 0a9a4bb=
=2E=2E1afabbd 100644</span><br><span>--- a/src/openvpn/oob=2Eh</span><br><s=
pan>+++ b/src/openvpn/oob=2Eh</span><br><span>@@ -107,4 +107,58 @@</span><b=
r><span>  */</span><br><span> bool oob_probe_reply_read(struct buffer *buf,=
 struct oob_probe_reply *r);</span><br><span> </span><br><span style=3D"col=
or: hsl(120, 100%, 40%);">+/**</span><br><span style=3D"color: hsl(120, 100=
%, 40%);">+ * Write a complete SERVER_PROBE message (message-type header + =
probe_parameter</span><br><span style=3D"color: hsl(120, 100%, 40%);">+ * T=
LV) to buf=2E Sent by the client=2E</span><br><span style=3D"color: hsl(120=
, 100%, 40%);">+ */</span><br><span style=3D"color: hsl(120, 100%, 40%);">+=
bool oob_server_probe_write(struct buffer *buf, const struct oob_probe_para=
meter *param);</span><br><span style=3D"color: hsl(120, 100%, 40%);">+</spa=
n><br><span style=3D"color: hsl(120, 100%, 40%);">+/**</span><br><span styl=
e=3D"color: hsl(120, 100%, 40%);">+ * Read a received OOB SERVER_PROBE: ver=
ify its message-type header, then scan</span><br><span style=3D"color: hsl(=
120, 100%, 40%);">+ * for the probe_parameter TLV=2E TLV types other than p=
robe_parameter are</span><br><span style=3D"color: hsl(120, 100%, 40%);">+ =
* skipped=2E payload is consumed as it is read=2E</span><br><span style=3D"=
color: hsl(120, 100%, 40%);">+ *</span><br><span style=3D"color: hsl(120, 1=
00%, 40%);">+ * @param payload  buffer positioned at the start of the OOB m=
essage payload</span><br><span style=3D"color: hsl(120, 100%, 40%);">+ * @p=
aram param    filled with the parsed probe_parameter on success</span><br><=
span style=3D"color: hsl(120, 100%, 40%);">+ * @return true if the header m=
atched and a well-formed probe_parameter was</span><br><span style=3D"color=
: hsl(120, 100%, 40%);">+ *         found, false otherwise</span><br><span =
style=3D"color: hsl(120, 100%, 40%);">+ */</span><br><span style=3D"color: =
hsl(120, 100%, 40%);">+bool oob_server_probe_read(struct buffer *payload, s=
truct oob_probe_parameter *param);</span><br><span style=3D"color: hsl(120,=
 100%, 40%);">+</span><br><span style=3D"color: hsl(120, 100%, 40%);">+/**<=
/span><br><span style=3D"color: hsl(120, 100%, 40%);">+ * Check whether a p=
robe timestamp is within an acceptable window around the</span><br><span st=
yle=3D"color: hsl(120, 100%, 40%);">+ * current time=2E Used to cheaply dro=
p replayed or implausibly-timed probes</span><br><span style=3D"color: hsl(=
120, 100%, 40%);">+ * before doing any further work (see the probe_paramete=
r timestamp rationale</span><br><span style=3D"color: hsl(120, 100%, 40%);"=
>+ * in the wire protocol specification)=2E</span><br><span style=3D"color:=
 hsl(120, 100%, 40%);">+ *</span><br><span style=3D"color: hsl(120, 100%, 4=
0%);">+ * @param probe_ts     timestamp from the probe_parameter (UNIX seco=
nds)</span><br><span style=3D"color: hsl(120, 100%, 40%);">+ * @param now  =
        current time (UNIX seconds)</span><br><span style=3D"color: hsl(120=
, 100%, 40%);">+ * @param window_secs  maximum allowed difference, in eithe=
r direction</span><br><span style=3D"color: hsl(120, 100%, 40%);">+ * @retu=
rn true if |now - probe_ts| &lt;=3D window_secs</span><br><span style=3D"co=
lor: hsl(120, 100%, 40%);">+ */</span><br><span style=3D"color: hsl(120, 10=
0%, 40%);">+bool oob_timestamp_in_window(uint64_t probe_ts, uint64_t now, u=
int64_t window_secs);</span><br><span style=3D"color: hsl(120, 100%, 40%);"=
>+</span><br><span style=3D"color: hsl(120, 100%, 40%);">+/**</span><br><sp=
an style=3D"color: hsl(120, 100%, 40%);">+ * Process the TLV payload of a r=
eceived SERVER_PROBE and decide whether to</span><br><span style=3D"color: =
hsl(120, 100%, 40%);">+ * answer it=2E Combines oob_server_probe_read() and=
 oob_timestamp_in_window():</span><br><span style=3D"color: hsl(120, 100%, =
40%);">+ * the probe is dropped (false returned) if it has no valid probe_p=
arameter or</span><br><span style=3D"color: hsl(120, 100%, 40%);">+ * its t=
imestamp is outside the acceptable window=2E</span><br><span style=3D"color=
: hsl(120, 100%, 40%);">+ *</span><br><span style=3D"color: hsl(120, 100%, =
40%);">+ * reply carries the values the server advertises (priority, weight=
,</span><br><span style=3D"color: hsl(120, 100%, 40%);">+ * max_latency_dif=
f, connect_lifetime, flags) in, and is deliberately not</span><br><span sty=
le=3D"color: hsl(120, 100%, 40%);">+ * cleared: on success only the peer&#3=
9;s session id is filled in, leaving the</span><br><span style=3D"color: hs=
l(120, 100%, 40%);">+ * reply ready to be wrapped and sent=2E</span><br><sp=
an style=3D"color: hsl(120, 100%, 40%);">+ *</span><br><span style=3D"color=
: hsl(120, 100%, 40%);">+ * This is the transport-agnostic decision step; t=
he caller performs the send=2E</span><br><span style=3D"color: hsl(120, 100=
%, 40%);">+ *</span><br><span style=3D"color: hsl(120, 100%, 40%);">+ * @pa=
ram probe_payload  TLV payload of the received OOB SERVER_PROBE</span><br><=
span style=3D"color: hsl(120, 100%, 40%);">+ * @param now            curren=
t time (UNIX seconds)</span><br><span style=3D"color: hsl(120, 100%, 40%);"=
>+ * @param window_secs    acceptable timestamp skew, in either direction</=
span><br><span style=3D"color: hsl(120, 100%, 40%);">+ * @param peer_sid   =
    session id of the requesting peer (echoed in the reply)</span><br><span=
 style=3D"color: hsl(120, 100%, 40%);">+ * @param reply          in: the va=
lues to advertise; out: the reply to send</span><br><span style=3D"color: h=
sl(120, 100%, 40%);">+ * @return true if a reply should be sent, false to s=
ilently drop the probe</span><br><span style=3D"color: hsl(120, 100%, 40%);=
">+ */</span><br><span style=3D"color: hsl(120, 100%, 40%);">+bool oob_buil=
d_probe_reply(struct buffer *probe_payload, uint64_t now, uint64_t window_s=
ecs,</span><br><span style=3D"color: hsl(120, 100%, 40%);">+               =
            const struct session_id *peer_sid, struct oob_probe_reply *repl=
y);</span><br><span style=3D"color: hsl(120, 100%, 40%);">+</span><br><span=
> #endif /* OOB_H */</span><br><span>diff --git a/tests/unit_tests/openvpn/=
test_oob=2Ec b/tests/unit_tests/openvpn/test_oob=2Ec</span><br><span>index =
aa4eb23=2E=2E371cbc6 100644</span><br><span>--- a/tests/unit_tests/openvpn/=
test_oob=2Ec</span><br><span>+++ b/tests/unit_tests/openvpn/test_oob=2Ec</s=
pan><br><span>@@ -259,6 +259,181 @@</span><br><span>     gc_free(&amp;gc);<=
/span><br><span> }</span><br><span> </span><br><span style=3D"color: hsl(12=
0, 100%, 40%);">+/* A SERVER_PROBE carrying just a probe_parameter is found=
 by the scan=2E */</span><br><span style=3D"color: hsl(120, 100%, 40%);">+s=
tatic void</span><br><span style=3D"color: hsl(120, 100%, 40%);">+test_serv=
er_probe_read_finds_parameter(void **state)</span><br><span style=3D"color:=
 hsl(120, 100%, 40%);">+{</span><br><span style=3D"color: hsl(120, 100%, 40=
%);">+    struct gc_arena gc =3D gc_new();</span><br><span style=3D"color: =
hsl(120, 100%, 40%);">+    struct buffer buf =3D alloc_buf_gc(128, &amp;gc)=
;</span><br><span style=3D"color: hsl(120, 100%, 40%);">+</span><br><span s=
tyle=3D"color: hsl(120, 100%, 40%);">+    const struct oob_probe_parameter =
in =3D {</span><br><span style=3D"color: hsl(120, 100%, 40%);">+        =2E=
timestamp =3D 0x1122334455667788ULL,</span><br><span style=3D"color: hsl(12=
0, 100%, 40%);">+        =2Eflags =3D 0,</span><br><span style=3D"color: hs=
l(120, 100%, 40%);">+    };</span><br><span style=3D"color: hsl(120, 100%, =
40%);">+    assert_true(oob_server_probe_write(&amp;buf, &amp;in));</span><=
br><span style=3D"color: hsl(120, 100%, 40%);">+</span><br><span style=3D"c=
olor: hsl(120, 100%, 40%);">+    struct oob_probe_parameter out =3D { 0 };<=
/span><br><span style=3D"color: hsl(120, 100%, 40%);">+    assert_true(oob_=
server_probe_read(&amp;buf, &amp;out));</span><br><span style=3D"color: hsl=
(120, 100%, 40%);">+    assert_true(in=2Etimestamp =3D=3D out=2Etimestamp);=
</span><br><span style=3D"color: hsl(120, 100%, 40%);">+    assert_int_equa=
l(in=2Eflags, out=2Eflags);</span><br><span style=3D"color: hsl(120, 100%, =
40%);">+</span><br><span style=3D"color: hsl(120, 100%, 40%);">+    gc_free=
(&amp;gc);</span><br><span style=3D"color: hsl(120, 100%, 40%);">+}</span><=
br><span style=3D"color: hsl(120, 100%, 40%);">+</span><br><span style=3D"c=
olor: hsl(120, 100%, 40%);">+/* TLVs other than probe_parameter are skipped=
, so the scan finds the</span><br><span style=3D"color: hsl(120, 100%, 40%)=
;">+ * probe_parameter even when preceded by an unknown TLV=2E */</span><br=
><span style=3D"color: hsl(120, 100%, 40%);">+static void</span><br><span s=
tyle=3D"color: hsl(120, 100%, 40%);">+test_server_probe_read_skips_unknown(=
void **state)</span><br><span style=3D"color: hsl(120, 100%, 40%);">+{</spa=
n><br><span style=3D"color: hsl(120, 100%, 40%);">+    struct gc_arena gc =
=3D gc_new();</span><br><span style=3D"color: hsl(120, 100%, 40%);">+    st=
ruct buffer buf =3D alloc_buf_gc(128, &amp;gc);</span><br><span style=3D"co=
lor: hsl(120, 100%, 40%);">+</span><br><span style=3D"color: hsl(120, 100%,=
 40%);">+    /* SERVER_PROBE message header, then an unknown TLV (type 0x7f=
f) =2E=2E=2E */</span><br><span style=3D"color: hsl(120, 100%, 40%);">+    =
assert_true(buf_write_u16(&amp;buf, OOB_MSG_SERVER_PROBE));</span><br><span=
 style=3D"color: hsl(120, 100%, 40%);">+    assert_true(ctrl_msg_tlv_write_=
header(&amp;buf, 0x7ff, false, 4));</span><br><span style=3D"color: hsl(120=
, 100%, 40%);">+    assert_true(buf_write_u32(&amp;buf, 0xcafef00d));</span=
><br><span style=3D"color: hsl(120, 100%, 40%);">+    /* =2E=2E=2E followed=
 by the real probe_parameter */</span><br><span style=3D"color: hsl(120, 10=
0%, 40%);">+    const struct oob_probe_parameter in =3D { =2Etimestamp =3D =
42, =2Eflags =3D 0 };</span><br><span style=3D"color: hsl(120, 100%, 40%);"=
>+    assert_true(oob_probe_parameter_write(&amp;buf, &amp;in));</span><br>=
<span style=3D"color: hsl(120, 100%, 40%);">+</span><br><span style=3D"colo=
r: hsl(120, 100%, 40%);">+    struct oob_probe_parameter out =3D { 0 };</sp=
an><br><span style=3D"color: hsl(120, 100%, 40%);">+    assert_true(oob_ser=
ver_probe_read(&amp;buf, &amp;out));</span><br><span style=3D"color: hsl(12=
0, 100%, 40%);">+    assert_true(out=2Etimestamp =3D=3D 42);</span><br><spa=
n style=3D"color: hsl(120, 100%, 40%);">+</span><br><span style=3D"color: h=
sl(120, 100%, 40%);">+    gc_free(&amp;gc);</span><br><span style=3D"color:=
 hsl(120, 100%, 40%);">+}</span><br><span style=3D"color: hsl(120, 100%, 40=
%);">+</span><br><span style=3D"color: hsl(120, 100%, 40%);">+/* A payload =
with no probe_parameter must be rejected=2E */</span><br><span style=3D"col=
or: hsl(120, 100%, 40%);">+static void</span><br><span style=3D"color: hsl(=
120, 100%, 40%);">+test_server_probe_read_missing(void **state)</span><br><=
span style=3D"color: hsl(120, 100%, 40%);">+{</span><br><span style=3D"colo=
r: hsl(120, 100%, 40%);">+    struct gc_arena gc =3D gc_new();</span><br><s=
pan style=3D"color: hsl(120, 100%, 40%);">+    struct buffer buf =3D alloc_=
buf_gc(128, &amp;gc);</span><br><span style=3D"color: hsl(120, 100%, 40%);"=
>+</span><br><span style=3D"color: hsl(120, 100%, 40%);">+    assert_true(b=
uf_write_u16(&amp;buf, OOB_MSG_SERVER_PROBE));</span><br><span style=3D"col=
or: hsl(120, 100%, 40%);">+    assert_true(ctrl_msg_tlv_write_header(&amp;b=
uf, 0x7ff, false, 4));</span><br><span style=3D"color: hsl(120, 100%, 40%);=
">+    assert_true(buf_write_u32(&amp;buf, 0));</span><br><span style=3D"co=
lor: hsl(120, 100%, 40%);">+</span><br><span style=3D"color: hsl(120, 100%,=
 40%);">+    struct oob_probe_parameter out =3D { 0 };</span><br><span styl=
e=3D"color: hsl(120, 100%, 40%);">+    assert_false(oob_server_probe_read(&=
amp;buf, &amp;out));</span><br><span style=3D"color: hsl(120, 100%, 40%);">=
+</span><br><span style=3D"color: hsl(120, 100%, 40%);">+    gc_free(&amp;g=
c);</span><br><span style=3D"color: hsl(120, 100%, 40%);">+}</span><br><spa=
n style=3D"color: hsl(120, 100%, 40%);">+</span><br><span style=3D"color: h=
sl(120, 100%, 40%);">+/* A TLV whose declared length runs past the buffer m=
ust be rejected, not</span><br><span style=3D"color: hsl(120, 100%, 40%);">=
+ * read out of bounds=2E */</span><br><span style=3D"color: hsl(120, 100%,=
 40%);">+static void</span><br><span style=3D"color: hsl(120, 100%, 40%);">=
+test_server_probe_read_truncated(void **state)</span><br><span style=3D"co=
lor: hsl(120, 100%, 40%);">+{</span><br><span style=3D"color: hsl(120, 100%=
, 40%);">+    struct gc_arena gc =3D gc_new();</span><br><span style=3D"col=
or: hsl(120, 100%, 40%);">+    struct buffer buf =3D alloc_buf_gc(128, &amp=
;gc);</span><br><span style=3D"color: hsl(120, 100%, 40%);">+</span><br><sp=
an style=3D"color: hsl(120, 100%, 40%);">+    /* TLV header claims a 16-byt=
e value but no value bytes follow */</span><br><span style=3D"color: hsl(12=
0, 100%, 40%);">+    assert_true(buf_write_u16(&amp;buf, OOB_MSG_SERVER_PRO=
BE));</span><br><span style=3D"color: hsl(120, 100%, 40%);">+    assert_tru=
e(ctrl_msg_tlv_write_header(&amp;buf, 0x7ff, false, 16));</span><br><span s=
tyle=3D"color: hsl(120, 100%, 40%);">+</span><br><span style=3D"color: hsl(=
120, 100%, 40%);">+    struct oob_probe_parameter out =3D { 0 };</span><br>=
<span style=3D"color: hsl(120, 100%, 40%);">+    assert_false(oob_server_pr=
obe_read(&amp;buf, &amp;out));</span><br><span style=3D"color: hsl(120, 100=
%, 40%);">+</span><br><span style=3D"color: hsl(120, 100%, 40%);">+    gc_f=
ree(&amp;gc);</span><br><span style=3D"color: hsl(120, 100%, 40%);">+}</spa=
n><br><span style=3D"color: hsl(120, 100%, 40%);">+</span><br><span style=
=3D"color: hsl(120, 100%, 40%);">+/* A SERVER_PROBE reader rejects a payloa=
d carrying a different message type</span><br><span style=3D"color: hsl(120=
, 100%, 40%);">+ * (here a PROBE_REPLY&#39;s), even if it contains a valid =
probe_parameter TLV=2E */</span><br><span style=3D"color: hsl(120, 100%, 40=
%);">+static void</span><br><span style=3D"color: hsl(120, 100%, 40%);">+te=
st_server_probe_read_wrong_msg_type(void **state)</span><br><span style=3D"=
color: hsl(120, 100%, 40%);">+{</span><br><span style=3D"color: hsl(120, 10=
0%, 40%);">+    struct gc_arena gc =3D gc_new();</span><br><span style=3D"c=
olor: hsl(120, 100%, 40%);">+    struct buffer buf =3D alloc_buf_gc(128, &a=
mp;gc);</span><br><span style=3D"color: hsl(120, 100%, 40%);">+</span><br><=
span style=3D"color: hsl(120, 100%, 40%);">+    assert_true(buf_write_u16(&=
amp;buf, OOB_MSG_PROBE_REPLY));</span><br><span style=3D"color: hsl(120, 10=
0%, 40%);">+    const struct oob_probe_parameter in =3D { =2Etimestamp =3D =
42, =2Eflags =3D 0 };</span><br><span style=3D"color: hsl(120, 100%, 40%);"=
>+    assert_true(oob_probe_parameter_write(&amp;buf, &amp;in));</span><br>=
<span style=3D"color: hsl(120, 100%, 40%);">+</span><br><span style=3D"colo=
r: hsl(120, 100%, 40%);">+    struct oob_probe_parameter out =3D { 0 };</sp=
an><br><span style=3D"color: hsl(120, 100%, 40%);">+    assert_false(oob_se=
rver_probe_read(&amp;buf, &amp;out));</span><br><span style=3D"color: hsl(1=
20, 100%, 40%);">+</span><br><span style=3D"color: hsl(120, 100%, 40%);">+ =
   gc_free(&amp;gc);</span><br><span style=3D"color: hsl(120, 100%, 40%);">=
+}</span><br><span style=3D"color: hsl(120, 100%, 40%);">+</span><br><span =
style=3D"color: hsl(120, 100%, 40%);">+/* Timestamp window check accepts va=
lues within the window (either direction)</span><br><span style=3D"color: h=
sl(120, 100%, 40%);">+ * and rejects values outside it=2E */</span><br><spa=
n style=3D"color: hsl(120, 100%, 40%);">+static void</span><br><span style=
=3D"color: hsl(120, 100%, 40%);">+test_timestamp_in_window(void **state)</s=
pan><br><span style=3D"color: hsl(120, 100%, 40%);">+{</span><br><span styl=
e=3D"color: hsl(120, 100%, 40%);">+    const uint64_t now =3D 1000000;</spa=
n><br><span style=3D"color: hsl(120, 100%, 40%);">+    const uint64_t windo=
w =3D 30;</span><br><span style=3D"color: hsl(120, 100%, 40%);">+</span><br=
><span style=3D"color: hsl(120, 100%, 40%);">+    assert_true(oob_timestamp=
_in_window(now, now, window));</span><br><span style=3D"color: hsl(120, 100=
%, 40%);">+    assert_true(oob_timestamp_in_window(now - window, now, windo=
w));      /* boundary, past */</span><br><span style=3D"color: hsl(120, 100=
%, 40%);">+    assert_true(oob_timestamp_in_window(now + window, now, windo=
w));      /* boundary, future */</span><br><span style=3D"color: hsl(120, 1=
00%, 40%);">+    assert_false(oob_timestamp_in_window(now - window - 1, now=
, window)); /* too old */</span><br><span style=3D"color: hsl(120, 100%, 40=
%);">+    assert_false(oob_timestamp_in_window(now + window + 1, now, windo=
w)); /* too far ahead */</span><br><span style=3D"color: hsl(120, 100%, 40%=
);">+}</span><br><span style=3D"color: hsl(120, 100%, 40%);">+</span><br><s=
pan style=3D"color: hsl(120, 100%, 40%);">+/* A valid, in-window SERVER_PRO=
BE yields a reply that echoes the peer&#39;s</span><br><span style=3D"color=
: hsl(120, 100%, 40%);">+ * session id and zeroes the remaining fields=2E *=
/</span><br><span style=3D"color: hsl(120, 100%, 40%);">+static void</span>=
<br><span style=3D"color: hsl(120, 100%, 40%);">+test_build_probe_reply_val=
id(void **state)</span><br><span style=3D"color: hsl(120, 100%, 40%);">+{</=
span><br><span style=3D"color: hsl(120, 100%, 40%);">+    struct gc_arena g=
c =3D gc_new();</span><br><span style=3D"color: hsl(120, 100%, 40%);">+    =
struct buffer buf =3D alloc_buf_gc(128, &amp;gc);</span><br><span style=3D"=
color: hsl(120, 100%, 40%);">+</span><br><span style=3D"color: hsl(120, 100=
%, 40%);">+    const uint64_t now =3D 1000000;</span><br><span style=3D"col=
or: hsl(120, 100%, 40%);">+    const struct oob_probe_parameter probe =3D {=
 =2Etimestamp =3D now, =2Eflags =3D 0 };</span><br><span style=3D"color: hs=
l(120, 100%, 40%);">+    assert_true(oob_server_probe_write(&amp;buf, &amp;=
probe));</span><br><span style=3D"color: hsl(120, 100%, 40%);">+</span><br>=
<span style=3D"color: hsl(120, 100%, 40%);">+    struct session_id peer;</s=
pan><br><span style=3D"color: hsl(120, 100%, 40%);">+    memcpy(peer=2Eid, =
&quot;PEER1234&quot;, SID_SIZE);</span><br><span style=3D"color: hsl(120, 1=
00%, 40%);">+</span><br><span style=3D"color: hsl(120, 100%, 40%);">+    /*=
 left as the caller set them: the function only fills the session id */</sp=
an><br><span style=3D"color: hsl(120, 100%, 40%);">+    struct oob_probe_re=
ply reply =3D { 0 };</span><br><span style=3D"color: hsl(120, 100%, 40%);">=
+    assert_true(oob_build_probe_reply(&amp;buf, now, 30, &amp;peer, &amp;r=
eply));</span><br><span style=3D"color: hsl(120, 100%, 40%);">+    assert_m=
emory_equal(reply=2Epeer_session_id=2Eid, peer=2Eid, SID_SIZE);</span><br><=
span style=3D"color: hsl(120, 100%, 40%);">+    assert_int_equal(reply=2Epr=
iority, 0);</span><br><span style=3D"color: hsl(120, 100%, 40%);">+    asse=
rt_int_equal(reply=2Eweight, 0);</span><br><span style=3D"color: hsl(120, 1=
00%, 40%);">+    assert_int_equal(reply=2Econnect_lifetime, 0);</span><br><=
span style=3D"color: hsl(120, 100%, 40%);">+    assert_int_equal(reply=2Efl=
ags, 0);</span><br><span style=3D"color: hsl(120, 100%, 40%);">+</span><br>=
<span style=3D"color: hsl(120, 100%, 40%);">+    gc_free(&amp;gc);</span><b=
r><span style=3D"color: hsl(120, 100%, 40%);">+}</span><br><span style=3D"c=
olor: hsl(120, 100%, 40%);">+</span><br><span style=3D"color: hsl(120, 100%=
, 40%);">+/* A probe whose timestamp is outside the window is dropped (no r=
eply)=2E */</span><br><span style=3D"color: hsl(120, 100%, 40%);">+static v=
oid</span><br><span style=3D"color: hsl(120, 100%, 40%);">+test_build_probe=
_reply_stale(void **state)</span><br><span style=3D"color: hsl(120, 100%, 4=
0%);">+{</span><br><span style=3D"color: hsl(120, 100%, 40%);">+    struct =
gc_arena gc =3D gc_new();</span><br><span style=3D"color: hsl(120, 100%, 40=
%);">+    struct buffer buf =3D alloc_buf_gc(128, &amp;gc);</span><br><span=
 style=3D"color: hsl(120, 100%, 40%);">+</span><br><span style=3D"color: hs=
l(120, 100%, 40%);">+    const uint64_t now =3D 1000000;</span><br><span st=
yle=3D"color: hsl(120, 100%, 40%);">+    const struct oob_probe_parameter p=
robe =3D { =2Etimestamp =3D now - 1000, =2Eflags =3D 0 };</span><br><span s=
tyle=3D"color: hsl(120, 100%, 40%);">+    assert_true(oob_server_probe_writ=
e(&amp;buf, &amp;probe));</span><br><span style=3D"color: hsl(120, 100%, 40=
%);">+</span><br><span style=3D"color: hsl(120, 100%, 40%);">+    struct se=
ssion_id peer =3D { 0 };</span><br><span style=3D"color: hsl(120, 100%, 40%=
);">+    struct oob_probe_reply reply =3D { 0 };</span><br><span style=3D"c=
olor: hsl(120, 100%, 40%);">+    assert_false(oob_build_probe_reply(&amp;bu=
f, now, 30, &amp;peer, &amp;reply));</span><br><span style=3D"color: hsl(12=
0, 100%, 40%);">+</span><br><span style=3D"color: hsl(120, 100%, 40%);">+  =
  gc_free(&amp;gc);</span><br><span style=3D"color: hsl(120, 100%, 40%);">+=
}</span><br><span style=3D"color: hsl(120, 100%, 40%);">+</span><br><span s=
tyle=3D"color: hsl(120, 100%, 40%);">+/* A payload without a probe_paramete=
r is dropped (no reply)=2E */</span><br><span style=3D"color: hsl(120, 100%=
, 40%);">+static void</span><br><span style=3D"color: hsl(120, 100%, 40%);"=
>+test_build_probe_reply_no_parameter(void **state)</span><br><span style=
=3D"color: hsl(120, 100%, 40%);">+{</span><br><span style=3D"color: hsl(120=
, 100%, 40%);">+    struct gc_arena gc =3D gc_new();</span><br><span style=
=3D"color: hsl(120, 100%, 40%);">+    struct buffer buf =3D alloc_buf_gc(12=
8, &amp;gc);</span><br><span style=3D"color: hsl(120, 100%, 40%);">+</span>=
<br><span style=3D"color: hsl(120, 100%, 40%);">+    assert_true(buf_write_=
u16(&amp;buf, OOB_MSG_SERVER_PROBE));</span><br><span style=3D"color: hsl(1=
20, 100%, 40%);">+    assert_true(ctrl_msg_tlv_write_header(&amp;buf, 0x7ff=
, false, 4));</span><br><span style=3D"color: hsl(120, 100%, 40%);">+    as=
sert_true(buf_write_u32(&amp;buf, 0));</span><br><span style=3D"color: hsl(=
120, 100%, 40%);">+</span><br><span style=3D"color: hsl(120, 100%, 40%);">+=
    struct session_id peer =3D { 0 };</span><br><span style=3D"color: hsl(1=
20, 100%, 40%);">+    struct oob_probe_reply reply =3D { 0 };</span><br><sp=
an style=3D"color: hsl(120, 100%, 40%);">+    assert_false(oob_build_probe_=
reply(&amp;buf, 1000000, 30, &amp;peer, &amp;reply));</span><br><span style=
=3D"color: hsl(120, 100%, 40%);">+</span><br><span style=3D"color: hsl(120,=
 100%, 40%);">+    gc_free(&amp;gc);</span><br><span style=3D"color: hsl(12=
0, 100%, 40%);">+}</span><br><span style=3D"color: hsl(120, 100%, 40%);">+<=
/span><br><span> int</span><br><span> main(void)</span><br><span> {</span><=
br><span>@@ -271,6 +446,15 @@</span><br><span>         cmocka_unit_test(tes=
t_probe_parameter_too_short),</span><br><span>         cmocka_unit_test(tes=
t_find_tlv_value_truncated),</span><br><span>         cmocka_unit_test(test=
_tlv_header_truncated),</span><br><span style=3D"color: hsl(120, 100%, 40%)=
;">+        cmocka_unit_test(test_server_probe_read_finds_parameter),</span=
><br><span style=3D"color: hsl(120, 100%, 40%);">+        cmocka_unit_test(=
test_server_probe_read_skips_unknown),</span><br><span style=3D"color: hsl(=
120, 100%, 40%);">+        cmocka_unit_test(test_server_probe_read_missing)=
,</span><br><span style=3D"color: hsl(120, 100%, 40%);">+        cmocka_uni=
t_test(test_server_probe_read_truncated),</span><br><span style=3D"color: h=
sl(120, 100%, 40%);">+        cmocka_unit_test(test_server_probe_read_wrong=
_msg_type),</span><br><span style=3D"color: hsl(120, 100%, 40%);">+        =
cmocka_unit_test(test_timestamp_in_window),</span><br><span style=3D"color:=
 hsl(120, 100%, 40%);">+        cmocka_unit_test(test_build_probe_reply_val=
id),</span><br><span style=3D"color: hsl(120, 100%, 40%);">+        cmocka_=
unit_test(test_build_probe_reply_stale),</span><br><span style=3D"color: hs=
l(120, 100%, 40%);">+        cmocka_unit_test(test_build_probe_reply_no_par=
ameter),</span><br><span>     };</span><br><span> </span><br><span>     ret=
urn cmocka_run_group_tests_name(&quot;oob tests&quot;, tests, NULL, NULL);<=
/span><br><span></span><br></pre><p>To view, visit <a href=3D"http://gerrit=
=2Eopenvpn=2Enet/c/openvpn/+/1742?usp=3Demail">change 1742</a>=2E To unsubs=
cribe, or for help writing mail filters, visit <a href=3D"http://gerrit=2Eo=
penvpn=2Enet/settings?usp=3Demail">settings</a>=2E</p><div itemscope itemty=
pe=3D"http://schema=2Eorg/EmailMessage"><div itemscope itemprop=3D"action" =
itemtype=3D"http://schema=2Eorg/ViewAction"><link itemprop=3D"url" href=3D"=
http://gerrit=2Eopenvpn=2Enet/c/openvpn/+/1742?usp=3Demail"/><meta itemprop=
=3D"name" content=3D"View Change"/></div></div>

<div style=3D"display:none=
"> Gerrit-MessageType: newpatchset </div>
<div style=3D"display:none"> Gerr=
it-Project: openvpn </div>
<div style=3D"display:none"> Gerrit-Branch: mast=
er </div>
<div style=3D"display:none"> Gerrit-Change-Id: Iafa9efc1c156974ad=
9f5752e9de810a8c2f68c30 </div>
<div style=3D"display:none"> Gerrit-Change-N=
umber: 1742 </div>
<div style=3D"display:none"> Gerrit-PatchSet: 10 </div>
=
<div style=3D"display:none"> Gerrit-Owner: stipa &lt;lstipakov@gmail=2Ecom&=
gt; </div>
<div style=3D"display:none"> Gerrit-Reviewer: flichtenheld &lt;f=
rank@lichtenheld=2Ecom&gt; </div>
<div style=3D"display:none"> Gerrit-Revie=
wer: plaisthos &lt;arne-openvpn@rfc2549=2Eorg&gt; </div>
<div style=3D"disp=
lay:none"> Gerrit-CC: openvpn-devel &lt;openvpn-devel@lists=2Esourceforge=
=2Enet&gt; </div>
<div style=3D"display:none"> Gerrit-Attention: plaisthos =
&lt;arne-openvpn@rfc2549=2Eorg&gt; </div>
<div style=3D"display:none"> Gerr=
it-Attention: flichtenheld &lt;frank@lichtenheld=2Ecom&gt; </div>

</body><=
/html>
--vyKnPnI6aSw=--


--===============4548603142499845786==
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: inline


--===============4548603142499845786==
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: inline

_______________________________________________
Openvpn-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/openvpn-devel

--===============4548603142499845786==--