diff options
Diffstat (limited to 'extras/fap/dhcpd')
-rw-r--r-- | extras/fap/dhcpd/README.md | 9 | ||||
-rwxr-xr-x | extras/fap/dhcpd/module_lease.py | 78 | ||||
-rwxr-xr-x | extras/fap/dhcpd/server_dhcp.py | 27 |
3 files changed, 39 insertions, 75 deletions
diff --git a/extras/fap/dhcpd/README.md b/extras/fap/dhcpd/README.md new file mode 100644 index 0000000..308184b --- /dev/null +++ b/extras/fap/dhcpd/README.md @@ -0,0 +1,9 @@ +# DHCPD + +FAP carefully mimic ISC-DHCPD in regards to the exact bytes that needs to be sent to the Juniper platform in order to get ZTP (zero touch protocol) to play along. + +## Files +* DHCP_protocol_breakdown.txt - Describes each field in the DHCP packet +* module_craft_option.py - Creates the correct byte sequence for DHCP options (suboptions can be solved by chaining the class) +* module_lease.py - Provedes access to set/get info from the DB (NMS) +* server_dhcp.py - The whole shebang that responds to DHCP packets. diff --git a/extras/fap/dhcpd/module_lease.py b/extras/fap/dhcpd/module_lease.py index 0473579..a33ab61 100755 --- a/extras/fap/dhcpd/module_lease.py +++ b/extras/fap/dhcpd/module_lease.py @@ -19,10 +19,10 @@ import psycopg2.extras # settings settings = dict( db = dict( - user = 'fap', - password = '<sensored>', - dbname = 'fap', - host = 'localhost' + user = '<user>', + password = '<password>', + dbname = '<db>', + host = '<host>' ) ) @@ -31,66 +31,12 @@ connect_params = ("dbname='%s' user='%s' host='%s' password='%s'" % (settings['d conn = psycopg2.connect(connect_params) cur = conn.cursor(cursor_factory=psycopg2.extras.DictCursor) -class lease(object): - debug = False - - def __init__(self, identifiers): - if len(identifiers) > 0: # 1 or more identifiers - we're good to go - - # build query string - where_pieces = [] - for x in identifiers.items(): - where_pieces.append(str(x[0]) + " = '" + str(x[1]) + "'") - where = ' AND '.join(where_pieces) - select = "SELECT * FROM switches WHERE " + where + " LIMIT 1" - - if self.debug is True: - print('Executing query: ' + select) - - cur.execute(select) - - rows = cur.fetchall() - if len(rows) is 1: - if self.debug is True: - print('returned from DB:') - for key, value in rows[0].items(): - print('%s: %s' % (key, value)) - - self.row = rows[0] - else: - self.row = False - else: - print('Missing identifier parameter') - exit() - - def get_ip(self): - if self.row is not False: - return self.row['ip'] - else: - print('identifiers (%s) not found' % self.row) - return False - - def get_config(self): - if self.row is not False: - return self.row['config'] - else: - print('identifiers (%s) not found' % self.row) - return False - - def get_dict(self): - if self.row is not False: - return self.row - else: - print('identifiers (%s) not found' % self.row) - return False - - # # TESTING - Bruker ID fra DB-en som identifier, og kjører en query per lease.get_x() # class lease2(object): debug = False - hostname = False + sysname = False identifiers = False # identifiers = dict of field/values @@ -105,7 +51,7 @@ class lease2(object): for identifier in identifiers.items(): where_pieces.append(str(identifier[0]) + " = '" + str(identifier[1]) + "'") where = ' AND '.join(where_pieces) - select = "SELECT hostname FROM switches WHERE " + where + " LIMIT 1" + select = "SELECT sysname FROM switches WHERE " + where + " LIMIT 1" if self.debug is True: print('Executing query: ' + select) @@ -117,19 +63,19 @@ class lease2(object): if self.debug is True: print('returned from DB:') print(rows[0][0]) - self.hostname = rows[0][0] + self.sysname = rows[0][0] else: - self.hostname = False + self.sysname = False else: print('Missing identifier parameter') exit() # Used to fetch fields from DB def get(self, field): - if self.hostname is not False: + if self.sysname is not False: cur = conn.cursor(cursor_factory=psycopg2.extras.DictCursor) - query = "SELECT %s FROM switches WHERE hostname = '%s' LIMIT 1" % (field, self.hostname) + query = "SELECT %s FROM switches WHERE sysname = '%s' LIMIT 1" % (field, self.sysname) if self.debug is True: print('Query: %s' % query) @@ -156,9 +102,9 @@ class lease2(object): # Used to set fields in DB def set(self, field, value): - if self.hostname is not False: + if self.sysname is not False: cur = conn.cursor(cursor_factory=psycopg2.extras.DictCursor) - query = "UPDATE switches SET %s = '%s' WHERE hostname = '%s'" % (field, value, self.hostname) + query = "UPDATE switches SET %s = '%s' WHERE sysname = '%s'" % (field, value, self.sysname) if self.debug is True: print('Query: %s' % query) try: diff --git a/extras/fap/dhcpd/server_dhcp.py b/extras/fap/dhcpd/server_dhcp.py index 592e1eb..438c505 100755 --- a/extras/fap/dhcpd/server_dhcp.py +++ b/extras/fap/dhcpd/server_dhcp.py @@ -236,13 +236,19 @@ def reqparse(message): print('[%s] --> Query details: distro_name:%s, distro_phy_port:%s' % (client, distro, phy.split('.')[0])) lease_identifiers = {'distro_name': distro, 'distro_phy_port': phy.split('.')[0]} - if lease(lease_identifiers).get('hostname') is not False: + print('### lease identifiers ###') + print(lease_identifiers) + if lease(lease_identifiers).get('sysname') is not False: + l={ - 'hostname': lease(lease_identifiers).get('hostname'), + 'sysname': lease(lease_identifiers).get('sysname'), 'mgmt_v4_addr': lease(lease_identifiers).get('mgmt_v4_addr'), 'mgmt_v4_gw': lease(lease_identifiers).get('mgmt_v4_gw'), 'mgmt_v4_cidr': lease(lease_identifiers).get('mgmt_v4_cidr') } + + print('### variabel l ###') + print(l) # lease_details = lease({'distro_name': distro, 'distro_phy_port': phy[:-2]}).get_dict() print('[%s] --> Data found, switch exists in DB - ready to craft response' % client) @@ -267,7 +273,9 @@ def reqparse(message): print('[%s] --> Client IP: %s' % (client, l['mgmt_v4_addr'])) print('[%s] --> DHCP forwarder IP: %s' % (client, l['mgmt_v4_gw'])) print('[%s] --> Client MAC: %s' % (client, client)) - + + fix_mgmt_v4_addr = l['mgmt_v4_addr'].split('/')[0] + data = b'\x02' # Message type - boot reply data += b'\x01' # Hardware type - ethernet data += b'\x06' # Hardware address length - 6 octets for MAC @@ -276,7 +284,7 @@ def reqparse(message): data += b'\x00\x00' # seconds elapsed - 1 second data += b'\x80\x00' # BOOTP flags - broadcast (unicast: 0x0000) data += b'\x00'*4 # Client IP address - data += socket.inet_aton(l['mgmt_v4_addr']) # New IP to client + data += socket.inet_aton(fix_mgmt_v4_addr) # New IP to client data += socket.inet_aton(dhcp_server_address) # Next server IP address data += socket.inet_aton(l['mgmt_v4_gw']) # Relay agent IP - DHCP forwarder data += binascii.unhexlify(messagesplit[11]) # Client MAC @@ -302,8 +310,9 @@ def reqparse(message): data += craft_option(51).raw_hex(b'\x00\x00\xa8\xc0') # Option 51 - Lease time left padded with "0" print('[%s] --> Option 51 (Lease time): %s' % (client, '43200 (12 hours)')) - data += craft_option(1).ip(cidr_to_subnet(l['mgmt_v4_cidr'])) # Option 1 - Subnet mask - print('[%s] --> Option 1 (subnet mask): %s' % (client, cidr_to_subnet(l['mgmt_v4_cidr']))) + # data += craft_option(1).ip(cidr_to_subnet(l['mgmt_v4_cidr'])) # Option 1 - Subnet mask + data += craft_option(1).ip(cidr_to_subnet(26)) # Option 1 - Subnet mask + print('[%s] --> Option 1 (subnet mask): %s' % (client, cidr_to_subnet(26))) data += craft_option(3).ip(l['mgmt_v4_gw']) # Option 3 - Default gateway (set to DHCP forwarders IP) print('[%s] --> Option 3 (default gateway): %s' % (client, l['mgmt_v4_gw'])) @@ -312,10 +321,10 @@ def reqparse(message): print('[%s] --> Option 150 (Cisco proprietary TFTP server(s)): %s' % (client, dhcp_server_address)) # http://www.juniper.net/documentation/en_US/junos13.2/topics/concept/software-image-and-configuration-automatic-provisioning-understanding.html - data += craft_option(43).bytes(craft_option(0).string(target_junos_file) + craft_option(1).string('/tg-edge/' + l['hostname']) + craft_option(3).string('http')) # Option 43 - ZTP + data += craft_option(43).bytes(craft_option(0).string(target_junos_file) + craft_option(1).string('/tg-edge/' + l['sysname']) + craft_option(3).string('http')) # Option 43 - ZTP print('[%s] --> Option 43 (Vendor-specific option):' % client) print('[%s] --> Suboption 0: %s' % (client, target_junos_file)) - print('[%s] --> Suboption 1: %s' % (client, '/tg-edge/' + l['hostname'])) + print('[%s] --> Suboption 1: %s' % (client, '/tg-edge/' + l['sysname'])) print('[%s] --> Suboption 3: %s' % (client, 'http')) data += b'\xff' @@ -326,7 +335,7 @@ def reqparse(message): if __name__ == "__main__": interface = b'eth0' - dhcp_server_address = '185.12.59.11' + dhcp_server_address = '185.110.148.22' target_junos_file = '/files/jinstall-ex-2200-14.1X53-D15.2-domestic-signed.tgz' # Setting up the server, and how it will communicate |