nf_sockopt.c (2849B)
1// SPDX-License-Identifier: GPL-2.0 2#include <linux/kernel.h> 3#include <linux/init.h> 4#include <linux/module.h> 5#include <linux/skbuff.h> 6#include <linux/netfilter.h> 7#include <linux/mutex.h> 8#include <net/sock.h> 9 10#include "nf_internals.h" 11 12/* Sockopts only registered and called from user context, so 13 net locking would be overkill. Also, [gs]etsockopt calls may 14 sleep. */ 15static DEFINE_MUTEX(nf_sockopt_mutex); 16static LIST_HEAD(nf_sockopts); 17 18/* Do exclusive ranges overlap? */ 19static inline int overlap(int min1, int max1, int min2, int max2) 20{ 21 return max1 > min2 && min1 < max2; 22} 23 24/* Functions to register sockopt ranges (exclusive). */ 25int nf_register_sockopt(struct nf_sockopt_ops *reg) 26{ 27 struct nf_sockopt_ops *ops; 28 int ret = 0; 29 30 mutex_lock(&nf_sockopt_mutex); 31 list_for_each_entry(ops, &nf_sockopts, list) { 32 if (ops->pf == reg->pf 33 && (overlap(ops->set_optmin, ops->set_optmax, 34 reg->set_optmin, reg->set_optmax) 35 || overlap(ops->get_optmin, ops->get_optmax, 36 reg->get_optmin, reg->get_optmax))) { 37 pr_debug("nf_sock overlap: %u-%u/%u-%u v %u-%u/%u-%u\n", 38 ops->set_optmin, ops->set_optmax, 39 ops->get_optmin, ops->get_optmax, 40 reg->set_optmin, reg->set_optmax, 41 reg->get_optmin, reg->get_optmax); 42 ret = -EBUSY; 43 goto out; 44 } 45 } 46 47 list_add(®->list, &nf_sockopts); 48out: 49 mutex_unlock(&nf_sockopt_mutex); 50 return ret; 51} 52EXPORT_SYMBOL(nf_register_sockopt); 53 54void nf_unregister_sockopt(struct nf_sockopt_ops *reg) 55{ 56 mutex_lock(&nf_sockopt_mutex); 57 list_del(®->list); 58 mutex_unlock(&nf_sockopt_mutex); 59} 60EXPORT_SYMBOL(nf_unregister_sockopt); 61 62static struct nf_sockopt_ops *nf_sockopt_find(struct sock *sk, u_int8_t pf, 63 int val, int get) 64{ 65 struct nf_sockopt_ops *ops; 66 67 mutex_lock(&nf_sockopt_mutex); 68 list_for_each_entry(ops, &nf_sockopts, list) { 69 if (ops->pf == pf) { 70 if (!try_module_get(ops->owner)) 71 goto out_nosup; 72 73 if (get) { 74 if (val >= ops->get_optmin && 75 val < ops->get_optmax) 76 goto out; 77 } else { 78 if (val >= ops->set_optmin && 79 val < ops->set_optmax) 80 goto out; 81 } 82 module_put(ops->owner); 83 } 84 } 85out_nosup: 86 ops = ERR_PTR(-ENOPROTOOPT); 87out: 88 mutex_unlock(&nf_sockopt_mutex); 89 return ops; 90} 91 92int nf_setsockopt(struct sock *sk, u_int8_t pf, int val, sockptr_t opt, 93 unsigned int len) 94{ 95 struct nf_sockopt_ops *ops; 96 int ret; 97 98 ops = nf_sockopt_find(sk, pf, val, 0); 99 if (IS_ERR(ops)) 100 return PTR_ERR(ops); 101 ret = ops->set(sk, val, opt, len); 102 module_put(ops->owner); 103 return ret; 104} 105EXPORT_SYMBOL(nf_setsockopt); 106 107int nf_getsockopt(struct sock *sk, u_int8_t pf, int val, char __user *opt, 108 int *len) 109{ 110 struct nf_sockopt_ops *ops; 111 int ret; 112 113 ops = nf_sockopt_find(sk, pf, val, 1); 114 if (IS_ERR(ops)) 115 return PTR_ERR(ops); 116 ret = ops->get(sk, val, opt, len); 117 module_put(ops->owner); 118 return ret; 119} 120EXPORT_SYMBOL(nf_getsockopt);