diff options
author | Wang Chen <wangchen@cn.fujitsu.com> | 2008-06-18 04:48:28 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2008-06-18 04:48:28 -0400 |
commit | dad9b335c6940de2746a9788eb456d09cf102f81 (patch) | |
tree | c74092de70d7c2c9ba88bf580bc404133b55c490 | |
parent | dd574dbfcc9e74e7dd8fd59ae0075d23e71a3da1 (diff) |
netdevice: Fix promiscuity and allmulti overflow
Max of promiscuity and allmulti plus positive @inc can cause overflow.
Fox example: when allmulti=0xFFFFFFFF, any caller give dev_set_allmulti() a
positive @inc will cause allmulti be off.
This is not what we want, though it's rare case.
The fix is that only negative @inc will cause allmulti or promiscuity be off
and when any caller makes the counters touch the roof, we return error.
Change of v2:
Change void function dev_set_promiscuity/allmulti to return int.
So callers can get the overflow error.
Caller's fix will be done later.
Change of v3:
1. Since we return error to caller, we don't need to print KERN_ERROR,
KERN_WARNING is enough.
2. In dev_set_promiscuity(), if __dev_set_promiscuity() failed, we
return at once.
Signed-off-by: Wang Chen <wangchen@cn.fujitsu.com>
Acked-by: Patrick McHardy <kaber@trash.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | include/linux/netdevice.h | 4 | ||||
-rw-r--r-- | net/core/dev.c | 55 |
2 files changed, 47 insertions, 12 deletions
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 4bf613cd9e2d..45dce2b58d4c 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h | |||
@@ -1476,8 +1476,8 @@ extern int __dev_addr_delete(struct dev_addr_list **list, int *count, void *ad | |||
1476 | extern int __dev_addr_add(struct dev_addr_list **list, int *count, void *addr, int alen, int newonly); | 1476 | extern int __dev_addr_add(struct dev_addr_list **list, int *count, void *addr, int alen, int newonly); |
1477 | extern int __dev_addr_sync(struct dev_addr_list **to, int *to_count, struct dev_addr_list **from, int *from_count); | 1477 | extern int __dev_addr_sync(struct dev_addr_list **to, int *to_count, struct dev_addr_list **from, int *from_count); |
1478 | extern void __dev_addr_unsync(struct dev_addr_list **to, int *to_count, struct dev_addr_list **from, int *from_count); | 1478 | extern void __dev_addr_unsync(struct dev_addr_list **to, int *to_count, struct dev_addr_list **from, int *from_count); |
1479 | extern void dev_set_promiscuity(struct net_device *dev, int inc); | 1479 | extern int dev_set_promiscuity(struct net_device *dev, int inc); |
1480 | extern void dev_set_allmulti(struct net_device *dev, int inc); | 1480 | extern int dev_set_allmulti(struct net_device *dev, int inc); |
1481 | extern void netdev_state_change(struct net_device *dev); | 1481 | extern void netdev_state_change(struct net_device *dev); |
1482 | extern void netdev_bonding_change(struct net_device *dev); | 1482 | extern void netdev_bonding_change(struct net_device *dev); |
1483 | extern void netdev_features_change(struct net_device *dev); | 1483 | extern void netdev_features_change(struct net_device *dev); |
diff --git a/net/core/dev.c b/net/core/dev.c index 0e45742e7158..a495f712d38c 100644 --- a/net/core/dev.c +++ b/net/core/dev.c | |||
@@ -2771,16 +2771,29 @@ int netdev_set_master(struct net_device *slave, struct net_device *master) | |||
2771 | return 0; | 2771 | return 0; |
2772 | } | 2772 | } |
2773 | 2773 | ||
2774 | static void __dev_set_promiscuity(struct net_device *dev, int inc) | 2774 | static int __dev_set_promiscuity(struct net_device *dev, int inc) |
2775 | { | 2775 | { |
2776 | unsigned short old_flags = dev->flags; | 2776 | unsigned short old_flags = dev->flags; |
2777 | 2777 | ||
2778 | ASSERT_RTNL(); | 2778 | ASSERT_RTNL(); |
2779 | 2779 | ||
2780 | if ((dev->promiscuity += inc) == 0) | 2780 | dev->flags |= IFF_PROMISC; |
2781 | dev->flags &= ~IFF_PROMISC; | 2781 | dev->promiscuity += inc; |
2782 | else | 2782 | if (dev->promiscuity == 0) { |
2783 | dev->flags |= IFF_PROMISC; | 2783 | /* |
2784 | * Avoid overflow. | ||
2785 | * If inc causes overflow, untouch promisc and return error. | ||
2786 | */ | ||
2787 | if (inc < 0) | ||
2788 | dev->flags &= ~IFF_PROMISC; | ||
2789 | else { | ||
2790 | dev->promiscuity -= inc; | ||
2791 | printk(KERN_WARNING "%s: promiscuity touches roof, " | ||
2792 | "set promiscuity failed, promiscuity feature " | ||
2793 | "of device might be broken.\n", dev->name); | ||
2794 | return -EOVERFLOW; | ||
2795 | } | ||
2796 | } | ||
2784 | if (dev->flags != old_flags) { | 2797 | if (dev->flags != old_flags) { |
2785 | printk(KERN_INFO "device %s %s promiscuous mode\n", | 2798 | printk(KERN_INFO "device %s %s promiscuous mode\n", |
2786 | dev->name, (dev->flags & IFF_PROMISC) ? "entered" : | 2799 | dev->name, (dev->flags & IFF_PROMISC) ? "entered" : |
@@ -2798,6 +2811,7 @@ static void __dev_set_promiscuity(struct net_device *dev, int inc) | |||
2798 | if (dev->change_rx_flags) | 2811 | if (dev->change_rx_flags) |
2799 | dev->change_rx_flags(dev, IFF_PROMISC); | 2812 | dev->change_rx_flags(dev, IFF_PROMISC); |
2800 | } | 2813 | } |
2814 | return 0; | ||
2801 | } | 2815 | } |
2802 | 2816 | ||
2803 | /** | 2817 | /** |
@@ -2809,14 +2823,19 @@ static void __dev_set_promiscuity(struct net_device *dev, int inc) | |||
2809 | * remains above zero the interface remains promiscuous. Once it hits zero | 2823 | * remains above zero the interface remains promiscuous. Once it hits zero |
2810 | * the device reverts back to normal filtering operation. A negative inc | 2824 | * the device reverts back to normal filtering operation. A negative inc |
2811 | * value is used to drop promiscuity on the device. | 2825 | * value is used to drop promiscuity on the device. |
2826 | * Return 0 if successful or a negative errno code on error. | ||
2812 | */ | 2827 | */ |
2813 | void dev_set_promiscuity(struct net_device *dev, int inc) | 2828 | int dev_set_promiscuity(struct net_device *dev, int inc) |
2814 | { | 2829 | { |
2815 | unsigned short old_flags = dev->flags; | 2830 | unsigned short old_flags = dev->flags; |
2831 | int err; | ||
2816 | 2832 | ||
2817 | __dev_set_promiscuity(dev, inc); | 2833 | err = __dev_set_promiscuity(dev, inc); |
2834 | if (!err) | ||
2835 | return err; | ||
2818 | if (dev->flags != old_flags) | 2836 | if (dev->flags != old_flags) |
2819 | dev_set_rx_mode(dev); | 2837 | dev_set_rx_mode(dev); |
2838 | return err; | ||
2820 | } | 2839 | } |
2821 | 2840 | ||
2822 | /** | 2841 | /** |
@@ -2829,22 +2848,38 @@ void dev_set_promiscuity(struct net_device *dev, int inc) | |||
2829 | * to all interfaces. Once it hits zero the device reverts back to normal | 2848 | * to all interfaces. Once it hits zero the device reverts back to normal |
2830 | * filtering operation. A negative @inc value is used to drop the counter | 2849 | * filtering operation. A negative @inc value is used to drop the counter |
2831 | * when releasing a resource needing all multicasts. | 2850 | * when releasing a resource needing all multicasts. |
2851 | * Return 0 if successful or a negative errno code on error. | ||
2832 | */ | 2852 | */ |
2833 | 2853 | ||
2834 | void dev_set_allmulti(struct net_device *dev, int inc) | 2854 | int dev_set_allmulti(struct net_device *dev, int inc) |
2835 | { | 2855 | { |
2836 | unsigned short old_flags = dev->flags; | 2856 | unsigned short old_flags = dev->flags; |
2837 | 2857 | ||
2838 | ASSERT_RTNL(); | 2858 | ASSERT_RTNL(); |
2839 | 2859 | ||
2840 | dev->flags |= IFF_ALLMULTI; | 2860 | dev->flags |= IFF_ALLMULTI; |
2841 | if ((dev->allmulti += inc) == 0) | 2861 | dev->allmulti += inc; |
2842 | dev->flags &= ~IFF_ALLMULTI; | 2862 | if (dev->allmulti == 0) { |
2863 | /* | ||
2864 | * Avoid overflow. | ||
2865 | * If inc causes overflow, untouch allmulti and return error. | ||
2866 | */ | ||
2867 | if (inc < 0) | ||
2868 | dev->flags &= ~IFF_ALLMULTI; | ||
2869 | else { | ||
2870 | dev->allmulti -= inc; | ||
2871 | printk(KERN_WARNING "%s: allmulti touches roof, " | ||
2872 | "set allmulti failed, allmulti feature of " | ||
2873 | "device might be broken.\n", dev->name); | ||
2874 | return -EOVERFLOW; | ||
2875 | } | ||
2876 | } | ||
2843 | if (dev->flags ^ old_flags) { | 2877 | if (dev->flags ^ old_flags) { |
2844 | if (dev->change_rx_flags) | 2878 | if (dev->change_rx_flags) |
2845 | dev->change_rx_flags(dev, IFF_ALLMULTI); | 2879 | dev->change_rx_flags(dev, IFF_ALLMULTI); |
2846 | dev_set_rx_mode(dev); | 2880 | dev_set_rx_mode(dev); |
2847 | } | 2881 | } |
2882 | return 0; | ||
2848 | } | 2883 | } |
2849 | 2884 | ||
2850 | /* | 2885 | /* |