This shows you the differences between two versions of the page.
Previous revisionLast revision | |||
— | fuss:isc_dhcpd [2018/02/01 09:11] – [Get Active Leases] office | ||
---|---|---|---|
Line 1: | Line 1: | ||
+ | ====== Debug DHCP Messages ====== | ||
+ | In order to see the messages passed back and forth between clients and the DHCP server, we can make use of '' | ||
+ | <code bash> | ||
+ | tcpdump -n -vvv -i eth0 port bootps or port bootpc | ||
+ | </ | ||
+ | |||
+ | where: | ||
+ | |||
+ | * '' | ||
+ | * '' | ||
+ | * '' | ||
+ | * '' | ||
+ | |||
+ | ====== Broadcast LDAP Server to Clients ====== | ||
+ | |||
+ | Add the following snippet to the ISC DHCPd configuration (usually at ''/ | ||
+ | < | ||
+ | option ldap-server code 095 = ip-address; | ||
+ | option ldap-server SERVER; | ||
+ | </ | ||
+ | where: | ||
+ | * '' | ||
+ | |||
+ | in order to advertise the LDAP server on the network. | ||
+ | |||
+ | ====== Get Active Leases ====== | ||
+ | |||
+ | The following script by [[http:// | ||
+ | |||
+ | <code python> | ||
+ | # | ||
+ | # Created by Canadian Luke: http:// | ||
+ | import datetime, bisect | ||
+ | |||
+ | DHCP_LEASE_FILE = '/ | ||
+ | |||
+ | def parse_timestamp(raw_str): | ||
+ | tokens = raw_str.split() | ||
+ | |||
+ | if len(tokens) == 1: | ||
+ | if tokens[0].lower() == ' | ||
+ | return ' | ||
+ | |||
+ | else: | ||
+ | raise Exception(' | ||
+ | |||
+ | elif len(tokens) == 3: | ||
+ | return datetime.datetime.strptime(' | ||
+ | ' | ||
+ | |||
+ | else: | ||
+ | raise Exception(' | ||
+ | |||
+ | |||
+ | def timestamp_is_ge(t1, | ||
+ | if t1 == ' | ||
+ | return True | ||
+ | |||
+ | elif t2 == ' | ||
+ | return False | ||
+ | |||
+ | else: | ||
+ | return t1 >= t2 | ||
+ | |||
+ | |||
+ | def timestamp_is_lt(t1, | ||
+ | if t1 == ' | ||
+ | return False | ||
+ | |||
+ | elif t2 == ' | ||
+ | return t1 != ' | ||
+ | |||
+ | else: | ||
+ | return t1 < t2 | ||
+ | |||
+ | |||
+ | def timestamp_is_between(t, | ||
+ | return timestamp_is_ge(t, | ||
+ | |||
+ | |||
+ | def parse_hardware(raw_str): | ||
+ | tokens = raw_str.split() | ||
+ | |||
+ | if len(tokens) == 2: | ||
+ | return tokens[1] | ||
+ | |||
+ | else: | ||
+ | raise Exception(' | ||
+ | |||
+ | |||
+ | 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(' | ||
+ | |||
+ | |||
+ | def parse_next_binding_state(raw_str): | ||
+ | tokens = raw_str.split() | ||
+ | |||
+ | if len(tokens) == 3: | ||
+ | return tokens[2] | ||
+ | |||
+ | else: | ||
+ | raise Exception(' | ||
+ | |||
+ | |||
+ | def parse_rewind_binding_state(raw_str): | ||
+ | tokens = raw_str.split() | ||
+ | |||
+ | if len(tokens) == 3: | ||
+ | return tokens[2] | ||
+ | |||
+ | else: | ||
+ | raise Exception(' | ||
+ | |||
+ | |||
+ | def parse_leases_file(leases_file): | ||
+ | valid_keys = { | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | } | ||
+ | |||
+ | 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 == ' | ||
+ | if not in_lease: | ||
+ | ip_address = tokens[1] | ||
+ | |||
+ | lease_rec = {' | ||
+ | in_lease = True | ||
+ | |||
+ | else: | ||
+ | raise Exception(' | ||
+ | |||
+ | elif key == ' | ||
+ | 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[' | ||
+ | |||
+ | if ip_address in leases_db: | ||
+ | leases_db[ip_address].insert(0, | ||
+ | |||
+ | else: | ||
+ | leases_db[ip_address] = [lease_rec] | ||
+ | |||
+ | lease_rec = {} | ||
+ | in_lease = False | ||
+ | |||
+ | elif in_failover: | ||
+ | in_failover = False | ||
+ | continue | ||
+ | else: | ||
+ | raise Exception(' | ||
+ | |||
+ | elif key in valid_keys: | ||
+ | if in_lease: | ||
+ | value = line[(line.index(key) + len(key)):] | ||
+ | value = value.strip().rstrip(';' | ||
+ | |||
+ | if callable(valid_keys[key]): | ||
+ | lease_rec[key] = valid_keys[key](value) | ||
+ | else: | ||
+ | lease_rec[key] = True | ||
+ | |||
+ | else: | ||
+ | raise Exception(' | ||
+ | |||
+ | else: | ||
+ | if in_lease: | ||
+ | raise Exception(' | ||
+ | |||
+ | if in_lease: | ||
+ | raise Exception(' | ||
+ | |||
+ | 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.second + (0 if n.microsecond < 500000 else 1)) | ||
+ | |||
+ | |||
+ | def lease_is_active(lease_rec, | ||
+ | return timestamp_is_between(as_of_ts, | ||
+ | lease_rec[' | ||
+ | |||
+ | |||
+ | 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, | ||
+ | retarray = [] | ||
+ | sortedarray = [] | ||
+ | |||
+ | for ip_address in leases_db: | ||
+ | lease_rec = leases_db[ip_address][0] | ||
+ | |||
+ | if lease_is_active(lease_rec, | ||
+ | ip_as_int = ipv4_to_int(ip_address) | ||
+ | insertpos = bisect.bisect(sortedarray, | ||
+ | sortedarray.insert(insertpos, | ||
+ | retarray.insert(insertpos, | ||
+ | |||
+ | return retarray | ||
+ | |||
+ | |||
+ | ############################################################################## | ||
+ | |||
+ | |||
+ | try: | ||
+ | dhcp_lease_file = open(DHCP_LEASE_FILE, | ||
+ | leases = parse_leases_file(dhcp_lease_file) | ||
+ | except IOError: | ||
+ | print 'Could not open lease file: ' + e | ||
+ | except e: | ||
+ | print 'Could not parse lease file: ' + e | ||
+ | finally: | ||
+ | dhcp_lease_file.close(); | ||
+ | |||
+ | now = timestamp_now() | ||
+ | report_dataset = select_active_leases(leases, | ||
+ | |||
+ | print(' | ||
+ | print(' | ||
+ | print(' | ||
+ | print(' | ||
+ | print(' | ||
+ | |||
+ | for lease in report_dataset: | ||
+ | print(' | ||
+ | format(lease[' | ||
+ | format(str((lease[' | ||
+ | format(lease[' | ||
+ | |||
+ | print(' | ||
+ | print(' | ||
+ | print(' | ||
+ | print(' | ||
+ | |||
+ | </ | ||
+ | |||
+ | ====== Force DDNS Name ====== | ||
+ | |||
+ | For dynamic DNS, ISC DHCP assigns IP addresses using names reported by the DNS client however, sometimes it is favourable to force the name to something custom using a static lease. This can be done by editing the DHCP configuration and creating a static lease with the '' | ||
+ | < | ||
+ | host test.internal { | ||
+ | hardware ethernet 7e: | ||
+ | fixed-address 192.16.1.20; | ||
+ | ddns-hostname " | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | will forcibly set the hostname to '' |