Skip to content

Instantly share code, notes, and snippets.

@mentha
Created May 3, 2022 13:33
Show Gist options
  • Save mentha/9a12eefcb3b78659c51439dcd2fcf357 to your computer and use it in GitHub Desktop.
Save mentha/9a12eefcb3b78659c51439dcd2fcf357 to your computer and use it in GitHub Desktop.
convert arp packets to wake-on-lan packets
#!/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