net: add sysctl accept_ra_min_rtr_lft
authorPatrick Rohr <prohr@google.com>
Wed, 19 Jul 2023 14:52:13 +0000 (07:52 -0700)
committerDavid S. Miller <davem@davemloft.net>
Sun, 23 Jul 2023 10:51:24 +0000 (11:51 +0100)
This change adds a new sysctl accept_ra_min_rtr_lft to specify the
minimum acceptable router lifetime in an RA. If the received RA router
lifetime is less than the configured value (and not 0), the RA is
ignored.
This is useful for mobile devices, whose battery life can be impacted
by networks that configure RAs with a short lifetime. On such networks,
the device should never gain IPv6 provisioning and should attempt to
drop RAs via hardware offload, if available.

Signed-off-by: Patrick Rohr <prohr@google.com>
Cc: Maciej Żenczykowski <maze@google.com>
Cc: Lorenzo Colitti <lorenzo@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Documentation/networking/ip-sysctl.rst
include/linux/ipv6.h
include/uapi/linux/ipv6.h
net/ipv6/addrconf.c
net/ipv6/ndisc.c

index 82f2117cf2b36a834e5e391feda0210d916bff8b..37603ad6126b340813152524e52d30c85126a987 100644 (file)
@@ -2288,6 +2288,14 @@ accept_ra_min_hop_limit - INTEGER
 
        Default: 1
 
+accept_ra_min_rtr_lft - INTEGER
+       Minimum acceptable router lifetime in Router Advertisement.
+
+       RAs with a router lifetime less than this value shall be
+       ignored. RAs with a router lifetime of 0 are unaffected.
+
+       Default: 0
+
 accept_ra_pinfo - BOOLEAN
        Learn Prefix Information in Router Advertisement.
 
index 839247a4f48ea76b5d6daa9a54a7b87627635066..ed3d110c2eb5931703a81a995ec01e8e1a356878 100644 (file)
@@ -33,6 +33,7 @@ struct ipv6_devconf {
        __s32           accept_ra_defrtr;
        __u32           ra_defrtr_metric;
        __s32           accept_ra_min_hop_limit;
+       __s32           accept_ra_min_rtr_lft;
        __s32           accept_ra_pinfo;
        __s32           ignore_routes_with_linkdown;
 #ifdef CONFIG_IPV6_ROUTER_PREF
index ac56605fe9bcaca5d44a540dc044f679fd063957..8b6bcbf6ed4a892ea543539a9b1c37da5a430da4 100644 (file)
@@ -198,6 +198,7 @@ enum {
        DEVCONF_IOAM6_ID_WIDE,
        DEVCONF_NDISC_EVICT_NOCARRIER,
        DEVCONF_ACCEPT_UNTRACKED_NA,
+       DEVCONF_ACCEPT_RA_MIN_RTR_LFT,
        DEVCONF_MAX
 };
 
index e5213e598a0408a55091cab9da7351c9cefc604d..19eb4b3d26ea7fce5f13cdd6cced93aee7c4ae0c 100644 (file)
@@ -202,6 +202,7 @@ static struct ipv6_devconf ipv6_devconf __read_mostly = {
        .ra_defrtr_metric       = IP6_RT_PRIO_USER,
        .accept_ra_from_local   = 0,
        .accept_ra_min_hop_limit= 1,
+       .accept_ra_min_rtr_lft  = 0,
        .accept_ra_pinfo        = 1,
 #ifdef CONFIG_IPV6_ROUTER_PREF
        .accept_ra_rtr_pref     = 1,
@@ -262,6 +263,7 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
        .ra_defrtr_metric       = IP6_RT_PRIO_USER,
        .accept_ra_from_local   = 0,
        .accept_ra_min_hop_limit= 1,
+       .accept_ra_min_rtr_lft  = 0,
        .accept_ra_pinfo        = 1,
 #ifdef CONFIG_IPV6_ROUTER_PREF
        .accept_ra_rtr_pref     = 1,
@@ -5596,6 +5598,7 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf,
        array[DEVCONF_IOAM6_ID_WIDE] = cnf->ioam6_id_wide;
        array[DEVCONF_NDISC_EVICT_NOCARRIER] = cnf->ndisc_evict_nocarrier;
        array[DEVCONF_ACCEPT_UNTRACKED_NA] = cnf->accept_untracked_na;
+       array[DEVCONF_ACCEPT_RA_MIN_RTR_LFT] = cnf->accept_ra_min_rtr_lft;
 }
 
 static inline size_t inet6_ifla6_size(void)
@@ -6789,6 +6792,13 @@ static const struct ctl_table addrconf_sysctl[] = {
                .mode           = 0644,
                .proc_handler   = proc_dointvec,
        },
+       {
+               .procname       = "accept_ra_min_rtr_lft",
+               .data           = &ipv6_devconf.accept_ra_min_rtr_lft,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec,
+       },
        {
                .procname       = "accept_ra_pinfo",
                .data           = &ipv6_devconf.accept_ra_pinfo,
index 18634ebd20a47d2d0d707c164e0691d3beb60427..29ddad1c1a2f3ba186b00b282328de58cb824278 100644 (file)
@@ -1280,6 +1280,8 @@ static enum skb_drop_reason ndisc_router_discovery(struct sk_buff *skb)
        if (!ndisc_parse_options(skb->dev, opt, optlen, &ndopts))
                return SKB_DROP_REASON_IPV6_NDISC_BAD_OPTIONS;
 
+       lifetime = ntohs(ra_msg->icmph.icmp6_rt_lifetime);
+
        if (!ipv6_accept_ra(in6_dev)) {
                ND_PRINTK(2, info,
                          "RA: %s, did not accept ra for dev: %s\n",
@@ -1287,6 +1289,13 @@ static enum skb_drop_reason ndisc_router_discovery(struct sk_buff *skb)
                goto skip_linkparms;
        }
 
+       if (lifetime != 0 && lifetime < in6_dev->cnf.accept_ra_min_rtr_lft) {
+               ND_PRINTK(2, info,
+                         "RA: router lifetime (%ds) is too short: %s\n",
+                         lifetime, skb->dev->name);
+               goto skip_linkparms;
+       }
+
 #ifdef CONFIG_IPV6_NDISC_NODETYPE
        /* skip link-specific parameters from interior routers */
        if (skb->ndisc_nodetype == NDISC_NODETYPE_NODEFAULT) {
@@ -1339,8 +1348,6 @@ static enum skb_drop_reason ndisc_router_discovery(struct sk_buff *skb)
                goto skip_defrtr;
        }
 
-       lifetime = ntohs(ra_msg->icmph.icmp6_rt_lifetime);
-
 #ifdef CONFIG_IPV6_ROUTER_PREF
        pref = ra_msg->icmph.icmp6_router_pref;
        /* 10b is handled as if it were 00b (medium) */
@@ -1492,6 +1499,13 @@ skip_linkparms:
                goto out;
        }
 
+       if (lifetime != 0 && lifetime < in6_dev->cnf.accept_ra_min_rtr_lft) {
+               ND_PRINTK(2, info,
+                         "RA: router lifetime (%ds) is too short: %s\n",
+                         lifetime, skb->dev->name);
+               goto out;
+       }
+
 #ifdef CONFIG_IPV6_ROUTE_INFO
        if (!in6_dev->cnf.accept_ra_from_local &&
            ipv6_chk_addr(dev_net(in6_dev->dev), &ipv6_hdr(skb)->saddr,