In this section we'll have a look at how the packet sending mechanism works on libtins.
The PacketSender
class is the responsible for sending packets
over the network. Internally, it stores raw sockets for different socket
layers(2 and 3 for example).
When calling PacketSender::send(PDU&)
the PDU
parameter
is serialized into a an array of bytes and sent through the appropriate
socket.
Sending network layer PDU
s, such as IP
and
IPv6
is quite intuitive:
Note that no source address was specified in the IP
constructor.
This uses by default the address 0.0.0.0. However, when sending network
layer PDU
s, if the source address is 0.0.0.0, the PDU
will
perform a lookup on the routing table to find out which should be the
source address and automatically sets it. This is done by the network
driver already, but some transport layer protocols such as TCP
,
require this address when calculating the checksum, so this must be
done by the library as well.
When sending link layer PDU
s, such as EthernetII
, there
is one more thing that should be kept in mind. In this case, the packet
must be sent through a specific network interface. You can specify this
when sending it:
That will send the packet through the eth0 interface.
It is quite common to use the same network interface to send several
packets. PacketSender
contains a default interface, in which
link layer PDU
's are sent when using the
PacketSender::send(PDU&)
overload:
Note that by default this interface is invalid, so you need to set it
before sending link layer PDU
s as shown above:
So far we've seen how to send packets, but what if you're expecting a response to that packet? Let's take as an example an ARP request. After it's sent, you'll most likely want to receive a response.
This could be achieved by sniffing while sending the packet, checking each sniffed packet until the response is found. However, in order to match the packet response, it would be necessary to perform several protocol dependent comparisons. In the case of an ARP response, it would be fairly straightforward. However, other protocols require checking destination and source address and ports, identifier numbers, etc.
Fortunately, a sending and receiving mechanism has already been included
in the library. This can be achieved by using PacketSender::send_recv
,
which provides two overloads:
The NetworkInterface
parameter serves the same purpose as in
PacketSender::send
.
Let's see how it could be used to perform an ARP
request and
receiving its response:
Note that inside PacketSender::send_recv
, packets read from the
socket will be matched against the sent one until a valid one is found.
Just as a side note, hardware addresses can be resolved much easily,
using Utils::resolve_hwaddr
:
Going back to the sending and receiving mechanism, we could also use it to determine whether a TCP port is open:
As a last example, the following code resolves a domain name using
PacketSender::send_recv
:
In the examples above, some of the protocols used, such as IP
and TCP
contain a checksum field. This checksum must be
calculated everytime the packet is sent. libtins does this
automatically: everytime a packet is serialized(this happens inside
PacketSender::send
), the checksums are calculated; so there is
no need for you to worry about them.
One thing that should be noticed is that the raw socket opening
operation is not thread-safe, so in case you're having multiple
writers, you should explicitly open the required sockets yourself(this
can be done through PacketSender::open_l2_socket
and
PacketSender::open_l3_socket
). Otherwise, the sockets will be
open when needed.