Monday, September 4, 2017
Flow of tcpdump printing capability
Flow of tcpdump printing capability
Tcpdump is built on top of the libpcap library. Libpcap doesnt support printing packet information on stdout but it does support saving captured packets to file. Tcpdump implements printing capabilities so packet information can be seen live on stdout. Because there are many various protocols on almost each level of TCP/IP stack it is important that each packet type has its own printer. In this post, flow of how tcpdump chooses correct printer for packet type will be explained. Motivation of this post lays in the desire to extend tcpdumps printing capabilities so it can print netfilter log message (NFLOG) on stdout.
Printing flow
Whole process goes somewhat cascading, first in tcpdump.c (main function), tcpdump looks for link-layer type and defines callback function (print_packet) for pcap_loop().type = pcap_datalink(pd);
printinfo.ndo_type = 1;
printinfo.ndo = gndo;
printinfo.p.ndo_printer = lookup_ndo_printer(type);
...
callback = print_packet;
For choosing correct printer for protocol, tcpdump with help of pcap_datalink() reads link-layer type. Then lookup_ndo_printer(type) function returns correct function to be called every time packet of type is captured and that function is stored in printinfo.p.ndo_printer structure. Type and associated function for link-layer are defined in ndo_printers variable in tcpdump.c. So for example, for DLT_IPNET type ipnet_if_print function will be called or for DLT_EN10MB ether_if_print function. Complete list of link layer header types in libpcap can be found on their website, not all of them could be printed on stdout with tcpdump. Later, tcpdump uses pcap_loop function with callback = print_packet, so every time packet is captured print_packet() function is called. The latter function calls ndo_printer function as shown below:
if(print_info->ndo_type) {
hdrlen = (*print_info->p.ndo_printer)(print_info->ndo, h, sp);
} else {
hdrlen = (*print_info->p.printer)(h, sp);
}
So lets assume TCP packet came to interface. It uses IP (IPv4) protocol on network layer, so function ipnet_if_print (which then immediately calls ip_print) will be called. That function inspects header information and chooses between ip_print (for IPv4) or ip6_print (for IPv6). Furthermore, ip_print function prints some packet information and calls ip_print_demux function which will call printer for higher protocol.
case IPPROTO_TCP: /* pass on the MF bit plus the offset to detect fragments */ tcp_print(ipds->cp, ipds->len, (const u_char *)ipds->ip, ipds->off & (IP_MF|IP_OFFMASK)); break;
The tcp_print function extracts header information and then prints it on the stdout.
Shown flow is just for the TCP protocol but the idea is same for all available printers in tcpdump.
Shown flow is just for the TCP protocol but the idea is same for all available printers in tcpdump.
Netfilter log message printing
Netfilter log message (NFLOG) is specific message structure used in Netfilter framework. It is defined on link layer in TCP/IP stack. Its structure is nicely described on following link. It is used for various things but one of importance for this blogpost is that NFLOG format is found when libpcap captures Netfilter packets over netlink socket. In this sniffing process, Netfilter uses its log message format as header in link layer and libpcap receives it that way. How to capture packets from Netfilter over netlink socket with tcpdump can be found in one of my previous post. Libpcap can receive Netfilter log message but tcpdump however cant print them on stdout. The question is why? Answer is quite simple, tcpdump doesnt have implemented that functionality. The answer however will be explained a bit more. Lets examine code at top of blogpost having in mind that we are capturing NFLOG packets, pcap_datalink function returns code number 239, if that number is passed to function pcap_datalink_val_to_name and we store return value in string, it is possible to print name of the link layer protocol, which in this case is "NFLOG". OK, so libpcap does recognize format but lookup_ndo_printer does not return printer for NFLOG because it does not exists. So the question is, how to extend tcpdump so it can print NFLOG packets on stdout? Something similar was already asked on tcpdumps mailing list and from there the simple idea arises. Printing IP packets is already implemented and NFLOG packet encapsulates IP packet somewhere so the simplest thing to do would be to just remove NFLOG header and pass packet as IP packet to ipnet_if_print function. Some information would be lost because header would be discarded, but it can be parsed in some later expansions. I will update this post as soon as I make some new progress.
Solution for printing Netfilter log message
The solution for printing Netfilter log message information on stdout is somehow very similar to what I explained how should it work. As I mentioned before I dont parse Netfilter log message header I just discard it and print IP information which is encapsulated by NFLOG.
The main problem for not printing on the packet information on stdout was because there was no printer function for DLT_NFLOG which is data link type protocol. So the first step in creating printer function was to define it in printers variable.
The main problem for not printing on the packet information on stdout was because there was no printer function for DLT_NFLOG which is data link type protocol. So the first step in creating printer function was to define it in printers variable.
static struct ndo_printer ndo_printers[] = {
{ nflog_if_print, DLT_NFLOG },
{ ether_if_print, DLT_EN10MB },
#ifdef DLT_IPNET
{ ipnet_if_print, DLT_IPNET },
#endif
...}So the new nflog_if_print function is responsible for printing packet information on screen. Mentioned function was placed in new file called print-nflog.c because that is the standard in tcpdump file hierarchy. The main part of that file is shown below.
static void
nflog_print(struct netdissect_options *ndo,
const u_char *p, u_int length, u_int caplen)
{
ip_print(ndo, p, length);
return;
}
u_int
nflog_if_print(struct netdissect_options *ndo,
const struct pcap_pkthdr *h, const u_char *p)
{
int j = 0;
for (j = 0; j < 104; j++) {
*p++;
}
nflog_print(ndo, p, h->len - 104, h->caplen - 104);
return 0;
}
Loop in nflog_if_print function is responsible for discarding NFLOG header. Header size is 104 bytes, so that much the pointer is shifted. After shifting pointer it is necessary to correct packet size by subtracting size of discarded header and that is 104 bytes. In nflog_print function there is now packet with starting point at IP header and correct packet size, so the only task of function is to forward packet to ip_print function which will print IP packet information. For things to work correctly it is necessary to include print-nflog.c in Makefile and to declare nflog_if_print function as extern in netdissect.h.
Fork of tcpdump with explained modifications can be found on https://github.com/PetarAlilovic/tcpdump. Modification were submitted for further consideration and lately as I hope they will be accepted in master branch.
EDIT: Pull request has been made and after modifications it was accepted. Flow of modifications and arguments for them can be found on https://github.com/the-tcpdump-group/tcpdump/issues/315 and accepted pull request on https://github.com/the-tcpdump-group/tcpdump/pull/329.
EDIT: Some security modifications have been made and pull request was accepted https://github.com/the-tcpdump-group/tcpdump/pull/337.
Fork of tcpdump with explained modifications can be found on https://github.com/PetarAlilovic/tcpdump. Modification were submitted for further consideration and lately as I hope they will be accepted in master branch.
EDIT: Pull request has been made and after modifications it was accepted. Flow of modifications and arguments for them can be found on https://github.com/the-tcpdump-group/tcpdump/issues/315 and accepted pull request on https://github.com/the-tcpdump-group/tcpdump/pull/329.
EDIT: Some security modifications have been made and pull request was accepted https://github.com/the-tcpdump-group/tcpdump/pull/337.
download file now
Labels:
capability,
flow,
of,
printing,
tcpdump