pkt4.cc 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571
  1. // Copyright (C) 2011-2015 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 <config.h>
  15. #include <asiolink/io_address.h>
  16. #include <dhcp/dhcp4.h>
  17. #include <dhcp/libdhcp++.h>
  18. #include <dhcp/option_int.h>
  19. #include <dhcp/pkt4.h>
  20. #include <exceptions/exceptions.h>
  21. #include <algorithm>
  22. #include <iostream>
  23. #include <sstream>
  24. using namespace std;
  25. using namespace isc::dhcp;
  26. using namespace isc::asiolink;
  27. namespace {
  28. /// @brief Default address used in Pkt4 constructor
  29. const IOAddress DEFAULT_ADDRESS("0.0.0.0");
  30. }
  31. namespace isc {
  32. namespace dhcp {
  33. Pkt4::Pkt4(uint8_t msg_type, uint32_t transid)
  34. :Pkt(transid, DEFAULT_ADDRESS, DEFAULT_ADDRESS, DHCP4_SERVER_PORT,
  35. DHCP4_CLIENT_PORT),
  36. op_(DHCPTypeToBootpType(msg_type)),
  37. hwaddr_(new HWAddr()),
  38. hops_(0),
  39. secs_(0),
  40. flags_(0),
  41. ciaddr_(DEFAULT_ADDRESS),
  42. yiaddr_(DEFAULT_ADDRESS),
  43. siaddr_(DEFAULT_ADDRESS),
  44. giaddr_(DEFAULT_ADDRESS)
  45. {
  46. memset(sname_, 0, MAX_SNAME_LEN);
  47. memset(file_, 0, MAX_FILE_LEN);
  48. setType(msg_type);
  49. }
  50. Pkt4::Pkt4(const uint8_t* data, size_t len)
  51. :Pkt(data, len, DEFAULT_ADDRESS, DEFAULT_ADDRESS, DHCP4_SERVER_PORT,
  52. DHCP4_CLIENT_PORT),
  53. op_(BOOTREQUEST),
  54. hwaddr_(new HWAddr()),
  55. hops_(0),
  56. secs_(0),
  57. flags_(0),
  58. ciaddr_(DEFAULT_ADDRESS),
  59. yiaddr_(DEFAULT_ADDRESS),
  60. siaddr_(DEFAULT_ADDRESS),
  61. giaddr_(DEFAULT_ADDRESS)
  62. {
  63. if (len < DHCPV4_PKT_HDR_LEN) {
  64. isc_throw(OutOfRange, "Truncated DHCPv4 packet (len=" << len
  65. << ") received, at least " << DHCPV4_PKT_HDR_LEN
  66. << " is expected.");
  67. }
  68. memset(sname_, 0, MAX_SNAME_LEN);
  69. memset(file_, 0, MAX_FILE_LEN);
  70. }
  71. size_t
  72. Pkt4::len() {
  73. size_t length = DHCPV4_PKT_HDR_LEN; // DHCPv4 header
  74. // ... and sum of lengths of all options
  75. for (OptionCollection::const_iterator it = options_.begin();
  76. it != options_.end();
  77. ++it) {
  78. length += (*it).second->len();
  79. }
  80. return (length);
  81. }
  82. void
  83. Pkt4::pack() {
  84. if (!hwaddr_) {
  85. isc_throw(InvalidOperation, "Can't build Pkt4 packet. HWAddr not set.");
  86. }
  87. // Clear the output buffer to make sure that consecutive calls to pack()
  88. // will not result in concatenation of multiple packet copies.
  89. buffer_out_.clear();
  90. try {
  91. size_t hw_len = hwaddr_->hwaddr_.size();
  92. buffer_out_.writeUint8(op_);
  93. buffer_out_.writeUint8(hwaddr_->htype_);
  94. buffer_out_.writeUint8(hw_len < MAX_CHADDR_LEN ?
  95. hw_len : MAX_CHADDR_LEN);
  96. buffer_out_.writeUint8(hops_);
  97. buffer_out_.writeUint32(transid_);
  98. buffer_out_.writeUint16(secs_);
  99. buffer_out_.writeUint16(flags_);
  100. buffer_out_.writeUint32(ciaddr_);
  101. buffer_out_.writeUint32(yiaddr_);
  102. buffer_out_.writeUint32(siaddr_);
  103. buffer_out_.writeUint32(giaddr_);
  104. if ((hw_len > 0) && (hw_len <= MAX_CHADDR_LEN)) {
  105. // write up to 16 bytes of the hardware address (CHADDR field is 16
  106. // bytes long in DHCPv4 message).
  107. buffer_out_.writeData(&hwaddr_->hwaddr_[0],
  108. (hw_len < MAX_CHADDR_LEN ?
  109. hw_len : MAX_CHADDR_LEN) );
  110. hw_len = MAX_CHADDR_LEN - hw_len;
  111. } else {
  112. hw_len = MAX_CHADDR_LEN;
  113. }
  114. // write (len) bytes of padding
  115. if (hw_len > 0) {
  116. vector<uint8_t> zeros(hw_len, 0);
  117. buffer_out_.writeData(&zeros[0], hw_len);
  118. }
  119. buffer_out_.writeData(sname_, MAX_SNAME_LEN);
  120. buffer_out_.writeData(file_, MAX_FILE_LEN);
  121. // write DHCP magic cookie
  122. buffer_out_.writeUint32(DHCP_OPTIONS_COOKIE);
  123. LibDHCP::packOptions4(buffer_out_, options_);
  124. // add END option that indicates end of options
  125. // (End option is very simple, just a 255 octet)
  126. buffer_out_.writeUint8(DHO_END);
  127. } catch(const Exception& e) {
  128. // An exception is thrown and message will be written to Logger
  129. isc_throw(InvalidOperation, e.what());
  130. }
  131. }
  132. void
  133. Pkt4::unpack() {
  134. // input buffer (used during message reception)
  135. isc::util::InputBuffer buffer_in(&data_[0], data_.size());
  136. if (buffer_in.getLength() < DHCPV4_PKT_HDR_LEN) {
  137. isc_throw(OutOfRange, "Received truncated DHCPv4 packet (len="
  138. << buffer_in.getLength() << " received, at least "
  139. << DHCPV4_PKT_HDR_LEN << "is expected");
  140. }
  141. op_ = buffer_in.readUint8();
  142. uint8_t htype = buffer_in.readUint8();
  143. uint8_t hlen = buffer_in.readUint8();
  144. hops_ = buffer_in.readUint8();
  145. transid_ = buffer_in.readUint32();
  146. secs_ = buffer_in.readUint16();
  147. flags_ = buffer_in.readUint16();
  148. ciaddr_ = IOAddress(buffer_in.readUint32());
  149. yiaddr_ = IOAddress(buffer_in.readUint32());
  150. siaddr_ = IOAddress(buffer_in.readUint32());
  151. giaddr_ = IOAddress(buffer_in.readUint32());
  152. vector<uint8_t> hw_addr(MAX_CHADDR_LEN, 0);
  153. buffer_in.readVector(hw_addr, MAX_CHADDR_LEN);
  154. buffer_in.readData(sname_, MAX_SNAME_LEN);
  155. buffer_in.readData(file_, MAX_FILE_LEN);
  156. hw_addr.resize(hlen);
  157. hwaddr_ = HWAddrPtr(new HWAddr(hw_addr, htype));
  158. if (buffer_in.getLength() == buffer_in.getPosition()) {
  159. // this is *NOT* DHCP packet. It does not have any DHCPv4 options. In
  160. // particular, it does not have magic cookie, a 4 byte sequence that
  161. // differentiates between DHCP and BOOTP packets.
  162. isc_throw(InvalidOperation, "Received BOOTP packet. BOOTP is not supported.");
  163. }
  164. if (buffer_in.getLength() - buffer_in.getPosition() < 4) {
  165. // there is not enough data to hold magic DHCP cookie
  166. isc_throw(Unexpected, "Truncated or no DHCP packet.");
  167. }
  168. uint32_t magic = buffer_in.readUint32();
  169. if (magic != DHCP_OPTIONS_COOKIE) {
  170. isc_throw(Unexpected, "Invalid or missing DHCP magic cookie");
  171. }
  172. size_t opts_len = buffer_in.getLength() - buffer_in.getPosition();
  173. vector<uint8_t> opts_buffer;
  174. // Use readVector because a function which parses option requires
  175. // a vector as an input.
  176. size_t offset;
  177. buffer_in.readVector(opts_buffer, opts_len);
  178. if (callback_.empty()) {
  179. offset = LibDHCP::unpackOptions4(opts_buffer, "dhcp4", options_);
  180. } else {
  181. // The last two arguments are set to NULL because they are
  182. // specific to DHCPv6 options parsing. They are unused for
  183. // DHCPv4 case. In DHCPv6 case they hold are the relay message
  184. // offset and length.
  185. offset = callback_(opts_buffer, "dhcp4", options_, NULL, NULL);
  186. }
  187. // If offset is not equal to the size and there is no DHO_END,
  188. // then something is wrong here. We either parsed past input
  189. // buffer (bug in our code) or we haven't parsed everything
  190. // (received trailing garbage or truncated option).
  191. //
  192. // Invoking Jon Postel's law here: be conservative in what you send, and be
  193. // liberal in what you accept. There's no easy way to log something from
  194. // libdhcp++ library, so we just choose to be silent about remaining
  195. // bytes. We also need to quell compiler warning about unused offset
  196. // variable.
  197. //
  198. // if ((offset != size) && (opts_buffer[offset] != DHO_END)) {
  199. // isc_throw(BadValue, "Received DHCPv6 buffer of size " << size
  200. // << ", were able to parse " << offset << " bytes.");
  201. // }
  202. (void)offset;
  203. // No need to call check() here. There are thorough tests for this
  204. // later (see Dhcp4Srv::accept()). We want to drop the packet later,
  205. // so we'll be able to log more detailed drop reason.
  206. }
  207. uint8_t Pkt4::getType() const {
  208. OptionPtr generic = getOption(DHO_DHCP_MESSAGE_TYPE);
  209. if (!generic) {
  210. isc_throw(Unexpected, "Missing DHCP Message Type option");
  211. }
  212. // Check if Message Type is specified as OptionInt<uint8_t>
  213. boost::shared_ptr<OptionInt<uint8_t> > type_opt =
  214. boost::dynamic_pointer_cast<OptionInt<uint8_t> >(generic);
  215. if (type_opt) {
  216. return (type_opt->getValue());
  217. }
  218. // Try to use it as generic option
  219. return (generic->getUint8());
  220. }
  221. void Pkt4::setType(uint8_t dhcp_type) {
  222. OptionPtr opt = getOption(DHO_DHCP_MESSAGE_TYPE);
  223. if (opt) {
  224. // There is message type option already, update it. It seems that
  225. // we do have two types of objects representing message-type option.
  226. // It would be more preferable to use only one type, but there's no
  227. // easy way to enforce it.
  228. //
  229. // One is an instance of the Option class. It stores type in
  230. // Option::data_, so Option::setUint8() and Option::getUint8() can be
  231. // used. The other one is an instance of OptionInt<uint8_t> and
  232. // it stores message type as integer, hence
  233. // OptionInt<uint8_t>::getValue() and OptionInt<uint8_t>::setValue()
  234. // should be used.
  235. boost::shared_ptr<OptionInt<uint8_t> > type_opt =
  236. boost::dynamic_pointer_cast<OptionInt<uint8_t> >(opt);
  237. if (type_opt) {
  238. type_opt->setValue(dhcp_type);
  239. } else {
  240. opt->setUint8(dhcp_type);
  241. }
  242. } else {
  243. // There is no message type option yet, add it
  244. std::vector<uint8_t> tmp(1, dhcp_type);
  245. opt = OptionPtr(new OptionInt<uint8_t>(Option::V4, DHO_DHCP_MESSAGE_TYPE,
  246. tmp.begin(), tmp.end()));
  247. addOption(opt);
  248. }
  249. }
  250. const char*
  251. Pkt4::getName(const uint8_t type) {
  252. static const char* DHCPDISCOVER_NAME = "DHCPDISCOVER";
  253. static const char* DHCPOFFER_NAME = "DHCPOFFER";
  254. static const char* DHCPREQUEST_NAME = "DHCPREQUEST";
  255. static const char* DHCPDECLINE_NAME = "DHCPDECLINE";
  256. static const char* DHCPACK_NAME = "DHCPACK";
  257. static const char* DHCPNAK_NAME = "DHCPNAK";
  258. static const char* DHCPRELEASE_NAME = "DHCPRELEASE";
  259. static const char* DHCPINFORM_NAME = "DHCPINFORM";
  260. static const char* UNKNOWN_NAME = "UNKNOWN";
  261. switch (type) {
  262. case DHCPDISCOVER:
  263. return (DHCPDISCOVER_NAME);
  264. case DHCPOFFER:
  265. return (DHCPOFFER_NAME);
  266. case DHCPREQUEST:
  267. return (DHCPREQUEST_NAME);
  268. case DHCPDECLINE:
  269. return (DHCPDECLINE_NAME);
  270. case DHCPACK:
  271. return (DHCPACK_NAME);
  272. case DHCPNAK:
  273. return (DHCPNAK_NAME);
  274. case DHCPRELEASE:
  275. return (DHCPRELEASE_NAME);
  276. case DHCPINFORM:
  277. return (DHCPINFORM_NAME);
  278. default:
  279. ;
  280. }
  281. return (UNKNOWN_NAME);
  282. }
  283. const char*
  284. Pkt4::getName() const {
  285. return (Pkt4::getName(getType()));
  286. }
  287. std::string
  288. Pkt4::getLabel() const {
  289. /// @todo If and when client id is extracted into Pkt4, this method should
  290. /// use the instance member rather than fetch it every time.
  291. std::string suffix;
  292. ClientIdPtr client_id;
  293. OptionPtr client_opt = getOption(DHO_DHCP_CLIENT_IDENTIFIER);
  294. if (client_opt) {
  295. try {
  296. client_id = ClientIdPtr(new ClientId(client_opt->getData()));
  297. } catch (...) {
  298. // ClientId may throw if the client-id is too short.
  299. suffix = "(malformed client-id)";
  300. }
  301. }
  302. std::string txt;
  303. try {
  304. txt = makeLabel(hwaddr_, client_id, transid_);
  305. } catch (...) {
  306. // This should not happen with the current code, but we may add extra
  307. // sanity checks in the future that would possibly throw if
  308. // the hwaddr lenght is 0.
  309. txt = "(malformed hw address)";
  310. }
  311. return (txt + suffix);
  312. }
  313. std::string
  314. Pkt4::makeLabel(const HWAddrPtr& hwaddr, const ClientIdPtr& client_id,
  315. const uint32_t transid) {
  316. // Create label with HW address and client identifier.
  317. stringstream label;
  318. label << makeLabel(hwaddr, client_id);
  319. // Append transaction id.
  320. label << ", tid=0x" << hex << transid << dec;
  321. return label.str();
  322. }
  323. std::string
  324. Pkt4::makeLabel(const HWAddrPtr& hwaddr, const ClientIdPtr& client_id) {
  325. stringstream label;
  326. label << "[" << (hwaddr ? hwaddr->toText() : "no hwaddr info")
  327. << "], cid=[" << (client_id ? client_id->toText() : "no info")
  328. << "]";
  329. return label.str();
  330. }
  331. std::string
  332. Pkt4::toText() const {
  333. stringstream output;
  334. output << "local_address=" << local_addr_ << ":" << local_port_
  335. << ", remote_adress=" << remote_addr_
  336. << ":" << remote_port_ << ", msg_type=";
  337. // Try to obtain message type. This may throw if the Message Type option is
  338. // not present. Therefore we guard it with try-catch, because we don't want
  339. // toText method to throw.
  340. try {
  341. uint8_t msg_type = getType();
  342. output << getName(msg_type) << " (" << static_cast<int>(msg_type) << ")";
  343. } catch (...) {
  344. // Message Type option is missing.
  345. output << "(missing)";
  346. }
  347. output << ", transid=0x" << hex << transid_ << dec;
  348. if (!options_.empty()) {
  349. output << "," << std::endl << "options:";
  350. for (isc::dhcp::OptionCollection::const_iterator opt = options_.begin();
  351. opt != options_.end(); ++opt) {
  352. output << std::endl << opt->second->toText(2);
  353. }
  354. } else {
  355. output << ", message contains no options";
  356. }
  357. return (output.str());
  358. }
  359. void
  360. Pkt4::setHWAddr(uint8_t htype, uint8_t hlen,
  361. const std::vector<uint8_t>& mac_addr) {
  362. setHWAddrMember(htype, hlen, mac_addr, hwaddr_);
  363. }
  364. void
  365. Pkt4::setHWAddr(const HWAddrPtr& addr) {
  366. if (!addr) {
  367. isc_throw(BadValue, "Setting DHCPv4 chaddr field to NULL"
  368. << " is forbidden");
  369. }
  370. hwaddr_ = addr;
  371. }
  372. void
  373. Pkt4::setHWAddrMember(const uint8_t htype, const uint8_t hlen,
  374. const std::vector<uint8_t>& mac_addr,
  375. HWAddrPtr& hw_addr) {
  376. /// @todo Rewrite this once support for client-identifier option
  377. /// is implemented (ticket 1228?)
  378. if (hlen > MAX_CHADDR_LEN) {
  379. isc_throw(OutOfRange, "Hardware address (len=" << hlen
  380. << " too long. Max " << MAX_CHADDR_LEN << " supported.");
  381. } else if (mac_addr.empty() && (hlen > 0) ) {
  382. isc_throw(OutOfRange, "Invalid HW Address specified");
  383. }
  384. hw_addr.reset(new HWAddr(mac_addr, htype));
  385. }
  386. void
  387. Pkt4::setLocalHWAddr(const uint8_t htype, const uint8_t hlen,
  388. const std::vector<uint8_t>& mac_addr) {
  389. setHWAddrMember(htype, hlen, mac_addr, local_hwaddr_);
  390. }
  391. void
  392. Pkt4::setLocalHWAddr(const HWAddrPtr& addr) {
  393. if (!addr) {
  394. isc_throw(BadValue, "Setting local HW address to NULL is"
  395. << " forbidden.");
  396. }
  397. local_hwaddr_ = addr;
  398. }
  399. void
  400. Pkt4::setSname(const uint8_t* sname, size_t snameLen /*= MAX_SNAME_LEN*/) {
  401. if (snameLen > MAX_SNAME_LEN) {
  402. isc_throw(OutOfRange, "sname field (len=" << snameLen
  403. << ") too long, Max " << MAX_SNAME_LEN << " supported.");
  404. } else if (sname == NULL) {
  405. isc_throw(InvalidParameter, "Invalid sname specified");
  406. }
  407. std::copy(sname, (sname + snameLen), sname_);
  408. std::fill((sname_ + snameLen), (sname_ + MAX_SNAME_LEN), 0);
  409. // No need to store snameLen as any empty space is filled with 0s
  410. }
  411. void
  412. Pkt4::setFile(const uint8_t* file, size_t fileLen /*= MAX_FILE_LEN*/) {
  413. if (fileLen > MAX_FILE_LEN) {
  414. isc_throw(OutOfRange, "file field (len=" << fileLen
  415. << ") too long, Max " << MAX_FILE_LEN << " supported.");
  416. } else if (file == NULL) {
  417. isc_throw(InvalidParameter, "Invalid file name specified");
  418. }
  419. std::copy(file, (file + fileLen), file_);
  420. std::fill((file_ + fileLen), (file_ + MAX_FILE_LEN), 0);
  421. // No need to store fileLen as any empty space is filled with 0s
  422. }
  423. uint8_t
  424. // cppcheck-suppress unusedFunction
  425. Pkt4::DHCPTypeToBootpType(uint8_t dhcpType) {
  426. switch (dhcpType) {
  427. case DHCPDISCOVER:
  428. case DHCPREQUEST:
  429. case DHCPDECLINE:
  430. case DHCPRELEASE:
  431. case DHCPINFORM:
  432. case DHCPLEASEQUERY:
  433. case DHCPBULKLEASEQUERY:
  434. return (BOOTREQUEST);
  435. case DHCPACK:
  436. case DHCPNAK:
  437. case DHCPOFFER:
  438. case DHCPLEASEUNASSIGNED:
  439. case DHCPLEASEUNKNOWN:
  440. case DHCPLEASEACTIVE:
  441. case DHCPLEASEQUERYDONE:
  442. return (BOOTREPLY);
  443. default:
  444. isc_throw(OutOfRange, "Invalid message type: "
  445. << static_cast<int>(dhcpType) );
  446. }
  447. }
  448. uint8_t
  449. Pkt4::getHtype() const {
  450. if (!hwaddr_) {
  451. isc_throw(InvalidOperation, "Can't get HType. HWAddr not defined");
  452. }
  453. return (hwaddr_->htype_);
  454. }
  455. uint8_t
  456. Pkt4::getHlen() const {
  457. if (!hwaddr_) {
  458. isc_throw(InvalidOperation, "Can't get HType. HWAddr not defined");
  459. }
  460. uint8_t len = hwaddr_->hwaddr_.size();
  461. return (len <= MAX_CHADDR_LEN ? len : MAX_CHADDR_LEN);
  462. }
  463. void
  464. Pkt4::addOption(const OptionPtr& opt) {
  465. // Check for uniqueness (DHCPv4 options must be unique)
  466. if (getOption(opt->getType())) {
  467. isc_throw(BadValue, "Option " << opt->getType()
  468. << " already present in this message.");
  469. }
  470. Pkt::addOption(opt);
  471. }
  472. bool
  473. Pkt4::isRelayed() const {
  474. return (!giaddr_.isV4Zero() && !giaddr_.isV4Bcast());
  475. }
  476. } // end of namespace isc::dhcp
  477. } // end of namespace isc