diff --git a/sys/kern/uipc_accf.c b/sys/kern/uipc_accf.c index 874f75e981cf..a764e8d5b1a1 100644 --- a/sys/kern/uipc_accf.c +++ b/sys/kern/uipc_accf.c @@ -160,3 +160,123 @@ accept_filt_generic_mod_event(module_t mod, int event, void *data) return (error); } + +int +do_setopt_accept_filter(so, sopt) + struct socket *so; + struct sockopt *sopt; +{ + struct accept_filter_arg *afap; + struct accept_filter *afp; + struct so_accf *newaf; + int error = 0; + + newaf = NULL; + afap = NULL; + + /* + * XXXRW: Configuring accept filters should be an atomic test-and-set + * operation to prevent races during setup and attach. There may be + * more general issues of racing and ordering here that are not yet + * addressed by locking. + */ + /* do not set/remove accept filters on non listen sockets */ + SOCK_LOCK(so); + if ((so->so_options & SO_ACCEPTCONN) == 0) { + SOCK_UNLOCK(so); + return (EINVAL); + } + + /* removing the filter */ + if (sopt == NULL) { + if (so->so_accf != NULL) { + struct so_accf *af = so->so_accf; + if (af->so_accept_filter != NULL && + af->so_accept_filter->accf_destroy != NULL) { + af->so_accept_filter->accf_destroy(so); + } + if (af->so_accept_filter_str != NULL) { + FREE(af->so_accept_filter_str, M_ACCF); + } + FREE(af, M_ACCF); + so->so_accf = NULL; + } + so->so_options &= ~SO_ACCEPTFILTER; + SOCK_UNLOCK(so); + return (0); + } + SOCK_UNLOCK(so); + + /*- + * Adding a filter. + * + * Do memory allocation, copyin, and filter lookup now while we're + * not holding any locks. Avoids sleeping with a mutex, as well as + * introducing a lock order between accept filter locks and socket + * locks here. + */ + MALLOC(afap, struct accept_filter_arg *, sizeof(*afap), M_TEMP, + M_WAITOK); + /* don't put large objects on the kernel stack */ + error = sooptcopyin(sopt, afap, sizeof *afap, sizeof *afap); + afap->af_name[sizeof(afap->af_name)-1] = '\0'; + afap->af_arg[sizeof(afap->af_arg)-1] = '\0'; + if (error) { + FREE(afap, M_TEMP); + return (error); + } + afp = accept_filt_get(afap->af_name); + if (afp == NULL) { + FREE(afap, M_TEMP); + return (ENOENT); + } + + /* + * Allocate the new accept filter instance storage. We may have to + * free it again later if we fail to attach it. If attached + * properly, 'newaf' is NULLed to avoid a free() while in use. + */ + MALLOC(newaf, struct so_accf *, sizeof(*newaf), M_ACCF, M_WAITOK | + M_ZERO); + if (afp->accf_create != NULL && afap->af_name[0] != '\0') { + int len = strlen(afap->af_name) + 1; + MALLOC(newaf->so_accept_filter_str, char *, len, M_ACCF, + M_WAITOK); + strcpy(newaf->so_accept_filter_str, afap->af_name); + } + + SOCK_LOCK(so); + /* must remove previous filter first */ + if (so->so_accf != NULL) { + error = EINVAL; + goto out; + } + /* + * Invoke the accf_create() method of the filter if required. + * XXXRW: the socket mutex is held over this call, so the create + * method cannot block. This may be something we have to change, but + * it would require addressing possible races. + */ + if (afp->accf_create != NULL) { + newaf->so_accept_filter_arg = + afp->accf_create(so, afap->af_arg); + if (newaf->so_accept_filter_arg == NULL) { + error = EINVAL; + goto out; + } + } + newaf->so_accept_filter = afp; + so->so_accf = newaf; + so->so_options |= SO_ACCEPTFILTER; + newaf = NULL; +out: + SOCK_UNLOCK(so); + if (newaf != NULL) { + if (newaf->so_accept_filter_str != NULL) + FREE(newaf->so_accept_filter_str, M_ACCF); + FREE(newaf, M_ACCF); + } + if (afap != NULL) + FREE(afap, M_TEMP); + return (error); +} diff --git a/sys/kern/uipc_socket.c b/sys/kern/uipc_socket.c index c66db4c255e0..7fbf6fdd33d3 100644 --- a/sys/kern/uipc_socket.c +++ b/sys/kern/uipc_socket.c @@ -68,10 +68,6 @@ __FBSDID("$FreeBSD$"); static int soreceive_rcvoob(struct socket *so, struct uio *uio, int flags); -#ifdef INET -static int do_setopt_accept_filter(struct socket *so, struct sockopt *sopt); -#endif - static void filt_sordetach(struct knote *kn); static int filt_soread(struct knote *kn, long hint); static void filt_sowdetach(struct knote *kn); @@ -1463,128 +1459,6 @@ sorflush(so) SOCKBUF_LOCK_DESTROY(&asb); } -#ifdef INET -static int -do_setopt_accept_filter(so, sopt) - struct socket *so; - struct sockopt *sopt; -{ - struct accept_filter_arg *afap; - struct accept_filter *afp; - struct so_accf *newaf; - int error = 0; - - newaf = NULL; - afap = NULL; - - /* - * XXXRW: Configuring accept filters should be an atomic test-and-set - * operation to prevent races during setup and attach. There may be - * more general issues of racing and ordering here that are not yet - * addressed by locking. - */ - /* do not set/remove accept filters on non listen sockets */ - SOCK_LOCK(so); - if ((so->so_options & SO_ACCEPTCONN) == 0) { - SOCK_UNLOCK(so); - return (EINVAL); - } - - /* removing the filter */ - if (sopt == NULL) { - if (so->so_accf != NULL) { - struct so_accf *af = so->so_accf; - if (af->so_accept_filter != NULL && - af->so_accept_filter->accf_destroy != NULL) { - af->so_accept_filter->accf_destroy(so); - } - if (af->so_accept_filter_str != NULL) { - FREE(af->so_accept_filter_str, M_ACCF); - } - FREE(af, M_ACCF); - so->so_accf = NULL; - } - so->so_options &= ~SO_ACCEPTFILTER; - SOCK_UNLOCK(so); - return (0); - } - SOCK_UNLOCK(so); - - /*- - * Adding a filter. - * - * Do memory allocation, copyin, and filter lookup now while we're - * not holding any locks. Avoids sleeping with a mutex, as well as - * introducing a lock order between accept filter locks and socket - * locks here. - */ - MALLOC(afap, struct accept_filter_arg *, sizeof(*afap), M_TEMP, - M_WAITOK); - /* don't put large objects on the kernel stack */ - error = sooptcopyin(sopt, afap, sizeof *afap, sizeof *afap); - afap->af_name[sizeof(afap->af_name)-1] = '\0'; - afap->af_arg[sizeof(afap->af_arg)-1] = '\0'; - if (error) { - FREE(afap, M_TEMP); - return (error); - } - afp = accept_filt_get(afap->af_name); - if (afp == NULL) { - FREE(afap, M_TEMP); - return (ENOENT); - } - - /* - * Allocate the new accept filter instance storage. We may have to - * free it again later if we fail to attach it. If attached - * properly, 'newaf' is NULLed to avoid a free() while in use. - */ - MALLOC(newaf, struct so_accf *, sizeof(*newaf), M_ACCF, M_WAITOK | - M_ZERO); - if (afp->accf_create != NULL && afap->af_name[0] != '\0') { - int len = strlen(afap->af_name) + 1; - MALLOC(newaf->so_accept_filter_str, char *, len, M_ACCF, - M_WAITOK); - strcpy(newaf->so_accept_filter_str, afap->af_name); - } - - SOCK_LOCK(so); - /* must remove previous filter first */ - if (so->so_accf != NULL) { - error = EINVAL; - goto out; - } - /* - * Invoke the accf_create() method of the filter if required. - * XXXRW: the socket mutex is held over this call, so the create - * method cannot block. This may be something we have to change, but - * it would require addressing possible races. - */ - if (afp->accf_create != NULL) { - newaf->so_accept_filter_arg = - afp->accf_create(so, afap->af_arg); - if (newaf->so_accept_filter_arg == NULL) { - error = EINVAL; - goto out; - } - } - newaf->so_accept_filter = afp; - so->so_accf = newaf; - so->so_options |= SO_ACCEPTFILTER; - newaf = NULL; -out: - SOCK_UNLOCK(so); - if (newaf != NULL) { - if (newaf->so_accept_filter_str != NULL) - FREE(newaf->so_accept_filter_str, M_ACCF); - FREE(newaf, M_ACCF); - } - if (afap != NULL) - FREE(afap, M_TEMP); - return (error); -} -#endif /* INET */ - /* * Perhaps this routine, and sooptcopyout(), below, ought to come in * an additional variant to handle the case where the option value needs diff --git a/sys/sys/socketvar.h b/sys/sys/socketvar.h index 6403389734bc..9ac3dfe41f13 100644 --- a/sys/sys/socketvar.h +++ b/sys/sys/socketvar.h @@ -447,6 +447,7 @@ struct uio; /* * From uipc_socket and friends */ +int do_setopt_accept_filter(struct socket *so, struct sockopt *sopt); int so_setsockopt(struct socket *so, int level, int optname, void *optval, size_t optlen); int sockargs(struct mbuf **mp, caddr_t buf, int buflen, int type);