Skip to content

Commit

Permalink
Handle missing DCO peer by restarting the session
Browse files Browse the repository at this point in the history
Occasionally, CMD_DEL_PEER is not delivered to userspace, preventing the
openvpn process from registering the event. To handle this case, we
check if calls to the Linux DCO module return an error, and, if so, send
a SIGUSR1 signal to reset the session.

Most DCO commands that return an error already trigger a SIGUSR1 signal
or even call _exit(1). This commit extends that behavior to include
dco_get_peer_stats_multi() and dco_get_peer_stats().

Change-Id: Ib118426c5a69256894040c69856a4003d9f4637c
Signed-off-by: Ralf Lici <[email protected]>
Acked-by: Frank Lichtenheld <[email protected]>
Message-Id: <[email protected]>
URL: https://www.mail-archive.com/[email protected]/msg31022.html
Signed-off-by: Gert Doering <[email protected]>
  • Loading branch information
ralflici authored and cron2 committed Mar 8, 2025
1 parent 7e55352 commit 6f9ba8b
Show file tree
Hide file tree
Showing 8 changed files with 63 additions and 21 deletions.
18 changes: 11 additions & 7 deletions src/openvpn/dco.h
Original file line number Diff line number Diff line change
Expand Up @@ -231,17 +231,20 @@ void dco_delete_iroutes(struct multi_context *m, struct multi_instance *mi);
/**
* Update traffic statistics for all peers
*
* @param dco DCO device context
* @param m the server context
* @param dco DCO device context
* @param m the server context
* @param raise_sigusr1_on_err whether to raise SIGUSR1 on error
**/
int dco_get_peer_stats_multi(dco_context_t *dco, struct multi_context *m);
int dco_get_peer_stats_multi(dco_context_t *dco, struct multi_context *m,
const bool raise_sigusr1_on_err);

/**
* Update traffic statistics for single peer
*
* @param c instance context of the peer
* @param c instance context of the peer
* @param raise_sigusr1_on_err whether to raise SIGUSR1 on error
**/
int dco_get_peer_stats(struct context *c);
int dco_get_peer_stats(struct context *c, const bool raise_sigusr1_on_err);

/**
* Retrieve the list of ciphers supported by the current platform
Expand Down Expand Up @@ -373,13 +376,14 @@ dco_delete_iroutes(struct multi_context *m, struct multi_instance *mi)
}

static inline int
dco_get_peer_stats_multi(dco_context_t *dco, struct multi_context *m)
dco_get_peer_stats_multi(dco_context_t *dco, struct multi_context *m,
const bool raise_sigusr1_on_err)
{
return 0;
}

static inline int
dco_get_peer_stats(struct context *c)
dco_get_peer_stats(struct context *c, const bool raise_sigusr1_on_err)
{
return 0;
}
Expand Down
5 changes: 3 additions & 2 deletions src/openvpn/dco_freebsd.c
Original file line number Diff line number Diff line change
Expand Up @@ -713,7 +713,8 @@ dco_update_peer_stat(struct multi_context *m, uint32_t peerid, const nvlist_t *n
}

int
dco_get_peer_stats_multi(dco_context_t *dco, struct multi_context *m)
dco_get_peer_stats_multi(dco_context_t *dco, struct multi_context *m,
const bool raise_sigusr1_on_err)
{

struct ifdrv drv;
Expand Down Expand Up @@ -781,7 +782,7 @@ dco_get_peer_stats_multi(dco_context_t *dco, struct multi_context *m)
}

int
dco_get_peer_stats(struct context *c)
dco_get_peer_stats(struct context *c, const bool raise_sigusr1_on_err)
{
/* Not implemented. */
return 0;
Expand Down
28 changes: 25 additions & 3 deletions src/openvpn/dco_linux.c
Original file line number Diff line number Diff line change
Expand Up @@ -952,7 +952,8 @@ dco_parse_peer_multi(struct nl_msg *msg, void *arg)
}

int
dco_get_peer_stats_multi(dco_context_t *dco, struct multi_context *m)
dco_get_peer_stats_multi(dco_context_t *dco, struct multi_context *m,
const bool raise_sigusr1_on_err)
{
msg(D_DCO_DEBUG, "%s", __func__);

Expand All @@ -963,6 +964,14 @@ dco_get_peer_stats_multi(dco_context_t *dco, struct multi_context *m)
int ret = ovpn_nl_msg_send(dco, nl_msg, dco_parse_peer_multi, m, __func__);

nlmsg_free(nl_msg);

if (raise_sigusr1_on_err && ret < 0)
{
msg(M_WARN, "Error retrieving DCO peer stats: the underlying DCO peer"
"may have been deleted from the kernel without notifying "
"userspace. Restarting the session");
register_signal(m->top.sig, SIGUSR1, "dco peer stats error");
}
return ret;
}

Expand Down Expand Up @@ -1008,9 +1017,14 @@ dco_parse_peer(struct nl_msg *msg, void *arg)
}

int
dco_get_peer_stats(struct context *c)
dco_get_peer_stats(struct context *c, const bool raise_sigusr1_on_err)
{
uint32_t peer_id = c->c2.tls_multi->dco_peer_id;
int peer_id = c->c2.tls_multi->dco_peer_id;
if (peer_id == -1)
{
return 0;
}

msg(D_DCO_DEBUG, "%s: peer-id %d", __func__, peer_id);

if (!c->c1.tuntap)
Expand All @@ -1030,6 +1044,14 @@ dco_get_peer_stats(struct context *c)

nla_put_failure:
nlmsg_free(nl_msg);

if (raise_sigusr1_on_err && ret < 0)
{
msg(M_WARN, "Error retrieving DCO peer stats: the underlying DCO peer"
"may have been deleted from the kernel without notifying "
"userspace. Restarting the session");
register_signal(c->sig, SIGUSR1, "dco peer stats error");
}
return ret;
}

Expand Down
5 changes: 3 additions & 2 deletions src/openvpn/dco_win.c
Original file line number Diff line number Diff line change
Expand Up @@ -712,14 +712,15 @@ dco_do_read(dco_context_t *dco)
}

int
dco_get_peer_stats_multi(dco_context_t *dco, struct multi_context *m)
dco_get_peer_stats_multi(dco_context_t *dco, struct multi_context *m,
const bool raise_sigusr1_on_err)
{
/* Not implemented. */
return 0;
}

int
dco_get_peer_stats(struct context *c)
dco_get_peer_stats(struct context *c, const bool raise_sigusr1_on_err)
{
struct tuntap *tt = c->c1.tuntap;

Expand Down
3 changes: 1 addition & 2 deletions src/openvpn/forward.c
Original file line number Diff line number Diff line change
Expand Up @@ -488,7 +488,7 @@ check_add_routes(struct context *c)
static void
check_inactivity_timeout(struct context *c)
{
if (dco_enabled(&c->options) && dco_get_peer_stats(c) == 0)
if (dco_enabled(&c->options) && dco_get_peer_stats(c, true) == 0)
{
int64_t tot_bytes = c->c2.tun_read_bytes + c->c2.tun_write_bytes;
int64_t new_bytes = tot_bytes - c->c2.inactivity_bytes;
Expand All @@ -497,7 +497,6 @@ check_inactivity_timeout(struct context *c)
{
c->c2.inactivity_bytes = tot_bytes;
event_timeout_reset(&c->c2.inactivity_interval);

return;
}
}
Expand Down
10 changes: 8 additions & 2 deletions src/openvpn/manage.c
Original file line number Diff line number Diff line change
Expand Up @@ -4146,8 +4146,13 @@ management_check_bytecount(struct context *c, struct management *man, struct tim
counter_type dco_read_bytes = 0;
counter_type dco_write_bytes = 0;

if (dco_enabled(&c->options) && (dco_get_peer_stats(c) == 0))
if (dco_enabled(&c->options))
{
if (dco_get_peer_stats(c, true) < 0)
{
return;
}

dco_read_bytes = c->c2.dco_read_bytes;
dco_write_bytes = c->c2.dco_write_bytes;
}
Expand All @@ -4166,7 +4171,8 @@ management_check_bytecount(struct context *c, struct management *man, struct tim
void
man_persist_client_stats(struct management *man, struct context *c)
{
if (dco_enabled(&c->options) && (dco_get_peer_stats(c) == 0))
/* no need to raise SIGUSR1 since we are already closing the instance */
if (dco_enabled(&c->options) && (dco_get_peer_stats(c, false) == 0))
{
management_bytes_client(man, c->c2.dco_read_bytes, c->c2.dco_write_bytes);
}
Expand Down
10 changes: 8 additions & 2 deletions src/openvpn/multi.c
Original file line number Diff line number Diff line change
Expand Up @@ -548,7 +548,10 @@ setenv_stats(struct multi_context *m, struct context *c)
{
if (dco_enabled(&m->top.options))
{
dco_get_peer_stats_multi(&m->top.c1.tuntap->dco, m);
if (dco_get_peer_stats_multi(&m->top.c1.tuntap->dco, m, false) < 0)
{
return;
}
}

setenv_counter(c->c2.es, "bytes_received", c->c2.link_read_bytes + c->c2.dco_read_bytes);
Expand Down Expand Up @@ -856,7 +859,10 @@ multi_print_status(struct multi_context *m, struct status_output *so, const int

if (dco_enabled(&m->top.options))
{
dco_get_peer_stats_multi(&m->top.c1.tuntap->dco, m);
if (dco_get_peer_stats_multi(&m->top.c1.tuntap->dco, m, true) < 0)
{
return;
}
}

if (version == 1)
Expand Down
5 changes: 4 additions & 1 deletion src/openvpn/sig.c
Original file line number Diff line number Diff line change
Expand Up @@ -489,7 +489,10 @@ print_status(struct context *c, struct status_output *so)

if (dco_enabled(&c->options))
{
dco_get_peer_stats(c);
if (dco_get_peer_stats(c, true) < 0)
{
return;
}
}

status_printf(so, "OpenVPN STATISTICS");
Expand Down

0 comments on commit 6f9ba8b

Please sign in to comment.