Commit 8308f3ff authored by David Ahern's avatar David Ahern Committed by David S. Miller

net/ipv6: Add support for specifying metric of connected routes

Add support for IFA_RT_PRIORITY to ipv6 addresses.

If the metric is changed on an existing address then the new route
is inserted before removing the old one. Since the metric is one
of the route keys, the prefix route can not be atomically replaced.
Signed-off-by: default avatarDavid Ahern <dsahern@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent af4d768a
...@@ -65,6 +65,7 @@ struct ifa6_config { ...@@ -65,6 +65,7 @@ struct ifa6_config {
const struct in6_addr *peer_pfx; const struct in6_addr *peer_pfx;
u32 rt_priority;
u32 ifa_flags; u32 ifa_flags;
u32 preferred_lft; u32 preferred_lft;
u32 valid_lft; u32 valid_lft;
......
...@@ -42,6 +42,7 @@ enum { ...@@ -42,6 +42,7 @@ enum {
struct inet6_ifaddr { struct inet6_ifaddr {
struct in6_addr addr; struct in6_addr addr;
__u32 prefix_len; __u32 prefix_len;
__u32 rt_priority;
/* In seconds, relative to tstamp. Expiry is at tstamp + HZ * lft. */ /* In seconds, relative to tstamp. Expiry is at tstamp + HZ * lft. */
__u32 valid_lft; __u32 valid_lft;
......
...@@ -1056,6 +1056,7 @@ ipv6_add_addr(struct inet6_dev *idev, struct ifa6_config *cfg, ...@@ -1056,6 +1056,7 @@ ipv6_add_addr(struct inet6_dev *idev, struct ifa6_config *cfg,
INIT_HLIST_NODE(&ifa->addr_lst); INIT_HLIST_NODE(&ifa->addr_lst);
ifa->scope = cfg->scope; ifa->scope = cfg->scope;
ifa->prefix_len = cfg->plen; ifa->prefix_len = cfg->plen;
ifa->rt_priority = cfg->rt_priority;
ifa->flags = cfg->ifa_flags; ifa->flags = cfg->ifa_flags;
/* No need to add the TENTATIVE flag for addresses with NODAD */ /* No need to add the TENTATIVE flag for addresses with NODAD */
if (!(cfg->ifa_flags & IFA_F_NODAD)) if (!(cfg->ifa_flags & IFA_F_NODAD))
...@@ -1356,6 +1357,7 @@ static int ipv6_create_tempaddr(struct inet6_ifaddr *ifp, ...@@ -1356,6 +1357,7 @@ static int ipv6_create_tempaddr(struct inet6_ifaddr *ifp,
cfg.pfx = &addr; cfg.pfx = &addr;
cfg.scope = ipv6_addr_scope(cfg.pfx); cfg.scope = ipv6_addr_scope(cfg.pfx);
cfg.rt_priority = 0;
ift = ipv6_add_addr(idev, &cfg, block, NULL); ift = ipv6_add_addr(idev, &cfg, block, NULL);
if (IS_ERR(ift)) { if (IS_ERR(ift)) {
...@@ -2314,12 +2316,13 @@ static void ipv6_try_regen_rndid(struct inet6_dev *idev, struct in6_addr *tmpad ...@@ -2314,12 +2316,13 @@ static void ipv6_try_regen_rndid(struct inet6_dev *idev, struct in6_addr *tmpad
*/ */
static void static void
addrconf_prefix_route(struct in6_addr *pfx, int plen, struct net_device *dev, addrconf_prefix_route(struct in6_addr *pfx, int plen, u32 metric,
unsigned long expires, u32 flags, gfp_t gfp_flags) struct net_device *dev, unsigned long expires,
u32 flags, gfp_t gfp_flags)
{ {
struct fib6_config cfg = { struct fib6_config cfg = {
.fc_table = l3mdev_fib_table(dev) ? : RT6_TABLE_PREFIX, .fc_table = l3mdev_fib_table(dev) ? : RT6_TABLE_PREFIX,
.fc_metric = IP6_RT_PRIO_ADDRCONF, .fc_metric = metric ? : IP6_RT_PRIO_ADDRCONF,
.fc_ifindex = dev->ifindex, .fc_ifindex = dev->ifindex,
.fc_expires = expires, .fc_expires = expires,
.fc_dst_len = plen, .fc_dst_len = plen,
...@@ -2683,7 +2686,8 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao) ...@@ -2683,7 +2686,8 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao)
expires = jiffies_to_clock_t(rt_expires); expires = jiffies_to_clock_t(rt_expires);
} }
addrconf_prefix_route(&pinfo->prefix, pinfo->prefix_len, addrconf_prefix_route(&pinfo->prefix, pinfo->prefix_len,
dev, expires, flags, GFP_ATOMIC); 0, dev, expires, flags,
GFP_ATOMIC);
} }
fib6_info_release(rt); fib6_info_release(rt);
} }
...@@ -2891,8 +2895,9 @@ static int inet6_addr_add(struct net *net, int ifindex, ...@@ -2891,8 +2895,9 @@ static int inet6_addr_add(struct net *net, int ifindex,
ifp = ipv6_add_addr(idev, cfg, true, extack); ifp = ipv6_add_addr(idev, cfg, true, extack);
if (!IS_ERR(ifp)) { if (!IS_ERR(ifp)) {
if (!(cfg->ifa_flags & IFA_F_NOPREFIXROUTE)) { if (!(cfg->ifa_flags & IFA_F_NOPREFIXROUTE)) {
addrconf_prefix_route(&ifp->addr, ifp->prefix_len, dev, addrconf_prefix_route(&ifp->addr, ifp->prefix_len,
expires, flags, GFP_KERNEL); ifp->rt_priority, dev, expires,
flags, GFP_KERNEL);
} }
/* Send a netlink notification if DAD is enabled and /* Send a netlink notification if DAD is enabled and
...@@ -3056,7 +3061,7 @@ static void sit_add_v4_addrs(struct inet6_dev *idev) ...@@ -3056,7 +3061,7 @@ static void sit_add_v4_addrs(struct inet6_dev *idev)
if (addr.s6_addr32[3]) { if (addr.s6_addr32[3]) {
add_addr(idev, &addr, plen, scope); add_addr(idev, &addr, plen, scope);
addrconf_prefix_route(&addr, plen, idev->dev, 0, pflags, addrconf_prefix_route(&addr, plen, 0, idev->dev, 0, pflags,
GFP_ATOMIC); GFP_ATOMIC);
return; return;
} }
...@@ -3081,8 +3086,8 @@ static void sit_add_v4_addrs(struct inet6_dev *idev) ...@@ -3081,8 +3086,8 @@ static void sit_add_v4_addrs(struct inet6_dev *idev)
} }
add_addr(idev, &addr, plen, flag); add_addr(idev, &addr, plen, flag);
addrconf_prefix_route(&addr, plen, idev->dev, 0, addrconf_prefix_route(&addr, plen, 0, idev->dev,
pflags, GFP_ATOMIC); 0, pflags, GFP_ATOMIC);
} }
} }
} }
...@@ -3128,7 +3133,7 @@ void addrconf_add_linklocal(struct inet6_dev *idev, ...@@ -3128,7 +3133,7 @@ void addrconf_add_linklocal(struct inet6_dev *idev,
ifp = ipv6_add_addr(idev, &cfg, true, NULL); ifp = ipv6_add_addr(idev, &cfg, true, NULL);
if (!IS_ERR(ifp)) { if (!IS_ERR(ifp)) {
addrconf_prefix_route(&ifp->addr, ifp->prefix_len, idev->dev, addrconf_prefix_route(&ifp->addr, ifp->prefix_len, 0, idev->dev,
0, 0, GFP_ATOMIC); 0, 0, GFP_ATOMIC);
addrconf_dad_start(ifp); addrconf_dad_start(ifp);
in6_ifa_put(ifp); in6_ifa_put(ifp);
...@@ -3244,7 +3249,7 @@ static void addrconf_addr_gen(struct inet6_dev *idev, bool prefix_route) ...@@ -3244,7 +3249,7 @@ static void addrconf_addr_gen(struct inet6_dev *idev, bool prefix_route)
addrconf_add_linklocal(idev, &addr, addrconf_add_linklocal(idev, &addr,
IFA_F_STABLE_PRIVACY); IFA_F_STABLE_PRIVACY);
else if (prefix_route) else if (prefix_route)
addrconf_prefix_route(&addr, 64, idev->dev, addrconf_prefix_route(&addr, 64, 0, idev->dev,
0, 0, GFP_KERNEL); 0, 0, GFP_KERNEL);
break; break;
case IN6_ADDR_GEN_MODE_EUI64: case IN6_ADDR_GEN_MODE_EUI64:
...@@ -3255,7 +3260,7 @@ static void addrconf_addr_gen(struct inet6_dev *idev, bool prefix_route) ...@@ -3255,7 +3260,7 @@ static void addrconf_addr_gen(struct inet6_dev *idev, bool prefix_route)
if (ipv6_generate_eui64(addr.s6_addr + 8, idev->dev) == 0) if (ipv6_generate_eui64(addr.s6_addr + 8, idev->dev) == 0)
addrconf_add_linklocal(idev, &addr, 0); addrconf_add_linklocal(idev, &addr, 0);
else if (prefix_route) else if (prefix_route)
addrconf_prefix_route(&addr, 64, idev->dev, addrconf_prefix_route(&addr, 64, 0, idev->dev,
0, 0, GFP_KERNEL); 0, 0, GFP_KERNEL);
break; break;
case IN6_ADDR_GEN_MODE_NONE: case IN6_ADDR_GEN_MODE_NONE:
...@@ -3375,7 +3380,8 @@ static int fixup_permanent_addr(struct net *net, ...@@ -3375,7 +3380,8 @@ static int fixup_permanent_addr(struct net *net,
if (!(ifp->flags & IFA_F_NOPREFIXROUTE)) { if (!(ifp->flags & IFA_F_NOPREFIXROUTE)) {
addrconf_prefix_route(&ifp->addr, ifp->prefix_len, addrconf_prefix_route(&ifp->addr, ifp->prefix_len,
idev->dev, 0, 0, GFP_ATOMIC); ifp->rt_priority, idev->dev, 0, 0,
GFP_ATOMIC);
} }
if (ifp->state == INET6_IFADDR_STATE_PREDAD) if (ifp->state == INET6_IFADDR_STATE_PREDAD)
...@@ -4495,6 +4501,7 @@ static const struct nla_policy ifa_ipv6_policy[IFA_MAX+1] = { ...@@ -4495,6 +4501,7 @@ static const struct nla_policy ifa_ipv6_policy[IFA_MAX+1] = {
[IFA_LOCAL] = { .len = sizeof(struct in6_addr) }, [IFA_LOCAL] = { .len = sizeof(struct in6_addr) },
[IFA_CACHEINFO] = { .len = sizeof(struct ifa_cacheinfo) }, [IFA_CACHEINFO] = { .len = sizeof(struct ifa_cacheinfo) },
[IFA_FLAGS] = { .len = sizeof(u32) }, [IFA_FLAGS] = { .len = sizeof(u32) },
[IFA_RT_PRIORITY] = { .len = sizeof(u32) },
}; };
static int static int
...@@ -4527,6 +4534,37 @@ inet6_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -4527,6 +4534,37 @@ inet6_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh,
ifm->ifa_prefixlen); ifm->ifa_prefixlen);
} }
static int modify_prefix_route(struct inet6_ifaddr *ifp,
unsigned long expires, u32 flags)
{
struct fib6_info *f6i;
f6i = addrconf_get_prefix_route(&ifp->addr,
ifp->prefix_len,
ifp->idev->dev,
0, RTF_GATEWAY | RTF_DEFAULT);
if (!f6i)
return -ENOENT;
if (f6i->fib6_metric != ifp->rt_priority) {
/* add new one */
addrconf_prefix_route(&ifp->addr, ifp->prefix_len,
ifp->rt_priority, ifp->idev->dev,
expires, flags, GFP_KERNEL);
/* delete old one */
ip6_del_rt(dev_net(ifp->idev->dev), f6i);
} else {
if (!expires)
fib6_clean_expires(f6i);
else
fib6_set_expires(f6i, expires);
fib6_info_release(f6i);
}
return 0;
}
static int inet6_addr_modify(struct inet6_ifaddr *ifp, struct ifa6_config *cfg) static int inet6_addr_modify(struct inet6_ifaddr *ifp, struct ifa6_config *cfg)
{ {
u32 flags; u32 flags;
...@@ -4577,14 +4615,25 @@ static int inet6_addr_modify(struct inet6_ifaddr *ifp, struct ifa6_config *cfg) ...@@ -4577,14 +4615,25 @@ static int inet6_addr_modify(struct inet6_ifaddr *ifp, struct ifa6_config *cfg)
ifp->valid_lft = cfg->valid_lft; ifp->valid_lft = cfg->valid_lft;
ifp->prefered_lft = cfg->preferred_lft; ifp->prefered_lft = cfg->preferred_lft;
if (cfg->rt_priority && cfg->rt_priority != ifp->rt_priority)
ifp->rt_priority = cfg->rt_priority;
spin_unlock_bh(&ifp->lock); spin_unlock_bh(&ifp->lock);
if (!(ifp->flags&IFA_F_TENTATIVE)) if (!(ifp->flags&IFA_F_TENTATIVE))
ipv6_ifa_notify(0, ifp); ipv6_ifa_notify(0, ifp);
if (!(cfg->ifa_flags & IFA_F_NOPREFIXROUTE)) { if (!(cfg->ifa_flags & IFA_F_NOPREFIXROUTE)) {
addrconf_prefix_route(&ifp->addr, ifp->prefix_len, int rc = -ENOENT;
ifp->idev->dev, expires, flags,
GFP_KERNEL); if (had_prefixroute)
rc = modify_prefix_route(ifp, expires, flags);
/* prefix route could have been deleted; if so restore it */
if (rc == -ENOENT) {
addrconf_prefix_route(&ifp->addr, ifp->prefix_len,
ifp->rt_priority, ifp->idev->dev,
expires, flags, GFP_KERNEL);
}
} else if (had_prefixroute) { } else if (had_prefixroute) {
enum cleanup_prefix_rt_t action; enum cleanup_prefix_rt_t action;
unsigned long rt_expires; unsigned long rt_expires;
...@@ -4643,6 +4692,9 @@ inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -4643,6 +4692,9 @@ inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh,
cfg.peer_pfx = peer_pfx; cfg.peer_pfx = peer_pfx;
cfg.plen = ifm->ifa_prefixlen; cfg.plen = ifm->ifa_prefixlen;
if (tb[IFA_RT_PRIORITY])
cfg.rt_priority = nla_get_u32(tb[IFA_RT_PRIORITY]);
cfg.valid_lft = INFINITY_LIFE_TIME; cfg.valid_lft = INFINITY_LIFE_TIME;
cfg.preferred_lft = INFINITY_LIFE_TIME; cfg.preferred_lft = INFINITY_LIFE_TIME;
...@@ -4745,7 +4797,8 @@ static inline int inet6_ifaddr_msgsize(void) ...@@ -4745,7 +4797,8 @@ static inline int inet6_ifaddr_msgsize(void)
+ nla_total_size(16) /* IFA_LOCAL */ + nla_total_size(16) /* IFA_LOCAL */
+ nla_total_size(16) /* IFA_ADDRESS */ + nla_total_size(16) /* IFA_ADDRESS */
+ nla_total_size(sizeof(struct ifa_cacheinfo)) + nla_total_size(sizeof(struct ifa_cacheinfo))
+ nla_total_size(4) /* IFA_FLAGS */; + nla_total_size(4) /* IFA_FLAGS */
+ nla_total_size(4) /* IFA_RT_PRIORITY */;
} }
static int inet6_fill_ifaddr(struct sk_buff *skb, struct inet6_ifaddr *ifa, static int inet6_fill_ifaddr(struct sk_buff *skb, struct inet6_ifaddr *ifa,
...@@ -4791,6 +4844,10 @@ static int inet6_fill_ifaddr(struct sk_buff *skb, struct inet6_ifaddr *ifa, ...@@ -4791,6 +4844,10 @@ static int inet6_fill_ifaddr(struct sk_buff *skb, struct inet6_ifaddr *ifa,
if (nla_put_in6_addr(skb, IFA_ADDRESS, &ifa->addr) < 0) if (nla_put_in6_addr(skb, IFA_ADDRESS, &ifa->addr) < 0)
goto error; goto error;
if (ifa->rt_priority &&
nla_put_u32(skb, IFA_RT_PRIORITY, ifa->rt_priority))
goto error;
if (put_cacheinfo(skb, ifa->cstamp, ifa->tstamp, preferred, valid) < 0) if (put_cacheinfo(skb, ifa->cstamp, ifa->tstamp, preferred, valid) < 0)
goto error; goto error;
...@@ -5635,7 +5692,7 @@ static void __ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp) ...@@ -5635,7 +5692,7 @@ static void __ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp)
if (ifp->idev->cnf.forwarding) if (ifp->idev->cnf.forwarding)
addrconf_join_anycast(ifp); addrconf_join_anycast(ifp);
if (!ipv6_addr_any(&ifp->peer_addr)) if (!ipv6_addr_any(&ifp->peer_addr))
addrconf_prefix_route(&ifp->peer_addr, 128, addrconf_prefix_route(&ifp->peer_addr, 128, 0,
ifp->idev->dev, 0, 0, ifp->idev->dev, 0, 0,
GFP_ATOMIC); GFP_ATOMIC);
break; break;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment