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

ipv6: add IFLA_INET6_RA_MTU to expose mtu value

The kernel provides a "/proc/sys/net/ipv6/conf/<iface>/mtu"
file, which can temporarily record the mtu value of the last
received RA message when the RA mtu value is lower than the
interface mtu, but this proc has following limitations:

(1) when the interface mtu (/sys/class/net/<iface>/mtu) is
updeated, mtu6 (/proc/sys/net/ipv6/conf/<iface>/mtu) will
be updated to the value of interface mtu;
(2) mtu6 (/proc/sys/net/ipv6/conf/<iface>/mtu) only affect
ipv6 connection, and not affect ipv4.

Therefore, when the mtu option is carried in the RA message,
there will be a problem that the user sometimes cannot obtain
RA mtu value correctly by reading mtu6.

After this patch set, if a RA message carries the mtu option,
you can send a netlink msg which nlmsg_type is RTM_GETLINK,
and then by parsing the attribute of IFLA_INET6_RA_MTU to
get the mtu value carried in the RA message received on the
inet6 device. In addition, you can also get a link notification
when ra_mtu is updated so it doesn't have to poll.

In this way, if the MTU values that the device receives from
the network in the PCO IPv4 and the RA IPv6 procedures are
different, the user can obtain the correct ipv6 ra_mtu value
and compare the value of ra_mtu and ipv4 mtu, then the device
can use the lower MTU value for both IPv4 and IPv6.

Signed-off-by: Rocco Yue <rocco.yue@mediatek.com>
Reviewed-by: David Ahern <dsahern@kernel.org>
Link: https://lore.kernel.org/r/20210827150412.9267-1-rocco.yue@mediatek.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Rocco Yue and committed by
Jakub Kicinski
49b99da2 0975d8b4

+25 -6
+2
include/net/if_inet6.h
··· 210 210 211 211 unsigned long tstamp; /* ipv6InterfaceTable update timestamp */ 212 212 struct rcu_head rcu; 213 + 214 + unsigned int ra_mtu; 213 215 }; 214 216 215 217 static inline void ipv6_eth_mc_map(const struct in6_addr *addr, char *buf)
+1
include/uapi/linux/if_link.h
··· 417 417 IFLA_INET6_ICMP6STATS, /* statistics (icmpv6) */ 418 418 IFLA_INET6_TOKEN, /* device token */ 419 419 IFLA_INET6_ADDR_GEN_MODE, /* implicit address generator mode */ 420 + IFLA_INET6_RA_MTU, /* mtu carried in the RA message */ 420 421 __IFLA_INET6_MAX 421 422 }; 422 423
+10
net/ipv6/addrconf.c
··· 394 394 ndev->cnf.addr_gen_mode = IN6_ADDR_GEN_MODE_STABLE_PRIVACY; 395 395 396 396 ndev->cnf.mtu6 = dev->mtu; 397 + ndev->ra_mtu = 0; 397 398 ndev->nd_parms = neigh_parms_alloc(dev, &nd_tbl); 398 399 if (!ndev->nd_parms) { 399 400 kfree(ndev); ··· 3850 3849 } 3851 3850 3852 3851 idev->tstamp = jiffies; 3852 + idev->ra_mtu = 0; 3853 3853 3854 3854 /* Last: Shot the device (if unregistered) */ 3855 3855 if (unregister) { ··· 5545 5543 + nla_total_size(ICMP6_MIB_MAX * 8) /* IFLA_INET6_ICMP6STATS */ 5546 5544 + nla_total_size(sizeof(struct in6_addr)) /* IFLA_INET6_TOKEN */ 5547 5545 + nla_total_size(1) /* IFLA_INET6_ADDR_GEN_MODE */ 5546 + + nla_total_size(4) /* IFLA_INET6_RA_MTU */ 5548 5547 + 0; 5549 5548 } 5550 5549 ··· 5652 5649 read_unlock_bh(&idev->lock); 5653 5650 5654 5651 if (nla_put_u8(skb, IFLA_INET6_ADDR_GEN_MODE, idev->cnf.addr_gen_mode)) 5652 + goto nla_put_failure; 5653 + 5654 + if (idev->ra_mtu && 5655 + nla_put_u32(skb, IFLA_INET6_RA_MTU, idev->ra_mtu)) 5655 5656 goto nla_put_failure; 5656 5657 5657 5658 return 0; ··· 5774 5767 static const struct nla_policy inet6_af_policy[IFLA_INET6_MAX + 1] = { 5775 5768 [IFLA_INET6_ADDR_GEN_MODE] = { .type = NLA_U8 }, 5776 5769 [IFLA_INET6_TOKEN] = { .len = sizeof(struct in6_addr) }, 5770 + [IFLA_INET6_RA_MTU] = { .type = NLA_REJECT, 5771 + .reject_message = 5772 + "IFLA_INET6_RA_MTU can not be set" }, 5777 5773 }; 5778 5774 5779 5775 static int check_addr_gen_mode(int mode)
+11 -6
net/ipv6/ndisc.c
··· 1391 1391 } 1392 1392 } 1393 1393 1394 - /* 1395 - * Send a notify if RA changed managed/otherconf flags or timer settings 1396 - */ 1397 - if (send_ifinfo_notify) 1398 - inet6_ifinfo_notify(RTM_NEWLINK, in6_dev); 1399 - 1400 1394 skip_linkparms: 1401 1395 1402 1396 /* ··· 1490 1496 memcpy(&n, ((u8 *)(ndopts.nd_opts_mtu+1))+2, sizeof(mtu)); 1491 1497 mtu = ntohl(n); 1492 1498 1499 + if (in6_dev->ra_mtu != mtu) { 1500 + in6_dev->ra_mtu = mtu; 1501 + send_ifinfo_notify = true; 1502 + } 1503 + 1493 1504 if (mtu < IPV6_MIN_MTU || mtu > skb->dev->mtu) { 1494 1505 ND_PRINTK(2, warn, "RA: invalid mtu: %d\n", mtu); 1495 1506 } else if (in6_dev->cnf.mtu6 != mtu) { ··· 1518 1519 ND_PRINTK(2, warn, "RA: invalid RA options\n"); 1519 1520 } 1520 1521 out: 1522 + /* Send a notify if RA changed managed/otherconf flags or 1523 + * timer settings or ra_mtu value 1524 + */ 1525 + if (send_ifinfo_notify) 1526 + inet6_ifinfo_notify(RTM_NEWLINK, in6_dev); 1527 + 1521 1528 fib6_info_release(rt); 1522 1529 if (neigh) 1523 1530 neigh_release(neigh);
+1
tools/include/uapi/linux/if_link.h
··· 230 230 IFLA_INET6_ICMP6STATS, /* statistics (icmpv6) */ 231 231 IFLA_INET6_TOKEN, /* device token */ 232 232 IFLA_INET6_ADDR_GEN_MODE, /* implicit address generator mode */ 233 + IFLA_INET6_RA_MTU, /* mtu carried in the RA message */ 233 234 __IFLA_INET6_MAX 234 235 }; 235 236