X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=net%2Fnetfilter%2Fnf_sockopt.c;h=8b8ece750313b5a401741564c57c4eb3b775b552;hb=cb276805803b8e0616159d80a441ab26a931ada4;hp=61a833a9caa645eae6ca837da097d1b35584b4ee;hpb=1d6ae775d7a948c9575658eb41184fd2e506c0df;p=linux-2.6 diff --git a/net/netfilter/nf_sockopt.c b/net/netfilter/nf_sockopt.c index 61a833a9ca..8b8ece7503 100644 --- a/net/netfilter/nf_sockopt.c +++ b/net/netfilter/nf_sockopt.c @@ -1,9 +1,9 @@ -#include #include #include #include #include #include +#include #include #include "nf_internals.h" @@ -11,7 +11,7 @@ /* Sockopts only registered and called from user context, so net locking would be overkill. Also, [gs]etsockopt calls may sleep. */ -static DECLARE_MUTEX(nf_sockopt_mutex); +static DEFINE_MUTEX(nf_sockopt_mutex); static LIST_HEAD(nf_sockopts); /* Do exclusive ranges overlap? */ @@ -26,19 +26,19 @@ int nf_register_sockopt(struct nf_sockopt_ops *reg) struct list_head *i; int ret = 0; - if (down_interruptible(&nf_sockopt_mutex) != 0) + if (mutex_lock_interruptible(&nf_sockopt_mutex) != 0) return -EINTR; list_for_each(i, &nf_sockopts) { struct nf_sockopt_ops *ops = (struct nf_sockopt_ops *)i; if (ops->pf == reg->pf - && (overlap(ops->set_optmin, ops->set_optmax, + && (overlap(ops->set_optmin, ops->set_optmax, reg->set_optmin, reg->set_optmax) - || overlap(ops->get_optmin, ops->get_optmax, + || overlap(ops->get_optmin, ops->get_optmax, reg->get_optmin, reg->get_optmax))) { NFDEBUG("nf_sock overlap: %u-%u/%u-%u v %u-%u/%u-%u\n", - ops->set_optmin, ops->set_optmax, - ops->get_optmin, ops->get_optmax, + ops->set_optmin, ops->set_optmax, + ops->get_optmin, ops->get_optmax, reg->set_optmin, reg->set_optmax, reg->get_optmin, reg->get_optmax); ret = -EBUSY; @@ -48,7 +48,7 @@ int nf_register_sockopt(struct nf_sockopt_ops *reg) list_add(®->list, &nf_sockopts); out: - up(&nf_sockopt_mutex); + mutex_unlock(&nf_sockopt_mutex); return ret; } EXPORT_SYMBOL(nf_register_sockopt); @@ -57,30 +57,30 @@ void nf_unregister_sockopt(struct nf_sockopt_ops *reg) { /* No point being interruptible: we're probably in cleanup_module() */ restart: - down(&nf_sockopt_mutex); + mutex_lock(&nf_sockopt_mutex); if (reg->use != 0) { /* To be woken by nf_sockopt call... */ /* FIXME: Stuart Young's name appears gratuitously. */ set_current_state(TASK_UNINTERRUPTIBLE); reg->cleanup_task = current; - up(&nf_sockopt_mutex); + mutex_unlock(&nf_sockopt_mutex); schedule(); goto restart; } list_del(®->list); - up(&nf_sockopt_mutex); + mutex_unlock(&nf_sockopt_mutex); } EXPORT_SYMBOL(nf_unregister_sockopt); /* Call get/setsockopt() */ -static int nf_sockopt(struct sock *sk, int pf, int val, +static int nf_sockopt(struct sock *sk, int pf, int val, char __user *opt, int *len, int get) { struct list_head *i; struct nf_sockopt_ops *ops; int ret; - if (down_interruptible(&nf_sockopt_mutex) != 0) + if (mutex_lock_interruptible(&nf_sockopt_mutex) != 0) return -EINTR; list_for_each(i, &nf_sockopts) { @@ -90,7 +90,7 @@ static int nf_sockopt(struct sock *sk, int pf, int val, if (val >= ops->get_optmin && val < ops->get_optmax) { ops->use++; - up(&nf_sockopt_mutex); + mutex_unlock(&nf_sockopt_mutex); ret = ops->get(sk, val, opt, len); goto out; } @@ -98,22 +98,22 @@ static int nf_sockopt(struct sock *sk, int pf, int val, if (val >= ops->set_optmin && val < ops->set_optmax) { ops->use++; - up(&nf_sockopt_mutex); + mutex_unlock(&nf_sockopt_mutex); ret = ops->set(sk, val, opt, *len); goto out; } } } } - up(&nf_sockopt_mutex); + mutex_unlock(&nf_sockopt_mutex); return -ENOPROTOOPT; - + out: - down(&nf_sockopt_mutex); + mutex_lock(&nf_sockopt_mutex); ops->use--; if (ops->cleanup_task) wake_up_process(ops->cleanup_task); - up(&nf_sockopt_mutex); + mutex_unlock(&nf_sockopt_mutex); return ret; } @@ -130,3 +130,72 @@ int nf_getsockopt(struct sock *sk, int pf, int val, char __user *opt, int *len) } EXPORT_SYMBOL(nf_getsockopt); +#ifdef CONFIG_COMPAT +static int compat_nf_sockopt(struct sock *sk, int pf, int val, + char __user *opt, int *len, int get) +{ + struct list_head *i; + struct nf_sockopt_ops *ops; + int ret; + + if (mutex_lock_interruptible(&nf_sockopt_mutex) != 0) + return -EINTR; + + list_for_each(i, &nf_sockopts) { + ops = (struct nf_sockopt_ops *)i; + if (ops->pf == pf) { + if (get) { + if (val >= ops->get_optmin + && val < ops->get_optmax) { + ops->use++; + mutex_unlock(&nf_sockopt_mutex); + if (ops->compat_get) + ret = ops->compat_get(sk, + val, opt, len); + else + ret = ops->get(sk, + val, opt, len); + goto out; + } + } else { + if (val >= ops->set_optmin + && val < ops->set_optmax) { + ops->use++; + mutex_unlock(&nf_sockopt_mutex); + if (ops->compat_set) + ret = ops->compat_set(sk, + val, opt, *len); + else + ret = ops->set(sk, + val, opt, *len); + goto out; + } + } + } + } + mutex_unlock(&nf_sockopt_mutex); + return -ENOPROTOOPT; + + out: + mutex_lock(&nf_sockopt_mutex); + ops->use--; + if (ops->cleanup_task) + wake_up_process(ops->cleanup_task); + mutex_unlock(&nf_sockopt_mutex); + return ret; +} + +int compat_nf_setsockopt(struct sock *sk, int pf, + int val, char __user *opt, int len) +{ + return compat_nf_sockopt(sk, pf, val, opt, &len, 0); +} +EXPORT_SYMBOL(compat_nf_setsockopt); + +int compat_nf_getsockopt(struct sock *sk, int pf, + int val, char __user *opt, int *len) +{ + return compat_nf_sockopt(sk, pf, val, opt, len, 1); +} +EXPORT_SYMBOL(compat_nf_getsockopt); +#endif