Blame view

drivers/connector/connector.c 6.64 KB
1a59d1b8e   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
2
  /*
f3c48ecce   Valentin Ilie   drivers: connecto...
3
   *	connector.c
1a5645bc9   Frederic Weisbecker   connector: create...
4
   *
acb9c1b2f   Evgeniy Polyakov   connector: mainta...
5
   * 2004+ Copyright (c) Evgeniy Polyakov <zbr@ioremap.net>
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
6
   * All rights reserved.
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
7
   */
d2af686c2   Randy Dunlap   connector: fix de...
8
  #include <linux/compiler.h>
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
9
10
11
12
  #include <linux/kernel.h>
  #include <linux/module.h>
  #include <linux/list.h>
  #include <linux/skbuff.h>
9631d79e8   Hong zhi guo   connector: replac...
13
  #include <net/netlink.h>
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
14
15
  #include <linux/moduleparam.h>
  #include <linux/connector.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
16
  #include <linux/slab.h>
8ed965d61   Arjan van de Ven   [PATCH] sem2mutex...
17
  #include <linux/mutex.h>
a0a61a604   Li Zefan   CONNECTOR: add a ...
18
19
  #include <linux/proc_fs.h>
  #include <linux/spinlock.h>
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
20
21
22
23
  
  #include <net/sock.h>
  
  MODULE_LICENSE("GPL");
acb9c1b2f   Evgeniy Polyakov   connector: mainta...
24
  MODULE_AUTHOR("Evgeniy Polyakov <zbr@ioremap.net>");
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
25
  MODULE_DESCRIPTION("Generic userspace <-> kernelspace connector.");
3700c3c29   Stephen Hemminger   connector: add mo...
26
  MODULE_ALIAS_NET_PF_PROTO(PF_NETLINK, NETLINK_CONNECTOR);
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
27

7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
28
  static struct cn_dev cdev;
78374676e   Li Zefan   CONNECTOR: make c...
29
  static int cn_already_initialized;
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
30
31
  
  /*
34470e0bf   David Fries   connector: allow ...
32
33
   * Sends mult (multiple) cn_msg at a time.
   *
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
34
35
36
37
38
39
40
   * msg->seq and msg->ack are used to determine message genealogy.
   * When someone sends message it puts there locally unique sequence
   * and random acknowledge numbers.  Sequence number may be copied into
   * nlmsghdr->nlmsg_seq too.
   *
   * Sequence number is incremented with each message to be sent.
   *
ac8f73305   David Fries   connector: add po...
41
   * If we expect a reply to our message then the sequence number in
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
42
43
44
45
46
47
48
49
50
51
52
   * received message MUST be the same as in original message, and
   * acknowledge number MUST be the same + 1.
   *
   * If we receive a message and its sequence number is not equal to the
   * one we are expecting then it is a new message.
   *
   * If we receive a message and its sequence number is the same as one
   * we are expecting but it's acknowledgement number is not equal to
   * the acknowledgement number in the original message + 1, then it is
   * a new message.
   *
34470e0bf   David Fries   connector: allow ...
53
54
55
   * If msg->len != len, then additional cn_msg messages are expected following
   * the first msg.
   *
ac8f73305   David Fries   connector: add po...
56
57
   * The message is sent to, the portid if given, the group if given, both if
   * both, or if both are zero then the group is looked up and sent there.
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
58
   */
34470e0bf   David Fries   connector: allow ...
59
  int cn_netlink_send_mult(struct cn_msg *msg, u16 len, u32 portid, u32 __group,
ac8f73305   David Fries   connector: add po...
60
  	gfp_t gfp_mask)
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
61
62
63
64
65
66
67
68
69
  {
  	struct cn_callback_entry *__cbq;
  	unsigned int size;
  	struct sk_buff *skb;
  	struct nlmsghdr *nlh;
  	struct cn_msg *data;
  	struct cn_dev *dev = &cdev;
  	u32 group = 0;
  	int found = 0;
ac8f73305   David Fries   connector: add po...
70
71
72
  	if (portid || __group) {
  		group = __group;
  	} else {
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
73
74
75
  		spin_lock_bh(&dev->cbdev->queue_lock);
  		list_for_each_entry(__cbq, &dev->cbdev->queue_list,
  				    callback_entry) {
acd042bb2   Evgeniy Polyakov   [CONNECTOR]: asyn...
76
  			if (cn_cb_equal(&__cbq->id.id, &msg->id)) {
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
77
78
  				found = 1;
  				group = __cbq->group;
fd00eeccd   Li Zefan   [CONNECTOR]: add ...
79
  				break;
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
80
81
82
83
84
85
  			}
  		}
  		spin_unlock_bh(&dev->cbdev->queue_lock);
  
  		if (!found)
  			return -ENODEV;
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
86
  	}
ac8f73305   David Fries   connector: add po...
87
  	if (!portid && !netlink_has_listeners(dev->nls, group))
b191ba0d5   Evgeniy Polyakov   [CONNECTOR]: Use ...
88
  		return -ESRCH;
34470e0bf   David Fries   connector: allow ...
89
  	size = sizeof(*msg) + len;
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
90

9631d79e8   Hong zhi guo   connector: replac...
91
  	skb = nlmsg_new(size, gfp_mask);
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
92
93
  	if (!skb)
  		return -ENOMEM;
9631d79e8   Hong zhi guo   connector: replac...
94
  	nlh = nlmsg_put(skb, 0, msg->seq, NLMSG_DONE, size, 0);
85c931665   Javier Martinez Canillas   connector: use nl...
95
96
97
98
  	if (!nlh) {
  		kfree_skb(skb);
  		return -EMSGSIZE;
  	}
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
99

85c931665   Javier Martinez Canillas   connector: use nl...
100
  	data = nlmsg_data(nlh);
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
101

ac73bf50b   Mathias Krause   connector: use 's...
102
  	memcpy(data, msg, size);
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
103
104
  
  	NETLINK_CB(skb).dst_group = group;
ac8f73305   David Fries   connector: add po...
105
106
107
  	if (group)
  		return netlink_broadcast(dev->nls, skb, portid, group,
  					 gfp_mask);
d0164adc8   Mel Gorman   mm, page_alloc: d...
108
109
  	return netlink_unicast(dev->nls, skb, portid,
  			!gfpflags_allow_blocking(gfp_mask));
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
110
  }
34470e0bf   David Fries   connector: allow ...
111
112
113
114
115
116
117
118
  EXPORT_SYMBOL_GPL(cn_netlink_send_mult);
  
  /* same as cn_netlink_send_mult except msg->len is used for len */
  int cn_netlink_send(struct cn_msg *msg, u32 portid, u32 __group,
  	gfp_t gfp_mask)
  {
  	return cn_netlink_send_mult(msg, msg->len, portid, __group, gfp_mask);
  }
deb0e9b23   Andrew Morton   [PATCH] connector...
119
  EXPORT_SYMBOL_GPL(cn_netlink_send);
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
120
121
122
123
  
  /*
   * Callback helper - queues work and setup destructor for given data.
   */
f1489cfb1   Philipp Reisner   connector: Remove...
124
  static int cn_call_callback(struct sk_buff *skb)
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
125
  {
a30cfa475   David Fries   cn: verify msg->l...
126
  	struct nlmsghdr *nlh;
04f482faf   Patrick McHardy   connector: conver...
127
  	struct cn_callback_entry *i, *cbq = NULL;
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
128
  	struct cn_dev *dev = &cdev;
9631d79e8   Hong zhi guo   connector: replac...
129
  	struct cn_msg *msg = nlmsg_data(nlmsg_hdr(skb));
04f482faf   Patrick McHardy   connector: conver...
130
  	struct netlink_skb_parms *nsp = &NETLINK_CB(skb);
acd042bb2   Evgeniy Polyakov   [CONNECTOR]: asyn...
131
  	int err = -ENODEV;
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
132

a30cfa475   David Fries   cn: verify msg->l...
133
134
135
136
  	/* verify msg->len is within skb */
  	nlh = nlmsg_hdr(skb);
  	if (nlh->nlmsg_len < NLMSG_HDRLEN + sizeof(struct cn_msg) + msg->len)
  		return -EINVAL;
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
137
  	spin_lock_bh(&dev->cbdev->queue_lock);
04f482faf   Patrick McHardy   connector: conver...
138
139
  	list_for_each_entry(i, &dev->cbdev->queue_list, callback_entry) {
  		if (cn_cb_equal(&i->id.id, &msg->id)) {
e65f7ee39   Elena Reshetova   drivers, connecto...
140
  			refcount_inc(&i->refcnt);
04f482faf   Patrick McHardy   connector: conver...
141
  			cbq = i;
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
142
143
144
145
  			break;
  		}
  	}
  	spin_unlock_bh(&dev->cbdev->queue_lock);
04f482faf   Patrick McHardy   connector: conver...
146
147
148
149
  	if (cbq != NULL) {
  		cbq->callback(msg, nsp);
  		kfree_skb(skb);
  		cn_queue_release_callback(cbq);
0e0878584   Patrick McHardy   connector: fix sk...
150
  		err = 0;
04f482faf   Patrick McHardy   connector: conver...
151
  	}
acd042bb2   Evgeniy Polyakov   [CONNECTOR]: asyn...
152
  	return err;
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
153
154
155
  }
  
  /*
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
156
157
   * Main netlink receiving function.
   *
00f5e06c0   Li Zefan   [CONNECTOR]: clea...
158
   * It checks skb, netlink header and msg sizes, and calls callback helper.
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
159
   */
55285bf09   Florian Westphal   connector: bump s...
160
  static void cn_rx_skb(struct sk_buff *skb)
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
161
162
  {
  	struct nlmsghdr *nlh;
162b2bedc   Mathias Krause   connector: use nl...
163
  	int len, err;
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
164

9631d79e8   Hong zhi guo   connector: replac...
165
  	if (skb->len >= NLMSG_HDRLEN) {
b529ccf27   Arnaldo Carvalho de Melo   [NETLINK]: Introd...
166
  		nlh = nlmsg_hdr(skb);
162b2bedc   Mathias Krause   connector: use nl...
167
  		len = nlmsg_len(nlh);
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
168

162b2bedc   Mathias Krause   connector: use nl...
169
  		if (len < (int)sizeof(struct cn_msg) ||
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
170
  		    skb->len < nlh->nlmsg_len ||
55285bf09   Florian Westphal   connector: bump s...
171
  		    len > CONNECTOR_MAX_MSG_SIZE)
6cf92e98a   Michal Januszewski   [CONNECTOR]: Fix ...
172
  			return;
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
173

55285bf09   Florian Westphal   connector: bump s...
174
  		err = cn_call_callback(skb_get(skb));
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
175
176
177
  		if (err < 0)
  			kfree_skb(skb);
  	}
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
178
179
180
  }
  
  /*
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
181
182
183
184
185
   * Callback add routing - adds callback with given ID and name.
   * If there is registered callback with the same ID it will not be added.
   *
   * May sleep.
   */
008536e84   Joe Perches   connector: Conver...
186
  int cn_add_callback(struct cb_id *id, const char *name,
f3c48ecce   Valentin Ilie   drivers: connecto...
187
188
  		    void (*callback)(struct cn_msg *,
  				     struct netlink_skb_parms *))
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
189
  {
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
190
  	struct cn_dev *dev = &cdev;
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
191

d6cc7f1a3   Evgeniy Polyakov   [CONNECTOR]: Init...
192
193
  	if (!cn_already_initialized)
  		return -EAGAIN;
fe6bc89ab   Qinglang Miao   connector: simpli...
194
  	return cn_queue_add_callback(dev->cbdev, name, id, callback);
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
195
  }
deb0e9b23   Andrew Morton   [PATCH] connector...
196
  EXPORT_SYMBOL_GPL(cn_add_callback);
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
197
198
199
200
201
202
203
204
205
206
207
208
209
210
  
  /*
   * Callback remove routing - removes callback
   * with given ID.
   * If there is no registered callback with given
   * ID nothing happens.
   *
   * May sleep while waiting for reference counter to become zero.
   */
  void cn_del_callback(struct cb_id *id)
  {
  	struct cn_dev *dev = &cdev;
  
  	cn_queue_del_callback(dev->cbdev, id);
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
211
  }
deb0e9b23   Andrew Morton   [PATCH] connector...
212
  EXPORT_SYMBOL_GPL(cn_del_callback);
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
213

d2af686c2   Randy Dunlap   connector: fix de...
214
  static int __maybe_unused cn_proc_show(struct seq_file *m, void *v)
a0a61a604   Li Zefan   CONNECTOR: add a ...
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
  {
  	struct cn_queue_dev *dev = cdev.cbdev;
  	struct cn_callback_entry *cbq;
  
  	seq_printf(m, "Name            ID
  ");
  
  	spin_lock_bh(&dev->queue_lock);
  
  	list_for_each_entry(cbq, &dev->queue_list, callback_entry) {
  		seq_printf(m, "%-15s %u:%u
  ",
  			   cbq->id.name,
  			   cbq->id.id.idx,
  			   cbq->id.id.val);
  	}
  
  	spin_unlock_bh(&dev->queue_lock);
  
  	return 0;
  }
0fe763c57   Greg Kroah-Hartman   Drivers: misc: re...
236
  static int cn_init(void)
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
237
238
  {
  	struct cn_dev *dev = &cdev;
a31f2d17b   Pablo Neira Ayuso   netlink: add netl...
239
240
  	struct netlink_kernel_cfg cfg = {
  		.groups	= CN_NETLINK_USERS + 0xf,
903e9d1bf   Vasily Averin   connector: remove...
241
  		.input	= cn_rx_skb,
a31f2d17b   Pablo Neira Ayuso   netlink: add netl...
242
  	};
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
243

9f00d9776   Pablo Neira Ayuso   netlink: hide str...
244
  	dev->nls = netlink_kernel_create(&init_net, NETLINK_CONNECTOR, &cfg);
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
245
246
247
248
249
  	if (!dev->nls)
  		return -EIO;
  
  	dev->cbdev = cn_queue_alloc_dev("cqueue", dev->nls);
  	if (!dev->cbdev) {
b7c6ba6eb   Denis V. Lunev   [NETNS]: Consolid...
250
  		netlink_kernel_release(dev->nls);
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
251
252
  		return -EINVAL;
  	}
1a5645bc9   Frederic Weisbecker   connector: create...
253

d6cc7f1a3   Evgeniy Polyakov   [CONNECTOR]: Init...
254
  	cn_already_initialized = 1;
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
255

3f3942aca   Christoph Hellwig   proc: introduce p...
256
  	proc_create_single("connector", S_IRUGO, init_net.proc_net, cn_proc_show);
a0a61a604   Li Zefan   CONNECTOR: add a ...
257

7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
258
259
  	return 0;
  }
0fe763c57   Greg Kroah-Hartman   Drivers: misc: re...
260
  static void cn_fini(void)
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
261
262
263
264
  {
  	struct cn_dev *dev = &cdev;
  
  	cn_already_initialized = 0;
ece31ffd5   Gao feng   net: proc: change...
265
  	remove_proc_entry("connector", init_net.proc_net);
a0a61a604   Li Zefan   CONNECTOR: add a ...
266

7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
267
  	cn_queue_free_dev(dev->cbdev);
b7c6ba6eb   Denis V. Lunev   [NETNS]: Consolid...
268
  	netlink_kernel_release(dev->nls);
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
269
  }
d6cc7f1a3   Evgeniy Polyakov   [CONNECTOR]: Init...
270
  subsys_initcall(cn_init);
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
271
  module_exit(cn_fini);