Khởi động lại (reboot) modem cáp quang FPT

Khởi động lại modem cáp quang + Wi-Fi FPT do một công ty nào đó của Trung Quốc sản xuất. Dù modem cũng hỗ trợ Reboot Timer nhưng có vấn đề gì đó khiến modem không được ổn định khi bật tính năng này.

Ví dụ sau dành cho thiết bị Modem + Wi-Fi có tên Internet Hub AC1000C (Model: G-97RG6M) hỗ trợ 4 đầu ra LAN, Wi-Fi băng tần kép 2.4GHz (802.11b/g/n) và 5GHz (802.11 a/n/ac) – tôi được “tặng” khi đăng ký gói cáp quang FPT.

Dù thiết bị hoạt động khá ổn định nhưng tôi vẫn muốn khởi động lại thiết bị này mỗi tuần cho tươi mới. Vì modem nào cũng vậy, chạy lâu lâu rồi kiểu gì cũng sẽ có vấn đề.

Cách làm dưới đây về nguyên tắc không hề phụ thuộc bạn đang dùng modem nào bởi chúng chỉ khác nhau về giao diện web và/hoặc cú pháp dòng lệnh (nếu có). Hãy thay đổi tùy theo thiết bị bạn đang có.

Khởi động lại bằng SSH

Modem này hỗ trợ SSH. Để bật SSH, ta đăng nhập vào giao diện web tại địa chỉ http://192.168.1.1 như sau:

Đăng nhập vào modem (địa chỉ và tên user có thể khác với thiết bị của bạn)

Vào mục Security Setup › SSH, bật (enable) Local SSH lên, đừng có bật Remote SSH vì khá mạo hiểm. Cài đặt UsernamePassword đăng nhập (nên khác với user và password đăng nhập giao diện web).

Cấu hình SSH trên modem

Bạn có thể thử nghịch ngợm với SSH terminal này bằng lệnh ssh.

ssh icreativ@192.168.1.1

Lưu ý: Nếu bạn nhận được thông báo sau:

Unable to negotiate with 192.168.1.1 port 22: no matching key exchange method found. Their offer: diffie-hellman-group1-sha1,diffie-hellman-group14-sha1

Nguyên nhân là do SSH phiên bản mới mặc định không hỗ trợ thuật toán mã hóa “cũ”. Hãy thêm đoạn sau vào .ssh/config rồi thử lại.

Host 192.168.1.1
    KexAlgorithms +diffie-hellman-group1-sha1

SSH của modem không hỗ trợ SSH key và phải thao tác vài bước mới đến được shell để gọi lệnh reboot. Để tự động hóa, ta có thể dùng lệnh expect như sau:

reboot-modem.exp

#!/usr/bin/expect
# reboot-modem.exp
# @author: Nguyen Hong Hai

# Your username@host
spawn ssh icreativ@192.168.1.1
expect "password: "

# Your SSH password goes here
send -- "my-ugly-password\r"
expect "#ONT>"

# Get into shell
send "system/shell\r"
expect "#ONT/system/shell>"

# Perform the reboot command
send "/sbin/reboot\r"
expect eof

Chú ý ký tự \r (tương ứng phím Enter) sau các chuỗi mỗi lần gọi send.

Chạy script như sau:

expect ./reboot-modem.exp

Hoặc phân quyền executable cho script và chạy thẳng lệnh này luôn:

chmod +x reboot-modem.exp
./reboot-modem.exp

Khởi động lại bằng headline browser

Nếu không muốn dùng đến SSH thì ta vẫn còn một cách khác là thao tác trên giao diện web. Cụ thể chúng ta sẽ giả lập kết quả sau khi nút Reboot dưới đây bị ấn. Tôi định sử dụng Nightmare.js.

Nút khởi động (reboot) trên giao diện Web

Tuy nhiên điều khó khăn khi dùng nightmare js trong trường hợp này là toàn bộ giao diện bị nhúng trong một FRAME và có cơ chế bảo vệ không cho load riêng trang trong FRAME. Do giới hạn của công cụ, tôi không thể dò đến các nút để thao tác được.

Tuy nhiên sau khi xem qua mã nguồn JS, hóa ra quy trình reboot là thực hiện một POST request đến địa chỉ /GponForm/reboot_XForm kèm theo một biến XWebPageName mang giá trị reboot. Tất nhiên điều này chỉ hiệu lực khi ta đã login thành công.

Vì thế script sau sẽ thực hiện hai bước:

  • Đăng nhập giao diện web bằng Nightmare JS.
  • Thực hiện POST request với XMLHttpRequest.

Yêu cầu trước hết bạn cần cài đặt Node.Js, sau đó cài đặt NightmareJS như sau:

mkdir reboot-modem
cd reboot-modem
npm i nightmare --save-dev

Mã nguồn chương trình khởi động modem bằng JavaScript. Lưu ý thay USERNAMEPASSWORD bằng tài khoản đăng nhập modem qua web.

reboot-modem.js

/**
 * reboot-modem.js
 * @author Nguyen Hong Hai
 */
const LOGINURL = 'http://192.168.1.1/login.html';
const USERNAME = 'admin';
const PASSWORD = 'password';

const Nightmare = require('nightmare');
const nightmare = Nightmare({
    show: true
});

nightmare
    .goto(LOGINURL)

    // Perform the login operation
    .type('#username', USERNAME)
    .type('#password', PASSWORD)
    .click('#btn_login')
    .wait('#contentfrm')

    // Perform a POST request to reboot the modem
    .evaluate(() => {
        return new Promise((resolve, reject) => {
            var oXHR = new XMLHttpRequest();
            oXHR.open("POST", "/GponForm/reboot_XForm", true);
            oXHR.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
            oXHR.onreadystatechange = function() {
                if (oXHR.readyState === 4) {
                    if (oXHR.status === 200) {
                        resolve("Success!");
                    } else {
                        reject(oXHR.statusText);
                    }
                }
            }
            oXHR.send("XWebPageName=reboot");
        });
    })

    .end()
    .then(console.log)
    .catch(error => {
        console.error('Error:', error)
    });

Chạy script này như sau:

cd reboot-modem
node ./reboot-modem.js

Sau đó việc cuối cùng là tôi đặt (một trong hai) lệnh trên vào crontab trên thiết bị Raspberry Pi để lên lịch gọi mỗi tuần.

Cấu hình crontab khởi động lại modem 3 giờ sáng thứ 2 hàng tuần (dòng cuối)