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 (in git), PCAP dumpfile reading/writing, BPF, timeouts and operation in non-blocking mode.
Also, full support for Win32 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/non-existent overhead over plain libpcap. Low memory use with very few dynamic allocations. GC delays/packet loss should not be an issue.

NOTE: Read timeouts, non-blocking mode operation and the use of select/epoll/kqueue on the live interface file descriptor (as returned by pcap-live-descriptor) depend on support from operating system/libpcap. If things involving these operations do not work as they should, consult libpcap documentation and platform specific notes.

The file descriptor can not be used with kqueue on OSX (known OSX issue) although the same file 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

  1. Download
  2. Examples
  3. The PLOKAMI dictionary
    1. Constructors and readers
      1. make-pcap-live
      2. make-pcap-reader
      3. make-pcap-writer
      4. pcap-live-alive
      5. pcap-live-datalink
      6. pcap-live-interface
      7. pcap-live-promisc
      8. pcap-live-snaplen
      9. pcap-live-timeout
      10. pcap-live-descriptor
      11. pcap-reader-alive
      12. pcap-reader-datalink
      13. pcap-reader-file
      14. pcap-reader-major
      15. pcap-reader-minor
      16. pcap-reader-snaplen
      17. pcap-reader-swapped
      18. pcap-writer-alive
      19. pcap-writer-datalink
      20. pcap-writer-file
      21. pcap-writer-snaplen
    2. Generic functions and methods
      1. set-filter
      2. set-nonblock
      3. stats
      4. stop
      5. dump
      6. inject
      7. capture
    3. Macros
      1. with-pcap-interface
      2. with-pcap-reader
      3. with-pcap-writer
    4. Conditions
      1. network-interface-error
      2. packet-capture-error
      3. packet-filter-error
      4. block-mode-error
      5. capture-file-error
      6. packet-inject-error
    5. *pcap-version*
    6. find-all-devs
  4. Acknowledgements

 

Download

The current STABLE version is 1.1. You can download it here or install it automatically with ASDF-INSTALL. For feature-requests, questions, patches email me at xristos (AT) suspicious.

Latest DEVELOPMENT version in git. Try it if you are having problems with stable or want packet injection via pcap.


 

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 access on /dev/bpf* devices for OSX/BSD, root/CAP_NET_RAW on Linux).
;; Result list layout mirrors the one explained in pcap_findalldevs(3)
PLOKAMI> (find-all-devs)
(("lo0" NIL 1
  (((:ADDR :AF_INET "127.0.0.1") (:NETMASK :AF_INET "255.0.0.0"))
   ((:ADDR :AF_INET6 "fe80:1::1") (:NETMASK :AF_INET6 "ffff:ffff:ffff:ffff::"))
   ((:ADDR :AF_INET6 "::1")
    (:NETMASK :AF_INET6 "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"))
   ((:ADDR :AF_LINK "lo0"))))
 ("wlt1" NIL 0 (((:ADDR :AF_LINK "wlt1"))))
 ("en1" NIL 0
  (((:ADDR :AF_INET "10.0.3.1") (:NETMASK :AF_INET "255.255.255.0")
    (:BROADADDR :AF_INET "10.0.3.255"))
   ((:ADDR :AF_LINK "en1:0.16.cb.b7.37.39"))))
 ("ppp0" NIL 0
  (((:ADDR :AF_INET "10.0.2.15") (:NETMASK :AF_INET "255.0.0.0")
    (:DSTADDR :AF_INET "10.0.2.2"))
   ((:ADDR :AF_LINK "ppp0"))))
 ("fw0" NIL 0 (((:ADDR :AF_LINK "fw0:0.16.cb.ff.fe.61.7a.b0"))))
 ("en0" NIL 0
  (((:ADDR :AF_INET "192.16.109.30") (:NETMASK :AF_INET "255.255.252.0")
    (:BROADADDR :AF_INET "192.16.111.255"))
   ((:ADDR :AF_LINK "en0:0.16.cb.a5.1e.a2")))))
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)
                    ;; sec and usec: capture timestamp (UNIX epoch time)
                    ;; caplen: size of captured packet
                    ;; len: original size of packet (may be > caplen, depends on snaplen)
                    ;; buffer: byte vector with packet contents
                    (dump writer buffer :length caplen
                          :origlength len :sec sec :usec usec)
                    (format t "Captured ~A bytes [~A]~%" caplen len)))
        (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
                       )))
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

Constructors and readers


[Function]
make-pcap-live interface &key promisc nbio (timeout 100) (snaplen 68) => PCAP-LIVE instance


Create and return a PCAP-LIVE instance. INTERFACE is a string that names the network interface used for capture. If NIL, one is selected automatically from the available ones. PROMISC should be T when capturing in promiscuous mode, NIL otherwise. NBIO should be T when non-blocking operation is required. TIMEOUT should hold read timeout in milliseconds. 0 will wait until a packet arrives. TIMEOUT only takes effect when in blocking mode. SNAPLEN should contain the number of bytes to capture per packet. Default is 68 which should be enough for headers.


[Function]
make-pcap-reader file &key (snaplen 68) => PCAP-READER instance


Create and return a PCAP-READER instance. FILE is the filename to open and read packets from. SNAPLEN should contain the number of bytes to read per packet captured. Default is 68 which should be enough for headers.


[Function]
make-pcap-writer file &key (datalink "EN10MB") (snaplen 68) => PCAP-WRITER instance


Create and return a PCAP-WRITER instance. FILE is the filename to open and dump packets to. DATALINK should contain a string that represents the datalink protocol of the network interface used to capture the packets. Default is Ethernet. SNAPLEN should contain the number of bytes read per packet captured and should be the same as the one used when capturing/reading packets.


[Method]
pcap-live-alive (object pcap-live) => result


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


[Method]
pcap-live-datalink (object pcap-live) => result


Return string representation of the datalink protocol that is used by PCAP-LIVE object.


[Method]
pcap-live-interface (object pcap-live) => result


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


[Method]
pcap-live-promisc (object pcap-live) => result


T if capturing in promiscuous mode.


[Method]
pcap-live-snaplen (object pcap-live) => result


Return snapshot length (how many bytes to capture per packet) for PCAP-LIVE object.


[Method]
pcap-live-timeout (object pcap-live) => result


Read timeout in milliseconds. 0 will wait until a packet arrives. Only takes effect in blocking mode.


[Method]
pcap-live-descriptor (object pcap-live) => result


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


[Method]
pcap-reader-alive (object pcap-reader) => result


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


[Method]
pcap-reader-datalink (object pcap-reader) => result


Return string representation of the datalink protocol that is used by PCAP-READER object.


[Method]
pcap-reader-file (object pcap-reader) => result


File to read packets from.


[Method]
pcap-reader-major (object pcap-reader) => result


Major version of savefile.


[Method]
pcap-reader-minor (object pcap-reader) => result


Minor version of savefile.


[Method]
pcap-reader-snaplen (object pcap-reader) => result


Number of bytes to read per packet processed.


[Method]
pcap-reader-swapped (object pcap-reader) => result


T if savefile uses different byte order from host system.


[Method]
pcap-writer-alive (object pcap-writer) => result


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.


[Method]
pcap-writer-datalink (object pcap-writer) => result


Return string representation of the datalink protocol that is used by PCAP-WRITER object.


[Method]
pcap-writer-file (object pcap-writer) => result


File to write packets to.


[Method]
pcap-writer-snaplen (object pcap-writer) => result


Number of bytes to write per packet processed.

Generic functions and methods


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


Set a packet filter on a PCAP-LIVE or PCAP-READER instance. The filter must be given as a BPF expression in STRING. PACKET-FILTER-ERROR is signalled on failure. A restart, CONTINUE-NO-FILTER, is also setup that can be invoked to continue without the filter setup.


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


Set non-blocking mode if MODE is T, blocking mode if NIL. BLOCK-MODE-ERROR is signalled on failure and a restart is setup, that can be invoked to continue.


[Generic function]
stats pcap-live => result


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 this order). NETWORK-INTERFACE-ERROR is signalled on failure.


[Generic function]
stop pcap-mixin => result


Deallocate resources for PCAP-LIVE, PCAP-READER, PCAP-WRITER instance.


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


Dump a byte vector DATA on PCAP-WRITER instance (which corresponds to a pcap savefile). LENGTH corresponds to the number of bytes captured and is set to the size of DATA when omitted. ORIGLENGTH corresponds to the number of bytes originally present in the packet and is set to LENGTH when omitted. SEC and USEC correspond to seconds/microseconds since the UNIX epoch at the time of packet capture (timeval structure in C) and are set to current values when omitted. CAPTURE-FILE-ERROR is signalled on errors.


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


Inject LENGTH bytes of packet data (size of BUFFER if ommitted). PACKET-INJECT-ERROR is signalled on failure.


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


Only works for PCAP-LIVE or PCAP-READER instances. Capture and process maximum number of PACKETS. Minimum is zero. Return 0 when no packets available (for dumpfiles: when end of file) otherwise return number of packets processed which can be fewer than the maximum given in PACKETS (due to pcap 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 arguments 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 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 signalled for live interfaces and CAPTURE-FILE-ERROR for pcap dumpfiles.

Macros


[Macro]
with-pcap-interface (pcaplive iface &rest options) declaration* statement* => result


Call MAKE-PCAP-LIVE passing IFACE, OPTIONS and store the resulting instance in PCAPLIVE. 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 signalled, skipping the filter setup.


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


Call MAKE-PCAP-READER passing FILE, OPTIONS 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 signalled, skipping the filter setup.


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


Call MAKE-PCAP-WRITER passing FILE, OPTIONS 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.

Conditions


[Condition type]
network-interface-error


Signaled on all network interface errors.


[Condition type]
packet-capture-error


Signaled on error during live packet capture.


[Condition type]
packet-filter-error


Signaled when a Berkeley packet filter could not be established.


[Condition type]
block-mode-error


Signaled on error when changing blocking mode.


[Condition type]
capture-file-error


Signaled on all pcap file errors.


[Condition type]
packet-inject-error


Signaled on errors during packet injection.


[Special variable]
*pcap-version*


Version of native LIBPCAP library.


[Function]
find-all-devs => result


Return a list of all network devices that can be opened for capture. Result list mirrors layout explained in pcap_findalldevs().

 

Acknowledgements

This documentation was prepared with DOCUMENTATION-TEMPLATE.