diff options
Diffstat (limited to 'net/ipv4/devinet.c')
-rw-r--r-- | net/ipv4/devinet.c | 108 |
1 files changed, 75 insertions, 33 deletions
diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index 9f3ffbec3296..6b297c8697e6 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c | |||
@@ -62,6 +62,7 @@ | |||
62 | #include <net/ip.h> | 62 | #include <net/ip.h> |
63 | #include <net/route.h> | 63 | #include <net/route.h> |
64 | #include <net/ip_fib.h> | 64 | #include <net/ip_fib.h> |
65 | #include <net/netlink.h> | ||
65 | 66 | ||
66 | struct ipv4_devconf ipv4_devconf = { | 67 | struct ipv4_devconf ipv4_devconf = { |
67 | .accept_redirects = 1, | 68 | .accept_redirects = 1, |
@@ -78,6 +79,14 @@ static struct ipv4_devconf ipv4_devconf_dflt = { | |||
78 | .accept_source_route = 1, | 79 | .accept_source_route = 1, |
79 | }; | 80 | }; |
80 | 81 | ||
82 | static struct nla_policy ifa_ipv4_policy[IFA_MAX+1] __read_mostly = { | ||
83 | [IFA_LOCAL] = { .type = NLA_U32 }, | ||
84 | [IFA_ADDRESS] = { .type = NLA_U32 }, | ||
85 | [IFA_BROADCAST] = { .type = NLA_U32 }, | ||
86 | [IFA_ANYCAST] = { .type = NLA_U32 }, | ||
87 | [IFA_LABEL] = { .type = NLA_STRING }, | ||
88 | }; | ||
89 | |||
81 | static void rtmsg_ifa(int event, struct in_ifaddr *); | 90 | static void rtmsg_ifa(int event, struct in_ifaddr *); |
82 | 91 | ||
83 | static BLOCKING_NOTIFIER_HEAD(inetaddr_chain); | 92 | static BLOCKING_NOTIFIER_HEAD(inetaddr_chain); |
@@ -451,57 +460,90 @@ out: | |||
451 | return -EADDRNOTAVAIL; | 460 | return -EADDRNOTAVAIL; |
452 | } | 461 | } |
453 | 462 | ||
454 | static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) | 463 | static struct in_ifaddr *rtm_to_ifaddr(struct nlmsghdr *nlh) |
455 | { | 464 | { |
456 | struct rtattr **rta = arg; | 465 | struct nlattr *tb[IFA_MAX+1]; |
466 | struct in_ifaddr *ifa; | ||
467 | struct ifaddrmsg *ifm; | ||
457 | struct net_device *dev; | 468 | struct net_device *dev; |
458 | struct in_device *in_dev; | 469 | struct in_device *in_dev; |
459 | struct ifaddrmsg *ifm = NLMSG_DATA(nlh); | 470 | int err = -EINVAL; |
460 | struct in_ifaddr *ifa; | ||
461 | int rc = -EINVAL; | ||
462 | 471 | ||
463 | ASSERT_RTNL(); | 472 | err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv4_policy); |
473 | if (err < 0) | ||
474 | goto errout; | ||
464 | 475 | ||
465 | if (ifm->ifa_prefixlen > 32 || !rta[IFA_LOCAL - 1]) | 476 | ifm = nlmsg_data(nlh); |
466 | goto out; | 477 | if (ifm->ifa_prefixlen > 32 || tb[IFA_LOCAL] == NULL) |
478 | goto errout; | ||
467 | 479 | ||
468 | rc = -ENODEV; | 480 | dev = __dev_get_by_index(ifm->ifa_index); |
469 | if ((dev = __dev_get_by_index(ifm->ifa_index)) == NULL) | 481 | if (dev == NULL) { |
470 | goto out; | 482 | err = -ENODEV; |
483 | goto errout; | ||
484 | } | ||
471 | 485 | ||
472 | rc = -ENOBUFS; | 486 | in_dev = __in_dev_get_rtnl(dev); |
473 | if ((in_dev = __in_dev_get_rtnl(dev)) == NULL) { | 487 | if (in_dev == NULL) { |
474 | in_dev = inetdev_init(dev); | 488 | in_dev = inetdev_init(dev); |
475 | if (!in_dev) | 489 | if (in_dev == NULL) { |
476 | goto out; | 490 | err = -ENOBUFS; |
491 | goto errout; | ||
492 | } | ||
477 | } | 493 | } |
478 | 494 | ||
479 | if ((ifa = inet_alloc_ifa()) == NULL) | 495 | ifa = inet_alloc_ifa(); |
480 | goto out; | 496 | if (ifa == NULL) { |
497 | /* | ||
498 | * A potential indev allocation can be left alive, it stays | ||
499 | * assigned to its device and is destroy with it. | ||
500 | */ | ||
501 | err = -ENOBUFS; | ||
502 | goto errout; | ||
503 | } | ||
504 | |||
505 | in_dev_hold(in_dev); | ||
506 | |||
507 | if (tb[IFA_ADDRESS] == NULL) | ||
508 | tb[IFA_ADDRESS] = tb[IFA_LOCAL]; | ||
481 | 509 | ||
482 | if (!rta[IFA_ADDRESS - 1]) | ||
483 | rta[IFA_ADDRESS - 1] = rta[IFA_LOCAL - 1]; | ||
484 | memcpy(&ifa->ifa_local, RTA_DATA(rta[IFA_LOCAL - 1]), 4); | ||
485 | memcpy(&ifa->ifa_address, RTA_DATA(rta[IFA_ADDRESS - 1]), 4); | ||
486 | ifa->ifa_prefixlen = ifm->ifa_prefixlen; | 510 | ifa->ifa_prefixlen = ifm->ifa_prefixlen; |
487 | ifa->ifa_mask = inet_make_mask(ifm->ifa_prefixlen); | 511 | ifa->ifa_mask = inet_make_mask(ifm->ifa_prefixlen); |
488 | if (rta[IFA_BROADCAST - 1]) | ||
489 | memcpy(&ifa->ifa_broadcast, | ||
490 | RTA_DATA(rta[IFA_BROADCAST - 1]), 4); | ||
491 | if (rta[IFA_ANYCAST - 1]) | ||
492 | memcpy(&ifa->ifa_anycast, RTA_DATA(rta[IFA_ANYCAST - 1]), 4); | ||
493 | ifa->ifa_flags = ifm->ifa_flags; | 512 | ifa->ifa_flags = ifm->ifa_flags; |
494 | ifa->ifa_scope = ifm->ifa_scope; | 513 | ifa->ifa_scope = ifm->ifa_scope; |
495 | in_dev_hold(in_dev); | 514 | ifa->ifa_dev = in_dev; |
496 | ifa->ifa_dev = in_dev; | 515 | |
497 | if (rta[IFA_LABEL - 1]) | 516 | ifa->ifa_local = nla_get_u32(tb[IFA_LOCAL]); |
498 | rtattr_strlcpy(ifa->ifa_label, rta[IFA_LABEL - 1], IFNAMSIZ); | 517 | ifa->ifa_address = nla_get_u32(tb[IFA_ADDRESS]); |
518 | |||
519 | if (tb[IFA_BROADCAST]) | ||
520 | ifa->ifa_broadcast = nla_get_u32(tb[IFA_BROADCAST]); | ||
521 | |||
522 | if (tb[IFA_ANYCAST]) | ||
523 | ifa->ifa_anycast = nla_get_u32(tb[IFA_ANYCAST]); | ||
524 | |||
525 | if (tb[IFA_LABEL]) | ||
526 | nla_strlcpy(ifa->ifa_label, tb[IFA_LABEL], IFNAMSIZ); | ||
499 | else | 527 | else |
500 | memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); | 528 | memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); |
501 | 529 | ||
502 | rc = inet_insert_ifa(ifa); | 530 | return ifa; |
503 | out: | 531 | |
504 | return rc; | 532 | errout: |
533 | return ERR_PTR(err); | ||
534 | } | ||
535 | |||
536 | static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) | ||
537 | { | ||
538 | struct in_ifaddr *ifa; | ||
539 | |||
540 | ASSERT_RTNL(); | ||
541 | |||
542 | ifa = rtm_to_ifaddr(nlh); | ||
543 | if (IS_ERR(ifa)) | ||
544 | return PTR_ERR(ifa); | ||
545 | |||
546 | return inet_insert_ifa(ifa); | ||
505 | } | 547 | } |
506 | 548 | ||
507 | /* | 549 | /* |