다음과 같은 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;
...
}
서브넷당 활성 DHCP 임대 수를 추출하고 싶습니다. 이 예는 다음과 같습니다.
10.1.1.0/24: 1
10.1.2.0/24: 1
자, 여기에서 dhcpd.leases 파일에 대한 많은 질문을 보았는데, 대부분의 대답은 각 IP의 Mac 주소를 출력하는 것과 관련이 있는데, 이는 제가 여기서 찾고 있는 것이 아닙니다. 제가 시도한 명령의 문제점은 비활성 임대를 완전히 제외하는 방법을 모르고, 서브넷을 분할하는 방법을 모르고, 실제로 어떤 도구를 사용해야 하는지 모른다는 것입니다.
어떤 도움이라도 대단히 감사하겠습니다.
다음은 제가 시도한 몇 가지 사항입니다. 제가 올바른 길을 가고 있는지 확신할 수 없으며 몇 시간 동안 이 명령을 가지고 놀았습니다...
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
그러나 실제로는 내가 원하는 것을 수행하지 않습니다.
참고: 저는 pfSense(FreeBSD)를 사용하고 있습니다.
답변1
더 많은 연구 끝에 저는 이 Python 스크립트를 제 목적에 맞게 조정하기로 결정했습니다. https://askubuntu.com/questions/219609/how-do-i-show-active-dhcp-leases/553387#553387
또한 여기의 네트워크 토폴로지는 알려져 있고 고정되어 있으며 변경되지 않습니다. 따라서 제가 수행한 수학 중 일부는 이를 고려했지만 필요한 대로 쉽게 변경할 수 있습니다. 마지막으로 이 모든 것은 궁극적으로 Checkmk 모니터링 차트 스크립트에 대한 것이므로 Checkmk가 구문 분석해야 하는 방식으로 출력이 표시됩니다. 어쨌든 더 이상 고민하지 않고 다음과 같습니다.
#!/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)')