mirror of
https://github.com/torvalds/linux.git
synced 2026-07-27 09:36:22 +02:00
Merge branch 'ip6mr-no-rtnl-for-rtnl_family_ip6mr-rtnetlink'
Kuniyuki Iwashima says: ==================== ip6mr: No RTNL for RTNL_FAMILY_IP6MR rtnetlink. This series is the IPv6 version of https://lore.kernel.org/netdev/20260228221800.1082070-1-kuniyu@google.com/ and removes RTNL from ip6mr rtnetlink handlers. After this series, there are a few RTNL left in net/ipv6/ip6mr.c and such users will be converted to per-netns RTNL in another series. Patch 1 extends the ipmr selftest to exercise most of the RTNL paths in net/ipv6/ipmr.c Patch 2 - 6 converts RTM_GETROUTE handlers to RCU. Patch 7 removes struct fib_dump_filter.rtnl_held. Patch 8 use RCU for mr_table for CONFIG_IPV6_MROUTE_MULTIPLE_TABLES=n for ->exit_rtnl(). Patch 9 move fib_rules_unregister() to ->exit() Patch 10 - 12 converts ->exit_batch() to ->exit_rtnl() to save one RTNL in cleanup_net(). Patch 13 removes unnecessary RTNL during setup_net() failure. Patch 14 drops RTNL for MRT6_(ADD|DEL)_MFC(_PROXY)?. Patch 15 misc clean up v2: https://lore.kernel.org/20260410211726.1668756-1-kuniyu@google.com v1: https://lore.kernel.org/20260407212001.2368593-1-kuniyu@google.com ==================== Link: https://patch.msgid.link/20260604224712.3209821-1-kuniyu@google.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
commit
b8e40c907b
|
|
@ -269,7 +269,6 @@ struct fib_dump_filter {
|
|||
bool filter_set;
|
||||
bool dump_routes;
|
||||
bool dump_exceptions;
|
||||
bool rtnl_held;
|
||||
unsigned char protocol;
|
||||
unsigned char rt_type;
|
||||
unsigned int flags;
|
||||
|
|
|
|||
|
|
@ -112,13 +112,14 @@ struct netns_ipv6 {
|
|||
struct list_head mr6_tables;
|
||||
struct fib_rules_ops *mr6_rules_ops;
|
||||
#endif
|
||||
struct fib_notifier_ops *ip6mr_notifier_ops;
|
||||
atomic_t ipmr_seq;
|
||||
struct mutex mfc_mutex;
|
||||
#endif
|
||||
atomic_t dev_addr_genid;
|
||||
atomic_t fib6_sernum;
|
||||
struct seg6_pernet_data *seg6_data;
|
||||
struct fib_notifier_ops *notifier_ops;
|
||||
struct fib_notifier_ops *ip6mr_notifier_ops;
|
||||
atomic_t ipmr_seq;
|
||||
int flowlabel_count;
|
||||
struct {
|
||||
struct hlist_head head;
|
||||
|
|
|
|||
|
|
@ -946,9 +946,6 @@ int ip_valid_fib_dump_req(struct net *net, const struct nlmsghdr *nlh,
|
|||
struct rtmsg *rtm;
|
||||
int err, i;
|
||||
|
||||
if (filter->rtnl_held)
|
||||
ASSERT_RTNL();
|
||||
|
||||
rtm = nlmsg_payload(nlh, sizeof(*rtm));
|
||||
if (!rtm) {
|
||||
NL_SET_ERR_MSG(extack, "Invalid header for FIB dump request");
|
||||
|
|
@ -992,10 +989,8 @@ int ip_valid_fib_dump_req(struct net *net, const struct nlmsghdr *nlh,
|
|||
break;
|
||||
case RTA_OIF:
|
||||
ifindex = nla_get_u32(tb[i]);
|
||||
if (filter->rtnl_held)
|
||||
filter->dev = __dev_get_by_index(net, ifindex);
|
||||
else
|
||||
filter->dev = dev_get_by_index_rcu(net, ifindex);
|
||||
|
||||
filter->dev = dev_get_by_index_rcu(net, ifindex);
|
||||
if (!filter->dev)
|
||||
return -ENODEV;
|
||||
break;
|
||||
|
|
@ -1017,18 +1012,16 @@ EXPORT_SYMBOL_GPL(ip_valid_fib_dump_req);
|
|||
|
||||
static int inet_dump_fib(struct sk_buff *skb, struct netlink_callback *cb)
|
||||
{
|
||||
const struct nlmsghdr *nlh = cb->nlh;
|
||||
struct net *net = sock_net(skb->sk);
|
||||
struct fib_dump_filter filter = {
|
||||
.dump_routes = true,
|
||||
.dump_exceptions = true,
|
||||
.rtnl_held = false,
|
||||
};
|
||||
const struct nlmsghdr *nlh = cb->nlh;
|
||||
struct net *net = sock_net(skb->sk);
|
||||
unsigned int h, s_h;
|
||||
unsigned int e = 0, s_e;
|
||||
struct fib_table *tb;
|
||||
unsigned int e = 0, s_e, h, s_h;
|
||||
struct hlist_head *head;
|
||||
int dumped = 0, err = 0;
|
||||
struct fib_table *tb;
|
||||
|
||||
rcu_read_lock();
|
||||
if (cb->strict_check) {
|
||||
|
|
|
|||
|
|
@ -2777,9 +2777,7 @@ static int ipmr_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh,
|
|||
|
||||
static int ipmr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb)
|
||||
{
|
||||
struct fib_dump_filter filter = {
|
||||
.rtnl_held = false,
|
||||
};
|
||||
struct fib_dump_filter filter = {};
|
||||
int err;
|
||||
|
||||
rcu_read_lock();
|
||||
|
|
|
|||
|
|
@ -633,7 +633,6 @@ static int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb)
|
|||
struct rt6_rtnl_dump_arg arg = {
|
||||
.filter.dump_exceptions = true,
|
||||
.filter.dump_routes = true,
|
||||
.filter.rtnl_held = false,
|
||||
};
|
||||
const struct nlmsghdr *nlh = cb->nlh;
|
||||
struct net *net = sock_net(skb->sk);
|
||||
|
|
|
|||
299
net/ipv6/ip6mr.c
299
net/ipv6/ip6mr.c
|
|
@ -85,7 +85,8 @@ static DEFINE_SPINLOCK(mfc_unres_lock);
|
|||
static struct kmem_cache *mrt_cachep __read_mostly;
|
||||
|
||||
static struct mr_table *ip6mr_new_table(struct net *net, u32 id);
|
||||
static void ip6mr_free_table(struct mr_table *mrt);
|
||||
static void ip6mr_free_table(struct mr_table *mrt,
|
||||
struct list_head *dev_kill_list);
|
||||
|
||||
static void ip6_mr_forward(struct net *net, struct mr_table *mrt,
|
||||
struct net_device *dev, struct sk_buff *skb,
|
||||
|
|
@ -99,7 +100,8 @@ static int ip6mr_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh,
|
|||
struct netlink_ext_ack *extack);
|
||||
static int ip6mr_rtm_dumproute(struct sk_buff *skb,
|
||||
struct netlink_callback *cb);
|
||||
static void mroute_clean_tables(struct mr_table *mrt, int flags);
|
||||
static void mroute_clean_tables(struct mr_table *mrt, int flags,
|
||||
struct list_head *dev_kill_list);
|
||||
static void ipmr_expire_process(struct timer_list *t);
|
||||
|
||||
#ifdef CONFIG_IPV6_MROUTE_MULTIPLE_TABLES
|
||||
|
|
@ -136,16 +138,6 @@ static struct mr_table *__ip6mr_get_table(struct net *net, u32 id)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static struct mr_table *ip6mr_get_table(struct net *net, u32 id)
|
||||
{
|
||||
struct mr_table *mrt;
|
||||
|
||||
rcu_read_lock();
|
||||
mrt = __ip6mr_get_table(net, id);
|
||||
rcu_read_unlock();
|
||||
return mrt;
|
||||
}
|
||||
|
||||
static int ip6mr_fib_lookup(struct net *net, struct flowi6 *flp6,
|
||||
struct mr_table **mrt)
|
||||
{
|
||||
|
|
@ -237,6 +229,7 @@ static const struct fib_rules_ops __net_initconst ip6mr_rules_ops_template = {
|
|||
static int __net_init ip6mr_rules_init(struct net *net)
|
||||
{
|
||||
struct fib_rules_ops *ops;
|
||||
LIST_HEAD(dev_kill_list);
|
||||
struct mr_table *mrt;
|
||||
int err;
|
||||
|
||||
|
|
@ -260,24 +253,26 @@ static int __net_init ip6mr_rules_init(struct net *net)
|
|||
return 0;
|
||||
|
||||
err2:
|
||||
rtnl_lock();
|
||||
ip6mr_free_table(mrt);
|
||||
rtnl_unlock();
|
||||
ip6mr_free_table(mrt, &dev_kill_list);
|
||||
err1:
|
||||
fib_rules_unregister(ops);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __net_exit ip6mr_rules_exit(struct net *net)
|
||||
{
|
||||
fib_rules_unregister(net->ipv6.mr6_rules_ops);
|
||||
}
|
||||
|
||||
static void __net_exit ip6mr_rules_exit_rtnl(struct net *net,
|
||||
struct list_head *dev_kill_list)
|
||||
{
|
||||
struct mr_table *mrt, *next;
|
||||
|
||||
ASSERT_RTNL();
|
||||
list_for_each_entry_safe(mrt, next, &net->ipv6.mr6_tables, list) {
|
||||
list_del(&mrt->list);
|
||||
ip6mr_free_table(mrt);
|
||||
list_del_rcu(&mrt->list);
|
||||
ip6mr_free_table(mrt, dev_kill_list);
|
||||
}
|
||||
fib_rules_unregister(net->ipv6.mr6_rules_ops);
|
||||
}
|
||||
|
||||
static int ip6mr_rules_dump(struct net *net, struct notifier_block *nb,
|
||||
|
|
@ -298,28 +293,30 @@ bool ip6mr_rule_default(const struct fib_rule *rule)
|
|||
}
|
||||
EXPORT_SYMBOL(ip6mr_rule_default);
|
||||
#else
|
||||
#define ip6mr_for_each_table(mrt, net) \
|
||||
for (mrt = net->ipv6.mrt6; mrt; mrt = NULL)
|
||||
|
||||
static struct mr_table *ip6mr_mr_table_iter(struct net *net,
|
||||
struct mr_table *mrt)
|
||||
{
|
||||
if (!mrt)
|
||||
return net->ipv6.mrt6;
|
||||
return rcu_dereference(net->ipv6.mrt6);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct mr_table *ip6mr_get_table(struct net *net, u32 id)
|
||||
static struct mr_table *__ip6mr_get_table(struct net *net, u32 id)
|
||||
{
|
||||
return net->ipv6.mrt6;
|
||||
return rcu_dereference_check(net->ipv6.mrt6,
|
||||
lockdep_rtnl_is_held() ||
|
||||
!rcu_access_pointer(net->ipv6.mrt6));
|
||||
}
|
||||
|
||||
#define __ip6mr_get_table ip6mr_get_table
|
||||
#define ip6mr_for_each_table(mrt, net) \
|
||||
for (mrt = __ip6mr_get_table(net, 0); mrt; mrt = NULL)
|
||||
|
||||
static int ip6mr_fib_lookup(struct net *net, struct flowi6 *flp6,
|
||||
struct mr_table **mrt)
|
||||
{
|
||||
*mrt = net->ipv6.mrt6;
|
||||
*mrt = rcu_dereference(net->ipv6.mrt6);
|
||||
if (!*mrt)
|
||||
return -EAGAIN;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -330,15 +327,22 @@ static int __net_init ip6mr_rules_init(struct net *net)
|
|||
mrt = ip6mr_new_table(net, RT6_TABLE_DFLT);
|
||||
if (IS_ERR(mrt))
|
||||
return PTR_ERR(mrt);
|
||||
net->ipv6.mrt6 = mrt;
|
||||
|
||||
rcu_assign_pointer(net->ipv6.mrt6, mrt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __net_exit ip6mr_rules_exit(struct net *net)
|
||||
{
|
||||
ASSERT_RTNL();
|
||||
ip6mr_free_table(net->ipv6.mrt6);
|
||||
net->ipv6.mrt6 = NULL;
|
||||
}
|
||||
|
||||
static void __net_exit ip6mr_rules_exit_rtnl(struct net *net,
|
||||
struct list_head *dev_kill_list)
|
||||
{
|
||||
struct mr_table *mrt = rcu_dereference_protected(net->ipv6.mrt6, 1);
|
||||
|
||||
RCU_INIT_POINTER(net->ipv6.mrt6, NULL);
|
||||
ip6mr_free_table(mrt, dev_kill_list);
|
||||
}
|
||||
|
||||
static int ip6mr_rules_dump(struct net *net, struct notifier_block *nb,
|
||||
|
|
@ -353,6 +357,17 @@ static unsigned int ip6mr_rules_seq_read(const struct net *net)
|
|||
}
|
||||
#endif
|
||||
|
||||
static struct mr_table *ip6mr_get_table(struct net *net, u32 id)
|
||||
{
|
||||
struct mr_table *mrt;
|
||||
|
||||
rcu_read_lock();
|
||||
mrt = __ip6mr_get_table(net, id);
|
||||
rcu_read_unlock();
|
||||
|
||||
return mrt;
|
||||
}
|
||||
|
||||
static int ip6mr_hash_cmp(struct rhashtable_compare_arg *arg,
|
||||
const void *ptr)
|
||||
{
|
||||
|
|
@ -402,17 +417,23 @@ static struct mr_table *ip6mr_new_table(struct net *net, u32 id)
|
|||
ipmr_expire_process, ip6mr_new_table_set);
|
||||
}
|
||||
|
||||
static void ip6mr_free_table(struct mr_table *mrt)
|
||||
static void ip6mr_free_table(struct mr_table *mrt,
|
||||
struct list_head *dev_kill_list)
|
||||
{
|
||||
struct net *net = read_pnet(&mrt->net);
|
||||
LIST_HEAD(ip6mr_dev_kill_list);
|
||||
|
||||
WARN_ON_ONCE(!mr_can_free_table(net));
|
||||
|
||||
timer_shutdown_sync(&mrt->ipmr_expire_timer);
|
||||
mroute_clean_tables(mrt, MRT6_FLUSH_MIFS | MRT6_FLUSH_MIFS_STATIC |
|
||||
MRT6_FLUSH_MFC | MRT6_FLUSH_MFC_STATIC);
|
||||
rhltable_destroy(&mrt->mfc_hash);
|
||||
kfree(mrt);
|
||||
MRT6_FLUSH_MFC | MRT6_FLUSH_MFC_STATIC,
|
||||
&ip6mr_dev_kill_list);
|
||||
|
||||
mr_table_free(mrt);
|
||||
|
||||
WARN_ON_ONCE(!net_initialized(net) && !list_empty(&ip6mr_dev_kill_list));
|
||||
list_splice(&ip6mr_dev_kill_list, dev_kill_list);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
|
|
@ -623,18 +644,22 @@ static netdev_tx_t reg_vif_xmit(struct sk_buff *skb,
|
|||
if (!pskb_inet_may_pull(skb))
|
||||
goto tx_err;
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
if (ip6mr_fib_lookup(net, &fl6, &mrt) < 0)
|
||||
goto tx_err;
|
||||
goto tx_lookup_err;
|
||||
|
||||
DEV_STATS_ADD(dev, tx_bytes, skb->len);
|
||||
DEV_STATS_INC(dev, tx_packets);
|
||||
rcu_read_lock();
|
||||
|
||||
ip6mr_cache_report(mrt, skb, READ_ONCE(mrt->mroute_reg_vif_num),
|
||||
MRT6MSG_WHOLEPKT);
|
||||
rcu_read_unlock();
|
||||
kfree_skb(skb);
|
||||
return NETDEV_TX_OK;
|
||||
|
||||
tx_lookup_err:
|
||||
rcu_read_unlock();
|
||||
tx_err:
|
||||
DEV_STATS_INC(dev, tx_errors);
|
||||
kfree_skb(skb);
|
||||
|
|
@ -1157,11 +1182,18 @@ static int ip6mr_cache_report(const struct mr_table *mrt, struct sk_buff *pkt,
|
|||
static int ip6mr_cache_unresolved(struct mr_table *mrt, mifi_t mifi,
|
||||
struct sk_buff *skb, struct net_device *dev)
|
||||
{
|
||||
struct mfc6_cache *c;
|
||||
struct net *net = read_pnet(&mrt->net);
|
||||
struct mfc6_cache *c = NULL;
|
||||
bool found = false;
|
||||
int err;
|
||||
|
||||
spin_lock_bh(&mfc_unres_lock);
|
||||
|
||||
if (!check_net(net)) {
|
||||
err = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
list_for_each_entry(c, &mrt->mfc_unres_queue, _c.list) {
|
||||
if (ipv6_addr_equal(&c->mf6c_mcastgrp, &ipv6_hdr(skb)->daddr) &&
|
||||
ipv6_addr_equal(&c->mf6c_origin, &ipv6_hdr(skb)->saddr)) {
|
||||
|
|
@ -1177,10 +1209,8 @@ static int ip6mr_cache_unresolved(struct mr_table *mrt, mifi_t mifi,
|
|||
|
||||
c = ip6mr_cache_alloc_unres();
|
||||
if (!c) {
|
||||
spin_unlock_bh(&mfc_unres_lock);
|
||||
|
||||
kfree_skb(skb);
|
||||
return -ENOBUFS;
|
||||
err = -ENOBUFS;
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Fill in the new cache entry */
|
||||
|
|
@ -1192,16 +1222,8 @@ static int ip6mr_cache_unresolved(struct mr_table *mrt, mifi_t mifi,
|
|||
* Reflect first query at pim6sd
|
||||
*/
|
||||
err = ip6mr_cache_report(mrt, skb, mifi, MRT6MSG_NOCACHE);
|
||||
if (err < 0) {
|
||||
/* If the report failed throw the cache entry
|
||||
out - Brad Parker
|
||||
*/
|
||||
spin_unlock_bh(&mfc_unres_lock);
|
||||
|
||||
ip6mr_cache_free(c);
|
||||
kfree_skb(skb);
|
||||
return err;
|
||||
}
|
||||
if (err < 0)
|
||||
goto err;
|
||||
|
||||
atomic_inc(&mrt->cache_resolve_queue_len);
|
||||
list_add(&c->_c.list, &mrt->mfc_unres_queue);
|
||||
|
|
@ -1212,18 +1234,26 @@ static int ip6mr_cache_unresolved(struct mr_table *mrt, mifi_t mifi,
|
|||
|
||||
/* See if we can append the packet */
|
||||
if (c->_c.mfc_un.unres.unresolved.qlen > 3) {
|
||||
kfree_skb(skb);
|
||||
c = NULL;
|
||||
err = -ENOBUFS;
|
||||
} else {
|
||||
if (dev) {
|
||||
skb->dev = dev;
|
||||
skb->skb_iif = dev->ifindex;
|
||||
}
|
||||
skb_queue_tail(&c->_c.mfc_un.unres.unresolved, skb);
|
||||
err = 0;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (dev) {
|
||||
skb->dev = dev;
|
||||
skb->skb_iif = dev->ifindex;
|
||||
}
|
||||
|
||||
skb_queue_tail(&c->_c.mfc_un.unres.unresolved, skb);
|
||||
|
||||
spin_unlock_bh(&mfc_unres_lock);
|
||||
return 0;
|
||||
|
||||
err:
|
||||
spin_unlock_bh(&mfc_unres_lock);
|
||||
if (c)
|
||||
ip6mr_cache_free(c);
|
||||
kfree_skb(skb);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
@ -1236,7 +1266,6 @@ static int ip6mr_mfc_delete(struct mr_table *mrt, struct mf6cctl *mfc,
|
|||
{
|
||||
struct mfc6_cache *c;
|
||||
|
||||
/* The entries are added/deleted only under RTNL */
|
||||
rcu_read_lock();
|
||||
c = ip6mr_cache_find_parent(mrt, &mfc->mf6cc_origin.sin6_addr,
|
||||
&mfc->mf6cc_mcastgrp.sin6_addr, parent);
|
||||
|
|
@ -1323,8 +1352,13 @@ static void __net_exit ip6mr_notifier_exit(struct net *net)
|
|||
/* Setup for IP multicast routing */
|
||||
static int __net_init ip6mr_net_init(struct net *net)
|
||||
{
|
||||
#ifdef CONFIG_PROC_FS
|
||||
LIST_HEAD(dev_kill_list);
|
||||
#endif
|
||||
int err;
|
||||
|
||||
mutex_init(&net->ipv6.mfc_mutex);
|
||||
|
||||
err = ip6mr_notifier_init(net);
|
||||
if (err)
|
||||
return err;
|
||||
|
|
@ -1349,9 +1383,8 @@ static int __net_init ip6mr_net_init(struct net *net)
|
|||
proc_cache_fail:
|
||||
remove_proc_entry("ip6_mr_vif", net->proc_net);
|
||||
proc_vif_fail:
|
||||
rtnl_lock();
|
||||
ip6mr_rules_exit_rtnl(net, &dev_kill_list);
|
||||
ip6mr_rules_exit(net);
|
||||
rtnl_unlock();
|
||||
#endif
|
||||
ip6mr_rules_fail:
|
||||
ip6mr_notifier_exit(net);
|
||||
|
|
@ -1364,29 +1397,27 @@ static void __net_exit ip6mr_net_exit(struct net *net)
|
|||
remove_proc_entry("ip6_mr_cache", net->proc_net);
|
||||
remove_proc_entry("ip6_mr_vif", net->proc_net);
|
||||
#endif
|
||||
ip6mr_rules_exit(net);
|
||||
ip6mr_notifier_exit(net);
|
||||
}
|
||||
|
||||
static void __net_exit ip6mr_net_exit_batch(struct list_head *net_list)
|
||||
static void __net_exit ip6mr_net_exit_rtnl(struct net *net,
|
||||
struct list_head *dev_kill_list)
|
||||
{
|
||||
struct net *net;
|
||||
|
||||
rtnl_lock();
|
||||
list_for_each_entry(net, net_list, exit_list)
|
||||
ip6mr_rules_exit(net);
|
||||
rtnl_unlock();
|
||||
ip6mr_rules_exit_rtnl(net, dev_kill_list);
|
||||
}
|
||||
|
||||
static struct pernet_operations ip6mr_net_ops = {
|
||||
.init = ip6mr_net_init,
|
||||
.exit = ip6mr_net_exit,
|
||||
.exit_batch = ip6mr_net_exit_batch,
|
||||
.exit_rtnl = ip6mr_net_exit_rtnl,
|
||||
};
|
||||
|
||||
static const struct rtnl_msg_handler ip6mr_rtnl_msg_handlers[] __initconst_or_module = {
|
||||
{.owner = THIS_MODULE, .protocol = RTNL_FAMILY_IP6MR,
|
||||
.msgtype = RTM_GETROUTE,
|
||||
.doit = ip6mr_rtm_getroute, .dumpit = ip6mr_rtm_dumproute},
|
||||
.doit = ip6mr_rtm_getroute, .dumpit = ip6mr_rtm_dumproute,
|
||||
.flags = RTNL_FLAG_DOIT_UNLOCKED | RTNL_FLAG_DUMP_UNLOCKED},
|
||||
};
|
||||
|
||||
int __init ip6_mr_init(void)
|
||||
|
|
@ -1456,7 +1487,6 @@ static int ip6mr_mfc_add(struct net *net, struct mr_table *mrt,
|
|||
ttls[i] = 1;
|
||||
}
|
||||
|
||||
/* The entries are added/deleted only under RTNL */
|
||||
rcu_read_lock();
|
||||
c = ip6mr_cache_find_parent(mrt, &mfc->mf6cc_origin.sin6_addr,
|
||||
&mfc->mf6cc_mcastgrp.sin6_addr, parent);
|
||||
|
|
@ -1531,10 +1561,11 @@ static int ip6mr_mfc_add(struct net *net, struct mr_table *mrt,
|
|||
* Close the multicast socket, and clear the vif tables etc
|
||||
*/
|
||||
|
||||
static void mroute_clean_tables(struct mr_table *mrt, int flags)
|
||||
static void mroute_clean_tables(struct mr_table *mrt, int flags,
|
||||
struct list_head *dev_kill_list)
|
||||
{
|
||||
struct net *net = read_pnet(&mrt->net);
|
||||
struct mr_mfc *c, *tmp;
|
||||
LIST_HEAD(list);
|
||||
int i;
|
||||
|
||||
/* Shut down all active vif entries */
|
||||
|
|
@ -1544,29 +1575,32 @@ static void mroute_clean_tables(struct mr_table *mrt, int flags)
|
|||
!(flags & MRT6_FLUSH_MIFS_STATIC)) ||
|
||||
(!(mrt->vif_table[i].flags & VIFF_STATIC) && !(flags & MRT6_FLUSH_MIFS)))
|
||||
continue;
|
||||
mif6_delete(mrt, i, 0, &list);
|
||||
mif6_delete(mrt, i, 0, dev_kill_list);
|
||||
}
|
||||
unregister_netdevice_many(&list);
|
||||
}
|
||||
|
||||
/* Wipe the cache */
|
||||
if (flags & (MRT6_FLUSH_MFC | MRT6_FLUSH_MFC_STATIC)) {
|
||||
mutex_lock(&net->ipv6.mfc_mutex);
|
||||
|
||||
list_for_each_entry_safe(c, tmp, &mrt->mfc_cache_list, list) {
|
||||
if (((c->mfc_flags & MFC_STATIC) && !(flags & MRT6_FLUSH_MFC_STATIC)) ||
|
||||
(!(c->mfc_flags & MFC_STATIC) && !(flags & MRT6_FLUSH_MFC)))
|
||||
continue;
|
||||
rhltable_remove(&mrt->mfc_hash, &c->mnode, ip6mr_rht_params);
|
||||
list_del_rcu(&c->list);
|
||||
call_ip6mr_mfc_entry_notifiers(read_pnet(&mrt->net),
|
||||
FIB_EVENT_ENTRY_DEL,
|
||||
call_ip6mr_mfc_entry_notifiers(net, FIB_EVENT_ENTRY_DEL,
|
||||
(struct mfc6_cache *)c, mrt->id);
|
||||
mr6_netlink_event(mrt, (struct mfc6_cache *)c, RTM_DELROUTE);
|
||||
mr_cache_put(c);
|
||||
}
|
||||
|
||||
mutex_unlock(&net->ipv6.mfc_mutex);
|
||||
}
|
||||
|
||||
if (flags & MRT6_FLUSH_MFC) {
|
||||
if (atomic_read(&mrt->cache_resolve_queue_len) != 0) {
|
||||
if (atomic_read(&mrt->cache_resolve_queue_len) != 0 ||
|
||||
!check_net(net)) {
|
||||
spin_lock_bh(&mfc_unres_lock);
|
||||
list_for_each_entry_safe(c, tmp, &mrt->mfc_unres_queue, list) {
|
||||
list_del(&c->list);
|
||||
|
|
@ -1609,6 +1643,7 @@ int ip6mr_sk_done(struct sock *sk)
|
|||
{
|
||||
struct net *net = sock_net(sk);
|
||||
struct ipv6_devconf *devconf;
|
||||
LIST_HEAD(dev_kill_list);
|
||||
struct mr_table *mrt;
|
||||
int err = -EACCES;
|
||||
|
||||
|
|
@ -1636,11 +1671,13 @@ int ip6mr_sk_done(struct sock *sk)
|
|||
NETCONFA_IFINDEX_ALL,
|
||||
net->ipv6.devconf_all);
|
||||
|
||||
mroute_clean_tables(mrt, MRT6_FLUSH_MIFS | MRT6_FLUSH_MFC);
|
||||
mroute_clean_tables(mrt, MRT6_FLUSH_MIFS | MRT6_FLUSH_MFC,
|
||||
&dev_kill_list);
|
||||
err = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
unregister_netdevice_many(&dev_kill_list);
|
||||
rtnl_unlock();
|
||||
|
||||
return err;
|
||||
|
|
@ -1742,27 +1779,33 @@ int ip6_mroute_setsockopt(struct sock *sk, int optname, sockptr_t optval,
|
|||
return -EFAULT;
|
||||
if (parent == 0)
|
||||
parent = mfc.mf6cc_parent;
|
||||
rtnl_lock();
|
||||
|
||||
mutex_lock(&net->ipv6.mfc_mutex);
|
||||
|
||||
if (optname == MRT6_DEL_MFC || optname == MRT6_DEL_MFC_PROXY)
|
||||
ret = ip6mr_mfc_delete(mrt, &mfc, parent);
|
||||
else
|
||||
ret = ip6mr_mfc_add(net, mrt, &mfc,
|
||||
sk ==
|
||||
rtnl_dereference(mrt->mroute_sk),
|
||||
rcu_access_pointer(mrt->mroute_sk),
|
||||
parent);
|
||||
rtnl_unlock();
|
||||
|
||||
mutex_unlock(&net->ipv6.mfc_mutex);
|
||||
return ret;
|
||||
|
||||
case MRT6_FLUSH:
|
||||
{
|
||||
LIST_HEAD(dev_kill_list);
|
||||
int flags;
|
||||
|
||||
if (optlen != sizeof(flags))
|
||||
return -EINVAL;
|
||||
if (copy_from_sockptr(&flags, optval, sizeof(flags)))
|
||||
return -EFAULT;
|
||||
|
||||
rtnl_lock();
|
||||
mroute_clean_tables(mrt, flags);
|
||||
mroute_clean_tables(mrt, flags, &dev_kill_list);
|
||||
unregister_netdevice_many(&dev_kill_list);
|
||||
rtnl_unlock();
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1778,7 +1821,7 @@ int ip6_mroute_setsockopt(struct sock *sk, int optname, sockptr_t optval,
|
|||
return -EINVAL;
|
||||
if (copy_from_sockptr(&v, optval, sizeof(v)))
|
||||
return -EFAULT;
|
||||
mrt->mroute_do_assert = v;
|
||||
WRITE_ONCE(mrt->mroute_do_assert, v);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1798,9 +1841,9 @@ int ip6_mroute_setsockopt(struct sock *sk, int optname, sockptr_t optval,
|
|||
rtnl_lock();
|
||||
ret = 0;
|
||||
if (v != mrt->mroute_do_pim) {
|
||||
mrt->mroute_do_pim = v;
|
||||
mrt->mroute_do_assert = v;
|
||||
mrt->mroute_do_wrvifwhole = do_wrmifwhole;
|
||||
WRITE_ONCE(mrt->mroute_do_pim, v);
|
||||
WRITE_ONCE(mrt->mroute_do_assert, v);
|
||||
WRITE_ONCE(mrt->mroute_do_wrvifwhole, do_wrmifwhole);
|
||||
}
|
||||
rtnl_unlock();
|
||||
return ret;
|
||||
|
|
@ -1868,11 +1911,11 @@ int ip6_mroute_getsockopt(struct sock *sk, int optname, sockptr_t optval,
|
|||
break;
|
||||
#ifdef CONFIG_IPV6_PIMSM_V2
|
||||
case MRT6_PIM:
|
||||
val = mrt->mroute_do_pim;
|
||||
val = READ_ONCE(mrt->mroute_do_pim);
|
||||
break;
|
||||
#endif
|
||||
case MRT6_ASSERT:
|
||||
val = mrt->mroute_do_assert;
|
||||
val = READ_ONCE(mrt->mroute_do_assert);
|
||||
break;
|
||||
default:
|
||||
return -ENOPROTOOPT;
|
||||
|
|
@ -2175,20 +2218,20 @@ static void ip6_mr_forward(struct net *net, struct mr_table *mrt,
|
|||
if (rcu_access_pointer(mrt->vif_table[vif].dev) != dev) {
|
||||
atomic_long_inc(&c->_c.mfc_un.res.wrong_if);
|
||||
|
||||
if (true_vifi >= 0 && mrt->mroute_do_assert &&
|
||||
if (true_vifi >= 0 && READ_ONCE(mrt->mroute_do_assert) &&
|
||||
/* pimsm uses asserts, when switching from RPT to SPT,
|
||||
so that we cannot check that packet arrived on an oif.
|
||||
It is bad, but otherwise we would need to move pretty
|
||||
large chunk of pimd to kernel. Ough... --ANK
|
||||
*/
|
||||
(mrt->mroute_do_pim ||
|
||||
(READ_ONCE(mrt->mroute_do_pim) ||
|
||||
c->_c.mfc_un.res.ttls[true_vifi] < 255) &&
|
||||
time_after(jiffies,
|
||||
c->_c.mfc_un.res.last_assert +
|
||||
MFC_ASSERT_THRESH)) {
|
||||
c->_c.mfc_un.res.last_assert = jiffies;
|
||||
ip6mr_cache_report(mrt, skb, true_vifi, MRT6MSG_WRONGMIF);
|
||||
if (mrt->mroute_do_wrvifwhole)
|
||||
if (READ_ONCE(mrt->mroute_do_wrvifwhole))
|
||||
ip6mr_cache_report(mrt, skb, true_vifi,
|
||||
MRT6MSG_WRMIFWHOLE);
|
||||
}
|
||||
|
|
@ -2537,7 +2580,7 @@ static int _ip6mr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb,
|
|||
cmd, flags);
|
||||
}
|
||||
|
||||
static int mr6_msgsize(bool unresolved, int maxvif)
|
||||
static int mr6_msgsize(bool unresolved)
|
||||
{
|
||||
size_t len =
|
||||
NLMSG_ALIGN(sizeof(struct rtmsg))
|
||||
|
|
@ -2550,7 +2593,7 @@ static int mr6_msgsize(bool unresolved, int maxvif)
|
|||
len = len
|
||||
+ nla_total_size(4) /* RTA_IIF */
|
||||
+ nla_total_size(0) /* RTA_MULTIPATH */
|
||||
+ maxvif * NLA_ALIGN(sizeof(struct rtnexthop))
|
||||
+ MAXMIFS * NLA_ALIGN(sizeof(struct rtnexthop))
|
||||
/* RTA_MFC_STATS */
|
||||
+ nla_total_size_64bit(sizeof(struct rta_mfc_stats))
|
||||
;
|
||||
|
|
@ -2565,8 +2608,7 @@ static void mr6_netlink_event(struct mr_table *mrt, struct mfc6_cache *mfc,
|
|||
struct sk_buff *skb;
|
||||
int err = -ENOBUFS;
|
||||
|
||||
skb = nlmsg_new(mr6_msgsize(mfc->_c.mfc_parent >= MAXMIFS, mrt->maxvif),
|
||||
GFP_ATOMIC);
|
||||
skb = nlmsg_new(mr6_msgsize(mfc->_c.mfc_parent >= MAXMIFS), GFP_ATOMIC);
|
||||
if (!skb)
|
||||
goto errout;
|
||||
|
||||
|
|
@ -2701,54 +2743,60 @@ static int ip6mr_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh,
|
|||
if (err < 0)
|
||||
return err;
|
||||
|
||||
skb = nlmsg_new(mr6_msgsize(false), GFP_KERNEL);
|
||||
if (!skb)
|
||||
return -ENOBUFS;
|
||||
|
||||
if (tb[RTA_SRC])
|
||||
src = nla_get_in6_addr(tb[RTA_SRC]);
|
||||
if (tb[RTA_DST])
|
||||
grp = nla_get_in6_addr(tb[RTA_DST]);
|
||||
tableid = nla_get_u32_default(tb[RTA_TABLE], 0);
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
mrt = __ip6mr_get_table(net, tableid ?: RT_TABLE_DEFAULT);
|
||||
if (!mrt) {
|
||||
NL_SET_ERR_MSG_MOD(extack, "MR table does not exist");
|
||||
return -ENOENT;
|
||||
err = -ENOENT;
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* entries are added/deleted only under RTNL */
|
||||
rcu_read_lock();
|
||||
cache = ip6mr_cache_find(mrt, &src, &grp);
|
||||
rcu_read_unlock();
|
||||
if (!cache) {
|
||||
NL_SET_ERR_MSG_MOD(extack, "MR cache entry not found");
|
||||
return -ENOENT;
|
||||
err = -ENOENT;
|
||||
goto err;
|
||||
}
|
||||
|
||||
skb = nlmsg_new(mr6_msgsize(false, mrt->maxvif), GFP_KERNEL);
|
||||
if (!skb)
|
||||
return -ENOBUFS;
|
||||
|
||||
err = ip6mr_fill_mroute(mrt, skb, NETLINK_CB(in_skb).portid,
|
||||
nlh->nlmsg_seq, cache, RTM_NEWROUTE, 0);
|
||||
if (err < 0) {
|
||||
kfree_skb(skb);
|
||||
return err;
|
||||
}
|
||||
if (err < 0)
|
||||
goto err;
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
return rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
|
||||
|
||||
err:
|
||||
rcu_read_unlock();
|
||||
kfree_skb(skb);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int ip6mr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb)
|
||||
{
|
||||
const struct nlmsghdr *nlh = cb->nlh;
|
||||
struct fib_dump_filter filter = {
|
||||
.rtnl_held = true,
|
||||
};
|
||||
struct fib_dump_filter filter = {};
|
||||
int err;
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
if (cb->strict_check) {
|
||||
err = ip_valid_fib_dump_req(sock_net(skb->sk), nlh,
|
||||
&filter, cb);
|
||||
if (err < 0)
|
||||
return err;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
if (filter.table_id) {
|
||||
|
|
@ -2756,17 +2804,26 @@ static int ip6mr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb)
|
|||
|
||||
mrt = __ip6mr_get_table(sock_net(skb->sk), filter.table_id);
|
||||
if (!mrt) {
|
||||
if (rtnl_msg_family(cb->nlh) != RTNL_FAMILY_IP6MR)
|
||||
return skb->len;
|
||||
if (rtnl_msg_family(cb->nlh) != RTNL_FAMILY_IP6MR) {
|
||||
err = skb->len;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
NL_SET_ERR_MSG_MOD(cb->extack, "MR table does not exist");
|
||||
return -ENOENT;
|
||||
err = -ENOENT;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
err = mr_table_dump(mrt, skb, cb, _ip6mr_fill_mroute,
|
||||
&mfc_unres_lock, &filter);
|
||||
return skb->len ? : err;
|
||||
err = skb->len ? : err;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
return mr_rtm_dumproute(skb, cb, ip6mr_mr_table_iter,
|
||||
_ip6mr_fill_mroute, &mfc_unres_lock, &filter);
|
||||
err = mr_rtm_dumproute(skb, cb, ip6mr_mr_table_iter,
|
||||
_ip6mr_fill_mroute, &mfc_unres_lock, &filter);
|
||||
unlock:
|
||||
rcu_read_unlock();
|
||||
|
||||
return err;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2221,12 +2221,10 @@ static bool mpls_rt_uses_dev(struct mpls_route *rt,
|
|||
|
||||
static int mpls_dump_routes(struct sk_buff *skb, struct netlink_callback *cb)
|
||||
{
|
||||
struct mpls_route __rcu **platform_label;
|
||||
const struct nlmsghdr *nlh = cb->nlh;
|
||||
struct net *net = sock_net(skb->sk);
|
||||
struct mpls_route __rcu **platform_label;
|
||||
struct fib_dump_filter filter = {
|
||||
.rtnl_held = false,
|
||||
};
|
||||
struct fib_dump_filter filter = {};
|
||||
unsigned int flags = NLM_F_MULTI;
|
||||
size_t platform_labels;
|
||||
unsigned int index;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,9 @@
|
|||
/* Copyright 2026 Google LLC */
|
||||
|
||||
#include <linux/if.h>
|
||||
#include <linux/in6.h>
|
||||
#include <linux/mroute.h>
|
||||
#include <linux/mroute6.h>
|
||||
#include <linux/netlink.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
#include <linux/socket.h>
|
||||
|
|
@ -17,6 +19,14 @@ FIXTURE(ipmr)
|
|||
int netlink_sk;
|
||||
int raw_sk;
|
||||
int veth_ifindex;
|
||||
union {
|
||||
struct vifctl vif;
|
||||
struct mif6ctl vif6;
|
||||
};
|
||||
union {
|
||||
struct mfcctl mfc;
|
||||
struct mf6cctl mfc6;
|
||||
};
|
||||
};
|
||||
|
||||
FIXTURE_VARIANT(ipmr)
|
||||
|
|
@ -24,7 +34,14 @@ FIXTURE_VARIANT(ipmr)
|
|||
int family;
|
||||
int protocol;
|
||||
int level;
|
||||
int rtm_family;
|
||||
int opts[MRT_MAX - MRT_BASE + 1];
|
||||
int flush_flags;
|
||||
int vif_size;
|
||||
char vif_check_cmd_pimreg[64];
|
||||
char vif_check_cmd_veth[64];
|
||||
int mfc_size;
|
||||
char mfc_check_cmd[1024];
|
||||
};
|
||||
|
||||
FIXTURE_VARIANT_ADD(ipmr, ipv4)
|
||||
|
|
@ -32,6 +49,7 @@ FIXTURE_VARIANT_ADD(ipmr, ipv4)
|
|||
.family = AF_INET,
|
||||
.protocol = IPPROTO_IGMP,
|
||||
.level = IPPROTO_IP,
|
||||
.rtm_family = RTNL_FAMILY_IPMR,
|
||||
.opts = {
|
||||
MRT_INIT,
|
||||
MRT_DONE,
|
||||
|
|
@ -47,6 +65,44 @@ FIXTURE_VARIANT_ADD(ipmr, ipv4)
|
|||
MRT_DEL_MFC_PROXY,
|
||||
MRT_FLUSH,
|
||||
},
|
||||
.flush_flags = MRT_FLUSH_MFC | MRT_FLUSH_MFC_STATIC |
|
||||
MRT_FLUSH_VIFS | MRT_FLUSH_VIFS_STATIC,
|
||||
.vif_size = sizeof(struct vifctl),
|
||||
.vif_check_cmd_pimreg = "cat /proc/net/ip_mr_vif | grep -q pimreg",
|
||||
.vif_check_cmd_veth = "cat /proc/net/ip_mr_vif | grep -q veth",
|
||||
.mfc_size = sizeof(struct mfcctl),
|
||||
.mfc_check_cmd = "cat /proc/net/ip_mr_cache | grep -q '00000000 00000000'",
|
||||
};
|
||||
|
||||
FIXTURE_VARIANT_ADD(ipmr, ipv6)
|
||||
{
|
||||
.family = AF_INET6,
|
||||
.protocol = IPPROTO_ICMPV6,
|
||||
.level = IPPROTO_IPV6,
|
||||
.rtm_family = RTNL_FAMILY_IP6MR,
|
||||
.opts = {
|
||||
MRT6_INIT,
|
||||
MRT6_DONE,
|
||||
MRT6_ADD_MIF,
|
||||
MRT6_DEL_MIF,
|
||||
MRT6_ADD_MFC,
|
||||
MRT6_DEL_MFC,
|
||||
MRT6_VERSION,
|
||||
MRT6_ASSERT,
|
||||
MRT6_PIM,
|
||||
MRT6_TABLE,
|
||||
MRT6_ADD_MFC_PROXY,
|
||||
MRT6_DEL_MFC_PROXY,
|
||||
MRT6_FLUSH,
|
||||
},
|
||||
.flush_flags = MRT6_FLUSH_MFC | MRT6_FLUSH_MFC_STATIC |
|
||||
MRT6_FLUSH_MIFS | MRT6_FLUSH_MIFS_STATIC,
|
||||
.vif_size = sizeof(struct mif6ctl),
|
||||
.vif_check_cmd_pimreg = "cat /proc/net/ip6_mr_vif | grep -q pim6reg",
|
||||
.vif_check_cmd_veth = "cat /proc/net/ip6_mr_vif | grep -q veth",
|
||||
.mfc_size = sizeof(struct mf6cctl),
|
||||
.mfc_check_cmd = "cat /proc/net/ip6_mr_cache | "
|
||||
"grep -q '0000:0000:0000:0000:0000:0000:0000:0000 0000:0000:0000:0000:0000:0000:0000:0000'",
|
||||
};
|
||||
|
||||
struct mfc_attr {
|
||||
|
|
@ -71,7 +127,9 @@ static struct rtattr *nl_add_rtattr(struct nlmsghdr *nlmsg, struct rtattr *rta,
|
|||
return RTA_NEXT(rta, unused);
|
||||
}
|
||||
|
||||
static int nl_sendmsg_mfc(struct __test_metadata *_metadata, FIXTURE_DATA(ipmr) *self,
|
||||
static int nl_sendmsg_mfc(struct __test_metadata *_metadata,
|
||||
FIXTURE_DATA(ipmr) *self,
|
||||
const FIXTURE_VARIANT(ipmr) *variant,
|
||||
__u16 nlmsg_type, struct mfc_attr *mfc_attr)
|
||||
{
|
||||
struct {
|
||||
|
|
@ -87,7 +145,7 @@ static int nl_sendmsg_mfc(struct __test_metadata *_metadata, FIXTURE_DATA(ipmr)
|
|||
},
|
||||
.rtm = {
|
||||
/* hard requirements in rtm_to_ipmr_mfcc() */
|
||||
.rtm_family = RTNL_FAMILY_IPMR,
|
||||
.rtm_family = variant->rtm_family,
|
||||
.rtm_dst_len = 32,
|
||||
.rtm_type = RTN_MULTICAST,
|
||||
.rtm_scope = RT_SCOPE_UNIVERSE,
|
||||
|
|
@ -144,6 +202,18 @@ FIXTURE_SETUP(ipmr)
|
|||
ASSERT_EQ(0, err);
|
||||
|
||||
self->veth_ifindex = ifr.ifr_ifindex;
|
||||
|
||||
if (variant->family == AF_INET) {
|
||||
self->vif = (struct vifctl){
|
||||
.vifc_flags = VIFF_USE_IFINDEX,
|
||||
.vifc_lcl_ifindex = self->veth_ifindex,
|
||||
};
|
||||
} else {
|
||||
self->vif6 = (struct mif6ctl){
|
||||
.mif6c_flags = 0,
|
||||
.mif6c_pifi = self->veth_ifindex,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
FIXTURE_TEARDOWN(ipmr)
|
||||
|
|
@ -169,41 +239,39 @@ TEST_F(ipmr, mrt_init)
|
|||
|
||||
TEST_F(ipmr, mrt_add_vif_register)
|
||||
{
|
||||
struct vifctl vif = {
|
||||
.vifc_vifi = 0,
|
||||
.vifc_flags = VIFF_REGISTER,
|
||||
};
|
||||
int err;
|
||||
|
||||
memset(&self->vif, 0, variant->vif_size);
|
||||
|
||||
if (variant->family == AF_INET)
|
||||
self->vif.vifc_flags = VIFF_REGISTER;
|
||||
else
|
||||
self->vif6.mif6c_flags = MIFF_REGISTER;
|
||||
|
||||
err = setsockopt(self->raw_sk,
|
||||
variant->level, variant->opts[MRT_ADD_VIF - MRT_BASE],
|
||||
&vif, sizeof(vif));
|
||||
&self->vif, variant->vif_size);
|
||||
ASSERT_EQ(0, err);
|
||||
|
||||
err = system("cat /proc/net/ip_mr_vif | grep -q pimreg");
|
||||
err = system(variant->vif_check_cmd_pimreg);
|
||||
ASSERT_EQ(0, err);
|
||||
|
||||
err = setsockopt(self->raw_sk,
|
||||
variant->level, variant->opts[MRT_DEL_VIF - MRT_BASE],
|
||||
&vif, sizeof(vif));
|
||||
&self->vif, variant->vif_size);
|
||||
ASSERT_EQ(0, err);
|
||||
}
|
||||
|
||||
TEST_F(ipmr, mrt_del_vif_unreg)
|
||||
{
|
||||
struct vifctl vif = {
|
||||
.vifc_vifi = 0,
|
||||
.vifc_flags = VIFF_USE_IFINDEX,
|
||||
.vifc_lcl_ifindex = self->veth_ifindex,
|
||||
};
|
||||
int err;
|
||||
|
||||
err = setsockopt(self->raw_sk,
|
||||
variant->level, variant->opts[MRT_ADD_VIF - MRT_BASE],
|
||||
&vif, sizeof(vif));
|
||||
&self->vif, variant->vif_size);
|
||||
ASSERT_EQ(0, err);
|
||||
|
||||
err = system("cat /proc/net/ip_mr_vif | grep -q veth0");
|
||||
err = system(variant->vif_check_cmd_veth);
|
||||
ASSERT_EQ(0, err);
|
||||
|
||||
/* VIF is removed along with its device. */
|
||||
|
|
@ -213,23 +281,18 @@ TEST_F(ipmr, mrt_del_vif_unreg)
|
|||
/* mrt->vif_table[veth_ifindex]->dev is NULL. */
|
||||
err = setsockopt(self->raw_sk,
|
||||
variant->level, variant->opts[MRT_DEL_VIF - MRT_BASE],
|
||||
&vif, sizeof(vif));
|
||||
&self->vif, variant->vif_size);
|
||||
ASSERT_EQ(-1, err);
|
||||
ASSERT_EQ(EADDRNOTAVAIL, errno);
|
||||
}
|
||||
|
||||
TEST_F(ipmr, mrt_del_vif_netns_dismantle)
|
||||
{
|
||||
struct vifctl vif = {
|
||||
.vifc_vifi = 0,
|
||||
.vifc_flags = VIFF_USE_IFINDEX,
|
||||
.vifc_lcl_ifindex = self->veth_ifindex,
|
||||
};
|
||||
int err;
|
||||
|
||||
err = setsockopt(self->raw_sk,
|
||||
variant->level, variant->opts[MRT_ADD_VIF - MRT_BASE],
|
||||
&vif, sizeof(vif));
|
||||
&self->vif, variant->vif_size);
|
||||
ASSERT_EQ(0, err);
|
||||
|
||||
/* Let cleanup_net() remove veth0 and VIF. */
|
||||
|
|
@ -237,49 +300,42 @@ TEST_F(ipmr, mrt_del_vif_netns_dismantle)
|
|||
|
||||
TEST_F(ipmr, mrt_add_mfc)
|
||||
{
|
||||
struct mfcctl mfc = {};
|
||||
int err;
|
||||
|
||||
/* MRT_ADD_MFC / MRT_ADD_MFC_PROXY does not need vif to exist (unlike netlink). */
|
||||
err = setsockopt(self->raw_sk,
|
||||
variant->level, variant->opts[MRT_ADD_MFC - MRT_BASE],
|
||||
&mfc, sizeof(mfc));
|
||||
&self->mfc, variant->mfc_size);
|
||||
ASSERT_EQ(0, err);
|
||||
|
||||
/* (0.0.0.0 -> 0.0.0.0) */
|
||||
err = system("cat /proc/net/ip_mr_cache | grep -q '00000000 00000000' ");
|
||||
err = system(variant->mfc_check_cmd);
|
||||
ASSERT_EQ(0, err);
|
||||
|
||||
err = setsockopt(self->raw_sk,
|
||||
variant->level, variant->opts[MRT_DEL_MFC - MRT_BASE],
|
||||
&mfc, sizeof(mfc));
|
||||
&self->mfc, variant->mfc_size);
|
||||
}
|
||||
|
||||
TEST_F(ipmr, mrt_add_mfc_proxy)
|
||||
{
|
||||
struct mfcctl mfc = {};
|
||||
int err;
|
||||
|
||||
err = setsockopt(self->raw_sk,
|
||||
variant->level, variant->opts[MRT_ADD_MFC_PROXY - MRT_BASE],
|
||||
&mfc, sizeof(mfc));
|
||||
&self->mfc, variant->mfc_size);
|
||||
ASSERT_EQ(0, err);
|
||||
|
||||
err = system("cat /proc/net/ip_mr_cache | grep -q '00000000 00000000' ");
|
||||
err = system(variant->mfc_check_cmd);
|
||||
ASSERT_EQ(0, err);
|
||||
|
||||
err = setsockopt(self->raw_sk,
|
||||
variant->level, variant->opts[MRT_DEL_MFC_PROXY - MRT_BASE],
|
||||
&mfc, sizeof(mfc));
|
||||
&self->mfc, variant->mfc_size);
|
||||
}
|
||||
|
||||
TEST_F(ipmr, mrt_add_mfc_netlink)
|
||||
{
|
||||
struct vifctl vif = {
|
||||
.vifc_vifi = 0,
|
||||
.vifc_flags = VIFF_USE_IFINDEX,
|
||||
.vifc_lcl_ifindex = self->veth_ifindex,
|
||||
};
|
||||
struct mfc_attr mfc_attr = {
|
||||
.table = RT_TABLE_DEFAULT,
|
||||
.origin = 0,
|
||||
|
|
@ -291,26 +347,21 @@ TEST_F(ipmr, mrt_add_mfc_netlink)
|
|||
|
||||
err = setsockopt(self->raw_sk,
|
||||
variant->level, variant->opts[MRT_ADD_VIF - MRT_BASE],
|
||||
&vif, sizeof(vif));
|
||||
&self->vif, variant->vif_size);
|
||||
ASSERT_EQ(0, err);
|
||||
|
||||
err = nl_sendmsg_mfc(_metadata, self, RTM_NEWROUTE, &mfc_attr);
|
||||
err = nl_sendmsg_mfc(_metadata, self, variant, RTM_NEWROUTE, &mfc_attr);
|
||||
ASSERT_EQ(0, err);
|
||||
|
||||
err = system("cat /proc/net/ip_mr_cache | grep -q '00000000 00000000' ");
|
||||
err = system(variant->mfc_check_cmd);
|
||||
ASSERT_EQ(0, err);
|
||||
|
||||
err = nl_sendmsg_mfc(_metadata, self, RTM_DELROUTE, &mfc_attr);
|
||||
err = nl_sendmsg_mfc(_metadata, self, variant, RTM_DELROUTE, &mfc_attr);
|
||||
ASSERT_EQ(0, err);
|
||||
}
|
||||
|
||||
TEST_F(ipmr, mrt_add_mfc_netlink_proxy)
|
||||
{
|
||||
struct vifctl vif = {
|
||||
.vifc_vifi = 0,
|
||||
.vifc_flags = VIFF_USE_IFINDEX,
|
||||
.vifc_lcl_ifindex = self->veth_ifindex,
|
||||
};
|
||||
struct mfc_attr mfc_attr = {
|
||||
.table = RT_TABLE_DEFAULT,
|
||||
.origin = 0,
|
||||
|
|
@ -322,16 +373,16 @@ TEST_F(ipmr, mrt_add_mfc_netlink_proxy)
|
|||
|
||||
err = setsockopt(self->raw_sk,
|
||||
variant->level, variant->opts[MRT_ADD_VIF - MRT_BASE],
|
||||
&vif, sizeof(vif));
|
||||
&self->vif, variant->vif_size);
|
||||
ASSERT_EQ(0, err);
|
||||
|
||||
err = nl_sendmsg_mfc(_metadata, self, RTM_NEWROUTE, &mfc_attr);
|
||||
err = nl_sendmsg_mfc(_metadata, self, variant, RTM_NEWROUTE, &mfc_attr);
|
||||
ASSERT_EQ(0, err);
|
||||
|
||||
err = system("cat /proc/net/ip_mr_cache | grep -q '00000000 00000000' ");
|
||||
err = system(variant->mfc_check_cmd);
|
||||
ASSERT_EQ(0, err);
|
||||
|
||||
err = nl_sendmsg_mfc(_metadata, self, RTM_DELROUTE, &mfc_attr);
|
||||
err = nl_sendmsg_mfc(_metadata, self, variant, RTM_DELROUTE, &mfc_attr);
|
||||
ASSERT_EQ(0, err);
|
||||
}
|
||||
|
||||
|
|
@ -347,12 +398,12 @@ TEST_F(ipmr, mrt_add_mfc_netlink_no_vif)
|
|||
|
||||
/* netlink always requires RTA_IIF of an existing vif. */
|
||||
mfc_attr.ifindex = 0;
|
||||
err = nl_sendmsg_mfc(_metadata, self, RTM_NEWROUTE, &mfc_attr);
|
||||
err = nl_sendmsg_mfc(_metadata, self, variant, RTM_NEWROUTE, &mfc_attr);
|
||||
ASSERT_EQ(-ENFILE, err);
|
||||
|
||||
/* netlink always requires RTA_IIF of an existing vif. */
|
||||
mfc_attr.ifindex = self->veth_ifindex;
|
||||
err = nl_sendmsg_mfc(_metadata, self, RTM_NEWROUTE, &mfc_attr);
|
||||
err = nl_sendmsg_mfc(_metadata, self, variant, RTM_NEWROUTE, &mfc_attr);
|
||||
ASSERT_EQ(-ENFILE, err);
|
||||
}
|
||||
|
||||
|
|
@ -387,10 +438,10 @@ TEST_F(ipmr, mrt_del_mfc_netlink_netns_dismantle)
|
|||
}
|
||||
|
||||
/* Create a MFC for mrt->vif_table[0]. */
|
||||
err = nl_sendmsg_mfc(_metadata, self, RTM_NEWROUTE, &mfc_attr);
|
||||
err = nl_sendmsg_mfc(_metadata, self, variant, RTM_NEWROUTE, &mfc_attr);
|
||||
ASSERT_EQ(0, err);
|
||||
|
||||
err = system("cat /proc/net/ip_mr_cache | grep -q '00000000 00000000' ");
|
||||
err = system(variant->mfc_check_cmd);
|
||||
ASSERT_EQ(0, err);
|
||||
|
||||
/* Remove mrt->vif_table[0]. */
|
||||
|
|
@ -398,13 +449,13 @@ TEST_F(ipmr, mrt_del_mfc_netlink_netns_dismantle)
|
|||
ASSERT_EQ(0, err);
|
||||
|
||||
/* MFC entry is NOT removed even if the tied VIF is removed... */
|
||||
err = system("cat /proc/net/ip_mr_cache | grep -q '00000000 00000000' ");
|
||||
err = system(variant->mfc_check_cmd);
|
||||
ASSERT_EQ(0, err);
|
||||
|
||||
/* ... and netlink is not capable of removing such an entry
|
||||
* because netlink always requires a valid RTA_IIF ... :/
|
||||
*/
|
||||
err = nl_sendmsg_mfc(_metadata, self, RTM_DELROUTE, &mfc_attr);
|
||||
err = nl_sendmsg_mfc(_metadata, self, variant, RTM_DELROUTE, &mfc_attr);
|
||||
ASSERT_EQ(-ENODEV, err);
|
||||
|
||||
/* It can be removed by setsockopt(), but let cleanup_net() remove this time. */
|
||||
|
|
@ -412,11 +463,6 @@ TEST_F(ipmr, mrt_del_mfc_netlink_netns_dismantle)
|
|||
|
||||
TEST_F(ipmr, mrt_table_flush)
|
||||
{
|
||||
struct vifctl vif = {
|
||||
.vifc_vifi = 0,
|
||||
.vifc_flags = VIFF_USE_IFINDEX,
|
||||
.vifc_lcl_ifindex = self->veth_ifindex,
|
||||
};
|
||||
struct mfc_attr mfc_attr = {
|
||||
.origin = 0,
|
||||
.group = 0,
|
||||
|
|
@ -424,7 +470,7 @@ TEST_F(ipmr, mrt_table_flush)
|
|||
.proxy = false,
|
||||
};
|
||||
int table_id = 92;
|
||||
int err, flags;
|
||||
int err;
|
||||
|
||||
/* Set a random table id rather than RT_TABLE_DEFAULT.
|
||||
* Note that /proc/net/ip_mr_{vif,cache} only supports RT_TABLE_DEFAULT.
|
||||
|
|
@ -436,20 +482,29 @@ TEST_F(ipmr, mrt_table_flush)
|
|||
|
||||
err = setsockopt(self->raw_sk,
|
||||
variant->level, variant->opts[MRT_ADD_VIF - MRT_BASE],
|
||||
&vif, sizeof(vif));
|
||||
&self->vif, variant->vif_size);
|
||||
ASSERT_EQ(0, err);
|
||||
|
||||
mfc_attr.table = table_id;
|
||||
err = nl_sendmsg_mfc(_metadata, self, RTM_NEWROUTE, &mfc_attr);
|
||||
if (variant->family == AF_INET) {
|
||||
mfc_attr.table = table_id;
|
||||
err = nl_sendmsg_mfc(_metadata, self, variant, RTM_NEWROUTE, &mfc_attr);
|
||||
} else {
|
||||
err = setsockopt(self->raw_sk,
|
||||
variant->level, variant->opts[MRT_ADD_MFC - MRT_BASE],
|
||||
&self->mfc, variant->mfc_size);
|
||||
}
|
||||
ASSERT_EQ(0, err);
|
||||
|
||||
/* Flush mrt->vif_table[] and all caches. */
|
||||
flags = MRT_FLUSH_VIFS | MRT_FLUSH_VIFS_STATIC |
|
||||
MRT_FLUSH_MFC | MRT_FLUSH_MFC_STATIC;
|
||||
err = setsockopt(self->raw_sk,
|
||||
variant->level, variant->opts[MRT_FLUSH - MRT_BASE],
|
||||
&flags, sizeof(flags));
|
||||
&variant->flush_flags, sizeof(variant->flush_flags));
|
||||
ASSERT_EQ(0, err);
|
||||
}
|
||||
|
||||
XFAIL_ADD(ipmr, ipv6, mrt_add_mfc_netlink);
|
||||
XFAIL_ADD(ipmr, ipv6, mrt_add_mfc_netlink_proxy);
|
||||
XFAIL_ADD(ipmr, ipv6, mrt_add_mfc_netlink_no_vif);
|
||||
XFAIL_ADD(ipmr, ipv6, mrt_del_mfc_netlink_netns_dismantle);
|
||||
|
||||
TEST_HARNESS_MAIN
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user