0887 371 498 support@itservice-bg.net
RAW в RouterOS: notrack не спира веригата, а accept изключва anti-spoof-а
22.09.2026 · Самуил Арсов · MikroTik, Киберсигурност, Рутери

RAW в RouterOS: notrack не спира веригата, а accept изключва anti-spoof-а

На граничен рутер с няколко хиляди клиента connection tracking е най-скъпото нещо, което върви на всеки пакет. Затова на такива машини се ползва /ip firewall raw — веригата, която се изпълнява преди conntrack и може да каже „този пакет изобщо да не влиза в таблицата“.

Схемата работи. Но при преглед на една такава верига излязоха две неща, които не са описани ясно никъде: едно действие не спира обхождането, макар че изглежда като терминиращо, а друго спира — и с това тихо изключва защитата под него. Комбинацията прави anti-spoof правило, което броячите показват като работещо, а половината трафик изобщо не го докосва.

Трите действия в RAW и какво точно правят

Действие Какво прави Спира ли веригата
accept спира обхождането на raw и праща пакета в conntrack ДА
notrack маркира пакета да не влиза в conntrack НЕ
drop изхвърля пакета ДА

Първият ред е най-често подценяваният. accept в raw не значи „пусни го“ — значи „спри да четеш raw и track-вай този пакет“. Това е съвсем различно от accept във filter.

notrack не спира обхождането — и това се доказва с броячите

Типичната верига изглежда така: няколко изключения най-горе, после catch-all notrack, а под него — anti-spoof правилата.

0  accept  prerouting  src-address-list=nat-range
1  accept  prerouting  dst-address-list=nat-range
2  notrack prerouting                              <-- catch-all
3  notrack output
4  accept  prerouting  in-interface=ether2 src-address-list=allowed
5  accept  prerouting  in-interface=ether2 udp 68->67 src=0.0.0.0
6  drop    prerouting  in-interface=ether2

Ако notrack беше терминиращо, правила 4–6 щяха да са мъртъв код. Броячите казват друго:

Правило Пакети
2 — notrack (catch-all) 36 796 313 104
4 — accept (под него) 14 535 497 341
6 — drop (под него) 20 572 653

Правилата под catch-all-а броят. Значи notrack просто слага маркер и пуска пакета нататък по веригата.

Веригата работи. Но работи, защото notrack се държи по начин, на който никой не би трябвало да разчита. Ако някой размени реда или поведението се промени в бъдеща версия, anti-spoof-ът става мъртъв код без грешка и без ред в лога.

Истинският проблем: един accept отгоре изключва всичко отдолу

Ето защо правила 0 и 1 съществуват. Част от клиентите са зад NAT — да речем диапазонът 100.64.0.0/24, който излиза през един публичен адрес. NAT-ът изисква conntrack, затова този диапазон трябва да се изключи от catch-all notrack-а. Изключва се със списък nat-range и accept най-отгоре.

И точно тук се получава дупката. Списъкът nat-range съдържа цялата мрежа, а accept спира веригата. Значи:

пакет от 100.64.0.77
   |
правило 0: accept src-address-list=nat-range
           100.64.0.77 е в диапазона -> ACCEPT, СТОП
   |
правило 6 (drop) никога не се стига

Anti-spoof правилото на ред 6 пази публичните адреси на клиентите — те не са в nat-range, падат надолу и се проверяват коректно. Но целият NAT-нат диапазон го прескача. Хост от него може да е какъвто си иска — веригата не го пита нищо.

Въпросът се задава в грешен ред: „има ли нужда от conntrack?“ се пита преди „разрешен ли е изобщо този източник?“.

Как се вижда, че наистина се случва

Не по броячите — те показват само че правилата се изпълняват. Сравнете живите хостове на клиентския интерфейс със списъка с разрешени адреси, и после вижте дали „неразрешените“ имат живи сесии в conntrack:

# живите хостове
/ip arp print terse where interface=ether2

# разрешените
/ip firewall address-list print terse where list=allowed

# сесиите на един конкретен адрес
/ip firewall connection print count-only where src-address~"^100\.64\.0\.77:"

Капан при сравняването. Ако изнесете двата списъка и ги сравнявате с grep -Ff, ще получите надути числа — grep мачва като подниз, тъй че образецът 100.64.0.5 хваща и 100.64.0.50, .51 и така нататък. Ползвайте точно съвпадение: awk 'NR==FNR{s[$0]=1;next} ($0 in s)'.

В разгледания случай почти една четвърт от сесиите в conntrack бяха на хостове, които anti-spoof правилото би трябвало да е спряло.

Поправката: две неща наведнъж

1. Слейте whitelist-а и drop-а в едно правило

Класическата двойка „пусни разрешените, хвърли останалите“ са две правила:

accept  in-interface=ether2  src-address-list=allowed
drop    in-interface=ether2

RouterOS приема отрицание на address-list, така че това е едно правило:

/ip firewall raw add chain=prerouting action=drop \
    in-interface=ether2 src-address-list=!allowed

Това не е само козметика. Вариантът с accept има странични ефекти — при него разрешените пакети влизат в conntrack. Слятото правило е чист drop: разрешеният трафик просто не съвпада с него и продължава надолу до notrack-а, както трябва.

2. Вдигнете drop-а над изключенията за conntrack

0  accept   prerouting  in-interface=ether2 udp 68->67 src=0.0.0.0 dst=255.255.255.255
1  drop     prerouting  in-interface=ether2 src-address-list=!allowed
2  accept   prerouting  src-address-list=nat-range
3  accept   prerouting  dst-address-list=nat-range
4  notrack  prerouting
5  notrack  output

Сега редът е верен: първо „разрешен ли е“, после „нужен ли е conntrack“. И catch-all notrack-ът е последен — където му е мястото — тъй че веригата вече не зависи от това дали notrack спира обхождането.

DHCP капанът — заради него редът на нулевото правило не е по избор. DHCP Discover тръгва със src=0.0.0.0, а нулевият адрес не е в никакъв whitelist. Ако drop-ът иде най-отгоре, клиентите спират да си вземат адреси. И понеже lease-time обикновено е ден, няма да го забележите веднага — ще започне да капе чак на следващия ден, по един клиент.

Как се сменя работеща верига на жив рутер

Пренареждането на firewall верига в продукция е от нещата, при които между две команди мрежата може да остане без защита или клиентите без интернет. Последователност, при която веригата е валидна във всеки един момент:

  1. Експорт на веригата във файл на самия рутер — /ip firewall raw export file=raw-before.
  2. Новото правило влиза disabled=yes и веднага се проверява с print как точно го е записал рутерът. Така се хваща и дали синтаксисът с отрицание е приет.
  3. Пренареждане, докато правилото още е изключено. Старите правила продължават да пазят.
  4. Предпазна мрежа — scheduler, който след 3 минути изключва новото правило сам:
    /system scheduler add name=autorevert interval=3m \
      on-event="/ip firewall raw disable [find comment=\"new-rule\"]"
  5. Включване и мерене за 10–15 секунди. Доказателството не е че новият брояч расте, а че старият е замръзнал — значи новото правило хваща точно неговия трафик.
  6. Чак тогава се махат старите правила и се сваля предпазната мрежа.

Обратното — да изтриете веригата и да я напишете наново „за да е чисто“ — е лоша идея. Има реални случаи, в които за момент правилата изчезват съвсем.

Два капана на инструментите

move ляга с един слот встрани. /ip firewall raw move 7 destination=3 може да остави правилото на позиция 4. При три местения това се случи два пъти. Винаги print след всяко местене и корекция с ново изрично move.

Нов ред в on-event на scheduler изчезва мълчаливо. Ако подавате командата през SSH, \r\n може да се запише буквално като rn и скриптът става счупен — без грешка при добавянето. Пишете on-event с една-единствена команда или проверявайте какво е записано:

:put [/system scheduler get [find name=autorevert] on-event]

Колко CPU спестява всичко това

Нищо. И това си заслужава да се каже направо.

Прогнозата преди промяната беше 1–1,5 процентни пункта. Замерът след нея: категорията firewall в /tool profile беше 3,8–5,6% преди и 3,6–6,5% след — тоест изцяло в шума на профайлъра. Цялата raw верига струва 4–5% от процесора; дори да я изтриете напълно, толкова ще спечелите.

Ако търсите производителност на такава машина, гледайте другаде — при разгледания случай опашките се оказаха 11 пункта, измерени с контролен тест при непроменен трафик. RAW веригата не е там, където са парите.

Печалбата от тази промяна е друга и е по-важна: хостове, които мислите за спрени, наистина са спрени; conntrack таблицата олеква с изчистените паразитни сесии; и подредбата вече не виси на недокументирано поведение.

Накратко

  • accept в RAW е терминиращо и праща в conntrack. notrack не е терминиращо.
  • Всеки accept над drop правилото е дупка в anti-spoof-а с размера на своя address-list.
  • Питайте „разрешен ли е?“ преди „нужен ли е conntrack?“.
  • Whitelist + drop се сливат в едно правило с src-address-list=!списък — и така разрешеният трафик не влиза в conntrack без нужда.
  • DHCP-то (src=0.0.0.0) се изключва над drop-а, иначе чупите lease-ите бавно и незабележимо.
  • Catch-all notrack стои последен.