File ovpn-dco-0.2.20240320~git0.2aa7f93.obscpio of Package ovpn-dco

07070100000000000081A400000000000000000000000165BCF4370000018F000000000000000000000000000000000000002E00000000ovpn-dco-0.2.20240320~git0.2aa7f93/.gitignore/.cache.mk
/compat-autoconf.h
/compat-autoconf.h.tmp
/modules.order
/Module.symvers
/drivers/net/ovpn-dco/ovpn-dco.ko
/drivers/net/ovpn-dco/.ovpn-dco.ko.cmd
/drivers/net/ovpn-dco/ovpn-dco.mod.c
/drivers/net/ovpn-dco/.ovpn-dco.mod.cmd
/drivers/net/ovpn-dco/ovpn-dco.mod
/drivers/net/ovpn-dco/modules.order
/drivers/net/ovpn-dco/*.o
/drivers/net/ovpn-dco/.*.cmd
/.tmp_versions
/tests/ovpn-cli
/.*.cmd
07070100000001000081A400000000000000000000000165BCF43700000570000000000000000000000000000000000000002C00000000ovpn-dco-0.2.20240320~git0.2aa7f93/Makefile# SPDX-License-Identifier: GPL-2.0
# Copyright (C) 2020-2023 OpenVPN, Inc.
#
#  Author:	Antonio Quartulli <antonio@openvpn.net>

PWD:=$(shell pwd)
KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build
ifeq ($(shell cd $(KERNEL_SRC) && pwd),)
$(warning $(KERNEL_SRC) is missing, please set KERNEL_SRC)
endif

export KERNEL_SRC
RM ?= rm -f
CP := cp -fpR
LN := ln -sf
DEPMOD := depmod -a

REVISION= $(shell	if [ -d "$(PWD)/.git" ]; then \
				echo $$(git --git-dir="$(PWD)/.git" describe --always --dirty --match "v*" |sed 's/^v//' 2> /dev/null || echo "[unknown]"); \
			fi)

NOSTDINC_FLAGS += \
	-I$(PWD)/include/ \
	$(CFLAGS) \
	-include $(PWD)/linux-compat.h \
	-I$(PWD)/compat-include/

ifneq ($(REVISION),)
NOSTDINC_FLAGS += -DOVPN_DCO_VERSION=\"$(REVISION)\"
endif

ifeq ($(DEBUG),1)
NOSTDINC_FLAGS += -DDEBUG=1
endif

obj-y += drivers/net/ovpn-dco/
export ovpn-dco-v2-y

BUILD_FLAGS := \
	M=$(PWD) \
	PWD=$(PWD) \
	REVISION=$(REVISION) \
	CONFIG_OVPN_DCO_V2=m \
	INSTALL_MOD_DIR=updates/

all: config
	$(MAKE) -C $(KERNEL_SRC) $(BUILD_FLAGS)	modules

clean:
	$(RM) psk_client
	$(RM) compat-autoconf.h*
	$(MAKE) -C $(KERNEL_SRC) $(BUILD_FLAGS) clean
	$(MAKE) -C tests clean

install: config
	$(MAKE) -C $(KERNEL_SRC) $(BUILD_FLAGS) modules_install
	$(DEPMOD)

config:
	$(PWD)/gen-compat-autoconf.sh $(PWD)/compat-autoconf.h

tests:
	$(MAKE) -C tests

.PHONY: all clean install config tests 
07070100000002000081A400000000000000000000000165BCF43700000F0B000000000000000000000000000000000000002A00000000ovpn-dco-0.2.20240320~git0.2aa7f93/README== OpenVPN Data Channel Offload in the linux kernel (ovpn-dco) ==

This repository contains a linux kernel module implementing the data channel of
the OpenVPN protocol in the linux kernel.

This kernel module allows OpenVPN to offload any data plane management to the
linux kernel, thus allowing it to exploit any Linux low level API, while avoiding
expensive and slow payload transfer between kernel space and user space.

** NOTE **
ovpn-dco is currently under heavy development, therefore neither its userspace API
nor the code itself is considered stable and may change radically over time.

** Kernel compatibility **
ovpn-dco is developed against the latest David Miller's net-next tree.
However, a compat layer is provided to allow people to compile ovpn-dco
against older kernel versions.


== License ==

ovpn-dco is released under the terms of the GPLv2 license.


== Submitting patches ==

Patches for ovpn-dco can be submitted to the openvpn-devel mailing list at
openvpn-devel@lists.sourceforge.net

The patch subject *must* start with "ovpn-dco:". This way patches for this
project can easily be dinstinguished from patches for other projects.
At the same time it is part of the kernel guidelines to have subjects starting
with a prefix identifying the component being modified (ovpn-dco in this case).

To generate patches, please use git-format-patch and git-send-email.


== Building ==

To build the ovpn-dco kernel module, just type:

$ make

in the root folder.
The Makefile will autodetect your running kernel and will try to use its
headers to get the code compiled.

If you want to build ovpn-dco against a kernel different from the one
running on the host, run:

$ make KERNEL_SRC=/path/to/the/kernel/tree

The control is passed to the kernel Makefile, therefore any kernel Makefile
argument can be specified on the command line and it will be passed
automatically.

Once done building, executing the command:

$ make install

will install the ovpn-dco.ko kernel module in the updates/ subfolder of
the kernel modules directory on your system.
It normally means `/lib/modules/$(uname -r)/updates/`.


== Testing ==

A basic pre-shared-key client (called ovpn-cli) is also provided in the
tests/ folder.
It can be compiled by typing:

$ make tests

One way to test ovpn-dco is to run multiple tun interfaces on the same hosts
associated with different network namespaces.
A script that takes care of setting up 2 NS and 2 interfaces is provided at
`tests/netns-test.sh`.

By running this script from the tests folder as follows:

$ cd tests
$ ./netns-test.sh

the host will configure a basic tunnel using a pre-shared key (the ovpn-cli
binary is used for this).

The 2 namespaces are named `peer0` and `peer1`. Each interface is respectively
configured with `5.5.5.1/24` and `5.5.5.2/24`.

At this point it is possible to make a basic ping test by executing:

$ ip netns exec peer0 ping 5.5.5.2

If the command above works, it means that the 2 interfaces are exchanging
traffic properly over the ovpn link.

Note: running kernel must have network namespaces support compiled in, but it
is fairly standard on modern Linux distros.

For reference, a sample kernel config file is provided in tests/qemu/config.net-next.
This config file is used for compiling a minimal kernel based on the net-next tree.


To run net-next, it's better to rely on any VM, so that the developer does not need
to worry about bugs or spurious kernel crashes. For this reason qemu is suggested
(but any other VM is fine too). At `tests/qemu/launch_deb2.sh` a sample script
can be found that shows how qemu can be launched for testing.


== Limitations ==

This is a list of current limitations which are planned to be removed as we move forward:
* Only AEAD mode and 'none' (with no auth) supported
* Only AES-GCM and CHACHA20POLY1305 ciphers supported
07070100000003000041ED00000000000000000000000265BCF43700000000000000000000000000000000000000000000003200000000ovpn-dco-0.2.20240320~git0.2aa7f93/compat-include07070100000004000041ED00000000000000000000000265BCF43700000000000000000000000000000000000000000000003600000000ovpn-dco-0.2.20240320~git0.2aa7f93/compat-include/net07070100000005000081A400000000000000000000000165BCF437000001B7000000000000000000000000000000000000003C00000000ovpn-dco-0.2.20240320~git0.2aa7f93/compat-include/net/gso.h/* SPDX-License-Identifier: GPL-2.0-only */
/* OpenVPN data channel accelerator
 *
 *  Copyright (C) 2023 OpenVPN, Inc.
 *
 *  Author:	Antonio Quartulli <antonio@openvpn.net>
 */

#ifndef _NET_OVPN_COMPAT_NET_GSO_H
#define _NET_OVPN_COMPAT_NET_GSO_H

#include <linux/version.h>

#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 10)
#include_next <net/gso.h>
#else
#include <linux/netdevice.h>
#endif

#endif /* _NET_OVPN_COMPAT_NET_GSO_H */
07070100000006000041ED00000000000000000000000265BCF43700000000000000000000000000000000000000000000002B00000000ovpn-dco-0.2.20240320~git0.2aa7f93/drivers07070100000007000041ED00000000000000000000000265BCF43700000000000000000000000000000000000000000000002F00000000ovpn-dco-0.2.20240320~git0.2aa7f93/drivers/net07070100000008000081A400000000000000000000000165BCF437000001EB000000000000000000000000000000000000003700000000ovpn-dco-0.2.20240320~git0.2aa7f93/drivers/net/Kconfig# SPDX-License-Identifier: GPL-2.0
# Copyright (C) 2020-2023 OpenVPN, Inc.
#
#  Author:	Antonio Quartulli <antonio@openvpn.net>

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.
07070100000009000041ED00000000000000000000000265BCF43700000000000000000000000000000000000000000000003800000000ovpn-dco-0.2.20240320~git0.2aa7f93/drivers/net/ovpn-dco0707010000000A000081A400000000000000000000000165BCF43700000216000000000000000000000000000000000000004100000000ovpn-dco-0.2.20240320~git0.2aa7f93/drivers/net/ovpn-dco/Makefile# SPDX-License-Identifier: GPL-2.0
#
# ovpn-dco -- OpenVPN data channel offload in kernel space
#
# Copyright (C) 2020-2023 OpenVPN, Inc.
#
# Author:	Antonio Quartulli <antonio@openvpn.net>

obj-$(CONFIG_OVPN_DCO_V2) += ovpn-dco-v2.o
ovpn-dco-v2-y += main.o
ovpn-dco-v2-y += bind.o
ovpn-dco-v2-y += crypto.o
ovpn-dco-v2-y += ovpn.o
ovpn-dco-v2-y += peer.o
ovpn-dco-v2-y += sock.o
ovpn-dco-v2-y += stats.o
ovpn-dco-v2-y += netlink.o
ovpn-dco-v2-y += crypto_aead.o
ovpn-dco-v2-y += pktid.o
ovpn-dco-v2-y += tcp.o
ovpn-dco-v2-y += udp.o
0707010000000B000081A400000000000000000000000165BCF43700000358000000000000000000000000000000000000003F00000000ovpn-dco-0.2.20240320~git0.2aa7f93/drivers/net/ovpn-dco/addr.h/* SPDX-License-Identifier: GPL-2.0-only */
/*  OpenVPN data channel accelerator
 *
 *  Copyright (C) 2020-2023 OpenVPN, Inc.
 *
 *  Author:	James Yonan <james@openvpn.net>
 *		Antonio Quartulli <antonio@openvpn.net>
 */

#ifndef _NET_OVPN_DCO_OVPNADDR_H_
#define _NET_OVPN_DCO_OVPNADDR_H_

#include "crypto.h"

#include <linux/jhash.h>
#include <linux/in.h>
#include <linux/in6.h>
#include <net/ipv6.h>

/* our basic transport layer address */
struct ovpn_sockaddr {
	union {
		struct sockaddr_in in4;
		struct sockaddr_in6 in6;
	};
};

/* Translate skb->protocol value to AF_INET or AF_INET6 */
static inline unsigned short skb_protocol_to_family(const struct sk_buff *skb)
{
	switch (skb->protocol) {
	case htons(ETH_P_IP):
		return AF_INET;
	case htons(ETH_P_IPV6):
		return AF_INET6;
	default:
		return 0;
	}
}

#endif /* _NET_OVPN_DCO_OVPNADDR_H_ */
0707010000000C000081A400000000000000000000000165BCF43700000599000000000000000000000000000000000000003F00000000ovpn-dco-0.2.20240320~git0.2aa7f93/drivers/net/ovpn-dco/bind.c// SPDX-License-Identifier: GPL-2.0
/*  OpenVPN data channel accelerator
 *
 *  Copyright (C) 2012-2023 OpenVPN, Inc.
 *
 *  Author:	James Yonan <james@openvpn.net>
 *		Antonio Quartulli <antonio@openvpn.net>
 */

#include "ovpn.h"
#include "bind.h"
#include "peer.h"

#include <linux/in.h>
#include <linux/in6.h>
#include <linux/socket.h>
#include <linux/types.h>

/* Given a remote sockaddr, compute the skb hash
 * and get a dst_entry so we can send packets to the remote.
 * Called from process context or softirq (must be indicated with
 * process_context bool).
 */
struct ovpn_bind *ovpn_bind_from_sockaddr(const struct sockaddr_storage *ss)
{
	struct ovpn_bind *bind;
	size_t sa_len;

	if (ss->ss_family == AF_INET)
		sa_len = sizeof(struct sockaddr_in);
	else if (ss->ss_family == AF_INET6)
		sa_len = sizeof(struct sockaddr_in6);
	else
		return ERR_PTR(-EAFNOSUPPORT);

	bind = kzalloc(sizeof(*bind), GFP_ATOMIC);
	if (unlikely(!bind))
		return ERR_PTR(-ENOMEM);

	memcpy(&bind->sa, ss, sa_len);

	return bind;
}

static void ovpn_bind_release_rcu(struct rcu_head *head)
{
	struct ovpn_bind *bind = container_of(head, struct ovpn_bind, rcu);

	kfree(bind);
}

void ovpn_bind_reset(struct ovpn_peer *peer, struct ovpn_bind *new)
{
	struct ovpn_bind *old;

	spin_lock_bh(&peer->lock);
	old = rcu_replace_pointer(peer->bind, new, true);
	spin_unlock_bh(&peer->lock);

	if (old)
		call_rcu(&old->rcu, ovpn_bind_release_rcu);
}
0707010000000D000081A400000000000000000000000165BCF437000005E1000000000000000000000000000000000000003F00000000ovpn-dco-0.2.20240320~git0.2aa7f93/drivers/net/ovpn-dco/bind.h/* SPDX-License-Identifier: GPL-2.0-only */
/*  OVPN -- OpenVPN protocol accelerator for Linux
 *  Copyright (C) 2012-2023 OpenVPN, Inc.
 *  All rights reserved.
 *  Author: James Yonan <james@openvpn.net>
 */

#ifndef _NET_OVPN_DCO_OVPNBIND_H_
#define _NET_OVPN_DCO_OVPNBIND_H_

#include "addr.h"
#include "rcu.h"

#include <net/ip.h>
#include <linux/skbuff.h>
#include <linux/spinlock.h>

struct ovpn_peer;

struct ovpn_bind {
	struct ovpn_sockaddr sa;  /* remote sockaddr */

	union {
		struct in_addr ipv4;
		struct in6_addr ipv6;
	} local;

	struct rcu_head rcu;
};

static inline bool ovpn_bind_skb_src_match(const struct ovpn_bind *bind, struct sk_buff *skb)
{
	const unsigned short family = skb_protocol_to_family(skb);
	const struct ovpn_sockaddr *sa = &bind->sa;

	if (unlikely(!bind))
		return false;

	if (unlikely(sa->in4.sin_family != family))
		return false;

	switch (family) {
	case AF_INET:
		if (unlikely(sa->in4.sin_addr.s_addr != ip_hdr(skb)->saddr))
			return false;

		if (unlikely(sa->in4.sin_port != udp_hdr(skb)->source))
			return false;
		break;
	case AF_INET6:
		if (unlikely(!ipv6_addr_equal(&sa->in6.sin6_addr, &ipv6_hdr(skb)->saddr)))
			return false;

		if (unlikely(sa->in6.sin6_port != udp_hdr(skb)->source))
			return false;
		break;
	default:
		return false;
	}

	return true;
}

struct ovpn_bind *ovpn_bind_from_sockaddr(const struct sockaddr_storage *sa);
void ovpn_bind_reset(struct ovpn_peer *peer, struct ovpn_bind *bind);

#endif /* _NET_OVPN_DCO_OVPNBIND_H_ */
0707010000000E000081A400000000000000000000000165BCF43700000EF7000000000000000000000000000000000000004100000000ovpn-dco-0.2.20240320~git0.2aa7f93/drivers/net/ovpn-dco/crypto.c// SPDX-License-Identifier: GPL-2.0
/*  OpenVPN data channel accelerator
 *
 *  Copyright (C) 2020-2023 OpenVPN, Inc.
 *
 *  Author:	James Yonan <james@openvpn.net>
 *		Antonio Quartulli <antonio@openvpn.net>
 */

#include "main.h"
#include "crypto_aead.h"
#include "crypto.h"

#include <uapi/linux/ovpn_dco.h>

static void ovpn_ks_destroy_rcu(struct rcu_head *head)
{
	struct ovpn_crypto_key_slot *ks;

	ks = container_of(head, struct ovpn_crypto_key_slot, rcu);
	ovpn_aead_crypto_key_slot_destroy(ks);
}

void ovpn_crypto_key_slot_release(struct kref *kref)
{
	struct ovpn_crypto_key_slot *ks;

	ks = container_of(kref, struct ovpn_crypto_key_slot, refcount);
	call_rcu(&ks->rcu, ovpn_ks_destroy_rcu);
}

/* can only be invoked when all peer references have been dropped (i.e. RCU
 * release routine)
 */
void ovpn_crypto_state_release(struct ovpn_crypto_state *cs)
{
	struct ovpn_crypto_key_slot *ks;

	ks = rcu_access_pointer(cs->primary);
	if (ks) {
		RCU_INIT_POINTER(cs->primary, NULL);
		ovpn_crypto_key_slot_put(ks);
	}

	ks = rcu_access_pointer(cs->secondary);
	if (ks) {
		RCU_INIT_POINTER(cs->secondary, NULL);
		ovpn_crypto_key_slot_put(ks);
	}

	mutex_destroy(&cs->mutex);
}

/* removes the primary key from the crypto context */
void ovpn_crypto_kill_primary(struct ovpn_crypto_state *cs)
{
	struct ovpn_crypto_key_slot *ks;

	mutex_lock(&cs->mutex);
	ks = rcu_replace_pointer(cs->primary, NULL, lockdep_is_held(&cs->mutex));
	ovpn_crypto_key_slot_put(ks);
	mutex_unlock(&cs->mutex);
}

/* Reset the ovpn_crypto_state object in a way that is atomic
 * to RCU readers.
 */
int ovpn_crypto_state_reset(struct ovpn_crypto_state *cs,
			    const struct ovpn_peer_key_reset *pkr)
	__must_hold(cs->mutex)
{
	struct ovpn_crypto_key_slot *old = NULL;
	struct ovpn_crypto_key_slot *new;

	lockdep_assert_held(&cs->mutex);

	new = ovpn_aead_crypto_key_slot_new(&pkr->key);
	if (IS_ERR(new))
		return PTR_ERR(new);

	switch (pkr->slot) {
	case OVPN_KEY_SLOT_PRIMARY:
		old = rcu_replace_pointer(cs->primary, new,
					  lockdep_is_held(&cs->mutex));
		break;
	case OVPN_KEY_SLOT_SECONDARY:
		old = rcu_replace_pointer(cs->secondary, new,
					  lockdep_is_held(&cs->mutex));
		break;
	default:
		goto free_key;
	}

	if (old)
		ovpn_crypto_key_slot_put(old);

	return 0;
free_key:
	ovpn_crypto_key_slot_put(new);
	return -EINVAL;
}

void ovpn_crypto_key_slot_delete(struct ovpn_crypto_state *cs,
				 enum ovpn_key_slot slot)
{
	struct ovpn_crypto_key_slot *ks = NULL;

	mutex_lock(&cs->mutex);
	switch (slot) {
	case OVPN_KEY_SLOT_PRIMARY:
		ks = rcu_replace_pointer(cs->primary, NULL,
					 lockdep_is_held(&cs->mutex));
		break;
	case OVPN_KEY_SLOT_SECONDARY:
		ks = rcu_replace_pointer(cs->secondary, NULL,
					 lockdep_is_held(&cs->mutex));
		break;
	default:
		pr_warn("Invalid slot to release: %u\n", slot);
		break;
	}
	mutex_unlock(&cs->mutex);

	if (!ks) {
		pr_debug("Key slot already released: %u\n", slot);
		return;
	}
	pr_debug("deleting key slot %u, key_id=%u\n", slot, ks->key_id);

	ovpn_crypto_key_slot_put(ks);
}

/* this swap is not atomic, but there will be a very short time frame where the
 * old_secondary key won't be available. This should not be a big deal as most
 * likely both peers are already using the new primary at this point.
 */
void ovpn_crypto_key_slots_swap(struct ovpn_crypto_state *cs)
{
	const struct ovpn_crypto_key_slot *old_primary, *old_secondary;

	mutex_lock(&cs->mutex);

	old_secondary = rcu_dereference_protected(cs->secondary,
						  lockdep_is_held(&cs->mutex));
	old_primary = rcu_replace_pointer(cs->primary, old_secondary,
					  lockdep_is_held(&cs->mutex));
	rcu_assign_pointer(cs->secondary, old_primary);

	pr_debug("key swapped: %u <-> %u\n",
		 old_primary ? old_primary->key_id : 0,
		 old_secondary ? old_secondary->key_id : 0);

	mutex_unlock(&cs->mutex);
}
0707010000000F000081A400000000000000000000000165BCF43700000DD7000000000000000000000000000000000000004100000000ovpn-dco-0.2.20240320~git0.2aa7f93/drivers/net/ovpn-dco/crypto.h/* SPDX-License-Identifier: GPL-2.0-only */
/*  OpenVPN data channel accelerator
 *
 *  Copyright (C) 2020-2023 OpenVPN, Inc.
 *
 *  Author:	James Yonan <james@openvpn.net>
 *		Antonio Quartulli <antonio@openvpn.net>
 */

#ifndef _NET_OVPN_DCO_OVPNCRYPTO_H_
#define _NET_OVPN_DCO_OVPNCRYPTO_H_

#include "main.h"
#include "pktid.h"

#include <uapi/linux/ovpn_dco.h>
#include <linux/skbuff.h>

struct ovpn_peer;
struct ovpn_crypto_key_slot;

/* info needed for both encrypt and decrypt directions */
struct ovpn_key_direction {
	const u8 *cipher_key;
	size_t cipher_key_size;
	const u8 *nonce_tail; /* only needed for GCM modes */
	size_t nonce_tail_size; /* only needed for GCM modes */
};

/* all info for a particular symmetric key (primary or secondary) */
struct ovpn_key_config {
	enum ovpn_cipher_alg cipher_alg;
	u8 key_id;
	struct ovpn_key_direction encrypt;
	struct ovpn_key_direction decrypt;
};

/* used to pass settings from netlink to the crypto engine */
struct ovpn_peer_key_reset {
	enum ovpn_key_slot slot;
	struct ovpn_key_config key;
};

struct ovpn_crypto_key_slot {
	u8 key_id;

	struct crypto_aead *encrypt;
	struct crypto_aead *decrypt;
	struct ovpn_nonce_tail nonce_tail_xmit;
	struct ovpn_nonce_tail nonce_tail_recv;

	struct ovpn_pktid_recv pid_recv ____cacheline_aligned_in_smp;
	struct ovpn_pktid_xmit pid_xmit ____cacheline_aligned_in_smp;
	struct kref refcount;
	struct rcu_head rcu;
};

struct ovpn_crypto_state {
	struct ovpn_crypto_key_slot __rcu *primary;
	struct ovpn_crypto_key_slot __rcu *secondary;

	/* protects primary and secondary slots */
	struct mutex mutex;
};

static inline bool ovpn_crypto_key_slot_hold(struct ovpn_crypto_key_slot *ks)
{
	return kref_get_unless_zero(&ks->refcount);
}

static inline void ovpn_crypto_state_init(struct ovpn_crypto_state *cs)
{
	RCU_INIT_POINTER(cs->primary, NULL);
	RCU_INIT_POINTER(cs->secondary, NULL);
	mutex_init(&cs->mutex);
}

static inline struct ovpn_crypto_key_slot *
ovpn_crypto_key_id_to_slot(const struct ovpn_crypto_state *cs, u8 key_id)
{
	struct ovpn_crypto_key_slot *ks;

	if (unlikely(!cs))
		return NULL;

	rcu_read_lock();
	ks = rcu_dereference(cs->primary);
	if (ks && ks->key_id == key_id) {
		if (unlikely(!ovpn_crypto_key_slot_hold(ks)))
			ks = NULL;
		goto out;
	}

	ks = rcu_dereference(cs->secondary);
	if (ks && ks->key_id == key_id) {
		if (unlikely(!ovpn_crypto_key_slot_hold(ks)))
			ks = NULL;
		goto out;
	}

	/* when both key slots are occupied but no matching key ID is found, ks has to be reset to
	 * NULL to avoid carrying a stale pointer
	 */
	ks = NULL;
out:
	rcu_read_unlock();

	return ks;
}

static inline struct ovpn_crypto_key_slot *
ovpn_crypto_key_slot_primary(const struct ovpn_crypto_state *cs)
{
	struct ovpn_crypto_key_slot *ks;

	rcu_read_lock();
	ks = rcu_dereference(cs->primary);
	if (unlikely(ks && !ovpn_crypto_key_slot_hold(ks)))
		ks = NULL;
	rcu_read_unlock();

	return ks;
}

void ovpn_crypto_key_slot_release(struct kref *kref);

static inline void ovpn_crypto_key_slot_put(struct ovpn_crypto_key_slot *ks)
{
	kref_put(&ks->refcount, ovpn_crypto_key_slot_release);
}

int ovpn_crypto_state_reset(struct ovpn_crypto_state *cs,
			    const struct ovpn_peer_key_reset *pkr);

void ovpn_crypto_key_slot_delete(struct ovpn_crypto_state *cs,
				 enum ovpn_key_slot slot);

void ovpn_crypto_state_release(struct ovpn_crypto_state *cs);

void ovpn_crypto_key_slots_swap(struct ovpn_crypto_state *cs);

void ovpn_crypto_kill_primary(struct ovpn_crypto_state *cs);

#endif /* _NET_OVPN_DCO_OVPNCRYPTO_H_ */
07070100000010000081A400000000000000000000000165BCF43700002831000000000000000000000000000000000000004600000000ovpn-dco-0.2.20240320~git0.2aa7f93/drivers/net/ovpn-dco/crypto_aead.c// SPDX-License-Identifier: GPL-2.0
/*  OpenVPN data channel accelerator
 *
 *  Copyright (C) 2020-2023 OpenVPN, Inc.
 *
 *  Author:	James Yonan <james@openvpn.net>
 *		Antonio Quartulli <antonio@openvpn.net>
 */

#include "crypto_aead.h"
#include "crypto.h"
#include "pktid.h"
#include "proto.h"
#include "skb.h"

#include <crypto/aead.h>
#include <linux/skbuff.h>
#include <linux/printk.h>

#define AUTH_TAG_SIZE	16

static int ovpn_aead_encap_overhead(const struct ovpn_crypto_key_slot *ks)
{
	return  OVPN_OP_SIZE_V2 +			/* OP header size */
		4 +					/* Packet ID */
		crypto_aead_authsize(ks->encrypt);	/* Auth Tag */
}

int ovpn_aead_encrypt(struct ovpn_crypto_key_slot *ks, struct sk_buff *skb, u32 peer_id)
{
	const unsigned int tag_size = crypto_aead_authsize(ks->encrypt);
	const unsigned int head_size = ovpn_aead_encap_overhead(ks);
	struct scatterlist sg[MAX_SKB_FRAGS + 2];
	DECLARE_CRYPTO_WAIT(wait);
	struct aead_request *req;
	struct sk_buff *trailer;
	u8 iv[NONCE_SIZE];
	int nfrags, ret;
	u32 pktid, op;

	/* Sample AEAD header format:
	 * 48000001 00000005 7e7046bd 444a7e28 cc6387b1 64a4d6c1 380275a...
	 * [ OP32 ] [seq # ] [             auth tag            ] [ payload ... ]
	 *          [4-byte
	 *          IV head]
	 */

	/* check that there's enough headroom in the skb for packet
	 * encapsulation, after adding network header and encryption overhead
	 */
	if (unlikely(skb_cow_head(skb, OVPN_HEAD_ROOM + head_size)))
		return -ENOBUFS;

	/* get number of skb frags and ensure that packet data is writable */
	nfrags = skb_cow_data(skb, 0, &trailer);
	if (unlikely(nfrags < 0))
		return nfrags;

	if (unlikely(nfrags + 2 > ARRAY_SIZE(sg)))
		return -ENOSPC;

	req = aead_request_alloc(ks->encrypt, GFP_KERNEL);
	if (unlikely(!req))
		return -ENOMEM;

	/* sg table:
	 * 0: op, wire nonce (AD, len=OVPN_OP_SIZE_V2+NONCE_WIRE_SIZE),
	 * 1, 2, 3, ..., n: payload,
	 * n+1: auth_tag (len=tag_size)
	 */
	sg_init_table(sg, nfrags + 2);

	/* build scatterlist to encrypt packet payload */
	ret = skb_to_sgvec_nomark(skb, sg + 1, 0, skb->len);
	if (unlikely(nfrags != ret)) {
		ret = -EINVAL;
		goto free_req;
	}

	/* append auth_tag onto scatterlist */
	__skb_push(skb, tag_size);
	sg_set_buf(sg + nfrags + 1, skb->data, tag_size);

	/* obtain packet ID, which is used both as a first
	 * 4 bytes of nonce and last 4 bytes of associated data.
	 */
	ret = ovpn_pktid_xmit_next(&ks->pid_xmit, &pktid);
	if (unlikely(ret < 0))
		goto free_req;

	/* concat 4 bytes packet id and 8 bytes nonce tail into 12 bytes nonce */
	ovpn_pktid_aead_write(pktid, &ks->nonce_tail_xmit, iv);

	/* make space for packet id and push it to the front */
	__skb_push(skb, NONCE_WIRE_SIZE);
	memcpy(skb->data, iv, NONCE_WIRE_SIZE);

	/* add packet op as head of additional data */
	op = ovpn_opcode_compose(OVPN_DATA_V2, ks->key_id, peer_id);
	__skb_push(skb, OVPN_OP_SIZE_V2);
	BUILD_BUG_ON(sizeof(op) != OVPN_OP_SIZE_V2);
	*((__force __be32 *)skb->data) = htonl(op);

	/* AEAD Additional data */
	sg_set_buf(sg, skb->data, OVPN_OP_SIZE_V2 + NONCE_WIRE_SIZE);

	/* setup async crypto operation */
	aead_request_set_tfm(req, ks->encrypt);
	aead_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG |
				       CRYPTO_TFM_REQ_MAY_SLEEP,
				  crypto_req_done, &wait);
	aead_request_set_crypt(req, sg, sg, skb->len - head_size, iv);
	aead_request_set_ad(req, OVPN_OP_SIZE_V2 + NONCE_WIRE_SIZE);

	/* encrypt it */
	ret = crypto_wait_req(crypto_aead_encrypt(req), &wait);
	if (ret < 0)
		net_err_ratelimited("%s: encrypt failed: %d\n", __func__, ret);

free_req:
	aead_request_free(req);
	return ret;
}

int ovpn_aead_decrypt(struct ovpn_crypto_key_slot *ks, struct sk_buff *skb)
{
	const unsigned int tag_size = crypto_aead_authsize(ks->decrypt);
	struct scatterlist sg[MAX_SKB_FRAGS + 2];
	int ret, payload_len, nfrags;
	u8 *sg_data, iv[NONCE_SIZE];
	unsigned int payload_offset;
	DECLARE_CRYPTO_WAIT(wait);
	struct aead_request *req;
	struct sk_buff *trailer;
	unsigned int sg_len;
	__be32 *pid;

	payload_offset = OVPN_OP_SIZE_V2 + NONCE_WIRE_SIZE + tag_size;
	payload_len = skb->len - payload_offset;

	/* sanity check on packet size, payload size must be >= 0 */
	if (unlikely(payload_len < 0))
		return -EINVAL;

	/* Prepare the skb data buffer to be accessed up until the auth tag.
	 * This is required because this area is directly mapped into the sg list.
	 */
	if (unlikely(!pskb_may_pull(skb, payload_offset)))
		return -ENODATA;

	/* get number of skb frags and ensure that packet data is writable */
	nfrags = skb_cow_data(skb, 0, &trailer);
	if (unlikely(nfrags < 0))
		return nfrags;

	if (unlikely(nfrags + 2 > ARRAY_SIZE(sg)))
		return -ENOSPC;

	req = aead_request_alloc(ks->decrypt, GFP_KERNEL);
	if (unlikely(!req))
		return -ENOMEM;

	/* sg table:
	 * 0: op, wire nonce (AD, len=OVPN_OP_SIZE_V2+NONCE_WIRE_SIZE),
	 * 1, 2, 3, ..., n: payload,
	 * n+1: auth_tag (len=tag_size)
	 */
	sg_init_table(sg, nfrags + 2);

	/* packet op is head of additional data */
	sg_data = skb->data;
	sg_len = OVPN_OP_SIZE_V2 + NONCE_WIRE_SIZE;
	sg_set_buf(sg, sg_data, sg_len);

	/* build scatterlist to decrypt packet payload */
	ret = skb_to_sgvec_nomark(skb, sg + 1, payload_offset, payload_len);
	if (unlikely(nfrags != ret)) {
		ret = -EINVAL;
		goto free_req;
	}

	/* append auth_tag onto scatterlist */
	sg_set_buf(sg + nfrags + 1, skb->data + sg_len, tag_size);

	/* copy nonce into IV buffer */
	memcpy(iv, skb->data + OVPN_OP_SIZE_V2, NONCE_WIRE_SIZE);
	memcpy(iv + NONCE_WIRE_SIZE, ks->nonce_tail_recv.u8,
	       sizeof(struct ovpn_nonce_tail));

	/* setup async crypto operation */
	aead_request_set_tfm(req, ks->decrypt);
	aead_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG |
				       CRYPTO_TFM_REQ_MAY_SLEEP,
				  crypto_req_done, &wait);
	aead_request_set_crypt(req, sg, sg, payload_len + tag_size, iv);

	aead_request_set_ad(req, NONCE_WIRE_SIZE + OVPN_OP_SIZE_V2);

	/* decrypt it */
	ret = crypto_wait_req(crypto_aead_decrypt(req), &wait);
	if (ret < 0) {
		net_err_ratelimited("%s: decrypt failed: %d\n", __func__, ret);
		goto free_req;
	}

	/* PID sits after the op */
	pid = (__force __be32 *)(skb->data + OVPN_OP_SIZE_V2);
	ret = ovpn_pktid_recv(&ks->pid_recv, ntohl(*pid), 0);
	if (unlikely(ret < 0))
		goto free_req;

	/* point to encapsulated IP packet */
	__skb_pull(skb, payload_offset);

free_req:
	aead_request_free(req);
	return ret;
}

/* Initialize a struct crypto_aead object */
struct crypto_aead *ovpn_aead_init(const char *title, const char *alg_name,
				   const unsigned char *key, unsigned int keylen)
{
	struct crypto_aead *aead;
	int ret;

	aead = crypto_alloc_aead(alg_name, 0, 0);
	if (IS_ERR(aead)) {
		ret = PTR_ERR(aead);
		pr_err("%s crypto_alloc_aead failed, err=%d\n", title, ret);
		aead = NULL;
		goto error;
	}

	ret = crypto_aead_setkey(aead, key, keylen);
	if (ret) {
		pr_err("%s crypto_aead_setkey size=%u failed, err=%d\n", title, keylen, ret);
		goto error;
	}

	ret = crypto_aead_setauthsize(aead, AUTH_TAG_SIZE);
	if (ret) {
		pr_err("%s crypto_aead_setauthsize failed, err=%d\n", title, ret);
		goto error;
	}

	/* basic AEAD assumption */
	if (crypto_aead_ivsize(aead) != NONCE_SIZE) {
		pr_err("%s IV size must be %d\n", title, NONCE_SIZE);
		ret = -EINVAL;
		goto error;
	}

	pr_debug("********* Cipher %s (%s)\n", alg_name, title);
	pr_debug("*** IV size=%u\n", crypto_aead_ivsize(aead));
	pr_debug("*** req size=%u\n", crypto_aead_reqsize(aead));
	pr_debug("*** block size=%u\n", crypto_aead_blocksize(aead));
	pr_debug("*** auth size=%u\n", crypto_aead_authsize(aead));
	pr_debug("*** alignmask=0x%x\n", crypto_aead_alignmask(aead));

	return aead;

error:
	crypto_free_aead(aead);
	return ERR_PTR(ret);
}

void ovpn_aead_crypto_key_slot_destroy(struct ovpn_crypto_key_slot *ks)
{
	if (!ks)
		return;

	crypto_free_aead(ks->encrypt);
	crypto_free_aead(ks->decrypt);
	kfree(ks);
}

static struct ovpn_crypto_key_slot *
ovpn_aead_crypto_key_slot_init(enum ovpn_cipher_alg alg,
			       const unsigned char *encrypt_key,
			       unsigned int encrypt_keylen,
			       const unsigned char *decrypt_key,
			       unsigned int decrypt_keylen,
			       const unsigned char *encrypt_nonce_tail,
			       unsigned int encrypt_nonce_tail_len,
			       const unsigned char *decrypt_nonce_tail,
			       unsigned int decrypt_nonce_tail_len,
			       u16 key_id)
{
	struct ovpn_crypto_key_slot *ks = NULL;
	const char *alg_name;
	int ret;

	/* validate crypto alg */
	switch (alg) {
	case OVPN_CIPHER_ALG_AES_GCM:
		alg_name = "gcm(aes)";
		break;
	case OVPN_CIPHER_ALG_CHACHA20_POLY1305:
		alg_name = "rfc7539(chacha20,poly1305)";
		break;
	default:
		return ERR_PTR(-EOPNOTSUPP);
	}

	/* build the key slot */
	ks = kmalloc(sizeof(*ks), GFP_KERNEL);
	if (!ks)
		return ERR_PTR(-ENOMEM);

	ks->encrypt = NULL;
	ks->decrypt = NULL;
	kref_init(&ks->refcount);
	ks->key_id = key_id;

	ks->encrypt = ovpn_aead_init("encrypt", alg_name, encrypt_key,
				     encrypt_keylen);
	if (IS_ERR(ks->encrypt)) {
		ret = PTR_ERR(ks->encrypt);
		ks->encrypt = NULL;
		goto destroy_ks;
	}

	ks->decrypt = ovpn_aead_init("decrypt", alg_name, decrypt_key,
				     decrypt_keylen);
	if (IS_ERR(ks->decrypt)) {
		ret = PTR_ERR(ks->decrypt);
		ks->decrypt = NULL;
		goto destroy_ks;
	}

	if (sizeof(struct ovpn_nonce_tail) != encrypt_nonce_tail_len ||
	    sizeof(struct ovpn_nonce_tail) != decrypt_nonce_tail_len) {
		ret = -EINVAL;
		goto destroy_ks;
	}

	memcpy(ks->nonce_tail_xmit.u8, encrypt_nonce_tail,
	       sizeof(struct ovpn_nonce_tail));
	memcpy(ks->nonce_tail_recv.u8, decrypt_nonce_tail,
	       sizeof(struct ovpn_nonce_tail));

	/* init packet ID generation/validation */
	ovpn_pktid_xmit_init(&ks->pid_xmit);
	ovpn_pktid_recv_init(&ks->pid_recv);

	return ks;

destroy_ks:
	ovpn_aead_crypto_key_slot_destroy(ks);
	return ERR_PTR(ret);
}

struct ovpn_crypto_key_slot *
ovpn_aead_crypto_key_slot_new(const struct ovpn_key_config *kc)
{
	return ovpn_aead_crypto_key_slot_init(kc->cipher_alg,
					      kc->encrypt.cipher_key,
					      kc->encrypt.cipher_key_size,
					      kc->decrypt.cipher_key,
					      kc->decrypt.cipher_key_size,
					      kc->encrypt.nonce_tail,
					      kc->encrypt.nonce_tail_size,
					      kc->decrypt.nonce_tail,
					      kc->decrypt.nonce_tail_size,
					      kc->key_id);
}
07070100000011000081A400000000000000000000000165BCF43700000365000000000000000000000000000000000000004600000000ovpn-dco-0.2.20240320~git0.2aa7f93/drivers/net/ovpn-dco/crypto_aead.h/* SPDX-License-Identifier: GPL-2.0-only */
/*  OpenVPN data channel accelerator
 *
 *  Copyright (C) 2020-2023 OpenVPN, Inc.
 *
 *  Author:	James Yonan <james@openvpn.net>
 *		Antonio Quartulli <antonio@openvpn.net>
 */

#ifndef _NET_OVPN_DCO_OVPNAEAD_H_
#define _NET_OVPN_DCO_OVPNAEAD_H_

#include "crypto.h"

#include <asm/types.h>
#include <linux/skbuff.h>

struct crypto_aead *ovpn_aead_init(const char *title, const char *alg_name,
				   const unsigned char *key, unsigned int keylen);

int ovpn_aead_encrypt(struct ovpn_crypto_key_slot *ks, struct sk_buff *skb, u32 peer_id);
int ovpn_aead_decrypt(struct ovpn_crypto_key_slot *ks, struct sk_buff *skb);

struct ovpn_crypto_key_slot *ovpn_aead_crypto_key_slot_new(const struct ovpn_key_config *kc);
void ovpn_aead_crypto_key_slot_destroy(struct ovpn_crypto_key_slot *ks);

#endif /* _NET_OVPN_DCO_OVPNAEAD_H_ */
07070100000012000081A400000000000000000000000165BCF437000019EC000000000000000000000000000000000000003F00000000ovpn-dco-0.2.20240320~git0.2aa7f93/drivers/net/ovpn-dco/main.c// SPDX-License-Identifier: GPL-2.0
/*  OpenVPN data channel accelerator
 *
 *  Copyright (C) 2020-2023 OpenVPN, Inc.
 *
 *  Author:	Antonio Quartulli <antonio@openvpn.net>
 *		James Yonan <james@openvpn.net>
 */

#include "main.h"

#include "ovpn.h"
#include "ovpnstruct.h"
#include "netlink.h"
#include "tcp.h"

#include <linux/ethtool.h>
#include <linux/genetlink.h>
#include <linux/inetdevice.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/types.h>
#include <linux/lockdep.h>
#include <linux/rcupdate.h>
#include <linux/net.h>
#include <linux/netdevice.h>
#include <linux/skbuff.h>
#include <linux/version.h>

#include <net/ip_tunnels.h>

/* Driver info */
#define DRV_NAME	"ovpn-dco"
#define DRV_VERSION	OVPN_DCO_VERSION
#define DRV_DESCRIPTION	"OpenVPN data channel offload (ovpn-dco)"
#define DRV_COPYRIGHT	"(C) 2020-2023 OpenVPN, Inc."

static void ovpn_struct_free(struct net_device *net)
{
	struct ovpn_struct *ovpn = netdev_priv(net);

	security_tun_dev_free_security(ovpn->security);
	free_percpu(net->tstats);
	flush_workqueue(ovpn->crypto_wq);
	flush_workqueue(ovpn->events_wq);
	destroy_workqueue(ovpn->crypto_wq);
	destroy_workqueue(ovpn->events_wq);
	rcu_barrier();
}

/* Net device open */
static int ovpn_net_open(struct net_device *dev)
{
	struct in_device *dev_v4 = __in_dev_get_rtnl(dev);

	if (dev_v4) {
		/* disable redirects as Linux gets confused by ovpn-dco handling same-LAN routing */
		IN_DEV_CONF_SET(dev_v4, SEND_REDIRECTS, false);
		IPV4_DEVCONF_ALL(dev_net(dev), SEND_REDIRECTS) = false;
	}

	netif_tx_start_all_queues(dev);
	return 0;
}

/* Net device stop -- called prior to device unload */
static int ovpn_net_stop(struct net_device *dev)
{
	netif_tx_stop_all_queues(dev);
	return 0;
}

/*******************************************
 * ovpn ethtool ops
 *******************************************/

static int ovpn_get_link_ksettings(struct net_device *dev,
				   struct ethtool_link_ksettings *cmd)
{
	ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported, 0);
	ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising, 0);
	cmd->base.speed	= SPEED_1000;
	cmd->base.duplex = DUPLEX_FULL;
	cmd->base.port = PORT_TP;
	cmd->base.phy_address = 0;
	cmd->base.transceiver = XCVR_INTERNAL;
	cmd->base.autoneg = AUTONEG_DISABLE;

	return 0;
}

static void ovpn_get_drvinfo(struct net_device *dev,
			     struct ethtool_drvinfo *info)
{
	strscpy(info->driver, DRV_NAME, sizeof(info->driver));
	strscpy(info->version, DRV_VERSION, sizeof(info->version));
	strscpy(info->bus_info, "ovpn", sizeof(info->bus_info));
}

bool ovpn_dev_is_valid(const struct net_device *dev)
{
	return dev->netdev_ops->ndo_start_xmit == ovpn_net_xmit;
}

/*******************************************
 * ovpn exported methods
 *******************************************/

static const struct net_device_ops ovpn_netdev_ops = {
	.ndo_open		= ovpn_net_open,
	.ndo_stop		= ovpn_net_stop,
	.ndo_start_xmit		= ovpn_net_xmit,
	.ndo_get_stats64        = dev_get_tstats64,
};

static const struct ethtool_ops ovpn_ethtool_ops = {
	.get_link_ksettings	= ovpn_get_link_ksettings,
	.get_drvinfo		= ovpn_get_drvinfo,
	.get_link		= ethtool_op_get_link,
	.get_ts_info		= ethtool_op_get_ts_info,
};

static void ovpn_setup(struct net_device *dev)
{
	/* compute the overhead considering AEAD encryption */
	const int overhead = sizeof(u32) + NONCE_WIRE_SIZE + 16 + sizeof(struct udphdr) +
			     max(sizeof(struct ipv6hdr), sizeof(struct iphdr));

	netdev_features_t feat = NETIF_F_SG | NETIF_F_LLTX |
				 NETIF_F_HW_CSUM | NETIF_F_RXCSUM | NETIF_F_GSO |
				 NETIF_F_GSO_SOFTWARE | NETIF_F_HIGHDMA;

	dev->ethtool_ops = &ovpn_ethtool_ops;
	dev->needs_free_netdev = true;

	dev->netdev_ops = &ovpn_netdev_ops;

	dev->priv_destructor = ovpn_struct_free;

	/* Point-to-Point TUN Device */
	dev->hard_header_len = 0;
	dev->addr_len = 0;
	dev->mtu = ETH_DATA_LEN - overhead;
	dev->min_mtu = IPV4_MIN_MTU;
	dev->max_mtu = IP_MAX_MTU - overhead;

	/* Zero header length */
	dev->type = ARPHRD_NONE;
	dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;

	dev->features |= feat;
	dev->hw_features |= feat;
	dev->hw_enc_features |= feat;

	dev->needed_headroom = OVPN_HEAD_ROOM;
	dev->needed_tailroom = OVPN_MAX_PADDING;
}

static const struct nla_policy ovpn_policy[IFLA_OVPN_MAX + 1] = {
	[IFLA_OVPN_MODE] = NLA_POLICY_RANGE(NLA_U8, __OVPN_MODE_FIRST,
					    __OVPN_MODE_AFTER_LAST - 1),
};

static int ovpn_newlink(struct net *src_net, struct net_device *dev, struct nlattr *tb[],
			struct nlattr *data[], struct netlink_ext_ack *extack)
{
	struct ovpn_struct *ovpn = netdev_priv(dev);
	int ret;

	ret = security_tun_dev_create();
	if (ret < 0)
		return ret;

	ret = ovpn_struct_init(dev);
	if (ret < 0)
		return ret;

	ovpn->mode = OVPN_MODE_P2P;
	if (data && data[IFLA_OVPN_MODE]) {
		ovpn->mode = nla_get_u8(data[IFLA_OVPN_MODE]);
		netdev_dbg(dev, "%s: setting device (%s) mode: %u\n", __func__, dev->name,
			   ovpn->mode);
	}

	return register_netdevice(dev);
}

static void ovpn_dellink(struct net_device *dev, struct list_head *head)
{
	struct ovpn_struct *ovpn = netdev_priv(dev);

	switch (ovpn->mode) {
	case OVPN_MODE_P2P:
		ovpn_peer_release_p2p(ovpn);
		break;
	default:
		ovpn_peers_free(ovpn);
		break;
	}

	unregister_netdevice_queue(dev, head);
}

static struct rtnl_link_ops ovpn_link_ops __read_mostly = {
	.kind			= DRV_NAME,
	.priv_size		= sizeof(struct ovpn_struct),
	.setup			= ovpn_setup,
	.policy			= ovpn_policy,
	.maxtype		= IFLA_OVPN_MAX,
	.newlink		= ovpn_newlink,
	.dellink		= ovpn_dellink,
};

static int __init ovpn_init(void)
{
	int err = 0;

	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;
	}

	/* init RTNL link ops */
	err = rtnl_link_register(&ovpn_link_ops);
	if (err) {
		pr_err("ovpn: can't register RTNL link ops\n");
		goto err;
	}

	err = ovpn_netlink_register();
	if (err) {
		pr_err("ovpn: can't register netlink family\n");
		goto err_rtnl_unregister;
	}

	return 0;

err_rtnl_unregister:
	rtnl_link_unregister(&ovpn_link_ops);
err:
	pr_err("ovpn: initialization failed, error status=%d\n", err);
	return err;
}

static __exit void ovpn_cleanup(void)
{
	rtnl_link_unregister(&ovpn_link_ops);
	ovpn_netlink_unregister();
	rcu_barrier(); /* because we use call_rcu */
}

module_init(ovpn_init);
module_exit(ovpn_cleanup);

MODULE_DESCRIPTION(DRV_DESCRIPTION);
MODULE_AUTHOR(DRV_COPYRIGHT);
MODULE_LICENSE("GPL");
MODULE_VERSION(DRV_VERSION);
MODULE_ALIAS_RTNL_LINK(DRV_NAME);
MODULE_ALIAS_GENL_FAMILY(OVPN_NL_NAME);
07070100000013000081A400000000000000000000000165BCF437000003FB000000000000000000000000000000000000003F00000000ovpn-dco-0.2.20240320~git0.2aa7f93/drivers/net/ovpn-dco/main.h/* SPDX-License-Identifier: GPL-2.0-only */
/*  OpenVPN data channel accelerator
 *
 *  Copyright (C) 2019-2023 OpenVPN, Inc.
 *
 *  Author:	James Yonan <james@openvpn.net>
 *		Antonio Quartulli <antonio@openvpn.net>
 */

#ifndef _NET_OVPN_DCO_MAIN_H_
#define _NET_OVPN_DCO_MAIN_H_

#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/printk.h>
#include <linux/udp.h>

#ifndef OVPN_DCO_VERSION
#define OVPN_DCO_VERSION "2.0.0"
#endif

/* Our UDP encapsulation types, must be unique
 * (other values in include/uapi/linux/udp.h)
 */
#define UDP_ENCAP_OVPNINUDP 100  /* transport layer */

struct net_device;
bool ovpn_dev_is_valid(const struct net_device *dev);

#define SKB_HEADER_LEN                                       \
	(max(sizeof(struct iphdr), sizeof(struct ipv6hdr)) + \
	 sizeof(struct udphdr) + NET_SKB_PAD)

#define OVPN_HEAD_ROOM ALIGN(16 + SKB_HEADER_LEN, 4)
#define OVPN_MAX_PADDING 16
#define OVPN_QUEUE_LEN 1024
#define OVPN_MAX_TUN_QUEUE_LEN 0x10000

#endif /* _NET_OVPN_DCO_OVPN_DCO_H_ */
07070100000014000081A400000000000000000000000165BCF43700006C06000000000000000000000000000000000000004200000000ovpn-dco-0.2.20240320~git0.2aa7f93/drivers/net/ovpn-dco/netlink.c// SPDX-License-Identifier: GPL-2.0
/*  OpenVPN data channel accelerator
 *
 *  Copyright (C) 2020-2023 OpenVPN, Inc.
 *
 *  Author:	Antonio Quartulli <antonio@openvpn.net>
 */

#include "main.h"
#include "ovpn.h"
#include "peer.h"
#include "proto.h"
#include "netlink.h"
#include "ovpnstruct.h"
#include "udp.h"

#include <uapi/linux/ovpn_dco.h>

#include <linux/netdevice.h>
#include <linux/netlink.h>
#include <linux/rcupdate.h>
#include <linux/socket.h>
#include <linux/types.h>
#include <linux/spinlock.h>
#include <net/genetlink.h>
#include <uapi/linux/in.h>
#include <uapi/linux/in6.h>

/** The ovpn-dco netlink family */
static struct genl_family ovpn_netlink_family;

enum ovpn_netlink_multicast_groups {
	OVPN_MCGRP_PEERS,
};

static const struct genl_multicast_group ovpn_netlink_mcgrps[] = {
	[OVPN_MCGRP_PEERS] = { .name = OVPN_NL_MULTICAST_GROUP_PEERS },
};

/** Key direction policy. Can be used for configuring an encryption and a decryption key */
static const struct nla_policy ovpn_netlink_policy_key_dir[OVPN_KEY_DIR_ATTR_MAX + 1] = {
	[OVPN_KEY_DIR_ATTR_CIPHER_KEY] = NLA_POLICY_MAX_LEN(U8_MAX),
	[OVPN_KEY_DIR_ATTR_NONCE_TAIL] = NLA_POLICY_EXACT_LEN(NONCE_TAIL_SIZE),
};

/** CMD_NEW_KEY policy */
static const struct nla_policy ovpn_netlink_policy_new_key[OVPN_NEW_KEY_ATTR_MAX + 1] = {
	[OVPN_NEW_KEY_ATTR_PEER_ID] = { .type = NLA_U32 },
	[OVPN_NEW_KEY_ATTR_KEY_SLOT] = NLA_POLICY_RANGE(NLA_U8, __OVPN_KEY_SLOT_FIRST,
							__OVPN_KEY_SLOT_AFTER_LAST - 1),
	[OVPN_NEW_KEY_ATTR_KEY_ID] = { .type = NLA_U8 },
	[OVPN_NEW_KEY_ATTR_CIPHER_ALG] = { .type = NLA_U16 },
	[OVPN_NEW_KEY_ATTR_ENCRYPT_KEY] = NLA_POLICY_NESTED(ovpn_netlink_policy_key_dir),
	[OVPN_NEW_KEY_ATTR_DECRYPT_KEY] = NLA_POLICY_NESTED(ovpn_netlink_policy_key_dir),
};

/** CMD_DEL_KEY policy */
static const struct nla_policy ovpn_netlink_policy_del_key[OVPN_DEL_KEY_ATTR_MAX + 1] = {
	[OVPN_DEL_KEY_ATTR_PEER_ID] = { .type = NLA_U32 },
	[OVPN_DEL_KEY_ATTR_KEY_SLOT] = NLA_POLICY_RANGE(NLA_U8, __OVPN_KEY_SLOT_FIRST,
							__OVPN_KEY_SLOT_AFTER_LAST - 1),
};

/** CMD_SWAP_KEYS policy */
static const struct nla_policy ovpn_netlink_policy_swap_keys[OVPN_SWAP_KEYS_ATTR_MAX + 1] = {
	[OVPN_SWAP_KEYS_ATTR_PEER_ID] = { .type = NLA_U32 },
};

/** CMD_NEW_PEER policy */
static const struct nla_policy ovpn_netlink_policy_new_peer[OVPN_NEW_PEER_ATTR_MAX + 1] = {
	[OVPN_NEW_PEER_ATTR_PEER_ID] = { .type = NLA_U32 },
	[OVPN_NEW_PEER_ATTR_SOCKADDR_REMOTE] = NLA_POLICY_MIN_LEN(sizeof(struct sockaddr)),
	[OVPN_NEW_PEER_ATTR_SOCKET] = { .type = NLA_U32 },
	[OVPN_NEW_PEER_ATTR_IPV4] = { .type = NLA_U32 },
	[OVPN_NEW_PEER_ATTR_IPV6] = NLA_POLICY_EXACT_LEN(sizeof(struct in6_addr)),
	[OVPN_NEW_PEER_ATTR_LOCAL_IP] = NLA_POLICY_MAX_LEN(sizeof(struct in6_addr)),
};

/** CMD_SET_PEER policy */
static const struct nla_policy ovpn_netlink_policy_set_peer[OVPN_SET_PEER_ATTR_MAX + 1] = {
	[OVPN_SET_PEER_ATTR_PEER_ID] = { .type = NLA_U32 },
	[OVPN_SET_PEER_ATTR_KEEPALIVE_INTERVAL] = { .type = NLA_U32 },
	[OVPN_SET_PEER_ATTR_KEEPALIVE_TIMEOUT] = { .type = NLA_U32 },
};

/** CMD_DEL_PEER policy */
static const struct nla_policy ovpn_netlink_policy_del_peer[OVPN_DEL_PEER_ATTR_MAX + 1] = {
	[OVPN_DEL_PEER_ATTR_REASON] = NLA_POLICY_RANGE(NLA_U8, __OVPN_DEL_PEER_REASON_FIRST,
						       __OVPN_DEL_PEER_REASON_AFTER_LAST - 1),
	[OVPN_DEL_PEER_ATTR_PEER_ID] = { .type = NLA_U32 },
};

/** CMD_GET_PEER policy */
static const struct nla_policy ovpn_netlink_policy_get_peer[OVPN_GET_PEER_ATTR_MAX + 1] = {
	[OVPN_GET_PEER_ATTR_PEER_ID] = { .type = NLA_U32 },
};

/** Generic message container policy */
static const struct nla_policy ovpn_netlink_policy[OVPN_ATTR_MAX + 1] = {
	[OVPN_ATTR_IFINDEX] = { .type = NLA_U32 },
	[OVPN_ATTR_NEW_PEER] = NLA_POLICY_NESTED(ovpn_netlink_policy_new_peer),
	[OVPN_ATTR_SET_PEER] = NLA_POLICY_NESTED(ovpn_netlink_policy_set_peer),
	[OVPN_ATTR_DEL_PEER] = NLA_POLICY_NESTED(ovpn_netlink_policy_del_peer),
	[OVPN_ATTR_GET_PEER] = NLA_POLICY_NESTED(ovpn_netlink_policy_get_peer),
	[OVPN_ATTR_NEW_KEY] = NLA_POLICY_NESTED(ovpn_netlink_policy_new_key),
	[OVPN_ATTR_SWAP_KEYS] = NLA_POLICY_NESTED(ovpn_netlink_policy_swap_keys),
	[OVPN_ATTR_DEL_KEY] = NLA_POLICY_NESTED(ovpn_netlink_policy_del_key),
};

static struct net_device *
ovpn_get_dev_from_attrs(struct net *net, struct nlattr **attrs)
{
	struct net_device *dev;
	int ifindex;

	if (!attrs[OVPN_ATTR_IFINDEX])
		return ERR_PTR(-EINVAL);

	ifindex = nla_get_u32(attrs[OVPN_ATTR_IFINDEX]);

	dev = dev_get_by_index(net, ifindex);
	if (!dev)
		return ERR_PTR(-ENODEV);

	if (!ovpn_dev_is_valid(dev))
		goto err_put_dev;

	return dev;

err_put_dev:
	dev_put(dev);

	return ERR_PTR(-EINVAL);
}

/**
 * ovpn_pre_doit() - Prepare ovpn genl doit request
 * @ops: requested netlink operation
 * @skb: Netlink message with request data
 * @info: receiver information
 *
 * Return: 0 on success or negative error number in case of failure
 */
static int ovpn_pre_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
			 struct genl_info *info)
{
	struct net *net = genl_info_net(info);
	struct net_device *dev;

	dev = ovpn_get_dev_from_attrs(net, info->attrs);
	if (IS_ERR(dev))
		return PTR_ERR(dev);

	info->user_ptr[0] = netdev_priv(dev);

	return 0;
}

/**
 * ovpn_post_doit() - complete ovpn genl doit request
 * @ops: requested netlink operation
 * @skb: Netlink message with request data
 * @info: receiver information
 */
static void ovpn_post_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
			   struct genl_info *info)
{
	struct ovpn_struct *ovpn;

	ovpn = info->user_ptr[0];
	dev_put(ovpn->dev);
}

static int ovpn_netlink_get_key_dir(struct genl_info *info, struct nlattr *key,
				    enum ovpn_cipher_alg cipher,
				    struct ovpn_key_direction *dir)
{
	struct nlattr *attr, *attrs[OVPN_KEY_DIR_ATTR_MAX + 1];
	int ret;

	ret = nla_parse_nested(attrs, OVPN_KEY_DIR_ATTR_MAX, key, NULL, info->extack);
	if (ret)
		return ret;

	switch (cipher) {
	case OVPN_CIPHER_ALG_AES_GCM:
	case OVPN_CIPHER_ALG_CHACHA20_POLY1305:
		attr = attrs[OVPN_KEY_DIR_ATTR_CIPHER_KEY];
		if (!attr)
			return -EINVAL;

		dir->cipher_key = nla_data(attr);
		dir->cipher_key_size = nla_len(attr);

		attr = attrs[OVPN_KEY_DIR_ATTR_NONCE_TAIL];
		/* These algorithms require a 96bit nonce,
		 * Construct it by combining 4-bytes packet id and
		 * 8-bytes nonce-tail from userspace
		 */
		if (!attr)
			return -EINVAL;

		dir->nonce_tail = nla_data(attr);
		dir->nonce_tail_size = nla_len(attr);
		break;
	default:
		return -EINVAL;
	}

	return 0;
}

static int ovpn_netlink_new_key(struct sk_buff *skb, struct genl_info *info)
{
	struct nlattr *attrs[OVPN_NEW_KEY_ATTR_MAX + 1];
	struct ovpn_struct *ovpn = info->user_ptr[0];
	struct ovpn_peer_key_reset pkr;
	struct ovpn_peer *peer;
	u32 peer_id;
	int ret;

	if (!info->attrs[OVPN_ATTR_NEW_KEY])
		return -EINVAL;

	ret = nla_parse_nested(attrs, OVPN_NEW_KEY_ATTR_MAX, info->attrs[OVPN_ATTR_NEW_KEY],
			       NULL, info->extack);
	if (ret)
		return ret;

	if (!attrs[OVPN_NEW_KEY_ATTR_PEER_ID] ||
	    !attrs[OVPN_NEW_KEY_ATTR_KEY_SLOT] ||
	    !attrs[OVPN_NEW_KEY_ATTR_KEY_ID] ||
	    !attrs[OVPN_NEW_KEY_ATTR_CIPHER_ALG] ||
	    !attrs[OVPN_NEW_KEY_ATTR_ENCRYPT_KEY] ||
	    !attrs[OVPN_NEW_KEY_ATTR_DECRYPT_KEY])
		return -EINVAL;

	peer_id = nla_get_u32(attrs[OVPN_NEW_KEY_ATTR_PEER_ID]);
	pkr.slot = nla_get_u8(attrs[OVPN_NEW_KEY_ATTR_KEY_SLOT]);
	pkr.key.key_id = nla_get_u16(attrs[OVPN_NEW_KEY_ATTR_KEY_ID]);

	pkr.key.cipher_alg = nla_get_u16(attrs[OVPN_NEW_KEY_ATTR_CIPHER_ALG]);

	ret = ovpn_netlink_get_key_dir(info, attrs[OVPN_NEW_KEY_ATTR_ENCRYPT_KEY],
				       pkr.key.cipher_alg, &pkr.key.encrypt);
	if (ret < 0)
		return ret;

	ret = ovpn_netlink_get_key_dir(info, attrs[OVPN_NEW_KEY_ATTR_DECRYPT_KEY],
				       pkr.key.cipher_alg, &pkr.key.decrypt);
	if (ret < 0)
		return ret;

	peer = ovpn_peer_lookup_id(ovpn, peer_id);
	if (!peer) {
		netdev_dbg(ovpn->dev, "%s: no peer with id %u to set key for\n", __func__, peer_id);
		return -ENOENT;
	}

	mutex_lock(&peer->crypto.mutex);
	ret = ovpn_crypto_state_reset(&peer->crypto, &pkr);
	if (ret < 0) {
		netdev_dbg(ovpn->dev, "%s: cannot install new key for peer %u\n", __func__,
			   peer_id);
		goto unlock;
	}

	netdev_dbg(ovpn->dev, "%s: new key installed (id=%u) for peer %u\n", __func__,
		   pkr.key.key_id, peer_id);
unlock:
	mutex_unlock(&peer->crypto.mutex);
	ovpn_peer_put(peer);
	return ret;
}

static int ovpn_netlink_del_key(struct sk_buff *skb, struct genl_info *info)
{
	struct nlattr *attrs[OVPN_DEL_KEY_ATTR_MAX + 1];
	struct ovpn_struct *ovpn = info->user_ptr[0];
	enum ovpn_key_slot slot;
	struct ovpn_peer *peer;
	u32 peer_id;
	int ret;

	if (!info->attrs[OVPN_ATTR_DEL_KEY])
		return -EINVAL;

	ret = nla_parse_nested(attrs, OVPN_DEL_KEY_ATTR_MAX, info->attrs[OVPN_ATTR_DEL_KEY], NULL,
			       info->extack);
	if (ret)
		return ret;

	if (!attrs[OVPN_DEL_KEY_ATTR_PEER_ID] || !attrs[OVPN_DEL_KEY_ATTR_KEY_SLOT])
		return -EINVAL;

	peer_id = nla_get_u32(attrs[OVPN_DEL_KEY_ATTR_PEER_ID]);
	slot = nla_get_u8(attrs[OVPN_DEL_KEY_ATTR_KEY_SLOT]);

	peer = ovpn_peer_lookup_id(ovpn, peer_id);
	if (!peer)
		return -ENOENT;

	ovpn_crypto_key_slot_delete(&peer->crypto, slot);
	ovpn_peer_put(peer);

	return 0;
}

static int ovpn_netlink_swap_keys(struct sk_buff *skb, struct genl_info *info)
{
	struct nlattr *attrs[OVPN_SWAP_KEYS_ATTR_MAX + 1];
	struct ovpn_struct *ovpn = info->user_ptr[0];
	struct ovpn_peer *peer;
	u32 peer_id;
	int ret;

	if (!info->attrs[OVPN_ATTR_SWAP_KEYS])
		return -EINVAL;

	ret = nla_parse_nested(attrs, OVPN_SWAP_KEYS_ATTR_MAX, info->attrs[OVPN_ATTR_SWAP_KEYS],
			       NULL, info->extack);
	if (ret)
		return ret;

	if (!attrs[OVPN_SWAP_KEYS_ATTR_PEER_ID])
		return -EINVAL;

	peer_id = nla_get_u32(attrs[OVPN_SWAP_KEYS_ATTR_PEER_ID]);

	peer = ovpn_peer_lookup_id(ovpn, peer_id);
	if (!peer)
		return -ENOENT;

	ovpn_crypto_key_slots_swap(&peer->crypto);
	ovpn_peer_put(peer);

	return 0;
}

static int ovpn_netlink_new_peer(struct sk_buff *skb, struct genl_info *info)
{
	struct nlattr *attrs[OVPN_NEW_PEER_ATTR_MAX + 1];
	struct ovpn_struct *ovpn = info->user_ptr[0];
	struct sockaddr_storage *ss = NULL;
	struct sockaddr_in mapped;
	struct sockaddr_in6 *in6;
	struct ovpn_peer *peer;
	size_t sa_len, ip_len;
	struct socket *sock;
	u8 *local_ip = NULL;
	u32 sockfd, id;
	int ret;

	if (!info->attrs[OVPN_ATTR_NEW_PEER])
		return -EINVAL;

	ret = nla_parse_nested(attrs, OVPN_NEW_PEER_ATTR_MAX, info->attrs[OVPN_ATTR_NEW_PEER], NULL,
			       info->extack);
	if (ret)
		return ret;

	if (!attrs[OVPN_NEW_PEER_ATTR_PEER_ID] || !attrs[OVPN_NEW_PEER_ATTR_SOCKET]) {
		netdev_err(ovpn->dev, "%s: basic attributes missing\n", __func__);
		return -EINVAL;
	}


	if (ovpn->mode == OVPN_MODE_MP && !attrs[OVPN_NEW_PEER_ATTR_IPV4] &&
	    !attrs[OVPN_NEW_PEER_ATTR_IPV6]) {
		netdev_err(ovpn->dev, "%s: a VPN IP is required when adding a peer in MP mode\n",
			   __func__);
		return -EINVAL;
	}

	/* lookup the fd in the kernel table and extract the socket object */
	sockfd = nla_get_u32(attrs[OVPN_NEW_PEER_ATTR_SOCKET]);
	/* sockfd_lookup() increases sock's refcounter */
	sock = sockfd_lookup(sockfd, &ret);
	if (!sock) {
		netdev_dbg(ovpn->dev, "%s: cannot lookup peer socket (fd=%u): %d\n", __func__,
			   sockfd, ret);
		return -ENOTSOCK;
	}

	/* Only when using UDP as transport protocol the remote endpoint must be configured
	 * so that ovpn-dco knows where to send packets to.
	 *
	 * In case of TCP, the socket is connected to the peer and ovpn-dco will just send bytes
	 * over it, without the need to specify a destination.
	 */
	if (sock->sk->sk_protocol == IPPROTO_UDP) {
		ret = -EINVAL;

		if (!attrs[OVPN_NEW_PEER_ATTR_SOCKADDR_REMOTE]) {
			netdev_err(ovpn->dev, "%s: cannot add UDP peer with no remote endpoint\n",
				   __func__);
			goto sockfd_release;
		}

		ss = nla_data(attrs[OVPN_NEW_PEER_ATTR_SOCKADDR_REMOTE]);
		sa_len = nla_len(attrs[OVPN_NEW_PEER_ATTR_SOCKADDR_REMOTE]);
		switch (sa_len) {
		case sizeof(struct sockaddr_in):
			if (ss->ss_family == AF_INET)
				/* valid sockaddr */
				break;

			netdev_err(ovpn->dev, "%s: remote sockaddr_in has invalid family\n",
				   __func__);
			goto sockfd_release;
		case sizeof(struct sockaddr_in6):
			if (ss->ss_family == AF_INET6)
				/* valid sockaddr */
				break;

			netdev_err(ovpn->dev, "%s: remote sockaddr_in6 has invalid family\n",
				   __func__);
			goto sockfd_release;
		default:
			netdev_err(ovpn->dev, "%s: invalid size for sockaddr\n", __func__);
			goto sockfd_release;
		}

		if (ss->ss_family == AF_INET6) {
			in6 = (struct sockaddr_in6 *)ss;

			if (ipv6_addr_type(&in6->sin6_addr) & IPV6_ADDR_MAPPED) {
				mapped.sin_family = AF_INET;
				mapped.sin_addr.s_addr = in6->sin6_addr.s6_addr32[3];
				mapped.sin_port = in6->sin6_port;
				ss = (struct sockaddr_storage *)&mapped;
			}
		}

		/* When using UDP we may be talking over socket bound to 0.0.0.0/::.
		 * In this case, if the host has multiple IPs, we need to make sure
		 * that outgoing traffic has as source IP the same address that the
		 * peer is using to reach us.
		 *
		 * Since early control packets were all forwarded to userspace, we
		 * need the latter to tell us what IP has to be used.
		 */
		if (attrs[OVPN_NEW_PEER_ATTR_LOCAL_IP]) {
			ip_len = nla_len(attrs[OVPN_NEW_PEER_ATTR_LOCAL_IP]);
			local_ip = nla_data(attrs[OVPN_NEW_PEER_ATTR_LOCAL_IP]);

			if (ip_len == sizeof(struct in_addr)) {
				if (ss->ss_family != AF_INET) {
					netdev_dbg(ovpn->dev,
						   "%s: the specified local IP is IPv4, but the peer endpoint is not\n",
						   __func__);
					goto sockfd_release;
				}
			} else if (ip_len == sizeof(struct in6_addr)) {
				bool is_mapped = ipv6_addr_type((struct in6_addr *)local_ip) &
						 IPV6_ADDR_MAPPED;

				if (ss->ss_family != AF_INET6 && !is_mapped) {
					netdev_dbg(ovpn->dev,
						   "%s: the specified local IP is IPv6, but the peer endpoint is not\n",
						   __func__);
					goto sockfd_release;
				}

				if (is_mapped)
					/* this is an IPv6-mapped IPv4 address, therefore extract
					 * the actual v4 address from the last 4 bytes
					 */
					local_ip += 12;
			} else {
				netdev_dbg(ovpn->dev,
					   "%s: invalid length %zu for local IP\n", __func__,
					   ip_len);
				goto sockfd_release;
			}
		}

		/* sanity checks passed */
		ret = 0;
	}

	id = nla_get_u32(attrs[OVPN_NEW_PEER_ATTR_PEER_ID]);
	peer = ovpn_peer_new(ovpn, ss, sock, id, local_ip);
	if (IS_ERR(peer)) {
		netdev_err(ovpn->dev, "%s: cannot create new peer object for peer %u (sockaddr=%pIScp): %ld\n",
			   __func__, id, ss, PTR_ERR(peer));
		ret = PTR_ERR(peer);
		goto sockfd_release;
	}

	if (attrs[OVPN_NEW_PEER_ATTR_IPV4]) {
		if (nla_len(attrs[OVPN_NEW_PEER_ATTR_IPV4]) != sizeof(struct in_addr)) {
			ret = -EINVAL;
			goto peer_release;
		}

		peer->vpn_addrs.ipv4.s_addr = nla_get_be32(attrs[OVPN_NEW_PEER_ATTR_IPV4]);
	}

	if (attrs[OVPN_NEW_PEER_ATTR_IPV6]) {
		if (nla_len(attrs[OVPN_NEW_PEER_ATTR_IPV6]) != sizeof(struct in6_addr)) {
			ret = -EINVAL;
			goto peer_release;
		}

		memcpy(&peer->vpn_addrs.ipv6, nla_data(attrs[OVPN_NEW_PEER_ATTR_IPV6]),
		       sizeof(struct in6_addr));
	}

	netdev_dbg(ovpn->dev,
		   "%s: adding peer with endpoint=%pIScp/%s id=%u VPN-IPv4=%pI4 VPN-IPv6=%pI6c\n",
		   __func__, ss, sock->sk->sk_prot_creator->name, peer->id,
		   &peer->vpn_addrs.ipv4.s_addr, &peer->vpn_addrs.ipv6);

	ret = ovpn_peer_add(ovpn, peer);
	if (ret < 0) {
		netdev_err(ovpn->dev, "%s: cannot add new peer (id=%u) to hashtable: %d\n",
			   __func__, peer->id, ret);
		goto peer_release;
	}

	return 0;

peer_release:
	/* release right away because peer is not really used in any context */
	ovpn_peer_release(peer);
	return ret;

sockfd_release:
	sockfd_put(sock);
	return ret;
}

static int ovpn_netlink_set_peer(struct sk_buff *skb, struct genl_info *info)
{
	struct nlattr *attrs[OVPN_SET_PEER_ATTR_MAX + 1];
	struct ovpn_struct *ovpn = info->user_ptr[0];
	u32 peer_id, interv, timeout;
	bool keepalive_set = false;
	struct ovpn_peer *peer;
	int ret;

	if (!info->attrs[OVPN_ATTR_SET_PEER])
		return -EINVAL;

	ret = nla_parse_nested(attrs, OVPN_SET_PEER_ATTR_MAX, info->attrs[OVPN_ATTR_SET_PEER], NULL,
			       info->extack);
	if (ret)
		return ret;

	if (!attrs[OVPN_SET_PEER_ATTR_PEER_ID])
		return -EINVAL;

	peer_id = nla_get_u32(attrs[OVPN_SET_PEER_ATTR_PEER_ID]);

	peer = ovpn_peer_lookup_id(ovpn, peer_id);
	if (!peer)
		return -ENOENT;

	/* when setting the keepalive, both parameters have to be configured */
	if (attrs[OVPN_SET_PEER_ATTR_KEEPALIVE_INTERVAL] &&
	    attrs[OVPN_SET_PEER_ATTR_KEEPALIVE_TIMEOUT]) {
		keepalive_set = true;
		interv = nla_get_u32(attrs[OVPN_SET_PEER_ATTR_KEEPALIVE_INTERVAL]);
		timeout = nla_get_u32(attrs[OVPN_SET_PEER_ATTR_KEEPALIVE_TIMEOUT]);
	}

	if (keepalive_set)
		ovpn_peer_keepalive_set(peer, interv, timeout);

	ovpn_peer_put(peer);
	return 0;
}

static int ovpn_netlink_send_peer(struct sk_buff *skb, const struct ovpn_peer *peer, u32 portid,
				  u32 seq, int flags)
{
	const struct ovpn_bind *bind;
	struct nlattr *attr;
	void *hdr;

	hdr = genlmsg_put(skb, portid, seq, &ovpn_netlink_family, flags, OVPN_CMD_GET_PEER);
	if (!hdr) {
		netdev_dbg(peer->ovpn->dev, "%s: cannot create message header\n", __func__);
		return -EMSGSIZE;
	}

	attr = nla_nest_start(skb, OVPN_ATTR_GET_PEER);
	if (!attr) {
		netdev_dbg(peer->ovpn->dev, "%s: cannot create submessage\n", __func__);
		goto err;
	}

	if (nla_put_u32(skb, OVPN_GET_PEER_RESP_ATTR_PEER_ID, peer->id))
		goto err;

	if (peer->vpn_addrs.ipv4.s_addr != htonl(INADDR_ANY))
		if (nla_put(skb, OVPN_GET_PEER_RESP_ATTR_IPV4, sizeof(peer->vpn_addrs.ipv4),
			    &peer->vpn_addrs.ipv4))
			goto err;

	if (memcmp(&peer->vpn_addrs.ipv6, &in6addr_any, sizeof(peer->vpn_addrs.ipv6)))
		if (nla_put(skb, OVPN_GET_PEER_RESP_ATTR_IPV6, sizeof(peer->vpn_addrs.ipv6),
			    &peer->vpn_addrs.ipv6))
			goto err;

	if (nla_put_u32(skb, OVPN_GET_PEER_RESP_ATTR_KEEPALIVE_INTERVAL,
			peer->keepalive_interval) ||
	    nla_put_u32(skb, OVPN_GET_PEER_RESP_ATTR_KEEPALIVE_TIMEOUT,
			peer->keepalive_timeout))
		goto err;

	rcu_read_lock();
	bind = rcu_dereference(peer->bind);
	if (bind) {
		if (bind->sa.in4.sin_family == AF_INET) {
			if (nla_put(skb, OVPN_GET_PEER_RESP_ATTR_SOCKADDR_REMOTE,
				    sizeof(bind->sa.in4), &bind->sa.in4) ||
			    nla_put(skb, OVPN_GET_PEER_RESP_ATTR_LOCAL_IP,
				    sizeof(bind->local.ipv4), &bind->local.ipv4))
				goto err_unlock;
		} else if (bind->sa.in4.sin_family == AF_INET6) {
			if (nla_put(skb, OVPN_GET_PEER_RESP_ATTR_SOCKADDR_REMOTE,
				    sizeof(bind->sa.in6), &bind->sa.in6) ||
			    nla_put(skb, OVPN_GET_PEER_RESP_ATTR_LOCAL_IP,
				    sizeof(bind->local.ipv6), &bind->local.ipv6))
				goto err_unlock;
		}
	}
	rcu_read_unlock();

	if (nla_put_net16(skb, OVPN_GET_PEER_RESP_ATTR_LOCAL_PORT,
			  inet_sk(peer->sock->sock->sk)->inet_sport) ||
	    /* VPN RX stats */
	    nla_put_u64_64bit(skb, OVPN_GET_PEER_RESP_ATTR_VPN_RX_BYTES,
			      atomic64_read(&peer->vpn_stats.rx.bytes),
			      OVPN_GET_PEER_RESP_ATTR_UNSPEC) ||
	    nla_put_u32(skb, OVPN_GET_PEER_RESP_ATTR_VPN_RX_PACKETS,
			atomic_read(&peer->vpn_stats.rx.packets)) ||
	    /* VPN TX stats */
	    nla_put_u64_64bit(skb, OVPN_GET_PEER_RESP_ATTR_VPN_TX_BYTES,
			      atomic64_read(&peer->vpn_stats.tx.bytes),
			      OVPN_GET_PEER_RESP_ATTR_UNSPEC) ||
	    nla_put_u32(skb, OVPN_GET_PEER_RESP_ATTR_VPN_TX_PACKETS,
			atomic_read(&peer->vpn_stats.tx.packets)) ||
	    /* link RX stats */
	    nla_put_u64_64bit(skb, OVPN_GET_PEER_RESP_ATTR_LINK_RX_BYTES,
			      atomic64_read(&peer->link_stats.rx.bytes),
			      OVPN_GET_PEER_RESP_ATTR_UNSPEC) ||
	    nla_put_u32(skb, OVPN_GET_PEER_RESP_ATTR_LINK_RX_PACKETS,
			atomic_read(&peer->link_stats.rx.packets)) ||
	    /* link TX stats */
	    nla_put_u64_64bit(skb, OVPN_GET_PEER_RESP_ATTR_LINK_TX_BYTES,
			      atomic64_read(&peer->link_stats.tx.bytes),
			      OVPN_GET_PEER_RESP_ATTR_UNSPEC) ||
	    nla_put_u32(skb, OVPN_GET_PEER_RESP_ATTR_LINK_TX_PACKETS,
			atomic_read(&peer->link_stats.tx.packets)))
		goto err;

	nla_nest_end(skb, attr);
	genlmsg_end(skb, hdr);

	return 0;
err_unlock:
	rcu_read_unlock();
err:
	genlmsg_cancel(skb, hdr);
	return -EMSGSIZE;
}

static int ovpn_netlink_get_peer(struct sk_buff *skb, struct genl_info *info)
{
	struct nlattr *attrs[OVPN_SET_PEER_ATTR_MAX + 1];
	struct ovpn_struct *ovpn = info->user_ptr[0];
	struct ovpn_peer *peer;
	struct sk_buff *msg;
	u32 peer_id;
	int ret;

	if (!info->attrs[OVPN_ATTR_GET_PEER])
		return -EINVAL;

	ret = nla_parse_nested(attrs, OVPN_GET_PEER_ATTR_MAX, info->attrs[OVPN_ATTR_GET_PEER], NULL,
			       info->extack);
	if (ret)
		return ret;

	if (!attrs[OVPN_GET_PEER_ATTR_PEER_ID])
		return -EINVAL;

	peer_id = nla_get_u32(attrs[OVPN_GET_PEER_ATTR_PEER_ID]);
	peer = ovpn_peer_lookup_id(ovpn, peer_id);
	if (!peer)
		return -ENOENT;

	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
	if (!msg)
		return -ENOMEM;

	ret = ovpn_netlink_send_peer(msg, peer, info->snd_portid, info->snd_seq, 0);
	if (ret < 0) {
		nlmsg_free(msg);
		goto err;
	}

	ret = genlmsg_reply(msg, info);
err:
	ovpn_peer_put(peer);
	return ret;
}

static int ovpn_netlink_dump_peers(struct sk_buff *skb, struct netlink_callback *cb)
{
	struct net *netns = sock_net(cb->skb->sk);
	struct nlattr **attrbuf;
	struct ovpn_struct *ovpn;
	struct net_device *dev;
	int ret, bkt, last_idx = cb->args[1], dumped = 0;
	struct ovpn_peer *peer;

	attrbuf = kcalloc(OVPN_ATTR_MAX + 1, sizeof(*attrbuf), GFP_KERNEL);
	if (!attrbuf)
		return -ENOMEM;

	ret = nlmsg_parse_deprecated(cb->nlh, GENL_HDRLEN, attrbuf, OVPN_ATTR_MAX,
				     ovpn_netlink_policy, NULL);
	if (ret < 0) {
		pr_err("ovpn: cannot parse incoming request in %s: %d\n", __func__, ret);
		goto err;
	}

	dev = ovpn_get_dev_from_attrs(netns, attrbuf);
	if (IS_ERR(dev)) {
		ret = PTR_ERR(dev);
		pr_err("ovpn: cannot retrieve device in %s: %d\n", __func__, ret);
		goto err;
	}

	ovpn = netdev_priv(dev);

	rcu_read_lock();
	hash_for_each_rcu(ovpn->peers.by_id, bkt, peer, hash_entry_id) {
		/* skip already dumped peers that were dumped by previous invocations */
		if (last_idx > 0) {
			last_idx--;
			continue;
		}

		if (ovpn_netlink_send_peer(skb, peer, NETLINK_CB(cb->skb).portid,
					   cb->nlh->nlmsg_seq, NLM_F_MULTI) < 0)
			break;

		/* count peers being dumped during this invocation */
		dumped++;
	}
	rcu_read_unlock();

	dev_put(dev);

	/* sum up peers dumped in this message, so that at the next invocation
	 * we can continue from where we left
	 */
	cb->args[1] += dumped;
	ret = skb->len;
err:
	kfree(attrbuf);
	return ret;
}

static int ovpn_netlink_del_peer(struct sk_buff *skb, struct genl_info *info)
{
	struct nlattr *attrs[OVPN_SET_PEER_ATTR_MAX + 1];
	struct ovpn_struct *ovpn = info->user_ptr[0];
	struct ovpn_peer *peer;
	u32 peer_id;
	int ret;

	if (!info->attrs[OVPN_ATTR_DEL_PEER])
		return -EINVAL;

	ret = nla_parse_nested(attrs, OVPN_DEL_PEER_ATTR_MAX, info->attrs[OVPN_ATTR_DEL_PEER], NULL,
			       info->extack);
	if (ret)
		return ret;

	if (!attrs[OVPN_DEL_PEER_ATTR_PEER_ID])
		return -EINVAL;

	peer_id = nla_get_u32(attrs[OVPN_DEL_PEER_ATTR_PEER_ID]);

	peer = ovpn_peer_lookup_id(ovpn, peer_id);
	if (!peer)
		return -ENOENT;

	netdev_dbg(ovpn->dev, "%s: peer id=%u\n", __func__, peer->id);
	ret = ovpn_peer_del(peer, OVPN_DEL_PEER_REASON_USERSPACE);
	ovpn_peer_put(peer);

	return ret;
}

static const struct genl_small_ops ovpn_netlink_ops[] = {
	{
		.cmd = OVPN_CMD_NEW_PEER,
		.flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
		.doit = ovpn_netlink_new_peer,
	},
	{
		.cmd = OVPN_CMD_SET_PEER,
		.flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
		.doit = ovpn_netlink_set_peer,
	},
	{
		.cmd = OVPN_CMD_DEL_PEER,
		.flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
		.doit = ovpn_netlink_del_peer,
	},
	{
		.cmd = OVPN_CMD_GET_PEER,
		.flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO | GENL_CMD_CAP_DUMP,
		.doit = ovpn_netlink_get_peer,
		.dumpit = ovpn_netlink_dump_peers,
	},
	{
		.cmd = OVPN_CMD_NEW_KEY,
		.flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
		.doit = ovpn_netlink_new_key,
	},
	{
		.cmd = OVPN_CMD_DEL_KEY,
		.flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
		.doit = ovpn_netlink_del_key,
	},
	{
		.cmd = OVPN_CMD_SWAP_KEYS,
		.flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
		.doit = ovpn_netlink_swap_keys,
	},
};

static struct genl_family ovpn_netlink_family __ro_after_init = {
	.hdrsize = 0,
	.name = OVPN_NL_NAME,
	.version = 1,
	.maxattr = OVPN_ATTR_MAX,
	.policy = ovpn_netlink_policy,
	.netnsok = true,
	.pre_doit = ovpn_pre_doit,
	.post_doit = ovpn_post_doit,
	.module = THIS_MODULE,
	.small_ops = ovpn_netlink_ops,
	.n_small_ops = ARRAY_SIZE(ovpn_netlink_ops),
	.mcgrps = ovpn_netlink_mcgrps,
	.n_mcgrps = ARRAY_SIZE(ovpn_netlink_mcgrps),
};

int ovpn_netlink_notify_del_peer(struct ovpn_peer *peer)
{
	struct sk_buff *msg;
	struct nlattr *attr;
	void *hdr;
	int ret;

	netdev_info(peer->ovpn->dev, "deleting peer with id %u, reason %d\n",
		    peer->id, peer->delete_reason);

	msg = nlmsg_new(100, GFP_KERNEL);
	if (!msg)
		return -ENOMEM;

	hdr = genlmsg_put(msg, 0, 0, &ovpn_netlink_family, 0,
			  OVPN_CMD_DEL_PEER);
	if (!hdr) {
		ret = -ENOBUFS;
		goto err_free_msg;
	}

	if (nla_put_u32(msg, OVPN_ATTR_IFINDEX, peer->ovpn->dev->ifindex)) {
		ret = -EMSGSIZE;
		goto err_free_msg;
	}

	attr = nla_nest_start(msg, OVPN_ATTR_DEL_PEER);
	if (!attr) {
		ret = -EMSGSIZE;
		goto err_free_msg;
	}

	if (nla_put_u8(msg, OVPN_DEL_PEER_ATTR_REASON, peer->delete_reason)) {
		ret = -EMSGSIZE;
		goto err_free_msg;
	}

	if (nla_put_u32(msg, OVPN_DEL_PEER_ATTR_PEER_ID, peer->id)) {
		ret = -EMSGSIZE;
		goto err_free_msg;
	}

	nla_nest_end(msg, attr);

	genlmsg_end(msg, hdr);

	genlmsg_multicast_netns(&ovpn_netlink_family, dev_net(peer->ovpn->dev),
				msg, 0, OVPN_MCGRP_PEERS, GFP_KERNEL);

	return 0;

err_free_msg:
	nlmsg_free(msg);
	return ret;
}

static int ovpn_netlink_notify(struct notifier_block *nb, unsigned long state,
			       void *_notify)
{
	struct netlink_notify *notify = _notify;
	struct ovpn_struct *ovpn;
	struct net_device *dev;
	struct net *netns;
	bool found = false;

	if (state != NETLINK_URELEASE || notify->protocol != NETLINK_GENERIC)
		return NOTIFY_DONE;

	rcu_read_lock();
	for_each_net_rcu(netns) {
		for_each_netdev_rcu(netns, dev) {
			if (!ovpn_dev_is_valid(dev))
				continue;

			ovpn = netdev_priv(dev);
			if (notify->portid != ovpn->registered_nl_portid)
				continue;

			found = true;
			netdev_dbg(ovpn->dev, "%s: deregistering userspace listener\n", __func__);
			ovpn->registered_nl_portid_set = false;
			break;
		}
	}
	rcu_read_unlock();

	/* if no interface matched our purposes, pass the notification along */
	if (!found)
		return NOTIFY_DONE;

	return NOTIFY_OK;
}

static struct notifier_block ovpn_netlink_notifier = {
	.notifier_call = ovpn_netlink_notify,
};

int ovpn_netlink_init(struct ovpn_struct *ovpn)
{
	ovpn->registered_nl_portid_set = false;

	return 0;
}

/**
 * ovpn_netlink_register() - register the ovpn genl netlink family
 */
int __init ovpn_netlink_register(void)
{
	int ret;

	ret = genl_register_family(&ovpn_netlink_family);
	if (ret) {
		pr_err("ovpn: genl_register_family() failed: %d\n", ret);
		return ret;
	}

	ret = netlink_register_notifier(&ovpn_netlink_notifier);
	if (ret) {
		pr_err("ovpn: netlink_register_notifier() failed: %d\n", ret);
		goto err;
	}

	return 0;
err:
	genl_unregister_family(&ovpn_netlink_family);
	return ret;
}

/**
 * ovpn_netlink_unregister() - unregister the ovpn genl netlink family
 */
void __exit ovpn_netlink_unregister(void)
{
	netlink_unregister_notifier(&ovpn_netlink_notifier);
	genl_unregister_family(&ovpn_netlink_family);
}
07070100000015000081A400000000000000000000000165BCF43700000274000000000000000000000000000000000000004200000000ovpn-dco-0.2.20240320~git0.2aa7f93/drivers/net/ovpn-dco/netlink.h/* SPDX-License-Identifier: GPL-2.0-only */
/*  OpenVPN data channel accelerator
 *
 *  Copyright (C) 2020-2023 OpenVPN, Inc.
 *
 *  Author:	Antonio Quartulli <antonio@openvpn.net>
 */

#ifndef _NET_OVPN_DCO_NETLINK_H_
#define _NET_OVPN_DCO_NETLINK_H_

struct ovpn_struct;
struct ovpn_peer;

int ovpn_netlink_init(struct ovpn_struct *ovpn);
int ovpn_netlink_register(void);
void ovpn_netlink_unregister(void);
int ovpn_netlink_send_packet(struct ovpn_struct *ovpn, const struct ovpn_peer *peer,
			     const u8 *buf, size_t len);
int ovpn_netlink_notify_del_peer(struct ovpn_peer *peer);

#endif /* _NET_OVPN_DCO_NETLINK_H_ */
07070100000016000081A400000000000000000000000165BCF437000037FC000000000000000000000000000000000000003F00000000ovpn-dco-0.2.20240320~git0.2aa7f93/drivers/net/ovpn-dco/ovpn.c// SPDX-License-Identifier: GPL-2.0
/*  OpenVPN data channel accelerator
 *
 *  Copyright (C) 2019-2023 OpenVPN, Inc.
 *
 *  Author:	James Yonan <james@openvpn.net>
 *		Antonio Quartulli <antonio@openvpn.net>
 */

#include "main.h"
#include "bind.h"
#include "netlink.h"
#include "ovpn.h"
#include "sock.h"
#include "peer.h"
#include "stats.h"
#include "proto.h"
#include "crypto.h"
#include "crypto_aead.h"
#include "skb.h"
#include "tcp.h"
#include "udp.h"

#include <linux/workqueue.h>
#include <net/gso.h>
#include <uapi/linux/if_ether.h>

static const unsigned char ovpn_keepalive_message[] = {
	0x2a, 0x18, 0x7b, 0xf3, 0x64, 0x1e, 0xb4, 0xcb,
	0x07, 0xed, 0x2d, 0x0a, 0x98, 0x1f, 0xc7, 0x48
};

static const unsigned char ovpn_explicit_exit_notify_message[] = {
	0x28, 0x7f, 0x34, 0x6b, 0xd4, 0xef, 0x7a, 0x81,
	0x2d, 0x56, 0xb8, 0xd3, 0xaf, 0xc5, 0x45, 0x9c,
	6 // OCC_EXIT
};

/* Is keepalive message?
 * Assumes that single byte at skb->data is defined.
 */
static bool ovpn_is_keepalive(struct sk_buff *skb)
{
	if (*skb->data != OVPN_KEEPALIVE_FIRST_BYTE)
		return false;

	if (!pskb_may_pull(skb, sizeof(ovpn_keepalive_message)))
		return false;

	return !memcmp(skb->data, ovpn_keepalive_message,
		       sizeof(ovpn_keepalive_message));
}

int ovpn_struct_init(struct net_device *dev)
{
	struct ovpn_struct *ovpn = netdev_priv(dev);
	int err;

	memset(ovpn, 0, sizeof(*ovpn));

	ovpn->dev = dev;

	err = ovpn_netlink_init(ovpn);
	if (err < 0)
		return err;

	spin_lock_init(&ovpn->lock);
	spin_lock_init(&ovpn->peers.lock);

	ovpn->crypto_wq = alloc_workqueue("ovpn-crypto-wq-%s",
					  WQ_CPU_INTENSIVE | WQ_MEM_RECLAIM, 0,
					  dev->name);
	if (!ovpn->crypto_wq)
		return -ENOMEM;

	ovpn->events_wq = alloc_workqueue("ovpn-event-wq-%s", WQ_MEM_RECLAIM, 0, dev->name);
	if (!ovpn->events_wq)
		return -ENOMEM;

	dev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats);
	if (!dev->tstats)
		return -ENOMEM;

	err = security_tun_dev_alloc_security(&ovpn->security);
	if (err < 0)
		return err;

	/* kernel -> userspace tun queue length */
	ovpn->max_tun_queue_len = OVPN_MAX_TUN_QUEUE_LEN;

	return 0;
}

/* Called after decrypt to write IP packet to tun netdev.
 * This method is expected to manage/free skb.
 */
static void tun_netdev_write(struct ovpn_peer *peer, struct sk_buff *skb)
{
	/* packet integrity was verified on the VPN layer - no need to perform
	 * any additional check along the stack
	 */
	skb->ip_summed = CHECKSUM_UNNECESSARY;
	skb->csum_level = ~0;

	/* skb hash for transport packet no longer valid after decapsulation */
	skb_clear_hash(skb);

	/* post-decrypt scrub -- prepare to inject encapsulated packet onto tun
	 * interface, based on __skb_tunnel_rx() in dst.h
	 */
	skb->dev = peer->ovpn->dev;
	skb_set_queue_mapping(skb, 0);
	skb_scrub_packet(skb, true);

	skb_reset_network_header(skb);
	skb_reset_transport_header(skb);
	skb_probe_transport_header(skb);
	skb_reset_inner_headers(skb);

	/* update per-cpu RX stats with the stored size of encrypted packet */

	/* we are in softirq context - hence no locking nor disable preemption needed */
	dev_sw_netstats_rx_add(peer->ovpn->dev, skb->len);

	/* cause packet to be "received" by tun interface */
	napi_gro_receive(&peer->napi, skb);
}

int ovpn_napi_poll(struct napi_struct *napi, int budget)
{
	struct ovpn_peer *peer = container_of(napi, struct ovpn_peer, napi);
	struct sk_buff *skb;
	int work_done = 0;

	if (unlikely(budget <= 0))
		return 0;
	/* this function should schedule at most 'budget' number of
	 * packets for delivery to the tun interface.
	 * If in the queue we have more packets than what allowed by the
	 * budget, the next polling will take care of those
	 */
	while ((work_done < budget) &&
	       (skb = ptr_ring_consume_bh(&peer->netif_rx_ring))) {
		tun_netdev_write(peer, skb);
		work_done++;
	}

	if (work_done < budget)
		napi_complete_done(napi, work_done);

	return work_done;
}

/* Entry point for processing an incoming packet (in skb form)
 *
 * Enqueue the packet and schedule RX consumer.
 * Reference to peer is dropped only in case of success.
 *
 * Return 0  if the packet was handled (and consumed)
 * Return <0 in case of error (return value is error code)
 */
int ovpn_recv(struct ovpn_struct *ovpn, struct ovpn_peer *peer, struct sk_buff *skb)
{
	if (unlikely(ptr_ring_produce_bh(&peer->rx_ring, skb) < 0))
		return -ENOSPC;

	if (!queue_work(ovpn->crypto_wq, &peer->decrypt_work))
		ovpn_peer_put(peer);

	return 0;
}

static int ovpn_decrypt_one(struct ovpn_peer *peer, struct sk_buff *skb)
{
	struct ovpn_peer *allowed_peer = NULL;
	struct ovpn_crypto_key_slot *ks;
	__be16 proto;
	int ret = -1;
	u8 key_id;

	ovpn_peer_stats_increment_rx(&peer->link_stats, skb->len);

	/* get the key slot matching the key Id in the received packet */
	key_id = ovpn_key_id_from_skb(skb);
	ks = ovpn_crypto_key_id_to_slot(&peer->crypto, key_id);
	if (unlikely(!ks)) {
		net_info_ratelimited("%s: no available key for peer %u, key-id: %u\n", __func__,
				    peer->id, key_id);
		goto drop;
	}

	/* decrypt */
	ret = ovpn_aead_decrypt(ks, skb);

	ovpn_crypto_key_slot_put(ks);

	if (unlikely(ret < 0)) {
		net_err_ratelimited("%s: error during decryption for peer %u, key-id %u: %d\n",
				   __func__, peer->id, key_id, ret);
		goto drop;
	}

	/* note event of authenticated packet received for keepalive */
	ovpn_peer_keepalive_recv_reset(peer);

	/* update source and destination endpoint for this peer */
	if (peer->sock->sock->sk->sk_protocol == IPPROTO_UDP)
		ovpn_peer_update_local_endpoint(peer, skb);

	/* increment RX stats */
	ovpn_peer_stats_increment_rx(&peer->vpn_stats, skb->len);

	/* check if this is a valid datapacket that has to be delivered to the
	 * tun interface
	 */
	skb_reset_network_header(skb);
	proto = ovpn_ip_check_protocol(skb);
	if (unlikely(!proto)) {
		/* check if null packet */
		if (unlikely(!pskb_may_pull(skb, 1))) {
			ret = -EINVAL;
			goto drop;
		}

		/* check if special OpenVPN message */
		if (ovpn_is_keepalive(skb)) {
			netdev_dbg(peer->ovpn->dev, "%s: ping received from peer with id %u\n",
				   __func__, peer->id);
			/* not an error */
			consume_skb(skb);
			/* inform the caller that NAPI should not be scheduled
			 * for this packet
			 */
			return -1;
		}

		ret = -EPROTONOSUPPORT;
		goto drop;
	}
	skb->protocol = proto;

	/* perform Reverse Path Filtering (RPF) */
	allowed_peer = ovpn_peer_lookup_vpn_addr(peer->ovpn, skb, true);
	if (unlikely(allowed_peer != peer)) {
		ret = -EPERM;
		goto drop;
	}

	ret = ptr_ring_produce_bh(&peer->netif_rx_ring, skb);
drop:
	if (likely(allowed_peer))
		ovpn_peer_put(allowed_peer);

	if (unlikely(ret < 0))
		kfree_skb(skb);

	return ret;
}

/* pick packet from RX queue, decrypt and forward it to the tun device */
void ovpn_decrypt_work(struct work_struct *work)
{
	struct ovpn_peer *peer;
	struct sk_buff *skb;

	peer = container_of(work, struct ovpn_peer, decrypt_work);
	while ((skb = ptr_ring_consume_bh(&peer->rx_ring))) {
		if (likely(ovpn_decrypt_one(peer, skb) == 0)) {
			/* if a packet has been enqueued for NAPI, signal
			 * availability to the networking stack
			 */
			local_bh_disable();
			napi_schedule(&peer->napi);
			local_bh_enable();
		}

		/* give a chance to be rescheduled if needed */
		cond_resched();
	}
	ovpn_peer_put(peer);
}

static bool ovpn_encrypt_one(struct ovpn_peer *peer, struct sk_buff *skb)
{
	struct ovpn_crypto_key_slot *ks;
	bool success = false;
	int ret;

	/* get primary key to be used for encrypting data */
	ks = ovpn_crypto_key_slot_primary(&peer->crypto);
	if (unlikely(!ks)) {
		net_warn_ratelimited("%s: error while retrieving primary key slot\n", __func__);
		return false;
	}

	if (unlikely(skb->ip_summed == CHECKSUM_PARTIAL &&
		     skb_checksum_help(skb))) {
		net_err_ratelimited("%s: cannot compute checksum for outgoing packet\n", __func__);
		goto err;
	}

	ovpn_peer_stats_increment_tx(&peer->vpn_stats, skb->len);

	/* encrypt */
	ret = ovpn_aead_encrypt(ks, skb, peer->id);
	if (unlikely(ret < 0)) {
		/* if we ran out of IVs we must kill the key as it can't be used anymore */
		if (ret == -ERANGE) {
			netdev_warn(peer->ovpn->dev,
				    "%s: killing primary key as we ran out of IVs\n", __func__);
			ovpn_crypto_kill_primary(&peer->crypto);
			goto err;
		}
		net_err_ratelimited("%s: error during encryption for peer %u, key-id %u: %d\n",
				   __func__, peer->id, ks->key_id, ret);
		goto err;
	}

	success = true;

	ovpn_peer_stats_increment_tx(&peer->link_stats, skb->len);
err:
	ovpn_crypto_key_slot_put(ks);
	return success;
}

/* Process packets in TX queue in a transport-specific way.
 *
 * UDP transport - encrypt and send across the tunnel.
 * TCP transport - encrypt and put into TCP TX queue.
 */
void ovpn_encrypt_work(struct work_struct *work)
{
	struct sk_buff *skb, *curr, *next;
	struct ovpn_peer *peer;

	peer = container_of(work, struct ovpn_peer, encrypt_work);
	while ((skb = ptr_ring_consume_bh(&peer->tx_ring))) {
		/* this might be a GSO-segmented skb list: process each skb
		 * independently
		 */
		skb_list_walk_safe(skb, curr, next) {
			/* if one segment fails encryption, we drop the entire
			 * packet, because it does not really make sense to send
			 * only part of it at this point
			 */
			if (unlikely(!ovpn_encrypt_one(peer, curr))) {
				kfree_skb_list(skb);
				skb = NULL;
				break;
			}
		}

		/* successful encryption */
		if (skb) {
			skb_list_walk_safe(skb, curr, next) {
				skb_mark_not_on_list(curr);

				switch (peer->sock->sock->sk->sk_protocol) {
				case IPPROTO_UDP:
					ovpn_udp_send_skb(peer->ovpn, peer, curr);
					break;
				case IPPROTO_TCP:
					ovpn_tcp_send_skb(peer, curr);
					break;
				default:
					/* no transport configured yet */
					consume_skb(skb);
					break;
				}
			}

			/* note event of authenticated packet xmit for keepalive */
			ovpn_peer_keepalive_xmit_reset(peer);
		}

		/* give a chance to be rescheduled if needed */
		cond_resched();
	}
	ovpn_peer_put(peer);
}

/* Put skb into TX queue and schedule a consumer */
static void ovpn_queue_skb(struct ovpn_struct *ovpn, struct sk_buff *skb, struct ovpn_peer *peer)
{
	int ret;

	if (likely(!peer))
		peer = ovpn_peer_lookup_vpn_addr(ovpn, skb, false);
	if (unlikely(!peer)) {
		net_dbg_ratelimited("%s: no peer to send data to\n", ovpn->dev->name);
		goto drop;
	}

	ret = ptr_ring_produce_bh(&peer->tx_ring, skb);
	if (unlikely(ret < 0)) {
		net_err_ratelimited("%s: cannot queue packet to TX ring\n", __func__);
		goto drop;
	}

	if (!queue_work(ovpn->crypto_wq, &peer->encrypt_work))
		ovpn_peer_put(peer);

	return;
drop:
	if (peer)
		ovpn_peer_put(peer);
	kfree_skb_list(skb);
}

/* Net device start xmit
 */
netdev_tx_t ovpn_net_xmit(struct sk_buff *skb, struct net_device *dev)
{
	struct ovpn_struct *ovpn = netdev_priv(dev);
	struct sk_buff *segments, *tmp, *curr, *next;
	struct sk_buff_head skb_list;
	__be16 proto;
	int ret;

	/* reset netfilter state */
	nf_reset_ct(skb);

	/* verify IP header size in network packet */
	proto = ovpn_ip_check_protocol(skb);
	if (unlikely(!proto || skb->protocol != proto)) {
		net_err_ratelimited("%s: dropping malformed payload packet\n",
				    dev->name);
		goto drop;
	}

	if (skb_is_gso(skb)) {
		segments = skb_gso_segment(skb, 0);
		if (IS_ERR(segments)) {
			ret = PTR_ERR(segments);
			net_err_ratelimited("%s: cannot segment packet: %d\n", dev->name, ret);
			goto drop;
		}

		consume_skb(skb);
		skb = segments;
	}

	/* from this moment on, "skb" might be a list */

	__skb_queue_head_init(&skb_list);
	skb_list_walk_safe(skb, curr, next) {
		skb_mark_not_on_list(curr);

		tmp = skb_share_check(curr, GFP_ATOMIC);
		if (unlikely(!tmp)) {
			kfree_skb_list(next);
			net_err_ratelimited("%s: skb_share_check failed\n", dev->name);
			goto drop_list;
		}

		__skb_queue_tail(&skb_list, tmp);
	}
	skb_list.prev->next = NULL;

	ovpn_queue_skb(ovpn, skb_list.next, NULL);

	return NETDEV_TX_OK;

drop_list:
	skb_queue_walk_safe(&skb_list, curr, next)
		kfree_skb(curr);
drop:
	skb_tx_error(skb);
	kfree_skb_list(skb);
	return NET_XMIT_DROP;
}

/* Encrypt and transmit a special message to peer, such as keepalive
 * or explicit-exit-notify.  Called from softirq context.
 * Assumes that caller holds a reference to peer.
 */
static void ovpn_xmit_special(struct ovpn_peer *peer, const void *data,
			      const unsigned int len)
{
	struct ovpn_struct *ovpn;
	struct sk_buff *skb;

	ovpn = peer->ovpn;
	if (unlikely(!ovpn))
		return;

	skb = alloc_skb(256 + len, GFP_ATOMIC);
	if (unlikely(!skb))
		return;

	skb_reserve(skb, 128);
	skb->priority = TC_PRIO_BESTEFFORT;
	memcpy(__skb_put(skb, len), data, len);

	/* increase reference counter when passing peer to sending queue */
	if (!ovpn_peer_hold(peer)) {
		netdev_dbg(ovpn->dev, "%s: cannot hold peer reference for sending special packet\n",
			   __func__);
		kfree_skb(skb);
		return;
	}

	ovpn_queue_skb(ovpn, skb, peer);
}

void ovpn_keepalive_xmit(struct ovpn_peer *peer)
{
	ovpn_xmit_special(peer, ovpn_keepalive_message,
			  sizeof(ovpn_keepalive_message));
}

/* Transmit explicit exit notification.
 * Called from process context.
 */
void ovpn_explicit_exit_notify_xmit(struct ovpn_peer *peer)
{
	ovpn_xmit_special(peer, ovpn_explicit_exit_notify_message,
			  sizeof(ovpn_explicit_exit_notify_message));
}

/* Copy buffer into skb and send it across the tunnel.
 *
 * For UDP transport: just sent the skb to peer
 * For TCP transport: put skb into TX queue
 */
int ovpn_send_data(struct ovpn_struct *ovpn, u32 peer_id, const u8 *data, size_t len)
{
	u16 skb_len = SKB_HEADER_LEN + len;
	struct ovpn_peer *peer;
	struct sk_buff *skb;
	bool tcp = false;
	int ret = 0;

	peer = ovpn_peer_lookup_id(ovpn, peer_id);
	if (unlikely(!peer)) {
		netdev_dbg(ovpn->dev, "no peer to send data to\n");
		return -EHOSTUNREACH;
	}

	if (peer->sock->sock->sk->sk_protocol == IPPROTO_TCP) {
		skb_len += sizeof(u16);
		tcp = true;
	}

	skb = alloc_skb(skb_len, GFP_ATOMIC);
	if (unlikely(!skb)) {
		ret = -ENOMEM;
		goto out;
	}

	skb_reserve(skb, SKB_HEADER_LEN);
	skb_put_data(skb, data, len);

	/* prepend TCP packet with size, as required by OpenVPN protocol */
	if (tcp) {
		*(__be16 *)__skb_push(skb, sizeof(u16)) = htons(len);
		ovpn_queue_tcp_skb(peer, skb);
	} else {
		ovpn_udp_send_skb(ovpn, peer, skb);
	}
out:
	ovpn_peer_put(peer);
	return ret;
}
07070100000017000081A400000000000000000000000165BCF4370000049F000000000000000000000000000000000000003F00000000ovpn-dco-0.2.20240320~git0.2aa7f93/drivers/net/ovpn-dco/ovpn.h/* SPDX-License-Identifier: GPL-2.0-only */
/* OpenVPN data channel accelerator
 *
 *  Copyright (C) 2019-2023 OpenVPN, Inc.
 *
 *  Author:	James Yonan <james@openvpn.net>
 *		Antonio Quartulli <antonio@openvpn.net>
 */

#ifndef _NET_OVPN_DCO_OVPN_H_
#define _NET_OVPN_DCO_OVPN_H_

#include "main.h"
#include "peer.h"
#include "sock.h"
#include "ovpnstruct.h"

#include <linux/workqueue.h>
#include <linux/types.h>
#include <net/sock.h>

struct ovpn_struct;
struct net_device;

int ovpn_struct_init(struct net_device *dev);

u16 ovpn_select_queue(struct net_device *dev, struct sk_buff *skb,
		      struct net_device *sb_dev);

void ovpn_keepalive_xmit(struct ovpn_peer *peer);
void ovpn_explicit_exit_notify_xmit(struct ovpn_peer *peer);

netdev_tx_t ovpn_net_xmit(struct sk_buff *skb, struct net_device *dev);

int ovpn_recv(struct ovpn_struct *ovpn, struct ovpn_peer *peer, struct sk_buff *skb);

void ovpn_encrypt_work(struct work_struct *work);
void ovpn_decrypt_work(struct work_struct *work);
int ovpn_napi_poll(struct napi_struct *napi, int budget);

int ovpn_send_data(struct ovpn_struct *ovpn, u32 peer_id, const u8 *data, size_t len);

#endif /* _NET_OVPN_DCO_OVPN_H_ */
07070100000018000081A400000000000000000000000165BCF4370000057B000000000000000000000000000000000000004500000000ovpn-dco-0.2.20240320~git0.2aa7f93/drivers/net/ovpn-dco/ovpnstruct.h/* SPDX-License-Identifier: GPL-2.0-only */
/*  OpenVPN data channel accelerator
 *
 *  Copyright (C) 2019-2023 OpenVPN, Inc.
 *
 *  Author:	James Yonan <james@openvpn.net>
 *		Antonio Quartulli <antonio@openvpn.net>
 */

#ifndef _NET_OVPN_DCO_OVPNSTRUCT_H_
#define _NET_OVPN_DCO_OVPNSTRUCT_H_

#include "peer.h"

#include <uapi/linux/ovpn_dco.h>
#include <linux/spinlock.h>
#include <linux/workqueue.h>

/* Our state per ovpn interface */
struct ovpn_struct {
	/* read-mostly objects in this section */
	struct net_device *dev;

	/* device operation mode (i.e. P2P, MP) */
	enum ovpn_mode mode;

	/* protect writing to the ovpn_struct object */
	spinlock_t lock;

	/* workqueue used to schedule crypto work that may sleep */
	struct workqueue_struct *crypto_wq;
	/* workqueue used to schedule generic event that may sleep or that need
	 * to be performed out of softirq context
	 */
	struct workqueue_struct *events_wq;

	/* list of known peers */
	struct {
		DECLARE_HASHTABLE(by_id, 12);
		DECLARE_HASHTABLE(by_transp_addr, 12);
		DECLARE_HASHTABLE(by_vpn_addr, 12);
		/* protects write access to any of the hashtables above */
		spinlock_t lock;
	} peers;

	/* for p2p mode */
	struct ovpn_peer __rcu *peer;

	unsigned int max_tun_queue_len;

	netdev_features_t set_features;

	void *security;

	u32 registered_nl_portid;
	bool registered_nl_portid_set;
};

#endif /* _NET_OVPN_DCO_OVPNSTRUCT_H_ */
07070100000019000081A400000000000000000000000165BCF437000055F5000000000000000000000000000000000000003F00000000ovpn-dco-0.2.20240320~git0.2aa7f93/drivers/net/ovpn-dco/peer.c// SPDX-License-Identifier: GPL-2.0
/*  OpenVPN data channel accelerator
 *
 *  Copyright (C) 2020-2023 OpenVPN, Inc.
 *
 *  Author:	James Yonan <james@openvpn.net>
 *		Antonio Quartulli <antonio@openvpn.net>
 */

#include "ovpn.h"
#include "bind.h"
#include "crypto.h"
#include "peer.h"
#include "netlink.h"
#include "tcp.h"

#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/list.h>
#include <linux/timer.h>
#include <linux/workqueue.h>

static void ovpn_peer_ping(struct timer_list *t)
{
	struct ovpn_peer *peer = from_timer(peer, t, keepalive_xmit);

	netdev_dbg(peer->ovpn->dev, "%s: sending ping to peer %u\n", __func__, peer->id);
	ovpn_keepalive_xmit(peer);
}

static void ovpn_peer_expire(struct timer_list *t)
{
	struct ovpn_peer *peer = from_timer(peer, t, keepalive_recv);

	netdev_dbg(peer->ovpn->dev, "%s: peer %u expired\n", __func__, peer->id);
	ovpn_peer_del(peer, OVPN_DEL_PEER_REASON_EXPIRED);
}

/* Construct a new peer */
static struct ovpn_peer *ovpn_peer_create(struct ovpn_struct *ovpn, u32 id)
{
	struct ovpn_peer *peer;
	int ret;

	/* alloc and init peer object */
	peer = kzalloc(sizeof(*peer), GFP_KERNEL);
	if (!peer)
		return ERR_PTR(-ENOMEM);

	peer->id = id;
	peer->halt = false;
	peer->ovpn = ovpn;

	peer->vpn_addrs.ipv4.s_addr = htonl(INADDR_ANY);
	peer->vpn_addrs.ipv6 = in6addr_any;

	RCU_INIT_POINTER(peer->bind, NULL);
	ovpn_crypto_state_init(&peer->crypto);
	spin_lock_init(&peer->lock);
	kref_init(&peer->refcount);
	ovpn_peer_stats_init(&peer->vpn_stats);
	ovpn_peer_stats_init(&peer->link_stats);

	INIT_WORK(&peer->encrypt_work, ovpn_encrypt_work);
	INIT_WORK(&peer->decrypt_work, ovpn_decrypt_work);

	ret = dst_cache_init(&peer->dst_cache, GFP_KERNEL);
	if (ret < 0) {
		netdev_err(ovpn->dev, "%s: cannot initialize dst cache\n", __func__);
		goto err;
	}

	ret = ptr_ring_init(&peer->tx_ring, OVPN_QUEUE_LEN, GFP_KERNEL);
	if (ret < 0) {
		netdev_err(ovpn->dev, "%s: cannot allocate TX ring\n", __func__);
		goto err_dst_cache;
	}

	ret = ptr_ring_init(&peer->rx_ring, OVPN_QUEUE_LEN, GFP_KERNEL);
	if (ret < 0) {
		netdev_err(ovpn->dev, "%s: cannot allocate RX ring\n", __func__);
		goto err_tx_ring;
	}

	ret = ptr_ring_init(&peer->netif_rx_ring, OVPN_QUEUE_LEN, GFP_KERNEL);
	if (ret < 0) {
		netdev_err(ovpn->dev, "%s: cannot allocate NETIF RX ring\n", __func__);
		goto err_rx_ring;
	}

	/* configure and start NAPI */
	netif_napi_add_tx_weight(ovpn->dev, &peer->napi, ovpn_napi_poll,
				 NAPI_POLL_WEIGHT);
	napi_enable(&peer->napi);

	dev_hold(ovpn->dev);

	timer_setup(&peer->keepalive_xmit, ovpn_peer_ping, 0);
	timer_setup(&peer->keepalive_recv, ovpn_peer_expire, 0);

	return peer;
err_rx_ring:
	ptr_ring_cleanup(&peer->rx_ring, NULL);
err_tx_ring:
	ptr_ring_cleanup(&peer->tx_ring, NULL);
err_dst_cache:
	dst_cache_destroy(&peer->dst_cache);
err:
	kfree(peer);
	return ERR_PTR(ret);
}

/* Reset the ovpn_sockaddr associated with a peer */
static int ovpn_peer_reset_sockaddr(struct ovpn_peer *peer, const struct sockaddr_storage *ss,
				    const u8 *local_ip)
{
	struct ovpn_bind *bind;
	size_t ip_len;

	/* create new ovpn_bind object */
	bind = ovpn_bind_from_sockaddr(ss);
	if (IS_ERR(bind))
		return PTR_ERR(bind);

	if (local_ip) {
		if (ss->ss_family == AF_INET) {
			ip_len = sizeof(struct in_addr);
		} else if (ss->ss_family == AF_INET6) {
			ip_len = sizeof(struct in6_addr);
		} else {
			netdev_dbg(peer->ovpn->dev, "%s: invalid family for remote endpoint\n",
				   __func__);
			kfree(bind);
			return -EINVAL;
		}

		memcpy(&bind->local, local_ip, ip_len);
	}

	/* set binding */
	ovpn_bind_reset(peer, bind);

	return 0;
}

void ovpn_peer_float(struct ovpn_peer *peer, struct sk_buff *skb)
{
	struct sockaddr_storage ss;
	const u8 *local_ip = NULL;
	struct sockaddr_in6 *sa6;
	struct sockaddr_in *sa;
	struct ovpn_bind *bind;
	sa_family_t family;

	rcu_read_lock();
	bind = rcu_dereference(peer->bind);
	if (unlikely(!bind))
		goto unlock;

	if (likely(ovpn_bind_skb_src_match(bind, skb)))
		goto unlock;

	family = skb_protocol_to_family(skb);

	if (bind->sa.in4.sin_family == family)
		local_ip = (u8 *)&bind->local;

	switch (family) {
	case AF_INET:
		sa = (struct sockaddr_in *)&ss;
		sa->sin_family = AF_INET;
		sa->sin_addr.s_addr = ip_hdr(skb)->saddr;
		sa->sin_port = udp_hdr(skb)->source;
		break;
	case AF_INET6:
		sa6 = (struct sockaddr_in6 *)&ss;
		sa6->sin6_family = AF_INET6;
		sa6->sin6_addr = ipv6_hdr(skb)->saddr;
		sa6->sin6_port = udp_hdr(skb)->source;
		sa6->sin6_scope_id = ipv6_iface_scope_id(&ipv6_hdr(skb)->saddr, skb->skb_iif);
		break;
	default:
		goto unlock;
	}

	netdev_dbg(peer->ovpn->dev, "%s: peer %d floated to %pIScp", __func__, peer->id, &ss);
	ovpn_peer_reset_sockaddr(peer, (struct sockaddr_storage *)&ss, local_ip);
unlock:
	rcu_read_unlock();
}

static void ovpn_peer_timer_delete_all(struct ovpn_peer *peer)
{
	del_timer_sync(&peer->keepalive_xmit);
	del_timer_sync(&peer->keepalive_recv);
}

static void ovpn_peer_free(struct ovpn_peer *peer)
{
	ovpn_bind_reset(peer, NULL);
	ovpn_peer_timer_delete_all(peer);

	WARN_ON(!__ptr_ring_empty(&peer->tx_ring));
	ptr_ring_cleanup(&peer->tx_ring, NULL);
	WARN_ON(!__ptr_ring_empty(&peer->rx_ring));
	ptr_ring_cleanup(&peer->rx_ring, NULL);
	WARN_ON(!__ptr_ring_empty(&peer->netif_rx_ring));
	ptr_ring_cleanup(&peer->netif_rx_ring, NULL);

	dst_cache_destroy(&peer->dst_cache);

	dev_put(peer->ovpn->dev);

	kfree(peer);
}

static void ovpn_peer_release_rcu(struct rcu_head *head)
{
	struct ovpn_peer *peer = container_of(head, struct ovpn_peer, rcu);

	ovpn_crypto_state_release(&peer->crypto);
	ovpn_peer_free(peer);
}

void ovpn_peer_release(struct ovpn_peer *peer)
{
	napi_disable(&peer->napi);
	netif_napi_del(&peer->napi);

	if (peer->sock)
		ovpn_socket_put(peer->sock);

	call_rcu(&peer->rcu, ovpn_peer_release_rcu);
}

static void ovpn_peer_delete_work(struct work_struct *work)
{
	struct ovpn_peer *peer = container_of(work, struct ovpn_peer,
					      delete_work);
	ovpn_peer_release(peer);
	ovpn_netlink_notify_del_peer(peer);
}

/* Use with kref_put calls, when releasing refcount
 * on ovpn_peer objects.  This method should only
 * be called from process context with config_mutex held.
 */
void ovpn_peer_release_kref(struct kref *kref)
{
	struct ovpn_peer *peer = container_of(kref, struct ovpn_peer, refcount);

	INIT_WORK(&peer->delete_work, ovpn_peer_delete_work);
	queue_work(peer->ovpn->events_wq, &peer->delete_work);
}

struct ovpn_peer *ovpn_peer_new(struct ovpn_struct *ovpn, const struct sockaddr_storage *sa,
				struct socket *sock, u32 id, uint8_t *local_ip)
{
	struct ovpn_peer *peer;
	int ret;

	/* create new peer */
	peer = ovpn_peer_create(ovpn, id);
	if (IS_ERR(peer))
		return peer;

	if (sock->sk->sk_protocol == IPPROTO_UDP) {
		/* a UDP peer must have a remote endpoint */
		if (!sa) {
			ovpn_peer_release(peer);
			return ERR_PTR(-EINVAL);
		}

		/* set peer sockaddr */
		ret = ovpn_peer_reset_sockaddr(peer, sa, local_ip);
		if (ret < 0) {
			ovpn_peer_release(peer);
			return ERR_PTR(ret);
		}
	}

	peer->sock = ovpn_socket_new(sock, peer);
	if (IS_ERR(peer->sock)) {
		peer->sock = NULL;
		ovpn_peer_release(peer);
		return ERR_PTR(-ENOTSOCK);
	}

	return peer;
}

/* Configure keepalive parameters */
void ovpn_peer_keepalive_set(struct ovpn_peer *peer, u32 interval, u32 timeout)
{
	u32 delta;

	netdev_dbg(peer->ovpn->dev,
		   "%s: scheduling keepalive for peer %u: interval=%u timeout=%u\n", __func__,
		   peer->id, interval, timeout);

	peer->keepalive_interval = interval;
	if (interval > 0) {
		delta = msecs_to_jiffies(interval * MSEC_PER_SEC);
		mod_timer(&peer->keepalive_xmit, jiffies + delta);
	} else {
		del_timer(&peer->keepalive_xmit);
	}

	peer->keepalive_timeout = timeout;
	if (timeout) {
		delta = msecs_to_jiffies(timeout * MSEC_PER_SEC);
		mod_timer(&peer->keepalive_recv, jiffies + delta);
	} else {
		del_timer(&peer->keepalive_recv);
	}
}

#define ovpn_peer_index(_tbl, _key, _key_len)		\
	(jhash(_key, _key_len, 0) % HASH_SIZE(_tbl))	\

static struct ovpn_peer *ovpn_peer_lookup_vpn_addr4(struct hlist_head *head, __be32 *addr)
{
	struct ovpn_peer *tmp, *peer = NULL;

	rcu_read_lock();
	hlist_for_each_entry_rcu(tmp, head, hash_entry_addr4) {
		if (*addr != tmp->vpn_addrs.ipv4.s_addr)
			continue;

		if (!ovpn_peer_hold(tmp))
			continue;

		peer = tmp;
		break;
	}
	rcu_read_unlock();

	return peer;
}

static struct ovpn_peer *ovpn_peer_lookup_vpn_addr6(struct hlist_head *head, struct in6_addr *addr)
{
	struct ovpn_peer *tmp, *peer = NULL;
	int i;

	rcu_read_lock();
	hlist_for_each_entry_rcu(tmp, head, hash_entry_addr6) {
		for (i = 0; i < 4; i++) {
			if (addr->s6_addr32[i] != tmp->vpn_addrs.ipv6.s6_addr32[i])
				continue;
		}

		if (!ovpn_peer_hold(tmp))
			continue;

		peer = tmp;
		break;
	}
	rcu_read_unlock();

	return peer;
}

/**
 * ovpn_nexthop4() - looks up the IP of the nexthop for the given destination
 *
 * Looks up in the IPv4 system routing table the IO of the nexthop to be used
 * to reach the destination passed as argument. IF no nexthop can be found, the
 * destination itself is returned as it probably has to be used as nexthop.
 *
 * @ovpn: the private data representing the current VPN session
 * @dst: the destination to be looked up
 *
 * Return the IP of the next hop if found or the dst itself otherwise
 */
static __be32 ovpn_nexthop4(struct ovpn_struct *ovpn, __be32 dst)
{
	struct rtable *rt;
	struct flowi4 fl = {
		.daddr = dst
	};

	rt = ip_route_output_flow(dev_net(ovpn->dev), &fl, NULL);
	if (IS_ERR(rt)) {
		net_dbg_ratelimited("%s: no route to host %pI4\n", __func__, &dst);
		/* if we end up here this packet is probably going to be thrown away later */
		return dst;
	}

	if (!rt->rt_uses_gateway)
		goto out;

	dst = rt->rt_gw4;
out:
	ip_rt_put(rt);
	return dst;
}

/**
 * ovpn_nexthop6() - looks up the IPv6 of the nexthop for the given destination
 *
 * Looks up in the IPv6 system routing table the IO of the nexthop to be used
 * to reach the destination passed as argument. IF no nexthop can be found, the
 * destination itself is returned as it probably has to be used as nexthop.
 *
 * @ovpn: the private data representing the current VPN session
 * @dst: the destination to be looked up
 *
 * Return the IP of the next hop if found or the dst itself otherwise
 */
static struct in6_addr ovpn_nexthop6(struct ovpn_struct *ovpn, struct in6_addr dst)
{
#if IS_ENABLED(CONFIG_IPV6)
	struct rt6_info *rt;
	struct flowi6 fl = {
		.daddr = dst,
	};

	rt = (struct rt6_info *)ipv6_stub->ipv6_dst_lookup_flow(dev_net(ovpn->dev), NULL, &fl,
								NULL);
	if (IS_ERR(rt)) {
		net_dbg_ratelimited("%s: no route to host %pI6\n", __func__, &dst);
		/* if we end up here this packet is probably going to be thrown away later */
		return dst;
	}

	if (!(rt->rt6i_flags & RTF_GATEWAY))
		goto out;

	dst = rt->rt6i_gateway;
out:
	dst_release((struct dst_entry *)rt);
#endif
	return dst;
}

/**
 * ovpn_peer_lookup_vpn_addr() - Lookup peer to send skb to
 *
 * This function takes a tunnel packet and looks up the peer to send it to
 * after encapsulation. The skb is expected to be the in-tunnel packet, without
 * any OpenVPN related header.
 *
 * Assume that the IP header is accessible in the skb data.
 *
 * @ovpn: the private data representing the current VPN session
 * @skb: the skb to extract the destination address from
 *
 * Return the peer if found or NULL otherwise.
 */
struct ovpn_peer *ovpn_peer_lookup_vpn_addr(struct ovpn_struct *ovpn, struct sk_buff *skb,
					    bool use_src)
{
	struct ovpn_peer *tmp, *peer = NULL;
	struct hlist_head *head;
	sa_family_t sa_fam;
	struct in6_addr addr6;
	__be32 addr4;
	u32 index;

	/* in P2P mode, no matter the destination, packets are always sent to the single peer
	 * listening on the other side
	 */
	if (ovpn->mode == OVPN_MODE_P2P) {
		rcu_read_lock();
		tmp = rcu_dereference(ovpn->peer);
		if (likely(tmp && ovpn_peer_hold(tmp)))
			peer = tmp;
		rcu_read_unlock();
		return peer;
	}

	sa_fam = skb_protocol_to_family(skb);

	switch (sa_fam) {
	case AF_INET:
		if (use_src)
			addr4 = ip_hdr(skb)->saddr;
		else
			addr4 = ip_hdr(skb)->daddr;
		addr4 = ovpn_nexthop4(ovpn, addr4);

		index = ovpn_peer_index(ovpn->peers.by_vpn_addr, &addr4, sizeof(addr4));
		head = &ovpn->peers.by_vpn_addr[index];

		peer = ovpn_peer_lookup_vpn_addr4(head, &addr4);
		break;
	case AF_INET6:
		if (use_src)
			addr6 = ipv6_hdr(skb)->saddr;
		else
			addr6 = ipv6_hdr(skb)->daddr;
		addr6 = ovpn_nexthop6(ovpn, addr6);

		index = ovpn_peer_index(ovpn->peers.by_vpn_addr, &addr6, sizeof(addr6));
		head = &ovpn->peers.by_vpn_addr[index];

		peer = ovpn_peer_lookup_vpn_addr6(head, &addr6);
		break;
	}

	return peer;
}

static bool ovpn_peer_transp_match(struct ovpn_peer *peer, struct sockaddr_storage *ss)
{
	struct ovpn_bind *bind = rcu_dereference(peer->bind);
	struct sockaddr_in6 *sa6;
	struct sockaddr_in *sa4;

	if (unlikely(!bind))
		return false;

	if (ss->ss_family != bind->sa.in4.sin_family)
		return false;

	switch (ss->ss_family) {
	case AF_INET:
		sa4 = (struct sockaddr_in *)ss;
		if (sa4->sin_addr.s_addr != bind->sa.in4.sin_addr.s_addr)
			return false;
		if (sa4->sin_port != bind->sa.in4.sin_port)
			return false;
		break;
	case AF_INET6:
		sa6 = (struct sockaddr_in6 *)ss;
		if (memcmp(&sa6->sin6_addr, &bind->sa.in6.sin6_addr, sizeof(struct in6_addr)))
			return false;
		if (sa6->sin6_port != bind->sa.in6.sin6_port)
			return false;
		break;
	default:
		return false;
	}

	return true;
}

static bool ovpn_peer_skb_to_sockaddr(struct sk_buff *skb, struct sockaddr_storage *ss)
{
	struct sockaddr_in6 *sa6;
	struct sockaddr_in *sa4;

	ss->ss_family = skb_protocol_to_family(skb);
	switch (ss->ss_family) {
	case AF_INET:
		sa4 = (struct sockaddr_in *)ss;
		sa4->sin_family = AF_INET;
		sa4->sin_addr.s_addr = ip_hdr(skb)->saddr;
		sa4->sin_port = udp_hdr(skb)->source;
		break;
	case AF_INET6:
		sa6 = (struct sockaddr_in6 *)ss;
		sa6->sin6_family = AF_INET6;
		sa6->sin6_addr = ipv6_hdr(skb)->saddr;
		sa6->sin6_port = udp_hdr(skb)->source;
		break;
	default:
		return false;
	}

	return true;
}

static struct ovpn_peer *ovpn_peer_lookup_transp_addr_p2p(struct ovpn_struct *ovpn,
							  struct sockaddr_storage *ss)
{
	struct ovpn_peer *tmp, *peer = NULL;

	rcu_read_lock();
	tmp = rcu_dereference(ovpn->peer);
	if (likely(tmp && ovpn_peer_transp_match(tmp, ss) && ovpn_peer_hold(tmp)))
		peer = tmp;
	rcu_read_unlock();

	return peer;
}

struct ovpn_peer *ovpn_peer_lookup_transp_addr(struct ovpn_struct *ovpn, struct sk_buff *skb)
{
	struct ovpn_peer *peer = NULL, *tmp;
	struct sockaddr_storage ss = { 0 };
	struct hlist_head *head;
	size_t sa_len;
	bool found;
	u32 index;

	if (unlikely(!ovpn_peer_skb_to_sockaddr(skb, &ss)))
		return NULL;

	if (ovpn->mode == OVPN_MODE_P2P)
		return ovpn_peer_lookup_transp_addr_p2p(ovpn, &ss);

	switch (ss.ss_family) {
	case AF_INET:
		sa_len = sizeof(struct sockaddr_in);
		break;
	case AF_INET6:
		sa_len = sizeof(struct sockaddr_in6);
		break;
	default:
		return NULL;
	}

	index = ovpn_peer_index(ovpn->peers.by_transp_addr, &ss, sa_len);
	head = &ovpn->peers.by_transp_addr[index];

	rcu_read_lock();
	hlist_for_each_entry_rcu(tmp, head, hash_entry_transp_addr) {
		found = ovpn_peer_transp_match(tmp, &ss);
		if (!found)
			continue;

		if (!ovpn_peer_hold(tmp))
			continue;

		peer = tmp;
		break;
	}
	rcu_read_unlock();

	return peer;
}

static struct ovpn_peer *ovpn_peer_lookup_id_p2p(struct ovpn_struct *ovpn, u32 peer_id)
{
	struct ovpn_peer *tmp, *peer = NULL;

	rcu_read_lock();
	tmp = rcu_dereference(ovpn->peer);
	if (likely(tmp && tmp->id == peer_id && ovpn_peer_hold(tmp)))
		peer = tmp;
	rcu_read_unlock();

	return peer;
}

struct ovpn_peer *ovpn_peer_lookup_id(struct ovpn_struct *ovpn, u32 peer_id)
{
	struct ovpn_peer *tmp,  *peer = NULL;
	struct hlist_head *head;
	u32 index;

	if (ovpn->mode == OVPN_MODE_P2P)
		return ovpn_peer_lookup_id_p2p(ovpn, peer_id);

	index = ovpn_peer_index(ovpn->peers.by_id, &peer_id, sizeof(peer_id));
	head = &ovpn->peers.by_id[index];

	rcu_read_lock();
	hlist_for_each_entry_rcu(tmp, head, hash_entry_id) {
		if (tmp->id != peer_id)
			continue;

		if (!ovpn_peer_hold(tmp))
			continue;

		peer = tmp;
		break;
	}
	rcu_read_unlock();

	return peer;
}

void ovpn_peer_update_local_endpoint(struct ovpn_peer *peer, struct sk_buff *skb)
{
	struct ovpn_bind *bind;

	rcu_read_lock();
	bind = rcu_dereference(peer->bind);
	if (unlikely(!bind))
		goto unlock;

	switch (skb_protocol_to_family(skb)) {
	case AF_INET:
		if (unlikely(bind->local.ipv4.s_addr != ip_hdr(skb)->daddr)) {
			netdev_dbg(peer->ovpn->dev,
				   "%s: learning local IPv4 for peer %d (%pI4 -> %pI4)\n", __func__,
				   peer->id, &bind->local.ipv4.s_addr, &ip_hdr(skb)->daddr);
			bind->local.ipv4.s_addr = ip_hdr(skb)->daddr;
		}
		break;
	case AF_INET6:
		if (unlikely(memcmp(&bind->local.ipv6, &ipv6_hdr(skb)->daddr,
				    sizeof(bind->local.ipv6)))) {
			netdev_dbg(peer->ovpn->dev,
				   "%s: learning local IPv6 for peer %d (%pI6c -> %pI6c\n",
				   __func__, peer->id, &bind->local.ipv6, &ipv6_hdr(skb)->daddr);
			bind->local.ipv6 = ipv6_hdr(skb)->daddr;
		}
		break;
	default:
		break;
	}
unlock:
	rcu_read_unlock();
}

static int ovpn_peer_add_mp(struct ovpn_struct *ovpn, struct ovpn_peer *peer)
{
	struct sockaddr_storage sa = { 0 };
	struct sockaddr_in6 *sa6;
	struct sockaddr_in *sa4;
	struct ovpn_bind *bind;
	struct ovpn_peer *tmp;
	size_t salen;
	int ret = 0;
	u32 index;

	spin_lock_bh(&ovpn->peers.lock);
	/* do not add duplicates */
	tmp = ovpn_peer_lookup_id(ovpn, peer->id);
	if (tmp) {
		ovpn_peer_put(tmp);
		ret = -EEXIST;
		goto unlock;
	}

	hlist_del_init_rcu(&peer->hash_entry_transp_addr);
	bind = rcu_dereference_protected(peer->bind, true);
	/* peers connected via UDP have bind == NULL */
	if (bind) {
		switch (bind->sa.in4.sin_family) {
		case AF_INET:
			sa4 = (struct sockaddr_in *)&sa;

			sa4->sin_family = AF_INET;
			sa4->sin_addr.s_addr = bind->sa.in4.sin_addr.s_addr;
			sa4->sin_port = bind->sa.in4.sin_port;
			salen = sizeof(*sa4);
			break;
		case AF_INET6:
			sa6 = (struct sockaddr_in6 *)&sa;

			sa6->sin6_family = AF_INET6;
			sa6->sin6_addr = bind->sa.in6.sin6_addr;
			sa6->sin6_port = bind->sa.in6.sin6_port;
			salen = sizeof(*sa6);
			break;
		default:
			ret = -EPROTONOSUPPORT;
			goto unlock;
		}

		index = ovpn_peer_index(ovpn->peers.by_transp_addr, &sa, salen);
		hlist_add_head_rcu(&peer->hash_entry_transp_addr,
				   &ovpn->peers.by_transp_addr[index]);
	}

	index = ovpn_peer_index(ovpn->peers.by_id, &peer->id, sizeof(peer->id));
	hlist_add_head_rcu(&peer->hash_entry_id, &ovpn->peers.by_id[index]);

	if (peer->vpn_addrs.ipv4.s_addr != htonl(INADDR_ANY)) {
		index = ovpn_peer_index(ovpn->peers.by_vpn_addr, &peer->vpn_addrs.ipv4,
					sizeof(peer->vpn_addrs.ipv4));
		hlist_add_head_rcu(&peer->hash_entry_addr4, &ovpn->peers.by_vpn_addr[index]);
	}

	hlist_del_init_rcu(&peer->hash_entry_addr6);
	if (memcmp(&peer->vpn_addrs.ipv6, &in6addr_any, sizeof(peer->vpn_addrs.ipv6))) {
		index = ovpn_peer_index(ovpn->peers.by_vpn_addr, &peer->vpn_addrs.ipv6,
					sizeof(peer->vpn_addrs.ipv6));
		hlist_add_head_rcu(&peer->hash_entry_addr6, &ovpn->peers.by_vpn_addr[index]);
	}

unlock:
	spin_unlock_bh(&ovpn->peers.lock);

	return ret;
}

static int ovpn_peer_add_p2p(struct ovpn_struct *ovpn, struct ovpn_peer *peer)
{
	struct ovpn_peer *tmp;

	spin_lock_bh(&ovpn->lock);
	/* in p2p mode it is possible to have a single peer only, therefore the
	 * old one is released and substituted by the new one
	 */
	tmp = rcu_dereference(ovpn->peer);
	if (tmp) {
		tmp->delete_reason = OVPN_DEL_PEER_REASON_TEARDOWN;
		ovpn_peer_put(tmp);
	}

	rcu_assign_pointer(ovpn->peer, peer);
	spin_unlock_bh(&ovpn->lock);

	return 0;
}

/* assume refcounter was increased by caller */
int ovpn_peer_add(struct ovpn_struct *ovpn, struct ovpn_peer *peer)
{
	switch (ovpn->mode) {
	case OVPN_MODE_MP:
		return ovpn_peer_add_mp(ovpn, peer);
	case OVPN_MODE_P2P:
		return ovpn_peer_add_p2p(ovpn, peer);
	default:
		return -EOPNOTSUPP;
	}
}

static void ovpn_peer_unhash(struct ovpn_peer *peer, enum ovpn_del_peer_reason reason)
{
	hlist_del_init_rcu(&peer->hash_entry_id);
	hlist_del_init_rcu(&peer->hash_entry_addr4);
	hlist_del_init_rcu(&peer->hash_entry_addr6);
	hlist_del_init_rcu(&peer->hash_entry_transp_addr);

	ovpn_peer_put(peer);
	peer->delete_reason = reason;
}

static int ovpn_peer_del_mp(struct ovpn_peer *peer, enum ovpn_del_peer_reason reason)
{
	struct ovpn_peer *tmp;
	int ret = 0;

	spin_lock_bh(&peer->ovpn->peers.lock);
	tmp = ovpn_peer_lookup_id(peer->ovpn, peer->id);
	if (tmp != peer) {
		ret = -ENOENT;
		goto unlock;
	}
	ovpn_peer_unhash(peer, reason);

unlock:
	spin_unlock_bh(&peer->ovpn->peers.lock);

	if (tmp)
		ovpn_peer_put(tmp);

	return ret;
}

static int ovpn_peer_del_p2p(struct ovpn_peer *peer, enum ovpn_del_peer_reason reason)
{
	struct ovpn_peer *tmp;
	int ret = -ENOENT;

	spin_lock_bh(&peer->ovpn->lock);
	tmp = rcu_dereference(peer->ovpn->peer);
	if (tmp != peer)
		goto unlock;

	ovpn_peer_put(tmp);
	tmp->delete_reason = reason;
	RCU_INIT_POINTER(peer->ovpn->peer, NULL);
	ret = 0;

unlock:
	spin_unlock_bh(&peer->ovpn->lock);

	return ret;
}

void ovpn_peer_release_p2p(struct ovpn_struct *ovpn)
{
	struct ovpn_peer *tmp;

	rcu_read_lock();
	tmp = rcu_dereference(ovpn->peer);
	if (!tmp)
		goto unlock;

	ovpn_peer_del_p2p(tmp, OVPN_DEL_PEER_REASON_TEARDOWN);
unlock:
	rcu_read_unlock();
}

int ovpn_peer_del(struct ovpn_peer *peer, enum ovpn_del_peer_reason reason)
{
	switch (peer->ovpn->mode) {
	case OVPN_MODE_MP:
		return ovpn_peer_del_mp(peer, reason);
	case OVPN_MODE_P2P:
		return ovpn_peer_del_p2p(peer, reason);
	default:
		return -EOPNOTSUPP;
	}
}

void ovpn_peers_free(struct ovpn_struct *ovpn)
{
	struct hlist_node *tmp;
	struct ovpn_peer *peer;
	int bkt;

	spin_lock_bh(&ovpn->peers.lock);
	hash_for_each_safe(ovpn->peers.by_id, bkt, tmp, peer, hash_entry_id)
		ovpn_peer_unhash(peer, OVPN_DEL_PEER_REASON_TEARDOWN);
	spin_unlock_bh(&ovpn->peers.lock);
}
0707010000001A000081A400000000000000000000000165BCF4370000125D000000000000000000000000000000000000003F00000000ovpn-dco-0.2.20240320~git0.2aa7f93/drivers/net/ovpn-dco/peer.h/* SPDX-License-Identifier: GPL-2.0-only */
/* OpenVPN data channel accelerator
 *
 *  Copyright (C) 2020-2023 OpenVPN, Inc.
 *
 *  Author:	James Yonan <james@openvpn.net>
 *		Antonio Quartulli <antonio@openvpn.net>
 */

#ifndef _NET_OVPN_DCO_OVPNPEER_H_
#define _NET_OVPN_DCO_OVPNPEER_H_

#include "addr.h"
#include "bind.h"
#include "sock.h"
#include "stats.h"

#include <linux/timer.h>
#include <linux/ptr_ring.h>
#include <net/dst_cache.h>

struct ovpn_peer {
	struct ovpn_struct *ovpn;

	u32 id;

	struct {
		struct in_addr ipv4;
		struct in6_addr ipv6;
	} vpn_addrs;

	struct hlist_node hash_entry_id;
	struct hlist_node hash_entry_addr4;
	struct hlist_node hash_entry_addr6;
	struct hlist_node hash_entry_transp_addr;

	/* work objects to handle encryption/decryption of packets.
	 * these works are queued on the ovpn->crypt_wq workqueue.
	 */
	struct work_struct encrypt_work;
	struct work_struct decrypt_work;

	struct ptr_ring tx_ring;
	struct ptr_ring rx_ring;
	struct ptr_ring netif_rx_ring;

	struct napi_struct napi;

	struct ovpn_socket *sock;

	/* state of the TCP reading. Needed to keep track of how much of a single packet has already
	 * been read from the stream and how much is missing
	 */
	struct {
		struct ptr_ring tx_ring;
		struct work_struct tx_work;
		struct work_struct rx_work;

		u8 raw_len[sizeof(u16)];
		struct sk_buff *skb;
		u16 offset;
		u16 data_len;
		struct {
			void (*sk_state_change)(struct sock *sk);
			void (*sk_data_ready)(struct sock *sk);
			void (*sk_write_space)(struct sock *sk);
			struct proto *prot;
		} sk_cb;
	} tcp;

	struct dst_cache dst_cache;

	/* our crypto state */
	struct ovpn_crypto_state crypto;

	/* our binding to peer, protected by spinlock */
	struct ovpn_bind __rcu *bind;

	/* timer used to send periodic ping messages to the other peer, if no
	 * other data was sent within the past keepalive_interval seconds
	 */
	struct timer_list keepalive_xmit;
	/* keepalive interval in seconds */
	unsigned long keepalive_interval;

	/* timer used to mark a peer as expired when no data is received for
	 * keepalive_timeout seconds
	 */
	struct timer_list keepalive_recv;
	/* keepalive timeout in seconds */
	unsigned long keepalive_timeout;

	/* true if ovpn_peer_mark_delete was called */
	bool halt;

	/* per-peer in-VPN rx/tx stats */
	struct ovpn_peer_stats vpn_stats;

	/* per-peer link/transport rx/tx stats */
	struct ovpn_peer_stats link_stats;

	/* why peer was deleted - keepalive timeout, module removed etc */
	enum ovpn_del_peer_reason delete_reason;

	/* protects binding to peer (bind) and timers
	 * (keepalive_xmit, keepalive_expire)
	 */
	spinlock_t lock;

	/* needed because crypto methods can go async */
	struct kref refcount;

	/* needed to free a peer in an RCU safe way */
	struct rcu_head rcu;

	/* needed to notify userspace about deletion */
	struct work_struct delete_work;
};

void ovpn_peer_release_kref(struct kref *kref);
void ovpn_peer_release(struct ovpn_peer *peer);

static inline bool ovpn_peer_hold(struct ovpn_peer *peer)
{
	return kref_get_unless_zero(&peer->refcount);
}

static inline void ovpn_peer_put(struct ovpn_peer *peer)
{
	kref_put(&peer->refcount, ovpn_peer_release_kref);
}

static inline void ovpn_peer_keepalive_recv_reset(struct ovpn_peer *peer)
{
	u32 delta = msecs_to_jiffies(peer->keepalive_timeout * MSEC_PER_SEC);

	if (unlikely(!delta))
		return;

	mod_timer(&peer->keepalive_recv, jiffies + delta);
}

static inline void ovpn_peer_keepalive_xmit_reset(struct ovpn_peer *peer)
{
	u32 delta = msecs_to_jiffies(peer->keepalive_interval * MSEC_PER_SEC);

	if (unlikely(!delta))
		return;

	mod_timer(&peer->keepalive_xmit, jiffies + delta);
}

struct ovpn_peer *ovpn_peer_new(struct ovpn_struct *ovpn, const struct sockaddr_storage *sa,
				struct socket *sock, u32 id, uint8_t *local_ip);

void ovpn_peer_keepalive_set(struct ovpn_peer *peer, u32 interval, u32 timeout);

int ovpn_peer_add(struct ovpn_struct *ovpn, struct ovpn_peer *peer);
int ovpn_peer_del(struct ovpn_peer *peer, enum ovpn_del_peer_reason reason);
struct ovpn_peer *ovpn_peer_find(struct ovpn_struct *ovpn, u32 peer_id);
void ovpn_peer_release_p2p(struct ovpn_struct *ovpn);
void ovpn_peers_free(struct ovpn_struct *ovpn);

struct ovpn_peer *ovpn_peer_lookup_transp_addr(struct ovpn_struct *ovpn, struct sk_buff *skb);
struct ovpn_peer *ovpn_peer_lookup_vpn_addr(struct ovpn_struct *ovpn, struct sk_buff *skb,
					    bool use_src);
struct ovpn_peer *ovpn_peer_lookup_id(struct ovpn_struct *ovpn, u32 peer_id);

void ovpn_peer_update_local_endpoint(struct ovpn_peer *peer, struct sk_buff *skb);
void ovpn_peer_float(struct ovpn_peer *peer, struct sk_buff *skb);

#endif /* _NET_OVPN_DCO_OVPNPEER_H_ */
0707010000001B000081A400000000000000000000000165BCF43700000B1F000000000000000000000000000000000000004000000000ovpn-dco-0.2.20240320~git0.2aa7f93/drivers/net/ovpn-dco/pktid.c// SPDX-License-Identifier: GPL-2.0
/*  OpenVPN data channel accelerator
 *
 *  Copyright (C) 2020-2023 OpenVPN, Inc.
 *
 *  Author:	Antonio Quartulli <antonio@openvpn.net>
 *		James Yonan <james@openvpn.net>
 */

#include "pktid.h"

#include <linux/atomic.h>
#include <linux/jiffies.h>

void ovpn_pktid_xmit_init(struct ovpn_pktid_xmit *pid)
{
	atomic64_set(&pid->seq_num, 1);
	pid->tcp_linear = NULL;
}

void ovpn_pktid_recv_init(struct ovpn_pktid_recv *pr)
{
	memset(pr, 0, sizeof(*pr));
	spin_lock_init(&pr->lock);
}

/* Packet replay detection.
 * Allows ID backtrack of up to REPLAY_WINDOW_SIZE - 1.
 */
int ovpn_pktid_recv(struct ovpn_pktid_recv *pr, u32 pkt_id, u32 pkt_time)
{
	const unsigned long now = jiffies;
	int ret;

	spin_lock(&pr->lock);

	/* expire backtracks at or below pr->id after PKTID_RECV_EXPIRE time */
	if (unlikely(time_after_eq(now, pr->expire)))
		pr->id_floor = pr->id;

	/* ID must not be zero */
	if (unlikely(pkt_id == 0)) {
		ret = -EINVAL;
		goto out;
	}

	/* time changed? */
	if (unlikely(pkt_time != pr->time)) {
		if (pkt_time > pr->time) {
			/* time moved forward, accept */
			pr->base = 0;
			pr->extent = 0;
			pr->id = 0;
			pr->time = pkt_time;
			pr->id_floor = 0;
		} else {
			/* time moved backward, reject */
			ret = -ETIME;
			goto out;
		}
	}

	if (likely(pkt_id == pr->id + 1)) {
		/* well-formed ID sequence (incremented by 1) */
		pr->base = REPLAY_INDEX(pr->base, -1);
		pr->history[pr->base / 8] |= (1 << (pr->base % 8));
		if (pr->extent < REPLAY_WINDOW_SIZE)
			++pr->extent;
		pr->id = pkt_id;
	} else if (pkt_id > pr->id) {
		/* ID jumped forward by more than one */
		const unsigned int delta = pkt_id - pr->id;

		if (delta < REPLAY_WINDOW_SIZE) {
			unsigned int i;

			pr->base = REPLAY_INDEX(pr->base, -delta);
			pr->history[pr->base / 8] |= (1 << (pr->base % 8));
			pr->extent += delta;
			if (pr->extent > REPLAY_WINDOW_SIZE)
				pr->extent = REPLAY_WINDOW_SIZE;
			for (i = 1; i < delta; ++i) {
				unsigned int newb = REPLAY_INDEX(pr->base, i);

				pr->history[newb / 8] &= ~BIT(newb % 8);
			}
		} else {
			pr->base = 0;
			pr->extent = REPLAY_WINDOW_SIZE;
			memset(pr->history, 0, sizeof(pr->history));
			pr->history[0] = 1;
		}
		pr->id = pkt_id;
	} else {
		/* ID backtrack */
		const unsigned int delta = pr->id - pkt_id;

		if (delta > pr->max_backtrack)
			pr->max_backtrack = delta;
		if (delta < pr->extent) {
			if (pkt_id > pr->id_floor) {
				const unsigned int ri = REPLAY_INDEX(pr->base,
								     delta);
				u8 *p = &pr->history[ri / 8];
				const u8 mask = (1 << (ri % 8));

				if (*p & mask) {
					ret = -EINVAL;
					goto out;
				}
				*p |= mask;
			} else {
				ret = -EINVAL;
				goto out;
			}
		} else {
			ret = -EINVAL;
			goto out;
		}
	}

	pr->expire = now + PKTID_RECV_EXPIRE;
	ret = 0;
out:
	spin_unlock(&pr->lock);
	return ret;
}
0707010000001C000081A400000000000000000000000165BCF43700000D59000000000000000000000000000000000000004000000000ovpn-dco-0.2.20240320~git0.2aa7f93/drivers/net/ovpn-dco/pktid.h/* SPDX-License-Identifier: GPL-2.0-only */
/*  OpenVPN data channel accelerator
 *
 *  Copyright (C) 2020-2023 OpenVPN, Inc.
 *
 *  Author:	Antonio Quartulli <antonio@openvpn.net>
 *		James Yonan <james@openvpn.net>
 */

#ifndef _NET_OVPN_DCO_OVPNPKTID_H_
#define _NET_OVPN_DCO_OVPNPKTID_H_

#include "main.h"

/* When the OpenVPN protocol is run in AEAD mode, use
 * the OpenVPN packet ID as the AEAD nonce:
 *
 *    00000005 521c3b01 4308c041
 *    [seq # ] [  nonce_tail   ]
 *    [     12-byte full IV    ] -> NONCE_SIZE
 *    [4-bytes                   -> NONCE_WIRE_SIZE
 *    on wire]
 */

/* OpenVPN nonce size */
#define NONCE_SIZE 12
/* amount of bytes of the nonce received from user space */
#define NONCE_TAIL_SIZE 8

/* OpenVPN nonce size reduced by 8-byte nonce tail -- this is the
 * size of the AEAD Associated Data (AD) sent over the wire
 * and is normally the head of the IV
 */
#define NONCE_WIRE_SIZE (NONCE_SIZE - sizeof(struct ovpn_nonce_tail))

/* If no packets received for this length of time, set a backtrack floor
 * at highest received packet ID thus far.
 */
#define PKTID_RECV_EXPIRE (30 * HZ)

/* Last 8 bytes of AEAD nonce
 * Provided by userspace and usually derived from
 * key material generated during TLS handshake
 */
struct ovpn_nonce_tail {
	u8 u8[NONCE_TAIL_SIZE];
};

/* Packet-ID state for transmitter */
struct ovpn_pktid_xmit {
	atomic64_t seq_num;
	struct ovpn_tcp_linear *tcp_linear;
};

/* replay window sizing in bytes = 2^REPLAY_WINDOW_ORDER */
#define REPLAY_WINDOW_ORDER 8

#define REPLAY_WINDOW_BYTES BIT(REPLAY_WINDOW_ORDER)
#define REPLAY_WINDOW_SIZE  (REPLAY_WINDOW_BYTES * 8)
#define REPLAY_INDEX(base, i) (((base) + (i)) & (REPLAY_WINDOW_SIZE - 1))

/* Packet-ID state for receiver.
 * Other than lock member, can be zeroed to initialize.
 */
struct ovpn_pktid_recv {
	/* "sliding window" bitmask of recent packet IDs received */
	u8 history[REPLAY_WINDOW_BYTES];
	/* bit position of deque base in history */
	unsigned int base;
	/* extent (in bits) of deque in history */
	unsigned int extent;
	/* expiration of history in jiffies */
	unsigned long expire;
	/* highest sequence number received */
	u32 id;
	/* highest time stamp received */
	u32 time;
	/* we will only accept backtrack IDs > id_floor */
	u32 id_floor;
	unsigned int max_backtrack;
	/* protects entire pktd ID state */
	spinlock_t lock;
};

/* Get the next packet ID for xmit */
static inline int ovpn_pktid_xmit_next(struct ovpn_pktid_xmit *pid, u32 *pktid)
{
	const s64 seq_num = atomic64_fetch_add_unless(&pid->seq_num, 1,
						      0x100000000LL);
	/* when the 32bit space is over, we return an error because the packet ID is used to create
	 * the cipher IV and we do not want to re-use the same value more than once
	 */
	if (unlikely(seq_num == 0x100000000LL))
		return -ERANGE;

	*pktid = (u32)seq_num;

	return 0;
}

/* Write 12-byte AEAD IV to dest */
static inline void ovpn_pktid_aead_write(const u32 pktid,
					 const struct ovpn_nonce_tail *nt,
					 unsigned char *dest)
{
	*(__force __be32 *)(dest) = htonl(pktid);
	BUILD_BUG_ON(4 + sizeof(struct ovpn_nonce_tail) != NONCE_SIZE);
	memcpy(dest + 4, nt->u8, sizeof(struct ovpn_nonce_tail));
}

void ovpn_pktid_xmit_init(struct ovpn_pktid_xmit *pid);
void ovpn_pktid_recv_init(struct ovpn_pktid_recv *pr);

int ovpn_pktid_recv(struct ovpn_pktid_recv *pr, u32 pkt_id, u32 pkt_time);

#endif /* _NET_OVPN_DCO_OVPNPKTID_H_ */
0707010000001D000081A400000000000000000000000165BCF437000009EB000000000000000000000000000000000000004000000000ovpn-dco-0.2.20240320~git0.2aa7f93/drivers/net/ovpn-dco/proto.h/* SPDX-License-Identifier: GPL-2.0-only */
/*  OpenVPN data channel accelerator
 *
 *  Copyright (C) 2020-2023 OpenVPN, Inc.
 *
 *  Author:	Antonio Quartulli <antonio@openvpn.net>
 *		James Yonan <james@openvpn.net>
 */

#ifndef _NET_OVPN_DCO_OVPNPROTO_H_
#define _NET_OVPN_DCO_OVPNPROTO_H_

#include "main.h"

#include <linux/skbuff.h>

/* Methods for operating on the initial command
 * byte of the OpenVPN protocol.
 */

/* packet opcode (high 5 bits) and key-id (low 3 bits) are combined in
 * one byte
 */
#define OVPN_KEY_ID_MASK 0x07
#define OVPN_OPCODE_SHIFT 3
#define OVPN_OPCODE_MASK 0x1F
/* upper bounds on opcode and key ID */
#define OVPN_KEY_ID_MAX (OVPN_KEY_ID_MASK + 1)
#define OVPN_OPCODE_MAX (OVPN_OPCODE_MASK + 1)
/* packet opcodes of interest to us */
#define OVPN_DATA_V1 6 /* data channel V1 packet */
#define OVPN_DATA_V2 9 /* data channel V2 packet */
/* size of initial packet opcode */
#define OVPN_OP_SIZE_V1 1
#define OVPN_OP_SIZE_V2	4
#define OVPN_PEER_ID_MASK 0x00FFFFFF
#define OVPN_PEER_ID_UNDEF 0x00FFFFFF
/* first byte of keepalive message */
#define OVPN_KEEPALIVE_FIRST_BYTE 0x2a
/* first byte of exit message */
#define OVPN_EXPLICIT_EXIT_NOTIFY_FIRST_BYTE 0x28

/**
 * Extract the OP code from the specified byte
 *
 * Return the OP code
 */
static inline u8 ovpn_opcode_from_byte(u8 byte)
{
	return byte >> OVPN_OPCODE_SHIFT;
}

/**
 * Extract the OP code from the skb head.
 *
 * Note: this function assumes that the skb head was pulled enough
 * to access the first byte.
 *
 * Return the OP code
 */
static inline u8 ovpn_opcode_from_skb(const struct sk_buff *skb, u16 offset)
{
	return ovpn_opcode_from_byte(*(skb->data + offset));
}

/**
 * Extract the key ID from the skb head.
 *
 * Note: this function assumes that the skb head was pulled enough
 * to access the first byte.
 *
 * Return the key ID
 */

static inline u8 ovpn_key_id_from_skb(const struct sk_buff *skb)
{
	return *skb->data & OVPN_KEY_ID_MASK;
}

/**
 * Extract the peer ID from the skb head.
 *
 * Note: this function assumes that the skb head was pulled enough
 * to access the first 4 bytes.
 *
 * Return the peer ID.
 */

static inline u32 ovpn_peer_id_from_skb(const struct sk_buff *skb, u16 offset)
{
	return ntohl(*(__be32 *)(skb->data + offset)) & OVPN_PEER_ID_MASK;
}

static inline u32 ovpn_opcode_compose(u8 opcode, u8 key_id, u32 peer_id)
{
	const u8 op = (opcode << OVPN_OPCODE_SHIFT) | (key_id & OVPN_KEY_ID_MASK);

	return (op << 24) | (peer_id & OVPN_PEER_ID_MASK);
}

#endif /* _NET_OVPN_DCO_OVPNPROTO_H_ */
0707010000001E000081A400000000000000000000000165BCF437000001F2000000000000000000000000000000000000003E00000000ovpn-dco-0.2.20240320~git0.2aa7f93/drivers/net/ovpn-dco/rcu.h/* SPDX-License-Identifier: GPL-2.0-only */
/*  OpenVPN data channel accelerator
 *
 *  Copyright (C) 2019-2023 OpenVPN, Inc.
 *
 *  Author:	James Yonan <james@openvpn.net>
 *		Antonio Quartulli <antonio@openvpn.net>
 */

#ifndef _NET_OVPN_DCO_OVPNRCU_H_
#define _NET_OVPN_DCO_OVPNRCU_H_

static inline void ovpn_rcu_lockdep_assert_held(void)
{
#ifdef CONFIG_PROVE_RCU
	RCU_LOCKDEP_WARN(!rcu_read_lock_held(),
			 "ovpn-dco RCU read lock not held");
#endif
}

#endif /* _NET_OVPN_DCO_OVPNRCU_H_ */
0707010000001F000081A400000000000000000000000165BCF4370000046A000000000000000000000000000000000000003E00000000ovpn-dco-0.2.20240320~git0.2aa7f93/drivers/net/ovpn-dco/skb.h/* SPDX-License-Identifier: GPL-2.0-only */
/*  OpenVPN data channel accelerator
 *
 *  Copyright (C) 2020-2023 OpenVPN, Inc.
 *
 *  Author:	Antonio Quartulli <antonio@openvpn.net>
 *		James Yonan <james@openvpn.net>
 */

#ifndef _NET_OVPN_DCO_SKB_H_
#define _NET_OVPN_DCO_SKB_H_

#include <linux/in.h>
#include <linux/in6.h>
#include <linux/ip.h>
#include <linux/skbuff.h>
#include <linux/socket.h>
#include <linux/types.h>

#define OVPN_SKB_CB(skb) ((struct ovpn_skb_cb *)&((skb)->cb))

struct ovpn_skb_cb {
	union {
		struct in_addr ipv4;
		struct in6_addr ipv6;
	} local;
	sa_family_t sa_fam;
};

/* Return IP protocol version from skb header.
 * Return 0 if protocol is not IPv4/IPv6 or cannot be read.
 */
static inline __be16 ovpn_ip_check_protocol(struct sk_buff *skb)
{
	__be16 proto = 0;

	/* skb could be non-linear,
	 * make sure IP header is in non-fragmented part
	 */
	if (!pskb_network_may_pull(skb, sizeof(struct iphdr)))
		return 0;

	if (ip_hdr(skb)->version == 4)
		proto = htons(ETH_P_IP);
	else if (ip_hdr(skb)->version == 6)
		proto = htons(ETH_P_IPV6);

	return proto;
}

#endif /* _NET_OVPN_DCO_SKB_H_ */
07070100000020000081A400000000000000000000000165BCF43700000D96000000000000000000000000000000000000003F00000000ovpn-dco-0.2.20240320~git0.2aa7f93/drivers/net/ovpn-dco/sock.c// SPDX-License-Identifier: GPL-2.0
/*  OpenVPN data channel accelerator
 *
 *  Copyright (C) 2020-2023 OpenVPN, Inc.
 *
 *  Author:	James Yonan <james@openvpn.net>
 *		Antonio Quartulli <antonio@openvpn.net>
 */

#include "main.h"
#include "ovpn.h"
#include "peer.h"
#include "sock.h"
#include "rcu.h"
#include "tcp.h"
#include "udp.h"

#include <net/udp.h>
#include <net/udp_tunnel.h>

/* Finalize release of socket, called after RCU grace period */
static void ovpn_socket_detach(struct socket *sock)
{
	if (!sock)
		return;

	if (sock->sk->sk_protocol == IPPROTO_UDP)
		ovpn_udp_socket_detach(sock);
	else if (sock->sk->sk_protocol == IPPROTO_TCP)
		ovpn_tcp_socket_detach(sock);

	sockfd_put(sock);
}

void ovpn_socket_release_kref(struct kref *kref)
{
	struct ovpn_socket *sock = container_of(kref, struct ovpn_socket, refcount);

	ovpn_socket_detach(sock->sock);
	kfree_rcu(sock, rcu);
}

static bool ovpn_socket_hold(struct ovpn_socket *sock)
{
	return kref_get_unless_zero(&sock->refcount);
}

static struct ovpn_socket *ovpn_socket_get(struct socket *sock)
{
	struct ovpn_socket *ovpn_sock;

	rcu_read_lock();
	ovpn_sock = rcu_dereference_sk_user_data(sock->sk);
	if (!ovpn_socket_hold(ovpn_sock)) {
		pr_warn("%s: found ovpn_socket with ref = 0\n", __func__);
		ovpn_sock = NULL;
	}
	rcu_read_unlock();

	return ovpn_sock;
}

/* Finalize release of socket, called after RCU grace period */
static int ovpn_socket_attach(struct socket *sock, struct ovpn_peer *peer)
{
	int ret = -EOPNOTSUPP;

	if (!sock || !peer)
		return -EINVAL;

	if (sock->sk->sk_protocol == IPPROTO_UDP)
		ret = ovpn_udp_socket_attach(sock, peer->ovpn);
	else if (sock->sk->sk_protocol == IPPROTO_TCP)
		ret = ovpn_tcp_socket_attach(sock, peer);

	return ret;
}

struct ovpn_struct *ovpn_from_udp_sock(struct sock *sk)
{
	struct ovpn_socket *ovpn_sock;

	ovpn_rcu_lockdep_assert_held();

	if (unlikely(READ_ONCE(udp_sk(sk)->encap_type) != UDP_ENCAP_OVPNINUDP))
		return NULL;

	ovpn_sock = rcu_dereference_sk_user_data(sk);
	if (unlikely(!ovpn_sock))
		return NULL;

	/* make sure that sk matches our stored transport socket */
	if (unlikely(!ovpn_sock->sock || sk != ovpn_sock->sock->sk))
		return NULL;

	return ovpn_sock->ovpn;
}

struct ovpn_socket *ovpn_socket_new(struct socket *sock, struct ovpn_peer *peer)
{
	struct ovpn_socket *ovpn_sock;
	int ret;

	ret = ovpn_socket_attach(sock, peer);
	if (ret < 0 && ret != -EALREADY)
		return ERR_PTR(ret);

	/* if this socket is already owned by this interface, just increase the refcounter */
	if (ret == -EALREADY) {
		/* caller is expected to increase the sock refcounter before passing it to this
		 * function. For this reason we drop it if not needed, like when this socket is
		 * already owned.
		 */
		ovpn_sock = ovpn_socket_get(sock);
		sockfd_put(sock);
		return ovpn_sock;
	}

	ovpn_sock = kzalloc(sizeof(*ovpn_sock), GFP_KERNEL);
	if (!ovpn_sock)
		return ERR_PTR(-ENOMEM);

	ovpn_sock->ovpn = peer->ovpn;
	ovpn_sock->sock = sock;
	kref_init(&ovpn_sock->refcount);

	/* TCP sockets are per-peer, therefore they are linked to their unique peer */
	if (sock->sk->sk_protocol == IPPROTO_TCP) {
		ovpn_sock->peer = peer;
		ret = ptr_ring_init(&ovpn_sock->recv_ring, OVPN_QUEUE_LEN, GFP_KERNEL);
		if (ret < 0) {
			netdev_err(peer->ovpn->dev, "%s: cannot allocate TCP recv ring\n",
				   __func__);
			goto err;
		}
	}

	rcu_assign_sk_user_data(sock->sk, ovpn_sock);

	return ovpn_sock;
err:
	kfree(ovpn_sock);
	return ERR_PTR(ret);
}
07070100000021000081A400000000000000000000000165BCF43700000569000000000000000000000000000000000000003F00000000ovpn-dco-0.2.20240320~git0.2aa7f93/drivers/net/ovpn-dco/sock.h/* SPDX-License-Identifier: GPL-2.0-only */
/*  OpenVPN data channel accelerator
 *
 *  Copyright (C) 2020-2023 OpenVPN, Inc.
 *
 *  Author:	James Yonan <james@openvpn.net>
 *		Antonio Quartulli <antonio@openvpn.net>
 */

#ifndef _NET_OVPN_DCO_SOCK_H_
#define _NET_OVPN_DCO_SOCK_H_

#include <linux/net.h>
#include <linux/kref.h>
#include <linux/ptr_ring.h>
#include <net/sock.h>

#include "peer.h"

struct ovpn_struct;

/**
 * struct ovpn_socket - a kernel socket referenced in the ovpn-dco code
 */
struct ovpn_socket {
	union {
		/** @ovpn: the VPN session object owning this socket (UDP only) */
		struct ovpn_struct *ovpn;

		/* TCP only */
		struct {
			/** @peer: the unique peer transmitting over this socket (TCP only) */
			struct ovpn_peer *peer;
			struct ptr_ring recv_ring;
		};
	};

	/** @sock: the kernel socket */
	struct socket *sock;

	/** @refcount: amount of contexts currently referencing this object */
	struct kref refcount;

	/** @rcu: member used to schedule RCU destructor callback */
	struct rcu_head rcu;
};

struct ovpn_struct *ovpn_from_udp_sock(struct sock *sk);

void ovpn_socket_release_kref(struct kref *kref);

static inline void ovpn_socket_put(struct ovpn_socket *sock)
{
	kref_put(&sock->refcount, ovpn_socket_release_kref);
}

struct ovpn_socket *ovpn_socket_new(struct socket *sock, struct ovpn_peer *peer);

#endif /* _NET_OVPN_DCO_SOCK_H_ */
07070100000022000081A400000000000000000000000165BCF437000001BB000000000000000000000000000000000000004000000000ovpn-dco-0.2.20240320~git0.2aa7f93/drivers/net/ovpn-dco/stats.c// SPDX-License-Identifier: GPL-2.0
/*  OpenVPN data channel accelerator
 *
 *  Copyright (C) 2020-2023 OpenVPN, Inc.
 *
 *  Author:	James Yonan <james@openvpn.net>
 *		Antonio Quartulli <antonio@openvpn.net>
 */

#include "main.h"
#include "stats.h"

void ovpn_peer_stats_init(struct ovpn_peer_stats *ps)
{
	atomic64_set(&ps->rx.bytes, 0);
	atomic_set(&ps->rx.packets, 0);

	atomic64_set(&ps->tx.bytes, 0);
	atomic_set(&ps->tx.packets, 0);
}
07070100000023000081A400000000000000000000000165BCF43700000601000000000000000000000000000000000000004000000000ovpn-dco-0.2.20240320~git0.2aa7f93/drivers/net/ovpn-dco/stats.h/* SPDX-License-Identifier: GPL-2.0-only */
/*  OpenVPN data channel accelerator
 *
 *  Copyright (C) 2020-2023 OpenVPN, Inc.
 *
 *  Author:	James Yonan <james@openvpn.net>
 *		Antonio Quartulli <antonio@openvpn.net>
 *		Lev Stipakov <lev@openvpn.net>
 */

#ifndef _NET_OVPN_DCO_OVPNSTATS_H_
#define _NET_OVPN_DCO_OVPNSTATS_H_

#include <linux/atomic.h>
#include <linux/jiffies.h>

struct ovpn_struct;

/* per-peer stats, measured on transport layer */

/* one stat */
struct ovpn_peer_stat {
	atomic64_t bytes;
	atomic_t packets;
};

/* rx and tx stats, enabled by notify_per != 0 or period != 0 */
struct ovpn_peer_stats {
	struct ovpn_peer_stat rx;
	struct ovpn_peer_stat tx;
};

/* struct for OVPN_ERR_STATS */

struct ovpn_err_stat {
	unsigned int category;
	int errcode;
	u64 count;
};

struct ovpn_err_stats {
	/* total stats, returned by kovpn */
	unsigned int total_stats;
	/* number of stats dimensioned below */
	unsigned int n_stats;
	struct ovpn_err_stat stats[];
};

void ovpn_peer_stats_init(struct ovpn_peer_stats *ps);

static inline void ovpn_peer_stats_increment(struct ovpn_peer_stat *stat, const unsigned int n)
{
	atomic64_add(n, &stat->bytes);
	atomic_inc(&stat->packets);
}

static inline void ovpn_peer_stats_increment_rx(struct ovpn_peer_stats *stats, const unsigned int n)
{
	ovpn_peer_stats_increment(&stats->rx, n);
}

static inline void ovpn_peer_stats_increment_tx(struct ovpn_peer_stats *stats, const unsigned int n)
{
	ovpn_peer_stats_increment(&stats->tx, n);
}

#endif /* _NET_OVPN_DCO_OVPNSTATS_H_ */
07070100000024000081A400000000000000000000000165BCF4370000318A000000000000000000000000000000000000003E00000000ovpn-dco-0.2.20240320~git0.2aa7f93/drivers/net/ovpn-dco/tcp.c// SPDX-License-Identifier: GPL-2.0
/*  OpenVPN data channel accelerator
 *
 *  Copyright (C) 2019-2023 OpenVPN, Inc.
 *
 *  Author:	Antonio Quartulli <antonio@openvpn.net>
 */

#include "main.h"
#include "ovpnstruct.h"
#include "ovpn.h"
#include "peer.h"
#include "proto.h"
#include "skb.h"
#include "tcp.h"

#include <linux/ptr_ring.h>
#include <linux/skbuff.h>
#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)
{
	struct sock *sk = desc->arg.data;
	struct ovpn_socket *sock;
	struct ovpn_skb_cb *cb;
	struct ovpn_peer *peer;
	size_t chunk, copied = 0;
	int status;
	void *data;
	u16 len;

	rcu_read_lock();
	sock = rcu_dereference_sk_user_data(sk);
	rcu_read_unlock();

	if (unlikely(!sock || !sock->peer)) {
		pr_err("ovpn: read_sock triggered for socket with no metadata\n");
		desc->error = -EINVAL;
		return 0;
	}

	peer = sock->peer;

	while (in_len > 0) {
		/* no skb allocated means that we have to read (or finish reading) the 2 bytes
		 * prefix containing the actual packet size.
		 */
		if (!peer->tcp.skb) {
			chunk = min_t(size_t, in_len, sizeof(u16) - peer->tcp.offset);
			WARN_ON(skb_copy_bits(in_skb, in_offset,
					      peer->tcp.raw_len + peer->tcp.offset, chunk) < 0);
			peer->tcp.offset += chunk;

			/* keep on reading until we got the whole packet size */
			if (peer->tcp.offset != sizeof(u16))
				goto next_read;

			len = ntohs(*(__be16 *)peer->tcp.raw_len);
			/* invalid packet length: this is a fatal TCP error */
			if (!len) {
				netdev_err(peer->ovpn->dev, "%s: received invalid packet length: %d\n",
					   __func__, len);
				desc->error = -EINVAL;
				goto err;
			}

			/* add 2 bytes to allocated space (and immediately reserve them) for packet
			 * length prepending, in case the skb has to be forwarded to userspace
			 */
			peer->tcp.skb = netdev_alloc_skb_ip_align(peer->ovpn->dev,
								  len + sizeof(u16));
			if (!peer->tcp.skb) {
				desc->error = -ENOMEM;
				goto err;
			}
			skb_reserve(peer->tcp.skb, sizeof(u16));

			peer->tcp.offset = 0;
			peer->tcp.data_len = len;
		} else {
			chunk = min_t(size_t, in_len, peer->tcp.data_len - peer->tcp.offset);

			/* extend skb to accommodate the new chunk and copy it from the input skb */
			data = skb_put(peer->tcp.skb, chunk);
			WARN_ON(skb_copy_bits(in_skb, in_offset, data, chunk) < 0);
			peer->tcp.offset += chunk;

			/* keep on reading until we get the full packet */
			if (peer->tcp.offset != peer->tcp.data_len)
				goto next_read;

			/* do not perform IP caching for TCP connections */
			cb = OVPN_SKB_CB(peer->tcp.skb);
			cb->sa_fam = AF_UNSPEC;

			/* At this point we know the packet is from a configured peer.
			 * DATA_V2 packets are handled in kernel space, the rest goes to user space.
			 *
			 * Queue skb for sending to userspace via recvmsg on the socket
			 */
			if (likely(ovpn_opcode_from_skb(peer->tcp.skb, 0) == OVPN_DATA_V2)) {
				/* hold reference to peer as required by ovpn_recv().
				 *
				 * NOTE: in this context we should already be holding a
				 * reference to this peer, therefore ovpn_peer_hold() is
				 * not expected to fail
				 */
				WARN_ON(!ovpn_peer_hold(peer));
				status = ovpn_recv(peer->ovpn, peer, peer->tcp.skb);
				if (unlikely(status < 0))
					ovpn_peer_put(peer);

			} else {
				/* prepend skb with packet len. this way userspace can parse
				 * the packet as if it just arrived from the remote endpoint
				 */
				void *raw_len = __skb_push(peer->tcp.skb, sizeof(u16));
				memcpy(raw_len, peer->tcp.raw_len, sizeof(u16));

				status = ptr_ring_produce_bh(&peer->sock->recv_ring, peer->tcp.skb);
				if (likely(!status))
					peer->tcp.sk_cb.sk_data_ready(sk);
			}

			/* skb not consumed - free it now */
			if (unlikely(status < 0))
				kfree_skb(peer->tcp.skb);

			peer->tcp.skb = NULL;
			peer->tcp.offset = 0;
			peer->tcp.data_len = 0;
		}
next_read:
		in_len -= chunk;
		in_offset += chunk;
		copied += chunk;
	}

	return copied;
err:
	netdev_err(peer->ovpn->dev, "cannot process incoming TCP data: %d\n", desc->error);
	ovpn_peer_del(peer, OVPN_DEL_PEER_REASON_TRANSPORT_ERROR);
	return 0;
}

static void ovpn_tcp_data_ready(struct sock *sk)
{
	struct socket *sock = sk->sk_socket;
	read_descriptor_t desc;

	if (unlikely(!sock || !sock->ops || !sock->ops->read_sock))
		return;

	desc.arg.data = sk;
	desc.error = 0;
	desc.count = 1;

	sock->ops->read_sock(sk, &desc, ovpn_tcp_read_sock);
}

static void ovpn_tcp_write_space(struct sock *sk)
{
	struct ovpn_socket *sock;

	rcu_read_lock();
	sock = rcu_dereference_sk_user_data(sk);
	rcu_read_unlock();

	if (!sock || !sock->peer)
		return;

	queue_work(sock->peer->ovpn->events_wq, &sock->peer->tcp.tx_work);
}

static bool ovpn_tcp_sock_is_readable(
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 0) && RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(9, 0)
				      const struct sock *sk
#else
				      struct sock *sk
#endif
				      )

{
	struct ovpn_socket *sock;

	rcu_read_lock();
	sock = rcu_dereference_sk_user_data(sk);
	rcu_read_unlock();

	if (!sock || !sock->peer)
		return false;

	return !ptr_ring_empty_bh(&sock->recv_ring);
}

static int ovpn_tcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 19, 0)
			    int noblock,
#endif
			    int flags, int *addr_len)
{
	bool tmp = flags & MSG_DONTWAIT;
	DEFINE_WAIT_FUNC(wait, woken_wake_function);
	int ret, chunk, copied = 0;
	struct ovpn_socket *sock;
	struct sk_buff *skb;
	long timeo;

#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 19, 0)
	tmp = noblock;
#endif

	if (unlikely(flags & MSG_ERRQUEUE))
		return sock_recv_errqueue(sk, msg, len, SOL_IP, IP_RECVERR);

	timeo = sock_rcvtimeo(sk, tmp);

	rcu_read_lock();
	sock = rcu_dereference_sk_user_data(sk);
	rcu_read_unlock();

	if (!sock || !sock->peer) {
		ret = -EBADF;
		goto unlock;
	}

	while (ptr_ring_empty_bh(&sock->recv_ring)) {
		if (sk->sk_shutdown & RCV_SHUTDOWN)
			return 0;

		if (sock_flag(sk, SOCK_DONE))
			return 0;

		if (!timeo) {
			ret = -EAGAIN;
			goto unlock;
		}

		add_wait_queue(sk_sleep(sk), &wait);
		sk_set_bit(SOCKWQ_ASYNC_WAITDATA, sk);
		sk_wait_event(sk, &timeo, !ptr_ring_empty_bh(&sock->recv_ring), &wait);
		sk_clear_bit(SOCKWQ_ASYNC_WAITDATA, sk);
		remove_wait_queue(sk_sleep(sk), &wait);

		/* take care of signals */
		if (signal_pending(current)) {
			ret = sock_intr_errno(timeo);
			goto unlock;
		}
	}

	while (len && (skb = __ptr_ring_peek(&sock->recv_ring))) {
		chunk = min_t(size_t, len, skb->len);
		ret = skb_copy_datagram_msg(skb, 0, msg, chunk);
		if (ret < 0) {
			pr_err("ovpn: cannot copy TCP data to userspace: %d\n", ret);
			kfree_skb(skb);
			goto unlock;
		}

		__skb_pull(skb, chunk);

		if (!skb->len) {
			/* skb was entirely consumed and can now be removed from the ring */
			__ptr_ring_discard_one(&sock->recv_ring);
			consume_skb(skb);
		}

		len -= chunk;
		copied += chunk;
	}
	ret = copied;

unlock:
	return ret ? : -EAGAIN;
}

static void ovpn_destroy_skb(void *skb)
{
	consume_skb(skb);
}

void ovpn_tcp_socket_detach(struct socket *sock)
{
	struct ovpn_socket *ovpn_sock;
	struct ovpn_peer *peer;

	if (!sock)
		return;

	rcu_read_lock();
	ovpn_sock = rcu_dereference_sk_user_data(sock->sk);
	rcu_read_unlock();

	if (!ovpn_sock->peer)
		return;

	peer = ovpn_sock->peer;

	/* restore CBs that were saved in ovpn_sock_set_tcp_cb() */
	write_lock_bh(&sock->sk->sk_callback_lock);
	sock->sk->sk_data_ready = peer->tcp.sk_cb.sk_data_ready;
	sock->sk->sk_write_space = peer->tcp.sk_cb.sk_write_space;
	sock->sk->sk_prot = peer->tcp.sk_cb.prot;
	rcu_assign_sk_user_data(sock->sk, NULL);
	write_unlock_bh(&sock->sk->sk_callback_lock);

	/* cancel any ongoing work. Done after removing the CBs so that these workers cannot be
	 * re-armed
	 */
	cancel_work_sync(&peer->tcp.tx_work);

	ptr_ring_cleanup(&ovpn_sock->recv_ring, ovpn_destroy_skb);
	ptr_ring_cleanup(&peer->tcp.tx_ring, ovpn_destroy_skb);
}

/* Try to send one skb (or part of it) over the TCP stream.
 *
 * Return 0 on success or a negative error code otherwise.
 *
 * Note that the skb is modified by putting away the data being sent, therefore
 * the caller should check if skb->len is zero to understand if the full skb was
 * sent or not.
 */
static int ovpn_tcp_send_one(struct ovpn_peer *peer, struct sk_buff *skb)
{
	struct msghdr msg = { .msg_flags = MSG_DONTWAIT | MSG_NOSIGNAL };
	struct kvec iv = { 0 };
	int ret;

	if (skb_linearize(skb) < 0) {
		net_err_ratelimited("%s: can't linearize packet\n", __func__);
		return -ENOMEM;
	}

	/* initialize iv structure now as skb_linearize() may have changed skb->data */
	iv.iov_base = skb->data;
	iv.iov_len = skb->len;

	ret = kernel_sendmsg(peer->sock->sock, &msg, &iv, 1, iv.iov_len);
	if (ret > 0) {
		__skb_pull(skb, ret);

		/* since we update per-cpu stats in process context,
		 * we need to disable softirqs
		 */
		local_bh_disable();
		dev_sw_netstats_tx_add(peer->ovpn->dev, 1, ret);
		local_bh_enable();

		return 0;
	}

	return ret;
}

/* Process packets in TCP TX queue */
static void ovpn_tcp_tx_work(struct work_struct *work)
{
	struct ovpn_peer *peer;
	struct sk_buff *skb;
	int ret;

	peer = container_of(work, struct ovpn_peer, tcp.tx_work);
	while ((skb = __ptr_ring_peek(&peer->tcp.tx_ring))) {
		ret = ovpn_tcp_send_one(peer, skb);
		if (ret < 0 && ret != -EAGAIN) {
			net_warn_ratelimited("%s: cannot send TCP packet to peer %u: %d\n", __func__,
					    peer->id, ret);
			/* in case of TCP error stop sending loop and delete peer */
			ovpn_peer_del(peer, OVPN_DEL_PEER_REASON_TRANSPORT_ERROR);
			break;
		} else if (!skb->len) {
			/* skb was entirely consumed and can now be removed from the ring */
			__ptr_ring_discard_one(&peer->tcp.tx_ring);
			consume_skb(skb);
		}

		/* give a chance to be rescheduled if needed */
		cond_resched();
	}
}

/* Put packet into TCP TX queue and schedule a consumer */
void ovpn_queue_tcp_skb(struct ovpn_peer *peer, struct sk_buff *skb)
{
	int ret;

	ret = ptr_ring_produce_bh(&peer->tcp.tx_ring, skb);
	if (ret < 0) {
		kfree_skb_list(skb);
		return;
	}

	queue_work(peer->ovpn->events_wq, &peer->tcp.tx_work);
}

/* Set TCP encapsulation callbacks */
int ovpn_tcp_socket_attach(struct socket *sock, struct ovpn_peer *peer)
{
	void *old_data;
	int ret;

	INIT_WORK(&peer->tcp.tx_work, ovpn_tcp_tx_work);

	ret = ptr_ring_init(&peer->tcp.tx_ring, OVPN_QUEUE_LEN, GFP_KERNEL);
	if (ret < 0) {
		netdev_err(peer->ovpn->dev, "cannot allocate TCP TX ring\n");
		return ret;
	}

	peer->tcp.skb = NULL;
	peer->tcp.offset = 0;
	peer->tcp.data_len = 0;

	write_lock_bh(&sock->sk->sk_callback_lock);

	/* make sure no pre-existing encapsulation handler exists */
	rcu_read_lock();
	old_data = rcu_dereference_sk_user_data(sock->sk);
	rcu_read_unlock();
	if (old_data) {
		netdev_err(peer->ovpn->dev, "provided socket already taken by other user\n");
		ret = -EBUSY;
		goto err;
	}

	/* sanity check */
	if (sock->sk->sk_protocol != IPPROTO_TCP) {
		netdev_err(peer->ovpn->dev, "provided socket is UDP but expected TCP\n");
		ret = -EINVAL;
		goto err;
	}

	/* only a fully connected socket are expected. Connection should be handled in userspace */
	if (sock->sk->sk_state != TCP_ESTABLISHED) {
		netdev_err(peer->ovpn->dev, "provided TCP socket is not in ESTABLISHED state: %d\n",
			   sock->sk->sk_state);
		ret = -EINVAL;
		goto err;
	}

	/* save current CBs so that they can be restored upon socket release */
	peer->tcp.sk_cb.sk_data_ready = sock->sk->sk_data_ready;
	peer->tcp.sk_cb.sk_write_space = sock->sk->sk_write_space;
	peer->tcp.sk_cb.prot = sock->sk->sk_prot;

	/* assign our static CBs */
	sock->sk->sk_data_ready = ovpn_tcp_data_ready;
	sock->sk->sk_write_space = ovpn_tcp_write_space;
	sock->sk->sk_prot = &ovpn_tcp_prot;

	write_unlock_bh(&sock->sk->sk_callback_lock);

	return 0;
err:
	write_unlock_bh(&sock->sk->sk_callback_lock);
	ptr_ring_cleanup(&peer->tcp.tx_ring, NULL);

	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;
}
07070100000025000081A400000000000000000000000165BCF43700000449000000000000000000000000000000000000003E00000000ovpn-dco-0.2.20240320~git0.2aa7f93/drivers/net/ovpn-dco/tcp.h/* SPDX-License-Identifier: GPL-2.0-only */
/*  OpenVPN data channel accelerator
 *
 *  Copyright (C) 2019-2023 OpenVPN, Inc.
 *
 *  Author:	Antonio Quartulli <antonio@openvpn.net>
 */

#ifndef _NET_OVPN_DCO_TCP_H_
#define _NET_OVPN_DCO_TCP_H_

#include "peer.h"

#include <linux/net.h>
#include <linux/skbuff.h>
#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);
void ovpn_tcp_socket_detach(struct socket *sock);

/* Prepare skb and enqueue it for sending to peer.
 *
 * Preparation consist in prepending the skb payload with its size.
 * Required by the OpenVPN protocol in order to extract packets from
 * the TCP stream on the receiver side.
 */
static inline void ovpn_tcp_send_skb(struct ovpn_peer *peer, struct sk_buff *skb)
{
	u16 len = skb->len;

	*(__be16 *)__skb_push(skb, sizeof(u16)) = htons(len);
	ovpn_queue_tcp_skb(peer, skb);
}

#endif /* _NET_OVPN_DCO_TCP_H_ */
07070100000026000081A400000000000000000000000165BCF43700002494000000000000000000000000000000000000003E00000000ovpn-dco-0.2.20240320~git0.2aa7f93/drivers/net/ovpn-dco/udp.c// SPDX-License-Identifier: GPL-2.0
/*  OpenVPN data channel accelerator
 *
 *  Copyright (C) 2019-2023 OpenVPN, Inc.
 *
 *  Author:	Antonio Quartulli <antonio@openvpn.net>
 */

#include "main.h"
#include "bind.h"
#include "ovpn.h"
#include "ovpnstruct.h"
#include "peer.h"
#include "proto.h"
#include "skb.h"
#include "udp.h"

#include <linux/inetdevice.h>
#include <linux/skbuff.h>
#include <linux/socket.h>
#include <net/addrconf.h>
#include <net/dst_cache.h>
#include <net/route.h>
#include <net/ipv6_stubs.h>
#include <net/udp_tunnel.h>

/**
 * ovpn_udp_encap_recv() - Start processing a received UDP packet.
 * If the first byte of the payload is DATA_V2, the packet is further processed,
 * otherwise it is forwarded to the UDP stack for delivery to user space.
 *
 * @sk: the socket the packet was received on
 * @skb: the sk_buff containing the actual packet
 *
 * Return codes:
 *  0 : we consumed or dropped packet
 * >0 : skb should be passed up to userspace as UDP (packet not consumed)
 * <0 : skb should be resubmitted as proto -N (packet not consumed)
 */
static int ovpn_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
{
	struct ovpn_peer *peer = NULL;
	struct ovpn_struct *ovpn;
	u32 peer_id;
	u8 opcode;
	int ret;

	ovpn = ovpn_from_udp_sock(sk);
	if (unlikely(!ovpn)) {
		net_err_ratelimited("%s: cannot obtain ovpn object from UDP socket\n", __func__);
		goto drop;
	}

	/* Make sure the first 4 bytes of the skb data buffer after the UDP header are accessible.
	 * They are required to fetch the OP code, the key ID and the peer ID.
	 */
	if (unlikely(!pskb_may_pull(skb, sizeof(struct udphdr) + 4))) {
		net_dbg_ratelimited("%s: packet too small\n", __func__);
		goto drop;
	}

	opcode = ovpn_opcode_from_skb(skb, sizeof(struct udphdr));
	if (likely(opcode == OVPN_DATA_V2)) {
		peer_id = ovpn_peer_id_from_skb(skb, sizeof(struct udphdr));
		/* some OpenVPN server implementations send data packets with the peer-id set to
		 * undef. In this case we skip the peer lookup by peer-id and we try with the
		 * transport address
		 */
		if (peer_id != OVPN_PEER_ID_UNDEF) {
			peer = ovpn_peer_lookup_id(ovpn, peer_id);
			if (!peer) {
				net_err_ratelimited("%s: received data from unknown peer (id: %d)\n",
						   __func__, peer_id);
				goto drop;
			}

			/* check if this peer changed it's IP address and update state */
			ovpn_peer_float(peer, skb);
		}
	}

	if (!peer) {
		/* might be a control packet or a data packet with undef peer-id */
		peer = ovpn_peer_lookup_transp_addr(ovpn, skb);
		if (unlikely(!peer)) {
			if (opcode != OVPN_DATA_V2) {
				netdev_dbg(ovpn->dev,
					   "%s: control packet from unknown peer, sending to userspace",
					   __func__);
				return 1;
			}

			netdev_dbg(ovpn->dev,
				   "%s: received data with undef peer-id from unknown source\n",
				   __func__);
			goto drop;
		}
	}

	/* At this point we know the packet is from a configured peer.
	 * DATA_V2 packets are handled in kernel space, the rest goes to user space.
	 *
	 * Return 1 to instruct the stack to let the packet bubble up to userspace
	 */
	if (unlikely(opcode != OVPN_DATA_V2)) {
		ovpn_peer_put(peer);
		return 1;
	}

	/* pop off outer UDP header */
	__skb_pull(skb, sizeof(struct udphdr));

	ret = ovpn_recv(ovpn, peer, skb);
	if (unlikely(ret < 0)) {
		net_err_ratelimited("%s: cannot handle incoming packet from peer %d: %d\n",
				    __func__, peer->id, ret);
		goto drop;
	}

	/* should this be a non DATA_V2 packet, ret will be >0 and this will instruct the UDP
	 * stack to continue processing this packet as usual (i.e. deliver to user space)
	 */
	return ret;

drop:
	if (peer)
		ovpn_peer_put(peer);
	kfree_skb(skb);
	return 0;
}

static int ovpn_udp4_output(struct ovpn_struct *ovpn, struct ovpn_bind *bind,
			    struct dst_cache *cache, struct sock *sk,
			    struct sk_buff *skb)
{
	struct rtable *rt;
	struct flowi4 fl = {
		.saddr = bind->local.ipv4.s_addr,
		.daddr = bind->sa.in4.sin_addr.s_addr,
		.fl4_sport = inet_sk(sk)->inet_sport,
		.fl4_dport = bind->sa.in4.sin_port,
		.flowi4_proto = sk->sk_protocol,
		.flowi4_mark = sk->sk_mark,
	};
	int ret;

	local_bh_disable();
	rt = dst_cache_get_ip4(cache, &fl.saddr);
	if (rt)
		goto transmit;

	if (unlikely(!inet_confirm_addr(sock_net(sk), NULL, 0, fl.saddr, RT_SCOPE_HOST))) {
		/* we may end up here when the cached address is not usable anymore.
		 * In this case we reset address/cache and perform a new look up
		 */
		fl.saddr = 0;
		bind->local.ipv4.s_addr = 0;
		dst_cache_reset(cache);
	}

	rt = ip_route_output_flow(sock_net(sk), &fl, sk);
	if (IS_ERR(rt) && PTR_ERR(rt) == -EINVAL) {
		fl.saddr = 0;
		bind->local.ipv4.s_addr = 0;
		dst_cache_reset(cache);

		rt = ip_route_output_flow(sock_net(sk), &fl, sk);
	}

	if (IS_ERR(rt)) {
		ret = PTR_ERR(rt);
		net_dbg_ratelimited("%s: no route to host %pISpc: %d\n", ovpn->dev->name,
				    &bind->sa.in4, ret);
		goto err;
	}
	dst_cache_set_ip4(cache, &rt->dst, fl.saddr);

transmit:
	udp_tunnel_xmit_skb(rt, sk, skb, fl.saddr, fl.daddr, 0,
			    ip4_dst_hoplimit(&rt->dst), 0, fl.fl4_sport,
			    fl.fl4_dport, false, sk->sk_no_check_tx);
	ret = 0;
err:
	local_bh_enable();
	return ret;
}

#if IS_ENABLED(CONFIG_IPV6)
static int ovpn_udp6_output(struct ovpn_struct *ovpn, struct ovpn_bind *bind,
			    struct dst_cache *cache, struct sock *sk,
			    struct sk_buff *skb)
{
	struct dst_entry *dst;
	int ret;

	struct flowi6 fl = {
		.saddr = bind->local.ipv6,
		.daddr = bind->sa.in6.sin6_addr,
		.fl6_sport = inet_sk(sk)->inet_sport,
		.fl6_dport = bind->sa.in6.sin6_port,
		.flowi6_proto = sk->sk_protocol,
		.flowi6_mark = sk->sk_mark,
		.flowi6_oif = bind->sa.in6.sin6_scope_id,
	};

	local_bh_disable();
	dst = dst_cache_get_ip6(cache, &fl.saddr);
	if (dst)
		goto transmit;

	if (unlikely(!ipv6_chk_addr(sock_net(sk), &fl.saddr, NULL, 0))) {
		/* we may end up here when the cached address is not usable anymore.
		 * In this case we reset address/cache and perform a new look up
		 */
		fl.saddr = in6addr_any;
		bind->local.ipv6 = in6addr_any;
		dst_cache_reset(cache);
	}

	dst = ipv6_stub->ipv6_dst_lookup_flow(sock_net(sk), sk, &fl, NULL);
	if (IS_ERR(dst)) {
		ret = PTR_ERR(dst);
		net_dbg_ratelimited("%s: no route to host %pISpc: %d\n", ovpn->dev->name,
				    &bind->sa.in6, ret);
		goto err;
	}
	dst_cache_set_ip6(cache, dst, &fl.saddr);

transmit:
	udp_tunnel6_xmit_skb(dst, sk, skb, skb->dev, &fl.saddr, &fl.daddr, 0,
			     ip6_dst_hoplimit(dst), 0, fl.fl6_sport,
			     fl.fl6_dport, udp_get_no_check6_tx(sk));
	ret = 0;
err:
	local_bh_enable();
	return ret;
}
#endif

/* Transmit skb utilizing kernel-provided UDP tunneling framework.
 *
 * rcu_read_lock should be held on entry.
 * On return, the skb is consumed.
 */
static int ovpn_udp_output(struct ovpn_struct *ovpn, struct ovpn_bind *bind,
			   struct dst_cache *cache, struct sock *sk,
			   struct sk_buff *skb)
{
	int ret;

	ovpn_rcu_lockdep_assert_held();

	/* set sk to null if skb is already orphaned */
	if (!skb->destructor)
		skb->sk = NULL;

	/* always permit openvpn-created packets to be (outside) fragmented */
	skb->ignore_df = 1;

	switch (bind->sa.in4.sin_family) {
	case AF_INET:
		ret = ovpn_udp4_output(ovpn, bind, cache, sk, skb);
		break;
#if IS_ENABLED(CONFIG_IPV6)
	case AF_INET6:
		ret = ovpn_udp6_output(ovpn, bind, cache, sk, skb);
		break;
#endif
	default:
		ret = -EAFNOSUPPORT;
		break;
	}

	return ret;
}

void ovpn_udp_send_skb(struct ovpn_struct *ovpn, struct ovpn_peer *peer,
		       struct sk_buff *skb)
{
	struct ovpn_bind *bind;
	struct socket *sock;
	int ret = -1;

	skb->dev = ovpn->dev;
	/* no checksum performed at this layer */
	skb->ip_summed = CHECKSUM_NONE;

	/* get socket info */
	sock = peer->sock->sock;
	if (unlikely(!sock)) {
		net_warn_ratelimited("%s: no sock for remote peer\n", __func__);
		goto out;
	}

	rcu_read_lock();
	/* get binding */
	bind = rcu_dereference(peer->bind);
	if (unlikely(!bind)) {
		net_warn_ratelimited("%s: no bind for remote peer\n", __func__);
		goto out_unlock;
	}

	/* crypto layer -> transport (UDP) */
	ret = ovpn_udp_output(ovpn, bind, &peer->dst_cache, sock->sk, skb);

out_unlock:
	rcu_read_unlock();
out:
	if (ret < 0)
		kfree_skb(skb);
}

/* Set UDP encapsulation callbacks */
int ovpn_udp_socket_attach(struct socket *sock, struct ovpn_struct *ovpn)
{
	struct udp_tunnel_sock_cfg cfg = {
		.sk_user_data = ovpn,
		.encap_type = UDP_ENCAP_OVPNINUDP,
		.encap_rcv = ovpn_udp_encap_recv,
	};
	struct ovpn_socket *old_data;

	/* sanity check */
	if (sock->sk->sk_protocol != IPPROTO_UDP) {
		netdev_err(ovpn->dev, "%s: expected UDP socket\n", __func__);
		return -EINVAL;
	}

	/* make sure no pre-existing encapsulation handler exists */
	rcu_read_lock();
	old_data = rcu_dereference_sk_user_data(sock->sk);
	rcu_read_unlock();
	if (old_data) {
		if (old_data->ovpn == ovpn) {
			netdev_dbg(ovpn->dev,
				   "%s: provided socket already owned by this interface\n",
				   __func__);
			return -EALREADY;
		}

		netdev_err(ovpn->dev, "%s: provided socket already taken by other user\n",
			   __func__);
		return -EBUSY;
	}

	setup_udp_tunnel_sock(sock_net(sock->sk), sock, &cfg);

	return 0;
}

/* Detach socket from encapsulation handler and/or other callbacks */
void ovpn_udp_socket_detach(struct socket *sock)
{
	struct udp_tunnel_sock_cfg cfg = { };

	setup_udp_tunnel_sock(sock_net(sock->sk), sock, &cfg);
}
07070100000027000081A400000000000000000000000165BCF43700000289000000000000000000000000000000000000003E00000000ovpn-dco-0.2.20240320~git0.2aa7f93/drivers/net/ovpn-dco/udp.h/* SPDX-License-Identifier: GPL-2.0-only */
/*  OpenVPN data channel accelerator
 *
 *  Copyright (C) 2019-2023 OpenVPN, Inc.
 *
 *  Author:	Antonio Quartulli <antonio@openvpn.net>
 */

#ifndef _NET_OVPN_DCO_UDP_H_
#define _NET_OVPN_DCO_UDP_H_

#include "peer.h"
#include "ovpnstruct.h"

#include <linux/net.h>
#include <linux/skbuff.h>
#include <linux/types.h>
#include <net/sock.h>

int ovpn_udp_socket_attach(struct socket *sock, struct ovpn_struct *ovpn);
void ovpn_udp_socket_detach(struct socket *sock);
void ovpn_udp_send_skb(struct ovpn_struct *ovpn, struct ovpn_peer *peer,
		       struct sk_buff *skb);

#endif /* _NET_OVPN_DCO_UDP_H_ */
07070100000028000081ED00000000000000000000000165BCF437000003B9000000000000000000000000000000000000003A00000000ovpn-dco-0.2.20240320~git0.2aa7f93/gen-compat-autoconf.sh#! /bin/sh
# SPDX-License-Identifier: GPL-2.0
# Copyright (C) 2020-2023 OpenVPN, Inc.
#
#  Author:	Antonio Quartulli <antonio@openvpn.net>

set -e

TARGET=${1:="compat-autoconf.h"}
TMP="${TARGET}.tmp"

echo > "${TMP}"

gen_config() {
	KEY="${1}"
	VALUE="${2}"

	echo "#undef ${KEY}"
	echo "#undef __enabled_${KEY}"
	echo "#undef __enabled_${KEY}_MODULE"
	case "${VALUE}" in
	y)
		echo "#define ${KEY} 1"
		echo "#define __enabled_${KEY} 1"
		echo "#define __enabled_${KEY}_MODULE 0"
		;;
	m)
		echo "#define ${KEY} 1"
		echo "#define __enabled_${KEY} 0"
		echo "#define __enabled_${KEY}_MODULE 1"
		;;
	n)
		echo "#define __enabled_${KEY} 0"
		echo "#define __enabled_${KEY}_MODULE 0"
		;;
	*)
		echo "#define ${KEY} \"${VALUE}\""
		;;
	esac
}

gen_config 'CONFIG_OVPN_DCO_DEBUG' ${CONFIG_OVPN_DCO_DEBUG:="n"} >> "${TMP}"

# only regenerate compat-autoconf.h when config was changed
diff "${TMP}" "${TARGET}" > /dev/null 2>&1 || cp "${TMP}" "${TARGET}"
07070100000029000041ED00000000000000000000000265BCF43700000000000000000000000000000000000000000000002B00000000ovpn-dco-0.2.20240320~git0.2aa7f93/include0707010000002A000041ED00000000000000000000000265BCF43700000000000000000000000000000000000000000000003000000000ovpn-dco-0.2.20240320~git0.2aa7f93/include/uapi0707010000002B000041ED00000000000000000000000265BCF43700000000000000000000000000000000000000000000003600000000ovpn-dco-0.2.20240320~git0.2aa7f93/include/uapi/linux0707010000002C000081A400000000000000000000000165BCF4370000182B000000000000000000000000000000000000004100000000ovpn-dco-0.2.20240320~git0.2aa7f93/include/uapi/linux/ovpn_dco.h/* SPDX-License-Identifier: (GPL-2.0-only WITH Linux-syscall-note) OR MIT */
/*
 *  OpenVPN data channel accelerator
 *
 *  Copyright (C) 2019-2023 OpenVPN, Inc.
 *
 *  Author:	James Yonan <james@openvpn.net>
 *		Antonio Quartulli <antonio@openvpn.net>
 */

#ifndef _UAPI_LINUX_OVPN_DCO_H_
#define _UAPI_LINUX_OVPN_DCO_H_

#define OVPN_NL_NAME "ovpn-dco-v2"

#define OVPN_NL_MULTICAST_GROUP_PEERS "peers"

/**
 * enum ovpn_nl_commands - supported netlink commands
 */
enum ovpn_nl_commands {
	/**
	 * @OVPN_CMD_UNSPEC: unspecified command to catch errors
	 */
	OVPN_CMD_UNSPEC = 0,

	/**
	 * @OVPN_CMD_NEW_PEER: Configure peer with its crypto keys
	 */
	OVPN_CMD_NEW_PEER,

	/**
	 * @OVPN_CMD_SET_PEER: Tweak parameters for an existing peer
	 */
	OVPN_CMD_SET_PEER,

	/**
	 * @OVPN_CMD_DEL_PEER: Remove peer from internal table
	 */
	OVPN_CMD_DEL_PEER,

	OVPN_CMD_NEW_KEY,

	OVPN_CMD_SWAP_KEYS,

	OVPN_CMD_DEL_KEY,

	/**
	 * @OVPN_CMD_GET_PEER: Retrieve the status of a peer or all peers
	 */
	OVPN_CMD_GET_PEER,
};

enum ovpn_cipher_alg {
	/**
	 * @OVPN_CIPHER_ALG_NONE: No encryption - reserved for debugging only
	 */
	OVPN_CIPHER_ALG_NONE = 0,
	/**
	 * @OVPN_CIPHER_ALG_AES_GCM: AES-GCM AEAD cipher with any allowed key size
	 */
	OVPN_CIPHER_ALG_AES_GCM,
	/**
	 * @OVPN_CIPHER_ALG_CHACHA20_POLY1305: ChaCha20Poly1305 AEAD cipher
	 */
	OVPN_CIPHER_ALG_CHACHA20_POLY1305,
};

enum ovpn_del_peer_reason {
	__OVPN_DEL_PEER_REASON_FIRST,
	OVPN_DEL_PEER_REASON_TEARDOWN = __OVPN_DEL_PEER_REASON_FIRST,
	OVPN_DEL_PEER_REASON_USERSPACE,
	OVPN_DEL_PEER_REASON_EXPIRED,
	OVPN_DEL_PEER_REASON_TRANSPORT_ERROR,
	OVPN_DEL_PEER_REASON_TRANSPORT_DISCONNECT,
	__OVPN_DEL_PEER_REASON_AFTER_LAST
};

enum ovpn_key_slot {
	__OVPN_KEY_SLOT_FIRST,
	OVPN_KEY_SLOT_PRIMARY = __OVPN_KEY_SLOT_FIRST,
	OVPN_KEY_SLOT_SECONDARY,
	__OVPN_KEY_SLOT_AFTER_LAST,
};

enum ovpn_netlink_attrs {
	OVPN_ATTR_UNSPEC = 0,
	OVPN_ATTR_IFINDEX,
	OVPN_ATTR_NEW_PEER,
	OVPN_ATTR_SET_PEER,
	OVPN_ATTR_DEL_PEER,
	OVPN_ATTR_NEW_KEY,
	OVPN_ATTR_SWAP_KEYS,
	OVPN_ATTR_DEL_KEY,
	OVPN_ATTR_GET_PEER,

	__OVPN_ATTR_AFTER_LAST,
	OVPN_ATTR_MAX = __OVPN_ATTR_AFTER_LAST - 1,
};

enum ovpn_netlink_key_dir_attrs {
	OVPN_KEY_DIR_ATTR_UNSPEC = 0,
	OVPN_KEY_DIR_ATTR_CIPHER_KEY,
	OVPN_KEY_DIR_ATTR_NONCE_TAIL,

	__OVPN_KEY_DIR_ATTR_AFTER_LAST,
	OVPN_KEY_DIR_ATTR_MAX = __OVPN_KEY_DIR_ATTR_AFTER_LAST - 1,
};

enum ovpn_netlink_new_key_attrs {
	OVPN_NEW_KEY_ATTR_UNSPEC = 0,
	OVPN_NEW_KEY_ATTR_PEER_ID,
	OVPN_NEW_KEY_ATTR_KEY_SLOT,
	OVPN_NEW_KEY_ATTR_KEY_ID,
	OVPN_NEW_KEY_ATTR_CIPHER_ALG,
	OVPN_NEW_KEY_ATTR_ENCRYPT_KEY,
	OVPN_NEW_KEY_ATTR_DECRYPT_KEY,

	__OVPN_NEW_KEY_ATTR_AFTER_LAST,
	OVPN_NEW_KEY_ATTR_MAX = __OVPN_NEW_KEY_ATTR_AFTER_LAST - 1,
};

enum ovpn_netlink_del_key_attrs {
	OVPN_DEL_KEY_ATTR_UNSPEC = 0,
	OVPN_DEL_KEY_ATTR_PEER_ID,
	OVPN_DEL_KEY_ATTR_KEY_SLOT,

	__OVPN_DEL_KEY_ATTR_AFTER_LAST,
	OVPN_DEL_KEY_ATTR_MAX = __OVPN_DEL_KEY_ATTR_AFTER_LAST - 1,
};

enum ovpn_netlink_swap_keys_attrs {
	OVPN_SWAP_KEYS_ATTR_UNSPEC = 0,
	OVPN_SWAP_KEYS_ATTR_PEER_ID,

	__OVPN_SWAP_KEYS_ATTR_AFTER_LAST,
	OVPN_SWAP_KEYS_ATTR_MAX = __OVPN_SWAP_KEYS_ATTR_AFTER_LAST - 1,

};

enum ovpn_netlink_new_peer_attrs {
	OVPN_NEW_PEER_ATTR_UNSPEC = 0,
	OVPN_NEW_PEER_ATTR_PEER_ID,
	OVPN_NEW_PEER_ATTR_SOCKADDR_REMOTE,
	OVPN_NEW_PEER_ATTR_SOCKET,
	OVPN_NEW_PEER_ATTR_IPV4,
	OVPN_NEW_PEER_ATTR_IPV6,
	OVPN_NEW_PEER_ATTR_LOCAL_IP,

	__OVPN_NEW_PEER_ATTR_AFTER_LAST,
	OVPN_NEW_PEER_ATTR_MAX = __OVPN_NEW_PEER_ATTR_AFTER_LAST - 1,
};

enum ovpn_netlink_set_peer_attrs {
	OVPN_SET_PEER_ATTR_UNSPEC = 0,
	OVPN_SET_PEER_ATTR_PEER_ID,
	OVPN_SET_PEER_ATTR_KEEPALIVE_INTERVAL,
	OVPN_SET_PEER_ATTR_KEEPALIVE_TIMEOUT,

	__OVPN_SET_PEER_ATTR_AFTER_LAST,
	OVPN_SET_PEER_ATTR_MAX = __OVPN_SET_PEER_ATTR_AFTER_LAST - 1,
};

enum ovpn_netlink_del_peer_attrs {
	OVPN_DEL_PEER_ATTR_UNSPEC = 0,
	OVPN_DEL_PEER_ATTR_REASON,
	OVPN_DEL_PEER_ATTR_PEER_ID,

	__OVPN_DEL_PEER_ATTR_AFTER_LAST,
	OVPN_DEL_PEER_ATTR_MAX = __OVPN_DEL_PEER_ATTR_AFTER_LAST - 1,
};

enum ovpn_netlink_get_peer_attrs {
	OVPN_GET_PEER_ATTR_UNSPEC = 0,
	OVPN_GET_PEER_ATTR_PEER_ID,

	__OVPN_GET_PEER_ATTR_AFTER_LAST,
	OVPN_GET_PEER_ATTR_MAX = __OVPN_GET_PEER_ATTR_AFTER_LAST - 1,
};

enum ovpn_netlink_get_peer_response_attrs {
	OVPN_GET_PEER_RESP_ATTR_UNSPEC = 0,
	OVPN_GET_PEER_RESP_ATTR_PEER_ID,
	OVPN_GET_PEER_RESP_ATTR_SOCKADDR_REMOTE,
	OVPN_GET_PEER_RESP_ATTR_IPV4,
	OVPN_GET_PEER_RESP_ATTR_IPV6,
	OVPN_GET_PEER_RESP_ATTR_LOCAL_IP,
	OVPN_GET_PEER_RESP_ATTR_LOCAL_PORT,
	OVPN_GET_PEER_RESP_ATTR_KEEPALIVE_INTERVAL,
	OVPN_GET_PEER_RESP_ATTR_KEEPALIVE_TIMEOUT,
	OVPN_GET_PEER_RESP_ATTR_VPN_RX_BYTES,
	OVPN_GET_PEER_RESP_ATTR_VPN_TX_BYTES,
	OVPN_GET_PEER_RESP_ATTR_VPN_RX_PACKETS,
	OVPN_GET_PEER_RESP_ATTR_VPN_TX_PACKETS,
	OVPN_GET_PEER_RESP_ATTR_LINK_RX_BYTES,
	OVPN_GET_PEER_RESP_ATTR_LINK_TX_BYTES,
	OVPN_GET_PEER_RESP_ATTR_LINK_RX_PACKETS,
	OVPN_GET_PEER_RESP_ATTR_LINK_TX_PACKETS,

	__OVPN_GET_PEER_RESP_ATTR_AFTER_LAST,
	OVPN_GET_PEER_RESP_ATTR_MAX = __OVPN_GET_PEER_RESP_ATTR_AFTER_LAST - 1,
};

enum ovpn_netlink_peer_stats_attrs {
	OVPN_PEER_STATS_ATTR_UNSPEC = 0,
	OVPN_PEER_STATS_BYTES,
	OVPN_PEER_STATS_PACKETS,

	__OVPN_PEER_STATS_ATTR_AFTER_LAST,
	OVPN_PEER_STATS_ATTR_MAX = __OVPN_PEER_STATS_ATTR_AFTER_LAST - 1,
};

enum ovpn_netlink_peer_attrs {
	OVPN_PEER_ATTR_UNSPEC = 0,
	OVPN_PEER_ATTR_PEER_ID,
	OVPN_PEER_ATTR_SOCKADDR_REMOTE,
	OVPN_PEER_ATTR_IPV4,
	OVPN_PEER_ATTR_IPV6,
	OVPN_PEER_ATTR_LOCAL_IP,
	OVPN_PEER_ATTR_KEEPALIVE_INTERVAL,
	OVPN_PEER_ATTR_KEEPALIVE_TIMEOUT,
	OVPN_PEER_ATTR_ENCRYPT_KEY,
	OVPN_PEER_ATTR_DECRYPT_KEY,
	OVPN_PEER_ATTR_RX_STATS,
	OVPN_PEER_ATTR_TX_STATS,

	__OVPN_PEER_ATTR_AFTER_LAST,
	OVPN_PEER_ATTR_MAX = __OVPN_PEER_ATTR_AFTER_LAST - 1,
};

enum ovpn_netlink_packet_attrs {
	OVPN_PACKET_ATTR_UNSPEC = 0,
	OVPN_PACKET_ATTR_PACKET,
	OVPN_PACKET_ATTR_PEER_ID,

	__OVPN_PACKET_ATTR_AFTER_LAST,
	OVPN_PACKET_ATTR_MAX = __OVPN_PACKET_ATTR_AFTER_LAST - 1,
};

enum ovpn_ifla_attrs {
	IFLA_OVPN_UNSPEC = 0,
	IFLA_OVPN_MODE,

	__IFLA_OVPN_AFTER_LAST,
	IFLA_OVPN_MAX = __IFLA_OVPN_AFTER_LAST - 1,
};

enum ovpn_mode {
	__OVPN_MODE_FIRST = 0,
	OVPN_MODE_P2P = __OVPN_MODE_FIRST,
	OVPN_MODE_MP,

	__OVPN_MODE_AFTER_LAST,
};

#endif /* _UAPI_LINUX_OVPN_DCO_H_ */
0707010000002D000081A400000000000000000000000165BCF437000013C5000000000000000000000000000000000000003200000000ovpn-dco-0.2.20240320~git0.2aa7f93/linux-compat.h/* SPDX-License-Identifier: GPL-2.0-only */
/* OpenVPN data channel accelerator
 *
 *  Copyright (C) 2020-2023 OpenVPN, Inc.
 *
 *  Author:	Lev Stipakov <lev@openvpn.net>
 *		Antonio Quartulli <antonio@openvpn.net>
 */

#ifndef _NET_OVPN_DCO_LINUX_COMPAT_H_
#define _NET_OVPN_DCO_LINUX_COMPAT_H_

#include <linux/kconfig.h>
#include <linux/version.h>

/*
 *  Red Hat Enterprise Linux kernels provides helper macros for
 *  detecting the distribution version.  This is needed here as
 *  Red Hat backports features and changes from newer kernels
 *  into the older kernel baseline.  Therefore the RHEL kernel
 *  features may not be correctly identified by the Linux kernel
 *  version alone.
 *
 *  To be able to build ovpn-dco on non-RHEL kernels, we need
 *  these helper macros defined.  And we want the result to
 *  always be true, to not disable the other kernel version
 *  checks
 */
#ifndef RHEL_RELEASE_CODE
#define RHEL_RELEASE_CODE 0
#endif
#ifndef RHEL_RELEASE_VERSION
#define RHEL_RELEASE_VERSION(m, n) 1
#endif

/* not part of any kernel yet */
#ifndef NLA_POLICY_MAX_LEN
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0)
#define NLA_POLICY_MAX_LEN(_len) { .type = NLA_BINARY, .len = _len }
#else
#define NLA_POLICY_MAX_LEN(_len) NLA_POLICY_MAX(NLA_BINARY, _len)
#endif
#endif

#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 2, 0) && RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(9, 3)

#define genl_split_ops genl_ops

#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(6, 2, 0) && RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(9, 3) */

#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 19, 0)

/**
 * commit 58caed3dacb4 renamed to netif_napi_add_tx_weight,
 * commit c3f760ef1287 removed netif_tx_napi_add
 */
#define netif_napi_add_tx_weight netif_tx_napi_add

#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(5, 19, 0) */

#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 0)

#define sock_is_readable stream_memory_read

#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 0) */

#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 11, 0) && RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(8, 0)

#define dev_get_tstats64 ip_tunnel_get_stats64

#include <linux/netdevice.h>

static inline void dev_sw_netstats_tx_add(struct net_device *dev,
					  unsigned int packets,
					  unsigned int len)
{
	struct pcpu_sw_netstats *tstats = this_cpu_ptr(dev->tstats);

	u64_stats_update_begin(&tstats->syncp);
	tstats->tx_bytes += len;
	tstats->tx_packets += packets;
	u64_stats_update_end(&tstats->syncp);
}

#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(5, 11, 0) && RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(8, 0) */

#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0) && RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(8, 0)

#define genl_small_ops genl_ops
#define small_ops ops
#define n_small_ops n_ops

#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0) && RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(8, 0) */

#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0) && RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(8, 0)

#include <linux/netdevice.h>

static inline void dev_sw_netstats_rx_add(struct net_device *dev, unsigned int len)
{
	struct pcpu_sw_netstats *tstats = this_cpu_ptr(dev->tstats);

	u64_stats_update_begin(&tstats->syncp);
	tstats->rx_bytes += len;
	tstats->rx_packets++;
	u64_stats_update_end(&tstats->syncp);
}

#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0) && RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(8, 0) */

#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 6, 0)

/* Iterate through singly-linked GSO fragments of an skb. */
#define skb_list_walk_safe(first, skb, next_skb)				\
	for ((skb) = (first), (next_skb) = (skb) ? (skb)->next : NULL; (skb);	\
	     (skb) = (next_skb), (next_skb) = (skb) ? (skb)->next : NULL)

#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(5, 6, 0) */

#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 5, 0)
/**
 * rcu_replace_pointer() - replace an RCU pointer, returning its old value
 * @rcu_ptr: RCU pointer, whose old value is returned
 * @ptr: regular pointer
 * @c: the lockdep conditions under which the dereference will take place
 *
 * Perform a replacement, where @rcu_ptr is an RCU-annotated
 * pointer and @c is the lockdep argument that is passed to the
 * rcu_dereference_protected() call used to read that pointer.  The old
 * value of @rcu_ptr is returned, and @rcu_ptr is set to @ptr.
 */
#undef rcu_replace_pointer
#define rcu_replace_pointer(rcu_ptr, ptr, c)				\
({									\
	typeof(ptr) __tmp = rcu_dereference_protected((rcu_ptr), (c));	\
	rcu_assign_pointer((rcu_ptr), (ptr));				\
	__tmp;								\
})

#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(5, 5, 0) */

#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)

/* commit 895b5c9f206e renamed nf_reset to nf_reset_ct */
#undef nf_reset_ct
#define nf_reset_ct nf_reset

#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0) */

#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 2, 0)

/* commit 1550c171935d introduced rt_gw4 and rt_gw6 for IPv6 gateways */
#define rt_gw4 rt_gateway

#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(5, 2, 0) */

#endif /* _NET_OVPN_DCO_LINUX_COMPAT_H_ */
0707010000002E000041ED00000000000000000000000265BCF43700000000000000000000000000000000000000000000002900000000ovpn-dco-0.2.20240320~git0.2aa7f93/tests0707010000002F000081A400000000000000000000000165BCF43700000154000000000000000000000000000000000000003200000000ovpn-dco-0.2.20240320~git0.2aa7f93/tests/Makefile# SPDX-License-Identifier: GPL-2.0
# Copyright (C) 2020-2023 OpenVPN, Inc.
#
#  Author:	Antonio Quartulli <antonio@openvpn.net>

RM ?= rm -f
CFLAGS = -Wall


ovpn-cli: ovpn-cli.c
	$(CC) $(CFLAGS) $@.c -I../include/uapi \
		`pkg-config --cflags --libs libnl-3.0 libnl-genl-3.0` \
		-lmbedtls -lmbedcrypto -Wall -o $@

clean:
	$(RM) ovpn-cli
07070100000030000081A400000000000000000000000165BCF4370000015D000000000000000000000000000000000000003400000000ovpn-dco-0.2.20240320~git0.2aa7f93/tests/data64.keyjRqMACN7d7/aFQNT8S7jkrBD8uwrgHbG5OQZP2eu4R1Y7tfpS2bf5RHv06Vi163CGoaIiTX99R3B
ia9ycAH8Wz1+9PWv51dnBLur9jbShlgZ2QHLtUc4a/gfT7zZwULXuuxdLnvR21DDeMBaTbkgbai9
uvAa7ne1liIgGFzbv+Bas4HDVrygxIxuAnP5Qgc3648IJkZ0QEXPF+O9f0n5+QIvGCxkAUVx+5K6
KIs+SoeWXnAopELmoGSjUpFtJbagXK82HfdqpuUxT2Tnuef0/14SzVE/vNleBNu2ZbyrSAaah8tE
BofkPJUBFY+YQcfZNM5Dgrw3i+Bpmpq/gpdg5w==
07070100000031000081ED00000000000000000000000165BCF43700000BB9000000000000000000000000000000000000003700000000ovpn-dco-0.2.20240320~git0.2aa7f93/tests/float-test.sh#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
# Copyright (C) 2020-2023 OpenVPN, Inc.
#
#  Author:	Antonio Quartulli <antonio@openvpn.net>

#set -x
set -e

UDP_PEERS_FILE=${UDP_PEERS_FILE:-udp_peers.txt}
TCP_PEERS_FILE=${TCP_PEERS_FILE:-tcp_peers.txt}
OVPN_CLI=${OVPN_CLI:-./ovpn-cli}
ALG=${ALG:-aes}

function create_ns() {
	ip netns add peer$1
}

function setup_ns() {
	if [ $1 -eq 0 ]; then
		for p in $(seq 1 $NUM_PEERS); do
			ip link add veth${p} netns peer0 type veth peer name veth${p} netns peer${p}

			ip -n peer0 addr add 10.10.${p}.1/24 dev veth${p}
			ip -n peer0 link set veth${p} up

			ip -n peer${p} addr add 10.10.${p}.2/24 dev veth${p}
			ip -n peer${p} link set veth${p} up
		done
	fi

	if [ $ipv6 -eq 1 ]; then
		sleep 5
	fi

	ip -n peer$1 link add tun0 type ovpn-dco
	ip -n peer$1 addr add $2 dev tun0
	ip -n peer$1 link set tun0 up
}

function add_peer() {
	if [ $tcp -eq 0 ]; then
		if [ $1 -eq 0 ]; then
			ip netns exec peer0 $OVPN_CLI tun0 new_multi_peer 1 $UDP_PEERS_FILE

			for p in $(seq 1 $NUM_PEERS); do
			#	ip netns exec peer0 $OVPN_CLI tun0 new_peer ${p} ${p} 10.10.${p}.2 1 5.5.5.$((${p} + 1))
				ip netns exec peer0 $OVPN_CLI tun0 new_key ${p} $ALG 0 data64.key
			done
		else
			ip netns exec peer${1} $OVPN_CLI tun0 new_peer 1 ${1} 10.10.${1}.1 1 5.5.5.1
			ip netns exec peer${1} $OVPN_CLI tun0 new_key ${1} $ALG 1 data64.key
		fi
	else
		if [ $1 -eq 0 ]; then
			(ip netns exec peer$1 $OVPN_CLI tun0 listen 1 $TCP_PEERS_FILE && {
				for p in $(seq 1 $NUM_PEERS); do
					ip netns exec peer0 $OVPN_CLI tun0 new_key ${p} $ALG 0 data64.key
				done
			}) &
			sleep 5
		else
			ip netns exec peer${1} $OVPN_CLI tun0 connect ${1} 10.10.${1}.1 1 5.5.5.1
			ip netns exec peer${1} $OVPN_CLI tun0 new_key ${1} $ALG 1 data64.key
		fi
	fi
}


# clean up
for p in $(seq 1 10); do
	ip -n peer0 link del veth${p} 2>/dev/null || true
done
for p in $(seq 0 10); do
	ip -n peer${p} link del tun0 2>/dev/null || true
	ip netns del peer${p} 2>/dev/null || true
done

ipv6=0
if [ "$1" == "-6" ]; then
	ipv6=1
	shift
fi

tcp=0
if [ "$1" == "-t" ]; then
	shift
	tcp=1
	NUM_PEERS=${NUM_PEERS:-$(wc -l $TCP_PEERS_FILE | awk '{print $1}')}
else
	NUM_PEERS=${NUM_PEERS:-$(wc -l $UDP_PEERS_FILE | awk '{print $1}')}
fi

for p in $(seq 0 $NUM_PEERS); do
	create_ns ${p}
done

if [ $ipv6 -eq 1 ]; then
	setup_ns 0 fc00::1 64 5.5.5.1/24 1 fc00::2 2 5.5.5.2 ipv6
	setup_ns 1 fc00::2 64 5.5.5.2/24 2 fc00::1 1 5.5.5.1 ipv6
	setup_ns 2 fc00::2 64 5.5.5.2/24 2 fc00::1 1 5.5.5.1 ipv6
else
	for p in $(seq 0 $NUM_PEERS); do
		setup_ns ${p} 5.5.5.$((${p} + 1))/24
	done

	for p in $(seq 0 $NUM_PEERS); do
		add_peer ${p}
	done
fi

for p in $(seq 1 $NUM_PEERS); do
	ip netns exec peer0 ping -qfc 2000 -w 5 5.5.5.$((${p} + 1))
done
# make clients float..
for p in $(seq 1 $NUM_PEERS); do
	ip -n peer${p} addr del 10.10.${p}.2/24 dev veth${p}
	ip -n peer${p} addr add 10.10.${p}.3/24 dev veth${p}
done
for p in $(seq 1 $NUM_PEERS); do
	ip netns exec peer${p} ping -qfc 2000 -w 5 5.5.5.1
done
07070100000032000081ED00000000000000000000000165BCF437000009E7000000000000000000000000000000000000003B00000000ovpn-dco-0.2.20240320~git0.2aa7f93/tests/multihome-test.sh#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
# Copyright (C) 2021-2023 OpenVPN, Inc.
#
#  Author:	Antonio Quartulli <antonio@openvpn.net>

#set -x
set -e

UDP_PEERS_FILE=${UDP_PEERS_FILE:-udp_peers.txt}
TCP_PEERS_FILE=${TCP_PEERS_FILE:-tcp_peers.txt}
OVPN_CLI=${OVPN_CLI:-./ovpn-cli}
ALG=${ALG:-aes}

function create_ns() {
	ip netns add peer$1
}

function setup_ns() {
	if [ $1 -eq 0 ]; then
		ip -n peer0 link add br0 type bridge
		ip -n peer0 link set br0 up

		for p in $(seq 1 $NUM_PEERS); do
			ip link add veth${p} netns peer0 type veth peer name veth${p} netns peer${p}
			ip -n peer0 addr add 10.10.0.${p}/16 dev br0
			ip -n peer0 link set veth${p} master br0			
			ip -n peer0 link set veth${p} up

			ip -n peer${p} addr add 10.10.${p}.2/16 dev veth${p}
			ip -n peer${p} link set veth${p} up
		done
	fi

	ip -n peer$1 link add tun0 type ovpn-dco
	ip -n peer$1 addr add $2 dev tun0
	ip -n peer$1 link set tun0 up
}

function add_peer() {
	if [ $tcp -eq 0 ]; then
		if [ $1 -eq 0 ]; then
			ip netns exec peer0 $OVPN_CLI tun0 new_multi_peer 1 $UDP_PEERS_FILE

			for p in $(seq 1 $NUM_PEERS); do
			#	ip netns exec peer0 $OVPN_CLI tun0 new_peer ${p} ${p} 10.10.${p}.2 1 5.5.5.$((${p} + 1))
				ip netns exec peer0 $OVPN_CLI tun0 new_key ${p} $ALG 0 data64.key
			done
		else
			ip netns exec peer${1} $OVPN_CLI tun0 new_peer 1 ${1} 10.10.0.${1} 1 5.5.5.1
			ip netns exec peer${1} $OVPN_CLI tun0 new_key ${1} $ALG 1 data64.key
		fi
	else
		if [ $1 -eq 0 ]; then
			(ip netns exec peer$1 $OVPN_CLI tun0 listen 1 $TCP_PEERS_FILE && {
				for p in $(seq 1 $NUM_PEERS); do
					ip netns exec peer0 $OVPN_CLI tun0 new_key ${p} $ALG 0 data64.key
				done
			}) &
			sleep 5
		else
			ip netns exec peer${1} $OVPN_CLI tun0 connect ${1} 10.10.0.${1} 1 5.5.5.1
			ip netns exec peer${1} $OVPN_CLI tun0 new_key ${1} $ALG 1 data64.key
		fi
	fi
}


# clean up
for p in $(seq 1 10); do
	ip -n peer0 link del veth${p} 2>/dev/null || true
done
for p in $(seq 0 10); do
	ip -n peer${p} link del tun0 2>/dev/null || true
	ip netns del peer${p} 2>/dev/null || true
done

tcp=0
if [ "$1" == "-t" ]; then
	shift
	tcp=1
	NUM_PEERS=${NUM_PEERS:-$(wc -l $TCP_PEERS_FILE | awk '{print $1}')}
else
	NUM_PEERS=${NUM_PEERS:-$(wc -l $UDP_PEERS_FILE | awk '{print $1}')}
fi

for p in $(seq 0 $NUM_PEERS); do
	create_ns ${p}
done

for p in $(seq 0 $NUM_PEERS); do
	setup_ns ${p} 5.5.5.$((${p} + 1))/24
done

for p in $(seq 0 $NUM_PEERS); do
	add_peer ${p}
done

for p in $(seq 1 $NUM_PEERS); do
	ip netns exec peer${p} ping -qfc 2000 -w 5 5.5.5.1
done
07070100000033000081ED00000000000000000000000165BCF43700000BD3000000000000000000000000000000000000003700000000ovpn-dco-0.2.20240320~git0.2aa7f93/tests/netns-test.sh#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
# Copyright (C) 2020-2023 OpenVPN, Inc.
#
#  Author:	Antonio Quartulli <antonio@openvpn.net>

#set -x
set -e

UDP_PEERS_FILE=${UDP_PEERS_FILE:-udp_peers.txt}
TCP_PEERS_FILE=${TCP_PEERS_FILE:-tcp_peers.txt}
OVPN_CLI=${OVPN_CLI:-./ovpn-cli}
ALG=${ALG:-aes}

function create_ns() {
	ip netns add peer$1
}

function setup_ns() {
	if [ $1 -eq 0 ]; then
		for p in $(seq 1 $NUM_PEERS); do
			ip link add veth${p} netns peer0 type veth peer name veth${p} netns peer${p}

			ip -n peer0 addr add 10.10.${p}.1/24 dev veth${p}
			ip -n peer0 link set veth${p} up

			ip -n peer${p} addr add 10.10.${p}.2/24 dev veth${p}
			ip -n peer${p} link set veth${p} up
		done
	fi

	if [ $ipv6 -eq 1 ]; then
		sleep 5
	fi

	ip -n peer$1 link add tun0 type ovpn-dco
	ip -n peer$1 addr add $2 dev tun0
	ip -n peer$1 link set tun0 up
}

function add_peer() {
	if [ $tcp -eq 0 ]; then
		if [ $1 -eq 0 ]; then
			ip netns exec peer0 $OVPN_CLI tun0 new_multi_peer 1 $UDP_PEERS_FILE

			for p in $(seq 1 $NUM_PEERS); do
			#	ip netns exec peer0 $OVPN_CLI tun0 new_peer ${p} ${p} 10.10.${p}.2 1 5.5.5.$((${p} + 1))
				ip netns exec peer0 $OVPN_CLI tun0 new_key ${p} $ALG 0 data64.key
			done
		else
			ip netns exec peer${1} $OVPN_CLI tun0 new_peer 1 ${1} 10.10.${1}.1 1 5.5.5.1
			ip netns exec peer${1} $OVPN_CLI tun0 new_key ${1} $ALG 1 data64.key
		fi
	else
		if [ $1 -eq 0 ]; then
			(ip netns exec peer$1 $OVPN_CLI tun0 listen 1 $TCP_PEERS_FILE && {
				for p in $(seq 1 $NUM_PEERS); do
					ip netns exec peer0 $OVPN_CLI tun0 new_key ${p} $ALG 0 data64.key
				done
			}) &
			sleep 5
		else
			ip netns exec peer${1} $OVPN_CLI tun0 connect ${1} 10.10.${1}.1 1 5.5.5.1
			ip netns exec peer${1} $OVPN_CLI tun0 new_key ${1} $ALG 1 data64.key
		fi
	fi
}


# clean up
for p in $(seq 1 10); do
	ip -n peer0 link del veth${p} 2>/dev/null || true
done
for p in $(seq 0 10); do
	ip -n peer${p} link del tun0 2>/dev/null || true
	ip netns del peer${p} 2>/dev/null || true
done

ipv6=0
if [ "$1" == "-6" ]; then
	ipv6=1
	shift
fi

tcp=0
if [ "$1" == "-t" ]; then
	shift
	tcp=1
	NUM_PEERS=${NUM_PEERS:-$(wc -l $TCP_PEERS_FILE | awk '{print $1}')}
else
	NUM_PEERS=${NUM_PEERS:-$(wc -l $UDP_PEERS_FILE | awk '{print $1}')}
fi

for p in $(seq 0 $NUM_PEERS); do
	create_ns ${p}
done

if [ $ipv6 -eq 1 ]; then
	setup_ns 0 fc00::1 64 5.5.5.1/24 1 fc00::2 2 5.5.5.2 ipv6
	setup_ns 1 fc00::2 64 5.5.5.2/24 2 fc00::1 1 5.5.5.1 ipv6
	setup_ns 2 fc00::2 64 5.5.5.2/24 2 fc00::1 1 5.5.5.1 ipv6
else
	for p in $(seq 0 $NUM_PEERS); do
		setup_ns ${p} 5.5.5.$((${p} + 1))/24
	done

	for p in $(seq 0 $NUM_PEERS); do
		add_peer ${p}
	done
fi

for p in $(seq 1 $NUM_PEERS); do
	ip netns exec peer0 ping -qfc 2000 -w 5 5.5.5.$((${p} + 1))
done

echo "Querying all peers:"
ip netns exec peer0 $OVPN_CLI tun0 get_peer

echo "Querying peer 1:"
ip netns exec peer0 $OVPN_CLI tun0 get_peer 1

echo "Querying non-existent peer 10:"
ip netns exec peer0 $OVPN_CLI tun0 get_peer 10 || true

#ip netns exec peer0 $OVPN_CLI tun0 del_peer 1
07070100000034000081A400000000000000000000000165BCF43700008FB1000000000000000000000000000000000000003400000000ovpn-dco-0.2.20240320~git0.2aa7f93/tests/ovpn-cli.c// SPDX-License-Identifier: GPL-2.0
/*  OpenVPN data channel accelerator
 *
 *  Copyright (C) 2020-2023 OpenVPN, Inc.
 *
 *  Author:	Antonio Quartulli <antonio@openvpn.net>
 */

#include <stdio.h>
#include <inttypes.h>
#include <stdbool.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <netinet/in.h>

#include <linux/ovpn_dco.h>
#include <linux/types.h>
#include <linux/netlink.h>

#include <netlink/socket.h>
#include <netlink/netlink.h>
#include <netlink/genl/genl.h>
#include <netlink/genl/family.h>
#include <netlink/genl/ctrl.h>

#include <mbedtls/base64.h>
#include <mbedtls/error.h>

/* libnl < 3.5.0 does not set the NLA_F_NESTED on its own, therefore we
 * have to explicitly do it to prevent the kernel from failing upon
 * parsing of the message
 */
#define nla_nest_start(_msg, _type) \
	nla_nest_start(_msg, (_type) | NLA_F_NESTED)

typedef int (*ovpn_nl_cb)(struct nl_msg *msg, void *arg);

enum ovpn_key_direction {
	KEY_DIR_IN = 0,
	KEY_DIR_OUT,
};

#define KEY_LEN (256 / 8)
#define NONCE_LEN 8

#define PEER_ID_UNDEF 0x00FFFFFF

struct nl_ctx {
	struct nl_sock *nl_sock;
	struct nl_msg *nl_msg;
	struct nl_cb *nl_cb;

	int ovpn_dco_id;
};

struct ovpn_ctx {
	__u8 key_enc[KEY_LEN];
	__u8 key_dec[KEY_LEN];
	__u8 nonce[NONCE_LEN];

	enum ovpn_cipher_alg cipher;

	sa_family_t sa_family;

	__u32 peer_id;
	__u16 lport;

	union {
		struct sockaddr_in in4;
		struct sockaddr_in6 in6;
	} remote;

	union {
		struct sockaddr_in in4;
		struct sockaddr_in6 in6;
	} peer_ip;

	unsigned int ifindex;

	int socket;

	__u32 keepalive_interval;
	__u32 keepalive_timeout;

	enum ovpn_key_direction key_dir;
};

static int ovpn_nl_recvmsgs(struct nl_ctx *ctx)
{
	int ret;

	ret = nl_recvmsgs(ctx->nl_sock, ctx->nl_cb);

	switch (ret) {
	case -NLE_INTR:
		fprintf(stderr,
			"netlink received interrupt due to signal - ignoring\n");
		break;
	case -NLE_NOMEM:
		fprintf(stderr, "netlink out of memory error\n");
		break;
	case -NLE_AGAIN:
		fprintf(stderr,
			"netlink reports blocking read - aborting wait\n");
		break;
	default:
		if (ret)
			fprintf(stderr, "netlink reports error (%d): %s\n",
				ret, nl_geterror(-ret));
		break;
	}

	return ret;
}

static struct nl_ctx *nl_ctx_alloc_flags(struct ovpn_ctx *ovpn,
					 enum ovpn_nl_commands cmd, int flags)
{
	struct nl_ctx *ctx;
	int ret;

	ctx = calloc(1, sizeof(*ctx));
	if (!ctx)
		return NULL;

	ctx->nl_sock = nl_socket_alloc();
	if (!ctx->nl_sock) {
		fprintf(stderr, "cannot allocate netlink socket\n");
		goto err_free;
	}

	nl_socket_set_buffer_size(ctx->nl_sock, 8192, 8192);

	ret = genl_connect(ctx->nl_sock);
	if (ret) {
		fprintf(stderr, "cannot connect to generic netlink: %s\n",
			nl_geterror(ret));
		goto err_sock;
	}

	ctx->ovpn_dco_id = genl_ctrl_resolve(ctx->nl_sock, OVPN_NL_NAME);
	if (ctx->ovpn_dco_id < 0) {
		fprintf(stderr, "cannot find ovpn_dco netlink component: %d\n",
			ctx->ovpn_dco_id);
		goto err_free;
	}

	ctx->nl_msg = nlmsg_alloc();
	if (!ctx->nl_msg) {
		fprintf(stderr, "cannot allocate netlink message\n");
		goto err_sock;
	}

	ctx->nl_cb = nl_cb_alloc(NL_CB_DEFAULT);
	if (!ctx->nl_cb) {
		fprintf(stderr, "failed to allocate netlink callback\n");
		goto err_msg;
	}

	nl_socket_set_cb(ctx->nl_sock, ctx->nl_cb);

	genlmsg_put(ctx->nl_msg, 0, 0, ctx->ovpn_dco_id, 0, flags, cmd, 0);
	NLA_PUT_U32(ctx->nl_msg, OVPN_ATTR_IFINDEX, ovpn->ifindex);

	return ctx;
nla_put_failure:
err_msg:
	nlmsg_free(ctx->nl_msg);
err_sock:
	nl_socket_free(ctx->nl_sock);
err_free:
	free(ctx);
	return NULL;
}

static struct nl_ctx *nl_ctx_alloc(struct ovpn_ctx *ovpn,
				   enum ovpn_nl_commands cmd)
{
	return nl_ctx_alloc_flags(ovpn, cmd, 0);
}

static void nl_ctx_free(struct nl_ctx *ctx)
{
	if (!ctx)
		return;

	nl_socket_free(ctx->nl_sock);
	nlmsg_free(ctx->nl_msg);
	nl_cb_put(ctx->nl_cb);
	free(ctx);
}

static int ovpn_nl_cb_error(struct sockaddr_nl (*nla)__attribute__((unused)),
			    struct nlmsgerr *err, void *arg)
{
	struct nlmsghdr *nlh = (struct nlmsghdr *)err - 1;
	struct nlattr *tb_msg[NLMSGERR_ATTR_MAX + 1];
	int len = nlh->nlmsg_len;
	struct nlattr *attrs;
	int *ret = arg;
	int ack_len = sizeof(*nlh) + sizeof(int) + sizeof(*nlh);

	*ret = err->error;

	if (!(nlh->nlmsg_flags & NLM_F_ACK_TLVS))
		return NL_STOP;

	if (!(nlh->nlmsg_flags & NLM_F_CAPPED))
		ack_len += err->msg.nlmsg_len - sizeof(*nlh);

	if (len <= ack_len)
		return NL_STOP;

	attrs = (void *)((unsigned char *)nlh + ack_len);
	len -= ack_len;

	nla_parse(tb_msg, NLMSGERR_ATTR_MAX, attrs, len, NULL);
	if (tb_msg[NLMSGERR_ATTR_MSG]) {
		len = strnlen((char *)nla_data(tb_msg[NLMSGERR_ATTR_MSG]),
			      nla_len(tb_msg[NLMSGERR_ATTR_MSG]));
		fprintf(stderr, "kernel error: %*s\n", len,
			(char *)nla_data(tb_msg[NLMSGERR_ATTR_MSG]));
	}

	return NL_STOP;
}

static int ovpn_nl_cb_finish(struct nl_msg (*msg)__attribute__((unused)),
			     void *arg)
{
	int *status = arg;

	*status = 0;
	return NL_SKIP;
}

static int ovpn_nl_msg_send(struct nl_ctx *ctx, ovpn_nl_cb cb)
{
	int status = 1;

	nl_cb_err(ctx->nl_cb, NL_CB_CUSTOM, ovpn_nl_cb_error, &status);
	nl_cb_set(ctx->nl_cb, NL_CB_FINISH, NL_CB_CUSTOM, ovpn_nl_cb_finish,
		  &status);
	nl_cb_set(ctx->nl_cb, NL_CB_ACK, NL_CB_CUSTOM, ovpn_nl_cb_finish,
		  &status);

	if (cb)
		nl_cb_set(ctx->nl_cb, NL_CB_VALID, NL_CB_CUSTOM, cb, ctx);

	nl_send_auto_complete(ctx->nl_sock, ctx->nl_msg);

	while (status == 1)
		ovpn_nl_recvmsgs(ctx);

	if (status < 0)
		fprintf(stderr, "failed to send netlink message: %s (%d)\n",
			strerror(-status), status);

	return status;
}

static int ovpn_read_key(const char *file, struct ovpn_ctx *ctx)
{
	int idx_enc, idx_dec, ret = -1;
	unsigned char *ckey = NULL;
	__u8 *bkey = NULL;
	size_t olen = 0;
	long ckey_len;
	FILE *fp;

	fp = fopen(file, "r");
	if (!fp) {
		fprintf(stderr, "cannot open: %s\n", file);
		return -1;
	}

	/* get file size */
	fseek(fp, 0L, SEEK_END);
	ckey_len = ftell(fp);
	rewind(fp);

	/* if the file is longer, let's just read a portion */
	if (ckey_len > 256)
		ckey_len = 256;

	ckey = malloc(ckey_len);
	if (!ckey)
		goto err;

	ret = fread(ckey, 1, ckey_len, fp);
	if (ret != ckey_len) {
		fprintf(stderr,
			"couldn't read enough data from key file: %dbytes read\n",
			ret);
		goto err;
	}

	olen = 0;
	ret = mbedtls_base64_decode(NULL, 0, &olen, ckey, ckey_len);
	if (ret != MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL) {
		char buf[256];

		mbedtls_strerror(ret, buf, sizeof(buf));
		fprintf(stderr, "unexpected base64 error1: %s (%d)\n", buf,
			ret);

		goto err;
	}

	bkey = malloc(olen);
	if (!bkey) {
		fprintf(stderr, "cannot allocate binary key buffer\n");
		goto err;
	}

	ret = mbedtls_base64_decode(bkey, olen, &olen, ckey, ckey_len);
	if (ret) {
		char buf[256];

		mbedtls_strerror(ret, buf, sizeof(buf));
		fprintf(stderr, "unexpected base64 error2: %s (%d)\n", buf,
			ret);

		goto err;
	}

	if (olen < 2 * KEY_LEN + NONCE_LEN) {
		fprintf(stderr,
			"not enough data in key file, found %zdB but needs %dB\n",
			olen, 2 * KEY_LEN + NONCE_LEN);
		goto err;
	}

	switch (ctx->key_dir) {
	case KEY_DIR_IN:
		idx_enc = 0;
		idx_dec = 1;
		break;
	case KEY_DIR_OUT:
		idx_enc = 1;
		idx_dec = 0;
		break;
	}

	memcpy(ctx->key_enc, bkey + KEY_LEN * idx_enc, KEY_LEN);
	memcpy(ctx->key_dec, bkey + KEY_LEN * idx_dec, KEY_LEN);
	memcpy(ctx->nonce, bkey + 2 * KEY_LEN, NONCE_LEN);

	ret = 0;

err:
	fclose(fp);
	free(bkey);
	free(ckey);

	return ret;
}

static int ovpn_read_cipher(const char *cipher, struct ovpn_ctx *ctx)
{
	if (strcmp(cipher, "aes") == 0)
		ctx->cipher = OVPN_CIPHER_ALG_AES_GCM;
	else if (strcmp(cipher, "chachapoly") == 0)
		ctx->cipher = OVPN_CIPHER_ALG_CHACHA20_POLY1305;
	else if (strcmp(cipher, "none") == 0)
		ctx->cipher = OVPN_CIPHER_ALG_NONE;
	else
		return -ENOTSUP;

	return 0;
}

static int ovpn_read_key_direction(const char *dir, struct ovpn_ctx *ctx)
{
	int in_dir;

	in_dir = strtoll(dir, NULL, 10);
	switch (in_dir) {
	case KEY_DIR_IN:
	case KEY_DIR_OUT:
		ctx->key_dir = in_dir;
		break;
	default:
		fprintf(stderr,
			"invalid key direction provided. Can be 0 or 1 only\n");
		return -1;
	}

	return 0;
}

static int ovpn_socket(struct ovpn_ctx *ctx, sa_family_t family, int proto)
{
	struct sockaddr_storage local_sock;
	struct sockaddr_in6 *in6;
	struct sockaddr_in *in;
	int ret, s, sock_type;
	size_t sock_len;

	if (proto == IPPROTO_UDP)
		sock_type = SOCK_DGRAM;
	else if (proto == IPPROTO_TCP)
		sock_type = SOCK_STREAM;
	else
		return -EINVAL;

	s = socket(family, sock_type, 0);
	if (s < 0) {
		perror("cannot create socket");
		return -1;
	}

	memset((char *)&local_sock, 0, sizeof(local_sock));

	switch (family) {
	case AF_INET:
		in = (struct sockaddr_in *)&local_sock;
		in->sin_family = family;
		in->sin_port = htons(ctx->lport);
		in->sin_addr.s_addr = htonl(INADDR_ANY);
		sock_len = sizeof(*in);
		break;
	case AF_INET6:
		in6 = (struct sockaddr_in6 *)&local_sock;
		in6->sin6_family = family;
		in6->sin6_port = htons(ctx->lport);
		in6->sin6_addr = in6addr_any;
		sock_len = sizeof(*in6);
		break;
	default:
		return -1;
	}

	int opt = 1;
	ret = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
	if (ret < 0) {
		perror("setsockopt for SO_REUSEADDR");
		return ret;
	}

	ret = setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt));
	if (ret < 0) {
		perror("setsockopt for SO_REUSEPORT");
		return ret;
	}

	if (family == AF_INET6) {
		opt = 0;
		if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt))) {
			perror("failed to set IPV6_V6ONLY");
			return -1;
		}
	}

	ret = bind(s, (struct sockaddr *)&local_sock, sock_len);
	if (ret < 0) {
		perror("cannot bind socket");
		goto err_socket;
	}

	ctx->socket = s;
	ctx->sa_family = family;
	return 0;

err_socket:
	close(s);
	return -1;
}

static int ovpn_udp_socket(struct ovpn_ctx *ctx, sa_family_t family)
{
	return ovpn_socket(ctx, family, IPPROTO_UDP);
}

static int ovpn_listen(struct ovpn_ctx *ctx, sa_family_t family)
{
	int ret;

	ret = ovpn_socket(ctx, family, IPPROTO_TCP);
	if (ret < 0)
		return ret;

	ret = listen(ctx->socket, 10);
	if (ret < 0) {
		perror("listen");
		close(ctx->socket);
		return -1;
	}

	return 0;
}

static int ovpn_accept(struct ovpn_ctx *ctx)
{
	socklen_t socklen;
	int ret;

	socklen = sizeof(ctx->remote);
	ret = accept(ctx->socket, (struct sockaddr *)&ctx->remote, &socklen);
	if (ret < 0) {
		perror("accept");
		goto err;
	}

	fprintf(stderr, "Connection received!\n");

	switch (socklen) {
	case sizeof(struct sockaddr_in):
	case sizeof(struct sockaddr_in6):
		break;
	default:
		fprintf(stderr, "error: expecting IPv4 or IPv6 connection\n");
		close(ret);
		ret = -EINVAL;
		goto err;
	}

	return ret;
err:
	close(ctx->socket);
	return ret;
}

static int ovpn_connect(struct ovpn_ctx *ovpn)
{
	socklen_t socklen;
	int s, ret;

	s = socket(ovpn->remote.in4.sin_family, SOCK_STREAM, 0);
	if (s < 0) {
		perror("cannot create socket");
		return -1;
	}

	switch (ovpn->remote.in4.sin_family) {
	case AF_INET:
		socklen = sizeof(struct sockaddr_in);
		break;
	case AF_INET6:
		socklen = sizeof(struct sockaddr_in6);
		break;
	default:
		return -EOPNOTSUPP;
	}

	ret = connect(s, (struct sockaddr *)&ovpn->remote, socklen);
	if (ret < 0) {
		perror("connect");
		goto err;
	}

	fprintf(stderr, "connected\n");

	ovpn->socket = s;

	return 0;
err:
	close(s);
	return ret;
}

static int ovpn_new_peer(struct ovpn_ctx *ovpn, bool is_tcp)
{
	struct nlattr *attr;
	struct nl_ctx *ctx;
	size_t alen;
	int ret = -1;

	ctx = nl_ctx_alloc(ovpn, OVPN_CMD_NEW_PEER);
	if (!ctx)
		return -ENOMEM;

	attr = nla_nest_start(ctx->nl_msg, OVPN_ATTR_NEW_PEER);
	NLA_PUT_U32(ctx->nl_msg, OVPN_NEW_PEER_ATTR_PEER_ID, ovpn->peer_id);
	NLA_PUT_U32(ctx->nl_msg, OVPN_NEW_PEER_ATTR_SOCKET, ovpn->socket);

	if (!is_tcp) {
		switch (ovpn->remote.in4.sin_family) {
		case AF_INET:
			alen = sizeof(struct sockaddr_in);
			break;
		case AF_INET6:
			alen = sizeof(struct sockaddr_in6);
			break;
		default:
			fprintf(stderr, "Invalid family for remote socket address\n");
			goto nla_put_failure;
		}
		NLA_PUT(ctx->nl_msg, OVPN_NEW_PEER_ATTR_SOCKADDR_REMOTE, alen, &ovpn->remote);
	}


	switch (ovpn->peer_ip.in4.sin_family) {
	case AF_INET:
		NLA_PUT_U32(ctx->nl_msg, OVPN_NEW_PEER_ATTR_IPV4,
			    ovpn->peer_ip.in4.sin_addr.s_addr);
		break;
	case AF_INET6:
		NLA_PUT(ctx->nl_msg, OVPN_NEW_PEER_ATTR_IPV6, sizeof(struct in6_addr),
			&ovpn->peer_ip.in6.sin6_addr);
		break;
	default:
		fprintf(stderr, "Invalid family for peer address\n");
		goto nla_put_failure;
	}

	nla_nest_end(ctx->nl_msg, attr);

	ret = ovpn_nl_msg_send(ctx, NULL);
nla_put_failure:
	nl_ctx_free(ctx);
	return ret;
}

static int ovpn_set_peer(struct ovpn_ctx *ovpn)
{
	struct nlattr *attr;
	struct nl_ctx *ctx;
	int ret = -1;

	ctx = nl_ctx_alloc(ovpn, OVPN_CMD_SET_PEER);
	if (!ctx)
		return -ENOMEM;

	attr = nla_nest_start(ctx->nl_msg, OVPN_ATTR_SET_PEER);
	NLA_PUT_U32(ctx->nl_msg, OVPN_SET_PEER_ATTR_PEER_ID, ovpn->peer_id);
	NLA_PUT_U32(ctx->nl_msg, OVPN_SET_PEER_ATTR_KEEPALIVE_INTERVAL,
		    ovpn->keepalive_interval);
	NLA_PUT_U32(ctx->nl_msg, OVPN_SET_PEER_ATTR_KEEPALIVE_TIMEOUT,
		    ovpn->keepalive_timeout);
	nla_nest_end(ctx->nl_msg, attr);

	ret = ovpn_nl_msg_send(ctx, NULL);
nla_put_failure:
	nl_ctx_free(ctx);
	return ret;
}

static int ovpn_del_peer(struct ovpn_ctx *ovpn)
{
	struct nlattr *attr;
	struct nl_ctx *ctx;
	int ret = -1;

	ctx = nl_ctx_alloc(ovpn, OVPN_CMD_DEL_PEER);
	if (!ctx)
		return -ENOMEM;

	attr = nla_nest_start(ctx->nl_msg, OVPN_ATTR_DEL_PEER);
	NLA_PUT_U32(ctx->nl_msg, OVPN_DEL_PEER_ATTR_PEER_ID, ovpn->peer_id);
	nla_nest_end(ctx->nl_msg, attr);

	ret = ovpn_nl_msg_send(ctx, NULL);
nla_put_failure:
	nl_ctx_free(ctx);
	return ret;
}

static int ovpn_handle_peer(struct nl_msg *msg, void *arg)
{
	struct nlattr *attrs_peer[OVPN_GET_PEER_RESP_ATTR_MAX + 1];
	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
	struct nlattr *attrs[OVPN_ATTR_MAX + 1];
	__u16 port = 0;

	nla_parse(attrs, OVPN_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
		  genlmsg_attrlen(gnlh, 0), NULL);

	if (!attrs[OVPN_ATTR_GET_PEER]) {
		fprintf(stderr, "no packet content in netlink message\n");
		return NL_SKIP;
	}

	nla_parse(attrs_peer, OVPN_GET_PEER_RESP_ATTR_MAX, nla_data(attrs[OVPN_ATTR_GET_PEER]),
		  nla_len(attrs[OVPN_ATTR_GET_PEER]), NULL);

	if (attrs_peer[OVPN_GET_PEER_RESP_ATTR_PEER_ID])
		fprintf(stderr, "* Peer %u\n",
			nla_get_u32(attrs_peer[OVPN_GET_PEER_RESP_ATTR_PEER_ID]));

	if (attrs_peer[OVPN_GET_PEER_RESP_ATTR_IPV4]) {
		char buf[INET_ADDRSTRLEN];
		inet_ntop(AF_INET, nla_data(attrs_peer[OVPN_GET_PEER_RESP_ATTR_IPV4]), buf,
			  sizeof(buf));
		fprintf(stderr, "\tVPN IPv4: %s\n", buf);
	}

	if (attrs_peer[OVPN_GET_PEER_RESP_ATTR_IPV6]) {
		char buf[INET6_ADDRSTRLEN];
		inet_ntop(AF_INET6, nla_data(attrs_peer[OVPN_GET_PEER_RESP_ATTR_IPV6]), buf,
			  sizeof(buf));
		fprintf(stderr, "\tVPN IPv6: %s\n", buf);
	}

	if (attrs_peer[OVPN_GET_PEER_RESP_ATTR_LOCAL_PORT])
		port = ntohs(nla_get_u16(attrs_peer[OVPN_GET_PEER_RESP_ATTR_LOCAL_PORT]));

	if (attrs_peer[OVPN_GET_PEER_RESP_ATTR_SOCKADDR_REMOTE]) {
		struct sockaddr_storage ss;
		struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)&ss;
		struct sockaddr_in *in = (struct sockaddr_in *)&ss;

		memcpy(&ss, nla_data(attrs_peer[OVPN_GET_PEER_RESP_ATTR_SOCKADDR_REMOTE]),
		       nla_len(attrs_peer[OVPN_GET_PEER_RESP_ATTR_SOCKADDR_REMOTE]));

		if (in->sin_family == AF_INET) {
			char buf[INET_ADDRSTRLEN];

			if (attrs_peer[OVPN_GET_PEER_RESP_ATTR_LOCAL_IP]) {
				inet_ntop(AF_INET,
					  nla_data(attrs_peer[OVPN_GET_PEER_RESP_ATTR_LOCAL_IP]),
					  buf, sizeof(buf));
				fprintf(stderr, "\tLocal: %s:%hu\n", buf, port);
			}

			inet_ntop(AF_INET, &in->sin_addr, buf, sizeof(buf));
			fprintf(stderr, "\tRemote: %s:%u\n", buf, ntohs(in->sin_port));
		} else if (in->sin_family == AF_INET6) {
			char buf[INET6_ADDRSTRLEN];

			if (attrs_peer[OVPN_GET_PEER_RESP_ATTR_LOCAL_IP]) {
				inet_ntop(AF_INET6,
					  nla_data(attrs_peer[OVPN_GET_PEER_RESP_ATTR_LOCAL_IP]),
					  buf, sizeof(buf));
				fprintf(stderr, "\tLocal: %s\n", buf);
			}

			inet_ntop(AF_INET6, &in6->sin6_addr, buf, sizeof(buf));
			fprintf(stderr, "\tRemote: %s:%u (scope-id: %u)\n", buf,
				ntohs(in6->sin6_port), ntohl(in6->sin6_scope_id));
		}
	}

	if (attrs_peer[OVPN_GET_PEER_RESP_ATTR_KEEPALIVE_INTERVAL])
		fprintf(stderr, "\tKeepalive interval: %u sec\n",
			nla_get_u32(attrs_peer[OVPN_GET_PEER_RESP_ATTR_KEEPALIVE_INTERVAL]));

	if (attrs_peer[OVPN_GET_PEER_RESP_ATTR_KEEPALIVE_TIMEOUT])
		fprintf(stderr, "\tKeepalive timeout: %u sec\n",
			nla_get_u32(attrs_peer[OVPN_GET_PEER_RESP_ATTR_KEEPALIVE_TIMEOUT]));

	if (attrs_peer[OVPN_GET_PEER_RESP_ATTR_VPN_RX_BYTES])
		fprintf(stderr, "\tVPN RX bytes: %" PRIu64 "\n",
			nla_get_u64(attrs_peer[OVPN_GET_PEER_RESP_ATTR_VPN_RX_BYTES]));

	if (attrs_peer[OVPN_GET_PEER_RESP_ATTR_VPN_TX_BYTES])
		fprintf(stderr, "\tVPN TX bytes: %" PRIu64 "\n",
			nla_get_u64(attrs_peer[OVPN_GET_PEER_RESP_ATTR_VPN_TX_BYTES]));

	if (attrs_peer[OVPN_GET_PEER_RESP_ATTR_VPN_RX_PACKETS])
		fprintf(stderr, "\tVPN RX packets: %u\n",
			nla_get_u32(attrs_peer[OVPN_GET_PEER_RESP_ATTR_VPN_RX_PACKETS]));

	if (attrs_peer[OVPN_GET_PEER_RESP_ATTR_VPN_TX_PACKETS])
		fprintf(stderr, "\tVPN TX packets: %u\n",
			nla_get_u32(attrs_peer[OVPN_GET_PEER_RESP_ATTR_VPN_TX_PACKETS]));

	if (attrs_peer[OVPN_GET_PEER_RESP_ATTR_LINK_RX_BYTES])
		fprintf(stderr, "\tLINK RX bytes: %" PRIu64 "\n",
			nla_get_u64(attrs_peer[OVPN_GET_PEER_RESP_ATTR_LINK_RX_BYTES]));

	if (attrs_peer[OVPN_GET_PEER_RESP_ATTR_LINK_TX_BYTES])
		fprintf(stderr, "\tLINK TX bytes: %" PRIu64 "\n",
			nla_get_u64(attrs_peer[OVPN_GET_PEER_RESP_ATTR_LINK_TX_BYTES]));

	if (attrs_peer[OVPN_GET_PEER_RESP_ATTR_LINK_RX_PACKETS])
		fprintf(stderr, "\tLINK RX packets: %u\n",
			nla_get_u32(attrs_peer[OVPN_GET_PEER_RESP_ATTR_LINK_RX_PACKETS]));

	if (attrs_peer[OVPN_GET_PEER_RESP_ATTR_LINK_TX_PACKETS])
		fprintf(stderr, "\tLINK TX packets: %u\n",
			nla_get_u32(attrs_peer[OVPN_GET_PEER_RESP_ATTR_LINK_TX_PACKETS]));

	return NL_SKIP;
}

static int ovpn_get_peer(struct ovpn_ctx *ovpn)
{
	int flags = 0, ret = -1;
	struct nlattr *attr;
	struct nl_ctx *ctx;

	if (ovpn->peer_id == PEER_ID_UNDEF)
		flags = NLM_F_DUMP;

	ctx = nl_ctx_alloc_flags(ovpn, OVPN_CMD_GET_PEER, flags);
	if (!ctx)
		return -ENOMEM;

	if (ovpn->peer_id >= 0) {
		attr = nla_nest_start(ctx->nl_msg, OVPN_ATTR_GET_PEER);
		NLA_PUT_U32(ctx->nl_msg, OVPN_GET_PEER_ATTR_PEER_ID, ovpn->peer_id);
		nla_nest_end(ctx->nl_msg, attr);
	}

	ret = ovpn_nl_msg_send(ctx, ovpn_handle_peer);
nla_put_failure:
	nl_ctx_free(ctx);
	return ret;
}

static int ovpn_new_key(struct ovpn_ctx *ovpn)
{
	struct nlattr *attr, *key_dir;
	struct nl_ctx *ctx;
	int ret = -1;

	ctx = nl_ctx_alloc(ovpn, OVPN_CMD_NEW_KEY);
	if (!ctx)
		return -ENOMEM;

	attr = nla_nest_start(ctx->nl_msg, OVPN_ATTR_NEW_KEY);
	NLA_PUT_U32(ctx->nl_msg, OVPN_NEW_KEY_ATTR_PEER_ID, ovpn->peer_id);
	NLA_PUT_U8(ctx->nl_msg, OVPN_NEW_KEY_ATTR_KEY_SLOT, OVPN_KEY_SLOT_PRIMARY);
	NLA_PUT_U8(ctx->nl_msg, OVPN_NEW_KEY_ATTR_KEY_ID, 0);

	NLA_PUT_U16(ctx->nl_msg, OVPN_NEW_KEY_ATTR_CIPHER_ALG, ovpn->cipher);

	key_dir = nla_nest_start(ctx->nl_msg, OVPN_NEW_KEY_ATTR_ENCRYPT_KEY);
	NLA_PUT(ctx->nl_msg, OVPN_KEY_DIR_ATTR_CIPHER_KEY, KEY_LEN,
		ovpn->key_enc);
	NLA_PUT(ctx->nl_msg, OVPN_KEY_DIR_ATTR_NONCE_TAIL, NONCE_LEN,
		ovpn->nonce);
	nla_nest_end(ctx->nl_msg, key_dir);

	key_dir = nla_nest_start(ctx->nl_msg, OVPN_NEW_KEY_ATTR_DECRYPT_KEY);
	NLA_PUT(ctx->nl_msg, OVPN_KEY_DIR_ATTR_CIPHER_KEY, KEY_LEN,
		ovpn->key_dec);
	NLA_PUT(ctx->nl_msg, OVPN_KEY_DIR_ATTR_NONCE_TAIL, NONCE_LEN,
		ovpn->nonce);
	nla_nest_end(ctx->nl_msg, key_dir);
	nla_nest_end(ctx->nl_msg, attr);

	ret = ovpn_nl_msg_send(ctx, NULL);
nla_put_failure:
	nl_ctx_free(ctx);
	return ret;
}

static int ovpn_del_key(struct ovpn_ctx *ovpn)
{
	struct nlattr *attr;
	struct nl_ctx *ctx;
	int ret = -1;

	ctx = nl_ctx_alloc(ovpn, OVPN_CMD_DEL_KEY);
	if (!ctx)
		return -ENOMEM;

	attr = nla_nest_start(ctx->nl_msg, OVPN_ATTR_DEL_KEY);
	NLA_PUT_U32(ctx->nl_msg, OVPN_DEL_KEY_ATTR_PEER_ID, ovpn->peer_id);
	NLA_PUT_U8(ctx->nl_msg, OVPN_DEL_KEY_ATTR_KEY_SLOT, OVPN_KEY_SLOT_PRIMARY);
	nla_nest_end(ctx->nl_msg, attr);

	ret = ovpn_nl_msg_send(ctx, NULL);
nla_put_failure:
	nl_ctx_free(ctx);
	return ret;
}

static int ovpn_swap_keys(struct ovpn_ctx *ovpn)
{
	struct nlattr *attr;
	struct nl_ctx *ctx;
	int ret = -1;

	ctx = nl_ctx_alloc(ovpn, OVPN_CMD_SWAP_KEYS);
	if (!ctx)
		return -ENOMEM;

	attr = nla_nest_start(ctx->nl_msg, OVPN_ATTR_SWAP_KEYS);
	NLA_PUT_U32(ctx->nl_msg, OVPN_SWAP_KEYS_ATTR_PEER_ID, ovpn->peer_id);
	nla_nest_end(ctx->nl_msg, attr);

	ret = ovpn_nl_msg_send(ctx, NULL);
nla_put_failure:
	nl_ctx_free(ctx);
	return ret;
}

static int nl_seq_check(struct nl_msg *msg, void *arg)
{
	return NL_OK;
}

struct mcast_handler_args {
	const char *group;
	int id;
};

static int mcast_family_handler(struct nl_msg *msg, void *arg)
{
	struct mcast_handler_args *grp = arg;
	struct nlattr *tb[CTRL_ATTR_MAX + 1];
	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
	struct nlattr *mcgrp;
	int rem_mcgrp;

	nla_parse(tb, CTRL_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
		  genlmsg_attrlen(gnlh, 0), NULL);

	if (!tb[CTRL_ATTR_MCAST_GROUPS])
		return NL_SKIP;

	nla_for_each_nested(mcgrp, tb[CTRL_ATTR_MCAST_GROUPS], rem_mcgrp) {
		struct nlattr *tb_mcgrp[CTRL_ATTR_MCAST_GRP_MAX + 1];

		nla_parse(tb_mcgrp, CTRL_ATTR_MCAST_GRP_MAX,
			  nla_data(mcgrp), nla_len(mcgrp), NULL);

		if (!tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME] ||
		    !tb_mcgrp[CTRL_ATTR_MCAST_GRP_ID])
			continue;
		if (strncmp(nla_data(tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME]),
			    grp->group, nla_len(tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME])))
			continue;
		grp->id = nla_get_u32(tb_mcgrp[CTRL_ATTR_MCAST_GRP_ID]);
		break;
	}

	return NL_SKIP;
}

static int mcast_error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err,
			       void *arg)
{
	int *ret = arg;

	*ret = err->error;
	return NL_STOP;
}

static int mcast_ack_handler(struct nl_msg *msg, void *arg)
{
	int *ret = arg;

	*ret = 0;
	return NL_STOP;
}

static int ovpn_handle_msg(struct nl_msg *msg, void *arg)
{
	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
	struct nlattr *attrs[OVPN_ATTR_MAX + 1];
	struct nlmsghdr *nlh = nlmsg_hdr(msg);
	//enum ovpn_del_peer_reason reason;
	char ifname[IF_NAMESIZE];
	__u32 ifindex;

	fprintf(stderr, "received message from ovpn-dco\n");

	if (!genlmsg_valid_hdr(nlh, 0)) {
		fprintf(stderr, "invalid header\n");
		return NL_STOP;
	}

	if (nla_parse(attrs, OVPN_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
		      genlmsg_attrlen(gnlh, 0), NULL)) {
		fprintf(stderr, "received bogus data from ovpn-dco\n");
		return NL_STOP;
	}

	if (!attrs[OVPN_ATTR_IFINDEX]) {
		fprintf(stderr, "no ifindex in this message\n");
		return NL_STOP;
	}

	ifindex = nla_get_u32(attrs[OVPN_ATTR_IFINDEX]);
	if (!if_indextoname(ifindex, ifname)) {
		fprintf(stderr, "cannot resolve ifname for ifinxed: %u\n",
			ifindex);
		return NL_STOP;
	}

	switch (gnlh->cmd) {
	case OVPN_CMD_DEL_PEER:
		/*if (!attrs[OVPN_ATTR_DEL_PEER_REASON]) {
			fprintf(stderr, "no reason in DEL_PEER message\n");
			return NL_STOP;
		}
		reason = nla_get_u8(attrs[OVPN_ATTR_DEL_PEER_REASON]);
		fprintf(stderr,
			"received CMD_DEL_PEER, ifname: %s reason: %d\n",
			ifname, reason);
		*/
		fprintf(stdout, "received CMD_DEL_PEER\n");
		break;
	default:
		fprintf(stderr, "received unknown command: %d\n", gnlh->cmd);
		return NL_STOP;
	}

	return NL_OK;
}

static int ovpn_get_mcast_id(struct nl_sock *sock, const char *family,
			     const char *group)
{
	struct nl_msg *msg;
	struct nl_cb *cb;
	int ret, ctrlid;
	struct mcast_handler_args grp = {
		.group = group,
		.id = -ENOENT,
	};

	msg = nlmsg_alloc();
	if (!msg)
		return -ENOMEM;

	cb = nl_cb_alloc(NL_CB_DEFAULT);
	if (!cb) {
		ret = -ENOMEM;
		goto out_fail_cb;
	}

	ctrlid = genl_ctrl_resolve(sock, "nlctrl");

	genlmsg_put(msg, 0, 0, ctrlid, 0, 0, CTRL_CMD_GETFAMILY, 0);

	ret = -ENOBUFS;
	NLA_PUT_STRING(msg, CTRL_ATTR_FAMILY_NAME, family);

	ret = nl_send_auto_complete(sock, msg);
	if (ret < 0)
		goto nla_put_failure;

	ret = 1;

	nl_cb_err(cb, NL_CB_CUSTOM, mcast_error_handler, &ret);
	nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, mcast_ack_handler, &ret);
	nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, mcast_family_handler, &grp);

	while (ret > 0)
		nl_recvmsgs(sock, cb);

	if (ret == 0)
		ret = grp.id;
 nla_put_failure:
	nl_cb_put(cb);
 out_fail_cb:
	nlmsg_free(msg);
	return ret;
}

static void ovpn_listen_mcast(void)
{
	struct nl_sock *sock;
	struct nl_cb *cb;
	int mcid, ret;

	sock = nl_socket_alloc();
	if (!sock) {
		fprintf(stderr, "cannot allocate netlink socket\n");
		goto err_free;
	}

	nl_socket_set_buffer_size(sock, 8192, 8192);

	ret = genl_connect(sock);
	if (ret < 0) {
		fprintf(stderr, "cannot connect to generic netlink: %s\n",
			nl_geterror(ret));
		goto err_free;
	}

	mcid = ovpn_get_mcast_id(sock, OVPN_NL_NAME,
				 OVPN_NL_MULTICAST_GROUP_PEERS);
	if (mcid < 0) {
		fprintf(stderr, "cannot get mcast group: %s\n",
			nl_geterror(mcid));
		goto err_free;
	}

	ret = nl_socket_add_membership(sock, mcid);
	if (ret) {
		fprintf(stderr, "failed to join mcast group: %d\n", ret);
		goto err_free;
	}

	ret = 0;
	cb = nl_cb_alloc(NL_CB_DEFAULT);
	nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, nl_seq_check, NULL);
	nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, ovpn_handle_msg, &ret);
	nl_cb_err(cb, NL_CB_CUSTOM, ovpn_nl_cb_error, &ret);

	while (ret != -EINTR)
		ret = nl_recvmsgs(sock, cb);

	nl_cb_put(cb);
err_free:
	nl_socket_free(sock);
}

static void usage(const char *cmd)
{
	fprintf(stderr, "Error: invalid arguments.\n\n");
	fprintf(stderr,
		"Usage %s <iface> <connect|listen|new_peer|new_multi_peer|set_peer|del_peer|new_key|del_key|recv|send|listen_mcast> [arguments..]\n",
		cmd);
	fprintf(stderr, "\tiface: tun interface name\n\n");

	fprintf(stderr, "* connect <peer_id> <raddr> <rport> <vpnaddr>: start connecting peer of TCP-based VPN session\n");
	fprintf(stderr, "\tpeer-id: peer ID of the connecting peer\n");
	fprintf(stderr, "\tremote-addr: peer IP address\n");
	fprintf(stderr, "\tremote-port: peer TCP port\n");
	fprintf(stderr, "\tvpn-ip: peer VPN IP\n\n");

	fprintf(stderr, "* listen <lport> <peers_file>: listen for incoming peer TCP connections\n");
	fprintf(stderr, "\tlport: src TCP port\n");
	fprintf(stderr, "\tpeers_file: file containing one peer per line: Line format:\n");
	fprintf(stderr, "\t\t<peer_id> <vpnaddr>\n\n");

	fprintf(stderr, "* new_peer <lport> <peer-id> <raddr> <rport> <vpnaddr>: add new peer\n");
	fprintf(stderr, "\tpeer-id: peer ID to be used in data packets to/from this peer\n");
	fprintf(stderr, "\tlocal-port: local UDP port\n");
	fprintf(stderr, "\tremote-addr: peer IP address\n");
	fprintf(stderr, "\tremote-port: peer UDP port\n");
	fprintf(stderr, "\tvpnaddr: peer VPN IP\n\n");

	fprintf(stderr, "* new_multi_peer <lport> <file>: add multiple peers as listed in the file\n");
	fprintf(stderr, "\tlport: local UDP port to bind to\n");
	fprintf(stderr, "\tfile: text file containing one peer per line. Line format:\n");
	fprintf(stderr, "\t\t<peer-id> <raddr> <rport> <vpnaddr>\n\n");

	fprintf(stderr,
		"* set_peer <peer-id> <keepalive_interval> <keepalive_timeout>: set peer attributes\n");
	fprintf(stderr, "\tpeer-id: peer ID of the peer to modify\n");
	fprintf(stderr,
		"\tkeepalive_interval: interval for sending ping messages\n");
	fprintf(stderr,
		"\tkeepalive_timeout: time after which a peer is timed out\n\n");

	fprintf(stderr, "* del_peer <peer-id>: delete peer\n");
	fprintf(stderr, "\tpeer-id: peer ID of the peer to delete\n\n");

	fprintf(stderr,
		"* new_key <peer-id> <cipher> <key_dir> <key_file>: set data channel key\n");
	fprintf(stderr, "\tpeer-id: peer ID of the peer to configure the key for\n");
	fprintf(stderr,
		"\tcipher: cipher to use, supported: aes (AES-GCM), chachapoly (CHACHA20POLY1305), none\n");
	fprintf(stderr,
		"\tkey_dir: key direction, must 0 on one host and 1 on the other\n");
	fprintf(stderr, "\tkey_file: file containing the pre-shared key\n\n");

	fprintf(stderr, "* del_key <peer-id>: erase existing data channel key\n");
	fprintf(stderr, "\tpeer-id: peer ID of the peer to modify\n\n");

	fprintf(stderr, "* swap_keys <peer-id>: swap primary and seconday key slots\n");
	fprintf(stderr, "\tpeer-id: peer ID of the peer to modify\n\n");

	fprintf(stderr, "* listen_mcast: listen to ovpn-dco netlink multicast messages\n");
}

static int ovpn_parse_remote(struct ovpn_ctx *ovpn, const char *host, const char *service,
			     const char *vpn_addr)
{
	int ret;
	struct addrinfo *result;
	struct addrinfo hints = {
		.ai_family = ovpn->sa_family,
		.ai_socktype = SOCK_DGRAM,
		.ai_protocol = IPPROTO_UDP
	};

	if (host) {
		ret = getaddrinfo(host, service, &hints, &result);
		if (ret == EAI_NONAME || ret == EAI_FAIL)
			return -1;

		if (!(result->ai_family == AF_INET && result->ai_addrlen == sizeof(struct sockaddr_in)) &&
		    !(result->ai_family == AF_INET6 && result->ai_addrlen == sizeof(struct sockaddr_in6))) {
			ret = -EINVAL;
			goto out;
		}

		memcpy(&ovpn->remote, result->ai_addr, result->ai_addrlen);
	}

	ret = getaddrinfo(vpn_addr, NULL, &hints, &result);
	if (ret == EAI_NONAME || ret == EAI_FAIL)
		return -1;

	if (!(result->ai_family == AF_INET && result->ai_addrlen == sizeof(struct sockaddr_in)) &&
	    !(result->ai_family == AF_INET6 && result->ai_addrlen == sizeof(struct sockaddr_in6))) {
		ret = -EINVAL;
		goto out;
	}

	memcpy(&ovpn->peer_ip, result->ai_addr, result->ai_addrlen);
	ovpn->sa_family = result->ai_family;

	ret = 0;
out:
	freeaddrinfo(result);
	return ret;
}

static int ovpn_parse_new_peer(struct ovpn_ctx *ovpn, const char *peer_id, const char *raddr,
			       const char *rport, const char *vpnip)
{
	ovpn->peer_id = strtoul(peer_id, NULL, 10);
	if (errno == ERANGE) {
		fprintf(stderr, "peer ID value out of range\n");
		return -1;
	}

	return ovpn_parse_remote(ovpn, raddr, rport, vpnip);
}

static int ovpn_parse_set_peer(struct ovpn_ctx *ovpn, int argc, char *argv[])
{
	if (argc < 5) {
		usage(argv[0]);
		return -1;
	}

	ovpn->keepalive_interval = strtoul(argv[3], NULL, 10);
	if (errno == ERANGE) {
		fprintf(stderr, "keepalive interval value out of range\n");
		return -1;
	}

	ovpn->keepalive_timeout = strtoul(argv[4], NULL, 10);
	if (errno == ERANGE) {
		fprintf(stderr, "keepalive interval value out of range\n");
		return -1;
	}

	return 0;
}

int main(int argc, char *argv[])
{
	struct ovpn_ctx ovpn;
//	struct nl_ctx *ctx;
	int ret;

	if (argc < 3) {
		usage(argv[0]);
		return -1;
	}

	memset(&ovpn, 0, sizeof(ovpn));
	ovpn.sa_family = AF_INET;

	ovpn.ifindex = if_nametoindex(argv[1]);
	if (!ovpn.ifindex) {
		fprintf(stderr, "cannot find interface: %s\n",
			strerror(errno));
		return -1;
	}

	if (!strcmp(argv[2], "listen")) {
		char peer_id[10], vpnip[100];
		int n;
		FILE *fp;

		if (argc < 4) {
			usage(argv[0]);
			return -1;
		}

		ovpn.lport = strtoul(argv[3], NULL, 10);
		if (errno == ERANGE || ovpn.lport > 65535) {
			fprintf(stderr, "lport value out of range\n");
			return -1;
		}

		if (argc > 4 && !strcmp(argv[4], "ipv6"))
			ovpn.sa_family = AF_INET6;

		ret = ovpn_listen(&ovpn, ovpn.sa_family);
		if (ret < 0) {
			fprintf(stderr, "cannot listen on TCP socket\n");
			return ret;
		}

		fp = fopen(argv[4], "r");
		if (!fp) {
			fprintf(stderr, "cannot open file: %s\n", argv[4]);
			return -1;
		}

		while ((n = fscanf(fp, "%s %s\n", peer_id, vpnip)) == 2) {
			struct ovpn_ctx peer_ctx = { 0 };

			peer_ctx.ifindex = if_nametoindex(argv[1]);
			peer_ctx.sa_family = ovpn.sa_family;

			peer_ctx.socket = ovpn_accept(&ovpn);
			if (peer_ctx.socket < 0) {
				fprintf(stderr, "cannot accept connection!\n");
				return -1;
			}

			ret = ovpn_parse_new_peer(&peer_ctx, peer_id, NULL, NULL, vpnip);
			if (ret < 0) {
				fprintf(stderr, "error while parsing line\n");
				return -1;
			}

			ret = ovpn_new_peer(&peer_ctx, true);
			if (ret < 0) {
				fprintf(stderr, "cannot add peer to VPN: %s %s\n", peer_id, vpnip);
				return ret;
			}
		}
	} else if (!strcmp(argv[2], "connect")) {
		if (argc < 6) {
			usage(argv[0]);
			return -1;
		}

		ovpn.sa_family = AF_INET;

		ret = ovpn_parse_new_peer(&ovpn, argv[3], argv[4], argv[5], argv[6]);
		if (ret < 0) {
			fprintf(stderr, "Cannot parse remote peer data\n");
			return ret;
		}

		ret = ovpn_connect(&ovpn);
		if (ret < 0) {
			fprintf(stderr, "cannot connect TCP socket\n");
			return ret;
		}

		ret = ovpn_new_peer(&ovpn, true);
		if (ret < 0) {
			fprintf(stderr, "cannot add peer to VPN\n");
			close(ovpn.socket);
			return ret;
		}
	} else if (!strcmp(argv[2], "new_peer")) {
		if (argc < 8) {
			usage(argv[0]);
			return -1;
		}

		ovpn.lport = strtoul(argv[3], NULL, 10);
		if (errno == ERANGE || ovpn.lport > 65535) {
			fprintf(stderr, "lport value out of range\n");
			return -1;
		}

		ret = ovpn_parse_new_peer(&ovpn, argv[4], argv[5], argv[6], argv[7]);
		if (ret < 0)
			return ret;

		ret = ovpn_udp_socket(&ovpn, AF_INET6);//ovpn.sa_family);
		if (ret < 0)
			return ret;

		ret = ovpn_new_peer(&ovpn, false);
		if (ret < 0) {
			fprintf(stderr, "cannot add peer to VPN\n");
			return ret;
		}
	} else if (!strcmp(argv[2], "new_multi_peer")) {
		char peer_id[10], raddr[128], rport[10], vpnip[100];
		FILE *fp;
		int n;

		if (argc < 5) {
			usage(argv[0]);
			return -1;
		}

		ovpn.lport = strtoul(argv[3], NULL, 10);
		if (errno == ERANGE || ovpn.lport > 65535) {
			fprintf(stderr, "lport value out of range\n");
			return -1;
		}

		fp = fopen(argv[4], "r");
		if (!fp) {
			fprintf(stderr, "cannot open file: %s\n", argv[4]);
			return -1;
		}

		ret = ovpn_udp_socket(&ovpn, AF_INET6);
		if (ret < 0)
			return ret;

		while ((n = fscanf(fp, "%s %s %s %s\n", peer_id, raddr, rport, vpnip)) == 4) {
			struct ovpn_ctx peer_ctx = { 0 };

			peer_ctx.ifindex = if_nametoindex(argv[1]);
			peer_ctx.socket = ovpn.socket;
			peer_ctx.sa_family = AF_UNSPEC;

			ret = ovpn_parse_new_peer(&peer_ctx, peer_id, raddr, rport, vpnip);
			if (ret < 0) {
				fprintf(stderr, "error while parsing line\n");
				return -1;
			}

			ret = ovpn_new_peer(&peer_ctx, false);
			if (ret < 0) {
				fprintf(stderr, "cannot add peer to VPN: %s %s %s %s\n", peer_id,
					raddr, rport, vpnip);
				return ret;
			}
		}
	} else if (!strcmp(argv[2], "set_peer")) {
		ovpn.peer_id = strtoul(argv[3], NULL, 10);
		if (errno == ERANGE) {
			fprintf(stderr, "peer ID value out of range\n");
			return -1;
		}

		argv++;
		argc--;

		ret = ovpn_parse_set_peer(&ovpn, argc, argv);
		if (ret < 0)
			return ret;

		ret = ovpn_set_peer(&ovpn);
		if (ret < 0) {
			fprintf(stderr, "cannot set peer to VPN\n");
			return ret;
		}
	} else if (!strcmp(argv[2], "del_peer")) {
		if (argc < 4) {
			usage(argv[0]);
			return -1;
		}

		ovpn.peer_id = strtoul(argv[3], NULL, 10);
		if (errno == ERANGE) {
			fprintf(stderr, "peer ID value out of range\n");
			return -1;
		}

		ret = ovpn_del_peer(&ovpn);
		if (ret < 0) {
			fprintf(stderr, "cannot delete peer to VPN\n");
			return ret;
		}
	} else if (!strcmp(argv[2], "get_peer")) {
		ovpn.peer_id = PEER_ID_UNDEF;
		if (argc > 3)
			ovpn.peer_id = strtoul(argv[3], NULL, 10);

		fprintf(stderr, "List of peers connected to: %s\n", argv[1]);

		ret = ovpn_get_peer(&ovpn);
		if (ret < 0) {
			fprintf(stderr, "cannot get peer(s): %d\n", ret);
			return ret;
		}
	} else if (!strcmp(argv[2], "new_key")) {
		if (argc < 6) {
			usage(argv[0]);
			return -1;
		}

		ovpn.peer_id = strtoul(argv[3], NULL, 10);
		if (errno == ERANGE) {
			fprintf(stderr, "peer ID value out of range\n");
			return -1;
		}

		ret = ovpn_read_cipher(argv[4], &ovpn);
		if (ret < 0)
			return ret;

		ret = ovpn_read_key_direction(argv[5], &ovpn);
		if (ret < 0)
			return ret;

		ret = ovpn_read_key(argv[6], &ovpn);
		if (ret)
			return ret;

		ret = ovpn_new_key(&ovpn);
		if (ret < 0) {
			fprintf(stderr, "cannot set key\n");
			return ret;
		}
	} else if (!strcmp(argv[2], "del_key")) {
		if (argc < 3) {
			usage(argv[0]);
			return -1;
		}

		ovpn.peer_id = strtoul(argv[3], NULL, 10);
		if (errno == ERANGE) {
			fprintf(stderr, "peer ID value out of range\n");
			return -1;
		}

		argv++;
		argc--;

		ret = ovpn_del_key(&ovpn);
		if (ret < 0) {
			fprintf(stderr, "cannot delete key\n");
			return ret;
		}
	} else if (!strcmp(argv[2], "swap_keys")) {
		if (argc < 3) {
			usage(argv[0]);
			return -1;
		}

		ovpn.peer_id = strtoul(argv[3], NULL, 10);
		if (errno == ERANGE) {
			fprintf(stderr, "peer ID value out of range\n");
			return -1;
		}

		argv++;
		argc--;

		ret = ovpn_swap_keys(&ovpn);
		if (ret < 0) {
			fprintf(stderr, "cannot swap keys\n");
			return ret;
		}
	} else if (!strcmp(argv[2], "listen_mcast")) {
		ovpn_listen_mcast();
	} else {
		usage(argv[0]);
		return -1;
	}

	return ret;
}
07070100000035000041ED00000000000000000000000265BCF43700000000000000000000000000000000000000000000002E00000000ovpn-dco-0.2.20240320~git0.2aa7f93/tests/qemu07070100000036000081A400000000000000000000000165BCF43700012DA0000000000000000000000000000000000000003E00000000ovpn-dco-0.2.20240320~git0.2aa7f93/tests/qemu/config.net-next#
# Automatically generated file; DO NOT EDIT.
# Linux/x86 5.7.0-rc1 Kernel Configuration
#

#
# Compiler: gcc (Gentoo 9.2.0-r2 p3) 9.2.0
#
CONFIG_CC_IS_GCC=y
CONFIG_GCC_VERSION=90200
CONFIG_LD_VERSION=233010000
CONFIG_CLANG_VERSION=0
CONFIG_CC_CAN_LINK=y
CONFIG_CC_HAS_ASM_GOTO=y
CONFIG_CC_HAS_ASM_INLINE=y
CONFIG_CC_HAS_WARN_MAYBE_UNINITIALIZED=y
CONFIG_CONSTRUCTORS=y
CONFIG_IRQ_WORK=y
CONFIG_BUILDTIME_TABLE_SORT=y
CONFIG_THREAD_INFO_IN_TASK=y

#
# General setup
#
CONFIG_INIT_ENV_ARG_LIMIT=32
# CONFIG_COMPILE_TEST is not set
CONFIG_LOCALVERSION=""
CONFIG_LOCALVERSION_AUTO=y
CONFIG_BUILD_SALT=""
CONFIG_HAVE_KERNEL_GZIP=y
CONFIG_HAVE_KERNEL_BZIP2=y
CONFIG_HAVE_KERNEL_LZMA=y
CONFIG_HAVE_KERNEL_XZ=y
CONFIG_HAVE_KERNEL_LZO=y
CONFIG_HAVE_KERNEL_LZ4=y
# CONFIG_KERNEL_GZIP is not set
# CONFIG_KERNEL_BZIP2 is not set
# CONFIG_KERNEL_LZMA is not set
CONFIG_KERNEL_XZ=y
# CONFIG_KERNEL_LZO is not set
# CONFIG_KERNEL_LZ4 is not set
CONFIG_DEFAULT_HOSTNAME="emu0"
# CONFIG_SWAP is not set
CONFIG_SYSVIPC=y
CONFIG_SYSVIPC_SYSCTL=y
CONFIG_POSIX_MQUEUE=y
CONFIG_POSIX_MQUEUE_SYSCTL=y
CONFIG_CROSS_MEMORY_ATTACH=y
CONFIG_USELIB=y
# CONFIG_AUDIT is not set
CONFIG_HAVE_ARCH_AUDITSYSCALL=y

#
# IRQ subsystem
#
CONFIG_GENERIC_IRQ_PROBE=y
CONFIG_GENERIC_IRQ_SHOW=y
CONFIG_GENERIC_IRQ_EFFECTIVE_AFF_MASK=y
CONFIG_GENERIC_PENDING_IRQ=y
CONFIG_GENERIC_IRQ_MIGRATION=y
CONFIG_HARDIRQS_SW_RESEND=y
CONFIG_IRQ_DOMAIN=y
CONFIG_IRQ_DOMAIN_HIERARCHY=y
CONFIG_GENERIC_MSI_IRQ=y
CONFIG_GENERIC_MSI_IRQ_DOMAIN=y
CONFIG_GENERIC_IRQ_MATRIX_ALLOCATOR=y
CONFIG_GENERIC_IRQ_RESERVATION_MODE=y
CONFIG_IRQ_FORCED_THREADING=y
CONFIG_SPARSE_IRQ=y
# CONFIG_GENERIC_IRQ_DEBUGFS is not set
# end of IRQ subsystem

CONFIG_CLOCKSOURCE_WATCHDOG=y
CONFIG_ARCH_CLOCKSOURCE_INIT=y
CONFIG_CLOCKSOURCE_VALIDATE_LAST_CYCLE=y
CONFIG_GENERIC_TIME_VSYSCALL=y
CONFIG_GENERIC_CLOCKEVENTS=y
CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y
CONFIG_GENERIC_CLOCKEVENTS_MIN_ADJUST=y
CONFIG_GENERIC_CMOS_UPDATE=y

#
# Timers subsystem
#
CONFIG_TICK_ONESHOT=y
CONFIG_NO_HZ_COMMON=y
# CONFIG_HZ_PERIODIC is not set
# CONFIG_NO_HZ_IDLE is not set
CONFIG_NO_HZ_FULL=y
CONFIG_CONTEXT_TRACKING=y
# CONFIG_CONTEXT_TRACKING_FORCE is not set
# CONFIG_NO_HZ is not set
CONFIG_HIGH_RES_TIMERS=y
# end of Timers subsystem

# CONFIG_PREEMPT_NONE is not set
# CONFIG_PREEMPT_VOLUNTARY is not set
CONFIG_PREEMPT=y
CONFIG_PREEMPT_COUNT=y
CONFIG_PREEMPTION=y

#
# CPU/Task time and stats accounting
#
CONFIG_VIRT_CPU_ACCOUNTING=y
CONFIG_VIRT_CPU_ACCOUNTING_GEN=y
# CONFIG_IRQ_TIME_ACCOUNTING is not set
# CONFIG_SCHED_THERMAL_PRESSURE is not set
# CONFIG_BSD_PROCESS_ACCT is not set
# CONFIG_TASKSTATS is not set
# CONFIG_PSI is not set
# end of CPU/Task time and stats accounting

CONFIG_CPU_ISOLATION=y

#
# RCU Subsystem
#
CONFIG_TREE_RCU=y
CONFIG_PREEMPT_RCU=y
# CONFIG_RCU_EXPERT is not set
CONFIG_SRCU=y
CONFIG_TREE_SRCU=y
CONFIG_TASKS_RCU=y
CONFIG_RCU_STALL_COMMON=y
CONFIG_RCU_NEED_SEGCBLIST=y
CONFIG_RCU_NOCB_CPU=y
# end of RCU Subsystem

# CONFIG_IKCONFIG is not set
# CONFIG_IKHEADERS is not set
CONFIG_LOG_BUF_SHIFT=18
CONFIG_LOG_CPU_MAX_BUF_SHIFT=12
CONFIG_PRINTK_SAFE_LOG_BUF_SHIFT=13
CONFIG_HAVE_UNSTABLE_SCHED_CLOCK=y

#
# Scheduler features
#
# CONFIG_UCLAMP_TASK is not set
# end of Scheduler features

CONFIG_ARCH_SUPPORTS_NUMA_BALANCING=y
CONFIG_ARCH_WANT_BATCHED_UNMAP_TLB_FLUSH=y
CONFIG_CC_HAS_INT128=y
CONFIG_ARCH_SUPPORTS_INT128=y
# CONFIG_NUMA_BALANCING is not set
CONFIG_CGROUPS=y
# CONFIG_MEMCG is not set
# CONFIG_BLK_CGROUP is not set
# CONFIG_CGROUP_SCHED is not set
# CONFIG_CGROUP_PIDS is not set
# CONFIG_CGROUP_RDMA is not set
# CONFIG_CGROUP_FREEZER is not set
# CONFIG_CGROUP_HUGETLB is not set
# CONFIG_CPUSETS is not set
# CONFIG_CGROUP_DEVICE is not set
# CONFIG_CGROUP_CPUACCT is not set
# CONFIG_CGROUP_PERF is not set
# CONFIG_CGROUP_DEBUG is not set
CONFIG_NAMESPACES=y
CONFIG_UTS_NS=y
CONFIG_TIME_NS=y
CONFIG_IPC_NS=y
CONFIG_USER_NS=y
CONFIG_PID_NS=y
CONFIG_NET_NS=y
# CONFIG_CHECKPOINT_RESTORE is not set
# CONFIG_SCHED_AUTOGROUP is not set
# CONFIG_SYSFS_DEPRECATED is not set
CONFIG_RELAY=y
# CONFIG_BLK_DEV_INITRD is not set
# CONFIG_BOOT_CONFIG is not set
CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE=y
# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
CONFIG_SYSCTL=y
CONFIG_SYSCTL_EXCEPTION_TRACE=y
CONFIG_HAVE_PCSPKR_PLATFORM=y
CONFIG_BPF=y
# CONFIG_EXPERT is not set
CONFIG_MULTIUSER=y
CONFIG_SGETMASK_SYSCALL=y
CONFIG_SYSFS_SYSCALL=y
CONFIG_FHANDLE=y
CONFIG_POSIX_TIMERS=y
CONFIG_PRINTK=y
CONFIG_PRINTK_NMI=y
CONFIG_BUG=y
CONFIG_ELF_CORE=y
CONFIG_PCSPKR_PLATFORM=y
CONFIG_BASE_FULL=y
CONFIG_FUTEX=y
CONFIG_FUTEX_PI=y
CONFIG_EPOLL=y
CONFIG_SIGNALFD=y
CONFIG_TIMERFD=y
CONFIG_EVENTFD=y
CONFIG_SHMEM=y
CONFIG_AIO=y
CONFIG_IO_URING=y
CONFIG_ADVISE_SYSCALLS=y
CONFIG_MEMBARRIER=y
CONFIG_KALLSYMS=y
CONFIG_KALLSYMS_ALL=y
CONFIG_KALLSYMS_ABSOLUTE_PERCPU=y
CONFIG_KALLSYMS_BASE_RELATIVE=y
# CONFIG_BPF_SYSCALL is not set
CONFIG_ARCH_WANT_DEFAULT_BPF_JIT=y
# CONFIG_USERFAULTFD is not set
CONFIG_ARCH_HAS_MEMBARRIER_SYNC_CORE=y
CONFIG_RSEQ=y
# CONFIG_EMBEDDED is not set
CONFIG_HAVE_PERF_EVENTS=y

#
# Kernel Performance Events And Counters
#
CONFIG_PERF_EVENTS=y
# CONFIG_DEBUG_PERF_USE_VMALLOC is not set
# end of Kernel Performance Events And Counters

CONFIG_VM_EVENT_COUNTERS=y
CONFIG_SLUB_DEBUG=y
CONFIG_COMPAT_BRK=y
# CONFIG_SLAB is not set
CONFIG_SLUB=y
CONFIG_SLAB_MERGE_DEFAULT=y
# CONFIG_SLAB_FREELIST_RANDOM is not set
# CONFIG_SLAB_FREELIST_HARDENED is not set
# CONFIG_SHUFFLE_PAGE_ALLOCATOR is not set
CONFIG_SLUB_CPU_PARTIAL=y
# CONFIG_PROFILING is not set
CONFIG_TRACEPOINTS=y
# end of General setup

CONFIG_64BIT=y
CONFIG_X86_64=y
CONFIG_X86=y
CONFIG_INSTRUCTION_DECODER=y
CONFIG_OUTPUT_FORMAT="elf64-x86-64"
CONFIG_LOCKDEP_SUPPORT=y
CONFIG_STACKTRACE_SUPPORT=y
CONFIG_MMU=y
CONFIG_ARCH_MMAP_RND_BITS_MIN=28
CONFIG_ARCH_MMAP_RND_BITS_MAX=32
CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MIN=8
CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MAX=16
CONFIG_GENERIC_ISA_DMA=y
CONFIG_GENERIC_BUG=y
CONFIG_GENERIC_BUG_RELATIVE_POINTERS=y
CONFIG_ARCH_MAY_HAVE_PC_FDC=y
CONFIG_GENERIC_CALIBRATE_DELAY=y
CONFIG_ARCH_HAS_CPU_RELAX=y
CONFIG_ARCH_HAS_CACHE_LINE_SIZE=y
CONFIG_ARCH_HAS_FILTER_PGPROT=y
CONFIG_HAVE_SETUP_PER_CPU_AREA=y
CONFIG_NEED_PER_CPU_EMBED_FIRST_CHUNK=y
CONFIG_NEED_PER_CPU_PAGE_FIRST_CHUNK=y
CONFIG_ARCH_HIBERNATION_POSSIBLE=y
CONFIG_ARCH_SUSPEND_POSSIBLE=y
CONFIG_ARCH_WANT_GENERAL_HUGETLB=y
CONFIG_ZONE_DMA32=y
CONFIG_AUDIT_ARCH=y
CONFIG_ARCH_SUPPORTS_DEBUG_PAGEALLOC=y
CONFIG_KASAN_SHADOW_OFFSET=0xdffffc0000000000
CONFIG_X86_64_SMP=y
CONFIG_ARCH_SUPPORTS_UPROBES=y
CONFIG_FIX_EARLYCON_MEM=y
CONFIG_PGTABLE_LEVELS=4
CONFIG_CC_HAS_SANE_STACKPROTECTOR=y

#
# Processor type and features
#
CONFIG_ZONE_DMA=y
CONFIG_SMP=y
CONFIG_X86_FEATURE_NAMES=y
# CONFIG_X86_X2APIC is not set
CONFIG_X86_MPPARSE=y
# CONFIG_GOLDFISH is not set
CONFIG_RETPOLINE=y
# CONFIG_X86_CPU_RESCTRL is not set
# CONFIG_X86_EXTENDED_PLATFORM is not set
# CONFIG_X86_INTEL_LPSS is not set
# CONFIG_X86_AMD_PLATFORM_DEVICE is not set
# CONFIG_IOSF_MBI is not set
# CONFIG_SCHED_OMIT_FRAME_POINTER is not set
CONFIG_HYPERVISOR_GUEST=y
CONFIG_PARAVIRT=y
# CONFIG_PARAVIRT_DEBUG is not set
# CONFIG_PARAVIRT_SPINLOCKS is not set
# CONFIG_XEN is not set
CONFIG_KVM_GUEST=y
CONFIG_ARCH_CPUIDLE_HALTPOLL=y
# CONFIG_PVH is not set
# CONFIG_KVM_DEBUG_FS is not set
# CONFIG_PARAVIRT_TIME_ACCOUNTING is not set
CONFIG_PARAVIRT_CLOCK=y
# CONFIG_JAILHOUSE_GUEST is not set
# CONFIG_ACRN_GUEST is not set
# CONFIG_MK8 is not set
# CONFIG_MPSC is not set
# CONFIG_MCORE2 is not set
# CONFIG_MATOM is not set
CONFIG_GENERIC_CPU=y
CONFIG_X86_INTERNODE_CACHE_SHIFT=6
CONFIG_X86_L1_CACHE_SHIFT=6
CONFIG_X86_TSC=y
CONFIG_X86_CMPXCHG64=y
CONFIG_X86_CMOV=y
CONFIG_X86_MINIMUM_CPU_FAMILY=64
CONFIG_X86_DEBUGCTLMSR=y
CONFIG_IA32_FEAT_CTL=y
CONFIG_X86_VMX_FEATURE_NAMES=y
CONFIG_CPU_SUP_INTEL=y
CONFIG_CPU_SUP_AMD=y
CONFIG_CPU_SUP_HYGON=y
CONFIG_CPU_SUP_CENTAUR=y
CONFIG_CPU_SUP_ZHAOXIN=y
CONFIG_HPET_TIMER=y
CONFIG_HPET_EMULATE_RTC=y
CONFIG_DMI=y
# CONFIG_GART_IOMMU is not set
# CONFIG_MAXSMP is not set
CONFIG_NR_CPUS_RANGE_BEGIN=2
CONFIG_NR_CPUS_RANGE_END=512
CONFIG_NR_CPUS_DEFAULT=64
CONFIG_NR_CPUS=8
CONFIG_SCHED_SMT=y
CONFIG_SCHED_MC=y
CONFIG_SCHED_MC_PRIO=y
CONFIG_X86_LOCAL_APIC=y
CONFIG_X86_IO_APIC=y
# CONFIG_X86_REROUTE_FOR_BROKEN_BOOT_IRQS is not set
# CONFIG_X86_MCE is not set

#
# Performance monitoring
#
# CONFIG_PERF_EVENTS_INTEL_UNCORE is not set
# CONFIG_PERF_EVENTS_INTEL_RAPL is not set
# CONFIG_PERF_EVENTS_INTEL_CSTATE is not set
# CONFIG_PERF_EVENTS_AMD_POWER is not set
# end of Performance monitoring

CONFIG_X86_16BIT=y
CONFIG_X86_ESPFIX64=y
CONFIG_X86_VSYSCALL_EMULATION=y
CONFIG_X86_IOPL_IOPERM=y
# CONFIG_I8K is not set
# CONFIG_MICROCODE is not set
# CONFIG_X86_MSR is not set
# CONFIG_X86_CPUID is not set
# CONFIG_X86_5LEVEL is not set
CONFIG_X86_DIRECT_GBPAGES=y
# CONFIG_X86_CPA_STATISTICS is not set
# CONFIG_AMD_MEM_ENCRYPT is not set
CONFIG_NUMA=y
CONFIG_AMD_NUMA=y
CONFIG_X86_64_ACPI_NUMA=y
CONFIG_NODES_SPAN_OTHER_NODES=y
# CONFIG_NUMA_EMU is not set
CONFIG_NODES_SHIFT=6
CONFIG_ARCH_SPARSEMEM_ENABLE=y
CONFIG_ARCH_SPARSEMEM_DEFAULT=y
CONFIG_ARCH_SELECT_MEMORY_MODEL=y
CONFIG_ARCH_PROC_KCORE_TEXT=y
CONFIG_ILLEGAL_POINTER_VALUE=0xdead000000000000
# CONFIG_X86_PMEM_LEGACY is not set
# CONFIG_X86_CHECK_BIOS_CORRUPTION is not set
CONFIG_X86_RESERVE_LOW=64
CONFIG_MTRR=y
# CONFIG_MTRR_SANITIZER is not set
CONFIG_X86_PAT=y
CONFIG_ARCH_USES_PG_UNCACHED=y
CONFIG_ARCH_RANDOM=y
CONFIG_X86_SMAP=y
CONFIG_X86_UMIP=y
# CONFIG_X86_INTEL_MEMORY_PROTECTION_KEYS is not set
CONFIG_X86_INTEL_TSX_MODE_OFF=y
# CONFIG_X86_INTEL_TSX_MODE_ON is not set
# CONFIG_X86_INTEL_TSX_MODE_AUTO is not set
# CONFIG_EFI is not set
# CONFIG_SECCOMP is not set
# CONFIG_HZ_100 is not set
# CONFIG_HZ_250 is not set
# CONFIG_HZ_300 is not set
CONFIG_HZ_1000=y
CONFIG_HZ=1000
CONFIG_SCHED_HRTICK=y
# CONFIG_KEXEC is not set
# CONFIG_KEXEC_FILE is not set
CONFIG_CRASH_DUMP=y
CONFIG_PHYSICAL_START=0x1000000
# CONFIG_RELOCATABLE is not set
CONFIG_PHYSICAL_ALIGN=0x1000000
CONFIG_HOTPLUG_CPU=y
# CONFIG_BOOTPARAM_HOTPLUG_CPU0 is not set
# CONFIG_DEBUG_HOTPLUG_CPU0 is not set
# CONFIG_LEGACY_VSYSCALL_EMULATE is not set
# CONFIG_LEGACY_VSYSCALL_XONLY is not set
CONFIG_LEGACY_VSYSCALL_NONE=y
# CONFIG_CMDLINE_BOOL is not set
CONFIG_MODIFY_LDT_SYSCALL=y
CONFIG_HAVE_LIVEPATCH=y
# CONFIG_LIVEPATCH is not set
# end of Processor type and features

CONFIG_ARCH_HAS_ADD_PAGES=y
CONFIG_ARCH_ENABLE_MEMORY_HOTPLUG=y
CONFIG_USE_PERCPU_NUMA_NODE_ID=y
CONFIG_ARCH_ENABLE_SPLIT_PMD_PTLOCK=y
CONFIG_ARCH_ENABLE_HUGEPAGE_MIGRATION=y

#
# Power management and ACPI options
#
# CONFIG_SUSPEND is not set
# CONFIG_PM is not set
# CONFIG_ENERGY_MODEL is not set
CONFIG_ARCH_SUPPORTS_ACPI=y
CONFIG_ACPI=y
CONFIG_ACPI_LEGACY_TABLES_LOOKUP=y
CONFIG_ARCH_MIGHT_HAVE_ACPI_PDC=y
CONFIG_ACPI_SYSTEM_POWER_STATES_SUPPORT=y
# CONFIG_ACPI_DEBUGGER is not set
CONFIG_ACPI_SPCR_TABLE=y
CONFIG_ACPI_LPIT=y
# CONFIG_ACPI_PROCFS_POWER is not set
CONFIG_ACPI_REV_OVERRIDE_POSSIBLE=y
# CONFIG_ACPI_EC_DEBUGFS is not set
CONFIG_ACPI_AC=y
CONFIG_ACPI_BATTERY=y
CONFIG_ACPI_BUTTON=y
CONFIG_ACPI_FAN=y
# CONFIG_ACPI_DOCK is not set
CONFIG_ACPI_CPU_FREQ_PSS=y
CONFIG_ACPI_PROCESSOR_CSTATE=y
CONFIG_ACPI_PROCESSOR_IDLE=y
CONFIG_ACPI_CPPC_LIB=y
CONFIG_ACPI_PROCESSOR=y
CONFIG_ACPI_HOTPLUG_CPU=y
# CONFIG_ACPI_PROCESSOR_AGGREGATOR is not set
CONFIG_ACPI_THERMAL=y
CONFIG_ARCH_HAS_ACPI_TABLE_UPGRADE=y
# CONFIG_ACPI_DEBUG is not set
# CONFIG_ACPI_PCI_SLOT is not set
CONFIG_ACPI_CONTAINER=y
CONFIG_ACPI_HOTPLUG_IOAPIC=y
# CONFIG_ACPI_SBS is not set
# CONFIG_ACPI_HED is not set
# CONFIG_ACPI_CUSTOM_METHOD is not set
# CONFIG_ACPI_NFIT is not set
CONFIG_ACPI_NUMA=y
# CONFIG_ACPI_HMAT is not set
CONFIG_HAVE_ACPI_APEI=y
CONFIG_HAVE_ACPI_APEI_NMI=y
# CONFIG_ACPI_APEI is not set
# CONFIG_DPTF_POWER is not set
# CONFIG_PMIC_OPREGION is not set
# CONFIG_ACPI_CONFIGFS is not set
CONFIG_X86_PM_TIMER=y
# CONFIG_SFI is not set

#
# CPU Frequency scaling
#
CONFIG_CPU_FREQ=y
CONFIG_CPU_FREQ_GOV_ATTR_SET=y
# CONFIG_CPU_FREQ_STAT is not set
CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y
# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set
# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set
# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set
# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set
# CONFIG_CPU_FREQ_DEFAULT_GOV_SCHEDUTIL is not set
CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
# CONFIG_CPU_FREQ_GOV_POWERSAVE is not set
# CONFIG_CPU_FREQ_GOV_USERSPACE is not set
# CONFIG_CPU_FREQ_GOV_ONDEMAND is not set
# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set
CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y

#
# CPU frequency scaling drivers
#
CONFIG_X86_INTEL_PSTATE=y
# CONFIG_X86_PCC_CPUFREQ is not set
# CONFIG_X86_ACPI_CPUFREQ is not set
# CONFIG_X86_SPEEDSTEP_CENTRINO is not set
# CONFIG_X86_P4_CLOCKMOD is not set

#
# shared options
#
# end of CPU Frequency scaling

#
# CPU Idle
#
CONFIG_CPU_IDLE=y
CONFIG_CPU_IDLE_GOV_LADDER=y
CONFIG_CPU_IDLE_GOV_MENU=y
# CONFIG_CPU_IDLE_GOV_TEO is not set
# CONFIG_CPU_IDLE_GOV_HALTPOLL is not set
CONFIG_HALTPOLL_CPUIDLE=y
# end of CPU Idle

# CONFIG_INTEL_IDLE is not set
# end of Power management and ACPI options

#
# Bus options (PCI etc.)
#
CONFIG_PCI_DIRECT=y
CONFIG_PCI_MMCONFIG=y
CONFIG_MMCONF_FAM10H=y
CONFIG_ISA_DMA_API=y
CONFIG_AMD_NB=y
CONFIG_X86_SYSFB=y
# end of Bus options (PCI etc.)

#
# Binary Emulations
#
# CONFIG_IA32_EMULATION is not set
# CONFIG_X86_X32 is not set
# end of Binary Emulations

#
# Firmware Drivers
#
# CONFIG_EDD is not set
CONFIG_FIRMWARE_MEMMAP=y
# CONFIG_DMIID is not set
# CONFIG_DMI_SYSFS is not set
CONFIG_DMI_SCAN_MACHINE_NON_EFI_FALLBACK=y
# CONFIG_FW_CFG_SYSFS is not set
# CONFIG_GOOGLE_FIRMWARE is not set
CONFIG_EFI_EARLYCON=y

#
# Tegra firmware driver
#
# end of Tegra firmware driver
# end of Firmware Drivers

CONFIG_HAVE_KVM=y
CONFIG_VIRTUALIZATION=y
# CONFIG_KVM is not set
CONFIG_AS_AVX512=y
CONFIG_AS_SHA1_NI=y
CONFIG_AS_SHA256_NI=y

#
# General architecture-dependent options
#
CONFIG_CRASH_CORE=y
CONFIG_HOTPLUG_SMT=y
CONFIG_HAVE_OPROFILE=y
CONFIG_OPROFILE_NMI_TIMER=y
# CONFIG_KPROBES is not set
# CONFIG_JUMP_LABEL is not set
CONFIG_UPROBES=y
CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y
CONFIG_ARCH_USE_BUILTIN_BSWAP=y
CONFIG_HAVE_IOREMAP_PROT=y
CONFIG_HAVE_KPROBES=y
CONFIG_HAVE_KRETPROBES=y
CONFIG_HAVE_OPTPROBES=y
CONFIG_HAVE_KPROBES_ON_FTRACE=y
CONFIG_HAVE_FUNCTION_ERROR_INJECTION=y
CONFIG_HAVE_NMI=y
CONFIG_HAVE_ARCH_TRACEHOOK=y
CONFIG_HAVE_DMA_CONTIGUOUS=y
CONFIG_GENERIC_SMP_IDLE_THREAD=y
CONFIG_ARCH_HAS_FORTIFY_SOURCE=y
CONFIG_ARCH_HAS_SET_MEMORY=y
CONFIG_ARCH_HAS_SET_DIRECT_MAP=y
CONFIG_HAVE_ARCH_THREAD_STRUCT_WHITELIST=y
CONFIG_ARCH_WANTS_DYNAMIC_TASK_STRUCT=y
CONFIG_HAVE_ASM_MODVERSIONS=y
CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y
CONFIG_HAVE_RSEQ=y
CONFIG_HAVE_FUNCTION_ARG_ACCESS_API=y
CONFIG_HAVE_CLK=y
CONFIG_HAVE_HW_BREAKPOINT=y
CONFIG_HAVE_MIXED_BREAKPOINTS_REGS=y
CONFIG_HAVE_USER_RETURN_NOTIFIER=y
CONFIG_HAVE_PERF_EVENTS_NMI=y
CONFIG_HAVE_HARDLOCKUP_DETECTOR_PERF=y
CONFIG_HAVE_PERF_REGS=y
CONFIG_HAVE_PERF_USER_STACK_DUMP=y
CONFIG_HAVE_ARCH_JUMP_LABEL=y
CONFIG_HAVE_ARCH_JUMP_LABEL_RELATIVE=y
CONFIG_MMU_GATHER_TABLE_FREE=y
CONFIG_MMU_GATHER_RCU_TABLE_FREE=y
CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG=y
CONFIG_HAVE_ALIGNED_STRUCT_PAGE=y
CONFIG_HAVE_CMPXCHG_LOCAL=y
CONFIG_HAVE_CMPXCHG_DOUBLE=y
CONFIG_HAVE_ARCH_SECCOMP_FILTER=y
CONFIG_HAVE_ARCH_STACKLEAK=y
CONFIG_HAVE_STACKPROTECTOR=y
CONFIG_CC_HAS_STACKPROTECTOR_NONE=y
CONFIG_STACKPROTECTOR=y
CONFIG_STACKPROTECTOR_STRONG=y
CONFIG_HAVE_ARCH_WITHIN_STACK_FRAMES=y
CONFIG_HAVE_CONTEXT_TRACKING=y
CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y
CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y
CONFIG_HAVE_MOVE_PMD=y
CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE=y
CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD=y
CONFIG_HAVE_ARCH_HUGE_VMAP=y
CONFIG_ARCH_WANT_HUGE_PMD_SHARE=y
CONFIG_HAVE_ARCH_SOFT_DIRTY=y
CONFIG_HAVE_MOD_ARCH_SPECIFIC=y
CONFIG_MODULES_USE_ELF_RELA=y
CONFIG_HAVE_IRQ_EXIT_ON_IRQ_STACK=y
CONFIG_ARCH_HAS_ELF_RANDOMIZE=y
CONFIG_HAVE_ARCH_MMAP_RND_BITS=y
CONFIG_HAVE_EXIT_THREAD=y
CONFIG_ARCH_MMAP_RND_BITS=28
CONFIG_HAVE_COPY_THREAD_TLS=y
CONFIG_HAVE_STACK_VALIDATION=y
CONFIG_HAVE_RELIABLE_STACKTRACE=y
CONFIG_COMPAT_32BIT_TIME=y
CONFIG_HAVE_ARCH_VMAP_STACK=y
CONFIG_ARCH_HAS_STRICT_KERNEL_RWX=y
CONFIG_STRICT_KERNEL_RWX=y
CONFIG_ARCH_HAS_STRICT_MODULE_RWX=y
CONFIG_STRICT_MODULE_RWX=y
CONFIG_HAVE_ARCH_PREL32_RELOCATIONS=y
CONFIG_ARCH_USE_MEMREMAP_PROT=y
# CONFIG_LOCK_EVENT_COUNTS is not set
CONFIG_ARCH_HAS_MEM_ENCRYPT=y

#
# GCOV-based kernel profiling
#
# CONFIG_GCOV_KERNEL is not set
CONFIG_ARCH_HAS_GCOV_PROFILE_ALL=y
# end of GCOV-based kernel profiling

CONFIG_HAVE_GCC_PLUGINS=y
# CONFIG_GCC_PLUGINS is not set
# end of General architecture-dependent options

CONFIG_RT_MUTEXES=y
CONFIG_BASE_SMALL=0
CONFIG_MODULES=y
# CONFIG_MODULE_FORCE_LOAD is not set
CONFIG_MODULE_UNLOAD=y
CONFIG_MODULE_FORCE_UNLOAD=y
# CONFIG_MODVERSIONS is not set
# CONFIG_MODULE_SRCVERSION_ALL is not set
# CONFIG_MODULE_SIG is not set
# CONFIG_MODULE_COMPRESS is not set
# CONFIG_MODULE_ALLOW_MISSING_NAMESPACE_IMPORTS is not set
CONFIG_UNUSED_SYMBOLS=y
CONFIG_MODULES_TREE_LOOKUP=y
CONFIG_BLOCK=y
CONFIG_BLK_SCSI_REQUEST=y
CONFIG_BLK_DEV_BSG=y
# CONFIG_BLK_DEV_BSGLIB is not set
# CONFIG_BLK_DEV_INTEGRITY is not set
# CONFIG_BLK_DEV_ZONED is not set
# CONFIG_BLK_CMDLINE_PARSER is not set
# CONFIG_BLK_WBT is not set
CONFIG_BLK_DEBUG_FS=y
# CONFIG_BLK_SED_OPAL is not set

#
# Partition Types
#
CONFIG_PARTITION_ADVANCED=y
# CONFIG_ACORN_PARTITION is not set
# CONFIG_AIX_PARTITION is not set
# CONFIG_OSF_PARTITION is not set
# CONFIG_AMIGA_PARTITION is not set
# CONFIG_ATARI_PARTITION is not set
# CONFIG_MAC_PARTITION is not set
CONFIG_MSDOS_PARTITION=y
CONFIG_BSD_DISKLABEL=y
CONFIG_MINIX_SUBPARTITION=y
CONFIG_SOLARIS_X86_PARTITION=y
CONFIG_UNIXWARE_DISKLABEL=y
# CONFIG_LDM_PARTITION is not set
# CONFIG_SGI_PARTITION is not set
# CONFIG_ULTRIX_PARTITION is not set
# CONFIG_SUN_PARTITION is not set
# CONFIG_KARMA_PARTITION is not set
CONFIG_EFI_PARTITION=y
# CONFIG_SYSV68_PARTITION is not set
# CONFIG_CMDLINE_PARTITION is not set
# end of Partition Types

CONFIG_BLK_MQ_PCI=y
CONFIG_BLK_MQ_VIRTIO=y

#
# IO Schedulers
#
CONFIG_MQ_IOSCHED_DEADLINE=y
# CONFIG_MQ_IOSCHED_KYBER is not set
# CONFIG_IOSCHED_BFQ is not set
# end of IO Schedulers

CONFIG_PADATA=y
CONFIG_ASN1=y
CONFIG_UNINLINE_SPIN_UNLOCK=y
CONFIG_ARCH_SUPPORTS_ATOMIC_RMW=y
CONFIG_MUTEX_SPIN_ON_OWNER=y
CONFIG_RWSEM_SPIN_ON_OWNER=y
CONFIG_LOCK_SPIN_ON_OWNER=y
CONFIG_ARCH_USE_QUEUED_SPINLOCKS=y
CONFIG_QUEUED_SPINLOCKS=y
CONFIG_ARCH_USE_QUEUED_RWLOCKS=y
CONFIG_QUEUED_RWLOCKS=y
CONFIG_ARCH_HAS_SYNC_CORE_BEFORE_USERMODE=y
CONFIG_ARCH_HAS_SYSCALL_WRAPPER=y

#
# Executable file formats
#
CONFIG_BINFMT_ELF=y
CONFIG_ELFCORE=y
CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y
CONFIG_BINFMT_SCRIPT=y
CONFIG_BINFMT_MISC=y
CONFIG_COREDUMP=y
# end of Executable file formats

#
# Memory Management options
#
CONFIG_SELECT_MEMORY_MODEL=y
CONFIG_SPARSEMEM_MANUAL=y
CONFIG_SPARSEMEM=y
CONFIG_NEED_MULTIPLE_NODES=y
CONFIG_HAVE_MEMORY_PRESENT=y
CONFIG_SPARSEMEM_EXTREME=y
CONFIG_SPARSEMEM_VMEMMAP_ENABLE=y
CONFIG_SPARSEMEM_VMEMMAP=y
CONFIG_HAVE_MEMBLOCK_NODE_MAP=y
CONFIG_HAVE_FAST_GUP=y
# CONFIG_MEMORY_HOTPLUG is not set
CONFIG_SPLIT_PTLOCK_CPUS=4
CONFIG_MEMORY_BALLOON=y
CONFIG_BALLOON_COMPACTION=y
CONFIG_COMPACTION=y
CONFIG_PAGE_REPORTING=y
CONFIG_MIGRATION=y
CONFIG_PHYS_ADDR_T_64BIT=y
# CONFIG_BOUNCE is not set
CONFIG_VIRT_TO_BUS=y
# CONFIG_KSM is not set
CONFIG_DEFAULT_MMAP_MIN_ADDR=4096
# CONFIG_TRANSPARENT_HUGEPAGE is not set
CONFIG_ARCH_WANTS_THP_SWAP=y
# CONFIG_CLEANCACHE is not set
# CONFIG_CMA is not set
# CONFIG_ZPOOL is not set
# CONFIG_ZBUD is not set
# CONFIG_ZSMALLOC is not set
CONFIG_GENERIC_EARLY_IOREMAP=y
# CONFIG_DEFERRED_STRUCT_PAGE_INIT is not set
# CONFIG_IDLE_PAGE_TRACKING is not set
CONFIG_ARCH_HAS_PTE_DEVMAP=y
# CONFIG_PERCPU_STATS is not set
# CONFIG_GUP_BENCHMARK is not set
CONFIG_ARCH_HAS_PTE_SPECIAL=y
# end of Memory Management options

CONFIG_NET=y
CONFIG_NET_INGRESS=y
CONFIG_SKB_EXTENSIONS=y

#
# Networking options
#
CONFIG_PACKET=y
# CONFIG_PACKET_DIAG is not set
CONFIG_UNIX=y
CONFIG_UNIX_SCM=y
# CONFIG_UNIX_DIAG is not set
# CONFIG_TLS is not set
CONFIG_XFRM=y
CONFIG_XFRM_ALGO=y
CONFIG_XFRM_USER=y
# CONFIG_XFRM_INTERFACE is not set
# CONFIG_XFRM_SUB_POLICY is not set
# CONFIG_XFRM_MIGRATE is not set
# CONFIG_XFRM_STATISTICS is not set
# CONFIG_NET_KEY is not set
CONFIG_INET=y
CONFIG_IP_MULTICAST=y
CONFIG_IP_ADVANCED_ROUTER=y
# CONFIG_IP_FIB_TRIE_STATS is not set
CONFIG_IP_MULTIPLE_TABLES=y
CONFIG_IP_ROUTE_MULTIPATH=y
CONFIG_IP_ROUTE_VERBOSE=y
CONFIG_IP_PNP=y
CONFIG_IP_PNP_DHCP=y
CONFIG_IP_PNP_BOOTP=y
CONFIG_IP_PNP_RARP=y
# CONFIG_NET_IPIP is not set
# CONFIG_NET_IPGRE_DEMUX is not set
# CONFIG_IP_MROUTE is not set
CONFIG_SYN_COOKIES=y
# CONFIG_NET_IPVTI is not set
# CONFIG_NET_FOU is not set
# CONFIG_INET_AH is not set
# CONFIG_INET_ESP is not set
# CONFIG_INET_IPCOMP is not set
CONFIG_INET_DIAG=y
CONFIG_INET_TCP_DIAG=y
CONFIG_INET_UDP_DIAG=y
CONFIG_INET_RAW_DIAG=y
# CONFIG_INET_DIAG_DESTROY is not set
CONFIG_TCP_CONG_ADVANCED=y
# CONFIG_TCP_CONG_BIC is not set
# CONFIG_TCP_CONG_CUBIC is not set
# CONFIG_TCP_CONG_WESTWOOD is not set
# CONFIG_TCP_CONG_HTCP is not set
# CONFIG_TCP_CONG_HSTCP is not set
# CONFIG_TCP_CONG_HYBLA is not set
# CONFIG_TCP_CONG_VEGAS is not set
# CONFIG_TCP_CONG_NV is not set
# CONFIG_TCP_CONG_SCALABLE is not set
# CONFIG_TCP_CONG_LP is not set
# CONFIG_TCP_CONG_VENO is not set
# CONFIG_TCP_CONG_YEAH is not set
# CONFIG_TCP_CONG_ILLINOIS is not set
# CONFIG_TCP_CONG_DCTCP is not set
# CONFIG_TCP_CONG_CDG is not set
CONFIG_TCP_CONG_BBR=y
CONFIG_DEFAULT_BBR=y
# CONFIG_DEFAULT_RENO is not set
CONFIG_DEFAULT_TCP_CONG="bbr"
CONFIG_TCP_MD5SIG=y
CONFIG_IPV6=y
# CONFIG_IPV6_ROUTER_PREF is not set
# CONFIG_IPV6_OPTIMISTIC_DAD is not set
# CONFIG_INET6_AH is not set
# CONFIG_INET6_ESP is not set
# CONFIG_INET6_IPCOMP is not set
# CONFIG_IPV6_MIP6 is not set
# CONFIG_IPV6_ILA is not set
# CONFIG_IPV6_VTI is not set
# CONFIG_IPV6_SIT is not set
# CONFIG_IPV6_TUNNEL is not set
CONFIG_IPV6_MULTIPLE_TABLES=y
CONFIG_IPV6_SUBTREES=y
# CONFIG_IPV6_MROUTE is not set
# CONFIG_IPV6_SEG6_LWTUNNEL is not set
# CONFIG_IPV6_SEG6_HMAC is not set
# CONFIG_IPV6_RPL_LWTUNNEL is not set
# CONFIG_MPTCP is not set
# CONFIG_NETWORK_SECMARK is not set
CONFIG_NET_PTP_CLASSIFY=y
# CONFIG_NETWORK_PHY_TIMESTAMPING is not set
CONFIG_NETFILTER=y
CONFIG_NETFILTER_ADVANCED=y
CONFIG_BRIDGE_NETFILTER=y

#
# Core Netfilter Configuration
#
CONFIG_NETFILTER_INGRESS=y
CONFIG_NETFILTER_NETLINK=y
CONFIG_NETFILTER_FAMILY_BRIDGE=y
# CONFIG_NETFILTER_NETLINK_ACCT is not set
CONFIG_NETFILTER_NETLINK_QUEUE=y
CONFIG_NETFILTER_NETLINK_LOG=y
# CONFIG_NETFILTER_NETLINK_OSF is not set
CONFIG_NF_CONNTRACK=y
CONFIG_NF_LOG_COMMON=y
# CONFIG_NF_LOG_NETDEV is not set
# CONFIG_NF_CONNTRACK_MARK is not set
# CONFIG_NF_CONNTRACK_ZONES is not set
CONFIG_NF_CONNTRACK_PROCFS=y
# CONFIG_NF_CONNTRACK_EVENTS is not set
# CONFIG_NF_CONNTRACK_TIMEOUT is not set
# CONFIG_NF_CONNTRACK_TIMESTAMP is not set
# CONFIG_NF_CONNTRACK_LABELS is not set
CONFIG_NF_CT_PROTO_DCCP=y
CONFIG_NF_CT_PROTO_SCTP=y
CONFIG_NF_CT_PROTO_UDPLITE=y
# CONFIG_NF_CONNTRACK_AMANDA is not set
# CONFIG_NF_CONNTRACK_FTP is not set
# CONFIG_NF_CONNTRACK_H323 is not set
# CONFIG_NF_CONNTRACK_IRC is not set
# CONFIG_NF_CONNTRACK_NETBIOS_NS is not set
# CONFIG_NF_CONNTRACK_SNMP is not set
# CONFIG_NF_CONNTRACK_PPTP is not set
# CONFIG_NF_CONNTRACK_SANE is not set
# CONFIG_NF_CONNTRACK_SIP is not set
# CONFIG_NF_CONNTRACK_TFTP is not set
# CONFIG_NF_CT_NETLINK is not set
CONFIG_NF_NAT=y
CONFIG_NF_NAT_REDIRECT=y
CONFIG_NF_NAT_MASQUERADE=y
CONFIG_NF_TABLES=y
# CONFIG_NF_TABLES_INET is not set
# CONFIG_NF_TABLES_NETDEV is not set
# CONFIG_NFT_NUMGEN is not set
CONFIG_NFT_CT=y
# CONFIG_NFT_COUNTER is not set
# CONFIG_NFT_CONNLIMIT is not set
CONFIG_NFT_LOG=y
CONFIG_NFT_LIMIT=y
CONFIG_NFT_MASQ=y
CONFIG_NFT_REDIR=y
# CONFIG_NFT_TUNNEL is not set
# CONFIG_NFT_OBJREF is not set
# CONFIG_NFT_QUEUE is not set
# CONFIG_NFT_QUOTA is not set
CONFIG_NFT_REJECT=y
CONFIG_NFT_COMPAT=y
CONFIG_NFT_HASH=y
# CONFIG_NFT_XFRM is not set
# CONFIG_NFT_SOCKET is not set
# CONFIG_NFT_OSF is not set
# CONFIG_NFT_TPROXY is not set
# CONFIG_NFT_SYNPROXY is not set
# CONFIG_NF_FLOW_TABLE is not set
CONFIG_NETFILTER_XTABLES=y

#
# Xtables combined modules
#
CONFIG_NETFILTER_XT_MARK=y
# CONFIG_NETFILTER_XT_CONNMARK is not set

#
# Xtables targets
#
# CONFIG_NETFILTER_XT_TARGET_CHECKSUM is not set
# CONFIG_NETFILTER_XT_TARGET_CLASSIFY is not set
# CONFIG_NETFILTER_XT_TARGET_CONNMARK is not set
# CONFIG_NETFILTER_XT_TARGET_DSCP is not set
# CONFIG_NETFILTER_XT_TARGET_HL is not set
# CONFIG_NETFILTER_XT_TARGET_HMARK is not set
# CONFIG_NETFILTER_XT_TARGET_IDLETIMER is not set
CONFIG_NETFILTER_XT_TARGET_LOG=y
CONFIG_NETFILTER_XT_TARGET_MARK=y
CONFIG_NETFILTER_XT_NAT=y
# CONFIG_NETFILTER_XT_TARGET_NETMAP is not set
# CONFIG_NETFILTER_XT_TARGET_NFLOG is not set
CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y
# CONFIG_NETFILTER_XT_TARGET_RATEEST is not set
CONFIG_NETFILTER_XT_TARGET_REDIRECT=y
CONFIG_NETFILTER_XT_TARGET_MASQUERADE=y
# CONFIG_NETFILTER_XT_TARGET_TEE is not set
# CONFIG_NETFILTER_XT_TARGET_TPROXY is not set
CONFIG_NETFILTER_XT_TARGET_TCPMSS=y
# CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP is not set

#
# Xtables matches
#
CONFIG_NETFILTER_XT_MATCH_ADDRTYPE=y
# CONFIG_NETFILTER_XT_MATCH_BPF is not set
# CONFIG_NETFILTER_XT_MATCH_CGROUP is not set
# CONFIG_NETFILTER_XT_MATCH_CLUSTER is not set
# CONFIG_NETFILTER_XT_MATCH_COMMENT is not set
# CONFIG_NETFILTER_XT_MATCH_CONNBYTES is not set
# CONFIG_NETFILTER_XT_MATCH_CONNLABEL is not set
# CONFIG_NETFILTER_XT_MATCH_CONNLIMIT is not set
# CONFIG_NETFILTER_XT_MATCH_CONNMARK is not set
CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y
# CONFIG_NETFILTER_XT_MATCH_CPU is not set
# CONFIG_NETFILTER_XT_MATCH_DCCP is not set
# CONFIG_NETFILTER_XT_MATCH_DEVGROUP is not set
# CONFIG_NETFILTER_XT_MATCH_DSCP is not set
# CONFIG_NETFILTER_XT_MATCH_ECN is not set
# CONFIG_NETFILTER_XT_MATCH_ESP is not set
# CONFIG_NETFILTER_XT_MATCH_HASHLIMIT is not set
# CONFIG_NETFILTER_XT_MATCH_HELPER is not set
# CONFIG_NETFILTER_XT_MATCH_HL is not set
# CONFIG_NETFILTER_XT_MATCH_IPCOMP is not set
# CONFIG_NETFILTER_XT_MATCH_IPRANGE is not set
# CONFIG_NETFILTER_XT_MATCH_L2TP is not set
# CONFIG_NETFILTER_XT_MATCH_LENGTH is not set
# CONFIG_NETFILTER_XT_MATCH_LIMIT is not set
# CONFIG_NETFILTER_XT_MATCH_MAC is not set
# CONFIG_NETFILTER_XT_MATCH_MARK is not set
# CONFIG_NETFILTER_XT_MATCH_MULTIPORT is not set
# CONFIG_NETFILTER_XT_MATCH_NFACCT is not set
# CONFIG_NETFILTER_XT_MATCH_OSF is not set
# CONFIG_NETFILTER_XT_MATCH_OWNER is not set
# CONFIG_NETFILTER_XT_MATCH_POLICY is not set
# CONFIG_NETFILTER_XT_MATCH_PHYSDEV is not set
# CONFIG_NETFILTER_XT_MATCH_PKTTYPE is not set
# CONFIG_NETFILTER_XT_MATCH_QUOTA is not set
# CONFIG_NETFILTER_XT_MATCH_RATEEST is not set
# CONFIG_NETFILTER_XT_MATCH_REALM is not set
# CONFIG_NETFILTER_XT_MATCH_RECENT is not set
# CONFIG_NETFILTER_XT_MATCH_SCTP is not set
# CONFIG_NETFILTER_XT_MATCH_SOCKET is not set
CONFIG_NETFILTER_XT_MATCH_STATE=y
# CONFIG_NETFILTER_XT_MATCH_STATISTIC is not set
# CONFIG_NETFILTER_XT_MATCH_STRING is not set
# CONFIG_NETFILTER_XT_MATCH_TCPMSS is not set
# CONFIG_NETFILTER_XT_MATCH_TIME is not set
CONFIG_NETFILTER_XT_MATCH_U32=y
# end of Core Netfilter Configuration

# CONFIG_IP_SET is not set
# CONFIG_IP_VS is not set

#
# IP: Netfilter Configuration
#
CONFIG_NF_DEFRAG_IPV4=y
# CONFIG_NF_SOCKET_IPV4 is not set
# CONFIG_NF_TPROXY_IPV4 is not set
# CONFIG_NF_TABLES_IPV4 is not set
# CONFIG_NF_TABLES_ARP is not set
# CONFIG_NF_DUP_IPV4 is not set
# CONFIG_NF_LOG_ARP is not set
CONFIG_NF_LOG_IPV4=y
CONFIG_NF_REJECT_IPV4=y
CONFIG_IP_NF_IPTABLES=y
# CONFIG_IP_NF_MATCH_AH is not set
# CONFIG_IP_NF_MATCH_ECN is not set
# CONFIG_IP_NF_MATCH_RPFILTER is not set
# CONFIG_IP_NF_MATCH_TTL is not set
CONFIG_IP_NF_FILTER=y
CONFIG_IP_NF_TARGET_REJECT=y
# CONFIG_IP_NF_TARGET_SYNPROXY is not set
CONFIG_IP_NF_NAT=y
CONFIG_IP_NF_TARGET_MASQUERADE=y
# CONFIG_IP_NF_TARGET_NETMAP is not set
CONFIG_IP_NF_TARGET_REDIRECT=y
CONFIG_IP_NF_MANGLE=y
# CONFIG_IP_NF_TARGET_CLUSTERIP is not set
# CONFIG_IP_NF_TARGET_ECN is not set
# CONFIG_IP_NF_TARGET_TTL is not set
# CONFIG_IP_NF_RAW is not set
# CONFIG_IP_NF_ARPTABLES is not set
# end of IP: Netfilter Configuration

#
# IPv6: Netfilter Configuration
#
# CONFIG_NF_SOCKET_IPV6 is not set
# CONFIG_NF_TPROXY_IPV6 is not set
# CONFIG_NF_TABLES_IPV6 is not set
# CONFIG_NF_DUP_IPV6 is not set
CONFIG_NF_REJECT_IPV6=y
CONFIG_NF_LOG_IPV6=y
CONFIG_IP6_NF_IPTABLES=y
# CONFIG_IP6_NF_MATCH_AH is not set
# CONFIG_IP6_NF_MATCH_EUI64 is not set
# CONFIG_IP6_NF_MATCH_FRAG is not set
# CONFIG_IP6_NF_MATCH_OPTS is not set
# CONFIG_IP6_NF_MATCH_HL is not set
CONFIG_IP6_NF_MATCH_IPV6HEADER=y
# CONFIG_IP6_NF_MATCH_MH is not set
# CONFIG_IP6_NF_MATCH_RPFILTER is not set
# CONFIG_IP6_NF_MATCH_RT is not set
# CONFIG_IP6_NF_MATCH_SRH is not set
# CONFIG_IP6_NF_TARGET_HL is not set
CONFIG_IP6_NF_FILTER=y
CONFIG_IP6_NF_TARGET_REJECT=y
# CONFIG_IP6_NF_TARGET_SYNPROXY is not set
CONFIG_IP6_NF_MANGLE=y
# CONFIG_IP6_NF_RAW is not set
# CONFIG_IP6_NF_NAT is not set
# end of IPv6: Netfilter Configuration

CONFIG_NF_DEFRAG_IPV6=y
# CONFIG_NF_TABLES_BRIDGE is not set
# CONFIG_NF_CONNTRACK_BRIDGE is not set
# CONFIG_BRIDGE_NF_EBTABLES is not set
# CONFIG_BPFILTER is not set
# CONFIG_IP_DCCP is not set
# CONFIG_IP_SCTP is not set
# CONFIG_RDS is not set
# CONFIG_TIPC is not set
# CONFIG_ATM is not set
# CONFIG_L2TP is not set
CONFIG_STP=y
CONFIG_BRIDGE=y
CONFIG_BRIDGE_IGMP_SNOOPING=y
CONFIG_BRIDGE_VLAN_FILTERING=y
CONFIG_HAVE_NET_DSA=y
# CONFIG_NET_DSA is not set
CONFIG_VLAN_8021Q=y
# CONFIG_VLAN_8021Q_GVRP is not set
# CONFIG_VLAN_8021Q_MVRP is not set
# CONFIG_DECNET is not set
CONFIG_LLC=y
CONFIG_LLC2=y
# CONFIG_ATALK is not set
# CONFIG_X25 is not set
# CONFIG_LAPB is not set
# CONFIG_PHONET is not set
# CONFIG_6LOWPAN is not set
# CONFIG_IEEE802154 is not set
# CONFIG_NET_SCHED is not set
# CONFIG_DCB is not set
# CONFIG_BATMAN_ADV is not set
# CONFIG_OPENVSWITCH is not set
# CONFIG_VSOCKETS is not set
# CONFIG_NETLINK_DIAG is not set
# CONFIG_MPLS is not set
# CONFIG_NET_NSH is not set
# CONFIG_HSR is not set
# CONFIG_NET_SWITCHDEV is not set
# CONFIG_NET_L3_MASTER_DEV is not set
# CONFIG_NET_NCSI is not set
CONFIG_RPS=y
CONFIG_RFS_ACCEL=y
CONFIG_XPS=y
# CONFIG_CGROUP_NET_PRIO is not set
# CONFIG_CGROUP_NET_CLASSID is not set
CONFIG_NET_RX_BUSY_POLL=y
CONFIG_BQL=y
# CONFIG_BPF_JIT is not set
CONFIG_NET_FLOW_LIMIT=y

#
# Network testing
#
# CONFIG_NET_PKTGEN is not set
# CONFIG_NET_DROP_MONITOR is not set
# end of Network testing
# end of Networking options

# CONFIG_HAMRADIO is not set
# CONFIG_CAN is not set
# CONFIG_BT is not set
# CONFIG_AF_RXRPC is not set
# CONFIG_AF_KCM is not set
CONFIG_FIB_RULES=y
# CONFIG_WIRELESS is not set
# CONFIG_WIMAX is not set
# CONFIG_RFKILL is not set
CONFIG_NET_9P=y
CONFIG_NET_9P_VIRTIO=y
# CONFIG_NET_9P_DEBUG is not set
# CONFIG_CAIF is not set
# CONFIG_CEPH_LIB is not set
# CONFIG_NFC is not set
# CONFIG_PSAMPLE is not set
# CONFIG_NET_IFE is not set
CONFIG_LWTUNNEL=y
CONFIG_LWTUNNEL_BPF=y
CONFIG_GRO_CELLS=y
CONFIG_FAILOVER=y
CONFIG_ETHTOOL_NETLINK=y
CONFIG_HAVE_EBPF_JIT=y

#
# Device Drivers
#
CONFIG_HAVE_EISA=y
# CONFIG_EISA is not set
CONFIG_HAVE_PCI=y
CONFIG_PCI=y
CONFIG_PCI_DOMAINS=y
CONFIG_PCIEPORTBUS=y
# CONFIG_PCIEAER is not set
CONFIG_PCIEASPM=y
CONFIG_PCIEASPM_DEFAULT=y
# CONFIG_PCIEASPM_POWERSAVE is not set
# CONFIG_PCIEASPM_POWER_SUPERSAVE is not set
# CONFIG_PCIEASPM_PERFORMANCE is not set
# CONFIG_PCIE_PTM is not set
# CONFIG_PCIE_BW is not set
CONFIG_PCI_MSI=y
CONFIG_PCI_MSI_IRQ_DOMAIN=y
CONFIG_PCI_QUIRKS=y
# CONFIG_PCI_DEBUG is not set
# CONFIG_PCI_REALLOC_ENABLE_AUTO is not set
# CONFIG_PCI_STUB is not set
# CONFIG_PCI_PF_STUB is not set
CONFIG_PCI_ATS=y
CONFIG_PCI_LOCKLESS_CONFIG=y
CONFIG_PCI_IOV=y
CONFIG_PCI_PRI=y
CONFIG_PCI_PASID=y
CONFIG_PCI_LABEL=y
# CONFIG_HOTPLUG_PCI is not set

#
# PCI controller drivers
#
# CONFIG_VMD is not set

#
# DesignWare PCI Core Support
#
# CONFIG_PCIE_DW_PLAT_HOST is not set
# CONFIG_PCI_MESON is not set
# end of DesignWare PCI Core Support

#
# Mobiveil PCIe Core Support
#
# end of Mobiveil PCIe Core Support

#
# Cadence PCIe controllers support
#
# end of Cadence PCIe controllers support
# end of PCI controller drivers

#
# PCI Endpoint
#
# CONFIG_PCI_ENDPOINT is not set
# end of PCI Endpoint

#
# PCI switch controller drivers
#
# CONFIG_PCI_SW_SWITCHTEC is not set
# end of PCI switch controller drivers

# CONFIG_PCCARD is not set
# CONFIG_RAPIDIO is not set

#
# Generic Driver Options
#
CONFIG_UEVENT_HELPER=y
CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
CONFIG_DEVTMPFS=y
CONFIG_DEVTMPFS_MOUNT=y
CONFIG_STANDALONE=y
CONFIG_PREVENT_FIRMWARE_BUILD=y

#
# Firmware loader
#
CONFIG_FW_LOADER=y
CONFIG_EXTRA_FIRMWARE=""
# CONFIG_FW_LOADER_USER_HELPER is not set
# CONFIG_FW_LOADER_COMPRESS is not set
# end of Firmware loader

CONFIG_ALLOW_DEV_COREDUMP=y
# CONFIG_DEBUG_DRIVER is not set
# CONFIG_DEBUG_DEVRES is not set
# CONFIG_DEBUG_TEST_DRIVER_REMOVE is not set
# CONFIG_PM_QOS_KUNIT_TEST is not set
# CONFIG_TEST_ASYNC_DRIVER_PROBE is not set
# CONFIG_KUNIT_DRIVER_PE_TEST is not set
CONFIG_GENERIC_CPU_AUTOPROBE=y
CONFIG_GENERIC_CPU_VULNERABILITIES=y
# end of Generic Driver Options

#
# Bus devices
#
# CONFIG_MHI_BUS is not set
# end of Bus devices

CONFIG_CONNECTOR=y
CONFIG_PROC_EVENTS=y
# CONFIG_GNSS is not set
# CONFIG_MTD is not set
# CONFIG_OF is not set
CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y
# CONFIG_PARPORT is not set
CONFIG_PNP=y
CONFIG_PNP_DEBUG_MESSAGES=y

#
# Protocols
#
CONFIG_PNPACPI=y
CONFIG_BLK_DEV=y
# CONFIG_BLK_DEV_NULL_BLK is not set
# CONFIG_BLK_DEV_FD is not set
# CONFIG_BLK_DEV_PCIESSD_MTIP32XX is not set
# CONFIG_BLK_DEV_UMEM is not set
# CONFIG_BLK_DEV_LOOP is not set
# CONFIG_BLK_DEV_DRBD is not set
# CONFIG_BLK_DEV_NBD is not set
# CONFIG_BLK_DEV_SKD is not set
# CONFIG_BLK_DEV_SX8 is not set
# CONFIG_BLK_DEV_RAM is not set
# CONFIG_CDROM_PKTCDVD is not set
# CONFIG_ATA_OVER_ETH is not set
CONFIG_VIRTIO_BLK=y
# CONFIG_BLK_DEV_RBD is not set
# CONFIG_BLK_DEV_RSXX is not set

#
# NVME Support
#
# CONFIG_BLK_DEV_NVME is not set
# CONFIG_NVME_FC is not set
# CONFIG_NVME_TARGET is not set
# end of NVME Support

#
# Misc devices
#
# CONFIG_DUMMY_IRQ is not set
# CONFIG_IBM_ASM is not set
# CONFIG_PHANTOM is not set
# CONFIG_TIFM_CORE is not set
# CONFIG_ENCLOSURE_SERVICES is not set
# CONFIG_HP_ILO is not set
# CONFIG_SRAM is not set
# CONFIG_PCI_ENDPOINT_TEST is not set
# CONFIG_XILINX_SDFEC is not set
# CONFIG_PVPANIC is not set
# CONFIG_C2PORT is not set

#
# EEPROM support
#
# CONFIG_EEPROM_93CX6 is not set
# end of EEPROM support

# CONFIG_CB710_CORE is not set

#
# Texas Instruments shared transport line discipline
#
# end of Texas Instruments shared transport line discipline

#
# Altera FPGA firmware download module (requires I2C)
#
# CONFIG_INTEL_MEI is not set
# CONFIG_INTEL_MEI_ME is not set
# CONFIG_INTEL_MEI_TXE is not set
# CONFIG_VMWARE_VMCI is not set

#
# Intel MIC & related support
#
# CONFIG_INTEL_MIC_BUS is not set
# CONFIG_SCIF_BUS is not set
# CONFIG_VOP_BUS is not set
# end of Intel MIC & related support

# CONFIG_GENWQE is not set
# CONFIG_ECHO is not set
# CONFIG_MISC_ALCOR_PCI is not set
# CONFIG_MISC_RTSX_PCI is not set
# CONFIG_HABANA_AI is not set
# end of Misc devices

CONFIG_HAVE_IDE=y
# CONFIG_IDE is not set

#
# SCSI device support
#
CONFIG_SCSI_MOD=y
# CONFIG_RAID_ATTRS is not set
# CONFIG_SCSI is not set
# end of SCSI device support

# CONFIG_ATA is not set
# CONFIG_MD is not set
# CONFIG_TARGET_CORE is not set
# CONFIG_FUSION is not set

#
# IEEE 1394 (FireWire) support
#
# CONFIG_FIREWIRE is not set
# CONFIG_FIREWIRE_NOSY is not set
# end of IEEE 1394 (FireWire) support

# CONFIG_MACINTOSH_DRIVERS is not set
CONFIG_NETDEVICES=y
CONFIG_NET_CORE=y
# CONFIG_BONDING is not set
# CONFIG_DUMMY is not set
# CONFIG_WIREGUARD is not set
# CONFIG_EQUALIZER is not set
# CONFIG_NET_TEAM is not set
# CONFIG_MACVLAN is not set
# CONFIG_IPVLAN is not set
# CONFIG_VXLAN is not set
# CONFIG_GENEVE is not set
# CONFIG_BAREUDP is not set
# CONFIG_GTP is not set
# CONFIG_MACSEC is not set
# CONFIG_NETCONSOLE is not set
CONFIG_TUN=y
# CONFIG_TUN_VNET_CROSS_LE is not set
CONFIG_VETH=y
CONFIG_VIRTIO_NET=y
CONFIG_NLMON=y
# CONFIG_ARCNET is not set

#
# Distributed Switch Architecture drivers
#
# end of Distributed Switch Architecture drivers

CONFIG_ETHERNET=y
# CONFIG_NET_VENDOR_3COM is not set
# CONFIG_NET_VENDOR_ADAPTEC is not set
# CONFIG_NET_VENDOR_AGERE is not set
# CONFIG_NET_VENDOR_ALACRITECH is not set
# CONFIG_NET_VENDOR_ALTEON is not set
# CONFIG_ALTERA_TSE is not set
# CONFIG_NET_VENDOR_AMAZON is not set
# CONFIG_NET_VENDOR_AMD is not set
# CONFIG_NET_VENDOR_AQUANTIA is not set
# CONFIG_NET_VENDOR_ARC is not set
# CONFIG_NET_VENDOR_ATHEROS is not set
# CONFIG_NET_VENDOR_AURORA is not set
# CONFIG_NET_VENDOR_BROADCOM is not set
# CONFIG_NET_VENDOR_BROCADE is not set
# CONFIG_NET_VENDOR_CADENCE is not set
# CONFIG_NET_VENDOR_CAVIUM is not set
# CONFIG_NET_VENDOR_CHELSIO is not set
# CONFIG_NET_VENDOR_CISCO is not set
# CONFIG_NET_VENDOR_CORTINA is not set
# CONFIG_CX_ECAT is not set
# CONFIG_DNET is not set
# CONFIG_NET_VENDOR_DEC is not set
# CONFIG_NET_VENDOR_DLINK is not set
# CONFIG_NET_VENDOR_EMULEX is not set
# CONFIG_NET_VENDOR_EZCHIP is not set
# CONFIG_NET_VENDOR_GOOGLE is not set
# CONFIG_NET_VENDOR_HUAWEI is not set
# CONFIG_NET_VENDOR_INTEL is not set
# CONFIG_JME is not set
# CONFIG_NET_VENDOR_MARVELL is not set
# CONFIG_NET_VENDOR_MELLANOX is not set
# CONFIG_NET_VENDOR_MICREL is not set
# CONFIG_NET_VENDOR_MICROCHIP is not set
# CONFIG_NET_VENDOR_MICROSEMI is not set
# CONFIG_NET_VENDOR_MYRI is not set
# CONFIG_FEALNX is not set
# CONFIG_NET_VENDOR_NATSEMI is not set
# CONFIG_NET_VENDOR_NETERION is not set
# CONFIG_NET_VENDOR_NETRONOME is not set
# CONFIG_NET_VENDOR_NI is not set
# CONFIG_NET_VENDOR_NVIDIA is not set
# CONFIG_NET_VENDOR_OKI is not set
# CONFIG_ETHOC is not set
# CONFIG_NET_VENDOR_PACKET_ENGINES is not set
# CONFIG_NET_VENDOR_PENSANDO is not set
# CONFIG_NET_VENDOR_QLOGIC is not set
# CONFIG_NET_VENDOR_QUALCOMM is not set
# CONFIG_NET_VENDOR_RDC is not set
# CONFIG_NET_VENDOR_REALTEK is not set
# CONFIG_NET_VENDOR_RENESAS is not set
# CONFIG_NET_VENDOR_ROCKER is not set
# CONFIG_NET_VENDOR_SAMSUNG is not set
# CONFIG_NET_VENDOR_SEEQ is not set
# CONFIG_NET_VENDOR_SOLARFLARE is not set
# CONFIG_NET_VENDOR_SILAN is not set
# CONFIG_NET_VENDOR_SIS is not set
# CONFIG_NET_VENDOR_SMSC is not set
# CONFIG_NET_VENDOR_SOCIONEXT is not set
# CONFIG_NET_VENDOR_STMICRO is not set
# CONFIG_NET_VENDOR_SUN is not set
# CONFIG_NET_VENDOR_SYNOPSYS is not set
# CONFIG_NET_VENDOR_TEHUTI is not set
# CONFIG_NET_VENDOR_TI is not set
# CONFIG_NET_VENDOR_VIA is not set
# CONFIG_NET_VENDOR_WIZNET is not set
# CONFIG_NET_VENDOR_XILINX is not set
# CONFIG_FDDI is not set
# CONFIG_HIPPI is not set
# CONFIG_NET_SB1000 is not set
CONFIG_MDIO_DEVICE=y
CONFIG_MDIO_BUS=y
# CONFIG_MDIO_BCM_UNIMAC is not set
# CONFIG_MDIO_BITBANG is not set
# CONFIG_MDIO_MSCC_MIIM is not set
# CONFIG_MDIO_THUNDER is not set
# CONFIG_MDIO_XPCS is not set
CONFIG_PHYLIB=y

#
# MII PHY device drivers
#
# CONFIG_ADIN_PHY is not set
# CONFIG_AMD_PHY is not set
# CONFIG_AQUANTIA_PHY is not set
# CONFIG_AX88796B_PHY is not set
# CONFIG_BCM7XXX_PHY is not set
# CONFIG_BCM87XX_PHY is not set
# CONFIG_BROADCOM_PHY is not set
# CONFIG_BCM84881_PHY is not set
# CONFIG_CICADA_PHY is not set
# CONFIG_CORTINA_PHY is not set
# CONFIG_DAVICOM_PHY is not set
# CONFIG_DP83822_PHY is not set
# CONFIG_DP83TC811_PHY is not set
# CONFIG_DP83848_PHY is not set
# CONFIG_DP83867_PHY is not set
# CONFIG_DP83869_PHY is not set
# CONFIG_FIXED_PHY is not set
# CONFIG_ICPLUS_PHY is not set
# CONFIG_INTEL_XWAY_PHY is not set
# CONFIG_LSI_ET1011C_PHY is not set
# CONFIG_LXT_PHY is not set
# CONFIG_MARVELL_PHY is not set
# CONFIG_MARVELL_10G_PHY is not set
# CONFIG_MICREL_PHY is not set
# CONFIG_MICROCHIP_PHY is not set
# CONFIG_MICROCHIP_T1_PHY is not set
# CONFIG_MICROSEMI_PHY is not set
# CONFIG_NATIONAL_PHY is not set
# CONFIG_QSEMI_PHY is not set
# CONFIG_REALTEK_PHY is not set
# CONFIG_RENESAS_PHY is not set
# CONFIG_ROCKCHIP_PHY is not set
# CONFIG_SMSC_PHY is not set
# CONFIG_STE10XP is not set
# CONFIG_TERANETICS_PHY is not set
# CONFIG_VITESSE_PHY is not set
# CONFIG_XILINX_GMII2RGMII is not set
# CONFIG_PPP is not set
# CONFIG_SLIP is not set

#
# Host-side USB support is needed for USB Network Adapter support
#
# CONFIG_WLAN is not set

#
# Enable WiMAX (Networking options) to see the WiMAX drivers
#
# CONFIG_WAN is not set
# CONFIG_VMXNET3 is not set
# CONFIG_FUJITSU_ES is not set
# CONFIG_NETDEVSIM is not set
CONFIG_NET_FAILOVER=y
# CONFIG_ISDN is not set
# CONFIG_NVM is not set

#
# Input device support
#
CONFIG_INPUT=y
CONFIG_INPUT_FF_MEMLESS=y
CONFIG_INPUT_POLLDEV=y
CONFIG_INPUT_SPARSEKMAP=y
# CONFIG_INPUT_MATRIXKMAP is not set

#
# Userland interfaces
#
# CONFIG_INPUT_MOUSEDEV is not set
# CONFIG_INPUT_JOYDEV is not set
CONFIG_INPUT_EVDEV=y
# CONFIG_INPUT_EVBUG is not set

#
# Input Device Drivers
#
CONFIG_INPUT_KEYBOARD=y
CONFIG_KEYBOARD_ATKBD=y
# CONFIG_KEYBOARD_LKKBD is not set
# CONFIG_KEYBOARD_NEWTON is not set
# CONFIG_KEYBOARD_OPENCORES is not set
# CONFIG_KEYBOARD_SAMSUNG is not set
# CONFIG_KEYBOARD_STOWAWAY is not set
# CONFIG_KEYBOARD_SUNKBD is not set
# CONFIG_KEYBOARD_XTKBD is not set
# CONFIG_INPUT_MOUSE is not set
# CONFIG_INPUT_JOYSTICK is not set
# CONFIG_INPUT_TABLET is not set
# CONFIG_INPUT_TOUCHSCREEN is not set
# CONFIG_INPUT_MISC is not set
# CONFIG_RMI4_CORE is not set

#
# Hardware I/O ports
#
CONFIG_SERIO=y
CONFIG_ARCH_MIGHT_HAVE_PC_SERIO=y
CONFIG_SERIO_I8042=y
CONFIG_SERIO_SERPORT=y
# CONFIG_SERIO_CT82C710 is not set
# CONFIG_SERIO_PCIPS2 is not set
CONFIG_SERIO_LIBPS2=y
# CONFIG_SERIO_RAW is not set
# CONFIG_SERIO_ALTERA_PS2 is not set
# CONFIG_SERIO_PS2MULT is not set
# CONFIG_SERIO_ARC_PS2 is not set
# CONFIG_USERIO is not set
# CONFIG_GAMEPORT is not set
# end of Hardware I/O ports
# end of Input device support

#
# Character devices
#
CONFIG_TTY=y
CONFIG_VT=y
CONFIG_CONSOLE_TRANSLATIONS=y
CONFIG_VT_CONSOLE=y
CONFIG_HW_CONSOLE=y
CONFIG_VT_HW_CONSOLE_BINDING=y
CONFIG_UNIX98_PTYS=y
CONFIG_LEGACY_PTYS=y
CONFIG_LEGACY_PTY_COUNT=256
CONFIG_LDISC_AUTOLOAD=y

#
# Serial drivers
#
CONFIG_SERIAL_EARLYCON=y
CONFIG_SERIAL_8250=y
CONFIG_SERIAL_8250_DEPRECATED_OPTIONS=y
CONFIG_SERIAL_8250_PNP=y
# CONFIG_SERIAL_8250_16550A_VARIANTS is not set
# CONFIG_SERIAL_8250_FINTEK is not set
CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_SERIAL_8250_PCI=y
CONFIG_SERIAL_8250_EXAR=y
CONFIG_SERIAL_8250_NR_UARTS=4
CONFIG_SERIAL_8250_RUNTIME_UARTS=4
# CONFIG_SERIAL_8250_EXTENDED is not set
CONFIG_SERIAL_8250_DWLIB=y
# CONFIG_SERIAL_8250_DW is not set
# CONFIG_SERIAL_8250_RT288X is not set
CONFIG_SERIAL_8250_LPSS=y
CONFIG_SERIAL_8250_MID=y

#
# Non-8250 serial port support
#
# CONFIG_SERIAL_KGDB_NMI is not set
# CONFIG_SERIAL_UARTLITE is not set
CONFIG_SERIAL_CORE=y
CONFIG_SERIAL_CORE_CONSOLE=y
CONFIG_CONSOLE_POLL=y
# CONFIG_SERIAL_JSM is not set
# CONFIG_SERIAL_SCCNXP is not set
# CONFIG_SERIAL_ALTERA_JTAGUART is not set
# CONFIG_SERIAL_ALTERA_UART is not set
# CONFIG_SERIAL_ARC is not set
# CONFIG_SERIAL_RP2 is not set
# CONFIG_SERIAL_FSL_LPUART is not set
# CONFIG_SERIAL_FSL_LINFLEXUART is not set
# CONFIG_SERIAL_SPRD is not set
# end of Serial drivers

# CONFIG_SERIAL_NONSTANDARD is not set
# CONFIG_N_GSM is not set
# CONFIG_NOZOMI is not set
# CONFIG_NULL_TTY is not set
# CONFIG_TRACE_SINK is not set
CONFIG_HVC_DRIVER=y
CONFIG_SERIAL_DEV_BUS=y
CONFIG_SERIAL_DEV_CTRL_TTYPORT=y
CONFIG_VIRTIO_CONSOLE=y
# CONFIG_IPMI_HANDLER is not set
CONFIG_HW_RANDOM=y
# CONFIG_HW_RANDOM_TIMERIOMEM is not set
# CONFIG_HW_RANDOM_INTEL is not set
# CONFIG_HW_RANDOM_AMD is not set
# CONFIG_HW_RANDOM_VIA is not set
CONFIG_HW_RANDOM_VIRTIO=y
# CONFIG_APPLICOM is not set
# CONFIG_MWAVE is not set
CONFIG_DEVMEM=y
CONFIG_DEVKMEM=y
# CONFIG_NVRAM is not set
# CONFIG_RAW_DRIVER is not set
CONFIG_DEVPORT=y
# CONFIG_HPET is not set
# CONFIG_HANGCHECK_TIMER is not set
# CONFIG_TCG_TPM is not set
# CONFIG_TELCLOCK is not set
# CONFIG_XILLYBUS is not set
# end of Character devices

# CONFIG_RANDOM_TRUST_CPU is not set
# CONFIG_RANDOM_TRUST_BOOTLOADER is not set

#
# I2C support
#
# CONFIG_I2C is not set
# end of I2C support

# CONFIG_I3C is not set
# CONFIG_SPI is not set
# CONFIG_SPMI is not set
# CONFIG_HSI is not set
CONFIG_PPS=y
# CONFIG_PPS_DEBUG is not set

#
# PPS clients support
#
# CONFIG_PPS_CLIENT_KTIMER is not set
# CONFIG_PPS_CLIENT_LDISC is not set
# CONFIG_PPS_CLIENT_GPIO is not set

#
# PPS generators support
#

#
# PTP clock support
#
CONFIG_PTP_1588_CLOCK=y

#
# Enable PHYLIB and NETWORK_PHY_TIMESTAMPING to see the additional clocks.
#
CONFIG_PTP_1588_CLOCK_KVM=y
# CONFIG_PTP_1588_CLOCK_VMW is not set
# end of PTP clock support

# CONFIG_PINCTRL is not set
# CONFIG_GPIOLIB is not set
# CONFIG_W1 is not set
# CONFIG_POWER_AVS is not set
# CONFIG_POWER_RESET is not set
CONFIG_POWER_SUPPLY=y
# CONFIG_POWER_SUPPLY_DEBUG is not set
# CONFIG_PDA_POWER is not set
# CONFIG_TEST_POWER is not set
# CONFIG_BATTERY_DS2780 is not set
# CONFIG_BATTERY_DS2781 is not set
# CONFIG_BATTERY_BQ27XXX is not set
# CONFIG_CHARGER_MAX8903 is not set
# CONFIG_HWMON is not set
CONFIG_THERMAL=y
# CONFIG_THERMAL_STATISTICS is not set
CONFIG_THERMAL_EMERGENCY_POWEROFF_DELAY_MS=0
# CONFIG_THERMAL_WRITABLE_TRIPS is not set
CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE=y
# CONFIG_THERMAL_DEFAULT_GOV_FAIR_SHARE is not set
# CONFIG_THERMAL_DEFAULT_GOV_USER_SPACE is not set
# CONFIG_THERMAL_GOV_FAIR_SHARE is not set
CONFIG_THERMAL_GOV_STEP_WISE=y
# CONFIG_THERMAL_GOV_BANG_BANG is not set
# CONFIG_THERMAL_GOV_USER_SPACE is not set
# CONFIG_THERMAL_EMULATION is not set

#
# Intel thermal drivers
#
# CONFIG_INTEL_POWERCLAMP is not set
# CONFIG_INTEL_SOC_DTS_THERMAL is not set

#
# ACPI INT340X thermal drivers
#
# CONFIG_INT340X_THERMAL is not set
# end of ACPI INT340X thermal drivers

# CONFIG_INTEL_PCH_THERMAL is not set
# end of Intel thermal drivers

# CONFIG_WATCHDOG is not set
CONFIG_SSB_POSSIBLE=y
CONFIG_SSB=y
CONFIG_SSB_SPROM=y
CONFIG_SSB_PCIHOST_POSSIBLE=y
CONFIG_SSB_PCIHOST=y
CONFIG_SSB_DRIVER_PCICORE_POSSIBLE=y
# CONFIG_SSB_DRIVER_PCICORE is not set
CONFIG_BCMA_POSSIBLE=y
# CONFIG_BCMA is not set

#
# Multifunction device drivers
#
# CONFIG_MFD_MADERA is not set
# CONFIG_HTC_PASIC3 is not set
# CONFIG_MFD_INTEL_QUARK_I2C_GPIO is not set
# CONFIG_LPC_ICH is not set
# CONFIG_LPC_SCH is not set
# CONFIG_MFD_INTEL_LPSS_ACPI is not set
# CONFIG_MFD_INTEL_LPSS_PCI is not set
# CONFIG_MFD_JANZ_CMODIO is not set
# CONFIG_MFD_KEMPLD is not set
# CONFIG_MFD_MT6397 is not set
# CONFIG_MFD_RDC321X is not set
# CONFIG_MFD_SM501 is not set
# CONFIG_ABX500_CORE is not set
# CONFIG_MFD_SYSCON is not set
# CONFIG_MFD_TI_AM335X_TSCADC is not set
# CONFIG_MFD_TQMX86 is not set
# CONFIG_MFD_VX855 is not set
# CONFIG_RAVE_SP_CORE is not set
# end of Multifunction device drivers

# CONFIG_REGULATOR is not set
# CONFIG_RC_CORE is not set
# CONFIG_MEDIA_SUPPORT is not set

#
# Graphics support
#
# CONFIG_AGP is not set
CONFIG_VGA_ARB=y
CONFIG_VGA_ARB_MAX_GPUS=1
# CONFIG_VGA_SWITCHEROO is not set
# CONFIG_DRM is not set

#
# ARM devices
#
# end of ARM devices

#
# Frame buffer Devices
#
# CONFIG_FB is not set
# end of Frame buffer Devices

#
# Backlight & LCD device support
#
# CONFIG_LCD_CLASS_DEVICE is not set
# CONFIG_BACKLIGHT_CLASS_DEVICE is not set
# end of Backlight & LCD device support

#
# Console display driver support
#
CONFIG_VGA_CONSOLE=y
CONFIG_VGACON_SOFT_SCROLLBACK=y
CONFIG_VGACON_SOFT_SCROLLBACK_SIZE=64
# CONFIG_VGACON_SOFT_SCROLLBACK_PERSISTENT_ENABLE_BY_DEFAULT is not set
CONFIG_DUMMY_CONSOLE=y
CONFIG_DUMMY_CONSOLE_COLUMNS=80
CONFIG_DUMMY_CONSOLE_ROWS=25
# end of Console display driver support
# end of Graphics support

# CONFIG_SOUND is not set

#
# HID support
#
CONFIG_HID=y
# CONFIG_HID_BATTERY_STRENGTH is not set
# CONFIG_HIDRAW is not set
# CONFIG_UHID is not set
CONFIG_HID_GENERIC=y

#
# Special HID drivers
#
CONFIG_HID_A4TECH=y
# CONFIG_HID_ACRUX is not set
CONFIG_HID_APPLE=y
# CONFIG_HID_AUREAL is not set
CONFIG_HID_BELKIN=y
CONFIG_HID_CHERRY=y
CONFIG_HID_CHICONY=y
# CONFIG_HID_COUGAR is not set
# CONFIG_HID_MACALLY is not set
# CONFIG_HID_CMEDIA is not set
CONFIG_HID_CYPRESS=y
# CONFIG_HID_DRAGONRISE is not set
# CONFIG_HID_EMS_FF is not set
# CONFIG_HID_ELECOM is not set
CONFIG_HID_EZKEY=y
# CONFIG_HID_GEMBIRD is not set
# CONFIG_HID_GFRM is not set
# CONFIG_HID_GLORIOUS is not set
# CONFIG_HID_KEYTOUCH is not set
# CONFIG_HID_KYE is not set
# CONFIG_HID_WALTOP is not set
# CONFIG_HID_VIEWSONIC is not set
# CONFIG_HID_GYRATION is not set
# CONFIG_HID_ICADE is not set
CONFIG_HID_ITE=y
# CONFIG_HID_JABRA is not set
# CONFIG_HID_TWINHAN is not set
CONFIG_HID_KENSINGTON=y
# CONFIG_HID_LCPOWER is not set
# CONFIG_HID_LENOVO is not set
# CONFIG_HID_MAGICMOUSE is not set
# CONFIG_HID_MALTRON is not set
# CONFIG_HID_MAYFLASH is not set
CONFIG_HID_REDRAGON=y
CONFIG_HID_MICROSOFT=y
CONFIG_HID_MONTEREY=y
# CONFIG_HID_MULTITOUCH is not set
# CONFIG_HID_NTI is not set
# CONFIG_HID_ORTEK is not set
# CONFIG_HID_PANTHERLORD is not set
# CONFIG_HID_PETALYNX is not set
# CONFIG_HID_PICOLCD is not set
# CONFIG_HID_PLANTRONICS is not set
# CONFIG_HID_PRIMAX is not set
# CONFIG_HID_SAITEK is not set
# CONFIG_HID_SAMSUNG is not set
# CONFIG_HID_SPEEDLINK is not set
# CONFIG_HID_STEAM is not set
# CONFIG_HID_STEELSERIES is not set
# CONFIG_HID_SUNPLUS is not set
# CONFIG_HID_RMI is not set
# CONFIG_HID_GREENASIA is not set
# CONFIG_HID_SMARTJOYPLUS is not set
# CONFIG_HID_TIVO is not set
# CONFIG_HID_TOPSEED is not set
# CONFIG_HID_THRUSTMASTER is not set
# CONFIG_HID_UDRAW_PS3 is not set
# CONFIG_HID_XINMO is not set
# CONFIG_HID_ZEROPLUS is not set
# CONFIG_HID_ZYDACRON is not set
# CONFIG_HID_SENSOR_HUB is not set
# CONFIG_HID_ALPS is not set
# end of Special HID drivers

#
# Intel ISH HID support
#
# CONFIG_INTEL_ISH_HID is not set
# end of Intel ISH HID support
# end of HID support

CONFIG_USB_OHCI_LITTLE_ENDIAN=y
# CONFIG_USB_SUPPORT is not set
# CONFIG_MMC is not set
# CONFIG_MEMSTICK is not set
# CONFIG_NEW_LEDS is not set
# CONFIG_ACCESSIBILITY is not set
# CONFIG_INFINIBAND is not set
CONFIG_EDAC_ATOMIC_SCRUB=y
CONFIG_EDAC_SUPPORT=y
CONFIG_RTC_LIB=y
CONFIG_RTC_MC146818_LIB=y
CONFIG_RTC_CLASS=y
CONFIG_RTC_HCTOSYS=y
CONFIG_RTC_HCTOSYS_DEVICE="rtc0"
CONFIG_RTC_SYSTOHC=y
CONFIG_RTC_SYSTOHC_DEVICE="rtc0"
# CONFIG_RTC_DEBUG is not set
CONFIG_RTC_NVMEM=y

#
# RTC interfaces
#
CONFIG_RTC_INTF_SYSFS=y
CONFIG_RTC_INTF_PROC=y
CONFIG_RTC_INTF_DEV=y
# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set
# CONFIG_RTC_DRV_TEST is not set

#
# I2C RTC drivers
#

#
# SPI RTC drivers
#

#
# SPI and I2C RTC drivers
#

#
# Platform RTC drivers
#
CONFIG_RTC_DRV_CMOS=y
# CONFIG_RTC_DRV_DS1286 is not set
# CONFIG_RTC_DRV_DS1511 is not set
# CONFIG_RTC_DRV_DS1553 is not set
# CONFIG_RTC_DRV_DS1685_FAMILY is not set
# CONFIG_RTC_DRV_DS1742 is not set
# CONFIG_RTC_DRV_DS2404 is not set
# CONFIG_RTC_DRV_STK17TA8 is not set
# CONFIG_RTC_DRV_M48T86 is not set
# CONFIG_RTC_DRV_M48T35 is not set
# CONFIG_RTC_DRV_M48T59 is not set
# CONFIG_RTC_DRV_MSM6242 is not set
# CONFIG_RTC_DRV_BQ4802 is not set
# CONFIG_RTC_DRV_RP5C01 is not set
# CONFIG_RTC_DRV_V3020 is not set

#
# on-CPU RTC drivers
#
# CONFIG_RTC_DRV_FTRTC010 is not set

#
# HID Sensor RTC drivers
#
# CONFIG_DMADEVICES is not set

#
# DMABUF options
#
# CONFIG_SYNC_FILE is not set
# CONFIG_DMABUF_MOVE_NOTIFY is not set
# CONFIG_DMABUF_HEAPS is not set
# end of DMABUF options

# CONFIG_AUXDISPLAY is not set
# CONFIG_UIO is not set
# CONFIG_VIRT_DRIVERS is not set
CONFIG_VIRTIO=y
CONFIG_VIRTIO_MENU=y
CONFIG_VIRTIO_PCI=y
CONFIG_VIRTIO_PCI_LEGACY=y
CONFIG_VIRTIO_VDPA=y
CONFIG_VIRTIO_BALLOON=y
CONFIG_VIRTIO_INPUT=y
CONFIG_VIRTIO_MMIO=y
CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES=y
CONFIG_VDPA=y
# CONFIG_VDPA_MENU is not set
CONFIG_VHOST_IOTLB=y
CONFIG_VHOST=y
CONFIG_VHOST_MENU=y
# CONFIG_VHOST_NET is not set
CONFIG_VHOST_VDPA=y
# CONFIG_VHOST_CROSS_ENDIAN_LEGACY is not set

#
# Microsoft Hyper-V guest support
#
# CONFIG_HYPERV is not set
# end of Microsoft Hyper-V guest support

# CONFIG_GREYBUS is not set
# CONFIG_STAGING is not set
# CONFIG_X86_PLATFORM_DEVICES is not set
CONFIG_PMC_ATOM=y
# CONFIG_MFD_CROS_EC is not set
# CONFIG_CHROME_PLATFORMS is not set
# CONFIG_MELLANOX_PLATFORM is not set
CONFIG_CLKDEV_LOOKUP=y
CONFIG_HAVE_CLK_PREPARE=y
CONFIG_COMMON_CLK=y

#
# Common Clock Framework
#
# end of Common Clock Framework

# CONFIG_HWSPINLOCK is not set

#
# Clock Source drivers
#
CONFIG_CLKEVT_I8253=y
CONFIG_I8253_LOCK=y
CONFIG_CLKBLD_I8253=y
# end of Clock Source drivers

CONFIG_MAILBOX=y
CONFIG_PCC=y
# CONFIG_ALTERA_MBOX is not set
CONFIG_IOMMU_SUPPORT=y

#
# Generic IOMMU Pagetable Support
#
# end of Generic IOMMU Pagetable Support

# CONFIG_IOMMU_DEBUGFS is not set
# CONFIG_AMD_IOMMU is not set
# CONFIG_INTEL_IOMMU is not set
# CONFIG_IRQ_REMAP is not set

#
# Remoteproc drivers
#
# CONFIG_REMOTEPROC is not set
# end of Remoteproc drivers

#
# Rpmsg drivers
#
# CONFIG_RPMSG_QCOM_GLINK_RPM is not set
# CONFIG_RPMSG_VIRTIO is not set
# end of Rpmsg drivers

# CONFIG_SOUNDWIRE is not set

#
# SOC (System On Chip) specific Drivers
#

#
# Amlogic SoC drivers
#
# end of Amlogic SoC drivers

#
# Aspeed SoC drivers
#
# end of Aspeed SoC drivers

#
# Broadcom SoC drivers
#
# end of Broadcom SoC drivers

#
# NXP/Freescale QorIQ SoC drivers
#
# end of NXP/Freescale QorIQ SoC drivers

#
# i.MX SoC drivers
#
# end of i.MX SoC drivers

#
# Qualcomm SoC drivers
#
# end of Qualcomm SoC drivers

# CONFIG_SOC_TI is not set

#
# Xilinx SoC drivers
#
# CONFIG_XILINX_VCU is not set
# end of Xilinx SoC drivers
# end of SOC (System On Chip) specific Drivers

# CONFIG_PM_DEVFREQ is not set
# CONFIG_EXTCON is not set
# CONFIG_MEMORY is not set
# CONFIG_IIO is not set
# CONFIG_NTB is not set
# CONFIG_VME_BUS is not set
# CONFIG_PWM is not set

#
# IRQ chip support
#
# end of IRQ chip support

# CONFIG_IPACK_BUS is not set
# CONFIG_RESET_CONTROLLER is not set

#
# PHY Subsystem
#
# CONFIG_GENERIC_PHY is not set
# CONFIG_BCM_KONA_USB2_PHY is not set
# CONFIG_PHY_PXA_28NM_HSIC is not set
# CONFIG_PHY_PXA_28NM_USB2 is not set
# CONFIG_PHY_INTEL_EMMC is not set
# end of PHY Subsystem

# CONFIG_POWERCAP is not set
# CONFIG_MCB is not set

#
# Performance monitor support
#
# end of Performance monitor support

# CONFIG_RAS is not set
# CONFIG_USB4 is not set

#
# Android
#
# CONFIG_ANDROID is not set
# end of Android

# CONFIG_LIBNVDIMM is not set
# CONFIG_DAX is not set
CONFIG_NVMEM=y
CONFIG_NVMEM_SYSFS=y

#
# HW tracing support
#
# CONFIG_STM is not set
# CONFIG_INTEL_TH is not set
# end of HW tracing support

# CONFIG_FPGA is not set
# CONFIG_TEE is not set
# CONFIG_UNISYS_VISORBUS is not set
# CONFIG_SIOX is not set
# CONFIG_SLIMBUS is not set
# CONFIG_INTERCONNECT is not set
# CONFIG_COUNTER is not set
# CONFIG_MOST is not set
# end of Device Drivers

#
# File systems
#
CONFIG_DCACHE_WORD_ACCESS=y
# CONFIG_VALIDATE_FS_PARSER is not set
CONFIG_FS_IOMAP=y
# CONFIG_EXT2_FS is not set
# CONFIG_EXT3_FS is not set
CONFIG_EXT4_FS=y
CONFIG_EXT4_USE_FOR_EXT2=y
CONFIG_EXT4_FS_POSIX_ACL=y
CONFIG_EXT4_FS_SECURITY=y
# CONFIG_EXT4_DEBUG is not set
# CONFIG_EXT4_KUNIT_TESTS is not set
CONFIG_JBD2=y
# CONFIG_JBD2_DEBUG is not set
CONFIG_FS_MBCACHE=y
# CONFIG_REISERFS_FS is not set
# CONFIG_JFS_FS is not set
# CONFIG_XFS_FS is not set
# CONFIG_GFS2_FS is not set
# CONFIG_OCFS2_FS is not set
# CONFIG_BTRFS_FS is not set
# CONFIG_NILFS2_FS is not set
# CONFIG_F2FS_FS is not set
# CONFIG_FS_DAX is not set
CONFIG_FS_POSIX_ACL=y
CONFIG_EXPORTFS=y
# CONFIG_EXPORTFS_BLOCK_OPS is not set
CONFIG_FILE_LOCKING=y
CONFIG_MANDATORY_FILE_LOCKING=y
# CONFIG_FS_ENCRYPTION is not set
# CONFIG_FS_VERITY is not set
CONFIG_FSNOTIFY=y
CONFIG_DNOTIFY=y
CONFIG_INOTIFY_USER=y
# CONFIG_FANOTIFY is not set
CONFIG_QUOTA=y
CONFIG_QUOTA_NETLINK_INTERFACE=y
CONFIG_PRINT_QUOTA_WARNING=y
# CONFIG_QUOTA_DEBUG is not set
CONFIG_QUOTA_TREE=y
# CONFIG_QFMT_V1 is not set
CONFIG_QFMT_V2=y
CONFIG_QUOTACTL=y
CONFIG_AUTOFS4_FS=y
CONFIG_AUTOFS_FS=y
# CONFIG_FUSE_FS is not set
# CONFIG_OVERLAY_FS is not set

#
# Caches
#
# CONFIG_FSCACHE is not set
# end of Caches

#
# CD-ROM/DVD Filesystems
#
# CONFIG_ISO9660_FS is not set
# CONFIG_UDF_FS is not set
# end of CD-ROM/DVD Filesystems

#
# DOS/FAT/EXFAT/NT Filesystems
#
# CONFIG_MSDOS_FS is not set
# CONFIG_VFAT_FS is not set
# CONFIG_EXFAT_FS is not set
# CONFIG_NTFS_FS is not set
# end of DOS/FAT/EXFAT/NT Filesystems

#
# Pseudo filesystems
#
CONFIG_PROC_FS=y
CONFIG_PROC_KCORE=y
CONFIG_PROC_VMCORE=y
# CONFIG_PROC_VMCORE_DEVICE_DUMP is not set
CONFIG_PROC_SYSCTL=y
CONFIG_PROC_PAGE_MONITOR=y
# CONFIG_PROC_CHILDREN is not set
CONFIG_PROC_PID_ARCH_STATUS=y
CONFIG_KERNFS=y
CONFIG_SYSFS=y
CONFIG_TMPFS=y
CONFIG_TMPFS_POSIX_ACL=y
CONFIG_TMPFS_XATTR=y
CONFIG_HUGETLBFS=y
CONFIG_HUGETLB_PAGE=y
CONFIG_MEMFD_CREATE=y
CONFIG_ARCH_HAS_GIGANTIC_PAGE=y
CONFIG_CONFIGFS_FS=y
# end of Pseudo filesystems

# CONFIG_MISC_FILESYSTEMS is not set
CONFIG_NETWORK_FILESYSTEMS=y
# CONFIG_NFS_FS is not set
# CONFIG_NFSD is not set
# CONFIG_CEPH_FS is not set
# CONFIG_CIFS is not set
# CONFIG_CODA_FS is not set
# CONFIG_AFS_FS is not set
CONFIG_9P_FS=y
CONFIG_9P_FS_POSIX_ACL=y
CONFIG_9P_FS_SECURITY=y
CONFIG_NLS=y
CONFIG_NLS_DEFAULT="utf8"
# CONFIG_NLS_CODEPAGE_437 is not set
# CONFIG_NLS_CODEPAGE_737 is not set
# CONFIG_NLS_CODEPAGE_775 is not set
CONFIG_NLS_CODEPAGE_850=y
# CONFIG_NLS_CODEPAGE_852 is not set
# CONFIG_NLS_CODEPAGE_855 is not set
# CONFIG_NLS_CODEPAGE_857 is not set
# CONFIG_NLS_CODEPAGE_860 is not set
# CONFIG_NLS_CODEPAGE_861 is not set
# CONFIG_NLS_CODEPAGE_862 is not set
# CONFIG_NLS_CODEPAGE_863 is not set
# CONFIG_NLS_CODEPAGE_864 is not set
# CONFIG_NLS_CODEPAGE_865 is not set
# CONFIG_NLS_CODEPAGE_866 is not set
# CONFIG_NLS_CODEPAGE_869 is not set
# CONFIG_NLS_CODEPAGE_936 is not set
# CONFIG_NLS_CODEPAGE_950 is not set
# CONFIG_NLS_CODEPAGE_932 is not set
# CONFIG_NLS_CODEPAGE_949 is not set
# CONFIG_NLS_CODEPAGE_874 is not set
# CONFIG_NLS_ISO8859_8 is not set
# CONFIG_NLS_CODEPAGE_1250 is not set
# CONFIG_NLS_CODEPAGE_1251 is not set
CONFIG_NLS_ASCII=y
CONFIG_NLS_ISO8859_1=y
# CONFIG_NLS_ISO8859_2 is not set
# CONFIG_NLS_ISO8859_3 is not set
# CONFIG_NLS_ISO8859_4 is not set
# CONFIG_NLS_ISO8859_5 is not set
# CONFIG_NLS_ISO8859_6 is not set
# CONFIG_NLS_ISO8859_7 is not set
# CONFIG_NLS_ISO8859_9 is not set
# CONFIG_NLS_ISO8859_13 is not set
# CONFIG_NLS_ISO8859_14 is not set
CONFIG_NLS_ISO8859_15=y
# CONFIG_NLS_KOI8_R is not set
# CONFIG_NLS_KOI8_U is not set
# CONFIG_NLS_MAC_ROMAN is not set
# CONFIG_NLS_MAC_CELTIC is not set
# CONFIG_NLS_MAC_CENTEURO is not set
# CONFIG_NLS_MAC_CROATIAN is not set
# CONFIG_NLS_MAC_CYRILLIC is not set
# CONFIG_NLS_MAC_GAELIC is not set
# CONFIG_NLS_MAC_GREEK is not set
# CONFIG_NLS_MAC_ICELAND is not set
# CONFIG_NLS_MAC_INUIT is not set
# CONFIG_NLS_MAC_ROMANIAN is not set
# CONFIG_NLS_MAC_TURKISH is not set
CONFIG_NLS_UTF8=y
# CONFIG_DLM is not set
# CONFIG_UNICODE is not set
CONFIG_IO_WQ=y
# end of File systems

#
# Security options
#
# CONFIG_KEYS is not set
# CONFIG_SECURITY_DMESG_RESTRICT is not set
# CONFIG_SECURITY is not set
# CONFIG_SECURITYFS is not set
CONFIG_PAGE_TABLE_ISOLATION=y
CONFIG_HAVE_HARDENED_USERCOPY_ALLOCATOR=y
# CONFIG_HARDENED_USERCOPY is not set
# CONFIG_FORTIFY_SOURCE is not set
# CONFIG_STATIC_USERMODEHELPER is not set
CONFIG_DEFAULT_SECURITY_DAC=y
CONFIG_LSM="lockdown,yama,loadpin,safesetid,integrity"

#
# Kernel hardening options
#

#
# Memory initialization
#
CONFIG_INIT_STACK_NONE=y
# CONFIG_INIT_ON_ALLOC_DEFAULT_ON is not set
# CONFIG_INIT_ON_FREE_DEFAULT_ON is not set
# end of Memory initialization
# end of Kernel hardening options
# end of Security options

CONFIG_CRYPTO=y

#
# Crypto core or helper
#
CONFIG_CRYPTO_ALGAPI=y
CONFIG_CRYPTO_ALGAPI2=y
CONFIG_CRYPTO_AEAD=y
CONFIG_CRYPTO_AEAD2=y
CONFIG_CRYPTO_SKCIPHER=y
CONFIG_CRYPTO_SKCIPHER2=y
CONFIG_CRYPTO_HASH=y
CONFIG_CRYPTO_HASH2=y
CONFIG_CRYPTO_RNG=y
CONFIG_CRYPTO_RNG2=y
CONFIG_CRYPTO_RNG_DEFAULT=y
CONFIG_CRYPTO_AKCIPHER2=y
CONFIG_CRYPTO_AKCIPHER=y
CONFIG_CRYPTO_KPP2=y
CONFIG_CRYPTO_KPP=y
CONFIG_CRYPTO_ACOMP2=y
CONFIG_CRYPTO_MANAGER=y
CONFIG_CRYPTO_MANAGER2=y
# CONFIG_CRYPTO_USER is not set
CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y
CONFIG_CRYPTO_GF128MUL=y
CONFIG_CRYPTO_NULL=y
CONFIG_CRYPTO_NULL2=y
CONFIG_CRYPTO_PCRYPT=y
CONFIG_CRYPTO_CRYPTD=y
CONFIG_CRYPTO_AUTHENC=y
# CONFIG_CRYPTO_TEST is not set
CONFIG_CRYPTO_SIMD=y
CONFIG_CRYPTO_GLUE_HELPER_X86=y
CONFIG_CRYPTO_ENGINE=y

#
# Public-key cryptography
#
CONFIG_CRYPTO_RSA=y
CONFIG_CRYPTO_DH=y
CONFIG_CRYPTO_ECC=y
CONFIG_CRYPTO_ECDH=y
CONFIG_CRYPTO_ECRDSA=y
CONFIG_CRYPTO_CURVE25519=y
CONFIG_CRYPTO_CURVE25519_X86=y

#
# Authenticated Encryption with Associated Data
#
CONFIG_CRYPTO_CCM=y
CONFIG_CRYPTO_GCM=y
CONFIG_CRYPTO_CHACHA20POLY1305=y
# CONFIG_CRYPTO_AEGIS128 is not set
# CONFIG_CRYPTO_AEGIS128_AESNI_SSE2 is not set
CONFIG_CRYPTO_SEQIV=y
CONFIG_CRYPTO_ECHAINIV=y

#
# Block modes
#
CONFIG_CRYPTO_CBC=y
# CONFIG_CRYPTO_CFB is not set
CONFIG_CRYPTO_CTR=y
# CONFIG_CRYPTO_CTS is not set
# CONFIG_CRYPTO_ECB is not set
# CONFIG_CRYPTO_LRW is not set
# CONFIG_CRYPTO_OFB is not set
# CONFIG_CRYPTO_PCBC is not set
# CONFIG_CRYPTO_XTS is not set
# CONFIG_CRYPTO_KEYWRAP is not set
# CONFIG_CRYPTO_NHPOLY1305_SSE2 is not set
# CONFIG_CRYPTO_NHPOLY1305_AVX2 is not set
# CONFIG_CRYPTO_ADIANTUM is not set
# CONFIG_CRYPTO_ESSIV is not set

#
# Hash modes
#
CONFIG_CRYPTO_CMAC=y
CONFIG_CRYPTO_HMAC=y
# CONFIG_CRYPTO_XCBC is not set
# CONFIG_CRYPTO_VMAC is not set

#
# Digest
#
CONFIG_CRYPTO_CRC32C=y
CONFIG_CRYPTO_CRC32C_INTEL=y
CONFIG_CRYPTO_CRC32=y
CONFIG_CRYPTO_CRC32_PCLMUL=y
# CONFIG_CRYPTO_XXHASH is not set
# CONFIG_CRYPTO_BLAKE2B is not set
# CONFIG_CRYPTO_BLAKE2S is not set
# CONFIG_CRYPTO_BLAKE2S_X86 is not set
CONFIG_CRYPTO_CRCT10DIF=y
# CONFIG_CRYPTO_CRCT10DIF_PCLMUL is not set
CONFIG_CRYPTO_GHASH=y
CONFIG_CRYPTO_POLY1305=y
CONFIG_CRYPTO_POLY1305_X86_64=y
# CONFIG_CRYPTO_MD4 is not set
CONFIG_CRYPTO_MD5=y
# CONFIG_CRYPTO_MICHAEL_MIC is not set
# CONFIG_CRYPTO_RMD128 is not set
# CONFIG_CRYPTO_RMD160 is not set
# CONFIG_CRYPTO_RMD256 is not set
# CONFIG_CRYPTO_RMD320 is not set
CONFIG_CRYPTO_SHA1=y
CONFIG_CRYPTO_SHA1_SSSE3=y
CONFIG_CRYPTO_SHA256_SSSE3=y
CONFIG_CRYPTO_SHA512_SSSE3=y
CONFIG_CRYPTO_SHA256=y
CONFIG_CRYPTO_SHA512=y
CONFIG_CRYPTO_SHA3=y
# CONFIG_CRYPTO_SM3 is not set
CONFIG_CRYPTO_STREEBOG=y
# CONFIG_CRYPTO_TGR192 is not set
# CONFIG_CRYPTO_WP512 is not set
# CONFIG_CRYPTO_GHASH_CLMUL_NI_INTEL is not set

#
# Ciphers
#
CONFIG_CRYPTO_AES=y
# CONFIG_CRYPTO_AES_TI is not set
CONFIG_CRYPTO_AES_NI_INTEL=y
# CONFIG_CRYPTO_ANUBIS is not set
# CONFIG_CRYPTO_ARC4 is not set
# CONFIG_CRYPTO_BLOWFISH is not set
# CONFIG_CRYPTO_BLOWFISH_X86_64 is not set
# CONFIG_CRYPTO_CAMELLIA is not set
# CONFIG_CRYPTO_CAMELLIA_X86_64 is not set
# CONFIG_CRYPTO_CAMELLIA_AESNI_AVX_X86_64 is not set
# CONFIG_CRYPTO_CAMELLIA_AESNI_AVX2_X86_64 is not set
# CONFIG_CRYPTO_CAST5 is not set
# CONFIG_CRYPTO_CAST5_AVX_X86_64 is not set
# CONFIG_CRYPTO_CAST6 is not set
# CONFIG_CRYPTO_CAST6_AVX_X86_64 is not set
CONFIG_CRYPTO_DES=y
CONFIG_CRYPTO_DES3_EDE_X86_64=y
# CONFIG_CRYPTO_FCRYPT is not set
# CONFIG_CRYPTO_KHAZAD is not set
# CONFIG_CRYPTO_SALSA20 is not set
CONFIG_CRYPTO_CHACHA20=y
CONFIG_CRYPTO_CHACHA20_X86_64=y
# CONFIG_CRYPTO_SEED is not set
# CONFIG_CRYPTO_SERPENT is not set
# CONFIG_CRYPTO_SERPENT_SSE2_X86_64 is not set
# CONFIG_CRYPTO_SERPENT_AVX_X86_64 is not set
# CONFIG_CRYPTO_SERPENT_AVX2_X86_64 is not set
# CONFIG_CRYPTO_SM4 is not set
# CONFIG_CRYPTO_TEA is not set
# CONFIG_CRYPTO_TWOFISH is not set
# CONFIG_CRYPTO_TWOFISH_X86_64 is not set
# CONFIG_CRYPTO_TWOFISH_X86_64_3WAY is not set
# CONFIG_CRYPTO_TWOFISH_AVX_X86_64 is not set

#
# Compression
#
CONFIG_CRYPTO_DEFLATE=y
CONFIG_CRYPTO_LZO=y
# CONFIG_CRYPTO_842 is not set
CONFIG_CRYPTO_LZ4=y
CONFIG_CRYPTO_LZ4HC=y
# CONFIG_CRYPTO_ZSTD is not set

#
# Random Number Generation
#
# CONFIG_CRYPTO_ANSI_CPRNG is not set
CONFIG_CRYPTO_DRBG_MENU=y
CONFIG_CRYPTO_DRBG_HMAC=y
# CONFIG_CRYPTO_DRBG_HASH is not set
CONFIG_CRYPTO_DRBG_CTR=y
CONFIG_CRYPTO_DRBG=y
CONFIG_CRYPTO_JITTERENTROPY=y
# CONFIG_CRYPTO_USER_API_HASH is not set
# CONFIG_CRYPTO_USER_API_SKCIPHER is not set
# CONFIG_CRYPTO_USER_API_RNG is not set
# CONFIG_CRYPTO_USER_API_AEAD is not set

#
# Crypto library routines
#
CONFIG_CRYPTO_LIB_AES=y
# CONFIG_CRYPTO_LIB_BLAKE2S is not set
CONFIG_CRYPTO_ARCH_HAVE_LIB_CHACHA=y
CONFIG_CRYPTO_LIB_CHACHA_GENERIC=y
# CONFIG_CRYPTO_LIB_CHACHA is not set
CONFIG_CRYPTO_ARCH_HAVE_LIB_CURVE25519=y
CONFIG_CRYPTO_LIB_CURVE25519_GENERIC=y
# CONFIG_CRYPTO_LIB_CURVE25519 is not set
CONFIG_CRYPTO_LIB_DES=y
CONFIG_CRYPTO_LIB_POLY1305_RSIZE=11
CONFIG_CRYPTO_ARCH_HAVE_LIB_POLY1305=y
CONFIG_CRYPTO_LIB_POLY1305_GENERIC=y
# CONFIG_CRYPTO_LIB_POLY1305 is not set
# CONFIG_CRYPTO_LIB_CHACHA20POLY1305 is not set
CONFIG_CRYPTO_LIB_SHA256=y
CONFIG_CRYPTO_HW=y
# CONFIG_CRYPTO_DEV_PADLOCK is not set
# CONFIG_CRYPTO_DEV_CCP is not set
# CONFIG_CRYPTO_DEV_QAT_DH895xCC is not set
# CONFIG_CRYPTO_DEV_QAT_C3XXX is not set
# CONFIG_CRYPTO_DEV_QAT_C62X is not set
# CONFIG_CRYPTO_DEV_QAT_DH895xCCVF is not set
# CONFIG_CRYPTO_DEV_QAT_C3XXXVF is not set
# CONFIG_CRYPTO_DEV_QAT_C62XVF is not set
# CONFIG_CRYPTO_DEV_NITROX_CNN55XX is not set
CONFIG_CRYPTO_DEV_VIRTIO=y
# CONFIG_CRYPTO_DEV_SAFEXCEL is not set
# CONFIG_CRYPTO_DEV_AMLOGIC_GXL is not set

#
# Certificates for signature checking
#
# end of Certificates for signature checking

CONFIG_BINARY_PRINTF=y

#
# Library routines
#
# CONFIG_PACKING is not set
CONFIG_BITREVERSE=y
CONFIG_GENERIC_STRNCPY_FROM_USER=y
CONFIG_GENERIC_STRNLEN_USER=y
CONFIG_GENERIC_NET_UTILS=y
CONFIG_GENERIC_FIND_FIRST_BIT=y
# CONFIG_CORDIC is not set
CONFIG_RATIONAL=y
CONFIG_GENERIC_PCI_IOMAP=y
CONFIG_GENERIC_IOMAP=y
CONFIG_ARCH_USE_CMPXCHG_LOCKREF=y
CONFIG_ARCH_HAS_FAST_MULTIPLIER=y
CONFIG_CRC_CCITT=y
CONFIG_CRC16=y
CONFIG_CRC_T10DIF=y
CONFIG_CRC_ITU_T=y
CONFIG_CRC32=y
# CONFIG_CRC32_SELFTEST is not set
CONFIG_CRC32_SLICEBY8=y
# CONFIG_CRC32_SLICEBY4 is not set
# CONFIG_CRC32_SARWATE is not set
# CONFIG_CRC32_BIT is not set
# CONFIG_CRC64 is not set
# CONFIG_CRC4 is not set
# CONFIG_CRC7 is not set
CONFIG_LIBCRC32C=y
# CONFIG_CRC8 is not set
# CONFIG_RANDOM32_SELFTEST is not set
CONFIG_ZLIB_INFLATE=y
CONFIG_ZLIB_DEFLATE=y
CONFIG_LZO_COMPRESS=y
CONFIG_LZO_DECOMPRESS=y
CONFIG_LZ4_COMPRESS=y
CONFIG_LZ4HC_COMPRESS=y
CONFIG_LZ4_DECOMPRESS=y
# CONFIG_XZ_DEC is not set
CONFIG_HAS_IOMEM=y
CONFIG_HAS_IOPORT_MAP=y
CONFIG_HAS_DMA=y
CONFIG_NEED_SG_DMA_LENGTH=y
CONFIG_NEED_DMA_MAP_STATE=y
CONFIG_ARCH_DMA_ADDR_T_64BIT=y
CONFIG_SWIOTLB=y
# CONFIG_DMA_API_DEBUG is not set
CONFIG_SGL_ALLOC=y
CONFIG_CPU_RMAP=y
CONFIG_DQL=y
CONFIG_GLOB=y
# CONFIG_GLOB_SELFTEST is not set
CONFIG_NLATTR=y
CONFIG_CLZ_TAB=y
# CONFIG_IRQ_POLL is not set
CONFIG_MPILIB=y
CONFIG_OID_REGISTRY=y
CONFIG_HAVE_GENERIC_VDSO=y
CONFIG_GENERIC_GETTIMEOFDAY=y
CONFIG_GENERIC_VDSO_TIME_NS=y
CONFIG_FONT_SUPPORT=y
CONFIG_FONT_8x16=y
CONFIG_FONT_AUTOSELECT=y
CONFIG_ARCH_HAS_PMEM_API=y
CONFIG_ARCH_HAS_UACCESS_FLUSHCACHE=y
CONFIG_ARCH_STACKWALK=y
CONFIG_STACKDEPOT=y
CONFIG_SBITMAP=y
# CONFIG_STRING_SELFTEST is not set
# end of Library routines

#
# Kernel hacking
#

#
# printk and dmesg options
#
CONFIG_PRINTK_TIME=y
# CONFIG_PRINTK_CALLER is not set
CONFIG_CONSOLE_LOGLEVEL_DEFAULT=7
CONFIG_CONSOLE_LOGLEVEL_QUIET=4
CONFIG_MESSAGE_LOGLEVEL_DEFAULT=4
# CONFIG_BOOT_PRINTK_DELAY is not set
# CONFIG_DYNAMIC_DEBUG is not set
CONFIG_SYMBOLIC_ERRNAME=y
CONFIG_DEBUG_BUGVERBOSE=y
# end of printk and dmesg options

#
# Compile-time checks and compiler options
#
CONFIG_DEBUG_INFO=y
# CONFIG_DEBUG_INFO_REDUCED is not set
# CONFIG_DEBUG_INFO_SPLIT is not set
# CONFIG_DEBUG_INFO_DWARF4 is not set
# CONFIG_DEBUG_INFO_BTF is not set
CONFIG_GDB_SCRIPTS=y
CONFIG_ENABLE_MUST_CHECK=y
CONFIG_FRAME_WARN=2048
# CONFIG_STRIP_ASM_SYMS is not set
# CONFIG_READABLE_ASM is not set
# CONFIG_HEADERS_INSTALL is not set
# CONFIG_DEBUG_SECTION_MISMATCH is not set
CONFIG_SECTION_MISMATCH_WARN_ONLY=y
CONFIG_FRAME_POINTER=y
CONFIG_STACK_VALIDATION=y
# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set
# end of Compile-time checks and compiler options

#
# Generic Kernel Debugging Instruments
#
CONFIG_MAGIC_SYSRQ=y
CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE=0x1
CONFIG_MAGIC_SYSRQ_SERIAL=y
CONFIG_MAGIC_SYSRQ_SERIAL_SEQUENCE=""
CONFIG_DEBUG_FS=y
CONFIG_HAVE_ARCH_KGDB=y
CONFIG_KGDB=y
CONFIG_KGDB_SERIAL_CONSOLE=y
# CONFIG_KGDB_TESTS is not set
# CONFIG_KGDB_LOW_LEVEL_TRAP is not set
# CONFIG_KGDB_KDB is not set
CONFIG_ARCH_HAS_UBSAN_SANITIZE_ALL=y
CONFIG_UBSAN=y
# CONFIG_UBSAN_TRAP is not set
CONFIG_UBSAN_BOUNDS=y
CONFIG_UBSAN_MISC=y
CONFIG_UBSAN_SANITIZE_ALL=y
CONFIG_UBSAN_NO_ALIGNMENT=y
# CONFIG_TEST_UBSAN is not set
# end of Generic Kernel Debugging Instruments

CONFIG_DEBUG_KERNEL=y
CONFIG_DEBUG_MISC=y

#
# Memory Debugging
#
# CONFIG_PAGE_EXTENSION is not set
# CONFIG_DEBUG_PAGEALLOC is not set
# CONFIG_PAGE_OWNER is not set
CONFIG_PAGE_POISONING=y
# CONFIG_PAGE_POISONING_NO_SANITY is not set
# CONFIG_PAGE_POISONING_ZERO is not set
# CONFIG_DEBUG_PAGE_REF is not set
# CONFIG_DEBUG_RODATA_TEST is not set
CONFIG_GENERIC_PTDUMP=y
# CONFIG_PTDUMP_DEBUGFS is not set
CONFIG_DEBUG_OBJECTS=y
# CONFIG_DEBUG_OBJECTS_SELFTEST is not set
# CONFIG_DEBUG_OBJECTS_FREE is not set
# CONFIG_DEBUG_OBJECTS_TIMERS is not set
# CONFIG_DEBUG_OBJECTS_WORK is not set
CONFIG_DEBUG_OBJECTS_RCU_HEAD=y
# CONFIG_DEBUG_OBJECTS_PERCPU_COUNTER is not set
CONFIG_DEBUG_OBJECTS_ENABLE_DEFAULT=1
# CONFIG_SLUB_DEBUG_ON is not set
# CONFIG_SLUB_STATS is not set
CONFIG_HAVE_DEBUG_KMEMLEAK=y
CONFIG_DEBUG_KMEMLEAK=y
CONFIG_DEBUG_KMEMLEAK_MEM_POOL_SIZE=100000
# CONFIG_DEBUG_KMEMLEAK_TEST is not set
# CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF is not set
CONFIG_DEBUG_KMEMLEAK_AUTO_SCAN=y
# CONFIG_DEBUG_STACK_USAGE is not set
# CONFIG_SCHED_STACK_END_CHECK is not set
# CONFIG_DEBUG_VM is not set
CONFIG_ARCH_HAS_DEBUG_VIRTUAL=y
# CONFIG_DEBUG_VIRTUAL is not set
CONFIG_DEBUG_MEMORY_INIT=y
# CONFIG_DEBUG_PER_CPU_MAPS is not set
CONFIG_HAVE_ARCH_KASAN=y
CONFIG_HAVE_ARCH_KASAN_VMALLOC=y
CONFIG_CC_HAS_KASAN_GENERIC=y
CONFIG_KASAN=y
CONFIG_KASAN_GENERIC=y
CONFIG_KASAN_OUTLINE=y
# CONFIG_KASAN_INLINE is not set
CONFIG_KASAN_STACK=1
# CONFIG_KASAN_VMALLOC is not set
# CONFIG_TEST_KASAN is not set
# end of Memory Debugging

# CONFIG_DEBUG_SHIRQ is not set

#
# Debug Oops, Lockups and Hangs
#
# CONFIG_PANIC_ON_OOPS is not set
CONFIG_PANIC_ON_OOPS_VALUE=0
CONFIG_PANIC_TIMEOUT=0
CONFIG_LOCKUP_DETECTOR=y
CONFIG_SOFTLOCKUP_DETECTOR=y
# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set
CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0
CONFIG_HARDLOCKUP_DETECTOR_PERF=y
CONFIG_HARDLOCKUP_CHECK_TIMESTAMP=y
CONFIG_HARDLOCKUP_DETECTOR=y
# CONFIG_BOOTPARAM_HARDLOCKUP_PANIC is not set
CONFIG_BOOTPARAM_HARDLOCKUP_PANIC_VALUE=0
CONFIG_DETECT_HUNG_TASK=y
CONFIG_DEFAULT_HUNG_TASK_TIMEOUT=120
# CONFIG_BOOTPARAM_HUNG_TASK_PANIC is not set
CONFIG_BOOTPARAM_HUNG_TASK_PANIC_VALUE=0
CONFIG_WQ_WATCHDOG=y
# CONFIG_TEST_LOCKUP is not set
# end of Debug Oops, Lockups and Hangs

#
# Scheduler Debugging
#
CONFIG_SCHED_DEBUG=y
# CONFIG_SCHEDSTATS is not set
# end of Scheduler Debugging

# CONFIG_DEBUG_TIMEKEEPING is not set
CONFIG_DEBUG_PREEMPT=y

#
# Lock Debugging (spinlocks, mutexes, etc...)
#
CONFIG_LOCK_DEBUGGING_SUPPORT=y
CONFIG_PROVE_LOCKING=y
CONFIG_PROVE_RAW_LOCK_NESTING=y
# CONFIG_LOCK_STAT is not set
CONFIG_DEBUG_RT_MUTEXES=y
CONFIG_DEBUG_SPINLOCK=y
CONFIG_DEBUG_MUTEXES=y
CONFIG_DEBUG_WW_MUTEX_SLOWPATH=y
CONFIG_DEBUG_RWSEMS=y
CONFIG_DEBUG_LOCK_ALLOC=y
CONFIG_LOCKDEP=y
# CONFIG_DEBUG_LOCKDEP is not set
CONFIG_DEBUG_ATOMIC_SLEEP=y
# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set
# CONFIG_LOCK_TORTURE_TEST is not set
# CONFIG_WW_MUTEX_SELFTEST is not set
# end of Lock Debugging (spinlocks, mutexes, etc...)

CONFIG_TRACE_IRQFLAGS=y
CONFIG_STACKTRACE=y
# CONFIG_WARN_ALL_UNSEEDED_RANDOM is not set
# CONFIG_DEBUG_KOBJECT is not set

#
# Debug kernel data structures
#
CONFIG_DEBUG_LIST=y
# CONFIG_DEBUG_PLIST is not set
# CONFIG_DEBUG_SG is not set
# CONFIG_DEBUG_NOTIFIERS is not set
# CONFIG_BUG_ON_DATA_CORRUPTION is not set
# end of Debug kernel data structures

# CONFIG_DEBUG_CREDENTIALS is not set

#
# RCU Debugging
#
CONFIG_PROVE_RCU=y
# CONFIG_RCU_PERF_TEST is not set
# CONFIG_RCU_TORTURE_TEST is not set
CONFIG_RCU_CPU_STALL_TIMEOUT=21
CONFIG_RCU_TRACE=y
# CONFIG_RCU_EQS_DEBUG is not set
# end of RCU Debugging

# CONFIG_DEBUG_WQ_FORCE_RR_CPU is not set
# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set
# CONFIG_CPU_HOTPLUG_STATE_CONTROL is not set
# CONFIG_LATENCYTOP is not set
CONFIG_USER_STACKTRACE_SUPPORT=y
CONFIG_NOP_TRACER=y
CONFIG_HAVE_FUNCTION_TRACER=y
CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y
CONFIG_HAVE_DYNAMIC_FTRACE=y
CONFIG_HAVE_DYNAMIC_FTRACE_WITH_REGS=y
CONFIG_HAVE_DYNAMIC_FTRACE_WITH_DIRECT_CALLS=y
CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y
CONFIG_HAVE_SYSCALL_TRACEPOINTS=y
CONFIG_HAVE_FENTRY=y
CONFIG_HAVE_C_RECORDMCOUNT=y
CONFIG_TRACE_CLOCK=y
CONFIG_RING_BUFFER=y
CONFIG_EVENT_TRACING=y
CONFIG_CONTEXT_SWITCH_TRACER=y
CONFIG_PREEMPTIRQ_TRACEPOINTS=y
CONFIG_TRACING=y
CONFIG_GENERIC_TRACER=y
CONFIG_TRACING_SUPPORT=y
CONFIG_FTRACE=y
# CONFIG_BOOTTIME_TRACING is not set
CONFIG_FUNCTION_TRACER=y
CONFIG_FUNCTION_GRAPH_TRACER=y
CONFIG_DYNAMIC_FTRACE=y
CONFIG_DYNAMIC_FTRACE_WITH_REGS=y
CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS=y
# CONFIG_FUNCTION_PROFILER is not set
# CONFIG_STACK_TRACER is not set
# CONFIG_PREEMPTIRQ_EVENTS is not set
# CONFIG_IRQSOFF_TRACER is not set
# CONFIG_PREEMPT_TRACER is not set
# CONFIG_SCHED_TRACER is not set
# CONFIG_HWLAT_TRACER is not set
# CONFIG_MMIOTRACE is not set
# CONFIG_FTRACE_SYSCALLS is not set
# CONFIG_TRACER_SNAPSHOT is not set
CONFIG_BRANCH_PROFILE_NONE=y
# CONFIG_PROFILE_ANNOTATED_BRANCHES is not set
# CONFIG_PROFILE_ALL_BRANCHES is not set
CONFIG_BLK_DEV_IO_TRACE=y
CONFIG_UPROBE_EVENTS=y
CONFIG_DYNAMIC_EVENTS=y
CONFIG_PROBE_EVENTS=y
CONFIG_FTRACE_MCOUNT_RECORD=y
# CONFIG_HIST_TRIGGERS is not set
# CONFIG_TRACE_EVENT_INJECT is not set
# CONFIG_TRACEPOINT_BENCHMARK is not set
# CONFIG_RING_BUFFER_BENCHMARK is not set
# CONFIG_TRACE_EVAL_MAP_FILE is not set
# CONFIG_FTRACE_STARTUP_TEST is not set
# CONFIG_RING_BUFFER_STARTUP_TEST is not set
# CONFIG_PREEMPTIRQ_DELAY_TEST is not set
# CONFIG_PROVIDE_OHCI1394_DMA_INIT is not set
# CONFIG_SAMPLES is not set
CONFIG_ARCH_HAS_DEVMEM_IS_ALLOWED=y
CONFIG_STRICT_DEVMEM=y
# CONFIG_IO_STRICT_DEVMEM is not set

#
# x86 Debugging
#
CONFIG_TRACE_IRQFLAGS_SUPPORT=y
CONFIG_EARLY_PRINTK_USB=y
CONFIG_X86_VERBOSE_BOOTUP=y
CONFIG_EARLY_PRINTK=y
CONFIG_EARLY_PRINTK_DBGP=y
# CONFIG_EARLY_PRINTK_USB_XDBC is not set
# CONFIG_DEBUG_WX is not set
CONFIG_DOUBLEFAULT=y
# CONFIG_DEBUG_TLBFLUSH is not set
CONFIG_HAVE_MMIOTRACE_SUPPORT=y
# CONFIG_X86_DECODER_SELFTEST is not set
CONFIG_IO_DELAY_0X80=y
# CONFIG_IO_DELAY_0XED is not set
# CONFIG_IO_DELAY_UDELAY is not set
# CONFIG_IO_DELAY_NONE is not set
# CONFIG_DEBUG_BOOT_PARAMS is not set
# CONFIG_CPA_DEBUG is not set
# CONFIG_DEBUG_ENTRY is not set
# CONFIG_DEBUG_NMI_SELFTEST is not set
CONFIG_X86_DEBUG_FPU=y
# CONFIG_PUNIT_ATOM_DEBUG is not set
# CONFIG_UNWINDER_ORC is not set
CONFIG_UNWINDER_FRAME_POINTER=y
# end of x86 Debugging

#
# Kernel Testing and Coverage
#
CONFIG_KUNIT=y
# CONFIG_KUNIT_DEBUGFS is not set
# CONFIG_KUNIT_TEST is not set
# CONFIG_KUNIT_EXAMPLE_TEST is not set
# CONFIG_NOTIFIER_ERROR_INJECTION is not set
# CONFIG_FAULT_INJECTION is not set
CONFIG_ARCH_HAS_KCOV=y
CONFIG_CC_HAS_SANCOV_TRACE_PC=y
# CONFIG_KCOV is not set
CONFIG_RUNTIME_TESTING_MENU=y
# CONFIG_LKDTM is not set
# CONFIG_TEST_LIST_SORT is not set
# CONFIG_TEST_MIN_HEAP is not set
# CONFIG_TEST_SORT is not set
# CONFIG_BACKTRACE_SELF_TEST is not set
# CONFIG_RBTREE_TEST is not set
# CONFIG_REED_SOLOMON_TEST is not set
# CONFIG_INTERVAL_TREE_TEST is not set
# CONFIG_PERCPU_TEST is not set
# CONFIG_ATOMIC64_SELFTEST is not set
# CONFIG_TEST_HEXDUMP is not set
# CONFIG_TEST_STRING_HELPERS is not set
# CONFIG_TEST_STRSCPY is not set
# CONFIG_TEST_KSTRTOX is not set
# CONFIG_TEST_PRINTF is not set
# CONFIG_TEST_BITMAP is not set
# CONFIG_TEST_BITFIELD is not set
# CONFIG_TEST_UUID is not set
# CONFIG_TEST_XARRAY is not set
# CONFIG_TEST_OVERFLOW is not set
# CONFIG_TEST_RHASHTABLE is not set
# CONFIG_TEST_HASH is not set
# CONFIG_TEST_IDA is not set
# CONFIG_TEST_LKM is not set
# CONFIG_TEST_VMALLOC is not set
# CONFIG_TEST_USER_COPY is not set
# CONFIG_TEST_BPF is not set
# CONFIG_TEST_BLACKHOLE_DEV is not set
# CONFIG_FIND_BIT_BENCHMARK is not set
# CONFIG_TEST_FIRMWARE is not set
# CONFIG_TEST_SYSCTL is not set
# CONFIG_SYSCTL_KUNIT_TEST is not set
# CONFIG_LIST_KUNIT_TEST is not set
# CONFIG_TEST_UDELAY is not set
# CONFIG_TEST_STATIC_KEYS is not set
# CONFIG_TEST_KMOD is not set
# CONFIG_TEST_MEMCAT_P is not set
# CONFIG_TEST_STACKINIT is not set
# CONFIG_TEST_MEMINIT is not set
# CONFIG_MEMTEST is not set
# end of Kernel Testing and Coverage
# end of Kernel hacking
07070100000037000081ED00000000000000000000000165BCF43700000240000000000000000000000000000000000000003D00000000ovpn-dco-0.2.20240320~git0.2aa7f93/tests/qemu/launch_deb2.sh#!/bin/bash

DISK=emulation/debian2.img
BZIMAGE=linux-kernel/arch/x86/boot/bzImage
SHARE=/home/user/share

sudo qemu-system-x86_64 \
    -device virtio-balloon \
    -enable-kvm \
    -accel kvm \
    -kernel ${BZIMAGE} \
    -append 'console=ttyS0 root=/dev/vda1 pci=noacpi' \
    -drive file=${DISK},if=virtio,cache=writeback \
    -virtfs local,path=${SHARE},security_model=passthrough,mount_tag=k1 \
    -nic tap,ifname=tap0,script=no,downscript=no,model=virtio-net-pci \
    -display none \
    -nographic \
    -serial mon:stdio \
    -m 2048 -boot c \
    -snapshot -s
07070100000038000081A400000000000000000000000165BCF43700000032000000000000000000000000000000000000003700000000ovpn-dco-0.2.20240320~git0.2aa7f93/tests/tcp_peers.txt1 5.5.5.2
2 5.5.5.3
3 5.5.5.4
4 5.5.5.5
5 5.5.5.6
07070100000039000081A400000000000000000000000165BCF4370000006E000000000000000000000000000000000000003700000000ovpn-dco-0.2.20240320~git0.2aa7f93/tests/udp_peers.txt1 10.10.1.2 1 5.5.5.2
2 10.10.2.2 1 5.5.5.3
3 10.10.3.2 1 5.5.5.4
4 10.10.4.2 1 5.5.5.5
5 10.10.5.2 1 5.5.5.6
07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000B00000000TRAILER!!!579 blocks
openSUSE Build Service is sponsored by