Alternative method scan subnet
This commit is contained in:
parent
a69c5ac5fb
commit
660fb66fc0
2 changed files with 357 additions and 2 deletions
|
|
@ -433,6 +433,65 @@ class SubnetService:
|
|||
logger.warning(f"Failed to determine interface: {str(e)}, defaulting to eth0")
|
||||
return "eth0"
|
||||
|
||||
def _get_prefix_from_ip_cmd(self, iface: str) -> Optional[int]:
|
||||
"""Пытается получить префикс подсети из вывода `ip addr`, если netifaces дал неточную маску."""
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["ip", "-o", "-f", "inet", "addr", "show", "dev", iface],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=False,
|
||||
timeout=2,
|
||||
)
|
||||
if result.returncode != 0 or not result.stdout:
|
||||
return None
|
||||
# пример строки: "3: wlp0s20f3 inet 10.1.1.31/24 brd 10.1.1.255 scope global dynamic noprefixroute wlp0s20f3"
|
||||
for part in result.stdout.split():
|
||||
if "/" in part and part.count(".") == 3:
|
||||
try:
|
||||
return int(part.split("/")[1])
|
||||
except ValueError:
|
||||
continue
|
||||
return None
|
||||
except Exception as e:
|
||||
logger.debug(f"ip addr prefix detection failed for {iface}: {e}")
|
||||
return None
|
||||
|
||||
def _get_prefix_from_proc_route(self, iface: str) -> Optional[int]:
|
||||
"""Читает маску для интерфейса из /proc/net/route (не требует прав netlink)."""
|
||||
try:
|
||||
with open("/proc/net/route") as f:
|
||||
for line in f:
|
||||
parts = line.strip().split()
|
||||
if len(parts) < 8 or parts[0] != iface:
|
||||
continue
|
||||
mask_hex = parts[7]
|
||||
try:
|
||||
mask_int = int(mask_hex, 16)
|
||||
except ValueError:
|
||||
continue
|
||||
# mask_int в host byte order, например FF000000 для 255.0.0.0
|
||||
mask_str = socket.inet_ntoa(mask_int.to_bytes(4, byteorder="little"))
|
||||
return ipaddress.ip_network(f"0.0.0.0/{mask_str}", strict=False).prefixlen
|
||||
return None
|
||||
except Exception as e:
|
||||
logger.debug(f"/proc/net/route prefix detection failed for {iface}: {e}")
|
||||
return None
|
||||
|
||||
def _get_prefix_from_broadcast(self, ip: str, broadcast: Optional[str]) -> Optional[int]:
|
||||
"""Вычисляет префикс из пары IP + broadcast, если она присутствует (часто точнее для 10.x)."""
|
||||
if not ip or not broadcast:
|
||||
return None
|
||||
try:
|
||||
ip_int = int(ipaddress.IPv4Address(ip))
|
||||
bcast_int = int(ipaddress.IPv4Address(broadcast))
|
||||
mask_int = ~(ip_int ^ bcast_int) & 0xFFFFFFFF
|
||||
mask = ipaddress.ip_network(f"0.0.0.0/{ipaddress.IPv4Address(mask_int)}", strict=False).prefixlen
|
||||
return mask
|
||||
except Exception as e:
|
||||
logger.debug(f"Broadcast-based prefix detection failed for {ip}/{broadcast}: {e}")
|
||||
return None
|
||||
|
||||
def _get_default_subnet(self) -> str:
|
||||
"""Автоматически определяет подсеть локального устройства.
|
||||
|
||||
|
|
@ -454,8 +513,15 @@ class SubnetService:
|
|||
ip_info = addrs[netifaces.AF_INET][0]
|
||||
ip = ip_info["addr"]
|
||||
netmask = ip_info["netmask"]
|
||||
# Преобразуем маску из строки (например, "255.255.255.0") в CIDR
|
||||
mask = ipaddress.ip_network(f"0.0.0.0/{netmask}", strict=False).prefixlen
|
||||
broadcast = ip_info.get("broadcast")
|
||||
# netifaces иногда отдаёт неточную маску (например, 255.0.0.0 для 10.x),
|
||||
# пробуем уточнить через `ip addr`, затем /proc/net/route, затем по broadcast, и только потом берём netmask.
|
||||
mask = (
|
||||
self._get_prefix_from_ip_cmd(iface)
|
||||
or self._get_prefix_from_proc_route(iface)
|
||||
or self._get_prefix_from_broadcast(ip, broadcast)
|
||||
or ipaddress.ip_network(f"0.0.0.0/{netmask}", strict=False).prefixlen
|
||||
)
|
||||
network = ipaddress.ip_network(f"{ip}/{mask}", strict=False)
|
||||
logger.info(f"Auto-detected subnet: {network} (IP: {ip}, Mask: {netmask} -> /{mask})")
|
||||
return str(network)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue