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