Skip to content

Commit

Permalink
ipv4: igmp: guard against silly MTU values
Browse files Browse the repository at this point in the history
[ Upstream commit b547602 ]

IPv4 stack reacts to changes to small MTU, by disabling itself under
RTNL.

But there is a window where threads not using RTNL can see a wrong
device mtu. This can lead to surprises, in igmp code where it is
assumed the mtu is suitable.

Fix this by reading device mtu once and checking IPv4 minimal MTU.

This patch adds missing IPV4_MIN_MTU define, to not abuse
ETH_MIN_MTU anymore.

Signed-off-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
  • Loading branch information
Eric Dumazet authored and gregkh committed Jan 2, 2018
1 parent b929ccc commit c2f78bf
Show file tree
Hide file tree
Showing 4 changed files with 20 additions and 12 deletions.
2 changes: 2 additions & 0 deletions include/net/ip.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
#include <net/flow.h>
#include <net/flow_dissector.h>

#define IPV4_MIN_MTU 68 /* RFC 791 */

struct sock;

struct inet_skb_parm {
Expand Down
2 changes: 1 addition & 1 deletion net/ipv4/devinet.c
Original file line number Diff line number Diff line change
Expand Up @@ -1380,7 +1380,7 @@ static void inetdev_changename(struct net_device *dev, struct in_device *in_dev)

static bool inetdev_valid_mtu(unsigned int mtu)
{
return mtu >= 68;
return mtu >= IPV4_MIN_MTU;
}

static void inetdev_send_gratuitous_arp(struct net_device *dev,
Expand Down
24 changes: 15 additions & 9 deletions net/ipv4/igmp.c
Original file line number Diff line number Diff line change
Expand Up @@ -404,16 +404,17 @@ static int grec_size(struct ip_mc_list *pmc, int type, int gdel, int sdel)
}

static struct sk_buff *add_grhead(struct sk_buff *skb, struct ip_mc_list *pmc,
int type, struct igmpv3_grec **ppgr)
int type, struct igmpv3_grec **ppgr, unsigned int mtu)
{
struct net_device *dev = pmc->interface->dev;
struct igmpv3_report *pih;
struct igmpv3_grec *pgr;

if (!skb)
skb = igmpv3_newpack(dev, dev->mtu);
if (!skb)
return NULL;
if (!skb) {
skb = igmpv3_newpack(dev, mtu);
if (!skb)
return NULL;
}
pgr = (struct igmpv3_grec *)skb_put(skb, sizeof(struct igmpv3_grec));
pgr->grec_type = type;
pgr->grec_auxwords = 0;
Expand All @@ -436,12 +437,17 @@ static struct sk_buff *add_grec(struct sk_buff *skb, struct ip_mc_list *pmc,
struct igmpv3_grec *pgr = NULL;
struct ip_sf_list *psf, *psf_next, *psf_prev, **psf_list;
int scount, stotal, first, isquery, truncate;
unsigned int mtu;

if (pmc->multiaddr == IGMP_ALL_HOSTS)
return skb;
if (ipv4_is_local_multicast(pmc->multiaddr) && !net->ipv4.sysctl_igmp_llm_reports)
return skb;

mtu = READ_ONCE(dev->mtu);
if (mtu < IPV4_MIN_MTU)
return skb;

isquery = type == IGMPV3_MODE_IS_INCLUDE ||
type == IGMPV3_MODE_IS_EXCLUDE;
truncate = type == IGMPV3_MODE_IS_EXCLUDE ||
Expand All @@ -462,7 +468,7 @@ static struct sk_buff *add_grec(struct sk_buff *skb, struct ip_mc_list *pmc,
AVAILABLE(skb) < grec_size(pmc, type, gdeleted, sdeleted)) {
if (skb)
igmpv3_sendpack(skb);
skb = igmpv3_newpack(dev, dev->mtu);
skb = igmpv3_newpack(dev, mtu);
}
}
first = 1;
Expand Down Expand Up @@ -498,12 +504,12 @@ static struct sk_buff *add_grec(struct sk_buff *skb, struct ip_mc_list *pmc,
pgr->grec_nsrcs = htons(scount);
if (skb)
igmpv3_sendpack(skb);
skb = igmpv3_newpack(dev, dev->mtu);
skb = igmpv3_newpack(dev, mtu);
first = 1;
scount = 0;
}
if (first) {
skb = add_grhead(skb, pmc, type, &pgr);
skb = add_grhead(skb, pmc, type, &pgr, mtu);
first = 0;
}
if (!skb)
Expand Down Expand Up @@ -538,7 +544,7 @@ static struct sk_buff *add_grec(struct sk_buff *skb, struct ip_mc_list *pmc,
igmpv3_sendpack(skb);
skb = NULL; /* add_grhead will get a new one */
}
skb = add_grhead(skb, pmc, type, &pgr);
skb = add_grhead(skb, pmc, type, &pgr, mtu);
}
}
if (pgr)
Expand Down
4 changes: 2 additions & 2 deletions net/ipv4/ip_tunnel.c
Original file line number Diff line number Diff line change
Expand Up @@ -346,8 +346,8 @@ static int ip_tunnel_bind_dev(struct net_device *dev)
dev->needed_headroom = t_hlen + hlen;
mtu -= (dev->hard_header_len + t_hlen);

if (mtu < 68)
mtu = 68;
if (mtu < IPV4_MIN_MTU)
mtu = IPV4_MIN_MTU;

return mtu;
}
Expand Down

0 comments on commit c2f78bf

Please sign in to comment.