1 /* SCTP kernel Implementation
2 * (C) Copyright IBM Corp. 2001, 2003
3 * Copyright (c) 1999-2000 Cisco, Inc.
4 * Copyright (c) 1999-2001 Motorola, Inc.
5 * Copyright (c) 2001 Intel Corp.
6 * Copyright (c) 2001 Nokia, Inc.
7 *
8 * The SCTP implementation is free software;
9 * you can redistribute it and/or modify it under the terms of
10 * the GNU General Public License as published by
11 * the Free Software Foundation; either version 2, or (at your option)
12 * any later version.
13 *
14 * The SCTP implementation is distributed in the hope that it
15 * will be useful, but WITHOUT ANY WARRANTY; without even the implied
16 * ************************
17 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18 * See the GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with GNU CC; see the file COPYING. If not, write to
22 * the Free Software Foundation, 59 Temple Place - Suite 330,
23 * Boston, MA 02111-1307, USA.
24 *
25 * Please send any bug reports or fixes you make to the
26 * email address(es):
27 * lksctp developers <lksctp-developers@lists.sourceforge.net>
28 *
29 * Or submit a bug report through the following website:
30 * http://www.sf.net/projects/lksctp
31 *
32 * Any bugs reported to us we will try to fix... any fixes shared will
33 * be incorporated into the next SCTP release.
34 *
35 * Written or modified by:
36 * Jon Grimm <jgrimm@us.ibm.com>
37 * Sridhar Samudrala <sri@us.ibm.com>
38 */
39
40 /*
41 * This is a basic functional test for the SCTP kernel
42 * implementation of sndrcvinfo.sinfo_timetolive.
43 *
44 * 1) Create two sockets, the listening socket sets its RECVBUF small
45 * 2) Create a connection. Send enough data to the non-reading listener
46 * to fill the RCVBUF.
47 * 5) Set sinfo_timetolive on a message and send.
48 * 6) Disable sinfo_timetolive on a message and send.
49 * 7) Wait sinfo_timetolive.
50 * 8) Read out all the data at the receiver.
51 * 9) Make sure timed out message did not make it.
52 * 10) Make sure that the message with no timeout makes it to the receiver.
53 *
54 * Also test with SEND_FAILED notifications. Also, use a fragmented
55 * message so as to also exercise the SEND_FAILED of fragmentation
56 * code.
57 */
58
59 #include <stdio.h>
60 #include <unistd.h>
61 #include <stdlib.h>
62 #include <string.h>
63 #include <sys/types.h>
64 #include <sys/socket.h>
65 #include <sys/uio.h>
66 #include <netinet/in.h>
67 #include <sys/errno.h>
68 #include <errno.h>
69 #include <netinet/sctp.h>
70 #include <sctputil.h>
71 #include "tst_kernel.h"
72
73 char *TCID = __FILE__;
74 int TST_TOTAL = 6;
75 int TST_CNT = 0;
76
77 /* This is the size of our RCVBUF */
78 #define SMALL_RCVBUF 3000
79
80 /* MAX segment size */
81 #define SMALL_MAXSEG 500
82
83 /* RWND_SLOP is the extra data that fills up the rwnd */
84 #define RWND_SLOP 100
85 static char *fillmsg = NULL;
86 static char *ttlmsg = "This should time out!\n";
87 static char *nottlmsg = "This should NOT time out!\n";
88 static char ttlfrag[SMALL_MAXSEG*3] = {0};
89 static char *message = "Hello world\n";
90
main(void)91 int main(void)
92 {
93 int sk1, sk2;
94 sockaddr_storage_t loop1;
95 sockaddr_storage_t loop2;
96 struct iovec iov;
97 struct msghdr inmessage;
98 struct msghdr outmessage;
99 char incmsg[CMSG_SPACE(sizeof(sctp_cmsg_data_t))];
100 char outcmsg[CMSG_SPACE(sizeof(struct sctp_sndrcvinfo))];
101 struct cmsghdr *cmsg;
102 struct sctp_sndrcvinfo *sinfo;
103 struct iovec out_iov;
104 int error;
105 int pf_class;
106 uint32_t ppid;
107 uint32_t stream;
108 sctp_assoc_t associd1;
109 struct sctp_assoc_change *sac;
110 struct sctp_event_subscribe subscribe;
111 char *big_buffer;
112 int offset;
113 struct sctp_send_failed *ssf;
114 socklen_t len; /* Really becomes 2xlen when set. */
115 int orig_len;
116 struct sctp_status gstatus;
117
118 if (tst_check_driver("sctp"))
119 tst_brkm(TCONF, tst_exit, "sctp driver not available");
120
121 /* Rather than fflush() throughout the code, set stdout to
122 * be unbuffered.
123 */
124 setvbuf(stdout, NULL, _IONBF, 0);
125
126 /* Set some basic values which depend on the address family. */
127 #if TEST_V6
128 pf_class = PF_INET6;
129
130 loop1.v6.sin6_family = AF_INET6;
131 loop1.v6.sin6_addr = in6addr_loopback;
132 loop1.v6.sin6_port = htons(SCTP_TESTPORT_1);
133
134 loop2.v6.sin6_family = AF_INET6;
135 loop2.v6.sin6_addr = in6addr_loopback;
136 loop2.v6.sin6_port = htons(SCTP_TESTPORT_2);
137 #else
138 pf_class = PF_INET;
139
140 loop1.v4.sin_family = AF_INET;
141 loop1.v4.sin_addr.s_addr = SCTP_IP_LOOPBACK;
142 loop1.v4.sin_port = htons(SCTP_TESTPORT_1);
143
144 loop2.v4.sin_family = AF_INET;
145 loop2.v4.sin_addr.s_addr = SCTP_IP_LOOPBACK;
146 loop2.v4.sin_port = htons(SCTP_TESTPORT_2);
147 #endif /* TEST_V6 */
148
149 /* Create the two endpoints which will talk to each other. */
150 sk1 = test_socket(pf_class, SOCK_SEQPACKET, IPPROTO_SCTP);
151 sk2 = test_socket(pf_class, SOCK_SEQPACKET, IPPROTO_SCTP);
152
153 len = sizeof(int);
154 error = getsockopt(sk2, SOL_SOCKET, SO_RCVBUF, &orig_len,
155 &len);
156 if (error)
157 tst_brkm(TBROK, tst_exit, "can't get rcvbuf size: %s",
158 strerror(errno));
159 /* Set the MAXSEG to something smallish. */
160 {
161 int val = SMALL_MAXSEG;
162 test_setsockopt(sk1, SCTP_MAXSEG, &val, sizeof(val));
163 }
164
165 memset(&subscribe, 0, sizeof(subscribe));
166 subscribe.sctp_data_io_event = 1;
167 subscribe.sctp_association_event = 1;
168 subscribe.sctp_send_failure_event = 1;
169 test_setsockopt(sk1, SCTP_EVENTS, &subscribe, sizeof(subscribe));
170 test_setsockopt(sk2, SCTP_EVENTS, &subscribe, sizeof(subscribe));
171
172 /* Bind these sockets to the test ports. */
173 test_bind(sk1, &loop1.sa, sizeof(loop1));
174 test_bind(sk2, &loop2.sa, sizeof(loop2));
175
176 /*
177 * This code sets the associations RWND very small so we can
178 * fill it. It does this by manipulating the rcvbuf as follows:
179 * 1) Reduce the rcvbuf size on the socket
180 * 2) create an association so that we advertize rcvbuf/2 as
181 * our initial rwnd
182 * 3) raise the rcvbuf value so that we don't drop data wile
183 * receiving later data
184 */
185 len = SMALL_RCVBUF;
186 error = setsockopt(sk2, SOL_SOCKET, SO_RCVBUF, &len,
187 sizeof(len));
188 if (error)
189 tst_brkm(TBROK, tst_exit, "setsockopt(SO_RCVBUF): %s",
190 strerror(errno));
191
192 /* Mark sk2 as being able to accept new associations. */
193 test_listen(sk2, 1);
194
195 /* Send the first message. This will create the association. */
196 outmessage.msg_name = &loop2;
197 outmessage.msg_namelen = sizeof(loop2);
198 outmessage.msg_iov = &out_iov;
199 outmessage.msg_iovlen = 1;
200 outmessage.msg_control = outcmsg;
201 outmessage.msg_controllen = sizeof(outcmsg);
202 outmessage.msg_flags = 0;
203 cmsg = CMSG_FIRSTHDR(&outmessage);
204 cmsg->cmsg_level = IPPROTO_SCTP;
205 cmsg->cmsg_type = SCTP_SNDRCV;
206 cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_sndrcvinfo));
207 outmessage.msg_controllen = cmsg->cmsg_len;
208 sinfo = (struct sctp_sndrcvinfo *)CMSG_DATA(cmsg);
209 memset(sinfo, 0x00, sizeof(struct sctp_sndrcvinfo));
210 ppid = rand(); /* Choose an arbitrary value. */
211 stream = 1;
212 sinfo->sinfo_ppid = ppid;
213 sinfo->sinfo_stream = stream;
214 outmessage.msg_iov->iov_base = message;
215 outmessage.msg_iov->iov_len = strlen(message) + 1;
216 test_sendmsg(sk1, &outmessage, 0, strlen(message)+1);
217
218 /* Initialize inmessage for all receives. */
219 big_buffer = test_malloc(REALLY_BIG);
220 memset(&inmessage, 0, sizeof(inmessage));
221 iov.iov_base = big_buffer;
222 iov.iov_len = REALLY_BIG;
223 inmessage.msg_iov = &iov;
224 inmessage.msg_iovlen = 1;
225 inmessage.msg_control = incmsg;
226
227 /* Get the communication up message on sk2. */
228 inmessage.msg_controllen = sizeof(incmsg);
229 error = test_recvmsg(sk2, &inmessage, MSG_WAITALL);
230 test_check_msg_notification(&inmessage, error,
231 sizeof(struct sctp_assoc_change),
232 SCTP_ASSOC_CHANGE, SCTP_COMM_UP);
233 #if 0
234 sac = (struct sctp_assoc_change *)iov.iov_base;
235 associd2 = sac->sac_assoc_id;
236 #endif
237
238 /* Get the communication up message on sk1. */
239 inmessage.msg_controllen = sizeof(incmsg);
240 error = test_recvmsg(sk1, &inmessage, MSG_WAITALL);
241 test_check_msg_notification(&inmessage, error,
242 sizeof(struct sctp_assoc_change),
243 SCTP_ASSOC_CHANGE, SCTP_COMM_UP);
244 sac = (struct sctp_assoc_change *)iov.iov_base;
245 associd1 = sac->sac_assoc_id;
246
247 /* restore the rcvbuffer size for the receiving socket */
248 error = setsockopt(sk2, SOL_SOCKET, SO_RCVBUF, &orig_len,
249 sizeof(orig_len));
250
251 if (error)
252 tst_brkm(TBROK, tst_exit, "setsockopt(SO_RCVBUF): %s",
253 strerror(errno));
254
255 /* Get the first data message which was sent. */
256 inmessage.msg_controllen = sizeof(incmsg);
257 error = test_recvmsg(sk2, &inmessage, MSG_WAITALL);
258 test_check_msg_data(&inmessage, error, strlen(message) + 1,
259 MSG_EOR, stream, ppid);
260
261 /* Figure out how big to make our fillmsg */
262 len = sizeof(struct sctp_status);
263 memset(&gstatus,0,sizeof(struct sctp_status));
264 gstatus.sstat_assoc_id = associd1;
265 error = getsockopt(sk1, IPPROTO_SCTP, SCTP_STATUS, &gstatus, &len);
266
267 if (error)
268 tst_brkm(TBROK, tst_exit, "can't get rwnd size: %s",
269 strerror(errno));
270 tst_resm(TINFO, "Creating fillmsg of size %d",
271 gstatus.sstat_rwnd+RWND_SLOP);
272 fillmsg = malloc(gstatus.sstat_rwnd+RWND_SLOP);
273
274 /* Send a fillmsg */
275 outmessage.msg_controllen = sizeof(outcmsg);
276 outmessage.msg_flags = 0;
277 cmsg = CMSG_FIRSTHDR(&outmessage);
278 cmsg->cmsg_level = IPPROTO_SCTP;
279 cmsg->cmsg_type = SCTP_SNDRCV;
280 cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_sndrcvinfo));
281 outmessage.msg_controllen = cmsg->cmsg_len;
282 sinfo = (struct sctp_sndrcvinfo *)CMSG_DATA(cmsg);
283 memset(sinfo, 0x00, sizeof(struct sctp_sndrcvinfo));
284 ppid++;
285 stream++;
286 sinfo->sinfo_ppid = ppid;
287 sinfo->sinfo_stream = stream;
288 memset(fillmsg, 'X', gstatus.sstat_rwnd+RWND_SLOP);
289 fillmsg[gstatus.sstat_rwnd+RWND_SLOP-1] = '\0';
290 outmessage.msg_iov->iov_base = fillmsg;
291 outmessage.msg_iov->iov_len = gstatus.sstat_rwnd+RWND_SLOP;
292 outmessage.msg_name = NULL;
293 outmessage.msg_namelen = 0;
294 sinfo->sinfo_assoc_id = associd1;
295 sinfo->sinfo_timetolive = 0;
296 test_sendmsg(sk1, &outmessage, MSG_NOSIGNAL,
297 gstatus.sstat_rwnd+RWND_SLOP);
298
299 /* Now send the message with timeout. */
300 sinfo->sinfo_ppid = ppid;
301 sinfo->sinfo_stream = stream;
302 outmessage.msg_iov->iov_base = ttlmsg;
303 outmessage.msg_iov->iov_len = strlen(ttlmsg) + 1;
304 outmessage.msg_name = NULL;
305 outmessage.msg_namelen = 0;
306 sinfo->sinfo_assoc_id = associd1;
307 sinfo->sinfo_timetolive = 2000;
308 test_sendmsg(sk1, &outmessage, MSG_NOSIGNAL, strlen(ttlmsg) + 1);
309
310 tst_resm(TPASS, "Send a message with timeout");
311
312 /* Next send a message with no timeout. */
313 sinfo->sinfo_ppid = ppid;
314 sinfo->sinfo_stream = stream;
315 outmessage.msg_iov->iov_base = nottlmsg;
316 outmessage.msg_iov->iov_len = strlen(nottlmsg) + 1;
317 outmessage.msg_name = NULL;
318 outmessage.msg_namelen = 0;
319 sinfo->sinfo_assoc_id = associd1;
320 sinfo->sinfo_timetolive = 0;
321 test_sendmsg(sk1, &outmessage, MSG_NOSIGNAL, strlen(nottlmsg)+1);
322
323 tst_resm(TPASS, "Send a message with no timeout");
324
325 /* And finally a fragmented message that will time out. */
326 sinfo->sinfo_ppid = ppid;
327 sinfo->sinfo_stream = stream;
328 memset(ttlfrag, '0', sizeof(ttlfrag));
329 ttlfrag[sizeof(ttlfrag)-1] = '\0';
330 outmessage.msg_iov->iov_base = ttlfrag;
331 outmessage.msg_iov->iov_len = sizeof(ttlfrag);
332 outmessage.msg_name = NULL;
333 outmessage.msg_namelen = 0;
334 sinfo->sinfo_assoc_id = associd1;
335 sinfo->sinfo_timetolive = 2000;
336 test_sendmsg(sk1, &outmessage, MSG_NOSIGNAL, sizeof(ttlfrag));
337
338 tst_resm(TPASS, "Send a fragmented message with timeout");
339
340 /* Sleep waiting for the message to time out. */
341 tst_resm(TINFO, " ** SLEEPING for 3 seconds **");
342 sleep(3);
343
344 /* Read the fillmsg snuck in between the ttl'd messages. */
345 do {
346 inmessage.msg_controllen = sizeof(incmsg);
347 error = test_recvmsg(sk2, &inmessage, MSG_WAITALL);
348 } while (!(inmessage.msg_flags & MSG_EOR));
349
350 /* Now get the message that did NOT time out. */
351 inmessage.msg_controllen = sizeof(incmsg);
352 error = test_recvmsg(sk2, &inmessage, MSG_WAITALL);
353 test_check_msg_data(&inmessage, error, strlen(nottlmsg) + 1,
354 MSG_EOR, stream, ppid);
355 if (0 != strncmp(iov.iov_base, nottlmsg, strlen(nottlmsg)+1))
356 tst_brkm(TBROK, tst_exit, "Received Wrong Message !!!");
357
358 tst_resm(TPASS, "Receive message with no timeout");
359
360 /* Get the SEND_FAILED notification for the message that DID
361 * time out.
362 */
363 inmessage.msg_controllen = sizeof(incmsg);
364 error = test_recvmsg(sk1, &inmessage, MSG_WAITALL);
365 test_check_msg_notification(&inmessage, error,
366 sizeof(struct sctp_send_failed) +
367 strlen(ttlmsg) + 1,
368 SCTP_SEND_FAILED, 0);
369 ssf = (struct sctp_send_failed *)iov.iov_base;
370 if (0 != strncmp(ttlmsg, (char *)ssf->ssf_data, strlen(ttlmsg) + 1))
371 tst_brkm(TBROK, tst_exit, "SEND_FAILED data mismatch");
372
373 tst_resm(TPASS, "Receive SEND_FAILED for message with timeout");
374
375 /* Get the SEND_FAILED notification for the fragmented message that
376 * DID time out.
377 */
378 offset = 0;
379 do {
380 inmessage.msg_controllen = sizeof(incmsg);
381 error = test_recvmsg(sk1, &inmessage, MSG_WAITALL);
382 test_check_msg_notification(&inmessage, error,
383 sizeof(struct sctp_send_failed) +
384 SMALL_MAXSEG,
385 SCTP_SEND_FAILED, 0);
386 ssf = (struct sctp_send_failed *)iov.iov_base;
387 if (0 != strncmp(&ttlfrag[offset], (char *)ssf->ssf_data,
388 SMALL_MAXSEG))
389 tst_brkm(TBROK, tst_exit, "SEND_FAILED data mismatch");
390 offset += SMALL_MAXSEG;
391 } while (!(ssf->ssf_info.sinfo_flags & 0x01)); /* LAST_FRAG */
392
393 tst_resm(TPASS, "Receive SEND_FAILED for fragmented message with "
394 "timeout");
395
396 /* Shut down the link. */
397 close(sk1);
398
399 /* Get the shutdown complete notification. */
400 inmessage.msg_controllen = sizeof(incmsg);
401 error = test_recvmsg(sk2, &inmessage, MSG_WAITALL);
402 test_check_msg_notification(&inmessage, error,
403 sizeof(struct sctp_assoc_change),
404 SCTP_ASSOC_CHANGE, SCTP_SHUTDOWN_COMP);
405
406 close(sk2);
407
408 /* Indicate successful completion. */
409 return 0;
410 }
411