blob: 0692be78b0f6b8f61c3f960bebbd684f6b38144a [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
Chetan Gaonkerc11d3222016-05-11 17:39:36 -0700208 else:
209 L6 = DHCP(options=[("message-type","request"), ("server_id",server_id),
210 ("subnet_mask",subnet_mask), ("requested_addr",cip), "end"])
211
Chetan Gaonkerf72ca402016-05-02 16:29:32 -0700212 resp=srp1(L2/L3/L4/L5/L6, filter="udp and port 68", timeout=10, iface=self.iface)
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700213 if resp == None:
214 return (None, None)
Chetan Gaonker1dabecc2016-05-16 14:56:01 -0700215
216
217 self.servermac = resp.getlayer(Ether).src
218
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700219 for x in resp.lastlayer().options:
220 if(x == 'end'):
221 break
222 op,val = x
223 if(op == "message-type"):
ChetanGaonker42d75812016-06-06 16:32:52 -0700224
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700225 if(val == 5):
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700226 try:
227 srcIP = resp.yiaddr
228 serverIP = resp.siaddr
229 except AttributeError:
ChetanGaonker42d75812016-06-06 16:32:52 -0700230 log.info("In Attribute error.")
231 log.info("Failed to acquire IP via DHCP for %s on interface %s" %(mac, self.iface))
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700232 return (None, None)
ChetanGaonker42d75812016-06-06 16:32:52 -0700233
Chetan Gaonkerc11d3222016-05-11 17:39:36 -0700234 if lease_time or renew_time or rebind_time:
235 for x in resp.lastlayer().options:
236 if(x == 'end'):
237 break
238 op,val = x
ChetanGaonker42d75812016-06-06 16:32:52 -0700239
Chetan Gaonkerc11d3222016-05-11 17:39:36 -0700240 if op == "lease_time":
241 if lease_time == True:
242 self.mac_map[mac] = (srcIP, serverIP)
243 self.mac_inverse_map[srcIP] = (mac, serverIP)
244 return (srcIP, serverIP, val)
245 elif op == "renewal_time":
246 if renew_time == True:
247 self.mac_map[mac] = (srcIP, serverIP)
248 self.mac_inverse_map[srcIP] = (mac, serverIP)
249 return (srcIP, serverIP, val)
250 elif op == "rebinding_time":
251 if rebind_time == True:
252 self.mac_map[mac] = (srcIP, serverIP)
253 self.mac_inverse_map[srcIP] = (mac, serverIP)
254 return (srcIP, serverIP, val)
255 else:
256 self.mac_map[mac] = (srcIP, serverIP)
257 self.mac_inverse_map[srcIP] = (mac, serverIP)
258 return (srcIP, serverIP)
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700259 elif(val == 6):
ChetanGaonker42d75812016-06-06 16:32:52 -0700260
Chetan Gaonker1dabecc2016-05-16 14:56:01 -0700261 log.info("Got DHCP NAK.")
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700262 return (None, None)
ChetanGaonker42d75812016-06-06 16:32:52 -0700263
264
Chetan Gaonkerf72ca402016-05-02 16:29:32 -0700265
Chetan Gaonker1f7c3f82016-03-08 12:17:37 -0800266 def discover_next(self):
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -0800267 '''Send next dhcp discover/request with updated mac'''
Chetan Gaonker1f7c3f82016-03-08 12:17:37 -0800268 return self.discover(update_seed = True)
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -0800269
Chetan Gaonker1f7c3f82016-03-08 12:17:37 -0800270 def release(self, ip):
271 '''Send a DHCP discover/offer'''
272 if ip is None:
273 return False
274 if not self.mac_inverse_map.has_key(ip):
275 return False
276 mac, server_ip = self.mac_inverse_map[ip]
277 chmac = self.macToChaddr(mac)
278 L2 = Ether(dst="ff:ff:ff:ff:ff:ff", src=mac)
279 L3 = IP(src="0.0.0.0", dst="255.255.255.255")
280 L4 = UDP(sport=68, dport=67)
281 L5 = BOOTP(chaddr=chmac, ciaddr = ip)
282 L6 = DHCP(options=[("message-type","release"), ("server_id", server_ip), "end"])
283 sendp(L2/L3/L4/L5/L6, iface = self.iface)
284 del self.mac_map[mac]
285 del self.mac_inverse_map[ip]
286 return True
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -0800287
288 def macToChaddr(self, mac):
289 rv = []
290 mac = mac.split(":")
291 for x in mac:
292 rv.append(chr(int(x, 16)))
293 return reduce(lambda x,y: x + y, rv)
294
295 def get_ip(self, mac):
296 if self.mac_map.has_key(mac):
297 return self.mac_map[mac]
298 return (None, None)
299
300 def get_mac(self, ip):
301 if self.mac_inverse_map.has_key(ip):
302 return self.mac_inverse_map[ip]
303 return (None, None)
304
305 def ipToMac(self, ip):
306 '''Generate a mac from a ip'''
307
308 mcast = self.is_mcast(ip)
309 mac = "01:00:5e" if mcast == True else "00:00:00"
310 octets = ip.split(".")
311 for x in range(1,4):
312 num = str(hex(int(octets[x])))
313 num = num.split("x")[1]
314 if len(num) < 2:
315 num = "0" + str(num)
316 mac += ":" + num
317 return mac
318
319 def incIP(self, ip, n=1):
320 '''Increment an IP'''
321
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700322 if n < 1:
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -0800323 return ip
324 o = ip.split(".")
325 for ii in range(3,-1,-1):
326 if int(o[ii]) < 255:
327 o[ii] = str(int(o[ii]) + 1)
328 break
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700329 else:
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -0800330 o[ii] = str(0)
331
332 n -= 1
333 return self.incIP(".".join(o), n)