pkt4.cc 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. // Copyright (C) 2011 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 <dhcp/pkt4.h>
  15. #include <dhcp/libdhcp++.h>
  16. #include <dhcp/dhcp4.h>
  17. #include <exceptions/exceptions.h>
  18. #include <asiolink/io_address.h>
  19. #include <iostream>
  20. #include <sstream>
  21. using namespace std;
  22. using namespace isc::dhcp;
  23. using namespace isc::asiolink;
  24. namespace isc {
  25. namespace dhcp {
  26. const IOAddress DEFAULT_ADDRESS("0.0.0.0");
  27. Pkt4::Pkt4(uint8_t msg_type, uint32_t transid)
  28. :local_addr_(DEFAULT_ADDRESS),
  29. remote_addr_(DEFAULT_ADDRESS),
  30. iface_(""),
  31. ifindex_(0),
  32. local_port_(DHCP4_SERVER_PORT),
  33. remote_port_(DHCP4_CLIENT_PORT),
  34. op_(DHCPTypeToBootpType(msg_type)),
  35. htype_(HTYPE_ETHER),
  36. hlen_(0),
  37. hops_(0),
  38. transid_(transid),
  39. secs_(0),
  40. flags_(0),
  41. ciaddr_(DEFAULT_ADDRESS),
  42. yiaddr_(DEFAULT_ADDRESS),
  43. siaddr_(DEFAULT_ADDRESS),
  44. giaddr_(DEFAULT_ADDRESS),
  45. bufferOut_(DHCPV4_PKT_HDR_LEN),
  46. msg_type_(msg_type)
  47. {
  48. memset(chaddr_, 0, MAX_CHADDR_LEN);
  49. memset(sname_, 0, MAX_SNAME_LEN);
  50. memset(file_, 0, MAX_FILE_LEN);
  51. }
  52. Pkt4::Pkt4(const uint8_t* data, size_t len)
  53. :local_addr_(DEFAULT_ADDRESS),
  54. remote_addr_(DEFAULT_ADDRESS),
  55. iface_(""),
  56. ifindex_(0),
  57. local_port_(DHCP4_SERVER_PORT),
  58. remote_port_(DHCP4_CLIENT_PORT),
  59. op_(BOOTREQUEST),
  60. transid_(0),
  61. secs_(0),
  62. flags_(0),
  63. ciaddr_(DEFAULT_ADDRESS),
  64. yiaddr_(DEFAULT_ADDRESS),
  65. siaddr_(DEFAULT_ADDRESS),
  66. giaddr_(DEFAULT_ADDRESS),
  67. bufferOut_(0), // not used, this is RX packet
  68. msg_type_(DHCPDISCOVER)
  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. }
  75. data_.resize(len);
  76. memcpy(&data_[0], data, len);
  77. }
  78. size_t
  79. Pkt4::len() {
  80. size_t length = DHCPV4_PKT_HDR_LEN; // DHCPv4 header
  81. // ... and sum of lengths of all options
  82. for (Option::OptionCollection::const_iterator it = options_.begin();
  83. it != options_.end();
  84. ++it) {
  85. length += (*it).second->len();
  86. }
  87. return (length);
  88. }
  89. bool
  90. Pkt4::pack() {
  91. bufferOut_.writeUint8(op_);
  92. bufferOut_.writeUint8(htype_);
  93. bufferOut_.writeUint8(hlen_);
  94. bufferOut_.writeUint8(hops_);
  95. bufferOut_.writeUint32(transid_);
  96. bufferOut_.writeUint16(secs_);
  97. bufferOut_.writeUint16(flags_);
  98. bufferOut_.writeUint32(ciaddr_);
  99. bufferOut_.writeUint32(yiaddr_);
  100. bufferOut_.writeUint32(siaddr_);
  101. bufferOut_.writeUint32(giaddr_);
  102. bufferOut_.writeData(chaddr_, MAX_CHADDR_LEN);
  103. bufferOut_.writeData(sname_, MAX_SNAME_LEN);
  104. bufferOut_.writeData(file_, MAX_FILE_LEN);
  105. // write DHCP magic cookie
  106. bufferOut_.writeUint32(DHCP_OPTIONS_COOKIE);
  107. LibDHCP::packOptions(bufferOut_, options_);
  108. // add END option that indicates end of options
  109. // (End option is very simple, just a 255 octet)
  110. bufferOut_.writeUint8(DHO_END);
  111. return (true);
  112. }
  113. void
  114. Pkt4::unpack() {
  115. // input buffer (used during message reception)
  116. isc::util::InputBuffer bufferIn(&data_[0], data_.size());
  117. if (bufferIn.getLength() < DHCPV4_PKT_HDR_LEN) {
  118. isc_throw(OutOfRange, "Received truncated DHCPv4 packet (len="
  119. << bufferIn.getLength() << " received, at least "
  120. << DHCPV4_PKT_HDR_LEN << "is expected");
  121. }
  122. op_ = bufferIn.readUint8();
  123. htype_ = bufferIn.readUint8();
  124. hlen_ = bufferIn.readUint8();
  125. hops_ = bufferIn.readUint8();
  126. transid_ = bufferIn.readUint32();
  127. secs_ = bufferIn.readUint16();
  128. flags_ = bufferIn.readUint16();
  129. ciaddr_ = IOAddress(bufferIn.readUint32());
  130. yiaddr_ = IOAddress(bufferIn.readUint32());
  131. siaddr_ = IOAddress(bufferIn.readUint32());
  132. giaddr_ = IOAddress(bufferIn.readUint32());
  133. bufferIn.readData(chaddr_, MAX_CHADDR_LEN);
  134. bufferIn.readData(sname_, MAX_SNAME_LEN);
  135. bufferIn.readData(file_, MAX_FILE_LEN);
  136. if (bufferIn.getLength() == bufferIn.getPosition()) {
  137. // this is *NOT* DHCP packet. It does not have any DHCPv4 options. In
  138. // particular, it does not have magic cookie, a 4 byte sequence that
  139. // differentiates between DHCP and BOOTP packets.
  140. isc_throw(InvalidOperation, "Recevied BOOTP packet. BOOTP is not supported.");
  141. }
  142. if (bufferIn.getLength() - bufferIn.getPosition() < 4) {
  143. // there is not enough data to hold magic DHCP cookie
  144. isc_throw(Unexpected, "Truncated or no DHCP packet.");
  145. }
  146. uint32_t magic = bufferIn.readUint32();
  147. if (magic != DHCP_OPTIONS_COOKIE) {
  148. isc_throw(Unexpected, "Invalid or missing DHCP magic cookie");
  149. }
  150. size_t opts_len = bufferIn.getLength() - bufferIn.getPosition();
  151. vector<uint8_t> optsBuffer;
  152. // First use of readVector.
  153. bufferIn.readVector(optsBuffer, opts_len);
  154. LibDHCP::unpackOptions4(optsBuffer, options_);
  155. // TODO: check will need to be called separately, so hooks can be called after
  156. // packet is parsed, but before its content is verified
  157. check();
  158. }
  159. void Pkt4::check() {
  160. boost::shared_ptr<Option> typeOpt = getOption(DHO_DHCP_MESSAGE_TYPE);
  161. if (typeOpt) {
  162. uint8_t msg_type = typeOpt->getUint8();
  163. if (msg_type>DHCPLEASEACTIVE) {
  164. isc_throw(BadValue, "Invalid DHCP message type received:" << msg_type);
  165. }
  166. msg_type_ = msg_type;
  167. } else {
  168. isc_throw(Unexpected, "Missing DHCP Message Type option");
  169. }
  170. }
  171. void Pkt4::repack() {
  172. cout << "Convering RX packet to TX packet: " << data_.size() << " bytes." << endl;
  173. bufferOut_.writeData(&data_[0], data_.size());
  174. }
  175. std::string
  176. Pkt4::toText() {
  177. stringstream tmp;
  178. tmp << "localAddr=" << local_addr_.toText() << ":" << local_port_
  179. << " remoteAddr=" << remote_addr_.toText()
  180. << ":" << remote_port_ << ", msgtype=" << int(msg_type_)
  181. << ", transid=0x" << hex << transid_ << dec << endl;
  182. for (isc::dhcp::Option::OptionCollection::iterator opt=options_.begin();
  183. opt != options_.end();
  184. ++opt) {
  185. tmp << " " << opt->second->toText() << std::endl;
  186. }
  187. return tmp.str();
  188. }
  189. void
  190. Pkt4::setHWAddr(uint8_t hType, uint8_t hlen,
  191. const std::vector<uint8_t>& macAddr) {
  192. /// TODO Rewrite this once support for client-identifier option
  193. /// is implemented (ticket 1228?)
  194. if (hlen>MAX_CHADDR_LEN) {
  195. isc_throw(OutOfRange, "Hardware address (len=" << hlen
  196. << " too long. Max " << MAX_CHADDR_LEN << " supported.");
  197. }
  198. if ( (macAddr.size() == 0) && (hlen > 0) ) {
  199. isc_throw(OutOfRange, "Invalid HW Address specified");
  200. }
  201. htype_ = hType;
  202. hlen_ = hlen;
  203. memset(chaddr_, 0, MAX_CHADDR_LEN);
  204. memcpy(chaddr_, &macAddr[0], hlen);
  205. }
  206. void
  207. Pkt4::setSname(const uint8_t* sname, size_t snameLen /*= MAX_SNAME_LEN*/) {
  208. if (snameLen > MAX_SNAME_LEN) {
  209. isc_throw(OutOfRange, "sname field (len=" << snameLen
  210. << ") too long, Max " << MAX_SNAME_LEN << " supported.");
  211. }
  212. memset(sname_, 0, MAX_SNAME_LEN);
  213. memcpy(sname_, sname, snameLen);
  214. // no need to store snameLen as any empty space is filled with 0s
  215. }
  216. void
  217. Pkt4::setFile(const uint8_t* file, size_t fileLen /*= MAX_FILE_LEN*/) {
  218. if (fileLen > MAX_FILE_LEN) {
  219. isc_throw(OutOfRange, "file field (len=" << fileLen
  220. << ") too long, Max " << MAX_FILE_LEN << " supported.");
  221. }
  222. memset(file_, 0, MAX_FILE_LEN);
  223. memcpy(file_, file, fileLen);
  224. // no need to store fileLen as any empty space is filled with 0s
  225. }
  226. uint8_t
  227. Pkt4::DHCPTypeToBootpType(uint8_t dhcpType) {
  228. switch (dhcpType) {
  229. case DHCPDISCOVER:
  230. case DHCPREQUEST:
  231. case DHCPDECLINE:
  232. case DHCPRELEASE:
  233. case DHCPINFORM:
  234. case DHCPLEASEQUERY:
  235. return (BOOTREQUEST);
  236. case DHCPACK:
  237. case DHCPNAK:
  238. case DHCPOFFER:
  239. case DHCPLEASEUNASSIGNED:
  240. case DHCPLEASEUNKNOWN:
  241. case DHCPLEASEACTIVE:
  242. return (BOOTREPLY);
  243. default:
  244. isc_throw(OutOfRange, "Invalid message type: "
  245. << static_cast<int>(dhcpType) );
  246. }
  247. }
  248. void
  249. Pkt4::addOption(boost::shared_ptr<Option> opt) {
  250. // check for uniqueness (DHCPv4 options must be unique)
  251. if (getOption(opt->getType())) {
  252. isc_throw(BadValue, "Option " << opt->getType()
  253. << " already present in this message.");
  254. }
  255. options_.insert(pair<int, boost::shared_ptr<Option> >(opt->getType(), opt));
  256. }
  257. boost::shared_ptr<isc::dhcp::Option>
  258. Pkt4::getOption(uint8_t type) {
  259. Option::OptionCollection::const_iterator x = options_.find(type);
  260. if (x!=options_.end()) {
  261. return (*x).second;
  262. }
  263. return boost::shared_ptr<isc::dhcp::Option>(); // NULL
  264. }
  265. void
  266. Pkt4::updateTimestamp() {
  267. timestamp_ = boost::posix_time::microsec_clock::universal_time();
  268. }
  269. } // end of namespace isc::dhcp
  270. } // end of namespace isc