]> git.meshlink.io Git - meshlink/blob - test/pmtu.c
Add test for detecting failing UDP communication.
[meshlink] / test / pmtu.c
1 #ifndef NDEBUG
2 #undef NDEBUG
3 #endif
4
5 #include <assert.h>
6 #include <stdio.h>
7 #include <unistd.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <time.h>
11 #include <sys/types.h>
12 #include <sys/stat.h>
13 #include <fcntl.h>
14
15 #include "meshlink.h"
16 #include "devtools.h"
17 #include "utils.h"
18
19 #define nnodes 3
20
21 static const struct info {
22         const char *name;
23         const char *confdir;
24         const char *netns;
25         dev_class_t devclass;
26 } nodes[nnodes] = {
27         {"relay", "pmtu_conf.1", "/run/netns/pmtu_r", DEV_CLASS_BACKBONE},
28         {"peer", "pmtu_conf.2", "/run/netns/pmtu_p", DEV_CLASS_STATIONARY},
29         {"nut", "pmtu_conf.3", "/run/netns/pmtu_n", DEV_CLASS_STATIONARY},
30 };
31
32 static struct state {
33         meshlink_handle_t *mesh;
34         int netns;
35         struct sync_flag up_flag;
36         int pmtu;
37         int probe_count;
38         int probe_bytes;
39 } states[nnodes];
40
41 static void relay_up_cb(meshlink_handle_t *mesh, meshlink_node_t *node, bool reachable) {
42         struct state *state = mesh->priv;
43
44         // Check that we are connected to another peer besides the relay
45         if(reachable && node != meshlink_get_self(mesh) && strcmp(node->name, "relay")) {
46                 set_sync_flag(&state->up_flag, true);
47                 meshlink_set_node_status_cb(mesh, NULL);
48         }
49 }
50
51 static void receive_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, const void *data, size_t len) {
52         if(!data && !len) {
53                 meshlink_channel_close(mesh, channel);
54         }
55 }
56
57 static bool accept_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, uint16_t port, const void *data, size_t len) {
58         (void)port;
59         (void)data;
60         (void)len;
61         meshlink_set_channel_receive_cb(mesh, channel, receive_cb);
62         return true;
63 }
64
65 static void wait_for_pmtu(void) {
66         // Set up a channel from peer to nut
67         meshlink_set_channel_accept_cb(states[2].mesh, accept_cb);
68         meshlink_handle_t *mesh = states[1].mesh;
69         meshlink_node_t *peer = meshlink_get_node(mesh, nodes[2].name);
70         meshlink_channel_t *channel = meshlink_channel_open(mesh, peer, 1, NULL, NULL, 0);
71         assert(channel);
72
73         // While sending regular data, wait for PMTU discovery to finish
74         for(int i = 0; i < 30; i++) {
75                 sleep(1);
76
77                 devtool_node_status_t status;
78                 devtool_get_node_status(mesh, peer, &status);
79                 states[1].pmtu = status.minmtu;
80
81                 if(status.minmtu == status.maxmtu) {
82                         break;
83                 }
84
85                 assert(meshlink_channel_send(mesh, channel, "ping", 4) == 4);
86         }
87
88         meshlink_channel_close(mesh, channel);
89 }
90
91 static void wait_for_udp_timeout(void) {
92         // Set up a channel from peer to nut
93         meshlink_set_channel_accept_cb(states[2].mesh, accept_cb);
94         meshlink_handle_t *mesh = states[1].mesh;
95         meshlink_node_t *peer = meshlink_get_node(mesh, nodes[2].name);
96         meshlink_channel_t *channel = meshlink_channel_open(mesh, peer, 1, NULL, NULL, 0);
97         assert(channel);
98
99         // While sending regular data, wait for UDP to time out
100         for(int i = 0; i < 20; i++) {
101                 sleep(1);
102
103                 devtool_node_status_t status;
104                 devtool_get_node_status(mesh, peer, &status);
105                 states[1].pmtu = status.minmtu;
106
107                 if(!status.minmtu) {
108                         break;
109                 }
110
111                 assert(meshlink_channel_send(mesh, channel, "ping", 4) == 4);
112         }
113
114         meshlink_channel_close(mesh, channel);
115 }
116
117 static void start_peer_nut(void) {
118         // Start peer and nut
119         for(int i = 1; i < nnodes; i++) {
120                 meshlink_set_node_status_cb(states[i].mesh, relay_up_cb);
121                 assert(meshlink_start(states[i].mesh));
122         }
123
124         // Wait for the peer and nut to see each other
125         for(int i = 1; i < nnodes; i++) {
126                 assert(wait_sync_flag(&states[i].up_flag, 5));
127         }
128 }
129
130 static void stop_peer_nut(void) {
131         // Stop peer and nut, reset counters
132         for(int i = 1; i < nnodes; i++) {
133                 meshlink_stop(states[i].mesh);
134                 states[i].up_flag.flag = false;
135                 states[i].pmtu = 0;
136                 states[i].probe_count = 0;
137                 states[i].probe_bytes = 0;
138         }
139 }
140
141 int main(void) {
142         // This test requires root access due to the use of network namespaces
143         if(getuid() != 0) {
144                 return 77;
145         }
146
147         // Set up namespaces
148         assert(system("./pmtu-setup") == 0);
149
150         // Bring up the nodes
151         for(int i = 0; i < nnodes; i++) {
152                 assert(meshlink_destroy(nodes[i].confdir));
153
154                 // Open the network namespace
155                 states[i].netns = open(nodes[i].netns, O_RDONLY);
156                 assert(states[i].netns != -1);
157
158                 // Open the MeshLink instance
159                 meshlink_open_params_t *params;
160                 assert(params = meshlink_open_params_init(nodes[i].confdir, nodes[i].name, "pmtu", nodes[i].devclass));
161                 assert(meshlink_open_params_set_netns(params, states[i].netns));
162                 assert(states[i].mesh = meshlink_open_ex(params));
163                 free(params);
164
165                 states[i].mesh->priv = &states[i];
166                 meshlink_enable_discovery(states[i].mesh, false);
167                 init_sync_flag(&states[i].up_flag);
168
169                 meshlink_set_log_cb(states[i].mesh, MESHLINK_INFO, log_cb);
170
171                 // Link the relay node to the other nodes
172                 if(i > 0) {
173                         link_meshlink_pair(states[0].mesh, states[i].mesh);
174                 }
175         }
176
177         // Start the relay
178         assert(meshlink_start(states[0].mesh));
179
180         // Start peers and wait for them to connect
181         start_peer_nut();
182
183         // Wait for PMTU discovery to finish
184         wait_for_pmtu();
185
186         assert(states[1].pmtu >= 1400 && states[1].pmtu <= 1500);
187         assert(states[1].probe_count <= 10);
188         assert(states[1].probe_bytes <= 1500 * 10);
189
190         // Drop the MTU to 800
191         stop_peer_nut();
192
193         assert(system("ip netns exec pmtu_p ip link set eth0 mtu 800") == 0);
194         assert(system("ip netns exec pmtu_n ip link set eth0 mtu 800") == 0);
195
196         // Workaround for autoconnect algorithm throttling reconnects
197         sleep(15);
198
199         start_peer_nut();
200         wait_for_pmtu();
201
202         assert(states[1].pmtu >= 700 && states[1].pmtu <= 800);
203         assert(states[1].probe_count <= 20);
204         assert(states[1].probe_bytes <= 800 * 20);
205
206         // Block UDP
207
208         assert(system("ip netns exec pmtu_p iptables -A INPUT -p udp -j DROP") == 0);
209         assert(system("ip netns exec pmtu_n iptables -A INPUT -p udp -j DROP") == 0);
210
211         // Wait
212
213         wait_for_udp_timeout();
214         assert(states[1].pmtu == 0);
215
216         // Cleanup
217         for(int i = 0; i < nnodes; i++) {
218                 meshlink_close(states[i].mesh);
219         }
220 }