|
| 1 | +// SPDX-License-Identifier: GPL-2.0 |
| 2 | +/* Author: Dmitry Safonov <[email protected]> */ |
| 3 | +#include <inttypes.h> |
| 4 | +#include "aolib.h" |
| 5 | + |
| 6 | +static union tcp_addr local_addr; |
| 7 | + |
| 8 | +static void __setup_lo_intf(const char *lo_intf, |
| 9 | + const char *addr_str, uint8_t prefix) |
| 10 | +{ |
| 11 | + if (inet_pton(TEST_FAMILY, addr_str, &local_addr) != 1) |
| 12 | + test_error("Can't convert local ip address"); |
| 13 | + |
| 14 | + if (ip_addr_add(lo_intf, TEST_FAMILY, local_addr, prefix)) |
| 15 | + test_error("Failed to add %s ip address", lo_intf); |
| 16 | + |
| 17 | + if (link_set_up(lo_intf)) |
| 18 | + test_error("Failed to bring %s up", lo_intf); |
| 19 | +} |
| 20 | + |
| 21 | +static void setup_lo_intf(const char *lo_intf) |
| 22 | +{ |
| 23 | +#ifdef IPV6_TEST |
| 24 | + __setup_lo_intf(lo_intf, "::1", 128); |
| 25 | +#else |
| 26 | + __setup_lo_intf(lo_intf, "127.0.0.1", 8); |
| 27 | +#endif |
| 28 | +} |
| 29 | + |
| 30 | +static void tcp_self_connect(const char *tst, unsigned int port, |
| 31 | + bool different_keyids, bool check_restore) |
| 32 | +{ |
| 33 | + uint64_t before_challenge_ack, after_challenge_ack; |
| 34 | + uint64_t before_syn_challenge, after_syn_challenge; |
| 35 | + struct tcp_ao_counters before_ao, after_ao; |
| 36 | + uint64_t before_aogood, after_aogood; |
| 37 | + struct netstat *ns_before, *ns_after; |
| 38 | + const size_t nr_packets = 20; |
| 39 | + struct tcp_ao_repair ao_img; |
| 40 | + struct tcp_sock_state img; |
| 41 | + sockaddr_af addr; |
| 42 | + int sk; |
| 43 | + |
| 44 | + tcp_addr_to_sockaddr_in(&addr, &local_addr, htons(port)); |
| 45 | + |
| 46 | + sk = socket(test_family, SOCK_STREAM, IPPROTO_TCP); |
| 47 | + if (sk < 0) |
| 48 | + test_error("socket()"); |
| 49 | + |
| 50 | + if (different_keyids) { |
| 51 | + if (test_add_key(sk, DEFAULT_TEST_PASSWORD, local_addr, -1, 5, 7)) |
| 52 | + test_error("setsockopt(TCP_AO_ADD_KEY)"); |
| 53 | + if (test_add_key(sk, DEFAULT_TEST_PASSWORD, local_addr, -1, 7, 5)) |
| 54 | + test_error("setsockopt(TCP_AO_ADD_KEY)"); |
| 55 | + } else { |
| 56 | + if (test_add_key(sk, DEFAULT_TEST_PASSWORD, local_addr, -1, 100, 100)) |
| 57 | + test_error("setsockopt(TCP_AO_ADD_KEY)"); |
| 58 | + } |
| 59 | + |
| 60 | + if (bind(sk, (struct sockaddr *)&addr, sizeof(addr)) < 0) |
| 61 | + test_error("bind()"); |
| 62 | + |
| 63 | + ns_before = netstat_read(); |
| 64 | + before_aogood = netstat_get(ns_before, "TCPAOGood", NULL); |
| 65 | + before_challenge_ack = netstat_get(ns_before, "TCPChallengeACK", NULL); |
| 66 | + before_syn_challenge = netstat_get(ns_before, "TCPSYNChallenge", NULL); |
| 67 | + if (test_get_tcp_ao_counters(sk, &before_ao)) |
| 68 | + test_error("test_get_tcp_ao_counters()"); |
| 69 | + |
| 70 | + if (__test_connect_socket(sk, "lo", (struct sockaddr *)&addr, |
| 71 | + sizeof(addr), TEST_TIMEOUT_SEC) < 0) { |
| 72 | + ns_after = netstat_read(); |
| 73 | + netstat_print_diff(ns_before, ns_after); |
| 74 | + test_error("failed to connect()"); |
| 75 | + } |
| 76 | + |
| 77 | + if (test_client_verify(sk, 100, nr_packets, TEST_TIMEOUT_SEC)) { |
| 78 | + test_fail("%s: tcp connection verify failed", tst); |
| 79 | + close(sk); |
| 80 | + return; |
| 81 | + } |
| 82 | + |
| 83 | + ns_after = netstat_read(); |
| 84 | + after_aogood = netstat_get(ns_after, "TCPAOGood", NULL); |
| 85 | + after_challenge_ack = netstat_get(ns_after, "TCPChallengeACK", NULL); |
| 86 | + after_syn_challenge = netstat_get(ns_after, "TCPSYNChallenge", NULL); |
| 87 | + if (test_get_tcp_ao_counters(sk, &after_ao)) |
| 88 | + test_error("test_get_tcp_ao_counters()"); |
| 89 | + if (!check_restore) { |
| 90 | + /* to debug: netstat_print_diff(ns_before, ns_after); */ |
| 91 | + netstat_free(ns_before); |
| 92 | + } |
| 93 | + netstat_free(ns_after); |
| 94 | + |
| 95 | + if (after_aogood <= before_aogood) { |
| 96 | + test_fail("%s: TCPAOGood counter mismatch: %zu <= %zu", |
| 97 | + tst, after_aogood, before_aogood); |
| 98 | + close(sk); |
| 99 | + return; |
| 100 | + } |
| 101 | + if (after_challenge_ack <= before_challenge_ack || |
| 102 | + after_syn_challenge <= before_syn_challenge) { |
| 103 | + /* |
| 104 | + * It's also meant to test simultaneous open, so check |
| 105 | + * these counters as well. |
| 106 | + */ |
| 107 | + test_fail("%s: Didn't challenge SYN or ACK: %zu <= %zu OR %zu <= %zu", |
| 108 | + tst, after_challenge_ack, before_challenge_ack, |
| 109 | + after_syn_challenge, before_syn_challenge); |
| 110 | + close(sk); |
| 111 | + return; |
| 112 | + } |
| 113 | + |
| 114 | + if (test_tcp_ao_counters_cmp(tst, &before_ao, &after_ao, TEST_CNT_GOOD)) { |
| 115 | + close(sk); |
| 116 | + return; |
| 117 | + } |
| 118 | + |
| 119 | + if (!check_restore) { |
| 120 | + test_ok("%s: connect TCPAOGood %" PRIu64 " => %" PRIu64, |
| 121 | + tst, before_aogood, after_aogood); |
| 122 | + close(sk); |
| 123 | + return; |
| 124 | + } |
| 125 | + |
| 126 | + test_enable_repair(sk); |
| 127 | + test_sock_checkpoint(sk, &img, &addr); |
| 128 | +#ifdef IPV6_TEST |
| 129 | + addr.sin6_port = htons(port + 1); |
| 130 | +#else |
| 131 | + addr.sin_port = htons(port + 1); |
| 132 | +#endif |
| 133 | + test_ao_checkpoint(sk, &ao_img); |
| 134 | + test_kill_sk(sk); |
| 135 | + |
| 136 | + sk = socket(test_family, SOCK_STREAM, IPPROTO_TCP); |
| 137 | + if (sk < 0) |
| 138 | + test_error("socket()"); |
| 139 | + |
| 140 | + test_enable_repair(sk); |
| 141 | + __test_sock_restore(sk, "lo", &img, &addr, &addr, sizeof(addr)); |
| 142 | + if (different_keyids) { |
| 143 | + if (test_add_repaired_key(sk, DEFAULT_TEST_PASSWORD, 0, |
| 144 | + local_addr, -1, 7, 5)) |
| 145 | + test_error("setsockopt(TCP_AO_ADD_KEY)"); |
| 146 | + if (test_add_repaired_key(sk, DEFAULT_TEST_PASSWORD, 0, |
| 147 | + local_addr, -1, 5, 7)) |
| 148 | + test_error("setsockopt(TCP_AO_ADD_KEY)"); |
| 149 | + } else { |
| 150 | + if (test_add_repaired_key(sk, DEFAULT_TEST_PASSWORD, 0, |
| 151 | + local_addr, -1, 100, 100)) |
| 152 | + test_error("setsockopt(TCP_AO_ADD_KEY)"); |
| 153 | + } |
| 154 | + test_ao_restore(sk, &ao_img); |
| 155 | + test_disable_repair(sk); |
| 156 | + test_sock_state_free(&img); |
| 157 | + if (test_client_verify(sk, 100, nr_packets, TEST_TIMEOUT_SEC)) { |
| 158 | + test_fail("%s: tcp connection verify failed", tst); |
| 159 | + close(sk); |
| 160 | + return; |
| 161 | + } |
| 162 | + ns_after = netstat_read(); |
| 163 | + after_aogood = netstat_get(ns_after, "TCPAOGood", NULL); |
| 164 | + /* to debug: netstat_print_diff(ns_before, ns_after); */ |
| 165 | + netstat_free(ns_before); |
| 166 | + netstat_free(ns_after); |
| 167 | + close(sk); |
| 168 | + if (after_aogood <= before_aogood) { |
| 169 | + test_fail("%s: TCPAOGood counter mismatch: %zu <= %zu", |
| 170 | + tst, after_aogood, before_aogood); |
| 171 | + return; |
| 172 | + } |
| 173 | + test_ok("%s: connect TCPAOGood %" PRIu64 " => %" PRIu64, |
| 174 | + tst, before_aogood, after_aogood); |
| 175 | +} |
| 176 | + |
| 177 | +static void *client_fn(void *arg) |
| 178 | +{ |
| 179 | + unsigned int port = test_server_port; |
| 180 | + |
| 181 | + setup_lo_intf("lo"); |
| 182 | + |
| 183 | + tcp_self_connect("self-connect(same keyids)", port++, false, false); |
| 184 | + tcp_self_connect("self-connect(different keyids)", port++, true, false); |
| 185 | + tcp_self_connect("self-connect(restore)", port, false, true); |
| 186 | + port += 2; |
| 187 | + tcp_self_connect("self-connect(restore, different keyids)", port, true, true); |
| 188 | + port += 2; |
| 189 | + |
| 190 | + return NULL; |
| 191 | +} |
| 192 | + |
| 193 | +int main(int argc, char *argv[]) |
| 194 | +{ |
| 195 | + test_init(4, client_fn, NULL); |
| 196 | + return 0; |
| 197 | +} |
0 commit comments