Настройка OpenVPN DCO на RockyLinux 9

Как-то на очередных выходных я решил актуализировать свои виртуальные и не очень серверы. Дома стоит 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 процесс занимается обработкой трафика:

source: community.openvpn.net

Так вот, теперь у 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:

Картинка взята с openvpn.net

Кстати, поддержка 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 я опишу в отдельной статье и выложу позже. Заходите ещё!

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Оптимизировано Optimole
ru_RUРусский