blob: 61c27a38fb9579fa03bca199dcf5b1c2c3136f35 [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 = {}
14
15 def is_mcast(self, ip):
16 mcast_octet = (atol(ip) >> 24) & 0xff
17 return True if mcast_octet >= 224 and mcast_octet <= 239 else False
18
Chetan Gaonker1f7c3f82016-03-08 12:17:37 -080019 def discover(self, mac = None, update_seed = False):
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080020 '''Send a DHCP discover/offer'''
21
22 if mac is None:
23 mac = self.seed_mac
24 if update_seed:
25 self.seed_ip = self.incIP(self.seed_ip)
26 self.seed_mac = self.ipToMac(self.seed_ip)
27 mac = self.seed_mac
28
29 chmac = self.macToChaddr(mac)
30 L2 = Ether(dst="ff:ff:ff:ff:ff:ff", src=mac)
31 L3 = IP(src="0.0.0.0", dst="255.255.255.255")
32 L4 = UDP(sport=68, dport=67)
33 L5 = BOOTP(chaddr=chmac)
34 L6 = DHCP(options=[("message-type","discover"),"end"])
Chetan Gaonker49ef0852016-03-23 15:06:18 -070035 resp = srp1(L2/L3/L4/L5/L6, filter="udp and port 68", timeout=10, iface=self.iface)
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080036 try:
37 srcIP = resp.yiaddr
38 serverIP = resp.siaddr
39 except AttributeError:
40 print("Failed to acquire IP via DHCP for %s on interface %s" %(mac, self.iface))
41 return (None, None)
42
Chetan Gaonkercd86bdd2016-03-17 00:08:12 -070043 subnet_mask = "0.0.0.0"
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080044 for x in resp.lastlayer().options:
45 if(x == 'end'):
46 break
47 op,val = x
48 if(op == "subnet_mask"):
49 subnet_mask = val
50 elif(op == 'server_id'):
51 server_id = val
52
53 L5 = BOOTP(chaddr=chmac, yiaddr=srcIP)
54 L6 = DHCP(options=[("message-type","request"), ("server_id",server_id),
55 ("subnet_mask",subnet_mask), ("requested_addr",srcIP), "end"])
Chetan Gaonker49ef0852016-03-23 15:06:18 -070056 srp(L2/L3/L4/L5/L6, filter="udp and port 68", timeout=10, iface=self.iface)
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080057 self.mac_map[mac] = (srcIP, serverIP)
58 self.mac_inverse_map[srcIP] = (mac, serverIP)
59 return (srcIP, serverIP)
60
Chetan Gaonker1f7c3f82016-03-08 12:17:37 -080061 def discover_next(self):
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080062 '''Send next dhcp discover/request with updated mac'''
Chetan Gaonker1f7c3f82016-03-08 12:17:37 -080063 return self.discover(update_seed = True)
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080064
Chetan Gaonker1f7c3f82016-03-08 12:17:37 -080065 def release(self, ip):
66 '''Send a DHCP discover/offer'''
67 if ip is None:
68 return False
69 if not self.mac_inverse_map.has_key(ip):
70 return False
71 mac, server_ip = self.mac_inverse_map[ip]
72 chmac = self.macToChaddr(mac)
73 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)
76 L5 = BOOTP(chaddr=chmac, ciaddr = ip)
77 L6 = DHCP(options=[("message-type","release"), ("server_id", server_ip), "end"])
78 sendp(L2/L3/L4/L5/L6, iface = self.iface)
79 del self.mac_map[mac]
80 del self.mac_inverse_map[ip]
81 return True
Chetan Gaonkerb2bd8242016-03-03 15:39:24 -080082
83 def macToChaddr(self, mac):
84 rv = []
85 mac = mac.split(":")
86 for x in mac:
87 rv.append(chr(int(x, 16)))
88 return reduce(lambda x,y: x + y, rv)
89
90 def get_ip(self, mac):
91 if self.mac_map.has_key(mac):
92 return self.mac_map[mac]
93 return (None, None)
94
95 def get_mac(self, ip):
96 if self.mac_inverse_map.has_key(ip):
97 return self.mac_inverse_map[ip]
98 return (None, None)
99
100 def ipToMac(self, ip):
101 '''Generate a mac from a ip'''
102
103 mcast = self.is_mcast(ip)
104 mac = "01:00:5e" if mcast == True else "00:00:00"
105 octets = ip.split(".")
106 for x in range(1,4):
107 num = str(hex(int(octets[x])))
108 num = num.split("x")[1]
109 if len(num) < 2:
110 num = "0" + str(num)
111 mac += ":" + num
112 return mac
113
114 def incIP(self, ip, n=1):
115 '''Increment an IP'''
116
117 if n < 1:
118 return ip
119 o = ip.split(".")
120 for ii in range(3,-1,-1):
121 if int(o[ii]) < 255:
122 o[ii] = str(int(o[ii]) + 1)
123 break
124 else:
125 o[ii] = str(0)
126
127 n -= 1
128 return self.incIP(".".join(o), n)