2

In a tutorial program of libpcap I see the following structures:

/* IP header */
struct sniff_ip {
    u_char  ip_vhl;                 
    u_char  ip_tos;                 
    u_short ip_len;                 
    u_short ip_id;                  
    u_short ip_off;                 
    #define IP_RF 0x8000            
    #define IP_DF 0x4000            
    #define IP_MF 0x2000            
    #define IP_OFFMASK 0x1fff       
    u_char  ip_ttl;                 
    u_char  ip_p;                   
    u_short ip_sum;                 
    struct  in_addr ip_src,ip_dst;  
};
#define IP_HL(ip)               (((ip)->ip_vhl) & 0x0f)
#define IP_V(ip)                (((ip)->ip_vhl) >> 4)

/* TCP header */
typedef u_int tcp_seq;

struct sniff_tcp {
    u_short th_sport;               
    u_short th_dport;               
    tcp_seq th_seq;                 
    tcp_seq th_ack;                 
    u_char  th_offx2;               
    #define TH_OFF(th)      (((th)->th_offx2 & 0xf0) >> 4)
    u_char  th_flags;
    #define TH_FIN  0x01
    #define TH_SYN  0x02
    #define TH_RST  0x04
    #define TH_PUSH 0x08
    #define TH_ACK  0x10
    #define TH_URG  0x20
    #define TH_ECE  0x40
    #define TH_CWR  0x80
    #define TH_FLAGS        (TH_FIN|TH_SYN|TH_RST|TH_ACK|TH_URG|TH_ECE|TH_CWR)
    u_short th_win;                 
    u_short th_sum;                 
    u_short th_urp;                 
};

Why the struct of the headers above has been redeclared and the TCP and IP structures in netinet/tcp.h, netinet/ip.h wasn't used? Is there some advantage in using this custom struct?

elmazzun
  • 1,066
  • 2
  • 18
  • 44

1 Answers1

2

The short answer: it is just an example.


As noted in the comments to this post by the author of the tutorial there are issues with portability, since not all operating systems provide such network structures and on the other hand the format is clearly defined by RFCs, so own structures can be used.


Tutorial evolution

The original document was modified in 2005 (there is a note "Further editing and development by Guy Harris"). The orginal document can be found as version 1.1 in the cvs:

First, we must have the actual structures define before we can typecast to them. The following is the structure definitions that I use to describe a TCP/IP packet over Ethernet. All three definitions that I use are taken directly out of the POSIX libraries. Normally I would have simply just used the definitions in those libraries, but it has been my experience that the libraries vary slightly from platform to platform, making it complicated to implement them quickly. So for demonstration purposes we will just avoid that mess and simply copy the relevant structures. All of these, incidentally, can be found in include/netinet on your local Unix system. Here are the structures:

There were original copies of linux structures (now they are modified in the example). The explanation:

Note: On my Slackware Linux 8 box (stock kernel 2.2.19) I found that code using the above structures would not compile. The problem, as it turns out, was in include/features.h, which implements a POSIX interface unless _BSD_SOURCE is defined. If it was not defined, then I had to use a different structure definition for the TCP header. The more universal solution, that does not prevent the code from working on FreeBSD or OpenBSD (where it had previously worked fine), is simply to do the following:
#define _BSD_SOURCE 1
prior to including any of your header files. This will ensure that a BSD style API is being used. Again, if you don't wish to do this, then you can simply use the alternative TCP header structure, which I've linked to here, along with some quick notes about using it.

So, after the document editions the structures were modified in v1.2 and the text is changed in v1.6 with the following commit comment:

Don't talk about any of this coming from POSIX (it doesn't) and don't note that they might be available on a UN*X system (don't take your packet layouts from the OS; it might not have them, or they might not have all the protocol features you want).

Orest Hera
  • 6,706
  • 2
  • 21
  • 35
  • 1
    And that's my commit comment. There is no guarantee that a UN\*X will have those headers, or, if it does, that they'll look like that, *or even that the person working on the program will be using a UN\*X system*, given that there's a WinPcap port of libpcap to Windows. tcpdump stopped using OS header files a long time ago - it was just too much trouble (what I think finally pushed me to change tcpdump was HP-UX's header files not declaring everything needed to fully dissect DNS, but different OSes declaring the version and length fields in the IP header didn't help). –  Oct 05 '15 at 06:54
  • 1
    And neither TCP nor IP are defined by your OS's headers. IPv4 is defined by [RFC 791](http://tools.ietf.org/html/rfc791) and subsequent RFCs that update it, TCP is defined by [RFC 793](http://tools.ietf.org/html/rfc793) and subsequent RFCs that update it, and IPv6 is defined by [RFC 2460](http://tools.ietf.org/html/rfc2460) and subsequent RFCs that update it. So it's not as if the declarations in your OS's headers are somehow special and that separate definitions in your program are "redefining" the structures somehow. –  Oct 05 '15 at 06:56
  • I'm working with a Raspbian distro in a RPi 2 and my program will be written to work only in this OS. I compiled and executed my program successfully and casted my sniffed packets to the header structs I need (from `netinet`). So, as long as I stay in only this environment, all the matters you explained above will not afflict me, right? – elmazzun Oct 05 '15 at 08:28
  • @Guy Harris Thanks for detailed explanation of the original motivation and shedding light on related portability problems. – Orest Hera Oct 05 '15 at 21:41
  • @elmazzun Yes, you are right. As noted by Guy the network data is provided as raw buffer in the format defined by RFCs. It is up to the user how to parse that data. In C it is possible to use casting to structures with mapping byte to byte with the buffer. You must be sure that the structures are packed to avoid spare space due to alignment. Current structures are packed automatically due to data types. In Linux you can have `netinet` structures. You can also check on your platform that the size of all those structures (`netinet` and from the tutorial) is 20. So, there are no padding bytes. – Orest Hera Oct 05 '15 at 21:41
  • "You must be sure that the structures are packed" Note that `__attribute__((packed))` is not guaranteed to make structures packed on all compilers; not all compilers support it. One way to do this is to declare all multi-byte data types as arrays of bytes; this means that you can't just load multi-byte values from the packet, but you can't do that *anyway* if you care about portability, as you might be running either on a big-endian or little-endian machine. –  Oct 06 '15 at 07:57
  • 1
    "I'm working with a Raspbian distro" "bian" comes from "Debian", which, being a Linux distribution, is a UN\*X, so that issue doesn't matter. As long as Linux doesn't change the headers in ways that break your program, which it probably won't do, then, as long as you're willing to avoid trying to run your program on anything other than Linux, you're probably going to be OK (modulo byte-order issues, which, if you always run on a Raspberry Pi, you presumably won't, as they're not likely to change to a processor with a different byte order). –  Oct 06 '15 at 08:00
  • I'll accept Hera's answer, but many thanks to you too, sir Harris. :) – elmazzun Oct 06 '15 at 09:25