Introduction

libtins provides support for several network protocols. The documentation contains information about each class, method and function present in the library. However, you might not want to read the whole documentation just to learn how to craft a simple TCP packet.

In this section, we'll have a look at how some of the protocols you will most likely see in your home network are implemented in the library.

Ethernet II

The Ethernet II protocol is represented by the EthernetII class, and is actually very simple. It just contains methods to get and set the destination and source addresses, and the payload type. It also contains a constructor which lets you, optionally, indicate both of the addresses:

// Both addresses are 00:00:00:00:00:00
EthernetII eth;
eth.dst_addr("01:02:03:04:05:06");
eth.src_addr("00:01:02:03:04:05");

// Same as above, just shorter
EthernetII eth2("01:02:03:04:05:06", "00:01:02:03:04:05");

IP

The IP class contains much more methods, both for accessing the protocol's fields and for adding and retrieving the stored options. In this example, we'll modify some of those fields:

// Both addresses are 0.0.0.0
IP ip;
ip.dst_addr("192.168.0.100");
ip.src_addr("192.168.0.50");

// Same as above, just shorter
IP ip2("192.168.0.100", "192.168.0.50");
// Set the time-to-live attribute
ip2.ttl(10);
// Set the type-of-service attribute
ip2.tos(3);

IP is one of many protocols that support TLV (type-length-value) encoded options. This means that each option stored in the protocol contains a field that indicates its type, another one that holds its length, and a third one that carries the actual data.

Every class that contains options, such as IP, TCP and DHCP, among others, behave the same way. For any type T that contains options, T::option is the type in which the option is actually stored. Each of this protocols provides two member functions:

  • void T::add_option(const T::option&)
    Which adds an option to that PDU.
  • const T::option* T::search_option(T::option::option_type)
    Which finds and option.

Using these member functions, it is necessary to know both the length of the fields that compose each option type and their endianness. Since this is very cumbersome, each of these protocols provides a getter and a setter for every valid option. The getter will always throw a option_not_found exception if the option is not present in the PDU.

In the case of IP, these are some of the supported options getters/setters:

IP ip;
// Sets the Stream Identifier option.
ip.stream_identifier(165);
// Sets the Record Route option.
ip.record_route(
    { // Constructing a record_route_type object
        2, // pointer
        { "192.168.0.1", "192.168.0.2" } // routes
    }
);

// Retrieve the Record Route option.
IP::record_route_type routes = ip.record_route();
// Echo
std::cout << static_cast<int>(routes.pointer) << std::endl;
std::copy(
    routes.routes.begin(),
    routes.routes.end(),
    std::ostream_iterator<IP::address_type>(std::cout, "\n")
);

// This will throw an option_not_found exception.
auto x = ip.security();

TCP

TCP contains several option types as well, so everything mentioned above about them is still valid. Let's have a look at how to craft some basic TCP frames:

// Both source and destination ports are 0.
TCP tcp;
tcp.dport(22);
tcp.sport(22334);

// Same as above
TCP tcp2(22, 22334);

// Set the sequence number
tcp.seq(0x9283);
// Set the acknowledge number
tcp.ack_seq(0x9283);
// Set the SYN flag
tcp.set_flag(TCP::SYN, 1);
// This will be available as of libtins 1.2
tcp.flags(TCP::SYN | TCP::ACK);

// Get the SYN flag
auto s = tcp.get_flag(TCP::SYN);
// This will be available as of libtins 1.2
bool is_syn_ack = (tcp.flags() == (TCP::SYN | TCP::ACK));

// Set some options
tcp.sack_permitted();
if (tcp.has_sack_permitted()) {
    // whatever
}
tcp.altchecksum(TCP::CHK_8FLETCHER);
Previous part: TCP streams
Next part: IEEE 802.11