blob: 8a36efdfc18e6a62944d27bdcdb9e96850462a92 [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
33
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080034
35 def is_mcast(self, ip):
36 mcast_octet = (atol(ip) >> 24) & 0xff
37 return True if mcast_octet >= 224 and mcast_octet <= 239 else False
38
Chetan Gaonker1f7c3f82016-03-08 12:17:37 -080039 def discover(self, mac = None, update_seed = False):
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080040 '''Send a DHCP discover/offer'''
41
42 if mac is None:
43 mac = self.seed_mac
44 if update_seed:
45 self.seed_ip = self.incIP(self.seed_ip)
46 self.seed_mac = self.ipToMac(self.seed_ip)
47 mac = self.seed_mac
Chetan Gaonkerf1483862016-05-06 14:14:31 -070048
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080049 chmac = self.macToChaddr(mac)
Chetan Gaonkerf72ca402016-05-02 16:29:32 -070050 self.bootpmac = chmac
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080051 L2 = Ether(dst="ff:ff:ff:ff:ff:ff", src=mac)
52 L3 = IP(src="0.0.0.0", dst="255.255.255.255")
53 L4 = UDP(sport=68, dport=67)
54 L5 = BOOTP(chaddr=chmac)
55 L6 = DHCP(options=[("message-type","discover"),"end"])
Chetan Gaonker49ef0852016-03-23 15:06:18 -070056 resp = srp1(L2/L3/L4/L5/L6, filter="udp and port 68", timeout=10, iface=self.iface)
Chetan Gaonkerf72ca402016-05-02 16:29:32 -070057 self.dhcpresp = resp
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080058 try:
59 srcIP = resp.yiaddr
60 serverIP = resp.siaddr
61 except AttributeError:
62 print("Failed to acquire IP via DHCP for %s on interface %s" %(mac, self.iface))
63 return (None, None)
64
Chetan Gaonkercd86bdd2016-03-17 00:08:12 -070065 subnet_mask = "0.0.0.0"
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080066 for x in resp.lastlayer().options:
67 if(x == 'end'):
68 break
69 op,val = x
70 if(op == "subnet_mask"):
71 subnet_mask = val
72 elif(op == 'server_id'):
73 server_id = val
Chetan Gaonkerf1483862016-05-06 14:14:31 -070074
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080075 L5 = BOOTP(chaddr=chmac, yiaddr=srcIP)
Chetan Gaonkerf1483862016-05-06 14:14:31 -070076 L6 = DHCP(options=[("message-type","request"), ("server_id",server_id),
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080077 ("subnet_mask",subnet_mask), ("requested_addr",srcIP), "end"])
Chetan Gaonker49ef0852016-03-23 15:06:18 -070078 srp(L2/L3/L4/L5/L6, filter="udp and port 68", timeout=10, iface=self.iface)
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080079 self.mac_map[mac] = (srcIP, serverIP)
80 self.mac_inverse_map[srcIP] = (mac, serverIP)
81 return (srcIP, serverIP)
82
Chetan Gaonkerf1483862016-05-06 14:14:31 -070083 def only_discover(self, mac = None, desired = False, lease_time = False):
Chetan Gaonkerf72ca402016-05-02 16:29:32 -070084 '''Send a DHCP discover'''
85
86 if mac is None:
87 mac = self.seed_mac
88
89 chmac = self.macToChaddr(mac)
Chetan Gaonkerf1483862016-05-06 14:14:31 -070090 self.bootpmac = chmac
Chetan Gaonkerf72ca402016-05-02 16:29:32 -070091 L2 = Ether(dst="ff:ff:ff:ff:ff:ff", src=mac)
92 L3 = IP(src="0.0.0.0", dst="255.255.255.255")
93 L4 = UDP(sport=68, dport=67)
Chetan Gaonkerf1483862016-05-06 14:14:31 -070094 L5 = BOOTP(chaddr=chmac)
Chetan Gaonkerf72ca402016-05-02 16:29:32 -070095 if desired:
96 L6 = DHCP(options=[("message-type","discover"),("requested_addr",self.seed_ip),"end"])
Chetan Gaonkerf1483862016-05-06 14:14:31 -070097
Chetan Gaonkerc11d3222016-05-11 17:39:36 -070098 elif lease_time:
Chetan Gaonkerf1483862016-05-06 14:14:31 -070099 L6 = DHCP(options=[("message-type","discover"),("lease_time",700),"end"])
100
Chetan Gaonkerc11d3222016-05-11 17:39:36 -0700101 else:
102 L6 = DHCP(options=[("message-type","discover"),"end"])
Chetan Gaonkerf72ca402016-05-02 16:29:32 -0700103
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700104
105 resp = srp1(L2/L3/L4/L5/L6, filter="udp and port 68", timeout=10, iface=self.iface)
106 if resp == None:
Chetan Gaonkerc11d3222016-05-11 17:39:36 -0700107 return (None, None, mac)
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700108
109 self.dhcpresp = resp
110 for x in resp.lastlayer().options:
111 if(x == 'end'):
112 break
113 op,val = x
114 if(op == "message-type"):
115
116 if(val == 2):
117
118 try:
119 srcIP = resp.yiaddr
120 serverIP = resp.siaddr
121 except AttributeError:
122 print "In Attribute error."
123 print("Failed to acquire IP via DHCP for %s on interface %s" %(mac, self.iface))
124 return (None, None, None)
Chetan Gaonkerc11d3222016-05-11 17:39:36 -0700125
126
127 return (srcIP, serverIP, mac)
128
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700129 elif(val == 6):
130
131 return (None, None, mac)
Chetan Gaonkerf72ca402016-05-02 16:29:32 -0700132
133
Chetan Gaonkerc11d3222016-05-11 17:39:36 -0700134 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 -0700135 '''Send a DHCP offer'''
136
Chetan Gaonkerf72ca402016-05-02 16:29:32 -0700137 subnet_mask = "0.0.0.0"
138 for x in self.dhcpresp.lastlayer().options:
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700139 if(x == 'end'):
140 break
141 op,val = x
142 if(op == "subnet_mask"):
143 subnet_mask = val
144 elif(op == 'server_id'):
145 server_id = val
Chetan Gaonkerf72ca402016-05-02 16:29:32 -0700146
Chetan Gaonkerc11d3222016-05-11 17:39:36 -0700147 if unicast and self.servermac:
148 L2 = Ether(dst=self.servermac, src=mac)
149 L3 = IP(src=cip, dst=server_id)
150 else:
151 L2 = Ether(dst="ff:ff:ff:ff:ff:ff", src=mac)
152 if self.after_T2:
153 L3 = IP(src=cip, dst="255.255.255.255")
154 else:
155 L3 = IP(src="0.0.0.0", dst="255.255.255.255")
Chetan Gaonkerf72ca402016-05-02 16:29:32 -0700156 L4 = UDP(sport=68, dport=67)
Chetan Gaonkerc11d3222016-05-11 17:39:36 -0700157
158 if self.after_T2 == True:
159 L5 = BOOTP(chaddr=self.bootpmac, ciaddr = cip)
160 else:
161
162 L5 = BOOTP(chaddr=self.bootpmac, yiaddr=cip)
163
164 if cl_reboot or self.after_T2:
165 L6 = DHCP(options=[("message-type","request"),("subnet_mask",subnet_mask), ("requested_addr",cip), "end"])
166 else:
167 L6 = DHCP(options=[("message-type","request"), ("server_id",server_id),
168 ("subnet_mask",subnet_mask), ("requested_addr",cip), "end"])
169
Chetan Gaonkerf72ca402016-05-02 16:29:32 -0700170 resp=srp1(L2/L3/L4/L5/L6, filter="udp and port 68", timeout=10, iface=self.iface)
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700171 if resp == None:
172 return (None, None)
173
174 for x in resp.lastlayer().options:
175 if(x == 'end'):
176 break
177 op,val = x
178 if(op == "message-type"):
179
180 if(val == 5):
181
182 try:
183 srcIP = resp.yiaddr
184 serverIP = resp.siaddr
185 except AttributeError:
186 print "In Attribute error."
187 print("Failed to acquire IP via DHCP for %s on interface %s" %(mac, self.iface))
188 return (None, None)
Chetan Gaonkerc11d3222016-05-11 17:39:36 -0700189
190 if lease_time or renew_time or rebind_time:
191 for x in resp.lastlayer().options:
192 if(x == 'end'):
193 break
194 op,val = x
195
196 if op == "lease_time":
197 if lease_time == True:
198 self.mac_map[mac] = (srcIP, serverIP)
199 self.mac_inverse_map[srcIP] = (mac, serverIP)
200 return (srcIP, serverIP, val)
201 elif op == "renewal_time":
202 if renew_time == True:
203 self.mac_map[mac] = (srcIP, serverIP)
204 self.mac_inverse_map[srcIP] = (mac, serverIP)
205 return (srcIP, serverIP, val)
206 elif op == "rebinding_time":
207 if rebind_time == True:
208 self.mac_map[mac] = (srcIP, serverIP)
209 self.mac_inverse_map[srcIP] = (mac, serverIP)
210 return (srcIP, serverIP, val)
211 else:
212 self.mac_map[mac] = (srcIP, serverIP)
213 self.mac_inverse_map[srcIP] = (mac, serverIP)
214 return (srcIP, serverIP)
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700215 elif(val == 6):
216
217 return (None, None)
218
219
Chetan Gaonkerf72ca402016-05-02 16:29:32 -0700220
Chetan Gaonker1f7c3f82016-03-08 12:17:37 -0800221 def discover_next(self):
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -0800222 '''Send next dhcp discover/request with updated mac'''
Chetan Gaonker1f7c3f82016-03-08 12:17:37 -0800223 return self.discover(update_seed = True)
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -0800224
Chetan Gaonker1f7c3f82016-03-08 12:17:37 -0800225 def release(self, ip):
226 '''Send a DHCP discover/offer'''
227 if ip is None:
228 return False
229 if not self.mac_inverse_map.has_key(ip):
230 return False
231 mac, server_ip = self.mac_inverse_map[ip]
232 chmac = self.macToChaddr(mac)
233 L2 = Ether(dst="ff:ff:ff:ff:ff:ff", src=mac)
234 L3 = IP(src="0.0.0.0", dst="255.255.255.255")
235 L4 = UDP(sport=68, dport=67)
236 L5 = BOOTP(chaddr=chmac, ciaddr = ip)
237 L6 = DHCP(options=[("message-type","release"), ("server_id", server_ip), "end"])
238 sendp(L2/L3/L4/L5/L6, iface = self.iface)
239 del self.mac_map[mac]
240 del self.mac_inverse_map[ip]
241 return True
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -0800242
243 def macToChaddr(self, mac):
244 rv = []
245 mac = mac.split(":")
246 for x in mac:
247 rv.append(chr(int(x, 16)))
248 return reduce(lambda x,y: x + y, rv)
249
250 def get_ip(self, mac):
251 if self.mac_map.has_key(mac):
252 return self.mac_map[mac]
253 return (None, None)
254
255 def get_mac(self, ip):
256 if self.mac_inverse_map.has_key(ip):
257 return self.mac_inverse_map[ip]
258 return (None, None)
259
260 def ipToMac(self, ip):
261 '''Generate a mac from a ip'''
262
263 mcast = self.is_mcast(ip)
264 mac = "01:00:5e" if mcast == True else "00:00:00"
265 octets = ip.split(".")
266 for x in range(1,4):
267 num = str(hex(int(octets[x])))
268 num = num.split("x")[1]
269 if len(num) < 2:
270 num = "0" + str(num)
271 mac += ":" + num
272 return mac
273
274 def incIP(self, ip, n=1):
275 '''Increment an IP'''
276
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700277 if n < 1:
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -0800278 return ip
279 o = ip.split(".")
280 for ii in range(3,-1,-1):
281 if int(o[ii]) < 255:
282 o[ii] = str(int(o[ii]) + 1)
283 break
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700284 else:
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -0800285 o[ii] = str(0)
286
287 n -= 1
288 return self.incIP(".".join(o), n)