pkt4.cc 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472
  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. } else if (data == NULL) {
  68. isc_throw(InvalidParameter, "data buffer passed to Pkt4 is NULL");
  69. }
  70. data_.resize(len);
  71. memcpy(&data_[0], data, len);
  72. }
  73. size_t
  74. Pkt4::len() {
  75. size_t length = DHCPV4_PKT_HDR_LEN; // DHCPv4 header
  76. // ... and sum of lengths of all options
  77. for (OptionCollection::const_iterator it = options_.begin();
  78. it != options_.end();
  79. ++it) {
  80. length += (*it).second->len();
  81. }
  82. return (length);
  83. }
  84. void
  85. Pkt4::pack() {
  86. if (!hwaddr_) {
  87. isc_throw(InvalidOperation, "Can't build Pkt4 packet. HWAddr not set.");
  88. }
  89. // Clear the output buffer to make sure that consecutive calls to pack()
  90. // will not result in concatenation of multiple packet copies.
  91. buffer_out_.clear();
  92. try {
  93. size_t hw_len = hwaddr_->hwaddr_.size();
  94. buffer_out_.writeUint8(op_);
  95. buffer_out_.writeUint8(hwaddr_->htype_);
  96. buffer_out_.writeUint8(hw_len < MAX_CHADDR_LEN ?
  97. hw_len : MAX_CHADDR_LEN);
  98. buffer_out_.writeUint8(hops_);
  99. buffer_out_.writeUint32(transid_);
  100. buffer_out_.writeUint16(secs_);
  101. buffer_out_.writeUint16(flags_);
  102. buffer_out_.writeUint32(ciaddr_);
  103. buffer_out_.writeUint32(yiaddr_);
  104. buffer_out_.writeUint32(siaddr_);
  105. buffer_out_.writeUint32(giaddr_);
  106. if (hw_len <= MAX_CHADDR_LEN) {
  107. // write up to 16 bytes of the hardware address (CHADDR field is 16
  108. // bytes long in DHCPv4 message).
  109. buffer_out_.writeData(&hwaddr_->hwaddr_[0],
  110. (hw_len < MAX_CHADDR_LEN ?
  111. hw_len : MAX_CHADDR_LEN) );
  112. hw_len = MAX_CHADDR_LEN - hw_len;
  113. } else {
  114. hw_len = MAX_CHADDR_LEN;
  115. }
  116. // write (len) bytes of padding
  117. vector<uint8_t> zeros(hw_len, 0);
  118. buffer_out_.writeData(&zeros[0], hw_len);
  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::packOptions(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, then something is wrong here. We
  188. // either parsed past input buffer (bug in our code) or we haven't parsed
  189. // everything (received trailing garbage or truncated option).
  190. //
  191. // Invoking Jon Postel's law here: be conservative in what you send, and be
  192. // liberal in what you accept. There's no easy way to log something from
  193. // libdhcp++ library, so we just choose to be silent about remaining
  194. // bytes. We also need to quell compiler warning about unused offset
  195. // variable.
  196. //
  197. // if (offset != size) {
  198. // isc_throw(BadValue, "Received DHCPv6 buffer of size " << size
  199. // << ", were able to parse " << offset << " bytes.");
  200. // }
  201. (void)offset;
  202. // @todo check will need to be called separately, so hooks can be called
  203. // after the packet is parsed, but before its content is verified
  204. check();
  205. }
  206. void Pkt4::check() {
  207. uint8_t msg_type = getType();
  208. if (msg_type > DHCPLEASEACTIVE) {
  209. isc_throw(BadValue, "Invalid DHCP message type received: "
  210. << static_cast<int>(msg_type));
  211. }
  212. }
  213. uint8_t Pkt4::getType() const {
  214. OptionPtr generic = getOption(DHO_DHCP_MESSAGE_TYPE);
  215. if (!generic) {
  216. isc_throw(Unexpected, "Missing DHCP Message Type option");
  217. }
  218. // Check if Message Type is specified as OptionInt<uint8_t>
  219. boost::shared_ptr<OptionInt<uint8_t> > type_opt =
  220. boost::dynamic_pointer_cast<OptionInt<uint8_t> >(generic);
  221. if (type_opt) {
  222. return (type_opt->getValue());
  223. }
  224. // Try to use it as generic option
  225. return (generic->getUint8());
  226. }
  227. void Pkt4::setType(uint8_t dhcp_type) {
  228. OptionPtr opt = getOption(DHO_DHCP_MESSAGE_TYPE);
  229. if (opt) {
  230. // There is message type option already, update it
  231. opt->setUint8(dhcp_type);
  232. } else {
  233. // There is no message type option yet, add it
  234. std::vector<uint8_t> tmp(1, dhcp_type);
  235. opt = OptionPtr(new Option(Option::V4, DHO_DHCP_MESSAGE_TYPE, tmp));
  236. addOption(opt);
  237. }
  238. }
  239. std::string
  240. Pkt4::getLabel() const {
  241. /// @todo If and when client id is extracted into Pkt4, this method should
  242. /// the instance member rather than fetch it every time.
  243. ClientIdPtr client_id;
  244. OptionPtr client_opt = getOption(DHO_DHCP_CLIENT_IDENTIFIER);
  245. if (client_opt ) {
  246. client_id = ClientIdPtr(new ClientId(client_opt->getData()));
  247. }
  248. return makeLabel(hwaddr_, client_id, transid_);
  249. }
  250. std::string
  251. Pkt4::makeLabel(const HWAddrPtr& hwaddr, const ClientIdPtr& client_id,
  252. const uint32_t transid) {
  253. stringstream label;
  254. label << "hwaddr=[" << (hwaddr ? hwaddr->toText() : "no info")
  255. << "], client-id=[" << (client_id ? client_id->toText() : "no info")
  256. << "], transid=0x" << hex << transid << dec;
  257. return label.str();
  258. }
  259. std::string
  260. Pkt4::toText() {
  261. stringstream tmp;
  262. tmp << "localAddr=" << local_addr_ << ":" << local_port_
  263. << " remoteAddr=" << remote_addr_
  264. << ":" << remote_port_ << ", msgtype=" << static_cast<int>(getType())
  265. << ", transid=0x" << hex << transid_ << dec << endl;
  266. for (isc::dhcp::OptionCollection::iterator opt=options_.begin();
  267. opt != options_.end();
  268. ++opt) {
  269. tmp << " " << opt->second->toText() << std::endl;
  270. }
  271. return tmp.str();
  272. }
  273. void
  274. Pkt4::setHWAddr(uint8_t htype, uint8_t hlen,
  275. const std::vector<uint8_t>& mac_addr) {
  276. setHWAddrMember(htype, hlen, mac_addr, hwaddr_);
  277. }
  278. void
  279. Pkt4::setHWAddr(const HWAddrPtr& addr) {
  280. if (!addr) {
  281. isc_throw(BadValue, "Setting DHCPv4 chaddr field to NULL"
  282. << " is forbidden");
  283. }
  284. hwaddr_ = addr;
  285. }
  286. void
  287. Pkt4::setHWAddrMember(const uint8_t htype, const uint8_t hlen,
  288. const std::vector<uint8_t>& mac_addr,
  289. HWAddrPtr& hw_addr) {
  290. /// @todo Rewrite this once support for client-identifier option
  291. /// is implemented (ticket 1228?)
  292. if (hlen > MAX_CHADDR_LEN) {
  293. isc_throw(OutOfRange, "Hardware address (len=" << hlen
  294. << " too long. Max " << MAX_CHADDR_LEN << " supported.");
  295. } else if (mac_addr.empty() && (hlen > 0) ) {
  296. isc_throw(OutOfRange, "Invalid HW Address specified");
  297. }
  298. hw_addr.reset(new HWAddr(mac_addr, htype));
  299. }
  300. void
  301. Pkt4::setLocalHWAddr(const uint8_t htype, const uint8_t hlen,
  302. const std::vector<uint8_t>& mac_addr) {
  303. setHWAddrMember(htype, hlen, mac_addr, local_hwaddr_);
  304. }
  305. void
  306. Pkt4::setLocalHWAddr(const HWAddrPtr& addr) {
  307. if (!addr) {
  308. isc_throw(BadValue, "Setting local HW address to NULL is"
  309. << " forbidden.");
  310. }
  311. local_hwaddr_ = addr;
  312. }
  313. void
  314. Pkt4::setSname(const uint8_t* sname, size_t snameLen /*= MAX_SNAME_LEN*/) {
  315. if (snameLen > MAX_SNAME_LEN) {
  316. isc_throw(OutOfRange, "sname field (len=" << snameLen
  317. << ") too long, Max " << MAX_SNAME_LEN << " supported.");
  318. } else if (sname == NULL) {
  319. isc_throw(InvalidParameter, "Invalid sname specified");
  320. }
  321. std::copy(sname, (sname + snameLen), sname_);
  322. std::fill((sname_ + snameLen), (sname_ + MAX_SNAME_LEN), 0);
  323. // No need to store snameLen as any empty space is filled with 0s
  324. }
  325. void
  326. Pkt4::setFile(const uint8_t* file, size_t fileLen /*= MAX_FILE_LEN*/) {
  327. if (fileLen > MAX_FILE_LEN) {
  328. isc_throw(OutOfRange, "file field (len=" << fileLen
  329. << ") too long, Max " << MAX_FILE_LEN << " supported.");
  330. } else if (file == NULL) {
  331. isc_throw(InvalidParameter, "Invalid file name specified");
  332. }
  333. std::copy(file, (file + fileLen), file_);
  334. std::fill((file_ + fileLen), (file_ + MAX_FILE_LEN), 0);
  335. // No need to store fileLen as any empty space is filled with 0s
  336. }
  337. uint8_t
  338. // cppcheck-suppress unusedFunction
  339. Pkt4::DHCPTypeToBootpType(uint8_t dhcpType) {
  340. switch (dhcpType) {
  341. case DHCPDISCOVER:
  342. case DHCPREQUEST:
  343. case DHCPDECLINE:
  344. case DHCPRELEASE:
  345. case DHCPINFORM:
  346. case DHCPLEASEQUERY:
  347. case DHCPBULKLEASEQUERY:
  348. return (BOOTREQUEST);
  349. case DHCPACK:
  350. case DHCPNAK:
  351. case DHCPOFFER:
  352. case DHCPLEASEUNASSIGNED:
  353. case DHCPLEASEUNKNOWN:
  354. case DHCPLEASEACTIVE:
  355. case DHCPLEASEQUERYDONE:
  356. return (BOOTREPLY);
  357. default:
  358. isc_throw(OutOfRange, "Invalid message type: "
  359. << static_cast<int>(dhcpType) );
  360. }
  361. }
  362. uint8_t
  363. Pkt4::getHtype() const {
  364. if (!hwaddr_) {
  365. isc_throw(InvalidOperation, "Can't get HType. HWAddr not defined");
  366. }
  367. return (hwaddr_->htype_);
  368. }
  369. uint8_t
  370. Pkt4::getHlen() const {
  371. if (!hwaddr_) {
  372. isc_throw(InvalidOperation, "Can't get HType. HWAddr not defined");
  373. }
  374. uint8_t len = hwaddr_->hwaddr_.size();
  375. return (len <= MAX_CHADDR_LEN ? len : MAX_CHADDR_LEN);
  376. }
  377. void
  378. Pkt4::addOption(const OptionPtr& opt) {
  379. // Check for uniqueness (DHCPv4 options must be unique)
  380. if (getOption(opt->getType())) {
  381. isc_throw(BadValue, "Option " << opt->getType()
  382. << " already present in this message.");
  383. }
  384. Pkt::addOption(opt);
  385. }
  386. bool
  387. Pkt4::isRelayed() const {
  388. return (!giaddr_.isV4Zero() && !giaddr_.isV4Bcast());
  389. }
  390. } // end of namespace isc::dhcp
  391. } // end of namespace isc