повна версія назви, мабуть, має звучати десь так: «автоматизація рутера з 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