Blame view

drivers/connector/connector.c 6.81 KB
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
1
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
26
27
28
   * 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>
  #include <linux/netlink.h>
  #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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
  
  /*
   * 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.
   *
   * If we expect reply to our message then the sequence number in
   * 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.
   *
   */
dd0fc66fb   Al Viro   [PATCH] gfp flags...
65
  int cn_netlink_send(struct cn_msg *msg, u32 __group, gfp_t gfp_mask)
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
66
67
68
69
70
71
72
73
74
75
76
77
78
79
  {
  	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;
  
  	if (!__group) {
  		spin_lock_bh(&dev->cbdev->queue_lock);
  		list_for_each_entry(__cbq, &dev->cbdev->queue_list,
  				    callback_entry) {
acd042bb2   Evgeniy Polyakov   [CONNECTOR]: asyn...
80
  			if (cn_cb_equal(&__cbq->id.id, &msg->id)) {
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
81
82
  				found = 1;
  				group = __cbq->group;
fd00eeccd   Li Zefan   [CONNECTOR]: add ...
83
  				break;
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
84
85
86
87
88
89
90
91
92
  			}
  		}
  		spin_unlock_bh(&dev->cbdev->queue_lock);
  
  		if (!found)
  			return -ENODEV;
  	} else {
  		group = __group;
  	}
b191ba0d5   Evgeniy Polyakov   [CONNECTOR]: Use ...
93
94
  	if (!netlink_has_listeners(dev->nls, group))
  		return -ESRCH;
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
95
96
97
98
99
100
101
102
103
104
105
106
107
  	size = NLMSG_SPACE(sizeof(*msg) + msg->len);
  
  	skb = alloc_skb(size, gfp_mask);
  	if (!skb)
  		return -ENOMEM;
  
  	nlh = NLMSG_PUT(skb, 0, msg->seq, NLMSG_DONE, size - sizeof(*nlh));
  
  	data = NLMSG_DATA(nlh);
  
  	memcpy(data, msg, sizeof(*data) + msg->len);
  
  	NETLINK_CB(skb).dst_group = group;
b191ba0d5   Evgeniy Polyakov   [CONNECTOR]: Use ...
108
  	return netlink_broadcast(dev->nls, skb, 0, group, gfp_mask);
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
109
110
111
112
113
  
  nlmsg_failure:
  	kfree_skb(skb);
  	return -EINVAL;
  }
deb0e9b23   Andrew Morton   [PATCH] connector...
114
  EXPORT_SYMBOL_GPL(cn_netlink_send);
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
115
116
117
118
  
  /*
   * Callback helper - queues work and setup destructor for given data.
   */
f1489cfb1   Philipp Reisner   connector: Remove...
119
  static int cn_call_callback(struct sk_buff *skb)
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
120
  {
04f482faf   Patrick McHardy   connector: conver...
121
  	struct cn_callback_entry *i, *cbq = NULL;
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
122
  	struct cn_dev *dev = &cdev;
293500a23   Philipp Reisner   connector: Keep t...
123
  	struct cn_msg *msg = NLMSG_DATA(nlmsg_hdr(skb));
04f482faf   Patrick McHardy   connector: conver...
124
  	struct netlink_skb_parms *nsp = &NETLINK_CB(skb);
acd042bb2   Evgeniy Polyakov   [CONNECTOR]: asyn...
125
  	int err = -ENODEV;
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
126
127
  
  	spin_lock_bh(&dev->cbdev->queue_lock);
04f482faf   Patrick McHardy   connector: conver...
128
129
130
131
  	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...
132
133
134
135
  			break;
  		}
  	}
  	spin_unlock_bh(&dev->cbdev->queue_lock);
04f482faf   Patrick McHardy   connector: conver...
136
  	if (cbq != NULL) {
663dd6dca   K. Y. Srinivasan   Connector: Correc...
137
  		err = 0;
04f482faf   Patrick McHardy   connector: conver...
138
139
140
  		cbq->callback(msg, nsp);
  		kfree_skb(skb);
  		cn_queue_release_callback(cbq);
0e0878584   Patrick McHardy   connector: fix sk...
141
  		err = 0;
04f482faf   Patrick McHardy   connector: conver...
142
  	}
acd042bb2   Evgeniy Polyakov   [CONNECTOR]: asyn...
143
  	return err;
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
144
145
146
  }
  
  /*
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
147
148
   * Main netlink receiving function.
   *
00f5e06c0   Li Zefan   [CONNECTOR]: clea...
149
   * It checks skb, netlink header and msg sizes, and calls callback helper.
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
150
151
152
153
   */
  static void cn_rx_skb(struct sk_buff *__skb)
  {
  	struct nlmsghdr *nlh;
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
154
155
156
157
158
159
  	int err;
  	struct sk_buff *skb;
  
  	skb = skb_get(__skb);
  
  	if (skb->len >= NLMSG_SPACE(0)) {
b529ccf27   Arnaldo Carvalho de Melo   [NETLINK]: Introd...
160
  		nlh = nlmsg_hdr(skb);
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
161
162
163
164
165
  
  		if (nlh->nlmsg_len < sizeof(struct cn_msg) ||
  		    skb->len < nlh->nlmsg_len ||
  		    nlh->nlmsg_len > CONNECTOR_MAX_MSG_SIZE) {
  			kfree_skb(skb);
6cf92e98a   Michal Januszewski   [CONNECTOR]: Fix ...
166
  			return;
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
167
  		}
f1489cfb1   Philipp Reisner   connector: Remove...
168
  		err = cn_call_callback(skb);
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
169
170
171
  		if (err < 0)
  			kfree_skb(skb);
  	}
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
172
173
174
  }
  
  /*
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
175
176
177
178
179
   * 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...
180
  int cn_add_callback(struct cb_id *id, const char *name,
7069331db   Philipp Reisner   connector: Provid...
181
  		    void (*callback)(struct cn_msg *, struct netlink_skb_parms *))
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
182
183
184
  {
  	int err;
  	struct cn_dev *dev = &cdev;
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
185

d6cc7f1a3   Evgeniy Polyakov   [CONNECTOR]: Init...
186
187
  	if (!cn_already_initialized)
  		return -EAGAIN;
acd042bb2   Evgeniy Polyakov   [CONNECTOR]: asyn...
188
189
  	err = cn_queue_add_callback(dev->cbdev, name, id, callback);
  	if (err)
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
190
  		return err;
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
191

7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
192
193
  	return 0;
  }
deb0e9b23   Andrew Morton   [PATCH] connector...
194
  EXPORT_SYMBOL_GPL(cn_add_callback);
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
195
196
197
198
199
200
201
202
203
204
205
206
207
208
  
  /*
   * 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...
209
  }
deb0e9b23   Andrew Morton   [PATCH] connector...
210
  EXPORT_SYMBOL_GPL(cn_del_callback);
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
211

a0a61a604   Li Zefan   CONNECTOR: add a ...
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
  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
  };
d6cc7f1a3   Evgeniy Polyakov   [CONNECTOR]: Init...
247
  static int __devinit cn_init(void)
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
248
249
  {
  	struct cn_dev *dev = &cdev;
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
250

cd40b7d39   Denis V. Lunev   [NET]: make netli...
251
  	dev->input = cn_rx_skb;
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
252

b4b510290   Eric W. Biederman   [NET]: Support mu...
253
  	dev->nls = netlink_kernel_create(&init_net, NETLINK_CONNECTOR,
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
254
  					 CN_NETLINK_USERS + 0xf,
af65bdfce   Patrick McHardy   [NETLINK]: Switch...
255
  					 dev->input, NULL, THIS_MODULE);
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
256
257
258
259
260
  	if (!dev->nls)
  		return -EIO;
  
  	dev->cbdev = cn_queue_alloc_dev("cqueue", dev->nls);
  	if (!dev->cbdev) {
b7c6ba6eb   Denis V. Lunev   [NETNS]: Consolid...
261
  		netlink_kernel_release(dev->nls);
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
262
263
  		return -EINVAL;
  	}
1a5645bc9   Frederic Weisbecker   connector: create...
264

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

a0a61a604   Li Zefan   CONNECTOR: add a ...
267
  	proc_net_fops_create(&init_net, "connector", S_IRUGO, &cn_file_ops);
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
268
269
  	return 0;
  }
d6cc7f1a3   Evgeniy Polyakov   [CONNECTOR]: Init...
270
  static void __devexit cn_fini(void)
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
271
272
273
274
  {
  	struct cn_dev *dev = &cdev;
  
  	cn_already_initialized = 0;
a0a61a604   Li Zefan   CONNECTOR: add a ...
275
  	proc_net_remove(&init_net, "connector");
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
276
  	cn_queue_free_dev(dev->cbdev);
b7c6ba6eb   Denis V. Lunev   [NETNS]: Consolid...
277
  	netlink_kernel_release(dev->nls);
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
278
  }
d6cc7f1a3   Evgeniy Polyakov   [CONNECTOR]: Init...
279
  subsys_initcall(cn_init);
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
280
  module_exit(cn_fini);