Skip to content

Commit 35103d1

Browse files
gospodavem330
authored andcommitted
net: ipv6 sysctl option to ignore routes when nexthop link is down
Like the ipv4 patch with a similar title, this adds a sysctl to allow the user to change routing behavior based on whether or not the interface associated with the nexthop was an up or down link. The default setting preserves the current behavior, but anyone that enables it will notice that nexthops on down interfaces will no longer be selected: net.ipv6.conf.all.ignore_routes_with_linkdown = 0 net.ipv6.conf.default.ignore_routes_with_linkdown = 0 net.ipv6.conf.lo.ignore_routes_with_linkdown = 0 ... When the above sysctls are set, not only will link status be reported to userspace, but an indication that a nexthop is dead and will not be used is also reported. 1000::/8 via 7000::2 dev p7p1 metric 1024 dead linkdown pref medium 1000::/8 via 8000::2 dev p8p1 metric 1024 pref medium 7000::/8 dev p7p1 proto kernel metric 256 dead linkdown pref medium 8000::/8 dev p8p1 proto kernel metric 256 pref medium 9000::/8 via 8000::2 dev p8p1 metric 2048 pref medium 9000::/8 via 7000::2 dev p7p1 metric 1024 dead linkdown pref medium fe80::/64 dev p7p1 proto kernel metric 256 dead linkdown pref medium fe80::/64 dev p8p1 proto kernel metric 256 pref medium This also adds devconf support and notification when sysctl values change. v2: drop use of rt6i_nhflags since it is not needed right now Signed-off-by: Andy Gospodarek <[email protected]> Signed-off-by: Dinesh Dutt <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent cea45e2 commit 35103d1

File tree

4 files changed

+116
-2
lines changed

4 files changed

+116
-2
lines changed

include/linux/ipv6.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ struct ipv6_devconf {
3131
__s32 accept_ra_defrtr;
3232
__s32 accept_ra_min_hop_limit;
3333
__s32 accept_ra_pinfo;
34+
__s32 ignore_routes_with_linkdown;
3435
#ifdef CONFIG_IPV6_ROUTER_PREF
3536
__s32 accept_ra_rtr_pref;
3637
__s32 rtr_probe_interval;

include/uapi/linux/ipv6.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ enum {
173173
DEVCONF_STABLE_SECRET,
174174
DEVCONF_USE_OIF_ADDRS_ONLY,
175175
DEVCONF_ACCEPT_RA_MIN_HOP_LIMIT,
176+
DEVCONF_IGNORE_ROUTES_WITH_LINKDOWN,
176177
DEVCONF_MAX
177178
};
178179

net/ipv6/addrconf.c

Lines changed: 104 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,7 @@ static struct ipv6_devconf ipv6_devconf __read_mostly = {
214214
.initialized = false,
215215
},
216216
.use_oif_addrs_only = 0,
217+
.ignore_routes_with_linkdown = 0,
217218
};
218219

219220
static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
@@ -257,6 +258,7 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
257258
.initialized = false,
258259
},
259260
.use_oif_addrs_only = 0,
261+
.ignore_routes_with_linkdown = 0,
260262
};
261263

262264
/* Check if a valid qdisc is available */
@@ -472,6 +474,9 @@ static int inet6_netconf_msgsize_devconf(int type)
472474
if (type == -1 || type == NETCONFA_PROXY_NEIGH)
473475
size += nla_total_size(4);
474476

477+
if (type == -1 || type == NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN)
478+
size += nla_total_size(4);
479+
475480
return size;
476481
}
477482

@@ -508,6 +513,11 @@ static int inet6_netconf_fill_devconf(struct sk_buff *skb, int ifindex,
508513
nla_put_s32(skb, NETCONFA_PROXY_NEIGH, devconf->proxy_ndp) < 0)
509514
goto nla_put_failure;
510515

516+
if ((type == -1 || type == NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN) &&
517+
nla_put_s32(skb, NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN,
518+
devconf->ignore_routes_with_linkdown) < 0)
519+
goto nla_put_failure;
520+
511521
nlmsg_end(skb, nlh);
512522
return 0;
513523

@@ -544,6 +554,7 @@ static const struct nla_policy devconf_ipv6_policy[NETCONFA_MAX+1] = {
544554
[NETCONFA_IFINDEX] = { .len = sizeof(int) },
545555
[NETCONFA_FORWARDING] = { .len = sizeof(int) },
546556
[NETCONFA_PROXY_NEIGH] = { .len = sizeof(int) },
557+
[NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN] = { .len = sizeof(int) },
547558
};
548559

549560
static int inet6_netconf_get_devconf(struct sk_buff *in_skb,
@@ -766,6 +777,63 @@ static int addrconf_fixup_forwarding(struct ctl_table *table, int *p, int newf)
766777
rt6_purge_dflt_routers(net);
767778
return 1;
768779
}
780+
781+
static void addrconf_linkdown_change(struct net *net, __s32 newf)
782+
{
783+
struct net_device *dev;
784+
struct inet6_dev *idev;
785+
786+
for_each_netdev(net, dev) {
787+
idev = __in6_dev_get(dev);
788+
if (idev) {
789+
int changed = (!idev->cnf.ignore_routes_with_linkdown) ^ (!newf);
790+
791+
idev->cnf.ignore_routes_with_linkdown = newf;
792+
if (changed)
793+
inet6_netconf_notify_devconf(dev_net(dev),
794+
NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN,
795+
dev->ifindex,
796+
&idev->cnf);
797+
}
798+
}
799+
}
800+
801+
static int addrconf_fixup_linkdown(struct ctl_table *table, int *p, int newf)
802+
{
803+
struct net *net;
804+
int old;
805+
806+
if (!rtnl_trylock())
807+
return restart_syscall();
808+
809+
net = (struct net *)table->extra2;
810+
old = *p;
811+
*p = newf;
812+
813+
if (p == &net->ipv6.devconf_dflt->ignore_routes_with_linkdown) {
814+
if ((!newf) ^ (!old))
815+
inet6_netconf_notify_devconf(net,
816+
NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN,
817+
NETCONFA_IFINDEX_DEFAULT,
818+
net->ipv6.devconf_dflt);
819+
rtnl_unlock();
820+
return 0;
821+
}
822+
823+
if (p == &net->ipv6.devconf_all->ignore_routes_with_linkdown) {
824+
net->ipv6.devconf_dflt->ignore_routes_with_linkdown = newf;
825+
addrconf_linkdown_change(net, newf);
826+
if ((!newf) ^ (!old))
827+
inet6_netconf_notify_devconf(net,
828+
NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN,
829+
NETCONFA_IFINDEX_ALL,
830+
net->ipv6.devconf_all);
831+
}
832+
rtnl_unlock();
833+
834+
return 1;
835+
}
836+
769837
#endif
770838

771839
/* Nobody refers to this ifaddr, destroy it */
@@ -4616,6 +4684,7 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf,
46164684
array[DEVCONF_SUPPRESS_FRAG_NDISC] = cnf->suppress_frag_ndisc;
46174685
array[DEVCONF_ACCEPT_RA_FROM_LOCAL] = cnf->accept_ra_from_local;
46184686
array[DEVCONF_ACCEPT_RA_MTU] = cnf->accept_ra_mtu;
4687+
array[DEVCONF_IGNORE_ROUTES_WITH_LINKDOWN] = cnf->ignore_routes_with_linkdown;
46194688
/* we omit DEVCONF_STABLE_SECRET for now */
46204689
array[DEVCONF_USE_OIF_ADDRS_ONLY] = cnf->use_oif_addrs_only;
46214690
}
@@ -5338,6 +5407,34 @@ static int addrconf_sysctl_stable_secret(struct ctl_table *ctl, int write,
53385407
return err;
53395408
}
53405409

5410+
static
5411+
int addrconf_sysctl_ignore_routes_with_linkdown(struct ctl_table *ctl,
5412+
int write,
5413+
void __user *buffer,
5414+
size_t *lenp,
5415+
loff_t *ppos)
5416+
{
5417+
int *valp = ctl->data;
5418+
int val = *valp;
5419+
loff_t pos = *ppos;
5420+
struct ctl_table lctl;
5421+
int ret;
5422+
5423+
/* ctl->data points to idev->cnf.ignore_routes_when_linkdown
5424+
* we should not modify it until we get the rtnl lock.
5425+
*/
5426+
lctl = *ctl;
5427+
lctl.data = &val;
5428+
5429+
ret = proc_dointvec(&lctl, write, buffer, lenp, ppos);
5430+
5431+
if (write)
5432+
ret = addrconf_fixup_linkdown(ctl, valp, val);
5433+
if (ret)
5434+
*ppos = pos;
5435+
return ret;
5436+
}
5437+
53415438
static struct addrconf_sysctl_table
53425439
{
53435440
struct ctl_table_header *sysctl_header;
@@ -5629,7 +5726,13 @@ static struct addrconf_sysctl_table
56295726
.maxlen = sizeof(int),
56305727
.mode = 0644,
56315728
.proc_handler = proc_dointvec,
5632-
5729+
},
5730+
{
5731+
.procname = "ignore_routes_with_linkdown",
5732+
.data = &ipv6_devconf.ignore_routes_with_linkdown,
5733+
.maxlen = sizeof(int),
5734+
.mode = 0644,
5735+
.proc_handler = addrconf_sysctl_ignore_routes_with_linkdown,
56335736
},
56345737
{
56355738
/* sentinel */

net/ipv6/route.c

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -665,6 +665,12 @@ static struct rt6_info *find_match(struct rt6_info *rt, int oif, int strict,
665665
{
666666
int m;
667667
bool match_do_rr = false;
668+
struct inet6_dev *idev = rt->rt6i_idev;
669+
struct net_device *dev = rt->dst.dev;
670+
671+
if (dev && !netif_carrier_ok(dev) &&
672+
idev->cnf.ignore_routes_with_linkdown)
673+
goto out;
668674

669675
if (rt6_check_expired(rt))
670676
goto out;
@@ -2887,8 +2893,11 @@ static int rt6_fill_node(struct net *net,
28872893
else
28882894
rtm->rtm_type = RTN_UNICAST;
28892895
rtm->rtm_flags = 0;
2890-
if (!netif_carrier_ok(rt->dst.dev))
2896+
if (!netif_carrier_ok(rt->dst.dev)) {
28912897
rtm->rtm_flags |= RTNH_F_LINKDOWN;
2898+
if (rt->rt6i_idev->cnf.ignore_routes_with_linkdown)
2899+
rtm->rtm_flags |= RTNH_F_DEAD;
2900+
}
28922901
rtm->rtm_scope = RT_SCOPE_UNIVERSE;
28932902
rtm->rtm_protocol = rt->rt6i_protocol;
28942903
if (rt->rt6i_flags & RTF_DYNAMIC)

0 commit comments

Comments
 (0)