pkt_transform.cc 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. // Copyright (C) 2012 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 <iostream>
  15. #include <exceptions/exceptions.h>
  16. #include <dhcp/option.h>
  17. #include <dhcp/libdhcp++.h>
  18. #include <dhcp/dhcp6.h>
  19. #include "pkt_transform.h"
  20. #include "localized_option.h"
  21. using namespace std;
  22. using namespace isc;
  23. using namespace dhcp;
  24. namespace isc {
  25. namespace perfdhcp {
  26. bool
  27. PktTransform::pack(const Option::Universe universe,
  28. const OptionBuffer& in_buffer,
  29. const Option::OptionCollection& options,
  30. const size_t transid_offset,
  31. const uint32_t transid,
  32. util::OutputBuffer& out_buffer) {
  33. // Always override the packet if function is called.
  34. out_buffer.clear();
  35. // Write whole buffer to output buffer.
  36. out_buffer.writeData(&in_buffer[0], in_buffer.size());
  37. uint8_t transid_len = (universe == Option::V6) ? 3 : 4;
  38. if ((transid_offset + transid_len >= in_buffer.size()) ||
  39. (transid_offset == 0)) {
  40. cout << "Failed to build packet: provided transaction id offset: "
  41. << transid_offset << " is out of bounds (expected 1.."
  42. << in_buffer.size()-1 << ")." << endl;
  43. return (false);
  44. }
  45. try {
  46. size_t offset_ptr = transid_offset;
  47. if (universe == Option::V4) {
  48. out_buffer.writeUint8At(transid >> 24 & 0xFF, offset_ptr++);
  49. }
  50. out_buffer.writeUint8At(transid >> 16 & 0xFF, offset_ptr++);
  51. out_buffer.writeUint8At(transid >> 8 & 0xFF, offset_ptr++);
  52. out_buffer.writeUint8At(transid & 0xFF, offset_ptr++);
  53. // We already have packet template stored in output buffer
  54. // but still some options have to be updated if client
  55. // specified them along with their offsets in the buffer.
  56. PktTransform::packOptions(in_buffer, options, out_buffer);
  57. } catch (const isc::BadValue& e) {
  58. cout << "Building packet failed: " << e.what() << endl;
  59. return (false);
  60. }
  61. return (true);
  62. }
  63. bool
  64. PktTransform::unpack(const Option::Universe universe,
  65. const OptionBuffer& in_buffer,
  66. const Option::OptionCollection& options,
  67. const size_t transid_offset,
  68. uint32_t& transid) {
  69. uint8_t transid_len = (universe == Option::V6) ? 3 : 4;
  70. // Validate transaction id offset.
  71. if ((transid_offset + transid_len + 1 > in_buffer.size()) ||
  72. (transid_offset == 0)) {
  73. cout << "Failed to parse packet: provided transaction id offset: "
  74. << transid_offset << " is out of bounds (expected 1.."
  75. << in_buffer.size()-1 << ")." << endl;
  76. return (false);
  77. }
  78. // Read transaction id from the buffer.
  79. // For DHCPv6 we transaction id is 3 bytes long so the high byte
  80. // of transid will be zero.
  81. OptionBufferConstIter it = in_buffer.begin() + transid_offset;
  82. transid = 0;
  83. for (int i = 0; i < transid_len; ++i, ++it) {
  84. // Read next byte and shift it left to its position in
  85. // transid (shift by the number of bytes read so far.
  86. transid += *it << (transid_len - i - 1) * 8;
  87. }
  88. try {
  89. PktTransform::unpackOptions(in_buffer, options);
  90. } catch (const isc::BadValue& e) {
  91. cout << "Packet parsing failed: " << e.what() << endl;
  92. return (false);
  93. }
  94. return (true);
  95. }
  96. void
  97. PktTransform::packOptions(const OptionBuffer& in_buffer,
  98. const Option::OptionCollection& options,
  99. util::OutputBuffer& out_buffer) {
  100. try {
  101. // If there are any options on the list, we will use provided
  102. // options offsets to override them in the output buffer
  103. // with new contents.
  104. for (Option::OptionCollection::const_iterator it = options.begin();
  105. it != options.end(); ++it) {
  106. // Get options with their position (offset).
  107. boost::shared_ptr<LocalizedOption> option =
  108. boost::dynamic_pointer_cast<LocalizedOption>(it->second);
  109. if (option == NULL) {
  110. isc_throw(isc::BadValue, "option is null");
  111. }
  112. uint32_t offset = option->getOffset();
  113. if ((offset == 0) ||
  114. (offset + option->len() > in_buffer.size())) {
  115. isc_throw(isc::BadValue,
  116. "option offset for option: " << option->getType()
  117. << " is out of bounds (expected 1.."
  118. << in_buffer.size() - option->len() << ")");
  119. }
  120. // Create temporary buffer to store option contents.
  121. util::OutputBuffer buf(option->len());
  122. // Pack option contents into temporary buffer.
  123. option->pack(buf);
  124. // OutputBuffer class has nice functions that write
  125. // data at the specified position so we can use it to
  126. // inject contents of temporary buffer to output buffer.
  127. const uint8_t *buf_data =
  128. static_cast<const uint8_t*>(buf.getData());
  129. for (int i = 0; i < buf.getLength(); ++i) {
  130. out_buffer.writeUint8At(buf_data[i], offset + i);
  131. }
  132. }
  133. }
  134. catch (const Exception&) {
  135. isc_throw(isc::BadValue, "failed to pack options into buffer.");
  136. }
  137. }
  138. void
  139. PktTransform::unpackOptions(const OptionBuffer& in_buffer,
  140. const Option::OptionCollection& options) {
  141. for (Option::OptionCollection::const_iterator it = options.begin();
  142. it != options.end(); ++it) {
  143. boost::shared_ptr<LocalizedOption> option =
  144. boost::dynamic_pointer_cast<LocalizedOption>(it->second);
  145. if (option == NULL) {
  146. isc_throw(isc::BadValue, "option is null");
  147. }
  148. size_t opt_pos = option->getOffset();
  149. if (opt_pos == 0) {
  150. isc_throw(isc::BadValue, "failed to unpack packet from raw buffer "
  151. "(Option position not specified)");
  152. } else if (opt_pos + option->getHeaderLen() > in_buffer.size()) {
  153. isc_throw(isc::BadValue,
  154. "failed to unpack options from from raw buffer "
  155. "(Option position out of bounds)");
  156. }
  157. size_t offset = opt_pos;
  158. size_t offset_step = 1;
  159. uint16_t opt_type = 0;
  160. if (option->getUniverse() == Option::V6) {
  161. offset_step = 2;
  162. // For DHCPv6 option type is in first two octets.
  163. opt_type = in_buffer[offset] * 256 + in_buffer[offset + 1];
  164. } else {
  165. // For DHCPv4 option type is in first octet.
  166. opt_type = in_buffer[offset];
  167. }
  168. // Check if we got expected option type.
  169. if (opt_type != option->getType()) {
  170. isc_throw(isc::BadValue,
  171. "failed to unpack option from raw buffer "
  172. "(option type mismatch)");
  173. }
  174. // Get option length which is supposed to be after option type.
  175. offset += offset_step;
  176. uint16_t opt_len = in_buffer[offset] * 256 + in_buffer[offset + 1];
  177. if (option->getUniverse() == Option::V6) {
  178. opt_len = in_buffer[offset] * 256 + in_buffer[offset + 1];
  179. } else {
  180. opt_len = in_buffer[offset];
  181. }
  182. // Check if packet is not truncated.
  183. if (offset + option->getHeaderLen() + opt_len > in_buffer.size()) {
  184. isc_throw(isc::BadValue,
  185. "failed to unpack option from raw buffer "
  186. "(option truncated)");
  187. }
  188. // Seek to actual option data and replace it.
  189. offset += offset_step;
  190. option->setData(in_buffer.begin() + offset,
  191. in_buffer.begin() + offset + opt_len);
  192. }
  193. }
  194. } // namespace perfdhcp
  195. } // namespace isc