Branch data Line data Source code
1 : : /*
2 : : * connector.c
3 : : *
4 : : * 2004+ Copyright (c) Evgeniy Polyakov <zbr@ioremap.net>
5 : : * All rights reserved.
6 : : *
7 : : * This program is free software; you can redistribute it and/or modify
8 : : * it under the terms of the GNU General Public License as published by
9 : : * the Free Software Foundation; either version 2 of the License, or
10 : : * (at your option) any later version.
11 : : *
12 : : * This program is distributed in the hope that it will be useful,
13 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 : : * GNU General Public License for more details.
16 : : *
17 : : * You should have received a copy of the GNU General Public License
18 : : * along with this program; if not, write to the Free Software
19 : : * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 : : */
21 : :
22 : : #include <linux/kernel.h>
23 : : #include <linux/module.h>
24 : : #include <linux/list.h>
25 : : #include <linux/skbuff.h>
26 : : #include <net/netlink.h>
27 : : #include <linux/moduleparam.h>
28 : : #include <linux/connector.h>
29 : : #include <linux/slab.h>
30 : : #include <linux/mutex.h>
31 : : #include <linux/proc_fs.h>
32 : : #include <linux/spinlock.h>
33 : :
34 : : #include <net/sock.h>
35 : :
36 : : MODULE_LICENSE("GPL");
37 : : MODULE_AUTHOR("Evgeniy Polyakov <zbr@ioremap.net>");
38 : : MODULE_DESCRIPTION("Generic userspace <-> kernelspace connector.");
39 : : MODULE_ALIAS_NET_PF_PROTO(PF_NETLINK, NETLINK_CONNECTOR);
40 : :
41 : : static struct cn_dev cdev;
42 : :
43 : : static int cn_already_initialized;
44 : :
45 : : /*
46 : : * msg->seq and msg->ack are used to determine message genealogy.
47 : : * When someone sends message it puts there locally unique sequence
48 : : * and random acknowledge numbers. Sequence number may be copied into
49 : : * nlmsghdr->nlmsg_seq too.
50 : : *
51 : : * Sequence number is incremented with each message to be sent.
52 : : *
53 : : * If we expect reply to our message then the sequence number in
54 : : * received message MUST be the same as in original message, and
55 : : * acknowledge number MUST be the same + 1.
56 : : *
57 : : * If we receive a message and its sequence number is not equal to the
58 : : * one we are expecting then it is a new message.
59 : : *
60 : : * If we receive a message and its sequence number is the same as one
61 : : * we are expecting but it's acknowledgement number is not equal to
62 : : * the acknowledgement number in the original message + 1, then it is
63 : : * a new message.
64 : : *
65 : : */
66 : 0 : int cn_netlink_send(struct cn_msg *msg, u32 __group, gfp_t gfp_mask)
67 : : {
68 : : struct cn_callback_entry *__cbq;
69 : : unsigned int size;
70 : : struct sk_buff *skb;
71 : : struct nlmsghdr *nlh;
72 : : struct cn_msg *data;
73 : : struct cn_dev *dev = &cdev;
74 : : u32 group = 0;
75 : : int found = 0;
76 : :
77 [ - + ]: 47 : if (!__group) {
78 : 0 : spin_lock_bh(&dev->cbdev->queue_lock);
79 [ # # ]: 0 : list_for_each_entry(__cbq, &dev->cbdev->queue_list,
80 : : callback_entry) {
81 [ # # ]: 0 : if (cn_cb_equal(&__cbq->id.id, &msg->id)) {
82 : : found = 1;
83 : 0 : group = __cbq->group;
84 : 0 : break;
85 : : }
86 : : }
87 : 0 : spin_unlock_bh(&dev->cbdev->queue_lock);
88 : :
89 [ # # ]: 0 : if (!found)
90 : : return -ENODEV;
91 : : } else {
92 : : group = __group;
93 : : }
94 : :
95 [ + - ]: 47 : if (!netlink_has_listeners(dev->nls, group))
96 : : return -ESRCH;
97 : :
98 : 47 : size = sizeof(*msg) + msg->len;
99 : :
100 : : skb = nlmsg_new(size, gfp_mask);
101 [ + - ]: 47 : if (!skb)
102 : : return -ENOMEM;
103 : :
104 : 47 : nlh = nlmsg_put(skb, 0, msg->seq, NLMSG_DONE, size, 0);
105 [ - + ]: 47 : if (!nlh) {
106 : 0 : kfree_skb(skb);
107 : 0 : return -EMSGSIZE;
108 : : }
109 : :
110 : 47 : data = nlmsg_data(nlh);
111 : :
112 : 47 : memcpy(data, msg, size);
113 : :
114 : 47 : NETLINK_CB(skb).dst_group = group;
115 : :
116 : 47 : return netlink_broadcast(dev->nls, skb, 0, group, gfp_mask);
117 : : }
118 : : EXPORT_SYMBOL_GPL(cn_netlink_send);
119 : :
120 : : /*
121 : : * Callback helper - queues work and setup destructor for given data.
122 : : */
123 : 0 : static int cn_call_callback(struct sk_buff *skb)
124 : : {
125 : : struct cn_callback_entry *i, *cbq = NULL;
126 : : struct cn_dev *dev = &cdev;
127 : 10 : struct cn_msg *msg = nlmsg_data(nlmsg_hdr(skb));
128 : 10 : struct netlink_skb_parms *nsp = &NETLINK_CB(skb);
129 : : int err = -ENODEV;
130 : :
131 : 10 : spin_lock_bh(&dev->cbdev->queue_lock);
132 [ + - ]: 10 : list_for_each_entry(i, &dev->cbdev->queue_list, callback_entry) {
133 [ + - ]: 10 : if (cn_cb_equal(&i->id.id, &msg->id)) {
134 : 10 : atomic_inc(&i->refcnt);
135 : : cbq = i;
136 : 10 : break;
137 : : }
138 : : }
139 : 10 : spin_unlock_bh(&dev->cbdev->queue_lock);
140 : :
141 [ + - ]: 10 : if (cbq != NULL) {
142 : : err = 0;
143 : 10 : cbq->callback(msg, nsp);
144 : 10 : kfree_skb(skb);
145 : 10 : cn_queue_release_callback(cbq);
146 : : err = 0;
147 : : }
148 : :
149 : 10 : return err;
150 : : }
151 : :
152 : : /*
153 : : * Main netlink receiving function.
154 : : *
155 : : * It checks skb, netlink header and msg sizes, and calls callback helper.
156 : : */
157 : 0 : static void cn_rx_skb(struct sk_buff *__skb)
158 : : {
159 : 10 : struct nlmsghdr *nlh;
160 : 10 : struct sk_buff *skb;
161 : : int len, err;
162 : :
163 : : skb = skb_get(__skb);
164 : :
165 [ + - ]: 10 : if (skb->len >= NLMSG_HDRLEN) {
166 : : nlh = nlmsg_hdr(skb);
167 : : len = nlmsg_len(nlh);
168 : :
169 [ + - ][ + - ]: 10 : if (len < (int)sizeof(struct cn_msg) ||
170 [ - + ]: 10 : skb->len < nlh->nlmsg_len ||
171 : : len > CONNECTOR_MAX_MSG_SIZE) {
172 : 0 : kfree_skb(skb);
173 : 0 : return;
174 : : }
175 : :
176 : 10 : err = cn_call_callback(skb);
177 [ - + ]: 10 : if (err < 0)
178 : 0 : kfree_skb(skb);
179 : : }
180 : : }
181 : :
182 : : /*
183 : : * Callback add routing - adds callback with given ID and name.
184 : : * If there is registered callback with the same ID it will not be added.
185 : : *
186 : : * May sleep.
187 : : */
188 : 0 : int cn_add_callback(struct cb_id *id, const char *name,
189 : : void (*callback)(struct cn_msg *,
190 : : struct netlink_skb_parms *))
191 : : {
192 : : int err;
193 : : struct cn_dev *dev = &cdev;
194 : :
195 [ # # ]: 0 : if (!cn_already_initialized)
196 : : return -EAGAIN;
197 : :
198 : 0 : err = cn_queue_add_callback(dev->cbdev, name, id, callback);
199 [ # # ]: 0 : if (err)
200 : 0 : return err;
201 : :
202 : : return 0;
203 : : }
204 : : EXPORT_SYMBOL_GPL(cn_add_callback);
205 : :
206 : : /*
207 : : * Callback remove routing - removes callback
208 : : * with given ID.
209 : : * If there is no registered callback with given
210 : : * ID nothing happens.
211 : : *
212 : : * May sleep while waiting for reference counter to become zero.
213 : : */
214 : 0 : void cn_del_callback(struct cb_id *id)
215 : : {
216 : : struct cn_dev *dev = &cdev;
217 : :
218 : 0 : cn_queue_del_callback(dev->cbdev, id);
219 : 0 : }
220 : : EXPORT_SYMBOL_GPL(cn_del_callback);
221 : :
222 : 0 : static int cn_proc_show(struct seq_file *m, void *v)
223 : : {
224 : 2 : struct cn_queue_dev *dev = cdev.cbdev;
225 : : struct cn_callback_entry *cbq;
226 : :
227 : 2 : seq_printf(m, "Name ID\n");
228 : :
229 : : spin_lock_bh(&dev->queue_lock);
230 : :
231 [ + + ]: 6 : list_for_each_entry(cbq, &dev->queue_list, callback_entry) {
232 : 2 : seq_printf(m, "%-15s %u:%u\n",
233 : 2 : cbq->id.name,
234 : : cbq->id.id.idx,
235 : : cbq->id.id.val);
236 : : }
237 : :
238 : : spin_unlock_bh(&dev->queue_lock);
239 : :
240 : 2 : return 0;
241 : : }
242 : :
243 : 0 : static int cn_proc_open(struct inode *inode, struct file *file)
244 : : {
245 : 2 : return single_open(file, cn_proc_show, NULL);
246 : : }
247 : :
248 : : static const struct file_operations cn_file_ops = {
249 : : .owner = THIS_MODULE,
250 : : .open = cn_proc_open,
251 : : .read = seq_read,
252 : : .llseek = seq_lseek,
253 : : .release = single_release
254 : : };
255 : :
256 : : static struct cn_dev cdev = {
257 : : .input = cn_rx_skb,
258 : : };
259 : :
260 : 0 : static int cn_init(void)
261 : : {
262 : : struct cn_dev *dev = &cdev;
263 : 0 : struct netlink_kernel_cfg cfg = {
264 : : .groups = CN_NETLINK_USERS + 0xf,
265 : 0 : .input = dev->input,
266 : : };
267 : :
268 : 0 : dev->nls = netlink_kernel_create(&init_net, NETLINK_CONNECTOR, &cfg);
269 [ # # ]: 0 : if (!dev->nls)
270 : : return -EIO;
271 : :
272 : 0 : dev->cbdev = cn_queue_alloc_dev("cqueue", dev->nls);
273 [ # # ]: 0 : if (!dev->cbdev) {
274 : 0 : netlink_kernel_release(dev->nls);
275 : 0 : return -EINVAL;
276 : : }
277 : :
278 : 0 : cn_already_initialized = 1;
279 : :
280 : 0 : proc_create("connector", S_IRUGO, init_net.proc_net, &cn_file_ops);
281 : :
282 : 0 : return 0;
283 : : }
284 : :
285 : 0 : static void cn_fini(void)
286 : : {
287 : : struct cn_dev *dev = &cdev;
288 : :
289 : 0 : cn_already_initialized = 0;
290 : :
291 : 0 : remove_proc_entry("connector", init_net.proc_net);
292 : :
293 : 0 : cn_queue_free_dev(dev->cbdev);
294 : 0 : netlink_kernel_release(dev->nls);
295 : 0 : }
296 : :
297 : : subsys_initcall(cn_init);
298 : : module_exit(cn_fini);
|