PLOKAMI - Common Lisp PCAP Interface


 

Abstract

PLOKAMI is a lispy interface to libpcap built on top of a CFFI portability layer. Complete bindings to every function in libpcap are in pcap.lisp although not exported. A higher level lisp-like interface is implemented in plokami.lisp and documented here.

Supported: Realtime network packet capture, injection, PCAP dumpfile reading/writing, BPF, timeouts and operation in non-blocking mode.
Also, full support for Windows through winpcap but see here. The library has been tested on SBCL (linux/darwin), ClozureCL and LispWorks (darwin/win32) but should work on every platform that CFFI supports.

Performance: Minimal overhead over plain libpcap. Low memory use with few dynamic allocations.

NOTE: Read timeouts, non-blocking mode operation and the use of select/epoll/kqueue on the live interface descriptor (as returned by pcap-live-descriptor) depend on support from operating system/libpcap. If issues arise, consult libpcap documentation and platform specific notes.

The descriptor can not be used with kqueue on OSX (as of 10.5) although the same descriptor works fine with select. Recent FreeBSD versions have fixed this and kqueue works with no issues on these platforms.

The code comes with a BSD-style license so you can basically do with it whatever you want.


 

Contents


 

Download

The latest version is available from git repository. You can also install it with Quicklisp.

For feature-requests, questions, patches email me at xristos (AT) sdf (DOT) org.

Examples

List interfaces (addresses will not show up correctly on Win32, patches welcome)

If find-all-devs returns NIL, make sure you have appropriate permissions for packet capture (read /dev/bpf* for OSX/BSD, root/CAP_NET_RAW on Linux).

PLOKAMI> (find-all-devs)
(("lo0" NIL 1
  (((:ADDR :AF_INET6 "::1")
    (:NETMASK :AF_INET6 "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"))
   ((:ADDR :AF_INET "127.0.0.1") (:NETMASK :AF_INET "255.0.0.0"))
   ((:ADDR :AF_INET6 "fe80::1") (:NETMASK :AF_INET6 "ffff:ffff:ffff:ffff::"))
   ((:ADDR :AF_LINK "lo0"))))
 ("en1" NIL 0
  (((:ADDR :AF_INET "192.168.1.64") (:NETMASK :AF_INET "255.255.255.0")
    (:BROADADDR :AF_INET "192.168.1.255"))
   ((:ADDR :AF_LINK "en1:0.1e.52.70.2f.9e"))))
 ("en0" NIL 0 (((:ADDR :AF_LINK "en0:0.1b.23.cc.ea.50"))))
 ("fw0" NIL 0 (((:ADDR :AF_LINK "fw0:0.1d.41.cf.fe.78.b4.ee")))))
Read packets from the network in non-blocking mode, filter for IP, process and write them out to a PCAP dumpfile. Interrupt lisp to clean up and exit.
(with-pcap-interface (pcap "en0" :promisc t :snaplen 1500 :nbio t)
  (with-pcap-writer (writer "session.pcap" :snaplen 1500 :datalink (pcap-live-datalink pcap))
    (set-filter pcap "ip")
    (loop
       (capture pcap -1
                (lambda (sec usec caplen len buffer)
                    (dump writer buffer sec usec :length caplen :origlength len)
                    (format t "Packet length: ~A bytes, on the wire: ~A bytes~%" caplen len)))
       ;; Better to use select/epoll/kqueue on pcap-live-descriptor
       (sleep 0.01))))
Read all packets available in PCAP dumpfile session.pcap and process them.
(with-pcap-reader (reader "session.pcap" :snaplen 1500)
  (capture reader -1
           (lambda (sec usec caplen len buffer)
             ;; Packet processing code here
             (format t "Packet length: ~A bytes, on the wire: ~A bytes~%" caplen len))))
In all cases, BUFFER vector used in callback handler supplied to capture is allocated once per pcap instance and is overwritten every time capture gets called. If packet persistence is required, BUFFER contents should be copied before capture gets called again.

Under win32/winpcap, SNAPLEN only takes effect when a filter is used. It is therefore advised that an empty filter be installed, (set-filter instance ""), when capturing on this platform. Also see here.


 

The PLOKAMI dictionary


Special Variables


[Special variable]
*pcap-version*


Version of native libpcap library.

Constructors & Functions


[Function]
make-pcap-writer file &key datalink snaplen => pcap-writer

Creates and returns a pcap-writer instance that is used to write packets to a pcap dumpfile.

file is the filename (namestring or pathname) to open and write packets to.
datalink should contain a string that represents the datalink protocol of the network interface used to capture the packets. Default is "EN10MB" for Ethernet. See *supported-datalinks* in pcap.lisp for more.
snaplen should contain the number of bytes read per packet captured and should be the same as the one used when capturing/reading the packets. CAPTURE-FILE-WRITE-ERROR is signaled on errors.


[Function]
make-pcap-reader file &key snaplen => pcap-reader

Creates and returns a pcap-reader instance that is used for reading packets from a pcap dumpfile.

file is the filename (namestring or pathname) to open and read packets from.
snaplen should contain the number of bytes read per packet captured. Default is 68 which should be enough for headers. CAPTURE-FILE-READ-ERROR is signaled on errors.


[Function]
make-pcap-live interface &key promisc nbio timeout snaplen => pcap-live

Creates and returns a pcap-live instance that is used for live packet capture from a network interface.

interface is a string that defines the network interface to use for capture.
promisc should be T for promiscuous mode, NIL otherwise.
nbio should be T when non-blocking operation is required. NIL otherwise (default).
timeout should hold read timeout in milliseconds (default is 50). 0 will wait forever. Only used when in blocking mode and only in platforms that support it. No guarantee of actually returning within timeout is made. Use non-blocking mode if that is not adequate.
snaplen should contain the number of bytes captured per packet. Default is 68 which should be enough for headers. NETWORK-INTERFACE-ERROR or BLOCK-MODE-ERROR is signaled on errors.


[Function]
find-all-devs => list of devices/addresses


Return a list of all network devices that can be opened for capture. Result list layout explained in pcap_findalldevs(3). NIL is returned when no interfaces are available (e.g. permissionissues)
Signals NETWORK-INTERFACE-ERROR on errors.

Macros


[Macro]
with-pcap-writer (writer file &rest options) &body body => result


Call MAKE-PCAP-WRITER using file, options as arguments and store the resulting instance in writer. Forms in body are wrapped in an unwind-protect form that takes care of deallocating resources on error.


[Macro]
with-pcap-reader (reader file &rest options) &body body => result


Call MAKE-PCAP-READER using file, options as arguments and store the resulting instance in reader. Forms in body are wrapped in an unwind-protect form that takes care of deallocating resources on error. A restart is also automatically invoked when PACKET-FILTER-ERROR is signaled, skipping the filter setup.


[Macro]
with-pcap-interface (live iface &rest options) &body body => result


Call MAKE-PCAP-LIVE using iface, options as arguments and store the resulting instance in live. Forms in body are wrapped in an unwind-protect form that takes care of deallocating resources on error and also returns packet capture statistics when possible. A restart is also automatically invoked when PACKET-FILTER-ERROR is signaled, skipping the filter setup.

Generic functions


[Generic function]
stop pcap-mixin => result


Deallocates resources for pcap-live, pcap-reader, pcap-writer instance.


[Generic function]
stats pcap-live => capture statistics


Return packet capture statistics from the start of the run to the time of the call for live interface capture only. Statistics are returned as multiple values and correspond to packets received, packets dropped and packets dropped by interface (in that order). NETWORK-INTERFACE-ERROR is signaled on failure.


[Generic function]
set-non-block pcap-live block-mode => result


Sets non-blocking mode if block-mode is T, blocking mode if NIL. BLOCK-MODE-ERROR is signaled on failure and a restart, continue-block-mode is installed, that can be invoked to continue.


[Generic function]
set-filter pcap-process-mixin string => result


Sets a packet filter on a pcap-live or pcap-reader instance. The filter should be given as a BPF expression in string.
PACKET-FILTER-ERROR is signaled on failure. A restart, continue-no-filter is installed that can be invoked to continue on error.


[Generic function]
inject pcap-live buffer &key length => integer


Injects length bytes to a live pcap interface (size of buffer if omitted). Return number of bytes injected on success. For performance reasons buffer should be a byte vector allocated with cffi:make-shareable-byte-vector.
PACKET-INJECT-ERROR is signaled on failure.


[Generic function]
dump pcap-writer data sec usec &key length origlength => result

Writes contents of byte vector data to pcap-writer instance (which corresponds to a pcap dumpfile).

length is the number of bytes to write and is set to the size of data when omitted.
origlength should be set to the number of bytes originally present in the packet and is set to length when omitted.
sec and usec should be set to seconds/microseconds since the UNIX epoch at the time of capture (timeval structure in C). If you are using your own source buffer (instead of the one used by plokami), it should be allocated with cffi:make-shareable-byte-vector. As libpcap does not return useful value on pcap_dump() no plokami specific conditions, beyond simple assertions of argument checks, are raised by this function.


[Generic function]
capture pcap-process-mixin packets handler => integer

Only works for pcap-live or pcap-reader instances. Captures and processes maximum number of packets. Minimum is zero. Return 0 when no packets available (did not pass installed packet filter, end of file for dumpfiles, read timeout expired before packets arrive or no packets available at the time of the call if in non-blocking mode) otherwise return number of packets processed which can be fewer than the maximum given in packets (due to internal libpcap buffer).

A count of -1 in packets processes all the packets received so far when live capturing, or all the packets in a file when reading a pcap dumpfile. Handler must be a user defined function that accepts five arguments and will get called once for every packet received. The values passed are sec, usec, caplen, len and buffer.

sec and usec correspond to seconds/microseconds since the UNIX epoch (timeval structure in C) at the time of capture.
caplen corresponds to the number of bytes captured.
len corresponds to the number of bytes originally present in the packet but not necessarily captured.
buffer is a statically allocated byte vector (via cffi:make-shareable-byte-vector) with the contents of the captured packet. This means that successive calls of the packet handler will overwrite its contents and if packet persistence is required, contents of buffer should be copied somewhere else from within handler.

If an error occurs, PACKET-CAPTURE-ERROR is signaled for live interfaces and CAPTURE-FILE-READ-ERROR for pcap dumpfiles. For more details on callback handling, see CFFI callback pcap-handler.


Readers


[Generic function]
pcap-writer-snaplen pcap-writer => integer


Number of bytes to write per packet processed.


[Generic function]
pcap-writer-file pcap-writer => string


File (native namestring) to write packets to. Represents a pathname using the native conventions of the operating system.


[Generic function]
pcap-writer-datalink pcap-writer => string


Return string representation of the datalink protocol that is used by pcap-writer object.


[Generic function]
pcap-writer-alive-p pcap-writer => boolean


Returns T when pcap-writer object is live and can be used for packet dumping. NIL when stop has been invoked and object is dead.


[Generic function]
pcap-reader-swapped-p pcap-reader => boolean


T if savefile uses different byte order from host system.


[Generic function]
pcap-reader-snaplen pcap-reader => integer


Number of bytes to read per packet processed.


[Generic function]
pcap-reader-minor pcap-reader => integer


Minor version of savefile.


[Generic function]
pcap-reader-major pcap-reader => integer


Major version of savefile.


[Generic function]
pcap-reader-file pcap-reader => string


File (native namestring) to read packets from. Represents a pathname using the native conventions of the operating system.


[Generic function]
pcap-reader-datalink pcap-reader => string


Return string representation of the datalink protocol used by pcap-reader object.


[Generic function]
pcap-reader-alive-p pcap-reader => boolean


Returns T when pcap-reader object is live and can be used for capture. NIL when stop has been invoked and object is dead.


[Generic function]
pcap-live-timeout pcap-live => integer


Read timeout in milliseconds. 0 will wait until a packet arrives. Only takes effect in blocking mode/platforms that support it. No guarantee of returning within timeout is made.


[Generic function]
pcap-live-snaplen pcap-live => integer


Return snapshot length (how many bytes to capture per packet).


[Generic function]
pcap-live-promisc-p pcap-live => boolean


T if capturing in promiscuous mode.


[Generic function]
pcap-live-interface pcap-live => string


Return string representation of network interface that is used for packet capture.


[Generic function]
pcap-live-descriptor pcap-live => integer


File descriptor that can be used with epoll/kqueue/select and non-blocking mode.


[Generic function]
pcap-live-datalink pcap-live => string


Return string representation of the datalink protocol that is used by pcap-live object.


[Generic function]
pcap-live-alive-p pcap-live => boolean


Returns T when pcap-live object is live and can be used for capture. NIL when stop has been invoked and object is dead.


[Generic function]
plokami-error-text condition => string


Reader that returns error description for all plokami-error conditions.

Conditions


[Condition type]
plokami-error


Generic condition for this package. Superclass of all other PLOKAMI conditions.


[Condition type]
packet-inject-error


Signaled on errors during packet injection.


[Condition type]
packet-filter-error


Signaled when a Berkeley packet filter could not be established.


[Condition type]
packet-capture-error


Signaled on errors during live packet capture.


[Condition type]
network-interface-error


Signaled on all network interface errors.


[Condition type]
capture-file-write-error


Signaled on all pcap dumpfile errors.


[Condition type]
capture-file-read-error


Signaled on all pcap readfile errors.


[Condition type]
block-mode-error


Signaled on errors when changing blocking mode.

Restarts


[Restart]
continue-block-mode


Continue without changing blocking mode.


[Restart]
continue-no-filter


Continue on filter setup error.

 

Acknowledgments

This documentation was prepared with DOCUMENTATION-TEMPLATE.

Copyright © 2008-2010 xristos (AT) sdf (DOT) org, All Rights Reserved