IPv4 network packet: C++98/STL parsing library

KS implementation details

License: CC0-1.0
Minimal Kaitai Struct required: 0.8

References

This page hosts a formal specification of IPv4 network packet using Kaitai Struct. This specification can be automatically translated into a variety of programming languages to get a parsing library.

Usage

Runtime library

All parsing code for C++98/STL generated by Kaitai Struct depends on the C++/STL runtime library. You have to install it before you can parse data.

For C++, the easiest way is to clone the runtime library sources and build them along with your project.

Code

Using Kaitai Struct in C++/STL usually consists of 3 steps.

  1. We need to create an STL input stream (std::istream). One can open local file for that, or use existing std::string or char* buffer.
    #include <fstream>
    
    std::ifstream is("path/to/local/file.bin", std::ifstream::binary);
    
    #include <sstream>
    
    std::istringstream is(str);
    
    #include <sstream>
    
    const char buf[] = { ... };
    std::string str(buf, sizeof buf);
    std::istringstream is(str);
    
  2. We need to wrap our input stream into Kaitai stream:
    #include "kaitai/kaitaistream.h"
    
    kaitai::kstream ks(&is);
    
  3. And finally, we can invoke the parsing:
    ipv4_packet_t data(&ks);
    

After that, one can get various attributes from the structure by invoking getter methods like:

data.b1() // => get b1

C++98/STL source code to parse IPv4 network packet

ipv4_packet.h

#ifndef IPV4_PACKET_H_
#define IPV4_PACKET_H_

// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild

#include "kaitai/kaitaistruct.h"
#include <stdint.h>
#include "protocol_body.h"
#include <vector>

#if KAITAI_STRUCT_VERSION < 9000L
#error "Incompatible Kaitai Struct C++/STL API: version 0.9 or later is required"
#endif
class protocol_body_t;

class ipv4_packet_t : public kaitai::kstruct {

public:
    class ipv4_options_t;
    class ipv4_option_t;

    ipv4_packet_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent = 0, ipv4_packet_t* p__root = 0);

private:
    void _read();
    void _clean_up();

public:
    ~ipv4_packet_t();

    class ipv4_options_t : public kaitai::kstruct {

    public:

        ipv4_options_t(kaitai::kstream* p__io, ipv4_packet_t* p__parent = 0, ipv4_packet_t* p__root = 0);

    private:
        void _read();
        void _clean_up();

    public:
        ~ipv4_options_t();

    private:
        std::vector<ipv4_option_t*>* m_entries;
        ipv4_packet_t* m__root;
        ipv4_packet_t* m__parent;

    public:
        std::vector<ipv4_option_t*>* entries() const { return m_entries; }
        ipv4_packet_t* _root() const { return m__root; }
        ipv4_packet_t* _parent() const { return m__parent; }
    };

    class ipv4_option_t : public kaitai::kstruct {

    public:

        ipv4_option_t(kaitai::kstream* p__io, ipv4_packet_t::ipv4_options_t* p__parent = 0, ipv4_packet_t* p__root = 0);

    private:
        void _read();
        void _clean_up();

    public:
        ~ipv4_option_t();

    private:
        bool f_copy;
        int32_t m_copy;

    public:
        int32_t copy();

    private:
        bool f_opt_class;
        int32_t m_opt_class;

    public:
        int32_t opt_class();

    private:
        bool f_number;
        int32_t m_number;

    public:
        int32_t number();

    private:
        uint8_t m_b1;
        uint8_t m_len;
        std::string m_body;
        ipv4_packet_t* m__root;
        ipv4_packet_t::ipv4_options_t* m__parent;

    public:
        uint8_t b1() const { return m_b1; }
        uint8_t len() const { return m_len; }
        std::string body() const { return m_body; }
        ipv4_packet_t* _root() const { return m__root; }
        ipv4_packet_t::ipv4_options_t* _parent() const { return m__parent; }
    };

private:
    bool f_version;
    int32_t m_version;

public:
    int32_t version();

private:
    bool f_ihl;
    int32_t m_ihl;

public:
    int32_t ihl();

private:
    bool f_ihl_bytes;
    int32_t m_ihl_bytes;

public:
    int32_t ihl_bytes();

private:
    uint8_t m_b1;
    uint8_t m_b2;
    uint16_t m_total_length;
    uint16_t m_identification;
    uint16_t m_b67;
    uint8_t m_ttl;
    uint8_t m_protocol;
    uint16_t m_header_checksum;
    std::string m_src_ip_addr;
    std::string m_dst_ip_addr;
    ipv4_options_t* m_options;
    protocol_body_t* m_body;
    ipv4_packet_t* m__root;
    kaitai::kstruct* m__parent;
    std::string m__raw_options;
    kaitai::kstream* m__io__raw_options;
    std::string m__raw_body;
    kaitai::kstream* m__io__raw_body;

public:
    uint8_t b1() const { return m_b1; }
    uint8_t b2() const { return m_b2; }
    uint16_t total_length() const { return m_total_length; }
    uint16_t identification() const { return m_identification; }
    uint16_t b67() const { return m_b67; }
    uint8_t ttl() const { return m_ttl; }
    uint8_t protocol() const { return m_protocol; }
    uint16_t header_checksum() const { return m_header_checksum; }
    std::string src_ip_addr() const { return m_src_ip_addr; }
    std::string dst_ip_addr() const { return m_dst_ip_addr; }
    ipv4_options_t* options() const { return m_options; }
    protocol_body_t* body() const { return m_body; }
    ipv4_packet_t* _root() const { return m__root; }
    kaitai::kstruct* _parent() const { return m__parent; }
    std::string _raw_options() const { return m__raw_options; }
    kaitai::kstream* _io__raw_options() const { return m__io__raw_options; }
    std::string _raw_body() const { return m__raw_body; }
    kaitai::kstream* _io__raw_body() const { return m__io__raw_body; }
};

#endif  // IPV4_PACKET_H_

ipv4_packet.cpp

// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild

#include "ipv4_packet.h"

ipv4_packet_t::ipv4_packet_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, ipv4_packet_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = this;
    m_options = 0;
    m__io__raw_options = 0;
    m_body = 0;
    m__io__raw_body = 0;
    f_version = false;
    f_ihl = false;
    f_ihl_bytes = false;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void ipv4_packet_t::_read() {
    m_b1 = m__io->read_u1();
    m_b2 = m__io->read_u1();
    m_total_length = m__io->read_u2be();
    m_identification = m__io->read_u2be();
    m_b67 = m__io->read_u2be();
    m_ttl = m__io->read_u1();
    m_protocol = m__io->read_u1();
    m_header_checksum = m__io->read_u2be();
    m_src_ip_addr = m__io->read_bytes(4);
    m_dst_ip_addr = m__io->read_bytes(4);
    m__raw_options = m__io->read_bytes((ihl_bytes() - 20));
    m__io__raw_options = new kaitai::kstream(m__raw_options);
    m_options = new ipv4_options_t(m__io__raw_options, this, m__root);
    m__raw_body = m__io->read_bytes((total_length() - ihl_bytes()));
    m__io__raw_body = new kaitai::kstream(m__raw_body);
    m_body = new protocol_body_t(protocol(), m__io__raw_body);
}

ipv4_packet_t::~ipv4_packet_t() {
    _clean_up();
}

void ipv4_packet_t::_clean_up() {
    if (m__io__raw_options) {
        delete m__io__raw_options; m__io__raw_options = 0;
    }
    if (m_options) {
        delete m_options; m_options = 0;
    }
    if (m__io__raw_body) {
        delete m__io__raw_body; m__io__raw_body = 0;
    }
    if (m_body) {
        delete m_body; m_body = 0;
    }
}

ipv4_packet_t::ipv4_options_t::ipv4_options_t(kaitai::kstream* p__io, ipv4_packet_t* p__parent, ipv4_packet_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m_entries = 0;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void ipv4_packet_t::ipv4_options_t::_read() {
    m_entries = new std::vector<ipv4_option_t*>();
    {
        int i = 0;
        while (!m__io->is_eof()) {
            m_entries->push_back(new ipv4_option_t(m__io, this, m__root));
            i++;
        }
    }
}

ipv4_packet_t::ipv4_options_t::~ipv4_options_t() {
    _clean_up();
}

void ipv4_packet_t::ipv4_options_t::_clean_up() {
    if (m_entries) {
        for (std::vector<ipv4_option_t*>::iterator it = m_entries->begin(); it != m_entries->end(); ++it) {
            delete *it;
        }
        delete m_entries; m_entries = 0;
    }
}

ipv4_packet_t::ipv4_option_t::ipv4_option_t(kaitai::kstream* p__io, ipv4_packet_t::ipv4_options_t* p__parent, ipv4_packet_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    f_copy = false;
    f_opt_class = false;
    f_number = false;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void ipv4_packet_t::ipv4_option_t::_read() {
    m_b1 = m__io->read_u1();
    m_len = m__io->read_u1();
    m_body = m__io->read_bytes(((len() > 2) ? ((len() - 2)) : (0)));
}

ipv4_packet_t::ipv4_option_t::~ipv4_option_t() {
    _clean_up();
}

void ipv4_packet_t::ipv4_option_t::_clean_up() {
}

int32_t ipv4_packet_t::ipv4_option_t::copy() {
    if (f_copy)
        return m_copy;
    m_copy = ((b1() & 128) >> 7);
    f_copy = true;
    return m_copy;
}

int32_t ipv4_packet_t::ipv4_option_t::opt_class() {
    if (f_opt_class)
        return m_opt_class;
    m_opt_class = ((b1() & 96) >> 5);
    f_opt_class = true;
    return m_opt_class;
}

int32_t ipv4_packet_t::ipv4_option_t::number() {
    if (f_number)
        return m_number;
    m_number = (b1() & 31);
    f_number = true;
    return m_number;
}

int32_t ipv4_packet_t::version() {
    if (f_version)
        return m_version;
    m_version = ((b1() & 240) >> 4);
    f_version = true;
    return m_version;
}

int32_t ipv4_packet_t::ihl() {
    if (f_ihl)
        return m_ihl;
    m_ihl = (b1() & 15);
    f_ihl = true;
    return m_ihl;
}

int32_t ipv4_packet_t::ihl_bytes() {
    if (f_ihl_bytes)
        return m_ihl_bytes;
    m_ihl_bytes = (ihl() * 4);
    f_ihl_bytes = true;
    return m_ihl_bytes;
}