pkt4.cc 17 KB

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