| 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 |