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