ip: generate unique IP identificator if local fragmentation is allowed
[pandora-kernel.git] / net / ipv4 / igmp.c
index cd71190..dace87f 100644 (file)
@@ -88,6 +88,7 @@
 #include <linux/if_arp.h>
 #include <linux/rtnetlink.h>
 #include <linux/times.h>
+#include <linux/pkt_sched.h>
 
 #include <net/net_namespace.h>
 #include <net/arp.h>
 
 #define IGMP_V1_Router_Present_Timeout         (400*HZ)
 #define IGMP_V2_Router_Present_Timeout         (400*HZ)
-#define IGMP_Unsolicited_Report_Interval       (10*HZ)
+#define IGMP_V2_Unsolicited_Report_Interval    (10*HZ)
+#define IGMP_V3_Unsolicited_Report_Interval    (1*HZ)
 #define IGMP_Query_Response_Interval           (10*HZ)
 #define IGMP_Unsolicited_Report_Count          2
 
         ((in_dev)->mr_v2_seen && \
          time_before(jiffies, (in_dev)->mr_v2_seen)))
 
+static int unsolicited_report_interval(struct in_device *in_dev)
+{
+       int interval_ms, interval_jiffies;
+
+       if (IGMP_V1_SEEN(in_dev) || IGMP_V2_SEEN(in_dev))
+               interval_ms = IN_DEV_CONF_GET(
+                       in_dev,
+                       IGMPV2_UNSOLICITED_REPORT_INTERVAL);
+       else /* v3 */
+               interval_ms = IN_DEV_CONF_GET(
+                       in_dev,
+                       IGMPV3_UNSOLICITED_REPORT_INTERVAL);
+
+       interval_jiffies = msecs_to_jiffies(interval_ms);
+
+       /* _timer functions can't handle a delay of 0 jiffies so ensure
+        *  we always return a positive value.
+        */
+       if (interval_jiffies <= 0)
+               interval_jiffies = 1;
+       return interval_jiffies;
+}
+
 static void igmpv3_add_delrec(struct in_device *in_dev, struct ip_mc_list *im);
 static void igmpv3_del_delrec(struct in_device *in_dev, __be32 multiaddr);
 static void igmpv3_clear_delrec(struct in_device *in_dev);
@@ -315,6 +340,7 @@ static struct sk_buff *igmpv3_newpack(struct net_device *dev, int size)
                if (size < 256)
                        return NULL;
        }
+       skb->priority = TC_PRIO_CONTROL;
        igmp_skb_size(skb) = size;
 
        rt = ip_route_output_ports(net, &fl4, NULL, IGMPV3_ALL_MCR, 0,
@@ -343,7 +369,7 @@ static struct sk_buff *igmpv3_newpack(struct net_device *dev, int size)
        pip->saddr    = fl4.saddr;
        pip->protocol = IPPROTO_IGMP;
        pip->tot_len  = 0;      /* filled in later */
-       ip_select_ident(pip, &rt->dst, NULL);
+       ip_select_ident(skb, &rt->dst, NULL);
        ((u8 *)&pip[1])[0] = IPOPT_RA;
        ((u8 *)&pip[1])[1] = 4;
        ((u8 *)&pip[1])[2] = 0;
@@ -670,6 +696,7 @@ static int igmp_send_report(struct in_device *in_dev, struct ip_mc_list *pmc,
                ip_rt_put(rt);
                return -1;
        }
+       skb->priority = TC_PRIO_CONTROL;
 
        skb_dst_set(skb, &rt->dst);
 
@@ -687,7 +714,7 @@ static int igmp_send_report(struct in_device *in_dev, struct ip_mc_list *pmc,
        iph->daddr    = dst;
        iph->saddr    = fl4.saddr;
        iph->protocol = IPPROTO_IGMP;
-       ip_select_ident(iph, &rt->dst, NULL);
+       ip_select_ident(skb, &rt->dst, NULL);
        ((u8 *)&iph[1])[0] = IPOPT_RA;
        ((u8 *)&iph[1])[1] = 4;
        ((u8 *)&iph[1])[2] = 0;
@@ -719,7 +746,8 @@ static void igmp_ifc_timer_expire(unsigned long data)
        igmpv3_send_cr(in_dev);
        if (in_dev->mr_ifc_count) {
                in_dev->mr_ifc_count--;
-               igmp_ifc_start_timer(in_dev, IGMP_Unsolicited_Report_Interval);
+               igmp_ifc_start_timer(in_dev,
+                                    unsolicited_report_interval(in_dev));
        }
        __in_dev_put(in_dev);
 }
@@ -744,7 +772,7 @@ static void igmp_timer_expire(unsigned long data)
 
        if (im->unsolicit_count) {
                im->unsolicit_count--;
-               igmp_start_timer(im, IGMP_Unsolicited_Report_Interval);
+               igmp_start_timer(im, unsolicited_report_interval(in_dev));
        }
        im->reporter = 1;
        spin_unlock(&im->lock);
@@ -1323,16 +1351,17 @@ out:
 EXPORT_SYMBOL(ip_mc_inc_group);
 
 /*
- *     Resend IGMP JOIN report; used for bonding.
- *     Called with rcu_read_lock()
+ *     Resend IGMP JOIN report; used by netdev notifier.
  */
-void ip_mc_rejoin_groups(struct in_device *in_dev)
+static void ip_mc_rejoin_groups(struct in_device *in_dev)
 {
 #ifdef CONFIG_IP_MULTICAST
        struct ip_mc_list *im;
        int type;
 
-       for_each_pmc_rcu(in_dev, im) {
+       ASSERT_RTNL();
+
+       for_each_pmc_rtnl(in_dev, im) {
                if (im->multiaddr == IGMP_ALL_HOSTS)
                        continue;
 
@@ -1349,7 +1378,6 @@ void ip_mc_rejoin_groups(struct in_device *in_dev)
        }
 #endif
 }
-EXPORT_SYMBOL(ip_mc_rejoin_groups);
 
 /*
  *     A socket has left a multicast group on device dev
@@ -2735,8 +2763,42 @@ static struct pernet_operations igmp_net_ops = {
        .exit = igmp_net_exit,
 };
 
+static int igmp_netdev_event(struct notifier_block *this,
+                            unsigned long event, void *ptr)
+{
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
+       struct in_device *in_dev;
+
+       switch (event) {
+       case NETDEV_RESEND_IGMP:
+               in_dev = __in_dev_get_rtnl(dev);
+               if (in_dev)
+                       ip_mc_rejoin_groups(in_dev);
+               break;
+       default:
+               break;
+       }
+       return NOTIFY_DONE;
+}
+
+static struct notifier_block igmp_notifier = {
+       .notifier_call = igmp_netdev_event,
+};
+
 int __init igmp_mc_proc_init(void)
 {
-       return register_pernet_subsys(&igmp_net_ops);
+       int err;
+
+       err = register_pernet_subsys(&igmp_net_ops);
+       if (err)
+               return err;
+       err = register_netdevice_notifier(&igmp_notifier);
+       if (err)
+               goto reg_notif_fail;
+       return 0;
+
+reg_notif_fail:
+       unregister_pernet_subsys(&igmp_net_ops);
+       return err;
 }
 #endif