Skip to content

Commit

Permalink
net: ipv6 sysctl option to ignore routes when nexthop link is down
Browse files Browse the repository at this point in the history
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 <gospo@cumulusnetworks.com>
Signed-off-by: Dinesh Dutt <ddutt@cumulusnetworks.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Andy Gospodarek authored and David S. Miller committed Aug 14, 2015
1 parent cea45e2 commit 35103d1
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 2 deletions.
1 change: 1 addition & 0 deletions include/linux/ipv6.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ struct ipv6_devconf {
__s32 accept_ra_defrtr;
__s32 accept_ra_min_hop_limit;
__s32 accept_ra_pinfo;
__s32 ignore_routes_with_linkdown;
#ifdef CONFIG_IPV6_ROUTER_PREF
__s32 accept_ra_rtr_pref;
__s32 rtr_probe_interval;
Expand Down
1 change: 1 addition & 0 deletions include/uapi/linux/ipv6.h
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ enum {
DEVCONF_STABLE_SECRET,
DEVCONF_USE_OIF_ADDRS_ONLY,
DEVCONF_ACCEPT_RA_MIN_HOP_LIMIT,
DEVCONF_IGNORE_ROUTES_WITH_LINKDOWN,
DEVCONF_MAX
};

Expand Down
105 changes: 104 additions & 1 deletion net/ipv6/addrconf.c
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,7 @@ static struct ipv6_devconf ipv6_devconf __read_mostly = {
.initialized = false,
},
.use_oif_addrs_only = 0,
.ignore_routes_with_linkdown = 0,
};

static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
Expand Down Expand Up @@ -257,6 +258,7 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
.initialized = false,
},
.use_oif_addrs_only = 0,
.ignore_routes_with_linkdown = 0,
};

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

if (type == -1 || type == NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN)
size += nla_total_size(4);

return size;
}

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

if ((type == -1 || type == NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN) &&
nla_put_s32(skb, NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN,
devconf->ignore_routes_with_linkdown) < 0)
goto nla_put_failure;

nlmsg_end(skb, nlh);
return 0;

Expand Down Expand Up @@ -544,6 +554,7 @@ static const struct nla_policy devconf_ipv6_policy[NETCONFA_MAX+1] = {
[NETCONFA_IFINDEX] = { .len = sizeof(int) },
[NETCONFA_FORWARDING] = { .len = sizeof(int) },
[NETCONFA_PROXY_NEIGH] = { .len = sizeof(int) },
[NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN] = { .len = sizeof(int) },
};

static int inet6_netconf_get_devconf(struct sk_buff *in_skb,
Expand Down Expand Up @@ -766,6 +777,63 @@ static int addrconf_fixup_forwarding(struct ctl_table *table, int *p, int newf)
rt6_purge_dflt_routers(net);
return 1;
}

static void addrconf_linkdown_change(struct net *net, __s32 newf)
{
struct net_device *dev;
struct inet6_dev *idev;

for_each_netdev(net, dev) {
idev = __in6_dev_get(dev);
if (idev) {
int changed = (!idev->cnf.ignore_routes_with_linkdown) ^ (!newf);

idev->cnf.ignore_routes_with_linkdown = newf;
if (changed)
inet6_netconf_notify_devconf(dev_net(dev),
NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN,
dev->ifindex,
&idev->cnf);
}
}
}

static int addrconf_fixup_linkdown(struct ctl_table *table, int *p, int newf)
{
struct net *net;
int old;

if (!rtnl_trylock())
return restart_syscall();

net = (struct net *)table->extra2;
old = *p;
*p = newf;

if (p == &net->ipv6.devconf_dflt->ignore_routes_with_linkdown) {
if ((!newf) ^ (!old))
inet6_netconf_notify_devconf(net,
NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN,
NETCONFA_IFINDEX_DEFAULT,
net->ipv6.devconf_dflt);
rtnl_unlock();
return 0;
}

if (p == &net->ipv6.devconf_all->ignore_routes_with_linkdown) {
net->ipv6.devconf_dflt->ignore_routes_with_linkdown = newf;
addrconf_linkdown_change(net, newf);
if ((!newf) ^ (!old))
inet6_netconf_notify_devconf(net,
NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN,
NETCONFA_IFINDEX_ALL,
net->ipv6.devconf_all);
}
rtnl_unlock();

return 1;
}

#endif

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

static
int addrconf_sysctl_ignore_routes_with_linkdown(struct ctl_table *ctl,
int write,
void __user *buffer,
size_t *lenp,
loff_t *ppos)
{
int *valp = ctl->data;
int val = *valp;
loff_t pos = *ppos;
struct ctl_table lctl;
int ret;

/* ctl->data points to idev->cnf.ignore_routes_when_linkdown
* we should not modify it until we get the rtnl lock.
*/
lctl = *ctl;
lctl.data = &val;

ret = proc_dointvec(&lctl, write, buffer, lenp, ppos);

if (write)
ret = addrconf_fixup_linkdown(ctl, valp, val);
if (ret)
*ppos = pos;
return ret;
}

static struct addrconf_sysctl_table
{
struct ctl_table_header *sysctl_header;
Expand Down Expand Up @@ -5629,7 +5726,13 @@ static struct addrconf_sysctl_table
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = proc_dointvec,

},
{
.procname = "ignore_routes_with_linkdown",
.data = &ipv6_devconf.ignore_routes_with_linkdown,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = addrconf_sysctl_ignore_routes_with_linkdown,
},
{
/* sentinel */
Expand Down
11 changes: 10 additions & 1 deletion net/ipv6/route.c
Original file line number Diff line number Diff line change
Expand Up @@ -665,6 +665,12 @@ static struct rt6_info *find_match(struct rt6_info *rt, int oif, int strict,
{
int m;
bool match_do_rr = false;
struct inet6_dev *idev = rt->rt6i_idev;
struct net_device *dev = rt->dst.dev;

if (dev && !netif_carrier_ok(dev) &&
idev->cnf.ignore_routes_with_linkdown)
goto out;

if (rt6_check_expired(rt))
goto out;
Expand Down Expand Up @@ -2887,8 +2893,11 @@ static int rt6_fill_node(struct net *net,
else
rtm->rtm_type = RTN_UNICAST;
rtm->rtm_flags = 0;
if (!netif_carrier_ok(rt->dst.dev))
if (!netif_carrier_ok(rt->dst.dev)) {
rtm->rtm_flags |= RTNH_F_LINKDOWN;
if (rt->rt6i_idev->cnf.ignore_routes_with_linkdown)
rtm->rtm_flags |= RTNH_F_DEAD;
}
rtm->rtm_scope = RT_SCOPE_UNIVERSE;
rtm->rtm_protocol = rt->rt6i_protocol;
if (rt->rt6i_flags & RTF_DYNAMIC)
Expand Down

0 comments on commit 35103d1

Please sign in to comment.