Как-то на очередных выходных я решил актуализировать свои виртуальные и не очень серверы. Дома стоит HP Proliant MicroServer Gen10+, на котором крутятся всякие домашние сервисы: он и роутером у меня выступает и видеокамеры на него пишут потоки, и различные докеры с полезными сервисами на нём бегут.
Также в DigitalOcean и CROC Cloud у меня есть по виртуалочке, на которых также крутится всякая всячина. Между собой эти 3 точки связаны OpenVPN’ом. При этом среди них аж 2 сервера: оба в облаках.
Недавно я наткнулся на новость о том, что OpenVPN теперь такие же крутые, как и wireguard – трафик (data channel) умеют “сгружать” в ядро операционной системы. Ранее вся обработка трафика осуществлялась в userspace – то есть пакеты приходили в сетевую карточку, стандартно копировались в пространство ядра, далее происходило копирование буфера данные в пространство процесса openvpn. OpenVPN процесс решал, куда нужно отправить пакет дальше – либо выплюнуть локально в tun/tap интерфейс, либо отправить пакет дальше – другому клиенту. После принятия решения происходил такой же процесс, как при получении, только в обратном направлении: пакет копировался из userspace в kernel и затем отправлялся уже получателю. При этом процессор, при переключении работы из user-space в kernel-space и обратно, вынужден делать достаточно дорогую операцию – переключение контекста. Дорогую – в смысле она занимает много времени. Вот как работает, когда openvpn процесс занимается обработкой трафика:
Так вот, теперь у openvpn появилась поддержка так называемого Data Channel Offload – DCO. Работает это так: openvpn процесс занимается только control plane задачами – настройкой маршрутизации, сетевых интерфейсов, обменом ключами с клиентами, keepalive’ами. В общем, различной контрольной работой. Непосредственно обработкой трафика он больше не занимается. Теперь это забота нового компонента – модуля ядра “ovpn-dco”.
При поступлении пакета, предназначающегося openvpn’у, он теперь обрабатывается модулем ядра. Если нужно, пакет передаётся в user-space процессу openvpn (например, для первичного установления шифрованного канала между сервером и клиентом). А если пакет – это обычный транзитный IP-пакет, то обработку его полностью на себя берет ovpn-dco
модуль. Ключевой момент здесь в том, что обработка сетевого трафика (99% нагрузки openvpn сервера) теперь будет делаться там же в пространстве ядра, куда пакеты изначально и поступают. Больше никакого копирования из kernel-space в user-space и никаких контекстных переключений (ну почти)!
Ребята из OpenVPN показывают очень впечатляющие графики прироста производительности для Linux и чуть менее скромные, но тоже с приростом производительности для Windows. Приведу пример для Linux:
Кстати, поддержка DCO в openvpn есть как для серверов, так и для клиентов. Разумеется, разных операционных систем.
Пара слов об ограничениях
Версия openvpn. Тут, в общем, всё просто: поддержка DCO стала публично доступной в openvpn 2.6. Это значит, что процесс openvpn умеет взаимодействовать с модулем ядра ovpn-dco
.
Поставка ovpn-dco. В Linux этот модуль ядра поставляется отдельно! Это значит, что установить openvpn недостаточно и нужно ставить дополнительный пакет/компонент. Утверждается, что для Windows ovpn-dco поставляется вместе с основным пакетом. Я Windows не пользуюсь и не интересуюсь, может где-то не прав.
Совместимые режимы шифрования. DCO поддерживается только при использовании AEAD режиме шифра. А именно, поддерживаются AES-***-GCM
и ChaCha20-Poly1305
.
Драйвер. Для разгрузки поддерживается только tun
(L3) драйвер. Если у вас режим работы tap
(L2), то можете закрывать эту заметку, либо подумать о переходе на L3 VPN (tun).
Требования к версии ядра Linux. По-любому тут есть какие-то четкие требования на наличие тех или иных функций в ядре, но я не встречался с таким списком, поэтому помечу этот пункт пока как TBA. Если найду такую информацию, добавлю.
Переходим к практике
Решил я попробовать, как это работает, на своих серверах. Из личных у меня 2 OpenVPN сервера: на RockyLinux 9 (бежит на ядре 5.14) и на Fedora 38 (Linux 6.4).
Начнём с Rocky
OpenVPN 2.6 недоступен ни из базовых репозиториев, ни из EPEL. Авторы openvpn для получения современных версий openvpn RHEL-family рекомендуют использовать их copr репозиторий. Подключаем его на системе, согласившись с предупреждением о том, что это сторонний репозиторий и rocky linux не отвечает за качество содержимого:
# yum install yum-plugin-copr -y # yum copr enable dsommers/openvpn-release-2.6 ... Do you really want to enable copr.fedorainfracloud.org/dsommers/openvpn-release-2.6? [y/N]: y Repository successfully enabled.
Ставим openvpn (убедитесь, что ставите 2.6.x версию):
# yum install openvpn -y
Далее нужно установить модуль ядра. Вот тут у меня возникли трудности. Определённо, рекомендуемый вариант указывает на необходимость подключения copr репозитория openvpn3. Однако также есть пометочка, что это работает только для fedora. В общем, я попробовал подключить репозиторий, но установить пакет из него в рабочем состоянии не удалось. Дело в том, что модуль ядра должен “соответствовать” ядру, под которым он будет загружен. В случае с ovpn-dco
это “соответствие” достигается сборкой модуля из исходников при помощи dkms прямо во время установки. Во время установки ставятся все зависимости, необходимые для сборки + тулинг сборки:
Нажми, чтобы увидеть детали…
# yum install kmod-ovpn-dco Copr repo for openvpn3 owned by dsommers 33 kB/s | 21 kB 00:00 Dependencies resolved. ====================================================================================================================== Package Arch Version Repository Size ====================================================================================================================== Installing: kmod-ovpn-dco noarch 0-20220905git3ba6c07.el9 copr:copr.fedorainfracloud.org:dsommers:openvpn3 77 k Installing dependencies: binutils x86_64 2.35.2-37.el9 baseos 4.5 M binutils-gold x86_64 2.35.2-37.el9 baseos 731 k bison x86_64 3.7.4-5.el9 appstream 921 k cpp x86_64 11.3.1-4.3.el9 appstream 11 M dkms noarch 3.0.11-1.el9 epel 85 k elfutils-debuginfod-client x86_64 0.188-3.el9 baseos 37 k elfutils-libelf-devel x86_64 0.188-3.el9 appstream 22 k flex x86_64 2.6.4-9.el9 appstream 308 k gcc x86_64 11.3.1-4.3.el9 appstream 32 M glibc-devel x86_64 2.34-60.el9 appstream 48 k glibc-headers x86_64 2.34-60.el9 appstream 448 k kernel-devel x86_64 5.14.0-284.25.1.el9_2 appstream 19 M kernel-devel-matched x86_64 5.14.0-284.25.1.el9_2 appstream 3.4 M kernel-headers x86_64 5.14.0-284.25.1.el9_2 appstream 4.8 M libmpc x86_64 1.2.1-4.el9 appstream 61 k libxcrypt-devel x86_64 4.4.18-3.el9 appstream 28 k m4 x86_64 1.4.19-1.el9 appstream 294 k make x86_64 1:4.3-7.el9 baseos 530 k openssl-devel x86_64 1:3.0.7-17.el9_2 appstream 3.0 M perl-AutoLoader noarch 5.74-480.el9 appstream 21 k perl-B x86_64 1.80-480.el9 appstream 179 k perl-Carp noarch 1.50-460.el9 appstream 29 k perl-Class-Struct noarch 0.66-480.el9 appstream 22 k perl-Data-Dumper x86_64 2.174-462.el9 appstream 55 k perl-Digest noarch 1.19-4.el9 appstream 25 k perl-Digest-MD5 x86_64 2.58-4.el9 appstream 36 k perl-Encode x86_64 4:3.08-462.el9 appstream 1.7 M perl-Errno x86_64 1.30-480.el9 appstream 15 k perl-Exporter noarch 5.74-461.el9 appstream 31 k perl-Fcntl x86_64 1.13-480.el9 appstream 20 k perl-File-Basename noarch 2.85-480.el9 appstream 17 k perl-File-Path noarch 2.18-4.el9 appstream 35 k perl-File-Temp noarch 1:0.231.100-4.el9 appstream 59 k perl-File-stat noarch 1.09-480.el9 appstream 17 k perl-FileHandle noarch 2.03-480.el9 appstream 16 k perl-Getopt-Long noarch 1:2.52-4.el9 appstream 60 k perl-Getopt-Std noarch 1.12-480.el9 appstream 16 k perl-HTTP-Tiny noarch 0.076-460.el9 appstream 54 k perl-IO x86_64 1.43-480.el9 appstream 87 k perl-IO-Socket-IP noarch 0.41-5.el9 appstream 42 k perl-IPC-Open3 noarch 1.21-480.el9 appstream 23 k perl-MIME-Base64 x86_64 3.16-4.el9 appstream 30 k perl-Net-SSLeay x86_64 1.92-2.el9 appstream 365 k perl-POSIX x86_64 1.94-480.el9 appstream 96 k perl-PathTools x86_64 3.78-461.el9 appstream 85 k perl-Pod-Escapes noarch 1:1.07-460.el9 appstream 20 k perl-Pod-Perldoc noarch 3.28.01-461.el9 appstream 83 k perl-Pod-Simple noarch 1:3.42-4.el9 appstream 215 k perl-Pod-Usage noarch 4:2.01-4.el9 appstream 40 k perl-Scalar-List-Utils x86_64 4:1.56-461.el9 appstream 71 k perl-SelectSaver noarch 1.02-480.el9 appstream 12 k perl-Socket x86_64 4:2.031-4.el9 appstream 54 k perl-Storable x86_64 1:3.21-460.el9 appstream 95 k perl-Symbol noarch 1.08-480.el9 appstream 14 k perl-Term-ANSIColor noarch 5.01-461.el9 appstream 48 k perl-Term-Cap noarch 1.17-460.el9 appstream 22 k perl-Text-ParseWords noarch 3.30-460.el9 appstream 16 k perl-Text-Tabs+Wrap noarch 2013.0523-460.el9 appstream 23 k perl-Time-Local noarch 2:1.300-7.el9 appstream 33 k perl-URI noarch 5.09-3.el9 appstream 108 k perl-base noarch 2.27-480.el9 appstream 16 k perl-constant noarch 1.33-461.el9 appstream 23 k perl-if noarch 0.60.800-480.el9 appstream 14 k perl-interpreter x86_64 4:5.32.1-480.el9 appstream 71 k perl-libnet noarch 3.13-4.el9 appstream 125 k perl-libs x86_64 4:5.32.1-480.el9 appstream 2.0 M perl-mro x86_64 1.23-480.el9 appstream 28 k perl-overload noarch 1.31-480.el9 appstream 46 k perl-overloading noarch 0.02-480.el9 appstream 13 k perl-parent noarch 1:0.238-460.el9 appstream 14 k perl-podlators noarch 1:4.14-460.el9 appstream 112 k perl-subs noarch 1.03-480.el9 appstream 12 k perl-vars noarch 1.05-480.el9 appstream 13 k zlib-devel x86_64 1.2.11-39.el9 appstream 44 k Installing weak dependencies: perl-IO-Socket-SSL noarch 2.073-1.el9 appstream 217 k perl-Mozilla-CA noarch 20200520-6.el9 appstream 12 k perl-NDBM_File x86_64 1.15-480.el9 appstream 22 k Transaction Summary ====================================================================================================================== Install 78 Packages
Во время установки kmod-ovpn-dco пакета выскакивает сообщение об ошибке в POST-скриптах пакета:
Installing : kmod-ovpn-dco-0-20220905git3ba6c07.el9.noarch 78/78 Running scriptlet: kmod-ovpn-dco-0-20220905git3ba6c07.el9.noarch 78/78 Loading new ovpn-dco-0.20220905git3ba6c07.el9 DKMS files... Deprecated feature: REMAKE_INITRD (/usr/src/ovpn-dco-0.20220905git3ba6c07.el9/dkms.conf) Building for 5.14.0-284.25.1.el9_2.x86_64 Building initial module for 5.14.0-284.25.1.el9_2.x86_64 Deprecated feature: REMAKE_INITRD (/var/lib/dkms/ovpn-dco/0.20220905git3ba6c07.el9/source/dkms.conf) Error! Bad return status for module build on kernel: 5.14.0-284.25.1.el9_2.x86_64 (x86_64) Consult /var/lib/dkms/ovpn-dco/0.20220905git3ba6c07.el9/build/make.log for more information. warning: %post(kmod-ovpn-dco-0-20220905git3ba6c07.el9.noarch) scriptlet failed, exit status 10 Error in POSTIN scriptlet in rpm package kmod-ovpn-dco
В лог-файле установки ошибки указывают на то, что исходный код ядра не предоставляет тех функций, которые вызываются в коде модуля ядра. Проще говоря – ядро не подходит к этим исходникам. Ниже можно раскрыть спойлер, чтобы увидеть лог.
DKMS лог сборки пакета
DKMS make.log for ovpn-dco-0.20220905git3ba6c07.el9 for kernel 5.14.0-284.25.1.el9_2.x86_64 (x86_64) Sun Sep 3 04:24:40 PM UTC 2023 make: Entering directory '/var/lib/dkms/ovpn-dco/0.20220905git3ba6c07.el9/build' /var/lib/dkms/ovpn-dco/0.20220905git3ba6c07.el9/build/gen-compat-autoconf.sh /var/lib/dkms/ovpn-dco/0.20220905git3ba6c07.el9/build/compat-autoconf.h make -C /lib/modules/5.14.0-284.25.1.el9_2.x86_64/build M=/var/lib/dkms/ovpn-dco/0.20220905git3ba6c07.el9/build PWD=/var/lib/dkms/ovpn-dco/0.20220905git3ba6c07.el9/build REVISION=copr:0.20220905git3ba6c07.el9 CONFIG_OVPN_DCO=m INSTALL_MOD_DIR=updates/ modules make[1]: Entering directory '/usr/src/kernels/5.14.0-284.25.1.el9_2.x86_64' CC [M] /var/lib/dkms/ovpn-dco/0.20220905git3ba6c07.el9/build/drivers/net/ovpn-dco/main.o CC [M] /var/lib/dkms/ovpn-dco/0.20220905git3ba6c07.el9/build/drivers/net/ovpn-dco/bind.o CC [M] /var/lib/dkms/ovpn-dco/0.20220905git3ba6c07.el9/build/drivers/net/ovpn-dco/crypto.o CC [M] /var/lib/dkms/ovpn-dco/0.20220905git3ba6c07.el9/build/drivers/net/ovpn-dco/ovpn.o In file included from ./include/linux/ip.h:16, from /var/lib/dkms/ovpn-dco/0.20220905git3ba6c07.el9/build/drivers/net/ovpn-dco/main.h:13, from /var/lib/dkms/ovpn-dco/0.20220905git3ba6c07.el9/build/drivers/net/ovpn-dco/ovpn.c:10: /var/lib/dkms/ovpn-dco/0.20220905git3ba6c07.el9/build/drivers/net/ovpn-dco/ovpn.c: In function ‘ovpn_net_xmit’: ./include/linux/skbuff.h:2112:9: warning: array subscript ‘struct sk_buff[0]’ is partly outside array bounds of ‘struct sk_buff_head[1]’ [-Warray-bounds] 2112 | __skb_insert(newsk, next->prev, next, list); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /var/lib/dkms/ovpn-dco/0.20220905git3ba6c07.el9/build/drivers/net/ovpn-dco/ovpn.c:452:29: note: while referencing ‘skb_list’ 452 | struct sk_buff_head skb_list; | ^~~~~~~~ CC [M] /var/lib/dkms/ovpn-dco/0.20220905git3ba6c07.el9/build/drivers/net/ovpn-dco/peer.o /var/lib/dkms/ovpn-dco/0.20220905git3ba6c07.el9/build/drivers/net/ovpn-dco/peer.c: In function ‘ovpn_peer_create’: /var/lib/dkms/ovpn-dco/0.20220905git3ba6c07.el9/build/drivers/net/ovpn-dco/peer.c:91:9: error: implicit declaration of function ‘netif_tx_napi_add’; did you mean ‘netif_napi_add’? [-Werror=implicit-function-declaration] 91 | netif_tx_napi_add(ovpn->dev, &peer->napi, ovpn_napi_poll, | ^~~~~~~~~~~~~~~~~ | netif_napi_add cc1: some warnings being treated as errors make[3]: *** [scripts/Makefile.build:321: /var/lib/dkms/ovpn-dco/0.20220905git3ba6c07.el9/build/drivers/net/ovpn-dco/peer.o] Error 1 make[2]: *** [scripts/Makefile.build:607: /var/lib/dkms/ovpn-dco/0.20220905git3ba6c07.el9/build/drivers/net/ovpn-dco] Error 2 make[1]: *** [Makefile:1923: /var/lib/dkms/ovpn-dco/0.20220905git3ba6c07.el9/build] Error 2 make[1]: Leaving directory '/usr/src/kernels/5.14.0-284.25.1.el9_2.x86_64' make: *** [Makefile:52: all] Error 2
Я попробовал обновить ядро до более свежих версий (kernel-lt 6.2 и kernel-ml 6.5) из elrepo, но это тоже не помогло. Были другие похожие ошибки о несовпадающем API. В общем, эту гонку пора было прекращать и искать другой способ.
Сборка ovpn-dco из исходников
Этот вариант для меня сработал. На всякий случай оставлю точную версию ядра: 5.14.0-284.25.1.el9_2.x86_64
. Для неё с первого раза удалось собрать и загрузить модуль ядра ovpn-dco
.
Итак, по шагам:
1. Ставим необходимые пакеты:
yum install git kernel-devel kernel-headers gcc -y
2. Клонируем официальный git-репозиторий и переходим в него:
git clone https://github.com/OpenVPN/ovpn-dco.git cd ovpn-dco
3. Запускаем сборку и затем устанавливаем:
make make install
В результате, если не будет ошибок во время сборки, будет собран и установлен модуль ядра. Во время установки у меня вылезла ошибка openssl о невозможности найти файл crypto/bio/bss_file.c
, но она не повлияла на работу модуля.
Вывод сборки и установки у меня выглядел так:
# make /root/ovpn-dco/gen-compat-autoconf.sh /root/ovpn-dco/compat-autoconf.h make -C /lib/modules/5.14.0-284.25.1.el9_2.x86_64/build M=/root/ovpn-dco PWD=/root/ovpn-dco REVISION=0.2.20230426 CONFIG_OVPN_DCO_V2=m INSTALL_MOD_DIR=updates/ modules make[1]: Entering directory '/usr/src/kernels/5.14.0-284.25.1.el9_2.x86_64' CC [M] /root/ovpn-dco/drivers/net/ovpn-dco/main.o CC [M] /root/ovpn-dco/drivers/net/ovpn-dco/bind.o CC [M] /root/ovpn-dco/drivers/net/ovpn-dco/crypto.o CC [M] /root/ovpn-dco/drivers/net/ovpn-dco/ovpn.o In file included from ./include/linux/ip.h:16, from /root/ovpn-dco/drivers/net/ovpn-dco/main.h:13, from /root/ovpn-dco/drivers/net/ovpn-dco/ovpn.c:10: /root/ovpn-dco/drivers/net/ovpn-dco/ovpn.c: In function ‘ovpn_net_xmit’: ./include/linux/skbuff.h:2112:9: warning: array subscript ‘struct sk_buff[0]’ is partly outside array bounds of ‘struct sk_buff_head[1]’ [-Warray-bounds] 2112 | __skb_insert(newsk, next->prev, next, list); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /root/ovpn-dco/drivers/net/ovpn-dco/ovpn.c:416:29: note: while referencing ‘skb_list’ 416 | struct sk_buff_head skb_list; | ^~~~~~~~ CC [M] /root/ovpn-dco/drivers/net/ovpn-dco/peer.o CC [M] /root/ovpn-dco/drivers/net/ovpn-dco/sock.o CC [M] /root/ovpn-dco/drivers/net/ovpn-dco/stats.o CC [M] /root/ovpn-dco/drivers/net/ovpn-dco/netlink.o CC [M] /root/ovpn-dco/drivers/net/ovpn-dco/crypto_aead.o CC [M] /root/ovpn-dco/drivers/net/ovpn-dco/pktid.o CC [M] /root/ovpn-dco/drivers/net/ovpn-dco/tcp.o CC [M] /root/ovpn-dco/drivers/net/ovpn-dco/udp.o LD [M] /root/ovpn-dco/drivers/net/ovpn-dco/ovpn-dco-v2.o MODPOST /root/ovpn-dco/Module.symvers CC [M] /root/ovpn-dco/drivers/net/ovpn-dco/ovpn-dco-v2.mod.o LD [M] /root/ovpn-dco/drivers/net/ovpn-dco/ovpn-dco-v2.ko BTF [M] /root/ovpn-dco/drivers/net/ovpn-dco/ovpn-dco-v2.ko Skipping BTF generation for /root/ovpn-dco/drivers/net/ovpn-dco/ovpn-dco-v2.ko due to unavailability of vmlinux make[1]: Leaving directory '/usr/src/kernels/5.14.0-284.25.1.el9_2.x86_64' # make install /root/ovpn-dco/gen-compat-autoconf.sh /root/ovpn-dco/compat-autoconf.h make -C /lib/modules/5.14.0-284.25.1.el9_2.x86_64/build M=/root/ovpn-dco PWD=/root/ovpn-dco REVISION=0.2.20230426 CONFIG_OVPN_DCO_V2=m INSTALL_MOD_DIR=updates/ modules_install make[1]: Entering directory '/usr/src/kernels/5.14.0-284.25.1.el9_2.x86_64' INSTALL /lib/modules/5.14.0-284.25.1.el9_2.x86_64/updates//drivers/net/ovpn-dco/ovpn-dco-v2.ko SIGN /lib/modules/5.14.0-284.25.1.el9_2.x86_64/updates//drivers/net/ovpn-dco/ovpn-dco-v2.ko At main.c:167: - SSL error:FFFFFFFF80000002:system library::No such file or directory: crypto/bio/bss_file.c:67 - SSL error:10000080:BIO routines::no such file: crypto/bio/bss_file.c:75 sign-file: certs/signing_key.pem: No such file or directory DEPMOD /lib/modules/5.14.0-284.25.1.el9_2.x86_64 make[1]: Leaving directory '/usr/src/kernels/5.14.0-284.25.1.el9_2.x86_64' depmod -a
Далее проверяем, что модуль установился и виден в системе:
# modinfo ovpn-dco-v2 filename: /lib/modules/5.14.0-284.25.1.el9_2.x86_64/updates/drivers/net/ovpn-dco/ovpn-dco-v2.ko alias: net-pf-16-proto-16-family-ovpn-dco-v2 alias: rtnl-link-ovpn-dco version: 0.2.20230426 license: GPL author: (C) 2020-2023 OpenVPN, Inc. description: OpenVPN data channel offload (ovpn-dco) rhelversion: 9.2 srcversion: A22F191CDB6BC3553A7C87B depends: udp_tunnel,ip6_udp_tunnel retpoline: Y
Пробуем его загрузить:
# modprobe ovpn-dco-v2 # dmesg | tail -1 [12555.595171] OpenVPN data channel offload (ovpn-dco) 0.2.20230426 -- (C) 2020-2023 OpenVPN, Inc.
Похоже, всё отлично! Теперь нужно заставить openvpn сервер работать с ним.
У меня уже есть настроенный openvpn-сервер, я не буду рассказывать, как поднять базовый конфиг – на эту тему в интернете есть множество мануалов.
После того, как установлена нужная версия openvpn (2.6+) и модуль ядра доступен для загрузки, можно попробовать перезапустить openvpn процесс и заглянуть в его логи на предмет работы DCO. На старте openvpn пытается инициализировать DCO: если в конфиге DCO не отключен явно (опция disable-dco
), если выбранные шифры совместимы с DCO, а также модуль ядра доступен, то сервис запустится в режиме разгрузки data-канала.
В противном случае в логе можно будет увидеть одно из следующих сообщений:
Note: cipher 'AES-256-CBC' in --data-ciphers is not supported by ovpn-dco, disabling data channel offload.
Note: Kernel support for ovpn-dco missing, disabling data channel offload.
Также отдельно в логе пишется версия ядерного модуля dco, если он найден:
DCO version: 0.2.20230426
Так это выглядит, если модуль не найден:
DCO version: N/A
И последний, самый явный показатель, что openvpn работает без разгрузки канала данных – это драйвер устройства tun#, который можно проверить при помощи утилиты ethtool
. Для DCO вывод должен быть следующим:
# ethtool -i tun1 driver: ovpn-dco version: 0.2.20230426 firmware-version: expansion-rom-version: bus-info: ovpn supports-statistics: no supports-test: no supports-eeprom-access: no supports-register-dump: no supports-priv-flags: no
Старый режим работы будет сообщать о хорошо многим знакомом tun драйвере:
# ethtool -i tun1 driver: tun version: 1.6 firmware-version: expansion-rom-version: bus-info: tun supports-statistics: no supports-test: no supports-eeprom-access: no supports-register-dump: no supports-priv-flags: no
Последний этап, чтобы активировать режим dco – проверить поддерживаемые шифры. Необходимо перечислить только совместимые с DCO шифры, иначе DCO не будет включён, например:
cipher AES-256-GCM data-ciphers AES-256-GCM
Или сразу несколько (в качестве разделителя используется знак двоеточия):
cipher AES-256-GCM:Chacha20-Poly1305 data-ciphers AES-256-GCM:Chacha20-Poly1305
Когда всё будет корректно настроено, в логах вы увидите следующие сообщения (в конфиге вербозность логов настроена так: verb 3
):
DCO version: 0.2.20230426 ... net_iface_new: add tun0 type ovpn-dco ... DCO device tun1 opened
Voila!
Готово! Чтобы окончательно убедиться, что разгрузка работает, я запустил strace
на процесс openvpn. В режиме, когда dco отключен и через vpn-сервер проходит трафик (в данном примере я запустил ping
), мы видим, что процесс openvpn получает от ядра пакеты и отправляет их обратно (то самое взаимодействие kernel- и user-space). Пример вывода можно посмотреть, раскрыв спойлер ниже.
strace на openvpn процессе с выключенным dco
# strace -fp 20185 strace: Process 20185 attached restart_syscall(<... resuming interrupted read ...>) = 1 recvfrom(5, "H\0\0\0\0\0\0002\224<~\273\274\207cWFX8\23|\v%;k\32\273#\364\314\16\320"..., 2330, 0, {sa_family=AF_INET, sin_port=htons(50716), sin_addr=inet_addr("x.x.x.x")}, [28 => 16]) = 108 poll([{fd=5, events=0}, {fd=4, events=POLLOUT}, {fd=6, events=POLLIN|POLLPRI}], 3, 1240) = 1 ([{fd=4, revents=POLLOUT}]) write(4, "E\0\0T\335\352\0\0@\1\366\274\n\377\310\2\n\377\310\1\10\0\242\221\202z\0\6d\364\317\326"..., 84) = 84 poll([{fd=5, events=POLLIN|POLLPRI}, {fd=4, events=POLLIN|POLLPRI}, {fd=6, events=POLLIN|POLLPRI}], 3, 1240) = 1 ([{fd=4, revents=POLLIN}]) read(4, "E\0\0T\351@\0\0@\1\353f\n\377\310\1\n\377\310\2\0\0\252\221\202z\0\6d\364\317\326"..., 1768) = 84 poll([{fd=5, events=POLLOUT}, {fd=4, events=0}, {fd=6, events=POLLIN|POLLPRI}], 3, 1240) = 1 ([{fd=5, revents=POLLOUT}]) sendto(5, "H\0\0\0\0\0\0005\f\246JK\265)9\262$@\227\247\375\307\304\375cS\374\232\354?\266\333"..., 108, 0, {sa_family=AF_INET, sin_port=htons(50716), sin_addr=inet_addr("x.x.x.x")}, 16) = 108 poll([{fd=5, events=POLLIN|POLLPRI}, {fd=4, events=POLLIN|POLLPRI}, {fd=6, events=POLLIN|POLLPRI}], 3, 1240) = 1 ([{fd=5, revents=POLLIN}]) recvfrom(5, "H\0\0\0\0\0\0003>m\262zQ\310/N\275&0\243S\315VO27\361\341\224\255\376$"..., 2330, 0, {sa_family=AF_INET, sin_port=htons(50716), sin_addr=inet_addr("x.x.x.x")}, [28 => 16]) = 108 poll([{fd=5, events=0}, {fd=4, events=POLLOUT}, {fd=6, events=POLLIN|POLLPRI}], 3, 1137) = 1 ([{fd=4, revents=POLLOUT}]) write(4, "E\0\0T\257\10\0\0@\1%\237\n\377\310\2\n\377\310\1\10\0N\226\202z\0\7d\364\317\327"..., 84) = 84 poll([{fd=5, events=POLLIN|POLLPRI}, {fd=4, events=POLLIN|POLLPRI}, {fd=6, events=POLLIN|POLLPRI}], 3, 1136) = 1 ([{fd=4, revents=POLLIN}]) read(4, "E\0\0T\351\223\0\0@\1\353\23\n\377\310\1\n\377\310\2\0\0V\226\202z\0\7d\364\317\327"..., 1768) = 84 poll([{fd=5, events=POLLOUT}, {fd=4, events=0}, {fd=6, events=POLLIN|POLLPRI}], 3, 1136) = 1 ([{fd=5, revents=POLLOUT}]) sendto(5, "H\0\0\0\0\0\0006a\270%\200\37\360\223\257\v\212X\244\211b\261\3150\2_(;\224XP"..., 108, 0, {sa_family=AF_INET, sin_port=htons(50716), sin_addr=inet_addr("x.x.x.x")}, 16) = 108 poll([{fd=5, events=POLLIN|POLLPRI}, {fd=4, events=POLLIN|POLLPRI}, {fd=6, events=POLLIN|POLLPRI}], 3, 1136) = 1 ([{fd=5, revents=POLLIN}]) recvfrom(5, "H\0\0\0\0\0\0004\256\236\322\22\215\320\315\341\217\231\246\200w\315\36\240\232N\343I2Q\37\22"..., 2330, 0, {sa_family=AF_INET, sin_port=htons(50716), sin_addr=inet_addr("x.x.x.x")}, [28 => 16]) = 108 poll([{fd=5, events=0}, {fd=4, events=POLLOUT}, {fd=6, events=POLLIN|POLLPRI}], 3, 1240) = 1 ([{fd=4, revents=POLLOUT}]) write(4, "E\0\0T\252Z\0\0@\1*M\n\377\310\2\n\377\310\1\10\0\263\330\202z\0\10d\364\317\327"..., 84) = 84 poll([{fd=5, events=POLLIN|POLLPRI}, {fd=4, events=POLLIN|POLLPRI}, {fd=6, events=POLLIN|POLLPRI}], 3, 1240) = 1 ([{fd=4, revents=POLLIN}]) read(4, "E\0\0T\351\321\0\0@\1\352\325\n\377\310\1\n\377\310\2\0\0\273\330\202z\0\10d\364\317\327"..., 1768) = 84 poll([{fd=5, events=POLLOUT}, {fd=4, events=0}, {fd=6, events=POLLIN|POLLPRI}], 3, 1240) = 1 ([{fd=5, revents=POLLOUT}]) sendto(5, "H\0\0\0\0\0\0007\224(\350\216\tr\204\253n \272\260e\346\241=]\272\n3\266\213\213*"..., 108, 0, {sa_family=AF_INET, sin_port=htons(50716), sin_addr=inet_addr("x.x.x.x")}, 16) = 108 poll([{fd=5, events=POLLIN|POLLPRI}, {fd=4, events=POLLIN|POLLPRI}, {fd=6, events=POLLIN|POLLPRI}], 3, 1239) = 1 ([{fd=5, revents=POLLIN}]) recvfrom(5, "H\0\0\0\0\0\0005\261[\306l\v1\345\255r\303\373|\212\f\267d\223\262\242\307\334\236\220\364"..., 2330, 0, {sa_family=AF_INET, sin_port=htons(50716), sin_addr=inet_addr("x.x.x.x")}, [28 => 16]) = 108 poll([{fd=5, events=0}, {fd=4, events=POLLOUT}, {fd=6, events=POLLIN|POLLPRI}], 3, 1137) = 1 ([{fd=4, revents=POLLOUT}]) write(4, "E\0\0T\205\n\0\0@\1O\235\n\377\310\2\n\377\310\1\10\0\34\225\202z\0\td\364\317\327"..., 84) = 84 poll([{fd=5, events=POLLIN|POLLPRI}, {fd=4, events=POLLIN|POLLPRI}, {fd=6, events=POLLIN|POLLPRI}], 3, 1136) = 1 ([{fd=4, revents=POLLIN}]) read(4, "E\0\0T\352\3\0\0@\1\352\243\n\377\310\1\n\377\310\2\0\0$\225\202z\0\td\364\317\327"..., 1768) = 84 poll([{fd=5, events=POLLOUT}, {fd=4, events=0}, {fd=6, events=POLLIN|POLLPRI}], 3, 1136) = 1 ([{fd=5, revents=POLLOUT}]) sendto(5, "H\0\0\0\0\0\08\351\224\326<\202\221\261+\315\r}#\376\277\31\200A\34/\375P\243CR"..., 108, 0, {sa_family=AF_INET, sin_port=htons(50716), sin_addr=inet_addr("x.x.x.x")}, 16) = 108 poll([{fd=5, events=POLLIN|POLLPRI}, {fd=4, events=POLLIN|POLLPRI}, {fd=6, events=POLLIN|POLLPRI}], 3, 1136^Cstrace: Process 20185 detached
strace на openvpn процессе со включенным dco
# strace -fp 20213 strace: Process 20213 attached restart_syscall(<... resuming interrupted read ...>) = 0 poll([{fd=5, events=POLLIN|POLLPRI}, {fd=4, events=POLLIN|POLLPRI}, {fd=6, events=POLLIN|POLLPRI}], 3, 5131^Cstrace: Process 20213 detached <detached ...>
А со включенным dco openvpn большую часть времени отдыхает, пока его коллега в kernel-space трудится. Ниже под спойлером вывод команды strace.
Настройку на Fedora 38 я опишу в отдельной статье и выложу позже. Заходите ещё!