Created
May 3, 2022 13:33
-
-
Save mentha/9a12eefcb3b78659c51439dcd2fcf357 to your computer and use it in GitHub Desktop.
convert arp packets to wake-on-lan packets
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
# Send wake-on-lan packets to targets of ARP packets | |
MAX_AGE = 86400 * 7 | |
from sys import argv, stderr | |
from time import time | |
import os | |
import subprocess as sp | |
if len(argv) != 2: | |
print(f'usage: {argv[0]} <interface>', file=stderr) | |
exit(1) | |
intf = argv[1] | |
td = sp.Popen(['tcpdump', '--immediate-mode', '-l', '-enti', intf, | |
'arp or icmp6[icmp6type] = icmp6-neighborsolicit or icmp6[icmp6type] = icmp6-neighboradvert'], | |
stdin=sp.DEVNULL, stdout=sp.PIPE, universal_newlines=True) | |
class R: | |
def __init__(self, intf): | |
self.macs = {} | |
self.last_clean = 0 | |
self.intf = intf | |
self.ethers = {} | |
self.ethers_time = 0 | |
self.update_ethers() | |
def update_ethers(self): | |
st = None | |
try: | |
st = os.stat('/etc/ethers') | |
except FileNotFoundError: | |
self.ethers = {} | |
self.ethers_time = 0 | |
return | |
if st.st_mtime <= self.ethers_time: | |
return | |
self.ethers_time = st.st_mtime | |
self.ethers = {} | |
with open('/etc/ethers') as f: | |
for l in f.readlines(): | |
l = l.split() | |
mac = l[0] | |
ip = l[1] | |
self.ethers[ip] = mac | |
def clean(self): | |
now = time() | |
if now - self.last_clean < 60: | |
return | |
d = [] | |
for m in self.macs: | |
if now - self.macs[m]['time'] > MAX_AGE: | |
d.append(m) | |
for m in d: | |
del self.macs[d] | |
self.last_clean = now | |
def learn(self, ip, mac): | |
if ip in self.ethers: | |
return | |
print(f'learning {ip} is at {mac}') | |
self.macs[ip] = { | |
'mac': mac, | |
'time': time() | |
} | |
def wake(self, ip): | |
self.clean() | |
if ip in self.ethers: | |
print(f'waking {ip} at {self.ethers[ip]}') | |
self.wakemac(self.ethers[ip]) | |
elif ip in self.macs: | |
print(f'waking {ip} at {self.macs[ip]["mac"]}') | |
self.wakemac(self.macs[ip]['mac']) | |
else: | |
print(f'waking {ip} failed unknown mac') | |
def wakemac(self, mac): | |
sp.run(['etherwake', '-bi', self.intf, mac], stdin=sp.DEVNULL) | |
r = R(intf) | |
while True: | |
l = td.stdout.readline() | |
if not l: | |
break | |
l = l.strip() | |
src = l[0:17] | |
dst = l[20:37] | |
msg = l[40:].split(':', 1)[1].strip() | |
if 'who-has' in msg: | |
r.wake(msg.split()[2]) | |
elif 'who has' in msg: | |
r.wake(msg.split()[8]) | |
elif 'is-at' in msg: | |
m = msg.split() | |
r.learn(m[1], m[3]) | |
elif 'neighbor advertisement' in msg: | |
r.learn(msg.split()[8], src) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment