| /* SCTP kernel Implementation |
| * (C) Copyright IBM Corp. 2001, 2003 |
| * Copyright (C) 1999 Cisco |
| * Copyright (C) 1999-2000 Motorola |
| # Copyright (C) 2001 Nokia |
| * Copyright (C) 2001 La Monte H.P. Yarroll |
| * |
| * The SCTP implementation is free software; |
| * you can redistribute it and/or modify it under the terms of |
| * the GNU General Public License as published by |
| * the Free Software Foundation; either version 2, or (at your option) |
| * any later version. |
| * |
| * The SCTP implementation is distributed in the hope that it |
| * will be useful, but WITHOUT ANY WARRANTY; without even the implied |
| * ************************ |
| * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| * See the GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with GNU CC; see the file COPYING. If not, write to |
| * the Free Software Foundation, 59 Temple Place - Suite 330, |
| * Boston, MA 02111-1307, USA. |
| * |
| * Please send any bug reports or fixes you make to the |
| * email address(es): |
| * lksctp developers <lksctp-developers@lists.sourceforge.net> |
| * |
| * Or submit a bug report through the following website: |
| * http://www.sf.net/projects/lksctp |
| * |
| * Any bugs reported to us we will try to fix... any fixes shared will |
| * be incorporated into the next SCTP release. |
| * |
| * Written or modified by: |
| * La Monte H.P. Yarroll <piggy@acm.org> |
| * Narasimha Budihal <narsi@refcode.org> |
| * Karl Knutson <karl@athena.chicago.il.us> |
| * Jon Grimm <jgrimm@us.ibm.com> |
| * Daisy Chang <daisyc@us.ibm.com> |
| * Sridhar Samudrala <sri@us.ibm.com> |
| */ |
| |
| #include <stdio.h> |
| #include <ctype.h> |
| #include <string.h> |
| #include <sys/types.h> |
| #include <sys/socket.h> |
| #include <sys/uio.h> |
| #include <netinet/in.h> |
| #include <errno.h> |
| #include <malloc.h> |
| #include <netinet/sctp.h> |
| #include <sctputil.h> |
| |
| /* This function prints the cmsg data. */ |
| void |
| test_print_cmsg(sctp_cmsg_t type, sctp_cmsg_data_t *data) |
| { |
| switch(type) { |
| case SCTP_INIT: |
| printf("INIT\n"); |
| printf(" sinit_num_ostreams %d\n", |
| data->init.sinit_num_ostreams); |
| printf(" sinit_max_instreams %d\n", |
| data->init.sinit_max_instreams); |
| printf(" sinit_max_attempts %d\n", |
| data->init.sinit_max_attempts); |
| printf(" sinit_max_init_timeo %d\n", |
| data->init.sinit_max_init_timeo); |
| |
| break; |
| case SCTP_SNDRCV: |
| printf("SNDRCV\n"); |
| printf(" sinfo_stream %u\n", data->sndrcv.sinfo_stream); |
| printf(" sinfo_ssn %u\n", data->sndrcv.sinfo_ssn); |
| printf(" sinfo_flags 0x%x\n", data->sndrcv.sinfo_flags); |
| printf(" sinfo_ppid %u\n", data->sndrcv.sinfo_ppid); |
| printf(" sinfo_context %x\n", data->sndrcv.sinfo_context); |
| printf(" sinfo_tsn %u\n", data->sndrcv.sinfo_tsn); |
| printf(" sinfo_cumtsn %u\n", data->sndrcv.sinfo_cumtsn); |
| printf(" sinfo_assoc_id %u\n", data->sndrcv.sinfo_assoc_id); |
| |
| break; |
| |
| default: |
| printf("UNKNOWN CMSG: %d\n", type); |
| break; |
| } |
| } |
| |
| /* This function prints the message. */ |
| void |
| test_print_message(int sk, struct msghdr *msg, size_t msg_len) |
| { |
| sctp_cmsg_data_t *data; |
| struct cmsghdr *cmsg; |
| int i; |
| int done = 0; |
| char save; |
| union sctp_notification *sn; |
| |
| for (cmsg = CMSG_FIRSTHDR(msg); |
| cmsg != NULL; |
| cmsg = CMSG_NXTHDR(msg, cmsg)) { |
| data = (sctp_cmsg_data_t *)CMSG_DATA(cmsg); |
| test_print_cmsg(cmsg->cmsg_type, data); |
| } |
| |
| if (!(MSG_NOTIFICATION & msg->msg_flags)) { |
| int index = 0; |
| /* Make sure that everything is printable and that we |
| * are NUL terminated... |
| */ |
| printf("DATA(%lu): ", msg_len); |
| while ( msg_len > 0 ) { |
| char *text; |
| int len; |
| |
| text = msg->msg_iov[index].iov_base; |
| len = msg->msg_iov[index].iov_len; |
| |
| save = text[msg_len-1]; |
| if ( len > msg_len ) { |
| text[(len = msg_len) - 1] = '\0'; |
| } |
| |
| if ( (msg_len -= len) > 0 ) { index++; } |
| |
| for (i = 0; i < len - 1; ++i) { |
| if (!isprint(text[i])) text[i] = '.'; |
| } |
| |
| printf("%s", text); |
| text[msg_len-1] = save; |
| |
| if ( (done = !strcmp(text, "exit")) ) { break; } |
| } |
| } else { |
| printf("NOTIFICATION: "); |
| sn = (union sctp_notification *)msg->msg_iov[0].iov_base; |
| switch (sn->sn_header.sn_type) { |
| case SCTP_ASSOC_CHANGE: |
| switch (sn->sn_assoc_change.sac_state) { |
| case SCTP_COMM_UP: |
| printf("ASSOC_CHANGE - COMM_UP"); |
| break; |
| case SCTP_COMM_LOST: |
| printf("ASSOC_CHANGE - COMM_LOST"); |
| break; |
| case SCTP_RESTART: |
| printf("ASSOC_CHANGE - RESTART"); |
| break; |
| case SCTP_SHUTDOWN_COMP: |
| printf("ASSOC_CHANGE - SHUTDOWN_COMP"); |
| break; |
| case SCTP_CANT_STR_ASSOC: |
| printf("ASSOC_CHANGE - CANT_STR_ASSOC"); |
| break; |
| default: |
| printf("ASSOC_CHANGE - UNEXPECTED(%d)", |
| sn->sn_assoc_change.sac_state); |
| break; |
| } |
| break; |
| default: |
| printf("%d", sn->sn_header.sn_type); |
| break; |
| } |
| } |
| |
| printf("\n"); |
| } |
| |
| /* Check if a buf/msg_flags matches a notification, its type, and possibly an |
| * additional field in the corresponding notification structure. |
| */ |
| void |
| test_check_buf_notification(void *buf, int datalen, int msg_flags, |
| int expected_datalen, uint16_t expected_sn_type, |
| uint32_t expected_additional) |
| { |
| union sctp_notification *sn; |
| |
| if (!(msg_flags & MSG_NOTIFICATION)) |
| tst_brkm(TBROK, tst_exit, "Got a datamsg, expecting " |
| "notification"); |
| |
| if (expected_datalen <= 0) |
| return; |
| |
| if (datalen != expected_datalen) |
| tst_brkm(TBROK, tst_exit, "Got a notification of unexpected " |
| "length:%d, expected length:%d", datalen, |
| expected_datalen); |
| |
| sn = (union sctp_notification *)buf; |
| if (sn->sn_header.sn_type != expected_sn_type) |
| tst_brkm(TBROK, tst_exit, "Unexpected notification:%d" |
| "expected:%d", sn->sn_header.sn_type, |
| expected_sn_type); |
| |
| switch(sn->sn_header.sn_type){ |
| case SCTP_ASSOC_CHANGE: |
| if (sn->sn_assoc_change.sac_state != expected_additional) |
| tst_brkm(TBROK, tst_exit, "Unexpected sac_state:%d " |
| "expected:%d", sn->sn_assoc_change.sac_state, |
| expected_additional); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| /* Check if a message matches a notification, its type, and possibly an |
| * additional field in the corresponding notification structure. |
| */ |
| void |
| test_check_msg_notification(struct msghdr *msg, int datalen, |
| int expected_datalen, uint16_t expected_sn_type, |
| uint32_t expected_additional) |
| { |
| test_check_buf_notification(msg->msg_iov[0].iov_base, datalen, |
| msg->msg_flags, expected_datalen, |
| expected_sn_type, expected_additional); |
| } |
| |
| /* Check if a buf/msg_flags/sinfo corresponds to data, its length, msg_flags, |
| * stream and ppid. |
| */ |
| void |
| test_check_buf_data(void *buf, int datalen, int msg_flags, |
| struct sctp_sndrcvinfo *sinfo, int expected_datalen, |
| int expected_msg_flags, uint16_t expected_stream, |
| uint32_t expected_ppid) |
| { |
| if (msg_flags & MSG_NOTIFICATION) |
| tst_brkm(TBROK, tst_exit, "Got a notification, expecting a" |
| "datamsg"); |
| |
| if (expected_datalen <= 0) |
| return; |
| |
| if (datalen != expected_datalen) |
| tst_brkm(TBROK, tst_exit, "Got a datamsg of unexpected " |
| "length:%d, expected length:%d", datalen, |
| expected_datalen); |
| |
| if ((msg_flags & ~0x80000000) != expected_msg_flags) |
| tst_brkm(TBROK, tst_exit, "Unexpected msg_flags:0x%x " |
| "expecting:0x%x", msg_flags, expected_msg_flags); |
| |
| if ((0 == expected_stream) && (0 == expected_ppid)) |
| return; |
| |
| if (!sinfo) |
| tst_brkm(TBROK, tst_exit, "Null sinfo, but expected " |
| "stream:%d expected ppid:%d", expected_stream, |
| expected_ppid); |
| |
| if (sinfo->sinfo_stream != expected_stream) |
| tst_brkm(TBROK, tst_exit, "stream mismatch: expected:%x " |
| "got:%x", expected_stream, sinfo->sinfo_stream); |
| if (sinfo->sinfo_ppid != expected_ppid) |
| tst_brkm(TBROK, tst_exit, "ppid mismatch: expected:%x " |
| "got:%x\n", expected_ppid, sinfo->sinfo_ppid); |
| } |
| |
| /* Check if a message corresponds to data, its length, msg_flags, stream and |
| * ppid. |
| */ |
| void |
| test_check_msg_data(struct msghdr *msg, int datalen, int expected_datalen, |
| int expected_msg_flags, uint16_t expected_stream, |
| uint32_t expected_ppid) |
| { |
| struct cmsghdr *cmsg = NULL; |
| struct sctp_sndrcvinfo *sinfo = NULL; |
| |
| /* Receive auxiliary data in msgh. */ |
| for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL; |
| cmsg = CMSG_NXTHDR(msg, cmsg)){ |
| if (IPPROTO_SCTP == cmsg->cmsg_level && |
| SCTP_SNDRCV == cmsg->cmsg_type) |
| break; |
| } /* for( all cmsgs) */ |
| |
| if ((!cmsg) || |
| (cmsg->cmsg_len < CMSG_LEN(sizeof(struct sctp_sndrcvinfo)))) |
| sinfo = NULL; |
| else |
| sinfo = (struct sctp_sndrcvinfo *)CMSG_DATA(cmsg); |
| |
| test_check_buf_data(msg->msg_iov[0].iov_base, datalen, msg->msg_flags, |
| sinfo, expected_datalen, expected_msg_flags, |
| expected_stream, expected_ppid); |
| |
| } |
| |
| |
| /* Allocate a buffer of requested len and fill in with data. */ |
| void * |
| test_build_msg(int len) |
| { |
| int i = len - 1; |
| int n; |
| unsigned char msg[] = |
| "012345678901234567890123456789012345678901234567890"; |
| char *msg_buf, *p; |
| |
| msg_buf = (char *)malloc(len); |
| if (!msg_buf) |
| tst_brkm(TBROK, tst_exit, "malloc failed"); |
| |
| p = msg_buf; |
| |
| do { |
| n = ((i > 50)?50:i); |
| memcpy(p, msg, ((i > 50)?50:i)); |
| p += n; |
| i -= n; |
| } while (i > 0); |
| |
| msg_buf[len-1] = '\0'; |
| |
| return(msg_buf); |
| } |
| |
| /* Enable ASSOC_CHANGE and SNDRCVINFO notifications. */ |
| void test_enable_assoc_change(int fd) |
| { |
| struct sctp_event_subscribe subscribe; |
| |
| memset(&subscribe, 0, sizeof(subscribe)); |
| subscribe.sctp_data_io_event = 1; |
| subscribe.sctp_association_event = 1; |
| test_setsockopt(fd, SCTP_EVENTS, (char *)&subscribe, |
| sizeof(subscribe)); |
| } |
| |
| static int cmp_addr(sockaddr_storage_t *addr1, sockaddr_storage_t *addr2) |
| { |
| if (addr1->sa.sa_family != addr2->sa.sa_family) |
| return 0; |
| switch (addr1->sa.sa_family) { |
| case AF_INET6: |
| if (addr1->v6.sin6_port != addr2->v6.sin6_port) |
| return -1; |
| return memcmp(&addr1->v6.sin6_addr, &addr2->v6.sin6_addr, |
| sizeof(addr1->v6.sin6_addr)); |
| case AF_INET: |
| if (addr1->v4.sin_port != addr2->v4.sin_port) |
| return 0; |
| return memcmp(&addr1->v4.sin_addr, &addr2->v4.sin_addr, |
| sizeof(addr1->v4.sin_addr)); |
| default: |
| tst_brkm(TBROK, tst_exit, "invalid address type %d", |
| addr1->sa.sa_family); |
| return -1; |
| } |
| } |
| |
| /* Test peer addresses for association. */ |
| int test_peer_addr(int sk, sctp_assoc_t asoc, sockaddr_storage_t *peers, int count) |
| { |
| struct sockaddr *addrs; |
| int error, i, j; |
| struct sockaddr *sa_addr; |
| socklen_t addrs_size = 0; |
| void *addrbuf; |
| char *found = (char *) malloc(count); |
| memset(found, 0, count); |
| |
| error = sctp_getpaddrs(sk, asoc, &addrs); |
| if (-1 == error) { |
| tst_brkm(TBROK, tst_exit, "sctp_getpaddrs: %s", strerror(errno)); |
| return error; |
| } |
| if (error != count) { |
| sctp_freepaddrs(addrs); |
| tst_brkm(TBROK, tst_exit, "peer count %d mismatch, expected %d", |
| error, count); |
| } |
| addrbuf = addrs; |
| for (i = 0; i < count; i++) { |
| sa_addr = (struct sockaddr *)addrbuf; |
| switch (sa_addr->sa_family) { |
| case AF_INET: |
| addrs_size += sizeof(struct sockaddr_in); |
| addrbuf += sizeof(struct sockaddr_in); |
| break; |
| case AF_INET6: |
| addrs_size += sizeof(struct sockaddr_in6); |
| addrbuf += sizeof(struct sockaddr_in6); |
| break; |
| default: |
| errno = EINVAL; |
| sctp_freepaddrs(addrs); |
| tst_brkm(TBROK, tst_exit, "sctp_getpaddrs: %s", strerror(errno)); |
| return -1; |
| } |
| for (j = 0; j < count; j++) { |
| if (cmp_addr((sockaddr_storage_t *)sa_addr, |
| &peers[j]) == 0) { |
| found[j] = 1; |
| } |
| } |
| } |
| for (j = 0; j < count; j++) { |
| if (found[j] == 0) { |
| tst_brkm(TBROK, tst_exit, "peer address %d not found", j); |
| } |
| } |
| sctp_freepaddrs(addrs); |
| free(found); |
| return 0; |
| } |