Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ipv4: igmp: guard against silly MTU values

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>

authored by

Eric Dumazet and committed by
David S. Miller
b5476022 b9b312a7

+19 -12
+1
include/net/ip.h
··· 34 34 #include <net/flow_dissector.h> 35 35 36 36 #define IPV4_MAX_PMTU 65535U /* RFC 2675, Section 5.1 */ 37 + #define IPV4_MIN_MTU 68 /* RFC 791 */ 37 38 38 39 struct sock; 39 40
+1 -1
net/ipv4/devinet.c
··· 1428 1428 1429 1429 static bool inetdev_valid_mtu(unsigned int mtu) 1430 1430 { 1431 - return mtu >= 68; 1431 + return mtu >= IPV4_MIN_MTU; 1432 1432 } 1433 1433 1434 1434 static void inetdev_send_gratuitous_arp(struct net_device *dev,
+15 -9
net/ipv4/igmp.c
··· 404 404 } 405 405 406 406 static struct sk_buff *add_grhead(struct sk_buff *skb, struct ip_mc_list *pmc, 407 - int type, struct igmpv3_grec **ppgr) 407 + int type, struct igmpv3_grec **ppgr, unsigned int mtu) 408 408 { 409 409 struct net_device *dev = pmc->interface->dev; 410 410 struct igmpv3_report *pih; 411 411 struct igmpv3_grec *pgr; 412 412 413 - if (!skb) 414 - skb = igmpv3_newpack(dev, dev->mtu); 415 - if (!skb) 416 - return NULL; 413 + if (!skb) { 414 + skb = igmpv3_newpack(dev, mtu); 415 + if (!skb) 416 + return NULL; 417 + } 417 418 pgr = skb_put(skb, sizeof(struct igmpv3_grec)); 418 419 pgr->grec_type = type; 419 420 pgr->grec_auxwords = 0; ··· 437 436 struct igmpv3_grec *pgr = NULL; 438 437 struct ip_sf_list *psf, *psf_next, *psf_prev, **psf_list; 439 438 int scount, stotal, first, isquery, truncate; 439 + unsigned int mtu; 440 440 441 441 if (pmc->multiaddr == IGMP_ALL_HOSTS) 442 442 return skb; 443 443 if (ipv4_is_local_multicast(pmc->multiaddr) && !net->ipv4.sysctl_igmp_llm_reports) 444 + return skb; 445 + 446 + mtu = READ_ONCE(dev->mtu); 447 + if (mtu < IPV4_MIN_MTU) 444 448 return skb; 445 449 446 450 isquery = type == IGMPV3_MODE_IS_INCLUDE || ··· 468 462 AVAILABLE(skb) < grec_size(pmc, type, gdeleted, sdeleted)) { 469 463 if (skb) 470 464 igmpv3_sendpack(skb); 471 - skb = igmpv3_newpack(dev, dev->mtu); 465 + skb = igmpv3_newpack(dev, mtu); 472 466 } 473 467 } 474 468 first = 1; ··· 504 498 pgr->grec_nsrcs = htons(scount); 505 499 if (skb) 506 500 igmpv3_sendpack(skb); 507 - skb = igmpv3_newpack(dev, dev->mtu); 501 + skb = igmpv3_newpack(dev, mtu); 508 502 first = 1; 509 503 scount = 0; 510 504 } 511 505 if (first) { 512 - skb = add_grhead(skb, pmc, type, &pgr); 506 + skb = add_grhead(skb, pmc, type, &pgr, mtu); 513 507 first = 0; 514 508 } 515 509 if (!skb) ··· 544 538 igmpv3_sendpack(skb); 545 539 skb = NULL; /* add_grhead will get a new one */ 546 540 } 547 - skb = add_grhead(skb, pmc, type, &pgr); 541 + skb = add_grhead(skb, pmc, type, &pgr, mtu); 548 542 } 549 543 } 550 544 if (pgr)
+2 -2
net/ipv4/ip_tunnel.c
··· 349 349 dev->needed_headroom = t_hlen + hlen; 350 350 mtu -= (dev->hard_header_len + t_hlen); 351 351 352 - if (mtu < 68) 353 - mtu = 68; 352 + if (mtu < IPV4_MIN_MTU) 353 + mtu = IPV4_MIN_MTU; 354 354 355 355 return mtu; 356 356 }