ПишСм Π·Π°Ρ‰ΠΈΡ‚Ρƒ ΠΎΡ‚ DDoS-Π°Ρ‚Π°ΠΊ Π½Π° XDP. ЯдСрная Ρ‡Π°ΡΡ‚ΡŒ

ВСхнология eXpress Data Path (XDP) позволяСт Π²Ρ‹ΠΏΠΎΠ»Π½ΠΈΡ‚ΡŒ ΠΏΡ€ΠΎΠΈΠ·Π²ΠΎΠ»ΡŒΠ½ΡƒΡŽ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΡƒ Ρ‚Ρ€Π°Ρ„ΠΈΠΊΠ° Π½Π° интСрфСйсах Linux Π΄ΠΎ Ρ‚ΠΎΠ³ΠΎ, ΠΊΠ°ΠΊ ΠΏΠ°ΠΊΠ΅Ρ‚Ρ‹ поступят Π² сСтСвой стСк ядра. ΠŸΡ€ΠΈΠΌΠ΅Π½Π΅Π½ΠΈΠ΅ XDP β€” Π·Π°Ρ‰ΠΈΡ‚Π° ΠΎΡ‚ DDoS-Π°Ρ‚Π°ΠΊ (CloudFlare), слоТныС Ρ„ΠΈΠ»ΡŒΡ‚Ρ€Ρ‹, сбор статистики (Netflix). ΠŸΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΡ‹ XDP ΠΈΡΠΏΠΎΠ»Π½ΡΡŽΡ‚ΡΡ Π²ΠΈΡ€Ρ‚ΡƒΠ°Π»ΡŒΠ½ΠΎΠΉ машиной eBPF, поэтому ΠΈΠΌΠ΅ΡŽΡ‚ ограничСния ΠΊΠ°ΠΊ Π½Π° свой ΠΊΠΎΠ΄, Ρ‚Π°ΠΊ ΠΈ Π½Π° доступныС Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ ядра Π² зависимости ΠΎΡ‚ Ρ‚ΠΈΠΏΠ° Ρ„ΠΈΠ»ΡŒΡ‚Ρ€Π°.

Π‘Ρ‚Π°Ρ‚ΡŒΡ ΠΏΡ€ΠΈΠ·Π²Π°Π½Π° Π²ΠΎΡΠΏΠΎΠ»Π½ΠΈΡ‚ΡŒ нСдостатки многочислСнных ΠΌΠ°Ρ‚Π΅Ρ€ΠΈΠ°Π»ΠΎΠ² ΠΏΠΎ XDP. Π’ΠΎ-ΠΏΠ΅Ρ€Π²Ρ‹Ρ…, Π² Π½ΠΈΡ… даСтся Π³ΠΎΡ‚ΠΎΠ²Ρ‹ΠΉ ΠΊΠΎΠ΄, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ сразу ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΡ‚ особСнности XDP: ΠΏΠΎΠ΄Π³ΠΎΡ‚ΠΎΠ²Π»Π΅Π½ для Π²Π΅Ρ€ΠΈΡ„ΠΈΠΊΠ°Ρ†ΠΈΠΈ ΠΈΠ»ΠΈ слишком прост, Ρ‡Ρ‚ΠΎΠ±Ρ‹ Π²Ρ‹Π·Π²Π°Ρ‚ΡŒ ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΡ‹. ΠŸΡ€ΠΈ ΠΏΠΎΠΏΡ‹Ρ‚ΠΊΠ΅ ΠΏΠΎΡ‚ΠΎΠΌ Π½Π°ΠΏΠΈΡΠ°Ρ‚ΡŒ свой ΠΊΠΎΠ΄ с нуля Π½Π΅Ρ‚ понимания, Ρ‡Ρ‚ΠΎ Π΄Π΅Π»Π°Ρ‚ΡŒ с характСрными ошибками. Π’ΠΎ-Π²Ρ‚ΠΎΡ€Ρ‹Ρ…, Π½Π΅Β ΠΎΡΠ²Π΅Ρ‰Π°ΡŽΡ‚ΡΡ способы локально Ρ‚Π΅ΡΡ‚ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ XDP Π±Π΅Π· Π’Πœ ΠΈ Β«ΠΆΠ΅Π»Π΅Π·Π°Β», ΠΏΡ€ΠΈ Ρ‚ΠΎΠΌ, Ρ‡Ρ‚ΠΎΒ ΡƒΒ Π½ΠΈΡ… свои Β«ΠΏΠΎΠ΄Π²ΠΎΠ΄Π½Ρ‹Π΅ ΠΊΠ°ΠΌΠ½ΠΈΒ». ВСкст рассчитан на программистов, Π·Π½Π°ΠΊΠΎΠΌΡ‹Ρ… с сСтями ΠΈΒ Linux, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΌ интСрСсСн XDP ΠΈΒ eBPF.

В этой части Π΄Π΅Ρ‚Π°Π»ΡŒΠ½ΠΎ разбСрСмся, ΠΊΠ°ΠΊ собираСтся XDP-Ρ„ΠΈΠ»ΡŒΡ‚Ρ€ ΠΈ ΠΊΠ°ΠΊ Π΅Π³ΠΎ Ρ‚Π΅ΡΡ‚ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ, Π·Π°Ρ‚Π΅ΠΌ напишСм простой Π²Π°Ρ€ΠΈΠ°Π½Ρ‚ извСстного ΠΌΠ΅Ρ…Π°Π½ΠΈΠ·ΠΌΠ° SYN cookies Π½Π°Β ΡƒΡ€ΠΎΠ²Π½Π΅ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠΈ ΠΏΠ°ΠΊΠ΅Ρ‚ΠΎΠ². Пока Π½Π΅Β Π±ΡƒΠ΄Π΅ΠΌ Ρ„ΠΎΡ€ΠΌΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ Β«Π±Π΅Π»Ρ‹ΠΉ список»
ΠΏΡ€ΠΎΠ²Π΅Ρ€Π΅Π½Π½Ρ‹Ρ… ΠΊΠ»ΠΈΠ΅Π½Ρ‚ΠΎΠ², вСсти счСтчики ΠΈΒ ΡƒΠΏΡ€Π°Π²Π»ΡΡ‚ΡŒ Ρ„ΠΈΠ»ΡŒΡ‚Ρ€ΠΎΠΌΒ β€” Ρ…Π²Π°Ρ‚ΠΈΡ‚ Π»ΠΎΠ³ΠΎΠ².

ΠŸΠΈΡΠ°Ρ‚ΡŒ Π±ΡƒΠ΄Π΅ΠΌ Π½Π°Β CΒ β€” это Π½Π΅Β ΠΌΠΎΠ΄Π½ΠΎ, Π·Π°Ρ‚ΠΎ ΠΏΡ€Π°ΠΊΡ‚ΠΈΡ‡Π½ΠΎ. Π’Π΅ΡΡŒ ΠΊΠΎΠ΄ доступСн Π½Π°Β GitHub по ссылкС Π²Β ΠΊΠΎΠ½Ρ†Π΅ ΠΈΒ Ρ€Π°Π·Π±ΠΈΡ‚ Π½Π°Β ΠΊΠΎΠΌΠΌΠΈΡ‚Ρ‹ по этапам, описанным Π²Β ΡΡ‚Π°Ρ‚ΡŒΠ΅.

Disclaimer. Π’Β Ρ…ΠΎΠ΄Π΅ ΡΡ‚Π°Ρ‚ΡŒΠΈ Π±ΡƒΠ΄Π΅Ρ‚ Ρ€Π°Π·Ρ€Π°Π±Π°Ρ‚Ρ‹Π²Π°Ρ‚ΡŒΡΡ ΠΌΠΈΠ½ΠΈ-Ρ€Π΅ΡˆΠ΅Π½ΠΈΠ΅ для отраТСния ΠΎΡ‚Β DDoS-Π°Ρ‚Π°ΠΊ, ΠΏΠΎΡ‚ΠΎΠΌΡƒ Ρ‡Ρ‚ΠΎ это рСалистичная Π·Π°Π΄Π°Ρ‡Π° для XDP и моя ΠΎΠ±Π»Π°ΡΡ‚ΡŒ. Однако главная Ρ†Π΅Π»ΡŒΒ β€” Ρ€Π°Π·ΠΎΠ±Ρ€Π°Ρ‚ΡŒΡΡ с тСхнологиСй, это нС руководство по созданию Π³ΠΎΡ‚ΠΎΠ²ΠΎΠΉ Π·Π°Ρ‰ΠΈΡ‚Ρ‹. Π£Ρ‡Π΅Π±Π½Ρ‹ΠΉ ΠΊΠΎΠ΄ Π½Π΅Β ΠΎΠΏΡ‚ΠΈΠΌΠΈΠ·ΠΈΡ€ΠΎΠ²Π°Π½ и опускаСт Π½Π΅ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ Π½ΡŽΠ°Π½ΡΡ‹.

ΠšΡ€Π°Ρ‚ΠΊΠΈΠΉ ΠΎΠ±Π·ΠΎΡ€ XDP

Π˜Π·Π»ΠΎΠΆΡƒ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ ΠΊΠ»ΡŽΡ‡Π΅Π²Ρ‹Π΅ ΠΌΠΎΠΌΠ΅Π½Ρ‚Ρ‹, Ρ‡Ρ‚ΠΎΠ±Ρ‹ Π½Π΅ Π΄ΡƒΠ±Π»ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ Π΄ΠΎΠΊΡƒΠΌΠ΅Π½Ρ‚Π°Ρ†ΠΈΡŽ ΠΈΒ ΡΡƒΡ‰Π΅ΡΡ‚Π²ΡƒΡŽΡ‰ΠΈΠ΅ ΡΡ‚Π°Ρ‚ΡŒΠΈ.

Π˜Ρ‚Π°ΠΊ, Π² ядро загруТаСтся ΠΊΠΎΠ΄ Ρ„ΠΈΠ»ΡŒΡ‚Ρ€Π°. Π€ΠΈΠ»ΡŒΡ‚Ρ€Ρƒ ΠΏΠ΅Ρ€Π΅Π΄Π°ΡŽΡ‚ΡΡ входящиС ΠΏΠ°ΠΊΠ΅Ρ‚Ρ‹. Π’Β ΠΈΡ‚ΠΎΠ³Π΅ Ρ„ΠΈΠ»ΡŒΡ‚Ρ€ Π΄ΠΎΠ»ΠΆΠ΅Π½ ΠΏΡ€ΠΈΠ½ΡΡ‚ΡŒ Ρ€Π΅ΡˆΠ΅Π½ΠΈΠ΅: ΠΏΡ€ΠΎΠΏΡƒΡΡ‚ΠΈΡ‚ΡŒ ΠΏΠ°ΠΊΠ΅Ρ‚ в ядро (XDP_PASS), ΡΠ±Ρ€ΠΎΡΠΈΡ‚ΡŒ ΠΏΠ°ΠΊΠ΅Ρ‚ (XDP_DROP) ΠΈΠ»ΠΈΒ ΠΎΡ‚ΠΏΡ€Π°Π²ΠΈΡ‚ΡŒ Π΅Π³ΠΎΒ ΠΎΠ±Ρ€Π°Ρ‚Π½ΠΎ (XDP_TX). Π€ΠΈΠ»ΡŒΡ‚Ρ€ ΠΌΠΎΠΆΠ΅Ρ‚ ΠΈΠ·ΠΌΠ΅Π½ΠΈΡ‚ΡŒ ΠΏΠ°ΠΊΠ΅Ρ‚, это особСнно Π°ΠΊΡ‚ΡƒΠ°Π»ΡŒΠ½ΠΎ для XDP_TX. Π’Π°ΠΊΠΆΠ΅ ΠΌΠΎΠΆΠ½ΠΎ Π°Π²Π°Ρ€ΠΈΠΉΠ½ΠΎ ΠΏΡ€Π΅Ρ€Π²Π°Ρ‚ΡŒ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΡƒ (XDP_ABORTED) ΠΈΒ ΡΠ±Ρ€ΠΎΡΠΈΡ‚ΡŒ ΠΏΠ°ΠΊΠ΅Ρ‚, но это Π°Π½Π°Π»ΠΎΠ³ assert(0)Β β€” для отладки.

Π’ΠΈΡ€Ρ‚ΡƒΠ°Π»ΡŒΠ½Π°Ρ машина eBPF (extended Berkley Packet Filter) ΡΠΏΠ΅Ρ†ΠΈΠ°Π»ΡŒΠ½ΠΎ сдСлана простой, Π΄Π°Π±Ρ‹ ядро ΠΌΠΎΠ³Π»ΠΎ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΈΡ‚ΡŒ, Ρ‡Ρ‚ΠΎ ΠΊΠΎΠ΄ нС зацикливаСтся ΠΈΒ Π½Π΅Β ΠΏΠΎΠ²Ρ€Π΅ΠΆΠ΄Π°Π΅Ρ‚ Ρ‡ΡƒΠΆΡƒΡŽ ΠΏΠ°ΠΌΡΡ‚ΡŒ. Π‘ΠΎΠ²ΠΎΠΊΡƒΠΏΠ½Ρ‹Π΅ ограничСния ΠΈ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠΈ:

  • Π—Π°ΠΏΡ€Π΅Ρ‰Π΅Π½Ρ‹ Ρ†ΠΈΠΊΠ»Ρ‹ (ΠΏΠ΅Ρ€Π΅Ρ…ΠΎΠ΄Ρ‹ Π½Π°Π·Π°Π΄).
  • Π•ΡΡ‚ΡŒ стСк для Π΄Π°Π½Π½Ρ‹Ρ…, Π½ΠΎ Π½Π΅Ρ‚ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΉ (всС Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ C Π΄ΠΎΠ»ΠΆΠ½Ρ‹ Π²ΡΡ‚Ρ€Π°ΠΈΠ²Π°Ρ‚ΡŒΡΡ).
  • Π—Π°ΠΏΡ€Π΅Ρ‰Π΅Π½Ρ‹ обращСния ΠΊ памяти Π·Π° ΠΏΡ€Π΅Π΄Π΅Π»Π°ΠΌΠΈ стСка ΠΈ Π±ΡƒΡ„Π΅Ρ€Π° ΠΏΠ°ΠΊΠ΅Ρ‚Π°.
  • Π Π°Π·ΠΌΠ΅Ρ€ ΠΊΠΎΠ΄Π° ΠΎΠ³Ρ€Π°Π½ΠΈΡ‡Π΅Π½, Π½ΠΎ Π½Π°Β ΠΏΡ€Π°ΠΊΡ‚ΠΈΠΊΠ΅ это Π½Π΅Β ΠΎΡ‡Π΅Π½ΡŒ сущСствСнно.
  • Π Π°Π·Ρ€Π΅ΡˆΠ΅Π½Ρ‹ Π²Ρ‹Π·ΠΎΠ²Ρ‹ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ ΡΠΏΠ΅Ρ†ΠΈΠ°Π»ΡŒΠ½Ρ‹Ρ… Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΉ ядра (eBPF helpers).

Π Π°Π·Ρ€Π°Π±ΠΎΡ‚ΠΊΠ° ΠΈ установка Ρ„ΠΈΠ»ΡŒΡ‚Ρ€Π° выглядят Ρ‚Π°ΠΊ:

  1. Π˜ΡΡ…ΠΎΠ΄Π½Ρ‹ΠΉ ΠΊΠΎΠ΄ (Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€, kernel.c) компилируСтся Π² ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π½Ρ‹ΠΉ (kernel.o) ΠΏΠΎΠ΄Β Π°Ρ€Ρ…ΠΈΡ‚Π΅ΠΊΡ‚ΡƒΡ€Ρƒ Π²ΠΈΡ€Ρ‚ΡƒΠ°Π»ΡŒΠ½ΠΎΠΉ ΠΌΠ°ΡˆΠΈΠ½Ρ‹ eBPF. ΠΠ°Β ΠΎΠΊΡ‚ΡΠ±Ρ€ΡŒ 2019 компиляция Π²Β eBPF поддСрТиваСтся Clang ΠΈΒ ΠΎΠ±Π΅Ρ‰Π°Π½Π° Π²Β GCCΒ 10.1.
  2. Если в этом ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π½ΠΎΠΌ ΠΊΠΎΠ΄Π΅ Π΅ΡΡ‚ΡŒ обращСния к структурам ядра (Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€, ΠΊ Ρ‚Π°Π±Π»ΠΈΡ†Π°ΠΌ ΠΈ счСтчикам), вмСсто ΠΈΡ… ID стоят Π½ΡƒΠ»ΠΈ, Ρ‚ΠΎΒ Π΅ΡΡ‚ΡŒ Π²Ρ‹ΠΏΠΎΠ»Π½ΠΈΡ‚ΡŒ Ρ‚Π°ΠΊΠΎΠΉ ΠΊΠΎΠ΄ нСльзя. ΠŸΠ΅Ρ€Π΅Π΄ Π·Π°Π³Ρ€ΡƒΠ·ΠΊΠΎΠΉ в ядро Π½ΡƒΠΆΠ½ΠΎ эти Π½ΡƒΠ»ΠΈ Π·Π°ΠΌΠ΅Π½ΠΈΡ‚ΡŒ Π½Π°Β ID ΠΊΠΎΠ½ΠΊΡ€Π΅Ρ‚Π½Ρ‹Ρ… ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ², созданных Ρ‡Π΅Ρ€Π΅Π· Π²Ρ‹Π·ΠΎΠ²Ρ‹ ядра (ΡΠ»ΠΈΠ½ΠΊΠΎΠ²Π°Ρ‚ΡŒ ΠΊΠΎΠ΄). МоТно ΡΠ΄Π΅Π»Π°Ρ‚ΡŒ это внСшними ΡƒΡ‚ΠΈΠ»ΠΈΡ‚Π°ΠΌΠΈ, Π°Β ΠΌΠΎΠΆΠ½ΠΎ Π½Π°ΠΏΠΈΡΠ°Ρ‚ΡŒ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΡƒ, которая Π±ΡƒΠ΄Π΅Ρ‚ Π»ΠΈΠ½ΠΊΠΎΠ²Π°Ρ‚ΡŒ ΠΈΒ Π·Π°Π³Ρ€ΡƒΠΆΠ°Ρ‚ΡŒ ΠΊΠΎΠ½ΠΊΡ€Π΅Ρ‚Π½Ρ‹ΠΉ Ρ„ΠΈΠ»ΡŒΡ‚Ρ€.
  3. Π―Π΄Ρ€ΠΎ Π²Π΅Ρ€ΠΈΡ„ΠΈΡ†ΠΈΡ€ΡƒΠ΅Ρ‚ Π·Π°Π³Ρ€ΡƒΠΆΠ°Π΅ΠΌΡƒΡŽ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΡƒ. ΠŸΡ€ΠΎΠ²Π΅Ρ€ΡΠ΅Ρ‚ΡΡ отсутствиС Ρ†ΠΈΠΊΠ»ΠΎΠ² ΠΈΒ Π½Π΅Π²Ρ‹Ρ…ΠΎΠ΄ Π·Π°Β Π³Ρ€Π°Π½ΠΈΡ†Ρ‹ ΠΏΠ°ΠΊΠ΅Ρ‚Π° и стСка. Если Π²Π΅Ρ€ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ Π½Π΅Β ΠΌΠΎΠΆΠ΅Ρ‚ Π΄ΠΎΠΊΠ°Π·Π°Ρ‚ΡŒ, Ρ‡Ρ‚ΠΎΒ ΠΊΠΎΠ΄ ΠΊΠΎΡ€Ρ€Π΅ΠΊΡ‚Π΅Π½, ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΠ° отвСргаСтся,Β β€” Π½Π°Π΄ΠΎ ΡƒΠΌΠ΅Ρ‚ΡŒ ΡƒΠ±Π»Π°ΠΆΠ°Ρ‚ΡŒ Π΅Π³ΠΎ.
  4. ПослС ΡƒΡΠΏΠ΅ΡˆΠ½ΠΎΠΉ Π²Π΅Ρ€ΠΈΡ„ΠΈΠΊΠ°Ρ†ΠΈΠΈ ядро ΠΊΠΎΠΌΠΏΠΈΠ»ΠΈΡ€ΡƒΠ΅Ρ‚ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π½Ρ‹ΠΉ ΠΊΠΎΠ΄ Π°Ρ€Ρ…ΠΈΡ‚Π΅ΠΊΡ‚ΡƒΡ€Ρ‹ eBPF Π²Β ΠΌΠ°ΡˆΠΈΠ½Π½Ρ‹ΠΉ ΠΊΠΎΠ΄ систСмной Π°Ρ€Ρ…ΠΈΡ‚Π΅ΠΊΡ‚ΡƒΡ€Ρ‹ (just-in-time).
  5. ΠŸΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΠ° прикрСпляСтся к интСрфСйсу ΠΈΒ Π½Π°Ρ‡ΠΈΠ½Π°Π΅Ρ‚ ΠΎΠ±Ρ€Π°Π±Π°Ρ‚Ρ‹Π²Π°Ρ‚ΡŒ ΠΏΠ°ΠΊΠ΅Ρ‚Ρ‹.

ΠŸΠΎΡΠΊΠΎΠ»ΡŒΠΊΡƒ XDP Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚ в ядрС, ΠΎΡ‚Π»Π°Π΄ΠΊΠ° вСдСтся ΠΏΠΎΒ Π»ΠΎΠ³Π°ΠΌ трассировки ΠΈ, собствСнно, ΠΏΠΎΒ ΠΏΠ°ΠΊΠ΅Ρ‚Π°ΠΌ, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΠ° Ρ„ΠΈΠ»ΡŒΡ‚Ρ€ΡƒΠ΅Ρ‚ ΠΈΠ»ΠΈΒ Π³Π΅Π½Π΅Ρ€ΠΈΡ€ΡƒΠ΅Ρ‚. Π’Π΅ΠΌ Π½Π΅Β ΠΌΠ΅Π½Π΅Π΅, eBPF обСспСчиваСт Π±Π΅Π·ΠΎΠΏΠ°ΡΠ½ΠΎΡΡ‚ΡŒ Π·Π°Π³Ρ€ΡƒΠΆΠ΅Π½Π½ΠΎΠ³ΠΎ ΠΊΠΎΠ΄Π° для систСмы, поэтому ΡΠΊΡΠΏΠ΅Ρ€ΠΈΠΌΠ΅Π½Ρ‚ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ с XDP ΠΌΠΎΠΆΠ½ΠΎ прямо на локальном Linux.

ΠŸΠΎΠ΄Π³ΠΎΡ‚ΠΎΠ²ΠΊΠ° окруТСния

Π‘Π±ΠΎΡ€ΠΊΠ°

Clang Π½Π΅ ΠΌΠΎΠΆΠ΅Ρ‚ Π½Π°ΠΏΡ€ΡΠΌΡƒΡŽ Π²Ρ‹Π΄Π°Π²Π°Ρ‚ΡŒ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π½Ρ‹ΠΉ ΠΊΠΎΠ΄ для архитСктуры eBPF, поэтому процСсс состоит ΠΈΠ·Β Π΄Π²ΡƒΡ… шагов:

  1. Π‘ΠΊΠΎΠΌΠΏΠΈΠ»ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ ΠΊΠΎΠ΄ Π½Π°Β C Π²Β Π±Π°ΠΉΡ‚-ΠΊΠΎΠ΄ LLVM (clang -emit-llvm).
  2. ΠŸΡ€Π΅ΠΎΠ±Ρ€Π°Π·ΠΎΠ²Π°Ρ‚ΡŒ Π±Π°ΠΉΡ‚-ΠΊΠΎΠ΄ Π²Β ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π½Ρ‹ΠΉ ΠΊΠΎΠ΄ eBPF (llc -march=bpf -filetype=obj).

ΠŸΡ€ΠΈΒ Π½Π°ΠΏΠΈΡΠ°Π½ΠΈΠΈ Ρ„ΠΈΠ»ΡŒΡ‚Ρ€Π° пригодится ΠΏΠ°Ρ€Π° Ρ„Π°ΠΉΠ»ΠΎΠ² ΡΠΎΒ Π²ΡΠΏΠΎΠΌΠΎΠ³Π°Ρ‚Π΅Π»ΡŒΠ½Ρ‹ΠΌΠΈ функциями и макросами из тСстов ядра. Π’Π°ΠΆΠ½ΠΎ, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΠΎΠ½ΠΈ соотвСтствовали вСрсии ядра (KVER). ΠšΠ°Ρ‡Π°Π΅ΠΌ ΠΈΡ… Π²Β helpers/:

export KVER=v5.3.7
export BASE=https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/plain/tools/testing/selftests/bpf
wget -P helpers --content-disposition "${BASE}/bpf_helpers.h?h=${KVER}" "${BASE}/bpf_endian.h?h=${KVER}"
unset KVER BASE

Makefile для Arch Linux (ядро 5.3.7):

CLANG ?= clang
LLC ?= llc

KDIR ?= /lib/modules/$(shell uname -r)/build
ARCH ?= $(subst x86_64,x86,$(shell uname -m))

CFLAGS = 
    -Ihelpers 
    
    -I$(KDIR)/include 
    -I$(KDIR)/include/uapi 
    -I$(KDIR)/include/generated/uapi 
    -I$(KDIR)/arch/$(ARCH)/include 
    -I$(KDIR)/arch/$(ARCH)/include/generated 
    -I$(KDIR)/arch/$(ARCH)/include/uapi 
    -I$(KDIR)/arch/$(ARCH)/include/generated/uapi 
    -D__KERNEL__ 
    
    -fno-stack-protector -O2 -g

xdp_%.o: xdp_%.c Makefile
    $(CLANG) -c -emit-llvm $(CFLAGS) $< -o - | 
    $(LLC) -march=bpf -filetype=obj -o $@

.PHONY: all clean

all: xdp_filter.o

clean:
    rm -f ./*.o

KDIR содСрТит ΠΏΡƒΡ‚ΡŒ ΠΊΒ Π·Π°Π³ΠΎΠ»ΠΎΠ²ΠΊΠ°ΠΌ ядра, ARCHΒ β€” Π°Ρ€Ρ…ΠΈΡ‚Π΅ΠΊΡ‚ΡƒΡ€Ρƒ систСмы. ΠŸΡƒΡ‚ΠΈ и инструмСнты ΠΌΠΎΠ³ΡƒΡ‚ Π½Π΅ΠΌΠ½ΠΎΠ³ΠΎ ΠΎΡ‚Π»ΠΈΡ‡Π°Ρ‚ΡŒΡΡ ΠΌΠ΅ΠΆΠ΄Ρƒ дистрибутивами.

ΠŸΡ€ΠΈΠΌΠ΅Ρ€ ΠΎΡ‚Π»ΠΈΡ‡ΠΈΠΉ для Debian 10 (ядро 4.19.67)

# другая ΠΊΠΎΠΌΠ°Π½Π΄Π°
CLANG ?= clang
LLC ?= llc-7

# Π΄Ρ€ΡƒΠ³ΠΎΠΉ ΠΊΠ°Ρ‚Π°Π»ΠΎΠ³
KDIR ?= /usr/src/linux-headers-$(shell uname -r)
ARCH ?= $(subst x86_64,x86,$(shell uname -m))

# Π΄Π²Π° Π΄ΠΎΠΏΠΎΠ»Π½ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹Ρ… ΠΊΠ°Ρ‚Π°Π»ΠΎΠ³Π° -I
CFLAGS = 
    -Ihelpers 
    
    -I/usr/src/linux-headers-4.19.0-6-common/include 
    -I/usr/src/linux-headers-4.19.0-6-common/arch/$(ARCH)/include 
    # Π΄Π°Π»Π΅Π΅ Π±Π΅Π· ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠΉ

CFLAGS ΠΏΠΎΠ΄ΠΊΠ»ΡŽΡ‡Π°ΡŽΡ‚ Π΄ΠΈΡ€Π΅ΠΊΡ‚ΠΎΡ€ΠΈΡŽ ΡΠΎΒ Π²ΡΠΏΠΎΠΌΠΎΠ³Π°Ρ‚Π΅Π»ΡŒΠ½Ρ‹ΠΌΠΈ Π·Π°Π³ΠΎΠ»ΠΎΠ²ΠΊΠ°ΠΌΠΈ и нСсколько Π΄ΠΈΡ€Π΅ΠΊΡ‚ΠΎΡ€ΠΈΠΉ с заголовками ядра. Π‘ΠΈΠΌΠ²ΠΎΠ» __KERNEL__ ΠΎΠ·Π½Π°Ρ‡Π°Π΅Ρ‚, Ρ‡Ρ‚ΠΎΒ Π·Π°Π³ΠΎΠ»ΠΎΠ²ΠΊΠΈ UAPI (userspace API) ΠΎΠΏΡ€Π΅Π΄Π΅Π»ΡΡŽΡ‚ΡΡ для кода ядра, Ρ‚Π°ΠΊ ΠΊΠ°ΠΊ Ρ„ΠΈΠ»ΡŒΡ‚Ρ€ выполняСтся в ядрС.

Π—Π°Ρ‰ΠΈΡ‚Ρƒ стСка ΠΌΠΎΠΆΠ½ΠΎ ΠΎΡ‚ΠΊΠ»ΡŽΡ‡ΠΈΡ‚ΡŒ (-fno-stack-protector), ΠΏΠΎΡ‚ΠΎΠΌΡƒ Ρ‡Ρ‚ΠΎ Π²Π΅Ρ€ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ ΠΊΠΎΠ΄Π° eBPF всС Ρ€Π°Π²Π½ΠΎ провСряСт Π½Π΅Π²Ρ‹Ρ…ΠΎΠ΄ Π·Π° Π³Ρ€Π°Π½ΠΈΡ†Ρ‹ стСка. Π‘Ρ€Π°Π·Ρƒ стоит Π²ΠΊΠ»ΡŽΡ‡ΠΈΡ‚ΡŒ ΠΎΠΏΡ‚ΠΈΠΌΠΈΠ·Π°Ρ†ΠΈΠΈ, ΠΏΠΎΡ‚ΠΎΠΌΡƒ Ρ‡Ρ‚ΠΎ Ρ€Π°Π·ΠΌΠ΅Ρ€ Π±Π°ΠΉΡ‚-ΠΊΠΎΠ΄Π° eBPF ΠΎΠ³Ρ€Π°Π½ΠΈΡ‡Π΅Π½.

НачнСм с Ρ„ΠΈΠ»ΡŒΡ‚Ρ€Π°, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ пропускаСт всС ΠΏΠ°ΠΊΠ΅Ρ‚Ρ‹ ΠΈ Π½ΠΈΡ‡Π΅Π³ΠΎ Π½Π΅Β Π΄Π΅Π»Π°Π΅Ρ‚:

#include <uapi/linux/bpf.h>

#include <bpf_helpers.h>

SEC("prog")
int xdp_main(struct xdp_md* ctx) {
    return XDP_PASS;
}

char _license[] SEC("license") = "GPL";

Команда make собираСт xdp_filter.o. Π“Π΄Π΅ Π΅Π³ΠΎ Ρ‚Π΅ΠΏΠ΅Ρ€ΡŒ ΠΈΡΠΏΡ‹Ρ‚Π°Ρ‚ΡŒ?

ВСстовый стСнд

Π‘Ρ‚Π΅Π½Π΄ Π΄ΠΎΠ»ΠΆΠ΅Π½ Π²ΠΊΠ»ΡŽΡ‡Π°Ρ‚ΡŒ Π΄Π²Π° интСрфСйса: Π½Π°Β ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΌ Π±ΡƒΠ΄Π΅Ρ‚ Ρ„ΠΈΠ»ΡŒΡ‚Ρ€ и с которого Π±ΡƒΠ΄ΡƒΡ‚ ΠΎΡ‚ΠΏΡ€Π°Π²Π»ΡΡ‚ΡŒΡΡ ΠΏΠ°ΠΊΠ΅Ρ‚Ρ‹. Π­Ρ‚ΠΎ Π΄ΠΎΠ»ΠΆΠ½Ρ‹ Π±Ρ‹Ρ‚ΡŒ ΠΏΠΎΠ»Π½ΠΎΡ†Π΅Π½Π½Ρ‹Π΅ устройства Linux со своими IP, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΡΡ‚ΡŒ, ΠΊΠ°ΠΊΒ ΠΎΠ±Ρ‹Ρ‡Π½Ρ‹Π΅ прилоТСния Ρ€Π°Π±ΠΎΡ‚Π°ΡŽΡ‚ с нашим Ρ„ΠΈΠ»ΡŒΡ‚Ρ€ΠΎΠΌ.

Устройства Ρ‚ΠΈΠΏΠ° veth (virtual Ethernet) Π½Π°ΠΌ подходят: это ΠΏΠ°Ρ€Π° Π²ΠΈΡ€Ρ‚ΡƒΠ°Π»ΡŒΠ½Ρ‹Ρ… сСтСвых интСрфСйсов, «соСдинСнных» ΠΌΠ΅ΠΆΠ΄Ρƒ собой Π½Π°ΠΏΡ€ΡΠΌΡƒΡŽ. Π‘ΠΎΠ·Π΄Π°Ρ‚ΡŒ ΠΈΡ… ΠΌΠΎΠΆΠ½ΠΎ Ρ‚Π°ΠΊ (в этом Ρ€Π°Π·Π΄Π΅Π»Π΅ всС ΠΊΠΎΠΌΠ°Π½Π΄Ρ‹ ip Π²Ρ‹ΠΏΠΎΠ»Π½ΡΡŽΡ‚ΡΡ ΠΎΡ‚Β root):

ip link add xdp-remote type veth peer name xdp-local

Π—Π΄Π΅ΡΡŒ xdp-remote ΠΈΒ xdp-localΒ β€” ΠΈΠΌΠ΅Π½Π° устройств. На xdp-local (192.0.2.1/24) Π±ΡƒΠ΄Π΅Ρ‚ присоСдинСн Ρ„ΠΈΠ»ΡŒΡ‚Ρ€, с xdp-remote (192.0.2.2/24) Π±ΡƒΠ΄Π΅Ρ‚ ΠΎΡ‚ΠΏΡ€Π°Π²Π»ΡΡ‚ΡŒΡΡ входящий Ρ‚Ρ€Π°Ρ„ΠΈΠΊ. Однако Π΅ΡΡ‚ΡŒ ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΠ°: интСрфСйсы находятся Π½Π°Β ΠΎΠ΄Π½ΠΎΠΉ машинС, ΠΈΒ Linux Π½Π΅Β Π±ΡƒΠ΄Π΅Ρ‚ ΡΠ»Π°Ρ‚ΡŒ Ρ‚Ρ€Π°Ρ„ΠΈΠΊ Π½Π°Β ΠΎΠ΄ΠΈΠ½ ΠΈΠ·Β Π½ΠΈΡ… Ρ‡Π΅Ρ€Π΅Π· Π΄Ρ€ΡƒΠ³ΠΎΠΉ. МоТно Ρ€Π΅ΡˆΠ°Ρ‚ΡŒ это Ρ…ΠΈΡ‚Ρ€Ρ‹ΠΌΠΈ ΠΏΡ€Π°Π²ΠΈΠ»Π°ΠΌΠΈ iptables, Π½ΠΎΒ ΠΈΠΌ придСтся ΠΌΠ΅Π½ΡΡ‚ΡŒ ΠΏΠ°ΠΊΠ΅Ρ‚Ρ‹, Ρ‡Ρ‚ΠΎΒ Π½Π΅ΡƒΠ΄ΠΎΠ±Π½ΠΎ ΠΏΡ€ΠΈΒ ΠΎΡ‚Π»Π°Π΄ΠΊΠ΅. Π›ΡƒΡ‡ΡˆΠ΅ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ сСтСвыС пространства ΠΈΠΌΠ΅Π½ (network namespaces, Π΄Π°Π»Π΅Π΅ netns).

Π‘Π΅Ρ‚Π΅Π²ΠΎΠ΅ пространство ΠΈΠΌΠ΅Π½ содСрТит Π½Π°Π±ΠΎΡ€ интСрфСйсов, Ρ‚Π°Π±Π»ΠΈΡ† ΠΌΠ°Ρ€ΡˆΡ€ΡƒΡ‚ΠΈΠ·Π°Ρ†ΠΈΠΈ ΠΈΒ ΠΏΡ€Π°Π²ΠΈΠ» NetFilter, ΠΈΠ·ΠΎΠ»ΠΈΡ€ΠΎΠ²Π°Π½Π½Ρ‹Π΅ ΠΎΡ‚Β Π°Π½Π°Π»ΠΎΠ³ΠΈΡ‡Π½Ρ‹Ρ… ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ² Π²Β Π΄Ρ€ΡƒΠ³ΠΈΡ… netns. ΠšΠ°ΠΆΠ΄Ρ‹ΠΉ процСсс Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚ Π²Β ΠΊΠ°ΠΊΠΎΠΌ-Ρ‚ΠΎ пространствС ΠΈΠΌΠ΅Π½, ΠΈΒ Π΅ΠΌΡƒ доступны Ρ‚ΠΎΠ»ΡŒΠΊΠΎ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Ρ‹ этого netns. ΠŸΠΎΒ ΡƒΠΌΠΎΠ»Ρ‡Π°Π½ΠΈΡŽ в систСмС СдинствСнноС сСтСвоС пространство ΠΈΠΌΠ΅Π½ для всСх ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ², поэтому ΠΌΠΎΠΆΠ½ΠΎ Ρ€Π°Π±ΠΎΡ‚Π°Ρ‚ΡŒ Π²Β Linux ΠΈΒ Π½Π΅Β Π·Π½Π°Ρ‚ΡŒ ΠΏΡ€ΠΎΒ netns.

Π‘ΠΎΠ·Π΄Π°Π΄ΠΈΠΌ Π½ΠΎΠ²ΠΎΠ΅ пространство ΠΈΠΌΠ΅Π½ xdp-test и пСрСмСстим Ρ‚ΡƒΠ΄Π° xdp-remote.

ip netns add xdp-test
ip link set dev xdp-remote netns xdp-test

Π’ΠΎΠ³Π΄Π° процСсс, Π²Ρ‹ΠΏΠΎΠ»Π½ΡΡŽΡ‰ΠΈΠΉΡΡ Π²Β xdp-test, Π½Π΅Β Π±ΡƒΠ΄Π΅Ρ‚ Β«Π²ΠΈΠ΄Π΅Ρ‚ΡŒΒ» xdp-local (ΠΎΠ½ останСтся Π²Β netns ΠΏΠΎΒ ΡƒΠΌΠΎΠ»Ρ‡Π°Π½ΠΈΡŽ) ΠΈΒ ΠΏΡ€ΠΈΒ ΠΎΡ‚ΠΏΡ€Π°Π²ΠΊΠ΅ ΠΏΠ°ΠΊΠ΅Ρ‚Π° Π½Π°Β 192.0.2.1 пСрСдаст Π΅Π³ΠΎ Ρ‡Π΅Ρ€Π΅Π·Β xdp-remote, ΠΏΠΎΡ‚ΠΎΠΌΡƒ Ρ‡Ρ‚ΠΎ это СдинствСнный интСрфСйс Π²Β 192.0.2.0/24, доступный этому процСссу. Π­Ρ‚ΠΎ дСйствуСт ΠΈΒ Π²Β ΠΎΠ±Ρ€Π°Ρ‚Π½ΡƒΡŽ строну.

ΠŸΡ€ΠΈΒ ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Ρ‰Π΅Π½ΠΈΠΈ ΠΌΠ΅ΠΆΠ΄Ρƒ netns интСрфСйс опускаСтся и тСряСт адрСс. Π§Ρ‚ΠΎΠ±Ρ‹ Π½Π°ΡΡ‚Ρ€ΠΎΠΈΡ‚ΡŒ интСрфСйс Π²Β netns, Π½ΡƒΠΆΠ½ΠΎ Π·Π°ΠΏΡƒΡΡ‚ΠΈΡ‚ΡŒ ip ... в этом пространствС ΠΈΠΌΠ΅Π½ ΠΊΠΎΠΌΠ°Π½Π΄Π½ΠΎΠΉ ip netns exec:

ip netns exec xdp-test 
    ip address add 192.0.2.2/24 dev xdp-remote
ip netns exec xdp-test 
    ip link set xdp-remote up

Как ΠΌΠΎΠΆΠ½ΠΎ Π²ΠΈΠ΄Π΅Ρ‚ΡŒ, это нС отличаСтся от настройки xdp-local в пространствС ΠΈΠΌΠ΅Π½ ΠΏΠΎΒ ΡƒΠΌΠΎΠ»Ρ‡Π°Π½ΠΈΡŽ:

    ip address add 192.0.2.1/24 dev xdp-local
    ip link set xdp-local up

Если Π·Π°ΠΏΡƒΡΡ‚ΠΈΡ‚ΡŒ tcpdump -tnevi xdp-local, ΠΌΠΎΠΆΠ½ΠΎ ΡƒΠ²ΠΈΠ΄Π΅Ρ‚ΡŒ, Ρ‡Ρ‚ΠΎΒ ΠΏΠ°ΠΊΠ΅Ρ‚Ρ‹, ΠΎΡ‚ΠΏΡ€Π°Π²Π»Π΅Π½Π½Ρ‹Π΅ ΠΈΠ·Β xdp-test, Π΄ΠΎΡΡ‚Π°Π²Π»ΡΡŽΡ‚ΡΡ на этот интСрфСйс:

ip netns exec xdp-test   ping 192.0.2.1

Π£Π΄ΠΎΠ±Π½ΠΎ Π·Π°ΠΏΡƒΡΡ‚ΠΈΡ‚ΡŒ шСлл Π²Β xdp-test. Π’Β Ρ€Π΅ΠΏΠΎΠ·ΠΈΡ‚Π°Ρ€ΠΈΠΈ Π΅ΡΡ‚ΡŒ скрипт, Π°Π²Ρ‚ΠΎΠΌΠ°Ρ‚ΠΈΠ·ΠΈΡ€ΡƒΡŽΡ‰ΠΈΠΉ Ρ€Π°Π±ΠΎΡ‚Ρƒ со стСндом, Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€, ΠΌΠΎΠΆΠ½ΠΎ Π½Π°ΡΡ‚Ρ€ΠΎΠΈΡ‚ΡŒ стСнд ΠΊΠΎΠΌΠ°Π½Π΄ΠΎΠΉ sudo ./stand up ΠΈΒ ΡƒΠ΄Π°Π»ΠΈΡ‚ΡŒ Π΅Π³ΠΎ sudo ./stand down.

Врассировка

Π€ΠΈΠ»ΡŒΡ‚Ρ€ привязываСтся к устройству Ρ‚Π°ΠΊ:

ip -force link set dev xdp-local xdp object xdp_filter.o verbose

ΠšΠ»ΡŽΡ‡ -force Π½ΡƒΠΆΠ΅Π½, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΠΏΡ€ΠΈΠ²ΡΠ·Π°Ρ‚ΡŒ Π½ΠΎΠ²ΡƒΡŽ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΡƒ, Ссли другая ΡƒΠΆΠ΅ привязана. Β«No news is good newsΒ» нС про эту ΠΊΠΎΠΌΠ°Π½Π΄Ρƒ, Π²Ρ‹Π²ΠΎΠ΄ в любом случаС ΠΎΠ±ΡŠΠ΅ΠΌΠ½Ρ‹ΠΉ. Π£ΠΊΠ°Π·Ρ‹Π²Π°Ρ‚ΡŒ verbose Π½Π΅ΠΎΠ±ΡΠ·Π°Ρ‚Π΅Π»ΡŒΠ½ΠΎ, но с ним появляСтся ΠΎΡ‚Ρ‡Π΅Ρ‚ ΠΎΒ Ρ€Π°Π±ΠΎΡ‚Π΅ Π²Π΅Ρ€ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€Π° ΠΊΠΎΠ΄Π° с листингом ассСмблСра:

Verifier analysis:

0: (b7) r0 = 2
1: (95) exit

ΠžΡ‚Π²ΡΠ·Π°Ρ‚ΡŒ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΡƒ от интСрфСйса:

ip link set dev xdp-local xdp off

В скриптС это ΠΊΠΎΠΌΠ°Π½Π΄Ρ‹ sudo ./stand attach ΠΈΒ sudo ./stand detach.

ΠŸΡ€ΠΈΠ²ΡΠ·Π°Π² Ρ„ΠΈΠ»ΡŒΡ‚Ρ€, ΠΌΠΎΠΆΠ½ΠΎ ΡƒΠ±Π΅Π΄ΠΈΡ‚ΡŒΡΡ, Ρ‡Ρ‚ΠΎΒ ping ΠΏΡ€ΠΎΠ΄ΠΎΠ»ΠΆΠ°Π΅Ρ‚ Ρ€Π°Π±ΠΎΡ‚Π°Ρ‚ΡŒ, Π½ΠΎΒ Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚Β Π»ΠΈ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΠ°? Π”ΠΎΠ±Π°Π²ΠΈΠΌ Π»ΠΎΠ³ΠΈ. Ѐункция bpf_trace_printk() ΠΏΠΎΡ…ΠΎΠΆΠ° Π½Π°Β printf(), Π½ΠΎΒ ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΈΠ²Π°Π΅Ρ‚ всСго Π΄ΠΎΒ Ρ‚Ρ€Π΅Ρ… Π°Ρ€Π³ΡƒΠΌΠ΅Π½Ρ‚ΠΎΠ², ΠΊΡ€ΠΎΠΌΠ΅ шаблона, ΠΈΒ ΠΎΠ³Ρ€Π°Π½ΠΈΡ‡Π΅Π½Π½Ρ‹ΠΉ список спСцификаторов. ΠœΠ°ΠΊΡ€ΠΎΡ bpf_printk() ΡƒΠΏΡ€ΠΎΡ‰Π°Π΅Ρ‚ Π²Ρ‹Π·ΠΎΠ².

   SEC("prog")
   int xdp_main(struct xdp_md* ctx) {
+      bpf_printk("got packet: %pn", ctx);
       return XDP_PASS;
   }

Π’Ρ‹Π²ΠΎΠ΄ ΠΈΠ΄Π΅Ρ‚ Π²Β ΠΊΠ°Π½Π°Π» трассировки ядра, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ Π½ΡƒΠΆΠ½ΠΎ Π²ΠΊΠ»ΡŽΡ‡ΠΈΡ‚ΡŒ:

echo -n 1 | sudo tee /sys/kernel/debug/tracing/options/trace_printk

ΠŸΡ€ΠΎΡΠΌΠΎΡ‚Ρ€ ΠΏΠΎΡ‚ΠΎΠΊΠ° сообщСний:

cat /sys/kernel/debug/tracing/trace_pipe

ОбС этих ΠΊΠΎΠΌΠ°Π½Π΄Ρ‹ Π΄Π΅Π»Π°Π΅Ρ‚ Π²Ρ‹Π·ΠΎΠ² sudo ./stand log.

Ping Ρ‚Π΅ΠΏΠ΅Ρ€ΡŒ Π΄ΠΎΠ»ΠΆΠ΅Π½ Π²Ρ‹Π·Ρ‹Π²Π°Ρ‚ΡŒ Π²Β Π½Π΅ΠΌ Ρ‚Π°ΠΊΠΈΠ΅ сообщСния:

<...>-110930 [004] ..s1 78803.244967: 0: got packet: 00000000ac510377

Если ΠΏΡ€ΠΈΡΠΌΠΎΡ‚Ρ€Π΅Ρ‚ΡŒΡΡ ΠΊΒ Π²Ρ‹Π²ΠΎΠ΄Ρƒ Π²Π΅Ρ€ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€Π°, ΠΌΠΎΠΆΠ½ΠΎ Π·Π°ΠΌΠ΅Ρ‚ΠΈΡ‚ΡŒ странныС вычислСния:

0: (bf) r3 = r1
1: (18) r1 = 0xa7025203a7465
3: (7b) *(u64 *)(r10 -8) = r1
4: (18) r1 = 0x6b63617020746f67
6: (7b) *(u64 *)(r10 -16) = r1
7: (bf) r1 = r10
8: (07) r1 += -16
9: (b7) r2 = 16
10: (85) call bpf_trace_printk#6
<...>

Π”Π΅Π»ΠΎ Π²Β Ρ‚ΠΎΠΌ, Ρ‡Ρ‚ΠΎΒ ΡƒΒ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌ Π½Π°Β eBPF Π½Π΅Ρ‚ сСкции Π΄Π°Π½Π½Ρ‹Ρ…, поэтому СдинствСнный способ Π·Π°ΠΊΠΎΠ΄ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Π½ΡƒΡŽ строку — immediate-Π°Ρ€Π³ΡƒΠΌΠ΅Π½Ρ‚Ρ‹ ΠΊΠΎΠΌΠ°Π½Π΄ Π’Πœ:

$ python -c "import binascii; print(bytes(reversed(binascii.unhexlify('0a7025203a74656b63617020746f67'))))"
b'got packet: %pn'

ΠŸΠΎΒ ΡΡ‚ΠΎΠΉ ΠΏΡ€ΠΈΡ‡ΠΈΠ½Π΅ ΠΎΡ‚Π»Π°Π΄ΠΎΡ‡Π½Ρ‹ΠΉ Π²Ρ‹Π²ΠΎΠ΄ сильно Ρ€Π°Π·Π΄ΡƒΠ²Π°Π΅Ρ‚ ΠΈΡ‚ΠΎΠ³ΠΎΠ²Ρ‹ΠΉ ΠΊΠΎΠ΄.

ΠžΡ‚ΠΏΡ€Π°Π²ΠΊΠ° ΠΏΠ°ΠΊΠ΅Ρ‚ΠΎΠ² XDP

ИзмСним Ρ„ΠΈΠ»ΡŒΡ‚Ρ€: ΠΏΡƒΡΡ‚ΡŒ ΠΎΠ½ всС входящиС ΠΏΠ°ΠΊΠ΅Ρ‚Ρ‹ отправляСт ΠΎΠ±Ρ€Π°Ρ‚Π½ΠΎ. Π­Ρ‚ΠΎ Π½Π΅ΠΊΠΎΡ€Ρ€Π΅ΠΊΡ‚Π½ΠΎ с сСтСвой Ρ‚ΠΎΡ‡ΠΊΠΈ зрСния, Ρ‚Π°ΠΊ ΠΊΠ°ΠΊ Π½ΡƒΠΆΠ½ΠΎ Π±Ρ‹Π»ΠΎΒ Π±Ρ‹ ΠΌΠ΅Π½ΡΡ‚ΡŒ адрСса Π²Β Π·Π°Π³ΠΎΠ»ΠΎΠ²ΠΊΠ°Ρ…, но сСйчас Π²Π°ΠΆΠ½Π° Ρ€Π°Π±ΠΎΡ‚Π° Π²Β ΠΏΡ€ΠΈΠ½Ρ†ΠΈΠΏΠ΅.

       bpf_printk("got packet: %pn", ctx);
-      return XDP_PASS;
+      return XDP_TX;
   }

ЗапускаСм tcpdump Π½Π°Β xdp-remote. Он Π΄ΠΎΠ»ΠΆΠ΅Π½ ΠΏΠΎΠΊΠ°Π·Π°Ρ‚ΡŒ ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ‡Π½Ρ‹Π΅ исходящиС и входящиС ICMP Echo Request ΠΈΒ ΠΏΠ΅Ρ€Π΅ΡΡ‚Π°Ρ‚ΡŒ ΠΏΠΎΠΊΠ°Π·Ρ‹Π²Π°Ρ‚ΡŒ ICMP Echo Reply. Но нС показываСт. ΠžΠΊΠ°Π·Ρ‹Π²Π°Π΅Ρ‚ΡΡ, для Ρ€Π°Π±ΠΎΡ‚Ρ‹ XDP_TX Π² ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΠ΅ Π½Π° xdp-local Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎ, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΠΏΠ°Ρ€Π½ΠΎΠΌΡƒ интСрфСйсу xdp-remote Ρ‚ΠΎΠΆΠ΅ Π±Ρ‹Π»Π° Π½Π°Π·Π½Π°Ρ‡Π΅Π½Π° ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΠ°, хотя Π±Ρ‹ пустая, ΠΈΒ ΠΎΠ½ Π±Ρ‹Π» поднят.

Как я это ΡƒΠ·Π½Π°Π»?

ΠŸΡ€ΠΎΡΠ»Π΅Π΄ΠΈΡ‚ΡŒ ΠΏΡƒΡ‚ΡŒ ΠΏΠ°ΠΊΠ΅Ρ‚Π° в ядрС позволяСт ΠΌΠ΅Ρ…Π°Π½ΠΈΠ·ΠΌ perf events, кстати, ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡŽΡ‰ΠΈΠΉ Ρ‚Ρƒ ΠΆΠ΅ Π²ΠΈΡ€Ρ‚ΡƒΠ°Π»ΡŒΠ½ΡƒΡŽ ΠΌΠ°ΡˆΠΈΠ½Ρƒ, Ρ‚ΠΎΒ Π΅ΡΡ‚ΡŒ для разборок с eBPF примСняСтся eBPF.

Π’Ρ‹ Π΄ΠΎΠ»ΠΆΠ΅Π½ ΡΠ΄Π΅Π»Π°Ρ‚ΡŒ Π΄ΠΎΠ±Ρ€ΠΎ ΠΈΠ· Π·Π»Π°, ΠΏΠΎΡ‚ΠΎΠΌΡƒ Ρ‡Ρ‚ΠΎ Π΅Π³ΠΎ большС Π½Π΅ ΠΈΠ· Ρ‡Π΅Π³ΠΎ ΡΠ΄Π΅Π»Π°Ρ‚ΡŒ.

$ sudo perf trace --call-graph dwarf -e 'xdp:*'
   0.000 ping/123455 xdp:xdp_bulk_tx:ifindex=19 action=TX sent=0 drops=1 err=-6
                                     veth_xdp_flush_bq ([veth])
                                     veth_xdp_flush_bq ([veth])
                                     veth_poll ([veth])
                                     <...>

Π§Ρ‚ΠΎ Ρ‚Π°ΠΊΠΎΠ΅ ΠΊΠΎΠ΄ 6?

$ errno 6
ENXIO 6 No such device or address

Ѐункция veth_xdp_flush_bq() ΠΏΠΎΠ»ΡƒΡ‡Π°Π΅Ρ‚ ΠΊΠΎΠ΄ ошибки ΠΎΡ‚Β veth_xdp_xmit(), гдС поиском ΠΏΠΎΒ ENXIO ΠΈΒ Π½Π°Ρ…ΠΎΠ΄ΠΈΠΌ ΠΊΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΉ.

Восстановим ΠΌΠΈΠ½ΠΈΠΌΠ°Π»ΡŒΠ½Ρ‹ΠΉ Ρ„ΠΈΠ»ΡŒΡ‚Ρ€ (XDP_PASS) Π²Β Ρ„Π°ΠΉΠ»Π΅ xdp_dummy.c, Π΄ΠΎΠ±Π°Π²ΠΈΠΌ Π΅Π³ΠΎ Π²Β Makefile, привяТСм ΠΊΒ xdp-remote:

ip netns exec remote 
    ip link set dev int xdp object dummy.o

Π’Π΅ΠΏΠ΅Ρ€ΡŒ tcpdump ΠΏΠΎΠΊΠ°Π·Ρ‹Π²Π°Π΅Ρ‚ Ρ‚ΠΎ, что оТидаСтся:

62:57:8e:70:44:64 > 26:0e:25:37:8f:96, ethertype IPv4 (0x0800), length 98: (tos 0x0, ttl 64, id 13762, offset 0, flags [DF], proto ICMP (1), length 84)
    192.0.2.2 > 192.0.2.1: ICMP echo request, id 46966, seq 1, length 64
62:57:8e:70:44:64 > 26:0e:25:37:8f:96, ethertype IPv4 (0x0800), length 98: (tos 0x0, ttl 64, id 13762, offset 0, flags [DF], proto ICMP (1), length 84)
    192.0.2.2 > 192.0.2.1: ICMP echo request, id 46966, seq 1, length 64

Если вмСсто этого ΠΏΠΎΠΊΠ°Π·Ρ‹Π²Π°ΡŽΡ‚ΡΡ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ ARP, Π½ΡƒΠΆΠ½ΠΎ ΡƒΠ±Ρ€Π°Ρ‚ΡŒ Ρ„ΠΈΠ»ΡŒΡ‚Ρ€Ρ‹ (это Π΄Π΅Π»Π°Π΅Ρ‚ sudo ./stand detach), ΠΏΡƒΡΡ‚ΠΈΡ‚ΡŒ ping, Π·Π°Ρ‚Π΅ΠΌ ΡƒΡΡ‚Π°Π½ΠΎΠ²ΠΈΡ‚ΡŒ Ρ„ΠΈΠ»ΡŒΡ‚Ρ€Ρ‹ ΠΈΒ ΠΏΠΎΠΏΡ€ΠΎΠ±ΠΎΠ²Π°Ρ‚ΡŒ снова. ΠŸΡ€ΠΎΠ±Π»Π΅ΠΌΠ° Π²Β Ρ‚ΠΎΠΌ, Ρ‡Ρ‚ΠΎΒ Ρ„ΠΈΠ»ΡŒΡ‚Ρ€ XDP_TX дСйствуСт ΠΈΒ Π½Π°Β ARP, и Ссли стСк
пространства ΠΈΠΌΠ΅Π½ xdp-test успСл Β«Π·Π°Π±Ρ‹Ρ‚ΡŒΒ» MAC-адрСс 192.0.2.1, ΠΎΠ½ нС смоТСт Ρ€Π°Π·Ρ€Π΅ΡˆΠΈΡ‚ΡŒ этот IP.

ΠŸΠΎΡΡ‚Π°Π½ΠΎΠ²ΠΊΠ° Π·Π°Π΄Π°Ρ‡ΠΈ

ΠŸΠ΅Ρ€Π΅ΠΉΠ΄Π΅ΠΌ к заявлСнной Π·Π°Π΄Π°Ρ‡ΠΈ: Π½Π°ΠΏΠΈΡΠ°Ρ‚ΡŒ Π½Π°Β XDP ΠΌΠ΅Ρ…Π°Π½ΠΈΠ·ΠΌ SYN cookies.

До сих ΠΏΠΎΡ€ популярной DDoS-Π°Ρ‚Π°ΠΊΠΎΠΉ остаСтся SYN flood, ΡΡƒΡ‚ΡŒ ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΉ Π²Β ΡΠ»Π΅Π΄ΡƒΡŽΡ‰Π΅ΠΌ. ΠŸΡ€ΠΈΒ ΡƒΡΡ‚Π°Π½ΠΎΠ²ΠΊΠ΅ соСдинСния (TCP handshake) сСрвСр ΠΏΠΎΠ»ΡƒΡ‡Π°Π΅Ρ‚ SYN, выдСляСт рСсурсы ΠΏΠΎΠ΄Β Π±ΡƒΠ΄ΡƒΡ‰Π΅Π΅ соСдинСниС, ΠΎΡ‚Π²Π΅Ρ‡Π°Π΅Ρ‚ SYNACK-ΠΏΠ°ΠΊΠ΅Ρ‚ΠΎΠΌ ΠΈΒ ΠΎΠΆΠΈΠ΄Π°Π΅Ρ‚ ACK. ΠΡ‚Π°ΠΊΡƒΡŽΡ‰ΠΈΠΉ просто отправляСт SYN-ΠΏΠ°ΠΊΠ΅Ρ‚Ρ‹ ΡΒ ΠΏΠΎΠ΄Π΄Π΅Π»ΡŒΠ½Ρ‹Ρ… адрСсов в количСствС тысяч в сСкунду с каТдого хоста из многотысячного Π±ΠΎΡ‚Π½Π΅Ρ‚Π°. Π‘Π΅Ρ€Π²Π΅Ρ€ Π²Ρ‹Π½ΡƒΠΆΠ΄Π΅Π½ Π²Ρ‹Π΄Π΅Π»ΡΡ‚ΡŒ рСсурсы сразу ΠΏΠΎΒ ΠΏΡ€ΠΈΠ±Ρ‹Ρ‚ΠΈΠΈ ΠΏΠ°ΠΊΠ΅Ρ‚Π°, а освобоТдаСт ΠΏΠΎΒ Π±ΠΎΠ»ΡŒΡˆΠΎΠΌΡƒ Ρ‚Π°ΠΉΠΌΠ°ΡƒΡ‚Ρƒ, Π²Β Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚Π΅ исчСрпываСтся ΠΏΠ°ΠΌΡΡ‚ΡŒ ΠΈΠ»ΠΈΒ Π»ΠΈΠΌΠΈΡ‚Ρ‹, Π½ΠΎΠ²Ρ‹Π΅ соСдинСния Π½Π΅Β ΠΏΡ€ΠΈΠ½ΠΈΠΌΠ°ΡŽΡ‚ΡΡ, сСрвис нСдоступСн.

Если Π½Π΅Β Π²Ρ‹Π΄Π΅Π»ΡΡ‚ΡŒ ΠΏΠΎΒ SYN-ΠΏΠ°ΠΊΠ΅Ρ‚Ρƒ рСсурсы, Π°Β Ρ‚ΠΎΠ»ΡŒΠΊΠΎ ΠΎΡ‚Π²Π΅Ρ‡Π°Ρ‚ΡŒ SYNACK-ΠΏΠ°ΠΊΠ΅Ρ‚ΠΎΠΌ, ΠΊΠ°ΠΊ Ρ‚ΠΎΠ³Π΄Π° сСрвСру ΠΏΠΎΠ½ΡΡ‚ΡŒ, Ρ‡Ρ‚ΠΎΒ ACK-ΠΏΠ°ΠΊΠ΅Ρ‚, ΠΏΡ€ΠΈΡˆΠ΅Π΄ΡˆΠΈΠΉ ΠΏΠΎΠ·ΠΆΠ΅, относится ΠΊΒ SYN-ΠΏΠ°ΠΊΠ΅Ρ‚Ρƒ, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ нС сохраняли? Π’Π΅Π΄ΡŒ Π°Ρ‚Π°ΠΊΡƒΡŽΡ‰ΠΈΠΉ ΠΌΠΎΠΆΠ΅Ρ‚ Π³Π΅Π½Π΅Ρ€ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ ΠΈΒ Ρ„Π°Π»ΡŒΡˆΠΈΠ²Ρ‹Π΅ ACK. Π‘ΡƒΡ‚ΡŒ SYN cookie Π² Ρ‚ΠΎΠΌ, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΠΊΠΎΠ΄ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ Π²Β seqnum ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Ρ‹ соСдинСния ΠΊΠ°ΠΊΒ Ρ…ΡΡˆ от адрСсов, ΠΏΠΎΡ€Ρ‚ΠΎΠ² ΠΈΒ ΠΌΠ΅Π½ΡΡŽΡ‰Π΅ΠΉΡΡ соли. Если ACK успСл ΠΏΡ€ΠΈΠΉΡ‚ΠΈ до смСны соли, ΠΌΠΎΠΆΠ½ΠΎ Π΅Ρ‰Π΅ Ρ€Π°Π· ΠΏΠΎΡΡ‡ΠΈΡ‚Π°Ρ‚ΡŒ Ρ…ΡΡˆ ΠΈΒ ΡΡ€Π°Π²Π½ΠΈΡ‚ΡŒ с acknum. ΠŸΠΎΠ΄Π΄Π΅Π»Π°Ρ‚ΡŒ acknum Π°Ρ‚Π°ΠΊΡƒΡŽΡ‰ΠΈΠΉ Π½Π΅Β ΠΌΠΎΠΆΠ΅Ρ‚, Ρ‚Π°ΠΊ ΠΊΠ°ΠΊ соль Π²ΠΊΠ»ΡŽΡ‡Π°Π΅Ρ‚ сСкрСт, Π°Β ΠΏΠ΅Ρ€Π΅Π±Ρ€Π°Ρ‚ΡŒ нС успССт ΠΈΠ·-Π·Π°Β ΠΎΠ³Ρ€Π°Π½ΠΈΡ‡Π΅Π½Π½ΠΎΠ³ΠΎ ΠΊΠ°Π½Π°Π»Π°.

SYN cookie Π΄Π°Π²Π½ΠΎ Ρ€Π΅Π°Π»ΠΈΠ·ΠΎΠ²Π°Π½ в ядрС Linux ΠΈΒ Π΄Π°ΠΆΠ΅ ΠΌΠΎΠΆΠ΅Ρ‚ автоматичСски Π²ΠΊΠ»ΡŽΡ‡Π°Ρ‚ΡŒΡΡ, Ссли SYN приходят слишком быстро и массово.

Π›ΠΈΠΊΠ±Π΅Π· ΠΏΠΎ TCP handshake

TCP обСспСчиваСт ΠΏΠ΅Ρ€Π΅Π΄Π°Ρ‡Ρƒ Π΄Π°Π½Π½Ρ‹Ρ… ΠΊΠ°ΠΊ ΠΏΠΎΡ‚ΠΎΠΊΠ° Π±Π°ΠΉΡ‚ΠΎΠ², Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€, ΠΏΠΎΠ²Π΅Ρ€Ρ… TCP ΠΏΠ΅Ρ€Π΅Π΄Π°ΡŽΡ‚ΡΡ HTTP-запросы. ΠŸΠΎΡ‚ΠΎΠΊ пСрСдаСтся по кусочкам Π²Β ΠΏΠ°ΠΊΠ΅Ρ‚Π°Ρ…. У всСх ΠΏΠ°ΠΊΠ΅Ρ‚ΠΎΠ² TCP Π΅ΡΡ‚ΡŒ логичСскиС Ρ„Π»Π°Π³ΠΈ ΠΈΒ 32-Π±ΠΈΡ‚Π½Ρ‹Π΅ Π½ΠΎΠΌΠ΅Ρ€Π° ΠΏΠΎΡΠ»Π΅Π΄ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚Π΅ΠΉ:

  • ΠšΠΎΠΌΠ±ΠΈΠ½Π°Ρ†ΠΈΡ Ρ„Π»Π°Π³ΠΎΠ² опрСдСляСт Ρ€ΠΎΠ»ΡŒ ΠΊΠΎΠ½ΠΊΡ€Π΅Ρ‚Π½ΠΎΠ³ΠΎ ΠΏΠ°ΠΊΠ΅Ρ‚Π°. Π€Π»Π°Π³Β SYN ΠΎΠ·Π½Π°Ρ‡Π°Π΅Ρ‚, что это ΠΏΠ΅Ρ€Π²Ρ‹ΠΉ ΠΏΠ°ΠΊΠ΅Ρ‚ отправитСля в соСдинСнии. Π€Π»Π°Π³ ACK ΠΎΠ·Π½Π°Ρ‡Π°Π΅Ρ‚, Ρ‡Ρ‚ΠΎΒ ΠΎΡ‚ΠΏΡ€Π°Π²ΠΈΡ‚Π΅Π»ΡŒ ΠΏΠΎΠ»ΡƒΡ‡ΠΈΠ» всС Π΄Π°Π½Π½Ρ‹Π΅ соСдинСния Π΄ΠΎΒ Π±Π°ΠΉΡ‚Π° acknum. ΠŸΠ°ΠΊΠ΅Ρ‚ ΠΌΠΎΠΆΠ΅Ρ‚ ΠΈΠΌΠ΅Ρ‚ΡŒ нСсколько Ρ„Π»Π°Π³ΠΎΠ² и называСтся ΠΏΠΎΒ ΠΈΡ… ΠΊΠΎΠΌΠ±ΠΈΠ½Π°Ρ†ΠΈΠΈ, Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€, SYNACK-ΠΏΠ°ΠΊΠ΅Ρ‚.

  • Sequence number (seqnum) опрСдСляСт смСщСниС Π²Β ΠΏΠΎΡ‚ΠΎΠΊΠ΅ Π΄Π°Π½Π½Ρ‹Ρ… для пСрвого Π±Π°ΠΉΡ‚Π°, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ пСрСдаСтся в этом ΠΏΠ°ΠΊΠ΅Ρ‚Π΅. НапримСр, Ссли Π²Β ΠΏΠ΅Ρ€Π²ΠΎΠΌ ΠΏΠ°ΠΊΠ΅Ρ‚Π΅ с XΒ Π±Π°ΠΉΡ‚Π°ΠΌΠΈ Π΄Π°Π½Π½Ρ‹Ρ… этот Π½ΠΎΠΌΠ΅Ρ€ Π±Ρ‹Π»Β N, Π²Β ΡΠ»Π΅Π΄ΡƒΡŽΡ‰Π΅ΠΌ ΠΏΠ°ΠΊΠ΅Ρ‚Π΅ с новыми Π΄Π°Π½Π½Ρ‹ΠΌΠΈ ΠΎΠ½ Π±ΡƒΠ΄Π΅Ρ‚ N+X. Π’Β Π½Π°Ρ‡Π°Π»Π΅ соСдинСния каТдая сторона Π²Ρ‹Π±ΠΈΡ€Π°Π΅Ρ‚ этот Π½ΠΎΠΌΠ΅Ρ€ ΠΏΡ€ΠΎΠΈΠ·Π²ΠΎΠ»ΡŒΠ½Ρ‹ΠΌ ΠΎΠ±Ρ€Π°Π·ΠΎΠΌ.

  • Acknowledgement number (acknum)Β β€” Ρ‚Π°ΠΊΠΎΠ΅Β ΠΆΠ΅ смСщСниС, ΠΊΠ°ΠΊΒ seqnum, но опрСдСляСт Π½Π΅Β Π½ΠΎΠΌΠ΅Ρ€ ΠΏΠ΅Ρ€Π΅Π΄Π°Π²Π°Π΅ΠΌΠΎΠ³ΠΎ Π±Π°ΠΉΡ‚Π°, Π°Β Π½ΠΎΠΌΠ΅Ρ€ ΠΏΠ΅Ρ€Π²ΠΎΠ³ΠΎ Π±Π°ΠΉΡ‚Π° от получатСля, ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ³ΠΎ ΠΎΡ‚ΠΏΡ€Π°Π²ΠΈΡ‚Π΅Π»ΡŒ Π½Π΅Β Π²ΠΈΠ΄Π΅Π».

Π’Β Π½Π°Ρ‡Π°Π»Π΅ соСдинСния стороны Π΄ΠΎΠ»ΠΆΠ½Ρ‹ ΡΠΎΠ³Π»Π°ΡΠΎΠ²Π°Ρ‚ΡŒ seqnum ΠΈΒ acknum. ΠšΠ»ΠΈΠ΅Π½Ρ‚ отправляСт SYN-ΠΏΠ°ΠΊΠ΅Ρ‚ со своим seqnum = X. Π‘Π΅Ρ€Π²Π΅Ρ€ ΠΎΡ‚Π²Π΅Ρ‡Π°Π΅Ρ‚ SYNACK-ΠΏΠ°ΠΊΠ΅Ρ‚ΠΎΠΌ, ΠΊΡƒΠ΄Π° записываСт свой seqnum = Y и выставляСт acknum = X + 1. ΠšΠ»ΠΈΠ΅Π½Ρ‚ Π½Π°Β SYNACK ΠΎΡ‚Π²Π΅Ρ‡Π°Π΅Ρ‚ ACK-ΠΏΠ°ΠΊΠ΅Ρ‚ΠΎΠΌ, Π³Π΄Π΅Β seqnum = X + 1, acknum = Y + 1. ПослС этого начинаСтся собствСнно ΠΏΠ΅Ρ€Π΅Π΄Π°Ρ‡Π° Π΄Π°Π½Π½Ρ‹Ρ….

Если собСсСдник Π½Π΅Β ΠΏΠΎΠ΄Ρ‚Π²Π΅Ρ€ΠΆΠ΄Π°Π΅Ρ‚ ΠΏΠΎΠ»ΡƒΡ‡Π΅Π½ΠΈΠ΅ ΠΏΠ°ΠΊΠ΅Ρ‚Π°, TCP отправляСт Π΅Π³ΠΎ ΠΏΠΎΠ²Ρ‚ΠΎΡ€Π½ΠΎ ΠΏΠΎΒ Ρ‚Π°ΠΉΠΌΠ°ΡƒΡ‚Ρƒ.

ΠŸΠΎΡ‡Π΅ΠΌΡƒ SYN cookies Π½Π΅Β ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡŽΡ‚ΡΡ всСгда?

Π’ΠΎ-ΠΏΠ΅Ρ€Π²Ρ‹Ρ…, Ссли потСряСтся SYNACK ΠΈΠ»ΠΈΒ ACK, придСтся ΠΆΠ΄Π°Ρ‚ΡŒ ΠΏΠΎΠ²Ρ‚ΠΎΡ€Π½ΠΎΠΉ ΠΎΡ‚ΠΏΡ€Π°Π²ΠΊΠΈΒ β€” замСдляСтся установка соСдинСния. Π’ΠΎ-Π²Ρ‚ΠΎΡ€Ρ‹Ρ…, Π²Β SYN-ΠΏΠ°ΠΊΠ΅Ρ‚Π΅Β β€” ΠΈΒ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ Π²Β Π½Π΅ΠΌ!Β β€” пСрСдаСтся ряд ΠΎΠΏΡ†ΠΈΠΉ, Π²Π»ΠΈΡΡŽΡ‰ΠΈΡ… Π½Π°Β Π΄Π°Π»ΡŒΠ½Π΅ΠΉΡˆΡƒΡŽ Ρ€Π°Π±ΠΎΡ‚Ρƒ соСдинСния. НС запоминая входящиС SYN-ΠΏΠ°ΠΊΠ΅Ρ‚Ρ‹, сСрвСр Ρ‚Π°ΠΊΠΈΠΌ ΠΎΠ±Ρ€Π°Π·ΠΎΠΌ ΠΈΠ³Π½ΠΎΡ€ΠΈΡ€ΡƒΠ΅Ρ‚ эти ΠΎΠΏΡ†ΠΈΠΈ, Π²Β ΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΡ… ΠΏΠ°ΠΊΠ΅Ρ‚Π°Ρ… ΠΊΠ»ΠΈΠ΅Π½Ρ‚ ΡƒΠΆΠ΅ Π½Π΅Β ΠΏΡ€ΠΈΡˆΠ»Π΅Ρ‚ ΠΈΡ…. Π Π°Π±ΠΎΡ‚Π°Ρ‚ΡŒ TCP при этом ΠΌΠΎΠΆΠ΅Ρ‚, Π½ΠΎΒ ΠΊΠ°ΠΊ ΠΌΠΈΠ½ΠΈΠΌΡƒΠΌ Π½Π°Β Π½Π°Ρ‡Π°Π»ΡŒΠ½ΠΎΠΌ этапС качСство соСдинСния снизится.

Π‘Β Ρ‚ΠΎΡ‡ΠΊΠΈ зрСния ΠΏΠ°ΠΊΠ΅Ρ‚ΠΎΠ², XDP-ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΠ° Π΄ΠΎΠ»ΠΆΠ½Π° Π΄Π΅Π»Π°Ρ‚ΡŒ ΡΠ»Π΅Π΄ΡƒΡŽΡ‰Π΅Π΅:

  • Π½Π° SYN ΠΎΡ‚Π²Π΅Ρ‡Π°Ρ‚ΡŒ SYNACK с cookie;
  • Π½Π°Β ACK ΠΎΡ‚Π²Π΅Ρ‡Π°Ρ‚ΡŒ RST (Ρ€Π°Π·Ρ€Ρ‹Π²Π°Ρ‚ΡŒ соСдинСниС);
  • ΠΎΡΡ‚Π°Π»ΡŒΠ½Ρ‹Π΅ ΠΏΠ°ΠΊΠ΅Ρ‚Ρ‹ ΡΠ±Ρ€Π°ΡΡ‹Π²Π°Ρ‚ΡŒ.

ПсСвдокод Π°Π»Π³ΠΎΡ€ΠΈΡ‚ΠΌΠ° вмСстС с разбором ΠΏΠ°ΠΊΠ΅Ρ‚Π°:

Если это Π½Π΅ Ethernet,
    ΠΏΡ€ΠΎΠΏΡƒΡΡ‚ΠΈΡ‚ΡŒ ΠΏΠ°ΠΊΠ΅Ρ‚.
Если это Π½Π΅ IPv4,
    ΠΏΡ€ΠΎΠΏΡƒΡΡ‚ΠΈΡ‚ΡŒ ΠΏΠ°ΠΊΠ΅Ρ‚.
Если адрСс Π² Ρ‚Π°Π±Π»ΠΈΡ†Π΅ ΠΏΡ€ΠΎΠ²Π΅Ρ€Π΅Π½Π½Ρ‹Ρ…,               (*)
        ΡƒΠΌΠ΅Π½ΡŒΡˆΠΈΡ‚ΡŒ счСтчик ΠΎΡΡ‚Π°Π²ΡˆΠΈΡ…ΡΡ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΎΠΊ,
        ΠΏΡ€ΠΎΠΏΡƒΡΡ‚ΠΈΡ‚ΡŒ ΠΏΠ°ΠΊΠ΅Ρ‚.
Если это Π½Π΅ TCP,
    ΡΠ±Ρ€ΠΎΡΠΈΡ‚ΡŒ ΠΏΠ°ΠΊΠ΅Ρ‚.     (**)
Если это SYN,
    ΠΎΡ‚Π²Π΅Ρ‚ΠΈΡ‚ΡŒ SYN-ACK с cookie.
Если это ACK,
    Ссли Π² acknum Π»Π΅ΠΆΠΈΡ‚ Π½Π΅ cookie,
        ΡΠ±Ρ€ΠΎΡΠΈΡ‚ΡŒ ΠΏΠ°ΠΊΠ΅Ρ‚.
    ЗанСсти Π² Ρ‚Π°Π±Π»ΠΈΡ†Ρƒ адрСс с N ΠΎΡΡ‚Π°Π²ΡˆΠΈΡ…ΡΡ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΎΠΊ.    (*)
    ΠžΡ‚Π²Π΅Ρ‚ΠΈΡ‚ΡŒ RST.   (**)
Π’ ΠΎΡΡ‚Π°Π»ΡŒΠ½Ρ‹Ρ… случаях ΡΠ±Ρ€ΠΎΡΠΈΡ‚ΡŒ ΠΏΠ°ΠΊΠ΅Ρ‚.

Одной (*) ΠΎΡ‚ΠΌΠ΅Ρ‡Π΅Π½Ρ‹ ΠΏΡƒΠ½ΠΊΡ‚Ρ‹, Π²Β ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Ρ… Π½ΡƒΠΆΠ½ΠΎ ΡƒΠΏΡ€Π°Π²Π»ΡΡ‚ΡŒ состояниСм систСмы — Π½Π°Β ΠΏΠ΅Ρ€Π²ΠΎΠΌ этапС ΠΌΠΎΠΆΠ½ΠΎ ΠΎΠ±ΠΎΠΉΡ‚ΠΈΡΡŒ Π±Π΅Π·Β Π½ΠΈΡ…, просто Ρ€Π΅Π°Π»ΠΈΠ·ΠΎΠ²Π°Π² TCP handshake с гСнСрациСй SYN cookie в качСствС seqnum.

На мСстС (**), ΠΏΠΎΠΊΠ° у нас Π½Π΅Ρ‚ Ρ‚Π°Π±Π»ΠΈΡ†Ρ‹, Π±ΡƒΠ΄Π΅ΠΌ ΠΏΡ€ΠΎΠΏΡƒΡΠΊΠ°Ρ‚ΡŒ ΠΏΠ°ΠΊΠ΅Ρ‚.

РСализация TCP handshake

Π Π°Π·Π±ΠΎΡ€ ΠΏΠ°ΠΊΠ΅Ρ‚Π° ΠΈ вСрификация ΠΊΠΎΠ΄Π°

Нам понадобятся структуры сСтСвых Π·Π°Π³ΠΎΠ»ΠΎΠ²ΠΊΠΎΠ²: Ethernet (uapi/linux/if_ether.h), IPv4 (uapi/linux/ip.h) ΠΈΒ TCP (uapi/linux/tcp.h). ПослСдний у мСня Ρ‚Π°ΠΊ ΠΈΒ Π½Π΅Β ΠΏΠΎΠ»ΡƒΡ‡ΠΈΠ»ΠΎΡΡŒ ΠΏΠΎΠ΄ΠΊΠ»ΡŽΡ‡ΠΈΡ‚ΡŒ ΠΈΠ·-за ошибок, связанных с atomic64_t, ΠΏΡ€ΠΈΡˆΠ»ΠΎΡΡŒ ΡΠΊΠΎΠΏΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ Π½ΡƒΠΆΠ½Ρ‹Π΅ опрСдСлСния Π²Β ΠΊΠΎΠ΄.

ВсС Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ Π²Β C Π²Ρ‹Π΄Π΅Π»ΡΡŽΡ‚ΡΡ для удобства чтСния, Π΄ΠΎΠ»ΠΆΠ½Ρ‹ Π±Ρ‹Ρ‚ΡŒ встроСны по мСсту Π²Ρ‹Π·ΠΎΠ²Π°, Ρ‚Π°ΠΊ ΠΊΠ°ΠΊ Π²Π΅Ρ€ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ eBPF в ядрС Π·Π°ΠΏΡ€Π΅Ρ‰Π°Π΅Ρ‚ ΠΏΠ΅Ρ€Π΅Ρ…ΠΎΠ΄Ρ‹ Π½Π°Π·Π°Π΄, Ρ‚ΠΎΒ Π΅ΡΡ‚ΡŒ, фактичСски, Ρ†ΠΈΠΊΠ»Ρ‹ ΠΈΒ Π²Ρ‹Π·ΠΎΠ²Ρ‹ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΉ.

#define INTERNAL static __attribute__((always_inline))

ΠœΠ°ΠΊΡ€ΠΎΡ LOG() ΠΎΡ‚ΠΊΠ»ΡŽΡ‡Π°Π΅Ρ‚ ΠΏΠ΅Ρ‡Π°Ρ‚ΡŒ Π²Β Ρ€Π΅Π»ΠΈΠ·Π½ΠΎΠΉ сборкС.

ΠŸΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΠ° прСдставляСт собой ΠΊΠΎΠ½Π²Π΅ΠΉΠ΅Ρ€ ΠΈΠ·Β Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΉ. КаТдая ΠΏΡ€ΠΈΠ½ΠΈΠΌΠ°Π΅Ρ‚ ΠΏΠ°ΠΊΠ΅Ρ‚, Π²Β ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΌ Π²Ρ‹Π΄Π΅Π»Π΅Π½ Π·Π°Π³ΠΎΠ»ΠΎΠ²ΠΎΠΊ ΡΠΎΠΎΡ‚Π²Π΅Ρ‚ΡΡ‚Π²ΡƒΡŽΡ‰Π΅Π³ΠΎ уровня, Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€, process_ether() ΠΎΠΆΠΈΠ΄Π°Π΅Ρ‚, Ρ‡Ρ‚ΠΎ Π·Π°ΠΏΠΎΠ»Π½Π΅Π½ΠΎ ether. ΠŸΠΎΒ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚Π°ΠΌ Π°Π½Π°Π»ΠΈΠ·Π° ΠΏΠΎΠ»Π΅ΠΉ функция ΠΌΠΎΠΆΠ΅Ρ‚ ΠΏΠ΅Ρ€Π΅Π΄Π°Ρ‚ΡŒ ΠΏΠ°ΠΊΠ΅Ρ‚ Π½Π°Β ΡƒΡ€ΠΎΠ²Π΅Π½ΡŒ Π²Ρ‹ΡˆΠ΅. Π Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ Ρ€Π°Π±ΠΎΡ‚Ρ‹ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈΒ β€” дСйствиС XDP. Пока ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠΈ SYN ΠΈΒ ACK ΠΏΡ€ΠΎΠΏΡƒΡΠΊΠ°ΡŽΡ‚ всС ΠΏΠ°ΠΊΠ΅Ρ‚Ρ‹.

struct Packet {
    struct xdp_md* ctx;

    struct ethhdr* ether;
    struct iphdr* ip;
    struct tcphdr* tcp;
};

INTERNAL int process_tcp_syn(struct Packet* packet) { return XDP_PASS; }
INTERNAL int process_tcp_ack(struct Packet* packet) { return XDP_PASS; }
INTERNAL int process_tcp(struct Packet* packet) { ... }
INTERNAL int process_ip(struct Packet* packet) { ... }

INTERNAL int
process_ether(struct Packet* packet) {
    struct ethhdr* ether = packet->ether;

    LOG("Ether(proto=0x%x)", bpf_ntohs(ether->h_proto));

    if (ether->h_proto != bpf_ntohs(ETH_P_IP)) {
        return XDP_PASS;
    }

    // B
    struct iphdr* ip = (struct iphdr*)(ether + 1);
    if ((void*)(ip + 1) > (void*)packet->ctx->data_end) {
        return XDP_DROP; /* malformed packet */
    }

    packet->ip = ip;
    return process_ip(packet);
}

SEC("prog")
int xdp_main(struct xdp_md* ctx) {
    struct Packet packet;
    packet.ctx = ctx;

    // A
    struct ethhdr* ether = (struct ethhdr*)(void*)ctx->data;
    if ((void*)(ether + 1) > (void*)ctx->data_end) {
        return XDP_PASS;
    }

    packet.ether = ether;
    return process_ether(&packet);
}

ΠžΠ±Ρ€Π°Ρ‰Π°ΡŽ Π²Π½ΠΈΠΌΠ°Π½ΠΈΠ΅ Π½Π°Β ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠΈ, ΠΎΡ‚ΠΌΠ΅Ρ‡Π΅Π½Π½Ρ‹Π΅ A ΠΈΒ B. Если Π·Π°ΠΊΠΎΠΌΠΌΠ΅Π½Ρ‚ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒΒ A, ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΠ° собСрСтся, Π½ΠΎΒ ΠΏΡ€ΠΈΒ Π·Π°Π³Ρ€ΡƒΠ·ΠΊΠ΅ Π±ΡƒΠ΄Π΅Ρ‚ ошибка Π²Π΅Ρ€ΠΈΡ„ΠΈΠΊΠ°Ρ†ΠΈΠΈ:

Verifier analysis:

<...>
11: (7b) *(u64 *)(r10 -48) = r1
12: (71) r3 = *(u8 *)(r7 +13)
invalid access to packet, off=13 size=1, R7(id=0,off=0,r=0)
R7 offset is outside of the packet
processed 11 insns (limit 1000000) max_states_per_insn 0 total_states 0 peak_states 0 mark_read 0

Error fetching program/map!

ΠšΠ»ΡŽΡ‡Π΅Π²Π°Ρ строка invalid access to packet, off=13 size=1, R7(id=0,off=0,r=0): Π΅ΡΡ‚ΡŒ ΠΏΡƒΡ‚ΠΈ выполнСния, ΠΊΠΎΠ³Π΄Π° Ρ‚Ρ€ΠΈΠ½Π°Π΄Ρ†Π°Ρ‚Ρ‹ΠΉ Π±Π°ΠΉΡ‚ ΠΎΡ‚Β Π½Π°Ρ‡Π°Π»Π° Π±ΡƒΡ„Π΅Ρ€Π° находится Π²Π½Π΅Β ΠΏΠ°ΠΊΠ΅Ρ‚Π°. ΠŸΠΎΒ Π»ΠΈΡΡ‚ΠΈΠ½Π³Ρƒ слоТновато ΠΏΠΎΠ½ΡΡ‚ΡŒ, ΠΎΒ ΠΊΠ°ΠΊΠΎΠΉ строкС ΠΈΠ΄Π΅Ρ‚ Ρ€Π΅Ρ‡ΡŒ, Π·Π°Ρ‚ΠΎ Π΅ΡΡ‚ΡŒ Π½ΠΎΠΌΠ΅Ρ€ инструкции (12) и дизассСмблСр, ΠΏΠΎΠΊΠ°Π·Ρ‹Π²Π°ΡŽΡ‰ΠΈΠΉ строки исходного ΠΊΠΎΠ΄Π°:

llvm-objdump -S xdp_filter.o | less

Π’Β Π΄Π°Π½Π½ΠΎΠΌ случаС ΠΎΠ½ ΡƒΠΊΠ°Π·Ρ‹Π²Π°Π΅Ρ‚ на строку

LOG("Ether(proto=0x%x)", bpf_ntohs(ether->h_proto));

ΠΏΠΎΒ ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΉ понятно, Ρ‡Ρ‚ΠΎΒ ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΠ° Π²Β ether. ВсСгда бы Ρ‚Π°ΠΊ.

ΠžΡ‚Π²Π΅Ρ‚ Π½Π° SYN

ЦСль на этом этапС — Ρ„ΠΎΡ€ΠΌΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ ΠΊΠΎΡ€Ρ€Π΅ΠΊΡ‚Π½Ρ‹ΠΉ SYNACK-ΠΏΠ°ΠΊΠ΅Ρ‚ с фиксированным seqnum, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ Π²Β Π±ΡƒΠ΄ΡƒΡ‰Π΅ΠΌ замСнится Π½Π°Β SYN cookie. ВсС измСнСния происходят Π²Β process_tcp_syn() и окрСстностях.

ΠŸΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ° ΠΏΠ°ΠΊΠ΅Ρ‚Π°

Как Π½ΠΈ странно, Π²ΠΎΡ‚ самая ΠΏΡ€ΠΈΠΌΠ΅Ρ‡Π°Ρ‚Π΅Π»ΡŒΠ½Π°Ρ строка, Ρ‚ΠΎΡ‡Π½Π΅Π΅, ΠΊΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΉ ΠΊΒ Π½Π΅ΠΉ:

/* Required to verify checksum calculation */
const void* data_end = (const void*)ctx->data_end;

ΠŸΡ€ΠΈΒ Π½Π°ΠΏΠΈΡΠ°Π½ΠΈΠΈ ΠΏΠ΅Ρ€Π²ΠΎΠΉ вСрсии ΠΊΠΎΠ΄Π° использовалось ядро 5.1, для вСрификатора ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ³ΠΎ Π±Ρ‹Π»Π° Ρ€Π°Π·Π½ΠΈΡ†Π° ΠΌΠ΅ΠΆΠ΄Ρƒ data_end ΠΈΒ (const void*)ctx->data_end. ΠŸΡ€ΠΈΒ Π½Π°ΠΏΠΈΡΠ°Π½ΠΈΠΈ ΡΡ‚Π°Ρ‚ΡŒΠΈ ядро 5.3.1 Π½Π΅Β ΠΈΠΌΠ΅Π»ΠΎ Ρ‚Π°ΠΊΠΎΠΉ ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΡ‹. Π’ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎ, компилятор обращался к локальной ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½ΠΎΠΉ ΠΈΠ½Π°Ρ‡Π΅, Ρ‡Π΅ΠΌΒ ΠΊΒ ΠΏΠΎΠ»ΡŽ. ΠœΠΎΡ€Π°Π»ΡŒΒ β€” на большой влоТСнности ΡƒΠΏΡ€ΠΎΡ‰Π΅Π½ΠΈΠ΅ ΠΊΠΎΠ΄Π° ΠΌΠΎΠΆΠ΅Ρ‚ ΠΏΠΎΠΌΠΎΡ‡ΡŒ.

Π”Π°Π»Π΅Π΅ Ρ€ΡƒΡ‚ΠΈΠ½Π½Ρ‹Π΅ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠΈ Π΄Π»ΠΈΠ½ Π²ΠΎ славу Π²Π΅Ρ€ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€Π°; ΠΎΒ MAX_CSUM_BYTES Π½ΠΈΠΆΠ΅.

const u32 ip_len = ip->ihl * 4;
if ((void*)ip + ip_len > data_end) {
    return XDP_DROP; /* malformed packet */
}
if (ip_len > MAX_CSUM_BYTES) {
    return XDP_ABORTED; /* implementation limitation */
}

const u32 tcp_len = tcp->doff * 4;
if ((void*)tcp + tcp_len > (void*)ctx->data_end) {
    return XDP_DROP; /* malformed packet */
}
if (tcp_len > MAX_CSUM_BYTES) {
    return XDP_ABORTED; /* implementation limitation */
}

Π Π°Π·Π²ΠΎΡ€ΠΎΡ‚ ΠΏΠ°ΠΊΠ΅Ρ‚Π°

ЗаполняСм seqnum ΠΈ acknum, выставляСм ACK (SYN ΡƒΠΆΠ΅ выставлСн):

const u32 cookie = 42;
tcp->ack_seq = bpf_htonl(bpf_ntohl(tcp->seq) + 1);
tcp->seq = bpf_htonl(cookie);
tcp->ack = 1;

МСняСм мСстами ΠΏΠΎΡ€Ρ‚Ρ‹ TCP, адрСс IP ΠΈΒ MAC-адрСса. Бтандартная Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠ° нСдоступна ΠΈΠ·Β XDP-ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΡ‹, поэтому memcpy()Β β€” макрос, ΡΠΊΡ€Ρ‹Π²Π°ΡŽΡ‰ΠΈΠΉ интринсик Clang.

const u16 temp_port = tcp->source;
tcp->source = tcp->dest;
tcp->dest = temp_port;

const u32 temp_ip = ip->saddr;
ip->saddr = ip->daddr;
ip->daddr = temp_ip;

struct ethhdr temp_ether = *ether;
memcpy(ether->h_dest, temp_ether.h_source, ETH_ALEN);
memcpy(ether->h_source, temp_ether.h_dest, ETH_ALEN);

ΠŸΠ΅Ρ€Π΅ΡΡ‡Π΅Ρ‚ ΠΊΠΎΠ½Ρ‚Ρ€ΠΎΠ»ΡŒΠ½Ρ‹Ρ… сумм

ΠšΠΎΠ½Ρ‚Ρ€ΠΎΠ»ΡŒΠ½Ρ‹Π΅ суммы IPv4 ΠΈΒ TCP Ρ‚Ρ€Π΅Π±ΡƒΡŽΡ‚ слоТСния всСх 16-Π±ΠΈΡ‚Π½Ρ‹Ρ… слов Π²Β Π·Π°Π³ΠΎΠ»ΠΎΠ²ΠΊΠ°Ρ…, Π°Β Ρ€Π°Π·ΠΌΠ΅Ρ€ Π·Π°Π³ΠΎΠ»ΠΎΠ²ΠΊΠΎΠ² записан Π²Β Π½ΠΈΡ…, Ρ‚ΠΎΒ Π΅ΡΡ‚ΡŒ Π½Π°Β ΠΌΠΎΠΌΠ΅Π½Ρ‚ компиляции нСизвСстСн. Π­Ρ‚ΠΎ ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΠ°, ΠΏΠΎΡ‚ΠΎΠΌΡƒ Ρ‡Ρ‚ΠΎ Π²Π΅Ρ€ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ нС пропустит ΠΎΠ±Ρ‹Ρ‡Π½Ρ‹ΠΉ Ρ†ΠΈΠΊΠ» Π΄ΠΎΒ ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½ΠΎΠΉ Π³Ρ€Π°Π½ΠΈΡ†Ρ‹. Π—Π°Ρ‚ΠΎ Ρ€Π°Π·ΠΌΠ΅Ρ€ Π·Π°Π³ΠΎΠ»ΠΎΠ²ΠΊΠΎΠ² ΠΎΠ³Ρ€Π°Π½ΠΈΡ‡Π΅Π½: Π΄ΠΎΒ 64 Π±Π°ΠΉΡ‚ΠΎΠ² ΠΊΠ°ΠΆΠ΄Ρ‹ΠΉ. МоТно ΡΠ΄Π΅Π»Π°Ρ‚ΡŒ Ρ†ΠΈΠΊΠ» с фиксированным количСством ΠΈΡ‚Π΅Ρ€Π°Ρ†ΠΈΠΉ, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ ΠΌΠΎΠΆΠ΅Ρ‚ Π·Π°ΠΊΠΎΠ½Ρ‡ΠΈΡ‚ΡŒΡΡ досрочно.

Π—Π°ΠΌΠ΅Ρ‡Ρƒ, Ρ‡Ρ‚ΠΎ Π΅ΡΡ‚ΡŒ RFC 1624 ΠΏΡ€ΠΎΒ Ρ‚ΠΎ, ΠΊΠ°ΠΊΒ ΠΏΠ΅Ρ€Π΅ΡΡ‡ΠΈΡ‚Ρ‹Π²Π°Ρ‚ΡŒ ΠΊΠΎΠ½Ρ‚Ρ€ΠΎΠ»ΡŒΠ½ΡƒΡŽ сумму частично, Ссли ΠΈΠ·ΠΌΠ΅Π½Π΅Π½Ρ‹ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ фиксированныС слова ΠΏΠ°ΠΊΠ΅Ρ‚ΠΎΠ². Однако способ Π½Π΅Β ΡƒΠ½ΠΈΠ²Π΅Ρ€ΡΠ°Π»ΡŒΠ½Ρ‹ΠΉ, Π°Β Ρ€Π΅Π°Π»ΠΈΠ·Π°Ρ†ΠΈΡŽ Π±Ρ‹Π»ΠΎΒ Π±Ρ‹ слоТнСС ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΈΠ²Π°Ρ‚ΡŒ.

Ѐункция расчСта ΠΊΠΎΠ½Ρ‚Ρ€ΠΎΠ»ΡŒΠ½ΠΎΠΉ суммы:

#define MAX_CSUM_WORDS 32
#define MAX_CSUM_BYTES (MAX_CSUM_WORDS * 2)

INTERNAL u32
sum16(const void* data, u32 size, const void* data_end) {
    u32 s = 0;
#pragma unroll
    for (u32 i = 0; i < MAX_CSUM_WORDS; i++) {
        if (2*i >= size) {
            return s; /* normal exit */
        }
        if (data + 2*i + 1 + 1 > data_end) {
            return 0; /* should be unreachable */
        }
        s += ((const u16*)data)[i];
    }
    return s;
}

НСсмотря Π½Π°Β Ρ‚ΠΎ, Ρ‡Ρ‚ΠΎΒ size ΠΏΡ€ΠΎΠ²Π΅Ρ€Π΅Π½ΠΎ Π²Ρ‹Π·Ρ‹Π²Π°ΡŽΡ‰ΠΈΠΌ ΠΊΠΎΠ΄ΠΎΠΌ, Π²Ρ‚ΠΎΡ€ΠΎΠ΅ условиС Π²Ρ‹Ρ…ΠΎΠ΄Π° Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎ, Ρ‡Ρ‚ΠΎΠ±Ρ‹ Π²Π΅Ρ€ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ ΠΌΠΎΠ³ Π΄ΠΎΠΊΠ°Π·Π°Ρ‚ΡŒ Π·Π°Π²Π΅Ρ€ΡˆΠ΅Π½ΠΈΠ΅ Ρ†ΠΈΠΊΠ»Π°.

Для 32-Π±ΠΈΡ‚Π½Ρ‹Ρ… слов Ρ€Π΅Π°Π»ΠΈΠ·ΠΎΠ²Π°Π½Π° Π±ΠΎΠ»Π΅Π΅ простая вСрсия:

INTERNAL u32
sum16_32(u32 v) {
    return (v >> 16) + (v & 0xffff);
}

БобствСнно пСрСсчСт ΠΊΠΎΠ½Ρ‚Ρ€ΠΎΠ»ΡŒΠ½Ρ‹Ρ… сумм ΠΈΒ ΠΎΡ‚ΠΏΡ€Π°Π²ΠΊΠ° ΠΏΠ°ΠΊΠ΅Ρ‚Π° ΠΎΠ±Ρ€Π°Ρ‚Π½ΠΎ:

ip->check = 0;
ip->check = carry(sum16(ip, ip_len, data_end));

u32 tcp_csum = 0;
tcp_csum += sum16_32(ip->saddr);
tcp_csum += sum16_32(ip->daddr);
tcp_csum += 0x0600;
tcp_csum += tcp_len << 8;
tcp->check = 0;
tcp_csum += sum16(tcp, tcp_len, data_end);
tcp->check = carry(tcp_csum);

return XDP_TX;

Ѐункция carry() Π΄Π΅Π»Π°Π΅Ρ‚ ΠΈΠ·Β 32-Π±ΠΈΡ‚Π½ΠΎΠΉ суммы 16-Π±ΠΈΡ‚Π½Ρ‹Ρ… слов ΠΊΠΎΠ½Ρ‚Ρ€ΠΎΠ»ΡŒΠ½ΡƒΡŽ сумму, согласно RFCΒ 791.

ΠŸΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ° рукопоТатия TCP

Π€ΠΈΠ»ΡŒΡ‚Ρ€ ΠΊΠΎΡ€Ρ€Π΅ΠΊΡ‚Π½ΠΎ устанавливаСт соСдинСниС с netcat, пропуская Ρ„ΠΈΠ½Π°Π»ΡŒΠ½Ρ‹ΠΉ ACK, Π½Π°Β ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ Linux ΠΎΡ‚Π²Π΅Ρ‡Π°Π» RST-ΠΏΠ°ΠΊΠ΅Ρ‚ΠΎΠΌ, Ρ‚Π°ΠΊΒ ΠΊΠ°ΠΊ сСтСвой стСк Π½Π΅Β ΠΏΠΎΠ»ΡƒΡ‡Π°Π» SYNΒ β€” ΠΎΠ½ Π±Ρ‹Π» ΠΏΠ΅Ρ€Π΅Π΄Π΅Π»Π°Π½ Π²Β SYNACK ΠΈΒ ΠΎΡ‚ΠΏΡ€Π°Π²Π»Π΅Π½ ΠΎΠ±Ρ€Π°Ρ‚Π½ΠΎΒ — и с точки зрСния ОБ ΠΏΡ€ΠΈΠ±Ρ‹Π» ΠΏΠ°ΠΊΠ΅Ρ‚, нС относящийся ΠΊΒ ΠΎΡ‚ΠΊΡ€Ρ‹Ρ‚Ρ‹ΠΌ соСдинСниям.

$ sudo ip netns exec xdp-test   nc -nv 192.0.2.1 6666
192.0.2.1 6666: Connection reset by peer

Π’Π°ΠΆΠ½ΠΎ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΡΡ‚ΡŒ ΠΈΠΌΠ΅Π½Π½ΠΎ ΠΏΠΎΠ»Π½ΠΎΡ†Π΅Π½Π½Ρ‹ΠΌΠΈ прилоТСниями ΠΈΒ Π½Π°Π±Π»ΡŽΠ΄Π°Ρ‚ΡŒ tcpdump Π½Π°Β xdp-remote ΠΏΠΎΡ‚ΠΎΠΌΡƒ Ρ‡Ρ‚ΠΎ, Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€, hping3 Π½Π΅Β Ρ€Π΅Π°Π³ΠΈΡ€ΡƒΠ΅Ρ‚ Π½Π°Β Π½Π΅ΠΊΠΎΡ€Ρ€Π΅ΠΊΡ‚Π½Ρ‹Π΅ ΠΊΠΎΠ½Ρ‚Ρ€ΠΎΠ»ΡŒΠ½Ρ‹Π΅ суммы.

Π‘Β Ρ‚ΠΎΡ‡ΠΊΠΈ зрСния XDP сама ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ° Ρ‚Ρ€ΠΈΠ²ΠΈΠ°Π»ΡŒΠ½Π°. Алгоритм расчСта ΠΏΡ€ΠΈΠΌΠΈΡ‚ΠΈΠ²Π½Ρ‹ΠΉ ΠΈ, вСроятно, уязвимый для изощрСнного Π·Π»ΠΎΡƒΠΌΡ‹ΡˆΠ»Π΅Π½Π½ΠΈΠΊΠ°. Π―Π΄Ρ€ΠΎ Linux, Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€, ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ криптографичСский SipHash, Π½ΠΎΒ Π΅Π³ΠΎ рСализация для XDP явно Π²Ρ‹Ρ…ΠΎΠ΄ΠΈΡ‚ Π·Π°Β Ρ€Π°ΠΌΠΊΠΈ ΡΡ‚Π°Ρ‚ΡŒΠΈ.

Появилось для Π½ΠΎΠ²Ρ‹Ρ… TODO, связанных со внСшним взаимодСйствиСм:

  • XDP-ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΠ° Π½Π΅Β ΠΌΠΎΠΆΠ΅Ρ‚ Ρ…Ρ€Π°Π½ΠΈΡ‚ΡŒ cookie_seed (ΡΠ΅ΠΊΡ€Π΅Ρ‚Π½ΡƒΡŽ Ρ‡Π°ΡΡ‚ΡŒ соли) в глобальной ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½ΠΎΠΉ, Π½ΡƒΠΆΠ½ΠΎ Ρ…Ρ€Π°Π½ΠΈΠ»ΠΈΡ‰Π΅ в ядрС, Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ Π²Β ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΌ Π±ΡƒΠ΄Π΅Ρ‚ пСриодичСски ΠΎΠ±Π½ΠΎΠ²Π»ΡΡ‚ΡŒΡΡ ΠΈΠ·Β Π½Π°Π΄Π΅ΠΆΠ½ΠΎΠ³ΠΎ Π³Π΅Π½Π΅Ρ€Π°Ρ‚ΠΎΡ€Π°.

  • ΠŸΡ€ΠΈ совпадСнии SYN cookie Π²Β ACK-ΠΏΠ°ΠΊΠ΅Ρ‚Π΅ Π½ΡƒΠΆΠ½ΠΎ Π½Π΅Β ΠΏΠ΅Ρ‡Π°Ρ‚Π°Ρ‚ΡŒ сообщСниС, Π°Β Π·Π°ΠΏΠΎΠΌΠΈΠ½Π°Ρ‚ΡŒ IP ΠΏΡ€ΠΎΠ²Π΅Ρ€Π΅Π½Π½ΠΎΠ³ΠΎ ΠΊΠ»ΠΈΠ΅Π½Ρ‚Π°, Ρ‡Ρ‚ΠΎΠ±Ρ‹ Π΄Π°Π»Π΅Π΅ ΠΏΡ€ΠΎΠΏΡƒΡΠΊΠ°Ρ‚ΡŒ ΠΏΠ°ΠΊΠ΅Ρ‚Ρ‹ΠΎΡ‚Β Π½Π΅Π³ΠΎ.

ΠŸΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ° Π»Π΅Π³ΠΈΡ‚ΠΈΠΌΠ½Ρ‹ΠΌ ΠΊΠ»ΠΈΠ΅Π½Ρ‚ΠΎΠΌ:

$ sudoip netns exec xdp-test   nc -nv 192.0.2.1 6666
192.0.2.1 6666: Connection reset by peer

Π’Β Π»ΠΎΠ³Π°Ρ… зафиксировано ΠΏΡ€ΠΎΡ…ΠΎΠΆΠ΄Π΅Π½ΠΈΠ΅ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠΈ (flags=0x2Β β€” это SYN, flags=0x10Β β€” это ACK):

Ether(proto=0x800)
  IP(src=0x20e6e11a dst=0x20e6e11e proto=6)
    TCP(sport=50836 dport=6666 flags=0x2)
Ether(proto=0x800)
  IP(src=0xfe2cb11a dst=0xfe2cb11e proto=6)
    TCP(sport=50836 dport=6666 flags=0x10)
      cookie matches for client 20200c0

Пока Π½Π΅Ρ‚ списка ΠΏΡ€ΠΎΠ²Π΅Ρ€Π΅Π½Π½Ρ‹Ρ… IP, Π·Π°Ρ‰ΠΈΡ‚Ρ‹ от собствСнно SYN flood Π½Π΅Β Π±ΡƒΠ΄Π΅Ρ‚, Π½ΠΎΒ Π²ΠΎΡ‚ рСакция Π½Π°Β ACK flood, запускаСмый Ρ‚Π°ΠΊΠΎΠΉ ΠΊΠΎΠΌΠ°Π½Π΄ΠΎΠΉ:

sudo ip netns exec xdp-test   hping3 --flood -A -s 1111 -p 2222 192.0.2.1

Записи Π²Β Π»ΠΎΠ³Π΅:

Ether(proto=0x800)
  IP(src=0x15bd11a dst=0x15bd11e proto=6)
    TCP(sport=3236 dport=2222 flags=0x10)
      cookie mismatch

Π—Π°ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅

Иногда eBPF Π²ΠΎΠΎΠ±Ρ‰Π΅ ΠΈ XDP Π² частности прСдставляСтся скорСС как инструмСнт ΠΏΡ€ΠΎΠ΄Π²ΠΈΠ½ΡƒΡ‚ΠΎΠ³ΠΎ администратора, Π½Π΅ΠΆΠ΅Π»ΠΈΒ ΠΊΠ°ΠΊΒ ΠΏΠ»Π°Ρ‚Ρ„ΠΎΡ€ΠΌΠ° для разработки. Π”Π΅ΠΉΡΡ‚Π²ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎ, XDPΒ β€” инструмСнт Π²ΠΌΠ΅ΡˆΠ°Ρ‚Π΅Π»ΡŒΡΡ‚Π²Π° Π²Β ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΡƒ ΠΏΠ°ΠΊΠ΅Ρ‚ΠΎΠ² ядром, Π°Β Π½Π΅Β Π°Π»ΡŒΡ‚Π΅Ρ€Π½Π°Ρ‚ΠΈΠ²Π° ядСрному стСку, ΠΊΠ°ΠΊΒ DPDK ΠΈΒ ΠΏΡ€ΠΎΡ‡ΠΈΠ΅ Π²Π°Ρ€ΠΈΠ°Π½Ρ‚Ρ‹ kernel bypass. Π‘Β Π΄Ρ€ΡƒΠ³ΠΎΠΉ стороны, XDP позволяСт Ρ€Π΅Π°Π»ΠΈΠ·ΠΎΠ²Π°Ρ‚ΡŒ довольно ΡΠ»ΠΎΠΆΠ½ΡƒΡŽ Π»ΠΎΠ³ΠΈΠΊΡƒ, ΠΊΠΎΡ‚ΠΎΡ€ΡƒΡŽ, ΠΊΒ Ρ‚ΠΎΠΌΡƒΒ ΠΆΠ΅, Π»Π΅Π³ΠΊΠΎ ΠΎΠ±Π½ΠΎΠ²Π»ΡΡ‚ΡŒ Π±Π΅Π·Β ΠΏΠ°ΡƒΠ·Ρ‹ Π²Β ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ΅ Ρ‚Ρ€Π°Ρ„ΠΈΠΊΠ°. Π’Π΅Ρ€ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ нС создаСт Π±ΠΎΠ»ΡŒΡˆΠΈΡ… ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌ, Π»ΠΈΡ‡Π½ΠΎ я нС отказался бы ΠΎΡ‚Β Ρ‚Π°ΠΊΠΎΠ³ΠΎ для частСй userspace-ΠΊΠΎΠ΄Π°.

Π’ΠΎ Π²Ρ‚ΠΎΡ€ΠΎΠΉ части, Ссли Ρ‚Π΅ΠΌΠ° интСрСсна, Π΄ΠΎΠ΄Π΅Π»Π°Π΅ΠΌ Ρ‚Π°Π±Π»ΠΈΡ†Ρƒ ΠΏΡ€ΠΎΠ²Π΅Ρ€Π΅Π½Π½Ρ‹Ρ… ΠΊΠ»ΠΈΠ΅Π½Ρ‚ΠΎΠ² ΠΈ Ρ€Π°Π·Ρ€Ρ‹Π² соСдинСний, Π²Π½Π΅Π΄Ρ€ΠΈΠΌ счСтчики ΠΈ напишСм userspace-ΡƒΡ‚ΠΈΠ»ΠΈΡ‚Ρƒ для управлСния Ρ„ΠΈΠ»ΡŒΡ‚Ρ€ΠΎΠΌ.

Бсылки:

Π˜ΡΡ‚ΠΎΡ‡Π½ΠΈΠΊ: habr.com