Linux multihomed — отправка ответов на входящие пакеты по тому же интерфейсу, по какому пришли
Invite pending
Тем, кто сломал мозг в попытках отправить ответный пакет по тому же интерфейсу, откуда пришёл запрос, посвящается.
Думаю, на сегодня достаточно стандартная ситуация, когда на входе в локальную сеть стоит Linux-роутер, к которому подключены несколько внешних линий от разных провайдеров.
Как управляться с такой конфигурацией, писали много, например вот здесь.
Но когда берёшь реальный линукс и начинаешь строить, немедленно наступаешь на грабли.
Повторять все шаги из статьи по ссылке не буду, опишу только участвующие в «граблях».
А именно (далее все примеры в предположении, что основной внешний интерфейс eth0, а запасной — ppp0):
Чтобы ответы на пакеты уходили по тому же интерфейсу, по какому пришёл запрос, везде советуют включать маркировку пакетов
и далее назначать таблицу маршрутизации (с именем modem в файле /etc/iproute2/rt_tables) согласно меткам:
И вроде бы всё правильно, но — НЕ РАБОТАЕТ! Проверяешь с внешнего хоста пингом на адрес внешнего интерфейса — просто не увеличиваются счётчики на правиле iptables в таблице mangle, цепочке INPUT — то есть пакет туда НЕ ПОПАДАЕТ.
Начинаешь экспериментировать — выясняется, что в цепочку PREROUTING таблицы mangle пакеты попадают, а в цепочку INPUT уже нет. Продолжение экспериментов показывает, что и в цепочку PREROUTING таблицы nat пакеты попадают, а ни в INPUT ни в FORWARD — нет, значит согласно схеме прохождения цепочек пакеты теряются на первом узле «routing decision», где принимается решение куда отправить пакет — в FORWARD или в INPUT.
Так вот, многочасовое разбирательство, включая попытку влезть в исходники iptables и iproute, привели к поразительно простому открытию, которое почему-то почти нигде не упоминается: надо на входящих интерфейсах выключить rp_filter:
После чего всё немедленно начинает работать.
Можно выключить этот фильтр и через соответствующий параметр в /etc/sysctl.conf, но там надо быть уверенным, что интерфейс, для которого устанавливаете параметр, уже поднят.
Удачи на интерфейсах!
Думаю, на сегодня достаточно стандартная ситуация, когда на входе в локальную сеть стоит Linux-роутер, к которому подключены несколько внешних линий от разных провайдеров.
Как управляться с такой конфигурацией, писали много, например вот здесь.
Но когда берёшь реальный линукс и начинаешь строить, немедленно наступаешь на грабли.
Повторять все шаги из статьи по ссылке не буду, опишу только участвующие в «граблях».
А именно (далее все примеры в предположении, что основной внешний интерфейс eth0, а запасной — ppp0):
Чтобы ответы на пакеты уходили по тому же интерфейсу, по какому пришёл запрос, везде советуют включать маркировку пакетов
iptables -t mangle -I INPUT -i ppp0 -j CONNMARK --set-mark 0x1
iptables -t mangle -I OUTPUT -j CONNMARK --restore-mark
и далее назначать таблицу маршрутизации (с именем modem в файле /etc/iproute2/rt_tables) согласно меткам:
ip route add default dev ppp0 table modem
ip rule add fwmark 0x1 lookup modem
И вроде бы всё правильно, но — НЕ РАБОТАЕТ! Проверяешь с внешнего хоста пингом на адрес внешнего интерфейса — просто не увеличиваются счётчики на правиле iptables в таблице mangle, цепочке INPUT — то есть пакет туда НЕ ПОПАДАЕТ.
Начинаешь экспериментировать — выясняется, что в цепочку PREROUTING таблицы mangle пакеты попадают, а в цепочку INPUT уже нет. Продолжение экспериментов показывает, что и в цепочку PREROUTING таблицы nat пакеты попадают, а ни в INPUT ни в FORWARD — нет, значит согласно схеме прохождения цепочек пакеты теряются на первом узле «routing decision», где принимается решение куда отправить пакет — в FORWARD или в INPUT.
Так вот, многочасовое разбирательство, включая попытку влезть в исходники iptables и iproute, привели к поразительно простому открытию, которое почему-то почти нигде не упоминается: надо на входящих интерфейсах выключить rp_filter:
# prevent incoming packets on masqueraded connections from being dropped
# as "martians" due to the destination address being translated before the
# rp_filter check is performed
echo 0 > /proc/sys/net/ipv4/conf/ppp0/rp_filter
После чего всё немедленно начинает работать.
Можно выключить этот фильтр и через соответствующий параметр в /etc/sysctl.conf, но там надо быть уверенным, что интерфейс, для которого устанавливаете параметр, уже поднят.
Удачи на интерфейсах!