4de6f16b9e
From kernel perspective, allow entrance in nf_hook_slow(). Stuff which uses nf_register_hook/nf_register_hooks, but otherwise not netns-ready: DECnet netfilter ipt_CLUSTERIP nf_nat_standalone.c together with XFRM (?) IPVS several individual match modules (like hashlimit) ctnetlink NOTRACK all sorts of queueing and reporting to userspace L3 and L4 protocol sysctls, bridge sysctls probably something else Anyway critical mass has been achieved, there is no reason to hide netfilter any longer. From userspace perspective, allow to manipulate all sorts of iptables/ip6tables/arptables rules. Signed-off-by: Alexey Dobriyan <adobriyan@gmail.com> Signed-off-by: Patrick McHardy <kaber@trash.net>
170 lines
3.8 KiB
C
170 lines
3.8 KiB
C
#include <linux/kernel.h>
|
|
#include <linux/init.h>
|
|
#include <linux/module.h>
|
|
#include <linux/skbuff.h>
|
|
#include <linux/netfilter.h>
|
|
#include <linux/mutex.h>
|
|
#include <net/sock.h>
|
|
|
|
#include "nf_internals.h"
|
|
|
|
/* Sockopts only registered and called from user context, so
|
|
net locking would be overkill. Also, [gs]etsockopt calls may
|
|
sleep. */
|
|
static DEFINE_MUTEX(nf_sockopt_mutex);
|
|
static LIST_HEAD(nf_sockopts);
|
|
|
|
/* Do exclusive ranges overlap? */
|
|
static inline int overlap(int min1, int max1, int min2, int max2)
|
|
{
|
|
return max1 > min2 && min1 < max2;
|
|
}
|
|
|
|
/* Functions to register sockopt ranges (exclusive). */
|
|
int nf_register_sockopt(struct nf_sockopt_ops *reg)
|
|
{
|
|
struct nf_sockopt_ops *ops;
|
|
int ret = 0;
|
|
|
|
if (mutex_lock_interruptible(&nf_sockopt_mutex) != 0)
|
|
return -EINTR;
|
|
|
|
list_for_each_entry(ops, &nf_sockopts, list) {
|
|
if (ops->pf == reg->pf
|
|
&& (overlap(ops->set_optmin, ops->set_optmax,
|
|
reg->set_optmin, reg->set_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,
|
|
reg->set_optmin, reg->set_optmax,
|
|
reg->get_optmin, reg->get_optmax);
|
|
ret = -EBUSY;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
list_add(®->list, &nf_sockopts);
|
|
out:
|
|
mutex_unlock(&nf_sockopt_mutex);
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL(nf_register_sockopt);
|
|
|
|
void nf_unregister_sockopt(struct nf_sockopt_ops *reg)
|
|
{
|
|
mutex_lock(&nf_sockopt_mutex);
|
|
list_del(®->list);
|
|
mutex_unlock(&nf_sockopt_mutex);
|
|
}
|
|
EXPORT_SYMBOL(nf_unregister_sockopt);
|
|
|
|
static struct nf_sockopt_ops *nf_sockopt_find(struct sock *sk, u_int8_t pf,
|
|
int val, int get)
|
|
{
|
|
struct nf_sockopt_ops *ops;
|
|
|
|
if (mutex_lock_interruptible(&nf_sockopt_mutex) != 0)
|
|
return ERR_PTR(-EINTR);
|
|
|
|
list_for_each_entry(ops, &nf_sockopts, list) {
|
|
if (ops->pf == pf) {
|
|
if (!try_module_get(ops->owner))
|
|
goto out_nosup;
|
|
|
|
if (get) {
|
|
if (val >= ops->get_optmin &&
|
|
val < ops->get_optmax)
|
|
goto out;
|
|
} else {
|
|
if (val >= ops->set_optmin &&
|
|
val < ops->set_optmax)
|
|
goto out;
|
|
}
|
|
module_put(ops->owner);
|
|
}
|
|
}
|
|
out_nosup:
|
|
ops = ERR_PTR(-ENOPROTOOPT);
|
|
out:
|
|
mutex_unlock(&nf_sockopt_mutex);
|
|
return ops;
|
|
}
|
|
|
|
/* Call get/setsockopt() */
|
|
static int nf_sockopt(struct sock *sk, u_int8_t pf, int val,
|
|
char __user *opt, int *len, int get)
|
|
{
|
|
struct nf_sockopt_ops *ops;
|
|
int ret;
|
|
|
|
ops = nf_sockopt_find(sk, pf, val, get);
|
|
if (IS_ERR(ops))
|
|
return PTR_ERR(ops);
|
|
|
|
if (get)
|
|
ret = ops->get(sk, val, opt, len);
|
|
else
|
|
ret = ops->set(sk, val, opt, *len);
|
|
|
|
module_put(ops->owner);
|
|
return ret;
|
|
}
|
|
|
|
int nf_setsockopt(struct sock *sk, u_int8_t pf, int val, char __user *opt,
|
|
int len)
|
|
{
|
|
return nf_sockopt(sk, pf, val, opt, &len, 0);
|
|
}
|
|
EXPORT_SYMBOL(nf_setsockopt);
|
|
|
|
int nf_getsockopt(struct sock *sk, u_int8_t pf, int val, char __user *opt,
|
|
int *len)
|
|
{
|
|
return nf_sockopt(sk, pf, val, opt, len, 1);
|
|
}
|
|
EXPORT_SYMBOL(nf_getsockopt);
|
|
|
|
#ifdef CONFIG_COMPAT
|
|
static int compat_nf_sockopt(struct sock *sk, u_int8_t pf, int val,
|
|
char __user *opt, int *len, int get)
|
|
{
|
|
struct nf_sockopt_ops *ops;
|
|
int ret;
|
|
|
|
ops = nf_sockopt_find(sk, pf, val, get);
|
|
if (IS_ERR(ops))
|
|
return PTR_ERR(ops);
|
|
|
|
if (get) {
|
|
if (ops->compat_get)
|
|
ret = ops->compat_get(sk, val, opt, len);
|
|
else
|
|
ret = ops->get(sk, val, opt, len);
|
|
} else {
|
|
if (ops->compat_set)
|
|
ret = ops->compat_set(sk, val, opt, *len);
|
|
else
|
|
ret = ops->set(sk, val, opt, *len);
|
|
}
|
|
|
|
module_put(ops->owner);
|
|
return ret;
|
|
}
|
|
|
|
int compat_nf_setsockopt(struct sock *sk, u_int8_t 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, u_int8_t pf,
|
|
int val, char __user *opt, int *len)
|
|
{
|
|
return compat_nf_sockopt(sk, pf, val, opt, len, 1);
|
|
}
|
|
EXPORT_SYMBOL(compat_nf_getsockopt);
|
|
#endif
|