You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

dhcp_packet.py 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  1. # pydhcplib
  2. # Copyright (C) 2008 Mathieu Ignacio -- mignacio@april.org
  3. #
  4. # This file is part of pydhcplib.
  5. # Pydhcplib is free software; you can redistribute it and/or modify
  6. # it under the terms of the GNU General Public License as published by
  7. # the Free Software Foundation; either version 3 of the License, or
  8. # (at your option) any later version.
  9. #
  10. # This program is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. # GNU General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License
  16. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. import operator
  18. from struct import unpack
  19. from struct import pack
  20. from dhcp_basic_packet import *
  21. from dhcp_constants import *
  22. from type_ipv4 import ipv4
  23. from type_strlist import strlist
  24. from type_hwmac import hwmac
  25. import sys
  26. class DhcpPacket(DhcpBasicPacket):
  27. def str(self):
  28. # Process headers :
  29. printable_data = "# Header fields\n"
  30. op = self.packet_data[DhcpFields['op'][0]:DhcpFields['op'][0]+DhcpFields['op'][1]]
  31. printable_data += "op : " + DhcpFieldsName['op'][str(op[0])] + "\n"
  32. for opt in ['htype','hlen','hops','xid','secs','flags',
  33. 'ciaddr','yiaddr','siaddr','giaddr','chaddr','sname','file'] :
  34. begin = DhcpFields[opt][0]
  35. end = DhcpFields[opt][0]+DhcpFields[opt][1]
  36. data = self.packet_data[begin:end]
  37. result = ''
  38. if DhcpFieldsTypes[opt] == "int" : result = str(data[0])
  39. elif DhcpFieldsTypes[opt] == "int2" : result = str(data[0]*256+data[1])
  40. elif DhcpFieldsTypes[opt] == "int4" : result = str(ipv4(data).int())
  41. elif DhcpFieldsTypes[opt] == "str" :
  42. for each in data :
  43. if each != 0 : result += chr(each)
  44. else : break
  45. elif DhcpFieldsTypes[opt] == "ipv4" : result = ipv4(data).str()
  46. elif DhcpFieldsTypes[opt] == "hwmac" :
  47. result = []
  48. hexsym = ['0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f']
  49. for iterator in range(6) :
  50. result += [str(hexsym[data[iterator]/16]+hexsym[data[iterator]%16])]
  51. result = ':'.join(result)
  52. printable_data += opt+" : "+result + "\n"
  53. # Process options :
  54. printable_data += "# Options fields\n"
  55. for opt in self.options_data.keys():
  56. data = self.options_data[opt]
  57. result = ""
  58. optnum = DhcpOptions[opt]
  59. if opt=='dhcp_message_type' : result = DhcpFieldsName['dhcp_message_type'][str(data[0])]
  60. elif DhcpOptionsTypes[optnum] == "char" : result = str(data[0])
  61. elif DhcpOptionsTypes[optnum] == "16-bits" : result = str(data[0]*256+data[0])
  62. elif DhcpOptionsTypes[optnum] == "32-bits" : result = str(ipv4(data).int())
  63. elif DhcpOptionsTypes[optnum] == "string" :
  64. for each in data :
  65. if each != 0 : result += chr(each)
  66. else : break
  67. elif DhcpOptionsTypes[optnum] == "ipv4" : result = ipv4(data).str()
  68. elif DhcpOptionsTypes[optnum] == "ipv4+" :
  69. for i in range(0,len(data),4) :
  70. if len(data[i:i+4]) == 4 :
  71. result += ipv4(data[i:i+4]).str() + " - "
  72. elif DhcpOptionsTypes[optnum] == "char+" :
  73. if optnum == 55 : # parameter_request_list
  74. result = ','.join([DhcpOptionsList[each] for each in data])
  75. else : result += str(data)
  76. printable_data += opt + " : " + result + "\n"
  77. return printable_data
  78. def AddLine(self,_string) :
  79. (parameter,junk,value) = _string.partition(':')
  80. parameter = parameter.strip()
  81. # If value begin with a whitespace, remove it, leave others
  82. if len(value)>0 and value[0] == ' ' : value = value[1:]
  83. value = self._OptionsToBinary(parameter,value)
  84. if value : self.SetOption(parameter,value)
  85. def _OptionsToBinary(self,parameter,value) :
  86. # Transform textual data into dhcp binary data
  87. p = parameter.strip()
  88. # 1- Search for header informations or specific parameter
  89. if p == 'op' or p == 'htype' :
  90. value = value.strip()
  91. if value.isdigit() : return [int(value)]
  92. try :
  93. value = DhcpNames[value.strip()]
  94. return [value]
  95. except KeyError :
  96. return [0]
  97. elif p == 'hlen' or p == 'hops' :
  98. try :
  99. value = int(value)
  100. return [value]
  101. except ValueError :
  102. return [0]
  103. elif p == 'secs' or p == 'flags' :
  104. try :
  105. value = ipv4(int(value)).list()
  106. except ValueError :
  107. value = [0,0,0,0]
  108. return value[2:]
  109. elif p == 'xid' :
  110. try :
  111. value = ipv4(int(value)).list()
  112. except ValueError :
  113. value = [0,0,0,0]
  114. return value
  115. elif p == 'ciaddr' or p == 'yiaddr' or p == 'siaddr' or p == 'giaddr' :
  116. try :
  117. ip = ipv4(value).list()
  118. except ValueError :
  119. ip = [0,0,0,0]
  120. return ip
  121. elif p == 'chaddr' :
  122. try:
  123. value = hwmac(value).list()+[0]*10
  124. except ValueError,TypeError :
  125. value = [0]*16
  126. return value
  127. elif p == 'sname' :
  128. return
  129. elif p == 'file' :
  130. return
  131. elif p == 'parameter_request_list' :
  132. value = value.strip().split(',')
  133. tmp = []
  134. for each in value:
  135. if DhcpOptions.has_key(each) : tmp.append(DhcpOptions[each])
  136. return tmp
  137. elif p=='dhcp_message_type' :
  138. try :
  139. return [DhcpNames[value]]
  140. except KeyError:
  141. return
  142. # 2- Search for options
  143. try : option_type = DhcpOptionsTypes[DhcpOptions[parameter]]
  144. except KeyError : return False
  145. if option_type == "ipv4" :
  146. # this is a single ip address
  147. try :
  148. binary_value = map(int,value.split("."))
  149. except ValueError : return False
  150. elif option_type == "ipv4+" :
  151. # this is multiple ip address
  152. iplist = value.split(",")
  153. opt = []
  154. for single in iplist :
  155. opt += (ipv4(single).list())
  156. binary_value = opt
  157. elif option_type == "32-bits" :
  158. # This is probably a number...
  159. try :
  160. digit = int(value)
  161. binary_value = [digit>>24&0xFF,(digit>>16)&0xFF,(digit>>8)&0xFF,digit&0xFF]
  162. except ValueError :
  163. return False
  164. elif option_type == "16-bits" :
  165. try :
  166. digit = int(value)
  167. binary_value = [(digit>>8)&0xFF,digit&0xFF]
  168. except ValueError : return False
  169. elif option_type == "char" :
  170. try :
  171. digit = int(value)
  172. binary_value = [digit&0xFF]
  173. except ValueError : return False
  174. elif option_type == "bool" :
  175. if value=="False" or value=="false" or value==0 :
  176. binary_value = [0]
  177. else : binary_value = [1]
  178. elif option_type == "string" :
  179. binary_value = strlist(value).list()
  180. else :
  181. binary_value = strlist(value).list()
  182. return binary_value
  183. # FIXME: This is called from IsDhcpSomethingPacket, but is this really
  184. # needed? Or maybe this testing should be done in
  185. # DhcpBasicPacket.DecodePacket().
  186. # Test Packet Type
  187. def IsDhcpSomethingPacket(self,type):
  188. if self.IsDhcpPacket() == False : return False
  189. if self.IsOption("dhcp_message_type") == False : return False
  190. if self.GetOption("dhcp_message_type") != type : return False
  191. return True
  192. def IsDhcpDiscoverPacket(self):
  193. return self.IsDhcpSomethingPacket([1])
  194. def IsDhcpOfferPacket(self):
  195. return self.IsDhcpSomethingPacket([2])
  196. def IsDhcpRequestPacket(self):
  197. return self.IsDhcpSomethingPacket([3])
  198. def IsDhcpDeclinePacket(self):
  199. return self.IsDhcpSomethingPacket([4])
  200. def IsDhcpAckPacket(self):
  201. return self.IsDhcpSomethingPacket([5])
  202. def IsDhcpNackPacket(self):
  203. return self.IsDhcpSomethingPacket([6])
  204. def IsDhcpReleasePacket(self):
  205. return self.IsDhcpSomethingPacket([7])
  206. def IsDhcpInformPacket(self):
  207. return self.IsDhcpSomethingPacket([8])
  208. def GetMultipleOptions(self,options=()):
  209. result = {}
  210. for each in options:
  211. result[each] = self.GetOption(each)
  212. return result
  213. def SetMultipleOptions(self,options={}):
  214. for each in options.keys():
  215. self.SetOption(each,options[each])
  216. # Creating Response Packet
  217. # Server-side functions
  218. # From RFC 2132 page 28/29
  219. def CreateDhcpOfferPacketFrom(self,src): # src = discover packet
  220. self.SetOption("htype",src.GetOption("htype"))
  221. self.SetOption("xid",src.GetOption("xid"))
  222. self.SetOption("flags",src.GetOption("flags"))
  223. self.SetOption("giaddr",src.GetOption("giaddr"))
  224. self.SetOption("chaddr",src.GetOption("chaddr"))
  225. self.SetOption("ip_address_lease_time",src.GetOption("ip_address_lease_time"))
  226. self.TransformToDhcpOfferPacket()
  227. def TransformToDhcpOfferPacket(self):
  228. self.SetOption("dhcp_message_type",[2])
  229. self.SetOption("op",[2])
  230. self.SetOption("hlen",[6])
  231. self.DeleteOption("secs")
  232. self.DeleteOption("ciaddr")
  233. self.DeleteOption("request_ip_address")
  234. self.DeleteOption("parameter_request_list")
  235. self.DeleteOption("client_identifier")
  236. self.DeleteOption("maximum_message_size")
  237. """ Dhcp ACK packet creation """
  238. def CreateDhcpAckPacketFrom(self,src): # src = request or inform packet
  239. self.SetOption("htype",src.GetOption("htype"))
  240. self.SetOption("xid",src.GetOption("xid"))
  241. self.SetOption("ciaddr",src.GetOption("ciaddr"))
  242. self.SetOption("flags",src.GetOption("flags"))
  243. self.SetOption("giaddr",src.GetOption("giaddr"))
  244. self.SetOption("chaddr",src.GetOption("chaddr"))
  245. self.SetOption("ip_address_lease_time_option",src.GetOption("ip_address_lease_time_option"))
  246. self.TransformToDhcpAckPacket()
  247. def TransformToDhcpAckPacket(self): # src = request or inform packet
  248. self.SetOption("op",[2])
  249. self.SetOption("hlen",[6])
  250. self.SetOption("dhcp_message_type",[5])
  251. self.DeleteOption("secs")
  252. self.DeleteOption("request_ip_address")
  253. self.DeleteOption("parameter_request_list")
  254. self.DeleteOption("client_identifier")
  255. self.DeleteOption("maximum_message_size")
  256. """ Dhcp NACK packet creation """
  257. def CreateDhcpNackPacketFrom(self,src): # src = request or inform packet
  258. self.SetOption("htype",src.GetOption("htype"))
  259. self.SetOption("xid",src.GetOption("xid"))
  260. self.SetOption("flags",src.GetOption("flags"))
  261. self.SetOption("giaddr",src.GetOption("giaddr"))
  262. self.SetOption("chaddr",src.GetOption("chaddr"))
  263. self.TransformToDhcpNackPacket()
  264. def TransformToDhcpNackPacket(self):
  265. self.SetOption("op",[2])
  266. self.SetOption("hlen",[6])
  267. self.DeleteOption("secs")
  268. self.DeleteOption("ciaddr")
  269. self.DeleteOption("yiaddr")
  270. self.DeleteOption("siaddr")
  271. self.DeleteOption("sname")
  272. self.DeleteOption("file")
  273. self.DeleteOption("request_ip_address")
  274. self.DeleteOption("ip_address_lease_time_option")
  275. self.DeleteOption("parameter_request_list")
  276. self.DeleteOption("client_identifier")
  277. self.DeleteOption("maximum_message_size")
  278. self.SetOption("dhcp_message_type",[6])
  279. """ GetClientIdentifier """
  280. def GetClientIdentifier(self) :
  281. if self.IsOption("client_identifier") :
  282. return self.GetOption("client_identifier")
  283. return []
  284. def GetGiaddr(self) :
  285. return self.GetOption("giaddr")
  286. def GetHardwareAddress(self) :
  287. length = self.GetOption("hlen")[0]
  288. full_hw = self.GetOption("chaddr")
  289. if length!=[] and length<len(full_hw) : return full_hw[0:length]
  290. return full_hw