From: Marcin Siodelski Date: Tue, 23 Apr 2013 16:54:24 +0000 (+0200) Subject: [2902] Implemented a function decoding IP and UDP header. X-Git-Tag: bind10-1.2.0beta1-release~440^2~31 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=decee38f00f3efac436fa899bf6160fff787e318;p=thirdparty%2Fkea.git [2902] Implemented a function decoding IP and UDP header. --- diff --git a/src/lib/dhcp/protocol_util.cc b/src/lib/dhcp/protocol_util.cc index 842c038e3d..63bb59dcae 100644 --- a/src/lib/dhcp/protocol_util.cc +++ b/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 #include // defines HWTYPE_ETHERNET #include #include #include +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) { @@ -53,6 +56,49 @@ void decodeEthernetHeader(InputBuffer& buf, Pkt4Ptr& pkt) { buf.setPosition(ETHERNET_HEADER_LEN); } +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) { diff --git a/src/lib/dhcp/protocol_util.h b/src/lib/dhcp/protocol_util.h index c34bba4081..00aa9b25bc 100644 --- a/src/lib/dhcp/protocol_util.h +++ b/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. diff --git a/src/lib/dhcp/tests/protocol_util_unittest.cc b/src/lib/dhcp/tests/protocol_util_unittest.cc index 3fe9a9f22e..d492787ed9 100644 --- a/src/lib/dhcp/tests/protocol_util_unittest.cc +++ b/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.