aboutsummaryrefslogtreecommitdiffstats
path: root/extras/fap/dhcpd
diff options
context:
space:
mode:
authorJonas Lindstad <jonaslindstad@gmail.com>2016-05-23 13:56:26 +0200
committerJonas Lindstad <jonaslindstad@gmail.com>2016-05-23 13:56:26 +0200
commitd7db901796438c811ab239ecbbee0ad0dd49832c (patch)
tree870021685ed17fbdc14e5b9b24cfe0687eeccf20 /extras/fap/dhcpd
parent34cebee827c1fb2410ea8551a5a3c3242084fc07 (diff)
Added all changes to FAP for TG16 + some documentation
Diffstat (limited to 'extras/fap/dhcpd')
-rw-r--r--extras/fap/dhcpd/README.md9
-rwxr-xr-xextras/fap/dhcpd/module_lease.py78
-rwxr-xr-xextras/fap/dhcpd/server_dhcp.py27
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