Skip to content

Commit 8308f3f

Browse files
dsaherndavem330
authored andcommitted
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: David Ahern <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent af4d768 commit 8308f3f

File tree

3 files changed

+77
-18
lines changed

3 files changed

+77
-18
lines changed

include/net/addrconf.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ struct ifa6_config {
6565

6666
const struct in6_addr *peer_pfx;
6767

68+
u32 rt_priority;
6869
u32 ifa_flags;
6970
u32 preferred_lft;
7071
u32 valid_lft;

include/net/if_inet6.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ enum {
4242
struct inet6_ifaddr {
4343
struct in6_addr addr;
4444
__u32 prefix_len;
45+
__u32 rt_priority;
4546

4647
/* In seconds, relative to tstamp. Expiry is at tstamp + HZ * lft. */
4748
__u32 valid_lft;

net/ipv6/addrconf.c

Lines changed: 75 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1056,6 +1056,7 @@ ipv6_add_addr(struct inet6_dev *idev, struct ifa6_config *cfg,
10561056
INIT_HLIST_NODE(&ifa->addr_lst);
10571057
ifa->scope = cfg->scope;
10581058
ifa->prefix_len = cfg->plen;
1059+
ifa->rt_priority = cfg->rt_priority;
10591060
ifa->flags = cfg->ifa_flags;
10601061
/* No need to add the TENTATIVE flag for addresses with NODAD */
10611062
if (!(cfg->ifa_flags & IFA_F_NODAD))
@@ -1356,6 +1357,7 @@ static int ipv6_create_tempaddr(struct inet6_ifaddr *ifp,
13561357

13571358
cfg.pfx = &addr;
13581359
cfg.scope = ipv6_addr_scope(cfg.pfx);
1360+
cfg.rt_priority = 0;
13591361

13601362
ift = ipv6_add_addr(idev, &cfg, block, NULL);
13611363
if (IS_ERR(ift)) {
@@ -2314,12 +2316,13 @@ static void ipv6_try_regen_rndid(struct inet6_dev *idev, struct in6_addr *tmpad
23142316
*/
23152317

23162318
static void
2317-
addrconf_prefix_route(struct in6_addr *pfx, int plen, struct net_device *dev,
2318-
unsigned long expires, u32 flags, gfp_t gfp_flags)
2319+
addrconf_prefix_route(struct in6_addr *pfx, int plen, u32 metric,
2320+
struct net_device *dev, unsigned long expires,
2321+
u32 flags, gfp_t gfp_flags)
23192322
{
23202323
struct fib6_config cfg = {
23212324
.fc_table = l3mdev_fib_table(dev) ? : RT6_TABLE_PREFIX,
2322-
.fc_metric = IP6_RT_PRIO_ADDRCONF,
2325+
.fc_metric = metric ? : IP6_RT_PRIO_ADDRCONF,
23232326
.fc_ifindex = dev->ifindex,
23242327
.fc_expires = expires,
23252328
.fc_dst_len = plen,
@@ -2683,7 +2686,8 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao)
26832686
expires = jiffies_to_clock_t(rt_expires);
26842687
}
26852688
addrconf_prefix_route(&pinfo->prefix, pinfo->prefix_len,
2686-
dev, expires, flags, GFP_ATOMIC);
2689+
0, dev, expires, flags,
2690+
GFP_ATOMIC);
26872691
}
26882692
fib6_info_release(rt);
26892693
}
@@ -2891,8 +2895,9 @@ static int inet6_addr_add(struct net *net, int ifindex,
28912895
ifp = ipv6_add_addr(idev, cfg, true, extack);
28922896
if (!IS_ERR(ifp)) {
28932897
if (!(cfg->ifa_flags & IFA_F_NOPREFIXROUTE)) {
2894-
addrconf_prefix_route(&ifp->addr, ifp->prefix_len, dev,
2895-
expires, flags, GFP_KERNEL);
2898+
addrconf_prefix_route(&ifp->addr, ifp->prefix_len,
2899+
ifp->rt_priority, dev, expires,
2900+
flags, GFP_KERNEL);
28962901
}
28972902

28982903
/* Send a netlink notification if DAD is enabled and
@@ -3056,7 +3061,7 @@ static void sit_add_v4_addrs(struct inet6_dev *idev)
30563061

30573062
if (addr.s6_addr32[3]) {
30583063
add_addr(idev, &addr, plen, scope);
3059-
addrconf_prefix_route(&addr, plen, idev->dev, 0, pflags,
3064+
addrconf_prefix_route(&addr, plen, 0, idev->dev, 0, pflags,
30603065
GFP_ATOMIC);
30613066
return;
30623067
}
@@ -3081,8 +3086,8 @@ static void sit_add_v4_addrs(struct inet6_dev *idev)
30813086
}
30823087

30833088
add_addr(idev, &addr, plen, flag);
3084-
addrconf_prefix_route(&addr, plen, idev->dev, 0,
3085-
pflags, GFP_ATOMIC);
3089+
addrconf_prefix_route(&addr, plen, 0, idev->dev,
3090+
0, pflags, GFP_ATOMIC);
30863091
}
30873092
}
30883093
}
@@ -3128,7 +3133,7 @@ void addrconf_add_linklocal(struct inet6_dev *idev,
31283133

31293134
ifp = ipv6_add_addr(idev, &cfg, true, NULL);
31303135
if (!IS_ERR(ifp)) {
3131-
addrconf_prefix_route(&ifp->addr, ifp->prefix_len, idev->dev,
3136+
addrconf_prefix_route(&ifp->addr, ifp->prefix_len, 0, idev->dev,
31323137
0, 0, GFP_ATOMIC);
31333138
addrconf_dad_start(ifp);
31343139
in6_ifa_put(ifp);
@@ -3244,7 +3249,7 @@ static void addrconf_addr_gen(struct inet6_dev *idev, bool prefix_route)
32443249
addrconf_add_linklocal(idev, &addr,
32453250
IFA_F_STABLE_PRIVACY);
32463251
else if (prefix_route)
3247-
addrconf_prefix_route(&addr, 64, idev->dev,
3252+
addrconf_prefix_route(&addr, 64, 0, idev->dev,
32483253
0, 0, GFP_KERNEL);
32493254
break;
32503255
case IN6_ADDR_GEN_MODE_EUI64:
@@ -3255,7 +3260,7 @@ static void addrconf_addr_gen(struct inet6_dev *idev, bool prefix_route)
32553260
if (ipv6_generate_eui64(addr.s6_addr + 8, idev->dev) == 0)
32563261
addrconf_add_linklocal(idev, &addr, 0);
32573262
else if (prefix_route)
3258-
addrconf_prefix_route(&addr, 64, idev->dev,
3263+
addrconf_prefix_route(&addr, 64, 0, idev->dev,
32593264
0, 0, GFP_KERNEL);
32603265
break;
32613266
case IN6_ADDR_GEN_MODE_NONE:
@@ -3375,7 +3380,8 @@ static int fixup_permanent_addr(struct net *net,
33753380

33763381
if (!(ifp->flags & IFA_F_NOPREFIXROUTE)) {
33773382
addrconf_prefix_route(&ifp->addr, ifp->prefix_len,
3378-
idev->dev, 0, 0, GFP_ATOMIC);
3383+
ifp->rt_priority, idev->dev, 0, 0,
3384+
GFP_ATOMIC);
33793385
}
33803386

33813387
if (ifp->state == INET6_IFADDR_STATE_PREDAD)
@@ -4495,6 +4501,7 @@ static const struct nla_policy ifa_ipv6_policy[IFA_MAX+1] = {
44954501
[IFA_LOCAL] = { .len = sizeof(struct in6_addr) },
44964502
[IFA_CACHEINFO] = { .len = sizeof(struct ifa_cacheinfo) },
44974503
[IFA_FLAGS] = { .len = sizeof(u32) },
4504+
[IFA_RT_PRIORITY] = { .len = sizeof(u32) },
44984505
};
44994506

45004507
static int
@@ -4527,6 +4534,37 @@ inet6_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh,
45274534
ifm->ifa_prefixlen);
45284535
}
45294536

4537+
static int modify_prefix_route(struct inet6_ifaddr *ifp,
4538+
unsigned long expires, u32 flags)
4539+
{
4540+
struct fib6_info *f6i;
4541+
4542+
f6i = addrconf_get_prefix_route(&ifp->addr,
4543+
ifp->prefix_len,
4544+
ifp->idev->dev,
4545+
0, RTF_GATEWAY | RTF_DEFAULT);
4546+
if (!f6i)
4547+
return -ENOENT;
4548+
4549+
if (f6i->fib6_metric != ifp->rt_priority) {
4550+
/* add new one */
4551+
addrconf_prefix_route(&ifp->addr, ifp->prefix_len,
4552+
ifp->rt_priority, ifp->idev->dev,
4553+
expires, flags, GFP_KERNEL);
4554+
/* delete old one */
4555+
ip6_del_rt(dev_net(ifp->idev->dev), f6i);
4556+
} else {
4557+
if (!expires)
4558+
fib6_clean_expires(f6i);
4559+
else
4560+
fib6_set_expires(f6i, expires);
4561+
4562+
fib6_info_release(f6i);
4563+
}
4564+
4565+
return 0;
4566+
}
4567+
45304568
static int inet6_addr_modify(struct inet6_ifaddr *ifp, struct ifa6_config *cfg)
45314569
{
45324570
u32 flags;
@@ -4577,14 +4615,25 @@ static int inet6_addr_modify(struct inet6_ifaddr *ifp, struct ifa6_config *cfg)
45774615
ifp->valid_lft = cfg->valid_lft;
45784616
ifp->prefered_lft = cfg->preferred_lft;
45794617

4618+
if (cfg->rt_priority && cfg->rt_priority != ifp->rt_priority)
4619+
ifp->rt_priority = cfg->rt_priority;
4620+
45804621
spin_unlock_bh(&ifp->lock);
45814622
if (!(ifp->flags&IFA_F_TENTATIVE))
45824623
ipv6_ifa_notify(0, ifp);
45834624

45844625
if (!(cfg->ifa_flags & IFA_F_NOPREFIXROUTE)) {
4585-
addrconf_prefix_route(&ifp->addr, ifp->prefix_len,
4586-
ifp->idev->dev, expires, flags,
4587-
GFP_KERNEL);
4626+
int rc = -ENOENT;
4627+
4628+
if (had_prefixroute)
4629+
rc = modify_prefix_route(ifp, expires, flags);
4630+
4631+
/* prefix route could have been deleted; if so restore it */
4632+
if (rc == -ENOENT) {
4633+
addrconf_prefix_route(&ifp->addr, ifp->prefix_len,
4634+
ifp->rt_priority, ifp->idev->dev,
4635+
expires, flags, GFP_KERNEL);
4636+
}
45884637
} else if (had_prefixroute) {
45894638
enum cleanup_prefix_rt_t action;
45904639
unsigned long rt_expires;
@@ -4643,6 +4692,9 @@ inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh,
46434692

46444693
cfg.peer_pfx = peer_pfx;
46454694
cfg.plen = ifm->ifa_prefixlen;
4695+
if (tb[IFA_RT_PRIORITY])
4696+
cfg.rt_priority = nla_get_u32(tb[IFA_RT_PRIORITY]);
4697+
46464698
cfg.valid_lft = INFINITY_LIFE_TIME;
46474699
cfg.preferred_lft = INFINITY_LIFE_TIME;
46484700

@@ -4745,7 +4797,8 @@ static inline int inet6_ifaddr_msgsize(void)
47454797
+ nla_total_size(16) /* IFA_LOCAL */
47464798
+ nla_total_size(16) /* IFA_ADDRESS */
47474799
+ nla_total_size(sizeof(struct ifa_cacheinfo))
4748-
+ nla_total_size(4) /* IFA_FLAGS */;
4800+
+ nla_total_size(4) /* IFA_FLAGS */
4801+
+ nla_total_size(4) /* IFA_RT_PRIORITY */;
47494802
}
47504803

47514804
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,
47914844
if (nla_put_in6_addr(skb, IFA_ADDRESS, &ifa->addr) < 0)
47924845
goto error;
47934846

4847+
if (ifa->rt_priority &&
4848+
nla_put_u32(skb, IFA_RT_PRIORITY, ifa->rt_priority))
4849+
goto error;
4850+
47944851
if (put_cacheinfo(skb, ifa->cstamp, ifa->tstamp, preferred, valid) < 0)
47954852
goto error;
47964853

@@ -5635,7 +5692,7 @@ static void __ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp)
56355692
if (ifp->idev->cnf.forwarding)
56365693
addrconf_join_anycast(ifp);
56375694
if (!ipv6_addr_any(&ifp->peer_addr))
5638-
addrconf_prefix_route(&ifp->peer_addr, 128,
5695+
addrconf_prefix_route(&ifp->peer_addr, 128, 0,
56395696
ifp->idev->dev, 0, 0,
56405697
GFP_ATOMIC);
56415698
break;

0 commit comments

Comments
 (0)