blob: 1ce8801c38de6da3637b5864e0048fbf6afac26a [file] [log] [blame]
Chetan Gaonkercb122cc2016-05-10 10:58:34 -07001#!/usr/bin/env python
Chetan Gaonkercfcce782016-05-10 10:10:42 -07002#
3# Copyright 2016-present Ciena Corporation
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16#
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080017from scapy.all import *
18
19conf.verb = 0 # Disable Scapy verbosity
20conf.checkIPaddr = 0 # Don't check response packets for matching destination IPs
21
22class DHCPTest:
23
24 def __init__(self, seed_ip = '192.168.1.1', iface = 'veth0'):
25 self.seed_ip = seed_ip
26 self.seed_mac = self.ipToMac(self.seed_ip)
27 self.iface = iface
28 self.mac_map = {}
29 self.mac_inverse_map = {}
Chetan Gaonkerf72ca402016-05-02 16:29:32 -070030 self.bootpmac = None
31 self.dhcpresp = None
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080032
33 def is_mcast(self, ip):
34 mcast_octet = (atol(ip) >> 24) & 0xff
35 return True if mcast_octet >= 224 and mcast_octet <= 239 else False
36
Chetan Gaonker1f7c3f82016-03-08 12:17:37 -080037 def discover(self, mac = None, update_seed = False):
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080038 '''Send a DHCP discover/offer'''
39
40 if mac is None:
41 mac = self.seed_mac
42 if update_seed:
43 self.seed_ip = self.incIP(self.seed_ip)
44 self.seed_mac = self.ipToMac(self.seed_ip)
45 mac = self.seed_mac
Chetan Gaonkerf1483862016-05-06 14:14:31 -070046
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080047 chmac = self.macToChaddr(mac)
Chetan Gaonkerf72ca402016-05-02 16:29:32 -070048 self.bootpmac = chmac
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080049 L2 = Ether(dst="ff:ff:ff:ff:ff:ff", src=mac)
50 L3 = IP(src="0.0.0.0", dst="255.255.255.255")
51 L4 = UDP(sport=68, dport=67)
52 L5 = BOOTP(chaddr=chmac)
53 L6 = DHCP(options=[("message-type","discover"),"end"])
Chetan Gaonker49ef0852016-03-23 15:06:18 -070054 resp = srp1(L2/L3/L4/L5/L6, filter="udp and port 68", timeout=10, iface=self.iface)
Chetan Gaonkerf72ca402016-05-02 16:29:32 -070055 self.dhcpresp = resp
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080056 try:
57 srcIP = resp.yiaddr
58 serverIP = resp.siaddr
59 except AttributeError:
60 print("Failed to acquire IP via DHCP for %s on interface %s" %(mac, self.iface))
61 return (None, None)
62
Chetan Gaonkercd86bdd2016-03-17 00:08:12 -070063 subnet_mask = "0.0.0.0"
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080064 for x in resp.lastlayer().options:
65 if(x == 'end'):
66 break
67 op,val = x
68 if(op == "subnet_mask"):
69 subnet_mask = val
70 elif(op == 'server_id'):
71 server_id = val
Chetan Gaonkerf1483862016-05-06 14:14:31 -070072
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080073 L5 = BOOTP(chaddr=chmac, yiaddr=srcIP)
Chetan Gaonkerf1483862016-05-06 14:14:31 -070074 L6 = DHCP(options=[("message-type","request"), ("server_id",server_id),
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080075 ("subnet_mask",subnet_mask), ("requested_addr",srcIP), "end"])
Chetan Gaonker49ef0852016-03-23 15:06:18 -070076 srp(L2/L3/L4/L5/L6, filter="udp and port 68", timeout=10, iface=self.iface)
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080077 self.mac_map[mac] = (srcIP, serverIP)
78 self.mac_inverse_map[srcIP] = (mac, serverIP)
79 return (srcIP, serverIP)
80
Chetan Gaonkerf1483862016-05-06 14:14:31 -070081 def only_discover(self, mac = None, desired = False, lease_time = False):
Chetan Gaonkerf72ca402016-05-02 16:29:32 -070082 '''Send a DHCP discover'''
83
84 if mac is None:
85 mac = self.seed_mac
86
87 chmac = self.macToChaddr(mac)
Chetan Gaonkerf1483862016-05-06 14:14:31 -070088 self.bootpmac = chmac
Chetan Gaonkerf72ca402016-05-02 16:29:32 -070089 L2 = Ether(dst="ff:ff:ff:ff:ff:ff", src=mac)
90 L3 = IP(src="0.0.0.0", dst="255.255.255.255")
91 L4 = UDP(sport=68, dport=67)
Chetan Gaonkerf1483862016-05-06 14:14:31 -070092 L5 = BOOTP(chaddr=chmac)
Chetan Gaonkerf72ca402016-05-02 16:29:32 -070093 if desired:
94 L6 = DHCP(options=[("message-type","discover"),("requested_addr",self.seed_ip),"end"])
Chetan Gaonkerf1483862016-05-06 14:14:31 -070095
96 elif time:
97 L6 = DHCP(options=[("message-type","discover"),("lease_time",700),"end"])
98
99 elif lease_time:
Chetan Gaonkerf72ca402016-05-02 16:29:32 -0700100 L6 = DHCP(options=[("message-type","discover"),"end"])
101
Chetan Gaonkerf72ca402016-05-02 16:29:32 -0700102
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700103
104 resp = srp1(L2/L3/L4/L5/L6, filter="udp and port 68", timeout=10, iface=self.iface)
105 if resp == None:
106 return (None, None, None)
107
108 self.dhcpresp = resp
109 for x in resp.lastlayer().options:
110 if(x == 'end'):
111 break
112 op,val = x
113 if(op == "message-type"):
114
115 if(val == 2):
116
117 try:
118 srcIP = resp.yiaddr
119 serverIP = resp.siaddr
120 except AttributeError:
121 print "In Attribute error."
122 print("Failed to acquire IP via DHCP for %s on interface %s" %(mac, self.iface))
123 return (None, None, None)
124
125 if lease_time == True:
126 for x in resp.lastlayer().options:
127 if(x == 'end'):
128 break
129 op,val = x
130 if(op == "lease_time"):
131 return (srcIP, serverIP, mac, val)
132 else:
133 return (srcIP, serverIP, mac)
134
135 elif(val == 6):
136
137 return (None, None, mac)
Chetan Gaonkerf72ca402016-05-02 16:29:32 -0700138
139
140 def only_request(self, cip, mac):
141 '''Send a DHCP offer'''
142
Chetan Gaonkerf72ca402016-05-02 16:29:32 -0700143 subnet_mask = "0.0.0.0"
144 for x in self.dhcpresp.lastlayer().options:
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700145 if(x == 'end'):
146 break
147 op,val = x
148 if(op == "subnet_mask"):
149 subnet_mask = val
150 elif(op == 'server_id'):
151 server_id = val
Chetan Gaonkerf72ca402016-05-02 16:29:32 -0700152
153 L2 = Ether(dst="ff:ff:ff:ff:ff:ff", src=mac)
154 L3 = IP(src="0.0.0.0", dst="255.255.255.255")
155 L4 = UDP(sport=68, dport=67)
156 L5 = BOOTP(chaddr=self.bootpmac, yiaddr=cip)
157 L6 = DHCP(options=[("message-type","request"), ("server_id",server_id),
158 ("subnet_mask",subnet_mask), ("requested_addr",cip), "end"])
159 resp=srp1(L2/L3/L4/L5/L6, filter="udp and port 68", timeout=10, iface=self.iface)
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700160 if resp == None:
161 return (None, None)
162
163 for x in resp.lastlayer().options:
164 if(x == 'end'):
165 break
166 op,val = x
167 if(op == "message-type"):
168
169 if(val == 5):
170
171 try:
172 srcIP = resp.yiaddr
173 serverIP = resp.siaddr
174 except AttributeError:
175 print "In Attribute error."
176 print("Failed to acquire IP via DHCP for %s on interface %s" %(mac, self.iface))
177 return (None, None)
178 self.mac_map[mac] = (srcIP, serverIP)
179 self.mac_inverse_map[srcIP] = (mac, serverIP)
Chetan Gaonkerf72ca402016-05-02 16:29:32 -0700180
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700181 return (srcIP, serverIP)
182
183 elif(val == 6):
184
185 return (None, None)
186
187
Chetan Gaonkerf72ca402016-05-02 16:29:32 -0700188
Chetan Gaonker1f7c3f82016-03-08 12:17:37 -0800189 def discover_next(self):
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -0800190 '''Send next dhcp discover/request with updated mac'''
Chetan Gaonker1f7c3f82016-03-08 12:17:37 -0800191 return self.discover(update_seed = True)
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -0800192
Chetan Gaonker1f7c3f82016-03-08 12:17:37 -0800193 def release(self, ip):
194 '''Send a DHCP discover/offer'''
195 if ip is None:
196 return False
197 if not self.mac_inverse_map.has_key(ip):
198 return False
199 mac, server_ip = self.mac_inverse_map[ip]
200 chmac = self.macToChaddr(mac)
201 L2 = Ether(dst="ff:ff:ff:ff:ff:ff", src=mac)
202 L3 = IP(src="0.0.0.0", dst="255.255.255.255")
203 L4 = UDP(sport=68, dport=67)
204 L5 = BOOTP(chaddr=chmac, ciaddr = ip)
205 L6 = DHCP(options=[("message-type","release"), ("server_id", server_ip), "end"])
206 sendp(L2/L3/L4/L5/L6, iface = self.iface)
207 del self.mac_map[mac]
208 del self.mac_inverse_map[ip]
209 return True
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -0800210
211 def macToChaddr(self, mac):
212 rv = []
213 mac = mac.split(":")
214 for x in mac:
215 rv.append(chr(int(x, 16)))
216 return reduce(lambda x,y: x + y, rv)
217
218 def get_ip(self, mac):
219 if self.mac_map.has_key(mac):
220 return self.mac_map[mac]
221 return (None, None)
222
223 def get_mac(self, ip):
224 if self.mac_inverse_map.has_key(ip):
225 return self.mac_inverse_map[ip]
226 return (None, None)
227
228 def ipToMac(self, ip):
229 '''Generate a mac from a ip'''
230
231 mcast = self.is_mcast(ip)
232 mac = "01:00:5e" if mcast == True else "00:00:00"
233 octets = ip.split(".")
234 for x in range(1,4):
235 num = str(hex(int(octets[x])))
236 num = num.split("x")[1]
237 if len(num) < 2:
238 num = "0" + str(num)
239 mac += ":" + num
240 return mac
241
242 def incIP(self, ip, n=1):
243 '''Increment an IP'''
244
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700245 if n < 1:
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -0800246 return ip
247 o = ip.split(".")
248 for ii in range(3,-1,-1):
249 if int(o[ii]) < 255:
250 o[ii] = str(int(o[ii]) + 1)
251 break
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700252 else:
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -0800253 o[ii] = str(0)
254
255 n -= 1
256 return self.incIP(".".join(o), n)