Skip to content
Permalink
master
Switch branches/tags
Go to file
 
 
Cannot retrieve contributors at this time
How to write a libpcap module
WARNING: this document describes an unstable interface; future releases
of libpcap may, and some probably will, change the interface in an
incompatible fashion. If you submit your module to the libpcap
developers for inclusion in libpcap, not only does that make it more
likely that it will be available in the libpcap provided by operating
system vendors (such as Linux distributions), but it also means that we
will attempt to update it to handle future changes to this interface.
If we add new capabilities, we may have to ask you how to provide those
additional capabilities if you're using an underlying mechanism for
which we have neither the source code nor the documentation.
NOTE: this document assumes familiarity with the entire libpcap API.
TODO: more routines, more stuff that the activate routine has to do
(such as setting the list of DLT_s), convert to Markdown?
On Linux, *BSD, macOS, Solaris, AIX, HP-UX, IRIX, and Tru64 UNIX,
Libpcap supports capturing on network interfaces as supported by the
operating system networking stack, using the native packet capture
mechanism provided by the OS. On Windows, it supports it with the help
of the driver and library supplied by WinPcap and Npcap.
In addition, it also supports capturing on other types of devices, such
as:
specialized capture cards, such as Endace DAG cards;
network adapters that provide special high-performance code
paths, such as CSPI Myricom adapters;
buses such as USB;
software communication channels such as D-Bus and Linux netlink;
etc..
Support for those devices is provided by modules compiled into libpcap.
If you want to add such a module, you would first have to check the list
of link-layer header types supported by libpcap, to see if one of those
would be sufficient for your device. The current version of the list
can be found at
https://www.tcpdump.org/linktypes.html
If none of those would work for your device, please read
doc/DLT_ALLOCATE_HOWTO.md and the introductory paragraphs on the Web
page mentioned above, and then send a request for the new link-layer
header type to tcpdump-workers@lists.tcpdump.org.
Once you have a link-layer header type value or values that you can use,
you can add new module.
The module should be a C source file, with a name of the form
pcap-{MOD}.c, where {MOD} is a name appropriate for your device; for
example, the support for DAG cards is in pcap-dag.c, and the support for
capturing USB traffic on Linux is pcap-usb-linux.c.
Your module is assumed to support one or more named devices. The names
should be relatively short names, containing only lower-case
alphanumeric characters, consisting of a prefix that ends with an
alphabetic character and, if there can be more than one device instance,
possibly followed by a numerical device ID, such as "mydevice" or
"mydevice0"/"mydevice1"/.... If you have more than one type of device
that you can support, you can have more than one prefix, each of which
can be followed by a numerical device ID.
The two exported functions that your module must provide are routines to
provide a list of device instances and a program to initialize a
created-but-not-activated pcap_t for an instance of one of your devices.
The "list of device instances" routine takes, as arguments:
a pointer to a pcap_if_list_t;
a pointer to an error message buffer.
The error message buffer may be assumed to be PCAP_ERRBUF_SIZE bytes
large, but must not be assumed to be larger. By convention, the routine
typically has a name containing "findalldevs".
The routine should attempt to determine what device instances are
available and add them to the list pointed to by the first argument;
this may be impossible for some modules, but, for those modules, it may
be difficult to capture on the devices using Wirehshark (although it
should be possible to capture on them using tcpdump, TShark, or other
programs that take a device name on the command line), so we recommend
that your routine provide the list of devices if possible. If it
cannot, it should just immediately return 0.
The routine should add devices to the list by calling the add_dev()
routine in libpcap, declared in the pcap-int.h header. It takes, as
arguments:
the pointer to the pcap_if_list_t passed as an argument to the
routine;
the device name, as described above;
a 32-bit word of flags, as provided by pcap_findalldevs();
a text description of the device, or NULL if there is no
description;
the error message buffer pointer provided to the routine.
add_dev() will, if it succeeds, return a pointer to a pcap_if_t that was
added to the list of devices. If it fails, it will return NULL; in this
case, the error message buffer has been filled in with an error string,
and your routine must return -1 to indicate the error.
If your routine succeeds, it must return 0. If it fails, it must fill
in the error message buffer with an error string and return -1.
The "initialize the pcap_t" routine takes, as arguments:
a pointer to a device name;
a pointer to an error message buffer;
a pointer to an int.
It returns a pointer to a pcap_t.
Your module will probably need, for each pcap_t for an opened device, a
private data structure to maintain its own information about the opened
device. These should be allocated per opened instance, not per device;
if, for example, mydevice0 can be captured on by more than one program
at the same time, there will be more than one pcap_t opened for
mydevice0, and so there will be separate private data structures for
each pcap_t. If you need to maintain per-device, rather than per-opened
instance information, you will have to maintain that yourself.
The routine should first check the device to see whether it looks like a
device that this module would handle; for example, it should begin with
one of the device name prefixes for your module and, if your devices
have instance numbers, be followed by a number. If it is not one of
those devices, you must set the integer pointed to by the third
argument to 0, to indicate that this is *not* one of the devices for
your module, and return NULL.
If it *is* one of those devices, it should call pcap_create_common,
passing to it the error message buffer as the first argument and the
size of the per-opened instance data structure as the second argument.
If it fails, it will return NULL; you must return NULL in this case.
If it succeeds, the pcap_t pointed to by the return value has been
partially initialized, but you will need to complete the process. It
has a "priv" member, which is a void * that points to the private data
structure attached to it; that structure has been initialized to zeroes.
What you need to set are some function pointers to your routines to
handle certain operations:
activate_op
the routine called when pcap_activate() is done on the
pcap_t
can_set_rfmon_op
the routine called when pcap_can_set_rfmon() is done on
the pcap_t - if your device doesn't support 802.11
monitor mode, you can leave this as initialized by
pcap_create_common(), as that routine will return "no,
monitor mode isn't supported".
Once you've set the activate_op and, if necessary, the can_set_rfmon_op,
you must return the pcap_t * that was returned to you.
Your activate routine takes, as an argument, a pointer to the pcap_t
being activated, and returns an int.
The perameters set for the device in the pcap_create() call, and after
that call(), are mostly in the opt member of the pcap_t:
device
the name of the device
timeout
the buffering timeout, in milliseconds
buffer_size
the buffer size to use
promisc
1 if promiscuous mode is to be used, 0 otherwise
rfmon
1 if monitor mode is to be used, 0 otherwise
immediate
1 if the device should be in immediate mode, 0 otherwise
nonblock
1 if the device should be in non-blocking mode, 0
otherwise
tstamp_type
the type of time stamp to supply
tstamp_precision
the time stamp precision to supply
The snapshot member of the pcap_t structure will contain the snapshot
length to be used.
Your routine should attempt to set up the device for capturing. If it
fails, it must return an error indication which is one of the PCAP_ERROR
values. For PCAP_ERROR, it must also set the errbuf member of the
pcap_t to an error string. For PCAP_ERROR_NO_SUCH_DEVICE and
PCAP_ERROR_PERM_DENIED, it may set it to an error string providing
additional information that may be useful for debugging, or may just
leave it as a null string.
If it succeeds, it must set certain function pointers in the pcap_t
structure:
read_op
called whenever packets are to be read
inject_op
called whenever packets are to be injected
setfilter_op
called whenever pcap_setfilter() is called
setdirection_op
called whenever pcap_setdirection() is called
set_datalink_op
called whnever pcap_set_datalink() is called
getnonblock_op
called whenever pcap_getnonblock() is called
setnonblock_op
called whenever pcap_setnonblock() is called
stats_op
called whenever pcap_stats() is called
cleanup_op
called if the activate routine fails or pcap_close() is
called
and must also set the linktype member to the DLT_ value for the device.
On UN*Xes, if the device supports waiting for packets to arrive with
select()/poll()/epoll()/kqueues etc., it should set the selectable_fd
member of the structure to the descriptor you would use with those
calls. If it does not, then, if that's because the device polls for
packets rather than receiving interrupts or other signals when packets
arrive, it should have a struct timeval in the private data structure,
set the value of that struct timeval to the poll timeout, and set the
required_select_timeout member of the pcap_t to point to the struct
timeval.
The read_op routine is called when pcap_dispatch(), pcap_loop(),
pcap_next(), or pcap_next_ex() is called. It is passed the same
arguments as pcap_dispatch() is called.
The routine should first check if the break_loop member of the pcap_t is
non-zero and, if so, set that member to zero and return
PCAP_ERROR_BREAK.
Then, if the pcap_t is in blocking mode (as opposed to non-blocking
mode), and there are no packets immediately available to be passed to
the callback, it should block waiting for packets to arrive, using the
buffering timeout, first, and read packets from the device if necessary.
Then it should loop through the available packets, calling the callback
routine for each packet:
If the PACKET_COUNT_IS_UNLIMITED() macro evaluates to true when
passed the packet count argument, the loop should continue until
there are no more packets immediately available or the
break_loop member of the pcap_t is non-zero. If the break_loop
member is fount to be non-zero, it should set that member to
zero and return PCAP_ERROR_BREAK.
If it doesn't evaluat to true, then the loop should also
terminate if the specified number of packets have been delivered
to the callback.
Note that there is *NO* requirement that the packet header or data
provided to the callback remain available, or valid, after the callback
routine returns; if the callback needs to save the data for other code
to use, it must make a copy of that data. This means that the module is
free to, for example, overwrite the buffer into which it read the
packet, or release back to the kernel a packet in a memory-mapped
buffer shared between the kernel and userland, after the callback
returns.
If an error occurs when reading packets from the device, it must set the
errbuf member of the pcap_t to an error string and return PCAP_ERROR.
If no error occurs, it must return the number of packets that were
supplied to the callback routine.
The inject routine is passed a pointer to the pcap_t, a buffer
containing the contents of the packet to inject, and the number of bytes
in the packet. If the device doesn't support packet injection, the
routine must set the errbuf member of the pcap_t to a message indicating
that packet injection isn't supported and return PCAP_ERROR. Otherwise,
it should attempt to inject the packet; if the attempt fails, it must
set the errbuf member of the pcap_t to an error message and return
PCAP_ERROR. Otherwise, it should return the number of bytes injected.
The setfilter routine is passed a pointer to the pcap_t and a pointer
to a struct bpf_program containing a BPF program to be used as a filter.
If the mechanism used by your module can perform filtering with a BPF
program, it would attempt to set that filter to the specified program.
If that failed because the program was too large, or used BPF features
not supported by that mechanism, the module should fall back on
filtering in userland by saving a copy of the filter with a call to
install_bpf_program(), setting a flag in the private data instructure
indicating that filtering is being done by the module and, in the read
routine's main loop, checking the flag and, if it's set, calling
pcap_filter(), passing it the fcode.bf_insns member of the pcap_t, the
raw packet data, the on-the-wire length of the packet, and the captured
length of the packet, and only passing the packet to the callback
routine, and counting it, if pcap_filter() returns a non-zero value.
(If the flag is not set, all packets should be passed to the callback
routine and counted, as the filtering is being done by the mechanism
used by the module.) If install_bpf_program() returns a negative value,
the routine should return PCAP_ERROR.
If the attempt to set the filter failed for any other reason, the
routine must set the errbuf member of the pcap_t to an error message and
return PCAP_ERROR.
If the attempt to set the filter succeeded, or it failed because the
mechanism used by the module rejected it and the call to
install_bpf_program() succeeded, the routine should return 0.
If the mechanism the module uses doesn't support filtering, the pointer
to the setfilter routine can just be set to point to
install_bpf_program; the module does not need a routine of its own to
handle that.
The setdirection routine is passed a pointer to the pcap_t and a
pcap_direction_t indicating which packet directions should be accepted.
If the module can't arrange to handle only incoming packets or only
outgoing packets, it can set the pointer to the setdirection routine to
NULL, and calls to pcap_setdirection() will fail with an error message
indicating that setting the direction isn't supported.
XXX describe set_datalink, including what the activate routine has to do
XXX
XXX describe the rest of the routines XXX