Branch data Line data Source code
1 : : /*
2 : : * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc.
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 : : #undef NDEBUG
19 : : #include <assert.h>
20 : : #include <errno.h>
21 : : #include <inttypes.h>
22 : : #include <signal.h>
23 : : #include <stdlib.h>
24 : : #include <unistd.h>
25 : : #include "command-line.h"
26 : : #include "fatal-signal.h"
27 : : #include "openflow/openflow.h"
28 : : #include "openvswitch/ofp-msgs.h"
29 : : #include "openvswitch/ofp-util.h"
30 : : #include "openvswitch/ofpbuf.h"
31 : : #include "openvswitch/vconn.h"
32 : : #include "openvswitch/vlog.h"
33 : : #include "ovstest.h"
34 : : #include "poll-loop.h"
35 : : #include "socket-util.h"
36 : : #include "stream.h"
37 : : #include "stream-ssl.h"
38 : : #include "timeval.h"
39 : : #include "util.h"
40 : :
41 : : struct fake_pvconn {
42 : : const char *type;
43 : : char *pvconn_name;
44 : : char *vconn_name;
45 : : struct pstream *pstream;
46 : : };
47 : :
48 : : static void
49 : 60 : check(int a, int b, const char *as, const char *file, int line)
50 : : {
51 [ - + ]: 60 : if (a != b) {
52 : 0 : ovs_fatal(0, "%s:%d: %s is %d but should be %d", file, line, as, a, b);
53 : : }
54 : 60 : }
55 : :
56 : :
57 : : #define CHECK(A, B) check(A, B, #A, __FILE__, __LINE__)
58 : :
59 : : static void
60 : 86 : check_errno(int a, int b, const char *as, const char *file, int line)
61 : : {
62 [ - + ]: 86 : if (a != b) {
63 : 0 : char *str_b = xstrdup(ovs_strerror(abs(b)));
64 : 0 : ovs_fatal(0, "%s:%d: %s is %d (%s) but should be %d (%s)",
65 : : file, line, as, a, ovs_strerror(abs(a)), b, str_b);
66 : : }
67 : 86 : }
68 : :
69 : : #define CHECK_ERRNO(A, B) check_errno(A, B, #A, __FILE__, __LINE__)
70 : :
71 : : static void
72 : 24 : fpv_create(const char *type, struct fake_pvconn *fpv)
73 : : {
74 : : #ifdef HAVE_OPENSSL
75 [ + + ]: 24 : if (!strcmp(type, "ssl")) {
76 : 8 : stream_ssl_set_private_key_file("testpki-privkey.pem");
77 : 8 : stream_ssl_set_certificate_file("testpki-cert.pem");
78 : 8 : stream_ssl_set_ca_cert_file("testpki-cacert.pem", false);
79 : : }
80 : : #endif
81 : :
82 : 24 : fpv->type = type;
83 [ + + ]: 24 : if (!strcmp(type, "unix")) {
84 : : static int unix_count = 0;
85 : : char *bind_path;
86 : :
87 : 8 : bind_path = xasprintf("fake-pvconn.%d", unix_count++);
88 : 8 : fpv->pvconn_name = xasprintf("punix:%s", bind_path);
89 : 8 : fpv->vconn_name = xasprintf("unix:%s", bind_path);
90 : 8 : CHECK_ERRNO(pstream_open(fpv->pvconn_name, &fpv->pstream,
91 : : DSCP_DEFAULT), 0);
92 : 8 : free(bind_path);
93 [ + + ][ + - ]: 32 : } else if (!strcmp(type, "tcp") || !strcmp(type, "ssl")) {
94 : 16 : char *s, *port, *save_ptr = NULL;
95 : : char *open_name;
96 : :
97 : 16 : open_name = xasprintf("p%s:0:127.0.0.1", type);
98 : 16 : CHECK_ERRNO(pstream_open(open_name, &fpv->pstream, DSCP_DEFAULT), 0);
99 : :
100 : : /* Extract bound port number from pstream name. */
101 : 16 : s = xstrdup(pstream_get_name(fpv->pstream));
102 : 16 : strtok_r(s, ":", &save_ptr);
103 : 16 : port = strtok_r(NULL, ":", &save_ptr);
104 : :
105 : : /* Save info. */
106 : 16 : fpv->pvconn_name = xstrdup(pstream_get_name(fpv->pstream));
107 : 16 : fpv->vconn_name = xasprintf("%s:127.0.0.1:%s", type, port);
108 : :
109 : 16 : free(open_name);
110 : 16 : free(s);
111 : : } else {
112 : 0 : abort();
113 : : }
114 : 24 : }
115 : :
116 : : static struct stream *
117 : 21 : fpv_accept(struct fake_pvconn *fpv)
118 : : {
119 : : struct stream *stream;
120 : :
121 : 21 : CHECK_ERRNO(pstream_accept_block(fpv->pstream, &stream), 0);
122 : :
123 : 21 : return stream;
124 : : }
125 : :
126 : : static void
127 : 30 : fpv_close(struct fake_pvconn *fpv)
128 : : {
129 : 30 : pstream_close(fpv->pstream);
130 : 30 : fpv->pstream = NULL;
131 : 30 : }
132 : :
133 : : static void
134 : 24 : fpv_destroy(struct fake_pvconn *fpv)
135 : : {
136 : 24 : fpv_close(fpv);
137 : 24 : free(fpv->pvconn_name);
138 : 24 : free(fpv->vconn_name);
139 : 24 : }
140 : :
141 : : /* Connects to a fake_pvconn with vconn_open(), then closes the listener and
142 : : * verifies that vconn_connect() reports 'expected_error'. */
143 : : static void
144 : 3 : test_refuse_connection(struct ovs_cmdl_context *ctx)
145 : : {
146 : 3 : const char *type = ctx->argv[1];
147 : : struct fake_pvconn fpv;
148 : : struct vconn *vconn;
149 : : int error;
150 : :
151 : 3 : fpv_create(type, &fpv);
152 : 3 : CHECK_ERRNO(vconn_open(fpv.vconn_name, 0, DSCP_DEFAULT, &vconn), 0);
153 : 3 : fpv_close(&fpv);
154 : 3 : vconn_run(vconn);
155 : :
156 : 3 : error = vconn_connect_block(vconn);
157 [ + + ]: 3 : if (!strcmp(type, "tcp")) {
158 [ - + ][ # # ]: 1 : if (error != ECONNRESET && error != EPIPE
159 : : #ifdef _WIN32
160 : : && error != WSAECONNRESET
161 : : #endif
162 : : ) {
163 : 0 : ovs_fatal(0, "unexpected vconn_connect() return value %d (%s)",
164 : : error, ovs_strerror(error));
165 : : }
166 [ + + ]: 2 : } else if (!strcmp(type, "unix")) {
167 : : #ifndef _WIN32
168 : 1 : CHECK_ERRNO(error, EPIPE);
169 : : #else
170 : : CHECK_ERRNO(error, WSAECONNRESET);
171 : : #endif
172 [ + - ]: 1 : } else if (!strcmp(type, "ssl")) {
173 [ - + ][ # # ]: 1 : if (error != EPROTO && error != ECONNRESET) {
174 : 0 : ovs_fatal(0, "unexpected vconn_connect() return value %d (%s)",
175 : : error, ovs_strerror(error));
176 : : }
177 : : } else {
178 : 0 : ovs_fatal(0, "invalid connection type %s", type);
179 : : }
180 : :
181 : 3 : vconn_close(vconn);
182 : 3 : fpv_destroy(&fpv);
183 : 3 : }
184 : :
185 : : /* Connects to a fake_pvconn with vconn_open(), accepts that connection and
186 : : * closes it immediately, and verifies that vconn_connect() reports
187 : : * 'expected_error'. */
188 : : static void
189 : 3 : test_accept_then_close(struct ovs_cmdl_context *ctx)
190 : : {
191 : 3 : const char *type = ctx->argv[1];
192 : : struct fake_pvconn fpv;
193 : : struct vconn *vconn;
194 : : int error;
195 : :
196 : 3 : fpv_create(type, &fpv);
197 : 3 : CHECK_ERRNO(vconn_open(fpv.vconn_name, 0, DSCP_DEFAULT, &vconn), 0);
198 : 3 : vconn_run(vconn);
199 : 3 : stream_close(fpv_accept(&fpv));
200 : 3 : fpv_close(&fpv);
201 : :
202 : 3 : error = vconn_connect_block(vconn);
203 [ + + ][ + + ]: 3 : if (!strcmp(type, "tcp") || !strcmp(type, "unix")) {
204 [ - + ][ # # ]: 2 : if (error != ECONNRESET && error != EPIPE
205 : : #ifdef _WIN32
206 : : && error != WSAECONNRESET
207 : : #endif
208 : : ) {
209 : 0 : ovs_fatal(0, "unexpected vconn_connect() return value %d (%s)",
210 : : error, ovs_strerror(error));
211 : : }
212 : : } else {
213 : 1 : CHECK_ERRNO(error, EPROTO);
214 : : }
215 : :
216 : 3 : vconn_close(vconn);
217 : 3 : fpv_destroy(&fpv);
218 : 3 : }
219 : :
220 : : /* Connects to a fake_pvconn with vconn_open(), accepts that connection and
221 : : * reads the hello message from it, then closes the connection and verifies
222 : : * that vconn_connect() reports 'expected_error'. */
223 : : static void
224 : 3 : test_read_hello(struct ovs_cmdl_context *ctx)
225 : : {
226 : 3 : const char *type = ctx->argv[1];
227 : : struct fake_pvconn fpv;
228 : : struct vconn *vconn;
229 : : struct stream *stream;
230 : : int error;
231 : :
232 : 3 : fpv_create(type, &fpv);
233 : 3 : CHECK_ERRNO(vconn_open(fpv.vconn_name, 0, DSCP_DEFAULT, &vconn), 0);
234 : 3 : vconn_run(vconn);
235 : 3 : stream = fpv_accept(&fpv);
236 : 3 : fpv_destroy(&fpv);
237 : : for (;;) {
238 : : struct ofp_header hello;
239 : : int retval;
240 : :
241 : 5 : retval = stream_recv(stream, &hello, sizeof hello);
242 [ + + ]: 5 : if (retval == sizeof hello) {
243 : : enum ofpraw raw;
244 : :
245 : 3 : CHECK(hello.version, OFP13_VERSION);
246 : 3 : CHECK(ofpraw_decode_partial(&raw, &hello, sizeof hello), 0);
247 : 3 : CHECK(raw, OFPRAW_OFPT_HELLO);
248 : 3 : CHECK(ntohs(hello.length), sizeof hello);
249 : 3 : break;
250 : : } else {
251 : 2 : CHECK_ERRNO(retval, -EAGAIN);
252 : : }
253 : :
254 : 2 : vconn_run(vconn);
255 : 2 : CHECK_ERRNO(vconn_connect(vconn), EAGAIN);
256 : 2 : vconn_run_wait(vconn);
257 : 2 : vconn_connect_wait(vconn);
258 : 2 : stream_recv_wait(stream);
259 : 2 : poll_block();
260 : 2 : }
261 : 3 : stream_close(stream);
262 : 3 : error = vconn_connect_block(vconn);
263 [ - + ][ # # ]: 3 : if (error != ECONNRESET && error != EPIPE) {
264 : 0 : ovs_fatal(0, "unexpected vconn_connect() return value %d (%s)",
265 : : error, ovs_strerror(error));
266 : : }
267 : 3 : vconn_close(vconn);
268 : 3 : }
269 : :
270 : : /* Connects to a fake_pvconn with vconn_open(), accepts that connection and
271 : : * sends the 'out' bytes in 'out_size' to it (presumably an OFPT_HELLO
272 : : * message), then verifies that vconn_connect() reports
273 : : * 'expect_connect_error'. */
274 : : static void
275 : 15 : test_send_hello(const char *type, const void *out, size_t out_size,
276 : : int expect_connect_error)
277 : : {
278 : : struct fake_pvconn fpv;
279 : : struct vconn *vconn;
280 : : bool read_hello, connected;
281 : : struct ofpbuf *msg;
282 : : struct stream *stream;
283 : : size_t n_sent;
284 : :
285 : 15 : fpv_create(type, &fpv);
286 : 15 : CHECK_ERRNO(vconn_open(fpv.vconn_name, 0, DSCP_DEFAULT, &vconn), 0);
287 : 15 : vconn_run(vconn);
288 : 15 : stream = fpv_accept(&fpv);
289 : 15 : fpv_destroy(&fpv);
290 : :
291 : 15 : n_sent = 0;
292 [ + + ]: 35 : while (n_sent < out_size) {
293 : : int retval;
294 : :
295 : 20 : retval = stream_send(stream, (char *) out + n_sent, out_size - n_sent);
296 [ + + ]: 20 : if (retval > 0) {
297 : 15 : n_sent += retval;
298 [ + - ]: 5 : } else if (retval == -EAGAIN) {
299 : 5 : stream_run(stream);
300 : 5 : vconn_run(vconn);
301 : 5 : stream_recv_wait(stream);
302 : 5 : vconn_connect_wait(vconn);
303 : 5 : vconn_run_wait(vconn);
304 : 5 : poll_block();
305 : : } else {
306 : 0 : ovs_fatal(0, "stream_send returned unexpected value %d", retval);
307 : : }
308 : : }
309 : :
310 : 15 : read_hello = connected = false;
311 : : for (;;) {
312 [ + - ]: 17 : if (!read_hello) {
313 : : struct ofp_header hello;
314 : 17 : int retval = stream_recv(stream, &hello, sizeof hello);
315 [ + + ]: 17 : if (retval == sizeof hello) {
316 : : enum ofpraw raw;
317 : :
318 : 12 : CHECK(hello.version, OFP13_VERSION);
319 : 12 : CHECK(ofpraw_decode_partial(&raw, &hello, sizeof hello), 0);
320 : 12 : CHECK(raw, OFPRAW_OFPT_HELLO);
321 : 12 : CHECK(ntohs(hello.length), sizeof hello);
322 : 12 : read_hello = true;
323 : : } else {
324 : 17 : CHECK_ERRNO(retval, -EAGAIN);
325 : : }
326 : : }
327 : :
328 : 17 : vconn_run(vconn);
329 [ + + ]: 17 : if (!connected) {
330 : 15 : int error = vconn_connect(vconn);
331 [ + - ]: 15 : if (error == expect_connect_error) {
332 [ + + ]: 15 : if (!error) {
333 : 6 : connected = true;
334 : : } else {
335 : 9 : stream_close(stream);
336 : 9 : vconn_close(vconn);
337 : 9 : return;
338 : : }
339 : : } else {
340 : 0 : CHECK_ERRNO(error, EAGAIN);
341 : : }
342 : : }
343 : :
344 [ + + ][ + - ]: 8 : if (read_hello && connected) {
345 : 6 : break;
346 : : }
347 : :
348 : 2 : vconn_run_wait(vconn);
349 [ - + ]: 2 : if (!connected) {
350 : 0 : vconn_connect_wait(vconn);
351 : : }
352 [ + - ]: 2 : if (!read_hello) {
353 : 2 : stream_recv_wait(stream);
354 : : }
355 : 2 : poll_block();
356 : 2 : }
357 : 6 : stream_close(stream);
358 : 6 : CHECK_ERRNO(vconn_recv_block(vconn, &msg), EOF);
359 : 6 : vconn_close(vconn);
360 : : }
361 : :
362 : : /* Try connecting and sending a normal hello, which should succeed. */
363 : : static void
364 : 3 : test_send_plain_hello(struct ovs_cmdl_context *ctx)
365 : : {
366 : 3 : const char *type = ctx->argv[1];
367 : : struct ofpbuf *hello;
368 : :
369 : 3 : hello = ofpraw_alloc_xid(OFPRAW_OFPT_HELLO, OFP13_VERSION,
370 : : htonl(0x12345678), 0);
371 : 3 : test_send_hello(type, hello->data, hello->size, 0);
372 : 3 : ofpbuf_delete(hello);
373 : 3 : }
374 : :
375 : : /* Try connecting and sending an extra-long hello, which should succeed (since
376 : : * the specification says that implementations must accept and ignore extra
377 : : * data). */
378 : : static void
379 : 3 : test_send_long_hello(struct ovs_cmdl_context *ctx)
380 : : {
381 : 3 : const char *type = ctx->argv[1];
382 : : struct ofpbuf *hello;
383 : : enum { EXTRA_BYTES = 8 };
384 : :
385 : 3 : hello = ofpraw_alloc_xid(OFPRAW_OFPT_HELLO, OFP13_VERSION,
386 : : htonl(0x12345678), EXTRA_BYTES);
387 : 3 : ofpbuf_put_zeros(hello, EXTRA_BYTES);
388 : 3 : ofpmsg_update_length(hello);
389 : 3 : test_send_hello(type, hello->data, hello->size, 0);
390 : 3 : ofpbuf_delete(hello);
391 : 3 : }
392 : :
393 : : /* Try connecting and sending an echo request instead of a hello, which should
394 : : * fail with EPROTO. */
395 : : static void
396 : 3 : test_send_echo_hello(struct ovs_cmdl_context *ctx)
397 : : {
398 : 3 : const char *type = ctx->argv[1];
399 : : struct ofpbuf *echo;
400 : :
401 : 3 : echo = ofpraw_alloc_xid(OFPRAW_OFPT_ECHO_REQUEST, OFP13_VERSION,
402 : : htonl(0x12345678), 0);
403 : 3 : test_send_hello(type, echo->data, echo->size, EPROTO);
404 : 3 : ofpbuf_delete(echo);
405 : 3 : }
406 : :
407 : : /* Try connecting and sending a hello packet that has its length field as 0,
408 : : * which should fail with EPROTO. */
409 : : static void
410 : 3 : test_send_short_hello(struct ovs_cmdl_context *ctx)
411 : : {
412 : 3 : const char *type = ctx->argv[1];
413 : : struct ofp_header hello;
414 : :
415 : 3 : memset(&hello, 0, sizeof hello);
416 : 3 : test_send_hello(type, &hello, sizeof hello, EPROTO);
417 : 3 : }
418 : :
419 : : /* Try connecting and sending a hello packet that has a bad version, which
420 : : * should fail with EPROTO. */
421 : : static void
422 : 3 : test_send_invalid_version_hello(struct ovs_cmdl_context *ctx)
423 : : {
424 : 3 : const char *type = ctx->argv[1];
425 : : struct ofpbuf *hello;
426 : :
427 : 3 : hello = ofpraw_alloc_xid(OFPRAW_OFPT_HELLO, OFP13_VERSION,
428 : : htonl(0x12345678), 0);
429 : 3 : ((struct ofp_header *) hello->data)->version = 0;
430 : 3 : test_send_hello(type, hello->data, hello->size, EPROTO);
431 : 3 : ofpbuf_delete(hello);
432 : 3 : }
433 : :
434 : : static const struct ovs_cmdl_command commands[] = {
435 : : {"refuse-connection", NULL, 1, 1, test_refuse_connection, OVS_RO},
436 : : {"accept-then-close", NULL, 1, 1, test_accept_then_close, OVS_RO},
437 : : {"read-hello", NULL, 1, 1, test_read_hello, OVS_RO},
438 : : {"send-plain-hello", NULL, 1, 1, test_send_plain_hello, OVS_RO},
439 : : {"send-long-hello", NULL, 1, 1, test_send_long_hello, OVS_RO},
440 : : {"send-echo-hello", NULL, 1, 1, test_send_echo_hello, OVS_RO},
441 : : {"send-short-hello", NULL, 1, 1, test_send_short_hello, OVS_RO},
442 : : {"send-invalid-version-hello", NULL, 1, 1, test_send_invalid_version_hello, OVS_RO},
443 : : {NULL, NULL, 0, 0, NULL, OVS_RO},
444 : : };
445 : :
446 : : static void
447 : 24 : test_vconn_main(int argc, char *argv[])
448 : : {
449 : 72 : struct ovs_cmdl_context ctx = {
450 : 24 : .argc = argc - 1,
451 : 24 : .argv = argv + 1,
452 : : };
453 : :
454 : 24 : set_program_name(argv[0]);
455 : 24 : vlog_set_levels(NULL, VLF_ANY_DESTINATION, VLL_EMER);
456 : 24 : vlog_set_levels(NULL, VLF_CONSOLE, VLL_DBG);
457 : 24 : fatal_ignore_sigpipe();
458 : :
459 : 24 : time_alarm(10);
460 : :
461 : 24 : ovs_cmdl_run_command(&ctx, commands);
462 : 24 : }
463 : :
464 : 1222 : OVSTEST_REGISTER("test-vconn", test_vconn_main);
|