Сетевые соединения X11
Есть две технологии в ИТ, которые казалось должны были исчезнуть на рубеже прошлого века, но их живучесть и удобство раз за разом отодвигает их уход со сцены. Речь идет об IPv4 и X11. Если первый из них практически во всех аспектах уступает IPv6, то преимущества Wayland, как технологии над X11 очевидны не всем. Wayland вовсе не универсален, как X Windows System, он намного более прост. Это дает ему ряд преимуществ по сравнению с иксами, но в этом же кроются его недостатки.
Если говорить о преимуществах, то это в первую очередь простота реализации и долгожданное избавление пользователей графической среды Linux от таких артефактов перерисовки, как разрывы изображения, a․ k․ a․ tearing. С этим особенно часто сталкиваются обладатели видео карт NVidia. Хватает и недостатков и противники замены X-сервера напирают на гибкость использования сетевых возможностей в различных сценариях.
As mentioned, X is essentially a networking protocol with graphical displaying capabilities.
На самом деле X11 сетевой протокол с графическими возможностями, а не наоборот. Из этого простого факта следует, что даже если в скором времени основные дистрибутивы Linux перейдут на протокол Wayland, многие из возможностей и примеров использования X сервера будут востребованы еще достаточно долгое время. Удаленный доступ к приложениям и сессиям X.Org — современной реализации X Window System, одна из таких ключевых особенностей системы.
▍ Сетевая структура взаимодействий X-сервера
Наиболее распространенным IPC, т․ е․ способом взаимодействия между процессами, X-клиента и X-сервера, являются сокеты. Их роль в предоставлении API связи с использование TCP/IP, а также сокетов домена Unix. Помимо сокетов клиент и сервер для коммуникации могут также использовать иные каналы IPC, например MIT Shared Memory Extension.
Доменные сокеты Unix (от англ. Unix Domain Sockets, UDS) являются POSIX-механизмом IPC, с помощью которого различные процессы ОС могут взаимодействовать друг с другом. Такие сокеты эффективнее локальных TCP/IP соединений, так как не требуют дополнительных байтов в заголовке протокола. Подобно сокетам TCP/IP, доменные сокеты Unix поддерживают надёжную потоковую передачу данных с помощью SOCK_STREAM. Они также могут работать в режиме упорядоченной (см․ SOCK_SEQPACKET) и неупорядоченной (см․ SOCK_DGRAM) передачи датаграмм.
Рис. 1 Сетевое взаимодействие между клиентом и сервером X с помощью UDS.
UDS используют файловую систему ОС в качестве адресного пространства имён (например /tmp/my_xapp
), сами сокеты в ней всего лишь inode, а процессы обращаются к сокетам, так же, как к файлу. Однако обмен данными в активном соединении использует не файловую систему, а только буферы памяти ядра.
Рис. 2 Сетевое взаимодействие между клиентом и сервером X поверх TCP/IP.
Сокеты TCP/IP и UDS создаются с помощью функции CreateWellKnownSockets (). Для этого она обращается к _XSERVTransMakeAllCOTSServerListeners
. Эта процедура пробегает по каждому интерфейсу транспорта, поддерживаемого сервером.
static Bool
TryCreateSocket(int num, int *partial)
{
char port[20];
snprintf(port, sizeof(port), "%d", num);
return (_XSERVTransMakeAllCOTSServerListeners(port, partial,
&ListenTransCount,
&ListenTransConns) >= 0);
}
Количество таких интерфейсов определено константой NUMTRANS
в файле X11/Xtrans/Xtrans.c.
#define NUMTRANS (sizeof(Xtransports)/sizeof(Xtransport_table))
Таблица Xtransports[]
определена в том же самом файле и содержит 10 элементов, таким образом NUMTRANS
≤ 10.
|adm@redeyes:[~]> grep '{ &TRANS' /usr/include/X11/Xtrans/Xtrans.c
{ &TRANS(SocketTCPFuncs), TRANS_SOCKET_TCP_INDEX },
{ &TRANS(SocketINET6Funcs), TRANS_SOCKET_INET6_INDEX },
{ &TRANS(SocketINETFuncs), TRANS_SOCKET_INET_INDEX },
{ &TRANS(SocketLocalFuncs), TRANS_SOCKET_LOCAL_INDEX },
{ &TRANS(SocketUNIXFuncs), TRANS_SOCKET_UNIX_INDEX },
{ &TRANS(LocalFuncs), TRANS_LOCAL_LOCAL_INDEX },
{ &TRANS(PTSFuncs), TRANS_LOCAL_PTS_INDEX },
{ &TRANS(NAMEDFuncs), TRANS_LOCAL_NAMED_INDEX },
{ &TRANS(PIPEFuncs), TRANS_LOCAL_PIPE_INDEX },
{ &TRANS(SCOFuncs), TRANS_LOCAL_SCO_INDEX },
Сами же флаги, которые во время сборки определяют транспортные интерфейсы X сервера, можно найти в xc/config/cf/linux.cf
.
#if HasDECnet
# define ConnectionFlags -DUNIXCONN -DTCPCONN -DDNETCONN
# define ExtraLibraries -ldnet
#else
# define ConnectionFlags -DUNIXCONN -DTCPCONN
#endif
Проследив цепочку до системного вызова socket()
находим такую последовательность транспортных процедур.
TRANS(MakeAllCOTSServerListeners)
|
v +-------------------------+
+----->TRANS(OpenCOTSServer)+--->|TRANS(SocketSelectFamily)|
| | |TRANS(SocketOpen) |
| v +-------+-----------------+
| TRANS(Open) |
| | |
| v v
+------+TRANS(ParseAddress) socket()
Серверный процесс стартует с вызова функции TRANS(CreateListener)
из Xtrans.c
. Далее переход в состояние ожидания входящего соединения происходит в такой последовательности.
TRANS(MakeAllCOTSServerListeners)
|
v
TRANS(CreateListener)
|
v
TRANS(SocketCreateListener)
|
v
bind()
В файле Xtransmit.h
можно обнаружить X_TCP_PORT
. Первое клиентское соединение будет использовать порт 6000, второе — 6001 и т․ д․
#define X_TCP_PORT 6000
▍ Взгляд изнутри трафика между клиентом и сервером X
Запустим простейшую иксовую программу Xclock и проверим как все это работаете на практике.
|admin@redeye:[~]> DISPLAY=tcp/192.168.10.10:0.0 strace -e trace=network -f xclock 2>& 1 |grep -A3 TCP
1. socket(AF_INET, SOCK_STREAM|SOCK_CLOEXEC, IPPROTO_TCP) = 3
2. setsockopt(3, SOL_TCP, TCP_NODELAY, [1], 4) = 0
3. setsockopt(3, SOL_SOCKET, SO_KEEPALIVE, [1], 4) = 0
4. connect(3, {sa_family=AF_INET, sin_port=htons(6000), sin_addr=inet_addr("192.168.10.10")}, 16) = 0
В первой строке вывода видим, что системный вызов socket()
завершился успешно и вернул файловый десткриптор 3. Далее, во второй строке setsockopt()
выставляет параметр TCP_NODELAY
, с которым TCP фрагменты отправляются по сети как можно ранее, даже если эти фрагменты слишком малы. В третьей строке опять встречается setsockopt()
на это раз для активации опции SO_KEEPALIVE
. Данный параметр поддерживает соединение в активном состоянии, с помощью периодической отправки служебных сообщений. Наконец в четвертой строке системный вызов connect()
соединяет клиент X с сервером, по TCP порту 6000.
Используемый TCP порт напрямую связан с количеством соединений и номером переменной $DISPLAY
, если бы использовалось DISPLAY=tcp/192.168.10.10:1.0
, то тогда в последней строке connect()
состоялся бы по TCP порту 6001.
После рукопожатия обмен данными происходит с использованием структур X-клиента — xConnClientPrefix
, и X-сервера — xConnSetupPrefix
, XconnSetup
, определенных в файле /usr/include/X11/Xproto.h
. Размер занимаемой памяти в структур в байтах определен в #define
директивах заголовочного файла.
#define sz_xConnClientPrefix 12
#define sz_xConnSetupPrefix 8
#define sz_xConnSetup 32
...
typedef struct {
CARD8 byteOrder;
BYTE pad;
CARD16 majorVersion, minorVersion;
CARD16 nbytesAuthProto; /* Authorization protocol */
CARD16 nbytesAuthString; /* Authorization string */
CARD16 pad2;
} xConnClientPrefix;
...
typedef struct {
CARD8 success;
BYTE lengthReason; /*num bytes in string following if failure */
CARD16 majorVersion,
minorVersion;
CARD16 length; /* 1/4 additional bytes in setup info */
} xConnSetupPrefix;
Рис. 3 Установка параметров соединения в сессии X11.
Можно также пронаблюдать за процессом с помощью сетевого анализатора, клиентский запрос Initial connection request соответствует вызову xConnClientPrefix
. Соответственно Initial connection reply в следующем пакете создан со стороны xConnSetupPrefix
и xConnSetup
. Для сбора трафика можно запустить Wireshark и слушать локальный интерфейс, либо с помощью следующей команды tcpdump.
|root@redeye:[~]> tcpdump -i lo -vv not arp -w x11_trace.pcapng
Далее, сам файл можно открыть в Wireshark и отфильтровать просмотр по протоколу x11
. Обычно для сбора трафика нужны привилегии root.
Рис. 4 Сетевой след соединения X11 в WireShark.
То же самое и во всех подробностях можно увидеть в tshark в текстовом формате. Так выглядит клиентский запрос X11 в сторону сервера.
Frame 11: 134 bytes on wire (1072 bits), 134 bytes captured (1072 bits)
Encapsulation type: Ethernet (1)
...
[Protocols in frame: eth:ethertype:ipv6:tcp:x11]
Ethernet II, Src: 00:00:00_00:00:00 (00:00:00:00:00:00), Dst: 00:00:00_00:00:00 (00:00:00:00:00:00)
Destination: 00:00:00_00:00:00 (00:00:00:00:00:00)
Address: 00:00:00_00:00:00 (00:00:00:00:00:00)
...
Internet Protocol Version 6, Src: ::1, Dst: ::1
0110 .... = Version: 6
...
Transmission Control Protocol, Src Port: 49202, Dst Port: 6010, Seq: 1, Ack: 1, Len: 48
Source Port: 49202
Destination Port: 6010
...
X11, Request, Initial connection request
byte-order: 0x6c (Little-endian)
unused
protocol-major-version: 11
protocol-minor-version: 0
authorization-protocol-name-length: 18
authorization-protocol-data-length: 16
unused
authorization-protocol-name: MIT-MAGIC-COOKIE-1
Пакет с ответом со стороны X-сервера совсем не так лаконичен, в нем указаны все необходимые детали для форматирования графического вывода на экран.
Frame 13: 14698 bytes on wire (117584 bits), 14698 bytes captured (117584 bits)
Encapsulation type: Ethernet (1)
...
[Protocols in frame: eth:ethertype:ipv6:tcp:x11]
Ethernet II, Src: 00:00:00_00:00:00 (00:00:00:00:00:00), Dst: 00:00:00_00:00:00 (00:00:00:00:00:00)
Destination: 00:00:00_00:00:00 (00:00:00:00:00:00)
Address: 00:00:00_00:00:00 (00:00:00:00:00:00)
...
Internet Protocol Version 6, Src: ::1, Dst: ::1
0110 .... = Version: 6
...
Transmission Control Protocol, Src Port: 6010, Dst Port: 49202, Seq: 1, Ack: 49, Len: 14612
Source Port: 6010
...
X11, Reply, Initial connection reply
success: 1
unused
protocol-major-version: 11
protocol-minor-version: 0
replylength: 3651
release-number: 12011000
resource-id-base: 0x0b200000
resource-id-mask: 0x001fffff
motion-buffer-size: 256
length-of-vendor: 20
maximum-request-length: 65535
number-of-screens-in-roots: 1
number-of-formats-in-pixmap-formats: 7
image-byte-order: 0x00 (LSBFirst)
bitmap-format-bit-order: 0x00 (LSBFirst)
bitmap-format-scanline-unit: 32
bitmap-format-scanline-pad: 32
min-keycode: 8
max-keycode: 255
unused
vendor: The X.Org Foundation
pixmap-format
pixmap-format
...
pixmap-format
...
screen
screen (000007a4: 1920 x 1080 x 24)
root: 0x000007a4
default_colormap: 0x00000020
white_pixel: 0x00ffffff
black_pixel: 0x00000000
current_input_masks: 0x00fa8033
width_in_pixels: 1920
height_in_pixels: 1080
width_in_millimeters: 451
height_in_millimeters: 254
min_installed_maps: 1
max_installed_maps: 1
root_visual: 0x00000021
backing_stores: 0x01
save_unders: False
root_depth: 24
allowed_depths_len: 7
depth-detail
depth-detail
depth: 24
unused
visualtypes-numbers: 576
unused
visualtype
visualtype
...
visualtype
...
visualtype
...
▍ Удаленное подключение к X-серверу по ssh
Переходя от теории сетевых взаимодействий X Window System к практике, рассмотрим, как запустить графическое приложение на уделенном X-сервере при подключении по ssh.
|admin@redeye:[~]> ssh -X my.remote.host
|admin@redeye:[~]> ssh -Y my.remote.host
- Подключение необходимо запускать с ключом
-X
, который активирует переадресацию X11. На практике, однако из-за X11 SECURITY extension это часто не работает. В таких случаях используют-Y
для подключения с доверительной переадресацией X11. - Убедитесь, что на удаленном сервере установлен пакет
xauth
, который создаст файл~/.Xauthorit
y. Если все нормально, то файл уже создан и проверка показывает на наличие MAGIC COOKIE.
|admin@redeye:[~]> xauth list
host/unix:0 MIT-MAGIC-COOKIE-1 00000000000000111111111111111111
- Общепринятым наименьшим общим делителем иксов является xclock, в качестве проверки запустите эту программу прежде, чем запускать JAVA installer, или мастер настроек некоей корпоративной CRM, или ERP системы.
Рис. 5 Собственно xclock, значит удаленные иксы настроены и работают.
- Если после логина по ssh была выполнена команда смены пользователя
sudo su -
для перехода в root, то тогда необходимо выставить переменную DISPLAY. Этого не надо делать, если используется изначальный пользователь и сеанс shell.
|admin@redeye:[~]> export DISPLAY=:0
echo $DISPLAY
:0
Я стараюсь по мере возможности тестировать сеанс Wayland при каждом новом релизе KDE Plasma, несмотря на многочисленные улучшения с каждым разом, на версии kde-apps-21.04.3 работать можно лишь на Plasma-X11. В чем бы не состояли преимущества Wayland, до тех пор пока в терминале зависает команда man, хаотично прыгают элементы ниспадающего меню и блуждает внешний монитор, старый и добрый X Window System остается вне конкуренции. Надеюсь так не будет продолжаться долго.