Commit 4d0312e0 authored by David S. Miller's avatar David S. Miller

Merge branch 'net-Add-address-attribute-to-control-metric-of-prefix-route'

David Ahern says:

====================
net: Add address attribute to control metric of prefix route

For use cases such as VRR (Virtual Router Redundancy) interface managers
want efficient control over the order of prefix routes when multiple
interfaces have addresses with overlapping/duplicate subnets.

Currently, if two interfaces have addresses in the same subnet, the order
of the prefix route entries is determined by the order in which the
addresses are assigned or the links brought up. Any actions like cycling
an interface up and down changes that order. This set adds a new attribute
for addresses to allow a user to specify the metric of the prefix route
associated with an address giving interface managers better and more
efficient control of the order of prefix routes.

Patches 1-3 refactor IPv6 address add functions to pass an ifa6_config
struct. The functions currently have a long list of arguments and adding
the metric just makes it worse. Because of the overall diff size in
moving the arguments to a struct, the change is done in stages to make
it easier to review starting with the bottom function and pushing the
struct up to callers in each successive patch.

Patch 4 introduces the new attribute.

Patches 5 and 6 add support for the new attribute to IPv4 and IPv6
addresses.

Patch 7 adds a set of test cases.

Patch 8 adds support to iproute2

Changes since RFC
- collapsed patches 1 and 3 into patch 2
- simplified stack variables in fib_modify_prefix_metric in patch 5
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 49fb6fe3 d69faad7
......@@ -139,6 +139,7 @@ struct in_ifaddr {
__be32 ifa_local;
__be32 ifa_address;
__be32 ifa_mask;
__u32 ifa_rt_priority;
__be32 ifa_broadcast;
unsigned char ifa_scope;
unsigned char ifa_prefixlen;
......
......@@ -59,6 +59,19 @@ struct in6_validator_info {
struct netlink_ext_ack *extack;
};
struct ifa6_config {
const struct in6_addr *pfx;
unsigned int plen;
const struct in6_addr *peer_pfx;
u32 rt_priority;
u32 ifa_flags;
u32 preferred_lft;
u32 valid_lft;
u16 scope;
};
int addrconf_init(void);
void addrconf_cleanup(void);
......
......@@ -42,6 +42,7 @@ enum {
struct inet6_ifaddr {
struct in6_addr addr;
__u32 prefix_len;
__u32 rt_priority;
/* In seconds, relative to tstamp. Expiry is at tstamp + HZ * lft. */
__u32 valid_lft;
......
......@@ -225,6 +225,7 @@ struct rtable *rt_dst_alloc(struct net_device *dev,
struct in_ifaddr;
void fib_add_ifaddr(struct in_ifaddr *);
void fib_del_ifaddr(struct in_ifaddr *, struct in_ifaddr *);
void fib_modify_prefix_metric(struct in_ifaddr *ifa, u32 new_metric);
void rt_add_uncached_list(struct rtable *rt);
void rt_del_uncached_list(struct rtable *rt);
......
......@@ -33,6 +33,7 @@ enum {
IFA_CACHEINFO,
IFA_MULTICAST,
IFA_FLAGS,
IFA_RT_PRIORITY, /* u32, priority/metric for prefix route */
__IFA_MAX,
};
......
......@@ -99,6 +99,7 @@ static const struct nla_policy ifa_ipv4_policy[IFA_MAX+1] = {
[IFA_LABEL] = { .type = NLA_STRING, .len = IFNAMSIZ - 1 },
[IFA_CACHEINFO] = { .len = sizeof(struct ifa_cacheinfo) },
[IFA_FLAGS] = { .type = NLA_U32 },
[IFA_RT_PRIORITY] = { .type = NLA_U32 },
};
#define IN4_ADDR_HSIZE_SHIFT 8
......@@ -835,6 +836,9 @@ static struct in_ifaddr *rtm_to_ifaddr(struct net *net, struct nlmsghdr *nlh,
else
memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
if (tb[IFA_RT_PRIORITY])
ifa->ifa_rt_priority = nla_get_u32(tb[IFA_RT_PRIORITY]);
if (tb[IFA_CACHEINFO]) {
struct ifa_cacheinfo *ci;
......@@ -906,12 +910,20 @@ static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh,
return __inet_insert_ifa(ifa, nlh, NETLINK_CB(skb).portid,
extack);
} else {
u32 new_metric = ifa->ifa_rt_priority;
inet_free_ifa(ifa);
if (nlh->nlmsg_flags & NLM_F_EXCL ||
!(nlh->nlmsg_flags & NLM_F_REPLACE))
return -EEXIST;
ifa = ifa_existing;
if (ifa->ifa_rt_priority != new_metric) {
fib_modify_prefix_metric(ifa, new_metric);
ifa->ifa_rt_priority = new_metric;
}
set_ifa_lifetime(ifa, valid_lft, prefered_lft);
cancel_delayed_work(&check_lifetime_work);
queue_delayed_work(system_power_efficient_wq,
......@@ -1549,6 +1561,7 @@ static size_t inet_nlmsg_size(void)
+ nla_total_size(4) /* IFA_BROADCAST */
+ nla_total_size(IFNAMSIZ) /* IFA_LABEL */
+ nla_total_size(4) /* IFA_FLAGS */
+ nla_total_size(4) /* IFA_RT_PRIORITY */
+ nla_total_size(sizeof(struct ifa_cacheinfo)); /* IFA_CACHEINFO */
}
......@@ -1618,6 +1631,8 @@ static int inet_fill_ifaddr(struct sk_buff *skb, struct in_ifaddr *ifa,
(ifa->ifa_label[0] &&
nla_put_string(skb, IFA_LABEL, ifa->ifa_label)) ||
nla_put_u32(skb, IFA_FLAGS, ifa->ifa_flags) ||
(ifa->ifa_rt_priority &&
nla_put_u32(skb, IFA_RT_PRIORITY, ifa->ifa_rt_priority)) ||
put_cacheinfo(skb, ifa->ifa_cstamp, ifa->ifa_tstamp,
preferred, valid))
goto nla_put_failure;
......
......@@ -847,7 +847,8 @@ static int inet_dump_fib(struct sk_buff *skb, struct netlink_callback *cb)
* to fib engine. It is legal, because all events occur
* only when netlink is already locked.
*/
static void fib_magic(int cmd, int type, __be32 dst, int dst_len, struct in_ifaddr *ifa)
static void fib_magic(int cmd, int type, __be32 dst, int dst_len,
struct in_ifaddr *ifa, u32 rt_priority)
{
struct net *net = dev_net(ifa->ifa_dev->dev);
u32 tb_id = l3mdev_fib_table(ifa->ifa_dev->dev);
......@@ -857,6 +858,7 @@ static void fib_magic(int cmd, int type, __be32 dst, int dst_len, struct in_ifad
.fc_type = type,
.fc_dst = dst,
.fc_dst_len = dst_len,
.fc_priority = rt_priority,
.fc_prefsrc = ifa->ifa_local,
.fc_oif = ifa->ifa_dev->dev->ifindex,
.fc_nlflags = NLM_F_CREATE | NLM_F_APPEND,
......@@ -902,31 +904,57 @@ void fib_add_ifaddr(struct in_ifaddr *ifa)
}
}
fib_magic(RTM_NEWROUTE, RTN_LOCAL, addr, 32, prim);
fib_magic(RTM_NEWROUTE, RTN_LOCAL, addr, 32, prim, 0);
if (!(dev->flags & IFF_UP))
return;
/* Add broadcast address, if it is explicitly assigned. */
if (ifa->ifa_broadcast && ifa->ifa_broadcast != htonl(0xFFFFFFFF))
fib_magic(RTM_NEWROUTE, RTN_BROADCAST, ifa->ifa_broadcast, 32, prim);
fib_magic(RTM_NEWROUTE, RTN_BROADCAST, ifa->ifa_broadcast, 32,
prim, 0);
if (!ipv4_is_zeronet(prefix) && !(ifa->ifa_flags & IFA_F_SECONDARY) &&
(prefix != addr || ifa->ifa_prefixlen < 32)) {
if (!(ifa->ifa_flags & IFA_F_NOPREFIXROUTE))
fib_magic(RTM_NEWROUTE,
dev->flags & IFF_LOOPBACK ? RTN_LOCAL : RTN_UNICAST,
prefix, ifa->ifa_prefixlen, prim);
prefix, ifa->ifa_prefixlen, prim,
ifa->ifa_rt_priority);
/* Add network specific broadcasts, when it takes a sense */
if (ifa->ifa_prefixlen < 31) {
fib_magic(RTM_NEWROUTE, RTN_BROADCAST, prefix, 32, prim);
fib_magic(RTM_NEWROUTE, RTN_BROADCAST, prefix, 32,
prim, 0);
fib_magic(RTM_NEWROUTE, RTN_BROADCAST, prefix | ~mask,
32, prim);
32, prim, 0);
}
}
}
void fib_modify_prefix_metric(struct in_ifaddr *ifa, u32 new_metric)
{
__be32 prefix = ifa->ifa_address & ifa->ifa_mask;
struct in_device *in_dev = ifa->ifa_dev;
struct net_device *dev = in_dev->dev;
if (!(dev->flags & IFF_UP) ||
ifa->ifa_flags & (IFA_F_SECONDARY | IFA_F_NOPREFIXROUTE) ||
ipv4_is_zeronet(prefix) ||
prefix == ifa->ifa_local || ifa->ifa_prefixlen == 32)
return;
/* add the new */
fib_magic(RTM_NEWROUTE,
dev->flags & IFF_LOOPBACK ? RTN_LOCAL : RTN_UNICAST,
prefix, ifa->ifa_prefixlen, ifa, new_metric);
/* delete the old */
fib_magic(RTM_DELROUTE,
dev->flags & IFF_LOOPBACK ? RTN_LOCAL : RTN_UNICAST,
prefix, ifa->ifa_prefixlen, ifa, ifa->ifa_rt_priority);
}
/* Delete primary or secondary address.
* Optionally, on secondary address promotion consider the addresses
* from subnet iprim as deleted, even if they are in device list.
......@@ -968,7 +996,7 @@ void fib_del_ifaddr(struct in_ifaddr *ifa, struct in_ifaddr *iprim)
if (!(ifa->ifa_flags & IFA_F_NOPREFIXROUTE))
fib_magic(RTM_DELROUTE,
dev->flags & IFF_LOOPBACK ? RTN_LOCAL : RTN_UNICAST,
any, ifa->ifa_prefixlen, prim);
any, ifa->ifa_prefixlen, prim, 0);
subnet = 1;
}
......@@ -1052,17 +1080,20 @@ void fib_del_ifaddr(struct in_ifaddr *ifa, struct in_ifaddr *iprim)
no_promotions:
if (!(ok & BRD_OK))
fib_magic(RTM_DELROUTE, RTN_BROADCAST, ifa->ifa_broadcast, 32, prim);
fib_magic(RTM_DELROUTE, RTN_BROADCAST, ifa->ifa_broadcast, 32,
prim, 0);
if (subnet && ifa->ifa_prefixlen < 31) {
if (!(ok & BRD1_OK))
fib_magic(RTM_DELROUTE, RTN_BROADCAST, brd, 32, prim);
fib_magic(RTM_DELROUTE, RTN_BROADCAST, brd, 32,
prim, 0);
if (!(ok & BRD0_OK))
fib_magic(RTM_DELROUTE, RTN_BROADCAST, any, 32, prim);
fib_magic(RTM_DELROUTE, RTN_BROADCAST, any, 32,
prim, 0);
}
if (!(ok & LOCAL_OK)) {
unsigned int addr_type;
fib_magic(RTM_DELROUTE, RTN_LOCAL, ifa->ifa_local, 32, prim);
fib_magic(RTM_DELROUTE, RTN_LOCAL, ifa->ifa_local, 32, prim, 0);
/* Check, that this local address finally disappeared. */
addr_type = inet_addr_type_dev_table(dev_net(dev), dev,
......
......@@ -986,17 +986,15 @@ static int ipv6_add_addr_hash(struct net_device *dev, struct inet6_ifaddr *ifa)
/* On success it returns ifp with increased reference count */
static struct inet6_ifaddr *
ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr,
const struct in6_addr *peer_addr, int pfxlen,
int scope, u32 flags, u32 valid_lft, u32 prefered_lft,
ipv6_add_addr(struct inet6_dev *idev, struct ifa6_config *cfg,
bool can_block, struct netlink_ext_ack *extack)
{
gfp_t gfp_flags = can_block ? GFP_KERNEL : GFP_ATOMIC;
int addr_type = ipv6_addr_type(cfg->pfx);
struct net *net = dev_net(idev->dev);
struct inet6_ifaddr *ifa = NULL;
struct fib6_info *f6i = NULL;
int err = 0;
int addr_type = ipv6_addr_type(addr);
if (addr_type == IPV6_ADDR_ANY ||
addr_type & IPV6_ADDR_MULTICAST ||
......@@ -1019,7 +1017,7 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr,
*/
if (can_block) {
struct in6_validator_info i6vi = {
.i6vi_addr = *addr,
.i6vi_addr = *cfg->pfx,
.i6vi_dev = idev,
.extack = extack,
};
......@@ -1036,7 +1034,7 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr,
goto out;
}
f6i = addrconf_f6i_alloc(net, idev, addr, false, gfp_flags);
f6i = addrconf_f6i_alloc(net, idev, cfg->pfx, false, gfp_flags);
if (IS_ERR(f6i)) {
err = PTR_ERR(f6i);
f6i = NULL;
......@@ -1049,21 +1047,22 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr,
neigh_parms_data_state_setall(idev->nd_parms);
ifa->addr = *addr;
if (peer_addr)
ifa->peer_addr = *peer_addr;
ifa->addr = *cfg->pfx;
if (cfg->peer_pfx)
ifa->peer_addr = *cfg->peer_pfx;
spin_lock_init(&ifa->lock);
INIT_DELAYED_WORK(&ifa->dad_work, addrconf_dad_work);
INIT_HLIST_NODE(&ifa->addr_lst);
ifa->scope = scope;
ifa->prefix_len = pfxlen;
ifa->flags = flags;
ifa->scope = cfg->scope;
ifa->prefix_len = cfg->plen;
ifa->rt_priority = cfg->rt_priority;
ifa->flags = cfg->ifa_flags;
/* No need to add the TENTATIVE flag for addresses with NODAD */
if (!(flags & IFA_F_NODAD))
if (!(cfg->ifa_flags & IFA_F_NODAD))
ifa->flags |= IFA_F_TENTATIVE;
ifa->valid_lft = valid_lft;
ifa->prefered_lft = prefered_lft;
ifa->valid_lft = cfg->valid_lft;
ifa->prefered_lft = cfg->preferred_lft;
ifa->cstamp = ifa->tstamp = jiffies;
ifa->tokenized = false;
......@@ -1260,11 +1259,10 @@ static int ipv6_create_tempaddr(struct inet6_ifaddr *ifp,
{
struct inet6_dev *idev = ifp->idev;
struct in6_addr addr, *tmpaddr;
unsigned long tmp_prefered_lft, tmp_valid_lft, tmp_tstamp, age;
unsigned long tmp_tstamp, age;
unsigned long regen_advance;
int tmp_plen;
struct ifa6_config cfg;
int ret = 0;
u32 addr_flags;
unsigned long now = jiffies;
long max_desync_factor;
s32 cnf_temp_preferred_lft;
......@@ -1326,13 +1324,12 @@ static int ipv6_create_tempaddr(struct inet6_ifaddr *ifp,
}
}
tmp_valid_lft = min_t(__u32,
ifp->valid_lft,
cfg.valid_lft = min_t(__u32, ifp->valid_lft,
idev->cnf.temp_valid_lft + age);
tmp_prefered_lft = cnf_temp_preferred_lft + age -
idev->desync_factor;
tmp_prefered_lft = min_t(__u32, ifp->prefered_lft, tmp_prefered_lft);
tmp_plen = ifp->prefix_len;
cfg.preferred_lft = cnf_temp_preferred_lft + age - idev->desync_factor;
cfg.preferred_lft = min_t(__u32, ifp->prefered_lft, cfg.preferred_lft);
cfg.plen = ifp->prefix_len;
tmp_tstamp = ifp->tstamp;
spin_unlock_bh(&ifp->lock);
......@@ -1346,21 +1343,23 @@ static int ipv6_create_tempaddr(struct inet6_ifaddr *ifp,
* temporary addresses being generated.
*/
age = (now - tmp_tstamp + ADDRCONF_TIMER_FUZZ_MINUS) / HZ;
if (tmp_prefered_lft <= regen_advance + age) {
if (cfg.preferred_lft <= regen_advance + age) {
in6_ifa_put(ifp);
in6_dev_put(idev);
ret = -1;
goto out;
}
addr_flags = IFA_F_TEMPORARY;
cfg.ifa_flags = IFA_F_TEMPORARY;
/* set in addrconf_prefix_rcv() */
if (ifp->flags & IFA_F_OPTIMISTIC)
addr_flags |= IFA_F_OPTIMISTIC;
cfg.ifa_flags |= IFA_F_OPTIMISTIC;
cfg.pfx = &addr;
cfg.scope = ipv6_addr_scope(cfg.pfx);
cfg.rt_priority = 0;
ift = ipv6_add_addr(idev, &addr, NULL, tmp_plen,
ipv6_addr_scope(&addr), addr_flags,
tmp_valid_lft, tmp_prefered_lft, block, NULL);
ift = ipv6_add_addr(idev, &cfg, block, NULL);
if (IS_ERR(ift)) {
in6_ifa_put(ifp);
in6_dev_put(idev);
......@@ -2031,13 +2030,17 @@ void addrconf_dad_failure(struct sk_buff *skb, struct inet6_ifaddr *ifp)
spin_lock_bh(&ifp->lock);
if (ifp->flags & IFA_F_STABLE_PRIVACY) {
int scope = ifp->scope;
u32 flags = ifp->flags;
struct in6_addr new_addr;
struct inet6_ifaddr *ifp2;
u32 valid_lft, preferred_lft;
int pfxlen = ifp->prefix_len;
int retries = ifp->stable_privacy_retry + 1;
struct ifa6_config cfg = {
.pfx = &new_addr,
.plen = ifp->prefix_len,
.ifa_flags = ifp->flags,
.valid_lft = ifp->valid_lft,
.preferred_lft = ifp->prefered_lft,
.scope = ifp->scope,
};
if (retries > net->ipv6.sysctl.idgen_retries) {
net_info_ratelimited("%s: privacy stable address generation failed because of DAD conflicts!\n",
......@@ -2050,9 +2053,6 @@ void addrconf_dad_failure(struct sk_buff *skb, struct inet6_ifaddr *ifp)
idev))
goto errdad;
valid_lft = ifp->valid_lft;
preferred_lft = ifp->prefered_lft;
spin_unlock_bh(&ifp->lock);
if (idev->cnf.max_addresses &&
......@@ -2063,9 +2063,7 @@ void addrconf_dad_failure(struct sk_buff *skb, struct inet6_ifaddr *ifp)
net_info_ratelimited("%s: generating new stable privacy address because of DAD conflict\n",
ifp->idev->dev->name);
ifp2 = ipv6_add_addr(idev, &new_addr, NULL, pfxlen,
scope, flags, valid_lft,
preferred_lft, false, NULL);
ifp2 = ipv6_add_addr(idev, &cfg, false, NULL);
if (IS_ERR(ifp2))
goto lock_errdad;
......@@ -2318,12 +2316,13 @@ static void ipv6_try_regen_rndid(struct inet6_dev *idev, struct in6_addr *tmpad
*/
static void
addrconf_prefix_route(struct in6_addr *pfx, int plen, struct net_device *dev,
unsigned long expires, u32 flags, gfp_t gfp_flags)
addrconf_prefix_route(struct in6_addr *pfx, int plen, u32 metric,
struct net_device *dev, unsigned long expires,
u32 flags, gfp_t gfp_flags)
{
struct fib6_config cfg = {
.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_expires = expires,
.fc_dst_len = plen,
......@@ -2507,12 +2506,20 @@ int addrconf_prefix_rcv_add_addr(struct net *net, struct net_device *dev,
if (!ifp && valid_lft) {
int max_addresses = in6_dev->cnf.max_addresses;
struct ifa6_config cfg = {
.pfx = addr,
.plen = pinfo->prefix_len,
.ifa_flags = addr_flags,
.valid_lft = valid_lft,
.preferred_lft = prefered_lft,
.scope = addr_type & IPV6_ADDR_SCOPE_MASK,
};
#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
if ((net->ipv6.devconf_all->optimistic_dad ||
in6_dev->cnf.optimistic_dad) &&
!net->ipv6.devconf_all->forwarding && sllao)
addr_flags |= IFA_F_OPTIMISTIC;
cfg.ifa_flags |= IFA_F_OPTIMISTIC;
#endif
/* Do not allow to create too much of autoconfigured
......@@ -2520,11 +2527,7 @@ int addrconf_prefix_rcv_add_addr(struct net *net, struct net_device *dev,
*/
if (!max_addresses ||
ipv6_count_addresses(in6_dev) < max_addresses)
ifp = ipv6_add_addr(in6_dev, addr, NULL,
pinfo->prefix_len,
addr_type&IPV6_ADDR_SCOPE_MASK,
addr_flags, valid_lft,
prefered_lft, false, NULL);
ifp = ipv6_add_addr(in6_dev, &cfg, false, NULL);
if (IS_ERR_OR_NULL(ifp))
return -1;
......@@ -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);
}
addrconf_prefix_route(&pinfo->prefix, pinfo->prefix_len,
dev, expires, flags, GFP_ATOMIC);
0, dev, expires, flags,
GFP_ATOMIC);
}
fib6_info_release(rt);
}
......@@ -2830,10 +2834,7 @@ static int ipv6_mc_config(struct sock *sk, bool join,
* Manual configuration of address on an interface
*/
static int inet6_addr_add(struct net *net, int ifindex,
const struct in6_addr *pfx,
const struct in6_addr *peer_pfx,
unsigned int plen, __u32 ifa_flags,
__u32 prefered_lft, __u32 valid_lft,
struct ifa6_config *cfg,
struct netlink_ext_ack *extack)
{
struct inet6_ifaddr *ifp;
......@@ -2841,19 +2842,18 @@ static int inet6_addr_add(struct net *net, int ifindex,
struct net_device *dev;
unsigned long timeout;
clock_t expires;
int scope;
u32 flags;
ASSERT_RTNL();
if (plen > 128)
if (cfg->plen > 128)
return -EINVAL;
/* check the lifetime */
if (!valid_lft || prefered_lft > valid_lft)
if (!cfg->valid_lft || cfg->preferred_lft > cfg->valid_lft)
return -EINVAL;
if (ifa_flags & IFA_F_MANAGETEMPADDR && plen != 64)
if (cfg->ifa_flags & IFA_F_MANAGETEMPADDR && cfg->plen != 64)
return -EINVAL;
dev = __dev_get_by_index(net, ifindex);
......@@ -2864,41 +2864,40 @@ static int inet6_addr_add(struct net *net, int ifindex,
if (IS_ERR(idev))
return PTR_ERR(idev);
if (ifa_flags & IFA_F_MCAUTOJOIN) {
if (cfg->ifa_flags & IFA_F_MCAUTOJOIN) {
int ret = ipv6_mc_config(net->ipv6.mc_autojoin_sk,
true, pfx, ifindex);
true, cfg->pfx, ifindex);
if (ret < 0)
return ret;
}
scope = ipv6_addr_scope(pfx);
cfg->scope = ipv6_addr_scope(cfg->pfx);
timeout = addrconf_timeout_fixup(valid_lft, HZ);
timeout = addrconf_timeout_fixup(cfg->valid_lft, HZ);
if (addrconf_finite_timeout(timeout)) {
expires = jiffies_to_clock_t(timeout * HZ);
valid_lft = timeout;
cfg->valid_lft = timeout;
flags = RTF_EXPIRES;
} else {
expires = 0;
flags = 0;
ifa_flags |= IFA_F_PERMANENT;
cfg->ifa_flags |= IFA_F_PERMANENT;
}
timeout = addrconf_timeout_fixup(prefered_lft, HZ);
timeout = addrconf_timeout_fixup(cfg->preferred_lft, HZ);
if (addrconf_finite_timeout(timeout)) {
if (timeout == 0)
ifa_flags |= IFA_F_DEPRECATED;
prefered_lft = timeout;
cfg->ifa_flags |= IFA_F_DEPRECATED;
cfg->preferred_lft = timeout;
}
ifp = ipv6_add_addr(idev, pfx, peer_pfx, plen, scope, ifa_flags,
valid_lft, prefered_lft, true, extack);
ifp = ipv6_add_addr(idev, cfg, true, extack);
if (!IS_ERR(ifp)) {
if (!(ifa_flags & IFA_F_NOPREFIXROUTE)) {
addrconf_prefix_route(&ifp->addr, ifp->prefix_len, dev,
expires, flags, GFP_KERNEL);
if (!(cfg->ifa_flags & IFA_F_NOPREFIXROUTE)) {
addrconf_prefix_route(&ifp->addr, ifp->prefix_len,
ifp->rt_priority, dev, expires,
flags, GFP_KERNEL);
}
/* Send a netlink notification if DAD is enabled and
......@@ -2912,15 +2911,15 @@ static int inet6_addr_add(struct net *net, int ifindex,
* manually configured addresses
*/
addrconf_dad_start(ifp);
if (ifa_flags & IFA_F_MANAGETEMPADDR)
manage_tempaddrs(idev, ifp, valid_lft, prefered_lft,
true, jiffies);
if (cfg->ifa_flags & IFA_F_MANAGETEMPADDR)
manage_tempaddrs(idev, ifp, cfg->valid_lft,
cfg->preferred_lft, true, jiffies);
in6_ifa_put(ifp);
addrconf_verify_rtnl();
return 0;
} else if (ifa_flags & IFA_F_MCAUTOJOIN) {
ipv6_mc_config(net->ipv6.mc_autojoin_sk,
false, pfx, ifindex);
} else if (cfg->ifa_flags & IFA_F_MCAUTOJOIN) {
ipv6_mc_config(net->ipv6.mc_autojoin_sk, false,
cfg->pfx, ifindex);
}
return PTR_ERR(ifp);
......@@ -2971,6 +2970,11 @@ static int inet6_addr_del(struct net *net, int ifindex, u32 ifa_flags,
int addrconf_add_ifaddr(struct net *net, void __user *arg)
{
struct ifa6_config cfg = {
.ifa_flags = IFA_F_PERMANENT,
.preferred_lft = INFINITY_LIFE_TIME,
.valid_lft = INFINITY_LIFE_TIME,
};
struct in6_ifreq ireq;
int err;
......@@ -2980,10 +2984,11 @@ int addrconf_add_ifaddr(struct net *net, void __user *arg)
if (copy_from_user(&ireq, arg, sizeof(struct in6_ifreq)))
return -EFAULT;
cfg.pfx = &ireq.ifr6_addr;
cfg.plen = ireq.ifr6_prefixlen;
rtnl_lock();
err = inet6_addr_add(net, ireq.ifr6_ifindex, &ireq.ifr6_addr, NULL,
ireq.ifr6_prefixlen, IFA_F_PERMANENT,
INFINITY_LIFE_TIME, INFINITY_LIFE_TIME, NULL);
err = inet6_addr_add(net, ireq.ifr6_ifindex, &cfg, NULL);
rtnl_unlock();
return err;
}
......@@ -3010,11 +3015,16 @@ static void add_addr(struct inet6_dev *idev, const struct in6_addr *addr,
int plen, int scope)
{
struct inet6_ifaddr *ifp;
struct ifa6_config cfg = {
.pfx = addr,
.plen = plen,
.ifa_flags = IFA_F_PERMANENT,
.valid_lft = INFINITY_LIFE_TIME,
.preferred_lft = INFINITY_LIFE_TIME,
.scope = scope
};
ifp = ipv6_add_addr(idev, addr, NULL, plen,
scope, IFA_F_PERMANENT,
INFINITY_LIFE_TIME, INFINITY_LIFE_TIME,
true, NULL);
ifp = ipv6_add_addr(idev, &cfg, true, NULL);
if (!IS_ERR(ifp)) {
spin_lock_bh(&ifp->lock);
ifp->flags &= ~IFA_F_TENTATIVE;
......@@ -3051,7 +3061,7 @@ static void sit_add_v4_addrs(struct inet6_dev *idev)
if (addr.s6_addr32[3]) {
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);
return;
}
......@@ -3076,8 +3086,8 @@ static void sit_add_v4_addrs(struct inet6_dev *idev)
}
add_addr(idev, &addr, plen, flag);
addrconf_prefix_route(&addr, plen, idev->dev, 0,
pflags, GFP_ATOMIC);
addrconf_prefix_route(&addr, plen, 0, idev->dev,
0, pflags, GFP_ATOMIC);
}
}
}
......@@ -3104,20 +3114,26 @@ static void init_loopback(struct net_device *dev)
void addrconf_add_linklocal(struct inet6_dev *idev,
const struct in6_addr *addr, u32 flags)
{
struct ifa6_config cfg = {
.pfx = addr,
.plen = 64,
.ifa_flags = flags | IFA_F_PERMANENT,
.valid_lft = INFINITY_LIFE_TIME,
.preferred_lft = INFINITY_LIFE_TIME,
.scope = IFA_LINK
};
struct inet6_ifaddr *ifp;
u32 addr_flags = flags | IFA_F_PERMANENT;
#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
if ((dev_net(idev->dev)->ipv6.devconf_all->optimistic_dad ||
idev->cnf.optimistic_dad) &&
!dev_net(idev->dev)->ipv6.devconf_all->forwarding)
addr_flags |= IFA_F_OPTIMISTIC;
cfg.ifa_flags |= IFA_F_OPTIMISTIC;
#endif
ifp = ipv6_add_addr(idev, addr, NULL, 64, IFA_LINK, addr_flags,
INFINITY_LIFE_TIME, INFINITY_LIFE_TIME, true, NULL);
ifp = ipv6_add_addr(idev, &cfg, true, NULL);
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);
addrconf_dad_start(ifp);
in6_ifa_put(ifp);
......@@ -3233,7 +3249,7 @@ static void addrconf_addr_gen(struct inet6_dev *idev, bool prefix_route)
addrconf_add_linklocal(idev, &addr,
IFA_F_STABLE_PRIVACY);
else if (prefix_route)
addrconf_prefix_route(&addr, 64, idev->dev,
addrconf_prefix_route(&addr, 64, 0, idev->dev,
0, 0, GFP_KERNEL);
break;
case IN6_ADDR_GEN_MODE_EUI64:
......@@ -3244,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)
addrconf_add_linklocal(idev, &addr, 0);
else if (prefix_route)
addrconf_prefix_route(&addr, 64, idev->dev,
addrconf_prefix_route(&addr, 64, 0, idev->dev,
0, 0, GFP_KERNEL);
break;
case IN6_ADDR_GEN_MODE_NONE:
......@@ -3364,7 +3380,8 @@ static int fixup_permanent_addr(struct net *net,
if (!(ifp->flags & IFA_F_NOPREFIXROUTE)) {
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)
......@@ -4484,6 +4501,7 @@ static const struct nla_policy ifa_ipv6_policy[IFA_MAX+1] = {
[IFA_LOCAL] = { .len = sizeof(struct in6_addr) },
[IFA_CACHEINFO] = { .len = sizeof(struct ifa_cacheinfo) },
[IFA_FLAGS] = { .len = sizeof(u32) },
[IFA_RT_PRIORITY] = { .len = sizeof(u32) },
};
static int
......@@ -4516,8 +4534,38 @@ inet6_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh,
ifm->ifa_prefixlen);
}
static int inet6_addr_modify(struct inet6_ifaddr *ifp, u32 ifa_flags,
u32 prefered_lft, u32 valid_lft)
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)
{
u32 flags;
clock_t expires;
......@@ -4527,32 +4575,32 @@ static int inet6_addr_modify(struct inet6_ifaddr *ifp, u32 ifa_flags,
ASSERT_RTNL();
if (!valid_lft || (prefered_lft > valid_lft))
if (!cfg->valid_lft || cfg->preferred_lft > cfg->valid_lft)
return -EINVAL;
if (ifa_flags & IFA_F_MANAGETEMPADDR &&
if (cfg->ifa_flags & IFA_F_MANAGETEMPADDR &&
(ifp->flags & IFA_F_TEMPORARY || ifp->prefix_len != 64))
return -EINVAL;
if (!(ifp->flags & IFA_F_TENTATIVE) || ifp->flags & IFA_F_DADFAILED)
ifa_flags &= ~IFA_F_OPTIMISTIC;
cfg->ifa_flags &= ~IFA_F_OPTIMISTIC;
timeout = addrconf_timeout_fixup(valid_lft, HZ);
timeout = addrconf_timeout_fixup(cfg->valid_lft, HZ);
if (addrconf_finite_timeout(timeout)) {
expires = jiffies_to_clock_t(timeout * HZ);
valid_lft = timeout;
cfg->valid_lft = timeout;
flags = RTF_EXPIRES;
} else {
expires = 0;
flags = 0;
ifa_flags |= IFA_F_PERMANENT;
cfg->ifa_flags |= IFA_F_PERMANENT;
}
timeout = addrconf_timeout_fixup(prefered_lft, HZ);
timeout = addrconf_timeout_fixup(cfg->preferred_lft, HZ);
if (addrconf_finite_timeout(timeout)) {
if (timeout == 0)
ifa_flags |= IFA_F_DEPRECATED;
prefered_lft = timeout;
cfg->ifa_flags |= IFA_F_DEPRECATED;
cfg->preferred_lft = timeout;
}
spin_lock_bh(&ifp->lock);
......@@ -4562,19 +4610,30 @@ static int inet6_addr_modify(struct inet6_ifaddr *ifp, u32 ifa_flags,
ifp->flags &= ~(IFA_F_DEPRECATED | IFA_F_PERMANENT | IFA_F_NODAD |
IFA_F_HOMEADDRESS | IFA_F_MANAGETEMPADDR |
IFA_F_NOPREFIXROUTE);
ifp->flags |= ifa_flags;
ifp->flags |= cfg->ifa_flags;
ifp->tstamp = jiffies;
ifp->valid_lft = valid_lft;
ifp->prefered_lft = prefered_lft;
ifp->valid_lft = cfg->valid_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);
if (!(ifp->flags&IFA_F_TENTATIVE))
ipv6_ifa_notify(0, ifp);
if (!(ifa_flags & IFA_F_NOPREFIXROUTE)) {
addrconf_prefix_route(&ifp->addr, ifp->prefix_len,
ifp->idev->dev, expires, flags,
GFP_KERNEL);
if (!(cfg->ifa_flags & IFA_F_NOPREFIXROUTE)) {
int rc = -ENOENT;
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) {
enum cleanup_prefix_rt_t action;
unsigned long rt_expires;
......@@ -4590,10 +4649,14 @@ static int inet6_addr_modify(struct inet6_ifaddr *ifp, u32 ifa_flags,
}
if (was_managetempaddr || ifp->flags & IFA_F_MANAGETEMPADDR) {
if (was_managetempaddr && !(ifp->flags & IFA_F_MANAGETEMPADDR))
valid_lft = prefered_lft = 0;
manage_tempaddrs(ifp->idev, ifp, valid_lft, prefered_lft,
!was_managetempaddr, jiffies);
if (was_managetempaddr &&
!(ifp->flags & IFA_F_MANAGETEMPADDR)) {
cfg->valid_lft = 0;
cfg->preferred_lft = 0;
}
manage_tempaddrs(ifp->idev, ifp, cfg->valid_lft,
cfg->preferred_lft, !was_managetempaddr,
jiffies);
}
addrconf_verify_rtnl();
......@@ -4608,12 +4671,11 @@ inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh,
struct net *net = sock_net(skb->sk);
struct ifaddrmsg *ifm;
struct nlattr *tb[IFA_MAX+1];
struct in6_addr *pfx, *peer_pfx;
struct in6_addr *peer_pfx;
struct inet6_ifaddr *ifa;
struct net_device *dev;
struct inet6_dev *idev;
u32 valid_lft = INFINITY_LIFE_TIME, preferred_lft = INFINITY_LIFE_TIME;
u32 ifa_flags;
struct ifa6_config cfg;
int err;
err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv6_policy,
......@@ -4621,60 +4683,70 @@ inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh,
if (err < 0)
return err;
memset(&cfg, 0, sizeof(cfg));
ifm = nlmsg_data(nlh);
pfx = extract_addr(tb[IFA_ADDRESS], tb[IFA_LOCAL], &peer_pfx);
if (!pfx)
cfg.pfx = extract_addr(tb[IFA_ADDRESS], tb[IFA_LOCAL], &peer_pfx);
if (!cfg.pfx)
return -EINVAL;
cfg.peer_pfx = peer_pfx;
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.preferred_lft = INFINITY_LIFE_TIME;
if (tb[IFA_CACHEINFO]) {
struct ifa_cacheinfo *ci;
ci = nla_data(tb[IFA_CACHEINFO]);
valid_lft = ci->ifa_valid;
preferred_lft = ci->ifa_prefered;
} else {
preferred_lft = INFINITY_LIFE_TIME;
valid_lft = INFINITY_LIFE_TIME;
cfg.valid_lft = ci->ifa_valid;
cfg.preferred_lft = ci->ifa_prefered;
}
dev = __dev_get_by_index(net, ifm->ifa_index);
if (!dev)
return -ENODEV;
ifa_flags = tb[IFA_FLAGS] ? nla_get_u32(tb[IFA_FLAGS]) : ifm->ifa_flags;
if (tb[IFA_FLAGS])
cfg.ifa_flags = nla_get_u32(tb[IFA_FLAGS]);
else
cfg.ifa_flags = ifm->ifa_flags;
/* We ignore other flags so far. */
ifa_flags &= IFA_F_NODAD | IFA_F_HOMEADDRESS | IFA_F_MANAGETEMPADDR |
IFA_F_NOPREFIXROUTE | IFA_F_MCAUTOJOIN | IFA_F_OPTIMISTIC;
cfg.ifa_flags &= IFA_F_NODAD | IFA_F_HOMEADDRESS |
IFA_F_MANAGETEMPADDR | IFA_F_NOPREFIXROUTE |
IFA_F_MCAUTOJOIN | IFA_F_OPTIMISTIC;
idev = ipv6_find_idev(dev);
if (IS_ERR(idev))
return PTR_ERR(idev);
if (!ipv6_allow_optimistic_dad(net, idev))
ifa_flags &= ~IFA_F_OPTIMISTIC;
cfg.ifa_flags &= ~IFA_F_OPTIMISTIC;
if (ifa_flags & IFA_F_NODAD && ifa_flags & IFA_F_OPTIMISTIC) {
if (cfg.ifa_flags & IFA_F_NODAD &&
cfg.ifa_flags & IFA_F_OPTIMISTIC) {
NL_SET_ERR_MSG(extack, "IFA_F_NODAD and IFA_F_OPTIMISTIC are mutually exclusive");
return -EINVAL;
}
ifa = ipv6_get_ifaddr(net, pfx, dev, 1);
ifa = ipv6_get_ifaddr(net, cfg.pfx, dev, 1);
if (!ifa) {
/*
* It would be best to check for !NLM_F_CREATE here but
* userspace already relies on not having to provide this.
*/
return inet6_addr_add(net, ifm->ifa_index, pfx, peer_pfx,
ifm->ifa_prefixlen, ifa_flags,
preferred_lft, valid_lft, extack);
return inet6_addr_add(net, ifm->ifa_index, &cfg, extack);
}
if (nlh->nlmsg_flags & NLM_F_EXCL ||
!(nlh->nlmsg_flags & NLM_F_REPLACE))
err = -EEXIST;
else
err = inet6_addr_modify(ifa, ifa_flags, preferred_lft, valid_lft);
err = inet6_addr_modify(ifa, &cfg);
in6_ifa_put(ifa);
......@@ -4725,7 +4797,8 @@ static inline int inet6_ifaddr_msgsize(void)
+ nla_total_size(16) /* IFA_LOCAL */
+ nla_total_size(16) /* IFA_ADDRESS */
+ 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,
......@@ -4771,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)
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)
goto error;
......@@ -5615,7 +5692,7 @@ static void __ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp)
if (ifp->idev->cnf.forwarding)
addrconf_join_anycast(ifp);
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,
GFP_ATOMIC);
break;
......
......@@ -6,7 +6,8 @@
ret=0
TESTS="unregister down carrier nexthop ipv6_rt ipv4_rt"
# all tests in this script. Can be overridden with -t option
TESTS="unregister down carrier nexthop ipv6_rt ipv4_rt ipv6_addr_metric ipv4_addr_metric"
VERBOSE=0
PAUSE_ON_FAIL=no
PAUSE=no
......@@ -642,6 +643,8 @@ check_route6()
local rc=0
out=$($IP -6 ro ls match ${pfx} | sed -e 's/ pref medium//')
[ "${out}" = "${expected}" ] && return 0
if [ -z "${out}" ]; then
if [ "$VERBOSE" = "1" ]; then
printf "\nNo route entry found\n"
......@@ -911,6 +914,98 @@ ipv6_route_test()
route_cleanup
}
ip_addr_metric_check()
{
ip addr help 2>&1 | grep -q metric
if [ $? -ne 0 ]; then
echo "iproute2 command does not support metric for addresses. Skipping test"
return 1
fi
return 0
}
ipv6_addr_metric_test()
{
local rc
echo
echo "IPv6 prefix route tests"
ip_addr_metric_check || return 1
setup
set -e
$IP li add dummy1 type dummy
$IP li add dummy2 type dummy
$IP li set dummy1 up
$IP li set dummy2 up
# default entry is metric 256
run_cmd "$IP -6 addr add dev dummy1 2001:db8:104::1/64"
run_cmd "$IP -6 addr add dev dummy2 2001:db8:104::2/64"
set +e
check_route6 "2001:db8:104::/64 dev dummy1 proto kernel metric 256 2001:db8:104::/64 dev dummy2 proto kernel metric 256"
log_test $? 0 "Default metric"
set -e
run_cmd "$IP -6 addr flush dev dummy1"
run_cmd "$IP -6 addr add dev dummy1 2001:db8:104::1/64 metric 257"
set +e
check_route6 "2001:db8:104::/64 dev dummy2 proto kernel metric 256 2001:db8:104::/64 dev dummy1 proto kernel metric 257"
log_test $? 0 "User specified metric on first device"
set -e
run_cmd "$IP -6 addr flush dev dummy2"
run_cmd "$IP -6 addr add dev dummy2 2001:db8:104::2/64 metric 258"
set +e
check_route6 "2001:db8:104::/64 dev dummy1 proto kernel metric 257 2001:db8:104::/64 dev dummy2 proto kernel metric 258"
log_test $? 0 "User specified metric on second device"
run_cmd "$IP -6 addr del dev dummy1 2001:db8:104::1/64 metric 257"
rc=$?
if [ $rc -eq 0 ]; then
check_route6 "2001:db8:104::/64 dev dummy2 proto kernel metric 258"
rc=$?
fi
log_test $rc 0 "Delete of address on first device"
run_cmd "$IP -6 addr change dev dummy2 2001:db8:104::2/64 metric 259"
rc=$?
if [ $rc -eq 0 ]; then
check_route6 "2001:db8:104::/64 dev dummy2 proto kernel metric 259"
rc=$?
fi
log_test $rc 0 "Modify metric of address"
# verify prefix route removed on down
run_cmd "ip netns exec testns sysctl -qw net.ipv6.conf.all.keep_addr_on_down=1"
run_cmd "$IP li set dev dummy2 down"
rc=$?
if [ $rc -eq 0 ]; then
check_route6 ""
rc=$?
fi
log_test $rc 0 "Prefix route removed on link down"
# verify prefix route re-inserted with assigned metric
run_cmd "$IP li set dev dummy2 up"
rc=$?
if [ $rc -eq 0 ]; then
check_route6 "2001:db8:104::/64 dev dummy2 proto kernel metric 259"
rc=$?
fi
log_test $rc 0 "Prefix route with metric on link up"
$IP li del dummy1
$IP li del dummy2
cleanup
}
# add route for a prefix, flushing any existing routes first
# expected to be the first step of a test
add_route()
......@@ -955,6 +1050,8 @@ check_route()
local rc=0
out=$($IP ro ls match ${pfx})
[ "${out}" = "${expected}" ] && return 0
if [ -z "${out}" ]; then
if [ "$VERBOSE" = "1" ]; then
printf "\nNo route entry found\n"
......@@ -1181,6 +1278,86 @@ ipv4_route_test()
route_cleanup
}
ipv4_addr_metric_test()
{
local rc
echo
echo "IPv4 prefix route tests"
ip_addr_metric_check || return 1
setup
set -e
$IP li add dummy1 type dummy
$IP li add dummy2 type dummy
$IP li set dummy1 up
$IP li set dummy2 up
# default entry is metric 256
run_cmd "$IP addr add dev dummy1 172.16.104.1/24"
run_cmd "$IP addr add dev dummy2 172.16.104.2/24"
set +e
check_route "172.16.104.0/24 dev dummy1 proto kernel scope link src 172.16.104.1 172.16.104.0/24 dev dummy2 proto kernel scope link src 172.16.104.2"
log_test $? 0 "Default metric"
set -e
run_cmd "$IP addr flush dev dummy1"
run_cmd "$IP addr add dev dummy1 172.16.104.1/24 metric 257"
set +e
check_route "172.16.104.0/24 dev dummy2 proto kernel scope link src 172.16.104.2 172.16.104.0/24 dev dummy1 proto kernel scope link src 172.16.104.1 metric 257"
log_test $? 0 "User specified metric on first device"
set -e
run_cmd "$IP addr flush dev dummy2"
run_cmd "$IP addr add dev dummy2 172.16.104.2/24 metric 258"
set +e
check_route "172.16.104.0/24 dev dummy1 proto kernel scope link src 172.16.104.1 metric 257 172.16.104.0/24 dev dummy2 proto kernel scope link src 172.16.104.2 metric 258"
log_test $? 0 "User specified metric on second device"
run_cmd "$IP addr del dev dummy1 172.16.104.1/24 metric 257"
rc=$?
if [ $rc -eq 0 ]; then
check_route "172.16.104.0/24 dev dummy2 proto kernel scope link src 172.16.104.2 metric 258"
rc=$?
fi
log_test $rc 0 "Delete of address on first device"
run_cmd "$IP addr change dev dummy2 172.16.104.2/24 metric 259"
rc=$?
if [ $rc -eq 0 ]; then
check_route "172.16.104.0/24 dev dummy2 proto kernel scope link src 172.16.104.2 metric 259"
rc=$?
fi
log_test $rc 0 "Modify metric of address"
# verify prefix route removed on down
run_cmd "$IP li set dev dummy2 down"
rc=$?
if [ $rc -eq 0 ]; then
check_route ""
rc=$?
fi
log_test $rc 0 "Prefix route removed on link down"
# verify prefix route re-inserted with assigned metric
run_cmd "$IP li set dev dummy2 up"
rc=$?
if [ $rc -eq 0 ]; then
check_route "172.16.104.0/24 dev dummy2 proto kernel scope link src 172.16.104.2 metric 259"
rc=$?
fi
log_test $rc 0 "Prefix route with metric on link up"
$IP li del dummy1
$IP li del dummy2
cleanup
}
################################################################################
# usage
......@@ -1245,6 +1422,8 @@ do
fib_nexthop_test|nexthop) fib_nexthop_test;;
ipv6_route_test|ipv6_rt) ipv6_route_test;;
ipv4_route_test|ipv4_rt) ipv4_route_test;;
ipv6_addr_metric) ipv6_addr_metric_test;;
ipv4_addr_metric) ipv4_addr_metric_test;;
help) echo "Test names: $TESTS"; exit 0;;
esac
......
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