Эта статья является дополнением моей предыдущий публикации о настройке домашнего роутера / файл-сервера. Здесь речь пойдет о проблеме автоматического переподключения к интернету при зависании 4G-модема. На оригинальность идеи не претендую, просто хочу поделиться с читателями своим решением.


В процессе постоянной работы, примерно раз в месяц, случалось так, что USB модем зависал. Это доставляло мне определенные неудобства: невозможность удаленно попасть на домашний компьютер, заранее поставить на закачку фильмы. Удаленно эту проблему устранить было невозможно, помогала только перезагрузка модема по питанию. Выход был единственным написать сценарий, который в определенное время проверяет доступность узла в интернете и при необходимости перезагружает модем.

В интернете я нашел несколько реализаций, но не одна из них у меня нормально не заработала. Поэтому я решил написать свой сторожевой таймер с преферансом и барышнями. За основу был взят скрипт из этой темы. Переписан, насколько мне позволяет моя квалификация, и дополнен новыми возможностями такими как внешние опции.

Установка и настройка USB 4G модема в CentOS 7.
Для начала необходимо скачать недостающие пакеты.
yum install usb_modeswitch usb_modeswitch-data

Подключить модем и посмотреть как он определяется в системе.
dmesg
ip a

Далее необходимо настроить интерфейс 4G модема.
vim /etc/sysconfig/network-scripts/ifcfg-wwp6s0u1i1

DEVICE="wwp6s0u1i1"
NAME="wwp6s0u1i1"
TYPE="Ethernet"
ONBOOT="yes"
BOOTPROTO="dhcp"
HWADDR="XX:XX:XX:XX:XX:XX"
NM_CONTROLLED="no"
DNS1=127.0.0.1
DNS2=127.0.0.1
DNS3=127.0.0.1
NOZEROCONF="yes"
IPV4_FAILURE_FATAL="no"
IPV6INIT="no"
ZONE="external"

Создаем скрипты для активации и де активации интернета при включении или отключении интерфейса.
vim /sbin/ifup-pre-local

#!/bin/bash
#
PREUP="/etc/sysconfig/network-scripts/pre-up-${1:6}"
if [ -x $PREUP ]; then
exec $PREUP
fi

vim /sbin/ifdown-pre-local

#!/bin/bash
#
PREDOWN="/etc/sysconfig/network-scripts/pre-down-$1"
if [ -x $PREDOWN ]; then
exec $PREDOWN
fi

vim /etc/sysconfig/network-scripts/pre-up-wwp6s0u1i1

#!/bin/bash
#
echo -en 'AT^NDISDUP=1,1,"internet.yota"\r\n' > /dev/ttyUSB0

vim /etc/sysconfig/network-scripts/pre-down-wwp6s0u1i1

#!/bin/bash
#
echo -en 'AT^NDISDUP=1,0,"internet.yota"\r\n' > /dev/ttyUSB0

Здесь ttyUSB0 это порт модема.

Поднимаем интерфейс и проверяем соединение.
ifup wwp6s0u1i1
ip a


Непосредственно сам скрипт
#!/bin/bash

export PATH="$PATH:/usr/sbin"
SN="$(basename "$0")"

function print_help() {
    printf "\n"
    printf "Использование: %s options...\n" "$SN"
    printf "Параметры:\n"
    printf "  -s         Проверяемый ресурс.\n"
    printf "  -i         Имя сетевого интерфейса.\n"
    printf "  -d         Шина и порт модема lsusb -t.\n"
    printf "  -n         Число ошибочных пингов.\n"
    printf "  -m         Маркер модема, из команды lsusb.\n"
    printf "  -h         Справка.\n"
    printf "\n"
}

# Если скрипт запущен без аргументов, открываем справку.
if [[ $# = 0 ]]; then
    print_help && exit 1
fi
while getopts ":s:i:d:n:m:h" opt ;
do
    case $opt in
        s) SITE=$OPTARG;
            ;;
        i) IF=$OPTARG;
            ;;
        d) DEV=$OPTARG;
            ;;
        n) EP=$OPTARG;
            ;;
        m) MM=$OPTARG;
            ;;
        h) print_help
            exit 1
            ;;
        *) printf "Неправильный параметр\n";
           printf "Для вызова справки запустите %s -h\n" "$SN";
            exit 1
            ;;
        esac
done

if [[ "$SITE" == "" ]] || [[ "$IF" == "" ]] || [[ "$DEV" == "" ]] || [[ "$EP" == "" ]] || [[ "$MM" == "" ]] ;  then
 printf "\n"
 printf "Одна или несколько опций не указаны.\n"
 printf "Для справки наберите: %s -h\n" "$SN"
 printf "\n"
 exit 1
fi

M="$(lsusb | grep -w "$MM")"  #строка модема из lsusb


if [[ "$M" != "" ]]; then   #если модем выбран, можно проверять пинги

  if grep -w -q "$IF" /proc/net/dev; then #проверяем наличие сетевого интерфейса
   printf "\n"
   printf "Проверка доступности %s через интерфейс %s\n" "$SITE" "$IF"
   printf "\n"
    if [[ "$EP" -ge 6 ]]; then
     printf "Число ошибочных пингов должно быть меньше или равно 5\n"
     exit 1
    else
     printf "Делаем пинги...\n"
     flag="0"
     for i in {1..5}; do #делаем 5 пингов до сервера
     timeout -k 2 -s TERM 16 ping -w 14 -s 8 -c 1 -I "$IF" "$SITE" || flag=$((flag+1)) && printf "пинг:%s/5 (ошибок:%s)\n" "$i" "$flag" #пинг не прошел - инкрементируем счетчик
      if (("$flag" >= "$EP")); then
       break
      else
       read -r -t 1 > /dev/null
      fi
     done
     printf "потерь пакетов: %s из %s\n" "$flag" "$i"
     printf "\n"

     if (("$flag" >= "$EP")); then #если потерь пакетов больше 2х
      M="$(lsusb | grep "$MM")"   #на всякий случай снова глянем - вдруг модем выдернули
      printf "Будет сброшен модем:\n"
      printf "%s\n" "$M" | cut -c 34-
      if ! [[ -d /sys/bus/usb/drivers/usb/"$DEV" ]]; then
       printf "Неверно указаны Bus и Port модема.\n"
       exit 1
      else
      ifdown "$IF" #деактивируем интерфейс
      printf "%s" "$DEV" > "/sys/bus/usb/drivers/usb/unbind" && printf "%s" "$DEV" > "/sys/bus/usb/drivers/usb/bind" #перезегрузка модема
#      read -r -t 1 > /dev/null
      ifup "$IF" #активируем интерфейс
      fi
     fi
    fi
  else
   printf "\n"
   printf "Интерфейс %s не существует\n" "$IF"
   printf "\n"
   exit 1
  fi
else
  printf "Модем %s не найден.\n" "$MM"
fi


Скрипт располагается в /usr/local/bin/.
Чтобы скрипт запускался автоматически, раз в пять минут, добавим задание в cron.
crontab -e

*/5 * * * * /usr/local/bin/watchdog -m Huawei -s ya.ru -i wwp6s0u2i1 -n 3 -d 1-1 > /dev/null 2>&1

Это вывод dmesg, на нем виден сброс модема при выполнении скрипта.
[181709.595498] option1 ttyUSB0: GSM modem (1-port) converter now disconnected from ttyUSB0
[181709.595568] option 1-1:1.0: device disconnected
[181709.595798] huawei_cdc_ncm 1-1:1.1 wwp6s0u2i1: unregister 'huawei_cdc_ncm' usb-0000:06:00.0-1, Huawei CDC NCM device
[181709.615005] option 1-1:1.0: GSM modem (1-port) converter detected
[181709.616597] usb 1-1: GSM modem (1-port) converter now attached to ttyUSB0
[181709.623449] usb 1-1: MAC-Address: 0c:5b:8f:27:9a:64
[181709.623958] huawei_cdc_ncm 1-1:1.1: cdc-wdm0: USB WDM device
[181709.624341] huawei_cdc_ncm 1-1:1.1 wwan0: register 'huawei_cdc_ncm' at usb-0000:06:00.0-1, Huawei CDC NCM device, XX:XX:XX:XX:XX:XX

Сразу скажу, скрипт очень далек от идеала, поэтому с радостью приму советы и обоснованную критику в свой адрес.
Отдельно хочу поблагодарить пользователя с Toster.ru под ником @AlekseyNemiro, за оказанную помощь в оптимизации скрипта.

UPD 22.01.16 Дополнил скрипт командой добавления пути в переменную PATH.
Будет ли Вам полезен данный скрипт?

Проголосовало 67 человек. Воздержалось 34 человека.

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

Комментарии (5)


  1. devpreview
    21.01.2016 14:27

    Просветите, пожалуйста, а все ли материнские платы умеют отключать питание USB?


    1. mihmig
      22.01.2016 15:26

      Там может не отключение питания, а сброс шины USB возможен. При удалённом доступе к компу на Windows «передёргивали» устройство путём отключения/подключения USB-хаба в диспетчере задач.