Browse Source

[2902] Implemented function which decodes Ethernet frame header.

Marcin Siodelski 12 years ago
parent
commit
35b62ac905

+ 37 - 2
src/lib/dhcp/protocol_util.cc

@@ -12,15 +12,50 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-#include <protocol_util.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::util;
+
 namespace isc {
 namespace dhcp {
 
+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) {
+        isc_throw(InvalidPacketHeader, "size of ethernet header in received "
+                  << "packet is invalid, expected at least 14 bytes, received "
+                  << buf.getLength() - buf.getPosition() << " bytes");
+    }
+    // Packet object must not be NULL. We want to output some values
+    // to this object.
+    if (!pkt) {
+        isc_throw(BadValue, "NULL packet object provided when parsing ethernet"
+                  " frame header");
+    }
+
+    // The size of the single address is always lower then the size of
+    // the header that holds this address. Otherwise, it is a programming
+    // error that we want to detect in the compilation time.
+    BOOST_STATIC_ASSERT(ETHERNET_HEADER_LEN > HWAddr::ETHERNET_HWADDR_LEN);
+
+    // Skip destination address.
+    buf.setPosition(HWAddr::ETHERNET_HWADDR_LEN);
+    // Read the source HW address.
+    std::vector<uint8_t> dest_addr;
+    buf.readVector(dest_addr, HWAddr::ETHERNET_HWADDR_LEN);
+    // Set the address we have read.
+    pkt->setHWAddr(HWTYPE_ETHERNET, HWAddr::ETHERNET_HWADDR_LEN, dest_addr);
+    // Move the buffer read pointer to the end of the Ethernet frame header.
+    buf.setPosition(ETHERNET_HEADER_LEN);
+}
+
 void
 writeEthernetHeader(const uint8_t* src_hw_addr, const uint8_t* dest_hw_addr,
-                    util::OutputBuffer& out_buf) {
+                    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);

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

@@ -23,6 +23,34 @@
 namespace isc {
 namespace dhcp {
 
+/// @brief Exception thrown when error occured during parsing packet's headers.
+///
+/// This exception is thrown when parsing link, Internet or Transport layer
+/// header has failed.
+class InvalidPacketHeader : public Exception {
+public:
+    InvalidPacketHeader(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) { };
+};
+
+/// Size of the Ethernet frame header.
+static const size_t ETHERNET_HEADER_LEN = 14;
+
+/// @brief Decode the Ethernet header.
+///
+/// This function reads Ethernet frame header from the provided
+/// buffer at the current read position. The source HW address
+/// is read from the header and assigned as client address in
+/// a pkt object. The buffer read pointer is set to the end
+/// of the Ethernet frame header if read was successful.
+///
+/// @param buf input buffer holding header to be parsed.
+/// @param [out] pkt packet object receiving HW source address read from header.
+///
+/// @throw InvalidPacketHeader if packet header is truncated
+/// @throw BadValue if pkt object is NULL.
+void decodeEthernetHeader(util::InputBuffer& buf, Pkt4Ptr& pkt);
+
 /// @brief Writes ethernet frame header into a buffer.
 ///
 /// @param src_hw_addr source HW address.

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

@@ -14,6 +14,8 @@
 
 #include <config.h>
 #include <asiolink/io_address.h>
+#include <dhcp/dhcp6.h>
+#include <dhcp/hwaddr.h>
 #include <dhcp/protocol_util.h>
 #include <util/buffer.h>
 
@@ -63,6 +65,65 @@ TEST(ProtocolUtilTest, checksum) {
     EXPECT_EQ(0xb1e4, chksum);
 }
 
+// The purpose of this test is to verify that the Ethernet frame header
+// can be decoded correctly. In particular it verifies that the source
+// HW address can be extracted from it.
+TEST(ProtocolUtilTest, decodeEthernetHeader) {
+    // 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
+    };
+
+    // Prepare a buffer holding Ethernet frame header and 4 bytes of
+    // dummy data.
+    OutputBuffer buf(1);
+    buf.writeData(dest_hw_addr, sizeof(src_hw_addr));
+    buf.writeData(src_hw_addr, sizeof(src_hw_addr));
+    buf.writeUint16(0x800);
+    // Append dummy data. We will later check that this data is not
+    // removed or corrupted when reading the ethernet header.
+    buf.writeUint32(0x01020304);
+
+    // Create a buffer with truncated ethernet frame header..
+    InputBuffer in_buf_truncated(buf.getData(), buf.getLength() - 6);
+    // But provide valid packet object to make sure that the function
+    // under test does not throw due to NULL pointer packet.
+    Pkt4Ptr pkt(new Pkt4(DHCPDISCOVER, 0));
+    // Function should throw because header data is truncated.
+    EXPECT_THROW(decodeEthernetHeader(in_buf_truncated, pkt),
+                 InvalidPacketHeader);
+
+    // Get not truncated buffer.
+    InputBuffer in_buf(buf.getData(), buf.getLength());
+    // But provide NULL packet object instead.
+    pkt.reset();
+    // It should throw again but a different exception.
+    EXPECT_THROW(decodeEthernetHeader(in_buf, pkt),
+                 BadValue);
+    // Now provide, correct data.
+    pkt.reset(new Pkt4(DHCPDISCOVER, 0));
+    // It should not throw now.
+    ASSERT_NO_THROW(decodeEthernetHeader(in_buf, pkt));
+    // Verify that the HW address of the source has been initialized.
+    HWAddrPtr hwaddr = pkt->getHWAddr();
+    ASSERT_TRUE(hwaddr);
+    // And that it is correct.
+    EXPECT_EQ(HWTYPE_ETHERNET, hwaddr->htype_);
+    ASSERT_EQ(sizeof(dest_hw_addr), hwaddr->hwaddr_.size());
+    EXPECT_TRUE(std::equal(src_hw_addr, src_hw_addr + sizeof(src_hw_addr),
+                           hwaddr->hwaddr_.begin()));
+    // The entire ethernet packet header should have been read. This means
+    // that the internal buffer pointer should now point to its tail.
+    ASSERT_EQ(ETHERNET_HEADER_LEN, in_buf.getPosition());
+    // And the dummy data should be still readable and correct.
+    uint32_t dummy_data = in_buf.readUint32();
+    EXPECT_EQ(0x01020304, dummy_data);
+}
+
 /// The purpose of this test is to verify that the ethernet
 /// header is correctly constructed from the destination and
 /// hardware addresses.