protocol_util.cc 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. // Copyright (C) 2013 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 <asiolink/io_address.h>
  15. #include <dhcp/dhcp6.h>
  16. #include <dhcp/protocol_util.h>
  17. #include <boost/static_assert.hpp>
  18. // in_systm.h is required on some some BSD systems
  19. // complaining that n_time is undefined but used
  20. // in ip.h.
  21. #include <netinet/in_systm.h>
  22. #include <netinet/ip.h>
  23. using namespace isc::asiolink;
  24. using namespace isc::util;
  25. namespace isc {
  26. namespace dhcp {
  27. void
  28. decodeEthernetHeader(InputBuffer& buf, Pkt4Ptr& pkt) {
  29. // The size of the buffer to be parsed must not be lower
  30. // then the size of the Ethernet frame header.
  31. if (buf.getLength() - buf.getPosition() < ETHERNET_HEADER_LEN) {
  32. isc_throw(InvalidPacketHeader, "size of ethernet header in received "
  33. << "packet is invalid, expected at least "
  34. << ETHERNET_HEADER_LEN << " bytes, received "
  35. << buf.getLength() - buf.getPosition() << " bytes");
  36. }
  37. // Packet object must not be NULL. We want to output some values
  38. // to this object.
  39. if (!pkt) {
  40. isc_throw(BadValue, "NULL packet object provided when parsing ethernet"
  41. " frame header");
  42. }
  43. // The size of the single address is always lower then the size of
  44. // the header that holds this address. Otherwise, it is a programming
  45. // error that we want to detect in the compilation time.
  46. BOOST_STATIC_ASSERT(ETHERNET_HEADER_LEN > HWAddr::ETHERNET_HWADDR_LEN);
  47. // Remember initial position.
  48. size_t start_pos = buf.getPosition();
  49. // Read the destination HW address.
  50. std::vector<uint8_t> dest_addr;
  51. buf.readVector(dest_addr, HWAddr::ETHERNET_HWADDR_LEN);
  52. pkt->setLocalHWAddr(HWTYPE_ETHERNET, HWAddr::ETHERNET_HWADDR_LEN, dest_addr);
  53. // Read the source HW address.
  54. std::vector<uint8_t> src_addr;
  55. buf.readVector(src_addr, HWAddr::ETHERNET_HWADDR_LEN);
  56. pkt->setRemoteHWAddr(HWTYPE_ETHERNET, HWAddr::ETHERNET_HWADDR_LEN, src_addr);
  57. // Move the buffer read pointer to the end of the Ethernet frame header.
  58. buf.setPosition(start_pos + ETHERNET_HEADER_LEN);
  59. }
  60. void
  61. decodeIpUdpHeader(InputBuffer& buf, Pkt4Ptr& pkt) {
  62. // The size of the buffer must be at least equal to the minimal size of
  63. // the IPv4 packet header plus UDP header length.
  64. if (buf.getLength() - buf.getPosition() < MIN_IP_HEADER_LEN + UDP_HEADER_LEN) {
  65. isc_throw(InvalidPacketHeader, "the total size of the IP and UDP headers in "
  66. << "received packet is invalid, expected at least "
  67. << MIN_IP_HEADER_LEN + UDP_HEADER_LEN
  68. << " bytes, received " << buf.getLength() - buf.getPosition()
  69. << " bytes");
  70. }
  71. // Packet object must not be NULL.
  72. if (!pkt) {
  73. isc_throw(BadValue, "NULL packet object provided when parsing IP and UDP"
  74. " packet headers");
  75. }
  76. BOOST_STATIC_ASSERT(IP_SRC_ADDR_OFFSET < MIN_IP_HEADER_LEN);
  77. // Remember initial position of the read pointer.
  78. size_t start_pos = buf.getPosition();
  79. // Read IP header length (mask most significant bits as they indicate IP version).
  80. uint8_t ip_len = buf.readUint8() & 0xF;
  81. // IP length is the number of 4 byte chunks that construct IPv4 header.
  82. // It must not be lower than 5 because first 20 bytes are fixed.
  83. if (ip_len < 5) {
  84. isc_throw(InvalidPacketHeader, "Value of the length of the IP header must not be"
  85. << " lower than 5 words. The length of the received header is "
  86. << ip_len << ".");
  87. }
  88. // Seek to the position of source IP address.
  89. buf.setPosition(start_pos + IP_SRC_ADDR_OFFSET);
  90. // Read source address.
  91. pkt->setRemoteAddr(IOAddress(buf.readUint32()));
  92. // Read destination address.
  93. pkt->setLocalAddr(IOAddress(buf.readUint32()));
  94. // Skip IP header options (if any) to start of the
  95. // UDP header.
  96. buf.setPosition(start_pos + ip_len * 4);
  97. // Read source port from UDP header.
  98. pkt->setRemotePort(buf.readUint16());
  99. // Read destination port from UDP header.
  100. pkt->setLocalPort(buf.readUint16());
  101. // Set the pointer position to the first byte o the
  102. // UDP payload (DHCP packet).
  103. buf.setPosition(start_pos + ip_len * 4 + UDP_HEADER_LEN);
  104. }
  105. void
  106. writeEthernetHeader(const Pkt4Ptr& pkt, OutputBuffer& out_buf) {
  107. // Set destination HW address.
  108. HWAddrPtr remote_addr = pkt->getRemoteHWAddr();
  109. if (remote_addr) {
  110. if (remote_addr->hwaddr_.size() == HWAddr::ETHERNET_HWADDR_LEN) {
  111. out_buf.writeData(&remote_addr->hwaddr_[0],
  112. HWAddr::ETHERNET_HWADDR_LEN);
  113. } else {
  114. isc_throw(BadValue, "invalid size of the remote HW address "
  115. << remote_addr->hwaddr_.size() << " when constructing"
  116. << " an ethernet frame header; expected size is"
  117. << " " << HWAddr::ETHERNET_HWADDR_LEN);
  118. }
  119. } else {
  120. // HW address has not been specified. This is possible when receiving
  121. // packet through a logical interface (e.g. lo). In such cases, we
  122. // don't want to fail but rather provide a default HW address, which
  123. // consists of zeros.
  124. out_buf.writeData(&std::vector<uint8_t>(HWAddr::ETHERNET_HWADDR_LEN)[0],
  125. HWAddr::ETHERNET_HWADDR_LEN);
  126. }
  127. // Set source HW address.
  128. HWAddrPtr local_addr = pkt->getLocalHWAddr();
  129. if (local_addr) {
  130. if (local_addr->hwaddr_.size() == HWAddr::ETHERNET_HWADDR_LEN) {
  131. out_buf.writeData(&local_addr->hwaddr_[0],
  132. HWAddr::ETHERNET_HWADDR_LEN);
  133. } else {
  134. isc_throw(BadValue, "invalid size of the local HW address "
  135. << local_addr->hwaddr_.size() << " when constructing"
  136. << " an ethernet frame header; expected size is"
  137. << " " << HWAddr::ETHERNET_HWADDR_LEN);
  138. }
  139. } else {
  140. // Provide default HW address.
  141. out_buf.writeData(&std::vector<uint8_t>(HWAddr::ETHERNET_HWADDR_LEN)[0],
  142. HWAddr::ETHERNET_HWADDR_LEN);
  143. }
  144. // Type IP.
  145. out_buf.writeUint16(ETHERNET_TYPE_IP);
  146. }
  147. void
  148. writeIpUdpHeader(const Pkt4Ptr& pkt, util::OutputBuffer& out_buf) {
  149. out_buf.writeUint8(0x45); // IP version 4, IP header length 5
  150. out_buf.writeUint8(IPTOS_LOWDELAY); // DSCP and ECN
  151. out_buf.writeUint16(28 + pkt->getBuffer().getLength()); // Total length.
  152. out_buf.writeUint16(0); // Identification
  153. out_buf.writeUint16(0x4000); // Disable fragmentation.
  154. out_buf.writeUint8(128); // TTL
  155. out_buf.writeUint8(IPPROTO_UDP); // Protocol UDP.
  156. out_buf.writeUint16(0); // Temporarily set checksum to 0.
  157. out_buf.writeUint32(pkt->getLocalAddr()); // Source address.
  158. out_buf.writeUint32(pkt->getRemoteAddr()); // Destination address.
  159. // Calculate pseudo header checksum. It will be necessary to compute
  160. // UDP checksum.
  161. // Get the UDP length. This includes udp header's and data length.
  162. uint32_t udp_len = 8 + pkt->getBuffer().getLength();
  163. // The magic number "8" indicates the offset where the source address
  164. // is stored in the buffer. This offset is counted here from the
  165. // current tail of the buffer. Starting from this offset we calculate
  166. // the checksum using 8 following bytes of data. This will include
  167. // 4 bytes of source address and 4 bytes of destination address.
  168. // The IPPROTO_UDP and udp_len are also added up to the checksum.
  169. uint16_t pseudo_hdr_checksum =
  170. calcChecksum(static_cast<const uint8_t*>(out_buf.getData()) + out_buf.getLength() - 8,
  171. 8, IPPROTO_UDP + udp_len);
  172. // Calculate IP header checksum.
  173. uint16_t ip_checksum = ~calcChecksum(static_cast<const uint8_t*>(out_buf.getData())
  174. + out_buf.getLength() - 20, 20);
  175. // Write checksum in the IP header. The offset of the checksum is 10 bytes
  176. // back from the tail of the current buffer.
  177. out_buf.writeUint16At(ip_checksum, out_buf.getLength() - 10);
  178. // Start UDP header.
  179. out_buf.writeUint16(pkt->getLocalPort()); // Source port.
  180. out_buf.writeUint16(pkt->getRemotePort()); // Destination port.
  181. out_buf.writeUint16(udp_len); // Length of the header and data.
  182. // Checksum is calculated from the contents of UDP header, data and pseudo ip header.
  183. // The magic number "6" indicates that the UDP header starts at offset 6 from the
  184. // tail of the current buffer. These 6 bytes contain source and destination port
  185. // as well as the length of the header.
  186. uint16_t udp_checksum =
  187. ~calcChecksum(static_cast<const uint8_t*>(out_buf.getData()) + out_buf.getLength() - 6, 6,
  188. calcChecksum(static_cast<const uint8_t*>(pkt->getBuffer().getData()),
  189. pkt->getBuffer().getLength(),
  190. pseudo_hdr_checksum));
  191. // Write UDP checksum.
  192. out_buf.writeUint16(udp_checksum);
  193. }
  194. uint16_t
  195. calcChecksum(const uint8_t* buf, const uint32_t buf_size, uint32_t sum) {
  196. uint32_t i;
  197. for (i = 0; i < (buf_size & ~1U); i += 2) {
  198. uint16_t chunk = buf[i] << 8 | buf[i + 1];
  199. sum += chunk;
  200. if (sum > 0xFFFF) {
  201. sum -= 0xFFFF;
  202. }
  203. }
  204. // If one byte has left, we also need to add it to the checksum.
  205. if (i < buf_size) {
  206. sum += buf[i] << 8;
  207. if (sum > 0xFFFF) {
  208. sum -= 0xFFFF;
  209. }
  210. }
  211. return (sum);
  212. }
  213. }
  214. }