diff --git a/CMakeLists.txt b/CMakeLists.txt index fdd91800f4..62137878fb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/CREDITS b/CREDITS index d01e832213..7cab8fd832 100644 --- a/CREDITS +++ b/CREDITS @@ -169,6 +169,8 @@ Additional people who have contributed patches (in alphabetical order): Nick Kelsey Nicolas Dade Niko Delarich + Noam Meltzer + N. Leiten nnposter diff --git a/Makefile.in b/Makefile.in index c6ba79faf6..39d665b5ad 100644 --- a/Makefile.in +++ b/Makefile.in @@ -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 \ diff --git a/pcap-int.h b/pcap-int.h index acb038e453..a52ee442d8 100644 --- a/pcap-int.h +++ b/pcap-int.h @@ -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 */ diff --git a/pcap-linux.c b/pcap-linux.c index 3ae93c501b..cef6dccf63 100644 --- a/pcap-linux.c +++ b/pcap-linux.c @@ -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 *); @@ -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; @@ -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; @@ -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. * @@ -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. */ diff --git a/pcap.c b/pcap.c index c27492b312..c4bfaadb7b 100644 --- a/pcap.c +++ b/pcap.c @@ -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; diff --git a/pcap/pcap.h b/pcap/pcap.h index 8182bef44a..6e49f98c42 100644 --- a/pcap/pcap.h +++ b/pcap/pcap.h @@ -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 /* diff --git a/pcap_set_fanout_linux.3pcap b/pcap_set_fanout_linux.3pcap new file mode 100644 index 0000000000..f0297b0a1a --- /dev/null +++ b/pcap_set_fanout_linux.3pcap @@ -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 +#include +.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)