Skip to content

Commit

Permalink
Merge branch 'af_xdp_extension_libxdp' of https:/plangarb…
Browse files Browse the repository at this point in the history
…alint/tcpreplay into plangarbalint-af_xdp_extension_libxdp

* 'af_xdp_extension_libxdp' of https:/plangarbalint/tcpreplay:
  Fix copy/paste error in configure.ac
  Check packet fits in umem frame before copying
  Fix loop feature for AF_XDP packet sending
  libxdp implementation
  • Loading branch information
fklassen committed Sep 4, 2023
2 parents bea06a4 + 5ac105d commit 0f012c8
Show file tree
Hide file tree
Showing 9 changed files with 588 additions and 19 deletions.
45 changes: 45 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -566,6 +566,10 @@ AC_ARG_ENABLE(force-libdnet,
AS_HELP_STRING([--enable-force-libdnet],[Force using libdnet for sending packets]),
[ AC_DEFINE([FORCE_INJECT_LIBDNET], [1], [Force using libdnet for sending packets])])

AC_ARG_ENABLE(force-libxdp,
AS_HELP_STRING([--enable-force-libxdp],[Force using libxdp for sending packets]),
[ AC_DEFINE([FORCE_INJECT_LIBXDP], [1], [Force using libxdp for sending packets])])

AC_ARG_ENABLE(force-inject,
AS_HELP_STRING([--enable-force-inject],[Force using libpcap's pcap_inject() for sending packets]),
[ AC_DEFINE([FORCE_INJECT_PCAP_INJECT],[1], [Force using libpcap's pcap_inject() for sending packets])])
Expand Down Expand Up @@ -844,6 +848,12 @@ fi
AC_SEARCH_LIBS([nl_handle_alloc], [nl],
[AC_MSG_NOTICE([Unable to find nl library - may be needed by libpcap])])

AC_CHECK_LIB(bpf, bpf_object__open_file,,
[AC_MSG_NOTICE([Unable to find libbpf library ])])

AC_CHECK_LIB(xdp, xsk_umem__delete,,
[AC_MSG_NOTICE([Unable to find libxdp library ])])

##
## If not automatically configured,
## check for newer and full-featured libpcap's
Expand Down Expand Up @@ -1399,6 +1409,37 @@ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
AC_MSG_RESULT(no)
])

have_libxdp=no
dnl Check for LIBXDP AF_XDP socket support
AC_MSG_CHECKING(for LIBXDP XDP packet sending support)
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
#include <stdlib.h>
#include <xdp/xsk.h>
#include <sys/socket.h>
]], [[
struct xsk_socket {
struct xsk_ring_cons *rx;
struct xsk_ring_prod *tx;
struct xsk_ctx *ctx;
struct xsk_socket_config config;
int fd;
};
struct xsk_socket *xsk;
struct xsk_ring_cons *rxr = NULL;
struct xsk_ring_prod *txr = NULL;
xsk = (struct xsk_socket*)malloc(sizeof(struct xsk_socket));
int queue_id = 0;
xsk_socket__create(xsk, "lo", queue_id, NULL, rxr, txr, NULL);
socket(AF_XDP, SOCK_RAW, 0);
]])],[
AC_DEFINE([HAVE_LIBXDP], [1],
[Do we have LIBXDP AF_XDP socket support?])
AC_MSG_RESULT(yes)
have_libxdp=yes
],[
AC_MSG_RESULT(no)
])

have_tx_ring=no
dnl Check for older Linux TX_RING support
AC_MSG_CHECKING(for TX_RING socket sending support)
Expand All @@ -1420,6 +1461,9 @@ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
AC_MSG_RESULT(no)
])

AC_CHECK_HEADERS([bpf/libbpf.h])
AC_CHECK_HEADERS([bpf/bpf.h])
AC_CHECK_HEADERS([xdp/libxdp.h])

AC_CHECK_HEADERS([net/bpf.h], [have_bpf=yes], [have_bpf=no])
if test $have_bpf = yes ; then
Expand Down Expand Up @@ -1948,6 +1992,7 @@ pcap_sendpacket: ${have_pcap_sendpacket} **
pcap_netmap ${have_pcap_netmap}
Linux/BSD netmap: ${have_netmap}
Tuntap device support: ${have_tuntap}
LIBXDP for AF_XDP socket: ${have_libxdp}
* In order of preference; see configure --help to override
** Required for tcpbridge
Expand Down
214 changes: 210 additions & 4 deletions src/common/sendpacket.c
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
#undef HAVE_PCAP_INJECT
#undef HAVE_PCAP_SENDPACKET
#undef HAVE_BPF
#undef HAVE_LIBXDP
#endif

#ifdef FORCE_INJECT_PF_PACKET
Expand All @@ -71,6 +72,7 @@
#undef HAVE_PCAP_INJECT
#undef HAVE_PCAP_SENDPACKET
#undef HAVE_BPF
#undef HAVE_LIBXDP
#endif

#ifdef FORCE_INJECT_LIBDNET
Expand All @@ -79,6 +81,7 @@
#undef HAVE_PCAP_INJECT
#undef HAVE_PCAP_SENDPACKET
#undef HAVE_BPF
#undef HAVE_LIBXDP
#endif

#ifdef FORCE_INJECT_BPF
Expand All @@ -87,6 +90,7 @@
#undef HAVE_PCAP_INJECT
#undef HAVE_PCAP_SENDPACKET
#undef HAVE_PF_PACKET
#undef HAVE_LIBXDP
#endif

#ifdef FORCE_INJECT_PCAP_INJECT
Expand All @@ -95,6 +99,7 @@
#undef HAVE_PCAP_SENDPACKET
#undef HAVE_BPF
#undef HAVE_PF_PACKET
#undef HAVE_LIBXDP
#endif

#ifdef FORCE_INJECT_PCAP_SENDPACKET
Expand All @@ -103,15 +108,25 @@
#undef HAVE_PCAP_INJECT
#undef HAVE_BPF
#undef HAVE_PF_PACKET
#undef HAVE_LIBXDP
#endif

#ifdef FORCE_INJECT_LIBXDP
#undef HAVE_TX_RING
#undef HAVE_LIBDNET
#undef HAVE_PF_PACKET
#undef HAVE_PCAP_INJECT
#undef HAVE_PCAP_SENDPACKET
#undef HAVE_BPF
#endif

#if (defined HAVE_WINPCAP && defined HAVE_PCAP_INJECT)
#undef HAVE_PCAP_INJECT /* configure returns true for some odd reason */
#endif

#if !defined HAVE_PCAP_INJECT && !defined HAVE_PCAP_SENDPACKET && !defined HAVE_LIBDNET && !defined HAVE_PF_PACKET && \
!defined HAVE_BPF && !defined TX_RING
#error You need pcap_inject() or pcap_sendpacket() from libpcap, libdnet, Linux's PF_PACKET/TX_RING or *BSD's BPF
!defined HAVE_BPF && !defined TX_RING && !defined HAVE_LIBXDP
#error You need pcap_inject() or pcap_sendpacket() from libpcap, libdnet, Linux's PF_PACKET/TX_RING/AF_XDP with libxdp or *BSD's BPF
#endif

#ifdef HAVE_SYS_PARAM_H
Expand Down Expand Up @@ -211,7 +226,15 @@ static struct tcpr_ether_addr *sendpacket_get_hwaddr_pcap(sendpacket_t *) _U_;
#undef INJECT_METHOD
#define INJECT_METHOD "pcap_sendpacket()"
#endif

#ifdef HAVE_LIBXDP
#include <sys/mman.h>
static sendpacket_t *sendpacket_open_xsk(const char *, char *) _U_;
static struct tcpr_ether_addr *sendpacket_get_hwaddr_libxdp(sendpacket_t *);
#endif
#if defined HAVE_LIBXDP && !defined INJECT_METHOD
#undef INJECT_METHOD
#define INJECT_METHOD "xsk_ring_prod_submit()"
#endif
static void sendpacket_seterr(sendpacket_t *sp, const char *fmt, ...);
static sendpacket_t *sendpacket_open_khial(const char *, char *) _U_;
static struct tcpr_ether_addr *sendpacket_get_hwaddr_khial(sendpacket_t *) _U_;
Expand All @@ -237,7 +260,10 @@ sendpacket(sendpacket_t *sp, const u_char *data, size_t len, struct pcap_pkthdr
static const size_t buffer_payload_size = sizeof(buffer) + sizeof(struct pcap_pkthdr);

assert(sp);
#ifndef HAVE_LIBXDP
// In case of XDP packet processing we are storing data in sp->packet_processing->xdp_descs
assert(data);
#endif

if (len == 0)
return -1;
Expand Down Expand Up @@ -452,7 +478,18 @@ sendpacket(sendpacket_t *sp, const u_char *data, size_t len, struct pcap_pkthdr
}
#endif /* HAVE_NETMAP */
break;

case SP_TYPE_LIBXDP:
#ifdef HAVE_LIBXDP
retcode = len;
xsk_ring_prod__submit(&(sp->xsk_info->tx), sp->pckt_count); // submit all packets at once
sp->xsk_info->ring_stats.tx_npkts += sp->pckt_count;
sp->xsk_info->outstanding_tx += sp->pckt_count;
while (sp->xsk_info->outstanding_tx != 0) {
complete_tx_only(sp);
}
sp->sent += sp->pckt_count;
#endif
break;
default:
errx(-1, "Unsupported sp->handle_type = %d", sp->handle_type);
} /* end case */
Expand All @@ -465,8 +502,15 @@ sendpacket(sendpacket_t *sp, const u_char *data, size_t len, struct pcap_pkthdr
sendpacket_seterr(sp, "Only able to write %d bytes out of %lu bytes total", retcode, len);
sp->trunc_packets++;
} else {
#ifndef HAVE_LIBXDP
sp->bytes_sent += len;
sp->sent++;
#else
if (sp->handle_type != SP_TYPE_LIBXDP) {
sp->bytes_sent += len;
sp->sent++;
}
#endif
}
return retcode;
}
Expand Down Expand Up @@ -539,6 +583,8 @@ sendpacket_open(const char *device,
sp = sendpacket_open_libdnet(device, errbuf);
#elif (defined HAVE_PCAP_INJECT || defined HAVE_PCAP_SENDPACKET)
sp = sendpacket_open_pcap(device, errbuf);
#elif defined HAVE_LIBXDP
sp = sendpacket_open_xsk(device, errbuf);
#else
#error "No defined packet injection method for sendpacket_open()"
#endif
Expand Down Expand Up @@ -647,6 +693,11 @@ sendpacket_close(sendpacket_t *sp)
case SP_TYPE_TUNTAP:
#ifdef HAVE_TUNTAP
close(sp->handle.fd);
#endif
break;
case SP_TYPE_LIBXDP:
#if defined HAVE_LIBXDP
close(sp->handle.fd);
#endif
break;
case SP_TYPE_NONE:
Expand Down Expand Up @@ -674,6 +725,8 @@ sendpacket_get_hwaddr(sendpacket_t *sp)
} else {
#if defined HAVE_PF_PACKET
addr = sendpacket_get_hwaddr_pf(sp);
#elif defined HAVE_LIBXDP
addr = sendpacket_get_hwaddr_libxdp(sp);
#elif defined HAVE_BPF
addr = sendpacket_get_hwaddr_bpf(sp);
#elif defined HAVE_LIBDNET
Expand Down Expand Up @@ -1294,3 +1347,156 @@ sendpacket_abort(sendpacket_t *sp)

sp->abort = true;
}
#ifdef HAVE_LIBXDP
static struct xsk_socket_info *
xsk_configure_socket(struct xsk_umem_info *umem, struct xsk_socket_config *cfg, int queue_id, const char *device)
{
struct xsk_socket_info *xsk;
struct xsk_ring_cons *rxr = NULL;
struct xsk_ring_prod *txr;
int ret;
xsk = (struct xsk_socket_info *)safe_malloc(sizeof(struct xsk_socket_info));
xsk->umem = umem;
ret = xsk_socket__create(&xsk->xsk, device, queue_id, umem->umem, rxr, &xsk->tx, cfg);
if (ret) {
return NULL;
}

memset(&xsk->app_stats, 0, sizeof(xsk->app_stats));

return xsk;
}

static sendpacket_t *
sendpacket_open_xsk(const char *device, char *errbuf)
{
sendpacket_t *sp;

assert(device);
assert(errbuf);

int nb_of_frames = 4096;
int frame_size = 4096;
int nb_of_completion_queue_desc = 4096;
int nb_of_fill_queue_desc = 4096;
struct xsk_umem_info *umem_info =
create_umem_area(nb_of_frames, frame_size, nb_of_completion_queue_desc, nb_of_fill_queue_desc);
if (umem_info == NULL) {
return NULL;
}

int nb_of_tx_queue_desc = 4096;
int nb_of_rx_queue_desc = 4096;
u_int32_t queue_id = 0;
struct xsk_socket_info *xsk_info =
create_xsk_socket(umem_info, nb_of_tx_queue_desc, nb_of_rx_queue_desc, device, queue_id, errbuf);
if (xsk_info == NULL) {
return NULL;
}

sp = (sendpacket_t *)safe_malloc(sizeof(sendpacket_t));
strlcpy(sp->device, device, sizeof(sp->device));
sp->handle.fd = xsk_info->xsk->fd;
sp->handle_type = SP_TYPE_LIBXDP;
sp->xsk_info = xsk_info;
sp->umem_info = umem_info;
sp->frame_size = frame_size;
sp->tx_size = nb_of_tx_queue_desc;
return sp;
}

struct xsk_umem_info *
create_umem_area(int nb_of_frames, int frame_size, int nb_of_completion_queue_descs, int nb_of_fill_queue_descs)
{
int umem_size = nb_of_frames * frame_size;
struct xsk_umem_info *umem;
void *umem_area = NULL;
struct xsk_umem_config cfg = {/* We recommend that you set the fill ring size >= HW RX ring size +
* AF_XDP RX ring size. Make sure you fill up the fill ring
* with buffers at regular intervals, and you will with this setting
* avoid allocation failures in the driver. These are usually quite
* expensive since drivers have not been written to assume that
* allocation failures are common. For regular sockets, kernel
* allocated memory is used that only runs out in OOM situations
* that should be rare.
*/
.fill_size = nb_of_fill_queue_descs * 2,
.comp_size = nb_of_completion_queue_descs,
.frame_size = frame_size,
.frame_headroom = 0,
.flags = XDP_UMEM_UNALIGNED_CHUNK_FLAG};
umem = (struct xsk_umem_info *)safe_malloc(sizeof(struct xsk_umem_info));
if (posix_memalign(&umem_area,
getpagesize(), /* PAGE_SIZE aligned */
umem_size)) {
fprintf(stderr, "ERROR: Can't allocate buffer memory \"%s\"\n", strerror(errno));
exit(EXIT_FAILURE);
}
int ret = xsk_umem__create(&umem->umem, umem_area, umem_size, &umem->fq, &umem->cq, &cfg);
umem->buffer = umem_area;
if (ret != 0) {
return NULL;
}
return umem;
}

static struct xsk_socket_info *
create_xsk_socket(struct xsk_umem_info *umem_info,
int nb_of_tx_queue_desc,
int nb_of_rx_queue_desc,
const char *device,
u_int32_t queue_id,
char *errbuf)
{
struct xsk_socket_info *xsk_info = (struct xsk_socket_info *)safe_malloc(sizeof(struct xsk_socket_info));
struct xsk_socket_config *socket_config = (struct xsk_socket_config *)safe_malloc(sizeof(struct xsk_socket_config));

socket_config->rx_size = nb_of_rx_queue_desc;
socket_config->tx_size = nb_of_tx_queue_desc;
socket_config->libbpf_flags = XSK_LIBBPF_FLAGS__INHIBIT_PROG_LOAD;
socket_config->bind_flags = 0; // XDP_FLAGS_SKB_MODE (1U << 1) or XDP_FLAGS_DRV_MODE (1U << 2)
xsk_info = xsk_configure_socket(umem_info, socket_config, queue_id, device);

if (xsk_info == NULL) {
snprintf(errbuf, SENDPACKET_ERRBUF_SIZE, "AF_XDP socket configuration is not successful: %s", strerror(errno));
return NULL;
}
return xsk_info;
}

/**
* gets the hardware address via Linux's PF packet interface
*/
static struct tcpr_ether_addr *
sendpacket_get_hwaddr_libxdp(sendpacket_t *sp)
{
struct ifreq ifr;
int fd;

assert(sp);

if (!sp->open) {
sendpacket_seterr(sp, "Unable to get hardware address on un-opened sendpacket handle");
return NULL;
}

/* create dummy socket for ioctl */
if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
sendpacket_seterr(sp, "Unable to open dummy socket for get_hwaddr: %s", strerror(errno));
return NULL;
}

memset(&ifr, 0, sizeof(ifr));
strlcpy(ifr.ifr_name, sp->device, sizeof(ifr.ifr_name));

if (ioctl(fd, SIOCGIFHWADDR, (int8_t *)&ifr) < 0) {
close(fd);
sendpacket_seterr(sp, "Error getting hardware address: %s", strerror(errno));
return NULL;
}

memcpy(&sp->ether, &ifr.ifr_hwaddr.sa_data, ETHER_ADDR_LEN);
close(fd);
return (&sp->ether);
}
#endif /* HAVE_LIBXDP */
Loading

0 comments on commit 0f012c8

Please sign in to comment.