Branch data Line data Source code
1 : : #include <linux/kernel.h>
2 : : #include <linux/init.h>
3 : : #include <linux/module.h>
4 : : #include <linux/skbuff.h>
5 : : #include <linux/netfilter.h>
6 : : #include <linux/mutex.h>
7 : : #include <net/sock.h>
8 : :
9 : : #include "nf_internals.h"
10 : :
11 : : /* Sockopts only registered and called from user context, so
12 : : net locking would be overkill. Also, [gs]etsockopt calls may
13 : : sleep. */
14 : : static DEFINE_MUTEX(nf_sockopt_mutex);
15 : : static LIST_HEAD(nf_sockopts);
16 : :
17 : : /* Do exclusive ranges overlap? */
18 : : static inline int overlap(int min1, int max1, int min2, int max2)
19 : : {
20 : 0 : return max1 > min2 && min1 < max2;
21 : : }
22 : :
23 : : /* Functions to register sockopt ranges (exclusive). */
24 : 0 : int nf_register_sockopt(struct nf_sockopt_ops *reg)
25 : : {
26 : : struct nf_sockopt_ops *ops;
27 : : int ret = 0;
28 : :
29 [ # # ]: 0 : if (mutex_lock_interruptible(&nf_sockopt_mutex) != 0)
30 : : return -EINTR;
31 : :
32 [ # # ]: 0 : list_for_each_entry(ops, &nf_sockopts, list) {
33 [ # # ]: 0 : if (ops->pf == reg->pf
34 [ # # ]: 0 : && (overlap(ops->set_optmin, ops->set_optmax,
35 : : reg->set_optmin, reg->set_optmax)
36 [ # # ]: 0 : || overlap(ops->get_optmin, ops->get_optmax,
37 : : reg->get_optmin, reg->get_optmax))) {
38 : : NFDEBUG("nf_sock overlap: %u-%u/%u-%u v %u-%u/%u-%u\n",
39 : : ops->set_optmin, ops->set_optmax,
40 : : ops->get_optmin, ops->get_optmax,
41 : : reg->set_optmin, reg->set_optmax,
42 : : reg->get_optmin, reg->get_optmax);
43 : : ret = -EBUSY;
44 : : goto out;
45 : : }
46 : : }
47 : :
48 : 0 : list_add(®->list, &nf_sockopts);
49 : : out:
50 : 0 : mutex_unlock(&nf_sockopt_mutex);
51 : 0 : return ret;
52 : : }
53 : : EXPORT_SYMBOL(nf_register_sockopt);
54 : :
55 : 0 : void nf_unregister_sockopt(struct nf_sockopt_ops *reg)
56 : : {
57 : 0 : mutex_lock(&nf_sockopt_mutex);
58 : : list_del(®->list);
59 : 0 : mutex_unlock(&nf_sockopt_mutex);
60 : 0 : }
61 : : EXPORT_SYMBOL(nf_unregister_sockopt);
62 : :
63 : 2 : static struct nf_sockopt_ops *nf_sockopt_find(struct sock *sk, u_int8_t pf,
64 : : int val, int get)
65 : : {
66 : : struct nf_sockopt_ops *ops;
67 : :
68 [ + - ]: 2 : if (mutex_lock_interruptible(&nf_sockopt_mutex) != 0)
69 : : return ERR_PTR(-EINTR);
70 : :
71 [ - + ]: 2 : list_for_each_entry(ops, &nf_sockopts, list) {
72 [ # # ]: 0 : if (ops->pf == pf) {
73 [ # # ]: 0 : if (!try_module_get(ops->owner))
74 : : goto out_nosup;
75 : :
76 [ # # ]: 0 : if (get) {
77 [ # # ][ # # ]: 0 : if (val >= ops->get_optmin &&
78 : 0 : val < ops->get_optmax)
79 : : goto out;
80 : : } else {
81 [ # # ][ # # ]: 0 : if (val >= ops->set_optmin &&
82 : 0 : val < ops->set_optmax)
83 : : goto out;
84 : : }
85 : 0 : module_put(ops->owner);
86 : : }
87 : : }
88 : : out_nosup:
89 : : ops = ERR_PTR(-ENOPROTOOPT);
90 : : out:
91 : 2 : mutex_unlock(&nf_sockopt_mutex);
92 : : return ops;
93 : : }
94 : :
95 : : /* Call get/setsockopt() */
96 : 0 : static int nf_sockopt(struct sock *sk, u_int8_t pf, int val,
97 : : char __user *opt, int *len, int get)
98 : : {
99 : : struct nf_sockopt_ops *ops;
100 : : int ret;
101 : :
102 : 2 : ops = nf_sockopt_find(sk, pf, val, get);
103 [ + - ]: 4 : if (IS_ERR(ops))
104 : 2 : return PTR_ERR(ops);
105 : :
106 [ # # ]: 0 : if (get)
107 : 0 : ret = ops->get(sk, val, opt, len);
108 : : else
109 : 0 : ret = ops->set(sk, val, opt, *len);
110 : :
111 : 0 : module_put(ops->owner);
112 : 0 : return ret;
113 : : }
114 : :
115 : 0 : int nf_setsockopt(struct sock *sk, u_int8_t pf, int val, char __user *opt,
116 : : unsigned int len)
117 : : {
118 : 1 : return nf_sockopt(sk, pf, val, opt, &len, 0);
119 : : }
120 : : EXPORT_SYMBOL(nf_setsockopt);
121 : :
122 : 0 : int nf_getsockopt(struct sock *sk, u_int8_t pf, int val, char __user *opt,
123 : : int *len)
124 : : {
125 : 1 : return nf_sockopt(sk, pf, val, opt, len, 1);
126 : : }
127 : : EXPORT_SYMBOL(nf_getsockopt);
128 : :
129 : : #ifdef CONFIG_COMPAT
130 : : static int compat_nf_sockopt(struct sock *sk, u_int8_t pf, int val,
131 : : char __user *opt, int *len, int get)
132 : : {
133 : : struct nf_sockopt_ops *ops;
134 : : int ret;
135 : :
136 : : ops = nf_sockopt_find(sk, pf, val, get);
137 : : if (IS_ERR(ops))
138 : : return PTR_ERR(ops);
139 : :
140 : : if (get) {
141 : : if (ops->compat_get)
142 : : ret = ops->compat_get(sk, val, opt, len);
143 : : else
144 : : ret = ops->get(sk, val, opt, len);
145 : : } else {
146 : : if (ops->compat_set)
147 : : ret = ops->compat_set(sk, val, opt, *len);
148 : : else
149 : : ret = ops->set(sk, val, opt, *len);
150 : : }
151 : :
152 : : module_put(ops->owner);
153 : : return ret;
154 : : }
155 : :
156 : : int compat_nf_setsockopt(struct sock *sk, u_int8_t pf,
157 : : int val, char __user *opt, unsigned int len)
158 : : {
159 : : return compat_nf_sockopt(sk, pf, val, opt, &len, 0);
160 : : }
161 : : EXPORT_SYMBOL(compat_nf_setsockopt);
162 : :
163 : : int compat_nf_getsockopt(struct sock *sk, u_int8_t pf,
164 : : int val, char __user *opt, int *len)
165 : : {
166 : : return compat_nf_sockopt(sk, pf, val, opt, len, 1);
167 : : }
168 : : EXPORT_SYMBOL(compat_nf_getsockopt);
169 : : #endif
|