]>
Commit | Line | Data |
---|---|---|
b2441318 | 1 | // SPDX-License-Identifier: GPL-2.0 |
f6ebe77f HW |
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> | |
4a3e2f71 | 7 | #include <linux/mutex.h> |
f6ebe77f HW |
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. */ | |
4a3e2f71 | 15 | static DEFINE_MUTEX(nf_sockopt_mutex); |
f6ebe77f HW |
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 | { | |
55d84acd | 27 | struct nf_sockopt_ops *ops; |
f6ebe77f HW |
28 | int ret = 0; |
29 | ||
7926dbfa | 30 | mutex_lock(&nf_sockopt_mutex); |
55d84acd | 31 | list_for_each_entry(ops, &nf_sockopts, list) { |
f6ebe77f | 32 | if (ops->pf == reg->pf |
601e68e1 | 33 | && (overlap(ops->set_optmin, ops->set_optmax, |
f6ebe77f | 34 | reg->set_optmin, reg->set_optmax) |
601e68e1 | 35 | || overlap(ops->get_optmin, ops->get_optmax, |
f6ebe77f | 36 | reg->get_optmin, reg->get_optmax))) { |
b38cf901 | 37 | pr_debug("nf_sock overlap: %u-%u/%u-%u v %u-%u/%u-%u\n", |
601e68e1 YH |
38 | ops->set_optmin, ops->set_optmax, |
39 | ops->get_optmin, ops->get_optmax, | |
f6ebe77f HW |
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: | |
4a3e2f71 | 49 | mutex_unlock(&nf_sockopt_mutex); |
f6ebe77f HW |
50 | return ret; |
51 | } | |
52 | EXPORT_SYMBOL(nf_register_sockopt); | |
53 | ||
54 | void nf_unregister_sockopt(struct nf_sockopt_ops *reg) | |
55 | { | |
4a3e2f71 | 56 | mutex_lock(&nf_sockopt_mutex); |
f6ebe77f | 57 | list_del(®->list); |
4a3e2f71 | 58 | mutex_unlock(&nf_sockopt_mutex); |
f6ebe77f HW |
59 | } |
60 | EXPORT_SYMBOL(nf_unregister_sockopt); | |
61 | ||
76108cea | 62 | static struct nf_sockopt_ops *nf_sockopt_find(struct sock *sk, u_int8_t pf, |
4ce5ba6a | 63 | int val, int get) |
f6ebe77f | 64 | { |
f6ebe77f | 65 | struct nf_sockopt_ops *ops; |
f6ebe77f | 66 | |
7926dbfa | 67 | mutex_lock(&nf_sockopt_mutex); |
55d84acd | 68 | list_for_each_entry(ops, &nf_sockopts, list) { |
f6ebe77f | 69 | if (ops->pf == pf) { |
16fcec35 NH |
70 | if (!try_module_get(ops->owner)) |
71 | goto out_nosup; | |
4ce5ba6a | 72 | |
f6ebe77f | 73 | if (get) { |
4ce5ba6a PE |
74 | if (val >= ops->get_optmin && |
75 | val < ops->get_optmax) | |
f6ebe77f | 76 | goto out; |
f6ebe77f | 77 | } else { |
4ce5ba6a PE |
78 | if (val >= ops->set_optmin && |
79 | val < ops->set_optmax) | |
f6ebe77f | 80 | goto out; |
f6ebe77f | 81 | } |
16fcec35 | 82 | module_put(ops->owner); |
f6ebe77f HW |
83 | } |
84 | } | |
4ce5ba6a PE |
85 | out_nosup: |
86 | ops = ERR_PTR(-ENOPROTOOPT); | |
87 | out: | |
4a3e2f71 | 88 | mutex_unlock(&nf_sockopt_mutex); |
4ce5ba6a PE |
89 | return ops; |
90 | } | |
91 | ||
c2f12630 | 92 | int nf_setsockopt(struct sock *sk, u_int8_t pf, int val, sockptr_t opt, |
657e4c34 | 93 | unsigned int len) |
4ce5ba6a PE |
94 | { |
95 | struct nf_sockopt_ops *ops; | |
96 | int ret; | |
97 | ||
657e4c34 | 98 | ops = nf_sockopt_find(sk, pf, val, 0); |
4ce5ba6a PE |
99 | if (IS_ERR(ops)) |
100 | return PTR_ERR(ops); | |
657e4c34 | 101 | ret = ops->set(sk, val, opt, len); |
16fcec35 | 102 | module_put(ops->owner); |
f6ebe77f HW |
103 | return ret; |
104 | } | |
f6ebe77f HW |
105 | EXPORT_SYMBOL(nf_setsockopt); |
106 | ||
76108cea JE |
107 | int nf_getsockopt(struct sock *sk, u_int8_t pf, int val, char __user *opt, |
108 | int *len) | |
f6ebe77f | 109 | { |
657e4c34 CH |
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; | |
f6ebe77f HW |
119 | } |
120 | EXPORT_SYMBOL(nf_getsockopt); |