From 7d821344b6c147a55849e6f8e3652c412b08c80a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Purzy=C5=84ski?= Date: Tue, 21 Apr 2015 11:12:44 +0200 Subject: [PATCH 1/4] netsniff-ng: add packet fanout support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Upstream commit: f00d4d54f28c0374cc57e6ca07dd648d7684c69c -- netsniff-ng: add packet fanout support This work adds packet fanout support to netsniff-ng. Multiple netsniff-ng instances can join the same fanout group with a particular id in order to improve scaling. Based on different fanout disciplines, e.g. distribute to fanout member by packet hash, round-robin, by arrival cpu, by random, by socket rollover (if one members socket queue is full, switch to next one, etc), by hardware queue mapping, traffic can be distributed to one of the fanout members. Moreover, we also allow the user to specify additional aux arguments, e.g. whether to defrag incoming traffic for the fanout group or not, and whether to roll over a socket in case other disciplines than socket rollover have been used. All that is configurable via command line option. Signed-off-by: Michał Purzyński [ dbkm made some bigger changes to get this upstream ready ] Signed-off-by: Daniel Borkmann --- netsniff-ng.c | 50 +++++++++++++++++++++++++++++++++++++++++++++++--- ring_rx.c | 16 ++++++++++++++++ ring_rx.h | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 97 insertions(+), 3 deletions(-) diff --git a/netsniff-ng.c b/netsniff-ng.c index 0d9046c..b238979 100644 --- a/netsniff-ng.c +++ b/netsniff-ng.c @@ -57,14 +57,18 @@ struct ctx { int cpu, rfraw, dump, print_mode, dump_dir, packet_type, verbose; unsigned long kpull, dump_interval, reserve_size, tx_bytes, tx_packets; bool randomize, promiscuous, enforce, jumbo, dump_bpf; - enum pcap_ops_groups pcap; enum dump_mode dump_mode; - uid_t uid; gid_t gid; uint32_t link_type, magic; + enum pcap_ops_groups pcap; + enum dump_mode dump_mode; + uid_t uid; + gid_t gid; + uint32_t link_type, magic; + uint32_t fanout_group, fanout_type; }; static volatile sig_atomic_t sigint = 0; static volatile bool next_dump = false; -static const char *short_options = "d:i:o:rf:MJt:S:k:n:b:HQmcsqXlvhF:RGAP:Vu:g:T:DBU"; +static const char *short_options = "d:i:o:rf:MJt:S:k:n:b:HQmcsqXlvhF:RGAP:Vu:g:T:DBUC:K:L:"; static const struct option long_options[] = { {"dev", required_argument, NULL, 'd'}, {"in", required_argument, NULL, 'i'}, @@ -80,6 +84,9 @@ static const struct option long_options[] = { {"user", required_argument, NULL, 'u'}, {"group", required_argument, NULL, 'g'}, {"magic", required_argument, NULL, 'T'}, + {"fanout-group", required_argument, NULL, 'C'}, + {"fanout-type", required_argument, NULL, 'K'}, + {"fanout-opts", required_argument, NULL, 'L'}, {"rand", no_argument, NULL, 'r'}, {"rfraw", no_argument, NULL, 'R'}, {"mmap", no_argument, NULL, 'm'}, @@ -382,6 +389,7 @@ static void receive_to_xmit(struct ctx *ctx) mmap_rx_ring(rx_sock, &rx_ring); alloc_rx_ring_frames(rx_sock, &rx_ring); bind_rx_ring(rx_sock, &rx_ring, ifindex_in); + join_fanout_group(rx_sock, ctx->fanout_group, ctx->fanout_type); prepare_polling(rx_sock, &rx_poll); set_packet_loss_discard(tx_sock); @@ -924,6 +932,7 @@ static void recv_only_or_dump(struct ctx *ctx) mmap_rx_ring(sock, &rx_ring); alloc_rx_ring_frames(sock, &rx_ring); bind_rx_ring(sock, &rx_ring, ifindex); + join_fanout_group(sock, ctx->fanout_group, ctx->fanout_type); prepare_polling(sock, &rx_poll); dissector_init_all(ctx->print_mode); @@ -1021,12 +1030,15 @@ static void recv_only_or_dump(struct ctx *ctx) static void init_ctx(struct ctx *ctx) { memset(ctx, 0, sizeof(*ctx)); + ctx->uid = getuid(); ctx->uid = getgid(); ctx->cpu = -1; ctx->packet_type = -1; + ctx->fanout_type = PACKET_FANOUT_ROLLOVER; + ctx->magic = ORIGINAL_TCPDUMP_MAGIC; ctx->print_mode = PRINT_NORM; ctx->pcap = PCAP_OPS_SG; @@ -1055,6 +1067,9 @@ static void __noreturn help(void) "Options:\n" " -i|-d|--dev|--in Input source as netdev, pcap or pcap stdin\n" " -o|--out Output sink as netdev, pcap, directory, trafgen, or stdout\n" + " -C|--fanout-group Join packet fanout group\n" + " -K|--fanout-type Apply fanout discipline: hash|lb|cpu|rnd|roll|qm\n" + " -L|--fanout-opts Additional fanout options: defrag|roll\n" " -f|--filter Use BPF filter file from bpfc or tcpdump-like expression\n" " -t|--type Filter for: host|broadcast|multicast|others|outgoing\n" " -F|--interval Dump interval if -o is a dir: KiB/MiB/GiB/s/sec/min/hrs\n" @@ -1180,6 +1195,35 @@ int main(int argc, char **argv) ctx.gid = strtoul(optarg, NULL, 0); ctx.enforce = true; break; + case 'C': + ctx.fanout_group = strtoul(optarg, NULL, 0); + if (ctx.fanout_group == 0) + panic("Non-zero fanout group id required!\n"); + break; + case 'K': + if (!strncmp(optarg, "hash", strlen("hash"))) + ctx.fanout_type = PACKET_FANOUT_HASH; + else if (!strncmp(optarg, "lb", strlen("lb"))) + ctx.fanout_type = PACKET_FANOUT_LB; + else if (!strncmp(optarg, "cpu", strlen("cpu"))) + ctx.fanout_type = PACKET_FANOUT_CPU; + else if (!strncmp(optarg, "rnd", strlen("rnd"))) + ctx.fanout_type = PACKET_FANOUT_RND; + else if (!strncmp(optarg, "roll", strlen("roll"))) + ctx.fanout_type = PACKET_FANOUT_ROLLOVER; + else if (!strncmp(optarg, "qm", strlen("qm"))) + ctx.fanout_type = PACKET_FANOUT_QM; + else + panic("Unkown fanout type!\n"); + break; + case 'L': + if (!strncmp(optarg, "defrag", strlen("defrag"))) + ctx.fanout_type |= PACKET_FANOUT_FLAG_DEFRAG; + else if (!strncmp(optarg, "roll", strlen("roll"))) + ctx.fanout_type |= PACKET_FANOUT_FLAG_ROLLOVER; + else + panic("Unkown fanout option!\n"); + break; case 't': if (!strncmp(optarg, "host", strlen("host"))) ctx.packet_type = PACKET_HOST; diff --git a/ring_rx.c b/ring_rx.c index 3c701da..24a8c6d 100644 --- a/ring_rx.c +++ b/ring_rx.c @@ -131,6 +131,22 @@ void bind_rx_ring(int sock, struct ring *ring, int ifindex) bind_ring_generic(sock, ring, ifindex); } +void join_fanout_group(int sock, uint32_t fanout_group, uint32_t fanout_type) +{ + uint32_t fanout_opt = 0; + int ret; + + if (fanout_group == 0) + return; + + fanout_opt = (fanout_group & 0xffff) | (fanout_type << 16); + + ret = setsockopt(sock, SOL_PACKET, PACKET_FANOUT, &fanout_opt, + sizeof(fanout_opt)); + if (ret < 0) + panic("Cannot set fanout ring mode!\n"); +} + void sock_rx_net_stats(int sock, unsigned long seen) { int ret; diff --git a/ring_rx.h b/ring_rx.h index 8bf439b..256984d 100644 --- a/ring_rx.h +++ b/ring_rx.h @@ -41,4 +41,38 @@ static inline void kernel_may_pull_from_rx_block(struct block_desc *pbd) pbd->h1.block_status = TP_STATUS_KERNEL; } +/* Fanout types. */ + +#ifndef PACKET_FANOUT_HASH +# define PACKET_FANOUT_HASH 0 +#endif + +#ifndef PACKET_FANOUT_LB +# define PACKET_FANOUT_LB 1 +#endif + +#ifndef PACKET_FANOUT_CPU +# define PACKET_FANOUT_CPU 2 +#endif + +#ifndef PACKET_FANOUT_ROLLOVER +# define PACKET_FANOUT_ROLLOVER 3 +#endif + +#ifndef PACKET_FANOUT_RND +# define PACKET_FANOUT_RND 4 +#endif + +#ifndef PACKET_FANOUT_QM +# define PACKET_FANOUT_QM 5 +#endif + +#ifndef PACKET_FANOUT_FLAG_ROLLOVER +# define PACKET_FANOUT_FLAG_ROLLOVER 0x1000 +#endif + +#ifndef PACKET_FANOUT_FLAG_DEFRAG +# define PACKET_FANOUT_FLAG_DEFRAG 0x8000 +#endif + #endif /* RX_RING_H */ -- 2.5.5