#!/bin/sh . /lib/mcast/common.sh . /lib/functions/network.sh include /lib/network CONFFILE= PROG_EXE=/usr/sbin/mcproxy PROG_PARAMS= PROG_PARAMS_SEPARATOR=: snooping_bridges= __device_is_bridge() { local device="$2" local devsec__="$(uci show network | grep -F ".name='$device'" | cut -d'.' -f2)" local sectype="$(uci -q get network.$devsec__)" local devtype="$(uci -q get network.$devsec__.type)" [ "$sectype" != "device" -o "$devtype" != "bridge" ] && return 1 eval "$1=$devsec__" } device_is_bridge() { local device="$1" local devsec= __device_is_bridge devsec "$device" || return 1 } device_ports() { local device="$1" local devsec= if __device_is_bridge devsec "$device"; then echo "$(uci get network.$devsec.ports)" else echo "$device" fi } device_has_ip() { local protocol="$1" local device="$2" # Read the openwrt interface for the device. # Device can have multiple logical interfaces like wan and wan6 # but same l3 device local ifaces=$(ubus call network.interface dump | jsonfilter -e "@.interface[@.device='$device'].interface") for iface in $ifaces; do local ip= case "$protocol" in "igmp") network_get_ipaddr ip "$iface" ;; "mld") network_get_ipaddr6 ip "$iface" ;; esac [ -n "$ip" ] && return done return 1 } config_mcproxy_interfaces() { local protocol="$1" local upstreams="$2" local downstreams="$3" local exceptions="$4" if [ -z "$upstreams" ] || [ -z "$downstreams" ]; then return 1 fi local str_up="" for upstream in $upstreams; do device_has_ip "$protocol" "$upstream" || continue str_up="$str_up \"$upstream\"" done [ -z "$str_up" ] && return 1 local str_down="" for downstream in $downstreams; do device_has_ip "$protocol" "$downstream" || continue str_down="$str_down \"$downstream\"" done [ -z "$str_down" ] && return 1 echo -e "pinstance main:$str_up ==>$str_down;\n" >> $CONFFILE for excp in $exceptions; do local filter="" case $excp in */*) ip_start="$(ipcalc.sh $excp | grep IP | awk '{print substr($0,4)}')" ip_end="$(ipcalc.sh $excp | grep BROADCAST | awk '{print substr($0,11)}')" filter="$filter ($ip_start - $ip_end | *)" ;; *) filter="$filter ($excp | *)" ;; esac for upstream in $str_up; do echo "pinstance main upstream $upstream in blacklist table{$filter };" >> $CONFFILE echo "pinstance main upstream $upstream out blacklist table{$filter };" >> $CONFFILE done for downstream in $str_down; do echo "pinstance main downstream $downstream in blacklist table{$filter };" >> $CONFFILE echo "pinstance main downstream $downstream out blacklist table{$filter };" >> $CONFFILE done done } config_sysfs_mcast_snooping() { local downstreams="$1" local snooping="$2" for downstream in $downstreams; do if device_is_bridge "$downstream"; then echo $snooping > /sys/class/net/$downstream/bridge/multicast_snooping fi done } config_sysfs_mcast_fastleave() { local downstreams="$1" local fastleave="$2" local prt for downstream in $downstreams; do for prt in $(device_ports $downstream); do if [ -f /sys/class/net/$prt/brport/multicast_fast_leave ]; then echo $fastleave > /sys/class/net/$prt/brport/multicast_fast_leave fi done done } config_sysfs_mcast_version() { local protocol="$1" local interfaces="$2" local version="$3" for iface in $interfaces; do echo $version > /sys/class/net/$iface/bridge/multicast_"$protocol"_version done } config_sysfs_mcast_robustness() { local interfaces="$1" local robustness="$2" for iface in $interfaces; do echo $robustness > /sys/class/net/$iface/bridge/multicast_last_member_count done } config_sysfs_mcast_query_interval() { local interfaces="$1" local query_interval="$2" for iface in $interfaces; do echo $query_interval > /sys/class/net/$iface/bridge/multicast_query_interval done } config_sysfs_mcast_q_resp_interval() { local interfaces="$1" local q_resp_interval="$2" for iface in $interfaces; do echo $q_resp_interval > /sys/class/net/$iface/bridge/multicast_query_response_interval done } config_sysfs_mcast_last_mem_q_int() { local interfaces="$1" local last_mem_q_int="$2" for iface in $interfaces; do echo $last_mem_q_int > /sys/class/net/$iface/bridge/multicast_last_member_interval done } config_sysfs_mcast_flood() { local downstreams=$1 local mcast_mode=$2 local prt local mcast_flood= if [ $mcast_mode == "2" ]; then # disable mcast flood mcast_flood=0 else mcast_flood=1 fi for downstream in $downstreams; do for prt in $(device_ports $downstream); do if [ -f /sys/class/net/$prt/brport/multicast_flood ]; then echo $mcast_flood > /sys/class/net/$prt/brport/multicast_flood fi done done } config_snooping_mode() { local interfaces="$1" local snooping="$2" # snooping_mode: # 0 - snooping is disabled # 1 - multicast flood is enabled # 2 - multicast flood is disabled [ -z "$snooping_mode" ] && snooping_mode=2 if [ "$snooping_mode" == 0 ]; then config_sysfs_mcast_snooping "$interfaces" 0 else config_sysfs_mcast_snooping "$interfaces" 1 fi config_sysfs_mcast_flood "$interfaces" "$snooping_mode" } config_mcproxy_instance() { local protocol="$1" local version="$2" local robustness= local query_interval= local q_resp_interval= local last_mem_q_int= local fast_leave=0 local exceptions= local upstreams= local downstreams= local mcast_mode=2 # default value 2 is for blocking mode CONFFILE=/var/etc/mcproxy_"$protocol".conf rm -f $CONFFILE touch $CONFFILE if [ "$protocol" == "igmp" ]; then case "$version" in [1-3]) echo -e "protocol IGMPv${version};\n" >> $CONFFILE ;; *) echo -e "protocol IGMPv2;\n" >> $CONFFILE ;; esac robustness=$igmp_p_robustness query_interval=$igmp_query_interval q_resp_interval=$igmp_q_resp_interval last_mem_q_int=$igmp_last_mem_q_int fast_leave=$igmp_fast_leave exceptions=$igmp_p_exceptions upstreams=$igmp_p_up_interfaces downstreams=$igmp_p_down_interfaces mcast_mode=$igmp_p_mode # mcproxy reserves two multicast subscriptions for igmp router service groups local mg=$(cat /proc/sys/net/ipv4/igmp_max_memberships) mg=$((mg+2)) echo $mg > /proc/sys/net/ipv4/igmp_max_memberships elif [ "$protocol" == "mld" ]; then case "$version" in [1-2]) echo -e "protocol MLDv${version};\n" >> $CONFFILE ;; *) echo -e "protocol MLDv2;\n" >> $CONFFILE ;; esac robustness=$mld_p_robustness query_interval=$mld_query_interval q_resp_interval=$mld_q_resp_interval last_mem_q_int=$mld_last_mem_q_int fast_leave=$mld_fast_leave exceptions=$mld_p_exceptions upstreams=$mld_p_up_interfaces downstreams=$mld_p_down_interfaces mcast_mode=$mld_p_mode fi [ -n "$max_groups" ] && echo -e "max_groups $max_groups;" >> $CONFFILE [ -n "$robustness" ] && echo -e "rv $robustness;" >> $CONFFILE [ -n "$query_interval" ] && echo -e "qi $query_interval;" >> $CONFFILE [ -n "$q_resp_interval" ] && echo -e "qri $q_resp_interval;" >> $CONFFILE [ -n "$last_mem_q_int" ] && echo -e "lmqi $last_mem_q_int;" >> $CONFFILE [ -n "$fast_leave" ] && echo -e "fastleave $fast_leave;\n" >> $CONFFILE config_mcproxy_interfaces "$protocol" "$upstreams" "$downstreams" "$exceptions" || return # for snooping to work we should enable it on the bridge, doing it from # here instead of from inside network config if [ "$downstreams" != "$snooping_bridges" ]; then if [ "$mcast_mode" == "0" ]; then config_sysfs_mcast_snooping "$downstreams" 0 else config_sysfs_mcast_snooping "$downstreams" 1 fi [ -n $fast_leave ] && config_sysfs_mcast_fastleave "$downstreams" "$fast_leave" config_sysfs_mcast_flood "$downstreams" "$mcast_mode" fi PROG_PARAMS="${PROG_PARAMS} -f ${CONFFILE}${PROG_PARAMS_SEPARATOR}" } disable_snooping_iface() { local iface="$(uci -q get network.$1.name)" config_sysfs_mcast_snooping "$iface" 0 } disable_snooping() { config_load network config_foreach disable_snooping_iface device } config_snooping() { local protocol="$1" local version= local robustness= local query_interval= local q_resp_interval= local last_mem_q_int= local fast_leave=0 local snooping_mode= local interfaces= local HZ=100 local all_interfaces= if [ "$protocol" == "igmp" ]; then all_interfaces=$igmp_s_iface elif [ "$protocol" == "mld" ]; then all_interfaces=$mld_s_iface fi for iface in $all_interfaces; do device_is_bridge "$iface" || continue interfaces="$interfaces $iface" done [ -z "$interfaces" ] && return snooping_bridges="$interfaces" if [ "$protocol" == "igmp" ]; then case "$igmp_s_version" in [1-3]) version="$igmp_s_version" ;; *) version="2" ;; esac robustness=$igmp_s_robustness query_interval=$(( igmp_s_query_interval * HZ )) q_resp_interval=$(( igmp_s_q_resp_interval * HZ / 10 )) last_mem_q_int=$(( igmp_s_last_mem_q_int * HZ / 10 )) fast_leave=$igmp_s_fast_leave snooping_mode=$igmp_s_mode elif [ "$protocol" == "mld" ]; then case "$mld_s_version" in [1-2]) version="$mld_s_version" ;; *) version="2" ;; esac robustness=$mld_s_robustness query_interval=$(( mld_s_query_interval * HZ )) q_resp_interval=$(( mld_s_q_resp_interval * HZ / 10 )) last_mem_q_int=$(( mld_s_last_mem_q_int * HZ / 10 )) fast_leave=$mld_s_fast_leave snooping_mode=$mld_s_mode fi config_snooping_mode "$interfaces" "$snooping_mode" [ -n "$version" ] && config_sysfs_mcast_version "$protocol" "$interfaces" "$version" [ -n "$robustness" ] && config_sysfs_mcast_robustness "$interfaces" "$robustness" [ -n "$query_interval" ] && config_sysfs_mcast_query_interval "$interfaces" "$query_interval" [ -n "$q_resp_interval" ] && config_sysfs_mcast_q_resp_interval "$interfaces" "$q_resp_interval" [ -n "$last_mem_q_int" ] && config_sysfs_mcast_last_mem_q_int "$interfaces" "$last_mem_q_int" [ -n "$fast_leave" ] && config_sysfs_mcast_fastleave "$interfaces" "$fast_leave" } config_mcproxy() { disable_snooping if [ "$igmp_p_enable" == "1" ]; then config_mcproxy_instance igmp "$igmp_p_version" elif [ "$igmp_s_enable" == "1" ]; then config_snooping igmp "$igmp_s_version" fi if [ "$mld_p_enable" == "1" ]; then config_mcproxy_instance mld "$mld_p_version" elif [ "$mld_s_enable" == "1" ]; then config_snooping mld "$mld_s_version" fi } setup_mcast_mode() { : } configure_mcast() { config_global_params "set_max_groups_and_sources" read_mcast_snooping_params read_mcast_proxy_params config_mcproxy if [ -z "${PROG_PARAMS}" ]; then exit 0 fi }