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