Шейпер для Linux в користувацькому просторі (NFQUEUE-based)

Часи вузьких інтернет-каналів поступово відходять у минуле, але іноді ще буває потрібно шейпить мережевий трафік. В Linux для цього є відповідні механізми ядра і утиліти для керування механізмами. Все це господарство досить складно влаштоване, зазвичай осягнення шейпінгу займає не один день. Хоча, в простих випадках можна накопипастить заклинання tc зі статей або знайти скрипт, який ці заклинання генерує.

Як допитливій людині, завжди було цікаво, чи можна зробити процес налаштування шейпінгу для невеликих мереж простіше? Можна хоча б грубо детектувати важливий трафік і відділяти його від неважливого без DPI і сигнатурного аналізу? Можна шейпить трафік в будь-яких напрямках без створення псевдо-інтерфейсів або додавання модулів в ядро? І ось, після деяких роздумів і гуглежа, вирішив написати простий шейпер в userspace. Щоб спробувати відповісти на питання експериментом.

В результаті вийшла ось така штука github.com/vmxdev/damper

Працює штука приблизно так:

При старті створюються два програмних потоку. У першому NFQUEUE перехоплює пакети, вони аналізуються, кожного пакету призначається «вага» (або пріоритет) і він зберігається в черги з пріоритетами. Коли черга заповнена, пакети з низьким пріоритетом затираються высокоприоритетными. Інший потік вибирає пакети з найбільшою вагою і позначає їх до відправлення. Відправка відбувається з обмеженою швидкістю, з-за цього власне і виходить шейпирование.

Невеликий відступ про механізм NFQUEUE
Цей механізм дозволяє копіювати мережеві пакети в користувальницьке простір для обробки. Додаток, яке слухає відповідну чергу, має винести вердикт щодо пакету (пропустити або відкинути). Крім цього допускається зміна пакета. Цим механізмом користуються IDS/IPS типу Snort або Suricata. Пакети використовуються для обробки в iptables, мета NFQUEUE. Тобто ми можемо вибрати будь-який напрям (вхідний трафік, що виходить, транзитний, або, скажімо, UDP з порту 666 на порт 13) та направити його в шейпер. Там пакети будуть аналізуватися, можливо змінювати свій порядок, а при перевищенні ліміту самі низкоприоритетные будуть відкидатися.

Перші робочі версії шейпера захоплювали пакети, поміщали їх в чергу і потім ре-инъектировали в сирій (raw) сокет. На StackOverflow і деяких інших статтях пишуть що це єдиний спосіб затримувати пакети перед перепосылкой. Конструкція працювала, але товариш Vel роз'яснив, де можна затримувати пакет прямо в NFQUEUE, налаштування шейпера спростилася.

Модулі
Так як шейпер експериментальний, я зробив його не монолітним, а «модульним». В кожному модулі обчислюється вага пакета за різними критеріями. З коробки є 4 модуля, але можна легко написати ще який-небудь. Модулі можна використовувати разом, можна окремо.

Модулі:

  • inhibit_big_flows — пригнічує великі потоки. Чим більше передано байт між двома IP-адресами, тим меншим стає «вага» пакету. Тобто підвищується ймовірність того, що пакет з великою важкою сесії буде відкинутий. Інформація зберігається в кільцевому буфері, так що час від часу настає амністія (кількість досліджуваних сесій задається в конфіги), потоки заміщуються більш свіжими

  • bymark — вага пакетів задається по марці. iptables-му виставляється марка по якимось критеріям, для цих пакетів буде застосовуватися коефіцієнт, зазначений в конфіги. Можна вручну підняти або знизити пріоритет якогось отмаркированного трафіку

  • entropy — вага вважається залежно від ентропії (точніше, міру ентропії за Шенноном) потоку. Потік ідентифікується номером протоколу та адресами учасників. Для TCP/UDP враховується ще і порт джерела і призначення. Чим вище ентропія, тим менше вага. Тобто мультимедіа, шифрований, стиснене трафік відкидається з більшою ймовірністю, ніж інші.

  • І дуже примітивний модуль random — просто додає випадковості в процес відкидання і переупорядковування пакетів (якщо використовувати тільки цей модуль, вийде класичний RED).
Ваги пакетів, виміряні в кожному модулі, множаться на коефіцієнт(кожному модулю можна задати свій) і складаються. Виходить результуючий вагу.

Статистика та графіки
Шейпер вміє вести статистику і малювати графіки. Вживу це виглядає так: damper.xenoeye.com. Зелений — скільки пропущено байт/пакетів, червоний — скільки відкинуто. Графік можна позумить/поскроллить.

Другий графік (включається директивою «wchart yes» в конфіги) — середні ваги пакетів за секунду, отнормированные, з розбивкою по модулям.

Демо працює на не дуже швидких ARMах (Scaleway bare metal, 32-бітні ARMv7), іноді може злегка залипати.

Налагодження
У модулів inhibit_big_flows і entropy є налагоджувальний режим, включається в конфіги. У цьому режимі модулі кожні N секунд роблять дамп поточних потоків з вагами.

Крім цього є режим без обмеження швидкості («no limit» в конфіги). У цьому режимі всі пакети пропускаються (без аналізу в модулях), але можна вести статистику прошедних пакетів/байт, медитувати на графік завантаження каналу, наприклад.

Результати
Шейпер вийшов досить простий (ну, на мій, замилений, погляд). Для використання потрібно вибрати напрям за яким шейпить, додати правило iptables, виставити в конфіги потрібну швидкість і запустити.

Важкі і высокоэнтропийные сесії визначаються і знижуються в пріоритеті, але чудес не буває: якщо дуже вузький канал, комфортного серфінгу не вийде.

На великих швидкостях і великій кількості користувачів я його не тестував, але на десятки мегабіт і з декількома користувачами суб'єктивно виходить краще ніж без шейпера. Хоча він вантажить CPU, але це не тільки від використання NFQUEUE, а ще й від загальної корявости коду (і трохи від особливостей clock_nanosleep()), можна отпимизировать і оптимізувати.

Це, звичайно, тільки proof of concepts, код місцями сумбурний і практично не причесывался.

Якщо у когось є думки, побажання та пропозиції з приводу, було б цікаво почитати.
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.