автоматизація рутера з openwrt

| щоденник, комп'ютери, openwrt, мережі, підказки, ssh

повна версія назви, мабуть, має звучати десь так: «автоматизація рутера з openwrt, або як увімкнути/вимкнути бездротову мережу (ssid) на маршрутизаторі openwrt з bash-скрипта», але це надто довго для заголовка. до того ж, це не буде справжня стаття, а лише нотатки собі на згадку, бо моя перша спроба вийшла трохи на «пшик», і треба не позабувати деталі до того часу, коли я матиму час і наснагу переробити справу як слід.

задача: автоматично вимикати загальну мережу (якою користуються діти) ввечері о 21:00 з неділі по четвер, і знову вмикати наступного ранку о 6:00.

ssh до рутера

отже, що маю? є тунель ssh до сервера в локальній мережі, сервер, у свою чергу, котрий підключений мережевим кабелем до маршрутизатора з прошивкою openwrt і має безпарольний (сертифікати) тунель ssh до нього (можна було б просто на рутері все зробити, але так цікавіше). відтак, запускати команди на рутері з сервера дуже просто:

ssh user@router.lan <команда>

замість user має бути користувач з відповідними правами, замість router.lan — інше ім’я вузла чи просто ip рутера.

uci show

openwrt має конфігуратор для командного рядка — uci (посібник користувача). найпростіше, що може uci — показати конфігурацію рутера (uci show); мені потрібна лише секція з налаштуванням бездротових мереж (wireless):

ssh user@router.lan uci show wireless

результат на екрані приблизно такий:

wireless.radio0=wifi-device
wireless.radio0.type='mac80211'
wireless.radio0.hwmode='11g'
wireless.radio0.path='platform/ar934x_wmac'
wireless.radio0.htmode='HT20'
wireless.radio0.txpower='21'
wireless.radio0.country='CA'
wireless.radio0.channel='auto'
...
wireless.@wifi-iface[2]=wifi-iface
wireless.@wifi-iface[2].device='radio0'
wireless.@wifi-iface[2].mode='ap'
wireless.@wifi-iface[2].ssid='guest_wifi'
wireless.@wifi-iface[2].key='guest_password'
wireless.@wifi-iface[2].network='guest'
wireless.@wifi-iface[2].encryption='psk2+tkip+ccmp'

тут є одна секція з налаштуванням радіомодулів (wireless.radio?), і друга, цікавіша — з налаштуванням бездротових інтерфейсів (wireless.@wifi-iface[?]). тих і інших може бути декілька.

uci set

змінити налаштування мережі wifi, приміром дезактивувати її, можна так:

ssh user@router.lan uci set wireless.@wifi-iface[2].disabled=1

це додасть до конфігурації відповідний рядок; якщо його видалити, або встановити .disabled в нуль — мережу буде знову активовано. номер мережі [2] «виник» з конфігурації вище для мережі guest_wifi:

wireless.@wifi-iface[2].ssid='guest_wifi'

commit чи не commit?

щоби записати змінену конфігурацію до енергонезалежної пам’яті рутера (nvram), потрібно зробити commit:

ssh user@router.lan uci commit

в більшости випадків цього не треба робити для простого увімкнення/вимкнення однієї мережі wifi; після перезавантаження рутера буде відновлено конфігурацію з nvram, в т.ч. стан wifi.

перезавантаження радіомодуля

а от що треба обов’язково зробити — то це перезавантажити радіомодуль, щоби зміни до конфігурації (незалежно від commit) було застосовано (саме тут я схибив у першій версії свого скрипта). для цього є команда wifi:

ssh user@router.lan wifi reload radio0

назва радіомодуля radio0 — з конфігурації для відповідної мережі:

wireless.@wifi-iface[2].device='radio0'

індекс мережі та радіомодуль

але як видобути номер мережі та назву радіомодуля з конфігурації за назвою мережі (ssid), щоби все це автоматизувати у скрипті bash?

по-перше, затягти конфігурацію бездротових мереж до масива UCI_DUMP (якщо це виглядає страшно — марш учити уроки bash, бо далі буде ще страшніше):

UCI_DUMP=();
while IFS= read -r line; do
  UCI_DUMP+=(${line});
done < <( ssh user@router.lan uci show wireless )

по-друге, знайти рядок з потрібним ssid (я додаю «зайві» крапко-коми, щоби можна було легше зібрати все в один рядок за потреби):

SSID="guest_wifi";
for ((i=0; i<${#UCI_DUMP[@]}; i++)); do 
    [[ ${UCI_DUMP[i]} = *"${SSID}"* ]] && SSID_LINE=${UCI_DUMP[i]};
done

по-третє, вирізати номер мережі й зберегти до SSID_ID:

TEMP=${SSID_LINE##*[};
SSID_ID=${TEMP%%]*}

по-четверте, знайти рядок .device з цим індексом мережі:

for ((i=0; i<${#UCI_DUMP[@]}; i++)); do
  [[ ${UCI_DUMP[i]} = *"[${SSID_ID}].device"* ]] && DEVICE_LINE=${UCI_DUMP[i]};
done

нарешті, вирізати назву радіомодуля до DEVICE_NAME:

TEMP=${DEVICE_LINE##*.device=\'};
DEVICE_NAME=${TEMP%%\'*}

накидати сюди трохи перевірок на порожні змінні (на випадок помилки в назві мережі тощо) — і можна зібрати скрипт для вмикання/вимикання бездротової мережі.

журнал

дрібничка, але важлива — журналювання (до /var/log/messages або /var/log/syslog залежно від налаштувань демона-журналіста), щось таке (в $ACTION завчасу покласти on/off залежно від операції):

logger "$( date +'%Y-%m-%d %H:%M' ) WIFI: ${ACTION} ${SSID} (ID ${SSID_ID}, device ${DEVICE_NAME})"

cron

коли скрипт готовий (і відтестований!) — можна запланувати в sudo crontab -e щось таке:

# WiFi scheduling for "guest_wifi"
# a) turn off at 21:00 on Sundays-Thursdays (0-4)
# b) turn back on at 6:00 on Mondays-Fridays (1-5)
0 21 * * 0-4 /home/user/.bin/wireless off "guest_wifi" >/dev/null 2>&1
0 6 * * 1-5 /home/user/.bin/wireless on "guest_wifi" >/dev/null 2>&1

зображення