diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /net/core/rtnetlink.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'net/core/rtnetlink.c')
-rw-r--r-- | net/core/rtnetlink.c | 711 |
1 files changed, 711 insertions, 0 deletions
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c new file mode 100644 index 000000000000..d69ad90e5811 --- /dev/null +++ b/net/core/rtnetlink.c | |||
@@ -0,0 +1,711 @@ | |||
1 | /* | ||
2 | * INET An implementation of the TCP/IP protocol suite for the LINUX | ||
3 | * operating system. INET is implemented using the BSD Socket | ||
4 | * interface as the means of communication with the user level. | ||
5 | * | ||
6 | * Routing netlink socket interface: protocol independent part. | ||
7 | * | ||
8 | * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or | ||
11 | * modify it under the terms of the GNU General Public License | ||
12 | * as published by the Free Software Foundation; either version | ||
13 | * 2 of the License, or (at your option) any later version. | ||
14 | * | ||
15 | * Fixes: | ||
16 | * Vitaly E. Lavrov RTA_OK arithmetics was wrong. | ||
17 | */ | ||
18 | |||
19 | #include <linux/config.h> | ||
20 | #include <linux/errno.h> | ||
21 | #include <linux/module.h> | ||
22 | #include <linux/types.h> | ||
23 | #include <linux/socket.h> | ||
24 | #include <linux/kernel.h> | ||
25 | #include <linux/major.h> | ||
26 | #include <linux/sched.h> | ||
27 | #include <linux/timer.h> | ||
28 | #include <linux/string.h> | ||
29 | #include <linux/sockios.h> | ||
30 | #include <linux/net.h> | ||
31 | #include <linux/fcntl.h> | ||
32 | #include <linux/mm.h> | ||
33 | #include <linux/slab.h> | ||
34 | #include <linux/interrupt.h> | ||
35 | #include <linux/capability.h> | ||
36 | #include <linux/skbuff.h> | ||
37 | #include <linux/init.h> | ||
38 | #include <linux/security.h> | ||
39 | |||
40 | #include <asm/uaccess.h> | ||
41 | #include <asm/system.h> | ||
42 | #include <asm/string.h> | ||
43 | |||
44 | #include <linux/inet.h> | ||
45 | #include <linux/netdevice.h> | ||
46 | #include <net/ip.h> | ||
47 | #include <net/protocol.h> | ||
48 | #include <net/arp.h> | ||
49 | #include <net/route.h> | ||
50 | #include <net/udp.h> | ||
51 | #include <net/sock.h> | ||
52 | #include <net/pkt_sched.h> | ||
53 | |||
54 | DECLARE_MUTEX(rtnl_sem); | ||
55 | |||
56 | void rtnl_lock(void) | ||
57 | { | ||
58 | rtnl_shlock(); | ||
59 | } | ||
60 | |||
61 | int rtnl_lock_interruptible(void) | ||
62 | { | ||
63 | return down_interruptible(&rtnl_sem); | ||
64 | } | ||
65 | |||
66 | void rtnl_unlock(void) | ||
67 | { | ||
68 | rtnl_shunlock(); | ||
69 | |||
70 | netdev_run_todo(); | ||
71 | } | ||
72 | |||
73 | int rtattr_parse(struct rtattr *tb[], int maxattr, struct rtattr *rta, int len) | ||
74 | { | ||
75 | memset(tb, 0, sizeof(struct rtattr*)*maxattr); | ||
76 | |||
77 | while (RTA_OK(rta, len)) { | ||
78 | unsigned flavor = rta->rta_type; | ||
79 | if (flavor && flavor <= maxattr) | ||
80 | tb[flavor-1] = rta; | ||
81 | rta = RTA_NEXT(rta, len); | ||
82 | } | ||
83 | return 0; | ||
84 | } | ||
85 | |||
86 | struct sock *rtnl; | ||
87 | |||
88 | struct rtnetlink_link * rtnetlink_links[NPROTO]; | ||
89 | |||
90 | static const int rtm_min[(RTM_MAX+1-RTM_BASE)/4] = | ||
91 | { | ||
92 | NLMSG_LENGTH(sizeof(struct ifinfomsg)), | ||
93 | NLMSG_LENGTH(sizeof(struct ifaddrmsg)), | ||
94 | NLMSG_LENGTH(sizeof(struct rtmsg)), | ||
95 | NLMSG_LENGTH(sizeof(struct ndmsg)), | ||
96 | NLMSG_LENGTH(sizeof(struct rtmsg)), | ||
97 | NLMSG_LENGTH(sizeof(struct tcmsg)), | ||
98 | NLMSG_LENGTH(sizeof(struct tcmsg)), | ||
99 | NLMSG_LENGTH(sizeof(struct tcmsg)), | ||
100 | NLMSG_LENGTH(sizeof(struct tcamsg)) | ||
101 | }; | ||
102 | |||
103 | static const int rta_max[(RTM_MAX+1-RTM_BASE)/4] = | ||
104 | { | ||
105 | IFLA_MAX, | ||
106 | IFA_MAX, | ||
107 | RTA_MAX, | ||
108 | NDA_MAX, | ||
109 | RTA_MAX, | ||
110 | TCA_MAX, | ||
111 | TCA_MAX, | ||
112 | TCA_MAX, | ||
113 | TCAA_MAX | ||
114 | }; | ||
115 | |||
116 | void __rta_fill(struct sk_buff *skb, int attrtype, int attrlen, const void *data) | ||
117 | { | ||
118 | struct rtattr *rta; | ||
119 | int size = RTA_LENGTH(attrlen); | ||
120 | |||
121 | rta = (struct rtattr*)skb_put(skb, RTA_ALIGN(size)); | ||
122 | rta->rta_type = attrtype; | ||
123 | rta->rta_len = size; | ||
124 | memcpy(RTA_DATA(rta), data, attrlen); | ||
125 | } | ||
126 | |||
127 | size_t rtattr_strlcpy(char *dest, const struct rtattr *rta, size_t size) | ||
128 | { | ||
129 | size_t ret = RTA_PAYLOAD(rta); | ||
130 | char *src = RTA_DATA(rta); | ||
131 | |||
132 | if (ret > 0 && src[ret - 1] == '\0') | ||
133 | ret--; | ||
134 | if (size > 0) { | ||
135 | size_t len = (ret >= size) ? size - 1 : ret; | ||
136 | memset(dest, 0, size); | ||
137 | memcpy(dest, src, len); | ||
138 | } | ||
139 | return ret; | ||
140 | } | ||
141 | |||
142 | int rtnetlink_send(struct sk_buff *skb, u32 pid, unsigned group, int echo) | ||
143 | { | ||
144 | int err = 0; | ||
145 | |||
146 | NETLINK_CB(skb).dst_groups = group; | ||
147 | if (echo) | ||
148 | atomic_inc(&skb->users); | ||
149 | netlink_broadcast(rtnl, skb, pid, group, GFP_KERNEL); | ||
150 | if (echo) | ||
151 | err = netlink_unicast(rtnl, skb, pid, MSG_DONTWAIT); | ||
152 | return err; | ||
153 | } | ||
154 | |||
155 | int rtnetlink_put_metrics(struct sk_buff *skb, u32 *metrics) | ||
156 | { | ||
157 | struct rtattr *mx = (struct rtattr*)skb->tail; | ||
158 | int i; | ||
159 | |||
160 | RTA_PUT(skb, RTA_METRICS, 0, NULL); | ||
161 | for (i=0; i<RTAX_MAX; i++) { | ||
162 | if (metrics[i]) | ||
163 | RTA_PUT(skb, i+1, sizeof(u32), metrics+i); | ||
164 | } | ||
165 | mx->rta_len = skb->tail - (u8*)mx; | ||
166 | if (mx->rta_len == RTA_LENGTH(0)) | ||
167 | skb_trim(skb, (u8*)mx - skb->data); | ||
168 | return 0; | ||
169 | |||
170 | rtattr_failure: | ||
171 | skb_trim(skb, (u8*)mx - skb->data); | ||
172 | return -1; | ||
173 | } | ||
174 | |||
175 | |||
176 | static int rtnetlink_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, | ||
177 | int type, u32 pid, u32 seq, u32 change) | ||
178 | { | ||
179 | struct ifinfomsg *r; | ||
180 | struct nlmsghdr *nlh; | ||
181 | unsigned char *b = skb->tail; | ||
182 | |||
183 | nlh = NLMSG_PUT(skb, pid, seq, type, sizeof(*r)); | ||
184 | if (pid) nlh->nlmsg_flags |= NLM_F_MULTI; | ||
185 | r = NLMSG_DATA(nlh); | ||
186 | r->ifi_family = AF_UNSPEC; | ||
187 | r->ifi_type = dev->type; | ||
188 | r->ifi_index = dev->ifindex; | ||
189 | r->ifi_flags = dev_get_flags(dev); | ||
190 | r->ifi_change = change; | ||
191 | |||
192 | RTA_PUT(skb, IFLA_IFNAME, strlen(dev->name)+1, dev->name); | ||
193 | |||
194 | if (1) { | ||
195 | u32 txqlen = dev->tx_queue_len; | ||
196 | RTA_PUT(skb, IFLA_TXQLEN, sizeof(txqlen), &txqlen); | ||
197 | } | ||
198 | |||
199 | if (1) { | ||
200 | u32 weight = dev->weight; | ||
201 | RTA_PUT(skb, IFLA_WEIGHT, sizeof(weight), &weight); | ||
202 | } | ||
203 | |||
204 | if (1) { | ||
205 | struct rtnl_link_ifmap map = { | ||
206 | .mem_start = dev->mem_start, | ||
207 | .mem_end = dev->mem_end, | ||
208 | .base_addr = dev->base_addr, | ||
209 | .irq = dev->irq, | ||
210 | .dma = dev->dma, | ||
211 | .port = dev->if_port, | ||
212 | }; | ||
213 | RTA_PUT(skb, IFLA_MAP, sizeof(map), &map); | ||
214 | } | ||
215 | |||
216 | if (dev->addr_len) { | ||
217 | RTA_PUT(skb, IFLA_ADDRESS, dev->addr_len, dev->dev_addr); | ||
218 | RTA_PUT(skb, IFLA_BROADCAST, dev->addr_len, dev->broadcast); | ||
219 | } | ||
220 | |||
221 | if (1) { | ||
222 | u32 mtu = dev->mtu; | ||
223 | RTA_PUT(skb, IFLA_MTU, sizeof(mtu), &mtu); | ||
224 | } | ||
225 | |||
226 | if (dev->ifindex != dev->iflink) { | ||
227 | u32 iflink = dev->iflink; | ||
228 | RTA_PUT(skb, IFLA_LINK, sizeof(iflink), &iflink); | ||
229 | } | ||
230 | |||
231 | if (dev->qdisc_sleeping) | ||
232 | RTA_PUT(skb, IFLA_QDISC, | ||
233 | strlen(dev->qdisc_sleeping->ops->id) + 1, | ||
234 | dev->qdisc_sleeping->ops->id); | ||
235 | |||
236 | if (dev->master) { | ||
237 | u32 master = dev->master->ifindex; | ||
238 | RTA_PUT(skb, IFLA_MASTER, sizeof(master), &master); | ||
239 | } | ||
240 | |||
241 | if (dev->get_stats) { | ||
242 | unsigned long *stats = (unsigned long*)dev->get_stats(dev); | ||
243 | if (stats) { | ||
244 | struct rtattr *a; | ||
245 | __u32 *s; | ||
246 | int i; | ||
247 | int n = sizeof(struct rtnl_link_stats)/4; | ||
248 | |||
249 | a = __RTA_PUT(skb, IFLA_STATS, n*4); | ||
250 | s = RTA_DATA(a); | ||
251 | for (i=0; i<n; i++) | ||
252 | s[i] = stats[i]; | ||
253 | } | ||
254 | } | ||
255 | nlh->nlmsg_len = skb->tail - b; | ||
256 | return skb->len; | ||
257 | |||
258 | nlmsg_failure: | ||
259 | rtattr_failure: | ||
260 | skb_trim(skb, b - skb->data); | ||
261 | return -1; | ||
262 | } | ||
263 | |||
264 | static int rtnetlink_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb) | ||
265 | { | ||
266 | int idx; | ||
267 | int s_idx = cb->args[0]; | ||
268 | struct net_device *dev; | ||
269 | |||
270 | read_lock(&dev_base_lock); | ||
271 | for (dev=dev_base, idx=0; dev; dev = dev->next, idx++) { | ||
272 | if (idx < s_idx) | ||
273 | continue; | ||
274 | if (rtnetlink_fill_ifinfo(skb, dev, RTM_NEWLINK, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq, 0) <= 0) | ||
275 | break; | ||
276 | } | ||
277 | read_unlock(&dev_base_lock); | ||
278 | cb->args[0] = idx; | ||
279 | |||
280 | return skb->len; | ||
281 | } | ||
282 | |||
283 | static int do_setlink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) | ||
284 | { | ||
285 | struct ifinfomsg *ifm = NLMSG_DATA(nlh); | ||
286 | struct rtattr **ida = arg; | ||
287 | struct net_device *dev; | ||
288 | int err, send_addr_notify = 0; | ||
289 | |||
290 | if (ifm->ifi_index >= 0) | ||
291 | dev = dev_get_by_index(ifm->ifi_index); | ||
292 | else if (ida[IFLA_IFNAME - 1]) { | ||
293 | char ifname[IFNAMSIZ]; | ||
294 | |||
295 | if (rtattr_strlcpy(ifname, ida[IFLA_IFNAME - 1], | ||
296 | IFNAMSIZ) >= IFNAMSIZ) | ||
297 | return -EINVAL; | ||
298 | dev = dev_get_by_name(ifname); | ||
299 | } else | ||
300 | return -EINVAL; | ||
301 | |||
302 | if (!dev) | ||
303 | return -ENODEV; | ||
304 | |||
305 | err = -EINVAL; | ||
306 | |||
307 | if (ifm->ifi_flags) | ||
308 | dev_change_flags(dev, ifm->ifi_flags); | ||
309 | |||
310 | if (ida[IFLA_MAP - 1]) { | ||
311 | struct rtnl_link_ifmap *u_map; | ||
312 | struct ifmap k_map; | ||
313 | |||
314 | if (!dev->set_config) { | ||
315 | err = -EOPNOTSUPP; | ||
316 | goto out; | ||
317 | } | ||
318 | |||
319 | if (!netif_device_present(dev)) { | ||
320 | err = -ENODEV; | ||
321 | goto out; | ||
322 | } | ||
323 | |||
324 | if (ida[IFLA_MAP - 1]->rta_len != RTA_LENGTH(sizeof(*u_map))) | ||
325 | goto out; | ||
326 | |||
327 | u_map = RTA_DATA(ida[IFLA_MAP - 1]); | ||
328 | |||
329 | k_map.mem_start = (unsigned long) u_map->mem_start; | ||
330 | k_map.mem_end = (unsigned long) u_map->mem_end; | ||
331 | k_map.base_addr = (unsigned short) u_map->base_addr; | ||
332 | k_map.irq = (unsigned char) u_map->irq; | ||
333 | k_map.dma = (unsigned char) u_map->dma; | ||
334 | k_map.port = (unsigned char) u_map->port; | ||
335 | |||
336 | err = dev->set_config(dev, &k_map); | ||
337 | |||
338 | if (err) | ||
339 | goto out; | ||
340 | } | ||
341 | |||
342 | if (ida[IFLA_ADDRESS - 1]) { | ||
343 | if (!dev->set_mac_address) { | ||
344 | err = -EOPNOTSUPP; | ||
345 | goto out; | ||
346 | } | ||
347 | if (!netif_device_present(dev)) { | ||
348 | err = -ENODEV; | ||
349 | goto out; | ||
350 | } | ||
351 | if (ida[IFLA_ADDRESS - 1]->rta_len != RTA_LENGTH(dev->addr_len)) | ||
352 | goto out; | ||
353 | |||
354 | err = dev->set_mac_address(dev, RTA_DATA(ida[IFLA_ADDRESS - 1])); | ||
355 | if (err) | ||
356 | goto out; | ||
357 | send_addr_notify = 1; | ||
358 | } | ||
359 | |||
360 | if (ida[IFLA_BROADCAST - 1]) { | ||
361 | if (ida[IFLA_BROADCAST - 1]->rta_len != RTA_LENGTH(dev->addr_len)) | ||
362 | goto out; | ||
363 | memcpy(dev->broadcast, RTA_DATA(ida[IFLA_BROADCAST - 1]), | ||
364 | dev->addr_len); | ||
365 | send_addr_notify = 1; | ||
366 | } | ||
367 | |||
368 | if (ida[IFLA_MTU - 1]) { | ||
369 | if (ida[IFLA_MTU - 1]->rta_len != RTA_LENGTH(sizeof(u32))) | ||
370 | goto out; | ||
371 | err = dev_set_mtu(dev, *((u32 *) RTA_DATA(ida[IFLA_MTU - 1]))); | ||
372 | |||
373 | if (err) | ||
374 | goto out; | ||
375 | |||
376 | } | ||
377 | |||
378 | if (ida[IFLA_TXQLEN - 1]) { | ||
379 | if (ida[IFLA_TXQLEN - 1]->rta_len != RTA_LENGTH(sizeof(u32))) | ||
380 | goto out; | ||
381 | |||
382 | dev->tx_queue_len = *((u32 *) RTA_DATA(ida[IFLA_TXQLEN - 1])); | ||
383 | } | ||
384 | |||
385 | if (ida[IFLA_WEIGHT - 1]) { | ||
386 | if (ida[IFLA_WEIGHT - 1]->rta_len != RTA_LENGTH(sizeof(u32))) | ||
387 | goto out; | ||
388 | |||
389 | dev->weight = *((u32 *) RTA_DATA(ida[IFLA_WEIGHT - 1])); | ||
390 | } | ||
391 | |||
392 | if (ifm->ifi_index >= 0 && ida[IFLA_IFNAME - 1]) { | ||
393 | char ifname[IFNAMSIZ]; | ||
394 | |||
395 | if (rtattr_strlcpy(ifname, ida[IFLA_IFNAME - 1], | ||
396 | IFNAMSIZ) >= IFNAMSIZ) | ||
397 | goto out; | ||
398 | err = dev_change_name(dev, ifname); | ||
399 | if (err) | ||
400 | goto out; | ||
401 | } | ||
402 | |||
403 | err = 0; | ||
404 | |||
405 | out: | ||
406 | if (send_addr_notify) | ||
407 | call_netdevice_notifiers(NETDEV_CHANGEADDR, dev); | ||
408 | |||
409 | dev_put(dev); | ||
410 | return err; | ||
411 | } | ||
412 | |||
413 | static int rtnetlink_dump_all(struct sk_buff *skb, struct netlink_callback *cb) | ||
414 | { | ||
415 | int idx; | ||
416 | int s_idx = cb->family; | ||
417 | |||
418 | if (s_idx == 0) | ||
419 | s_idx = 1; | ||
420 | for (idx=1; idx<NPROTO; idx++) { | ||
421 | int type = cb->nlh->nlmsg_type-RTM_BASE; | ||
422 | if (idx < s_idx || idx == PF_PACKET) | ||
423 | continue; | ||
424 | if (rtnetlink_links[idx] == NULL || | ||
425 | rtnetlink_links[idx][type].dumpit == NULL) | ||
426 | continue; | ||
427 | if (idx > s_idx) | ||
428 | memset(&cb->args[0], 0, sizeof(cb->args)); | ||
429 | if (rtnetlink_links[idx][type].dumpit(skb, cb)) | ||
430 | break; | ||
431 | } | ||
432 | cb->family = idx; | ||
433 | |||
434 | return skb->len; | ||
435 | } | ||
436 | |||
437 | void rtmsg_ifinfo(int type, struct net_device *dev, unsigned change) | ||
438 | { | ||
439 | struct sk_buff *skb; | ||
440 | int size = NLMSG_SPACE(sizeof(struct ifinfomsg) + | ||
441 | sizeof(struct rtnl_link_ifmap) + | ||
442 | sizeof(struct rtnl_link_stats) + 128); | ||
443 | |||
444 | skb = alloc_skb(size, GFP_KERNEL); | ||
445 | if (!skb) | ||
446 | return; | ||
447 | |||
448 | if (rtnetlink_fill_ifinfo(skb, dev, type, 0, 0, change) < 0) { | ||
449 | kfree_skb(skb); | ||
450 | return; | ||
451 | } | ||
452 | NETLINK_CB(skb).dst_groups = RTMGRP_LINK; | ||
453 | netlink_broadcast(rtnl, skb, 0, RTMGRP_LINK, GFP_KERNEL); | ||
454 | } | ||
455 | |||
456 | static int rtnetlink_done(struct netlink_callback *cb) | ||
457 | { | ||
458 | return 0; | ||
459 | } | ||
460 | |||
461 | /* Protected by RTNL sempahore. */ | ||
462 | static struct rtattr **rta_buf; | ||
463 | static int rtattr_max; | ||
464 | |||
465 | /* Process one rtnetlink message. */ | ||
466 | |||
467 | static __inline__ int | ||
468 | rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, int *errp) | ||
469 | { | ||
470 | struct rtnetlink_link *link; | ||
471 | struct rtnetlink_link *link_tab; | ||
472 | int sz_idx, kind; | ||
473 | int min_len; | ||
474 | int family; | ||
475 | int type; | ||
476 | int err; | ||
477 | |||
478 | /* Only requests are handled by kernel now */ | ||
479 | if (!(nlh->nlmsg_flags&NLM_F_REQUEST)) | ||
480 | return 0; | ||
481 | |||
482 | type = nlh->nlmsg_type; | ||
483 | |||
484 | /* A control message: ignore them */ | ||
485 | if (type < RTM_BASE) | ||
486 | return 0; | ||
487 | |||
488 | /* Unknown message: reply with EINVAL */ | ||
489 | if (type > RTM_MAX) | ||
490 | goto err_inval; | ||
491 | |||
492 | type -= RTM_BASE; | ||
493 | |||
494 | /* All the messages must have at least 1 byte length */ | ||
495 | if (nlh->nlmsg_len < NLMSG_LENGTH(sizeof(struct rtgenmsg))) | ||
496 | return 0; | ||
497 | |||
498 | family = ((struct rtgenmsg*)NLMSG_DATA(nlh))->rtgen_family; | ||
499 | if (family >= NPROTO) { | ||
500 | *errp = -EAFNOSUPPORT; | ||
501 | return -1; | ||
502 | } | ||
503 | |||
504 | link_tab = rtnetlink_links[family]; | ||
505 | if (link_tab == NULL) | ||
506 | link_tab = rtnetlink_links[PF_UNSPEC]; | ||
507 | link = &link_tab[type]; | ||
508 | |||
509 | sz_idx = type>>2; | ||
510 | kind = type&3; | ||
511 | |||
512 | if (kind != 2 && security_netlink_recv(skb)) { | ||
513 | *errp = -EPERM; | ||
514 | return -1; | ||
515 | } | ||
516 | |||
517 | if (kind == 2 && nlh->nlmsg_flags&NLM_F_DUMP) { | ||
518 | u32 rlen; | ||
519 | |||
520 | if (link->dumpit == NULL) | ||
521 | link = &(rtnetlink_links[PF_UNSPEC][type]); | ||
522 | |||
523 | if (link->dumpit == NULL) | ||
524 | goto err_inval; | ||
525 | |||
526 | if ((*errp = netlink_dump_start(rtnl, skb, nlh, | ||
527 | link->dumpit, | ||
528 | rtnetlink_done)) != 0) { | ||
529 | return -1; | ||
530 | } | ||
531 | rlen = NLMSG_ALIGN(nlh->nlmsg_len); | ||
532 | if (rlen > skb->len) | ||
533 | rlen = skb->len; | ||
534 | skb_pull(skb, rlen); | ||
535 | return -1; | ||
536 | } | ||
537 | |||
538 | memset(rta_buf, 0, (rtattr_max * sizeof(struct rtattr *))); | ||
539 | |||
540 | min_len = rtm_min[sz_idx]; | ||
541 | if (nlh->nlmsg_len < min_len) | ||
542 | goto err_inval; | ||
543 | |||
544 | if (nlh->nlmsg_len > min_len) { | ||
545 | int attrlen = nlh->nlmsg_len - NLMSG_ALIGN(min_len); | ||
546 | struct rtattr *attr = (void*)nlh + NLMSG_ALIGN(min_len); | ||
547 | |||
548 | while (RTA_OK(attr, attrlen)) { | ||
549 | unsigned flavor = attr->rta_type; | ||
550 | if (flavor) { | ||
551 | if (flavor > rta_max[sz_idx]) | ||
552 | goto err_inval; | ||
553 | rta_buf[flavor-1] = attr; | ||
554 | } | ||
555 | attr = RTA_NEXT(attr, attrlen); | ||
556 | } | ||
557 | } | ||
558 | |||
559 | if (link->doit == NULL) | ||
560 | link = &(rtnetlink_links[PF_UNSPEC][type]); | ||
561 | if (link->doit == NULL) | ||
562 | goto err_inval; | ||
563 | err = link->doit(skb, nlh, (void *)&rta_buf[0]); | ||
564 | |||
565 | *errp = err; | ||
566 | return err; | ||
567 | |||
568 | err_inval: | ||
569 | *errp = -EINVAL; | ||
570 | return -1; | ||
571 | } | ||
572 | |||
573 | /* | ||
574 | * Process one packet of messages. | ||
575 | * Malformed skbs with wrong lengths of messages are discarded silently. | ||
576 | */ | ||
577 | |||
578 | static inline int rtnetlink_rcv_skb(struct sk_buff *skb) | ||
579 | { | ||
580 | int err; | ||
581 | struct nlmsghdr * nlh; | ||
582 | |||
583 | while (skb->len >= NLMSG_SPACE(0)) { | ||
584 | u32 rlen; | ||
585 | |||
586 | nlh = (struct nlmsghdr *)skb->data; | ||
587 | if (nlh->nlmsg_len < sizeof(*nlh) || skb->len < nlh->nlmsg_len) | ||
588 | return 0; | ||
589 | rlen = NLMSG_ALIGN(nlh->nlmsg_len); | ||
590 | if (rlen > skb->len) | ||
591 | rlen = skb->len; | ||
592 | if (rtnetlink_rcv_msg(skb, nlh, &err)) { | ||
593 | /* Not error, but we must interrupt processing here: | ||
594 | * Note, that in this case we do not pull message | ||
595 | * from skb, it will be processed later. | ||
596 | */ | ||
597 | if (err == 0) | ||
598 | return -1; | ||
599 | netlink_ack(skb, nlh, err); | ||
600 | } else if (nlh->nlmsg_flags&NLM_F_ACK) | ||
601 | netlink_ack(skb, nlh, 0); | ||
602 | skb_pull(skb, rlen); | ||
603 | } | ||
604 | |||
605 | return 0; | ||
606 | } | ||
607 | |||
608 | /* | ||
609 | * rtnetlink input queue processing routine: | ||
610 | * - try to acquire shared lock. If it is failed, defer processing. | ||
611 | * - feed skbs to rtnetlink_rcv_skb, until it refuse a message, | ||
612 | * that will occur, when a dump started and/or acquisition of | ||
613 | * exclusive lock failed. | ||
614 | */ | ||
615 | |||
616 | static void rtnetlink_rcv(struct sock *sk, int len) | ||
617 | { | ||
618 | do { | ||
619 | struct sk_buff *skb; | ||
620 | |||
621 | if (rtnl_shlock_nowait()) | ||
622 | return; | ||
623 | |||
624 | while ((skb = skb_dequeue(&sk->sk_receive_queue)) != NULL) { | ||
625 | if (rtnetlink_rcv_skb(skb)) { | ||
626 | if (skb->len) | ||
627 | skb_queue_head(&sk->sk_receive_queue, | ||
628 | skb); | ||
629 | else | ||
630 | kfree_skb(skb); | ||
631 | break; | ||
632 | } | ||
633 | kfree_skb(skb); | ||
634 | } | ||
635 | |||
636 | up(&rtnl_sem); | ||
637 | |||
638 | netdev_run_todo(); | ||
639 | } while (rtnl && rtnl->sk_receive_queue.qlen); | ||
640 | } | ||
641 | |||
642 | static struct rtnetlink_link link_rtnetlink_table[RTM_MAX-RTM_BASE+1] = | ||
643 | { | ||
644 | [RTM_GETLINK - RTM_BASE] = { .dumpit = rtnetlink_dump_ifinfo }, | ||
645 | [RTM_SETLINK - RTM_BASE] = { .doit = do_setlink }, | ||
646 | [RTM_GETADDR - RTM_BASE] = { .dumpit = rtnetlink_dump_all }, | ||
647 | [RTM_GETROUTE - RTM_BASE] = { .dumpit = rtnetlink_dump_all }, | ||
648 | [RTM_NEWNEIGH - RTM_BASE] = { .doit = neigh_add }, | ||
649 | [RTM_DELNEIGH - RTM_BASE] = { .doit = neigh_delete }, | ||
650 | [RTM_GETNEIGH - RTM_BASE] = { .dumpit = neigh_dump_info } | ||
651 | }; | ||
652 | |||
653 | static int rtnetlink_event(struct notifier_block *this, unsigned long event, void *ptr) | ||
654 | { | ||
655 | struct net_device *dev = ptr; | ||
656 | switch (event) { | ||
657 | case NETDEV_UNREGISTER: | ||
658 | rtmsg_ifinfo(RTM_DELLINK, dev, ~0U); | ||
659 | break; | ||
660 | case NETDEV_REGISTER: | ||
661 | rtmsg_ifinfo(RTM_NEWLINK, dev, ~0U); | ||
662 | break; | ||
663 | case NETDEV_UP: | ||
664 | case NETDEV_DOWN: | ||
665 | rtmsg_ifinfo(RTM_NEWLINK, dev, IFF_UP|IFF_RUNNING); | ||
666 | break; | ||
667 | case NETDEV_CHANGE: | ||
668 | case NETDEV_GOING_DOWN: | ||
669 | break; | ||
670 | default: | ||
671 | rtmsg_ifinfo(RTM_NEWLINK, dev, 0); | ||
672 | break; | ||
673 | } | ||
674 | return NOTIFY_DONE; | ||
675 | } | ||
676 | |||
677 | static struct notifier_block rtnetlink_dev_notifier = { | ||
678 | .notifier_call = rtnetlink_event, | ||
679 | }; | ||
680 | |||
681 | void __init rtnetlink_init(void) | ||
682 | { | ||
683 | int i; | ||
684 | |||
685 | rtattr_max = 0; | ||
686 | for (i = 0; i < ARRAY_SIZE(rta_max); i++) | ||
687 | if (rta_max[i] > rtattr_max) | ||
688 | rtattr_max = rta_max[i]; | ||
689 | rta_buf = kmalloc(rtattr_max * sizeof(struct rtattr *), GFP_KERNEL); | ||
690 | if (!rta_buf) | ||
691 | panic("rtnetlink_init: cannot allocate rta_buf\n"); | ||
692 | |||
693 | rtnl = netlink_kernel_create(NETLINK_ROUTE, rtnetlink_rcv); | ||
694 | if (rtnl == NULL) | ||
695 | panic("rtnetlink_init: cannot initialize rtnetlink\n"); | ||
696 | netlink_set_nonroot(NETLINK_ROUTE, NL_NONROOT_RECV); | ||
697 | register_netdevice_notifier(&rtnetlink_dev_notifier); | ||
698 | rtnetlink_links[PF_UNSPEC] = link_rtnetlink_table; | ||
699 | rtnetlink_links[PF_PACKET] = link_rtnetlink_table; | ||
700 | } | ||
701 | |||
702 | EXPORT_SYMBOL(__rta_fill); | ||
703 | EXPORT_SYMBOL(rtattr_strlcpy); | ||
704 | EXPORT_SYMBOL(rtattr_parse); | ||
705 | EXPORT_SYMBOL(rtnetlink_links); | ||
706 | EXPORT_SYMBOL(rtnetlink_put_metrics); | ||
707 | EXPORT_SYMBOL(rtnl); | ||
708 | EXPORT_SYMBOL(rtnl_lock); | ||
709 | EXPORT_SYMBOL(rtnl_lock_interruptible); | ||
710 | EXPORT_SYMBOL(rtnl_sem); | ||
711 | EXPORT_SYMBOL(rtnl_unlock); | ||