| From foo@baz Sun Dec 31 11:13:15 CET 2017 |
| From: Eric Dumazet <edumazet@google.com> |
| Date: Mon, 11 Dec 2017 07:17:39 -0800 |
| Subject: ipv4: igmp: guard against silly MTU values |
| |
| From: Eric Dumazet <edumazet@google.com> |
| |
| |
| [ Upstream commit b5476022bbada3764609368f03329ca287528dc8 ] |
| |
| 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> |
| --- |
| include/net/ip.h | 2 ++ |
| net/ipv4/devinet.c | 2 +- |
| net/ipv4/igmp.c | 24 +++++++++++++++--------- |
| net/ipv4/ip_tunnel.c | 4 ++-- |
| 4 files changed, 20 insertions(+), 12 deletions(-) |
| |
| --- a/include/net/ip.h |
| +++ b/include/net/ip.h |
| @@ -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 { |
| --- a/net/ipv4/devinet.c |
| +++ b/net/ipv4/devinet.c |
| @@ -1380,7 +1380,7 @@ skip: |
| |
| 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, |
| --- a/net/ipv4/igmp.c |
| +++ b/net/ipv4/igmp.c |
| @@ -404,16 +404,17 @@ static int grec_size(struct ip_mc_list * |
| } |
| |
| 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; |
| @@ -436,12 +437,17 @@ static struct sk_buff *add_grec(struct s |
| 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 || |
| @@ -462,7 +468,7 @@ static struct sk_buff *add_grec(struct s |
| 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; |
| @@ -498,12 +504,12 @@ static struct sk_buff *add_grec(struct s |
| 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) |
| @@ -538,7 +544,7 @@ empty_source: |
| 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) |
| --- a/net/ipv4/ip_tunnel.c |
| +++ b/net/ipv4/ip_tunnel.c |
| @@ -346,8 +346,8 @@ static int ip_tunnel_bind_dev(struct net |
| 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; |
| } |