aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWang Chen <wangchen@cn.fujitsu.com>2008-06-18 04:48:28 -0400
committerDavid S. Miller <davem@davemloft.net>2008-06-18 04:48:28 -0400
commitdad9b335c6940de2746a9788eb456d09cf102f81 (patch)
treec74092de70d7c2c9ba88bf580bc404133b55c490
parentdd574dbfcc9e74e7dd8fd59ae0075d23e71a3da1 (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.h4
-rw-r--r--net/core/dev.c55
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
1476extern int __dev_addr_add(struct dev_addr_list **list, int *count, void *addr, int alen, int newonly); 1476extern int __dev_addr_add(struct dev_addr_list **list, int *count, void *addr, int alen, int newonly);
1477extern int __dev_addr_sync(struct dev_addr_list **to, int *to_count, struct dev_addr_list **from, int *from_count); 1477extern int __dev_addr_sync(struct dev_addr_list **to, int *to_count, struct dev_addr_list **from, int *from_count);
1478extern void __dev_addr_unsync(struct dev_addr_list **to, int *to_count, struct dev_addr_list **from, int *from_count); 1478extern void __dev_addr_unsync(struct dev_addr_list **to, int *to_count, struct dev_addr_list **from, int *from_count);
1479extern void dev_set_promiscuity(struct net_device *dev, int inc); 1479extern int dev_set_promiscuity(struct net_device *dev, int inc);
1480extern void dev_set_allmulti(struct net_device *dev, int inc); 1480extern int dev_set_allmulti(struct net_device *dev, int inc);
1481extern void netdev_state_change(struct net_device *dev); 1481extern void netdev_state_change(struct net_device *dev);
1482extern void netdev_bonding_change(struct net_device *dev); 1482extern void netdev_bonding_change(struct net_device *dev);
1483extern void netdev_features_change(struct net_device *dev); 1483extern 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
2774static void __dev_set_promiscuity(struct net_device *dev, int inc) 2774static 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 */
2813void dev_set_promiscuity(struct net_device *dev, int inc) 2828int 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
2834void dev_set_allmulti(struct net_device *dev, int inc) 2854int 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/*