Files
strongswan-ext/src/libhydra/plugins/kernel_netlink/suites/test_socket.c
T
Martin Willi 6c58fabe29 kernel-netlink: Add options to enable parallel Netlink queries explicitly
As under vanilla Linux the kernel can't handle parallel dump queries and returns
EBUSY, it makes not much sense to use them. Disable parallel queries by default
to basically restore original behavior, improving performance.
2014-11-21 10:55:45 +01:00

303 lines
6.5 KiB
C

/*
* Copyright (C) 2014 Martin Willi
* Copyright (C) 2014 revosec AG
*
* 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 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* 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.
*/
#include <test_suite.h>
#include <threading/thread.h>
#include "../kernel_netlink_shared.h"
/**
* Netlink message drop configuration
*/
static int drop_interval = 0;
/**
* Netlink message drop hook
*/
bool netlink_msg_loss(struct nlmsghdr *hdr)
{
static refcount_t i;
if (drop_interval)
{
return ref_get(&i) % drop_interval == drop_interval - 1;
}
return FALSE;
}
START_TEST(test_echo)
{
netlink_socket_t *s;
struct nlmsghdr *out;
struct rtmsg *msg;
char dst[] = {
127,0,0,1
};
size_t len;
netlink_buf_t request = {
.hdr = {
.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)),
.nlmsg_flags = NLM_F_REQUEST,
.nlmsg_type = RTM_GETROUTE,
},
};
msg = NLMSG_DATA(&request.hdr);
msg->rtm_family = AF_INET;
netlink_add_attribute(&request.hdr, RTA_DST,
chunk_from_thing(dst), sizeof(request));
s = netlink_socket_create(NETLINK_ROUTE, NULL, _i != 0);
ck_assert(s->send(s, &request.hdr, &out, &len) == SUCCESS);
ck_assert_int_eq(out->nlmsg_type, RTM_NEWROUTE);
free(out);
s->destroy(s);
}
END_TEST
START_TEST(test_echo_dump)
{
netlink_socket_t *s;
struct nlmsghdr *out, *current;
struct rtgenmsg *msg;
size_t len;
netlink_buf_t request = {
.hdr = {
.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg)),
.nlmsg_flags = NLM_F_REQUEST | NLM_F_MATCH | NLM_F_ROOT,
.nlmsg_type = RTM_GETLINK,
},
};
s = netlink_socket_create(NETLINK_ROUTE, NULL, _i != 0);
msg = NLMSG_DATA(&request.hdr);
msg->rtgen_family = AF_UNSPEC;
ck_assert(s->send(s, &request.hdr, &out, &len) == SUCCESS);
current = out;
while (TRUE)
{
ck_assert(NLMSG_OK(current, len));
if (current->nlmsg_type == NLMSG_DONE)
{
break;
}
ck_assert_int_eq(current->nlmsg_type, RTM_NEWLINK);
current = NLMSG_NEXT(current, len);
}
free(out);
s->destroy(s);
}
END_TEST
CALLBACK(stress, void*,
netlink_socket_t *s)
{
struct nlmsghdr *out;
struct rtmsg *msg;
char dst[] = {
127,0,0,1
};
size_t len;
int i;
netlink_buf_t request = {
.hdr = {
.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)),
.nlmsg_flags = NLM_F_REQUEST,
.nlmsg_type = RTM_GETROUTE,
},
};
for (i = 0; i < 10; i++)
{
msg = NLMSG_DATA(&request.hdr);
msg->rtm_family = AF_INET;
netlink_add_attribute(&request.hdr, RTA_DST,
chunk_from_thing(dst), sizeof(request));
ck_assert(s->send(s, &request.hdr, &out, &len) == SUCCESS);
ck_assert_int_eq(out->nlmsg_type, RTM_NEWROUTE);
free(out);
}
return NULL;
}
CALLBACK(stress_dump, void*,
netlink_socket_t *s)
{
struct nlmsghdr *out, *current;
struct rtgenmsg *msg;
size_t len;
int i;
netlink_buf_t request = {
.hdr = {
.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg)),
.nlmsg_flags = NLM_F_REQUEST | NLM_F_MATCH | NLM_F_ROOT,
.nlmsg_type = RTM_GETLINK,
},
};
msg = NLMSG_DATA(&request.hdr);
msg->rtgen_family = AF_UNSPEC;
for (i = 0; i < 10; i++)
{
ck_assert(s->send(s, &request.hdr, &out, &len) == SUCCESS);
current = out;
while (TRUE)
{
ck_assert(NLMSG_OK(current, len));
if (current->nlmsg_type == NLMSG_DONE)
{
break;
}
ck_assert_int_eq(current->nlmsg_type, RTM_NEWLINK);
current = NLMSG_NEXT(current, len);
}
free(out);
}
return NULL;
}
START_TEST(test_stress)
{
thread_t *threads[10];
netlink_socket_t *s;
int i;
s = netlink_socket_create(NETLINK_ROUTE, NULL, _i != 0);
for (i = 0; i < countof(threads); i++)
{
threads[i] = thread_create(stress, s);
}
for (i = 0; i < countof(threads); i++)
{
threads[i]->join(threads[i]);
}
s->destroy(s);
}
END_TEST
START_TEST(test_stress_dump)
{
thread_t *threads[10];
netlink_socket_t *s;
int i;
s = netlink_socket_create(NETLINK_ROUTE, NULL, _i != 0);
for (i = 0; i < countof(threads); i++)
{
threads[i] = thread_create(stress_dump, s);
}
for (i = 0; i < countof(threads); i++)
{
threads[i]->join(threads[i]);
}
s->destroy(s);
}
END_TEST
START_TEST(test_retransmit_success)
{
netlink_socket_t *s;
struct nlmsghdr *out;
struct rtgenmsg *msg;
size_t len;
netlink_buf_t request = {
.hdr = {
.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg)),
.nlmsg_flags = NLM_F_REQUEST | NLM_F_MATCH | NLM_F_ROOT,
.nlmsg_type = RTM_GETLINK,
},
};
drop_interval = 2;
lib->settings->set_int(lib->settings,
"%s.plugins.kernel-netlink.timeout", 100, lib->ns);
lib->settings->set_int(lib->settings,
"%s.plugins.kernel-netlink.retries", 1, lib->ns);
s = netlink_socket_create(NETLINK_ROUTE, NULL, _i != 0);
msg = NLMSG_DATA(&request.hdr);
msg->rtgen_family = AF_UNSPEC;
ck_assert(s->send(s, &request.hdr, &out, &len) == SUCCESS);
free(out);
s->destroy(s);
drop_interval = 0;
}
END_TEST
START_TEST(test_retransmit_fail)
{
netlink_socket_t *s;
struct nlmsghdr *out;
struct rtgenmsg *msg;
size_t len;
netlink_buf_t request = {
.hdr = {
.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg)),
.nlmsg_flags = NLM_F_REQUEST | NLM_F_MATCH | NLM_F_ROOT,
.nlmsg_type = RTM_GETLINK,
},
};
drop_interval = 1;
lib->settings->set_int(lib->settings,
"%s.plugins.kernel-netlink.timeout", 50, lib->ns);
lib->settings->set_int(lib->settings,
"%s.plugins.kernel-netlink.retries", 3, lib->ns);
s = netlink_socket_create(NETLINK_ROUTE, NULL, _i != 0);
msg = NLMSG_DATA(&request.hdr);
msg->rtgen_family = AF_UNSPEC;
ck_assert(s->send(s, &request.hdr, &out, &len) == OUT_OF_RES);
s->destroy(s);
drop_interval = 0;
}
END_TEST
Suite *socket_suite_create()
{
Suite *s;
TCase *tc;
s = suite_create("netlink socket");
tc = tcase_create("echo");
tcase_add_loop_test(tc, test_echo, 0, 2);
tcase_add_loop_test(tc, test_echo_dump, 0, 2);
suite_add_tcase(s, tc);
tc = tcase_create("stress");
tcase_add_loop_test(tc, test_stress, 0, 2);
tcase_add_loop_test(tc, test_stress_dump, 0, 2);
suite_add_tcase(s, tc);
tc = tcase_create("retransmit");
tcase_add_loop_test(tc, test_retransmit_success, 0, 2);
tcase_add_loop_test(tc, test_retransmit_fail, 0, 2);
suite_add_tcase(s, tc);
return s;
}