mirror of
git://git.yoctoproject.org/linux-yocto.git
synced 2025-08-22 00:42:01 +02:00

Setup trace points, add a new ftrace instance in order to not interfere with the rest of the system, filtering by net namespace cookies. Raise a new background thread that parses trace_pipe, matches them with the list of expected events. Wiring up trace events to selftests provides another insight if there is anything unexpected happining in the tcp-ao code (i.e. key rotation when it's not expected). Note: in real programs libtraceevent should be used instead of this manual labor of setting ftrace up and parsing. I'm not using it here as I don't want to have an .so library dependency that one would have to bring into VM or DUT (Device Under Test). Please, don't copy it over into any real world programs, that aren't tests. Signed-off-by: Dmitry Safonov <0x7f454c46@gmail.com> Link: https://patch.msgid.link/20240823-tcp-ao-selftests-upd-6-12-v4-8-05623636fe8c@gmail.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
836 lines
25 KiB
C
836 lines
25 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/* Author: Dmitry Safonov <dima@arista.com> */
|
|
#include <inttypes.h>
|
|
#include "../../../../include/linux/kernel.h"
|
|
#include "aolib.h"
|
|
|
|
static union tcp_addr tcp_md5_client;
|
|
|
|
static int test_port = 7788;
|
|
static void make_listen(int sk)
|
|
{
|
|
sockaddr_af addr;
|
|
|
|
tcp_addr_to_sockaddr_in(&addr, &this_ip_addr, htons(test_port++));
|
|
if (bind(sk, (struct sockaddr *)&addr, sizeof(addr)) < 0)
|
|
test_error("bind()");
|
|
if (listen(sk, 1))
|
|
test_error("listen()");
|
|
}
|
|
|
|
static void test_vefify_ao_info(int sk, struct tcp_ao_info_opt *info,
|
|
const char *tst)
|
|
{
|
|
struct tcp_ao_info_opt tmp = {};
|
|
socklen_t len = sizeof(tmp);
|
|
|
|
if (getsockopt(sk, IPPROTO_TCP, TCP_AO_INFO, &tmp, &len))
|
|
test_error("getsockopt(TCP_AO_INFO) failed");
|
|
|
|
#define __cmp_ao(member) \
|
|
do { \
|
|
if (info->member != tmp.member) { \
|
|
test_fail("%s: getsockopt(): " __stringify(member) " %" PRIu64 " != %" PRIu64, \
|
|
tst, (uint64_t)info->member, (uint64_t)tmp.member); \
|
|
return; \
|
|
} \
|
|
} while(0)
|
|
if (info->set_current)
|
|
__cmp_ao(current_key);
|
|
if (info->set_rnext)
|
|
__cmp_ao(rnext);
|
|
if (info->set_counters) {
|
|
__cmp_ao(pkt_good);
|
|
__cmp_ao(pkt_bad);
|
|
__cmp_ao(pkt_key_not_found);
|
|
__cmp_ao(pkt_ao_required);
|
|
__cmp_ao(pkt_dropped_icmp);
|
|
}
|
|
__cmp_ao(ao_required);
|
|
__cmp_ao(accept_icmps);
|
|
|
|
test_ok("AO info get: %s", tst);
|
|
#undef __cmp_ao
|
|
}
|
|
|
|
static void __setsockopt_checked(int sk, int optname, bool get,
|
|
void *optval, socklen_t *len,
|
|
int err, const char *tst, const char *tst2)
|
|
{
|
|
int ret;
|
|
|
|
if (!tst)
|
|
tst = "";
|
|
if (!tst2)
|
|
tst2 = "";
|
|
|
|
errno = 0;
|
|
if (get)
|
|
ret = getsockopt(sk, IPPROTO_TCP, optname, optval, len);
|
|
else
|
|
ret = setsockopt(sk, IPPROTO_TCP, optname, optval, *len);
|
|
if (ret == -1) {
|
|
if (errno == err)
|
|
test_ok("%s%s", tst ?: "", tst2 ?: "");
|
|
else
|
|
test_fail("%s%s: %setsockopt() failed",
|
|
tst, tst2, get ? "g" : "s");
|
|
close(sk);
|
|
return;
|
|
}
|
|
|
|
if (err) {
|
|
test_fail("%s%s: %setsockopt() was expected to fail with %d",
|
|
tst, tst2, get ? "g" : "s", err);
|
|
} else {
|
|
test_ok("%s%s", tst ?: "", tst2 ?: "");
|
|
if (optname == TCP_AO_ADD_KEY) {
|
|
test_verify_socket_key(sk, optval);
|
|
} else if (optname == TCP_AO_INFO && !get) {
|
|
test_vefify_ao_info(sk, optval, tst2);
|
|
} else if (optname == TCP_AO_GET_KEYS) {
|
|
if (*len != sizeof(struct tcp_ao_getsockopt))
|
|
test_fail("%s%s: get keys returned wrong tcp_ao_getsockopt size",
|
|
tst, tst2);
|
|
}
|
|
}
|
|
close(sk);
|
|
}
|
|
|
|
static void setsockopt_checked(int sk, int optname, void *optval,
|
|
int err, const char *tst)
|
|
{
|
|
const char *cmd = NULL;
|
|
socklen_t len;
|
|
|
|
switch (optname) {
|
|
case TCP_AO_ADD_KEY:
|
|
cmd = "key add: ";
|
|
len = sizeof(struct tcp_ao_add);
|
|
break;
|
|
case TCP_AO_DEL_KEY:
|
|
cmd = "key del: ";
|
|
len = sizeof(struct tcp_ao_del);
|
|
break;
|
|
case TCP_AO_INFO:
|
|
cmd = "AO info set: ";
|
|
len = sizeof(struct tcp_ao_info_opt);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
__setsockopt_checked(sk, optname, false, optval, &len, err, cmd, tst);
|
|
}
|
|
|
|
static int prepare_defs(int cmd, void *optval)
|
|
{
|
|
int sk = socket(test_family, SOCK_STREAM, IPPROTO_TCP);
|
|
|
|
if (sk < 0)
|
|
test_error("socket()");
|
|
|
|
switch (cmd) {
|
|
case TCP_AO_ADD_KEY: {
|
|
struct tcp_ao_add *add = optval;
|
|
|
|
if (test_prepare_def_key(add, DEFAULT_TEST_PASSWORD, 0, this_ip_dest,
|
|
-1, 0, 100, 100))
|
|
test_error("prepare default tcp_ao_add");
|
|
break;
|
|
}
|
|
case TCP_AO_DEL_KEY: {
|
|
struct tcp_ao_del *del = optval;
|
|
|
|
if (test_add_key(sk, DEFAULT_TEST_PASSWORD, this_ip_dest,
|
|
DEFAULT_TEST_PREFIX, 100, 100))
|
|
test_error("add default key");
|
|
memset(del, 0, sizeof(struct tcp_ao_del));
|
|
del->sndid = 100;
|
|
del->rcvid = 100;
|
|
del->prefix = DEFAULT_TEST_PREFIX;
|
|
tcp_addr_to_sockaddr_in(&del->addr, &this_ip_dest, 0);
|
|
break;
|
|
}
|
|
case TCP_AO_INFO: {
|
|
struct tcp_ao_info_opt *info = optval;
|
|
|
|
if (test_add_key(sk, DEFAULT_TEST_PASSWORD, this_ip_dest,
|
|
DEFAULT_TEST_PREFIX, 100, 100))
|
|
test_error("add default key");
|
|
memset(info, 0, sizeof(struct tcp_ao_info_opt));
|
|
break;
|
|
}
|
|
case TCP_AO_GET_KEYS: {
|
|
struct tcp_ao_getsockopt *get = optval;
|
|
|
|
if (test_add_key(sk, DEFAULT_TEST_PASSWORD, this_ip_dest,
|
|
DEFAULT_TEST_PREFIX, 100, 100))
|
|
test_error("add default key");
|
|
memset(get, 0, sizeof(struct tcp_ao_getsockopt));
|
|
get->nkeys = 1;
|
|
get->get_all = 1;
|
|
break;
|
|
}
|
|
default:
|
|
test_error("unknown cmd");
|
|
}
|
|
|
|
return sk;
|
|
}
|
|
|
|
static void test_extend(int cmd, bool get, const char *tst, socklen_t under_size)
|
|
{
|
|
struct {
|
|
union {
|
|
struct tcp_ao_add add;
|
|
struct tcp_ao_del del;
|
|
struct tcp_ao_getsockopt get;
|
|
struct tcp_ao_info_opt info;
|
|
};
|
|
char *extend[100];
|
|
} tmp_opt;
|
|
socklen_t extended_size = sizeof(tmp_opt);
|
|
int sk;
|
|
|
|
memset(&tmp_opt, 0, sizeof(tmp_opt));
|
|
sk = prepare_defs(cmd, &tmp_opt);
|
|
__setsockopt_checked(sk, cmd, get, &tmp_opt, &under_size,
|
|
EINVAL, tst, ": minimum size");
|
|
|
|
memset(&tmp_opt, 0, sizeof(tmp_opt));
|
|
sk = prepare_defs(cmd, &tmp_opt);
|
|
__setsockopt_checked(sk, cmd, get, &tmp_opt, &extended_size,
|
|
0, tst, ": extended size");
|
|
|
|
memset(&tmp_opt, 0, sizeof(tmp_opt));
|
|
sk = prepare_defs(cmd, &tmp_opt);
|
|
__setsockopt_checked(sk, cmd, get, NULL, &extended_size,
|
|
EFAULT, tst, ": null optval");
|
|
|
|
if (get) {
|
|
memset(&tmp_opt, 0, sizeof(tmp_opt));
|
|
sk = prepare_defs(cmd, &tmp_opt);
|
|
__setsockopt_checked(sk, cmd, get, &tmp_opt, NULL,
|
|
EFAULT, tst, ": null optlen");
|
|
}
|
|
}
|
|
|
|
static void extend_tests(void)
|
|
{
|
|
test_extend(TCP_AO_ADD_KEY, false, "AO add",
|
|
offsetof(struct tcp_ao_add, key));
|
|
test_extend(TCP_AO_DEL_KEY, false, "AO del",
|
|
offsetof(struct tcp_ao_del, keyflags));
|
|
test_extend(TCP_AO_INFO, false, "AO set info",
|
|
offsetof(struct tcp_ao_info_opt, pkt_dropped_icmp));
|
|
test_extend(TCP_AO_INFO, true, "AO get info", -1);
|
|
test_extend(TCP_AO_GET_KEYS, true, "AO get keys", -1);
|
|
}
|
|
|
|
static void test_optmem_limit(void)
|
|
{
|
|
size_t i, keys_limit, current_optmem = test_get_optmem();
|
|
struct tcp_ao_add ao;
|
|
union tcp_addr net = {};
|
|
int sk;
|
|
|
|
if (inet_pton(TEST_FAMILY, TEST_NETWORK, &net) != 1)
|
|
test_error("Can't convert ip address %s", TEST_NETWORK);
|
|
|
|
sk = prepare_defs(TCP_AO_ADD_KEY, &ao);
|
|
keys_limit = current_optmem / KERNEL_TCP_AO_KEY_SZ_ROUND_UP;
|
|
for (i = 0;; i++) {
|
|
union tcp_addr key_peer;
|
|
int err;
|
|
|
|
key_peer = gen_tcp_addr(net, i + 1);
|
|
tcp_addr_to_sockaddr_in(&ao.addr, &key_peer, 0);
|
|
err = setsockopt(sk, IPPROTO_TCP, TCP_AO_ADD_KEY,
|
|
&ao, sizeof(ao));
|
|
if (!err) {
|
|
/*
|
|
* TCP_AO_ADD_KEY should be the same order as the real
|
|
* sizeof(struct tcp_ao_key) in kernel.
|
|
*/
|
|
if (i <= keys_limit * 10)
|
|
continue;
|
|
test_fail("optmem limit test failed: added %zu key", i);
|
|
break;
|
|
}
|
|
if (i < keys_limit) {
|
|
test_fail("optmem limit test failed: couldn't add %zu key", i);
|
|
break;
|
|
}
|
|
test_ok("optmem limit was hit on adding %zu key", i);
|
|
break;
|
|
}
|
|
close(sk);
|
|
}
|
|
|
|
static void test_einval_add_key(void)
|
|
{
|
|
struct tcp_ao_add ao;
|
|
int sk;
|
|
|
|
sk = prepare_defs(TCP_AO_ADD_KEY, &ao);
|
|
ao.keylen = TCP_AO_MAXKEYLEN + 1;
|
|
setsockopt_checked(sk, TCP_AO_ADD_KEY, &ao, EINVAL, "too big keylen");
|
|
|
|
sk = prepare_defs(TCP_AO_ADD_KEY, &ao);
|
|
ao.reserved = 1;
|
|
setsockopt_checked(sk, TCP_AO_ADD_KEY, &ao, EINVAL, "using reserved padding");
|
|
|
|
sk = prepare_defs(TCP_AO_ADD_KEY, &ao);
|
|
ao.reserved2 = 1;
|
|
setsockopt_checked(sk, TCP_AO_ADD_KEY, &ao, EINVAL, "using reserved2 padding");
|
|
|
|
/* tcp_ao_verify_ipv{4,6}() checks */
|
|
sk = prepare_defs(TCP_AO_ADD_KEY, &ao);
|
|
ao.addr.ss_family = AF_UNIX;
|
|
memcpy(&ao.addr, &SOCKADDR_ANY, sizeof(SOCKADDR_ANY));
|
|
setsockopt_checked(sk, TCP_AO_ADD_KEY, &ao, EINVAL, "wrong address family");
|
|
|
|
sk = prepare_defs(TCP_AO_ADD_KEY, &ao);
|
|
tcp_addr_to_sockaddr_in(&ao.addr, &this_ip_dest, 1234);
|
|
setsockopt_checked(sk, TCP_AO_ADD_KEY, &ao, EINVAL, "port (unsupported)");
|
|
|
|
sk = prepare_defs(TCP_AO_ADD_KEY, &ao);
|
|
ao.prefix = 0;
|
|
setsockopt_checked(sk, TCP_AO_ADD_KEY, &ao, EINVAL, "no prefix, addr");
|
|
|
|
sk = prepare_defs(TCP_AO_ADD_KEY, &ao);
|
|
ao.prefix = 0;
|
|
memcpy(&ao.addr, &SOCKADDR_ANY, sizeof(SOCKADDR_ANY));
|
|
setsockopt_checked(sk, TCP_AO_ADD_KEY, &ao, 0, "no prefix, any addr");
|
|
|
|
sk = prepare_defs(TCP_AO_ADD_KEY, &ao);
|
|
ao.prefix = 32;
|
|
memcpy(&ao.addr, &SOCKADDR_ANY, sizeof(SOCKADDR_ANY));
|
|
setsockopt_checked(sk, TCP_AO_ADD_KEY, &ao, EINVAL, "prefix, any addr");
|
|
|
|
sk = prepare_defs(TCP_AO_ADD_KEY, &ao);
|
|
ao.prefix = 129;
|
|
setsockopt_checked(sk, TCP_AO_ADD_KEY, &ao, EINVAL, "too big prefix");
|
|
|
|
sk = prepare_defs(TCP_AO_ADD_KEY, &ao);
|
|
ao.prefix = 2;
|
|
setsockopt_checked(sk, TCP_AO_ADD_KEY, &ao, EINVAL, "too short prefix");
|
|
|
|
sk = prepare_defs(TCP_AO_ADD_KEY, &ao);
|
|
ao.keyflags = (uint8_t)(-1);
|
|
setsockopt_checked(sk, TCP_AO_ADD_KEY, &ao, EINVAL, "bad key flags");
|
|
|
|
sk = prepare_defs(TCP_AO_ADD_KEY, &ao);
|
|
make_listen(sk);
|
|
ao.set_current = 1;
|
|
setsockopt_checked(sk, TCP_AO_ADD_KEY, &ao, EINVAL, "add current key on a listen socket");
|
|
|
|
sk = prepare_defs(TCP_AO_ADD_KEY, &ao);
|
|
make_listen(sk);
|
|
ao.set_rnext = 1;
|
|
setsockopt_checked(sk, TCP_AO_ADD_KEY, &ao, EINVAL, "add rnext key on a listen socket");
|
|
|
|
sk = prepare_defs(TCP_AO_ADD_KEY, &ao);
|
|
make_listen(sk);
|
|
ao.set_current = 1;
|
|
ao.set_rnext = 1;
|
|
setsockopt_checked(sk, TCP_AO_ADD_KEY, &ao, EINVAL, "add current+rnext key on a listen socket");
|
|
|
|
sk = prepare_defs(TCP_AO_ADD_KEY, &ao);
|
|
ao.set_current = 1;
|
|
setsockopt_checked(sk, TCP_AO_ADD_KEY, &ao, 0, "add key and set as current");
|
|
|
|
sk = prepare_defs(TCP_AO_ADD_KEY, &ao);
|
|
ao.set_rnext = 1;
|
|
setsockopt_checked(sk, TCP_AO_ADD_KEY, &ao, 0, "add key and set as rnext");
|
|
|
|
sk = prepare_defs(TCP_AO_ADD_KEY, &ao);
|
|
ao.set_current = 1;
|
|
ao.set_rnext = 1;
|
|
setsockopt_checked(sk, TCP_AO_ADD_KEY, &ao, 0, "add key and set as current+rnext");
|
|
|
|
sk = prepare_defs(TCP_AO_ADD_KEY, &ao);
|
|
ao.ifindex = 42;
|
|
setsockopt_checked(sk, TCP_AO_ADD_KEY, &ao, EINVAL,
|
|
"ifindex without TCP_AO_KEYF_IFNINDEX");
|
|
|
|
sk = prepare_defs(TCP_AO_ADD_KEY, &ao);
|
|
ao.keyflags |= TCP_AO_KEYF_IFINDEX;
|
|
ao.ifindex = 42;
|
|
setsockopt_checked(sk, TCP_AO_ADD_KEY, &ao, EINVAL, "non-existent VRF");
|
|
/*
|
|
* tcp_md5_do_lookup{,_any_l3index}() are checked in unsigned-md5
|
|
* see client_vrf_tests().
|
|
*/
|
|
|
|
test_optmem_limit();
|
|
|
|
/* tcp_ao_parse_crypto() */
|
|
sk = prepare_defs(TCP_AO_ADD_KEY, &ao);
|
|
ao.maclen = 100;
|
|
setsockopt_checked(sk, TCP_AO_ADD_KEY, &ao, EMSGSIZE, "maclen bigger than TCP hdr");
|
|
|
|
sk = prepare_defs(TCP_AO_ADD_KEY, &ao);
|
|
strcpy(ao.alg_name, "imaginary hash algo");
|
|
setsockopt_checked(sk, TCP_AO_ADD_KEY, &ao, ENOENT, "bad algo");
|
|
}
|
|
|
|
static void test_einval_del_key(void)
|
|
{
|
|
struct tcp_ao_del del;
|
|
int sk;
|
|
|
|
sk = prepare_defs(TCP_AO_DEL_KEY, &del);
|
|
del.reserved = 1;
|
|
setsockopt_checked(sk, TCP_AO_DEL_KEY, &del, EINVAL, "using reserved padding");
|
|
|
|
sk = prepare_defs(TCP_AO_DEL_KEY, &del);
|
|
del.reserved2 = 1;
|
|
setsockopt_checked(sk, TCP_AO_DEL_KEY, &del, EINVAL, "using reserved2 padding");
|
|
|
|
sk = prepare_defs(TCP_AO_DEL_KEY, &del);
|
|
make_listen(sk);
|
|
if (test_add_key(sk, DEFAULT_TEST_PASSWORD, this_ip_dest, DEFAULT_TEST_PREFIX, 0, 0))
|
|
test_error("add key");
|
|
del.set_current = 1;
|
|
setsockopt_checked(sk, TCP_AO_DEL_KEY, &del, EINVAL, "del and set current key on a listen socket");
|
|
|
|
sk = prepare_defs(TCP_AO_DEL_KEY, &del);
|
|
make_listen(sk);
|
|
if (test_add_key(sk, DEFAULT_TEST_PASSWORD, this_ip_dest, DEFAULT_TEST_PREFIX, 0, 0))
|
|
test_error("add key");
|
|
del.set_rnext = 1;
|
|
setsockopt_checked(sk, TCP_AO_DEL_KEY, &del, EINVAL, "del and set rnext key on a listen socket");
|
|
|
|
sk = prepare_defs(TCP_AO_DEL_KEY, &del);
|
|
make_listen(sk);
|
|
if (test_add_key(sk, DEFAULT_TEST_PASSWORD, this_ip_dest, DEFAULT_TEST_PREFIX, 0, 0))
|
|
test_error("add key");
|
|
del.set_current = 1;
|
|
del.set_rnext = 1;
|
|
setsockopt_checked(sk, TCP_AO_DEL_KEY, &del, EINVAL, "del and set current+rnext key on a listen socket");
|
|
|
|
sk = prepare_defs(TCP_AO_DEL_KEY, &del);
|
|
del.keyflags = (uint8_t)(-1);
|
|
setsockopt_checked(sk, TCP_AO_DEL_KEY, &del, EINVAL, "bad key flags");
|
|
|
|
sk = prepare_defs(TCP_AO_DEL_KEY, &del);
|
|
del.ifindex = 42;
|
|
setsockopt_checked(sk, TCP_AO_DEL_KEY, &del, EINVAL,
|
|
"ifindex without TCP_AO_KEYF_IFNINDEX");
|
|
|
|
sk = prepare_defs(TCP_AO_DEL_KEY, &del);
|
|
del.keyflags |= TCP_AO_KEYF_IFINDEX;
|
|
del.ifindex = 42;
|
|
setsockopt_checked(sk, TCP_AO_DEL_KEY, &del, ENOENT, "non-existent VRF");
|
|
|
|
sk = prepare_defs(TCP_AO_DEL_KEY, &del);
|
|
del.set_current = 1;
|
|
setsockopt_checked(sk, TCP_AO_DEL_KEY, &del, ENOENT, "set non-existing current key");
|
|
|
|
sk = prepare_defs(TCP_AO_DEL_KEY, &del);
|
|
del.set_rnext = 1;
|
|
setsockopt_checked(sk, TCP_AO_DEL_KEY, &del, ENOENT, "set non-existing rnext key");
|
|
|
|
sk = prepare_defs(TCP_AO_DEL_KEY, &del);
|
|
del.set_current = 1;
|
|
del.set_rnext = 1;
|
|
setsockopt_checked(sk, TCP_AO_DEL_KEY, &del, ENOENT, "set non-existing current+rnext key");
|
|
|
|
sk = prepare_defs(TCP_AO_DEL_KEY, &del);
|
|
if (test_add_key(sk, DEFAULT_TEST_PASSWORD, this_ip_dest, DEFAULT_TEST_PREFIX, 0, 0))
|
|
test_error("add key");
|
|
del.set_current = 1;
|
|
setsockopt_checked(sk, TCP_AO_DEL_KEY, &del, 0, "set current key");
|
|
|
|
sk = prepare_defs(TCP_AO_DEL_KEY, &del);
|
|
if (test_add_key(sk, DEFAULT_TEST_PASSWORD, this_ip_dest, DEFAULT_TEST_PREFIX, 0, 0))
|
|
test_error("add key");
|
|
del.set_rnext = 1;
|
|
setsockopt_checked(sk, TCP_AO_DEL_KEY, &del, 0, "set rnext key");
|
|
|
|
sk = prepare_defs(TCP_AO_DEL_KEY, &del);
|
|
if (test_add_key(sk, DEFAULT_TEST_PASSWORD, this_ip_dest, DEFAULT_TEST_PREFIX, 0, 0))
|
|
test_error("add key");
|
|
del.set_current = 1;
|
|
del.set_rnext = 1;
|
|
setsockopt_checked(sk, TCP_AO_DEL_KEY, &del, 0, "set current+rnext key");
|
|
|
|
sk = prepare_defs(TCP_AO_DEL_KEY, &del);
|
|
del.set_current = 1;
|
|
del.current_key = 100;
|
|
setsockopt_checked(sk, TCP_AO_DEL_KEY, &del, ENOENT, "set as current key to be removed");
|
|
|
|
sk = prepare_defs(TCP_AO_DEL_KEY, &del);
|
|
del.set_rnext = 1;
|
|
del.rnext = 100;
|
|
setsockopt_checked(sk, TCP_AO_DEL_KEY, &del, ENOENT, "set as rnext key to be removed");
|
|
|
|
sk = prepare_defs(TCP_AO_DEL_KEY, &del);
|
|
del.set_current = 1;
|
|
del.current_key = 100;
|
|
del.set_rnext = 1;
|
|
del.rnext = 100;
|
|
setsockopt_checked(sk, TCP_AO_DEL_KEY, &del, ENOENT, "set as current+rnext key to be removed");
|
|
|
|
sk = prepare_defs(TCP_AO_DEL_KEY, &del);
|
|
del.del_async = 1;
|
|
setsockopt_checked(sk, TCP_AO_DEL_KEY, &del, EINVAL, "async on non-listen");
|
|
|
|
sk = prepare_defs(TCP_AO_DEL_KEY, &del);
|
|
del.sndid = 101;
|
|
setsockopt_checked(sk, TCP_AO_DEL_KEY, &del, ENOENT, "non-existing sndid");
|
|
|
|
sk = prepare_defs(TCP_AO_DEL_KEY, &del);
|
|
del.rcvid = 101;
|
|
setsockopt_checked(sk, TCP_AO_DEL_KEY, &del, ENOENT, "non-existing rcvid");
|
|
|
|
sk = prepare_defs(TCP_AO_DEL_KEY, &del);
|
|
tcp_addr_to_sockaddr_in(&del.addr, &this_ip_addr, 0);
|
|
setsockopt_checked(sk, TCP_AO_DEL_KEY, &del, ENOENT, "incorrect addr");
|
|
|
|
sk = prepare_defs(TCP_AO_DEL_KEY, &del);
|
|
setsockopt_checked(sk, TCP_AO_DEL_KEY, &del, 0, "correct key delete");
|
|
}
|
|
|
|
static void test_einval_ao_info(void)
|
|
{
|
|
struct tcp_ao_info_opt info;
|
|
int sk;
|
|
|
|
sk = prepare_defs(TCP_AO_INFO, &info);
|
|
make_listen(sk);
|
|
info.set_current = 1;
|
|
setsockopt_checked(sk, TCP_AO_INFO, &info, EINVAL, "set current key on a listen socket");
|
|
|
|
sk = prepare_defs(TCP_AO_INFO, &info);
|
|
make_listen(sk);
|
|
info.set_rnext = 1;
|
|
setsockopt_checked(sk, TCP_AO_INFO, &info, EINVAL, "set rnext key on a listen socket");
|
|
|
|
sk = prepare_defs(TCP_AO_INFO, &info);
|
|
make_listen(sk);
|
|
info.set_current = 1;
|
|
info.set_rnext = 1;
|
|
setsockopt_checked(sk, TCP_AO_INFO, &info, EINVAL, "set current+rnext key on a listen socket");
|
|
|
|
sk = prepare_defs(TCP_AO_INFO, &info);
|
|
info.reserved = 1;
|
|
setsockopt_checked(sk, TCP_AO_INFO, &info, EINVAL, "using reserved padding");
|
|
|
|
sk = prepare_defs(TCP_AO_INFO, &info);
|
|
info.reserved2 = 1;
|
|
setsockopt_checked(sk, TCP_AO_INFO, &info, EINVAL, "using reserved2 padding");
|
|
|
|
sk = prepare_defs(TCP_AO_INFO, &info);
|
|
info.accept_icmps = 1;
|
|
setsockopt_checked(sk, TCP_AO_INFO, &info, 0, "accept_icmps");
|
|
|
|
sk = prepare_defs(TCP_AO_INFO, &info);
|
|
info.ao_required = 1;
|
|
setsockopt_checked(sk, TCP_AO_INFO, &info, 0, "ao required");
|
|
|
|
if (!should_skip_test("ao required with MD5 key", KCONFIG_TCP_MD5)) {
|
|
sk = prepare_defs(TCP_AO_INFO, &info);
|
|
info.ao_required = 1;
|
|
if (test_set_md5(sk, tcp_md5_client, TEST_PREFIX, -1,
|
|
"long long secret")) {
|
|
test_error("setsockopt(TCP_MD5SIG_EXT)");
|
|
close(sk);
|
|
} else {
|
|
setsockopt_checked(sk, TCP_AO_INFO, &info, EKEYREJECTED,
|
|
"ao required with MD5 key");
|
|
}
|
|
}
|
|
|
|
sk = prepare_defs(TCP_AO_INFO, &info);
|
|
info.set_current = 1;
|
|
setsockopt_checked(sk, TCP_AO_INFO, &info, ENOENT, "set non-existing current key");
|
|
|
|
sk = prepare_defs(TCP_AO_INFO, &info);
|
|
info.set_rnext = 1;
|
|
setsockopt_checked(sk, TCP_AO_INFO, &info, ENOENT, "set non-existing rnext key");
|
|
|
|
sk = prepare_defs(TCP_AO_INFO, &info);
|
|
info.set_current = 1;
|
|
info.set_rnext = 1;
|
|
setsockopt_checked(sk, TCP_AO_INFO, &info, ENOENT, "set non-existing current+rnext key");
|
|
|
|
sk = prepare_defs(TCP_AO_INFO, &info);
|
|
info.set_current = 1;
|
|
info.current_key = 100;
|
|
setsockopt_checked(sk, TCP_AO_INFO, &info, 0, "set current key");
|
|
|
|
sk = prepare_defs(TCP_AO_INFO, &info);
|
|
info.set_rnext = 1;
|
|
info.rnext = 100;
|
|
setsockopt_checked(sk, TCP_AO_INFO, &info, 0, "set rnext key");
|
|
|
|
sk = prepare_defs(TCP_AO_INFO, &info);
|
|
info.set_current = 1;
|
|
info.set_rnext = 1;
|
|
info.current_key = 100;
|
|
info.rnext = 100;
|
|
setsockopt_checked(sk, TCP_AO_INFO, &info, 0, "set current+rnext key");
|
|
|
|
sk = prepare_defs(TCP_AO_INFO, &info);
|
|
info.set_counters = 1;
|
|
info.pkt_good = 321;
|
|
info.pkt_bad = 888;
|
|
info.pkt_key_not_found = 654;
|
|
info.pkt_ao_required = 987654;
|
|
info.pkt_dropped_icmp = 10000;
|
|
setsockopt_checked(sk, TCP_AO_INFO, &info, 0, "set counters");
|
|
|
|
sk = prepare_defs(TCP_AO_INFO, &info);
|
|
setsockopt_checked(sk, TCP_AO_INFO, &info, 0, "no-op");
|
|
}
|
|
|
|
static void getsockopt_checked(int sk, struct tcp_ao_getsockopt *optval,
|
|
int err, const char *tst)
|
|
{
|
|
socklen_t len = sizeof(struct tcp_ao_getsockopt);
|
|
|
|
__setsockopt_checked(sk, TCP_AO_GET_KEYS, true, optval, &len, err,
|
|
"get keys: ", tst);
|
|
}
|
|
|
|
static void test_einval_get_keys(void)
|
|
{
|
|
struct tcp_ao_getsockopt out;
|
|
int sk;
|
|
|
|
sk = socket(test_family, SOCK_STREAM, IPPROTO_TCP);
|
|
if (sk < 0)
|
|
test_error("socket()");
|
|
getsockopt_checked(sk, &out, ENOENT, "no ao_info");
|
|
|
|
sk = prepare_defs(TCP_AO_GET_KEYS, &out);
|
|
getsockopt_checked(sk, &out, 0, "proper tcp_ao_get_mkts()");
|
|
|
|
sk = prepare_defs(TCP_AO_GET_KEYS, &out);
|
|
out.pkt_good = 643;
|
|
getsockopt_checked(sk, &out, EINVAL, "set out-only pkt_good counter");
|
|
|
|
sk = prepare_defs(TCP_AO_GET_KEYS, &out);
|
|
out.pkt_bad = 94;
|
|
getsockopt_checked(sk, &out, EINVAL, "set out-only pkt_bad counter");
|
|
|
|
sk = prepare_defs(TCP_AO_GET_KEYS, &out);
|
|
out.keyflags = (uint8_t)(-1);
|
|
getsockopt_checked(sk, &out, EINVAL, "bad keyflags");
|
|
|
|
sk = prepare_defs(TCP_AO_GET_KEYS, &out);
|
|
out.ifindex = 42;
|
|
getsockopt_checked(sk, &out, EINVAL,
|
|
"ifindex without TCP_AO_KEYF_IFNINDEX");
|
|
|
|
sk = prepare_defs(TCP_AO_GET_KEYS, &out);
|
|
out.reserved = 1;
|
|
getsockopt_checked(sk, &out, EINVAL, "using reserved field");
|
|
|
|
sk = prepare_defs(TCP_AO_GET_KEYS, &out);
|
|
out.get_all = 0;
|
|
out.prefix = 0;
|
|
tcp_addr_to_sockaddr_in(&out.addr, &this_ip_dest, 0);
|
|
getsockopt_checked(sk, &out, EINVAL, "no prefix, addr");
|
|
|
|
sk = prepare_defs(TCP_AO_GET_KEYS, &out);
|
|
out.get_all = 0;
|
|
out.prefix = 0;
|
|
memcpy(&out.addr, &SOCKADDR_ANY, sizeof(SOCKADDR_ANY));
|
|
getsockopt_checked(sk, &out, 0, "no prefix, any addr");
|
|
|
|
sk = prepare_defs(TCP_AO_GET_KEYS, &out);
|
|
out.get_all = 0;
|
|
out.prefix = 32;
|
|
memcpy(&out.addr, &SOCKADDR_ANY, sizeof(SOCKADDR_ANY));
|
|
getsockopt_checked(sk, &out, EINVAL, "prefix, any addr");
|
|
|
|
sk = prepare_defs(TCP_AO_GET_KEYS, &out);
|
|
out.get_all = 0;
|
|
out.prefix = 129;
|
|
tcp_addr_to_sockaddr_in(&out.addr, &this_ip_dest, 0);
|
|
getsockopt_checked(sk, &out, EINVAL, "too big prefix");
|
|
|
|
sk = prepare_defs(TCP_AO_GET_KEYS, &out);
|
|
out.get_all = 0;
|
|
out.prefix = 2;
|
|
tcp_addr_to_sockaddr_in(&out.addr, &this_ip_dest, 0);
|
|
getsockopt_checked(sk, &out, EINVAL, "too short prefix");
|
|
|
|
sk = prepare_defs(TCP_AO_GET_KEYS, &out);
|
|
out.get_all = 0;
|
|
out.prefix = DEFAULT_TEST_PREFIX;
|
|
tcp_addr_to_sockaddr_in(&out.addr, &this_ip_dest, 0);
|
|
getsockopt_checked(sk, &out, 0, "prefix + addr");
|
|
|
|
sk = prepare_defs(TCP_AO_GET_KEYS, &out);
|
|
out.get_all = 1;
|
|
out.prefix = DEFAULT_TEST_PREFIX;
|
|
getsockopt_checked(sk, &out, EINVAL, "get_all + prefix");
|
|
|
|
sk = prepare_defs(TCP_AO_GET_KEYS, &out);
|
|
out.get_all = 1;
|
|
tcp_addr_to_sockaddr_in(&out.addr, &this_ip_dest, 0);
|
|
getsockopt_checked(sk, &out, EINVAL, "get_all + addr");
|
|
|
|
sk = prepare_defs(TCP_AO_GET_KEYS, &out);
|
|
out.get_all = 1;
|
|
out.sndid = 1;
|
|
getsockopt_checked(sk, &out, EINVAL, "get_all + sndid");
|
|
|
|
sk = prepare_defs(TCP_AO_GET_KEYS, &out);
|
|
out.get_all = 1;
|
|
out.rcvid = 1;
|
|
getsockopt_checked(sk, &out, EINVAL, "get_all + rcvid");
|
|
|
|
sk = prepare_defs(TCP_AO_GET_KEYS, &out);
|
|
out.get_all = 0;
|
|
out.is_current = 1;
|
|
out.prefix = DEFAULT_TEST_PREFIX;
|
|
getsockopt_checked(sk, &out, EINVAL, "current + prefix");
|
|
|
|
sk = prepare_defs(TCP_AO_GET_KEYS, &out);
|
|
out.get_all = 0;
|
|
out.is_current = 1;
|
|
tcp_addr_to_sockaddr_in(&out.addr, &this_ip_dest, 0);
|
|
getsockopt_checked(sk, &out, EINVAL, "current + addr");
|
|
|
|
sk = prepare_defs(TCP_AO_GET_KEYS, &out);
|
|
out.get_all = 0;
|
|
out.is_current = 1;
|
|
out.sndid = 1;
|
|
getsockopt_checked(sk, &out, EINVAL, "current + sndid");
|
|
|
|
sk = prepare_defs(TCP_AO_GET_KEYS, &out);
|
|
out.get_all = 0;
|
|
out.is_current = 1;
|
|
out.rcvid = 1;
|
|
getsockopt_checked(sk, &out, EINVAL, "current + rcvid");
|
|
|
|
sk = prepare_defs(TCP_AO_GET_KEYS, &out);
|
|
out.get_all = 0;
|
|
out.is_rnext = 1;
|
|
out.prefix = DEFAULT_TEST_PREFIX;
|
|
getsockopt_checked(sk, &out, EINVAL, "rnext + prefix");
|
|
|
|
sk = prepare_defs(TCP_AO_GET_KEYS, &out);
|
|
out.get_all = 0;
|
|
out.is_rnext = 1;
|
|
tcp_addr_to_sockaddr_in(&out.addr, &this_ip_dest, 0);
|
|
getsockopt_checked(sk, &out, EINVAL, "rnext + addr");
|
|
|
|
sk = prepare_defs(TCP_AO_GET_KEYS, &out);
|
|
out.get_all = 0;
|
|
out.is_rnext = 1;
|
|
out.sndid = 1;
|
|
getsockopt_checked(sk, &out, EINVAL, "rnext + sndid");
|
|
|
|
sk = prepare_defs(TCP_AO_GET_KEYS, &out);
|
|
out.get_all = 0;
|
|
out.is_rnext = 1;
|
|
out.rcvid = 1;
|
|
getsockopt_checked(sk, &out, EINVAL, "rnext + rcvid");
|
|
|
|
sk = prepare_defs(TCP_AO_GET_KEYS, &out);
|
|
out.get_all = 1;
|
|
out.is_current = 1;
|
|
getsockopt_checked(sk, &out, EINVAL, "get_all + current");
|
|
|
|
sk = prepare_defs(TCP_AO_GET_KEYS, &out);
|
|
out.get_all = 1;
|
|
out.is_rnext = 1;
|
|
getsockopt_checked(sk, &out, EINVAL, "get_all + rnext");
|
|
|
|
sk = prepare_defs(TCP_AO_GET_KEYS, &out);
|
|
out.get_all = 0;
|
|
out.is_current = 1;
|
|
out.is_rnext = 1;
|
|
getsockopt_checked(sk, &out, 0, "current + rnext");
|
|
}
|
|
|
|
static void einval_tests(void)
|
|
{
|
|
test_einval_add_key();
|
|
test_einval_del_key();
|
|
test_einval_ao_info();
|
|
test_einval_get_keys();
|
|
}
|
|
|
|
static void duplicate_tests(void)
|
|
{
|
|
union tcp_addr network_dup;
|
|
struct tcp_ao_add ao, ao2;
|
|
int sk;
|
|
|
|
sk = prepare_defs(TCP_AO_ADD_KEY, &ao);
|
|
if (setsockopt(sk, IPPROTO_TCP, TCP_AO_ADD_KEY, &ao, sizeof(ao)))
|
|
test_error("setsockopt()");
|
|
setsockopt_checked(sk, TCP_AO_ADD_KEY, &ao, EEXIST, "duplicate: full copy");
|
|
|
|
sk = prepare_defs(TCP_AO_ADD_KEY, &ao);
|
|
ao2 = ao;
|
|
memcpy(&ao2.addr, &SOCKADDR_ANY, sizeof(SOCKADDR_ANY));
|
|
ao2.prefix = 0;
|
|
if (setsockopt(sk, IPPROTO_TCP, TCP_AO_ADD_KEY, &ao2, sizeof(ao)))
|
|
test_error("setsockopt()");
|
|
setsockopt_checked(sk, TCP_AO_ADD_KEY, &ao, EEXIST, "duplicate: any addr key on the socket");
|
|
|
|
sk = prepare_defs(TCP_AO_ADD_KEY, &ao);
|
|
if (setsockopt(sk, IPPROTO_TCP, TCP_AO_ADD_KEY, &ao, sizeof(ao)))
|
|
test_error("setsockopt()");
|
|
memcpy(&ao.addr, &SOCKADDR_ANY, sizeof(SOCKADDR_ANY));
|
|
ao.prefix = 0;
|
|
setsockopt_checked(sk, TCP_AO_ADD_KEY, &ao, EEXIST, "duplicate: add any addr key");
|
|
|
|
if (inet_pton(TEST_FAMILY, TEST_NETWORK, &network_dup) != 1)
|
|
test_error("Can't convert ip address %s", TEST_NETWORK);
|
|
sk = prepare_defs(TCP_AO_ADD_KEY, &ao);
|
|
if (setsockopt(sk, IPPROTO_TCP, TCP_AO_ADD_KEY, &ao, sizeof(ao)))
|
|
test_error("setsockopt()");
|
|
if (test_prepare_def_key(&ao, "password", 0, network_dup,
|
|
16, 0, 100, 100))
|
|
test_error("prepare default tcp_ao_add");
|
|
setsockopt_checked(sk, TCP_AO_ADD_KEY, &ao, EEXIST, "duplicate: add any addr for the same subnet");
|
|
|
|
sk = prepare_defs(TCP_AO_ADD_KEY, &ao);
|
|
if (setsockopt(sk, IPPROTO_TCP, TCP_AO_ADD_KEY, &ao, sizeof(ao)))
|
|
test_error("setsockopt()");
|
|
setsockopt_checked(sk, TCP_AO_ADD_KEY, &ao, EEXIST, "duplicate: full copy of a key");
|
|
|
|
sk = prepare_defs(TCP_AO_ADD_KEY, &ao);
|
|
if (setsockopt(sk, IPPROTO_TCP, TCP_AO_ADD_KEY, &ao, sizeof(ao)))
|
|
test_error("setsockopt()");
|
|
ao.rcvid = 101;
|
|
setsockopt_checked(sk, TCP_AO_ADD_KEY, &ao, EEXIST, "duplicate: RecvID differs");
|
|
|
|
sk = prepare_defs(TCP_AO_ADD_KEY, &ao);
|
|
if (setsockopt(sk, IPPROTO_TCP, TCP_AO_ADD_KEY, &ao, sizeof(ao)))
|
|
test_error("setsockopt()");
|
|
ao.sndid = 101;
|
|
setsockopt_checked(sk, TCP_AO_ADD_KEY, &ao, EEXIST, "duplicate: SendID differs");
|
|
}
|
|
|
|
static void *client_fn(void *arg)
|
|
{
|
|
if (inet_pton(TEST_FAMILY, __TEST_CLIENT_IP(2), &tcp_md5_client) != 1)
|
|
test_error("Can't convert ip address");
|
|
extend_tests();
|
|
einval_tests();
|
|
duplicate_tests();
|
|
/*
|
|
* TODO: check getsockopt(TCP_AO_GET_KEYS) with different filters
|
|
* returning proper nr & keys;
|
|
*/
|
|
|
|
return NULL;
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
test_init(121, client_fn, NULL);
|
|
return 0;
|
|
}
|