path: root/net/ipv4/igmp.c
diff options
authorKevin Cernekee <cernekee@chromium.org>2017-12-11 11:13:45 -0800
committerDavid S. Miller <davem@davemloft.net>2017-12-13 13:51:27 -0500
commita46182b00290839fa3fa159d54fd3237bd8669f0 (patch)
tree48f19d1cd5275ab4c88cb9d8a5c967f556c35382 /net/ipv4/igmp.c
parenttipc: eliminate potential memory leak (diff)
net: igmp: Use correct source address on IGMPv3 reports
Closing a multicast socket after the final IPv4 address is deleted from an interface can generate a membership report that uses the source IP from a different interface. The following test script, run from an isolated netns, reproduces the issue: #!/bin/bash ip link add dummy0 type dummy ip link add dummy1 type dummy ip link set dummy0 up ip link set dummy1 up ip addr add dev dummy0 ip addr add dev dummy1 tcpdump -U -i dummy0 & socat EXEC:"sleep 2" \ UDP4-DATAGRAM:,ip-add-membership= & sleep 1 ip addr del dev dummy0 sleep 5 kill %tcpdump RFC 3376 specifies that the report must be sent with a valid IP source address from the destination subnet, or from address Add an extra check to make sure this is the case. Signed-off-by: Kevin Cernekee <cernekee@chromium.org> Reviewed-by: Andrew Lunn <andrew@lunn.ch> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv4/igmp.c')
1 files changed, 19 insertions, 1 deletions
diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c
index 50448a220a1f..726f6b608274 100644
--- a/net/ipv4/igmp.c
+++ b/net/ipv4/igmp.c
@@ -89,6 +89,7 @@
#include <linux/rtnetlink.h>
#include <linux/times.h>
#include <linux/pkt_sched.h>
+#include <linux/byteorder/generic.h>
#include <net/net_namespace.h>
#include <net/arp.h>
@@ -321,6 +322,23 @@ igmp_scount(struct ip_mc_list *pmc, int type, int gdeleted, int sdeleted)
return scount;
+/* source address selection per RFC 3376 section 4.2.13 */
+static __be32 igmpv3_get_srcaddr(struct net_device *dev,
+ const struct flowi4 *fl4)
+ struct in_device *in_dev = __in_dev_get_rcu(dev);
+ if (!in_dev)
+ return htonl(INADDR_ANY);
+ for_ifa(in_dev) {
+ if (inet_ifa_match(fl4->saddr, ifa))
+ return fl4->saddr;
+ } endfor_ifa(in_dev);
+ return htonl(INADDR_ANY);
static struct sk_buff *igmpv3_newpack(struct net_device *dev, unsigned int mtu)
struct sk_buff *skb;
@@ -368,7 +386,7 @@ static struct sk_buff *igmpv3_newpack(struct net_device *dev, unsigned int mtu)
pip->frag_off = htons(IP_DF);
pip->ttl = 1;
pip->daddr = fl4.daddr;
- pip->saddr = fl4.saddr;
+ pip->saddr = igmpv3_get_srcaddr(dev, &fl4);
pip->protocol = IPPROTO_IGMP;
pip->tot_len = 0; /* filled in later */
ip_select_ident(net, skb, NULL);