blob: 500c91831eeae3143a0db376d4f996b58458dc6c [file] [log] [blame]
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -08001from scapy.all import *
2
3conf.verb = 0 # Disable Scapy verbosity
4conf.checkIPaddr = 0 # Don't check response packets for matching destination IPs
5
6class DHCPTest:
7
8 def __init__(self, seed_ip = '192.168.1.1', iface = 'veth0'):
9 self.seed_ip = seed_ip
10 self.seed_mac = self.ipToMac(self.seed_ip)
11 self.iface = iface
12 self.mac_map = {}
13 self.mac_inverse_map = {}
Chetan Gaonkerf72ca402016-05-02 16:29:32 -070014 self.bootpmac = None
15 self.dhcpresp = None
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080016
17 def is_mcast(self, ip):
18 mcast_octet = (atol(ip) >> 24) & 0xff
19 return True if mcast_octet >= 224 and mcast_octet <= 239 else False
20
Chetan Gaonker1f7c3f82016-03-08 12:17:37 -080021 def discover(self, mac = None, update_seed = False):
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080022 '''Send a DHCP discover/offer'''
23
24 if mac is None:
25 mac = self.seed_mac
26 if update_seed:
27 self.seed_ip = self.incIP(self.seed_ip)
28 self.seed_mac = self.ipToMac(self.seed_ip)
29 mac = self.seed_mac
Chetan Gaonkerf1483862016-05-06 14:14:31 -070030
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080031 chmac = self.macToChaddr(mac)
Chetan Gaonkerf72ca402016-05-02 16:29:32 -070032 self.bootpmac = chmac
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080033 L2 = Ether(dst="ff:ff:ff:ff:ff:ff", src=mac)
34 L3 = IP(src="0.0.0.0", dst="255.255.255.255")
35 L4 = UDP(sport=68, dport=67)
36 L5 = BOOTP(chaddr=chmac)
37 L6 = DHCP(options=[("message-type","discover"),"end"])
Chetan Gaonker49ef0852016-03-23 15:06:18 -070038 resp = srp1(L2/L3/L4/L5/L6, filter="udp and port 68", timeout=10, iface=self.iface)
Chetan Gaonkerf72ca402016-05-02 16:29:32 -070039 self.dhcpresp = resp
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080040 try:
41 srcIP = resp.yiaddr
42 serverIP = resp.siaddr
43 except AttributeError:
44 print("Failed to acquire IP via DHCP for %s on interface %s" %(mac, self.iface))
45 return (None, None)
46
Chetan Gaonkercd86bdd2016-03-17 00:08:12 -070047 subnet_mask = "0.0.0.0"
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080048 for x in resp.lastlayer().options:
49 if(x == 'end'):
50 break
51 op,val = x
52 if(op == "subnet_mask"):
53 subnet_mask = val
54 elif(op == 'server_id'):
55 server_id = val
Chetan Gaonkerf1483862016-05-06 14:14:31 -070056
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080057 L5 = BOOTP(chaddr=chmac, yiaddr=srcIP)
Chetan Gaonkerf1483862016-05-06 14:14:31 -070058 L6 = DHCP(options=[("message-type","request"), ("server_id",server_id),
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080059 ("subnet_mask",subnet_mask), ("requested_addr",srcIP), "end"])
Chetan Gaonker49ef0852016-03-23 15:06:18 -070060 srp(L2/L3/L4/L5/L6, filter="udp and port 68", timeout=10, iface=self.iface)
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080061 self.mac_map[mac] = (srcIP, serverIP)
62 self.mac_inverse_map[srcIP] = (mac, serverIP)
63 return (srcIP, serverIP)
64
Chetan Gaonkerf1483862016-05-06 14:14:31 -070065 def only_discover(self, mac = None, desired = False, lease_time = False):
Chetan Gaonkerf72ca402016-05-02 16:29:32 -070066 '''Send a DHCP discover'''
67
68 if mac is None:
69 mac = self.seed_mac
70
71 chmac = self.macToChaddr(mac)
Chetan Gaonkerf1483862016-05-06 14:14:31 -070072 self.bootpmac = chmac
Chetan Gaonkerf72ca402016-05-02 16:29:32 -070073 L2 = Ether(dst="ff:ff:ff:ff:ff:ff", src=mac)
74 L3 = IP(src="0.0.0.0", dst="255.255.255.255")
75 L4 = UDP(sport=68, dport=67)
Chetan Gaonkerf1483862016-05-06 14:14:31 -070076 L5 = BOOTP(chaddr=chmac)
Chetan Gaonkerf72ca402016-05-02 16:29:32 -070077 if desired:
78 L6 = DHCP(options=[("message-type","discover"),("requested_addr",self.seed_ip),"end"])
Chetan Gaonkerf1483862016-05-06 14:14:31 -070079
80 elif time:
81 L6 = DHCP(options=[("message-type","discover"),("lease_time",700),"end"])
82
83 elif lease_time:
Chetan Gaonkerf72ca402016-05-02 16:29:32 -070084 L6 = DHCP(options=[("message-type","discover"),"end"])
85
Chetan Gaonkerf72ca402016-05-02 16:29:32 -070086
Chetan Gaonkerf1483862016-05-06 14:14:31 -070087
88 resp = srp1(L2/L3/L4/L5/L6, filter="udp and port 68", timeout=10, iface=self.iface)
89 if resp == None:
90 return (None, None, None)
91
92 self.dhcpresp = resp
93 for x in resp.lastlayer().options:
94 if(x == 'end'):
95 break
96 op,val = x
97 if(op == "message-type"):
98
99 if(val == 2):
100
101 try:
102 srcIP = resp.yiaddr
103 serverIP = resp.siaddr
104 except AttributeError:
105 print "In Attribute error."
106 print("Failed to acquire IP via DHCP for %s on interface %s" %(mac, self.iface))
107 return (None, None, None)
108
109 if lease_time == True:
110 for x in resp.lastlayer().options:
111 if(x == 'end'):
112 break
113 op,val = x
114 if(op == "lease_time"):
115 return (srcIP, serverIP, mac, val)
116 else:
117 return (srcIP, serverIP, mac)
118
119 elif(val == 6):
120
121 return (None, None, mac)
Chetan Gaonkerf72ca402016-05-02 16:29:32 -0700122
123
124 def only_request(self, cip, mac):
125 '''Send a DHCP offer'''
126
Chetan Gaonkerf72ca402016-05-02 16:29:32 -0700127 subnet_mask = "0.0.0.0"
128 for x in self.dhcpresp.lastlayer().options:
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700129 if(x == 'end'):
130 break
131 op,val = x
132 if(op == "subnet_mask"):
133 subnet_mask = val
134 elif(op == 'server_id'):
135 server_id = val
Chetan Gaonkerf72ca402016-05-02 16:29:32 -0700136
137 L2 = Ether(dst="ff:ff:ff:ff:ff:ff", src=mac)
138 L3 = IP(src="0.0.0.0", dst="255.255.255.255")
139 L4 = UDP(sport=68, dport=67)
140 L5 = BOOTP(chaddr=self.bootpmac, yiaddr=cip)
141 L6 = DHCP(options=[("message-type","request"), ("server_id",server_id),
142 ("subnet_mask",subnet_mask), ("requested_addr",cip), "end"])
143 resp=srp1(L2/L3/L4/L5/L6, filter="udp and port 68", timeout=10, iface=self.iface)
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700144 if resp == None:
145 return (None, None)
146
147 for x in resp.lastlayer().options:
148 if(x == 'end'):
149 break
150 op,val = x
151 if(op == "message-type"):
152
153 if(val == 5):
154
155 try:
156 srcIP = resp.yiaddr
157 serverIP = resp.siaddr
158 except AttributeError:
159 print "In Attribute error."
160 print("Failed to acquire IP via DHCP for %s on interface %s" %(mac, self.iface))
161 return (None, None)
162 self.mac_map[mac] = (srcIP, serverIP)
163 self.mac_inverse_map[srcIP] = (mac, serverIP)
Chetan Gaonkerf72ca402016-05-02 16:29:32 -0700164
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700165 return (srcIP, serverIP)
166
167 elif(val == 6):
168
169 return (None, None)
170
171
Chetan Gaonkerf72ca402016-05-02 16:29:32 -0700172
Chetan Gaonker1f7c3f82016-03-08 12:17:37 -0800173 def discover_next(self):
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -0800174 '''Send next dhcp discover/request with updated mac'''
Chetan Gaonker1f7c3f82016-03-08 12:17:37 -0800175 return self.discover(update_seed = True)
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -0800176
Chetan Gaonker1f7c3f82016-03-08 12:17:37 -0800177 def release(self, ip):
178 '''Send a DHCP discover/offer'''
179 if ip is None:
180 return False
181 if not self.mac_inverse_map.has_key(ip):
182 return False
183 mac, server_ip = self.mac_inverse_map[ip]
184 chmac = self.macToChaddr(mac)
185 L2 = Ether(dst="ff:ff:ff:ff:ff:ff", src=mac)
186 L3 = IP(src="0.0.0.0", dst="255.255.255.255")
187 L4 = UDP(sport=68, dport=67)
188 L5 = BOOTP(chaddr=chmac, ciaddr = ip)
189 L6 = DHCP(options=[("message-type","release"), ("server_id", server_ip), "end"])
190 sendp(L2/L3/L4/L5/L6, iface = self.iface)
191 del self.mac_map[mac]
192 del self.mac_inverse_map[ip]
193 return True
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -0800194
195 def macToChaddr(self, mac):
196 rv = []
197 mac = mac.split(":")
198 for x in mac:
199 rv.append(chr(int(x, 16)))
200 return reduce(lambda x,y: x + y, rv)
201
202 def get_ip(self, mac):
203 if self.mac_map.has_key(mac):
204 return self.mac_map[mac]
205 return (None, None)
206
207 def get_mac(self, ip):
208 if self.mac_inverse_map.has_key(ip):
209 return self.mac_inverse_map[ip]
210 return (None, None)
211
212 def ipToMac(self, ip):
213 '''Generate a mac from a ip'''
214
215 mcast = self.is_mcast(ip)
216 mac = "01:00:5e" if mcast == True else "00:00:00"
217 octets = ip.split(".")
218 for x in range(1,4):
219 num = str(hex(int(octets[x])))
220 num = num.split("x")[1]
221 if len(num) < 2:
222 num = "0" + str(num)
223 mac += ":" + num
224 return mac
225
226 def incIP(self, ip, n=1):
227 '''Increment an IP'''
228
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700229 if n < 1:
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -0800230 return ip
231 o = ip.split(".")
232 for ii in range(3,-1,-1):
233 if int(o[ii]) < 255:
234 o[ii] = str(int(o[ii]) + 1)
235 break
Chetan Gaonkerf1483862016-05-06 14:14:31 -0700236 else:
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -0800237 o[ii] = str(0)
238
239 n -= 1
240 return self.incIP(".".join(o), n)