root@H500:/# cat /etc/config/upnpc
config on_off 'upnpc_info'
option mode 'auto'
option enabled 'on'
config entry 'vhttpd'
option proto 'TCP'
option ext_port '8800'
option desc 'vhttpd'
option status 'off'
config config 'pub_ip'
option ip 'null'
config config 'communicate'
option status 'failed'
option timestamp '1630479342'
config entry 'tp_live'
option proto 'TCP'
option desc 'tp_live'
option status 'off'
option ext_port '8800'
option comm_status 'success'
option timestamp '1761113129'
config entry 'tp_vod'
option proto 'TCP'
option desc 'tp_vod'
option status 'off'
option ext_port '8800'
option comm_status 'failed'
option timestamp '1630479342'
config entry 'tp_speaker'
option proto 'TCP'
option desc 'tp_speaker'
option status 'off'
option ext_port '8800'
option comm_status 'failed'
option timestamp '1630479342'
config entry 'uhttpd'
option proto 'TCP'
option desc 'uhttpd'
option status 'off'
option ext_port '18443'
option comm_status 'failed'
option timestamp '1761115038'
config entry 'tp_stream'
option proto 'TCP'
option desc 'tp_stream'
option status 'off'
option ext_port '21443'
option comm_status 'failed'
option timestamp '1761115038'
我的upnpc.sh是这样的:#!/bin/sh
RUN_ONCE_FILE=/var/run/upnpc_running
RUN_ONCE_FILE_RESTART=/var/run/upnpc_running_restart
RUN_ONCE_FILE_STOP=/var/run/upnpc_running_stop
if [ -e $RUN_ONCE_FILE ]; then
exit 0
fi
touch $RUN_ONCE_FILE
. /lib/functions.sh
. /usr/share/libubox/jshn.sh
__network_ipaddr()
{
local __var="$1"
local __family="$2"
local __prefix="${3:-0}"
local __tmp="$(ubus -t 2 call network wan_status 2>/dev/null)"
json_load "${__tmp:-{}}"
json_get_type __tmp "ipv${__family}_address"
if [ "$__tmp" = array ]; then
json_select "ipv${__family}_address"
json_get_type __tmp 1
if [ "$__tmp" = object ]; then
json_select 1
json_get_var $__var address
[ $__prefix -gt 0 ] && {
json_get_var __tmp mask
eval "export -- \"$__var=\${$__var}/$__tmp\""
}
return 0
fi
fi
return 1
}
# IP_ADDR=$(echo $(ifconfig) |cut -d ' ' -f7|cut -d ':' -f2)
# IP_ADDR=$(cat /tmp/udhcpc/dhcpip)
# network_get_ipaddr() { __network_ipaddr $1 4 0; }
BIND_STATUS=""
PUB_IP=""
COMM_STATUS=""
IP_ADDR=""
COMPARE_RESULT=0
DELETE_STRING=""
ADD_STRING=""
# random method
RANDOM_INDEX=""
RANDOM_MIN=10000
RANDOM_MAX=30000
upnpc_mode=$(uci_get "upnpc" "upnpc_info" "mode")
upnpc_enabled=$(uci_get "upnpc" "upnpc_info" "enabled")
uhttpd_inner_port=$(uci_get "uhttpd" "main" "listen_https" "443")
#vhttpd_inner_port=$(uci_get "cet" "vhttpd" "port" "8080")
rtsp_inner_port=$(uci_get "cet" "rtsp" "port" "554")
onvif_service_inner_port="2020"
tp_live_inner_port="19443"
tp_vod_inner_port="18443"
tp_speaker_inner_port="17443"
tp_stream_inner_port="21443"
uhttpd_status=$(uci_get "upnpc" "uhttpd" "status")
uhttpd_ext_port=$(uci_get "upnpc" "uhttpd" "ext_port")
uhttpd_proto=$(uci_get "upnpc" "uhttpd" "proto")
uhttpd_desc=$(uci_get "upnpc" "uhttpd" "desc")
uhttpd_flag=1
uhttpd_mapping=0
rtsp_status=$(uci_get "upnpc" "rtsp" "status")
rtsp_ext_port=$(uci_get "upnpc" "rtsp" "ext_port")
rtsp_proto=$(uci_get "upnpc" "rtsp" "proto")
rtsp_desc=$(uci_get "upnpc" "rtsp" "desc")
rtsp_flag=2
rtsp_mapping=0
onvif_service_status=$(uci_get "upnpc" "onvif_service" "status")
onvif_service_ext_port=$(uci_get "upnpc" "onvif_service" "ext_port")
onvif_service_proto=$(uci_get "upnpc" "onvif_service" "proto")
onvif_service_desc=$(uci_get "upnpc" "onvif_service" "desc")
onvif_service_flag=4
onvif_service_mapping=0
tp_live_status=$(uci_get "upnpc" "tp_live" "status")
tp_live_ext_port=$(uci_get "upnpc" "tp_live" "ext_port")
tp_live_proto=$(uci_get "upnpc" "tp_live" "proto")
tp_live_desc=$(uci_get "upnpc" "tp_live" "desc")
tp_live_flag=8
tp_live_mapping=0
tp_vod_status=$(uci_get "upnpc" "tp_vod" "status")
tp_vod_ext_port=$(uci_get "upnpc" "tp_vod" "ext_port")
tp_vod_proto=$(uci_get "upnpc" "tp_vod" "proto")
tp_vod_desc=$(uci_get "upnpc" "tp_vod" "desc")
tp_vod_flag=16
tp_vod_mapping=0
tp_speaker_status=$(uci_get "upnpc" "tp_speaker" "status")
tp_speaker_ext_port=$(uci_get "upnpc" "tp_speaker" "ext_port")
tp_speaker_proto=$(uci_get "upnpc" "tp_speaker" "proto")
tp_speaker_desc=$(uci_get "upnpc" "tp_speaker" "desc")
tp_speaker_flag=32
tp_speaker_mapping=0
tp_stream_status=$(uci_get "upnpc" "tp_stream" "status")
tp_stream_ext_port=$(uci_get "upnpc" "tp_stream" "ext_port")
tp_stream_proto=$(uci_get "upnpc" "tp_stream" "proto")
tp_stream_desc=$(uci_get "upnpc" "tp_stream" "desc")
tp_stream_flag=1
tp_stream_mapping=0
upnpc_get_mapping()
{
local compare_times=0
# we will try three times at most to get port mapping
while [ "$compare_times" -lt 3 ]
do
upnpc -l > "/tmp/upnpc_output"
if [ -s "/tmp/upnpc_output" ]
then
break;
else
let compare_times=$compare_times+1
sleep 1
fi
done
}
upnpc_update_comm()
{
local port_status=$1
local secname=$2
local port=$3
# check status
local config_status
local config_ext_port
local config_comm_status
eval config_get config_status "${secname}" status
eval config_get config_ext_port "${secname}" ext_port
eval config_get config_comm_status "${secname}" comm_status
if [ $config_status == $port_status ]
then
if [ -z $port ]
then
return 0
elif [ $config_ext_port == $port ]
then
return 0
fi
fi
if [ $port_status == "on" ]
then
if [ $PUB_IP == "null" ]
then
if [ $config_comm_status != "failed" ]
then
uci set upnpc."${secname}".comm_status='failed'
timestamp=`date +%s`
uci set upnpc."${secname}".timestamp=$timestamp
fi
else
if [ $config_comm_status != "unknown" ]
then
uci set upnpc."${secname}".comm_status='unknown'
timestamp=`date +%s`
uci set upnpc."${secname}".timestamp=$timestamp
fi
fi
elif [ $port_status == "off" ]
then
if [ $config_comm_status != "failed" ]
then
uci set upnpc."${secname}".comm_status='failed'
timestamp=`date +%s`
uci set upnpc."${secname}".timestamp=$timestamp
fi
fi
}
# get config values from the upnpc config file
upnpc_get_config()
{
local secname="$1"
config_get "${secname}_status" "$secname" status
config_get "${secname}_ext_port" "$secname" ext_port
config_get "${secname}_proto" "$secname" proto
config_get "${secname}_desc" "$secname" desc
}
# set config values base on the "upnpc -l" results
upnpc_set_config_from_real()
{
local secname="$1"
local file_path="$2"
local ip
local inner_port
local flag=0
while read proto ext_port ip_port desc
do
# divide ip_port such as 192.168.0.60:8000 into 192.168.0.60 8000
ip=$(echo $ip_port | sed 's/:.*//g')
inner_port=$(echo $ip_port | sed 's/.*://g')
local config_inner_port
local config_proto
local config_desc
local config_ext_port
eval config_inner_port="\${${secname}_inner_port}"
eval config_proto="\${${secname}_proto}"
eval config_desc="\${${secname}_desc}"
eval config_ext_port="\${${secname}_ext_port}"
if [ "$IP_ADDR" == "$ip" ] \
&& [ "$config_inner_port" == "$inner_port" ] \
&& [ "$config_proto" == "$proto" ] \
&& [ "$config_desc" == "$desc" ];
then
if [ "manual" == "$upnpc_mode" ]
then
# upnp works in manual mode
# we need to keep user`s config to do port mapping again
uci set upnpc."$secname".status='on'
flag=1
break
fi
# 处理一下port变化的情况
if [ $secname == "tp_live" ] \
|| [ $secname == "tp_vod" ] \
|| [ $secname == "tp_speaker" ];
then
upnpc_update_comm "on" $secname $ext_port
fi
uci set upnpc."$secname".status='on'
uci set upnpc."$secname".ext_port=$ext_port
flag=1
break
fi
done < "$file_path"
if [ $flag == 0 ]
then
if [ $secname == "tp_live" ] \
|| [ $secname == "tp_vod" ] \
|| [ $secname == "tp_speaker" ];
then
upnpc_update_comm "off" $secname
fi
uci set upnpc."$secname".status='off'
fi
}
# compare config values with the "upnpc -l" results
upnpc_compare_real_config()
{
local secname="$1"
local file_path="$2"
local mode="$3"
local flag=1
local ip
local inner_port
while read proto ext_port ip_port desc
do
# divide ip_port such as 192.168.0.60:8000 into 192.168.0.60 8000
ip=$(echo $ip_port | sed 's/:.*//g')
inner_port=$(echo $ip_port | sed 's/.*://g')
local config_inner_port
local config_ext_port
local config_proto
local config_desc
eval config_inner_port="\${${secname}_inner_port}"
eval config_ext_port="\${${secname}_ext_port}"
eval config_proto="\${${secname}_proto}"
eval config_desc="\${${secname}_desc}"
if [ "$IP_ADDR" == "$ip" ] \
&& [ "$config_inner_port" == "$inner_port" ] \
&& [ "$config_ext_port" == "$ext_port" ] \
&& [ "$config_proto" == "$proto" ] \
&& [ "$config_desc" == "$desc" ];
then
if [ "auto" == "$mode" ] \
&& [ "$ext_port" -le "$RANDOM_MIN" ];
then
break
fi
if [ $secname == "tp_live" ] \
|| [ $secname == "tp_vod" ] \
|| [ $secname == "tp_speaker" ];
then
upnpc_update_comm "on" $secname $ext_port
fi
uci set upnpc."$secname".status='on'
flag=0
break
else
# delete port, if not in config
if [ "$config_desc" == "$desc" ]
then
upnpc -d $ext_port $proto
fi
fi
done < "$file_path"
if [ "$flag" == "1" ]
then
local config_flag
eval config_flag="\${${secname}_flag}"
let COMPARE_RESULT=$COMPARE_RESULT+$config_flag
fi
}
# decide whether to delete certain port mapping
upnpc_decide_delete_string()
{
local secname="$1"
local proto="$2"
local ext_port="$3"
local ip_port="$4"
local desc="$5"
local mode="$6"
local inner_port
# divide ip_port such as 192.168.0.60:8000 into 192.168.0.60 8000
inner_port=$(echo $ip_port | sed 's/.*://g')
local config_inner_port
local config_ext_port
local config_proto
local config_desc
eval config_inner_port="\${${secname}_inner_port}"
eval config_ext_port="\${${secname}_ext_port}"
eval config_proto="\${${secname}_proto}"
eval config_desc="\${${secname}_desc}"
if [ "$config_inner_port" == "$inner_port" ] \
&& [ "$config_ext_port" == "$ext_port" ] \
&& [ "$config_proto" == "$proto" ] \
&& [ "$config_desc" == "$desc" ];
then
if [ "auto" == "$mode" ] \
&& [ "$ext_port" -lt "$RANDOM_MIN" ];
then
# need to delete this port mapping
DELETE_FLAG=1
let ${secname}_mapping=0
else
# do not need to delete this port mapping
DELETE_FLAG=0
# mark that the port mapping of this secname has already been done
let ${secname}_mapping=1
fi
fi
}
# build ADD_STRING
upnpc_build_add_string()
{
local secname="$1"
local mode="$2"
local config_ext_port
local config_proto
local config_desc
local config_inner_port
local port_mapping
eval config_inner_port="\${${secname}_inner_port}"
eval config_ext_port="\${${secname}_ext_port}"
eval config_proto="\${${secname}_proto}"
eval config_desc="\${${secname}_desc}"
eval port_mapping="\${${secname}_mapping}"
if [ "0" == "$port_mapping" ]
then
# the port mapping of this secname has not already been done
# we need to do port mapping of this secname, so add to ADD_STRING
if [ "auto" != "$mode" ] \
|| [ "$config_ext_port" -ge "$RANDOM_MIN" ];
then
ADD_STRING=$ADD_STRING" "$config_inner_port" "$config_ext_port" "$config_proto" "$config_desc
fi
fi
}
# reconfigure port mapping in auto mode
upnpc_auto_reconfigure()
{
#RANDOM_INDEX=$(lua -e 'math.randomseed(tostring(os.time()):reverse():sub(1, 6)); print(math.random(10000, 30000))')
myrand() {
if [ -z "$RANDOM" ] ; then
SEED=`tr -cd 0-9 </dev/urandom | head -c 8`
else
SEED=$RANDOM
fi
RND_NUM=`echo $SEED $1 $2|awk '{srand($1);printf "%d",rand()*10000%($3-$2)+$2}'`
echo $RND_NUM
}
RANDOM_INDEX=$(myrand 20000 30000)
local compare_times=0
# we will try three times at most to do port mapping
while [ "$compare_times" -lt 3 ]
do
COMPARE_RESULT=0
config_foreach upnpc_compare_real_config entry /tmp/upnpc_output auto
if [ "0" == "$COMPARE_RESULT" ]
then
# the port mappings have been all successful
break
else
# the port mappings have not been all successful
# we need to build ADD_STRING based on COMPARE_RESULT
ADD_STRING=$IP_ADDR
local flag
# randomly set the external port by +1
let flag="$COMPARE_RESULT"\&"$uhttpd_flag"
if [ "$flag" -ne "0" ]
then
uhttpd_ext_port=$RANDOM_INDEX
let RANDOM_INDEX=$RANDOM_INDEX+1
ADD_STRING=$ADD_STRING" "$uhttpd_inner_port" "$uhttpd_ext_port" "$uhttpd_proto" "$uhttpd_desc
fi
let flag="$COMPARE_RESULT"\&"$rtsp_flag"
if [ "$flag" -ne "0" ]
then
rtsp_ext_port=$RANDOM_INDEX
let RANDOM_INDEX=$RANDOM_INDEX+1
ADD_STRING=$ADD_STRING" "$rtsp_inner_port" "$rtsp_ext_port" "$rtsp_proto" "$rtsp_desc
fi
let flag="$COMPARE_RESULT"\&"$onvif_service_flag"
if [ "$flag" -ne "0" ]
then
onvif_service_ext_port=$RANDOM_INDEX
let RANDOM_INDEX=$RANDOM_INDEX+1
ADD_STRING=$ADD_STRING" "$onvif_service_inner_port" "$onvif_service_ext_port" "$onvif_service_proto" "$onvif_service_desc
fi
let flag="$COMPARE_RESULT"\&"$tp_live_flag"
if [ "$flag" -ne "0" ]
then
tp_live_ext_port=$RANDOM_INDEX
let RANDOM_INDEX=$RANDOM_INDEX+1
ADD_STRING=$ADD_STRING" "$tp_live_inner_port" "$tp_live_ext_port" "$tp_live_proto" "$tp_live_desc
fi
let flag="$COMPARE_RESULT"\&"$tp_vod_flag"
if [ "$flag" -ne "0" ]
then
tp_vod_ext_port=$RANDOM_INDEX
let RANDOM_INDEX=$RANDOM_INDEX+1
ADD_STRING=$ADD_STRING" "$tp_vod_inner_port" "$tp_vod_ext_port" "$tp_vod_proto" "$tp_vod_desc
fi
let flag="$COMPARE_RESULT"\&"$tp_speaker_flag"
if [ "$flag" -ne "0" ]
then
tp_speaker_ext_port=$RANDOM_INDEX
let RANDOM_INDEX=$RANDOM_INDEX+1
ADD_STRING=$ADD_STRING" "$tp_speaker_inner_port" "$tp_speaker_ext_port" "$tp_speaker_proto" "$tp_speaker_desc
fi
let flag="$COMPARE_RESULT"\&"$tp_stream_flag"
if [ "$flag" -ne "0" ]
then
tp_stream_ext_port=$RANDOM_INDEX
let RANDOM_INDEX=$RANDOM_INDEX+1
ADD_STRING=$ADD_STRING" "$tp_stream_inner_port" "$tp_stream_ext_port" "$tp_stream_proto" "$tp_stream_desc
fi
# execute the upnpc comand to do the port mapping
# grep the "upnpc -l" results to get only map values that are belonged to current_ip
upnpc -a $ADD_STRING \
-l | grep $IP_ADDR > /tmp/upnpc_output
let compare_times=$compare_times+1
fi
done
}
# reconfigure port mapping
upnpc_reconfigure()
{
config_load upnpc
config_get upnpc_mode upnpc_info mode
config_get PUB_IP pub_ip ip
IP_ADDR=$(echo $(ifconfig) |cut -d ' ' -f7|cut -d ':' -f2)
if [ -e /tmp/upnpc_pre_ip ]
then
PRE_IP=`cat /tmp/upnpc_pre_ip`
else
PRE_IP=""
fi
if [ -z "$IP_ADDR" ] \
&& [ -z "$PRE_IP" ];
then
# current ip and preview ip are all empty
cat /tmp/upnpc_output > /tmp/upnpc_output1
elif [ "$IP_ADDR" == "$PRE_IP" ] \
|| [ -z "$PRE_IP" ]
then
# preview_ip is equal to current_ip
# so we do not need to check PRE_IP
PRE_IP=""
# grep the "upnpc -l" results to get only map values that are belonged to current_ip
cat /tmp/upnpc_output | grep "$IP_ADDR" > /tmp/upnpc_output1
elif [ -z "$IP_ADDR" ]
then
# grep the "upnpc -l" results to get only map values that are belonged to preview_ip
cat /tmp/upnpc_output | grep "$PRE_IP" > /tmp/upnpc_output1
else
# grep the "upnpc -l" results to get only map values that are belonged to preview_ip and current_ip
cat /tmp/upnpc_output | grep -E "$IP_ADDR|$PRE_IP" > /tmp/upnpc_output1
fi
# grep the "upnpc -l" results to get only map values that are belonged to preview_ip and current_ip
cat /tmp/upnpc_output | grep -E "$IP_ADDR|$PRE_IP" > /tmp/upnpc_output1
config_foreach upnpc_get_config entry
local ip
while read proto ext_port ip_port desc
do
# divide ip_port such as 192.168.0.60:8000 into 192.168.0.60 8000
# and get the ip value such as "192.168.0.60"
ip=$(echo $ip_port | sed 's/:.*//g')
if [ "$IP_ADDR" == "$ip" ]
then
# this port mapping is belonged to IP_ADDR
DELETE_FLAG=1
config_foreach upnpc_decide_delete_string entry $proto $ext_port $ip_port $desc $upnpc_mode
if [ "1" == "$DELETE_FLAG" ]
then
# this port mapping is not useful, delete it
# package the DELETE_STRING with the form: ext_port proto
DELETE_STRING=$DELETE_STRING" "$ext_port" "$proto
fi
fi
if [ "$PRE_IP" == "$ip" ]
then
# this port mapping is belonged to PRE_IP, delete it
# package the DELETE_STRING with the form: ext_port proto
DELETE_STRING=$DELETE_STRING" "$ext_port" "$proto
fi
done < "/tmp/upnpc_output1"
# begin to build ADD_STRING
ADD_STRING=$IP_ADDR
config_foreach upnpc_build_add_string entry $upnpc_mode
# begin to build upnpc_string
local upnpc_string
if [ -n "$DELETE_STRING" ]
then
# DELETE_STRING is not empty, add to upnpc_string
upnpc_string=$upnpc_string" -d "$DELETE_STRING
fi
if [ "$IP_ADDR" != "$ADD_STRING" ]
then
# ADD_STRING is not default, add to upnpc_string
upnpc_string=$upnpc_string" -a "$ADD_STRING
fi
# add -l to upnpc_string to list all the port mappings
upnpc_string=$upnpc_string" -l"
# execute the upnpc comand to do the port mapping
# grep the "upnpc -l" results to get only map values that are belonged to current_ip
upnpc $upnpc_string | grep $IP_ADDR > /tmp/upnpc_output
# save current ip into /tmp/upnpc_pre_ip
echo $IP_ADDR > /tmp/upnpc_pre_ip
if [ "auto" == "$upnpc_mode" ]
then
# upnp works in auto mode, if failed
# we need to use randomly port to do port mapping again
upnpc_auto_reconfigure
fi
# set upnpc config file based on the "upnpc -l" results
config_foreach upnpc_set_config_from_real entry /tmp/upnpc_output
uci commit upnpc
rm -f "/tmp/upnpc_output1"
rm -f "/tmp/upnpc_output"
}
# the following function will be called outside asynchronously
# so we need to add lock to prevent them from being called at the same time
# maintain port mapping
upnpc_maintain()
{
config_load cloud_brd
config_get BIND_STATUS bind status
config_load upnpc
config_get upnpc_enabled upnpc_info enabled
config_get upnpc_mode upnpc_info mode
config_get PUB_IP pub_ip ip
timestamp=`date +%s`
echo $timestamp >/dev/console
if [ "$BIND_STATUS" == "1" ]
then
if [ "$upnpc_enabled" == "on" ]
then
IP_ADDR=$(echo $(ifconfig) |cut -d ' ' -f7|cut -d ':' -f2)
if [ -n "$IP_ADDR" ]
then
lock "/var/run/upnpc"
#upnpc -l > "/tmp/upnpc_output"
upnpc_get_mapping
# grep the "upnpc -l" results to get only map values that are belonged to current_ip
cat /tmp/upnpc_output | grep $IP_ADDR > /tmp/upnpc_output1
COMPARE_RESULT=0
config_foreach upnpc_compare_real_config entry /tmp/upnpc_output1 $upnpc_mode
if [ $COMPARE_RESULT == 0 ]
then
uci commit upnpc
# do not need to maintain
# save current ip into /tmp/upnpc_pre_ip
echo $IP_ADDR > /tmp/upnpc_pre_ip
rm -f "/tmp/upnpc_output1"
rm -f "/tmp/upnpc_output"
else
# need to maintain, reconfigure upnpc
upnpc_reconfigure
fi
lock -u "/var/run/upnpc"
fi
fi
else
upnpc_stop
fi
}
# stop port mapping
upnpc_stop()
{
if [ -e $RUN_ONCE_FILE_STOP ]; then
exit 0
fi
touch $RUN_ONCE_FILE_STOP
lock "/var/run/upnpc"
config_load upnpc
config_get PUB_IP pub_ip ip
# network_get_ipaddr IP_ADDR
IP_ADDR=$(echo $(ifconfig) |cut -d ' ' -f7|cut -d ':' -f2)
local DELETE_STRING
local PRE_IP
local ip
if [ -e /tmp/upnpc_pre_ip ]
then
PRE_IP=`cat /tmp/upnpc_pre_ip`
else
PRE_IP=""
fi
# grep the "upnpc -l" results to get only map values that are belonged to preview_ip and current_ip
upnpc -l | grep -E "$IP_ADDR|$PRE_IP" > "/tmp/upnpc_output"
while read proto ext_port ip_port desc
do
# divide ip_port such as 192.168.0.60:8000 into 192.168.0.60 8000
# and get the ip value such as "192.168.0.60"
ip=$(echo $ip_port | sed 's/:.*//g')
if [ "$ip" == "$IP_ADDR" ] \
|| [ "$ip" == "$PRE_IP" ];
then
DELETE_STRING=$DELETE_STRING" "$ext_port" "$proto
fi
done < "/tmp/upnpc_output"
if [ -n "$DELETE_STRING" ]
then
upnpc -d $DELETE_STRING \
-l | grep $IP_ADDR > "/tmp/upnpc_output"
fi
config_foreach upnpc_set_config_from_real entry /tmp/upnpc_output
uci commit upnpc
rm -f /tmp/upnpc_output
lock -u "/var/run/upnpc"
rm $RUN_ONCE_FILE_STOP
}
# restart port mapping
upnpc_restart()
{
if [ -e $RUN_ONCE_FILE_RESTART ]; then
exit 0
fi
touch $RUN_ONCE_FILE_RESTART
config_load cloud_brd
config_get BIND_STATUS bind status
config_load upnpc
config_get upnpc_enabled upnpc_info enabled
if [ "$BIND_STATUS" == "1" ]
then
if [ "$upnpc_enabled" == "on" ]
then
lock "/var/run/upnpc"
upnpc -l > "/tmp/upnpc_output"
upnpc_reconfigure
lock -u "/var/run/upnpc"
else
upnpc_stop
fi
else
upnpc_stop
fi
rm $RUN_ONCE_FILE_RESTART
}
case "$1" in
maintain)
upnpc_maintain
;;
esac
rm $RUN_ONCE_FILE,upnp.c代码:/*****************************************************************************
* Copyright@ 2019-2021 TP-LINK TECHNOLOGIES CO., LTD.
* File Name: ulinkied_upnp.c
* Author: Mengmingzhan
* Version: 1.0
* Description:
*
* information.
*
* History:
* 2021-01-06: File created.
*****************************************************************************/
#include "upnp.h"
#include "udsd.h"
#include <time.h>
#include <lib-tpcom/lib_tpcom.h>
#include <lib-tpcom/err_code.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <sys/ioctl.h>
#include <net/if.h>
typedef struct _UPNPC_ENTRY_INFO
{
char proto[16];
char desc[16];
char ip[32];
char inner_port[8];
char ext_port[8];
} UPNPC_ENTRY_INFO;
static int get_local_ip(char *ip)
{
FILE *fp = NULL;
// char ipaddr[20] = {0};
if ((fp = fopen("/tmp/udhcpc/dhcpip", "r")) == NULL)
{
DBG_ERR( "open dhcpip file fail.\n");
goto END;
}
fseek(fp, 0L, SEEK_END);
int len = ftell(fp);
fseek(fp, 0L, SEEK_SET);
fread(ip, 1, len-1, fp);
fclose(fp);
return 1;
END:
if (fp)
fclose(fp);
return 0;
}
void udsd_get_upnp_info(struct ubus_app *svr_ubus_app, struct blob_buf* bBuf, struct blob_attr *msg)
{
int ret = 0;
int result = 0;
char enabled[10] = {0};
char mode[10] = {0};
struct uci_context *uci_ctx = tpuci_open();
if(!uci_ctx) {
result = UPNP_OTHER_ERROR;
goto failed;
}
/* enabled */
ret = tpuci_get_val_string(uci_ctx, enabled, sizeof(enabled), "upnpc", "upnpc_info", "enabled");
if (0 != ret){
DBG_ERR( "get upnpc enabled failed.\n");
result = UPNP_PARAM_ENABLED_ERROR;
goto failed;
}
if(strcmp(enabled, "on") && strcmp(enabled, "off")){
DBG_ERR( "upnp enabled config arg failed.\n");
result = UPNP_PARAM_ENABLED_ERROR;
goto failed;
}
blobmsg_add_string(bBuf, "enabled", enabled);
/* mode */
ret = tpuci_get_val_string(uci_ctx, mode, sizeof(mode), "upnpc", "upnpc_info", "mode");
if (0 != ret){
DBG_ERR( "get upnpc mode failed.\n");
result = UPNP_PARAM_MODE_ERROR;
goto failed;
}
if(strcmp(mode, "auto")){
DBG_ERR( "upnp enabled config arg failed.\n");
result = UPNP_PARAM_MODE_ERROR;
goto failed;
}
blobmsg_add_string(bBuf, "mode", "auto");
failed:
tpuci_close(uci_ctx);
common_end(bBuf, result);
return;
}
void udsd_set_upnp_info(struct ubus_app *svr_ubus_app, struct blob_buf* bBuf, struct blob_attr *msg)
{
int ret = 0;
int result = 0;
char *enabled_str = NULL;
char *mode_str = NULL;
if (!bBuf) {
DBG_ERR( "arg err\n");
return;
}
struct uci_context *uci_ctx = tpuci_open();
if (!msg || !uci_ctx) {
DBG_ERR( "msg || uci_ctx err\n");
result = UPNP_OTHER_ERROR;
goto failed;
}
struct blobmsg (
blobmsg_string enabled,
blobmsg_string mode
) (req, msg, false);
char *json_str = blobmsg_format_json(msg, true);
if (json_str) {
DBG_ERR("Received params: %s\n", json_str);
free(json_str);
}
if (!req.enabled) {
DBG_ERR( "set upnpc blobmsg err\n");
result = UPNP_PARAM_ENABLED_ERROR;
goto failed;
}
if (!req.mode) {
DBG_ERR( "set upnpc blobmsg err\n");
result = UPNP_PARAM_MODE_ERROR;
goto failed;
}
enabled_str = blobmsg_get_string(req.enabled);
mode_str = blobmsg_get_string(req.mode);
/* enabled */
if(strcmp(enabled_str, "on") && strcmp(enabled_str, "off")){
DBG_ERR( "upnpc enabled config arg failed.\n");
result = UPNP_PARAM_ENABLED_ERROR;
goto failed;
}
ret = tpuci_set_val(uci_ctx, enabled_str, "upnpc", "upnpc_info", "enabled", 1);
if (0 != ret){
DBG_ERR( "set upnp enabled format uci err\n");
result = UPNP_PARAM_ENABLED_ERROR;
goto failed;
}
/* mode */
if(strcmp(mode_str, "auto")){
DBG_ERR( "upnpc mode config arg failed.\n");
result = UPNP_PARAM_MODE_ERROR;
goto failed;
}
ret = tpuci_set_val(uci_ctx, mode_str, "upnpc", "upnpc_info", "mode", 1);
if (0 != ret){
DBG_ERR( "set upnp mode format uci err\n");
result = UPNP_PARAM_MODE_ERROR;
goto failed;
}
system(". /lib/upnpc/upnpc.sh; upnpc_restart");
failed:
tpuci_close(uci_ctx);
common_end(bBuf, result);
return;
}
void udsd_get_upnp_status(struct ubus_app *svr_ubus_app, struct blob_buf* bBuf, struct blob_attr *msg)
{
int ret = 0;
int i = 0;
int result = 0;
char ipaddr[20] = {0};
char status[20] = {0};
char proto[20] = {0};
char desc[20] = {0};
int inner_port = 0;
int ext_port = 0;
// char mode[8] = {0};
FILE *fp = NULL;
char buf[128] = {0};
char *lines[LINES_MAX] = {0};
char *read_buf = NULL;
int lines_cnt = 0;
int flag[7] = {0};
UPNPC_ENTRY_INFO entries_info;
int part_num = 0;
char *entry[] = {"uhttpd", "rtsp", "onvif_service", "tp_live", "tp_vod", "tp_speaker", "tp_stream"};
int fix_port[] = {443, 554, 2020, 19443, 18443, 17443, 21443};
struct uci_context *uci_ctx = tpuci_open();
if(!uci_ctx) {
result = UPNP_OTHER_ERROR;
goto failed;
}
/* ipaddr */
if ( 0 == get_local_ip(ipaddr) || NULL == ipaddr)
{
DBG_ERR( "get upnpc local ip failed.\n");
result = UPNP_PARAM_IPADDR_ERROR;
goto failed;
}
fp = popen("upnpc -l", "r");
if (!fp)
{
goto failed;
}
while (NULL != (read_buf = fgets(buf, 128, fp)) && strlen(read_buf) != 0)
{
lines[i] = strdup(read_buf);
DBG_ERR("line: %s\n", lines[i]);
i++;
/* 最大支持16行数据,超过则做截断处理 */
if (i >= LINES_MAX)
{
break;
}
}
lines_cnt = i;
struct blob_buf b_tmp = {NULL};
blobmsg_buf_init(&b_tmp);
for (i = 0; i < lines_cnt; i++)
{
if (strstr(lines[i], ipaddr))
{
part_num = sscanf(lines[i], "%15[^ ]%*[ ]%7[^ ]%*[ ]%31[^:]:%7[^ ]%*[ ]%15s", entries_info.proto,
entries_info.ext_port, entries_info.ip, entries_info.inner_port, entries_info.desc);
if (part_num == 5)
{
if (0 == strlen(ipaddr))
{
sscanf(entries_info.ip, "%s", ipaddr);
}
void *table = NULL;
if (NULL != (table = blobmsg_open_table(&b_tmp, entries_info.desc)))
{
//DBG_ERR("desc: %s\n", entries_info.desc);
blobmsg_add_u32(&b_tmp, "ext_port", atoi(entries_info.ext_port));
blobmsg_add_u32(&b_tmp, "inner_port", atoi(entries_info.inner_port));
blobmsg_close_table(&b_tmp, table);
//DBG_DBG_BLOB(b_tmp.head);
}
}
}
}
blobmsg_add_string(bBuf, "ipaddr", ipaddr);
struct blobmsg (
blobmsg_table uhttpd,
blobmsg_table rtsp,
blobmsg_table onvif_service,
blobmsg_table tp_live,
blobmsg_table tp_vod,
blobmsg_table tp_speaker,
blobmsg_table tp_stream,
) (info, b_tmp.head, false);
if (info.uhttpd) flag[0] = 1;
if (info.rtsp) flag[1] = 1;
if (info.onvif_service) flag[2] = 1;
if (info.tp_live) flag[3] = 1;
if (info.tp_vod) flag[4] = 1;
if (info.tp_speaker) flag[5] = 1;
if (info.tp_stream) flag[6] = 1;
//DBG_ERR( "flag: %d, %d, %d\n", flag[0],flag[1],flag[2]);
void *array;
void *table;
if (NULL != (array = blobmsg_open_array(bBuf, "entry_list")))
{
for (i = 0; i < 6; i++)
{
/* proto */
ret = tpuci_get_val_string(uci_ctx, proto, sizeof(proto), "upnpc", entry[i], "proto");
if (0 != ret){
DBG_ERR( "get upnpc proto failed.\n");
result = UPNP_PARAM_PROTO_ERROR;
goto failed;
}
/* desc */
ret = tpuci_get_val_string(uci_ctx, desc, sizeof(desc), "upnpc", entry[i], "desc");
if (0 != ret){
DBG_ERR( "get upnpc desc failed.\n");
result = UPNP_PARAM_DESC_ERROR;
goto failed;
}
/* status */
if (0 == flag[i])
{
strcpy(status, "off");
}else
{
strcpy(status, "on");
}
/* ext_port */
if (0 == flag[i])
{
ext_port = 0;
}else
{
if (0 == i)
{
struct blobmsg (
blobmsg_int32 ext_port,
) (src, info.uhttpd, false);
if (src.ext_port) ext_port = blobmsg_get_u32(src.ext_port);
}
if (1 == i)
{
struct blobmsg (
blobmsg_int32 ext_port,
) (src, info.rtsp, false);
if (src.ext_port) ext_port = blobmsg_get_u32(src.ext_port);
}
if (2 == i)
{
struct blobmsg (
blobmsg_int32 ext_port,
) (src, info.onvif_service, false);
if (src.ext_port) ext_port = blobmsg_get_u32(src.ext_port);
}
if (3 == i)
{
struct blobmsg (
blobmsg_int32 ext_port,
) (src, info.tp_live, false);
if (src.ext_port) ext_port = blobmsg_get_u32(src.ext_port);
}
if (4 == i)
{
struct blobmsg (
blobmsg_int32 ext_port,
) (src, info.tp_vod, false);
if (src.ext_port) ext_port = blobmsg_get_u32(src.ext_port);
}
if (5 == i)
{
struct blobmsg (
blobmsg_int32 ext_port,
) (src, info.tp_speaker, false);
if (src.ext_port) ext_port = blobmsg_get_u32(src.ext_port);
}
if (6 == i)
{
struct blobmsg (
blobmsg_int32 ext_port,
) (src, info.tp_stream, false);
if (src.ext_port) ext_port = blobmsg_get_u32(src.ext_port);
}
}
/* inner_port */
inner_port = fix_port[i];
/* add info */
if (NULL != (table = blobmsg_open_table(bBuf, NULL)))
{
blobmsg_add_u32(bBuf, "inner_port", inner_port);
blobmsg_add_string(bBuf, "status", status);
blobmsg_add_string(bBuf, "proto", proto);
blobmsg_add_string(bBuf, "desc", desc);
blobmsg_add_u32(bBuf, "ext_port", ext_port);
blobmsg_close_table(bBuf, table);
}
else
{
DBG_ERR("blobmsg_open_table failed.\n");
result = UPNP_OTHER_ERROR;
}
}
blobmsg_close_array(bBuf, array);
}
else
{
DBG_ERR("blobmsg_open_array failed.\n");
result = UPNP_OTHER_ERROR;
}
failed:
tpuci_close(uci_ctx);
common_end(bBuf, result);
blob_buf_free(&b_tmp);
pclose(fp);
for (i = 0; i < lines_cnt; i++)
{
SAFE_FREE(lines[i]);
}
return;
}
void udsd_get_pub_ip(struct ubus_app *svr_ubus_app, struct blob_buf* bBuf, struct blob_attr *msg)
{
int ret = 0;
int result = 0;
char pubip[20] = {0};
struct uci_context *uci_ctx = tpuci_open();
if(!uci_ctx) {
result = UPNP_OTHER_ERROR;
goto failed;
}
ret = tpuci_get_val_string(uci_ctx, pubip, sizeof(pubip), "upnpc", "pub_ip", "ip");
if (0 != ret){
DBG_ERR( "get upnpc pub_ip failed.\n");
result = UPNP_PARAM_PUBIP_ERROR;
goto failed;
}
blobmsg_add_string(bBuf, "ip", pubip);
failed:
tpuci_close(uci_ctx);
common_end(bBuf, result);
return;
}
void udsd_get_upnp_commstatus(struct ubus_app *svr_ubus_app, struct blob_buf* bBuf, struct blob_attr *msg)
{
int ret = 0;
int result = 0;
int timestamp = 0;
char desc[15] = {0};
char status_str[10] = {0};
char *comm[] = {"uhttpd", "rtsp", "onvif_service", "tp_live", "tp_vod", "tp_speaker", "tp_stream"};
struct uci_context *uci_ctx = tpuci_open();
if(!uci_ctx) {
result = UPNP_OTHER_ERROR;
goto failed;
}
void *array;
void *table;
int i = 0;
if (NULL != (array = blobmsg_open_array(bBuf, "comm_list")))
{
for (i = 0; i < 6; i++)
{
/* desc */
ret = tpuci_get_val_string(uci_ctx, desc, sizeof(desc), "upnpc", comm[i], "desc");
if (0 != ret){
DBG_ERR( "get upnpc desc failed.\n");
result = UPNP_PARAM_DESC_ERROR;
goto failed;
}
/* comm_status */
ret = tpuci_get_val_string(uci_ctx, status_str, sizeof(status_str), "upnpc", comm[i], "comm_status");
if (0 != ret){
DBG_ERR( "get upnpc comm_status failed.\n");
result = UPNP_PARAM_COMM_STATUS_ERROR;
goto failed;
}
/* timestamp */
ret = tpuci_get_val_int(uci_ctx, ×tamp, "upnpc", comm[i], "timestamp");
if (0 != ret){
DBG_ERR( "get upnpc timestamp failed.\n");
result = UPNP_PARAM_COMM_TIMESTAMP_ERROR;
goto failed;
}
/* add info */
if (NULL != (table = blobmsg_open_table(bBuf, NULL)))
{
blobmsg_add_string(bBuf, "desc", desc);
blobmsg_add_string(bBuf, "comm_status", status_str);
blobmsg_add_u32(bBuf, "timestamp", timestamp);
blobmsg_close_table(bBuf, table);
}
else
{
DBG_ERR("blobmsg_open_table failed.\n");
result = UPNP_OTHER_ERROR;
}
}
blobmsg_close_array(bBuf, array);
}
else
{
DBG_ERR("blobmsg_open_array failed.\n");
result = UPNP_OTHER_ERROR;
}
failed:
tpuci_close(uci_ctx);
common_end(bBuf, result);
return;
}
void udsd_set_upnp_commstatus(struct ubus_app *svr_ubus_app, struct blob_buf* bBuf, struct blob_attr *msg)
{
if (!bBuf) {
DBG_ERR( "arg err\n");
return;
}
int ret = 0;
int result = 0;
char *status_str = NULL;
char *desc = NULL;
int timestamp = 0;
time_t time_now = 0;
struct uci_context *uci_ctx = tpuci_open();
if (!msg || !uci_ctx) {
DBG_ERR( "msg || uci_ctx err\n");
result = UPNP_OTHER_ERROR;
goto failed;
}
struct blobmsg (
blobmsg_string comm_status,
blobmsg_string desc,
blobmsg_int32 timestamp
) (req, msg, false);
char *json_str = blobmsg_format_json(msg, true);
if (json_str) {
DBG_ERR("Received params: %s\n", json_str);
free(json_str);
}
if (!req.comm_status) {
DBG_ERR( "set upnpc blobmsg err\n");
result = UPNP_PARAM_COMM_STATUS_ERROR;
goto failed;
}
if (!req.timestamp) {
DBG_ERR( "set upnpc blobmsg err\n");
result = UPNP_PARAM_COMM_TIMESTAMP_ERROR;
goto failed;
}
if (!req.desc) {
DBG_ERR( "set upnpc blobmsg err\n");
result = UPNP_PARAM_DESC_ERROR;
goto failed;
}
status_str = blobmsg_get_string(req.comm_status);
desc = blobmsg_get_string(req.desc);
timestamp = blobmsg_get_u32(req.timestamp);
DBG_ERR( "status_str:%s\n",status_str);
DBG_ERR( "desc:%s\n",desc);
DBG_ERR( "timestamp:%d\n",timestamp);
/* comm_status */
if(strcmp(status_str, "failed") && strcmp(status_str, "success") && strcmp(status_str, "unknown")){
DBG_ERR( "upnpc config arg failed.\n");
result = UPNP_PARAM_COMM_STATUS_ERROR;
goto failed;
}
ret = tpuci_set_val(uci_ctx, status_str, "upnpc", desc, "comm_status", 1);
if (0 != ret){
DBG_ERR( "set upnp comm_status format uci err\n");
result = UPNP_PARAM_COMM_STATUS_ERROR;
goto failed;
}
/* timestamp */
time_now = time(NULL);
if (timestamp - time_now > 86400 || time_now - timestamp > 86400) {
DBG_ERR("timestamp too large or small.");
result = UPNP_PARAM_COMM_TIMESTAMP_ERROR;
goto failed;
}
ret = tpuci_set_val_int(uci_ctx, timestamp, "upnpc", desc, "timestamp", 1);
if (0 != ret){
DBG_ERR( "set upnp timestamp format uci err\n");
result = UPNP_PARAM_COMM_TIMESTAMP_ERROR;
goto failed;
}
failed:
tpuci_close(uci_ctx);
common_end(bBuf, result);
return;
}
/**************************************************************************************************/
/* PUBLIC_FUNCTIONS */
/**************************************************************************************************/
static const UDSD_MOD_CMD g_udsd_cmds_upnp[] = {
{UDSD_CMD_R, "get_upnp_info", udsd_get_upnp_info},
{UDSD_CMD_W, "set_upnp_info", udsd_set_upnp_info},
{UDSD_CMD_R, "get_upnp_status", udsd_get_upnp_status},
{UDSD_CMD_R, "get_pub_ip", udsd_get_pub_ip},
{UDSD_CMD_R, "get_upnp_commstatus", udsd_get_upnp_commstatus},
{UDSD_CMD_W, "set_upnp_commstatus", udsd_set_upnp_commstatus},
{UDSD_CMD_N, NULL, NULL}
};
UDSD_CMD_ADD(upnp, "upnp", g_udsd_cmds_upnp);
upnpc.config:config on_off 'upnpc_info'
option enabled 'on'
option mode 'auto'
config entry 'uhttpd'
option proto 'TCP'
option desc 'uhttpd'
option status 'off'
option ext_port '18443'
option comm_status 'failed'
option timestamp '1761115038'
config entry 'tp_stream'
option proto 'TCP'
option desc 'tp_stream'
option status 'off'
option ext_port '21443'
option comm_status 'failed'
option timestamp '1761115038'
config entry 'tp_live'
option proto 'TCP'
option desc 'tp_live'
option status 'off'
option ext_port '18445'
option comm_status 'failed'
option timestamp '1761115038'
config entry 'tp_vod'
option proto 'TCP'
option desc 'tp_vod'
option status 'off'
option ext_port '18446'
option comm_status 'failed'
option timestamp '1761115038'
config entry 'tp_speaker'
option proto 'TCP'
option desc 'tp_speaker'
option status 'off'
option ext_port '18447'
option comm_status 'failed'
option timestamp '1761115038'
config config 'pub_ip'
option ip 'null' 分析下为什么cat /etc/config/upnpc得到的结果不是预期的。
最新发布