BIND 10 trac2902, updated. d8595ab6bd85996c00019da0b086fdb40374e1f9 [2902] Implemented utility functions encoding UDP packets.
BIND 10 source code commits
bind10-changes at lists.isc.org
Tue Apr 23 12:37:43 UTC 2013
The branch, trac2902 has been updated
via d8595ab6bd85996c00019da0b086fdb40374e1f9 (commit)
from edd0b0623481ea70b4af23a0cbd2bb4ef797d10f (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commit d8595ab6bd85996c00019da0b086fdb40374e1f9
Author: Marcin Siodelski <marcin at isc.org>
Date: Tue Apr 23 14:37:21 2013 +0200
[2902] Implemented utility functions encoding UDP packets.
-----------------------------------------------------------------------
Summary of changes:
src/lib/dhcp/pkt_filter_lpf.cc | 12 +-
src/lib/dhcp/protocol_util.cc | 91 +++++-----
src/lib/dhcp/protocol_util.h | 22 +--
src/lib/dhcp/tests/Makefile.am | 1 +
src/lib/dhcp/tests/protocol_util_unittest.cc | 237 ++++++++++++++++++++++++++
5 files changed, 310 insertions(+), 53 deletions(-)
create mode 100644 src/lib/dhcp/tests/protocol_util_unittest.cc
-----------------------------------------------------------------------
diff --git a/src/lib/dhcp/pkt_filter_lpf.cc b/src/lib/dhcp/pkt_filter_lpf.cc
index 4070c9b..83bb707 100644
--- a/src/lib/dhcp/pkt_filter_lpf.cc
+++ b/src/lib/dhcp/pkt_filter_lpf.cc
@@ -54,9 +54,9 @@ PktFilterLPF::openSocket(const Iface& iface, const isc::asiolink::IOAddress&,
}
Pkt4Ptr
-PktFilterLPF::receive(const Iface&, const SocketInfo& socket_info) {
+PktFilterLPF::receive(const Iface& iface, const SocketInfo& socket_info) {
// @todo: implement this function
- unsigned char buf[1536];
+ uint8_t buf[IfaceMgr::RCVBUFSIZE];
int data_len = read(socket_info.sockfd_, buf, sizeof(buf));
if (data_len <= 0) {
return Pkt4Ptr();
@@ -66,6 +66,10 @@ PktFilterLPF::receive(const Iface&, const SocketInfo& socket_info) {
int data_offset = 42;
Pkt4Ptr pkt = Pkt4Ptr(new Pkt4(buf + data_offset,
data_len - data_offset));
+
+ pkt->setIndex(iface.getIndex());
+ pkt->setIface(iface.getName());
+
return (pkt);
}
@@ -99,9 +103,9 @@ PktFilterLPF::send(const Iface& iface, uint16_t sockfd, const Pkt4Ptr& pkt) {
if (result < 0) {
isc_throw(SocketWriteError, "pkt4 send failed");
}
-
+
return (0);
-
+
}
diff --git a/src/lib/dhcp/protocol_util.cc b/src/lib/dhcp/protocol_util.cc
index eb8209a..b73436b 100644
--- a/src/lib/dhcp/protocol_util.cc
+++ b/src/lib/dhcp/protocol_util.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
@@ -13,16 +13,14 @@
// PERFORMANCE OF THIS SOFTWARE.
#include <protocol_util.h>
-
#include <netinet/ip.h>
-#include <netinet/udp.h>
namespace isc {
namespace dhcp {
void
writeEthernetHeader(const uint8_t* src_hw_addr, const uint8_t* dest_hw_addr,
- util::OutputBuffer& out_buf) {
+ util::OutputBuffer& out_buf) {
// Write destination and source address.
out_buf.writeData(dest_hw_addr, HWAddr::ETHERNET_HWADDR_LEN);
out_buf.writeData(src_hw_addr, HWAddr::ETHERNET_HWADDR_LEN);
@@ -33,38 +31,58 @@ writeEthernetHeader(const uint8_t* src_hw_addr, const uint8_t* dest_hw_addr,
void
writeIpUdpHeader(const Pkt4Ptr& pkt, util::OutputBuffer& out_buf) {
- struct ip ip_hdr;
- memset(&ip_hdr, 0, sizeof(ip_hdr));
- ip_hdr.ip_hl = (ip_hdr.ip_hl | 5) & 0xF;
- ip_hdr.ip_v = (ip_hdr.ip_v | 4) & 0xF;
- ip_hdr.ip_tos = IPTOS_LOWDELAY;
- ip_hdr.ip_len = htons(sizeof(ip) + sizeof(udphdr) +
- pkt->getBuffer().getLength());
- ip_hdr.ip_id = 0;
- ip_hdr.ip_off = 0;
- ip_hdr.ip_ttl = 128;
- ip_hdr.ip_p = IPPROTO_UDP;
- ip_hdr.ip_src.s_addr = htonl(pkt->getLocalAddr());
- ip_hdr.ip_dst.s_addr = htonl(pkt->getRemoteAddr());
- ip_hdr.ip_sum =
- wrapChecksum(calculateChecksum(reinterpret_cast<const char*>(&ip_hdr),
- sizeof(ip_hdr)));
-
- out_buf.writeData(static_cast<void*>(&ip_hdr), sizeof(ip_hdr));
-
- struct udphdr udp_hdr;
- memset(&udp_hdr, 0, sizeof(udp_hdr));
- udp_hdr.source = htons(pkt->getLocalPort());
- udp_hdr.dest = htons(pkt->getRemotePort());
- udp_hdr.len = htons(sizeof(udp_hdr) + pkt->getBuffer().getLength());
- udp_hdr.check = 0;
-
- out_buf.writeData(static_cast<void*>(&udp_hdr), sizeof(udp_hdr));
-
+ out_buf.writeUint8(0x45); // IP version 4, IP header length 5
+ out_buf.writeUint8(IPTOS_LOWDELAY); // DSCP and ECN
+ out_buf.writeUint16(28 + pkt->getBuffer().getLength()); // Total length.
+ out_buf.writeUint16(0); // Identification
+ out_buf.writeUint16(0x4000); // Disable fragmentation.
+ out_buf.writeUint8(128); // TTL
+ out_buf.writeUint8(IPPROTO_UDP); // Protocol UDP.
+ out_buf.writeUint16(0); // Temporarily set checksum to 0.
+ out_buf.writeUint32(pkt->getLocalAddr()); // Source address.
+ out_buf.writeUint32(pkt->getRemoteAddr()); // Destination address.
+
+ // Calculate pseudo header checksum. It will be necessary to compute
+ // UDP checksum.
+ // Get the UDP length. This includes udp header's and data length.
+ uint32_t udp_len = 8 + pkt->getBuffer().getLength();
+ // The magic number "8" indicates the offset where the source address
+ // is stored in the buffer. This offset is counted here from the
+ // current tail of the buffer. Starting from this offset we calculate
+ // the checksum using 8 following bytes of data. This will include
+ // 4 bytes of source address and 4 bytes of destination address.
+ // The IPPROTO_UDP and udp_len are also added up to the checksum.
+ uint16_t pseudo_hdr_checksum =
+ calcChecksum(static_cast<const uint8_t*>(out_buf.getData()) + out_buf.getLength() - 8,
+ 8, IPPROTO_UDP + udp_len);
+
+ // Calculate IP header checksum.
+ uint16_t ip_checksum = ~calcChecksum(static_cast<const uint8_t*>(out_buf.getData())
+ + out_buf.getLength() - 20, 20);
+ // Write checksum in the IP header. The offset of the checksum is 10 bytes
+ // back from the tail of the current buffer.
+ out_buf.writeUint16At(ip_checksum, out_buf.getLength() - 10);
+
+ // Start UDP header.
+ out_buf.writeUint16(pkt->getLocalPort()); // Source port.
+ out_buf.writeUint16(pkt->getRemotePort()); // Destination port.
+ out_buf.writeUint16(udp_len); // Length of the header and data.
+
+ // Checksum is calculated from the contents of UDP header, data and pseudo ip header.
+ // The magic number "6" indicates that the UDP header starts at offset 6 from the
+ // tail of the current buffer. These 6 bytes contain source and destination port
+ // as well as the length of the header.
+ uint16_t udp_checksum =
+ ~calcChecksum(static_cast<const uint8_t*>(out_buf.getData()) + out_buf.getLength() - 6, 6,
+ calcChecksum(static_cast<const uint8_t*>(pkt->getBuffer().getData()),
+ pkt->getBuffer().getLength(),
+ pseudo_hdr_checksum));
+ // Write UDP checksum.
+ out_buf.writeUint16(udp_checksum);
}
uint16_t
-calculateChecksum(const char* buf, const uint32_t buf_size, uint32_t sum) {
+calcChecksum(const uint8_t* buf, const uint32_t buf_size, uint32_t sum) {
uint32_t i;
for (i = 0; i < (buf_size & ~1U); i += 2) {
uint16_t chunk = buf[i] << 8 | buf[i+1];
@@ -73,7 +91,7 @@ calculateChecksum(const char* buf, const uint32_t buf_size, uint32_t sum) {
sum -= 0xFFFF;
}
}
-
+ // If one byte has left, we also need to add it to the checksum.
if (i < buf_size) {
sum += buf[i] << 8;
if (sum > 0xFFFF) {
@@ -85,10 +103,5 @@ calculateChecksum(const char* buf, const uint32_t buf_size, uint32_t sum) {
}
-uint16_t
-wrapChecksum(uint16_t sum) {
- return (htons(~sum));
-}
-
}
}
diff --git a/src/lib/dhcp/protocol_util.h b/src/lib/dhcp/protocol_util.h
index 0c8ae13..5a6cfb4 100644
--- a/src/lib/dhcp/protocol_util.h
+++ b/src/lib/dhcp/protocol_util.h
@@ -45,20 +45,22 @@ void writeIpUdpHeader(const Pkt4Ptr& pkt, util::OutputBuffer& out_buf);
/// @brief Calculates checksum for provided buffer
///
+/// This function returns the sum of 16-bit values from the provided
+/// buffer. If the third parameter is specified, it indicates the
+/// initial checksum value. This parameter can be a result of
+/// calcChecksum function's invocation on different data buffer.
+/// The IP or UDP checksum value is a complement of the result returned
+/// by this function. However, this function does not compute complement
+/// of the summed values. It must be calculated outside of this function
+/// before writing the value to the packet buffer.
+///
/// @param buf buffer for which the checksum is calculated.
/// @param buf_size size of the buffer for which checksum is calculated.
-/// @param sum initial checksum value.
+/// @param sum initial checksum value, other values will be added to it.
///
/// @return calculated checksum.
-uint16_t calculateChecksum(const char* buf, const uint32_t buf_size,
- uint32_t sum = 0);
-
-/// @brief Wraps the calculated checksum and stores it in network byte order.
-///
-/// @param sum calculated checksum
-///
-/// @return wrapped checksum value.
-uint16_t wrapChecksum(uint16_t sum);
+uint16_t calcChecksum(const uint8_t* buf, const uint32_t buf_size,
+ uint32_t sum = 0);
}
}
diff --git a/src/lib/dhcp/tests/Makefile.am b/src/lib/dhcp/tests/Makefile.am
index c868553..74a34c8 100644
--- a/src/lib/dhcp/tests/Makefile.am
+++ b/src/lib/dhcp/tests/Makefile.am
@@ -43,6 +43,7 @@ libdhcp___unittests_SOURCES += option_unittest.cc
libdhcp___unittests_SOURCES += option_space_unittest.cc
libdhcp___unittests_SOURCES += pkt4_unittest.cc
libdhcp___unittests_SOURCES += pkt6_unittest.cc
+libdhcp___unittests_SOURCES += protocol_util_unittest.cc
libdhcp___unittests_SOURCES += duid_unittest.cc
libdhcp___unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) $(LOG4CPLUS_INCLUDES)
diff --git a/src/lib/dhcp/tests/protocol_util_unittest.cc b/src/lib/dhcp/tests/protocol_util_unittest.cc
new file mode 100644
index 0000000..ffdf0d3
--- /dev/null
+++ b/src/lib/dhcp/tests/protocol_util_unittest.cc
@@ -0,0 +1,237 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+#include <asiolink/io_address.h>
+#include <dhcp/protocol_util.h>
+#include <util/buffer.h>
+
+#include <gtest/gtest.h>
+
+#include <netinet/ip.h>
+
+using namespace isc;
+using namespace isc::asiolink;
+using namespace isc::dhcp;
+using namespace isc::util;
+
+namespace {
+
+ /*/// @brief OptionCustomTest test class.
+class OptionCustomTest : public ::testing::Test {
+public:
+};*/
+
+/// The purpose of this test is to verify that the IP header checksum
+/// is calculated correctly.
+TEST(ProtocolUtilTest, checksum) {
+ // IPv4 header to be used to calculate checksum.
+ const uint8_t hdr[] = {
+ 0x45, // IP version and header length
+ 0x00, // TOS
+ 0x00, 0x3c, // Total length of the IP packet.
+ 0x1c, 0x46, // Identification field.
+ 0x40, 0x00, // Fragmentation.
+ 0x40, // TTL
+ 0x06, // Protocol
+ 0x00, 0x00, // Checksum (reset to 0x00).
+ 0xac, 0x10, 0x0a, 0x63, // Source IP address.
+ 0xac, 0x10, 0x0a, 0x0c // Destination IP address.
+ };
+ // Calculate size of the header array.
+ const uint32_t hdr_size = sizeof(hdr) / sizeof(hdr[0]);
+ // Get the actual checksum.
+ uint16_t chksum = ~calcChecksum(hdr, hdr_size);
+ // The 0xb1e6 value has been calculated by other means.
+ EXPECT_EQ(0xb1e6, chksum);
+ // Tested function may also take the initial value of the sum.
+ // Let's set it to 2 and see whether it is included in the
+ // calculation.
+ chksum = ~calcChecksum(hdr, hdr_size, 2);
+ // The checkum value should change.
+ EXPECT_EQ(0xb1e4, chksum);
+}
+
+/// The purpose of this test is to verify that the ethernet
+/// header is correctly constructed from the destination and
+/// hardware addresses.
+TEST(ProtocolUtilTest, writeEthernetHeader) {
+ // Source HW address, 6 bytes.
+ const uint8_t src_hw_addr[6] = {
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15
+ };
+ // Destination HW address, 6 bytes.
+ const uint8_t dest_hw_addr[6] = {
+ 0x20, 0x31, 0x42, 0x53, 0x64, 0x75
+ };
+ // Create output buffer.
+ OutputBuffer buf(1);
+
+ // Construct the ethernet header using HW addresses.
+ writeEthernetHeader(src_hw_addr, dest_hw_addr, buf);
+
+ // The resulting ethernet header consists of destination
+ // and src HW address (each 6 bytes long) and two bytes
+ // of encapsulated protocol type.
+ ASSERT_EQ(14, buf.getLength());
+
+ // Verify that first 6 bytes comprise valid destination
+ // HW address. Instead of using memory comparison functions
+ // we check bytes one-by-one. In case of mismatch we will
+ // get exact values that are mismatched. If memcmp was used
+ // the error message would not indicate the values of
+ // mismatched bytes.
+ for (int i = 0; i < 6; ++i) {
+ EXPECT_EQ(dest_hw_addr[i], buf[i]);
+ }
+ // Verify that following 6 bytes comprise the valid source
+ // HW address.
+ for (int i = 0; i < 6; ++i) {
+ EXPECT_EQ(src_hw_addr[i], buf[i + 6]);
+ }
+
+ // The last two bytes comprise the encapsulated protocol type.
+ // We expect IPv4 protocol type which is specified by 0x0800.
+ EXPECT_EQ(0x08, buf[12]);
+ EXPECT_EQ(0x0, buf[13]);
+}
+
+TEST(ProtocolUtilTest, writeIpUdpHeader) {
+ // Create DHCPv4 packet. Some values held by this object are
+ // used by the utility function under test to figure out the
+ // contents of the IP and UDP headers, e.g. source and
+ // destination IP address or port number.
+ Pkt4Ptr pkt(new Pkt4(DHCPOFFER, 0));
+ ASSERT_TRUE(pkt);
+
+ // Set local and remote address and port.
+ pkt->setLocalAddr(IOAddress("192.0.2.1"));
+ pkt->setRemoteAddr(IOAddress("192.0.2.111"));
+ pkt->setLocalPort(DHCP4_SERVER_PORT);
+ pkt->setRemotePort(DHCP4_CLIENT_PORT);
+
+ // Pack the contents of the packet.
+ ASSERT_NO_THROW(pkt->pack());
+
+ // Create output buffer. The headers will be written to it.
+ OutputBuffer buf(1);
+ // Write some dummy data to the buffer. We will check that the
+ // function under test appends to this data, not overrides it.
+ buf.writeUint16(0x0102);
+
+ // Write both IP and UDP headers.
+ writeIpUdpHeader(pkt, buf);
+
+ // The resulting size of the buffer must be 30. The 28 bytes are
+ // consumed by the IP and UDP headers. The other 2 bytes are dummy
+ // data at the beginning of the buffer.
+ ASSERT_EQ(30, buf.getLength());
+
+ // Make sure that the existing data in the buffer was not corrupted
+ // by the function under test.
+ EXPECT_EQ(0x01, buf[0]);
+ EXPECT_EQ(0x02, buf[1]);
+
+ // Copy the contents of the buffer to InputBuffer object. This object
+ // exposes convenient functions for reading.
+ InputBuffer in_buf(buf.getData(), buf.getLength());
+
+ // Check dummy data.
+ uint16_t dummy_data = in_buf.readUint16();
+ EXPECT_EQ(0x0102, dummy_data);
+
+ // The IP version and IHL are stored in the same octet (4 bits each).
+ uint8_t ver_len = in_buf.readUint8();
+ // The most significant bits define IP version.
+ uint8_t ip_ver = ver_len >> 4;
+ EXPECT_EQ(4, ip_ver);
+ // The least significant bits define header length (in 32-bits chunks).
+ uint8_t ip_len = ver_len & 0x0F;
+ EXPECT_EQ(5, ip_len);
+
+ // Get Differentiated Services Codepoint and Explicit Congestion
+ // Notification field.
+ uint8_t dscp_ecn = in_buf.readUint8();
+ EXPECT_EQ(IPTOS_LOWDELAY, dscp_ecn);
+
+ // Total length of IP packet. Includes UDP header and payload.
+ uint16_t total_len = in_buf.readUint16();
+ EXPECT_EQ(28 + pkt->getBuffer().getLength(), total_len);
+
+ // Identification field.
+ uint16_t ident = in_buf.readUint16();
+ EXPECT_EQ(0, ident);
+
+ // Fragmentation.
+ uint16_t fragment = in_buf.readUint16();
+ // Setting second most significant bit means no fragmentation.
+ EXPECT_EQ(0x4000, fragment);
+
+ // Get TTL
+ uint8_t ttl = in_buf.readUint8();
+ // Expect non-zero TTL.
+ EXPECT_GE(ttl, 1);
+
+ // Protocol type is UDP.
+ uint8_t proto = in_buf.readUint8();
+ EXPECT_EQ(IPPROTO_UDP, proto);
+
+ // Check that the checksum is correct. The reference checksum value
+ // has been calculated manually.
+ uint16_t ip_checksum = in_buf.readUint16();
+ EXPECT_EQ(0x755c, ip_checksum);
+
+ // Validate source address.
+ // Initializing it to IPv6 address guarantees that it is not initialized
+ // to the value that we expect to be read from a header since the value
+ // read from a header will be IPv4.
+ IOAddress src_addr("::1");
+ // Read src address as an array of bytes because it is easely convertible
+ // to IOAddress object.
+ uint8_t src_addr_data[4];
+ ASSERT_NO_THROW(
+ in_buf.readData(src_addr_data, 4);
+ src_addr = IOAddress::fromBytes(AF_INET, src_addr_data);
+ );
+ EXPECT_EQ(IOAddress("192.0.2.1").toText(), src_addr.toText());
+
+ // Validate destination address.
+ IOAddress dest_addr("::1");
+ uint8_t dest_addr_data[4];
+ ASSERT_NO_THROW(
+ in_buf.readData(dest_addr_data, 4);
+ dest_addr = IOAddress::fromBytes(AF_INET, dest_addr_data);
+ );
+ EXPECT_EQ(IOAddress("192.0.2.111").toText(), dest_addr.toText());
+
+ // UDP header starts here.
+
+ // Check source port.
+ uint16_t src_port = in_buf.readUint16();
+ EXPECT_EQ(pkt->getLocalPort(), src_port);
+
+ // Check destination port.
+ uint16_t dest_port = in_buf.readUint16();
+ EXPECT_EQ(pkt->getRemotePort(), dest_port);
+
+ // UDP header and data length.
+ uint16_t udp_len = in_buf.readUint16();
+ EXPECT_EQ(8 + pkt->getBuffer().getLength(), udp_len);
+
+ // Verify UDP checksum. The reference checksum has been calculated manually.
+ uint16_t udp_checksum = in_buf.readUint16();
+ EXPECT_EQ(0x8817, udp_checksum);
+}
+
+} // anonymous namespace
More information about the bind10-changes
mailing list