Blame view

drivers/connector/connector.c 7.68 KB
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
1
  /*
f3c48ecce   Valentin Ilie   drivers: connecto...
2
   *	connector.c
1a5645bc9   Frederic Weisbecker   connector: create...
3
   *
acb9c1b2f   Evgeniy Polyakov   connector: mainta...
4
   * 2004+ Copyright (c) Evgeniy Polyakov <zbr@ioremap.net>
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
5
   * All rights reserved.
1a5645bc9   Frederic Weisbecker   connector: create...
6
   *
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
   * the Free Software Foundation; either version 2 of the License, or
   * (at your option) any later version.
   *
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
   *
   * You should have received a copy of the GNU General Public License
   * along with this program; if not, write to the Free Software
   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   */
  
  #include <linux/kernel.h>
  #include <linux/module.h>
  #include <linux/list.h>
  #include <linux/skbuff.h>
9631d79e8   Hong zhi guo   connector: replac...
26
  #include <net/netlink.h>
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
27
28
  #include <linux/moduleparam.h>
  #include <linux/connector.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
29
  #include <linux/slab.h>
8ed965d61   Arjan van de Ven   [PATCH] sem2mutex...
30
  #include <linux/mutex.h>
a0a61a604   Li Zefan   CONNECTOR: add a ...
31
32
  #include <linux/proc_fs.h>
  #include <linux/spinlock.h>
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
33
34
35
36
  
  #include <net/sock.h>
  
  MODULE_LICENSE("GPL");
acb9c1b2f   Evgeniy Polyakov   connector: mainta...
37
  MODULE_AUTHOR("Evgeniy Polyakov <zbr@ioremap.net>");
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
38
  MODULE_DESCRIPTION("Generic userspace <-> kernelspace connector.");
3700c3c29   Stephen Hemminger   connector: add mo...
39
  MODULE_ALIAS_NET_PF_PROTO(PF_NETLINK, NETLINK_CONNECTOR);
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
40

7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
41
  static struct cn_dev cdev;
78374676e   Li Zefan   CONNECTOR: make c...
42
  static int cn_already_initialized;
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
43
44
  
  /*
34470e0bf   David Fries   connector: allow ...
45
46
   * Sends mult (multiple) cn_msg at a time.
   *
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
47
48
49
50
51
52
53
   * 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...
54
   * If we expect a reply to our message then the sequence number in
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
55
56
57
58
59
60
61
62
63
64
65
   * 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 ...
66
67
68
   * If msg->len != len, then additional cn_msg messages are expected following
   * the first msg.
   *
ac8f73305   David Fries   connector: add po...
69
70
   * 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...
71
   */
34470e0bf   David Fries   connector: allow ...
72
  int cn_netlink_send_mult(struct cn_msg *msg, u16 len, u32 portid, u32 __group,
ac8f73305   David Fries   connector: add po...
73
  	gfp_t gfp_mask)
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
74
75
76
77
78
79
80
81
82
  {
  	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...
83
84
85
  	if (portid || __group) {
  		group = __group;
  	} else {
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
86
87
88
  		spin_lock_bh(&dev->cbdev->queue_lock);
  		list_for_each_entry(__cbq, &dev->cbdev->queue_list,
  				    callback_entry) {
acd042bb2   Evgeniy Polyakov   [CONNECTOR]: asyn...
89
  			if (cn_cb_equal(&__cbq->id.id, &msg->id)) {
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
90
91
  				found = 1;
  				group = __cbq->group;
fd00eeccd   Li Zefan   [CONNECTOR]: add ...
92
  				break;
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
93
94
95
96
97
98
  			}
  		}
  		spin_unlock_bh(&dev->cbdev->queue_lock);
  
  		if (!found)
  			return -ENODEV;
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
99
  	}
ac8f73305   David Fries   connector: add po...
100
  	if (!portid && !netlink_has_listeners(dev->nls, group))
b191ba0d5   Evgeniy Polyakov   [CONNECTOR]: Use ...
101
  		return -ESRCH;
34470e0bf   David Fries   connector: allow ...
102
  	size = sizeof(*msg) + len;
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
103

9631d79e8   Hong zhi guo   connector: replac...
104
  	skb = nlmsg_new(size, gfp_mask);
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
105
106
  	if (!skb)
  		return -ENOMEM;
9631d79e8   Hong zhi guo   connector: replac...
107
  	nlh = nlmsg_put(skb, 0, msg->seq, NLMSG_DONE, size, 0);
85c931665   Javier Martinez Canillas   connector: use nl...
108
109
110
111
  	if (!nlh) {
  		kfree_skb(skb);
  		return -EMSGSIZE;
  	}
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
112

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

ac73bf50b   Mathias Krause   connector: use 's...
115
  	memcpy(data, msg, size);
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
116
117
  
  	NETLINK_CB(skb).dst_group = group;
ac8f73305   David Fries   connector: add po...
118
119
120
121
  	if (group)
  		return netlink_broadcast(dev->nls, skb, portid, group,
  					 gfp_mask);
  	return netlink_unicast(dev->nls, skb, portid, !(gfp_mask&__GFP_WAIT));
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
122
  }
34470e0bf   David Fries   connector: allow ...
123
124
125
126
127
128
129
130
  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...
131
  EXPORT_SYMBOL_GPL(cn_netlink_send);
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
132
133
134
135
  
  /*
   * Callback helper - queues work and setup destructor for given data.
   */
f1489cfb1   Philipp Reisner   connector: Remove...
136
  static int cn_call_callback(struct sk_buff *skb)
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
137
  {
a30cfa475   David Fries   cn: verify msg->l...
138
  	struct nlmsghdr *nlh;
04f482faf   Patrick McHardy   connector: conver...
139
  	struct cn_callback_entry *i, *cbq = NULL;
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
140
  	struct cn_dev *dev = &cdev;
9631d79e8   Hong zhi guo   connector: replac...
141
  	struct cn_msg *msg = nlmsg_data(nlmsg_hdr(skb));
04f482faf   Patrick McHardy   connector: conver...
142
  	struct netlink_skb_parms *nsp = &NETLINK_CB(skb);
acd042bb2   Evgeniy Polyakov   [CONNECTOR]: asyn...
143
  	int err = -ENODEV;
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
144

a30cfa475   David Fries   cn: verify msg->l...
145
146
147
148
  	/* 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...
149
  	spin_lock_bh(&dev->cbdev->queue_lock);
04f482faf   Patrick McHardy   connector: conver...
150
151
152
153
  	list_for_each_entry(i, &dev->cbdev->queue_list, callback_entry) {
  		if (cn_cb_equal(&i->id.id, &msg->id)) {
  			atomic_inc(&i->refcnt);
  			cbq = i;
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
154
155
156
157
  			break;
  		}
  	}
  	spin_unlock_bh(&dev->cbdev->queue_lock);
04f482faf   Patrick McHardy   connector: conver...
158
159
160
161
  	if (cbq != NULL) {
  		cbq->callback(msg, nsp);
  		kfree_skb(skb);
  		cn_queue_release_callback(cbq);
0e0878584   Patrick McHardy   connector: fix sk...
162
  		err = 0;
04f482faf   Patrick McHardy   connector: conver...
163
  	}
acd042bb2   Evgeniy Polyakov   [CONNECTOR]: asyn...
164
  	return err;
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
165
166
167
  }
  
  /*
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
168
169
   * Main netlink receiving function.
   *
00f5e06c0   Li Zefan   [CONNECTOR]: clea...
170
   * It checks skb, netlink header and msg sizes, and calls callback helper.
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
171
172
173
174
   */
  static void cn_rx_skb(struct sk_buff *__skb)
  {
  	struct nlmsghdr *nlh;
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
175
  	struct sk_buff *skb;
162b2bedc   Mathias Krause   connector: use nl...
176
  	int len, err;
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
177
178
  
  	skb = skb_get(__skb);
9631d79e8   Hong zhi guo   connector: replac...
179
  	if (skb->len >= NLMSG_HDRLEN) {
b529ccf27   Arnaldo Carvalho de Melo   [NETLINK]: Introd...
180
  		nlh = nlmsg_hdr(skb);
162b2bedc   Mathias Krause   connector: use nl...
181
  		len = nlmsg_len(nlh);
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
182

162b2bedc   Mathias Krause   connector: use nl...
183
  		if (len < (int)sizeof(struct cn_msg) ||
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
184
  		    skb->len < nlh->nlmsg_len ||
162b2bedc   Mathias Krause   connector: use nl...
185
  		    len > CONNECTOR_MAX_MSG_SIZE) {
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
186
  			kfree_skb(skb);
6cf92e98a   Michal Januszewski   [CONNECTOR]: Fix ...
187
  			return;
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
188
  		}
f1489cfb1   Philipp Reisner   connector: Remove...
189
  		err = cn_call_callback(skb);
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
190
191
192
  		if (err < 0)
  			kfree_skb(skb);
  	}
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
193
194
195
  }
  
  /*
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
196
197
198
199
200
   * 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...
201
  int cn_add_callback(struct cb_id *id, const char *name,
f3c48ecce   Valentin Ilie   drivers: connecto...
202
203
  		    void (*callback)(struct cn_msg *,
  				     struct netlink_skb_parms *))
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
204
205
206
  {
  	int err;
  	struct cn_dev *dev = &cdev;
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
207

d6cc7f1a3   Evgeniy Polyakov   [CONNECTOR]: Init...
208
209
  	if (!cn_already_initialized)
  		return -EAGAIN;
acd042bb2   Evgeniy Polyakov   [CONNECTOR]: asyn...
210
211
  	err = cn_queue_add_callback(dev->cbdev, name, id, callback);
  	if (err)
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
212
  		return err;
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
213

7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
214
215
  	return 0;
  }
deb0e9b23   Andrew Morton   [PATCH] connector...
216
  EXPORT_SYMBOL_GPL(cn_add_callback);
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
217
218
219
220
221
222
223
224
225
226
227
228
229
230
  
  /*
   * 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...
231
  }
deb0e9b23   Andrew Morton   [PATCH] connector...
232
  EXPORT_SYMBOL_GPL(cn_del_callback);
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
233

a0a61a604   Li Zefan   CONNECTOR: add a ...
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
  static int cn_proc_show(struct seq_file *m, void *v)
  {
  	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;
  }
  
  static int cn_proc_open(struct inode *inode, struct file *file)
  {
  	return single_open(file, cn_proc_show, NULL);
  }
  
  static const struct file_operations cn_file_ops = {
  	.owner   = THIS_MODULE,
  	.open    = cn_proc_open,
  	.read    = seq_read,
  	.llseek  = seq_lseek,
  	.release = single_release
  };
a31f2d17b   Pablo Neira Ayuso   netlink: add netl...
269
270
271
  static struct cn_dev cdev = {
  	.input   = cn_rx_skb,
  };
0fe763c57   Greg Kroah-Hartman   Drivers: misc: re...
272
  static int cn_init(void)
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
273
274
  {
  	struct cn_dev *dev = &cdev;
a31f2d17b   Pablo Neira Ayuso   netlink: add netl...
275
276
277
278
  	struct netlink_kernel_cfg cfg = {
  		.groups	= CN_NETLINK_USERS + 0xf,
  		.input	= dev->input,
  	};
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
279

9f00d9776   Pablo Neira Ayuso   netlink: hide str...
280
  	dev->nls = netlink_kernel_create(&init_net, NETLINK_CONNECTOR, &cfg);
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
281
282
283
284
285
  	if (!dev->nls)
  		return -EIO;
  
  	dev->cbdev = cn_queue_alloc_dev("cqueue", dev->nls);
  	if (!dev->cbdev) {
b7c6ba6eb   Denis V. Lunev   [NETNS]: Consolid...
286
  		netlink_kernel_release(dev->nls);
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
287
288
  		return -EINVAL;
  	}
1a5645bc9   Frederic Weisbecker   connector: create...
289

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

d4beaa66a   Gao feng   net: proc: change...
292
  	proc_create("connector", S_IRUGO, init_net.proc_net, &cn_file_ops);
a0a61a604   Li Zefan   CONNECTOR: add a ...
293

7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
294
295
  	return 0;
  }
0fe763c57   Greg Kroah-Hartman   Drivers: misc: re...
296
  static void cn_fini(void)
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
297
298
299
300
  {
  	struct cn_dev *dev = &cdev;
  
  	cn_already_initialized = 0;
ece31ffd5   Gao feng   net: proc: change...
301
  	remove_proc_entry("connector", init_net.proc_net);
a0a61a604   Li Zefan   CONNECTOR: add a ...
302

7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
303
  	cn_queue_free_dev(dev->cbdev);
b7c6ba6eb   Denis V. Lunev   [NETNS]: Consolid...
304
  	netlink_kernel_release(dev->nls);
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
305
  }
d6cc7f1a3   Evgeniy Polyakov   [CONNECTOR]: Init...
306
  subsys_initcall(cn_init);
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
307
  module_exit(cn_fini);