blob: c82bc4890d372782a62908f8d8a6c3c2843df54b [file] [log] [blame]
ChetanGaonker42d75812016-06-06 16:32:52 -07001#
Chetan Gaonkercfcce782016-05-10 10:10:42 -07002# Copyright 2016-present Ciena Corporation
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
ChetanGaonker42d75812016-06-06 16:32:52 -07007#
Chetan Gaonkercfcce782016-05-10 10:10:42 -07008# http://www.apache.org/licenses/LICENSE-2.0
ChetanGaonker42d75812016-06-06 16:32:52 -07009#
Chetan Gaonkercfcce782016-05-10 10:10:42 -070010# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15#
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080016from scapy.all import *
17
18conf.verb = 0 # Disable Scapy verbosity
19conf.checkIPaddr = 0 # Don't check response packets for matching destination IPs
20
21class DHCPTest:
22
23 def __init__(self, seed_ip = '192.168.1.1', iface = 'veth0'):
24 self.seed_ip = seed_ip
25 self.seed_mac = self.ipToMac(self.seed_ip)
26 self.iface = iface
27 self.mac_map = {}
28 self.mac_inverse_map = {}
Chetan Gaonkerf72ca402016-05-02 16:29:32 -070029 self.bootpmac = None
30 self.dhcpresp = None
Chetan Gaonkerc11d3222016-05-11 17:39:36 -070031 self.servermac = None
Chetan Gaonker1dabecc2016-05-16 14:56:01 -070032 self.return_option = None
Chetan Gaonkerc11d3222016-05-11 17:39:36 -070033 self.after_T2 = False
Chetan Gaonker1dabecc2016-05-16 14:56:01 -070034 self.send_different_option = None
ChetanGaonker5b984cb2016-07-12 15:50:49 -070035 self.specific_lease = None
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080036
37 def is_mcast(self, ip):
38 mcast_octet = (atol(ip) >> 24) & 0xff
39 return True if mcast_octet >= 224 and mcast_octet <= 239 else False
40
Chetan Gaonker1f7c3f82016-03-08 12:17:37 -080041 def discover(self, mac = None, update_seed = False):
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080042 '''Send a DHCP discover/offer'''
43
44 if mac is None:
45 mac = self.seed_mac
46 if update_seed:
47 self.seed_ip = self.incIP(self.seed_ip)
48 self.seed_mac = self.ipToMac(self.seed_ip)
49 mac = self.seed_mac
Chetan Gaonkerf1483862016-05-06 14:14:31 -070050
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080051 chmac = self.macToChaddr(mac)
Chetan Gaonkerf72ca402016-05-02 16:29:32 -070052 self.bootpmac = chmac
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080053 L2 = Ether(dst="ff:ff:ff:ff:ff:ff", src=mac)
54 L3 = IP(src="0.0.0.0", dst="255.255.255.255")
55 L4 = UDP(sport=68, dport=67)
56 L5 = BOOTP(chaddr=chmac)
57 L6 = DHCP(options=[("message-type","discover"),"end"])
Chetan Gaonker49ef0852016-03-23 15:06:18 -070058 resp = srp1(L2/L3/L4/L5/L6, filter="udp and port 68", timeout=10, iface=self.iface)
ChetanGaonker42d75812016-06-06 16:32:52 -070059 self.dhcpresp = resp
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080060 try:
61 srcIP = resp.yiaddr
62 serverIP = resp.siaddr
63 except AttributeError:
ChetanGaonker42d75812016-06-06 16:32:52 -070064 log.info("Failed to acquire IP via DHCP for %s on interface %s" %(mac, self.iface))
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080065 return (None, None)
66
Chetan Gaonkercd86bdd2016-03-17 00:08:12 -070067 subnet_mask = "0.0.0.0"
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080068 for x in resp.lastlayer().options:
69 if(x == 'end'):
70 break
71 op,val = x
72 if(op == "subnet_mask"):
73 subnet_mask = val
74 elif(op == 'server_id'):
75 server_id = val
Chetan Gaonkerf1483862016-05-06 14:14:31 -070076
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080077 L5 = BOOTP(chaddr=chmac, yiaddr=srcIP)
Chetan Gaonkerf1483862016-05-06 14:14:31 -070078 L6 = DHCP(options=[("message-type","request"), ("server_id",server_id),
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080079 ("subnet_mask",subnet_mask), ("requested_addr",srcIP), "end"])
Chetan Gaonker49ef0852016-03-23 15:06:18 -070080 srp(L2/L3/L4/L5/L6, filter="udp and port 68", timeout=10, iface=self.iface)
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080081 self.mac_map[mac] = (srcIP, serverIP)
82 self.mac_inverse_map[srcIP] = (mac, serverIP)
83 return (srcIP, serverIP)
84
ChetanGaonker42d75812016-06-06 16:32:52 -070085 def only_discover(self, mac = None, desired = False, lease_time = False, multiple = False):
Chetan Gaonkerf72ca402016-05-02 16:29:32 -070086 '''Send a DHCP discover'''
87
88 if mac is None:
ChetanGaonker42d75812016-06-06 16:32:52 -070089 if multiple:
90 mac = RandMAC()._fix()
91 else:
92 mac = self.seed_mac
93
Chetan Gaonkerf72ca402016-05-02 16:29:32 -070094
95 chmac = self.macToChaddr(mac)
Chetan Gaonkerf1483862016-05-06 14:14:31 -070096 self.bootpmac = chmac
Chetan Gaonkerf72ca402016-05-02 16:29:32 -070097 L2 = Ether(dst="ff:ff:ff:ff:ff:ff", src=mac)
98 L3 = IP(src="0.0.0.0", dst="255.255.255.255")
99 L4 = UDP(sport=68, dport=67)
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700100 L5 = BOOTP(chaddr=chmac)
Chetan Gaonkerf72ca402016-05-02 16:29:32 -0700101 if desired:
102 L6 = DHCP(options=[("message-type","discover"),("requested_addr",self.seed_ip),"end"])
ChetanGaonker42d75812016-06-06 16:32:52 -0700103
Chetan Gaonkerc11d3222016-05-11 17:39:36 -0700104 elif lease_time:
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700105 L6 = DHCP(options=[("message-type","discover"),("lease_time",700),"end"])
ChetanGaonker42d75812016-06-06 16:32:52 -0700106
Chetan Gaonkerc11d3222016-05-11 17:39:36 -0700107 else:
108 L6 = DHCP(options=[("message-type","discover"),"end"])
ChetanGaonker42d75812016-06-06 16:32:52 -0700109
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700110
111 resp = srp1(L2/L3/L4/L5/L6, filter="udp and port 68", timeout=10, iface=self.iface)
112 if resp == None:
ChetanGaonker42d75812016-06-06 16:32:52 -0700113 return (None, None, mac, None)
114
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700115 self.dhcpresp = resp
116 for x in resp.lastlayer().options:
117 if(x == 'end'):
118 break
119 op,val = x
120 if(op == "message-type"):
ChetanGaonker42d75812016-06-06 16:32:52 -0700121
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700122 if(val == 2):
ChetanGaonker42d75812016-06-06 16:32:52 -0700123
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700124 try:
125 srcIP = resp.yiaddr
126 serverIP = resp.siaddr
127 except AttributeError:
ChetanGaonker42d75812016-06-06 16:32:52 -0700128 log.info("In Attribute error.")
129 log.info("Failed to acquire IP via DHCP for %s on interface %s" %(mac, self.iface))
130 return (None, None, None, None)
Chetan Gaonker1dabecc2016-05-16 14:56:01 -0700131
132 if self.return_option:
Chetan Gaonker717b2942016-05-13 17:42:59 -0700133 for x in resp.lastlayer().options:
134 if(x == 'end'):
135 break
136 op,val = x
ChetanGaonker42d75812016-06-06 16:32:52 -0700137
Chetan Gaonker717b2942016-05-13 17:42:59 -0700138 if op == "lease_time":
Chetan Gaonker1dabecc2016-05-16 14:56:01 -0700139 if self.return_option == 'lease':
Chetan Gaonker717b2942016-05-13 17:42:59 -0700140 return (srcIP, serverIP, mac, val)
141
142 elif op == "subnet_mask":
Chetan Gaonker1dabecc2016-05-16 14:56:01 -0700143 if self.return_option == 'subnet':
ChetanGaonker42d75812016-06-06 16:32:52 -0700144 return (srcIP, serverIP, mac, val)
Chetan Gaonker1dabecc2016-05-16 14:56:01 -0700145 elif op == "router":
146 if self.return_option == 'router':
ChetanGaonker42d75812016-06-06 16:32:52 -0700147 return (srcIP, serverIP, mac, val)
Chetan Gaonker1dabecc2016-05-16 14:56:01 -0700148 elif op == "broadcast_address":
149 if self.return_option == 'broadcast_address':
ChetanGaonker42d75812016-06-06 16:32:52 -0700150 return (srcIP, serverIP, mac, val)
Chetan Gaonker1dabecc2016-05-16 14:56:01 -0700151 elif op == "name_server":
152 if self.return_option == 'dns':
ChetanGaonker42d75812016-06-06 16:32:52 -0700153 return (srcIP, serverIP, mac, val)
Chetan Gaonkerc11d3222016-05-11 17:39:36 -0700154
ChetanGaonker42d75812016-06-06 16:32:52 -0700155
Chetan Gaonker1dabecc2016-05-16 14:56:01 -0700156 else:
ChetanGaonker42d75812016-06-06 16:32:52 -0700157 return (srcIP, serverIP, mac, None)
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700158 elif(val == 6):
ChetanGaonker42d75812016-06-06 16:32:52 -0700159 return (None, None, mac, None)
Chetan Gaonkerf72ca402016-05-02 16:29:32 -0700160
ChetanGaonker42d75812016-06-06 16:32:52 -0700161
Chetan Gaonkerc11d3222016-05-11 17:39:36 -0700162 def only_request(self, cip, mac, cl_reboot = False, lease_time = False, renew_time = False, rebind_time = False, unicast = False):
Chetan Gaonkerf72ca402016-05-02 16:29:32 -0700163 '''Send a DHCP offer'''
ChetanGaonker42d75812016-06-06 16:32:52 -0700164
Chetan Gaonkerf72ca402016-05-02 16:29:32 -0700165 subnet_mask = "0.0.0.0"
166 for x in self.dhcpresp.lastlayer().options:
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700167 if(x == 'end'):
168 break
169 op,val = x
170 if(op == "subnet_mask"):
171 subnet_mask = val
172 elif(op == 'server_id'):
173 server_id = val
Chetan Gaonkerf72ca402016-05-02 16:29:32 -0700174
Chetan Gaonkerc11d3222016-05-11 17:39:36 -0700175 if unicast and self.servermac:
176 L2 = Ether(dst=self.servermac, src=mac)
177 L3 = IP(src=cip, dst=server_id)
ChetanGaonker42d75812016-06-06 16:32:52 -0700178 else:
Chetan Gaonkerc11d3222016-05-11 17:39:36 -0700179 L2 = Ether(dst="ff:ff:ff:ff:ff:ff", src=mac)
180 if self.after_T2:
181 L3 = IP(src=cip, dst="255.255.255.255")
182 else:
183 L3 = IP(src="0.0.0.0", dst="255.255.255.255")
Chetan Gaonkerf72ca402016-05-02 16:29:32 -0700184 L4 = UDP(sport=68, dport=67)
ChetanGaonker42d75812016-06-06 16:32:52 -0700185
Chetan Gaonkerc11d3222016-05-11 17:39:36 -0700186 if self.after_T2 == True:
187 L5 = BOOTP(chaddr=self.bootpmac, ciaddr = cip)
188 else:
189
190 L5 = BOOTP(chaddr=self.bootpmac, yiaddr=cip)
ChetanGaonker42d75812016-06-06 16:32:52 -0700191
Chetan Gaonkerc11d3222016-05-11 17:39:36 -0700192 if cl_reboot or self.after_T2:
ChetanGaonker42d75812016-06-06 16:32:52 -0700193 L6 = DHCP(options=[("message-type","request"),("subnet_mask",subnet_mask), ("requested_addr",cip), "end"])
Chetan Gaonker1dabecc2016-05-16 14:56:01 -0700194 elif self.send_different_option:
195 if self.send_different_option == 'subnet':
196 L6 = DHCP(options=[("message-type","request"),("server_id",server_id),
197 ("subnet_mask",'255.255.252.0'), ("requested_addr",cip), "end"])
198 elif self.send_different_option == 'router':
199 L6 = DHCP(options=[("message-type","request"),("server_id",server_id),
200 ("subnet_mask",subnet_mask), ("router",'1.1.1.1'), ("requested_addr",cip), "end"])
201 elif self.send_different_option == 'broadcast_address':
202 L6 = DHCP(options=[("message-type","request"),("server_id",server_id),
203 ("subnet_mask",subnet_mask), ("broadcast_address",'1.1.1.1'), ("requested_addr",cip), "end"])
204
205 elif self.send_different_option == 'dns':
206 L6 = DHCP(options=[("message-type","request"),("server_id",server_id),
207 ("subnet_mask",subnet_mask), ("name_server",'1.1.1.1'), ("requested_addr",cip), "end"])
ChetanGaonker42d75812016-06-06 16:32:52 -0700208
ChetanGaonker5860c182016-07-05 16:33:06 -0700209 elif self.specific_lease:
210 L6 = DHCP(options=[("message-type","request"), ("server_id",server_id),
211 ("subnet_mask",subnet_mask), ("requested_addr",cip),("lease_time",self.specific_lease), "end"])
Chetan Gaonkerc11d3222016-05-11 17:39:36 -0700212 else:
ChetanGaonker5860c182016-07-05 16:33:06 -0700213 L6 = DHCP(options=[("message-type","request"), ("server_id",server_id),
Chetan Gaonkerc11d3222016-05-11 17:39:36 -0700214 ("subnet_mask",subnet_mask), ("requested_addr",cip), "end"])
215
Chetan Gaonkerf72ca402016-05-02 16:29:32 -0700216 resp=srp1(L2/L3/L4/L5/L6, filter="udp and port 68", timeout=10, iface=self.iface)
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700217 if resp == None:
218 return (None, None)
Chetan Gaonker1dabecc2016-05-16 14:56:01 -0700219
220
221 self.servermac = resp.getlayer(Ether).src
222
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700223 for x in resp.lastlayer().options:
224 if(x == 'end'):
225 break
226 op,val = x
227 if(op == "message-type"):
ChetanGaonker42d75812016-06-06 16:32:52 -0700228
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700229 if(val == 5):
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700230 try:
231 srcIP = resp.yiaddr
232 serverIP = resp.siaddr
ChetanGaonker5860c182016-07-05 16:33:06 -0700233 self.mac_map[mac] = (srcIP, serverIP)
234 self.mac_inverse_map[srcIP] = (mac, serverIP)
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700235 except AttributeError:
ChetanGaonker42d75812016-06-06 16:32:52 -0700236 log.info("In Attribute error.")
237 log.info("Failed to acquire IP via DHCP for %s on interface %s" %(mac, self.iface))
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700238 return (None, None)
ChetanGaonker42d75812016-06-06 16:32:52 -0700239
ChetanGaonker5860c182016-07-05 16:33:06 -0700240 if lease_time or renew_time or rebind_time or self.specific_lease:
Chetan Gaonkerc11d3222016-05-11 17:39:36 -0700241 for x in resp.lastlayer().options:
242 if(x == 'end'):
243 break
244 op,val = x
ChetanGaonker42d75812016-06-06 16:32:52 -0700245
Chetan Gaonkerc11d3222016-05-11 17:39:36 -0700246 if op == "lease_time":
ChetanGaonker5860c182016-07-05 16:33:06 -0700247
248 if self.specific_lease:
249 return (srcIP, serverIP, val)
Chetan Gaonkerc11d3222016-05-11 17:39:36 -0700250 if lease_time == True:
251 self.mac_map[mac] = (srcIP, serverIP)
252 self.mac_inverse_map[srcIP] = (mac, serverIP)
253 return (srcIP, serverIP, val)
254 elif op == "renewal_time":
255 if renew_time == True:
256 self.mac_map[mac] = (srcIP, serverIP)
257 self.mac_inverse_map[srcIP] = (mac, serverIP)
258 return (srcIP, serverIP, val)
259 elif op == "rebinding_time":
260 if rebind_time == True:
261 self.mac_map[mac] = (srcIP, serverIP)
262 self.mac_inverse_map[srcIP] = (mac, serverIP)
263 return (srcIP, serverIP, val)
264 else:
265 self.mac_map[mac] = (srcIP, serverIP)
266 self.mac_inverse_map[srcIP] = (mac, serverIP)
267 return (srcIP, serverIP)
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700268 elif(val == 6):
ChetanGaonker42d75812016-06-06 16:32:52 -0700269
Chetan Gaonker1dabecc2016-05-16 14:56:01 -0700270 log.info("Got DHCP NAK.")
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700271 return (None, None)
ChetanGaonker42d75812016-06-06 16:32:52 -0700272
273
Chetan Gaonkerf72ca402016-05-02 16:29:32 -0700274
Chetan Gaonker1f7c3f82016-03-08 12:17:37 -0800275 def discover_next(self):
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -0800276 '''Send next dhcp discover/request with updated mac'''
Chetan Gaonker1f7c3f82016-03-08 12:17:37 -0800277 return self.discover(update_seed = True)
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -0800278
Chetan Gaonker1f7c3f82016-03-08 12:17:37 -0800279 def release(self, ip):
280 '''Send a DHCP discover/offer'''
281 if ip is None:
282 return False
283 if not self.mac_inverse_map.has_key(ip):
284 return False
285 mac, server_ip = self.mac_inverse_map[ip]
286 chmac = self.macToChaddr(mac)
287 L2 = Ether(dst="ff:ff:ff:ff:ff:ff", src=mac)
288 L3 = IP(src="0.0.0.0", dst="255.255.255.255")
289 L4 = UDP(sport=68, dport=67)
290 L5 = BOOTP(chaddr=chmac, ciaddr = ip)
291 L6 = DHCP(options=[("message-type","release"), ("server_id", server_ip), "end"])
292 sendp(L2/L3/L4/L5/L6, iface = self.iface)
293 del self.mac_map[mac]
294 del self.mac_inverse_map[ip]
295 return True
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -0800296
297 def macToChaddr(self, mac):
298 rv = []
299 mac = mac.split(":")
300 for x in mac:
301 rv.append(chr(int(x, 16)))
302 return reduce(lambda x,y: x + y, rv)
303
304 def get_ip(self, mac):
305 if self.mac_map.has_key(mac):
306 return self.mac_map[mac]
307 return (None, None)
308
309 def get_mac(self, ip):
310 if self.mac_inverse_map.has_key(ip):
311 return self.mac_inverse_map[ip]
312 return (None, None)
313
314 def ipToMac(self, ip):
315 '''Generate a mac from a ip'''
316
317 mcast = self.is_mcast(ip)
318 mac = "01:00:5e" if mcast == True else "00:00:00"
319 octets = ip.split(".")
320 for x in range(1,4):
321 num = str(hex(int(octets[x])))
322 num = num.split("x")[1]
323 if len(num) < 2:
324 num = "0" + str(num)
325 mac += ":" + num
326 return mac
327
328 def incIP(self, ip, n=1):
329 '''Increment an IP'''
330
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700331 if n < 1:
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -0800332 return ip
333 o = ip.split(".")
334 for ii in range(3,-1,-1):
335 if int(o[ii]) < 255:
336 o[ii] = str(int(o[ii]) + 1)
337 break
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700338 else:
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -0800339 o[ii] = str(0)
340
341 n -= 1
342 return self.incIP(".".join(o), n)