You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
354 lines
14 KiB
354 lines
14 KiB
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
|