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 72char *TCID = __FILE__; 73int TST_TOTAL = 6; 74int 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 100 81 82/* RWND_SLOP is the extra data that fills up the rwnd */ 83#define RWND_SLOP 100 84static char *fillmsg = NULL; 85static char *ttlmsg = "This should time out!\n"; 86static char *nottlmsg = "This should NOT time out!\n"; 87static char ttlfrag[SMALL_MAXSEG*3] = {0}; 88static char *message = "Hello world\n"; 89 90int 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