blob: 34ca04b2b583ae8bef2a9bfa90dc3eb53d5d952a [file] [log] [blame]
Chetan Gaonkercfcce782016-05-10 10:10:42 -07001#
2# 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
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# 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)
Chetan Gaonkerf72ca402016-05-02 16:29:32 -070058 self.dhcpresp = resp
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080059 try:
60 srcIP = resp.yiaddr
61 serverIP = resp.siaddr
62 except AttributeError:
63 print("Failed to acquire IP via DHCP for %s on interface %s" %(mac, self.iface))
64 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
Chetan Gaonkerf1483862016-05-06 14:14:31 -070084 def only_discover(self, mac = None, desired = False, lease_time = False):
Chetan Gaonkerf72ca402016-05-02 16:29:32 -070085 '''Send a DHCP discover'''
86
87 if mac is None:
88 mac = self.seed_mac
89
90 chmac = self.macToChaddr(mac)
Chetan Gaonkerf1483862016-05-06 14:14:31 -070091 self.bootpmac = chmac
Chetan Gaonkerf72ca402016-05-02 16:29:32 -070092 L2 = Ether(dst="ff:ff:ff:ff:ff:ff", src=mac)
93 L3 = IP(src="0.0.0.0", dst="255.255.255.255")
94 L4 = UDP(sport=68, dport=67)
Chetan Gaonkerf1483862016-05-06 14:14:31 -070095 L5 = BOOTP(chaddr=chmac)
Chetan Gaonkerf72ca402016-05-02 16:29:32 -070096 if desired:
97 L6 = DHCP(options=[("message-type","discover"),("requested_addr",self.seed_ip),"end"])
Chetan Gaonkerf1483862016-05-06 14:14:31 -070098
Chetan Gaonkerc11d3222016-05-11 17:39:36 -070099 elif lease_time:
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700100 L6 = DHCP(options=[("message-type","discover"),("lease_time",700),"end"])
101
Chetan Gaonkerc11d3222016-05-11 17:39:36 -0700102 else:
103 L6 = DHCP(options=[("message-type","discover"),"end"])
Chetan Gaonkerf72ca402016-05-02 16:29:32 -0700104
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700105
106 resp = srp1(L2/L3/L4/L5/L6, filter="udp and port 68", timeout=10, iface=self.iface)
107 if resp == None:
Chetan Gaonkerc11d3222016-05-11 17:39:36 -0700108 return (None, None, mac)
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700109
110 self.dhcpresp = resp
111 for x in resp.lastlayer().options:
112 if(x == 'end'):
113 break
114 op,val = x
115 if(op == "message-type"):
116
117 if(val == 2):
118
119 try:
120 srcIP = resp.yiaddr
121 serverIP = resp.siaddr
122 except AttributeError:
123 print "In Attribute error."
124 print("Failed to acquire IP via DHCP for %s on interface %s" %(mac, self.iface))
125 return (None, None, None)
Chetan Gaonker1dabecc2016-05-16 14:56:01 -0700126
127 if self.return_option:
Chetan Gaonker717b2942016-05-13 17:42:59 -0700128 for x in resp.lastlayer().options:
129 if(x == 'end'):
130 break
131 op,val = x
132
133 if op == "lease_time":
Chetan Gaonker1dabecc2016-05-16 14:56:01 -0700134 if self.return_option == 'lease':
Chetan Gaonker717b2942016-05-13 17:42:59 -0700135 return (srcIP, serverIP, mac, val)
136
137 elif op == "subnet_mask":
Chetan Gaonker1dabecc2016-05-16 14:56:01 -0700138 if self.return_option == 'subnet':
139 return (srcIP, serverIP, mac, val)
140 elif op == "router":
141 if self.return_option == 'router':
142 return (srcIP, serverIP, mac, val)
143 elif op == "broadcast_address":
144 if self.return_option == 'broadcast_address':
145 return (srcIP, serverIP, mac, val)
146 elif op == "name_server":
147 if self.return_option == 'dns':
Chetan Gaonker717b2942016-05-13 17:42:59 -0700148 return (srcIP, serverIP, mac, val)
Chetan Gaonkerc11d3222016-05-11 17:39:36 -0700149
150
Chetan Gaonker1dabecc2016-05-16 14:56:01 -0700151 else:
152 return (srcIP, serverIP, mac)
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700153 elif(val == 6):
154
155 return (None, None, mac)
Chetan Gaonkerf72ca402016-05-02 16:29:32 -0700156
157
Chetan Gaonkerc11d3222016-05-11 17:39:36 -0700158 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 -0700159 '''Send a DHCP offer'''
160
Chetan Gaonkerf72ca402016-05-02 16:29:32 -0700161 subnet_mask = "0.0.0.0"
162 for x in self.dhcpresp.lastlayer().options:
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700163 if(x == 'end'):
164 break
165 op,val = x
166 if(op == "subnet_mask"):
167 subnet_mask = val
168 elif(op == 'server_id'):
169 server_id = val
Chetan Gaonkerf72ca402016-05-02 16:29:32 -0700170
Chetan Gaonkerc11d3222016-05-11 17:39:36 -0700171 if unicast and self.servermac:
172 L2 = Ether(dst=self.servermac, src=mac)
173 L3 = IP(src=cip, dst=server_id)
174 else:
175 L2 = Ether(dst="ff:ff:ff:ff:ff:ff", src=mac)
176 if self.after_T2:
177 L3 = IP(src=cip, dst="255.255.255.255")
178 else:
179 L3 = IP(src="0.0.0.0", dst="255.255.255.255")
Chetan Gaonkerf72ca402016-05-02 16:29:32 -0700180 L4 = UDP(sport=68, dport=67)
Chetan Gaonkerc11d3222016-05-11 17:39:36 -0700181
182 if self.after_T2 == True:
183 L5 = BOOTP(chaddr=self.bootpmac, ciaddr = cip)
184 else:
185
186 L5 = BOOTP(chaddr=self.bootpmac, yiaddr=cip)
187
188 if cl_reboot or self.after_T2:
189 L6 = DHCP(options=[("message-type","request"),("subnet_mask",subnet_mask), ("requested_addr",cip), "end"])
Chetan Gaonker1dabecc2016-05-16 14:56:01 -0700190 elif self.send_different_option:
191 if self.send_different_option == 'subnet':
192 L6 = DHCP(options=[("message-type","request"),("server_id",server_id),
193 ("subnet_mask",'255.255.252.0'), ("requested_addr",cip), "end"])
194 elif self.send_different_option == 'router':
195 L6 = DHCP(options=[("message-type","request"),("server_id",server_id),
196 ("subnet_mask",subnet_mask), ("router",'1.1.1.1'), ("requested_addr",cip), "end"])
197 elif self.send_different_option == 'broadcast_address':
198 L6 = DHCP(options=[("message-type","request"),("server_id",server_id),
199 ("subnet_mask",subnet_mask), ("broadcast_address",'1.1.1.1'), ("requested_addr",cip), "end"])
200
201 elif self.send_different_option == 'dns':
202 L6 = DHCP(options=[("message-type","request"),("server_id",server_id),
203 ("subnet_mask",subnet_mask), ("name_server",'1.1.1.1'), ("requested_addr",cip), "end"])
204
Chetan Gaonkerc11d3222016-05-11 17:39:36 -0700205 else:
206 L6 = DHCP(options=[("message-type","request"), ("server_id",server_id),
207 ("subnet_mask",subnet_mask), ("requested_addr",cip), "end"])
208
Chetan Gaonkerf72ca402016-05-02 16:29:32 -0700209 resp=srp1(L2/L3/L4/L5/L6, filter="udp and port 68", timeout=10, iface=self.iface)
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700210 if resp == None:
211 return (None, None)
Chetan Gaonker1dabecc2016-05-16 14:56:01 -0700212
213
214 self.servermac = resp.getlayer(Ether).src
215
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700216 for x in resp.lastlayer().options:
217 if(x == 'end'):
218 break
219 op,val = x
220 if(op == "message-type"):
221
222 if(val == 5):
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700223 try:
224 srcIP = resp.yiaddr
225 serverIP = resp.siaddr
226 except AttributeError:
227 print "In Attribute error."
228 print("Failed to acquire IP via DHCP for %s on interface %s" %(mac, self.iface))
229 return (None, None)
Chetan Gaonkerc11d3222016-05-11 17:39:36 -0700230
231 if lease_time or renew_time or rebind_time:
232 for x in resp.lastlayer().options:
233 if(x == 'end'):
234 break
235 op,val = x
236
237 if op == "lease_time":
238 if lease_time == True:
239 self.mac_map[mac] = (srcIP, serverIP)
240 self.mac_inverse_map[srcIP] = (mac, serverIP)
241 return (srcIP, serverIP, val)
242 elif op == "renewal_time":
243 if renew_time == True:
244 self.mac_map[mac] = (srcIP, serverIP)
245 self.mac_inverse_map[srcIP] = (mac, serverIP)
246 return (srcIP, serverIP, val)
247 elif op == "rebinding_time":
248 if rebind_time == True:
249 self.mac_map[mac] = (srcIP, serverIP)
250 self.mac_inverse_map[srcIP] = (mac, serverIP)
251 return (srcIP, serverIP, val)
252 else:
253 self.mac_map[mac] = (srcIP, serverIP)
254 self.mac_inverse_map[srcIP] = (mac, serverIP)
255 return (srcIP, serverIP)
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700256 elif(val == 6):
257
Chetan Gaonker1dabecc2016-05-16 14:56:01 -0700258 log.info("Got DHCP NAK.")
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700259 return (None, None)
260
261
Chetan Gaonkerf72ca402016-05-02 16:29:32 -0700262
Chetan Gaonker1f7c3f82016-03-08 12:17:37 -0800263 def discover_next(self):
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -0800264 '''Send next dhcp discover/request with updated mac'''
Chetan Gaonker1f7c3f82016-03-08 12:17:37 -0800265 return self.discover(update_seed = True)
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -0800266
Chetan Gaonker1f7c3f82016-03-08 12:17:37 -0800267 def release(self, ip):
268 '''Send a DHCP discover/offer'''
269 if ip is None:
270 return False
271 if not self.mac_inverse_map.has_key(ip):
272 return False
273 mac, server_ip = self.mac_inverse_map[ip]
274 chmac = self.macToChaddr(mac)
275 L2 = Ether(dst="ff:ff:ff:ff:ff:ff", src=mac)
276 L3 = IP(src="0.0.0.0", dst="255.255.255.255")
277 L4 = UDP(sport=68, dport=67)
278 L5 = BOOTP(chaddr=chmac, ciaddr = ip)
279 L6 = DHCP(options=[("message-type","release"), ("server_id", server_ip), "end"])
280 sendp(L2/L3/L4/L5/L6, iface = self.iface)
281 del self.mac_map[mac]
282 del self.mac_inverse_map[ip]
283 return True
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -0800284
285 def macToChaddr(self, mac):
286 rv = []
287 mac = mac.split(":")
288 for x in mac:
289 rv.append(chr(int(x, 16)))
290 return reduce(lambda x,y: x + y, rv)
291
292 def get_ip(self, mac):
293 if self.mac_map.has_key(mac):
294 return self.mac_map[mac]
295 return (None, None)
296
297 def get_mac(self, ip):
298 if self.mac_inverse_map.has_key(ip):
299 return self.mac_inverse_map[ip]
300 return (None, None)
301
302 def ipToMac(self, ip):
303 '''Generate a mac from a ip'''
304
305 mcast = self.is_mcast(ip)
306 mac = "01:00:5e" if mcast == True else "00:00:00"
307 octets = ip.split(".")
308 for x in range(1,4):
309 num = str(hex(int(octets[x])))
310 num = num.split("x")[1]
311 if len(num) < 2:
312 num = "0" + str(num)
313 mac += ":" + num
314 return mac
315
316 def incIP(self, ip, n=1):
317 '''Increment an IP'''
318
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700319 if n < 1:
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -0800320 return ip
321 o = ip.split(".")
322 for ii in range(3,-1,-1):
323 if int(o[ii]) < 255:
324 o[ii] = str(int(o[ii]) + 1)
325 break
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700326 else:
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -0800327 o[ii] = str(0)
328
329 n -= 1
330 return self.incIP(".".join(o), n)