blob: 1ca73bbdd378ea0c16241da93d5e25dfbd598789 [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
Anil Kumar Sankacfa7c582016-12-09 23:17:22 +000023 def __init__(self, seed_ip = '192.168.1.1', iface = 'veth0',lease_time=600):
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080024 self.seed_ip = seed_ip
25 self.seed_mac = self.ipToMac(self.seed_ip)
26 self.iface = iface
Anil Kumar Sankacfa7c582016-12-09 23:17:22 +000027 self.lease_time = lease_time
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080028 self.mac_map = {}
29 self.mac_inverse_map = {}
Chetan Gaonkerf72ca402016-05-02 16:29:32 -070030 self.bootpmac = None
31 self.dhcpresp = None
Chetan Gaonkerc11d3222016-05-11 17:39:36 -070032 self.servermac = None
Chetan Gaonker1dabecc2016-05-16 14:56:01 -070033 self.return_option = None
Chetan Gaonkerc11d3222016-05-11 17:39:36 -070034 self.after_T2 = False
Chetan Gaonker1dabecc2016-05-16 14:56:01 -070035 self.send_different_option = None
ChetanGaonker5b984cb2016-07-12 15:50:49 -070036 self.specific_lease = None
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080037
38 def is_mcast(self, ip):
39 mcast_octet = (atol(ip) >> 24) & 0xff
40 return True if mcast_octet >= 224 and mcast_octet <= 239 else False
41
Chetan Gaonker1f7c3f82016-03-08 12:17:37 -080042 def discover(self, mac = None, update_seed = False):
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080043 '''Send a DHCP discover/offer'''
44
45 if mac is None:
46 mac = self.seed_mac
47 if update_seed:
48 self.seed_ip = self.incIP(self.seed_ip)
49 self.seed_mac = self.ipToMac(self.seed_ip)
50 mac = self.seed_mac
Chetan Gaonkerf1483862016-05-06 14:14:31 -070051
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080052 chmac = self.macToChaddr(mac)
Anil Kumar Sankacfa7c582016-12-09 23:17:22 +000053 #log.info('mac and chmac are %s %s'%(mac, chmac))
Chetan Gaonkerf72ca402016-05-02 16:29:32 -070054 self.bootpmac = chmac
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080055 L2 = Ether(dst="ff:ff:ff:ff:ff:ff", src=mac)
56 L3 = IP(src="0.0.0.0", dst="255.255.255.255")
57 L4 = UDP(sport=68, dport=67)
58 L5 = BOOTP(chaddr=chmac)
59 L6 = DHCP(options=[("message-type","discover"),"end"])
Chetan Gaonker49ef0852016-03-23 15:06:18 -070060 resp = srp1(L2/L3/L4/L5/L6, filter="udp and port 68", timeout=10, iface=self.iface)
Anil Kumar Sankacfa7c582016-12-09 23:17:22 +000061 #log.info('dhcp discover packet is %s'%(L2/L3/L4/L5/L6).show())
ChetanGaonker42d75812016-06-06 16:32:52 -070062 self.dhcpresp = resp
Anil Kumar Sankacfa7c582016-12-09 23:17:22 +000063 #log.info('discover response is %s'%resp.show())
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080064 try:
65 srcIP = resp.yiaddr
66 serverIP = resp.siaddr
67 except AttributeError:
ChetanGaonker42d75812016-06-06 16:32:52 -070068 log.info("Failed to acquire IP via DHCP for %s on interface %s" %(mac, self.iface))
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080069 return (None, None)
70
Chetan Gaonkercd86bdd2016-03-17 00:08:12 -070071 subnet_mask = "0.0.0.0"
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080072 for x in resp.lastlayer().options:
73 if(x == 'end'):
74 break
75 op,val = x
76 if(op == "subnet_mask"):
77 subnet_mask = val
78 elif(op == 'server_id'):
79 server_id = val
Chetan Gaonkerf1483862016-05-06 14:14:31 -070080
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080081 L5 = BOOTP(chaddr=chmac, yiaddr=srcIP)
Chetan Gaonkerf1483862016-05-06 14:14:31 -070082 L6 = DHCP(options=[("message-type","request"), ("server_id",server_id),
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080083 ("subnet_mask",subnet_mask), ("requested_addr",srcIP), "end"])
Anil Kumar Sankacfa7c582016-12-09 23:17:22 +000084 resp2 = srp1(L2/L3/L4/L5/L6, filter="udp and port 68", timeout=10, iface=self.iface)
85 #log.info('request response is %s'%resp2.show())
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080086 self.mac_map[mac] = (srcIP, serverIP)
87 self.mac_inverse_map[srcIP] = (mac, serverIP)
88 return (srcIP, serverIP)
89
Anil Kumar Sankacfa7c582016-12-09 23:17:22 +000090 def only_discover(self, mac = None, desired = False, lease_time = False, lease_value=600, multiple = False):
Chetan Gaonkerf72ca402016-05-02 16:29:32 -070091 '''Send a DHCP discover'''
92
93 if mac is None:
ChetanGaonker42d75812016-06-06 16:32:52 -070094 if multiple:
95 mac = RandMAC()._fix()
96 else:
97 mac = self.seed_mac
98
Chetan Gaonkerf72ca402016-05-02 16:29:32 -070099
100 chmac = self.macToChaddr(mac)
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700101 self.bootpmac = chmac
Chetan Gaonkerf72ca402016-05-02 16:29:32 -0700102 L2 = Ether(dst="ff:ff:ff:ff:ff:ff", src=mac)
103 L3 = IP(src="0.0.0.0", dst="255.255.255.255")
104 L4 = UDP(sport=68, dport=67)
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700105 L5 = BOOTP(chaddr=chmac)
Chetan Gaonkerf72ca402016-05-02 16:29:32 -0700106 if desired:
107 L6 = DHCP(options=[("message-type","discover"),("requested_addr",self.seed_ip),"end"])
ChetanGaonker42d75812016-06-06 16:32:52 -0700108
Chetan Gaonkerc11d3222016-05-11 17:39:36 -0700109 elif lease_time:
Anil Kumar Sankacfa7c582016-12-09 23:17:22 +0000110 L6 = DHCP(options=[("message-type","discover"),("lease_time",lease_value),"end"])
ChetanGaonker42d75812016-06-06 16:32:52 -0700111
Chetan Gaonkerc11d3222016-05-11 17:39:36 -0700112 else:
113 L6 = DHCP(options=[("message-type","discover"),"end"])
Anil Kumar Sankacfa7c582016-12-09 23:17:22 +0000114 #log.info('only discover packet is %s'%(L2/L3/L4/L5/L6).show())
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700115
116 resp = srp1(L2/L3/L4/L5/L6, filter="udp and port 68", timeout=10, iface=self.iface)
Anil Kumar Sankacfa7c582016-12-09 23:17:22 +0000117 #log.info('discovery packet is %s'%(L2/L3/L4/L5/L6).show())
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700118 if resp == None:
ChetanGaonker42d75812016-06-06 16:32:52 -0700119 return (None, None, mac, None)
Anil Kumar Sankacfa7c582016-12-09 23:17:22 +0000120 #log.info('only discover response is %s'%resp.show())
ChetanGaonker42d75812016-06-06 16:32:52 -0700121
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700122 self.dhcpresp = resp
123 for x in resp.lastlayer().options:
124 if(x == 'end'):
125 break
126 op,val = x
127 if(op == "message-type"):
ChetanGaonker42d75812016-06-06 16:32:52 -0700128
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700129 if(val == 2):
ChetanGaonker42d75812016-06-06 16:32:52 -0700130
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700131 try:
132 srcIP = resp.yiaddr
133 serverIP = resp.siaddr
134 except AttributeError:
ChetanGaonker42d75812016-06-06 16:32:52 -0700135 log.info("In Attribute error.")
136 log.info("Failed to acquire IP via DHCP for %s on interface %s" %(mac, self.iface))
137 return (None, None, None, None)
Chetan Gaonker1dabecc2016-05-16 14:56:01 -0700138
139 if self.return_option:
Chetan Gaonker717b2942016-05-13 17:42:59 -0700140 for x in resp.lastlayer().options:
141 if(x == 'end'):
142 break
143 op,val = x
ChetanGaonker42d75812016-06-06 16:32:52 -0700144
Chetan Gaonker717b2942016-05-13 17:42:59 -0700145 if op == "lease_time":
Chetan Gaonker1dabecc2016-05-16 14:56:01 -0700146 if self.return_option == 'lease':
Chetan Gaonker717b2942016-05-13 17:42:59 -0700147 return (srcIP, serverIP, mac, val)
148
149 elif op == "subnet_mask":
Chetan Gaonker1dabecc2016-05-16 14:56:01 -0700150 if self.return_option == 'subnet':
ChetanGaonker42d75812016-06-06 16:32:52 -0700151 return (srcIP, serverIP, mac, val)
Chetan Gaonker1dabecc2016-05-16 14:56:01 -0700152 elif op == "router":
153 if self.return_option == 'router':
ChetanGaonker42d75812016-06-06 16:32:52 -0700154 return (srcIP, serverIP, mac, val)
Chetan Gaonker1dabecc2016-05-16 14:56:01 -0700155 elif op == "broadcast_address":
156 if self.return_option == 'broadcast_address':
ChetanGaonker42d75812016-06-06 16:32:52 -0700157 return (srcIP, serverIP, mac, val)
Chetan Gaonker1dabecc2016-05-16 14:56:01 -0700158 elif op == "name_server":
159 if self.return_option == 'dns':
ChetanGaonker42d75812016-06-06 16:32:52 -0700160 return (srcIP, serverIP, mac, val)
Chetan Gaonkerc11d3222016-05-11 17:39:36 -0700161
ChetanGaonker42d75812016-06-06 16:32:52 -0700162
Chetan Gaonker1dabecc2016-05-16 14:56:01 -0700163 else:
ChetanGaonker42d75812016-06-06 16:32:52 -0700164 return (srcIP, serverIP, mac, None)
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700165 elif(val == 6):
ChetanGaonker42d75812016-06-06 16:32:52 -0700166 return (None, None, mac, None)
Chetan Gaonkerf72ca402016-05-02 16:29:32 -0700167
ChetanGaonker42d75812016-06-06 16:32:52 -0700168
Anil Kumar Sankacfa7c582016-12-09 23:17:22 +0000169 def only_request(self, cip, mac, cl_reboot = False, lease_time = False, lease_value=600, renew_time = False, rebind_time = False, unicast = False):
Chetan Gaonkerf72ca402016-05-02 16:29:32 -0700170 '''Send a DHCP offer'''
ChetanGaonker42d75812016-06-06 16:32:52 -0700171
Chetan Gaonkerf72ca402016-05-02 16:29:32 -0700172 subnet_mask = "0.0.0.0"
173 for x in self.dhcpresp.lastlayer().options:
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700174 if(x == 'end'):
175 break
176 op,val = x
177 if(op == "subnet_mask"):
178 subnet_mask = val
179 elif(op == 'server_id'):
180 server_id = val
Chetan Gaonkerf72ca402016-05-02 16:29:32 -0700181
Chetan Gaonkerc11d3222016-05-11 17:39:36 -0700182 if unicast and self.servermac:
183 L2 = Ether(dst=self.servermac, src=mac)
184 L3 = IP(src=cip, dst=server_id)
ChetanGaonker42d75812016-06-06 16:32:52 -0700185 else:
Chetan Gaonkerc11d3222016-05-11 17:39:36 -0700186 L2 = Ether(dst="ff:ff:ff:ff:ff:ff", src=mac)
187 if self.after_T2:
188 L3 = IP(src=cip, dst="255.255.255.255")
189 else:
190 L3 = IP(src="0.0.0.0", dst="255.255.255.255")
Chetan Gaonkerf72ca402016-05-02 16:29:32 -0700191 L4 = UDP(sport=68, dport=67)
ChetanGaonker42d75812016-06-06 16:32:52 -0700192
Chetan Gaonkerc11d3222016-05-11 17:39:36 -0700193 if self.after_T2 == True:
194 L5 = BOOTP(chaddr=self.bootpmac, ciaddr = cip)
195 else:
196
197 L5 = BOOTP(chaddr=self.bootpmac, yiaddr=cip)
ChetanGaonker42d75812016-06-06 16:32:52 -0700198
Chetan Gaonkerc11d3222016-05-11 17:39:36 -0700199 if cl_reboot or self.after_T2:
ChetanGaonker42d75812016-06-06 16:32:52 -0700200 L6 = DHCP(options=[("message-type","request"),("subnet_mask",subnet_mask), ("requested_addr",cip), "end"])
Chetan Gaonker1dabecc2016-05-16 14:56:01 -0700201 elif self.send_different_option:
202 if self.send_different_option == 'subnet':
203 L6 = DHCP(options=[("message-type","request"),("server_id",server_id),
Anil Kumar Sankacfa7c582016-12-09 23:17:22 +0000204 ("subnet_mask",'255.255.252.252'), ("requested_addr",cip), "end"])
Chetan Gaonker1dabecc2016-05-16 14:56:01 -0700205 elif self.send_different_option == 'router':
206 L6 = DHCP(options=[("message-type","request"),("server_id",server_id),
207 ("subnet_mask",subnet_mask), ("router",'1.1.1.1'), ("requested_addr",cip), "end"])
208 elif self.send_different_option == 'broadcast_address':
209 L6 = DHCP(options=[("message-type","request"),("server_id",server_id),
210 ("subnet_mask",subnet_mask), ("broadcast_address",'1.1.1.1'), ("requested_addr",cip), "end"])
211
212 elif self.send_different_option == 'dns':
213 L6 = DHCP(options=[("message-type","request"),("server_id",server_id),
214 ("subnet_mask",subnet_mask), ("name_server",'1.1.1.1'), ("requested_addr",cip), "end"])
ChetanGaonker42d75812016-06-06 16:32:52 -0700215
Anil Kumar Sankacfa7c582016-12-09 23:17:22 +0000216 elif lease_time:
ChetanGaonker5860c182016-07-05 16:33:06 -0700217 L6 = DHCP(options=[("message-type","request"), ("server_id",server_id),
Anil Kumar Sankacfa7c582016-12-09 23:17:22 +0000218 ("subnet_mask",subnet_mask), ("requested_addr",cip),("lease_time",lease_value), "end"])
Chetan Gaonkerc11d3222016-05-11 17:39:36 -0700219 else:
ChetanGaonker5860c182016-07-05 16:33:06 -0700220 L6 = DHCP(options=[("message-type","request"), ("server_id",server_id),
Chetan Gaonkerc11d3222016-05-11 17:39:36 -0700221 ("subnet_mask",subnet_mask), ("requested_addr",cip), "end"])
222
Chetan Gaonkerf72ca402016-05-02 16:29:32 -0700223 resp=srp1(L2/L3/L4/L5/L6, filter="udp and port 68", timeout=10, iface=self.iface)
Anil Kumar Sankacfa7c582016-12-09 23:17:22 +0000224 #log.info('request packet is %s'%(L2/L3/L4/L5/L6).show())
225 #log.info('response packet is %s'%resp.show())
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700226 if resp == None:
227 return (None, None)
Chetan Gaonker1dabecc2016-05-16 14:56:01 -0700228
229
230 self.servermac = resp.getlayer(Ether).src
231
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700232 for x in resp.lastlayer().options:
233 if(x == 'end'):
234 break
235 op,val = x
236 if(op == "message-type"):
ChetanGaonker42d75812016-06-06 16:32:52 -0700237
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700238 if(val == 5):
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700239 try:
240 srcIP = resp.yiaddr
241 serverIP = resp.siaddr
ChetanGaonker5860c182016-07-05 16:33:06 -0700242 self.mac_map[mac] = (srcIP, serverIP)
243 self.mac_inverse_map[srcIP] = (mac, serverIP)
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700244 except AttributeError:
ChetanGaonker42d75812016-06-06 16:32:52 -0700245 log.info("In Attribute error.")
246 log.info("Failed to acquire IP via DHCP for %s on interface %s" %(mac, self.iface))
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700247 return (None, None)
ChetanGaonker42d75812016-06-06 16:32:52 -0700248
ChetanGaonker5860c182016-07-05 16:33:06 -0700249 if lease_time or renew_time or rebind_time or self.specific_lease:
Chetan Gaonkerc11d3222016-05-11 17:39:36 -0700250 for x in resp.lastlayer().options:
251 if(x == 'end'):
252 break
253 op,val = x
ChetanGaonker42d75812016-06-06 16:32:52 -0700254
Chetan Gaonkerc11d3222016-05-11 17:39:36 -0700255 if op == "lease_time":
ChetanGaonker5860c182016-07-05 16:33:06 -0700256
257 if self.specific_lease:
258 return (srcIP, serverIP, val)
Chetan Gaonkerc11d3222016-05-11 17:39:36 -0700259 if lease_time == True:
260 self.mac_map[mac] = (srcIP, serverIP)
261 self.mac_inverse_map[srcIP] = (mac, serverIP)
262 return (srcIP, serverIP, val)
263 elif op == "renewal_time":
264 if renew_time == True:
265 self.mac_map[mac] = (srcIP, serverIP)
266 self.mac_inverse_map[srcIP] = (mac, serverIP)
267 return (srcIP, serverIP, val)
268 elif op == "rebinding_time":
269 if rebind_time == True:
270 self.mac_map[mac] = (srcIP, serverIP)
271 self.mac_inverse_map[srcIP] = (mac, serverIP)
272 return (srcIP, serverIP, val)
273 else:
274 self.mac_map[mac] = (srcIP, serverIP)
275 self.mac_inverse_map[srcIP] = (mac, serverIP)
276 return (srcIP, serverIP)
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700277 elif(val == 6):
ChetanGaonker42d75812016-06-06 16:32:52 -0700278
Chetan Gaonker1dabecc2016-05-16 14:56:01 -0700279 log.info("Got DHCP NAK.")
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700280 return (None, None)
ChetanGaonker42d75812016-06-06 16:32:52 -0700281
282
Chetan Gaonkerf72ca402016-05-02 16:29:32 -0700283
Chetan Gaonker1f7c3f82016-03-08 12:17:37 -0800284 def discover_next(self):
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -0800285 '''Send next dhcp discover/request with updated mac'''
Chetan Gaonker1f7c3f82016-03-08 12:17:37 -0800286 return self.discover(update_seed = True)
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -0800287
Chetan Gaonker1f7c3f82016-03-08 12:17:37 -0800288 def release(self, ip):
289 '''Send a DHCP discover/offer'''
290 if ip is None:
291 return False
292 if not self.mac_inverse_map.has_key(ip):
293 return False
294 mac, server_ip = self.mac_inverse_map[ip]
295 chmac = self.macToChaddr(mac)
296 L2 = Ether(dst="ff:ff:ff:ff:ff:ff", src=mac)
297 L3 = IP(src="0.0.0.0", dst="255.255.255.255")
298 L4 = UDP(sport=68, dport=67)
299 L5 = BOOTP(chaddr=chmac, ciaddr = ip)
300 L6 = DHCP(options=[("message-type","release"), ("server_id", server_ip), "end"])
Anil Kumar Sankacfa7c582016-12-09 23:17:22 +0000301 sendp(L2/L3/L4/L5/L6, iface = self.iface, count=2)
302 #log.info('release response is %s'%resp)
Chetan Gaonker1f7c3f82016-03-08 12:17:37 -0800303 del self.mac_map[mac]
304 del self.mac_inverse_map[ip]
305 return True
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -0800306
307 def macToChaddr(self, mac):
308 rv = []
309 mac = mac.split(":")
310 for x in mac:
311 rv.append(chr(int(x, 16)))
312 return reduce(lambda x,y: x + y, rv)
313
314 def get_ip(self, mac):
315 if self.mac_map.has_key(mac):
316 return self.mac_map[mac]
317 return (None, None)
318
319 def get_mac(self, ip):
320 if self.mac_inverse_map.has_key(ip):
321 return self.mac_inverse_map[ip]
322 return (None, None)
323
324 def ipToMac(self, ip):
325 '''Generate a mac from a ip'''
326
327 mcast = self.is_mcast(ip)
328 mac = "01:00:5e" if mcast == True else "00:00:00"
329 octets = ip.split(".")
330 for x in range(1,4):
331 num = str(hex(int(octets[x])))
332 num = num.split("x")[1]
333 if len(num) < 2:
334 num = "0" + str(num)
335 mac += ":" + num
336 return mac
337
338 def incIP(self, ip, n=1):
339 '''Increment an IP'''
340
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700341 if n < 1:
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -0800342 return ip
343 o = ip.split(".")
344 for ii in range(3,-1,-1):
345 if int(o[ii]) < 255:
346 o[ii] = str(int(o[ii]) + 1)
347 break
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700348 else:
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -0800349 o[ii] = str(0)
350
351 n -= 1
352 return self.incIP(".".join(o), n)
Anil Kumar Sankacfa7c582016-12-09 23:17:22 +0000353