blob: c940aa3aeb6c516b5c56a771d96558c12af2dcca [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
32 self.after_T2 = False
Chetan Gaonker717b2942016-05-13 17:42:59 -070033 self.return_lease = False
34 self.return_subnet = False
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 Gaonker717b2942016-05-13 17:42:59 -0700126 if self.return_lease or self.return_subnet:
127 for x in resp.lastlayer().options:
128 if(x == 'end'):
129 break
130 op,val = x
131
132 if op == "lease_time":
133 if self.return_lease:
134 return (srcIP, serverIP, mac, val)
135
136 elif op == "subnet_mask":
137 log.info("Got Field Subnet mask.")
138 if self.return_subnet:
139 log.info("Subnet Mask Returned.")
140 return (srcIP, serverIP, mac, val)
Chetan Gaonkerc11d3222016-05-11 17:39:36 -0700141
142
143 return (srcIP, serverIP, mac)
144
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700145 elif(val == 6):
146
147 return (None, None, mac)
Chetan Gaonkerf72ca402016-05-02 16:29:32 -0700148
149
Chetan Gaonkerc11d3222016-05-11 17:39:36 -0700150 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 -0700151 '''Send a DHCP offer'''
152
Chetan Gaonkerf72ca402016-05-02 16:29:32 -0700153 subnet_mask = "0.0.0.0"
154 for x in self.dhcpresp.lastlayer().options:
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700155 if(x == 'end'):
156 break
157 op,val = x
158 if(op == "subnet_mask"):
159 subnet_mask = val
160 elif(op == 'server_id'):
161 server_id = val
Chetan Gaonkerf72ca402016-05-02 16:29:32 -0700162
Chetan Gaonkerc11d3222016-05-11 17:39:36 -0700163 if unicast and self.servermac:
164 L2 = Ether(dst=self.servermac, src=mac)
165 L3 = IP(src=cip, dst=server_id)
166 else:
167 L2 = Ether(dst="ff:ff:ff:ff:ff:ff", src=mac)
168 if self.after_T2:
169 L3 = IP(src=cip, dst="255.255.255.255")
170 else:
171 L3 = IP(src="0.0.0.0", dst="255.255.255.255")
Chetan Gaonkerf72ca402016-05-02 16:29:32 -0700172 L4 = UDP(sport=68, dport=67)
Chetan Gaonkerc11d3222016-05-11 17:39:36 -0700173
174 if self.after_T2 == True:
175 L5 = BOOTP(chaddr=self.bootpmac, ciaddr = cip)
176 else:
177
178 L5 = BOOTP(chaddr=self.bootpmac, yiaddr=cip)
179
180 if cl_reboot or self.after_T2:
181 L6 = DHCP(options=[("message-type","request"),("subnet_mask",subnet_mask), ("requested_addr",cip), "end"])
182 else:
183 L6 = DHCP(options=[("message-type","request"), ("server_id",server_id),
184 ("subnet_mask",subnet_mask), ("requested_addr",cip), "end"])
185
Chetan Gaonkerf72ca402016-05-02 16:29:32 -0700186 resp=srp1(L2/L3/L4/L5/L6, filter="udp and port 68", timeout=10, iface=self.iface)
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700187 if resp == None:
188 return (None, None)
189
190 for x in resp.lastlayer().options:
191 if(x == 'end'):
192 break
193 op,val = x
194 if(op == "message-type"):
195
196 if(val == 5):
197
198 try:
199 srcIP = resp.yiaddr
200 serverIP = resp.siaddr
201 except AttributeError:
202 print "In Attribute error."
203 print("Failed to acquire IP via DHCP for %s on interface %s" %(mac, self.iface))
204 return (None, None)
Chetan Gaonkerc11d3222016-05-11 17:39:36 -0700205
206 if lease_time or renew_time or rebind_time:
207 for x in resp.lastlayer().options:
208 if(x == 'end'):
209 break
210 op,val = x
211
212 if op == "lease_time":
213 if lease_time == True:
214 self.mac_map[mac] = (srcIP, serverIP)
215 self.mac_inverse_map[srcIP] = (mac, serverIP)
216 return (srcIP, serverIP, val)
217 elif op == "renewal_time":
218 if renew_time == True:
219 self.mac_map[mac] = (srcIP, serverIP)
220 self.mac_inverse_map[srcIP] = (mac, serverIP)
221 return (srcIP, serverIP, val)
222 elif op == "rebinding_time":
223 if rebind_time == True:
224 self.mac_map[mac] = (srcIP, serverIP)
225 self.mac_inverse_map[srcIP] = (mac, serverIP)
226 return (srcIP, serverIP, val)
227 else:
228 self.mac_map[mac] = (srcIP, serverIP)
229 self.mac_inverse_map[srcIP] = (mac, serverIP)
230 return (srcIP, serverIP)
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700231 elif(val == 6):
232
233 return (None, None)
234
235
Chetan Gaonkerf72ca402016-05-02 16:29:32 -0700236
Chetan Gaonker1f7c3f82016-03-08 12:17:37 -0800237 def discover_next(self):
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -0800238 '''Send next dhcp discover/request with updated mac'''
Chetan Gaonker1f7c3f82016-03-08 12:17:37 -0800239 return self.discover(update_seed = True)
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -0800240
Chetan Gaonker1f7c3f82016-03-08 12:17:37 -0800241 def release(self, ip):
242 '''Send a DHCP discover/offer'''
243 if ip is None:
244 return False
245 if not self.mac_inverse_map.has_key(ip):
246 return False
247 mac, server_ip = self.mac_inverse_map[ip]
248 chmac = self.macToChaddr(mac)
249 L2 = Ether(dst="ff:ff:ff:ff:ff:ff", src=mac)
250 L3 = IP(src="0.0.0.0", dst="255.255.255.255")
251 L4 = UDP(sport=68, dport=67)
252 L5 = BOOTP(chaddr=chmac, ciaddr = ip)
253 L6 = DHCP(options=[("message-type","release"), ("server_id", server_ip), "end"])
254 sendp(L2/L3/L4/L5/L6, iface = self.iface)
255 del self.mac_map[mac]
256 del self.mac_inverse_map[ip]
257 return True
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -0800258
259 def macToChaddr(self, mac):
260 rv = []
261 mac = mac.split(":")
262 for x in mac:
263 rv.append(chr(int(x, 16)))
264 return reduce(lambda x,y: x + y, rv)
265
266 def get_ip(self, mac):
267 if self.mac_map.has_key(mac):
268 return self.mac_map[mac]
269 return (None, None)
270
271 def get_mac(self, ip):
272 if self.mac_inverse_map.has_key(ip):
273 return self.mac_inverse_map[ip]
274 return (None, None)
275
276 def ipToMac(self, ip):
277 '''Generate a mac from a ip'''
278
279 mcast = self.is_mcast(ip)
280 mac = "01:00:5e" if mcast == True else "00:00:00"
281 octets = ip.split(".")
282 for x in range(1,4):
283 num = str(hex(int(octets[x])))
284 num = num.split("x")[1]
285 if len(num) < 2:
286 num = "0" + str(num)
287 mac += ":" + num
288 return mac
289
290 def incIP(self, ip, n=1):
291 '''Increment an IP'''
292
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700293 if n < 1:
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -0800294 return ip
295 o = ip.split(".")
296 for ii in range(3,-1,-1):
297 if int(o[ii]) < 255:
298 o[ii] = str(int(o[ii]) + 1)
299 break
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700300 else:
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -0800301 o[ii] = str(0)
302
303 n -= 1
304 return self.incIP(".".join(o), n)