blob: 653b1e5408d601437ca92993d55def44f0770e7a [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 Gaonkerb2bd8242016-03-03 15:39:24 -080031
32 def is_mcast(self, ip):
33 mcast_octet = (atol(ip) >> 24) & 0xff
34 return True if mcast_octet >= 224 and mcast_octet <= 239 else False
35
Chetan Gaonker1f7c3f82016-03-08 12:17:37 -080036 def discover(self, mac = None, update_seed = False):
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080037 '''Send a DHCP discover/offer'''
38
39 if mac is None:
40 mac = self.seed_mac
41 if update_seed:
42 self.seed_ip = self.incIP(self.seed_ip)
43 self.seed_mac = self.ipToMac(self.seed_ip)
44 mac = self.seed_mac
Chetan Gaonkerf1483862016-05-06 14:14:31 -070045
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080046 chmac = self.macToChaddr(mac)
Chetan Gaonkerf72ca402016-05-02 16:29:32 -070047 self.bootpmac = chmac
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080048 L2 = Ether(dst="ff:ff:ff:ff:ff:ff", src=mac)
49 L3 = IP(src="0.0.0.0", dst="255.255.255.255")
50 L4 = UDP(sport=68, dport=67)
51 L5 = BOOTP(chaddr=chmac)
52 L6 = DHCP(options=[("message-type","discover"),"end"])
Chetan Gaonker49ef0852016-03-23 15:06:18 -070053 resp = srp1(L2/L3/L4/L5/L6, filter="udp and port 68", timeout=10, iface=self.iface)
Chetan Gaonkerf72ca402016-05-02 16:29:32 -070054 self.dhcpresp = resp
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080055 try:
56 srcIP = resp.yiaddr
57 serverIP = resp.siaddr
58 except AttributeError:
59 print("Failed to acquire IP via DHCP for %s on interface %s" %(mac, self.iface))
60 return (None, None)
61
Chetan Gaonkercd86bdd2016-03-17 00:08:12 -070062 subnet_mask = "0.0.0.0"
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080063 for x in resp.lastlayer().options:
64 if(x == 'end'):
65 break
66 op,val = x
67 if(op == "subnet_mask"):
68 subnet_mask = val
69 elif(op == 'server_id'):
70 server_id = val
Chetan Gaonkerf1483862016-05-06 14:14:31 -070071
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080072 L5 = BOOTP(chaddr=chmac, yiaddr=srcIP)
Chetan Gaonkerf1483862016-05-06 14:14:31 -070073 L6 = DHCP(options=[("message-type","request"), ("server_id",server_id),
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080074 ("subnet_mask",subnet_mask), ("requested_addr",srcIP), "end"])
Chetan Gaonker49ef0852016-03-23 15:06:18 -070075 srp(L2/L3/L4/L5/L6, filter="udp and port 68", timeout=10, iface=self.iface)
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080076 self.mac_map[mac] = (srcIP, serverIP)
77 self.mac_inverse_map[srcIP] = (mac, serverIP)
78 return (srcIP, serverIP)
79
Chetan Gaonkerf1483862016-05-06 14:14:31 -070080 def only_discover(self, mac = None, desired = False, lease_time = False):
Chetan Gaonkerf72ca402016-05-02 16:29:32 -070081 '''Send a DHCP discover'''
82
83 if mac is None:
84 mac = self.seed_mac
85
86 chmac = self.macToChaddr(mac)
Chetan Gaonkerf1483862016-05-06 14:14:31 -070087 self.bootpmac = chmac
Chetan Gaonkerf72ca402016-05-02 16:29:32 -070088 L2 = Ether(dst="ff:ff:ff:ff:ff:ff", src=mac)
89 L3 = IP(src="0.0.0.0", dst="255.255.255.255")
90 L4 = UDP(sport=68, dport=67)
Chetan Gaonkerf1483862016-05-06 14:14:31 -070091 L5 = BOOTP(chaddr=chmac)
Chetan Gaonkerf72ca402016-05-02 16:29:32 -070092 if desired:
93 L6 = DHCP(options=[("message-type","discover"),("requested_addr",self.seed_ip),"end"])
Chetan Gaonkerf1483862016-05-06 14:14:31 -070094
95 elif time:
96 L6 = DHCP(options=[("message-type","discover"),("lease_time",700),"end"])
97
98 elif lease_time:
Chetan Gaonkerf72ca402016-05-02 16:29:32 -070099 L6 = DHCP(options=[("message-type","discover"),"end"])
100
Chetan Gaonkerf72ca402016-05-02 16:29:32 -0700101
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700102
103 resp = srp1(L2/L3/L4/L5/L6, filter="udp and port 68", timeout=10, iface=self.iface)
104 if resp == None:
105 return (None, None, None)
106
107 self.dhcpresp = resp
108 for x in resp.lastlayer().options:
109 if(x == 'end'):
110 break
111 op,val = x
112 if(op == "message-type"):
113
114 if(val == 2):
115
116 try:
117 srcIP = resp.yiaddr
118 serverIP = resp.siaddr
119 except AttributeError:
120 print "In Attribute error."
121 print("Failed to acquire IP via DHCP for %s on interface %s" %(mac, self.iface))
122 return (None, None, None)
123
124 if lease_time == True:
125 for x in resp.lastlayer().options:
126 if(x == 'end'):
127 break
128 op,val = x
129 if(op == "lease_time"):
130 return (srcIP, serverIP, mac, val)
131 else:
132 return (srcIP, serverIP, mac)
133
134 elif(val == 6):
135
136 return (None, None, mac)
Chetan Gaonkerf72ca402016-05-02 16:29:32 -0700137
138
139 def only_request(self, cip, mac):
140 '''Send a DHCP offer'''
141
Chetan Gaonkerf72ca402016-05-02 16:29:32 -0700142 subnet_mask = "0.0.0.0"
143 for x in self.dhcpresp.lastlayer().options:
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700144 if(x == 'end'):
145 break
146 op,val = x
147 if(op == "subnet_mask"):
148 subnet_mask = val
149 elif(op == 'server_id'):
150 server_id = val
Chetan Gaonkerf72ca402016-05-02 16:29:32 -0700151
152 L2 = Ether(dst="ff:ff:ff:ff:ff:ff", src=mac)
153 L3 = IP(src="0.0.0.0", dst="255.255.255.255")
154 L4 = UDP(sport=68, dport=67)
155 L5 = BOOTP(chaddr=self.bootpmac, yiaddr=cip)
156 L6 = DHCP(options=[("message-type","request"), ("server_id",server_id),
157 ("subnet_mask",subnet_mask), ("requested_addr",cip), "end"])
158 resp=srp1(L2/L3/L4/L5/L6, filter="udp and port 68", timeout=10, iface=self.iface)
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700159 if resp == None:
160 return (None, None)
161
162 for x in resp.lastlayer().options:
163 if(x == 'end'):
164 break
165 op,val = x
166 if(op == "message-type"):
167
168 if(val == 5):
169
170 try:
171 srcIP = resp.yiaddr
172 serverIP = resp.siaddr
173 except AttributeError:
174 print "In Attribute error."
175 print("Failed to acquire IP via DHCP for %s on interface %s" %(mac, self.iface))
176 return (None, None)
177 self.mac_map[mac] = (srcIP, serverIP)
178 self.mac_inverse_map[srcIP] = (mac, serverIP)
Chetan Gaonkerf72ca402016-05-02 16:29:32 -0700179
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700180 return (srcIP, serverIP)
181
182 elif(val == 6):
183
184 return (None, None)
185
186
Chetan Gaonkerf72ca402016-05-02 16:29:32 -0700187
Chetan Gaonker1f7c3f82016-03-08 12:17:37 -0800188 def discover_next(self):
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -0800189 '''Send next dhcp discover/request with updated mac'''
Chetan Gaonker1f7c3f82016-03-08 12:17:37 -0800190 return self.discover(update_seed = True)
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -0800191
Chetan Gaonker1f7c3f82016-03-08 12:17:37 -0800192 def release(self, ip):
193 '''Send a DHCP discover/offer'''
194 if ip is None:
195 return False
196 if not self.mac_inverse_map.has_key(ip):
197 return False
198 mac, server_ip = self.mac_inverse_map[ip]
199 chmac = self.macToChaddr(mac)
200 L2 = Ether(dst="ff:ff:ff:ff:ff:ff", src=mac)
201 L3 = IP(src="0.0.0.0", dst="255.255.255.255")
202 L4 = UDP(sport=68, dport=67)
203 L5 = BOOTP(chaddr=chmac, ciaddr = ip)
204 L6 = DHCP(options=[("message-type","release"), ("server_id", server_ip), "end"])
205 sendp(L2/L3/L4/L5/L6, iface = self.iface)
206 del self.mac_map[mac]
207 del self.mac_inverse_map[ip]
208 return True
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -0800209
210 def macToChaddr(self, mac):
211 rv = []
212 mac = mac.split(":")
213 for x in mac:
214 rv.append(chr(int(x, 16)))
215 return reduce(lambda x,y: x + y, rv)
216
217 def get_ip(self, mac):
218 if self.mac_map.has_key(mac):
219 return self.mac_map[mac]
220 return (None, None)
221
222 def get_mac(self, ip):
223 if self.mac_inverse_map.has_key(ip):
224 return self.mac_inverse_map[ip]
225 return (None, None)
226
227 def ipToMac(self, ip):
228 '''Generate a mac from a ip'''
229
230 mcast = self.is_mcast(ip)
231 mac = "01:00:5e" if mcast == True else "00:00:00"
232 octets = ip.split(".")
233 for x in range(1,4):
234 num = str(hex(int(octets[x])))
235 num = num.split("x")[1]
236 if len(num) < 2:
237 num = "0" + str(num)
238 mac += ":" + num
239 return mac
240
241 def incIP(self, ip, n=1):
242 '''Increment an IP'''
243
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700244 if n < 1:
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -0800245 return ip
246 o = ip.split(".")
247 for ii in range(3,-1,-1):
248 if int(o[ii]) < 255:
249 o[ii] = str(int(o[ii]) + 1)
250 break
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700251 else:
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -0800252 o[ii] = str(0)
253
254 n -= 1
255 return self.incIP(".".join(o), n)