Прозрачный сервис локальной сети в Linux
Мне нужно реализовать службу прозрачной локальной сети на основе VLAN в Linux. Это означает, что мне нужно взять настроенную VLAN и перенаправить ее непосредственно на указанный порт (все широковещательные / многоадресные и одноадресные пакеты).
Тривиальным решением будет определение моста 1: 1 между интерфейсом VLAN и указанным портом. Недостатком этого решения является то, что мне стало известно обо всех mac-адресах в этом туннеле. Я работаю на встроенном устройстве с ограниченной таблицей Mac и хочу избежать загрязнения таблицы Mac устройствами из подключаемых сетей.
Я пытался найти способ использовать ebtables для этой задачи, но кажется, что -o опции ebtables полезны только в цепочке FORWARD, которая происходит после изучения Mac. Мне нужна цепочка BROUTING, но кажется, что я не могу заставить пакет выходить на конкретный интерфейс с этой точки.
Итак, ebtables кажется тупиком. Есть еще варианты? В идеальном мире я бы предпочел иметь службу TLS, основанную на любом ключе, и не только VLAN, но VLAN пока подойдет.
Спасибо, Илья.
1 ответ
ОБНОВЛЕНИЕ: добавлено решение, все еще использующее мост. В любом случае, для VLAN можно использовать мост Linux для его возможностей фильтрации VLAN и полностью отключить обучение MAC. tc
ниже может все еще быть полезным для его общего способа сопоставления селекторов (вероятно, было бы проще использовать tc с адекватным соответствием для чего-то другого, кроме VLAN в качестве селектора, чем использовать мост без кода для его обработки).
мост с отключенным обучением MAC на каждом порту
Можно отключить изучение MAC-адресов. Это сделано с bridge link
команда. Затем можно настроить мост для фильтрации VLAN (используя также bridge vlan
): ему не нужен MAC-адрес, вся его пересылка будет выполняться на основе настроенных настроек VLAN.
учиться или учиться
Управляет тем, будет ли данный порт узнавать MAC-адреса из полученного трафика или нет. Если вы узнаете, что отключено, мост в конечном итоге будет затоплять любой трафик, для которого у него нет записи FDB. По умолчанию этот флаг включен.
learning_sync включен или learning_sync выключен
Управляет тем, будет ли данный порт синхронизировать MAC-адреса, полученные на порте устройства, с мостом FDB.
Так, например, давайте рассмотрим систему с интерфейсом eth0 как транк с помеченными кадрами и eth1 eth2 eth3 соответственно. для идентификаторов VLAN 10, 20 и 30, без тегов. Это будет сделано с:
ip link add name br0 type bridge vlan_filtering 1
#remove implicit bridge's self port br0 from any interaction.
# Might have to not be done if using an IP on the bridge
# but more configuration might then be needed anyway.
bridge vlan del vid 1 dev br0 self
bridge link set dev br0 learning off learning_sync off self
for $nic in eth0 eth1 eth2 eth3; do
ip link set dev $nic master br0
bridge link set dev $nic learning off learning_sync off
bridge vlan del vid 1 dev $nic
done
ip link set br0 up
bridge vlan add 10 dev eth0
bridge vlan add 20 dev eth0
bridge vlan add 30 dev eth0
bridge vlan add vid 10 pvid 10 untagged dev eth1
bridge vlan add vid 20 pvid 20 untagged dev eth2
bridge vlan add vid 30 pvid 30 untagged dev eth3
Чтобы проверить, как ведут себя эти разные настройки, просто замените следующие строки в скрипте установки в конце (которые используют метод tc, описанный в следующей части ответа):
ip netns exec fakebridge tc qdisc add dev trunk0 ingress
for vlan in 10 20 30; do
ip netns exec fakebridge tc qdisc add dev vlan$vlan ingress
ip netns exec fakebridge tc filter add dev vlan$vlan parent ffff: matchall action vlan push id $vlan action mirred egress redirect dev trunk0
ip netns exec fakebridge tc filter add dev trunk0 parent ffff: basic match "meta(vlan mask 0xfff eq $vlan)" action vlan pop action mirred egress redirect dev vlan$vlan
done
с ними вместо этого (это уже не поддельный мост, но в любом случае...):
ip -n fakebridge link add name br0 type bridge vlan_filtering 1
ip netns exec fakebridge bridge vlan del vid 1 dev br0 self #remove implicit bridge's self port br0 from any interaction
ip -n fakebridge link set dev trunk0 master br0
ip netns exec fakebridge bridge vlan del vid 1 dev trunk0
ip netns exec fakebridge bridge link set dev trunk0 learning off learning_sync off
for vlan in 10 20 30; do
ip -n fakebridge link set dev vlan$vlan master br0
ip netns exec fakebridge bridge link set dev vlan$vlan learning off learning_sync off
ip netns exec fakebridge bridge vlan add vid $vlan dev trunk0
ip netns exec fakebridge bridge vlan del vid 1 dev vlan$vlan
ip netns exec fakebridge bridge vlan add vid $vlan pvid $vlan untagged dev vlan$vlan
done
ip -n fakebridge link set br0 up
Также возможно вообще не использовать мост и работать с VLAN ID для операций, используя...
тс (управление движением)
tc может манипулировать VLAN напрямую, используя tc vlan:
ОПИСАНИЕ
Действие vlan позволяет выполнить энкапсуляцию 802.1Q для пакета, отраженную в режимах работы POP, PUSH и MODIFY. Режим POP прост, поскольку никакой дополнительной информации не требуется, чтобы просто отбросить инкапсуляцию самой внешней VLAN. Режимы PUSH и MODIFY требуют как минимум VLANID и позволяют при желании выбрать VLANPROTO для использования.
Наряду с другими функциями tc:
- matchall (можно заменить на
u32 match u32 0 0
на более старых ядрах), чтобы соответствовать безоговорочно пакетам, - basic + ematch для сопоставления с метаинформацией, такой как vlan id (этот SF Q/A помог: tc u32 - как сопоставить протоколы L2 в последних ядрах?),
- зеркальный, чтобы на самом деле перемещать пакеты между интерфейсами без мостов и маршрутизации.
и обычное подключение (есть qdisc, присоединить фильтр с действием), можно перемещать пакеты с одного интерфейса на другой, инкапсулируя или декапсулируя идентификатор VLAN 802.1Q. Система не будет соединять или маршрутизировать эти пакеты. Системе не придется запоминать MAC-адрес или манипулировать IP-адресом, ее осведомленность о пакетах и протоколах будет ограничена тем, что делается с tc
,
Обратите внимание, что это доказательство концепции. Конечно, реальной системе все равно придется обмениваться данными, используя IP, стараясь не мешать этим настройкам. Правильная реализация этого для производства, вероятно, сопряжена с непредвиденными дополнительными трудностями, учитывая, что tc является сложным инструментом. Также могут быть и другие лучшие способы, доступные с tc, для более общей обработки (думая о потоке tc, чтобы использовать идентификатор VLAN в качестве ключа для отображения в classid, который можно было бы использовать более обобщенно, или, возможно, можно использовать что-то другое в качестве ключа помимо VLAN, если есть способ инкапсуляции / дешифрования.)
Так, например, давайте рассмотрим систему с интерфейсом eth0 как транк с помеченными кадрами и eth1 eth2 eth3 соответственно. для идентификаторов VLAN 10, 20 и 30, без тегов. Разрешение помеченной стороне взаимодействовать с правильной неотмеченной стороной, и обратное будет сделано с помощью:
tc qdisc add dev eth0 handle ffff: ingress
tc qdisc add dev eth1 handle ffff: ingress
tc qdisc add dev eth2 handle ffff: ingress
tc qdisc add dev eth3 handle ffff: ingress
tc filter add dev eth0 parent ffff: basic match "meta(vlan mask 0xfff eq 10)" action vlan pop action mirred egress redirect dev eth1
tc filter add dev eth0 parent ffff: basic match "meta(vlan mask 0xfff eq 20)" action vlan pop action mirred egress redirect dev eth2
tc filter add dev eth0 parent ffff: basic match "meta(vlan mask 0xfff eq 30)" action vlan pop action mirred egress redirect dev eth3
tc filter add dev eth1 parent ffff: matchall action vlan push id 10 action mirred egress redirect dev eth0
tc filter add dev eth2 parent ffff: matchall action vlan push id 20 action mirred egress redirect dev eth0
tc filter add dev eth3 parent ffff: matchall action vlan push id 30 action mirred egress redirect dev eth0
Может показаться логичным, что фактические интерфейсы должны быть переведены в смешанный режим, чтобы фактически перенаправить трафик, но это не было необходимо с ядрами 5.0.x и интерфейсами veth во время тестирования в любом случае.
макет с использованием ip netns для сетевых пространств имен
Я провел несколько экспериментов по реализации поддельного моста с одним интерфейсом магистрали с тегами и несколькими интерфейсами без тегов vlan с использованием сетевых пространств имен. Каждый "хост" имеет свое собственное пространство имен и связан с другими хостами с помощью сетевых элементов, которые сами реализованы с другими сетевыми пространствами имен, которые включают в себя мост. Фактическая система, имитирующая то, что может сделать ваше встроенное устройство, будет называться fake bridge, поскольку она может выглядеть аналогично мосту с поддержкой VLAN.
отмеченные как нетегированные _______ .| Host10b| +-------+ . ======= +------+ | |....vlan10....|host10| | |....... багажник......| подделка | ====== | Маршрутизатор |..................| |....vlan20....|host20| | | (vlans 10+20+30) | мост | ====== +------+ | |....vlan30....|host30| +-------+ ------
Таким образом, 1+1+4 = 6 хостов, 1 + 3 = 4 сети в общей сложности 10 пространств имен.
После запуска приведенного ниже сценария (с правами root) можно тестировать и наблюдать с помощью таких команд:
term1:
ip netns exec fakebridge tcpdump -l -n -s0 -e -p -i trunk0
term2:
ip netns exec host10 ping -c1 198.51.100.20
Давать например:
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on trunk0, link-type EN10MB (Ethernet), capture size 262144 bytes
00:27:56.036743 c2:e8:f4:79:28:96 > ff:ff:ff:ff:ff:ff, ethertype 802.1Q (0x8100), length 46: vlan 10, p 0, ethertype ARP, Request who-has 192.0.2.110 tell 192.0.2.10, length 28
00:27:56.036777 16:51:fa:18:21:b0 > c2:e8:f4:79:28:96, ethertype 802.1Q (0x8100), length 46: vlan 10, p 0, ethertype ARP, Reply 192.0.2.110 is-at 16:51:fa:18:21:b0, length 28
00:27:56.036794 c2:e8:f4:79:28:96 > 16:51:fa:18:21:b0, ethertype 802.1Q (0x8100), length 102: vlan 10, p 0, ethertype IPv4, 192.0.2.10 > 198.51.100.20: ICMP echo request, id 13483, seq 1, length 64
00:27:56.036807 16:51:fa:18:21:b0 > ff:ff:ff:ff:ff:ff, ethertype 802.1Q (0x8100), length 46: vlan 20, p 0, ethertype ARP, Request who-has 198.51.100.20 tell 198.51.100.120, length 28
00:27:56.036832 b6:1d:bc:33:87:98 > 16:51:fa:18:21:b0, ethertype 802.1Q (0x8100), length 46: vlan 20, p 0, ethertype ARP, Reply 198.51.100.20 is-at b6:1d:bc:33:87:98, length 28
00:27:56.036841 16:51:fa:18:21:b0 > b6:1d:bc:33:87:98, ethertype 802.1Q (0x8100), length 102: vlan 20, p 0, ethertype IPv4, 192.0.2.10 > 198.51.100.20: ICMP echo request, id 13483, seq 1, length 64
00:27:56.036860 b6:1d:bc:33:87:98 > 16:51:fa:18:21:b0, ethertype 802.1Q (0x8100), length 102: vlan 20, p 0, ethertype IPv4, 198.51.100.20 > 192.0.2.10: ICMP echo reply, id 13483, seq 1, length 64
00:27:56.036867 16:51:fa:18:21:b0 > c2:e8:f4:79:28:96, ethertype 802.1Q (0x8100), length 102: vlan 10, p 0, ethertype IPv4, 198.51.100.20 > 192.0.2.10: ICMP echo reply, id 13483, seq 1, length 64
00:28:01.043203 16:51:fa:18:21:b0 > c2:e8:f4:79:28:96, ethertype 802.1Q (0x8100), length 46: vlan 10, p 0, ethertype ARP, Request who-has 192.0.2.10 tell 192.0.2.110, length 28
00:28:01.043246 b6:1d:bc:33:87:98 > 16:51:fa:18:21:b0, ethertype 802.1Q (0x8100), length 46: vlan 20, p 0, ethertype ARP, Request who-has 198.51.100.120 tell 198.51.100.20, length 28
00:28:01.043287 c2:e8:f4:79:28:96 > 16:51:fa:18:21:b0, ethertype 802.1Q (0x8100), length 46: vlan 10, p 0, ethertype ARP, Reply 192.0.2.10 is-at c2:e8:f4:79:28:96, length 28
00:28:01.043284 16:51:fa:18:21:b0 > b6:1d:bc:33:87:98, ethertype 802.1Q (0x8100), length 46: vlan 20, p 0, ethertype ARP, Reply 198.51.100.120 is-at 16:51:fa:18:21:b0, length 28
Сценарий установки для запуска от имени пользователя root. Он создает различные пространства имен сети, используя ip netns
заполняет необходимые сетевые ссылки (мосты и veth), настраивает tc- фильтры на fake bridge и, наконец, настраивает IP-адреса различных хостов, чтобы можно было экспериментировать. фэйкбридж остается без IP или моста. Там нет таблицы MAC, которую можно заполнить: ip neigh
или же bridge fdb
не будет показывать ничего, связанного с трафиком, так как нет ни ARP без IP, ни MAC-обучения без моста.
#!/bin/sh
if ip netns id | grep -qv '^ *$' ; then
printf 'ERROR: leave netns "%s" first\n' $(ip netns id) >&2
exit 1
fi
hosts='router fakebridge host10 host10b host20 host30'
nets='trunk vlan10 vlan20 vlan30'
for ns in $hosts $nets; do
ip netns del $ns 2>/dev/null || :
ip netns add $ns
ip netns exec $ns sysctl -q -w net.ipv6.conf.default.disable_ipv6=1
ip netns exec $ns sysctl -q -w net.ipv4.icmp_echo_ignore_broadcasts=0
done
for ns in $hosts; do
ip -n $ns link set lo up
done
bmac=1
for ns in $nets; do
ip -n $ns link add bridge0 address 02:00:00:00:00:$(printf '%02d' $bmac) type bridge
ip -n $ns link set bridge0 up
bmac=$(($bmac+1))
done
link_ns () {
ip -n $1 link add name "$3" type veth peer netns $2 name "$4"
ip -n $1 link set dev "$3" up
ip -n $2 link set dev "$4" up
if printf '%s\n' "$nets" | grep -q -w "$1"; then
ip -n "$1" link set dev "$3" master bridge0
fi
if printf '%s\n' "$nets" | grep -q -w "$2"; then
ip -n "$2" link set dev "$4" master bridge0
fi
}
link_ns trunk fakebridge fakebridge trunk0
link_ns vlan10 fakebridge fakebridge vlan10
link_ns vlan20 fakebridge fakebridge vlan20
link_ns vlan30 fakebridge fakebridge vlan30
link_ns trunk router router trunk0
link_ns vlan10 host10 host10 eth0
link_ns vlan10 host10b host10b eth0
link_ns vlan20 host20 host20 eth0
link_ns vlan30 host30 host30 eth0
ip netns exec fakebridge tc qdisc add dev trunk0 ingress
for vlan in 10 20 30; do
ip netns exec fakebridge tc qdisc add dev vlan$vlan ingress
ip netns exec fakebridge tc filter add dev vlan$vlan parent ffff: matchall action vlan push id $vlan action mirred egress redirect dev trunk0
ip netns exec fakebridge tc filter add dev trunk0 parent ffff: basic match "meta(vlan mask 0xfff eq $vlan)" action vlan pop action mirred egress redirect dev vlan$vlan
done
for vlan in 10 20 30; do
ip -n router link add link trunk0 name trunk.$vlan type vlan id $vlan
ip -n router link set dev trunk.$vlan up
ip netns exec router sysctl -q -w net.ipv4.conf.trunk/$vlan.forwarding=1
done
ip -n router address add 192.0.2.110/24 dev trunk.10
ip -n router address add 198.51.100.120/24 dev trunk.20
ip -n router address add 203.0.113.130/24 dev trunk.30
ip -n host10 address add 192.0.2.10/24 dev eth0
ip -n host10b address add 192.0.2.11/24 dev eth0
ip -n host20 address add 198.51.100.20/24 dev eth0
ip -n host30 address add 203.0.113.30/24 dev eth0
ip -n host10 route add default via 192.0.2.110
ip -n host10b route add default via 192.0.2.110
ip -n host20 route add default via 198.51.100.120
ip -n host30 route add default via 203.0.113.130