Strawing Blog Archivers

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

Supported by DHL Author:Wolfit