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
72 char *TCID = __FILE__;
73 int TST_TOTAL = 6;
74 int TST_CNT = 0;
75
76 /* This is the size of our RCVBUF */
77 #define SMALL_RCVBUF 3000
78
79 /* MAX segment size */
80 #define SMALL_MAXSEG 500
81
82 /* RWND_SLOP is the extra data that fills up the rwnd */
83 #define RWND_SLOP 100
84 static char *fillmsg = NULL;
85 static char *ttlmsg = "This should time out!\n";
86 static char *nottlmsg = "This should NOT time out!\n";
87 static char ttlfrag[SMALL_MAXSEG*3] = {0};
88 static char *message = "Hello world\n";
89
main(int argc,char * argv[])90 int main(int argc, char *argv[])
91 {
92 int sk1, sk2;
93 sockaddr_storage_t loop1;
94 sockaddr_storage_t loop2;
95 struct iovec iov;
96 struct msghdr inmessage;
97 struct msghdr outmessage;
98 char incmsg[CMSG_SPACE(sizeof(sctp_cmsg_data_t))];
99 char outcmsg[CMSG_SPACE(sizeof(struct sctp_sndrcvinfo))];
100 struct cmsghdr *cmsg;
101 struct sctp_sndrcvinfo *sinfo;
102 struct iovec out_iov;
103 int error;
104 int pf_class;
105 uint32_t ppid;
106 uint32_t stream;
107 sctp_assoc_t associd1;
108 struct sctp_assoc_change *sac;
109 struct sctp_event_subscribe subscribe;
110 char *big_buffer;
111 int offset;
112 struct sctp_send_failed *ssf;
113 socklen_t len; /* Really becomes 2xlen when set. */
114 int orig_len;
115 struct sctp_status gstatus;
116
117 /* Rather than fflush() throughout the code, set stdout to
118 * be unbuffered.
119 */
120 setvbuf(stdout, NULL, _IONBF, 0);
121
122 /* Set some basic values which depend on the address family. */
123 #if TEST_V6
124 pf_class = PF_INET6;
125
126 loop1.v6.sin6_family = AF_INET6;
127 loop1.v6.sin6_addr = in6addr_loopback;
128 loop1.v6.sin6_port = htons(SCTP_TESTPORT_1);
129
130 loop2.v6.sin6_family = AF_INET6;
131 loop2.v6.sin6_addr = in6addr_loopback;
132 loop2.v6.sin6_port = htons(SCTP_TESTPORT_2);
133 #else
134 pf_class = PF_INET;
135
136 loop1.v4.sin_family = AF_INET;
137 loop1.v4.sin_addr.s_addr = SCTP_IP_LOOPBACK;
138 loop1.v4.sin_port = htons(SCTP_TESTPORT_1);
139
140 loop2.v4.sin_family = AF_INET;
141 loop2.v4.sin_addr.s_addr = SCTP_IP_LOOPBACK;
142 loop2.v4.sin_port = htons(SCTP_TESTPORT_2);
143 #endif /* TEST_V6 */
144
145 /* Create the two endpoints which will talk to each other. */
146 sk1 = test_socket(pf_class, SOCK_SEQPACKET, IPPROTO_SCTP);
147 sk2 = test_socket(pf_class, SOCK_SEQPACKET, IPPROTO_SCTP);
148
149 len = sizeof(int);
150 error = getsockopt(sk2, SOL_SOCKET, SO_RCVBUF, &orig_len,
151 &len);
152 if (error)
153 tst_brkm(TBROK, tst_exit, "can't get rcvbuf size: %s",
154 strerror(errno));
155 /* Set the MAXSEG to something smallish. */
156 {
157 int val = SMALL_MAXSEG;
158 test_setsockopt(sk1, SCTP_MAXSEG, &val, sizeof(val));
159 }
160
161 memset(&subscribe, 0, sizeof(subscribe));
162 subscribe.sctp_data_io_event = 1;
163 subscribe.sctp_association_event = 1;
164 subscribe.sctp_send_failure_event = 1;
165 test_setsockopt(sk1, SCTP_EVENTS, &subscribe, sizeof(subscribe));
166 test_setsockopt(sk2, SCTP_EVENTS, &subscribe, sizeof(subscribe));
167
168 /* Bind these sockets to the test ports. */
169 test_bind(sk1, &loop1.sa, sizeof(loop1));
170 test_bind(sk2, &loop2.sa, sizeof(loop2));
171
172 /*
173 * This code sets the associations RWND very small so we can
174 * fill it. It does this by manipulating the rcvbuf as follows:
175 * 1) Reduce the rcvbuf size on the socket
176 * 2) create an association so that we advertize rcvbuf/2 as
177 * our initial rwnd
178 * 3) raise the rcvbuf value so that we don't drop data wile
179 * receiving later data
180 */
181 len = SMALL_RCVBUF;
182 error = setsockopt(sk2, SOL_SOCKET, SO_RCVBUF, &len,
183 sizeof(len));
184 if (error)
185 tst_brkm(TBROK, tst_exit, "setsockopt(SO_RCVBUF): %s",
186 strerror(errno));
187
188 /* Mark sk2 as being able to accept new associations. */
189 test_listen(sk2, 1);
190
191 /* Send the first message. This will create the association. */
192 outmessage.msg_name = &loop2;
193 outmessage.msg_namelen = sizeof(loop2);
194 outmessage.msg_iov = &out_iov;
195 outmessage.msg_iovlen = 1;
196 outmessage.msg_control = outcmsg;
197 outmessage.msg_controllen = sizeof(outcmsg);
198 outmessage.msg_flags = 0;
199 cmsg = CMSG_FIRSTHDR(&outmessage);
200 cmsg->cmsg_level = IPPROTO_SCTP;
201 cmsg->cmsg_type = SCTP_SNDRCV;
202 cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_sndrcvinfo));
203 outmessage.msg_controllen = cmsg->cmsg_len;
204 sinfo = (struct sctp_sndrcvinfo *)CMSG_DATA(cmsg);
205 memset(sinfo, 0x00, sizeof(struct sctp_sndrcvinfo));
206 ppid = rand(); /* Choose an arbitrary value. */
207 stream = 1;
208 sinfo->sinfo_ppid = ppid;
209 sinfo->sinfo_stream = stream;
210 outmessage.msg_iov->iov_base = message;
211 outmessage.msg_iov->iov_len = strlen(message) + 1;
212 test_sendmsg(sk1, &outmessage, 0, strlen(message)+1);
213
214 /* Initialize inmessage for all receives. */
215 big_buffer = test_malloc(REALLY_BIG);
216 memset(&inmessage, 0, sizeof(inmessage));
217 iov.iov_base = big_buffer;
218 iov.iov_len = REALLY_BIG;
219 inmessage.msg_iov = &iov;
220 inmessage.msg_iovlen = 1;
221 inmessage.msg_control = incmsg;
222
223 /* Get the communication up message on sk2. */
224 inmessage.msg_controllen = sizeof(incmsg);
225 error = test_recvmsg(sk2, &inmessage, MSG_WAITALL);
226 test_check_msg_notification(&inmessage, error,
227 sizeof(struct sctp_assoc_change),
228 SCTP_ASSOC_CHANGE, SCTP_COMM_UP);
229 #if 0
230 sac = (struct sctp_assoc_change *)iov.iov_base;
231 associd2 = sac->sac_assoc_id;
232 #endif
233
234 /* Get the communication up message on sk1. */
235 inmessage.msg_controllen = sizeof(incmsg);
236 error = test_recvmsg(sk1, &inmessage, MSG_WAITALL);
237 test_check_msg_notification(&inmessage, error,
238 sizeof(struct sctp_assoc_change),
239 SCTP_ASSOC_CHANGE, SCTP_COMM_UP);
240 sac = (struct sctp_assoc_change *)iov.iov_base;
241 associd1 = sac->sac_assoc_id;
242
243 /* restore the rcvbuffer size for the receiving socket */
244 error = setsockopt(sk2, SOL_SOCKET, SO_RCVBUF, &orig_len,
245 sizeof(orig_len));
246
247 if (error)
248 tst_brkm(TBROK, tst_exit, "setsockopt(SO_RCVBUF): %s",
249 strerror(errno));
250
251 /* Get the first data message which was sent. */
252 inmessage.msg_controllen = sizeof(incmsg);
253 error = test_recvmsg(sk2, &inmessage, MSG_WAITALL);
254 test_check_msg_data(&inmessage, error, strlen(message) + 1,
255 MSG_EOR, stream, ppid);
256
257 /* Figure out how big to make our fillmsg */
258 len = sizeof(struct sctp_status);
259 memset(&gstatus,0,sizeof(struct sctp_status));
260 gstatus.sstat_assoc_id = associd1;
261 error = getsockopt(sk1, IPPROTO_SCTP, SCTP_STATUS, &gstatus, &len);
262
263 if (error)
264 tst_brkm(TBROK, tst_exit, "can't get rwnd size: %s",
265 strerror(errno));
266 tst_resm(TINFO, "Creating fillmsg of size %d",
267 gstatus.sstat_rwnd+RWND_SLOP);
268 fillmsg = malloc(gstatus.sstat_rwnd+RWND_SLOP);
269
270 /* Send a fillmsg */
271 outmessage.msg_controllen = sizeof(outcmsg);
272 outmessage.msg_flags = 0;
273 cmsg = CMSG_FIRSTHDR(&outmessage);
274 cmsg->cmsg_level = IPPROTO_SCTP;
275 cmsg->cmsg_type = SCTP_SNDRCV;
276 cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_sndrcvinfo));
277 outmessage.msg_controllen = cmsg->cmsg_len;
278 sinfo = (struct sctp_sndrcvinfo *)CMSG_DATA(cmsg);
279 memset(sinfo, 0x00, sizeof(struct sctp_sndrcvinfo));
280 ppid++;
281 stream++;
282 sinfo->sinfo_ppid = ppid;
283 sinfo->sinfo_stream = stream;
284 memset(fillmsg, 'X', gstatus.sstat_rwnd+RWND_SLOP);
285 fillmsg[gstatus.sstat_rwnd+RWND_SLOP-1] = '\0';
286 outmessage.msg_iov->iov_base = fillmsg;
287 outmessage.msg_iov->iov_len = gstatus.sstat_rwnd+RWND_SLOP;
288 outmessage.msg_name = NULL;
289 outmessage.msg_namelen = 0;
290 sinfo->sinfo_assoc_id = associd1;
291 sinfo->sinfo_timetolive = 0;
292 test_sendmsg(sk1, &outmessage, MSG_NOSIGNAL,
293 gstatus.sstat_rwnd+RWND_SLOP);
294
295 /* Now send the message with timeout. */
296 sinfo->sinfo_ppid = ppid;
297 sinfo->sinfo_stream = stream;
298 outmessage.msg_iov->iov_base = ttlmsg;
299 outmessage.msg_iov->iov_len = strlen(ttlmsg) + 1;
300 outmessage.msg_name = NULL;
301 outmessage.msg_namelen = 0;
302 sinfo->sinfo_assoc_id = associd1;
303 sinfo->sinfo_timetolive = 2000;
304 test_sendmsg(sk1, &outmessage, MSG_NOSIGNAL, strlen(ttlmsg) + 1);
305
306 tst_resm(TPASS, "Send a message with timeout");
307
308 /* Next send a message with no timeout. */
309 sinfo->sinfo_ppid = ppid;
310 sinfo->sinfo_stream = stream;
311 outmessage.msg_iov->iov_base = nottlmsg;
312 outmessage.msg_iov->iov_len = strlen(nottlmsg) + 1;
313 outmessage.msg_name = NULL;
314 outmessage.msg_namelen = 0;
315 sinfo->sinfo_assoc_id = associd1;
316 sinfo->sinfo_timetolive = 0;
317 test_sendmsg(sk1, &outmessage, MSG_NOSIGNAL, strlen(nottlmsg)+1);
318
319 tst_resm(TPASS, "Send a message with no timeout");
320
321 /* And finally a fragmented message that will time out. */
322 sinfo->sinfo_ppid = ppid;
323 sinfo->sinfo_stream = stream;
324 memset(ttlfrag, '0', sizeof(ttlfrag));
325 ttlfrag[sizeof(ttlfrag)-1] = '\0';
326 outmessage.msg_iov->iov_base = ttlfrag;
327 outmessage.msg_iov->iov_len = sizeof(ttlfrag);
328 outmessage.msg_name = NULL;
329 outmessage.msg_namelen = 0;
330 sinfo->sinfo_assoc_id = associd1;
331 sinfo->sinfo_timetolive = 2000;
332 test_sendmsg(sk1, &outmessage, MSG_NOSIGNAL, sizeof(ttlfrag));
333
334 tst_resm(TPASS, "Send a fragmented message with timeout");
335
336 /* Sleep waiting for the message to time out. */
337 tst_resm(TINFO, " ** SLEEPING for 3 seconds **");
338 sleep(3);
339
340 /* Read the fillmsg snuck in between the ttl'd messages. */
341 do {
342 inmessage.msg_controllen = sizeof(incmsg);
343 error = test_recvmsg(sk2, &inmessage, MSG_WAITALL);
344 } while (!(inmessage.msg_flags & MSG_EOR));
345
346 /* Now get the message that did NOT time out. */
347 inmessage.msg_controllen = sizeof(incmsg);
348 error = test_recvmsg(sk2, &inmessage, MSG_WAITALL);
349 test_check_msg_data(&inmessage, error, strlen(nottlmsg) + 1,
350 MSG_EOR, stream, ppid);
351 if (0 != strncmp(iov.iov_base, nottlmsg, strlen(nottlmsg)+1))
352 tst_brkm(TBROK, tst_exit, "Received Wrong Message !!!");
353
354 tst_resm(TPASS, "Receive message with no timeout");
355
356 /* Get the SEND_FAILED notification for the message that DID
357 * time out.
358 */
359 inmessage.msg_controllen = sizeof(incmsg);
360 error = test_recvmsg(sk1, &inmessage, MSG_WAITALL);
361 test_check_msg_notification(&inmessage, error,
362 sizeof(struct sctp_send_failed) +
363 strlen(ttlmsg) + 1,
364 SCTP_SEND_FAILED, 0);
365 ssf = (struct sctp_send_failed *)iov.iov_base;
366 if (0 != strncmp(ttlmsg, (char *)ssf->ssf_data, strlen(ttlmsg) + 1))
367 tst_brkm(TBROK, tst_exit, "SEND_FAILED data mismatch");
368
369 tst_resm(TPASS, "Receive SEND_FAILED for message with timeout");
370
371 /* Get the SEND_FAILED notification for the fragmented message that
372 * DID time out.
373 */
374 offset = 0;
375 do {
376 inmessage.msg_controllen = sizeof(incmsg);
377 error = test_recvmsg(sk1, &inmessage, MSG_WAITALL);
378 test_check_msg_notification(&inmessage, error,
379 sizeof(struct sctp_send_failed) +
380 SMALL_MAXSEG,
381 SCTP_SEND_FAILED, 0);
382 ssf = (struct sctp_send_failed *)iov.iov_base;
383 if (0 != strncmp(&ttlfrag[offset], (char *)ssf->ssf_data,
384 SMALL_MAXSEG))
385 tst_brkm(TBROK, tst_exit, "SEND_FAILED data mismatch");
386 offset += SMALL_MAXSEG;
387 } while (!(ssf->ssf_info.sinfo_flags & 0x01)); /* LAST_FRAG */
388
389 tst_resm(TPASS, "Receive SEND_FAILED for fragmented message with "
390 "timeout");
391
392 /* Shut down the link. */
393 close(sk1);
394
395 /* Get the shutdown complete notification. */
396 inmessage.msg_controllen = sizeof(incmsg);
397 error = test_recvmsg(sk2, &inmessage, MSG_WAITALL);
398 test_check_msg_notification(&inmessage, error,
399 sizeof(struct sctp_assoc_change),
400 SCTP_ASSOC_CHANGE, SCTP_SHUTDOWN_COMP);
401
402 close(sk2);
403
404 /* Indicate successful completion. */
405 return 0;
406 }
407