diff options
Diffstat (limited to 'net/core/rtnetlink.c')
-rw-r--r-- | net/core/rtnetlink.c | 191 |
1 files changed, 100 insertions, 91 deletions
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 35712031e2c3..2adc966d981e 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c | |||
@@ -336,52 +336,69 @@ static int rtnetlink_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *c | |||
336 | return skb->len; | 336 | return skb->len; |
337 | } | 337 | } |
338 | 338 | ||
339 | static int do_setlink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) | 339 | static struct nla_policy ifla_policy[IFLA_MAX+1] __read_mostly = { |
340 | [IFLA_IFNAME] = { .type = NLA_STRING }, | ||
341 | [IFLA_MAP] = { .minlen = sizeof(struct rtnl_link_ifmap) }, | ||
342 | [IFLA_MTU] = { .type = NLA_U32 }, | ||
343 | [IFLA_TXQLEN] = { .type = NLA_U32 }, | ||
344 | [IFLA_WEIGHT] = { .type = NLA_U32 }, | ||
345 | [IFLA_OPERSTATE] = { .type = NLA_U8 }, | ||
346 | [IFLA_LINKMODE] = { .type = NLA_U8 }, | ||
347 | }; | ||
348 | |||
349 | static int rtnl_setlink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) | ||
340 | { | 350 | { |
341 | struct ifinfomsg *ifm = NLMSG_DATA(nlh); | 351 | struct ifinfomsg *ifm; |
342 | struct rtattr **ida = arg; | ||
343 | struct net_device *dev; | 352 | struct net_device *dev; |
344 | int err, send_addr_notify = 0; | 353 | int err, send_addr_notify = 0, modified = 0; |
354 | struct nlattr *tb[IFLA_MAX+1]; | ||
355 | char ifname[IFNAMSIZ]; | ||
345 | 356 | ||
357 | err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFLA_MAX, ifla_policy); | ||
358 | if (err < 0) | ||
359 | goto errout; | ||
360 | |||
361 | if (tb[IFLA_IFNAME] && | ||
362 | nla_strlcpy(ifname, tb[IFLA_IFNAME], IFNAMSIZ) >= IFNAMSIZ) | ||
363 | return -EINVAL; | ||
364 | |||
365 | err = -EINVAL; | ||
366 | ifm = nlmsg_data(nlh); | ||
346 | if (ifm->ifi_index >= 0) | 367 | if (ifm->ifi_index >= 0) |
347 | dev = dev_get_by_index(ifm->ifi_index); | 368 | dev = dev_get_by_index(ifm->ifi_index); |
348 | else if (ida[IFLA_IFNAME - 1]) { | 369 | else if (tb[IFLA_IFNAME]) |
349 | char ifname[IFNAMSIZ]; | ||
350 | |||
351 | if (rtattr_strlcpy(ifname, ida[IFLA_IFNAME - 1], | ||
352 | IFNAMSIZ) >= IFNAMSIZ) | ||
353 | return -EINVAL; | ||
354 | dev = dev_get_by_name(ifname); | 370 | dev = dev_get_by_name(ifname); |
355 | } else | 371 | else |
356 | return -EINVAL; | 372 | goto errout; |
357 | 373 | ||
358 | if (!dev) | 374 | if (dev == NULL) { |
359 | return -ENODEV; | 375 | err = -ENODEV; |
376 | goto errout; | ||
377 | } | ||
360 | 378 | ||
361 | err = -EINVAL; | 379 | if (tb[IFLA_ADDRESS] && |
380 | nla_len(tb[IFLA_ADDRESS]) < dev->addr_len) | ||
381 | goto errout_dev; | ||
362 | 382 | ||
363 | if (ifm->ifi_flags) | 383 | if (tb[IFLA_BROADCAST] && |
364 | dev_change_flags(dev, ifm->ifi_flags); | 384 | nla_len(tb[IFLA_BROADCAST]) < dev->addr_len) |
385 | goto errout_dev; | ||
365 | 386 | ||
366 | if (ida[IFLA_MAP - 1]) { | 387 | if (tb[IFLA_MAP]) { |
367 | struct rtnl_link_ifmap *u_map; | 388 | struct rtnl_link_ifmap *u_map; |
368 | struct ifmap k_map; | 389 | struct ifmap k_map; |
369 | 390 | ||
370 | if (!dev->set_config) { | 391 | if (!dev->set_config) { |
371 | err = -EOPNOTSUPP; | 392 | err = -EOPNOTSUPP; |
372 | goto out; | 393 | goto errout_dev; |
373 | } | 394 | } |
374 | 395 | ||
375 | if (!netif_device_present(dev)) { | 396 | if (!netif_device_present(dev)) { |
376 | err = -ENODEV; | 397 | err = -ENODEV; |
377 | goto out; | 398 | goto errout_dev; |
378 | } | 399 | } |
379 | |||
380 | if (ida[IFLA_MAP - 1]->rta_len != RTA_LENGTH(sizeof(*u_map))) | ||
381 | goto out; | ||
382 | |||
383 | u_map = RTA_DATA(ida[IFLA_MAP - 1]); | ||
384 | 400 | ||
401 | u_map = nla_data(tb[IFLA_MAP]); | ||
385 | k_map.mem_start = (unsigned long) u_map->mem_start; | 402 | k_map.mem_start = (unsigned long) u_map->mem_start; |
386 | k_map.mem_end = (unsigned long) u_map->mem_end; | 403 | k_map.mem_end = (unsigned long) u_map->mem_end; |
387 | k_map.base_addr = (unsigned short) u_map->base_addr; | 404 | k_map.base_addr = (unsigned short) u_map->base_addr; |
@@ -390,119 +407,111 @@ static int do_setlink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) | |||
390 | k_map.port = (unsigned char) u_map->port; | 407 | k_map.port = (unsigned char) u_map->port; |
391 | 408 | ||
392 | err = dev->set_config(dev, &k_map); | 409 | err = dev->set_config(dev, &k_map); |
410 | if (err < 0) | ||
411 | goto errout_dev; | ||
393 | 412 | ||
394 | if (err) | 413 | modified = 1; |
395 | goto out; | ||
396 | } | 414 | } |
397 | 415 | ||
398 | if (ida[IFLA_ADDRESS - 1]) { | 416 | if (tb[IFLA_ADDRESS]) { |
399 | struct sockaddr *sa; | 417 | struct sockaddr *sa; |
400 | int len; | 418 | int len; |
401 | 419 | ||
402 | if (!dev->set_mac_address) { | 420 | if (!dev->set_mac_address) { |
403 | err = -EOPNOTSUPP; | 421 | err = -EOPNOTSUPP; |
404 | goto out; | 422 | goto errout_dev; |
405 | } | 423 | } |
424 | |||
406 | if (!netif_device_present(dev)) { | 425 | if (!netif_device_present(dev)) { |
407 | err = -ENODEV; | 426 | err = -ENODEV; |
408 | goto out; | 427 | goto errout_dev; |
409 | } | 428 | } |
410 | if (ida[IFLA_ADDRESS - 1]->rta_len != RTA_LENGTH(dev->addr_len)) | ||
411 | goto out; | ||
412 | 429 | ||
413 | len = sizeof(sa_family_t) + dev->addr_len; | 430 | len = sizeof(sa_family_t) + dev->addr_len; |
414 | sa = kmalloc(len, GFP_KERNEL); | 431 | sa = kmalloc(len, GFP_KERNEL); |
415 | if (!sa) { | 432 | if (!sa) { |
416 | err = -ENOMEM; | 433 | err = -ENOMEM; |
417 | goto out; | 434 | goto errout_dev; |
418 | } | 435 | } |
419 | sa->sa_family = dev->type; | 436 | sa->sa_family = dev->type; |
420 | memcpy(sa->sa_data, RTA_DATA(ida[IFLA_ADDRESS - 1]), | 437 | memcpy(sa->sa_data, nla_data(tb[IFLA_ADDRESS]), |
421 | dev->addr_len); | 438 | dev->addr_len); |
422 | err = dev->set_mac_address(dev, sa); | 439 | err = dev->set_mac_address(dev, sa); |
423 | kfree(sa); | 440 | kfree(sa); |
424 | if (err) | 441 | if (err) |
425 | goto out; | 442 | goto errout_dev; |
426 | send_addr_notify = 1; | 443 | send_addr_notify = 1; |
444 | modified = 1; | ||
427 | } | 445 | } |
428 | 446 | ||
429 | if (ida[IFLA_BROADCAST - 1]) { | 447 | if (tb[IFLA_MTU]) { |
430 | if (ida[IFLA_BROADCAST - 1]->rta_len != RTA_LENGTH(dev->addr_len)) | 448 | err = dev_set_mtu(dev, nla_get_u32(tb[IFLA_MTU])); |
431 | goto out; | 449 | if (err < 0) |
432 | memcpy(dev->broadcast, RTA_DATA(ida[IFLA_BROADCAST - 1]), | 450 | goto errout_dev; |
433 | dev->addr_len); | 451 | modified = 1; |
434 | send_addr_notify = 1; | ||
435 | } | 452 | } |
436 | 453 | ||
437 | if (ida[IFLA_MTU - 1]) { | 454 | /* |
438 | if (ida[IFLA_MTU - 1]->rta_len != RTA_LENGTH(sizeof(u32))) | 455 | * Interface selected by interface index but interface |
439 | goto out; | 456 | * name provided implies that a name change has been |
440 | err = dev_set_mtu(dev, *((u32 *) RTA_DATA(ida[IFLA_MTU - 1]))); | 457 | * requested. |
441 | 458 | */ | |
442 | if (err) | 459 | if (ifm->ifi_index >= 0 && ifname[0]) { |
443 | goto out; | 460 | err = dev_change_name(dev, ifname); |
444 | 461 | if (err < 0) | |
462 | goto errout_dev; | ||
463 | modified = 1; | ||
445 | } | 464 | } |
446 | 465 | ||
447 | if (ida[IFLA_TXQLEN - 1]) { | 466 | #ifdef CONFIG_NET_WIRELESS_RTNETLINK |
448 | if (ida[IFLA_TXQLEN - 1]->rta_len != RTA_LENGTH(sizeof(u32))) | 467 | if (tb[IFLA_WIRELESS]) { |
449 | goto out; | 468 | /* Call Wireless Extensions. |
469 | * Various stuff checked in there... */ | ||
470 | err = wireless_rtnetlink_set(dev, nla_data(tb[IFLA_WIRELESS]), | ||
471 | nla_len(tb[IFLA_WIRELESS])); | ||
472 | if (err < 0) | ||
473 | goto errout_dev; | ||
474 | } | ||
475 | #endif /* CONFIG_NET_WIRELESS_RTNETLINK */ | ||
450 | 476 | ||
451 | dev->tx_queue_len = *((u32 *) RTA_DATA(ida[IFLA_TXQLEN - 1])); | 477 | if (tb[IFLA_BROADCAST]) { |
478 | nla_memcpy(dev->broadcast, tb[IFLA_BROADCAST], dev->addr_len); | ||
479 | send_addr_notify = 1; | ||
452 | } | 480 | } |
453 | 481 | ||
454 | if (ida[IFLA_WEIGHT - 1]) { | ||
455 | if (ida[IFLA_WEIGHT - 1]->rta_len != RTA_LENGTH(sizeof(u32))) | ||
456 | goto out; | ||
457 | 482 | ||
458 | dev->weight = *((u32 *) RTA_DATA(ida[IFLA_WEIGHT - 1])); | 483 | if (ifm->ifi_flags) |
459 | } | 484 | dev_change_flags(dev, ifm->ifi_flags); |
460 | 485 | ||
461 | if (ida[IFLA_OPERSTATE - 1]) { | 486 | if (tb[IFLA_TXQLEN]) |
462 | if (ida[IFLA_OPERSTATE - 1]->rta_len != RTA_LENGTH(sizeof(u8))) | 487 | dev->tx_queue_len = nla_get_u32(tb[IFLA_TXQLEN]); |
463 | goto out; | ||
464 | 488 | ||
465 | set_operstate(dev, *((u8 *) RTA_DATA(ida[IFLA_OPERSTATE - 1]))); | 489 | if (tb[IFLA_WEIGHT]) |
466 | } | 490 | dev->weight = nla_get_u32(tb[IFLA_WEIGHT]); |
467 | 491 | ||
468 | if (ida[IFLA_LINKMODE - 1]) { | 492 | if (tb[IFLA_OPERSTATE]) |
469 | if (ida[IFLA_LINKMODE - 1]->rta_len != RTA_LENGTH(sizeof(u8))) | 493 | set_operstate(dev, nla_get_u8(tb[IFLA_OPERSTATE])); |
470 | goto out; | ||
471 | 494 | ||
495 | if (tb[IFLA_LINKMODE]) { | ||
472 | write_lock_bh(&dev_base_lock); | 496 | write_lock_bh(&dev_base_lock); |
473 | dev->link_mode = *((u8 *) RTA_DATA(ida[IFLA_LINKMODE - 1])); | 497 | dev->link_mode = nla_get_u8(tb[IFLA_LINKMODE]); |
474 | write_unlock_bh(&dev_base_lock); | 498 | write_unlock_bh(&dev_base_lock); |
475 | } | 499 | } |
476 | 500 | ||
477 | if (ifm->ifi_index >= 0 && ida[IFLA_IFNAME - 1]) { | ||
478 | char ifname[IFNAMSIZ]; | ||
479 | |||
480 | if (rtattr_strlcpy(ifname, ida[IFLA_IFNAME - 1], | ||
481 | IFNAMSIZ) >= IFNAMSIZ) | ||
482 | goto out; | ||
483 | err = dev_change_name(dev, ifname); | ||
484 | if (err) | ||
485 | goto out; | ||
486 | } | ||
487 | |||
488 | #ifdef CONFIG_NET_WIRELESS_RTNETLINK | ||
489 | if (ida[IFLA_WIRELESS - 1]) { | ||
490 | |||
491 | /* Call Wireless Extensions. | ||
492 | * Various stuff checked in there... */ | ||
493 | err = wireless_rtnetlink_set(dev, RTA_DATA(ida[IFLA_WIRELESS - 1]), ida[IFLA_WIRELESS - 1]->rta_len); | ||
494 | if (err) | ||
495 | goto out; | ||
496 | } | ||
497 | #endif /* CONFIG_NET_WIRELESS_RTNETLINK */ | ||
498 | |||
499 | err = 0; | 501 | err = 0; |
500 | 502 | ||
501 | out: | 503 | errout_dev: |
504 | if (err < 0 && modified && net_ratelimit()) | ||
505 | printk(KERN_WARNING "A link change request failed with " | ||
506 | "some changes comitted already. Interface %s may " | ||
507 | "have been left with an inconsistent configuration, " | ||
508 | "please check.\n", dev->name); | ||
509 | |||
502 | if (send_addr_notify) | 510 | if (send_addr_notify) |
503 | call_netdevice_notifiers(NETDEV_CHANGEADDR, dev); | 511 | call_netdevice_notifiers(NETDEV_CHANGEADDR, dev); |
504 | 512 | ||
505 | dev_put(dev); | 513 | dev_put(dev); |
514 | errout: | ||
506 | return err; | 515 | return err; |
507 | } | 516 | } |
508 | 517 | ||
@@ -753,7 +762,7 @@ static struct rtnetlink_link link_rtnetlink_table[RTM_NR_MSGTYPES] = | |||
753 | .doit = do_getlink, | 762 | .doit = do_getlink, |
754 | #endif /* CONFIG_NET_WIRELESS_RTNETLINK */ | 763 | #endif /* CONFIG_NET_WIRELESS_RTNETLINK */ |
755 | .dumpit = rtnetlink_dump_ifinfo }, | 764 | .dumpit = rtnetlink_dump_ifinfo }, |
756 | [RTM_SETLINK - RTM_BASE] = { .doit = do_setlink }, | 765 | [RTM_SETLINK - RTM_BASE] = { .doit = rtnl_setlink }, |
757 | [RTM_GETADDR - RTM_BASE] = { .dumpit = rtnetlink_dump_all }, | 766 | [RTM_GETADDR - RTM_BASE] = { .dumpit = rtnetlink_dump_all }, |
758 | [RTM_GETROUTE - RTM_BASE] = { .dumpit = rtnetlink_dump_all }, | 767 | [RTM_GETROUTE - RTM_BASE] = { .dumpit = rtnetlink_dump_all }, |
759 | [RTM_NEWNEIGH - RTM_BASE] = { .doit = neigh_add }, | 768 | [RTM_NEWNEIGH - RTM_BASE] = { .doit = neigh_add }, |