diff options
Diffstat (limited to 'net/ipv4/devinet.c')
-rw-r--r-- | net/ipv4/devinet.c | 66 |
1 files changed, 48 insertions, 18 deletions
diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index f678507bc829..c6287cd978c2 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c | |||
@@ -587,13 +587,16 @@ static void check_lifetime(struct work_struct *work) | |||
587 | { | 587 | { |
588 | unsigned long now, next, next_sec, next_sched; | 588 | unsigned long now, next, next_sec, next_sched; |
589 | struct in_ifaddr *ifa; | 589 | struct in_ifaddr *ifa; |
590 | struct hlist_node *n; | ||
590 | int i; | 591 | int i; |
591 | 592 | ||
592 | now = jiffies; | 593 | now = jiffies; |
593 | next = round_jiffies_up(now + ADDR_CHECK_FREQUENCY); | 594 | next = round_jiffies_up(now + ADDR_CHECK_FREQUENCY); |
594 | 595 | ||
595 | rcu_read_lock(); | ||
596 | for (i = 0; i < IN4_ADDR_HSIZE; i++) { | 596 | for (i = 0; i < IN4_ADDR_HSIZE; i++) { |
597 | bool change_needed = false; | ||
598 | |||
599 | rcu_read_lock(); | ||
597 | hlist_for_each_entry_rcu(ifa, &inet_addr_lst[i], hash) { | 600 | hlist_for_each_entry_rcu(ifa, &inet_addr_lst[i], hash) { |
598 | unsigned long age; | 601 | unsigned long age; |
599 | 602 | ||
@@ -606,16 +609,7 @@ static void check_lifetime(struct work_struct *work) | |||
606 | 609 | ||
607 | if (ifa->ifa_valid_lft != INFINITY_LIFE_TIME && | 610 | if (ifa->ifa_valid_lft != INFINITY_LIFE_TIME && |
608 | age >= ifa->ifa_valid_lft) { | 611 | age >= ifa->ifa_valid_lft) { |
609 | struct in_ifaddr **ifap ; | 612 | change_needed = true; |
610 | |||
611 | rtnl_lock(); | ||
612 | for (ifap = &ifa->ifa_dev->ifa_list; | ||
613 | *ifap != NULL; ifap = &ifa->ifa_next) { | ||
614 | if (*ifap == ifa) | ||
615 | inet_del_ifa(ifa->ifa_dev, | ||
616 | ifap, 1); | ||
617 | } | ||
618 | rtnl_unlock(); | ||
619 | } else if (ifa->ifa_preferred_lft == | 613 | } else if (ifa->ifa_preferred_lft == |
620 | INFINITY_LIFE_TIME) { | 614 | INFINITY_LIFE_TIME) { |
621 | continue; | 615 | continue; |
@@ -625,10 +619,8 @@ static void check_lifetime(struct work_struct *work) | |||
625 | next = ifa->ifa_tstamp + | 619 | next = ifa->ifa_tstamp + |
626 | ifa->ifa_valid_lft * HZ; | 620 | ifa->ifa_valid_lft * HZ; |
627 | 621 | ||
628 | if (!(ifa->ifa_flags & IFA_F_DEPRECATED)) { | 622 | if (!(ifa->ifa_flags & IFA_F_DEPRECATED)) |
629 | ifa->ifa_flags |= IFA_F_DEPRECATED; | 623 | change_needed = true; |
630 | rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0); | ||
631 | } | ||
632 | } else if (time_before(ifa->ifa_tstamp + | 624 | } else if (time_before(ifa->ifa_tstamp + |
633 | ifa->ifa_preferred_lft * HZ, | 625 | ifa->ifa_preferred_lft * HZ, |
634 | next)) { | 626 | next)) { |
@@ -636,8 +628,42 @@ static void check_lifetime(struct work_struct *work) | |||
636 | ifa->ifa_preferred_lft * HZ; | 628 | ifa->ifa_preferred_lft * HZ; |
637 | } | 629 | } |
638 | } | 630 | } |
631 | rcu_read_unlock(); | ||
632 | if (!change_needed) | ||
633 | continue; | ||
634 | rtnl_lock(); | ||
635 | hlist_for_each_entry_safe(ifa, n, &inet_addr_lst[i], hash) { | ||
636 | unsigned long age; | ||
637 | |||
638 | if (ifa->ifa_flags & IFA_F_PERMANENT) | ||
639 | continue; | ||
640 | |||
641 | /* We try to batch several events at once. */ | ||
642 | age = (now - ifa->ifa_tstamp + | ||
643 | ADDRCONF_TIMER_FUZZ_MINUS) / HZ; | ||
644 | |||
645 | if (ifa->ifa_valid_lft != INFINITY_LIFE_TIME && | ||
646 | age >= ifa->ifa_valid_lft) { | ||
647 | struct in_ifaddr **ifap; | ||
648 | |||
649 | for (ifap = &ifa->ifa_dev->ifa_list; | ||
650 | *ifap != NULL; ifap = &(*ifap)->ifa_next) { | ||
651 | if (*ifap == ifa) { | ||
652 | inet_del_ifa(ifa->ifa_dev, | ||
653 | ifap, 1); | ||
654 | break; | ||
655 | } | ||
656 | } | ||
657 | } else if (ifa->ifa_preferred_lft != | ||
658 | INFINITY_LIFE_TIME && | ||
659 | age >= ifa->ifa_preferred_lft && | ||
660 | !(ifa->ifa_flags & IFA_F_DEPRECATED)) { | ||
661 | ifa->ifa_flags |= IFA_F_DEPRECATED; | ||
662 | rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0); | ||
663 | } | ||
664 | } | ||
665 | rtnl_unlock(); | ||
639 | } | 666 | } |
640 | rcu_read_unlock(); | ||
641 | 667 | ||
642 | next_sec = round_jiffies_up(next); | 668 | next_sec = round_jiffies_up(next); |
643 | next_sched = next; | 669 | next_sched = next; |
@@ -802,8 +828,12 @@ static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg | |||
802 | if (nlh->nlmsg_flags & NLM_F_EXCL || | 828 | if (nlh->nlmsg_flags & NLM_F_EXCL || |
803 | !(nlh->nlmsg_flags & NLM_F_REPLACE)) | 829 | !(nlh->nlmsg_flags & NLM_F_REPLACE)) |
804 | return -EEXIST; | 830 | return -EEXIST; |
805 | 831 | ifa = ifa_existing; | |
806 | set_ifa_lifetime(ifa_existing, valid_lft, prefered_lft); | 832 | set_ifa_lifetime(ifa, valid_lft, prefered_lft); |
833 | cancel_delayed_work(&check_lifetime_work); | ||
834 | schedule_delayed_work(&check_lifetime_work, 0); | ||
835 | rtmsg_ifa(RTM_NEWADDR, ifa, nlh, NETLINK_CB(skb).portid); | ||
836 | blocking_notifier_call_chain(&inetaddr_chain, NETDEV_UP, ifa); | ||
807 | } | 837 | } |
808 | return 0; | 838 | return 0; |
809 | } | 839 | } |