Скрипт собирающий netgraph мост для мультикаст трафика (IGMP, UDP) между двумя сетевыми интерфейсами предназначен для замены igmpproxy/mrouted.
#!/bin/sh
IF_UPSTREAM="$2"
IF_DOWNSTREAM="$3"
BR_NAME="${IF_UPSTREAM}-${IF_DOWNSTREAM}-br"
PATTERN_MCAST_IGMP="ether[0] & 1 = 1 and (ether[0:4] != 0xffffffff or ether[4:2] != 0xffff) and ip[9] = 2"
PATTERN_MCAST_IGMP_UDP="ether[0] & 1 = 1 and (ether[0:4] != 0xffffffff or ether[4:2] != 0xffff) and (ip[9] = 17 or ip[9] = 2)"
BPFPROG_PASSTROUTH="bpf_prog_len=1 bpf_prog=[ { code=6 jt=0 jf=0 k=0 } ]"
usage_msg()
{
echo "usage: start|stop upstreamIF downstreamIF"
echo "This will create bridge between two interfaces for IGMP, and UDP from upstreamIF to downstreamIF"
}
if [ -z "${IF_UPSTREAM}" -o -z "${IF_DOWNSTREAM}" ]; then
usage_msg
return 1
fi
if ! ifconfig "$IF_UPSTREAM" > /dev/null 2>&1 ; then
usage_msg
echo "Invalid upstream interface: ${IF_UPSTREAM}"
return 1
fi
if ! ifconfig "$IF_DOWNSTREAM" > /dev/null 2>&1 ; then
usage_msg
echo "Invalid downstream interface: ${IF_DOWNSTREAM}"
return 1
fi
case "$1" in
start)
echo "start bridging beetwen ${IF_UPSTREAM} and ${IF_DOWNSTREAM}"
kldload ng_ether > /dev/null 2>&1
kldload ng_bpf > /dev/null 2>&1
BPFPROG_MCAST_IGMP=$( tcpdump -s 65535 -ddd ${PATTERN_MCAST_IGMP} | \
( read len ; \
echo -n "bpf_prog_len=$len " ; \
echo -n "bpf_prog=[" ; \
while read code jt jf k ; do \
echo -n " { code=$code jt=$jt jf=$jf k=$k }" ; \
done ; \
echo " ]" ) )
BPFPROG_MCAST_IGMP_UDP=$( tcpdump -s 65535 -ddd ${PATTERN_MCAST_IGMP_UDP} | \
( read len ; \
echo -n "bpf_prog_len=$len " ; \
echo -n "bpf_prog=[" ; \
while read code jt jf k ; do \
echo -n " { code=$code jt=$jt jf=$jf k=$k }" ; \
done ; \
echo " ]" ) )
ngctl mkpeer ${IF_UPSTREAM}: bpf lower ${IF_UPSTREAM}-lower
ngctl name ${IF_UPSTREAM}:lower ${BR_NAME}-bpf
ngctl connect ${IF_UPSTREAM}: ${BR_NAME}-bpf: upper ${IF_UPSTREAM}-upper
ngctl connect ${IF_DOWNSTREAM}: ${BR_NAME}-bpf: lower ${IF_DOWNSTREAM}-lower
ngctl connect ${IF_DOWNSTREAM}: ${BR_NAME}-bpf: upper ${IF_DOWNSTREAM}-upper
ngctl msg ${BR_NAME}-bpf: setprogram { thisHook=\"${IF_UPSTREAM}-lower\" ifMatch=\"${IF_DOWNSTREAM}-lower\" ifNotMatch=\"${IF_UPSTREAM}-upper\" ${BPFPROG_MCAST_IGMP_UDP} }
ngctl msg ${BR_NAME}-bpf: setprogram { thisHook=\"${IF_UPSTREAM}-upper\" ifMatch=\"\" ifNotMatch=\"${IF_UPSTREAM}-lower\" ${BPFPROG_PASSTROUTH} }
ngctl msg ${BR_NAME}-bpf: setprogram { thisHook=\"${IF_DOWNSTREAM}-lower\" ifMatch=\"${IF_UPSTREAM}-lower\" ifNotMatch=\"${IF_DOWNSTREAM}-upper\" ${BPFPROG_MCAST_IGMP} }
ngctl msg ${BR_NAME}-bpf: setprogram { thisHook=\"${IF_DOWNSTREAM}-upper\" ifMatch=\"\" ifNotMatch=\"${IF_DOWNSTREAM}-lower\" ${BPFPROG_PASSTROUTH} }
ngctl msg ${IF_UPSTREAM}: setautosrc 1
ngctl msg ${IF_UPSTREAM}: setpromisc 1
ngctl msg ${IF_DOWNSTREAM}: setpromisc 1
;;
stop)
echo "stop bridging beetwen ${IF_UPSTREAM} and ${IF_DOWNSTREAM}"
ngctl msg ${IF_UPSTREAM}: setautosrc 0
ngctl msg ${IF_UPSTREAM}: setpromisc 0
ngctl msg ${IF_DOWNSTREAM}: setpromisc 0
ngctl rmhook ${IF_UPSTREAM}: lower
ngctl rmhook ${IF_UPSTREAM}: upper
ngctl rmhook ${IF_DOWNSTREAM}: lower
ngctl rmhook ${IF_DOWNSTREAM}: upper
ngctl shutdown ${BR_NAME}-bpf:
;;
*)
usage_msg
esac
return 0ИспользованиеСоздать мост между em0 и re0, где re0 подключён к сети с мультикастом:
Код:
mcastbr2.sh start re0 em0
Удалить мост:
Код:
mcastbr2.sh stop re0 em0
Историяigmpproxy и mrouted у меня работать отказались, после нескольких часов проб, чтения и новых проб я решил не заниматься ремонтом ядра и этих приложений, а просто выборочно сбриджевать два сетевых интерфейса: - IGMP на multicast адреса из локалки в сеть провайдера и обратно; - UDP на multicast адреса из сети провайдера в локалку. Броадкаст не нужен, и направляется в ядро как обычно.Несмотря на цифру 2, по сути эта третья версия графа, получившая в результате оптимизации первых двух.Первая содержала ng_ether, ng_tee, ng_one2many, ng_bpf.Вторая содержала ng_ether, ng_tee, ng_one2many, ng_bpf.Окончательная содержит: ng_ether, ng_left2right, ng_tee, ng_bpf.В первых двух версиях создавалась копия пакета, и один уходил через мост в другую сеть либо отбрасывался, а второй попадал в ядро.Поскольку ядро всё равно их дропало где то внутри и нетграф их очень много отбрасывал, ничего полезного не делая, то я решил их вообще туда не посылать и организовать так чтобы не создавать дубликатов и не уничтожать пакеты.Принцип работыBPF настроен таким образом чтобы пропускать все без исключения пакеты с upper хуков ng_ether нод на lower хуки (пакеты от системы в сеть). Приходящие из сети пакеты с lower хуков нод проверяются в BPF, и - если это мультикаст; - и не броадкаст; - и IGMP [или UDP в случае если пакет от адаптера подключённого к провайдеру]то такой пакет целиком пересылается на lower хук другого адаптера, минуя сетевой стёк операционной системы.Тонкости1. Пришлось включить promisc режим на обоих интерфейсах, иначе мультикаст дропается самим сетевым адаптером, это нормальное поведение.2. Пришлось включить autosrc на интерфейсе в сети провайдера, тк у провайдера на коммутаторе настроен Port Security на пропускание только одного MAC адреса - первого изученного на порту после поднятия линка.3. Моему провайдеру нет дела до того какой src-ip в IP приходит от меня, если бы было, то я бы попробовал гнать трафик в сторону провайдера через ng_patch ноду, которая бы заменяла src-ip на нужны, и выставляла CSUM_IP и CSUM_UDP в заголовке пакета - есть шанс что драйвер сетевого адаптера сам рассчитает эти суммы либо что оборудование провайдера проигнорирует неверную контрольную сумму в IGMP пакетах от меня. Нода также подключается обоими хуками к BPF, выход настраивается на passtrouth (пересылку всех пакетов) на lower хук ng_ether на адаптере в сети провайдера, а вход ng_patch должен быть match выходом от lower на адаптере в локальной сети. Те совсем не большая модификация графа.4. Работает на vlan интерфейсах.PSДля создания аналогичного по функционалу моста, в котором будет несколько сетевых интерфейсов в разных сетях с мультикастом и несколько сетевых адаптеров в сетях куда его нужно переправить потребуется на каждый сетевой адаптер вешать по ng_split + ng_one2many и по одной ng_one2many с каждой стороны моста для рассылки копий мультикаста на все интерфейсы. upper хуки ng_ether нод по прежнему будут напрямую подключатся к BPF. В случае нескольких сетей - источников мультикаста будет ещё проблема с возможным перекрытием адресных пространств, которую можно частично разрешить настроив в BPF фильтрацию по адресам.
|