- libpnet6's functionality regarding raw sockets. See pnet6(3) for an introduction to libpnet6.
This document describes the functions in libpnet6 that you can use with raw sockets (where supported).
NOTE: in order to be able to follow this, some familiarity with IP datagrams and the IP protocol is required (and assumed in this document). Please, read through pnet6-ip(3), first if you need to know the format and functions necessary to work with raw IP datagrams.
In order to use raw sockets, you only need to include the generic <pnet6.h> file. However, normally you'd want to parse protocol headers, or calculate checksums when you're working with raw sockets. The data structures and function prototypes for this are defined in the file <pnet6tlp.h>.
Raw sockets provide a way to manipulate sockets directly at the IP layer, something which is not possible through the standard sockets API, since that provides access to the layer above IP, i.e. TCP or UDP. Raw sockets are often used to implement functionality that the standard sockets API cannot provide, such as sending ICMP messages, or IP datagrams carrying payload for protocols other than TCP or UDP, or which need to be dealt with by the application instead of by the kernel. (Protocols making use of IP are referred to (at least in this document) as Tranport Layer Protocols.)
libpnet6 provides an interface to work with raw sockets, which is in many ways similar to the standard socket interface discussed in pnet6-socket (3). In broad terms, a raw socket can be created, either in the IPv4 or IPv6 address family, after which, messages (datagrams) can be sent and/or received on the socket. Note however, that, since it is a raw socket, any headers above the IP layer need to be filled in by the application. This basically means that you can send you own UDP datagrams, but all your UDP data must be preceeded by a properly initialized UDP header. This is quite different than a UDP socket, where the UDP header is filled in by the kernel.
There's a few more subtleties regarding raw sockets. For IPv4, one can choose to go even further, and request that the application fill out the IPv4 header as well, not only the transport layer header. This requires the caller to possess detailed knowledge of the IP header, what the different fields mean and the way they operate together. However, filling out a complete IP header and passing it to a raw socket is not possible with IPv6, which means that you cannot write your own IPv6 headers. You can, however, use the libpnet6 API to set various fields in the IPv6 header.
Should you need to read, make and write complete IPv6 headers, you'll have to go even lower in the network protocol stack, and work at the datalink layer. See the document on libpnet6 datalink layer access, pnet6-pkt for more information.
PNETSOCK pnetRAWSocket( int protocol );
PNETSOCK pnetRAWSocket2( int family, int protocol );
These functions create a raw socket. The first function always creates a socket in the IPv6 address family. The second function creates a socket in the specified address family (i.e IPv4 or IPv6).
The argument protocol tells the kernel what type of messages we will be sending or receiving through this socket. This field is used by the kernel to distinguish between all applications that have outstanding raw sockets listening for messages (i.e. incoming datagrams). If protocol is 0, then all messages, except for TCP and UDP, and a few of the ICMP messages, such as ECHO request and reply, (this is not true for Linux however, so don't rely on anything!) will be passed to the socket. For example, passing a value for protocol of 89, will result in the kernel only passing Open Short Path First gateway protocol messages to this raw socket.
int pnetSockOwnIPHeader( PNETSOCK ps, int on);
This function enables or disables the own header flag for this raw socket, depending on the value of on.
This function is only applicable to IPv4 sockets. It tells the kernel that datagrams sent on this raw socket will have their own IPv4 header set. The kernel only inspects and computes the IP header checksum (the exact semantics may vary from kernel to kernel).
If the own header flag is set, then any data sent on this socket must be prefixed by a proper IPv4 header. Libpnet6 will perform absolutely no consistency or checksum checks on such datagrams.
int pnetSockChecksum( PNETSOCK ps, int on, int offset );
This function is only applicable to raw sockets in the IPv6 address family.
For raw sockets with a protocol other than ICMPV6, this function instructs the kernel to compute and fill in the checksum for datagrams sent on this socket on behalf of the application. The reason for this is the fact that once a datagram is sent, the kernel first needs to determine the outgoing interface and address. Since this information is required in for checksum computation, it makes sense to let the kernel do this, instead of asking an application to determine the outgoing address for the datagram for the single purpose of computing the checksum.
The variable on tells the kernel whether to turn IPv6 checksum computation on or off for this socket. The variable offset tells the kernel at what offset in the application data buffer (i.e. the IPv6 datagram payload) the checksum field is located (offsets start at 0 for the first byte, 1 for the seconds, etc...) The checksum is assumed to be a 16 unsigned integer field.
Once a socket has been created, any of the generic socket functions can be used to listen for incoming datagrams, and send or receive datagrams. A connect can be performed on a raw socket, with the only difference being that a call to pnetWriteTo() can be replaced by a call to pnetWrite(), since all the connect does is to fill in store the foreign address for this socket.
With each raw socket, auxiliary information for the socket is maintained. This includes the full IPv4 or IPv6 headers associated with the last received datagram on this socket, as well as the interface on which it was received, the actual address it was sent to, and the datagram's hop limit.
Auxiliary information is discussed in detail in pnet6-aux (3). Consult that document for more information.
Although raw sockets are available on all platforms, there are fine subleties involved. I've tried to hide all the nasty details away inside the API, however it is not altogether impossible that I've omitted something or just plainly did something wrong. Feel free to send comments or patches should you feel you can improve the code.
The current version of libpnet6 is highly experimental.
You can always get the most recent version from http://pnet6.sourceforge.net.
Peter Bozarov. Send mail to kingofgib (at) users.sourceforge.net