af_packet: Add FANOUT_CBPF and FANOUT_EBPF

This commit is contained in:
Johanna Amann 2021-05-10 10:52:02 +01:00 committed by Tim Wojtulewicz
parent 79842b25c1
commit e4aa85d2a6
2 changed files with 230 additions and 221 deletions

View file

@ -1,4 +1,3 @@
#include "zeek/zeek-config.h" #include "zeek/zeek-config.h"
#include "AF_Packet.h" #include "AF_Packet.h"
@ -9,292 +8,300 @@
using namespace af_packet::iosource::pktsrc; using namespace af_packet::iosource::pktsrc;
AF_PacketSource::~AF_PacketSource() AF_PacketSource::~AF_PacketSource()
{ {
Close(); Close();
} }
AF_PacketSource::AF_PacketSource(const std::string& path, bool is_live) AF_PacketSource::AF_PacketSource(const std::string& path, bool is_live)
{ {
if ( ! is_live ) if ( ! is_live )
Error("AF_Packet source does not support offline input"); Error("AF_Packet source does not support offline input");
current_filter = -1; current_filter = -1;
props.path = path; props.path = path;
props.is_live = is_live; props.is_live = is_live;
} }
void AF_PacketSource::Open() void AF_PacketSource::Open()
{ {
uint64_t buffer_size = zeek::BifConst::AF_Packet::buffer_size; uint64_t buffer_size = zeek::BifConst::AF_Packet::buffer_size;
bool enable_hw_timestamping = zeek::BifConst::AF_Packet::enable_hw_timestamping; bool enable_hw_timestamping = zeek::BifConst::AF_Packet::enable_hw_timestamping;
bool enable_fanout = zeek::BifConst::AF_Packet::enable_fanout; bool enable_fanout = zeek::BifConst::AF_Packet::enable_fanout;
bool enable_defrag = zeek::BifConst::AF_Packet::enable_defrag; bool enable_defrag = zeek::BifConst::AF_Packet::enable_defrag;
socket_fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); socket_fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if ( socket_fd < 0 ) if ( socket_fd < 0 )
{ {
Error(errno ? strerror(errno) : "unable to create socket"); Error(errno ? strerror(errno) : "unable to create socket");
return; return;
} }
// Create RX-ring // Create RX-ring
try { try {
rx_ring = new RX_Ring(socket_fd, buffer_size); rx_ring = new RX_Ring(socket_fd, buffer_size);
} catch (RX_RingException& e) { } catch (RX_RingException& e) {
Error(errno ? strerror(errno) : "unable to create RX-ring"); Error(errno ? strerror(errno) : "unable to create RX-ring");
close(socket_fd); close(socket_fd);
return; return;
} }
// Setup interface // Setup interface
if ( ! BindInterface() ) if ( ! BindInterface() )
{ {
Error(errno ? strerror(errno) : "unable to bind to interface"); Error(errno ? strerror(errno) : "unable to bind to interface");
close(socket_fd); close(socket_fd);
return; return;
} }
if ( ! EnablePromiscMode() ) if ( ! EnablePromiscMode() )
{ {
Error(errno ? strerror(errno) : "unable enter promiscious mode"); Error(errno ? strerror(errno) : "unable enter promiscious mode");
close(socket_fd); close(socket_fd);
return; return;
} }
if ( ! ConfigureFanoutGroup(enable_fanout) ) if ( ! ConfigureFanoutGroup(enable_fanout) )
{ {
Error(errno ? strerror(errno) : "failed to join fanout group"); Error(errno ? strerror(errno) : "failed to join fanout group");
close(socket_fd); close(socket_fd);
return; return;
} }
if ( ! ConfigureHWTimestamping(enable_hw_timestamping) ) if ( ! ConfigureHWTimestamping(enable_hw_timestamping) )
{ {
Error(errno ? strerror(errno) : "failed to configure hardware timestamping"); Error(errno ? strerror(errno) : "failed to configure hardware timestamping");
close(socket_fd); close(socket_fd);
return; return;
} }
props.netmask = NETMASK_UNKNOWN; props.netmask = NETMASK_UNKNOWN;
props.selectable_fd = socket_fd; props.selectable_fd = socket_fd;
props.is_live = true; props.is_live = true;
props.link_type = DLT_EN10MB; // Ethernet headers props.link_type = DLT_EN10MB; // Ethernet headers
stats.received = stats.dropped = stats.link = stats.bytes_received = 0; stats.received = stats.dropped = stats.link = stats.bytes_received = 0;
num_discarded = 0; num_discarded = 0;
Opened(props); Opened(props);
} }
inline bool AF_PacketSource::BindInterface() inline bool AF_PacketSource::BindInterface()
{ {
struct ifreq ifr; struct ifreq ifr;
struct sockaddr_ll saddr_ll; struct sockaddr_ll saddr_ll;
int ret; int ret;
memset(&ifr, 0, sizeof(ifr)); memset(&ifr, 0, sizeof(ifr));
snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", props.path.c_str()); snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", props.path.c_str());
ret = ioctl(socket_fd, SIOCGIFINDEX, &ifr); ret = ioctl(socket_fd, SIOCGIFINDEX, &ifr);
if ( ret < 0 ) if ( ret < 0 )
return false; return false;
memset(&saddr_ll, 0, sizeof(saddr_ll)); memset(&saddr_ll, 0, sizeof(saddr_ll));
saddr_ll.sll_family = AF_PACKET; saddr_ll.sll_family = AF_PACKET;
saddr_ll.sll_protocol = htons(ETH_P_ALL); saddr_ll.sll_protocol = htons(ETH_P_ALL);
saddr_ll.sll_ifindex = ifr.ifr_ifindex; saddr_ll.sll_ifindex = ifr.ifr_ifindex;
ret = bind(socket_fd, (struct sockaddr *) &saddr_ll, sizeof(saddr_ll)); ret = bind(socket_fd, (struct sockaddr *) &saddr_ll, sizeof(saddr_ll));
return (ret >= 0); return (ret >= 0);
} }
inline bool AF_PacketSource::EnablePromiscMode() inline bool AF_PacketSource::EnablePromiscMode()
{ {
struct ifreq ifr; struct ifreq ifr;
struct packet_mreq mreq; struct packet_mreq mreq;
int ret; int ret;
memset(&ifr, 0, sizeof(ifr)); memset(&ifr, 0, sizeof(ifr));
snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", props.path.c_str()); snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", props.path.c_str());
ret = ioctl(socket_fd, SIOCGIFINDEX, &ifr); ret = ioctl(socket_fd, SIOCGIFINDEX, &ifr);
if ( ret < 0 ) if ( ret < 0 )
return false; return false;
memset(&mreq, 0, sizeof(mreq)); memset(&mreq, 0, sizeof(mreq));
mreq.mr_ifindex = ifr.ifr_ifindex; mreq.mr_ifindex = ifr.ifr_ifindex;
mreq.mr_type = PACKET_MR_PROMISC; mreq.mr_type = PACKET_MR_PROMISC;
ret = setsockopt(socket_fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq)); ret = setsockopt(socket_fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
return (ret >= 0); return (ret >= 0);
} }
inline bool AF_PacketSource::ConfigureFanoutGroup(bool enabled, bool defrag) inline bool AF_PacketSource::ConfigureFanoutGroup(bool enabled, bool defrag)
{ {
if ( enabled ) if ( enabled )
{ {
uint32_t fanout_arg, fanout_id; uint32_t fanout_arg, fanout_id;
int ret; int ret;
fanout_id = zeek::BifConst::AF_Packet::fanout_id; fanout_id = zeek::BifConst::AF_Packet::fanout_id;
fanout_arg = ((fanout_id & 0xffff) | (GetFanoutMode(defrag) << 16)); fanout_arg = ((fanout_id & 0xffff) | (GetFanoutMode(defrag) << 16));
ret = setsockopt(socket_fd, SOL_PACKET, PACKET_FANOUT, ret = setsockopt(socket_fd, SOL_PACKET, PACKET_FANOUT,
&fanout_arg, sizeof(fanout_arg)); &fanout_arg, sizeof(fanout_arg));
if ( ret < 0 ) if ( ret < 0 )
return false; return false;
} }
return true; return true;
} }
inline bool AF_PacketSource::ConfigureHWTimestamping(bool enabled) inline bool AF_PacketSource::ConfigureHWTimestamping(bool enabled)
{ {
if ( enabled ) if ( enabled )
{ {
struct ifreq ifr; struct ifreq ifr;
struct hwtstamp_config hwts_cfg; struct hwtstamp_config hwts_cfg;
int ret, opt; int ret, opt;
memset(&hwts_cfg, 0, sizeof(hwts_cfg)); memset(&hwts_cfg, 0, sizeof(hwts_cfg));
hwts_cfg.tx_type = HWTSTAMP_TX_OFF; hwts_cfg.tx_type = HWTSTAMP_TX_OFF;
hwts_cfg.rx_filter = HWTSTAMP_FILTER_ALL; hwts_cfg.rx_filter = HWTSTAMP_FILTER_ALL;
memset(&ifr, 0, sizeof(ifr)); memset(&ifr, 0, sizeof(ifr));
snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", props.path.c_str()); snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", props.path.c_str());
ifr.ifr_data = &hwts_cfg; ifr.ifr_data = &hwts_cfg;
ret = ioctl(socket_fd, SIOCSHWTSTAMP, &ifr); ret = ioctl(socket_fd, SIOCSHWTSTAMP, &ifr);
if ( ret < 0 ) if ( ret < 0 )
return false; return false;
opt = SOF_TIMESTAMPING_RAW_HARDWARE | SOF_TIMESTAMPING_RX_HARDWARE; opt = SOF_TIMESTAMPING_RAW_HARDWARE | SOF_TIMESTAMPING_RX_HARDWARE;
ret = setsockopt(socket_fd, SOL_PACKET, PACKET_TIMESTAMP, ret = setsockopt(socket_fd, SOL_PACKET, PACKET_TIMESTAMP,
&opt, sizeof(opt)); &opt, sizeof(opt));
if( ret < 0 ) if( ret < 0 )
return false; return false;
} }
return true; return true;
} }
inline uint32_t AF_PacketSource::GetFanoutMode(bool defrag) inline uint32_t AF_PacketSource::GetFanoutMode(bool defrag)
{ {
uint32_t fanout_mode; uint32_t fanout_mode;
switch ( zeek::BifConst::AF_Packet::fanout_mode->AsEnum() ) { switch ( zeek::BifConst::AF_Packet::fanout_mode->AsEnum() ) {
case BifEnum::AF_Packet::FANOUT_CPU: fanout_mode = PACKET_FANOUT_CPU; case BifEnum::AF_Packet::FANOUT_CPU: fanout_mode = PACKET_FANOUT_CPU;
break; break;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0) #ifdef PACKET_FANOUT_QM
case BifEnum::AF_Packet::FANOUT_QM: fanout_mode = PACKET_FANOUT_QM; case BifEnum::AF_Packet::FANOUT_QM: fanout_mode = PACKET_FANOUT_QM;
break; break;
#endif #endif
default: fanout_mode = PACKET_FANOUT_HASH; #ifdef PACKET_FANOUT_CBPF
break; case BifEnum::AF_Packet::FANOUT_CBPF: fanout_mode = PACKET_FANOUT_CBPF;
} break;
#endif
#ifdef PACKET_FANOUT_EBPF
case BifEnum::AF_Packet::FANOUT_EBPF: fanout_mode = PACKET_FANOUT_EBPF;
break;
#endif
default: fanout_mode = PACKET_FANOUT_HASH;
break;
}
if ( defrag ) if ( defrag )
fanout_mode |= PACKET_FANOUT_FLAG_DEFRAG; fanout_mode |= PACKET_FANOUT_FLAG_DEFRAG;
return fanout_mode; return fanout_mode;
} }
void AF_PacketSource::Close() void AF_PacketSource::Close()
{ {
if ( ! socket_fd ) if ( ! socket_fd )
return; return;
delete rx_ring; delete rx_ring;
close(socket_fd); close(socket_fd);
socket_fd = 0; socket_fd = 0;
Closed(); Closed();
} }
bool AF_PacketSource::ExtractNextPacket(zeek::Packet* pkt) bool AF_PacketSource::ExtractNextPacket(zeek::Packet* pkt)
{ {
if ( ! socket_fd ) if ( ! socket_fd )
return false; return false;
struct tpacket3_hdr *packet = 0; struct tpacket3_hdr *packet = 0;
const u_char *data; const u_char *data;
struct timeval ts; struct timeval ts;
while ( true ) while ( true )
{ {
if ( ! rx_ring->GetNextPacket(&packet) ) if ( ! rx_ring->GetNextPacket(&packet) )
return false; return false;
current_hdr.ts.tv_sec = packet->tp_sec; current_hdr.ts.tv_sec = packet->tp_sec;
current_hdr.ts.tv_usec = packet->tp_nsec / 1000; current_hdr.ts.tv_usec = packet->tp_nsec / 1000;
current_hdr.caplen = packet->tp_snaplen; current_hdr.caplen = packet->tp_snaplen;
current_hdr.len = packet->tp_len; current_hdr.len = packet->tp_len;
data = (u_char *) packet + packet->tp_mac; data = (u_char *) packet + packet->tp_mac;
if ( !ApplyBPFFilter(current_filter, &current_hdr, data) ) if ( !ApplyBPFFilter(current_filter, &current_hdr, data) )
{ {
++num_discarded; ++num_discarded;
DoneWithPacket(); DoneWithPacket();
continue; continue;
} }
pkt->Init(props.link_type, &current_hdr.ts, current_hdr.caplen, current_hdr.len, data); pkt->Init(props.link_type, &current_hdr.ts, current_hdr.caplen, current_hdr.len, data);
if ( current_hdr.len == 0 || current_hdr.caplen == 0 ) if ( current_hdr.len == 0 || current_hdr.caplen == 0 )
{ {
Weird("empty_af_packet_header", pkt); Weird("empty_af_packet_header", pkt);
return false; return false;
} }
stats.received++; stats.received++;
stats.bytes_received += current_hdr.len; stats.bytes_received += current_hdr.len;
return true; return true;
} }
return false; return false;
} }
void AF_PacketSource::DoneWithPacket() void AF_PacketSource::DoneWithPacket()
{ {
rx_ring->ReleasePacket(); rx_ring->ReleasePacket();
} }
bool AF_PacketSource::PrecompileFilter(int index, const std::string& filter) bool AF_PacketSource::PrecompileFilter(int index, const std::string& filter)
{ {
return PktSrc::PrecompileBPFFilter(index, filter); return PktSrc::PrecompileBPFFilter(index, filter);
} }
bool AF_PacketSource::SetFilter(int index) bool AF_PacketSource::SetFilter(int index)
{ {
current_filter = index; current_filter = index;
return true; return true;
} }
void AF_PacketSource::Statistics(Stats* s) void AF_PacketSource::Statistics(Stats* s)
{ {
if ( ! socket_fd ) if ( ! socket_fd )
{ {
s->received = s->bytes_received = s->link = s->dropped = 0; s->received = s->bytes_received = s->link = s->dropped = 0;
return; return;
} }
struct tpacket_stats_v3 tp_stats; struct tpacket_stats_v3 tp_stats;
socklen_t tp_stats_len = sizeof (struct tpacket_stats_v3); socklen_t tp_stats_len = sizeof (struct tpacket_stats_v3);
int ret; int ret;
ret = getsockopt(socket_fd, SOL_PACKET, PACKET_STATISTICS, &tp_stats, &tp_stats_len); ret = getsockopt(socket_fd, SOL_PACKET, PACKET_STATISTICS, &tp_stats, &tp_stats_len);
if ( ret < 0 ) if ( ret < 0 )
{ {
Error(errno ? strerror(errno) : "unable to retrieve statistics"); Error(errno ? strerror(errno) : "unable to retrieve statistics");
s->received = s->bytes_received = s->link = s->dropped = 0; s->received = s->bytes_received = s->link = s->dropped = 0;
return; return;
} }
stats.link += tp_stats.tp_packets; stats.link += tp_stats.tp_packets;
stats.dropped += tp_stats.tp_drops; stats.dropped += tp_stats.tp_drops;
memcpy(s, &stats, sizeof(Stats)); memcpy(s, &stats, sizeof(Stats));
} }
zeek::iosource::PktSrc* AF_PacketSource::InstantiateAF_Packet(const std::string& path, bool is_live) zeek::iosource::PktSrc* AF_PacketSource::InstantiateAF_Packet(const std::string& path, bool is_live)
{ {
return new AF_PacketSource(path, is_live); return new AF_PacketSource(path, is_live);
} }

View file

@ -7,6 +7,8 @@ enum FanoutMode %{
FANOUT_HASH, # PACKET_FANOUT_HASH FANOUT_HASH, # PACKET_FANOUT_HASH
FANOUT_CPU, # PACKET_FANOUT_CPU FANOUT_CPU, # PACKET_FANOUT_CPU
FANOUT_QM, # PACKET_FANOUT_QM FANOUT_QM, # PACKET_FANOUT_QM
FANOUT_CBPF, # PACKET_FANOUT_CBPF
FANOUT_EBPF, # PACKET_FANOUT_EBPF
%} %}
const buffer_size: count; const buffer_size: count;