tests: Improve vconn tests.
[sliver-openvswitch.git] / tests / test-vconn.c
1 /*
2  * Copyright (c) 2009, 2010 Nicira Networks.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at:
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <config.h>
18 #include "vconn.h"
19 #include <errno.h>
20 #include <inttypes.h>
21 #include <signal.h>
22 #include <stdlib.h>
23 #include <unistd.h>
24 #include "command-line.h"
25 #include "poll-loop.h"
26 #include "socket-util.h"
27 #include "stream.h"
28 #include "stream-ssl.h"
29 #include "timeval.h"
30 #include "util.h"
31 #include "vlog.h"
32
33 #undef NDEBUG
34 #include <assert.h>
35
36 struct fake_pvconn {
37     const char *type;
38     char *pvconn_name;
39     char *vconn_name;
40     struct pstream *pstream;
41 };
42
43 static void
44 check(int a, int b, const char *as, const char *file, int line)
45 {
46     if (a != b) {
47         ovs_fatal(0, "%s:%d: %s is %d but should be %d", file, line, as, a, b);
48     }
49 }
50
51
52 #define CHECK(A, B) check(A, B, #A, __FILE__, __LINE__)
53
54 static void
55 check_errno(int a, int b, const char *as, const char *file, int line)
56 {
57     if (a != b) {
58         ovs_fatal(0, "%s:%d: %s is %d (%s) but should be %d (%s)",
59                   file, line, as, a, strerror(abs(a)), b, strerror(abs(b)));
60     }
61 }
62
63 #define CHECK_ERRNO(A, B) check_errno(A, B, #A, __FILE__, __LINE__)
64
65 static void
66 fpv_create(const char *type, struct fake_pvconn *fpv)
67 {
68     if (!strcmp(type, "ssl")) {
69         stream_ssl_set_private_key_file("testpki-privkey.pem");
70         stream_ssl_set_certificate_file("testpki-cert.pem");
71         stream_ssl_set_ca_cert_file("testpki-cacert.pem", false);
72     }
73
74     fpv->type = type;
75     if (!strcmp(type, "unix")) {
76         static int unix_count = 0;
77         char *bind_path;
78
79         bind_path = xasprintf("fake-pvconn.%d", unix_count++);
80         fpv->pvconn_name = xasprintf("punix:%s", bind_path);
81         fpv->vconn_name = xasprintf("unix:%s", bind_path);
82         CHECK_ERRNO(pstream_open(fpv->pvconn_name, &fpv->pstream), 0);
83         free(bind_path);
84     } else if (!strcmp(type, "tcp") || !strcmp(type, "ssl")) {
85         char *s, *method, *port, *save_ptr = NULL;
86         char *open_name;
87
88         open_name = xasprintf("p%s:0:127.0.0.1", type);
89         CHECK_ERRNO(pstream_open(open_name, &fpv->pstream), 0);
90
91         /* Extract bound port number from pstream name. */
92         s = xstrdup(pstream_get_name(fpv->pstream));
93         method = strtok_r(s, ":", &save_ptr);
94         port = strtok_r(NULL, ":", &save_ptr);
95
96         /* Save info. */
97         fpv->pvconn_name = xstrdup(pstream_get_name(fpv->pstream));
98         fpv->vconn_name = xasprintf("%s:127.0.0.1:%s", type, port);
99
100         free(open_name);
101         free(s);
102     } else {
103         abort();
104     }
105 }
106
107 static struct stream *
108 fpv_accept(struct fake_pvconn *fpv)
109 {
110     struct stream *stream;
111
112     CHECK_ERRNO(pstream_accept_block(fpv->pstream, &stream), 0);
113
114     return stream;
115 }
116
117 static void
118 fpv_close(struct fake_pvconn *fpv)
119 {
120     pstream_close(fpv->pstream);
121     fpv->pstream = NULL;
122 }
123
124 static void
125 fpv_destroy(struct fake_pvconn *fpv)
126 {
127     fpv_close(fpv);
128     free(fpv->pvconn_name);
129     free(fpv->vconn_name);
130 }
131
132 /* Connects to a fake_pvconn with vconn_open(), then closes the listener and
133  * verifies that vconn_connect() reports 'expected_error'. */
134 static void
135 test_refuse_connection(int argc UNUSED, char *argv[])
136 {
137     const char *type = argv[1];
138     int expected_error;
139     struct fake_pvconn fpv;
140     struct vconn *vconn;
141
142     expected_error = !strcmp(type, "unix") ? EPIPE : ECONNRESET;
143
144     fpv_create(type, &fpv);
145     CHECK_ERRNO(vconn_open(fpv.vconn_name, OFP_VERSION, &vconn), 0);
146     fpv_close(&fpv);
147     vconn_run(vconn);
148     CHECK_ERRNO(vconn_connect(vconn), expected_error);
149     vconn_close(vconn);
150     fpv_destroy(&fpv);
151 }
152
153 /* Connects to a fake_pvconn with vconn_open(), accepts that connection and
154  * closes it immediately, and verifies that vconn_connect() reports
155  * 'expected_error'. */
156 static void
157 test_accept_then_close(int argc UNUSED, char *argv[])
158 {
159     const char *type = argv[1];
160     int expected_error;
161     struct fake_pvconn fpv;
162     struct vconn *vconn;
163
164     expected_error = (!strcmp(type, "unix") ? EPIPE
165                       : !strcmp(type, "tcp") ? ECONNRESET
166                       : EPROTO);
167
168     fpv_create(type, &fpv);
169     CHECK_ERRNO(vconn_open(fpv.vconn_name, OFP_VERSION, &vconn), 0);
170     vconn_run(vconn);
171     stream_close(fpv_accept(&fpv));
172     fpv_close(&fpv);
173     CHECK_ERRNO(vconn_connect(vconn), expected_error);
174     vconn_close(vconn);
175     fpv_destroy(&fpv);
176 }
177
178 /* Connects to a fake_pvconn with vconn_open(), accepts that connection and
179  * reads the hello message from it, then closes the connection and verifies
180  * that vconn_connect() reports 'expected_error'. */
181 static void
182 test_read_hello(int argc UNUSED, char *argv[])
183 {
184     const char *type = argv[1];
185     struct fake_pvconn fpv;
186     struct vconn *vconn;
187     struct stream *stream;
188
189     fpv_create(type, &fpv);
190     CHECK_ERRNO(vconn_open(fpv.vconn_name, OFP_VERSION, &vconn), 0);
191     vconn_run(vconn);
192     stream = fpv_accept(&fpv);
193     fpv_destroy(&fpv);
194     for (;;) {
195        struct ofp_header hello;
196        int retval;
197
198        retval = stream_recv(stream, &hello, sizeof hello);
199        if (retval == sizeof hello) {
200            CHECK(hello.version, OFP_VERSION);
201            CHECK(hello.type, OFPT_HELLO);
202            CHECK(hello.length, htons(sizeof hello));
203            break;
204        } else {
205            CHECK_ERRNO(retval, -EAGAIN);
206        }
207
208        vconn_run(vconn);
209        CHECK_ERRNO(vconn_connect(vconn), EAGAIN);
210        vconn_run_wait(vconn);
211        vconn_connect_wait(vconn);
212        stream_recv_wait(stream);
213        poll_block();
214     }
215     stream_close(stream);
216     CHECK_ERRNO(vconn_connect(vconn), ECONNRESET);
217     vconn_close(vconn);
218 }
219
220 /* Connects to a fake_pvconn with vconn_open(), accepts that connection and
221  * sends the 'out' bytes in 'out_size' to it (presumably an OFPT_HELLO
222  * message), then verifies that vconn_connect() reports
223  * 'expect_connect_error'. */
224 static void
225 test_send_hello(const char *type, const void *out, size_t out_size,
226                 int expect_connect_error)
227 {
228     struct fake_pvconn fpv;
229     struct vconn *vconn;
230     bool read_hello, connected;
231     struct ofpbuf *msg;
232     struct stream *stream;
233     size_t n_sent;
234
235     fpv_create(type, &fpv);
236     CHECK_ERRNO(vconn_open(fpv.vconn_name, OFP_VERSION, &vconn), 0);
237     vconn_run(vconn);
238     stream = fpv_accept(&fpv);
239     fpv_destroy(&fpv);
240
241     n_sent = 0;
242     while (n_sent < out_size) {
243         int retval;
244
245         retval = stream_send(stream, (char *) out + n_sent, out_size - n_sent);
246         if (retval > 0) {
247             n_sent += retval;
248         } else if (retval == -EAGAIN) {
249             stream_run(stream);
250             vconn_run(vconn);
251             stream_recv_wait(stream);
252             vconn_connect_wait(vconn);
253             vconn_run_wait(vconn);
254             poll_block();
255         } else {
256             ovs_fatal(0, "stream_send returned unexpected value %d", retval);
257         }
258     }
259
260     read_hello = connected = false;
261     for (;;) {
262        if (!read_hello) {
263            struct ofp_header hello;
264            int retval = stream_recv(stream, &hello, sizeof hello);
265            if (retval == sizeof hello) {
266                CHECK(hello.version, OFP_VERSION);
267                CHECK(hello.type, OFPT_HELLO);
268                CHECK(hello.length, htons(sizeof hello));
269                read_hello = true;
270            } else {
271                CHECK_ERRNO(retval, -EAGAIN);
272            }
273        }
274
275        vconn_run(vconn);
276        if (!connected) {
277            int error = vconn_connect(vconn);
278            if (error == expect_connect_error) {
279                if (!error) {
280                    connected = true;
281                } else {
282                    stream_close(stream);
283                    vconn_close(vconn);
284                    return;
285                }
286            } else {
287                CHECK_ERRNO(error, EAGAIN);
288            }
289        }
290
291        if (read_hello && connected) {
292            break;
293        }
294
295        vconn_run_wait(vconn);
296        if (!connected) {
297            vconn_connect_wait(vconn);
298        }
299        if (!read_hello) {
300            stream_recv_wait(stream);
301        }
302        poll_block();
303     }
304     stream_close(stream);
305     CHECK_ERRNO(vconn_recv(vconn, &msg), EOF);
306     vconn_close(vconn);
307 }
308
309 /* Try connecting and sending a normal hello, which should succeed. */
310 static void
311 test_send_plain_hello(int argc UNUSED, char *argv[])
312 {
313     const char *type = argv[1];
314     struct ofp_header hello;
315
316     hello.version = OFP_VERSION;
317     hello.type = OFPT_HELLO;
318     hello.length = htons(sizeof hello);
319     hello.xid = htonl(0x12345678);
320     test_send_hello(type, &hello, sizeof hello, 0);
321 }
322
323 /* Try connecting and sending an extra-long hello, which should succeed (since
324  * the specification says that implementations must accept and ignore extra
325  * data). */
326 static void
327 test_send_long_hello(int argc UNUSED, char *argv[])
328 {
329     const char *type = argv[1];
330     struct ofp_header hello;
331     char buffer[sizeof hello * 2];
332
333     hello.version = OFP_VERSION;
334     hello.type = OFPT_HELLO;
335     hello.length = htons(sizeof buffer);
336     hello.xid = htonl(0x12345678);
337     memset(buffer, 0, sizeof buffer);
338     memcpy(buffer, &hello, sizeof hello);
339     test_send_hello(type, buffer, sizeof buffer, 0);
340 }
341
342 /* Try connecting and sending an echo request instead of a hello, which should
343  * fail with EPROTO. */
344 static void
345 test_send_echo_hello(int argc UNUSED, char *argv[])
346 {
347     const char *type = argv[1];
348     struct ofp_header echo;
349
350     echo.version = OFP_VERSION;
351     echo.type = OFPT_ECHO_REQUEST;
352     echo.length = htons(sizeof echo);
353     echo.xid = htonl(0x89abcdef);
354     test_send_hello(type, &echo, sizeof echo, EPROTO);
355 }
356
357 /* Try connecting and sending a hello packet that has its length field as 0,
358  * which should fail with EPROTO. */
359 static void
360 test_send_short_hello(int argc UNUSED, char *argv[])
361 {
362     const char *type = argv[1];
363     struct ofp_header hello;
364
365     memset(&hello, 0, sizeof hello);
366     test_send_hello(type, &hello, sizeof hello, EPROTO);
367 }
368
369 /* Try connecting and sending a hello packet that has a bad version, which
370  * should fail with EPROTO. */
371 static void
372 test_send_invalid_version_hello(int argc UNUSED, char *argv[])
373 {
374     const char *type = argv[1];
375     struct ofp_header hello;
376
377     hello.version = OFP_VERSION - 1;
378     hello.type = OFPT_HELLO;
379     hello.length = htons(sizeof hello);
380     hello.xid = htonl(0x12345678);
381     test_send_hello(type, &hello, sizeof hello, EPROTO);
382 }
383
384 static const struct command commands[] = {
385     {"refuse-connection", 1, 1, test_refuse_connection},
386     {"accept-then-close", 1, 1, test_accept_then_close},
387     {"read-hello", 1, 1, test_read_hello},
388     {"send-plain-hello", 1, 1, test_send_plain_hello},
389     {"send-long-hello", 1, 1, test_send_long_hello},
390     {"send-echo-hello", 1, 1, test_send_echo_hello},
391     {"send-short-hello", 1, 1, test_send_short_hello},
392     {"send-invalid-version-hello", 1, 1, test_send_invalid_version_hello},
393     {NULL, 0, 0, NULL},
394 };
395
396 int
397 main(int argc, char *argv[])
398 {
399     set_program_name(argv[0]);
400     time_init();
401     vlog_init();
402     vlog_set_levels(VLM_ANY_MODULE, VLF_ANY_FACILITY, VLL_EMER);
403     vlog_set_levels(VLM_ANY_MODULE, VLF_CONSOLE, VLL_DBG);
404     signal(SIGPIPE, SIG_IGN);
405
406     time_alarm(10);
407
408     run_command(argc - 1, argv + 1, commands);
409
410     return 0;
411 }