Date: Wed, 15 Jun 2011 23:50:27 +0000 (UTC) From: Michael Tuexen <tuexen@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r223132 - in head: lib/libc/net sys/netinet Message-ID: <201106152350.p5FNoR4f003652@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: tuexen Date: Wed Jun 15 23:50:27 2011 New Revision: 223132 URL: http://svn.freebsd.org/changeset/base/223132 Log: Add support for the newly added SCTP API. In particular add support for: * SCTP_SNDINFO, SCTP_PRINFO, SCTP_AUTHINFO, SCTP_DSTADDRV4, and SCTP_DSTADDRV6 cmsgs. * SCTP_NXTINFO and SCTP_RCVINFO cmgs. * SCTP_EVENT, SCTP_RECVRCVINFO, SCTP_RECVNXTINFO and SCTP_DEFAULT_SNDINFO socket option. * Special association ids (SCTP_FUTURE_ASSOC, ...) * sctp_recvv() and sctp_sendv() functions. MFC after: 1 month. Modified: head/lib/libc/net/sctp_sys_calls.c head/sys/netinet/sctp.h head/sys/netinet/sctp_auth.c head/sys/netinet/sctp_indata.c head/sys/netinet/sctp_output.c head/sys/netinet/sctp_pcb.c head/sys/netinet/sctp_structs.h head/sys/netinet/sctp_uio.h head/sys/netinet/sctp_usrreq.c head/sys/netinet/sctp_var.h head/sys/netinet/sctputil.c Modified: head/lib/libc/net/sctp_sys_calls.c ============================================================================== --- head/lib/libc/net/sctp_sys_calls.c Wed Jun 15 23:45:35 2011 (r223131) +++ head/lib/libc/net/sctp_sys_calls.c Wed Jun 15 23:50:27 2011 (r223132) @@ -141,7 +141,7 @@ in6_sin6_2_sin(struct sockaddr_in *sin, int sctp_getaddrlen(sa_family_t family) { - int error, sd; + int ret, sd; socklen_t siz; struct sctp_assoc_value av; @@ -151,13 +151,15 @@ sctp_getaddrlen(sa_family_t family) sd = socket(AF_INET, SOCK_SEQPACKET, IPPROTO_SCTP); #elif defined(AF_INET6) sd = socket(AF_INET6, SOCK_SEQPACKET, IPPROTO_SCTP); +#else + sd = -1; #endif if (sd == -1) { return (-1); } - error = getsockopt(sd, IPPROTO_SCTP, SCTP_GET_ADDR_LEN, &av, &siz); + ret = getsockopt(sd, IPPROTO_SCTP, SCTP_GET_ADDR_LEN, &av, &siz); close(sd); - if (error == 0) { + if (ret == 0) { return ((int)av.assoc_value); } else { return (-1); @@ -402,6 +404,9 @@ sctp_opt_info(int sd, sctp_assoc_t id, i case SCTP_TIMEOUTS: ((struct sctp_timeouts *)arg)->stimo_assoc_id = id; break; + case SCTP_EVENT: + ((struct sctp_event *)arg)->se_assoc_id = id; + break; default: break; } @@ -919,32 +924,259 @@ sctp_recvmsg(int s, #endif } +ssize_t +sctp_recvv(int sd, + const struct iovec *iov, + int iovlen, + struct sockaddr *from, + socklen_t * fromlen, + void *info, + socklen_t * infolen, + unsigned int *infotype, + int *flags) +{ + char ctlbuf[SCTP_CONTROL_VEC_SIZE_RCV]; + struct msghdr msg; + struct cmsghdr *cmsg; + ssize_t n; + struct sctp_rcvinfo *rcvinfo; + struct sctp_nxtinfo *nxtinfo; -#if defined(HAVE_SCTP_PEELOFF_SOCKOPT) -#include <netinet/sctp_peeloff.h> + if (infotype) { + *infotype = SCTP_RECVV_NOINFO; + } + msg.msg_name = from; + if (fromlen == NULL) { + msg.msg_namelen = 0; + } else { + msg.msg_namelen = *fromlen; + } + msg.msg_iov = (struct iovec *)iov; + msg.msg_iovlen = iovlen; + msg.msg_control = ctlbuf; + msg.msg_controllen = sizeof(ctlbuf); + errno = 0; + n = recvmsg(sd, &msg, *flags); + *flags = msg.msg_flags; + if ((n > 0) && + (msg.msg_controllen > 0) && + (infotype != NULL) && + (infolen != NULL) && + (*infolen > 0)) { + rcvinfo = NULL; + nxtinfo = NULL; + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { + if (cmsg->cmsg_level != IPPROTO_SCTP) { + continue; + } + if (cmsg->cmsg_type == SCTP_RCVINFO) { + rcvinfo = (struct sctp_rcvinfo *)CMSG_DATA(cmsg); + } + if (cmsg->cmsg_type == SCTP_NXTINFO) { + nxtinfo = (struct sctp_nxtinfo *)CMSG_DATA(cmsg); + } + if (rcvinfo && nxtinfo) { + break; + } + } + if (rcvinfo) { + if (nxtinfo) { + if (*infolen >= sizeof(struct sctp_recvv_rn)) { + struct sctp_recvv_rn *rn_info; + + rn_info = (struct sctp_recvv_rn *)info; + rn_info->recvv_rcvinfo = *rcvinfo; + rn_info->recvv_nxtinfo = *nxtinfo; + *infolen = (socklen_t) sizeof(struct sctp_recvv_rn); + *infotype = SCTP_RECVV_RN; + } + } else { + if (*infolen >= sizeof(struct sctp_rcvinfo)) { + memcpy(info, rcvinfo, sizeof(struct sctp_rcvinfo)); + *infolen = (socklen_t) sizeof(struct sctp_rcvinfo); + *infotype = SCTP_RECVV_RCVINFO; + } + } + } else if (nxtinfo) { + if (*infolen >= sizeof(struct sctp_rcvinfo)) { + memcpy(info, nxtinfo, sizeof(struct sctp_nxtinfo)); + *infolen = (socklen_t) sizeof(struct sctp_nxtinfo); + *infotype = SCTP_RECVV_NXTINFO; + } + } + } + return (n); +} -int -sctp_peeloff(int sd, sctp_assoc_t assoc_id) +ssize_t +sctp_sendv(int sd, + const struct iovec *iov, int iovcnt, + struct sockaddr *addrs, int addrcnt, + void *info, socklen_t infolen, unsigned int infotype, + int flags) { - struct sctp_peeloff_opt peeloff; - int result; - socklen_t optlen; - - /* set in the socket option params */ - memset(&peeloff, 0, sizeof(peeloff)); - peeloff.s = sd; - peeloff.assoc_id = assoc_id; - optlen = sizeof(peeloff); - result = getsockopt(sd, IPPROTO_SCTP, SCTP_PEELOFF, (void *)&peeloff, &optlen); + ssize_t ret; + int i; + size_t addr_len; + struct sctp_sendv_spa *spa_info; + struct msghdr msg; + struct cmsghdr *cmsg; + char *cmsgbuf; + struct sockaddr *addr; + struct sockaddr_in *addr_in; + struct sockaddr_in6 *addr_in6; - if (result < 0) { + if ((addrcnt < 0) || (iovcnt < 0)) { + errno = EINVAL; + return (-1); + } + cmsgbuf = malloc(CMSG_SPACE(sizeof(struct sctp_sndinfo)) + + CMSG_SPACE(sizeof(struct sctp_prinfo)) + + CMSG_SPACE(sizeof(struct sctp_authinfo)) + + addrcnt * CMSG_SPACE(sizeof(struct in6_addr))); + if (cmsgbuf == NULL) { + errno = ENOBUFS; return (-1); + } + msg.msg_control = cmsgbuf; + msg.msg_controllen = 0; + cmsg = (struct cmsghdr *)cmsgbuf; + switch (infotype) { + case SCTP_SENDV_SNDINFO: + if (infolen < sizeof(struct sctp_sndinfo)) { + free(cmsgbuf); + errno = EINVAL; + return (-1); + } + cmsg->cmsg_level = IPPROTO_SCTP; + cmsg->cmsg_type = SCTP_SNDINFO; + cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_sndinfo)); + memcpy(CMSG_DATA(cmsg), info, sizeof(struct sctp_sndinfo)); + msg.msg_controllen += CMSG_SPACE(sizeof(struct sctp_sndinfo)); + cmsg = (struct cmsghdr *)((caddr_t)cmsg + CMSG_SPACE(sizeof(struct sctp_sndinfo))); + break; + case SCTP_SENDV_PRINFO: + if (infolen < sizeof(struct sctp_prinfo)) { + free(cmsgbuf); + errno = EINVAL; + return (-1); + } + cmsg->cmsg_level = IPPROTO_SCTP; + cmsg->cmsg_type = SCTP_PRINFO; + cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_prinfo)); + memcpy(CMSG_DATA(cmsg), info, sizeof(struct sctp_prinfo)); + msg.msg_controllen += CMSG_SPACE(sizeof(struct sctp_prinfo)); + cmsg = (struct cmsghdr *)((caddr_t)cmsg + CMSG_SPACE(sizeof(struct sctp_prinfo))); + break; + case SCTP_SENDV_AUTHINFO: + if (infolen < sizeof(struct sctp_authinfo)) { + free(cmsgbuf); + errno = EINVAL; + return (-1); + } + cmsg->cmsg_level = IPPROTO_SCTP; + cmsg->cmsg_type = SCTP_AUTHINFO; + cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_authinfo)); + memcpy(CMSG_DATA(cmsg), info, sizeof(struct sctp_authinfo)); + msg.msg_controllen += CMSG_SPACE(sizeof(struct sctp_authinfo)); + cmsg = (struct cmsghdr *)((caddr_t)cmsg + CMSG_SPACE(sizeof(struct sctp_authinfo))); + break; + case SCTP_SENDV_SPA: + if (infolen < sizeof(struct sctp_sendv_spa)) { + free(cmsgbuf); + errno = EINVAL; + return (-1); + } + spa_info = (struct sctp_sendv_spa *)info; + if (spa_info->sendv_flags & SCTP_SEND_SNDINFO_VALID) { + cmsg->cmsg_level = IPPROTO_SCTP; + cmsg->cmsg_type = SCTP_SNDINFO; + cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_sndinfo)); + memcpy(CMSG_DATA(cmsg), &spa_info->sendv_sndinfo, sizeof(struct sctp_sndinfo)); + msg.msg_controllen += CMSG_SPACE(sizeof(struct sctp_sndinfo)); + cmsg = (struct cmsghdr *)((caddr_t)cmsg + CMSG_SPACE(sizeof(struct sctp_sndinfo))); + } + if (spa_info->sendv_flags & SCTP_SEND_PRINFO_VALID) { + cmsg->cmsg_level = IPPROTO_SCTP; + cmsg->cmsg_type = SCTP_PRINFO; + cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_prinfo)); + memcpy(CMSG_DATA(cmsg), &spa_info->sendv_prinfo, sizeof(struct sctp_prinfo)); + msg.msg_controllen += CMSG_SPACE(sizeof(struct sctp_prinfo)); + cmsg = (struct cmsghdr *)((caddr_t)cmsg + CMSG_SPACE(sizeof(struct sctp_prinfo))); + } + if (spa_info->sendv_flags & SCTP_SEND_AUTHINFO_VALID) { + cmsg->cmsg_level = IPPROTO_SCTP; + cmsg->cmsg_type = SCTP_AUTHINFO; + cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_authinfo)); + memcpy(CMSG_DATA(cmsg), &spa_info->sendv_authinfo, sizeof(struct sctp_authinfo)); + msg.msg_controllen += CMSG_SPACE(sizeof(struct sctp_authinfo)); + cmsg = (struct cmsghdr *)((caddr_t)cmsg + CMSG_SPACE(sizeof(struct sctp_authinfo))); + } + break; + default: + free(cmsgbuf); + errno = EINVAL; + return (-1); + } + addr = addrs; + if (addrcnt == 1) { + msg.msg_name = addr; + switch (addr->sa_family) { + case AF_INET: + msg.msg_namelen = sizeof(struct sockaddr_in); + break; + case AF_INET6: + msg.msg_namelen = sizeof(struct sockaddr_in6); + break; + default: + free(cmsgbuf); + errno = EINVAL; + return (-1); + } } else { - return (peeloff.new_sd); + msg.msg_name = NULL; + msg.msg_namelen = 0; + for (i = 0; i < addrcnt; i++) { + switch (addr->sa_family) { + case AF_INET: + addr_len = sizeof(struct sockaddr_in); + addr_in = (struct sockaddr_in *)addr; + cmsg->cmsg_level = IPPROTO_SCTP; + cmsg->cmsg_type = SCTP_DSTADDRV4; + cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_addr)); + memcpy(CMSG_DATA(cmsg), &addr_in->sin_addr, sizeof(struct in_addr)); + msg.msg_controllen += CMSG_SPACE(sizeof(struct in_addr)); + cmsg = (struct cmsghdr *)((caddr_t)cmsg + CMSG_SPACE(sizeof(struct in_addr))); + break; + case AF_INET6: + addr_len = sizeof(struct sockaddr_in6); + addr_in6 = (struct sockaddr_in6 *)addr; + cmsg->cmsg_level = IPPROTO_SCTP; + cmsg->cmsg_type = SCTP_DSTADDRV6; + cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_addr)); + memcpy(CMSG_DATA(cmsg), &addr_in6->sin6_addr, sizeof(struct in6_addr)); + msg.msg_controllen += CMSG_SPACE(sizeof(struct in6_addr)); + cmsg = (struct cmsghdr *)((caddr_t)cmsg + CMSG_SPACE(sizeof(struct in6_addr))); + break; + default: + free(cmsgbuf); + errno = EINVAL; + return (-1); + } + addr = (struct sockaddr *)((caddr_t)addr + addr_len); + } } + if (msg.msg_controllen == 0) { + msg.msg_control = NULL; + } + msg.msg_iov = (struct iovec *)iov; + msg.msg_iovlen = iovcnt; + msg.msg_flags = 0; + ret = sendmsg(sd, &msg, flags); + free(cmsgbuf); + return (ret); } -#endif #if !defined(SYS_sctp_peeloff) && !defined(HAVE_SCTP_PEELOFF_SOCKOPT) Modified: head/sys/netinet/sctp.h ============================================================================== --- head/sys/netinet/sctp.h Wed Jun 15 23:45:35 2011 (r223131) +++ head/sys/netinet/sctp.h Wed Jun 15 23:50:27 2011 (r223132) @@ -91,7 +91,7 @@ struct sctp_paramhdr { #define SCTP_PEER_ADDR_PARAMS 0x0000000a #define SCTP_DEFAULT_SEND_PARAM 0x0000000b /* ancillary data/notification interest options */ -#define SCTP_EVENTS 0x0000000c +#define SCTP_EVENTS 0x0000000c /* deprecated */ /* Without this applied we will give V4 and V6 addresses on a V6 socket */ #define SCTP_I_WANT_MAPPED_V4_ADDR 0x0000000d #define SCTP_MAXSEG 0x0000000e @@ -114,6 +114,10 @@ struct sctp_paramhdr { #define SCTP_EXPLICIT_EOR 0x0000001b #define SCTP_REUSE_PORT 0x0000001c /* rw */ #define SCTP_AUTH_DEACTIVATE_KEY 0x0000001d +#define SCTP_EVENT 0x0000001e +#define SCTP_RECVRCVINFO 0x0000001f +#define SCTP_RECVNXTINFO 0x00000020 +#define SCTP_DEFAULT_SNDINFO 0x00000021 /* * read-only options @@ -490,7 +494,7 @@ struct sctp_error_unrecognized_chunk { /* * PCB Features (in sctp_features bitmask) */ -#define SCTP_PCB_FLAGS_EXT_RCVINFO 0x00000002 +#define SCTP_PCB_FLAGS_EXT_RCVINFO 0x00000002 /* deprecated */ #define SCTP_PCB_FLAGS_DONOT_HEARTBEAT 0x00000004 #define SCTP_PCB_FLAGS_FRAG_INTERLEAVE 0x00000008 #define SCTP_PCB_FLAGS_INTERLEAVE_STRMS 0x00000010 @@ -500,7 +504,7 @@ struct sctp_error_unrecognized_chunk { /* socket options */ #define SCTP_PCB_FLAGS_NODELAY 0x00000100 #define SCTP_PCB_FLAGS_AUTOCLOSE 0x00000200 -#define SCTP_PCB_FLAGS_RECVDATAIOEVNT 0x00000400 +#define SCTP_PCB_FLAGS_RECVDATAIOEVNT 0x00000400 /* deprecated */ #define SCTP_PCB_FLAGS_RECVASSOCEVNT 0x00000800 #define SCTP_PCB_FLAGS_RECVPADDREVNT 0x00001000 #define SCTP_PCB_FLAGS_RECVPEERERR 0x00002000 @@ -516,6 +520,9 @@ struct sctp_error_unrecognized_chunk { #define SCTP_PCB_FLAGS_MULTIPLE_ASCONFS 0x01000000 #define SCTP_PCB_FLAGS_PORTREUSE 0x02000000 #define SCTP_PCB_FLAGS_DRYEVNT 0x04000000 +#define SCTP_PCB_FLAGS_RECVRCVINFO 0x08000000 +#define SCTP_PCB_FLAGS_RECVNXTINFO 0x10000000 + /*- * mobility_features parameters (by micchie).Note * these features are applied against the Modified: head/sys/netinet/sctp_auth.c ============================================================================== --- head/sys/netinet/sctp_auth.c Wed Jun 15 23:45:35 2011 (r223131) +++ head/sys/netinet/sctp_auth.c Wed Jun 15 23:50:27 2011 (r223132) @@ -1866,7 +1866,7 @@ sctp_notify_authentication(struct sctp_t /* If the socket is gone we are out of here */ return; } - if (sctp_is_feature_off(stcb->sctp_ep, SCTP_PCB_FLAGS_AUTHEVNT)) + if (sctp_stcb_is_feature_off(stcb->sctp_ep, stcb, SCTP_PCB_FLAGS_AUTHEVNT)) /* event not enabled */ return; Modified: head/sys/netinet/sctp_indata.c ============================================================================== --- head/sys/netinet/sctp_indata.c Wed Jun 15 23:45:35 2011 (r223131) +++ head/sys/netinet/sctp_indata.c Wed Jun 15 23:50:27 2011 (r223132) @@ -201,48 +201,110 @@ failed_build: struct mbuf * -sctp_build_ctl_nchunk(struct sctp_inpcb *inp, - struct sctp_sndrcvinfo *sinfo) +sctp_build_ctl_nchunk(struct sctp_inpcb *inp, struct sctp_sndrcvinfo *sinfo) { + struct sctp_extrcvinfo *seinfo; struct sctp_sndrcvinfo *outinfo; + struct sctp_rcvinfo *rcvinfo; + struct sctp_nxtinfo *nxtinfo; struct cmsghdr *cmh; struct mbuf *ret; int len; - int use_extended = 0; + int use_extended; + int provide_nxt; - if (sctp_is_feature_off(inp, SCTP_PCB_FLAGS_RECVDATAIOEVNT)) { - /* user does not want the sndrcv ctl */ + if (sctp_is_feature_off(inp, SCTP_PCB_FLAGS_RECVDATAIOEVNT) && + sctp_is_feature_off(inp, SCTP_PCB_FLAGS_RECVRCVINFO) && + sctp_is_feature_off(inp, SCTP_PCB_FLAGS_RECVNXTINFO)) { + /* user does not want any ancillary data */ return (NULL); } - if (sctp_is_feature_on(inp, SCTP_PCB_FLAGS_EXT_RCVINFO)) { - use_extended = 1; - len = CMSG_LEN(sizeof(struct sctp_extrcvinfo)); + len = 0; + if (sctp_is_feature_on(inp, SCTP_PCB_FLAGS_RECVRCVINFO)) { + len += CMSG_SPACE(sizeof(struct sctp_rcvinfo)); + } + seinfo = (struct sctp_extrcvinfo *)sinfo; + if (sctp_is_feature_on(inp, SCTP_PCB_FLAGS_RECVNXTINFO) && + (seinfo->sreinfo_next_flags & SCTP_NEXT_MSG_AVAIL)) { + provide_nxt = 1; + len += CMSG_SPACE(sizeof(struct sctp_rcvinfo)); } else { - len = CMSG_LEN(sizeof(struct sctp_sndrcvinfo)); + provide_nxt = 0; + } + if (sctp_is_feature_on(inp, SCTP_PCB_FLAGS_RECVDATAIOEVNT)) { + if (sctp_is_feature_on(inp, SCTP_PCB_FLAGS_EXT_RCVINFO)) { + use_extended = 1; + len += CMSG_SPACE(sizeof(struct sctp_extrcvinfo)); + } else { + use_extended = 0; + len += CMSG_SPACE(sizeof(struct sctp_sndrcvinfo)); + } + } else { + use_extended = 0; } - - ret = sctp_get_mbuf_for_msg(len, - 0, M_DONTWAIT, 1, MT_DATA); - + ret = sctp_get_mbuf_for_msg(len, 0, M_DONTWAIT, 1, MT_DATA); if (ret == NULL) { /* No space */ return (ret); } - /* We need a CMSG header followed by the struct */ + SCTP_BUF_LEN(ret) = 0; + + /* We need a CMSG header followed by the struct */ cmh = mtod(ret, struct cmsghdr *); - outinfo = (struct sctp_sndrcvinfo *)CMSG_DATA(cmh); - cmh->cmsg_level = IPPROTO_SCTP; - if (use_extended) { - cmh->cmsg_type = SCTP_EXTRCV; - cmh->cmsg_len = len; - memcpy(outinfo, sinfo, len); - } else { - cmh->cmsg_type = SCTP_SNDRCV; - cmh->cmsg_len = len; - *outinfo = *sinfo; + if (sctp_is_feature_on(inp, SCTP_PCB_FLAGS_RECVRCVINFO)) { + cmh->cmsg_level = IPPROTO_SCTP; + cmh->cmsg_len = CMSG_LEN(sizeof(struct sctp_rcvinfo)); + cmh->cmsg_type = SCTP_RCVINFO; + rcvinfo = (struct sctp_rcvinfo *)CMSG_DATA(cmh); + rcvinfo->rcv_sid = sinfo->sinfo_stream; + rcvinfo->rcv_ssn = sinfo->sinfo_ssn; + rcvinfo->rcv_flags = sinfo->sinfo_flags; + rcvinfo->rcv_ppid = sinfo->sinfo_ppid; + rcvinfo->rcv_tsn = sinfo->sinfo_tsn; + rcvinfo->rcv_cumtsn = sinfo->sinfo_cumtsn; + rcvinfo->rcv_context = sinfo->sinfo_context; + rcvinfo->rcv_assoc_id = sinfo->sinfo_assoc_id; + cmh = (struct cmsghdr *)((caddr_t)cmh + CMSG_SPACE(sizeof(struct sctp_rcvinfo))); + SCTP_BUF_LEN(ret) += CMSG_SPACE(sizeof(struct sctp_rcvinfo)); + } + if (provide_nxt) { + cmh->cmsg_level = IPPROTO_SCTP; + cmh->cmsg_len = CMSG_LEN(sizeof(struct sctp_nxtinfo)); + cmh->cmsg_type = SCTP_NXTINFO; + nxtinfo = (struct sctp_nxtinfo *)CMSG_DATA(cmh); + nxtinfo->nxt_sid = seinfo->sreinfo_next_stream; + nxtinfo->nxt_flags = 0; + if (seinfo->sreinfo_next_flags & SCTP_NEXT_MSG_IS_UNORDERED) { + nxtinfo->nxt_flags |= SCTP_UNORDERED; + } + if (seinfo->sreinfo_next_flags & SCTP_NEXT_MSG_IS_NOTIFICATION) { + nxtinfo->nxt_flags |= SCTP_NOTIFICATION; + } + if (seinfo->sreinfo_next_flags & SCTP_NEXT_MSG_ISCOMPLETE) { + nxtinfo->nxt_flags |= SCTP_COMPLETE; + } + nxtinfo->nxt_ppid = seinfo->sreinfo_next_ppid; + nxtinfo->nxt_length = seinfo->sreinfo_next_length; + nxtinfo->nxt_assoc_id = seinfo->sreinfo_next_aid; + cmh = (struct cmsghdr *)((caddr_t)cmh + CMSG_SPACE(sizeof(struct sctp_nxtinfo))); + SCTP_BUF_LEN(ret) += CMSG_SPACE(sizeof(struct sctp_nxtinfo)); + } + if (sctp_is_feature_on(inp, SCTP_PCB_FLAGS_RECVDATAIOEVNT)) { + cmh->cmsg_level = IPPROTO_SCTP; + outinfo = (struct sctp_sndrcvinfo *)CMSG_DATA(cmh); + if (use_extended) { + cmh->cmsg_len = CMSG_LEN(sizeof(struct sctp_extrcvinfo)); + cmh->cmsg_type = SCTP_EXTRCV; + memcpy(outinfo, sinfo, sizeof(struct sctp_extrcvinfo)); + SCTP_BUF_LEN(ret) += CMSG_SPACE(sizeof(struct sctp_extrcvinfo)); + } else { + cmh->cmsg_len = CMSG_LEN(sizeof(struct sctp_sndrcvinfo)); + cmh->cmsg_type = SCTP_SNDRCV; + *outinfo = *sinfo; + SCTP_BUF_LEN(ret) += CMSG_SPACE(sizeof(struct sctp_sndrcvinfo)); + } } - SCTP_BUF_LEN(ret) = cmh->cmsg_len; return (ret); } Modified: head/sys/netinet/sctp_output.c ============================================================================== --- head/sys/netinet/sctp_output.c Wed Jun 15 23:45:35 2011 (r223131) +++ head/sys/netinet/sctp_output.c Wed Jun 15 23:50:27 2011 (r223132) @@ -3355,54 +3355,338 @@ sctp_source_address_selection(struct sct } static int -sctp_find_cmsg(int c_type, void *data, struct mbuf *control, int cpsize) +sctp_find_cmsg(int c_type, void *data, struct mbuf *control, size_t cpsize) { struct cmsghdr cmh; - int tlen, at; + int tlen, at, found; + struct sctp_sndinfo sndinfo; + struct sctp_prinfo prinfo; + struct sctp_authinfo authinfo; tlen = SCTP_BUF_LEN(control); at = 0; + found = 0; /* * Independent of how many mbufs, find the c_type inside the control * structure and copy out the data. */ while (at < tlen) { if ((tlen - at) < (int)CMSG_ALIGN(sizeof(cmh))) { - /* not enough room for one more we are done. */ - return (0); + /* There is not enough room for one more. */ + return (found); } m_copydata(control, at, sizeof(cmh), (caddr_t)&cmh); + if (cmh.cmsg_len < CMSG_ALIGN(sizeof(struct cmsghdr))) { + /* We dont't have a complete CMSG header. */ + return (found); + } if (((int)cmh.cmsg_len + at) > tlen) { - /* - * this is real messed up since there is not enough - * data here to cover the cmsg header. We are done. - */ - return (0); + /* We don't have the complete CMSG. */ + return (found); } if ((cmh.cmsg_level == IPPROTO_SCTP) && - (c_type == cmh.cmsg_type)) { - /* found the one we want, copy it out */ - at += CMSG_ALIGN(sizeof(struct cmsghdr)); - if ((int)(cmh.cmsg_len - CMSG_ALIGN(sizeof(struct cmsghdr))) < cpsize) { - /* - * space of cmsg_len after header not big - * enough - */ - return (0); + ((c_type == cmh.cmsg_type) || + ((c_type == SCTP_SNDRCV) && + ((cmh.cmsg_type == SCTP_SNDINFO) || + (cmh.cmsg_type == SCTP_PRINFO) || + (cmh.cmsg_type == SCTP_AUTHINFO))))) { + if (c_type == cmh.cmsg_type) { + if ((size_t)(cmh.cmsg_len - CMSG_ALIGN(sizeof(struct cmsghdr))) < cpsize) { + return (found); + } + /* It is exactly what we want. Copy it out. */ + m_copydata(control, at + CMSG_ALIGN(sizeof(struct cmsghdr)), cpsize, (caddr_t)data); + return (1); + } else { + struct sctp_sndrcvinfo *sndrcvinfo; + + sndrcvinfo = (struct sctp_sndrcvinfo *)data; + if (found == 0) { + if (cpsize < sizeof(struct sctp_sndrcvinfo)) { + return (found); + } + memset(sndrcvinfo, 0, sizeof(struct sctp_sndrcvinfo)); + } + switch (cmh.cmsg_type) { + case SCTP_SNDINFO: + if ((size_t)(cmh.cmsg_len - CMSG_ALIGN(sizeof(struct cmsghdr))) < sizeof(struct sctp_sndinfo)) { + return (found); + } + m_copydata(control, at + CMSG_ALIGN(sizeof(struct cmsghdr)), sizeof(struct sctp_sndinfo), (caddr_t)&sndinfo); + sndrcvinfo->sinfo_stream = sndinfo.snd_sid; + sndrcvinfo->sinfo_flags = sndinfo.snd_flags; + sndrcvinfo->sinfo_ppid = sndinfo.snd_ppid; + sndrcvinfo->sinfo_context = sndinfo.snd_context; + sndrcvinfo->sinfo_assoc_id = sndinfo.snd_assoc_id; + break; + case SCTP_PRINFO: + if ((size_t)(cmh.cmsg_len - CMSG_ALIGN(sizeof(struct cmsghdr))) < sizeof(struct sctp_prinfo)) { + return (found); + } + m_copydata(control, at + CMSG_ALIGN(sizeof(struct cmsghdr)), sizeof(struct sctp_prinfo), (caddr_t)&prinfo); + sndrcvinfo->sinfo_timetolive = prinfo.pr_value; + sndrcvinfo->sinfo_flags |= prinfo.pr_policy; + break; + case SCTP_AUTHINFO: + if ((size_t)(cmh.cmsg_len - CMSG_ALIGN(sizeof(struct cmsghdr))) < sizeof(struct sctp_authinfo)) { + return (found); + } + m_copydata(control, at + CMSG_ALIGN(sizeof(struct cmsghdr)), sizeof(struct sctp_authinfo), (caddr_t)&authinfo); + sndrcvinfo->sinfo_keynumber_valid = 1; + sndrcvinfo->sinfo_keynumber = authinfo.auth_keyid; + break; + default: + return (found); + } + found = 1; } - m_copydata(control, at, cpsize, data); + } + at += CMSG_ALIGN(cmh.cmsg_len); + } + return (found); +} + +static int +sctp_process_cmsgs_for_init(struct sctp_tcb *stcb, struct mbuf *control, int *error) +{ + struct cmsghdr cmh; + int tlen, at; + struct sctp_initmsg initmsg; + +#ifdef INET + struct sockaddr_in sin; + +#endif +#ifdef INET6 + struct sockaddr_in6 sin6; + +#endif + + tlen = SCTP_BUF_LEN(control); + at = 0; + while (at < tlen) { + if ((tlen - at) < (int)CMSG_ALIGN(sizeof(cmh))) { + /* There is not enough room for one more. */ + *error = EINVAL; return (1); - } else { - at += CMSG_ALIGN(cmh.cmsg_len); - if (cmh.cmsg_len == 0) { + } + m_copydata(control, at, sizeof(cmh), (caddr_t)&cmh); + if (cmh.cmsg_len < CMSG_ALIGN(sizeof(struct cmsghdr))) { + /* We dont't have a complete CMSG header. */ + *error = EINVAL; + return (1); + } + if (((int)cmh.cmsg_len + at) > tlen) { + /* We don't have the complete CMSG. */ + *error = EINVAL; + return (1); + } + if (cmh.cmsg_level == IPPROTO_SCTP) { + switch (cmh.cmsg_type) { + case SCTP_INIT: + if ((size_t)(cmh.cmsg_len - CMSG_ALIGN(sizeof(struct cmsghdr))) < sizeof(struct sctp_initmsg)) { + *error = EINVAL; + return (1); + } + m_copydata(control, at + CMSG_ALIGN(sizeof(struct cmsghdr)), sizeof(struct sctp_initmsg), (caddr_t)&initmsg); + if (initmsg.sinit_max_attempts) + stcb->asoc.max_init_times = initmsg.sinit_max_attempts; + if (initmsg.sinit_num_ostreams) + stcb->asoc.pre_open_streams = initmsg.sinit_num_ostreams; + if (initmsg.sinit_max_instreams) + stcb->asoc.max_inbound_streams = initmsg.sinit_max_instreams; + if (initmsg.sinit_max_init_timeo) + stcb->asoc.initial_init_rto_max = initmsg.sinit_max_init_timeo; + if (stcb->asoc.streamoutcnt < stcb->asoc.pre_open_streams) { + struct sctp_stream_out *tmp_str; + unsigned int i; + + /* Default is NOT correct */ + SCTPDBG(SCTP_DEBUG_OUTPUT1, "Ok, default:%d pre_open:%d\n", + stcb->asoc.streamoutcnt, stcb->asoc.pre_open_streams); + SCTP_TCB_UNLOCK(stcb); + SCTP_MALLOC(tmp_str, + struct sctp_stream_out *, + (stcb->asoc.pre_open_streams * sizeof(struct sctp_stream_out)), + SCTP_M_STRMO); + SCTP_TCB_LOCK(stcb); + if (tmp_str != NULL) { + SCTP_FREE(stcb->asoc.strmout, SCTP_M_STRMO); + stcb->asoc.strmout = tmp_str; + stcb->asoc.strm_realoutsize = stcb->asoc.streamoutcnt = stcb->asoc.pre_open_streams; + } else { + stcb->asoc.pre_open_streams = stcb->asoc.streamoutcnt; + } + for (i = 0; i < stcb->asoc.streamoutcnt; i++) { + stcb->asoc.strmout[i].next_sequence_sent = 0; + TAILQ_INIT(&stcb->asoc.strmout[i].outqueue); + stcb->asoc.strmout[i].stream_no = i; + stcb->asoc.strmout[i].last_msg_incomplete = 0; + stcb->asoc.ss_functions.sctp_ss_init_stream(&stcb->asoc.strmout[i], NULL); + } + } + break; +#ifdef INET + case SCTP_DSTADDRV4: + if ((size_t)(cmh.cmsg_len - CMSG_ALIGN(sizeof(struct cmsghdr))) < sizeof(struct in_addr)) { + *error = EINVAL; + return (1); + } + memset(&sin, 0, sizeof(struct sockaddr_in)); + sin.sin_family = AF_INET; + sin.sin_len = sizeof(struct sockaddr_in); + sin.sin_port = stcb->rport; + m_copydata(control, at + CMSG_ALIGN(sizeof(struct cmsghdr)), sizeof(struct in_addr), (caddr_t)&sin.sin_addr); + if ((sin.sin_addr.s_addr == INADDR_ANY) || + (sin.sin_addr.s_addr == INADDR_BROADCAST) || + IN_MULTICAST(ntohl(sin.sin_addr.s_addr))) { + *error = EINVAL; + return (-1); + } + if (sctp_add_remote_addr(stcb, (struct sockaddr *)&sin, SCTP_DONOT_SETSCOPE, SCTP_ADDR_IS_CONFIRMED)) { + *error = ENOBUFS; + return (1); + } + break; +#endif +#ifdef INET6 + case SCTP_DSTADDRV6: + if ((size_t)(cmh.cmsg_len - CMSG_ALIGN(sizeof(struct cmsghdr))) < sizeof(struct in6_addr)) { + *error = EINVAL; + return (1); + } + memset(&sin6, 0, sizeof(struct sockaddr_in6)); + sin6.sin6_family = AF_INET6; + sin6.sin6_len = sizeof(struct sockaddr_in6); + sin6.sin6_port = stcb->rport; + m_copydata(control, at + CMSG_ALIGN(sizeof(struct cmsghdr)), sizeof(struct in6_addr), (caddr_t)&sin6.sin6_addr); + if (IN6_IS_ADDR_UNSPECIFIED(&sin6.sin6_addr) || + IN6_IS_ADDR_MULTICAST(&sin6.sin6_addr)) { + *error = EINVAL; + return (-1); + } +#ifdef INET + if (IN6_IS_ADDR_V4MAPPED(&sin6.sin6_addr)) { + in6_sin6_2_sin(&sin, &sin6); + if ((sin.sin_addr.s_addr == INADDR_ANY) || + (sin.sin_addr.s_addr == INADDR_BROADCAST) || + IN_MULTICAST(ntohl(sin.sin_addr.s_addr))) { + *error = EINVAL; + return (-1); + } + if (sctp_add_remote_addr(stcb, (struct sockaddr *)&sin, SCTP_DONOT_SETSCOPE, SCTP_ADDR_IS_CONFIRMED)) { + *error = ENOBUFS; + return (1); + } + } else +#endif + if (sctp_add_remote_addr(stcb, (struct sockaddr *)&sin6, SCTP_DONOT_SETSCOPE, SCTP_ADDR_IS_CONFIRMED)) { + *error = ENOBUFS; + return (1); + } + break; +#endif + default: break; } } + at += CMSG_ALIGN(cmh.cmsg_len); } - /* not found */ return (0); } +static struct sctp_tcb * +sctp_findassociation_cmsgs(struct sctp_inpcb **inp_p, + in_port_t port, + struct mbuf *control, + struct sctp_nets **net_p, + int *error) +{ + struct cmsghdr cmh; + int tlen, at; + struct sctp_tcb *stcb; + struct sockaddr *addr; + +#ifdef INET + struct sockaddr_in sin; + +#endif +#ifdef INET6 + struct sockaddr_in6 sin6; + +#endif + + tlen = SCTP_BUF_LEN(control); + at = 0; + while (at < tlen) { + if ((tlen - at) < (int)CMSG_ALIGN(sizeof(cmh))) { + /* There is not enough room for one more. */ + *error = EINVAL; + return (NULL); + } + m_copydata(control, at, sizeof(cmh), (caddr_t)&cmh); + if (cmh.cmsg_len < CMSG_ALIGN(sizeof(struct cmsghdr))) { + /* We dont't have a complete CMSG header. */ + *error = EINVAL; + return (NULL); + } + if (((int)cmh.cmsg_len + at) > tlen) { + /* We don't have the complete CMSG. */ + *error = EINVAL; + return (NULL); + } + if (cmh.cmsg_level == IPPROTO_SCTP) { + switch (cmh.cmsg_type) { +#ifdef INET + case SCTP_DSTADDRV4: + if ((size_t)(cmh.cmsg_len - CMSG_ALIGN(sizeof(struct cmsghdr))) < sizeof(struct in_addr)) { + *error = EINVAL; + return (NULL); + } + memset(&sin, 0, sizeof(struct sockaddr_in)); + sin.sin_family = AF_INET; + sin.sin_len = sizeof(struct sockaddr_in); + sin.sin_port = port; + m_copydata(control, at + CMSG_ALIGN(sizeof(struct cmsghdr)), sizeof(struct in_addr), (caddr_t)&sin.sin_addr); + addr = (struct sockaddr *)&sin; + break; +#endif +#ifdef INET6 + case SCTP_DSTADDRV6: + if ((size_t)(cmh.cmsg_len - CMSG_ALIGN(sizeof(struct cmsghdr))) < sizeof(struct in6_addr)) { + *error = EINVAL; + return (NULL); + } + memset(&sin6, 0, sizeof(struct sockaddr_in6)); + sin6.sin6_family = AF_INET6; + sin6.sin6_len = sizeof(struct sockaddr_in6); + sin6.sin6_port = port; + m_copydata(control, at + CMSG_ALIGN(sizeof(struct cmsghdr)), sizeof(struct in6_addr), (caddr_t)&sin6.sin6_addr); +#ifdef INET + if (IN6_IS_ADDR_V4MAPPED(&sin6.sin6_addr)) { + in6_sin6_2_sin(&sin, &sin6); + addr = (struct sockaddr *)&sin; + } else +#endif + addr = (struct sockaddr *)&sin6; + break; +#endif + default: + addr = NULL; + break; + } + if (addr) { + stcb = sctp_findassociation_ep_addr(inp_p, addr, net_p, NULL, NULL); + if (stcb != NULL) { + return (stcb); + } + } + } + at += CMSG_ALIGN(cmh.cmsg_len); + } + return (NULL); +} + static struct mbuf * sctp_add_cookie(struct sctp_inpcb *inp, struct mbuf *init, int init_offset, struct mbuf *initack, int initack_offset, struct sctp_state_cookie *stc_in, uint8_t ** signature) @@ -5989,19 +6273,26 @@ sctp_msg_append(struct sctp_tcb *stcb, sp->some_taken = 0; sp->data = m; sp->tail_mbuf = NULL; - sp->length = 0; - at = m; sctp_set_prsctp_policy(sp); /* * We could in theory (for sendall) sifa the length in, but we would * still have to hunt through the chain since we need to setup the * tail_mbuf */ - while (at) { + sp->length = 0; + for (at = m; at; at = SCTP_BUF_NEXT(at)) { if (SCTP_BUF_NEXT(at) == NULL) sp->tail_mbuf = at; sp->length += SCTP_BUF_LEN(at); - at = SCTP_BUF_NEXT(at); + } + if (srcv->sinfo_keynumber_valid) { + sp->auth_keyid = srcv->sinfo_keynumber; + } else { + sp->auth_keyid = stcb->asoc.authinfo.active_keyid; + } + if (sctp_auth_is_required_chunk(SCTP_DATA, stcb->asoc.peer_auth_chunks)) { + sctp_auth_key_acquire(stcb, sp->auth_keyid); + sp->holds_key_ref = 1; } SCTP_TCB_SEND_LOCK(stcb); sctp_snd_sb_alloc(stcb, sp->length); @@ -6478,7 +6769,9 @@ sctp_sendall(struct sctp_inpcb *inp, str memset(ca, 0, sizeof(struct sctp_copy_all)); ca->inp = inp; - memcpy(&ca->sndrcv, srcv, sizeof(struct sctp_nonpad_sndrcvinfo)); + if (srcv) { + memcpy(&ca->sndrcv, srcv, sizeof(struct sctp_nonpad_sndrcvinfo)); + } /* * take off the sendall flag, it would be bad if we failed to do * this :-0 @@ -12229,9 +12522,13 @@ sctp_copy_it_in(struct sctp_tcb *stcb, *error = 0; goto skip_copy; } - sp->auth_keyid = stcb->asoc.authinfo.active_keyid; + if (srcv->sinfo_keynumber_valid) { + sp->auth_keyid = srcv->sinfo_keynumber; + } else { + sp->auth_keyid = stcb->asoc.authinfo.active_keyid; + } if (sctp_auth_is_required_chunk(SCTP_DATA, stcb->asoc.peer_auth_chunks)) { - sctp_auth_key_acquire(stcb, stcb->asoc.authinfo.active_keyid); + sctp_auth_key_acquire(stcb, sp->auth_keyid); sp->holds_key_ref = 1; } *error = sctp_copy_one(sp, uio, resv_in_first); @@ -12263,8 +12560,8 @@ sctp_sosend(struct socket *so, struct thread *p ) { - int error, use_rcvinfo = 0; - struct sctp_sndrcvinfo srcv; + int error, use_sndinfo = 0; + struct sctp_sndrcvinfo sndrcvninfo; struct sockaddr *addr_to_use; #if defined(INET) && defined(INET6) @@ -12274,10 +12571,10 @@ sctp_sosend(struct socket *so, if (control) { /* process cmsg snd/rcv info (maybe a assoc-id) */ - if (sctp_find_cmsg(SCTP_SNDRCV, (void *)&srcv, control, - sizeof(srcv))) { + if (sctp_find_cmsg(SCTP_SNDRCV, (void *)&sndrcvninfo, control, + sizeof(sndrcvninfo))) { /* got one */ - use_rcvinfo = 1; + use_sndinfo = 1; } } addr_to_use = addr; @@ -12295,7 +12592,7 @@ sctp_sosend(struct socket *so, error = sctp_lower_sosend(so, addr_to_use, uio, top, control, flags, - use_rcvinfo ? &srcv : NULL + use_sndinfo ? &sndrcvninfo : NULL ,p ); return (error); @@ -12500,6 +12797,9 @@ sctp_lower_sosend(struct socket *so, SCTP_INP_WUNLOCK(inp); /* With the lock applied look again */ stcb = sctp_findassociation_ep_addr(&t_inp, addr, &net, NULL, NULL); + if ((stcb == NULL) && (control != NULL) && (port > 0)) { + stcb = sctp_findassociation_cmsgs(&t_inp, port, control, &net, &error); + } if (stcb == NULL) { SCTP_INP_WLOCK(inp); SCTP_INP_DECR_REF(inp); @@ -12507,6 +12807,9 @@ sctp_lower_sosend(struct socket *so, } else { hold_tcblock = 1; } + if (error) { + goto out_unlocked; + } if (t_inp != inp) { SCTP_LTRACE_ERR_RET(inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, ENOTCONN); error = ENOTCONN; @@ -12555,6 +12858,7 @@ sctp_lower_sosend(struct socket *so, /* Error is setup for us in the call */ goto out_unlocked; } + hold_tcblock = 1; if (create_lock_applied) { SCTP_ASOC_CREATE_UNLOCK(inp); create_lock_applied = 0; @@ -12574,84 +12878,13 @@ sctp_lower_sosend(struct socket *so, *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201106152350.p5FNoR4f003652>