mirror of
https://github.com/zeek/zeek.git
synced 2025-10-02 06:38:20 +00:00
af_packet: Add FANOUT_CBPF and FANOUT_EBPF
This commit is contained in:
parent
79842b25c1
commit
e4aa85d2a6
2 changed files with 230 additions and 221 deletions
|
@ -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, ¤t_hdr, data) )
|
if ( !ApplyBPFFilter(current_filter, ¤t_hdr, data) )
|
||||||
{
|
{
|
||||||
++num_discarded;
|
++num_discarded;
|
||||||
DoneWithPacket();
|
DoneWithPacket();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
pkt->Init(props.link_type, ¤t_hdr.ts, current_hdr.caplen, current_hdr.len, data);
|
pkt->Init(props.link_type, ¤t_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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue