Merge "citrix" into "master".
[sliver-openvswitch.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     assert(vconn_connect(vconn) == expected_error);
147     vconn_close(vconn);
148     fpv_destroy(&fpv);
149 }
150
151 /* Connects to a fake_pvconn with vconn_open(), accepts that connection and
152  * closes it immediately, and verifies that vconn_connect() reports
153  * 'expected_error'. */
154 static void
155 test_accept_then_close(const char *type, int expected_error)
156 {
157     struct fake_pvconn fpv;
158     struct vconn *vconn;
159
160     fpv_create(type, &fpv);
161     assert(!vconn_open(fpv.vconn_name, OFP_VERSION, &vconn));
162     close(fpv_accept(&fpv));
163     fpv_close(&fpv);
164     assert(vconn_connect(vconn) == expected_error);
165     vconn_close(vconn);
166     fpv_destroy(&fpv);
167 }
168
169 /* Connects to a fake_pvconn with vconn_open(), accepts that connection and
170  * reads the hello message from it, then closes the connection and verifies
171  * that vconn_connect() reports 'expected_error'. */
172 static void
173 test_read_hello(const char *type, int expected_error)
174 {
175     struct fake_pvconn fpv;
176     struct vconn *vconn;
177     int fd;
178
179     fpv_create(type, &fpv);
180     assert(!vconn_open(fpv.vconn_name, OFP_VERSION, &vconn));
181     fd = fpv_accept(&fpv);
182     fpv_destroy(&fpv);
183     assert(!set_nonblocking(fd));
184     for (;;) {
185        struct ofp_header hello;
186        int retval;
187
188        retval = read(fd, &hello, sizeof hello);
189        if (retval == sizeof hello) {
190            assert(hello.version == OFP_VERSION);
191            assert(hello.type == OFPT_HELLO);
192            assert(hello.length == htons(sizeof hello));
193            break;
194        } else {
195            assert(errno == EAGAIN);
196        }
197
198        assert(vconn_connect(vconn) == EAGAIN);
199        vconn_connect_wait(vconn);
200        poll_fd_wait(fd, POLLIN);
201        poll_block();
202     }
203     close(fd);
204     assert(vconn_connect(vconn) == expected_error);
205     vconn_close(vconn);
206 }
207
208 /* Connects to a fake_pvconn with vconn_open(), accepts that connection and
209  * sends the 'out' bytes in 'out_size' to it (presumably an OFPT_HELLO
210  * message), then verifies that vconn_connect() reports
211  * 'expect_connect_error'. */
212 static void
213 test_send_hello(const char *type, const void *out, size_t out_size,
214                 int expect_connect_error)
215 {
216     struct fake_pvconn fpv;
217     struct vconn *vconn;
218     bool read_hello, connected;
219     struct ofpbuf *msg;
220     int fd;
221
222     fpv_create(type, &fpv);
223     assert(!vconn_open(fpv.vconn_name, OFP_VERSION, &vconn));
224     fd = fpv_accept(&fpv);
225     fpv_destroy(&fpv);
226
227     write(fd, out, out_size);
228
229     assert(!set_nonblocking(fd));
230
231     read_hello = connected = false;
232     for (;;) {
233        if (!read_hello) {
234            struct ofp_header hello;
235            int retval = read(fd, &hello, sizeof hello);
236            if (retval == sizeof hello) {
237                assert(hello.version == OFP_VERSION);
238                assert(hello.type == OFPT_HELLO);
239                assert(hello.length == htons(sizeof hello));
240                read_hello = true;
241            } else {
242                assert(errno == EAGAIN);
243            }
244        }
245
246        if (!connected) {
247            int error = vconn_connect(vconn);
248            if (error == expect_connect_error) {
249                if (!error) {
250                    connected = true;
251                } else {
252                    close(fd);
253                    vconn_close(vconn);
254                    return;
255                }
256            } else {
257                assert(error == EAGAIN);
258            }
259        }
260
261        if (read_hello && connected) {
262            break;
263        }
264
265        if (!connected) {
266            vconn_connect_wait(vconn);
267        }
268        if (!read_hello) {
269            poll_fd_wait(fd, POLLIN);
270        }
271        poll_block();
272     }
273     close(fd);
274     assert(vconn_recv(vconn, &msg) == EOF);
275     vconn_close(vconn);
276 }
277
278 /* Try connecting and sending a normal hello, which should succeed. */
279 static void
280 test_send_plain_hello(const char *type)
281 {
282     struct ofp_header hello;
283
284     hello.version = OFP_VERSION;
285     hello.type = OFPT_HELLO;
286     hello.length = htons(sizeof hello);
287     hello.xid = htonl(0x12345678);
288     test_send_hello(type, &hello, sizeof hello, 0);
289 }
290
291 /* Try connecting and sending an extra-long hello, which should succeed (since
292  * the specification says that implementations must accept and ignore extra
293  * data). */
294 static void
295 test_send_long_hello(const char *type)
296 {
297     struct ofp_header hello;
298     char buffer[sizeof hello * 2];
299
300     hello.version = OFP_VERSION;
301     hello.type = OFPT_HELLO;
302     hello.length = htons(sizeof buffer);
303     hello.xid = htonl(0x12345678);
304     memset(buffer, 0, sizeof buffer);
305     memcpy(buffer, &hello, sizeof hello);
306     test_send_hello(type, buffer, sizeof buffer, 0);
307 }
308
309 /* Try connecting and sending an echo request instead of a hello, which should
310  * fail with EPROTO. */
311 static void
312 test_send_echo_hello(const char *type)
313 {
314     struct ofp_header echo;
315
316     echo.version = OFP_VERSION;
317     echo.type = OFPT_ECHO_REQUEST;
318     echo.length = htons(sizeof echo);
319     echo.xid = htonl(0x89abcdef);
320     test_send_hello(type, &echo, sizeof echo, EPROTO);
321 }
322
323 /* Try connecting and sending a hello packet that has its length field as 0,
324  * which should fail with EPROTO. */
325 static void
326 test_send_short_hello(const char *type)
327 {
328     struct ofp_header hello;
329
330     memset(&hello, 0, sizeof hello);
331     test_send_hello(type, &hello, sizeof hello, EPROTO);
332 }
333
334 /* Try connecting and sending a hello packet that has a bad version, which
335  * should fail with EPROTO. */
336 static void
337 test_send_invalid_version_hello(const char *type)
338 {
339     struct ofp_header hello;
340
341     hello.version = OFP_VERSION - 1;
342     hello.type = OFPT_HELLO;
343     hello.length = htons(sizeof hello);
344     hello.xid = htonl(0x12345678);
345     test_send_hello(type, &hello, sizeof hello, EPROTO);
346 }
347
348 int
349 main(int argc UNUSED, char *argv[])
350 {
351     set_program_name(argv[0]);
352     time_init();
353     vlog_init();
354     signal(SIGPIPE, SIG_IGN);
355     vlog_set_levels(VLM_ANY_MODULE, VLF_ANY_FACILITY, VLL_EMER);
356
357     time_alarm(10);
358
359     test_refuse_connection("unix", EPIPE);
360     test_refuse_connection("tcp", ECONNRESET);
361
362     test_accept_then_close("unix", EPIPE);
363     test_accept_then_close("tcp", ECONNRESET);
364
365     test_read_hello("unix", ECONNRESET);
366     test_read_hello("tcp", ECONNRESET);
367
368     test_send_plain_hello("unix");
369     test_send_plain_hello("tcp");
370
371     test_send_long_hello("unix");
372     test_send_long_hello("tcp");
373
374     test_send_echo_hello("unix");
375     test_send_echo_hello("tcp");
376
377     test_send_short_hello("unix");
378     test_send_short_hello("tcp");
379
380     test_send_invalid_version_hello("unix");
381     test_send_invalid_version_hello("tcp");
382
383     return 0;
384 }