option.cc 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  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 <string.h>
  15. #include <stdint.h>
  16. #include <arpa/inet.h>
  17. #include <sstream>
  18. #include <iomanip>
  19. #include <boost/shared_array.hpp>
  20. #include "exceptions/exceptions.h"
  21. #include "util/io_utilities.h"
  22. #include "dhcp/option.h"
  23. #include "dhcp/libdhcp++.h"
  24. using namespace std;
  25. using namespace isc::dhcp;
  26. using namespace isc::util;
  27. Option::Option(Universe u, unsigned short type)
  28. :universe_(u), type_(type) {
  29. if ((u == V4) && (type > 255)) {
  30. isc_throw(BadValue, "Can't create V4 option of type "
  31. << type << ", V4 options are in range 0..255");
  32. }
  33. }
  34. Option::Option(Universe u, unsigned short type,
  35. const boost::shared_array<uint8_t>& buf,
  36. unsigned int offset, unsigned int len)
  37. :universe_(u), type_(type),
  38. offset_(offset)
  39. {
  40. uint8_t* ptr = &buf[offset];
  41. data_ = std::vector<uint8_t>(ptr, ptr + len);
  42. check();
  43. }
  44. Option::Option(Universe u, unsigned short type, std::vector<uint8_t>& data)
  45. :universe_(u), type_(type), data_(data) {
  46. check();
  47. }
  48. Option::Option(Universe u, uint16_t type, vector<uint8_t>::const_iterator first,
  49. vector<uint8_t>::const_iterator last)
  50. :universe_(u), type_(type), data_(std::vector<uint8_t>(first,last)) {
  51. check();
  52. }
  53. void
  54. Option::check() {
  55. if ( (universe_ != V4) && (universe_ != V6) ) {
  56. isc_throw(BadValue, "Invalid universe type specified."
  57. << "Only V4 and V6 are allowed.");
  58. }
  59. if (universe_ == V4) {
  60. if (type_ > 255) {
  61. isc_throw(OutOfRange, "DHCPv4 Option type " << type_ << " is too big."
  62. << "For DHCPv4 allowed type range is 0..255");
  63. } else if (data_.size() > 255) {
  64. isc_throw(OutOfRange, "DHCPv4 Option " << type_ << " is too big.");
  65. /// TODO Larger options can be stored as separate instances
  66. /// of DHCPv4 options. Clients MUST concatenate them.
  67. /// Fortunately, there are no such large options used today.
  68. }
  69. }
  70. // no need to check anything for DHCPv6. It allows full range (0-64k) of
  71. // both types and data size.
  72. }
  73. unsigned int
  74. Option::pack(boost::shared_array<uint8_t>& buf,
  75. unsigned int buf_len,
  76. unsigned int offset) {
  77. if (universe_ != V6) {
  78. isc_throw(BadValue, "Failed to pack " << type_ << " option. Do not "
  79. << "use this method for options other than DHCPv6.");
  80. }
  81. return pack6(buf, buf_len, offset);
  82. }
  83. void
  84. Option::pack4(isc::util::OutputBuffer& buf) {
  85. switch (universe_) {
  86. case V4: {
  87. if (data_.size() > 255) {
  88. isc_throw(OutOfRange, "DHCPv4 Option " << type_ << " is too big."
  89. << "At most 255 bytes are supported.");
  90. /// TODO Larger options can be stored as separate instances
  91. /// of DHCPv4 options. Clients MUST concatenate them.
  92. /// Fortunately, there are no such large options used today.
  93. }
  94. buf.writeUint8(type_);
  95. buf.writeUint8(len() - getHeaderLen());
  96. buf.writeData(&data_[0], data_.size());
  97. LibDHCP::packOptions(buf, options_);
  98. return;
  99. }
  100. case V6:
  101. /// TODO: Do we need a sanity check for option size here?
  102. buf.writeUint16(type_);
  103. buf.writeUint16(len() - getHeaderLen());
  104. LibDHCP::packOptions(buf, options_);
  105. return;
  106. default:
  107. isc_throw(OutOfRange, "Invalid universe type" << universe_);
  108. }
  109. }
  110. unsigned int
  111. Option::pack6(boost::shared_array<uint8_t>& buf,
  112. unsigned int buf_len,
  113. unsigned int offset) {
  114. if (offset+len() > buf_len) {
  115. isc_throw(OutOfRange, "Failed to pack v6 option=" <<
  116. type_ << ",len=" << len() << ": too small buffer.");
  117. }
  118. uint8_t* ptr = &buf[offset];
  119. ptr = writeUint16(type_, ptr);
  120. ptr = writeUint16(len() - getHeaderLen(), ptr);
  121. if (! data_.empty())
  122. memcpy(ptr, &data_[0], data_.size());
  123. // end of fixed part of this option
  124. offset += OPTION6_HDR_LEN + data_.size();
  125. return LibDHCP::packOptions6(buf, buf_len, offset, options_);
  126. }
  127. unsigned int
  128. Option::unpack(const boost::shared_array<uint8_t>& buf,
  129. unsigned int buf_len,
  130. unsigned int offset,
  131. unsigned int parse_len) {
  132. switch (universe_) {
  133. case V4:
  134. return unpack4(buf, buf_len, offset, parse_len);
  135. case V6:
  136. return unpack6(buf, buf_len, offset, parse_len);
  137. default:
  138. isc_throw(BadValue, "Unknown universe defined for Option " << type_);
  139. }
  140. return 0; // should not happen
  141. }
  142. unsigned int
  143. Option::unpack4(const boost::shared_array<uint8_t>&,
  144. unsigned int ,
  145. unsigned int ,
  146. unsigned int ) {
  147. isc_throw(Unexpected, "IPv4 support not implemented yet.");
  148. return 0;
  149. }
  150. unsigned int
  151. Option::unpack6(const boost::shared_array<uint8_t>& buf,
  152. unsigned int buf_len,
  153. unsigned int offset,
  154. unsigned int parse_len) {
  155. if (buf_len < offset+parse_len) {
  156. isc_throw(OutOfRange, "Failed to unpack DHCPv6 option len="
  157. << parse_len << " offset=" << offset
  158. << " from buffer (length=" << buf_len
  159. << "): too small buffer.");
  160. }
  161. uint8_t* ptr = &buf[offset];
  162. data_ = std::vector<uint8_t>(ptr, ptr + parse_len);
  163. offset_ = offset;
  164. return (offset+parse_len);
  165. //return LibDHCP::unpackOptions6(buf, buf_len, offset, parse_len,
  166. // options_);
  167. }
  168. /// Returns length of the complete option (data length + DHCPv4/DHCPv6
  169. /// option header)
  170. uint16_t
  171. Option::len() {
  172. // length of the whole option is header and data stored in this option...
  173. int length = getHeaderLen() + data_.size();
  174. // ... and sum of lengths of all suboptions
  175. for (Option::OptionCollection::iterator it = options_.begin();
  176. it != options_.end();
  177. ++it) {
  178. length += (*it).second->len();
  179. }
  180. // note that this is not equal to lenght field. This value denotes
  181. // number of bytes required to store this option. length option should
  182. // contain (len()-getHeaderLen()) value.
  183. return (length);
  184. }
  185. bool
  186. Option::valid() {
  187. if (universe_ != V4 &&
  188. universe_ != V6) {
  189. return (false);
  190. }
  191. return (true);
  192. }
  193. boost::shared_ptr<isc::dhcp::Option>
  194. Option::getOption(unsigned short opt_type) {
  195. isc::dhcp::Option::OptionCollection::const_iterator x =
  196. options_.find(opt_type);
  197. if ( x != options_.end() ) {
  198. return (*x).second;
  199. }
  200. return boost::shared_ptr<isc::dhcp::Option>(); // NULL
  201. }
  202. bool
  203. Option::delOption(unsigned short opt_type) {
  204. isc::dhcp::Option::OptionCollection::iterator x = options_.find(opt_type);
  205. if ( x != options_.end() ) {
  206. options_.erase(x);
  207. return true; // delete successful
  208. }
  209. return (false); // option not found, can't delete
  210. }
  211. std::string Option::toText(int indent /* =0 */ ) {
  212. std::stringstream tmp;
  213. for (int i = 0; i < indent; i++)
  214. tmp << " ";
  215. tmp << "type=" << type_ << ", len=" << len()-getHeaderLen() << ": ";
  216. for (unsigned int i = 0; i < data_.size(); i++) {
  217. if (i) {
  218. tmp << ":";
  219. }
  220. tmp << setfill('0') << setw(2) << hex
  221. << static_cast<unsigned short>(data_[i]);
  222. }
  223. // print suboptions
  224. for (OptionCollection::const_iterator opt = options_.begin();
  225. opt != options_.end();
  226. ++opt) {
  227. tmp << (*opt).second->toText(indent+2);
  228. }
  229. return tmp.str();
  230. }
  231. uint16_t
  232. Option::getHeaderLen() {
  233. switch (universe_) {
  234. case V4:
  235. return OPTION4_HDR_LEN; // header length for v4
  236. case V6:
  237. return OPTION6_HDR_LEN; // header length for v6
  238. }
  239. return 0; // should not happen
  240. }
  241. void
  242. Option::addOption(boost::shared_ptr<Option> opt) {
  243. if (universe_ == V4) {
  244. // check for uniqueness (DHCPv4 options must be unique)
  245. if (getOption(opt->getType())) {
  246. isc_throw(BadValue, "Option " << opt->getType()
  247. << " already present in this message.");
  248. }
  249. }
  250. options_.insert(pair<int, boost::shared_ptr<Option> >(opt->getType(), opt));
  251. }
  252. Option::~Option() {
  253. }