aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorThomas Graf <tgraf@suug.ch>2005-11-09 20:25:56 -0500
committerThomas Graf <tgr@axs.localdomain>2005-11-09 20:26:41 -0500
commit482a8524f85a7d8c40c6fb5d072e85bc2fef327f (patch)
treecbd4225da63a687dd70441d41aad35ff5c171f48 /net
parent9ac4a16983ea4edf719c390a1a234d956947688d (diff)
[NETLINK]: Generic netlink family
The generic netlink family builds on top of netlink and provides simplifies access for the less demanding netlink users. It solves the problem of protocol numbers running out by introducing a so called controller taking care of id management and name resolving. Generic netlink modules register themself after filling out their id card (struct genl_family), after successful registration the modules are able to register callbacks to command numbers by filling out a struct genl_ops and calling genl_register_op(). The registered callbacks are invoked with attributes parsed making life of simple modules a lot easier. Although generic netlink modules can request static identifiers, it is recommended to use GENL_ID_GENERATE and to let the controller assign a unique identifier to the module. Userspace applications will then ask the controller and lookup the idenfier by the module name. Due to the current multicast implementation of netlink, the number of generic netlink modules is restricted to 1024 to avoid wasting memory for the per socket multiacst subscription bitmask. Signed-off-by: Thomas Graf <tgraf@suug.ch> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r--net/netlink/Makefile2
-rw-r--r--net/netlink/genetlink.c579
2 files changed, 580 insertions, 1 deletions
diff --git a/net/netlink/Makefile b/net/netlink/Makefile
index 68179015bd39..e3589c2de49e 100644
--- a/net/netlink/Makefile
+++ b/net/netlink/Makefile
@@ -2,4 +2,4 @@
2# Makefile for the netlink driver. 2# Makefile for the netlink driver.
3# 3#
4 4
5obj-y := af_netlink.o attr.o 5obj-y := af_netlink.o attr.o genetlink.o
diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c
new file mode 100644
index 000000000000..287cfcc56951
--- /dev/null
+++ b/net/netlink/genetlink.c
@@ -0,0 +1,579 @@
1/*
2 * NETLINK Generic Netlink Family
3 *
4 * Authors: Jamal Hadi Salim
5 * Thomas Graf <tgraf@suug.ch>
6 */
7
8#include <linux/config.h>
9#include <linux/module.h>
10#include <linux/kernel.h>
11#include <linux/errno.h>
12#include <linux/types.h>
13#include <linux/socket.h>
14#include <linux/string.h>
15#include <linux/skbuff.h>
16#include <net/sock.h>
17#include <net/genetlink.h>
18
19struct sock *genl_sock = NULL;
20
21static DECLARE_MUTEX(genl_sem); /* serialization of message processing */
22
23static void genl_lock(void)
24{
25 down(&genl_sem);
26}
27
28static int genl_trylock(void)
29{
30 return down_trylock(&genl_sem);
31}
32
33static void genl_unlock(void)
34{
35 up(&genl_sem);
36
37 if (genl_sock && genl_sock->sk_receive_queue.qlen)
38 genl_sock->sk_data_ready(genl_sock, 0);
39}
40
41#define GENL_FAM_TAB_SIZE 16
42#define GENL_FAM_TAB_MASK (GENL_FAM_TAB_SIZE - 1)
43
44static struct list_head family_ht[GENL_FAM_TAB_SIZE];
45
46static int genl_ctrl_event(int event, void *data);
47
48static inline unsigned int genl_family_hash(unsigned int id)
49{
50 return id & GENL_FAM_TAB_MASK;
51}
52
53static inline struct list_head *genl_family_chain(unsigned int id)
54{
55 return &family_ht[genl_family_hash(id)];
56}
57
58static struct genl_family *genl_family_find_byid(unsigned int id)
59{
60 struct genl_family *f;
61
62 list_for_each_entry(f, genl_family_chain(id), family_list)
63 if (f->id == id)
64 return f;
65
66 return NULL;
67}
68
69static struct genl_family *genl_family_find_byname(char *name)
70{
71 struct genl_family *f;
72 int i;
73
74 for (i = 0; i < GENL_FAM_TAB_SIZE; i++)
75 list_for_each_entry(f, genl_family_chain(i), family_list)
76 if (strcmp(f->name, name) == 0)
77 return f;
78
79 return NULL;
80}
81
82static struct genl_ops *genl_get_cmd(u8 cmd, struct genl_family *family)
83{
84 struct genl_ops *ops;
85
86 list_for_each_entry(ops, &family->ops_list, ops_list)
87 if (ops->cmd == cmd)
88 return ops;
89
90 return NULL;
91}
92
93/* Of course we are going to have problems once we hit
94 * 2^16 alive types, but that can only happen by year 2K
95*/
96static inline u16 genl_generate_id(void)
97{
98 static u16 id_gen_idx;
99 int overflowed = 0;
100
101 do {
102 if (id_gen_idx == 0)
103 id_gen_idx = GENL_MIN_ID;
104
105 if (++id_gen_idx > GENL_MAX_ID) {
106 if (!overflowed) {
107 overflowed = 1;
108 id_gen_idx = 0;
109 continue;
110 } else
111 return 0;
112 }
113
114 } while (genl_family_find_byid(id_gen_idx));
115
116 return id_gen_idx;
117}
118
119/**
120 * genl_register_ops - register generic netlink operations
121 * @family: generic netlink family
122 * @ops: operations to be registered
123 *
124 * Registers the specified operations and assigns them to the specified
125 * family. Either a doit or dumpit callback must be specified or the
126 * operation will fail. Only one operation structure per command
127 * identifier may be registered.
128 *
129 * See include/net/genetlink.h for more documenation on the operations
130 * structure.
131 *
132 * Returns 0 on success or a negative error code.
133 */
134int genl_register_ops(struct genl_family *family, struct genl_ops *ops)
135{
136 int err = -EINVAL;
137
138 if (ops->dumpit == NULL && ops->doit == NULL)
139 goto errout;
140
141 if (genl_get_cmd(ops->cmd, family)) {
142 err = -EEXIST;
143 goto errout;
144 }
145
146 genl_lock();
147 list_add_tail(&ops->ops_list, &family->ops_list);
148 genl_unlock();
149
150 genl_ctrl_event(CTRL_CMD_NEWOPS, ops);
151 err = 0;
152errout:
153 return err;
154}
155
156/**
157 * genl_unregister_ops - unregister generic netlink operations
158 * @family: generic netlink family
159 * @ops: operations to be unregistered
160 *
161 * Unregisters the specified operations and unassigns them from the
162 * specified family. The operation blocks until the current message
163 * processing has finished and doesn't start again until the
164 * unregister process has finished.
165 *
166 * Note: It is not necessary to unregister all operations before
167 * unregistering the family, unregistering the family will cause
168 * all assigned operations to be unregistered automatically.
169 *
170 * Returns 0 on success or a negative error code.
171 */
172int genl_unregister_ops(struct genl_family *family, struct genl_ops *ops)
173{
174 struct genl_ops *rc;
175
176 genl_lock();
177 list_for_each_entry(rc, &family->ops_list, ops_list) {
178 if (rc == ops) {
179 list_del(&ops->ops_list);
180 genl_unlock();
181 genl_ctrl_event(CTRL_CMD_DELOPS, ops);
182 return 0;
183 }
184 }
185 genl_unlock();
186
187 return -ENOENT;
188}
189
190/**
191 * genl_register_family - register a generic netlink family
192 * @family: generic netlink family
193 *
194 * Registers the specified family after validating it first. Only one
195 * family may be registered with the same family name or identifier.
196 * The family id may equal GENL_ID_GENERATE causing an unique id to
197 * be automatically generated and assigned.
198 *
199 * Return 0 on success or a negative error code.
200 */
201int genl_register_family(struct genl_family *family)
202{
203 int err = -EINVAL;
204
205 if (family->id && family->id < GENL_MIN_ID)
206 goto errout;
207
208 if (family->id > GENL_MAX_ID)
209 goto errout;
210
211 INIT_LIST_HEAD(&family->ops_list);
212
213 genl_lock();
214
215 if (genl_family_find_byname(family->name)) {
216 err = -EEXIST;
217 goto errout_locked;
218 }
219
220 if (genl_family_find_byid(family->id)) {
221 err = -EEXIST;
222 goto errout_locked;
223 }
224
225 if (!try_module_get(family->owner)) {
226 err = -EBUSY;
227 goto errout_locked;
228 }
229
230 if (family->id == GENL_ID_GENERATE) {
231 u16 newid = genl_generate_id();
232
233 if (!newid) {
234 err = -ENOMEM;
235 goto errout_locked;
236 }
237
238 family->id = newid;
239 }
240
241 if (family->maxattr) {
242 family->attrbuf = kmalloc((family->maxattr+1) *
243 sizeof(struct nlattr *), GFP_KERNEL);
244 if (family->attrbuf == NULL) {
245 err = -ENOMEM;
246 goto errout;
247 }
248 } else
249 family->attrbuf = NULL;
250
251 list_add_tail(&family->family_list, genl_family_chain(family->id));
252 genl_unlock();
253
254 genl_ctrl_event(CTRL_CMD_NEWFAMILY, family);
255
256 return 0;
257
258errout_locked:
259 genl_unlock();
260errout:
261 return err;
262}
263
264/**
265 * genl_unregister_family - unregister generic netlink family
266 * @family: generic netlink family
267 *
268 * Unregisters the specified family.
269 *
270 * Returns 0 on success or a negative error code.
271 */
272int genl_unregister_family(struct genl_family *family)
273{
274 struct genl_family *rc;
275
276 genl_lock();
277
278 list_for_each_entry(rc, genl_family_chain(family->id), family_list) {
279 if (family->id != rc->id || strcmp(rc->name, family->name))
280 continue;
281
282 list_del(&rc->family_list);
283 INIT_LIST_HEAD(&family->ops_list);
284 genl_unlock();
285
286 module_put(family->owner);
287 kfree(family->attrbuf);
288 genl_ctrl_event(CTRL_CMD_DELFAMILY, family);
289 return 0;
290 }
291
292 genl_unlock();
293
294 return -ENOENT;
295}
296
297static inline int genl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh,
298 int *errp)
299{
300 struct genl_ops *ops;
301 struct genl_family *family;
302 struct genl_info info;
303 struct genlmsghdr *hdr = nlmsg_data(nlh);
304 int hdrlen, err = -EINVAL;
305
306 if (!(nlh->nlmsg_flags & NLM_F_REQUEST))
307 goto ignore;
308
309 if (nlh->nlmsg_type < NLMSG_MIN_TYPE)
310 goto ignore;
311
312 family = genl_family_find_byid(nlh->nlmsg_type);
313 if (family == NULL) {
314 err = -ENOENT;
315 goto errout;
316 }
317
318 hdrlen = GENL_HDRLEN + family->hdrsize;
319 if (nlh->nlmsg_len < nlmsg_msg_size(hdrlen))
320 goto errout;
321
322 ops = genl_get_cmd(hdr->cmd, family);
323 if (ops == NULL) {
324 err = -EOPNOTSUPP;
325 goto errout;
326 }
327
328 if ((ops->flags & GENL_ADMIN_PERM) && security_netlink_recv(skb)) {
329 err = -EPERM;
330 goto errout;
331 }
332
333 if (nlh->nlmsg_flags & NLM_F_DUMP) {
334 if (ops->dumpit == NULL) {
335 err = -EOPNOTSUPP;
336 goto errout;
337 }
338
339 *errp = err = netlink_dump_start(genl_sock, skb, nlh,
340 ops->dumpit, NULL);
341 if (err == 0)
342 skb_pull(skb, min(NLMSG_ALIGN(nlh->nlmsg_len),
343 skb->len));
344 return -1;
345 }
346
347 if (ops->doit == NULL) {
348 err = -EOPNOTSUPP;
349 goto errout;
350 }
351
352 if (family->attrbuf) {
353 err = nlmsg_parse(nlh, hdrlen, family->attrbuf, family->maxattr,
354 ops->policy);
355 if (err < 0)
356 goto errout;
357 }
358
359 info.snd_seq = nlh->nlmsg_seq;
360 info.snd_pid = NETLINK_CB(skb).pid;
361 info.nlhdr = nlh;
362 info.genlhdr = nlmsg_data(nlh);
363 info.userhdr = nlmsg_data(nlh) + GENL_HDRLEN;
364 info.attrs = family->attrbuf;
365
366 *errp = err = ops->doit(skb, &info);
367 return err;
368
369ignore:
370 return 0;
371
372errout:
373 *errp = err;
374 return -1;
375}
376
377static void genl_rcv(struct sock *sk, int len)
378{
379 unsigned int qlen = 0;
380
381 do {
382 if (genl_trylock())
383 return;
384 netlink_run_queue(sk, &qlen, &genl_rcv_msg);
385 genl_unlock();
386 } while (qlen && genl_sock && genl_sock->sk_receive_queue.qlen);
387}
388
389/**************************************************************************
390 * Controller
391 **************************************************************************/
392
393static int ctrl_fill_info(struct genl_family *family, u32 pid, u32 seq,
394 u32 flags, struct sk_buff *skb, u8 cmd)
395{
396 void *hdr;
397
398 hdr = genlmsg_put(skb, pid, seq, GENL_ID_CTRL, 0, flags, cmd,
399 family->version);
400 if (hdr == NULL)
401 return -1;
402
403 NLA_PUT_STRING(skb, CTRL_ATTR_FAMILY_NAME, family->name);
404 NLA_PUT_U16(skb, CTRL_ATTR_FAMILY_ID, family->id);
405
406 return genlmsg_end(skb, hdr);
407
408nla_put_failure:
409 return genlmsg_cancel(skb, hdr);
410}
411
412static int ctrl_dumpfamily(struct sk_buff *skb, struct netlink_callback *cb)
413{
414
415 int i, n = 0;
416 struct genl_family *rt;
417 int chains_to_skip = cb->args[0];
418 int fams_to_skip = cb->args[1];
419
420 for (i = 0; i < GENL_FAM_TAB_SIZE; i++) {
421 if (i < chains_to_skip)
422 continue;
423 n = 0;
424 list_for_each_entry(rt, genl_family_chain(i), family_list) {
425 if (++n < fams_to_skip)
426 continue;
427 if (ctrl_fill_info(rt, NETLINK_CB(cb->skb).pid,
428 cb->nlh->nlmsg_seq, NLM_F_MULTI,
429 skb, CTRL_CMD_NEWFAMILY) < 0)
430 goto errout;
431 }
432
433 fams_to_skip = 0;
434 }
435
436errout:
437 cb->args[0] = i;
438 cb->args[1] = n;
439
440 return skb->len;
441}
442
443static struct sk_buff *ctrl_build_msg(struct genl_family *family, u32 pid,
444 int seq, int cmd)
445{
446 struct sk_buff *skb;
447 int err;
448
449 skb = nlmsg_new(NLMSG_GOODSIZE);
450 if (skb == NULL)
451 return ERR_PTR(-ENOBUFS);
452
453 err = ctrl_fill_info(family, pid, seq, 0, skb, cmd);
454 if (err < 0) {
455 nlmsg_free(skb);
456 return ERR_PTR(err);
457 }
458
459 return skb;
460}
461
462static struct nla_policy ctrl_policy[CTRL_ATTR_MAX+1] __read_mostly = {
463 [CTRL_ATTR_FAMILY_ID] = { .type = NLA_U16 },
464 [CTRL_ATTR_FAMILY_NAME] = { .type = NLA_STRING },
465};
466
467static int ctrl_getfamily(struct sk_buff *skb, struct genl_info *info)
468{
469 struct sk_buff *msg;
470 struct genl_family *res = NULL;
471 int err = -EINVAL;
472
473 if (info->attrs[CTRL_ATTR_FAMILY_ID]) {
474 u16 id = nla_get_u16(info->attrs[CTRL_ATTR_FAMILY_ID]);
475 res = genl_family_find_byid(id);
476 }
477
478 if (info->attrs[CTRL_ATTR_FAMILY_NAME]) {
479 char name[GENL_NAMSIZ];
480
481 if (nla_strlcpy(name, info->attrs[CTRL_ATTR_FAMILY_NAME],
482 GENL_NAMSIZ) >= GENL_NAMSIZ)
483 goto errout;
484
485 res = genl_family_find_byname(name);
486 }
487
488 if (res == NULL) {
489 err = -ENOENT;
490 goto errout;
491 }
492
493 msg = ctrl_build_msg(res, info->snd_pid, info->snd_seq,
494 CTRL_CMD_NEWFAMILY);
495 if (IS_ERR(msg)) {
496 err = PTR_ERR(msg);
497 goto errout;
498 }
499
500 err = genlmsg_unicast(msg, info->snd_pid);
501errout:
502 return err;
503}
504
505static int genl_ctrl_event(int event, void *data)
506{
507 struct sk_buff *msg;
508
509 if (genl_sock == NULL)
510 return 0;
511
512 switch (event) {
513 case CTRL_CMD_NEWFAMILY:
514 case CTRL_CMD_DELFAMILY:
515 msg = ctrl_build_msg(data, 0, 0, event);
516 if (IS_ERR(msg))
517 return PTR_ERR(msg);
518
519 genlmsg_multicast(msg, 0, GENL_ID_CTRL);
520 break;
521 }
522
523 return 0;
524}
525
526static struct genl_ops genl_ctrl_ops = {
527 .cmd = CTRL_CMD_GETFAMILY,
528 .doit = ctrl_getfamily,
529 .dumpit = ctrl_dumpfamily,
530 .policy = ctrl_policy,
531};
532
533static struct genl_family genl_ctrl = {
534 .id = GENL_ID_CTRL,
535 .name = "nlctrl",
536 .version = 0x1,
537 .maxattr = CTRL_ATTR_MAX,
538 .owner = THIS_MODULE,
539};
540
541static int __init genl_init(void)
542{
543 int i, err;
544
545 for (i = 0; i < GENL_FAM_TAB_SIZE; i++)
546 INIT_LIST_HEAD(&family_ht[i]);
547
548 err = genl_register_family(&genl_ctrl);
549 if (err < 0)
550 goto errout;
551
552 err = genl_register_ops(&genl_ctrl, &genl_ctrl_ops);
553 if (err < 0)
554 goto errout_register;
555
556 netlink_set_nonroot(NETLINK_GENERIC, NL_NONROOT_RECV);
557 genl_sock = netlink_kernel_create(NETLINK_GENERIC, GENL_MAX_ID,
558 genl_rcv, THIS_MODULE);
559 if (genl_sock == NULL) {
560 panic("GENL: Cannot initialize generic netlink\n");
561 return -ENOMEM;
562 }
563
564 return 0;
565
566errout_register:
567 genl_unregister_family(&genl_ctrl);
568errout:
569 panic("GENL: Cannot register controller: %d\n", err);
570 return err;
571}
572
573subsys_initcall(genl_init);
574
575EXPORT_SYMBOL(genl_sock);
576EXPORT_SYMBOL(genl_register_ops);
577EXPORT_SYMBOL(genl_unregister_ops);
578EXPORT_SYMBOL(genl_register_family);
579EXPORT_SYMBOL(genl_unregister_family);