In this section we'll have a deeper look at how to perform sniffing using libtins.
Sniffing is done through the
Sniffer class. This class accepts
a libpcap string filter, and lets you sniff on some network device,
interpreting the packets sent through it, and giving you
objects so you can easily work with them.
Once you've set the filter, there are two functions which allow to
retrieve the sniffed packets. One of them is
This member function lets you retrieve a packet using the provided
Since version 3.2, there's a class that represents the
different parameters that can be given to the sniffer in order to affect
the sniffing sessions. They are all wrappers over the different
libpcap functions, such as pcap_setfilter,
pcap_set_promisc, etc. It's an improvement over the many
parameters that the other
Sniffer constructors' take.
For example, if you wanted to capture packets on port 80, sniff on promiscuous mode and set a snapshot length of 400 bytes, you would do it this way:
Note: if you notice sniffed packets come in bursts or there's
a delay in their capture (e.g. 1 second), this is very likely due to
libpcap >= v1.5 using a buffered mode by default.
If you want to get packets as fast as possible, make sure to use
immediate mode by using
There is another way to extract packets from a
object, apart from
Sniffer::next_packet. It's very common
that you want to sniff lots packets until some certain condition is
met. In that case its better to use
This method takes a template functor as an argument, which must define an operator with one of the following signatures:
The call to
Sniffer::sniff_loop will make the sniffer start
processing packets. The functor will be called using each processed
packet as its argument. If at some point, you want to stop sniffing,
then your functor should return
false. Otherwise return
true and the
Sniffer object will keep looping.
The functor object will be copy constructed, so it must implement copy
semantics. There is a helper template function which takes a pointer
to an object of a template parameter type, and a member function,
and returns a
HandlerProxy. That object implements the
required operator, in which it forwards the call to the member function pointer
provided, using the object pointer given:
As you can see, sniffing using
Sniffer::sniff_loop can not
only be an easy way to process several packets, but also can make your
code a lot tidier when using classes.
Now the interesting part. In the above example we know we are sniffing
IP PDUs sent by the ip address 192.168.0.100, but our
function takes a
PDU&. We want to search the
stored inside the parameter(which will probably be of type
Luckily for us, you can ask a
PDU to search for a certain
PDU type inside its whole stack of PDUs(including
itself), and return a reference to it. If no such
found in the packet, a
pdu_not_found exception is thrown:
Another thing that makes the loop-sniffing mechanism better
than fetching packets one by one, is exception handling.
Sniffer::sniff_loop catches both
malformed_packet exceptions thrown in the functor body. This
means you can use
PDU::rfind_pdu and don't even care if such
PDU is not found, since the exception will be caught by
Sniffer, and the sniffing session will continue.
Note to Windows users: you may want to check out the sniffing on Windows extra section of this tutorial to make sure you know what you need to before starting a packet capture on that platform.
There is yet another way to retrieve packets from a Sniffer object.
This class defines two methods,
return forward iterators. These can be used to retrieve packets while
they're being sniffed:
If you require to store a
PDU along with the timestamp object,
then you should use the
Timestamp, can be copyed and moved.
Let's see an example in which we'll store 10 packets read from the wire into a vector:
As you may have noticed
Packet objects can also be used along
Packets can also be accepted on the functor object used
Sniffer::sniff_loop, but only when you are compiling
in C++11 mode.
Reading files in pcap format is very straightforward. The
FileSniffer class takes the name of the file to be opened as
argument, and lets you process the packets in it. Both
FileSniffer inherit from
BaseSniffer, which is
the class that actually implements
Therefore, we can use the
FileSniffer class in the same way
Sniffer in the examples above:
Now that we've seen the ways in which you can read pcap files and sniff from network interfaces, we'll have a look at how packet interpretation is performed.
Every time a packet is read from one of those sources, an object of that
source's link layer type is created(
etc). Each of these types of object detects which is the type of the next
PDU based on its internal flags, creates it, adds it as its
child, and propagates the same action.
This action is performed by every instantiated
PDU, except for
transport-layer protocols. This means that, for example, if a
packet is sniffed off an ethernet interface, you'll get the following
You can then interpret that
DNS packet constructing a
object using that
The same mechanism should be used for other protocols such as
In case you're wondering why application-layer protocols aren't interpreted
automatically by transport-layer
PDUs, the reason is efficiency.
Transport layer protocols, such as
DNS, require much more processing
in order to parse them than lower layer protocols. In addition, some
applications might not even require to use those protocols, so making them
pay for that extra processing is undesirable.