Skip to content

Commit

Permalink
Implement in-tree build for DCO with proto fix
Browse files Browse the repository at this point in the history
The initialization hack for ovpn_tcp_prot doesn't work when the
tcp_prot struct is constified (by Grsec/PaX, for instance) and is
generally unsafe as it performs runtime function hooking/hijack
to achieve its goal.

The correct way to set up the structure requires access to symbols
not exported by the kernel's headers, which makes out-of-tree
compilation with the appropriate initializer impossible. In-tree
builds can also benefit from LTO and other toolchain optimizations,
as well as become part of monolithic kernels which do not use
modules such as long-running network devices.

Notes:
  Built and run using GCC 13 for Grsecurity/PaX (stable8) with all
features except PRIVKSTACK and KERNSEAL enabled - x86_64 only.
  Built using LLVM17 with LTO & kCFI on GrapheneOS' linux-hardened
patchset with their default features enabled - x86_64 only.
  • Loading branch information
RageLtMan committed Sep 14, 2023
1 parent dba96d2 commit 6f4fea9
Show file tree
Hide file tree
Showing 3 changed files with 239 additions and 0 deletions.
36 changes: 36 additions & 0 deletions in-tree/config.diff
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 9d4ab0c134ea..ca3462dd7322 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -115,6 +115,19 @@ config WIREGUARD_DEBUG

Say N here unless you know what you're doing.

+config OVPN_DCO_V2
+ tristate "OpenVPN data channel offload (reloaded)"
+ depends on NET && INET
+ select NET_UDP_TUNNEL
+ select DST_CACHE
+ select CRYPTO
+ select CRYPTO_AES
+ select CRYPTO_GCM
+ select CRYPTO_CHACHA20POLY1305
+ help
+ This module enhances the performance of the OpenVPN userspace software
+ by offloading the data channel processing to kernelspace.
+
config EQUALIZER
tristate "EQL (serial line load balancing) support"
help
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 913fdf988281..7447bd47459b 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -11,6 +11,7 @@ obj-$(CONFIG_IPVLAN) += ipvlan/
obj-$(CONFIG_IPVTAP) += ipvlan/
obj-$(CONFIG_DUMMY) += dummy.o
obj-$(CONFIG_WIREGUARD) += wireguard/
+obj-$(CONFIG_OVPN_DCO_V2) += ovpn-dco/
obj-$(CONFIG_EQUALIZER) += eql.o
obj-$(CONFIG_IFB) += ifb.o
obj-$(CONFIG_MACSEC) += macsec.o
39 changes: 39 additions & 0 deletions in-tree/patch_kernel_tree.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#!/bin/bash

# Copy ovpn_dco_v2 source and headers into kernel tree
# Apply diff to relevant configuration files for in-tree builds
# Set KDIR to appropriate source tree and run this script

KDIR="${KDIR:="/usr/src/linux"}"
COMMIT="$(git log|head -1|cut -d' ' -f2|cut -c 1-8)"
DIFFDIR="$(cd $( dirname "${BASH_SOURCE[0]}") && pwd )"
echo "Patching kernel in $KDIR from $DIFFDIR @ $COMMIT"

if [[ ! -d "$KDIR" ]]; then
echo "KDIR improperly set"
exit 1
else
if [[ ! -f "$KDIR/Kconfig" ]]; then
echo "$KDIR does not appear to be a kernel tree"
exit 1
fi

cd "$DIFFDIR"
cp -r ../drivers/net/ovpn-dco "$KDIR/net/"
cp -r ../include/* "$KDIR/include/"

cd "$KDIR"
if [[ $(patch -p1 -i "$DIFFDIR/config.diff" -i "$DIFFDIR/proto.diff") ]]; then
if [[ -d "$KDIR/.git" ]]; then
git add "$KDIR/drivers/net"
git add "$KDIR/include"
git commit -am "OVPN DCO: in-tree @ $COMMIT"
fi
echo "Update kernel build configuration to enable OVPN_DCO module"
exit 0
else
echo "Failed to patch kernel tree, review output and PR a fix please"
exit 1
fi
fi

164 changes: 164 additions & 0 deletions in-tree/proto.diff
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
diff --git a/drivers/net/ovpn-dco/main.c b/drivers/net/ovpn-dco/main.c
index 810ac8024fde..2c724e16b997 100644
--- a/drivers/net/ovpn-dco/main.c
+++ b/drivers/net/ovpn-dco/main.c
@@ -217,7 +217,6 @@ static int __init ovpn_init(void)

pr_info("%s %s -- %s\n", DRV_DESCRIPTION, DRV_VERSION, DRV_COPYRIGHT);

- err = ovpn_tcp_init();
if (err) {
pr_err("ovpn: can't initialize TCP subsystem\n");
goto err;
diff --git a/drivers/net/ovpn-dco/tcp.c b/drivers/net/ovpn-dco/tcp.c
index 288a69101016..f8ef48c48299 100644
--- a/drivers/net/ovpn-dco/tcp.c
+++ b/drivers/net/ovpn-dco/tcp.c
@@ -19,8 +19,6 @@
#include <net/tcp.h>
#include <net/route.h>

-static struct proto ovpn_tcp_prot;
-
static int ovpn_tcp_read_sock(read_descriptor_t *desc, struct sk_buff *in_skb,
unsigned int in_offset, size_t in_len)
{
@@ -271,6 +269,59 @@ static int ovpn_tcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
return ret ? : -EAGAIN;
}

+static struct proto ovpn_tcp_prot __ro_after_init = {
+ .name = "TCP",
+ .owner = THIS_MODULE,
+ .close = tcp_close,
+ .pre_connect = tcp_v4_pre_connect,
+ .connect = tcp_v4_connect,
+ .disconnect = tcp_disconnect,
+ .accept = inet_csk_accept,
+ .ioctl = tcp_ioctl,
+ .init = tcp_v4_init_sock,
+ .destroy = tcp_v4_destroy_sock,
+ .shutdown = tcp_shutdown,
+ .setsockopt = tcp_setsockopt,
+ .getsockopt = tcp_getsockopt,
+ .bpf_bypass_getsockopt = tcp_bpf_bypass_getsockopt,
+ .keepalive = tcp_set_keepalive,
+ .recvmsg = ovpn_tcp_recvmsg,
+ .sendmsg = tcp_sendmsg,
+ .sendpage = tcp_sendpage,
+ .backlog_rcv = tcp_v4_do_rcv,
+ .release_cb = tcp_release_cb,
+ .hash = inet_hash,
+ .unhash = inet_unhash,
+ .get_port = inet_csk_get_port,
+ .put_port = inet_put_port,
+#ifdef CONFIG_BPF_SYSCALL
+ .psock_update_sk_prot = tcp_bpf_update_proto,
+#endif
+ .enter_memory_pressure = tcp_enter_memory_pressure,
+ .leave_memory_pressure = tcp_leave_memory_pressure,
+ .stream_memory_free = tcp_stream_memory_free,
+ .sockets_allocated = &tcp_sockets_allocated,
+ .orphan_count = &tcp_orphan_count,
+
+ .memory_allocated = &tcp_memory_allocated,
+ .per_cpu_fw_alloc = &tcp_memory_per_cpu_fw_alloc,
+
+ .memory_pressure = &tcp_memory_pressure,
+ .sysctl_mem = sysctl_tcp_mem,
+ .sysctl_wmem_offset = offsetof(struct net, ipv4.sysctl_tcp_wmem),
+ .sysctl_rmem_offset = offsetof(struct net, ipv4.sysctl_tcp_rmem),
+ .max_header = MAX_TCP_HEADER,
+ .typename = "tcp_sock",
+ .obj_size = sizeof(struct tcp_sock),
+ .slab_flags = SLAB_TYPESAFE_BY_RCU,
+ .twsk_prot = &tcp_timewait_sock_ops,
+ .rsk_prot = &tcp_request_sock_ops,
+ .h.hashinfo = NULL,
+ .no_autobind = true,
+ .diag_destroy = tcp_abort,
+ .sock_is_readable = ovpn_tcp_sock_is_readable,
+};
+
static void ovpn_destroy_skb(void *skb)
{
consume_skb(skb);
@@ -455,24 +506,3 @@ int ovpn_tcp_socket_attach(struct socket *sock, struct ovpn_peer *peer)

return ret;
}
-
-int __init ovpn_tcp_init(void)
-{
- /* We need to substitute the recvmsg and the sock_is_readable
- * callbacks in the sk_prot member of the sock object for TCP
- * sockets.
- *
- * However sock->sk_prot is a pointer to a static variable and
- * therefore we can't directly modify it, otherwise every socket
- * pointing to it will be affected.
- *
- * For this reason we create our own static copy and modify what
- * we need. Then we make sk_prot point to this copy
- * (in ovpn_tcp_socket_attach())
- */
- ovpn_tcp_prot = tcp_prot;
- ovpn_tcp_prot.recvmsg = ovpn_tcp_recvmsg;
- ovpn_tcp_prot.sock_is_readable = ovpn_tcp_sock_is_readable;
-
- return 0;
-}
diff --git a/drivers/net/ovpn-dco/tcp.h b/drivers/net/ovpn-dco/tcp.h
index 7f0e4ec826ad..fdb8e5e48b4b 100644
--- a/drivers/net/ovpn-dco/tcp.h
+++ b/drivers/net/ovpn-dco/tcp.h
@@ -16,9 +16,6 @@
#include <linux/types.h>
#include <linux/workqueue.h>

-/* Initialize TCP static objects */
-int __init ovpn_tcp_init(void);
-
void ovpn_queue_tcp_skb(struct ovpn_peer *peer, struct sk_buff *skb);

int ovpn_tcp_socket_attach(struct socket *sock, struct ovpn_peer *peer);
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 1d1163cc86c5..a0fccca41bf6 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -197,6 +197,8 @@ int tcp_v4_pre_connect(struct sock *sk, struct sockaddr *uaddr,
return BPF_CGROUP_RUN_PROG_INET4_CONNECT(sk, uaddr);
}

+EXPORT_SYMBOL(tcp_v4_pre_connect);
+
/* This will initiate an outgoing connection. */
int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
{
@@ -1464,6 +1466,8 @@ struct request_sock_ops tcp_request_sock_ops __read_mostly = {
.syn_ack_timeout = tcp_syn_ack_timeout,
};

+EXPORT_SYMBOL(tcp_request_sock_ops);
+
/* net/mptcp/subflow.c:subflow_request_sock_ipv4_ops */
const struct tcp_request_sock_ops tcp_request_sock_ipv4_ops = {
.mss_clamp = TCP_MSS_DEFAULT,
@@ -2207,6 +2211,8 @@ struct timewait_sock_ops tcp_timewait_sock_ops = {
.twsk_destructor= tcp_twsk_destructor,
};

+EXPORT_SYMBOL(tcp_timewait_sock_ops);
+
void inet_sk_rx_dst_set(struct sock *sk, const struct sk_buff *skb)
{
struct dst_entry *dst = skb_dst(skb);
@@ -2261,6 +2267,8 @@ int tcp_v4_init_sock(struct sock *sk)
return 0;
}

+EXPORT_SYMBOL(tcp_v4_init_sock);
+
void tcp_v4_destroy_sock(struct sock *sk)
{
struct tcp_sock *tp = tcp_sk(sk);

0 comments on commit 6f4fea9

Please sign in to comment.