diff options
author | rwatson <rwatson@FreeBSD.org> | 2005-02-18 18:54:42 +0000 |
---|---|---|
committer | rwatson <rwatson@FreeBSD.org> | 2005-02-18 18:54:42 +0000 |
commit | cb47ade08f79b52cb97c0c7593f4925607de5733 (patch) | |
tree | e4c1aa170f49c89091c87f3460a610ff5bb97f5b /sys/kern/uipc_accf.c | |
parent | de4599e9a3c0761274ac2851028761ea59ba8f06 (diff) | |
download | FreeBSD-src-cb47ade08f79b52cb97c0c7593f4925607de5733.zip FreeBSD-src-cb47ade08f79b52cb97c0c7593f4925607de5733.tar.gz |
Move do_setopt_accept_filter() from uipc_socket.c to uipc_accf.c, where
the rest of the accept filter code currently lives.
MFC after: 3 days
Diffstat (limited to 'sys/kern/uipc_accf.c')
-rw-r--r-- | sys/kern/uipc_accf.c | 120 |
1 files changed, 120 insertions, 0 deletions
diff --git a/sys/kern/uipc_accf.c b/sys/kern/uipc_accf.c index 874f75e..a764e8d 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); +} |