Browse Source

[2902] Implemented a function decoding IP and UDP header.

Marcin Siodelski 12 years ago
parent
commit
decee38f00

+ 47 - 1
src/lib/dhcp/protocol_util.cc

@@ -12,17 +12,20 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
+#include <asiolink/io_address.h>
 #include <dhcp/dhcp6.h> // defines HWTYPE_ETHERNET
 #include <dhcp/protocol_util.h>
 #include <boost/static_assert.hpp>
 #include <netinet/ip.h>
 
+using namespace isc::asiolink;
 using namespace isc::util;
 
 namespace isc {
 namespace dhcp {
 
-void decodeEthernetHeader(InputBuffer& buf, Pkt4Ptr& pkt) {
+void
+decodeEthernetHeader(InputBuffer& buf, Pkt4Ptr& pkt) {
     // The size of the buffer to be parsed must not be lower
     // then the size of the Ethernet frame header.
     if (buf.getLength() - buf.getPosition() < ETHERNET_HEADER_LEN) {
@@ -54,6 +57,49 @@ void decodeEthernetHeader(InputBuffer& buf, Pkt4Ptr& pkt) {
 }
 
 void
+decodeIpUdpHeader(InputBuffer& buf, Pkt4Ptr& pkt) {
+    // The size of the buffer must be at least equal to the minimal size of
+    // the IPv4 packet header plus UDP header length.
+    if (buf.getLength() - buf.getPosition() < MIN_IP_HEADER_LEN + UDP_HEADER_LEN) {
+        isc_throw(InvalidPacketHeader, "the total size of the IP and UDP headers in "
+                  << "received packet is invalid, expected at least "
+                  << MIN_IP_HEADER_LEN + UDP_HEADER_LEN
+                  << " bytes, received " << buf.getLength() - buf.getPosition()
+                  << " bytes");
+    }
+
+    // Packet object must not be NULL.
+    if (!pkt) {
+        isc_throw(BadValue, "NULL packet object provided when parsing IP and UDP"
+                  " packet headers");
+    }
+
+    BOOST_STATIC_ASSERT(IP_SRC_ADDR_OFFSET < MIN_IP_HEADER_LEN);
+
+    // Read IP header length (mask most significant bits as they indicate IP version).
+    uint8_t ip_len = buf.readUint8() & 0xF;
+    // IP length is the number of 4 byte chunks that construct IPv4 header.
+    // It must not be lower than 5 because first 20 bytes are fixed.
+    if (ip_len < 5) {
+        ip_len = 5;
+    }
+
+    // Seek to the position of source IP address.
+    buf.setPosition(IP_SRC_ADDR_OFFSET);
+    // Read source address.
+    pkt->setRemoteAddr(IOAddress(buf.readUint32()));
+    // Read destination address.
+    pkt->setLocalAddr(IOAddress(buf.readUint32()));
+    // Read source port from UDP header.
+    pkt->setRemotePort(buf.readUint16());
+    // Read destination port from UDP header.
+    pkt->setLocalPort(buf.readUint16());
+
+    // Set the pointer position to the tail of UDP header.
+    buf.setPosition(ip_len * 4 + UDP_HEADER_LEN);
+}
+
+void
 writeEthernetHeader(const uint8_t* src_hw_addr, const uint8_t* dest_hw_addr,
                     OutputBuffer& out_buf) {
     // Write destination and source address.

+ 21 - 0
src/lib/dhcp/protocol_util.h

@@ -35,6 +35,12 @@ public:
 
 /// Size of the Ethernet frame header.
 static const size_t ETHERNET_HEADER_LEN = 14;
+/// Minimal IPv4 header length.
+static const size_t MIN_IP_HEADER_LEN = 20;
+/// UDP header length.
+static const size_t UDP_HEADER_LEN = 8;
+/// Offset of source address in the IPv4 header.
+static const size_t IP_SRC_ADDR_OFFSET = 12;
 
 /// @brief Decode the Ethernet header.
 ///
@@ -51,6 +57,21 @@ static const size_t ETHERNET_HEADER_LEN = 14;
 /// @throw BadValue if pkt object is NULL.
 void decodeEthernetHeader(util::InputBuffer& buf, Pkt4Ptr& pkt);
 
+/// @brief Decode IP and UDP header.
+///
+/// This function reads IP and UDP headers from the provided buffer
+/// at the current read position. The source and destination IP
+/// addresses and ports and read from these headers and stored in
+/// the appropriate members of pkt object.
+///
+/// @param buf input buffer holding headers to be parsed.
+/// @param [out] pkt packet object where IP addresses and ports
+/// are stored.
+///
+/// @throw InvalidPacketHeader if packet header is truncated
+/// @throw BadValue if pkt object is NULL.
+void decodeIpUdpHeader(util::InputBuffer& buf, Pkt4Ptr& pkt);
+
 /// @brief Writes ethernet frame header into a buffer.
 ///
 /// @param src_hw_addr source HW address.

+ 64 - 0
src/lib/dhcp/tests/protocol_util_unittest.cc

@@ -124,6 +124,70 @@ TEST(ProtocolUtilTest, decodeEthernetHeader) {
     EXPECT_EQ(0x01020304, dummy_data);
 }
 
+/// The purpose of this test is to verify that the IP and UDP header
+/// is decoded correctly and appropriate values of IP addresses and
+/// ports are assigned to a Pkt4 object.
+TEST(ProtocolUtilTest, decodeIpUdpHeader) {
+    // IPv4 header to be parsed.
+    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
+        IPPROTO_UDP,               // Protocol
+        0x00, 0x00,                // Checksum (reset to 0x00).
+        0xc0, 0x00, 0x02, 0x63,    // Source IP address.
+        0xc0, 0x00, 0x02, 0x0c,    // Destination IP address.
+        0x27, 0x54,                // Source port
+        0x27, 0x53,                // Destination port
+        0x00, 0x08,                // UDP length
+        0x00, 0x00                 // Checksum
+    };
+
+    // Write header data to the buffer.
+    OutputBuffer buf(1);
+    buf.writeData(hdr, sizeof(hdr));
+    // Append some dummy data.
+    buf.writeUint32(0x01020304);
+
+    // Create an input buffer holding truncated headers.
+    InputBuffer in_buf_truncated(buf.getData(), buf.getLength() - 10);
+    // Create non NULL Pkt4 object to make sure that the function under
+    // test does not throw due to invalid Pkt4 object.
+    Pkt4Ptr pkt(new Pkt4(DHCPDISCOVER, 0));
+    // Function should throw because buffer holds truncated data.
+    EXPECT_THROW(decodeIpUdpHeader(in_buf_truncated, pkt), InvalidPacketHeader);
+
+    // Create a valid input buffer (not truncated).
+    InputBuffer in_buf(buf.getData(), buf.getLength());
+    // Set NULL Pkt4 object to verify that function under test will
+    // return exception as expected.
+    pkt.reset();
+    // And check whether it throws exception.
+    EXPECT_THROW(decodeIpUdpHeader(in_buf, pkt), BadValue);
+
+    // Now, let's provide valid arguments and make sure it doesn't throw.
+    pkt.reset(new Pkt4(DHCPDISCOVER, 0));
+    ASSERT_TRUE(pkt);
+    EXPECT_NO_THROW(decodeIpUdpHeader(in_buf, pkt));
+
+    // Verify the source address and port.
+    EXPECT_EQ("192.0.2.99", pkt->getRemoteAddr().toText());
+    EXPECT_EQ(10068, pkt->getRemotePort());
+
+    // Verify the destination address and port.
+    EXPECT_EQ("192.0.2.12", pkt->getLocalAddr().toText());
+    EXPECT_EQ(10067, pkt->getLocalPort());
+
+    // Verify that the dummy data has not been corrupted and that the
+    // internal read pointer has been moved to the tail of the UDP
+    // header.
+    ASSERT_EQ(MIN_IP_HEADER_LEN + UDP_HEADER_LEN, in_buf.getPosition());
+    EXPECT_EQ(0x01020304, in_buf.readUint32());
+}
+
 /// The purpose of this test is to verify that the ethernet
 /// header is correctly constructed from the destination and
 /// hardware addresses.