Задача:
Есть большое число указанных в теме маршрутизаторов, нужно по SNMP получать информацию о модели устройства, уровне сигнала, режиме работы и номере телефона, установленной и активной, в данной момент SIM-карты операторов Beeline / Мегафон / МТС.
Спецификация устройств может быть найдена на сайте производителя (
www.radiofid.ru).
При детальном рассмотрении - это маршрутизаторы на платформе с процессором ARM и обычным USB-модемом внутри корпуса устройства, в случае RUH2 или mini-PCIE в случае RUH2b. Прошивка устройств выполнена на основе Openwrt.
Сами модемы представляют из себя следующее:
[ RUH 2 ]
Manufacturer: huawei
Model: E173
Revision: 11.126.56.17.272
[ RUH2b Longcheer ]
Manufacturer: Manufacturer
Model: HSPA USB MODEM
Revision: LWA0021.1.2_MG37
[ RUH2b New ]
Manufacturer: Huawei Technologies Co., Ltd.
Model: MU709s-2
Revision: 11.652.65.00.00
Как видно, старый RUH2 / RUH2b новой серии - это нормальный радио-модуль производства Huawei, а старая серия RUH2b - это китайский noname-модуль.
К сожалению встроенное ПО - это mini-snmpd входящий в состав не самого свежего Openwrt и не позволяющий получать информацию о уровне сигнала и, тем более, о номере телефона, т.е из внешних скриптов.
Основной идеей решения задачи было получение:
- модели устройства из внутренних файлов прошивки модема;
- уровень сигнала из встроенной в ПО утилиты (gsminfo / gsminfod) для Huawei и прямых AT-комманд для Longcheer ;
- получению номера телефона при помощи USSD и разбора ответа приходящего по SMS либо в виде USSD-reply;
- отображение полученной информации в SNMP description устройства для дальшейшего разбора системой мониторинга.
Проблемы:
1. USSD при помощи встроенной утилиты ПО работают, только когда радио-модуль находится в 3G, что не всегда возможно;
[Решение]: USSD - запрос выполняется при помощи AT-комманд chat-скриптом.
2. Радио-модуль Huawei MU709s-2 хранит SMS только на установленной в него SIM-карте, в отличие от остальных модемов;
[Решение]: Проблема была изложена производителю, в ответ получена прошивка с исправлением.
3. Вывод встроенной утилиты gsminfo (задвоение данных, ошибки в определение режима работы и пр.)
[Решение]: Старался максимально учесть все в скриптах, но работает мягко скажем не идеально.
Решение - набор скриптов запускаемых по cron, при этом нужно учесть, что слишком часто выполнять USSD запросы нельзя.
Для работы скриптов желательно задать hostname вручную.
1. irz-signal (определение уровня сигнала и режима работы / добавление номера телефона, если он есть / формирование SNMP-description)
#!/bin/sh
# Signal info in SNMP-description (cron script)
# Last change: Wed Aug 30 12:11:25 GMT+3 2017
# Static variables
DEV=/dev/modem
LOCK=/var/lock/modem
GLBCFG=/etc/version
HNAMECFG=/mnt/rwfs/settings/hostname
OPCFG=/tmp/opinfo
USERIAL_FILE=/proc/tty/driver/usbserial
RMVNDR=''
TMP_FILE=/tmp/irz-signal.$$.out
# Dynamic variables
# Hostname
if [ -r $HNAMECFG ]; then
HOST=`cat "$HNAMECFG"`
else
logger -t user_signal -p daemon.error "You should setup hostname - file missing ($HNAMECFG)";
exit 1
fi
# Device type
if [ -r $GLBCFG ]; then
# Import variables from file
. "$GLBCFG"
else
logger -t user_signal -p daemon.error "Can't read global device's configuration ($GLBCGG)"
exit 1
fi
# Radio module vendor
if [ -r "${USERIAL_FILE}" ]; then
# Values:
# 12d1 - Huawei (RUH2/RUH2b)
# 1c9e - Longcheer / Longung / Noname (RUH2/RUH2b)
RMVNDR=`cat "${USERIAL_FILE}" | grep 'vendor:' | awk -F "vendor:" '{ print $2 }' | sort -u | awk '{ print $1 }'`
else
logger -t user_signal -p daemon.error "Can't read usb-serial driver configuration ($USERIAL_FILE)"
exit 0
fi
# Registration
if [ -x "`which sim_check_reg`" ]; then
sim_check_reg
# Return codes: 1 - home network , 5 - roaming , * - not registred
if [ $? -eq 1 ] || [ $? -eq 5 ] ; then
time_stamp=`date +"%d.%m.%y-%H:%M:%S"`
# Signal level & working mode
if [ "$RMVNDR" = "1c9e" ]; then
# Longcheer / Longung / Noname (RUH2/RUH2b)
csq=`talk -t $DEV -c "+CSQ" | awk -F ":" '{ print $2 }' | awk -F "," '{ print "-" $2 "dbm" }'`
mode=`talk -t /dev/modem -c "+PSRAT" | awk -F":" '{ print $2 }' | tr -d " " | tr [:upper:] [:lower:]`
else
# Huawei (RUH2/RUH2b)
# Ugly code for buggy gsminfo output
INFO_READ=0
RETRY_NUM=4
read_try=1
while [ ${INFO_READ} -ne 1 -a $read_try -le ${RETRY_NUM} ]
do
gsminfo > ${TMP_FILE}
lines_q=`grep -i 'quality' ${TMP_FILE} | wc -l`
lines_t=`grep -i 'type' ${TMP_FILE} | wc -l`
if [ ${lines_q} -ge 1 -a ${lines_t} -ge 1 ]; then
INFO_READ=1
csq=`grep -m 1 -i "quality" ${TMP_FILE} | sort -u | awk '{ print $3 "dbm" }'`
mode=`grep -m 1 -i "type" ${TMP_FILE} | sort -u | awk -F":" '{ print $2 }' | tr -d " " | tr [:upper:] [:lower:]`
break
else
read_try=`expr $read_try + 1`
rm -rf /tmp/irz-signal.*
fi
done
[ ${INFO_READ} -ne 1 ] && logger -t user_signal -p daemon.warn "Output of gsminfo parsed with errors - some parameters may be missed"
rm -rf /tmp/irz-signal.*
fi
# Operator
mop=`grep -i "operator" ${OPCFG} | awk -F":" '{ print $2 }' | awk '{ print $1 }'`
# Phone number injection
if [ -r /tmp/phonenumber ]; then
number=`cat /tmp/phonenumber`;
logger -t user_signal -p daemon.info "$HOST,$MODEL,$mop,$csq,$mode,$number,$time_stamp"
DESC_STRING=`echo -n "$HOST,$MODEL,$mop,$csq,$mode,$number,$time_stamp" | tr [:upper:] [:lower:] | base64 | tr -d "\n"`
else
logger -t user_signal -p daemon.info "$HOST,$MODEL,$mop,$csq,$mode,$time_stamp"
DESC_STRING=`echo -n "$HOST,$MODEL,$mop,$csq,$mode,$time_stamp" | tr [:upper:] [:lower:] | base64`
fi
grep 'SNMP' /mnt/rwfs/settings/settings.snmp > ${TMP_FILE} && mv ${TMP_FILE} /mnt/rwfs/settings/settings.snmp
sed -i '/SNMP_DESCRIPTION=/d' /mnt/rwfs/settings/settings.snmp > /dev/null 2>&1
echo "SNMP_DESCRIPTION=\"${DESC_STRING}\"" >> /mnt/rwfs/settings/settings.snmp
/etc/init.d/snmp stop > /dev/null 2>&1
/etc/init.d/snmp start > /dev/null 2>&1
exit 0
else
time_stamp=`date +"%d.%m.%y-%H:%M:%S"`
logger -t user_signal -p daemon.info "$HOST,$MODEL,not-registred,$time_stamp"
DESC_STRING=`echo -n "$HOST,$MODEL,not-registred,$time_stamp" | tr [:upper:] [:lower:] | base64`
grep 'SNMP' /mnt/rwfs/settings/settings.snmp > ${TMP_FILE} && mv ${TMP_FILE} /mnt/rwfs/settings/settings.snmp
sed -i '/SNMP_DESCRIPTION=/d' /mnt/rwfs/settings/settings.snmp > /dev/null 2>&1
sed -i "1a\SNMP_DESCRIPTION=\"$DESC_STRING\"" /mnt/rwfs/settings/settings.snmp > /dev/null 2>&1
/etc/init.d/snmp stop > /dev/null 2>&1
/etc/init.d/snmp start > /dev/null 2>&1
exit 0
fi
else
logger -t user_signal -p daemon.error "Can not determinate network registration - executable missing (sim_check_reg)";
exit 1
fi
# End
2. irz-getnumber (опеределение номера телефона и добавление его в Web-интерфейс устройства)
#!/bin/ash -
# Get phone number via USSD
# Last change: Wed Aug 30 12:11:25 GMT+3 2017
# Static variables
DEV=/dev/modem
GLBCFG='/etc/version'
OPMNC_FILE=/tmp/opnumber
USERIAL_FILE=/proc/tty/driver/usbserial
RMVNDR=''
# Dynamic variables
# Operator number
if [ -r "${OPMNC_FILE}" ]; then
OPMNC=`cat "${OPMNC_FILE}" | awk -F "=" '{ print $2 }'`
else
logger -t user_getnumber -p daemon.err "Can't read mobile network code (/tmp/opnumber)";
exit -1
fi
# Device type
if [ -r $GLBCFG ]; then
# Import variables from file
. "$GLBCFG"
else
logger -t user_getnumber -p daemon.error "Can't read global device's configuration ($GLBCGG)"
exit 0
fi
# Radio module vendor
if [ -r "${USERIAL_FILE}" ]; then
# Values:
# 12d1 - Huawei (RUH2/RUH2b)
# 1c9e - Longcheer / Longung / Noname (RUH2/RUH2b)
RMVNDR=`cat "${USERIAL_FILE}" | grep 'vendor:' | awk -F "vendor:" '{ print $2 }' | sort -u | awk '{ print $1 }'`
else
logger -t user_getnumber -p daemon.error "Can't read usb-serial driver configuration ($USERIAL_FILE)"
exit 0
fi
# Functions
pdunums2string () {
number=""
i=0
# Debug:
# echo "pdu: $1"
while [ $i -lt ${#1} ];
do
symbol=`echo -en "\x"${1:$i:2}`
if [ -n "${symbol}" ]; then
echo ${symbol} | grep -E "^[0-9]$" > /dev/null 2>&1
if [ $? -eq 0 -a ${#number} -lt 11 ]; then
number="${number}${symbol}"
# Debug:
# echo "symbol: ${symbol} / number: ${number}"
else
[ ${#number} -ge 10 ] || number=''
fi
fi
let "i += 2"
done
if [ ${#number} -lt 10 ]; then
logger -t user_getnumber -p daemon.warn "USSD-reply or SMS doesn't contain phone number";
exit 0 ;
else
[ ${#number} -eq 11 ] && number=${number:1}
logger -t user_getnumber -p daemon.info "Phone number is detected (${number})";
echo -n "${number}" > /tmp/phonenumber
sed -i '/Phone/d' /tmp/opinfo > /dev/null 2>&1
echo "Phone number: ${number}" >> /tmp/opinfo
exit 0 ;
fi
}
getpdufromsms () {
USSD_QRY=$1
USSD_WAIT=15
PDU_READ=0
RETRY_NUM=4
ussd_try=1
talk -t ${DEV} -c +CMGD=0,4 &&
while [ ${PDU_READ} -ne 1 -a $ussd_try -le ${RETRY_NUM} ]
do
ussd ${DEV} ${USSD_QRY} > /dev/null 2>&1
for sms_try in `seq ${RETRY_NUM}`; do
# SMS number may be starting from 0 or 1
sms_num=`sms list | head -n 1 | tr -d " \n"`
sms read ${sms_num} | grep REC > /dev/null 2>&1
if [ $? -eq 0 ]; then
echo -n 'PDU=' && sms read ${sms_num} | tail -n 1
PDU_READ=1
break
else
sleep ${USSD_WAIT}
fi
done
ussd_try=`expr $ussd_try + 1`
done
}
getpdufromussd () {
USSD_QRY=$1
USSD_WAIT=15
PDU_READ=0
RETRY_NUM=4
ussd_try=1
LOCK_FILE="/var/lock/modem"
CHAT_FILE="/tmp/irz-getnumber.$$.chat"
echo "ABORT ERROR" > ${CHAT_FILE}
echo "ABORT BUSY" >> ${CHAT_FILE}
echo "REPORT CUSD:" >> ${CHAT_FILE}
echo "'' AT\+CUSD=1,${USSD_QRY},15" >> ${CHAT_FILE}
echo "TIMEOUT ${USSD_WAIT}" >> ${CHAT_FILE}
echo "CUSD:" >> ${CHAT_FILE}
TMP_FILE="/tmp/irz-getnumber.$$.out"
opname=$2
while [ ${PDU_READ} -ne 1 -a $ussd_try -le ${RETRY_NUM} ]
do
lockfile-create --retry 5 ${LOCK_FILE}
if [ $? -eq 0 ]; then
chat -v -r ${TMP_FILE} -t 5 -f ${CHAT_FILE} > ${DEV} < ${DEV}
exsts=$?
if [ ${exsts} -eq 0 ]; then
PDU_READ=1
cat ${TMP_FILE} | grep 'CUSD:' | awk -F"," '{ print $2 }' | tr -d "\""
lockfile-remove ${LOCK_FILE}
break
else
logger -t user_getnumber -p daemon.err "Chat script to ${DEV} failed with err-code ${exsts} - attempt ${ussd_try}/${RETRY_NUM}";
fi
lockfile-remove ${LOCK_FILE}
else
logger -t user_getnumber -p daemon.err "Can't create lock-file for ${DEV} - attempt ${ussd_try}/${RETRY_NUM} ";
fi
ussd_try=`expr $ussd_try + 1`
sleep ${USSD_WAIT}
done
[ ${PDU_READ} -ne 1 ] && logger -t user_getnumber -p daemon.err "USSD sending or reply receiving failed due previous errors ($opname)";
rm -rf /tmp/irz-getnumber.*
}
# Main
# Operator
case $OPMNC in
25099) OPNAME="beeline";
output=$( getpdufromsms "*110*10#" );
echo ${output} | grep "PDU" > /dev/null 2>&1 ;
if [ $? -eq 0 ]; then
pdu=`echo $output | awk -F "=" '{ print $2 }'` ;
pdunums2string "${pdu}";
else
logger -t user_getnumber -p daemon.err "USSD has been sent, but no SMS recieved ($OPNAME)";
exit 0 ;
fi ;;
25002) OPNAME="megafon";
if [ "$MODEL" = "RUH2b" ]; then
# RUH2b modules has to different radio-modules
case $RMVNDR in
12d1) pdu=$( getpdufromussd \"2A19AC3602\" "${OPNAME}" );;
1c9e) pdu=$( getpdufromussd "*205#" "${OPNAME}" );;
*) logger -t user_getnumber -p daemon.warn "Sorry, code to send USSD with this radio-module not emplemented yet ($OPNAME/$RMVNDR)";;
esac
else
pdu=$( getpdufromussd "2A19AC3602" "${OPNAME}" );
fi
[ ${#pdu} -ne 0 ] && pdunums2string "${pdu}";;
25001) OPNAME="mts-rus";
output=$( getpdufromsms "*111*0887#" );
echo ${output} | grep "PDU" > /dev/null 2>&1 ;
if [ $? -eq 0 ]; then
pdu=`echo $output | awk -F "=" '{ print $2 }'` ;
pdunums2string "${pdu}";
else
logger -t user_getnumber -p daemon.err "USSD has been sent, but no SMS recieved ($OPNAME)";
exit 0 ;
fi ;;
*) OPNAME="unknown";
logger -t user_getnumber -p daemon.err "Sorry, code to work with this operator not emplemented yet ($OPMNC)";
exit 0 ;;
esac
Скрипты должны работать c отсутсвием пересечения по времени выполнения по скольку используют блокирующий доступ к модему.
Рабочий пример usercrontab:
# Signal status
*/30 * * * * [ ! -f /tmp/noreboot ] && /mnt/rwfs/scripts/irz-signal
# Phone number detection
15 */2 * * * [ ! -f /tmp/noreboot ] && /mnt/rwfs/scripts/irz-getnumber
Полный набор скриптов доступен по ссылке (
irz-scripts).