Programing With Lipbcap en
Programing With Lipbcap en
Difficulty
Since the first message was sent over the ARPANET in 1969,
computer networks have changed a great deal. Back then,
networks were small and problems were solved using simple
diagnostic tools. As these networks got more complex, the need
for management and troubleshooting increased.
N
owadays, computer networks are usu- systems, port knocking daemons, password
ally large and diverse systems that sniffers, ARP poisoners, tracerouters, etc.
communicate using a wide variety of First of all let's review how packet capture
protocols. This complexity created the need works in Ethernet-based networks. Every time
for more sophisticated tools to monitor and a network card receives an Ethernet frame
troubleshoot network traffic. Today, one of the it checks that its destination MAC address
critical tools in any network administrator tool- matches its own. If it does, it generates an inter-
box is the sniffer. rupt request. The routine in charge of handling
Sniffers, also known as packet analyzers, the interrupt is the system's network card driver.
are programs that have the ability to intercept The driver timestamps received data and cop-
the traffic that passes over a network. They are
very popular between network administrators
and the black hat community because they can What you will learn...
be used for both – good and evil. In this article
we will go through main principles of packet • The principles of packet capture
• How to capture packets using libpcap
capture and introduce libpcap, an open source
• Aspects to consider when writing a packet cap-
and portable packet capture library which is the
ture application
core of tools like tcpdump, dsniff, kismet, snort
or ettercap.
What you should know...
Packet Capture
Packet capture is the action of collecting data • The C programming language
as it travels over a network. Sniffers are the • The basics of networking and the OSI Refer-
best example of packet capture systems but ence Model
many other types of applications need to grab • How common protocols like Ethernet, TCP/IP
or ARP work
packets off a network card. Those include
network statistical tools, intrusion detection
ies it from the card buffer to a block through but, as we will see later, Python, Java, C# or Ruby. Libpcap
of memory in kernel space. Then, it they usually offer advanced filtering runs on most UNIX-like operating
determines which type of packet has capabilities. As packet capture may systems (Linux, Solaris, BSD, HP-
been received looking at the ether- involve security risks, most systems UX...). There is also a Windows ver-
type field of the Ethernet header and require administrator privileges in sion named Winpcap. Today, libpcap
passes it to the appropriate protocol order to use this feature. Figure 1 is maintained by the Tcpdump Group.
handler in the protocol stack. In most illustrates the capture process. Full documentation and source code
cases the frame will contain an IPv4 is available from the tcpdump's official
datagram so the IPv4 packet handler Libpcap site at http://www.tcpdump.org. (http:
will be called. This handler performs Libpcap is an open source library that //www.winpcap.org/ for Winpcap)
a number of check to ensure, for provides a high level interface to net-
example, that the packet is not cor- work packet capture systems. It was Our First Steps
rupt and that is actually destined created in 1994 by McCanne, Leres With Libpcap
for this host. If all tests are passed, and Jacobson – researchers at the Now that we know the basics of
the IP headers are removed and Lawrence Berkeley National Labora- packet capture let us write our own
the remainder is passed to the next tory from the University of California at sniffing application.
protocol handler (probably TCP or Berkeley as part of a research project The first thing we need is a net-
UDP). This process is repeated until to investigate and improve TCP and work interface to listen on. We can
the data gets to the application layer Internet gateway performance. either specify one explicitly or let
where it is processed by the user- Libpcap authors' main objective libpcap get one for us. The function
level application. was to create a platform-independ- char *pcap _ lookupdev(char *errbuf)
When we use a sniffer, packets ent API to eliminate the need for returns a pointer to a string contain-
go through the same process de- system-dependent packet capture ing the name of the first network
scribed above but with one differ- modules in each application, as vir- device that is suitable for packet cap-
ence: the network driver also sends tually every OS vendor implements ture. Usually this function is called
a copy of any received or transmitted its own capture mechanisms. when end-users do not specify any
packet to a part of the kernel called The libpcap API is designed to network interface. It is generally
the packet filter. Packet filters are be used from C and C++. However, a bad idea to use hard coded inter-
what makes packet capture pos- there are many wrappers that allow face names as they are usually not
sible. By default they let any packet its use from languages like Perl, portable across platforms.
The errbuf argument of pcap _ mented by libpcap take this param- Once we have the name of the
lookupdev() is a user supplied buffer eter. When allocating the buffer we network device we have to open
that the library uses to store an error have to be careful because it must be it. The function pcap _ t *pcap _
message in case something goes able to hold at least PCAP _ ERRBUF _ open _ live(const char *device, int
wrong. Many of the functions imple- SIZE bytes (currently defined as 256). snaplen, int promisc, int to _ ms,
char *errbuf) does that. It returns an
Listing 1. Structure pcap _pkthdr interface handler of type pcap _ t that
will be used later when calling the rest
struct pcap_pkthdr { of the functions provided by libpcap.
struct timeval ts; /* Timestamp of capture */
The first argument of pcap _
bpf_u_int32 caplen; /* Number of bytes that were stored */
open _ live() is a string containing
bpf_u_int32 len; /* Total length of the packet */
}; the name of the network interface
we want to open. The second one
Listing 2. Simple sniffer is the maximum number of bytes to
capture. Setting a low value for this
/* Simple Sniffer */
/* To compile: gcc simplesniffer.c -o simplesniffer -lpcap */
parameter might be useful in case
we are only interested in grabbing
#include <pcap.h> headers or when programming for
#include <string.h> embedded systems with important
#include <stdlib.h>
memory limitations. Typically the
#define MAXBYTES2CAPTURE 2048
maximum Ethernet frame size is
1518 bytes. However, other link
void processPacket(u_char *arg, const struct pcap_pkthdr* pkthdr, const types like FDDI or 802.11 have big-
u_char * packet){ ger limits. A value of 65535 should
be enough to hold any packet from
int i=0, *counter = (int *)arg;
any network.
printf("Packet Count: %d\n", ++(*counter)); The option to _ ms defines how
printf("Received Packet Size: %d\n", pkthdr->len); many milliseconds should the kernel
printf("Payload:\n"); wait before copying the captured
for (i=0; i<pkthdr->len; i++){
information from kernel space to
if ( isprint(packet[i]) )
user space. Changes of context are
printf("%c ", packet[i]); computationally expensive. If we are
else capturing a high volume of network
printf(". "); traffic it is better to let the kernel
group some packets before cross-
if( (i%16 == 0 && i!=0) || i==pkthdr->len-1 )
printf("\n");
ing the kernel-userspace bound-
} ary. A value of zero will cause the
return; read operations to wait forever until
} enough packets arrived to the net-
int main( ){
work interface. Libpcap documenta-
int i=0, count=0;
tion does not provide any suggestion
pcap_t *descr = NULL; for this value. To have an idea we
char errbuf[PCAP_ERRBUF_SIZE], *device=NULL; can examine what other sniffers do.
memset(errbuf,0,PCAP_ERRBUF_SIZE); Tcpdump uses a value of 1000, dsniff
uses 512 and ettercap distinguishes
/* Get the name of the first device suitable for capture */
device = pcap_lookupdev(errbuf);
between different operating systems
using 0 for Linux or OpenBSD and 10
printf("Opening device %s\n", device); for the rest.
The promisc flag decides wheth-
/* Open device in promiscuous mode */
er the network interface should be
descr = pcap_open_live(device, MAXBYTES2CAPTURE, 1, 512, errbuf);
put into promiscuous mode or not.
/* Loop forever & call processPacket() for every received packet*/ That is, whether the network card
pcap_loop(descr, -1, processPacket, (u_char *)&count); should accept packets that are not
destined to it or not. Specify 0 for
return 0;
non-promiscuous and any other
}
value for promiscuous mode. Note
that even if we tell libpcap to listen
in non-promiscuous mode, if the You are probably wondering if the struct pcap_pkthdr* pkthdr, const u_
interface was already in promiscu- function only returns an integer, where char * packet);
ous mode it may stay that way. We are the packets that were captured?
should not take for granted that we The answer is a bit tricky. pcap _ loop() The first argument is the user pointer
will not receive traffic destined for does not return those packets, instead, that we passed to pcap _ loop(), the
other hosts, instead, it is better to it calls a user-defined function every second one is a pointer to a structure
use the filtering capabilities that lib- time there is a packet ready to be read. that contains information about the
pcap provides, as we will see later. This way we can do our own process- captured packet. Listing 1 shows the
Once we have a network inter- ing in a separate function instead of definition of this structure.
face open for packet capture, we calling pcap _ next() in a loop and The caplen member has usually
have to actually tell pcap that we process everything inside. However the same value as len except the
want to start getting packets. For this there is a problem. If pcap _ loop() situation when the size of the cap-
we have some options: calls our function, how can we pass ar- tured packet exceeds the snaplen
guments to it? Do we have to use ugly specified in open _ pcap _ live().
• The function const u _ char globals? The answer is no, the libpcap The third alternative is to use int
*pcap _ next(pcap _ t *p, struct guys thought about this problem and pcap _ dispatch(pcap _ t *p, int cnt,
pcap _ pkthdr *h)takes the included a way to pass information to pcap _ handler callback, u _ char
pcap _ t handler returned by the callback function. This is the user *user), which is similar to pcap _
pcap _ open _ live, a pointer to argument. This pointer is passed in loop() but it also returns when the
a structure of type pcap _ pkthdr every call. The pointer is of type u _ to _ ms timeout specified in pcap _
and returns the first packet that char so we will have to cast it for our open _ live() elapses.
arrives to the network interface. own needs when calling pcap _ loop() Listing 1 provides an example
• The function int pcap _ and when using it inside the callback of a simple sniffer that prints the
loop(pcap _ t *p, int cnt, function. Our packet processing func- raw data that it captures. Note that
pcap _ handler callback, u _ char tion must have a specific prototype, header file pcap.h must be included.
*user) is used to collect packets otherwise pcap _ loop() wouldn't Error checks have been omitted for
and process them. It will not re- know how to use it. This is the way it clarity.
turn until cnt packets have been should be declared:
captured. A negative cnt value Once
will cause pcap _ loop() to return void function_name(u_char *userarg, We Capture a Packet
only in case of error. const When a packet is captured, the only
thing that our application has got is
a bunch of bytes. Usually, the net-
work card driver and the protocol
stack process that data for us but
when we are capturing packets from
our own application we do it at the
lowest level so we are the ones in
charge of making the data rational.
To do that there are some things that
should be taken into account.
types. However, it is the responsibil- defined. A complete list can be found for example, we capture a packet that
ity of the user to know the specific at http://www.iana.org/assignments/ is targeted to or comes from port 80
details of any particular technology. protocol-numbers. and it is payload is plain ASCII text, it
This means that we, as program- will probably be some kind of HTTP
mers, must know the exact format Application Layer Protocol traffic between a web browser and a
of the data link headers that the cap- Ok, so we have got the Ethernet web server. However, this is not exact
tured packets will have. In most ap- header, the IP header, the TCP science so we have to be very care-
plications we would just want to know header and now what?. Application ful when handling the TCP payload, it
the length of the header so we know layer protocols are a bit harder to may contain unexpected data.
where the IP datagram starts. distinguish. The TCP header does
Table 1 summarizes the most not provide any information about Malformed Packets
common data link types, their the payload it transports but TCP In Louis Amstrong's wonderful world
names in libpcap and the offsets port numbers can give as a clue. If, everything is beautiful and perfect
that should be applied to the start
Table 1. Common data link types
of the captured data to get the next
protocol header. Data Link Type Pcap Alias Offset (in bytes)
Probably the best way to handle Ethernet 10/100/1000 Mbs 14
DLT_EN10MB
the different link layer header sizes
is to implement a function that takes Wi-Fi 802.11 22
DLT_IEEE802_11
a pcap _ t structure and returns the
offset that should be used to get the FDDI( Fiber Distributed Data 21
Interface) DLT_FFDI
network layer headers. Dsniff takes
this approach. Have a look at func- PPPoE (PPP over Ethernet) 14 (Ethernet) + 6
DLT_PPP_ETHER
tion pcap _ dloff() in file pcap _ util.c (PPP) = 20
from the Dsniff source code. BSD Loopback 4
DLT_NULL
but sniffers usually live in hell. Net- we cannot blindly trust the protocol we are expecting an ARP packet
works do not always carry valid pack- field of an IP datagram to contain the on an Ethernet network, packets
ets. Sometimes packets may not be correct value for the following header. with a length different than 14 +
crafted according to the standards Not even the fields that specify lengths 28 = 42 bytes should be discard-
or may get corrupted in their way. can be trusted. If we want to design ed. Failing to check the length of
These situations must be taken into a powerful packet analyzer, avoiding a packet may result in a noisy
account when designing an applica- segmentation faults and headaches, segmentation fault when trying to
tion that handles sniffed traffic. every detail must be checked. access the received data.
The fact that an ethertype value Here are a few tips: • Check IP and TCP checksums.
says that the next header is of type If checksums are not valid then
ARP does not mean we will actually • Check the whole size of the re- the data contained in the head-
find an ARP header. In the same way, ceived packet. If, for example, ers may be garbage. However,
the fact that checksums are cor- an IP address, checks should CPU time. Capturing everything that
rect does not guarantee that the be made to ensure that the data flows past the network card could
packet contains valid header actually represents a valid IPv4 easily degrade the overall perform-
values. address. ance of our host and cause the ker-
• Check encoding. HTTP or SMTP nel to drop packets.
are text oriented protocols while Filtering Packets If we really need to capture all
Ethernet or TCP/IP use binary fo As we saw before, the capture proc- traffic, then there is little we can do
rmat. Check whether you have ess takes place in the kernel while to optimize the capture process, but
what you expect. our application runs at user level. if we are only interested in a specific
• Any data extracted from a packet When the kernel gets a packet from type of packets we can tell the kernel
for later use should be validated. the network interface it has to copy to filter the incoming traffic so we just
For example, If the payload of it from kernel space to user space, get a copy of the packets that match
a packet is supposed to contain consuming a significant amount of a filter expression. The part of the
#define MAXBYTES2CAPTURE 2048 /* Load the filter program into the packet capture
device. */
int TCP_RST_send(tcp_seq seq, tcp_seq ack, unsigned pcap_setfilter(descr,&filter);
long src_ip,
unsigned long dst_ip, u_short src_prt, u_short while(1){
dst_prt, u_short win){
packet = pcap_next(descr,&pkthdr);
/* This function crafts a custom TCP/IP packet with the
RST flag set iphdr = (struct ip *)(packet+14); /* Assuming is
and sends it through a raw socket. Check Ethernet! */
http://www.programming-pcap.aldabaknocking.com/ for tcphdr = (struct tcphdr *)(packet+14+20); /* Assuming
the full example. */ no IP options! */
printf("+---------------------------------------+\n");
/* [...] */ printf("Received Packet %d:\n", ++count);
printf("ACK: %u\n", ntohl(tcphdr->th_ack) );
return 0; printf("SEQ: %u\n", ntohl(tcphdr->th_seq) );
} printf("DST IP: %s\n", inet_ntoa(iphdr->ip_dst));
printf("SRC IP: %s\n", inet_ntoa(iphdr->ip_src));
int main(int argc, char *argv[] ){ printf("SRC PORT: %d\n", ntohs(tcphdr->th_sport) );
printf("DST PORT: %d\n", ntohs(tcphdr->th_dport) );
int count=0; printf("\n");
bpf_u_int32 netaddr=0, mask=0;
pcap_t *descr = NULL; TCP_RST_send(tcphdr->th_ack, 0, iphdr->ip_dst.s_addr,
struct bpf_program filter; iphdr->ip_src.s_addr, tcphdr->th_dport,
struct ip *iphdr = NULL; tcphdr->th_sport, 0);
struct tcphdr *tcphdr = NULL; }
struct pcap_pkthdr pkthdr; return 0;
const unsigned char *packet=NULL; }