Flowgrind
Advanced TCP traffic generator
fg_pcap.c File Reference

Packet capture support for the Flowgrind daemon. More...

#include "config.h"
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <netinet/if_ether.h>
#include <stdbool.h>
#include <string.h>
#include <syslog.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <pthread.h>
#include <errno.h>
#include <pcap.h>
#include <netdb.h>
#include "debug.h"
#include "fg_socket.h"
#include "fg_time.h"
#include "fg_string.h"
#include "fg_log.h"
#include "daemon.h"
#include "fg_pcap.h"

Go to the source code of this file.

Macros

#define PCAP_FILTER   "tcp"
 
#define PCAP_PROMISC   0
 
#define PCAP_SNAPLEN   130
 

Functions

void fg_pcap_cleanup (void *arg)
 Cleanup method to be called after dumping of the specified flow has finished. More...
 
void fg_pcap_go (struct flow *flow)
 Start a tcpdump to capture traffic of the provided flow. More...
 
int fg_pcap_init (void)
 Initialize flowgrind's pcap library. More...
 
static void * fg_pcap_work (void *arg)
 Worker method performing actual packet capturing for the provided flow. More...
 

Variables

static pcap_if_t * alldevs
 
static bool dumping
 
static char errbuf [PCAP_ERRBUF_SIZE] = ""
 
static pthread_barrier_t pcap_barrier
 

Detailed Description

Packet capture support for the Flowgrind daemon.

Definition in file fg_pcap.c.

Macro Definition Documentation

◆ PCAP_FILTER

#define PCAP_FILTER   "tcp"

Definition at line 67 of file fg_pcap.c.

◆ PCAP_PROMISC

#define PCAP_PROMISC   0

Definition at line 70 of file fg_pcap.c.

◆ PCAP_SNAPLEN

#define PCAP_SNAPLEN   130

Definition at line 64 of file fg_pcap.c.

Function Documentation

◆ fg_pcap_cleanup()

void fg_pcap_cleanup ( void *  arg)

Cleanup method to be called after dumping of the specified flow has finished.

It closes the handle to the savefile and the handle to the device whose traffic was captured.

Parameters
[in]argpointer to the flow whose dumping finished

Definition at line 125 of file fg_pcap.c.

126 {
127  /* signature of pthread_create() requires that all arguments must be
128  * passed by reference and cast to (void *) */
129  struct flow *flow = (struct flow *) arg;
130 
131  if (!dumping)
132  return;
133 
134  DEBUG_MSG(LOG_DEBUG, "fg_pcap_cleanup() called for flow %d", flow->id);
135  if (flow->pcap_dumper)
136  pcap_dump_close((pcap_dumper_t *)flow->pcap_dumper);
137  flow->pcap_dumper = NULL;
138 
139  if (flow->pcap_handle)
140  pcap_close((pcap_t *)flow->pcap_handle);
141  flow->pcap_handle = NULL;
142  dumping = false;
143 }

◆ fg_pcap_go()

void fg_pcap_go ( struct flow flow)

Start a tcpdump to capture traffic of the provided flow.

If the flow was not configured for tcp dumping or dumping is already in progress the method will do nothing and return immediately. Otherwise the method blocks until the actual capturing starts. In case an error occurs a log message is created.

Parameters
[in]flowthe flow whose traffic should be captured

Definition at line 314 of file fg_pcap.c.

315 {
316  if (!flow->settings.traffic_dump)
317  return;
318 
319  if (dumping) {
320  logging(LOG_WARNING, "pcap: dumping already in progress on "
321  "this host");
322  return;
323  }
324 
325  DEBUG_MSG(LOG_DEBUG, "called fg_pcap_go() for flow %d", flow->id);
326  dumping = true;
327 
328  int rc = pthread_create(&flow->pcap_thread, NULL, fg_pcap_work,
329  (void*)flow);
330 
331  /* barrier: dump thread is ready (or aborted) */
333 
334  if (rc)
335  logging(LOG_WARNING, "could not start pcap thread: %s",
336  strerror(rc) );
337 }

◆ fg_pcap_init()

int fg_pcap_init ( void  )

Initialize flowgrind's pcap library.

This method fills internal structures on which other methods of this library depend. It is therefore crucial to call it before any call to other methods of this library.

Returns
return 0 for success, or -1 for failure

Definition at line 84 of file fg_pcap.c.

85 {
86  /* initalize *alldevs for later use */
87  if (pcap_findalldevs(&alldevs, errbuf) == -1) {
88  logging(LOG_WARNING,"error in pcap_findalldevs: %s\n", errbuf);
89  return -1;
90  }
91 
92 #ifdef DEBUG
93  for (pcap_if_t *d = alldevs; d; d = d->next) {
94  char *devdes = NULL;
95  if (asprintf(&devdes, "%s: ", d->name) == -1)
96  return -1;
97 
98  for (pcap_addr_t *a = d->addresses; a; a = a->next) {
99  if (!a->addr)
100  continue;
101 
102  asprintf_append(&devdes, "a=%s",
103  fg_nameinfo(a->addr,
104  sizeof(struct sockaddr)));
105  if (a->next)
106  asprintf_append(&devdes, ", ");
107  }
108  DEBUG_MSG(LOG_ERR, "pcap: found pcapable device (%s)", devdes);
109  free(devdes);
110  }
111 #endif /* DEBUG*/
112 
114  return 0;
115 }

◆ fg_pcap_work()

static void* fg_pcap_work ( void *  arg)
static

Worker method performing actual packet capturing for the provided flow.

It prepares the flow for packet capturing by figuring out the correct device, constructing the pcap filter, etc. and finally starts capturing. Synchronizes with fg_pcap_go() after initialization before starting capturing.

Parameters
[in]argpointer to the flow whose traffic should be captured

Definition at line 155 of file fg_pcap.c.

156 {
157  /* note: all the wierd casts in this function are completely useless,
158  * execpt they cirumvent strange compiler warnings because of libpcap
159  * typedef woo's */
160 
161  /* signature of pthread_create() requires that all arguments must be
162  * passed by reference and cast to (void *) */
163  struct flow *flow = (struct flow *) arg;
164 
165  DEBUG_MSG(LOG_DEBUG, "fg_pcap_thread() called for flow %d", flow->id);
166 
167  /* make sure all resources are released when finished */
168  pthread_cleanup_push(fg_pcap_cleanup, (void*) flow);
169 
170  struct addrinfo *ainf = NULL;
171  int rc = getaddrinfo(flow->settings.bind_address, NULL, NULL, &ainf);
172  if (rc) {
173  logging(LOG_WARNING, "getaddrinfo() failed (%s). Eliding "
174  "packet capture for flow", gai_strerror(rc));
175  goto remove;
176  }
177 
178  /* find appropriate (used for test) interface to dump */
179  bool found = false;
180  pcap_if_t *d = NULL;
181  for (d = alldevs; d; d = d->next) {
182  for (pcap_addr_t *a = d->addresses; a; a = a->next) {
183  if (!a->addr)
184  continue;
185  if (sockaddr_compare(a->addr, ainf->ai_addr)) {
186  DEBUG_MSG(LOG_NOTICE, "pcap: data connection "
187  "inbound from %s (%s)", d->name,
188  fg_nameinfo(a->addr,
189  sizeof(struct sockaddr)));
190  found = true;
191  break;
192  }
193  }
194  if (found)
195  break;
196  }
197 
198  if (!found) {
199  logging(LOG_WARNING, "failed to determine interface for data "
200  "connection. No pcap support");
201  goto remove;
202  }
203 
204  flow->pcap_handle = (struct pcap_t *)pcap_open_live(
205  d->name, PCAP_SNAPLEN, PCAP_PROMISC,
206  0, errbuf); /* 0 = no read timeout */
207 
208  if (!flow->pcap_handle) {
209  logging(LOG_WARNING, "failed to init pcap on device %s: %s",
210  d->name, errbuf);
211  goto remove;
212  }
213 
214  uint32_t net = 0, mask = 0;
215  if (pcap_lookupnet(d->name, &net, &mask, errbuf) < 0) {
216  logging(LOG_WARNING, "pcap: netmask lookup failed: %s", errbuf);
217  goto remove;
218  }
219 
220  /* we rely on a non-blocking dispatch loop */
221  if (pcap_setnonblock((pcap_t *)flow->pcap_handle, 1, errbuf) < 0) {
222  logging(LOG_WARNING, "pcap: failed to set non-blocking: %s",
223  errbuf);
224  goto remove;
225  }
226 
227  /* compile filter */
228  struct bpf_program pcap_program;
229  if (pcap_compile((pcap_t *)flow->pcap_handle,
230  &pcap_program, PCAP_FILTER, 1, mask) < 0) {
231  logging(LOG_WARNING, "pcap: failed compiling filter '%s': %s",
232  PCAP_FILTER, pcap_geterr((pcap_t *)flow->pcap_handle));
233  goto remove;
234  }
235 
236  /* attach filter to interface */
237  if (pcap_setfilter((pcap_t *)flow->pcap_handle,
238  &pcap_program) < 0) {
239  logging(LOG_WARNING, "pcap: failed to set filter: %s",
240  pcap_geterr((pcap_t *)flow->pcap_handle));
241  goto remove;
242  }
243 
244  /* generate a nice filename */
245  char *dump_filename = NULL;
246 
247  /* dir and prefix */
248  if (dump_dir)
249  asprintf_append(&dump_filename, "%s", dump_dir);
250  if (dump_prefix)
251  asprintf_append(&dump_filename, "%s", dump_prefix);
252 
253  /* timestamp - we need to use the thread-safe version ctimenow_r() */
254  char timestamp[30] = "";
255  ctimenow_r(timestamp, sizeof(timestamp), false);
256  asprintf_append(&dump_filename, "%s", timestamp);
257 
258  /* hostname */
259  char hostname[128] = "";
260  if (!gethostname(hostname, sizeof(hostname)))
261  asprintf_append(&dump_filename, "-%s", hostname);
262 
263  /* interface and suffix */
264  asprintf_append(&dump_filename, "-%s.pcap", d->name);
265 
266  DEBUG_MSG(LOG_NOTICE, "dumping to \"%s\"", dump_filename);
267 
268  flow->pcap_dumper = (struct pcap_dumper_t *)pcap_dump_open(
269  (pcap_t *)flow->pcap_handle, dump_filename);
270  free(dump_filename);
271 
272  if (!flow->pcap_dumper) {
273  logging(LOG_WARNING, "pcap: failed to open dump file writing: %s",
274  pcap_geterr((pcap_t *)flow->pcap_handle));
275  goto remove;
276  }
277 
278  /* barrier: dump is ready */
280 
281  for (;;) {
282  int rc = pcap_dispatch((pcap_t *)flow->pcap_handle, -1,
283  &pcap_dump, (u_char *)flow->pcap_dumper);
284 
285  if (rc < 0) {
286  logging(LOG_WARNING, "pcap_dispatch() failed. Packet "
287  "dumping stopped for flow %d", flow->id);
288  /* cleanup automatically called */
289  pthread_exit(0);
290  }
291 #ifdef DEBUG
292  struct pcap_stat p_stats;
293  pcap_stats((pcap_t *)flow->pcap_handle, &p_stats);
294 #endif /* DEBUG */
295  DEBUG_MSG(LOG_NOTICE, "pcap: finished dumping %d packets for "
296  "flow %d", rc, flow->id);
297  DEBUG_MSG(LOG_NOTICE, "pcap: %d packets received by filter for "
298  "flow %d", p_stats.ps_recv, flow->id);
299  DEBUG_MSG(LOG_NOTICE, "pcap: %d packets dropped by kernel for "
300  "flow %d", p_stats.ps_drop, flow->id);
301  if (rc == 0)
302  /* if no packets are received try if we should cancel */
303  pthread_testcancel();
304  }
305 
306 remove: ;
307 
308  pthread_cleanup_pop(1);
309 
311  return 0;
312 }

Variable Documentation

◆ alldevs

pcap_if_t* alldevs
static

Definition at line 79 of file fg_pcap.c.

◆ dumping

bool dumping
static

Definition at line 82 of file fg_pcap.c.

◆ errbuf

char errbuf[PCAP_ERRBUF_SIZE] = ""
static

Definition at line 73 of file fg_pcap.c.

◆ pcap_barrier

pthread_barrier_t pcap_barrier
static

Definition at line 76 of file fg_pcap.c.

DEBUG_MSG
#define DEBUG_MSG(LVL, MSG,...)
Print debug message to standard error.
Definition: debug.h:49
pthread_barrier_wait
int pthread_barrier_wait(pthread_barrier_t *barrier)
Synchronizes participating threads at the barrier referenced by barrier.
Definition: fg_barrier.c:80
alldevs
static pcap_if_t * alldevs
Definition: fg_pcap.c:79
fg_pcap_work
static void * fg_pcap_work(void *arg)
Worker method performing actual packet capturing for the provided flow.
Definition: fg_pcap.c:155
flow
Definition: daemon.h:73
pthread_barrier_init
int pthread_barrier_init(pthread_barrier_t *barrier, const pthread_barrierattr_t *attr, unsigned count)
Allocates resources required to use the barrier referenced by barrier.
Definition: fg_barrier.c:37
pcap_barrier
static pthread_barrier_t pcap_barrier
Definition: fg_pcap.c:76
logging
void logging(int priority, const char *fmt,...)
Definition: fg_log.c:69
flow::settings
struct flow_settings settings
Definition: daemon.h:83
flow::pcap_dumper
struct pcap_dumper_t * pcap_dumper
Definition: daemon.h:163
fg_pcap_cleanup
void fg_pcap_cleanup(void *arg)
Cleanup method to be called after dumping of the specified flow has finished.
Definition: fg_pcap.c:125
errbuf
static char errbuf[PCAP_ERRBUF_SIZE]
Definition: fg_pcap.c:73
flow_settings::traffic_dump
int traffic_dump
Dump traffic using libpcap (option -M).
Definition: common.h:204
dump_dir
char * dump_dir
Definition: daemon.c:91
dumping
static bool dumping
Definition: fg_pcap.c:82
PCAP_FILTER
#define PCAP_FILTER
Definition: fg_pcap.c:67
PCAP_SNAPLEN
#define PCAP_SNAPLEN
Definition: fg_pcap.c:64
dump_prefix
char * dump_prefix
Definition: daemon.c:90
sockaddr_compare
char sockaddr_compare(const struct sockaddr *a, const struct sockaddr *b)
Definition: fg_socket.c:389
asprintf_append
int asprintf_append(char **strp, const char *fmt,...)
Definition: fg_string.c:98
flow::id
int id
Definition: daemon.h:75
fg_nameinfo
const char * fg_nameinfo(const struct sockaddr *sa, socklen_t salen)
Definition: fg_socket.c:374
flow::pcap_handle
struct pcap_t * pcap_handle
Definition: daemon.h:162
ctimenow_r
const char * ctimenow_r(char *buf, size_t size, bool ns)
Returns the current wall-clock time as null-terminated string.
Definition: fg_time.c:47
flow::pcap_thread
pthread_t pcap_thread
Definition: daemon.h:161
flow_settings::bind_address
char bind_address[1000]
The interface address for the flow (used by daemon).
Definition: common.h:183
PCAP_PROMISC
#define PCAP_PROMISC
Definition: fg_pcap.c:70