// SPDX-License-Identifier: GPL-2.0
/* Author: Dmitry Safonov <[email protected]> */
#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;
}