Index: head/sys/kern/uipc_syscalls.c =================================================================== --- head/sys/kern/uipc_syscalls.c (revision 197325) +++ head/sys/kern/uipc_syscalls.c (revision 197326) @@ -1,2724 +1,2733 @@ /*- * Copyright (c) 1982, 1986, 1989, 1990, 1993 * The Regents of the University of California. All rights reserved. * * sendfile(2) and related extensions: * Copyright (c) 1998, David Greenman. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)uipc_syscalls.c 8.4 (Berkeley) 2/21/94 */ #include __FBSDID("$FreeBSD$"); #include "opt_inet.h" #include "opt_inet6.h" #include "opt_sctp.h" #include "opt_compat.h" #include "opt_ktrace.h" #include #include #include #include #include #include #include #include #include #include #include #include #include +#include #include #include #include #include #include #include #include #include #include #include #include #ifdef KTRACE #include #endif #include #include #include #include #include #include #include #include #include #if defined(INET) || defined(INET6) #ifdef SCTP #include #include #endif /* SCTP */ #endif /* INET || INET6 */ static int sendit(struct thread *td, int s, struct msghdr *mp, int flags); static int recvit(struct thread *td, int s, struct msghdr *mp, void *namelenp); static int accept1(struct thread *td, struct accept_args *uap, int compat); static int do_sendfile(struct thread *td, struct sendfile_args *uap, int compat); static int getsockname1(struct thread *td, struct getsockname_args *uap, int compat); static int getpeername1(struct thread *td, struct getpeername_args *uap, int compat); /* * NSFBUFS-related variables and associated sysctls */ int nsfbufs; int nsfbufspeak; int nsfbufsused; SYSCTL_INT(_kern_ipc, OID_AUTO, nsfbufs, CTLFLAG_RDTUN, &nsfbufs, 0, "Maximum number of sendfile(2) sf_bufs available"); SYSCTL_INT(_kern_ipc, OID_AUTO, nsfbufspeak, CTLFLAG_RD, &nsfbufspeak, 0, "Number of sendfile(2) sf_bufs at peak usage"); SYSCTL_INT(_kern_ipc, OID_AUTO, nsfbufsused, CTLFLAG_RD, &nsfbufsused, 0, "Number of sendfile(2) sf_bufs in use"); /* * Convert a user file descriptor to a kernel file entry. A reference on the * file entry is held upon returning. This is lighter weight than * fgetsock(), which bumps the socket reference drops the file reference * count instead, as this approach avoids several additional mutex operations * associated with the additional reference count. If requested, return the * open file flags. */ static int getsock(struct filedesc *fdp, int fd, struct file **fpp, u_int *fflagp) { struct file *fp; int error; fp = NULL; if (fdp == NULL || (fp = fget_unlocked(fdp, fd)) == NULL) { error = EBADF; } else if (fp->f_type != DTYPE_SOCKET) { fdrop(fp, curthread); fp = NULL; error = ENOTSOCK; } else { if (fflagp != NULL) *fflagp = fp->f_flag; error = 0; } *fpp = fp; return (error); } /* * System call interface to the socket abstraction. */ #if defined(COMPAT_43) #define COMPAT_OLDSOCK #endif int socket(td, uap) struct thread *td; struct socket_args /* { int domain; int type; int protocol; } */ *uap; { struct filedesc *fdp; struct socket *so; struct file *fp; int fd, error; AUDIT_ARG_SOCKET(uap->domain, uap->type, uap->protocol); #ifdef MAC error = mac_socket_check_create(td->td_ucred, uap->domain, uap->type, uap->protocol); if (error) return (error); #endif fdp = td->td_proc->p_fd; error = falloc(td, &fp, &fd); if (error) return (error); /* An extra reference on `fp' has been held for us by falloc(). */ error = socreate(uap->domain, &so, uap->type, uap->protocol, td->td_ucred, td); if (error) { fdclose(fdp, fp, fd, td); } else { finit(fp, FREAD | FWRITE, DTYPE_SOCKET, so, &socketops); td->td_retval[0] = fd; } fdrop(fp, td); return (error); } /* ARGSUSED */ int bind(td, uap) struct thread *td; struct bind_args /* { int s; caddr_t name; int namelen; } */ *uap; { struct sockaddr *sa; int error; if ((error = getsockaddr(&sa, uap->name, uap->namelen)) != 0) return (error); error = kern_bind(td, uap->s, sa); free(sa, M_SONAME); return (error); } int kern_bind(td, fd, sa) struct thread *td; int fd; struct sockaddr *sa; { struct socket *so; struct file *fp; int error; AUDIT_ARG_FD(fd); error = getsock(td->td_proc->p_fd, fd, &fp, NULL); if (error) return (error); so = fp->f_data; #ifdef KTRACE if (KTRPOINT(td, KTR_STRUCT)) ktrsockaddr(sa); #endif #ifdef MAC error = mac_socket_check_bind(td->td_ucred, so, sa); if (error == 0) #endif error = sobind(so, sa, td); fdrop(fp, td); return (error); } /* ARGSUSED */ int listen(td, uap) struct thread *td; struct listen_args /* { int s; int backlog; } */ *uap; { struct socket *so; struct file *fp; int error; AUDIT_ARG_FD(uap->s); error = getsock(td->td_proc->p_fd, uap->s, &fp, NULL); if (error == 0) { so = fp->f_data; #ifdef MAC error = mac_socket_check_listen(td->td_ucred, so); if (error == 0) { #endif CURVNET_SET(so->so_vnet); error = solisten(so, uap->backlog, td); CURVNET_RESTORE(); #ifdef MAC } #endif fdrop(fp, td); } return(error); } /* * accept1() */ static int accept1(td, uap, compat) struct thread *td; struct accept_args /* { int s; struct sockaddr * __restrict name; socklen_t * __restrict anamelen; } */ *uap; int compat; { struct sockaddr *name; socklen_t namelen; struct file *fp; int error; if (uap->name == NULL) return (kern_accept(td, uap->s, NULL, NULL, NULL)); error = copyin(uap->anamelen, &namelen, sizeof (namelen)); if (error) return (error); error = kern_accept(td, uap->s, &name, &namelen, &fp); /* * return a namelen of zero for older code which might * ignore the return value from accept. */ if (error) { (void) copyout(&namelen, uap->anamelen, sizeof(*uap->anamelen)); return (error); } if (error == 0 && name != NULL) { #ifdef COMPAT_OLDSOCK if (compat) ((struct osockaddr *)name)->sa_family = name->sa_family; #endif error = copyout(name, uap->name, namelen); } if (error == 0) error = copyout(&namelen, uap->anamelen, sizeof(namelen)); if (error) fdclose(td->td_proc->p_fd, fp, td->td_retval[0], td); fdrop(fp, td); free(name, M_SONAME); return (error); } int kern_accept(struct thread *td, int s, struct sockaddr **name, socklen_t *namelen, struct file **fp) { struct filedesc *fdp; struct file *headfp, *nfp = NULL; struct sockaddr *sa = NULL; int error; struct socket *head, *so; int fd; u_int fflag; pid_t pgid; int tmp; if (name) { *name = NULL; if (*namelen < 0) return (EINVAL); } AUDIT_ARG_FD(s); fdp = td->td_proc->p_fd; error = getsock(fdp, s, &headfp, &fflag); if (error) return (error); head = headfp->f_data; if ((head->so_options & SO_ACCEPTCONN) == 0) { error = EINVAL; goto done; } #ifdef MAC error = mac_socket_check_accept(td->td_ucred, head); if (error != 0) goto done; #endif error = falloc(td, &nfp, &fd); if (error) goto done; ACCEPT_LOCK(); if ((head->so_state & SS_NBIO) && TAILQ_EMPTY(&head->so_comp)) { ACCEPT_UNLOCK(); error = EWOULDBLOCK; goto noconnection; } while (TAILQ_EMPTY(&head->so_comp) && head->so_error == 0) { if (head->so_rcv.sb_state & SBS_CANTRCVMORE) { head->so_error = ECONNABORTED; break; } error = msleep(&head->so_timeo, &accept_mtx, PSOCK | PCATCH, "accept", 0); if (error) { ACCEPT_UNLOCK(); goto noconnection; } } if (head->so_error) { error = head->so_error; head->so_error = 0; ACCEPT_UNLOCK(); goto noconnection; } so = TAILQ_FIRST(&head->so_comp); KASSERT(!(so->so_qstate & SQ_INCOMP), ("accept1: so SQ_INCOMP")); KASSERT(so->so_qstate & SQ_COMP, ("accept1: so not SQ_COMP")); /* * Before changing the flags on the socket, we have to bump the * reference count. Otherwise, if the protocol calls sofree(), * the socket will be released due to a zero refcount. */ SOCK_LOCK(so); /* soref() and so_state update */ soref(so); /* file descriptor reference */ TAILQ_REMOVE(&head->so_comp, so, so_list); head->so_qlen--; so->so_state |= (head->so_state & SS_NBIO); so->so_qstate &= ~SQ_COMP; so->so_head = NULL; SOCK_UNLOCK(so); ACCEPT_UNLOCK(); /* An extra reference on `nfp' has been held for us by falloc(). */ td->td_retval[0] = fd; /* connection has been removed from the listen queue */ KNOTE_UNLOCKED(&head->so_rcv.sb_sel.si_note, 0); pgid = fgetown(&head->so_sigio); if (pgid != 0) fsetown(pgid, &so->so_sigio); finit(nfp, fflag, DTYPE_SOCKET, so, &socketops); /* Sync socket nonblocking/async state with file flags */ tmp = fflag & FNONBLOCK; (void) fo_ioctl(nfp, FIONBIO, &tmp, td->td_ucred, td); tmp = fflag & FASYNC; (void) fo_ioctl(nfp, FIOASYNC, &tmp, td->td_ucred, td); sa = 0; CURVNET_SET(so->so_vnet); error = soaccept(so, &sa); CURVNET_RESTORE(); if (error) { /* * return a namelen of zero for older code which might * ignore the return value from accept. */ if (name) *namelen = 0; goto noconnection; } if (sa == NULL) { if (name) *namelen = 0; goto done; } if (name) { /* check sa_len before it is destroyed */ if (*namelen > sa->sa_len) *namelen = sa->sa_len; #ifdef KTRACE if (KTRPOINT(td, KTR_STRUCT)) ktrsockaddr(sa); #endif *name = sa; sa = NULL; } noconnection: if (sa) free(sa, M_SONAME); /* * close the new descriptor, assuming someone hasn't ripped it * out from under us. */ if (error) fdclose(fdp, nfp, fd, td); /* * Release explicitly held references before returning. We return * a reference on nfp to the caller on success if they request it. */ done: if (fp != NULL) { if (error == 0) { *fp = nfp; nfp = NULL; } else *fp = NULL; } if (nfp != NULL) fdrop(nfp, td); fdrop(headfp, td); return (error); } int accept(td, uap) struct thread *td; struct accept_args *uap; { return (accept1(td, uap, 0)); } #ifdef COMPAT_OLDSOCK int oaccept(td, uap) struct thread *td; struct accept_args *uap; { return (accept1(td, uap, 1)); } #endif /* COMPAT_OLDSOCK */ /* ARGSUSED */ int connect(td, uap) struct thread *td; struct connect_args /* { int s; caddr_t name; int namelen; } */ *uap; { struct sockaddr *sa; int error; error = getsockaddr(&sa, uap->name, uap->namelen); if (error) return (error); error = kern_connect(td, uap->s, sa); free(sa, M_SONAME); return (error); } int kern_connect(td, fd, sa) struct thread *td; int fd; struct sockaddr *sa; { struct socket *so; struct file *fp; int error; int interrupted = 0; AUDIT_ARG_FD(fd); error = getsock(td->td_proc->p_fd, fd, &fp, NULL); if (error) return (error); so = fp->f_data; if (so->so_state & SS_ISCONNECTING) { error = EALREADY; goto done1; } #ifdef KTRACE if (KTRPOINT(td, KTR_STRUCT)) ktrsockaddr(sa); #endif #ifdef MAC error = mac_socket_check_connect(td->td_ucred, so, sa); if (error) goto bad; #endif error = soconnect(so, sa, td); if (error) goto bad; if ((so->so_state & SS_NBIO) && (so->so_state & SS_ISCONNECTING)) { error = EINPROGRESS; goto done1; } SOCK_LOCK(so); while ((so->so_state & SS_ISCONNECTING) && so->so_error == 0) { error = msleep(&so->so_timeo, SOCK_MTX(so), PSOCK | PCATCH, "connec", 0); if (error) { if (error == EINTR || error == ERESTART) interrupted = 1; break; } } if (error == 0) { error = so->so_error; so->so_error = 0; } SOCK_UNLOCK(so); bad: if (!interrupted) so->so_state &= ~SS_ISCONNECTING; if (error == ERESTART) error = EINTR; done1: fdrop(fp, td); return (error); } int kern_socketpair(struct thread *td, int domain, int type, int protocol, int *rsv) { struct filedesc *fdp = td->td_proc->p_fd; struct file *fp1, *fp2; struct socket *so1, *so2; int fd, error; AUDIT_ARG_SOCKET(domain, type, protocol); #ifdef MAC /* We might want to have a separate check for socket pairs. */ error = mac_socket_check_create(td->td_ucred, domain, type, protocol); if (error) return (error); #endif error = socreate(domain, &so1, type, protocol, td->td_ucred, td); if (error) return (error); error = socreate(domain, &so2, type, protocol, td->td_ucred, td); if (error) goto free1; /* On success extra reference to `fp1' and 'fp2' is set by falloc. */ error = falloc(td, &fp1, &fd); if (error) goto free2; rsv[0] = fd; fp1->f_data = so1; /* so1 already has ref count */ error = falloc(td, &fp2, &fd); if (error) goto free3; fp2->f_data = so2; /* so2 already has ref count */ rsv[1] = fd; error = soconnect2(so1, so2); if (error) goto free4; if (type == SOCK_DGRAM) { /* * Datagram socket connection is asymmetric. */ error = soconnect2(so2, so1); if (error) goto free4; } finit(fp1, FREAD | FWRITE, DTYPE_SOCKET, fp1->f_data, &socketops); finit(fp2, FREAD | FWRITE, DTYPE_SOCKET, fp2->f_data, &socketops); fdrop(fp1, td); fdrop(fp2, td); return (0); free4: fdclose(fdp, fp2, rsv[1], td); fdrop(fp2, td); free3: fdclose(fdp, fp1, rsv[0], td); fdrop(fp1, td); free2: if (so2 != NULL) (void)soclose(so2); free1: if (so1 != NULL) (void)soclose(so1); return (error); } int socketpair(struct thread *td, struct socketpair_args *uap) { int error, sv[2]; error = kern_socketpair(td, uap->domain, uap->type, uap->protocol, sv); if (error) return (error); error = copyout(sv, uap->rsv, 2 * sizeof(int)); if (error) { (void)kern_close(td, sv[0]); (void)kern_close(td, sv[1]); } return (error); } static int sendit(td, s, mp, flags) struct thread *td; int s; struct msghdr *mp; int flags; { struct mbuf *control; struct sockaddr *to; int error; if (mp->msg_name != NULL) { error = getsockaddr(&to, mp->msg_name, mp->msg_namelen); if (error) { to = NULL; goto bad; } mp->msg_name = to; } else { to = NULL; } if (mp->msg_control) { if (mp->msg_controllen < sizeof(struct cmsghdr) #ifdef COMPAT_OLDSOCK && mp->msg_flags != MSG_COMPAT #endif ) { error = EINVAL; goto bad; } error = sockargs(&control, mp->msg_control, mp->msg_controllen, MT_CONTROL); if (error) goto bad; #ifdef COMPAT_OLDSOCK if (mp->msg_flags == MSG_COMPAT) { struct cmsghdr *cm; M_PREPEND(control, sizeof(*cm), M_WAIT); cm = mtod(control, struct cmsghdr *); cm->cmsg_len = control->m_len; cm->cmsg_level = SOL_SOCKET; cm->cmsg_type = SCM_RIGHTS; } #endif } else { control = NULL; } error = kern_sendit(td, s, mp, flags, control, UIO_USERSPACE); bad: if (to) free(to, M_SONAME); return (error); } int kern_sendit(td, s, mp, flags, control, segflg) struct thread *td; int s; struct msghdr *mp; int flags; struct mbuf *control; enum uio_seg segflg; { struct file *fp; struct uio auio; struct iovec *iov; struct socket *so; int i; int len, error; #ifdef KTRACE struct uio *ktruio = NULL; #endif AUDIT_ARG_FD(s); error = getsock(td->td_proc->p_fd, s, &fp, NULL); if (error) return (error); so = (struct socket *)fp->f_data; #ifdef MAC if (mp->msg_name != NULL) { error = mac_socket_check_connect(td->td_ucred, so, mp->msg_name); if (error) goto bad; } error = mac_socket_check_send(td->td_ucred, so); if (error) goto bad; #endif auio.uio_iov = mp->msg_iov; auio.uio_iovcnt = mp->msg_iovlen; auio.uio_segflg = segflg; auio.uio_rw = UIO_WRITE; auio.uio_td = td; auio.uio_offset = 0; /* XXX */ auio.uio_resid = 0; iov = mp->msg_iov; for (i = 0; i < mp->msg_iovlen; i++, iov++) { if ((auio.uio_resid += iov->iov_len) < 0) { error = EINVAL; goto bad; } } #ifdef KTRACE if (KTRPOINT(td, KTR_GENIO)) ktruio = cloneuio(&auio); #endif len = auio.uio_resid; error = sosend(so, mp->msg_name, &auio, 0, control, flags, td); if (error) { if (auio.uio_resid != len && (error == ERESTART || error == EINTR || error == EWOULDBLOCK)) error = 0; /* Generation of SIGPIPE can be controlled per socket */ if (error == EPIPE && !(so->so_options & SO_NOSIGPIPE) && !(flags & MSG_NOSIGNAL)) { PROC_LOCK(td->td_proc); psignal(td->td_proc, SIGPIPE); PROC_UNLOCK(td->td_proc); } } if (error == 0) td->td_retval[0] = len - auio.uio_resid; #ifdef KTRACE if (ktruio != NULL) { ktruio->uio_resid = td->td_retval[0]; ktrgenio(s, UIO_WRITE, ktruio, error); } #endif bad: fdrop(fp, td); return (error); } int sendto(td, uap) struct thread *td; struct sendto_args /* { int s; caddr_t buf; size_t len; int flags; caddr_t to; int tolen; } */ *uap; { struct msghdr msg; struct iovec aiov; int error; msg.msg_name = uap->to; msg.msg_namelen = uap->tolen; msg.msg_iov = &aiov; msg.msg_iovlen = 1; msg.msg_control = 0; #ifdef COMPAT_OLDSOCK msg.msg_flags = 0; #endif aiov.iov_base = uap->buf; aiov.iov_len = uap->len; error = sendit(td, uap->s, &msg, uap->flags); return (error); } #ifdef COMPAT_OLDSOCK int osend(td, uap) struct thread *td; struct osend_args /* { int s; caddr_t buf; int len; int flags; } */ *uap; { struct msghdr msg; struct iovec aiov; int error; msg.msg_name = 0; msg.msg_namelen = 0; msg.msg_iov = &aiov; msg.msg_iovlen = 1; aiov.iov_base = uap->buf; aiov.iov_len = uap->len; msg.msg_control = 0; msg.msg_flags = 0; error = sendit(td, uap->s, &msg, uap->flags); return (error); } int osendmsg(td, uap) struct thread *td; struct osendmsg_args /* { int s; caddr_t msg; int flags; } */ *uap; { struct msghdr msg; struct iovec *iov; int error; error = copyin(uap->msg, &msg, sizeof (struct omsghdr)); if (error) return (error); error = copyiniov(msg.msg_iov, msg.msg_iovlen, &iov, EMSGSIZE); if (error) return (error); msg.msg_iov = iov; msg.msg_flags = MSG_COMPAT; error = sendit(td, uap->s, &msg, uap->flags); free(iov, M_IOV); return (error); } #endif int sendmsg(td, uap) struct thread *td; struct sendmsg_args /* { int s; caddr_t msg; int flags; } */ *uap; { struct msghdr msg; struct iovec *iov; int error; error = copyin(uap->msg, &msg, sizeof (msg)); if (error) return (error); error = copyiniov(msg.msg_iov, msg.msg_iovlen, &iov, EMSGSIZE); if (error) return (error); msg.msg_iov = iov; #ifdef COMPAT_OLDSOCK msg.msg_flags = 0; #endif error = sendit(td, uap->s, &msg, uap->flags); free(iov, M_IOV); return (error); } int kern_recvit(td, s, mp, fromseg, controlp) struct thread *td; int s; struct msghdr *mp; enum uio_seg fromseg; struct mbuf **controlp; { struct uio auio; struct iovec *iov; int i; socklen_t len; int error; struct mbuf *m, *control = 0; caddr_t ctlbuf; struct file *fp; struct socket *so; struct sockaddr *fromsa = 0; #ifdef KTRACE struct uio *ktruio = NULL; #endif if(controlp != NULL) *controlp = 0; AUDIT_ARG_FD(s); error = getsock(td->td_proc->p_fd, s, &fp, NULL); if (error) return (error); so = fp->f_data; #ifdef MAC error = mac_socket_check_receive(td->td_ucred, so); if (error) { fdrop(fp, td); return (error); } #endif auio.uio_iov = mp->msg_iov; auio.uio_iovcnt = mp->msg_iovlen; auio.uio_segflg = UIO_USERSPACE; auio.uio_rw = UIO_READ; auio.uio_td = td; auio.uio_offset = 0; /* XXX */ auio.uio_resid = 0; iov = mp->msg_iov; for (i = 0; i < mp->msg_iovlen; i++, iov++) { if ((auio.uio_resid += iov->iov_len) < 0) { fdrop(fp, td); return (EINVAL); } } #ifdef KTRACE if (KTRPOINT(td, KTR_GENIO)) ktruio = cloneuio(&auio); #endif len = auio.uio_resid; CURVNET_SET(so->so_vnet); error = soreceive(so, &fromsa, &auio, (struct mbuf **)0, (mp->msg_control || controlp) ? &control : (struct mbuf **)0, &mp->msg_flags); CURVNET_RESTORE(); if (error) { if (auio.uio_resid != (int)len && (error == ERESTART || error == EINTR || error == EWOULDBLOCK)) error = 0; } #ifdef KTRACE if (ktruio != NULL) { ktruio->uio_resid = (int)len - auio.uio_resid; ktrgenio(s, UIO_READ, ktruio, error); } #endif if (error) goto out; td->td_retval[0] = (int)len - auio.uio_resid; if (mp->msg_name) { len = mp->msg_namelen; if (len <= 0 || fromsa == 0) len = 0; else { /* save sa_len before it is destroyed by MSG_COMPAT */ len = MIN(len, fromsa->sa_len); #ifdef COMPAT_OLDSOCK if (mp->msg_flags & MSG_COMPAT) ((struct osockaddr *)fromsa)->sa_family = fromsa->sa_family; #endif if (fromseg == UIO_USERSPACE) { error = copyout(fromsa, mp->msg_name, (unsigned)len); if (error) goto out; } else bcopy(fromsa, mp->msg_name, len); } mp->msg_namelen = len; } if (mp->msg_control && controlp == NULL) { #ifdef COMPAT_OLDSOCK /* * We assume that old recvmsg calls won't receive access * rights and other control info, esp. as control info * is always optional and those options didn't exist in 4.3. * If we receive rights, trim the cmsghdr; anything else * is tossed. */ if (control && mp->msg_flags & MSG_COMPAT) { if (mtod(control, struct cmsghdr *)->cmsg_level != SOL_SOCKET || mtod(control, struct cmsghdr *)->cmsg_type != SCM_RIGHTS) { mp->msg_controllen = 0; goto out; } control->m_len -= sizeof (struct cmsghdr); control->m_data += sizeof (struct cmsghdr); } #endif len = mp->msg_controllen; m = control; mp->msg_controllen = 0; ctlbuf = mp->msg_control; while (m && len > 0) { unsigned int tocopy; if (len >= m->m_len) tocopy = m->m_len; else { mp->msg_flags |= MSG_CTRUNC; tocopy = len; } if ((error = copyout(mtod(m, caddr_t), ctlbuf, tocopy)) != 0) goto out; ctlbuf += tocopy; len -= tocopy; m = m->m_next; } mp->msg_controllen = ctlbuf - (caddr_t)mp->msg_control; } out: fdrop(fp, td); #ifdef KTRACE if (fromsa && KTRPOINT(td, KTR_STRUCT)) ktrsockaddr(fromsa); #endif if (fromsa) free(fromsa, M_SONAME); if (error == 0 && controlp != NULL) *controlp = control; else if (control) m_freem(control); return (error); } static int recvit(td, s, mp, namelenp) struct thread *td; int s; struct msghdr *mp; void *namelenp; { int error; error = kern_recvit(td, s, mp, UIO_USERSPACE, NULL); if (error) return (error); if (namelenp) { error = copyout(&mp->msg_namelen, namelenp, sizeof (socklen_t)); #ifdef COMPAT_OLDSOCK if (mp->msg_flags & MSG_COMPAT) error = 0; /* old recvfrom didn't check */ #endif } return (error); } int recvfrom(td, uap) struct thread *td; struct recvfrom_args /* { int s; caddr_t buf; size_t len; int flags; struct sockaddr * __restrict from; socklen_t * __restrict fromlenaddr; } */ *uap; { struct msghdr msg; struct iovec aiov; int error; if (uap->fromlenaddr) { error = copyin(uap->fromlenaddr, &msg.msg_namelen, sizeof (msg.msg_namelen)); if (error) goto done2; } else { msg.msg_namelen = 0; } msg.msg_name = uap->from; msg.msg_iov = &aiov; msg.msg_iovlen = 1; aiov.iov_base = uap->buf; aiov.iov_len = uap->len; msg.msg_control = 0; msg.msg_flags = uap->flags; error = recvit(td, uap->s, &msg, uap->fromlenaddr); done2: return(error); } #ifdef COMPAT_OLDSOCK int orecvfrom(td, uap) struct thread *td; struct recvfrom_args *uap; { uap->flags |= MSG_COMPAT; return (recvfrom(td, uap)); } #endif #ifdef COMPAT_OLDSOCK int orecv(td, uap) struct thread *td; struct orecv_args /* { int s; caddr_t buf; int len; int flags; } */ *uap; { struct msghdr msg; struct iovec aiov; int error; msg.msg_name = 0; msg.msg_namelen = 0; msg.msg_iov = &aiov; msg.msg_iovlen = 1; aiov.iov_base = uap->buf; aiov.iov_len = uap->len; msg.msg_control = 0; msg.msg_flags = uap->flags; error = recvit(td, uap->s, &msg, NULL); return (error); } /* * Old recvmsg. This code takes advantage of the fact that the old msghdr * overlays the new one, missing only the flags, and with the (old) access * rights where the control fields are now. */ int orecvmsg(td, uap) struct thread *td; struct orecvmsg_args /* { int s; struct omsghdr *msg; int flags; } */ *uap; { struct msghdr msg; struct iovec *iov; int error; error = copyin(uap->msg, &msg, sizeof (struct omsghdr)); if (error) return (error); error = copyiniov(msg.msg_iov, msg.msg_iovlen, &iov, EMSGSIZE); if (error) return (error); msg.msg_flags = uap->flags | MSG_COMPAT; msg.msg_iov = iov; error = recvit(td, uap->s, &msg, &uap->msg->msg_namelen); if (msg.msg_controllen && error == 0) error = copyout(&msg.msg_controllen, &uap->msg->msg_accrightslen, sizeof (int)); free(iov, M_IOV); return (error); } #endif int recvmsg(td, uap) struct thread *td; struct recvmsg_args /* { int s; struct msghdr *msg; int flags; } */ *uap; { struct msghdr msg; struct iovec *uiov, *iov; int error; error = copyin(uap->msg, &msg, sizeof (msg)); if (error) return (error); error = copyiniov(msg.msg_iov, msg.msg_iovlen, &iov, EMSGSIZE); if (error) return (error); msg.msg_flags = uap->flags; #ifdef COMPAT_OLDSOCK msg.msg_flags &= ~MSG_COMPAT; #endif uiov = msg.msg_iov; msg.msg_iov = iov; error = recvit(td, uap->s, &msg, NULL); if (error == 0) { msg.msg_iov = uiov; error = copyout(&msg, uap->msg, sizeof(msg)); } free(iov, M_IOV); return (error); } /* ARGSUSED */ int shutdown(td, uap) struct thread *td; struct shutdown_args /* { int s; int how; } */ *uap; { struct socket *so; struct file *fp; int error; AUDIT_ARG_FD(uap->s); error = getsock(td->td_proc->p_fd, uap->s, &fp, NULL); if (error == 0) { so = fp->f_data; error = soshutdown(so, uap->how); fdrop(fp, td); } return (error); } /* ARGSUSED */ int setsockopt(td, uap) struct thread *td; struct setsockopt_args /* { int s; int level; int name; caddr_t val; int valsize; } */ *uap; { return (kern_setsockopt(td, uap->s, uap->level, uap->name, uap->val, UIO_USERSPACE, uap->valsize)); } int kern_setsockopt(td, s, level, name, val, valseg, valsize) struct thread *td; int s; int level; int name; void *val; enum uio_seg valseg; socklen_t valsize; { int error; struct socket *so; struct file *fp; struct sockopt sopt; if (val == NULL && valsize != 0) return (EFAULT); if ((int)valsize < 0) return (EINVAL); sopt.sopt_dir = SOPT_SET; sopt.sopt_level = level; sopt.sopt_name = name; sopt.sopt_val = val; sopt.sopt_valsize = valsize; switch (valseg) { case UIO_USERSPACE: sopt.sopt_td = td; break; case UIO_SYSSPACE: sopt.sopt_td = NULL; break; default: panic("kern_setsockopt called with bad valseg"); } AUDIT_ARG_FD(s); error = getsock(td->td_proc->p_fd, s, &fp, NULL); if (error == 0) { so = fp->f_data; CURVNET_SET(so->so_vnet); error = sosetopt(so, &sopt); CURVNET_RESTORE(); fdrop(fp, td); } return(error); } /* ARGSUSED */ int getsockopt(td, uap) struct thread *td; struct getsockopt_args /* { int s; int level; int name; void * __restrict val; socklen_t * __restrict avalsize; } */ *uap; { socklen_t valsize; int error; if (uap->val) { error = copyin(uap->avalsize, &valsize, sizeof (valsize)); if (error) return (error); } error = kern_getsockopt(td, uap->s, uap->level, uap->name, uap->val, UIO_USERSPACE, &valsize); if (error == 0) error = copyout(&valsize, uap->avalsize, sizeof (valsize)); return (error); } /* * Kernel version of getsockopt. * optval can be a userland or userspace. optlen is always a kernel pointer. */ int kern_getsockopt(td, s, level, name, val, valseg, valsize) struct thread *td; int s; int level; int name; void *val; enum uio_seg valseg; socklen_t *valsize; { int error; struct socket *so; struct file *fp; struct sockopt sopt; if (val == NULL) *valsize = 0; if ((int)*valsize < 0) return (EINVAL); sopt.sopt_dir = SOPT_GET; sopt.sopt_level = level; sopt.sopt_name = name; sopt.sopt_val = val; sopt.sopt_valsize = (size_t)*valsize; /* checked non-negative above */ switch (valseg) { case UIO_USERSPACE: sopt.sopt_td = td; break; case UIO_SYSSPACE: sopt.sopt_td = NULL; break; default: panic("kern_getsockopt called with bad valseg"); } AUDIT_ARG_FD(s); error = getsock(td->td_proc->p_fd, s, &fp, NULL); if (error == 0) { so = fp->f_data; CURVNET_SET(so->so_vnet); error = sogetopt(so, &sopt); CURVNET_RESTORE(); *valsize = sopt.sopt_valsize; fdrop(fp, td); } return (error); } /* * getsockname1() - Get socket name. */ /* ARGSUSED */ static int getsockname1(td, uap, compat) struct thread *td; struct getsockname_args /* { int fdes; struct sockaddr * __restrict asa; socklen_t * __restrict alen; } */ *uap; int compat; { struct sockaddr *sa; socklen_t len; int error; error = copyin(uap->alen, &len, sizeof(len)); if (error) return (error); error = kern_getsockname(td, uap->fdes, &sa, &len); if (error) return (error); if (len != 0) { #ifdef COMPAT_OLDSOCK if (compat) ((struct osockaddr *)sa)->sa_family = sa->sa_family; #endif error = copyout(sa, uap->asa, (u_int)len); } free(sa, M_SONAME); if (error == 0) error = copyout(&len, uap->alen, sizeof(len)); return (error); } int kern_getsockname(struct thread *td, int fd, struct sockaddr **sa, socklen_t *alen) { struct socket *so; struct file *fp; socklen_t len; int error; if (*alen < 0) return (EINVAL); AUDIT_ARG_FD(fd); error = getsock(td->td_proc->p_fd, fd, &fp, NULL); if (error) return (error); so = fp->f_data; *sa = NULL; CURVNET_SET(so->so_vnet); error = (*so->so_proto->pr_usrreqs->pru_sockaddr)(so, sa); CURVNET_RESTORE(); if (error) goto bad; if (*sa == NULL) len = 0; else len = MIN(*alen, (*sa)->sa_len); *alen = len; #ifdef KTRACE if (KTRPOINT(td, KTR_STRUCT)) ktrsockaddr(*sa); #endif bad: fdrop(fp, td); if (error && *sa) { free(*sa, M_SONAME); *sa = NULL; } return (error); } int getsockname(td, uap) struct thread *td; struct getsockname_args *uap; { return (getsockname1(td, uap, 0)); } #ifdef COMPAT_OLDSOCK int ogetsockname(td, uap) struct thread *td; struct getsockname_args *uap; { return (getsockname1(td, uap, 1)); } #endif /* COMPAT_OLDSOCK */ /* * getpeername1() - Get name of peer for connected socket. */ /* ARGSUSED */ static int getpeername1(td, uap, compat) struct thread *td; struct getpeername_args /* { int fdes; struct sockaddr * __restrict asa; socklen_t * __restrict alen; } */ *uap; int compat; { struct sockaddr *sa; socklen_t len; int error; error = copyin(uap->alen, &len, sizeof (len)); if (error) return (error); error = kern_getpeername(td, uap->fdes, &sa, &len); if (error) return (error); if (len != 0) { #ifdef COMPAT_OLDSOCK if (compat) ((struct osockaddr *)sa)->sa_family = sa->sa_family; #endif error = copyout(sa, uap->asa, (u_int)len); } free(sa, M_SONAME); if (error == 0) error = copyout(&len, uap->alen, sizeof(len)); return (error); } int kern_getpeername(struct thread *td, int fd, struct sockaddr **sa, socklen_t *alen) { struct socket *so; struct file *fp; socklen_t len; int error; if (*alen < 0) return (EINVAL); AUDIT_ARG_FD(fd); error = getsock(td->td_proc->p_fd, fd, &fp, NULL); if (error) return (error); so = fp->f_data; if ((so->so_state & (SS_ISCONNECTED|SS_ISCONFIRMING)) == 0) { error = ENOTCONN; goto done; } *sa = NULL; CURVNET_SET(so->so_vnet); error = (*so->so_proto->pr_usrreqs->pru_peeraddr)(so, sa); CURVNET_RESTORE(); if (error) goto bad; if (*sa == NULL) len = 0; else len = MIN(*alen, (*sa)->sa_len); *alen = len; #ifdef KTRACE if (KTRPOINT(td, KTR_STRUCT)) ktrsockaddr(*sa); #endif bad: if (error && *sa) { free(*sa, M_SONAME); *sa = NULL; } done: fdrop(fp, td); return (error); } int getpeername(td, uap) struct thread *td; struct getpeername_args *uap; { return (getpeername1(td, uap, 0)); } #ifdef COMPAT_OLDSOCK int ogetpeername(td, uap) struct thread *td; struct ogetpeername_args *uap; { /* XXX uap should have type `getpeername_args *' to begin with. */ return (getpeername1(td, (struct getpeername_args *)uap, 1)); } #endif /* COMPAT_OLDSOCK */ int sockargs(mp, buf, buflen, type) struct mbuf **mp; caddr_t buf; int buflen, type; { struct sockaddr *sa; struct mbuf *m; int error; if ((u_int)buflen > MLEN) { #ifdef COMPAT_OLDSOCK if (type == MT_SONAME && (u_int)buflen <= 112) buflen = MLEN; /* unix domain compat. hack */ else #endif if ((u_int)buflen > MCLBYTES) return (EINVAL); } m = m_get(M_WAIT, type); if ((u_int)buflen > MLEN) MCLGET(m, M_WAIT); m->m_len = buflen; error = copyin(buf, mtod(m, caddr_t), (u_int)buflen); if (error) (void) m_free(m); else { *mp = m; if (type == MT_SONAME) { sa = mtod(m, struct sockaddr *); #if defined(COMPAT_OLDSOCK) && BYTE_ORDER != BIG_ENDIAN if (sa->sa_family == 0 && sa->sa_len < AF_MAX) sa->sa_family = sa->sa_len; #endif sa->sa_len = buflen; } } return (error); } int getsockaddr(namp, uaddr, len) struct sockaddr **namp; caddr_t uaddr; size_t len; { struct sockaddr *sa; int error; if (len > SOCK_MAXADDRLEN) return (ENAMETOOLONG); if (len < offsetof(struct sockaddr, sa_data[0])) return (EINVAL); sa = malloc(len, M_SONAME, M_WAITOK); error = copyin(uaddr, sa, len); if (error) { free(sa, M_SONAME); } else { #if defined(COMPAT_OLDSOCK) && BYTE_ORDER != BIG_ENDIAN if (sa->sa_family == 0 && sa->sa_len < AF_MAX) sa->sa_family = sa->sa_len; #endif sa->sa_len = len; *namp = sa; } return (error); } #include struct sendfile_sync { struct mtx mtx; struct cv cv; unsigned count; }; /* * Detach mapped page and release resources back to the system. */ void sf_buf_mext(void *addr, void *args) { vm_page_t m; struct sendfile_sync *sfs; m = sf_buf_page(args); sf_buf_free(args); vm_page_lock_queues(); vm_page_unwire(m, 0); /* * Check for the object going away on us. This can * happen since we don't hold a reference to it. * If so, we're responsible for freeing the page. */ if (m->wire_count == 0 && m->object == NULL) vm_page_free(m); vm_page_unlock_queues(); if (addr == NULL) return; sfs = addr; mtx_lock(&sfs->mtx); KASSERT(sfs->count> 0, ("Sendfile sync botchup count == 0")); if (--sfs->count == 0) cv_signal(&sfs->cv); mtx_unlock(&sfs->mtx); } /* * sendfile(2) * * int sendfile(int fd, int s, off_t offset, size_t nbytes, * struct sf_hdtr *hdtr, off_t *sbytes, int flags) * * Send a file specified by 'fd' and starting at 'offset' to a socket * specified by 's'. Send only 'nbytes' of the file or until EOF if nbytes == * 0. Optionally add a header and/or trailer to the socket output. If * specified, write the total number of bytes sent into *sbytes. */ int sendfile(struct thread *td, struct sendfile_args *uap) { return (do_sendfile(td, uap, 0)); } static int do_sendfile(struct thread *td, struct sendfile_args *uap, int compat) { struct sf_hdtr hdtr; struct uio *hdr_uio, *trl_uio; int error; hdr_uio = trl_uio = NULL; if (uap->hdtr != NULL) { error = copyin(uap->hdtr, &hdtr, sizeof(hdtr)); if (error) goto out; if (hdtr.headers != NULL) { error = copyinuio(hdtr.headers, hdtr.hdr_cnt, &hdr_uio); if (error) goto out; } if (hdtr.trailers != NULL) { error = copyinuio(hdtr.trailers, hdtr.trl_cnt, &trl_uio); if (error) goto out; } } error = kern_sendfile(td, uap, hdr_uio, trl_uio, compat); out: if (hdr_uio) free(hdr_uio, M_IOV); if (trl_uio) free(trl_uio, M_IOV); return (error); } #ifdef COMPAT_FREEBSD4 int freebsd4_sendfile(struct thread *td, struct freebsd4_sendfile_args *uap) { struct sendfile_args args; args.fd = uap->fd; args.s = uap->s; args.offset = uap->offset; args.nbytes = uap->nbytes; args.hdtr = uap->hdtr; args.sbytes = uap->sbytes; args.flags = uap->flags; return (do_sendfile(td, &args, 1)); } #endif /* COMPAT_FREEBSD4 */ int kern_sendfile(struct thread *td, struct sendfile_args *uap, struct uio *hdr_uio, struct uio *trl_uio, int compat) { struct file *sock_fp; struct vnode *vp; struct vm_object *obj = NULL; struct socket *so = NULL; struct mbuf *m = NULL; struct sf_buf *sf; struct vm_page *pg; off_t off, xfsize, fsbytes = 0, sbytes = 0, rem = 0; int error, hdrlen = 0, mnw = 0; int vfslocked; struct sendfile_sync *sfs = NULL; /* * The file descriptor must be a regular file and have a * backing VM object. * File offset must be positive. If it goes beyond EOF * we send only the header/trailer and no payload data. */ AUDIT_ARG_FD(uap->fd); if ((error = fgetvp_read(td, uap->fd, &vp)) != 0) goto out; vfslocked = VFS_LOCK_GIANT(vp->v_mount); vn_lock(vp, LK_SHARED | LK_RETRY); if (vp->v_type == VREG) { obj = vp->v_object; if (obj != NULL) { /* * Temporarily increase the backing VM * object's reference count so that a forced * reclamation of its vnode does not * immediately destroy it. */ VM_OBJECT_LOCK(obj); if ((obj->flags & OBJ_DEAD) == 0) { vm_object_reference_locked(obj); VM_OBJECT_UNLOCK(obj); } else { VM_OBJECT_UNLOCK(obj); obj = NULL; } } } VOP_UNLOCK(vp, 0); VFS_UNLOCK_GIANT(vfslocked); if (obj == NULL) { error = EINVAL; goto out; } if (uap->offset < 0) { error = EINVAL; goto out; } /* * The socket must be a stream socket and connected. * Remember if it a blocking or non-blocking socket. */ if ((error = getsock(td->td_proc->p_fd, uap->s, &sock_fp, NULL)) != 0) goto out; so = sock_fp->f_data; if (so->so_type != SOCK_STREAM) { error = EINVAL; goto out; } if ((so->so_state & SS_ISCONNECTED) == 0) { error = ENOTCONN; goto out; } /* * Do not wait on memory allocations but return ENOMEM for * caller to retry later. * XXX: Experimental. */ if (uap->flags & SF_MNOWAIT) mnw = 1; if (uap->flags & SF_SYNC) { sfs = malloc(sizeof *sfs, M_TEMP, M_WAITOK); memset(sfs, 0, sizeof *sfs); mtx_init(&sfs->mtx, "sendfile", MTX_DEF, 0); cv_init(&sfs->cv, "sendfile"); } #ifdef MAC error = mac_socket_check_send(td->td_ucred, so); if (error) goto out; #endif /* If headers are specified copy them into mbufs. */ if (hdr_uio != NULL) { hdr_uio->uio_td = td; hdr_uio->uio_rw = UIO_WRITE; if (hdr_uio->uio_resid > 0) { /* * In FBSD < 5.0 the nbytes to send also included * the header. If compat is specified subtract the * header size from nbytes. */ if (compat) { if (uap->nbytes > hdr_uio->uio_resid) uap->nbytes -= hdr_uio->uio_resid; else uap->nbytes = 0; } m = m_uiotombuf(hdr_uio, (mnw ? M_NOWAIT : M_WAITOK), 0, 0, 0); if (m == NULL) { error = mnw ? EAGAIN : ENOBUFS; goto out; } hdrlen = m_length(m, NULL); } } /* * Protect against multiple writers to the socket. * * XXXRW: Historically this has assumed non-interruptibility, so now * we implement that, but possibly shouldn't. */ (void)sblock(&so->so_snd, SBL_WAIT | SBL_NOINTR); /* * Loop through the pages of the file, starting with the requested * offset. Get a file page (do I/O if necessary), map the file page * into an sf_buf, attach an mbuf header to the sf_buf, and queue * it on the socket. * This is done in two loops. The inner loop turns as many pages * as it can, up to available socket buffer space, without blocking * into mbufs to have it bulk delivered into the socket send buffer. * The outer loop checks the state and available space of the socket * and takes care of the overall progress. */ for (off = uap->offset, rem = uap->nbytes; ; ) { int loopbytes = 0; int space = 0; int done = 0; /* * Check the socket state for ongoing connection, * no errors and space in socket buffer. * If space is low allow for the remainder of the * file to be processed if it fits the socket buffer. * Otherwise block in waiting for sufficient space * to proceed, or if the socket is nonblocking, return * to userland with EAGAIN while reporting how far * we've come. * We wait until the socket buffer has significant free * space to do bulk sends. This makes good use of file * system read ahead and allows packet segmentation * offloading hardware to take over lots of work. If * we were not careful here we would send off only one * sfbuf at a time. */ SOCKBUF_LOCK(&so->so_snd); if (so->so_snd.sb_lowat < so->so_snd.sb_hiwat / 2) so->so_snd.sb_lowat = so->so_snd.sb_hiwat / 2; retry_space: if (so->so_snd.sb_state & SBS_CANTSENDMORE) { error = EPIPE; SOCKBUF_UNLOCK(&so->so_snd); goto done; } else if (so->so_error) { error = so->so_error; so->so_error = 0; SOCKBUF_UNLOCK(&so->so_snd); goto done; } space = sbspace(&so->so_snd); if (space < rem && (space <= 0 || space < so->so_snd.sb_lowat)) { if (so->so_state & SS_NBIO) { SOCKBUF_UNLOCK(&so->so_snd); error = EAGAIN; goto done; } /* * sbwait drops the lock while sleeping. * When we loop back to retry_space the * state may have changed and we retest * for it. */ error = sbwait(&so->so_snd); /* * An error from sbwait usually indicates that we've * been interrupted by a signal. If we've sent anything * then return bytes sent, otherwise return the error. */ if (error) { SOCKBUF_UNLOCK(&so->so_snd); goto done; } goto retry_space; } SOCKBUF_UNLOCK(&so->so_snd); /* * Reduce space in the socket buffer by the size of * the header mbuf chain. * hdrlen is set to 0 after the first loop. */ space -= hdrlen; /* * Loop and construct maximum sized mbuf chain to be bulk * dumped into socket buffer. */ while(space > loopbytes) { vm_pindex_t pindex; vm_offset_t pgoff; struct mbuf *m0; VM_OBJECT_LOCK(obj); /* * Calculate the amount to transfer. * Not to exceed a page, the EOF, * or the passed in nbytes. */ pgoff = (vm_offset_t)(off & PAGE_MASK); xfsize = omin(PAGE_SIZE - pgoff, obj->un_pager.vnp.vnp_size - uap->offset - fsbytes - loopbytes); if (uap->nbytes) rem = (uap->nbytes - fsbytes - loopbytes); else rem = obj->un_pager.vnp.vnp_size - uap->offset - fsbytes - loopbytes; xfsize = omin(rem, xfsize); if (xfsize <= 0) { VM_OBJECT_UNLOCK(obj); done = 1; /* all data sent */ break; } /* * Don't overflow the send buffer. * Stop here and send out what we've * already got. */ if (space < loopbytes + xfsize) { VM_OBJECT_UNLOCK(obj); break; } /* * Attempt to look up the page. Allocate * if not found or wait and loop if busy. */ pindex = OFF_TO_IDX(off); pg = vm_page_grab(obj, pindex, VM_ALLOC_NOBUSY | VM_ALLOC_NORMAL | VM_ALLOC_WIRED | VM_ALLOC_RETRY); /* * Check if page is valid for what we need, * otherwise initiate I/O. * If we already turned some pages into mbufs, * send them off before we come here again and * block. */ if (pg->valid && vm_page_is_valid(pg, pgoff, xfsize)) VM_OBJECT_UNLOCK(obj); else if (m != NULL) error = EAGAIN; /* send what we already got */ else if (uap->flags & SF_NODISKIO) error = EBUSY; else { int bsize, resid; /* * Ensure that our page is still around * when the I/O completes. */ vm_page_io_start(pg); VM_OBJECT_UNLOCK(obj); /* * Get the page from backing store. */ bsize = vp->v_mount->mnt_stat.f_iosize; vfslocked = VFS_LOCK_GIANT(vp->v_mount); vn_lock(vp, LK_SHARED | LK_RETRY); /* * XXXMAC: Because we don't have fp->f_cred * here, we pass in NOCRED. This is probably * wrong, but is consistent with our original * implementation. */ error = vn_rdwr(UIO_READ, vp, NULL, MAXBSIZE, trunc_page(off), UIO_NOCOPY, IO_NODELOCKED | IO_VMIO | ((MAXBSIZE / bsize) << IO_SEQSHIFT), td->td_ucred, NOCRED, &resid, td); VOP_UNLOCK(vp, 0); VFS_UNLOCK_GIANT(vfslocked); VM_OBJECT_LOCK(obj); vm_page_io_finish(pg); if (!error) VM_OBJECT_UNLOCK(obj); mbstat.sf_iocnt++; } if (error) { vm_page_lock_queues(); vm_page_unwire(pg, 0); /* * See if anyone else might know about * this page. If not and it is not valid, * then free it. */ if (pg->wire_count == 0 && pg->valid == 0 && pg->busy == 0 && !(pg->oflags & VPO_BUSY) && pg->hold_count == 0) { vm_page_free(pg); } vm_page_unlock_queues(); VM_OBJECT_UNLOCK(obj); if (error == EAGAIN) error = 0; /* not a real error */ break; } /* * Get a sendfile buf. We usually wait as long * as necessary, but this wait can be interrupted. */ if ((sf = sf_buf_alloc(pg, (mnw ? SFB_NOWAIT : SFB_CATCH))) == NULL) { mbstat.sf_allocfail++; vm_page_lock_queues(); vm_page_unwire(pg, 0); /* * XXX: Not same check as above!? */ if (pg->wire_count == 0 && pg->object == NULL) vm_page_free(pg); vm_page_unlock_queues(); error = (mnw ? EAGAIN : EINTR); break; } /* * Get an mbuf and set it up as having * external storage. */ m0 = m_get((mnw ? M_NOWAIT : M_WAITOK), MT_DATA); if (m0 == NULL) { error = (mnw ? EAGAIN : ENOBUFS); sf_buf_mext((void *)sf_buf_kva(sf), sf); break; } MEXTADD(m0, sf_buf_kva(sf), PAGE_SIZE, sf_buf_mext, sfs, sf, M_RDONLY, EXT_SFBUF); m0->m_data = (char *)sf_buf_kva(sf) + pgoff; m0->m_len = xfsize; /* Append to mbuf chain. */ if (m != NULL) m_cat(m, m0); else m = m0; /* Keep track of bits processed. */ loopbytes += xfsize; off += xfsize; if (sfs != NULL) { mtx_lock(&sfs->mtx); sfs->count++; mtx_unlock(&sfs->mtx); } } /* Add the buffer chain to the socket buffer. */ if (m != NULL) { int mlen, err; mlen = m_length(m, NULL); SOCKBUF_LOCK(&so->so_snd); if (so->so_snd.sb_state & SBS_CANTSENDMORE) { error = EPIPE; SOCKBUF_UNLOCK(&so->so_snd); goto done; } SOCKBUF_UNLOCK(&so->so_snd); CURVNET_SET(so->so_vnet); /* Avoid error aliasing. */ err = (*so->so_proto->pr_usrreqs->pru_send) (so, 0, m, NULL, NULL, td); CURVNET_RESTORE(); if (err == 0) { /* * We need two counters to get the * file offset and nbytes to send * right: * - sbytes contains the total amount * of bytes sent, including headers. * - fsbytes contains the total amount * of bytes sent from the file. */ sbytes += mlen; fsbytes += mlen; if (hdrlen) { fsbytes -= hdrlen; hdrlen = 0; } } else if (error == 0) error = err; m = NULL; /* pru_send always consumes */ } /* Quit outer loop on error or when we're done. */ if (done) break; if (error) goto done; } /* * Send trailers. Wimp out and use writev(2). */ if (trl_uio != NULL) { sbunlock(&so->so_snd); error = kern_writev(td, uap->s, trl_uio); if (error == 0) sbytes += td->td_retval[0]; goto out; } done: sbunlock(&so->so_snd); out: /* * If there was no error we have to clear td->td_retval[0] * because it may have been set by writev. */ if (error == 0) { td->td_retval[0] = 0; } if (uap->sbytes != NULL) { copyout(&sbytes, uap->sbytes, sizeof(off_t)); } if (obj != NULL) vm_object_deallocate(obj); if (vp != NULL) { vfslocked = VFS_LOCK_GIANT(vp->v_mount); vrele(vp); VFS_UNLOCK_GIANT(vfslocked); } if (so) fdrop(sock_fp, td); if (m) m_freem(m); if (sfs != NULL) { mtx_lock(&sfs->mtx); if (sfs->count != 0) cv_wait(&sfs->cv, &sfs->mtx); KASSERT(sfs->count == 0, ("sendfile sync still busy")); cv_destroy(&sfs->cv); mtx_destroy(&sfs->mtx); free(sfs, M_TEMP); } if (error == ERESTART) error = EINTR; return (error); } /* * SCTP syscalls. * Functionality only compiled in if SCTP is defined in the kernel Makefile, * otherwise all return EOPNOTSUPP. * XXX: We should make this loadable one day. */ int sctp_peeloff(td, uap) struct thread *td; struct sctp_peeloff_args /* { int sd; caddr_t name; } */ *uap; { #if (defined(INET) || defined(INET6)) && defined(SCTP) struct filedesc *fdp; struct file *nfp = NULL; int error; struct socket *head, *so; int fd; u_int fflag; fdp = td->td_proc->p_fd; AUDIT_ARG_FD(uap->sd); error = fgetsock(td, uap->sd, &head, &fflag); if (error) goto done2; error = sctp_can_peel_off(head, (sctp_assoc_t)uap->name); if (error) goto done2; /* * At this point we know we do have a assoc to pull * we proceed to get the fd setup. This may block * but that is ok. */ error = falloc(td, &nfp, &fd); if (error) goto done; td->td_retval[0] = fd; + CURVNET_SET(head->so_vnet); so = sonewconn(head, SS_ISCONNECTED); if (so == NULL) goto noconnection; /* * Before changing the flags on the socket, we have to bump the * reference count. Otherwise, if the protocol calls sofree(), * the socket will be released due to a zero refcount. */ SOCK_LOCK(so); soref(so); /* file descriptor reference */ SOCK_UNLOCK(so); ACCEPT_LOCK(); TAILQ_REMOVE(&head->so_comp, so, so_list); head->so_qlen--; so->so_state |= (head->so_state & SS_NBIO); so->so_state &= ~SS_NOFDREF; so->so_qstate &= ~SQ_COMP; so->so_head = NULL; ACCEPT_UNLOCK(); finit(nfp, fflag, DTYPE_SOCKET, so, &socketops); error = sctp_do_peeloff(head, so, (sctp_assoc_t)uap->name); if (error) goto noconnection; if (head->so_sigio != NULL) fsetown(fgetown(&head->so_sigio), &so->so_sigio); noconnection: /* * close the new descriptor, assuming someone hasn't ripped it * out from under us. */ if (error) fdclose(fdp, nfp, fd, td); /* * Release explicitly held references before returning. */ + CURVNET_RESTORE(); done: if (nfp != NULL) fdrop(nfp, td); fputsock(head); done2: return (error); #else /* SCTP */ return (EOPNOTSUPP); #endif /* SCTP */ } int sctp_generic_sendmsg (td, uap) struct thread *td; struct sctp_generic_sendmsg_args /* { int sd, caddr_t msg, int mlen, caddr_t to, __socklen_t tolen, struct sctp_sndrcvinfo *sinfo, int flags } */ *uap; { #if (defined(INET) || defined(INET6)) && defined(SCTP) struct sctp_sndrcvinfo sinfo, *u_sinfo = NULL; struct socket *so; struct file *fp = NULL; int use_rcvinfo = 1; int error = 0, len; struct sockaddr *to = NULL; #ifdef KTRACE struct uio *ktruio = NULL; #endif struct uio auio; struct iovec iov[1]; if (uap->sinfo) { error = copyin(uap->sinfo, &sinfo, sizeof (sinfo)); if (error) return (error); u_sinfo = &sinfo; } if (uap->tolen) { error = getsockaddr(&to, uap->to, uap->tolen); if (error) { to = NULL; goto sctp_bad2; } } AUDIT_ARG_FD(uap->sd); error = getsock(td->td_proc->p_fd, uap->sd, &fp, NULL); if (error) goto sctp_bad; #ifdef KTRACE if (KTRPOINT(td, KTR_STRUCT)) ktrsockaddr(to); #endif iov[0].iov_base = uap->msg; iov[0].iov_len = uap->mlen; so = (struct socket *)fp->f_data; #ifdef MAC error = mac_socket_check_send(td->td_ucred, so); if (error) goto sctp_bad; #endif /* MAC */ auio.uio_iov = iov; auio.uio_iovcnt = 1; auio.uio_segflg = UIO_USERSPACE; auio.uio_rw = UIO_WRITE; auio.uio_td = td; auio.uio_offset = 0; /* XXX */ auio.uio_resid = 0; len = auio.uio_resid = uap->mlen; + CURVNET_SET(so->so_vnet); error = sctp_lower_sosend(so, to, &auio, (struct mbuf *)NULL, (struct mbuf *)NULL, uap->flags, use_rcvinfo, u_sinfo, td); + CURVNET_RESTORE(); if (error) { if (auio.uio_resid != len && (error == ERESTART || error == EINTR || error == EWOULDBLOCK)) error = 0; /* Generation of SIGPIPE can be controlled per socket. */ if (error == EPIPE && !(so->so_options & SO_NOSIGPIPE) && !(uap->flags & MSG_NOSIGNAL)) { PROC_LOCK(td->td_proc); psignal(td->td_proc, SIGPIPE); PROC_UNLOCK(td->td_proc); } } if (error == 0) td->td_retval[0] = len - auio.uio_resid; #ifdef KTRACE if (ktruio != NULL) { ktruio->uio_resid = td->td_retval[0]; ktrgenio(uap->sd, UIO_WRITE, ktruio, error); } #endif /* KTRACE */ sctp_bad: if (fp) fdrop(fp, td); sctp_bad2: if (to) free(to, M_SONAME); return (error); #else /* SCTP */ return (EOPNOTSUPP); #endif /* SCTP */ } int sctp_generic_sendmsg_iov(td, uap) struct thread *td; struct sctp_generic_sendmsg_iov_args /* { int sd, struct iovec *iov, int iovlen, caddr_t to, __socklen_t tolen, struct sctp_sndrcvinfo *sinfo, int flags } */ *uap; { #if (defined(INET) || defined(INET6)) && defined(SCTP) struct sctp_sndrcvinfo sinfo, *u_sinfo = NULL; struct socket *so; struct file *fp = NULL; int use_rcvinfo = 1; int error=0, len, i; struct sockaddr *to = NULL; #ifdef KTRACE struct uio *ktruio = NULL; #endif struct uio auio; struct iovec *iov, *tiov; if (uap->sinfo) { error = copyin(uap->sinfo, &sinfo, sizeof (sinfo)); if (error) return (error); u_sinfo = &sinfo; } if (uap->tolen) { error = getsockaddr(&to, uap->to, uap->tolen); if (error) { to = NULL; goto sctp_bad2; } } AUDIT_ARG_FD(uap->sd); error = getsock(td->td_proc->p_fd, uap->sd, &fp, NULL); if (error) goto sctp_bad1; error = copyiniov(uap->iov, uap->iovlen, &iov, EMSGSIZE); if (error) goto sctp_bad1; #ifdef KTRACE if (KTRPOINT(td, KTR_STRUCT)) ktrsockaddr(to); #endif so = (struct socket *)fp->f_data; #ifdef MAC error = mac_socket_check_send(td->td_ucred, so); if (error) goto sctp_bad; #endif /* MAC */ auio.uio_iov = iov; auio.uio_iovcnt = uap->iovlen; auio.uio_segflg = UIO_USERSPACE; auio.uio_rw = UIO_WRITE; auio.uio_td = td; auio.uio_offset = 0; /* XXX */ auio.uio_resid = 0; tiov = iov; for (i = 0; i iovlen; i++, tiov++) { if ((auio.uio_resid += tiov->iov_len) < 0) { error = EINVAL; goto sctp_bad; } } len = auio.uio_resid; + CURVNET_SET(so->so_vnet); error = sctp_lower_sosend(so, to, &auio, (struct mbuf *)NULL, (struct mbuf *)NULL, uap->flags, use_rcvinfo, u_sinfo, td); + CURVNET_RESTORE(); if (error) { if (auio.uio_resid != len && (error == ERESTART || error == EINTR || error == EWOULDBLOCK)) error = 0; /* Generation of SIGPIPE can be controlled per socket */ if (error == EPIPE && !(so->so_options & SO_NOSIGPIPE) && !(uap->flags & MSG_NOSIGNAL)) { PROC_LOCK(td->td_proc); psignal(td->td_proc, SIGPIPE); PROC_UNLOCK(td->td_proc); } } if (error == 0) td->td_retval[0] = len - auio.uio_resid; #ifdef KTRACE if (ktruio != NULL) { ktruio->uio_resid = td->td_retval[0]; ktrgenio(uap->sd, UIO_WRITE, ktruio, error); } #endif /* KTRACE */ sctp_bad: free(iov, M_IOV); sctp_bad1: if (fp) fdrop(fp, td); sctp_bad2: if (to) free(to, M_SONAME); return (error); #else /* SCTP */ return (EOPNOTSUPP); #endif /* SCTP */ } int sctp_generic_recvmsg(td, uap) struct thread *td; struct sctp_generic_recvmsg_args /* { int sd, struct iovec *iov, int iovlen, struct sockaddr *from, __socklen_t *fromlenaddr, struct sctp_sndrcvinfo *sinfo, int *msg_flags } */ *uap; { #if (defined(INET) || defined(INET6)) && defined(SCTP) u_int8_t sockbufstore[256]; struct uio auio; struct iovec *iov, *tiov; struct sctp_sndrcvinfo sinfo; struct socket *so; struct file *fp = NULL; struct sockaddr *fromsa; int fromlen; int len, i, msg_flags; int error = 0; #ifdef KTRACE struct uio *ktruio = NULL; #endif AUDIT_ARG_FD(uap->sd); error = getsock(td->td_proc->p_fd, uap->sd, &fp, NULL); if (error) { return (error); } error = copyiniov(uap->iov, uap->iovlen, &iov, EMSGSIZE); if (error) { goto out1; } so = fp->f_data; #ifdef MAC error = mac_socket_check_receive(td->td_ucred, so); if (error) { goto out; return (error); } #endif /* MAC */ if (uap->fromlenaddr) { error = copyin(uap->fromlenaddr, &fromlen, sizeof (fromlen)); if (error) { goto out; } } else { fromlen = 0; } if(uap->msg_flags) { error = copyin(uap->msg_flags, &msg_flags, sizeof (int)); if (error) { goto out; } } else { msg_flags = 0; } auio.uio_iov = iov; auio.uio_iovcnt = uap->iovlen; auio.uio_segflg = UIO_USERSPACE; auio.uio_rw = UIO_READ; auio.uio_td = td; auio.uio_offset = 0; /* XXX */ auio.uio_resid = 0; tiov = iov; for (i = 0; i iovlen; i++, tiov++) { if ((auio.uio_resid += tiov->iov_len) < 0) { error = EINVAL; goto out; } } len = auio.uio_resid; fromsa = (struct sockaddr *)sockbufstore; #ifdef KTRACE if (KTRPOINT(td, KTR_GENIO)) ktruio = cloneuio(&auio); #endif /* KTRACE */ + CURVNET_SET(so->so_vnet); error = sctp_sorecvmsg(so, &auio, (struct mbuf **)NULL, fromsa, fromlen, &msg_flags, (struct sctp_sndrcvinfo *)&sinfo, 1); + CURVNET_RESTORE(); if (error) { if (auio.uio_resid != (int)len && (error == ERESTART || error == EINTR || error == EWOULDBLOCK)) error = 0; } else { if (uap->sinfo) error = copyout(&sinfo, uap->sinfo, sizeof (sinfo)); } #ifdef KTRACE if (ktruio != NULL) { ktruio->uio_resid = (int)len - auio.uio_resid; ktrgenio(uap->sd, UIO_READ, ktruio, error); } #endif /* KTRACE */ if (error) goto out; td->td_retval[0] = (int)len - auio.uio_resid; if (fromlen && uap->from) { len = fromlen; if (len <= 0 || fromsa == 0) len = 0; else { len = MIN(len, fromsa->sa_len); error = copyout(fromsa, uap->from, (unsigned)len); if (error) goto out; } error = copyout(&len, uap->fromlenaddr, sizeof (socklen_t)); if (error) { goto out; } } #ifdef KTRACE if (KTRPOINT(td, KTR_STRUCT)) ktrsockaddr(fromsa); #endif if (uap->msg_flags) { error = copyout(&msg_flags, uap->msg_flags, sizeof (int)); if (error) { goto out; } } out: free(iov, M_IOV); out1: if (fp) fdrop(fp, td); return (error); #else /* SCTP */ return (EOPNOTSUPP); #endif /* SCTP */ } Index: head/sys/netinet/in_proto.c =================================================================== --- head/sys/netinet/in_proto.c (revision 197325) +++ head/sys/netinet/in_proto.c (revision 197326) @@ -1,413 +1,416 @@ /*- * Copyright (c) 1982, 1986, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)in_proto.c 8.2 (Berkeley) 2/9/95 */ #include __FBSDID("$FreeBSD$"); #include "opt_ipx.h" #include "opt_mrouting.h" #include "opt_ipsec.h" #include "opt_inet6.h" #include "opt_pf.h" #include "opt_carp.h" #include "opt_sctp.h" #include "opt_mpath.h" #include #include #include #include #include #include #include #include #include #include #include #ifdef RADIX_MPATH #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * TCP/IP protocol family: IP, ICMP, UDP, TCP. */ static struct pr_usrreqs nousrreqs; #ifdef IPSEC #include #endif /* IPSEC */ #ifdef SCTP #include #include #include #include #endif /* SCTP */ #ifdef DEV_PFSYNC #include #include #endif #ifdef DEV_CARP #include #endif extern struct domain inetdomain; /* Spacer for loadable protocols. */ #define IPPROTOSPACER \ { \ .pr_domain = &inetdomain, \ .pr_protocol = PROTO_SPACER, \ .pr_usrreqs = &nousrreqs \ } struct protosw inetsw[] = { { .pr_type = 0, .pr_domain = &inetdomain, .pr_protocol = IPPROTO_IP, .pr_init = ip_init, .pr_slowtimo = ip_slowtimo, .pr_drain = ip_drain, .pr_usrreqs = &nousrreqs }, { .pr_type = SOCK_DGRAM, .pr_domain = &inetdomain, .pr_protocol = IPPROTO_UDP, .pr_flags = PR_ATOMIC|PR_ADDR, .pr_input = udp_input, .pr_ctlinput = udp_ctlinput, .pr_ctloutput = udp_ctloutput, .pr_init = udp_init, #ifdef VIMAGE .pr_destroy = udp_destroy, #endif .pr_usrreqs = &udp_usrreqs }, { .pr_type = SOCK_STREAM, .pr_domain = &inetdomain, .pr_protocol = IPPROTO_TCP, .pr_flags = PR_CONNREQUIRED|PR_IMPLOPCL|PR_WANTRCVD, .pr_input = tcp_input, .pr_ctlinput = tcp_ctlinput, .pr_ctloutput = tcp_ctloutput, .pr_init = tcp_init, #ifdef VIMAGE .pr_destroy = tcp_destroy, #endif .pr_slowtimo = tcp_slowtimo, .pr_drain = tcp_drain, .pr_usrreqs = &tcp_usrreqs }, #ifdef SCTP { - .pr_type = SOCK_DGRAM, - .pr_domain = &inetdomain, - .pr_protocol = IPPROTO_SCTP, - .pr_flags = PR_WANTRCVD, - .pr_input = sctp_input, - .pr_ctlinput = sctp_ctlinput, - .pr_ctloutput = sctp_ctloutput, - .pr_init = sctp_init, - .pr_drain = sctp_drain, - .pr_usrreqs = &sctp_usrreqs + .pr_type = SOCK_DGRAM, + .pr_domain = &inetdomain, + .pr_protocol = IPPROTO_SCTP, + .pr_flags = PR_WANTRCVD, + .pr_input = sctp_input, + .pr_ctlinput = sctp_ctlinput, + .pr_ctloutput = sctp_ctloutput, + .pr_init = sctp_init, +#ifdef VIMAGE + .pr_destroy = sctp_finish, +#endif + .pr_drain = sctp_drain, + .pr_usrreqs = &sctp_usrreqs }, { - .pr_type = SOCK_SEQPACKET, - .pr_domain = &inetdomain, - .pr_protocol = IPPROTO_SCTP, - .pr_flags = PR_WANTRCVD, - .pr_input = sctp_input, - .pr_ctlinput = sctp_ctlinput, - .pr_ctloutput = sctp_ctloutput, - .pr_drain = sctp_drain, - .pr_usrreqs = &sctp_usrreqs + .pr_type = SOCK_SEQPACKET, + .pr_domain = &inetdomain, + .pr_protocol = IPPROTO_SCTP, + .pr_flags = PR_WANTRCVD, + .pr_input = sctp_input, + .pr_ctlinput = sctp_ctlinput, + .pr_ctloutput = sctp_ctloutput, + .pr_drain = sctp_drain, + .pr_usrreqs = &sctp_usrreqs }, { - .pr_type = SOCK_STREAM, - .pr_domain = &inetdomain, - .pr_protocol = IPPROTO_SCTP, - .pr_flags = PR_WANTRCVD, - .pr_input = sctp_input, - .pr_ctlinput = sctp_ctlinput, - .pr_ctloutput = sctp_ctloutput, - .pr_drain = sctp_drain, - .pr_usrreqs = &sctp_usrreqs + .pr_type = SOCK_STREAM, + .pr_domain = &inetdomain, + .pr_protocol = IPPROTO_SCTP, + .pr_flags = PR_WANTRCVD, + .pr_input = sctp_input, + .pr_ctlinput = sctp_ctlinput, + .pr_ctloutput = sctp_ctloutput, + .pr_drain = sctp_drain, + .pr_usrreqs = &sctp_usrreqs }, #endif /* SCTP */ { .pr_type = SOCK_RAW, .pr_domain = &inetdomain, .pr_protocol = IPPROTO_RAW, .pr_flags = PR_ATOMIC|PR_ADDR, .pr_input = rip_input, .pr_ctlinput = rip_ctlinput, .pr_ctloutput = rip_ctloutput, .pr_usrreqs = &rip_usrreqs }, { .pr_type = SOCK_RAW, .pr_domain = &inetdomain, .pr_protocol = IPPROTO_ICMP, .pr_flags = PR_ATOMIC|PR_ADDR|PR_LASTHDR, .pr_input = icmp_input, .pr_ctloutput = rip_ctloutput, .pr_init = icmp_init, .pr_usrreqs = &rip_usrreqs }, { .pr_type = SOCK_RAW, .pr_domain = &inetdomain, .pr_protocol = IPPROTO_IGMP, .pr_flags = PR_ATOMIC|PR_ADDR|PR_LASTHDR, .pr_input = igmp_input, .pr_ctloutput = rip_ctloutput, .pr_fasttimo = igmp_fasttimo, .pr_slowtimo = igmp_slowtimo, .pr_usrreqs = &rip_usrreqs }, { .pr_type = SOCK_RAW, .pr_domain = &inetdomain, .pr_protocol = IPPROTO_RSVP, .pr_flags = PR_ATOMIC|PR_ADDR|PR_LASTHDR, .pr_input = rsvp_input, .pr_ctloutput = rip_ctloutput, .pr_usrreqs = &rip_usrreqs }, #ifdef IPSEC { .pr_type = SOCK_RAW, .pr_domain = &inetdomain, .pr_protocol = IPPROTO_AH, .pr_flags = PR_ATOMIC|PR_ADDR, .pr_input = ah4_input, .pr_ctlinput = ah4_ctlinput, .pr_usrreqs = &nousrreqs }, { .pr_type = SOCK_RAW, .pr_domain = &inetdomain, .pr_protocol = IPPROTO_ESP, .pr_flags = PR_ATOMIC|PR_ADDR, .pr_input = esp4_input, .pr_ctlinput = esp4_ctlinput, .pr_usrreqs = &nousrreqs }, { .pr_type = SOCK_RAW, .pr_domain = &inetdomain, .pr_protocol = IPPROTO_IPCOMP, .pr_flags = PR_ATOMIC|PR_ADDR, .pr_input = ipcomp4_input, .pr_usrreqs = &nousrreqs }, #endif /* IPSEC */ { .pr_type = SOCK_RAW, .pr_domain = &inetdomain, .pr_protocol = IPPROTO_IPV4, .pr_flags = PR_ATOMIC|PR_ADDR|PR_LASTHDR, .pr_input = encap4_input, .pr_ctloutput = rip_ctloutput, .pr_init = encap_init, .pr_usrreqs = &rip_usrreqs }, { .pr_type = SOCK_RAW, .pr_domain = &inetdomain, .pr_protocol = IPPROTO_MOBILE, .pr_flags = PR_ATOMIC|PR_ADDR|PR_LASTHDR, .pr_input = encap4_input, .pr_ctloutput = rip_ctloutput, .pr_init = encap_init, .pr_usrreqs = &rip_usrreqs }, { .pr_type = SOCK_RAW, .pr_domain = &inetdomain, .pr_protocol = IPPROTO_ETHERIP, .pr_flags = PR_ATOMIC|PR_ADDR|PR_LASTHDR, .pr_input = encap4_input, .pr_ctloutput = rip_ctloutput, .pr_init = encap_init, .pr_usrreqs = &rip_usrreqs }, { .pr_type = SOCK_RAW, .pr_domain = &inetdomain, .pr_protocol = IPPROTO_GRE, .pr_flags = PR_ATOMIC|PR_ADDR|PR_LASTHDR, .pr_input = encap4_input, .pr_ctloutput = rip_ctloutput, .pr_init = encap_init, .pr_usrreqs = &rip_usrreqs }, # ifdef INET6 { .pr_type = SOCK_RAW, .pr_domain = &inetdomain, .pr_protocol = IPPROTO_IPV6, .pr_flags = PR_ATOMIC|PR_ADDR|PR_LASTHDR, .pr_input = encap4_input, .pr_ctloutput = rip_ctloutput, .pr_init = encap_init, .pr_usrreqs = &rip_usrreqs }, #endif { .pr_type = SOCK_RAW, .pr_domain = &inetdomain, .pr_protocol = IPPROTO_PIM, .pr_flags = PR_ATOMIC|PR_ADDR|PR_LASTHDR, .pr_input = encap4_input, .pr_ctloutput = rip_ctloutput, .pr_usrreqs = &rip_usrreqs }, #ifdef DEV_PFSYNC { .pr_type = SOCK_RAW, .pr_domain = &inetdomain, .pr_protocol = IPPROTO_PFSYNC, .pr_flags = PR_ATOMIC|PR_ADDR, .pr_input = pfsync_input, .pr_ctloutput = rip_ctloutput, .pr_usrreqs = &rip_usrreqs }, #endif /* DEV_PFSYNC */ #ifdef DEV_CARP { .pr_type = SOCK_RAW, .pr_domain = &inetdomain, .pr_protocol = IPPROTO_CARP, .pr_flags = PR_ATOMIC|PR_ADDR, .pr_input = carp_input, .pr_output = (pr_output_t*)rip_output, .pr_ctloutput = rip_ctloutput, .pr_usrreqs = &rip_usrreqs }, #endif /* DEV_CARP */ /* Spacer n-times for loadable protocols. */ IPPROTOSPACER, IPPROTOSPACER, IPPROTOSPACER, IPPROTOSPACER, IPPROTOSPACER, IPPROTOSPACER, IPPROTOSPACER, IPPROTOSPACER, /* raw wildcard */ { .pr_type = SOCK_RAW, .pr_domain = &inetdomain, .pr_flags = PR_ATOMIC|PR_ADDR, .pr_input = rip_input, .pr_ctloutput = rip_ctloutput, .pr_init = rip_init, #ifdef VIMAGE .pr_destroy = rip_destroy, #endif .pr_usrreqs = &rip_usrreqs }, }; extern int in_inithead(void **, int); extern int in_detachhead(void **, int); struct domain inetdomain = { .dom_family = AF_INET, .dom_name = "internet", .dom_protosw = inetsw, .dom_protoswNPROTOSW = &inetsw[sizeof(inetsw)/sizeof(inetsw[0])], #ifdef RADIX_MPATH .dom_rtattach = rn4_mpath_inithead, #else .dom_rtattach = in_inithead, #endif #ifdef VIMAGE .dom_rtdetach = in_detachhead, #endif .dom_rtoffset = 32, .dom_maxrtkey = sizeof(struct sockaddr_in), .dom_ifattach = in_domifattach, .dom_ifdetach = in_domifdetach }; VNET_DOMAIN_SET(inet); SYSCTL_NODE(_net, PF_INET, inet, CTLFLAG_RW, 0, "Internet Family"); SYSCTL_NODE(_net_inet, IPPROTO_IP, ip, CTLFLAG_RW, 0, "IP"); SYSCTL_NODE(_net_inet, IPPROTO_ICMP, icmp, CTLFLAG_RW, 0, "ICMP"); SYSCTL_NODE(_net_inet, IPPROTO_UDP, udp, CTLFLAG_RW, 0, "UDP"); SYSCTL_NODE(_net_inet, IPPROTO_TCP, tcp, CTLFLAG_RW, 0, "TCP"); #ifdef SCTP SYSCTL_NODE(_net_inet, IPPROTO_SCTP, sctp, CTLFLAG_RW, 0, "SCTP"); #endif SYSCTL_NODE(_net_inet, IPPROTO_IGMP, igmp, CTLFLAG_RW, 0, "IGMP"); #ifdef IPSEC /* XXX no protocol # to use, pick something "reserved" */ SYSCTL_NODE(_net_inet, 253, ipsec, CTLFLAG_RW, 0, "IPSEC"); SYSCTL_NODE(_net_inet, IPPROTO_AH, ah, CTLFLAG_RW, 0, "AH"); SYSCTL_NODE(_net_inet, IPPROTO_ESP, esp, CTLFLAG_RW, 0, "ESP"); SYSCTL_NODE(_net_inet, IPPROTO_IPCOMP, ipcomp, CTLFLAG_RW, 0, "IPCOMP"); SYSCTL_NODE(_net_inet, IPPROTO_IPIP, ipip, CTLFLAG_RW, 0, "IPIP"); #endif /* IPSEC */ SYSCTL_NODE(_net_inet, IPPROTO_RAW, raw, CTLFLAG_RW, 0, "RAW"); #ifdef DEV_PFSYNC SYSCTL_NODE(_net_inet, IPPROTO_PFSYNC, pfsync, CTLFLAG_RW, 0, "PFSYNC"); #endif #ifdef DEV_CARP SYSCTL_NODE(_net_inet, IPPROTO_CARP, carp, CTLFLAG_RW, 0, "CARP"); #endif Index: head/sys/netinet/sctp_bsd_addr.c =================================================================== --- head/sys/netinet/sctp_bsd_addr.c (revision 197325) +++ head/sys/netinet/sctp_bsd_addr.c (revision 197326) @@ -1,534 +1,548 @@ /*- * Copyright (c) 2001-2007, by Cisco Systems, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * a) Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * b) Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the distribution. * * c) Neither the name of Cisco Systems, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ /* $KAME: sctp_output.c,v 1.46 2005/03/06 16:04:17 itojun Exp $ */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Declare all of our malloc named types */ /* Note to Michael/Peter for mac-os, * I think mac has this too since I * do see the M_PCB type, so I * will also put in the mac file the * MALLOC_DECLARE. If this does not * work for mac uncomment the defines for * the strings that we use in Panda, I put * them in comments in the mac-os file. */ MALLOC_DEFINE(SCTP_M_MAP, "sctp_map", "sctp asoc map descriptor"); MALLOC_DEFINE(SCTP_M_STRMI, "sctp_stri", "sctp stream in array"); MALLOC_DEFINE(SCTP_M_STRMO, "sctp_stro", "sctp stream out array"); MALLOC_DEFINE(SCTP_M_ASC_ADDR, "sctp_aadr", "sctp asconf address"); MALLOC_DEFINE(SCTP_M_ASC_IT, "sctp_a_it", "sctp asconf iterator"); MALLOC_DEFINE(SCTP_M_AUTH_CL, "sctp_atcl", "sctp auth chunklist"); MALLOC_DEFINE(SCTP_M_AUTH_KY, "sctp_atky", "sctp auth key"); MALLOC_DEFINE(SCTP_M_AUTH_HL, "sctp_athm", "sctp auth hmac list"); MALLOC_DEFINE(SCTP_M_AUTH_IF, "sctp_athi", "sctp auth info"); MALLOC_DEFINE(SCTP_M_STRESET, "sctp_stre", "sctp stream reset"); MALLOC_DEFINE(SCTP_M_CMSG, "sctp_cmsg", "sctp CMSG buffer"); MALLOC_DEFINE(SCTP_M_COPYAL, "sctp_cpal", "sctp copy all"); MALLOC_DEFINE(SCTP_M_VRF, "sctp_vrf", "sctp vrf struct"); MALLOC_DEFINE(SCTP_M_IFA, "sctp_ifa", "sctp ifa struct"); MALLOC_DEFINE(SCTP_M_IFN, "sctp_ifn", "sctp ifn struct"); MALLOC_DEFINE(SCTP_M_TIMW, "sctp_timw", "sctp time block"); MALLOC_DEFINE(SCTP_M_MVRF, "sctp_mvrf", "sctp mvrf pcb list"); MALLOC_DEFINE(SCTP_M_ITER, "sctp_iter", "sctp iterator control"); MALLOC_DEFINE(SCTP_M_SOCKOPT, "sctp_socko", "sctp socket option"); #if defined(SCTP_USE_THREAD_BASED_ITERATOR) void sctp_wakeup_iterator(void) { wakeup(&SCTP_BASE_INFO(iterator_running)); } static void sctp_iterator_thread(void *v) { + CURVNET_SET((struct vnet *)v); SCTP_IPI_ITERATOR_WQ_LOCK(); SCTP_BASE_INFO(iterator_running) = 0; while (1) { msleep(&SCTP_BASE_INFO(iterator_running), &SCTP_BASE_INFO(ipi_iterator_wq_mtx), 0, "waiting_for_work", 0); if (SCTP_BASE_INFO(threads_must_exit)) { kthread_exit(); } sctp_iterator_worker(); } + CURVNET_RESTORE(); } void sctp_startup_iterator(void) { int ret; ret = kproc_create(sctp_iterator_thread, - (void *)NULL, + (void *)curvnet, &SCTP_BASE_INFO(thread_proc), RFPROC, SCTP_KTHREAD_PAGES, SCTP_KTRHEAD_NAME); } #endif #ifdef INET6 void sctp_gather_internal_ifa_flags(struct sctp_ifa *ifa) { struct in6_ifaddr *ifa6; ifa6 = (struct in6_ifaddr *)ifa->ifa; ifa->flags = ifa6->ia6_flags; if (!MODULE_GLOBAL(ip6_use_deprecated)) { if (ifa->flags & IN6_IFF_DEPRECATED) { ifa->localifa_flags |= SCTP_ADDR_IFA_UNUSEABLE; } else { ifa->localifa_flags &= ~SCTP_ADDR_IFA_UNUSEABLE; } } else { ifa->localifa_flags &= ~SCTP_ADDR_IFA_UNUSEABLE; } if (ifa->flags & (IN6_IFF_DETACHED | IN6_IFF_ANYCAST | IN6_IFF_NOTREADY)) { ifa->localifa_flags |= SCTP_ADDR_IFA_UNUSEABLE; } else { ifa->localifa_flags &= ~SCTP_ADDR_IFA_UNUSEABLE; } } #endif /* INET6 */ static uint32_t sctp_is_desired_interface_type(struct ifaddr *ifa) { int result; /* check the interface type to see if it's one we care about */ switch (ifa->ifa_ifp->if_type) { case IFT_ETHER: case IFT_ISO88023: case IFT_ISO88024: case IFT_ISO88025: case IFT_ISO88026: case IFT_STARLAN: case IFT_P10: case IFT_P80: case IFT_HY: case IFT_FDDI: case IFT_XETHER: case IFT_ISDNBASIC: case IFT_ISDNPRIMARY: case IFT_PTPSERIAL: case IFT_OTHER: case IFT_PPP: case IFT_LOOP: case IFT_SLIP: case IFT_GIF: case IFT_L2VLAN: case IFT_IP: case IFT_IPOVERCDLC: case IFT_IPOVERCLAW: case IFT_VIRTUALIPADDRESS: result = 1; break; default: result = 0; } return (result); } static void sctp_init_ifns_for_vrf(int vrfid) { /* * Here we must apply ANY locks needed by the IFN we access and also * make sure we lock any IFA that exists as we float through the * list of IFA's */ struct ifnet *ifn; struct ifaddr *ifa; struct in6_ifaddr *ifa6; struct sctp_ifa *sctp_ifa; uint32_t ifa_flags; +#if 0 + IFNET_RLOCK(); +#endif TAILQ_FOREACH(ifn, &MODULE_GLOBAL(ifnet), if_list) { IF_ADDR_LOCK(ifn); TAILQ_FOREACH(ifa, &ifn->if_addrlist, ifa_list) { if (ifa->ifa_addr == NULL) { continue; } if ((ifa->ifa_addr->sa_family != AF_INET) && (ifa->ifa_addr->sa_family != AF_INET6)) { /* non inet/inet6 skip */ continue; } if (ifa->ifa_addr->sa_family == AF_INET6) { if (IN6_IS_ADDR_UNSPECIFIED(&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr)) { /* skip unspecifed addresses */ continue; } } else { if (((struct sockaddr_in *)ifa->ifa_addr)->sin_addr.s_addr == 0) { continue; } } if (sctp_is_desired_interface_type(ifa) == 0) { /* non desired type */ continue; } if (ifa->ifa_addr->sa_family == AF_INET6) { ifa6 = (struct in6_ifaddr *)ifa; ifa_flags = ifa6->ia6_flags; } else { ifa_flags = 0; } sctp_ifa = sctp_add_addr_to_vrf(vrfid, (void *)ifn, ifn->if_index, ifn->if_type, ifn->if_xname, (void *)ifa, ifa->ifa_addr, ifa_flags, 0); if (sctp_ifa) { sctp_ifa->localifa_flags &= ~SCTP_ADDR_DEFER_USE; } } IF_ADDR_UNLOCK(ifn); } +#if 0 + IFNET_RUNLOCK(); +#endif } void sctp_init_vrf_list(int vrfid) { if (vrfid > SCTP_MAX_VRF_ID) /* can't do that */ return; /* Don't care about return here */ (void)sctp_allocate_vrf(vrfid); /* * Now we need to build all the ifn's for this vrf and there * addresses */ sctp_init_ifns_for_vrf(vrfid); } void sctp_addr_change(struct ifaddr *ifa, int cmd) { struct sctp_ifa *ifap = NULL; uint32_t ifa_flags = 0; /* * BSD only has one VRF, if this changes we will need to hook in the * right things here to get the id to pass to the address managment * routine. */ if (SCTP_BASE_VAR(first_time) == 0) { /* Special test to see if my ::1 will showup with this */ SCTP_BASE_VAR(first_time) = 1; sctp_init_ifns_for_vrf(SCTP_DEFAULT_VRFID); } if ((cmd != RTM_ADD) && (cmd != RTM_DELETE)) { /* don't know what to do with this */ return; } if (ifa->ifa_addr == NULL) { return; } if ((ifa->ifa_addr->sa_family != AF_INET) && (ifa->ifa_addr->sa_family != AF_INET6)) { /* non inet/inet6 skip */ return; } if (ifa->ifa_addr->sa_family == AF_INET6) { ifa_flags = ((struct in6_ifaddr *)ifa)->ia6_flags; if (IN6_IS_ADDR_UNSPECIFIED(&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr)) { /* skip unspecifed addresses */ return; } } else { if (((struct sockaddr_in *)ifa->ifa_addr)->sin_addr.s_addr == 0) { return; } } if (sctp_is_desired_interface_type(ifa) == 0) { /* non desired type */ return; } if (cmd == RTM_ADD) { ifap = sctp_add_addr_to_vrf(SCTP_DEFAULT_VRFID, (void *)ifa->ifa_ifp, ifa->ifa_ifp->if_index, ifa->ifa_ifp->if_type, ifa->ifa_ifp->if_xname, (void *)ifa, ifa->ifa_addr, ifa_flags, 1); } else { sctp_del_addr_from_vrf(SCTP_DEFAULT_VRFID, ifa->ifa_addr, ifa->ifa_ifp->if_index, ifa->ifa_ifp->if_xname ); /* * We don't bump refcount here so when it completes the * final delete will happen. */ } } void sctp_add_or_del_interfaces(int (*pred) (struct ifnet *), int add){ struct ifnet *ifn; struct ifaddr *ifa; +#if 0 + IFNET_RLOCK(); +#endif TAILQ_FOREACH(ifn, &MODULE_GLOBAL(ifnet), if_list) { if (!(*pred) (ifn)) { continue; } TAILQ_FOREACH(ifa, &ifn->if_addrlist, ifa_list) { sctp_addr_change(ifa, add ? RTM_ADD : RTM_DELETE); } } +#if 0 + IFNET_RUNLOCK(); +#endif } struct mbuf * sctp_get_mbuf_for_msg(unsigned int space_needed, int want_header, int how, int allonebuf, int type) { struct mbuf *m = NULL; m = m_getm2(NULL, space_needed, how, type, want_header ? M_PKTHDR : 0); if (m == NULL) { /* bad, no memory */ return (m); } if (allonebuf) { int siz; if (SCTP_BUF_IS_EXTENDED(m)) { siz = SCTP_BUF_EXTEND_SIZE(m); } else { if (want_header) siz = MHLEN; else siz = MLEN; } if (siz < space_needed) { m_freem(m); return (NULL); } } if (SCTP_BUF_NEXT(m)) { sctp_m_freem(SCTP_BUF_NEXT(m)); SCTP_BUF_NEXT(m) = NULL; } #ifdef SCTP_MBUF_LOGGING if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_MBUF_LOGGING_ENABLE) { if (SCTP_BUF_IS_EXTENDED(m)) { sctp_log_mb(m, SCTP_MBUF_IALLOC); } } #endif return (m); } #ifdef SCTP_PACKET_LOGGING void sctp_packet_log(struct mbuf *m, int length) { int *lenat, thisone; void *copyto; uint32_t *tick_tock; int total_len; int grabbed_lock = 0; int value, newval, thisend, thisbegin; /* * Buffer layout. -sizeof this entry (total_len) -previous end * (value) -ticks of log (ticks) o -ip packet o -as logged - * where this started (thisbegin) x <--end points here */ total_len = SCTP_SIZE32((length + (4 * sizeof(int)))); /* Log a packet to the buffer. */ if (total_len > SCTP_PACKET_LOG_SIZE) { /* Can't log this packet I have not a buffer big enough */ return; } if (length < (int)(SCTP_MIN_V4_OVERHEAD + sizeof(struct sctp_cookie_ack_chunk))) { return; } atomic_add_int(&SCTP_BASE_VAR(packet_log_writers), 1); try_again: if (SCTP_BASE_VAR(packet_log_writers) > SCTP_PKTLOG_WRITERS_NEED_LOCK) { SCTP_IP_PKTLOG_LOCK(); grabbed_lock = 1; again_locked: value = SCTP_BASE_VAR(packet_log_end); newval = SCTP_BASE_VAR(packet_log_end) + total_len; if (newval >= SCTP_PACKET_LOG_SIZE) { /* we wrapped */ thisbegin = 0; thisend = total_len; } else { thisbegin = SCTP_BASE_VAR(packet_log_end); thisend = newval; } if (!(atomic_cmpset_int(&SCTP_BASE_VAR(packet_log_end), value, thisend))) { goto again_locked; } } else { value = SCTP_BASE_VAR(packet_log_end); newval = SCTP_BASE_VAR(packet_log_end) + total_len; if (newval >= SCTP_PACKET_LOG_SIZE) { /* we wrapped */ thisbegin = 0; thisend = total_len; } else { thisbegin = SCTP_BASE_VAR(packet_log_end); thisend = newval; } if (!(atomic_cmpset_int(&SCTP_BASE_VAR(packet_log_end), value, thisend))) { goto try_again; } } /* Sanity check */ if (thisend >= SCTP_PACKET_LOG_SIZE) { printf("Insanity stops a log thisbegin:%d thisend:%d writers:%d lock:%d end:%d\n", thisbegin, thisend, SCTP_BASE_VAR(packet_log_writers), grabbed_lock, SCTP_BASE_VAR(packet_log_end)); SCTP_BASE_VAR(packet_log_end) = 0; goto no_log; } lenat = (int *)&SCTP_BASE_VAR(packet_log_buffer)[thisbegin]; *lenat = total_len; lenat++; *lenat = value; lenat++; tick_tock = (uint32_t *) lenat; lenat++; *tick_tock = sctp_get_tick_count(); copyto = (void *)lenat; thisone = thisend - sizeof(int); lenat = (int *)&SCTP_BASE_VAR(packet_log_buffer)[thisone]; *lenat = thisbegin; if (grabbed_lock) { SCTP_IP_PKTLOG_UNLOCK(); grabbed_lock = 0; } m_copydata(m, 0, length, (caddr_t)copyto); no_log: if (grabbed_lock) { SCTP_IP_PKTLOG_UNLOCK(); } atomic_subtract_int(&SCTP_BASE_VAR(packet_log_writers), 1); } int sctp_copy_out_packet_log(uint8_t * target, int length) { /* * We wind through the packet log starting at start copying up to * length bytes out. We return the number of bytes copied. */ int tocopy, this_copy; int *lenat; int did_delay = 0; tocopy = length; if (length < (int)(2 * sizeof(int))) { /* not enough room */ return (0); } if (SCTP_PKTLOG_WRITERS_NEED_LOCK) { atomic_add_int(&SCTP_BASE_VAR(packet_log_writers), SCTP_PKTLOG_WRITERS_NEED_LOCK); again: if ((did_delay == 0) && (SCTP_BASE_VAR(packet_log_writers) != SCTP_PKTLOG_WRITERS_NEED_LOCK)) { /* * we delay here for just a moment hoping the * writer(s) that were present when we entered will * have left and we only have locking ones that will * contend with us for the lock. This does not * assure 100% access, but its good enough for a * logging facility like this. */ did_delay = 1; DELAY(10); goto again; } } SCTP_IP_PKTLOG_LOCK(); lenat = (int *)target; *lenat = SCTP_BASE_VAR(packet_log_end); lenat++; this_copy = min((length - sizeof(int)), SCTP_PACKET_LOG_SIZE); memcpy((void *)lenat, (void *)SCTP_BASE_VAR(packet_log_buffer), this_copy); if (SCTP_PKTLOG_WRITERS_NEED_LOCK) { atomic_subtract_int(&SCTP_BASE_VAR(packet_log_writers), SCTP_PKTLOG_WRITERS_NEED_LOCK); } SCTP_IP_PKTLOG_UNLOCK(); return (this_copy + sizeof(int)); } #endif Index: head/sys/netinet/sctp_os_bsd.h =================================================================== --- head/sys/netinet/sctp_os_bsd.h (revision 197325) +++ head/sys/netinet/sctp_os_bsd.h (revision 197326) @@ -1,511 +1,512 @@ /*- * Copyright (c) 2006-2007, by Cisco Systems, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * a) Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * b) Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the distribution. * * c) Neither the name of Cisco Systems, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #ifndef __sctp_os_bsd_h__ #define __sctp_os_bsd_h__ /* * includes */ #include "opt_ipsec.h" #include "opt_compat.h" #include "opt_inet6.h" #include "opt_inet.h" #include "opt_sctp.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef IPSEC #include #include #endif /* IPSEC */ #ifdef INET6 #include #ifdef IPSEC #include #endif #include #include #include #include #include #include #include #endif /* INET6 */ #include #ifndef in6pcb #define in6pcb inpcb #endif /* Declare all the malloc names for all the various mallocs */ MALLOC_DECLARE(SCTP_M_MAP); MALLOC_DECLARE(SCTP_M_STRMI); MALLOC_DECLARE(SCTP_M_STRMO); MALLOC_DECLARE(SCTP_M_ASC_ADDR); MALLOC_DECLARE(SCTP_M_ASC_IT); MALLOC_DECLARE(SCTP_M_AUTH_CL); MALLOC_DECLARE(SCTP_M_AUTH_KY); MALLOC_DECLARE(SCTP_M_AUTH_HL); MALLOC_DECLARE(SCTP_M_AUTH_IF); MALLOC_DECLARE(SCTP_M_STRESET); MALLOC_DECLARE(SCTP_M_CMSG); MALLOC_DECLARE(SCTP_M_COPYAL); MALLOC_DECLARE(SCTP_M_VRF); MALLOC_DECLARE(SCTP_M_IFA); MALLOC_DECLARE(SCTP_M_IFN); MALLOC_DECLARE(SCTP_M_TIMW); MALLOC_DECLARE(SCTP_M_MVRF); MALLOC_DECLARE(SCTP_M_ITER); MALLOC_DECLARE(SCTP_M_SOCKOPT); #if defined(SCTP_LOCAL_TRACE_BUF) #define SCTP_GET_CYCLECOUNT get_cyclecount() #define SCTP_CTR6 sctp_log_trace #else #define SCTP_CTR6 CTR6 #endif /* * Macros to expand out globals defined by various modules * to either a real global or a virtualized instance of one, * depending on whether VIMAGE is defined. */ /* then define the macro(s) that hook into the vimage macros */ -#define MODULE_GLOBAL(__SYMBOL) V_ ## __SYMBOL +#define MODULE_GLOBAL(__SYMBOL) V_##__SYMBOL -#define V_system_base_info VNET_NAME(system_base_info) +#define V_system_base_info VNET(system_base_info) #define SCTP_BASE_INFO(__m) V_system_base_info.sctppcbinfo.__m #define SCTP_BASE_STATS V_system_base_info.sctpstat +#define SCTP_BASE_STATS_SYSCTL VNET_NAME(system_base_info.sctpstat) #define SCTP_BASE_STAT(__m) V_system_base_info.sctpstat.__m -#define SCTP_BASE_SYSCTL(__m) V_system_base_info.sctpsysctl.__m +#define SCTP_BASE_SYSCTL(__m) VNET_NAME(system_base_info.sctpsysctl.__m) #define SCTP_BASE_VAR(__m) V_system_base_info.__m /* * */ #define USER_ADDR_NULL (NULL) /* FIX ME: temp */ #define SCTP_LIST_EMPTY(list) LIST_EMPTY(list) #if defined(SCTP_DEBUG) #define SCTPDBG(level, params...) \ { \ do { \ if (SCTP_BASE_SYSCTL(sctp_debug_on) & level ) { \ printf(params); \ } \ } while (0); \ } #define SCTPDBG_ADDR(level, addr) \ { \ do { \ if (SCTP_BASE_SYSCTL(sctp_debug_on) & level ) { \ sctp_print_address(addr); \ } \ } while (0); \ } #define SCTPDBG_PKT(level, iph, sh) \ { \ do { \ if (SCTP_BASE_SYSCTL(sctp_debug_on) & level) { \ sctp_print_address_pkt(iph, sh); \ } \ } while (0); \ } #else #define SCTPDBG(level, params...) #define SCTPDBG_ADDR(level, addr) #define SCTPDBG_PKT(level, iph, sh) #endif #define SCTP_PRINTF(params...) printf(params) #ifdef SCTP_LTRACE_CHUNKS #define SCTP_LTRACE_CHK(a, b, c, d) if(SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_LTRACE_CHUNK_ENABLE) SCTP_CTR6(KTR_SUBSYS, "SCTP:%d[%d]:%x-%x-%x-%x", SCTP_LOG_CHUNK_PROC, 0, a, b, c, d) #else #define SCTP_LTRACE_CHK(a, b, c, d) #endif #ifdef SCTP_LTRACE_ERRORS #define SCTP_LTRACE_ERR_RET_PKT(m, inp, stcb, net, file, err) if(SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_LTRACE_ERROR_ENABLE) \ printf("mbuf:%p inp:%p stcb:%p net:%p file:%x line:%d error:%d\n", \ m, inp, stcb, net, file, __LINE__, err); #define SCTP_LTRACE_ERR_RET(inp, stcb, net, file, err) if(SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_LTRACE_ERROR_ENABLE) \ printf("inp:%p stcb:%p net:%p file:%x line:%d error:%d\n", \ inp, stcb, net, file, __LINE__, err); #else #define SCTP_LTRACE_ERR_RET_PKT(m, inp, stcb, net, file, err) #define SCTP_LTRACE_ERR_RET(inp, stcb, net, file, err) #endif /* * Local address and interface list handling */ #define SCTP_MAX_VRF_ID 0 #define SCTP_SIZE_OF_VRF_HASH 3 #define SCTP_IFNAMSIZ IFNAMSIZ #define SCTP_DEFAULT_VRFID 0 #define SCTP_VRF_ADDR_HASH_SIZE 16 #define SCTP_VRF_IFN_HASH_SIZE 3 #define SCTP_INIT_VRF_TABLEID(vrf) #define SCTP_IFN_IS_IFT_LOOP(ifn) ((ifn)->ifn_type == IFT_LOOP) #define SCTP_ROUTE_IS_REAL_LOOP(ro) ((ro)->ro_rt && (ro)->ro_rt->rt_ifa && (ro)->ro_rt->rt_ifa->ifa_ifp && (ro)->ro_rt->rt_ifa->ifa_ifp->if_type == IFT_LOOP) /* * Access to IFN's to help with src-addr-selection */ /* This could return VOID if the index works but for BSD we provide both. */ #define SCTP_GET_IFN_VOID_FROM_ROUTE(ro) (void *)ro->ro_rt->rt_ifp #define SCTP_GET_IF_INDEX_FROM_ROUTE(ro) (ro)->ro_rt->rt_ifp->if_index #define SCTP_ROUTE_HAS_VALID_IFN(ro) ((ro)->ro_rt && (ro)->ro_rt->rt_ifp) /* * general memory allocation */ #define SCTP_MALLOC(var, type, size, name) \ do { \ var = (type)malloc(size, name, M_NOWAIT); \ } while (0) #define SCTP_FREE(var, type) free(var, type) #define SCTP_MALLOC_SONAME(var, type, size) \ do { \ var = (type)malloc(size, M_SONAME, M_WAITOK | M_ZERO); \ } while (0) #define SCTP_FREE_SONAME(var) free(var, M_SONAME) #define SCTP_PROCESS_STRUCT struct proc * /* * zone allocation functions */ #include /* SCTP_ZONE_INIT: initialize the zone */ typedef struct uma_zone *sctp_zone_t; #define UMA_ZFLAG_FULL 0x0020 #define SCTP_ZONE_INIT(zone, name, size, number) { \ zone = uma_zcreate(name, size, NULL, NULL, NULL, NULL, UMA_ALIGN_PTR,\ UMA_ZFLAG_FULL); \ uma_zone_set_max(zone, number); \ } #define SCTP_ZONE_DESTROY(zone) uma_zdestroy(zone) /* SCTP_ZONE_GET: allocate element from the zone */ #define SCTP_ZONE_GET(zone, type) \ (type *)uma_zalloc(zone, M_NOWAIT); /* SCTP_ZONE_FREE: free element from the zone */ #define SCTP_ZONE_FREE(zone, element) \ uma_zfree(zone, element); #define SCTP_HASH_INIT(size, hashmark) hashinit_flags(size, M_PCB, hashmark, HASH_NOWAIT) #define SCTP_HASH_FREE(table, hashmark) hashdestroy(table, M_PCB, hashmark) #define SCTP_M_COPYM m_copym /* * timers */ #include typedef struct callout sctp_os_timer_t; #define SCTP_OS_TIMER_INIT(tmr) callout_init(tmr, 1) #define SCTP_OS_TIMER_START callout_reset #define SCTP_OS_TIMER_STOP callout_stop #define SCTP_OS_TIMER_STOP_DRAIN callout_drain #define SCTP_OS_TIMER_PENDING callout_pending #define SCTP_OS_TIMER_ACTIVE callout_active #define SCTP_OS_TIMER_DEACTIVATE callout_deactivate #define sctp_get_tick_count() (ticks) #define SCTP_UNUSED __attribute__((unused)) /* * Functions */ /* Mbuf manipulation and access macros */ #define SCTP_BUF_LEN(m) (m->m_len) #define SCTP_BUF_NEXT(m) (m->m_next) #define SCTP_BUF_NEXT_PKT(m) (m->m_nextpkt) #define SCTP_BUF_RESV_UF(m, size) m->m_data += size #define SCTP_BUF_AT(m, size) m->m_data + size #define SCTP_BUF_IS_EXTENDED(m) (m->m_flags & M_EXT) #define SCTP_BUF_EXTEND_SIZE(m) (m->m_ext.ext_size) #define SCTP_BUF_TYPE(m) (m->m_type) #define SCTP_BUF_RECVIF(m) (m->m_pkthdr.rcvif) #define SCTP_BUF_PREPEND M_PREPEND #define SCTP_ALIGN_TO_END(m, len) if(m->m_flags & M_PKTHDR) { \ MH_ALIGN(m, len); \ } else if ((m->m_flags & M_EXT) == 0) { \ M_ALIGN(m, len); \ } /* We make it so if you have up to 4 threads * writting based on the default size of * the packet log 65 k, that would be * 4 16k packets before we would hit * a problem. */ #define SCTP_PKTLOG_WRITERS_NEED_LOCK 3 /*************************/ /* MTU */ /*************************/ #define SCTP_GATHER_MTU_FROM_IFN_INFO(ifn, ifn_index, af) ((struct ifnet *)ifn)->if_mtu #define SCTP_GATHER_MTU_FROM_ROUTE(sctp_ifa, sa, rt) ((rt != NULL) ? rt->rt_rmx.rmx_mtu : 0) #define SCTP_GATHER_MTU_FROM_INTFC(sctp_ifn) ((sctp_ifn->ifn_p != NULL) ? ((struct ifnet *)(sctp_ifn->ifn_p))->if_mtu : 0) #define SCTP_SET_MTU_OF_ROUTE(sa, rt, mtu) do { \ if (rt != NULL) \ rt->rt_rmx.rmx_mtu = mtu; \ } while(0) /* (de-)register interface event notifications */ #define SCTP_REGISTER_INTERFACE(ifhandle, af) #define SCTP_DEREGISTER_INTERFACE(ifhandle, af) /*************************/ /* These are for logging */ /*************************/ /* return the base ext data pointer */ #define SCTP_BUF_EXTEND_BASE(m) (m->m_ext.ext_buf) /* return the refcnt of the data pointer */ #define SCTP_BUF_EXTEND_REFCNT(m) (*m->m_ext.ref_cnt) /* return any buffer related flags, this is * used beyond logging for apple only. */ #define SCTP_BUF_GET_FLAGS(m) (m->m_flags) /* For BSD this just accesses the M_PKTHDR length * so it operates on an mbuf with hdr flag. Other * O/S's may have seperate packet header and mbuf * chain pointers.. thus the macro. */ #define SCTP_HEADER_TO_CHAIN(m) (m) #define SCTP_DETACH_HEADER_FROM_CHAIN(m) #define SCTP_HEADER_LEN(m) (m->m_pkthdr.len) #define SCTP_GET_HEADER_FOR_OUTPUT(o_pak) 0 #define SCTP_RELEASE_HEADER(m) #define SCTP_RELEASE_PKT(m) sctp_m_freem(m) #define SCTP_ENABLE_UDP_CSUM(m) do { \ m->m_pkthdr.csum_flags = CSUM_UDP; \ m->m_pkthdr.csum_data = offsetof(struct udphdr, uh_sum); \ } while (0) #define SCTP_GET_PKT_VRFID(m, vrf_id) ((vrf_id = SCTP_DEFAULT_VRFID) != SCTP_DEFAULT_VRFID) /* Attach the chain of data into the sendable packet. */ #define SCTP_ATTACH_CHAIN(pak, m, packet_length) do { \ pak = m; \ pak->m_pkthdr.len = packet_length; \ } while(0) /* Other m_pkthdr type things */ #define SCTP_IS_IT_BROADCAST(dst, m) ((m->m_flags & M_PKTHDR) ? in_broadcast(dst, m->m_pkthdr.rcvif) : 0) #define SCTP_IS_IT_LOOPBACK(m) ((m->m_flags & M_PKTHDR) && ((m->m_pkthdr.rcvif == NULL) || (m->m_pkthdr.rcvif->if_type == IFT_LOOP))) /* This converts any input packet header * into the chain of data holders, for BSD * its a NOP. */ /* Macro's for getting length from V6/V4 header */ #define SCTP_GET_IPV4_LENGTH(iph) (iph->ip_len) #define SCTP_GET_IPV6_LENGTH(ip6) (ntohs(ip6->ip6_plen)) /* get the v6 hop limit */ #define SCTP_GET_HLIM(inp, ro) in6_selecthlim((struct in6pcb *)&inp->ip_inp.inp, (ro ? (ro->ro_rt ? (ro->ro_rt->rt_ifp) : (NULL)) : (NULL))); /* is the endpoint v6only? */ #define SCTP_IPV6_V6ONLY(inp) (((struct inpcb *)inp)->inp_flags & IN6P_IPV6_V6ONLY) /* is the socket non-blocking? */ #define SCTP_SO_IS_NBIO(so) ((so)->so_state & SS_NBIO) #define SCTP_SET_SO_NBIO(so) ((so)->so_state |= SS_NBIO) #define SCTP_CLEAR_SO_NBIO(so) ((so)->so_state &= ~SS_NBIO) /* get the socket type */ #define SCTP_SO_TYPE(so) ((so)->so_type) /* reserve sb space for a socket */ #define SCTP_SORESERVE(so, send, recv) soreserve(so, send, recv) /* wakeup a socket */ #define SCTP_SOWAKEUP(so) wakeup(&(so)->so_timeo) /* clear the socket buffer state */ #define SCTP_SB_CLEAR(sb) \ (sb).sb_cc = 0; \ (sb).sb_mb = NULL; \ (sb).sb_mbcnt = 0; #define SCTP_SB_LIMIT_RCV(so) so->so_rcv.sb_hiwat #define SCTP_SB_LIMIT_SND(so) so->so_snd.sb_hiwat /* * routes, output, etc. */ typedef struct route sctp_route_t; typedef struct rtentry sctp_rtentry_t; #define SCTP_RTALLOC(ro, vrf_id) rtalloc_ign((struct route *)ro, 0UL) /* Future zero copy wakeup/send function */ #define SCTP_ZERO_COPY_EVENT(inp, so) /* This is re-pulse ourselves for sendbuf */ #define SCTP_ZERO_COPY_SENDQ_EVENT(inp, so) /* * IP output routines */ #define SCTP_IP_OUTPUT(result, o_pak, ro, stcb, vrf_id) \ { \ int o_flgs = 0; \ if (stcb && stcb->sctp_ep && stcb->sctp_ep->sctp_socket) { \ o_flgs = IP_RAWOUTPUT | (stcb->sctp_ep->sctp_socket->so_options & SO_DONTROUTE); \ } else { \ o_flgs = IP_RAWOUTPUT; \ } \ result = ip_output(o_pak, NULL, ro, o_flgs, 0, NULL); \ } #define SCTP_IP6_OUTPUT(result, o_pak, ro, ifp, stcb, vrf_id) \ { \ if (stcb && stcb->sctp_ep) \ result = ip6_output(o_pak, \ ((struct in6pcb *)(stcb->sctp_ep))->in6p_outputopts, \ (ro), 0, 0, ifp, NULL); \ else \ result = ip6_output(o_pak, NULL, (ro), 0, 0, ifp, NULL); \ } struct mbuf * sctp_get_mbuf_for_msg(unsigned int space_needed, int want_header, int how, int allonebuf, int type); /* * SCTP AUTH */ #define HAVE_SHA2 #define SCTP_READ_RANDOM(buf, len) read_random(buf, len) #ifdef USE_SCTP_SHA1 #include #else #include /* map standard crypto API names */ #define SHA1_Init SHA1Init #define SHA1_Update SHA1Update #define SHA1_Final(x,y) SHA1Final((caddr_t)x, y) #endif #if defined(HAVE_SHA2) #include #endif #include /* map standard crypto API names */ #define MD5_Init MD5Init #define MD5_Update MD5Update #define MD5_Final MD5Final #endif #define SCTP_DECREMENT_AND_CHECK_REFCOUNT(addr) (atomic_fetchadd_int(addr, -1) == 1) #if defined(INVARIANTS) #define SCTP_SAVE_ATOMIC_DECREMENT(addr, val) \ { \ int32_t oldval; \ oldval = atomic_fetchadd_int(addr, -val); \ if (oldval < val) { \ panic("Counter goes negative"); \ } \ } #else #define SCTP_SAVE_ATOMIC_DECREMENT(addr, val) \ { \ int32_t oldval; \ oldval = atomic_fetchadd_int(addr, -val); \ if (oldval < val) { \ *addr = 0; \ } \ } #endif Index: head/sys/netinet/sctp_pcb.c =================================================================== --- head/sys/netinet/sctp_pcb.c (revision 197325) +++ head/sys/netinet/sctp_pcb.c (revision 197326) @@ -1,6690 +1,6699 @@ /*- * Copyright (c) 2001-2008, by Cisco Systems, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * a) Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * b) Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the distribution. * * c) Neither the name of Cisco Systems, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ /* $KAME: sctp_pcb.c,v 1.38 2005/03/06 16:04:18 itojun Exp $ */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include VNET_DEFINE(struct sctp_base_info, system_base_info); /* FIX: we don't handle multiple link local scopes */ /* "scopeless" replacement IN6_ARE_ADDR_EQUAL */ #ifdef INET6 int SCTP6_ARE_ADDR_EQUAL(struct sockaddr_in6 *a, struct sockaddr_in6 *b) { struct sockaddr_in6 tmp_a, tmp_b; memcpy(&tmp_a, a, sizeof(struct sockaddr_in6)); if (sa6_embedscope(&tmp_a, MODULE_GLOBAL(ip6_use_defzone)) != 0) { return 0; } memcpy(&tmp_b, b, sizeof(struct sockaddr_in6)); if (sa6_embedscope(&tmp_b, MODULE_GLOBAL(ip6_use_defzone)) != 0) { return 0; } return (IN6_ARE_ADDR_EQUAL(&tmp_a.sin6_addr, &tmp_b.sin6_addr)); } #endif void sctp_fill_pcbinfo(struct sctp_pcbinfo *spcb) { /* * We really don't need to lock this, but I will just because it * does not hurt. */ SCTP_INP_INFO_RLOCK(); spcb->ep_count = SCTP_BASE_INFO(ipi_count_ep); spcb->asoc_count = SCTP_BASE_INFO(ipi_count_asoc); spcb->laddr_count = SCTP_BASE_INFO(ipi_count_laddr); spcb->raddr_count = SCTP_BASE_INFO(ipi_count_raddr); spcb->chk_count = SCTP_BASE_INFO(ipi_count_chunk); spcb->readq_count = SCTP_BASE_INFO(ipi_count_readq); spcb->stream_oque = SCTP_BASE_INFO(ipi_count_strmoq); spcb->free_chunks = SCTP_BASE_INFO(ipi_free_chunks); SCTP_INP_INFO_RUNLOCK(); } /* * Addresses are added to VRF's (Virtual Router's). For BSD we * have only the default VRF 0. We maintain a hash list of * VRF's. Each VRF has its own list of sctp_ifn's. Each of * these has a list of addresses. When we add a new address * to a VRF we lookup the ifn/ifn_index, if the ifn does * not exist we create it and add it to the list of IFN's * within the VRF. Once we have the sctp_ifn, we add the * address to the list. So we look something like: * * hash-vrf-table * vrf-> ifn-> ifn -> ifn * vrf | * ... +--ifa-> ifa -> ifa * vrf * * We keep these seperate lists since the SCTP subsystem will * point to these from its source address selection nets structure. * When an address is deleted it does not happen right away on * the SCTP side, it gets scheduled. What we do when a * delete happens is immediately remove the address from * the master list and decrement the refcount. As our * addip iterator works through and frees the src address * selection pointing to the sctp_ifa, eventually the refcount * will reach 0 and we will delete it. Note that it is assumed * that any locking on system level ifn/ifa is done at the * caller of these functions and these routines will only * lock the SCTP structures as they add or delete things. * * Other notes on VRF concepts. * - An endpoint can be in multiple VRF's * - An association lives within a VRF and only one VRF. * - Any incoming packet we can deduce the VRF for by * looking at the mbuf/pak inbound (for BSD its VRF=0 :D) * - Any downward send call or connect call must supply the * VRF via ancillary data or via some sort of set default * VRF socket option call (again for BSD no brainer since * the VRF is always 0). * - An endpoint may add multiple VRF's to it. * - Listening sockets can accept associations in any * of the VRF's they are in but the assoc will end up * in only one VRF (gotten from the packet or connect/send). * */ struct sctp_vrf * sctp_allocate_vrf(int vrf_id) { struct sctp_vrf *vrf = NULL; struct sctp_vrflist *bucket; /* First allocate the VRF structure */ vrf = sctp_find_vrf(vrf_id); if (vrf) { /* Already allocated */ return (vrf); } SCTP_MALLOC(vrf, struct sctp_vrf *, sizeof(struct sctp_vrf), SCTP_M_VRF); if (vrf == NULL) { /* No memory */ #ifdef INVARIANTS panic("No memory for VRF:%d", vrf_id); #endif return (NULL); } /* setup the VRF */ memset(vrf, 0, sizeof(struct sctp_vrf)); vrf->vrf_id = vrf_id; LIST_INIT(&vrf->ifnlist); vrf->total_ifa_count = 0; vrf->refcount = 0; /* now also setup table ids */ SCTP_INIT_VRF_TABLEID(vrf); /* Init the HASH of addresses */ vrf->vrf_addr_hash = SCTP_HASH_INIT(SCTP_VRF_ADDR_HASH_SIZE, &vrf->vrf_addr_hashmark); if (vrf->vrf_addr_hash == NULL) { /* No memory */ #ifdef INVARIANTS panic("No memory for VRF:%d", vrf_id); #endif SCTP_FREE(vrf, SCTP_M_VRF); return (NULL); } /* Add it to the hash table */ bucket = &SCTP_BASE_INFO(sctp_vrfhash)[(vrf_id & SCTP_BASE_INFO(hashvrfmark))]; LIST_INSERT_HEAD(bucket, vrf, next_vrf); atomic_add_int(&SCTP_BASE_INFO(ipi_count_vrfs), 1); return (vrf); } struct sctp_ifn * sctp_find_ifn(void *ifn, uint32_t ifn_index) { struct sctp_ifn *sctp_ifnp; struct sctp_ifnlist *hash_ifn_head; /* * We assume the lock is held for the addresses if thats wrong * problems could occur :-) */ hash_ifn_head = &SCTP_BASE_INFO(vrf_ifn_hash)[(ifn_index & SCTP_BASE_INFO(vrf_ifn_hashmark))]; LIST_FOREACH(sctp_ifnp, hash_ifn_head, next_bucket) { if (sctp_ifnp->ifn_index == ifn_index) { return (sctp_ifnp); } if (sctp_ifnp->ifn_p && ifn && (sctp_ifnp->ifn_p == ifn)) { return (sctp_ifnp); } } return (NULL); } struct sctp_vrf * sctp_find_vrf(uint32_t vrf_id) { struct sctp_vrflist *bucket; struct sctp_vrf *liste; bucket = &SCTP_BASE_INFO(sctp_vrfhash)[(vrf_id & SCTP_BASE_INFO(hashvrfmark))]; LIST_FOREACH(liste, bucket, next_vrf) { if (vrf_id == liste->vrf_id) { return (liste); } } return (NULL); } void sctp_free_vrf(struct sctp_vrf *vrf) { if (SCTP_DECREMENT_AND_CHECK_REFCOUNT(&vrf->refcount)) { if (vrf->vrf_addr_hash) { SCTP_HASH_FREE(vrf->vrf_addr_hash, vrf->vrf_addr_hashmark); vrf->vrf_addr_hash = NULL; } /* We zero'd the count */ LIST_REMOVE(vrf, next_vrf); SCTP_FREE(vrf, SCTP_M_VRF); atomic_subtract_int(&SCTP_BASE_INFO(ipi_count_vrfs), 1); } } void sctp_free_ifn(struct sctp_ifn *sctp_ifnp) { if (SCTP_DECREMENT_AND_CHECK_REFCOUNT(&sctp_ifnp->refcount)) { /* We zero'd the count */ if (sctp_ifnp->vrf) { sctp_free_vrf(sctp_ifnp->vrf); } SCTP_FREE(sctp_ifnp, SCTP_M_IFN); atomic_subtract_int(&SCTP_BASE_INFO(ipi_count_ifns), 1); } } void sctp_update_ifn_mtu(uint32_t ifn_index, uint32_t mtu) { struct sctp_ifn *sctp_ifnp; sctp_ifnp = sctp_find_ifn((void *)NULL, ifn_index); if (sctp_ifnp != NULL) { sctp_ifnp->ifn_mtu = mtu; } } void sctp_free_ifa(struct sctp_ifa *sctp_ifap) { if (SCTP_DECREMENT_AND_CHECK_REFCOUNT(&sctp_ifap->refcount)) { /* We zero'd the count */ if (sctp_ifap->ifn_p) { sctp_free_ifn(sctp_ifap->ifn_p); } SCTP_FREE(sctp_ifap, SCTP_M_IFA); atomic_subtract_int(&SCTP_BASE_INFO(ipi_count_ifas), 1); } } static void sctp_delete_ifn(struct sctp_ifn *sctp_ifnp, int hold_addr_lock) { struct sctp_ifn *found; found = sctp_find_ifn(sctp_ifnp->ifn_p, sctp_ifnp->ifn_index); if (found == NULL) { /* Not in the list.. sorry */ return; } if (hold_addr_lock == 0) SCTP_IPI_ADDR_WLOCK(); LIST_REMOVE(sctp_ifnp, next_bucket); LIST_REMOVE(sctp_ifnp, next_ifn); SCTP_DEREGISTER_INTERFACE(sctp_ifnp->ifn_index, sctp_ifnp->registered_af); if (hold_addr_lock == 0) SCTP_IPI_ADDR_WUNLOCK(); /* Take away the reference, and possibly free it */ sctp_free_ifn(sctp_ifnp); } void sctp_mark_ifa_addr_down(uint32_t vrf_id, struct sockaddr *addr, const char *if_name, uint32_t ifn_index) { struct sctp_vrf *vrf; struct sctp_ifa *sctp_ifap = NULL; SCTP_IPI_ADDR_RLOCK(); vrf = sctp_find_vrf(vrf_id); if (vrf == NULL) { SCTPDBG(SCTP_DEBUG_PCB4, "Can't find vrf_id 0x%x\n", vrf_id); goto out; } sctp_ifap = sctp_find_ifa_by_addr(addr, vrf->vrf_id, SCTP_ADDR_LOCKED); if (sctp_ifap == NULL) { SCTPDBG(SCTP_DEBUG_PCB4, "Can't find sctp_ifap for address\n"); goto out; } if (sctp_ifap->ifn_p == NULL) { SCTPDBG(SCTP_DEBUG_PCB4, "IFA has no IFN - can't mark unuseable\n"); goto out; } if (if_name) { int len1, len2; len1 = strlen(if_name); len2 = strlen(sctp_ifap->ifn_p->ifn_name); if (len1 != len2) { SCTPDBG(SCTP_DEBUG_PCB4, "IFN of ifa names different lenght %d vs %d - ignored\n", len1, len2); goto out; } if (strncmp(if_name, sctp_ifap->ifn_p->ifn_name, len1) != 0) { SCTPDBG(SCTP_DEBUG_PCB4, "IFN %s of IFA not the same as %s\n", sctp_ifap->ifn_p->ifn_name, if_name); goto out; } } else { if (sctp_ifap->ifn_p->ifn_index != ifn_index) { SCTPDBG(SCTP_DEBUG_PCB4, "IFA owned by ifn_index:%d down command for ifn_index:%d - ignored\n", sctp_ifap->ifn_p->ifn_index, ifn_index); goto out; } } sctp_ifap->localifa_flags &= (~SCTP_ADDR_VALID); sctp_ifap->localifa_flags |= SCTP_ADDR_IFA_UNUSEABLE; out: SCTP_IPI_ADDR_RUNLOCK(); } void sctp_mark_ifa_addr_up(uint32_t vrf_id, struct sockaddr *addr, const char *if_name, uint32_t ifn_index) { struct sctp_vrf *vrf; struct sctp_ifa *sctp_ifap = NULL; SCTP_IPI_ADDR_RLOCK(); vrf = sctp_find_vrf(vrf_id); if (vrf == NULL) { SCTPDBG(SCTP_DEBUG_PCB4, "Can't find vrf_id 0x%x\n", vrf_id); goto out; } sctp_ifap = sctp_find_ifa_by_addr(addr, vrf->vrf_id, SCTP_ADDR_LOCKED); if (sctp_ifap == NULL) { SCTPDBG(SCTP_DEBUG_PCB4, "Can't find sctp_ifap for address\n"); goto out; } if (sctp_ifap->ifn_p == NULL) { SCTPDBG(SCTP_DEBUG_PCB4, "IFA has no IFN - can't mark unuseable\n"); goto out; } if (if_name) { int len1, len2; len1 = strlen(if_name); len2 = strlen(sctp_ifap->ifn_p->ifn_name); if (len1 != len2) { SCTPDBG(SCTP_DEBUG_PCB4, "IFN of ifa names different lenght %d vs %d - ignored\n", len1, len2); goto out; } if (strncmp(if_name, sctp_ifap->ifn_p->ifn_name, len1) != 0) { SCTPDBG(SCTP_DEBUG_PCB4, "IFN %s of IFA not the same as %s\n", sctp_ifap->ifn_p->ifn_name, if_name); goto out; } } else { if (sctp_ifap->ifn_p->ifn_index != ifn_index) { SCTPDBG(SCTP_DEBUG_PCB4, "IFA owned by ifn_index:%d down command for ifn_index:%d - ignored\n", sctp_ifap->ifn_p->ifn_index, ifn_index); goto out; } } sctp_ifap->localifa_flags &= (~SCTP_ADDR_IFA_UNUSEABLE); sctp_ifap->localifa_flags |= SCTP_ADDR_VALID; out: SCTP_IPI_ADDR_RUNLOCK(); } /*- * Add an ifa to an ifn. * Register the interface as necessary. * NOTE: ADDR write lock MUST be held. */ static void sctp_add_ifa_to_ifn(struct sctp_ifn *sctp_ifnp, struct sctp_ifa *sctp_ifap) { int ifa_af; LIST_INSERT_HEAD(&sctp_ifnp->ifalist, sctp_ifap, next_ifa); sctp_ifap->ifn_p = sctp_ifnp; atomic_add_int(&sctp_ifap->ifn_p->refcount, 1); /* update address counts */ sctp_ifnp->ifa_count++; ifa_af = sctp_ifap->address.sa.sa_family; if (ifa_af == AF_INET) sctp_ifnp->num_v4++; else sctp_ifnp->num_v6++; if (sctp_ifnp->ifa_count == 1) { /* register the new interface */ SCTP_REGISTER_INTERFACE(sctp_ifnp->ifn_index, ifa_af); sctp_ifnp->registered_af = ifa_af; } } /*- * Remove an ifa from its ifn. * If no more addresses exist, remove the ifn too. Otherwise, re-register * the interface based on the remaining address families left. * NOTE: ADDR write lock MUST be held. */ static void sctp_remove_ifa_from_ifn(struct sctp_ifa *sctp_ifap) { uint32_t ifn_index; LIST_REMOVE(sctp_ifap, next_ifa); if (sctp_ifap->ifn_p) { /* update address counts */ sctp_ifap->ifn_p->ifa_count--; if (sctp_ifap->address.sa.sa_family == AF_INET6) sctp_ifap->ifn_p->num_v6--; else if (sctp_ifap->address.sa.sa_family == AF_INET) sctp_ifap->ifn_p->num_v4--; ifn_index = sctp_ifap->ifn_p->ifn_index; if (SCTP_LIST_EMPTY(&sctp_ifap->ifn_p->ifalist)) { /* remove the ifn, possibly freeing it */ sctp_delete_ifn(sctp_ifap->ifn_p, SCTP_ADDR_LOCKED); } else { /* re-register address family type, if needed */ if ((sctp_ifap->ifn_p->num_v6 == 0) && (sctp_ifap->ifn_p->registered_af == AF_INET6)) { SCTP_DEREGISTER_INTERFACE(ifn_index, AF_INET6); SCTP_REGISTER_INTERFACE(ifn_index, AF_INET); sctp_ifap->ifn_p->registered_af = AF_INET; } else if ((sctp_ifap->ifn_p->num_v4 == 0) && (sctp_ifap->ifn_p->registered_af == AF_INET)) { SCTP_DEREGISTER_INTERFACE(ifn_index, AF_INET); SCTP_REGISTER_INTERFACE(ifn_index, AF_INET6); sctp_ifap->ifn_p->registered_af = AF_INET6; } /* free the ifn refcount */ sctp_free_ifn(sctp_ifap->ifn_p); } sctp_ifap->ifn_p = NULL; } } struct sctp_ifa * sctp_add_addr_to_vrf(uint32_t vrf_id, void *ifn, uint32_t ifn_index, uint32_t ifn_type, const char *if_name, void *ifa, struct sockaddr *addr, uint32_t ifa_flags, int dynamic_add) { struct sctp_vrf *vrf; struct sctp_ifn *sctp_ifnp = NULL; struct sctp_ifa *sctp_ifap = NULL; struct sctp_ifalist *hash_addr_head; struct sctp_ifnlist *hash_ifn_head; uint32_t hash_of_addr; int new_ifn_af = 0; #ifdef SCTP_DEBUG SCTPDBG(SCTP_DEBUG_PCB4, "vrf_id 0x%x: adding address: ", vrf_id); SCTPDBG_ADDR(SCTP_DEBUG_PCB4, addr); #endif SCTP_IPI_ADDR_WLOCK(); sctp_ifnp = sctp_find_ifn(ifn, ifn_index); if (sctp_ifnp) { vrf = sctp_ifnp->vrf; } else { vrf = sctp_find_vrf(vrf_id); if (vrf == NULL) { vrf = sctp_allocate_vrf(vrf_id); if (vrf == NULL) { SCTP_IPI_ADDR_WUNLOCK(); return (NULL); } } } if (sctp_ifnp == NULL) { /* * build one and add it, can't hold lock until after malloc * done though. */ SCTP_IPI_ADDR_WUNLOCK(); SCTP_MALLOC(sctp_ifnp, struct sctp_ifn *, sizeof(struct sctp_ifn), SCTP_M_IFN); if (sctp_ifnp == NULL) { #ifdef INVARIANTS panic("No memory for IFN:%u", sctp_ifnp->ifn_index); #endif return (NULL); } memset(sctp_ifnp, 0, sizeof(struct sctp_ifn)); sctp_ifnp->ifn_index = ifn_index; sctp_ifnp->ifn_p = ifn; sctp_ifnp->ifn_type = ifn_type; sctp_ifnp->refcount = 0; sctp_ifnp->vrf = vrf; atomic_add_int(&vrf->refcount, 1); sctp_ifnp->ifn_mtu = SCTP_GATHER_MTU_FROM_IFN_INFO(ifn, ifn_index, addr->sa_family); if (if_name != NULL) { memcpy(sctp_ifnp->ifn_name, if_name, SCTP_IFNAMSIZ); } else { memcpy(sctp_ifnp->ifn_name, "unknown", min(7, SCTP_IFNAMSIZ)); } hash_ifn_head = &SCTP_BASE_INFO(vrf_ifn_hash)[(ifn_index & SCTP_BASE_INFO(vrf_ifn_hashmark))]; LIST_INIT(&sctp_ifnp->ifalist); SCTP_IPI_ADDR_WLOCK(); LIST_INSERT_HEAD(hash_ifn_head, sctp_ifnp, next_bucket); LIST_INSERT_HEAD(&vrf->ifnlist, sctp_ifnp, next_ifn); atomic_add_int(&SCTP_BASE_INFO(ipi_count_ifns), 1); new_ifn_af = 1; } sctp_ifap = sctp_find_ifa_by_addr(addr, vrf->vrf_id, SCTP_ADDR_LOCKED); if (sctp_ifap) { /* Hmm, it already exists? */ if ((sctp_ifap->ifn_p) && (sctp_ifap->ifn_p->ifn_index == ifn_index)) { SCTPDBG(SCTP_DEBUG_PCB4, "Using existing ifn %s (0x%x) for ifa %p\n", sctp_ifap->ifn_p->ifn_name, ifn_index, sctp_ifap); if (new_ifn_af) { /* Remove the created one that we don't want */ sctp_delete_ifn(sctp_ifnp, SCTP_ADDR_LOCKED); } if (sctp_ifap->localifa_flags & SCTP_BEING_DELETED) { /* easy to solve, just switch back to active */ SCTPDBG(SCTP_DEBUG_PCB4, "Clearing deleted ifa flag\n"); sctp_ifap->localifa_flags = SCTP_ADDR_VALID; sctp_ifap->ifn_p = sctp_ifnp; atomic_add_int(&sctp_ifap->ifn_p->refcount, 1); } exit_stage_left: SCTP_IPI_ADDR_WUNLOCK(); return (sctp_ifap); } else { if (sctp_ifap->ifn_p) { /* * The last IFN gets the address, removee * the old one */ SCTPDBG(SCTP_DEBUG_PCB4, "Moving ifa %p from %s (0x%x) to %s (0x%x)\n", sctp_ifap, sctp_ifap->ifn_p->ifn_name, sctp_ifap->ifn_p->ifn_index, if_name, ifn_index); /* remove the address from the old ifn */ sctp_remove_ifa_from_ifn(sctp_ifap); /* move the address over to the new ifn */ sctp_add_ifa_to_ifn(sctp_ifnp, sctp_ifap); goto exit_stage_left; } else { /* repair ifnp which was NULL ? */ sctp_ifap->localifa_flags = SCTP_ADDR_VALID; SCTPDBG(SCTP_DEBUG_PCB4, "Repairing ifn %p for ifa %p\n", sctp_ifnp, sctp_ifap); sctp_add_ifa_to_ifn(sctp_ifnp, sctp_ifap); } goto exit_stage_left; } } SCTP_IPI_ADDR_WUNLOCK(); SCTP_MALLOC(sctp_ifap, struct sctp_ifa *, sizeof(struct sctp_ifa), SCTP_M_IFA); if (sctp_ifap == NULL) { #ifdef INVARIANTS panic("No memory for IFA"); #endif return (NULL); } memset(sctp_ifap, 0, sizeof(struct sctp_ifa)); sctp_ifap->ifn_p = sctp_ifnp; atomic_add_int(&sctp_ifnp->refcount, 1); sctp_ifap->vrf_id = vrf_id; sctp_ifap->ifa = ifa; memcpy(&sctp_ifap->address, addr, addr->sa_len); sctp_ifap->localifa_flags = SCTP_ADDR_VALID | SCTP_ADDR_DEFER_USE; sctp_ifap->flags = ifa_flags; /* Set scope */ switch (sctp_ifap->address.sa.sa_family) { case AF_INET: { struct sockaddr_in *sin; sin = (struct sockaddr_in *)&sctp_ifap->address.sin; if (SCTP_IFN_IS_IFT_LOOP(sctp_ifap->ifn_p) || (IN4_ISLOOPBACK_ADDRESS(&sin->sin_addr))) { sctp_ifap->src_is_loop = 1; } if ((IN4_ISPRIVATE_ADDRESS(&sin->sin_addr))) { sctp_ifap->src_is_priv = 1; } sctp_ifnp->num_v4++; if (new_ifn_af) new_ifn_af = AF_INET; break; } #ifdef INET6 case AF_INET6: { /* ok to use deprecated addresses? */ struct sockaddr_in6 *sin6; sin6 = (struct sockaddr_in6 *)&sctp_ifap->address.sin6; if (SCTP_IFN_IS_IFT_LOOP(sctp_ifap->ifn_p) || (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr))) { sctp_ifap->src_is_loop = 1; } if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) { sctp_ifap->src_is_priv = 1; } sctp_ifnp->num_v6++; if (new_ifn_af) new_ifn_af = AF_INET6; break; } #endif default: new_ifn_af = 0; break; } hash_of_addr = sctp_get_ifa_hash_val(&sctp_ifap->address.sa); if ((sctp_ifap->src_is_priv == 0) && (sctp_ifap->src_is_loop == 0)) { sctp_ifap->src_is_glob = 1; } SCTP_IPI_ADDR_WLOCK(); hash_addr_head = &vrf->vrf_addr_hash[(hash_of_addr & vrf->vrf_addr_hashmark)]; LIST_INSERT_HEAD(hash_addr_head, sctp_ifap, next_bucket); sctp_ifap->refcount = 1; LIST_INSERT_HEAD(&sctp_ifnp->ifalist, sctp_ifap, next_ifa); sctp_ifnp->ifa_count++; vrf->total_ifa_count++; atomic_add_int(&SCTP_BASE_INFO(ipi_count_ifas), 1); if (new_ifn_af) { SCTP_REGISTER_INTERFACE(ifn_index, new_ifn_af); sctp_ifnp->registered_af = new_ifn_af; } SCTP_IPI_ADDR_WUNLOCK(); if (dynamic_add) { /* * Bump up the refcount so that when the timer completes it * will drop back down. */ struct sctp_laddr *wi; atomic_add_int(&sctp_ifap->refcount, 1); wi = SCTP_ZONE_GET(SCTP_BASE_INFO(ipi_zone_laddr), struct sctp_laddr); if (wi == NULL) { /* * Gak, what can we do? We have lost an address * change can you say HOSED? */ SCTPDBG(SCTP_DEBUG_PCB4, "Lost an address change?\n"); /* Opps, must decrement the count */ sctp_del_addr_from_vrf(vrf_id, addr, ifn_index, if_name); return (NULL); } SCTP_INCR_LADDR_COUNT(); bzero(wi, sizeof(*wi)); (void)SCTP_GETTIME_TIMEVAL(&wi->start_time); wi->ifa = sctp_ifap; wi->action = SCTP_ADD_IP_ADDRESS; SCTP_IPI_ITERATOR_WQ_LOCK(); /* * Should this really be a tailq? As it is we will process * the newest first :-0 */ LIST_INSERT_HEAD(&SCTP_BASE_INFO(addr_wq), wi, sctp_nxt_addr); SCTP_IPI_ITERATOR_WQ_UNLOCK(); sctp_timer_start(SCTP_TIMER_TYPE_ADDR_WQ, (struct sctp_inpcb *)NULL, (struct sctp_tcb *)NULL, (struct sctp_nets *)NULL); } else { /* it's ready for use */ sctp_ifap->localifa_flags &= ~SCTP_ADDR_DEFER_USE; } return (sctp_ifap); } void sctp_del_addr_from_vrf(uint32_t vrf_id, struct sockaddr *addr, uint32_t ifn_index, const char *if_name) { struct sctp_vrf *vrf; struct sctp_ifa *sctp_ifap = NULL; SCTP_IPI_ADDR_WLOCK(); vrf = sctp_find_vrf(vrf_id); if (vrf == NULL) { SCTPDBG(SCTP_DEBUG_PCB4, "Can't find vrf_id 0x%x\n", vrf_id); goto out_now; } #ifdef SCTP_DEBUG SCTPDBG(SCTP_DEBUG_PCB4, "vrf_id 0x%x: deleting address:", vrf_id); SCTPDBG_ADDR(SCTP_DEBUG_PCB4, addr); #endif sctp_ifap = sctp_find_ifa_by_addr(addr, vrf->vrf_id, SCTP_ADDR_LOCKED); if (sctp_ifap) { /* Validate the delete */ if (sctp_ifap->ifn_p) { int valid = 0; /*- * The name has priority over the ifn_index * if its given. We do this especially for * panda who might recycle indexes fast. */ if (if_name) { int len1, len2; len1 = min(SCTP_IFNAMSIZ, strlen(if_name)); len2 = min(SCTP_IFNAMSIZ, strlen(sctp_ifap->ifn_p->ifn_name)); if (len1 && len2 && (len1 == len2)) { /* we can compare them */ if (strncmp(if_name, sctp_ifap->ifn_p->ifn_name, len1) == 0) { /* * They match its a correct * delete */ valid = 1; } } } if (!valid) { /* last ditch check ifn_index */ if (ifn_index == sctp_ifap->ifn_p->ifn_index) { valid = 1; } } if (!valid) { SCTPDBG(SCTP_DEBUG_PCB4, "ifn:%d ifname:%s does not match addresses\n", ifn_index, ((if_name == NULL) ? "NULL" : if_name)); SCTPDBG(SCTP_DEBUG_PCB4, "ifn:%d ifname:%s - ignoring delete\n", sctp_ifap->ifn_p->ifn_index, sctp_ifap->ifn_p->ifn_name); SCTP_IPI_ADDR_WUNLOCK(); return; } } SCTPDBG(SCTP_DEBUG_PCB4, "Deleting ifa %p\n", sctp_ifap); sctp_ifap->localifa_flags &= SCTP_ADDR_VALID; sctp_ifap->localifa_flags |= SCTP_BEING_DELETED; vrf->total_ifa_count--; LIST_REMOVE(sctp_ifap, next_bucket); sctp_remove_ifa_from_ifn(sctp_ifap); } #ifdef SCTP_DEBUG else { SCTPDBG(SCTP_DEBUG_PCB4, "Del Addr-ifn:%d Could not find address:", ifn_index); SCTPDBG_ADDR(SCTP_DEBUG_PCB1, addr); } #endif out_now: SCTP_IPI_ADDR_WUNLOCK(); if (sctp_ifap) { struct sctp_laddr *wi; wi = SCTP_ZONE_GET(SCTP_BASE_INFO(ipi_zone_laddr), struct sctp_laddr); if (wi == NULL) { /* * Gak, what can we do? We have lost an address * change can you say HOSED? */ SCTPDBG(SCTP_DEBUG_PCB4, "Lost an address change?\n"); /* Oops, must decrement the count */ sctp_free_ifa(sctp_ifap); return; } SCTP_INCR_LADDR_COUNT(); bzero(wi, sizeof(*wi)); (void)SCTP_GETTIME_TIMEVAL(&wi->start_time); wi->ifa = sctp_ifap; wi->action = SCTP_DEL_IP_ADDRESS; SCTP_IPI_ITERATOR_WQ_LOCK(); /* * Should this really be a tailq? As it is we will process * the newest first :-0 */ LIST_INSERT_HEAD(&SCTP_BASE_INFO(addr_wq), wi, sctp_nxt_addr); SCTP_IPI_ITERATOR_WQ_UNLOCK(); sctp_timer_start(SCTP_TIMER_TYPE_ADDR_WQ, (struct sctp_inpcb *)NULL, (struct sctp_tcb *)NULL, (struct sctp_nets *)NULL); } return; } static struct sctp_tcb * sctp_tcb_special_locate(struct sctp_inpcb **inp_p, struct sockaddr *from, struct sockaddr *to, struct sctp_nets **netp, uint32_t vrf_id) { /**** ASSUMES THE CALLER holds the INP_INFO_RLOCK */ /* * If we support the TCP model, then we must now dig through to see * if we can find our endpoint in the list of tcp ep's. */ uint16_t lport, rport; struct sctppcbhead *ephead; struct sctp_inpcb *inp; struct sctp_laddr *laddr; struct sctp_tcb *stcb; struct sctp_nets *net; if ((to == NULL) || (from == NULL)) { return (NULL); } if (to->sa_family == AF_INET && from->sa_family == AF_INET) { lport = ((struct sockaddr_in *)to)->sin_port; rport = ((struct sockaddr_in *)from)->sin_port; } else if (to->sa_family == AF_INET6 && from->sa_family == AF_INET6) { lport = ((struct sockaddr_in6 *)to)->sin6_port; rport = ((struct sockaddr_in6 *)from)->sin6_port; } else { return NULL; } ephead = &SCTP_BASE_INFO(sctp_tcpephash)[SCTP_PCBHASH_ALLADDR((lport), SCTP_BASE_INFO(hashtcpmark))]; /* * Ok now for each of the guys in this bucket we must look and see: * - Does the remote port match. - Does there single association's * addresses match this address (to). If so we update p_ep to point * to this ep and return the tcb from it. */ LIST_FOREACH(inp, ephead, sctp_hash) { SCTP_INP_RLOCK(inp); if (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_ALLGONE) { SCTP_INP_RUNLOCK(inp); continue; } if (lport != inp->sctp_lport) { SCTP_INP_RUNLOCK(inp); continue; } if (inp->def_vrf_id != vrf_id) { SCTP_INP_RUNLOCK(inp); continue; } /* check to see if the ep has one of the addresses */ if ((inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) == 0) { /* We are NOT bound all, so look further */ int match = 0; LIST_FOREACH(laddr, &inp->sctp_addr_list, sctp_nxt_addr) { if (laddr->ifa == NULL) { SCTPDBG(SCTP_DEBUG_PCB1, "%s: NULL ifa\n", __FUNCTION__); continue; } if (laddr->ifa->localifa_flags & SCTP_BEING_DELETED) { SCTPDBG(SCTP_DEBUG_PCB1, "ifa being deleted\n"); continue; } if (laddr->ifa->address.sa.sa_family == to->sa_family) { /* see if it matches */ struct sockaddr_in *intf_addr, *sin; intf_addr = &laddr->ifa->address.sin; sin = (struct sockaddr_in *)to; if (from->sa_family == AF_INET) { if (sin->sin_addr.s_addr == intf_addr->sin_addr.s_addr) { match = 1; break; } } #ifdef INET6 if (from->sa_family == AF_INET6) { struct sockaddr_in6 *intf_addr6; struct sockaddr_in6 *sin6; sin6 = (struct sockaddr_in6 *) to; intf_addr6 = &laddr->ifa->address.sin6; if (SCTP6_ARE_ADDR_EQUAL(sin6, intf_addr6)) { match = 1; break; } } #endif } } if (match == 0) { /* This endpoint does not have this address */ SCTP_INP_RUNLOCK(inp); continue; } } /* * Ok if we hit here the ep has the address, does it hold * the tcb? */ stcb = LIST_FIRST(&inp->sctp_asoc_list); if (stcb == NULL) { SCTP_INP_RUNLOCK(inp); continue; } SCTP_TCB_LOCK(stcb); if (stcb->rport != rport) { /* remote port does not match. */ SCTP_TCB_UNLOCK(stcb); SCTP_INP_RUNLOCK(inp); continue; } if (stcb->asoc.state & SCTP_STATE_ABOUT_TO_BE_FREED) { SCTP_TCB_UNLOCK(stcb); SCTP_INP_RUNLOCK(inp); continue; } /* Does this TCB have a matching address? */ TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) { if (net->ro._l_addr.sa.sa_family != from->sa_family) { /* not the same family, can't be a match */ continue; } switch (from->sa_family) { case AF_INET: { struct sockaddr_in *sin, *rsin; sin = (struct sockaddr_in *)&net->ro._l_addr; rsin = (struct sockaddr_in *)from; if (sin->sin_addr.s_addr == rsin->sin_addr.s_addr) { /* found it */ if (netp != NULL) { *netp = net; } /* * Update the endpoint * pointer */ *inp_p = inp; SCTP_INP_RUNLOCK(inp); return (stcb); } break; } #ifdef INET6 case AF_INET6: { struct sockaddr_in6 *sin6, *rsin6; sin6 = (struct sockaddr_in6 *)&net->ro._l_addr; rsin6 = (struct sockaddr_in6 *)from; if (SCTP6_ARE_ADDR_EQUAL(sin6, rsin6)) { /* found it */ if (netp != NULL) { *netp = net; } /* * Update the endpoint * pointer */ *inp_p = inp; SCTP_INP_RUNLOCK(inp); return (stcb); } break; } #endif default: /* TSNH */ break; } } SCTP_TCB_UNLOCK(stcb); SCTP_INP_RUNLOCK(inp); } return (NULL); } /* * rules for use * * 1) If I return a NULL you must decrement any INP ref cnt. 2) If I find an * stcb, both will be locked (locked_tcb and stcb) but decrement will be done * (if locked == NULL). 3) Decrement happens on return ONLY if locked == * NULL. */ struct sctp_tcb * sctp_findassociation_ep_addr(struct sctp_inpcb **inp_p, struct sockaddr *remote, struct sctp_nets **netp, struct sockaddr *local, struct sctp_tcb *locked_tcb) { struct sctpasochead *head; struct sctp_inpcb *inp; struct sctp_tcb *stcb = NULL; struct sctp_nets *net; uint16_t rport; inp = *inp_p; if (remote->sa_family == AF_INET) { rport = (((struct sockaddr_in *)remote)->sin_port); } else if (remote->sa_family == AF_INET6) { rport = (((struct sockaddr_in6 *)remote)->sin6_port); } else { return (NULL); } if (locked_tcb) { /* * UN-lock so we can do proper locking here this occurs when * called from load_addresses_from_init. */ atomic_add_int(&locked_tcb->asoc.refcnt, 1); SCTP_TCB_UNLOCK(locked_tcb); } SCTP_INP_INFO_RLOCK(); if (inp->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) { /*- * Now either this guy is our listener or it's the * connector. If it is the one that issued the connect, then * it's only chance is to be the first TCB in the list. If * it is the acceptor, then do the special_lookup to hash * and find the real inp. */ if ((inp->sctp_socket) && (inp->sctp_socket->so_qlimit)) { /* to is peer addr, from is my addr */ stcb = sctp_tcb_special_locate(inp_p, remote, local, netp, inp->def_vrf_id); if ((stcb != NULL) && (locked_tcb == NULL)) { /* we have a locked tcb, lower refcount */ SCTP_INP_DECR_REF(inp); } if ((locked_tcb != NULL) && (locked_tcb != stcb)) { SCTP_INP_RLOCK(locked_tcb->sctp_ep); SCTP_TCB_LOCK(locked_tcb); atomic_subtract_int(&locked_tcb->asoc.refcnt, 1); SCTP_INP_RUNLOCK(locked_tcb->sctp_ep); } SCTP_INP_INFO_RUNLOCK(); return (stcb); } else { SCTP_INP_WLOCK(inp); if (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_ALLGONE) { goto null_return; } stcb = LIST_FIRST(&inp->sctp_asoc_list); if (stcb == NULL) { goto null_return; } SCTP_TCB_LOCK(stcb); if (stcb->rport != rport) { /* remote port does not match. */ SCTP_TCB_UNLOCK(stcb); goto null_return; } if (stcb->asoc.state & SCTP_STATE_ABOUT_TO_BE_FREED) { SCTP_TCB_UNLOCK(stcb); goto null_return; } /* now look at the list of remote addresses */ TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) { #ifdef INVARIANTS if (net == (TAILQ_NEXT(net, sctp_next))) { panic("Corrupt net list"); } #endif if (net->ro._l_addr.sa.sa_family != remote->sa_family) { /* not the same family */ continue; } switch (remote->sa_family) { case AF_INET: { struct sockaddr_in *sin, *rsin; sin = (struct sockaddr_in *) &net->ro._l_addr; rsin = (struct sockaddr_in *)remote; if (sin->sin_addr.s_addr == rsin->sin_addr.s_addr) { /* found it */ if (netp != NULL) { *netp = net; } if (locked_tcb == NULL) { SCTP_INP_DECR_REF(inp); } else if (locked_tcb != stcb) { SCTP_TCB_LOCK(locked_tcb); } if (locked_tcb) { atomic_subtract_int(&locked_tcb->asoc.refcnt, 1); } SCTP_INP_WUNLOCK(inp); SCTP_INP_INFO_RUNLOCK(); return (stcb); } break; } #ifdef INET6 case AF_INET6: { struct sockaddr_in6 *sin6, *rsin6; sin6 = (struct sockaddr_in6 *)&net->ro._l_addr; rsin6 = (struct sockaddr_in6 *)remote; if (SCTP6_ARE_ADDR_EQUAL(sin6, rsin6)) { /* found it */ if (netp != NULL) { *netp = net; } if (locked_tcb == NULL) { SCTP_INP_DECR_REF(inp); } else if (locked_tcb != stcb) { SCTP_TCB_LOCK(locked_tcb); } if (locked_tcb) { atomic_subtract_int(&locked_tcb->asoc.refcnt, 1); } SCTP_INP_WUNLOCK(inp); SCTP_INP_INFO_RUNLOCK(); return (stcb); } break; } #endif default: /* TSNH */ break; } } SCTP_TCB_UNLOCK(stcb); } } else { SCTP_INP_WLOCK(inp); if (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_ALLGONE) { goto null_return; } head = &inp->sctp_tcbhash[SCTP_PCBHASH_ALLADDR(rport, inp->sctp_hashmark)]; if (head == NULL) { goto null_return; } LIST_FOREACH(stcb, head, sctp_tcbhash) { if (stcb->rport != rport) { /* remote port does not match */ continue; } SCTP_TCB_LOCK(stcb); if (stcb->asoc.state & SCTP_STATE_ABOUT_TO_BE_FREED) { SCTP_TCB_UNLOCK(stcb); continue; } /* now look at the list of remote addresses */ TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) { #ifdef INVARIANTS if (net == (TAILQ_NEXT(net, sctp_next))) { panic("Corrupt net list"); } #endif if (net->ro._l_addr.sa.sa_family != remote->sa_family) { /* not the same family */ continue; } switch (remote->sa_family) { case AF_INET: { struct sockaddr_in *sin, *rsin; sin = (struct sockaddr_in *) &net->ro._l_addr; rsin = (struct sockaddr_in *)remote; if (sin->sin_addr.s_addr == rsin->sin_addr.s_addr) { /* found it */ if (netp != NULL) { *netp = net; } if (locked_tcb == NULL) { SCTP_INP_DECR_REF(inp); } else if (locked_tcb != stcb) { SCTP_TCB_LOCK(locked_tcb); } if (locked_tcb) { atomic_subtract_int(&locked_tcb->asoc.refcnt, 1); } SCTP_INP_WUNLOCK(inp); SCTP_INP_INFO_RUNLOCK(); return (stcb); } break; } #ifdef INET6 case AF_INET6: { struct sockaddr_in6 *sin6, *rsin6; sin6 = (struct sockaddr_in6 *) &net->ro._l_addr; rsin6 = (struct sockaddr_in6 *)remote; if (SCTP6_ARE_ADDR_EQUAL(sin6, rsin6)) { /* found it */ if (netp != NULL) { *netp = net; } if (locked_tcb == NULL) { SCTP_INP_DECR_REF(inp); } else if (locked_tcb != stcb) { SCTP_TCB_LOCK(locked_tcb); } if (locked_tcb) { atomic_subtract_int(&locked_tcb->asoc.refcnt, 1); } SCTP_INP_WUNLOCK(inp); SCTP_INP_INFO_RUNLOCK(); return (stcb); } break; } #endif default: /* TSNH */ break; } } SCTP_TCB_UNLOCK(stcb); } } null_return: /* clean up for returning null */ if (locked_tcb) { SCTP_TCB_LOCK(locked_tcb); atomic_subtract_int(&locked_tcb->asoc.refcnt, 1); } SCTP_INP_WUNLOCK(inp); SCTP_INP_INFO_RUNLOCK(); /* not found */ return (NULL); } /* * Find an association for a specific endpoint using the association id given * out in the COMM_UP notification */ struct sctp_tcb * sctp_findasoc_ep_asocid_locked(struct sctp_inpcb *inp, sctp_assoc_t asoc_id, int want_lock) { /* * Use my the assoc_id to find a endpoint */ struct sctpasochead *head; struct sctp_tcb *stcb; uint32_t id; if (inp == NULL) { SCTP_PRINTF("TSNH ep_associd\n"); return (NULL); } if (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_ALLGONE) { SCTP_PRINTF("TSNH ep_associd0\n"); return (NULL); } id = (uint32_t) asoc_id; head = &inp->sctp_asocidhash[SCTP_PCBHASH_ASOC(id, inp->hashasocidmark)]; if (head == NULL) { /* invalid id TSNH */ SCTP_PRINTF("TSNH ep_associd1\n"); return (NULL); } LIST_FOREACH(stcb, head, sctp_tcbasocidhash) { if (stcb->asoc.assoc_id == id) { if (inp != stcb->sctp_ep) { /* * some other guy has the same id active (id * collision ??). */ SCTP_PRINTF("TSNH ep_associd2\n"); continue; } if (stcb->asoc.state & SCTP_STATE_ABOUT_TO_BE_FREED) { continue; } if (want_lock) { SCTP_TCB_LOCK(stcb); } return (stcb); } } return (NULL); } struct sctp_tcb * sctp_findassociation_ep_asocid(struct sctp_inpcb *inp, sctp_assoc_t asoc_id, int want_lock) { struct sctp_tcb *stcb; SCTP_INP_RLOCK(inp); stcb = sctp_findasoc_ep_asocid_locked(inp, asoc_id, want_lock); SCTP_INP_RUNLOCK(inp); return (stcb); } static struct sctp_inpcb * sctp_endpoint_probe(struct sockaddr *nam, struct sctppcbhead *head, uint16_t lport, uint32_t vrf_id) { struct sctp_inpcb *inp; struct sockaddr_in *sin; #ifdef INET6 struct sockaddr_in6 *sin6; #endif struct sctp_laddr *laddr; #ifdef INET6 struct sockaddr_in6 *intf_addr6; #endif int fnd; /* * Endpoing probe expects that the INP_INFO is locked. */ sin = NULL; #ifdef INET6 sin6 = NULL; #endif switch (nam->sa_family) { case AF_INET: sin = (struct sockaddr_in *)nam; break; #ifdef INET6 case AF_INET6: sin6 = (struct sockaddr_in6 *)nam; break; #endif default: /* unsupported family */ return (NULL); } if (head == NULL) return (NULL); LIST_FOREACH(inp, head, sctp_hash) { SCTP_INP_RLOCK(inp); if (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_ALLGONE) { SCTP_INP_RUNLOCK(inp); continue; } if ((inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) && (inp->sctp_lport == lport)) { /* got it */ if ((nam->sa_family == AF_INET) && (inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) && SCTP_IPV6_V6ONLY(inp)) { /* IPv4 on a IPv6 socket with ONLY IPv6 set */ SCTP_INP_RUNLOCK(inp); continue; } /* A V6 address and the endpoint is NOT bound V6 */ if (nam->sa_family == AF_INET6 && (inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) == 0) { SCTP_INP_RUNLOCK(inp); continue; } /* does a VRF id match? */ fnd = 0; if (inp->def_vrf_id == vrf_id) fnd = 1; SCTP_INP_RUNLOCK(inp); if (!fnd) continue; return (inp); } SCTP_INP_RUNLOCK(inp); } if ((nam->sa_family == AF_INET) && (sin->sin_addr.s_addr == INADDR_ANY)) { /* Can't hunt for one that has no address specified */ return (NULL); } #ifdef INET6 if ((nam->sa_family == AF_INET6) && (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr))) { /* Can't hunt for one that has no address specified */ return (NULL); } #endif /* * ok, not bound to all so see if we can find a EP bound to this * address. */ LIST_FOREACH(inp, head, sctp_hash) { SCTP_INP_RLOCK(inp); if (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_ALLGONE) { SCTP_INP_RUNLOCK(inp); continue; } if ((inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL)) { SCTP_INP_RUNLOCK(inp); continue; } /* * Ok this could be a likely candidate, look at all of its * addresses */ if (inp->sctp_lport != lport) { SCTP_INP_RUNLOCK(inp); continue; } /* does a VRF id match? */ fnd = 0; if (inp->def_vrf_id == vrf_id) fnd = 1; if (!fnd) { SCTP_INP_RUNLOCK(inp); continue; } LIST_FOREACH(laddr, &inp->sctp_addr_list, sctp_nxt_addr) { if (laddr->ifa == NULL) { SCTPDBG(SCTP_DEBUG_PCB1, "%s: NULL ifa\n", __FUNCTION__); continue; } SCTPDBG(SCTP_DEBUG_PCB1, "Ok laddr->ifa:%p is possible, ", laddr->ifa); if (laddr->ifa->localifa_flags & SCTP_BEING_DELETED) { SCTPDBG(SCTP_DEBUG_PCB1, "Huh IFA being deleted\n"); continue; } if (laddr->ifa->address.sa.sa_family == nam->sa_family) { /* possible, see if it matches */ struct sockaddr_in *intf_addr; intf_addr = &laddr->ifa->address.sin; switch (nam->sa_family) { case AF_INET: if (sin->sin_addr.s_addr == intf_addr->sin_addr.s_addr) { SCTP_INP_RUNLOCK(inp); return (inp); } break; #ifdef INET6 case AF_INET6: intf_addr6 = &laddr->ifa->address.sin6; if (SCTP6_ARE_ADDR_EQUAL(sin6, intf_addr6)) { SCTP_INP_RUNLOCK(inp); return (inp); } break; #endif } } } SCTP_INP_RUNLOCK(inp); } return (NULL); } static struct sctp_inpcb * sctp_isport_inuse(struct sctp_inpcb *inp, uint16_t lport, uint32_t vrf_id) { struct sctppcbhead *head; struct sctp_inpcb *t_inp; int fnd; head = &SCTP_BASE_INFO(sctp_ephash)[SCTP_PCBHASH_ALLADDR(lport, SCTP_BASE_INFO(hashmark))]; LIST_FOREACH(t_inp, head, sctp_hash) { if (t_inp->sctp_lport != lport) { continue; } /* is it in the VRF in question */ fnd = 0; if (t_inp->def_vrf_id == vrf_id) fnd = 1; if (!fnd) continue; /* This one is in use. */ /* check the v6/v4 binding issue */ if ((t_inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) && SCTP_IPV6_V6ONLY(t_inp)) { if (inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) { /* collision in V6 space */ return (t_inp); } else { /* inp is BOUND_V4 no conflict */ continue; } } else if (t_inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) { /* t_inp is bound v4 and v6, conflict always */ return (t_inp); } else { /* t_inp is bound only V4 */ if ((inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) && SCTP_IPV6_V6ONLY(inp)) { /* no conflict */ continue; } /* else fall through to conflict */ } return (t_inp); } return (NULL); } int sctp_swap_inpcb_for_listen(struct sctp_inpcb *inp) { /* For 1-2-1 with port reuse */ struct sctppcbhead *head; struct sctp_inpcb *tinp; if (sctp_is_feature_off(inp, SCTP_PCB_FLAGS_PORTREUSE)) { /* only works with port reuse on */ return (-1); } if ((inp->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL) == 0) { return (0); } SCTP_INP_RUNLOCK(inp); head = &SCTP_BASE_INFO(sctp_ephash)[SCTP_PCBHASH_ALLADDR(inp->sctp_lport, SCTP_BASE_INFO(hashmark))]; /* Kick out all non-listeners to the TCP hash */ LIST_FOREACH(tinp, head, sctp_hash) { if (tinp->sctp_lport != inp->sctp_lport) { continue; } if (tinp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_ALLGONE) { continue; } if (tinp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) { continue; } if (tinp->sctp_socket->so_qlimit) { continue; } SCTP_INP_WLOCK(tinp); LIST_REMOVE(tinp, sctp_hash); head = &SCTP_BASE_INFO(sctp_tcpephash)[SCTP_PCBHASH_ALLADDR(tinp->sctp_lport, SCTP_BASE_INFO(hashtcpmark))]; tinp->sctp_flags |= SCTP_PCB_FLAGS_IN_TCPPOOL; LIST_INSERT_HEAD(head, tinp, sctp_hash); SCTP_INP_WUNLOCK(tinp); } SCTP_INP_WLOCK(inp); /* Pull from where he was */ LIST_REMOVE(inp, sctp_hash); inp->sctp_flags &= ~SCTP_PCB_FLAGS_IN_TCPPOOL; head = &SCTP_BASE_INFO(sctp_ephash)[SCTP_PCBHASH_ALLADDR(inp->sctp_lport, SCTP_BASE_INFO(hashmark))]; LIST_INSERT_HEAD(head, inp, sctp_hash); SCTP_INP_WUNLOCK(inp); SCTP_INP_RLOCK(inp); return (0); } struct sctp_inpcb * sctp_pcb_findep(struct sockaddr *nam, int find_tcp_pool, int have_lock, uint32_t vrf_id) { /* * First we check the hash table to see if someone has this port * bound with just the port. */ struct sctp_inpcb *inp; struct sctppcbhead *head; struct sockaddr_in *sin; struct sockaddr_in6 *sin6; int lport; if (nam->sa_family == AF_INET) { sin = (struct sockaddr_in *)nam; lport = ((struct sockaddr_in *)nam)->sin_port; } else if (nam->sa_family == AF_INET6) { sin6 = (struct sockaddr_in6 *)nam; lport = ((struct sockaddr_in6 *)nam)->sin6_port; } else { /* unsupported family */ return (NULL); } /* * I could cheat here and just cast to one of the types but we will * do it right. It also provides the check against an Unsupported * type too. */ /* Find the head of the ALLADDR chain */ if (have_lock == 0) { SCTP_INP_INFO_RLOCK(); } head = &SCTP_BASE_INFO(sctp_ephash)[SCTP_PCBHASH_ALLADDR(lport, SCTP_BASE_INFO(hashmark))]; inp = sctp_endpoint_probe(nam, head, lport, vrf_id); /* * If the TCP model exists it could be that the main listening * endpoint is gone but there exists a connected socket for this guy * yet. If so we can return the first one that we find. This may NOT * be the correct one so the caller should be wary on the return * INP. Currently the onlyc caller that sets this flag is in bindx * where we are verifying that a user CAN bind the address. He * either has bound it already, or someone else has, or its open to * bind, so this is good enough. */ if (inp == NULL && find_tcp_pool) { head = &SCTP_BASE_INFO(sctp_tcpephash)[SCTP_PCBHASH_ALLADDR(lport, SCTP_BASE_INFO(hashtcpmark))]; inp = sctp_endpoint_probe(nam, head, lport, vrf_id); } if (inp) { SCTP_INP_INCR_REF(inp); } if (have_lock == 0) { SCTP_INP_INFO_RUNLOCK(); } return (inp); } /* * Find an association for an endpoint with the pointer to whom you want to * send to and the endpoint pointer. The address can be IPv4 or IPv6. We may * need to change the *to to some other struct like a mbuf... */ struct sctp_tcb * sctp_findassociation_addr_sa(struct sockaddr *to, struct sockaddr *from, struct sctp_inpcb **inp_p, struct sctp_nets **netp, int find_tcp_pool, uint32_t vrf_id) { struct sctp_inpcb *inp = NULL; struct sctp_tcb *retval; SCTP_INP_INFO_RLOCK(); if (find_tcp_pool) { if (inp_p != NULL) { retval = sctp_tcb_special_locate(inp_p, from, to, netp, vrf_id); } else { retval = sctp_tcb_special_locate(&inp, from, to, netp, vrf_id); } if (retval != NULL) { SCTP_INP_INFO_RUNLOCK(); return (retval); } } inp = sctp_pcb_findep(to, 0, 1, vrf_id); if (inp_p != NULL) { *inp_p = inp; } SCTP_INP_INFO_RUNLOCK(); if (inp == NULL) { return (NULL); } /* * ok, we have an endpoint, now lets find the assoc for it (if any) * we now place the source address or from in the to of the find * endpoint call. Since in reality this chain is used from the * inbound packet side. */ if (inp_p != NULL) { retval = sctp_findassociation_ep_addr(inp_p, from, netp, to, NULL); } else { retval = sctp_findassociation_ep_addr(&inp, from, netp, to, NULL); } return retval; } /* * This routine will grub through the mbuf that is a INIT or INIT-ACK and * find all addresses that the sender has specified in any address list. Each * address will be used to lookup the TCB and see if one exits. */ static struct sctp_tcb * sctp_findassociation_special_addr(struct mbuf *m, int iphlen, int offset, struct sctphdr *sh, struct sctp_inpcb **inp_p, struct sctp_nets **netp, struct sockaddr *dest) { struct sockaddr_in sin4; struct sockaddr_in6 sin6; struct sctp_paramhdr *phdr, parm_buf; struct sctp_tcb *retval; uint32_t ptype, plen; memset(&sin4, 0, sizeof(sin4)); memset(&sin6, 0, sizeof(sin6)); sin4.sin_len = sizeof(sin4); sin4.sin_family = AF_INET; sin4.sin_port = sh->src_port; sin6.sin6_len = sizeof(sin6); sin6.sin6_family = AF_INET6; sin6.sin6_port = sh->src_port; retval = NULL; offset += sizeof(struct sctp_init_chunk); phdr = sctp_get_next_param(m, offset, &parm_buf, sizeof(parm_buf)); while (phdr != NULL) { /* now we must see if we want the parameter */ ptype = ntohs(phdr->param_type); plen = ntohs(phdr->param_length); if (plen == 0) { break; } if (ptype == SCTP_IPV4_ADDRESS && plen == sizeof(struct sctp_ipv4addr_param)) { /* Get the rest of the address */ struct sctp_ipv4addr_param ip4_parm, *p4; phdr = sctp_get_next_param(m, offset, (struct sctp_paramhdr *)&ip4_parm, min(plen, sizeof(ip4_parm))); if (phdr == NULL) { return (NULL); } p4 = (struct sctp_ipv4addr_param *)phdr; memcpy(&sin4.sin_addr, &p4->addr, sizeof(p4->addr)); /* look it up */ retval = sctp_findassociation_ep_addr(inp_p, (struct sockaddr *)&sin4, netp, dest, NULL); if (retval != NULL) { return (retval); } } else if (ptype == SCTP_IPV6_ADDRESS && plen == sizeof(struct sctp_ipv6addr_param)) { /* Get the rest of the address */ struct sctp_ipv6addr_param ip6_parm, *p6; phdr = sctp_get_next_param(m, offset, (struct sctp_paramhdr *)&ip6_parm, min(plen, sizeof(ip6_parm))); if (phdr == NULL) { return (NULL); } p6 = (struct sctp_ipv6addr_param *)phdr; memcpy(&sin6.sin6_addr, &p6->addr, sizeof(p6->addr)); /* look it up */ retval = sctp_findassociation_ep_addr(inp_p, (struct sockaddr *)&sin6, netp, dest, NULL); if (retval != NULL) { return (retval); } } offset += SCTP_SIZE32(plen); phdr = sctp_get_next_param(m, offset, &parm_buf, sizeof(parm_buf)); } return (NULL); } static int sctp_does_stcb_own_this_addr(struct sctp_tcb *stcb, struct sockaddr *to) { struct sctp_nets *net; /* * Simple question, the ports match, does the tcb own the to * address? */ if ((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL)) { /* of course */ return (1); } /* have to look at all bound addresses */ TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) { if (net->ro._l_addr.sa.sa_family != to->sa_family) { /* not the same family, can't be a match */ continue; } switch (to->sa_family) { case AF_INET: { struct sockaddr_in *sin, *rsin; sin = (struct sockaddr_in *)&net->ro._l_addr; rsin = (struct sockaddr_in *)to; if (sin->sin_addr.s_addr == rsin->sin_addr.s_addr) { /* found it */ return (1); } break; } #ifdef INET6 case AF_INET6: { struct sockaddr_in6 *sin6, *rsin6; sin6 = (struct sockaddr_in6 *)&net->ro._l_addr; rsin6 = (struct sockaddr_in6 *)to; if (SCTP6_ARE_ADDR_EQUAL(sin6, rsin6)) { /* Update the endpoint pointer */ return (1); } break; } #endif default: /* TSNH */ break; } } /* Nope, do not have the address ;-( */ return (0); } static struct sctp_tcb * sctp_findassoc_by_vtag(struct sockaddr *from, struct sockaddr *to, uint32_t vtag, struct sctp_inpcb **inp_p, struct sctp_nets **netp, uint16_t rport, uint16_t lport, int skip_src_check, uint32_t vrf_id, uint32_t remote_tag) { /* * Use my vtag to hash. If we find it we then verify the source addr * is in the assoc. If all goes well we save a bit on rec of a * packet. */ struct sctpasochead *head; struct sctp_nets *net; struct sctp_tcb *stcb; *netp = NULL; *inp_p = NULL; SCTP_INP_INFO_RLOCK(); head = &SCTP_BASE_INFO(sctp_asochash)[SCTP_PCBHASH_ASOC(vtag, SCTP_BASE_INFO(hashasocmark))]; if (head == NULL) { /* invalid vtag */ SCTP_INP_INFO_RUNLOCK(); return (NULL); } LIST_FOREACH(stcb, head, sctp_asocs) { SCTP_INP_RLOCK(stcb->sctp_ep); if (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_SOCKET_ALLGONE) { SCTP_INP_RUNLOCK(stcb->sctp_ep); continue; } SCTP_TCB_LOCK(stcb); SCTP_INP_RUNLOCK(stcb->sctp_ep); if (stcb->asoc.my_vtag == vtag) { /* candidate */ if (stcb->rport != rport) { SCTP_TCB_UNLOCK(stcb); continue; } if (stcb->sctp_ep->sctp_lport != lport) { SCTP_TCB_UNLOCK(stcb); continue; } if (stcb->asoc.state & SCTP_STATE_ABOUT_TO_BE_FREED) { SCTP_TCB_UNLOCK(stcb); continue; } /* RRS:Need toaddr check here */ if (sctp_does_stcb_own_this_addr(stcb, to) == 0) { /* Endpoint does not own this address */ SCTP_TCB_UNLOCK(stcb); continue; } if (remote_tag) { /* * If we have both vtags thats all we match * on */ if (stcb->asoc.peer_vtag == remote_tag) { /* * If both tags match we consider it * conclusive and check NO * source/destination addresses */ goto conclusive; } } if (skip_src_check) { conclusive: if (from) { net = sctp_findnet(stcb, from); } else { *netp = NULL; /* unknown */ } if (inp_p) *inp_p = stcb->sctp_ep; SCTP_INP_INFO_RUNLOCK(); return (stcb); } net = sctp_findnet(stcb, from); if (net) { /* yep its him. */ *netp = net; SCTP_STAT_INCR(sctps_vtagexpress); *inp_p = stcb->sctp_ep; SCTP_INP_INFO_RUNLOCK(); return (stcb); } else { /* * not him, this should only happen in rare * cases so I peg it. */ SCTP_STAT_INCR(sctps_vtagbogus); } } SCTP_TCB_UNLOCK(stcb); } SCTP_INP_INFO_RUNLOCK(); return (NULL); } /* * Find an association with the pointer to the inbound IP packet. This can be * a IPv4 or IPv6 packet. */ struct sctp_tcb * sctp_findassociation_addr(struct mbuf *m, int iphlen, int offset, struct sctphdr *sh, struct sctp_chunkhdr *ch, struct sctp_inpcb **inp_p, struct sctp_nets **netp, uint32_t vrf_id) { int find_tcp_pool; struct ip *iph; struct sctp_tcb *retval; struct sockaddr_storage to_store, from_store; struct sockaddr *to = (struct sockaddr *)&to_store; struct sockaddr *from = (struct sockaddr *)&from_store; struct sctp_inpcb *inp; iph = mtod(m, struct ip *); switch (iph->ip_v) { case IPVERSION: { /* its IPv4 */ struct sockaddr_in *from4; from4 = (struct sockaddr_in *)&from_store; bzero(from4, sizeof(*from4)); from4->sin_family = AF_INET; from4->sin_len = sizeof(struct sockaddr_in); from4->sin_addr.s_addr = iph->ip_src.s_addr; from4->sin_port = sh->src_port; break; } #ifdef INET6 case IPV6_VERSION >> 4: { /* its IPv6 */ struct ip6_hdr *ip6; struct sockaddr_in6 *from6; ip6 = mtod(m, struct ip6_hdr *); from6 = (struct sockaddr_in6 *)&from_store; bzero(from6, sizeof(*from6)); from6->sin6_family = AF_INET6; from6->sin6_len = sizeof(struct sockaddr_in6); from6->sin6_addr = ip6->ip6_src; from6->sin6_port = sh->src_port; /* Get the scopes in properly to the sin6 addr's */ /* we probably don't need these operations */ (void)sa6_recoverscope(from6); sa6_embedscope(from6, MODULE_GLOBAL(ip6_use_defzone)); break; } #endif default: /* Currently not supported. */ return (NULL); } switch (iph->ip_v) { case IPVERSION: { /* its IPv4 */ struct sockaddr_in *to4; to4 = (struct sockaddr_in *)&to_store; bzero(to4, sizeof(*to4)); to4->sin_family = AF_INET; to4->sin_len = sizeof(struct sockaddr_in); to4->sin_addr.s_addr = iph->ip_dst.s_addr; to4->sin_port = sh->dest_port; break; } #ifdef INET6 case IPV6_VERSION >> 4: { /* its IPv6 */ struct ip6_hdr *ip6; struct sockaddr_in6 *to6; ip6 = mtod(m, struct ip6_hdr *); to6 = (struct sockaddr_in6 *)&to_store; bzero(to6, sizeof(*to6)); to6->sin6_family = AF_INET6; to6->sin6_len = sizeof(struct sockaddr_in6); to6->sin6_addr = ip6->ip6_dst; to6->sin6_port = sh->dest_port; /* Get the scopes in properly to the sin6 addr's */ /* we probably don't need these operations */ (void)sa6_recoverscope(to6); sa6_embedscope(to6, MODULE_GLOBAL(ip6_use_defzone)); break; } #endif default: /* TSNH */ break; } if (sh->v_tag) { /* we only go down this path if vtag is non-zero */ retval = sctp_findassoc_by_vtag(from, to, ntohl(sh->v_tag), inp_p, netp, sh->src_port, sh->dest_port, 0, vrf_id, 0); if (retval) { return (retval); } } find_tcp_pool = 0; if ((ch->chunk_type != SCTP_INITIATION) && (ch->chunk_type != SCTP_INITIATION_ACK) && (ch->chunk_type != SCTP_COOKIE_ACK) && (ch->chunk_type != SCTP_COOKIE_ECHO)) { /* Other chunk types go to the tcp pool. */ find_tcp_pool = 1; } if (inp_p) { retval = sctp_findassociation_addr_sa(to, from, inp_p, netp, find_tcp_pool, vrf_id); inp = *inp_p; } else { retval = sctp_findassociation_addr_sa(to, from, &inp, netp, find_tcp_pool, vrf_id); } SCTPDBG(SCTP_DEBUG_PCB1, "retval:%p inp:%p\n", retval, inp); if (retval == NULL && inp) { /* Found a EP but not this address */ if ((ch->chunk_type == SCTP_INITIATION) || (ch->chunk_type == SCTP_INITIATION_ACK)) { /*- * special hook, we do NOT return linp or an * association that is linked to an existing * association that is under the TCP pool (i.e. no * listener exists). The endpoint finding routine * will always find a listner before examining the * TCP pool. */ if (inp->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL) { if (inp_p) { *inp_p = NULL; } return (NULL); } retval = sctp_findassociation_special_addr(m, iphlen, offset, sh, &inp, netp, to); if (inp_p != NULL) { *inp_p = inp; } } } SCTPDBG(SCTP_DEBUG_PCB1, "retval is %p\n", retval); return (retval); } /* * lookup an association by an ASCONF lookup address. * if the lookup address is 0.0.0.0 or ::0, use the vtag to do the lookup */ struct sctp_tcb * sctp_findassociation_ep_asconf(struct mbuf *m, int iphlen, int offset, struct sctphdr *sh, struct sctp_inpcb **inp_p, struct sctp_nets **netp, uint32_t vrf_id) { struct sctp_tcb *stcb; struct sockaddr_in *sin; #ifdef INET6 struct sockaddr_in6 *sin6; #endif struct sockaddr_storage local_store, remote_store; struct sockaddr *to; struct ip *iph; #ifdef INET6 struct ip6_hdr *ip6; #endif struct sctp_paramhdr parm_buf, *phdr; int ptype; int zero_address = 0; memset(&local_store, 0, sizeof(local_store)); memset(&remote_store, 0, sizeof(remote_store)); to = (struct sockaddr *)&local_store; /* First get the destination address setup too. */ iph = mtod(m, struct ip *); switch (iph->ip_v) { case IPVERSION: /* its IPv4 */ sin = (struct sockaddr_in *)&local_store; sin->sin_family = AF_INET; sin->sin_len = sizeof(*sin); sin->sin_port = sh->dest_port; sin->sin_addr.s_addr = iph->ip_dst.s_addr; break; #ifdef INET6 case IPV6_VERSION >> 4: /* its IPv6 */ ip6 = mtod(m, struct ip6_hdr *); sin6 = (struct sockaddr_in6 *)&local_store; sin6->sin6_family = AF_INET6; sin6->sin6_len = sizeof(*sin6); sin6->sin6_port = sh->dest_port; sin6->sin6_addr = ip6->ip6_dst; break; #endif default: return NULL; } phdr = sctp_get_next_param(m, offset + sizeof(struct sctp_asconf_chunk), &parm_buf, sizeof(struct sctp_paramhdr)); if (phdr == NULL) { SCTPDBG(SCTP_DEBUG_INPUT3, "%s: failed to get asconf lookup addr\n", __FUNCTION__); return NULL; } ptype = (int)((uint32_t) ntohs(phdr->param_type)); /* get the correlation address */ switch (ptype) { #ifdef INET6 case SCTP_IPV6_ADDRESS: { /* ipv6 address param */ struct sctp_ipv6addr_param *p6, p6_buf; if (ntohs(phdr->param_length) != sizeof(struct sctp_ipv6addr_param)) { return NULL; } p6 = (struct sctp_ipv6addr_param *)sctp_get_next_param(m, offset + sizeof(struct sctp_asconf_chunk), &p6_buf.ph, sizeof(*p6)); if (p6 == NULL) { SCTPDBG(SCTP_DEBUG_INPUT3, "%s: failed to get asconf v6 lookup addr\n", __FUNCTION__); return (NULL); } sin6 = (struct sockaddr_in6 *)&remote_store; sin6->sin6_family = AF_INET6; sin6->sin6_len = sizeof(*sin6); sin6->sin6_port = sh->src_port; memcpy(&sin6->sin6_addr, &p6->addr, sizeof(struct in6_addr)); if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) zero_address = 1; break; } #endif case SCTP_IPV4_ADDRESS: { /* ipv4 address param */ struct sctp_ipv4addr_param *p4, p4_buf; if (ntohs(phdr->param_length) != sizeof(struct sctp_ipv4addr_param)) { return NULL; } p4 = (struct sctp_ipv4addr_param *)sctp_get_next_param(m, offset + sizeof(struct sctp_asconf_chunk), &p4_buf.ph, sizeof(*p4)); if (p4 == NULL) { SCTPDBG(SCTP_DEBUG_INPUT3, "%s: failed to get asconf v4 lookup addr\n", __FUNCTION__); return (NULL); } sin = (struct sockaddr_in *)&remote_store; sin->sin_family = AF_INET; sin->sin_len = sizeof(*sin); sin->sin_port = sh->src_port; memcpy(&sin->sin_addr, &p4->addr, sizeof(struct in_addr)); if (sin->sin_addr.s_addr == INADDR_ANY) zero_address = 1; break; } default: /* invalid address param type */ return NULL; } if (zero_address) { stcb = sctp_findassoc_by_vtag(NULL, to, ntohl(sh->v_tag), inp_p, netp, sh->src_port, sh->dest_port, 1, vrf_id, 0); /* * printf("findassociation_ep_asconf: zero lookup address * finds stcb 0x%x\n", (uint32_t)stcb); */ } else { stcb = sctp_findassociation_ep_addr(inp_p, (struct sockaddr *)&remote_store, netp, to, NULL); } return (stcb); } /* * allocate a sctp_inpcb and setup a temporary binding to a port/all * addresses. This way if we don't get a bind we by default pick a ephemeral * port with all addresses bound. */ int sctp_inpcb_alloc(struct socket *so, uint32_t vrf_id) { /* * we get called when a new endpoint starts up. We need to allocate * the sctp_inpcb structure from the zone and init it. Mark it as * unbound and find a port that we can use as an ephemeral with * INADDR_ANY. If the user binds later no problem we can then add in * the specific addresses. And setup the default parameters for the * EP. */ int i, error; struct sctp_inpcb *inp; struct sctp_pcb *m; struct timeval time; sctp_sharedkey_t *null_key; error = 0; SCTP_INP_INFO_WLOCK(); inp = SCTP_ZONE_GET(SCTP_BASE_INFO(ipi_zone_ep), struct sctp_inpcb); if (inp == NULL) { SCTP_PRINTF("Out of SCTP-INPCB structures - no resources\n"); SCTP_INP_INFO_WUNLOCK(); SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PCB, ENOBUFS); return (ENOBUFS); } /* zap it */ bzero(inp, sizeof(*inp)); /* bump generations */ /* setup socket pointers */ inp->sctp_socket = so; inp->ip_inp.inp.inp_socket = so; inp->sctp_associd_counter = 1; inp->partial_delivery_point = SCTP_SB_LIMIT_RCV(so) >> SCTP_PARTIAL_DELIVERY_SHIFT; inp->sctp_frag_point = SCTP_DEFAULT_MAXSEGMENT; /* init the small hash table we use to track asocid <-> tcb */ inp->sctp_asocidhash = SCTP_HASH_INIT(SCTP_STACK_VTAG_HASH_SIZE, &inp->hashasocidmark); if (inp->sctp_asocidhash == NULL) { SCTP_ZONE_FREE(SCTP_BASE_INFO(ipi_zone_ep), inp); SCTP_INP_INFO_WUNLOCK(); return error; } #ifdef IPSEC { struct inpcbpolicy *pcb_sp = NULL; error = ipsec_init_policy(so, &pcb_sp); /* Arrange to share the policy */ inp->ip_inp.inp.inp_sp = pcb_sp; ((struct in6pcb *)(&inp->ip_inp.inp))->in6p_sp = pcb_sp; } if (error != 0) { SCTP_ZONE_FREE(SCTP_BASE_INFO(ipi_zone_ep), inp); SCTP_INP_INFO_WUNLOCK(); return error; } #endif /* IPSEC */ SCTP_INCR_EP_COUNT(); inp->ip_inp.inp.inp_ip_ttl = MODULE_GLOBAL(ip_defttl); SCTP_INP_INFO_WUNLOCK(); so->so_pcb = (caddr_t)inp; if ((SCTP_SO_TYPE(so) == SOCK_DGRAM) || (SCTP_SO_TYPE(so) == SOCK_SEQPACKET)) { /* UDP style socket */ inp->sctp_flags = (SCTP_PCB_FLAGS_UDPTYPE | SCTP_PCB_FLAGS_UNBOUND); /* Be sure it is NON-BLOCKING IO for UDP */ /* SCTP_SET_SO_NBIO(so); */ } else if (SCTP_SO_TYPE(so) == SOCK_STREAM) { /* TCP style socket */ inp->sctp_flags = (SCTP_PCB_FLAGS_TCPTYPE | SCTP_PCB_FLAGS_UNBOUND); /* Be sure we have blocking IO by default */ SCTP_CLEAR_SO_NBIO(so); } else { /* * unsupported socket type (RAW, etc)- in case we missed it * in protosw */ SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PCB, EOPNOTSUPP); SCTP_ZONE_FREE(SCTP_BASE_INFO(ipi_zone_ep), inp); return (EOPNOTSUPP); } if (SCTP_BASE_SYSCTL(sctp_default_frag_interleave) == SCTP_FRAG_LEVEL_1) { sctp_feature_on(inp, SCTP_PCB_FLAGS_FRAG_INTERLEAVE); sctp_feature_off(inp, SCTP_PCB_FLAGS_INTERLEAVE_STRMS); } else if (SCTP_BASE_SYSCTL(sctp_default_frag_interleave) == SCTP_FRAG_LEVEL_2) { sctp_feature_on(inp, SCTP_PCB_FLAGS_FRAG_INTERLEAVE); sctp_feature_on(inp, SCTP_PCB_FLAGS_INTERLEAVE_STRMS); } else if (SCTP_BASE_SYSCTL(sctp_default_frag_interleave) == SCTP_FRAG_LEVEL_0) { sctp_feature_off(inp, SCTP_PCB_FLAGS_FRAG_INTERLEAVE); sctp_feature_off(inp, SCTP_PCB_FLAGS_INTERLEAVE_STRMS); } inp->sctp_tcbhash = SCTP_HASH_INIT(SCTP_BASE_SYSCTL(sctp_pcbtblsize), &inp->sctp_hashmark); if (inp->sctp_tcbhash == NULL) { SCTP_PRINTF("Out of SCTP-INPCB->hashinit - no resources\n"); SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PCB, ENOBUFS); SCTP_ZONE_FREE(SCTP_BASE_INFO(ipi_zone_ep), inp); return (ENOBUFS); } inp->def_vrf_id = vrf_id; SCTP_INP_INFO_WLOCK(); SCTP_INP_LOCK_INIT(inp); INP_LOCK_INIT(&inp->ip_inp.inp, "inp", "sctpinp"); SCTP_INP_READ_INIT(inp); SCTP_ASOC_CREATE_LOCK_INIT(inp); /* lock the new ep */ SCTP_INP_WLOCK(inp); /* add it to the info area */ LIST_INSERT_HEAD(&SCTP_BASE_INFO(listhead), inp, sctp_list); SCTP_INP_INFO_WUNLOCK(); TAILQ_INIT(&inp->read_queue); LIST_INIT(&inp->sctp_addr_list); LIST_INIT(&inp->sctp_asoc_list); #ifdef SCTP_TRACK_FREED_ASOCS /* TEMP CODE */ LIST_INIT(&inp->sctp_asoc_free_list); #endif /* Init the timer structure for signature change */ SCTP_OS_TIMER_INIT(&inp->sctp_ep.signature_change.timer); inp->sctp_ep.signature_change.type = SCTP_TIMER_TYPE_NEWCOOKIE; /* now init the actual endpoint default data */ m = &inp->sctp_ep; /* setup the base timeout information */ m->sctp_timeoutticks[SCTP_TIMER_SEND] = SEC_TO_TICKS(SCTP_SEND_SEC); /* needed ? */ m->sctp_timeoutticks[SCTP_TIMER_INIT] = SEC_TO_TICKS(SCTP_INIT_SEC); /* needed ? */ m->sctp_timeoutticks[SCTP_TIMER_RECV] = MSEC_TO_TICKS(SCTP_BASE_SYSCTL(sctp_delayed_sack_time_default)); m->sctp_timeoutticks[SCTP_TIMER_HEARTBEAT] = MSEC_TO_TICKS(SCTP_BASE_SYSCTL(sctp_heartbeat_interval_default)); m->sctp_timeoutticks[SCTP_TIMER_PMTU] = SEC_TO_TICKS(SCTP_BASE_SYSCTL(sctp_pmtu_raise_time_default)); m->sctp_timeoutticks[SCTP_TIMER_MAXSHUTDOWN] = SEC_TO_TICKS(SCTP_BASE_SYSCTL(sctp_shutdown_guard_time_default)); m->sctp_timeoutticks[SCTP_TIMER_SIGNATURE] = SEC_TO_TICKS(SCTP_BASE_SYSCTL(sctp_secret_lifetime_default)); /* all max/min max are in ms */ m->sctp_maxrto = SCTP_BASE_SYSCTL(sctp_rto_max_default); m->sctp_minrto = SCTP_BASE_SYSCTL(sctp_rto_min_default); m->initial_rto = SCTP_BASE_SYSCTL(sctp_rto_initial_default); m->initial_init_rto_max = SCTP_BASE_SYSCTL(sctp_init_rto_max_default); m->sctp_sack_freq = SCTP_BASE_SYSCTL(sctp_sack_freq_default); m->max_open_streams_intome = MAX_SCTP_STREAMS; m->max_init_times = SCTP_BASE_SYSCTL(sctp_init_rtx_max_default); m->max_send_times = SCTP_BASE_SYSCTL(sctp_assoc_rtx_max_default); m->def_net_failure = SCTP_BASE_SYSCTL(sctp_path_rtx_max_default); m->sctp_sws_sender = SCTP_SWS_SENDER_DEF; m->sctp_sws_receiver = SCTP_SWS_RECEIVER_DEF; m->max_burst = SCTP_BASE_SYSCTL(sctp_max_burst_default); if ((SCTP_BASE_SYSCTL(sctp_default_cc_module) >= SCTP_CC_RFC2581) && (SCTP_BASE_SYSCTL(sctp_default_cc_module) <= SCTP_CC_HTCP)) { m->sctp_default_cc_module = SCTP_BASE_SYSCTL(sctp_default_cc_module); } else { /* sysctl done with invalid value, set to 2581 */ m->sctp_default_cc_module = SCTP_CC_RFC2581; } /* number of streams to pre-open on a association */ m->pre_open_stream_count = SCTP_BASE_SYSCTL(sctp_nr_outgoing_streams_default); /* Add adaptation cookie */ m->adaptation_layer_indicator = 0x504C5253; /* seed random number generator */ m->random_counter = 1; m->store_at = SCTP_SIGNATURE_SIZE; SCTP_READ_RANDOM(m->random_numbers, sizeof(m->random_numbers)); sctp_fill_random_store(m); /* Minimum cookie size */ m->size_of_a_cookie = (sizeof(struct sctp_init_msg) * 2) + sizeof(struct sctp_state_cookie); m->size_of_a_cookie += SCTP_SIGNATURE_SIZE; /* Setup the initial secret */ (void)SCTP_GETTIME_TIMEVAL(&time); m->time_of_secret_change = time.tv_sec; for (i = 0; i < SCTP_NUMBER_OF_SECRETS; i++) { m->secret_key[0][i] = sctp_select_initial_TSN(m); } sctp_timer_start(SCTP_TIMER_TYPE_NEWCOOKIE, inp, NULL, NULL); /* How long is a cookie good for ? */ m->def_cookie_life = MSEC_TO_TICKS(SCTP_BASE_SYSCTL(sctp_valid_cookie_life_default)); /* * Initialize authentication parameters */ m->local_hmacs = sctp_default_supported_hmaclist(); m->local_auth_chunks = sctp_alloc_chunklist(); sctp_auth_set_default_chunks(m->local_auth_chunks); LIST_INIT(&m->shared_keys); /* add default NULL key as key id 0 */ null_key = sctp_alloc_sharedkey(); sctp_insert_sharedkey(&m->shared_keys, null_key); SCTP_INP_WUNLOCK(inp); #ifdef SCTP_LOG_CLOSING sctp_log_closing(inp, NULL, 12); #endif return (error); } void sctp_move_pcb_and_assoc(struct sctp_inpcb *old_inp, struct sctp_inpcb *new_inp, struct sctp_tcb *stcb) { struct sctp_nets *net; uint16_t lport, rport; struct sctppcbhead *head; struct sctp_laddr *laddr, *oladdr; atomic_add_int(&stcb->asoc.refcnt, 1); SCTP_TCB_UNLOCK(stcb); SCTP_INP_INFO_WLOCK(); SCTP_INP_WLOCK(old_inp); SCTP_INP_WLOCK(new_inp); SCTP_TCB_LOCK(stcb); atomic_subtract_int(&stcb->asoc.refcnt, 1); new_inp->sctp_ep.time_of_secret_change = old_inp->sctp_ep.time_of_secret_change; memcpy(new_inp->sctp_ep.secret_key, old_inp->sctp_ep.secret_key, sizeof(old_inp->sctp_ep.secret_key)); new_inp->sctp_ep.current_secret_number = old_inp->sctp_ep.current_secret_number; new_inp->sctp_ep.last_secret_number = old_inp->sctp_ep.last_secret_number; new_inp->sctp_ep.size_of_a_cookie = old_inp->sctp_ep.size_of_a_cookie; /* make it so new data pours into the new socket */ stcb->sctp_socket = new_inp->sctp_socket; stcb->sctp_ep = new_inp; /* Copy the port across */ lport = new_inp->sctp_lport = old_inp->sctp_lport; rport = stcb->rport; /* Pull the tcb from the old association */ LIST_REMOVE(stcb, sctp_tcbhash); LIST_REMOVE(stcb, sctp_tcblist); if (stcb->asoc.in_asocid_hash) { LIST_REMOVE(stcb, sctp_tcbasocidhash); } /* Now insert the new_inp into the TCP connected hash */ head = &SCTP_BASE_INFO(sctp_tcpephash)[SCTP_PCBHASH_ALLADDR((lport), SCTP_BASE_INFO(hashtcpmark))]; LIST_INSERT_HEAD(head, new_inp, sctp_hash); /* Its safe to access */ new_inp->sctp_flags &= ~SCTP_PCB_FLAGS_UNBOUND; /* Now move the tcb into the endpoint list */ LIST_INSERT_HEAD(&new_inp->sctp_asoc_list, stcb, sctp_tcblist); /* * Question, do we even need to worry about the ep-hash since we * only have one connection? Probably not :> so lets get rid of it * and not suck up any kernel memory in that. */ if (stcb->asoc.in_asocid_hash) { struct sctpasochead *lhd; lhd = &new_inp->sctp_asocidhash[SCTP_PCBHASH_ASOC(stcb->asoc.assoc_id, new_inp->hashasocidmark)]; LIST_INSERT_HEAD(lhd, stcb, sctp_tcbasocidhash); } /* Ok. Let's restart timer. */ TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) { sctp_timer_start(SCTP_TIMER_TYPE_PATHMTURAISE, new_inp, stcb, net); } SCTP_INP_INFO_WUNLOCK(); if (new_inp->sctp_tcbhash != NULL) { SCTP_HASH_FREE(new_inp->sctp_tcbhash, new_inp->sctp_hashmark); new_inp->sctp_tcbhash = NULL; } if ((new_inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) == 0) { /* Subset bound, so copy in the laddr list from the old_inp */ LIST_FOREACH(oladdr, &old_inp->sctp_addr_list, sctp_nxt_addr) { laddr = SCTP_ZONE_GET(SCTP_BASE_INFO(ipi_zone_laddr), struct sctp_laddr); if (laddr == NULL) { /* * Gak, what can we do? This assoc is really * HOSED. We probably should send an abort * here. */ SCTPDBG(SCTP_DEBUG_PCB1, "Association hosed in TCP model, out of laddr memory\n"); continue; } SCTP_INCR_LADDR_COUNT(); bzero(laddr, sizeof(*laddr)); (void)SCTP_GETTIME_TIMEVAL(&laddr->start_time); laddr->ifa = oladdr->ifa; atomic_add_int(&laddr->ifa->refcount, 1); LIST_INSERT_HEAD(&new_inp->sctp_addr_list, laddr, sctp_nxt_addr); new_inp->laddr_count++; } } /* * Now any running timers need to be adjusted since we really don't * care if they are running or not just blast in the new_inp into * all of them. */ stcb->asoc.hb_timer.ep = (void *)new_inp; stcb->asoc.dack_timer.ep = (void *)new_inp; stcb->asoc.asconf_timer.ep = (void *)new_inp; stcb->asoc.strreset_timer.ep = (void *)new_inp; stcb->asoc.shut_guard_timer.ep = (void *)new_inp; stcb->asoc.autoclose_timer.ep = (void *)new_inp; stcb->asoc.delayed_event_timer.ep = (void *)new_inp; stcb->asoc.delete_prim_timer.ep = (void *)new_inp; /* now what about the nets? */ TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) { net->pmtu_timer.ep = (void *)new_inp; net->rxt_timer.ep = (void *)new_inp; net->fr_timer.ep = (void *)new_inp; } SCTP_INP_WUNLOCK(new_inp); SCTP_INP_WUNLOCK(old_inp); } /* sctp_ifap is used to bypass normal local address validation checks */ int sctp_inpcb_bind(struct socket *so, struct sockaddr *addr, struct sctp_ifa *sctp_ifap, struct thread *p) { /* bind a ep to a socket address */ struct sctppcbhead *head; struct sctp_inpcb *inp, *inp_tmp; struct inpcb *ip_inp; int port_reuse_active = 0; int bindall; uint16_t lport; int error; uint32_t vrf_id; lport = 0; error = 0; bindall = 1; inp = (struct sctp_inpcb *)so->so_pcb; ip_inp = (struct inpcb *)so->so_pcb; #ifdef SCTP_DEBUG if (addr) { SCTPDBG(SCTP_DEBUG_PCB1, "Bind called port:%d\n", ntohs(((struct sockaddr_in *)addr)->sin_port)); SCTPDBG(SCTP_DEBUG_PCB1, "Addr :"); SCTPDBG_ADDR(SCTP_DEBUG_PCB1, addr); } #endif if ((inp->sctp_flags & SCTP_PCB_FLAGS_UNBOUND) == 0) { /* already did a bind, subsequent binds NOT allowed ! */ SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PCB, EINVAL); return (EINVAL); } #ifdef INVARIANTS if (p == NULL) panic("null proc/thread"); #endif if (addr != NULL) { switch (addr->sa_family) { case AF_INET: { struct sockaddr_in *sin; /* IPV6_V6ONLY socket? */ if (SCTP_IPV6_V6ONLY(ip_inp)) { SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PCB, EINVAL); return (EINVAL); } if (addr->sa_len != sizeof(*sin)) { SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PCB, EINVAL); return (EINVAL); } sin = (struct sockaddr_in *)addr; lport = sin->sin_port; /* * For LOOPBACK the prison_local_ip4() call * will transmute the ip address to the * proper value. */ if (p && (error = prison_local_ip4(p->td_ucred, &sin->sin_addr)) != 0) { SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PCB, error); return (error); } if (sin->sin_addr.s_addr != INADDR_ANY) { bindall = 0; } break; } #ifdef INET6 case AF_INET6: { /* * Only for pure IPv6 Address. (No IPv4 * Mapped!) */ struct sockaddr_in6 *sin6; sin6 = (struct sockaddr_in6 *)addr; if (addr->sa_len != sizeof(*sin6)) { SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PCB, EINVAL); return (EINVAL); } lport = sin6->sin6_port; /* * For LOOPBACK the prison_local_ip6() call * will transmute the ipv6 address to the * proper value. */ if (p && (error = prison_local_ip6(p->td_ucred, &sin6->sin6_addr, (SCTP_IPV6_V6ONLY(inp) != 0))) != 0) { SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PCB, error); return (error); } if (!IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { bindall = 0; /* KAME hack: embed scopeid */ if (sa6_embedscope(sin6, MODULE_GLOBAL(ip6_use_defzone)) != 0) { SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PCB, EINVAL); return (EINVAL); } } /* this must be cleared for ifa_ifwithaddr() */ sin6->sin6_scope_id = 0; break; } #endif default: SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PCB, EAFNOSUPPORT); return (EAFNOSUPPORT); } } SCTP_INP_INFO_WLOCK(); SCTP_INP_WLOCK(inp); /* Setup a vrf_id to be the default for the non-bind-all case. */ vrf_id = inp->def_vrf_id; /* increase our count due to the unlock we do */ SCTP_INP_INCR_REF(inp); if (lport) { /* * Did the caller specify a port? if so we must see if a ep * already has this one bound. */ /* got to be root to get at low ports */ if (ntohs(lport) < IPPORT_RESERVED) { if (p && (error = priv_check(p, PRIV_NETINET_RESERVEDPORT) )) { SCTP_INP_DECR_REF(inp); SCTP_INP_WUNLOCK(inp); SCTP_INP_INFO_WUNLOCK(); return (error); } } if (p == NULL) { SCTP_INP_DECR_REF(inp); SCTP_INP_WUNLOCK(inp); SCTP_INP_INFO_WUNLOCK(); SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PCB, error); return (error); } SCTP_INP_WUNLOCK(inp); if (bindall) { vrf_id = inp->def_vrf_id; inp_tmp = sctp_pcb_findep(addr, 0, 1, vrf_id); if (inp_tmp != NULL) { /* * lock guy returned and lower count note * that we are not bound so inp_tmp should * NEVER be inp. And it is this inp * (inp_tmp) that gets the reference bump, * so we must lower it. */ SCTP_INP_DECR_REF(inp_tmp); /* unlock info */ if ((sctp_is_feature_on(inp, SCTP_PCB_FLAGS_PORTREUSE)) && (sctp_is_feature_on(inp_tmp, SCTP_PCB_FLAGS_PORTREUSE))) { /* * Ok, must be one-2-one and * allowing port re-use */ port_reuse_active = 1; goto continue_anyway; } SCTP_INP_DECR_REF(inp); SCTP_INP_INFO_WUNLOCK(); SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PCB, EADDRINUSE); return (EADDRINUSE); } } else { inp_tmp = sctp_pcb_findep(addr, 0, 1, vrf_id); if (inp_tmp != NULL) { /* * lock guy returned and lower count note * that we are not bound so inp_tmp should * NEVER be inp. And it is this inp * (inp_tmp) that gets the reference bump, * so we must lower it. */ SCTP_INP_DECR_REF(inp_tmp); /* unlock info */ if ((sctp_is_feature_on(inp, SCTP_PCB_FLAGS_PORTREUSE)) && (sctp_is_feature_on(inp_tmp, SCTP_PCB_FLAGS_PORTREUSE))) { /* * Ok, must be one-2-one and * allowing port re-use */ port_reuse_active = 1; goto continue_anyway; } SCTP_INP_DECR_REF(inp); SCTP_INP_INFO_WUNLOCK(); SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PCB, EADDRINUSE); return (EADDRINUSE); } } continue_anyway: SCTP_INP_WLOCK(inp); if (bindall) { /* verify that no lport is not used by a singleton */ if ((port_reuse_active == 0) && (inp_tmp = sctp_isport_inuse(inp, lport, vrf_id)) ) { /* Sorry someone already has this one bound */ if ((sctp_is_feature_on(inp, SCTP_PCB_FLAGS_PORTREUSE)) && (sctp_is_feature_on(inp_tmp, SCTP_PCB_FLAGS_PORTREUSE))) { port_reuse_active = 1; } else { SCTP_INP_DECR_REF(inp); SCTP_INP_WUNLOCK(inp); SCTP_INP_INFO_WUNLOCK(); SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PCB, EADDRINUSE); return (EADDRINUSE); } } } } else { uint16_t first, last, candidate; uint16_t count; int done; if (ip_inp->inp_flags & INP_HIGHPORT) { first = MODULE_GLOBAL(ipport_hifirstauto); last = MODULE_GLOBAL(ipport_hilastauto); } else if (ip_inp->inp_flags & INP_LOWPORT) { if (p && (error = priv_check(p, PRIV_NETINET_RESERVEDPORT) )) { SCTP_INP_DECR_REF(inp); SCTP_INP_WUNLOCK(inp); SCTP_INP_INFO_WUNLOCK(); SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PCB, error); return (error); } first = MODULE_GLOBAL(ipport_lowfirstauto); last = MODULE_GLOBAL(ipport_lowlastauto); } else { first = MODULE_GLOBAL(ipport_firstauto); last = MODULE_GLOBAL(ipport_lastauto); } if (first > last) { uint16_t temp; temp = first; first = last; last = temp; } count = last - first + 1; /* number of candidates */ candidate = first + sctp_select_initial_TSN(&inp->sctp_ep) % (count); done = 0; while (!done) { if (sctp_isport_inuse(inp, htons(candidate), inp->def_vrf_id) == NULL) { done = 1; } if (!done) { if (--count == 0) { SCTP_INP_DECR_REF(inp); SCTP_INP_WUNLOCK(inp); SCTP_INP_INFO_WUNLOCK(); SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PCB, EADDRINUSE); return (EADDRINUSE); } if (candidate == last) candidate = first; else candidate = candidate + 1; } } lport = htons(candidate); } SCTP_INP_DECR_REF(inp); if (inp->sctp_flags & (SCTP_PCB_FLAGS_SOCKET_GONE | SCTP_PCB_FLAGS_SOCKET_ALLGONE)) { /* * this really should not happen. The guy did a non-blocking * bind and then did a close at the same time. */ SCTP_INP_WUNLOCK(inp); SCTP_INP_INFO_WUNLOCK(); SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PCB, EINVAL); return (EINVAL); } /* ok we look clear to give out this port, so lets setup the binding */ if (bindall) { /* binding to all addresses, so just set in the proper flags */ inp->sctp_flags |= SCTP_PCB_FLAGS_BOUNDALL; /* set the automatic addr changes from kernel flag */ if (SCTP_BASE_SYSCTL(sctp_auto_asconf) == 0) { sctp_feature_off(inp, SCTP_PCB_FLAGS_DO_ASCONF); sctp_feature_off(inp, SCTP_PCB_FLAGS_AUTO_ASCONF); } else { sctp_feature_on(inp, SCTP_PCB_FLAGS_DO_ASCONF); sctp_feature_on(inp, SCTP_PCB_FLAGS_AUTO_ASCONF); } if (SCTP_BASE_SYSCTL(sctp_multiple_asconfs) == 0) { sctp_feature_off(inp, SCTP_PCB_FLAGS_MULTIPLE_ASCONFS); } else { sctp_feature_on(inp, SCTP_PCB_FLAGS_MULTIPLE_ASCONFS); } /* * set the automatic mobility_base from kernel flag (by * micchie) */ if (SCTP_BASE_SYSCTL(sctp_mobility_base) == 0) { sctp_mobility_feature_off(inp, SCTP_MOBILITY_BASE); sctp_mobility_feature_off(inp, SCTP_MOBILITY_PRIM_DELETED); } else { sctp_mobility_feature_on(inp, SCTP_MOBILITY_BASE); sctp_mobility_feature_off(inp, SCTP_MOBILITY_PRIM_DELETED); } /* * set the automatic mobility_fasthandoff from kernel flag * (by micchie) */ if (SCTP_BASE_SYSCTL(sctp_mobility_fasthandoff) == 0) { sctp_mobility_feature_off(inp, SCTP_MOBILITY_FASTHANDOFF); sctp_mobility_feature_off(inp, SCTP_MOBILITY_PRIM_DELETED); } else { sctp_mobility_feature_on(inp, SCTP_MOBILITY_FASTHANDOFF); sctp_mobility_feature_off(inp, SCTP_MOBILITY_PRIM_DELETED); } } else { /* * bind specific, make sure flags is off and add a new * address structure to the sctp_addr_list inside the ep * structure. * * We will need to allocate one and insert it at the head. The * socketopt call can just insert new addresses in there as * well. It will also have to do the embed scope kame hack * too (before adding). */ struct sctp_ifa *ifa; struct sockaddr_storage store_sa; memset(&store_sa, 0, sizeof(store_sa)); if (addr->sa_family == AF_INET) { struct sockaddr_in *sin; sin = (struct sockaddr_in *)&store_sa; memcpy(sin, addr, sizeof(struct sockaddr_in)); sin->sin_port = 0; } else if (addr->sa_family == AF_INET6) { struct sockaddr_in6 *sin6; sin6 = (struct sockaddr_in6 *)&store_sa; memcpy(sin6, addr, sizeof(struct sockaddr_in6)); sin6->sin6_port = 0; } /* * first find the interface with the bound address need to * zero out the port to find the address! yuck! can't do * this earlier since need port for sctp_pcb_findep() */ if (sctp_ifap != NULL) ifa = sctp_ifap; else { /* * Note for BSD we hit here always other O/S's will * pass things in via the sctp_ifap argument * (Panda). */ ifa = sctp_find_ifa_by_addr((struct sockaddr *)&store_sa, vrf_id, SCTP_ADDR_NOT_LOCKED); } if (ifa == NULL) { /* Can't find an interface with that address */ SCTP_INP_WUNLOCK(inp); SCTP_INP_INFO_WUNLOCK(); SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PCB, EADDRNOTAVAIL); return (EADDRNOTAVAIL); } if (addr->sa_family == AF_INET6) { /* GAK, more FIXME IFA lock? */ if (ifa->localifa_flags & SCTP_ADDR_IFA_UNUSEABLE) { /* Can't bind a non-existent addr. */ SCTP_INP_WUNLOCK(inp); SCTP_INP_INFO_WUNLOCK(); SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PCB, EINVAL); return (EINVAL); } } /* we're not bound all */ inp->sctp_flags &= ~SCTP_PCB_FLAGS_BOUNDALL; /* allow bindx() to send ASCONF's for binding changes */ sctp_feature_on(inp, SCTP_PCB_FLAGS_DO_ASCONF); /* clear automatic addr changes from kernel flag */ sctp_feature_off(inp, SCTP_PCB_FLAGS_AUTO_ASCONF); /* add this address to the endpoint list */ error = sctp_insert_laddr(&inp->sctp_addr_list, ifa, 0); if (error != 0) { SCTP_INP_WUNLOCK(inp); SCTP_INP_INFO_WUNLOCK(); return (error); } inp->laddr_count++; } /* find the bucket */ if (port_reuse_active) { /* Put it into tcp 1-2-1 hash */ head = &SCTP_BASE_INFO(sctp_tcpephash)[SCTP_PCBHASH_ALLADDR((lport), SCTP_BASE_INFO(hashtcpmark))]; inp->sctp_flags |= SCTP_PCB_FLAGS_IN_TCPPOOL; } else { head = &SCTP_BASE_INFO(sctp_ephash)[SCTP_PCBHASH_ALLADDR(lport, SCTP_BASE_INFO(hashmark))]; } /* put it in the bucket */ LIST_INSERT_HEAD(head, inp, sctp_hash); SCTPDBG(SCTP_DEBUG_PCB1, "Main hash to bind at head:%p, bound port:%d - in tcp_pool=%d\n", head, ntohs(lport), port_reuse_active); /* set in the port */ inp->sctp_lport = lport; /* turn off just the unbound flag */ inp->sctp_flags &= ~SCTP_PCB_FLAGS_UNBOUND; SCTP_INP_WUNLOCK(inp); SCTP_INP_INFO_WUNLOCK(); return (0); } static void sctp_iterator_inp_being_freed(struct sctp_inpcb *inp, struct sctp_inpcb *inp_next) { struct sctp_iterator *it; /* * We enter with the only the ITERATOR_LOCK in place and a write * lock on the inp_info stuff. */ /* * Go through all iterators, we must do this since it is possible * that some iterator does NOT have the lock, but is waiting for it. * And the one that had the lock has either moved in the last * iteration or we just cleared it above. We need to find all of * those guys. The list of iterators should never be very big * though. */ TAILQ_FOREACH(it, &SCTP_BASE_INFO(iteratorhead), sctp_nxt_itr) { if (it == inp->inp_starting_point_for_iterator) /* skip this guy, he's special */ continue; if (it->inp == inp) { /* * This is tricky and we DON'T lock the iterator. * Reason is he's running but waiting for me since * inp->inp_starting_point_for_iterator has the lock * on me (the guy above we skipped). This tells us * its is not running but waiting for * inp->inp_starting_point_for_iterator to be * released by the guy that does have our INP in a * lock. */ if (it->iterator_flags & SCTP_ITERATOR_DO_SINGLE_INP) { it->inp = NULL; it->stcb = NULL; } else { /* set him up to do the next guy not me */ it->inp = inp_next; it->stcb = NULL; } } } it = inp->inp_starting_point_for_iterator; if (it) { if (it->iterator_flags & SCTP_ITERATOR_DO_SINGLE_INP) { it->inp = NULL; } else { it->inp = inp_next; } it->stcb = NULL; } } /* release sctp_inpcb unbind the port */ void sctp_inpcb_free(struct sctp_inpcb *inp, int immediate, int from) { /* * Here we free a endpoint. We must find it (if it is in the Hash * table) and remove it from there. Then we must also find it in the * overall list and remove it from there. After all removals are * complete then any timer has to be stopped. Then start the actual * freeing. a) Any local lists. b) Any associations. c) The hash of * all associations. d) finally the ep itself. */ struct sctp_pcb *m; struct sctp_inpcb *inp_save; struct sctp_tcb *asoc, *nasoc; struct sctp_laddr *laddr, *nladdr; struct inpcb *ip_pcb; struct socket *so; struct sctp_queued_to_read *sq; int cnt; sctp_sharedkey_t *shared_key; #ifdef SCTP_LOG_CLOSING sctp_log_closing(inp, NULL, 0); #endif SCTP_ITERATOR_LOCK(); so = inp->sctp_socket; if (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_ALLGONE) { /* been here before.. eeks.. get out of here */ SCTP_PRINTF("This conflict in free SHOULD not be happening! from %d, imm %d\n", from, immediate); SCTP_ITERATOR_UNLOCK(); #ifdef SCTP_LOG_CLOSING sctp_log_closing(inp, NULL, 1); #endif return; } SCTP_ASOC_CREATE_LOCK(inp); SCTP_INP_INFO_WLOCK(); SCTP_INP_WLOCK(inp); /* First time through we have the socket lock, after that no more. */ if (from == SCTP_CALLED_AFTER_CMPSET_OFCLOSE) { /* * Once we are in we can remove the flag from = 1 is only * passed from the actual closing routines that are called * via the sockets layer. */ inp->sctp_flags &= ~SCTP_PCB_FLAGS_CLOSE_IP; /* socket is gone, so no more wakeups allowed */ inp->sctp_flags |= SCTP_PCB_FLAGS_DONT_WAKE; inp->sctp_flags &= ~SCTP_PCB_FLAGS_WAKEINPUT; inp->sctp_flags &= ~SCTP_PCB_FLAGS_WAKEOUTPUT; } sctp_timer_stop(SCTP_TIMER_TYPE_NEWCOOKIE, inp, NULL, NULL, SCTP_FROM_SCTP_PCB + SCTP_LOC_1); if (inp->control) { sctp_m_freem(inp->control); inp->control = NULL; } if (inp->pkt) { sctp_m_freem(inp->pkt); inp->pkt = NULL; } m = &inp->sctp_ep; ip_pcb = &inp->ip_inp.inp; /* we could just cast the main pointer * here but I will be nice :> (i.e. * ip_pcb = ep;) */ if (immediate == SCTP_FREE_SHOULD_USE_GRACEFUL_CLOSE) { int cnt_in_sd; cnt_in_sd = 0; for ((asoc = LIST_FIRST(&inp->sctp_asoc_list)); asoc != NULL; asoc = nasoc) { SCTP_TCB_LOCK(asoc); nasoc = LIST_NEXT(asoc, sctp_tcblist); if (asoc->asoc.state & SCTP_STATE_ABOUT_TO_BE_FREED) { /* Skip guys being freed */ /* asoc->sctp_socket = NULL; FIXME MT */ cnt_in_sd++; SCTP_TCB_UNLOCK(asoc); continue; } if (((SCTP_GET_STATE(&asoc->asoc) == SCTP_STATE_COOKIE_WAIT) || (SCTP_GET_STATE(&asoc->asoc) == SCTP_STATE_COOKIE_ECHOED)) && (asoc->asoc.total_output_queue_size == 0)) { /* * If we have data in queue, we don't want * to just free since the app may have done, * send()/close or connect/send/close. And * it wants the data to get across first. */ /* Just abandon things in the front states */ if (sctp_free_assoc(inp, asoc, SCTP_PCBFREE_NOFORCE, SCTP_FROM_SCTP_PCB + SCTP_LOC_2) == 0) { cnt_in_sd++; } continue; } /* Disconnect the socket please */ asoc->sctp_socket = NULL; asoc->asoc.state |= SCTP_STATE_CLOSED_SOCKET; if ((asoc->asoc.size_on_reasm_queue > 0) || (asoc->asoc.control_pdapi) || (asoc->asoc.size_on_all_streams > 0) || (so && (so->so_rcv.sb_cc > 0)) ) { /* Left with Data unread */ struct mbuf *op_err; op_err = sctp_get_mbuf_for_msg((sizeof(struct sctp_paramhdr) + sizeof(uint32_t)), 0, M_DONTWAIT, 1, MT_DATA); if (op_err) { /* Fill in the user initiated abort */ struct sctp_paramhdr *ph; uint32_t *ippp; SCTP_BUF_LEN(op_err) = sizeof(struct sctp_paramhdr) + sizeof(uint32_t); ph = mtod(op_err, struct sctp_paramhdr *); ph->param_type = htons( SCTP_CAUSE_USER_INITIATED_ABT); ph->param_length = htons(SCTP_BUF_LEN(op_err)); ippp = (uint32_t *) (ph + 1); *ippp = htonl(SCTP_FROM_SCTP_PCB + SCTP_LOC_3); } asoc->sctp_ep->last_abort_code = SCTP_FROM_SCTP_PCB + SCTP_LOC_3; #if defined(SCTP_PANIC_ON_ABORT) panic("inpcb_free does an abort"); #endif sctp_send_abort_tcb(asoc, op_err, SCTP_SO_LOCKED); SCTP_STAT_INCR_COUNTER32(sctps_aborted); if ((SCTP_GET_STATE(&asoc->asoc) == SCTP_STATE_OPEN) || (SCTP_GET_STATE(&asoc->asoc) == SCTP_STATE_SHUTDOWN_RECEIVED)) { SCTP_STAT_DECR_GAUGE32(sctps_currestab); } if (sctp_free_assoc(inp, asoc, SCTP_PCBFREE_NOFORCE, SCTP_FROM_SCTP_PCB + SCTP_LOC_4) == 0) { cnt_in_sd++; } continue; } else if (TAILQ_EMPTY(&asoc->asoc.send_queue) && TAILQ_EMPTY(&asoc->asoc.sent_queue) && (asoc->asoc.stream_queue_cnt == 0) ) { if (asoc->asoc.locked_on_sending) { goto abort_anyway; } if ((SCTP_GET_STATE(&asoc->asoc) != SCTP_STATE_SHUTDOWN_SENT) && (SCTP_GET_STATE(&asoc->asoc) != SCTP_STATE_SHUTDOWN_ACK_SENT)) { /* * there is nothing queued to send, * so I send shutdown */ sctp_send_shutdown(asoc, asoc->asoc.primary_destination); if ((SCTP_GET_STATE(&asoc->asoc) == SCTP_STATE_OPEN) || (SCTP_GET_STATE(&asoc->asoc) == SCTP_STATE_SHUTDOWN_RECEIVED)) { SCTP_STAT_DECR_GAUGE32(sctps_currestab); } SCTP_SET_STATE(&asoc->asoc, SCTP_STATE_SHUTDOWN_SENT); SCTP_CLEAR_SUBSTATE(&asoc->asoc, SCTP_STATE_SHUTDOWN_PENDING); sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWN, asoc->sctp_ep, asoc, asoc->asoc.primary_destination); sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWNGUARD, asoc->sctp_ep, asoc, asoc->asoc.primary_destination); sctp_chunk_output(inp, asoc, SCTP_OUTPUT_FROM_SHUT_TMR, SCTP_SO_LOCKED); } } else { /* mark into shutdown pending */ struct sctp_stream_queue_pending *sp; asoc->asoc.state |= SCTP_STATE_SHUTDOWN_PENDING; sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWNGUARD, asoc->sctp_ep, asoc, asoc->asoc.primary_destination); if (asoc->asoc.locked_on_sending) { sp = TAILQ_LAST(&((asoc->asoc.locked_on_sending)->outqueue), sctp_streamhead); if (sp == NULL) { SCTP_PRINTF("Error, sp is NULL, locked on sending is %p strm:%d\n", asoc->asoc.locked_on_sending, asoc->asoc.locked_on_sending->stream_no); } else { if ((sp->length == 0) && (sp->msg_is_complete == 0)) asoc->asoc.state |= SCTP_STATE_PARTIAL_MSG_LEFT; } } if (TAILQ_EMPTY(&asoc->asoc.send_queue) && TAILQ_EMPTY(&asoc->asoc.sent_queue) && (asoc->asoc.state & SCTP_STATE_PARTIAL_MSG_LEFT)) { struct mbuf *op_err; abort_anyway: op_err = sctp_get_mbuf_for_msg((sizeof(struct sctp_paramhdr) + sizeof(uint32_t)), 0, M_DONTWAIT, 1, MT_DATA); if (op_err) { /* * Fill in the user * initiated abort */ struct sctp_paramhdr *ph; uint32_t *ippp; SCTP_BUF_LEN(op_err) = (sizeof(struct sctp_paramhdr) + sizeof(uint32_t)); ph = mtod(op_err, struct sctp_paramhdr *); ph->param_type = htons( SCTP_CAUSE_USER_INITIATED_ABT); ph->param_length = htons(SCTP_BUF_LEN(op_err)); ippp = (uint32_t *) (ph + 1); *ippp = htonl(SCTP_FROM_SCTP_PCB + SCTP_LOC_5); } asoc->sctp_ep->last_abort_code = SCTP_FROM_SCTP_PCB + SCTP_LOC_5; #if defined(SCTP_PANIC_ON_ABORT) panic("inpcb_free does an abort"); #endif sctp_send_abort_tcb(asoc, op_err, SCTP_SO_LOCKED); SCTP_STAT_INCR_COUNTER32(sctps_aborted); if ((SCTP_GET_STATE(&asoc->asoc) == SCTP_STATE_OPEN) || (SCTP_GET_STATE(&asoc->asoc) == SCTP_STATE_SHUTDOWN_RECEIVED)) { SCTP_STAT_DECR_GAUGE32(sctps_currestab); } if (sctp_free_assoc(inp, asoc, SCTP_PCBFREE_NOFORCE, SCTP_FROM_SCTP_PCB + SCTP_LOC_6) == 0) { cnt_in_sd++; } continue; } else { sctp_chunk_output(inp, asoc, SCTP_OUTPUT_FROM_CLOSING, SCTP_SO_LOCKED); } } cnt_in_sd++; SCTP_TCB_UNLOCK(asoc); } /* now is there some left in our SHUTDOWN state? */ if (cnt_in_sd) { SCTP_INP_WUNLOCK(inp); SCTP_ASOC_CREATE_UNLOCK(inp); SCTP_INP_INFO_WUNLOCK(); SCTP_ITERATOR_UNLOCK(); #ifdef SCTP_LOG_CLOSING sctp_log_closing(inp, NULL, 2); #endif inp->sctp_socket = NULL; return; } } inp->sctp_socket = NULL; if ((inp->sctp_flags & SCTP_PCB_FLAGS_UNBOUND) != SCTP_PCB_FLAGS_UNBOUND) { /* * ok, this guy has been bound. It's port is somewhere in * the SCTP_BASE_INFO(hash table). Remove it! */ LIST_REMOVE(inp, sctp_hash); inp->sctp_flags |= SCTP_PCB_FLAGS_UNBOUND; } /* * If there is a timer running to kill us, forget it, since it may * have a contest on the INP lock.. which would cause us to die ... */ cnt = 0; for ((asoc = LIST_FIRST(&inp->sctp_asoc_list)); asoc != NULL; asoc = nasoc) { nasoc = LIST_NEXT(asoc, sctp_tcblist); if (asoc->asoc.state & SCTP_STATE_ABOUT_TO_BE_FREED) { cnt++; continue; } /* Free associations that are NOT killing us */ SCTP_TCB_LOCK(asoc); if ((SCTP_GET_STATE(&asoc->asoc) != SCTP_STATE_COOKIE_WAIT) && ((asoc->asoc.state & SCTP_STATE_ABOUT_TO_BE_FREED) == 0)) { struct mbuf *op_err; uint32_t *ippp; op_err = sctp_get_mbuf_for_msg((sizeof(struct sctp_paramhdr) + sizeof(uint32_t)), 0, M_DONTWAIT, 1, MT_DATA); if (op_err) { /* Fill in the user initiated abort */ struct sctp_paramhdr *ph; SCTP_BUF_LEN(op_err) = (sizeof(struct sctp_paramhdr) + sizeof(uint32_t)); ph = mtod(op_err, struct sctp_paramhdr *); ph->param_type = htons( SCTP_CAUSE_USER_INITIATED_ABT); ph->param_length = htons(SCTP_BUF_LEN(op_err)); ippp = (uint32_t *) (ph + 1); *ippp = htonl(SCTP_FROM_SCTP_PCB + SCTP_LOC_7); } asoc->sctp_ep->last_abort_code = SCTP_FROM_SCTP_PCB + SCTP_LOC_7; #if defined(SCTP_PANIC_ON_ABORT) panic("inpcb_free does an abort"); #endif sctp_send_abort_tcb(asoc, op_err, SCTP_SO_LOCKED); SCTP_STAT_INCR_COUNTER32(sctps_aborted); } else if (asoc->asoc.state & SCTP_STATE_ABOUT_TO_BE_FREED) { cnt++; SCTP_TCB_UNLOCK(asoc); continue; } if ((SCTP_GET_STATE(&asoc->asoc) == SCTP_STATE_OPEN) || (SCTP_GET_STATE(&asoc->asoc) == SCTP_STATE_SHUTDOWN_RECEIVED)) { SCTP_STAT_DECR_GAUGE32(sctps_currestab); } if (sctp_free_assoc(inp, asoc, SCTP_PCBFREE_FORCE, SCTP_FROM_SCTP_PCB + SCTP_LOC_8) == 0) { cnt++; } } if (cnt) { /* Ok we have someone out there that will kill us */ (void)SCTP_OS_TIMER_STOP(&inp->sctp_ep.signature_change.timer); SCTP_INP_WUNLOCK(inp); SCTP_ASOC_CREATE_UNLOCK(inp); SCTP_INP_INFO_WUNLOCK(); SCTP_ITERATOR_UNLOCK(); #ifdef SCTP_LOG_CLOSING sctp_log_closing(inp, NULL, 3); #endif return; } if ((inp->refcount) || (inp->sctp_flags & SCTP_PCB_FLAGS_CLOSE_IP)) { (void)SCTP_OS_TIMER_STOP(&inp->sctp_ep.signature_change.timer); sctp_timer_start(SCTP_TIMER_TYPE_INPKILL, inp, NULL, NULL); SCTP_INP_WUNLOCK(inp); SCTP_ASOC_CREATE_UNLOCK(inp); SCTP_INP_INFO_WUNLOCK(); SCTP_ITERATOR_UNLOCK(); #ifdef SCTP_LOG_CLOSING sctp_log_closing(inp, NULL, 4); #endif return; } (void)SCTP_OS_TIMER_STOP(&inp->sctp_ep.signature_change.timer); inp->sctp_ep.signature_change.type = 0; inp->sctp_flags |= SCTP_PCB_FLAGS_SOCKET_ALLGONE; #ifdef SCTP_LOG_CLOSING sctp_log_closing(inp, NULL, 5); #endif (void)SCTP_OS_TIMER_STOP(&inp->sctp_ep.signature_change.timer); inp->sctp_ep.signature_change.type = SCTP_TIMER_TYPE_NONE; /* Clear the read queue */ if ((inp->sctp_asocidhash) != NULL) { SCTP_HASH_FREE(inp->sctp_asocidhash, inp->hashasocidmark); inp->sctp_asocidhash = NULL; } /* sa_ignore FREED_MEMORY */ while ((sq = TAILQ_FIRST(&inp->read_queue)) != NULL) { /* Its only abandoned if it had data left */ if (sq->length) SCTP_STAT_INCR(sctps_left_abandon); TAILQ_REMOVE(&inp->read_queue, sq, next); sctp_free_remote_addr(sq->whoFrom); if (so) so->so_rcv.sb_cc -= sq->length; if (sq->data) { sctp_m_freem(sq->data); sq->data = NULL; } /* * no need to free the net count, since at this point all * assoc's are gone. */ SCTP_ZONE_FREE(SCTP_BASE_INFO(ipi_zone_readq), sq); SCTP_DECR_READQ_COUNT(); } /* Now the sctp_pcb things */ /* * free each asoc if it is not already closed/free. we can't use the * macro here since le_next will get freed as part of the * sctp_free_assoc() call. */ cnt = 0; if (so) { #ifdef IPSEC ipsec_delete_pcbpolicy(ip_pcb); #endif /* IPSEC */ /* Unlocks not needed since the socket is gone now */ } if (ip_pcb->inp_options) { (void)sctp_m_free(ip_pcb->inp_options); ip_pcb->inp_options = 0; } if (ip_pcb->inp_moptions) { inp_freemoptions(ip_pcb->inp_moptions); ip_pcb->inp_moptions = 0; } #ifdef INET6 if (ip_pcb->inp_vflag & INP_IPV6) { struct in6pcb *in6p; in6p = (struct in6pcb *)inp; ip6_freepcbopts(in6p->in6p_outputopts); } #endif /* INET6 */ ip_pcb->inp_vflag = 0; /* free up authentication fields */ if (inp->sctp_ep.local_auth_chunks != NULL) sctp_free_chunklist(inp->sctp_ep.local_auth_chunks); if (inp->sctp_ep.local_hmacs != NULL) sctp_free_hmaclist(inp->sctp_ep.local_hmacs); shared_key = LIST_FIRST(&inp->sctp_ep.shared_keys); while (shared_key) { LIST_REMOVE(shared_key, next); sctp_free_sharedkey(shared_key); /* sa_ignore FREED_MEMORY */ shared_key = LIST_FIRST(&inp->sctp_ep.shared_keys); } inp_save = LIST_NEXT(inp, sctp_list); LIST_REMOVE(inp, sctp_list); /* fix any iterators only after out of the list */ sctp_iterator_inp_being_freed(inp, inp_save); /* * if we have an address list the following will free the list of * ifaddr's that are set into this ep. Again macro limitations here, * since the LIST_FOREACH could be a bad idea. */ for ((laddr = LIST_FIRST(&inp->sctp_addr_list)); laddr != NULL; laddr = nladdr) { nladdr = LIST_NEXT(laddr, sctp_nxt_addr); sctp_remove_laddr(laddr); } #ifdef SCTP_TRACK_FREED_ASOCS /* TEMP CODE */ for ((asoc = LIST_FIRST(&inp->sctp_asoc_free_list)); asoc != NULL; asoc = nasoc) { nasoc = LIST_NEXT(asoc, sctp_tcblist); LIST_REMOVE(asoc, sctp_tcblist); SCTP_ZONE_FREE(SCTP_BASE_INFO(ipi_zone_asoc), asoc); SCTP_DECR_ASOC_COUNT(); } /* *** END TEMP CODE *** */ #endif /* Now lets see about freeing the EP hash table. */ if (inp->sctp_tcbhash != NULL) { SCTP_HASH_FREE(inp->sctp_tcbhash, inp->sctp_hashmark); inp->sctp_tcbhash = NULL; } /* Now we must put the ep memory back into the zone pool */ INP_LOCK_DESTROY(&inp->ip_inp.inp); SCTP_INP_LOCK_DESTROY(inp); SCTP_INP_READ_DESTROY(inp); SCTP_ASOC_CREATE_LOCK_DESTROY(inp); SCTP_INP_INFO_WUNLOCK(); SCTP_ITERATOR_UNLOCK(); SCTP_ZONE_FREE(SCTP_BASE_INFO(ipi_zone_ep), inp); SCTP_DECR_EP_COUNT(); } struct sctp_nets * sctp_findnet(struct sctp_tcb *stcb, struct sockaddr *addr) { struct sctp_nets *net; /* locate the address */ TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) { if (sctp_cmpaddr(addr, (struct sockaddr *)&net->ro._l_addr)) return (net); } return (NULL); } int sctp_is_address_on_local_host(struct sockaddr *addr, uint32_t vrf_id) { struct sctp_ifa *sctp_ifa; sctp_ifa = sctp_find_ifa_by_addr(addr, vrf_id, SCTP_ADDR_NOT_LOCKED); if (sctp_ifa) { return (1); } else { return (0); } } /* * add's a remote endpoint address, done with the INIT/INIT-ACK as well as * when a ASCONF arrives that adds it. It will also initialize all the cwnd * stats of stuff. */ int sctp_add_remote_addr(struct sctp_tcb *stcb, struct sockaddr *newaddr, int set_scope, int from) { /* * The following is redundant to the same lines in the * sctp_aloc_assoc() but is needed since other's call the add * address function */ struct sctp_nets *net, *netfirst; int addr_inscope; SCTPDBG(SCTP_DEBUG_PCB1, "Adding an address (from:%d) to the peer: ", from); SCTPDBG_ADDR(SCTP_DEBUG_PCB1, newaddr); netfirst = sctp_findnet(stcb, newaddr); if (netfirst) { /* * Lie and return ok, we don't want to make the association * go away for this behavior. It will happen in the TCP * model in a connected socket. It does not reach the hash * table until after the association is built so it can't be * found. Mark as reachable, since the initial creation will * have been cleared and the NOT_IN_ASSOC flag will have * been added... and we don't want to end up removing it * back out. */ if (netfirst->dest_state & SCTP_ADDR_UNCONFIRMED) { netfirst->dest_state = (SCTP_ADDR_REACHABLE | SCTP_ADDR_UNCONFIRMED); } else { netfirst->dest_state = SCTP_ADDR_REACHABLE; } return (0); } addr_inscope = 1; if (newaddr->sa_family == AF_INET) { struct sockaddr_in *sin; sin = (struct sockaddr_in *)newaddr; if (sin->sin_addr.s_addr == 0) { /* Invalid address */ return (-1); } /* zero out the bzero area */ memset(&sin->sin_zero, 0, sizeof(sin->sin_zero)); /* assure len is set */ sin->sin_len = sizeof(struct sockaddr_in); if (set_scope) { #ifdef SCTP_DONT_DO_PRIVADDR_SCOPE stcb->ipv4_local_scope = 1; #else if (IN4_ISPRIVATE_ADDRESS(&sin->sin_addr)) { stcb->asoc.ipv4_local_scope = 1; } #endif /* SCTP_DONT_DO_PRIVADDR_SCOPE */ } else { /* Validate the address is in scope */ if ((IN4_ISPRIVATE_ADDRESS(&sin->sin_addr)) && (stcb->asoc.ipv4_local_scope == 0)) { addr_inscope = 0; } } #ifdef INET6 } else if (newaddr->sa_family == AF_INET6) { struct sockaddr_in6 *sin6; sin6 = (struct sockaddr_in6 *)newaddr; if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { /* Invalid address */ return (-1); } /* assure len is set */ sin6->sin6_len = sizeof(struct sockaddr_in6); if (set_scope) { if (sctp_is_address_on_local_host(newaddr, stcb->asoc.vrf_id)) { stcb->asoc.loopback_scope = 1; stcb->asoc.local_scope = 0; stcb->asoc.ipv4_local_scope = 1; stcb->asoc.site_scope = 1; } else if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) { /* * If the new destination is a LINK_LOCAL we * must have common site scope. Don't set * the local scope since we may not share * all links, only loopback can do this. * Links on the local network would also be * on our private network for v4 too. */ stcb->asoc.ipv4_local_scope = 1; stcb->asoc.site_scope = 1; } else if (IN6_IS_ADDR_SITELOCAL(&sin6->sin6_addr)) { /* * If the new destination is SITE_LOCAL then * we must have site scope in common. */ stcb->asoc.site_scope = 1; } } else { /* Validate the address is in scope */ if (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr) && (stcb->asoc.loopback_scope == 0)) { addr_inscope = 0; } else if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr) && (stcb->asoc.local_scope == 0)) { addr_inscope = 0; } else if (IN6_IS_ADDR_SITELOCAL(&sin6->sin6_addr) && (stcb->asoc.site_scope == 0)) { addr_inscope = 0; } } #endif } else { /* not supported family type */ return (-1); } net = SCTP_ZONE_GET(SCTP_BASE_INFO(ipi_zone_net), struct sctp_nets); if (net == NULL) { return (-1); } SCTP_INCR_RADDR_COUNT(); bzero(net, sizeof(*net)); (void)SCTP_GETTIME_TIMEVAL(&net->start_time); memcpy(&net->ro._l_addr, newaddr, newaddr->sa_len); if (newaddr->sa_family == AF_INET) { ((struct sockaddr_in *)&net->ro._l_addr)->sin_port = stcb->rport; } else if (newaddr->sa_family == AF_INET6) { ((struct sockaddr_in6 *)&net->ro._l_addr)->sin6_port = stcb->rport; } net->addr_is_local = sctp_is_address_on_local_host(newaddr, stcb->asoc.vrf_id); if (net->addr_is_local && ((set_scope || (from == SCTP_ADDR_IS_CONFIRMED)))) { stcb->asoc.loopback_scope = 1; stcb->asoc.ipv4_local_scope = 1; stcb->asoc.local_scope = 0; stcb->asoc.site_scope = 1; addr_inscope = 1; } net->failure_threshold = stcb->asoc.def_net_failure; if (addr_inscope == 0) { net->dest_state = (SCTP_ADDR_REACHABLE | SCTP_ADDR_OUT_OF_SCOPE); } else { if (from == SCTP_ADDR_IS_CONFIRMED) /* SCTP_ADDR_IS_CONFIRMED is passed by connect_x */ net->dest_state = SCTP_ADDR_REACHABLE; else net->dest_state = SCTP_ADDR_REACHABLE | SCTP_ADDR_UNCONFIRMED; } /* * We set this to 0, the timer code knows that this means its an * initial value */ net->RTO = 0; net->RTO_measured = 0; stcb->asoc.numnets++; *(&net->ref_count) = 1; net->tos_flowlabel = 0; if (SCTP_BASE_SYSCTL(sctp_udp_tunneling_for_client_enable)) { net->port = htons(SCTP_BASE_SYSCTL(sctp_udp_tunneling_port)); } else { net->port = 0; } #ifdef INET if (newaddr->sa_family == AF_INET) net->tos_flowlabel = stcb->asoc.default_tos; #endif #ifdef INET6 if (newaddr->sa_family == AF_INET6) net->tos_flowlabel = stcb->asoc.default_flowlabel; #endif /* Init the timer structure */ SCTP_OS_TIMER_INIT(&net->rxt_timer.timer); SCTP_OS_TIMER_INIT(&net->fr_timer.timer); SCTP_OS_TIMER_INIT(&net->pmtu_timer.timer); /* Now generate a route for this guy */ #ifdef INET6 /* KAME hack: embed scopeid */ if (newaddr->sa_family == AF_INET6) { struct sockaddr_in6 *sin6; sin6 = (struct sockaddr_in6 *)&net->ro._l_addr; (void)sa6_embedscope(sin6, MODULE_GLOBAL(ip6_use_defzone)); sin6->sin6_scope_id = 0; } #endif SCTP_RTALLOC((sctp_route_t *) & net->ro, stcb->asoc.vrf_id); if (SCTP_ROUTE_HAS_VALID_IFN(&net->ro)) { /* Get source address */ net->ro._s_addr = sctp_source_address_selection(stcb->sctp_ep, stcb, (sctp_route_t *) & net->ro, net, 0, stcb->asoc.vrf_id); /* Now get the interface MTU */ if (net->ro._s_addr && net->ro._s_addr->ifn_p) { net->mtu = SCTP_GATHER_MTU_FROM_INTFC(net->ro._s_addr->ifn_p); } else { net->mtu = 0; } #ifdef SCTP_PRINT_FOR_B_AND_M SCTP_PRINTF("We have found an interface mtu of %d\n", net->mtu); #endif if (net->mtu == 0) { /* Huh ?? */ net->mtu = SCTP_DEFAULT_MTU; } else { uint32_t rmtu; rmtu = SCTP_GATHER_MTU_FROM_ROUTE(net->ro._s_addr, &net->ro._l_addr.sa, net->ro.ro_rt); #ifdef SCTP_PRINT_FOR_B_AND_M SCTP_PRINTF("The route mtu is %d\n", rmtu); #endif if (rmtu == 0) { /* * Start things off to match mtu of * interface please. */ SCTP_SET_MTU_OF_ROUTE(&net->ro._l_addr.sa, net->ro.ro_rt, net->mtu); } else { /* * we take the route mtu over the interface, * since the route may be leading out the * loopback, or a different interface. */ net->mtu = rmtu; } } if (from == SCTP_ALLOC_ASOC) { #ifdef SCTP_PRINT_FOR_B_AND_M SCTP_PRINTF("New assoc sets mtu to :%d\n", net->mtu); #endif stcb->asoc.smallest_mtu = net->mtu; } } else { net->mtu = stcb->asoc.smallest_mtu; } #ifdef INET6 if (newaddr->sa_family == AF_INET6) { struct sockaddr_in6 *sin6; sin6 = (struct sockaddr_in6 *)&net->ro._l_addr; (void)sa6_recoverscope(sin6); } #endif if (net->port) { net->mtu -= sizeof(struct udphdr); } if (stcb->asoc.smallest_mtu > net->mtu) { #ifdef SCTP_PRINT_FOR_B_AND_M SCTP_PRINTF("new address mtu:%d smaller than smallest:%d\n", net->mtu, stcb->asoc.smallest_mtu); #endif stcb->asoc.smallest_mtu = net->mtu; } /* JRS - Use the congestion control given in the CC module */ stcb->asoc.cc_functions.sctp_set_initial_cc_param(stcb, net); /* * CMT: CUC algo - set find_pseudo_cumack to TRUE (1) at beginning * of assoc (2005/06/27, iyengar@cis.udel.edu) */ net->find_pseudo_cumack = 1; net->find_rtx_pseudo_cumack = 1; net->src_addr_selected = 0; netfirst = TAILQ_FIRST(&stcb->asoc.nets); if (net->ro.ro_rt == NULL) { /* Since we have no route put it at the back */ TAILQ_INSERT_TAIL(&stcb->asoc.nets, net, sctp_next); } else if (netfirst == NULL) { /* We are the first one in the pool. */ TAILQ_INSERT_HEAD(&stcb->asoc.nets, net, sctp_next); } else if (netfirst->ro.ro_rt == NULL) { /* * First one has NO route. Place this one ahead of the first * one. */ TAILQ_INSERT_HEAD(&stcb->asoc.nets, net, sctp_next); } else if (net->ro.ro_rt->rt_ifp != netfirst->ro.ro_rt->rt_ifp) { /* * This one has a different interface than the one at the * top of the list. Place it ahead. */ TAILQ_INSERT_HEAD(&stcb->asoc.nets, net, sctp_next); } else { /* * Ok we have the same interface as the first one. Move * forward until we find either a) one with a NULL route... * insert ahead of that b) one with a different ifp.. insert * after that. c) end of the list.. insert at the tail. */ struct sctp_nets *netlook; do { netlook = TAILQ_NEXT(netfirst, sctp_next); if (netlook == NULL) { /* End of the list */ TAILQ_INSERT_TAIL(&stcb->asoc.nets, net, sctp_next); break; } else if (netlook->ro.ro_rt == NULL) { /* next one has NO route */ TAILQ_INSERT_BEFORE(netfirst, net, sctp_next); break; } else if (netlook->ro.ro_rt->rt_ifp != net->ro.ro_rt->rt_ifp) { TAILQ_INSERT_AFTER(&stcb->asoc.nets, netlook, net, sctp_next); break; } /* Shift forward */ netfirst = netlook; } while (netlook != NULL); } /* got to have a primary set */ if (stcb->asoc.primary_destination == 0) { stcb->asoc.primary_destination = net; } else if ((stcb->asoc.primary_destination->ro.ro_rt == NULL) && (net->ro.ro_rt) && ((net->dest_state & SCTP_ADDR_UNCONFIRMED) == 0)) { /* No route to current primary adopt new primary */ stcb->asoc.primary_destination = net; } sctp_timer_start(SCTP_TIMER_TYPE_PATHMTURAISE, stcb->sctp_ep, stcb, net); /* Validate primary is first */ net = TAILQ_FIRST(&stcb->asoc.nets); if ((net != stcb->asoc.primary_destination) && (stcb->asoc.primary_destination)) { /* * first one on the list is NOT the primary sctp_cmpaddr() * is much more efficent if the primary is the first on the * list, make it so. */ TAILQ_REMOVE(&stcb->asoc.nets, stcb->asoc.primary_destination, sctp_next); TAILQ_INSERT_HEAD(&stcb->asoc.nets, stcb->asoc.primary_destination, sctp_next); } return (0); } static uint32_t sctp_aloc_a_assoc_id(struct sctp_inpcb *inp, struct sctp_tcb *stcb) { uint32_t id; struct sctpasochead *head; struct sctp_tcb *lstcb; SCTP_INP_WLOCK(inp); try_again: if (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_ALLGONE) { /* TSNH */ SCTP_INP_WUNLOCK(inp); return (0); } /* * We don't allow assoc id to be 0, this is needed otherwise if the * id were to wrap we would have issues with some socket options. */ if (inp->sctp_associd_counter == 0) { inp->sctp_associd_counter++; } id = inp->sctp_associd_counter; inp->sctp_associd_counter++; lstcb = sctp_findasoc_ep_asocid_locked(inp, (sctp_assoc_t) id, 0); if (lstcb) { goto try_again; } head = &inp->sctp_asocidhash[SCTP_PCBHASH_ASOC(id, inp->hashasocidmark)]; LIST_INSERT_HEAD(head, stcb, sctp_tcbasocidhash); stcb->asoc.in_asocid_hash = 1; SCTP_INP_WUNLOCK(inp); return id; } /* * allocate an association and add it to the endpoint. The caller must be * careful to add all additional addresses once they are know right away or * else the assoc will be may experience a blackout scenario. */ struct sctp_tcb * sctp_aloc_assoc(struct sctp_inpcb *inp, struct sockaddr *firstaddr, int for_a_init, int *error, uint32_t override_tag, uint32_t vrf_id, struct thread *p ) { /* note the p argument is only valid in unbound sockets */ struct sctp_tcb *stcb; struct sctp_association *asoc; struct sctpasochead *head; uint16_t rport; int err; /* * Assumption made here: Caller has done a * sctp_findassociation_ep_addr(ep, addr's); to make sure the * address does not exist already. */ if (SCTP_BASE_INFO(ipi_count_asoc) >= SCTP_MAX_NUM_OF_ASOC) { /* Hit max assoc, sorry no more */ SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PCB, ENOBUFS); *error = ENOBUFS; return (NULL); } if (firstaddr == NULL) { SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PCB, EINVAL); *error = EINVAL; return (NULL); } SCTP_INP_RLOCK(inp); if ((inp->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL) && ((sctp_is_feature_off(inp, SCTP_PCB_FLAGS_PORTREUSE)) || (inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED))) { /* * If its in the TCP pool, its NOT allowed to create an * association. The parent listener needs to call * sctp_aloc_assoc.. or the one-2-many socket. If a peeled * off, or connected one does this.. its an error. */ SCTP_INP_RUNLOCK(inp); SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PCB, EINVAL); *error = EINVAL; return (NULL); } SCTPDBG(SCTP_DEBUG_PCB3, "Allocate an association for peer:"); #ifdef SCTP_DEBUG if (firstaddr) { SCTPDBG_ADDR(SCTP_DEBUG_PCB3, firstaddr); SCTPDBG(SCTP_DEBUG_PCB3, "Port:%d\n", ntohs(((struct sockaddr_in *)firstaddr)->sin_port)); } else { SCTPDBG(SCTP_DEBUG_PCB3, "None\n"); } #endif /* SCTP_DEBUG */ if (firstaddr->sa_family == AF_INET) { struct sockaddr_in *sin; sin = (struct sockaddr_in *)firstaddr; if ((sin->sin_port == 0) || (sin->sin_addr.s_addr == 0)) { /* Invalid address */ SCTP_INP_RUNLOCK(inp); SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PCB, EINVAL); *error = EINVAL; return (NULL); } rport = sin->sin_port; } else if (firstaddr->sa_family == AF_INET6) { struct sockaddr_in6 *sin6; sin6 = (struct sockaddr_in6 *)firstaddr; if ((sin6->sin6_port == 0) || (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr))) { /* Invalid address */ SCTP_INP_RUNLOCK(inp); SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PCB, EINVAL); *error = EINVAL; return (NULL); } rport = sin6->sin6_port; } else { /* not supported family type */ SCTP_INP_RUNLOCK(inp); SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PCB, EINVAL); *error = EINVAL; return (NULL); } SCTP_INP_RUNLOCK(inp); if (inp->sctp_flags & SCTP_PCB_FLAGS_UNBOUND) { /* * If you have not performed a bind, then we need to do the * ephemerial bind for you. */ if ((err = sctp_inpcb_bind(inp->sctp_socket, (struct sockaddr *)NULL, (struct sctp_ifa *)NULL, p ))) { /* bind error, probably perm */ *error = err; return (NULL); } } stcb = SCTP_ZONE_GET(SCTP_BASE_INFO(ipi_zone_asoc), struct sctp_tcb); if (stcb == NULL) { /* out of memory? */ SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PCB, ENOMEM); *error = ENOMEM; return (NULL); } SCTP_INCR_ASOC_COUNT(); bzero(stcb, sizeof(*stcb)); asoc = &stcb->asoc; asoc->assoc_id = sctp_aloc_a_assoc_id(inp, stcb); SCTP_TCB_LOCK_INIT(stcb); SCTP_TCB_SEND_LOCK_INIT(stcb); stcb->rport = rport; /* setup back pointer's */ stcb->sctp_ep = inp; stcb->sctp_socket = inp->sctp_socket; if ((err = sctp_init_asoc(inp, stcb, for_a_init, override_tag, vrf_id))) { /* failed */ SCTP_TCB_LOCK_DESTROY(stcb); SCTP_TCB_SEND_LOCK_DESTROY(stcb); LIST_REMOVE(stcb, sctp_tcbasocidhash); SCTP_ZONE_FREE(SCTP_BASE_INFO(ipi_zone_asoc), stcb); SCTP_DECR_ASOC_COUNT(); *error = err; return (NULL); } /* and the port */ SCTP_INP_INFO_WLOCK(); SCTP_INP_WLOCK(inp); if (inp->sctp_flags & (SCTP_PCB_FLAGS_SOCKET_GONE | SCTP_PCB_FLAGS_SOCKET_ALLGONE)) { /* inpcb freed while alloc going on */ SCTP_TCB_LOCK_DESTROY(stcb); SCTP_TCB_SEND_LOCK_DESTROY(stcb); LIST_REMOVE(stcb, sctp_tcbasocidhash); SCTP_ZONE_FREE(SCTP_BASE_INFO(ipi_zone_asoc), stcb); SCTP_INP_WUNLOCK(inp); SCTP_INP_INFO_WUNLOCK(); SCTP_DECR_ASOC_COUNT(); SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PCB, EINVAL); *error = EINVAL; return (NULL); } SCTP_TCB_LOCK(stcb); /* now that my_vtag is set, add it to the hash */ head = &SCTP_BASE_INFO(sctp_asochash)[SCTP_PCBHASH_ASOC(stcb->asoc.my_vtag, SCTP_BASE_INFO(hashasocmark))]; /* put it in the bucket in the vtag hash of assoc's for the system */ LIST_INSERT_HEAD(head, stcb, sctp_asocs); SCTP_INP_INFO_WUNLOCK(); if ((err = sctp_add_remote_addr(stcb, firstaddr, SCTP_DO_SETSCOPE, SCTP_ALLOC_ASOC))) { /* failure.. memory error? */ if (asoc->strmout) { SCTP_FREE(asoc->strmout, SCTP_M_STRMO); asoc->strmout = NULL; } if (asoc->mapping_array) { SCTP_FREE(asoc->mapping_array, SCTP_M_MAP); asoc->mapping_array = NULL; } if (asoc->nr_mapping_array) { SCTP_FREE(asoc->nr_mapping_array, SCTP_M_MAP); asoc->nr_mapping_array = NULL; } SCTP_DECR_ASOC_COUNT(); SCTP_TCB_LOCK_DESTROY(stcb); SCTP_TCB_SEND_LOCK_DESTROY(stcb); LIST_REMOVE(stcb, sctp_tcbasocidhash); SCTP_ZONE_FREE(SCTP_BASE_INFO(ipi_zone_asoc), stcb); SCTP_INP_WUNLOCK(inp); SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PCB, ENOBUFS); *error = ENOBUFS; return (NULL); } /* Init all the timers */ SCTP_OS_TIMER_INIT(&asoc->hb_timer.timer); SCTP_OS_TIMER_INIT(&asoc->dack_timer.timer); SCTP_OS_TIMER_INIT(&asoc->strreset_timer.timer); SCTP_OS_TIMER_INIT(&asoc->asconf_timer.timer); SCTP_OS_TIMER_INIT(&asoc->shut_guard_timer.timer); SCTP_OS_TIMER_INIT(&asoc->autoclose_timer.timer); SCTP_OS_TIMER_INIT(&asoc->delayed_event_timer.timer); SCTP_OS_TIMER_INIT(&asoc->delete_prim_timer.timer); LIST_INSERT_HEAD(&inp->sctp_asoc_list, stcb, sctp_tcblist); /* now file the port under the hash as well */ if (inp->sctp_tcbhash != NULL) { head = &inp->sctp_tcbhash[SCTP_PCBHASH_ALLADDR(stcb->rport, inp->sctp_hashmark)]; LIST_INSERT_HEAD(head, stcb, sctp_tcbhash); } SCTP_INP_WUNLOCK(inp); SCTPDBG(SCTP_DEBUG_PCB1, "Association %p now allocated\n", stcb); return (stcb); } void sctp_remove_net(struct sctp_tcb *stcb, struct sctp_nets *net) { struct sctp_association *asoc; asoc = &stcb->asoc; asoc->numnets--; TAILQ_REMOVE(&asoc->nets, net, sctp_next); if (net == asoc->primary_destination) { /* Reset primary */ struct sctp_nets *lnet; lnet = TAILQ_FIRST(&asoc->nets); /* * Mobility adaptation Ideally, if deleted destination is * the primary, it becomes a fast retransmission trigger by * the subsequent SET PRIMARY. (by micchie) */ if (sctp_is_mobility_feature_on(stcb->sctp_ep, SCTP_MOBILITY_BASE) || sctp_is_mobility_feature_on(stcb->sctp_ep, SCTP_MOBILITY_FASTHANDOFF)) { SCTPDBG(SCTP_DEBUG_ASCONF1, "remove_net: primary dst is deleting\n"); if (asoc->deleted_primary != NULL) { SCTPDBG(SCTP_DEBUG_ASCONF1, "remove_net: deleted primary may be already stored\n"); goto out; } asoc->deleted_primary = net; atomic_add_int(&net->ref_count, 1); memset(&net->lastsa, 0, sizeof(net->lastsa)); memset(&net->lastsv, 0, sizeof(net->lastsv)); sctp_mobility_feature_on(stcb->sctp_ep, SCTP_MOBILITY_PRIM_DELETED); sctp_timer_start(SCTP_TIMER_TYPE_PRIM_DELETED, stcb->sctp_ep, stcb, NULL); } out: /* Try to find a confirmed primary */ asoc->primary_destination = sctp_find_alternate_net(stcb, lnet, 0); } if (net == asoc->last_data_chunk_from) { /* Reset primary */ asoc->last_data_chunk_from = TAILQ_FIRST(&asoc->nets); } if (net == asoc->last_control_chunk_from) { /* Clear net */ asoc->last_control_chunk_from = NULL; } sctp_free_remote_addr(net); } /* * remove a remote endpoint address from an association, it will fail if the * address does not exist. */ int sctp_del_remote_addr(struct sctp_tcb *stcb, struct sockaddr *remaddr) { /* * Here we need to remove a remote address. This is quite simple, we * first find it in the list of address for the association * (tasoc->asoc.nets) and then if it is there, we do a LIST_REMOVE * on that item. Note we do not allow it to be removed if there are * no other addresses. */ struct sctp_association *asoc; struct sctp_nets *net, *net_tmp; asoc = &stcb->asoc; /* locate the address */ for (net = TAILQ_FIRST(&asoc->nets); net != NULL; net = net_tmp) { net_tmp = TAILQ_NEXT(net, sctp_next); if (net->ro._l_addr.sa.sa_family != remaddr->sa_family) { continue; } if (sctp_cmpaddr((struct sockaddr *)&net->ro._l_addr, remaddr)) { /* we found the guy */ if (asoc->numnets < 2) { /* Must have at LEAST two remote addresses */ return (-1); } else { sctp_remove_net(stcb, net); return (0); } } } /* not found. */ return (-2); } void sctp_delete_from_timewait(uint32_t tag, uint16_t lport, uint16_t rport) { struct sctpvtaghead *chain; struct sctp_tagblock *twait_block; int found = 0; int i; chain = &SCTP_BASE_INFO(vtag_timewait)[(tag % SCTP_STACK_VTAG_HASH_SIZE)]; if (!SCTP_LIST_EMPTY(chain)) { LIST_FOREACH(twait_block, chain, sctp_nxt_tagblock) { for (i = 0; i < SCTP_NUMBER_IN_VTAG_BLOCK; i++) { if ((twait_block->vtag_block[i].v_tag == tag) && (twait_block->vtag_block[i].lport == lport) && (twait_block->vtag_block[i].rport == rport)) { twait_block->vtag_block[i].tv_sec_at_expire = 0; twait_block->vtag_block[i].v_tag = 0; twait_block->vtag_block[i].lport = 0; twait_block->vtag_block[i].rport = 0; found = 1; break; } } if (found) break; } } } int sctp_is_in_timewait(uint32_t tag, uint16_t lport, uint16_t rport) { struct sctpvtaghead *chain; struct sctp_tagblock *twait_block; int found = 0; int i; SCTP_INP_INFO_WLOCK(); chain = &SCTP_BASE_INFO(vtag_timewait)[(tag % SCTP_STACK_VTAG_HASH_SIZE)]; if (!SCTP_LIST_EMPTY(chain)) { LIST_FOREACH(twait_block, chain, sctp_nxt_tagblock) { for (i = 0; i < SCTP_NUMBER_IN_VTAG_BLOCK; i++) { if ((twait_block->vtag_block[i].v_tag == tag) && (twait_block->vtag_block[i].lport == lport) && (twait_block->vtag_block[i].rport == rport)) { found = 1; break; } } if (found) break; } } SCTP_INP_INFO_WUNLOCK(); return (found); } void sctp_add_vtag_to_timewait(uint32_t tag, uint32_t time, uint16_t lport, uint16_t rport) { struct sctpvtaghead *chain; struct sctp_tagblock *twait_block; struct timeval now; int set, i; if (time == 0) { /* Its disabled */ return; } (void)SCTP_GETTIME_TIMEVAL(&now); chain = &SCTP_BASE_INFO(vtag_timewait)[(tag % SCTP_STACK_VTAG_HASH_SIZE)]; set = 0; if (!SCTP_LIST_EMPTY(chain)) { /* Block(s) present, lets find space, and expire on the fly */ LIST_FOREACH(twait_block, chain, sctp_nxt_tagblock) { for (i = 0; i < SCTP_NUMBER_IN_VTAG_BLOCK; i++) { if ((twait_block->vtag_block[i].v_tag == 0) && !set) { twait_block->vtag_block[i].tv_sec_at_expire = now.tv_sec + time; twait_block->vtag_block[i].v_tag = tag; twait_block->vtag_block[i].lport = lport; twait_block->vtag_block[i].rport = rport; set = 1; } else if ((twait_block->vtag_block[i].v_tag) && ((long)twait_block->vtag_block[i].tv_sec_at_expire < now.tv_sec)) { /* Audit expires this guy */ twait_block->vtag_block[i].tv_sec_at_expire = 0; twait_block->vtag_block[i].v_tag = 0; twait_block->vtag_block[i].lport = 0; twait_block->vtag_block[i].rport = 0; if (set == 0) { /* Reuse it for my new tag */ twait_block->vtag_block[i].tv_sec_at_expire = now.tv_sec + time; twait_block->vtag_block[i].v_tag = tag; twait_block->vtag_block[i].lport = lport; twait_block->vtag_block[i].rport = rport; set = 1; } } } if (set) { /* * We only do up to the block where we can * place our tag for audits */ break; } } } /* Need to add a new block to chain */ if (!set) { SCTP_MALLOC(twait_block, struct sctp_tagblock *, sizeof(struct sctp_tagblock), SCTP_M_TIMW); if (twait_block == NULL) { #ifdef INVARIANTS panic("Can not alloc tagblock"); #endif return; } memset(twait_block, 0, sizeof(struct sctp_tagblock)); LIST_INSERT_HEAD(chain, twait_block, sctp_nxt_tagblock); twait_block->vtag_block[0].tv_sec_at_expire = now.tv_sec + time; twait_block->vtag_block[0].v_tag = tag; twait_block->vtag_block[0].lport = lport; twait_block->vtag_block[0].rport = rport; } } static void sctp_iterator_asoc_being_freed(struct sctp_inpcb *inp, struct sctp_tcb *stcb) { struct sctp_iterator *it; /* * Unlock the tcb lock we do this so we avoid a dead lock scenario * where the iterator is waiting on the TCB lock and the TCB lock is * waiting on the iterator lock. */ it = stcb->asoc.stcb_starting_point_for_iterator; if (it == NULL) { return; } if (it->inp != stcb->sctp_ep) { /* hmm, focused on the wrong one? */ return; } if (it->stcb != stcb) { return; } it->stcb = LIST_NEXT(stcb, sctp_tcblist); if (it->stcb == NULL) { /* done with all asoc's in this assoc */ if (it->iterator_flags & SCTP_ITERATOR_DO_SINGLE_INP) { it->inp = NULL; } else { it->inp = LIST_NEXT(inp, sctp_list); } } } /*- * Free the association after un-hashing the remote port. This * function ALWAYS returns holding NO LOCK on the stcb. It DOES * expect that the input to this function IS a locked TCB. * It will return 0, if it did NOT destroy the association (instead * it unlocks it. It will return NON-zero if it either destroyed the * association OR the association is already destroyed. */ int sctp_free_assoc(struct sctp_inpcb *inp, struct sctp_tcb *stcb, int from_inpcbfree, int from_location) { int i; struct sctp_association *asoc; struct sctp_nets *net, *prev; struct sctp_laddr *laddr; struct sctp_tmit_chunk *chk; struct sctp_asconf_addr *aparam; struct sctp_asconf_ack *aack; struct sctp_stream_reset_list *liste; struct sctp_queued_to_read *sq; struct sctp_stream_queue_pending *sp; sctp_sharedkey_t *shared_key; struct socket *so; int ccnt = 0; int cnt = 0; /* first, lets purge the entry from the hash table. */ #ifdef SCTP_LOG_CLOSING sctp_log_closing(inp, stcb, 6); #endif if (stcb->asoc.state == 0) { #ifdef SCTP_LOG_CLOSING sctp_log_closing(inp, NULL, 7); #endif /* there is no asoc, really TSNH :-0 */ return (1); } /* TEMP CODE */ if (stcb->freed_from_where == 0) { /* Only record the first place free happened from */ stcb->freed_from_where = from_location; } /* TEMP CODE */ asoc = &stcb->asoc; if ((inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_ALLGONE) || (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE)) /* nothing around */ so = NULL; else so = inp->sctp_socket; /* * We used timer based freeing if a reader or writer is in the way. * So we first check if we are actually being called from a timer, * if so we abort early if a reader or writer is still in the way. */ if ((stcb->asoc.state & SCTP_STATE_ABOUT_TO_BE_FREED) && (from_inpcbfree == SCTP_NORMAL_PROC)) { /* * is it the timer driving us? if so are the reader/writers * gone? */ if (stcb->asoc.refcnt) { /* nope, reader or writer in the way */ sctp_timer_start(SCTP_TIMER_TYPE_ASOCKILL, inp, stcb, NULL); /* no asoc destroyed */ SCTP_TCB_UNLOCK(stcb); #ifdef SCTP_LOG_CLOSING sctp_log_closing(inp, stcb, 8); #endif return (0); } } /* now clean up any other timers */ (void)SCTP_OS_TIMER_STOP(&asoc->hb_timer.timer); asoc->hb_timer.self = NULL; (void)SCTP_OS_TIMER_STOP(&asoc->dack_timer.timer); asoc->dack_timer.self = NULL; (void)SCTP_OS_TIMER_STOP(&asoc->strreset_timer.timer); /*- * For stream reset we don't blast this unless * it is a str-reset timer, it might be the * free-asoc timer which we DON'T want to * disturb. */ if (asoc->strreset_timer.type == SCTP_TIMER_TYPE_STRRESET) asoc->strreset_timer.self = NULL; (void)SCTP_OS_TIMER_STOP(&asoc->asconf_timer.timer); asoc->asconf_timer.self = NULL; (void)SCTP_OS_TIMER_STOP(&asoc->autoclose_timer.timer); asoc->autoclose_timer.self = NULL; (void)SCTP_OS_TIMER_STOP(&asoc->shut_guard_timer.timer); asoc->shut_guard_timer.self = NULL; (void)SCTP_OS_TIMER_STOP(&asoc->delayed_event_timer.timer); asoc->delayed_event_timer.self = NULL; /* Mobility adaptation */ (void)SCTP_OS_TIMER_STOP(&asoc->delete_prim_timer.timer); asoc->delete_prim_timer.self = NULL; TAILQ_FOREACH(net, &asoc->nets, sctp_next) { (void)SCTP_OS_TIMER_STOP(&net->fr_timer.timer); net->fr_timer.self = NULL; (void)SCTP_OS_TIMER_STOP(&net->rxt_timer.timer); net->rxt_timer.self = NULL; (void)SCTP_OS_TIMER_STOP(&net->pmtu_timer.timer); net->pmtu_timer.self = NULL; } /* Now the read queue needs to be cleaned up (only once) */ cnt = 0; if ((stcb->asoc.state & SCTP_STATE_ABOUT_TO_BE_FREED) == 0) { stcb->asoc.state |= SCTP_STATE_ABOUT_TO_BE_FREED; SCTP_INP_READ_LOCK(inp); TAILQ_FOREACH(sq, &inp->read_queue, next) { if (sq->stcb == stcb) { sq->do_not_ref_stcb = 1; sq->sinfo_cumtsn = stcb->asoc.cumulative_tsn; /* * If there is no end, there never will be * now. */ if (sq->end_added == 0) { /* Held for PD-API clear that. */ sq->pdapi_aborted = 1; sq->held_length = 0; if (sctp_is_feature_on(inp, SCTP_PCB_FLAGS_PDAPIEVNT) && (so != NULL)) { /* * Need to add a PD-API * aborted indication. * Setting the control_pdapi * assures that it will be * added right after this * msg. */ uint32_t strseq; stcb->asoc.control_pdapi = sq; strseq = (sq->sinfo_stream << 16) | sq->sinfo_ssn; sctp_ulp_notify(SCTP_NOTIFY_PARTIAL_DELVIERY_INDICATION, stcb, SCTP_PARTIAL_DELIVERY_ABORTED, (void *)&strseq, SCTP_SO_LOCKED); stcb->asoc.control_pdapi = NULL; } } /* Add an end to wake them */ sq->end_added = 1; cnt++; } } SCTP_INP_READ_UNLOCK(inp); if (stcb->block_entry) { cnt++; SCTP_LTRACE_ERR_RET(inp, stcb, NULL, SCTP_FROM_SCTP_PCB, ECONNRESET); stcb->block_entry->error = ECONNRESET; stcb->block_entry = NULL; } } if (stcb->asoc.refcnt) { /* * reader or writer in the way, we have hopefully given him * something to chew on above. */ sctp_timer_start(SCTP_TIMER_TYPE_ASOCKILL, inp, stcb, NULL); SCTP_TCB_UNLOCK(stcb); if ((inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_ALLGONE) || (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE)) /* nothing around */ so = NULL; if (so) { /* Wake any reader/writers */ sctp_sorwakeup(inp, so); sctp_sowwakeup(inp, so); } #ifdef SCTP_LOG_CLOSING sctp_log_closing(inp, stcb, 9); #endif /* no asoc destroyed */ return (0); } #ifdef SCTP_LOG_CLOSING sctp_log_closing(inp, stcb, 10); #endif /* * When I reach here, no others want to kill the assoc yet.. and I * own the lock. Now its possible an abort comes in when I do the * lock exchange below to grab all the locks to do the final take * out. to prevent this we increment the count, which will start a * timer and blow out above thus assuring us that we hold exclusive * killing of the asoc. Note that after getting back the TCB lock we * will go ahead and increment the counter back up and stop any * timer a passing stranger may have started :-S */ if (from_inpcbfree == SCTP_NORMAL_PROC) { atomic_add_int(&stcb->asoc.refcnt, 1); SCTP_TCB_UNLOCK(stcb); SCTP_ITERATOR_LOCK(); SCTP_INP_INFO_WLOCK(); SCTP_INP_WLOCK(inp); SCTP_TCB_LOCK(stcb); } /* Double check the GONE flag */ if ((inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_ALLGONE) || (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE)) /* nothing around */ so = NULL; if ((inp->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) || (inp->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL)) { /* * For TCP type we need special handling when we are * connected. We also include the peel'ed off ones to. */ if (inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) { inp->sctp_flags &= ~SCTP_PCB_FLAGS_CONNECTED; inp->sctp_flags |= SCTP_PCB_FLAGS_WAS_CONNECTED; if (so) { SOCK_LOCK(so); if (so->so_rcv.sb_cc == 0) { so->so_state &= ~(SS_ISCONNECTING | SS_ISDISCONNECTING | SS_ISCONFIRMING | SS_ISCONNECTED); } SOCK_UNLOCK(so); socantrcvmore(so); sctp_sowwakeup(inp, so); sctp_sorwakeup(inp, so); SCTP_SOWAKEUP(so); } } } /* * Make it invalid too, that way if its about to run it will abort * and return. */ sctp_iterator_asoc_being_freed(inp, stcb); /* re-increment the lock */ if (from_inpcbfree == SCTP_NORMAL_PROC) { atomic_add_int(&stcb->asoc.refcnt, -1); } asoc->state = 0; if (inp->sctp_tcbhash) { LIST_REMOVE(stcb, sctp_tcbhash); } if (stcb->asoc.in_asocid_hash) { LIST_REMOVE(stcb, sctp_tcbasocidhash); } /* Now lets remove it from the list of ALL associations in the EP */ LIST_REMOVE(stcb, sctp_tcblist); if (from_inpcbfree == SCTP_NORMAL_PROC) { SCTP_INP_INCR_REF(inp); SCTP_INP_WUNLOCK(inp); SCTP_ITERATOR_UNLOCK(); } /* pull from vtag hash */ LIST_REMOVE(stcb, sctp_asocs); sctp_add_vtag_to_timewait(asoc->my_vtag, SCTP_BASE_SYSCTL(sctp_vtag_time_wait), inp->sctp_lport, stcb->rport); /* * Now restop the timers to be sure - this is paranoia at is finest! */ (void)SCTP_OS_TIMER_STOP(&asoc->strreset_timer.timer); (void)SCTP_OS_TIMER_STOP(&asoc->hb_timer.timer); (void)SCTP_OS_TIMER_STOP(&asoc->dack_timer.timer); (void)SCTP_OS_TIMER_STOP(&asoc->strreset_timer.timer); (void)SCTP_OS_TIMER_STOP(&asoc->asconf_timer.timer); (void)SCTP_OS_TIMER_STOP(&asoc->shut_guard_timer.timer); (void)SCTP_OS_TIMER_STOP(&asoc->autoclose_timer.timer); (void)SCTP_OS_TIMER_STOP(&asoc->delayed_event_timer.timer); TAILQ_FOREACH(net, &asoc->nets, sctp_next) { (void)SCTP_OS_TIMER_STOP(&net->fr_timer.timer); (void)SCTP_OS_TIMER_STOP(&net->rxt_timer.timer); (void)SCTP_OS_TIMER_STOP(&net->pmtu_timer.timer); } asoc->strreset_timer.type = SCTP_TIMER_TYPE_NONE; prev = NULL; /* * The chunk lists and such SHOULD be empty but we check them just * in case. */ /* anything on the wheel needs to be removed */ for (i = 0; i < asoc->streamoutcnt; i++) { struct sctp_stream_out *outs; outs = &asoc->strmout[i]; /* now clean up any chunks here */ sp = TAILQ_FIRST(&outs->outqueue); while (sp) { TAILQ_REMOVE(&outs->outqueue, sp, next); if (sp->data) { sctp_m_freem(sp->data); sp->data = NULL; sp->tail_mbuf = NULL; } sctp_free_remote_addr(sp->net); sctp_free_spbufspace(stcb, asoc, sp); if (sp->holds_key_ref) sctp_auth_key_release(stcb, sp->auth_keyid); /* Free the zone stuff */ SCTP_ZONE_FREE(SCTP_BASE_INFO(ipi_zone_strmoq), sp); SCTP_DECR_STRMOQ_COUNT(); /* sa_ignore FREED_MEMORY */ sp = TAILQ_FIRST(&outs->outqueue); } } /* sa_ignore FREED_MEMORY */ while ((liste = TAILQ_FIRST(&asoc->resetHead)) != NULL) { TAILQ_REMOVE(&asoc->resetHead, liste, next_resp); SCTP_FREE(liste, SCTP_M_STRESET); } sq = TAILQ_FIRST(&asoc->pending_reply_queue); while (sq) { TAILQ_REMOVE(&asoc->pending_reply_queue, sq, next); if (sq->data) { sctp_m_freem(sq->data); sq->data = NULL; } sctp_free_remote_addr(sq->whoFrom); sq->whoFrom = NULL; sq->stcb = NULL; /* Free the ctl entry */ SCTP_ZONE_FREE(SCTP_BASE_INFO(ipi_zone_readq), sq); SCTP_DECR_READQ_COUNT(); /* sa_ignore FREED_MEMORY */ sq = TAILQ_FIRST(&asoc->pending_reply_queue); } chk = TAILQ_FIRST(&asoc->free_chunks); while (chk) { TAILQ_REMOVE(&asoc->free_chunks, chk, sctp_next); if (chk->data) { sctp_m_freem(chk->data); chk->data = NULL; } if (chk->holds_key_ref) sctp_auth_key_release(stcb, chk->auth_keyid); ccnt++; SCTP_ZONE_FREE(SCTP_BASE_INFO(ipi_zone_chunk), chk); SCTP_DECR_CHK_COUNT(); atomic_subtract_int(&SCTP_BASE_INFO(ipi_free_chunks), 1); asoc->free_chunk_cnt--; /* sa_ignore FREED_MEMORY */ chk = TAILQ_FIRST(&asoc->free_chunks); } /* pending send queue SHOULD be empty */ if (!TAILQ_EMPTY(&asoc->send_queue)) { chk = TAILQ_FIRST(&asoc->send_queue); while (chk) { TAILQ_REMOVE(&asoc->send_queue, chk, sctp_next); if (chk->data) { sctp_m_freem(chk->data); chk->data = NULL; } if (chk->holds_key_ref) sctp_auth_key_release(stcb, chk->auth_keyid); ccnt++; sctp_free_remote_addr(chk->whoTo); SCTP_ZONE_FREE(SCTP_BASE_INFO(ipi_zone_chunk), chk); SCTP_DECR_CHK_COUNT(); /* sa_ignore FREED_MEMORY */ chk = TAILQ_FIRST(&asoc->send_queue); } } /* if (ccnt) { printf("Freed %d from send_queue\n", ccnt); ccnt = 0; } */ /* sent queue SHOULD be empty */ if (!TAILQ_EMPTY(&asoc->sent_queue)) { chk = TAILQ_FIRST(&asoc->sent_queue); while (chk) { TAILQ_REMOVE(&asoc->sent_queue, chk, sctp_next); if (chk->data) { sctp_m_freem(chk->data); chk->data = NULL; } if (chk->holds_key_ref) sctp_auth_key_release(stcb, chk->auth_keyid); ccnt++; sctp_free_remote_addr(chk->whoTo); SCTP_ZONE_FREE(SCTP_BASE_INFO(ipi_zone_chunk), chk); SCTP_DECR_CHK_COUNT(); /* sa_ignore FREED_MEMORY */ chk = TAILQ_FIRST(&asoc->sent_queue); } } /* if (ccnt) { printf("Freed %d from sent_queue\n", ccnt); ccnt = 0; } */ /* control queue MAY not be empty */ if (!TAILQ_EMPTY(&asoc->control_send_queue)) { chk = TAILQ_FIRST(&asoc->control_send_queue); while (chk) { TAILQ_REMOVE(&asoc->control_send_queue, chk, sctp_next); if (chk->data) { sctp_m_freem(chk->data); chk->data = NULL; } if (chk->holds_key_ref) sctp_auth_key_release(stcb, chk->auth_keyid); ccnt++; sctp_free_remote_addr(chk->whoTo); SCTP_ZONE_FREE(SCTP_BASE_INFO(ipi_zone_chunk), chk); SCTP_DECR_CHK_COUNT(); /* sa_ignore FREED_MEMORY */ chk = TAILQ_FIRST(&asoc->control_send_queue); } } /* if (ccnt) { printf("Freed %d from ctrl_queue\n", ccnt); ccnt = 0; } */ /* ASCONF queue MAY not be empty */ if (!TAILQ_EMPTY(&asoc->asconf_send_queue)) { chk = TAILQ_FIRST(&asoc->asconf_send_queue); while (chk) { TAILQ_REMOVE(&asoc->asconf_send_queue, chk, sctp_next); if (chk->data) { sctp_m_freem(chk->data); chk->data = NULL; } if (chk->holds_key_ref) sctp_auth_key_release(stcb, chk->auth_keyid); ccnt++; sctp_free_remote_addr(chk->whoTo); SCTP_ZONE_FREE(SCTP_BASE_INFO(ipi_zone_chunk), chk); SCTP_DECR_CHK_COUNT(); /* sa_ignore FREED_MEMORY */ chk = TAILQ_FIRST(&asoc->asconf_send_queue); } } /* if (ccnt) { printf("Freed %d from asconf_queue\n", ccnt); ccnt = 0; } */ if (!TAILQ_EMPTY(&asoc->reasmqueue)) { chk = TAILQ_FIRST(&asoc->reasmqueue); while (chk) { TAILQ_REMOVE(&asoc->reasmqueue, chk, sctp_next); if (chk->data) { sctp_m_freem(chk->data); chk->data = NULL; } if (chk->holds_key_ref) sctp_auth_key_release(stcb, chk->auth_keyid); sctp_free_remote_addr(chk->whoTo); ccnt++; SCTP_ZONE_FREE(SCTP_BASE_INFO(ipi_zone_chunk), chk); SCTP_DECR_CHK_COUNT(); /* sa_ignore FREED_MEMORY */ chk = TAILQ_FIRST(&asoc->reasmqueue); } } /* if (ccnt) { printf("Freed %d from reasm_queue\n", ccnt); ccnt = 0; } */ if (asoc->mapping_array) { SCTP_FREE(asoc->mapping_array, SCTP_M_MAP); asoc->mapping_array = NULL; } if (asoc->nr_mapping_array) { SCTP_FREE(asoc->nr_mapping_array, SCTP_M_MAP); asoc->nr_mapping_array = NULL; } /* the stream outs */ if (asoc->strmout) { SCTP_FREE(asoc->strmout, SCTP_M_STRMO); asoc->strmout = NULL; } asoc->strm_realoutsize = asoc->streamoutcnt = 0; if (asoc->strmin) { struct sctp_queued_to_read *ctl; for (i = 0; i < asoc->streamincnt; i++) { if (!TAILQ_EMPTY(&asoc->strmin[i].inqueue)) { /* We have somethings on the streamin queue */ ctl = TAILQ_FIRST(&asoc->strmin[i].inqueue); while (ctl) { TAILQ_REMOVE(&asoc->strmin[i].inqueue, ctl, next); sctp_free_remote_addr(ctl->whoFrom); if (ctl->data) { sctp_m_freem(ctl->data); ctl->data = NULL; } /* * We don't free the address here * since all the net's were freed * above. */ SCTP_ZONE_FREE(SCTP_BASE_INFO(ipi_zone_readq), ctl); SCTP_DECR_READQ_COUNT(); ctl = TAILQ_FIRST(&asoc->strmin[i].inqueue); } } } SCTP_FREE(asoc->strmin, SCTP_M_STRMI); asoc->strmin = NULL; } asoc->streamincnt = 0; while (!TAILQ_EMPTY(&asoc->nets)) { /* sa_ignore FREED_MEMORY */ net = TAILQ_FIRST(&asoc->nets); /* pull from list */ if ((SCTP_BASE_INFO(ipi_count_raddr) == 0) || (prev == net)) { #ifdef INVARIANTS panic("no net's left alloc'ed, or list points to itself"); #endif break; } prev = net; TAILQ_REMOVE(&asoc->nets, net, sctp_next); sctp_free_remote_addr(net); } while (!SCTP_LIST_EMPTY(&asoc->sctp_restricted_addrs)) { /* sa_ignore FREED_MEMORY */ laddr = LIST_FIRST(&asoc->sctp_restricted_addrs); sctp_remove_laddr(laddr); } /* pending asconf (address) parameters */ while (!TAILQ_EMPTY(&asoc->asconf_queue)) { /* sa_ignore FREED_MEMORY */ aparam = TAILQ_FIRST(&asoc->asconf_queue); TAILQ_REMOVE(&asoc->asconf_queue, aparam, next); SCTP_FREE(aparam, SCTP_M_ASC_ADDR); } while (!TAILQ_EMPTY(&asoc->asconf_ack_sent)) { /* sa_ignore FREED_MEMORY */ aack = TAILQ_FIRST(&asoc->asconf_ack_sent); TAILQ_REMOVE(&asoc->asconf_ack_sent, aack, next); if (aack->data != NULL) { sctp_m_freem(aack->data); } SCTP_ZONE_FREE(SCTP_BASE_INFO(ipi_zone_asconf_ack), aack); } /* clean up auth stuff */ if (asoc->local_hmacs) sctp_free_hmaclist(asoc->local_hmacs); if (asoc->peer_hmacs) sctp_free_hmaclist(asoc->peer_hmacs); if (asoc->local_auth_chunks) sctp_free_chunklist(asoc->local_auth_chunks); if (asoc->peer_auth_chunks) sctp_free_chunklist(asoc->peer_auth_chunks); sctp_free_authinfo(&asoc->authinfo); shared_key = LIST_FIRST(&asoc->shared_keys); while (shared_key) { LIST_REMOVE(shared_key, next); sctp_free_sharedkey(shared_key); /* sa_ignore FREED_MEMORY */ shared_key = LIST_FIRST(&asoc->shared_keys); } /* Insert new items here :> */ /* Get rid of LOCK */ SCTP_TCB_LOCK_DESTROY(stcb); SCTP_TCB_SEND_LOCK_DESTROY(stcb); if (from_inpcbfree == SCTP_NORMAL_PROC) { SCTP_INP_INFO_WUNLOCK(); SCTP_INP_RLOCK(inp); } #ifdef SCTP_TRACK_FREED_ASOCS if (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) { /* now clean up the tasoc itself */ SCTP_ZONE_FREE(SCTP_BASE_INFO(ipi_zone_asoc), stcb); SCTP_DECR_ASOC_COUNT(); } else { LIST_INSERT_HEAD(&inp->sctp_asoc_free_list, stcb, sctp_tcblist); } #else SCTP_ZONE_FREE(SCTP_BASE_INFO(ipi_zone_asoc), stcb); SCTP_DECR_ASOC_COUNT(); #endif if (from_inpcbfree == SCTP_NORMAL_PROC) { if (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) { /* * If its NOT the inp_free calling us AND sctp_close * as been called, we call back... */ SCTP_INP_RUNLOCK(inp); /* * This will start the kill timer (if we are the * lastone) since we hold an increment yet. But this * is the only safe way to do this since otherwise * if the socket closes at the same time we are here * we might collide in the cleanup. */ sctp_inpcb_free(inp, SCTP_FREE_SHOULD_USE_GRACEFUL_CLOSE, SCTP_CALLED_DIRECTLY_NOCMPSET); SCTP_INP_DECR_REF(inp); goto out_of; } else { /* The socket is still open. */ SCTP_INP_DECR_REF(inp); } } if (from_inpcbfree == SCTP_NORMAL_PROC) { SCTP_INP_RUNLOCK(inp); } out_of: /* destroyed the asoc */ #ifdef SCTP_LOG_CLOSING sctp_log_closing(inp, NULL, 11); #endif return (1); } /* * determine if a destination is "reachable" based upon the addresses bound * to the current endpoint (e.g. only v4 or v6 currently bound) */ /* * FIX: if we allow assoc-level bindx(), then this needs to be fixed to use * assoc level v4/v6 flags, as the assoc *may* not have the same address * types bound as its endpoint */ int sctp_destination_is_reachable(struct sctp_tcb *stcb, struct sockaddr *destaddr) { struct sctp_inpcb *inp; int answer; /* * No locks here, the TCB, in all cases is already locked and an * assoc is up. There is either a INP lock by the caller applied (in * asconf case when deleting an address) or NOT in the HB case, * however if HB then the INP increment is up and the INP will not * be removed (on top of the fact that we have a TCB lock). So we * only want to read the sctp_flags, which is either bound-all or * not.. no protection needed since once an assoc is up you can't be * changing your binding. */ inp = stcb->sctp_ep; if (inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) { /* if bound all, destination is not restricted */ /* * RRS: Question during lock work: Is this correct? If you * are bound-all you still might need to obey the V4--V6 * flags??? IMO this bound-all stuff needs to be removed! */ return (1); } /* NOTE: all "scope" checks are done when local addresses are added */ if (destaddr->sa_family == AF_INET6) { answer = inp->ip_inp.inp.inp_vflag & INP_IPV6; } else if (destaddr->sa_family == AF_INET) { answer = inp->ip_inp.inp.inp_vflag & INP_IPV4; } else { /* invalid family, so it's unreachable */ answer = 0; } return (answer); } /* * update the inp_vflags on an endpoint */ static void sctp_update_ep_vflag(struct sctp_inpcb *inp) { struct sctp_laddr *laddr; /* first clear the flag */ inp->ip_inp.inp.inp_vflag = 0; /* set the flag based on addresses on the ep list */ LIST_FOREACH(laddr, &inp->sctp_addr_list, sctp_nxt_addr) { if (laddr->ifa == NULL) { SCTPDBG(SCTP_DEBUG_PCB1, "%s: NULL ifa\n", __FUNCTION__); continue; } if (laddr->ifa->localifa_flags & SCTP_BEING_DELETED) { continue; } if (laddr->ifa->address.sa.sa_family == AF_INET6) { inp->ip_inp.inp.inp_vflag |= INP_IPV6; } else if (laddr->ifa->address.sa.sa_family == AF_INET) { inp->ip_inp.inp.inp_vflag |= INP_IPV4; } } } /* * Add the address to the endpoint local address list There is nothing to be * done if we are bound to all addresses */ void sctp_add_local_addr_ep(struct sctp_inpcb *inp, struct sctp_ifa *ifa, uint32_t action) { struct sctp_laddr *laddr; int fnd, error = 0; fnd = 0; if (inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) { /* You are already bound to all. You have it already */ return; } if (ifa->address.sa.sa_family == AF_INET6) { if (ifa->localifa_flags & SCTP_ADDR_IFA_UNUSEABLE) { /* Can't bind a non-useable addr. */ return; } } /* first, is it already present? */ LIST_FOREACH(laddr, &inp->sctp_addr_list, sctp_nxt_addr) { if (laddr->ifa == ifa) { fnd = 1; break; } } if (fnd == 0) { /* Not in the ep list */ error = sctp_insert_laddr(&inp->sctp_addr_list, ifa, action); if (error != 0) return; inp->laddr_count++; /* update inp_vflag flags */ if (ifa->address.sa.sa_family == AF_INET6) { inp->ip_inp.inp.inp_vflag |= INP_IPV6; } else if (ifa->address.sa.sa_family == AF_INET) { inp->ip_inp.inp.inp_vflag |= INP_IPV4; } } return; } /* * select a new (hopefully reachable) destination net (should only be used * when we deleted an ep addr that is the only usable source address to reach * the destination net) */ static void sctp_select_primary_destination(struct sctp_tcb *stcb) { struct sctp_nets *net; TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) { /* for now, we'll just pick the first reachable one we find */ if (net->dest_state & SCTP_ADDR_UNCONFIRMED) continue; if (sctp_destination_is_reachable(stcb, (struct sockaddr *)&net->ro._l_addr)) { /* found a reachable destination */ stcb->asoc.primary_destination = net; } } /* I can't there from here! ...we're gonna die shortly... */ } /* * Delete the address from the endpoint local address list There is nothing * to be done if we are bound to all addresses */ void sctp_del_local_addr_ep(struct sctp_inpcb *inp, struct sctp_ifa *ifa) { struct sctp_laddr *laddr; int fnd; fnd = 0; if (inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) { /* You are already bound to all. You have it already */ return; } LIST_FOREACH(laddr, &inp->sctp_addr_list, sctp_nxt_addr) { if (laddr->ifa == ifa) { fnd = 1; break; } } if (fnd && (inp->laddr_count < 2)) { /* can't delete unless there are at LEAST 2 addresses */ return; } if (fnd) { /* * clean up any use of this address go through our * associations and clear any last_used_address that match * this one for each assoc, see if a new primary_destination * is needed */ struct sctp_tcb *stcb; /* clean up "next_addr_touse" */ if (inp->next_addr_touse == laddr) /* delete this address */ inp->next_addr_touse = NULL; /* clean up "last_used_address" */ LIST_FOREACH(stcb, &inp->sctp_asoc_list, sctp_tcblist) { struct sctp_nets *net; SCTP_TCB_LOCK(stcb); if (stcb->asoc.last_used_address == laddr) /* delete this address */ stcb->asoc.last_used_address = NULL; /* * Now spin through all the nets and purge any ref * to laddr */ TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) { if (net->ro._s_addr && (net->ro._s_addr->ifa == laddr->ifa)) { /* Yep, purge src address selected */ sctp_rtentry_t *rt; /* delete this address if cached */ rt = net->ro.ro_rt; if (rt != NULL) { RTFREE(rt); net->ro.ro_rt = NULL; } sctp_free_ifa(net->ro._s_addr); net->ro._s_addr = NULL; net->src_addr_selected = 0; } } SCTP_TCB_UNLOCK(stcb); } /* for each tcb */ /* remove it from the ep list */ sctp_remove_laddr(laddr); inp->laddr_count--; /* update inp_vflag flags */ sctp_update_ep_vflag(inp); } return; } /* * Add the address to the TCB local address restricted list. * This is a "pending" address list (eg. addresses waiting for an * ASCONF-ACK response) and cannot be used as a valid source address. */ void sctp_add_local_addr_restricted(struct sctp_tcb *stcb, struct sctp_ifa *ifa) { struct sctp_inpcb *inp; struct sctp_laddr *laddr; struct sctpladdr *list; /* * Assumes TCB is locked.. and possibly the INP. May need to * confirm/fix that if we need it and is not the case. */ list = &stcb->asoc.sctp_restricted_addrs; inp = stcb->sctp_ep; if (ifa->address.sa.sa_family == AF_INET6) { if (ifa->localifa_flags & SCTP_ADDR_IFA_UNUSEABLE) { /* Can't bind a non-existent addr. */ return; } } /* does the address already exist? */ LIST_FOREACH(laddr, list, sctp_nxt_addr) { if (laddr->ifa == ifa) { return; } } /* add to the list */ (void)sctp_insert_laddr(list, ifa, 0); return; } /* * insert an laddr entry with the given ifa for the desired list */ int sctp_insert_laddr(struct sctpladdr *list, struct sctp_ifa *ifa, uint32_t act) { struct sctp_laddr *laddr; laddr = SCTP_ZONE_GET(SCTP_BASE_INFO(ipi_zone_laddr), struct sctp_laddr); if (laddr == NULL) { /* out of memory? */ SCTP_LTRACE_ERR_RET(NULL, NULL, NULL, SCTP_FROM_SCTP_PCB, EINVAL); return (EINVAL); } SCTP_INCR_LADDR_COUNT(); bzero(laddr, sizeof(*laddr)); (void)SCTP_GETTIME_TIMEVAL(&laddr->start_time); laddr->ifa = ifa; laddr->action = act; atomic_add_int(&ifa->refcount, 1); /* insert it */ LIST_INSERT_HEAD(list, laddr, sctp_nxt_addr); return (0); } /* * Remove an laddr entry from the local address list (on an assoc) */ void sctp_remove_laddr(struct sctp_laddr *laddr) { /* remove from the list */ LIST_REMOVE(laddr, sctp_nxt_addr); sctp_free_ifa(laddr->ifa); SCTP_ZONE_FREE(SCTP_BASE_INFO(ipi_zone_laddr), laddr); SCTP_DECR_LADDR_COUNT(); } /* * Remove a local address from the TCB local address restricted list */ void sctp_del_local_addr_restricted(struct sctp_tcb *stcb, struct sctp_ifa *ifa) { struct sctp_inpcb *inp; struct sctp_laddr *laddr; /* * This is called by asconf work. It is assumed that a) The TCB is * locked and b) The INP is locked. This is true in as much as I can * trace through the entry asconf code where I did these locks. * Again, the ASCONF code is a bit different in that it does lock * the INP during its work often times. This must be since we don't * want other proc's looking up things while what they are looking * up is changing :-D */ inp = stcb->sctp_ep; /* if subset bound and don't allow ASCONF's, can't delete last */ if (((inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) == 0) && sctp_is_feature_off(inp, SCTP_PCB_FLAGS_DO_ASCONF)) { if (stcb->sctp_ep->laddr_count < 2) { /* can't delete last address */ return; } } LIST_FOREACH(laddr, &stcb->asoc.sctp_restricted_addrs, sctp_nxt_addr) { /* remove the address if it exists */ if (laddr->ifa == NULL) continue; if (laddr->ifa == ifa) { sctp_remove_laddr(laddr); return; } } /* address not found! */ return; } /* * Temporarily remove for __APPLE__ until we use the Tiger equivalents */ /* sysctl */ static int sctp_max_number_of_assoc = SCTP_MAX_NUM_OF_ASOC; static int sctp_scale_up_for_address = SCTP_SCALE_FOR_ADDR; void sctp_pcb_init() { /* * SCTP initialization for the PCB structures should be called by * the sctp_init() funciton. */ int i; struct timeval tv; if (SCTP_BASE_VAR(sctp_pcb_initialized) != 0) { /* error I was called twice */ return; } SCTP_BASE_VAR(sctp_pcb_initialized) = 1; #if defined(SCTP_LOCAL_TRACE_BUF) bzero(&SCTP_BASE_SYSCTL(sctp_log), sizeof(struct sctp_log)); #endif (void)SCTP_GETTIME_TIMEVAL(&tv); SCTP_BASE_STAT(sctps_discontinuitytime).tv_sec = (uint32_t) tv.tv_sec; SCTP_BASE_STAT(sctps_discontinuitytime).tv_usec = (uint32_t) tv.tv_usec; /* init the empty list of (All) Endpoints */ LIST_INIT(&SCTP_BASE_INFO(listhead)); /* init the iterator head */ TAILQ_INIT(&SCTP_BASE_INFO(iteratorhead)); /* init the hash table of endpoints */ TUNABLE_INT_FETCH("net.inet.sctp.tcbhashsize", &SCTP_BASE_SYSCTL(sctp_hashtblsize)); TUNABLE_INT_FETCH("net.inet.sctp.pcbhashsize", &SCTP_BASE_SYSCTL(sctp_pcbtblsize)); TUNABLE_INT_FETCH("net.inet.sctp.chunkscale", &SCTP_BASE_SYSCTL(sctp_chunkscale)); SCTP_BASE_INFO(sctp_asochash) = SCTP_HASH_INIT((SCTP_BASE_SYSCTL(sctp_hashtblsize) * 31), &SCTP_BASE_INFO(hashasocmark)); SCTP_BASE_INFO(sctp_ephash) = SCTP_HASH_INIT(SCTP_BASE_SYSCTL(sctp_hashtblsize), &SCTP_BASE_INFO(hashmark)); SCTP_BASE_INFO(sctp_tcpephash) = SCTP_HASH_INIT(SCTP_BASE_SYSCTL(sctp_hashtblsize), &SCTP_BASE_INFO(hashtcpmark)); SCTP_BASE_INFO(hashtblsize) = SCTP_BASE_SYSCTL(sctp_hashtblsize); SCTP_BASE_INFO(sctp_vrfhash) = SCTP_HASH_INIT(SCTP_SIZE_OF_VRF_HASH, &SCTP_BASE_INFO(hashvrfmark)); SCTP_BASE_INFO(vrf_ifn_hash) = SCTP_HASH_INIT(SCTP_VRF_IFN_HASH_SIZE, &SCTP_BASE_INFO(vrf_ifn_hashmark)); /* init the zones */ /* * FIX ME: Should check for NULL returns, but if it does fail we are * doomed to panic anyways... add later maybe. */ SCTP_ZONE_INIT(SCTP_BASE_INFO(ipi_zone_ep), "sctp_ep", sizeof(struct sctp_inpcb), maxsockets); SCTP_ZONE_INIT(SCTP_BASE_INFO(ipi_zone_asoc), "sctp_asoc", sizeof(struct sctp_tcb), sctp_max_number_of_assoc); SCTP_ZONE_INIT(SCTP_BASE_INFO(ipi_zone_laddr), "sctp_laddr", sizeof(struct sctp_laddr), (sctp_max_number_of_assoc * sctp_scale_up_for_address)); SCTP_ZONE_INIT(SCTP_BASE_INFO(ipi_zone_net), "sctp_raddr", sizeof(struct sctp_nets), (sctp_max_number_of_assoc * sctp_scale_up_for_address)); SCTP_ZONE_INIT(SCTP_BASE_INFO(ipi_zone_chunk), "sctp_chunk", sizeof(struct sctp_tmit_chunk), (sctp_max_number_of_assoc * SCTP_BASE_SYSCTL(sctp_chunkscale))); SCTP_ZONE_INIT(SCTP_BASE_INFO(ipi_zone_readq), "sctp_readq", sizeof(struct sctp_queued_to_read), (sctp_max_number_of_assoc * SCTP_BASE_SYSCTL(sctp_chunkscale))); SCTP_ZONE_INIT(SCTP_BASE_INFO(ipi_zone_strmoq), "sctp_stream_msg_out", sizeof(struct sctp_stream_queue_pending), (sctp_max_number_of_assoc * SCTP_BASE_SYSCTL(sctp_chunkscale))); SCTP_ZONE_INIT(SCTP_BASE_INFO(ipi_zone_asconf), "sctp_asconf", sizeof(struct sctp_asconf), (sctp_max_number_of_assoc * SCTP_BASE_SYSCTL(sctp_chunkscale))); SCTP_ZONE_INIT(SCTP_BASE_INFO(ipi_zone_asconf_ack), "sctp_asconf_ack", sizeof(struct sctp_asconf_ack), (sctp_max_number_of_assoc * SCTP_BASE_SYSCTL(sctp_chunkscale))); /* Master Lock INIT for info structure */ SCTP_INP_INFO_LOCK_INIT(); SCTP_STATLOG_INIT_LOCK(); SCTP_ITERATOR_LOCK_INIT(); SCTP_IPI_COUNT_INIT(); SCTP_IPI_ADDR_INIT(); SCTP_IPI_ITERATOR_WQ_INIT(); #ifdef SCTP_PACKET_LOGGING SCTP_IP_PKTLOG_INIT(); #endif LIST_INIT(&SCTP_BASE_INFO(addr_wq)); /* not sure if we need all the counts */ SCTP_BASE_INFO(ipi_count_ep) = 0; /* assoc/tcb zone info */ SCTP_BASE_INFO(ipi_count_asoc) = 0; /* local addrlist zone info */ SCTP_BASE_INFO(ipi_count_laddr) = 0; /* remote addrlist zone info */ SCTP_BASE_INFO(ipi_count_raddr) = 0; /* chunk info */ SCTP_BASE_INFO(ipi_count_chunk) = 0; /* socket queue zone info */ SCTP_BASE_INFO(ipi_count_readq) = 0; /* stream out queue cont */ SCTP_BASE_INFO(ipi_count_strmoq) = 0; SCTP_BASE_INFO(ipi_free_strmoq) = 0; SCTP_BASE_INFO(ipi_free_chunks) = 0; SCTP_OS_TIMER_INIT(&SCTP_BASE_INFO(addr_wq_timer.timer)); /* Init the TIMEWAIT list */ for (i = 0; i < SCTP_STACK_VTAG_HASH_SIZE; i++) { LIST_INIT(&SCTP_BASE_INFO(vtag_timewait[i])); } #if defined(SCTP_USE_THREAD_BASED_ITERATOR) SCTP_BASE_INFO(iterator_running) = 0; SCTP_BASE_INFO(threads_must_exit) = 0; sctp_startup_iterator(); #endif /* * INIT the default VRF which for BSD is the only one, other O/S's * may have more. But initially they must start with one and then * add the VRF's as addresses are added. */ sctp_init_vrf_list(SCTP_DEFAULT_VRF); } /* * Assumes that the SCTP_BASE_INFO() lock is NOT held. */ void sctp_pcb_finish(void) { struct sctp_vrflist *vrf_bucket; struct sctp_vrf *vrf; struct sctp_ifn *ifn; struct sctp_ifa *ifa; struct sctpvtaghead *chain; struct sctp_tagblock *twait_block, *prev_twait_block; int i; /* * free the vrf/ifn/ifa lists and hashes (be sure address monitor is * destroyed first). */ vrf_bucket = &SCTP_BASE_INFO(sctp_vrfhash)[(SCTP_DEFAULT_VRFID & SCTP_BASE_INFO(hashvrfmark))]; vrf = LIST_FIRST(vrf_bucket); while (vrf) { ifn = LIST_FIRST(&vrf->ifnlist); while (ifn) { ifa = LIST_FIRST(&ifn->ifalist); while (ifa) { /* free the ifa */ LIST_REMOVE(ifa, next_bucket); LIST_REMOVE(ifa, next_ifa); SCTP_FREE(ifa, SCTP_M_IFA); ifa = LIST_FIRST(&ifn->ifalist); } /* free the ifn */ LIST_REMOVE(ifn, next_bucket); LIST_REMOVE(ifn, next_ifn); SCTP_FREE(ifn, SCTP_M_IFN); ifn = LIST_FIRST(&vrf->ifnlist); } SCTP_HASH_FREE(vrf->vrf_addr_hash, vrf->vrf_addr_hashmark); /* free the vrf */ LIST_REMOVE(vrf, next_vrf); SCTP_FREE(vrf, SCTP_M_VRF); vrf = LIST_FIRST(vrf_bucket); } /* free the vrf hashes */ SCTP_HASH_FREE(SCTP_BASE_INFO(sctp_vrfhash), SCTP_BASE_INFO(hashvrfmark)); SCTP_HASH_FREE(SCTP_BASE_INFO(vrf_ifn_hash), SCTP_BASE_INFO(vrf_ifn_hashmark)); /* * free the TIMEWAIT list elements malloc'd in the function * sctp_add_vtag_to_timewait()... */ for (i = 0; i < SCTP_STACK_VTAG_HASH_SIZE; i++) { chain = &SCTP_BASE_INFO(vtag_timewait)[i]; if (!SCTP_LIST_EMPTY(chain)) { prev_twait_block = NULL; LIST_FOREACH(twait_block, chain, sctp_nxt_tagblock) { if (prev_twait_block) { SCTP_FREE(prev_twait_block, SCTP_M_TIMW); } prev_twait_block = twait_block; } SCTP_FREE(prev_twait_block, SCTP_M_TIMW); } } /* free the locks and mutexes */ #ifdef SCTP_PACKET_LOGGING SCTP_IP_PKTLOG_DESTROY(); #endif SCTP_IPI_ITERATOR_WQ_DESTROY(); SCTP_IPI_ADDR_DESTROY(); SCTP_ITERATOR_LOCK_DESTROY(); SCTP_STATLOG_DESTROY(); SCTP_INP_INFO_LOCK_DESTROY(); SCTP_ZONE_DESTROY(SCTP_BASE_INFO(ipi_zone_ep)); SCTP_ZONE_DESTROY(SCTP_BASE_INFO(ipi_zone_asoc)); SCTP_ZONE_DESTROY(SCTP_BASE_INFO(ipi_zone_laddr)); SCTP_ZONE_DESTROY(SCTP_BASE_INFO(ipi_zone_net)); SCTP_ZONE_DESTROY(SCTP_BASE_INFO(ipi_zone_chunk)); SCTP_ZONE_DESTROY(SCTP_BASE_INFO(ipi_zone_readq)); SCTP_ZONE_DESTROY(SCTP_BASE_INFO(ipi_zone_strmoq)); SCTP_ZONE_DESTROY(SCTP_BASE_INFO(ipi_zone_asconf)); SCTP_ZONE_DESTROY(SCTP_BASE_INFO(ipi_zone_asconf_ack)); /* Get rid of other stuff to */ if (SCTP_BASE_INFO(sctp_asochash) != NULL) SCTP_HASH_FREE(SCTP_BASE_INFO(sctp_asochash), SCTP_BASE_INFO(hashasocmark)); if (SCTP_BASE_INFO(sctp_ephash) != NULL) SCTP_HASH_FREE(SCTP_BASE_INFO(sctp_ephash), SCTP_BASE_INFO(hashmark)); if (SCTP_BASE_INFO(sctp_tcpephash) != NULL) SCTP_HASH_FREE(SCTP_BASE_INFO(sctp_tcpephash), SCTP_BASE_INFO(hashtcpmark)); } int sctp_load_addresses_from_init(struct sctp_tcb *stcb, struct mbuf *m, int iphlen, int offset, int limit, struct sctphdr *sh, struct sockaddr *altsa) { /* * grub through the INIT pulling addresses and loading them to the * nets structure in the asoc. The from address in the mbuf should * also be loaded (if it is not already). This routine can be called * with either INIT or INIT-ACK's as long as the m points to the IP * packet and the offset points to the beginning of the parameters. */ struct sctp_inpcb *inp, *l_inp; struct sctp_nets *net, *net_tmp; struct ip *iph; struct sctp_paramhdr *phdr, parm_buf; struct sctp_tcb *stcb_tmp; uint16_t ptype, plen; struct sockaddr *sa; struct sockaddr_storage dest_store; struct sockaddr *local_sa = (struct sockaddr *)&dest_store; struct sockaddr_in sin; struct sockaddr_in6 sin6; uint8_t random_store[SCTP_PARAM_BUFFER_SIZE]; struct sctp_auth_random *p_random = NULL; uint16_t random_len = 0; uint8_t hmacs_store[SCTP_PARAM_BUFFER_SIZE]; struct sctp_auth_hmac_algo *hmacs = NULL; uint16_t hmacs_len = 0; uint8_t saw_asconf = 0; uint8_t saw_asconf_ack = 0; uint8_t chunks_store[SCTP_PARAM_BUFFER_SIZE]; struct sctp_auth_chunk_list *chunks = NULL; uint16_t num_chunks = 0; sctp_key_t *new_key; uint32_t keylen; int got_random = 0, got_hmacs = 0, got_chklist = 0; /* First get the destination address setup too. */ memset(&sin, 0, sizeof(sin)); memset(&sin6, 0, sizeof(sin6)); sin.sin_family = AF_INET; sin.sin_len = sizeof(sin); sin.sin_port = stcb->rport; sin6.sin6_family = AF_INET6; sin6.sin6_len = sizeof(struct sockaddr_in6); sin6.sin6_port = stcb->rport; if (altsa == NULL) { iph = mtod(m, struct ip *); switch (iph->ip_v) { case IPVERSION: { /* its IPv4 */ struct sockaddr_in *sin_2; sin_2 = (struct sockaddr_in *)(local_sa); memset(sin_2, 0, sizeof(sin)); sin_2->sin_family = AF_INET; sin_2->sin_len = sizeof(sin); sin_2->sin_port = sh->dest_port; sin_2->sin_addr.s_addr = iph->ip_dst.s_addr; sin.sin_addr = iph->ip_src; sa = (struct sockaddr *)&sin; break; } #ifdef INET6 case IPV6_VERSION >> 4: { /* its IPv6 */ struct ip6_hdr *ip6; struct sockaddr_in6 *sin6_2; ip6 = mtod(m, struct ip6_hdr *); sin6_2 = (struct sockaddr_in6 *)(local_sa); memset(sin6_2, 0, sizeof(sin6)); sin6_2->sin6_family = AF_INET6; sin6_2->sin6_len = sizeof(struct sockaddr_in6); sin6_2->sin6_port = sh->dest_port; sin6.sin6_addr = ip6->ip6_src; sa = (struct sockaddr *)&sin6; break; } #endif default: sa = NULL; break; } } else { /* * For cookies we use the src address NOT from the packet * but from the original INIT */ sa = altsa; } /* Turn off ECN until we get through all params */ stcb->asoc.ecn_allowed = 0; TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) { /* mark all addresses that we have currently on the list */ net->dest_state |= SCTP_ADDR_NOT_IN_ASSOC; } /* does the source address already exist? if so skip it */ l_inp = inp = stcb->sctp_ep; atomic_add_int(&stcb->asoc.refcnt, 1); stcb_tmp = sctp_findassociation_ep_addr(&inp, sa, &net_tmp, local_sa, stcb); atomic_add_int(&stcb->asoc.refcnt, -1); if ((stcb_tmp == NULL && inp == stcb->sctp_ep) || inp == NULL) { /* we must add the source address */ /* no scope set here since we have a tcb already. */ if ((sa->sa_family == AF_INET) && (stcb->asoc.ipv4_addr_legal)) { if (sctp_add_remote_addr(stcb, sa, SCTP_DONOT_SETSCOPE, SCTP_LOAD_ADDR_2)) { return (-1); } } else if ((sa->sa_family == AF_INET6) && (stcb->asoc.ipv6_addr_legal)) { if (sctp_add_remote_addr(stcb, sa, SCTP_DONOT_SETSCOPE, SCTP_LOAD_ADDR_3)) { return (-2); } } } else { if (net_tmp != NULL && stcb_tmp == stcb) { net_tmp->dest_state &= ~SCTP_ADDR_NOT_IN_ASSOC; } else if (stcb_tmp != stcb) { /* It belongs to another association? */ if (stcb_tmp) SCTP_TCB_UNLOCK(stcb_tmp); return (-3); } } if (stcb->asoc.state == 0) { /* the assoc was freed? */ return (-4); } /* * peer must explicitly turn this on. This may have been initialized * to be "on" in order to allow local addr changes while INIT's are * in flight. */ stcb->asoc.peer_supports_asconf = 0; /* now we must go through each of the params. */ phdr = sctp_get_next_param(m, offset, &parm_buf, sizeof(parm_buf)); while (phdr) { ptype = ntohs(phdr->param_type); plen = ntohs(phdr->param_length); /* * printf("ptype => %0x, plen => %d\n", (uint32_t)ptype, * (int)plen); */ if (offset + plen > limit) { break; } if (plen == 0) { break; } if (ptype == SCTP_IPV4_ADDRESS) { if (stcb->asoc.ipv4_addr_legal) { struct sctp_ipv4addr_param *p4, p4_buf; /* ok get the v4 address and check/add */ phdr = sctp_get_next_param(m, offset, (struct sctp_paramhdr *)&p4_buf, sizeof(p4_buf)); if (plen != sizeof(struct sctp_ipv4addr_param) || phdr == NULL) { return (-5); } p4 = (struct sctp_ipv4addr_param *)phdr; sin.sin_addr.s_addr = p4->addr; if (IN_MULTICAST(sin.sin_addr.s_addr)) { /* Skip multi-cast addresses */ goto next_param; } if ((sin.sin_addr.s_addr == INADDR_BROADCAST) || (sin.sin_addr.s_addr == INADDR_ANY)) { goto next_param; } sa = (struct sockaddr *)&sin; inp = stcb->sctp_ep; atomic_add_int(&stcb->asoc.refcnt, 1); stcb_tmp = sctp_findassociation_ep_addr(&inp, sa, &net, local_sa, stcb); atomic_add_int(&stcb->asoc.refcnt, -1); if ((stcb_tmp == NULL && inp == stcb->sctp_ep) || inp == NULL) { /* we must add the source address */ /* * no scope set since we have a tcb * already */ /* * we must validate the state again * here */ add_it_now: if (stcb->asoc.state == 0) { /* the assoc was freed? */ return (-7); } if (sctp_add_remote_addr(stcb, sa, SCTP_DONOT_SETSCOPE, SCTP_LOAD_ADDR_4)) { return (-8); } } else if (stcb_tmp == stcb) { if (stcb->asoc.state == 0) { /* the assoc was freed? */ return (-10); } if (net != NULL) { /* clear flag */ net->dest_state &= ~SCTP_ADDR_NOT_IN_ASSOC; } } else { /* * strange, address is in another * assoc? straighten out locks. */ if (stcb_tmp) { if (SCTP_GET_STATE(&stcb_tmp->asoc) & SCTP_STATE_COOKIE_WAIT) { /* * in setup state we * abort this guy */ sctp_abort_an_association(stcb_tmp->sctp_ep, stcb_tmp, 1, NULL, 0); goto add_it_now; } SCTP_TCB_UNLOCK(stcb_tmp); } if (stcb->asoc.state == 0) { /* the assoc was freed? */ return (-12); } return (-13); } } } else if (ptype == SCTP_IPV6_ADDRESS) { if (stcb->asoc.ipv6_addr_legal) { /* ok get the v6 address and check/add */ struct sctp_ipv6addr_param *p6, p6_buf; phdr = sctp_get_next_param(m, offset, (struct sctp_paramhdr *)&p6_buf, sizeof(p6_buf)); if (plen != sizeof(struct sctp_ipv6addr_param) || phdr == NULL) { return (-14); } p6 = (struct sctp_ipv6addr_param *)phdr; memcpy((caddr_t)&sin6.sin6_addr, p6->addr, sizeof(p6->addr)); if (IN6_IS_ADDR_MULTICAST(&sin6.sin6_addr)) { /* Skip multi-cast addresses */ goto next_param; } if (IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr)) { /* * Link local make no sense without * scope */ goto next_param; } sa = (struct sockaddr *)&sin6; inp = stcb->sctp_ep; atomic_add_int(&stcb->asoc.refcnt, 1); stcb_tmp = sctp_findassociation_ep_addr(&inp, sa, &net, local_sa, stcb); atomic_add_int(&stcb->asoc.refcnt, -1); if (stcb_tmp == NULL && (inp == stcb->sctp_ep || inp == NULL)) { /* * we must validate the state again * here */ add_it_now6: if (stcb->asoc.state == 0) { /* the assoc was freed? */ return (-16); } /* * we must add the address, no scope * set */ if (sctp_add_remote_addr(stcb, sa, SCTP_DONOT_SETSCOPE, SCTP_LOAD_ADDR_5)) { return (-17); } } else if (stcb_tmp == stcb) { /* * we must validate the state again * here */ if (stcb->asoc.state == 0) { /* the assoc was freed? */ return (-19); } if (net != NULL) { /* clear flag */ net->dest_state &= ~SCTP_ADDR_NOT_IN_ASSOC; } } else { /* * strange, address is in another * assoc? straighten out locks. */ if (stcb_tmp) if (SCTP_GET_STATE(&stcb_tmp->asoc) & SCTP_STATE_COOKIE_WAIT) { /* * in setup state we * abort this guy */ sctp_abort_an_association(stcb_tmp->sctp_ep, stcb_tmp, 1, NULL, 0); goto add_it_now6; } SCTP_TCB_UNLOCK(stcb_tmp); if (stcb->asoc.state == 0) { /* the assoc was freed? */ return (-21); } return (-22); } } } else if (ptype == SCTP_ECN_CAPABLE) { stcb->asoc.ecn_allowed = 1; } else if (ptype == SCTP_ULP_ADAPTATION) { if (stcb->asoc.state != SCTP_STATE_OPEN) { struct sctp_adaptation_layer_indication ai, *aip; phdr = sctp_get_next_param(m, offset, (struct sctp_paramhdr *)&ai, sizeof(ai)); aip = (struct sctp_adaptation_layer_indication *)phdr; if (aip) { stcb->asoc.peers_adaptation = ntohl(aip->indication); stcb->asoc.adaptation_needed = 1; } } } else if (ptype == SCTP_SET_PRIM_ADDR) { struct sctp_asconf_addr_param lstore, *fee; struct sctp_asconf_addrv4_param *fii; int lptype; struct sockaddr *lsa = NULL; stcb->asoc.peer_supports_asconf = 1; if (plen > sizeof(lstore)) { return (-23); } phdr = sctp_get_next_param(m, offset, (struct sctp_paramhdr *)&lstore, min(plen, sizeof(lstore))); if (phdr == NULL) { return (-24); } fee = (struct sctp_asconf_addr_param *)phdr; lptype = ntohs(fee->addrp.ph.param_type); if (lptype == SCTP_IPV4_ADDRESS) { if (plen != sizeof(struct sctp_asconf_addrv4_param)) { SCTP_PRINTF("Sizeof setprim in init/init ack not %d but %d - ignored\n", (int)sizeof(struct sctp_asconf_addrv4_param), plen); } else { fii = (struct sctp_asconf_addrv4_param *)fee; sin.sin_addr.s_addr = fii->addrp.addr; lsa = (struct sockaddr *)&sin; } } else if (lptype == SCTP_IPV6_ADDRESS) { if (plen != sizeof(struct sctp_asconf_addr_param)) { SCTP_PRINTF("Sizeof setprim (v6) in init/init ack not %d but %d - ignored\n", (int)sizeof(struct sctp_asconf_addr_param), plen); } else { memcpy(sin6.sin6_addr.s6_addr, fee->addrp.addr, sizeof(fee->addrp.addr)); lsa = (struct sockaddr *)&sin6; } } if (lsa) { (void)sctp_set_primary_addr(stcb, sa, NULL); } } else if (ptype == SCTP_HAS_NAT_SUPPORT) { stcb->asoc.peer_supports_nat = 1; } else if (ptype == SCTP_PRSCTP_SUPPORTED) { /* Peer supports pr-sctp */ stcb->asoc.peer_supports_prsctp = 1; } else if (ptype == SCTP_SUPPORTED_CHUNK_EXT) { /* A supported extension chunk */ struct sctp_supported_chunk_types_param *pr_supported; uint8_t local_store[SCTP_PARAM_BUFFER_SIZE]; int num_ent, i; phdr = sctp_get_next_param(m, offset, (struct sctp_paramhdr *)&local_store, min(sizeof(local_store), plen)); if (phdr == NULL) { return (-25); } stcb->asoc.peer_supports_asconf = 0; stcb->asoc.peer_supports_prsctp = 0; stcb->asoc.peer_supports_pktdrop = 0; stcb->asoc.peer_supports_strreset = 0; stcb->asoc.peer_supports_nr_sack = 0; stcb->asoc.peer_supports_auth = 0; pr_supported = (struct sctp_supported_chunk_types_param *)phdr; num_ent = plen - sizeof(struct sctp_paramhdr); for (i = 0; i < num_ent; i++) { switch (pr_supported->chunk_types[i]) { case SCTP_ASCONF: case SCTP_ASCONF_ACK: stcb->asoc.peer_supports_asconf = 1; break; case SCTP_FORWARD_CUM_TSN: stcb->asoc.peer_supports_prsctp = 1; break; case SCTP_PACKET_DROPPED: stcb->asoc.peer_supports_pktdrop = 1; break; case SCTP_NR_SELECTIVE_ACK: if (SCTP_BASE_SYSCTL(sctp_nr_sack_on_off)) stcb->asoc.peer_supports_nr_sack = 1; else stcb->asoc.peer_supports_nr_sack = 0; break; case SCTP_STREAM_RESET: stcb->asoc.peer_supports_strreset = 1; break; case SCTP_AUTHENTICATION: stcb->asoc.peer_supports_auth = 1; break; default: /* one I have not learned yet */ break; } } } else if (ptype == SCTP_ECN_NONCE_SUPPORTED) { /* Peer supports ECN-nonce */ stcb->asoc.peer_supports_ecn_nonce = 1; stcb->asoc.ecn_nonce_allowed = 1; } else if (ptype == SCTP_RANDOM) { if (plen > sizeof(random_store)) break; if (got_random) { /* already processed a RANDOM */ goto next_param; } phdr = sctp_get_next_param(m, offset, (struct sctp_paramhdr *)random_store, min(sizeof(random_store), plen)); if (phdr == NULL) return (-26); p_random = (struct sctp_auth_random *)phdr; random_len = plen - sizeof(*p_random); /* enforce the random length */ if (random_len != SCTP_AUTH_RANDOM_SIZE_REQUIRED) { SCTPDBG(SCTP_DEBUG_AUTH1, "SCTP: invalid RANDOM len\n"); return (-27); } got_random = 1; } else if (ptype == SCTP_HMAC_LIST) { int num_hmacs; int i; if (plen > sizeof(hmacs_store)) break; if (got_hmacs) { /* already processed a HMAC list */ goto next_param; } phdr = sctp_get_next_param(m, offset, (struct sctp_paramhdr *)hmacs_store, min(plen, sizeof(hmacs_store))); if (phdr == NULL) return (-28); hmacs = (struct sctp_auth_hmac_algo *)phdr; hmacs_len = plen - sizeof(*hmacs); num_hmacs = hmacs_len / sizeof(hmacs->hmac_ids[0]); /* validate the hmac list */ if (sctp_verify_hmac_param(hmacs, num_hmacs)) { return (-29); } if (stcb->asoc.peer_hmacs != NULL) sctp_free_hmaclist(stcb->asoc.peer_hmacs); stcb->asoc.peer_hmacs = sctp_alloc_hmaclist(num_hmacs); if (stcb->asoc.peer_hmacs != NULL) { for (i = 0; i < num_hmacs; i++) { (void)sctp_auth_add_hmacid(stcb->asoc.peer_hmacs, ntohs(hmacs->hmac_ids[i])); } } got_hmacs = 1; } else if (ptype == SCTP_CHUNK_LIST) { int i; if (plen > sizeof(chunks_store)) break; if (got_chklist) { /* already processed a Chunks list */ goto next_param; } phdr = sctp_get_next_param(m, offset, (struct sctp_paramhdr *)chunks_store, min(plen, sizeof(chunks_store))); if (phdr == NULL) return (-30); chunks = (struct sctp_auth_chunk_list *)phdr; num_chunks = plen - sizeof(*chunks); if (stcb->asoc.peer_auth_chunks != NULL) sctp_clear_chunklist(stcb->asoc.peer_auth_chunks); else stcb->asoc.peer_auth_chunks = sctp_alloc_chunklist(); for (i = 0; i < num_chunks; i++) { (void)sctp_auth_add_chunk(chunks->chunk_types[i], stcb->asoc.peer_auth_chunks); /* record asconf/asconf-ack if listed */ if (chunks->chunk_types[i] == SCTP_ASCONF) saw_asconf = 1; if (chunks->chunk_types[i] == SCTP_ASCONF_ACK) saw_asconf_ack = 1; } got_chklist = 1; } else if ((ptype == SCTP_HEARTBEAT_INFO) || (ptype == SCTP_STATE_COOKIE) || (ptype == SCTP_UNRECOG_PARAM) || (ptype == SCTP_COOKIE_PRESERVE) || (ptype == SCTP_SUPPORTED_ADDRTYPE) || (ptype == SCTP_ADD_IP_ADDRESS) || (ptype == SCTP_DEL_IP_ADDRESS) || (ptype == SCTP_ERROR_CAUSE_IND) || (ptype == SCTP_SUCCESS_REPORT)) { /* don't care */ ; } else { if ((ptype & 0x8000) == 0x0000) { /* * must stop processing the rest of the * param's. Any report bits were handled * with the call to * sctp_arethere_unrecognized_parameters() * when the INIT or INIT-ACK was first seen. */ break; } } next_param: offset += SCTP_SIZE32(plen); if (offset >= limit) { break; } phdr = sctp_get_next_param(m, offset, &parm_buf, sizeof(parm_buf)); } /* Now check to see if we need to purge any addresses */ for (net = TAILQ_FIRST(&stcb->asoc.nets); net != NULL; net = net_tmp) { net_tmp = TAILQ_NEXT(net, sctp_next); if ((net->dest_state & SCTP_ADDR_NOT_IN_ASSOC) == SCTP_ADDR_NOT_IN_ASSOC) { /* This address has been removed from the asoc */ /* remove and free it */ stcb->asoc.numnets--; TAILQ_REMOVE(&stcb->asoc.nets, net, sctp_next); sctp_free_remote_addr(net); if (net == stcb->asoc.primary_destination) { stcb->asoc.primary_destination = NULL; sctp_select_primary_destination(stcb); } } } /* validate authentication required parameters */ if (got_random && got_hmacs) { stcb->asoc.peer_supports_auth = 1; } else { stcb->asoc.peer_supports_auth = 0; } if (!stcb->asoc.peer_supports_auth && got_chklist) { /* peer does not support auth but sent a chunks list? */ return (-31); } if (!SCTP_BASE_SYSCTL(sctp_asconf_auth_nochk) && stcb->asoc.peer_supports_asconf && !stcb->asoc.peer_supports_auth) { /* peer supports asconf but not auth? */ return (-32); } else if ((stcb->asoc.peer_supports_asconf) && (stcb->asoc.peer_supports_auth) && ((saw_asconf == 0) || (saw_asconf_ack == 0))) { return (-33); } /* concatenate the full random key */ #ifdef SCTP_AUTH_DRAFT_04 keylen = random_len; new_key = sctp_alloc_key(keylen); if (new_key != NULL) { /* copy in the RANDOM */ if (p_random != NULL) bcopy(p_random->random_data, new_key->key, random_len); } #else keylen = sizeof(*p_random) + random_len + sizeof(*hmacs) + hmacs_len; if (chunks != NULL) { keylen += sizeof(*chunks) + num_chunks; } new_key = sctp_alloc_key(keylen); if (new_key != NULL) { /* copy in the RANDOM */ if (p_random != NULL) { keylen = sizeof(*p_random) + random_len; bcopy(p_random, new_key->key, keylen); } /* append in the AUTH chunks */ if (chunks != NULL) { bcopy(chunks, new_key->key + keylen, sizeof(*chunks) + num_chunks); keylen += sizeof(*chunks) + num_chunks; } /* append in the HMACs */ if (hmacs != NULL) { bcopy(hmacs, new_key->key + keylen, sizeof(*hmacs) + hmacs_len); } } #endif else { /* failed to get memory for the key */ return (-34); } if (stcb->asoc.authinfo.peer_random != NULL) sctp_free_key(stcb->asoc.authinfo.peer_random); stcb->asoc.authinfo.peer_random = new_key; #ifdef SCTP_AUTH_DRAFT_04 /* don't include the chunks and hmacs for draft -04 */ stcb->asoc.authinfo.peer_random->keylen = random_len; #endif sctp_clear_cachedkeys(stcb, stcb->asoc.authinfo.assoc_keyid); sctp_clear_cachedkeys(stcb, stcb->asoc.authinfo.recv_keyid); return (0); } int sctp_set_primary_addr(struct sctp_tcb *stcb, struct sockaddr *sa, struct sctp_nets *net) { /* make sure the requested primary address exists in the assoc */ if (net == NULL && sa) net = sctp_findnet(stcb, sa); if (net == NULL) { /* didn't find the requested primary address! */ return (-1); } else { /* set the primary address */ if (net->dest_state & SCTP_ADDR_UNCONFIRMED) { /* Must be confirmed, so queue to set */ net->dest_state |= SCTP_ADDR_REQ_PRIMARY; return (0); } stcb->asoc.primary_destination = net; net->dest_state &= ~SCTP_ADDR_WAS_PRIMARY; net = TAILQ_FIRST(&stcb->asoc.nets); if (net != stcb->asoc.primary_destination) { /* * first one on the list is NOT the primary * sctp_cmpaddr() is much more efficent if the * primary is the first on the list, make it so. */ TAILQ_REMOVE(&stcb->asoc.nets, stcb->asoc.primary_destination, sctp_next); TAILQ_INSERT_HEAD(&stcb->asoc.nets, stcb->asoc.primary_destination, sctp_next); } return (0); } } int sctp_is_vtag_good(struct sctp_inpcb *inp, uint32_t tag, uint16_t lport, uint16_t rport, struct timeval *now, int save_in_twait) { /* * This function serves two purposes. It will see if a TAG can be * re-used and return 1 for yes it is ok and 0 for don't use that * tag. A secondary function it will do is purge out old tags that * can be removed. */ struct sctpvtaghead *chain; struct sctp_tagblock *twait_block; struct sctpasochead *head; struct sctp_tcb *stcb; int i; SCTP_INP_INFO_RLOCK(); head = &SCTP_BASE_INFO(sctp_asochash)[SCTP_PCBHASH_ASOC(tag, SCTP_BASE_INFO(hashasocmark))]; if (head == NULL) { /* invalid vtag */ goto skip_vtag_check; } LIST_FOREACH(stcb, head, sctp_asocs) { /* * We choose not to lock anything here. TCB's can't be * removed since we have the read lock, so they can't be * freed on us, same thing for the INP. I may be wrong with * this assumption, but we will go with it for now :-) */ if (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_SOCKET_ALLGONE) { continue; } if (stcb->asoc.my_vtag == tag) { /* candidate */ if (stcb->rport != rport) { continue; } if (stcb->sctp_ep->sctp_lport != lport) { continue; } /* Its a used tag set */ SCTP_INP_INFO_WUNLOCK(); return (0); } } skip_vtag_check: chain = &SCTP_BASE_INFO(vtag_timewait[(tag % SCTP_STACK_VTAG_HASH_SIZE))]; /* Now what about timed wait ? */ if (!SCTP_LIST_EMPTY(chain)) { /* * Block(s) are present, lets see if we have this tag in the * list */ LIST_FOREACH(twait_block, chain, sctp_nxt_tagblock) { for (i = 0; i < SCTP_NUMBER_IN_VTAG_BLOCK; i++) { if (twait_block->vtag_block[i].v_tag == 0) { /* not used */ continue; } else if ((long)twait_block->vtag_block[i].tv_sec_at_expire < now->tv_sec) { /* Audit expires this guy */ twait_block->vtag_block[i].tv_sec_at_expire = 0; twait_block->vtag_block[i].v_tag = 0; twait_block->vtag_block[i].lport = 0; twait_block->vtag_block[i].rport = 0; } else if ((twait_block->vtag_block[i].v_tag == tag) && (twait_block->vtag_block[i].lport == lport) && (twait_block->vtag_block[i].rport == rport)) { /* Bad tag, sorry :< */ SCTP_INP_INFO_WUNLOCK(); return (0); } } } } SCTP_INP_INFO_RUNLOCK(); return (1); } static sctp_assoc_t reneged_asoc_ids[256]; static uint8_t reneged_at = 0; static void sctp_drain_mbufs(struct sctp_inpcb *inp, struct sctp_tcb *stcb) { /* * We must hunt this association for MBUF's past the cumack (i.e. * out of order data that we can renege on). */ struct sctp_association *asoc; struct sctp_tmit_chunk *chk, *nchk; uint32_t cumulative_tsn_p1, tsn; struct sctp_queued_to_read *ctl, *nctl; int cnt, strmat, gap; /* We look for anything larger than the cum-ack + 1 */ SCTP_STAT_INCR(sctps_protocol_drain_calls); if (SCTP_BASE_SYSCTL(sctp_do_drain) == 0) { return; } asoc = &stcb->asoc; if (asoc->cumulative_tsn == asoc->highest_tsn_inside_map) { /* none we can reneg on. */ return; } SCTP_STAT_INCR(sctps_protocol_drains_done); cumulative_tsn_p1 = asoc->cumulative_tsn + 1; cnt = 0; /* First look in the re-assembly queue */ chk = TAILQ_FIRST(&asoc->reasmqueue); while (chk) { /* Get the next one */ nchk = TAILQ_NEXT(chk, sctp_next); if (compare_with_wrap(chk->rec.data.TSN_seq, cumulative_tsn_p1, MAX_TSN)) { /* Yep it is above cum-ack */ cnt++; tsn = chk->rec.data.TSN_seq; if (tsn >= asoc->mapping_array_base_tsn) { gap = tsn - asoc->mapping_array_base_tsn; } else { gap = (MAX_TSN - asoc->mapping_array_base_tsn) + tsn + 1; } asoc->size_on_reasm_queue = sctp_sbspace_sub(asoc->size_on_reasm_queue, chk->send_size); sctp_ucount_decr(asoc->cnt_on_reasm_queue); SCTP_UNSET_TSN_PRESENT(asoc->mapping_array, gap); TAILQ_REMOVE(&asoc->reasmqueue, chk, sctp_next); if (chk->data) { sctp_m_freem(chk->data); chk->data = NULL; } sctp_free_a_chunk(stcb, chk); } chk = nchk; } /* Ok that was fun, now we will drain all the inbound streams? */ for (strmat = 0; strmat < asoc->streamincnt; strmat++) { ctl = TAILQ_FIRST(&asoc->strmin[strmat].inqueue); while (ctl) { nctl = TAILQ_NEXT(ctl, next); if (compare_with_wrap(ctl->sinfo_tsn, cumulative_tsn_p1, MAX_TSN)) { /* Yep it is above cum-ack */ cnt++; tsn = ctl->sinfo_tsn; if (tsn >= asoc->mapping_array_base_tsn) { gap = tsn - asoc->mapping_array_base_tsn; } else { gap = (MAX_TSN - asoc->mapping_array_base_tsn) + tsn + 1; } asoc->size_on_all_streams = sctp_sbspace_sub(asoc->size_on_all_streams, ctl->length); sctp_ucount_decr(asoc->cnt_on_all_streams); SCTP_UNSET_TSN_PRESENT(asoc->mapping_array, gap); TAILQ_REMOVE(&asoc->strmin[strmat].inqueue, ctl, next); if (ctl->data) { sctp_m_freem(ctl->data); ctl->data = NULL; } sctp_free_remote_addr(ctl->whoFrom); SCTP_ZONE_FREE(SCTP_BASE_INFO(ipi_zone_readq), ctl); SCTP_DECR_READQ_COUNT(); } ctl = nctl; } } /* * Question, should we go through the delivery queue? The only * reason things are on here is the app not reading OR a p-d-api up. * An attacker COULD send enough in to initiate the PD-API and then * send a bunch of stuff to other streams... these would wind up on * the delivery queue.. and then we would not get to them. But in * order to do this I then have to back-track and un-deliver * sequence numbers in streams.. el-yucko. I think for now we will * NOT look at the delivery queue and leave it to be something to * consider later. An alternative would be to abort the P-D-API with * a notification and then deliver the data.... Or another method * might be to keep track of how many times the situation occurs and * if we see a possible attack underway just abort the association. */ #ifdef SCTP_DEBUG if (cnt) { SCTPDBG(SCTP_DEBUG_PCB1, "Freed %d chunks from reneg harvest\n", cnt); } #endif if (cnt) { /* * Now do we need to find a new * asoc->highest_tsn_inside_map? */ if (asoc->highest_tsn_inside_map >= asoc->mapping_array_base_tsn) { gap = asoc->highest_tsn_inside_map - asoc->mapping_array_base_tsn; } else { gap = (MAX_TSN - asoc->mapping_array_base_tsn) + asoc->highest_tsn_inside_map + 1; } if (gap >= (asoc->mapping_array_size << 3)) { /* * Something bad happened or cum-ack and high were * behind the base, but if so earlier checks should * have found NO data... wierd... we will start at * end of mapping array. */ SCTP_PRINTF("Gap was larger than array?? %d set to max:%d maparraymax:%x\n", (int)gap, (int)(asoc->mapping_array_size << 3), (int)asoc->highest_tsn_inside_map); gap = asoc->mapping_array_size << 3; } while (gap > 0) { if (SCTP_IS_TSN_PRESENT(asoc->mapping_array, gap)) { /* found the new highest */ asoc->highest_tsn_inside_map = asoc->mapping_array_base_tsn + gap; if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_MAP_LOGGING_ENABLE) { sctp_log_map(0, 8, asoc->highest_tsn_inside_map, SCTP_MAP_SLIDE_RESULT); } break; } gap--; } if (gap == 0) { /* Nothing left in map */ memset(asoc->mapping_array, 0, asoc->mapping_array_size); asoc->mapping_array_base_tsn = asoc->cumulative_tsn + 1; asoc->highest_tsn_inside_map = asoc->cumulative_tsn; if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_MAP_LOGGING_ENABLE) { sctp_log_map(0, 9, asoc->highest_tsn_inside_map, SCTP_MAP_SLIDE_RESULT); } } asoc->last_revoke_count = cnt; (void)SCTP_OS_TIMER_STOP(&stcb->asoc.dack_timer.timer); /* sa_ignore NO_NULL_CHK */ sctp_send_sack(stcb); sctp_chunk_output(stcb->sctp_ep, stcb, SCTP_OUTPUT_FROM_DRAIN, SCTP_SO_NOT_LOCKED); reneged_asoc_ids[reneged_at] = sctp_get_associd(stcb); reneged_at++; } /* * Another issue, in un-setting the TSN's in the mapping array we * DID NOT adjust the higest_tsn marker. This will cause one of two * things to occur. It may cause us to do extra work in checking for * our mapping array movement. More importantly it may cause us to * SACK every datagram. This may not be a bad thing though since we * will recover once we get our cum-ack above and all this stuff we * dumped recovered. */ } void sctp_drain() { /* * We must walk the PCB lists for ALL associations here. The system * is LOW on MBUF's and needs help. This is where reneging will * occur. We really hope this does NOT happen! */ - struct sctp_inpcb *inp; - struct sctp_tcb *stcb; - SCTP_INP_INFO_RLOCK(); - LIST_FOREACH(inp, &SCTP_BASE_INFO(listhead), sctp_list) { - /* For each endpoint */ - SCTP_INP_RLOCK(inp); - LIST_FOREACH(stcb, &inp->sctp_asoc_list, sctp_tcblist) { - /* For each association */ - SCTP_TCB_LOCK(stcb); - sctp_drain_mbufs(inp, stcb); - SCTP_TCB_UNLOCK(stcb); + VNET_ITERATOR_DECL(vnet_iter); + + VNET_LIST_RLOCK_NOSLEEP(); + VNET_FOREACH(vnet_iter) { + CURVNET_SET(vnet_iter); + struct sctp_inpcb *inp; + struct sctp_tcb *stcb; + + SCTP_INP_INFO_RLOCK(); + LIST_FOREACH(inp, &SCTP_BASE_INFO(listhead), sctp_list) { + /* For each endpoint */ + SCTP_INP_RLOCK(inp); + LIST_FOREACH(stcb, &inp->sctp_asoc_list, sctp_tcblist) { + /* For each association */ + SCTP_TCB_LOCK(stcb); + sctp_drain_mbufs(inp, stcb); + SCTP_TCB_UNLOCK(stcb); + } + SCTP_INP_RUNLOCK(inp); } - SCTP_INP_RUNLOCK(inp); + SCTP_INP_INFO_RUNLOCK(); + CURVNET_RESTORE(); } - SCTP_INP_INFO_RUNLOCK(); + VNET_LIST_RUNLOCK_NOSLEEP(); } /* * start a new iterator * iterates through all endpoints and associations based on the pcb_state * flags and asoc_state. "af" (mandatory) is executed for all matching * assocs and "ef" (optional) is executed when the iterator completes. * "inpf" (optional) is executed for each new endpoint as it is being * iterated through. inpe (optional) is called when the inp completes * its way through all the stcbs. */ int sctp_initiate_iterator(inp_func inpf, asoc_func af, inp_func inpe, uint32_t pcb_state, uint32_t pcb_features, uint32_t asoc_state, void *argp, uint32_t argi, end_func ef, struct sctp_inpcb *s_inp, uint8_t chunk_output_off) { struct sctp_iterator *it = NULL; if (af == NULL) { return (-1); } SCTP_MALLOC(it, struct sctp_iterator *, sizeof(struct sctp_iterator), SCTP_M_ITER); if (it == NULL) { SCTP_LTRACE_ERR_RET(NULL, NULL, NULL, SCTP_FROM_SCTP_PCB, ENOMEM); return (ENOMEM); } memset(it, 0, sizeof(*it)); it->function_assoc = af; it->function_inp = inpf; if (inpf) it->done_current_ep = 0; else it->done_current_ep = 1; it->function_atend = ef; it->pointer = argp; it->val = argi; it->pcb_flags = pcb_state; it->pcb_features = pcb_features; it->asoc_state = asoc_state; it->function_inp_end = inpe; it->no_chunk_output = chunk_output_off; if (s_inp) { it->inp = s_inp; it->iterator_flags = SCTP_ITERATOR_DO_SINGLE_INP; } else { SCTP_INP_INFO_RLOCK(); it->inp = LIST_FIRST(&SCTP_BASE_INFO(listhead)); SCTP_INP_INFO_RUNLOCK(); it->iterator_flags = SCTP_ITERATOR_DO_ALL_INP; } SCTP_IPI_ITERATOR_WQ_LOCK(); if (it->inp) { SCTP_INP_INCR_REF(it->inp); } TAILQ_INSERT_TAIL(&SCTP_BASE_INFO(iteratorhead), it, sctp_nxt_itr); #if defined(SCTP_USE_THREAD_BASED_ITERATOR) if (SCTP_BASE_INFO(iterator_running) == 0) { sctp_wakeup_iterator(); } SCTP_IPI_ITERATOR_WQ_UNLOCK(); #else if (it->inp) SCTP_INP_DECR_REF(it->inp); SCTP_IPI_ITERATOR_WQ_UNLOCK(); /* Init the timer */ SCTP_OS_TIMER_INIT(&it->tmr.timer); /* add to the list of all iterators */ sctp_timer_start(SCTP_TIMER_TYPE_ITERATOR, (struct sctp_inpcb *)it, NULL, NULL); #endif /* sa_ignore MEMLEAK {memory is put on the tailq for the iterator} */ return (0); } Index: head/sys/netinet/sctp_structs.h =================================================================== --- head/sys/netinet/sctp_structs.h (revision 197325) +++ head/sys/netinet/sctp_structs.h (revision 197326) @@ -1,1083 +1,1084 @@ /*- * Copyright (c) 2001-2008, by Cisco Systems, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * a) Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * b) Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the distribution. * * c) Neither the name of Cisco Systems, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ /* $KAME: sctp_structs.h,v 1.13 2005/03/06 16:04:18 itojun Exp $ */ #include __FBSDID("$FreeBSD$"); #ifndef __sctp_structs_h__ #define __sctp_structs_h__ #include #include #include struct sctp_timer { sctp_os_timer_t timer; int type; /* * Depending on the timer type these will be setup and cast with the * appropriate entity. */ void *ep; void *tcb; void *net; + void *vnet; /* for sanity checking */ void *self; uint32_t ticks; uint32_t stopped_from; }; struct sctp_foo_stuff { struct sctp_inpcb *inp; uint32_t lineno; uint32_t ticks; int updown; }; /* * This is the information we track on each interface that we know about from * the distant end. */ TAILQ_HEAD(sctpnetlisthead, sctp_nets); struct sctp_stream_reset_list { TAILQ_ENTRY(sctp_stream_reset_list) next_resp; uint32_t tsn; int number_entries; struct sctp_stream_reset_out_request req; }; TAILQ_HEAD(sctp_resethead, sctp_stream_reset_list); /* * Users of the iterator need to malloc a iterator with a call to * sctp_initiate_iterator(inp_func, assoc_func, inp_func, pcb_flags, pcb_features, * asoc_state, void-ptr-arg, uint32-arg, end_func, inp); * * Use the following two defines if you don't care what pcb flags are on the EP * and/or you don't care what state the association is in. * * Note that if you specify an INP as the last argument then ONLY each * association of that single INP will be executed upon. Note that the pcb * flags STILL apply so if the inp you specify has different pcb_flags then * what you put in pcb_flags nothing will happen. use SCTP_PCB_ANY_FLAGS to * assure the inp you specify gets treated. */ #define SCTP_PCB_ANY_FLAGS 0x00000000 #define SCTP_PCB_ANY_FEATURES 0x00000000 #define SCTP_ASOC_ANY_STATE 0x00000000 typedef void (*asoc_func) (struct sctp_inpcb *, struct sctp_tcb *, void *ptr, uint32_t val); typedef int (*inp_func) (struct sctp_inpcb *, void *ptr, uint32_t val); typedef void (*end_func) (void *ptr, uint32_t val); struct sctp_iterator { TAILQ_ENTRY(sctp_iterator) sctp_nxt_itr; struct sctp_timer tmr; struct sctp_inpcb *inp; /* current endpoint */ struct sctp_tcb *stcb; /* current* assoc */ asoc_func function_assoc; /* per assoc function */ inp_func function_inp; /* per endpoint function */ inp_func function_inp_end; /* end INP function */ end_func function_atend;/* iterator completion function */ void *pointer; /* pointer for apply func to use */ uint32_t val; /* value for apply func to use */ uint32_t pcb_flags; /* endpoint flags being checked */ uint32_t pcb_features; /* endpoint features being checked */ uint32_t asoc_state; /* assoc state being checked */ uint32_t iterator_flags; uint8_t no_chunk_output; uint8_t done_current_ep; }; /* iterator_flags values */ #define SCTP_ITERATOR_DO_ALL_INP 0x00000001 #define SCTP_ITERATOR_DO_SINGLE_INP 0x00000002 TAILQ_HEAD(sctpiterators, sctp_iterator); struct sctp_copy_all { struct sctp_inpcb *inp; /* ep */ struct mbuf *m; struct sctp_sndrcvinfo sndrcv; int sndlen; int cnt_sent; int cnt_failed; }; struct sctp_asconf_iterator { struct sctpladdr list_of_work; int cnt; }; struct sctp_net_route { sctp_rtentry_t *ro_rt; void *ro_lle; union sctp_sockstore _l_addr; /* remote peer addr */ struct sctp_ifa *_s_addr; /* our selected src addr */ }; struct htcp { uint16_t alpha; /* Fixed point arith, << 7 */ uint8_t beta; /* Fixed point arith, << 7 */ uint8_t modeswitch; /* Delay modeswitch until we had at least one * congestion event */ uint32_t last_cong; /* Time since last congestion event end */ uint32_t undo_last_cong; uint16_t bytes_acked; uint32_t bytecount; uint32_t minRTT; uint32_t maxRTT; uint32_t undo_maxRTT; uint32_t undo_old_maxB; /* Bandwidth estimation */ uint32_t minB; uint32_t maxB; uint32_t old_maxB; uint32_t Bi; uint32_t lasttime; }; struct sctp_nets { TAILQ_ENTRY(sctp_nets) sctp_next; /* next link */ /* * Things on the top half may be able to be split into a common * structure shared by all. */ struct sctp_timer pmtu_timer; /* * The following two in combination equate to a route entry for v6 * or v4. */ struct sctp_net_route ro; /* mtu discovered so far */ uint32_t mtu; uint32_t ssthresh; /* not sure about this one for split */ /* smoothed average things for RTT and RTO itself */ int lastsa; int lastsv; int rtt; /* last measured rtt value in ms */ unsigned int RTO; /* This is used for SHUTDOWN/SHUTDOWN-ACK/SEND or INIT timers */ struct sctp_timer rxt_timer; struct sctp_timer fr_timer; /* for early fr */ /* last time in seconds I sent to it */ struct timeval last_sent_time; int ref_count; /* Congestion stats per destination */ /* * flight size variables and such, sorry Vern, I could not avoid * this if I wanted performance :> */ uint32_t flight_size; uint32_t cwnd; /* actual cwnd */ uint32_t prev_cwnd; /* cwnd before any processing */ uint32_t partial_bytes_acked; /* in CA tracks when to incr a MTU */ uint32_t prev_rtt; /* tracking variables to avoid the aloc/free in sack processing */ unsigned int net_ack; unsigned int net_ack2; /* * JRS - 5/8/07 - Variable to track last time a destination was * active for CMT PF */ uint32_t last_active; /* * CMT variables (iyengar@cis.udel.edu) */ uint32_t this_sack_highest_newack; /* tracks highest TSN newly * acked for a given dest in * the current SACK. Used in * SFR and HTNA algos */ uint32_t pseudo_cumack; /* CMT CUC algorithm. Maintains next expected * pseudo-cumack for this destination */ uint32_t rtx_pseudo_cumack; /* CMT CUC algorithm. Maintains next * expected pseudo-cumack for this * destination */ /* CMT fast recovery variables */ uint32_t fast_recovery_tsn; uint32_t heartbeat_random1; uint32_t heartbeat_random2; uint32_t tos_flowlabel; struct timeval start_time; /* time when this net was created */ uint32_t marked_retrans;/* number or DATA chunks marked for timer * based retransmissions */ uint32_t marked_fastretrans; /* if this guy is ok or not ... status */ uint16_t dest_state; /* number of transmit failures to down this guy */ uint16_t failure_threshold; /* error stats on destination */ uint16_t error_count; /* UDP port number in case of UDP tunneling */ uint16_t port; uint8_t fast_retran_loss_recovery; uint8_t will_exit_fast_recovery; /* Flags that probably can be combined into dest_state */ uint8_t fast_retran_ip; /* fast retransmit in progress */ uint8_t hb_responded; uint8_t saw_newack; /* CMT's SFR algorithm flag */ uint8_t src_addr_selected; /* if we split we move */ uint8_t indx_of_eligible_next_to_use; uint8_t addr_is_local; /* its a local address (if known) could move * in split */ /* * CMT variables (iyengar@cis.udel.edu) */ uint8_t find_pseudo_cumack; /* CMT CUC algorithm. Flag used to * find a new pseudocumack. This flag * is set after a new pseudo-cumack * has been received and indicates * that the sender should find the * next pseudo-cumack expected for * this destination */ uint8_t find_rtx_pseudo_cumack; /* CMT CUCv2 algorithm. Flag used to * find a new rtx-pseudocumack. This * flag is set after a new * rtx-pseudo-cumack has been received * and indicates that the sender * should find the next * rtx-pseudo-cumack expected for this * destination */ uint8_t new_pseudo_cumack; /* CMT CUC algorithm. Flag used to * indicate if a new pseudo-cumack or * rtx-pseudo-cumack has been received */ uint8_t window_probe; /* Doing a window probe? */ uint8_t RTO_measured; /* Have we done the first measure */ uint8_t last_hs_used; /* index into the last HS table entry we used */ /* JRS - struct used in HTCP algorithm */ struct htcp htcp_ca; }; struct sctp_data_chunkrec { uint32_t TSN_seq; /* the TSN of this transmit */ uint16_t stream_seq; /* the stream sequence number of this transmit */ uint16_t stream_number; /* the stream number of this guy */ uint32_t payloadtype; uint32_t context; /* from send */ /* ECN Nonce: Nonce Value for this chunk */ uint8_t ect_nonce; uint8_t fwd_tsn_cnt; /* * part of the Highest sacked algorithm to be able to stroke counts * on ones that are FR'd. */ uint32_t fast_retran_tsn; /* sending_seq at the time of FR */ struct timeval timetodrop; /* time we drop it from queue */ uint8_t doing_fast_retransmit; uint8_t rcv_flags; /* flags pulled from data chunk on inbound for * outbound holds sending flags for PR-SCTP. */ uint8_t state_flags; uint8_t chunk_was_revoked; }; TAILQ_HEAD(sctpchunk_listhead, sctp_tmit_chunk); /* The lower byte is used to enumerate PR_SCTP policies */ #define CHUNK_FLAGS_PR_SCTP_TTL SCTP_PR_SCTP_TTL #define CHUNK_FLAGS_PR_SCTP_BUF SCTP_PR_SCTP_BUF #define CHUNK_FLAGS_PR_SCTP_RTX SCTP_PR_SCTP_RTX /* The upper byte is used a a bit mask */ #define CHUNK_FLAGS_FRAGMENT_OK 0x0100 struct chk_id { uint16_t id; uint16_t can_take_data; }; struct sctp_tmit_chunk { union { struct sctp_data_chunkrec data; struct chk_id chunk_id; } rec; struct sctp_association *asoc; /* bp to asoc this belongs to */ struct timeval sent_rcv_time; /* filled in if RTT being calculated */ struct mbuf *data; /* pointer to mbuf chain of data */ struct mbuf *last_mbuf; /* pointer to last mbuf in chain */ struct sctp_nets *whoTo; TAILQ_ENTRY(sctp_tmit_chunk) sctp_next; /* next link */ int32_t sent; /* the send status */ uint16_t snd_count; /* number of times I sent */ uint16_t flags; /* flags, such as FRAGMENT_OK */ uint16_t send_size; uint16_t book_size; uint16_t mbcnt; uint16_t auth_keyid; uint8_t holds_key_ref; /* flag if auth keyid refcount is held */ uint8_t pad_inplace; uint8_t do_rtt; uint8_t book_size_scale; uint8_t addr_over; /* flag which is set if the dest address for * this chunk is overridden by user. Used for * CMT (iyengar@cis.udel.edu, 2005/06/21) */ uint8_t no_fr_allowed; uint8_t pr_sctp_on; uint8_t copy_by_ref; uint8_t window_probe; }; /* * The first part of this structure MUST be the entire sinfo structure. Maybe * I should have made it a sub structure... we can circle back later and do * that if we want. */ struct sctp_queued_to_read { /* sinfo structure Pluse more */ uint16_t sinfo_stream; /* off the wire */ uint16_t sinfo_ssn; /* off the wire */ uint16_t sinfo_flags; /* SCTP_UNORDERED from wire use SCTP_EOF for * EOR */ uint32_t sinfo_ppid; /* off the wire */ uint32_t sinfo_context; /* pick this up from assoc def context? */ uint32_t sinfo_timetolive; /* not used by kernel */ uint32_t sinfo_tsn; /* Use this in reassembly as first TSN */ uint32_t sinfo_cumtsn; /* Use this in reassembly as last TSN */ sctp_assoc_t sinfo_assoc_id; /* our assoc id */ /* Non sinfo stuff */ uint32_t length; /* length of data */ uint32_t held_length; /* length held in sb */ struct sctp_nets *whoFrom; /* where it came from */ struct mbuf *data; /* front of the mbuf chain of data with * PKT_HDR */ struct mbuf *tail_mbuf; /* used for multi-part data */ struct mbuf *aux_data; /* used to hold/cache control if o/s does not * take it from us */ struct sctp_tcb *stcb; /* assoc, used for window update */ TAILQ_ENTRY(sctp_queued_to_read) next; uint16_t port_from; uint16_t spec_flags; /* Flags to hold the notification field */ uint8_t do_not_ref_stcb; uint8_t end_added; uint8_t pdapi_aborted; uint8_t some_taken; }; /* This data structure will be on the outbound * stream queues. Data will be pulled off from * the front of the mbuf data and chunk-ified * by the output routines. We will custom * fit every chunk we pull to the send/sent * queue to make up the next full packet * if we can. An entry cannot be removed * from the stream_out queue until * the msg_is_complete flag is set. This * means at times data/tail_mbuf MIGHT * be NULL.. If that occurs it happens * for one of two reasons. Either the user * is blocked on a send() call and has not * awoken to copy more data down... OR * the user is in the explict MSG_EOR mode * and wrote some data, but has not completed * sending. */ struct sctp_stream_queue_pending { struct mbuf *data; struct mbuf *tail_mbuf; struct timeval ts; struct sctp_nets *net; TAILQ_ENTRY(sctp_stream_queue_pending) next; uint32_t length; uint32_t timetolive; uint32_t ppid; uint32_t context; uint16_t sinfo_flags; uint16_t stream; uint16_t strseq; uint16_t act_flags; uint16_t auth_keyid; uint8_t holds_key_ref; uint8_t msg_is_complete; uint8_t some_taken; uint8_t addr_over; uint8_t pr_sctp_on; uint8_t sender_all_done; uint8_t put_last_out; uint8_t discard_rest; }; /* * this struct contains info that is used to track inbound stream data and * help with ordering. */ TAILQ_HEAD(sctpwheelunrel_listhead, sctp_stream_in); struct sctp_stream_in { struct sctp_readhead inqueue; uint16_t stream_no; uint16_t last_sequence_delivered; /* used for re-order */ uint8_t delivery_started; }; /* This struct is used to track the traffic on outbound streams */ TAILQ_HEAD(sctpwheel_listhead, sctp_stream_out); struct sctp_stream_out { struct sctp_streamhead outqueue; TAILQ_ENTRY(sctp_stream_out) next_spoke; /* next link in wheel */ uint16_t stream_no; uint16_t next_sequence_sent; /* next one I expect to send out */ uint8_t last_msg_incomplete; }; /* used to keep track of the addresses yet to try to add/delete */ TAILQ_HEAD(sctp_asconf_addrhead, sctp_asconf_addr); struct sctp_asconf_addr { TAILQ_ENTRY(sctp_asconf_addr) next; struct sctp_asconf_addr_param ap; struct sctp_ifa *ifa; /* save the ifa for add/del ip */ uint8_t sent; /* has this been sent yet? */ uint8_t special_del; /* not to be used in lookup */ }; struct sctp_scoping { uint8_t ipv4_addr_legal; uint8_t ipv6_addr_legal; uint8_t loopback_scope; uint8_t ipv4_local_scope; uint8_t local_scope; uint8_t site_scope; }; #define SCTP_TSN_LOG_SIZE 40 struct sctp_tsn_log { void *stcb; uint32_t tsn; uint16_t strm; uint16_t seq; uint16_t sz; uint16_t flgs; uint16_t in_pos; uint16_t in_out; }; #define SCTP_FS_SPEC_LOG_SIZE 200 struct sctp_fs_spec_log { uint32_t sent; uint32_t total_flight; uint32_t tsn; uint16_t book; uint8_t incr; uint8_t decr; }; /* This struct is here to cut out the compatiabilty * pad that bulks up both the inp and stcb. The non * pad portion MUST stay in complete sync with * sctp_sndrcvinfo... i.e. if sinfo_xxxx is added * this must be done here too. */ struct sctp_nonpad_sndrcvinfo { uint16_t sinfo_stream; uint16_t sinfo_ssn; uint16_t sinfo_flags; uint32_t sinfo_ppid; uint32_t sinfo_context; uint32_t sinfo_timetolive; uint32_t sinfo_tsn; uint32_t sinfo_cumtsn; sctp_assoc_t sinfo_assoc_id; }; /* * JRS - Structure to hold function pointers to the functions responsible * for congestion control. */ struct sctp_cc_functions { void (*sctp_set_initial_cc_param) (struct sctp_tcb *stcb, struct sctp_nets *net); void (*sctp_cwnd_update_after_sack) (struct sctp_tcb *stcb, struct sctp_association *asoc, int accum_moved, int reneged_all, int will_exit); void (*sctp_cwnd_update_after_fr) (struct sctp_tcb *stcb, struct sctp_association *asoc); void (*sctp_cwnd_update_after_timeout) (struct sctp_tcb *stcb, struct sctp_nets *net); void (*sctp_cwnd_update_after_ecn_echo) (struct sctp_tcb *stcb, struct sctp_nets *net); void (*sctp_cwnd_update_after_packet_dropped) (struct sctp_tcb *stcb, struct sctp_nets *net, struct sctp_pktdrop_chunk *cp, uint32_t * bottle_bw, uint32_t * on_queue); void (*sctp_cwnd_update_after_output) (struct sctp_tcb *stcb, struct sctp_nets *net, int burst_limit); void (*sctp_cwnd_update_after_fr_timer) (struct sctp_inpcb *inp, struct sctp_tcb *stcb, struct sctp_nets *net); }; /* used to save ASCONF chunks for retransmission */ TAILQ_HEAD(sctp_asconf_head, sctp_asconf); struct sctp_asconf { TAILQ_ENTRY(sctp_asconf) next; uint32_t serial_number; uint16_t snd_count; struct mbuf *data; uint16_t len; }; /* used to save ASCONF-ACK chunks for retransmission */ TAILQ_HEAD(sctp_asconf_ackhead, sctp_asconf_ack); struct sctp_asconf_ack { TAILQ_ENTRY(sctp_asconf_ack) next; uint32_t serial_number; struct sctp_nets *last_sent_to; struct mbuf *data; uint16_t len; }; /* * Here we have information about each individual association that we track. * We probably in production would be more dynamic. But for ease of * implementation we will have a fixed array that we hunt for in a linear * fashion. */ struct sctp_association { /* association state */ int state; /* queue of pending addrs to add/delete */ struct sctp_asconf_addrhead asconf_queue; struct timeval time_entered; /* time we entered state */ struct timeval time_last_rcvd; struct timeval time_last_sent; struct timeval time_last_sat_advance; struct sctp_nonpad_sndrcvinfo def_send; /* timers and such */ struct sctp_timer hb_timer; /* hb timer */ struct sctp_timer dack_timer; /* Delayed ack timer */ struct sctp_timer asconf_timer; /* asconf */ struct sctp_timer strreset_timer; /* stream reset */ struct sctp_timer shut_guard_timer; /* shutdown guard */ struct sctp_timer autoclose_timer; /* automatic close timer */ struct sctp_timer delayed_event_timer; /* timer for delayed events */ struct sctp_timer delete_prim_timer; /* deleting primary dst */ /* list of restricted local addresses */ struct sctpladdr sctp_restricted_addrs; /* last local address pending deletion (waiting for an address add) */ struct sctp_ifa *asconf_addr_del_pending; /* Deleted primary destination (used to stop timer) */ struct sctp_nets *deleted_primary; struct sctpnetlisthead nets; /* remote address list */ /* Free chunk list */ struct sctpchunk_listhead free_chunks; /* Control chunk queue */ struct sctpchunk_listhead control_send_queue; /* ASCONF chunk queue */ struct sctpchunk_listhead asconf_send_queue; /* * Once a TSN hits the wire it is moved to the sent_queue. We * maintain two counts here (don't know if any but retran_cnt is * needed). The idea is that the sent_queue_retran_cnt reflects how * many chunks have been marked for retranmission by either T3-rxt * or FR. */ struct sctpchunk_listhead sent_queue; struct sctpchunk_listhead send_queue; /* re-assembly queue for fragmented chunks on the inbound path */ struct sctpchunk_listhead reasmqueue; /* * this queue is used when we reach a condition that we can NOT put * data into the socket buffer. We track the size of this queue and * set our rwnd to the space in the socket minus also the * size_on_delivery_queue. */ struct sctpwheel_listhead out_wheel; /* * This pointer will be set to NULL most of the time. But when we * have a fragmented message, where we could not get out all of the * message at the last send then this will point to the stream to go * get data from. */ struct sctp_stream_out *locked_on_sending; /* If an iterator is looking at me, this is it */ struct sctp_iterator *stcb_starting_point_for_iterator; /* ASCONF save the last ASCONF-ACK so we can resend it if necessary */ struct sctp_asconf_ackhead asconf_ack_sent; /* * pointer to last stream reset queued to control queue by us with * requests. */ struct sctp_tmit_chunk *str_reset; /* * if Source Address Selection happening, this will rotate through * the link list. */ struct sctp_laddr *last_used_address; /* stream arrays */ struct sctp_stream_in *strmin; struct sctp_stream_out *strmout; uint8_t *mapping_array; /* primary destination to use */ struct sctp_nets *primary_destination; /* For CMT */ struct sctp_nets *last_net_cmt_send_started; /* last place I got a data chunk from */ struct sctp_nets *last_data_chunk_from; /* last place I got a control from */ struct sctp_nets *last_control_chunk_from; /* circular looking for output selection */ struct sctp_stream_out *last_out_stream; /* * wait to the point the cum-ack passes req->send_reset_at_tsn for * any req on the list. */ struct sctp_resethead resetHead; /* queue of chunks waiting to be sent into the local stack */ struct sctp_readhead pending_reply_queue; /* JRS - the congestion control functions are in this struct */ struct sctp_cc_functions cc_functions; /* * JRS - value to store the currently loaded congestion control * module */ uint32_t congestion_control_module; uint32_t vrf_id; uint32_t cookie_preserve_req; /* ASCONF next seq I am sending out, inits at init-tsn */ uint32_t asconf_seq_out; uint32_t asconf_seq_out_acked; /* ASCONF last received ASCONF from peer, starts at peer's TSN-1 */ uint32_t asconf_seq_in; /* next seq I am sending in str reset messages */ uint32_t str_reset_seq_out; /* next seq I am expecting in str reset messages */ uint32_t str_reset_seq_in; /* various verification tag information */ uint32_t my_vtag; /* The tag to be used. if assoc is re-initited * by remote end, and I have unlocked this * will be regenerated to a new random value. */ uint32_t peer_vtag; /* The peers last tag */ uint32_t my_vtag_nonce; uint32_t peer_vtag_nonce; uint32_t assoc_id; /* This is the SCTP fragmentation threshold */ uint32_t smallest_mtu; /* * Special hook for Fast retransmit, allows us to track the highest * TSN that is NEW in this SACK if gap ack blocks are present. */ uint32_t this_sack_highest_gap; /* * The highest consecutive TSN that has been acked by peer on my * sends */ uint32_t last_acked_seq; /* The next TSN that I will use in sending. */ uint32_t sending_seq; /* Original seq number I used ??questionable to keep?? */ uint32_t init_seq_number; /* The Advanced Peer Ack Point, as required by the PR-SCTP */ /* (A1 in Section 4.2) */ uint32_t advanced_peer_ack_point; /* * The highest consequetive TSN at the bottom of the mapping array * (for his sends). */ uint32_t cumulative_tsn; /* * Used to track the mapping array and its offset bits. This MAY be * lower then cumulative_tsn. */ uint32_t mapping_array_base_tsn; /* * used to track highest TSN we have received and is listed in the * mapping array. */ uint32_t highest_tsn_inside_map; /* EY - new NR variables used for nr_sack based on mapping_array */ uint8_t *nr_mapping_array; uint32_t nr_mapping_array_base_tsn; uint32_t highest_tsn_inside_nr_map; uint16_t nr_mapping_array_size; uint32_t last_echo_tsn; uint32_t last_cwr_tsn; uint32_t fast_recovery_tsn; uint32_t sat_t3_recovery_tsn; uint32_t tsn_last_delivered; /* * For the pd-api we should re-write this a bit more efficent. We * could have multiple sctp_queued_to_read's that we are building at * once. Now we only do this when we get ready to deliver to the * socket buffer. Note that we depend on the fact that the struct is * "stuck" on the read queue until we finish all the pd-api. */ struct sctp_queued_to_read *control_pdapi; uint32_t tsn_of_pdapi_last_delivered; uint32_t pdapi_ppid; uint32_t context; uint32_t last_reset_action[SCTP_MAX_RESET_PARAMS]; uint32_t last_sending_seq[SCTP_MAX_RESET_PARAMS]; uint32_t last_base_tsnsent[SCTP_MAX_RESET_PARAMS]; #ifdef SCTP_ASOCLOG_OF_TSNS /* * special log - This adds considerable size to the asoc, but * provides a log that you can use to detect problems via kgdb. */ struct sctp_tsn_log in_tsnlog[SCTP_TSN_LOG_SIZE]; struct sctp_tsn_log out_tsnlog[SCTP_TSN_LOG_SIZE]; uint32_t cumack_log[SCTP_TSN_LOG_SIZE]; uint32_t cumack_logsnt[SCTP_TSN_LOG_SIZE]; uint16_t tsn_in_at; uint16_t tsn_out_at; uint16_t tsn_in_wrapped; uint16_t tsn_out_wrapped; uint16_t cumack_log_at; uint16_t cumack_log_atsnt; #endif /* SCTP_ASOCLOG_OF_TSNS */ #ifdef SCTP_FS_SPEC_LOG struct sctp_fs_spec_log fslog[SCTP_FS_SPEC_LOG_SIZE]; uint16_t fs_index; #endif /* * window state information and smallest MTU that I use to bound * segmentation */ uint32_t peers_rwnd; uint32_t my_rwnd; uint32_t my_last_reported_rwnd; uint32_t sctp_frag_point; uint32_t total_output_queue_size; uint32_t sb_cc; /* shadow of sb_cc */ uint32_t sb_send_resv; /* amount reserved on a send */ uint32_t my_rwnd_control_len; /* shadow of sb_mbcnt used for rwnd * control */ /* 32 bit nonce stuff */ uint32_t nonce_resync_tsn; uint32_t nonce_wait_tsn; uint32_t default_flowlabel; uint32_t pr_sctp_cnt; int ctrl_queue_cnt; /* could be removed REM */ /* * All outbound datagrams queue into this list from the individual * stream queue. Here they get assigned a TSN and then await * sending. The stream seq comes when it is first put in the * individual str queue */ unsigned int stream_queue_cnt; unsigned int send_queue_cnt; unsigned int sent_queue_cnt; unsigned int sent_queue_cnt_removeable; /* * Number on sent queue that are marked for retran until this value * is 0 we only send one packet of retran'ed data. */ unsigned int sent_queue_retran_cnt; unsigned int size_on_reasm_queue; unsigned int cnt_on_reasm_queue; /* amount of data (bytes) currently in flight (on all destinations) */ unsigned int total_flight; /* Total book size in flight */ unsigned int total_flight_count; /* count of chunks used with * book total */ /* count of destinaton nets and list of destination nets */ unsigned int numnets; /* Total error count on this association */ unsigned int overall_error_count; unsigned int cnt_msg_on_sb; /* All stream count of chunks for delivery */ unsigned int size_on_all_streams; unsigned int cnt_on_all_streams; /* Heart Beat delay in ticks */ unsigned int heart_beat_delay; /* autoclose */ unsigned int sctp_autoclose_ticks; /* how many preopen streams we have */ unsigned int pre_open_streams; /* How many streams I support coming into me */ unsigned int max_inbound_streams; /* the cookie life I award for any cookie, in seconds */ unsigned int cookie_life; /* time to delay acks for */ unsigned int delayed_ack; unsigned int old_delayed_ack; unsigned int sack_freq; unsigned int data_pkts_seen; unsigned int numduptsns; int dup_tsns[SCTP_MAX_DUP_TSNS]; unsigned int initial_init_rto_max; /* initial RTO for INIT's */ unsigned int initial_rto; /* initial send RTO */ unsigned int minrto; /* per assoc RTO-MIN */ unsigned int maxrto; /* per assoc RTO-MAX */ /* authentication fields */ sctp_auth_chklist_t *local_auth_chunks; sctp_auth_chklist_t *peer_auth_chunks; sctp_hmaclist_t *local_hmacs; /* local HMACs supported */ sctp_hmaclist_t *peer_hmacs; /* peer HMACs supported */ struct sctp_keyhead shared_keys; /* assoc's shared keys */ sctp_authinfo_t authinfo; /* randoms, cached keys */ /* * refcnt to block freeing when a sender or receiver is off coping * user data in. */ uint32_t refcnt; uint32_t chunks_on_out_queue; /* total chunks floating around, * locked by send socket buffer */ uint32_t peers_adaptation; uint16_t peer_hmac_id; /* peer HMAC id to send */ /* * Being that we have no bag to collect stale cookies, and that we * really would not want to anyway.. we will count them in this * counter. We of course feed them to the pigeons right away (I have * always thought of pigeons as flying rats). */ uint16_t stale_cookie_count; /* * For the partial delivery API, if up, invoked this is what last * TSN I delivered */ uint16_t str_of_pdapi; uint16_t ssn_of_pdapi; /* counts of actual built streams. Allocation may be more however */ /* could re-arrange to optimize space here. */ uint16_t streamincnt; uint16_t streamoutcnt; uint16_t strm_realoutsize; /* my maximum number of retrans of INIT and SEND */ /* copied from SCTP but should be individually setable */ uint16_t max_init_times; uint16_t max_send_times; uint16_t def_net_failure; /* * lock flag: 0 is ok to send, 1+ (duals as a retran count) is * awaiting ACK */ uint16_t mapping_array_size; uint16_t last_strm_seq_delivered; uint16_t last_strm_no_delivered; uint16_t last_revoke_count; int16_t num_send_timers_up; uint16_t stream_locked_on; uint16_t ecn_echo_cnt_onq; uint16_t free_chunk_cnt; uint8_t stream_locked; uint8_t authenticated; /* packet authenticated ok */ /* * This flag indicates that a SACK need to be sent. Initially this * is 1 to send the first sACK immediately. */ uint8_t send_sack; /* max burst after fast retransmit completes */ uint8_t max_burst; uint8_t sat_network; /* RTT is in range of sat net or greater */ uint8_t sat_network_lockout; /* lockout code */ uint8_t burst_limit_applied; /* Burst limit in effect at last send? */ /* flag goes on when we are doing a partial delivery api */ uint8_t hb_random_values[4]; uint8_t fragmented_delivery_inprogress; uint8_t fragment_flags; uint8_t last_flags_delivered; uint8_t hb_ect_randombit; uint8_t hb_random_idx; uint8_t hb_is_disabled; /* is the hb disabled? */ uint8_t default_tos; uint8_t asconf_del_pending; /* asconf delete last addr pending */ /* ECN Nonce stuff */ uint8_t receiver_nonce_sum; /* nonce I sum and put in my sack */ uint8_t ecn_nonce_allowed; /* Tells us if ECN nonce is on */ uint8_t nonce_sum_check;/* On off switch used during re-sync */ uint8_t nonce_wait_for_ecne; /* flag when we expect a ECN */ uint8_t peer_supports_ecn_nonce; /* * This value, plus all other ack'd but above cum-ack is added * together to cross check against the bit that we have yet to * define (probably in the SACK). When the cum-ack is updated, this * sum is updated as well. */ uint8_t nonce_sum_expect_base; /* Flag to tell if ECN is allowed */ uint8_t ecn_allowed; /* flag to indicate if peer can do asconf */ uint8_t peer_supports_asconf; /* EY - flag to indicate if peer can do nr_sack */ uint8_t peer_supports_nr_sack; /* pr-sctp support flag */ uint8_t peer_supports_prsctp; /* peer authentication support flag */ uint8_t peer_supports_auth; /* stream resets are supported by the peer */ uint8_t peer_supports_strreset; uint8_t peer_supports_nat; /* * packet drop's are supported by the peer, we don't really care * about this but we bookkeep it anyway. */ uint8_t peer_supports_pktdrop; /* Do we allow V6/V4? */ uint8_t ipv4_addr_legal; uint8_t ipv6_addr_legal; /* Address scoping flags */ /* scope value for IPv4 */ uint8_t ipv4_local_scope; /* scope values for IPv6 */ uint8_t local_scope; uint8_t site_scope; /* loopback scope */ uint8_t loopback_scope; /* flags to handle send alternate net tracking */ uint8_t used_alt_onsack; uint8_t used_alt_asconfack; uint8_t fast_retran_loss_recovery; uint8_t sat_t3_loss_recovery; uint8_t dropped_special_cnt; uint8_t seen_a_sack_this_pkt; uint8_t stream_reset_outstanding; uint8_t stream_reset_out_is_outstanding; uint8_t delayed_connection; uint8_t ifp_had_enobuf; uint8_t saw_sack_with_frags; /* EY */ uint8_t saw_sack_with_nr_frags; uint8_t in_asocid_hash; uint8_t assoc_up_sent; uint8_t adaptation_needed; uint8_t adaptation_sent; /* CMT variables */ uint8_t cmt_dac_pkts_rcvd; uint8_t sctp_cmt_on_off; uint8_t iam_blocking; uint8_t cookie_how[8]; /* EY 05/05/08 - NR_SACK variable */ uint8_t sctp_nr_sack_on_off; /* JRS 5/21/07 - CMT PF variable */ uint8_t sctp_cmt_pf; /* * The mapping array is used to track out of order sequences above * last_acked_seq. 0 indicates packet missing 1 indicates packet * rec'd. We slide it up every time we raise last_acked_seq and 0 * trailing locactions out. If I get a TSN above the array * mappingArraySz, I discard the datagram and let retransmit happen. */ uint32_t marked_retrans; uint32_t timoinit; uint32_t timodata; uint32_t timosack; uint32_t timoshutdown; uint32_t timoheartbeat; uint32_t timocookie; uint32_t timoshutdownack; struct timeval start_time; struct timeval discontinuity_time; }; #endif Index: head/sys/netinet/sctp_sysctl.c =================================================================== --- head/sys/netinet/sctp_sysctl.c (revision 197325) +++ head/sys/netinet/sctp_sysctl.c (revision 197326) @@ -1,938 +1,938 @@ /*- * Copyright (c) 2007, by Cisco Systems, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * a) Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * b) Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the distribution. * * c) Neither the name of Cisco Systems, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include /* * sysctl tunable variables */ void sctp_init_sysctls() { SCTP_BASE_SYSCTL(sctp_sendspace) = SCTPCTL_MAXDGRAM_DEFAULT; SCTP_BASE_SYSCTL(sctp_recvspace) = SCTPCTL_RECVSPACE_DEFAULT; SCTP_BASE_SYSCTL(sctp_auto_asconf) = SCTPCTL_AUTOASCONF_DEFAULT; SCTP_BASE_SYSCTL(sctp_multiple_asconfs) = SCTPCTL_MULTIPLEASCONFS_DEFAULT; SCTP_BASE_SYSCTL(sctp_ecn_enable) = SCTPCTL_ECN_ENABLE_DEFAULT; SCTP_BASE_SYSCTL(sctp_ecn_nonce) = SCTPCTL_ECN_NONCE_DEFAULT; SCTP_BASE_SYSCTL(sctp_strict_sacks) = SCTPCTL_STRICT_SACKS_DEFAULT; SCTP_BASE_SYSCTL(sctp_no_csum_on_loopback) = SCTPCTL_LOOPBACK_NOCSUM_DEFAULT; SCTP_BASE_SYSCTL(sctp_strict_init) = SCTPCTL_STRICT_INIT_DEFAULT; SCTP_BASE_SYSCTL(sctp_peer_chunk_oh) = SCTPCTL_PEER_CHKOH_DEFAULT; SCTP_BASE_SYSCTL(sctp_max_burst_default) = SCTPCTL_MAXBURST_DEFAULT; SCTP_BASE_SYSCTL(sctp_max_chunks_on_queue) = SCTPCTL_MAXCHUNKS_DEFAULT; SCTP_BASE_SYSCTL(sctp_hashtblsize) = SCTPCTL_TCBHASHSIZE_DEFAULT; SCTP_BASE_SYSCTL(sctp_pcbtblsize) = SCTPCTL_PCBHASHSIZE_DEFAULT; SCTP_BASE_SYSCTL(sctp_min_split_point) = SCTPCTL_MIN_SPLIT_POINT_DEFAULT; SCTP_BASE_SYSCTL(sctp_chunkscale) = SCTPCTL_CHUNKSCALE_DEFAULT; SCTP_BASE_SYSCTL(sctp_delayed_sack_time_default) = SCTPCTL_DELAYED_SACK_TIME_DEFAULT; SCTP_BASE_SYSCTL(sctp_sack_freq_default) = SCTPCTL_SACK_FREQ_DEFAULT; SCTP_BASE_SYSCTL(sctp_system_free_resc_limit) = SCTPCTL_SYS_RESOURCE_DEFAULT; SCTP_BASE_SYSCTL(sctp_asoc_free_resc_limit) = SCTPCTL_ASOC_RESOURCE_DEFAULT; SCTP_BASE_SYSCTL(sctp_heartbeat_interval_default) = SCTPCTL_HEARTBEAT_INTERVAL_DEFAULT; SCTP_BASE_SYSCTL(sctp_pmtu_raise_time_default) = SCTPCTL_PMTU_RAISE_TIME_DEFAULT; SCTP_BASE_SYSCTL(sctp_shutdown_guard_time_default) = SCTPCTL_SHUTDOWN_GUARD_TIME_DEFAULT; SCTP_BASE_SYSCTL(sctp_secret_lifetime_default) = SCTPCTL_SECRET_LIFETIME_DEFAULT; SCTP_BASE_SYSCTL(sctp_rto_max_default) = SCTPCTL_RTO_MAX_DEFAULT; SCTP_BASE_SYSCTL(sctp_rto_min_default) = SCTPCTL_RTO_MIN_DEFAULT; SCTP_BASE_SYSCTL(sctp_rto_initial_default) = SCTPCTL_RTO_INITIAL_DEFAULT; SCTP_BASE_SYSCTL(sctp_init_rto_max_default) = SCTPCTL_INIT_RTO_MAX_DEFAULT; SCTP_BASE_SYSCTL(sctp_valid_cookie_life_default) = SCTPCTL_VALID_COOKIE_LIFE_DEFAULT; SCTP_BASE_SYSCTL(sctp_init_rtx_max_default) = SCTPCTL_INIT_RTX_MAX_DEFAULT; SCTP_BASE_SYSCTL(sctp_assoc_rtx_max_default) = SCTPCTL_ASSOC_RTX_MAX_DEFAULT; SCTP_BASE_SYSCTL(sctp_path_rtx_max_default) = SCTPCTL_PATH_RTX_MAX_DEFAULT; SCTP_BASE_SYSCTL(sctp_add_more_threshold) = SCTPCTL_ADD_MORE_ON_OUTPUT_DEFAULT; SCTP_BASE_SYSCTL(sctp_nr_outgoing_streams_default) = SCTPCTL_OUTGOING_STREAMS_DEFAULT; SCTP_BASE_SYSCTL(sctp_cmt_on_off) = SCTPCTL_CMT_ON_OFF_DEFAULT; /* EY */ SCTP_BASE_SYSCTL(sctp_nr_sack_on_off) = SCTPCTL_NR_SACK_ON_OFF_DEFAULT; SCTP_BASE_SYSCTL(sctp_cmt_use_dac) = SCTPCTL_CMT_USE_DAC_DEFAULT; SCTP_BASE_SYSCTL(sctp_cmt_pf) = SCTPCTL_CMT_PF_DEFAULT; SCTP_BASE_SYSCTL(sctp_use_cwnd_based_maxburst) = SCTPCTL_CWND_MAXBURST_DEFAULT; SCTP_BASE_SYSCTL(sctp_early_fr) = SCTPCTL_EARLY_FAST_RETRAN_DEFAULT; SCTP_BASE_SYSCTL(sctp_early_fr_msec) = SCTPCTL_EARLY_FAST_RETRAN_MSEC_DEFAULT; SCTP_BASE_SYSCTL(sctp_asconf_auth_nochk) = SCTPCTL_ASCONF_AUTH_NOCHK_DEFAULT; SCTP_BASE_SYSCTL(sctp_auth_disable) = SCTPCTL_AUTH_DISABLE_DEFAULT; SCTP_BASE_SYSCTL(sctp_nat_friendly) = SCTPCTL_NAT_FRIENDLY_DEFAULT; SCTP_BASE_SYSCTL(sctp_L2_abc_variable) = SCTPCTL_ABC_L_VAR_DEFAULT; SCTP_BASE_SYSCTL(sctp_mbuf_threshold_count) = SCTPCTL_MAX_CHAINED_MBUFS_DEFAULT; SCTP_BASE_SYSCTL(sctp_do_drain) = SCTPCTL_DO_SCTP_DRAIN_DEFAULT; SCTP_BASE_SYSCTL(sctp_hb_maxburst) = SCTPCTL_HB_MAX_BURST_DEFAULT; SCTP_BASE_SYSCTL(sctp_abort_if_one_2_one_hits_limit) = SCTPCTL_ABORT_AT_LIMIT_DEFAULT; SCTP_BASE_SYSCTL(sctp_strict_data_order) = SCTPCTL_STRICT_DATA_ORDER_DEFAULT; SCTP_BASE_SYSCTL(sctp_min_residual) = SCTPCTL_MIN_RESIDUAL_DEFAULT; SCTP_BASE_SYSCTL(sctp_max_retran_chunk) = SCTPCTL_MAX_RETRAN_CHUNK_DEFAULT; SCTP_BASE_SYSCTL(sctp_logging_level) = SCTPCTL_LOGGING_LEVEL_DEFAULT; /* JRS - Variable for default congestion control module */ SCTP_BASE_SYSCTL(sctp_default_cc_module) = SCTPCTL_DEFAULT_CC_MODULE_DEFAULT; SCTP_BASE_SYSCTL(sctp_default_frag_interleave) = SCTPCTL_DEFAULT_FRAG_INTERLEAVE_DEFAULT; SCTP_BASE_SYSCTL(sctp_mobility_base) = SCTPCTL_MOBILITY_BASE_DEFAULT; SCTP_BASE_SYSCTL(sctp_mobility_fasthandoff) = SCTPCTL_MOBILITY_FASTHANDOFF_DEFAULT; SCTP_BASE_SYSCTL(sctp_vtag_time_wait) = SCTPCTL_TIME_WAIT_DEFAULT; #if defined(SCTP_LOCAL_TRACE_BUF) memset(&SCTP_BASE_SYSCTL(sctp_log), 0, sizeof(struct sctp_log)); #endif SCTP_BASE_SYSCTL(sctp_udp_tunneling_for_client_enable) = SCTPCTL_UDP_TUNNELING_FOR_CLIENT_ENABLE_DEFAULT; SCTP_BASE_SYSCTL(sctp_udp_tunneling_port) = SCTPCTL_UDP_TUNNELING_PORT_DEFAULT; SCTP_BASE_SYSCTL(sctp_enable_sack_immediately) = SCTPCTL_SACK_IMMEDIATELY_ENABLE_DEFAULT; SCTP_BASE_SYSCTL(sctp_inits_include_nat_friendly) = SCTPCTL_NAT_FRIENDLY_INITS_DEFAULT; #if defined(SCTP_DEBUG) SCTP_BASE_SYSCTL(sctp_debug_on) = SCTPCTL_DEBUG_DEFAULT; #endif #if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING) SCTP_BASE_SYSCTL(sctp_output_unlocked) = SCTPCTL_OUTPUT_UNLOCKED_DEFAULT; #endif } /* It returns an upper limit. No filtering is done here */ static unsigned int number_of_addresses(struct sctp_inpcb *inp) { int cnt; struct sctp_vrf *vrf; struct sctp_ifn *sctp_ifn; struct sctp_ifa *sctp_ifa; struct sctp_laddr *laddr; cnt = 0; /* neither Mac OS X nor FreeBSD support mulitple routing functions */ if ((vrf = sctp_find_vrf(inp->def_vrf_id)) == NULL) { return (0); } if (inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) { LIST_FOREACH(sctp_ifn, &vrf->ifnlist, next_ifn) { LIST_FOREACH(sctp_ifa, &sctp_ifn->ifalist, next_ifa) { if ((sctp_ifa->address.sa.sa_family == AF_INET) || (sctp_ifa->address.sa.sa_family == AF_INET6)) { cnt++; } } } } else { LIST_FOREACH(laddr, &inp->sctp_addr_list, sctp_nxt_addr) { if ((laddr->ifa->address.sa.sa_family == AF_INET) || (laddr->ifa->address.sa.sa_family == AF_INET6)) { cnt++; } } } return (cnt); } static int copy_out_local_addresses(struct sctp_inpcb *inp, struct sctp_tcb *stcb, struct sysctl_req *req) { struct sctp_ifn *sctp_ifn; struct sctp_ifa *sctp_ifa; int loopback_scope, ipv4_local_scope, local_scope, site_scope; int ipv4_addr_legal, ipv6_addr_legal; struct sctp_vrf *vrf; struct xsctp_laddr xladdr; struct sctp_laddr *laddr; int error; /* Turn on all the appropriate scope */ if (stcb) { /* use association specific values */ loopback_scope = stcb->asoc.loopback_scope; ipv4_local_scope = stcb->asoc.ipv4_local_scope; local_scope = stcb->asoc.local_scope; site_scope = stcb->asoc.site_scope; } else { /* use generic values for endpoints */ loopback_scope = 1; ipv4_local_scope = 1; local_scope = 1; site_scope = 1; } /* use only address families of interest */ if (inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) { ipv6_addr_legal = 1; if (SCTP_IPV6_V6ONLY(inp)) { ipv4_addr_legal = 0; } else { ipv4_addr_legal = 1; } } else { ipv4_addr_legal = 1; ipv6_addr_legal = 0; } error = 0; /* neither Mac OS X nor FreeBSD support mulitple routing functions */ if ((vrf = sctp_find_vrf(inp->def_vrf_id)) == NULL) { SCTP_INP_RUNLOCK(inp); SCTP_INP_INFO_RUNLOCK(); return (-1); } if (inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) { LIST_FOREACH(sctp_ifn, &vrf->ifnlist, next_ifn) { if ((loopback_scope == 0) && SCTP_IFN_IS_IFT_LOOP(sctp_ifn)) /* Skip loopback if loopback_scope not set */ continue; LIST_FOREACH(sctp_ifa, &sctp_ifn->ifalist, next_ifa) { if (stcb) { /* * ignore if blacklisted at * association level */ if (sctp_is_addr_restricted(stcb, sctp_ifa)) continue; } switch (sctp_ifa->address.sa.sa_family) { case AF_INET: if (ipv4_addr_legal) { struct sockaddr_in *sin; sin = (struct sockaddr_in *)&sctp_ifa->address.sa; if (sin->sin_addr.s_addr == 0) continue; if ((ipv4_local_scope == 0) && (IN4_ISPRIVATE_ADDRESS(&sin->sin_addr))) continue; } else { continue; } break; #ifdef INET6 case AF_INET6: if (ipv6_addr_legal) { struct sockaddr_in6 *sin6; sin6 = (struct sockaddr_in6 *)&sctp_ifa->address.sa; if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) continue; if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) { if (local_scope == 0) continue; if (sin6->sin6_scope_id == 0) { /* * bad link * local * address */ if (sa6_recoverscope(sin6) != 0) continue; } } if ((site_scope == 0) && (IN6_IS_ADDR_SITELOCAL(&sin6->sin6_addr))) continue; } else { continue; } break; #endif default: continue; } memset((void *)&xladdr, 0, sizeof(struct xsctp_laddr)); memcpy((void *)&xladdr.address, (const void *)&sctp_ifa->address, sizeof(union sctp_sockstore)); SCTP_INP_RUNLOCK(inp); SCTP_INP_INFO_RUNLOCK(); error = SYSCTL_OUT(req, &xladdr, sizeof(struct xsctp_laddr)); if (error) { return (error); } else { SCTP_INP_INFO_RLOCK(); SCTP_INP_RLOCK(inp); } } } } else { LIST_FOREACH(laddr, &inp->sctp_addr_list, sctp_nxt_addr) { /* ignore if blacklisted at association level */ if (stcb && sctp_is_addr_restricted(stcb, laddr->ifa)) continue; memset((void *)&xladdr, 0, sizeof(struct xsctp_laddr)); memcpy((void *)&xladdr.address, (const void *)&laddr->ifa->address, sizeof(union sctp_sockstore)); xladdr.start_time.tv_sec = (uint32_t) laddr->start_time.tv_sec; xladdr.start_time.tv_usec = (uint32_t) laddr->start_time.tv_usec; SCTP_INP_RUNLOCK(inp); SCTP_INP_INFO_RUNLOCK(); error = SYSCTL_OUT(req, &xladdr, sizeof(struct xsctp_laddr)); if (error) { return (error); } else { SCTP_INP_INFO_RLOCK(); SCTP_INP_RLOCK(inp); } } } memset((void *)&xladdr, 0, sizeof(struct xsctp_laddr)); xladdr.last = 1; SCTP_INP_RUNLOCK(inp); SCTP_INP_INFO_RUNLOCK(); error = SYSCTL_OUT(req, &xladdr, sizeof(struct xsctp_laddr)); if (error) { return (error); } else { SCTP_INP_INFO_RLOCK(); SCTP_INP_RLOCK(inp); return (0); } } /* * sysctl functions */ static int sctp_assoclist(SYSCTL_HANDLER_ARGS) { unsigned int number_of_endpoints; unsigned int number_of_local_addresses; unsigned int number_of_associations; unsigned int number_of_remote_addresses; unsigned int n; int error; struct sctp_inpcb *inp; struct sctp_tcb *stcb; struct sctp_nets *net; struct xsctp_inpcb xinpcb; struct xsctp_tcb xstcb; struct xsctp_raddr xraddr; number_of_endpoints = 0; number_of_local_addresses = 0; number_of_associations = 0; number_of_remote_addresses = 0; SCTP_INP_INFO_RLOCK(); if (req->oldptr == USER_ADDR_NULL) { LIST_FOREACH(inp, &SCTP_BASE_INFO(listhead), sctp_list) { SCTP_INP_RLOCK(inp); number_of_endpoints++; number_of_local_addresses += number_of_addresses(inp); LIST_FOREACH(stcb, &inp->sctp_asoc_list, sctp_tcblist) { number_of_associations++; number_of_local_addresses += number_of_addresses(inp); TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) { number_of_remote_addresses++; } } SCTP_INP_RUNLOCK(inp); } SCTP_INP_INFO_RUNLOCK(); n = (number_of_endpoints + 1) * sizeof(struct xsctp_inpcb) + (number_of_local_addresses + number_of_endpoints + number_of_associations) * sizeof(struct xsctp_laddr) + (number_of_associations + number_of_endpoints) * sizeof(struct xsctp_tcb) + (number_of_remote_addresses + number_of_associations) * sizeof(struct xsctp_raddr); /* request some more memory than needed */ req->oldidx = (n + n / 8); return 0; } if (req->newptr != USER_ADDR_NULL) { SCTP_INP_INFO_RUNLOCK(); SCTP_LTRACE_ERR_RET(NULL, NULL, NULL, SCTP_FROM_SCTP_SYSCTL, EPERM); return EPERM; } LIST_FOREACH(inp, &SCTP_BASE_INFO(listhead), sctp_list) { SCTP_INP_RLOCK(inp); xinpcb.last = 0; xinpcb.local_port = ntohs(inp->sctp_lport); xinpcb.flags = inp->sctp_flags; xinpcb.features = inp->sctp_features; xinpcb.total_sends = inp->total_sends; xinpcb.total_recvs = inp->total_recvs; xinpcb.total_nospaces = inp->total_nospaces; xinpcb.fragmentation_point = inp->sctp_frag_point; if ((inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_ALLGONE) || (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE)) { xinpcb.qlen = 0; xinpcb.maxqlen = 0; } else { xinpcb.qlen = inp->sctp_socket->so_qlen; xinpcb.maxqlen = inp->sctp_socket->so_qlimit; } SCTP_INP_INCR_REF(inp); SCTP_INP_RUNLOCK(inp); SCTP_INP_INFO_RUNLOCK(); error = SYSCTL_OUT(req, &xinpcb, sizeof(struct xsctp_inpcb)); if (error) { SCTP_INP_DECR_REF(inp); return error; } SCTP_INP_INFO_RLOCK(); SCTP_INP_RLOCK(inp); error = copy_out_local_addresses(inp, NULL, req); if (error) { SCTP_INP_DECR_REF(inp); return error; } LIST_FOREACH(stcb, &inp->sctp_asoc_list, sctp_tcblist) { SCTP_TCB_LOCK(stcb); atomic_add_int(&stcb->asoc.refcnt, 1); SCTP_TCB_UNLOCK(stcb); xstcb.last = 0; xstcb.local_port = ntohs(inp->sctp_lport); xstcb.remote_port = ntohs(stcb->rport); if (stcb->asoc.primary_destination != NULL) xstcb.primary_addr = stcb->asoc.primary_destination->ro._l_addr; xstcb.heartbeat_interval = stcb->asoc.heart_beat_delay; xstcb.state = SCTP_GET_STATE(&stcb->asoc); /* FIXME */ /* 7.0 does not support these */ xstcb.assoc_id = sctp_get_associd(stcb); xstcb.peers_rwnd = stcb->asoc.peers_rwnd; xstcb.in_streams = stcb->asoc.streamincnt; xstcb.out_streams = stcb->asoc.streamoutcnt; xstcb.max_nr_retrans = stcb->asoc.overall_error_count; xstcb.primary_process = 0; /* not really supported * yet */ xstcb.T1_expireries = stcb->asoc.timoinit + stcb->asoc.timocookie; xstcb.T2_expireries = stcb->asoc.timoshutdown + stcb->asoc.timoshutdownack; xstcb.retransmitted_tsns = stcb->asoc.marked_retrans; xstcb.start_time.tv_sec = (uint32_t) stcb->asoc.start_time.tv_sec; xstcb.start_time.tv_usec = (uint32_t) stcb->asoc.start_time.tv_usec; xstcb.discontinuity_time.tv_sec = (uint32_t) stcb->asoc.discontinuity_time.tv_sec; xstcb.discontinuity_time.tv_usec = (uint32_t) stcb->asoc.discontinuity_time.tv_usec; xstcb.total_sends = stcb->total_sends; xstcb.total_recvs = stcb->total_recvs; xstcb.local_tag = stcb->asoc.my_vtag; xstcb.remote_tag = stcb->asoc.peer_vtag; xstcb.initial_tsn = stcb->asoc.init_seq_number; xstcb.highest_tsn = stcb->asoc.sending_seq - 1; xstcb.cumulative_tsn = stcb->asoc.last_acked_seq; xstcb.cumulative_tsn_ack = stcb->asoc.cumulative_tsn; xstcb.mtu = stcb->asoc.smallest_mtu; xstcb.refcnt = stcb->asoc.refcnt; SCTP_INP_RUNLOCK(inp); SCTP_INP_INFO_RUNLOCK(); error = SYSCTL_OUT(req, &xstcb, sizeof(struct xsctp_tcb)); if (error) { SCTP_INP_DECR_REF(inp); atomic_subtract_int(&stcb->asoc.refcnt, 1); return error; } SCTP_INP_INFO_RLOCK(); SCTP_INP_RLOCK(inp); error = copy_out_local_addresses(inp, stcb, req); if (error) { SCTP_INP_DECR_REF(inp); atomic_subtract_int(&stcb->asoc.refcnt, 1); return error; } TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) { xraddr.last = 0; xraddr.address = net->ro._l_addr; xraddr.active = ((net->dest_state & SCTP_ADDR_REACHABLE) == SCTP_ADDR_REACHABLE); xraddr.confirmed = ((net->dest_state & SCTP_ADDR_UNCONFIRMED) == 0); xraddr.heartbeat_enabled = ((net->dest_state & SCTP_ADDR_NOHB) == 0); xraddr.rto = net->RTO; xraddr.max_path_rtx = net->failure_threshold; xraddr.rtx = net->marked_retrans; xraddr.error_counter = net->error_count; xraddr.cwnd = net->cwnd; xraddr.flight_size = net->flight_size; xraddr.mtu = net->mtu; xraddr.rtt = net->rtt; xraddr.start_time.tv_sec = (uint32_t) net->start_time.tv_sec; xraddr.start_time.tv_usec = (uint32_t) net->start_time.tv_usec; SCTP_INP_RUNLOCK(inp); SCTP_INP_INFO_RUNLOCK(); error = SYSCTL_OUT(req, &xraddr, sizeof(struct xsctp_raddr)); if (error) { SCTP_INP_DECR_REF(inp); atomic_subtract_int(&stcb->asoc.refcnt, 1); return error; } SCTP_INP_INFO_RLOCK(); SCTP_INP_RLOCK(inp); } atomic_subtract_int(&stcb->asoc.refcnt, 1); memset((void *)&xraddr, 0, sizeof(struct xsctp_raddr)); xraddr.last = 1; SCTP_INP_RUNLOCK(inp); SCTP_INP_INFO_RUNLOCK(); error = SYSCTL_OUT(req, &xraddr, sizeof(struct xsctp_raddr)); if (error) { SCTP_INP_DECR_REF(inp); return error; } SCTP_INP_INFO_RLOCK(); SCTP_INP_RLOCK(inp); } SCTP_INP_DECR_REF(inp); SCTP_INP_RUNLOCK(inp); SCTP_INP_INFO_RUNLOCK(); memset((void *)&xstcb, 0, sizeof(struct xsctp_tcb)); xstcb.last = 1; error = SYSCTL_OUT(req, &xstcb, sizeof(struct xsctp_tcb)); if (error) { return error; } SCTP_INP_INFO_RLOCK(); } SCTP_INP_INFO_RUNLOCK(); memset((void *)&xinpcb, 0, sizeof(struct xsctp_inpcb)); xinpcb.last = 1; error = SYSCTL_OUT(req, &xinpcb, sizeof(struct xsctp_inpcb)); return error; } #define RANGECHK(var, min, max) \ if ((var) < (min)) { (var) = (min); } \ else if ((var) > (max)) { (var) = (max); } static int sysctl_sctp_udp_tunneling_check(SYSCTL_HANDLER_ARGS) { int error; uint32_t old_sctp_udp_tunneling_port; SCTP_INP_INFO_RLOCK(); old_sctp_udp_tunneling_port = SCTP_BASE_SYSCTL(sctp_udp_tunneling_port); SCTP_INP_INFO_RUNLOCK(); error = sysctl_handle_int(oidp, oidp->oid_arg1, oidp->oid_arg2, req); if (error == 0) { RANGECHK(SCTP_BASE_SYSCTL(sctp_udp_tunneling_port), SCTPCTL_UDP_TUNNELING_PORT_MIN, SCTPCTL_UDP_TUNNELING_PORT_MAX); if (old_sctp_udp_tunneling_port == SCTP_BASE_SYSCTL(sctp_udp_tunneling_port)) { error = 0; goto out; } SCTP_INP_INFO_WLOCK(); if (old_sctp_udp_tunneling_port) { sctp_over_udp_stop(); } if (SCTP_BASE_SYSCTL(sctp_udp_tunneling_port)) { if (sctp_over_udp_start()) { SCTP_BASE_SYSCTL(sctp_udp_tunneling_port) = 0; } } SCTP_INP_INFO_WUNLOCK(); } out: return (error); } static int sysctl_sctp_check(SYSCTL_HANDLER_ARGS) { int error; error = sysctl_handle_int(oidp, oidp->oid_arg1, oidp->oid_arg2, req); if (error == 0) { RANGECHK(SCTP_BASE_SYSCTL(sctp_sendspace), SCTPCTL_MAXDGRAM_MIN, SCTPCTL_MAXDGRAM_MAX); RANGECHK(SCTP_BASE_SYSCTL(sctp_recvspace), SCTPCTL_RECVSPACE_MIN, SCTPCTL_RECVSPACE_MAX); #if defined(__FreeBSD__) || defined(SCTP_APPLE_AUTO_ASCONF) RANGECHK(SCTP_BASE_SYSCTL(sctp_auto_asconf), SCTPCTL_AUTOASCONF_MIN, SCTPCTL_AUTOASCONF_MAX); #endif RANGECHK(SCTP_BASE_SYSCTL(sctp_ecn_enable), SCTPCTL_ECN_ENABLE_MIN, SCTPCTL_ECN_ENABLE_MAX); RANGECHK(SCTP_BASE_SYSCTL(sctp_ecn_nonce), SCTPCTL_ECN_NONCE_MIN, SCTPCTL_ECN_NONCE_MAX); RANGECHK(SCTP_BASE_SYSCTL(sctp_strict_sacks), SCTPCTL_STRICT_SACKS_MIN, SCTPCTL_STRICT_SACKS_MAX); RANGECHK(SCTP_BASE_SYSCTL(sctp_no_csum_on_loopback), SCTPCTL_LOOPBACK_NOCSUM_MIN, SCTPCTL_LOOPBACK_NOCSUM_MAX); RANGECHK(SCTP_BASE_SYSCTL(sctp_strict_init), SCTPCTL_STRICT_INIT_MIN, SCTPCTL_STRICT_INIT_MAX); RANGECHK(SCTP_BASE_SYSCTL(sctp_peer_chunk_oh), SCTPCTL_PEER_CHKOH_MIN, SCTPCTL_PEER_CHKOH_MAX); RANGECHK(SCTP_BASE_SYSCTL(sctp_max_burst_default), SCTPCTL_MAXBURST_MIN, SCTPCTL_MAXBURST_MAX); RANGECHK(SCTP_BASE_SYSCTL(sctp_max_chunks_on_queue), SCTPCTL_MAXCHUNKS_MIN, SCTPCTL_MAXCHUNKS_MAX); RANGECHK(SCTP_BASE_SYSCTL(sctp_hashtblsize), SCTPCTL_TCBHASHSIZE_MIN, SCTPCTL_TCBHASHSIZE_MAX); RANGECHK(SCTP_BASE_SYSCTL(sctp_pcbtblsize), SCTPCTL_PCBHASHSIZE_MIN, SCTPCTL_PCBHASHSIZE_MAX); RANGECHK(SCTP_BASE_SYSCTL(sctp_min_split_point), SCTPCTL_MIN_SPLIT_POINT_MIN, SCTPCTL_MIN_SPLIT_POINT_MAX); RANGECHK(SCTP_BASE_SYSCTL(sctp_chunkscale), SCTPCTL_CHUNKSCALE_MIN, SCTPCTL_CHUNKSCALE_MAX); RANGECHK(SCTP_BASE_SYSCTL(sctp_delayed_sack_time_default), SCTPCTL_DELAYED_SACK_TIME_MIN, SCTPCTL_DELAYED_SACK_TIME_MAX); RANGECHK(SCTP_BASE_SYSCTL(sctp_sack_freq_default), SCTPCTL_SACK_FREQ_MIN, SCTPCTL_SACK_FREQ_MAX); RANGECHK(SCTP_BASE_SYSCTL(sctp_system_free_resc_limit), SCTPCTL_SYS_RESOURCE_MIN, SCTPCTL_SYS_RESOURCE_MAX); RANGECHK(SCTP_BASE_SYSCTL(sctp_asoc_free_resc_limit), SCTPCTL_ASOC_RESOURCE_MIN, SCTPCTL_ASOC_RESOURCE_MAX); RANGECHK(SCTP_BASE_SYSCTL(sctp_heartbeat_interval_default), SCTPCTL_HEARTBEAT_INTERVAL_MIN, SCTPCTL_HEARTBEAT_INTERVAL_MAX); RANGECHK(SCTP_BASE_SYSCTL(sctp_pmtu_raise_time_default), SCTPCTL_PMTU_RAISE_TIME_MIN, SCTPCTL_PMTU_RAISE_TIME_MAX); RANGECHK(SCTP_BASE_SYSCTL(sctp_shutdown_guard_time_default), SCTPCTL_SHUTDOWN_GUARD_TIME_MIN, SCTPCTL_SHUTDOWN_GUARD_TIME_MAX); RANGECHK(SCTP_BASE_SYSCTL(sctp_secret_lifetime_default), SCTPCTL_SECRET_LIFETIME_MIN, SCTPCTL_SECRET_LIFETIME_MAX); RANGECHK(SCTP_BASE_SYSCTL(sctp_rto_max_default), SCTPCTL_RTO_MAX_MIN, SCTPCTL_RTO_MAX_MAX); RANGECHK(SCTP_BASE_SYSCTL(sctp_rto_min_default), SCTPCTL_RTO_MIN_MIN, SCTPCTL_RTO_MIN_MAX); RANGECHK(SCTP_BASE_SYSCTL(sctp_rto_initial_default), SCTPCTL_RTO_INITIAL_MIN, SCTPCTL_RTO_INITIAL_MAX); RANGECHK(SCTP_BASE_SYSCTL(sctp_init_rto_max_default), SCTPCTL_INIT_RTO_MAX_MIN, SCTPCTL_INIT_RTO_MAX_MAX); RANGECHK(SCTP_BASE_SYSCTL(sctp_valid_cookie_life_default), SCTPCTL_VALID_COOKIE_LIFE_MIN, SCTPCTL_VALID_COOKIE_LIFE_MAX); RANGECHK(SCTP_BASE_SYSCTL(sctp_init_rtx_max_default), SCTPCTL_INIT_RTX_MAX_MIN, SCTPCTL_INIT_RTX_MAX_MAX); RANGECHK(SCTP_BASE_SYSCTL(sctp_assoc_rtx_max_default), SCTPCTL_ASSOC_RTX_MAX_MIN, SCTPCTL_ASSOC_RTX_MAX_MAX); RANGECHK(SCTP_BASE_SYSCTL(sctp_path_rtx_max_default), SCTPCTL_PATH_RTX_MAX_MIN, SCTPCTL_PATH_RTX_MAX_MAX); RANGECHK(SCTP_BASE_SYSCTL(sctp_add_more_threshold), SCTPCTL_ADD_MORE_ON_OUTPUT_MIN, SCTPCTL_ADD_MORE_ON_OUTPUT_MAX); RANGECHK(SCTP_BASE_SYSCTL(sctp_nr_outgoing_streams_default), SCTPCTL_OUTGOING_STREAMS_MIN, SCTPCTL_OUTGOING_STREAMS_MAX); RANGECHK(SCTP_BASE_SYSCTL(sctp_cmt_on_off), SCTPCTL_CMT_ON_OFF_MIN, SCTPCTL_CMT_ON_OFF_MAX); /* EY */ RANGECHK(SCTP_BASE_SYSCTL(sctp_nr_sack_on_off), SCTPCTL_NR_SACK_ON_OFF_MIN, SCTPCTL_NR_SACK_ON_OFF_MAX); RANGECHK(SCTP_BASE_SYSCTL(sctp_cmt_use_dac), SCTPCTL_CMT_USE_DAC_MIN, SCTPCTL_CMT_USE_DAC_MAX); RANGECHK(SCTP_BASE_SYSCTL(sctp_cmt_pf), SCTPCTL_CMT_PF_MIN, SCTPCTL_CMT_PF_MAX); RANGECHK(SCTP_BASE_SYSCTL(sctp_use_cwnd_based_maxburst), SCTPCTL_CWND_MAXBURST_MIN, SCTPCTL_CWND_MAXBURST_MAX); RANGECHK(SCTP_BASE_SYSCTL(sctp_early_fr), SCTPCTL_EARLY_FAST_RETRAN_MIN, SCTPCTL_EARLY_FAST_RETRAN_MAX); RANGECHK(SCTP_BASE_SYSCTL(sctp_early_fr_msec), SCTPCTL_EARLY_FAST_RETRAN_MSEC_MIN, SCTPCTL_EARLY_FAST_RETRAN_MSEC_MAX); RANGECHK(SCTP_BASE_SYSCTL(sctp_asconf_auth_nochk), SCTPCTL_ASCONF_AUTH_NOCHK_MIN, SCTPCTL_ASCONF_AUTH_NOCHK_MAX); RANGECHK(SCTP_BASE_SYSCTL(sctp_auth_disable), SCTPCTL_AUTH_DISABLE_MIN, SCTPCTL_AUTH_DISABLE_MAX); RANGECHK(SCTP_BASE_SYSCTL(sctp_nat_friendly), SCTPCTL_NAT_FRIENDLY_MIN, SCTPCTL_NAT_FRIENDLY_MAX); RANGECHK(SCTP_BASE_SYSCTL(sctp_L2_abc_variable), SCTPCTL_ABC_L_VAR_MIN, SCTPCTL_ABC_L_VAR_MAX); RANGECHK(SCTP_BASE_SYSCTL(sctp_mbuf_threshold_count), SCTPCTL_MAX_CHAINED_MBUFS_MIN, SCTPCTL_MAX_CHAINED_MBUFS_MAX); RANGECHK(SCTP_BASE_SYSCTL(sctp_do_drain), SCTPCTL_DO_SCTP_DRAIN_MIN, SCTPCTL_DO_SCTP_DRAIN_MAX); RANGECHK(SCTP_BASE_SYSCTL(sctp_hb_maxburst), SCTPCTL_HB_MAX_BURST_MIN, SCTPCTL_HB_MAX_BURST_MAX); RANGECHK(SCTP_BASE_SYSCTL(sctp_abort_if_one_2_one_hits_limit), SCTPCTL_ABORT_AT_LIMIT_MIN, SCTPCTL_ABORT_AT_LIMIT_MAX); RANGECHK(SCTP_BASE_SYSCTL(sctp_strict_data_order), SCTPCTL_STRICT_DATA_ORDER_MIN, SCTPCTL_STRICT_DATA_ORDER_MAX); RANGECHK(SCTP_BASE_SYSCTL(sctp_min_residual), SCTPCTL_MIN_RESIDUAL_MIN, SCTPCTL_MIN_RESIDUAL_MAX); RANGECHK(SCTP_BASE_SYSCTL(sctp_max_retran_chunk), SCTPCTL_MAX_RETRAN_CHUNK_MIN, SCTPCTL_MAX_RETRAN_CHUNK_MAX); RANGECHK(SCTP_BASE_SYSCTL(sctp_logging_level), SCTPCTL_LOGGING_LEVEL_MIN, SCTPCTL_LOGGING_LEVEL_MAX); RANGECHK(SCTP_BASE_SYSCTL(sctp_default_cc_module), SCTPCTL_DEFAULT_CC_MODULE_MIN, SCTPCTL_DEFAULT_CC_MODULE_MAX); RANGECHK(SCTP_BASE_SYSCTL(sctp_default_frag_interleave), SCTPCTL_DEFAULT_FRAG_INTERLEAVE_MIN, SCTPCTL_DEFAULT_FRAG_INTERLEAVE_MAX); RANGECHK(SCTP_BASE_SYSCTL(sctp_vtag_time_wait), SCTPCTL_TIME_WAIT_MIN, SCTPCTL_TIME_WAIT_MAX); #if defined(__FreeBSD__) || defined(SCTP_APPLE_MOBILITY_BASE) RANGECHK(SCTP_BASE_SYSCTL(sctp_mobility_base), SCTPCTL_MOBILITY_BASE_MIN, SCTPCTL_MOBILITY_BASE_MAX); #endif #if defined(__FreeBSD__) || defined(SCTP_APPLE_MOBILITY_FASTHANDOFF) RANGECHK(SCTP_BASE_SYSCTL(sctp_mobility_fasthandoff), SCTPCTL_MOBILITY_FASTHANDOFF_MIN, SCTPCTL_MOBILITY_FASTHANDOFF_MAX); #endif RANGECHK(SCTP_BASE_SYSCTL(sctp_udp_tunneling_for_client_enable), SCTPCTL_UDP_TUNNELING_FOR_CLIENT_ENABLE_MIN, SCTPCTL_UDP_TUNNELING_FOR_CLIENT_ENABLE_MAX); RANGECHK(SCTP_BASE_SYSCTL(sctp_enable_sack_immediately), SCTPCTL_SACK_IMMEDIATELY_ENABLE_MIN, SCTPCTL_SACK_IMMEDIATELY_ENABLE_MAX); RANGECHK(SCTP_BASE_SYSCTL(sctp_inits_include_nat_friendly), SCTPCTL_NAT_FRIENDLY_INITS_MIN, SCTPCTL_NAT_FRIENDLY_INITS_MAX); #ifdef SCTP_DEBUG RANGECHK(SCTP_BASE_SYSCTL(sctp_debug_on), SCTPCTL_DEBUG_MIN, SCTPCTL_DEBUG_MAX); #endif #if defined (__APPLE__) || defined(SCTP_SO_LOCK_TESTING) RANGECHK(SCTP_BASE_SYSCTL(sctp_output_unlocked), SCTPCTL_OUTPUT_UNLOCKED_MIN, SCTPCTL_OUTPUT_UNLOCKED_MAX); #endif } return (error); } #if defined(SCTP_LOCAL_TRACE_BUF) static int sysctl_sctp_cleartrace(SYSCTL_HANDLER_ARGS) { int error = 0; memset(&SCTP_BASE_SYSCTL(sctp_log), 0, sizeof(struct sctp_log)); return (error); } #endif /* * sysctl definitions */ SYSCTL_PROC(_net_inet_sctp, OID_AUTO, sendspace, CTLTYPE_INT | CTLFLAG_RW, &SCTP_BASE_SYSCTL(sctp_sendspace), 0, sysctl_sctp_check, "IU", SCTPCTL_MAXDGRAM_DESC); SYSCTL_PROC(_net_inet_sctp, OID_AUTO, recvspace, CTLTYPE_INT | CTLFLAG_RW, &SCTP_BASE_SYSCTL(sctp_recvspace), 0, sysctl_sctp_check, "IU", SCTPCTL_RECVSPACE_DESC); #if defined(__FreeBSD__) || defined(SCTP_APPLE_AUTO_ASCONF) SYSCTL_PROC(_net_inet_sctp, OID_AUTO, auto_asconf, CTLTYPE_INT | CTLFLAG_RW, &SCTP_BASE_SYSCTL(sctp_auto_asconf), 0, sysctl_sctp_check, "IU", SCTPCTL_AUTOASCONF_DESC); #endif SYSCTL_PROC(_net_inet_sctp, OID_AUTO, ecn_enable, CTLTYPE_INT | CTLFLAG_RW, &SCTP_BASE_SYSCTL(sctp_ecn_enable), 0, sysctl_sctp_check, "IU", SCTPCTL_ECN_ENABLE_DESC); SYSCTL_PROC(_net_inet_sctp, OID_AUTO, ecn_nonce, CTLTYPE_INT | CTLFLAG_RW, &SCTP_BASE_SYSCTL(sctp_ecn_nonce), 0, sysctl_sctp_check, "IU", SCTPCTL_ECN_NONCE_DESC); SYSCTL_PROC(_net_inet_sctp, OID_AUTO, strict_sacks, CTLTYPE_INT | CTLFLAG_RW, &SCTP_BASE_SYSCTL(sctp_strict_sacks), 0, sysctl_sctp_check, "IU", SCTPCTL_STRICT_SACKS_DESC); SYSCTL_PROC(_net_inet_sctp, OID_AUTO, loopback_nocsum, CTLTYPE_INT | CTLFLAG_RW, &SCTP_BASE_SYSCTL(sctp_no_csum_on_loopback), 0, sysctl_sctp_check, "IU", SCTPCTL_LOOPBACK_NOCSUM_DESC); SYSCTL_PROC(_net_inet_sctp, OID_AUTO, strict_init, CTLTYPE_INT | CTLFLAG_RW, &SCTP_BASE_SYSCTL(sctp_strict_init), 0, sysctl_sctp_check, "IU", SCTPCTL_STRICT_INIT_DESC); SYSCTL_PROC(_net_inet_sctp, OID_AUTO, peer_chkoh, CTLTYPE_INT | CTLFLAG_RW, &SCTP_BASE_SYSCTL(sctp_peer_chunk_oh), 0, sysctl_sctp_check, "IU", SCTPCTL_PEER_CHKOH_DESC); SYSCTL_PROC(_net_inet_sctp, OID_AUTO, maxburst, CTLTYPE_INT | CTLFLAG_RW, &SCTP_BASE_SYSCTL(sctp_max_burst_default), 0, sysctl_sctp_check, "IU", SCTPCTL_MAXBURST_DESC); SYSCTL_PROC(_net_inet_sctp, OID_AUTO, maxchunks, CTLTYPE_INT | CTLFLAG_RW, &SCTP_BASE_SYSCTL(sctp_max_chunks_on_queue), 0, sysctl_sctp_check, "IU", SCTPCTL_MAXCHUNKS_DESC); SYSCTL_PROC(_net_inet_sctp, OID_AUTO, tcbhashsize, CTLTYPE_INT | CTLFLAG_RW, &SCTP_BASE_SYSCTL(sctp_hashtblsize), 0, sysctl_sctp_check, "IU", SCTPCTL_TCBHASHSIZE_DESC); SYSCTL_PROC(_net_inet_sctp, OID_AUTO, pcbhashsize, CTLTYPE_INT | CTLFLAG_RW, &SCTP_BASE_SYSCTL(sctp_pcbtblsize), 0, sysctl_sctp_check, "IU", SCTPCTL_PCBHASHSIZE_DESC); SYSCTL_PROC(_net_inet_sctp, OID_AUTO, min_split_point, CTLTYPE_INT | CTLFLAG_RW, &SCTP_BASE_SYSCTL(sctp_min_split_point), 0, sysctl_sctp_check, "IU", SCTPCTL_MIN_SPLIT_POINT_DESC); SYSCTL_PROC(_net_inet_sctp, OID_AUTO, chunkscale, CTLTYPE_INT | CTLFLAG_RW, &SCTP_BASE_SYSCTL(sctp_chunkscale), 0, sysctl_sctp_check, "IU", SCTPCTL_CHUNKSCALE_DESC); SYSCTL_PROC(_net_inet_sctp, OID_AUTO, delayed_sack_time, CTLTYPE_INT | CTLFLAG_RW, &SCTP_BASE_SYSCTL(sctp_delayed_sack_time_default), 0, sysctl_sctp_check, "IU", SCTPCTL_DELAYED_SACK_TIME_DESC); SYSCTL_PROC(_net_inet_sctp, OID_AUTO, sack_freq, CTLTYPE_INT | CTLFLAG_RW, &SCTP_BASE_SYSCTL(sctp_sack_freq_default), 0, sysctl_sctp_check, "IU", SCTPCTL_SACK_FREQ_DESC); SYSCTL_PROC(_net_inet_sctp, OID_AUTO, sys_resource, CTLTYPE_INT | CTLFLAG_RW, &SCTP_BASE_SYSCTL(sctp_system_free_resc_limit), 0, sysctl_sctp_check, "IU", SCTPCTL_SYS_RESOURCE_DESC); SYSCTL_PROC(_net_inet_sctp, OID_AUTO, asoc_resource, CTLTYPE_INT | CTLFLAG_RW, &SCTP_BASE_SYSCTL(sctp_asoc_free_resc_limit), 0, sysctl_sctp_check, "IU", SCTPCTL_ASOC_RESOURCE_DESC); SYSCTL_PROC(_net_inet_sctp, OID_AUTO, heartbeat_interval, CTLTYPE_INT | CTLFLAG_RW, &SCTP_BASE_SYSCTL(sctp_heartbeat_interval_default), 0, sysctl_sctp_check, "IU", SCTPCTL_HEARTBEAT_INTERVAL_DESC); SYSCTL_PROC(_net_inet_sctp, OID_AUTO, pmtu_raise_time, CTLTYPE_INT | CTLFLAG_RW, &SCTP_BASE_SYSCTL(sctp_pmtu_raise_time_default), 0, sysctl_sctp_check, "IU", SCTPCTL_PMTU_RAISE_TIME_DESC); SYSCTL_PROC(_net_inet_sctp, OID_AUTO, shutdown_guard_time, CTLTYPE_INT | CTLFLAG_RW, &SCTP_BASE_SYSCTL(sctp_shutdown_guard_time_default), 0, sysctl_sctp_check, "IU", SCTPCTL_SHUTDOWN_GUARD_TIME_DESC); SYSCTL_PROC(_net_inet_sctp, OID_AUTO, secret_lifetime, CTLTYPE_INT | CTLFLAG_RW, &SCTP_BASE_SYSCTL(sctp_secret_lifetime_default), 0, sysctl_sctp_check, "IU", SCTPCTL_SECRET_LIFETIME_DESC); SYSCTL_PROC(_net_inet_sctp, OID_AUTO, rto_max, CTLTYPE_INT | CTLFLAG_RW, &SCTP_BASE_SYSCTL(sctp_rto_max_default), 0, sysctl_sctp_check, "IU", SCTPCTL_RTO_MAX_DESC); SYSCTL_PROC(_net_inet_sctp, OID_AUTO, rto_min, CTLTYPE_INT | CTLFLAG_RW, &SCTP_BASE_SYSCTL(sctp_rto_min_default), 0, sysctl_sctp_check, "IU", SCTPCTL_RTO_MIN_DESC); SYSCTL_PROC(_net_inet_sctp, OID_AUTO, rto_initial, CTLTYPE_INT | CTLFLAG_RW, &SCTP_BASE_SYSCTL(sctp_rto_initial_default), 0, sysctl_sctp_check, "IU", SCTPCTL_RTO_INITIAL_DESC); SYSCTL_PROC(_net_inet_sctp, OID_AUTO, init_rto_max, CTLTYPE_INT | CTLFLAG_RW, &SCTP_BASE_SYSCTL(sctp_init_rto_max_default), 0, sysctl_sctp_check, "IU", SCTPCTL_INIT_RTO_MAX_DESC); SYSCTL_PROC(_net_inet_sctp, OID_AUTO, valid_cookie_life, CTLTYPE_INT | CTLFLAG_RW, &SCTP_BASE_SYSCTL(sctp_valid_cookie_life_default), 0, sysctl_sctp_check, "IU", SCTPCTL_VALID_COOKIE_LIFE_DESC); SYSCTL_PROC(_net_inet_sctp, OID_AUTO, init_rtx_max, CTLTYPE_INT | CTLFLAG_RW, &SCTP_BASE_SYSCTL(sctp_init_rtx_max_default), 0, sysctl_sctp_check, "IU", SCTPCTL_INIT_RTX_MAX_DESC); SYSCTL_PROC(_net_inet_sctp, OID_AUTO, assoc_rtx_max, CTLTYPE_INT | CTLFLAG_RW, &SCTP_BASE_SYSCTL(sctp_assoc_rtx_max_default), 0, sysctl_sctp_check, "IU", SCTPCTL_ASSOC_RTX_MAX_DESC); SYSCTL_PROC(_net_inet_sctp, OID_AUTO, path_rtx_max, CTLTYPE_INT | CTLFLAG_RW, &SCTP_BASE_SYSCTL(sctp_path_rtx_max_default), 0, sysctl_sctp_check, "IU", SCTPCTL_PATH_RTX_MAX_DESC); SYSCTL_PROC(_net_inet_sctp, OID_AUTO, add_more_on_output, CTLTYPE_INT | CTLFLAG_RW, &SCTP_BASE_SYSCTL(sctp_add_more_threshold), 0, sysctl_sctp_check, "IU", SCTPCTL_ADD_MORE_ON_OUTPUT_DESC); SYSCTL_PROC(_net_inet_sctp, OID_AUTO, outgoing_streams, CTLTYPE_INT | CTLFLAG_RW, &SCTP_BASE_SYSCTL(sctp_nr_outgoing_streams_default), 0, sysctl_sctp_check, "IU", SCTPCTL_OUTGOING_STREAMS_DESC); SYSCTL_PROC(_net_inet_sctp, OID_AUTO, cmt_on_off, CTLTYPE_INT | CTLFLAG_RW, &SCTP_BASE_SYSCTL(sctp_cmt_on_off), 0, sysctl_sctp_check, "IU", SCTPCTL_CMT_ON_OFF_DESC); /* EY */ SYSCTL_PROC(_net_inet_sctp, OID_AUTO, nr_sack_on_off, CTLTYPE_INT | CTLFLAG_RW, &SCTP_BASE_SYSCTL(sctp_nr_sack_on_off), 0, sysctl_sctp_check, "IU", SCTPCTL_NR_SACK_ON_OFF_DESC); SYSCTL_PROC(_net_inet_sctp, OID_AUTO, cmt_use_dac, CTLTYPE_INT | CTLFLAG_RW, &SCTP_BASE_SYSCTL(sctp_cmt_use_dac), 0, sysctl_sctp_check, "IU", SCTPCTL_CMT_USE_DAC_DESC); SYSCTL_PROC(_net_inet_sctp, OID_AUTO, cmt_pf, CTLTYPE_INT | CTLFLAG_RW, &SCTP_BASE_SYSCTL(sctp_cmt_pf), 0, sysctl_sctp_check, "IU", SCTPCTL_CMT_PF_DESC); SYSCTL_PROC(_net_inet_sctp, OID_AUTO, cwnd_maxburst, CTLTYPE_INT | CTLFLAG_RW, &SCTP_BASE_SYSCTL(sctp_use_cwnd_based_maxburst), 0, sysctl_sctp_check, "IU", SCTPCTL_CWND_MAXBURST_DESC); SYSCTL_PROC(_net_inet_sctp, OID_AUTO, early_fast_retran, CTLTYPE_INT | CTLFLAG_RW, &SCTP_BASE_SYSCTL(sctp_early_fr), 0, sysctl_sctp_check, "IU", SCTPCTL_EARLY_FAST_RETRAN_DESC); SYSCTL_PROC(_net_inet_sctp, OID_AUTO, early_fast_retran_msec, CTLTYPE_INT | CTLFLAG_RW, &SCTP_BASE_SYSCTL(sctp_early_fr_msec), 0, sysctl_sctp_check, "IU", SCTPCTL_EARLY_FAST_RETRAN_MSEC_DESC); SYSCTL_PROC(_net_inet_sctp, OID_AUTO, asconf_auth_nochk, CTLTYPE_INT | CTLFLAG_RW, &SCTP_BASE_SYSCTL(sctp_asconf_auth_nochk), 0, sysctl_sctp_check, "IU", SCTPCTL_ASCONF_AUTH_NOCHK_DESC); SYSCTL_PROC(_net_inet_sctp, OID_AUTO, auth_disable, CTLTYPE_INT | CTLFLAG_RW, &SCTP_BASE_SYSCTL(sctp_auth_disable), 0, sysctl_sctp_check, "IU", SCTPCTL_AUTH_DISABLE_DESC); SYSCTL_PROC(_net_inet_sctp, OID_AUTO, nat_friendly, CTLTYPE_INT | CTLFLAG_RW, &SCTP_BASE_SYSCTL(sctp_nat_friendly), 0, sysctl_sctp_check, "IU", SCTPCTL_NAT_FRIENDLY_DESC); SYSCTL_PROC(_net_inet_sctp, OID_AUTO, abc_l_var, CTLTYPE_INT | CTLFLAG_RW, &SCTP_BASE_SYSCTL(sctp_L2_abc_variable), 0, sysctl_sctp_check, "IU", SCTPCTL_ABC_L_VAR_DESC); SYSCTL_PROC(_net_inet_sctp, OID_AUTO, max_chained_mbufs, CTLTYPE_INT | CTLFLAG_RW, &SCTP_BASE_SYSCTL(sctp_mbuf_threshold_count), 0, sysctl_sctp_check, "IU", SCTPCTL_MAX_CHAINED_MBUFS_DESC); SYSCTL_PROC(_net_inet_sctp, OID_AUTO, do_sctp_drain, CTLTYPE_INT | CTLFLAG_RW, &SCTP_BASE_SYSCTL(sctp_do_drain), 0, sysctl_sctp_check, "IU", SCTPCTL_DO_SCTP_DRAIN_DESC); SYSCTL_PROC(_net_inet_sctp, OID_AUTO, hb_max_burst, CTLTYPE_INT | CTLFLAG_RW, &SCTP_BASE_SYSCTL(sctp_hb_maxburst), 0, sysctl_sctp_check, "IU", SCTPCTL_HB_MAX_BURST_DESC); SYSCTL_PROC(_net_inet_sctp, OID_AUTO, abort_at_limit, CTLTYPE_INT | CTLFLAG_RW, &SCTP_BASE_SYSCTL(sctp_abort_if_one_2_one_hits_limit), 0, sysctl_sctp_check, "IU", SCTPCTL_ABORT_AT_LIMIT_DESC); SYSCTL_PROC(_net_inet_sctp, OID_AUTO, strict_data_order, CTLTYPE_INT | CTLFLAG_RW, &SCTP_BASE_SYSCTL(sctp_strict_data_order), 0, sysctl_sctp_check, "IU", SCTPCTL_STRICT_DATA_ORDER_DESC); SYSCTL_PROC(_net_inet_sctp, OID_AUTO, min_residual, CTLTYPE_INT | CTLFLAG_RW, &SCTP_BASE_SYSCTL(sctp_min_residual), 0, sysctl_sctp_check, "IU", SCTPCTL_MIN_RESIDUAL_DESC); SYSCTL_PROC(_net_inet_sctp, OID_AUTO, max_retran_chunk, CTLTYPE_INT | CTLFLAG_RW, &SCTP_BASE_SYSCTL(sctp_max_retran_chunk), 0, sysctl_sctp_check, "IU", SCTPCTL_MAX_RETRAN_CHUNK_DESC); SYSCTL_PROC(_net_inet_sctp, OID_AUTO, log_level, CTLTYPE_INT | CTLFLAG_RW, &SCTP_BASE_SYSCTL(sctp_logging_level), 0, sysctl_sctp_check, "IU", SCTPCTL_LOGGING_LEVEL_DESC); SYSCTL_PROC(_net_inet_sctp, OID_AUTO, default_cc_module, CTLTYPE_INT | CTLFLAG_RW, &SCTP_BASE_SYSCTL(sctp_default_cc_module), 0, sysctl_sctp_check, "IU", SCTPCTL_DEFAULT_CC_MODULE_DESC); SYSCTL_PROC(_net_inet_sctp, OID_AUTO, default_frag_interleave, CTLTYPE_INT | CTLFLAG_RW, &SCTP_BASE_SYSCTL(sctp_default_frag_interleave), 0, sysctl_sctp_check, "IU", SCTPCTL_DEFAULT_FRAG_INTERLEAVE_DESC); #if defined(__FreeBSD__) || defined(SCTP_APPLE_MOBILITY_BASE) SYSCTL_PROC(_net_inet_sctp, OID_AUTO, mobility_base, CTLTYPE_INT | CTLFLAG_RW, &SCTP_BASE_SYSCTL(sctp_mobility_base), 0, sysctl_sctp_check, "IU", SCTPCTL_MOBILITY_BASE_DESC); #endif #if defined(__FreeBSD__) || defined(SCTP_APPLE_MOBILITY_FASTHANDOFF) SYSCTL_PROC(_net_inet_sctp, OID_AUTO, mobility_fasthandoff, CTLTYPE_INT | CTLFLAG_RW, &SCTP_BASE_SYSCTL(sctp_mobility_fasthandoff), 0, sysctl_sctp_check, "IU", SCTPCTL_MOBILITY_FASTHANDOFF_DESC); #endif #if defined(SCTP_LOCAL_TRACE_BUF) SYSCTL_STRUCT(_net_inet_sctp, OID_AUTO, log, CTLFLAG_RD, &SCTP_BASE_SYSCTL(sctp_log), sctp_log, "SCTP logging (struct sctp_log)"); SYSCTL_PROC(_net_inet_sctp, OID_AUTO, clear_trace, CTLTYPE_OPAQUE | CTLFLAG_RW, &SCTP_BASE_SYSCTL(sctp_log), 0, sysctl_sctp_cleartrace, "IU", "Clear SCTP Logging buffer"); #endif SYSCTL_PROC(_net_inet_sctp, OID_AUTO, udp_tunneling_for_client_enable, CTLTYPE_INT | CTLFLAG_RW, &SCTP_BASE_SYSCTL(sctp_udp_tunneling_for_client_enable), 0, sysctl_sctp_check, "IU", SCTPCTL_UDP_TUNNELING_FOR_CLIENT_ENABLE_DESC); SYSCTL_PROC(_net_inet_sctp, OID_AUTO, udp_tunneling_port, CTLTYPE_INT | CTLFLAG_RW, &SCTP_BASE_SYSCTL(sctp_udp_tunneling_port), 0, sysctl_sctp_udp_tunneling_check, "IU", SCTPCTL_UDP_TUNNELING_PORT_DESC); SYSCTL_PROC(_net_inet_sctp, OID_AUTO, enable_sack_immediately, CTLTYPE_INT | CTLFLAG_RW, &SCTP_BASE_SYSCTL(sctp_enable_sack_immediately), 0, sysctl_sctp_check, "IU", SCTPCTL_SACK_IMMEDIATELY_ENABLE_DESC); SYSCTL_PROC(_net_inet_sctp, OID_AUTO, nat_friendly_init, CTLTYPE_INT | CTLFLAG_RW, &SCTP_BASE_SYSCTL(sctp_inits_include_nat_friendly), 0, sysctl_sctp_check, "IU", SCTPCTL_NAT_FRIENDLY_INITS_DESC); SYSCTL_PROC(_net_inet_sctp, OID_AUTO, vtag_time_wait, CTLTYPE_INT | CTLFLAG_RW, &SCTP_BASE_SYSCTL(sctp_vtag_time_wait), 0, sysctl_sctp_check, "IU", SCTPCTL_TIME_WAIT_DESC); #ifdef SCTP_DEBUG SYSCTL_PROC(_net_inet_sctp, OID_AUTO, debug, CTLTYPE_INT | CTLFLAG_RW, &SCTP_BASE_SYSCTL(sctp_debug_on), 0, sysctl_sctp_check, "IU", SCTPCTL_DEBUG_DESC); #endif /* SCTP_DEBUG */ #if defined (__APPLE__) || defined(SCTP_SO_LOCK_TESTING) SYSCTL_PROC(_net_inet_sctp, OID_AUTO, output_unlocked, CTLTYPE_INT | CTLFLAG_RW, &SCTP_BASE_SYSCTL(sctp_output_unlocked), 0, sysctl_sctp_check, "IU", SCTPCTL_OUTPUT_UNLOCKED_DESC); #endif SYSCTL_STRUCT(_net_inet_sctp, OID_AUTO, stats, CTLFLAG_RW, - &SCTP_BASE_STATS, sctpstat, + &SCTP_BASE_STATS_SYSCTL, sctpstat, "SCTP statistics (struct sctp_stat)"); SYSCTL_PROC(_net_inet_sctp, OID_AUTO, assoclist, CTLFLAG_RD, 0, 0, sctp_assoclist, "S,xassoc", "List of active SCTP associations"); Index: head/sys/netinet/sctputil.c =================================================================== --- head/sys/netinet/sctputil.c (revision 197325) +++ head/sys/netinet/sctputil.c (revision 197326) @@ -1,6929 +1,6939 @@ /*- * Copyright (c) 2001-2008, by Cisco Systems, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * a) Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * b) Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the distribution. * * c) Neither the name of Cisco Systems, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ /* $KAME: sctputil.c,v 1.37 2005/03/07 23:26:09 itojun Exp $ */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #ifdef INET6 #endif #include #include #include #include #include /* for sctp_deliver_data() */ #include #include #include #define NUMBER_OF_MTU_SIZES 18 #if defined(__Windows__) && !defined(SCTP_LOCAL_TRACE_BUF) #include "eventrace_netinet.h" #include "sctputil.tmh" /* this is the file that will be auto * generated */ #else #ifndef KTR_SCTP #define KTR_SCTP KTR_SUBSYS #endif #endif void sctp_sblog(struct sockbuf *sb, struct sctp_tcb *stcb, int from, int incr) { struct sctp_cwnd_log sctp_clog; sctp_clog.x.sb.stcb = stcb; sctp_clog.x.sb.so_sbcc = sb->sb_cc; if (stcb) sctp_clog.x.sb.stcb_sbcc = stcb->asoc.sb_cc; else sctp_clog.x.sb.stcb_sbcc = 0; sctp_clog.x.sb.incr = incr; SCTP_CTR6(KTR_SCTP, "SCTP:%d[%d]:%x-%x-%x-%x", SCTP_LOG_EVENT_SB, from, sctp_clog.x.misc.log1, sctp_clog.x.misc.log2, sctp_clog.x.misc.log3, sctp_clog.x.misc.log4); } void sctp_log_closing(struct sctp_inpcb *inp, struct sctp_tcb *stcb, int16_t loc) { struct sctp_cwnd_log sctp_clog; sctp_clog.x.close.inp = (void *)inp; sctp_clog.x.close.sctp_flags = inp->sctp_flags; if (stcb) { sctp_clog.x.close.stcb = (void *)stcb; sctp_clog.x.close.state = (uint16_t) stcb->asoc.state; } else { sctp_clog.x.close.stcb = 0; sctp_clog.x.close.state = 0; } sctp_clog.x.close.loc = loc; SCTP_CTR6(KTR_SCTP, "SCTP:%d[%d]:%x-%x-%x-%x", SCTP_LOG_EVENT_CLOSE, 0, sctp_clog.x.misc.log1, sctp_clog.x.misc.log2, sctp_clog.x.misc.log3, sctp_clog.x.misc.log4); } void rto_logging(struct sctp_nets *net, int from) { struct sctp_cwnd_log sctp_clog; memset(&sctp_clog, 0, sizeof(sctp_clog)); sctp_clog.x.rto.net = (void *)net; sctp_clog.x.rto.rtt = net->prev_rtt; SCTP_CTR6(KTR_SCTP, "SCTP:%d[%d]:%x-%x-%x-%x", SCTP_LOG_EVENT_RTT, from, sctp_clog.x.misc.log1, sctp_clog.x.misc.log2, sctp_clog.x.misc.log3, sctp_clog.x.misc.log4); } void sctp_log_strm_del_alt(struct sctp_tcb *stcb, uint32_t tsn, uint16_t sseq, uint16_t stream, int from) { struct sctp_cwnd_log sctp_clog; sctp_clog.x.strlog.stcb = stcb; sctp_clog.x.strlog.n_tsn = tsn; sctp_clog.x.strlog.n_sseq = sseq; sctp_clog.x.strlog.e_tsn = 0; sctp_clog.x.strlog.e_sseq = 0; sctp_clog.x.strlog.strm = stream; SCTP_CTR6(KTR_SCTP, "SCTP:%d[%d]:%x-%x-%x-%x", SCTP_LOG_EVENT_STRM, from, sctp_clog.x.misc.log1, sctp_clog.x.misc.log2, sctp_clog.x.misc.log3, sctp_clog.x.misc.log4); } void sctp_log_nagle_event(struct sctp_tcb *stcb, int action) { struct sctp_cwnd_log sctp_clog; sctp_clog.x.nagle.stcb = (void *)stcb; sctp_clog.x.nagle.total_flight = stcb->asoc.total_flight; sctp_clog.x.nagle.total_in_queue = stcb->asoc.total_output_queue_size; sctp_clog.x.nagle.count_in_queue = stcb->asoc.chunks_on_out_queue; sctp_clog.x.nagle.count_in_flight = stcb->asoc.total_flight_count; SCTP_CTR6(KTR_SCTP, "SCTP:%d[%d]:%x-%x-%x-%x", SCTP_LOG_EVENT_NAGLE, action, sctp_clog.x.misc.log1, sctp_clog.x.misc.log2, sctp_clog.x.misc.log3, sctp_clog.x.misc.log4); } void sctp_log_sack(uint32_t old_cumack, uint32_t cumack, uint32_t tsn, uint16_t gaps, uint16_t dups, int from) { struct sctp_cwnd_log sctp_clog; sctp_clog.x.sack.cumack = cumack; sctp_clog.x.sack.oldcumack = old_cumack; sctp_clog.x.sack.tsn = tsn; sctp_clog.x.sack.numGaps = gaps; sctp_clog.x.sack.numDups = dups; SCTP_CTR6(KTR_SCTP, "SCTP:%d[%d]:%x-%x-%x-%x", SCTP_LOG_EVENT_SACK, from, sctp_clog.x.misc.log1, sctp_clog.x.misc.log2, sctp_clog.x.misc.log3, sctp_clog.x.misc.log4); } void sctp_log_map(uint32_t map, uint32_t cum, uint32_t high, int from) { struct sctp_cwnd_log sctp_clog; memset(&sctp_clog, 0, sizeof(sctp_clog)); sctp_clog.x.map.base = map; sctp_clog.x.map.cum = cum; sctp_clog.x.map.high = high; SCTP_CTR6(KTR_SCTP, "SCTP:%d[%d]:%x-%x-%x-%x", SCTP_LOG_EVENT_MAP, from, sctp_clog.x.misc.log1, sctp_clog.x.misc.log2, sctp_clog.x.misc.log3, sctp_clog.x.misc.log4); } void sctp_log_fr(uint32_t biggest_tsn, uint32_t biggest_new_tsn, uint32_t tsn, int from) { struct sctp_cwnd_log sctp_clog; memset(&sctp_clog, 0, sizeof(sctp_clog)); sctp_clog.x.fr.largest_tsn = biggest_tsn; sctp_clog.x.fr.largest_new_tsn = biggest_new_tsn; sctp_clog.x.fr.tsn = tsn; SCTP_CTR6(KTR_SCTP, "SCTP:%d[%d]:%x-%x-%x-%x", SCTP_LOG_EVENT_FR, from, sctp_clog.x.misc.log1, sctp_clog.x.misc.log2, sctp_clog.x.misc.log3, sctp_clog.x.misc.log4); } void sctp_log_mb(struct mbuf *m, int from) { struct sctp_cwnd_log sctp_clog; sctp_clog.x.mb.mp = m; sctp_clog.x.mb.mbuf_flags = (uint8_t) (SCTP_BUF_GET_FLAGS(m)); sctp_clog.x.mb.size = (uint16_t) (SCTP_BUF_LEN(m)); sctp_clog.x.mb.data = SCTP_BUF_AT(m, 0); if (SCTP_BUF_IS_EXTENDED(m)) { sctp_clog.x.mb.ext = SCTP_BUF_EXTEND_BASE(m); sctp_clog.x.mb.refcnt = (uint8_t) (SCTP_BUF_EXTEND_REFCNT(m)); } else { sctp_clog.x.mb.ext = 0; sctp_clog.x.mb.refcnt = 0; } SCTP_CTR6(KTR_SCTP, "SCTP:%d[%d]:%x-%x-%x-%x", SCTP_LOG_EVENT_MBUF, from, sctp_clog.x.misc.log1, sctp_clog.x.misc.log2, sctp_clog.x.misc.log3, sctp_clog.x.misc.log4); } void sctp_log_strm_del(struct sctp_queued_to_read *control, struct sctp_queued_to_read *poschk, int from) { struct sctp_cwnd_log sctp_clog; if (control == NULL) { SCTP_PRINTF("Gak log of NULL?\n"); return; } sctp_clog.x.strlog.stcb = control->stcb; sctp_clog.x.strlog.n_tsn = control->sinfo_tsn; sctp_clog.x.strlog.n_sseq = control->sinfo_ssn; sctp_clog.x.strlog.strm = control->sinfo_stream; if (poschk != NULL) { sctp_clog.x.strlog.e_tsn = poschk->sinfo_tsn; sctp_clog.x.strlog.e_sseq = poschk->sinfo_ssn; } else { sctp_clog.x.strlog.e_tsn = 0; sctp_clog.x.strlog.e_sseq = 0; } SCTP_CTR6(KTR_SCTP, "SCTP:%d[%d]:%x-%x-%x-%x", SCTP_LOG_EVENT_STRM, from, sctp_clog.x.misc.log1, sctp_clog.x.misc.log2, sctp_clog.x.misc.log3, sctp_clog.x.misc.log4); } void sctp_log_cwnd(struct sctp_tcb *stcb, struct sctp_nets *net, int augment, uint8_t from) { struct sctp_cwnd_log sctp_clog; sctp_clog.x.cwnd.net = net; if (stcb->asoc.send_queue_cnt > 255) sctp_clog.x.cwnd.cnt_in_send = 255; else sctp_clog.x.cwnd.cnt_in_send = stcb->asoc.send_queue_cnt; if (stcb->asoc.stream_queue_cnt > 255) sctp_clog.x.cwnd.cnt_in_str = 255; else sctp_clog.x.cwnd.cnt_in_str = stcb->asoc.stream_queue_cnt; if (net) { sctp_clog.x.cwnd.cwnd_new_value = net->cwnd; sctp_clog.x.cwnd.inflight = net->flight_size; sctp_clog.x.cwnd.pseudo_cumack = net->pseudo_cumack; sctp_clog.x.cwnd.meets_pseudo_cumack = net->new_pseudo_cumack; sctp_clog.x.cwnd.need_new_pseudo_cumack = net->find_pseudo_cumack; } if (SCTP_CWNDLOG_PRESEND == from) { sctp_clog.x.cwnd.meets_pseudo_cumack = stcb->asoc.peers_rwnd; } sctp_clog.x.cwnd.cwnd_augment = augment; SCTP_CTR6(KTR_SCTP, "SCTP:%d[%d]:%x-%x-%x-%x", SCTP_LOG_EVENT_CWND, from, sctp_clog.x.misc.log1, sctp_clog.x.misc.log2, sctp_clog.x.misc.log3, sctp_clog.x.misc.log4); } void sctp_log_lock(struct sctp_inpcb *inp, struct sctp_tcb *stcb, uint8_t from) { struct sctp_cwnd_log sctp_clog; memset(&sctp_clog, 0, sizeof(sctp_clog)); if (inp) { sctp_clog.x.lock.sock = (void *)inp->sctp_socket; } else { sctp_clog.x.lock.sock = (void *)NULL; } sctp_clog.x.lock.inp = (void *)inp; if (stcb) { sctp_clog.x.lock.tcb_lock = mtx_owned(&stcb->tcb_mtx); } else { sctp_clog.x.lock.tcb_lock = SCTP_LOCK_UNKNOWN; } if (inp) { sctp_clog.x.lock.inp_lock = mtx_owned(&inp->inp_mtx); sctp_clog.x.lock.create_lock = mtx_owned(&inp->inp_create_mtx); } else { sctp_clog.x.lock.inp_lock = SCTP_LOCK_UNKNOWN; sctp_clog.x.lock.create_lock = SCTP_LOCK_UNKNOWN; } sctp_clog.x.lock.info_lock = rw_wowned(&SCTP_BASE_INFO(ipi_ep_mtx)); if (inp->sctp_socket) { sctp_clog.x.lock.sock_lock = mtx_owned(&(inp->sctp_socket->so_rcv.sb_mtx)); sctp_clog.x.lock.sockrcvbuf_lock = mtx_owned(&(inp->sctp_socket->so_rcv.sb_mtx)); sctp_clog.x.lock.socksndbuf_lock = mtx_owned(&(inp->sctp_socket->so_snd.sb_mtx)); } else { sctp_clog.x.lock.sock_lock = SCTP_LOCK_UNKNOWN; sctp_clog.x.lock.sockrcvbuf_lock = SCTP_LOCK_UNKNOWN; sctp_clog.x.lock.socksndbuf_lock = SCTP_LOCK_UNKNOWN; } SCTP_CTR6(KTR_SCTP, "SCTP:%d[%d]:%x-%x-%x-%x", SCTP_LOG_LOCK_EVENT, from, sctp_clog.x.misc.log1, sctp_clog.x.misc.log2, sctp_clog.x.misc.log3, sctp_clog.x.misc.log4); } void sctp_log_maxburst(struct sctp_tcb *stcb, struct sctp_nets *net, int error, int burst, uint8_t from) { struct sctp_cwnd_log sctp_clog; memset(&sctp_clog, 0, sizeof(sctp_clog)); sctp_clog.x.cwnd.net = net; sctp_clog.x.cwnd.cwnd_new_value = error; sctp_clog.x.cwnd.inflight = net->flight_size; sctp_clog.x.cwnd.cwnd_augment = burst; if (stcb->asoc.send_queue_cnt > 255) sctp_clog.x.cwnd.cnt_in_send = 255; else sctp_clog.x.cwnd.cnt_in_send = stcb->asoc.send_queue_cnt; if (stcb->asoc.stream_queue_cnt > 255) sctp_clog.x.cwnd.cnt_in_str = 255; else sctp_clog.x.cwnd.cnt_in_str = stcb->asoc.stream_queue_cnt; SCTP_CTR6(KTR_SCTP, "SCTP:%d[%d]:%x-%x-%x-%x", SCTP_LOG_EVENT_MAXBURST, from, sctp_clog.x.misc.log1, sctp_clog.x.misc.log2, sctp_clog.x.misc.log3, sctp_clog.x.misc.log4); } void sctp_log_rwnd(uint8_t from, uint32_t peers_rwnd, uint32_t snd_size, uint32_t overhead) { struct sctp_cwnd_log sctp_clog; sctp_clog.x.rwnd.rwnd = peers_rwnd; sctp_clog.x.rwnd.send_size = snd_size; sctp_clog.x.rwnd.overhead = overhead; sctp_clog.x.rwnd.new_rwnd = 0; SCTP_CTR6(KTR_SCTP, "SCTP:%d[%d]:%x-%x-%x-%x", SCTP_LOG_EVENT_RWND, from, sctp_clog.x.misc.log1, sctp_clog.x.misc.log2, sctp_clog.x.misc.log3, sctp_clog.x.misc.log4); } void sctp_log_rwnd_set(uint8_t from, uint32_t peers_rwnd, uint32_t flight_size, uint32_t overhead, uint32_t a_rwndval) { struct sctp_cwnd_log sctp_clog; sctp_clog.x.rwnd.rwnd = peers_rwnd; sctp_clog.x.rwnd.send_size = flight_size; sctp_clog.x.rwnd.overhead = overhead; sctp_clog.x.rwnd.new_rwnd = a_rwndval; SCTP_CTR6(KTR_SCTP, "SCTP:%d[%d]:%x-%x-%x-%x", SCTP_LOG_EVENT_RWND, from, sctp_clog.x.misc.log1, sctp_clog.x.misc.log2, sctp_clog.x.misc.log3, sctp_clog.x.misc.log4); } void sctp_log_mbcnt(uint8_t from, uint32_t total_oq, uint32_t book, uint32_t total_mbcnt_q, uint32_t mbcnt) { struct sctp_cwnd_log sctp_clog; sctp_clog.x.mbcnt.total_queue_size = total_oq; sctp_clog.x.mbcnt.size_change = book; sctp_clog.x.mbcnt.total_queue_mb_size = total_mbcnt_q; sctp_clog.x.mbcnt.mbcnt_change = mbcnt; SCTP_CTR6(KTR_SCTP, "SCTP:%d[%d]:%x-%x-%x-%x", SCTP_LOG_EVENT_MBCNT, from, sctp_clog.x.misc.log1, sctp_clog.x.misc.log2, sctp_clog.x.misc.log3, sctp_clog.x.misc.log4); } void sctp_misc_ints(uint8_t from, uint32_t a, uint32_t b, uint32_t c, uint32_t d) { SCTP_CTR6(KTR_SCTP, "SCTP:%d[%d]:%x-%x-%x-%x", SCTP_LOG_MISC_EVENT, from, a, b, c, d); } void sctp_wakeup_log(struct sctp_tcb *stcb, uint32_t cumtsn, uint32_t wake_cnt, int from) { struct sctp_cwnd_log sctp_clog; sctp_clog.x.wake.stcb = (void *)stcb; sctp_clog.x.wake.wake_cnt = wake_cnt; sctp_clog.x.wake.flight = stcb->asoc.total_flight_count; sctp_clog.x.wake.send_q = stcb->asoc.send_queue_cnt; sctp_clog.x.wake.sent_q = stcb->asoc.sent_queue_cnt; if (stcb->asoc.stream_queue_cnt < 0xff) sctp_clog.x.wake.stream_qcnt = (uint8_t) stcb->asoc.stream_queue_cnt; else sctp_clog.x.wake.stream_qcnt = 0xff; if (stcb->asoc.chunks_on_out_queue < 0xff) sctp_clog.x.wake.chunks_on_oque = (uint8_t) stcb->asoc.chunks_on_out_queue; else sctp_clog.x.wake.chunks_on_oque = 0xff; sctp_clog.x.wake.sctpflags = 0; /* set in the defered mode stuff */ if (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_DONT_WAKE) sctp_clog.x.wake.sctpflags |= 1; if (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_WAKEOUTPUT) sctp_clog.x.wake.sctpflags |= 2; if (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_WAKEINPUT) sctp_clog.x.wake.sctpflags |= 4; /* what about the sb */ if (stcb->sctp_socket) { struct socket *so = stcb->sctp_socket; sctp_clog.x.wake.sbflags = (uint8_t) ((so->so_snd.sb_flags & 0x00ff)); } else { sctp_clog.x.wake.sbflags = 0xff; } SCTP_CTR6(KTR_SCTP, "SCTP:%d[%d]:%x-%x-%x-%x", SCTP_LOG_EVENT_WAKE, from, sctp_clog.x.misc.log1, sctp_clog.x.misc.log2, sctp_clog.x.misc.log3, sctp_clog.x.misc.log4); } void sctp_log_block(uint8_t from, struct socket *so, struct sctp_association *asoc, int sendlen) { struct sctp_cwnd_log sctp_clog; sctp_clog.x.blk.onsb = asoc->total_output_queue_size; sctp_clog.x.blk.send_sent_qcnt = (uint16_t) (asoc->send_queue_cnt + asoc->sent_queue_cnt); sctp_clog.x.blk.peer_rwnd = asoc->peers_rwnd; sctp_clog.x.blk.stream_qcnt = (uint16_t) asoc->stream_queue_cnt; sctp_clog.x.blk.chunks_on_oque = (uint16_t) asoc->chunks_on_out_queue; sctp_clog.x.blk.flight_size = (uint16_t) (asoc->total_flight / 1024); sctp_clog.x.blk.sndlen = sendlen; SCTP_CTR6(KTR_SCTP, "SCTP:%d[%d]:%x-%x-%x-%x", SCTP_LOG_EVENT_BLOCK, from, sctp_clog.x.misc.log1, sctp_clog.x.misc.log2, sctp_clog.x.misc.log3, sctp_clog.x.misc.log4); } int sctp_fill_stat_log(void *optval, size_t *optsize) { /* May need to fix this if ktrdump does not work */ return (0); } #ifdef SCTP_AUDITING_ENABLED uint8_t sctp_audit_data[SCTP_AUDIT_SIZE][2]; static int sctp_audit_indx = 0; static void sctp_print_audit_report(void) { int i; int cnt; cnt = 0; for (i = sctp_audit_indx; i < SCTP_AUDIT_SIZE; i++) { if ((sctp_audit_data[i][0] == 0xe0) && (sctp_audit_data[i][1] == 0x01)) { cnt = 0; SCTP_PRINTF("\n"); } else if (sctp_audit_data[i][0] == 0xf0) { cnt = 0; SCTP_PRINTF("\n"); } else if ((sctp_audit_data[i][0] == 0xc0) && (sctp_audit_data[i][1] == 0x01)) { SCTP_PRINTF("\n"); cnt = 0; } SCTP_PRINTF("%2.2x%2.2x ", (uint32_t) sctp_audit_data[i][0], (uint32_t) sctp_audit_data[i][1]); cnt++; if ((cnt % 14) == 0) SCTP_PRINTF("\n"); } for (i = 0; i < sctp_audit_indx; i++) { if ((sctp_audit_data[i][0] == 0xe0) && (sctp_audit_data[i][1] == 0x01)) { cnt = 0; SCTP_PRINTF("\n"); } else if (sctp_audit_data[i][0] == 0xf0) { cnt = 0; SCTP_PRINTF("\n"); } else if ((sctp_audit_data[i][0] == 0xc0) && (sctp_audit_data[i][1] == 0x01)) { SCTP_PRINTF("\n"); cnt = 0; } SCTP_PRINTF("%2.2x%2.2x ", (uint32_t) sctp_audit_data[i][0], (uint32_t) sctp_audit_data[i][1]); cnt++; if ((cnt % 14) == 0) SCTP_PRINTF("\n"); } SCTP_PRINTF("\n"); } void sctp_auditing(int from, struct sctp_inpcb *inp, struct sctp_tcb *stcb, struct sctp_nets *net) { int resend_cnt, tot_out, rep, tot_book_cnt; struct sctp_nets *lnet; struct sctp_tmit_chunk *chk; sctp_audit_data[sctp_audit_indx][0] = 0xAA; sctp_audit_data[sctp_audit_indx][1] = 0x000000ff & from; sctp_audit_indx++; if (sctp_audit_indx >= SCTP_AUDIT_SIZE) { sctp_audit_indx = 0; } if (inp == NULL) { sctp_audit_data[sctp_audit_indx][0] = 0xAF; sctp_audit_data[sctp_audit_indx][1] = 0x01; sctp_audit_indx++; if (sctp_audit_indx >= SCTP_AUDIT_SIZE) { sctp_audit_indx = 0; } return; } if (stcb == NULL) { sctp_audit_data[sctp_audit_indx][0] = 0xAF; sctp_audit_data[sctp_audit_indx][1] = 0x02; sctp_audit_indx++; if (sctp_audit_indx >= SCTP_AUDIT_SIZE) { sctp_audit_indx = 0; } return; } sctp_audit_data[sctp_audit_indx][0] = 0xA1; sctp_audit_data[sctp_audit_indx][1] = (0x000000ff & stcb->asoc.sent_queue_retran_cnt); sctp_audit_indx++; if (sctp_audit_indx >= SCTP_AUDIT_SIZE) { sctp_audit_indx = 0; } rep = 0; tot_book_cnt = 0; resend_cnt = tot_out = 0; TAILQ_FOREACH(chk, &stcb->asoc.sent_queue, sctp_next) { if (chk->sent == SCTP_DATAGRAM_RESEND) { resend_cnt++; } else if (chk->sent < SCTP_DATAGRAM_RESEND) { tot_out += chk->book_size; tot_book_cnt++; } } if (resend_cnt != stcb->asoc.sent_queue_retran_cnt) { sctp_audit_data[sctp_audit_indx][0] = 0xAF; sctp_audit_data[sctp_audit_indx][1] = 0xA1; sctp_audit_indx++; if (sctp_audit_indx >= SCTP_AUDIT_SIZE) { sctp_audit_indx = 0; } SCTP_PRINTF("resend_cnt:%d asoc-tot:%d\n", resend_cnt, stcb->asoc.sent_queue_retran_cnt); rep = 1; stcb->asoc.sent_queue_retran_cnt = resend_cnt; sctp_audit_data[sctp_audit_indx][0] = 0xA2; sctp_audit_data[sctp_audit_indx][1] = (0x000000ff & stcb->asoc.sent_queue_retran_cnt); sctp_audit_indx++; if (sctp_audit_indx >= SCTP_AUDIT_SIZE) { sctp_audit_indx = 0; } } if (tot_out != stcb->asoc.total_flight) { sctp_audit_data[sctp_audit_indx][0] = 0xAF; sctp_audit_data[sctp_audit_indx][1] = 0xA2; sctp_audit_indx++; if (sctp_audit_indx >= SCTP_AUDIT_SIZE) { sctp_audit_indx = 0; } rep = 1; SCTP_PRINTF("tot_flt:%d asoc_tot:%d\n", tot_out, (int)stcb->asoc.total_flight); stcb->asoc.total_flight = tot_out; } if (tot_book_cnt != stcb->asoc.total_flight_count) { sctp_audit_data[sctp_audit_indx][0] = 0xAF; sctp_audit_data[sctp_audit_indx][1] = 0xA5; sctp_audit_indx++; if (sctp_audit_indx >= SCTP_AUDIT_SIZE) { sctp_audit_indx = 0; } rep = 1; SCTP_PRINTF("tot_flt_book:%d\n", tot_book); stcb->asoc.total_flight_count = tot_book_cnt; } tot_out = 0; TAILQ_FOREACH(lnet, &stcb->asoc.nets, sctp_next) { tot_out += lnet->flight_size; } if (tot_out != stcb->asoc.total_flight) { sctp_audit_data[sctp_audit_indx][0] = 0xAF; sctp_audit_data[sctp_audit_indx][1] = 0xA3; sctp_audit_indx++; if (sctp_audit_indx >= SCTP_AUDIT_SIZE) { sctp_audit_indx = 0; } rep = 1; SCTP_PRINTF("real flight:%d net total was %d\n", stcb->asoc.total_flight, tot_out); /* now corrective action */ TAILQ_FOREACH(lnet, &stcb->asoc.nets, sctp_next) { tot_out = 0; TAILQ_FOREACH(chk, &stcb->asoc.sent_queue, sctp_next) { if ((chk->whoTo == lnet) && (chk->sent < SCTP_DATAGRAM_RESEND)) { tot_out += chk->book_size; } } if (lnet->flight_size != tot_out) { SCTP_PRINTF("net:%x flight was %d corrected to %d\n", (uint32_t) lnet, lnet->flight_size, tot_out); lnet->flight_size = tot_out; } } } if (rep) { sctp_print_audit_report(); } } void sctp_audit_log(uint8_t ev, uint8_t fd) { sctp_audit_data[sctp_audit_indx][0] = ev; sctp_audit_data[sctp_audit_indx][1] = fd; sctp_audit_indx++; if (sctp_audit_indx >= SCTP_AUDIT_SIZE) { sctp_audit_indx = 0; } } #endif /* * a list of sizes based on typical mtu's, used only if next hop size not * returned. */ static int sctp_mtu_sizes[] = { 68, 296, 508, 512, 544, 576, 1006, 1492, 1500, 1536, 2002, 2048, 4352, 4464, 8166, 17914, 32000, 65535 }; void sctp_stop_timers_for_shutdown(struct sctp_tcb *stcb) { struct sctp_association *asoc; struct sctp_nets *net; asoc = &stcb->asoc; (void)SCTP_OS_TIMER_STOP(&asoc->hb_timer.timer); (void)SCTP_OS_TIMER_STOP(&asoc->dack_timer.timer); (void)SCTP_OS_TIMER_STOP(&asoc->strreset_timer.timer); (void)SCTP_OS_TIMER_STOP(&asoc->asconf_timer.timer); (void)SCTP_OS_TIMER_STOP(&asoc->autoclose_timer.timer); (void)SCTP_OS_TIMER_STOP(&asoc->delayed_event_timer.timer); TAILQ_FOREACH(net, &asoc->nets, sctp_next) { (void)SCTP_OS_TIMER_STOP(&net->fr_timer.timer); (void)SCTP_OS_TIMER_STOP(&net->pmtu_timer.timer); } } int find_next_best_mtu(int totsz) { int i, perfer; /* * if we are in here we must find the next best fit based on the * size of the dg that failed to be sent. */ perfer = 0; for (i = 0; i < NUMBER_OF_MTU_SIZES; i++) { if (totsz < sctp_mtu_sizes[i]) { perfer = i - 1; if (perfer < 0) perfer = 0; break; } } return (sctp_mtu_sizes[perfer]); } void sctp_fill_random_store(struct sctp_pcb *m) { /* * Here we use the MD5/SHA-1 to hash with our good randomNumbers and * our counter. The result becomes our good random numbers and we * then setup to give these out. Note that we do no locking to * protect this. This is ok, since if competing folks call this we * will get more gobbled gook in the random store which is what we * want. There is a danger that two guys will use the same random * numbers, but thats ok too since that is random as well :-> */ m->store_at = 0; (void)sctp_hmac(SCTP_HMAC, (uint8_t *) m->random_numbers, sizeof(m->random_numbers), (uint8_t *) & m->random_counter, sizeof(m->random_counter), (uint8_t *) m->random_store); m->random_counter++; } uint32_t sctp_select_initial_TSN(struct sctp_pcb *inp) { /* * A true implementation should use random selection process to get * the initial stream sequence number, using RFC1750 as a good * guideline */ uint32_t x, *xp; uint8_t *p; int store_at, new_store; if (inp->initial_sequence_debug != 0) { uint32_t ret; ret = inp->initial_sequence_debug; inp->initial_sequence_debug++; return (ret); } retry: store_at = inp->store_at; new_store = store_at + sizeof(uint32_t); if (new_store >= (SCTP_SIGNATURE_SIZE - 3)) { new_store = 0; } if (!atomic_cmpset_int(&inp->store_at, store_at, new_store)) { goto retry; } if (new_store == 0) { /* Refill the random store */ sctp_fill_random_store(inp); } p = &inp->random_store[store_at]; xp = (uint32_t *) p; x = *xp; return (x); } uint32_t sctp_select_a_tag(struct sctp_inpcb *inp, uint16_t lport, uint16_t rport, int save_in_twait) { u_long x, not_done; struct timeval now; (void)SCTP_GETTIME_TIMEVAL(&now); not_done = 1; while (not_done) { x = sctp_select_initial_TSN(&inp->sctp_ep); if (x == 0) { /* we never use 0 */ continue; } if (sctp_is_vtag_good(inp, x, lport, rport, &now, save_in_twait)) { not_done = 0; } } return (x); } int sctp_init_asoc(struct sctp_inpcb *m, struct sctp_tcb *stcb, int for_a_init, uint32_t override_tag, uint32_t vrf_id) { struct sctp_association *asoc; /* * Anything set to zero is taken care of by the allocation routine's * bzero */ /* * Up front select what scoping to apply on addresses I tell my peer * Not sure what to do with these right now, we will need to come up * with a way to set them. We may need to pass them through from the * caller in the sctp_aloc_assoc() function. */ int i; asoc = &stcb->asoc; /* init all variables to a known value. */ SCTP_SET_STATE(&stcb->asoc, SCTP_STATE_INUSE); asoc->max_burst = m->sctp_ep.max_burst; asoc->heart_beat_delay = TICKS_TO_MSEC(m->sctp_ep.sctp_timeoutticks[SCTP_TIMER_HEARTBEAT]); asoc->cookie_life = m->sctp_ep.def_cookie_life; asoc->sctp_cmt_on_off = (uint8_t) SCTP_BASE_SYSCTL(sctp_cmt_on_off); /* EY Init nr_sack variable */ asoc->sctp_nr_sack_on_off = (uint8_t) SCTP_BASE_SYSCTL(sctp_nr_sack_on_off); /* JRS 5/21/07 - Init CMT PF variables */ asoc->sctp_cmt_pf = (uint8_t) SCTP_BASE_SYSCTL(sctp_cmt_pf); asoc->sctp_frag_point = m->sctp_frag_point; #ifdef INET asoc->default_tos = m->ip_inp.inp.inp_ip_tos; #else asoc->default_tos = 0; #endif #ifdef INET6 asoc->default_flowlabel = ((struct in6pcb *)m)->in6p_flowinfo; #else asoc->default_flowlabel = 0; #endif asoc->sb_send_resv = 0; if (override_tag) { asoc->my_vtag = override_tag; } else { asoc->my_vtag = sctp_select_a_tag(m, stcb->sctp_ep->sctp_lport, stcb->rport, 1); } /* Get the nonce tags */ asoc->my_vtag_nonce = sctp_select_a_tag(m, stcb->sctp_ep->sctp_lport, stcb->rport, 0); asoc->peer_vtag_nonce = sctp_select_a_tag(m, stcb->sctp_ep->sctp_lport, stcb->rport, 0); asoc->vrf_id = vrf_id; if (sctp_is_feature_on(m, SCTP_PCB_FLAGS_DONOT_HEARTBEAT)) asoc->hb_is_disabled = 1; else asoc->hb_is_disabled = 0; #ifdef SCTP_ASOCLOG_OF_TSNS asoc->tsn_in_at = 0; asoc->tsn_out_at = 0; asoc->tsn_in_wrapped = 0; asoc->tsn_out_wrapped = 0; asoc->cumack_log_at = 0; asoc->cumack_log_atsnt = 0; #endif #ifdef SCTP_FS_SPEC_LOG asoc->fs_index = 0; #endif asoc->refcnt = 0; asoc->assoc_up_sent = 0; asoc->asconf_seq_out = asoc->str_reset_seq_out = asoc->init_seq_number = asoc->sending_seq = sctp_select_initial_TSN(&m->sctp_ep); asoc->asconf_seq_out_acked = asoc->asconf_seq_out - 1; /* we are optimisitic here */ asoc->peer_supports_pktdrop = 1; asoc->peer_supports_nat = 0; asoc->sent_queue_retran_cnt = 0; /* for CMT */ asoc->last_net_cmt_send_started = NULL; /* This will need to be adjusted */ asoc->last_cwr_tsn = asoc->init_seq_number - 1; asoc->last_acked_seq = asoc->init_seq_number - 1; asoc->advanced_peer_ack_point = asoc->last_acked_seq; asoc->asconf_seq_in = asoc->last_acked_seq; /* here we are different, we hold the next one we expect */ asoc->str_reset_seq_in = asoc->last_acked_seq + 1; asoc->initial_init_rto_max = m->sctp_ep.initial_init_rto_max; asoc->initial_rto = m->sctp_ep.initial_rto; asoc->max_init_times = m->sctp_ep.max_init_times; asoc->max_send_times = m->sctp_ep.max_send_times; asoc->def_net_failure = m->sctp_ep.def_net_failure; asoc->free_chunk_cnt = 0; asoc->iam_blocking = 0; /* ECN Nonce initialization */ asoc->context = m->sctp_context; asoc->def_send = m->def_send; asoc->ecn_nonce_allowed = 0; asoc->receiver_nonce_sum = 1; asoc->nonce_sum_expect_base = 1; asoc->nonce_sum_check = 1; asoc->nonce_resync_tsn = 0; asoc->nonce_wait_for_ecne = 0; asoc->nonce_wait_tsn = 0; asoc->delayed_ack = TICKS_TO_MSEC(m->sctp_ep.sctp_timeoutticks[SCTP_TIMER_RECV]); asoc->sack_freq = m->sctp_ep.sctp_sack_freq; asoc->pr_sctp_cnt = 0; asoc->total_output_queue_size = 0; if (m->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) { struct in6pcb *inp6; /* Its a V6 socket */ inp6 = (struct in6pcb *)m; asoc->ipv6_addr_legal = 1; /* Now look at the binding flag to see if V4 will be legal */ if (SCTP_IPV6_V6ONLY(inp6) == 0) { asoc->ipv4_addr_legal = 1; } else { /* V4 addresses are NOT legal on the association */ asoc->ipv4_addr_legal = 0; } } else { /* Its a V4 socket, no - V6 */ asoc->ipv4_addr_legal = 1; asoc->ipv6_addr_legal = 0; } asoc->my_rwnd = max(SCTP_SB_LIMIT_RCV(m->sctp_socket), SCTP_MINIMAL_RWND); asoc->peers_rwnd = SCTP_SB_LIMIT_RCV(m->sctp_socket); asoc->smallest_mtu = m->sctp_frag_point; #ifdef SCTP_PRINT_FOR_B_AND_M SCTP_PRINTF("smallest_mtu init'd with asoc to :%d\n", asoc->smallest_mtu); #endif asoc->minrto = m->sctp_ep.sctp_minrto; asoc->maxrto = m->sctp_ep.sctp_maxrto; asoc->locked_on_sending = NULL; asoc->stream_locked_on = 0; asoc->ecn_echo_cnt_onq = 0; asoc->stream_locked = 0; asoc->send_sack = 1; LIST_INIT(&asoc->sctp_restricted_addrs); TAILQ_INIT(&asoc->nets); TAILQ_INIT(&asoc->pending_reply_queue); TAILQ_INIT(&asoc->asconf_ack_sent); /* Setup to fill the hb random cache at first HB */ asoc->hb_random_idx = 4; asoc->sctp_autoclose_ticks = m->sctp_ep.auto_close_time; /* * JRS - Pick the default congestion control module based on the * sysctl. */ switch (m->sctp_ep.sctp_default_cc_module) { /* JRS - Standard TCP congestion control */ case SCTP_CC_RFC2581: { stcb->asoc.congestion_control_module = SCTP_CC_RFC2581; stcb->asoc.cc_functions.sctp_set_initial_cc_param = &sctp_set_initial_cc_param; stcb->asoc.cc_functions.sctp_cwnd_update_after_sack = &sctp_cwnd_update_after_sack; stcb->asoc.cc_functions.sctp_cwnd_update_after_fr = &sctp_cwnd_update_after_fr; stcb->asoc.cc_functions.sctp_cwnd_update_after_timeout = &sctp_cwnd_update_after_timeout; stcb->asoc.cc_functions.sctp_cwnd_update_after_ecn_echo = &sctp_cwnd_update_after_ecn_echo; stcb->asoc.cc_functions.sctp_cwnd_update_after_packet_dropped = &sctp_cwnd_update_after_packet_dropped; stcb->asoc.cc_functions.sctp_cwnd_update_after_output = &sctp_cwnd_update_after_output; stcb->asoc.cc_functions.sctp_cwnd_update_after_fr_timer = &sctp_cwnd_update_after_fr_timer; break; } /* JRS - High Speed TCP congestion control (Floyd) */ case SCTP_CC_HSTCP: { stcb->asoc.congestion_control_module = SCTP_CC_HSTCP; stcb->asoc.cc_functions.sctp_set_initial_cc_param = &sctp_set_initial_cc_param; stcb->asoc.cc_functions.sctp_cwnd_update_after_sack = &sctp_hs_cwnd_update_after_sack; stcb->asoc.cc_functions.sctp_cwnd_update_after_fr = &sctp_hs_cwnd_update_after_fr; stcb->asoc.cc_functions.sctp_cwnd_update_after_timeout = &sctp_cwnd_update_after_timeout; stcb->asoc.cc_functions.sctp_cwnd_update_after_ecn_echo = &sctp_cwnd_update_after_ecn_echo; stcb->asoc.cc_functions.sctp_cwnd_update_after_packet_dropped = &sctp_cwnd_update_after_packet_dropped; stcb->asoc.cc_functions.sctp_cwnd_update_after_output = &sctp_cwnd_update_after_output; stcb->asoc.cc_functions.sctp_cwnd_update_after_fr_timer = &sctp_cwnd_update_after_fr_timer; break; } /* JRS - HTCP congestion control */ case SCTP_CC_HTCP: { stcb->asoc.congestion_control_module = SCTP_CC_HTCP; stcb->asoc.cc_functions.sctp_set_initial_cc_param = &sctp_htcp_set_initial_cc_param; stcb->asoc.cc_functions.sctp_cwnd_update_after_sack = &sctp_htcp_cwnd_update_after_sack; stcb->asoc.cc_functions.sctp_cwnd_update_after_fr = &sctp_htcp_cwnd_update_after_fr; stcb->asoc.cc_functions.sctp_cwnd_update_after_timeout = &sctp_htcp_cwnd_update_after_timeout; stcb->asoc.cc_functions.sctp_cwnd_update_after_ecn_echo = &sctp_htcp_cwnd_update_after_ecn_echo; stcb->asoc.cc_functions.sctp_cwnd_update_after_packet_dropped = &sctp_cwnd_update_after_packet_dropped; stcb->asoc.cc_functions.sctp_cwnd_update_after_output = &sctp_cwnd_update_after_output; stcb->asoc.cc_functions.sctp_cwnd_update_after_fr_timer = &sctp_htcp_cwnd_update_after_fr_timer; break; } /* JRS - By default, use RFC2581 */ default: { stcb->asoc.congestion_control_module = SCTP_CC_RFC2581; stcb->asoc.cc_functions.sctp_set_initial_cc_param = &sctp_set_initial_cc_param; stcb->asoc.cc_functions.sctp_cwnd_update_after_sack = &sctp_cwnd_update_after_sack; stcb->asoc.cc_functions.sctp_cwnd_update_after_fr = &sctp_cwnd_update_after_fr; stcb->asoc.cc_functions.sctp_cwnd_update_after_timeout = &sctp_cwnd_update_after_timeout; stcb->asoc.cc_functions.sctp_cwnd_update_after_ecn_echo = &sctp_cwnd_update_after_ecn_echo; stcb->asoc.cc_functions.sctp_cwnd_update_after_packet_dropped = &sctp_cwnd_update_after_packet_dropped; stcb->asoc.cc_functions.sctp_cwnd_update_after_output = &sctp_cwnd_update_after_output; stcb->asoc.cc_functions.sctp_cwnd_update_after_fr_timer = &sctp_cwnd_update_after_fr_timer; break; } } /* * Now the stream parameters, here we allocate space for all streams * that we request by default. */ asoc->strm_realoutsize = asoc->streamoutcnt = asoc->pre_open_streams = m->sctp_ep.pre_open_stream_count; SCTP_MALLOC(asoc->strmout, struct sctp_stream_out *, asoc->streamoutcnt * sizeof(struct sctp_stream_out), SCTP_M_STRMO); if (asoc->strmout == NULL) { /* big trouble no memory */ SCTP_LTRACE_ERR_RET(NULL, stcb, NULL, SCTP_FROM_SCTPUTIL, ENOMEM); return (ENOMEM); } for (i = 0; i < asoc->streamoutcnt; i++) { /* * inbound side must be set to 0xffff, also NOTE when we get * the INIT-ACK back (for INIT sender) we MUST reduce the * count (streamoutcnt) but first check if we sent to any of * the upper streams that were dropped (if some were). Those * that were dropped must be notified to the upper layer as * failed to send. */ asoc->strmout[i].next_sequence_sent = 0x0; TAILQ_INIT(&asoc->strmout[i].outqueue); asoc->strmout[i].stream_no = i; asoc->strmout[i].last_msg_incomplete = 0; asoc->strmout[i].next_spoke.tqe_next = 0; asoc->strmout[i].next_spoke.tqe_prev = 0; } /* Now the mapping array */ asoc->mapping_array_size = SCTP_INITIAL_MAPPING_ARRAY; SCTP_MALLOC(asoc->mapping_array, uint8_t *, asoc->mapping_array_size, SCTP_M_MAP); if (asoc->mapping_array == NULL) { SCTP_FREE(asoc->strmout, SCTP_M_STRMO); SCTP_LTRACE_ERR_RET(NULL, stcb, NULL, SCTP_FROM_SCTPUTIL, ENOMEM); return (ENOMEM); } memset(asoc->mapping_array, 0, asoc->mapping_array_size); /* EY - initialize the nr_mapping_array just like mapping array */ asoc->nr_mapping_array_size = SCTP_INITIAL_NR_MAPPING_ARRAY; SCTP_MALLOC(asoc->nr_mapping_array, uint8_t *, asoc->nr_mapping_array_size, SCTP_M_MAP); if (asoc->nr_mapping_array == NULL) { SCTP_FREE(asoc->strmout, SCTP_M_STRMO); SCTP_FREE(asoc->mapping_array, SCTP_M_MAP); SCTP_LTRACE_ERR_RET(NULL, stcb, NULL, SCTP_FROM_SCTPUTIL, ENOMEM); return (ENOMEM); } memset(asoc->nr_mapping_array, 0, asoc->nr_mapping_array_size); /* Now the init of the other outqueues */ TAILQ_INIT(&asoc->free_chunks); TAILQ_INIT(&asoc->out_wheel); TAILQ_INIT(&asoc->control_send_queue); TAILQ_INIT(&asoc->asconf_send_queue); TAILQ_INIT(&asoc->send_queue); TAILQ_INIT(&asoc->sent_queue); TAILQ_INIT(&asoc->reasmqueue); TAILQ_INIT(&asoc->resetHead); asoc->max_inbound_streams = m->sctp_ep.max_open_streams_intome; TAILQ_INIT(&asoc->asconf_queue); /* authentication fields */ asoc->authinfo.random = NULL; asoc->authinfo.active_keyid = 0; asoc->authinfo.assoc_key = NULL; asoc->authinfo.assoc_keyid = 0; asoc->authinfo.recv_key = NULL; asoc->authinfo.recv_keyid = 0; LIST_INIT(&asoc->shared_keys); asoc->marked_retrans = 0; asoc->timoinit = 0; asoc->timodata = 0; asoc->timosack = 0; asoc->timoshutdown = 0; asoc->timoheartbeat = 0; asoc->timocookie = 0; asoc->timoshutdownack = 0; (void)SCTP_GETTIME_TIMEVAL(&asoc->start_time); asoc->discontinuity_time = asoc->start_time; /* * sa_ignore MEMLEAK {memory is put in the assoc mapping array and * freed later whe the association is freed. */ return (0); } int sctp_expand_mapping_array(struct sctp_association *asoc, uint32_t needed) { /* mapping array needs to grow */ uint8_t *new_array; uint32_t new_size; new_size = asoc->mapping_array_size + ((needed + 7) / 8 + SCTP_MAPPING_ARRAY_INCR); SCTP_MALLOC(new_array, uint8_t *, new_size, SCTP_M_MAP); if (new_array == NULL) { /* can't get more, forget it */ SCTP_PRINTF("No memory for expansion of SCTP mapping array %d\n", new_size); return (-1); } memset(new_array, 0, new_size); memcpy(new_array, asoc->mapping_array, asoc->mapping_array_size); SCTP_FREE(asoc->mapping_array, SCTP_M_MAP); asoc->mapping_array = new_array; asoc->mapping_array_size = new_size; if (asoc->peer_supports_nr_sack) { new_size = asoc->nr_mapping_array_size + ((needed + 7) / 8 + SCTP_NR_MAPPING_ARRAY_INCR); SCTP_MALLOC(new_array, uint8_t *, new_size, SCTP_M_MAP); if (new_array == NULL) { /* can't get more, forget it */ SCTP_PRINTF("No memory for expansion of SCTP mapping array %d\n", new_size); return (-1); } memset(new_array, 0, new_size); memcpy(new_array, asoc->nr_mapping_array, asoc->nr_mapping_array_size); SCTP_FREE(asoc->nr_mapping_array, SCTP_M_MAP); asoc->nr_mapping_array = new_array; asoc->nr_mapping_array_size = new_size; } return (0); } #if defined(SCTP_USE_THREAD_BASED_ITERATOR) static void sctp_iterator_work(struct sctp_iterator *it) { int iteration_count = 0; int inp_skip = 0; SCTP_ITERATOR_LOCK(); if (it->inp) { SCTP_INP_DECR_REF(it->inp); } if (it->inp == NULL) { /* iterator is complete */ done_with_iterator: SCTP_ITERATOR_UNLOCK(); if (it->function_atend != NULL) { (*it->function_atend) (it->pointer, it->val); } SCTP_FREE(it, SCTP_M_ITER); return; } select_a_new_ep: SCTP_INP_WLOCK(it->inp); while (((it->pcb_flags) && ((it->inp->sctp_flags & it->pcb_flags) != it->pcb_flags)) || ((it->pcb_features) && ((it->inp->sctp_features & it->pcb_features) != it->pcb_features))) { /* endpoint flags or features don't match, so keep looking */ if (it->iterator_flags & SCTP_ITERATOR_DO_SINGLE_INP) { SCTP_INP_WUNLOCK(it->inp); goto done_with_iterator; } SCTP_INP_WUNLOCK(it->inp); it->inp = LIST_NEXT(it->inp, sctp_list); if (it->inp == NULL) { goto done_with_iterator; } SCTP_INP_WLOCK(it->inp); } SCTP_INP_WUNLOCK(it->inp); SCTP_INP_RLOCK(it->inp); /* now go through each assoc which is in the desired state */ if (it->done_current_ep == 0) { if (it->function_inp != NULL) inp_skip = (*it->function_inp) (it->inp, it->pointer, it->val); it->done_current_ep = 1; } if (it->stcb == NULL) { /* run the per instance function */ it->stcb = LIST_FIRST(&it->inp->sctp_asoc_list); } if ((inp_skip) || it->stcb == NULL) { if (it->function_inp_end != NULL) { inp_skip = (*it->function_inp_end) (it->inp, it->pointer, it->val); } SCTP_INP_RUNLOCK(it->inp); goto no_stcb; } while (it->stcb) { SCTP_TCB_LOCK(it->stcb); if (it->asoc_state && ((it->stcb->asoc.state & it->asoc_state) != it->asoc_state)) { /* not in the right state... keep looking */ SCTP_TCB_UNLOCK(it->stcb); goto next_assoc; } /* see if we have limited out the iterator loop */ iteration_count++; if (iteration_count > SCTP_ITERATOR_MAX_AT_ONCE) { /* Pause to let others grab the lock */ atomic_add_int(&it->stcb->asoc.refcnt, 1); SCTP_TCB_UNLOCK(it->stcb); SCTP_INP_INCR_REF(it->inp); SCTP_INP_RUNLOCK(it->inp); SCTP_ITERATOR_UNLOCK(); SCTP_ITERATOR_LOCK(); SCTP_INP_RLOCK(it->inp); SCTP_INP_DECR_REF(it->inp); SCTP_TCB_LOCK(it->stcb); atomic_add_int(&it->stcb->asoc.refcnt, -1); iteration_count = 0; } /* run function on this one */ (*it->function_assoc) (it->inp, it->stcb, it->pointer, it->val); /* * we lie here, it really needs to have its own type but * first I must verify that this won't effect things :-0 */ if (it->no_chunk_output == 0) sctp_chunk_output(it->inp, it->stcb, SCTP_OUTPUT_FROM_T3, SCTP_SO_NOT_LOCKED); SCTP_TCB_UNLOCK(it->stcb); next_assoc: it->stcb = LIST_NEXT(it->stcb, sctp_tcblist); if (it->stcb == NULL) { /* Run last function */ if (it->function_inp_end != NULL) { inp_skip = (*it->function_inp_end) (it->inp, it->pointer, it->val); } } } SCTP_INP_RUNLOCK(it->inp); no_stcb: /* done with all assocs on this endpoint, move on to next endpoint */ it->done_current_ep = 0; SCTP_INP_WLOCK(it->inp); SCTP_INP_WUNLOCK(it->inp); if (it->iterator_flags & SCTP_ITERATOR_DO_SINGLE_INP) { it->inp = NULL; } else { SCTP_INP_INFO_RLOCK(); it->inp = LIST_NEXT(it->inp, sctp_list); SCTP_INP_INFO_RUNLOCK(); } if (it->inp == NULL) { goto done_with_iterator; } goto select_a_new_ep; } void sctp_iterator_worker(void) { struct sctp_iterator *it = NULL; /* This function is called with the WQ lock in place */ SCTP_BASE_INFO(iterator_running) = 1; again: it = TAILQ_FIRST(&SCTP_BASE_INFO(iteratorhead)); while (it) { /* now lets work on this one */ TAILQ_REMOVE(&SCTP_BASE_INFO(iteratorhead), it, sctp_nxt_itr); SCTP_IPI_ITERATOR_WQ_UNLOCK(); sctp_iterator_work(it); SCTP_IPI_ITERATOR_WQ_LOCK(); /* sa_ignore FREED_MEMORY */ it = TAILQ_FIRST(&SCTP_BASE_INFO(iteratorhead)); } if (TAILQ_FIRST(&SCTP_BASE_INFO(iteratorhead))) { goto again; } SCTP_BASE_INFO(iterator_running) = 0; return; } #endif static void sctp_handle_addr_wq(void) { /* deal with the ADDR wq from the rtsock calls */ struct sctp_laddr *wi; struct sctp_asconf_iterator *asc; SCTP_MALLOC(asc, struct sctp_asconf_iterator *, sizeof(struct sctp_asconf_iterator), SCTP_M_ASC_IT); if (asc == NULL) { /* Try later, no memory */ sctp_timer_start(SCTP_TIMER_TYPE_ADDR_WQ, (struct sctp_inpcb *)NULL, (struct sctp_tcb *)NULL, (struct sctp_nets *)NULL); return; } LIST_INIT(&asc->list_of_work); asc->cnt = 0; SCTP_IPI_ITERATOR_WQ_LOCK(); wi = LIST_FIRST(&SCTP_BASE_INFO(addr_wq)); while (wi != NULL) { LIST_REMOVE(wi, sctp_nxt_addr); LIST_INSERT_HEAD(&asc->list_of_work, wi, sctp_nxt_addr); asc->cnt++; wi = LIST_FIRST(&SCTP_BASE_INFO(addr_wq)); } SCTP_IPI_ITERATOR_WQ_UNLOCK(); if (asc->cnt == 0) { SCTP_FREE(asc, SCTP_M_ASC_IT); } else { (void)sctp_initiate_iterator(sctp_asconf_iterator_ep, sctp_asconf_iterator_stcb, NULL, /* No ep end for boundall */ SCTP_PCB_FLAGS_BOUNDALL, SCTP_PCB_ANY_FEATURES, SCTP_ASOC_ANY_STATE, (void *)asc, 0, sctp_asconf_iterator_end, NULL, 0); } } int retcode = 0; int cur_oerr = 0; void sctp_timeout_handler(void *t) { struct sctp_inpcb *inp; struct sctp_tcb *stcb; struct sctp_nets *net; struct sctp_timer *tmr; #if defined (__APPLE__) || defined(SCTP_SO_LOCK_TESTING) struct socket *so; #endif int did_output, type; struct sctp_iterator *it = NULL; tmr = (struct sctp_timer *)t; inp = (struct sctp_inpcb *)tmr->ep; stcb = (struct sctp_tcb *)tmr->tcb; net = (struct sctp_nets *)tmr->net; + CURVNET_SET((struct vnet *)tmr->vnet); did_output = 1; #ifdef SCTP_AUDITING_ENABLED sctp_audit_log(0xF0, (uint8_t) tmr->type); sctp_auditing(3, inp, stcb, net); #endif /* sanity checks... */ if (tmr->self != (void *)tmr) { /* * SCTP_PRINTF("Stale SCTP timer fired (%p), ignoring...\n", * tmr); */ + CURVNET_RESTORE(); return; } tmr->stopped_from = 0xa001; if (!SCTP_IS_TIMER_TYPE_VALID(tmr->type)) { /* * SCTP_PRINTF("SCTP timer fired with invalid type: 0x%x\n", * tmr->type); */ + CURVNET_RESTORE(); return; } tmr->stopped_from = 0xa002; if ((tmr->type != SCTP_TIMER_TYPE_ADDR_WQ) && (inp == NULL)) { + CURVNET_RESTORE(); return; } /* if this is an iterator timeout, get the struct and clear inp */ tmr->stopped_from = 0xa003; if (tmr->type == SCTP_TIMER_TYPE_ITERATOR) { it = (struct sctp_iterator *)inp; inp = NULL; } type = tmr->type; if (inp) { SCTP_INP_INCR_REF(inp); if ((inp->sctp_socket == 0) && ((tmr->type != SCTP_TIMER_TYPE_INPKILL) && (tmr->type != SCTP_TIMER_TYPE_INIT) && (tmr->type != SCTP_TIMER_TYPE_SEND) && (tmr->type != SCTP_TIMER_TYPE_RECV) && (tmr->type != SCTP_TIMER_TYPE_HEARTBEAT) && (tmr->type != SCTP_TIMER_TYPE_SHUTDOWN) && (tmr->type != SCTP_TIMER_TYPE_SHUTDOWNACK) && (tmr->type != SCTP_TIMER_TYPE_SHUTDOWNGUARD) && (tmr->type != SCTP_TIMER_TYPE_ASOCKILL)) ) { SCTP_INP_DECR_REF(inp); + CURVNET_RESTORE(); return; } } tmr->stopped_from = 0xa004; if (stcb) { atomic_add_int(&stcb->asoc.refcnt, 1); if (stcb->asoc.state == 0) { atomic_add_int(&stcb->asoc.refcnt, -1); if (inp) { SCTP_INP_DECR_REF(inp); } + CURVNET_RESTORE(); return; } } tmr->stopped_from = 0xa005; SCTPDBG(SCTP_DEBUG_TIMER1, "Timer type %d goes off\n", tmr->type); if (!SCTP_OS_TIMER_ACTIVE(&tmr->timer)) { if (inp) { SCTP_INP_DECR_REF(inp); } if (stcb) { atomic_add_int(&stcb->asoc.refcnt, -1); } + CURVNET_RESTORE(); return; } tmr->stopped_from = 0xa006; if (stcb) { SCTP_TCB_LOCK(stcb); atomic_add_int(&stcb->asoc.refcnt, -1); if ((tmr->type != SCTP_TIMER_TYPE_ASOCKILL) && ((stcb->asoc.state == 0) || (stcb->asoc.state & SCTP_STATE_ABOUT_TO_BE_FREED))) { SCTP_TCB_UNLOCK(stcb); if (inp) { SCTP_INP_DECR_REF(inp); } + CURVNET_RESTORE(); return; } } /* record in stopped what t-o occured */ tmr->stopped_from = tmr->type; /* mark as being serviced now */ if (SCTP_OS_TIMER_PENDING(&tmr->timer)) { /* * Callout has been rescheduled. */ goto get_out; } if (!SCTP_OS_TIMER_ACTIVE(&tmr->timer)) { /* * Not active, so no action. */ goto get_out; } SCTP_OS_TIMER_DEACTIVATE(&tmr->timer); /* call the handler for the appropriate timer type */ switch (tmr->type) { case SCTP_TIMER_TYPE_ZERO_COPY: if (inp == NULL) { break; } if (sctp_is_feature_on(inp, SCTP_PCB_FLAGS_ZERO_COPY_ACTIVE)) { SCTP_ZERO_COPY_EVENT(inp, inp->sctp_socket); } break; case SCTP_TIMER_TYPE_ZCOPY_SENDQ: if (inp == NULL) { break; } if (sctp_is_feature_on(inp, SCTP_PCB_FLAGS_ZERO_COPY_ACTIVE)) { SCTP_ZERO_COPY_SENDQ_EVENT(inp, inp->sctp_socket); } break; case SCTP_TIMER_TYPE_ADDR_WQ: sctp_handle_addr_wq(); break; case SCTP_TIMER_TYPE_ITERATOR: SCTP_STAT_INCR(sctps_timoiterator); sctp_iterator_timer(it); break; case SCTP_TIMER_TYPE_SEND: if ((stcb == NULL) || (inp == NULL)) { break; } SCTP_STAT_INCR(sctps_timodata); stcb->asoc.timodata++; stcb->asoc.num_send_timers_up--; if (stcb->asoc.num_send_timers_up < 0) { stcb->asoc.num_send_timers_up = 0; } SCTP_TCB_LOCK_ASSERT(stcb); cur_oerr = stcb->asoc.overall_error_count; retcode = sctp_t3rxt_timer(inp, stcb, net); if (retcode) { /* no need to unlock on tcb its gone */ goto out_decr; } SCTP_TCB_LOCK_ASSERT(stcb); #ifdef SCTP_AUDITING_ENABLED sctp_auditing(4, inp, stcb, net); #endif sctp_chunk_output(inp, stcb, SCTP_OUTPUT_FROM_T3, SCTP_SO_NOT_LOCKED); if ((stcb->asoc.num_send_timers_up == 0) && (stcb->asoc.sent_queue_cnt > 0) ) { struct sctp_tmit_chunk *chk; /* * safeguard. If there on some on the sent queue * somewhere but no timers running something is * wrong... so we start a timer on the first chunk * on the send queue on whatever net it is sent to. */ chk = TAILQ_FIRST(&stcb->asoc.sent_queue); sctp_timer_start(SCTP_TIMER_TYPE_SEND, inp, stcb, chk->whoTo); } break; case SCTP_TIMER_TYPE_INIT: if ((stcb == NULL) || (inp == NULL)) { break; } SCTP_STAT_INCR(sctps_timoinit); stcb->asoc.timoinit++; if (sctp_t1init_timer(inp, stcb, net)) { /* no need to unlock on tcb its gone */ goto out_decr; } /* We do output but not here */ did_output = 0; break; case SCTP_TIMER_TYPE_RECV: if ((stcb == NULL) || (inp == NULL)) { break; } { int abort_flag; SCTP_STAT_INCR(sctps_timosack); stcb->asoc.timosack++; if (stcb->asoc.cumulative_tsn != stcb->asoc.highest_tsn_inside_map) sctp_sack_check(stcb, 0, 0, &abort_flag); /* * EY if nr_sacks used then send an nr-sack , a sack * otherwise */ if (SCTP_BASE_SYSCTL(sctp_nr_sack_on_off) && stcb->asoc.peer_supports_nr_sack) sctp_send_nr_sack(stcb); else sctp_send_sack(stcb); } #ifdef SCTP_AUDITING_ENABLED sctp_auditing(4, inp, stcb, net); #endif sctp_chunk_output(inp, stcb, SCTP_OUTPUT_FROM_SACK_TMR, SCTP_SO_NOT_LOCKED); break; case SCTP_TIMER_TYPE_SHUTDOWN: if ((stcb == NULL) || (inp == NULL)) { break; } if (sctp_shutdown_timer(inp, stcb, net)) { /* no need to unlock on tcb its gone */ goto out_decr; } SCTP_STAT_INCR(sctps_timoshutdown); stcb->asoc.timoshutdown++; #ifdef SCTP_AUDITING_ENABLED sctp_auditing(4, inp, stcb, net); #endif sctp_chunk_output(inp, stcb, SCTP_OUTPUT_FROM_SHUT_TMR, SCTP_SO_NOT_LOCKED); break; case SCTP_TIMER_TYPE_HEARTBEAT: { struct sctp_nets *lnet; int cnt_of_unconf = 0; if ((stcb == NULL) || (inp == NULL)) { break; } SCTP_STAT_INCR(sctps_timoheartbeat); stcb->asoc.timoheartbeat++; TAILQ_FOREACH(lnet, &stcb->asoc.nets, sctp_next) { if ((lnet->dest_state & SCTP_ADDR_UNCONFIRMED) && (lnet->dest_state & SCTP_ADDR_REACHABLE)) { cnt_of_unconf++; } } if (cnt_of_unconf == 0) { if (sctp_heartbeat_timer(inp, stcb, lnet, cnt_of_unconf)) { /* no need to unlock on tcb its gone */ goto out_decr; } } #ifdef SCTP_AUDITING_ENABLED sctp_auditing(4, inp, stcb, lnet); #endif sctp_timer_start(SCTP_TIMER_TYPE_HEARTBEAT, stcb->sctp_ep, stcb, lnet); sctp_chunk_output(inp, stcb, SCTP_OUTPUT_FROM_HB_TMR, SCTP_SO_NOT_LOCKED); } break; case SCTP_TIMER_TYPE_COOKIE: if ((stcb == NULL) || (inp == NULL)) { break; } if (sctp_cookie_timer(inp, stcb, net)) { /* no need to unlock on tcb its gone */ goto out_decr; } SCTP_STAT_INCR(sctps_timocookie); stcb->asoc.timocookie++; #ifdef SCTP_AUDITING_ENABLED sctp_auditing(4, inp, stcb, net); #endif /* * We consider T3 and Cookie timer pretty much the same with * respect to where from in chunk_output. */ sctp_chunk_output(inp, stcb, SCTP_OUTPUT_FROM_T3, SCTP_SO_NOT_LOCKED); break; case SCTP_TIMER_TYPE_NEWCOOKIE: { struct timeval tv; int i, secret; if (inp == NULL) { break; } SCTP_STAT_INCR(sctps_timosecret); (void)SCTP_GETTIME_TIMEVAL(&tv); SCTP_INP_WLOCK(inp); inp->sctp_ep.time_of_secret_change = tv.tv_sec; inp->sctp_ep.last_secret_number = inp->sctp_ep.current_secret_number; inp->sctp_ep.current_secret_number++; if (inp->sctp_ep.current_secret_number >= SCTP_HOW_MANY_SECRETS) { inp->sctp_ep.current_secret_number = 0; } secret = (int)inp->sctp_ep.current_secret_number; for (i = 0; i < SCTP_NUMBER_OF_SECRETS; i++) { inp->sctp_ep.secret_key[secret][i] = sctp_select_initial_TSN(&inp->sctp_ep); } SCTP_INP_WUNLOCK(inp); sctp_timer_start(SCTP_TIMER_TYPE_NEWCOOKIE, inp, stcb, net); } did_output = 0; break; case SCTP_TIMER_TYPE_PATHMTURAISE: if ((stcb == NULL) || (inp == NULL)) { break; } SCTP_STAT_INCR(sctps_timopathmtu); sctp_pathmtu_timer(inp, stcb, net); did_output = 0; break; case SCTP_TIMER_TYPE_SHUTDOWNACK: if ((stcb == NULL) || (inp == NULL)) { break; } if (sctp_shutdownack_timer(inp, stcb, net)) { /* no need to unlock on tcb its gone */ goto out_decr; } SCTP_STAT_INCR(sctps_timoshutdownack); stcb->asoc.timoshutdownack++; #ifdef SCTP_AUDITING_ENABLED sctp_auditing(4, inp, stcb, net); #endif sctp_chunk_output(inp, stcb, SCTP_OUTPUT_FROM_SHUT_ACK_TMR, SCTP_SO_NOT_LOCKED); break; case SCTP_TIMER_TYPE_SHUTDOWNGUARD: if ((stcb == NULL) || (inp == NULL)) { break; } SCTP_STAT_INCR(sctps_timoshutdownguard); sctp_abort_an_association(inp, stcb, SCTP_SHUTDOWN_GUARD_EXPIRES, NULL, SCTP_SO_NOT_LOCKED); /* no need to unlock on tcb its gone */ goto out_decr; case SCTP_TIMER_TYPE_STRRESET: if ((stcb == NULL) || (inp == NULL)) { break; } if (sctp_strreset_timer(inp, stcb, net)) { /* no need to unlock on tcb its gone */ goto out_decr; } SCTP_STAT_INCR(sctps_timostrmrst); sctp_chunk_output(inp, stcb, SCTP_OUTPUT_FROM_STRRST_TMR, SCTP_SO_NOT_LOCKED); break; case SCTP_TIMER_TYPE_EARLYFR: /* Need to do FR of things for net */ if ((stcb == NULL) || (inp == NULL)) { break; } SCTP_STAT_INCR(sctps_timoearlyfr); sctp_early_fr_timer(inp, stcb, net); break; case SCTP_TIMER_TYPE_ASCONF: if ((stcb == NULL) || (inp == NULL)) { break; } if (sctp_asconf_timer(inp, stcb, net)) { /* no need to unlock on tcb its gone */ goto out_decr; } SCTP_STAT_INCR(sctps_timoasconf); #ifdef SCTP_AUDITING_ENABLED sctp_auditing(4, inp, stcb, net); #endif sctp_chunk_output(inp, stcb, SCTP_OUTPUT_FROM_ASCONF_TMR, SCTP_SO_NOT_LOCKED); break; case SCTP_TIMER_TYPE_PRIM_DELETED: if ((stcb == NULL) || (inp == NULL)) { break; } sctp_delete_prim_timer(inp, stcb, net); SCTP_STAT_INCR(sctps_timodelprim); break; case SCTP_TIMER_TYPE_AUTOCLOSE: if ((stcb == NULL) || (inp == NULL)) { break; } SCTP_STAT_INCR(sctps_timoautoclose); sctp_autoclose_timer(inp, stcb, net); sctp_chunk_output(inp, stcb, SCTP_OUTPUT_FROM_AUTOCLOSE_TMR, SCTP_SO_NOT_LOCKED); did_output = 0; break; case SCTP_TIMER_TYPE_ASOCKILL: if ((stcb == NULL) || (inp == NULL)) { break; } SCTP_STAT_INCR(sctps_timoassockill); /* Can we free it yet? */ SCTP_INP_DECR_REF(inp); sctp_timer_stop(SCTP_TIMER_TYPE_ASOCKILL, inp, stcb, NULL, SCTP_FROM_SCTPUTIL + SCTP_LOC_1); #if defined (__APPLE__) || defined(SCTP_SO_LOCK_TESTING) so = SCTP_INP_SO(inp); atomic_add_int(&stcb->asoc.refcnt, 1); SCTP_TCB_UNLOCK(stcb); SCTP_SOCKET_LOCK(so, 1); SCTP_TCB_LOCK(stcb); atomic_subtract_int(&stcb->asoc.refcnt, 1); #endif (void)sctp_free_assoc(inp, stcb, SCTP_NORMAL_PROC, SCTP_FROM_SCTPUTIL + SCTP_LOC_2); #if defined (__APPLE__) || defined(SCTP_SO_LOCK_TESTING) SCTP_SOCKET_UNLOCK(so, 1); #endif /* * free asoc, always unlocks (or destroy's) so prevent * duplicate unlock or unlock of a free mtx :-0 */ stcb = NULL; goto out_no_decr; case SCTP_TIMER_TYPE_INPKILL: SCTP_STAT_INCR(sctps_timoinpkill); if (inp == NULL) { break; } /* * special case, take away our increment since WE are the * killer */ SCTP_INP_DECR_REF(inp); sctp_timer_stop(SCTP_TIMER_TYPE_INPKILL, inp, NULL, NULL, SCTP_FROM_SCTPUTIL + SCTP_LOC_3); sctp_inpcb_free(inp, SCTP_FREE_SHOULD_USE_ABORT, SCTP_CALLED_DIRECTLY_NOCMPSET); inp = NULL; goto out_no_decr; default: SCTPDBG(SCTP_DEBUG_TIMER1, "sctp_timeout_handler:unknown timer %d\n", tmr->type); break; }; #ifdef SCTP_AUDITING_ENABLED sctp_audit_log(0xF1, (uint8_t) tmr->type); if (inp) sctp_auditing(5, inp, stcb, net); #endif if ((did_output) && stcb) { /* * Now we need to clean up the control chunk chain if an * ECNE is on it. It must be marked as UNSENT again so next * call will continue to send it until such time that we get * a CWR, to remove it. It is, however, less likely that we * will find a ecn echo on the chain though. */ sctp_fix_ecn_echo(&stcb->asoc); } get_out: if (stcb) { SCTP_TCB_UNLOCK(stcb); } out_decr: if (inp) { SCTP_INP_DECR_REF(inp); } out_no_decr: SCTPDBG(SCTP_DEBUG_TIMER1, "Timer now complete (type %d)\n", type); + CURVNET_RESTORE(); } void sctp_timer_start(int t_type, struct sctp_inpcb *inp, struct sctp_tcb *stcb, struct sctp_nets *net) { int to_ticks; struct sctp_timer *tmr; if ((t_type != SCTP_TIMER_TYPE_ADDR_WQ) && (inp == NULL)) return; to_ticks = 0; tmr = NULL; if (stcb) { SCTP_TCB_LOCK_ASSERT(stcb); } switch (t_type) { case SCTP_TIMER_TYPE_ZERO_COPY: tmr = &inp->sctp_ep.zero_copy_timer; to_ticks = SCTP_ZERO_COPY_TICK_DELAY; break; case SCTP_TIMER_TYPE_ZCOPY_SENDQ: tmr = &inp->sctp_ep.zero_copy_sendq_timer; to_ticks = SCTP_ZERO_COPY_SENDQ_TICK_DELAY; break; case SCTP_TIMER_TYPE_ADDR_WQ: /* Only 1 tick away :-) */ tmr = &SCTP_BASE_INFO(addr_wq_timer); to_ticks = SCTP_ADDRESS_TICK_DELAY; break; case SCTP_TIMER_TYPE_ITERATOR: { struct sctp_iterator *it; it = (struct sctp_iterator *)inp; tmr = &it->tmr; to_ticks = SCTP_ITERATOR_TICKS; } break; case SCTP_TIMER_TYPE_SEND: /* Here we use the RTO timer */ { int rto_val; if ((stcb == NULL) || (net == NULL)) { return; } tmr = &net->rxt_timer; if (net->RTO == 0) { rto_val = stcb->asoc.initial_rto; } else { rto_val = net->RTO; } to_ticks = MSEC_TO_TICKS(rto_val); } break; case SCTP_TIMER_TYPE_INIT: /* * Here we use the INIT timer default usually about 1 * minute. */ if ((stcb == NULL) || (net == NULL)) { return; } tmr = &net->rxt_timer; if (net->RTO == 0) { to_ticks = MSEC_TO_TICKS(stcb->asoc.initial_rto); } else { to_ticks = MSEC_TO_TICKS(net->RTO); } break; case SCTP_TIMER_TYPE_RECV: /* * Here we use the Delayed-Ack timer value from the inp * ususually about 200ms. */ if (stcb == NULL) { return; } tmr = &stcb->asoc.dack_timer; to_ticks = MSEC_TO_TICKS(stcb->asoc.delayed_ack); break; case SCTP_TIMER_TYPE_SHUTDOWN: /* Here we use the RTO of the destination. */ if ((stcb == NULL) || (net == NULL)) { return; } if (net->RTO == 0) { to_ticks = MSEC_TO_TICKS(stcb->asoc.initial_rto); } else { to_ticks = MSEC_TO_TICKS(net->RTO); } tmr = &net->rxt_timer; break; case SCTP_TIMER_TYPE_HEARTBEAT: /* * the net is used here so that we can add in the RTO. Even * though we use a different timer. We also add the HB timer * PLUS a random jitter. */ if ((inp == NULL) || (stcb == NULL)) { return; } else { uint32_t rndval; uint8_t this_random; int cnt_of_unconf = 0; struct sctp_nets *lnet; TAILQ_FOREACH(lnet, &stcb->asoc.nets, sctp_next) { if ((lnet->dest_state & SCTP_ADDR_UNCONFIRMED) && (lnet->dest_state & SCTP_ADDR_REACHABLE)) { cnt_of_unconf++; } } if (cnt_of_unconf) { net = lnet = NULL; (void)sctp_heartbeat_timer(inp, stcb, lnet, cnt_of_unconf); } if (stcb->asoc.hb_random_idx > 3) { rndval = sctp_select_initial_TSN(&inp->sctp_ep); memcpy(stcb->asoc.hb_random_values, &rndval, sizeof(stcb->asoc.hb_random_values)); stcb->asoc.hb_random_idx = 0; } this_random = stcb->asoc.hb_random_values[stcb->asoc.hb_random_idx]; stcb->asoc.hb_random_idx++; stcb->asoc.hb_ect_randombit = 0; /* * this_random will be 0 - 256 ms RTO is in ms. */ if ((stcb->asoc.hb_is_disabled) && (cnt_of_unconf == 0)) { return; } if (net) { int delay; delay = stcb->asoc.heart_beat_delay; TAILQ_FOREACH(lnet, &stcb->asoc.nets, sctp_next) { if ((lnet->dest_state & SCTP_ADDR_UNCONFIRMED) && ((lnet->dest_state & SCTP_ADDR_OUT_OF_SCOPE) == 0) && (lnet->dest_state & SCTP_ADDR_REACHABLE)) { delay = 0; } } if (net->RTO == 0) { /* Never been checked */ to_ticks = this_random + stcb->asoc.initial_rto + delay; } else { /* set rto_val to the ms */ to_ticks = delay + net->RTO + this_random; } } else { if (cnt_of_unconf) { to_ticks = this_random + stcb->asoc.initial_rto; } else { to_ticks = stcb->asoc.heart_beat_delay + this_random + stcb->asoc.initial_rto; } } /* * Now we must convert the to_ticks that are now in * ms to ticks. */ to_ticks = MSEC_TO_TICKS(to_ticks); tmr = &stcb->asoc.hb_timer; } break; case SCTP_TIMER_TYPE_COOKIE: /* * Here we can use the RTO timer from the network since one * RTT was compelete. If a retran happened then we will be * using the RTO initial value. */ if ((stcb == NULL) || (net == NULL)) { return; } if (net->RTO == 0) { to_ticks = MSEC_TO_TICKS(stcb->asoc.initial_rto); } else { to_ticks = MSEC_TO_TICKS(net->RTO); } tmr = &net->rxt_timer; break; case SCTP_TIMER_TYPE_NEWCOOKIE: /* * nothing needed but the endpoint here ususually about 60 * minutes. */ if (inp == NULL) { return; } tmr = &inp->sctp_ep.signature_change; to_ticks = inp->sctp_ep.sctp_timeoutticks[SCTP_TIMER_SIGNATURE]; break; case SCTP_TIMER_TYPE_ASOCKILL: if (stcb == NULL) { return; } tmr = &stcb->asoc.strreset_timer; to_ticks = MSEC_TO_TICKS(SCTP_ASOC_KILL_TIMEOUT); break; case SCTP_TIMER_TYPE_INPKILL: /* * The inp is setup to die. We re-use the signature_chage * timer since that has stopped and we are in the GONE * state. */ if (inp == NULL) { return; } tmr = &inp->sctp_ep.signature_change; to_ticks = MSEC_TO_TICKS(SCTP_INP_KILL_TIMEOUT); break; case SCTP_TIMER_TYPE_PATHMTURAISE: /* * Here we use the value found in the EP for PMTU ususually * about 10 minutes. */ if ((stcb == NULL) || (inp == NULL)) { return; } if (net == NULL) { return; } to_ticks = inp->sctp_ep.sctp_timeoutticks[SCTP_TIMER_PMTU]; tmr = &net->pmtu_timer; break; case SCTP_TIMER_TYPE_SHUTDOWNACK: /* Here we use the RTO of the destination */ if ((stcb == NULL) || (net == NULL)) { return; } if (net->RTO == 0) { to_ticks = MSEC_TO_TICKS(stcb->asoc.initial_rto); } else { to_ticks = MSEC_TO_TICKS(net->RTO); } tmr = &net->rxt_timer; break; case SCTP_TIMER_TYPE_SHUTDOWNGUARD: /* * Here we use the endpoints shutdown guard timer usually * about 3 minutes. */ if ((inp == NULL) || (stcb == NULL)) { return; } to_ticks = inp->sctp_ep.sctp_timeoutticks[SCTP_TIMER_MAXSHUTDOWN]; tmr = &stcb->asoc.shut_guard_timer; break; case SCTP_TIMER_TYPE_STRRESET: /* * Here the timer comes from the stcb but its value is from * the net's RTO. */ if ((stcb == NULL) || (net == NULL)) { return; } if (net->RTO == 0) { to_ticks = MSEC_TO_TICKS(stcb->asoc.initial_rto); } else { to_ticks = MSEC_TO_TICKS(net->RTO); } tmr = &stcb->asoc.strreset_timer; break; case SCTP_TIMER_TYPE_EARLYFR: { unsigned int msec; if ((stcb == NULL) || (net == NULL)) { return; } if (net->flight_size > net->cwnd) { /* no need to start */ return; } SCTP_STAT_INCR(sctps_earlyfrstart); if (net->lastsa == 0) { /* Hmm no rtt estimate yet? */ msec = stcb->asoc.initial_rto >> 2; } else { msec = ((net->lastsa >> 2) + net->lastsv) >> 1; } if (msec < SCTP_BASE_SYSCTL(sctp_early_fr_msec)) { msec = SCTP_BASE_SYSCTL(sctp_early_fr_msec); if (msec < SCTP_MINFR_MSEC_FLOOR) { msec = SCTP_MINFR_MSEC_FLOOR; } } to_ticks = MSEC_TO_TICKS(msec); tmr = &net->fr_timer; } break; case SCTP_TIMER_TYPE_ASCONF: /* * Here the timer comes from the stcb but its value is from * the net's RTO. */ if ((stcb == NULL) || (net == NULL)) { return; } if (net->RTO == 0) { to_ticks = MSEC_TO_TICKS(stcb->asoc.initial_rto); } else { to_ticks = MSEC_TO_TICKS(net->RTO); } tmr = &stcb->asoc.asconf_timer; break; case SCTP_TIMER_TYPE_PRIM_DELETED: if ((stcb == NULL) || (net != NULL)) { return; } to_ticks = MSEC_TO_TICKS(stcb->asoc.initial_rto); tmr = &stcb->asoc.delete_prim_timer; break; case SCTP_TIMER_TYPE_AUTOCLOSE: if (stcb == NULL) { return; } if (stcb->asoc.sctp_autoclose_ticks == 0) { /* * Really an error since stcb is NOT set to * autoclose */ return; } to_ticks = stcb->asoc.sctp_autoclose_ticks; tmr = &stcb->asoc.autoclose_timer; break; default: SCTPDBG(SCTP_DEBUG_TIMER1, "%s: Unknown timer type %d\n", __FUNCTION__, t_type); return; break; }; if ((to_ticks <= 0) || (tmr == NULL)) { SCTPDBG(SCTP_DEBUG_TIMER1, "%s: %d:software error to_ticks:%d tmr:%p not set ??\n", __FUNCTION__, t_type, to_ticks, tmr); return; } if (SCTP_OS_TIMER_PENDING(&tmr->timer)) { /* * we do NOT allow you to have it already running. if it is * we leave the current one up unchanged */ return; } /* At this point we can proceed */ if (t_type == SCTP_TIMER_TYPE_SEND) { stcb->asoc.num_send_timers_up++; } tmr->stopped_from = 0; tmr->type = t_type; tmr->ep = (void *)inp; tmr->tcb = (void *)stcb; tmr->net = (void *)net; tmr->self = (void *)tmr; + tmr->vnet = (void *)curvnet; tmr->ticks = sctp_get_tick_count(); (void)SCTP_OS_TIMER_START(&tmr->timer, to_ticks, sctp_timeout_handler, tmr); return; } void sctp_timer_stop(int t_type, struct sctp_inpcb *inp, struct sctp_tcb *stcb, struct sctp_nets *net, uint32_t from) { struct sctp_timer *tmr; if ((t_type != SCTP_TIMER_TYPE_ADDR_WQ) && (inp == NULL)) return; tmr = NULL; if (stcb) { SCTP_TCB_LOCK_ASSERT(stcb); } switch (t_type) { case SCTP_TIMER_TYPE_ZERO_COPY: tmr = &inp->sctp_ep.zero_copy_timer; break; case SCTP_TIMER_TYPE_ZCOPY_SENDQ: tmr = &inp->sctp_ep.zero_copy_sendq_timer; break; case SCTP_TIMER_TYPE_ADDR_WQ: tmr = &SCTP_BASE_INFO(addr_wq_timer); break; case SCTP_TIMER_TYPE_EARLYFR: if ((stcb == NULL) || (net == NULL)) { return; } tmr = &net->fr_timer; SCTP_STAT_INCR(sctps_earlyfrstop); break; case SCTP_TIMER_TYPE_ITERATOR: { struct sctp_iterator *it; it = (struct sctp_iterator *)inp; tmr = &it->tmr; } break; case SCTP_TIMER_TYPE_SEND: if ((stcb == NULL) || (net == NULL)) { return; } tmr = &net->rxt_timer; break; case SCTP_TIMER_TYPE_INIT: if ((stcb == NULL) || (net == NULL)) { return; } tmr = &net->rxt_timer; break; case SCTP_TIMER_TYPE_RECV: if (stcb == NULL) { return; } tmr = &stcb->asoc.dack_timer; break; case SCTP_TIMER_TYPE_SHUTDOWN: if ((stcb == NULL) || (net == NULL)) { return; } tmr = &net->rxt_timer; break; case SCTP_TIMER_TYPE_HEARTBEAT: if (stcb == NULL) { return; } tmr = &stcb->asoc.hb_timer; break; case SCTP_TIMER_TYPE_COOKIE: if ((stcb == NULL) || (net == NULL)) { return; } tmr = &net->rxt_timer; break; case SCTP_TIMER_TYPE_NEWCOOKIE: /* nothing needed but the endpoint here */ tmr = &inp->sctp_ep.signature_change; /* * We re-use the newcookie timer for the INP kill timer. We * must assure that we do not kill it by accident. */ break; case SCTP_TIMER_TYPE_ASOCKILL: /* * Stop the asoc kill timer. */ if (stcb == NULL) { return; } tmr = &stcb->asoc.strreset_timer; break; case SCTP_TIMER_TYPE_INPKILL: /* * The inp is setup to die. We re-use the signature_chage * timer since that has stopped and we are in the GONE * state. */ tmr = &inp->sctp_ep.signature_change; break; case SCTP_TIMER_TYPE_PATHMTURAISE: if ((stcb == NULL) || (net == NULL)) { return; } tmr = &net->pmtu_timer; break; case SCTP_TIMER_TYPE_SHUTDOWNACK: if ((stcb == NULL) || (net == NULL)) { return; } tmr = &net->rxt_timer; break; case SCTP_TIMER_TYPE_SHUTDOWNGUARD: if (stcb == NULL) { return; } tmr = &stcb->asoc.shut_guard_timer; break; case SCTP_TIMER_TYPE_STRRESET: if (stcb == NULL) { return; } tmr = &stcb->asoc.strreset_timer; break; case SCTP_TIMER_TYPE_ASCONF: if (stcb == NULL) { return; } tmr = &stcb->asoc.asconf_timer; break; case SCTP_TIMER_TYPE_PRIM_DELETED: if (stcb == NULL) { return; } tmr = &stcb->asoc.delete_prim_timer; break; case SCTP_TIMER_TYPE_AUTOCLOSE: if (stcb == NULL) { return; } tmr = &stcb->asoc.autoclose_timer; break; default: SCTPDBG(SCTP_DEBUG_TIMER1, "%s: Unknown timer type %d\n", __FUNCTION__, t_type); break; }; if (tmr == NULL) { return; } if ((tmr->type != t_type) && tmr->type) { /* * Ok we have a timer that is under joint use. Cookie timer * per chance with the SEND timer. We therefore are NOT * running the timer that the caller wants stopped. So just * return. */ return; } if ((t_type == SCTP_TIMER_TYPE_SEND) && (stcb != NULL)) { stcb->asoc.num_send_timers_up--; if (stcb->asoc.num_send_timers_up < 0) { stcb->asoc.num_send_timers_up = 0; } } tmr->self = NULL; tmr->stopped_from = from; (void)SCTP_OS_TIMER_STOP(&tmr->timer); return; } uint32_t sctp_calculate_len(struct mbuf *m) { uint32_t tlen = 0; struct mbuf *at; at = m; while (at) { tlen += SCTP_BUF_LEN(at); at = SCTP_BUF_NEXT(at); } return (tlen); } void sctp_mtu_size_reset(struct sctp_inpcb *inp, struct sctp_association *asoc, uint32_t mtu) { /* * Reset the P-MTU size on this association, this involves changing * the asoc MTU, going through ANY chunk+overhead larger than mtu to * allow the DF flag to be cleared. */ struct sctp_tmit_chunk *chk; unsigned int eff_mtu, ovh; #ifdef SCTP_PRINT_FOR_B_AND_M SCTP_PRINTF("sctp_mtu_size_reset(%p, asoc:%p mtu:%d\n", inp, asoc, mtu); #endif asoc->smallest_mtu = mtu; if (inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) { ovh = SCTP_MIN_OVERHEAD; } else { ovh = SCTP_MIN_V4_OVERHEAD; } eff_mtu = mtu - ovh; TAILQ_FOREACH(chk, &asoc->send_queue, sctp_next) { if (chk->send_size > eff_mtu) { chk->flags |= CHUNK_FLAGS_FRAGMENT_OK; } } TAILQ_FOREACH(chk, &asoc->sent_queue, sctp_next) { if (chk->send_size > eff_mtu) { chk->flags |= CHUNK_FLAGS_FRAGMENT_OK; } } } /* * given an association and starting time of the current RTT period return * RTO in number of msecs net should point to the current network */ uint32_t sctp_calculate_rto(struct sctp_tcb *stcb, struct sctp_association *asoc, struct sctp_nets *net, struct timeval *told, int safe) { /*- * given an association and the starting time of the current RTT * period (in value1/value2) return RTO in number of msecs. */ int calc_time = 0; int o_calctime; uint32_t new_rto = 0; int first_measure = 0; struct timeval now, then, *old; /* Copy it out for sparc64 */ if (safe == sctp_align_unsafe_makecopy) { old = &then; memcpy(&then, told, sizeof(struct timeval)); } else if (safe == sctp_align_safe_nocopy) { old = told; } else { /* error */ SCTP_PRINTF("Huh, bad rto calc call\n"); return (0); } /************************/ /* 1. calculate new RTT */ /************************/ /* get the current time */ (void)SCTP_GETTIME_TIMEVAL(&now); /* compute the RTT value */ if ((u_long)now.tv_sec > (u_long)old->tv_sec) { calc_time = ((u_long)now.tv_sec - (u_long)old->tv_sec) * 1000; if ((u_long)now.tv_usec > (u_long)old->tv_usec) { calc_time += (((u_long)now.tv_usec - (u_long)old->tv_usec) / 1000); } else if ((u_long)now.tv_usec < (u_long)old->tv_usec) { /* Borrow 1,000ms from current calculation */ calc_time -= 1000; /* Add in the slop over */ calc_time += ((int)now.tv_usec / 1000); /* Add in the pre-second ms's */ calc_time += (((int)1000000 - (int)old->tv_usec) / 1000); } } else if ((u_long)now.tv_sec == (u_long)old->tv_sec) { if ((u_long)now.tv_usec > (u_long)old->tv_usec) { calc_time = ((u_long)now.tv_usec - (u_long)old->tv_usec) / 1000; } else if ((u_long)now.tv_usec < (u_long)old->tv_usec) { /* impossible .. garbage in nothing out */ goto calc_rto; } else if ((u_long)now.tv_usec == (u_long)old->tv_usec) { /* * We have to have 1 usec :-D this must be the * loopback. */ calc_time = 1; } else { /* impossible .. garbage in nothing out */ goto calc_rto; } } else { /* Clock wrapped? */ goto calc_rto; } /***************************/ /* 2. update RTTVAR & SRTT */ /***************************/ net->rtt = o_calctime = calc_time; /* this is Van Jacobson's integer version */ if (net->RTO_measured) { calc_time -= (net->lastsa >> SCTP_RTT_SHIFT); /* take away 1/8th when * shift=3 */ if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_RTTVAR_LOGGING_ENABLE) { rto_logging(net, SCTP_LOG_RTTVAR); } net->prev_rtt = o_calctime; net->lastsa += calc_time; /* add 7/8th into sa when * shift=3 */ if (calc_time < 0) { calc_time = -calc_time; } calc_time -= (net->lastsv >> SCTP_RTT_VAR_SHIFT); /* take away 1/4 when * VAR shift=2 */ net->lastsv += calc_time; if (net->lastsv == 0) { net->lastsv = SCTP_CLOCK_GRANULARITY; } } else { /* First RTO measurment */ net->RTO_measured = 1; net->lastsa = calc_time << SCTP_RTT_SHIFT; /* Multiply by 8 when * shift=3 */ net->lastsv = calc_time; if (net->lastsv == 0) { net->lastsv = SCTP_CLOCK_GRANULARITY; } first_measure = 1; net->prev_rtt = o_calctime; if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_RTTVAR_LOGGING_ENABLE) { rto_logging(net, SCTP_LOG_INITIAL_RTT); } } calc_rto: new_rto = (net->lastsa >> SCTP_RTT_SHIFT) + net->lastsv; if ((new_rto > SCTP_SAT_NETWORK_MIN) && (stcb->asoc.sat_network_lockout == 0)) { stcb->asoc.sat_network = 1; } else if ((!first_measure) && stcb->asoc.sat_network) { stcb->asoc.sat_network = 0; stcb->asoc.sat_network_lockout = 1; } /* bound it, per C6/C7 in Section 5.3.1 */ if (new_rto < stcb->asoc.minrto) { new_rto = stcb->asoc.minrto; } if (new_rto > stcb->asoc.maxrto) { new_rto = stcb->asoc.maxrto; } /* we are now returning the RTO */ return (new_rto); } /* * return a pointer to a contiguous piece of data from the given mbuf chain * starting at 'off' for 'len' bytes. If the desired piece spans more than * one mbuf, a copy is made at 'ptr'. caller must ensure that the buffer size * is >= 'len' returns NULL if there there isn't 'len' bytes in the chain. */ caddr_t sctp_m_getptr(struct mbuf *m, int off, int len, uint8_t * in_ptr) { uint32_t count; uint8_t *ptr; ptr = in_ptr; if ((off < 0) || (len <= 0)) return (NULL); /* find the desired start location */ while ((m != NULL) && (off > 0)) { if (off < SCTP_BUF_LEN(m)) break; off -= SCTP_BUF_LEN(m); m = SCTP_BUF_NEXT(m); } if (m == NULL) return (NULL); /* is the current mbuf large enough (eg. contiguous)? */ if ((SCTP_BUF_LEN(m) - off) >= len) { return (mtod(m, caddr_t)+off); } else { /* else, it spans more than one mbuf, so save a temp copy... */ while ((m != NULL) && (len > 0)) { count = min(SCTP_BUF_LEN(m) - off, len); bcopy(mtod(m, caddr_t)+off, ptr, count); len -= count; ptr += count; off = 0; m = SCTP_BUF_NEXT(m); } if ((m == NULL) && (len > 0)) return (NULL); else return ((caddr_t)in_ptr); } } struct sctp_paramhdr * sctp_get_next_param(struct mbuf *m, int offset, struct sctp_paramhdr *pull, int pull_limit) { /* This just provides a typed signature to Peter's Pull routine */ return ((struct sctp_paramhdr *)sctp_m_getptr(m, offset, pull_limit, (uint8_t *) pull)); } int sctp_add_pad_tombuf(struct mbuf *m, int padlen) { /* * add padlen bytes of 0 filled padding to the end of the mbuf. If * padlen is > 3 this routine will fail. */ uint8_t *dp; int i; if (padlen > 3) { SCTP_LTRACE_ERR_RET_PKT(m, NULL, NULL, NULL, SCTP_FROM_SCTPUTIL, ENOBUFS); return (ENOBUFS); } if (padlen <= M_TRAILINGSPACE(m)) { /* * The easy way. We hope the majority of the time we hit * here :) */ dp = (uint8_t *) (mtod(m, caddr_t)+SCTP_BUF_LEN(m)); SCTP_BUF_LEN(m) += padlen; } else { /* Hard way we must grow the mbuf */ struct mbuf *tmp; tmp = sctp_get_mbuf_for_msg(padlen, 0, M_DONTWAIT, 1, MT_DATA); if (tmp == NULL) { /* Out of space GAK! we are in big trouble. */ SCTP_LTRACE_ERR_RET_PKT(m, NULL, NULL, NULL, SCTP_FROM_SCTPUTIL, EINVAL); return (ENOSPC); } /* setup and insert in middle */ SCTP_BUF_LEN(tmp) = padlen; SCTP_BUF_NEXT(tmp) = NULL; SCTP_BUF_NEXT(m) = tmp; dp = mtod(tmp, uint8_t *); } /* zero out the pad */ for (i = 0; i < padlen; i++) { *dp = 0; dp++; } return (0); } int sctp_pad_lastmbuf(struct mbuf *m, int padval, struct mbuf *last_mbuf) { /* find the last mbuf in chain and pad it */ struct mbuf *m_at; m_at = m; if (last_mbuf) { return (sctp_add_pad_tombuf(last_mbuf, padval)); } else { while (m_at) { if (SCTP_BUF_NEXT(m_at) == NULL) { return (sctp_add_pad_tombuf(m_at, padval)); } m_at = SCTP_BUF_NEXT(m_at); } } SCTP_LTRACE_ERR_RET_PKT(m, NULL, NULL, NULL, SCTP_FROM_SCTPUTIL, EFAULT); return (EFAULT); } int sctp_asoc_change_wake = 0; static void sctp_notify_assoc_change(uint32_t event, struct sctp_tcb *stcb, uint32_t error, void *data, int so_locked #if !defined(__APPLE__) && !defined(SCTP_SO_LOCK_TESTING) SCTP_UNUSED #endif ) { struct mbuf *m_notify; struct sctp_assoc_change *sac; struct sctp_queued_to_read *control; #if defined (__APPLE__) || defined(SCTP_SO_LOCK_TESTING) struct socket *so; #endif /* * For TCP model AND UDP connected sockets we will send an error up * when an ABORT comes in. */ if (((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) || (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL)) && ((event == SCTP_COMM_LOST) || (event == SCTP_CANT_STR_ASSOC))) { if (SCTP_GET_STATE(&stcb->asoc) == SCTP_STATE_COOKIE_WAIT) { SCTP_LTRACE_ERR_RET(NULL, stcb, NULL, SCTP_FROM_SCTPUTIL, ECONNREFUSED); stcb->sctp_socket->so_error = ECONNREFUSED; } else { SCTP_LTRACE_ERR_RET(NULL, stcb, NULL, SCTP_FROM_SCTPUTIL, ECONNRESET); stcb->sctp_socket->so_error = ECONNRESET; } /* Wake ANY sleepers */ #if defined (__APPLE__) || defined(SCTP_SO_LOCK_TESTING) so = SCTP_INP_SO(stcb->sctp_ep); if (!so_locked) { atomic_add_int(&stcb->asoc.refcnt, 1); SCTP_TCB_UNLOCK(stcb); SCTP_SOCKET_LOCK(so, 1); SCTP_TCB_LOCK(stcb); atomic_subtract_int(&stcb->asoc.refcnt, 1); if (stcb->asoc.state & SCTP_STATE_CLOSED_SOCKET) { SCTP_SOCKET_UNLOCK(so, 1); return; } } #endif sorwakeup(stcb->sctp_socket); sowwakeup(stcb->sctp_socket); #if defined (__APPLE__) || defined(SCTP_SO_LOCK_TESTING) if (!so_locked) { SCTP_SOCKET_UNLOCK(so, 1); } #endif sctp_asoc_change_wake++; } if (sctp_is_feature_off(stcb->sctp_ep, SCTP_PCB_FLAGS_RECVASSOCEVNT)) { /* event not enabled */ return; } m_notify = sctp_get_mbuf_for_msg(sizeof(struct sctp_assoc_change), 0, M_DONTWAIT, 1, MT_DATA); if (m_notify == NULL) /* no space left */ return; SCTP_BUF_LEN(m_notify) = 0; sac = mtod(m_notify, struct sctp_assoc_change *); sac->sac_type = SCTP_ASSOC_CHANGE; sac->sac_flags = 0; sac->sac_length = sizeof(struct sctp_assoc_change); sac->sac_state = event; sac->sac_error = error; /* XXX verify these stream counts */ sac->sac_outbound_streams = stcb->asoc.streamoutcnt; sac->sac_inbound_streams = stcb->asoc.streamincnt; sac->sac_assoc_id = sctp_get_associd(stcb); SCTP_BUF_LEN(m_notify) = sizeof(struct sctp_assoc_change); SCTP_BUF_NEXT(m_notify) = NULL; control = sctp_build_readq_entry(stcb, stcb->asoc.primary_destination, 0, 0, 0, 0, 0, 0, m_notify); if (control == NULL) { /* no memory */ sctp_m_freem(m_notify); return; } control->length = SCTP_BUF_LEN(m_notify); /* not that we need this */ control->tail_mbuf = m_notify; control->spec_flags = M_NOTIFICATION; sctp_add_to_readq(stcb->sctp_ep, stcb, control, &stcb->sctp_socket->so_rcv, 1, SCTP_READ_LOCK_NOT_HELD, so_locked); if (event == SCTP_COMM_LOST) { /* Wake up any sleeper */ #if defined (__APPLE__) || defined(SCTP_SO_LOCK_TESTING) so = SCTP_INP_SO(stcb->sctp_ep); if (!so_locked) { atomic_add_int(&stcb->asoc.refcnt, 1); SCTP_TCB_UNLOCK(stcb); SCTP_SOCKET_LOCK(so, 1); SCTP_TCB_LOCK(stcb); atomic_subtract_int(&stcb->asoc.refcnt, 1); if (stcb->asoc.state & SCTP_STATE_CLOSED_SOCKET) { SCTP_SOCKET_UNLOCK(so, 1); return; } } #endif sctp_sowwakeup(stcb->sctp_ep, stcb->sctp_socket); #if defined (__APPLE__) || defined(SCTP_SO_LOCK_TESTING) if (!so_locked) { SCTP_SOCKET_UNLOCK(so, 1); } #endif } } static void sctp_notify_peer_addr_change(struct sctp_tcb *stcb, uint32_t state, struct sockaddr *sa, uint32_t error) { struct mbuf *m_notify; struct sctp_paddr_change *spc; struct sctp_queued_to_read *control; if (sctp_is_feature_off(stcb->sctp_ep, SCTP_PCB_FLAGS_RECVPADDREVNT)) { /* event not enabled */ return; } m_notify = sctp_get_mbuf_for_msg(sizeof(struct sctp_paddr_change), 0, M_DONTWAIT, 1, MT_DATA); if (m_notify == NULL) return; SCTP_BUF_LEN(m_notify) = 0; spc = mtod(m_notify, struct sctp_paddr_change *); spc->spc_type = SCTP_PEER_ADDR_CHANGE; spc->spc_flags = 0; spc->spc_length = sizeof(struct sctp_paddr_change); switch (sa->sa_family) { case AF_INET: memcpy(&spc->spc_aaddr, sa, sizeof(struct sockaddr_in)); break; #ifdef INET6 case AF_INET6: { struct sockaddr_in6 *sin6; memcpy(&spc->spc_aaddr, sa, sizeof(struct sockaddr_in6)); sin6 = (struct sockaddr_in6 *)&spc->spc_aaddr; if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr)) { if (sin6->sin6_scope_id == 0) { /* recover scope_id for user */ (void)sa6_recoverscope(sin6); } else { /* clear embedded scope_id for user */ in6_clearscope(&sin6->sin6_addr); } } break; } #endif default: /* TSNH */ break; } spc->spc_state = state; spc->spc_error = error; spc->spc_assoc_id = sctp_get_associd(stcb); SCTP_BUF_LEN(m_notify) = sizeof(struct sctp_paddr_change); SCTP_BUF_NEXT(m_notify) = NULL; /* append to socket */ control = sctp_build_readq_entry(stcb, stcb->asoc.primary_destination, 0, 0, 0, 0, 0, 0, m_notify); if (control == NULL) { /* no memory */ sctp_m_freem(m_notify); return; } control->length = SCTP_BUF_LEN(m_notify); control->spec_flags = M_NOTIFICATION; /* not that we need this */ control->tail_mbuf = m_notify; sctp_add_to_readq(stcb->sctp_ep, stcb, control, &stcb->sctp_socket->so_rcv, 1, SCTP_READ_LOCK_NOT_HELD, SCTP_SO_NOT_LOCKED); } static void sctp_notify_send_failed(struct sctp_tcb *stcb, uint32_t error, struct sctp_tmit_chunk *chk, int so_locked #if !defined(__APPLE__) && !defined(SCTP_SO_LOCK_TESTING) SCTP_UNUSED #endif ) { struct mbuf *m_notify; struct sctp_send_failed *ssf; struct sctp_queued_to_read *control; int length; if (sctp_is_feature_off(stcb->sctp_ep, SCTP_PCB_FLAGS_RECVSENDFAILEVNT)) { /* event not enabled */ return; } m_notify = sctp_get_mbuf_for_msg(sizeof(struct sctp_send_failed), 0, M_DONTWAIT, 1, MT_DATA); if (m_notify == NULL) /* no space left */ return; length = sizeof(struct sctp_send_failed) + chk->send_size; length -= sizeof(struct sctp_data_chunk); SCTP_BUF_LEN(m_notify) = 0; ssf = mtod(m_notify, struct sctp_send_failed *); ssf->ssf_type = SCTP_SEND_FAILED; if (error == SCTP_NOTIFY_DATAGRAM_UNSENT) ssf->ssf_flags = SCTP_DATA_UNSENT; else ssf->ssf_flags = SCTP_DATA_SENT; ssf->ssf_length = length; ssf->ssf_error = error; /* not exactly what the user sent in, but should be close :) */ bzero(&ssf->ssf_info, sizeof(ssf->ssf_info)); ssf->ssf_info.sinfo_stream = chk->rec.data.stream_number; ssf->ssf_info.sinfo_ssn = chk->rec.data.stream_seq; ssf->ssf_info.sinfo_flags = chk->rec.data.rcv_flags; ssf->ssf_info.sinfo_ppid = chk->rec.data.payloadtype; ssf->ssf_info.sinfo_context = chk->rec.data.context; ssf->ssf_info.sinfo_assoc_id = sctp_get_associd(stcb); ssf->ssf_assoc_id = sctp_get_associd(stcb); if (chk->data) { /* * trim off the sctp chunk header(it should be there) */ if (chk->send_size >= sizeof(struct sctp_data_chunk)) { m_adj(chk->data, sizeof(struct sctp_data_chunk)); sctp_mbuf_crush(chk->data); chk->send_size -= sizeof(struct sctp_data_chunk); } } SCTP_BUF_NEXT(m_notify) = chk->data; SCTP_BUF_LEN(m_notify) = sizeof(struct sctp_send_failed); /* Steal off the mbuf */ chk->data = NULL; /* * For this case, we check the actual socket buffer, since the assoc * is going away we don't want to overfill the socket buffer for a * non-reader */ if (sctp_sbspace_failedmsgs(&stcb->sctp_socket->so_rcv) < SCTP_BUF_LEN(m_notify)) { sctp_m_freem(m_notify); return; } /* append to socket */ control = sctp_build_readq_entry(stcb, stcb->asoc.primary_destination, 0, 0, 0, 0, 0, 0, m_notify); if (control == NULL) { /* no memory */ sctp_m_freem(m_notify); return; } control->spec_flags = M_NOTIFICATION; sctp_add_to_readq(stcb->sctp_ep, stcb, control, &stcb->sctp_socket->so_rcv, 1, SCTP_READ_LOCK_NOT_HELD, so_locked); } static void sctp_notify_send_failed2(struct sctp_tcb *stcb, uint32_t error, struct sctp_stream_queue_pending *sp, int so_locked #if !defined(__APPLE__) && !defined(SCTP_SO_LOCK_TESTING) SCTP_UNUSED #endif ) { struct mbuf *m_notify; struct sctp_send_failed *ssf; struct sctp_queued_to_read *control; int length; if (sctp_is_feature_off(stcb->sctp_ep, SCTP_PCB_FLAGS_RECVSENDFAILEVNT)) { /* event not enabled */ return; } length = sizeof(struct sctp_send_failed) + sp->length; m_notify = sctp_get_mbuf_for_msg(sizeof(struct sctp_send_failed), 0, M_DONTWAIT, 1, MT_DATA); if (m_notify == NULL) /* no space left */ return; SCTP_BUF_LEN(m_notify) = 0; ssf = mtod(m_notify, struct sctp_send_failed *); ssf->ssf_type = SCTP_SEND_FAILED; if (error == SCTP_NOTIFY_DATAGRAM_UNSENT) ssf->ssf_flags = SCTP_DATA_UNSENT; else ssf->ssf_flags = SCTP_DATA_SENT; ssf->ssf_length = length; ssf->ssf_error = error; /* not exactly what the user sent in, but should be close :) */ bzero(&ssf->ssf_info, sizeof(ssf->ssf_info)); ssf->ssf_info.sinfo_stream = sp->stream; ssf->ssf_info.sinfo_ssn = sp->strseq; if (sp->some_taken) { ssf->ssf_info.sinfo_flags = SCTP_DATA_LAST_FRAG; } else { ssf->ssf_info.sinfo_flags = SCTP_DATA_NOT_FRAG; } ssf->ssf_info.sinfo_ppid = sp->ppid; ssf->ssf_info.sinfo_context = sp->context; ssf->ssf_info.sinfo_assoc_id = sctp_get_associd(stcb); ssf->ssf_assoc_id = sctp_get_associd(stcb); SCTP_BUF_NEXT(m_notify) = sp->data; SCTP_BUF_LEN(m_notify) = sizeof(struct sctp_send_failed); /* Steal off the mbuf */ sp->data = NULL; /* * For this case, we check the actual socket buffer, since the assoc * is going away we don't want to overfill the socket buffer for a * non-reader */ if (sctp_sbspace_failedmsgs(&stcb->sctp_socket->so_rcv) < SCTP_BUF_LEN(m_notify)) { sctp_m_freem(m_notify); return; } /* append to socket */ control = sctp_build_readq_entry(stcb, stcb->asoc.primary_destination, 0, 0, 0, 0, 0, 0, m_notify); if (control == NULL) { /* no memory */ sctp_m_freem(m_notify); return; } control->spec_flags = M_NOTIFICATION; sctp_add_to_readq(stcb->sctp_ep, stcb, control, &stcb->sctp_socket->so_rcv, 1, SCTP_READ_LOCK_NOT_HELD, so_locked); } static void sctp_notify_adaptation_layer(struct sctp_tcb *stcb, uint32_t error) { struct mbuf *m_notify; struct sctp_adaptation_event *sai; struct sctp_queued_to_read *control; if (sctp_is_feature_off(stcb->sctp_ep, SCTP_PCB_FLAGS_ADAPTATIONEVNT)) { /* event not enabled */ return; } m_notify = sctp_get_mbuf_for_msg(sizeof(struct sctp_adaption_event), 0, M_DONTWAIT, 1, MT_DATA); if (m_notify == NULL) /* no space left */ return; SCTP_BUF_LEN(m_notify) = 0; sai = mtod(m_notify, struct sctp_adaptation_event *); sai->sai_type = SCTP_ADAPTATION_INDICATION; sai->sai_flags = 0; sai->sai_length = sizeof(struct sctp_adaptation_event); sai->sai_adaptation_ind = stcb->asoc.peers_adaptation; sai->sai_assoc_id = sctp_get_associd(stcb); SCTP_BUF_LEN(m_notify) = sizeof(struct sctp_adaptation_event); SCTP_BUF_NEXT(m_notify) = NULL; /* append to socket */ control = sctp_build_readq_entry(stcb, stcb->asoc.primary_destination, 0, 0, 0, 0, 0, 0, m_notify); if (control == NULL) { /* no memory */ sctp_m_freem(m_notify); return; } control->length = SCTP_BUF_LEN(m_notify); control->spec_flags = M_NOTIFICATION; /* not that we need this */ control->tail_mbuf = m_notify; sctp_add_to_readq(stcb->sctp_ep, stcb, control, &stcb->sctp_socket->so_rcv, 1, SCTP_READ_LOCK_NOT_HELD, SCTP_SO_NOT_LOCKED); } /* This always must be called with the read-queue LOCKED in the INP */ static void sctp_notify_partial_delivery_indication(struct sctp_tcb *stcb, uint32_t error, uint32_t val, int so_locked #if !defined(__APPLE__) && !defined(SCTP_SO_LOCK_TESTING) SCTP_UNUSED #endif ) { struct mbuf *m_notify; struct sctp_pdapi_event *pdapi; struct sctp_queued_to_read *control; struct sockbuf *sb; if (sctp_is_feature_off(stcb->sctp_ep, SCTP_PCB_FLAGS_PDAPIEVNT)) { /* event not enabled */ return; } m_notify = sctp_get_mbuf_for_msg(sizeof(struct sctp_pdapi_event), 0, M_DONTWAIT, 1, MT_DATA); if (m_notify == NULL) /* no space left */ return; SCTP_BUF_LEN(m_notify) = 0; pdapi = mtod(m_notify, struct sctp_pdapi_event *); pdapi->pdapi_type = SCTP_PARTIAL_DELIVERY_EVENT; pdapi->pdapi_flags = 0; pdapi->pdapi_length = sizeof(struct sctp_pdapi_event); pdapi->pdapi_indication = error; pdapi->pdapi_stream = (val >> 16); pdapi->pdapi_seq = (val & 0x0000ffff); pdapi->pdapi_assoc_id = sctp_get_associd(stcb); SCTP_BUF_LEN(m_notify) = sizeof(struct sctp_pdapi_event); SCTP_BUF_NEXT(m_notify) = NULL; control = sctp_build_readq_entry(stcb, stcb->asoc.primary_destination, 0, 0, 0, 0, 0, 0, m_notify); if (control == NULL) { /* no memory */ sctp_m_freem(m_notify); return; } control->spec_flags = M_NOTIFICATION; control->length = SCTP_BUF_LEN(m_notify); /* not that we need this */ control->tail_mbuf = m_notify; control->held_length = 0; control->length = 0; sb = &stcb->sctp_socket->so_rcv; if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_SB_LOGGING_ENABLE) { sctp_sblog(sb, control->do_not_ref_stcb ? NULL : stcb, SCTP_LOG_SBALLOC, SCTP_BUF_LEN(m_notify)); } sctp_sballoc(stcb, sb, m_notify); if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_SB_LOGGING_ENABLE) { sctp_sblog(sb, control->do_not_ref_stcb ? NULL : stcb, SCTP_LOG_SBRESULT, 0); } atomic_add_int(&control->length, SCTP_BUF_LEN(m_notify)); control->end_added = 1; if (stcb->asoc.control_pdapi) TAILQ_INSERT_AFTER(&stcb->sctp_ep->read_queue, stcb->asoc.control_pdapi, control, next); else { /* we really should not see this case */ TAILQ_INSERT_TAIL(&stcb->sctp_ep->read_queue, control, next); } if (stcb->sctp_ep && stcb->sctp_socket) { /* This should always be the case */ #if defined (__APPLE__) || defined(SCTP_SO_LOCK_TESTING) struct socket *so; so = SCTP_INP_SO(stcb->sctp_ep); if (!so_locked) { atomic_add_int(&stcb->asoc.refcnt, 1); SCTP_TCB_UNLOCK(stcb); SCTP_SOCKET_LOCK(so, 1); SCTP_TCB_LOCK(stcb); atomic_subtract_int(&stcb->asoc.refcnt, 1); if (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) { SCTP_SOCKET_UNLOCK(so, 1); return; } } #endif sctp_sorwakeup(stcb->sctp_ep, stcb->sctp_socket); #if defined (__APPLE__) || defined(SCTP_SO_LOCK_TESTING) if (!so_locked) { SCTP_SOCKET_UNLOCK(so, 1); } #endif } } static void sctp_notify_shutdown_event(struct sctp_tcb *stcb) { struct mbuf *m_notify; struct sctp_shutdown_event *sse; struct sctp_queued_to_read *control; /* * For TCP model AND UDP connected sockets we will send an error up * when an SHUTDOWN completes */ if ((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) || (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL)) { /* mark socket closed for read/write and wakeup! */ #if defined (__APPLE__) || defined(SCTP_SO_LOCK_TESTING) struct socket *so; so = SCTP_INP_SO(stcb->sctp_ep); atomic_add_int(&stcb->asoc.refcnt, 1); SCTP_TCB_UNLOCK(stcb); SCTP_SOCKET_LOCK(so, 1); SCTP_TCB_LOCK(stcb); atomic_subtract_int(&stcb->asoc.refcnt, 1); if (stcb->asoc.state & SCTP_STATE_CLOSED_SOCKET) { SCTP_SOCKET_UNLOCK(so, 1); return; } #endif socantsendmore(stcb->sctp_socket); #if defined (__APPLE__) || defined(SCTP_SO_LOCK_TESTING) SCTP_SOCKET_UNLOCK(so, 1); #endif } if (sctp_is_feature_off(stcb->sctp_ep, SCTP_PCB_FLAGS_RECVSHUTDOWNEVNT)) { /* event not enabled */ return; } m_notify = sctp_get_mbuf_for_msg(sizeof(struct sctp_shutdown_event), 0, M_DONTWAIT, 1, MT_DATA); if (m_notify == NULL) /* no space left */ return; sse = mtod(m_notify, struct sctp_shutdown_event *); sse->sse_type = SCTP_SHUTDOWN_EVENT; sse->sse_flags = 0; sse->sse_length = sizeof(struct sctp_shutdown_event); sse->sse_assoc_id = sctp_get_associd(stcb); SCTP_BUF_LEN(m_notify) = sizeof(struct sctp_shutdown_event); SCTP_BUF_NEXT(m_notify) = NULL; /* append to socket */ control = sctp_build_readq_entry(stcb, stcb->asoc.primary_destination, 0, 0, 0, 0, 0, 0, m_notify); if (control == NULL) { /* no memory */ sctp_m_freem(m_notify); return; } control->spec_flags = M_NOTIFICATION; control->length = SCTP_BUF_LEN(m_notify); /* not that we need this */ control->tail_mbuf = m_notify; sctp_add_to_readq(stcb->sctp_ep, stcb, control, &stcb->sctp_socket->so_rcv, 1, SCTP_READ_LOCK_NOT_HELD, SCTP_SO_NOT_LOCKED); } static void sctp_notify_sender_dry_event(struct sctp_tcb *stcb, int so_locked #if !defined(__APPLE__) && !defined(SCTP_SO_LOCK_TESTING) SCTP_UNUSED #endif ) { struct mbuf *m_notify; struct sctp_sender_dry_event *event; struct sctp_queued_to_read *control; if (sctp_is_feature_off(stcb->sctp_ep, SCTP_PCB_FLAGS_DRYEVNT)) { /* event not enabled */ return; } m_notify = sctp_get_mbuf_for_msg(sizeof(struct sctp_sender_dry_event), 0, M_DONTWAIT, 1, MT_DATA); if (m_notify == NULL) { /* no space left */ return; } SCTP_BUF_LEN(m_notify) = 0; event = mtod(m_notify, struct sctp_sender_dry_event *); event->sender_dry_type = SCTP_SENDER_DRY_EVENT; event->sender_dry_flags = 0; event->sender_dry_length = sizeof(struct sctp_sender_dry_event); event->sender_dry_assoc_id = sctp_get_associd(stcb); SCTP_BUF_LEN(m_notify) = sizeof(struct sctp_sender_dry_event); SCTP_BUF_NEXT(m_notify) = NULL; /* append to socket */ control = sctp_build_readq_entry(stcb, stcb->asoc.primary_destination, 0, 0, 0, 0, 0, 0, m_notify); if (control == NULL) { /* no memory */ sctp_m_freem(m_notify); return; } control->length = SCTP_BUF_LEN(m_notify); control->spec_flags = M_NOTIFICATION; /* not that we need this */ control->tail_mbuf = m_notify; sctp_add_to_readq(stcb->sctp_ep, stcb, control, &stcb->sctp_socket->so_rcv, 1, SCTP_READ_LOCK_NOT_HELD, so_locked); } static void sctp_notify_stream_reset_add(struct sctp_tcb *stcb, int number_entries, int flag) { struct mbuf *m_notify; struct sctp_queued_to_read *control; struct sctp_stream_reset_event *strreset; int len; if (sctp_is_feature_off(stcb->sctp_ep, SCTP_PCB_FLAGS_STREAM_RESETEVNT)) { /* event not enabled */ return; } m_notify = sctp_get_mbuf_for_msg(MCLBYTES, 0, M_DONTWAIT, 1, MT_DATA); if (m_notify == NULL) /* no space left */ return; SCTP_BUF_LEN(m_notify) = 0; len = sizeof(struct sctp_stream_reset_event) + (number_entries * sizeof(uint16_t)); if (len > M_TRAILINGSPACE(m_notify)) { /* never enough room */ sctp_m_freem(m_notify); return; } strreset = mtod(m_notify, struct sctp_stream_reset_event *); strreset->strreset_type = SCTP_STREAM_RESET_EVENT; strreset->strreset_flags = SCTP_STRRESET_ADD_STREAM | flag; strreset->strreset_length = len; strreset->strreset_assoc_id = sctp_get_associd(stcb); strreset->strreset_list[0] = number_entries; SCTP_BUF_LEN(m_notify) = len; SCTP_BUF_NEXT(m_notify) = NULL; if (sctp_sbspace(&stcb->asoc, &stcb->sctp_socket->so_rcv) < SCTP_BUF_LEN(m_notify)) { /* no space */ sctp_m_freem(m_notify); return; } /* append to socket */ control = sctp_build_readq_entry(stcb, stcb->asoc.primary_destination, 0, 0, 0, 0, 0, 0, m_notify); if (control == NULL) { /* no memory */ sctp_m_freem(m_notify); return; } control->spec_flags = M_NOTIFICATION; control->length = SCTP_BUF_LEN(m_notify); /* not that we need this */ control->tail_mbuf = m_notify; sctp_add_to_readq(stcb->sctp_ep, stcb, control, &stcb->sctp_socket->so_rcv, 1, SCTP_READ_LOCK_NOT_HELD, SCTP_SO_NOT_LOCKED); } static void sctp_notify_stream_reset(struct sctp_tcb *stcb, int number_entries, uint16_t * list, int flag) { struct mbuf *m_notify; struct sctp_queued_to_read *control; struct sctp_stream_reset_event *strreset; int len; if (sctp_is_feature_off(stcb->sctp_ep, SCTP_PCB_FLAGS_STREAM_RESETEVNT)) { /* event not enabled */ return; } m_notify = sctp_get_mbuf_for_msg(MCLBYTES, 0, M_DONTWAIT, 1, MT_DATA); if (m_notify == NULL) /* no space left */ return; SCTP_BUF_LEN(m_notify) = 0; len = sizeof(struct sctp_stream_reset_event) + (number_entries * sizeof(uint16_t)); if (len > M_TRAILINGSPACE(m_notify)) { /* never enough room */ sctp_m_freem(m_notify); return; } strreset = mtod(m_notify, struct sctp_stream_reset_event *); strreset->strreset_type = SCTP_STREAM_RESET_EVENT; if (number_entries == 0) { strreset->strreset_flags = flag | SCTP_STRRESET_ALL_STREAMS; } else { strreset->strreset_flags = flag | SCTP_STRRESET_STREAM_LIST; } strreset->strreset_length = len; strreset->strreset_assoc_id = sctp_get_associd(stcb); if (number_entries) { int i; for (i = 0; i < number_entries; i++) { strreset->strreset_list[i] = ntohs(list[i]); } } SCTP_BUF_LEN(m_notify) = len; SCTP_BUF_NEXT(m_notify) = NULL; if (sctp_sbspace(&stcb->asoc, &stcb->sctp_socket->so_rcv) < SCTP_BUF_LEN(m_notify)) { /* no space */ sctp_m_freem(m_notify); return; } /* append to socket */ control = sctp_build_readq_entry(stcb, stcb->asoc.primary_destination, 0, 0, 0, 0, 0, 0, m_notify); if (control == NULL) { /* no memory */ sctp_m_freem(m_notify); return; } control->spec_flags = M_NOTIFICATION; control->length = SCTP_BUF_LEN(m_notify); /* not that we need this */ control->tail_mbuf = m_notify; sctp_add_to_readq(stcb->sctp_ep, stcb, control, &stcb->sctp_socket->so_rcv, 1, SCTP_READ_LOCK_NOT_HELD, SCTP_SO_NOT_LOCKED); } void sctp_ulp_notify(uint32_t notification, struct sctp_tcb *stcb, uint32_t error, void *data, int so_locked #if !defined(__APPLE__) && !defined(SCTP_SO_LOCK_TESTING) SCTP_UNUSED #endif ) { if ((stcb == NULL) || (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) || (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_SOCKET_ALLGONE) || (stcb->asoc.state & SCTP_STATE_CLOSED_SOCKET)) { /* If the socket is gone we are out of here */ return; } if (stcb->sctp_socket->so_rcv.sb_state & SBS_CANTRCVMORE) { return; } if (stcb && ((stcb->asoc.state & SCTP_STATE_COOKIE_WAIT) || (stcb->asoc.state & SCTP_STATE_COOKIE_ECHOED))) { if ((notification == SCTP_NOTIFY_INTERFACE_DOWN) || (notification == SCTP_NOTIFY_INTERFACE_UP) || (notification == SCTP_NOTIFY_INTERFACE_CONFIRMED)) { /* Don't report these in front states */ return; } } switch (notification) { case SCTP_NOTIFY_ASSOC_UP: if (stcb->asoc.assoc_up_sent == 0) { sctp_notify_assoc_change(SCTP_COMM_UP, stcb, error, NULL, so_locked); stcb->asoc.assoc_up_sent = 1; } if (stcb->asoc.adaptation_needed && (stcb->asoc.adaptation_sent == 0)) { sctp_notify_adaptation_layer(stcb, error); } if (stcb->asoc.peer_supports_auth == 0) { sctp_ulp_notify(SCTP_NOTIFY_NO_PEER_AUTH, stcb, 0, NULL, so_locked); } break; case SCTP_NOTIFY_ASSOC_DOWN: sctp_notify_assoc_change(SCTP_SHUTDOWN_COMP, stcb, error, NULL, so_locked); break; case SCTP_NOTIFY_INTERFACE_DOWN: { struct sctp_nets *net; net = (struct sctp_nets *)data; sctp_notify_peer_addr_change(stcb, SCTP_ADDR_UNREACHABLE, (struct sockaddr *)&net->ro._l_addr, error); break; } case SCTP_NOTIFY_INTERFACE_UP: { struct sctp_nets *net; net = (struct sctp_nets *)data; sctp_notify_peer_addr_change(stcb, SCTP_ADDR_AVAILABLE, (struct sockaddr *)&net->ro._l_addr, error); break; } case SCTP_NOTIFY_INTERFACE_CONFIRMED: { struct sctp_nets *net; net = (struct sctp_nets *)data; sctp_notify_peer_addr_change(stcb, SCTP_ADDR_CONFIRMED, (struct sockaddr *)&net->ro._l_addr, error); break; } case SCTP_NOTIFY_SPECIAL_SP_FAIL: sctp_notify_send_failed2(stcb, error, (struct sctp_stream_queue_pending *)data, so_locked); break; case SCTP_NOTIFY_DG_FAIL: sctp_notify_send_failed(stcb, error, (struct sctp_tmit_chunk *)data, so_locked); break; case SCTP_NOTIFY_PARTIAL_DELVIERY_INDICATION: { uint32_t val; val = *((uint32_t *) data); sctp_notify_partial_delivery_indication(stcb, error, val, so_locked); break; } case SCTP_NOTIFY_STRDATA_ERR: break; case SCTP_NOTIFY_ASSOC_ABORTED: if ((stcb) && (((stcb->asoc.state & SCTP_STATE_MASK) == SCTP_STATE_COOKIE_WAIT) || ((stcb->asoc.state & SCTP_STATE_MASK) == SCTP_STATE_COOKIE_ECHOED))) { sctp_notify_assoc_change(SCTP_CANT_STR_ASSOC, stcb, error, NULL, so_locked); } else { sctp_notify_assoc_change(SCTP_COMM_LOST, stcb, error, NULL, so_locked); } break; case SCTP_NOTIFY_PEER_OPENED_STREAM: break; case SCTP_NOTIFY_STREAM_OPENED_OK: break; case SCTP_NOTIFY_ASSOC_RESTART: sctp_notify_assoc_change(SCTP_RESTART, stcb, error, data, so_locked); if (stcb->asoc.peer_supports_auth == 0) { sctp_ulp_notify(SCTP_NOTIFY_NO_PEER_AUTH, stcb, 0, NULL, so_locked); } break; case SCTP_NOTIFY_HB_RESP: break; case SCTP_NOTIFY_STR_RESET_INSTREAM_ADD_OK: sctp_notify_stream_reset_add(stcb, error, SCTP_STRRESET_INBOUND_STR); break; case SCTP_NOTIFY_STR_RESET_ADD_OK: sctp_notify_stream_reset_add(stcb, error, SCTP_STRRESET_OUTBOUND_STR); break; case SCTP_NOTIFY_STR_RESET_ADD_FAIL: sctp_notify_stream_reset_add(stcb, error, (SCTP_STRRESET_FAILED | SCTP_STRRESET_OUTBOUND_STR)); break; case SCTP_NOTIFY_STR_RESET_SEND: sctp_notify_stream_reset(stcb, error, ((uint16_t *) data), SCTP_STRRESET_OUTBOUND_STR); break; case SCTP_NOTIFY_STR_RESET_RECV: sctp_notify_stream_reset(stcb, error, ((uint16_t *) data), SCTP_STRRESET_INBOUND_STR); break; case SCTP_NOTIFY_STR_RESET_FAILED_OUT: sctp_notify_stream_reset(stcb, error, ((uint16_t *) data), (SCTP_STRRESET_OUTBOUND_STR | SCTP_STRRESET_FAILED)); break; case SCTP_NOTIFY_STR_RESET_FAILED_IN: sctp_notify_stream_reset(stcb, error, ((uint16_t *) data), (SCTP_STRRESET_INBOUND_STR | SCTP_STRRESET_FAILED)); break; case SCTP_NOTIFY_ASCONF_ADD_IP: sctp_notify_peer_addr_change(stcb, SCTP_ADDR_ADDED, data, error); break; case SCTP_NOTIFY_ASCONF_DELETE_IP: sctp_notify_peer_addr_change(stcb, SCTP_ADDR_REMOVED, data, error); break; case SCTP_NOTIFY_ASCONF_SET_PRIMARY: sctp_notify_peer_addr_change(stcb, SCTP_ADDR_MADE_PRIM, data, error); break; case SCTP_NOTIFY_ASCONF_SUCCESS: break; case SCTP_NOTIFY_ASCONF_FAILED: break; case SCTP_NOTIFY_PEER_SHUTDOWN: sctp_notify_shutdown_event(stcb); break; case SCTP_NOTIFY_AUTH_NEW_KEY: sctp_notify_authentication(stcb, SCTP_AUTH_NEWKEY, error, (uint16_t) (uintptr_t) data, so_locked); break; case SCTP_NOTIFY_AUTH_FREE_KEY: sctp_notify_authentication(stcb, SCTP_AUTH_FREE_KEY, error, (uint16_t) (uintptr_t) data, so_locked); break; case SCTP_NOTIFY_NO_PEER_AUTH: sctp_notify_authentication(stcb, SCTP_AUTH_NO_AUTH, error, (uint16_t) (uintptr_t) data, so_locked); break; case SCTP_NOTIFY_SENDER_DRY: sctp_notify_sender_dry_event(stcb, so_locked); break; default: SCTPDBG(SCTP_DEBUG_UTIL1, "%s: unknown notification %xh (%u)\n", __FUNCTION__, notification, notification); break; } /* end switch */ } void sctp_report_all_outbound(struct sctp_tcb *stcb, int holds_lock, int so_locked #if !defined(__APPLE__) && !defined(SCTP_SO_LOCK_TESTING) SCTP_UNUSED #endif ) { struct sctp_association *asoc; struct sctp_stream_out *outs; struct sctp_tmit_chunk *chk; struct sctp_stream_queue_pending *sp; int i; asoc = &stcb->asoc; if (stcb == NULL) { return; } if ((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) || (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_SOCKET_ALLGONE) || (stcb->asoc.state & SCTP_STATE_CLOSED_SOCKET)) { return; } /* now through all the gunk freeing chunks */ if (holds_lock == 0) { SCTP_TCB_SEND_LOCK(stcb); } /* sent queue SHOULD be empty */ if (!TAILQ_EMPTY(&asoc->sent_queue)) { chk = TAILQ_FIRST(&asoc->sent_queue); while (chk) { TAILQ_REMOVE(&asoc->sent_queue, chk, sctp_next); asoc->sent_queue_cnt--; if (chk->data != NULL) { sctp_free_bufspace(stcb, asoc, chk, 1); sctp_ulp_notify(SCTP_NOTIFY_DG_FAIL, stcb, SCTP_NOTIFY_DATAGRAM_SENT, chk, so_locked); if (chk->data) { sctp_m_freem(chk->data); chk->data = NULL; } } sctp_free_a_chunk(stcb, chk); /* sa_ignore FREED_MEMORY */ chk = TAILQ_FIRST(&asoc->sent_queue); } } /* pending send queue SHOULD be empty */ if (!TAILQ_EMPTY(&asoc->send_queue)) { chk = TAILQ_FIRST(&asoc->send_queue); while (chk) { TAILQ_REMOVE(&asoc->send_queue, chk, sctp_next); asoc->send_queue_cnt--; if (chk->data != NULL) { sctp_free_bufspace(stcb, asoc, chk, 1); sctp_ulp_notify(SCTP_NOTIFY_DG_FAIL, stcb, SCTP_NOTIFY_DATAGRAM_UNSENT, chk, so_locked); if (chk->data) { sctp_m_freem(chk->data); chk->data = NULL; } } sctp_free_a_chunk(stcb, chk); /* sa_ignore FREED_MEMORY */ chk = TAILQ_FIRST(&asoc->send_queue); } } for (i = 0; i < stcb->asoc.streamoutcnt; i++) { /* For each stream */ outs = &stcb->asoc.strmout[i]; /* clean up any sends there */ stcb->asoc.locked_on_sending = NULL; sp = TAILQ_FIRST(&outs->outqueue); while (sp) { stcb->asoc.stream_queue_cnt--; TAILQ_REMOVE(&outs->outqueue, sp, next); sctp_free_spbufspace(stcb, asoc, sp); sctp_ulp_notify(SCTP_NOTIFY_SPECIAL_SP_FAIL, stcb, SCTP_NOTIFY_DATAGRAM_UNSENT, (void *)sp, so_locked); if (sp->data) { sctp_m_freem(sp->data); sp->data = NULL; } if (sp->net) sctp_free_remote_addr(sp->net); sp->net = NULL; /* Free the chunk */ sctp_free_a_strmoq(stcb, sp); /* sa_ignore FREED_MEMORY */ sp = TAILQ_FIRST(&outs->outqueue); } } if (holds_lock == 0) { SCTP_TCB_SEND_UNLOCK(stcb); } } void sctp_abort_notification(struct sctp_tcb *stcb, int error, int so_locked #if !defined(__APPLE__) && !defined(SCTP_SO_LOCK_TESTING) SCTP_UNUSED #endif ) { if (stcb == NULL) { return; } if ((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) || (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_SOCKET_ALLGONE) || (stcb->asoc.state & SCTP_STATE_CLOSED_SOCKET)) { return; } /* Tell them we lost the asoc */ sctp_report_all_outbound(stcb, 1, so_locked); if ((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL) || ((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) && (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_CONNECTED))) { stcb->sctp_ep->sctp_flags |= SCTP_PCB_FLAGS_WAS_ABORTED; } sctp_ulp_notify(SCTP_NOTIFY_ASSOC_ABORTED, stcb, error, NULL, so_locked); } void sctp_abort_association(struct sctp_inpcb *inp, struct sctp_tcb *stcb, struct mbuf *m, int iphlen, struct sctphdr *sh, struct mbuf *op_err, uint32_t vrf_id, uint16_t port) { uint32_t vtag; #if defined (__APPLE__) || defined(SCTP_SO_LOCK_TESTING) struct socket *so; #endif vtag = 0; if (stcb != NULL) { /* We have a TCB to abort, send notification too */ vtag = stcb->asoc.peer_vtag; sctp_abort_notification(stcb, 0, SCTP_SO_NOT_LOCKED); /* get the assoc vrf id and table id */ vrf_id = stcb->asoc.vrf_id; stcb->asoc.state |= SCTP_STATE_WAS_ABORTED; } sctp_send_abort(m, iphlen, sh, vtag, op_err, vrf_id, port); if (stcb != NULL) { /* Ok, now lets free it */ #if defined (__APPLE__) || defined(SCTP_SO_LOCK_TESTING) so = SCTP_INP_SO(inp); atomic_add_int(&stcb->asoc.refcnt, 1); SCTP_TCB_UNLOCK(stcb); SCTP_SOCKET_LOCK(so, 1); SCTP_TCB_LOCK(stcb); atomic_subtract_int(&stcb->asoc.refcnt, 1); #endif (void)sctp_free_assoc(inp, stcb, SCTP_NORMAL_PROC, SCTP_FROM_SCTPUTIL + SCTP_LOC_4); #if defined (__APPLE__) || defined(SCTP_SO_LOCK_TESTING) SCTP_SOCKET_UNLOCK(so, 1); #endif } else { if (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) { if (LIST_FIRST(&inp->sctp_asoc_list) == NULL) { sctp_inpcb_free(inp, SCTP_FREE_SHOULD_USE_ABORT, SCTP_CALLED_DIRECTLY_NOCMPSET); } } } } #ifdef SCTP_ASOCLOG_OF_TSNS void sctp_print_out_track_log(struct sctp_tcb *stcb) { #ifdef NOSIY_PRINTS int i; SCTP_PRINTF("Last ep reason:%x\n", stcb->sctp_ep->last_abort_code); SCTP_PRINTF("IN bound TSN log-aaa\n"); if ((stcb->asoc.tsn_in_at == 0) && (stcb->asoc.tsn_in_wrapped == 0)) { SCTP_PRINTF("None rcvd\n"); goto none_in; } if (stcb->asoc.tsn_in_wrapped) { for (i = stcb->asoc.tsn_in_at; i < SCTP_TSN_LOG_SIZE; i++) { SCTP_PRINTF("TSN:%x strm:%d seq:%d flags:%x sz:%d\n", stcb->asoc.in_tsnlog[i].tsn, stcb->asoc.in_tsnlog[i].strm, stcb->asoc.in_tsnlog[i].seq, stcb->asoc.in_tsnlog[i].flgs, stcb->asoc.in_tsnlog[i].sz); } } if (stcb->asoc.tsn_in_at) { for (i = 0; i < stcb->asoc.tsn_in_at; i++) { SCTP_PRINTF("TSN:%x strm:%d seq:%d flags:%x sz:%d\n", stcb->asoc.in_tsnlog[i].tsn, stcb->asoc.in_tsnlog[i].strm, stcb->asoc.in_tsnlog[i].seq, stcb->asoc.in_tsnlog[i].flgs, stcb->asoc.in_tsnlog[i].sz); } } none_in: SCTP_PRINTF("OUT bound TSN log-aaa\n"); if ((stcb->asoc.tsn_out_at == 0) && (stcb->asoc.tsn_out_wrapped == 0)) { SCTP_PRINTF("None sent\n"); } if (stcb->asoc.tsn_out_wrapped) { for (i = stcb->asoc.tsn_out_at; i < SCTP_TSN_LOG_SIZE; i++) { SCTP_PRINTF("TSN:%x strm:%d seq:%d flags:%x sz:%d\n", stcb->asoc.out_tsnlog[i].tsn, stcb->asoc.out_tsnlog[i].strm, stcb->asoc.out_tsnlog[i].seq, stcb->asoc.out_tsnlog[i].flgs, stcb->asoc.out_tsnlog[i].sz); } } if (stcb->asoc.tsn_out_at) { for (i = 0; i < stcb->asoc.tsn_out_at; i++) { SCTP_PRINTF("TSN:%x strm:%d seq:%d flags:%x sz:%d\n", stcb->asoc.out_tsnlog[i].tsn, stcb->asoc.out_tsnlog[i].strm, stcb->asoc.out_tsnlog[i].seq, stcb->asoc.out_tsnlog[i].flgs, stcb->asoc.out_tsnlog[i].sz); } } #endif } #endif void sctp_abort_an_association(struct sctp_inpcb *inp, struct sctp_tcb *stcb, int error, struct mbuf *op_err, int so_locked #if !defined(__APPLE__) && !defined(SCTP_SO_LOCK_TESTING) SCTP_UNUSED #endif ) { uint32_t vtag; #if defined (__APPLE__) || defined(SCTP_SO_LOCK_TESTING) struct socket *so; #endif #if defined (__APPLE__) || defined(SCTP_SO_LOCK_TESTING) so = SCTP_INP_SO(inp); #endif if (stcb == NULL) { /* Got to have a TCB */ if (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) { if (LIST_FIRST(&inp->sctp_asoc_list) == NULL) { sctp_inpcb_free(inp, SCTP_FREE_SHOULD_USE_ABORT, SCTP_CALLED_DIRECTLY_NOCMPSET); } } return; } else { stcb->asoc.state |= SCTP_STATE_WAS_ABORTED; } vtag = stcb->asoc.peer_vtag; /* notify the ulp */ if ((inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) == 0) sctp_abort_notification(stcb, error, so_locked); /* notify the peer */ #if defined(SCTP_PANIC_ON_ABORT) panic("aborting an association"); #endif sctp_send_abort_tcb(stcb, op_err, so_locked); SCTP_STAT_INCR_COUNTER32(sctps_aborted); if ((SCTP_GET_STATE(&stcb->asoc) == SCTP_STATE_OPEN) || (SCTP_GET_STATE(&stcb->asoc) == SCTP_STATE_SHUTDOWN_RECEIVED)) { SCTP_STAT_DECR_GAUGE32(sctps_currestab); } /* now free the asoc */ #ifdef SCTP_ASOCLOG_OF_TSNS sctp_print_out_track_log(stcb); #endif #if defined (__APPLE__) || defined(SCTP_SO_LOCK_TESTING) if (!so_locked) { atomic_add_int(&stcb->asoc.refcnt, 1); SCTP_TCB_UNLOCK(stcb); SCTP_SOCKET_LOCK(so, 1); SCTP_TCB_LOCK(stcb); atomic_subtract_int(&stcb->asoc.refcnt, 1); } #endif (void)sctp_free_assoc(inp, stcb, SCTP_NORMAL_PROC, SCTP_FROM_SCTPUTIL + SCTP_LOC_5); #if defined (__APPLE__) || defined(SCTP_SO_LOCK_TESTING) if (!so_locked) { SCTP_SOCKET_UNLOCK(so, 1); } #endif } void sctp_handle_ootb(struct mbuf *m, int iphlen, int offset, struct sctphdr *sh, struct sctp_inpcb *inp, struct mbuf *op_err, uint32_t vrf_id, uint16_t port) { struct sctp_chunkhdr *ch, chunk_buf; unsigned int chk_length; SCTP_STAT_INCR_COUNTER32(sctps_outoftheblue); /* Generate a TO address for future reference */ if (inp && (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE)) { if (LIST_FIRST(&inp->sctp_asoc_list) == NULL) { sctp_inpcb_free(inp, SCTP_FREE_SHOULD_USE_ABORT, SCTP_CALLED_DIRECTLY_NOCMPSET); } } ch = (struct sctp_chunkhdr *)sctp_m_getptr(m, offset, sizeof(*ch), (uint8_t *) & chunk_buf); while (ch != NULL) { chk_length = ntohs(ch->chunk_length); if (chk_length < sizeof(*ch)) { /* break to abort land */ break; } switch (ch->chunk_type) { case SCTP_COOKIE_ECHO: /* We hit here only if the assoc is being freed */ return; case SCTP_PACKET_DROPPED: /* we don't respond to pkt-dropped */ return; case SCTP_ABORT_ASSOCIATION: /* we don't respond with an ABORT to an ABORT */ return; case SCTP_SHUTDOWN_COMPLETE: /* * we ignore it since we are not waiting for it and * peer is gone */ return; case SCTP_SHUTDOWN_ACK: sctp_send_shutdown_complete2(m, iphlen, sh, vrf_id, port); return; default: break; } offset += SCTP_SIZE32(chk_length); ch = (struct sctp_chunkhdr *)sctp_m_getptr(m, offset, sizeof(*ch), (uint8_t *) & chunk_buf); } sctp_send_abort(m, iphlen, sh, 0, op_err, vrf_id, port); } /* * check the inbound datagram to make sure there is not an abort inside it, * if there is return 1, else return 0. */ int sctp_is_there_an_abort_here(struct mbuf *m, int iphlen, uint32_t * vtagfill) { struct sctp_chunkhdr *ch; struct sctp_init_chunk *init_chk, chunk_buf; int offset; unsigned int chk_length; offset = iphlen + sizeof(struct sctphdr); ch = (struct sctp_chunkhdr *)sctp_m_getptr(m, offset, sizeof(*ch), (uint8_t *) & chunk_buf); while (ch != NULL) { chk_length = ntohs(ch->chunk_length); if (chk_length < sizeof(*ch)) { /* packet is probably corrupt */ break; } /* we seem to be ok, is it an abort? */ if (ch->chunk_type == SCTP_ABORT_ASSOCIATION) { /* yep, tell them */ return (1); } if (ch->chunk_type == SCTP_INITIATION) { /* need to update the Vtag */ init_chk = (struct sctp_init_chunk *)sctp_m_getptr(m, offset, sizeof(*init_chk), (uint8_t *) & chunk_buf); if (init_chk != NULL) { *vtagfill = ntohl(init_chk->init.initiate_tag); } } /* Nope, move to the next chunk */ offset += SCTP_SIZE32(chk_length); ch = (struct sctp_chunkhdr *)sctp_m_getptr(m, offset, sizeof(*ch), (uint8_t *) & chunk_buf); } return (0); } /* * currently (2/02), ifa_addr embeds scope_id's and don't have sin6_scope_id * set (i.e. it's 0) so, create this function to compare link local scopes */ #ifdef INET6 uint32_t sctp_is_same_scope(struct sockaddr_in6 *addr1, struct sockaddr_in6 *addr2) { struct sockaddr_in6 a, b; /* save copies */ a = *addr1; b = *addr2; if (a.sin6_scope_id == 0) if (sa6_recoverscope(&a)) { /* can't get scope, so can't match */ return (0); } if (b.sin6_scope_id == 0) if (sa6_recoverscope(&b)) { /* can't get scope, so can't match */ return (0); } if (a.sin6_scope_id != b.sin6_scope_id) return (0); return (1); } /* * returns a sockaddr_in6 with embedded scope recovered and removed */ struct sockaddr_in6 * sctp_recover_scope(struct sockaddr_in6 *addr, struct sockaddr_in6 *store) { /* check and strip embedded scope junk */ if (addr->sin6_family == AF_INET6) { if (IN6_IS_SCOPE_LINKLOCAL(&addr->sin6_addr)) { if (addr->sin6_scope_id == 0) { *store = *addr; if (!sa6_recoverscope(store)) { /* use the recovered scope */ addr = store; } } else { /* else, return the original "to" addr */ in6_clearscope(&addr->sin6_addr); } } } return (addr); } #endif /* * are the two addresses the same? currently a "scopeless" check returns: 1 * if same, 0 if not */ int sctp_cmpaddr(struct sockaddr *sa1, struct sockaddr *sa2) { /* must be valid */ if (sa1 == NULL || sa2 == NULL) return (0); /* must be the same family */ if (sa1->sa_family != sa2->sa_family) return (0); switch (sa1->sa_family) { #ifdef INET6 case AF_INET6: { /* IPv6 addresses */ struct sockaddr_in6 *sin6_1, *sin6_2; sin6_1 = (struct sockaddr_in6 *)sa1; sin6_2 = (struct sockaddr_in6 *)sa2; return (SCTP6_ARE_ADDR_EQUAL(sin6_1, sin6_2)); } #endif case AF_INET: { /* IPv4 addresses */ struct sockaddr_in *sin_1, *sin_2; sin_1 = (struct sockaddr_in *)sa1; sin_2 = (struct sockaddr_in *)sa2; return (sin_1->sin_addr.s_addr == sin_2->sin_addr.s_addr); } default: /* we don't do these... */ return (0); } } void sctp_print_address(struct sockaddr *sa) { #ifdef INET6 char ip6buf[INET6_ADDRSTRLEN]; ip6buf[0] = 0; #endif switch (sa->sa_family) { #ifdef INET6 case AF_INET6: { struct sockaddr_in6 *sin6; sin6 = (struct sockaddr_in6 *)sa; SCTP_PRINTF("IPv6 address: %s:port:%d scope:%u\n", ip6_sprintf(ip6buf, &sin6->sin6_addr), ntohs(sin6->sin6_port), sin6->sin6_scope_id); break; } #endif case AF_INET: { struct sockaddr_in *sin; unsigned char *p; sin = (struct sockaddr_in *)sa; p = (unsigned char *)&sin->sin_addr; SCTP_PRINTF("IPv4 address: %u.%u.%u.%u:%d\n", p[0], p[1], p[2], p[3], ntohs(sin->sin_port)); break; } default: SCTP_PRINTF("?\n"); break; } } void sctp_print_address_pkt(struct ip *iph, struct sctphdr *sh) { switch (iph->ip_v) { case IPVERSION: { struct sockaddr_in lsa, fsa; bzero(&lsa, sizeof(lsa)); lsa.sin_len = sizeof(lsa); lsa.sin_family = AF_INET; lsa.sin_addr = iph->ip_src; lsa.sin_port = sh->src_port; bzero(&fsa, sizeof(fsa)); fsa.sin_len = sizeof(fsa); fsa.sin_family = AF_INET; fsa.sin_addr = iph->ip_dst; fsa.sin_port = sh->dest_port; SCTP_PRINTF("src: "); sctp_print_address((struct sockaddr *)&lsa); SCTP_PRINTF("dest: "); sctp_print_address((struct sockaddr *)&fsa); break; } #ifdef INET6 case IPV6_VERSION >> 4: { struct ip6_hdr *ip6; struct sockaddr_in6 lsa6, fsa6; ip6 = (struct ip6_hdr *)iph; bzero(&lsa6, sizeof(lsa6)); lsa6.sin6_len = sizeof(lsa6); lsa6.sin6_family = AF_INET6; lsa6.sin6_addr = ip6->ip6_src; lsa6.sin6_port = sh->src_port; bzero(&fsa6, sizeof(fsa6)); fsa6.sin6_len = sizeof(fsa6); fsa6.sin6_family = AF_INET6; fsa6.sin6_addr = ip6->ip6_dst; fsa6.sin6_port = sh->dest_port; SCTP_PRINTF("src: "); sctp_print_address((struct sockaddr *)&lsa6); SCTP_PRINTF("dest: "); sctp_print_address((struct sockaddr *)&fsa6); break; } #endif default: /* TSNH */ break; } } void sctp_pull_off_control_to_new_inp(struct sctp_inpcb *old_inp, struct sctp_inpcb *new_inp, struct sctp_tcb *stcb, int waitflags) { /* * go through our old INP and pull off any control structures that * belong to stcb and move then to the new inp. */ struct socket *old_so, *new_so; struct sctp_queued_to_read *control, *nctl; struct sctp_readhead tmp_queue; struct mbuf *m; int error = 0; old_so = old_inp->sctp_socket; new_so = new_inp->sctp_socket; TAILQ_INIT(&tmp_queue); error = sblock(&old_so->so_rcv, waitflags); if (error) { /* * Gak, can't get sblock, we have a problem. data will be * left stranded.. and we don't dare look at it since the * other thread may be reading something. Oh well, its a * screwed up app that does a peeloff OR a accept while * reading from the main socket... actually its only the * peeloff() case, since I think read will fail on a * listening socket.. */ return; } /* lock the socket buffers */ SCTP_INP_READ_LOCK(old_inp); control = TAILQ_FIRST(&old_inp->read_queue); /* Pull off all for out target stcb */ while (control) { nctl = TAILQ_NEXT(control, next); if (control->stcb == stcb) { /* remove it we want it */ TAILQ_REMOVE(&old_inp->read_queue, control, next); TAILQ_INSERT_TAIL(&tmp_queue, control, next); m = control->data; while (m) { if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_SB_LOGGING_ENABLE) { sctp_sblog(&old_so->so_rcv, control->do_not_ref_stcb ? NULL : stcb, SCTP_LOG_SBFREE, SCTP_BUF_LEN(m)); } sctp_sbfree(control, stcb, &old_so->so_rcv, m); if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_SB_LOGGING_ENABLE) { sctp_sblog(&old_so->so_rcv, control->do_not_ref_stcb ? NULL : stcb, SCTP_LOG_SBRESULT, 0); } m = SCTP_BUF_NEXT(m); } } control = nctl; } SCTP_INP_READ_UNLOCK(old_inp); /* Remove the sb-lock on the old socket */ sbunlock(&old_so->so_rcv); /* Now we move them over to the new socket buffer */ control = TAILQ_FIRST(&tmp_queue); SCTP_INP_READ_LOCK(new_inp); while (control) { nctl = TAILQ_NEXT(control, next); TAILQ_INSERT_TAIL(&new_inp->read_queue, control, next); m = control->data; while (m) { if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_SB_LOGGING_ENABLE) { sctp_sblog(&new_so->so_rcv, control->do_not_ref_stcb ? NULL : stcb, SCTP_LOG_SBALLOC, SCTP_BUF_LEN(m)); } sctp_sballoc(stcb, &new_so->so_rcv, m); if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_SB_LOGGING_ENABLE) { sctp_sblog(&new_so->so_rcv, control->do_not_ref_stcb ? NULL : stcb, SCTP_LOG_SBRESULT, 0); } m = SCTP_BUF_NEXT(m); } control = nctl; } SCTP_INP_READ_UNLOCK(new_inp); } void sctp_add_to_readq(struct sctp_inpcb *inp, struct sctp_tcb *stcb, struct sctp_queued_to_read *control, struct sockbuf *sb, int end, int inp_read_lock_held, int so_locked #if !defined(__APPLE__) && !defined(SCTP_SO_LOCK_TESTING) SCTP_UNUSED #endif ) { /* * Here we must place the control on the end of the socket read * queue AND increment sb_cc so that select will work properly on * read. */ struct mbuf *m, *prev = NULL; if (inp == NULL) { /* Gak, TSNH!! */ #ifdef INVARIANTS panic("Gak, inp NULL on add_to_readq"); #endif return; } if (inp_read_lock_held == 0) SCTP_INP_READ_LOCK(inp); if (!(control->spec_flags & M_NOTIFICATION)) { atomic_add_int(&inp->total_recvs, 1); if (!control->do_not_ref_stcb) { atomic_add_int(&stcb->total_recvs, 1); } } m = control->data; control->held_length = 0; control->length = 0; while (m) { if (SCTP_BUF_LEN(m) == 0) { /* Skip mbufs with NO length */ if (prev == NULL) { /* First one */ control->data = sctp_m_free(m); m = control->data; } else { SCTP_BUF_NEXT(prev) = sctp_m_free(m); m = SCTP_BUF_NEXT(prev); } if (m == NULL) { control->tail_mbuf = prev;; } continue; } prev = m; if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_SB_LOGGING_ENABLE) { sctp_sblog(sb, control->do_not_ref_stcb ? NULL : stcb, SCTP_LOG_SBALLOC, SCTP_BUF_LEN(m)); } sctp_sballoc(stcb, sb, m); if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_SB_LOGGING_ENABLE) { sctp_sblog(sb, control->do_not_ref_stcb ? NULL : stcb, SCTP_LOG_SBRESULT, 0); } atomic_add_int(&control->length, SCTP_BUF_LEN(m)); m = SCTP_BUF_NEXT(m); } if (prev != NULL) { control->tail_mbuf = prev; } else { /* Everything got collapsed out?? */ if (inp_read_lock_held == 0) SCTP_INP_READ_UNLOCK(inp); return; } if (end) { control->end_added = 1; } TAILQ_INSERT_TAIL(&inp->read_queue, control, next); if (inp_read_lock_held == 0) SCTP_INP_READ_UNLOCK(inp); if (inp && inp->sctp_socket) { if (sctp_is_feature_on(inp, SCTP_PCB_FLAGS_ZERO_COPY_ACTIVE)) { SCTP_ZERO_COPY_EVENT(inp, inp->sctp_socket); } else { #if defined (__APPLE__) || defined(SCTP_SO_LOCK_TESTING) struct socket *so; so = SCTP_INP_SO(inp); if (!so_locked) { atomic_add_int(&stcb->asoc.refcnt, 1); SCTP_TCB_UNLOCK(stcb); SCTP_SOCKET_LOCK(so, 1); SCTP_TCB_LOCK(stcb); atomic_subtract_int(&stcb->asoc.refcnt, 1); if (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) { SCTP_SOCKET_UNLOCK(so, 1); return; } } #endif sctp_sorwakeup(inp, inp->sctp_socket); #if defined (__APPLE__) || defined(SCTP_SO_LOCK_TESTING) if (!so_locked) { SCTP_SOCKET_UNLOCK(so, 1); } #endif } } } int sctp_append_to_readq(struct sctp_inpcb *inp, struct sctp_tcb *stcb, struct sctp_queued_to_read *control, struct mbuf *m, int end, int ctls_cumack, struct sockbuf *sb) { /* * A partial delivery API event is underway. OR we are appending on * the reassembly queue. * * If PDAPI this means we need to add m to the end of the data. * Increase the length in the control AND increment the sb_cc. * Otherwise sb is NULL and all we need to do is put it at the end * of the mbuf chain. */ int len = 0; struct mbuf *mm, *tail = NULL, *prev = NULL; if (inp) { SCTP_INP_READ_LOCK(inp); } if (control == NULL) { get_out: if (inp) { SCTP_INP_READ_UNLOCK(inp); } return (-1); } if (control->end_added) { /* huh this one is complete? */ goto get_out; } mm = m; if (mm == NULL) { goto get_out; } while (mm) { if (SCTP_BUF_LEN(mm) == 0) { /* Skip mbufs with NO lenght */ if (prev == NULL) { /* First one */ m = sctp_m_free(mm); mm = m; } else { SCTP_BUF_NEXT(prev) = sctp_m_free(mm); mm = SCTP_BUF_NEXT(prev); } continue; } prev = mm; len += SCTP_BUF_LEN(mm); if (sb) { if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_SB_LOGGING_ENABLE) { sctp_sblog(sb, control->do_not_ref_stcb ? NULL : stcb, SCTP_LOG_SBALLOC, SCTP_BUF_LEN(mm)); } sctp_sballoc(stcb, sb, mm); if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_SB_LOGGING_ENABLE) { sctp_sblog(sb, control->do_not_ref_stcb ? NULL : stcb, SCTP_LOG_SBRESULT, 0); } } mm = SCTP_BUF_NEXT(mm); } if (prev) { tail = prev; } else { /* Really there should always be a prev */ if (m == NULL) { /* Huh nothing left? */ #ifdef INVARIANTS panic("Nothing left to add?"); #else goto get_out; #endif } tail = m; } if (control->tail_mbuf) { /* append */ SCTP_BUF_NEXT(control->tail_mbuf) = m; control->tail_mbuf = tail; } else { /* nothing there */ #ifdef INVARIANTS if (control->data != NULL) { panic("This should NOT happen"); } #endif control->data = m; control->tail_mbuf = tail; } atomic_add_int(&control->length, len); if (end) { /* message is complete */ if (stcb && (control == stcb->asoc.control_pdapi)) { stcb->asoc.control_pdapi = NULL; } control->held_length = 0; control->end_added = 1; } if (stcb == NULL) { control->do_not_ref_stcb = 1; } /* * When we are appending in partial delivery, the cum-ack is used * for the actual pd-api highest tsn on this mbuf. The true cum-ack * is populated in the outbound sinfo structure from the true cumack * if the association exists... */ control->sinfo_tsn = control->sinfo_cumtsn = ctls_cumack; if (inp) { SCTP_INP_READ_UNLOCK(inp); } if (inp && inp->sctp_socket) { if (sctp_is_feature_on(inp, SCTP_PCB_FLAGS_ZERO_COPY_ACTIVE)) { SCTP_ZERO_COPY_EVENT(inp, inp->sctp_socket); } else { #if defined (__APPLE__) || defined(SCTP_SO_LOCK_TESTING) struct socket *so; so = SCTP_INP_SO(inp); atomic_add_int(&stcb->asoc.refcnt, 1); SCTP_TCB_UNLOCK(stcb); SCTP_SOCKET_LOCK(so, 1); SCTP_TCB_LOCK(stcb); atomic_subtract_int(&stcb->asoc.refcnt, 1); if (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) { SCTP_SOCKET_UNLOCK(so, 1); return (0); } #endif sctp_sorwakeup(inp, inp->sctp_socket); #if defined (__APPLE__) || defined(SCTP_SO_LOCK_TESTING) SCTP_SOCKET_UNLOCK(so, 1); #endif } } return (0); } /*************HOLD THIS COMMENT FOR PATCH FILE OF *************ALTERNATE ROUTING CODE */ /*************HOLD THIS COMMENT FOR END OF PATCH FILE OF *************ALTERNATE ROUTING CODE */ struct mbuf * sctp_generate_invmanparam(int err) { /* Return a MBUF with a invalid mandatory parameter */ struct mbuf *m; m = sctp_get_mbuf_for_msg(sizeof(struct sctp_paramhdr), 0, M_DONTWAIT, 1, MT_DATA); if (m) { struct sctp_paramhdr *ph; SCTP_BUF_LEN(m) = sizeof(struct sctp_paramhdr); ph = mtod(m, struct sctp_paramhdr *); ph->param_length = htons(sizeof(struct sctp_paramhdr)); ph->param_type = htons(err); } return (m); } #ifdef SCTP_MBCNT_LOGGING void sctp_free_bufspace(struct sctp_tcb *stcb, struct sctp_association *asoc, struct sctp_tmit_chunk *tp1, int chk_cnt) { if (tp1->data == NULL) { return; } asoc->chunks_on_out_queue -= chk_cnt; if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_MBCNT_LOGGING_ENABLE) { sctp_log_mbcnt(SCTP_LOG_MBCNT_DECREASE, asoc->total_output_queue_size, tp1->book_size, 0, tp1->mbcnt); } if (asoc->total_output_queue_size >= tp1->book_size) { atomic_add_int(&asoc->total_output_queue_size, -tp1->book_size); } else { asoc->total_output_queue_size = 0; } if (stcb->sctp_socket && (((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL)) || ((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE)))) { if (stcb->sctp_socket->so_snd.sb_cc >= tp1->book_size) { stcb->sctp_socket->so_snd.sb_cc -= tp1->book_size; } else { stcb->sctp_socket->so_snd.sb_cc = 0; } } } #endif int sctp_release_pr_sctp_chunk(struct sctp_tcb *stcb, struct sctp_tmit_chunk *tp1, int reason, int so_locked #if !defined(__APPLE__) && !defined(SCTP_SO_LOCK_TESTING) SCTP_UNUSED #endif ) { struct sctp_stream_out *strq; struct sctp_tmit_chunk *chk = NULL; struct sctp_stream_queue_pending *sp; uint16_t stream = 0, seq = 0; uint8_t foundeom = 0; int ret_sz = 0; int notdone; int do_wakeup_routine = 0; stream = tp1->rec.data.stream_number; seq = tp1->rec.data.stream_seq; do { ret_sz += tp1->book_size; if (tp1->data != NULL) { if (tp1->sent < SCTP_DATAGRAM_RESEND) { sctp_flight_size_decrease(tp1); sctp_total_flight_decrease(stcb, tp1); } sctp_free_bufspace(stcb, &stcb->asoc, tp1, 1); stcb->asoc.peers_rwnd += tp1->send_size; stcb->asoc.peers_rwnd += SCTP_BASE_SYSCTL(sctp_peer_chunk_oh); sctp_ulp_notify(SCTP_NOTIFY_DG_FAIL, stcb, reason, tp1, so_locked); if (tp1->data) { sctp_m_freem(tp1->data); tp1->data = NULL; } do_wakeup_routine = 1; if (PR_SCTP_BUF_ENABLED(tp1->flags)) { stcb->asoc.sent_queue_cnt_removeable--; } } tp1->sent = SCTP_FORWARD_TSN_SKIP; if ((tp1->rec.data.rcv_flags & SCTP_DATA_NOT_FRAG) == SCTP_DATA_NOT_FRAG) { /* not frag'ed we ae done */ notdone = 0; foundeom = 1; } else if (tp1->rec.data.rcv_flags & SCTP_DATA_LAST_FRAG) { /* end of frag, we are done */ notdone = 0; foundeom = 1; } else { /* * Its a begin or middle piece, we must mark all of * it */ notdone = 1; tp1 = TAILQ_NEXT(tp1, sctp_next); } } while (tp1 && notdone); if (foundeom == 0) { /* * The multi-part message was scattered across the send and * sent queue. */ next_on_sent: tp1 = TAILQ_FIRST(&stcb->asoc.send_queue); /* * recurse throught the send_queue too, starting at the * beginning. */ if ((tp1) && (tp1->rec.data.stream_number == stream) && (tp1->rec.data.stream_seq == seq) ) { /* * save to chk in case we have some on stream out * queue. If so and we have an un-transmitted one we * don't have to fudge the TSN. */ chk = tp1; ret_sz += tp1->book_size; sctp_free_bufspace(stcb, &stcb->asoc, tp1, 1); sctp_ulp_notify(SCTP_NOTIFY_DG_FAIL, stcb, reason, tp1, so_locked); if (tp1->data) { sctp_m_freem(tp1->data); tp1->data = NULL; } /* No flight involved here book the size to 0 */ tp1->book_size = 0; if (tp1->rec.data.rcv_flags & SCTP_DATA_LAST_FRAG) { foundeom = 1; } do_wakeup_routine = 1; tp1->sent = SCTP_FORWARD_TSN_SKIP; TAILQ_REMOVE(&stcb->asoc.send_queue, tp1, sctp_next); /* * on to the sent queue so we can wait for it to be * passed by. */ TAILQ_INSERT_TAIL(&stcb->asoc.sent_queue, tp1, sctp_next); stcb->asoc.send_queue_cnt--; stcb->asoc.sent_queue_cnt++; goto next_on_sent; } } if (foundeom == 0) { /* * Still no eom found. That means there is stuff left on the * stream out queue.. yuck. */ strq = &stcb->asoc.strmout[stream]; SCTP_TCB_SEND_LOCK(stcb); sp = TAILQ_FIRST(&strq->outqueue); while (sp->strseq <= seq) { /* Check if its our SEQ */ if (sp->strseq == seq) { sp->discard_rest = 1; /* * We may need to put a chunk on the queue * that holds the TSN that would have been * sent with the LAST bit. */ if (chk == NULL) { /* Yep, we have to */ sctp_alloc_a_chunk(stcb, chk); if (chk == NULL) { /* * we are hosed. All we can * do is nothing.. which * will cause an abort if * the peer is paying * attention. */ goto oh_well; } memset(chk, 0, sizeof(*chk)); chk->rec.data.rcv_flags = SCTP_DATA_LAST_FRAG; chk->sent = SCTP_FORWARD_TSN_SKIP; chk->asoc = &stcb->asoc; chk->rec.data.stream_seq = sp->strseq; chk->rec.data.stream_number = sp->stream; chk->rec.data.payloadtype = sp->ppid; chk->rec.data.context = sp->context; chk->flags = sp->act_flags; chk->addr_over = sp->addr_over; chk->whoTo = sp->net; atomic_add_int(&chk->whoTo->ref_count, 1); chk->rec.data.TSN_seq = atomic_fetchadd_int(&stcb->asoc.sending_seq, 1); stcb->asoc.pr_sctp_cnt++; chk->pr_sctp_on = 1; TAILQ_INSERT_TAIL(&stcb->asoc.sent_queue, chk, sctp_next); stcb->asoc.sent_queue_cnt++; stcb->asoc.pr_sctp_cnt++; } else { chk->rec.data.rcv_flags |= SCTP_DATA_LAST_FRAG; } oh_well: if (sp->data) { /* * Pull any data to free up the SB * and allow sender to "add more" * whilc we will throw away :-) */ sctp_free_spbufspace(stcb, &stcb->asoc, sp); ret_sz += sp->length; do_wakeup_routine = 1; sp->some_taken = 1; sctp_m_freem(sp->data); sp->length = 0; sp->data = NULL; sp->tail_mbuf = NULL; } break; } else { /* Next one please */ sp = TAILQ_NEXT(sp, next); } } /* End while */ SCTP_TCB_SEND_UNLOCK(stcb); } if (do_wakeup_routine) { #if defined (__APPLE__) || defined(SCTP_SO_LOCK_TESTING) struct socket *so; so = SCTP_INP_SO(stcb->sctp_ep); if (!so_locked) { atomic_add_int(&stcb->asoc.refcnt, 1); SCTP_TCB_UNLOCK(stcb); SCTP_SOCKET_LOCK(so, 1); SCTP_TCB_LOCK(stcb); atomic_subtract_int(&stcb->asoc.refcnt, 1); if (stcb->asoc.state & SCTP_STATE_CLOSED_SOCKET) { /* assoc was freed while we were unlocked */ SCTP_SOCKET_UNLOCK(so, 1); return (ret_sz); } } #endif sctp_sowwakeup(stcb->sctp_ep, stcb->sctp_socket); #if defined (__APPLE__) || defined(SCTP_SO_LOCK_TESTING) if (!so_locked) { SCTP_SOCKET_UNLOCK(so, 1); } #endif } return (ret_sz); } /* * checks to see if the given address, sa, is one that is currently known by * the kernel note: can't distinguish the same address on multiple interfaces * and doesn't handle multiple addresses with different zone/scope id's note: * ifa_ifwithaddr() compares the entire sockaddr struct */ struct sctp_ifa * sctp_find_ifa_in_ep(struct sctp_inpcb *inp, struct sockaddr *addr, int holds_lock) { struct sctp_laddr *laddr; if (holds_lock == 0) { SCTP_INP_RLOCK(inp); } LIST_FOREACH(laddr, &inp->sctp_addr_list, sctp_nxt_addr) { if (laddr->ifa == NULL) continue; if (addr->sa_family != laddr->ifa->address.sa.sa_family) continue; if (addr->sa_family == AF_INET) { if (((struct sockaddr_in *)addr)->sin_addr.s_addr == laddr->ifa->address.sin.sin_addr.s_addr) { /* found him. */ if (holds_lock == 0) { SCTP_INP_RUNLOCK(inp); } return (laddr->ifa); break; } } #ifdef INET6 if (addr->sa_family == AF_INET6) { if (SCTP6_ARE_ADDR_EQUAL((struct sockaddr_in6 *)addr, &laddr->ifa->address.sin6)) { /* found him. */ if (holds_lock == 0) { SCTP_INP_RUNLOCK(inp); } return (laddr->ifa); break; } } #endif } if (holds_lock == 0) { SCTP_INP_RUNLOCK(inp); } return (NULL); } uint32_t sctp_get_ifa_hash_val(struct sockaddr *addr) { if (addr->sa_family == AF_INET) { struct sockaddr_in *sin; sin = (struct sockaddr_in *)addr; return (sin->sin_addr.s_addr ^ (sin->sin_addr.s_addr >> 16)); } else if (addr->sa_family == AF_INET6) { struct sockaddr_in6 *sin6; uint32_t hash_of_addr; sin6 = (struct sockaddr_in6 *)addr; hash_of_addr = (sin6->sin6_addr.s6_addr32[0] + sin6->sin6_addr.s6_addr32[1] + sin6->sin6_addr.s6_addr32[2] + sin6->sin6_addr.s6_addr32[3]); hash_of_addr = (hash_of_addr ^ (hash_of_addr >> 16)); return (hash_of_addr); } return (0); } struct sctp_ifa * sctp_find_ifa_by_addr(struct sockaddr *addr, uint32_t vrf_id, int holds_lock) { struct sctp_ifa *sctp_ifap; struct sctp_vrf *vrf; struct sctp_ifalist *hash_head; uint32_t hash_of_addr; if (holds_lock == 0) SCTP_IPI_ADDR_RLOCK(); vrf = sctp_find_vrf(vrf_id); if (vrf == NULL) { stage_right: if (holds_lock == 0) SCTP_IPI_ADDR_RUNLOCK(); return (NULL); } hash_of_addr = sctp_get_ifa_hash_val(addr); hash_head = &vrf->vrf_addr_hash[(hash_of_addr & vrf->vrf_addr_hashmark)]; if (hash_head == NULL) { SCTP_PRINTF("hash_of_addr:%x mask:%x table:%x - ", hash_of_addr, (uint32_t) vrf->vrf_addr_hashmark, (uint32_t) (hash_of_addr & vrf->vrf_addr_hashmark)); sctp_print_address(addr); SCTP_PRINTF("No such bucket for address\n"); if (holds_lock == 0) SCTP_IPI_ADDR_RUNLOCK(); return (NULL); } LIST_FOREACH(sctp_ifap, hash_head, next_bucket) { if (sctp_ifap == NULL) { #ifdef INVARIANTS panic("Huh LIST_FOREACH corrupt"); goto stage_right; #else SCTP_PRINTF("LIST corrupt of sctp_ifap's?\n"); goto stage_right; #endif } if (addr->sa_family != sctp_ifap->address.sa.sa_family) continue; if (addr->sa_family == AF_INET) { if (((struct sockaddr_in *)addr)->sin_addr.s_addr == sctp_ifap->address.sin.sin_addr.s_addr) { /* found him. */ if (holds_lock == 0) SCTP_IPI_ADDR_RUNLOCK(); return (sctp_ifap); break; } } #ifdef INET6 if (addr->sa_family == AF_INET6) { if (SCTP6_ARE_ADDR_EQUAL((struct sockaddr_in6 *)addr, &sctp_ifap->address.sin6)) { /* found him. */ if (holds_lock == 0) SCTP_IPI_ADDR_RUNLOCK(); return (sctp_ifap); break; } } #endif } if (holds_lock == 0) SCTP_IPI_ADDR_RUNLOCK(); return (NULL); } static void sctp_user_rcvd(struct sctp_tcb *stcb, uint32_t * freed_so_far, int hold_rlock, uint32_t rwnd_req) { /* User pulled some data, do we need a rwnd update? */ int r_unlocked = 0; uint32_t dif, rwnd; struct socket *so = NULL; if (stcb == NULL) return; atomic_add_int(&stcb->asoc.refcnt, 1); if (stcb->asoc.state & (SCTP_STATE_ABOUT_TO_BE_FREED | SCTP_STATE_SHUTDOWN_RECEIVED | SCTP_STATE_SHUTDOWN_ACK_SENT)) { /* Pre-check If we are freeing no update */ goto no_lock; } SCTP_INP_INCR_REF(stcb->sctp_ep); if ((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) || (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_SOCKET_ALLGONE)) { goto out; } so = stcb->sctp_socket; if (so == NULL) { goto out; } atomic_add_int(&stcb->freed_by_sorcv_sincelast, *freed_so_far); /* Have you have freed enough to look */ *freed_so_far = 0; /* Yep, its worth a look and the lock overhead */ /* Figure out what the rwnd would be */ rwnd = sctp_calc_rwnd(stcb, &stcb->asoc); if (rwnd >= stcb->asoc.my_last_reported_rwnd) { dif = rwnd - stcb->asoc.my_last_reported_rwnd; } else { dif = 0; } if (dif >= rwnd_req) { if (hold_rlock) { SCTP_INP_READ_UNLOCK(stcb->sctp_ep); r_unlocked = 1; } if (stcb->asoc.state & SCTP_STATE_ABOUT_TO_BE_FREED) { /* * One last check before we allow the guy possibly * to get in. There is a race, where the guy has not * reached the gate. In that case */ goto out; } SCTP_TCB_LOCK(stcb); if (stcb->asoc.state & SCTP_STATE_ABOUT_TO_BE_FREED) { /* No reports here */ SCTP_TCB_UNLOCK(stcb); goto out; } SCTP_STAT_INCR(sctps_wu_sacks_sent); /* * EY if nr_sacks used then send an nr-sack , a sack * otherwise */ if (SCTP_BASE_SYSCTL(sctp_nr_sack_on_off) && stcb->asoc.peer_supports_nr_sack) sctp_send_nr_sack(stcb); else sctp_send_sack(stcb); sctp_chunk_output(stcb->sctp_ep, stcb, SCTP_OUTPUT_FROM_USR_RCVD, SCTP_SO_LOCKED); /* make sure no timer is running */ sctp_timer_stop(SCTP_TIMER_TYPE_RECV, stcb->sctp_ep, stcb, NULL, SCTP_FROM_SCTPUTIL + SCTP_LOC_6); SCTP_TCB_UNLOCK(stcb); } else { /* Update how much we have pending */ stcb->freed_by_sorcv_sincelast = dif; } out: if (so && r_unlocked && hold_rlock) { SCTP_INP_READ_LOCK(stcb->sctp_ep); } SCTP_INP_DECR_REF(stcb->sctp_ep); no_lock: atomic_add_int(&stcb->asoc.refcnt, -1); return; } int sctp_sorecvmsg(struct socket *so, struct uio *uio, struct mbuf **mp, struct sockaddr *from, int fromlen, int *msg_flags, struct sctp_sndrcvinfo *sinfo, int filling_sinfo) { /* * MSG flags we will look at MSG_DONTWAIT - non-blocking IO. * MSG_PEEK - Look don't touch :-D (only valid with OUT mbuf copy * mp=NULL thus uio is the copy method to userland) MSG_WAITALL - ?? * On the way out we may send out any combination of: * MSG_NOTIFICATION MSG_EOR * */ struct sctp_inpcb *inp = NULL; int my_len = 0; int cp_len = 0, error = 0; struct sctp_queued_to_read *control = NULL, *ctl = NULL, *nxt = NULL; struct mbuf *m = NULL, *embuf = NULL; struct sctp_tcb *stcb = NULL; int wakeup_read_socket = 0; int freecnt_applied = 0; int out_flags = 0, in_flags = 0; int block_allowed = 1; uint32_t freed_so_far = 0; uint32_t copied_so_far = 0; int in_eeor_mode = 0; int no_rcv_needed = 0; uint32_t rwnd_req = 0; int hold_sblock = 0; int hold_rlock = 0; int slen = 0; uint32_t held_length = 0; int sockbuf_lock = 0; if (uio == NULL) { SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTPUTIL, EINVAL); return (EINVAL); } if (msg_flags) { in_flags = *msg_flags; if (in_flags & MSG_PEEK) SCTP_STAT_INCR(sctps_read_peeks); } else { in_flags = 0; } slen = uio->uio_resid; /* Pull in and set up our int flags */ if (in_flags & MSG_OOB) { /* Out of band's NOT supported */ return (EOPNOTSUPP); } if ((in_flags & MSG_PEEK) && (mp != NULL)) { SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTPUTIL, EINVAL); return (EINVAL); } if ((in_flags & (MSG_DONTWAIT | MSG_NBIO )) || SCTP_SO_IS_NBIO(so)) { block_allowed = 0; } /* setup the endpoint */ inp = (struct sctp_inpcb *)so->so_pcb; if (inp == NULL) { SCTP_LTRACE_ERR_RET(NULL, NULL, NULL, SCTP_FROM_SCTPUTIL, EFAULT); return (EFAULT); } rwnd_req = (SCTP_SB_LIMIT_RCV(so) >> SCTP_RWND_HIWAT_SHIFT); /* Must be at least a MTU's worth */ if (rwnd_req < SCTP_MIN_RWND) rwnd_req = SCTP_MIN_RWND; in_eeor_mode = sctp_is_feature_on(inp, SCTP_PCB_FLAGS_EXPLICIT_EOR); if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_RECV_RWND_LOGGING_ENABLE) { sctp_misc_ints(SCTP_SORECV_ENTER, rwnd_req, in_eeor_mode, so->so_rcv.sb_cc, uio->uio_resid); } if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_RECV_RWND_LOGGING_ENABLE) { sctp_misc_ints(SCTP_SORECV_ENTERPL, rwnd_req, block_allowed, so->so_rcv.sb_cc, uio->uio_resid); } error = sblock(&so->so_rcv, (block_allowed ? SBL_WAIT : 0)); sockbuf_lock = 1; if (error) { goto release_unlocked; } restart: restart_nosblocks: if (hold_sblock == 0) { SOCKBUF_LOCK(&so->so_rcv); hold_sblock = 1; } if ((inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) || (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_ALLGONE)) { goto out; } if (so->so_rcv.sb_state & SBS_CANTRCVMORE) { if (so->so_error) { error = so->so_error; if ((in_flags & MSG_PEEK) == 0) so->so_error = 0; goto out; } else { if (so->so_rcv.sb_cc == 0) { SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTPUTIL, ENOTCONN); /* indicate EOF */ error = 0; goto out; } } } if ((so->so_rcv.sb_cc <= held_length) && block_allowed) { /* we need to wait for data */ if ((so->so_rcv.sb_cc == 0) && ((inp->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) || (inp->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL))) { if ((inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) == 0) { /* * For active open side clear flags for * re-use passive open is blocked by * connect. */ if (inp->sctp_flags & SCTP_PCB_FLAGS_WAS_ABORTED) { /* * You were aborted, passive side * always hits here */ SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTPUTIL, ECONNRESET); error = ECONNRESET; /* * You get this once if you are * active open side */ if (!(inp->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL)) { /* * Remove flag if on the * active open side */ inp->sctp_flags &= ~SCTP_PCB_FLAGS_WAS_ABORTED; } } so->so_state &= ~(SS_ISCONNECTING | SS_ISDISCONNECTING | SS_ISCONFIRMING | SS_ISCONNECTED); if (error == 0) { if ((inp->sctp_flags & SCTP_PCB_FLAGS_WAS_CONNECTED) == 0) { SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTPUTIL, ENOTCONN); error = ENOTCONN; } else { inp->sctp_flags &= ~SCTP_PCB_FLAGS_WAS_CONNECTED; } } goto out; } } error = sbwait(&so->so_rcv); if (error) { goto out; } held_length = 0; goto restart_nosblocks; } else if (so->so_rcv.sb_cc == 0) { if (so->so_error) { error = so->so_error; if ((in_flags & MSG_PEEK) == 0) so->so_error = 0; } else { if ((inp->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) || (inp->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL)) { if ((inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) == 0) { /* * For active open side clear flags * for re-use passive open is * blocked by connect. */ if (inp->sctp_flags & SCTP_PCB_FLAGS_WAS_ABORTED) { /* * You were aborted, passive * side always hits here */ SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTPUTIL, ECONNRESET); error = ECONNRESET; /* * You get this once if you * are active open side */ if (!(inp->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL)) { /* * Remove flag if on * the active open * side */ inp->sctp_flags &= ~SCTP_PCB_FLAGS_WAS_ABORTED; } } so->so_state &= ~(SS_ISCONNECTING | SS_ISDISCONNECTING | SS_ISCONFIRMING | SS_ISCONNECTED); if (error == 0) { if ((inp->sctp_flags & SCTP_PCB_FLAGS_WAS_CONNECTED) == 0) { SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTPUTIL, ENOTCONN); error = ENOTCONN; } else { inp->sctp_flags &= ~SCTP_PCB_FLAGS_WAS_CONNECTED; } } goto out; } } SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTPUTIL, EWOULDBLOCK); error = EWOULDBLOCK; } goto out; } if (hold_sblock == 1) { SOCKBUF_UNLOCK(&so->so_rcv); hold_sblock = 0; } /* we possibly have data we can read */ /* sa_ignore FREED_MEMORY */ control = TAILQ_FIRST(&inp->read_queue); if (control == NULL) { /* * This could be happening since the appender did the * increment but as not yet did the tailq insert onto the * read_queue */ if (hold_rlock == 0) { SCTP_INP_READ_LOCK(inp); hold_rlock = 1; } control = TAILQ_FIRST(&inp->read_queue); if ((control == NULL) && (so->so_rcv.sb_cc != 0)) { #ifdef INVARIANTS panic("Huh, its non zero and nothing on control?"); #endif so->so_rcv.sb_cc = 0; } SCTP_INP_READ_UNLOCK(inp); hold_rlock = 0; goto restart; } if ((control->length == 0) && (control->do_not_ref_stcb)) { /* * Clean up code for freeing assoc that left behind a * pdapi.. maybe a peer in EEOR that just closed after * sending and never indicated a EOR. */ if (hold_rlock == 0) { hold_rlock = 1; SCTP_INP_READ_LOCK(inp); } control->held_length = 0; if (control->data) { /* Hmm there is data here .. fix */ struct mbuf *m_tmp; int cnt = 0; m_tmp = control->data; while (m_tmp) { cnt += SCTP_BUF_LEN(m_tmp); if (SCTP_BUF_NEXT(m_tmp) == NULL) { control->tail_mbuf = m_tmp; control->end_added = 1; } m_tmp = SCTP_BUF_NEXT(m_tmp); } control->length = cnt; } else { /* remove it */ TAILQ_REMOVE(&inp->read_queue, control, next); /* Add back any hiddend data */ sctp_free_remote_addr(control->whoFrom); sctp_free_a_readq(stcb, control); } if (hold_rlock) { hold_rlock = 0; SCTP_INP_READ_UNLOCK(inp); } goto restart; } if ((control->length == 0) && (control->end_added == 1)) { /* * Do we also need to check for (control->pdapi_aborted == * 1)? */ if (hold_rlock == 0) { hold_rlock = 1; SCTP_INP_READ_LOCK(inp); } TAILQ_REMOVE(&inp->read_queue, control, next); if (control->data) { #ifdef INVARIANTS panic("control->data not null but control->length == 0"); #else SCTP_PRINTF("Strange, data left in the control buffer. Cleaning up.\n"); sctp_m_freem(control->data); control->data = NULL; #endif } if (control->aux_data) { sctp_m_free(control->aux_data); control->aux_data = NULL; } sctp_free_remote_addr(control->whoFrom); sctp_free_a_readq(stcb, control); if (hold_rlock) { hold_rlock = 0; SCTP_INP_READ_UNLOCK(inp); } goto restart; } if (control->length == 0) { if ((sctp_is_feature_on(inp, SCTP_PCB_FLAGS_FRAG_INTERLEAVE)) && (filling_sinfo)) { /* find a more suitable one then this */ ctl = TAILQ_NEXT(control, next); while (ctl) { if ((ctl->stcb != control->stcb) && (ctl->length) && (ctl->some_taken || (ctl->spec_flags & M_NOTIFICATION) || ((ctl->do_not_ref_stcb == 0) && (ctl->stcb->asoc.strmin[ctl->sinfo_stream].delivery_started == 0))) ) { /*- * If we have a different TCB next, and there is data * present. If we have already taken some (pdapi), OR we can * ref the tcb and no delivery as started on this stream, we * take it. Note we allow a notification on a different * assoc to be delivered.. */ control = ctl; goto found_one; } else if ((sctp_is_feature_on(inp, SCTP_PCB_FLAGS_INTERLEAVE_STRMS)) && (ctl->length) && ((ctl->some_taken) || ((ctl->do_not_ref_stcb == 0) && ((ctl->spec_flags & M_NOTIFICATION) == 0) && (ctl->stcb->asoc.strmin[ctl->sinfo_stream].delivery_started == 0))) ) { /*- * If we have the same tcb, and there is data present, and we * have the strm interleave feature present. Then if we have * taken some (pdapi) or we can refer to tht tcb AND we have * not started a delivery for this stream, we can take it. * Note we do NOT allow a notificaiton on the same assoc to * be delivered. */ control = ctl; goto found_one; } ctl = TAILQ_NEXT(ctl, next); } } /* * if we reach here, not suitable replacement is available * fragment interleave is NOT on. So stuff the sb_cc * into the our held count, and its time to sleep again. */ held_length = so->so_rcv.sb_cc; control->held_length = so->so_rcv.sb_cc; goto restart; } /* Clear the held length since there is something to read */ control->held_length = 0; if (hold_rlock) { SCTP_INP_READ_UNLOCK(inp); hold_rlock = 0; } found_one: /* * If we reach here, control has a some data for us to read off. * Note that stcb COULD be NULL. */ control->some_taken++; if (hold_sblock) { SOCKBUF_UNLOCK(&so->so_rcv); hold_sblock = 0; } stcb = control->stcb; if (stcb) { if ((control->do_not_ref_stcb == 0) && (stcb->asoc.state & SCTP_STATE_ABOUT_TO_BE_FREED)) { if (freecnt_applied == 0) stcb = NULL; } else if (control->do_not_ref_stcb == 0) { /* you can't free it on me please */ /* * The lock on the socket buffer protects us so the * free code will stop. But since we used the * socketbuf lock and the sender uses the tcb_lock * to increment, we need to use the atomic add to * the refcnt */ if (freecnt_applied) { #ifdef INVARIANTS panic("refcnt already incremented"); #else printf("refcnt already incremented?\n"); #endif } else { atomic_add_int(&stcb->asoc.refcnt, 1); freecnt_applied = 1; } /* * Setup to remember how much we have not yet told * the peer our rwnd has opened up. Note we grab the * value from the tcb from last time. Note too that * sack sending clears this when a sack is sent, * which is fine. Once we hit the rwnd_req, we then * will go to the sctp_user_rcvd() that will not * lock until it KNOWs it MUST send a WUP-SACK. */ freed_so_far = stcb->freed_by_sorcv_sincelast; stcb->freed_by_sorcv_sincelast = 0; } } if (stcb && ((control->spec_flags & M_NOTIFICATION) == 0) && control->do_not_ref_stcb == 0) { stcb->asoc.strmin[control->sinfo_stream].delivery_started = 1; } /* First lets get off the sinfo and sockaddr info */ if ((sinfo) && filling_sinfo) { memcpy(sinfo, control, sizeof(struct sctp_nonpad_sndrcvinfo)); nxt = TAILQ_NEXT(control, next); if (sctp_is_feature_on(inp, SCTP_PCB_FLAGS_EXT_RCVINFO)) { struct sctp_extrcvinfo *s_extra; s_extra = (struct sctp_extrcvinfo *)sinfo; if ((nxt) && (nxt->length)) { s_extra->sreinfo_next_flags = SCTP_NEXT_MSG_AVAIL; if (nxt->sinfo_flags & SCTP_UNORDERED) { s_extra->sreinfo_next_flags |= SCTP_NEXT_MSG_IS_UNORDERED; } if (nxt->spec_flags & M_NOTIFICATION) { s_extra->sreinfo_next_flags |= SCTP_NEXT_MSG_IS_NOTIFICATION; } s_extra->sreinfo_next_aid = nxt->sinfo_assoc_id; s_extra->sreinfo_next_length = nxt->length; s_extra->sreinfo_next_ppid = nxt->sinfo_ppid; s_extra->sreinfo_next_stream = nxt->sinfo_stream; if (nxt->tail_mbuf != NULL) { if (nxt->end_added) { s_extra->sreinfo_next_flags |= SCTP_NEXT_MSG_ISCOMPLETE; } } } else { /* * we explicitly 0 this, since the memcpy * got some other things beyond the older * sinfo_ that is on the control's structure * :-D */ nxt = NULL; s_extra->sreinfo_next_flags = SCTP_NO_NEXT_MSG; s_extra->sreinfo_next_aid = 0; s_extra->sreinfo_next_length = 0; s_extra->sreinfo_next_ppid = 0; s_extra->sreinfo_next_stream = 0; } } /* * update off the real current cum-ack, if we have an stcb. */ if ((control->do_not_ref_stcb == 0) && stcb) sinfo->sinfo_cumtsn = stcb->asoc.cumulative_tsn; /* * mask off the high bits, we keep the actual chunk bits in * there. */ sinfo->sinfo_flags &= 0x00ff; if ((control->sinfo_flags >> 8) & SCTP_DATA_UNORDERED) { sinfo->sinfo_flags |= SCTP_UNORDERED; } } #ifdef SCTP_ASOCLOG_OF_TSNS { int index, newindex; struct sctp_pcbtsn_rlog *entry; do { index = inp->readlog_index; newindex = index + 1; if (newindex >= SCTP_READ_LOG_SIZE) { newindex = 0; } } while (atomic_cmpset_int(&inp->readlog_index, index, newindex) == 0); entry = &inp->readlog[index]; entry->vtag = control->sinfo_assoc_id; entry->strm = control->sinfo_stream; entry->seq = control->sinfo_ssn; entry->sz = control->length; entry->flgs = control->sinfo_flags; } #endif if (fromlen && from) { struct sockaddr *to; #ifdef INET cp_len = min((size_t)fromlen, (size_t)control->whoFrom->ro._l_addr.sin.sin_len); memcpy(from, &control->whoFrom->ro._l_addr, cp_len); ((struct sockaddr_in *)from)->sin_port = control->port_from; #else /* No AF_INET use AF_INET6 */ cp_len = min((size_t)fromlen, (size_t)control->whoFrom->ro._l_addr.sin6.sin6_len); memcpy(from, &control->whoFrom->ro._l_addr, cp_len); ((struct sockaddr_in6 *)from)->sin6_port = control->port_from; #endif to = from; #if defined(INET) && defined(INET6) if ((sctp_is_feature_on(inp, SCTP_PCB_FLAGS_NEEDS_MAPPED_V4)) && (to->sa_family == AF_INET) && ((size_t)fromlen >= sizeof(struct sockaddr_in6))) { struct sockaddr_in *sin; struct sockaddr_in6 sin6; sin = (struct sockaddr_in *)to; bzero(&sin6, sizeof(sin6)); sin6.sin6_family = AF_INET6; sin6.sin6_len = sizeof(struct sockaddr_in6); sin6.sin6_addr.s6_addr32[2] = htonl(0xffff); bcopy(&sin->sin_addr, &sin6.sin6_addr.s6_addr32[3], sizeof(sin6.sin6_addr.s6_addr32[3])); sin6.sin6_port = sin->sin_port; memcpy(from, (caddr_t)&sin6, sizeof(sin6)); } #endif #if defined(INET6) { struct sockaddr_in6 lsa6, *to6; to6 = (struct sockaddr_in6 *)to; sctp_recover_scope_mac(to6, (&lsa6)); } #endif } /* now copy out what data we can */ if (mp == NULL) { /* copy out each mbuf in the chain up to length */ get_more_data: m = control->data; while (m) { /* Move out all we can */ cp_len = (int)uio->uio_resid; my_len = (int)SCTP_BUF_LEN(m); if (cp_len > my_len) { /* not enough in this buf */ cp_len = my_len; } if (hold_rlock) { SCTP_INP_READ_UNLOCK(inp); hold_rlock = 0; } if (cp_len > 0) error = uiomove(mtod(m, char *), cp_len, uio); /* re-read */ if (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) { goto release; } if ((control->do_not_ref_stcb == 0) && stcb && stcb->asoc.state & SCTP_STATE_ABOUT_TO_BE_FREED) { no_rcv_needed = 1; } if (error) { /* error we are out of here */ goto release; } if ((SCTP_BUF_NEXT(m) == NULL) && (cp_len >= SCTP_BUF_LEN(m)) && ((control->end_added == 0) || (control->end_added && (TAILQ_NEXT(control, next) == NULL))) ) { SCTP_INP_READ_LOCK(inp); hold_rlock = 1; } if (cp_len == SCTP_BUF_LEN(m)) { if ((SCTP_BUF_NEXT(m) == NULL) && (control->end_added)) { out_flags |= MSG_EOR; if ((control->do_not_ref_stcb == 0) && ((control->spec_flags & M_NOTIFICATION) == 0)) control->stcb->asoc.strmin[control->sinfo_stream].delivery_started = 0; } if (control->spec_flags & M_NOTIFICATION) { out_flags |= MSG_NOTIFICATION; } /* we ate up the mbuf */ if (in_flags & MSG_PEEK) { /* just looking */ m = SCTP_BUF_NEXT(m); copied_so_far += cp_len; } else { /* dispose of the mbuf */ if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_SB_LOGGING_ENABLE) { sctp_sblog(&so->so_rcv, control->do_not_ref_stcb ? NULL : stcb, SCTP_LOG_SBFREE, SCTP_BUF_LEN(m)); } sctp_sbfree(control, stcb, &so->so_rcv, m); if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_SB_LOGGING_ENABLE) { sctp_sblog(&so->so_rcv, control->do_not_ref_stcb ? NULL : stcb, SCTP_LOG_SBRESULT, 0); } embuf = m; copied_so_far += cp_len; freed_so_far += cp_len; freed_so_far += MSIZE; atomic_subtract_int(&control->length, cp_len); control->data = sctp_m_free(m); m = control->data; /* * been through it all, must hold sb * lock ok to null tail */ if (control->data == NULL) { #ifdef INVARIANTS if ((control->end_added == 0) || (TAILQ_NEXT(control, next) == NULL)) { /* * If the end is not * added, OR the * next is NOT null * we MUST have the * lock. */ if (mtx_owned(&inp->inp_rdata_mtx) == 0) { panic("Hmm we don't own the lock?"); } } #endif control->tail_mbuf = NULL; #ifdef INVARIANTS if ((control->end_added) && ((out_flags & MSG_EOR) == 0)) { panic("end_added, nothing left and no MSG_EOR"); } #endif } } } else { /* Do we need to trim the mbuf? */ if (control->spec_flags & M_NOTIFICATION) { out_flags |= MSG_NOTIFICATION; } if ((in_flags & MSG_PEEK) == 0) { SCTP_BUF_RESV_UF(m, cp_len); SCTP_BUF_LEN(m) -= cp_len; if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_SB_LOGGING_ENABLE) { sctp_sblog(&so->so_rcv, control->do_not_ref_stcb ? NULL : stcb, SCTP_LOG_SBFREE, cp_len); } atomic_subtract_int(&so->so_rcv.sb_cc, cp_len); if ((control->do_not_ref_stcb == 0) && stcb) { atomic_subtract_int(&stcb->asoc.sb_cc, cp_len); } copied_so_far += cp_len; embuf = m; freed_so_far += cp_len; freed_so_far += MSIZE; if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_SB_LOGGING_ENABLE) { sctp_sblog(&so->so_rcv, control->do_not_ref_stcb ? NULL : stcb, SCTP_LOG_SBRESULT, 0); } atomic_subtract_int(&control->length, cp_len); } else { copied_so_far += cp_len; } } if ((out_flags & MSG_EOR) || (uio->uio_resid == 0)) { break; } if (((stcb) && (in_flags & MSG_PEEK) == 0) && (control->do_not_ref_stcb == 0) && (freed_so_far >= rwnd_req)) { sctp_user_rcvd(stcb, &freed_so_far, hold_rlock, rwnd_req); } } /* end while(m) */ /* * At this point we have looked at it all and we either have * a MSG_EOR/or read all the user wants... * control->length == 0. */ if ((out_flags & MSG_EOR) && ((in_flags & MSG_PEEK) == 0)) { /* we are done with this control */ if (control->length == 0) { if (control->data) { #ifdef INVARIANTS panic("control->data not null at read eor?"); #else SCTP_PRINTF("Strange, data left in the control buffer .. invarients would panic?\n"); sctp_m_freem(control->data); control->data = NULL; #endif } done_with_control: if (TAILQ_NEXT(control, next) == NULL) { /* * If we don't have a next we need a * lock, if there is a next * interrupt is filling ahead of us * and we don't need a lock to * remove this guy (which is the * head of the queue). */ if (hold_rlock == 0) { SCTP_INP_READ_LOCK(inp); hold_rlock = 1; } } TAILQ_REMOVE(&inp->read_queue, control, next); /* Add back any hiddend data */ if (control->held_length) { held_length = 0; control->held_length = 0; wakeup_read_socket = 1; } if (control->aux_data) { sctp_m_free(control->aux_data); control->aux_data = NULL; } no_rcv_needed = control->do_not_ref_stcb; sctp_free_remote_addr(control->whoFrom); control->data = NULL; sctp_free_a_readq(stcb, control); control = NULL; if ((freed_so_far >= rwnd_req) && (no_rcv_needed == 0)) sctp_user_rcvd(stcb, &freed_so_far, hold_rlock, rwnd_req); } else { /* * The user did not read all of this * message, turn off the returned MSG_EOR * since we are leaving more behind on the * control to read. */ #ifdef INVARIANTS if (control->end_added && (control->data == NULL) && (control->tail_mbuf == NULL)) { panic("Gak, control->length is corrupt?"); } #endif no_rcv_needed = control->do_not_ref_stcb; out_flags &= ~MSG_EOR; } } if (out_flags & MSG_EOR) { goto release; } if ((uio->uio_resid == 0) || ((in_eeor_mode) && (copied_so_far >= max(so->so_rcv.sb_lowat, 1))) ) { goto release; } /* * If I hit here the receiver wants more and this message is * NOT done (pd-api). So two questions. Can we block? if not * we are done. Did the user NOT set MSG_WAITALL? */ if (block_allowed == 0) { goto release; } /* * We need to wait for more data a few things: - We don't * sbunlock() so we don't get someone else reading. - We * must be sure to account for the case where what is added * is NOT to our control when we wakeup. */ /* * Do we need to tell the transport a rwnd update might be * needed before we go to sleep? */ if (((stcb) && (in_flags & MSG_PEEK) == 0) && ((freed_so_far >= rwnd_req) && (control->do_not_ref_stcb == 0) && (no_rcv_needed == 0))) { sctp_user_rcvd(stcb, &freed_so_far, hold_rlock, rwnd_req); } wait_some_more: if (so->so_rcv.sb_state & SBS_CANTRCVMORE) { goto release; } if (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) goto release; if (hold_rlock == 1) { SCTP_INP_READ_UNLOCK(inp); hold_rlock = 0; } if (hold_sblock == 0) { SOCKBUF_LOCK(&so->so_rcv); hold_sblock = 1; } if ((copied_so_far) && (control->length == 0) && (sctp_is_feature_on(inp, SCTP_PCB_FLAGS_FRAG_INTERLEAVE)) ) { goto release; } if (so->so_rcv.sb_cc <= control->held_length) { error = sbwait(&so->so_rcv); if (error) { goto release; } control->held_length = 0; } if (hold_sblock) { SOCKBUF_UNLOCK(&so->so_rcv); hold_sblock = 0; } if (control->length == 0) { /* still nothing here */ if (control->end_added == 1) { /* he aborted, or is done i.e.did a shutdown */ out_flags |= MSG_EOR; if (control->pdapi_aborted) { if ((control->do_not_ref_stcb == 0) && ((control->spec_flags & M_NOTIFICATION) == 0)) control->stcb->asoc.strmin[control->sinfo_stream].delivery_started = 0; out_flags |= MSG_TRUNC; } else { if ((control->do_not_ref_stcb == 0) && ((control->spec_flags & M_NOTIFICATION) == 0)) control->stcb->asoc.strmin[control->sinfo_stream].delivery_started = 0; } goto done_with_control; } if (so->so_rcv.sb_cc > held_length) { control->held_length = so->so_rcv.sb_cc; held_length = 0; } goto wait_some_more; } else if (control->data == NULL) { /* * we must re-sync since data is probably being * added */ SCTP_INP_READ_LOCK(inp); if ((control->length > 0) && (control->data == NULL)) { /* * big trouble.. we have the lock and its * corrupt? */ #ifdef INVARIANTS panic("Impossible data==NULL length !=0"); #endif out_flags |= MSG_EOR; out_flags |= MSG_TRUNC; control->length = 0; SCTP_INP_READ_UNLOCK(inp); goto done_with_control; } SCTP_INP_READ_UNLOCK(inp); /* We will fall around to get more data */ } goto get_more_data; } else { /*- * Give caller back the mbuf chain, * store in uio_resid the length */ wakeup_read_socket = 0; if ((control->end_added == 0) || (TAILQ_NEXT(control, next) == NULL)) { /* Need to get rlock */ if (hold_rlock == 0) { SCTP_INP_READ_LOCK(inp); hold_rlock = 1; } } if (control->end_added) { out_flags |= MSG_EOR; if ((control->do_not_ref_stcb == 0) && ((control->spec_flags & M_NOTIFICATION) == 0)) control->stcb->asoc.strmin[control->sinfo_stream].delivery_started = 0; } if (control->spec_flags & M_NOTIFICATION) { out_flags |= MSG_NOTIFICATION; } uio->uio_resid = control->length; *mp = control->data; m = control->data; while (m) { if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_SB_LOGGING_ENABLE) { sctp_sblog(&so->so_rcv, control->do_not_ref_stcb ? NULL : stcb, SCTP_LOG_SBFREE, SCTP_BUF_LEN(m)); } sctp_sbfree(control, stcb, &so->so_rcv, m); freed_so_far += SCTP_BUF_LEN(m); freed_so_far += MSIZE; if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_SB_LOGGING_ENABLE) { sctp_sblog(&so->so_rcv, control->do_not_ref_stcb ? NULL : stcb, SCTP_LOG_SBRESULT, 0); } m = SCTP_BUF_NEXT(m); } control->data = control->tail_mbuf = NULL; control->length = 0; if (out_flags & MSG_EOR) { /* Done with this control */ goto done_with_control; } } release: if (hold_rlock == 1) { SCTP_INP_READ_UNLOCK(inp); hold_rlock = 0; } if (hold_sblock == 1) { SOCKBUF_UNLOCK(&so->so_rcv); hold_sblock = 0; } sbunlock(&so->so_rcv); sockbuf_lock = 0; release_unlocked: if (hold_sblock) { SOCKBUF_UNLOCK(&so->so_rcv); hold_sblock = 0; } if ((stcb) && (in_flags & MSG_PEEK) == 0) { if ((freed_so_far >= rwnd_req) && (control && (control->do_not_ref_stcb == 0)) && (no_rcv_needed == 0)) sctp_user_rcvd(stcb, &freed_so_far, hold_rlock, rwnd_req); } out: if (msg_flags) { *msg_flags = out_flags; } if (((out_flags & MSG_EOR) == 0) && ((in_flags & MSG_PEEK) == 0) && (sinfo) && (sctp_is_feature_on(inp, SCTP_PCB_FLAGS_EXT_RCVINFO))) { struct sctp_extrcvinfo *s_extra; s_extra = (struct sctp_extrcvinfo *)sinfo; s_extra->sreinfo_next_flags = SCTP_NO_NEXT_MSG; } if (hold_rlock == 1) { SCTP_INP_READ_UNLOCK(inp); hold_rlock = 0; } if (hold_sblock) { SOCKBUF_UNLOCK(&so->so_rcv); hold_sblock = 0; } if (sockbuf_lock) { sbunlock(&so->so_rcv); } if (freecnt_applied) { /* * The lock on the socket buffer protects us so the free * code will stop. But since we used the socketbuf lock and * the sender uses the tcb_lock to increment, we need to use * the atomic add to the refcnt. */ if (stcb == NULL) { #ifdef INVARIANTS panic("stcb for refcnt has gone NULL?"); goto stage_left; #else goto stage_left; #endif } atomic_add_int(&stcb->asoc.refcnt, -1); freecnt_applied = 0; /* Save the value back for next time */ stcb->freed_by_sorcv_sincelast = freed_so_far; } if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_RECV_RWND_LOGGING_ENABLE) { if (stcb) { sctp_misc_ints(SCTP_SORECV_DONE, freed_so_far, ((uio) ? (slen - uio->uio_resid) : slen), stcb->asoc.my_rwnd, so->so_rcv.sb_cc); } else { sctp_misc_ints(SCTP_SORECV_DONE, freed_so_far, ((uio) ? (slen - uio->uio_resid) : slen), 0, so->so_rcv.sb_cc); } } stage_left: if (wakeup_read_socket) { sctp_sorwakeup(inp, so); } return (error); } #ifdef SCTP_MBUF_LOGGING struct mbuf * sctp_m_free(struct mbuf *m) { if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_MBUF_LOGGING_ENABLE) { if (SCTP_BUF_IS_EXTENDED(m)) { sctp_log_mb(m, SCTP_MBUF_IFREE); } } return (m_free(m)); } void sctp_m_freem(struct mbuf *mb) { while (mb != NULL) mb = sctp_m_free(mb); } #endif int sctp_dynamic_set_primary(struct sockaddr *sa, uint32_t vrf_id) { /* * Given a local address. For all associations that holds the * address, request a peer-set-primary. */ struct sctp_ifa *ifa; struct sctp_laddr *wi; ifa = sctp_find_ifa_by_addr(sa, vrf_id, 0); if (ifa == NULL) { SCTP_LTRACE_ERR_RET(NULL, NULL, NULL, SCTP_FROM_SCTPUTIL, EADDRNOTAVAIL); return (EADDRNOTAVAIL); } /* * Now that we have the ifa we must awaken the iterator with this * message. */ wi = SCTP_ZONE_GET(SCTP_BASE_INFO(ipi_zone_laddr), struct sctp_laddr); if (wi == NULL) { SCTP_LTRACE_ERR_RET(NULL, NULL, NULL, SCTP_FROM_SCTPUTIL, ENOMEM); return (ENOMEM); } /* Now incr the count and int wi structure */ SCTP_INCR_LADDR_COUNT(); bzero(wi, sizeof(*wi)); (void)SCTP_GETTIME_TIMEVAL(&wi->start_time); wi->ifa = ifa; wi->action = SCTP_SET_PRIM_ADDR; atomic_add_int(&ifa->refcount, 1); /* Now add it to the work queue */ SCTP_IPI_ITERATOR_WQ_LOCK(); /* * Should this really be a tailq? As it is we will process the * newest first :-0 */ LIST_INSERT_HEAD(&SCTP_BASE_INFO(addr_wq), wi, sctp_nxt_addr); sctp_timer_start(SCTP_TIMER_TYPE_ADDR_WQ, (struct sctp_inpcb *)NULL, (struct sctp_tcb *)NULL, (struct sctp_nets *)NULL); SCTP_IPI_ITERATOR_WQ_UNLOCK(); return (0); } int sctp_soreceive(struct socket *so, struct sockaddr **psa, struct uio *uio, struct mbuf **mp0, struct mbuf **controlp, int *flagsp) { int error, fromlen; uint8_t sockbuf[256]; struct sockaddr *from; struct sctp_extrcvinfo sinfo; int filling_sinfo = 1; struct sctp_inpcb *inp; inp = (struct sctp_inpcb *)so->so_pcb; /* pickup the assoc we are reading from */ if (inp == NULL) { SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTPUTIL, EINVAL); return (EINVAL); } if ((sctp_is_feature_off(inp, SCTP_PCB_FLAGS_RECVDATAIOEVNT)) || (controlp == NULL)) { /* user does not want the sndrcv ctl */ filling_sinfo = 0; } if (psa) { from = (struct sockaddr *)sockbuf; fromlen = sizeof(sockbuf); from->sa_len = 0; } else { from = NULL; fromlen = 0; } error = sctp_sorecvmsg(so, uio, mp0, from, fromlen, flagsp, (struct sctp_sndrcvinfo *)&sinfo, filling_sinfo); if ((controlp) && (filling_sinfo)) { /* copy back the sinfo in a CMSG format */ if (filling_sinfo) *controlp = sctp_build_ctl_nchunk(inp, (struct sctp_sndrcvinfo *)&sinfo); else *controlp = NULL; } if (psa) { /* copy back the address info */ if (from && from->sa_len) { *psa = sodupsockaddr(from, M_NOWAIT); } else { *psa = NULL; } } return (error); } int sctp_l_soreceive(struct socket *so, struct sockaddr **name, struct uio *uio, char **controlp, int *controllen, int *flag) { int error, fromlen; uint8_t sockbuf[256]; struct sockaddr *from; struct sctp_extrcvinfo sinfo; int filling_sinfo = 1; struct sctp_inpcb *inp; inp = (struct sctp_inpcb *)so->so_pcb; /* pickup the assoc we are reading from */ if (inp == NULL) { SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTPUTIL, EINVAL); return (EINVAL); } if ((sctp_is_feature_off(inp, SCTP_PCB_FLAGS_RECVDATAIOEVNT)) || (controlp == NULL)) { /* user does not want the sndrcv ctl */ filling_sinfo = 0; } if (name) { from = (struct sockaddr *)sockbuf; fromlen = sizeof(sockbuf); from->sa_len = 0; } else { from = NULL; fromlen = 0; } error = sctp_sorecvmsg(so, uio, (struct mbuf **)NULL, from, fromlen, flag, (struct sctp_sndrcvinfo *)&sinfo, filling_sinfo); if ((controlp) && (filling_sinfo)) { /* * copy back the sinfo in a CMSG format note that the caller * has reponsibility for freeing the memory. */ if (filling_sinfo) *controlp = sctp_build_ctl_cchunk(inp, controllen, (struct sctp_sndrcvinfo *)&sinfo); } if (name) { /* copy back the address info */ if (from && from->sa_len) { *name = sodupsockaddr(from, M_WAIT); } else { *name = NULL; } } return (error); } int sctp_connectx_helper_add(struct sctp_tcb *stcb, struct sockaddr *addr, int totaddr, int *error) { int added = 0; int i; struct sctp_inpcb *inp; struct sockaddr *sa; size_t incr = 0; sa = addr; inp = stcb->sctp_ep; *error = 0; for (i = 0; i < totaddr; i++) { if (sa->sa_family == AF_INET) { incr = sizeof(struct sockaddr_in); if (sctp_add_remote_addr(stcb, sa, SCTP_DONOT_SETSCOPE, SCTP_ADDR_IS_CONFIRMED)) { /* assoc gone no un-lock */ SCTP_LTRACE_ERR_RET(NULL, stcb, NULL, SCTP_FROM_SCTPUTIL, ENOBUFS); (void)sctp_free_assoc(inp, stcb, SCTP_NORMAL_PROC, SCTP_FROM_SCTP_USRREQ + SCTP_LOC_7); *error = ENOBUFS; goto out_now; } added++; } else if (sa->sa_family == AF_INET6) { incr = sizeof(struct sockaddr_in6); if (sctp_add_remote_addr(stcb, sa, SCTP_DONOT_SETSCOPE, SCTP_ADDR_IS_CONFIRMED)) { /* assoc gone no un-lock */ SCTP_LTRACE_ERR_RET(NULL, stcb, NULL, SCTP_FROM_SCTPUTIL, ENOBUFS); (void)sctp_free_assoc(inp, stcb, SCTP_NORMAL_PROC, SCTP_FROM_SCTP_USRREQ + SCTP_LOC_8); *error = ENOBUFS; goto out_now; } added++; } sa = (struct sockaddr *)((caddr_t)sa + incr); } out_now: return (added); } struct sctp_tcb * sctp_connectx_helper_find(struct sctp_inpcb *inp, struct sockaddr *addr, int *totaddr, int *num_v4, int *num_v6, int *error, int limit, int *bad_addr) { struct sockaddr *sa; struct sctp_tcb *stcb = NULL; size_t incr, at, i; at = incr = 0; sa = addr; *error = *num_v6 = *num_v4 = 0; /* account and validate addresses */ for (i = 0; i < (size_t)*totaddr; i++) { if (sa->sa_family == AF_INET) { (*num_v4) += 1; incr = sizeof(struct sockaddr_in); if (sa->sa_len != incr) { SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTPUTIL, EINVAL); *error = EINVAL; *bad_addr = 1; return (NULL); } } else if (sa->sa_family == AF_INET6) { struct sockaddr_in6 *sin6; sin6 = (struct sockaddr_in6 *)sa; if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { /* Must be non-mapped for connectx */ SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTPUTIL, EINVAL); *error = EINVAL; *bad_addr = 1; return (NULL); } (*num_v6) += 1; incr = sizeof(struct sockaddr_in6); if (sa->sa_len != incr) { SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTPUTIL, EINVAL); *error = EINVAL; *bad_addr = 1; return (NULL); } } else { *totaddr = i; /* we are done */ break; } SCTP_INP_INCR_REF(inp); stcb = sctp_findassociation_ep_addr(&inp, sa, NULL, NULL, NULL); if (stcb != NULL) { /* Already have or am bring up an association */ return (stcb); } else { SCTP_INP_DECR_REF(inp); } if ((at + incr) > (size_t)limit) { *totaddr = i; break; } sa = (struct sockaddr *)((caddr_t)sa + incr); } return ((struct sctp_tcb *)NULL); } /* * sctp_bindx(ADD) for one address. * assumes all arguments are valid/checked by caller. */ void sctp_bindx_add_address(struct socket *so, struct sctp_inpcb *inp, struct sockaddr *sa, sctp_assoc_t assoc_id, uint32_t vrf_id, int *error, void *p) { struct sockaddr *addr_touse; #ifdef INET6 struct sockaddr_in sin; #endif /* see if we're bound all already! */ if (inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) { SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTPUTIL, EINVAL); *error = EINVAL; return; } addr_touse = sa; #if defined(INET6) && !defined(__Userspace__) /* TODO port in6_sin6_2_sin */ if (sa->sa_family == AF_INET6) { struct sockaddr_in6 *sin6; if (sa->sa_len != sizeof(struct sockaddr_in6)) { SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTPUTIL, EINVAL); *error = EINVAL; return; } if ((inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) == 0) { /* can only bind v6 on PF_INET6 sockets */ SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTPUTIL, EINVAL); *error = EINVAL; return; } sin6 = (struct sockaddr_in6 *)addr_touse; if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { if ((inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) && SCTP_IPV6_V6ONLY(inp)) { /* can't bind v4-mapped on PF_INET sockets */ SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTPUTIL, EINVAL); *error = EINVAL; return; } in6_sin6_2_sin(&sin, sin6); addr_touse = (struct sockaddr *)&sin; } } #endif if (sa->sa_family == AF_INET) { if (sa->sa_len != sizeof(struct sockaddr_in)) { SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTPUTIL, EINVAL); *error = EINVAL; return; } if ((inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) && SCTP_IPV6_V6ONLY(inp)) { /* can't bind v4 on PF_INET sockets */ SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTPUTIL, EINVAL); *error = EINVAL; return; } } if (inp->sctp_flags & SCTP_PCB_FLAGS_UNBOUND) { if (p == NULL) { /* Can't get proc for Net/Open BSD */ SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTPUTIL, EINVAL); *error = EINVAL; return; } *error = sctp_inpcb_bind(so, addr_touse, NULL, p); return; } /* * No locks required here since bind and mgmt_ep_sa all do their own * locking. If we do something for the FIX: below we may need to * lock in that case. */ if (assoc_id == 0) { /* add the address */ struct sctp_inpcb *lep; struct sockaddr_in *lsin = (struct sockaddr_in *)addr_touse; /* validate the incoming port */ if ((lsin->sin_port != 0) && (lsin->sin_port != inp->sctp_lport)) { SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTPUTIL, EINVAL); *error = EINVAL; return; } else { /* user specified 0 port, set it to existing port */ lsin->sin_port = inp->sctp_lport; } lep = sctp_pcb_findep(addr_touse, 1, 0, vrf_id); if (lep != NULL) { /* * We must decrement the refcount since we have the * ep already and are binding. No remove going on * here. */ SCTP_INP_DECR_REF(lep); } if (lep == inp) { /* already bound to it.. ok */ return; } else if (lep == NULL) { ((struct sockaddr_in *)addr_touse)->sin_port = 0; *error = sctp_addr_mgmt_ep_sa(inp, addr_touse, SCTP_ADD_IP_ADDRESS, vrf_id, NULL); } else { *error = EADDRINUSE; } if (*error) return; } else { /* * FIX: decide whether we allow assoc based bindx */ } } /* * sctp_bindx(DELETE) for one address. * assumes all arguments are valid/checked by caller. */ void sctp_bindx_delete_address(struct socket *so, struct sctp_inpcb *inp, struct sockaddr *sa, sctp_assoc_t assoc_id, uint32_t vrf_id, int *error) { struct sockaddr *addr_touse; #ifdef INET6 struct sockaddr_in sin; #endif /* see if we're bound all already! */ if (inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) { SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTPUTIL, EINVAL); *error = EINVAL; return; } addr_touse = sa; #if defined(INET6) && !defined(__Userspace__) /* TODO port in6_sin6_2_sin */ if (sa->sa_family == AF_INET6) { struct sockaddr_in6 *sin6; if (sa->sa_len != sizeof(struct sockaddr_in6)) { SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTPUTIL, EINVAL); *error = EINVAL; return; } if ((inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) == 0) { /* can only bind v6 on PF_INET6 sockets */ SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTPUTIL, EINVAL); *error = EINVAL; return; } sin6 = (struct sockaddr_in6 *)addr_touse; if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { if ((inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) && SCTP_IPV6_V6ONLY(inp)) { /* can't bind mapped-v4 on PF_INET sockets */ SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTPUTIL, EINVAL); *error = EINVAL; return; } in6_sin6_2_sin(&sin, sin6); addr_touse = (struct sockaddr *)&sin; } } #endif if (sa->sa_family == AF_INET) { if (sa->sa_len != sizeof(struct sockaddr_in)) { SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTPUTIL, EINVAL); *error = EINVAL; return; } if ((inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) && SCTP_IPV6_V6ONLY(inp)) { /* can't bind v4 on PF_INET sockets */ SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTPUTIL, EINVAL); *error = EINVAL; return; } } /* * No lock required mgmt_ep_sa does its own locking. If the FIX: * below is ever changed we may need to lock before calling * association level binding. */ if (assoc_id == 0) { /* delete the address */ *error = sctp_addr_mgmt_ep_sa(inp, addr_touse, SCTP_DEL_IP_ADDRESS, vrf_id, NULL); } else { /* * FIX: decide whether we allow assoc based bindx */ } } /* * returns the valid local address count for an assoc, taking into account * all scoping rules */ int sctp_local_addr_count(struct sctp_tcb *stcb) { int loopback_scope, ipv4_local_scope, local_scope, site_scope; int ipv4_addr_legal, ipv6_addr_legal; struct sctp_vrf *vrf; struct sctp_ifn *sctp_ifn; struct sctp_ifa *sctp_ifa; int count = 0; /* Turn on all the appropriate scopes */ loopback_scope = stcb->asoc.loopback_scope; ipv4_local_scope = stcb->asoc.ipv4_local_scope; local_scope = stcb->asoc.local_scope; site_scope = stcb->asoc.site_scope; ipv4_addr_legal = ipv6_addr_legal = 0; if (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) { ipv6_addr_legal = 1; if (SCTP_IPV6_V6ONLY(stcb->sctp_ep) == 0) { ipv4_addr_legal = 1; } } else { ipv4_addr_legal = 1; } SCTP_IPI_ADDR_RLOCK(); vrf = sctp_find_vrf(stcb->asoc.vrf_id); if (vrf == NULL) { /* no vrf, no addresses */ SCTP_IPI_ADDR_RUNLOCK(); return (0); } if (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) { /* * bound all case: go through all ifns on the vrf */ LIST_FOREACH(sctp_ifn, &vrf->ifnlist, next_ifn) { if ((loopback_scope == 0) && SCTP_IFN_IS_IFT_LOOP(sctp_ifn)) { continue; } LIST_FOREACH(sctp_ifa, &sctp_ifn->ifalist, next_ifa) { if (sctp_is_addr_restricted(stcb, sctp_ifa)) continue; switch (sctp_ifa->address.sa.sa_family) { case AF_INET: if (ipv4_addr_legal) { struct sockaddr_in *sin; sin = (struct sockaddr_in *)&sctp_ifa->address.sa; if (sin->sin_addr.s_addr == 0) { /* * skip unspecified * addrs */ continue; } if ((ipv4_local_scope == 0) && (IN4_ISPRIVATE_ADDRESS(&sin->sin_addr))) { continue; } /* count this one */ count++; } else { continue; } break; #ifdef INET6 case AF_INET6: if (ipv6_addr_legal) { struct sockaddr_in6 *sin6; sin6 = (struct sockaddr_in6 *)&sctp_ifa->address.sa; if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { continue; } if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) { if (local_scope == 0) continue; if (sin6->sin6_scope_id == 0) { if (sa6_recoverscope(sin6) != 0) /* * * bad * * li * nk * * loc * al * * add * re * ss * */ continue; } } if ((site_scope == 0) && (IN6_IS_ADDR_SITELOCAL(&sin6->sin6_addr))) { continue; } /* count this one */ count++; } break; #endif default: /* TSNH */ break; } } } } else { /* * subset bound case */ struct sctp_laddr *laddr; LIST_FOREACH(laddr, &stcb->sctp_ep->sctp_addr_list, sctp_nxt_addr) { if (sctp_is_addr_restricted(stcb, laddr->ifa)) { continue; } /* count this one */ count++; } } SCTP_IPI_ADDR_RUNLOCK(); return (count); } #if defined(SCTP_LOCAL_TRACE_BUF) void sctp_log_trace(uint32_t subsys, const char *str SCTP_UNUSED, uint32_t a, uint32_t b, uint32_t c, uint32_t d, uint32_t e, uint32_t f) { uint32_t saveindex, newindex; do { saveindex = SCTP_BASE_SYSCTL(sctp_log).index; if (saveindex >= SCTP_MAX_LOGGING_SIZE) { newindex = 1; } else { newindex = saveindex + 1; } } while (atomic_cmpset_int(&SCTP_BASE_SYSCTL(sctp_log).index, saveindex, newindex) == 0); if (saveindex >= SCTP_MAX_LOGGING_SIZE) { saveindex = 0; } SCTP_BASE_SYSCTL(sctp_log).entry[saveindex].timestamp = SCTP_GET_CYCLECOUNT; SCTP_BASE_SYSCTL(sctp_log).entry[saveindex].subsys = subsys; SCTP_BASE_SYSCTL(sctp_log).entry[saveindex].params[0] = a; SCTP_BASE_SYSCTL(sctp_log).entry[saveindex].params[1] = b; SCTP_BASE_SYSCTL(sctp_log).entry[saveindex].params[2] = c; SCTP_BASE_SYSCTL(sctp_log).entry[saveindex].params[3] = d; SCTP_BASE_SYSCTL(sctp_log).entry[saveindex].params[4] = e; SCTP_BASE_SYSCTL(sctp_log).entry[saveindex].params[5] = f; } #endif /* We will need to add support * to bind the ports and such here * so we can do UDP tunneling. In * the mean-time, we return error */ #include #include #include #ifdef INET6 #include #endif static void sctp_recv_udp_tunneled_packet(struct mbuf *m, int off, struct inpcb *ignored) { struct ip *iph; struct mbuf *sp, *last; struct udphdr *uhdr; uint16_t port = 0, len; int header_size = sizeof(struct udphdr) + sizeof(struct sctphdr); /* * Split out the mbuf chain. Leave the IP header in m, place the * rest in the sp. */ if ((m->m_flags & M_PKTHDR) == 0) { /* Can't handle one that is not a pkt hdr */ goto out; } /* pull the src port */ iph = mtod(m, struct ip *); uhdr = (struct udphdr *)((caddr_t)iph + off); port = uhdr->uh_sport; sp = m_split(m, off, M_DONTWAIT); if (sp == NULL) { /* Gak, drop packet, we can't do a split */ goto out; } if (sp->m_pkthdr.len < header_size) { /* Gak, packet can't have an SCTP header in it - to small */ m_freem(sp); goto out; } /* ok now pull up the UDP header and SCTP header together */ sp = m_pullup(sp, header_size); if (sp == NULL) { /* Gak pullup failed */ goto out; } /* trim out the UDP header */ m_adj(sp, sizeof(struct udphdr)); /* Now reconstruct the mbuf chain */ /* 1) find last one */ last = m; while (last->m_next != NULL) { last = last->m_next; } last->m_next = sp; m->m_pkthdr.len += sp->m_pkthdr.len; last = m; while (last != NULL) { last = last->m_next; } /* Now its ready for sctp_input or sctp6_input */ iph = mtod(m, struct ip *); switch (iph->ip_v) { case IPVERSION: { /* its IPv4 */ len = SCTP_GET_IPV4_LENGTH(iph); len -= sizeof(struct udphdr); SCTP_GET_IPV4_LENGTH(iph) = len; sctp_input_with_port(m, off, port); break; } #ifdef INET6 case IPV6_VERSION >> 4: { /* its IPv6 - NOT supported */ goto out; break; } #endif default: { m_freem(m); break; } } return; out: m_freem(m); } void sctp_over_udp_stop(void) { struct socket *sop; /* * This function assumes sysctl caller holds sctp_sysctl_info_lock() * for writting! */ if (SCTP_BASE_INFO(udp_tun_socket) == NULL) { /* Nothing to do */ return; } sop = SCTP_BASE_INFO(udp_tun_socket); soclose(sop); SCTP_BASE_INFO(udp_tun_socket) = NULL; } int sctp_over_udp_start(void) { uint16_t port; int ret; struct sockaddr_in sin; struct socket *sop = NULL; struct thread *th; struct ucred *cred; /* * This function assumes sysctl caller holds sctp_sysctl_info_lock() * for writting! */ port = SCTP_BASE_SYSCTL(sctp_udp_tunneling_port); if (port == 0) { /* Must have a port set */ return (EINVAL); } if (SCTP_BASE_INFO(udp_tun_socket) != NULL) { /* Already running -- must stop first */ return (EALREADY); } th = curthread; cred = th->td_ucred; if ((ret = socreate(PF_INET, &sop, SOCK_DGRAM, IPPROTO_UDP, cred, th))) { return (ret); } SCTP_BASE_INFO(udp_tun_socket) = sop; /* call the special UDP hook */ ret = udp_set_kernel_tunneling(sop, sctp_recv_udp_tunneled_packet); if (ret) { goto exit_stage_left; } /* Ok we have a socket, bind it to the port */ memset(&sin, 0, sizeof(sin)); sin.sin_len = sizeof(sin); sin.sin_family = AF_INET; sin.sin_port = htons(port); ret = sobind(sop, (struct sockaddr *)&sin, th); if (ret) { /* Close up we cant get the port */ exit_stage_left: sctp_over_udp_stop(); return (ret); } /* * Ok we should now get UDP packets directly to our input routine * sctp_recv_upd_tunneled_packet(). */ return (0); }