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

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

Рутерът, за който става дума, не е граничният. Стои веднага след BGP рутера, а зад него има около 600 клиента. Докато трафикът е малък, никой не гледа как са подредени firewall правилата. Трафикът обаче расте. Идва момент, в който рутерът прекарва 2 Gbps в пиковите часове и ресурсите му започват да не стигат. Тогава почваш да търсиш откъде да спестиш и първото, за което се сещаш, са правилата.

Най-скъпото нещо, през което минава всеки пакет, е connection tracking. Трафикът на клиентите с публични адреси само се рутира, без NAT, и рутерът няма никаква нужда да помни връзките им. Ако ги помни, всяка връзка става запис в conntrack таблицата. Това е памет, а за всеки пакет има и търсене в таблицата. При 2 Gbps се събират много записи, които на никого не трябват.

Затова публичните адреси минават с notrack в /ip firewall raw. Тази верига се изпълнява преди conntrack и в нея може да се каже „този пакет изобщо не го записвай в таблицата“. В таблицата остават само клиентите зад NAT, защото без conntrack NAT не работи.

Когато седнах да прегледам raw веригата ред по ред, изскочиха две неща, които не съм виждал ясно описани никъде. Първото: notrack изглежда като действие, след което с пакета е приключено, а всъщност не е. Второто: accept наистина приключва, и точно затова тихо изключва всичко, което стои под него. Двете заедно дават anti-spoof правило, което по броячите изглежда живо, а почти една четвърт от сесиите минават покрай него.

Трите действия в RAW

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

На първия ред се спъват най-много хора. accept в raw не значи „пусни го“. Значи „спри да четеш raw и прати пакета в conntrack“. С accept във filter общото им е само името.

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

Обикновено веригата изглежда горе-долу така: отгоре няколко изключения, после 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 само слага маркер и пуска пакета надолу по веригата.

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

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

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

Тук е дупката. В nat-range е цялата мрежа, а accept спира веригата. Пакетът стига до правило 0 и дотам:

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

Публичните адреси на клиентите не са в nat-range. Те падат надолу и anti-spoof правилото на ред 6 ги проверява както трябва. Целият 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-ът е най-отдолу, така че вече няма значение дали спира обхождането или не.

Нулевото правило не е там за красота. 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. Едва тогава махате старите правила и scheduler-а.

Изкушението е да изтриете всичко и да напишете веригата наново, „за да е чисто“. Не го правете. Между изтриването и новото добавяне има момент, в който правилата ги няма изобщо.

Два капана по пътя

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

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

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

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

Нищо.

Преди промяната очаквахме 1–1,5 процентни пункта. След нея категорията firewall в /tool profile беше 3,6–6,5%, при 3,8–5,6% преди — чист шум на профайлъра. Цялата 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 стои последен.