Browse code

dco: introduce low-level code for handling ovpn-dco in the Linux kernel

Signed-off-by: Antonio Quartulli <a@unstable.cc>
Acked-by: Arne Schwabe <arne@rfc2549.org>
Message-Id: <20220624083809.23487-2-a@unstable.cc>
URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg24512.html
Signed-off-by: Gert Doering <gert@greenie.muc.de>

Antonio Quartulli authored on 2022/06/24 17:37:45
Showing 12 changed files
... ...
@@ -143,6 +143,13 @@ AC_ARG_ENABLE(
143 143
 )
144 144
 
145 145
 AC_ARG_ENABLE(
146
+	[dco],
147
+	[AS_HELP_STRING([--enable-dco], [enable data channel offload support using ovpn-dco kernel module @<:@default=no@:>@])],
148
+	,
149
+	[enable_dco="no"]
150
+)
151
+
152
+AC_ARG_ENABLE(
146 153
 	[iproute2],
147 154
 	[AS_HELP_STRING([--enable-iproute2], [enable support for iproute2 @<:@default=no@:>@])],
148 155
 	,
... ...
@@ -760,6 +767,32 @@ PKG_CHECK_MODULES(
760 760
 	[]
761 761
 )
762 762
 
763
+
764
+if test "$enable_dco" = "yes"; then
765
+dnl
766
+dnl Include generic netlink library used to talk to ovpn-dco
767
+dnl
768
+
769
+	case "$host" in
770
+		*-*-linux*)
771
+			PKG_CHECK_MODULES([LIBNL_GENL],
772
+					  [libnl-genl-3.0 >= 3.4.0],
773
+					  [have_libnl="yes"],
774
+					  [AC_MSG_ERROR([libnl-genl-3.0 package not found or too old. Is the development package and pkg-config installed? Must be version 3.4.0 or newer])]
775
+			)
776
+
777
+			CFLAGS="${CFLAGS} ${LIBNL_GENL_CFLAGS}"
778
+			LIBS="${LIBS} ${LIBNL_GENL_LIBS}"
779
+
780
+			AC_DEFINE(ENABLE_DCO, 1, [Enable shared data channel offload])
781
+			AC_MSG_NOTICE([Enabled ovpn-dco support for Linux])
782
+			;;
783
+		*)
784
+			AC_MSG_NOTICE([Ignoring --enable-dco on non Linux platform])
785
+			;;
786
+	esac
787
+fi
788
+
763 789
 if test "${with_crypto_library}" = "openssl"; then
764 790
 	AC_ARG_VAR([OPENSSL_CFLAGS], [C compiler flags for OpenSSL])
765 791
 	AC_ARG_VAR([OPENSSL_LIBS], [linker flags for OpenSSL])
... ...
@@ -1196,6 +1229,7 @@ fi
1196 1196
 AM_CONDITIONAL([HAVE_SITNL], [false])
1197 1197
 
1198 1198
 if test "${enable_iproute2}" = "yes"; then
1199
+	test "${enable_dco}" = "yes" && AC_MSG_ERROR([iproute2 support cannot be enabled when using DCO])
1199 1200
 	test -z "${IPROUTE}" && AC_MSG_ERROR([ip utility is required but missing])
1200 1201
 	AC_DEFINE([ENABLE_IPROUTE], [1], [enable iproute2 support])
1201 1202
 else if test "${have_sitnl}" = "yes"; then
... ...
@@ -1,3 +1,4 @@
1 1
 E:doc/doxygen/doc_key_generation.h     # @verbatim section gets mistreated, exclude it
2 2
 E:src/compat/compat-lz4.c              # Preserve LZ4 upstream formatting
3 3
 E:src/compat/compat-lz4.h              # Preserve LZ4 upstream formatting
4
+E:src/openvpn/ovpn_dco_linux.h         # Preserve ovpn-dco upstream formatting
... ...
@@ -53,6 +53,8 @@ openvpn_SOURCES = \
53 53
 	crypto.c crypto.h crypto_backend.h \
54 54
 	crypto_openssl.c crypto_openssl.h \
55 55
 	crypto_mbedtls.c crypto_mbedtls.h \
56
+	dco.h dco_internal.h \
57
+	dco_linux.c dco_linux.h \
56 58
 	dhcp.c dhcp.h \
57 59
 	dns.c dns.h \
58 60
 	env_set.c env_set.h \
... ...
@@ -75,6 +77,7 @@ openvpn_SOURCES = \
75 75
 	mbuf.c mbuf.h \
76 76
 	memdbg.h \
77 77
 	misc.c misc.h \
78
+	ovpn_dco_linux.h \
78 79
 	platform.c platform.h \
79 80
 	console.c console.h console_builtin.c console_systemd.c \
80 81
 	mroute.c mroute.h \
81 82
new file mode 100644
... ...
@@ -0,0 +1,165 @@
0
+/*
1
+ *  OpenVPN -- An application to securely tunnel IP networks
2
+ *             over a single TCP/UDP 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) 2021-2022 Arne Schwabe <arne@rfc2549.org>
8
+ *  Copyright (C) 2021-2022 Antonio Quartulli <a@unstable.cc>
9
+ *  Copyright (C) 2021-2022 OpenVPN Inc <sales@openvpn.net>
10
+ *
11
+ *  This program is free software; you can redistribute it and/or modify
12
+ *  it under the terms of the GNU General Public License version 2
13
+ *  as published by the Free Software Foundation.
14
+ *
15
+ *  This program is distributed in the hope that it will be useful,
16
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
+ *  GNU General Public License for more details.
19
+ *
20
+ *  You should have received a copy of the GNU General Public License
21
+ *  along with this program (see the file COPYING included with this
22
+ *  distribution); if not, write to the Free Software Foundation, Inc.,
23
+ *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
24
+ */
25
+#ifndef DCO_H
26
+#define DCO_H
27
+
28
+#include "buffer.h"
29
+#include "error.h"
30
+#include "dco_internal.h"
31
+#include "networking.h"
32
+
33
+/* forward declarations (including other headers leads to nasty include
34
+ * order problems)
35
+ */
36
+struct event_set;
37
+struct options;
38
+struct tuntap;
39
+
40
+#if defined(ENABLE_DCO)
41
+
42
+/**
43
+ * Check whether ovpn-dco is available on this platform (i.e. kernel support is
44
+ * there)
45
+ *
46
+ * @param msglevel      level to print messages to
47
+ * @return              true if ovpn-dco is available, false otherwise
48
+ */
49
+bool dco_available(int msglevel);
50
+
51
+/**
52
+ * Check whether the options struct has any option that is not supported by
53
+ * our current dco implementation. If so print a warning at warning level
54
+ * for the first conflicting option found and return false.
55
+ *
56
+ * @param msglevel  the msg level to use to print the warnings
57
+ * @param o         the options struct that hold the options
58
+ * @return          true if no conflict was detected, false otherwise
59
+ */
60
+bool dco_check_option_conflict(int msglevel, const struct options *o);
61
+
62
+/**
63
+ * Initialize the DCO context
64
+ *
65
+ * @param mode      the instance operating mode (P2P or multi-peer)
66
+ * @param dco       the context to initialize
67
+ * @return          true on success, false otherwise
68
+ */
69
+bool ovpn_dco_init(int mode, dco_context_t *dco);
70
+
71
+/**
72
+ * Open/create a DCO interface
73
+ *
74
+ * @param tt        the tuntap context
75
+ * @param ctx       the networking API context
76
+ * @param dev       the name of the interface to create
77
+ * @return          0 on success or a negative error code otherwise
78
+ */
79
+int open_tun_dco(struct tuntap *tt, openvpn_net_ctx_t *ctx, const char *dev);
80
+
81
+/**
82
+ * Close/destroy a DCO interface
83
+ *
84
+ * @param tt        the tuntap context
85
+ * @param ctx       the networking API context
86
+ */
87
+void close_tun_dco(struct tuntap *tt, openvpn_net_ctx_t *ctx);
88
+
89
+/**
90
+ * Read data from the DCO communication channel (i.e. a control packet)
91
+ *
92
+ * @param dco       the DCO context
93
+ * @return          0 on success or a negative error code otherwise
94
+ */
95
+int dco_do_read(dco_context_t *dco);
96
+
97
+/**
98
+ * Write data to the DCO communication channel (control packet expected)
99
+ *
100
+ * @param dco       the DCO context
101
+ * @param peer_id   the ID of the peer to send the data to
102
+ * @param buf       the buffer containing the data to send
103
+ */
104
+int dco_do_write(dco_context_t *dco, int peer_id, struct buffer *buf);
105
+
106
+/**
107
+ * Install a DCO in the main event loop
108
+ */
109
+void dco_event_set(dco_context_t *dco, struct event_set *es, void *arg);
110
+
111
+#else /* if defined(ENABLE_DCO) */
112
+
113
+typedef void *dco_context_t;
114
+
115
+static inline bool
116
+dco_available(int msglevel)
117
+{
118
+    return false;
119
+}
120
+
121
+static inline bool
122
+dco_check_option_conflict(int msglevel, const struct options *o)
123
+{
124
+    return false;
125
+}
126
+
127
+static inline bool
128
+ovpn_dco_init(int mode, dco_context_t *dco)
129
+{
130
+    return true;
131
+}
132
+
133
+static inline int
134
+open_tun_dco(struct tuntap *tt, openvpn_net_ctx_t *ctx, const char *dev)
135
+{
136
+    return 0;
137
+}
138
+
139
+static inline void
140
+close_tun_dco(struct tuntap *tt, openvpn_net_ctx_t *ctx)
141
+{
142
+}
143
+
144
+static inline int
145
+dco_do_read(dco_context_t *dco)
146
+{
147
+    ASSERT(false);
148
+    return 0;
149
+}
150
+
151
+static inline int
152
+dco_do_write(dco_context_t *dco, int peer_id, struct buffer *buf)
153
+{
154
+    ASSERT(false);
155
+    return 0;
156
+}
157
+
158
+static inline void
159
+dco_event_set(dco_context_t *dco, struct event_set *es, void *arg)
160
+{
161
+}
162
+
163
+#endif /* defined(ENABLE_DCO) */
164
+#endif /* ifndef DCO_H */
0 165
new file mode 100644
... ...
@@ -0,0 +1,78 @@
0
+/*
1
+ *  OpenVPN -- An application to securely tunnel IP networks
2
+ *             over a single TCP/UDP 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) 2022 Antonio Quartulli <a@unstable.cc>
8
+ *  Copyright (C) 2022 OpenVPN Inc <sales@openvpn.net>
9
+ *
10
+ *  This program is free software; you can redistribute it and/or modify
11
+ *  it under the terms of the GNU General Public License version 2
12
+ *  as published by the Free Software Foundation.
13
+ *
14
+ *  This program is distributed in the hope that it will be useful,
15
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
+ *  GNU General Public License for more details.
18
+ *
19
+ *  You should have received a copy of the GNU General Public License
20
+ *  along with this program (see the file COPYING included with this
21
+ *  distribution); if not, write to the Free Software Foundation, Inc.,
22
+ *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23
+ */
24
+#ifndef DCO_INTERNAL_H
25
+#define DCO_INTERNAL_H
26
+
27
+#if defined(ENABLE_DCO)
28
+
29
+#include "dco_linux.h"
30
+
31
+/**
32
+ * This file contains the internal DCO API definition.
33
+ * It is expected that this file is included only in dco.h.
34
+ * The OpenVPN code should never directly include this file
35
+ */
36
+
37
+static inline dco_cipher_t
38
+dco_get_cipher(const char *cipher)
39
+{
40
+    if (strcmp(cipher, "AES-256-GCM") == 0 || strcmp(cipher, "AES-128-GCM") == 0
41
+        || strcmp(cipher, "AES-192-GCM") == 0)
42
+    {
43
+        return OVPN_CIPHER_ALG_AES_GCM;
44
+    }
45
+    else if (strcmp(cipher, "CHACHA20-POLY1305") == 0)
46
+    {
47
+        return OVPN_CIPHER_ALG_CHACHA20_POLY1305;
48
+    }
49
+    else
50
+    {
51
+        msg(M_FATAL, "DCO: provided unsupported cipher: %s", cipher);
52
+    }
53
+}
54
+
55
+/**
56
+ * The following are the DCO APIs used to control the driver.
57
+ * They are implemented by dco_linux.c
58
+ */
59
+
60
+int dco_new_peer(dco_context_t *dco, unsigned int peerid, int sd,
61
+                 struct sockaddr *localaddr, struct sockaddr *remoteaddr,
62
+                 struct in_addr *remote_in4, struct in6_addr *remote_in6);
63
+
64
+int dco_del_peer(dco_context_t *dco, unsigned int peerid);
65
+
66
+int dco_new_key(dco_context_t *dco, unsigned int peerid, int keyid,
67
+                dco_key_slot_t slot,
68
+                const uint8_t *encrypt_key, const uint8_t *encrypt_iv,
69
+                const uint8_t *decrypt_key, const uint8_t *decrypt_iv,
70
+                const char *ciphername);
71
+
72
+int dco_del_key(dco_context_t *dco, unsigned int peerid, dco_key_slot_t slot);
73
+
74
+int dco_swap_keys(dco_context_t *dco, unsigned int peerid);
75
+
76
+#endif /* defined(ENABLE_DCO) */
77
+#endif /* ifndef DCO_INTERNAL_H */
0 78
new file mode 100644
... ...
@@ -0,0 +1,934 @@
0
+/*
1
+ *  Interface to linux dco networking code
2
+ *
3
+ *  Copyright (C) 2020-2022 Antonio Quartulli <a@unstable.cc>
4
+ *  Copyright (C) 2020-2022 Arne Schwabe <arne@rfc2549.org>
5
+ *  Copyright (C) 2020-2022 OpenVPN Inc <sales@openvpn.net>
6
+ *
7
+ *  This program is free software; you can redistribute it and/or modify
8
+ *  it under the terms of the GNU General Public License version 2
9
+ *  as published by the Free Software Foundation.
10
+ *
11
+ *  This program is distributed in the hope that it will be useful,
12
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
+ *  GNU General Public License for more details.
15
+ *
16
+ *  You should have received a copy of the GNU General Public License
17
+ *  along with this program (see the file COPYING included with this
18
+ *  distribution); if not, write to the Free Software Foundation, Inc.,
19
+ *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20
+ */
21
+
22
+
23
+#ifdef HAVE_CONFIG_H
24
+#include "config.h"
25
+#elif defined(_MSC_VER)
26
+#include "config-msvc.h"
27
+#endif
28
+
29
+#if defined(ENABLE_DCO) && defined(TARGET_LINUX)
30
+
31
+#include "syshead.h"
32
+
33
+#include "dco_linux.h"
34
+#include "errlevel.h"
35
+#include "buffer.h"
36
+#include "networking.h"
37
+#include "openvpn.h"
38
+
39
+#include "socket.h"
40
+#include "tun.h"
41
+#include "ssl.h"
42
+#include "fdmisc.h"
43
+#include "ssl_verify.h"
44
+
45
+#include "ovpn_dco_linux.h"
46
+
47
+#include <netlink/socket.h>
48
+#include <netlink/netlink.h>
49
+#include <netlink/genl/genl.h>
50
+#include <netlink/genl/family.h>
51
+#include <netlink/genl/ctrl.h>
52
+
53
+
54
+/* libnl < 3.5.0 does not set the NLA_F_NESTED on its own, therefore we
55
+ * have to explicitly do it to prevent the kernel from failing upon
56
+ * parsing of the message
57
+ */
58
+#define nla_nest_start(_msg, _type) \
59
+    nla_nest_start(_msg, (_type) | NLA_F_NESTED)
60
+
61
+static int ovpn_get_mcast_id(dco_context_t *dco);
62
+
63
+void dco_check_key_ctx(const struct key_ctx_bi *key);
64
+
65
+typedef int (*ovpn_nl_cb)(struct nl_msg *msg, void *arg);
66
+
67
+/**
68
+ * @brief resolves the netlink ID for ovpn-dco
69
+ *
70
+ * This function queries the kernel via a netlink socket
71
+ * whether the ovpn-dco netlink namespace is available
72
+ *
73
+ * This function can be used to determine if the kernel
74
+ * supports DCO offloading.
75
+ *
76
+ * @return ID on success, negative error code on error
77
+ */
78
+static int
79
+resolve_ovpn_netlink_id(int msglevel)
80
+{
81
+    int ret;
82
+    struct nl_sock *nl_sock = nl_socket_alloc();
83
+
84
+    ret = genl_connect(nl_sock);
85
+    if (ret)
86
+    {
87
+        msg(msglevel, "Cannot connect to generic netlink: %s",
88
+            nl_geterror(ret));
89
+        goto err_sock;
90
+    }
91
+    set_cloexec(nl_socket_get_fd(nl_sock));
92
+
93
+    ret = genl_ctrl_resolve(nl_sock, OVPN_NL_NAME);
94
+    if (ret < 0)
95
+    {
96
+        msg(msglevel, "Cannot find ovpn_dco netlink component: %s",
97
+            nl_geterror(ret));
98
+    }
99
+
100
+err_sock:
101
+    nl_socket_free(nl_sock);
102
+    return ret;
103
+}
104
+
105
+static struct nl_msg *
106
+ovpn_dco_nlmsg_create(dco_context_t *dco, enum ovpn_nl_commands cmd)
107
+{
108
+    struct nl_msg *nl_msg = nlmsg_alloc();
109
+    if (!nl_msg)
110
+    {
111
+        msg(M_ERR, "cannot allocate netlink message");
112
+        return NULL;
113
+    }
114
+
115
+    genlmsg_put(nl_msg, 0, 0, dco->ovpn_dco_id, 0, 0, cmd, 0);
116
+    NLA_PUT_U32(nl_msg, OVPN_ATTR_IFINDEX, dco->ifindex);
117
+
118
+    return nl_msg;
119
+nla_put_failure:
120
+    nlmsg_free(nl_msg);
121
+    msg(M_INFO, "cannot put into netlink message");
122
+    return NULL;
123
+}
124
+
125
+static int
126
+ovpn_nl_recvmsgs(dco_context_t *dco, const char *prefix)
127
+{
128
+    int ret = nl_recvmsgs(dco->nl_sock, dco->nl_cb);
129
+
130
+    switch (ret)
131
+    {
132
+        case -NLE_INTR:
133
+            msg(M_WARN, "%s: netlink received interrupt due to signal - ignoring", prefix);
134
+            break;
135
+
136
+        case -NLE_NOMEM:
137
+            msg(M_ERR, "%s: netlink out of memory error", prefix);
138
+            break;
139
+
140
+        case -M_ERR:
141
+            msg(M_WARN, "%s: netlink reports blocking read - aborting wait", prefix);
142
+            break;
143
+
144
+        case -NLE_NODEV:
145
+            msg(M_ERR, "%s: netlink reports device not found:", prefix);
146
+            break;
147
+
148
+        case -NLE_OBJ_NOTFOUND:
149
+            msg(M_INFO, "%s: netlink reports object not found, ovpn-dco unloaded?", prefix);
150
+            break;
151
+
152
+        default:
153
+            if (ret)
154
+            {
155
+                msg(M_NONFATAL|M_ERRNO, "%s: netlink reports error (%d): %s", prefix, ret, nl_geterror(-ret));
156
+            }
157
+            break;
158
+    }
159
+
160
+    return ret;
161
+}
162
+
163
+/**
164
+ * Send a prepared netlink message and registers cb as callback if non-null.
165
+ *
166
+ * The method will also free nl_msg
167
+ * @param dco       The dco context to use
168
+ * @param nl_msg    the message to use
169
+ * @param cb        An optional callback if the caller expects an answer
170
+ * @param prefix    A prefix to report in the error message to give the user context
171
+ * @return          status of sending the message
172
+ */
173
+static int
174
+ovpn_nl_msg_send(dco_context_t *dco, struct nl_msg *nl_msg, ovpn_nl_cb cb,
175
+                 const char *prefix)
176
+{
177
+    dco->status = 1;
178
+
179
+    nl_cb_set(dco->nl_cb, NL_CB_VALID, NL_CB_CUSTOM, cb, dco);
180
+    nl_send_auto(dco->nl_sock, nl_msg);
181
+
182
+    while (dco->status == 1)
183
+    {
184
+        ovpn_nl_recvmsgs(dco, prefix);
185
+    }
186
+
187
+    if (dco->status < 0)
188
+    {
189
+        msg(M_INFO, "%s: failed to send netlink message: %s (%d)",
190
+            prefix, strerror(-dco->status), dco->status);
191
+    }
192
+
193
+    return dco->status;
194
+}
195
+
196
+struct sockaddr *
197
+mapped_v4_to_v6(struct sockaddr *sock, struct gc_arena *gc)
198
+{
199
+    struct sockaddr_in6 *sock6 = (struct sockaddr_in6 *)sock;
200
+    if (sock->sa_family == AF_INET6 && IN6_IS_ADDR_V4MAPPED(&sock6->sin6_addr))
201
+    {
202
+
203
+        struct sockaddr_in *sock4;
204
+        ALLOC_OBJ_CLEAR_GC(sock4, struct sockaddr_in, gc);
205
+        memcpy(&sock4->sin_addr, sock6->sin6_addr.s6_addr + 12, 4);
206
+        sock4->sin_port = sock6->sin6_port;
207
+        sock4->sin_family = AF_INET;
208
+        return (struct sockaddr *)sock4;
209
+    }
210
+    return sock;
211
+}
212
+
213
+int
214
+dco_new_peer(dco_context_t *dco, unsigned int peerid, int sd,
215
+             struct sockaddr *localaddr, struct sockaddr *remoteaddr,
216
+             struct in_addr *remote_in4, struct in6_addr *remote_in6)
217
+{
218
+    msg(D_DCO_DEBUG, "%s: peer-id %d, fd %d", __func__, peerid, sd);
219
+
220
+    struct gc_arena gc = gc_new();
221
+    struct nl_msg *nl_msg = ovpn_dco_nlmsg_create(dco, OVPN_CMD_NEW_PEER);
222
+    struct nlattr *attr = nla_nest_start(nl_msg, OVPN_ATTR_NEW_PEER);
223
+    int ret = -EMSGSIZE;
224
+
225
+    NLA_PUT_U32(nl_msg, OVPN_NEW_PEER_ATTR_PEER_ID, peerid);
226
+    NLA_PUT_U32(nl_msg, OVPN_NEW_PEER_ATTR_SOCKET, sd);
227
+
228
+    /* Set the remote endpoint if defined (for UDP) */
229
+    if (remoteaddr)
230
+    {
231
+        remoteaddr = mapped_v4_to_v6(remoteaddr, &gc);
232
+        int alen = af_addr_size(remoteaddr->sa_family);
233
+
234
+        NLA_PUT(nl_msg, OVPN_NEW_PEER_ATTR_SOCKADDR_REMOTE, alen, remoteaddr);
235
+    }
236
+
237
+    if (localaddr)
238
+    {
239
+        localaddr = mapped_v4_to_v6(localaddr, &gc);
240
+        if (localaddr->sa_family == AF_INET)
241
+        {
242
+            NLA_PUT(nl_msg, OVPN_NEW_PEER_ATTR_LOCAL_IP, sizeof(struct in_addr),
243
+                    &((struct sockaddr_in *)localaddr)->sin_addr);
244
+        }
245
+        else if (localaddr->sa_family == AF_INET6)
246
+        {
247
+            NLA_PUT(nl_msg, OVPN_NEW_PEER_ATTR_LOCAL_IP, sizeof(struct in6_addr),
248
+                    &((struct sockaddr_in6 *)localaddr)->sin6_addr);
249
+        }
250
+    }
251
+
252
+    /* Set the primary VPN IP addresses of the peer */
253
+    if (remote_in4)
254
+    {
255
+        NLA_PUT_U32(nl_msg, OVPN_NEW_PEER_ATTR_IPV4, remote_in4->s_addr);
256
+    }
257
+    if (remote_in6)
258
+    {
259
+        NLA_PUT(nl_msg, OVPN_NEW_PEER_ATTR_IPV6, sizeof(struct in6_addr),
260
+                remote_in6);
261
+    }
262
+    nla_nest_end(nl_msg, attr);
263
+
264
+    ret = ovpn_nl_msg_send(dco, nl_msg, NULL, __func__);
265
+
266
+nla_put_failure:
267
+    nlmsg_free(nl_msg);
268
+    gc_free(&gc);
269
+    return ret;
270
+}
271
+
272
+static int
273
+ovpn_nl_cb_finish(struct nl_msg (*msg) __attribute__ ((unused)), void *arg)
274
+{
275
+    int *status = arg;
276
+
277
+    *status = 0;
278
+    return NL_SKIP;
279
+}
280
+
281
+/* This function is used as error callback on the netlink socket.
282
+ * When something goes wrong and the kernel returns an error, this function is
283
+ * invoked.
284
+ *
285
+ * We pass the error code to the user by means of a variable pointed by *arg
286
+ * (supplied by the user when setting this callback) and we parse the kernel
287
+ * reply to see if it contains a human readable error. If found, it is printed.
288
+ */
289
+static int
290
+ovpn_nl_cb_error(struct sockaddr_nl (*nla) __attribute__ ((unused)),
291
+                 struct nlmsgerr *err, void *arg)
292
+{
293
+    struct nlmsghdr *nlh = (struct nlmsghdr *)err - 1;
294
+    struct nlattr *tb_msg[NLMSGERR_ATTR_MAX + 1];
295
+    int len = nlh->nlmsg_len;
296
+    struct nlattr *attrs;
297
+    int *ret = arg;
298
+    int ack_len = sizeof(*nlh) + sizeof(int) + sizeof(*nlh);
299
+
300
+    *ret = err->error;
301
+
302
+    if (!(nlh->nlmsg_flags & NLM_F_ACK_TLVS))
303
+    {
304
+        return NL_STOP;
305
+    }
306
+
307
+    if (!(nlh->nlmsg_flags & NLM_F_CAPPED))
308
+    {
309
+        ack_len += err->msg.nlmsg_len - sizeof(*nlh);
310
+    }
311
+
312
+    if (len <= ack_len)
313
+    {
314
+        return NL_STOP;
315
+    }
316
+
317
+    attrs = (void *)((unsigned char *)nlh + ack_len);
318
+    len -= ack_len;
319
+
320
+    nla_parse(tb_msg, NLMSGERR_ATTR_MAX, attrs, len, NULL);
321
+    if (tb_msg[NLMSGERR_ATTR_MSG])
322
+    {
323
+        len = strnlen((char *)nla_data(tb_msg[NLMSGERR_ATTR_MSG]),
324
+                      nla_len(tb_msg[NLMSGERR_ATTR_MSG]));
325
+        msg(M_WARN, "kernel error: %*s\n", len,
326
+            (char *)nla_data(tb_msg[NLMSGERR_ATTR_MSG]));
327
+    }
328
+
329
+    return NL_STOP;
330
+}
331
+
332
+static void
333
+ovpn_dco_init_netlink(dco_context_t *dco)
334
+{
335
+    dco->ovpn_dco_id = resolve_ovpn_netlink_id(M_ERR);
336
+
337
+    dco->nl_sock = nl_socket_alloc();
338
+
339
+    if (!dco->nl_sock)
340
+    {
341
+        msg(M_ERR, "Cannot create netlink socket");
342
+    }
343
+
344
+    /* TODO: Why are we setting this buffer size? */
345
+    nl_socket_set_buffer_size(dco->nl_sock, 8192, 8192);
346
+
347
+    int ret = genl_connect(dco->nl_sock);
348
+    if (ret)
349
+    {
350
+        msg(M_ERR, "Cannot connect to generic netlink: %s",
351
+            nl_geterror(ret));
352
+    }
353
+
354
+    set_cloexec(nl_socket_get_fd(dco->nl_sock));
355
+
356
+    dco->nl_cb = nl_cb_alloc(NL_CB_DEFAULT);
357
+    if (!dco->nl_cb)
358
+    {
359
+        msg(M_ERR, "failed to allocate netlink callback");
360
+    }
361
+
362
+    nl_socket_set_cb(dco->nl_sock, dco->nl_cb);
363
+
364
+    nl_cb_err(dco->nl_cb, NL_CB_CUSTOM, ovpn_nl_cb_error, &dco->status);
365
+    nl_cb_set(dco->nl_cb, NL_CB_FINISH, NL_CB_CUSTOM, ovpn_nl_cb_finish,
366
+              &dco->status);
367
+    nl_cb_set(dco->nl_cb, NL_CB_ACK, NL_CB_CUSTOM, ovpn_nl_cb_finish,
368
+              &dco->status);
369
+
370
+    /* The async PACKET messages confuse libnl and it will drop them with
371
+     * wrong sequence numbers (NLE_SEQ_MISMATCH), so disable libnl's sequence
372
+     * number check */
373
+    nl_socket_disable_seq_check(dco->nl_sock);
374
+}
375
+
376
+bool
377
+ovpn_dco_init(int mode, dco_context_t *dco)
378
+{
379
+    switch (mode)
380
+    {
381
+        case CM_TOP:
382
+            dco->ifmode = OVPN_MODE_MP;
383
+            break;
384
+
385
+        case CM_P2P:
386
+            dco->ifmode = OVPN_MODE_P2P;
387
+            break;
388
+
389
+        default:
390
+            ASSERT(false);
391
+    }
392
+
393
+    ovpn_dco_init_netlink(dco);
394
+    return true;
395
+}
396
+
397
+static void
398
+ovpn_dco_uninit_netlink(dco_context_t *dco)
399
+{
400
+    nl_socket_free(dco->nl_sock);
401
+    dco->nl_sock = NULL;
402
+
403
+    /* Decrease reference count */
404
+    nl_cb_put(dco->nl_cb);
405
+
406
+    CLEAR(dco);
407
+}
408
+
409
+static void
410
+ovpn_dco_register(dco_context_t *dco)
411
+{
412
+    msg(D_DCO_DEBUG, __func__);
413
+    ovpn_get_mcast_id(dco);
414
+
415
+    if (dco->ovpn_dco_mcast_id < 0)
416
+    {
417
+        msg(M_ERR, "cannot get mcast group: %s",  nl_geterror(dco->ovpn_dco_mcast_id));
418
+    }
419
+
420
+    /* Register for ovpn-dco specific multicast messages that the kernel may
421
+     * send
422
+     */
423
+    int ret = nl_socket_add_membership(dco->nl_sock, dco->ovpn_dco_mcast_id);
424
+    if (ret)
425
+    {
426
+        msg(M_ERR, "%s: failed to join groups: %d", __func__, ret);
427
+    }
428
+
429
+    /* Register for non-data packets that ovpn-dco may receive. They will be
430
+     * forwarded to userspace
431
+     */
432
+    struct nl_msg *nl_msg = ovpn_dco_nlmsg_create(dco, OVPN_CMD_REGISTER_PACKET);
433
+    if (!nl_msg)
434
+    {
435
+        msg(M_ERR, "%s: cannot allocate message to register for control packets",
436
+            __func__);
437
+    }
438
+
439
+    ret = ovpn_nl_msg_send(dco, nl_msg, NULL, __func__);
440
+    if (ret)
441
+    {
442
+        msg(M_ERR, "%s: failed to register for control packets: %d", __func__,
443
+            ret);
444
+    }
445
+    nlmsg_free(nl_msg);
446
+}
447
+
448
+int
449
+open_tun_dco(struct tuntap *tt, openvpn_net_ctx_t *ctx, const char *dev)
450
+{
451
+    msg(D_DCO_DEBUG, "%s: %s", __func__, dev);
452
+    ASSERT(tt->type == DEV_TYPE_TUN);
453
+
454
+    int ret = net_iface_new(ctx, dev, "ovpn-dco", &tt->dco);
455
+    if (ret < 0)
456
+    {
457
+        msg(D_DCO_DEBUG, "Cannot create DCO interface %s: %d", dev, ret);
458
+        return ret;
459
+    }
460
+
461
+    tt->dco.ifindex = if_nametoindex(dev);
462
+    if (!tt->dco.ifindex)
463
+    {
464
+        msg(M_FATAL, "DCO: cannot retrieve ifindex for interface %s", dev);
465
+    }
466
+
467
+    tt->actual_name = string_alloc(dev, NULL);
468
+    uint8_t *dcobuf = malloc(65536);
469
+    buf_set_write(&tt->dco.dco_packet_in, dcobuf, 65536);
470
+    tt->dco.dco_message_peer_id = -1;
471
+
472
+    ovpn_dco_register(&tt->dco);
473
+
474
+    return 0;
475
+}
476
+
477
+void
478
+close_tun_dco(struct tuntap *tt, openvpn_net_ctx_t *ctx)
479
+{
480
+    msg(D_DCO_DEBUG, __func__);
481
+
482
+    net_iface_del(ctx, tt->actual_name);
483
+    ovpn_dco_uninit_netlink(&tt->dco);
484
+    free(tt->dco.dco_packet_in.data);
485
+}
486
+
487
+int
488
+dco_swap_keys(dco_context_t *dco, unsigned int peerid)
489
+{
490
+    msg(D_DCO_DEBUG, "%s: peer-id %d", __func__, peerid);
491
+
492
+    struct nl_msg *nl_msg = ovpn_dco_nlmsg_create(dco, OVPN_CMD_SWAP_KEYS);
493
+    if (!nl_msg)
494
+    {
495
+        return -ENOMEM;
496
+    }
497
+
498
+    struct nlattr *attr = nla_nest_start(nl_msg, OVPN_ATTR_SWAP_KEYS);
499
+    int ret = -EMSGSIZE;
500
+    NLA_PUT_U32(nl_msg, OVPN_SWAP_KEYS_ATTR_PEER_ID, peerid);
501
+    nla_nest_end(nl_msg, attr);
502
+
503
+    ret = ovpn_nl_msg_send(dco, nl_msg, NULL, __func__);
504
+
505
+nla_put_failure:
506
+    nlmsg_free(nl_msg);
507
+    return ret;
508
+}
509
+
510
+
511
+int
512
+dco_del_peer(dco_context_t *dco, unsigned int peerid)
513
+{
514
+    msg(D_DCO_DEBUG, "%s: peer-id %d", __func__, peerid);
515
+
516
+    struct nl_msg *nl_msg = ovpn_dco_nlmsg_create(dco, OVPN_CMD_DEL_PEER);
517
+    if (!nl_msg)
518
+    {
519
+        return -ENOMEM;
520
+    }
521
+
522
+    struct nlattr *attr = nla_nest_start(nl_msg, OVPN_ATTR_DEL_PEER);
523
+    int ret = -EMSGSIZE;
524
+    NLA_PUT_U32(nl_msg, OVPN_DEL_PEER_ATTR_PEER_ID, peerid);
525
+    nla_nest_end(nl_msg, attr);
526
+
527
+    ret = ovpn_nl_msg_send(dco, nl_msg, NULL, __func__);
528
+
529
+nla_put_failure:
530
+    nlmsg_free(nl_msg);
531
+    return ret;
532
+}
533
+
534
+
535
+int
536
+dco_del_key(dco_context_t *dco, unsigned int peerid,
537
+            dco_key_slot_t slot)
538
+{
539
+    msg(D_DCO_DEBUG, "%s: peer-id %d, slot %d", __func__, peerid, slot);
540
+
541
+    struct nl_msg *nl_msg = ovpn_dco_nlmsg_create(dco, OVPN_CMD_DEL_KEY);
542
+    if (!nl_msg)
543
+    {
544
+        return -ENOMEM;
545
+    }
546
+
547
+    struct nlattr *attr = nla_nest_start(nl_msg, OVPN_ATTR_DEL_KEY);
548
+    int ret = -EMSGSIZE;
549
+    NLA_PUT_U32(nl_msg, OVPN_DEL_KEY_ATTR_PEER_ID, peerid);
550
+    NLA_PUT_U8(nl_msg, OVPN_DEL_KEY_ATTR_KEY_SLOT, slot);
551
+    nla_nest_end(nl_msg, attr);
552
+
553
+    ret = ovpn_nl_msg_send(dco, nl_msg, NULL, __func__);
554
+
555
+nla_put_failure:
556
+    nlmsg_free(nl_msg);
557
+    return ret;
558
+}
559
+
560
+int
561
+dco_new_key(dco_context_t *dco, unsigned int peerid, int keyid,
562
+            dco_key_slot_t slot,
563
+            const uint8_t *encrypt_key, const uint8_t *encrypt_iv,
564
+            const uint8_t *decrypt_key, const uint8_t *decrypt_iv,
565
+            const char *ciphername)
566
+{
567
+    msg(D_DCO_DEBUG, "%s: slot %d, key-id %d, peer-id %d, cipher %s",
568
+        __func__, slot, keyid, peerid, ciphername);
569
+
570
+    const size_t key_len = cipher_kt_key_size(ciphername);
571
+    const int nonce_tail_len = 8;
572
+
573
+    struct nl_msg *nl_msg = ovpn_dco_nlmsg_create(dco, OVPN_CMD_NEW_KEY);
574
+    if (!nl_msg)
575
+    {
576
+        return -ENOMEM;
577
+    }
578
+
579
+    dco_cipher_t dco_cipher = dco_get_cipher(ciphername);
580
+
581
+    int ret = -EMSGSIZE;
582
+    struct nlattr *attr = nla_nest_start(nl_msg, OVPN_ATTR_NEW_KEY);
583
+    NLA_PUT_U32(nl_msg, OVPN_NEW_KEY_ATTR_PEER_ID, peerid);
584
+    NLA_PUT_U8(nl_msg, OVPN_NEW_KEY_ATTR_KEY_SLOT, slot);
585
+    NLA_PUT_U8(nl_msg, OVPN_NEW_KEY_ATTR_KEY_ID, keyid);
586
+    NLA_PUT_U16(nl_msg, OVPN_NEW_KEY_ATTR_CIPHER_ALG, dco_cipher);
587
+
588
+    struct nlattr *key_enc = nla_nest_start(nl_msg,
589
+                                            OVPN_NEW_KEY_ATTR_ENCRYPT_KEY);
590
+    if (dco_cipher != OVPN_CIPHER_ALG_NONE)
591
+    {
592
+        NLA_PUT(nl_msg, OVPN_KEY_DIR_ATTR_CIPHER_KEY, key_len, encrypt_key);
593
+        NLA_PUT(nl_msg, OVPN_KEY_DIR_ATTR_NONCE_TAIL, nonce_tail_len,
594
+                encrypt_iv);
595
+    }
596
+    nla_nest_end(nl_msg, key_enc);
597
+
598
+    struct nlattr *key_dec = nla_nest_start(nl_msg,
599
+                                            OVPN_NEW_KEY_ATTR_DECRYPT_KEY);
600
+    if (dco_cipher != OVPN_CIPHER_ALG_NONE)
601
+    {
602
+        NLA_PUT(nl_msg, OVPN_KEY_DIR_ATTR_CIPHER_KEY, key_len, decrypt_key);
603
+        NLA_PUT(nl_msg, OVPN_KEY_DIR_ATTR_NONCE_TAIL, nonce_tail_len,
604
+                decrypt_iv);
605
+    }
606
+    nla_nest_end(nl_msg, key_dec);
607
+
608
+    nla_nest_end(nl_msg, attr);
609
+
610
+    ret = ovpn_nl_msg_send(dco, nl_msg, NULL, __func__);
611
+
612
+nla_put_failure:
613
+    nlmsg_free(nl_msg);
614
+    return ret;
615
+}
616
+
617
+int
618
+dco_set_peer(dco_context_t *dco, unsigned int peerid,
619
+             int keepalive_interval, int keepalive_timeout, int mss)
620
+{
621
+    msg(D_DCO_DEBUG, "%s: peer-id %d, keepalive %d/%d, mss %d", __func__,
622
+        peerid, keepalive_interval, keepalive_timeout, mss);
623
+
624
+    struct nl_msg *nl_msg = ovpn_dco_nlmsg_create(dco, OVPN_CMD_SET_PEER);
625
+    if (!nl_msg)
626
+    {
627
+        return -ENOMEM;
628
+    }
629
+
630
+    struct nlattr *attr = nla_nest_start(nl_msg, OVPN_ATTR_SET_PEER);
631
+    int ret = -EMSGSIZE;
632
+    NLA_PUT_U32(nl_msg, OVPN_SET_PEER_ATTR_PEER_ID, peerid);
633
+    NLA_PUT_U32(nl_msg, OVPN_SET_PEER_ATTR_KEEPALIVE_INTERVAL,
634
+                keepalive_interval);
635
+    NLA_PUT_U32(nl_msg, OVPN_SET_PEER_ATTR_KEEPALIVE_TIMEOUT,
636
+                keepalive_timeout);
637
+    nla_nest_end(nl_msg, attr);
638
+
639
+    ret = ovpn_nl_msg_send(dco, nl_msg, NULL, __func__);
640
+
641
+nla_put_failure:
642
+    nlmsg_free(nl_msg);
643
+    return ret;
644
+}
645
+
646
+/* This function parses the reply provided by the kernel to the CTRL_CMD_GETFAMILY
647
+ * message. We parse the reply and we retrieve the multicast group ID associated
648
+ * with the "ovpn-dco" netlink family.
649
+ *
650
+ * The ID is later used to subscribe to the multicast group and be notified
651
+ * about any multicast message sent by the ovpn-dco kernel module.
652
+ */
653
+static int
654
+mcast_family_handler(struct nl_msg *msg, void *arg)
655
+{
656
+    dco_context_t *dco = arg;
657
+    struct nlattr *tb[CTRL_ATTR_MAX + 1];
658
+    struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
659
+
660
+    nla_parse(tb, CTRL_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
661
+              genlmsg_attrlen(gnlh, 0), NULL);
662
+
663
+    if (!tb[CTRL_ATTR_MCAST_GROUPS])
664
+    {
665
+        return NL_SKIP;
666
+    }
667
+
668
+    struct nlattr *mcgrp;
669
+    int rem_mcgrp;
670
+    nla_for_each_nested(mcgrp, tb[CTRL_ATTR_MCAST_GROUPS], rem_mcgrp)
671
+    {
672
+        struct nlattr *tb_mcgrp[CTRL_ATTR_MCAST_GRP_MAX + 1];
673
+
674
+        nla_parse(tb_mcgrp, CTRL_ATTR_MCAST_GRP_MAX,
675
+                  nla_data(mcgrp), nla_len(mcgrp), NULL);
676
+
677
+        if (!tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME]
678
+            || !tb_mcgrp[CTRL_ATTR_MCAST_GRP_ID])
679
+        {
680
+            continue;
681
+        }
682
+
683
+        if (strncmp(nla_data(tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME]),
684
+                    OVPN_NL_MULTICAST_GROUP_PEERS,
685
+                    nla_len(tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME])) != 0)
686
+        {
687
+            continue;
688
+        }
689
+        dco->ovpn_dco_mcast_id = nla_get_u32(tb_mcgrp[CTRL_ATTR_MCAST_GRP_ID]);
690
+        break;
691
+    }
692
+
693
+    return NL_SKIP;
694
+}
695
+/**
696
+ * Lookup the multicast id for OpenVPN. This method and its help method currently
697
+ * hardcode the lookup to OVPN_NL_NAME and OVPN_NL_MULTICAST_GROUP_PEERS but
698
+ * extended in the future if we need to lookup more than one mcast id.
699
+ */
700
+static int
701
+ovpn_get_mcast_id(dco_context_t *dco)
702
+{
703
+    dco->ovpn_dco_mcast_id = -ENOENT;
704
+
705
+    /* Even though 'nlctrl' is a constant, there seem to be no library
706
+     * provided define for it */
707
+    int ctrlid = genl_ctrl_resolve(dco->nl_sock, "nlctrl");
708
+
709
+    struct nl_msg *nl_msg = nlmsg_alloc();
710
+    if (!nl_msg)
711
+    {
712
+        return -ENOMEM;
713
+    }
714
+
715
+    genlmsg_put(nl_msg, 0, 0, ctrlid, 0, 0, CTRL_CMD_GETFAMILY, 0);
716
+
717
+    int ret = -EMSGSIZE;
718
+    NLA_PUT_STRING(nl_msg, CTRL_ATTR_FAMILY_NAME, OVPN_NL_NAME);
719
+
720
+    ret = ovpn_nl_msg_send(dco, nl_msg, mcast_family_handler, __func__);
721
+
722
+nla_put_failure:
723
+    nlmsg_free(nl_msg);
724
+    return ret;
725
+}
726
+
727
+/* This function parses any netlink message sent by ovpn-dco to userspace */
728
+static int
729
+ovpn_handle_msg(struct nl_msg *msg, void *arg)
730
+{
731
+    dco_context_t *dco = arg;
732
+
733
+    struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
734
+    struct nlattr *attrs[OVPN_ATTR_MAX + 1];
735
+    struct nlmsghdr *nlh = nlmsg_hdr(msg);
736
+
737
+    if (!genlmsg_valid_hdr(nlh, 0))
738
+    {
739
+        msg(D_DCO, "ovpn-dco: invalid header");
740
+        return NL_SKIP;
741
+    }
742
+
743
+    if (nla_parse(attrs, OVPN_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
744
+                  genlmsg_attrlen(gnlh, 0), NULL))
745
+    {
746
+        msg(D_DCO, "received bogus data from ovpn-dco");
747
+        return NL_SKIP;
748
+    }
749
+
750
+    /* we must know which interface this message is referring to in order to
751
+     * avoid mixing messages for other instances
752
+     */
753
+    if (!attrs[OVPN_ATTR_IFINDEX])
754
+    {
755
+        msg(D_DCO, "ovpn-dco: Received message without ifindex");
756
+        return NL_SKIP;
757
+    }
758
+
759
+    uint32_t ifindex = nla_get_u32(attrs[OVPN_ATTR_IFINDEX]);
760
+    if (ifindex != dco->ifindex)
761
+    {
762
+        msg(D_DCO, "ovpn-dco: received message type %d with mismatched ifindex %d\n",
763
+            gnlh->cmd, ifindex);
764
+        return NL_SKIP;
765
+    }
766
+
767
+    /* based on the message type, we parse the subobject contained in the
768
+     * message, that stores the type-specific attributes.
769
+     *
770
+     * the "dco" object is then filled accordingly with the information
771
+     * retrieved from the message, so that the rest of the OpenVPN code can
772
+     * react as need be.
773
+     */
774
+    switch (gnlh->cmd)
775
+    {
776
+        case OVPN_CMD_DEL_PEER:
777
+        {
778
+            if (!attrs[OVPN_ATTR_DEL_PEER])
779
+            {
780
+                msg(D_DCO, "ovpn-dco: no attributes in OVPN_DEL_PEER message");
781
+                return NL_SKIP;
782
+            }
783
+
784
+            struct nlattr *dp_attrs[OVPN_DEL_PEER_ATTR_MAX + 1];
785
+            if (nla_parse_nested(dp_attrs, OVPN_DEL_PEER_ATTR_MAX,
786
+                                 attrs[OVPN_ATTR_DEL_PEER], NULL))
787
+            {
788
+                msg(D_DCO, "received bogus del peer packet data from ovpn-dco");
789
+                return NL_SKIP;
790
+            }
791
+
792
+            if (!dp_attrs[OVPN_DEL_PEER_ATTR_REASON])
793
+            {
794
+                msg(D_DCO, "ovpn-dco: no reason in DEL_PEER message");
795
+                return NL_SKIP;
796
+            }
797
+            if (!dp_attrs[OVPN_DEL_PEER_ATTR_PEER_ID])
798
+            {
799
+                msg(D_DCO, "ovpn-dco: no peer-id in DEL_PEER message");
800
+                return NL_SKIP;
801
+            }
802
+            int reason = nla_get_u8(dp_attrs[OVPN_DEL_PEER_ATTR_REASON]);
803
+            unsigned int peerid = nla_get_u32(dp_attrs[OVPN_DEL_PEER_ATTR_PEER_ID]);
804
+
805
+            msg(D_DCO_DEBUG, "ovpn-dco: received CMD_DEL_PEER, ifindex: %d, peer-id %d, reason: %d",
806
+                ifindex, peerid, reason);
807
+            dco->dco_message_peer_id = peerid;
808
+            dco->dco_del_peer_reason = reason;
809
+            dco->dco_message_type = OVPN_CMD_DEL_PEER;
810
+
811
+            break;
812
+        }
813
+
814
+        case OVPN_CMD_PACKET:
815
+        {
816
+            if (!attrs[OVPN_ATTR_PACKET])
817
+            {
818
+                msg(D_DCO, "ovpn-dco: no packet in OVPN_CMD_PACKET message");
819
+                return NL_SKIP;
820
+            }
821
+            struct nlattr *pkt_attrs[OVPN_PACKET_ATTR_MAX + 1];
822
+
823
+            if (nla_parse_nested(pkt_attrs, OVPN_PACKET_ATTR_MAX,
824
+                                 attrs[OVPN_ATTR_PACKET], NULL))
825
+            {
826
+                msg(D_DCO, "received bogus cmd packet data from ovpn-dco");
827
+                return NL_SKIP;
828
+            }
829
+            if (!pkt_attrs[OVPN_PACKET_ATTR_PEER_ID])
830
+            {
831
+                msg(D_DCO, "ovpn-dco: Received OVPN_CMD_PACKET message without peer id");
832
+                return NL_SKIP;
833
+            }
834
+            if (!pkt_attrs[OVPN_PACKET_ATTR_PACKET])
835
+            {
836
+                msg(D_DCO, "ovpn-dco: Received OVPN_CMD_PACKET message without packet");
837
+                return NL_SKIP;
838
+            }
839
+
840
+            unsigned int peerid = nla_get_u32(pkt_attrs[OVPN_PACKET_ATTR_PEER_ID]);
841
+
842
+            uint8_t *data = nla_data(pkt_attrs[OVPN_PACKET_ATTR_PACKET]);
843
+            int len = nla_len(pkt_attrs[OVPN_PACKET_ATTR_PACKET]);
844
+
845
+            msg(D_DCO_DEBUG, "ovpn-dco: received OVPN_PACKET_ATTR_PACKET, ifindex: %d peer-id: %d, len %d",
846
+                ifindex, peerid, len);
847
+            if (BLEN(&dco->dco_packet_in) > 0)
848
+            {
849
+                msg(D_DCO, "DCO packet buffer still full?!");
850
+                return NL_SKIP;
851
+            }
852
+            buf_init(&dco->dco_packet_in, 0);
853
+            buf_write(&dco->dco_packet_in, data, len);
854
+            dco->dco_message_peer_id = peerid;
855
+            dco->dco_message_type = OVPN_CMD_PACKET;
856
+            break;
857
+        }
858
+
859
+        default:
860
+            msg(D_DCO, "ovpn-dco: received unknown command: %d", gnlh->cmd);
861
+            dco->dco_message_type = 0;
862
+            return NL_SKIP;
863
+    }
864
+
865
+    return NL_OK;
866
+}
867
+
868
+int
869
+dco_do_read(dco_context_t *dco)
870
+{
871
+    msg(D_DCO_DEBUG, __func__);
872
+    nl_cb_set(dco->nl_cb, NL_CB_VALID, NL_CB_CUSTOM, ovpn_handle_msg, dco);
873
+
874
+    return ovpn_nl_recvmsgs(dco, __func__);
875
+}
876
+
877
+int
878
+dco_do_write(dco_context_t *dco, int peer_id, struct buffer *buf)
879
+{
880
+    packet_size_type len = BLEN(buf);
881
+    dmsg(D_STREAM_DEBUG, "DCO: WRITE %d offset=%d", (int)len, buf->offset);
882
+
883
+    msg(D_DCO_DEBUG, "%s: peer-id %d, len=%d", __func__, peer_id, len);
884
+
885
+    struct nl_msg *nl_msg = ovpn_dco_nlmsg_create(dco, OVPN_CMD_PACKET);
886
+
887
+    if (!nl_msg)
888
+    {
889
+        return -ENOMEM;
890
+    }
891
+
892
+    struct nlattr *attr = nla_nest_start(nl_msg, OVPN_ATTR_PACKET);
893
+    int ret = -EMSGSIZE;
894
+    NLA_PUT_U32(nl_msg, OVPN_PACKET_ATTR_PEER_ID, peer_id);
895
+    NLA_PUT(nl_msg, OVPN_PACKET_ATTR_PACKET, len, BSTR(buf));
896
+    nla_nest_end(nl_msg, attr);
897
+
898
+    ret = ovpn_nl_msg_send(dco, nl_msg, NULL, __func__);
899
+    if (ret)
900
+    {
901
+        goto nla_put_failure;
902
+    }
903
+
904
+    /* return the length of the written data in case of success */
905
+    ret = len;
906
+
907
+nla_put_failure:
908
+    nlmsg_free(nl_msg);
909
+    return ret;
910
+}
911
+
912
+bool
913
+dco_available(int msglevel)
914
+{
915
+    if (resolve_ovpn_netlink_id(msglevel) < 0)
916
+    {
917
+        msg(msglevel,
918
+            "Note: Kernel support for ovpn-dco missing, disabling data channel offload.");
919
+        return false;
920
+    }
921
+    return true;
922
+}
923
+
924
+void
925
+dco_event_set(dco_context_t *dco, struct event_set *es, void *arg)
926
+{
927
+    if (dco && dco->nl_sock)
928
+    {
929
+        event_ctl(es, nl_socket_get_fd(dco->nl_sock), EVENT_READ, arg);
930
+    }
931
+}
932
+
933
+#endif /* defined(ENABLE_DCO) && defined(TARGET_LINUX) */
0 934
new file mode 100644
... ...
@@ -0,0 +1,60 @@
0
+/*
1
+ *  Interface to linux dco networking code
2
+ *
3
+ *  Copyright (C) 2020-2022 Antonio Quartulli <a@unstable.cc>
4
+ *  Copyright (C) 2020-2022 Arne Schwabe <arne@rfc2549.org>
5
+ *  Copyright (C) 2020-2022 OpenVPN Inc <sales@openvpn.net>
6
+ *
7
+ *  This program is free software; you can redistribute it and/or modify
8
+ *  it under the terms of the GNU General Public License version 2
9
+ *  as published by the Free Software Foundation.
10
+ *
11
+ *  This program is distributed in the hope that it will be useful,
12
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
+ *  GNU General Public License for more details.
15
+ *
16
+ *  You should have received a copy of the GNU General Public License
17
+ *  along with this program (see the file COPYING included with this
18
+ *  distribution); if not, write to the Free Software Foundation, Inc.,
19
+ *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20
+ */
21
+#ifndef DCO_LINUX_H
22
+#define DCO_LINUX_H
23
+
24
+#if defined(ENABLE_DCO) && defined(TARGET_LINUX)
25
+
26
+#include "event.h"
27
+
28
+#include "ovpn_dco_linux.h"
29
+
30
+#include <netlink/socket.h>
31
+#include <netlink/netlink.h>
32
+
33
+typedef enum ovpn_key_slot dco_key_slot_t;
34
+typedef enum ovpn_cipher_alg dco_cipher_t;
35
+
36
+#define DCO_SUPPORTED_CIPHERS "AES-128-GCM:AES-256-GCM:AES-192-GCM:CHACHA20-POLY1305"
37
+
38
+typedef struct
39
+{
40
+    struct nl_sock *nl_sock;
41
+    struct nl_cb *nl_cb;
42
+    int status;
43
+
44
+    enum ovpn_mode ifmode;
45
+
46
+    int ovpn_dco_id;
47
+    int ovpn_dco_mcast_id;
48
+
49
+    unsigned int ifindex;
50
+
51
+    struct buffer dco_packet_in;
52
+
53
+    int dco_message_type;
54
+    int dco_message_peer_id;
55
+    int dco_del_peer_reason;
56
+} dco_context_t;
57
+
58
+#endif /* defined(ENABLE_DCO) && defined(TARGET_LINUX) */
59
+#endif /* ifndef DCO_LINUX_H */
... ...
@@ -91,6 +91,7 @@
91 91
 #define D_OSBUF              LOGLEV(3, 43, 0)        /* show socket/tun/tap buffer sizes */
92 92
 #define D_PS_PROXY           LOGLEV(3, 44, 0)        /* messages related to --port-share option */
93 93
 #define D_IFCONFIG           LOGLEV(3, 0,  0)        /* show ifconfig info (don't mute) */
94
+#define D_DCO                LOGLEV(3, 0, 0)         /* show DCO related messages */
94 95
 
95 96
 #define D_SHOW_PARMS         LOGLEV(4, 50, 0)        /* show all parameters on program initiation */
96 97
 #define D_SHOW_OCC           LOGLEV(4, 51, 0)        /* show options compatibility string */
... ...
@@ -114,6 +115,7 @@
114 114
 #define D_TAP_WIN_DEBUG      LOGLEV(6, 69, M_DEBUG)  /* show TAP-Windows driver debug info */
115 115
 #define D_CLIENT_NAT         LOGLEV(6, 69, M_DEBUG)  /* show client NAT debug info */
116 116
 #define D_XKEY               LOGLEV(6, 69, M_DEBUG)  /* show xkey-provider debug info */
117
+#define D_DCO_DEBUG          LOGLEV(6, 69, M_DEBUG)  /* show DCO related lowlevel debug messages */
117 118
 
118 119
 #define D_SHOW_KEYS          LOGLEV(7, 70, M_DEBUG)  /* show data channel encryption keys */
119 120
 #define D_SHOW_KEY_SOURCE    LOGLEV(7, 70, M_DEBUG)  /* show data channel key source entropy */
... ...
@@ -276,9 +276,10 @@
276 276
     <ClCompile Include="crypto.c" />
277 277
     <ClCompile Include="crypto_openssl.c" />
278 278
     <ClCompile Include="cryptoapi.c" />
279
-    <ClCompile Include="env_set.c" />
279
+    <ClCompile Include="dco_linux.c" />
280 280
     <ClCompile Include="dhcp.c" />
281 281
     <ClCompile Include="dns.c" />
282
+    <ClCompile Include="env_set.c" />
282 283
     <ClCompile Include="error.c" />
283 284
     <ClCompile Include="event.c" />
284 285
     <ClCompile Include="fdmisc.c" />
... ...
@@ -362,6 +363,9 @@
362 362
     <ClInclude Include="crypto_backend.h" />
363 363
     <ClInclude Include="crypto_openssl.h" />
364 364
     <ClInclude Include="cryptoapi.h" />
365
+    <ClInclude Include="dco.h" />
366
+    <ClInclude Include="dco_internal.h" />
367
+    <ClInclude Include="dco_linux.h" />
365 368
     <ClInclude Include="dhcp.h" />
366 369
     <ClInclude Include="dns.h" />
367 370
     <ClInclude Include="env_set.h" />
... ...
@@ -396,6 +400,7 @@
396 396
     <ClInclude Include="openvpn.h" />
397 397
     <ClInclude Include="options.h" />
398 398
     <ClInclude Include="otime.h" />
399
+    <ClInclude Include="ovpn_dco_linux.h" />
399 400
     <ClInclude Include="packet_id.h" />
400 401
     <ClInclude Include="perf.h" />
401 402
     <ClInclude Include="ping.h" />
... ...
@@ -36,6 +36,9 @@
36 36
     <ClCompile Include="cryptoapi.c">
37 37
       <Filter>Source Files</Filter>
38 38
     </ClCompile>
39
+    <ClCompile Include="dco_linux.c">
40
+      <Filter>Source Files</Filter>
41
+    </ClCompile>
39 42
     <ClCompile Include="dhcp.c">
40 43
       <Filter>Source Files</Filter>
41 44
     </ClCompile>
... ...
@@ -299,6 +302,15 @@
299 299
     <ClInclude Include="cryptoapi.h">
300 300
       <Filter>Header Files</Filter>
301 301
     </ClInclude>
302
+    <ClCompile Include="dco.h">
303
+      <Filter>Header Files</Filter>
304
+    </ClInclude>
305
+    <ClInclude Include="dco_internal.h">
306
+      <Filter>Header Files</Filter>
307
+    </ClInclude>
308
+    <ClInclude Include="dco_linux.h">
309
+      <Filter>Header Files</Filter>
310
+    </ClInclude>
302 311
     <ClInclude Include="dhcp.h">
303 312
       <Filter>Header Files</Filter>
304 313
     </ClInclude>
... ...
@@ -398,6 +410,9 @@
398 398
     <ClInclude Include="otime.h">
399 399
       <Filter>Header Files</Filter>
400 400
     </ClInclude>
401
+    <ClInclude Include="ovpn_dco_linux.h">
402
+      <Filter>Header Files</Filter>
403
+    </ClInclude>
401 404
     <ClInclude Include="packet_id.h">
402 405
       <Filter>Header Files</Filter>
403 406
     </ClInclude>
404 407
new file mode 100644
... ...
@@ -0,0 +1,265 @@
0
+/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */
1
+/*
2
+ *  OpenVPN data channel accelerator
3
+ *
4
+ *  Copyright (C) 2019-2021 OpenVPN, Inc.
5
+ *
6
+ *  Author:	James Yonan <james@openvpn.net>
7
+ *		Antonio Quartulli <antonio@openvpn.net>
8
+ */
9
+
10
+#ifndef _UAPI_LINUX_OVPN_DCO_H_
11
+#define _UAPI_LINUX_OVPN_DCO_H_
12
+
13
+#define OVPN_NL_NAME "ovpn-dco"
14
+
15
+#define OVPN_NL_MULTICAST_GROUP_PEERS "peers"
16
+
17
+/**
18
+ * enum ovpn_nl_commands - supported netlink commands
19
+ */
20
+enum ovpn_nl_commands {
21
+	/**
22
+	 * @OVPN_CMD_UNSPEC: unspecified command to catch errors
23
+	 */
24
+	OVPN_CMD_UNSPEC = 0,
25
+
26
+	/**
27
+	 * @OVPN_CMD_NEW_PEER: Configure peer with its crypto keys
28
+	 */
29
+	OVPN_CMD_NEW_PEER,
30
+
31
+	/**
32
+	 * @OVPN_CMD_SET_PEER: Tweak parameters for an existing peer
33
+	 */
34
+	OVPN_CMD_SET_PEER,
35
+
36
+	/**
37
+	 * @OVPN_CMD_DEL_PEER: Remove peer from internal table
38
+	 */
39
+	OVPN_CMD_DEL_PEER,
40
+
41
+	OVPN_CMD_NEW_KEY,
42
+
43
+	OVPN_CMD_SWAP_KEYS,
44
+
45
+	OVPN_CMD_DEL_KEY,
46
+
47
+	/**
48
+	 * @OVPN_CMD_REGISTER_PACKET: Register for specific packet types to be
49
+	 * forwarded to userspace
50
+	 */
51
+	OVPN_CMD_REGISTER_PACKET,
52
+
53
+	/**
54
+	 * @OVPN_CMD_PACKET: Send a packet from userspace to kernelspace. Also
55
+	 * used to send to userspace packets for which a process had registered
56
+	 * with OVPN_CMD_REGISTER_PACKET
57
+	 */
58
+	OVPN_CMD_PACKET,
59
+
60
+	/**
61
+	 * @OVPN_CMD_GET_PEER: Retrieve the status of a peer or all peers
62
+	 */
63
+	OVPN_CMD_GET_PEER,
64
+};
65
+
66
+enum ovpn_cipher_alg {
67
+	/**
68
+	 * @OVPN_CIPHER_ALG_NONE: No encryption - reserved for debugging only
69
+	 */
70
+	OVPN_CIPHER_ALG_NONE = 0,
71
+	/**
72
+	 * @OVPN_CIPHER_ALG_AES_GCM: AES-GCM AEAD cipher with any allowed key size
73
+	 */
74
+	OVPN_CIPHER_ALG_AES_GCM,
75
+	/**
76
+	 * @OVPN_CIPHER_ALG_CHACHA20_POLY1305: ChaCha20Poly1305 AEAD cipher
77
+	 */
78
+	OVPN_CIPHER_ALG_CHACHA20_POLY1305,
79
+};
80
+
81
+enum ovpn_del_peer_reason {
82
+	__OVPN_DEL_PEER_REASON_FIRST,
83
+	OVPN_DEL_PEER_REASON_TEARDOWN = __OVPN_DEL_PEER_REASON_FIRST,
84
+	OVPN_DEL_PEER_REASON_USERSPACE,
85
+	OVPN_DEL_PEER_REASON_EXPIRED,
86
+	OVPN_DEL_PEER_REASON_TRANSPORT_ERROR,
87
+	__OVPN_DEL_PEER_REASON_AFTER_LAST
88
+};
89
+
90
+enum ovpn_key_slot {
91
+	__OVPN_KEY_SLOT_FIRST,
92
+	OVPN_KEY_SLOT_PRIMARY = __OVPN_KEY_SLOT_FIRST,
93
+	OVPN_KEY_SLOT_SECONDARY,
94
+	__OVPN_KEY_SLOT_AFTER_LAST,
95
+};
96
+
97
+enum ovpn_netlink_attrs {
98
+	OVPN_ATTR_UNSPEC = 0,
99
+	OVPN_ATTR_IFINDEX,
100
+	OVPN_ATTR_NEW_PEER,
101
+	OVPN_ATTR_SET_PEER,
102
+	OVPN_ATTR_DEL_PEER,
103
+	OVPN_ATTR_NEW_KEY,
104
+	OVPN_ATTR_SWAP_KEYS,
105
+	OVPN_ATTR_DEL_KEY,
106
+	OVPN_ATTR_PACKET,
107
+	OVPN_ATTR_GET_PEER,
108
+
109
+	__OVPN_ATTR_AFTER_LAST,
110
+	OVPN_ATTR_MAX = __OVPN_ATTR_AFTER_LAST - 1,
111
+};
112
+
113
+enum ovpn_netlink_key_dir_attrs {
114
+	OVPN_KEY_DIR_ATTR_UNSPEC = 0,
115
+	OVPN_KEY_DIR_ATTR_CIPHER_KEY,
116
+	OVPN_KEY_DIR_ATTR_NONCE_TAIL,
117
+
118
+	__OVPN_KEY_DIR_ATTR_AFTER_LAST,
119
+	OVPN_KEY_DIR_ATTR_MAX = __OVPN_KEY_DIR_ATTR_AFTER_LAST - 1,
120
+};
121
+
122
+enum ovpn_netlink_new_key_attrs {
123
+	OVPN_NEW_KEY_ATTR_UNSPEC = 0,
124
+	OVPN_NEW_KEY_ATTR_PEER_ID,
125
+	OVPN_NEW_KEY_ATTR_KEY_SLOT,
126
+	OVPN_NEW_KEY_ATTR_KEY_ID,
127
+	OVPN_NEW_KEY_ATTR_CIPHER_ALG,
128
+	OVPN_NEW_KEY_ATTR_ENCRYPT_KEY,
129
+	OVPN_NEW_KEY_ATTR_DECRYPT_KEY,
130
+
131
+	__OVPN_NEW_KEY_ATTR_AFTER_LAST,
132
+	OVPN_NEW_KEY_ATTR_MAX = __OVPN_NEW_KEY_ATTR_AFTER_LAST - 1,
133
+};
134
+
135
+enum ovpn_netlink_del_key_attrs {
136
+	OVPN_DEL_KEY_ATTR_UNSPEC = 0,
137
+	OVPN_DEL_KEY_ATTR_PEER_ID,
138
+	OVPN_DEL_KEY_ATTR_KEY_SLOT,
139
+
140
+	__OVPN_DEL_KEY_ATTR_AFTER_LAST,
141
+	OVPN_DEL_KEY_ATTR_MAX = __OVPN_DEL_KEY_ATTR_AFTER_LAST - 1,
142
+};
143
+
144
+enum ovpn_netlink_swap_keys_attrs {
145
+	OVPN_SWAP_KEYS_ATTR_UNSPEC = 0,
146
+	OVPN_SWAP_KEYS_ATTR_PEER_ID,
147
+
148
+	__OVPN_SWAP_KEYS_ATTR_AFTER_LAST,
149
+	OVPN_SWAP_KEYS_ATTR_MAX = __OVPN_SWAP_KEYS_ATTR_AFTER_LAST - 1,
150
+
151
+};
152
+
153
+enum ovpn_netlink_new_peer_attrs {
154
+	OVPN_NEW_PEER_ATTR_UNSPEC = 0,
155
+	OVPN_NEW_PEER_ATTR_PEER_ID,
156
+	OVPN_NEW_PEER_ATTR_SOCKADDR_REMOTE,
157
+	OVPN_NEW_PEER_ATTR_SOCKET,
158
+	OVPN_NEW_PEER_ATTR_IPV4,
159
+	OVPN_NEW_PEER_ATTR_IPV6,
160
+	OVPN_NEW_PEER_ATTR_LOCAL_IP,
161
+
162
+	__OVPN_NEW_PEER_ATTR_AFTER_LAST,
163
+	OVPN_NEW_PEER_ATTR_MAX = __OVPN_NEW_PEER_ATTR_AFTER_LAST - 1,
164
+};
165
+
166
+enum ovpn_netlink_set_peer_attrs {
167
+	OVPN_SET_PEER_ATTR_UNSPEC = 0,
168
+	OVPN_SET_PEER_ATTR_PEER_ID,
169
+	OVPN_SET_PEER_ATTR_KEEPALIVE_INTERVAL,
170
+	OVPN_SET_PEER_ATTR_KEEPALIVE_TIMEOUT,
171
+
172
+	__OVPN_SET_PEER_ATTR_AFTER_LAST,
173
+	OVPN_SET_PEER_ATTR_MAX = __OVPN_SET_PEER_ATTR_AFTER_LAST - 1,
174
+};
175
+
176
+enum ovpn_netlink_del_peer_attrs {
177
+	OVPN_DEL_PEER_ATTR_UNSPEC = 0,
178
+	OVPN_DEL_PEER_ATTR_REASON,
179
+	OVPN_DEL_PEER_ATTR_PEER_ID,
180
+
181
+	__OVPN_DEL_PEER_ATTR_AFTER_LAST,
182
+	OVPN_DEL_PEER_ATTR_MAX = __OVPN_DEL_PEER_ATTR_AFTER_LAST - 1,
183
+};
184
+
185
+enum ovpn_netlink_get_peer_attrs {
186
+	OVPN_GET_PEER_ATTR_UNSPEC = 0,
187
+	OVPN_GET_PEER_ATTR_PEER_ID,
188
+
189
+	__OVPN_GET_PEER_ATTR_AFTER_LAST,
190
+	OVPN_GET_PEER_ATTR_MAX = __OVPN_GET_PEER_ATTR_AFTER_LAST - 1,
191
+};
192
+
193
+enum ovpn_netlink_get_peer_response_attrs {
194
+	OVPN_GET_PEER_RESP_ATTR_UNSPEC = 0,
195
+	OVPN_GET_PEER_RESP_ATTR_PEER_ID,
196
+	OVPN_GET_PEER_RESP_ATTR_SOCKADDR_REMOTE,
197
+	OVPN_GET_PEER_RESP_ATTR_IPV4,
198
+	OVPN_GET_PEER_RESP_ATTR_IPV6,
199
+	OVPN_GET_PEER_RESP_ATTR_LOCAL_IP,
200
+	OVPN_GET_PEER_RESP_ATTR_LOCAL_PORT,
201
+	OVPN_GET_PEER_RESP_ATTR_KEEPALIVE_INTERVAL,
202
+	OVPN_GET_PEER_RESP_ATTR_KEEPALIVE_TIMEOUT,
203
+	OVPN_GET_PEER_RESP_ATTR_RX_BYTES,
204
+	OVPN_GET_PEER_RESP_ATTR_TX_BYTES,
205
+	OVPN_GET_PEER_RESP_ATTR_RX_PACKETS,
206
+	OVPN_GET_PEER_RESP_ATTR_TX_PACKETS,
207
+
208
+	__OVPN_GET_PEER_RESP_ATTR_AFTER_LAST,
209
+	OVPN_GET_PEER_RESP_ATTR_MAX = __OVPN_GET_PEER_RESP_ATTR_AFTER_LAST - 1,
210
+};
211
+
212
+enum ovpn_netlink_peer_stats_attrs {
213
+	OVPN_PEER_STATS_ATTR_UNSPEC = 0,
214
+	OVPN_PEER_STATS_BYTES,
215
+	OVPN_PEER_STATS_PACKETS,
216
+
217
+	__OVPN_PEER_STATS_ATTR_AFTER_LAST,
218
+	OVPN_PEER_STATS_ATTR_MAX = __OVPN_PEER_STATS_ATTR_AFTER_LAST - 1,
219
+};
220
+
221
+enum ovpn_netlink_peer_attrs {
222
+	OVPN_PEER_ATTR_UNSPEC = 0,
223
+	OVPN_PEER_ATTR_PEER_ID,
224
+	OVPN_PEER_ATTR_SOCKADDR_REMOTE,
225
+	OVPN_PEER_ATTR_IPV4,
226
+	OVPN_PEER_ATTR_IPV6,
227
+	OVPN_PEER_ATTR_LOCAL_IP,
228
+	OVPN_PEER_ATTR_KEEPALIVE_INTERVAL,
229
+	OVPN_PEER_ATTR_KEEPALIVE_TIMEOUT,
230
+	OVPN_PEER_ATTR_ENCRYPT_KEY,
231
+	OVPN_PEER_ATTR_DECRYPT_KEY,
232
+	OVPN_PEER_ATTR_RX_STATS,
233
+	OVPN_PEER_ATTR_TX_STATS,
234
+
235
+	__OVPN_PEER_ATTR_AFTER_LAST,
236
+	OVPN_PEER_ATTR_MAX = __OVPN_PEER_ATTR_AFTER_LAST - 1,
237
+};
238
+
239
+enum ovpn_netlink_packet_attrs {
240
+	OVPN_PACKET_ATTR_UNSPEC = 0,
241
+	OVPN_PACKET_ATTR_PACKET,
242
+	OVPN_PACKET_ATTR_PEER_ID,
243
+
244
+	__OVPN_PACKET_ATTR_AFTER_LAST,
245
+	OVPN_PACKET_ATTR_MAX = __OVPN_PACKET_ATTR_AFTER_LAST - 1,
246
+};
247
+
248
+enum ovpn_ifla_attrs {
249
+	IFLA_OVPN_UNSPEC = 0,
250
+	IFLA_OVPN_MODE,
251
+
252
+	__IFLA_OVPN_AFTER_LAST,
253
+	IFLA_OVPN_MAX = __IFLA_OVPN_AFTER_LAST - 1,
254
+};
255
+
256
+enum ovpn_mode {
257
+	__OVPN_MODE_FIRST = 0,
258
+	OVPN_MODE_P2P = __OVPN_MODE_FIRST,
259
+	OVPN_MODE_MP,
260
+
261
+	__OVPN_MODE_AFTER_LAST,
262
+};
263
+
264
+#endif /* _UAPI_LINUX_OVPN_DCO_H_ */
... ...
@@ -40,6 +40,7 @@
40 40
 #include "misc.h"
41 41
 #include "networking.h"
42 42
 #include "ring_buffer.h"
43
+#include "dco.h"
43 44
 
44 45
 #ifdef _WIN32
45 46
 #define WINTUN_COMPONENT_ID "wintun"
... ...
@@ -214,6 +215,8 @@ struct tuntap
214 214
 #endif
215 215
     /* used for printing status info only */
216 216
     unsigned int rwflags_debug;
217
+
218
+    dco_context_t dco;
217 219
 };
218 220
 
219 221
 static inline bool