Skip to content

Commit

Permalink
Add support for PACKET_FANOUT on Linux
Browse files Browse the repository at this point in the history
This work is based on PR #674 while taking into consideration the review
comments which caused the PR to be closed by the original author,
@xpahos.
  • Loading branch information
tsnoam committed Sep 2, 2021
1 parent 139a684 commit b578eba
Show file tree
Hide file tree
Showing 8 changed files with 141 additions and 2 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2619,6 +2619,7 @@ set(MAN3PCAP_NOEXPAND
pcap_open_live.3pcap
pcap_set_buffer_size.3pcap
pcap_set_datalink.3pcap
pcap_set_fanout_linux.3pcap
pcap_set_promisc.3pcap
pcap_set_protocol_linux.3pcap
pcap_set_rfmon.3pcap
Expand Down
2 changes: 2 additions & 0 deletions CREDITS
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,8 @@ Additional people who have contributed patches (in alphabetical order):
Nick Kelsey <nickk at silicondust dot com>
Nicolas Dade <ndade at nsd dot dyndns dot org>
Niko Delarich <niko dot delarich at gmail dot com>
Noam Meltzer <noam at cynerio dot co>
<tsnoam at gmail dot com>
N. Leiten <nleiten at sourceforge dot net>
nnposter <nnposter at users dot noreply dot github dot com>
<nvercamm at sourceforge dot net>
Expand Down
1 change: 1 addition & 0 deletions Makefile.in
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ MAN3PCAP_NOEXPAND = \
pcap_open_live.3pcap \
pcap_set_buffer_size.3pcap \
pcap_set_datalink.3pcap \
pcap_set_fanout_linux.3pcap \
pcap_set_promisc.3pcap \
pcap_set_protocol_linux.3pcap \
pcap_set_rfmon.3pcap \
Expand Down
2 changes: 2 additions & 0 deletions pcap-int.h
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,8 @@ struct pcap_opt {
*/
#ifdef __linux__
int protocol; /* protocol to use when creating PF_PACKET socket */
int fanout_enabled; /* If evaluates to TRUE, fanout mode is enabled; default: FALSE. */
int fanout_opt; /* 16 lower bits are the group id; upper bits are the mode. */
#endif
#ifdef _WIN32
int nocapture_local;/* disable NPF loopback */
Expand Down
58 changes: 56 additions & 2 deletions pcap-linux.c
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ static void map_arphrd_to_dlt(pcap_t *, int, const char *, int);
static int pcap_activate_linux(pcap_t *);
static int activate_pf_packet(pcap_t *, int);
static int setup_mmapped(pcap_t *, int *);
static int activate_fanout(pcap_t *);
static int pcap_can_set_rfmon_linux(pcap_t *);
static int pcap_inject_linux(pcap_t *, const void *, int);
static int pcap_stats_linux(pcap_t *, struct pcap_stat *);
Expand Down Expand Up @@ -1076,6 +1077,16 @@ pcap_activate_linux(pcap_t *handle)
goto fail;
}

/*
* Only after we have the socket properly binded and set with the
* correct protocol we can set the FANOUT mode.
*/
if ((status2 = activate_fanout(handle)) != 0) {
status = status2;
goto fail;
}


handle->inject_op = pcap_inject_linux;
handle->setfilter_op = pcap_setfilter_linux;
handle->setdirection_op = pcap_setdirection_linux;
Expand Down Expand Up @@ -2281,8 +2292,8 @@ activate_pf_packet(pcap_t *handle, int is_any_device)
const char *device = handle->opt.device;
int status = 0;
int sock_fd, arptype;
#ifdef HAVE_PACKET_AUXDATA
int val;
#if defined(HAVE_PACKET_AUXDATA)
int val = 0;
#endif
int err = 0;
struct packet_mreq mr;
Expand Down Expand Up @@ -2644,6 +2655,30 @@ activate_pf_packet(pcap_t *handle, int is_any_device)
return status;
}

/*
* Add the packet socket into FANOUT group, if needed.
*
* On success (or if nothing was done), returns 0.
*
* On error, returns -1 and sets handle->errbuf to the appropriate message.
*/
static int
activate_fanout(pcap_t *handle) {
int rc = 0;
#ifdef PACKET_FANOUT
int val = handle->opt.fanout_opt;

if (handle->opt.fanout_enabled &&
setsockopt(handle->fd, SOL_PACKET, PACKET_FANOUT, &val, sizeof(val)) < 0) {
pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE, errno, "setsockopt (PACKET_FANOUT)");
rc = -1;
}
#endif /* PACKET_FANOUT */

return rc;
}


/*
* Attempt to setup memory-mapped access.
*
Expand Down Expand Up @@ -5421,6 +5456,25 @@ pcap_set_protocol_linux(pcap_t *p, int protocol)
return (0);
}

int
pcap_set_fanout_linux(pcap_t *p, int enable, uint16_t mode, uint16_t group_id)
{
#ifdef PACKET_FANOUT
if (pcap_check_activated(p)) {
return (PCAP_ERROR_ACTIVATED);
}

p->opt.fanout_enabled = enable;
p->opt.fanout_opt = (((int)mode) << 16) | ((int)group_id & 0xffff);

return 0;
#else
pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE,
errno, "fanout is not supported");
return -1;
#endif
}

/*
* Libpcap version string.
*/
Expand Down
2 changes: 2 additions & 0 deletions pcap.c
Original file line number Diff line number Diff line change
Expand Up @@ -2520,6 +2520,8 @@ pcap_create_common(char *ebuf, size_t total_size, size_t private_offset)
*/
#ifdef __linux__
p->opt.protocol = 0;
p->opt.fanout_enabled = 0;
p->opt.fanout_opt = 0;
#endif
#ifdef _WIN32
p->opt.nocapture_local = 0;
Expand Down
1 change: 1 addition & 0 deletions pcap/pcap.h
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,7 @@ PCAP_API const char *pcap_tstamp_type_val_to_description(int);
#ifdef __linux__
PCAP_AVAILABLE_1_9
PCAP_API int pcap_set_protocol_linux(pcap_t *, int);
PCAP_API int pcap_set_fanout_linux(pcap_t *, int, uint16_t, uint16_t);
#endif

/*
Expand Down
76 changes: 76 additions & 0 deletions pcap_set_fanout_linux.3pcap
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
.\" Copyright (c) 1994, 1996, 1997
.\" The Regents of the University of California. All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that: (1) source code distributions
.\" retain the above copyright notice and this paragraph in its entirety, (2)
.\" distributions including binary code include the above copyright notice and
.\" this paragraph in its entirety in the documentation or other materials
.\" provided with the distribution, and (3) all advertising materials mentioning
.\" features or use of this software display the following acknowledgement:
.\" ``This product includes software developed by the University of California,
.\" Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
.\" the University nor the names of its contributors may be used to endorse
.\" or promote products derived from this software without specific prior
.\" written permission.
.\" THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
.\" WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
.\" MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
.\"
.TH PCAP_SET_FANOUT 3PCAP "16 April 2020"
.SH NAME
pcap_set_fanout_linux \- set fanout group for load balancing
among processes
.SH SYNOPSIS
.nf
.ft B
#include <linux/if_packet.h>
#include <pcap/pcap.h>
.LP
.ft B
int pcap_set_fanout_linux(pcap_t *p, int enable, uint16_t mode, uint16_t group_id);
.ft
.fi
.SH DESCRIPTION
On Linux network interface devices,
.B pcap_set_fanout_linux()
sets the fanout configuration to be used in the
.BR socket (2)
call to create a capture socket when the handle is activated.
.LP
Using fanout is required if you want to load-balance multiple processes and/or
threads to handle the intercepted traffic. When preparing the socket to be used,
it is required to set a group_id in order to distinguish between different
consumers.
.LP
A single fanout groups load-balances the handling of intercepted traffic.
.LP
It is possible to have any combination of fanout groups or "normal" consumers,
as it is possible with only "normal" consumers ("normal" - non fanout).
.LP
This function is only provided on Linux, and, if it is used on any device other
than a network interface, it will have no effect.
.LP
.IR enable
is a boolean flag to control if fanout is enabled (default is FALSE).
.LP
.IR mode
is the fanout mode and flags to use. More information on the various modes can
be found in
.BR packet (7)\c
\&.
.LP
.IR group_id
is a unique identifier of the socket group.
.LP
.BR pcap_set_fanout_linux()
should not be used in portable code.

.SH RETURN VALUE
Returns 0 on success or
.B PCAP_ERROR_ACTIVATED
if called on a capture handle that has been activated.
.SH SEE ALSO
.BR pcap (3PCAP)
.BR socket (2)
.BR packet (7)

0 comments on commit b578eba

Please sign in to comment.