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