Чому сценарій працює в Windows, але не працює в Linux?

Доброго дня всім. Ось такий сценарій

import socket
import os
import logging
import psutil

WOL_PORT = 9999
INTERFACE_NAME = "My Lan"
INTERFACE_NAME_ARCH = "enp37s0"

logging.basicConfig(format="%(levelname)s: %(asctime)s %(message)s", level=logging.INFO)
logger = logging.getLogger(__name__)

def get_ip_mac_address(interface_name: str) -> tuple:
    ip_addr = mac_addr = None

    for item in psutil.net_if_addrs()[interface_name]:
        addr = item.address

        if "." in addr:
            ip_addr = addr
        elif ("-" in addr or ":" in addr) and "::" not in addr:
            mac_addr = addr.replace(":", "-").upper()

    if not ip_addr or not mac_addr or ip_addr == "127.0.0.1":
        raise "Не удалось получить IP или MAC-адрес сетевого интерфейса"

    return ip_addr, mac_addr

def assemble_wol_packet(mac_address: str) -> str:
    return f'{"FF-" * 6}{(mac_address + "-") * 16}'

def check_is_wol_packet(raw_bytes: bytes, assembled_wol_packet: str) -> int:
    decoded_packet_data = "-".join(f"{byte:02x}" for byte in raw_bytes).upper() + "-"

    if decoded_packet_data == assembled_wol_packet:
        return 1

    return 0

def run_udp_port_listener(port: int, interface_name: str):
    ip_addr, mac_addr = get_ip_mac_address(interface_name)

    server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    server_socket.bind((ip_addr, port))
    logger.info(f"Listening on {ip_addr}:{port}")

    assembled_wol_packet = assemble_wol_packet(mac_addr)

    while True:
        data, _ = server_socket.recvfrom(1024)

        is_wol_packet = check_is_wol_packet(data, assembled_wol_packet)

        if is_wol_packet == 1:
            if os.name == "posix":
                os.system("reboot")
            elif os.name == "nt":
                os.system(
                    "shutdown -r -t 0 -f"
                )

if os.name == "posix":
    run_udp_port_listener(WOL_PORT, INTERFACE_NAME_ARCH)
elif os.name == "nt":
    run_udp_port_listener(WOL_PORT, INTERFACE_NAME)

Це чудово працює в Windows. Тобто, суть така, я починаю сценарій, він слухає порт 9999 (WOL). З телефону я надсилаю пакет до цього порту і, відповідно, залежно від ОС (це в умовах сценарію) виконується команда. Тож у Arch я запускаю, netstat -tulpan показує, що порт слухається, надсилаю пакет з телефону, і нічого не відбувається, хоча має бути перезавантаження. Я перевіряв команду окремо — os.system («reboot») працює. Підкажіть в чому проблема?

ПС. Брандмауер не встановлений, iptables -L все на ACCEPT.

Проблема була вирішена — необхідно було додати IP -адресу до сценарію телефону (sh в termux), який надсилає пакет

було

wol -p 9999 macaddress

стало

wol -p 9999 -h 192.168.0.2 macaddress

И все запрацювало :)

Всім дякую за допомогу!

Підписуйтеся на Telegram-канал «DOU #tech», щоб не пропустити нові технічні статті

👍ПодобаєтьсяСподобалось0
До обраногоВ обраному0
LinkedIn
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

перевірив в опенсузі. локально працює.
wol -p 9999 -h 192.168.1.105 8:3a:88:6c:38:61

INFO: 2024-11-06 09:55:09,513 enp1s0f1 08-3A-88-6C-38-61 : Listening on 192.168.1.105:9999
DEBUG: 2024-11-06 09:55:09,513 FF-FF-FF-FF-FF-...-08-3A-88-6C-38-61-

DEBUG: 2024-11-06 09:55:23,578 decoded_packet_data: FF-FF-FF-FF-FF-...-08-3A-88-6C-38-61-
DEBUG: 2024-11-06 09:55:23,578 1
INFO: 2024-11-06 09:55:23,578 reboot

якщо вимкнути файрвол # systemctl stop firewalld
то працює і з іншого компа в фізичній мережі
і з іншого компа в zerotier мережі (якщо слухати відповідний інтерфейс)

так може твоя система де перехоплює wol десь глибше? зроби не-wol пакет і надішли його.

Ну, якщо в тебе працюєте, то так, потрібно дивитись десь у Arch-i.
Дякую за допомогу!

UPD. Виявляється, Arch ставить stateful-firewall з деяким пакетом. І там є правила.
*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -p icmp -j ACCEPT
-A INPUT -m conntrack —ctstate RELATED,ESTABLISHED -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -p tcp -j REJECT —reject-with tcp-reset
-A INPUT -p udp -j REJECT —reject-with icmp-port-unreachable
-A INPUT -j REJECT —reject-with icmp-proto-unreachable
COMMIT

Не розумію, як це налаштувати, але відповідно до його правил зрозуміло, що він блокує.

Все як завжди просто. Додав до сценарію в телефоні
було

wol -p 9999 macaddress
стало
wol -p 9999 -h 192.168.0.2 macaddress
И все запрацювало :)

Всім дякую за допомогу!

1. WOL пакет відправляється за допомогою broadcast, тому перевірте чи не відкидається цей пакет роутером (деталі як це перевірити дивіться у документації до вашого обладнання). Також можна скористатися програмами, які дозволяють відправляти пакет до явно визначеного за IP-адресою хоста.
2. Wake-on-LAN потрібно явно включити або через BIOS/UEFI, або через відповідне налаштування у ОС. Для Linux можна подивитися і увімкнути через ethtool: «sudo ethtool enp37n0 | grep Wake-on» повинно повертати «Supports Wake-on: g» та «Wake-on: g». Якщо останне — щось інше, то увімкнути відповідну функцію можна так: «sudo ethtool -s enp37n0 wol g».

Edit: тільки зараз дійшло, що вам не потрібен сам функціонал WOL. Останній пункт можна зневажати.

1. Це перше, що я б подивився там, якби у Windows не працювало.А так працює ж.
Cпробую знайти так, щоб по IP.

Першим ділом понаставляй викликів logger.info у циклі після кожного рядка і дивись, що тобі приходить і чи це те, що ти очікуєш.
Оскільки, по твоїх словах, reboot працює і os.name той, який ти очікуєш, то першим місцем, яке треба перевірити, є те, що тобі приходить із server_socket.recvfrom та те, чи вірно працює check_is_wol_packet

Так треба, приблизно

def run_udp_port_listener(port: int, interface_name: str):
    logger.info()
    ip_addr, mac_addr = get_ip_mac_address(interface_name)
    logger.info()
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    logger.info()
    server_socket.bind((ip_addr, port))
    logger.info()
    logger.info(f'Listening on {ip_addr}:{port}')
    logger.info()

Якщо так, то винегред якийсь по суті

INFO: 2024-11-04 15:45:51,190 <function get_ip_mac_address at 0x000002E0E9ED1440>
INFO: 2024-11-04 15:45:51,192 <socket.socket fd=488, family=2, type=2, proto=0>
INFO: 2024-11-04 15:45:51,192 <socket.socket fd=488, family=2, type=2, proto=0>
INFO: 2024-11-04 15:45:51,193 <built-in method bind of socket object at 0x000002E0EA1869E0>
INFO: 2024-11-04 15:45:51,193 Listening on 192.168.0.2:9999
INFO: 2024-11-04 15:45:51,193 FF-FF-FF-FF-FF-FF-2C-F0-5D-D8-80-0A-2C-F0-5D-D8-80-0A-2C-F0-5D-D8-80-0A-2C-F0-5D-D8-80-0A-2C-F0-5D-D8-80-0A-2C-F0-5D-D8-80-0A-2C-F0-5D-D8-80-0A-2C-F0-5D-D8-80-0A-2C-F0-5D-D8-80-0A-2C-F0-5D-D8-80-0A-2C-F0-5D-D8-80-0A-2C-F0-5D-D8-80-0A-2C-F0-5D-D8-80-0A-2C-F0-5D-D8-80-0A-2C-F0-5D-D8-80-0A-2C-F0-5D-D8-80-0A-

Ні, я мав на увазі щось типу:

def run_udp_port_listener(port: int, interface_name: str):
    ip_addr, mac_addr = get_ip_mac_address(interface_name)

    server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    server_socket.bind((ip_addr, port))
    logger.info(f"Listening on {ip_addr}:{port}")

    assembled_wol_packet = assemble_wol_packet(mac_addr)
    logger.info(f'Assembled WOL packet: {assembled_wol_packet}')

    while True:
        data, _ = server_socket.recvfrom(1024)
        logger.info(f'Data from socket: {data}')

        is_wol_packet = check_is_wol_packet(data, assembled_wol_packet)
        logger.info(f'is_wol_packet: {is_wol_packet}')

        if is_wol_packet == 1:
            if os.name == "posix":
                os.system("reboot")
            elif os.name == "nt":
                os.system(
                    "shutdown -r -t 0 -f"
                )
Є підозра, що server_socket.recvfrom на лінуксі вертає трохи інше, ніж на віндовсі, і через те твій код не працює

Таке виводить

INFO: 2024-11-05 17:17:58,685 SRVSock: <socket.socket fd=3, family=2, type=2, proto=0, laddr=('0.0.0.0', 0)>
INFO: 2024-11-05 17:17:58,685 SRVSockBind: <built-in method bind of socket object at 0x76d92aa4f850>
INFO: 2024-11-05 17:17:58,685 Listening on 192.168.0.2:9999
INFO: 2024-11-05 17:17:58,685 Assembled WOL packet: FF-FF-FF-FF-FF-FF-2C-F0-5D-D8-80-0A-2C-F0-5D-D8-80-0A-2C-F0-5D-D8-80-0A-2C-F0-5D-D8-80-0A-2C-F0-5D-D8-80-0A-2C-F0-5D-D8-80-0A-2C-F0-5D-D8-80-0A-2C-F0-5D-D8-80-0A-2C-F0-5D-D8-80-0A-2C-F0-5D-D8-80-0A-2C-F0-5D-D8-80-0A-2C-F0-5D-D8-80-0A-2C-F0-5D-D8-80-0A-2C-F0-5D-D8-80-0A-2C-F0-5D-D8-80-0A-2C-F0-5D-D8-80-0A-
На Windows таке ж.

Так проблема не там, проблема нижче, у циклі while True
Що виводять

        logger.info(f'Data from socket: {data}')
        logger.info(f'is_wol_packet: {is_wol_packet}')

Тобто туди не дійшло, і відповідно нічого не прийшло на сервер. Тобто проблема десь в коннекшекні.

Хммм ...
Ось частина кода, як вставив

    while True:
        data, _ = server_socket.recvfrom(1024)
        logger.info(f"Data from socket: {data}")

        is_wol_packet = check_is_wol_packet(data, assembled_wol_packet)
        logger.info(f"is_wol_packet: {is_wol_packet}")

        if is_wol_packet == 1:
Виводить, як попереднє
❯ python3 'WoL 9999 [Reboot Arch].py'
INFO: 2024-11-06 00:38:11,320 SRVSock: <socket.socket fd=3, family=2, type=2, proto=0, laddr=('0.0.0.0', 0)>
INFO: 2024-11-06 00:38:11,320 SRVSockBind: <built-in method bind of socket object at 0x76a87ae4f850>
INFO: 2024-11-06 00:38:11,320 Listening on 192.168.0.2:9999
INFO: 2024-11-06 00:38:11,320 Assembled WOL packet: FF-FF-FF-FF-FF-FF-2C-F0-5D-D8-80-0A-2C-F0-5D-D8-80-0A-2C-F0-5D-D8-80-0A-2C-F0-5D-D8-80-0A-2C-F0-5D-D8-80-0A-2C-F0-5D-D8-80-0A-2C-F0-5D-D8-80-0A-2C-F0-5D-D8-80-0A-2C-F0-5D-D8-80-0A-2C-F0-5D-D8-80-0A-2C-F0-5D-D8-80-0A-2C-F0-5D-D8-80-0A-2C-F0-5D-D8-80-0A-2C-F0-5D-D8-80-0A-2C-F0-5D-D8-80-0A-2C-F0-5D-D8-80-0A-

Але перевірив на Windows. Коли пакет посилається з телефону, воно відображається в терміналі, тобто реагує, але Arch ні.

Хммм.... Ну тоді ще пара припущень
1 — перевір назву мережевого інтерфейсу у лінуксі, чи дійсно це «enp37s0»

2 — може у тебе якийся файрвол на лінуксі стоїть?

1. Тут copy/paste з терміналу. Але це ім’я спочатку мене дратує. Це як би якийсь псевдонім.
2. Нi, список установочных пакетов сам формировал. Та их и не так много ufw или firewalld вроде. Ни того, ни другого нет.
Список пакетів сам формував для встановлення в Arch. В метапакетах по типу plasma (тобто основа) їх нема. Та їх не так і багато — UFW та Firewalld здається. Нема ні першого, ні другого.

Ввводні — де Linux, в docker чи WSL на вінді ? Якщо так — там треба налаштовувати сам зовнішній коннекшн, там по різному forums.docker.com/...​o-external-network/118010. Якщо сервер показує, що усе ок — пнути його назовні з telnet наприклад.
telnet << IP address of ncat system >> 9999
www.redhat.com/...​blog/setting-tcp-listener
Швидше за усе мобліьна апка звертається не на ту адресу (IP або hostname [друге краще]), там можна портаблшотити сам коннекшн.

Та ні, якби в Docker або WSL було б логічно, що все не просто.
Arch окремо на диску.
Telnet не підходить, оскільки WOL працює на UDP.
Не може бути різним, бо, як я писав, що команда надсилається з телефону
wol -p 9999 макадрес
По -друге, та сама команда, і це той самий сценарій, скопійований у Arch на Windows працює.

nc -vz -u 8.8.8.8 9999
?
(8.8.8.8 — підставити аддресу свого сервера звісно)

макадрес

ip address show
?
Швидше за усе інша мак адресса назначена девайсу ОС-кою, якщо це дуал-бут, просто перевірити треба усі параметри один за одним.
P.S.

os.system("reboot«)

— оце ось, так запросто не спрацює, треба мати Root привілеї, та і в цілому якась кака кака. Такі штуки взагалі з розряду коннекшенів по SSH, та і швидше за усе десь по полісі заборонені політиками системи bbs.archlinux.org/viewtopic.php?id=261647 Це вам не вінда десктопна, яка і сама може сервер ребутнути, щоби перевірити ліцензії — це Unix. Тут логи чудово би додати — щоб зрозуміти що діється на сервері.

if os.name == «posix»:

воно швидше за усе не так, там щось своє.
В цілому opensource.com/...​article/19/7/reboot-linux

nc нічого не видає, коли сценарій запускається для прослуховування

MAC -адреса вірна

Залишаю в сценарії
import os
os.sysem("reboot")
виконую, пк перезавантажується
Це тому що я додав користувача до групи power
Так що тут все добре.

виконую в терміналі
python3
виконую в интерпретаторі
import os
os.name видає posix
import platform
platform.system выдаёт Linux

Я розумію, що це не windows, але все ж таки ставить у тупик.

Підписатись на коментарі