
This article is the fourth in a multi-part blog series intended to introduceand acquaint the user with Farsight Security’s NMSG suite. This articleintroduces the
libnmsg
C programming API.
Before reading this article, it is recommended that you read the followingFarsight Security Blog articles:
This article, geared towards intermediate-level C programmers, is by no meansan exhaustive API reference. For that, the reader is directed to theaccompanying Doxygen-based API manual.This article covers NMSG (protocol) version
2
and
nmsg
(C library)version
0.9.1
.
The reader is also directed to explore the examples directory in the
nmsg
repository, whereseveral other sample
libnmsg
-based programs will be found.
To demonstrate the some of
libnmsg
‘s functionality, we will examine a simpleexample of how to instantiate an
nmsg
session and plumb data through it. TheC source code for this program,
nmsgpacket
, is detailed below. All of thecode is contained in a single source file that can be compiled into a fullyfunctional program. To build
nmsgpacket
, you’ll need to link against
libnmsg
and
libpcap
. As such, you’ll want to ensure recent versions of bothlibraries are installed in a standard system location (such as
/usr/local/lib
).
nmsgpacket
is a simple program that reads network packets, converts them intoNMSG, and mirrors the payloads to a binary file and to stdout. It requires twocommand-line arguments: the network interface from which to capture packets anda count of the number of packets to capture and convert to NMSG payloads (eachcaptured packet will be converted into a single NMSG payload).
To wit:
$ ./nmsgpacket en0 5
[159] [2015-02-16 20:42:47.295350000] [1:12 base packet] [00000000] [] []
payload_type: IP
payload: <BYTE ARRAY LEN=154>
[159] [2015-02-16 20:42:47.296414000] [1:12 base packet] [00000000] [] []
payload_type: IP
payload: <BYTE ARRAY LEN=154>
[82] [2015-02-16 20:42:48.831159000] [1:12 base packet] [00000000] [] []
payload_type: IP
payload: <BYTE ARRAY LEN=78>
[333] [2015-02-16 20:42:48.832891000] [1:12 base packet] [00000000] [] []
payload_type: IP
payload: <BYTE ARRAY LEN=328>
[82] [2015-02-16 20:42:49.138423000] [1:12 base packet] [00000000] [] []
payload_type: IP
payload: <BYTE ARRAY LEN=78>
We use
nmsgtool
to verify the binary file’s payload contents are the sameas those display on the console:
$ nmsgtool -r nmsgpacket.nmsg
[159] [2015-02-16 20:42:47.295350000] [1:12 base packet] [00000000] [] []
payload_type: IP
payload: <BYTE ARRAY LEN=154>
[159] [2015-02-16 20:42:47.296414000] [1:12 base packet] [00000000] [] []
payload_type: IP
payload: <BYTE ARRAY LEN=154>
[82] [2015-02-16 20:42:48.831159000] [1:12 base packet] [00000000] [] []
payload_type: IP
payload: <BYTE ARRAY LEN=78>
[333] [2015-02-16 20:42:48.832891000] [1:12 base packet] [00000000] [] []
payload_type: IP
payload: <BYTE ARRAY LEN=328>
[82] [2015-02-16 20:42:49.138423000] [1:12 base packet] [00000000] [] []
payload_type: IP
payload: <BYTE ARRAY LEN=78>
The following is the entire 193 line C source code file with in-lineannotations explaining
libnmsg
API calls.
The full source code is available for download fromFarsight Security’s blog-code GitHub page.
The first section contains the source code license and the standard C headerfile include progression:
/*
* Copyright (c) 2015 by Farsight Security, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <pcap.h>
#include <nmsg.h>
#include <nmsg/base/defs.h>
Outlier
nmsg
-specific variables include the following:
nmsg_io_t io
nmsg
nmsg_res res
nmsg
libnmsg
nmsg_res_lookup()
nmsg_input_t input
nmsg
nmsg
nmsg_output_t output
nmsg
nmsg
nmsg_msgmod_t mod
int
main(int argc, char **argv)
{
int fd, rc, count;
nmsg_io_t io;
nmsg_res res;
pcap_t *phandle;
nmsg_pcap_t pcap;
nmsg_msgmod_t mod;
nmsg_input_t input;
nmsg_output_t output;
char errbuf[PCAP_ERRBUF_SIZE];
nmsgpacket
requires two command-line arguments, the network interface andpacket count. Anything more or less and it will throw an error:
if (argc != 3 || ((count = atoi(argv[2])) < 1))
{
printf("%s capture packets and encode as NMSG base:packet\n", argv[0]);
printf("usage: %s interface count\n", argv[0]);
printf(" interface:\tlook for packets here\n");
printf(" count:\tprocess this many payloads (positive integer)\n");
return EXIT_FAILURE;
}
Before any
libnmsg
API functions can be called, the library has to beinitialized:
/* initialize nmsg */
res = nmsg_init();
if (res != nmsg_res_success)
{
fprintf(stderr, "nmsg_init(): failed: %s\n", nmsg_res_lookup(res));
return EXIT_FAILURE;
}
The program employs a key feature of
libnmsg
, the multi-threaded IO engine.It handles the multiplexing of data between inputs and outputs transparentlyfor the application programmer and makes
libnmsg
programming much easier. Itis initialized here:
/* initialize the nmsg io engine */
io = nmsg_io_init();
if (io == NULL)
{
fprintf(stderr, "nmsg_io_init(): failed\n");
return EXIT_FAILURE;
}
nmsgpacket
will be sourcing input as packets from a network interface. Assuch, it will use the NMSG
base
:
packet
message module. It is looked uphere:
/* get the message module for base:packet */
mod = nmsg_msgmod_lookup(NMSG_VENDOR_BASE_ID, NMSG_VENDOR_BASE_PACKET_ID);
if (mod == NULL)
{
fprintf(stderr, "nmsg_msgmod_lookup(): unknown msgmod\n");
return EXIT_FAILURE;
}
The next block of code is straightforward and mostly boilerplate
libpcap
initialization, using the newer style interface API. Noteworthy here is thesnapshot length of
NMSG_DEFAULT_SNAPLEN
(recommended by the
libnmsg
API documentation) and the timeout of 1000 ms (according to
libpcap
APIdocumentation, not setting a timeout can result in undefined behavior.):
/* initialize a pcap handle */
phandle = pcap_create(argv[1], errbuf);
if (phandle == NULL)
{
fprintf(stderr, "pcap_create (%s): %s\n", argv[1], errbuf);
return EXIT_FAILURE;
}
/* set the pcap snapshot length to the nmsg recommended value */
rc = pcap_set_snaplen(phandle, NMSG_DEFAULT_SNAPLEN);
if (rc != 0)
{
fprintf(stderr, "pcap_set_snaplen() failed: %d\n", rc);
return EXIT_FAILURE;
}
/* set the pcap read timeout to 1000ms */
rc = pcap_set_timeout(phandle, 1000);
if (rc != 0)
{
fprintf(stderr, "pcap_set_timeout() failed: %d\n", rc);
return EXIT_FAILURE;
}
/* start packet capture */
rc = pcap_activate(phandle);
if (rc != 0)
{
fprintf(stderr, "pcap_activate() failed with error code: %d\n", rc);
return EXIT_FAILURE;
}
libnmsg
provides a reassembled IP datagram interface to
libpcap
. Thisinterface is quite handy for
libnmsg
programmers that write code to collectnetwork packets but don’t want to have to deal with the minutia of handling IPfragments.
nmsgpacket
uses this interface and opens a new
libnmsg
pcapinput from the previously opened
libpcap
handle. It then instantiates aninput object:
/* initialize a new nmsg pcap input */
pcap = nmsg_pcap_input_open(phandle);
if (pcap == NULL)
{
fprintf(stderr, "nmsg_pcap_input_open() failed\n");
return EXIT_FAILURE;
}
/* initialize a new NMSG pcap input from a pcap descriptor */
input = nmsg_input_open_pcap(pcap, mod);
if (input == NULL)
{
fprintf(stderr, "nmsg_input_open_pcap(): failed\n");
return EXIT_FAILURE;
}
The input object is then attached to the IO engine:
/* add an nmsg input object to the io engine */
res = nmsg_io_add_input(io, input, NULL);
if (res != nmsg_res_success)
{
fprintf(stderr, "nmsg_io_add_intput() failed: %s\n",
nmsg_res_lookup(res));
return EXIT_FAILURE;
}
Next, the program sets up the binary output object. A file is opened and thefile descriptor is used to instantiate a binary file output object which isthen attached to the IO engine:
/* open a new file to write binary NMSGs */
fd = open("nmsgpacket.nmsg", O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd == -1)
{
fprintf(stderr, "open() failed: %s\n", strerror(errno));
return EXIT_FAILURE;
}
/* initialize a new binary file nmsg output */
output = nmsg_output_open_file(fd, NMSG_WBUFSZ_MAX);
if (output == NULL)
{
fprintf(stderr, "nmsg_output_open_file(): failed\n");
return EXIT_FAILURE;
}
/* add an nmsg output object to the io engine */
res = nmsg_io_add_output(io, output, NULL);
if (res != nmsg_res_success)
{
fprintf(stderr, "nmsg_io_add_output() failed: %s\n",
nmsg_res_lookup(res));
return EXIT_FAILURE;
}
Next, the same process is used for a presentation output object (withoutopening a file —
stdout
is already open). The presentation output objectis attached to the IO engine:
/* initialize a new stdout presentation format nmsg output */
output = nmsg_output_open_pres(STDOUT_FILENO);
if (output == NULL)
{
fprintf(stderr, "nmsg_output_open_pres(): failed\n");
return EXIT_FAILURE;
}
/* add an nmsg output object to the io engine */
res = nmsg_io_add_output(io, output, NULL);
if (res != nmsg_res_success)
{
fprintf(stderr, "nmsg_io_add_output() failed: %s\n",
nmsg_res_lookup(res));
return EXIT_FAILURE;
}
First, the IO engine is configured to stop after processing the user specifiednumber of payloads. Next, it is configured to mirror payloads across eachoutput. Important to note here is that mirroring imposes the overhead of aper-output copy for each input payload (striping is default mode of operation).
/* configure the io engine to close after count payloads */
nmsg_io_set_count(io, atoi(argv[2]));
/* configure the io engine to mirror payloads to each output */
nmsg_io_set_output_mode(io, nmsg_io_output_mode_mirror);
Finally, the IO engine is started, which will read from the pcap input andmirror NMSG payloads to both outputs. After
count
payloads are processed,the IO engine is shutdown and resources are freed.
/* start processing nmsg inputs and outputs */
res = nmsg_io_loop(io);
if (res != nmsg_res_success)
{
fprintf(stderr, "nmsg_io_loop() failed: %s\n", nmsg_res_lookup(res));
return EXIT_FAILURE;
}
/* deallocate the resources associated with the io engine */
nmsg_io_destroy(&io);
return EXIT_SUCCESS;
}
The next article in the NMSG series will introduce the
pynmsg
Pythonprogramming API.
Mike Schiffman is a Protocol Legerdemainist for Farsight Security, Inc.
Read the next part in this series: Farsight’s Network Message, Volume 5: The Python Programming API