blob: 063b15e709ae3e435c542dfd94a551f82fbef0c9 [file] [log] [blame]
/* Route lookup benchmark tool using RTM_GETROUTE */
#include <stdio.h>
#include <stddef.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define _GNU_SOURCE
#include <getopt.h>
#include <libmnl/libmnl.h>
#include <linux/if.h>
#include <linux/if_link.h>
#include <linux/rtnetlink.h>
#include <linux/filter.h>
static int usage(void)
{
printf("usage: route_bench [ -o ] [ -l count ]\n");
printf("\t\t[ -s src_ip ] [ -a src_ip_stride ] [ -b src_ip_limit ]\n");
printf("\t\t[ -d dst_ip ] [ -e dst_ip_stride ] [ -f dst_ip_limit ]\n");
printf("\t\t[ -i iif ] [ -x iif_stride ] [ -y iif_limit ]\n");
printf("\t\t[ -m mark ] [ -n mark_stride ] [ -p mark_limit ]\n");
printf ("\t\t[ -t tos ] [ -q tos_stride ] [ -r tos_limit ]\n");
return -1;
}
struct bench_state {
unsigned int flags;
#define FLAG_SRC_ITERATE 0x00000001
#define FLAG_DST_ITERATE 0x00000002
#define FLAG_MARK_ITERATE 0x00000004
#define FLAG_IIF_ITERATE 0x00000008
#define FLAG_TOS_ITERATE 0x00000010
#define FLAG_ITERATE_ONE 0x00000020
in_addr_t src_addr;
in_addr_t dst_addr;
unsigned int mark;
unsigned int iif;
unsigned int tos;
in_addr_t orig_src_addr;
int src_addr_stride;
in_addr_t src_addr_limit;
in_addr_t orig_dst_addr;
int dst_addr_stride;
in_addr_t dst_addr_limit;
unsigned int orig_mark;
int mark_stride;
unsigned int mark_limit;
unsigned int orig_iif;
int iif_stride;
unsigned int iif_limit;
unsigned int orig_tos;
int tos_stride;
unsigned int tos_limit;
};
static struct bench_state state;
static void init_bench_state(struct bench_state *s)
{
s->src_addr = s->orig_src_addr;
s->dst_addr = s->orig_dst_addr;
s->mark = s->orig_mark;
s->iif = s->orig_iif;
s->tos = s->orig_tos;
}
static void advance_bench_state(struct bench_state *s)
{
if (s->flags & FLAG_SRC_ITERATE) {
in_addr_t orig = s->src_addr;
s->src_addr += s->src_addr_stride;
if (s->src_addr < orig ||
s->src_addr > s->src_addr_limit)
s->src_addr = s->orig_src_addr;
else if (s->flags & FLAG_ITERATE_ONE)
return;
}
if (s->flags & FLAG_DST_ITERATE) {
in_addr_t orig = s->dst_addr;
s->dst_addr += s->dst_addr_stride;
if (s->dst_addr < orig ||
s->dst_addr > s->dst_addr_limit)
s->dst_addr = s->orig_dst_addr;
else if (s->flags & FLAG_ITERATE_ONE)
return;
}
if (s->flags & FLAG_MARK_ITERATE) {
in_addr_t orig = s->mark;
s->mark += s->mark_stride;
if (s->mark < orig ||
s->mark > s->mark_limit)
s->mark = s->orig_mark;
else if (s->flags & FLAG_ITERATE_ONE)
return;
}
if (s->flags & FLAG_IIF_ITERATE) {
in_addr_t orig = s->iif;
s->iif += s->iif_stride;
if (s->iif < orig ||
s->iif > s->iif_limit)
s->iif = s->orig_iif;
else if (s->flags & FLAG_ITERATE_ONE)
return;
}
if (s->flags & FLAG_TOS_ITERATE) {
in_addr_t orig = s->tos;
s->tos += s->tos_stride;
if (s->tos < orig ||
s->tos > s->tos_limit)
s->tos = s->orig_tos;
else if (s->flags & FLAG_ITERATE_ONE)
return;
}
}
static int do_bench(int count, in_addr_t src_addr, in_addr_t dst_addr,
unsigned int mark, unsigned int iif)
{
char send_buf[MNL_SOCKET_BUFFER_SIZE];
unsigned int min, sec, frac, tmp;
struct sock_filter insns = { .code = BPF_RET | BPF_K,
.k = 0 };
struct sock_fprog filter = { .len = 1, .filter = &insns, };
struct bench_state *s = &state;
struct timeval start_time;
struct timeval end_time;
struct mnl_socket *nl;
struct nlmsghdr *nlh;
unsigned int portid;
struct rtmsg *rtm;
int i, err;
init_bench_state(s);
nl = mnl_socket_open(NETLINK_ROUTE);
if (!nl) {
perror("mnl_socket_open");
return -1;
}
if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
perror("mnl_socket_bind");
return -1;
}
portid = mnl_socket_get_portid(nl);
err = setsockopt(mnl_socket_get_fd(nl), SOL_SOCKET,
SO_ATTACH_FILTER, &filter, sizeof(filter));
if (err) {
perror("setsockopt");
return -1;
}
gettimeofday(&start_time, NULL);
for (i = 0; i < count; i++) {
unsigned int seq;
nlh = mnl_nlmsg_put_header(send_buf);
nlh->nlmsg_type = RTM_GETROUTE;
nlh->nlmsg_flags = NLM_F_REQUEST;
nlh->nlmsg_seq = seq = time(NULL);
rtm = mnl_nlmsg_put_extra_header(nlh, sizeof(struct rtmsg));
rtm->rtm_family = AF_INET;
rtm->rtm_tos = s->tos;
if (s->src_addr)
mnl_attr_put_u32(nlh, RTA_SRC, s->src_addr);
if (s->dst_addr)
mnl_attr_put_u32(nlh, RTA_DST, s->dst_addr);
if (s->iif)
mnl_attr_put_u32(nlh, RTA_IIF, s->iif);
#define RTA_MARK (RTA_TABLE + 1)
if (s->mark)
mnl_attr_put_u32(nlh, RTA_MARK, s->mark);
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
perror("mnl_socket_sendto");
return -1;
}
/* No need to do a receive, as the socket filter rejects
* all packets.
*/
advance_bench_state(s);
}
gettimeofday(&end_time, NULL);
sec = end_time.tv_sec - start_time.tv_sec;
frac = end_time.tv_usec - start_time.tv_usec;
tmp = frac % 1000;
frac = (frac * 1000) / 1000000;
if (tmp >= 500)
frac++;
if (frac >= 1000) {
sec++;
frac -= 1000;
}
min = sec / 60;
sec = sec % 60;
printf("Result: %um%u.%03us\n", min, sec, frac);
mnl_socket_close(nl);
return 0;
}
int main(int argc, char **argv, char **envp)
{
struct bench_state *s = &state;
int count, ret;
count = 1000;
s->flags = 0;
s->orig_mark = s->orig_iif = s->orig_tos = 0;
s->orig_src_addr = s->orig_dst_addr = INADDR_ANY;
while ((ret = getopt(argc, argv, "l:s:d:i:m:t:a:b:e:f:x:y:n:p:q:r:oh")) >= 0) {
switch (ret) {
case 'l':
sscanf(optarg, "%d", &count);
break;
case 's':
s->orig_src_addr = inet_addr(optarg);
if (s->orig_src_addr == INADDR_NONE)
return usage();
break;
case 'd':
s->orig_dst_addr = inet_addr(optarg);
if (s->orig_dst_addr == INADDR_NONE)
return usage();
break;
case 'i':
sscanf(optarg, "%u", &s->orig_iif);
break;
case 'm':
sscanf(optarg, "%u", &s->orig_mark);
break;
case 't':
sscanf(optarg, "%u", &s->orig_tos);
break;
case 'o':
s->flags |= FLAG_ITERATE_ONE;
break;
case 'a':
sscanf(optarg, "%u", &s->src_addr_stride);
s->flags |= FLAG_SRC_ITERATE;
break;
case 'b':
s->src_addr_limit = inet_addr(optarg);
if (s->src_addr_limit == INADDR_NONE)
return usage();
if (!s->src_addr_stride)
s->src_addr_stride = 1;
s->flags |= FLAG_SRC_ITERATE;
break;
case 'e':
sscanf(optarg, "%u", &s->dst_addr_stride);
s->flags |= FLAG_SRC_ITERATE;
break;
case 'f':
s->dst_addr_limit = inet_addr(optarg);
if (s->dst_addr_limit == INADDR_NONE)
return usage();
if (!s->dst_addr_stride)
s->dst_addr_stride = 1;
s->flags |= FLAG_DST_ITERATE;
break;
case 'x':
sscanf(optarg, "%u", &s->iif_stride);
s->flags |= FLAG_IIF_ITERATE;
break;
case 'y':
sscanf(optarg, "%u", &s->iif_limit);
if (!s->iif_stride)
s->iif_stride = 1;
s->flags |= FLAG_IIF_ITERATE;
break;
case 'n':
sscanf(optarg, "%u", &s->mark_stride);
s->flags |= FLAG_MARK_ITERATE;
break;
case 'p':
sscanf(optarg, "%u", &s->mark_limit);
if (!s->mark_stride)
s->mark_stride = 1;
s->flags |= FLAG_MARK_ITERATE;
break;
case 'q':
sscanf(optarg, "%u", &s->tos_stride);
s->flags |= FLAG_TOS_ITERATE;
break;
case 'r':
sscanf(optarg, "%u", &s->tos_limit);
if (!s->tos_stride)
s->tos_stride = 1;
s->flags |= FLAG_TOS_ITERATE;
break;
case '?':
case 'h':
return usage();
}
}
printf("Bench: count(%d) saddr[0x%08x] daddr[0x%08x] mark[0x%x] iif[0x%x]\n",
count, s->orig_src_addr, s->orig_dst_addr,
s->orig_mark, s->orig_iif);
return do_bench(count, s->orig_src_addr, s->orig_dst_addr,
s->orig_mark, s->orig_iif);
}