BadVPN – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | #include "test_sockets.h" |
2 | |||
3 | #include "lwip/mem.h" |
||
4 | #include "lwip/opt.h" |
||
5 | #include "lwip/sockets.h" |
||
6 | #include "lwip/priv/sockets_priv.h" |
||
7 | #include "lwip/stats.h" |
||
8 | |||
9 | #include "lwip/tcpip.h" |
||
10 | #include "lwip/priv/tcp_priv.h" |
||
11 | #include "lwip/api.h" |
||
12 | |||
13 | |||
14 | static int |
||
15 | test_sockets_get_used_count(void) |
||
16 | { |
||
17 | int used = 0; |
||
18 | int i; |
||
19 | |||
20 | for (i = 0; i < NUM_SOCKETS; i++) { |
||
21 | struct lwip_sock* s = lwip_socket_dbg_get_socket(i); |
||
22 | if (s != NULL) { |
||
23 | if (s->fd_used) { |
||
24 | used++; |
||
25 | } |
||
26 | } |
||
27 | } |
||
28 | return used; |
||
29 | } |
||
30 | |||
31 | |||
32 | /* Setups/teardown functions */ |
||
33 | |||
34 | static void |
||
35 | sockets_setup(void) |
||
36 | { |
||
37 | /* expect full free heap */ |
||
38 | lwip_check_ensure_no_alloc(SKIP_POOL(MEMP_SYS_TIMEOUT)); |
||
39 | } |
||
40 | |||
41 | static void |
||
42 | sockets_teardown(void) |
||
43 | { |
||
44 | fail_unless(test_sockets_get_used_count() == 0); |
||
45 | /* poll until all memory is released... */ |
||
46 | tcpip_thread_poll_one(); |
||
47 | while (tcp_tw_pcbs) { |
||
48 | tcp_abort(tcp_tw_pcbs); |
||
49 | tcpip_thread_poll_one(); |
||
50 | } |
||
51 | tcpip_thread_poll_one(); |
||
52 | /* ensure full free heap */ |
||
53 | lwip_check_ensure_no_alloc(SKIP_POOL(MEMP_SYS_TIMEOUT)); |
||
54 | } |
||
55 | |||
56 | #ifndef NUM_SOCKETS |
||
57 | #define NUM_SOCKETS MEMP_NUM_NETCONN |
||
58 | #endif |
||
59 | |||
60 | #if LWIP_SOCKET |
||
61 | static int |
||
62 | test_sockets_alloc_socket_nonblocking(int domain, int type) |
||
63 | { |
||
64 | int s = lwip_socket(domain, type, 0); |
||
65 | if (s >= 0) { |
||
66 | int ret = lwip_fcntl(s, F_SETFL, O_NONBLOCK); |
||
67 | fail_unless(ret == 0); |
||
68 | } |
||
69 | return s; |
||
70 | } |
||
71 | |||
72 | /* Verify basic sockets functionality |
||
73 | */ |
||
74 | START_TEST(test_sockets_basics) |
||
75 | { |
||
76 | int s, i, ret; |
||
77 | int s2[NUM_SOCKETS]; |
||
78 | LWIP_UNUSED_ARG(_i); |
||
79 | |||
80 | s = lwip_socket(AF_INET, SOCK_STREAM, 0); |
||
81 | fail_unless(s >= 0); |
||
82 | lwip_close(s); |
||
83 | |||
84 | for (i = 0; i < NUM_SOCKETS; i++) { |
||
85 | s2[i] = lwip_socket(AF_INET, SOCK_STREAM, 0); |
||
86 | fail_unless(s2[i] >= 0); |
||
87 | } |
||
88 | |||
89 | /* all sockets used, now it should fail */ |
||
90 | s = lwip_socket(AF_INET, SOCK_STREAM, 0); |
||
91 | fail_unless(s == -1); |
||
92 | /* close one socket */ |
||
93 | ret = lwip_close(s2[0]); |
||
94 | fail_unless(ret == 0); |
||
95 | /* now it should succeed */ |
||
96 | s2[0] = lwip_socket(AF_INET, SOCK_STREAM, 0); |
||
97 | fail_unless(s2[0] >= 0); |
||
98 | |||
99 | /* close all sockets */ |
||
100 | for (i = 0; i < NUM_SOCKETS; i++) { |
||
101 | ret = lwip_close(s2[i]); |
||
102 | fail_unless(ret == 0); |
||
103 | } |
||
104 | } |
||
105 | END_TEST |
||
106 | |||
107 | static void test_sockets_allfunctions_basic_domain(int domain) |
||
108 | { |
||
109 | int s, s2, s3, ret; |
||
110 | struct sockaddr_storage addr, addr2; |
||
111 | socklen_t addrlen, addr2len; |
||
112 | /* listen socket */ |
||
113 | s = lwip_socket(domain, SOCK_STREAM, 0); |
||
114 | fail_unless(s >= 0); |
||
115 | |||
116 | ret = lwip_listen(s, 0); |
||
117 | fail_unless(ret == 0); |
||
118 | |||
119 | addrlen = sizeof(addr); |
||
120 | ret = lwip_getsockname(s, (struct sockaddr*)&addr, &addrlen); |
||
121 | fail_unless(ret == 0); |
||
122 | |||
123 | s2 = test_sockets_alloc_socket_nonblocking(domain, SOCK_STREAM); |
||
124 | fail_unless(s2 >= 0); |
||
125 | /* nonblocking connect s2 to s (but use loopback address) */ |
||
126 | if (domain == AF_INET) { |
||
127 | #if LWIP_IPV4 |
||
128 | struct sockaddr_in *addr4 = (struct sockaddr_in *)&addr; |
||
129 | addr4->sin_addr.s_addr = PP_HTONL(INADDR_LOOPBACK); |
||
130 | #endif |
||
131 | } else { |
||
132 | #if LWIP_IPV6 |
||
133 | struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&addr; |
||
134 | struct in6_addr lo6 = IN6ADDR_LOOPBACK_INIT; |
||
135 | addr6->sin6_addr = lo6; |
||
136 | #endif |
||
137 | } |
||
138 | ret = lwip_connect(s2, (struct sockaddr*)&addr, addrlen); |
||
139 | fail_unless(ret == -1); |
||
140 | fail_unless(errno == EINPROGRESS); |
||
141 | ret = lwip_connect(s2, (struct sockaddr*)&addr, addrlen); |
||
142 | fail_unless(ret == -1); |
||
143 | fail_unless(errno == EALREADY); |
||
144 | |||
145 | while(tcpip_thread_poll_one()); |
||
146 | |||
147 | s3 = lwip_accept(s, (struct sockaddr*)&addr2, &addr2len); |
||
148 | fail_unless(s3 >= 0); |
||
149 | |||
150 | ret = lwip_connect(s2, (struct sockaddr*)&addr, addrlen); |
||
151 | fail_unless(ret == -1); |
||
152 | fail_unless(errno == EISCONN); |
||
153 | |||
154 | ret = lwip_close(s); |
||
155 | fail_unless(ret == 0); |
||
156 | ret = lwip_close(s2); |
||
157 | fail_unless(ret == 0); |
||
158 | ret = lwip_close(s3); |
||
159 | fail_unless(ret == 0); |
||
160 | } |
||
161 | |||
162 | /* Try to step through all sockets functions once... |
||
163 | */ |
||
164 | START_TEST(test_sockets_allfunctions_basic) |
||
165 | { |
||
166 | LWIP_UNUSED_ARG(_i); |
||
167 | #if LWIP_IPV4 |
||
168 | test_sockets_allfunctions_basic_domain(AF_INET); |
||
169 | #endif |
||
170 | #if LWIP_IPV6 |
||
171 | test_sockets_allfunctions_basic_domain(AF_INET6); |
||
172 | #endif |
||
173 | } |
||
174 | END_TEST |
||
175 | |||
176 | static void test_sockets_init_loopback_addr(int domain, struct sockaddr_storage *addr_st, socklen_t *sz) |
||
177 | { |
||
178 | memset(addr_st, 0, sizeof(*addr_st)); |
||
179 | switch(domain) { |
||
180 | #if LWIP_IPV6 |
||
181 | case AF_INET6: { |
||
182 | struct sockaddr_in6 *addr = (struct sockaddr_in6*)addr_st; |
||
183 | struct in6_addr lo6 = IN6ADDR_LOOPBACK_INIT; |
||
184 | addr->sin6_family = AF_INET6; |
||
185 | addr->sin6_port = 0; /* use ephemeral port */ |
||
186 | addr->sin6_addr = lo6; |
||
187 | *sz = sizeof(*addr); |
||
188 | } |
||
189 | break; |
||
190 | #endif /* LWIP_IPV6 */ |
||
191 | #if LWIP_IPV4 |
||
192 | case AF_INET: { |
||
193 | struct sockaddr_in *addr = (struct sockaddr_in*)addr_st; |
||
194 | addr->sin_family = AF_INET; |
||
195 | addr->sin_port = 0; /* use ephemeral port */ |
||
196 | addr->sin_addr.s_addr = PP_HTONL(INADDR_LOOPBACK); |
||
197 | *sz = sizeof(*addr); |
||
198 | } |
||
199 | break; |
||
200 | #endif /* LWIP_IPV4 */ |
||
201 | default: |
||
202 | *sz = 0; |
||
203 | fail(); |
||
204 | break; |
||
205 | } |
||
206 | } |
||
207 | |||
208 | static void test_sockets_msgapi_update_iovs(struct msghdr *msg, size_t bytes) |
||
209 | { |
||
210 | int i; |
||
211 | |||
212 | /* note: this modifies the underyling iov_base and iov_len for a partial |
||
213 | read for an individual vector. This updates the msg->msg_iov pointer |
||
214 | to skip fully consumed vecotrs */ |
||
215 | |||
216 | /* process fully consumed vectors */ |
||
217 | for (i = 0; i < msg->msg_iovlen; i++) { |
||
218 | if (msg->msg_iov[i].iov_len <= bytes) { |
||
219 | /* reduce bytes by amount of this vector */ |
||
220 | bytes -= msg->msg_iov[i].iov_len; |
||
221 | } else { |
||
222 | break; /* iov not fully consumed */ |
||
223 | } |
||
224 | } |
||
225 | |||
226 | /* slide down over fully consumed vectors */ |
||
227 | msg->msg_iov = &msg->msg_iov[i]; |
||
228 | msg->msg_iovlen -= i; |
||
229 | |||
230 | /* update new first vector with any remaining amount */ |
||
231 | msg->msg_iov[0].iov_base = ((u8_t *)msg->msg_iov[0].iov_base + bytes); |
||
232 | msg->msg_iov[0].iov_len -= bytes; |
||
233 | } |
||
234 | |||
235 | static void test_sockets_msgapi_tcp(int domain) |
||
236 | { |
||
237 | #define BUF_SZ (TCP_SND_BUF/4) |
||
238 | #define TOTAL_DATA_SZ (BUF_SZ*8) /* ~(TCP_SND_BUF*2) that accounts for integer rounding */ |
||
239 | #define NEED_TRAILER (BUF_SZ % 4 != 0) |
||
240 | int listnr, s1, s2, i, ret, opt; |
||
241 | int bytes_written, bytes_read; |
||
242 | struct sockaddr_storage addr_storage; |
||
243 | socklen_t addr_size; |
||
244 | struct iovec siovs[8]; |
||
245 | struct msghdr smsg; |
||
246 | u8_t * snd_buf; |
||
247 | struct iovec riovs[5]; |
||
248 | struct iovec riovs_tmp[5]; |
||
249 | struct msghdr rmsg; |
||
250 | u8_t * rcv_buf; |
||
251 | int rcv_off; |
||
252 | int rcv_trailer = 0; |
||
253 | u8_t val; |
||
254 | |||
255 | test_sockets_init_loopback_addr(domain, &addr_storage, &addr_size); |
||
256 | |||
257 | listnr = test_sockets_alloc_socket_nonblocking(domain, SOCK_STREAM); |
||
258 | fail_unless(listnr >= 0); |
||
259 | s1 = test_sockets_alloc_socket_nonblocking(domain, SOCK_STREAM); |
||
260 | fail_unless(s1 >= 0); |
||
261 | |||
262 | /* setup a listener socket on loopback with ephemeral port */ |
||
263 | ret = lwip_bind(listnr, (struct sockaddr*)&addr_storage, addr_size); |
||
264 | fail_unless(ret == 0); |
||
265 | ret = lwip_listen(listnr, 0); |
||
266 | fail_unless(ret == 0); |
||
267 | |||
268 | /* update address with ephemeral port */ |
||
269 | ret = lwip_getsockname(listnr, (struct sockaddr*)&addr_storage, &addr_size); |
||
270 | fail_unless(ret == 0); |
||
271 | |||
272 | /* connect, won't complete until we accept it */ |
||
273 | ret = lwip_connect(s1, (struct sockaddr*)&addr_storage, addr_size); |
||
274 | fail_unless(ret == -1); |
||
275 | fail_unless(errno == EINPROGRESS); |
||
276 | |||
277 | while (tcpip_thread_poll_one()); |
||
278 | |||
279 | /* accept, creating the other side of the connection */ |
||
280 | s2 = lwip_accept(listnr, NULL, NULL); |
||
281 | fail_unless(s2 >= 0); |
||
282 | |||
283 | /* double check s1 is connected */ |
||
284 | ret = lwip_connect(s1, (struct sockaddr*)&addr_storage, addr_size); |
||
285 | fail_unless(ret == -1); |
||
286 | fail_unless(errno == EISCONN); |
||
287 | |||
288 | /* set s2 to non-blocking, not inherited from listener */ |
||
289 | opt = lwip_fcntl(s2, F_GETFL, 0); |
||
290 | fail_unless(opt == 6); |
||
291 | opt = O_NONBLOCK; |
||
292 | ret = lwip_fcntl(s2, F_SETFL, opt); |
||
293 | fail_unless(ret == 0); |
||
294 | |||
295 | /* we are done with listener, close it */ |
||
296 | ret = lwip_close(listnr); |
||
297 | fail_unless(ret == 0); |
||
298 | |||
299 | /* allocate a buffer for a stream of incrementing hex (0x00..0xFF) which we will use |
||
300 | to create an input vector set that is larger than the TCP's send buffer. This will |
||
301 | force execution of the partial IO vector send case */ |
||
302 | snd_buf = (u8_t*)mem_malloc(BUF_SZ); |
||
303 | val = 0x00; |
||
304 | fail_unless(snd_buf != NULL); |
||
305 | for (i = 0; i < BUF_SZ; i++,val++) { |
||
306 | snd_buf[i] = val; |
||
307 | } |
||
308 | |||
309 | /* send the buffer 8 times in one message, equating to TOTAL_DATA_SZ */ |
||
310 | for (i = 0; i < 8; i++) { |
||
311 | siovs[i].iov_base = snd_buf; |
||
312 | siovs[i].iov_len = BUF_SZ; |
||
313 | } |
||
314 | |||
315 | /* allocate a receive buffer, same size as snd_buf for easy verification */ |
||
316 | rcv_buf = (u8_t*)mem_calloc(1, BUF_SZ); |
||
317 | fail_unless(rcv_buf != NULL); |
||
318 | /* split across iovs */ |
||
319 | for (i = 0; i < 4; i++) { |
||
320 | riovs[i].iov_base = &rcv_buf[i*(BUF_SZ/4)]; |
||
321 | riovs[i].iov_len = BUF_SZ/4; |
||
322 | } |
||
323 | /* handling trailing bytes if buffer doesn't evenly divide by 4 */ |
||
324 | #if NEED_TRAILER |
||
325 | if ((BUF_SZ % 4) != 0) { |
||
326 | riovs[5].iov_base = &rcv_buf[4*(BUF_SZ/4)]; |
||
327 | riovs[5].iov_len = BUF_SZ - (4*(BUF_SZ/4)); |
||
328 | rcv_trailer = 1; |
||
329 | } |
||
330 | #endif /* NEED_TRAILER */ |
||
331 | |||
332 | /* we use a copy of riovs since we'll be modifying base and len during |
||
333 | receiving. This gives us an easy way to reset the iovs for next recvmsg */ |
||
334 | memcpy(riovs_tmp, riovs, sizeof(riovs)); |
||
335 | |||
336 | memset(&smsg, 0, sizeof(smsg)); |
||
337 | smsg.msg_iov = siovs; |
||
338 | smsg.msg_iovlen = 8; |
||
339 | |||
340 | memset(&rmsg, 0, sizeof(rmsg)); |
||
341 | rmsg.msg_iov = riovs_tmp; |
||
342 | rmsg.msg_iovlen = (rcv_trailer ? 5 : 4); |
||
343 | |||
344 | bytes_written = 0; |
||
345 | bytes_read = 0; |
||
346 | rcv_off = 0; |
||
347 | |||
348 | while (bytes_written < TOTAL_DATA_SZ && (bytes_read < TOTAL_DATA_SZ)) { |
||
349 | /* send data */ |
||
350 | if (bytes_written < TOTAL_DATA_SZ) { |
||
351 | ret = lwip_sendmsg(s1, &smsg, 0); |
||
352 | /* note: since we always receive after sending, there will be open |
||
353 | space in the send buffer */ |
||
354 | fail_unless(ret > 0); |
||
355 | |||
356 | bytes_written += ret; |
||
357 | if (bytes_written < TOTAL_DATA_SZ) { |
||
358 | test_sockets_msgapi_update_iovs(&smsg, (size_t)ret); |
||
359 | } |
||
360 | } |
||
361 | |||
362 | while (tcpip_thread_poll_one()); |
||
363 | |||
364 | /* receive and verify data */ |
||
365 | do { |
||
366 | if (bytes_read < TOTAL_DATA_SZ) { |
||
367 | ret = lwip_recvmsg(s2, &rmsg, 0); |
||
368 | fail_unless(ret > 0 || (ret == -1 && errno == EWOULDBLOCK)); |
||
369 | |||
370 | if (ret > 0) { |
||
371 | rcv_off += ret; |
||
372 | /* we have received a full buffer */ |
||
373 | if (rcv_off == BUF_SZ) { |
||
374 | /* note: since iovs are just pointers, compare underlying buf */ |
||
375 | fail_unless(!memcmp(snd_buf, rcv_buf, BUF_SZ)); |
||
376 | bytes_read += BUF_SZ; |
||
377 | /* reset receive state for next buffer */ |
||
378 | rcv_off = 0; |
||
379 | memset(rcv_buf, 0, BUF_SZ); |
||
380 | memcpy(riovs_tmp, riovs, sizeof(riovs)); |
||
381 | rmsg.msg_iov = riovs_tmp; |
||
382 | rmsg.msg_iovlen = (rcv_trailer ? 5 : 4); |
||
383 | } else { /* partial read */ |
||
384 | test_sockets_msgapi_update_iovs(&rmsg, (size_t)ret); |
||
385 | } |
||
386 | } |
||
387 | } else { |
||
388 | break; |
||
389 | } |
||
390 | } while(ret > 0); |
||
391 | } |
||
392 | |||
393 | ret = lwip_close(s1); |
||
394 | fail_unless(ret == 0); |
||
395 | ret = lwip_close(s2); |
||
396 | fail_unless(ret == 0); |
||
397 | mem_free(snd_buf); |
||
398 | mem_free(rcv_buf); |
||
399 | } |
||
400 | |||
401 | static void test_sockets_msgapi_udp_send_recv_loop(int s, struct msghdr *smsg, struct msghdr *rmsg) |
||
402 | { |
||
403 | int i, ret; |
||
404 | |||
405 | /* send/receive our datagram of IO vectors 10 times */ |
||
406 | for (i = 0; i < 10; i++) { |
||
407 | ret = lwip_sendmsg(s, smsg, 0); |
||
408 | fail_unless(ret == 4); |
||
409 | |||
410 | while (tcpip_thread_poll_one()); |
||
411 | |||
412 | /* receive the datagram split across 4 buffers */ |
||
413 | ret = lwip_recvmsg(s, rmsg, 0); |
||
414 | fail_unless(ret == 4); |
||
415 | |||
416 | /* verify data */ |
||
417 | fail_unless(*((u8_t*)rmsg->msg_iov[0].iov_base) == 0xDE); |
||
418 | fail_unless(*((u8_t*)rmsg->msg_iov[1].iov_base) == 0xAD); |
||
419 | fail_unless(*((u8_t*)rmsg->msg_iov[2].iov_base) == 0xBE); |
||
420 | fail_unless(*((u8_t*)rmsg->msg_iov[3].iov_base) == 0xEF); |
||
421 | |||
422 | /* clear rcv_buf to ensure no data is being skipped */ |
||
423 | *((u8_t*)rmsg->msg_iov[0].iov_base) = 0x00; |
||
424 | *((u8_t*)rmsg->msg_iov[1].iov_base) = 0x00; |
||
425 | *((u8_t*)rmsg->msg_iov[2].iov_base) = 0x00; |
||
426 | *((u8_t*)rmsg->msg_iov[3].iov_base) = 0x00; |
||
427 | } |
||
428 | } |
||
429 | |||
430 | static void test_sockets_msgapi_udp(int domain) |
||
431 | { |
||
432 | int s, i, ret; |
||
433 | struct sockaddr_storage addr_storage; |
||
434 | socklen_t addr_size; |
||
435 | struct iovec riovs[4]; |
||
436 | struct msghdr rmsg; |
||
437 | u8_t rcv_buf[4]; |
||
438 | struct iovec siovs[4]; |
||
439 | struct msghdr smsg; |
||
440 | u8_t snd_buf[4] = {0xDE, 0xAD, 0xBE, 0xEF}; |
||
441 | |||
442 | /* initialize IO vectors with data */ |
||
443 | for (i = 0; i < 4; i++) { |
||
444 | siovs[i].iov_base = &snd_buf[i]; |
||
445 | siovs[i].iov_len = sizeof(u8_t); |
||
446 | riovs[i].iov_base = &rcv_buf[i]; |
||
447 | riovs[i].iov_len = sizeof(u8_t); |
||
448 | } |
||
449 | |||
450 | test_sockets_init_loopback_addr(domain, &addr_storage, &addr_size); |
||
451 | |||
452 | s = test_sockets_alloc_socket_nonblocking(domain, SOCK_DGRAM); |
||
453 | fail_unless(s >= 0); |
||
454 | |||
455 | ret = lwip_bind(s, (struct sockaddr*)&addr_storage, addr_size); |
||
456 | fail_unless(ret == 0); |
||
457 | |||
458 | /* Update addr with epehermal port */ |
||
459 | ret = lwip_getsockname(s, (struct sockaddr*)&addr_storage, &addr_size); |
||
460 | fail_unless(ret == 0); |
||
461 | switch(domain) { |
||
462 | #if LWIP_IPV6 |
||
463 | case AF_INET6: |
||
464 | fail_unless(addr_size == sizeof(struct sockaddr_in6)); |
||
465 | break; |
||
466 | #endif /* LWIP_IPV6 */ |
||
467 | #if LWIP_IPV4 |
||
468 | case AF_INET: |
||
469 | fail_unless(addr_size == sizeof(struct sockaddr_in)); |
||
470 | break; |
||
471 | #endif /* LWIP_IPV6 */ |
||
472 | default: |
||
473 | fail(); |
||
474 | break; |
||
475 | } |
||
476 | |||
477 | /* send and receive the datagram in 4 pieces */ |
||
478 | memset(&smsg, 0, sizeof(smsg)); |
||
479 | smsg.msg_iov = siovs; |
||
480 | smsg.msg_iovlen = 4; |
||
481 | memset(&rmsg, 0, sizeof(rmsg)); |
||
482 | rmsg.msg_iov = riovs; |
||
483 | rmsg.msg_iovlen = 4; |
||
484 | |||
485 | /* perform a sendmsg with remote host (self) */ |
||
486 | smsg.msg_name = &addr_storage; |
||
487 | smsg.msg_namelen = addr_size; |
||
488 | |||
489 | test_sockets_msgapi_udp_send_recv_loop(s, &smsg, &rmsg); |
||
490 | |||
491 | /* Connect to self, allowing us to not pass message name */ |
||
492 | ret = lwip_connect(s, (struct sockaddr*)&addr_storage, addr_size); |
||
493 | fail_unless(ret == 0); |
||
494 | |||
495 | smsg.msg_name = NULL; |
||
496 | smsg.msg_namelen = 0; |
||
497 | |||
498 | test_sockets_msgapi_udp_send_recv_loop(s, &smsg, &rmsg); |
||
499 | |||
500 | ret = lwip_close(s); |
||
501 | fail_unless(ret == 0); |
||
502 | } |
||
503 | |||
504 | #if LWIP_IPV4 |
||
505 | static void test_sockets_msgapi_cmsg(int domain) |
||
506 | { |
||
507 | int s, ret, enable; |
||
508 | struct sockaddr_storage addr_storage; |
||
509 | socklen_t addr_size; |
||
510 | struct iovec iov; |
||
511 | struct msghdr msg; |
||
512 | struct cmsghdr *cmsg; |
||
513 | struct in_pktinfo *pktinfo; |
||
514 | u8_t rcv_buf[4]; |
||
515 | u8_t snd_buf[4] = {0xDE, 0xAD, 0xBE, 0xEF}; |
||
516 | u8_t cmsg_buf[CMSG_SPACE(sizeof(struct in_pktinfo))]; |
||
517 | |||
518 | test_sockets_init_loopback_addr(domain, &addr_storage, &addr_size); |
||
519 | |||
520 | s = test_sockets_alloc_socket_nonblocking(domain, SOCK_DGRAM); |
||
521 | fail_unless(s >= 0); |
||
522 | |||
523 | ret = lwip_bind(s, (struct sockaddr*)&addr_storage, addr_size); |
||
524 | fail_unless(ret == 0); |
||
525 | |||
526 | /* Update addr with epehermal port */ |
||
527 | ret = lwip_getsockname(s, (struct sockaddr*)&addr_storage, &addr_size); |
||
528 | fail_unless(ret == 0); |
||
529 | |||
530 | enable = 1; |
||
531 | ret = lwip_setsockopt(s, IPPROTO_IP, IP_PKTINFO, &enable, sizeof(enable)); |
||
532 | fail_unless(ret == 0); |
||
533 | |||
534 | /* Receive full message, including control message */ |
||
535 | iov.iov_base = rcv_buf; |
||
536 | iov.iov_len = sizeof(rcv_buf); |
||
537 | msg.msg_control = cmsg_buf; |
||
538 | msg.msg_controllen = sizeof(cmsg_buf); |
||
539 | msg.msg_flags = 0; |
||
540 | msg.msg_iov = &iov; |
||
541 | msg.msg_iovlen = 1; |
||
542 | msg.msg_name = NULL; |
||
543 | msg.msg_namelen = 0; |
||
544 | |||
545 | memset(rcv_buf, 0, sizeof(rcv_buf)); |
||
546 | ret = lwip_sendto(s, snd_buf, sizeof(snd_buf), 0, (struct sockaddr*)&addr_storage, addr_size); |
||
547 | fail_unless(ret == sizeof(snd_buf)); |
||
548 | |||
549 | tcpip_thread_poll_one(); |
||
550 | |||
551 | ret = lwip_recvmsg(s, &msg, 0); |
||
552 | fail_unless(ret == sizeof(rcv_buf)); |
||
553 | fail_unless(!memcmp(rcv_buf, snd_buf, sizeof(rcv_buf))); |
||
554 | |||
555 | /* Verify message header */ |
||
556 | cmsg = CMSG_FIRSTHDR(&msg); |
||
557 | fail_unless(cmsg != NULL); |
||
558 | fail_unless(cmsg->cmsg_len > 0); |
||
559 | fail_unless(cmsg->cmsg_level == IPPROTO_IP); |
||
560 | fail_unless(cmsg->cmsg_type == IP_PKTINFO); |
||
561 | |||
562 | /* Verify message data */ |
||
563 | pktinfo = (struct in_pktinfo*)CMSG_DATA(cmsg); |
||
564 | /* We only have loopback interface enabled */ |
||
565 | fail_unless(pktinfo->ipi_ifindex == 1); |
||
566 | fail_unless(pktinfo->ipi_addr.s_addr == PP_HTONL(INADDR_LOOPBACK)); |
||
567 | |||
568 | /* Verify there are no additional messages */ |
||
569 | cmsg = CMSG_NXTHDR(&msg, cmsg); |
||
570 | fail_unless(cmsg == NULL); |
||
571 | |||
572 | /* Send datagram again, testing truncation */ |
||
573 | memset(rcv_buf, 0, sizeof(rcv_buf)); |
||
574 | ret = lwip_sendto(s, snd_buf, sizeof(snd_buf), 0, (struct sockaddr*)&addr_storage, addr_size); |
||
575 | fail_unless(ret == sizeof(snd_buf)); |
||
576 | |||
577 | tcpip_thread_poll_one(); |
||
578 | |||
579 | msg.msg_controllen = 1; |
||
580 | msg.msg_flags = 0; |
||
581 | ret = lwip_recvmsg(s, &msg, 0); |
||
582 | fail_unless(ret == sizeof(rcv_buf)); |
||
583 | fail_unless(!memcmp(rcv_buf, snd_buf, sizeof(rcv_buf))); |
||
584 | /* Ensure truncation was returned */ |
||
585 | fail_unless(msg.msg_flags & MSG_CTRUNC); |
||
586 | /* Ensure no control messages were returned */ |
||
587 | fail_unless(msg.msg_controllen == 0); |
||
588 | |||
589 | ret = lwip_close(s); |
||
590 | fail_unless(ret == 0); |
||
591 | } |
||
592 | #endif /* LWIP_IPV4 */ |
||
593 | |||
594 | START_TEST(test_sockets_msgapis) |
||
595 | { |
||
596 | LWIP_UNUSED_ARG(_i); |
||
597 | #if LWIP_IPV4 |
||
598 | test_sockets_msgapi_udp(AF_INET); |
||
599 | test_sockets_msgapi_tcp(AF_INET); |
||
600 | test_sockets_msgapi_cmsg(AF_INET); |
||
601 | #endif |
||
602 | #if LWIP_IPV6 |
||
603 | test_sockets_msgapi_udp(AF_INET6); |
||
604 | test_sockets_msgapi_tcp(AF_INET6); |
||
605 | #endif |
||
606 | } |
||
607 | END_TEST |
||
608 | |||
609 | START_TEST(test_sockets_select) |
||
610 | { |
||
611 | #if LWIP_SOCKET_SELECT |
||
612 | int s; |
||
613 | int ret; |
||
614 | fd_set readset; |
||
615 | fd_set writeset; |
||
616 | fd_set errset; |
||
617 | struct timeval tv; |
||
618 | |||
619 | fail_unless(test_sockets_get_used_count() == 0); |
||
620 | |||
621 | s = lwip_socket(AF_INET, SOCK_STREAM, 0); |
||
622 | fail_unless(s >= 0); |
||
623 | fail_unless(test_sockets_get_used_count() == 0); |
||
624 | |||
625 | FD_ZERO(&readset); |
||
626 | FD_SET(s, &readset); |
||
627 | FD_ZERO(&writeset); |
||
628 | FD_SET(s, &writeset); |
||
629 | FD_ZERO(&errset); |
||
630 | FD_SET(s, &errset); |
||
631 | |||
632 | tv.tv_sec = tv.tv_usec = 0; |
||
633 | ret = lwip_select(s + 1, &readset, &writeset, &errset, &tv); |
||
634 | fail_unless(ret == 0); |
||
635 | fail_unless(test_sockets_get_used_count() == 0); |
||
636 | |||
637 | ret = lwip_close(s); |
||
638 | fail_unless(ret == 0); |
||
639 | |||
640 | #endif |
||
641 | LWIP_UNUSED_ARG(_i); |
||
642 | } |
||
643 | END_TEST |
||
644 | |||
645 | START_TEST(test_sockets_recv_after_rst) |
||
646 | { |
||
647 | int sl, sact; |
||
648 | int spass = -1; |
||
649 | int ret; |
||
650 | struct sockaddr_in sa_listen; |
||
651 | const u16_t port = 1234; |
||
652 | int arg; |
||
653 | const char txbuf[] = "something"; |
||
654 | char rxbuf[16]; |
||
655 | struct lwip_sock *sact_sock; |
||
656 | int err; |
||
657 | LWIP_UNUSED_ARG(_i); |
||
658 | |||
659 | fail_unless(test_sockets_get_used_count() == 0); |
||
660 | |||
661 | memset(&sa_listen, 0, sizeof(sa_listen)); |
||
662 | sa_listen.sin_family = AF_INET; |
||
663 | sa_listen.sin_port = PP_HTONS(port); |
||
664 | sa_listen.sin_addr.s_addr = PP_HTONL(INADDR_LOOPBACK); |
||
665 | |||
666 | /* set up the listener */ |
||
667 | sl = lwip_socket(AF_INET, SOCK_STREAM, 0); |
||
668 | fail_unless(sl >= 0); |
||
669 | fail_unless(test_sockets_get_used_count() == 0); |
||
670 | |||
671 | ret = lwip_bind(sl, (struct sockaddr *)&sa_listen, sizeof(sa_listen)); |
||
672 | fail_unless(ret == 0); |
||
673 | ret = lwip_listen(sl, 0); |
||
674 | fail_unless(ret == 0); |
||
675 | |||
676 | /* set up the client */ |
||
677 | sact = lwip_socket(AF_INET, SOCK_STREAM, 0); |
||
678 | fail_unless(sact >= 0); |
||
679 | fail_unless(test_sockets_get_used_count() == 0); |
||
680 | /* set the client to nonblocking to simplify this test */ |
||
681 | arg = 1; |
||
682 | ret = lwip_ioctl(sact, FIONBIO, &arg); |
||
683 | fail_unless(ret == 0); |
||
684 | /* connect */ |
||
685 | do { |
||
686 | ret = lwip_connect(sact, (struct sockaddr *)&sa_listen, sizeof(sa_listen)); |
||
687 | err = errno; |
||
688 | fail_unless((ret == 0) || (ret == -1)); |
||
689 | if (ret != 0) { |
||
690 | if (err == EISCONN) { |
||
691 | /* Although this is not valid, use EISCONN as an indicator for successful connection. |
||
692 | This marks us as "connect phase is done". On error, we would either have a different |
||
693 | errno code or "send" fails later... -> good enough for this test. */ |
||
694 | ret = 0; |
||
695 | } else { |
||
696 | fail_unless(err == EINPROGRESS); |
||
697 | if (err != EINPROGRESS) { |
||
698 | goto cleanup; |
||
699 | } |
||
700 | /* we're in progress: little side check: test for EALREADY */ |
||
701 | ret = lwip_connect(sact, (struct sockaddr *)&sa_listen, sizeof(sa_listen)); |
||
702 | err = errno; |
||
703 | fail_unless(ret == -1); |
||
704 | fail_unless(err == EALREADY); |
||
705 | if ((ret != -1) || (err != EALREADY)) { |
||
706 | goto cleanup; |
||
707 | } |
||
708 | } |
||
709 | tcpip_thread_poll_one(); |
||
710 | tcpip_thread_poll_one(); |
||
711 | tcpip_thread_poll_one(); |
||
712 | tcpip_thread_poll_one(); |
||
713 | } |
||
714 | } while (ret != 0); |
||
715 | fail_unless(ret == 0); |
||
716 | |||
717 | /* accept the server connection part */ |
||
718 | spass = lwip_accept(sl, NULL, NULL); |
||
719 | fail_unless(spass >= 0); |
||
720 | |||
721 | /* write data from client */ |
||
722 | ret = lwip_send(sact, txbuf, sizeof(txbuf), 0); |
||
723 | fail_unless(ret == sizeof(txbuf)); |
||
724 | |||
725 | tcpip_thread_poll_one(); |
||
726 | tcpip_thread_poll_one(); |
||
727 | |||
728 | /* issue RST (This is a HACK, don't try this in your own app!) */ |
||
729 | sact_sock = lwip_socket_dbg_get_socket(sact); |
||
730 | fail_unless(sact_sock != NULL); |
||
731 | if (sact_sock != NULL) { |
||
732 | struct netconn *sact_conn = sact_sock->conn; |
||
733 | fail_unless(sact_conn != NULL); |
||
734 | if (sact_conn != NULL) { |
||
735 | struct tcp_pcb *pcb = sact_conn->pcb.tcp; |
||
736 | fail_unless(pcb != NULL); |
||
737 | if (pcb != NULL) { |
||
738 | tcp_rst(pcb, pcb->snd_nxt, pcb->rcv_nxt, &pcb->local_ip, &pcb->remote_ip, |
||
739 | pcb->local_port, pcb->remote_port); |
||
740 | } |
||
741 | } |
||
742 | } |
||
743 | tcpip_thread_poll_one(); |
||
744 | tcpip_thread_poll_one(); |
||
745 | |||
746 | /* expect to receive data first */ |
||
747 | ret = lwip_recv(spass, rxbuf, sizeof(rxbuf), 0); |
||
748 | fail_unless(ret > 0); |
||
749 | tcpip_thread_poll_one(); |
||
750 | tcpip_thread_poll_one(); |
||
751 | |||
752 | /* expect to receive RST indication */ |
||
753 | ret = lwip_recv(spass, rxbuf, sizeof(rxbuf), 0); |
||
754 | fail_unless(ret == -1); |
||
755 | err = errno; |
||
756 | fail_unless(err == ECONNRESET); |
||
757 | tcpip_thread_poll_one(); |
||
758 | tcpip_thread_poll_one(); |
||
759 | |||
760 | /* expect to receive ENOTCONN indication */ |
||
761 | ret = lwip_recv(spass, rxbuf, sizeof(rxbuf), 0); |
||
762 | fail_unless(ret == -1); |
||
763 | err = errno; |
||
764 | fail_unless(err == ENOTCONN); |
||
765 | tcpip_thread_poll_one(); |
||
766 | tcpip_thread_poll_one(); |
||
767 | |||
768 | /* expect to receive ENOTCONN indication */ |
||
769 | ret = lwip_recv(spass, rxbuf, sizeof(rxbuf), 0); |
||
770 | fail_unless(ret == -1); |
||
771 | err = errno; |
||
772 | fail_unless(err == ENOTCONN); |
||
773 | tcpip_thread_poll_one(); |
||
774 | tcpip_thread_poll_one(); |
||
775 | |||
776 | cleanup: |
||
777 | ret = lwip_close(sl); |
||
778 | fail_unless(ret == 0); |
||
779 | ret = lwip_close(sact); |
||
780 | fail_unless(ret == 0); |
||
781 | if (spass >= 0) { |
||
782 | ret = lwip_close(spass); |
||
783 | fail_unless(ret == 0); |
||
784 | } |
||
785 | } |
||
786 | END_TEST |
||
787 | |||
788 | /** Create the suite including all tests for this module */ |
||
789 | Suite * |
||
790 | sockets_suite(void) |
||
791 | { |
||
792 | testfunc tests[] = { |
||
793 | TESTFUNC(test_sockets_basics), |
||
794 | TESTFUNC(test_sockets_allfunctions_basic), |
||
795 | TESTFUNC(test_sockets_msgapis), |
||
796 | TESTFUNC(test_sockets_select), |
||
797 | TESTFUNC(test_sockets_recv_after_rst), |
||
798 | }; |
||
799 | return create_suite("SOCKETS", tests, sizeof(tests)/sizeof(testfunc), sockets_setup, sockets_teardown); |
||
800 | } |
||
801 | |||
802 | #else /* LWIP_SOCKET */ |
||
803 | |||
804 | Suite * |
||
805 | sockets_suite(void) |
||
806 | { |
||
807 | return create_suite("SOCKETS", NULL, 0, NULL, NULL); |
||
808 | } |
||
809 | #endif /* LWIP_SOCKET */ |