Skip to content
Snippets Groups Projects
Commit b95fd4ea authored by Daniel Salzman's avatar Daniel Salzman
Browse files

Merge branch 'xdp_gun_popen_fix' into 'master'

Xdp gun popen fix

See merge request !1149
parents b8f48e97 17957dde
No related branches found
No related tags found
1 merge request!1149Xdp gun popen fix
Pipeline #65803 failed
......@@ -469,6 +469,11 @@ src/utils/khost/khost_main.c
src/utils/khost/khost_params.c
src/utils/khost/khost_params.h
src/utils/kjournalprint/main.c
src/utils/knot-xdp-gun/load_queries.c
src/utils/knot-xdp-gun/load_queries.h
src/utils/knot-xdp-gun/main.c
src/utils/knot-xdp-gun/popenve.c
src/utils/knot-xdp-gun/popenve.h
src/utils/knotc/commands.c
src/utils/knotc/commands.h
src/utils/knotc/interactive.c
......@@ -486,9 +491,6 @@ src/utils/knsupdate/knsupdate_params.h
src/utils/kzonecheck/main.c
src/utils/kzonecheck/zone_check.c
src/utils/kzonecheck/zone_check.h
src/utils/knot-xdp-gun/load_queries.c
src/utils/knot-xdp-gun/load_queries.h
src/utils/knot-xdp-gun/main.c
tests-fuzz/fuzz_dname_from_str.c
tests-fuzz/fuzz_dname_to_str.c
tests-fuzz/fuzz_packet.c
......
......@@ -88,10 +88,12 @@ sbin_PROGRAMS += knot-xdp-gun
knot_xdp_gun_SOURCES = \
utils/knot-xdp-gun/load_queries.c \
utils/knot-xdp-gun/load_queries.h \
utils/knot-xdp-gun/main.c
utils/knot-xdp-gun/main.c \
utils/knot-xdp-gun/popenve.c \
utils/knot-xdp-gun/popenve.h
knot_xdp_gun_CPPFLAGS = $(AM_CPPFLAGS)
knot_xdp_gun_LDADD = libcontrib.la libknot.la $(pthread_LIBS)
knot_xdp_gun_LDADD = libcontrib.la libknot.la $(pthread_LIBS) $(cap_ng_LIBS)
endif ENABLE_XDP
endif HAVE_UTILS
......
......@@ -38,8 +38,8 @@
#include "libknot/libknot.h"
#include "contrib/openbsd/strlcpy.h"
#include "utils/common/params.h"
#include "load_queries.h"
#include "utils/knot-xdp-gun/load_queries.h"
#include "utils/knot-xdp-gun/popenve.h"
#define PROGRAM_NAME "knot-xdp-gun"
......@@ -53,6 +53,8 @@ uint64_t global_size_recv = 0;
#define LOCAL_PORT_MIN 1024
#define LOCAL_PORT_MAX 65535
#define KNOWN_RCODE_MAX (KNOT_RCODE_NOTZONE + 1)
typedef struct {
char dev[IFNAMSIZ];
uint64_t qps, duration;
......@@ -64,6 +66,7 @@ typedef struct {
uint16_t target_port;
uint32_t listen_port; // KNOT_XDP_LISTEN_PORT_ALL, KNOT_XDP_LISTEN_PORT_DROP
unsigned n_threads, thread_id;
uint64_t rcode_counts[KNOWN_RCODE_MAX];
} xdp_gun_ctx_t;
const static xdp_gun_ctx_t ctx_defaults = {
......@@ -240,6 +243,11 @@ void *xdp_gun_thread(void *_ctx)
}
for (int i = 0; i < recvd; i++) {
tot_size += pkts[i].payload.iov_len;
if (pkts[i].payload.iov_len < KNOT_WIRE_HEADER_SIZE) {
ctx->rcode_counts[KNOWN_RCODE_MAX - 1]++;
} else {
ctx->rcode_counts[((uint8_t *)pkts[i].payload.iov_base)[3] & 0xf]++;
}
}
knot_xdp_recv_finish(xsk, pkts, recvd);
tot_recv += recvd;
......@@ -342,34 +350,41 @@ static bool str2mac(const char *str, uint8_t mac[])
return true;
}
static int ip_route_get(const char *ip_str, const char *what, char **res)
static FILE *popen_ip(char *arg1, char *arg2, char *arg3)
{
char cmd[50 + strlen(ip_str) + strlen(what)];
(void)snprintf(cmd, sizeof(cmd), "ip route get %s | grep -o ' %s [^ ]* '", ip_str, what);
char *args[5] = { "ip", arg1, arg2, arg3, NULL };
char *env[] = { NULL };
return kpopenve("/sbin/ip", args, env, true);
}
static int ip_route_get(const char *ip_str, const char *what, char **res)
{
errno = 0;
FILE *p = popen(cmd, "r");
FILE *p = popen_ip("route", "get", (char *)ip_str); // hope ip_str gets not broken
if (p == NULL) {
return (errno != 0) ? knot_map_errno() : KNOT_ENOMEM;
}
char check[16] = { 0 }, got[256] = { 0 };
if (fscanf(p, "%15s%255s", check, got) != 2 ||
strcmp(check, what) != 0) {
int ret = feof(p) ? KNOT_ENOENT : KNOT_EMALF;
pclose(p);
return ret;
char buf[256] = { 0 };
bool hit = false;
while (fscanf(p, "%255s", buf) == 1) {
if (hit) {
*res = strdup(buf);
fclose(p);
return *res == NULL ? KNOT_ENOMEM : KNOT_EOK;
}
if (strcmp(buf, what) == 0) {
hit = true;
}
}
pclose(p);
*res = strdup(got);
return *res == NULL ? KNOT_ENOMEM : KNOT_EOK;
fclose(p);
return KNOT_ENOENT;
}
static int remoteIP2MAC(const char *ip_str, bool ipv6, char devname[], uint8_t remote_mac[])
{
errno = 0;
FILE *p = popen(ipv6 ? "ip -6 neigh" : "arp -ne", "r");
FILE *p = popen_ip(ipv6 ? "-6" : "-4", "neigh", NULL);
if (p == NULL) {
return (errno != 0) ? knot_map_errno() : KNOT_ENOMEM;
}
......@@ -384,14 +399,14 @@ static int remoteIP2MAC(const char *ip_str, bool ipv6, char devname[], uint8_t r
if (strcmp(fields[0], ip_str) != 0) {
continue;
}
if (!str2mac(fields[ipv6 ? 4 : 2], remote_mac)) {
if (!str2mac(fields[4], remote_mac)) {
ret = KNOT_EMALF;
} else {
strlcpy(devname, fields[ipv6 ? 2 : 4], IFNAMSIZ);
strlcpy(devname, fields[2], IFNAMSIZ);
ret = KNOT_EOK;
}
}
pclose(p);
fclose(p);
return ret;
}
......@@ -644,6 +659,17 @@ int main(int argc, char *argv[])
printf("total replies: %lu (%lu qps) (%lu%%)\n", global_pkts_recv,
global_pkts_recv * 1000 / (ctx.duration / 1000), global_pkts_recv * 100 / global_pkts_sent);
printf("average reply size: %lu B\n", global_pkts_recv > 0 ? global_size_recv / global_pkts_recv : 0);
for (int i = 0; i < KNOWN_RCODE_MAX; i++) {
uint64_t rcode_count = 0;
for (size_t j = 0; j < ctx.n_threads; j++) {
rcode_count += thread_ctxs[j].rcode_counts[i];
}
if (rcode_count > 0) {
const knot_lookup_t *rcode = knot_lookup_by_id(knot_rcode_names, i);
const char *rcname = rcode == NULL ? "unknown" : rcode->name;
printf("responded %s:\t%lu\n", rcname, rcode_count);
}
}
}
free(thread_ctxs);
......
/* Copyright (C) 2020 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include "utils/knot-xdp-gun/popenve.h"
#ifdef ENABLE_CAP_NG
#include <cap-ng.h>
static void drop_capabilities(void)
{
/* Drop all capabilities. */
if (capng_have_capability(CAPNG_EFFECTIVE, CAP_SETPCAP)) {
capng_clear(CAPNG_SELECT_BOTH);
capng_apply(CAPNG_SELECT_BOTH);
}
}
#else /* ENABLE_CAP_NG */
static void drop_capabilities(void) { }
#endif
int kpopenvef(const char *binfile, char *const args[], char *const env[], bool drop_cap)
{
int pipefds[2];
if (pipe(pipefds) < 0) {
return -errno;
}
if (fcntl(pipefds[0], F_SETFD, FD_CLOEXEC) < 0) {
int fcntlerrno = errno;
close(pipefds[0]);
close(pipefds[1]);
return -fcntlerrno;
}
pid_t forkpid = fork();
if (forkpid < 0) {
int forkerrno = errno;
close(pipefds[0]);
close(pipefds[1]);
return -forkerrno;
}
if (forkpid == 0) {
dup_stdout:
if (dup2(pipefds[1], STDOUT_FILENO) < 0) {
if (errno == EINTR) {
goto dup_stdout;
}
perror("dup_stdout");
close(pipefds[0]);
close(pipefds[1]);
exit(EXIT_FAILURE);
}
close(pipefds[1]);
if (drop_cap) {
drop_capabilities();
}
execve(binfile, args, env);
perror("execve");
exit(EXIT_FAILURE);
}
close(pipefds[1]);
return pipefds[0];
}
FILE *kpopenve(const char *binfile, char *const args[], char *const env[], bool drop_cap)
{
int p = kpopenvef(binfile, args, env, drop_cap);
if (p < 0) {
errno = -p;
return NULL;
}
FILE *res = fdopen(p, "r");
if (res == NULL) {
int fdoerrno = errno;
close(p);
errno = fdoerrno;
}
return res;
}
/* Copyright (C) 2020 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <stdbool.h>
#include <stdio.h>
/*!
* \brief Hybrid of popen() and execve() returning a file descriptor
*
* This function is a safer altervative to popen(), it is the same to
* popen() as execve() is to system().
*
* Warning: this function is designed to be as simple as possible,
* for reliable operation proper checking for transient
* error is needed.
*
* \param binfile Executable file to be executed.
* \param args NULL-terminated arguments; first shall be the prog name!
* \param env NULL-terminated environment variables "key=value"
* \param drop_cap Drop capabilities for the subprocess.
*
* \retval < 0 Error occured, set to -errno.
* \return > 0 File descriptor of the pipe reading end.
*/
int kpopenvef(const char *binfile, char *const args[], char *const env[], bool drop_cap);
/*!
* \brief Variant of kpopenvef() returning FILE*
*
* Warning: the same warning as for kpopenvef() applies here too.
*
* \param binfile Executable file to be executed.
* \param args NULL-terminated arguments; first shall be the prog name!
* \param env NULL-terminated environment variables "key=value"
* \param drop_cap Drop capabilities for the subprocess.
*
* \retval NULL Error occured, see errno.
* \return Pointer to open file descriptor.
*/
FILE *kpopenve(const char *binfile, char *const args[], char *const env[], bool drop_cap);
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment