
Digamos que eu tenha o seguinte arquivo dhcpd.leases:
# The format of this file is documented in the dhcpd.leases(5) manual page.
# This lease file was written by isc-dhcp-4.3.6-P1
# authoring-byte-order entry is generated, DO NOT DELETE
authoring-byte-order little-endian;
lease 10.1.1.108 {
...
starts 3 2020/03/04 08:23:54;
ends 3 2020/03/04 13:49:03;
tstp 3 2020/03/04 13:49:03;
cltt 3 2020/03/04 08:23:54;
binding state free;
...
}
lease 10.1.1.109 {
...
starts 3 2020/03/04 13:43:00;
ends 3 2020/03/04 13:49:44;
tstp 3 2020/03/04 13:49:44;
cltt 3 2020/03/04 13:43:00;
binding state free;
...
}
lease 10.1.2.100 {
...
starts 6 2020/03/28 12:49:45;
ends 0 2020/03/29 12:49:45;
tstp 0 2020/03/29 12:49:45;
cltt 6 2020/03/28 12:49:45;
binding state active;
...
lease 10.1.1.104 {
...
starts 6 2020/03/28 18:53:45;
ends 0 2020/03/29 18:53:45;
tstp 0 2020/03/29 18:53:45;
cltt 6 2020/03/28 18:53:45;
binding state active;
...
}
Gostaria de extrair o número de concessões de DHCP ativas para cada sub-rede, algo assim neste exemplo:
10.1.1.0/24: 1
10.1.2.0/24: 1
Agora, tenho visto inúmeras perguntas aqui sobre o arquivo dhcpd.leases, mas a maioria das respostas está relacionada à saída do endereço Mac para cada IP, que não é o que estou procurando aqui. O problema com os comandos que tentei é que não consigo descobrir como excluir completamente as concessões inativas, não consigo descobrir como dividir as sub-redes e não consigo descobrir qual ferramenta realmente devo usar para isso.
Qualquer ajuda seria apreciada.
Aqui estão algumas coisas que tentei, não faço ideia se estou no caminho certo, estou brincando há horas com esses comandos...
egrep "lease|active|\}" /var/dhcpd/var/db/dhcpd.leases
sed -e '/lease/,/active/!d' /var/dhcpd/var/db/dhcpd.leases
awk 'BEGIN{FS="\n"; RS="}";} {print $2}' /var/dhcpd/var/db/dhcpd.leases
No entanto, realmente não faz o que eu quero.
Nota: estou no pfSense (FreeBSD).
Responder1
Tudo bem, depois de mais pesquisas, decidi adaptar este script Python para o meu propósito: https://askubuntu.com/questions/219609/how-do-i-show-active-dhcp-leases/553387#553387
Além disso, minha topologia de rede aqui é conhecida, fixa e não muda, então as pequenas contas que faço no final levam isso em consideração, mas você pode facilmente alterá-la para o que precisar. E, finalmente, como tudo isso foi basicamente para um script de gráfico de monitoramento Checkmk, a saída é apresentada da maneira que o Checkmk precisa para analisá-la. De qualquer forma, sem mais delongas, aqui está:
#!/usr/bin/python
import datetime, bisect
def left(s, amount):
return s[:amount]
def right(s, amount):
return s[-amount:]
def mid(s, offset, amount):
return s[offset:offset+amount]
def parse_timestamp(raw_str):
tokens = raw_str.split()
if len(tokens) == 1:
if tokens[0].lower() == 'never':
return 'never';
else:
raise Exception('Parse error in timestamp')
elif len(tokens) == 3:
return datetime.datetime.strptime(' '.join(tokens[1:]),'%Y/%m/%d %H:%M:%S')
else:
raise Exception('Parse error in timestamp')
def timestamp_is_ge(t1, t2):
if t1 == 'never':
return True
elif t2 == 'never':
return False
else:
return t1 >= t2
def timestamp_is_lt(t1, t2):
if t1 == 'never':
return False
elif t2 == 'never':
return t1 != 'never'
else:
return t1 < t2
def timestamp_is_between(t, tstart, tend):
return timestamp_is_ge(t, tstart) and timestamp_is_lt(t, tend)
def parse_hardware(raw_str):
tokens = raw_str.split()
if len(tokens) == 2:
return tokens[1]
else:
raise Exception('Parse error in hardware')
def strip_endquotes(raw_str):
return raw_str.strip('"')
def identity(raw_str):
return raw_str
def parse_binding_state(raw_str):
tokens = raw_str.split()
if len(tokens) == 2:
return tokens[1]
else:
raise Exception('Parse error in binding state')
def parse_next_binding_state(raw_str):
tokens = raw_str.split()
if len(tokens) == 3:
return tokens[2]
else:
raise Exception('Parse error in next binding state')
def parse_rewind_binding_state(raw_str):
tokens = raw_str.split()
if len(tokens) == 3:
return tokens[2]
else:
raise Exception('Parse error in next binding state')
def parse_leases_file(leases_file):
valid_keys = {
'starts': parse_timestamp,
'ends': parse_timestamp,
'tstp': parse_timestamp,
'tsfp': parse_timestamp,
'atsfp': parse_timestamp,
'cltt': parse_timestamp,
'hardware': parse_hardware,
'binding': parse_binding_state,
'next': parse_next_binding_state,
'rewind': parse_rewind_binding_state,
'uid': strip_endquotes,
'client-hostname': strip_endquotes,
'option': identity,
'set': identity,
'on': identity,
'abandoned': None,
'bootp': None,
'reserved': None,
}
leases_db = {}
lease_rec = {}
in_lease = False
in_failover = False
for line in leases_file:
if line.lstrip().startswith('#'):
continue
tokens = line.split()
if len(tokens) == 0:
continue
key = tokens[0].lower()
if key == 'lease':
if not in_lease:
ip_address = tokens[1]
lease_rec = {'ip_address' : ip_address}
in_lease = True
else:
raise Exception('Parse error in leases file')
elif key == 'failover':
in_failover = True
elif key == '}':
if in_lease:
for k in valid_keys:
if callable(valid_keys[k]):
lease_rec[k] = lease_rec.get(k, '')
else:
lease_rec[k] = False
ip_address = lease_rec['ip_address']
if ip_address in leases_db:
leases_db[ip_address].insert(0, lease_rec)
else:
leases_db[ip_address] = [lease_rec]
lease_rec = {}
in_lease = False
elif in_failover:
in_failover = False
continue
else:
raise Exception('Parse error in leases file')
elif key in valid_keys:
if in_lease:
value = line[(line.index(key) + len(key)):]
value = value.strip().rstrip(';').rstrip()
if callable(valid_keys[key]):
lease_rec[key] = valid_keys[key](value)
else:
lease_rec[key] = True
else:
raise Exception('Parse error in leases file')
else:
if in_lease:
raise Exception('Parse error in leases file')
if in_lease:
raise Exception('Parse error in leases file')
return leases_db
def round_timedelta(tdelta):
return datetime.timedelta(tdelta.days,
tdelta.seconds + (0 if tdelta.microseconds < 500000 else 1))
def timestamp_now():
n = datetime.datetime.utcnow()
return datetime.datetime(n.year, n.month, n.day, n.hour, n.minute,
n.second + (0 if n.microsecond < 500000 else 1))
def lease_is_active(lease_rec, as_of_ts):
return timestamp_is_between(as_of_ts, lease_rec['starts'],
lease_rec['ends'])
def ipv4_to_int(ipv4_addr):
parts = ipv4_addr.split('.')
return (int(parts[0]) << 24) + (int(parts[1]) << 16) + \
(int(parts[2]) << 8) + int(parts[3])
def select_active_leases(leases_db, as_of_ts):
retarray = []
sortedarray = []
for ip_address in leases_db:
lease_rec = leases_db[ip_address][0]
if lease_is_active(lease_rec, as_of_ts):
ip_as_int = ipv4_to_int(ip_address)
insertpos = bisect.bisect(sortedarray, ip_as_int)
sortedarray.insert(insertpos, ip_as_int)
retarray.insert(insertpos, lease_rec)
return retarray
myfile = open('/var/dhcpd/var/db/dhcpd.leases', 'r')
leases = parse_leases_file(myfile)
myfile.close()
now = timestamp_now()
report_dataset = select_active_leases(leases, now)
lan_leases = sum(map(lambda x : left(x['ip_address'],7)=='10.1.1.', report_dataset))
wlan_leases = sum(map(lambda x : left(x['ip_address'],8)=='10.20.1.', report_dataset))
wlan_guest_leases = sum(map(lambda x : left(x['ip_address'],8)=='10.20.2.', report_dataset))
wlan_appliance_leases = sum(map(lambda x : left(x['ip_address'],8)=='10.20.3.', report_dataset))
print('0 dhcp_leases lan_leases=' + str(lan_leases) + '|wlan_leases=' + str(wlan_leases) + '|wlan_guest_leases=' + str(wlan_guest_leases) + '|wlan_appliance_leases=' + str(wlan_appliance_leases) + ' ' + str(lan_leases) + ' LAN lease(s), ' + str(wlan_leases) + ' WLAN lease(s), ' + str(wlan_guest_leases) + ' WLAN_GUEST lease(s), ' + str(wlan_appliance_leases) + ' WLAN_APPLIANCE lease(s)')