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