]> git.proxmox.com Git - ovs.git/blob - tests/test-vconn.c
vconn: Convert vconn code to modern OVS structure.
[ovs.git] / tests / test-vconn.c
1 /*
2 * Copyright (c) 2009 Nicira Networks.
3 *
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:
7 *
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.
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>
24 #include "poll-loop.h"
25 #include "socket-util.h"
26 #include "timeval.h"
27 #include "util.h"
28 #include "vlog.h"
29
30 #undef NDEBUG
31 #include <assert.h>
32
33 struct fake_pvconn {
34 const char *type;
35 char *pvconn_name;
36 char *vconn_name;
37 int fd;
38 };
39
40 static void
41 fpv_create(const char *type, struct fake_pvconn *fpv)
42 {
43 fpv->type = type;
44 if (!strcmp(type, "unix")) {
45 static int unix_count = 0;
46 char *bind_path;
47 int fd;
48
49 bind_path = xasprintf("fake-pvconn.%d", unix_count++);
50 fd = make_unix_socket(SOCK_STREAM, false, false, bind_path, NULL);
51 if (fd < 0) {
52 ovs_fatal(-fd, "%s: could not bind to Unix domain socket",
53 bind_path);
54 }
55
56 fpv->pvconn_name = xasprintf("punix:%s", bind_path);
57 fpv->vconn_name = xasprintf("unix:%s", bind_path);
58 fpv->fd = fd;
59 free(bind_path);
60 } else if (!strcmp(type, "tcp")) {
61 struct sockaddr_in sin;
62 socklen_t sin_len;
63 int fd;
64
65 /* Create TCP socket. */
66 fd = socket(PF_INET, SOCK_STREAM, 0);
67 if (fd < 0) {
68 ovs_fatal(errno, "failed to create TCP socket");
69 }
70
71 /* Bind TCP socket to localhost on any available port. */
72 sin.sin_family = AF_INET;
73 sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
74 sin.sin_port = htons(0);
75 if (bind(fd, (struct sockaddr *) &sin, sizeof sin) < 0) {
76 ovs_fatal(errno, "failed to bind TCP socket");
77 }
78
79 /* Retrieve socket's port number. */
80 sin_len = sizeof sin;
81 if (getsockname(fd, (struct sockaddr *)&sin, &sin_len) < 0) {
82 ovs_fatal(errno, "failed to read TCP socket name");
83 }
84 if (sin_len != sizeof sin || sin.sin_family != AF_INET) {
85 ovs_fatal(errno, "bad TCP socket name");
86 }
87
88 /* Save info. */
89 fpv->pvconn_name = xasprintf("ptcp:%"PRIu16":127.0.0.1",
90 ntohs(sin.sin_port));
91 fpv->vconn_name = xasprintf("tcp:127.0.0.1:%"PRIu16,
92 ntohs(sin.sin_port));
93 fpv->fd = fd;
94 } else {
95 abort();
96 }
97
98 /* Listen. */
99 if (listen(fpv->fd, 0) < 0) {
100 ovs_fatal(errno, "%s: listen failed", fpv->vconn_name);
101 }
102 }
103
104 static int
105 fpv_accept(struct fake_pvconn *fpv)
106 {
107 int fd;
108
109 fd = accept(fpv->fd, NULL, NULL);
110 if (fd < 0) {
111 ovs_fatal(errno, "%s: accept failed", fpv->pvconn_name);
112 }
113 return fd;
114 }
115
116 static void
117 fpv_close(struct fake_pvconn *fpv)
118 {
119 if (fpv->fd >= 0) {
120 if (close(fpv->fd) < 0) {
121 ovs_fatal(errno, "failed to close %s fake pvconn", fpv->type);
122 }
123 fpv->fd = -1;
124 }
125 }
126
127 static void
128 fpv_destroy(struct fake_pvconn *fpv)
129 {
130 fpv_close(fpv);
131 free(fpv->pvconn_name);
132 free(fpv->vconn_name);
133 }
134
135 /* Connects to a fake_pvconn with vconn_open(), then closes the listener and
136 * verifies that vconn_connect() reports 'expected_error'. */
137 static void
138 test_refuse_connection(const char *type, int expected_error)
139 {
140 struct fake_pvconn fpv;
141 struct vconn *vconn;
142
143 fpv_create(type, &fpv);
144 assert(!vconn_open(fpv.vconn_name, OFP_VERSION, &vconn));
145 fpv_close(&fpv);
146 vconn_run(vconn);
147 assert(vconn_connect(vconn) == expected_error);
148 vconn_close(vconn);
149 fpv_destroy(&fpv);
150 }
151
152 /* Connects to a fake_pvconn with vconn_open(), accepts that connection and
153 * closes it immediately, and verifies that vconn_connect() reports
154 * 'expected_error'. */
155 static void
156 test_accept_then_close(const char *type, int expected_error)
157 {
158 struct fake_pvconn fpv;
159 struct vconn *vconn;
160
161 fpv_create(type, &fpv);
162 assert(!vconn_open(fpv.vconn_name, OFP_VERSION, &vconn));
163 vconn_run(vconn);
164 close(fpv_accept(&fpv));
165 fpv_close(&fpv);
166 assert(vconn_connect(vconn) == expected_error);
167 vconn_close(vconn);
168 fpv_destroy(&fpv);
169 }
170
171 /* Connects to a fake_pvconn with vconn_open(), accepts that connection and
172 * reads the hello message from it, then closes the connection and verifies
173 * that vconn_connect() reports 'expected_error'. */
174 static void
175 test_read_hello(const char *type, int expected_error)
176 {
177 struct fake_pvconn fpv;
178 struct vconn *vconn;
179 int fd;
180
181 fpv_create(type, &fpv);
182 assert(!vconn_open(fpv.vconn_name, OFP_VERSION, &vconn));
183 vconn_run(vconn);
184 fd = fpv_accept(&fpv);
185 fpv_destroy(&fpv);
186 assert(!set_nonblocking(fd));
187 for (;;) {
188 struct ofp_header hello;
189 int retval;
190
191 retval = read(fd, &hello, sizeof hello);
192 if (retval == sizeof hello) {
193 assert(hello.version == OFP_VERSION);
194 assert(hello.type == OFPT_HELLO);
195 assert(hello.length == htons(sizeof hello));
196 break;
197 } else {
198 assert(errno == EAGAIN);
199 }
200
201 vconn_run(vconn);
202 assert(vconn_connect(vconn) == EAGAIN);
203 vconn_run_wait(vconn);
204 vconn_connect_wait(vconn);
205 poll_fd_wait(fd, POLLIN);
206 poll_block();
207 }
208 close(fd);
209 assert(vconn_connect(vconn) == expected_error);
210 vconn_close(vconn);
211 }
212
213 /* Connects to a fake_pvconn with vconn_open(), accepts that connection and
214 * sends the 'out' bytes in 'out_size' to it (presumably an OFPT_HELLO
215 * message), then verifies that vconn_connect() reports
216 * 'expect_connect_error'. */
217 static void
218 test_send_hello(const char *type, const void *out, size_t out_size,
219 int expect_connect_error)
220 {
221 struct fake_pvconn fpv;
222 struct vconn *vconn;
223 bool read_hello, connected;
224 struct ofpbuf *msg;
225 int fd;
226
227 fpv_create(type, &fpv);
228 assert(!vconn_open(fpv.vconn_name, OFP_VERSION, &vconn));
229 vconn_run(vconn);
230 fd = fpv_accept(&fpv);
231 fpv_destroy(&fpv);
232
233 assert(write(fd, out, out_size) == out_size);
234
235 assert(!set_nonblocking(fd));
236
237 read_hello = connected = false;
238 for (;;) {
239 if (!read_hello) {
240 struct ofp_header hello;
241 int retval = read(fd, &hello, sizeof hello);
242 if (retval == sizeof hello) {
243 assert(hello.version == OFP_VERSION);
244 assert(hello.type == OFPT_HELLO);
245 assert(hello.length == htons(sizeof hello));
246 read_hello = true;
247 } else {
248 assert(errno == EAGAIN);
249 }
250 }
251
252 vconn_run(vconn);
253 if (!connected) {
254 int error = vconn_connect(vconn);
255 if (error == expect_connect_error) {
256 if (!error) {
257 connected = true;
258 } else {
259 close(fd);
260 vconn_close(vconn);
261 return;
262 }
263 } else {
264 assert(error == EAGAIN);
265 }
266 }
267
268 if (read_hello && connected) {
269 break;
270 }
271
272 vconn_run_wait(vconn);
273 if (!connected) {
274 vconn_connect_wait(vconn);
275 }
276 if (!read_hello) {
277 poll_fd_wait(fd, POLLIN);
278 }
279 poll_block();
280 }
281 close(fd);
282 assert(vconn_recv(vconn, &msg) == EOF);
283 vconn_close(vconn);
284 }
285
286 /* Try connecting and sending a normal hello, which should succeed. */
287 static void
288 test_send_plain_hello(const char *type)
289 {
290 struct ofp_header hello;
291
292 hello.version = OFP_VERSION;
293 hello.type = OFPT_HELLO;
294 hello.length = htons(sizeof hello);
295 hello.xid = htonl(0x12345678);
296 test_send_hello(type, &hello, sizeof hello, 0);
297 }
298
299 /* Try connecting and sending an extra-long hello, which should succeed (since
300 * the specification says that implementations must accept and ignore extra
301 * data). */
302 static void
303 test_send_long_hello(const char *type)
304 {
305 struct ofp_header hello;
306 char buffer[sizeof hello * 2];
307
308 hello.version = OFP_VERSION;
309 hello.type = OFPT_HELLO;
310 hello.length = htons(sizeof buffer);
311 hello.xid = htonl(0x12345678);
312 memset(buffer, 0, sizeof buffer);
313 memcpy(buffer, &hello, sizeof hello);
314 test_send_hello(type, buffer, sizeof buffer, 0);
315 }
316
317 /* Try connecting and sending an echo request instead of a hello, which should
318 * fail with EPROTO. */
319 static void
320 test_send_echo_hello(const char *type)
321 {
322 struct ofp_header echo;
323
324 echo.version = OFP_VERSION;
325 echo.type = OFPT_ECHO_REQUEST;
326 echo.length = htons(sizeof echo);
327 echo.xid = htonl(0x89abcdef);
328 test_send_hello(type, &echo, sizeof echo, EPROTO);
329 }
330
331 /* Try connecting and sending a hello packet that has its length field as 0,
332 * which should fail with EPROTO. */
333 static void
334 test_send_short_hello(const char *type)
335 {
336 struct ofp_header hello;
337
338 memset(&hello, 0, sizeof hello);
339 test_send_hello(type, &hello, sizeof hello, EPROTO);
340 }
341
342 /* Try connecting and sending a hello packet that has a bad version, which
343 * should fail with EPROTO. */
344 static void
345 test_send_invalid_version_hello(const char *type)
346 {
347 struct ofp_header hello;
348
349 hello.version = OFP_VERSION - 1;
350 hello.type = OFPT_HELLO;
351 hello.length = htons(sizeof hello);
352 hello.xid = htonl(0x12345678);
353 test_send_hello(type, &hello, sizeof hello, EPROTO);
354 }
355
356 int
357 main(int argc UNUSED, char *argv[])
358 {
359 set_program_name(argv[0]);
360 time_init();
361 vlog_init();
362 signal(SIGPIPE, SIG_IGN);
363 vlog_set_levels(VLM_ANY_MODULE, VLF_ANY_FACILITY, VLL_EMER);
364
365 time_alarm(10);
366
367 test_refuse_connection("unix", EPIPE);
368 test_refuse_connection("tcp", ECONNRESET);
369
370 test_accept_then_close("unix", EPIPE);
371 test_accept_then_close("tcp", ECONNRESET);
372
373 test_read_hello("unix", ECONNRESET);
374 test_read_hello("tcp", ECONNRESET);
375
376 test_send_plain_hello("unix");
377 test_send_plain_hello("tcp");
378
379 test_send_long_hello("unix");
380 test_send_long_hello("tcp");
381
382 test_send_echo_hello("unix");
383 test_send_echo_hello("tcp");
384
385 test_send_short_hello("unix");
386 test_send_short_hello("tcp");
387
388 test_send_invalid_version_hello("unix");
389 test_send_invalid_version_hello("tcp");
390
391 return 0;
392 }