Коммутаторы, маршрутизаторы, брандмауэры — все это устройства, на которых держится интернет. Они перекидывают, фильтруют, дублируют и вырезают трафик такими способами, о которых большинство даже не догадывается. Без них вы бы не смогли прочитать этот текст.
Но сеть — это всего лишь один из уровней. Операционная система тоже играет по своим правилам: классификация, очереди, правила фаервола, NAT — все это влияет на то, что проходит, а что отбрасывается без следа. Каждый слой работает по-своему, и вместе они формируют ответ на вопрос: «А этот пакет вообще можно пропустить?»
Однажды мне стало интересно: а что будет, если отправить пакет с несуществующим транспортным протоколом? Не TCP, не UDP, не ICMP — вообще что-то выдуманное. Пропустит ли его ОС? Дойдет ли он хотя бы до сетевого интерфейса? Не зарежет ли его какой-нибудь промежуточный маршрутизатор? А вдруг он еще и быстрее обычного дойдет, потому что никто не знает, что с ним делать?
Ответа у меня не было. Так что я решил проверить.
Сначала — самый простой эксперимент: отправить такой пакет самому себе. Посмотреть, как мой собственный компьютер справится с этим ядом, который я приготовил. Потом — попробовать переслать его через океан на удаленную Linux-машину, чтобы проверить, смогут ли они действительно добраться туда.
Немного предыстории
Эту часть можно пропустить, если вы знаете, как устроен интернет. Остальным — добро пожаловать.
Что вообще такое «транспортный протокол»? Почему все говорят про TCP и UDP, но никто не говорит о протоколе номер 42?
Интернет — не магия, хоть иногда и выглядит как волшебство. На самом деле это аккуратная стопка протоколов. Каждый — со своей задачей, своей областью ответственности. Они передают данные друг другу, слой за слоем, пока байты не окажутся там, где надо.
Сверху — приложения. Браузеры, мессенджеры, игры. Они говорят: «дай страницу», «отправь сообщение», «подключи меня к серверу». Все это превращается в запрос. И этот запрос начинает обрастать метаданными: адресами, портами, заголовками. Слой за слоем. Пока не останется просто поток битов, уходящий в никуда.
IP говорит: «Этот пакет — туда». Он отвечает за доставку. Канальный уровень — это физическая передача: Wi-Fi, Ethernet, оптика. Тут мы останавливаться не будем, потому что интересное начинается дальше.
И вот он, транспортный уровень. Первый по-настоящему сложный уровень протокола. Тут уже не просто «отправь куда-то». Тут — «передай надежно», «раздели между приложениями», «проверь, все ли дошло». Здесь живут TCP и UDP.
В заголовке каждого IP-пакета есть поле Protocol. Если там стоит 6 — это TCP. Если 17 — это UDP. А еще есть десятки других номеров. Некоторые — задокументированы. Некоторые — зарезервированы на будущее. А некоторые — просто ничьи.
Что будет, если взять и отправить пакет с одним из этих «ничейных» номеров?
Эксперимент №1: Отправка трафика... самому себе!
В этом эксперименте слишком много переменных: моя ОС, мой роутер, гипотетическая ОС получателя и целая пачка промежуточных звеньев в интернете. Пытаться разгрести такую кашу — не самая благодарная задача. Поэтому я решил начать с самого простого: отправить пакеты самому себе.
Такой подход исключает все лишнее. Результаты будут зависеть только от поведения моей операционной системы и сетевого стека.
Я написал свой транспортный протокол. Назвал его HDP. Что он делает — неважно. Главное — он не похож ни на один из известных. Это что-то совсем чуждое, ни одна ОС такого не ждет.
Потом я написал сервер — он же слушатель. Он просто ждет пакеты с определенным номером протокола. А еще — клиент, который эти пакеты отправляет. Все просто.
План действий:
-
Запустить HDP-сервер
→ Он попросит ОС перенаправлять все IP-пакеты с номером протокола 255 на свой сокет.
-
Запустить HDP-клиент
→ Он отправит пакет на 127.0.0.1 — локальный адрес, он же loopback.
→ ОС направит пакет на интерфейс обратной петли.
→ Интерфейс решит: «Ага, это для нас» — и вернет пакет обратно в систему.
-
ОС доставит этот пакет на серверный сокет — без изменений… по крайней мере, я на это надеюсь.
Давайте попробуем. Открываю два терминала. В первом — сервер:
$ sudo cargo run --bin server
Во втором — клиент:
$ fortune | cowsay | sudo cargo run --bin client 127.0.0.1
3... 2... 1... Сервер получил сообщение!
$ sudo cargo run --bin server
~~~ IP Header ~~~
Version: 4
IHL: 5
DSCP: 0
ECN: 0
Total Length: 58625
Identification: 36455
Flags: 0
Fragment Offset: 0
TTL: 64
Protocol: 255
Header Checksum: 0
Source IP: [127, 0, 0, 1]
Destination IP: [127, 0, 0, 1]
~~~ HDP Header & Data ~~~
Source Port: 420
Destination Port: 420
Timestamp: 1739640243546134000
Data: _________________________________________
/ Marriage is not merely sharing the
| fettucine, but sharing the burden of |
| finding the fettucine restaurant in the |
| first place. |
| |
-- Calvin Trillin /
-----------------------------------------
^__^
(oo)_______
(__) )/
||----w |
|| ||
Победа! Пакет с номером 255 не только не был отброшен, но и вернулся обратно. ОС спокойно его приняла и передала серверу. Я ожидал подвоха. А его не было. Но эксперимент на этом не закончился. Мне стало интересно: а если отправить пакет не с 255, а с чем-то более... необычным?
Например:
-
6 — это TCP;
-
2 — это ICMP (все, что связано с ping);
-
256 — вообще за пределами допустимого диапазона.
Что сделает ОС? Примет? Отбросит? Или просто зависнет? Давайте узнаем:
fortune | cowsay | sudo cargo run --bin client 127.0.0.1 # На этот раз перебираем номера протоколов
Результаты
Огромная таблица
|
Protocol Number |
Source IP (Server) |
Byte Sum (Server) |
Received (Server) |
Succeeded (Client) |
Byte sum (Client) |
Failure reason (Client) |
Time difference (μs) |
|
0 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
70 |
|
1 |
nan |
nan |
🤯 |
🫡 |
373 |
- |
nan |
|
2 |
nan |
nan |
🤯 |
🫡 |
373 |
- |
nan |
|
3 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
61 |
|
4 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
52 |
|
5 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
54 |
|
6 |
nan |
nan |
🤯 |
🫡 |
373 |
- |
nan |
|
7 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
77 |
|
8 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
63 |
|
9 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
66 |
|
10 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
52 |
|
11 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
52 |
|
12 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
63 |
|
13 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
63 |
|
14 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
50 |
|
15 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
80 |
|
16 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
64 |
|
17 |
nan |
nan |
🤯 |
🫡 |
373 |
- |
nan |
|
18 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
42 |
|
19 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
82 |
|
20 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
71 |
|
21 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
59 |
|
22 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
50 |
|
23 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
51 |
|
24 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
54 |
|
25 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
46 |
|
26 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
48 |
|
27 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
43 |
|
28 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
46 |
|
29 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
66 |
|
30 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
56 |
|
31 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
65 |
|
32 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
56 |
|
33 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
49 |
|
34 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
47 |
|
35 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
48 |
|
36 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
59 |
|
37 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
47 |
|
38 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
45 |
|
39 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
52 |
|
40 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
57 |
|
41 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
56 |
|
42 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
51 |
|
43 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
45 |
|
44 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
58 |
|
45 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
52 |
|
46 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
50 |
|
47 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
46 |
|
48 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
51 |
|
49 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
84 |
|
50 |
nan |
nan |
🤯 |
🤯 |
- |
Operation not supported on socket (os error 102) |
nan |
|
51 |
nan |
nan |
🤯 |
🤯 |
- |
Operation not supported on socket (os error 102) |
nan |
|
52 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
92 |
|
53 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
115 |
|
54 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
81 |
|
55 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
83 |
|
56 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
96 |
|
57 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
71 |
|
58 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
69 |
|
59 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
80 |
|
60 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
84 |
|
61 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
105 |
|
62 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
109 |
|
63 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
97 |
|
64 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
100 |
|
65 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
94 |
|
66 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
124 |
|
67 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
101 |
|
68 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
100 |
|
69 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
87 |
|
70 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
95 |
|
71 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
101 |
|
72 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
97 |
|
73 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
111 |
|
74 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
104 |
|
75 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
115 |
|
76 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
96 |
|
77 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
77 |
|
78 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
65 |
|
79 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
54 |
|
80 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
150 |
|
81 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
95 |
|
82 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
97 |
|
83 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
74 |
|
84 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
93 |
|
85 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
71 |
|
86 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
77 |
|
87 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
70 |
|
88 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
49 |
|
89 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
59 |
|
90 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
74 |
|
91 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
78 |
|
92 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
61 |
|
93 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
59 |
|
94 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
55 |
|
95 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
46 |
|
96 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
59 |
|
97 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
94 |
|
98 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
66 |
|
99 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
54 |
|
100 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
53 |
|
101 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
91 |
|
102 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
148 |
|
103 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
111 |
|
104 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
119 |
|
105 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
75 |
|
106 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
52 |
|
107 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
53 |
|
108 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
52 |
|
109 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
44 |
|
110 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
59 |
|
111 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
51 |
|
112 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
45 |
|
113 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
75 |
|
114 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
91 |
|
115 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
85 |
|
116 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
84 |
|
117 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
64 |
|
118 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
24 |
|
119 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
46 |
|
120 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
62 |
|
121 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
48 |
|
122 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
50 |
|
123 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
50 |
|
124 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
49 |
|
125 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
74 |
|
126 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
54 |
|
127 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
46 |
|
128 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
103 |
|
129 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
73 |
|
130 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
57 |
|
131 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
49 |
|
132 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
62 |
|
133 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
43 |
|
134 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
47 |
|
135 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
90 |
|
136 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
112 |
|
137 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
94 |
|
138 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
53 |
|
139 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
57 |
|
140 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
74 |
|
141 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
64 |
|
142 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
77 |
|
143 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
77 |
|
144 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
75 |
|
145 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
77 |
|
146 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
88 |
|
147 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
96 |
|
148 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
106 |
|
149 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
72 |
|
150 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
80 |
|
151 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
77 |
|
152 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
78 |
|
153 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
91 |
|
154 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
75 |
|
155 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
80 |
|
156 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
96 |
|
157 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
110 |
|
158 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
105 |
|
159 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
83 |
|
160 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
89 |
|
161 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
95 |
|
162 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
111 |
|
163 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
103 |
|
164 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
97 |
|
165 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
91 |
|
166 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
95 |
|
167 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
84 |
|
168 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
57 |
|
169 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
50 |
|
170 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
65 |
|
171 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
75 |
|
172 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
80 |
|
173 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
78 |
|
174 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
67 |
|
175 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
55 |
|
176 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
60 |
|
177 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
85 |
|
178 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
78 |
|
179 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
73 |
|
180 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
79 |
|
181 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
91 |
|
182 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
96 |
|
183 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
88 |
|
184 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
95 |
|
185 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
91 |
|
186 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
74 |
|
187 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
92 |
|
188 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
79 |
|
189 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
75 |
|
190 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
81 |
|
191 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
96 |
|
192 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
95 |
|
193 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
91 |
|
194 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
88 |
|
195 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
92 |
|
196 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
99 |
|
197 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
90 |
|
198 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
90 |
|
199 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
100 |
|
200 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
96 |
|
201 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
89 |
|
202 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
100 |
|
203 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
92 |
|
204 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
109 |
|
205 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
104 |
|
206 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
108 |
|
207 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
95 |
|
208 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
96 |
|
209 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
71 |
|
210 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
76 |
|
211 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
71 |
|
212 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
78 |
|
213 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
91 |
|
214 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
97 |
|
215 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
97 |
|
216 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
93 |
|
217 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
105 |
|
218 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
97 |
|
219 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
91 |
|
220 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
98 |
|
221 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
90 |
|
222 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
108 |
|
223 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
92 |
|
224 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
104 |
|
225 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
109 |
|
226 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
94 |
|
227 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
99 |
|
228 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
94 |
|
229 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
79 |
|
230 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
84 |
|
231 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
79 |
|
232 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
102 |
|
233 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
101 |
|
234 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
113 |
|
235 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
95 |
|
236 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
100 |
|
237 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
91 |
|
238 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
106 |
|
239 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
92 |
|
240 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
97 |
|
241 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
89 |
|
242 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
99 |
|
243 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
90 |
|
244 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
98 |
|
245 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
93 |
|
246 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
94 |
|
247 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
91 |
|
248 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
94 |
|
249 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
94 |
|
250 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
90 |
|
251 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
88 |
|
252 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
94 |
|
253 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
96 |
|
254 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
76 |
|
255 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
nan |
|
255 |
127.0.0.1 |
373 |
🫡 |
🫡 |
373 |
- |
71 |
|
256 |
nan |
nan |
🤯 |
🤯 |
- |
Invalid argument (os error 22) |
nan |
Что за сбои?
Большинство номеров протоколов сработали нормально — ОС принимала пакеты, возвращала их на loopback-интерфейс, и сервер их успешно получал. Но не все номера оказались такими покладистыми. Некоторые пакеты терялись в пути, причем по разным причинам:
-
Протоколы 1, 2 и 6 (например, ICMP и TCP) не доходили до сервера. Клиент их отправлял, но ОС на серверной стороне перехватывала их до того, как они попадали в сокет.
-
Протоколы 50 и 51 даже не уходили с клиента — ОС отказывалась их отправлять.
-
Протокол 256 вообще не проходил вызов socket() — значение вне диапазона, невалидно.
Почему так? Что именно заставляет ОС вести себя по-разному?
Системные вызовы: что на самом деле важно
Одна из самых полезных техник отладки, которую я освоил в ходе моего эксперимента — при работе с низкоуровневым кодом отслеживать системные вызовы, которые выполняет процесс.
Системный вызов для непосвященных — это просто функция, которая позволяет приложениям запрашивать привилегированные ресурсы у ОС, будь то открытие файла, выделение памяти или, в нашем случае, отправка пакета по сети.
В моем коде на Rust я использовал библиотеку socket2, которая оборачивает системные вызовы в удобный интерфейс. Вот пример кода, создающего сокет:
int sockfd = socket(
AF_INET, // Домен: Интернет-протоколы ARPA. Это сообщает ОС, что нас интересуют IP-протоколы
SOCK_RAW, // Тип: Сырой сокет. Обычно ОС обрабатывает транспортный уровень, но это дает нам полный контроль.
255 // Протокол: Мы перебирали это поле.
);
Этот вызов говорит ОС: «Я хочу прямой доступ к IP-пакетам, вот протокол, с которым собираюсь работать». А дальше уже от ОС зависит — пустит она нас или нет.
Возвращаясь к неудачам
1, 2 и 6: Сервер их не видит
Эти пакеты клиент успешно отправлял, но сервер их не видел вовсе. Значит, ОС что-то сделала с ними по пути — возможно, перехватила, отбросила или обработала по-другому.
Я предполагал, что мой сервер сможет принимать вообще все IP-пакеты. Сокет я создавал вот так:
int sockfd = socket(
AF_INET, // Интернет-домен
SOCK_RAW, // Сырой сокет: должен дать нам полный контроль
0 // Пусть ОС решает, какой протокол использовать
);
Я думал, что 0 — это универсальный вариант. Мол, «передай все, что можешь». Но оказалось, это не так.
Для контекста: эксперименты я запускал на Mac, который работает на Darwin. Darwin похож на BSD, но с тонной макияжа. Поэтому он унаследовал не только системные вызовы, но и все причуды BSD-сокетов.
Немного покопавшись в документации, я не нашел ничего полезного про protocol = 0. Но потом наткнулся в BSD-документации на раздражающе расплывчатую фразу:
Значение 0 для protocol позволит системе выбрать подходящий протокол для запрошенного типа сокета.
То есть, вместо того чтобы честно передавать все подряд, система молча и без объяснений фильтрует пакеты. Например, ICMP(1), IGMP(2) или TCP(6) просто не доходят до моего сокета. Видимо, Darwin решил, что он лучше знает, что именно я должен получать.
Вот и ответ: не все сокеты одинаково полезны. А с Darwin — еще и не всегда предсказуемы.
50 и 51: Клиент даже не может их отправить
На этом этапе стало понятно: есть номера протоколов, к которым ОС относится как к особо охраняемым. Протоколы 50 и 51 — это не просто какие-то случайные значения. Это IPSec: ESP и AH, применяемые для шифрования VPN-трафика.
Darwin категорически отказался их отправлять. Почему? Точной причины нет. Но я предполагаю, что это встроенная мера безопасности: мол, если ты не VPN, не лезь.
256: Вызов socket() немедленно завершается неудачей
Этот случай прост:
-
поле protocol в IP-заголовке — 8-битное;
-
максимальное значение — 255;
-
256 просто не помещается.
ОС моментально отвергла этот аргумент, даже не попытавшись его обработать.
Честно говоря, ничего удивительного. Но вот что действительно удивило — это поведение Linux.
После всех этих несостыковок я решил: а давай-ка посмотрим, что скажет Linux. Поднял виртуалку, повторил все шаги — и сразу увидел другое поведение.
Linux, в отличие от Darwin, не позволяет привязать сырой сокет к протоколу 0. Но при этом он разрешил использовать некоторые нестандартные значения, включая те, которые на macOS даже не создавали сокет. В том числе — 256.
Результаты я сохранил в results_no_server_linux_client_loopback. И остался доволен тем, что хотя бы часть моих ожиданий оправдалась.
Извлеченные уроки
Написать свой транспортный протокол — технически возможно. Но ОС от этого будет не в восторге. Сетевой стек забит предположениями, которые не всегда очевидны, а «сырой» сокет оказывается не таким уж и сырым.
Все это, как мне кажется, и объясняет, почему подавляющее большинство новых протоколов живут на уровне приложений. Вместо того чтобы бодаться с ОС и файрволлами, инженеры просто строят поверх чего-то, что уже работает. Например, QUIC — это фактически новый транспортный протокол, но он катается на UDP и избегает всей этой возни.
Если вдруг решите поиграть с сырыми сокетами — умоляю, проверяйте код на разных ОС. То, что позволяет сделать Darwin, может вызывать ступор у Linux. А то, что позволяет сделать Linux, в Windows работать вообще не будет. Поведение не стандартизовано, даже если все клянутся в POSIX-соответствии.
Следующий шаг: что происходит за пределами loopback?
До сих пор эти пакеты никогда не покидали мой компьютер. Теперь я хочу отправить HDP через публичный интернет:
-
Будут ли маршрутизаторы пересылать его или отбрасывать?
-
Пропустят ли его брандмауэры или отметят как атаку?
-
Будет ли у него другая задержка по сравнению с TCP?
-
Не уроню ли я случайно DigitalOcean?
Пора выяснить.
Эксперимент №2:
Изначально казалось, что этот эксперимент будет простым (спойлер: НЕТ). Я хотел взять самый дешевый на DigitalOcean, запустить там сервер и начать швыряться в него чем попало: TCP, UDP, мой кастомный HDP и все остальное. Считать потери, смотреть задержки, делать выводы. В теории — просто.
На практике… все пошло не так. Не потому, что что-то не настроилось. А потому что результаты были странные. Они не вписывались в мои ожидания, и я не был морально готов их распутывать.
Настройка сервера
Я арендовал самый дешевый
root@debian-s-1vcpu-512mb-10gb-fra1-01:~# curl myip.wtf
161.35.222.56
root@debian-s-1vcpu-512mb-10gb-fra1-01:~# curl ipinfo.io/161.35.222.56
{
"ip": "161.35.222.56",
"city": "Frankfurt am Main",
"region": "Hesse",
"country": "DE",
"loc": "50.1155,8.6842",
"org": "AS14061 DigitalOcean, LLC",
"postal": "60306",
"timezone": "Europe/Berlin",
"readme": "https://ipinfo.io/missingauth"
}
Франкфурт. Отлично. А клиент у меня работает из Саудовской Аравии. Эксперимент — межконтинентальный. Прежде чем кидаться пакетами, я решил проверить, как пингуется сервер:
❯ ping 161.35.222.56
PING 161.35.222.56 (161.35.222.56): 56 data bytes
64 bytes from 161.35.222.56: icmp_seq=0 ttl=47 time=125.364 ms
64 bytes from 161.35.222.56: icmp_seq=1 ttl=47 time=128.061 ms
64 bytes from 161.35.222.56: icmp_seq=2 ttl=47 time=177.931 ms
64 bytes from 161.35.222.56: icmp_seq=3 ttl=47 time=225.798 ms
64 bytes from 161.35.222.56: icmp_seq=4 ttl=47 time=130.101 ms
64 bytes from 161.35.222.56: icmp_seq=5 ttl=47 time=194.563 ms
64 bytes from 161.35.222.56: icmp_seq=6 ttl=47 time=159.518 ms
64 bytes from 161.35.222.56: icmp_seq=7 ttl=47 time=134.343 ms
64 bytes from 161.35.222.56: icmp_seq=8 ttl=47 time=501.139 ms
64 bytes from 161.35.222.56: icmp_seq=9 ttl=47 time=153.672 ms
64 bytes from 161.35.222.56: icmp_seq=10 ttl=47 time=137.927 ms
64 bytes from 161.35.222.56: icmp_seq=11 ttl=47 time=355.672 ms
64 bytes from 161.35.222.56: icmp_seq=12 ttl=47 time=138.777 ms
64 bytes from 161.35.222.56: icmp_seq=13 ttl=47 time=166.116 ms
64 bytes from 161.35.222.56: icmp_seq=14 ttl=47 time=288.758 ms
64 bytes from 161.35.222.56: icmp_seq=15 ttl=47 time=151.458 ms
64 bytes from 161.35.222.56: icmp_seq=16 ttl=47 time=164.025 ms
64 bytes from 161.35.222.56: icmp_seq=17 ttl=47 time=170.132 ms
64 bytes from 161.35.222.56: icmp_seq=18 ttl=47 time=279.034 ms
^C
--- 161.35.222.56 ping statistics ---
19 packets transmitted, 19 packets received, 0.0% packet loss
Похоже, он довольно далеко, но мне кажется все в порядке. Давайте отправим несколько пакетов, используя наш новый протокол!
Сначала запускаю сервер на машине DigitalOcean:
root@debian-s-1vcpu-512mb-10gb-fra1-01:~/hdp/hdp# sudo cargo run --bin server
Listening on protocol 255
И со своего Mac отправляю пакет:
❯ fortune | cowsay | sudo cargo run --bin client 161.35.222.56
| Protocol Number | Succeeded (Client) | Time (μs) (Client) | Byte sum (Client) | Failure reason (Client) |
| 255 | 🫡 | timestamp | 563 | - |
Пакет отправлен. Давайте снова проверим сервер:
root@debian-s-1vcpu-512mb-10gb-fra1-01:~/hdp/hdp# sudo cargo run --bin server
Listening on protocol 255
| Protocol Number | Time (μs) (Server) | Source IP (Server) | Byte Sum (Server) |
| --- | --- | --- |
| 255 | timestamp | my_ip | 563 |
Отлично. Похоже, все прошло хорошо, по крайней мере, так я думал. На самом деле, с этого момента все пошло под откос. Я сделал короткий перерыв, а затем вернулся и попробовал отправить пакет снова:
| Protocol Number | Time (μs) (Server) | Source IP (Server) | Byte Sum (Server) |
| --- | --- | --- |
| 255 | timestamp | my_ip | 563 |
Зависло? Я не вижу второй пакет. Пусто. Второй пакет не приходит. Жму Ctrl+C, пробую еще. Ноль. Лезу в tcpdump:
❯ sudo tcpdump -i any 'ip[9] == 255'
tcpdump: data link type PKTAP
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on any, link-type PKTAP (Apple DLT_PKTAP), snapshot length 524288 bytes
IP mac > 161.35.222.56: reserved 427
IP mac > 161.35.222.56: reserved 427
IP mac > 161.35.222.56: reserved 427
IP mac > 161.35.222.56: reserved 427
IP mac > 161.35.222.56: reserved 427
IP mac > 161.35.222.56: reserved 427
IP mac > 161.35.222.56: reserved 427
IP mac > 161.35.222.56: reserved 427
Хорошо. Значит, мой клиент их точно отправляет. Что там на сервере?
root@debian-s-1vcpu-512mb-10gb-fra1-01:~/hdp# tcpdump -i any 'ip[9] > 17'
tcpdump: data link type LINUX_SLL2
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on any, link-type LINUX_SLL2 (Linux cooked v2), snapshot length 262144 bytes
Тут у меня начали сдавать нервы. Я пересмотрел старые логи — нет, я не сошел с ума. Пакет действительно доходил. Метка времени, сумма байт — все совпадает. Но остальные — исчезают в пустоте. Может, Торвальдс лично меня газлайтит?
Стоп. Как NAT-устройство моего интернет-провайдера переслало пакет? NAT полагается на порты — но мой протокол для них просто черная магия. Так что вполне возможно, он просто не знает, что с этим делать — и блокирует. После небольшого ресерча я нашел подтверждение: DigitalOcean не поддерживает нестандартные IP-протоколы.

Что, впрочем, только еще больше запутало ситуацию. Если они не поддерживают, то как первый пакет прошел? Ответа нет. Все, что у меня осталось — это лог, доказывающий, что он действительно дошел.
Самая. Последняя. Попытка.
Если где-то и существует облачный провайдер, поддерживающий нестандартные IP-протоколы, то это AWS. Я поднял две виртуалки, настроил сервер и клиент. Оно работает!
admin@ip-172-31-13-218:~/hdp$ sudo cargo run --bin server 255
Server is listening on SockAddr { ss_family: 2, len: 16 }, protocol: 255
| Protocol Number | Time (μs) (Server) | Source IP (Server) | Byte Sum (Server) |
| --- | --- | --- |
| 255 | timestamp | 54.153.13.186 | 33 |
| 255 | timestamp | 54.153.13.186 | 34 |
| 255 | timestamp | 54.153.13.186 | 35 |
| 255 | timestamp | 54.153.13.186 | 36 |
Правда, сервер находился всего в двух хопах от клиента, так что страшных приключений через весь интернет не было.
Я замерил разницу между HDP и UDP. Разница есть — но настолько крошечная, что ей можно пренебречь: примерно 20 мкс в среднем. Никакой реальной выгоды.
А как насчет интернета?
Я попробовал отправлять пакеты со своего Mac на сервер в AWS — и получил тот же эффект: первый пакет доходит, и все. Оставил результаты в файле tcpdump_tokyo_server_mac_client.md. Послал по одному пакету для каждого протокола. Работают только TCP, UDP и ICMP. Все остальное — тишина после первого запроса.
Как и ожидалось, между машиной на DigitalOcean и AWS ничего не передается. Гарантированно ничего сказать нельзя, но выводы напрашиваются.
Что я понял
Теоретически — да, можно использовать свой IP-протокол. Но если вы не мазохист — не надо.
-
Код будет привязан к ОС. Поддерживать это на кросс-платформе — отдельный ад.
-
NAT, фаерволы, маршрутизаторы — все будет стараться убить ваш пакет. И в большинстве случаев — убьет.
-
Локально, может, и заработает. В интернете — забудьте.
-
И главное: никакого выигрыша по задержке за счет использования нестандартного протокола я не увидел.
TL;DR: Используйте UDP или TCP.
А если через IPv6?
Несколько читателей предложили попробовать мой протокол по IPv6 — там нет NAT'а, как в IPv4. Любопытно? Еще бы.
Я добавил поддержку IPv6, подключился к тому же серверу на AWS:
admin@ip-172-31-2-72:~/hdp$ RUST_BACKTRACE=full sudo cargo run --bin server 6 255 # The 6 is for IPv6
| Protocol Number | Time (μs) (Server) | Source IP (Server) | Byte Sum (Server) |
| --- | --- | --- |
Теперь запускаю клиента с Mac, через океаны и материки. И — вуаля:
❯ fortune | cowsay | sudo cargo run --bin client 200 '2600:1f1c:1cf:b1ce:f653:afc7:4650:8aa0' 255 hdp
Running `target/debug/client 2000 '2600:1f1c:1cf:b1ce:f653:afc7:4650:8aa0' 17 udp`
| Protocol Number | Succeeded (Client) | Time (μs) (Client) | Byte sum (Client) | Failure reason (Client) |
| 255 | 🫡 | 1740779795209398 | 49 | - |
| 255 | 🫡 | 1740779795413344 | 50 | - |
| 255 | 🫡 | 1740779795620781 | 51 | - |
Он появляется на сервере | 255 | 1740779715088323 | my_ip | 49 |
Сработало! Это были настоящее американские горки, и поездка удалась.
Полезные ссылки
-
Спецификация UDP-протокола — настолько минимальна, что это даже смешно.
-
Протоколы, официально поддерживаемые IP.
-
Статья про отличия raw sockets в Linux и FreeBSD.
-
Интересный ответ на тему: как реализовать NAT для чего-то кроме TCP/UDP.
Автор: Basis_Habr
