@@ -26,6 +26,40 @@ if not logger.handlers:
2626 logger .addHandler (handler )
2727logger .setLevel (logging .INFO )
2828
29+ def is_device_up (device ):
30+ # Use JSON output to reliably inspect link state
31+ proc = subprocess .run (["/sbin/ip" , "-j" , "link" , "show" , "dev" , device ], capture_output = True , text = True )
32+ try :
33+ link_json = json .loads (proc .stdout ) if proc .stdout else []
34+ except json .JSONDecodeError :
35+ link_json = []
36+
37+ if isinstance (link_json , list ) and link_json :
38+ li = link_json [0 ]
39+ if li .get ('operstate' ) == 'UP' or 'UP' in li .get ('flags' , []):
40+ return True
41+ return False
42+
43+ def get_device_ips (device ):
44+ ipv4 = []
45+ ipv6 = []
46+ # Use JSON output to reliably inspect addresses
47+ addr_proc = subprocess .run (["/sbin/ip" , "-j" , "addr" , "show" , "dev" , device ], capture_output = True , text = True )
48+ try :
49+ addrs = json .loads (addr_proc .stdout ) if addr_proc .stdout else []
50+ except json .JSONDecodeError :
51+ addrs = []
52+
53+ for ent in addrs :
54+ for info in ent .get ('addr_info' , []):
55+ family = info .get ('family' )
56+ local = info .get ('local' , '' )
57+ if family == 'inet' :
58+ ipv4 .append (local )
59+ elif family == 'inet6' :
60+ ipv6 .append (local )
61+ return ipv4 , ipv6
62+
2963def enable_interfaces (file ):
3064 u = EUci ()
3165 with open (os .path .join (out_dir , file ), 'r' ) as f :
@@ -55,24 +89,38 @@ def send_gratuitous_arp(file):
5589 with open (os .path .join (out_dir , file ), 'r' ) as f :
5690 interfaces = json .load (f )
5791 for interface in interfaces :
58- if 'ipaddr' in interfaces [interface ]:
59- device = device_map .get (interface )
60- if not device :
61- logger .error ("Can't send gratuitous ARP: no device found for interface %s" , interface )
62- continue
63- # It should not happen, but ipaddr can contain multiple IPs
64- ipaddr = interfaces [interface ]['ipaddr' ]
65- if isinstance (ipaddr , str ):
66- ipaddr = [ipaddr ]
67- for ip in ipaddr :
68- # Remove /mask if present
69- ip = ip .split ('/' )[0 ]
70- # Send gratuitous ARP to update switches ARP tables
71- aproc = subprocess .run (["/usr/bin/arping" , "-c" , "1" , "-U" , "-I" , device , ip ], capture_output = True , text = True )
72- if aproc .returncode == 0 :
73- logger .info ("Sending gratuitous ARP on interface %s (%s) for IP %s: success" , interface , device , ip )
74- else :
75- logger .error ("Sending gratuitous ARP on interface %s (%s) for IP %s: fail, %s" , interface , device , ip , aproc .stderr .strip ())
92+ device = device_map .get (interface )
93+ if not device :
94+ logger .error ("Can't send gratuitous ARP: no device found for interface %s" , interface )
95+ continue
96+ # Check if device is up
97+ max_attempts = 10
98+ ready = False
99+ # First check if device is up
100+ for _ in range (max_attempts ):
101+ if is_device_up (device ):
102+ ready = True
103+ break
104+ time .sleep (0.5 )
105+ if not ready :
106+ logger .error ("Can't send gratuitous ARP: device %s for interface %s is down" , device , interface )
107+ continue
108+ # Check if device has IP address
109+ for _ in range (max_attempts ):
110+ # Ignore IPv6 which has a different ARP tools (ndisc6)
111+ ipv4 , _ = get_device_ips (device )
112+ if len (ipv4 ) > 0 :
113+ for ip in ipv4 :
114+ # Send gratuitous ARP to update switches ARP tables
115+ # Wait for the device to be up and to have the IP address (timeout ~10s)
116+ proc = subprocess .run (["/usr/bin/arping" , "-U" , "-I" , device , "-c" , "3" , ip ], capture_output = True , text = True )
117+ if proc .returncode == 0 :
118+ logger .info ("Sent gratuitous ARP for IP %s on interface %s: success" , ip , interface )
119+ else :
120+ logger .info ("Sent gratuitous ARP for IP %s on interface %s: fail, %s" , ip , interface , proc .stderr .strip ())
121+ break
122+ time .sleep (0.5 )
123+
76124
77125def enable_hotspot_mac ():
78126 u = EUci ()
0 commit comments