forked from dyninc/DHCPTest-python
-
Notifications
You must be signed in to change notification settings - Fork 0
/
dhcp_test.py
executable file
·198 lines (171 loc) · 7.27 KB
/
dhcp_test.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
#!/usr/bin/env python
'''
Script will load test a DHCP server by continuosly requesting address leases in a loop using randomly generated mac addresses. This will run serially as written, if you want to have multiple scripts running you will
need to run it in several processes. Be aware that if you run it in multiple processes you may run into a number of lease failures on your DHCP client
do to multiple discover packets hitting before a request hits thus several requests for the same address may occur. This is the normal behavior as in a real
setup a client would then retry several times in the event this occurs. (one thing you *MAY* need to do is set promiscuous for the pcap object -> the open_live call)
This is by no means a comprehensive DHCP test, just a little one off script to vefiy that a server is able to handle load numbers.
-Couple of pretty simple but sometimes forgotten notes:
*Make sure your DHCP server is reachable via it's IP (the client curring the script can see the subnet it's on)
*Make sure your DHCP server isn't attempting to lease its own ip (in other words don't assign an ip for testing within the lease range)
Usage: dhcp_test.py [DHCP server IP] [DHCP server port - Optional defaults to 67] [Number of Loops - Optional defaults to 1]
'''
from random import Random
from optparse import OptionParser
from pydhcplib.dhcp_packet import DhcpPacket
from pydhcplib.dhcp_network import DhcpClient
from pydhcplib.type_hw_addr import hwmac
from pydhcplib.type_ipv4 import ipv4
import socket
import sys
import time
import pcap
import struct
r = Random()
r.seed()
break_wait = 0
res = None
dhcp_ip = ''
# generamte a random mac address
def genmac():
i = []
for z in xrange(6):
i.append(r.randint(0,255))
return ':'.join(map(lambda x: "%02x"%x,i))
#generate a random xid
def genxid():
decxid = r.randint(0,0xffffffff)
xid = []
for i in xrange(4):
xid.insert(0, decxid & 0xff)
decxid = decxid >> 8
return xid
def get_packet(pktlen, data, timestamp):
global dhcp_ip
global break_wait
global res
if not data:
return
if data[12:14]=='\x08\x00':
decoded=decode_ip_packet(data[14:])
if decoded['source_address'] == dhcp_ip:
res = decoded['destination_address'] # take advantage of CNR using the new ip as the desination address...
break_wait = 1
# send the request to the server
def issueRequest(serverip, serverport, timeout, req):
global break_wait
global res
# Reset the global vars we will use here
break_wait = 0
res = None
client = DhcpClient(client_listen_port=67, server_listen_port=serverport)
client.dhcp_socket.settimeout(timeout)
if serverip == '0.0.0.0':
req.SetOption('flags',[128, 0])
req_type = req.GetOption('dhcp_message_type')[0]
pcap_obj = pcap.pcapObject()
dev = pcap.lookupdev()
pcap_obj.open_live(dev, 1600, 0, 100)
pcap_obj.setfilter("udp port 67", 0, 0)
sent = 0
while break_wait < 1:
if(sent < 1):
sent = 1
client.SendDhcpPacketTo(req,serverip,serverport)
if req_type == 3 or req_type == 7:
return
pcap_obj.dispatch(1, get_packet)
return res
#set up a dhcp packet, this defaults to the discover type
def preparePacket(xid=None,giaddr='0.0.0.0',chaddr='00:00:00:00:00:00',ciaddr='0.0.0.0', yiaddr='0.0.0.0', msgtype='discover',required_opts=[]):
req = DhcpPacket()
req.SetOption('op',[1])
req.SetOption('htype',[1])
req.SetOption('hlen',[6])
req.SetOption('hops',[0])
if not xid:
xid = genxid()
req.SetOption('xid',xid)
req.SetOption('giaddr',ipv4(giaddr).list())
req.SetOption('chaddr',hwmac(chaddr).list() + [0] * 10)
req.SetOption('ciaddr',ipv4(ciaddr).list())
if msgtype == 'request':
mt = 3
elif msgtype == 'release':
mt = 7
else:
mt = 1
if mt == 3:
req.SetOption('yiaddr', ipv4(yiaddr).list())
req.SetOption('request_ip_address', ipv4(yiaddr).list())
req.SetOption('dhcp_message_type',[mt])
return req
# decode the packect so we can get information such as the source address to verify if the reply is comeing from where we expect
def decode_ip_packet(s):
d={}
d['version']=(ord(s[0]) & 0xf0) >> 4
d['header_len']=ord(s[0]) & 0x0f
d['tos']=ord(s[1])
d['total_len']=socket.ntohs(struct.unpack('H',s[2:4])[0])
d['id']=socket.ntohs(struct.unpack('H',s[4:6])[0])
d['flags']=(ord(s[6]) & 0xe0) >> 5
d['fragment_offset']=socket.ntohs(struct.unpack('H',s[6:8])[0] & 0x1f)
d['ttl']=ord(s[8])
d['protocol']=ord(s[9])
d['checksum']=socket.ntohs(struct.unpack('H',s[10:12])[0])
d['source_address']=pcap.ntoa(struct.unpack('i',s[12:16])[0])
d['destination_address']=pcap.ntoa(struct.unpack('i',s[16:20])[0])
if d['header_len']>5:
d['options']=s[20:4*(d['header_len']-5)]
else:
d['options']=None
d['data']=s[4*d['header_len']:]
return d
# start of the global section, this is the "main" entry point
dhcp_ip = "0.0.0.0"
dhcp_port = 67
loops = 1
if len(sys.argv) != 3 and len(sys.argv) != 4 and len(sys.argv) != 5:
pass
print "Usage: dhcp_test.py [DHCP server IP] [DHCP server port - Optional defaults to 67] [Number of Loops - Optional defaults to 1]"
sys.exit(0)
elif len(sys.argv) != 4 and len(sys.argv) != 5:
loops = 1
dhcp_port = 67
dhcp_ip = sys.argv[1]
elif len(sys.argv) != 5:
loops = 1
dhcp_ip = sys.argv[1]
dhcp_port = int(sys.argv[2])
else:
loops = int(sys.argv[3])
dhcp_port = int(sys.argv[2])
dhcp_ip = sys.argv[1]
leases = {}
run_loops = loops
#run this as many times as needed to test your server
while run_loops > 0:
#get a mac address
mac = genmac()
# create a discovery packet
disc_packet = preparePacket(None, '0.0.0.0', mac, '0.0.0.0', '0.0.0.0', 'discover', [1,3,6,51])
#send the discover request to the server
ip_issued = issueRequest(dhcp_ip, dhcp_port, 4, disc_packet)
# use the returned discovered ip to create a request packet
req_packet = preparePacket(None, '0.0.0.0', mac, '0.0.0.0', ip_issued, 'request', [1,3,6,51])
#issue the actual lease request
res = issueRequest(dhcp_ip, dhcp_port, 4, req_packet)
#just print out if we get a bad lease reply
if ip_issued == '255.255.255.255':
print mac
print ip_issued
print "error getting lease"
else:
leases[ip_issued] = mac
run_loops = run_loops - 1
#pause before we release all of the addresses in case we want to view them in the DHCP server
entered = raw_input("Press 'Enter' key to continue...")
#loop through all our leases and tell the DHCP server we are done with them
for k, v in leases.iteritems():
rel_packet = preparePacket(None, '0.0.0.0', v, k, '0.0.0.0', 'release', [1,3,6,51])
ip_issued = issueRequest(dhcp_ip, dhcp_port, 4, rel_packet)