# #
#############################################################################*/
+#include <arpa/inet.h>
+#include <errno.h>
+#include <getopt.h>
+#include <netinet/in.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
#include "ctx.h"
+#include "logging.h"
+#include "random.h"
+
+static int parse_address(const char* string, struct in6_addr* address6) {
+ struct in_addr address4;
+
+ // Try parsing this address
+ int r = inet_pton(AF_INET6, string, address6);
+
+ // Success!
+ if (r == 1)
+ return 0;
+
+ // Try parsing this as an IPv4 address
+ r = inet_pton(AF_INET, string, &address4);
+ if (r == 1) {
+ // Convert to IPv6-mapped address
+ address6->s6_addr32[0] = htonl(0x0000);
+ address6->s6_addr32[1] = htonl(0x0000);
+ address6->s6_addr32[2] = htonl(0xffff);
+ address6->s6_addr32[3] = address4.s_addr;
+
+ return 0;
+ }
+
+ // Could not parse this
+ return 1;
+}
+
+static int check_port(int port) {
+ if (port <= 0 || port >= 65536) {
+ fprintf(stderr, "Invalid port number: %u\n", port);
+
+ return 2;
+ }
+
+ return 0;
+}
+
+static int parse_port_range(struct fireperf_ctx* ctx, const char* optarg) {
+ int first_port, last_port;
+
+ int r = sscanf(optarg, "%d:%d", &first_port, &last_port);
+ if (r != 2)
+ return 1;
+
+ // Check if both ports are in range
+ r = check_port(first_port);
+ if (r)
+ return r;
+
+ r = check_port(last_port);
+ if (r)
+ return r;
+
+ if (first_port > last_port) {
+ fprintf(stderr, "Invalid port range: %s\n", optarg);
+ return 2;
+ }
+
+ ctx->port = first_port;
+ ctx->listening_sockets = (last_port - first_port) + 1;
+
+ return 0;
+}
+
+static int parse_port(struct fireperf_ctx* ctx, const char* optarg) {
+ ctx->port = atoi(optarg);
+ ctx->listening_sockets = 1;
+
+ return check_port(ctx->port);
+}
+
+static int parse_argv(struct fireperf_ctx* ctx, int argc, char* argv[]) {
+ static struct option long_options[] = {
+ {"client", required_argument, 0, 'c'},
+ {"close", no_argument, 0, 'x'},
+ {"debug", no_argument, 0, 'd'},
+ {"duplex", no_argument, 0, 'D'},
+ {"keepalive", no_argument, 0, 'k'},
+ {"parallel", required_argument, 0, 'P'},
+ {"port", required_argument, 0, 'p'},
+ {"server", no_argument, 0, 's'},
+ {"timeout", required_argument, 0, 't'},
+ {"version", no_argument, 0, 'V'},
+ {"zero", no_argument, 0, 'z'},
+ {0, 0, 0, 0},
+ };
+
+ int option_index = 0;
+ int done = 0;
+
+ while (!done) {
+ int c = getopt_long(argc, argv, "c:dkp:st:xzDP:V", long_options, &option_index);
+
+ // End
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 0:
+ if (long_options[option_index].flag != 0)
+ break;
+
+ printf("option %s", long_options[option_index].name);
+
+ if (optarg)
+ printf(" with arg: %s", optarg);
+
+ printf("\n");
+ break;
+
+ case '?':
+ // getopt_long already printed the error message
+ return 1;
+
+ case 'V':
+ printf("%s %s\n", PACKAGE_NAME, PACKAGE_VERSION);
+ printf("Copyright (C) 2021 The IPFire Project (https://www.ipfire.org/)\n");
+ printf("License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>\n");
+ printf("This is free software: you are free to change and redistribute it\n");
+ printf("There is NO WARRANTY, to the extent permitted by law.\n\n");
+ printf("Written by Michael Tremer\n");
+
+ exit(0);
+ break;
+
+ case 'c':
+ ctx->mode = FIREPERF_MODE_CLIENT;
+
+ // Parse the given IP address
+ int r = parse_address(optarg, &ctx->address);
+ if (r) {
+ fprintf(stderr, "Could not parse IP address %s\n", optarg);
+ return 2;
+ }
+ break;
+
+ case 'D':
+ ctx->duplex = 1;
+ break;
+
+ case 'd':
+ ctx->loglevel = LOG_DEBUG;
+ break;
+
+ case 'k':
+ ctx->keepalive_only = 1;
+ break;
+
+ case 'P':
+ ctx->parallel = strtoul(optarg, NULL, 10);
+
+ if (ctx->parallel > MAX_PARALLEL) {
+ fprintf(stderr, "Number of parallel connections is too high: %lu\n",
+ ctx->parallel);
+ return 2;
+ }
+ break;
+
+ case 'p':
+ // Try parsing the port range first.
+ // If this fails, we try parsing a single port
+ r = parse_port_range(ctx, optarg);
+ if (r == 1)
+ r = parse_port(ctx, optarg);
+ if (r)
+ return r;
+
+ break;
+
+ case 's':
+ ctx->mode = FIREPERF_MODE_SERVER;
+ break;
+
+ case 't':
+ ctx->timeout = strtoul(optarg, NULL, 10);
+ break;
+
+ case 'x':
+ ctx->close = 1;
+ break;
+
+ case 'z':
+ ctx->zero = 1;
+ break;
+
+ default:
+ done = 1;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+int fireperf_ctx_create(struct fireperf_ctx** ctx, int argc, char* argv[]) {
+ struct fireperf_ctx* c = NULL;
+ int r;
+
+ // Allocate a new context
+ c = calloc(1, sizeof(*c));
+ if (!c)
+ return -errno;
+
+ // Configure keepalive
+ c->keepalive_count = DEFAULT_KEEPALIVE_COUNT;
+ c->keepalive_interval = DEFAULT_KEEPALIVE_INTERVAL;
+
+ // Configure listening sockets
+ c->listening_sockets = DEFAULT_LISTENING_SOCKETS;
+
+ // Configure logging
+ c->loglevel = DEFAULT_LOG_LEVEL;
+
+ // Configure mode
+ c->mode = FIREPERF_MODE_NONE;
+
+ // Configure port
+ c->port = DEFAULT_PORT;
+
+ // Configure parallelism
+ c->parallel = DEFAULT_PARALLEL;
+
+ // Fetch how many workers we would launch
+ c->max_workers = sysconf(_SC_NPROCESSORS_ONLN);
+
+ // Parse the command line
+ r = parse_argv(c, argc, argv);
+ if (r)
+ goto ERROR;
+
+ // Initialize random pool
+ if (!c->zero) {
+ c->pool = fireperf_random_pool_create(c, DEFAULT_RANDOM_POOL_SIZE);
+ if (!c->pool) {
+ ERROR(c, "Could not allocate random data\n");
+ r = 1;
+ goto ERROR;
+ }
+ }
+
+ // Return the context
+ *ctx = c;
+
+ return 0;
+
+ERROR:
+ if (c)
+ fireperf_ctx_free(c);
+
+ return r;
+}
+
+void fireperf_ctx_free(struct fireperf_ctx* ctx) {
+ if (ctx->pool)
+ fireperf_random_pool_free(ctx->pool);
+
+ free(ctx);
+}
# #
#############################################################################*/
-#include <arpa/inet.h>
#include <errno.h>
-#include <getopt.h>
-#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "server.h"
#include "util.h"
-static int parse_address(const char* string, struct in6_addr* address6) {
- // Try parsing this address
- int r = inet_pton(AF_INET6, string, address6);
-
- // Success!
- if (r == 1)
- return 0;
-
- // Try parsing this as an IPv4 address
- struct in_addr address4;
- r = inet_pton(AF_INET, string, &address4);
- if (r == 1) {
- // Convert to IPv6-mapped address
- address6->s6_addr32[0] = htonl(0x0000);
- address6->s6_addr32[1] = htonl(0x0000);
- address6->s6_addr32[2] = htonl(0xffff);
- address6->s6_addr32[3] = address4.s_addr;
-
- return 0;
- }
-
- // Could not parse this
- return 1;
-}
-
-static int check_port(int port) {
- if (port <= 0 || port >= 65536) {
- fprintf(stderr, "Invalid port number: %u\n", port);
- return 2;
- }
-
- return 0;
-}
-
-static int parse_port_range(struct fireperf_ctx* ctx, const char* optarg) {
- int first_port, last_port;
-
- int r = sscanf(optarg, "%d:%d", &first_port, &last_port);
- if (r != 2)
- return 1;
-
- // Check if both ports are in range
- r = check_port(first_port);
- if (r)
- return r;
-
- r = check_port(last_port);
- if (r)
- return r;
-
- if (first_port > last_port) {
- fprintf(stderr, "Invalid port range: %s\n", optarg);
- return 2;
- }
-
- ctx->port = first_port;
- ctx->listening_sockets = (last_port - first_port) + 1;
-
- return 0;
-}
-
-static int parse_port(struct fireperf_ctx* ctx, const char* optarg) {
- ctx->port = atoi(optarg);
- ctx->listening_sockets = 1;
-
- return check_port(ctx->port);
-}
-
static int set_limits(struct fireperf_ctx* ctx) {
struct rlimit limit;
return 0;
}
-static int parse_argv(int argc, char* argv[], struct fireperf_ctx* ctx) {
- static struct option long_options[] = {
- {"client", required_argument, 0, 'c'},
- {"close", no_argument, 0, 'x'},
- {"debug", no_argument, 0, 'd'},
- {"duplex", no_argument, 0, 'D'},
- {"keepalive", no_argument, 0, 'k'},
- {"parallel", required_argument, 0, 'P'},
- {"port", required_argument, 0, 'p'},
- {"server", no_argument, 0, 's'},
- {"timeout", required_argument, 0, 't'},
- {"version", no_argument, 0, 'V'},
- {"zero", no_argument, 0, 'z'},
- {0, 0, 0, 0},
- };
-
- int option_index = 0;
- int done = 0;
-
- while (!done) {
- int c = getopt_long(argc, argv, "c:dkp:st:xzDP:V", long_options, &option_index);
-
- // End
- if (c == -1)
- break;
-
- switch (c) {
- case 0:
- if (long_options[option_index].flag != 0)
- break;
-
- printf("option %s", long_options[option_index].name);
-
- if (optarg)
- printf(" with arg: %s", optarg);
-
- printf("\n");
- break;
-
- case '?':
- // getopt_long already printed the error message
- return 1;
-
- case 'V':
- printf("%s %s\n", PACKAGE_NAME, PACKAGE_VERSION);
- printf("Copyright (C) 2021 The IPFire Project (https://www.ipfire.org/)\n");
- printf("License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>\n");
- printf("This is free software: you are free to change and redistribute it\n");
- printf("There is NO WARRANTY, to the extent permitted by law.\n\n");
- printf("Written by Michael Tremer\n");
-
- exit(0);
- break;
-
- case 'c':
- ctx->mode = FIREPERF_MODE_CLIENT;
-
- // Parse the given IP address
- int r = parse_address(optarg, &ctx->address);
- if (r) {
- fprintf(stderr, "Could not parse IP address %s\n", optarg);
- return 2;
- }
- break;
-
- case 'D':
- ctx->duplex = 1;
- break;
-
- case 'd':
- ctx->loglevel = LOG_DEBUG;
- break;
-
- case 'k':
- ctx->keepalive_only = 1;
- break;
-
- case 'P':
- ctx->parallel = strtoul(optarg, NULL, 10);
-
- if (ctx->parallel > MAX_PARALLEL) {
- fprintf(stderr, "Number of parallel connections is too high: %lu\n",
- ctx->parallel);
- return 2;
- }
- break;
-
- case 'p':
- // Try parsing the port range first.
- // If this fails, we try parsing a single port
- r = parse_port_range(ctx, optarg);
- if (r == 1)
- r = parse_port(ctx, optarg);
- if (r)
- return r;
-
- break;
-
- case 's':
- ctx->mode = FIREPERF_MODE_SERVER;
- break;
-
- case 't':
- ctx->timeout = strtoul(optarg, NULL, 10);
- break;
-
- case 'x':
- ctx->close = 1;
- break;
-
- case 'z':
- ctx->zero = 1;
- break;
-
- default:
- done = 1;
- break;
- }
- }
-
- return 0;
-}
-
int main(int argc, char* argv[]) {
- struct fireperf_ctx ctx = {
- .keepalive_count = DEFAULT_KEEPALIVE_COUNT,
- .keepalive_interval = DEFAULT_KEEPALIVE_INTERVAL,
- .listening_sockets = DEFAULT_LISTENING_SOCKETS,
- .loglevel = DEFAULT_LOG_LEVEL,
- .mode = FIREPERF_MODE_NONE,
- .port = DEFAULT_PORT,
- .parallel = DEFAULT_PARALLEL,
- };
+ struct fireperf_ctx* ctx = NULL;
struct fireperf_stats stats = { 0 };
int r;
int epollfd = -1;
int timerfd = -1;
- // Parse command line
- r = parse_argv(argc, argv, &ctx);
+ // Create a new context
+ r = fireperf_ctx_create(&ctx, argc, argv);
if (r)
return r;
- // Initialise random number generator
- srandom(time(NULL));
-
- // Fetch how many workers we would launch
- ctx.max_workers = sysconf(_SC_NPROCESSORS_ONLN);
-
// Set limits
- r = set_limits(&ctx);
+ r = set_limits(ctx);
if (r)
return r;
- // Initialize random pool
- if (!ctx.zero) {
- ctx.pool = fireperf_random_pool_create(&ctx, DEFAULT_RANDOM_POOL_SIZE);
- if (!ctx.pool) {
- ERROR(&ctx, "Could not allocate random data\n");
- r = 1;
- goto ERROR;
- }
- }
-
// Initialize epoll()
epollfd = epoll_create1(0);
if (epollfd < 0) {
- ERROR(&ctx, "Could not initialize epoll(): %s\n", strerror(errno));
+ ERROR(ctx, "Could not initialize epoll(): %s\n", strerror(errno));
r = 1;
goto ERROR;
}
// Create timerfd() to print statistics
timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK|TFD_CLOEXEC);
if (timerfd < 0) {
- ERROR(&ctx, "timerfd_create() failed: %s\n", strerror(errno));
+ ERROR(ctx, "timerfd_create() failed: %s\n", strerror(errno));
r = 1;
goto ERROR;
}
};
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, timerfd, &ev)) {
- ERROR(&ctx, "Could not add timerfd to epoll(): %s\n", strerror(errno));
+ ERROR(ctx, "Could not add timerfd to epoll(): %s\n", strerror(errno));
r = 1;
goto ERROR;
}
r = timerfd_settime(timerfd, 0, &timer, NULL);
if (r) {
- ERROR(&ctx, "Could not set timer: %s\n", strerror(errno));
+ ERROR(ctx, "Could not set timer: %s\n", strerror(errno));
r = 1;
goto ERROR;
}
- switch (ctx.mode) {
+ switch (ctx->mode) {
case FIREPERF_MODE_CLIENT:
- return fireperf_client(&ctx, &stats, epollfd, timerfd);
+ return fireperf_client(ctx, &stats, epollfd, timerfd);
case FIREPERF_MODE_SERVER:
- return fireperf_server(&ctx, epollfd, timerfd);
+ return fireperf_server(ctx, epollfd, timerfd);
case FIREPERF_MODE_NONE:
fprintf(stderr, "No mode selected\n");
if (timerfd > 0)
close(timerfd);
- if (ctx.pool)
- fireperf_random_pool_free(ctx.pool);
+ if (ctx)
+ fireperf_ctx_free(ctx);
return r;
}