pkt4.cc 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500
  1. // Copyright (C) 2011-2013 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // Permission to use, copy, modify, and/or distribute this software for any
  4. // purpose with or without fee is hereby granted, provided that the above
  5. // copyright notice and this permission notice appear in all copies.
  6. //
  7. // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  8. // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  9. // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  10. // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  11. // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  12. // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  13. // PERFORMANCE OF THIS SOFTWARE.
  14. #include <asiolink/io_address.h>
  15. #include <dhcp/dhcp4.h>
  16. #include <dhcp/libdhcp++.h>
  17. #include <dhcp/option_int.h>
  18. #include <dhcp/pkt4.h>
  19. #include <exceptions/exceptions.h>
  20. #include <algorithm>
  21. #include <iostream>
  22. #include <sstream>
  23. using namespace std;
  24. using namespace isc::dhcp;
  25. using namespace isc::asiolink;
  26. namespace isc {
  27. namespace dhcp {
  28. const IOAddress DEFAULT_ADDRESS("0.0.0.0");
  29. Pkt4::Pkt4(uint8_t msg_type, uint32_t transid)
  30. :buffer_out_(DHCPV4_PKT_HDR_LEN),
  31. local_addr_(DEFAULT_ADDRESS),
  32. remote_addr_(DEFAULT_ADDRESS),
  33. iface_(""),
  34. ifindex_(0),
  35. local_port_(DHCP4_SERVER_PORT),
  36. remote_port_(DHCP4_CLIENT_PORT),
  37. op_(DHCPTypeToBootpType(msg_type)),
  38. hwaddr_(new HWAddr()),
  39. hops_(0),
  40. transid_(transid),
  41. secs_(0),
  42. flags_(0),
  43. ciaddr_(DEFAULT_ADDRESS),
  44. yiaddr_(DEFAULT_ADDRESS),
  45. siaddr_(DEFAULT_ADDRESS),
  46. giaddr_(DEFAULT_ADDRESS)
  47. {
  48. memset(sname_, 0, MAX_SNAME_LEN);
  49. memset(file_, 0, MAX_FILE_LEN);
  50. setType(msg_type);
  51. }
  52. Pkt4::Pkt4(const uint8_t* data, size_t len)
  53. :buffer_out_(0), // not used, this is RX packet
  54. local_addr_(DEFAULT_ADDRESS),
  55. remote_addr_(DEFAULT_ADDRESS),
  56. iface_(""),
  57. ifindex_(0),
  58. local_port_(DHCP4_SERVER_PORT),
  59. remote_port_(DHCP4_CLIENT_PORT),
  60. op_(BOOTREQUEST),
  61. hwaddr_(new HWAddr()),
  62. transid_(0),
  63. secs_(0),
  64. flags_(0),
  65. ciaddr_(DEFAULT_ADDRESS),
  66. yiaddr_(DEFAULT_ADDRESS),
  67. siaddr_(DEFAULT_ADDRESS),
  68. giaddr_(DEFAULT_ADDRESS)
  69. {
  70. if (len < DHCPV4_PKT_HDR_LEN) {
  71. isc_throw(OutOfRange, "Truncated DHCPv4 packet (len=" << len
  72. << ") received, at least " << DHCPV4_PKT_HDR_LEN
  73. << " is expected.");
  74. } else if (data == NULL) {
  75. isc_throw(InvalidParameter, "data buffer passed to Pkt4 is NULL");
  76. }
  77. data_.resize(len);
  78. memcpy(&data_[0], data, len);
  79. }
  80. size_t
  81. Pkt4::len() {
  82. size_t length = DHCPV4_PKT_HDR_LEN; // DHCPv4 header
  83. // ... and sum of lengths of all options
  84. for (OptionCollection::const_iterator it = options_.begin();
  85. it != options_.end();
  86. ++it) {
  87. length += (*it).second->len();
  88. }
  89. return (length);
  90. }
  91. void
  92. Pkt4::pack() {
  93. if (!hwaddr_) {
  94. isc_throw(InvalidOperation, "Can't build Pkt4 packet. HWAddr not set.");
  95. }
  96. // Clear the output buffer to make sure that consecutive calls to pack()
  97. // will not result in concatenation of multiple packet copies.
  98. buffer_out_.clear();
  99. try {
  100. size_t hw_len = hwaddr_->hwaddr_.size();
  101. buffer_out_.writeUint8(op_);
  102. buffer_out_.writeUint8(hwaddr_->htype_);
  103. buffer_out_.writeUint8(hw_len < MAX_CHADDR_LEN ?
  104. hw_len : MAX_CHADDR_LEN);
  105. buffer_out_.writeUint8(hops_);
  106. buffer_out_.writeUint32(transid_);
  107. buffer_out_.writeUint16(secs_);
  108. buffer_out_.writeUint16(flags_);
  109. buffer_out_.writeUint32(ciaddr_);
  110. buffer_out_.writeUint32(yiaddr_);
  111. buffer_out_.writeUint32(siaddr_);
  112. buffer_out_.writeUint32(giaddr_);
  113. if (hw_len <= MAX_CHADDR_LEN) {
  114. // write up to 16 bytes of the hardware address (CHADDR field is 16
  115. // bytes long in DHCPv4 message).
  116. buffer_out_.writeData(&hwaddr_->hwaddr_[0],
  117. (hw_len < MAX_CHADDR_LEN ?
  118. hw_len : MAX_CHADDR_LEN) );
  119. hw_len = MAX_CHADDR_LEN - hw_len;
  120. } else {
  121. hw_len = MAX_CHADDR_LEN;
  122. }
  123. // write (len) bytes of padding
  124. vector<uint8_t> zeros(hw_len, 0);
  125. buffer_out_.writeData(&zeros[0], hw_len);
  126. // buffer_out_.writeData(chaddr_, MAX_CHADDR_LEN);
  127. buffer_out_.writeData(sname_, MAX_SNAME_LEN);
  128. buffer_out_.writeData(file_, MAX_FILE_LEN);
  129. // write DHCP magic cookie
  130. buffer_out_.writeUint32(DHCP_OPTIONS_COOKIE);
  131. LibDHCP::packOptions(buffer_out_, options_);
  132. // add END option that indicates end of options
  133. // (End option is very simple, just a 255 octet)
  134. buffer_out_.writeUint8(DHO_END);
  135. } catch(const Exception& e) {
  136. // An exception is thrown and message will be written to Logger
  137. isc_throw(InvalidOperation, e.what());
  138. }
  139. }
  140. void
  141. Pkt4::unpack() {
  142. // input buffer (used during message reception)
  143. isc::util::InputBuffer buffer_in(&data_[0], data_.size());
  144. if (buffer_in.getLength() < DHCPV4_PKT_HDR_LEN) {
  145. isc_throw(OutOfRange, "Received truncated DHCPv4 packet (len="
  146. << buffer_in.getLength() << " received, at least "
  147. << DHCPV4_PKT_HDR_LEN << "is expected");
  148. }
  149. op_ = buffer_in.readUint8();
  150. uint8_t htype = buffer_in.readUint8();
  151. uint8_t hlen = buffer_in.readUint8();
  152. hops_ = buffer_in.readUint8();
  153. transid_ = buffer_in.readUint32();
  154. secs_ = buffer_in.readUint16();
  155. flags_ = buffer_in.readUint16();
  156. ciaddr_ = IOAddress(buffer_in.readUint32());
  157. yiaddr_ = IOAddress(buffer_in.readUint32());
  158. siaddr_ = IOAddress(buffer_in.readUint32());
  159. giaddr_ = IOAddress(buffer_in.readUint32());
  160. vector<uint8_t> hw_addr(MAX_CHADDR_LEN, 0);
  161. buffer_in.readVector(hw_addr, MAX_CHADDR_LEN);
  162. buffer_in.readData(sname_, MAX_SNAME_LEN);
  163. buffer_in.readData(file_, MAX_FILE_LEN);
  164. hw_addr.resize(hlen);
  165. hwaddr_ = HWAddrPtr(new HWAddr(hw_addr, htype));
  166. if (buffer_in.getLength() == buffer_in.getPosition()) {
  167. // this is *NOT* DHCP packet. It does not have any DHCPv4 options. In
  168. // particular, it does not have magic cookie, a 4 byte sequence that
  169. // differentiates between DHCP and BOOTP packets.
  170. isc_throw(InvalidOperation, "Received BOOTP packet. BOOTP is not supported.");
  171. }
  172. if (buffer_in.getLength() - buffer_in.getPosition() < 4) {
  173. // there is not enough data to hold magic DHCP cookie
  174. isc_throw(Unexpected, "Truncated or no DHCP packet.");
  175. }
  176. uint32_t magic = buffer_in.readUint32();
  177. if (magic != DHCP_OPTIONS_COOKIE) {
  178. isc_throw(Unexpected, "Invalid or missing DHCP magic cookie");
  179. }
  180. size_t opts_len = buffer_in.getLength() - buffer_in.getPosition();
  181. vector<uint8_t> opts_buffer;
  182. // Use readVector because a function which parses option requires
  183. // a vector as an input.
  184. buffer_in.readVector(opts_buffer, opts_len);
  185. if (callback_.empty()) {
  186. LibDHCP::unpackOptions4(opts_buffer, "dhcp4", options_);
  187. } else {
  188. // The last two arguments are set to NULL because they are
  189. // specific to DHCPv6 options parsing. They are unused for
  190. // DHCPv4 case. In DHCPv6 case they hold are the relay message
  191. // offset and length.
  192. callback_(opts_buffer, "dhcp4", options_, NULL, NULL);
  193. }
  194. // @todo check will need to be called separately, so hooks can be called
  195. // after the packet is parsed, but before its content is verified
  196. check();
  197. }
  198. void Pkt4::check() {
  199. uint8_t msg_type = getType();
  200. if (msg_type > DHCPLEASEACTIVE) {
  201. isc_throw(BadValue, "Invalid DHCP message type received: "
  202. << static_cast<int>(msg_type));
  203. }
  204. }
  205. uint8_t Pkt4::getType() const {
  206. OptionPtr generic = getOption(DHO_DHCP_MESSAGE_TYPE);
  207. if (!generic) {
  208. isc_throw(Unexpected, "Missing DHCP Message Type option");
  209. }
  210. // Check if Message Type is specified as OptionInt<uint8_t>
  211. boost::shared_ptr<OptionInt<uint8_t> > type_opt =
  212. boost::dynamic_pointer_cast<OptionInt<uint8_t> >(generic);
  213. if (type_opt) {
  214. return (type_opt->getValue());
  215. }
  216. // Try to use it as generic option
  217. return (generic->getUint8());
  218. }
  219. void Pkt4::setType(uint8_t dhcp_type) {
  220. OptionPtr opt = getOption(DHO_DHCP_MESSAGE_TYPE);
  221. if (opt) {
  222. // There is message type option already, update it
  223. opt->setUint8(dhcp_type);
  224. } else {
  225. // There is no message type option yet, add it
  226. std::vector<uint8_t> tmp(1, dhcp_type);
  227. opt = OptionPtr(new Option(Option::V4, DHO_DHCP_MESSAGE_TYPE, tmp));
  228. addOption(opt);
  229. }
  230. }
  231. void Pkt4::repack() {
  232. buffer_out_.writeData(&data_[0], data_.size());
  233. }
  234. std::string
  235. Pkt4::toText() {
  236. stringstream tmp;
  237. tmp << "localAddr=" << local_addr_ << ":" << local_port_
  238. << " remoteAddr=" << remote_addr_
  239. << ":" << remote_port_ << ", msgtype=" << static_cast<int>(getType())
  240. << ", transid=0x" << hex << transid_ << dec << endl;
  241. for (isc::dhcp::OptionCollection::iterator opt=options_.begin();
  242. opt != options_.end();
  243. ++opt) {
  244. tmp << " " << opt->second->toText() << std::endl;
  245. }
  246. return tmp.str();
  247. }
  248. void
  249. Pkt4::setHWAddr(uint8_t htype, uint8_t hlen,
  250. const std::vector<uint8_t>& mac_addr) {
  251. setHWAddrMember(htype, hlen, mac_addr, hwaddr_);
  252. }
  253. void
  254. Pkt4::setHWAddr(const HWAddrPtr& addr) {
  255. if (!addr) {
  256. isc_throw(BadValue, "Setting DHCPv4 chaddr field to NULL"
  257. << " is forbidden");
  258. }
  259. hwaddr_ = addr;
  260. }
  261. void
  262. Pkt4::setHWAddrMember(const uint8_t htype, const uint8_t hlen,
  263. const std::vector<uint8_t>& mac_addr,
  264. HWAddrPtr& hw_addr) {
  265. /// @todo Rewrite this once support for client-identifier option
  266. /// is implemented (ticket 1228?)
  267. if (hlen > MAX_CHADDR_LEN) {
  268. isc_throw(OutOfRange, "Hardware address (len=" << hlen
  269. << " too long. Max " << MAX_CHADDR_LEN << " supported.");
  270. } else if (mac_addr.empty() && (hlen > 0) ) {
  271. isc_throw(OutOfRange, "Invalid HW Address specified");
  272. }
  273. hw_addr.reset(new HWAddr(mac_addr, htype));
  274. }
  275. void
  276. Pkt4::setLocalHWAddr(const uint8_t htype, const uint8_t hlen,
  277. const std::vector<uint8_t>& mac_addr) {
  278. setHWAddrMember(htype, hlen, mac_addr, local_hwaddr_);
  279. }
  280. void
  281. Pkt4::setLocalHWAddr(const HWAddrPtr& addr) {
  282. if (!addr) {
  283. isc_throw(BadValue, "Setting local HW address to NULL is"
  284. << " forbidden.");
  285. }
  286. local_hwaddr_ = addr;
  287. }
  288. void
  289. Pkt4::setRemoteHWAddr(const uint8_t htype, const uint8_t hlen,
  290. const std::vector<uint8_t>& mac_addr) {
  291. setHWAddrMember(htype, hlen, mac_addr, remote_hwaddr_);
  292. }
  293. void
  294. Pkt4::setRemoteHWAddr(const HWAddrPtr& addr) {
  295. if (!addr) {
  296. isc_throw(BadValue, "Setting remote HW address to NULL is"
  297. << " forbidden.");
  298. }
  299. remote_hwaddr_ = addr;
  300. }
  301. void
  302. Pkt4::setSname(const uint8_t* sname, size_t snameLen /*= MAX_SNAME_LEN*/) {
  303. if (snameLen > MAX_SNAME_LEN) {
  304. isc_throw(OutOfRange, "sname field (len=" << snameLen
  305. << ") too long, Max " << MAX_SNAME_LEN << " supported.");
  306. } else if (sname == NULL) {
  307. isc_throw(InvalidParameter, "Invalid sname specified");
  308. }
  309. std::copy(&sname[0], &sname[snameLen], &sname_[0]);
  310. std::fill(&sname_[snameLen], &sname_[MAX_SNAME_LEN], 0);
  311. // No need to store snameLen as any empty space is filled with 0s
  312. }
  313. void
  314. Pkt4::setFile(const uint8_t* file, size_t fileLen /*= MAX_FILE_LEN*/) {
  315. if (fileLen > MAX_FILE_LEN) {
  316. isc_throw(OutOfRange, "file field (len=" << fileLen
  317. << ") too long, Max " << MAX_FILE_LEN << " supported.");
  318. } else if (file == NULL) {
  319. isc_throw(InvalidParameter, "Invalid file name specified");
  320. }
  321. std::copy(&file[0], &file[fileLen], &file_[0]);
  322. std::fill(&file_[fileLen], &file_[MAX_FILE_LEN], 0);
  323. // No need to store fileLen as any empty space is filled with 0s
  324. }
  325. uint8_t
  326. Pkt4::DHCPTypeToBootpType(uint8_t dhcpType) {
  327. switch (dhcpType) {
  328. case DHCPDISCOVER:
  329. case DHCPREQUEST:
  330. case DHCPDECLINE:
  331. case DHCPRELEASE:
  332. case DHCPINFORM:
  333. case DHCPLEASEQUERY:
  334. case DHCPBULKLEASEQUERY:
  335. return (BOOTREQUEST);
  336. case DHCPACK:
  337. case DHCPNAK:
  338. case DHCPOFFER:
  339. case DHCPLEASEUNASSIGNED:
  340. case DHCPLEASEUNKNOWN:
  341. case DHCPLEASEACTIVE:
  342. case DHCPLEASEQUERYDONE:
  343. return (BOOTREPLY);
  344. default:
  345. isc_throw(OutOfRange, "Invalid message type: "
  346. << static_cast<int>(dhcpType) );
  347. }
  348. }
  349. uint8_t
  350. Pkt4::getHtype() const {
  351. if (!hwaddr_) {
  352. isc_throw(InvalidOperation, "Can't get HType. HWAddr not defined");
  353. }
  354. return (hwaddr_->htype_);
  355. }
  356. uint8_t
  357. Pkt4::getHlen() const {
  358. if (!hwaddr_) {
  359. isc_throw(InvalidOperation, "Can't get HType. HWAddr not defined");
  360. }
  361. uint8_t len = hwaddr_->hwaddr_.size();
  362. return (len <= MAX_CHADDR_LEN ? len : MAX_CHADDR_LEN);
  363. }
  364. void
  365. Pkt4::addOption(boost::shared_ptr<Option> opt) {
  366. // Check for uniqueness (DHCPv4 options must be unique)
  367. if (getOption(opt->getType())) {
  368. isc_throw(BadValue, "Option " << opt->getType()
  369. << " already present in this message.");
  370. }
  371. options_.insert(pair<int, boost::shared_ptr<Option> >(opt->getType(), opt));
  372. }
  373. boost::shared_ptr<isc::dhcp::Option>
  374. Pkt4::getOption(uint8_t type) const {
  375. OptionCollection::const_iterator x = options_.find(type);
  376. if (x != options_.end()) {
  377. return (*x).second;
  378. }
  379. return boost::shared_ptr<isc::dhcp::Option>(); // NULL
  380. }
  381. bool
  382. Pkt4::delOption(uint8_t type) {
  383. isc::dhcp::OptionCollection::iterator x = options_.find(type);
  384. if (x != options_.end()) {
  385. options_.erase(x);
  386. return (true); // delete successful
  387. }
  388. return (false); // can't find option to be deleted
  389. }
  390. void
  391. Pkt4::updateTimestamp() {
  392. timestamp_ = boost::posix_time::microsec_clock::universal_time();
  393. }
  394. bool
  395. Pkt4::isRelayed() const {
  396. static const IOAddress zero_addr("0.0.0.0");
  397. // For non-relayed message both Giaddr and Hops are zero.
  398. if (getGiaddr() == zero_addr && getHops() == 0) {
  399. return (false);
  400. // For relayed message, both Giaddr and Hops are non-zero.
  401. } else if (getGiaddr() != zero_addr && getHops() > 0) {
  402. return (true);
  403. }
  404. // In any other case, the packet is considered malformed.
  405. isc_throw(isc::BadValue, "invalid combination of giaddr = "
  406. << getGiaddr().toText() << " and hops = "
  407. << static_cast<int>(getHops()) << ". Valid values"
  408. " are: (giaddr = 0 and hops = 0) or (giaddr != 0 and"
  409. "hops != 0)");
  410. }
  411. bool Pkt4::inClass(const std::string& client_class) {
  412. return (classes_.find(client_class) != classes_.end());
  413. }
  414. void
  415. Pkt4::addClass(const std::string& client_class) {
  416. if (classes_.find(client_class) == classes_.end()) {
  417. classes_.insert(client_class);
  418. }
  419. }
  420. } // end of namespace isc::dhcp
  421. } // end of namespace isc