blob: 2a760c29a371b2df5fde394a1db0a720d7a2f9d0 [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
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080035
36 def is_mcast(self, ip):
37 mcast_octet = (atol(ip) >> 24) & 0xff
38 return True if mcast_octet >= 224 and mcast_octet <= 239 else False
39
Chetan Gaonker1f7c3f82016-03-08 12:17:37 -080040 def discover(self, mac = None, update_seed = False):
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080041 '''Send a DHCP discover/offer'''
42
43 if mac is None:
44 mac = self.seed_mac
45 if update_seed:
46 self.seed_ip = self.incIP(self.seed_ip)
47 self.seed_mac = self.ipToMac(self.seed_ip)
48 mac = self.seed_mac
Chetan Gaonkerf1483862016-05-06 14:14:31 -070049
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080050 chmac = self.macToChaddr(mac)
Chetan Gaonkerf72ca402016-05-02 16:29:32 -070051 self.bootpmac = chmac
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080052 L2 = Ether(dst="ff:ff:ff:ff:ff:ff", src=mac)
53 L3 = IP(src="0.0.0.0", dst="255.255.255.255")
54 L4 = UDP(sport=68, dport=67)
55 L5 = BOOTP(chaddr=chmac)
56 L6 = DHCP(options=[("message-type","discover"),"end"])
Chetan Gaonker49ef0852016-03-23 15:06:18 -070057 resp = srp1(L2/L3/L4/L5/L6, filter="udp and port 68", timeout=10, iface=self.iface)
ChetanGaonker42d75812016-06-06 16:32:52 -070058 self.dhcpresp = resp
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080059 try:
60 srcIP = resp.yiaddr
61 serverIP = resp.siaddr
62 except AttributeError:
ChetanGaonker42d75812016-06-06 16:32:52 -070063 log.info("Failed to acquire IP via DHCP for %s on interface %s" %(mac, self.iface))
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080064 return (None, None)
65
Chetan Gaonkercd86bdd2016-03-17 00:08:12 -070066 subnet_mask = "0.0.0.0"
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080067 for x in resp.lastlayer().options:
68 if(x == 'end'):
69 break
70 op,val = x
71 if(op == "subnet_mask"):
72 subnet_mask = val
73 elif(op == 'server_id'):
74 server_id = val
Chetan Gaonkerf1483862016-05-06 14:14:31 -070075
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080076 L5 = BOOTP(chaddr=chmac, yiaddr=srcIP)
Chetan Gaonkerf1483862016-05-06 14:14:31 -070077 L6 = DHCP(options=[("message-type","request"), ("server_id",server_id),
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080078 ("subnet_mask",subnet_mask), ("requested_addr",srcIP), "end"])
Chetan Gaonker49ef0852016-03-23 15:06:18 -070079 srp(L2/L3/L4/L5/L6, filter="udp and port 68", timeout=10, iface=self.iface)
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080080 self.mac_map[mac] = (srcIP, serverIP)
81 self.mac_inverse_map[srcIP] = (mac, serverIP)
82 return (srcIP, serverIP)
83
ChetanGaonker42d75812016-06-06 16:32:52 -070084 def only_discover(self, mac = None, desired = False, lease_time = False, multiple = False):
Chetan Gaonkerf72ca402016-05-02 16:29:32 -070085 '''Send a DHCP discover'''
86
87 if mac is None:
ChetanGaonker42d75812016-06-06 16:32:52 -070088 if multiple:
89 mac = RandMAC()._fix()
90 else:
91 mac = self.seed_mac
92
Chetan Gaonkerf72ca402016-05-02 16:29:32 -070093
94 chmac = self.macToChaddr(mac)
Chetan Gaonkerf1483862016-05-06 14:14:31 -070095 self.bootpmac = chmac
Chetan Gaonkerf72ca402016-05-02 16:29:32 -070096 L2 = Ether(dst="ff:ff:ff:ff:ff:ff", src=mac)
97 L3 = IP(src="0.0.0.0", dst="255.255.255.255")
98 L4 = UDP(sport=68, dport=67)
Chetan Gaonkerf1483862016-05-06 14:14:31 -070099 L5 = BOOTP(chaddr=chmac)
Chetan Gaonkerf72ca402016-05-02 16:29:32 -0700100 if desired:
101 L6 = DHCP(options=[("message-type","discover"),("requested_addr",self.seed_ip),"end"])
ChetanGaonker42d75812016-06-06 16:32:52 -0700102
Chetan Gaonkerc11d3222016-05-11 17:39:36 -0700103 elif lease_time:
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700104 L6 = DHCP(options=[("message-type","discover"),("lease_time",700),"end"])
ChetanGaonker42d75812016-06-06 16:32:52 -0700105
Chetan Gaonkerc11d3222016-05-11 17:39:36 -0700106 else:
107 L6 = DHCP(options=[("message-type","discover"),"end"])
ChetanGaonker42d75812016-06-06 16:32:52 -0700108
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700109
110 resp = srp1(L2/L3/L4/L5/L6, filter="udp and port 68", timeout=10, iface=self.iface)
111 if resp == None:
ChetanGaonker42d75812016-06-06 16:32:52 -0700112 return (None, None, mac, None)
113
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700114 self.dhcpresp = resp
115 for x in resp.lastlayer().options:
116 if(x == 'end'):
117 break
118 op,val = x
119 if(op == "message-type"):
ChetanGaonker42d75812016-06-06 16:32:52 -0700120
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700121 if(val == 2):
ChetanGaonker42d75812016-06-06 16:32:52 -0700122
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700123 try:
124 srcIP = resp.yiaddr
125 serverIP = resp.siaddr
126 except AttributeError:
ChetanGaonker42d75812016-06-06 16:32:52 -0700127 log.info("In Attribute error.")
128 log.info("Failed to acquire IP via DHCP for %s on interface %s" %(mac, self.iface))
129 return (None, None, None, None)
Chetan Gaonker1dabecc2016-05-16 14:56:01 -0700130
131 if self.return_option:
Chetan Gaonker717b2942016-05-13 17:42:59 -0700132 for x in resp.lastlayer().options:
133 if(x == 'end'):
134 break
135 op,val = x
ChetanGaonker42d75812016-06-06 16:32:52 -0700136
Chetan Gaonker717b2942016-05-13 17:42:59 -0700137 if op == "lease_time":
Chetan Gaonker1dabecc2016-05-16 14:56:01 -0700138 if self.return_option == 'lease':
Chetan Gaonker717b2942016-05-13 17:42:59 -0700139 return (srcIP, serverIP, mac, val)
140
141 elif op == "subnet_mask":
Chetan Gaonker1dabecc2016-05-16 14:56:01 -0700142 if self.return_option == 'subnet':
ChetanGaonker42d75812016-06-06 16:32:52 -0700143 return (srcIP, serverIP, mac, val)
Chetan Gaonker1dabecc2016-05-16 14:56:01 -0700144 elif op == "router":
145 if self.return_option == 'router':
ChetanGaonker42d75812016-06-06 16:32:52 -0700146 return (srcIP, serverIP, mac, val)
Chetan Gaonker1dabecc2016-05-16 14:56:01 -0700147 elif op == "broadcast_address":
148 if self.return_option == 'broadcast_address':
ChetanGaonker42d75812016-06-06 16:32:52 -0700149 return (srcIP, serverIP, mac, val)
Chetan Gaonker1dabecc2016-05-16 14:56:01 -0700150 elif op == "name_server":
151 if self.return_option == 'dns':
ChetanGaonker42d75812016-06-06 16:32:52 -0700152 return (srcIP, serverIP, mac, val)
Chetan Gaonkerc11d3222016-05-11 17:39:36 -0700153
ChetanGaonker42d75812016-06-06 16:32:52 -0700154
Chetan Gaonker1dabecc2016-05-16 14:56:01 -0700155 else:
ChetanGaonker42d75812016-06-06 16:32:52 -0700156 return (srcIP, serverIP, mac, None)
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700157 elif(val == 6):
ChetanGaonker42d75812016-06-06 16:32:52 -0700158 return (None, None, mac, None)
Chetan Gaonkerf72ca402016-05-02 16:29:32 -0700159
ChetanGaonker42d75812016-06-06 16:32:52 -0700160
Chetan Gaonkerc11d3222016-05-11 17:39:36 -0700161 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 -0700162 '''Send a DHCP offer'''
ChetanGaonker42d75812016-06-06 16:32:52 -0700163
Chetan Gaonkerf72ca402016-05-02 16:29:32 -0700164 subnet_mask = "0.0.0.0"
165 for x in self.dhcpresp.lastlayer().options:
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700166 if(x == 'end'):
167 break
168 op,val = x
169 if(op == "subnet_mask"):
170 subnet_mask = val
171 elif(op == 'server_id'):
172 server_id = val
Chetan Gaonkerf72ca402016-05-02 16:29:32 -0700173
Chetan Gaonkerc11d3222016-05-11 17:39:36 -0700174 if unicast and self.servermac:
175 L2 = Ether(dst=self.servermac, src=mac)
176 L3 = IP(src=cip, dst=server_id)
ChetanGaonker42d75812016-06-06 16:32:52 -0700177 else:
Chetan Gaonkerc11d3222016-05-11 17:39:36 -0700178 L2 = Ether(dst="ff:ff:ff:ff:ff:ff", src=mac)
179 if self.after_T2:
180 L3 = IP(src=cip, dst="255.255.255.255")
181 else:
182 L3 = IP(src="0.0.0.0", dst="255.255.255.255")
Chetan Gaonkerf72ca402016-05-02 16:29:32 -0700183 L4 = UDP(sport=68, dport=67)
ChetanGaonker42d75812016-06-06 16:32:52 -0700184
Chetan Gaonkerc11d3222016-05-11 17:39:36 -0700185 if self.after_T2 == True:
186 L5 = BOOTP(chaddr=self.bootpmac, ciaddr = cip)
187 else:
188
189 L5 = BOOTP(chaddr=self.bootpmac, yiaddr=cip)
ChetanGaonker42d75812016-06-06 16:32:52 -0700190
Chetan Gaonkerc11d3222016-05-11 17:39:36 -0700191 if cl_reboot or self.after_T2:
ChetanGaonker42d75812016-06-06 16:32:52 -0700192 L6 = DHCP(options=[("message-type","request"),("subnet_mask",subnet_mask), ("requested_addr",cip), "end"])
Chetan Gaonker1dabecc2016-05-16 14:56:01 -0700193 elif self.send_different_option:
194 if self.send_different_option == 'subnet':
195 L6 = DHCP(options=[("message-type","request"),("server_id",server_id),
196 ("subnet_mask",'255.255.252.0'), ("requested_addr",cip), "end"])
197 elif self.send_different_option == 'router':
198 L6 = DHCP(options=[("message-type","request"),("server_id",server_id),
199 ("subnet_mask",subnet_mask), ("router",'1.1.1.1'), ("requested_addr",cip), "end"])
200 elif self.send_different_option == 'broadcast_address':
201 L6 = DHCP(options=[("message-type","request"),("server_id",server_id),
202 ("subnet_mask",subnet_mask), ("broadcast_address",'1.1.1.1'), ("requested_addr",cip), "end"])
203
204 elif self.send_different_option == 'dns':
205 L6 = DHCP(options=[("message-type","request"),("server_id",server_id),
206 ("subnet_mask",subnet_mask), ("name_server",'1.1.1.1'), ("requested_addr",cip), "end"])
ChetanGaonker42d75812016-06-06 16:32:52 -0700207
ChetanGaonker5860c182016-07-05 16:33:06 -0700208 elif self.specific_lease:
209 L6 = DHCP(options=[("message-type","request"), ("server_id",server_id),
210 ("subnet_mask",subnet_mask), ("requested_addr",cip),("lease_time",self.specific_lease), "end"])
Chetan Gaonkerc11d3222016-05-11 17:39:36 -0700211 else:
ChetanGaonker5860c182016-07-05 16:33:06 -0700212 L6 = DHCP(options=[("message-type","request"), ("server_id",server_id),
Chetan Gaonkerc11d3222016-05-11 17:39:36 -0700213 ("subnet_mask",subnet_mask), ("requested_addr",cip), "end"])
214
Chetan Gaonkerf72ca402016-05-02 16:29:32 -0700215 resp=srp1(L2/L3/L4/L5/L6, filter="udp and port 68", timeout=10, iface=self.iface)
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700216 if resp == None:
217 return (None, None)
Chetan Gaonker1dabecc2016-05-16 14:56:01 -0700218
219
220 self.servermac = resp.getlayer(Ether).src
221
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700222 for x in resp.lastlayer().options:
223 if(x == 'end'):
224 break
225 op,val = x
226 if(op == "message-type"):
ChetanGaonker42d75812016-06-06 16:32:52 -0700227
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700228 if(val == 5):
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700229 try:
230 srcIP = resp.yiaddr
231 serverIP = resp.siaddr
ChetanGaonker5860c182016-07-05 16:33:06 -0700232 self.mac_map[mac] = (srcIP, serverIP)
233 self.mac_inverse_map[srcIP] = (mac, serverIP)
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700234 except AttributeError:
ChetanGaonker42d75812016-06-06 16:32:52 -0700235 log.info("In Attribute error.")
236 log.info("Failed to acquire IP via DHCP for %s on interface %s" %(mac, self.iface))
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700237 return (None, None)
ChetanGaonker42d75812016-06-06 16:32:52 -0700238
ChetanGaonker5860c182016-07-05 16:33:06 -0700239 if lease_time or renew_time or rebind_time or self.specific_lease:
Chetan Gaonkerc11d3222016-05-11 17:39:36 -0700240 for x in resp.lastlayer().options:
241 if(x == 'end'):
242 break
243 op,val = x
ChetanGaonker42d75812016-06-06 16:32:52 -0700244
Chetan Gaonkerc11d3222016-05-11 17:39:36 -0700245 if op == "lease_time":
ChetanGaonker5860c182016-07-05 16:33:06 -0700246
247 if self.specific_lease:
248 return (srcIP, serverIP, val)
Chetan Gaonkerc11d3222016-05-11 17:39:36 -0700249 if lease_time == True:
250 self.mac_map[mac] = (srcIP, serverIP)
251 self.mac_inverse_map[srcIP] = (mac, serverIP)
252 return (srcIP, serverIP, val)
253 elif op == "renewal_time":
254 if renew_time == True:
255 self.mac_map[mac] = (srcIP, serverIP)
256 self.mac_inverse_map[srcIP] = (mac, serverIP)
257 return (srcIP, serverIP, val)
258 elif op == "rebinding_time":
259 if rebind_time == True:
260 self.mac_map[mac] = (srcIP, serverIP)
261 self.mac_inverse_map[srcIP] = (mac, serverIP)
262 return (srcIP, serverIP, val)
263 else:
264 self.mac_map[mac] = (srcIP, serverIP)
265 self.mac_inverse_map[srcIP] = (mac, serverIP)
266 return (srcIP, serverIP)
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700267 elif(val == 6):
ChetanGaonker42d75812016-06-06 16:32:52 -0700268
Chetan Gaonker1dabecc2016-05-16 14:56:01 -0700269 log.info("Got DHCP NAK.")
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700270 return (None, None)
ChetanGaonker42d75812016-06-06 16:32:52 -0700271
272
Chetan Gaonkerf72ca402016-05-02 16:29:32 -0700273
Chetan Gaonker1f7c3f82016-03-08 12:17:37 -0800274 def discover_next(self):
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -0800275 '''Send next dhcp discover/request with updated mac'''
Chetan Gaonker1f7c3f82016-03-08 12:17:37 -0800276 return self.discover(update_seed = True)
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -0800277
Chetan Gaonker1f7c3f82016-03-08 12:17:37 -0800278 def release(self, ip):
279 '''Send a DHCP discover/offer'''
280 if ip is None:
281 return False
282 if not self.mac_inverse_map.has_key(ip):
283 return False
284 mac, server_ip = self.mac_inverse_map[ip]
285 chmac = self.macToChaddr(mac)
286 L2 = Ether(dst="ff:ff:ff:ff:ff:ff", src=mac)
287 L3 = IP(src="0.0.0.0", dst="255.255.255.255")
288 L4 = UDP(sport=68, dport=67)
289 L5 = BOOTP(chaddr=chmac, ciaddr = ip)
290 L6 = DHCP(options=[("message-type","release"), ("server_id", server_ip), "end"])
291 sendp(L2/L3/L4/L5/L6, iface = self.iface)
292 del self.mac_map[mac]
293 del self.mac_inverse_map[ip]
294 return True
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -0800295
296 def macToChaddr(self, mac):
297 rv = []
298 mac = mac.split(":")
299 for x in mac:
300 rv.append(chr(int(x, 16)))
301 return reduce(lambda x,y: x + y, rv)
302
303 def get_ip(self, mac):
304 if self.mac_map.has_key(mac):
305 return self.mac_map[mac]
306 return (None, None)
307
308 def get_mac(self, ip):
309 if self.mac_inverse_map.has_key(ip):
310 return self.mac_inverse_map[ip]
311 return (None, None)
312
313 def ipToMac(self, ip):
314 '''Generate a mac from a ip'''
315
316 mcast = self.is_mcast(ip)
317 mac = "01:00:5e" if mcast == True else "00:00:00"
318 octets = ip.split(".")
319 for x in range(1,4):
320 num = str(hex(int(octets[x])))
321 num = num.split("x")[1]
322 if len(num) < 2:
323 num = "0" + str(num)
324 mac += ":" + num
325 return mac
326
327 def incIP(self, ip, n=1):
328 '''Increment an IP'''
329
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700330 if n < 1:
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -0800331 return ip
332 o = ip.split(".")
333 for ii in range(3,-1,-1):
334 if int(o[ii]) < 255:
335 o[ii] = str(int(o[ii]) + 1)
336 break
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700337 else:
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -0800338 o[ii] = str(0)
339
340 n -= 1
341 return self.incIP(".".join(o), n)