2 * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2017, 2019 Nicira, Inc.
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:
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
25 #include "command-line.h"
26 #include "fatal-signal.h"
27 #include "openflow/openflow.h"
28 #include "openvswitch/ofp-msgs.h"
29 #include "openvswitch/ofpbuf.h"
30 #include "openvswitch/vconn.h"
31 #include "openvswitch/vlog.h"
33 #include "openvswitch/poll-loop.h"
34 #include "socket-util.h"
36 #include "stream-ssl.h"
46 struct pstream
*pstream
;
50 check(int a
, int b
, const char *as
, const char *file
, int line
)
53 ovs_fatal(0, "%s:%d: %s is %d but should be %d", file
, line
, as
, a
, b
);
58 #define CHECK(A, B) check(A, B, #A, __FILE__, __LINE__)
61 check_errno(int a
, int b
, const char *as
, const char *file
, int line
)
64 char *str_b
= xstrdup(ovs_strerror(abs(b
)));
65 ovs_fatal(0, "%s:%d: %s is %d (%s) but should be %d (%s)",
66 file
, line
, as
, a
, ovs_strerror(abs(a
)), b
, str_b
);
70 #define CHECK_ERRNO(A, B) check_errno(A, B, #A, __FILE__, __LINE__)
73 fpv_create(const char *type
, struct fake_pvconn
*fpv
)
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);
84 if (!strcmp(type
, "unix")) {
85 static int unix_count
= 0;
88 bind_path
= xasprintf("fake-pvconn.%d", unix_count
++);
89 fpv
->pvconn_name
= xasprintf("punix:%s", bind_path
);
90 fpv
->vconn_name
= xasprintf("unix:%s", bind_path
);
91 CHECK_ERRNO(pstream_open(fpv
->pvconn_name
, &fpv
->pstream
,
94 } else if (!strcmp(type
, "tcp") || !strcmp(type
, "ssl")) {
95 char *s
, *port
, *save_ptr
= NULL
;
98 open_name
= xasprintf("p%s:0:127.0.0.1", type
);
99 CHECK_ERRNO(pstream_open(open_name
, &fpv
->pstream
, DSCP_DEFAULT
), 0);
101 /* Extract bound port number from pstream name. */
102 s
= xstrdup(pstream_get_name(fpv
->pstream
));
103 strtok_r(s
, ":", &save_ptr
);
104 port
= strtok_r(NULL
, ":", &save_ptr
);
107 fpv
->pvconn_name
= xstrdup(pstream_get_name(fpv
->pstream
));
108 fpv
->vconn_name
= xasprintf("%s:127.0.0.1:%s", type
, port
);
117 static struct stream
*
118 fpv_accept(struct fake_pvconn
*fpv
)
120 struct stream
*stream
;
122 CHECK_ERRNO(pstream_accept_block(fpv
->pstream
, &stream
), 0);
128 fpv_close(struct fake_pvconn
*fpv
)
130 pstream_close(fpv
->pstream
);
135 fpv_destroy(struct fake_pvconn
*fpv
)
138 free(fpv
->pvconn_name
);
139 free(fpv
->vconn_name
);
142 /* Connects to a fake_pvconn with vconn_open(), then closes the listener and
143 * verifies that vconn_connect() reports 'expected_error'. */
145 test_refuse_connection(struct ovs_cmdl_context
*ctx
)
147 const char *type
= ctx
->argv
[1];
148 struct fake_pvconn fpv
;
152 fpv_create(type
, &fpv
);
153 CHECK_ERRNO(vconn_open(fpv
.vconn_name
, 0, DSCP_DEFAULT
, &vconn
), 0);
157 error
= vconn_connect_block(vconn
, (TIMEOUT
- 2) * 1000);
158 if (!strcmp(type
, "tcp")) {
159 if (error
!= ECONNRESET
&& error
!= EPIPE
&& error
!= ETIMEDOUT
161 && error
!= WSAECONNRESET
164 ovs_fatal(0, "unexpected vconn_connect() return value %d (%s)",
165 error
, ovs_strerror(error
));
167 } else if (!strcmp(type
, "unix")) {
168 CHECK_ERRNO(error
, EPIPE
);
169 } else if (!strcmp(type
, "ssl")) {
170 if (error
!= EPROTO
&& error
!= ECONNRESET
&& error
!= ETIMEDOUT
) {
171 ovs_fatal(0, "unexpected vconn_connect() return value %d (%s)",
172 error
, ovs_strerror(error
));
175 ovs_fatal(0, "invalid connection type %s", type
);
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'. */
186 test_accept_then_close(struct ovs_cmdl_context
*ctx
)
188 const char *type
= ctx
->argv
[1];
189 struct fake_pvconn fpv
;
193 fpv_create(type
, &fpv
);
194 CHECK_ERRNO(vconn_open(fpv
.vconn_name
, 0, DSCP_DEFAULT
, &vconn
), 0);
196 stream_close(fpv_accept(&fpv
));
199 error
= vconn_connect_block(vconn
, -1);
200 if (!strcmp(type
, "tcp") || !strcmp(type
, "unix")) {
201 if (error
!= ECONNRESET
&& error
!= EPIPE
203 && error
!= WSAECONNRESET
206 ovs_fatal(0, "unexpected vconn_connect() return value %d (%s)",
207 error
, ovs_strerror(error
));
210 CHECK_ERRNO(error
, EPROTO
);
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'. */
221 test_read_hello(struct ovs_cmdl_context
*ctx
)
223 const char *type
= ctx
->argv
[1];
224 struct fake_pvconn fpv
;
226 struct stream
*stream
;
229 fpv_create(type
, &fpv
);
230 CHECK_ERRNO(vconn_open(fpv
.vconn_name
, 0, DSCP_DEFAULT
, &vconn
), 0);
232 stream
= fpv_accept(&fpv
);
235 struct ofp_header hello
;
238 retval
= stream_recv(stream
, &hello
, sizeof hello
);
239 if (retval
== sizeof hello
) {
242 CHECK(hello
.version
, OFP15_VERSION
);
243 CHECK(ofpraw_decode_partial(&raw
, &hello
, sizeof hello
), 0);
244 CHECK(raw
, OFPRAW_OFPT_HELLO
);
245 CHECK(ntohs(hello
.length
), sizeof hello
);
248 CHECK_ERRNO(retval
, -EAGAIN
);
252 CHECK_ERRNO(vconn_connect(vconn
), EAGAIN
);
253 vconn_run_wait(vconn
);
254 vconn_connect_wait(vconn
);
255 stream_recv_wait(stream
);
258 stream_close(stream
);
259 error
= vconn_connect_block(vconn
, -1);
260 if (error
!= ECONNRESET
&& error
!= EPIPE
) {
261 ovs_fatal(0, "unexpected vconn_connect() return value %d (%s)",
262 error
, ovs_strerror(error
));
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'. */
272 test_send_hello(const char *type
, const void *out
, size_t out_size
,
273 int expect_connect_error
)
275 struct fake_pvconn fpv
;
277 bool read_hello
, connected
;
279 struct stream
*stream
;
282 fpv_create(type
, &fpv
);
283 CHECK_ERRNO(vconn_open(fpv
.vconn_name
, 0, DSCP_DEFAULT
, &vconn
), 0);
285 stream
= fpv_accept(&fpv
);
289 while (n_sent
< out_size
) {
292 retval
= stream_send(stream
, (char *) out
+ n_sent
, out_size
- n_sent
);
295 } else if (retval
== -EAGAIN
) {
298 stream_recv_wait(stream
);
299 vconn_connect_wait(vconn
);
300 vconn_run_wait(vconn
);
303 ovs_fatal(0, "stream_send returned unexpected value %d", retval
);
307 read_hello
= connected
= false;
310 struct ofp_header hello
;
311 int retval
= stream_recv(stream
, &hello
, sizeof hello
);
312 if (retval
== sizeof hello
) {
315 CHECK(hello
.version
, OFP15_VERSION
);
316 CHECK(ofpraw_decode_partial(&raw
, &hello
, sizeof hello
), 0);
317 CHECK(raw
, OFPRAW_OFPT_HELLO
);
318 CHECK(ntohs(hello
.length
), sizeof hello
);
321 CHECK_ERRNO(retval
, -EAGAIN
);
327 int error
= vconn_connect(vconn
);
328 if (error
== expect_connect_error
) {
332 stream_close(stream
);
337 CHECK_ERRNO(error
, EAGAIN
);
341 if (read_hello
&& connected
) {
345 vconn_run_wait(vconn
);
347 vconn_connect_wait(vconn
);
350 stream_recv_wait(stream
);
354 stream_close(stream
);
355 CHECK_ERRNO(vconn_recv_block(vconn
, &msg
), EOF
);
359 /* Try connecting and sending a normal hello, which should succeed. */
361 test_send_plain_hello(struct ovs_cmdl_context
*ctx
)
363 const char *type
= ctx
->argv
[1];
364 struct ofpbuf
*hello
;
366 hello
= ofpraw_alloc_xid(OFPRAW_OFPT_HELLO
, OFP15_VERSION
,
367 htonl(0x12345678), 0);
368 test_send_hello(type
, hello
->data
, hello
->size
, 0);
369 ofpbuf_delete(hello
);
372 /* Try connecting and sending an extra-long hello, which should succeed (since
373 * the specification says that implementations must accept and ignore extra
376 test_send_long_hello(struct ovs_cmdl_context
*ctx
)
378 const char *type
= ctx
->argv
[1];
379 struct ofpbuf
*hello
;
380 enum { EXTRA_BYTES
= 8 };
382 hello
= ofpraw_alloc_xid(OFPRAW_OFPT_HELLO
, OFP15_VERSION
,
383 htonl(0x12345678), EXTRA_BYTES
);
384 ofpbuf_put_zeros(hello
, EXTRA_BYTES
);
385 ofpmsg_update_length(hello
);
386 test_send_hello(type
, hello
->data
, hello
->size
, 0);
387 ofpbuf_delete(hello
);
390 /* Try connecting and sending an echo request instead of a hello, which should
391 * fail with EPROTO. */
393 test_send_echo_hello(struct ovs_cmdl_context
*ctx
)
395 const char *type
= ctx
->argv
[1];
398 echo
= ofpraw_alloc_xid(OFPRAW_OFPT_ECHO_REQUEST
, OFP15_VERSION
,
399 htonl(0x12345678), 0);
400 test_send_hello(type
, echo
->data
, echo
->size
, EPROTO
);
404 /* Try connecting and sending a hello packet that has its length field as 0,
405 * which should fail with EPROTO. */
407 test_send_short_hello(struct ovs_cmdl_context
*ctx
)
409 const char *type
= ctx
->argv
[1];
410 struct ofp_header hello
;
412 memset(&hello
, 0, sizeof hello
);
413 test_send_hello(type
, &hello
, sizeof hello
, EPROTO
);
416 /* Try connecting and sending a hello packet that has a bad version, which
417 * should fail with EPROTO. */
419 test_send_invalid_version_hello(struct ovs_cmdl_context
*ctx
)
421 const char *type
= ctx
->argv
[1];
422 struct ofpbuf
*hello
;
424 hello
= ofpraw_alloc_xid(OFPRAW_OFPT_HELLO
, OFP15_VERSION
,
425 htonl(0x12345678), 0);
426 ((struct ofp_header
*) hello
->data
)->version
= 0;
427 test_send_hello(type
, hello
->data
, hello
->size
, EPROTO
);
428 ofpbuf_delete(hello
);
431 static const struct ovs_cmdl_command commands
[] = {
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
},
444 test_vconn_main(int argc
, char *argv
[])
446 struct ovs_cmdl_context ctx
= {
451 set_program_name(argv
[0]);
452 vlog_set_levels(NULL
, VLF_ANY_DESTINATION
, VLL_EMER
);
453 vlog_set_levels(NULL
, VLF_CONSOLE
, VLL_DBG
);
454 fatal_ignore_sigpipe();
458 ovs_cmdl_run_command(&ctx
, commands
);
461 OVSTEST_REGISTER("test-vconn", test_vconn_main
);