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. */ 15 static DEFINE_MUTEX(nf_sockopt_mutex); 16 static LIST_HEAD(nf_sockopts); 17 18 /* Do exclusive ranges overlap? */ 19 static 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). */ 25 int 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); 48 out: 49 mutex_unlock(&nf_sockopt_mutex); 50 return ret; 51 } 52 EXPORT_SYMBOL(nf_register_sockopt); 53 54 void 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 } 60 EXPORT_SYMBOL(nf_unregister_sockopt); 61 62 static 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 } 85 out_nosup: 86 ops = ERR_PTR(-ENOPROTOOPT); 87 out: 88 mutex_unlock(&nf_sockopt_mutex); 89 return ops; 90 } 91 92 /* Call get/setsockopt() */ 93 static int nf_sockopt(struct sock *sk, u_int8_t pf, int val, 94 char __user *opt, int *len, int get) 95 { 96 struct nf_sockopt_ops *ops; 97 int ret; 98 99 ops = nf_sockopt_find(sk, pf, val, get); 100 if (IS_ERR(ops)) 101 return PTR_ERR(ops); 102 103 if (get) 104 ret = ops->get(sk, val, opt, len); 105 else 106 ret = ops->set(sk, val, opt, *len); 107 108 module_put(ops->owner); 109 return ret; 110 } 111 112 int nf_setsockopt(struct sock *sk, u_int8_t pf, int val, char __user *opt, 113 unsigned int len) 114 { 115 return nf_sockopt(sk, pf, val, opt, &len, 0); 116 } 117 EXPORT_SYMBOL(nf_setsockopt); 118 119 int nf_getsockopt(struct sock *sk, u_int8_t pf, int val, char __user *opt, 120 int *len) 121 { 122 return nf_sockopt(sk, pf, val, opt, len, 1); 123 } 124 EXPORT_SYMBOL(nf_getsockopt); 125 126 #ifdef CONFIG_COMPAT 127 static int compat_nf_sockopt(struct sock *sk, u_int8_t pf, int val, 128 char __user *opt, int *len, int get) 129 { 130 struct nf_sockopt_ops *ops; 131 int ret; 132 133 ops = nf_sockopt_find(sk, pf, val, get); 134 if (IS_ERR(ops)) 135 return PTR_ERR(ops); 136 137 if (get) { 138 if (ops->compat_get) 139 ret = ops->compat_get(sk, val, opt, len); 140 else 141 ret = ops->get(sk, val, opt, len); 142 } else { 143 if (ops->compat_set) 144 ret = ops->compat_set(sk, val, opt, *len); 145 else 146 ret = ops->set(sk, val, opt, *len); 147 } 148 149 module_put(ops->owner); 150 return ret; 151 } 152 153 int compat_nf_setsockopt(struct sock *sk, u_int8_t pf, 154 int val, char __user *opt, unsigned int len) 155 { 156 return compat_nf_sockopt(sk, pf, val, opt, &len, 0); 157 } 158 EXPORT_SYMBOL(compat_nf_setsockopt); 159 160 int compat_nf_getsockopt(struct sock *sk, u_int8_t pf, 161 int val, char __user *opt, int *len) 162 { 163 return compat_nf_sockopt(sk, pf, val, opt, len, 1); 164 } 165 EXPORT_SYMBOL(compat_nf_getsockopt); 166 #endif 167