Початківцям для управління шириною каналу на Linux

Якийсь час тому мене попросили налаштувати у віддаленій філії найпростіше балансування трафіку. Працюють вони, бідолахи, через ADSL, і надсилання електронних листів великого обсягу (скани документів) забиває їм весь зворотний канал, що призводить до проблем роботи з офісними онлайн-програмами через VPN. В якості шлюзу вони використовують Linux (Fedora). До цього я пару разів бачив, як подібне балансування налаштовується через ipfw на FreeBSD, а тому що знаю механізм iptables досить добре, не очікував особливих проблем. Але пошукавши в Інтернеті, я був неприємно вражений тим, що iptables мені тут зовсім не помічник. І знання про порядок проходження пакетів через його таблиці та правила мені майже не знадобляться. Потрібно вивчати tc із пакету iproute2.

Безкласові дисципліни

Отже, в Linux для управління трафіком кожному мережевому інтерфейсу призначається дисципліна (qdisc). Саме з дисциплін і будується вся система керування трафіком. Але лякатися не варто насправді дисципліна — це просто алгоритм обробки черги мережевих пакетів. Дисциплін однією інтерфейсі може бути задіяно кілька, а безпосередньо до інтерфейсу кріпиться так званакоренева дисципліна (root qdisc). У цьому кожен інтерфейс має власну кореневу дисципліну.

За замовчуванням після завантаження системи root qdisc задає алгоритм обробки пакетів типуpfifo_fast.

початківцям

qdisc pfifo_fast 0: dev eth0 root bands 3 priomap 1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1

pfifo_fast- це звичайний алгоритм First Input - First Output, але з деякою пріоритизацією трафіку. Дисципліна такого типу містить у собі три черги FIFO з різним пріоритетом обробки пакетів. Пакети розкладаються на основі прапора ToS (Type ofService) у кожному IP-пакеті. Пакет, що потрапив у FIFO0, має найвищий пріоритет до обробки, у FIFO2 — найменший. Сам ToS вимагає окремої розмови, тому пропоную обмежитися тим фактом, що операційна система сама знає, який ToS призначити відправленому IP-пакету. Наприклад, у пакетах telnet та ping ToS буде мати різні значення.

0:- дескриптор кореневої дисципліни. Дескриптори повинні мати виглядстарший_номер: молодший_номер, але у дисциплін молодший номер завжди повинен бути 0, і тому його можна опускати.

Параметрpriomap 1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1 1, якраз задає побітову відповідність поля ТоS кожної внутрішньої черги (band). Наприклад, при ToS=4 пакет обробляється в черзі 1, при ToS=7 в черзі 0.

У ряді джерел зазначено, що параметри дисципліни pfifo_fast не можна змінити, повіримо.

Тепер розглянемо як можна обмежити швидкість загального вихідного трафіку. Для цього призначимо кореневою дисципліною інтерфейсу дисципліну типуTBF (Token Bucket Filter).

# tc qdisc add dev eth0 root tbf rate 180kbit latency 20ms buffer 1540

rate 180kbit- встановлює поріг швидкості передачі на інтерфейсі.

latency 20ms- задає максимальний час знаходження пакета даних в очікуванні токена.

buffer 1540- задаємо розмір буфера токенів у байтах. У прикладах пишуть, що для обмеження в 10Мbit/s достатньо буфера на 10Kbytes. Головне не зробити його надто малого розміру, більше можна. Орієнтовна формула розрахунку: rate_in_Bytes/100.

каналу

Дисципліна ТBF для роботи використовує механізм токенів. Токени генеруються системою з постійною швидкістю та поміщаються в буфер (bucket). За кожен токен, що вийшов з буфера з інтерфейсу, йде IP-пакет. Якщо швидкості передачі пакетів та генерації токенів збігається, процес передачі даних йде без затримки. Якщо швидкість передачі пакетів менша ніж швидкість токенів, останні починають накопичуватися в буфері і потім можуть використовуватися для короткочасної передачі даних на швидкості вище граничної. Якщо швидкість передачі пакетів вища — токенів починає бракувати. Пакети даних очікують на нові токени деякий час, а потім починають відкидатися.

Описані дві дисципліни відносяться до так званих безкласових дисциплін. Вони мають ряд функціональних обмежень: підключаються тільки до інтерфейсу (або крайового класу), плюс їм не можна використовувати фільтри пакетів. І відповідно моє завдання щодо балансування поштового трафіку з їх допомогою вирішити не вдасться. До речі, повний набір безкласових дисциплін дещо ширший: pfifo, bfifo, sqf (забезпечує однакову швидкість пакетів, що надійшли з різних потоків), esqf і т.д.

Класові дисципліни

Якщо дисципліни можна як відрізки водопровідних труб, то класи — це з'єднувачі (фітинги). Це може бути простий фітінг-перехідник, а може хитрий фітінг-розгалужувач із десятком відводів.

Як батько класу може виступати або дисципліна, або інший клас. До класу можуть приєднуватися дочірні класи (стикування кількох фітингів).

Клас, що не має дочірніх класів, називаєтьсякраєвим (leaf class). Тут пакети даних, пробігши по нашому "водопроводу" залишають систему керування трафіком і відправляються мережевим інтерфейсом. За замовчуванням будь-який крайовий клас має приєднану дисципліну типу fifo, і саме вона визначає порядок передачі пакетів для цього класу. Але вся краса в тому, що ми можемо поміняти цю дисципліну на будь-якуіншу. У разі додавання дочірнього класу ця дисципліна видаляється.

Повернемося до завдання з балансування поштового трафіку та розглянемо класову дисциплінуprio. Вона дуже схожа на вже описану pfifo_fast. Але ця дисципліна особлива тим, що при її призначенні автоматично створюється три класи (кількість можна змінювати параметром bands).

Замінимо кореневу дисципліну інтерфейсу на prio.

# tc qdisc add dev eth0 root handle 1: prio

handle 1:- задаємо дескриптор даної root qdisc. У класових дисциплінах його вказують при підключенні класів.

qdisc prio 1: dev eth0 bands 3 priomap 1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1

# tc -d -s class show dev eth0

class prio 1:1 parent 1: Sent 734914 bytes 7875 pkt (dropped 0, overlimits 0 requeues 0) backlog 0b 0p requeues 0 class prio 1:2 parent 1: Sent 15550 8280199 pkt (dropped 124919, overlimits 26443 requeues 0) backlog 0b 0p requeues 0 class prio 1:3 parent 1: Sent 57934378 bytes 802213 p40 0) backlog 0b 0p requeues 0

Бачимо три класи з ідентифікаторами 1:1, 1:2 та 1:3, підключені до батьківської дисципліни 1: типу prio (класи повинні мати загальний старший номер ідентифікатора зі своїм батьком). Тобто на “трубу” root qdisc, яка поділяє потоки даних також як це робить pfifo_fast ми насадили трійник. Керуючись ToS, до класу 1:1 потрапляє високопріоритетний трафік, до класу 1:2 звичайний трафік, до класу 1:3 зовсім «сміття».

Допустимо зворотний канал ADSL видає швидкість 90Кбайт/c. Розділимо його на 20Kбайт/c під пошту та 70Kбайт/c на все інше.

Трафік із класу 1:1 спеціально обмежувати не будемо. Його пакети завжди зможуть зайнятихоч всю ширину каналу через високий пріоритет ToS, але обсяг трафіку в цьому класі у нас буде мізерно малий у порівнянні з рештою двох класів. Тому окремої смуги під нього не резервуємо.

Стандартний трафік, як правило, потрапляє до класу 1:2. Підключаємо до другого висновку класу-трійника трубу-дисципліну на 70Kбайт/c:

# tc qdisc add dev eth0 parent 1:2 handle 10: tfb rate 70kbps buffer 1500 latency 50ms

На третій висновок трійника підключимо трубу-дисципліну на 20Kбайт/c:

# tc qdisc add dev eth0 parent 1:3 handle 20: tfb rate 20kbps buffer 1500 latency 50ms

Усі три цих класи крайові.

І тепер залишилося лише направити поштовий трафік не до класу 1:2, як відбувалося раніше, а до класу 1:3. Це робиться за допомогою фільтрів класових дисциплін.

# tc filter add dev eth0 parent 1: protocol ip prio 1 u32 match ip dport 25 0xffff flowid 1:3

parent 1:- фільтр може кріпитися тільки до дисципліни і викликається з неї. На основі спрацьовування фільтра дисципліна вирішує, в якому класі продовжиться обробка пакета.

protocol ip- визначаємо тип мережного протоколу

prio 1— параметр довго вводив мене в замішання, тому що він застосовується в класах та фільтрах, плюс це назва дисципліни. Тут prio задає пріоритет спрацьовування фільтрів, першими задіяні фільтри з меншим prio.

match ip dport 25 0xffff— задає спрацьовування фільтра під час відправлення пакетів на порт 25. 0xffff — це бітова маска для порту.

flowid 1:3- вказуємо в який клас передаються пакети при спрацьовуванні цього фільтра.

Зроблено брутально, але завдання виконає.

Дивимося статистику проходження пакетів:

# tc -s -d qdisq show dev eth0 # tc -s -d class show dev eth0 # tc -s -d filter show dev eth0

Швидко видалити всі класи, фільтри та повернути root qdisc інтерфейсу в початковий стан можна командою:

# tc qdisc del dev eth0 root

З іншого боку, у нас і так занадто тонкий зворотний канал, щоб резервувати 20Kбайт/c лише під надсилання електронної пошти. Тому краще використовувати класову дисципліну HTB (Hierarchical Token Bucket). Вона дозволяє проводити запозичення смуги пропускання дочірнім класами у батьківського.

управління

# tc qdics add dev eth0 root handle 1: htb default 20

default 20- задаємо клас за замовчуванням. У ньому будуть оброблятися пакети, які не потрапили до інших класів дисципліни htb. Якщо його не вказати, то буде призначено "default 0" і весь некласифікований трапа, що не потрапив під фільтри, буде відправлятися зі швидкістю інтерфейсу.

# tc class add dev eth0 parent 1: classid 1:1 htb rate 90kbps ceil 90kbps

прикріплюємо до root qdisc клас із ідентифікатором 1:1. Тим самим обмежуємо швидкість на інтерфейсі до 90Кбайт/c.

classid 1:1- ідентифікатор класу.

rate 90kbps- встановлюємо нижній поріг пропускної здатності для класу.

ceil 90kbps- встановлюємо верхній поріг пропускної спроможності для класу.

# tc class add dev eth0 parent 1:1 classid 1:10 htb rate 20kbps ceil 70kbps

створюємо клас 1:10, дочірній класу 1:1. Потім у нього фільтром надсилатиметься вихідний поштовий трафік.

rate 20kbps- встановлюємо гарантований нижній поріг пропускної здатності для класу.

ceil 70kbps- встановлюємо верхній поріг пропускної спроможності для класу. Якщо у батьківськогокласу буде вільна смуга пропускання (наявність "зайвих" токенів), class 1:10 зможе тимчасово підняти швидкість передачі даних, аж до вказаної межі 70Кбайт/c.

# tc class add dev eth0 parent 1:1 classid 1:20 htb rate 70kbps ceil 90kbps

Створюємо клас за замовчуванням. До нього потраплятиме решта трафіку. Так само, параметрами rate і ceil, задаємо розширення пропускної спроможності у разі відсутності поштового трафіку.

# tc filter add dev eth0 parent 1: protocol ip prio 1 u32 match ip dport 25 0xffff flowid 1:10

фільтр на базі u32, що направляє пакети, що виходять на 25-й порт у клас 1:10.

До речі, у документації зазначено, що за фактом у HTB шейпінг трафіку відбувається лише у крайових класах, у нашому випадку 1:10 та 1:20. Вказівка ​​параметрів обмеження смуги пропускання інших класах HTB необхідно лише функціонування системи запозичення між класами.

При додаванні класу також можна вказати параметрprio. Він задає пріоритет класу (0 - макс.пріоритет). Класи з меншим пріоритетом не обробляються, поки є дані в більш пріоритетних класах.

Хардкорна конфа за С++. Ми запрошуємо лише профі.