Strawing Blog » Archivers » Openwrt的miniupnpd不能监听/32以下子网UPNP客户端的解决方案
因为以前也不常用upnp,所以也没在意,今天突然要用到,不能用,就狂折腾。研究了一下午, 终于搞定了。
的确是miniupnpd的 bug。如果你的机子出于/32以下的子网(也就是255.255.0.0或者255.0.0.0或者0.0.0.0子网中),比如路由 IP192.168.2.1,而你的机子IP为192.168.1.1,子网掩码255.255.0.0,255.255.0.0通常在IP的末尾用 /16表示(255.255.255.0则是/32),如192.168.1.1/16。miniupnpd只认/32的IP,即便在/etc /config/network中设置了子网掩码为255.255.0.0,也无济于事。比如路由IP为192.168.2.1,那么miniupnpd 只认192.168.2.x的机子的自动映射请求。这是一个很大的bug,怎么他们的开发者都没有发现?
如果是非相同/32子网的IP请求upnp,那么就会(可以在luci中的upnp打开那个系统日志后,在系统日志里面看):
Jan 7 16:45:31 SSS daemon.info miniupnpd[2762]: SSDP M-SEARCH from 192.168.1.2:xxxxx ST: urn:schemas-upnp-org:service:WANPPPConnection:1
Jan 7 16:45:31 SSS daemon.err miniupnpd[2762]: Can't find in which sub network the client is
很明显,找不到子网的客户端,无法访问。
先梳理一下相关文件(注意以下配置文件中的IP等涉及隐私的资料均为乱编,并非我现在用的路由的资料):
miniupnpd的配置文件:/etc/config/upnpd
真正启动的配置文件/var/etc/miniupnpd.conf,但openwrt中/var linked到了/tmp,所以是/tmp/etc/miniupnpd.conf
服务脚本/etc/init.d/miniupnpd
我们打开/var/etc/miniupnpd.conf看看,
ext_ifname=wan
listening_ip=192.168.2.1
port=5000
enable_natpmp=yes
enable_upnp=yes
secure_mode=yes
system_uptime=yes
bitrate_down=17825792
bitrate_up=5242880
presentation_url=http://192.168.2.1/
uuid=xxxx
model_number=SSS
listening_ip很明显,后面没有指定子网。如果没有指定子网的话,就是默认的255.255.255.0,也就是说,在这个配置中只能访问192.168.2.x的主机。
由于这个配置是在tmp下的,所以我们得去查源头。
打开/etc/config/upnpd
config 'upnpd' 'config'
option 'external_iface' 'wan'
option 'internal_iface' 'lan'
option 'port' '5000'
option 'uuid' 'xxxxxx'
option 'presentation_url' 'http://192.168.2.1/'
option 'model_number' 'SSS'
option 'download' '2176'
option 'upload' '640'
option 'log_output' '1'
里面并没有ip有关的设置。
于是,我把心思转向了服务启动脚本上:/etc/init.d/miniupnpd
一行行慢慢看。我把思路简单说下。
平时是用start参数来启动服务的,那看看大约46行位置的start函数:
start() {
type miniupnpd_add_rules >/dev/null 2>/dev/null || \
ACTION=- . /etc/hotplug.d/firewall/50-miniupnpd
config_load "upnpd"
local extiface intiface upload download logging secure enabled natpmp
local extip port usesysuptime conffile serial_number model_number
local uuid notify_interval presentation_url enable_upnp
local upnp_lease_file clean_ruleset_threshold clean_ruleset_interval
config_get extiface config external_iface
config_get intiface config internal_iface
config_get extip config external_ip
config_get port config port 5000
config_get upload config upload
config_get download config download
config_get_bool logging config log_output 0
config_get conffile config config_file
config_get serial_number config serial_number
config_get model_number config model_number
config_get uuid config uuid
config_get notify_interval config notify_interval
config_get presentation_url config presentation_url
config_get upnp_lease_file config upnp_lease_file
config_get clean_ruleset_threshold config clean_ruleset_threshold
config_get clean_ruleset_interval config clean_ruleset_interval
local args
include /lib/network
scan_interfaces
local ifname
config_get ifname ${extiface:-wan} ifname
if [ -n "$conffile" ]; then
args="-f $conffile"
else
local tmpconf="/var/etc/miniupnpd.conf"
args="-f $tmpconf"
mkdir -p /var/etc
echo "ext_ifname=$ifname" >$tmpconf
[ -n "$extip" ] && \
echo "ext_ip=$extip" >>$tmpconf
local iface
for iface in ${intiface:-lan}; do
local ipaddr
config_get ipaddr "$iface" ipaddr
[ -n "$ipaddr" ] && \
echo "listening_ip=$ipaddr" >>$tmpconf
done
[ "$port" != "auto" ] && \
echo "port=$port" >>$tmpconf
config_load "upnpd"
upnpd_write_bool enable_natpmp 1
upnpd_write_bool enable_upnp 1
upnpd_write_bool secure_mode 1
upnpd_write_bool system_uptime 1
[ -n "$upnp_lease_file" ] && \
echo "lease_file=$upnp_lease_file" >>$tmpconf
[ -n "$upload" -a -n "$download" ] && {
echo "bitrate_down=$(($download * 1024 * 8))" >>$tmpconf
echo "bitrate_up=$(($upload * 1024 * 8))" >>$tmpconf
}
[ -n "${presentation_url}" ] && \
echo "presentation_url=${presentation_url}" >>$tmpconf
[ -n "${notify_interval}" ] && \
echo "notify_interval=${notify_interval}" >>$tmpconf
[ -n "${clean_ruleset_threshold}" ] && \
echo "clean_ruleset_threshold=${clean_ruleset_threshold}" >>$tmpconf
[ -n "${clean_ruleset_interval}" ] && \
echo "clean_ruleset_interval=${clean_ruleset_interval}" >>$tmpconf
[ -z "$uuid" ] && {
uuid="$(cat /proc/sys/kernel/random/uuid)"
uci set upnpd.config.uuid=$uuid
uci commit upnpd
}
[ "$uuid" = "nocli" ] || \
echo "uuid=$uuid" >>$tmpconf
[ -n "${serial_number}" ] && \
echo "serial=${serial_number}" >>$tmpconf
[ -n "${model_number}" ] && \
echo "model_number=${model_number}" >>$tmpconf
config_foreach conf_rule_add perm_rule "$tmpconf"
fi
if [ -n "$ifname" ]; then
# start firewall
local zone
config_load firewall
config_get zone core "${extiface:-wan}_zone"
[ -n "$zone" ] && \
miniupnpd_add_rules "$zone" "${extiface:-wan}" "$ifname"
if [ "$logging" = "1" ]; then
SERVICE_DAEMONIZE=1 \
service_start /usr/sbin/miniupnpd $args -d
else
SERVICE_DAEMONIZE= \
service_start /usr/sbin/miniupnpd $args
fi
else
logger -t "upnp daemon" "external interface not found, not starting"
fi
}
好了,我们也不用看其他的了,要研究的就在这里。
可见,先解析upnpd这个配置,然后输出到/var/etc/miniupnpd.conf中。
在开始解析的那堆里面,并没有看到和监听IP有关的信息,然后继续往下看。
下来,找到了echo "listening_ip=$ipaddr" >>$tmpconf
没错,这个就是输出ip地址到tmpconf(也就是/var/etc/miniupnpd.conf也就是link到的/tmp/etc/miniupnpd.conf)。
然后往前看,这个$ipaddr是从哪里得来的呢?
前面有个include /lib/network,那知道了,肯定是从network配置文件里面得到的。
我找了下/lib/network,发现是个文件夹,不管他,知道他是network配置文件,那就直接看它。于是打开/etc/config/network
我的lan段是:
config 'interface' 'lan'
option 'ifname' 'lan1 lan2 lan3 lan4'
option 'type' 'bridge'
option 'proto' 'static'
option 'ipaddr' '192.168.2.1'
option 'netmask' '255.255.0.0'
可见,这里的ipaddr并不包含子网掩码的信息,而是在netmask中。很明显,要改可不能从network配置文件下手,于是我干脆就改启动脚本:echo "listening_ip=$ipaddr" >>$tmpconf
直接在$ipaddr后面加个/16,也就是指定它工作在/16子网下也就是255.255.0.0
echo "listening_ip=$ipaddr/16" >>$tmpconf
就这么简单。然后重启miniupnpd服务(sh运行/etc/init.d/miniupnpd stop ,把它停止,然后再/etc/init.d/miniupnpd start )
我们再看看/tmp/etc/miniupnpd.conf:
ext_ifname=wan
listening_ip=192.168.2.1/16
port=5000
enable_natpmp=yes
enable_upnp=yes
secure_mode=yes
system_uptime=yes
bitrate_down=xxx
bitrate_up=xxx
presentation_url=http://192.168.2.1/
uuid=xxx
model_number=SSS
没错,listening_ip已经根据我们改的,工作在255.255.0.0子网下了。
确认miniupnpd启动以后,打开个迅雷啥的软件,然后到Luci下看,是不是有活动的客户端了?问题解决啦,哈哈。
还是那句话,看似复杂 甚至看似不能解决的问题,真的认真研究起来,还是可以解决的。
查看完整版本:Openwrt的miniupnpd不能监听/32以下子网UPNP客户端的解决方案
© Strawing Blog