Index: sys/kern/uipc_sockbuf.c =================================================================== --- sys/kern/uipc_sockbuf.c (revision 336040) +++ sys/kern/uipc_sockbuf.c (working copy) @@ -888,23 +888,16 @@ return (retval); } -int +void sbappendcontrol_locked(struct sockbuf *sb, struct mbuf *m0, struct mbuf *control) { - struct mbuf *m, *n, *mlast; - int space; + struct mbuf *m, *mlast; SOCKBUF_LOCK_ASSERT(sb); - if (control == NULL) - panic("sbappendcontrol_locked"); - space = m_length(control, &n) + m_length(m0, NULL); - - if (space > sbspace(sb)) - return (0); m_clrprotoflags(m0); - n->m_next = m0; /* concatenate data to control */ + m_last(control)->m_next = m0; /* concatenate data to control */ SBLASTRECORDCHK(sb); @@ -918,18 +911,14 @@ SBLASTMBUFCHK(sb); SBLASTRECORDCHK(sb); - return (1); } -int +void sbappendcontrol(struct sockbuf *sb, struct mbuf *m0, struct mbuf *control) { - int retval; - SOCKBUF_LOCK(sb); - retval = sbappendcontrol_locked(sb, m0, control); + sbappendcontrol_locked(sb, m0, control); SOCKBUF_UNLOCK(sb); - return (retval); } /* Index: sys/kern/uipc_socket.c =================================================================== --- sys/kern/uipc_socket.c (revision 336040) +++ sys/kern/uipc_socket.c (working copy) @@ -1049,6 +1049,23 @@ #define SBLOCKWAIT(f) (((f) & MSG_DONTWAIT) ? 0 : SBL_WAIT) +/* + * Like sbspace(&so->so_snd), but allow extra room for MSG_OOB. + * + * It's not clear whether the magic 1024 number is sensible, but + * at least it's now in only one place. + */ +static inline int +sosend_space(struct socket *so, int flags) +{ + long space; + + space = sbspace(&so->so_snd); + if (flags & MSG_OOB) + space += 1024; + return (space); +} + int sosend_dgram(struct socket *so, struct sockaddr *addr, struct uio *uio, struct mbuf *top, struct mbuf *control, int flags, struct thread *td) @@ -1061,6 +1078,11 @@ KASSERT(so->so_proto->pr_flags & PR_ATOMIC, ("sosend_dgram: !PR_ATOMIC")); + if (so->so_proto->pr_usrreqs->pru_finalizecontrol && + (error = (*so->so_proto->pr_usrreqs->pru_finalizecontrol)(so, + flags, &control, td))) + goto out; + if (uio != NULL) resid = uio->uio_resid; else @@ -1081,8 +1103,17 @@ (flags & MSG_DONTROUTE) && (so->so_options & SO_DONTROUTE) == 0; if (td != NULL) td->td_ru.ru_msgsnd++; + /* + * NB: Original user supplied control message may have + * fit and we may have made it too big when we finalized + * it, which will have us return EMSGSIZE below. This + * seems rude, but it is at least functional: the user + * can try sending smaller control values (mainly, fewer + * fd's at a time, as those are the ones that expand to + * twice their size on I32LP64 systems). + */ if (control != NULL) - clen = control->m_len; + clen = m_length(control, NULL); SOCKBUF_LOCK(&so->so_snd); if (so->so_snd.sb_state & SBS_CANTSENDMORE) { @@ -1120,15 +1151,13 @@ } } - /* - * Do we need MSG_OOB support in SOCK_DGRAM? Signs here may be a - * problem and need fixing. - */ - space = sbspace(&so->so_snd); - if (flags & MSG_OOB) - space += 1024; + space = sosend_space(so, flags); + SOCKBUF_UNLOCK(&so->so_snd); + if (clen > space) { + error = EMSGSIZE; + goto out; + } space -= clen; - SOCKBUF_UNLOCK(&so->so_snd); if (resid > space) { error = EMSGSIZE; goto out; @@ -1223,6 +1252,11 @@ int clen = 0, error, dontroute; int atomic = sosendallatonce(so) || top; + if (so->so_proto->pr_usrreqs->pru_finalizecontrol && + (error = (*so->so_proto->pr_usrreqs->pru_finalizecontrol)(so, + flags, &control, td))) + goto out; + if (uio != NULL) resid = uio->uio_resid; else @@ -1248,7 +1282,7 @@ if (td != NULL) td->td_ru.ru_msgsnd++; if (control != NULL) - clen = control->m_len; + clen = m_length(control, NULL); error = sblock(&so->so_snd, SBLOCKWAIT(flags)); if (error) @@ -1292,9 +1326,8 @@ goto release; } } - space = sbspace(&so->so_snd); - if (flags & MSG_OOB) - space += 1024; + space = sosend_space(so, flags); + /* NB: control msg is implicitly atomic */ if ((atomic && resid > so->so_snd.sb_hiwat) || clen > so->so_snd.sb_hiwat) { SOCKBUF_UNLOCK(&so->so_snd); @@ -1315,6 +1348,10 @@ goto restart; } SOCKBUF_UNLOCK(&so->so_snd); + if (clen > space) { + error = EMSGSIZE; + goto release; + } space -= clen; do { if (uio == NULL) { Index: sys/kern/uipc_usrreq.c =================================================================== --- sys/kern/uipc_usrreq.c (revision 336040) +++ sys/kern/uipc_usrreq.c (working copy) @@ -290,7 +290,7 @@ static void unp_internalize_fp(struct file *); static int unp_externalize(struct mbuf *, struct mbuf **, int); static int unp_externalize_fp(struct file *); -static struct mbuf *unp_addsockcred(struct thread *, struct mbuf *); +static int unp_addsockcred(struct mbuf **, struct thread *); static void unp_process_defers(void * __unused, int); /* @@ -800,6 +800,57 @@ } static int +uipc_finalizecontrol(struct socket *so, int flags, struct mbuf **pcontrol, + struct thread *td) +{ + struct unpcb *unp, *unp2; + int error = 0; + bool wantcred, oneshot; + + unp = sotounpcb(so); + KASSERT(unp != NULL, ("uipc_finalizecontrol: unp == NULL")); + + if (*pcontrol != NULL && (error = unp_internalize(pcontrol, td))) + return (error); + + UNP_LINK_RLOCK(); + unp2 = unp->unp_conn; + UNP_LINK_RUNLOCK(); + + /* + * If not connected, we're done now (might be auto-connect + * on send, leave everything to caller). Otherwise, handle + * one-shot credentials on stream and seqpacket sockets here. + * + * XXX If the send fails, we never get another chance. + * We could restore UNP_WANTCRED if the unp_addsockcred() + * call fails here but we can't handle the more likely + * entire-send-fails case. Deferring clearing the flag + * is not a great solution either. Perhaps best would be + * to have an additional UNP_CREDS_SENT_SUCCESSFULLY flag + * and check that here. For now, just leave it this way. + */ + if (unp2 == NULL) + return (0); + + oneshot = so->so_type == SOCK_SEQPACKET || + so->so_type == SOCK_STREAM; + if (oneshot) { + UNP_PCB_LOCK(unp2); + wantcred = (unp2->unp_flags & UNP_WANTCRED) != 0; + unp2->unp_flags &= ~UNP_WANTCRED; + UNP_PCB_UNLOCK(unp2); + } else { + wantcred = true; + } + + if (wantcred) + error = unp_addsockcred(pcontrol, td); + + return (error); +} + +static int uipc_rcvd(struct socket *so, int flags) { struct unpcb *unp, *unp2; @@ -868,8 +919,6 @@ error = EOPNOTSUPP; goto release; } - if (control != NULL && (error = unp_internalize(&control, td))) - goto release; if ((nam != NULL) || (flags & PRUS_EOF)) UNP_LINK_WLOCK(); else @@ -903,9 +952,6 @@ error = ENOTCONN; break; } - /* Lockless read. */ - if (unp2->unp_flags & UNP_WANTCRED) - control = unp_addsockcred(td, control); UNP_PCB_LOCK(unp); if (unp->unp_addr != NULL) from = (struct sockaddr *)unp->unp_addr; @@ -973,14 +1019,6 @@ so2 = unp2->unp_socket; UNP_PCB_LOCK(unp2); SOCKBUF_LOCK(&so2->so_rcv); - if (unp2->unp_flags & UNP_WANTCRED) { - /* - * Credentials are passed only once on SOCK_STREAM - * and SOCK_SEQPACKET. - */ - unp2->unp_flags &= ~UNP_WANTCRED; - control = unp_addsockcred(td, control); - } /* * Send to paired receive port, and then reduce send buffer * hiwater marks to maintain backpressure. Wake up readers. @@ -988,9 +1026,9 @@ switch (so->so_type) { case SOCK_STREAM: if (control != NULL) { - if (sbappendcontrol_locked(&so2->so_rcv, m, - control)) - control = NULL; + sbappendcontrol_locked(&so2->so_rcv, m, + control); + control = NULL; } else sbappend_locked(&so2->so_rcv, m, flags); break; @@ -1011,6 +1049,7 @@ break; } } + m = NULL; mbcnt = so2->so_rcv.sb_mbcnt; sbcc = sbavail(&so2->so_rcv); @@ -1031,7 +1070,6 @@ so->so_snd.sb_flags |= SB_STOP; SOCKBUF_UNLOCK(&so->so_snd); UNP_PCB_UNLOCK(unp2); - m = NULL; break; } @@ -1164,6 +1202,7 @@ .pru_disconnect = uipc_disconnect, .pru_listen = uipc_listen, .pru_peeraddr = uipc_peeraddr, + .pru_finalizecontrol = uipc_finalizecontrol, .pru_rcvd = uipc_rcvd, .pru_send = uipc_send, .pru_sense = uipc_sense, @@ -1186,6 +1225,7 @@ .pru_disconnect = uipc_disconnect, .pru_listen = uipc_listen, .pru_peeraddr = uipc_peeraddr, + .pru_finalizecontrol = uipc_finalizecontrol, .pru_rcvd = uipc_rcvd, .pru_send = uipc_send, .pru_sense = uipc_sense, @@ -1208,6 +1248,7 @@ .pru_disconnect = uipc_disconnect, .pru_listen = uipc_listen, .pru_peeraddr = uipc_peeraddr, + .pru_finalizecontrol = uipc_finalizecontrol, .pru_rcvd = uipc_rcvd, .pru_send = uipc_send, .pru_ready = uipc_ready, @@ -1808,7 +1849,7 @@ SCM_RIGHTS, SOL_SOCKET); if (*controlp == NULL) { FILEDESC_XUNLOCK(fdesc); - error = E2BIG; + error = ENOBUFS; unp_freerights(fdep, newfds); goto next; } @@ -1896,162 +1937,259 @@ UNP_DEFERRED_LOCK_INIT(); } +/* + * Arguments passed to internalizer/transformer (ix = internal xform). + * The transformation function may fill in a new ix_mbuf. + */ +struct internalize_transform_data { + socklen_t ix_odatalen; /* original data length in bytes */ + socklen_t ix_ndatalen; /* new data length, or 0 */ + void *ix_odata; /* original data */ + void *ix_ndata; /* new data area, or NULL */ + struct mbuf *ix_mbuf; /* mbuf for new data */ + struct thread *ix_td; /* calling thread */ +}; + +/* + * Internalizers. If you provide a nonzero size, we pre-allocate + * the ix_mbuf and you get a nonzero ndatasize and non-NULL ndata. + */ +struct unp_scm_internalize_op { + socklen_t size; /* predefined output size, or 0 */ + int (*func)(struct internalize_transform_data *); +}; + +static int unp_internalize_creds(struct internalize_transform_data *); +static int unp_internalize_fds(struct internalize_transform_data *); +static int unp_internalize_timestamp(struct internalize_transform_data *); +static int unp_internalize_bintime(struct internalize_transform_data *); + +static struct unp_scm_internalize_op unp_internalize_ops[] = { + [SCM_CREDS] = { sizeof(struct cmsgcred), unp_internalize_creds }, + [SCM_RIGHTS] = { 0, unp_internalize_fds }, + [SCM_TIMESTAMP] = { sizeof(struct timeval), unp_internalize_timestamp }, + [SCM_BINTIME] = { sizeof(struct bintime), unp_internalize_bintime }, +}; + +/* + * Convert incoming control message from user-supplied format + * to internal form. + * + * Note that when we're called, *controlp is a single mbuf + * whose m_len is the length of the cmsg data structures + * that have not yet been internalized. On return, *controlp + * is an mbuf chain whose individual mbufs are internalized; + * this chain may have a different length. + * + * Caller will always m_freem(*controlp), even if we return error. + */ static int unp_internalize(struct mbuf **controlp, struct thread *td) { - struct mbuf *control = *controlp; - struct proc *p = td->td_proc; - struct filedesc *fdesc = p->p_fd; - struct bintime *bt; - struct cmsghdr *cm = mtod(control, struct cmsghdr *); - struct cmsgcred *cmcred; - struct filedescent *fde, **fdep, *fdev; - struct file *fp; - struct timeval *tv; - int i, *fdp; - void *data; - socklen_t clen = control->m_len, datalen; - int error, oldfds; - u_int newlen; + struct unp_scm_internalize_op *op; + struct internalize_transform_data ix; + struct cmsghdr *cm; + struct mbuf *control, *m; + void *odata; + int cmtype, error; + socklen_t clen, size, odatalen; UNP_LINK_UNLOCK_ASSERT(); + ix.ix_td = td; /* never changes, just passed through */ + error = 0; + control = *controlp; *controlp = NULL; - while (cm != NULL) { - if (sizeof(*cm) > clen || cm->cmsg_level != SOL_SOCKET - || cm->cmsg_len > clen || cm->cmsg_len < sizeof(*cm)) { - error = EINVAL; - goto out; - } - data = CMSG_DATA(cm); - datalen = (caddr_t)cm + cm->cmsg_len - (caddr_t)data; + clen = control->m_len; + cm = mtod(control, struct cmsghdr *); - switch (cm->cmsg_type) { + while (error == 0) { /* - * Fill in credential information. + * Verify current control message, and set up type + * and old data pointer and size values. */ - case SCM_CREDS: - *controlp = sbcreatecontrol(NULL, sizeof(*cmcred), - SCM_CREDS, SOL_SOCKET); - if (*controlp == NULL) { - error = ENOBUFS; - goto out; - } - cmcred = (struct cmsgcred *) - CMSG_DATA(mtod(*controlp, struct cmsghdr *)); - cmcred->cmcred_pid = p->p_pid; - cmcred->cmcred_uid = td->td_ucred->cr_ruid; - cmcred->cmcred_gid = td->td_ucred->cr_rgid; - cmcred->cmcred_euid = td->td_ucred->cr_uid; - cmcred->cmcred_ngroups = MIN(td->td_ucred->cr_ngroups, - CMGROUP_MAX); - for (i = 0; i < cmcred->cmcred_ngroups; i++) - cmcred->cmcred_groups[i] = - td->td_ucred->cr_groups[i]; + if (clen < sizeof(*cm) || cm->cmsg_level != SOL_SOCKET || + cm->cmsg_len > clen || cm->cmsg_len < sizeof(*cm)) { + error = EINVAL; break; + } - case SCM_RIGHTS: - oldfds = datalen / sizeof (int); - if (oldfds == 0) - break; - /* - * Check that all the FDs passed in refer to legal - * files. If not, reject the entire operation. - */ - fdp = data; - FILEDESC_SLOCK(fdesc); - for (i = 0; i < oldfds; i++, fdp++) { - fp = fget_locked(fdesc, *fdp); - if (fp == NULL) { - FILEDESC_SUNLOCK(fdesc); - error = EBADF; - goto out; - } - if (!(fp->f_ops->fo_flags & DFLAG_PASSABLE)) { - FILEDESC_SUNLOCK(fdesc); - error = EOPNOTSUPP; - goto out; - } + cmtype = cm->cmsg_type; + if (cmtype < 0 || cmtype >= nitems(unp_internalize_ops)) { + error = EINVAL; + break; + } + op = &unp_internalize_ops[cmtype]; + if (op->func == NULL) { + error = EINVAL; + break; + } - } + odata = CMSG_DATA(cm); + odatalen = (caddr_t)cm + cm->cmsg_len - (caddr_t)odata; - /* - * Now replace the integer FDs with pointers to the - * file structure and capability rights. - */ - newlen = oldfds * sizeof(fdep[0]); - *controlp = sbcreatecontrol(NULL, newlen, - SCM_RIGHTS, SOL_SOCKET); - if (*controlp == NULL) { - FILEDESC_SUNLOCK(fdesc); - error = E2BIG; - goto out; - } - fdp = data; - fdep = (struct filedescent **) - CMSG_DATA(mtod(*controlp, struct cmsghdr *)); - fdev = malloc(sizeof(*fdev) * oldfds, M_FILECAPS, - M_WAITOK); - for (i = 0; i < oldfds; i++, fdev++, fdp++) { - fde = &fdesc->fd_ofiles[*fdp]; - fdep[i] = fdev; - fdep[i]->fde_file = fde->fde_file; - filecaps_copy(&fde->fde_caps, - &fdep[i]->fde_caps, true); - unp_internalize_fp(fdep[i]->fde_file); - } - FILEDESC_SUNLOCK(fdesc); - break; + ix.ix_odata = odata; + ix.ix_odatalen = odatalen; - case SCM_TIMESTAMP: - *controlp = sbcreatecontrol(NULL, sizeof(*tv), - SCM_TIMESTAMP, SOL_SOCKET); - if (*controlp == NULL) { + /* + * If transform function gives us a fixed data + * size, allocate new mbuf here, else leave it + * to the function. + */ + if ((size = op->size) != 0) { + m = sbcreatecontrol(NULL, size, cmtype, SOL_SOCKET); + if (m == NULL) { error = ENOBUFS; - goto out; + break; } - tv = (struct timeval *) - CMSG_DATA(mtod(*controlp, struct cmsghdr *)); - microtime(tv); - break; + ix.ix_mbuf = m; + ix.ix_ndata = CMSG_DATA(mtod(m, struct cmsghdr *)); + ix.ix_ndatalen = size; + } else { + ix.ix_mbuf = NULL; + ix.ix_ndata = NULL; + ix.ix_ndatalen = 0; + } - case SCM_BINTIME: - *controlp = sbcreatecontrol(NULL, sizeof(*bt), - SCM_BINTIME, SOL_SOCKET); - if (*controlp == NULL) { - error = ENOBUFS; - goto out; - } - bt = (struct bintime *) - CMSG_DATA(mtod(*controlp, struct cmsghdr *)); - bintime(bt); - break; - - default: - error = EINVAL; - goto out; + /* + * Apply transform and append new mbuf (if any) to + * new control chain, even on error, so that it + * will get freed. + */ + error = (*op->func)(&ix); + if ((m = ix.ix_mbuf) != NULL) { + *controlp = m; + controlp = &m->m_next; } - controlp = &(*controlp)->m_next; - if (CMSG_SPACE(datalen) < clen) { - clen -= CMSG_SPACE(datalen); - cm = (struct cmsghdr *) - ((caddr_t)cm + CMSG_SPACE(datalen)); - } else { - clen = 0; - cm = NULL; - } + /* Advance to next message. */ + size = CMSG_SPACE(odatalen); + if (size >= clen) + break; + cm = (struct cmsghdr *)((caddr_t)cm + size); + clen -= size; } -out: m_freem(control); return (error); } -static struct mbuf * -unp_addsockcred(struct thread *td, struct mbuf *control) +/* + * Internalize file descriptors ("rights"). + */ +static int +unp_internalize_fds(struct internalize_transform_data *ix) { + struct proc *p = ix->ix_td->td_proc; + struct filedesc *fdesc = p->p_fd; + struct filedescent *fde, **fdep, *fdev; + struct file *fp; + struct mbuf *m; + int i, *fdp; + int oldfds; + u_int newlen; + + KASSERT(ix->ix_ndatalen == 0, ("%s: datalen", __func__)); + + /* Round down: this is forgiving, if slightly wrong. */ + oldfds = ix->ix_odatalen / sizeof (int); + if (oldfds == 0) + return (0); + + /* + * Check that all the FDs passed in refer to legal + * files. If not, reject the entire operation. + */ + fdp = ix->ix_odata; + FILEDESC_SLOCK(fdesc); + for (i = 0; i < oldfds; i++, fdp++) { + fp = fget_locked(fdesc, *fdp); + if (fp == NULL) { + FILEDESC_SUNLOCK(fdesc); + return (EBADF); + } + if (!(fp->f_ops->fo_flags & DFLAG_PASSABLE)) { + FILEDESC_SUNLOCK(fdesc); + return (EOPNOTSUPP); + } + + } + + /* + * Now replace the integer FDs with pointers to the + * file structure and capability rights. + */ + newlen = oldfds * sizeof(fdep[0]); + m = sbcreatecontrol(NULL, newlen, SCM_RIGHTS, SOL_SOCKET); + if (m == NULL) { + FILEDESC_SUNLOCK(fdesc); + return (ENOBUFS); + } + + fdp = ix->ix_odata; + fdep = (struct filedescent **)CMSG_DATA(mtod(m, struct cmsghdr *)); + fdev = malloc(sizeof(*fdev) * oldfds, M_FILECAPS, M_WAITOK); + for (i = 0; i < oldfds; i++, fdev++, fdp++) { + fde = &fdesc->fd_ofiles[*fdp]; + fdep[i] = fdev; + fdep[i]->fde_file = fde->fde_file; + filecaps_copy(&fde->fde_caps, &fdep[i]->fde_caps, true); + unp_internalize_fp(fdep[i]->fde_file); + } + FILEDESC_SUNLOCK(fdesc); + + ix->ix_mbuf = m; + return (0); +} + +static int +unp_internalize_creds(struct internalize_transform_data *ix) +{ + struct cmsgcred *cmcred; + int i, ngroups; + struct thread *td = ix->ix_td; + struct ucred *cr = td->td_ucred; + + KASSERT(ix->ix_ndatalen == sizeof(*cmcred), ("%s: datalen", __func__)); + cmcred = ix->ix_ndata; + cmcred->cmcred_pid = td->td_proc->p_pid; + cmcred->cmcred_uid = cr->cr_ruid; + cmcred->cmcred_gid = cr->cr_rgid; + cmcred->cmcred_euid = cr->cr_uid; + ngroups = MIN(cr->cr_ngroups, CMGROUP_MAX); + cmcred->cmcred_ngroups = ngroups; + for (i = 0; i < ngroups; i++) + cmcred->cmcred_groups[i] = cr->cr_groups[i]; + return (0); +} + +static int +unp_internalize_timestamp(struct internalize_transform_data *ix) +{ + struct timeval *tv; + + KASSERT(ix->ix_ndatalen == sizeof(*tv), ("%s: datalen", __func__)); + tv = ix->ix_ndata; + microtime(tv); + return (0); +} + +static int +unp_internalize_bintime(struct internalize_transform_data *ix) +{ + struct bintime *bt; + + KASSERT(ix->ix_ndatalen == sizeof(*bt), ("%s: datalen", __func__)); + bt = ix->ix_ndata; + bintime(bt); + return (0); +} + +static int +unp_addsockcred(struct mbuf **pcontrol, struct thread *td) +{ + struct mbuf *control = *pcontrol; struct mbuf *m, *n, *n_prev; struct sockcred *sc; const struct cmsghdr *cm; @@ -2061,7 +2199,7 @@ ngroups = MIN(td->td_ucred->cr_ngroups, CMGROUP_MAX); m = sbcreatecontrol(NULL, SOCKCREDSIZE(ngroups), SCM_CREDS, SOL_SOCKET); if (m == NULL) - return (control); + return (ENOBUFS); sc = (struct sockcred *) CMSG_DATA(mtod(m, struct cmsghdr *)); sc->sc_uid = td->td_ucred->cr_ruid; @@ -2095,7 +2233,8 @@ /* Prepend it to the head. */ m->m_next = control; - return (m); + *pcontrol = m; + return (0); } static struct unpcb * Index: sys/sys/protosw.h =================================================================== --- sys/sys/protosw.h (revision 336040) +++ sys/sys/protosw.h (working copy) @@ -199,6 +199,8 @@ int (*pru_listen)(struct socket *so, int backlog, struct thread *td); int (*pru_peeraddr)(struct socket *so, struct sockaddr **nam); + int (*pru_finalizecontrol)(struct socket *so, int flags, struct mbuf **pcontrol, + struct thread *td); int (*pru_rcvd)(struct socket *so, int flags); int (*pru_rcvoob)(struct socket *so, struct mbuf *m, int flags); int (*pru_send)(struct socket *so, int flags, struct mbuf *m, Index: sys/sys/sockbuf.h =================================================================== --- sys/sys/sockbuf.h (revision 336040) +++ sys/sys/sockbuf.h (working copy) @@ -146,9 +146,9 @@ struct mbuf *m0, struct mbuf *control); int sbappendaddr_nospacecheck_locked(struct sockbuf *sb, const struct sockaddr *asa, struct mbuf *m0, struct mbuf *control); -int sbappendcontrol(struct sockbuf *sb, struct mbuf *m0, +void sbappendcontrol(struct sockbuf *sb, struct mbuf *m0, struct mbuf *control); -int sbappendcontrol_locked(struct sockbuf *sb, struct mbuf *m0, +void sbappendcontrol_locked(struct sockbuf *sb, struct mbuf *m0, struct mbuf *control); void sbappendrecord(struct sockbuf *sb, struct mbuf *m0); void sbappendrecord_locked(struct sockbuf *sb, struct mbuf *m0);