Merge "master" into "next".
[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 #ifdef HAVE_OPENSSL
69     if (!strcmp(type, "ssl")) {
70         stream_ssl_set_private_key_file("testpki-privkey.pem");
71         stream_ssl_set_certificate_file("testpki-cert.pem");
72         stream_ssl_set_ca_cert_file("testpki-cacert.pem", false);
73     }
74 #endif
75
76     fpv->type = type;
77     if (!strcmp(type, "unix")) {
78         static int unix_count = 0;
79         char *bind_path;
80
81         bind_path = xasprintf("fake-pvconn.%d", unix_count++);
82         fpv->pvconn_name = xasprintf("punix:%s", bind_path);
83         fpv->vconn_name = xasprintf("unix:%s", bind_path);
84         CHECK_ERRNO(pstream_open(fpv->pvconn_name, &fpv->pstream), 0);
85         free(bind_path);
86     } else if (!strcmp(type, "tcp") || !strcmp(type, "ssl")) {
87         char *s, *port, *save_ptr = NULL;
88         char *open_name;
89
90         open_name = xasprintf("p%s:0:127.0.0.1", type);
91         CHECK_ERRNO(pstream_open(open_name, &fpv->pstream), 0);
92
93         /* Extract bound port number from pstream name. */
94         s = xstrdup(pstream_get_name(fpv->pstream));
95         strtok_r(s, ":", &save_ptr);
96         port = strtok_r(NULL, ":", &save_ptr);
97
98         /* Save info. */
99         fpv->pvconn_name = xstrdup(pstream_get_name(fpv->pstream));
100         fpv->vconn_name = xasprintf("%s:127.0.0.1:%s", type, port);
101
102         free(open_name);
103         free(s);
104     } else {
105         abort();
106     }
107 }
108
109 static struct stream *
110 fpv_accept(struct fake_pvconn *fpv)
111 {
112     struct stream *stream;
113
114     CHECK_ERRNO(pstream_accept_block(fpv->pstream, &stream), 0);
115
116     return stream;
117 }
118
119 static void
120 fpv_close(struct fake_pvconn *fpv)
121 {
122     pstream_close(fpv->pstream);
123     fpv->pstream = NULL;
124 }
125
126 static void
127 fpv_destroy(struct fake_pvconn *fpv)
128 {
129     fpv_close(fpv);
130     free(fpv->pvconn_name);
131     free(fpv->vconn_name);
132 }
133
134 /* Connects to a fake_pvconn with vconn_open(), then closes the listener and
135  * verifies that vconn_connect() reports 'expected_error'. */
136 static void
137 test_refuse_connection(int argc OVS_UNUSED, char *argv[])
138 {
139     const char *type = argv[1];
140     int expected_error;
141     struct fake_pvconn fpv;
142     struct vconn *vconn;
143
144     expected_error = !strcmp(type, "unix") ? EPIPE : ECONNRESET;
145
146     fpv_create(type, &fpv);
147     CHECK_ERRNO(vconn_open(fpv.vconn_name, OFP_VERSION, &vconn), 0);
148     fpv_close(&fpv);
149     vconn_run(vconn);
150     CHECK_ERRNO(vconn_connect(vconn), expected_error);
151     vconn_close(vconn);
152     fpv_destroy(&fpv);
153 }
154
155 /* Connects to a fake_pvconn with vconn_open(), accepts that connection and
156  * closes it immediately, and verifies that vconn_connect() reports
157  * 'expected_error'. */
158 static void
159 test_accept_then_close(int argc OVS_UNUSED, char *argv[])
160 {
161     const char *type = argv[1];
162     int expected_error;
163     struct fake_pvconn fpv;
164     struct vconn *vconn;
165
166     expected_error = (!strcmp(type, "unix") ? EPIPE
167                       : !strcmp(type, "tcp") ? ECONNRESET
168                       : EPROTO);
169
170     fpv_create(type, &fpv);
171     CHECK_ERRNO(vconn_open(fpv.vconn_name, OFP_VERSION, &vconn), 0);
172     vconn_run(vconn);
173     stream_close(fpv_accept(&fpv));
174     fpv_close(&fpv);
175     CHECK_ERRNO(vconn_connect(vconn), expected_error);
176     vconn_close(vconn);
177     fpv_destroy(&fpv);
178 }
179
180 /* Connects to a fake_pvconn with vconn_open(), accepts that connection and
181  * reads the hello message from it, then closes the connection and verifies
182  * that vconn_connect() reports 'expected_error'. */
183 static void
184 test_read_hello(int argc OVS_UNUSED, char *argv[])
185 {
186     const char *type = argv[1];
187     struct fake_pvconn fpv;
188     struct vconn *vconn;
189     struct stream *stream;
190
191     fpv_create(type, &fpv);
192     CHECK_ERRNO(vconn_open(fpv.vconn_name, OFP_VERSION, &vconn), 0);
193     vconn_run(vconn);
194     stream = fpv_accept(&fpv);
195     fpv_destroy(&fpv);
196     for (;;) {
197        struct ofp_header hello;
198        int retval;
199
200        retval = stream_recv(stream, &hello, sizeof hello);
201        if (retval == sizeof hello) {
202            CHECK(hello.version, OFP_VERSION);
203            CHECK(hello.type, OFPT_HELLO);
204            CHECK(hello.length, htons(sizeof hello));
205            break;
206        } else {
207            CHECK_ERRNO(retval, -EAGAIN);
208        }
209
210        vconn_run(vconn);
211        CHECK_ERRNO(vconn_connect(vconn), EAGAIN);
212        vconn_run_wait(vconn);
213        vconn_connect_wait(vconn);
214        stream_recv_wait(stream);
215        poll_block();
216     }
217     stream_close(stream);
218     CHECK_ERRNO(vconn_connect(vconn), ECONNRESET);
219     vconn_close(vconn);
220 }
221
222 /* Connects to a fake_pvconn with vconn_open(), accepts that connection and
223  * sends the 'out' bytes in 'out_size' to it (presumably an OFPT_HELLO
224  * message), then verifies that vconn_connect() reports
225  * 'expect_connect_error'. */
226 static void
227 test_send_hello(const char *type, const void *out, size_t out_size,
228                 int expect_connect_error)
229 {
230     struct fake_pvconn fpv;
231     struct vconn *vconn;
232     bool read_hello, connected;
233     struct ofpbuf *msg;
234     struct stream *stream;
235     size_t n_sent;
236
237     fpv_create(type, &fpv);
238     CHECK_ERRNO(vconn_open(fpv.vconn_name, OFP_VERSION, &vconn), 0);
239     vconn_run(vconn);
240     stream = fpv_accept(&fpv);
241     fpv_destroy(&fpv);
242
243     n_sent = 0;
244     while (n_sent < out_size) {
245         int retval;
246
247         retval = stream_send(stream, (char *) out + n_sent, out_size - n_sent);
248         if (retval > 0) {
249             n_sent += retval;
250         } else if (retval == -EAGAIN) {
251             stream_run(stream);
252             vconn_run(vconn);
253             stream_recv_wait(stream);
254             vconn_connect_wait(vconn);
255             vconn_run_wait(vconn);
256             poll_block();
257         } else {
258             ovs_fatal(0, "stream_send returned unexpected value %d", retval);
259         }
260     }
261
262     read_hello = connected = false;
263     for (;;) {
264        if (!read_hello) {
265            struct ofp_header hello;
266            int retval = stream_recv(stream, &hello, sizeof hello);
267            if (retval == sizeof hello) {
268                CHECK(hello.version, OFP_VERSION);
269                CHECK(hello.type, OFPT_HELLO);
270                CHECK(hello.length, htons(sizeof hello));
271                read_hello = true;
272            } else {
273                CHECK_ERRNO(retval, -EAGAIN);
274            }
275        }
276
277        vconn_run(vconn);
278        if (!connected) {
279            int error = vconn_connect(vconn);
280            if (error == expect_connect_error) {
281                if (!error) {
282                    connected = true;
283                } else {
284                    stream_close(stream);
285                    vconn_close(vconn);
286                    return;
287                }
288            } else {
289                CHECK_ERRNO(error, EAGAIN);
290            }
291        }
292
293        if (read_hello && connected) {
294            break;
295        }
296
297        vconn_run_wait(vconn);
298        if (!connected) {
299            vconn_connect_wait(vconn);
300        }
301        if (!read_hello) {
302            stream_recv_wait(stream);
303        }
304        poll_block();
305     }
306     stream_close(stream);
307     CHECK_ERRNO(vconn_recv(vconn, &msg), EOF);
308     vconn_close(vconn);
309 }
310
311 /* Try connecting and sending a normal hello, which should succeed. */
312 static void
313 test_send_plain_hello(int argc OVS_UNUSED, char *argv[])
314 {
315     const char *type = argv[1];
316     struct ofp_header hello;
317
318     hello.version = OFP_VERSION;
319     hello.type = OFPT_HELLO;
320     hello.length = htons(sizeof hello);
321     hello.xid = htonl(0x12345678);
322     test_send_hello(type, &hello, sizeof hello, 0);
323 }
324
325 /* Try connecting and sending an extra-long hello, which should succeed (since
326  * the specification says that implementations must accept and ignore extra
327  * data). */
328 static void
329 test_send_long_hello(int argc OVS_UNUSED, char *argv[])
330 {
331     const char *type = argv[1];
332     struct ofp_header hello;
333     char buffer[sizeof hello * 2];
334
335     hello.version = OFP_VERSION;
336     hello.type = OFPT_HELLO;
337     hello.length = htons(sizeof buffer);
338     hello.xid = htonl(0x12345678);
339     memset(buffer, 0, sizeof buffer);
340     memcpy(buffer, &hello, sizeof hello);
341     test_send_hello(type, buffer, sizeof buffer, 0);
342 }
343
344 /* Try connecting and sending an echo request instead of a hello, which should
345  * fail with EPROTO. */
346 static void
347 test_send_echo_hello(int argc OVS_UNUSED, char *argv[])
348 {
349     const char *type = argv[1];
350     struct ofp_header echo;
351
352     echo.version = OFP_VERSION;
353     echo.type = OFPT_ECHO_REQUEST;
354     echo.length = htons(sizeof echo);
355     echo.xid = htonl(0x89abcdef);
356     test_send_hello(type, &echo, sizeof echo, EPROTO);
357 }
358
359 /* Try connecting and sending a hello packet that has its length field as 0,
360  * which should fail with EPROTO. */
361 static void
362 test_send_short_hello(int argc OVS_UNUSED, char *argv[])
363 {
364     const char *type = argv[1];
365     struct ofp_header hello;
366
367     memset(&hello, 0, sizeof hello);
368     test_send_hello(type, &hello, sizeof hello, EPROTO);
369 }
370
371 /* Try connecting and sending a hello packet that has a bad version, which
372  * should fail with EPROTO. */
373 static void
374 test_send_invalid_version_hello(int argc OVS_UNUSED, char *argv[])
375 {
376     const char *type = argv[1];
377     struct ofp_header hello;
378
379     hello.version = OFP_VERSION - 1;
380     hello.type = OFPT_HELLO;
381     hello.length = htons(sizeof hello);
382     hello.xid = htonl(0x12345678);
383     test_send_hello(type, &hello, sizeof hello, EPROTO);
384 }
385
386 static const struct command commands[] = {
387     {"refuse-connection", 1, 1, test_refuse_connection},
388     {"accept-then-close", 1, 1, test_accept_then_close},
389     {"read-hello", 1, 1, test_read_hello},
390     {"send-plain-hello", 1, 1, test_send_plain_hello},
391     {"send-long-hello", 1, 1, test_send_long_hello},
392     {"send-echo-hello", 1, 1, test_send_echo_hello},
393     {"send-short-hello", 1, 1, test_send_short_hello},
394     {"send-invalid-version-hello", 1, 1, test_send_invalid_version_hello},
395     {NULL, 0, 0, NULL},
396 };
397
398 int
399 main(int argc OVS_UNUSED, char *argv[])
400 {
401     set_program_name(argv[0]);
402     time_init();
403     vlog_init();
404     vlog_set_levels(VLM_ANY_MODULE, VLF_ANY_FACILITY, VLL_EMER);
405     vlog_set_levels(VLM_ANY_MODULE, VLF_CONSOLE, VLL_DBG);
406     signal(SIGPIPE, SIG_IGN);
407
408     time_alarm(10);
409
410     run_command(argc - 1, argv + 1, commands);
411
412     return 0;
413 }