Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for PACKET_FANOUT on Linux #869

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please explain what the affect of having packet fanout mode?
When would I turn it on and why? Is there is a downside to it? What if every application turned it on?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the AF_PACKET fanout mode, packet reception can be load balanced among processes/threads.

When "configuring" the socket to be used, you would be required to set a group_id, use the same group_id to achieve the required load balancing.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't tell me. Put it in the manpage!

.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)