]>
Commit | Line | Data |
---|---|---|
48d973e3 | 1 | /* |
13c952ca | 2 | * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2017, 2019 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" | |
64c96779 | 29 | #include "openvswitch/ofpbuf.h" |
d271907f BW |
30 | #include "openvswitch/vconn.h" |
31 | #include "openvswitch/vlog.h" | |
3f636c7e | 32 | #include "ovstest.h" |
fd016ae3 | 33 | #include "openvswitch/poll-loop.h" |
48d973e3 | 34 | #include "socket-util.h" |
dc681276 BP |
35 | #include "stream.h" |
36 | #include "stream-ssl.h" | |
48d973e3 BP |
37 | #include "timeval.h" |
38 | #include "util.h" | |
48d973e3 | 39 | |
04895042 IM |
40 | #define TIMEOUT 10 |
41 | ||
48d973e3 BP |
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) { | |
13a233f7 | 64 | char *str_b = xstrdup(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 | |
1636c761 | 145 | test_refuse_connection(struct ovs_cmdl_context *ctx) |
48d973e3 | 146 | { |
1636c761 | 147 | const char *type = ctx->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 | 156 | |
04895042 | 157 | error = vconn_connect_block(vconn, (TIMEOUT - 2) * 1000); |
b3229309 | 158 | if (!strcmp(type, "tcp")) { |
04895042 | 159 | if (error != ECONNRESET && error != EPIPE && error != ETIMEDOUT |
85ddb187 GS |
160 | #ifdef _WIN32 |
161 | && error != WSAECONNRESET | |
162 | #endif | |
163 | ) { | |
b3229309 | 164 | ovs_fatal(0, "unexpected vconn_connect() return value %d (%s)", |
10a89ef0 | 165 | error, ovs_strerror(error)); |
b3229309 | 166 | } |
dd445b79 EM |
167 | } else if (!strcmp(type, "unix")) { |
168 | CHECK_ERRNO(error, EPIPE); | |
169 | } else if (!strcmp(type, "ssl")) { | |
04895042 | 170 | if (error != EPROTO && error != ECONNRESET && error != ETIMEDOUT) { |
dd445b79 | 171 | ovs_fatal(0, "unexpected vconn_connect() return value %d (%s)", |
10a89ef0 | 172 | error, ovs_strerror(error)); |
dd445b79 | 173 | } |
b3229309 | 174 | } else { |
dd445b79 | 175 | ovs_fatal(0, "invalid connection type %s", type); |
b3229309 BP |
176 | } |
177 | ||
48d973e3 BP |
178 | vconn_close(vconn); |
179 | fpv_destroy(&fpv); | |
180 | } | |
181 | ||
182 | /* Connects to a fake_pvconn with vconn_open(), accepts that connection and | |
183 | * closes it immediately, and verifies that vconn_connect() reports | |
184 | * 'expected_error'. */ | |
185 | static void | |
1636c761 | 186 | test_accept_then_close(struct ovs_cmdl_context *ctx) |
48d973e3 | 187 | { |
1636c761 | 188 | const char *type = ctx->argv[1]; |
48d973e3 BP |
189 | struct fake_pvconn fpv; |
190 | struct vconn *vconn; | |
48d84b17 | 191 | int error; |
d27ce529 | 192 | |
48d973e3 | 193 | fpv_create(type, &fpv); |
82c8c53c | 194 | CHECK_ERRNO(vconn_open(fpv.vconn_name, 0, DSCP_DEFAULT, &vconn), 0); |
60cb3eb8 | 195 | vconn_run(vconn); |
dc681276 | 196 | stream_close(fpv_accept(&fpv)); |
48d973e3 | 197 | fpv_close(&fpv); |
48d84b17 | 198 | |
04895042 | 199 | error = vconn_connect_block(vconn, -1); |
48d84b17 | 200 | if (!strcmp(type, "tcp") || !strcmp(type, "unix")) { |
85ddb187 GS |
201 | if (error != ECONNRESET && error != EPIPE |
202 | #ifdef _WIN32 | |
203 | && error != WSAECONNRESET | |
204 | #endif | |
205 | ) { | |
48d84b17 | 206 | ovs_fatal(0, "unexpected vconn_connect() return value %d (%s)", |
10a89ef0 | 207 | error, ovs_strerror(error)); |
48d84b17 BP |
208 | } |
209 | } else { | |
210 | CHECK_ERRNO(error, EPROTO); | |
211 | } | |
212 | ||
48d973e3 BP |
213 | vconn_close(vconn); |
214 | fpv_destroy(&fpv); | |
215 | } | |
216 | ||
217 | /* Connects to a fake_pvconn with vconn_open(), accepts that connection and | |
218 | * reads the hello message from it, then closes the connection and verifies | |
219 | * that vconn_connect() reports 'expected_error'. */ | |
220 | static void | |
1636c761 | 221 | test_read_hello(struct ovs_cmdl_context *ctx) |
48d973e3 | 222 | { |
1636c761 | 223 | const char *type = ctx->argv[1]; |
48d973e3 BP |
224 | struct fake_pvconn fpv; |
225 | struct vconn *vconn; | |
dc681276 | 226 | struct stream *stream; |
b3229309 | 227 | int error; |
48d973e3 BP |
228 | |
229 | fpv_create(type, &fpv); | |
82c8c53c | 230 | CHECK_ERRNO(vconn_open(fpv.vconn_name, 0, DSCP_DEFAULT, &vconn), 0); |
60cb3eb8 | 231 | vconn_run(vconn); |
dc681276 | 232 | stream = fpv_accept(&fpv); |
48d973e3 | 233 | fpv_destroy(&fpv); |
48d973e3 BP |
234 | for (;;) { |
235 | struct ofp_header hello; | |
236 | int retval; | |
237 | ||
dc681276 | 238 | retval = stream_recv(stream, &hello, sizeof hello); |
48d973e3 | 239 | if (retval == sizeof hello) { |
982697a4 BP |
240 | enum ofpraw raw; |
241 | ||
13c952ca | 242 | CHECK(hello.version, OFP15_VERSION); |
982697a4 BP |
243 | CHECK(ofpraw_decode_partial(&raw, &hello, sizeof hello), 0); |
244 | CHECK(raw, OFPRAW_OFPT_HELLO); | |
d84d4b88 | 245 | CHECK(ntohs(hello.length), sizeof hello); |
48d973e3 BP |
246 | break; |
247 | } else { | |
dc681276 | 248 | CHECK_ERRNO(retval, -EAGAIN); |
48d973e3 BP |
249 | } |
250 | ||
60cb3eb8 | 251 | vconn_run(vconn); |
dc681276 | 252 | CHECK_ERRNO(vconn_connect(vconn), EAGAIN); |
60cb3eb8 | 253 | vconn_run_wait(vconn); |
48d973e3 | 254 | vconn_connect_wait(vconn); |
dc681276 | 255 | stream_recv_wait(stream); |
48d973e3 BP |
256 | poll_block(); |
257 | } | |
dc681276 | 258 | stream_close(stream); |
04895042 | 259 | error = vconn_connect_block(vconn, -1); |
b3229309 BP |
260 | if (error != ECONNRESET && error != EPIPE) { |
261 | ovs_fatal(0, "unexpected vconn_connect() return value %d (%s)", | |
10a89ef0 | 262 | error, ovs_strerror(error)); |
b3229309 | 263 | } |
48d973e3 BP |
264 | vconn_close(vconn); |
265 | } | |
266 | ||
267 | /* Connects to a fake_pvconn with vconn_open(), accepts that connection and | |
268 | * sends the 'out' bytes in 'out_size' to it (presumably an OFPT_HELLO | |
269 | * message), then verifies that vconn_connect() reports | |
270 | * 'expect_connect_error'. */ | |
271 | static void | |
272 | test_send_hello(const char *type, const void *out, size_t out_size, | |
273 | int expect_connect_error) | |
274 | { | |
275 | struct fake_pvconn fpv; | |
276 | struct vconn *vconn; | |
277 | bool read_hello, connected; | |
278 | struct ofpbuf *msg; | |
dc681276 BP |
279 | struct stream *stream; |
280 | size_t n_sent; | |
48d973e3 BP |
281 | |
282 | fpv_create(type, &fpv); | |
82c8c53c | 283 | CHECK_ERRNO(vconn_open(fpv.vconn_name, 0, DSCP_DEFAULT, &vconn), 0); |
60cb3eb8 | 284 | vconn_run(vconn); |
dc681276 | 285 | stream = fpv_accept(&fpv); |
48d973e3 BP |
286 | fpv_destroy(&fpv); |
287 | ||
dc681276 BP |
288 | n_sent = 0; |
289 | while (n_sent < out_size) { | |
290 | int retval; | |
291 | ||
292 | retval = stream_send(stream, (char *) out + n_sent, out_size - n_sent); | |
293 | if (retval > 0) { | |
294 | n_sent += retval; | |
295 | } else if (retval == -EAGAIN) { | |
296 | stream_run(stream); | |
297 | vconn_run(vconn); | |
298 | stream_recv_wait(stream); | |
299 | vconn_connect_wait(vconn); | |
300 | vconn_run_wait(vconn); | |
301 | poll_block(); | |
302 | } else { | |
303 | ovs_fatal(0, "stream_send returned unexpected value %d", retval); | |
304 | } | |
305 | } | |
48d973e3 BP |
306 | |
307 | read_hello = connected = false; | |
308 | for (;;) { | |
309 | if (!read_hello) { | |
310 | struct ofp_header hello; | |
dc681276 | 311 | int retval = stream_recv(stream, &hello, sizeof hello); |
48d973e3 | 312 | if (retval == sizeof hello) { |
982697a4 BP |
313 | enum ofpraw raw; |
314 | ||
13c952ca | 315 | CHECK(hello.version, OFP15_VERSION); |
982697a4 BP |
316 | CHECK(ofpraw_decode_partial(&raw, &hello, sizeof hello), 0); |
317 | CHECK(raw, OFPRAW_OFPT_HELLO); | |
d84d4b88 | 318 | CHECK(ntohs(hello.length), sizeof hello); |
48d973e3 BP |
319 | read_hello = true; |
320 | } else { | |
dc681276 | 321 | CHECK_ERRNO(retval, -EAGAIN); |
48d973e3 BP |
322 | } |
323 | } | |
324 | ||
60cb3eb8 | 325 | vconn_run(vconn); |
48d973e3 BP |
326 | if (!connected) { |
327 | int error = vconn_connect(vconn); | |
328 | if (error == expect_connect_error) { | |
329 | if (!error) { | |
330 | connected = true; | |
331 | } else { | |
dc681276 | 332 | stream_close(stream); |
48d973e3 BP |
333 | vconn_close(vconn); |
334 | return; | |
335 | } | |
336 | } else { | |
dc681276 | 337 | CHECK_ERRNO(error, EAGAIN); |
48d973e3 BP |
338 | } |
339 | } | |
340 | ||
341 | if (read_hello && connected) { | |
342 | break; | |
343 | } | |
344 | ||
60cb3eb8 | 345 | vconn_run_wait(vconn); |
48d973e3 BP |
346 | if (!connected) { |
347 | vconn_connect_wait(vconn); | |
348 | } | |
349 | if (!read_hello) { | |
dc681276 | 350 | stream_recv_wait(stream); |
48d973e3 BP |
351 | } |
352 | poll_block(); | |
353 | } | |
dc681276 | 354 | stream_close(stream); |
6d1fb217 | 355 | CHECK_ERRNO(vconn_recv_block(vconn, &msg), EOF); |
48d973e3 BP |
356 | vconn_close(vconn); |
357 | } | |
358 | ||
359 | /* Try connecting and sending a normal hello, which should succeed. */ | |
360 | static void | |
1636c761 | 361 | test_send_plain_hello(struct ovs_cmdl_context *ctx) |
48d973e3 | 362 | { |
1636c761 | 363 | const char *type = ctx->argv[1]; |
982697a4 | 364 | struct ofpbuf *hello; |
48d973e3 | 365 | |
13c952ca | 366 | hello = ofpraw_alloc_xid(OFPRAW_OFPT_HELLO, OFP15_VERSION, |
982697a4 | 367 | htonl(0x12345678), 0); |
6fd6ed71 | 368 | test_send_hello(type, hello->data, hello->size, 0); |
982697a4 | 369 | ofpbuf_delete(hello); |
48d973e3 BP |
370 | } |
371 | ||
372 | /* Try connecting and sending an extra-long hello, which should succeed (since | |
373 | * the specification says that implementations must accept and ignore extra | |
374 | * data). */ | |
375 | static void | |
1636c761 | 376 | test_send_long_hello(struct ovs_cmdl_context *ctx) |
48d973e3 | 377 | { |
1636c761 | 378 | const char *type = ctx->argv[1]; |
982697a4 BP |
379 | struct ofpbuf *hello; |
380 | enum { EXTRA_BYTES = 8 }; | |
381 | ||
13c952ca | 382 | hello = ofpraw_alloc_xid(OFPRAW_OFPT_HELLO, OFP15_VERSION, |
982697a4 BP |
383 | htonl(0x12345678), EXTRA_BYTES); |
384 | ofpbuf_put_zeros(hello, EXTRA_BYTES); | |
385 | ofpmsg_update_length(hello); | |
6fd6ed71 | 386 | test_send_hello(type, hello->data, hello->size, 0); |
982697a4 | 387 | ofpbuf_delete(hello); |
48d973e3 BP |
388 | } |
389 | ||
390 | /* Try connecting and sending an echo request instead of a hello, which should | |
391 | * fail with EPROTO. */ | |
392 | static void | |
1636c761 | 393 | test_send_echo_hello(struct ovs_cmdl_context *ctx) |
48d973e3 | 394 | { |
1636c761 | 395 | const char *type = ctx->argv[1]; |
982697a4 | 396 | struct ofpbuf *echo; |
48d973e3 | 397 | |
13c952ca | 398 | echo = ofpraw_alloc_xid(OFPRAW_OFPT_ECHO_REQUEST, OFP15_VERSION, |
982697a4 | 399 | htonl(0x12345678), 0); |
6fd6ed71 | 400 | test_send_hello(type, echo->data, echo->size, EPROTO); |
982697a4 | 401 | ofpbuf_delete(echo); |
48d973e3 BP |
402 | } |
403 | ||
404 | /* Try connecting and sending a hello packet that has its length field as 0, | |
405 | * which should fail with EPROTO. */ | |
406 | static void | |
1636c761 | 407 | test_send_short_hello(struct ovs_cmdl_context *ctx) |
48d973e3 | 408 | { |
1636c761 | 409 | const char *type = ctx->argv[1]; |
48d973e3 BP |
410 | struct ofp_header hello; |
411 | ||
412 | memset(&hello, 0, sizeof hello); | |
413 | test_send_hello(type, &hello, sizeof hello, EPROTO); | |
414 | } | |
415 | ||
416 | /* Try connecting and sending a hello packet that has a bad version, which | |
417 | * should fail with EPROTO. */ | |
418 | static void | |
1636c761 | 419 | test_send_invalid_version_hello(struct ovs_cmdl_context *ctx) |
48d973e3 | 420 | { |
1636c761 | 421 | const char *type = ctx->argv[1]; |
982697a4 | 422 | struct ofpbuf *hello; |
48d973e3 | 423 | |
13c952ca | 424 | hello = ofpraw_alloc_xid(OFPRAW_OFPT_HELLO, OFP15_VERSION, |
982697a4 | 425 | htonl(0x12345678), 0); |
6fd6ed71 PS |
426 | ((struct ofp_header *) hello->data)->version = 0; |
427 | test_send_hello(type, hello->data, hello->size, EPROTO); | |
982697a4 | 428 | ofpbuf_delete(hello); |
48d973e3 BP |
429 | } |
430 | ||
5f383751 | 431 | static const struct ovs_cmdl_command commands[] = { |
1f4a7252 RM |
432 | {"refuse-connection", NULL, 1, 1, test_refuse_connection, OVS_RO}, |
433 | {"accept-then-close", NULL, 1, 1, test_accept_then_close, OVS_RO}, | |
434 | {"read-hello", NULL, 1, 1, test_read_hello, OVS_RO}, | |
435 | {"send-plain-hello", NULL, 1, 1, test_send_plain_hello, OVS_RO}, | |
436 | {"send-long-hello", NULL, 1, 1, test_send_long_hello, OVS_RO}, | |
437 | {"send-echo-hello", NULL, 1, 1, test_send_echo_hello, OVS_RO}, | |
438 | {"send-short-hello", NULL, 1, 1, test_send_short_hello, OVS_RO}, | |
439 | {"send-invalid-version-hello", NULL, 1, 1, test_send_invalid_version_hello, OVS_RO}, | |
440 | {NULL, NULL, 0, 0, NULL, OVS_RO}, | |
d27ce529 BP |
441 | }; |
442 | ||
eadd1644 AZ |
443 | static void |
444 | test_vconn_main(int argc, char *argv[]) | |
48d973e3 | 445 | { |
1636c761 RB |
446 | struct ovs_cmdl_context ctx = { |
447 | .argc = argc - 1, | |
448 | .argv = argv + 1, | |
449 | }; | |
450 | ||
48d973e3 | 451 | set_program_name(argv[0]); |
d5460484 | 452 | vlog_set_levels(NULL, VLF_ANY_DESTINATION, VLL_EMER); |
480ce8ab | 453 | vlog_set_levels(NULL, VLF_CONSOLE, VLL_DBG); |
8a777cf6 | 454 | fatal_ignore_sigpipe(); |
48d973e3 | 455 | |
04895042 | 456 | time_alarm(TIMEOUT); |
48d973e3 | 457 | |
1636c761 | 458 | ovs_cmdl_run_command(&ctx, commands); |
48d973e3 | 459 | } |
eadd1644 AZ |
460 | |
461 | OVSTEST_REGISTER("test-vconn", test_vconn_main); |