From 31676f22655f45317d24486ab5f68ac9272b5dc5 Mon Sep 17 00:00:00 2001 From: Stefan Baranoff Date: Thu, 30 Oct 2025 17:29:44 +0000 Subject: [PATCH 1/4] dco: back-port a6996 As part of resolving #883 (async netlink message callback confusion on the 2.6 line of code) the changes applied to 2.7 have some major differences to 2.6 that need reconciled first. This back-ports one such necessary change. --- src/openvpn/dco.h | 8 ++++---- src/openvpn/dco_freebsd.c | 4 ++-- src/openvpn/dco_linux.c | 14 ++++++++++---- src/openvpn/dco_linux.h | 2 ++ src/openvpn/dco_win.c | 2 +- src/openvpn/init.c | 2 +- 6 files changed, 20 insertions(+), 12 deletions(-) diff --git a/src/openvpn/dco.h b/src/openvpn/dco.h index 50ebb3591fa..6be20062597 100644 --- a/src/openvpn/dco.h +++ b/src/openvpn/dco.h @@ -104,11 +104,10 @@ bool dco_check_pull_options(int msglevel, const struct options *o); /** * Initialize the DCO context * - * @param mode the instance operating mode (P2P or multi-peer) - * @param dco the context to initialize + * @param c the main instance context * @return true on success, false otherwise */ -bool ovpn_dco_init(int mode, dco_context_t *dco); +bool ovpn_dco_init(struct context *c); /** * Open/create a DCO interface @@ -169,6 +168,7 @@ int init_key_dco_bi(struct tls_multi *multi, struct key_state *ks, * recoverable and should reset the connection */ bool dco_update_keys(dco_context_t *dco, struct tls_multi *multi); + /** * Install a new peer in DCO - to be called by a CLIENT (or P2P) instance * @@ -284,7 +284,7 @@ dco_check_pull_options(int msglevel, const struct options *o) } static inline bool -ovpn_dco_init(int mode, dco_context_t *dco) +ovpn_dco_init(struct context *c) { return true; } diff --git a/src/openvpn/dco_freebsd.c b/src/openvpn/dco_freebsd.c index 13a08319a24..96b75a4d4af 100644 --- a/src/openvpn/dco_freebsd.c +++ b/src/openvpn/dco_freebsd.c @@ -220,9 +220,9 @@ close_fd(dco_context_t *dco) } bool -ovpn_dco_init(int mode, dco_context_t *dco) +ovpn_dco_init(struct context *c) { - if (open_fd(dco) < 0) + if (open_fd(&c->c1.tuntap->dco) < 0) { msg(M_ERR, "Failed to open socket"); return false; diff --git a/src/openvpn/dco_linux.c b/src/openvpn/dco_linux.c index b2584b973ce..e38d5b1a393 100644 --- a/src/openvpn/dco_linux.c +++ b/src/openvpn/dco_linux.c @@ -283,7 +283,7 @@ dco_new_peer(dco_context_t *dco, unsigned int peerid, int sd, } static int -ovpn_nl_cb_finish(struct nl_msg (*msg) __attribute__ ((unused)), void *arg) +ovpn_nl_cb_finish(struct nl_msg(*msg) __attribute__ ((unused)), void *arg) { int *status = arg; @@ -300,7 +300,7 @@ ovpn_nl_cb_finish(struct nl_msg (*msg) __attribute__ ((unused)), void *arg) * reply to see if it contains a human-readable error. If found, it is printed. */ static int -ovpn_nl_cb_error(struct sockaddr_nl (*nla) __attribute__ ((unused)), +ovpn_nl_cb_error(struct sockaddr_nl(*nla) __attribute__ ((unused)), struct nlmsgerr *err, void *arg) { struct nlmsghdr *nlh = (struct nlmsghdr *)err - 1; @@ -391,9 +391,11 @@ ovpn_dco_init_netlink(dco_context_t *dco) } bool -ovpn_dco_init(int mode, dco_context_t *dco) +ovpn_dco_init(struct context *c) { - switch (mode) + dco_context_t *dco = &c->c1.tuntap->dco; + + switch (c->mode) { case CM_TOP: dco->ifmode = OVPN_MODE_MP; @@ -406,6 +408,10 @@ ovpn_dco_init(int mode, dco_context_t *dco) default: ASSERT(false); } + /* store pointer to context as it may be required by message + * parsing routines + */ + dco->c = c; ovpn_dco_init_netlink(dco); return true; diff --git a/src/openvpn/dco_linux.h b/src/openvpn/dco_linux.h index 5179912b522..cf6bdd4a5dd 100644 --- a/src/openvpn/dco_linux.h +++ b/src/openvpn/dco_linux.h @@ -43,6 +43,8 @@ typedef struct struct nl_cb *nl_cb; int status; + struct context *c; + enum ovpn_mode ifmode; int ovpn_dco_id; diff --git a/src/openvpn/dco_win.c b/src/openvpn/dco_win.c index 0b8f8319de1..bc465db6c69 100644 --- a/src/openvpn/dco_win.c +++ b/src/openvpn/dco_win.c @@ -53,7 +53,7 @@ create_dco_handle(const char *devname, struct gc_arena *gc) } bool -ovpn_dco_init(int mode, dco_context_t *dco) +ovpn_dco_init(struct context *c) { return true; } diff --git a/src/openvpn/init.c b/src/openvpn/init.c index 1476737efbe..c2cfd240dbb 100644 --- a/src/openvpn/init.c +++ b/src/openvpn/init.c @@ -1882,7 +1882,7 @@ do_open_tun(struct context *c, int *error_flags) #endif if (dco_enabled(&c->options)) { - ovpn_dco_init(c->mode, &c->c1.tuntap->dco); + ovpn_dco_init(c); } /* open the tun device */ From 6711c5626b0c0c3723b9bdc25353c9cdfc27430f Mon Sep 17 00:00:00 2001 From: Stefan Baranoff Date: Thu, 30 Oct 2025 19:54:57 +0000 Subject: [PATCH 2/4] multi: back-port parts of 7f5a6 As part of resolving #883 (async netlink message callback confusion on the 2.6 line of code) the changes applied to 2.7 have some major differences to 2.6 that need reconciled first. This back-ports one such necessary change. --- src/openvpn/dco_linux.c | 134 ++++++++++++++++++++-------------------- src/openvpn/mtcp.c | 5 +- src/openvpn/mudp.c | 5 +- src/openvpn/multi.c | 9 +-- src/openvpn/multi.h | 4 +- src/openvpn/openvpn.h | 3 + 6 files changed, 83 insertions(+), 77 deletions(-) diff --git a/src/openvpn/dco_linux.c b/src/openvpn/dco_linux.c index e38d5b1a393..58dc90e52bf 100644 --- a/src/openvpn/dco_linux.c +++ b/src/openvpn/dco_linux.c @@ -725,6 +725,58 @@ ovpn_get_mcast_id(dco_context_t *dco) return ret; } +static void +dco_update_peer_stat(struct context_2 *c2, struct nlattr *tb[], uint32_t id) +{ + if (tb[OVPN_GET_PEER_RESP_ATTR_LINK_RX_BYTES]) + { + c2->dco_read_bytes = nla_get_u64(tb[OVPN_GET_PEER_RESP_ATTR_LINK_RX_BYTES]); + msg(D_DCO_DEBUG, "%s / dco_read_bytes: " counter_format, __func__, + c2->dco_read_bytes); + } + else + { + msg(M_WARN, "%s: no link RX bytes provided in reply for peer %u", + __func__, id); + } + + if (tb[OVPN_GET_PEER_RESP_ATTR_LINK_TX_BYTES]) + { + c2->dco_write_bytes = nla_get_u64(tb[OVPN_GET_PEER_RESP_ATTR_LINK_TX_BYTES]); + msg(D_DCO_DEBUG, "%s / dco_write_bytes: " counter_format, __func__, + c2->dco_write_bytes); + } + else + { + msg(M_WARN, "%s: no link TX bytes provided in reply for peer %u", + __func__, id); + } + + if (tb[OVPN_GET_PEER_RESP_ATTR_VPN_RX_BYTES]) + { + c2->tun_read_bytes = nla_get_u64(tb[OVPN_GET_PEER_RESP_ATTR_VPN_RX_BYTES]); + msg(D_DCO_DEBUG, "%s / tun_read_bytes: " counter_format, __func__, + c2->tun_read_bytes); + } + else + { + msg(M_WARN, "%s: no VPN RX bytes provided in reply for peer %u", + __func__, id); + } + + if (tb[OVPN_GET_PEER_RESP_ATTR_VPN_TX_BYTES]) + { + c2->tun_write_bytes = nla_get_u64(tb[OVPN_GET_PEER_RESP_ATTR_VPN_TX_BYTES]); + msg(D_DCO_DEBUG, "%s / tun_write_bytes: " counter_format, __func__, + c2->tun_write_bytes); + } + else + { + msg(M_WARN, "%s: no VPN TX bytes provided in reply for peer %u", + __func__, id); + } +} + /* This function parses any netlink message sent by ovpn-dco to userspace */ static int ovpn_handle_msg(struct nl_msg *msg, void *arg) @@ -831,58 +883,6 @@ dco_do_read(dco_context_t *dco) return ovpn_nl_recvmsgs(dco, __func__); } -static void -dco_update_peer_stat(struct context_2 *c2, struct nlattr *tb[], uint32_t id) -{ - if (tb[OVPN_GET_PEER_RESP_ATTR_LINK_RX_BYTES]) - { - c2->dco_read_bytes = nla_get_u64(tb[OVPN_GET_PEER_RESP_ATTR_LINK_RX_BYTES]); - msg(D_DCO_DEBUG, "%s / dco_read_bytes: " counter_format, __func__, - c2->dco_read_bytes); - } - else - { - msg(M_WARN, "%s: no link RX bytes provided in reply for peer %u", - __func__, id); - } - - if (tb[OVPN_GET_PEER_RESP_ATTR_LINK_TX_BYTES]) - { - c2->dco_write_bytes = nla_get_u64(tb[OVPN_GET_PEER_RESP_ATTR_LINK_TX_BYTES]); - msg(D_DCO_DEBUG, "%s / dco_write_bytes: " counter_format, __func__, - c2->dco_write_bytes); - } - else - { - msg(M_WARN, "%s: no link TX bytes provided in reply for peer %u", - __func__, id); - } - - if (tb[OVPN_GET_PEER_RESP_ATTR_VPN_RX_BYTES]) - { - c2->tun_read_bytes = nla_get_u64(tb[OVPN_GET_PEER_RESP_ATTR_VPN_RX_BYTES]); - msg(D_DCO_DEBUG, "%s / tun_read_bytes: " counter_format, __func__, - c2->tun_read_bytes); - } - else - { - msg(M_WARN, "%s: no VPN RX bytes provided in reply for peer %u", - __func__, id); - } - - if (tb[OVPN_GET_PEER_RESP_ATTR_VPN_TX_BYTES]) - { - c2->tun_write_bytes = nla_get_u64(tb[OVPN_GET_PEER_RESP_ATTR_VPN_TX_BYTES]); - msg(D_DCO_DEBUG, "%s / tun_write_bytes: " counter_format, __func__, - c2->tun_write_bytes); - } - else - { - msg(M_WARN, "%s: no VPN TX bytes provided in reply for peer %u", - __func__, id); - } -} - int dco_parse_peer_multi(struct nl_msg *msg, void *arg) { @@ -926,21 +926,6 @@ dco_parse_peer_multi(struct nl_msg *msg, void *arg) return NL_OK; } -int -dco_get_peer_stats_multi(dco_context_t *dco, struct multi_context *m) -{ - msg(D_DCO_DEBUG, "%s", __func__); - - struct nl_msg *nl_msg = ovpn_dco_nlmsg_create(dco, OVPN_CMD_GET_PEER); - - nlmsg_hdr(nl_msg)->nlmsg_flags |= NLM_F_DUMP; - - int ret = ovpn_nl_msg_send(dco, nl_msg, dco_parse_peer_multi, m, __func__); - - nlmsg_free(nl_msg); - return ret; -} - static int dco_parse_peer(struct nl_msg *msg, void *arg) { @@ -982,6 +967,21 @@ dco_parse_peer(struct nl_msg *msg, void *arg) return NL_OK; } +int +dco_get_peer_stats_multi(dco_context_t *dco, struct multi_context *m) +{ + msg(D_DCO_DEBUG, "%s", __func__); + + struct nl_msg *nl_msg = ovpn_dco_nlmsg_create(dco, OVPN_CMD_GET_PEER); + + nlmsg_hdr(nl_msg)->nlmsg_flags |= NLM_F_DUMP; + + int ret = ovpn_nl_msg_send(dco, nl_msg, dco_parse_peer_multi, m, __func__); + + nlmsg_free(nl_msg); + return ret; +} + int dco_get_peer_stats(struct context *c) { diff --git a/src/openvpn/mtcp.c b/src/openvpn/mtcp.c index 96408d11966..18ce9a518b0 100644 --- a/src/openvpn/mtcp.c +++ b/src/openvpn/mtcp.c @@ -792,6 +792,7 @@ tunnel_server_tcp(struct context *top) int status; top->mode = CM_TOP; + top->multi = &multi; context_clear_2(top); /* initialize top-tunnel instance */ @@ -802,10 +803,10 @@ tunnel_server_tcp(struct context *top) } /* initialize global multi_context object */ - multi_init(&multi, top, true); + multi_init(top, true); /* initialize our cloned top object */ - multi_top_init(&multi, top); + multi_top_init(top); /* initialize management interface */ init_management_callback_multi(&multi); diff --git a/src/openvpn/mudp.c b/src/openvpn/mudp.c index 0492311669c..b21fa1039f1 100644 --- a/src/openvpn/mudp.c +++ b/src/openvpn/mudp.c @@ -466,6 +466,7 @@ tunnel_server_udp(struct context *top) struct multi_context multi; top->mode = CM_TOP; + top->multi = &multi; context_clear_2(top); /* initialize top-tunnel instance */ @@ -476,10 +477,10 @@ tunnel_server_udp(struct context *top) } /* initialize global multi_context object */ - multi_init(&multi, top, false); + multi_init(top, false); /* initialize our cloned top object */ - multi_top_init(&multi, top); + multi_top_init(top); /* initialize management interface */ init_management_callback_multi(&multi); diff --git a/src/openvpn/multi.c b/src/openvpn/multi.c index 94e623ba22e..77f9649854f 100644 --- a/src/openvpn/multi.c +++ b/src/openvpn/multi.c @@ -289,8 +289,9 @@ int_compare_function(const void *key1, const void *key2) * Main initialization function, init multi_context object. */ void -multi_init(struct multi_context *m, struct context *t, bool tcp_mode) +multi_init(struct context *t, bool tcp_mode) { + struct multi_context *m = t->multi; int dev = DEV_TYPE_UNDEF; msg(D_MULTI_LOW, "MULTI: multi_init called, r=%d v=%d", @@ -3838,10 +3839,10 @@ multi_process_per_second_timers_dowork(struct multi_context *m) } void -multi_top_init(struct multi_context *m, struct context *top) +multi_top_init(struct context *top) { - inherit_context_top(&m->top, top); - m->top.c2.buffers = init_context_buffers(&top->c2.frame); + inherit_context_top(&top->multi->top, top); + top->multi->top.c2.buffers = init_context_buffers(&top->c2.frame); } void diff --git a/src/openvpn/multi.h b/src/openvpn/multi.h index 7167639e869..31433555712 100644 --- a/src/openvpn/multi.h +++ b/src/openvpn/multi.h @@ -260,11 +260,11 @@ const char *multi_instance_string(const struct multi_instance *mi, bool null, st * Called by mtcp.c, mudp.c, or other (to be written) protocol drivers */ -void multi_init(struct multi_context *m, struct context *t, bool tcp_mode); +void multi_init(struct context *t, bool tcp_mode); void multi_uninit(struct multi_context *m); -void multi_top_init(struct multi_context *m, struct context *top); +void multi_top_init(struct context *top); void multi_top_free(struct multi_context *m); diff --git a/src/openvpn/openvpn.h b/src/openvpn/openvpn.h index 9cba1c5adf3..3879772668d 100644 --- a/src/openvpn/openvpn.h +++ b/src/openvpn/openvpn.h @@ -492,6 +492,9 @@ struct context * CM_P2P, \c CM_TOP, \c CM_TOP_CLONE, * \c CM_CHILD_UDP, and \c CM_CHILD_TCP. */ + struct multi_context *multi; /**< Pointer to the main P2MP context. + * Non-NULL only when mode == CM_TOP. */ + struct gc_arena gc; /**< Garbage collection arena for * allocations done in the scope of this * context structure. */ From 3046702b7748d9290f5b16b9ab80f5cc736e9d1a Mon Sep 17 00:00:00 2001 From: Stefan Baranoff Date: Thu, 30 Oct 2025 19:03:54 +0000 Subject: [PATCH 3/4] dco_linux: back-port f353b As part of resolving #883 (async netlink message callback confusion on the 2.6 line of code) the changes applied to 2.7 have some major differences to 2.6 that need reconciled first. This back-ports one such necessary change. --- src/openvpn/dco_linux.c | 333 ++++++++++++++++++++++------------------ src/openvpn/dco_linux.h | 1 + 2 files changed, 188 insertions(+), 146 deletions(-) diff --git a/src/openvpn/dco_linux.c b/src/openvpn/dco_linux.c index 58dc90e52bf..246d52456d2 100644 --- a/src/openvpn/dco_linux.c +++ b/src/openvpn/dco_linux.c @@ -167,23 +167,19 @@ ovpn_nl_recvmsgs(dco_context_t *dco, const char *prefix) } /** - * Send a prepared netlink message and registers cb as callback if non-null. + * Send a prepared netlink message. * * The method will also free nl_msg * @param dco The dco context to use * @param nl_msg the message to use - * @param cb An optional callback if the caller expects an answer - * @param cb_arg An optional param to pass to the callback * @param prefix A prefix to report in the error message to give the user context * @return status of sending the message */ static int -ovpn_nl_msg_send(dco_context_t *dco, struct nl_msg *nl_msg, ovpn_nl_cb cb, - void *cb_arg, const char *prefix) +ovpn_nl_msg_send(dco_context_t *dco, struct nl_msg *nl_msg, const char *prefix) { dco->status = 1; - nl_cb_set(dco->nl_cb, NL_CB_VALID, NL_CB_CUSTOM, cb, cb_arg); nl_send_auto(dco->nl_sock, nl_msg); while (dco->status == 1) @@ -274,7 +270,7 @@ dco_new_peer(dco_context_t *dco, unsigned int peerid, int sd, } nla_nest_end(nl_msg, attr); - ret = ovpn_nl_msg_send(dco, nl_msg, NULL, NULL, __func__); + ret = ovpn_nl_msg_send(dco, nl_msg, __func__); nla_put_failure: nlmsg_free(nl_msg); @@ -342,6 +338,29 @@ ovpn_nl_cb_error(struct sockaddr_nl(*nla) __attribute__ ((unused)), return NL_STOP; } +static void +ovpn_dco_register(dco_context_t *dco) +{ + msg(D_DCO_DEBUG, __func__); + ovpn_get_mcast_id(dco); + + if (dco->ovpn_dco_mcast_id < 0) + { + msg(M_FATAL, "cannot get mcast group: %s", nl_geterror(dco->ovpn_dco_mcast_id)); + } + + /* Register for ovpn-dco specific multicast messages that the kernel may + * send + */ + int ret = nl_socket_add_membership(dco->nl_sock, dco->ovpn_dco_mcast_id); + if (ret) + { + msg(M_FATAL, "%s: failed to join groups: %d", __func__, ret); + } +} + +static int ovpn_handle_msg(struct nl_msg *msg, void *arg); + static void ovpn_dco_init_netlink(dco_context_t *dco) { @@ -373,11 +392,15 @@ ovpn_dco_init_netlink(dco_context_t *dco) nl_socket_set_cb(dco->nl_sock, dco->nl_cb); + dco->dco_message_peer_id = -1; nl_cb_err(dco->nl_cb, NL_CB_CUSTOM, ovpn_nl_cb_error, &dco->status); nl_cb_set(dco->nl_cb, NL_CB_FINISH, NL_CB_CUSTOM, ovpn_nl_cb_finish, &dco->status); nl_cb_set(dco->nl_cb, NL_CB_ACK, NL_CB_CUSTOM, ovpn_nl_cb_finish, &dco->status); + nl_cb_set(dco->nl_cb, NL_CB_VALID, NL_CB_CUSTOM, ovpn_handle_msg, dco); + + ovpn_dco_register(dco); /* The async PACKET messages confuse libnl and it will drop them with * wrong sequence numbers (NLE_SEQ_MISMATCH), so disable libnl's sequence @@ -429,27 +452,6 @@ ovpn_dco_uninit_netlink(dco_context_t *dco) CLEAR(dco); } -static void -ovpn_dco_register(dco_context_t *dco) -{ - msg(D_DCO_DEBUG, __func__); - ovpn_get_mcast_id(dco); - - if (dco->ovpn_dco_mcast_id < 0) - { - msg(M_ERR, "cannot get mcast group: %s", nl_geterror(dco->ovpn_dco_mcast_id)); - } - - /* Register for ovpn-dco specific multicast messages that the kernel may - * send - */ - int ret = nl_socket_add_membership(dco->nl_sock, dco->ovpn_dco_mcast_id); - if (ret) - { - msg(M_ERR, "%s: failed to join groups: %d", __func__, ret); - } -} - int open_tun_dco(struct tuntap *tt, openvpn_net_ctx_t *ctx, const char *dev) { @@ -469,10 +471,6 @@ open_tun_dco(struct tuntap *tt, openvpn_net_ctx_t *ctx, const char *dev) msg(M_FATAL, "DCO: cannot retrieve ifindex for interface %s", dev); } - tt->dco.dco_message_peer_id = -1; - - ovpn_dco_register(&tt->dco); - return 0; } @@ -501,7 +499,7 @@ dco_swap_keys(dco_context_t *dco, unsigned int peerid) NLA_PUT_U32(nl_msg, OVPN_SWAP_KEYS_ATTR_PEER_ID, peerid); nla_nest_end(nl_msg, attr); - ret = ovpn_nl_msg_send(dco, nl_msg, NULL, NULL, __func__); + ret = ovpn_nl_msg_send(dco, nl_msg, __func__); nla_put_failure: nlmsg_free(nl_msg); @@ -525,7 +523,7 @@ dco_del_peer(dco_context_t *dco, unsigned int peerid) NLA_PUT_U32(nl_msg, OVPN_DEL_PEER_ATTR_PEER_ID, peerid); nla_nest_end(nl_msg, attr); - ret = ovpn_nl_msg_send(dco, nl_msg, NULL, NULL, __func__); + ret = ovpn_nl_msg_send(dco, nl_msg, __func__); nla_put_failure: nlmsg_free(nl_msg); @@ -551,7 +549,7 @@ dco_del_key(dco_context_t *dco, unsigned int peerid, NLA_PUT_U8(nl_msg, OVPN_DEL_KEY_ATTR_KEY_SLOT, slot); nla_nest_end(nl_msg, attr); - ret = ovpn_nl_msg_send(dco, nl_msg, NULL, NULL, __func__); + ret = ovpn_nl_msg_send(dco, nl_msg, __func__); nla_put_failure: nlmsg_free(nl_msg); @@ -608,7 +606,7 @@ dco_new_key(dco_context_t *dco, unsigned int peerid, int keyid, nla_nest_end(nl_msg, attr); - ret = ovpn_nl_msg_send(dco, nl_msg, NULL, NULL, __func__); + ret = ovpn_nl_msg_send(dco, nl_msg, __func__); nla_put_failure: nlmsg_free(nl_msg); @@ -637,7 +635,7 @@ dco_set_peer(dco_context_t *dco, unsigned int peerid, keepalive_timeout); nla_nest_end(nl_msg, attr); - ret = ovpn_nl_msg_send(dco, nl_msg, NULL, NULL, __func__); + ret = ovpn_nl_msg_send(dco, nl_msg, __func__); nla_put_failure: nlmsg_free(nl_msg); @@ -705,7 +703,7 @@ ovpn_get_mcast_id(dco_context_t *dco) /* Even though 'nlctrl' is a constant, there seem to be no library * provided define for it */ - int ctrlid = genl_ctrl_resolve(dco->nl_sock, "nlctrl"); + dco->ctrlid = genl_ctrl_resolve(dco->nl_sock, "nlctrl"); struct nl_msg *nl_msg = nlmsg_alloc(); if (!nl_msg) @@ -713,12 +711,12 @@ ovpn_get_mcast_id(dco_context_t *dco) return -ENOMEM; } - genlmsg_put(nl_msg, 0, 0, ctrlid, 0, 0, CTRL_CMD_GETFAMILY, 0); + genlmsg_put(nl_msg, 0, 0, dco->ctrlid, 0, 0, CTRL_CMD_GETFAMILY, 0); int ret = -EMSGSIZE; NLA_PUT_STRING(nl_msg, CTRL_ATTR_FAMILY_NAME, OVPN_NL_NAME); - ret = ovpn_nl_msg_send(dco, nl_msg, mcast_family_handler, dco, __func__); + ret = ovpn_nl_msg_send(dco, nl_msg, __func__); nla_put_failure: nlmsg_free(nl_msg); @@ -777,15 +775,129 @@ dco_update_peer_stat(struct context_2 *c2, struct nlattr *tb[], uint32_t id) } } +static int +ovpn_handle_peer_multi(dco_context_t *dco, struct nlattr *attrs[]) +{ + msg(D_DCO_DEBUG, "%s: parsing message...", __func__); + + /* this function assumes openvpn is running in multipeer mode as + * it accesses c->multi + */ + if (dco->ifmode != OVPN_MODE_MP) + { + msg(M_WARN, "%s: can't parse 'multi-peer' message on P2P instance", __func__); + return NL_SKIP; + } + + if (!attrs[OVPN_ATTR_GET_PEER]) + { + return NL_SKIP; + } + + struct nlattr *tb_peer[OVPN_GET_PEER_RESP_ATTR_MAX + 1]; + + nla_parse(tb_peer, OVPN_GET_PEER_RESP_ATTR_MAX, + nla_data(attrs[OVPN_ATTR_GET_PEER]), + nla_len(attrs[OVPN_ATTR_GET_PEER]), NULL); + + if (!tb_peer[OVPN_GET_PEER_RESP_ATTR_PEER_ID]) + { + msg(M_WARN, "ovpn-dco: no peer-id provided in (MULTI) PEER_GET reply"); + return NL_SKIP; + } + + struct multi_context *m = dco->c->multi; + uint32_t peer_id = nla_get_u32(tb_peer[OVPN_GET_PEER_RESP_ATTR_PEER_ID]); + + if (peer_id >= m->max_clients || !m->instances[peer_id]) + { + msg(M_WARN, "%s: cannot store DCO stats for peer %u", __func__, + peer_id); + return NL_SKIP; + } + + dco_update_peer_stat(&m->instances[peer_id]->context.c2, tb_peer, peer_id); + + return NL_OK; +} + +static int +ovpn_handle_peer(dco_context_t *dco, struct nlattr *attrs[]) +{ + msg(D_DCO_DEBUG, "%s: parsing message...", __func__); + + if (!attrs[OVPN_ATTR_GET_PEER]) + { + msg(D_DCO_DEBUG, "%s: malformed reply", __func__); + return NL_SKIP; + } + + struct nlattr *tb_peer[OVPN_GET_PEER_RESP_ATTR_MAX + 1]; + + nla_parse(tb_peer, OVPN_GET_PEER_RESP_ATTR_MAX, + nla_data(attrs[OVPN_ATTR_GET_PEER]), + nla_len(attrs[OVPN_ATTR_GET_PEER]), NULL); + + if (!tb_peer[OVPN_GET_PEER_RESP_ATTR_PEER_ID]) + { + msg(M_WARN, "ovpn-dco: no peer-id provided in PEER_GET reply"); + return NL_SKIP; + } + + uint32_t peer_id = nla_get_u32(tb_peer[OVPN_GET_PEER_RESP_ATTR_PEER_ID]); + struct context_2 *c2; + + if (dco->ifmode == OVPN_MODE_P2P) + { + c2 = &dco->c->c2; + } + else + { + struct multi_instance *mi = dco->c->multi->instances[peer_id]; + if (!mi) + { + msg(M_WARN, "%s: received data for a non-existing peer %u", __func__, peer_id); + return NL_SKIP; + } + + c2 = &mi->context.c2; + } + + /* at this point this check should never fail for MP mode, + * but it's still fully valid for P2P mode + */ + if (c2->tls_multi->dco_peer_id != peer_id) + { + return NL_SKIP; + } + + dco_update_peer_stat(c2, tb_peer, peer_id); + + return NL_OK; +} + /* This function parses any netlink message sent by ovpn-dco to userspace */ static int ovpn_handle_msg(struct nl_msg *msg, void *arg) { dco_context_t *dco = arg; - struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); struct nlattr *attrs[OVPN_ATTR_MAX + 1]; struct nlmsghdr *nlh = nlmsg_hdr(msg); + struct genlmsghdr *gnlh = genlmsg_hdr(nlh); + + msg(D_DCO_DEBUG, "ovpn-dco: received netlink message type=%u cmd=%u flags=%#.4x", + nlh->nlmsg_type, gnlh->cmd, nlh->nlmsg_flags); + + /* if we get a message from the NLCTRL family, it means + * this is the reply to the mcast ID resolution request + * and we parse it accordingly. + */ + if (nlh->nlmsg_type == dco->ctrlid) + { + msg(D_DCO_DEBUG, "ovpn-dco: received CTRLID message"); + return mcast_family_handler(msg, dco); + } if (!genlmsg_valid_hdr(nlh, 0)) { @@ -800,24 +912,6 @@ ovpn_handle_msg(struct nl_msg *msg, void *arg) return NL_SKIP; } - /* we must know which interface this message is referring to in order to - * avoid mixing messages for other instances - */ - if (!attrs[OVPN_ATTR_IFINDEX]) - { - msg(D_DCO, "ovpn-dco: Received message without ifindex"); - return NL_SKIP; - } - - uint32_t ifindex = nla_get_u32(attrs[OVPN_ATTR_IFINDEX]); - if (ifindex != dco->ifindex) - { - msg(D_DCO_DEBUG, - "ovpn-dco: ignoring message (type=%d) for foreign ifindex %d", - gnlh->cmd, ifindex); - return NL_SKIP; - } - /* based on the message type, we parse the subobject contained in the * message, that stores the type-specific attributes. * @@ -827,8 +921,40 @@ ovpn_handle_msg(struct nl_msg *msg, void *arg) */ switch (gnlh->cmd) { + case OVPN_CMD_GET_PEER: + { + /* this message is part of a peer list dump, hence triggered + * by a MP/server instance + */ + if (nlh->nlmsg_flags & NLM_F_MULTI) + { + return ovpn_handle_peer_multi(dco, attrs); + } + else + { + return ovpn_handle_peer(dco, attrs); + } + } + case OVPN_CMD_DEL_PEER: { + /* we must know which interface this message is referring to in order to + * avoid mixing messages for other instances + */ + if (!attrs[OVPN_ATTR_IFINDEX]) + { + msg(D_DCO, "ovpn-dco: Received message without ifindex"); + return NL_SKIP; + } + + uint32_t ifindex = nla_get_u32(attrs[OVPN_ATTR_IFINDEX]); + if (ifindex != dco->ifindex) + { + msg(D_DCO_DEBUG, + "ovpn-dco: ignoring message (type=%d) for foreign ifindex %d", + gnlh->cmd, ifindex); + return NL_SKIP; + } if (!attrs[OVPN_ATTR_DEL_PEER]) { msg(D_DCO, "ovpn-dco: no attributes in OVPN_DEL_PEER message"); @@ -878,95 +1004,10 @@ int dco_do_read(dco_context_t *dco) { msg(D_DCO_DEBUG, __func__); - nl_cb_set(dco->nl_cb, NL_CB_VALID, NL_CB_CUSTOM, ovpn_handle_msg, dco); return ovpn_nl_recvmsgs(dco, __func__); } -int -dco_parse_peer_multi(struct nl_msg *msg, void *arg) -{ - struct nlattr *tb[OVPN_ATTR_MAX + 1]; - struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); - - msg(D_DCO_DEBUG, "%s: parsing message...", __func__); - - nla_parse(tb, OVPN_ATTR_MAX, genlmsg_attrdata(gnlh, 0), - genlmsg_attrlen(gnlh, 0), NULL); - - if (!tb[OVPN_ATTR_GET_PEER]) - { - return NL_SKIP; - } - - struct nlattr *tb_peer[OVPN_GET_PEER_RESP_ATTR_MAX + 1]; - - nla_parse(tb_peer, OVPN_GET_PEER_RESP_ATTR_MAX, - nla_data(tb[OVPN_ATTR_GET_PEER]), - nla_len(tb[OVPN_ATTR_GET_PEER]), NULL); - - if (!tb_peer[OVPN_GET_PEER_RESP_ATTR_PEER_ID]) - { - msg(M_WARN, "%s: no peer-id provided in reply", __func__); - return NL_SKIP; - } - - struct multi_context *m = arg; - uint32_t peer_id = nla_get_u32(tb_peer[OVPN_GET_PEER_RESP_ATTR_PEER_ID]); - - if (peer_id >= m->max_clients || !m->instances[peer_id]) - { - msg(M_WARN, "%s: cannot store DCO stats for peer %u", __func__, - peer_id); - return NL_SKIP; - } - - dco_update_peer_stat(&m->instances[peer_id]->context.c2, tb_peer, peer_id); - - return NL_OK; -} - -static int -dco_parse_peer(struct nl_msg *msg, void *arg) -{ - struct context *c = arg; - struct nlattr *tb[OVPN_ATTR_MAX + 1]; - struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); - - msg(D_DCO_DEBUG, "%s: parsing message...", __func__); - - nla_parse(tb, OVPN_ATTR_MAX, genlmsg_attrdata(gnlh, 0), - genlmsg_attrlen(gnlh, 0), NULL); - - if (!tb[OVPN_ATTR_GET_PEER]) - { - msg(D_DCO_DEBUG, "%s: malformed reply", __func__); - return NL_SKIP; - } - - struct nlattr *tb_peer[OVPN_GET_PEER_RESP_ATTR_MAX + 1]; - - nla_parse(tb_peer, OVPN_GET_PEER_RESP_ATTR_MAX, - nla_data(tb[OVPN_ATTR_GET_PEER]), - nla_len(tb[OVPN_ATTR_GET_PEER]), NULL); - - if (!tb_peer[OVPN_GET_PEER_RESP_ATTR_PEER_ID]) - { - msg(M_WARN, "%s: no peer-id provided in reply", __func__); - return NL_SKIP; - } - - uint32_t peer_id = nla_get_u32(tb_peer[OVPN_GET_PEER_RESP_ATTR_PEER_ID]); - if (c->c2.tls_multi->dco_peer_id != peer_id) - { - return NL_SKIP; - } - - dco_update_peer_stat(&c->c2, tb_peer, peer_id); - - return NL_OK; -} - int dco_get_peer_stats_multi(dco_context_t *dco, struct multi_context *m) { @@ -976,7 +1017,7 @@ dco_get_peer_stats_multi(dco_context_t *dco, struct multi_context *m) nlmsg_hdr(nl_msg)->nlmsg_flags |= NLM_F_DUMP; - int ret = ovpn_nl_msg_send(dco, nl_msg, dco_parse_peer_multi, m, __func__); + int ret = ovpn_nl_msg_send(dco, nl_msg, __func__); nlmsg_free(nl_msg); return ret; @@ -1001,7 +1042,7 @@ dco_get_peer_stats(struct context *c) NLA_PUT_U32(nl_msg, OVPN_GET_PEER_ATTR_PEER_ID, peer_id); nla_nest_end(nl_msg, attr); - ret = ovpn_nl_msg_send(dco, nl_msg, dco_parse_peer, c, __func__); + ret = ovpn_nl_msg_send(dco, nl_msg, __func__); nla_put_failure: nlmsg_free(nl_msg); diff --git a/src/openvpn/dco_linux.h b/src/openvpn/dco_linux.h index cf6bdd4a5dd..789954d6973 100644 --- a/src/openvpn/dco_linux.h +++ b/src/openvpn/dco_linux.h @@ -44,6 +44,7 @@ typedef struct int status; struct context *c; + int ctrlid; enum ovpn_mode ifmode; From 578935734ccf0ab8ecc2061230659dd3961da3b2 Mon Sep 17 00:00:00 2001 From: Stefan Baranoff Date: Sun, 2 Nov 2025 14:39:17 +0000 Subject: [PATCH 4/4] apply pending uncrustify changes since v2.6.15 and this patch set Starting with v2.6.15 tehre were uncrustify changes that needed applied; then there were also differences in dev environments: * Ubuntu 22.04 has 0.72.0_f * Alma 9 has 0.75.1_f and that caused output deltas from uncrustify; so reconcile those to make the build system happy. --- src/openvpn/dco_linux.c | 4 ++-- src/openvpn/error.h | 1 + src/openvpn/reflect_filter.h | 1 + src/openvpn/tun.h | 1 + src/openvpn/xkey_provider.c | 10 +++++----- 5 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/openvpn/dco_linux.c b/src/openvpn/dco_linux.c index 246d52456d2..45564ac8298 100644 --- a/src/openvpn/dco_linux.c +++ b/src/openvpn/dco_linux.c @@ -279,7 +279,7 @@ dco_new_peer(dco_context_t *dco, unsigned int peerid, int sd, } static int -ovpn_nl_cb_finish(struct nl_msg(*msg) __attribute__ ((unused)), void *arg) +ovpn_nl_cb_finish(struct nl_msg (*msg) __attribute__ ((unused)), void *arg) { int *status = arg; @@ -296,7 +296,7 @@ ovpn_nl_cb_finish(struct nl_msg(*msg) __attribute__ ((unused)), void *arg) * reply to see if it contains a human-readable error. If found, it is printed. */ static int -ovpn_nl_cb_error(struct sockaddr_nl(*nla) __attribute__ ((unused)), +ovpn_nl_cb_error(struct sockaddr_nl (*nla) __attribute__ ((unused)), struct nlmsgerr *err, void *arg) { struct nlmsghdr *nlh = (struct nlmsghdr *)err - 1; diff --git a/src/openvpn/error.h b/src/openvpn/error.h index ab2872aedce..efe0f4facdc 100644 --- a/src/openvpn/error.h +++ b/src/openvpn/error.h @@ -71,6 +71,7 @@ struct gc_arena; #ifdef _WIN32 #define openvpn_errno() GetLastError() const char *strerror_win32(DWORD errnum, struct gc_arena *gc); + #else #define openvpn_errno() errno #endif diff --git a/src/openvpn/reflect_filter.h b/src/openvpn/reflect_filter.h index 12eb0a15116..a6107671ff4 100644 --- a/src/openvpn/reflect_filter.h +++ b/src/openvpn/reflect_filter.h @@ -72,4 +72,5 @@ initial_rate_limit_init(int max_per_period, int period_length); * free the initial-packet rate limiter structure */ void initial_rate_limit_free(struct initial_packet_rate_limit *irl); + #endif /* ifndef REFLECT_FILTER_H */ diff --git a/src/openvpn/tun.h b/src/openvpn/tun.h index 91dbeefe000..4e39aef3dca 100644 --- a/src/openvpn/tun.h +++ b/src/openvpn/tun.h @@ -755,6 +755,7 @@ tun_set(struct tuntap *tt, } const char *tun_stat(const struct tuntap *tt, unsigned int rwflags, struct gc_arena *gc); + bool tun_name_is_fixed(const char *dev); static inline bool diff --git a/src/openvpn/xkey_provider.c b/src/openvpn/xkey_provider.c index f69239dde4e..1a75262b76c 100644 --- a/src/openvpn/xkey_provider.c +++ b/src/openvpn/xkey_provider.c @@ -55,7 +55,7 @@ typedef struct do { \ dmsg(f|M_NOLF, "xkey_provider: In %s: ", __func__); \ dmsg(f|M_NOPREFIX, __VA_ARGS__); \ - } while(0) + } while (0) typedef enum { @@ -597,7 +597,7 @@ static const OSSL_DISPATCH rsa_keymgmt_functions[] = { {OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS, (void (*)(void))keymgmt_gettable_params}, {OSSL_FUNC_KEYMGMT_GET_PARAMS, (void (*)(void))keymgmt_get_params}, {OSSL_FUNC_KEYMGMT_SET_PARAMS, (void (*)(void))keymgmt_set_params}, - {OSSL_FUNC_KEYMGMT_SETTABLE_PARAMS, (void (*)(void))keymgmt_gettable_params}, /* same as gettable */ + {OSSL_FUNC_KEYMGMT_SETTABLE_PARAMS, (void (*)(void))keymgmt_gettable_params}, /* same as gettable */ {OSSL_FUNC_KEYMGMT_QUERY_OPERATION_NAME, (void (*)(void))rsa_keymgmt_name}, {0, NULL } }; @@ -613,7 +613,7 @@ static const OSSL_DISPATCH ec_keymgmt_functions[] = { {OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS, (void (*)(void))keymgmt_gettable_params}, {OSSL_FUNC_KEYMGMT_GET_PARAMS, (void (*)(void))keymgmt_get_params}, {OSSL_FUNC_KEYMGMT_SET_PARAMS, (void (*)(void))keymgmt_set_params}, - {OSSL_FUNC_KEYMGMT_SETTABLE_PARAMS, (void (*)(void))keymgmt_gettable_params}, /* same as gettable */ + {OSSL_FUNC_KEYMGMT_SETTABLE_PARAMS, (void (*)(void))keymgmt_gettable_params}, /* same as gettable */ {OSSL_FUNC_KEYMGMT_QUERY_OPERATION_NAME, (void (*)(void))ec_keymgmt_name}, {0, NULL } }; @@ -629,7 +629,7 @@ static const OSSL_DISPATCH ed448_keymgmt_functions[] = { {OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS, (void (*)(void))keymgmt_gettable_params}, {OSSL_FUNC_KEYMGMT_GET_PARAMS, (void (*)(void))keymgmt_get_params}, {OSSL_FUNC_KEYMGMT_SET_PARAMS, (void (*)(void))keymgmt_set_params}, - {OSSL_FUNC_KEYMGMT_SETTABLE_PARAMS, (void (*)(void))keymgmt_gettable_params}, /* same as gettable */ + {OSSL_FUNC_KEYMGMT_SETTABLE_PARAMS, (void (*)(void))keymgmt_gettable_params}, /* same as gettable */ {0, NULL } }; @@ -644,7 +644,7 @@ static const OSSL_DISPATCH ed25519_keymgmt_functions[] = { {OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS, (void (*)(void))keymgmt_gettable_params}, {OSSL_FUNC_KEYMGMT_GET_PARAMS, (void (*)(void))keymgmt_get_params}, {OSSL_FUNC_KEYMGMT_SET_PARAMS, (void (*)(void))keymgmt_set_params}, - {OSSL_FUNC_KEYMGMT_SETTABLE_PARAMS, (void (*)(void))keymgmt_gettable_params}, /* same as gettable */ + {OSSL_FUNC_KEYMGMT_SETTABLE_PARAMS, (void (*)(void))keymgmt_gettable_params}, /* same as gettable */ {0, NULL } };