├── src └── nethuns │ ├── sockets │ ├── xdp │ ├── netmap_pkthdr.h │ ├── xdp_pkthdr.h │ ├── types.h │ ├── xsk_ext.h │ ├── base.h │ ├── libpcap.h │ ├── netmap.h │ ├── xsk_ext.c │ ├── ring.h │ ├── xdp.h │ ├── tpacket_v3.h │ └── libpcap.c │ ├── network │ ├── udp.h │ ├── ipv6.h │ ├── endian.h │ ├── tcp.h │ ├── ipv4.h │ ├── ether.h │ ├── icmp.h │ └── icmp6.h │ ├── nethuns.h │ ├── misc │ ├── macro.h │ └── compiler.h │ ├── version.c │ ├── define.h │ ├── version.c.in │ ├── vlan.h │ ├── global.h │ ├── types.h │ ├── global.c │ ├── nethuns.c │ ├── queue.h │ └── api.h ├── ebpf ├── clear.sh ├── net_xdp.c └── Makefile ├── tools ├── nethuns-dump │ ├── compile_commands.json │ ├── hdr │ │ ├── run.h │ │ ├── stats.h │ │ ├── dump.h │ │ └── options.h │ ├── src │ │ ├── main.c │ │ └── options.c │ └── CMakeLists.txt └── nethuns-gen │ ├── hdr │ ├── options.hpp │ ├── rate_limiter.hpp │ ├── netaddr.hpp │ ├── pretty.hpp │ ├── generator.hpp │ ├── affinity.hpp │ └── packet.hpp │ ├── src │ ├── packets.cpp │ ├── main.cpp │ └── options.cpp │ └── CMakeLists.txt ├── .gitmodules ├── .gitignore ├── test ├── src │ ├── pcap-version.c │ ├── version.c │ ├── pure-meter-pcap.cpp │ ├── pure-forward-pcap.cpp │ ├── pure-meter-pcap-mt.cpp │ ├── pure-forward-pcap-mt.cpp │ ├── file-pcap-mt.cpp │ ├── meter-mt.cpp │ ├── capture.c │ ├── forward.cpp │ ├── forward-mt.cpp │ ├── filter.cpp │ ├── file-pcap.cpp │ └── send.cpp └── CMakeLists.txt ├── scripts ├── make_revision.sh ├── build_test.hs └── Nethuns.cmake ├── LICENSE ├── misc ├── kselftest.h ├── psock_lib.h ├── example.c └── example3.cpp └── CMakeLists.txt /src/nethuns/sockets/xdp: -------------------------------------------------------------------------------- 1 | ../../../libbpf/src -------------------------------------------------------------------------------- /ebpf/clear.sh: -------------------------------------------------------------------------------- 1 | ip link set dev $1 xdp off 2 | -------------------------------------------------------------------------------- /tools/nethuns-dump/compile_commands.json: -------------------------------------------------------------------------------- 1 | BUILD/compile_commands.json -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "libbpf"] 2 | path = libbpf 3 | url = https://github.com/libbpf/libbpf.git 4 | -------------------------------------------------------------------------------- /tools/nethuns-dump/hdr/run.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "hdr/options.h" 4 | int 5 | run(struct options *opt); 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | BUILD/ 3 | build/ 4 | build-clang/ 5 | .DS_Store 6 | hdr/version.hpp 7 | CMakeFiles 8 | CMakeCache.txt 9 | cmake_install.cmake 10 | -------------------------------------------------------------------------------- /tools/nethuns-dump/hdr/stats.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | struct stats { 6 | uint64_t pkt_count; 7 | uint64_t byte_count; 8 | } __attribute((aligned(64))); -------------------------------------------------------------------------------- /tools/nethuns-dump/hdr/dump.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | void dump_frame(nethuns_pkthdr_t const *hdr, const unsigned char *frame, const char *dev, int queue, bool verbose); 5 | -------------------------------------------------------------------------------- /src/nethuns/network/udp.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | struct udp_hdr 9 | { 10 | uint16_t source; 11 | uint16_t dest; 12 | uint16_t len; 13 | uint16_t check; 14 | } __attribute__((packed, aligned(2))); 15 | -------------------------------------------------------------------------------- /src/nethuns/nethuns.h: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Larthia, University of Pisa. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | #pragma once 6 | #include "sockets/ring.h" 7 | #include "api.h" 8 | #include "stub.h" 9 | #include "vlan.h" 10 | -------------------------------------------------------------------------------- /test/src/pcap-version.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2021 Larthia, University of Pisa. All rights reserved. 3 | * Use of this source code is governed by a BSD-style 4 | * license that can be found in the LICENSE file. 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | int 12 | main(int argc, char *argv[]) 13 | { 14 | printf("version: %s\n", pcap_lib_version()); 15 | } 16 | -------------------------------------------------------------------------------- /src/nethuns/sockets/netmap_pkthdr.h: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Larthia, University of Pisa. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | 10 | struct netmap_pkthdr { 11 | struct timeval ts; 12 | uint32_t len; 13 | uint32_t caplen; 14 | uint32_t buf_idx; 15 | }; 16 | -------------------------------------------------------------------------------- /src/nethuns/sockets/xdp_pkthdr.h: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Larthia, University of Pisa. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | #pragma once 6 | 7 | #include 8 | 9 | struct xdp_pkthdr { 10 | uint32_t sec; 11 | uint32_t nsec; 12 | uint32_t len; 13 | uint32_t snaplen; 14 | unsigned char *packet; 15 | uint64_t addr; 16 | }; 17 | -------------------------------------------------------------------------------- /ebpf/net_xdp.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | struct { 5 | __uint(type, BPF_MAP_TYPE_XSKMAP); 6 | __uint(max_entries, 1); 7 | __uint(key_size, sizeof(int)); 8 | __uint(value_size, sizeof(int)); 9 | 10 | } xsk_map SEC(".maps"); 11 | 12 | SEC("xdp_sock") 13 | int xdp_sock_prog(struct xdp_md *ctx) 14 | { 15 | return bpf_redirect_map(&xsk_map, 0, 0); 16 | } 17 | 18 | char _license[] SEC("license") = "GPL"; 19 | -------------------------------------------------------------------------------- /test/src/version.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2021 Larthia, University of Pisa. All rights reserved. 3 | * Use of this source code is governed by a BSD-style 4 | * license that can be found in the LICENSE file. 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | 12 | 13 | 14 | int 15 | main(int argc, char *argv[]) 16 | { 17 | printf("version_full: %s\n", nethuns_version_full()); 18 | printf("version: %s\n", nethuns_version()); 19 | } 20 | -------------------------------------------------------------------------------- /tools/nethuns-dump/hdr/options.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | #include 5 | 6 | #define MAX_DEVICES 128 7 | 8 | struct dev_queue { 9 | const char *name; 10 | int queue; 11 | }; 12 | 13 | struct options { 14 | struct dev_queue dev[MAX_DEVICES]; 15 | int num_devs; 16 | uint64_t count; 17 | bool verbose; 18 | int meter; 19 | struct nethuns_socket_options sopt; 20 | }; 21 | 22 | extern struct options parse_opt(int argc, char *argv[]); 23 | 24 | extern void validate_options(const struct options *opt); 25 | -------------------------------------------------------------------------------- /tools/nethuns-gen/hdr/options.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | #include "hdr/generator.hpp" 5 | 6 | struct options { 7 | std::vector generators; 8 | }; 9 | 10 | constexpr unsigned int bitfield(bool first) { 11 | return (first ? 1u : 0u); 12 | } 13 | 14 | template 15 | constexpr unsigned int bitfield(bool first, Bools... rest) { 16 | return (first ? 1u : 0u) | bitfield(rest...) << 1; 17 | } 18 | 19 | void validate_options(const options& opt); 20 | 21 | options 22 | parse_opt(int argc, char **argv); 23 | 24 | int 25 | run(const options& opt); -------------------------------------------------------------------------------- /src/nethuns/misc/macro.h: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Larthia, University of Pisa. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | #pragma once 6 | 7 | #ifndef MAX 8 | #define MAX(a,b) \ 9 | ({ __typeof__ (a) _a = (a); \ 10 | __typeof__ (b) _b = (b); \ 11 | _a > _b ? _a : _b; }) 12 | #endif 13 | 14 | #ifndef MIN 15 | #define MIN(a,b) \ 16 | ({ __typeof__ (a) _a = (a); \ 17 | __typeof__ (b) _b = (b); \ 18 | _a < _b ? _a : _b; }) 19 | #endif -------------------------------------------------------------------------------- /tools/nethuns-gen/src/packets.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "hdr/packet.hpp" 7 | 8 | // build a packet from a hex stream 9 | 10 | std::map(std::optional)>> packet::catalog_ = { 11 | {"tcp:syn", [] (std::optional) -> std::vector { 12 | return { packet::from_hex_stream("20e52a9f5fcaa41f72a69c1b080045000034114640008006317e0a011977174c7d3cc0bb0050189ad2860000000080022000eae30000020405b40103030801010402") }; 13 | } 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /src/nethuns/version.c: -------------------------------------------------------------------------------- 1 | #ifdef NETHUNS_HAVE_LIBPCAP 2 | #include 3 | #endif 4 | 5 | const char * 6 | nethuns_version_full() 7 | { 8 | #ifdef NETHUNS_HAVE_LIBPCAP 9 | char *ret; 10 | if (asprintf(&ret, "nethuns v3.1.0; %s; libpcap:ON xdp:OFF netmap:OFF tpacket3:OFF", pcap_lib_version()) < 0) { 11 | return "nethuns v3.1.0; libpcap:ON xdp:OFF netmap:OFF tpacket3:OFF"; 12 | } 13 | return ret; 14 | #else 15 | return "nethuns v3.1.0; libpcap:ON xdp:OFF netmap:OFF tpacket3:OFF"; 16 | #endif 17 | } 18 | 19 | const char * 20 | nethuns_version() 21 | { 22 | return "nethuns v3.1.0"; 23 | } 24 | -------------------------------------------------------------------------------- /scripts/make_revision.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ "$#" -lt 3 ]; then 4 | echo "usage: make_revision GIT_PATH version.in version.out" 5 | exit 1 6 | fi 7 | 8 | echo "generating $3..." 9 | 10 | REV=`git -C $1 describe --abbrev=8 --dirty --always --long` 11 | VER=`git -C $1 describe --always --tags` 12 | 13 | cat $2 | sed -e "s#\\\$REVISION\\\$#${REV}#" \ 14 | -e "s#\\\$VERSION\\\$#${VER}#" \ 15 | -e "s#\\\$PCAP_TOGGLE\\\$#${4}#" \ 16 | -e "s#\\\$XDP_TOGGLE\\\$#${5}#" \ 17 | -e "s#\\\$NETMAP_TOGGLE\\\$#${6}#" \ 18 | -e "s#\\\$TPACKET3_TOGGLE\\\$#${7}#" \ 19 | > $3 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/nethuns/define.h: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Larthia, University of Pisa. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | #pragma once 6 | 7 | #define NETHUNS_ERRBUF_SIZE 512 8 | #define NETHUNS_ANY_QUEUE (-1) 9 | 10 | #define NETHUNS_SOCKET_LIBPCAP 0 11 | #define NETHUNS_SOCKET_NETMAP 1 12 | #define NETHUNS_SOCKET_XDP 2 13 | #define NETHUNS_SOCKET_TPACKET3 3 14 | 15 | #define NETHUNS_ERROR ((uint64_t)-1) 16 | #define NETHUNS_EOF ((uint64_t)-2) 17 | #define NETHUNS_ETH_P_8021Q 0x8100 18 | #define NETHUNS_ETH_P_8021AD 0x88A8 19 | 20 | #define typeof __typeof__ 21 | 22 | -------------------------------------------------------------------------------- /src/nethuns/misc/compiler.h: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Larthia, University of Pisa. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | #pragma once 6 | 7 | #ifndef likely 8 | # define likely(x) __builtin_expect((x),1) 9 | #endif 10 | 11 | #ifndef unlikely 12 | # define unlikely(x) __builtin_expect((x),0) 13 | #endif 14 | 15 | #ifndef __cacheline_aligned 16 | # define __cacheline_aligned __attribute__((aligned(64))) 17 | #endif 18 | 19 | #ifndef __maybe_unused 20 | # define __maybe_unused __attribute__((unused)) 21 | #endif 22 | 23 | #ifndef __always_inline 24 | # define __always_inline inline __attribute__((always_inline)) 25 | #endif 26 | -------------------------------------------------------------------------------- /tools/nethuns-dump/src/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Larthia, University of Pisa. All rights reserved. 3 | * Use of this source code is governed by a BSD-style 4 | * license that can be found in the LICENSE file. 5 | */ 6 | 7 | #include 8 | #include 9 | 10 | #include "hdr/options.h" 11 | #include "hdr/run.h" 12 | 13 | int sig_shutdown; 14 | 15 | void sighandler(int sig) 16 | { 17 | (void)sig; 18 | __atomic_store_n(&sig_shutdown, 1, __ATOMIC_RELAXED); 19 | } 20 | 21 | int 22 | main(int argc, char *argv[]) 23 | { 24 | struct options opt = parse_opt(argc, argv); 25 | 26 | validate_options(&opt); 27 | 28 | signal(SIGINT, sighandler); 29 | 30 | return run(&opt); 31 | } 32 | -------------------------------------------------------------------------------- /src/nethuns/version.c.in: -------------------------------------------------------------------------------- 1 | #ifdef NETHUNS_HAVE_LIBPCAP 2 | #include 3 | #endif 4 | 5 | const char * 6 | nethuns_version_full() 7 | { 8 | #ifdef NETHUNS_HAVE_LIBPCAP 9 | char *ret; 10 | if (asprintf(&ret, "nethuns $VERSION$; %s; libpcap:$PCAP_TOGGLE$ xdp:$XDP_TOGGLE$ netmap:$NETMAP_TOGGLE$ tpacket3:$TPACKET3_TOGGLE$", pcap_lib_version()) < 0) { 11 | return "nethuns $VERSION$; libpcap:$PCAP_TOGGLE$ xdp:$XDP_TOGGLE$ netmap:$NETMAP_TOGGLE$ tpacket3:$TPACKET3_TOGGLE$"; 12 | } 13 | return ret; 14 | #else 15 | return "nethuns $VERSION$; libpcap:$PCAP_TOGGLE$ xdp:$XDP_TOGGLE$ netmap:$NETMAP_TOGGLE$ tpacket3:$TPACKET3_TOGGLE$"; 16 | #endif 17 | } 18 | 19 | const char * 20 | nethuns_version() 21 | { 22 | return "nethuns $VERSION$"; 23 | } 24 | -------------------------------------------------------------------------------- /tools/nethuns-dump/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2021 Larthia, University of Pisa. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | cmake_minimum_required(VERSION 3.10) 6 | 7 | project (nethuns-dump) 8 | 9 | include(/usr/local/share/nethuns/Nethuns.cmake) 10 | 11 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON) 12 | 13 | # 14 | # Compiler options... 15 | # 16 | 17 | include_directories(${NETHUNS_INCLUDE_DIRS} . ../src) 18 | 19 | link_directories(/usr/local/lib) 20 | 21 | add_executable(nethuns-dump src/main.c 22 | src/options.c 23 | src/dump.c 24 | src/run.c) 25 | 26 | target_link_libraries(nethuns-dump ${NETHUNS_LIBRARY_DIRS} -pthread) 27 | -------------------------------------------------------------------------------- /src/nethuns/network/ipv6.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | struct ip6_hdr 7 | { 8 | #if defined(NETHUNS_LITTLE_ENDIAN) 9 | uint8_t priority:4, 10 | version:4; 11 | #elif defined(NETHUNS_BIG_ENDIAN) 12 | uint8_t version:4, 13 | priority:4; 14 | #else 15 | #error "nethuns: adjust your defines" 16 | #endif 17 | 18 | uint8_t flow_lbl[3]; /* label */ 19 | 20 | uint16_t plen; 21 | uint8_t next_hdr; /* next header */ 22 | uint8_t hop_lim; /* hop limit */ 23 | 24 | uint8_t saddr[16]; /* source address */ 25 | uint8_t daddr[16]; /* destination address */ 26 | 27 | } __attribute__((packed, aligned(2))); 28 | -------------------------------------------------------------------------------- /tools/nethuns-gen/hdr/rate_limiter.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | template 5 | struct rate_limiter 6 | { 7 | int period_ = 0; 8 | 9 | template 10 | auto wait(TimePoint deadline, size_t pktnum) -> void 11 | { 12 | if ((pktnum & ((1 << period_) - 1)) == 0) { 13 | auto spinning = false; 14 | while (std::chrono::steady_clock::now() < deadline) 15 | { 16 | spinning = true; 17 | } 18 | 19 | if (spinning) { 20 | if (period_ > 0) { 21 | --period_; 22 | } 23 | } else { 24 | if (period_ < MAX_PERIOD) { 25 | ++period_; 26 | } 27 | } 28 | } 29 | } 30 | }; -------------------------------------------------------------------------------- /ebpf/Makefile: -------------------------------------------------------------------------------- 1 | LLC ?= llc 2 | CLANG ?= clang 3 | CC ?= gcc 4 | 5 | KERNEL ?= /lib/modules/$(shell uname -r)/build/ 6 | 7 | CFLAGS ?= -Ikernel_include/tools 8 | CFLAGS += -Ikernel_include/tools/include 9 | CFLAGS += -Ikernel_include/tools/lib 10 | CFLAGS += -Ikernel_include/tools/perf 11 | 12 | CFLAGS_BPF ?= -I../libbpf/src 13 | CFLAGS_BPF ?= -I$(KERNEL)/tools/include 14 | CFLAGS_BPF += -I$(KERNEL)/tools/perf 15 | CFLAGS_BPF += -I$(KERNEL)/usr/include 16 | CFLAGS_BPF += -I$(KERNEL)/arch/x86/include 17 | 18 | all: net_xdp.o 19 | 20 | net_xdp.o: net_xdp.c 21 | $(CLANG) -g -c $< $(CFLAGS) $(CFLAGS_BPF) -D__KERNEL__ -D__BPF_TRACING__ -Wno-unused-value -Wno-pointer-sign -Wno-compare-distinct-pointer-types -Wno-gnu-variable-sized-type-not-at-end -Wno-address-of-packed-member -Wno-tautological-compare -Wno-unknown-warning-option -O2 -emit-llvm -c -o -| llc -march=bpf -filetype=obj -o $@ 22 | 23 | clean: 24 | rm -f net_xdp.o 25 | 26 | -------------------------------------------------------------------------------- /tools/nethuns-gen/src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "hdr/options.hpp" 6 | 7 | std::atomic_int sig_shutdown; 8 | 9 | void sighandler(int) 10 | { 11 | auto v = sig_shutdown.fetch_add(1, std::memory_order_relaxed); 12 | if (v < 2) { 13 | std::cerr << " received, graceful down..." << std::endl; 14 | } else { 15 | std::cerr << "EXIT!" << std::endl; 16 | std::exit(1); 17 | } 18 | } 19 | 20 | int main(int argc, char** argv) 21 | try 22 | { 23 | auto opt = parse_opt(argc, argv); 24 | signal(SIGINT, sighandler); 25 | signal(SIGSTOP, sighandler); 26 | 27 | validate_options(opt); 28 | 29 | return run(opt); 30 | } 31 | catch (std::exception& e) 32 | { 33 | std::cerr << e.what() << std::endl; 34 | return 1; 35 | } 36 | catch (...) 37 | { 38 | std::cerr << "unknow error!" << std::endl; 39 | return 1; 40 | } 41 | -------------------------------------------------------------------------------- /tools/nethuns-gen/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2021 Larthia, University of Pisa. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | cmake_minimum_required(VERSION 3.10) 6 | 7 | project (nethuns-gen) 8 | 9 | include(/usr/local/share/nethuns/Nethuns.cmake) 10 | 11 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON) 12 | 13 | set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-strict-aliasing -fomit-frame-pointer -fno-stack-protector -Wall -Wextra -Wno-unused-local-typedef -std=c++20 -pipe") 14 | 15 | # 16 | # Compiler options... 17 | # 18 | 19 | include_directories(${NETHUNS_INCLUDE_DIRS} . ../src) 20 | 21 | link_directories(/usr/local/lib) 22 | 23 | add_executable(nethuns-gen src/main.cpp 24 | src/options.cpp 25 | src/generator.cpp 26 | src/packets.cpp) 27 | 28 | target_link_libraries(nethuns-gen ${NETHUNS_LIBRARY_DIRS} -pthread) 29 | -------------------------------------------------------------------------------- /tools/nethuns-gen/hdr/netaddr.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | struct netaddr 8 | { 9 | uint32_t addr = 0; 10 | uint32_t mask = 0; 11 | 12 | netaddr(const char *str) { 13 | auto pos = std::string{str}.find('/'); 14 | if (pos == std::string::npos) { 15 | throw std::runtime_error("invalid prefix"); 16 | } 17 | auto addr_str = std::string{str}.substr(0, pos); 18 | auto prefix_str = std::string{str}.substr(pos + 1); 19 | 20 | mask = htonl(~((1ul << (32 - std::stoi(prefix_str))) - 1)); 21 | addr = inet_addr(addr_str.c_str()) & mask; 22 | } 23 | 24 | auto to_string() const -> std::string { 25 | char addr_buf[INET_ADDRSTRLEN]; 26 | inet_ntop(AF_INET, &addr, addr_buf, INET_ADDRSTRLEN); 27 | 28 | char mask_buf[INET_ADDRSTRLEN]; 29 | inet_ntop(AF_INET, &mask, mask_buf, INET_ADDRSTRLEN); 30 | 31 | return std::string{addr_buf} + "/" + std::string{mask_buf}; 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /src/nethuns/network/endian.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #if defined(__APPLE__) 4 | # include 5 | # include 6 | # if (BYTE_ORDER == LITTLE_ENDIAN) 7 | # define NETHUNS_LITTLE_ENDIAN 1 8 | # elif (BYTE_ORDER == BIG_ENDIAN) 9 | # define NETHUNS_BIG_ENDIAN 1 10 | # else 11 | # error "nethuns: endianess platform error" 12 | # endif 13 | #elif defined(BSD) 14 | # include 15 | # include 16 | # if (_BYTE_ORDER == _LITTLE_ENDIAN) 17 | # define NETHUNS_LITTLE_ENDIAN 1 18 | # elif (_BYTE_ORDER == _BIG_ENDIAN) 19 | # define NETHUNS_BIG_ENDIAN 1 20 | # else 21 | # error "nethuns: endianess platform error" 22 | # endif 23 | #elif defined(_WIN32) 24 | # include 25 | # define NETHUNS_LITTLE_ENDIAN 1 26 | #else 27 | # include 28 | # include 29 | # if (__BYTE_ORDER == __LITTLE_ENDIAN) 30 | # define NETHUNS_LITTLE_ENDIAN 1 31 | # elif (__BYTE_ORDER == __BIG_ENDIAN) 32 | # define NETHUNS_BIG_ENDIAN 1 33 | # else 34 | # error "nethuns: endianess platform error" 35 | # endif 36 | #endif -------------------------------------------------------------------------------- /tools/nethuns-gen/hdr/pretty.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | namespace detail { 6 | template 7 | std::string stringify(std::ostringstream &out, T &&arg) 8 | { 9 | out << std::move(arg); 10 | return out.str(); 11 | } 12 | template 13 | std::string stringify(std::ostringstream &out, T &&arg, Ts&&... args) 14 | { 15 | out << std::move(arg); 16 | return stringify(out, std::forward(args)...); 17 | } 18 | } 19 | 20 | template 21 | inline std::string 22 | stringify(Ts&& ... args) 23 | { 24 | std::ostringstream out; 25 | return detail::stringify(out, std::forward(args)...); 26 | } 27 | 28 | template 29 | inline std::string 30 | pretty_number(T value) 31 | { 32 | if (value < 1000000000) { 33 | if (value < 1000000) { 34 | if (value < 1000) { 35 | return stringify(value); 36 | } 37 | else return stringify(value/1000, "_K"); 38 | } 39 | else return stringify(value/1000000, "_M"); 40 | } 41 | else return stringify(value/1000000000, "_G"); 42 | } 43 | -------------------------------------------------------------------------------- /src/nethuns/sockets/types.h: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Larthia, University of Pisa. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | #pragma once 6 | #include "../define.h" 7 | 8 | #if NETHUNS_SOCKET == NETHUNS_SOCKET_TPACKET3 9 | 10 | typedef struct nethuns_socket_tpacket_v3 nethuns_socket_t; 11 | typedef struct tpacket3_hdr nethuns_pkthdr_t; 12 | 13 | #elif NETHUNS_SOCKET == NETHUNS_SOCKET_NETMAP 14 | 15 | #include 16 | 17 | typedef struct nethuns_socket_netmap nethuns_socket_t; 18 | typedef struct netmap_pkthdr nethuns_pkthdr_t; 19 | 20 | #elif NETHUNS_SOCKET == NETHUNS_SOCKET_LIBPCAP 21 | 22 | #include 23 | 24 | typedef struct nethuns_socket_libpcap nethuns_socket_t; 25 | typedef struct pcap_pkthdr nethuns_pkthdr_t; 26 | 27 | #elif NETHUNS_SOCKET == NETHUNS_SOCKET_XDP 28 | 29 | #include "xdp_pkthdr.h" 30 | 31 | typedef struct nethuns_socket_xdp nethuns_socket_t; 32 | typedef struct xdp_pkthdr nethuns_pkthdr_t; 33 | 34 | #else 35 | 36 | typedef void nethuns_socket_t; 37 | typedef void nethuns_pkthdr_t; 38 | 39 | #endif -------------------------------------------------------------------------------- /src/nethuns/network/tcp.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | #define NETHUNS_TCP_FIN (1 << 0) 9 | #define NETHUNS_TCP_SYN (1 << 1) 10 | #define NETHUNS_TCP_RST (1 << 2) 11 | #define NETHUNS_TCP_PSH (1 << 3) 12 | #define NETHUNS_TCP_ACK (1 << 4) 13 | #define NETHUNS_TCP_URG (1 << 5) 14 | #define NETHUNS_TCP_ECE (1 << 6) 15 | #define NETHUNS_TCP_CWR (1 << 7) 16 | 17 | struct tcp_hdr 18 | { 19 | uint16_t source; 20 | uint16_t dest; 21 | uint32_t seq; 22 | uint32_t ack_seq; 23 | #if defined(NETHUNS_LITTLE_ENDIAN) 24 | uint16_t res1:4; 25 | uint16_t doff:4; 26 | uint16_t fin:1; 27 | uint16_t syn:1; 28 | uint16_t rst:1; 29 | uint16_t psh:1; 30 | uint16_t ack:1; 31 | uint16_t urg:1; 32 | uint16_t ece:1; 33 | uint16_t cwr:1; 34 | #elif defined(NETHUNS_BIG_ENDIAN) 35 | uint16_t doff:4; 36 | uint16_t res1:4; 37 | uint16_t cwr:1; 38 | uint16_t ece:1; 39 | uint16_t urg:1; 40 | uint16_t ack:1; 41 | uint16_t psh:1; 42 | uint16_t rst:1; 43 | uint16_t syn:1; 44 | uint16_t fin:1; 45 | #else 46 | #error "nethuns: adjust your defines" 47 | #endif 48 | uint16_t window; 49 | uint16_t check; 50 | uint16_t urg_ptr; 51 | 52 | } __attribute__((packed, aligned(4))); 53 | -------------------------------------------------------------------------------- /src/nethuns/network/ipv4.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #define NETHUNS_IP_RES 0x8000 /* Flag: "Reserved, must be 0" */ 8 | #define NETHUNS_IP_DF 0x4000 /* Flag: "Don't Fragment" */ 9 | #define NETHUNS_IP_MF 0x2000 /* Flag: "More Fragments" */ 10 | #define NETHUNS_IP_FRAG_OFFSET 0x1FFF /* "Fragment Offset" part */ 11 | 12 | struct ip_hdr 13 | { 14 | #if defined(NETHUNS_LITTLE_ENDIAN) 15 | unsigned int ihl:4; 16 | unsigned int version:4; 17 | #elif defined(NETHUNS_BIG_ENDIAN) 18 | unsigned int version:4; 19 | unsigned int ihl:4; 20 | #else 21 | #error "nethuns: adjust your defines" 22 | #endif 23 | uint8_t tos; 24 | uint16_t tot_len; 25 | uint16_t id; 26 | uint16_t frag_off; 27 | uint8_t ttl; 28 | uint8_t protocol; 29 | uint16_t check; 30 | uint32_t saddr; 31 | uint32_t daddr; 32 | } __attribute__((packed, aligned(4))); 33 | 34 | struct ipopt 35 | { 36 | union 37 | { 38 | struct 39 | { 40 | uint8_t copied:1; 41 | uint8_t class_:2; 42 | uint8_t num:5; 43 | } field; 44 | 45 | uint8_t value; 46 | }; 47 | 48 | uint8_t len; 49 | }; 50 | -------------------------------------------------------------------------------- /src/nethuns/vlan.h: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Larthia, University of Pisa. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | #include "define.h" 10 | #include "misc/compiler.h" 11 | 12 | static __always_inline uint16_t 13 | nethuns_vlan_vid(uint16_t tci) 14 | { 15 | return (tci & ((1<<13)-1)); 16 | } 17 | 18 | static __always_inline uint16_t 19 | nethuns_vlan_pcp(uint16_t tci) 20 | { 21 | return (tci >> 13) & 7; 22 | } 23 | 24 | static __always_inline int 25 | nethuns_vlan_dei(uint16_t tci) 26 | { 27 | return (tci >> 12) & 1; 28 | } 29 | 30 | static __always_inline uint16_t 31 | nethuns_vlan_tpid(const uint8_t *payload) 32 | { 33 | struct ether_header const *eth = (struct ether_header const *)payload; 34 | if (eth->ether_type == htons(NETHUNS_ETH_P_8021Q) || eth->ether_type == htons(NETHUNS_ETH_P_8021AD)) 35 | return ntohs(eth->ether_type); 36 | return 0; 37 | } 38 | 39 | static __always_inline uint16_t 40 | nethuns_vlan_tci(const uint8_t *payload) 41 | { 42 | struct ether_header const *eth = (struct ether_header const *)payload; 43 | if (eth->ether_type == htons(NETHUNS_ETH_P_8021Q) || eth->ether_type == htons(NETHUNS_ETH_P_8021AD)) 44 | return ntohs(*(uint16_t const *)(eth+1)); 45 | return 0; 46 | } 47 | -------------------------------------------------------------------------------- /test/src/pure-meter-pcap.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Larthia, University of Pisa. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | 14 | std::atomic_long total; 15 | 16 | void meter() 17 | { 18 | auto now = std::chrono::system_clock::now(); 19 | for(;;) 20 | { 21 | now += std::chrono::seconds(1); 22 | std::this_thread::sleep_until(now); 23 | auto x = total.exchange(0); 24 | std::cout << "pkt/sec: " << x << std::endl; 25 | } 26 | } 27 | 28 | 29 | int 30 | main(int argc, char *argv[]) 31 | try 32 | { 33 | char errbuf[PCAP_ERRBUF_SIZE]; 34 | 35 | if (argc < 2) 36 | { 37 | std::cerr << "usage: " << argv[0] << " dev" << std::endl; 38 | return 0; 39 | } 40 | 41 | std::thread(meter).detach(); 42 | 43 | auto s = pcap_open_live(argv[1], 2048, 1, 0, errbuf); 44 | 45 | const unsigned char *frame; 46 | struct pcap_pkthdr pkthdr; 47 | 48 | for(;;) 49 | { 50 | if ((frame = pcap_next(s, &pkthdr))) 51 | { 52 | total++; 53 | } 54 | } 55 | 56 | pcap_close(s); 57 | return 0; 58 | } 59 | catch(std::exception &e) 60 | { 61 | std::cerr << e.what() << std::endl; 62 | return 1; 63 | } 64 | -------------------------------------------------------------------------------- /src/nethuns/network/ether.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | const int ETH_ALEN = 6; /* Octets in one ethernet addr */ 9 | const int ETH_HLEN = 14; /* Total octets in header. */ 10 | const int ETH_DATA_LEN = 1500; /* Max. octets in payload */ 11 | const int ETH_FRAME_LEN = 1514; /* Max. octets in frame sans FCS */ 12 | 13 | struct eth_hdr 14 | { 15 | unsigned char dest[ETH_ALEN]; /* destination eth addr */ 16 | unsigned char source[ETH_ALEN]; /* source ether addr */ 17 | uint16_t proto; /* packet type ID field */ 18 | } __attribute__((packed, aligned(2))); 19 | 20 | 21 | static inline 22 | int ether_ntoa(unsigned char const *addr, char buf[18]) 23 | { 24 | static const char lookup[] = "0123456789abcdef"; 25 | auto x = 0U; 26 | for(auto i = 0; i < ETH_ALEN; i++) 27 | { 28 | buf[x++] = lookup[addr[i] >> 4]; 29 | buf[x++] = lookup[addr[i] & 0xf]; 30 | if (i < (ETH_ALEN-1)) 31 | buf[x++]= ':'; 32 | } 33 | buf[x] = '\0'; 34 | return x; 35 | } 36 | 37 | inline bool 38 | ether_aton(const char * mac_str, unsigned char bytes[6]) 39 | { 40 | unsigned char mac[6]; 41 | 42 | if (sscanf(mac_str, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", 43 | &mac[0], &mac[1], &mac[2], 44 | &mac[3], &mac[4], &mac[5]) != 6) 45 | { 46 | return false; 47 | } 48 | 49 | memcpy(bytes, mac, 6); 50 | return true; 51 | } 52 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2021, Larthia 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /test/src/pure-forward-pcap.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Larthia, University of Pisa. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | 14 | std::atomic_long total; 15 | 16 | void meter() 17 | { 18 | auto now = std::chrono::system_clock::now(); 19 | for(;;) 20 | { 21 | now += std::chrono::seconds(1); 22 | std::this_thread::sleep_until(now); 23 | auto x = total.exchange(0); 24 | std::cout << "pkt/sec: " << x << std::endl; 25 | } 26 | } 27 | 28 | 29 | int 30 | main(int argc, char *argv[]) 31 | try 32 | { 33 | char errbuf[PCAP_ERRBUF_SIZE]; 34 | 35 | if (argc < 3) 36 | { 37 | std::cerr << "usage: " << argv[0] << " in out" << std::endl; 38 | return 0; 39 | } 40 | 41 | std::thread(meter).detach(); 42 | 43 | auto in = pcap_open_live(argv[1], 2048, 1, 0, errbuf); 44 | auto out = pcap_open_live(argv[2], 2048, 1, 0, errbuf); 45 | 46 | 47 | const unsigned char *frame; 48 | struct pcap_pkthdr pkthdr; 49 | 50 | for(;;) 51 | { 52 | if ((frame = pcap_next(in, &pkthdr))) 53 | { 54 | total++; 55 | 56 | while (pcap_inject(out, frame, pkthdr.caplen) < 0) 57 | { }; 58 | 59 | } 60 | } 61 | 62 | pcap_close(in); 63 | pcap_close(out); 64 | return 0; 65 | } 66 | catch(std::exception &e) 67 | { 68 | std::cerr << e.what() << std::endl; 69 | return 1; 70 | } 71 | 72 | -------------------------------------------------------------------------------- /src/nethuns/global.h: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Larthia, University of Pisa. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | 10 | #ifndef _GNU_SOURCE 11 | #define _GNU_SOURCE 12 | #endif 13 | 14 | #include "misc/hashmap.h" 15 | #include "misc/compiler.h" 16 | 17 | struct nethuns_netinfo { 18 | int promisc_refcnt; 19 | int xdp_prog_refcnt; 20 | uint32_t xdp_prog_id; 21 | }; 22 | 23 | static __always_inline 24 | void nethuns_netinfo_init(struct nethuns_netinfo *info) 25 | { 26 | info->promisc_refcnt = 0; 27 | info->xdp_prog_refcnt = 0; 28 | info->xdp_prog_id = 0; 29 | } 30 | 31 | static __always_inline 32 | void nethuns_netinfo_fini(__maybe_unused struct nethuns_netinfo *info) 33 | { 34 | } 35 | 36 | struct nethuns_global 37 | { 38 | pthread_mutex_t m; 39 | struct hashmap_s netinfo_map; 40 | }; 41 | 42 | #ifdef __cplusplus 43 | extern "C" { 44 | #endif 45 | 46 | extern struct nethuns_global __nethuns_global; 47 | 48 | extern void nethuns_init_(size_t hsize, int socket); 49 | 50 | extern void __attribute__((destructor)) nethuns_fini(); 51 | 52 | static __always_inline 53 | void nethuns_lock_global() 54 | { 55 | pthread_mutex_lock(&__nethuns_global.m); 56 | } 57 | 58 | static __always_inline 59 | void nethuns_unlock_global() 60 | { 61 | pthread_mutex_unlock(&__nethuns_global.m); 62 | } 63 | 64 | struct nethuns_netinfo *nethuns_lookup_netinfo(const char *); 65 | struct nethuns_netinfo *nethuns_create_netinfo(const char *); 66 | 67 | #ifdef __cplusplus 68 | } 69 | #endif 70 | -------------------------------------------------------------------------------- /tools/nethuns-gen/hdr/generator.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "hdr/netaddr.hpp" 14 | 15 | // to address the hardware prefetching for second-level cache... 16 | 17 | struct alignas(128) generator 18 | { 19 | std::string source; 20 | std::string dev; 21 | std::optional cpu = std::nullopt; 22 | 23 | int amp = 1; 24 | int speed = 1; 25 | int id = 0; 26 | int seed = 0; 27 | 28 | std::size_t max_packets = std::numeric_limits::max(); 29 | std::size_t pkt_rate = std::numeric_limits::max(); 30 | std::size_t loops = std::numeric_limits::max(); 31 | std::optional pktlen = std::nullopt; 32 | 33 | std::vector randomize_prefix; 34 | 35 | std::string mac_source; 36 | std::string mac_dest; 37 | 38 | bool fix_checksums = false; 39 | bool verbose = false; 40 | bool pcap_preload = false; 41 | 42 | bool is_pcap_file() const { 43 | std::filesystem::path p(source); 44 | return p.extension() == ".pcap" || p.extension() == ".pcapng"; 45 | } 46 | 47 | bool has_rate_limiter() const { 48 | return pkt_rate != std::numeric_limits::max(); 49 | } 50 | 51 | bool has_speed_control() const { 52 | return speed > 0; 53 | } 54 | 55 | bool has_randomizer() const { 56 | return !randomize_prefix.empty(); 57 | } 58 | }; 59 | 60 | struct alignas (128) generator_stats 61 | { 62 | std::atomic_size_t packets = 0; 63 | std::atomic_size_t bytes = 0; 64 | std::atomic_size_t errors = 0; 65 | std::atomic_size_t discarded = 0; 66 | }; 67 | -------------------------------------------------------------------------------- /tools/nethuns-gen/hdr/affinity.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #ifdef __APPLE__ 7 | #include // this header cannot be included in C++ apps 8 | #include 9 | #include 10 | #include 11 | #endif 12 | 13 | #include 14 | 15 | namespace this_thread { 16 | 17 | #if defined (__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) 18 | static inline void 19 | affinity(int core) 20 | { 21 | cpu_set_t cpuset; 22 | CPU_ZERO(&cpuset); CPU_SET(core, &cpuset); 23 | 24 | pthread_t pth = pthread_self(); 25 | if ( ::pthread_setaffinity_np(pth, sizeof(cpuset), &cpuset) != 0) 26 | throw std::runtime_error("this_thread::affinity: pthread_setaffinity_np error on core " + std::to_string(core)); 27 | } 28 | 29 | #elif defined(__APPLE__) 30 | 31 | [[maybe_unused]] static inline void 32 | affinity(int core) 33 | { 34 | thread_port_t mach_thread; 35 | thread_affinity_policy_data_t policy = { core }; 36 | mach_thread = pthread_mach_thread_np(pthread_self()); 37 | if (thread_policy_set(mach_thread, THREAD_AFFINITY_POLICY, (thread_policy_t)&policy, 1) != 0) 38 | throw std::runtime_error("this_thread::affinity: thread_policy_set"); 39 | 40 | policy.affinity_tag = -1; 41 | 42 | mach_msg_type_number_t policy_count = THREAD_AFFINITY_POLICY_COUNT; 43 | boolean_t get_default = false; 44 | 45 | if (thread_policy_get(mach_thread, THREAD_AFFINITY_POLICY, (thread_policy_t)&policy, &policy_count, &get_default) != 0) 46 | throw std::runtime_error("this_thread::affinity: thread_policy_get"); 47 | 48 | if (static_cast(policy.affinity_tag) != core) 49 | throw std::runtime_error("this_thread::affinity: couldn't set thread affinity"); 50 | } 51 | 52 | #else 53 | 54 | static inline void 55 | affinity(int core) 56 | { 57 | throw std::runtime_error("this_thread::affinity: not supported on this platform"); 58 | } 59 | 60 | #endif 61 | 62 | } -------------------------------------------------------------------------------- /src/nethuns/sockets/xsk_ext.h: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Larthia, University of Pisa. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | #pragma once 6 | 7 | #include "xdp/libbpf.h" 8 | #include "xdp/bpf.h" 9 | #include "xdp/xsk.h" 10 | 11 | #include 12 | 13 | #define XSK_INVALID_UMEM_FRAME UINT64_MAX 14 | 15 | struct xsk_umem_info { 16 | struct xsk_ring_prod fq; 17 | struct xsk_ring_cons cq; 18 | struct xsk_umem *umem; 19 | void *buffer; 20 | }; 21 | 22 | struct xsk_socket_info { 23 | struct xsk_ring_cons rx; 24 | struct xsk_ring_prod tx; 25 | struct xsk_umem_info *umem; 26 | struct xsk_socket *xsk; 27 | 28 | uint32_t outstanding_tx; 29 | 30 | unsigned long rx_npkts; 31 | unsigned long tx_npkts; 32 | 33 | uint32_t umem_frame_free; 34 | uint64_t umem_frame_addr[]; 35 | }; 36 | 37 | struct nethuns_socket_xdp; 38 | 39 | struct xsk_umem_info * 40 | xsk_configure_umem(struct nethuns_socket_xdp *sock, void *buffer, size_t size, size_t frame_size); 41 | 42 | int 43 | xsk_populate_fill_ring(struct nethuns_socket_xdp *sock, size_t frame_size); 44 | 45 | struct xsk_socket_info * 46 | xsk_configure_socket(struct nethuns_socket_xdp *sock); 47 | 48 | int 49 | xsk_enter_into_map(struct nethuns_socket_xdp *sock, int queue); 50 | 51 | static inline __u32 xsk_ring_prod__free(struct xsk_ring_prod *r) 52 | { 53 | r->cached_cons = *r->consumer + r->size; 54 | return r->cached_cons - r->cached_prod; 55 | } 56 | 57 | static uint64_t 58 | __maybe_unused xsk_alloc_umem_frame(struct xsk_socket_info *xsk) 59 | { 60 | uint64_t frame; 61 | if (xsk->umem_frame_free == 0) 62 | return XSK_INVALID_UMEM_FRAME; 63 | 64 | frame = xsk->umem_frame_addr[--xsk->umem_frame_free]; 65 | xsk->umem_frame_addr[xsk->umem_frame_free] = XSK_INVALID_UMEM_FRAME; 66 | return frame; 67 | } 68 | 69 | static void 70 | __maybe_unused xsk_free_umem_frame(struct xsk_socket_info *xsk, uint64_t frame) 71 | { 72 | // assert(xsk->umem_frame_free < XSK_NUM_FRAMES); 73 | xsk->umem_frame_addr[xsk->umem_frame_free++] = frame; 74 | } 75 | 76 | static uint64_t 77 | __maybe_unused xsk_umem_free_frames(struct xsk_socket_info *xsk) 78 | { 79 | return xsk->umem_frame_free; 80 | } 81 | -------------------------------------------------------------------------------- /test/src/pure-meter-pcap-mt.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Larthia, University of Pisa. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | std::atomic_long total; 16 | 17 | void meter() 18 | { 19 | auto now = std::chrono::system_clock::now(); 20 | for(;;) 21 | { 22 | now += std::chrono::seconds(1); 23 | std::this_thread::sleep_until(now); 24 | auto x = total.exchange(0); 25 | std::cout << "pkt/sec: " << x << std::endl; 26 | } 27 | } 28 | 29 | 30 | struct pcap_pkt 31 | { 32 | unsigned char *pkt; 33 | struct pcap_pkthdr pkthdr; 34 | }; 35 | 36 | 37 | nethuns_spsc_queue *queue; 38 | 39 | int consumer() 40 | { 41 | for(;;) 42 | { 43 | auto pkt = reinterpret_cast(nethuns_spsc_pop(queue)); 44 | if (pkt) { 45 | total++; 46 | free((void *)pkt->pkt); 47 | } 48 | } 49 | } 50 | 51 | 52 | int 53 | main(int argc, char *argv[]) 54 | try 55 | { 56 | char errbuf[PCAP_ERRBUF_SIZE]; 57 | 58 | if (argc < 2) 59 | { 60 | std::cerr << "usage: " << argv[0] << " dev" << std::endl; 61 | return 0; 62 | } 63 | 64 | queue = nethuns_spsc_init(65536, sizeof(pcap_pkt)); 65 | if (!queue) { 66 | throw std::runtime_error("nethuns_spsc: internal error"); 67 | } 68 | 69 | std::thread(meter).detach(); 70 | std::thread(consumer).detach(); 71 | 72 | auto s = pcap_open_live(argv[1], 2048, 1, 0, errbuf); 73 | 74 | const unsigned char *frame; 75 | struct pcap_pkthdr pkthdr; 76 | 77 | for(;;) 78 | { 79 | if ((frame = pcap_next(s, &pkthdr))) 80 | { 81 | pcap_pkt p { (unsigned char *)malloc(pkthdr.caplen), pkthdr }; 82 | 83 | memcpy(p.pkt, frame, pkthdr.caplen); 84 | 85 | while (!nethuns_spsc_push(queue,&p)) 86 | { }; 87 | } 88 | 89 | } 90 | 91 | pcap_close(s); 92 | return 0; 93 | } 94 | catch(std::exception &e) 95 | { 96 | std::cerr << e.what() << std::endl; 97 | return 1; 98 | } 99 | -------------------------------------------------------------------------------- /src/nethuns/types.h: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Larthia, University of Pisa. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "sockets/types.h" 13 | 14 | 15 | enum nethuns_capture_dir 16 | { 17 | nethuns_in 18 | , nethuns_out 19 | , nethuns_in_out 20 | }; 21 | 22 | 23 | enum nethuns_capture_mode 24 | { 25 | nethuns_cap_default 26 | , nethuns_cap_skb_mode 27 | , nethuns_cap_drv_mode 28 | , nethuns_cap_zero_copy 29 | }; 30 | 31 | 32 | enum nethuns_socket_mode 33 | { 34 | nethuns_socket_rx_tx 35 | , nethuns_socket_rx_only 36 | , nethuns_socket_tx_only 37 | }; 38 | 39 | 40 | struct nethuns_socket_options 41 | { 42 | unsigned int numblocks; 43 | unsigned int numpackets; 44 | unsigned int packetsize; 45 | unsigned int timeout_ms; 46 | enum nethuns_capture_dir dir; 47 | enum nethuns_capture_mode capture; 48 | enum nethuns_socket_mode mode; 49 | bool timestamp; 50 | bool promisc; 51 | bool rxhash; 52 | bool tx_qdisc_bypass; 53 | const char *xdp_prog; // xdp only 54 | const char *xdp_prog_sec; // xdp only 55 | const char *xsk_map_name; // xdp only 56 | bool reuse_maps; // xdp only 57 | const char *pin_dir; // xdp only 58 | }; 59 | 60 | struct nethuns_stat 61 | { 62 | uint64_t rx_packets; 63 | uint64_t tx_packets; 64 | uint64_t rx_dropped; 65 | uint64_t rx_if_dropped; 66 | uint64_t rx_invalid; // xdp only 67 | uint64_t tx_invalid; // xdp only 68 | uint64_t freeze; 69 | }; 70 | 71 | 72 | struct nethuns_socket_base; 73 | struct nethuns_packet 74 | { 75 | uint8_t const *payload; 76 | const nethuns_pkthdr_t *pkthdr; 77 | struct nethuns_socket_base *sock; 78 | uint64_t id; 79 | }; 80 | 81 | 82 | struct nethuns_timeval 83 | { 84 | uint32_t tv_sec; 85 | uint32_t tv_usec; 86 | }; 87 | -------------------------------------------------------------------------------- /scripts/build_test.hs: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env stack 2 | -- stack runghc --resolver lts-20.11 --package optparse-applicative --package ansi-terminal --allow-different-user --install-ghc 3 | -- 4 | 5 | {-# LANGUAGE RecordWildCards #-} 6 | 7 | import System.Exit ( ExitCode(ExitFailure) ) 8 | import System.FilePath.Posix 9 | import System.Process ( spawnProcess, waitForProcess ) 10 | 11 | data Build = Build { 12 | optBuiltinPcapReader :: Opt 13 | , optTpacketV3 :: Opt 14 | , optNetmap :: Opt 15 | , optLibpcap :: Opt 16 | , optXDP :: Opt 17 | } 18 | 19 | 20 | newtype Opt = Opt Bool 21 | deriving (Eq) 22 | 23 | 24 | on = Opt True 25 | off = Opt False 26 | 27 | 28 | (.|.) :: Opt -> Opt -> Opt 29 | (Opt a) .|. (Opt b) = Opt (a || b) 30 | {-# INLINE (.|.) #-} 31 | 32 | 33 | (.&.) :: Opt -> Opt -> Opt 34 | (Opt a) .&. (Opt b) = Opt (a && b) 35 | {-# INLINE (.&.) #-} 36 | 37 | 38 | main = do 39 | let builds = [ Build{..} | optBuiltinPcapReader <- [off, on] 40 | , optTpacketV3 <- [off, on] 41 | , optNetmap <- [off, on] 42 | , optLibpcap <- [off, on] 43 | , optXDP <- [off, on] 44 | , let (Opt filter) = optTpacketV3 .|. optNetmap .|. optLibpcap .|. optXDP 45 | in filter ] 46 | 47 | mapM_ testBuild builds 48 | 49 | 50 | instance Show Opt where 51 | show (Opt True) = "ON" 52 | show _ = "OFF" 53 | 54 | 55 | mkCMakeOpt :: Build -> [String] 56 | mkCMakeOpt Build{..} = 57 | [ "NETHUNS_OPT_BUILTIN_PCAP_READER" <> "=" <> show optBuiltinPcapReader 58 | , "NETHUNS_OPT_TPACKET_V3" <> "=" <> show optTpacketV3 59 | , "NETHUNS_OPT_NETMAP" <> "=" <> show optNetmap 60 | , "NETHUNS_OPT_LIBPCAP" <> "=" <> show optLibpcap 61 | , "NETHUNS_OPT_XDP" <> "=" <> show optXDP ] 62 | 63 | 64 | testBuild :: Build -> IO () 65 | testBuild build = do 66 | let opts = mkCMakeOpt build 67 | cmd "cmake" $ ["-B", "BUILD"] <> (("-D" <>) <$> mkCMakeOpt build) 68 | cmd "make" ["-C", "BUILD"] 69 | cmd "make" ["-C", "BUILD", "clean"] 70 | 71 | 72 | cmd :: FilePath -> [String] -> IO () 73 | cmd cmd args = do 74 | putStrLn $ "-> "<> unwords (cmd : args) 75 | ec <- waitForProcess =<< spawnProcess cmd args 76 | case ec of 77 | ExitFailure n -> errorWithoutStackTrace $ "builder: " <> cmd <> " exited with code " <> show n 78 | _ -> return () 79 | -------------------------------------------------------------------------------- /test/src/pure-forward-pcap-mt.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Larthia, University of Pisa. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | std::atomic_long total; 16 | 17 | void meter() 18 | { 19 | auto now = std::chrono::system_clock::now(); 20 | for(;;) 21 | { 22 | now += std::chrono::seconds(1); 23 | std::this_thread::sleep_until(now); 24 | auto x = total.exchange(0); 25 | std::cout << "pkt/sec: " << x << std::endl; 26 | } 27 | } 28 | 29 | 30 | struct pcap_pkt 31 | { 32 | unsigned char *pkt; 33 | struct pcap_pkthdr pkthdr; 34 | }; 35 | 36 | 37 | nethuns_spsc_queue *queue; 38 | 39 | int consumer(std::string dev) 40 | { 41 | char errbuf[PCAP_ERRBUF_SIZE]; 42 | auto out = pcap_open_live(dev.c_str(), 2048, 1, 0, errbuf); 43 | 44 | for(;;) 45 | { 46 | auto pkt = reinterpret_cast(nethuns_spsc_pop(queue)); 47 | if (pkt) { 48 | total++; 49 | 50 | while (pcap_inject(out, pkt->pkt, pkt->pkthdr.caplen) < 0) 51 | { }; 52 | 53 | free((void *)pkt->pkt); 54 | } 55 | } 56 | 57 | pcap_close(out); 58 | } 59 | 60 | 61 | int 62 | main(int argc, char *argv[]) 63 | try 64 | { 65 | char errbuf[PCAP_ERRBUF_SIZE]; 66 | 67 | if (argc < 3) 68 | { 69 | std::cerr << "usage: " << argv[0] << " in out" << std::endl; 70 | return 0; 71 | } 72 | 73 | queue = nethuns_spsc_init(65536, sizeof(pcap_pkt)); 74 | if (!queue) { 75 | throw std::runtime_error("nethuns_spsc: internal error"); 76 | } 77 | 78 | std::thread(meter).detach(); 79 | std::thread(consumer, std::string{argv[2]}).detach(); 80 | 81 | auto s = pcap_open_live(argv[1], 2048, 1, 0, errbuf); 82 | 83 | const unsigned char *frame; 84 | struct pcap_pkthdr pkthdr; 85 | 86 | for(;;) 87 | { 88 | if ((frame = pcap_next(s, &pkthdr))) 89 | { 90 | pcap_pkt p { (unsigned char *)malloc(pkthdr.caplen), pkthdr }; 91 | 92 | memcpy(p.pkt, frame, pkthdr.caplen); 93 | 94 | while (!nethuns_spsc_push(queue, &p)) 95 | { }; 96 | } 97 | 98 | } 99 | 100 | pcap_close(s); 101 | return 0; 102 | } 103 | catch(std::exception &e) 104 | { 105 | std::cerr << e.what() << std::endl; 106 | return 1; 107 | } 108 | -------------------------------------------------------------------------------- /src/nethuns/network/icmp.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | enum icmp_type 7 | { 8 | ICMP_ECHOREPLY = 0 , /* Echo Reply */ 9 | ICMP_DEST_UNREACH = 3 , /* Destination Unreachable */ 10 | ICMP_SOURCE_QUENCH = 4 , /* Source Quench */ 11 | ICMP_REDIRECT = 5 , /* Redirect (change route) */ 12 | ICMP_ECHO = 8 , /* Echo Request */ 13 | ICMP_ROUTER_ADV = 9 , /* Router ADV */ 14 | ICMP_ROUTER_SOLICIT = 10, /* Router ADV */ 15 | ICMP_TIME_EXCEEDED = 11, /* Time Exceeded */ 16 | ICMP_PARAMETERPROB = 12, /* Parameter Problem */ 17 | ICMP_TIMESTAMP = 13, /* Timestamp Request */ 18 | ICMP_TIMESTAMPREPLY = 14, /* Timestamp Reply */ 19 | ICMP_INFO_REQUEST = 15, /* Information Request */ 20 | ICMP_INFO_REPLY = 16, /* Information Reply */ 21 | ICMP_ADDRESS = 17, /* Address Mask Request */ 22 | ICMP_ADDRESSREPLY = 18, /* Address Mask Reply */ 23 | ICMP_EX_ECHO = 42, /* Ex Echo */ 24 | ICMP_EX_ECHOREPLY = 43, /* Ex Echo Reply */ 25 | }; 26 | 27 | enum icmp_code_unreach 28 | { 29 | ICMP_NET_UNREACH = 0 , /* Network Unreachable */ 30 | ICMP_HOST_UNREACH = 1 , /* Host Unreachable */ 31 | ICMP_PROT_UNREACH = 2 , /* Protocol Unreachable */ 32 | ICMP_PORT_UNREACH = 3 , /* Port Unreachable */ 33 | ICMP_FRAG_NEEDED = 4 , /* Fragmentation Needed/DF set */ 34 | ICMP_SR_FAILED = 5 , /* Source Route failed */ 35 | ICMP_NET_UNKNOWN = 6 , 36 | ICMP_HOST_UNKNOWN = 7 , 37 | ICMP_HOST_ISOLATED = 8 , 38 | ICMP_NET_ANO = 9 , 39 | ICMP_HOST_ANO = 10, 40 | ICMP_NET_UNR_TOS = 11, 41 | ICMP_HOST_UNR_TOS = 12, 42 | ICMP_PKT_FILTERED = 13, /* Packet filtered */ 43 | ICMP_PREC_VIOLATION = 14, /* Precedence violation */ 44 | ICMP_PREC_CUTOFF = 15 /* Precedence cut off */ 45 | }; 46 | 47 | enum icmp_code_redirect 48 | { 49 | ICMP_REDIR_NET = 0 , /* Redirect Net */ 50 | ICMP_REDIR_HOST = 1 , /* Redirect Host */ 51 | ICMP_REDIR_NETTOS = 2 , /* Redirect Net for TOS */ 52 | ICMP_REDIR_HOSTTOS = 3 /* Redirect Host for TOS */ 53 | }; 54 | 55 | enum icmp_code_time_exceed 56 | { 57 | ICMP_EXC_TTL = 0, /* TTL count exceeded */ 58 | ICMP_EXC_FRAGTIME = 1 /* Fragment Reass time exceeded */ 59 | }; 60 | 61 | 62 | struct icmp_hdr 63 | { 64 | icmp_type type; /* message type */ 65 | uint8_t code; /* type sub-code */ 66 | uint16_t checksum; 67 | 68 | union 69 | { 70 | struct 71 | { 72 | uint16_t id; 73 | uint16_t sequence; 74 | } echo; /* echo datagram */ 75 | uint32_t gateway; /* gateway address */ 76 | struct 77 | { 78 | uint16_t __glibc_reserved; 79 | uint16_t mtu; 80 | } frag; /* path mtu discovery */ 81 | } un; 82 | } __attribute__((packed,aligned(4))); 83 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2021 Larthia, University of Pisa. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | cmake_minimum_required(VERSION 3.10) 6 | 7 | project (nethuns-test) 8 | 9 | include(/usr/local/share/nethuns/Nethuns.cmake) 10 | 11 | # 12 | # Compiler options... 13 | # 14 | 15 | string(FIND "${CMAKE_CXX_COMPILER_ID}" "Clang" CLANG_SUBSTRING_INDEX) 16 | if ("${CLANG_SUBSTRING_INDEX}" GREATER -1) 17 | set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O2 -fomit-frame-pointer -Wall -Wextra -Wshadow") 18 | set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O2 -std=c++17 -fomit-frame-pointer -Wall -Wextra -Wshadow") 19 | else() 20 | set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O2 -march=native -fomit-frame-pointer -Wall -Wextra -Wshadow") 21 | set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O2 -std=c++17 -march=native -fomit-frame-pointer -Wall -Wextra -Wshadow") 22 | endif() 23 | 24 | 25 | include_directories(${NETHUNS_INCLUDE_DIRS} src) 26 | #link_directories(${NETHUNS_LIBRARY_DIRS} src) 27 | 28 | message(STATUS "HDRS: ${NETHUNS_INCLUDE_DIRS}") 29 | message(STATUS "LIBS: ${NETHUNS_LIBRARY_DIRS}") 30 | 31 | add_executable(nethuns-version src/version.c) 32 | add_executable(nethuns-send src/send.cpp) 33 | add_executable(nethuns-meter src/meter.cpp) 34 | add_executable(nethuns-filter src/filter.cpp) 35 | add_executable(nethuns-file-pcap src/file-pcap.cpp) 36 | add_executable(nethuns-file-pcap-mt src/file-pcap-mt.cpp) 37 | add_executable(nethuns-forward src/forward.cpp) 38 | add_executable(nethuns-forward-mt src/forward-mt.cpp) 39 | add_executable(nethuns-meter-mt src/meter-mt.cpp) 40 | 41 | add_executable(pure-pcap-meter src/pure-meter-pcap.cpp) 42 | add_executable(pure-pcap-meter-mt src/pure-meter-pcap-mt.cpp) 43 | add_executable(pure-pcap-forward src/pure-forward-pcap.cpp) 44 | add_executable(pure-pcap-forward-mt src/pure-forward-pcap-mt.cpp) 45 | 46 | add_executable(pcap-version src/pcap-version.c) 47 | target_link_libraries(nethuns-file-pcap-mt ${NETHUNS_LIBRARY_DIRS} -pthread) 48 | 49 | target_link_libraries(nethuns-version ${NETHUNS_LIBRARY_DIRS} -pthread) 50 | target_link_libraries(nethuns-meter-mt ${NETHUNS_LIBRARY_DIRS} -pthread) 51 | target_link_libraries(nethuns-send ${NETHUNS_LIBRARY_DIRS} -pthread) 52 | target_link_libraries(nethuns-forward ${NETHUNS_LIBRARY_DIRS} -pthread) 53 | target_link_libraries(nethuns-forward-mt ${NETHUNS_LIBRARY_DIRS} -pthread) 54 | target_link_libraries(nethuns-file-pcap ${NETHUNS_LIBRARY_DIRS} -pthread) 55 | target_link_libraries(nethuns-meter ${NETHUNS_LIBRARY_DIRS} -pthread) 56 | target_link_libraries(nethuns-filter ${NETHUNS_LIBRARY_DIRS} -pthread) 57 | 58 | target_link_libraries(pure-pcap-forward -pthread ${NETHUNS_LIBPCAP_LIBRARY}) 59 | target_link_libraries(pure-pcap-forward-mt -pthread ${NETHUNS_LIBPCAP_LIBRARY}) 60 | target_link_libraries(pure-pcap-meter -pthread ${NETHUNS_LIBPCAP_LIBRARY}) 61 | target_link_libraries(pure-pcap-meter-mt -pthread ${NETHUNS_LIBPCAP_LIBRARY}) 62 | 63 | target_link_libraries(pcap-version ${NETHUNS_LIBPCAP_LIBRARY}) 64 | -------------------------------------------------------------------------------- /test/src/file-pcap-mt.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Larthia, University of Pisa. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | nethuns_spsc_queue *queue; 14 | 15 | std::atomic_bool stop{false}; 16 | 17 | int consumer() 18 | { 19 | for(;;) 20 | { 21 | auto pkt = reinterpret_cast(nethuns_spsc_pop(queue)); 22 | if (pkt) 23 | { 24 | nethuns_rx_release(pkt->sock, pkt->id); 25 | } 26 | 27 | if (nethuns_spsc_is_empty(queue) && stop.load(std::memory_order_relaxed)) 28 | { 29 | return 0; 30 | } 31 | } 32 | } 33 | 34 | 35 | int 36 | main(int argc, char *argv[]) 37 | try 38 | { 39 | if (argc < 2) 40 | { 41 | fprintf(stderr,"usage: %s file\n", argv[0]); 42 | return 0; 43 | } 44 | 45 | nethuns_init(); 46 | 47 | queue = nethuns_spsc_init(65536, sizeof(nethuns_packet)); 48 | if (!queue) { 49 | throw std::runtime_error("nethuns_spsc: internal error"); 50 | } 51 | 52 | nethuns_pcap_t *p; 53 | 54 | struct nethuns_socket_options opt = 55 | { 56 | .numblocks = 1 57 | , .numpackets = 1024 58 | , .packetsize = 2048 59 | , .timeout_ms = 0 60 | , .dir = nethuns_in_out 61 | , .capture = nethuns_cap_default 62 | , .mode = nethuns_socket_rx_tx 63 | , .timestamp = true 64 | , .promisc = false 65 | , .rxhash = false 66 | , .tx_qdisc_bypass = false 67 | , .xdp_prog = nullptr 68 | , .xdp_prog_sec = nullptr 69 | , .xsk_map_name = nullptr 70 | , .reuse_maps = false 71 | , .pin_dir = nullptr 72 | }; 73 | 74 | char errbuf[NETHUNS_ERRBUF_SIZE]; 75 | p = nethuns_pcap_open(&opt, argv[1], 0, errbuf); 76 | if (!p) 77 | { 78 | throw std::runtime_error(errbuf); 79 | } 80 | 81 | const unsigned char *frame; 82 | const nethuns_pkthdr_t * pkthdr; 83 | 84 | uint64_t pkt_id; 85 | 86 | std::thread t(consumer); 87 | 88 | do 89 | { 90 | pkt_id = nethuns_pcap_read(p, &pkthdr, &frame); 91 | if (nethuns_pkt_is_valid(pkt_id)) 92 | { 93 | struct nethuns_packet hdr { frame, pkthdr, nethuns_socket(p), pkt_id }; 94 | while (!nethuns_spsc_push(queue, &hdr)) 95 | { }; 96 | } 97 | } 98 | while (!nethuns_pkt_is_err(pkt_id)); 99 | 100 | std::cerr << "head: " << p->base.rx_ring.head << std::endl; 101 | 102 | stop.store(true, std::memory_order_relaxed); 103 | 104 | t.join(); 105 | 106 | nethuns_pcap_close(p); 107 | 108 | return 0; 109 | } 110 | catch(nethuns_exception &e) 111 | { 112 | if (e.sock) { 113 | nethuns_close(e.sock); 114 | } 115 | std::cerr << e.what() << std::endl; 116 | return 1; 117 | } 118 | catch(std::exception &e) 119 | { 120 | std::cerr << e.what() << std::endl; 121 | return 1; 122 | } 123 | -------------------------------------------------------------------------------- /src/nethuns/sockets/base.h: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Larthia, University of Pisa. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | #pragma once 6 | 7 | #include "../define.h" 8 | #include "../types.h" 9 | #include "../misc/compiler.h" 10 | 11 | struct nethuns_ring_slot; 12 | 13 | typedef int (*nethuns_filter_t)(void *ctx, const nethuns_pkthdr_t *pkthdr, const uint8_t *pkt); 14 | 15 | #ifndef TEMPLATE_ 16 | #define TEMPLATE_(x,y) x ## y 17 | #define TEMPLATE(x,y) TEMPLATE_(x,y) 18 | #endif 19 | 20 | struct nethuns_ring 21 | { 22 | size_t size; 23 | size_t pktsize; 24 | 25 | uint64_t head; 26 | uint64_t tail; 27 | 28 | size_t mask; 29 | size_t shift; 30 | 31 | struct nethuns_ring_slot *ring; 32 | }; 33 | 34 | struct nethuns_socket_base 35 | { 36 | char errbuf[NETHUNS_ERRBUF_SIZE]; 37 | 38 | struct nethuns_socket_options opt; 39 | struct nethuns_ring tx_ring; 40 | struct nethuns_ring rx_ring; 41 | char *devname; 42 | int queue; 43 | int ifindex; 44 | 45 | nethuns_filter_t filter; 46 | void * filter_ctx; 47 | }; 48 | 49 | typedef struct nethuns_socket_base nethuns_socket_base_t; 50 | 51 | #define nethuns_socket(_sock) ((struct nethuns_socket_base *)(_sock)) 52 | #define nethuns_const_socket(_sock) ((struct nethuns_socket_base const *)(_sock)) 53 | 54 | 55 | struct nethuns_pcap_pkthdr 56 | { 57 | struct nethuns_timeval ts; /* time stamp */ 58 | uint32_t caplen; /* length of portion present */ 59 | uint32_t len; /* length this packet (off wire) */ 60 | }; 61 | 62 | 63 | struct nethuns_pcap_patched_pkthdr { 64 | struct nethuns_timeval ts; /* time stamp */ 65 | uint32_t caplen; /* length of portion present */ 66 | uint32_t len; /* length of this packet (off wire) */ 67 | int index; 68 | unsigned short protocol; 69 | unsigned char pkt_type; 70 | }; 71 | 72 | 73 | struct nethuns_pcap_file_header 74 | { 75 | uint32_t magic; 76 | unsigned short version_major; 77 | unsigned short version_minor; 78 | int32_t thiszone; /* gmt to local correction */ 79 | uint32_t sigfigs; /* accuracy of timestamps */ 80 | uint32_t snaplen; /* max length saved portion of each pkt */ 81 | uint32_t linktype; /* data link type (LINKTYPE_*) */ 82 | }; 83 | 84 | 85 | struct nethuns_socket_pcapfile 86 | { 87 | struct nethuns_socket_base base; 88 | void * r; // either FILE * or pcap_t *, depending on the value of NETHUNS_USE_BULTIN_PCAP_READER 89 | uint32_t snaplen; 90 | uint32_t magic; 91 | }; 92 | 93 | typedef struct nethuns_socket_pcapfile nethuns_pcap_t; 94 | 95 | static __always_inline void 96 | nethuns_set_filter(nethuns_socket_t * s, nethuns_filter_t filter, void *ctx) 97 | { 98 | nethuns_socket(s)->filter = filter; 99 | nethuns_socket(s)->filter_ctx = ctx; 100 | } 101 | 102 | static __always_inline void 103 | nethuns_clear_filter(nethuns_socket_t * s) 104 | { 105 | nethuns_socket(s)->filter = NULL; 106 | nethuns_socket(s)->filter_ctx = NULL; 107 | } 108 | -------------------------------------------------------------------------------- /test/src/meter-mt.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Larthia, University of Pisa. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | 17 | std::atomic_long total; 18 | 19 | void meter() 20 | { 21 | auto now = std::chrono::system_clock::now(); 22 | for(;;) 23 | { 24 | now += std::chrono::seconds(1); 25 | std::this_thread::sleep_until(now); 26 | auto x = total.exchange(0); 27 | std::cerr << "pkt/sec: " << x << std::endl; 28 | } 29 | } 30 | 31 | nethuns_spsc_queue *queue; 32 | 33 | int consumer() 34 | { 35 | for(;;) 36 | { 37 | auto pkt = reinterpret_cast(nethuns_spsc_pop(queue)); 38 | if (pkt) { 39 | total++; 40 | nethuns_rx_release(pkt->sock, pkt->id); 41 | } 42 | } 43 | } 44 | 45 | 46 | int 47 | main(int argc, char *argv[]) 48 | try 49 | { 50 | if (argc < 2) 51 | { 52 | std::cerr << "usage: " << argv[0] << " dev" << std::endl; 53 | return 0; 54 | } 55 | 56 | nethuns_init(); 57 | 58 | queue = nethuns_spsc_init(65536, sizeof(nethuns_packet)); 59 | if (!queue) { 60 | throw std::runtime_error("nethuns_spsc: internal error"); 61 | } 62 | 63 | std::thread(meter).detach(); 64 | std::thread(consumer).detach(); 65 | 66 | struct nethuns_socket_options opt = 67 | { 68 | .numblocks = 64 69 | , .numpackets = 2048 70 | , .packetsize = 2048 71 | , .timeout_ms = 0 72 | , .dir = nethuns_in_out 73 | , .capture = nethuns_cap_default 74 | , .mode = nethuns_socket_rx_tx 75 | , .timestamp = true 76 | , .promisc = true 77 | , .rxhash = false 78 | , .tx_qdisc_bypass = true 79 | , .xdp_prog = nullptr 80 | , .xdp_prog_sec = nullptr 81 | , .xsk_map_name = nullptr 82 | , .reuse_maps = false 83 | , .pin_dir = nullptr 84 | }; 85 | 86 | char errbuf[NETHUNS_ERRBUF_SIZE]; 87 | 88 | nethuns_socket_t * s = nethuns_open(&opt, errbuf); 89 | if (!s) 90 | { 91 | throw std::runtime_error(errbuf); 92 | } 93 | 94 | if (nethuns_bind(s, argv[1], NETHUNS_ANY_QUEUE) < 0) 95 | { 96 | throw nethuns_exception(s); 97 | } 98 | 99 | const unsigned char *frame; 100 | const nethuns_pkthdr_t * pkthdr; 101 | 102 | for(;;) 103 | { 104 | uint64_t id; 105 | 106 | if ((id = nethuns_recv(s, &pkthdr, &frame))) 107 | { 108 | struct nethuns_packet p { frame, pkthdr, nethuns_socket(s), id }; 109 | 110 | while (!nethuns_spsc_push(queue, &p)) 111 | { }; 112 | } 113 | } 114 | 115 | nethuns_close(s); 116 | return 0; 117 | } 118 | catch(nethuns_exception &e) 119 | { 120 | if (e.sock) { 121 | nethuns_close(e.sock); 122 | } 123 | std::cerr << e.what() << std::endl; 124 | return 1; 125 | } 126 | catch(std::exception &e) 127 | { 128 | std::cerr << e.what() << std::endl; 129 | return 1; 130 | } 131 | 132 | -------------------------------------------------------------------------------- /src/nethuns/global.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2021 Larthia, University of Pisa. All rights reserved. 3 | * Use of this source code is governed by a BSD-style 4 | * license that can be found in the LICENSE file. 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #if defined __linux__ 12 | #include 13 | #endif 14 | 15 | #include "api.h" 16 | #include "global.h" 17 | #include "define.h" 18 | 19 | struct nethuns_global __nethuns_global; 20 | 21 | #ifdef NETHUNS_USE_LIBPCAP 22 | int nethuns_check_libpcap(size_t hsize, char *errbuf); 23 | #endif 24 | #ifdef NETHUNS_USE_NETMAP 25 | int nethuns_check_netmap(size_t hsize, char *errbuf); 26 | #endif 27 | #ifdef NETHUNS_USE_XDP 28 | int nethuns_check_xdp(size_t hsize, char *errbuf); 29 | #endif 30 | #ifdef NETHUNS_USE_TPACKET_V3 31 | int nethuns_check_tpacket_v3(size_t hsize, char *errbuf); 32 | #endif 33 | 34 | void 35 | nethuns_init_(size_t hsize, int socket) 36 | { 37 | char errbuf[NETHUNS_ERRBUF_SIZE]; 38 | 39 | switch (socket) { 40 | #ifdef NETHUNS_USE_LIBPCAP 41 | case NETHUNS_SOCKET_LIBPCAP: { 42 | if (nethuns_check_libpcap(hsize, errbuf) < 0) { 43 | nethuns_fprintf(stderr, "%s\n", errbuf); 44 | exit(EXIT_FAILURE); 45 | } 46 | } break; 47 | #endif 48 | #ifdef NETHUNS_USE_NETMAP 49 | case NETHUNS_SOCKET_NETMAP: { 50 | if (nethuns_check_netmap(hsize, errbuf) < 0) { 51 | nethuns_fprintf(stderr, "%s\n", errbuf); 52 | exit(EXIT_FAILURE); 53 | } 54 | } break; 55 | #endif 56 | #ifdef NETHUNS_USE_XDP 57 | case NETHUNS_SOCKET_XDP: { 58 | if (nethuns_check_xdp(hsize, errbuf) < 0) { 59 | nethuns_fprintf(stderr, "%s\n", errbuf); 60 | exit(EXIT_FAILURE); 61 | } 62 | } break; 63 | #endif 64 | #ifdef NETHUNS_USE_TPACKET_V3 65 | case NETHUNS_SOCKET_TPACKET3: { 66 | if (nethuns_check_tpacket_v3(hsize, errbuf) < 0) { 67 | nethuns_fprintf(stderr, "%s\n", errbuf); 68 | exit(EXIT_FAILURE); 69 | } 70 | } break; 71 | #endif 72 | default: { 73 | nethuns_fprintf(stderr, "unsupported socket type %d\n", socket); 74 | exit(EXIT_FAILURE); 75 | } 76 | } 77 | 78 | pthread_mutex_init(&__nethuns_global.m, NULL); 79 | nethuns_fprintf(stderr, "initializing %s...\n", nethuns_version()); 80 | 81 | if (hashmap_create(64, &__nethuns_global.netinfo_map)) { 82 | nethuns_fprintf(stderr, "could not create netinfo hashmap\n"); 83 | exit(EXIT_FAILURE); 84 | } 85 | 86 | #if defined __linux__ 87 | { 88 | struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY}; 89 | 90 | if (setrlimit(RLIMIT_MEMLOCK, &r)) { 91 | nethuns_fprintf(stderr, "setrlimit(RLIMIT_MEMLOCK) \"%s\"\n", strerror(errno)); 92 | exit(EXIT_FAILURE); 93 | } 94 | } 95 | #endif 96 | } 97 | 98 | void __attribute__ ((destructor)) 99 | nethuns_fini() { 100 | if (__nethuns_global.netinfo_map.data != NULL) { 101 | nethuns_fprintf(stderr, "cleanup...\n"); 102 | hashmap_destroy(&__nethuns_global.netinfo_map); 103 | } 104 | } 105 | 106 | struct nethuns_netinfo * 107 | nethuns_lookup_netinfo(const char *dev) 108 | { 109 | return hashmap_get(&__nethuns_global.netinfo_map, dev, strlen(dev)); 110 | } 111 | 112 | struct nethuns_netinfo * 113 | nethuns_create_netinfo(const char *dev) 114 | { 115 | void * data = malloc(sizeof(struct nethuns_netinfo)); 116 | nethuns_netinfo_init(data); 117 | 118 | if (hashmap_put(&__nethuns_global.netinfo_map, dev, strlen(dev), data) != 0) 119 | { 120 | nethuns_netinfo_fini(data); 121 | free(data); 122 | return NULL; 123 | } 124 | return data; 125 | } 126 | -------------------------------------------------------------------------------- /test/src/capture.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2021 Larthia, University of Pisa. All rights reserved. 3 | * Use of this source code is governed by a BSD-style 4 | * license that can be found in the LICENSE file. 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | 12 | void dump_packet(nethuns_pkthdr_t const *hdr, const unsigned char *frame) 13 | { 14 | int i = 0; 15 | 16 | printf("%u:%u snap:%u len:%u offload{tci:%x tpid:%x} packet{tci:%x pid:%x} => [tci:%x tpid:%x vid:%d] rxhash:0x%x| ", nethuns_tstamp_sec(hdr) 17 | , nethuns_tstamp_nsec(hdr) 18 | , nethuns_snaplen(hdr) 19 | , nethuns_len(hdr) 20 | , nethuns_offvlan_tci(hdr) 21 | , nethuns_offvlan_tpid(hdr) 22 | , nethuns_vlan_tci(frame) 23 | , nethuns_vlan_tpid(frame) 24 | , nethuns_vlan_tci_(hdr, frame) 25 | , nethuns_vlan_tpid_(hdr, frame) 26 | , nethuns_vlan_vid(nethuns_vlan_tci_(hdr, frame)) 27 | , nethuns_rxhash(hdr)); 28 | for(; i < 14; i++) 29 | { 30 | printf("%02x ", frame[i]); 31 | } 32 | printf("\n"); 33 | } 34 | 35 | 36 | int 37 | main(int argc, char *argv[]) 38 | { 39 | nethuns_socket_t *s; 40 | 41 | if (argc < 2) 42 | { 43 | fprintf(stderr,"usage: %s dev\n", argv[0]); 44 | return 0; 45 | } 46 | 47 | nethuns_init(); 48 | 49 | struct nethuns_socket_options opt = 50 | { 51 | .numblocks = 4 52 | , .numpackets = 4096 53 | , .packetsize = 2048 54 | , .dir = nethuns_in_out 55 | , .capture = nethuns_cap_default 56 | , .mode = nethuns_socket_rx_tx 57 | , .timestamp = true 58 | , .promisc = true 59 | , .rxhash = true 60 | , .tx_qdisc_bypass = false 61 | , .xdp_prog = NULL 62 | // , .xdp_prog = "/etc/nethuns/net_xdp.o" 63 | , .xdp_prog_sec = NULL 64 | , .xsk_map_name = NULL 65 | , .reuse_maps = false 66 | , .pin_dir = NULL 67 | }; 68 | 69 | char errbuf[NETHUNS_ERRBUF_SIZE]; 70 | 71 | s = nethuns_open(&opt, errbuf); 72 | if (!s) 73 | { 74 | fprintf(stderr, "%s\n", errbuf); 75 | return -1; 76 | } 77 | 78 | if (nethuns_bind(s, argv[1], NETHUNS_ANY_QUEUE) < 0) 79 | { 80 | fprintf(stderr, "%s\n", nethuns_error(s)); 81 | return -1; 82 | } 83 | 84 | const unsigned char *frame; 85 | const nethuns_pkthdr_t *pkthdr; 86 | 87 | fprintf(stderr, "reading...\n"); 88 | for(int i =0; i < 1000;) 89 | { 90 | uint64_t pkt_id; 91 | 92 | if ((pkt_id = nethuns_recv(s, &pkthdr, &frame))) 93 | { 94 | dump_packet(pkthdr, frame); 95 | nethuns_rx_release(s, pkt_id); 96 | i++; 97 | } else { 98 | usleep(1); 99 | } 100 | } 101 | 102 | printf("done.\n"); 103 | nethuns_close(s); 104 | return 0; 105 | } 106 | -------------------------------------------------------------------------------- /scripts/Nethuns.cmake: -------------------------------------------------------------------------------- 1 | # Copyright 2021 Larthia, University of Pisa. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | cmake_minimum_required(VERSION 3.10) 6 | 7 | # 8 | # Nethuns options... 9 | # 10 | 11 | set(NETHUNS_OPT_LIBPCAP @NETHUNS_OPT_LIBPCAP@) 12 | set(NETHUNS_OPT_XDP @NETHUNS_OPT_XDP@) 13 | set(NETHUNS_OPT_NETMAP @NETHUNS_OPT_NETMAP@) 14 | set(NETHUNS_OPT_TPACKET_V3 @NETHUNS_OPT_TPACKET_V3@) 15 | 16 | set(NETHUNS_SOCK_LIST) 17 | 18 | if (NETHUNS_OPT_LIBPCAP) 19 | list(APPEND NETHUNS_SOCK_LIST "libpcap") 20 | endif() 21 | if (NETHUNS_OPT_XDP) 22 | list(APPEND NETHUNS_SOCK_LIST "xdp") 23 | endif() 24 | if (NETHUNS_OPT_NETMAP) 25 | list(APPEND NETHUNS_SOCK_LIST "netmap") 26 | endif() 27 | if (NETHUNS_OPT_TPACKET_V3) 28 | list(APPEND NETHUNS_SOCK_LIST "tpacket3") 29 | endif() 30 | 31 | list(GET NETHUNS_SOCK_LIST 0 FIRST_SOCKET) 32 | 33 | message ("Nethuns: Available sockets: ${NETHUNS_SOCK_LIST}, default: ${FIRST_SOCKET}") 34 | 35 | set(NETHUNS_CAPTURE_SOCKET ${FIRST_SOCKET} CACHE STRING "Nethuns underlying socket") 36 | set_property(CACHE NETHUNS_CAPTURE_SOCKET PROPERTY STRINGS ${NETHUNS_SOCK_LIST}) 37 | 38 | set(NETHUNS_LIBRARY_DIRS) 39 | set(NETHUNS_INCLUDE_DIRS) 40 | 41 | # 42 | # Nethuns installation... 43 | 44 | if (USE_LOCAL_NETHUNS) 45 | message ("Nethuns: using local installation") 46 | list(APPEND NETHUNS_INCLUDE_DIRS ${CMAKE_BINARY_DIR}/include) 47 | list(APPEND NETHUNS_LIBRARY_DIRS ${CMAKE_BINARY_DIR}/libs/Nethuns/libnethuns.a) 48 | list(APPEND NETHUNS_LIBBPF_LIBRARY libbpf/src/libbpf.a) 49 | else() 50 | message ("Nethuns: using system installation") 51 | list(APPEND NETHUNS_INCLUDE_DIRS ${CMAKE_INSTALL_PREFIX}/include) 52 | list(APPEND NETHUNS_LIBRARY_DIRS ${CMAKE_INSTALL_PREFIX}/lib/libnethuns.a) 53 | find_library(NETHUNS_LIBBPF_LIBRARY bpf) 54 | endif() 55 | 56 | # 57 | # detecting libpcap installation... 58 | 59 | if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") 60 | find_library(NETHUNS_LIBPCAP_LIBRARY NAMES pcap PATHS "/usr/local/opt/libpcap/lib" "/opt/homebrew/opt/libpcap/lib" NO_DEFAULT_PATH) 61 | find_path(NETHUNS_LIBPCAP_INCLUDE NAMES pcap/pcap.h PATHS "/usr/local/opt/libpcap/include/" "/opt/homebrew/opt/libpcap/include" NO_DEFAULT_PATH) 62 | elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") 63 | find_library(NETHUNS_LIBPCAP_LIBRARY pcap PATHS "/usr/local/lib") 64 | find_path(NETHUNS_LIBPCAP_INCLUDE NAMES pcap/pcap.h PATHS "/usr/local/include") 65 | else() 66 | message(FATAL_ERROR "${CMAKE_SYSTEM_NAME} platform not supported!") 67 | endif() 68 | 69 | 70 | list(APPEND NETHUNS_INCLUDE_DIRS ${NETHUNS_LIBPCAP_INCLUDE}) 71 | list(APPEND NETHUNS_LIBRARY_DIRS ${NETHUNS_LIBPCAP_LIBRARY}) 72 | 73 | # 74 | # detecting other libraries... 75 | 76 | find_package(ZLIB) 77 | find_library(NETHUNS_LIBELF_LIBRARY elf) 78 | find_library(NETHUNS_NETMAP_LIBRARY netmap) 79 | 80 | if (NETHUNS_CAPTURE_SOCKET STREQUAL "tpacket3") 81 | message ("Nethuns: TPACKET_v3 enabled!") 82 | add_definitions(-DNETHUNS_SOCKET=3) 83 | 84 | elseif (NETHUNS_CAPTURE_SOCKET STREQUAL "xdp") 85 | message ("Nethuns: AF_XDP enabled!") 86 | add_definitions(-DNETHUNS_SOCKET=2) 87 | 88 | elseif (NETHUNS_CAPTURE_SOCKET STREQUAL "netmap") 89 | message ("Nethuns: netmap socket enabled!") 90 | add_definitions(-DNETHUNS_SOCKET=1) 91 | 92 | elseif (NETHUNS_CAPTURE_SOCKET STREQUAL "libpcap") 93 | message ("Nethuns: basic libpcap enabled!") 94 | add_definitions(-DNETHUNS_SOCKET=0) 95 | endif() 96 | 97 | if (NETHUNS_OPT_XDP) 98 | list(APPEND NETHUNS_INCLUDE_DIRS ${CMAKE_INSTALL_PREFIX}/include/nethuns/sockets/xdp/) 99 | list(APPEND NETHUNS_LIBRARY_DIRS ${NETHUNS_LIBBPF_LIBRARY}) 100 | list(APPEND NETHUNS_LIBRARY_DIRS ${NETHUNS_LIBELF_LIBRARY}) 101 | list(APPEND NETHUNS_LIBRARY_DIRS ZLIB::ZLIB) 102 | endif() 103 | 104 | if (NETHUNS_OPT_NETMAP) 105 | list(APPEND NETHUNS_LIBRARY_DIRS ${NETHUNS_NETMAP_LIBRARY}) 106 | endif() 107 | -------------------------------------------------------------------------------- /src/nethuns/network/icmp6.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | enum icmp6_type 7 | { 8 | ICMP6_DST_UNREACH = 1, 9 | ICMP6_PACKET_TOO_BIG = 2, 10 | ICMP6_TIME_EXCEEDED = 3, 11 | ICMP6_PARAM_PROB = 4, 12 | ICMP6_ECHO_REQUEST = 128, 13 | ICMP6_ECHO_REPLY = 129, 14 | ICMP6_MLD_LISTENER_QUERY = 130, 15 | ICMP6_MLD_LISTENER_REPORT = 131, 16 | ICMP6_MLD_LISTENER_REDUCTION = 132, 17 | 18 | ICMP6_ROUTER_SOLICITATION = 133, 19 | ICMP6_ROUTER_ADVERTISEMENT = 134, 20 | ICMP6_NEIGHBOR_SOLICITATION = 135, 21 | ICMP6_NEIGHBOR_ADVERTISEMENT = 136, 22 | ICMP6_REDIRECT_MESSAGE = 137, 23 | ICMP6_ROUTER_RENUMBERING = 138, 24 | ICMP6_NODE_INFORMATION_QUERY = 139, 25 | ICMP6_NODE_INFORMATION_RESPONSE = 140, 26 | 27 | ICMP6_INVERSE_NEIGHBOR_DISCOVERY_SOLICITATION = 141, 28 | ICMP6_INVERSE_NEIGHBOR_DISCOVERY_ADVERTISEMENT = 142, 29 | ICMP6_MULTICAST_LISTENER_DISCOVERY_REPORTS = 143, 30 | ICMP6_HOME_AGENT_ADDRESS_DISCOVERY_REQUEST = 144, 31 | ICMP6_HOME_AGENT_ADDRESS_DISCOVERY_REPLY = 145, 32 | ICMP6_MOBILE_PREFIX_SOLICITATION = 146, 33 | ICMP6_MOBILE_PREFIX_ADVERTISEMENT = 147, 34 | ICMP6_CERTIFICATION_PATH_SOLICITATION = 148, 35 | ICMP6_CERTIFICATION_PATH_ADVERTISEMENT = 149, 36 | ICMP6_EXPERIMENTAL_MOBILITY = 150, 37 | ICMP6_MULTICAST_ROUTER_ADVERTISEMENT = 151, 38 | ICMP6_MULTICAST_ROUTER_SOLICITATION = 152, 39 | ICMP6_MULTICAST_ROUTER_TERMINATION = 153, 40 | ICMP6_FMIPV6 = 154, 41 | ICMP6_RPL_CONTROL_MESSAGE = 155, 42 | ICMP6_ILNPV6_LOCATOR_UPDATE = 156, 43 | ICMP6_DUPLICATE_ADDRESS_REQUEST = 157, 44 | ICMP6_DUPLICATE_ADDRESS_CONFIRM = 158, 45 | ICMP6_MPL_CONTROL_MESSAGE = 159, 46 | ICMP6_EXTENDED_ECHO_REQUEST = 160, 47 | ICMP6_EXTENDED_ECHO_REPLY = 161, 48 | }; 49 | 50 | enum icmp6_code_unreach 51 | { 52 | ICMP6_DST_UNREACH_NOROUTE = 0, /* no route to destination */ 53 | ICMP6_DST_UNREACH_ADMIN = 1, /* communication with destination */ 54 | ICMP6_DST_UNREACH_BEYONDSCOPE = 2, /* beyond scope of source address */ 55 | ICMP6_DST_UNREACH_ADDR = 3, /* address unreachable */ 56 | ICMP6_DST_UNREACH_NOPORT = 4 /* bad port */ 57 | }; 58 | 59 | enum icmp6_code_time_exceed 60 | { 61 | ICMP6_TIME_EXCEED_TRANSIT = 0, /* Hop Limit == 0 in transit */ 62 | ICMP6_TIME_EXCEED_REASSEMBLY = 1 /* Reassembly time out */ 63 | }; 64 | 65 | enum icmp6_param_prob 66 | { 67 | ICMP6_PARAMPROB_HEADER = 0, /* erroneous header field */ 68 | ICMP6_PARAMPROB_NEXTHEADER = 1, /* unrecognized Next Header */ 69 | ICMP6_PARAMPROB_OPTION = 2 /* unrecognized IPv6 option */ 70 | }; 71 | 72 | struct icmp6_hdr 73 | { 74 | icmp6_type type; /* message type */ 75 | uint8_t code; /* type sub-code */ 76 | uint16_t checksum; 77 | 78 | union 79 | { 80 | uint32_t data32[1]; /* type-specific field */ 81 | uint16_t data16[2]; /* type-specific field */ 82 | uint8_t data8[4]; /* type-specific field */ 83 | } un; 84 | 85 | } __attribute__((packed,aligned(4))); 86 | -------------------------------------------------------------------------------- /tools/nethuns-gen/hdr/packet.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | struct packet 14 | { 15 | pcap_pkthdr hdr_ = {}; 16 | std::shared_ptr data_; 17 | std::size_t len_ = 0; 18 | 19 | template 20 | static inline auto from_hex_stream(const char (&str)[N]) -> packet { 21 | auto pkt = std::shared_ptr(new uint8_t[N/2], std::default_delete()); 22 | for (std::size_t i = 0; i < N/2; ++i) { 23 | pkt.get()[i] = std::stoi(std::string(str + i*2, 2), nullptr, 16); 24 | } 25 | return packet{ .hdr_ = {}, .data_ = pkt, .len_ = N/2 }; 26 | } 27 | 28 | template 29 | static inline auto from_c_string(const char (&str)[N]) -> packet { 30 | auto pkt = std::shared_ptr(new uint8_t[N], std::default_delete()); 31 | memcpy(pkt.get(), str, N); 32 | return { .hdr_ ={}, .data_ = pkt, .len_ = N }; 33 | } 34 | 35 | static inline auto from_pcap(const char *pcap_file) -> std::vector { 36 | char errbuf[PCAP_ERRBUF_SIZE]; 37 | std::cerr << "preloading " << pcap_file << "..." << std::endl; 38 | auto pcap = ::pcap_open_offline(pcap_file, errbuf); 39 | if (!pcap) { 40 | throw std::runtime_error("pcap_open_offline: " + std::string(errbuf)); 41 | } 42 | 43 | std::vector packets; 44 | packets.reserve(65536); 45 | 46 | struct pcap_pkthdr *hdr; 47 | const uint8_t *data; 48 | while (int ret = ::pcap_next_ex(pcap, &hdr, &data)) { 49 | if (ret == -1) { 50 | throw std::runtime_error("pcap_next_ex: " + std::string(pcap_geterr(pcap))); 51 | } 52 | if (ret == -2) { 53 | break; 54 | } 55 | packets.push_back({ .hdr_ = *hdr, .data_ = make_buf(const_cast(data), hdr->len), .len_ = hdr->len}); 56 | } 57 | 58 | ::pcap_close(pcap); 59 | std::cerr << "preload done." << std::endl; 60 | return packets; 61 | } 62 | 63 | static auto builder(const std::string &name, std::optional len) -> std::vector 64 | { 65 | auto it = catalog_.find(name); 66 | if (it == catalog_.end()) { 67 | throw std::runtime_error("packet '" + name + "' not found!"); 68 | } 69 | 70 | return it->second(len); 71 | } 72 | 73 | void set_mac_source(const std::string &text) { 74 | if (!ether_aton(text, data_.get() + 6)) { 75 | throw std::runtime_error("invalid mac address: " + text); 76 | } 77 | } 78 | 79 | void set_mac_dest(const std::string &text) { 80 | if (!ether_aton(text, data_.get())) { 81 | throw std::runtime_error("invalid mac address: " + text); 82 | } 83 | } 84 | 85 | void resize(uint16_t size) { 86 | if (size < len_) { 87 | len_ = size; 88 | } else { 89 | auto new_data = std::shared_ptr(new uint8_t[size], std::default_delete()); 90 | memcpy(new_data.get(), data_.get(), len_); 91 | bzero(new_data.get() + len_, size - len_); 92 | data_ = new_data; 93 | len_ = size; 94 | } 95 | } 96 | 97 | private: 98 | 99 | static inline bool 100 | ether_aton(std::string const & macString, unsigned char *bytes) 101 | { 102 | unsigned char mac[6]; 103 | 104 | if (sscanf(macString.c_str(), "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]) != 6) 105 | { 106 | return false; 107 | } 108 | 109 | memcpy(bytes, mac, 6); 110 | return true; 111 | } 112 | 113 | static inline auto make_buf(uint8_t *data, size_t len) -> std::shared_ptr { 114 | auto buf = std::shared_ptr(new uint8_t[len], std::default_delete()); 115 | memcpy(buf.get(), data, len); 116 | return buf; 117 | } 118 | 119 | static std::map(std::optional)>> catalog_; 120 | }; 121 | -------------------------------------------------------------------------------- /src/nethuns/nethuns.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2021 Larthia, University of Pisa. All rights reserved. 3 | * Use of this source code is governed by a BSD-style 4 | * license that can be found in the LICENSE file. 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | #include "sockets/base.h" 17 | #include "global.h" 18 | #include "define.h" 19 | #include "types.h" 20 | #include "api.h" 21 | 22 | void 23 | nethuns_fprintf(FILE *out, const char *msg, ...) 24 | { 25 | va_list ap; 26 | va_start(ap, msg); 27 | fprintf(out, "nethuns: "); 28 | vfprintf(out, msg, ap); 29 | va_end(ap); 30 | } 31 | 32 | 33 | void 34 | nethuns_perror(char *buf, const char *msg, ...) 35 | { 36 | va_list ap; 37 | va_start(ap, msg); 38 | int n; 39 | 40 | n = vsnprintf(buf, NETHUNS_ERRBUF_SIZE, msg, ap); 41 | if (errno != 0) { 42 | snprintf(buf+n, NETHUNS_ERRBUF_SIZE - n, " (%s)", strerror(errno)); 43 | } 44 | va_end(ap); 45 | } 46 | 47 | int 48 | nethuns_ioctl_if(nethuns_socket_t *s, const char *devname, unsigned long what, uint32_t *flags) 49 | { 50 | struct ifreq ifr; 51 | int rv; 52 | 53 | int fd = socket(AF_INET, SOCK_DGRAM, 0); 54 | if (fd < 0) { 55 | nethuns_perror(nethuns_socket(s)->errbuf, "ioctl: could not open socket"); 56 | return -1; 57 | } 58 | 59 | bzero(&ifr, sizeof(ifr)); 60 | strncpy(ifr.ifr_name, devname, sizeof(ifr.ifr_name)-1); 61 | 62 | if (what == SIOCSIFFLAGS) 63 | ifr.ifr_flags = *flags; 64 | 65 | rv = ioctl(fd, what, &ifr); 66 | if (rv < 0) 67 | { 68 | nethuns_perror(nethuns_socket(s)->errbuf, "ioctl"); 69 | close (fd); 70 | return -1; 71 | } 72 | 73 | if (what == SIOCGIFFLAGS) 74 | *flags = ifr.ifr_flags; 75 | 76 | close (fd); 77 | return 0; 78 | } 79 | 80 | 81 | void 82 | __nethuns_free_base(nethuns_socket_t *s) 83 | { 84 | free(nethuns_socket(s)->devname); 85 | free(nethuns_socket(s)->rx_ring.ring); 86 | } 87 | 88 | 89 | int 90 | __nethuns_set_if_promisc(nethuns_socket_t *s, char const *devname) 91 | { 92 | uint32_t flags; 93 | struct nethuns_netinfo *info; 94 | bool do_promisc; 95 | 96 | if (nethuns_ioctl_if(s, devname, SIOCGIFFLAGS, &flags) < 0) 97 | return -1; 98 | 99 | nethuns_lock_global(); 100 | 101 | info = nethuns_lookup_netinfo(devname); 102 | if (info == NULL) { 103 | info = nethuns_create_netinfo(devname); 104 | if (info == NULL) { 105 | nethuns_unlock_global(); 106 | return -1; 107 | } 108 | 109 | info->promisc_refcnt = (flags & IFF_PROMISC) ? 1 : 0; 110 | } 111 | 112 | info->promisc_refcnt++; 113 | 114 | do_promisc = !(flags & IFF_PROMISC); 115 | 116 | if (do_promisc) 117 | { 118 | flags |= IFF_PROMISC; 119 | if (nethuns_ioctl_if(s, devname, SIOCSIFFLAGS, &flags) < 0) 120 | { 121 | info->promisc_refcnt--; 122 | nethuns_unlock_global(); 123 | return -1; 124 | } 125 | } 126 | 127 | if (do_promisc) 128 | nethuns_fprintf(stderr, "device %s promisc mode set\n", devname); 129 | else 130 | nethuns_fprintf(stderr, "device %s (already) promisc mode set\n", devname); 131 | 132 | nethuns_unlock_global(); 133 | return 0; 134 | } 135 | 136 | 137 | int 138 | __nethuns_clear_if_promisc(nethuns_socket_t *s, char const *devname) 139 | { 140 | uint32_t flags; 141 | struct nethuns_netinfo *info; 142 | bool do_clear = false; 143 | 144 | if (nethuns_ioctl_if(s, devname, SIOCGIFFLAGS, &flags) < 0) 145 | return -1; 146 | 147 | nethuns_lock_global(); 148 | 149 | info = nethuns_lookup_netinfo(devname); 150 | if (info != NULL) { 151 | if(--info->promisc_refcnt <= 0) { 152 | do_clear = true; 153 | } 154 | } 155 | 156 | if (do_clear) { 157 | flags &= ~IFF_PROMISC; 158 | if (nethuns_ioctl_if(s, devname, SIOCSIFFLAGS, &flags) < 0) { 159 | nethuns_unlock_global(); 160 | return -1; 161 | } 162 | nethuns_fprintf(stderr, "device %s promisc mode unset\n", devname); 163 | } 164 | 165 | nethuns_unlock_global(); 166 | return 0; 167 | } 168 | -------------------------------------------------------------------------------- /src/nethuns/sockets/libpcap.h: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Larthia, University of Pisa. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | # pragma once 6 | 7 | #include 8 | #include 9 | 10 | #include "../misc/compiler.h" 11 | #include "../misc/macro.h" 12 | #include "../vlan.h" 13 | #include "../types.h" 14 | #include "base.h" 15 | 16 | struct nethuns_socket_libpcap 17 | { 18 | struct nethuns_socket_base base; 19 | pcap_t *p; 20 | }; 21 | 22 | #ifdef __cplusplus 23 | extern "C" { 24 | #endif 25 | 26 | 27 | int nethuns_check_libpcap(size_t hsize, char *errbuf); 28 | 29 | nethuns_pcap_t * 30 | nethuns_pcap_open_libpcap(struct nethuns_socket_options *opt, const char *filename, int mode, char *errbuf); 31 | 32 | int 33 | nethuns_pcap_close_libpcap(nethuns_pcap_t *p); 34 | 35 | uint64_t 36 | nethuns_pcap_read_libpcap(nethuns_pcap_t *p, nethuns_pkthdr_t const **pkthdr, uint8_t const **payload); 37 | 38 | int 39 | nethuns_pcap_write_libpcap(nethuns_pcap_t *s, struct nethuns_pcap_pkthdr const *header, uint8_t const *packet, unsigned int len); 40 | 41 | int 42 | nethuns_pcap_store_libpcap(nethuns_pcap_t *s, nethuns_pkthdr_t const *pkthdr, uint8_t const *packet, unsigned int len); 43 | 44 | int 45 | nethuns_pcap_rewind_libpcap(nethuns_pcap_t *s); 46 | 47 | 48 | struct nethuns_socket_libpcap * 49 | nethuns_open_libpcap(struct nethuns_socket_options *opt, char *errbuf); 50 | 51 | int 52 | nethuns_close_libpcap(struct nethuns_socket_libpcap *s); 53 | 54 | int 55 | nethuns_bind_libpcap(struct nethuns_socket_libpcap *s, const char *dev, int queue); 56 | 57 | uint64_t 58 | nethuns_recv_libpcap(struct nethuns_socket_libpcap *s, nethuns_pkthdr_t const **pkthdr, uint8_t const **payload); 59 | 60 | int 61 | nethuns_send_libpcap(struct nethuns_socket_libpcap *s, uint8_t const *packet, unsigned int len); 62 | 63 | static __always_inline uint8_t * 64 | nethuns_get_buf_addr_libpcap(__maybe_unused nethuns_socket_t * s, __maybe_unused uint64_t pktid) { 65 | return NULL; 66 | } 67 | 68 | int 69 | nethuns_flush_libpcap(__maybe_unused struct nethuns_socket_libpcap *s); 70 | 71 | int 72 | nethuns_stats_libpcap(struct nethuns_socket_libpcap *s, struct nethuns_stat *stats); 73 | 74 | int 75 | nethuns_fanout_libpcap(__maybe_unused struct nethuns_socket_libpcap *s, __maybe_unused int group, __maybe_unused const char *fanout); 76 | 77 | int 78 | nethuns_fd_libpcap(__maybe_unused struct nethuns_socket_libpcap *s); 79 | 80 | void 81 | nethuns_dump_rings_libpcap(__maybe_unused struct nethuns_socket_libpcap *s); 82 | 83 | 84 | static __always_inline uint32_t 85 | nethuns_tstamp_sec_libpcap(struct pcap_pkthdr const *hdr) { 86 | return (uint32_t)hdr->ts.tv_sec; 87 | } 88 | 89 | static __always_inline uint32_t 90 | nethuns_tstamp_usec_libpcap(struct pcap_pkthdr const *hdr) { 91 | return (uint32_t)hdr->ts.tv_usec; 92 | } 93 | 94 | static __always_inline uint32_t 95 | nethuns_tstamp_nsec_libpcap(struct pcap_pkthdr const *hdr) { 96 | return (uint32_t)hdr->ts.tv_usec * 1000; 97 | } 98 | 99 | static __always_inline 100 | void nethuns_tstamp_set_sec_libpcap(struct pcap_pkthdr *hdr, uint32_t v) { 101 | hdr->ts.tv_sec = v; 102 | } 103 | 104 | static __always_inline 105 | void nethuns_tstamp_set_usec_libpcap(struct pcap_pkthdr *hdr, uint32_t v) { 106 | hdr->ts.tv_usec = v; 107 | } 108 | 109 | static __always_inline 110 | void nethuns_tstamp_set_nsec_libpcap(struct pcap_pkthdr *hdr, uint32_t v) { 111 | hdr->ts.tv_usec = v/1000; 112 | } 113 | 114 | static __always_inline uint32_t 115 | nethuns_snaplen_libpcap(struct pcap_pkthdr const *hdr) { 116 | return hdr->caplen; 117 | } 118 | 119 | static __always_inline uint32_t 120 | nethuns_len_libpcap(struct pcap_pkthdr const *hdr) { 121 | return hdr->len; 122 | } 123 | 124 | static __always_inline void 125 | nethuns_set_snaplen_libpcap(struct pcap_pkthdr *hdr, uint32_t v) { 126 | hdr->caplen = v; 127 | } 128 | 129 | static __always_inline void 130 | nethuns_set_len_libpcap(struct pcap_pkthdr *hdr, uint32_t v) { 131 | hdr->len = v; 132 | } 133 | 134 | static __always_inline uint32_t 135 | nethuns_rxhash_libpcap(__maybe_unused struct pcap_pkthdr const *hdr) { 136 | return 0; 137 | } 138 | 139 | static __always_inline uint16_t 140 | nethuns_offvlan_tpid_libpcap(__maybe_unused struct pcap_pkthdr const *hdr) { 141 | return 0; 142 | } 143 | 144 | static __always_inline uint16_t 145 | nethuns_offvlan_tci_libpcap(__maybe_unused struct pcap_pkthdr const *hdr) { 146 | return 0; 147 | } 148 | 149 | 150 | #ifdef __cplusplus 151 | } 152 | #endif 153 | -------------------------------------------------------------------------------- /test/src/forward.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Larthia, University of Pisa. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | void dump_packet(nethuns_pkthdr_t *hdr, const unsigned char *frame) 14 | { 15 | int i = 0; 16 | 17 | printf("%u:%u snap:%u len:%u rxhash:0x%x| ", nethuns_tstamp_sec(hdr) 18 | , nethuns_tstamp_nsec(hdr) 19 | , nethuns_snaplen(hdr) 20 | , nethuns_len(hdr) 21 | , nethuns_rxhash(hdr)); 22 | for(; i < 14; i++) 23 | { 24 | printf("%02x ", frame[i]); 25 | } 26 | 27 | printf("\n"); 28 | } 29 | 30 | 31 | std::atomic_long total_rcv; 32 | std::atomic_long total_fwd; 33 | 34 | 35 | void meter() 36 | { 37 | auto now = std::chrono::system_clock::now(); 38 | for(;;) 39 | { 40 | now += std::chrono::seconds(1); 41 | std::this_thread::sleep_until(now); 42 | auto r = total_rcv.exchange(0); 43 | auto f = total_fwd.exchange(0); 44 | std::cout << "pkt/sec: " << r << " fwd/sec: " << f << std::endl; 45 | } 46 | } 47 | 48 | 49 | int 50 | main(int argc, char *argv[]) 51 | try 52 | { 53 | if (argc < 3) 54 | { 55 | std::cerr << "usage: " << argv[0] << " in out" << std::endl; 56 | return 0; 57 | } 58 | 59 | nethuns_init(); 60 | 61 | std::thread(meter).detach(); 62 | 63 | struct nethuns_socket_options in_opt = 64 | { 65 | .numblocks = 4 66 | , .numpackets = 65536 67 | , .packetsize = 2048 68 | , .timeout_ms = 20 69 | , .dir = nethuns_in_out 70 | , .capture = nethuns_cap_default 71 | , .mode = nethuns_socket_rx_tx 72 | , .timestamp = true 73 | , .promisc = true 74 | , .rxhash = false 75 | , .tx_qdisc_bypass = true 76 | , .xdp_prog = nullptr 77 | , .xdp_prog_sec = nullptr 78 | , .xsk_map_name = nullptr 79 | , .reuse_maps = false 80 | , .pin_dir = nullptr 81 | }; 82 | 83 | struct nethuns_socket_options out_opt = 84 | { 85 | .numblocks = 4 86 | , .numpackets = 65536 87 | , .packetsize = 2048 88 | , .timeout_ms = 20 89 | , .dir = nethuns_in_out 90 | , .capture = nethuns_cap_default 91 | , .mode = nethuns_socket_rx_tx 92 | , .timestamp = true 93 | , .promisc = true 94 | , .rxhash = false 95 | , .tx_qdisc_bypass = true 96 | , .xdp_prog = nullptr 97 | , .xdp_prog_sec = nullptr 98 | , .xsk_map_name = nullptr 99 | , .reuse_maps = false 100 | , .pin_dir = nullptr 101 | }; 102 | 103 | char errbuf[NETHUNS_ERRBUF_SIZE]; 104 | 105 | nethuns_socket_t *in = nethuns_open(&in_opt, errbuf); 106 | if (!in) 107 | { 108 | throw std::runtime_error(errbuf); 109 | } 110 | 111 | nethuns_socket_t *out = nethuns_open(&out_opt, errbuf); 112 | if (!out) 113 | { 114 | throw std::runtime_error(errbuf); 115 | } 116 | 117 | if (nethuns_bind(in, argv[1], NETHUNS_ANY_QUEUE) < 0) 118 | { 119 | throw nethuns_exception(in); 120 | } 121 | 122 | if (nethuns_bind(out, argv[2], NETHUNS_ANY_QUEUE) < 0) 123 | { 124 | throw nethuns_exception(out); 125 | } 126 | 127 | const unsigned char *frame; 128 | const nethuns_pkthdr_t *pkthdr; 129 | 130 | for(;;) 131 | { 132 | uint64_t pkt_id; 133 | 134 | if ((pkt_id = nethuns_recv(in, &pkthdr, &frame))) 135 | { 136 | total_rcv++; 137 | 138 | retry: 139 | while (!nethuns_send(out, frame, nethuns_len(pkthdr))) 140 | { 141 | nethuns_flush(out); 142 | goto retry; 143 | }; 144 | 145 | total_fwd++; 146 | 147 | nethuns_rx_release(in, pkt_id); 148 | } 149 | } 150 | 151 | nethuns_close(in); 152 | nethuns_close(out); 153 | return 0; 154 | } 155 | catch(nethuns_exception &e) 156 | { 157 | if (e.sock) { 158 | nethuns_close(e.sock); 159 | } 160 | std::cerr << e.what() << std::endl; 161 | return 1; 162 | } 163 | catch(std::exception &e) 164 | { 165 | std::cerr << e.what() << std::endl; 166 | return 1; 167 | } 168 | -------------------------------------------------------------------------------- /src/nethuns/sockets/netmap.h: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Larthia, University of Pisa. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | # pragma once 6 | 7 | #include 8 | 9 | #include 10 | 11 | #include "../types.h" 12 | #include "../misc/compiler.h" 13 | 14 | struct nethuns_socket_netmap 15 | { 16 | struct nethuns_socket_base base; 17 | struct nmport_d *p; 18 | struct netmap_ring *some_ring; 19 | uint32_t *free_ring; 20 | uint64_t free_mask; 21 | uint64_t free_head; 22 | uint64_t free_tail; 23 | bool tx; 24 | bool rx; 25 | }; 26 | 27 | #ifdef __cplusplus 28 | extern "C" { 29 | #endif 30 | 31 | int nethuns_check_netmap(size_t hsize, char *errbuf); 32 | 33 | nethuns_pcap_t * 34 | nethuns_pcap_open_netmap(struct nethuns_socket_options *opt, const char *filename, int mode, char *errbuf); 35 | 36 | int 37 | nethuns_pcap_close_netmap(nethuns_pcap_t *p); 38 | 39 | uint64_t 40 | nethuns_pcap_read_netmap(nethuns_pcap_t *p, nethuns_pkthdr_t const **pkthdr, uint8_t const **payload); 41 | 42 | int 43 | nethuns_pcap_write_netmap(nethuns_pcap_t *s, struct nethuns_pcap_pkthdr const *header, uint8_t const *packet, unsigned int len); 44 | 45 | int 46 | nethuns_pcap_store_netmap(nethuns_pcap_t *s, nethuns_pkthdr_t const *pkthdr, uint8_t const *packet, unsigned int len); 47 | 48 | int 49 | nethuns_pcap_rewind_netmap(nethuns_pcap_t *s); 50 | 51 | 52 | 53 | struct nethuns_socket_netmap * 54 | nethuns_open_netmap(struct nethuns_socket_options *opt, char *errbuf); 55 | 56 | int nethuns_close_netmap(struct nethuns_socket_netmap *s); 57 | 58 | int nethuns_bind_netmap(struct nethuns_socket_netmap *s, const char *dev, int queue); 59 | 60 | uint64_t 61 | nethuns_recv_netmap(struct nethuns_socket_netmap *s, nethuns_pkthdr_t const **pkthdr, uint8_t const **payload); 62 | 63 | int 64 | nethuns_send_netmap(struct nethuns_socket_netmap *s, uint8_t const *packet, unsigned int len); 65 | 66 | static __always_inline uint8_t * 67 | nethuns_get_buf_addr_netmap(struct nethuns_socket_netmap *s, uint64_t pktid) 68 | { 69 | return (uint8_t*)NETMAP_BUF(s->some_ring, 70 | nethuns_ring_get_slot(&nethuns_socket(s)->tx_ring, pktid)->pkthdr.buf_idx); 71 | } 72 | 73 | int 74 | nethuns_flush_netmap(struct nethuns_socket_netmap *s); 75 | 76 | int 77 | nethuns_stats_netmap(struct nethuns_socket_netmap *s, struct nethuns_stat *stats); 78 | 79 | int 80 | nethuns_fanout_netmap(__maybe_unused struct nethuns_socket_netmap *s, __maybe_unused int group, __maybe_unused const char *fanout); 81 | 82 | int nethuns_fd_netmap(__maybe_unused struct nethuns_socket_netmap *s); 83 | 84 | void 85 | nethuns_dump_rings_netmap(__maybe_unused struct nethuns_socket_netmap *s); 86 | 87 | static __always_inline uint32_t 88 | nethuns_tstamp_sec_netmap(struct netmap_pkthdr const *hdr) { 89 | return (uint32_t)hdr->ts.tv_sec; 90 | } 91 | 92 | static __always_inline uint32_t 93 | nethuns_tstamp_usec_netmap(struct netmap_pkthdr const *hdr) { 94 | return (uint32_t)hdr->ts.tv_usec; 95 | } 96 | 97 | static __always_inline uint32_t 98 | nethuns_tstamp_nsec_netmap(struct netmap_pkthdr const *hdr) { 99 | return (uint32_t)hdr->ts.tv_usec * 1000; 100 | } 101 | 102 | static __always_inline 103 | void nethuns_tstamp_set_sec_netmap(struct netmap_pkthdr *hdr, uint32_t v) { 104 | hdr->ts.tv_sec = v; 105 | } 106 | 107 | static __always_inline 108 | void nethuns_tstamp_set_usec_netmap(struct netmap_pkthdr *hdr, uint32_t v) { 109 | hdr->ts.tv_usec = v; 110 | } 111 | 112 | static __always_inline 113 | void nethuns_tstamp_set_nsec_netmap(struct netmap_pkthdr *hdr, uint32_t v) { 114 | hdr->ts.tv_usec = v/1000; 115 | } 116 | 117 | static __always_inline uint32_t 118 | nethuns_snaplen_netmap(struct netmap_pkthdr const *hdr) { 119 | return hdr->caplen; 120 | } 121 | 122 | static __always_inline uint32_t 123 | nethuns_len_netmap(struct netmap_pkthdr const *hdr) { 124 | return hdr->len; 125 | } 126 | 127 | static __always_inline void 128 | nethuns_set_snaplen_netmap(struct netmap_pkthdr *hdr, uint32_t v) { 129 | hdr->caplen = v; 130 | } 131 | 132 | static __always_inline void 133 | nethuns_set_len_netmap(struct netmap_pkthdr *hdr, uint32_t v) { 134 | hdr->len = v; 135 | } 136 | 137 | 138 | static __always_inline uint32_t 139 | nethuns_rxhash_netmap(__maybe_unused struct netmap_pkthdr const *hdr) { 140 | return 0; 141 | } 142 | 143 | static __always_inline uint16_t 144 | nethuns_offvlan_tpid_netmap(__maybe_unused struct netmap_pkthdr const *hdr) { 145 | return 0; 146 | } 147 | 148 | static __always_inline uint16_t 149 | nethuns_offvlan_tci_netmap(__maybe_unused struct netmap_pkthdr const *hdr) { 150 | return 0; 151 | } 152 | 153 | #ifdef __cplusplus 154 | } 155 | #endif 156 | -------------------------------------------------------------------------------- /misc/kselftest.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 */ 2 | /* 3 | * kselftest.h: kselftest framework return codes to include from 4 | * selftests. 5 | * 6 | * Copyright (c) 2014 Shuah Khan 7 | * Copyright (c) 2014 Samsung Electronics Co., Ltd. 8 | * 9 | */ 10 | #ifndef __KSELFTEST_H 11 | #define __KSELFTEST_H 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | /* define kselftest exit codes */ 18 | #define KSFT_PASS 0 19 | #define KSFT_FAIL 1 20 | #define KSFT_XFAIL 2 21 | #define KSFT_XPASS 3 22 | #define KSFT_SKIP 4 23 | 24 | /* counters */ 25 | struct ksft_count { 26 | unsigned int ksft_pass; 27 | unsigned int ksft_fail; 28 | unsigned int ksft_xfail; 29 | unsigned int ksft_xpass; 30 | unsigned int ksft_xskip; 31 | unsigned int ksft_error; 32 | }; 33 | 34 | static struct ksft_count ksft_cnt; 35 | 36 | static inline int ksft_test_num(void) 37 | { 38 | return ksft_cnt.ksft_pass + ksft_cnt.ksft_fail + 39 | ksft_cnt.ksft_xfail + ksft_cnt.ksft_xpass + 40 | ksft_cnt.ksft_xskip + ksft_cnt.ksft_error; 41 | } 42 | 43 | static inline void ksft_inc_pass_cnt(void) { ksft_cnt.ksft_pass++; } 44 | static inline void ksft_inc_fail_cnt(void) { ksft_cnt.ksft_fail++; } 45 | static inline void ksft_inc_xfail_cnt(void) { ksft_cnt.ksft_xfail++; } 46 | static inline void ksft_inc_xpass_cnt(void) { ksft_cnt.ksft_xpass++; } 47 | static inline void ksft_inc_xskip_cnt(void) { ksft_cnt.ksft_xskip++; } 48 | static inline void ksft_inc_error_cnt(void) { ksft_cnt.ksft_error++; } 49 | 50 | static inline int ksft_get_pass_cnt(void) { return ksft_cnt.ksft_pass; } 51 | static inline int ksft_get_fail_cnt(void) { return ksft_cnt.ksft_fail; } 52 | static inline int ksft_get_xfail_cnt(void) { return ksft_cnt.ksft_xfail; } 53 | static inline int ksft_get_xpass_cnt(void) { return ksft_cnt.ksft_xpass; } 54 | static inline int ksft_get_xskip_cnt(void) { return ksft_cnt.ksft_xskip; } 55 | static inline int ksft_get_error_cnt(void) { return ksft_cnt.ksft_error; } 56 | 57 | static inline void ksft_print_header(void) 58 | { 59 | if (!(getenv("KSFT_TAP_LEVEL"))) 60 | printf("TAP version 13\n"); 61 | } 62 | 63 | static inline void ksft_print_cnts(void) 64 | { 65 | printf("Pass %d Fail %d Xfail %d Xpass %d Skip %d Error %d\n", 66 | ksft_cnt.ksft_pass, ksft_cnt.ksft_fail, 67 | ksft_cnt.ksft_xfail, ksft_cnt.ksft_xpass, 68 | ksft_cnt.ksft_xskip, ksft_cnt.ksft_error); 69 | printf("1..%d\n", ksft_test_num()); 70 | } 71 | 72 | static inline void ksft_print_msg(const char *msg, ...) 73 | { 74 | va_list args; 75 | 76 | va_start(args, msg); 77 | printf("# "); 78 | vprintf(msg, args); 79 | va_end(args); 80 | } 81 | 82 | static inline void ksft_test_result_pass(const char *msg, ...) 83 | { 84 | va_list args; 85 | 86 | ksft_cnt.ksft_pass++; 87 | 88 | va_start(args, msg); 89 | printf("ok %d ", ksft_test_num()); 90 | vprintf(msg, args); 91 | va_end(args); 92 | } 93 | 94 | static inline void ksft_test_result_fail(const char *msg, ...) 95 | { 96 | va_list args; 97 | 98 | ksft_cnt.ksft_fail++; 99 | 100 | va_start(args, msg); 101 | printf("not ok %d ", ksft_test_num()); 102 | vprintf(msg, args); 103 | va_end(args); 104 | } 105 | 106 | static inline void ksft_test_result_skip(const char *msg, ...) 107 | { 108 | va_list args; 109 | 110 | ksft_cnt.ksft_xskip++; 111 | 112 | va_start(args, msg); 113 | printf("ok %d # skip ", ksft_test_num()); 114 | vprintf(msg, args); 115 | va_end(args); 116 | } 117 | 118 | static inline void ksft_test_result_error(const char *msg, ...) 119 | { 120 | va_list args; 121 | 122 | ksft_cnt.ksft_error++; 123 | 124 | va_start(args, msg); 125 | printf("not ok %d # error ", ksft_test_num()); 126 | vprintf(msg, args); 127 | va_end(args); 128 | } 129 | 130 | static inline int ksft_exit_pass(void) 131 | { 132 | ksft_print_cnts(); 133 | exit(KSFT_PASS); 134 | } 135 | 136 | static inline int ksft_exit_fail(void) 137 | { 138 | printf("Bail out!\n"); 139 | ksft_print_cnts(); 140 | exit(KSFT_FAIL); 141 | } 142 | 143 | static inline int ksft_exit_fail_msg(const char *msg, ...) 144 | { 145 | va_list args; 146 | 147 | va_start(args, msg); 148 | printf("Bail out! "); 149 | vprintf(msg, args); 150 | va_end(args); 151 | 152 | ksft_print_cnts(); 153 | exit(KSFT_FAIL); 154 | } 155 | 156 | static inline int ksft_exit_xfail(void) 157 | { 158 | ksft_print_cnts(); 159 | exit(KSFT_XFAIL); 160 | } 161 | 162 | static inline int ksft_exit_xpass(void) 163 | { 164 | ksft_print_cnts(); 165 | exit(KSFT_XPASS); 166 | } 167 | 168 | static inline int ksft_exit_skip(const char *msg, ...) 169 | { 170 | if (msg) { 171 | va_list args; 172 | 173 | va_start(args, msg); 174 | printf("1..%d # Skipped: ", ksft_test_num()); 175 | vprintf(msg, args); 176 | va_end(args); 177 | } else { 178 | ksft_print_cnts(); 179 | } 180 | exit(KSFT_SKIP); 181 | } 182 | 183 | #endif /* __KSELFTEST_H */ 184 | -------------------------------------------------------------------------------- /misc/psock_lib.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Google Inc. 3 | * Author: Willem de Bruijn 4 | * Daniel Borkmann 5 | * 6 | * License (GPLv2): 7 | * 8 | * This program is free software; you can redistribute it and/or modify it 9 | * under the terms and conditions of the GNU General Public License, 10 | * version 2, as published by the Free Software Foundation. 11 | * 12 | * This program is distributed in the hope it will be useful, but WITHOUT 13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 | * FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for 15 | * more details. 16 | * 17 | * You should have received a copy of the GNU General Public License along with 18 | * this program; if not, write to the Free Software Foundation, Inc., 19 | * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. 20 | */ 21 | 22 | #ifndef PSOCK_LIB_H 23 | #define PSOCK_LIB_H 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #define DATA_LEN 100 32 | #define DATA_CHAR 'a' 33 | #define DATA_CHAR_1 'b' 34 | 35 | #define PORT_BASE 8000 36 | 37 | #ifndef __maybe_unused 38 | # define __maybe_unused __attribute__ ((__unused__)) 39 | #endif 40 | 41 | static __maybe_unused void pair_udp_setfilter(int fd) 42 | { 43 | /* the filter below checks for all of the following conditions that 44 | * are based on the contents of create_payload() 45 | * ether type 0x800 and 46 | * ip proto udp and 47 | * skb->len == DATA_LEN and 48 | * udp[38] == 'a' or udp[38] == 'b' 49 | * It can be generated from the following bpf_asm input: 50 | * ldh [12] 51 | * jne #0x800, drop ; ETH_P_IP 52 | * ldb [23] 53 | * jneq #17, drop ; IPPROTO_UDP 54 | * ld len ; ld skb->len 55 | * jlt #100, drop ; DATA_LEN 56 | * ldb [80] 57 | * jeq #97, pass ; DATA_CHAR 58 | * jne #98, drop ; DATA_CHAR_1 59 | * pass: 60 | * ret #-1 61 | * drop: 62 | * ret #0 63 | */ 64 | struct sock_filter bpf_filter[] = { 65 | { 0x28, 0, 0, 0x0000000c }, 66 | { 0x15, 0, 8, 0x00000800 }, 67 | { 0x30, 0, 0, 0x00000017 }, 68 | { 0x15, 0, 6, 0x00000011 }, 69 | { 0x80, 0, 0, 0000000000 }, 70 | { 0x35, 0, 4, 0x00000064 }, 71 | { 0x30, 0, 0, 0x00000050 }, 72 | { 0x15, 1, 0, 0x00000061 }, 73 | { 0x15, 0, 1, 0x00000062 }, 74 | { 0x06, 0, 0, 0xffffffff }, 75 | { 0x06, 0, 0, 0000000000 }, 76 | }; 77 | struct sock_fprog bpf_prog; 78 | 79 | bpf_prog.filter = bpf_filter; 80 | bpf_prog.len = sizeof(bpf_filter) / sizeof(struct sock_filter); 81 | 82 | if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &bpf_prog, 83 | sizeof(bpf_prog))) { 84 | perror("setsockopt SO_ATTACH_FILTER"); 85 | exit(1); 86 | } 87 | } 88 | 89 | static __maybe_unused void pair_udp_open(int fds[], uint16_t port) 90 | { 91 | struct sockaddr_in saddr, daddr; 92 | 93 | fds[0] = socket(PF_INET, SOCK_DGRAM, 0); 94 | fds[1] = socket(PF_INET, SOCK_DGRAM, 0); 95 | if (fds[0] == -1 || fds[1] == -1) { 96 | fprintf(stderr, "ERROR: socket dgram\n"); 97 | exit(1); 98 | } 99 | 100 | memset(&saddr, 0, sizeof(saddr)); 101 | saddr.sin_family = AF_INET; 102 | saddr.sin_port = htons(port); 103 | saddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 104 | 105 | memset(&daddr, 0, sizeof(daddr)); 106 | daddr.sin_family = AF_INET; 107 | daddr.sin_port = htons(port + 1); 108 | daddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 109 | 110 | /* must bind both to get consistent hash result */ 111 | if (bind(fds[1], (void *) &daddr, sizeof(daddr))) { 112 | perror("bind"); 113 | exit(1); 114 | } 115 | if (bind(fds[0], (void *) &saddr, sizeof(saddr))) { 116 | perror("bind"); 117 | exit(1); 118 | } 119 | if (connect(fds[0], (void *) &daddr, sizeof(daddr))) { 120 | perror("connect"); 121 | exit(1); 122 | } 123 | } 124 | 125 | static __maybe_unused void pair_udp_send_char(int fds[], int num, char payload) 126 | { 127 | char buf[DATA_LEN], rbuf[DATA_LEN]; 128 | 129 | memset(buf, payload, sizeof(buf)); 130 | while (num--) { 131 | /* Should really handle EINTR and EAGAIN */ 132 | if (write(fds[0], buf, sizeof(buf)) != sizeof(buf)) { 133 | fprintf(stderr, "ERROR: send failed left=%d\n", num); 134 | exit(1); 135 | } 136 | if (read(fds[1], rbuf, sizeof(rbuf)) != sizeof(rbuf)) { 137 | fprintf(stderr, "ERROR: recv failed left=%d\n", num); 138 | exit(1); 139 | } 140 | if (memcmp(buf, rbuf, sizeof(buf))) { 141 | fprintf(stderr, "ERROR: data failed left=%d\n", num); 142 | exit(1); 143 | } 144 | } 145 | } 146 | 147 | static __maybe_unused void pair_udp_send(int fds[], int num) 148 | { 149 | return pair_udp_send_char(fds, num, DATA_CHAR); 150 | } 151 | 152 | static __maybe_unused void pair_udp_close(int fds[]) 153 | { 154 | close(fds[0]); 155 | close(fds[1]); 156 | } 157 | 158 | #endif /* PSOCK_LIB_H */ 159 | -------------------------------------------------------------------------------- /test/src/forward-mt.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Larthia, University of Pisa. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | 15 | std::atomic_long total_rcv; 16 | 17 | std::atomic_long total_fwd; 18 | 19 | void meter() 20 | { 21 | auto now = std::chrono::system_clock::now(); 22 | for(;;) 23 | { 24 | now += std::chrono::seconds(1); 25 | std::this_thread::sleep_until(now); 26 | auto r = total_rcv.exchange(0); 27 | auto f = total_fwd.exchange(0); 28 | std::cout << "pkt/sec: " << r << " fwd/sec: " << f << std::endl; 29 | } 30 | } 31 | 32 | 33 | nethuns_spsc_queue *queue; 34 | 35 | int consumer(std::string dev) 36 | { 37 | struct nethuns_socket_options opt = 38 | { 39 | .numblocks = 4 40 | , .numpackets = 65536 41 | , .packetsize = 2048 42 | , .timeout_ms = 20 43 | , .dir = nethuns_in_out 44 | , .capture = nethuns_cap_default 45 | , .mode = nethuns_socket_rx_tx 46 | , .timestamp = true 47 | , .promisc = true 48 | , .rxhash = false 49 | , .tx_qdisc_bypass = true 50 | , .xdp_prog = nullptr 51 | , .xdp_prog_sec = nullptr 52 | , .xsk_map_name = nullptr 53 | , .reuse_maps = false 54 | , .pin_dir = nullptr 55 | }; 56 | 57 | char errbuf[NETHUNS_ERRBUF_SIZE]; 58 | 59 | nethuns_socket_t * out = nethuns_open(&opt, errbuf); 60 | if (!out) 61 | { 62 | throw std::runtime_error(errbuf); 63 | } 64 | 65 | if (nethuns_bind(out, dev.c_str(), NETHUNS_ANY_QUEUE) < 0) 66 | { 67 | throw nethuns_exception(out); 68 | } 69 | 70 | 71 | for(;;) 72 | { 73 | auto pkt = reinterpret_cast(nethuns_spsc_pop(queue)); 74 | 75 | if (pkt) { 76 | 77 | total_fwd++; 78 | 79 | retry: 80 | while (!nethuns_send(out, pkt->payload, nethuns_len(pkt->pkthdr))) 81 | { 82 | nethuns_flush(out); 83 | goto retry; 84 | }; 85 | 86 | total_fwd++; 87 | 88 | nethuns_rx_release(pkt->sock, pkt->id); 89 | } 90 | } 91 | 92 | nethuns_close(out); 93 | } 94 | 95 | 96 | int 97 | main(int argc, char *argv[]) 98 | try 99 | { 100 | if (argc < 3) 101 | { 102 | std::cerr << "usage: " << argv[0] << " in out" << std::endl; 103 | return 0; 104 | } 105 | 106 | nethuns_init(); 107 | 108 | queue = nethuns_spsc_init(65536, sizeof(nethuns_packet)); 109 | if (!queue) { 110 | throw std::runtime_error("nethuns_spsc: internal error"); 111 | } 112 | 113 | std::thread(meter).detach(); 114 | 115 | std::thread(consumer, std::string{argv[2]}).detach(); 116 | 117 | struct nethuns_socket_options opt = 118 | { 119 | .numblocks = 4 120 | , .numpackets = 65536 121 | , .packetsize = 2048 122 | , .timeout_ms = 20 123 | , .dir = nethuns_in_out 124 | , .capture = nethuns_cap_default 125 | , .mode = nethuns_socket_rx_tx 126 | , .timestamp = true 127 | , .promisc = true 128 | , .rxhash = false 129 | , .tx_qdisc_bypass = true 130 | , .xdp_prog = nullptr 131 | , .xdp_prog_sec = nullptr 132 | , .xsk_map_name = nullptr 133 | , .reuse_maps = false 134 | , .pin_dir = nullptr 135 | }; 136 | 137 | char errbuf[NETHUNS_ERRBUF_SIZE]; 138 | 139 | nethuns_socket_t * s = nethuns_open(&opt, errbuf); 140 | if (!s) 141 | { 142 | throw std::runtime_error(errbuf); 143 | } 144 | 145 | if (nethuns_bind(s, argv[1], NETHUNS_ANY_QUEUE) < 0) 146 | { 147 | throw nethuns_exception(s); 148 | } 149 | 150 | const unsigned char *frame; 151 | const nethuns_pkthdr_t * pkthdr; 152 | 153 | for(;;) 154 | { 155 | uint64_t id; 156 | 157 | if ((id = nethuns_recv(s, &pkthdr, &frame))) 158 | { 159 | total_rcv++; 160 | struct nethuns_packet p { frame, pkthdr, nethuns_socket(s), id }; 161 | 162 | while (!nethuns_spsc_push(queue, &p)) 163 | { }; 164 | } 165 | } 166 | 167 | nethuns_close(s); 168 | return 0; 169 | } 170 | catch(nethuns_exception &e) 171 | { 172 | if (e.sock) { 173 | nethuns_close(e.sock); 174 | } 175 | std::cerr << e.what() << std::endl; 176 | return 1; 177 | } 178 | catch(std::exception &e) 179 | { 180 | std::cerr << e.what() << std::endl; 181 | return 1; 182 | } 183 | -------------------------------------------------------------------------------- /test/src/filter.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Larthia, University of Pisa. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | void dump_packet(nethuns_pkthdr_t const *hdr, const unsigned char *frame) 14 | { 15 | int i = 0; 16 | 17 | printf("%u:%u snap:%u len:%u offload{tci:%x tpid:%x} packet{tci:%x pid:%x} => [tci:%x tpid:%x vid:%d] rxhash:0x%x| ", nethuns_tstamp_sec(hdr) 18 | , nethuns_tstamp_nsec(hdr) 19 | , nethuns_snaplen(hdr) 20 | , nethuns_len(hdr) 21 | , nethuns_offvlan_tci(hdr) 22 | , nethuns_offvlan_tpid(hdr) 23 | , nethuns_vlan_tci(frame) 24 | , nethuns_vlan_tpid(frame) 25 | , nethuns_vlan_tci_(hdr, frame) 26 | , nethuns_vlan_tpid_(hdr, frame) 27 | , nethuns_vlan_vid(nethuns_vlan_tci_(hdr, frame)) 28 | , nethuns_rxhash(hdr)); 29 | for(; i < 34; i++) 30 | { 31 | printf("%02x ", frame[i]); 32 | } 33 | printf("\n"); 34 | } 35 | 36 | 37 | int simple_filter(void *ctx, const nethuns_pkthdr_t *pkthdr, const uint8_t *pkt) 38 | { 39 | static int run; 40 | auto header = (std::string *)ctx; 41 | printf("filter context (%s)\n", header->c_str()); 42 | run++; 43 | if (run & 1) { 44 | return 1; /* pass */ 45 | } else { 46 | if (run & 3) { /* drop */ 47 | return 0; 48 | } else { 49 | return -1; /* virtual packet */ 50 | } 51 | } 52 | } 53 | 54 | 55 | std::atomic_long total; 56 | 57 | void meter() 58 | { 59 | auto now = std::chrono::system_clock::now(); 60 | for(;;) 61 | { 62 | now += std::chrono::seconds(1); 63 | std::this_thread::sleep_until(now); 64 | auto x = total.exchange(0); 65 | std::cout << "pkt/sec: " << x << std::endl; 66 | } 67 | } 68 | 69 | 70 | int 71 | main(int argc, char *argv[]) 72 | try 73 | { 74 | if (argc < 2) 75 | { 76 | std::cerr << "usage: " << argv[0] << " dev" << std::endl; 77 | return 0; 78 | } 79 | 80 | nethuns_init(); 81 | 82 | struct nethuns_socket_options opt = 83 | { 84 | .numblocks = 1 85 | , .numpackets = 65536 86 | , .packetsize = 2048 87 | , .timeout_ms = 0 88 | , .dir = nethuns_in_out 89 | , .capture = nethuns_cap_default 90 | , .mode = nethuns_socket_rx_tx 91 | , .timestamp = true 92 | , .promisc = true 93 | , .rxhash = false 94 | , .tx_qdisc_bypass = true 95 | , .xdp_prog = nullptr 96 | , .xdp_prog_sec = nullptr 97 | , .xsk_map_name = nullptr 98 | , .reuse_maps = false 99 | , .pin_dir = nullptr 100 | }; 101 | 102 | char errbuf[NETHUNS_ERRBUF_SIZE]; 103 | nethuns_socket_t *s = nethuns_open(&opt, errbuf); 104 | if (s == nullptr) 105 | { 106 | throw std::runtime_error(errbuf); 107 | } 108 | 109 | if (nethuns_bind(s, argv[1], NETHUNS_ANY_QUEUE) < 0) 110 | { 111 | throw nethuns_exception(s); 112 | } 113 | 114 | std::thread(meter).detach(); 115 | 116 | const unsigned char *frame; 117 | const nethuns_pkthdr_t *pkthdr; 118 | 119 | // set filter... 120 | 121 | std::string ctx = "packet"; 122 | nethuns_set_filter(s, &simple_filter, &ctx); 123 | 124 | for(;;) 125 | { 126 | uint64_t pkt_id; 127 | 128 | if ((pkt_id = nethuns_recv(s, &pkthdr, &frame))) 129 | { 130 | if (frame) { 131 | total++; 132 | dump_packet(pkthdr, frame); 133 | } else{ 134 | std::cout << "virtal packet!" << std::endl; 135 | } 136 | 137 | nethuns_rx_release(s, pkt_id); 138 | } 139 | } 140 | 141 | nethuns_close(s); 142 | return 0; 143 | } 144 | catch(nethuns_exception &e) 145 | { 146 | if (e.sock) { 147 | nethuns_close(e.sock); 148 | } 149 | std::cerr << e.what() << std::endl; 150 | return 1; 151 | } 152 | catch(std::exception &e) 153 | { 154 | std::cerr << e.what() << std::endl; 155 | return 1; 156 | } 157 | -------------------------------------------------------------------------------- /src/nethuns/sockets/xsk_ext.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2021 Larthia, University of Pisa. All rights reserved. 3 | * Use of this source code is governed by a BSD-style 4 | * license that can be found in the LICENSE file. 5 | */ 6 | 7 | #ifdef NETHUNS_SOCKET 8 | #undef NETHUNS_SOCKET 9 | #endif 10 | 11 | #define NETHUNS_SOCKET NETHUNS_SOCKET_XDP 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | #include "nethuns/sockets/xdp.h" 18 | #include 19 | #include 20 | 21 | #include "xdp/xsk.h" 22 | #include "xdp/bpf.h" 23 | #include "xdp/libbpf.h" 24 | 25 | #include "xsk_ext.h" 26 | 27 | #include "../api.h" 28 | 29 | struct xsk_umem_info * 30 | xsk_configure_umem( 31 | struct nethuns_socket_xdp *sock 32 | , void *buffer 33 | , size_t size 34 | , size_t frame_size) 35 | { 36 | struct xsk_umem_info *umem; 37 | struct xsk_umem_config cfg = { 38 | .fill_size = sock->base.rx_ring.mask + 1, 39 | .comp_size = sock->base.tx_ring.mask + 1, 40 | .frame_size = frame_size, 41 | .frame_headroom = XSK_UMEM__DEFAULT_FRAME_HEADROOM, 42 | .flags = 0 // opt_umem_flags -> HUGEFPAGE? 43 | }; 44 | 45 | int ret; 46 | 47 | //printf("buffer %p size %ld (%lx) frame_size %ld (%lx)\n", 48 | // buffer, size, size, frame_size, frame_size); 49 | 50 | umem = calloc(1, sizeof(*umem)); 51 | if (!umem) { 52 | nethuns_perror(nethuns_socket(sock)->errbuf, "xsk_config_umem: could not allocate memory"); 53 | return NULL; 54 | } 55 | 56 | ret = xsk_umem__create(&umem->umem, buffer, size, &umem->fq, &umem->cq, &cfg); 57 | if (ret) { 58 | nethuns_perror(nethuns_socket(sock)->errbuf, "xsk_config_umem: could not create umem"); 59 | return NULL; 60 | } 61 | 62 | umem->buffer = buffer; 63 | return umem; 64 | } 65 | 66 | 67 | //int 68 | //xsk_populate_fill_ring( 69 | // struct nethuns_socket_xdp *sock 70 | // , size_t frame_size) 71 | //{ 72 | // int ret, i; 73 | // uint32_t idx; 74 | // 75 | // ret = xsk_ring_prod__reserve(&sock->umem->fq, 76 | // XSK_RING_PROD__DEFAULT_NUM_DESCS, &idx); 77 | // 78 | // if (ret != XSK_RING_PROD__DEFAULT_NUM_DESCS) { 79 | // nethuns_perror(nethuns_socket(sock)->errbuf, "xsk_populate_fill_ring: could not reserve fill ring"); 80 | // return -ret; 81 | // } 82 | // 83 | // for (i = 0; i < XSK_RING_PROD__DEFAULT_NUM_DESCS; i++) 84 | // *xsk_ring_prod__fill_addr(&sock->umem->fq, idx++) = 85 | // i * frame_size; 86 | // xsk_ring_prod__submit(&sock->umem->fq, XSK_RING_PROD__DEFAULT_NUM_DESCS); 87 | // return 0; 88 | //} 89 | 90 | 91 | struct xsk_socket_info * 92 | xsk_configure_socket(struct nethuns_socket_xdp *sock) 93 | { 94 | struct xsk_socket_config cfg; 95 | struct xsk_socket_info *xsk; 96 | unsigned int ret; 97 | unsigned int i; 98 | unsigned int idx; 99 | 100 | xsk = calloc(1, sizeof(*xsk)); 101 | if (!xsk) 102 | return NULL; 103 | 104 | xsk->umem = sock->umem; 105 | cfg.rx_size = sock->base.rx_ring.mask + 1; 106 | cfg.tx_size = sock->base.tx_ring.mask + 1; 107 | 108 | cfg.libbpf_flags = nethuns_socket(sock)->opt.xdp_prog != NULL 109 | ? XSK_LIBBPF_FLAGS__INHIBIT_PROG_LOAD 110 | : 0; 111 | 112 | cfg.xdp_flags = sock->xdp_flags; 113 | cfg.bind_flags = sock->xdp_bind_flags; 114 | 115 | ret = xsk_socket__create(&xsk->xsk, nethuns_socket(sock)->devname, nethuns_socket(sock)->queue, sock->umem->umem, 116 | sock->rx ? &xsk->rx : NULL, 117 | sock->tx ? &xsk->tx : NULL, 118 | &cfg); 119 | 120 | if (ret) { 121 | nethuns_perror(nethuns_socket(sock)->errbuf, "xsk_config: could not create socket"); 122 | goto err; 123 | } 124 | 125 | ret = xsk_ring_prod__reserve(&xsk->umem->fq, cfg.rx_size, &idx); 126 | 127 | if (ret != cfg.rx_size) { 128 | nethuns_perror(nethuns_socket(sock)->errbuf, "xsk_config: could not reserve slots in fill ring"); 129 | goto err; 130 | } 131 | 132 | for (i = 0; i < cfg.rx_size; i ++) { 133 | *xsk_ring_prod__fill_addr(&xsk->umem->fq, idx) = rx_frame(sock, idx); 134 | idx++; 135 | } 136 | 137 | xsk_ring_prod__submit(&xsk->umem->fq, cfg.rx_size); 138 | 139 | return xsk; 140 | err: 141 | free(xsk); 142 | return NULL; 143 | } 144 | 145 | int 146 | xsk_enter_into_map(struct nethuns_socket_xdp *sock, int queue) 147 | { 148 | struct bpf_map *map; 149 | int xdp_map; 150 | 151 | // Load the xsk map. A set entry in this map means that an active AF_XDP socket is bound to the corresponding device and queue (see nethuns_bind_xdp). 152 | if (!nethuns_socket(sock)->opt.xsk_map_name) 153 | nethuns_socket(sock)->opt.xsk_map_name = "xsk_map"; 154 | 155 | map = bpf_object__find_map_by_name(sock->obj, nethuns_socket(sock)->opt.xsk_map_name); 156 | xdp_map = bpf_map__fd(map); 157 | if (xdp_map < 0) { 158 | nethuns_perror(nethuns_socket(sock)->errbuf, "xsk_enter_into_map: couldn't find xsk map: %s", strerror(xdp_map)); 159 | return -1; 160 | } 161 | 162 | int fd = xsk_socket__fd(sock->xsk->xsk); 163 | int key, ret; 164 | 165 | key = queue; 166 | ret = bpf_map_update_elem(xdp_map, &key, &fd, 0); 167 | if (ret) { 168 | nethuns_perror(nethuns_socket(sock)->errbuf, "xsk_enter_into_map: bfp_map_update_elem"); 169 | return -1; 170 | } 171 | 172 | return 0; 173 | } 174 | -------------------------------------------------------------------------------- /src/nethuns/queue.h: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Larthia, University of Pisa. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | #pragma once 6 | 7 | #include "misc/compiler.h" 8 | #include 9 | #include 10 | 11 | 12 | struct nethuns_spsc_queue 13 | { 14 | struct 15 | { 16 | size_t head_cache; 17 | 18 | } consumer __cacheline_aligned; 19 | 20 | struct 21 | { 22 | size_t tail_cache; 23 | 24 | } producer __cacheline_aligned; 25 | 26 | struct 27 | { 28 | size_t head; 29 | 30 | } __cacheline_aligned; 31 | 32 | struct 33 | { 34 | size_t tail; 35 | 36 | } __cacheline_aligned; 37 | 38 | struct 39 | { 40 | size_t nslots_1; 41 | size_t size; 42 | void * mem; 43 | 44 | } __cacheline_aligned; 45 | }; 46 | 47 | 48 | static __always_inline 49 | struct nethuns_spsc_queue * 50 | nethuns_spsc_init(size_t nslots, size_t size) 51 | { 52 | if (nslots & (nslots-1)) { 53 | return NULL; 54 | } 55 | 56 | struct nethuns_spsc_queue *fifo = (struct nethuns_spsc_queue *) 57 | calloc(1, sizeof(struct nethuns_spsc_queue) + nslots * size); 58 | 59 | if (fifo != NULL) 60 | { 61 | fifo->nslots_1 = nslots-1; 62 | fifo->size = size; 63 | fifo->mem = calloc(1, nslots*size); 64 | fifo->head = 0; 65 | fifo->tail = 0; 66 | fifo->producer.tail_cache = 0; 67 | fifo->consumer.head_cache = 0; 68 | } 69 | return fifo; 70 | } 71 | 72 | static __always_inline 73 | void *nethuns_slot_addr(struct nethuns_spsc_queue *fifo, size_t idx) { 74 | return (char *)fifo->mem + fifo->size * idx; 75 | } 76 | 77 | static __always_inline 78 | bool nethuns_spsc_is_empty(struct nethuns_spsc_queue const *fifo) 79 | { 80 | return __atomic_load_n(&fifo->head, __ATOMIC_RELAXED) == 81 | __atomic_load_n(&fifo->tail, __ATOMIC_RELAXED); 82 | } 83 | 84 | 85 | static __always_inline 86 | bool nethuns_spsc_is_full(struct nethuns_spsc_queue const *fifo) 87 | { 88 | return ((__atomic_load_n(&fifo->head, __ATOMIC_RELAXED) + 1) & (fifo->nslots_1)) == 89 | __atomic_load_n(&fifo->tail, __ATOMIC_RELAXED); 90 | } 91 | 92 | 93 | static __always_inline 94 | size_t nethuns_spsc_distance(struct nethuns_spsc_queue const *fifo, size_t h, size_t t) 95 | { 96 | return (h - t) & fifo->nslots_1; 97 | } 98 | 99 | 100 | static __always_inline 101 | void nethuns_spsc_consumer_sync(struct nethuns_spsc_queue *fifo) 102 | { 103 | fifo->consumer.head_cache = __atomic_load_n(&fifo->head, __ATOMIC_ACQUIRE); 104 | } 105 | 106 | 107 | static __always_inline 108 | void nethuns_spsc_producer_sync(struct nethuns_spsc_queue *fifo) 109 | { 110 | fifo->producer.tail_cache = __atomic_load_n(&fifo->tail, __ATOMIC_ACQUIRE); 111 | } 112 | 113 | 114 | static __always_inline 115 | size_t nethuns_spsc_len(struct nethuns_spsc_queue *fifo) 116 | { 117 | size_t h = __atomic_load_n(&fifo->head, __ATOMIC_RELAXED); 118 | size_t t = __atomic_load_n(&fifo->tail, __ATOMIC_RELAXED); 119 | return nethuns_spsc_distance(fifo, h, t); 120 | } 121 | 122 | 123 | static __always_inline 124 | size_t nethuns_spsc_next_index(struct nethuns_spsc_queue *fifo, size_t value) 125 | { 126 | return (value + 1) & fifo->nslots_1; 127 | } 128 | 129 | 130 | static __always_inline 131 | size_t nethuns_spsc_push(struct nethuns_spsc_queue *fifo, void *elem) 132 | { 133 | size_t w = __atomic_load_n(&fifo->head, __ATOMIC_RELAXED); 134 | size_t r = fifo->producer.tail_cache; 135 | size_t next = nethuns_spsc_next_index(fifo, w); 136 | 137 | if (next == r) { 138 | r = fifo->producer.tail_cache = __atomic_load_n(&fifo->tail, __ATOMIC_ACQUIRE); 139 | if (next == r) { 140 | return 0; 141 | } 142 | } 143 | 144 | memcpy(nethuns_slot_addr(fifo, w), elem, fifo->size); 145 | 146 | __atomic_store_n(&fifo->head, next, __ATOMIC_RELEASE); 147 | 148 | return nethuns_spsc_distance(fifo, next, r); 149 | } 150 | 151 | 152 | static __always_inline 153 | void *nethuns_spsc_pop(struct nethuns_spsc_queue *fifo) 154 | { 155 | size_t w = fifo->consumer.head_cache; 156 | size_t r = __atomic_load_n(&fifo->tail, __ATOMIC_RELAXED); 157 | size_t next; 158 | void *elem; 159 | 160 | if (w == r) { 161 | w = fifo->consumer.head_cache = __atomic_load_n(&fifo->head, __ATOMIC_ACQUIRE); 162 | if (w == r) 163 | return NULL; 164 | } 165 | 166 | elem = nethuns_slot_addr(fifo, r); 167 | 168 | next = nethuns_spsc_next_index(fifo, r); 169 | 170 | __atomic_store_n(&fifo->tail, next, __ATOMIC_RELEASE); 171 | 172 | return elem; 173 | } 174 | 175 | 176 | static __always_inline 177 | void *nethuns_spsc_peek(struct nethuns_spsc_queue *fifo) 178 | { 179 | size_t w = fifo->consumer.head_cache; 180 | size_t r = __atomic_load_n(&fifo->tail, __ATOMIC_RELAXED); 181 | 182 | if (w == r) { 183 | w = fifo->consumer.head_cache = __atomic_load_n(&fifo->head, __ATOMIC_ACQUIRE); 184 | if (w == r) 185 | return NULL; 186 | } 187 | 188 | return nethuns_slot_addr(fifo, r); 189 | } 190 | 191 | 192 | static __always_inline 193 | void nethuns_spsc_consume(struct nethuns_spsc_queue *fifo) 194 | { 195 | size_t next = nethuns_spsc_next_index(fifo, fifo->tail); 196 | __atomic_store_n(&fifo->tail, next, __ATOMIC_RELEASE); 197 | } 198 | 199 | 200 | static __always_inline 201 | void nethuns_spsc_free(struct nethuns_spsc_queue *fifo, void (*free_)(void *)) 202 | { 203 | void *ptr; 204 | 205 | if (free_) { 206 | while ((ptr = nethuns_spsc_pop(fifo))) 207 | free_(ptr); 208 | } 209 | 210 | free(fifo->mem); 211 | free(fifo); 212 | } 213 | -------------------------------------------------------------------------------- /src/nethuns/sockets/ring.h: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Larthia, University of Pisa. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | #pragma once 6 | 7 | #include "base.h" 8 | 9 | #include "../define.h" 10 | 11 | #if !defined NETHUNS_SOCKET 12 | #error NETHUNS_SOCKET is not defined. 13 | #endif 14 | 15 | #if NETHUNS_SOCKET == NETHUNS_SOCKET_LIBPCAP 16 | #include 17 | #endif 18 | 19 | #if NETHUNS_SOCKET == NETHUNS_SOCKET_TPACKET3 20 | #include 21 | #endif 22 | 23 | #if NETHUNS_SOCKET == NETHUNS_SOCKET_NETMAP 24 | #include "netmap_pkthdr.h" 25 | #endif 26 | 27 | 28 | #if NETHUNS_SOCKET == NETHUNS_SOCKET_XDP 29 | #include "xdp_pkthdr.h" 30 | #endif 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | #include "../misc/macro.h" 39 | #include "../misc/compiler.h" 40 | #include "../types.h" 41 | 42 | struct nethuns_ring_slot 43 | { 44 | #if NETHUNS_SOCKET == NETHUNS_SOCKET_TPACKET3 45 | struct tpacket3_hdr pkthdr; 46 | #elif NETHUNS_SOCKET == NETHUNS_SOCKET_NETMAP 47 | struct netmap_pkthdr pkthdr; 48 | #elif NETHUNS_SOCKET == NETHUNS_SOCKET_LIBPCAP 49 | struct pcap_pkthdr pkthdr; 50 | #elif NETHUNS_SOCKET == NETHUNS_SOCKET_XDP 51 | struct xdp_pkthdr pkthdr; 52 | #else 53 | #error "NETHUNS_SOCKET not defined" 54 | #endif 55 | uint64_t id; 56 | int inuse; 57 | int32_t len; 58 | 59 | unsigned char packet[]; 60 | }; 61 | 62 | 63 | static __always_inline 64 | size_t 65 | nethuns_lpow2(size_t n) 66 | { 67 | if (n && !(n & (n - 1))) 68 | return n; 69 | return 1UL << (sizeof(size_t) * CHAR_BIT - __builtin_clzl(n)); 70 | } 71 | 72 | static __always_inline 73 | int 74 | nethuns_make_ring(size_t nslots, size_t pktsize, struct nethuns_ring *r) 75 | { 76 | size_t ns = nethuns_lpow2(nslots); 77 | size_t ss = nethuns_lpow2(sizeof(struct nethuns_ring_slot) + pktsize); 78 | 79 | r->size = nslots; 80 | r->pktsize = pktsize; 81 | r->head = 0; 82 | r->tail = 0; 83 | r->ring = (struct nethuns_ring_slot *)calloc(1, ns * ss); 84 | r->mask = ns - 1; 85 | r->shift = __builtin_ctzl(ss); 86 | 87 | return r->ring ? 0 : -1; 88 | } 89 | 90 | static __always_inline 91 | void 92 | nethuns_delete_ring(struct nethuns_ring *r) 93 | { 94 | free(r->ring); 95 | memset(r, 0, sizeof(*r)); 96 | } 97 | 98 | 99 | static __always_inline 100 | struct nethuns_ring_slot * 101 | nethuns_ring_get_slot(struct nethuns_ring *ring, size_t n) 102 | { 103 | return (struct nethuns_ring_slot *) 104 | ((char *)ring->ring + ((n & ring->mask) << ring->shift)); 105 | } 106 | 107 | static __always_inline 108 | uint64_t 109 | nethuns_slot_get_idx(struct nethuns_ring *ring, struct nethuns_ring_slot *s) 110 | { 111 | return (uint64_t)((char *)ring->ring - (char *)s) >> ring->shift; 112 | } 113 | 114 | 115 | static __always_inline 116 | size_t 117 | nethuns_ring_num_free_slots(struct nethuns_ring *ring, size_t n) 118 | { 119 | size_t last = n + MIN(ring->size - 1, (size_t)32); 120 | size_t total = 0; 121 | for (size_t x = n; x < last; x++) 122 | { 123 | struct nethuns_ring_slot *s = nethuns_ring_get_slot(ring, x); 124 | if (likely(!__atomic_load_n(&s->inuse, __ATOMIC_ACQUIRE))) { 125 | total++; 126 | } 127 | else { 128 | break; 129 | } 130 | } 131 | 132 | return total; 133 | } 134 | 135 | static __always_inline 136 | struct nethuns_ring_slot * 137 | nethuns_ring_next_slot(struct nethuns_ring *ring) 138 | { 139 | return nethuns_ring_get_slot(ring,ring->head++); 140 | } 141 | 142 | 143 | typedef int(*nethuns_free_slot_t)(struct nethuns_ring_slot *slot, uint64_t id, void *user); 144 | 145 | 146 | static __always_inline int 147 | nethuns_ring_free_slots(struct nethuns_ring *ring, nethuns_free_slot_t cb, void *user) 148 | { 149 | int n = 0; 150 | 151 | struct nethuns_ring_slot *slot = nethuns_ring_get_slot(ring, ring->tail); 152 | 153 | while (ring->tail != ring->head && !__atomic_load_n(&slot->inuse, __ATOMIC_ACQUIRE)) 154 | { 155 | cb(slot, nethuns_ring_get_slot(ring, ring->tail)->id, user); 156 | slot = nethuns_ring_get_slot(ring, ++ring->tail); 157 | } 158 | 159 | return n; 160 | } 161 | 162 | #define nethuns_rx_release(_sock, _pktid) do \ 163 | { \ 164 | __atomic_store_n(&nethuns_ring_get_slot(&nethuns_socket(_sock)->rx_ring, (_pktid)-1)->inuse, 0, __ATOMIC_RELEASE); \ 165 | } while (0) 166 | 167 | #define nethuns_tx_release(_sock, _pktid) do \ 168 | { \ 169 | __atomic_store_n(&(nethuns_ring_get_slot(&nethuns_socket(_sock)->tx_ring, (_pktid)-1)->inuse), 0, __ATOMIC_RELEASE); \ 170 | } while (0) 171 | 172 | 173 | static __always_inline int 174 | nethuns_send_slot(nethuns_socket_t *sock, uint64_t pktid, size_t len) 175 | { 176 | struct nethuns_ring_slot * slot = nethuns_ring_get_slot(&nethuns_socket(sock)->tx_ring, pktid); 177 | if (__atomic_load_n(&slot->inuse, __ATOMIC_ACQUIRE)) 178 | return 0; 179 | slot->len = len; 180 | __atomic_store_n(&slot->inuse, 1, __ATOMIC_RELEASE); 181 | return 1; 182 | } 183 | 184 | static __always_inline 185 | size_t 186 | nethuns_rxring_get_size(nethuns_socket_t *s) 187 | { 188 | return nethuns_socket(s)->rx_ring.mask + 1; 189 | } 190 | 191 | static __always_inline 192 | size_t 193 | nethuns_txring_get_size(nethuns_socket_t *s) 194 | { 195 | return nethuns_socket(s)->tx_ring.mask + 1; 196 | } 197 | -------------------------------------------------------------------------------- /src/nethuns/sockets/xdp.h: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Larthia, University of Pisa. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | # pragma once 6 | 7 | #include 8 | 9 | #include "../misc/compiler.h" 10 | #include "../misc/macro.h" 11 | #include "../vlan.h" 12 | #include "../types.h" 13 | 14 | #include "xdp_pkthdr.h" 15 | #include "xsk_ext.h" 16 | 17 | #include "base.h" 18 | 19 | #ifndef PATH_MAX 20 | #define PATH_MAX 4096 /* maximum path length for bpf pinned maps */ 21 | #endif 22 | 23 | struct nethuns_socket_xdp 24 | { 25 | struct nethuns_socket_base base; 26 | 27 | uint32_t xdp_flags; 28 | uint32_t xdp_bind_flags; 29 | 30 | struct bpf_object *obj; 31 | 32 | struct xsk_socket_info *xsk; 33 | 34 | struct xsk_umem_info *umem; 35 | void *bufs; 36 | size_t total_mem; 37 | uint64_t first_tx_frame; 38 | uint64_t num_tx_frames; 39 | uint64_t first_rx_frame; 40 | uint64_t num_rx_frames; 41 | size_t framesz; /* real size of each frame (power of 2) */ 42 | size_t fshift; /* log_2 of the frame size */ 43 | 44 | bool rx; 45 | bool tx; 46 | 47 | unsigned int rcvd; 48 | unsigned int toflush; 49 | uint32_t idx_rx; 50 | }; 51 | 52 | 53 | #ifdef __cplusplus 54 | extern "C" { 55 | #endif 56 | 57 | int nethuns_check_libpcap(size_t hsize, char *errbuf); 58 | 59 | nethuns_pcap_t * 60 | nethuns_pcap_open_xdp(struct nethuns_socket_options *opt, const char *filename, int mode, char *errbuf); 61 | 62 | int 63 | nethuns_pcap_close_xdp(nethuns_pcap_t *p); 64 | 65 | uint64_t 66 | nethuns_pcap_read_xdp(nethuns_pcap_t *p, nethuns_pkthdr_t const **pkthdr, uint8_t const **payload); 67 | 68 | int 69 | nethuns_pcap_write_xdp(nethuns_pcap_t *s, struct nethuns_pcap_pkthdr const *header, uint8_t const *packet, unsigned int len); 70 | 71 | int 72 | nethuns_pcap_store_xdp(nethuns_pcap_t *s, nethuns_pkthdr_t const *pkthdr, uint8_t const *packet, unsigned int len); 73 | 74 | int 75 | nethuns_pcap_rewind_xdp(nethuns_pcap_t *s); 76 | 77 | int nethuns_check_xdp(size_t hsize, char *errbuf); 78 | 79 | struct nethuns_socket_xdp * 80 | nethuns_open_xdp(struct nethuns_socket_options *opt, char *errbuf); 81 | 82 | int 83 | nethuns_close_xdp(struct nethuns_socket_xdp *s); 84 | 85 | int 86 | nethuns_bind_xdp(struct nethuns_socket_xdp *s, const char *dev, int queue); 87 | 88 | uint64_t 89 | nethuns_recv_xdp(struct nethuns_socket_xdp *s, nethuns_pkthdr_t const **pkthdr, uint8_t const **payload); 90 | 91 | int 92 | nethuns_send_xdp(struct nethuns_socket_xdp *s, uint8_t const *packet, unsigned int len); 93 | 94 | uint8_t * 95 | nethuns_get_buf_addr_xdp(struct nethuns_socket_xdp *s, uint64_t pktid); 96 | 97 | int 98 | nethuns_flush_xdp(__maybe_unused struct nethuns_socket_xdp *s); 99 | 100 | int 101 | nethuns_stats_xdp(struct nethuns_socket_xdp *s, struct nethuns_stat *stats); 102 | 103 | int nethuns_fd_xdp(__maybe_unused struct nethuns_socket_xdp *s); 104 | 105 | int 106 | nethuns_fanout_xdp(__maybe_unused struct nethuns_socket_xdp *s, __maybe_unused int group, __maybe_unused const char *fanout); 107 | 108 | void 109 | nethuns_dump_rings_xdp(__maybe_unused struct nethuns_socket_xdp *s); 110 | 111 | static __always_inline uint32_t 112 | nethuns_tstamp_sec_xdp(struct xdp_pkthdr const *hdr) { 113 | return (uint32_t)hdr->sec; 114 | } 115 | static __always_inline uint32_t 116 | nethuns_tstamp_usec_xdp(struct xdp_pkthdr const *hdr) { 117 | return (uint32_t)hdr->nsec/1000; 118 | } 119 | 120 | static __always_inline uint32_t 121 | nethuns_tstamp_nsec_xdp(struct xdp_pkthdr const *hdr) { 122 | return (uint32_t)hdr->nsec; 123 | } 124 | 125 | static __always_inline 126 | void nethuns_tstamp_set_sec_xdp(struct xdp_pkthdr *hdr, uint32_t v) { 127 | hdr->sec = v; 128 | } 129 | 130 | static __always_inline 131 | void nethuns_tstamp_set_usec_xdp(struct xdp_pkthdr *hdr, uint32_t v) { 132 | hdr->nsec = v * 1000; 133 | } 134 | 135 | static __always_inline 136 | void nethuns_tstamp_set_nsec_xdp(struct xdp_pkthdr *hdr, uint32_t v) { 137 | hdr->nsec = v; 138 | } 139 | 140 | static __always_inline uint32_t 141 | nethuns_snaplen_xdp(struct xdp_pkthdr const *hdr) { 142 | return hdr->snaplen; 143 | } 144 | 145 | static __always_inline uint32_t 146 | nethuns_len_xdp(struct xdp_pkthdr const *hdr) { 147 | return hdr->len; 148 | } 149 | 150 | static __always_inline void 151 | nethuns_set_snaplen_xdp(struct xdp_pkthdr *hdr, uint32_t v) { 152 | hdr->snaplen = v; 153 | } 154 | 155 | static __always_inline void 156 | nethuns_set_len_xdp(struct xdp_pkthdr *hdr, uint32_t v) { 157 | hdr->len = v; 158 | } 159 | 160 | static __always_inline uint32_t 161 | nethuns_rxhash_xdp(__maybe_unused struct xdp_pkthdr const *hdr) { 162 | return 0; 163 | } 164 | 165 | static __always_inline uint16_t 166 | nethuns_offvlan_tpid_xdp(__maybe_unused struct xdp_pkthdr const *hdr) { 167 | return 0; 168 | } 169 | 170 | static __always_inline uint16_t 171 | nethuns_offvlan_tci_xdp(__maybe_unused struct xdp_pkthdr const *hdr) { 172 | return 0; 173 | } 174 | 175 | static __always_inline uint64_t 176 | tx_frame(struct nethuns_socket_xdp *s, uint64_t idx) 177 | { 178 | return (s->first_tx_frame + (idx & s->base.tx_ring.mask)) << s->fshift; 179 | } 180 | 181 | static __always_inline uint64_t 182 | tx_slot(struct nethuns_socket_xdp *s, uint64_t frame) 183 | { 184 | return ((frame >> s->fshift) - s->first_tx_frame) & s->base.tx_ring.mask; 185 | } 186 | 187 | static __always_inline uint64_t 188 | rx_frame(struct nethuns_socket_xdp *s, uint64_t idx) 189 | { 190 | return (s->first_rx_frame + (idx & s->base.rx_ring.mask)) << s->fshift; 191 | } 192 | 193 | #ifdef __cplusplus 194 | } 195 | #endif 196 | -------------------------------------------------------------------------------- /tools/nethuns-gen/src/options.cpp: -------------------------------------------------------------------------------- 1 | #include "hdr/options.hpp" 2 | #include "hdr/generator.hpp" 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | void help(const char *progname) { 9 | std::cout << "Usage: " << progname << " [OPTIONS]" << std::endl; 10 | std::cout << "Options:" << std::endl; 11 | std::cout << " -G, --generator\t\t\tadd a generator" << std::endl; 12 | std::cout << " -c, --cpu \t\t\tset cpu to run on" << std::endl; 13 | std::cout << " -I, --interface \t\t\tset device to send packets on" << std::endl; 14 | std::cout << " -S, --source \t\t\tset the source of packets (e.g. template name or pcap)" << std::endl; 15 | std::cout << " -s, --mac_source \t\tset source mac address" << std::endl; 16 | std::cout << " -d, --mac_dest \t\tset destination mac address" << std::endl; 17 | std::cout << " -m, --max_packets \t\tset maximum number of packets to send" << std::endl; 18 | std::cout << " -r, --pkt_rate \t\t\tset packet rate" << std::endl; 19 | std::cout << " -l, --loops \t\t\tset number of loops" << std::endl; 20 | std::cout << " -L, --pktlen \t\t\tset packet length" << std::endl; 21 | std::cout << " -R, --randomize \t\trandomize flows by addr/prefix (e.g. 192.168.0.0/24)" << std::endl; 22 | std::cout << " -a, --amplitude \t\t\tset amplitude" << std::endl; 23 | std::cout << " -y, --speed \t\t\tset speed (for pcap, 0 means top-speed)" << std::endl; 24 | std::cout << " -X, --seed \t\t\tset the random seed for then generator" << std::endl; 25 | std::cout << " -x, --fix_checksums\t\t\tfix checksums" << std::endl; 26 | std::cout << " -P, --preload\t\t\t\tpreload pcap files" << std::endl; 27 | std::cout << " -v, --verbose\t\t\t\tverbose" << std::endl; 28 | std::cout << " -h, --help\t\t\t\tshow this help" << std::endl; 29 | } 30 | 31 | template 32 | auto deref(T *ptr) -> T& { 33 | if (ptr == nullptr) { 34 | throw std::runtime_error("no generator to set option for"); 35 | } 36 | return *ptr; 37 | } 38 | 39 | 40 | options 41 | parse_opt(int argc, char **argv) 42 | { 43 | options opt; 44 | generator *gen = nullptr; 45 | 46 | uint32_t gen_id = 0; 47 | 48 | struct option long_options[] = { 49 | {"generator", no_argument, 0, 'G'}, 50 | {"cpu", required_argument, 0, 'c'}, 51 | {"interface", required_argument, 0, 'I'}, 52 | {"source", required_argument, 0, 'S'}, 53 | {"mac_source", required_argument, 0, 's'}, 54 | {"mac_dest", required_argument, 0, 'd'}, 55 | {"max_packets", required_argument, 0, 'm'}, 56 | {"pkt_rate", required_argument, 0, 'r'}, 57 | {"loops", required_argument, 0, 'l'}, 58 | {"pktlen", required_argument, 0, 'L'}, 59 | {"randomize", required_argument, 0, 'R'}, 60 | {"amplitude", required_argument, 0, 'a'}, 61 | {"speed", required_argument, 0, 'y'}, 62 | {"seed", required_argument, 0, 'X'}, 63 | {"fix_checksums", no_argument, 0, 'x'}, 64 | {"preload", no_argument, 0, 'P'}, 65 | {"verbose", no_argument, 0, 'v'}, 66 | {"help", no_argument, 0, 'h'}, 67 | {0, 0, 0, 0} 68 | }; 69 | 70 | 71 | int c; 72 | int option_index = 0; 73 | while ((c = getopt_long(argc, argv, "Gc:I:S:s:d:m:r:l:L:R:a:y:X:xPvh", long_options, &option_index)) != -1) { 74 | switch (c) { 75 | case 'G': 76 | opt.generators.emplace_back(); 77 | gen = &opt.generators.back(); 78 | gen->id = gen_id++; 79 | gen->seed = gen->id; 80 | break; 81 | case 'c': 82 | deref(gen).cpu = std::stoi(optarg); 83 | break; 84 | case 'I': 85 | deref(gen).dev = std::string{optarg}; 86 | break; 87 | case 'S': 88 | deref(gen).source = std::string{optarg}; 89 | break; 90 | case 's': 91 | deref(gen).mac_source = std::string{optarg}; 92 | break; 93 | case 'd': 94 | deref(gen).mac_dest = std::string{optarg}; 95 | break; 96 | case 'm': 97 | deref(gen).max_packets = std::stoul(optarg); 98 | break; 99 | case 'r': 100 | deref(gen).pkt_rate = std::stoul(optarg); 101 | break; 102 | case 'l': 103 | deref(gen).loops = std::stoul(optarg); 104 | break; 105 | case 'L': 106 | deref(gen).pktlen = std::stoi(optarg); 107 | break; 108 | case 'R': 109 | deref(gen).randomize_prefix.push_back(netaddr(optarg)); 110 | break; 111 | case 'a': 112 | deref(gen).amp = std::stoi(optarg); 113 | break; 114 | case 'y': 115 | deref(gen).speed = std::stoi(optarg); 116 | break; 117 | case 'X': 118 | deref(gen).seed = std::stoi(optarg); 119 | break; 120 | case 'x': 121 | deref(gen).fix_checksums = true; 122 | break; 123 | case 'P': 124 | deref(gen).pcap_preload = true; 125 | break; 126 | case 'v': 127 | deref(gen).verbose = true; 128 | break; 129 | case 'h': 130 | help(argv[0]); 131 | exit(0); 132 | default: 133 | help(argv[0]); 134 | exit(1); 135 | } 136 | } 137 | 138 | return opt; 139 | } 140 | 141 | void validate_options(const options &opt) { 142 | if (opt.generators.empty()) { 143 | throw std::runtime_error("no generators specified"); 144 | } 145 | 146 | for (auto &gen : opt.generators) { 147 | if (gen.source.empty()) { 148 | throw std::runtime_error("no generator source specified"); 149 | } 150 | } 151 | } -------------------------------------------------------------------------------- /misc/example.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #ifndef likely 20 | # define likely(x) __builtin_expect(!!(x), 1) 21 | #endif 22 | #ifndef unlikely 23 | # define unlikely(x) __builtin_expect(!!(x), 0) 24 | #endif 25 | 26 | struct block_desc { 27 | uint32_t version; 28 | uint32_t offset_to_priv; 29 | struct tpacket_hdr_v1 h1; 30 | }; 31 | 32 | struct ring { 33 | struct iovec *rd; 34 | uint8_t *map; 35 | struct tpacket_req3 req; 36 | }; 37 | 38 | static unsigned long packets_total = 0, bytes_total = 0; 39 | static sig_atomic_t sigint = 0; 40 | 41 | static void sighandler(int num) 42 | { 43 | sigint = 1; 44 | } 45 | 46 | static int setup_socket(struct ring *ring, char *netdev) 47 | { 48 | int err, i, fd, v = TPACKET_V3; 49 | struct sockaddr_ll ll; 50 | unsigned int blocksiz = 1 << 22, framesiz = 1 << 11; 51 | unsigned int blocknum = 64; 52 | 53 | fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); 54 | if (fd < 0) { 55 | perror("socket"); 56 | exit(1); 57 | } 58 | 59 | err = setsockopt(fd, SOL_PACKET, PACKET_VERSION, &v, sizeof(v)); 60 | if (err < 0) { 61 | perror("setsockopt"); 62 | exit(1); 63 | } 64 | 65 | memset(&ring->req, 0, sizeof(ring->req)); 66 | ring->req.tp_block_size = blocksiz; 67 | ring->req.tp_frame_size = framesiz; 68 | ring->req.tp_block_nr = blocknum; 69 | ring->req.tp_frame_nr = (blocksiz * blocknum) / framesiz; 70 | ring->req.tp_retire_blk_tov = 60; 71 | ring->req.tp_feature_req_word = TP_FT_REQ_FILL_RXHASH; 72 | 73 | err = setsockopt(fd, SOL_PACKET, PACKET_RX_RING, &ring->req, 74 | sizeof(ring->req)); 75 | if (err < 0) { 76 | perror("setsockopt"); 77 | exit(1); 78 | } 79 | 80 | ring->map = mmap(NULL, ring->req.tp_block_size * ring->req.tp_block_nr, 81 | PROT_READ | PROT_WRITE, MAP_SHARED | MAP_LOCKED, fd, 0); 82 | if (ring->map == MAP_FAILED) { 83 | perror("mmap"); 84 | exit(1); 85 | } 86 | 87 | ring->rd = malloc(ring->req.tp_block_nr * sizeof(*ring->rd)); 88 | assert(ring->rd); 89 | for (i = 0; i < ring->req.tp_block_nr; ++i) { 90 | ring->rd[i].iov_base = ring->map + (i * ring->req.tp_block_size); 91 | ring->rd[i].iov_len = ring->req.tp_block_size; 92 | } 93 | 94 | memset(&ll, 0, sizeof(ll)); 95 | ll.sll_family = PF_PACKET; 96 | ll.sll_protocol = htons(ETH_P_ALL); 97 | ll.sll_ifindex = if_nametoindex(netdev); 98 | ll.sll_hatype = 0; 99 | ll.sll_pkttype = 0; 100 | ll.sll_halen = 0; 101 | 102 | err = bind(fd, (struct sockaddr *) &ll, sizeof(ll)); 103 | if (err < 0) { 104 | perror("bind"); 105 | exit(1); 106 | } 107 | 108 | return fd; 109 | } 110 | 111 | static void display(struct tpacket3_hdr *ppd) 112 | { 113 | struct ethhdr *eth = (struct ethhdr *) ((uint8_t *) ppd + ppd->tp_mac); 114 | struct iphdr *ip = (struct iphdr *) ((uint8_t *) eth + ETH_HLEN); 115 | 116 | if (eth->h_proto == htons(ETH_P_IP)) { 117 | struct sockaddr_in ss, sd; 118 | char sbuff[NI_MAXHOST], dbuff[NI_MAXHOST]; 119 | 120 | memset(&ss, 0, sizeof(ss)); 121 | ss.sin_family = PF_INET; 122 | ss.sin_addr.s_addr = ip->saddr; 123 | getnameinfo((struct sockaddr *) &ss, sizeof(ss), 124 | sbuff, sizeof(sbuff), NULL, 0, NI_NUMERICHOST); 125 | 126 | memset(&sd, 0, sizeof(sd)); 127 | sd.sin_family = PF_INET; 128 | sd.sin_addr.s_addr = ip->daddr; 129 | getnameinfo((struct sockaddr *) &sd, sizeof(sd), 130 | dbuff, sizeof(dbuff), NULL, 0, NI_NUMERICHOST); 131 | 132 | printf("%s -> %s, ", sbuff, dbuff); 133 | } 134 | 135 | printf("rxhash: 0x%x\n", ppd->hv1.tp_rxhash); 136 | } 137 | 138 | 139 | static void walk_block(struct block_desc *pbd, const int block_num) 140 | { 141 | int num_pkts = pbd->h1.num_pkts, i; 142 | unsigned long bytes = 0; 143 | struct tpacket3_hdr *ppd; 144 | 145 | ppd = (struct tpacket3_hdr *) ((uint8_t *) pbd + 146 | pbd->h1.offset_to_first_pkt); 147 | for (i = 0; i < num_pkts; ++i) { 148 | bytes += ppd->tp_snaplen; 149 | display(ppd); 150 | 151 | ppd = (struct tpacket3_hdr *) ((uint8_t *) ppd + 152 | ppd->tp_next_offset); 153 | } 154 | 155 | packets_total += num_pkts; 156 | bytes_total += bytes; 157 | } 158 | 159 | static void flush_block(struct block_desc *pbd) 160 | { 161 | pbd->h1.block_status = TP_STATUS_KERNEL; 162 | } 163 | 164 | static void teardown_socket(struct ring *ring, int fd) 165 | { 166 | munmap(ring->map, ring->req.tp_block_size * ring->req.tp_block_nr); 167 | free(ring->rd); 168 | close(fd); 169 | } 170 | 171 | int main(int argc, char **argp) 172 | { 173 | int fd, err; 174 | socklen_t len; 175 | struct ring ring; 176 | struct pollfd pfd; 177 | unsigned int block_num = 0, blocks = 64; 178 | struct block_desc *pbd; 179 | struct tpacket_stats_v3 stats; 180 | 181 | if (argc != 2) { 182 | fprintf(stderr, "Usage: %s INTERFACE\n", argp[0]); 183 | return EXIT_FAILURE; 184 | } 185 | 186 | signal(SIGINT, sighandler); 187 | 188 | memset(&ring, 0, sizeof(ring)); 189 | fd = setup_socket(&ring, argp[argc - 1]); 190 | assert(fd > 0); 191 | 192 | memset(&pfd, 0, sizeof(pfd)); 193 | pfd.fd = fd; 194 | pfd.events = POLLIN | POLLERR; 195 | pfd.revents = 0; 196 | 197 | while (likely(!sigint)) { 198 | pbd = (struct block_desc *) ring.rd[block_num].iov_base; 199 | 200 | if ((pbd->h1.block_status & TP_STATUS_USER) == 0) { 201 | poll(&pfd, 1, -1); 202 | continue; 203 | } 204 | 205 | walk_block(pbd, block_num); 206 | flush_block(pbd); 207 | block_num = (block_num + 1) % blocks; 208 | } 209 | 210 | len = sizeof(stats); 211 | err = getsockopt(fd, SOL_PACKET, PACKET_STATISTICS, &stats, &len); 212 | if (err < 0) { 213 | perror("getsockopt"); 214 | exit(1); 215 | } 216 | 217 | fflush(stdout); 218 | printf("\nReceived %u packets, %lu bytes, %u dropped, freeze_q_cnt: %u\n", 219 | stats.tp_packets, bytes_total, stats.tp_drops, 220 | stats.tp_freeze_q_cnt); 221 | 222 | teardown_socket(&ring, fd); 223 | return 0; 224 | } 225 | -------------------------------------------------------------------------------- /src/nethuns/api.h: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Larthia, University of Pisa. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | #pragma once 6 | 7 | //#include 8 | //#include 9 | //#include 10 | //#include 11 | #ifndef IFNAMSIZ 12 | #define IFNAMSIZ 16 13 | #endif 14 | 15 | #include "misc/compiler.h" 16 | #include "sockets/base.h" 17 | #include "global.h" 18 | #include "types.h" 19 | 20 | #define NETHUNS_GLOBAL \ 21 | void (*__nethuns_fini)() = nethuns_global_fini; 22 | 23 | /* 24 | pktid is a 64 bit unsigned integer that uniquely identifies a packet. 25 | 26 | pktid == -2 means EOF 27 | pktid == -1 means error 28 | pktid == 0 means null packet (Polling mode) 29 | pktid > 0 means valid packet 30 | */ 31 | 32 | #define nethuns_error(_sock) ({nethuns_const_socket(_sock)->errbuf;}) 33 | #define nethuns_pkt_is_valid(_n) ((_n + 2) > 2) 34 | #define nethuns_pkt_is_null(_n) ((_n) == 0) 35 | #define nethuns_pkt_is_ok(_n) ((_n + 2) >= 2) 36 | #define nethuns_pkt_is_err(_n) ((_n) == (uint64_t)-1) 37 | #define nethuns_pkt_is_eof(_n) ((_n) == (uint64_t)-2) 38 | 39 | #ifdef __cplusplus 40 | extern "C" { 41 | #endif 42 | 43 | static __always_inline void nethuns_init(); 44 | 45 | static __always_inline nethuns_socket_t * nethuns_open(struct nethuns_socket_options *opt, char *errbuf); 46 | 47 | static __always_inline nethuns_socket_t * nethuns_open(struct nethuns_socket_options *opt, char *errbuf); 48 | 49 | static __always_inline int nethuns_close(nethuns_socket_t * s); 50 | 51 | static __always_inline int nethuns_bind(nethuns_socket_t * s, const char *dev, int queue); 52 | 53 | static __always_inline uint64_t nethuns_recv(nethuns_socket_t * s, const nethuns_pkthdr_t **pkthdr, const uint8_t **pkt); 54 | 55 | static __always_inline int nethuns_fd(nethuns_socket_t * s); 56 | 57 | static __always_inline int nethuns_send(nethuns_socket_t * s, const uint8_t *packet, unsigned int len); 58 | 59 | static __always_inline int nethuns_flush(nethuns_socket_t * s); 60 | 61 | static __always_inline int nethuns_fanout(nethuns_socket_t * s, int group, const char *fanout); 62 | 63 | static __always_inline int nethuns_stats(nethuns_socket_t * s, struct nethuns_stat *); 64 | 65 | static __always_inline void nethuns_dump_rings(nethuns_socket_t * s); 66 | 67 | static __always_inline uint8_t * nethuns_get_buf_addr(nethuns_socket_t * s, uint64_t pktid); 68 | 69 | static __always_inline nethuns_pcap_t * nethuns_pcap_open(struct nethuns_socket_options *opt, const char *filename, int mode, char *errbuf); 70 | static __always_inline int nethuns_pcap_close(nethuns_pcap_t * p); 71 | static __always_inline uint64_t nethuns_pcap_read(nethuns_pcap_t * p, const nethuns_pkthdr_t **pkthdr, const uint8_t **pkt); 72 | static __always_inline int nethuns_pcap_store(nethuns_pcap_t * s, nethuns_pkthdr_t const *pkthdr, uint8_t const *packet, unsigned int len); 73 | static __always_inline int nethuns_pcap_rewind(nethuns_pcap_t *p); 74 | static __always_inline int nethuns_pcap_write(nethuns_pcap_t *s, struct nethuns_pcap_pkthdr const *header, uint8_t const *packet, unsigned int len); 75 | 76 | static __always_inline uint32_t nethuns_tstamp_sec(nethuns_pkthdr_t const *hdr); 77 | static __always_inline uint32_t nethuns_tstamp_usec(nethuns_pkthdr_t const *hdr); 78 | static __always_inline uint32_t nethuns_tstamp_nsec(nethuns_pkthdr_t const *hdr); 79 | static __always_inline void nethuns_tstamp_set_sec(nethuns_pkthdr_t *hdr, uint32_t v); 80 | static __always_inline void nethuns_tstamp_set_usec(nethuns_pkthdr_t *hdr, uint32_t v); 81 | static __always_inline void nethuns_tstamp_set_nsec(nethuns_pkthdr_t *hdr, uint32_t v); 82 | static __always_inline uint32_t nethuns_snaplen(nethuns_pkthdr_t const *hdr); 83 | static __always_inline uint32_t nethuns_len(nethuns_pkthdr_t const *hdr); 84 | static __always_inline void nethuns_set_snaplen(nethuns_pkthdr_t *hdr, uint32_t v); 85 | static __always_inline void nethuns_set_len(nethuns_pkthdr_t *hdr, uint32_t v); 86 | static __always_inline uint32_t nethuns_rxhash(__maybe_unused nethuns_pkthdr_t const *hdr); 87 | static __always_inline uint16_t nethuns_offvlan_tpid(__maybe_unused nethuns_pkthdr_t const *hdr); 88 | static __always_inline uint16_t nethuns_offvlan_tci(__maybe_unused nethuns_pkthdr_t const *hdr); 89 | 90 | int nethuns_ioctl_if(nethuns_socket_t *s, const char *devname, unsigned long what, uint32_t *flags); 91 | 92 | void nethuns_perror(char *buf, const char *format, ...); 93 | 94 | void nethuns_fprintf(FILE *out, const char *msg, ...); 95 | 96 | const char * nethuns_version(); 97 | const char * nethuns_version_full(); 98 | 99 | int __nethuns_set_if_promisc(nethuns_socket_t *s, const char *devname); 100 | 101 | int __nethuns_clear_if_promisc(nethuns_socket_t *s, const char *devname); 102 | 103 | void __nethuns_free_base(nethuns_socket_t *s); 104 | 105 | // filter functions 106 | // 107 | 108 | static __always_inline char * 109 | nethuns_dev_queue_name(const char *dev, int queue) 110 | { 111 | static __thread char name[IFNAMSIZ+4]; 112 | if (dev == NULL) { 113 | snprintf(name, IFNAMSIZ+4, "unspec"); 114 | } else if (queue == NETHUNS_ANY_QUEUE) { 115 | snprintf(name, IFNAMSIZ+4, "%s", dev); 116 | } else { 117 | snprintf(name, IFNAMSIZ+4,"%s:%d", dev, queue); 118 | } 119 | return name; 120 | } 121 | 122 | static __always_inline char * 123 | nethuns_device_name(nethuns_socket_t *s) 124 | { 125 | return nethuns_dev_queue_name(nethuns_socket(s)->devname, nethuns_socket(s)->queue); 126 | } 127 | 128 | 129 | #ifdef __cplusplus 130 | } 131 | 132 | #include 133 | 134 | struct nethuns_exception : public std::runtime_error { 135 | 136 | nethuns_exception(nethuns_socket_t *s, const char *msg) 137 | : std::runtime_error(msg) 138 | , sock(s) 139 | {} 140 | 141 | nethuns_exception(nethuns_socket_t *s) 142 | : std::runtime_error(nethuns_error(s)) 143 | , sock(s) 144 | {} 145 | 146 | nethuns_socket_t *sock; 147 | }; 148 | 149 | #endif 150 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2021 Larthia, University of Pisa. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | cmake_minimum_required(VERSION 3.10) 6 | 7 | project (nethuns) 8 | 9 | # 10 | # Compiler options... 11 | # 12 | 13 | string(FIND "${CMAKE_CXX_COMPILER_ID}" "Clang" CLANG_SUBSTRING_INDEX) 14 | if ("${CLANG_SUBSTRING_INDEX}" GREATER -1) 15 | set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O2 -Wno-unused-function -fomit-frame-pointer -Wall -Wextra -Wshadow") 16 | set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O2 -Wno-unused-function -std=c++17 -fomit-frame-pointer -Wall -Wextra -Wshadow") 17 | else() 18 | set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O2 -Wno-unused-function -march=native -fomit-frame-pointer -Wall -Wextra -Wshadow") 19 | set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O2 -Wno-unused-function -std=c++17 -march=native -fomit-frame-pointer -Wall -Wextra -Wshadow") 20 | endif() 21 | 22 | 23 | set (CMAKE_POSITION_INDEPENDENT_CODE ON) 24 | 25 | option(NETHUNS_OPT_LIBPCAP "Enable libpcap support" ON) 26 | option(NETHUNS_OPT_BUILTIN_PCAP_READER "Enable builtin pcap reader" OFF) 27 | option(NETHUNS_OPT_TPACKET_V3 "Enable TPACKET v3 support" OFF) 28 | option(NETHUNS_OPT_XDP "Enable XDP" OFF) 29 | option(NETHUNS_OPT_NETMAP "Enable Netmap support" OFF) 30 | 31 | add_definitions(-D_GNU_SOURCE) 32 | 33 | if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") 34 | find_library(LIBPCAP_LIBRARY NAMES libpcap.a PATHS "/usr/local/opt/libpcap/lib" "/opt/homebrew/opt/libpcap/lib" NO_DEFAULT_PATH) 35 | find_path(LIBPCAP_INCLUDE_DIR NAMES pcap/pcap.h PATHS "/usr/local/opt/libpcap/include/" "/opt/homebrew/opt/libpcap/include" NO_DEFAULT_PATH) 36 | elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") 37 | find_library(LIBPCAP_LIBRARY NAMES libpcap.a PATHS "/usr/local/lib") 38 | find_path(LIBPCAP_INCLUDE_DIR NAMES pcap/pcap.h PATHS "/usr/local/include") 39 | else() 40 | message(FATAL_ERROR "${CMAKE_SYSTEM_NAME} platform not supported!") 41 | endif() 42 | 43 | include_directories(${LIBPCAP_INCLUDE_DIR}) 44 | include_directories(src) 45 | 46 | if (NETHUNS_OPT_XDP) 47 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}/libbpf/) 48 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}/libbpf/include/) 49 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}/libbpf/include/uapi) 50 | endif() 51 | 52 | # 53 | # define the list of library to link... 54 | # 55 | 56 | set(NETHUNS_LIBS) 57 | 58 | set(NETHUNS_SRC src/nethuns/nethuns.c 59 | src/nethuns/global.c) 60 | 61 | if (NETHUNS_OPT_BUILTIN_PCAP_READER) 62 | message("Nethuns: using built-in pcap reader") 63 | add_definitions(-DNETHUNS_USE_BUILTIN_PCAP_READER) 64 | else() 65 | list(APPEND NETHUNS_LIBS ${LIBPCAP_LIBRARY}) 66 | message("Nethuns: using libpcap to read files") 67 | endif() 68 | 69 | if (NETHUNS_OPT_TPACKET_V3) 70 | list(APPEND NETHUNS_SRC src/nethuns/sockets/tpacket_v3.c) 71 | message("Nethuns: tpacket v3 ENABLED") 72 | add_definitions(-DNETHUNS_USE_TPACKET_V3) 73 | endif() 74 | 75 | if (NETHUNS_OPT_NETMAP) 76 | list(APPEND NETHUNS_SRC src/nethuns/sockets/netmap.c) 77 | list(APPEND NETHUNS_DEP -lnetmap) 78 | list(APPEND OTHER_LIB -lnetmap) 79 | 80 | message("Nethuns: netmap ENABLED") 81 | add_definitions(-DNETHUNS_USE_NETMAP) 82 | endif() 83 | 84 | if (NETHUNS_OPT_XDP) 85 | message("Nethuns: xdp ENABLED") 86 | add_definitions(-DNETHUNS_USE_XDP) 87 | find_package(ZLIB) 88 | 89 | set(LIBBPF_LIBRARY ${CMAKE_CURRENT_SOURCE_DIR}/libbpf/src/libbpf.a) 90 | 91 | add_custom_target( 92 | build_libbpf ALL 93 | COMMAND ${CMAKE_MAKE_PROGRAM} 94 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/libbpf/src 95 | COMMENT "Libbpf makefile target" 96 | ) 97 | 98 | add_library(libbpf STATIC IMPORTED) 99 | 100 | set_property(TARGET libbpf APPEND PROPERTY IMPORTED_CONFIGURATIONS NOCONFIG) 101 | set_target_properties(libbpf PROPERTIES IMPORTED_LOCATION_NOCONFIG "${LIBBPF_LIBRARY}") 102 | 103 | add_dependencies(libbpf build_libbpf) 104 | 105 | list(APPEND NETHUNS_SRC src/nethuns/sockets/xdp.c) 106 | list(APPEND NETHUNS_SRC src/nethuns/sockets/xsk_ext.c) 107 | 108 | list(APPEND NETHUNS_LIBS libbpf) 109 | list(APPEND NETHUNS_LIBS -lelf) 110 | list(APPEND NETHUNS_LIBS ZLIB::ZLIB) 111 | 112 | endif() 113 | 114 | if (NETHUNS_OPT_LIBPCAP) 115 | message("Nethuns: libpcap ENABLED") 116 | add_definitions(-DNETHUNS_USE_LIBPCAP) 117 | list(APPEND NETHUNS_SRC src/nethuns/sockets/libpcap.c) 118 | list(APPEND NETHUNS_LIBS ${LIBPCAP_LIBRARY}) 119 | endif() 120 | 121 | add_custom_command( 122 | OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/src/nethuns/version.c 123 | ${CMAKE_CURRENT_SOURCE_DIR}/src/nethuns/_version.c # fake target that is never created, here just to trigger the execution of makeRevision script 124 | COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/scripts/make_revision.sh ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/src/nethuns/version.c.in ${CMAKE_CURRENT_SOURCE_DIR}/src/nethuns/version.c ${NETHUNS_OPT_LIBPCAP} ${NETHUNS_OPT_XDP} ${NETHUNS_OPT_NETMAP} ${NETHUNS_OPT_TPACKET_V3} 125 | ) 126 | 127 | 128 | add_custom_target(revision ALL DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/src/nethuns/version.c) 129 | 130 | list(APPEND NETHUNS_SRC src/nethuns/version.c) 131 | 132 | add_library(nethuns STATIC ${NETHUNS_SRC}) 133 | 134 | target_link_libraries(nethuns PRIVATE ${NETHUS_LIBS}) 135 | 136 | add_dependencies(nethuns revision) 137 | 138 | # 139 | # build Nethuns.cmake script... 140 | 141 | configure_file(scripts/Nethuns.cmake Nethuns.cmake @ONLY) 142 | 143 | # 144 | # copy headers into build directory... 145 | 146 | file(COPY src/nethuns/ DESTINATION ${CMAKE_BINARY_DIR}/include/nethuns FILES_MATCHING PATTERN "*.h" PATTERN src/nethuns/sockets/xdp EXCLUDE) 147 | if (NETHUNS_OPT_XDP) 148 | file(COPY libbpf/src/ DESTINATION ${CMAKE_BINARY_DIR}/include/nethuns/sockets/xdp FILES_MATCHING PATTERN "*.h" ) 149 | file(COPY libbpf/include/uapi/ DESTINATION ${CMAKE_BINARY_DIR}/include/nethuns/sockets/xdp FILES_MATCHING PATTERN "*.h" ) 150 | endif() 151 | 152 | # 153 | # install headers and libraries... 154 | 155 | install(DIRECTORY ${CMAKE_BINARY_DIR}/include/nethuns/ DESTINATION ${CMAKE_INSTALL_PREFIX}/include/nethuns FILES_MATCHING PATTERN "*.h") 156 | 157 | if (NETHUNS_OPT_XDP) 158 | install(FILES libbpf/src/libbpf.a DESTINATION ${CMAKE_INSTALL_PREFIX}/lib/) 159 | endif() 160 | 161 | install(TARGETS nethuns DESTINATION ${CMAKE_INSTALL_PREFIX}/lib) 162 | install(FILES ${CMAKE_BINARY_DIR}/Nethuns.cmake DESTINATION ${CMAKE_INSTALL_PREFIX}/share/nethuns/) 163 | 164 | 165 | -------------------------------------------------------------------------------- /src/nethuns/sockets/tpacket_v3.h: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Larthia, University of Pisa. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | # pragma once 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include 25 | 26 | // 27 | // Workaround: if __LINUX_TYPES_H is defined, then we are compiling against the broken linux/types.h of libbpf. 28 | // To fix it, we need to add the following typedefs before including 29 | // https://patchwork.ozlabs.org/project/netdev/patch/20190518004639.20648-2-mcroce@redhat.com/ 30 | 31 | #ifdef __LINUX_TYPES_H 32 | typedef __u16 __bitwise __sum16; 33 | typedef __u32 __bitwise __wsum; 34 | #endif 35 | 36 | 37 | #include "../types.h" 38 | #include "../misc/compiler.h" 39 | 40 | struct ring_v3 41 | { 42 | struct iovec *rd; 43 | uint8_t *map; 44 | struct tpacket_req3 req; 45 | }; 46 | 47 | 48 | struct nethuns_socket_tpacket_v3 49 | { 50 | struct nethuns_socket_base base; 51 | 52 | struct ring_v3 rx_ring; 53 | struct ring_v3 tx_ring; 54 | 55 | uint64_t rx_block_mod; 56 | uint64_t rx_block_idx; 57 | uint64_t rx_block_idx_rls; 58 | 59 | uint64_t tx_block_mod; 60 | uint64_t tx_block_idx; 61 | uint64_t tx_block_idx_rls; 62 | 63 | unsigned int rx_frame_idx; 64 | unsigned int tx_frame_idx; 65 | 66 | int fd; 67 | 68 | struct pollfd rx_pfd; 69 | struct pollfd tx_pfd; 70 | 71 | struct tpacket3_hdr *rx_ppd; 72 | struct tpacket3_hdr *tx_ppd; 73 | }; 74 | 75 | 76 | struct block_descr_v3 77 | { 78 | uint32_t version; 79 | uint32_t offset_to_priv; 80 | struct tpacket_hdr_v1 hdr; 81 | }; 82 | 83 | 84 | #ifdef __cplusplus 85 | extern "C" { 86 | #endif 87 | 88 | 89 | int nethuns_check_tpacket_v3(size_t hsize, char *errbuf); 90 | 91 | nethuns_pcap_t * 92 | nethuns_pcap_open_tpacket_v3(struct nethuns_socket_options *opt, const char *filename, int mode, char *errbuf); 93 | 94 | int 95 | nethuns_pcap_close_tpacket_v3(nethuns_pcap_t *p); 96 | 97 | uint64_t 98 | nethuns_pcap_read_tpacket_v3(nethuns_pcap_t *p, nethuns_pkthdr_t const **pkthdr, uint8_t const **payload); 99 | 100 | int 101 | nethuns_pcap_write_tpacket_v3(nethuns_pcap_t *s, struct nethuns_pcap_pkthdr const *header, uint8_t const *packet, unsigned int len); 102 | 103 | int 104 | nethuns_pcap_store_tpacket_v3(nethuns_pcap_t *s, nethuns_pkthdr_t const *pkthdr, uint8_t const *packet, unsigned int len); 105 | 106 | int 107 | nethuns_pcap_rewind_tpacket_v3(nethuns_pcap_t *s); 108 | 109 | 110 | struct nethuns_socket_tpacket_v3 * 111 | nethuns_open_tpacket_v3(struct nethuns_socket_options *opt, char *errbuf); 112 | 113 | int 114 | nethuns_close_tpacket_v3(struct nethuns_socket_tpacket_v3 *s); 115 | 116 | int 117 | nethuns_bind_tpacket_v3(struct nethuns_socket_tpacket_v3 *s, const char *dev, int queue); 118 | 119 | uint64_t 120 | nethuns_recv_tpacket_v3(struct nethuns_socket_tpacket_v3 *s, nethuns_pkthdr_t const **pkthdr, uint8_t const **payload); 121 | 122 | int 123 | nethuns_send_tpacket_v3(struct nethuns_socket_tpacket_v3 *s, uint8_t const *packet, unsigned int len); 124 | 125 | static __always_inline uint8_t * 126 | nethuns_get_buf_addr_tpacket_v3(__maybe_unused nethuns_socket_t * s, __maybe_unused uint64_t pktid) { 127 | return NULL; 128 | } 129 | 130 | int 131 | nethuns_flush_tpacket_v3(__maybe_unused struct nethuns_socket_tpacket_v3 *s); 132 | 133 | int 134 | nethuns_stats_tpacket_v3(struct nethuns_socket_tpacket_v3 *s, struct nethuns_stat *stats); 135 | 136 | int 137 | nethuns_fanout_tpacket_v3(__maybe_unused struct nethuns_socket_tpacket_v3 *s, __maybe_unused int group, __maybe_unused const char *fanout); 138 | 139 | int 140 | nethuns_fd_tpacket_v3(__maybe_unused struct nethuns_socket_tpacket_v3 *s); 141 | 142 | void 143 | nethuns_dump_rings_tpacket_v3(__maybe_unused struct nethuns_socket_tpacket_v3 *s); 144 | 145 | static __always_inline 146 | struct block_descr_v3 * 147 | __nethuns_block_mod_tpacket_v3(struct ring_v3 *ring, uint64_t id) 148 | { 149 | return (struct block_descr_v3 *) ring->rd[id % ring->req.tp_block_nr].iov_base; 150 | } 151 | 152 | static __always_inline 153 | struct block_descr_v3 * 154 | __nethuns_block_tpacket_v3(struct ring_v3 *ring, uint64_t id_mod) 155 | { 156 | return (struct block_descr_v3 *) ring->rd[id_mod].iov_base; 157 | } 158 | 159 | 160 | static __always_inline uint32_t 161 | nethuns_tstamp_sec_tpacket_v3(struct tpacket3_hdr const *hdr) 162 | { 163 | return hdr->tp_sec; 164 | } 165 | 166 | static __always_inline uint32_t 167 | nethuns_tstamp_usec_tpacket_v3(struct tpacket3_hdr const *hdr) 168 | { 169 | return hdr->tp_nsec/1000; 170 | } 171 | 172 | static __always_inline uint32_t 173 | nethuns_tstamp_nsec_tpacket_v3(struct tpacket3_hdr const *hdr) 174 | { 175 | return hdr->tp_nsec; 176 | } 177 | 178 | static __always_inline 179 | void nethuns_tstamp_set_sec_tpacket_v3(struct tpacket3_hdr *hdr, uint32_t v) { 180 | hdr->tp_sec = v; 181 | } 182 | 183 | static __always_inline 184 | void nethuns_tstamp_set_usec_tpacket_v3(struct tpacket3_hdr *hdr, uint32_t v) { 185 | hdr->tp_nsec = v *1000; 186 | } 187 | 188 | static __always_inline 189 | void nethuns_tstamp_set_nsec_tpacket_v3(struct tpacket3_hdr *hdr, uint32_t v) { 190 | hdr->tp_nsec = v; 191 | } 192 | 193 | static __always_inline uint32_t 194 | nethuns_snaplen_tpacket_v3(struct tpacket3_hdr const *hdr) { 195 | return hdr->tp_snaplen; 196 | } 197 | 198 | static __always_inline uint32_t 199 | nethuns_len_tpacket_v3(struct tpacket3_hdr const *hdr) { 200 | return hdr->tp_len; 201 | } 202 | 203 | static __always_inline void 204 | nethuns_set_snaplen_tpacket_v3(struct tpacket3_hdr *hdr, uint32_t v) { 205 | hdr->tp_snaplen = v; 206 | } 207 | 208 | static __always_inline void 209 | nethuns_set_len_tpacket_v3(struct tpacket3_hdr *hdr, uint32_t v) { 210 | hdr->tp_len = v; 211 | } 212 | 213 | static __always_inline uint32_t 214 | nethuns_rxhash_tpacket_v3(struct tpacket3_hdr const *hdr) { 215 | return hdr->hv1.tp_rxhash; 216 | } 217 | 218 | static __always_inline uint16_t 219 | nethuns_offvlan_tci_tpacket_v3(struct tpacket3_hdr const *hdr) { 220 | return hdr->hv1.tp_vlan_tci; 221 | } 222 | 223 | static __always_inline uint16_t 224 | nethuns_offvlan_tpid_tpacket_v3(struct tpacket3_hdr const *hdr) { 225 | return hdr->hv1.tp_vlan_tpid; 226 | } 227 | 228 | 229 | #ifdef __cplusplus 230 | } 231 | #endif 232 | -------------------------------------------------------------------------------- /test/src/file-pcap.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Larthia, University of Pisa. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | 11 | void dump_packet(nethuns_pkthdr_t const *hdr, const unsigned char *frame) 12 | { 13 | int i = 0; 14 | 15 | printf("%u:%u snap:%u len:%u offload{tci:%x tpid:%x} packet{tci:%x pid:%x} => [tci:%x tpid:%x vid:%d] rxhash:0x%x| ", nethuns_tstamp_sec(hdr) 16 | , nethuns_tstamp_nsec(hdr) 17 | , nethuns_snaplen(hdr) 18 | , nethuns_len(hdr) 19 | , nethuns_offvlan_tci(hdr) 20 | , nethuns_offvlan_tpid(hdr) 21 | , nethuns_vlan_tci(frame) 22 | , nethuns_vlan_tpid(frame) 23 | , nethuns_vlan_tci_(hdr, frame) 24 | , nethuns_vlan_tpid_(hdr, frame) 25 | , nethuns_vlan_vid(nethuns_vlan_tci_(hdr, frame)) 26 | , nethuns_rxhash(hdr)); 27 | for(; i < 14; i++) 28 | { 29 | printf("%02x ", frame[i]); 30 | } 31 | printf("\n"); 32 | } 33 | 34 | 35 | int 36 | main(int argc, char *argv[]) 37 | try 38 | { 39 | if (argc < 3) 40 | { 41 | fprintf(stderr,"usage: %s [read filepcap | count filepcap | capture ifname]\n", argv[0]); 42 | return 0; 43 | } 44 | 45 | nethuns_init(); 46 | 47 | bool count = strcmp(argv[1],"count") == 0; 48 | 49 | if (strcmp(argv[1], "read") == 0 || strcmp(argv[1], "count") == 0) 50 | { 51 | nethuns_pcap_t *p; 52 | 53 | struct nethuns_socket_options opt = 54 | { 55 | .numblocks = 1 56 | , .numpackets = 1024 57 | , .packetsize = 2048 58 | , .timeout_ms = 0 59 | , .dir = nethuns_in_out 60 | , .capture = nethuns_cap_default 61 | , .mode = nethuns_socket_rx_tx 62 | , .timestamp = true 63 | , .promisc = false 64 | , .rxhash = false 65 | , .tx_qdisc_bypass = false 66 | , .xdp_prog = nullptr 67 | , .xdp_prog_sec = nullptr 68 | , .xsk_map_name = nullptr 69 | , .reuse_maps = false 70 | , .pin_dir = nullptr 71 | }; 72 | 73 | char errbuf[NETHUNS_ERRBUF_SIZE]; 74 | p = nethuns_pcap_open(&opt, argv[2], 0, errbuf); 75 | if (!p) 76 | { 77 | throw std::runtime_error(errbuf); 78 | } 79 | 80 | const unsigned char *frame; 81 | const nethuns_pkthdr_t * pkthdr; 82 | 83 | uint64_t pkt_id; 84 | 85 | uint64_t total = 0; 86 | uint64_t errors = 0; 87 | do 88 | { 89 | pkt_id = nethuns_pcap_read(p, &pkthdr, &frame); 90 | 91 | if (nethuns_pkt_is_valid(pkt_id)) { 92 | total++; 93 | if (count) { 94 | if ((total % 1000000) == 0) { 95 | std::cerr << "packet: " << total << std::endl; 96 | } 97 | } else { 98 | std::cerr << nethuns_tstamp_sec(pkthdr) << ":" 99 | << nethuns_tstamp_nsec(pkthdr) 100 | << " caplen:" << nethuns_snaplen(pkthdr) 101 | << " len:" << nethuns_len(pkthdr) << ": PACKET!" 102 | << std::endl; 103 | } 104 | 105 | nethuns_rx_release(p, pkt_id); 106 | 107 | } else { 108 | errors++; 109 | if (nethuns_pkt_is_err(pkt_id)) { 110 | //std::cerr << "err: " << nethun s_error(p) << std::endl; 111 | } 112 | 113 | if ((errors % 1000000) == 0) { 114 | std::cerr << "errors: " << errors << std::endl; 115 | } 116 | } 117 | } 118 | while (!nethuns_pkt_is_eof(pkt_id)); 119 | 120 | std::cerr << "total packet: " << total << std::endl; 121 | std::cerr << "total errors: " << errors << std::endl; 122 | std::cerr << "total : " << (total + errors) << std::endl; 123 | 124 | nethuns_pcap_close(p); 125 | } 126 | else if (strcmp(argv[1], "capture") == 0) 127 | { 128 | nethuns_pcap_t *out; 129 | 130 | struct nethuns_socket_options opt = 131 | { 132 | .numblocks = 1 133 | , .numpackets = 1024 134 | , .packetsize = 2048 135 | , .timeout_ms = 0 136 | , .dir = nethuns_in_out 137 | , .capture = nethuns_cap_default 138 | , .mode = nethuns_socket_rx_tx 139 | , .timestamp = true 140 | , .promisc = false 141 | , .rxhash = false 142 | , .tx_qdisc_bypass = false 143 | , .xdp_prog = nullptr 144 | , .xdp_prog_sec = nullptr 145 | , .xsk_map_name = nullptr 146 | , .reuse_maps = false 147 | , .pin_dir = nullptr 148 | }; 149 | 150 | char errbuf[NETHUNS_ERRBUF_SIZE]; 151 | 152 | out = nethuns_pcap_open(&opt, (std::string{argv[2]} + ".pcap").c_str(), 1, errbuf); 153 | if (!out) { 154 | throw std::runtime_error(errbuf); 155 | } 156 | 157 | nethuns_socket_t * in; 158 | 159 | in = nethuns_open(&opt, errbuf); 160 | if (!in) 161 | { 162 | throw std::runtime_error(errbuf); 163 | } 164 | 165 | if (nethuns_bind(in, argv[2], NETHUNS_ANY_QUEUE) < 0) 166 | { 167 | throw nethuns_exception(in); 168 | } 169 | 170 | for(int i = 0; i < 10;) 171 | { 172 | const unsigned char *frame; 173 | const nethuns_pkthdr_t * pkthdr; 174 | 175 | uint64_t pkt_id; 176 | if ((pkt_id = nethuns_recv(in, &pkthdr, &frame))) 177 | { 178 | dump_packet(pkthdr, frame); 179 | nethuns_pcap_store(out, pkthdr, frame, nethuns_len(pkthdr)); 180 | 181 | nethuns_rx_release(in, pkt_id); 182 | i++; 183 | } 184 | } 185 | 186 | nethuns_pcap_close(out); 187 | nethuns_close(in); 188 | } 189 | else 190 | { 191 | std::cerr << argv[0] << ": argument error!" << std::endl; 192 | } 193 | 194 | return 0; 195 | } 196 | catch(nethuns_exception &e) 197 | { 198 | if (e.sock) { 199 | nethuns_close(e.sock); 200 | } 201 | std::cerr << e.what() << std::endl; 202 | return 1; 203 | } 204 | catch(std::exception &e) 205 | { 206 | std::cerr << e.what() << std::endl; 207 | return 1; 208 | } 209 | -------------------------------------------------------------------------------- /tools/nethuns-dump/src/options.c: -------------------------------------------------------------------------------- 1 | #include "hdr/options.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | static const char *version = "0.1"; 13 | 14 | void 15 | parse_device(const char *dev, struct dev_queue *dq) 16 | { 17 | const char *at; 18 | at = strchr(dev, '@') ? : strchr(dev, ':'); 19 | 20 | if (at == NULL) { 21 | dq->name = dev; 22 | dq->queue = NETHUNS_ANY_QUEUE; 23 | } else { 24 | dq->name = strndup(dev, at - dev); 25 | dq->queue = atoi(at+1); 26 | } 27 | } 28 | 29 | void help(const char* progname) { 30 | fprintf(stdout, 31 | "Usage: %s [-b num_blocks] [-r num_packets] [-s packet_size] [-t timeout_ms]\n" 32 | " [-Q direction] [-C capture_mode] [-M socket_mode] [-p] [-x] [-i device]\n" 33 | " [-q queue] [-c count] [-v] [-P xdp_prog] [-S xdp_prog_sec] [-K xsk_map_name]\n" 34 | " [-d pin_dir] [-R] [-Y] [-V] [-h]\n" 35 | "\n" 36 | "Options:\n" 37 | " -b num_blocks Number of blocks to allocate\n" 38 | " -r num_packets Number of packets to receive\n" 39 | " -s packet_size Size of each packet in bytes\n" 40 | " -t timeout_ms Timeout for each receive call in milliseconds\n" 41 | " -Q direction Direction of packets to capture (in/out/inout)\n" 42 | " -C capture_mode Capture mode (default/skb/drv/zerocopy)\n" 43 | " -M socket_mode Socket mode (rx_tx/rx/tx)\n" 44 | " -p Disable promiscuous mode\n" 45 | " -x Enable bypass of the tx queue discipline\n" 46 | " -i device Name of the device to capture packets from (e.g. eth0 or enp0s1@2)\n" 47 | " -c count Exit after capturing count packets\n" 48 | " -v Enable verbose output\n" 49 | #if NETHUNS_SOCKET == NETHUNS_SOCKET_XDP 50 | " -P xdp_prog Name of the XDP program to load\n" 51 | " -S xdp_prog_sec Name of the XDP program section to load\n" 52 | " -K xsk_map_name Name of the XDP shared memory map\n" 53 | " -d pin_dir Directory to pin XDP program and maps in\n" 54 | " -R Reuse existing maps when loading XDP program\n" 55 | #endif 56 | " -Y Run meter, don't print packets (repeat the option n times to reduce the frequency (factor 2^n)\n" 57 | " -V Print version information and exit\n" 58 | " -h Print this help message and exit\n", 59 | progname); 60 | exit(EXIT_SUCCESS); 61 | } 62 | 63 | struct options 64 | parse_opt(int argc, char *argv[]) { 65 | struct options ret = { 66 | .dev = {}, 67 | .num_devs = 0, 68 | .count = UINT64_MAX, 69 | .verbose = false, 70 | .meter = -1, 71 | .sopt = { 72 | .numblocks = 1 73 | , .numpackets = 4096 74 | , .packetsize = 2048 75 | , .timeout_ms = 0 76 | , .dir = nethuns_in_out 77 | , .capture = nethuns_cap_default 78 | , .mode = nethuns_socket_rx_tx 79 | , .timestamp = true 80 | , .promisc = true 81 | , .rxhash = true 82 | , .tx_qdisc_bypass = false 83 | , .xdp_prog = NULL 84 | , .xdp_prog_sec = NULL 85 | , .xsk_map_name = NULL 86 | , .reuse_maps = false 87 | , .pin_dir = NULL 88 | } 89 | }; 90 | 91 | int c; 92 | #if NETHUNS_SOCKET == NETHUNS_SOCKET_XDP 93 | while ((c = getopt(argc, argv, "b:r:s:t:Q:C:M:i:pvq:c:P:S:K:d:RYXh?V")) != -1) 94 | #else 95 | while ((c = getopt(argc, argv, "b:r:s:t:Q:C:M:i:pvq:c:YXh?V")) != -1) 96 | #endif 97 | { 98 | switch (c) 99 | { 100 | case 'b': 101 | ret.sopt.numblocks = atoi(optarg); 102 | break; 103 | case 'r': 104 | ret.sopt.numpackets = atoi(optarg); 105 | break; 106 | case 's': 107 | ret.sopt.packetsize = atoi(optarg); 108 | break; 109 | case 't': 110 | ret.sopt.timeout_ms = atoi(optarg); 111 | break; 112 | case 'Q': 113 | if (strcmp(optarg, "in") == 0) 114 | ret.sopt.dir = nethuns_in; 115 | else if (strcmp(optarg, "out") == 0) 116 | ret.sopt.dir = nethuns_out; 117 | else if (strcmp(optarg, "inout") == 0) 118 | ret.sopt.dir = nethuns_in_out; 119 | else { 120 | fprintf(stderr, "invalid direction: %s\n", optarg); 121 | exit(EXIT_FAILURE); 122 | } 123 | break; 124 | case 'C': 125 | if (strcmp(optarg, "default") == 0) 126 | ret.sopt.capture = nethuns_cap_default; 127 | else if (strcmp(optarg, "skb") == 0) 128 | ret.sopt.capture = nethuns_cap_skb_mode; 129 | else if (strcmp(optarg, "drv") == 0) 130 | ret.sopt.capture = nethuns_cap_drv_mode; 131 | else if (strcmp(optarg, "zerocopy") == 0) 132 | ret.sopt.capture = nethuns_cap_zero_copy; 133 | else { 134 | fprintf(stderr, "invalid capture mode: %s\n", optarg); 135 | exit(EXIT_FAILURE); 136 | } 137 | break; 138 | case 'M': 139 | if (strcmp(optarg, "rx_tx") == 0) 140 | ret.sopt.mode = nethuns_socket_rx_tx; 141 | else if (strcmp(optarg, "rx") == 0) 142 | ret.sopt.mode = nethuns_socket_rx_only; 143 | else if (strcmp(optarg, "tx") == 0) 144 | ret.sopt.mode = nethuns_socket_tx_only; 145 | else { 146 | fprintf(stderr, "invalid socket mode: %s\n", optarg); 147 | exit(EXIT_FAILURE); 148 | } 149 | break; 150 | case 'p': 151 | ret.sopt.promisc = false; 152 | break; 153 | case 'x': 154 | ret.sopt.tx_qdisc_bypass = true; 155 | break; 156 | case 'i': { 157 | if (ret.num_devs < MAX_DEVICES) { 158 | parse_device(optarg, &ret.dev[ret.num_devs]); 159 | ret.num_devs++; 160 | } else { 161 | fprintf(stderr, "too many devices!\n"); 162 | exit(EXIT_FAILURE); 163 | } 164 | } break; 165 | case 'c': 166 | ret.count = atoi(optarg); 167 | break; 168 | case 'v': 169 | ret.verbose = true; 170 | break; 171 | #if NETHUNS_SOCKET == NETHUNS_SOCKET_XDP 172 | case 'P': 173 | ret.sopt.xdp_prog = optarg; 174 | break; 175 | case 'S': 176 | ret.sopt.xdp_prog_sec = optarg; 177 | break; 178 | case 'K': 179 | ret.sopt.xsk_map_name = optarg; 180 | break; 181 | case 'd': 182 | ret.sopt.pin_dir = optarg; 183 | break; 184 | case 'R': 185 | ret.sopt.reuse_maps = true; 186 | break; 187 | #endif 188 | case 'Y': 189 | ret.meter++; 190 | break; 191 | case 'V': 192 | fprintf(stderr, "version: %s, %s\n", version, nethuns_version()); 193 | exit(EXIT_SUCCESS); 194 | case 'h': 195 | __attribute__ ((fallthrough)); 196 | case '?': 197 | help("nethuns-dump"); 198 | break; 199 | default: 200 | fprintf(stderr, "invalid option: %c\n", c); 201 | exit(EXIT_FAILURE); 202 | } 203 | } 204 | 205 | return ret; 206 | } 207 | 208 | 209 | void 210 | validate_options(const struct options *opt) 211 | { 212 | if (opt->num_devs == 0) 213 | { 214 | fprintf(stderr, "no device specified\n"); 215 | exit(EXIT_FAILURE); 216 | } 217 | } 218 | -------------------------------------------------------------------------------- /src/nethuns/sockets/libpcap.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2021 Larthia, University of Pisa. All rights reserved. 3 | * Use of this source code is governed by a BSD-style 4 | * license that can be found in the LICENSE file. 5 | */ 6 | 7 | #ifdef NETHUNS_SOCKET 8 | #undef NETHUNS_SOCKET 9 | #endif 10 | 11 | #define NETHUNS_SOCKET NETHUNS_SOCKET_LIBPCAP 12 | #include "ring.h" 13 | 14 | #define SOCKET_TYPE libpcap 15 | #include "file.inc" 16 | 17 | #include "../misc/compiler.h" 18 | #include "../api.h" 19 | 20 | #include "libpcap.h" 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | 32 | int 33 | nethuns_check_libpcap(size_t hsize, char *errbuf) { 34 | if (hsize != sizeof(nethuns_pkthdr_t)) { 35 | nethuns_perror(errbuf, "internal error: pkthdr size mismatch (expected %zu, got %zu)", sizeof(nethuns_pkthdr_t), hsize); 36 | return -1; 37 | } 38 | return 0; 39 | } 40 | 41 | struct nethuns_socket_libpcap * 42 | nethuns_open_libpcap(struct nethuns_socket_options *opt, char *errbuf) 43 | { 44 | struct nethuns_socket_libpcap *sock; 45 | 46 | sock = calloc(1, sizeof(struct nethuns_socket_libpcap)); 47 | if (!sock) 48 | { 49 | nethuns_perror(errbuf, "open: could not allocate socket"); 50 | return NULL; 51 | } 52 | 53 | if (nethuns_make_ring(opt->numblocks * opt->numpackets, opt->packetsize, &sock->base.rx_ring) < 0) 54 | { 55 | nethuns_perror(errbuf, "open: failed to allocate ring"); 56 | goto err_free; 57 | } 58 | 59 | if (nethuns_make_ring(opt->numblocks * opt->numpackets, opt->packetsize, &sock->base.tx_ring) < 0) 60 | { 61 | nethuns_perror(errbuf, "open: failed to allocate ring"); 62 | goto err_del_ring; 63 | } 64 | 65 | /* set a single consumer by default */ 66 | 67 | sock->base.opt = *opt; 68 | 69 | return sock; 70 | 71 | err_del_ring: 72 | nethuns_delete_ring(&sock->base.rx_ring); 73 | err_free: 74 | free(sock); 75 | return NULL; 76 | } 77 | 78 | 79 | int nethuns_close_libpcap(struct nethuns_socket_libpcap *s) 80 | { 81 | if (s && s->p) 82 | { 83 | pcap_close(s->p); 84 | 85 | __nethuns_free_base(s); 86 | 87 | free(s); 88 | } 89 | return 0; 90 | } 91 | 92 | 93 | int 94 | nethuns_bind_libpcap(struct nethuns_socket_libpcap *s, const char *dev, int queue) 95 | { 96 | char errbuf[PCAP_ERRBUF_SIZE]; 97 | 98 | if (queue != NETHUNS_ANY_QUEUE) 99 | { 100 | nethuns_perror(nethuns_socket(s)->errbuf, "open: only ANY_QUEUE is supported by this driver (%s)", nethuns_dev_queue_name(dev, queue)); 101 | return -1; 102 | } 103 | 104 | nethuns_socket(s)->queue = NETHUNS_ANY_QUEUE; 105 | 106 | s->p = pcap_create(dev, errbuf); 107 | if (!s->p) { 108 | nethuns_perror(s->base.errbuf, "bind: %s (%s)", errbuf, nethuns_dev_queue_name(dev, queue)); 109 | return -1; 110 | } 111 | 112 | if (pcap_set_immediate_mode(s->p, 1) != 0) 113 | { 114 | nethuns_perror(s->base.errbuf, "bind: %s (%s)", pcap_geterr(s->p), nethuns_dev_queue_name(dev, queue)); 115 | return -1; 116 | } 117 | 118 | if (pcap_set_buffer_size(s->p, (int)(nethuns_socket(s)->opt.numblocks * nethuns_socket(s)->opt.numpackets * nethuns_socket(s)->opt.packetsize)) != 0) 119 | { 120 | nethuns_perror(s->base.errbuf, "bind: %s (%s)", pcap_geterr(s->p), nethuns_dev_queue_name(dev, queue)); 121 | return -1; 122 | } 123 | 124 | if (nethuns_socket(s)->opt.promisc) 125 | { 126 | if (pcap_set_promisc(s->p, 1) != 0) 127 | { 128 | nethuns_perror(s->base.errbuf, "bind: %s (%s)", pcap_geterr(s->p), nethuns_dev_queue_name(dev, queue)); 129 | return -1; 130 | } 131 | } 132 | 133 | if (pcap_set_snaplen(s->p, (int)nethuns_socket(s)->opt.packetsize) != 0) 134 | { 135 | nethuns_perror(s->base.errbuf, "bind: %s (%s)", pcap_geterr(s->p), nethuns_dev_queue_name(dev, queue)); 136 | return -1; 137 | } 138 | 139 | if (pcap_set_timeout(s->p, (int)nethuns_socket(s)->opt.timeout_ms) != 0) 140 | { 141 | nethuns_perror(s->base.errbuf, "bind: %s (%s)", pcap_geterr(s->p), nethuns_dev_queue_name(dev, queue)); 142 | return -1; 143 | } 144 | 145 | if (pcap_activate(s->p) != 0) 146 | { 147 | nethuns_perror(s->base.errbuf, "bind: %s (%s)", pcap_geterr(s->p), nethuns_dev_queue_name(dev, queue)); 148 | return -1; 149 | } 150 | 151 | if (pcap_setnonblock(s->p, 1, errbuf) < 0) 152 | { 153 | nethuns_perror(s->base.errbuf, "bind: %s (%s)", errbuf, nethuns_dev_queue_name(dev, queue)); 154 | return -1; 155 | } 156 | 157 | switch (nethuns_socket(s)->opt.dir) 158 | { 159 | case nethuns_in: { 160 | if (pcap_setdirection(s->p, PCAP_D_IN) < 0) 161 | { 162 | nethuns_perror(s->base.errbuf, "bind: dir_in %s (%s)", pcap_geterr(s->p), nethuns_dev_queue_name(dev, queue)); 163 | return -1; 164 | } 165 | } break; 166 | case nethuns_out: { 167 | if (pcap_setdirection(s->p, PCAP_D_OUT) < 0) 168 | { 169 | nethuns_perror(s->base.errbuf, "bind: dir_out %s (%s)", pcap_geterr(s->p), nethuns_dev_queue_name(dev, queue)); 170 | return -1; 171 | } 172 | } break; 173 | case nethuns_in_out: 174 | { 175 | if (pcap_setdirection(s->p, PCAP_D_INOUT) < 0) 176 | { 177 | nethuns_perror(s->base.errbuf, "bind: dir_inout %s (%s)", pcap_geterr(s->p), nethuns_dev_queue_name(dev, queue)); 178 | return -1; 179 | } 180 | } 181 | } 182 | 183 | nethuns_socket(s)->queue = queue; 184 | nethuns_socket(s)->ifindex = (int)if_nametoindex(dev); 185 | nethuns_socket(s)->devname = strdup(dev); 186 | 187 | return 0; 188 | } 189 | 190 | 191 | uint64_t 192 | nethuns_recv_libpcap(struct nethuns_socket_libpcap *s, nethuns_pkthdr_t const **pkthdr, uint8_t const **payload) 193 | { 194 | unsigned int caplen = nethuns_socket(s)->opt.packetsize; 195 | unsigned int bytes; 196 | const uint8_t *ppayload; 197 | 198 | struct pcap_pkthdr header; 199 | 200 | struct nethuns_ring_slot * slot = nethuns_ring_get_slot(&s->base.rx_ring, s->base.rx_ring.head); 201 | 202 | #if 1 203 | if (s->p == NULL || __atomic_load_n(&slot->inuse, __ATOMIC_ACQUIRE)) 204 | { 205 | return 0; 206 | } 207 | #else 208 | if ((p->base.ring.head - p->base.ring.tail) == (p->base.ring.size-1)) 209 | { 210 | nethuns_ring_free_id(&p->base.ring, CALLBACK, ARG); 211 | if ((p->base.ring.head - p->base.ring.tail) == (p->base.ring.size-1)) 212 | return 0; 213 | } 214 | #endif 215 | 216 | ppayload = pcap_next(s->p, &header); 217 | 218 | bytes = MIN(caplen, header.caplen); 219 | 220 | if (ppayload) 221 | { 222 | int filt = !nethuns_socket(s)->filter ? 1 : nethuns_socket(s)->filter(nethuns_socket(s)->filter_ctx, &header, ppayload); 223 | if (filt > 0) { 224 | memcpy(&slot->pkthdr, &header, sizeof(slot->pkthdr)); 225 | memcpy(slot->packet, ppayload, bytes); 226 | slot->pkthdr.caplen = bytes; 227 | __atomic_store_n(&slot->inuse, 1, __ATOMIC_RELEASE); 228 | 229 | *pkthdr = &slot->pkthdr; 230 | *payload = slot->packet; 231 | 232 | return ++s->base.rx_ring.head; 233 | 234 | } else if (unlikely(filt < 0)) { 235 | memcpy(&slot->pkthdr, &header, sizeof(slot->pkthdr)); 236 | slot->pkthdr.caplen = bytes; 237 | __atomic_store_n(&slot->inuse, 1, __ATOMIC_RELEASE); 238 | 239 | *pkthdr = &slot->pkthdr; 240 | *payload = NULL; 241 | 242 | return ++s->base.rx_ring.head; 243 | } 244 | } 245 | 246 | return 0; 247 | } 248 | 249 | 250 | int 251 | nethuns_send_libpcap(struct nethuns_socket_libpcap *s, uint8_t const *packet, unsigned int len) 252 | { 253 | if (likely(s->p != NULL)) { 254 | return pcap_inject(s->p, packet, len); 255 | } 256 | 257 | return -1; 258 | } 259 | 260 | 261 | int 262 | nethuns_flush_libpcap(__maybe_unused struct nethuns_socket_libpcap *s) 263 | { 264 | return 0; 265 | } 266 | 267 | 268 | int 269 | nethuns_stats_libpcap(struct nethuns_socket_libpcap *s, struct nethuns_stat *stats) 270 | { 271 | struct pcap_stat ps; 272 | if (s->p == NULL || pcap_stats(s->p, &ps) == -1) 273 | { 274 | return -1; 275 | } 276 | 277 | stats->rx_packets = ps.ps_recv; 278 | stats->tx_packets = 0; 279 | stats->rx_dropped = ps.ps_drop; 280 | stats->rx_if_dropped = ps.ps_ifdrop; 281 | stats->rx_invalid = 0; 282 | stats->tx_invalid = 0; 283 | stats->freeze = 0; 284 | return 0; 285 | } 286 | 287 | 288 | int 289 | nethuns_fanout_libpcap(__maybe_unused struct nethuns_socket_libpcap *s, __maybe_unused int group, __maybe_unused const char *fanout) 290 | { 291 | nethuns_perror(s->base.errbuf, "fanout: not supported (%s)", nethuns_device_name(s)); 292 | return -1; 293 | } 294 | 295 | 296 | int nethuns_fd_libpcap(__maybe_unused struct nethuns_socket_libpcap *s) 297 | { 298 | nethuns_perror(s->base.errbuf, "fd: not supported (%s)", nethuns_device_name(s)); 299 | return -1; 300 | } 301 | 302 | 303 | void 304 | nethuns_dump_rings_libpcap(__maybe_unused struct nethuns_socket_libpcap *s) 305 | { 306 | } 307 | -------------------------------------------------------------------------------- /misc/example3.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #define BLOCK_SIZE (1 << 22) 25 | #define FRAME_SIZE 2048 26 | 27 | #define NUM_BLOCKS 64 28 | #define NUM_FRAMES ((BLOCK_SIZE * NUM_BLOCKS) / FRAME_SIZE) 29 | 30 | #define BLOCK_RETIRE_TOV_IN_MS 64 31 | #define BLOCK_PRIV_AREA_SZ 13 32 | 33 | #define ALIGN_8(x) (((x) + 8 - 1) & ~(8 - 1)) 34 | 35 | #define BLOCK_STATUS(x) ((x)->h1.block_status) 36 | #define BLOCK_NUM_PKTS(x) ((x)->h1.num_pkts) 37 | #define BLOCK_O2FP(x) ((x)->h1.offset_to_first_pkt) 38 | #define BLOCK_LEN(x) ((x)->h1.blk_len) 39 | #define BLOCK_SNUM(x) ((x)->h1.seq_num) 40 | #define BLOCK_O2PRIV(x) ((x)->offset_to_priv) 41 | #define BLOCK_PRIV(x) ((void *) ((uint8_t *) (x) + BLOCK_O2PRIV(x))) 42 | #define BLOCK_HDR_LEN (ALIGN_8(sizeof(struct block_desc))) 43 | #define BLOCK_PLUS_PRIV(sz_pri) (BLOCK_HDR_LEN + ALIGN_8((sz_pri))) 44 | 45 | #ifndef likely 46 | # define likely(x) __builtin_expect(!!(x), 1) 47 | #endif 48 | #ifndef unlikely 49 | # define unlikely(x) __builtin_expect(!!(x), 0) 50 | #endif 51 | 52 | struct block_desc { 53 | uint32_t version; 54 | uint32_t offset_to_priv; 55 | struct tpacket_hdr_v1 h1; 56 | }; 57 | 58 | struct ring { 59 | struct iovec *rd; 60 | uint8_t *map; 61 | struct tpacket_req3 req; 62 | }; 63 | 64 | static unsigned long packets_total = 0, bytes_total = 0; 65 | static sig_atomic_t sigint = 0; 66 | 67 | void sighandler(int num) 68 | { 69 | sigint = 1; 70 | } 71 | 72 | static int setup_socket(struct ring *ring, char *netdev) 73 | { 74 | int err, i, fd, v = TPACKET_V3; 75 | struct sockaddr_ll ll; 76 | 77 | fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); 78 | if (fd < 0) { 79 | perror("socket"); 80 | exit(1); 81 | } 82 | 83 | err = setsockopt(fd, SOL_PACKET, PACKET_VERSION, &v, sizeof(v)); 84 | if (err < 0) { 85 | perror("setsockopt"); 86 | exit(1); 87 | } 88 | 89 | memset(&ring->req, 0, sizeof(ring->req)); 90 | ring->req.tp_block_size = BLOCK_SIZE; 91 | ring->req.tp_frame_size = FRAME_SIZE; 92 | ring->req.tp_block_nr = NUM_BLOCKS; 93 | ring->req.tp_frame_nr = NUM_FRAMES; 94 | ring->req.tp_retire_blk_tov = BLOCK_RETIRE_TOV_IN_MS; 95 | ring->req.tp_sizeof_priv = BLOCK_PRIV_AREA_SZ; 96 | ring->req.tp_feature_req_word |= 0; // TP_FT_REQ_FILL_RXHASH; 97 | 98 | err = setsockopt(fd, SOL_PACKET, PACKET_RX_RING, &ring->req, 99 | sizeof(ring->req)); 100 | if (err < 0) { 101 | perror("setsockopt"); 102 | exit(1); 103 | } 104 | 105 | ring->map = (uint8_t *)mmap(NULL, ring->req.tp_block_size * ring->req.tp_block_nr, 106 | PROT_READ | PROT_WRITE, MAP_SHARED | MAP_LOCKED, 107 | fd, 0); 108 | if (ring->map == MAP_FAILED) { 109 | perror("mmap"); 110 | exit(1); 111 | } 112 | 113 | ring->rd = (iovec *)malloc(ring->req.tp_block_nr * sizeof(*ring->rd)); 114 | assert(ring->rd); 115 | for (i = 0; i < ring->req.tp_block_nr; ++i) { 116 | ring->rd[i].iov_base = ring->map + (i * ring->req.tp_block_size); 117 | ring->rd[i].iov_len = ring->req.tp_block_size; 118 | } 119 | 120 | memset(&ll, 0, sizeof(ll)); 121 | ll.sll_family = PF_PACKET; 122 | ll.sll_protocol = htons(ETH_P_ALL); 123 | ll.sll_ifindex = if_nametoindex(netdev); 124 | ll.sll_hatype = 0; 125 | ll.sll_pkttype = 0; 126 | ll.sll_halen = 0; 127 | 128 | err = bind(fd, (struct sockaddr *) &ll, sizeof(ll)); 129 | if (err < 0) { 130 | perror("bind"); 131 | exit(1); 132 | } 133 | 134 | return fd; 135 | } 136 | 137 | #ifdef __checked 138 | static uint64_t prev_block_seq_num = 0; 139 | 140 | void assert_block_seq_num(struct block_desc *pbd) 141 | { 142 | if (unlikely(prev_block_seq_num + 1 != BLOCK_SNUM(pbd))) { 143 | printf("prev_block_seq_num:%"PRIu64", expected seq:%"PRIu64" != " 144 | "actual seq:%"PRIu64"\n", prev_block_seq_num, 145 | prev_block_seq_num + 1, (uint64_t) BLOCK_SNUM(pbd)); 146 | exit(1); 147 | } 148 | 149 | prev_block_seq_num = BLOCK_SNUM(pbd); 150 | } 151 | 152 | static void assert_block_len(struct block_desc *pbd, uint32_t bytes, int block_num) 153 | { 154 | if (BLOCK_NUM_PKTS(pbd)) { 155 | if (unlikely(bytes != BLOCK_LEN(pbd))) { 156 | printf("block:%u with %upackets, expected len:%u != actual len:%u\n", 157 | block_num, BLOCK_NUM_PKTS(pbd), bytes, BLOCK_LEN(pbd)); 158 | exit(1); 159 | } 160 | } else { 161 | if (unlikely(BLOCK_LEN(pbd) != BLOCK_PLUS_PRIV(BLOCK_PRIV_AREA_SZ))) { 162 | printf("block:%u, expected len:%lu != actual len:%u\n", 163 | block_num, BLOCK_HDR_LEN, BLOCK_LEN(pbd)); 164 | exit(1); 165 | } 166 | } 167 | } 168 | 169 | static void assert_block_header(struct block_desc *pbd, const int block_num) 170 | { 171 | uint32_t block_status = BLOCK_STATUS(pbd); 172 | 173 | if (unlikely((block_status & TP_STATUS_USER) == 0)) { 174 | printf("block:%u, not in TP_STATUS_USER\n", block_num); 175 | exit(1); 176 | } 177 | 178 | assert_block_seq_num(pbd); 179 | } 180 | #else 181 | static inline void assert_block_header(struct block_desc *pbd, const int block_num) 182 | { 183 | } 184 | static void assert_block_len(struct block_desc *pbd, uint32_t bytes, int block_num) 185 | { 186 | } 187 | #endif 188 | 189 | static void display(struct tpacket3_hdr *ppd) 190 | { 191 | struct ethhdr *eth = (struct ethhdr *) ((uint8_t *) ppd + ppd->tp_mac); 192 | struct iphdr *ip = (struct iphdr *) ((uint8_t *) eth + ETH_HLEN); 193 | 194 | if (eth->h_proto == htons(ETH_P_IP)) { 195 | struct sockaddr_in ss, sd; 196 | char sbuff[NI_MAXHOST], dbuff[NI_MAXHOST]; 197 | 198 | memset(&ss, 0, sizeof(ss)); 199 | ss.sin_family = PF_INET; 200 | ss.sin_addr.s_addr = ip->saddr; 201 | getnameinfo((struct sockaddr *) &ss, sizeof(ss), 202 | sbuff, sizeof(sbuff), NULL, 0, NI_NUMERICHOST); 203 | 204 | memset(&sd, 0, sizeof(sd)); 205 | sd.sin_family = PF_INET; 206 | sd.sin_addr.s_addr = ip->daddr; 207 | getnameinfo((struct sockaddr *) &sd, sizeof(sd), 208 | dbuff, sizeof(dbuff), NULL, 0, NI_NUMERICHOST); 209 | 210 | printf("%s -> %s, ", sbuff, dbuff); 211 | } 212 | 213 | printf("rxhash: 0x%x\n", ppd->hv1.tp_rxhash); 214 | } 215 | 216 | 217 | std::atomic_long total_counter; 218 | 219 | void meter() 220 | { 221 | auto now = std::chrono::system_clock::now(); 222 | for(;;) 223 | { 224 | now += std::chrono::seconds(1); 225 | std::this_thread::sleep_until(now); 226 | auto x = total_counter.exchange(0); 227 | std::cout << "pkt/sec: " << x << std::endl; 228 | } 229 | } 230 | 231 | 232 | static void walk_block(struct block_desc *pbd, const int block_num) 233 | { 234 | int num_pkts = BLOCK_NUM_PKTS(pbd), i; 235 | unsigned long bytes = 0; 236 | unsigned long bytes_with_padding = BLOCK_PLUS_PRIV(BLOCK_PRIV_AREA_SZ); 237 | struct tpacket3_hdr *ppd; 238 | 239 | assert_block_header(pbd, block_num); 240 | 241 | ppd = (struct tpacket3_hdr *) ((uint8_t *) pbd + BLOCK_O2FP(pbd)); 242 | 243 | 244 | for (i = 0; i < num_pkts /* BLOCK_SIZE/FRAME_SIZE */; ++i) { 245 | 246 | static uint64_t cntr = 0; 247 | if (cntr++ == 2000000) 248 | { 249 | cntr = 0; 250 | std::cerr << "num_pkts: " << num_pkts << std::endl; 251 | } 252 | 253 | bytes += ppd->tp_snaplen; 254 | if (ppd->tp_next_offset) 255 | bytes_with_padding += ppd->tp_next_offset; 256 | else 257 | bytes_with_padding += ALIGN_8(ppd->tp_snaplen + ppd->tp_mac); 258 | 259 | total_counter.fetch_add(1, std::memory_order_relaxed); 260 | 261 | // display(ppd); 262 | 263 | ppd = (struct tpacket3_hdr *) ((uint8_t *) ppd + ppd->tp_next_offset); 264 | __sync_synchronize(); 265 | } 266 | 267 | assert_block_len(pbd, bytes_with_padding, block_num); 268 | 269 | packets_total += num_pkts; 270 | bytes_total += bytes; 271 | } 272 | 273 | void flush_block(struct block_desc *pbd) 274 | { 275 | BLOCK_STATUS(pbd) = TP_STATUS_KERNEL; 276 | __sync_synchronize(); 277 | } 278 | 279 | static void teardown_socket(struct ring *ring, int fd) 280 | { 281 | munmap(ring->map, ring->req.tp_block_size * ring->req.tp_block_nr); 282 | free(ring->rd); 283 | close(fd); 284 | } 285 | 286 | int main(int argc, char **argp) 287 | { 288 | int fd, err; 289 | socklen_t len; 290 | struct ring ring; 291 | struct pollfd pfd; 292 | unsigned int block_num = 0; 293 | struct block_desc *pbd; 294 | struct tpacket_stats_v3 stats; 295 | 296 | if (argc != 2) { 297 | fprintf(stderr, "Usage: %s INTERFACE\n", argp[0]); 298 | return EXIT_FAILURE; 299 | } 300 | 301 | std::thread(meter).detach(); 302 | 303 | signal(SIGINT, sighandler); 304 | 305 | memset(&ring, 0, sizeof(ring)); 306 | fd = setup_socket(&ring, argp[argc - 1]); 307 | assert(fd > 0); 308 | 309 | memset(&pfd, 0, sizeof(pfd)); 310 | pfd.fd = fd; 311 | pfd.events = POLLIN | POLLERR; 312 | pfd.revents = 0; 313 | 314 | while (likely(!sigint)) { 315 | pbd = (struct block_desc *) ring.rd[block_num].iov_base; 316 | retry_block: 317 | if ((BLOCK_STATUS(pbd) & TP_STATUS_USER) == 0) { 318 | poll(&pfd, 1, -1); 319 | goto retry_block; 320 | } 321 | 322 | walk_block(pbd, block_num); 323 | flush_block(pbd); 324 | block_num = (block_num + 1) % NUM_BLOCKS; 325 | } 326 | 327 | len = sizeof(stats); 328 | err = getsockopt(fd, SOL_PACKET, PACKET_STATISTICS, &stats, &len); 329 | if (err < 0) { 330 | perror("getsockopt"); 331 | exit(1); 332 | } 333 | 334 | fflush(stdout); 335 | printf("\nReceived %u packets, %lu bytes, %u dropped, freeze_q_cnt: %u\n", 336 | stats.tp_packets, bytes_total, stats.tp_drops, 337 | stats.tp_freeze_q_cnt); 338 | 339 | teardown_socket(&ring, fd); 340 | return 0; 341 | } 342 | 343 | -------------------------------------------------------------------------------- /test/src/send.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Larthia, University of Pisa. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | 17 | /// manage command line options 18 | const struct option long_opts[] = { 19 | {"help", no_argument, 0, 'h'}, 20 | {"interface", required_argument, 0, 'i'}, 21 | {"batch_size", required_argument, 0, 'b'}, 22 | {"sockets", required_argument, 0, 'n'}, 23 | {"multithreading", no_argument, 0, 'm'}, 24 | {"zerocopy", no_argument, 0, 'z'}, 25 | {0, 0, 0, 0} 26 | }; 27 | 28 | const std::string help_brief = "Usage: nethuns-send [ options ]\n" \ 29 | "Use --help (or -h) to see full option list and a complete description.\n\n" 30 | "Required options: \n" \ 31 | "\t\t\t[ -i ] \t set network interface \n" \ 32 | "Other options: \n" \ 33 | "\t\t\t[ -b ] \t set batch size \n" \ 34 | "\t\t\t[ -n ] \t\t set number of sockets \n" \ 35 | "\t\t\t[ -m ] \t\t\t enable multithreading \n" \ 36 | "\t\t\t[ -z ] \t\t\t enable send zero-copy \n"; 37 | 38 | const std::string help_long = "Usage: nethuns-send [ options ] \n\n" \ 39 | "-h, --help \t\t\t\t Show program usage and exit.\n\n" \ 40 | "Required options: \n\n" \ 41 | "-i, --interface \t \t Name of the network interface that nethuns-send operates on.\n\n" \ 42 | "Other options: \n\n" \ 43 | "-b, --batch_size \t \t Batch size for packet transmission (default = 1).\n\n" \ 44 | "-n, --sockets \t\t \t Number of sockets to use. By default, only one socket is used.\n\n" \ 45 | "-m, --multithreading \t\t\t Enable multithreading. By default, only one thread is used. " \ 46 | "\n\t\t\t\t\t If multithreading is enabled, and there is more than one socket in use, " \ 47 | "\n\t\t\t\t\t each socket is handled by a separated thread.\n\n" \ 48 | "-z, --zerocopy \t\t\t\t Enable send zero-copy. By default, classic send that requires a copy is used.\n"; 49 | 50 | 51 | nethuns_socket_t **out; 52 | struct nethuns_socket_options netopt; 53 | char (*errbufs)[NETHUNS_ERRBUF_SIZE]; 54 | 55 | uint64_t *pktid; 56 | std::string interface = ""; 57 | int batch_size = 1; 58 | int nsock = 1; 59 | bool mthreading = false; 60 | std::vector threads; 61 | bool zerocopy = false; 62 | 63 | // terminate application 64 | volatile bool term = false; 65 | 66 | // termination signal handler 67 | void terminate(int exit_signal) 68 | { 69 | (void)exit_signal; 70 | term = true; 71 | } 72 | 73 | // compute stats 74 | std::vector totals; 75 | 76 | void meter() 77 | { 78 | auto now = std::chrono::system_clock::now(); 79 | long old_totals = 0; 80 | while (!term) { 81 | now += std::chrono::seconds(1); 82 | std::this_thread::sleep_until(now); 83 | long x = 0; 84 | for (auto &total : totals) 85 | x += total; 86 | std::cout << "pkt/sec: " << x - old_totals << std::endl; 87 | old_totals = x; 88 | } 89 | } 90 | 91 | // setup and fill transmission ring 92 | void fill_tx_ring(int th_idx, const unsigned char *payload, int pkt_size) 93 | { 94 | unsigned int j; 95 | 96 | out[th_idx] = nethuns_open(&netopt, errbufs[th_idx]); 97 | if (!out[th_idx]) { 98 | throw std::runtime_error(errbufs[th_idx]); 99 | } 100 | 101 | if (nethuns_bind(out[th_idx], interface.c_str(), nsock > 1 ? th_idx : NETHUNS_ANY_QUEUE) < 0) { 102 | throw nethuns_exception(out[th_idx]); 103 | } 104 | 105 | // fill the slots in the tx ring (optimized send only) 106 | if (zerocopy) { 107 | for (j = 0; j < nethuns_txring_get_size(out[th_idx]); j++) { 108 | uint8_t *pkt = nethuns_get_buf_addr(out[th_idx], j); // tell me where to copy the j-th packet to be transmitted 109 | memcpy(pkt, payload, pkt_size); // copy the packet 110 | } 111 | pktid[th_idx] = 0; // first position (slot) in tx ring to be transmitted 112 | } 113 | } 114 | 115 | // transmit packets in the tx ring (use optimized send, zero copy) 116 | void transmit_zc(int th_idx, int pkt_size) 117 | { 118 | // prepare batch 119 | for (int n = 0; n < batch_size; n++) { 120 | if (nethuns_send_slot(out[th_idx], pktid[th_idx], pkt_size) <= 0) 121 | break; 122 | pktid[th_idx]++; 123 | totals.at(th_idx)++; 124 | } 125 | nethuns_flush(out[th_idx]); // send batch 126 | } 127 | 128 | // transmit packets in the tx ring (use classic send, copy) 129 | void transmit_c(int th_idx, const unsigned char *payload, int pkt_size) 130 | { 131 | // prepare batch 132 | for (int n = 0; n < batch_size; n++) { 133 | if (nethuns_send(out[th_idx], payload, pkt_size) <= 0) 134 | break; 135 | totals.at(th_idx)++; 136 | } 137 | nethuns_flush(out[th_idx]); // send batch 138 | } 139 | 140 | // single-thread single-socket transmission 141 | void st_send(int th_idx, const unsigned char *payload, int pkt_size) 142 | { 143 | try { 144 | fill_tx_ring(th_idx, payload, pkt_size); 145 | 146 | while (!term) { 147 | if (zerocopy) 148 | transmit_zc(th_idx, pkt_size); 149 | else 150 | transmit_c(th_idx, payload, pkt_size); 151 | } 152 | } catch(nethuns_exception &e) { 153 | if (e.sock) { 154 | nethuns_close(e.sock); 155 | } 156 | std::cerr << e.what() << std::endl; 157 | } catch(std::exception &e) { 158 | std::cerr << e.what() << std::endl; 159 | } 160 | } 161 | 162 | 163 | int 164 | main(int argc, char *argv[]) 165 | { 166 | int i; 167 | 168 | static const unsigned char payload[34] = 169 | { 170 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xbf, /* L`..UF.. */ 171 | 0x97, 0xe2, 0xff, 0xae, 0x08, 0x00, 0x45, 0x00, /* ......E. */ 172 | 0x00, 0x54, 0xb3, 0xf9, 0x40, 0x00, 0x40, 0x11, /* .T..@.@. */ 173 | 0xf5, 0x32, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* .2...... */ 174 | 0x07, 0x08 175 | }; 176 | 177 | // parse options from command line 178 | int opt = 0; 179 | int optidx = 0; 180 | opterr = 1; // turn on/off getopt error messages 181 | if (argc > 1 && argc < 10) { 182 | while ((opt = getopt_long(argc, argv, "hi:b:n:mz", long_opts, &optidx)) != -1) { 183 | switch (opt) { 184 | case 'h': 185 | std::cout << help_long << std::endl; 186 | return 0; 187 | case 'i': 188 | if (optarg) 189 | interface = optarg; 190 | break; 191 | case 'b': 192 | if (optarg) 193 | batch_size = atoi(optarg); 194 | break; 195 | case 'n': 196 | if (optarg) 197 | nsock = atoi(optarg); 198 | break; 199 | case 'm': 200 | mthreading = true; 201 | break; 202 | case 'z': 203 | zerocopy = true; 204 | break; 205 | default: 206 | std::cerr << "Error in parsing command line options.\n" << help_brief << std::endl; 207 | return 1; 208 | } 209 | } 210 | } else { 211 | std::cerr << help_brief << std::endl; 212 | return 1; 213 | } 214 | 215 | nethuns_init(); 216 | 217 | std::cout << "\nTest " << argv[0] << " started with parameters \n" 218 | << "* interface: " << interface << " \n" 219 | << "* batch_size: " << batch_size << " \n" 220 | << "* sockets: " << nsock << " \n" 221 | << "* multithreading: " << ((mthreading) ? " ON \n" : " OFF \n") 222 | << "* zero-copy: " << ((zerocopy) ? " ON \n" : " OFF \n") 223 | << std::endl; 224 | 225 | signal(SIGINT, terminate); // register termination signal 226 | 227 | // nethuns options 228 | netopt = { 229 | .numblocks = 1 230 | , .numpackets = 2048 231 | , .packetsize = 2048 232 | , .timeout_ms = 0 233 | , .dir = nethuns_in_out 234 | , .capture = nethuns_cap_zero_copy 235 | , .mode = nethuns_socket_rx_tx 236 | , .timestamp = true 237 | , .promisc = false 238 | , .rxhash = false 239 | , .tx_qdisc_bypass = true 240 | , .xdp_prog = nullptr 241 | , .xdp_prog_sec = nullptr 242 | , .xsk_map_name = nullptr 243 | , .reuse_maps = false 244 | , .pin_dir = nullptr 245 | }; 246 | 247 | out = new nethuns_socket_t*[nsock](); 248 | pktid = new uint64_t[nsock](); // one packet index per socket (pos of next slot/packet to send in tx ring) 249 | errbufs = new char[nsock][NETHUNS_ERRBUF_SIZE](); // one errbuf per thread 250 | 251 | for (i = 0; i < nsock; i++) { 252 | totals.push_back(0); // stats counters init 253 | } 254 | 255 | // create thread for computing statistics 256 | std::thread stats_th(meter); 257 | 258 | // case single thread (main) with generic number of sockets 259 | if (!mthreading) { 260 | try { 261 | for (i = 0; i < nsock; i++) { 262 | fill_tx_ring(i, payload, 34); 263 | } 264 | 265 | while (!term) { 266 | for (i = 0; i < nsock; i++) { 267 | if (zerocopy) 268 | transmit_zc(i, 34); 269 | else 270 | transmit_c(i, payload, 34); 271 | } 272 | } 273 | } catch(nethuns_exception &e) { 274 | if (e.sock) { 275 | nethuns_close(e.sock); 276 | } 277 | std::cerr << e.what() << std::endl; 278 | return 1; 279 | } catch(std::exception &e) { 280 | std::cerr << e.what() << std::endl; 281 | return 1; 282 | } 283 | } else { // case multithreading enabled (num_threads == num_sockets) 284 | for (i = 0; i < nsock; i++) { 285 | std::thread th(st_send, i, payload, 34); 286 | threads.push_back(std::move(th)); 287 | } 288 | } 289 | 290 | for (i = 0; i < nsock; i++) { 291 | if (mthreading) 292 | threads.at(i).join(); // nsock send_th threads to join 293 | nethuns_close(out[i]); // nsock sockets to close 294 | } 295 | stats_th.join(); // 1 stats thread to join 296 | 297 | return 0; 298 | } 299 | --------------------------------------------------------------------------------