mcastmngr: fixed mcproxy crash for multiupstream config

If more than one upstream iface is defined, mcastmngr runs mcproxy
multiple times for each protocol (e.g. igmp) with the same config.
So second mcproxy process fails because it can't lock the MRT flag
(/proc/sys/net/ipv/default/mc_forwarding) already blocked by the
first process.

If one good and one bad (no IP address) upstream interfaces are defined,
mcastmngr generates wrong filter configuration and mcproxy cannot start
at all.

Now upstream and downstream ifaces are checked in the
'config_mcproxy_interfaces' function and only good interfaces (with IP)
are written to the mcproxy config. Only one mcproxy process is started
per protocol (igmp, mld), configured to handle multiple interfaces
if necessary.

'get_network_of' function removed. This function fails in some
uci configurations for interfaces that refer to a device indirectly.
Example - network.wan6.device='@wan'
This commit is contained in:
Anatoly Mirin 2023-07-03 12:11:28 +03:00 committed by Rahul Thakur
parent 3f398d1016
commit 7c3d1f919c

View file

@ -10,70 +10,81 @@ PROG_EXE=/usr/sbin/mcproxy
PROG_PARAMS= PROG_PARAMS=
PROG_PARAMS_SEPARATOR=: PROG_PARAMS_SEPARATOR=:
setup_mcast_mode() {
local snooped_interface=$1
local mcast_mode=$2
local mcast_flood= __device_is_bridge() {
local device="$2"
local devsec__="$(uci show network | grep 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= local devsec=
local sectype= __device_is_bridge devsec "$device" || return 1
local devtype= }
local ports=
if [ -z "$snooped_interface" ]; then
return
fi
if [ $mcast_mode == "2" ]; then # disable mcast flood device_ports() {
mcast_flood=0 local device="$1"
else local devsec=
mcast_flood=1
fi
for intf in $snooped_interface; if __device_is_bridge devsec "$device"; then
do echo "$(uci get network.$devsec.ports)"
devsec="$(uci show network | grep name=.*$intf | cut -d'.' -f2)"
sectype="$(uci -q get network.$devsec)"
devtype="$(uci -q get network.$devsec.type)"
if [ "$sectype" == "device" -a "$devtype" == "bridge" ]; then
ports="$(uci get network.$devsec.ports)"
for prt in $ports; do
echo $mcast_flood > /sys/class/net/$prt/brport/multicast_flood
done
else else
if [ -f /sys/class/net/$intf/brport/multicast_flood ]; then echo "$device"
echo $mcast_flood > /sys/class/net/$intf/brport/multicast_flood
fi
fi 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
# NB. Don't use 'get_network_of' here.
# This function fails in some uci configurations for interfaces that refer
# to a device indirectly.
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 done
return 1
} }
config_mcproxy_interfaces() { config_mcproxy_interfaces() {
local upstreams="$1" local protocol="$1"
local downstreams="$2" local upstreams="$2"
local exceptions="$3" local downstreams="$3"
local exceptions="$4"
if [ -z "$upstreams" ] || [ -z "$downstreams" ]; then
return 1
fi
local str_up="" local str_up=""
if [ -n "$upstreams" ]; then
for upstream in $upstreams; do for upstream in $upstreams; do
device_has_ip "$protocol" "$upstream" || continue
str_up="$str_up \"$upstream\"" str_up="$str_up \"$upstream\""
done done
fi [ -z "$str_up" ] && return 1
local str_down="" local str_down=""
if [ -n "$downstreams" ]; then
for downstream in $downstreams; do for downstream in $downstreams; do
device_has_ip "$protocol" "$downstream" || continue
str_down="$str_down \"$downstream\"" str_down="$str_down \"$downstream\""
done done
fi [ -z "$str_down" ] && return 1
if [ ! -z $downstream ]; then
echo -e "pinstance main:$str_up ==>$str_down;\n" >> $CONFFILE echo -e "pinstance main:$str_up ==>$str_down;\n" >> $CONFFILE
fi
if [ -z "$exceptions" ] || [ -z "$upstreams" ]; then
return
fi
for excp in $exceptions; do for excp in $exceptions; do
local filter="" local filter=""
@ -89,34 +100,63 @@ config_mcproxy_interfaces() {
;; ;;
esac esac
for upstream in $upstreams; do for upstream in $str_up; do
echo "pinstance main upstream \"$upstream\" in blacklist table{$filter };" >> $CONFFILE echo "pinstance main upstream $upstream in blacklist table{$filter };" >> $CONFFILE
echo "pinstance main upstream \"$upstream\" out blacklist table{$filter };" >> $CONFFILE echo "pinstance main upstream $upstream out blacklist table{$filter };" >> $CONFFILE
done done
for downstream in $downstreams; do for downstream in $str_down; do
echo "pinstance main downstream \"$downstream\" in blacklist table{$filter };" >> $CONFFILE echo "pinstance main downstream $downstream in blacklist table{$filter };" >> $CONFFILE
echo "pinstance main downstream \"$downstream\" out blacklist table{$filter };" >> $CONFFILE echo "pinstance main downstream $downstream out blacklist table{$filter };" >> $CONFFILE
done done
done done
} }
config_sysfs_mcast_snooping() { config_sysfs_mcast_snooping() {
local devsec= local downstreams="$1"
local sectype=
local devtype=
local ports=
for downstream in $downstreams; do for downstream in $downstreams; do
devsec="$(uci show network | grep name=.*$downstream | cut -d'.' -f2)" if device_is_bridge "$downstream"; then
sectype="$(uci -q get network.$devsec)"
devtype="$(uci -q get network.$devsec.type)"
if [ "$sectype" == "device" -a "$devtype" == "bridge" ]; then
echo 1 > /sys/class/net/$downstream/bridge/multicast_snooping echo 1 > /sys/class/net/$downstream/bridge/multicast_snooping
fi fi
done 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_mode() {
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_mcproxy_instance() { config_mcproxy_instance() {
local protocol="$1" local protocol="$1"
local version="$2" local version="$2"
@ -129,7 +169,6 @@ config_mcproxy_instance() {
local exceptions= local exceptions=
local upstreams= local upstreams=
local downstreams= local downstreams=
local intf_has_ip=
local mcast_mode=2 # default value 2 is for blocking mode local mcast_mode=2 # default value 2 is for blocking mode
CONFFILE=/var/etc/mcproxy_"$protocol".conf CONFFILE=/var/etc/mcproxy_"$protocol".conf
@ -178,58 +217,23 @@ config_mcproxy_instance() {
mcast_mode=$mld_p_mode mcast_mode=$mld_p_mode
fi fi
# for snooping to work we should enable it on the bridge, doing it from
# here instead of from inside network config
config_sysfs_mcast_snooping
[ -n "$max_groups" ] && echo -e "max_groups $max_groups;" >> $CONFFILE [ -n "$max_groups" ] && echo -e "max_groups $max_groups;" >> $CONFFILE
[ -n "$robustness" ] && echo -e "rv $robustness;" >> $CONFFILE [ -n "$robustness" ] && echo -e "rv $robustness;" >> $CONFFILE
[ -n "$query_interval" ] && echo -e "qi $query_interval;" >> $CONFFILE [ -n "$query_interval" ] && echo -e "qi $query_interval;" >> $CONFFILE
[ -n "$q_resp_interval" ] && echo -e "qri $q_resp_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 "$last_mem_q_int" ] && echo -e "lmqi $last_mem_q_int;" >> $CONFFILE
if [[ -n $fast_leave ]]; then [ -n "$fast_leave" ] && echo -e "fastleave $fast_leave;\n" >> $CONFFILE
echo -e "fastleave $fast_leave;\n" >> $CONFFILE
config_sysfs_mcast_fastleave $fast_leave
fi
setup_mcast_mode $downstreams $mcast_mode config_mcproxy_interfaces "$protocol" "$upstreams" "$downstreams" "$exceptions" || return
[ -n "$upstreams" ] && [ -n "$downstreams" ] &&
config_mcproxy_interfaces "$upstreams" "$downstreams" "$exceptions"
# In case on proxy, upstreams is a list. Iterating and running the mcproxy # for snooping to work we should enable it on the bridge, doing it from
# for each valid upstream interface # here instead of from inside network config
for upstream_device in $upstreams; config_sysfs_mcast_snooping "$downstreams"
do [ -n $fast_leave ] &&
# Read the upstream interface for the upstream device config_sysfs_mcast_fastleave "$downstreams" "$fast_leave"
# upstream device can have multiple logical interfaces like wan and wan6 config_sysfs_mcast_mode "$downstreams" "$mcast_mode"
# but same l3 device
local upstream_ifaces=$(get_network_of $upstream_device)
for iface in $upstream_ifaces;
do
if [ "$protocol" == "igmp" ]; then
network_get_ipaddr upstream_ip $iface
if [ ! -z "${upstream_ip}" ]; then
intf_has_ip=1
break
fi
fi
if [ "$protocol" == "mld" ]; then
network_get_ipaddr6 upstream_ip $iface
if [ ! -z "${upstream_ip}" ]; then
intf_has_ip=1
break
fi
fi
done
if [ -z "${intf_has_ip}" ]; then
continue
fi
PROG_PARAMS="${PROG_PARAMS} -f ${CONFFILE}${PROG_PARAMS_SEPARATOR}" PROG_PARAMS="${PROG_PARAMS} -f ${CONFFILE}${PROG_PARAMS_SEPARATOR}"
done
} }
config_mcproxy() { config_mcproxy() {
@ -242,28 +246,6 @@ config_mcproxy() {
fi fi
} }
config_sysfs_mcast_fastleave() {
local devsec=
local sectype=
local devtype=
local ports=
for downstream in $downstreams; do
devsec="$(uci show network | grep name=.*$downstream | cut -d'.' -f2)"
sectype="$(uci -q get network.$devsec)"
devtype="$(uci -q get network.$devsec.type)"
if [ "$sectype" == "device" -a "$devtype" == "bridge" ]; then
ports="$(uci get network.$devsec.ports)"
for prt in $ports; do
echo $1 > /sys/class/net/$prt/brport/multicast_fast_leave
done
else
[[ -f /sys/class/net/$downstream/brport/multicast_fast_leave ]] && echo $1 > /sys/class/net/$downstream/brport/multicast_fast_leave
fi
done
}
configure_mcast() { configure_mcast() {
config_global_params "set_max_groups_and_sources" config_global_params "set_max_groups_and_sources"