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