12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
27bc570c8SYOSHIFUJI Hideaki /*
37bc570c8SYOSHIFUJI Hideaki * Linux IPv6 multicast routing support for BSD pim6sd
47bc570c8SYOSHIFUJI Hideaki * Based on net/ipv4/ipmr.c.
57bc570c8SYOSHIFUJI Hideaki *
67bc570c8SYOSHIFUJI Hideaki * (c) 2004 Mickael Hoerdt, <hoerdt@clarinet.u-strasbg.fr>
77bc570c8SYOSHIFUJI Hideaki * LSIIT Laboratory, Strasbourg, France
87bc570c8SYOSHIFUJI Hideaki * (c) 2004 Jean-Philippe Andriot, <jean-philippe.andriot@6WIND.com>
97bc570c8SYOSHIFUJI Hideaki * 6WIND, Paris, France
107bc570c8SYOSHIFUJI Hideaki * Copyright (C)2007,2008 USAGI/WIDE Project
117bc570c8SYOSHIFUJI Hideaki * YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
127bc570c8SYOSHIFUJI Hideaki */
137bc570c8SYOSHIFUJI Hideaki
147c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
157bc570c8SYOSHIFUJI Hideaki #include <linux/types.h>
167bc570c8SYOSHIFUJI Hideaki #include <linux/sched.h>
177bc570c8SYOSHIFUJI Hideaki #include <linux/errno.h>
187bc570c8SYOSHIFUJI Hideaki #include <linux/mm.h>
197bc570c8SYOSHIFUJI Hideaki #include <linux/kernel.h>
207bc570c8SYOSHIFUJI Hideaki #include <linux/fcntl.h>
217bc570c8SYOSHIFUJI Hideaki #include <linux/stat.h>
227bc570c8SYOSHIFUJI Hideaki #include <linux/socket.h>
237bc570c8SYOSHIFUJI Hideaki #include <linux/inet.h>
247bc570c8SYOSHIFUJI Hideaki #include <linux/netdevice.h>
257bc570c8SYOSHIFUJI Hideaki #include <linux/inetdevice.h>
267bc570c8SYOSHIFUJI Hideaki #include <linux/proc_fs.h>
277bc570c8SYOSHIFUJI Hideaki #include <linux/seq_file.h>
287bc570c8SYOSHIFUJI Hideaki #include <linux/init.h>
29e2d57766SDavid S. Miller #include <linux/compat.h>
300eb71a9dSNeilBrown #include <linux/rhashtable.h>
317bc570c8SYOSHIFUJI Hideaki #include <net/protocol.h>
327bc570c8SYOSHIFUJI Hideaki #include <linux/skbuff.h>
337bc570c8SYOSHIFUJI Hideaki #include <net/raw.h>
347bc570c8SYOSHIFUJI Hideaki #include <linux/notifier.h>
357bc570c8SYOSHIFUJI Hideaki #include <linux/if_arp.h>
367bc570c8SYOSHIFUJI Hideaki #include <net/checksum.h>
377bc570c8SYOSHIFUJI Hideaki #include <net/netlink.h>
38d1db275dSPatrick McHardy #include <net/fib_rules.h>
397bc570c8SYOSHIFUJI Hideaki
407bc570c8SYOSHIFUJI Hideaki #include <net/ipv6.h>
417bc570c8SYOSHIFUJI Hideaki #include <net/ip6_route.h>
427bc570c8SYOSHIFUJI Hideaki #include <linux/mroute6.h>
4314fb64e1SYOSHIFUJI Hideaki #include <linux/pim.h>
447bc570c8SYOSHIFUJI Hideaki #include <net/addrconf.h>
457bc570c8SYOSHIFUJI Hideaki #include <linux/netfilter_ipv6.h>
46bc3b2d7fSPaul Gortmaker #include <linux/export.h>
475d6e430dSDave Jones #include <net/ip6_checksum.h>
48d67b8c61SNicolas Dichtel #include <linux/netconf.h>
49cb9f1b78SWillem de Bruijn #include <net/ip_tunnels.h>
507bc570c8SYOSHIFUJI Hideaki
5169d2c867SGustavo A. R. Silva #include <linux/nospec.h>
5269d2c867SGustavo A. R. Silva
53d1db275dSPatrick McHardy struct ip6mr_rule {
54d1db275dSPatrick McHardy struct fib_rule common;
55d1db275dSPatrick McHardy };
56d1db275dSPatrick McHardy
57d1db275dSPatrick McHardy struct ip6mr_result {
58b70432f7SYuval Mintz struct mr_table *mrt;
59d1db275dSPatrick McHardy };
60d1db275dSPatrick McHardy
617bc570c8SYOSHIFUJI Hideaki /* Big lock, protecting vif table, mrt cache and mroute socket state.
627bc570c8SYOSHIFUJI Hideaki Note that the changes are semaphored via rtnl_lock.
637bc570c8SYOSHIFUJI Hideaki */
647bc570c8SYOSHIFUJI Hideaki
65a96f7a6aSEric Dumazet static DEFINE_SPINLOCK(mrt_lock);
667bc570c8SYOSHIFUJI Hideaki
vif_dev_read(const struct vif_device * vif)67ebc31979SEric Dumazet static struct net_device *vif_dev_read(const struct vif_device *vif)
68ebc31979SEric Dumazet {
69a96f7a6aSEric Dumazet return rcu_dereference(vif->dev);
70ebc31979SEric Dumazet }
71ebc31979SEric Dumazet
72b70432f7SYuval Mintz /* Multicast router control variables */
737bc570c8SYOSHIFUJI Hideaki
747bc570c8SYOSHIFUJI Hideaki /* Special spinlock for queue of unresolved entries */
757bc570c8SYOSHIFUJI Hideaki static DEFINE_SPINLOCK(mfc_unres_lock);
767bc570c8SYOSHIFUJI Hideaki
777bc570c8SYOSHIFUJI Hideaki /* We return to original Alan's scheme. Hash table of resolved
787bc570c8SYOSHIFUJI Hideaki entries is changed only in process context and protected
797bc570c8SYOSHIFUJI Hideaki with weak lock mrt_lock. Queue of unresolved entries is protected
807bc570c8SYOSHIFUJI Hideaki with strong spinlock mfc_unres_lock.
817bc570c8SYOSHIFUJI Hideaki
827bc570c8SYOSHIFUJI Hideaki In this case data path is free of exclusive locks at all.
837bc570c8SYOSHIFUJI Hideaki */
847bc570c8SYOSHIFUJI Hideaki
857bc570c8SYOSHIFUJI Hideaki static struct kmem_cache *mrt_cachep __read_mostly;
867bc570c8SYOSHIFUJI Hideaki
87b70432f7SYuval Mintz static struct mr_table *ip6mr_new_table(struct net *net, u32 id);
88b70432f7SYuval Mintz static void ip6mr_free_table(struct mr_table *mrt);
89d1db275dSPatrick McHardy
90b70432f7SYuval Mintz static void ip6_mr_forward(struct net *net, struct mr_table *mrt,
91e4a38c0cSPatrick Ruddy struct net_device *dev, struct sk_buff *skb,
92e4a38c0cSPatrick Ruddy struct mfc6_cache *cache);
933493a5b7SEric Dumazet static int ip6mr_cache_report(const struct mr_table *mrt, struct sk_buff *pkt,
948229efdaSBenjamin Thery mifi_t mifi, int assert);
95b70432f7SYuval Mintz static void mr6_netlink_event(struct mr_table *mrt, struct mfc6_cache *mfc,
96812e44ddSNicolas Dichtel int cmd);
973493a5b7SEric Dumazet static void mrt6msg_netlink_event(const struct mr_table *mrt, struct sk_buff *pkt);
98d7c31cbdSDavid Lamparter static int ip6mr_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh,
99d7c31cbdSDavid Lamparter struct netlink_ext_ack *extack);
1005b285cacSPatrick McHardy static int ip6mr_rtm_dumproute(struct sk_buff *skb,
1015b285cacSPatrick McHardy struct netlink_callback *cb);
102ca8d4794SCallum Sinclair static void mroute_clean_tables(struct mr_table *mrt, int flags);
103e99e88a9SKees Cook static void ipmr_expire_process(struct timer_list *t);
1047bc570c8SYOSHIFUJI Hideaki
105d1db275dSPatrick McHardy #ifdef CONFIG_IPV6_MROUTE_MULTIPLE_TABLES
1068ffb335eSEric Dumazet #define ip6mr_for_each_table(mrt, net) \
10728b380e2SAmol Grover list_for_each_entry_rcu(mrt, &net->ipv6.mr6_tables, list, \
108b6dd5acdSMadhuparna Bhowmik lockdep_rtnl_is_held() || \
109b6dd5acdSMadhuparna Bhowmik list_empty(&net->ipv6.mr6_tables))
110d1db275dSPatrick McHardy
ip6mr_mr_table_iter(struct net * net,struct mr_table * mrt)1117b0db857SYuval Mintz static struct mr_table *ip6mr_mr_table_iter(struct net *net,
1127b0db857SYuval Mintz struct mr_table *mrt)
1137b0db857SYuval Mintz {
1147b0db857SYuval Mintz struct mr_table *ret;
1157b0db857SYuval Mintz
1167b0db857SYuval Mintz if (!mrt)
1177b0db857SYuval Mintz ret = list_entry_rcu(net->ipv6.mr6_tables.next,
1187b0db857SYuval Mintz struct mr_table, list);
1197b0db857SYuval Mintz else
1207b0db857SYuval Mintz ret = list_entry_rcu(mrt->list.next,
1217b0db857SYuval Mintz struct mr_table, list);
1227b0db857SYuval Mintz
1237b0db857SYuval Mintz if (&ret->list == &net->ipv6.mr6_tables)
1247b0db857SYuval Mintz return NULL;
1257b0db857SYuval Mintz return ret;
1267b0db857SYuval Mintz }
1277b0db857SYuval Mintz
__ip6mr_get_table(struct net * net,u32 id)128bba7909bSPaolo Abeni static struct mr_table *__ip6mr_get_table(struct net *net, u32 id)
129d1db275dSPatrick McHardy {
130b70432f7SYuval Mintz struct mr_table *mrt;
131d1db275dSPatrick McHardy
132d1db275dSPatrick McHardy ip6mr_for_each_table(mrt, net) {
133d1db275dSPatrick McHardy if (mrt->id == id)
134d1db275dSPatrick McHardy return mrt;
135d1db275dSPatrick McHardy }
136d1db275dSPatrick McHardy return NULL;
137d1db275dSPatrick McHardy }
138d1db275dSPatrick McHardy
ip6mr_get_table(struct net * net,u32 id)139bba7909bSPaolo Abeni static struct mr_table *ip6mr_get_table(struct net *net, u32 id)
140bba7909bSPaolo Abeni {
141bba7909bSPaolo Abeni struct mr_table *mrt;
142bba7909bSPaolo Abeni
143bba7909bSPaolo Abeni rcu_read_lock();
144bba7909bSPaolo Abeni mrt = __ip6mr_get_table(net, id);
145bba7909bSPaolo Abeni rcu_read_unlock();
146bba7909bSPaolo Abeni return mrt;
147bba7909bSPaolo Abeni }
148bba7909bSPaolo Abeni
ip6mr_fib_lookup(struct net * net,struct flowi6 * flp6,struct mr_table ** mrt)1494c9483b2SDavid S. Miller static int ip6mr_fib_lookup(struct net *net, struct flowi6 *flp6,
150b70432f7SYuval Mintz struct mr_table **mrt)
151d1db275dSPatrick McHardy {
152d1db275dSPatrick McHardy int err;
15395f4a45dSHannes Frederic Sowa struct ip6mr_result res;
15495f4a45dSHannes Frederic Sowa struct fib_lookup_arg arg = {
15595f4a45dSHannes Frederic Sowa .result = &res,
15695f4a45dSHannes Frederic Sowa .flags = FIB_LOOKUP_NOREF,
15795f4a45dSHannes Frederic Sowa };
158d1db275dSPatrick McHardy
159e4a38c0cSPatrick Ruddy /* update flow if oif or iif point to device enslaved to l3mdev */
160e4a38c0cSPatrick Ruddy l3mdev_update_flow(net, flowi6_to_flowi(flp6));
161e4a38c0cSPatrick Ruddy
1624c9483b2SDavid S. Miller err = fib_rules_lookup(net->ipv6.mr6_rules_ops,
1634c9483b2SDavid S. Miller flowi6_to_flowi(flp6), 0, &arg);
164d1db275dSPatrick McHardy if (err < 0)
165d1db275dSPatrick McHardy return err;
166d1db275dSPatrick McHardy *mrt = res.mrt;
167d1db275dSPatrick McHardy return 0;
168d1db275dSPatrick McHardy }
169d1db275dSPatrick McHardy
ip6mr_rule_action(struct fib_rule * rule,struct flowi * flp,int flags,struct fib_lookup_arg * arg)170d1db275dSPatrick McHardy static int ip6mr_rule_action(struct fib_rule *rule, struct flowi *flp,
171d1db275dSPatrick McHardy int flags, struct fib_lookup_arg *arg)
172d1db275dSPatrick McHardy {
173d1db275dSPatrick McHardy struct ip6mr_result *res = arg->result;
174b70432f7SYuval Mintz struct mr_table *mrt;
175d1db275dSPatrick McHardy
176d1db275dSPatrick McHardy switch (rule->action) {
177d1db275dSPatrick McHardy case FR_ACT_TO_TBL:
178d1db275dSPatrick McHardy break;
179d1db275dSPatrick McHardy case FR_ACT_UNREACHABLE:
180d1db275dSPatrick McHardy return -ENETUNREACH;
181d1db275dSPatrick McHardy case FR_ACT_PROHIBIT:
182d1db275dSPatrick McHardy return -EACCES;
183d1db275dSPatrick McHardy case FR_ACT_BLACKHOLE:
184d1db275dSPatrick McHardy default:
185d1db275dSPatrick McHardy return -EINVAL;
186d1db275dSPatrick McHardy }
187d1db275dSPatrick McHardy
188e4a38c0cSPatrick Ruddy arg->table = fib_rule_get_table(rule, arg);
189e4a38c0cSPatrick Ruddy
190bba7909bSPaolo Abeni mrt = __ip6mr_get_table(rule->fr_net, arg->table);
19163159f29SIan Morris if (!mrt)
192d1db275dSPatrick McHardy return -EAGAIN;
193d1db275dSPatrick McHardy res->mrt = mrt;
194d1db275dSPatrick McHardy return 0;
195d1db275dSPatrick McHardy }
196d1db275dSPatrick McHardy
ip6mr_rule_match(struct fib_rule * rule,struct flowi * flp,int flags)197d1db275dSPatrick McHardy static int ip6mr_rule_match(struct fib_rule *rule, struct flowi *flp, int flags)
198d1db275dSPatrick McHardy {
199d1db275dSPatrick McHardy return 1;
200d1db275dSPatrick McHardy }
201d1db275dSPatrick McHardy
ip6mr_rule_configure(struct fib_rule * rule,struct sk_buff * skb,struct fib_rule_hdr * frh,struct nlattr ** tb,struct netlink_ext_ack * extack)202d1db275dSPatrick McHardy static int ip6mr_rule_configure(struct fib_rule *rule, struct sk_buff *skb,
203b16fb418SRoopa Prabhu struct fib_rule_hdr *frh, struct nlattr **tb,
204b16fb418SRoopa Prabhu struct netlink_ext_ack *extack)
205d1db275dSPatrick McHardy {
206d1db275dSPatrick McHardy return 0;
207d1db275dSPatrick McHardy }
208d1db275dSPatrick McHardy
ip6mr_rule_compare(struct fib_rule * rule,struct fib_rule_hdr * frh,struct nlattr ** tb)209d1db275dSPatrick McHardy static int ip6mr_rule_compare(struct fib_rule *rule, struct fib_rule_hdr *frh,
210d1db275dSPatrick McHardy struct nlattr **tb)
211d1db275dSPatrick McHardy {
212d1db275dSPatrick McHardy return 1;
213d1db275dSPatrick McHardy }
214d1db275dSPatrick McHardy
ip6mr_rule_fill(struct fib_rule * rule,struct sk_buff * skb,struct fib_rule_hdr * frh)215d1db275dSPatrick McHardy static int ip6mr_rule_fill(struct fib_rule *rule, struct sk_buff *skb,
216d1db275dSPatrick McHardy struct fib_rule_hdr *frh)
217d1db275dSPatrick McHardy {
218d1db275dSPatrick McHardy frh->dst_len = 0;
219d1db275dSPatrick McHardy frh->src_len = 0;
220d1db275dSPatrick McHardy frh->tos = 0;
221d1db275dSPatrick McHardy return 0;
222d1db275dSPatrick McHardy }
223d1db275dSPatrick McHardy
22404a6f82cSAndi Kleen static const struct fib_rules_ops __net_initconst ip6mr_rules_ops_template = {
225d1db275dSPatrick McHardy .family = RTNL_FAMILY_IP6MR,
226d1db275dSPatrick McHardy .rule_size = sizeof(struct ip6mr_rule),
227d1db275dSPatrick McHardy .addr_size = sizeof(struct in6_addr),
228d1db275dSPatrick McHardy .action = ip6mr_rule_action,
229d1db275dSPatrick McHardy .match = ip6mr_rule_match,
230d1db275dSPatrick McHardy .configure = ip6mr_rule_configure,
231d1db275dSPatrick McHardy .compare = ip6mr_rule_compare,
232d1db275dSPatrick McHardy .fill = ip6mr_rule_fill,
233d1db275dSPatrick McHardy .nlgroup = RTNLGRP_IPV6_RULE,
234d1db275dSPatrick McHardy .owner = THIS_MODULE,
235d1db275dSPatrick McHardy };
236d1db275dSPatrick McHardy
ip6mr_rules_init(struct net * net)237d1db275dSPatrick McHardy static int __net_init ip6mr_rules_init(struct net *net)
238d1db275dSPatrick McHardy {
239d1db275dSPatrick McHardy struct fib_rules_ops *ops;
240b70432f7SYuval Mintz struct mr_table *mrt;
241d1db275dSPatrick McHardy int err;
242d1db275dSPatrick McHardy
243d1db275dSPatrick McHardy ops = fib_rules_register(&ip6mr_rules_ops_template, net);
244d1db275dSPatrick McHardy if (IS_ERR(ops))
245d1db275dSPatrick McHardy return PTR_ERR(ops);
246d1db275dSPatrick McHardy
247d1db275dSPatrick McHardy INIT_LIST_HEAD(&net->ipv6.mr6_tables);
248d1db275dSPatrick McHardy
249d1db275dSPatrick McHardy mrt = ip6mr_new_table(net, RT6_TABLE_DFLT);
250e783bb00SSabrina Dubroca if (IS_ERR(mrt)) {
251e783bb00SSabrina Dubroca err = PTR_ERR(mrt);
252d1db275dSPatrick McHardy goto err1;
253d1db275dSPatrick McHardy }
254d1db275dSPatrick McHardy
255d1db275dSPatrick McHardy err = fib_default_rule_add(ops, 0x7fff, RT6_TABLE_DFLT, 0);
256d1db275dSPatrick McHardy if (err < 0)
257d1db275dSPatrick McHardy goto err2;
258d1db275dSPatrick McHardy
259d1db275dSPatrick McHardy net->ipv6.mr6_rules_ops = ops;
260d1db275dSPatrick McHardy return 0;
261d1db275dSPatrick McHardy
262d1db275dSPatrick McHardy err2:
2635611a006SEric Dumazet rtnl_lock();
264f243e5a7SWANG Cong ip6mr_free_table(mrt);
2655611a006SEric Dumazet rtnl_unlock();
266d1db275dSPatrick McHardy err1:
267d1db275dSPatrick McHardy fib_rules_unregister(ops);
268d1db275dSPatrick McHardy return err;
269d1db275dSPatrick McHardy }
270d1db275dSPatrick McHardy
ip6mr_rules_exit(struct net * net)271d1db275dSPatrick McHardy static void __net_exit ip6mr_rules_exit(struct net *net)
272d1db275dSPatrick McHardy {
273b70432f7SYuval Mintz struct mr_table *mrt, *next;
274d1db275dSPatrick McHardy
275e2f736b7SEric Dumazet ASSERT_RTNL();
276035320d5SEric Dumazet list_for_each_entry_safe(mrt, next, &net->ipv6.mr6_tables, list) {
277035320d5SEric Dumazet list_del(&mrt->list);
278d1db275dSPatrick McHardy ip6mr_free_table(mrt);
279035320d5SEric Dumazet }
280d1db275dSPatrick McHardy fib_rules_unregister(net->ipv6.mr6_rules_ops);
281d1db275dSPatrick McHardy }
282088aa3eeSYuval Mintz
ip6mr_rules_dump(struct net * net,struct notifier_block * nb,struct netlink_ext_ack * extack)283b7a59557SJiri Pirko static int ip6mr_rules_dump(struct net *net, struct notifier_block *nb,
284b7a59557SJiri Pirko struct netlink_ext_ack *extack)
285088aa3eeSYuval Mintz {
286b7a59557SJiri Pirko return fib_rules_dump(net, nb, RTNL_FAMILY_IP6MR, extack);
287088aa3eeSYuval Mintz }
288088aa3eeSYuval Mintz
ip6mr_rules_seq_read(struct net * net)289088aa3eeSYuval Mintz static unsigned int ip6mr_rules_seq_read(struct net *net)
290088aa3eeSYuval Mintz {
291088aa3eeSYuval Mintz return fib_rules_seq_read(net, RTNL_FAMILY_IP6MR);
292088aa3eeSYuval Mintz }
293d3c07e5bSYuval Mintz
ip6mr_rule_default(const struct fib_rule * rule)294d3c07e5bSYuval Mintz bool ip6mr_rule_default(const struct fib_rule *rule)
295d3c07e5bSYuval Mintz {
296d3c07e5bSYuval Mintz return fib_rule_matchall(rule) && rule->action == FR_ACT_TO_TBL &&
297d3c07e5bSYuval Mintz rule->table == RT6_TABLE_DFLT && !rule->l3mdev;
298d3c07e5bSYuval Mintz }
299d3c07e5bSYuval Mintz EXPORT_SYMBOL(ip6mr_rule_default);
300d1db275dSPatrick McHardy #else
301d1db275dSPatrick McHardy #define ip6mr_for_each_table(mrt, net) \
302d1db275dSPatrick McHardy for (mrt = net->ipv6.mrt6; mrt; mrt = NULL)
303d1db275dSPatrick McHardy
ip6mr_mr_table_iter(struct net * net,struct mr_table * mrt)3047b0db857SYuval Mintz static struct mr_table *ip6mr_mr_table_iter(struct net *net,
3057b0db857SYuval Mintz struct mr_table *mrt)
3067b0db857SYuval Mintz {
3077b0db857SYuval Mintz if (!mrt)
3087b0db857SYuval Mintz return net->ipv6.mrt6;
3097b0db857SYuval Mintz return NULL;
3107b0db857SYuval Mintz }
3117b0db857SYuval Mintz
ip6mr_get_table(struct net * net,u32 id)312b70432f7SYuval Mintz static struct mr_table *ip6mr_get_table(struct net *net, u32 id)
313d1db275dSPatrick McHardy {
314d1db275dSPatrick McHardy return net->ipv6.mrt6;
315d1db275dSPatrick McHardy }
316d1db275dSPatrick McHardy
317bba7909bSPaolo Abeni #define __ip6mr_get_table ip6mr_get_table
318bba7909bSPaolo Abeni
ip6mr_fib_lookup(struct net * net,struct flowi6 * flp6,struct mr_table ** mrt)3194c9483b2SDavid S. Miller static int ip6mr_fib_lookup(struct net *net, struct flowi6 *flp6,
320b70432f7SYuval Mintz struct mr_table **mrt)
321d1db275dSPatrick McHardy {
322d1db275dSPatrick McHardy *mrt = net->ipv6.mrt6;
323d1db275dSPatrick McHardy return 0;
324d1db275dSPatrick McHardy }
325d1db275dSPatrick McHardy
ip6mr_rules_init(struct net * net)326d1db275dSPatrick McHardy static int __net_init ip6mr_rules_init(struct net *net)
327d1db275dSPatrick McHardy {
328e783bb00SSabrina Dubroca struct mr_table *mrt;
329e783bb00SSabrina Dubroca
330e783bb00SSabrina Dubroca mrt = ip6mr_new_table(net, RT6_TABLE_DFLT);
331e783bb00SSabrina Dubroca if (IS_ERR(mrt))
332e783bb00SSabrina Dubroca return PTR_ERR(mrt);
333e783bb00SSabrina Dubroca net->ipv6.mrt6 = mrt;
334e783bb00SSabrina Dubroca return 0;
335d1db275dSPatrick McHardy }
336d1db275dSPatrick McHardy
ip6mr_rules_exit(struct net * net)337d1db275dSPatrick McHardy static void __net_exit ip6mr_rules_exit(struct net *net)
338d1db275dSPatrick McHardy {
339e2f736b7SEric Dumazet ASSERT_RTNL();
340d1db275dSPatrick McHardy ip6mr_free_table(net->ipv6.mrt6);
341905a6f96SHannes Frederic Sowa net->ipv6.mrt6 = NULL;
342d1db275dSPatrick McHardy }
343088aa3eeSYuval Mintz
ip6mr_rules_dump(struct net * net,struct notifier_block * nb,struct netlink_ext_ack * extack)344b7a59557SJiri Pirko static int ip6mr_rules_dump(struct net *net, struct notifier_block *nb,
345b7a59557SJiri Pirko struct netlink_ext_ack *extack)
346088aa3eeSYuval Mintz {
347088aa3eeSYuval Mintz return 0;
348088aa3eeSYuval Mintz }
349088aa3eeSYuval Mintz
ip6mr_rules_seq_read(struct net * net)350088aa3eeSYuval Mintz static unsigned int ip6mr_rules_seq_read(struct net *net)
351088aa3eeSYuval Mintz {
352088aa3eeSYuval Mintz return 0;
353088aa3eeSYuval Mintz }
354d1db275dSPatrick McHardy #endif
355d1db275dSPatrick McHardy
ip6mr_hash_cmp(struct rhashtable_compare_arg * arg,const void * ptr)35687c418bfSYuval Mintz static int ip6mr_hash_cmp(struct rhashtable_compare_arg *arg,
35787c418bfSYuval Mintz const void *ptr)
35887c418bfSYuval Mintz {
35987c418bfSYuval Mintz const struct mfc6_cache_cmp_arg *cmparg = arg->key;
36087c418bfSYuval Mintz struct mfc6_cache *c = (struct mfc6_cache *)ptr;
36187c418bfSYuval Mintz
36287c418bfSYuval Mintz return !ipv6_addr_equal(&c->mf6c_mcastgrp, &cmparg->mf6c_mcastgrp) ||
36387c418bfSYuval Mintz !ipv6_addr_equal(&c->mf6c_origin, &cmparg->mf6c_origin);
36487c418bfSYuval Mintz }
36587c418bfSYuval Mintz
36687c418bfSYuval Mintz static const struct rhashtable_params ip6mr_rht_params = {
367494fff56SYuval Mintz .head_offset = offsetof(struct mr_mfc, mnode),
36887c418bfSYuval Mintz .key_offset = offsetof(struct mfc6_cache, cmparg),
36987c418bfSYuval Mintz .key_len = sizeof(struct mfc6_cache_cmp_arg),
37087c418bfSYuval Mintz .nelem_hint = 3,
37187c418bfSYuval Mintz .obj_cmpfn = ip6mr_hash_cmp,
37287c418bfSYuval Mintz .automatic_shrinking = true,
37387c418bfSYuval Mintz };
37487c418bfSYuval Mintz
ip6mr_new_table_set(struct mr_table * mrt,struct net * net)3750bbbf0e7SYuval Mintz static void ip6mr_new_table_set(struct mr_table *mrt,
3760bbbf0e7SYuval Mintz struct net *net)
3770bbbf0e7SYuval Mintz {
3780bbbf0e7SYuval Mintz #ifdef CONFIG_IPV6_MROUTE_MULTIPLE_TABLES
3790bbbf0e7SYuval Mintz list_add_tail_rcu(&mrt->list, &net->ipv6.mr6_tables);
3800bbbf0e7SYuval Mintz #endif
3810bbbf0e7SYuval Mintz }
3820bbbf0e7SYuval Mintz
383845c9a7aSYuval Mintz static struct mfc6_cache_cmp_arg ip6mr_mr_table_ops_cmparg_any = {
384845c9a7aSYuval Mintz .mf6c_origin = IN6ADDR_ANY_INIT,
385845c9a7aSYuval Mintz .mf6c_mcastgrp = IN6ADDR_ANY_INIT,
386845c9a7aSYuval Mintz };
387845c9a7aSYuval Mintz
388845c9a7aSYuval Mintz static struct mr_table_ops ip6mr_mr_table_ops = {
389845c9a7aSYuval Mintz .rht_params = &ip6mr_rht_params,
390845c9a7aSYuval Mintz .cmparg_any = &ip6mr_mr_table_ops_cmparg_any,
391845c9a7aSYuval Mintz };
392845c9a7aSYuval Mintz
ip6mr_new_table(struct net * net,u32 id)393b70432f7SYuval Mintz static struct mr_table *ip6mr_new_table(struct net *net, u32 id)
394d1db275dSPatrick McHardy {
395b70432f7SYuval Mintz struct mr_table *mrt;
396d1db275dSPatrick McHardy
397bba7909bSPaolo Abeni mrt = __ip6mr_get_table(net, id);
39853b24b8fSIan Morris if (mrt)
399d1db275dSPatrick McHardy return mrt;
400d1db275dSPatrick McHardy
401845c9a7aSYuval Mintz return mr_table_alloc(net, id, &ip6mr_mr_table_ops,
4020bbbf0e7SYuval Mintz ipmr_expire_process, ip6mr_new_table_set);
403d1db275dSPatrick McHardy }
404d1db275dSPatrick McHardy
ip6mr_free_table(struct mr_table * mrt)405b70432f7SYuval Mintz static void ip6mr_free_table(struct mr_table *mrt)
406d1db275dSPatrick McHardy {
407292a089dSSteven Rostedt (Google) timer_shutdown_sync(&mrt->ipmr_expire_timer);
408ca8d4794SCallum Sinclair mroute_clean_tables(mrt, MRT6_FLUSH_MIFS | MRT6_FLUSH_MIFS_STATIC |
409ca8d4794SCallum Sinclair MRT6_FLUSH_MFC | MRT6_FLUSH_MFC_STATIC);
410b70432f7SYuval Mintz rhltable_destroy(&mrt->mfc_hash);
411d1db275dSPatrick McHardy kfree(mrt);
412d1db275dSPatrick McHardy }
4137bc570c8SYOSHIFUJI Hideaki
4147bc570c8SYOSHIFUJI Hideaki #ifdef CONFIG_PROC_FS
415c8d61968SYuval Mintz /* The /proc interfaces to multicast routing
416c8d61968SYuval Mintz * /proc/ip6_mr_cache /proc/ip6_mr_vif
4177bc570c8SYOSHIFUJI Hideaki */
4187bc570c8SYOSHIFUJI Hideaki
ip6mr_vif_seq_start(struct seq_file * seq,loff_t * pos)4197bc570c8SYOSHIFUJI Hideaki static void *ip6mr_vif_seq_start(struct seq_file *seq, loff_t *pos)
420b96ef16dSEric Dumazet __acquires(RCU)
4217bc570c8SYOSHIFUJI Hideaki {
4223feda6b4SYuval Mintz struct mr_vif_iter *iter = seq->private;
4238b90fc7eSBenjamin Thery struct net *net = seq_file_net(seq);
424b70432f7SYuval Mintz struct mr_table *mrt;
425d1db275dSPatrick McHardy
426bba7909bSPaolo Abeni rcu_read_lock();
427bba7909bSPaolo Abeni mrt = __ip6mr_get_table(net, RT6_TABLE_DFLT);
428bba7909bSPaolo Abeni if (!mrt) {
429bba7909bSPaolo Abeni rcu_read_unlock();
430d1db275dSPatrick McHardy return ERR_PTR(-ENOENT);
431bba7909bSPaolo Abeni }
432d1db275dSPatrick McHardy
433d1db275dSPatrick McHardy iter->mrt = mrt;
4348b90fc7eSBenjamin Thery
4353feda6b4SYuval Mintz return mr_vif_seq_start(seq, pos);
4367bc570c8SYOSHIFUJI Hideaki }
4377bc570c8SYOSHIFUJI Hideaki
ip6mr_vif_seq_stop(struct seq_file * seq,void * v)4387bc570c8SYOSHIFUJI Hideaki static void ip6mr_vif_seq_stop(struct seq_file *seq, void *v)
439b96ef16dSEric Dumazet __releases(RCU)
4407bc570c8SYOSHIFUJI Hideaki {
441b96ef16dSEric Dumazet rcu_read_unlock();
4427bc570c8SYOSHIFUJI Hideaki }
4437bc570c8SYOSHIFUJI Hideaki
ip6mr_vif_seq_show(struct seq_file * seq,void * v)4447bc570c8SYOSHIFUJI Hideaki static int ip6mr_vif_seq_show(struct seq_file *seq, void *v)
4457bc570c8SYOSHIFUJI Hideaki {
4463feda6b4SYuval Mintz struct mr_vif_iter *iter = seq->private;
447b70432f7SYuval Mintz struct mr_table *mrt = iter->mrt;
4488b90fc7eSBenjamin Thery
4497bc570c8SYOSHIFUJI Hideaki if (v == SEQ_START_TOKEN) {
4507bc570c8SYOSHIFUJI Hideaki seq_puts(seq,
4517bc570c8SYOSHIFUJI Hideaki "Interface BytesIn PktsIn BytesOut PktsOut Flags\n");
4527bc570c8SYOSHIFUJI Hideaki } else {
4536853f21fSYuval Mintz const struct vif_device *vif = v;
454ebc31979SEric Dumazet const struct net_device *vif_dev;
455ebc31979SEric Dumazet const char *name;
456ebc31979SEric Dumazet
457ebc31979SEric Dumazet vif_dev = vif_dev_read(vif);
458ebc31979SEric Dumazet name = vif_dev ? vif_dev->name : "none";
4597bc570c8SYOSHIFUJI Hideaki
4607bc570c8SYOSHIFUJI Hideaki seq_printf(seq,
461d430a227SAl Viro "%2td %-10s %8ld %7ld %8ld %7ld %05X\n",
462b70432f7SYuval Mintz vif - mrt->vif_table,
4637bc570c8SYOSHIFUJI Hideaki name, vif->bytes_in, vif->pkt_in,
4647bc570c8SYOSHIFUJI Hideaki vif->bytes_out, vif->pkt_out,
4657bc570c8SYOSHIFUJI Hideaki vif->flags);
4667bc570c8SYOSHIFUJI Hideaki }
4677bc570c8SYOSHIFUJI Hideaki return 0;
4687bc570c8SYOSHIFUJI Hideaki }
4697bc570c8SYOSHIFUJI Hideaki
47098147d52SStephen Hemminger static const struct seq_operations ip6mr_vif_seq_ops = {
4717bc570c8SYOSHIFUJI Hideaki .start = ip6mr_vif_seq_start,
4723feda6b4SYuval Mintz .next = mr_vif_seq_next,
4737bc570c8SYOSHIFUJI Hideaki .stop = ip6mr_vif_seq_stop,
4747bc570c8SYOSHIFUJI Hideaki .show = ip6mr_vif_seq_show,
4757bc570c8SYOSHIFUJI Hideaki };
4767bc570c8SYOSHIFUJI Hideaki
ipmr_mfc_seq_start(struct seq_file * seq,loff_t * pos)4777bc570c8SYOSHIFUJI Hideaki static void *ipmr_mfc_seq_start(struct seq_file *seq, loff_t *pos)
4787bc570c8SYOSHIFUJI Hideaki {
4798b90fc7eSBenjamin Thery struct net *net = seq_file_net(seq);
480b70432f7SYuval Mintz struct mr_table *mrt;
4818b90fc7eSBenjamin Thery
482d1db275dSPatrick McHardy mrt = ip6mr_get_table(net, RT6_TABLE_DFLT);
48363159f29SIan Morris if (!mrt)
484d1db275dSPatrick McHardy return ERR_PTR(-ENOENT);
485d1db275dSPatrick McHardy
486c8d61968SYuval Mintz return mr_mfc_seq_start(seq, pos, mrt, &mfc_unres_lock);
4877bc570c8SYOSHIFUJI Hideaki }
4887bc570c8SYOSHIFUJI Hideaki
ipmr_mfc_seq_show(struct seq_file * seq,void * v)4897bc570c8SYOSHIFUJI Hideaki static int ipmr_mfc_seq_show(struct seq_file *seq, void *v)
4907bc570c8SYOSHIFUJI Hideaki {
4917bc570c8SYOSHIFUJI Hideaki int n;
4927bc570c8SYOSHIFUJI Hideaki
4937bc570c8SYOSHIFUJI Hideaki if (v == SEQ_START_TOKEN) {
4947bc570c8SYOSHIFUJI Hideaki seq_puts(seq,
4957bc570c8SYOSHIFUJI Hideaki "Group "
4967bc570c8SYOSHIFUJI Hideaki "Origin "
4977bc570c8SYOSHIFUJI Hideaki "Iif Pkts Bytes Wrong Oifs\n");
4987bc570c8SYOSHIFUJI Hideaki } else {
4997bc570c8SYOSHIFUJI Hideaki const struct mfc6_cache *mfc = v;
500c8d61968SYuval Mintz const struct mr_mfc_iter *it = seq->private;
501b70432f7SYuval Mintz struct mr_table *mrt = it->mrt;
5027bc570c8SYOSHIFUJI Hideaki
503999890b2SBenjamin Thery seq_printf(seq, "%pI6 %pI6 %-3hd",
5040c6ce78aSHarvey Harrison &mfc->mf6c_mcastgrp, &mfc->mf6c_origin,
505494fff56SYuval Mintz mfc->_c.mfc_parent);
5061ea472e2SBenjamin Thery
507b70432f7SYuval Mintz if (it->cache != &mrt->mfc_unres_queue) {
5081ea472e2SBenjamin Thery seq_printf(seq, " %8lu %8lu %8lu",
509*5960f4d8SEric Dumazet atomic_long_read(&mfc->_c.mfc_un.res.pkt),
510*5960f4d8SEric Dumazet atomic_long_read(&mfc->_c.mfc_un.res.bytes),
511*5960f4d8SEric Dumazet atomic_long_read(&mfc->_c.mfc_un.res.wrong_if));
512494fff56SYuval Mintz for (n = mfc->_c.mfc_un.res.minvif;
513494fff56SYuval Mintz n < mfc->_c.mfc_un.res.maxvif; n++) {
514b70432f7SYuval Mintz if (VIF_EXISTS(mrt, n) &&
515494fff56SYuval Mintz mfc->_c.mfc_un.res.ttls[n] < 255)
5167bc570c8SYOSHIFUJI Hideaki seq_printf(seq,
517494fff56SYuval Mintz " %2d:%-3d", n,
518494fff56SYuval Mintz mfc->_c.mfc_un.res.ttls[n]);
5197bc570c8SYOSHIFUJI Hideaki }
5201ea472e2SBenjamin Thery } else {
5211ea472e2SBenjamin Thery /* unresolved mfc_caches don't contain
5221ea472e2SBenjamin Thery * pkt, bytes and wrong_if values
5231ea472e2SBenjamin Thery */
5241ea472e2SBenjamin Thery seq_printf(seq, " %8lu %8lu %8lu", 0ul, 0ul, 0ul);
5257bc570c8SYOSHIFUJI Hideaki }
5267bc570c8SYOSHIFUJI Hideaki seq_putc(seq, '\n');
5277bc570c8SYOSHIFUJI Hideaki }
5287bc570c8SYOSHIFUJI Hideaki return 0;
5297bc570c8SYOSHIFUJI Hideaki }
5307bc570c8SYOSHIFUJI Hideaki
53188e9d34cSJames Morris static const struct seq_operations ipmr_mfc_seq_ops = {
5327bc570c8SYOSHIFUJI Hideaki .start = ipmr_mfc_seq_start,
533c8d61968SYuval Mintz .next = mr_mfc_seq_next,
534c8d61968SYuval Mintz .stop = mr_mfc_seq_stop,
5357bc570c8SYOSHIFUJI Hideaki .show = ipmr_mfc_seq_show,
5367bc570c8SYOSHIFUJI Hideaki };
5377bc570c8SYOSHIFUJI Hideaki #endif
5387bc570c8SYOSHIFUJI Hideaki
53914fb64e1SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_PIMSM_V2
54014fb64e1SYOSHIFUJI Hideaki
pim6_rcv(struct sk_buff * skb)54114fb64e1SYOSHIFUJI Hideaki static int pim6_rcv(struct sk_buff *skb)
54214fb64e1SYOSHIFUJI Hideaki {
54314fb64e1SYOSHIFUJI Hideaki struct pimreghdr *pim;
54414fb64e1SYOSHIFUJI Hideaki struct ipv6hdr *encap;
54514fb64e1SYOSHIFUJI Hideaki struct net_device *reg_dev = NULL;
5468229efdaSBenjamin Thery struct net *net = dev_net(skb->dev);
547b70432f7SYuval Mintz struct mr_table *mrt;
5484c9483b2SDavid S. Miller struct flowi6 fl6 = {
5494c9483b2SDavid S. Miller .flowi6_iif = skb->dev->ifindex,
5504c9483b2SDavid S. Miller .flowi6_mark = skb->mark,
551d1db275dSPatrick McHardy };
552d1db275dSPatrick McHardy int reg_vif_num;
55314fb64e1SYOSHIFUJI Hideaki
55414fb64e1SYOSHIFUJI Hideaki if (!pskb_may_pull(skb, sizeof(*pim) + sizeof(*encap)))
55514fb64e1SYOSHIFUJI Hideaki goto drop;
55614fb64e1SYOSHIFUJI Hideaki
55714fb64e1SYOSHIFUJI Hideaki pim = (struct pimreghdr *)skb_transport_header(skb);
55856245caeSNikolay Aleksandrov if (pim->type != ((PIM_VERSION << 4) | PIM_TYPE_REGISTER) ||
55914fb64e1SYOSHIFUJI Hideaki (pim->flags & PIM_NULL_REGISTER) ||
5601d6e55f1SThomas Goff (csum_ipv6_magic(&ipv6_hdr(skb)->saddr, &ipv6_hdr(skb)->daddr,
5611d6e55f1SThomas Goff sizeof(*pim), IPPROTO_PIM,
5621d6e55f1SThomas Goff csum_partial((void *)pim, sizeof(*pim), 0)) &&
563ec6b486fSAl Viro csum_fold(skb_checksum(skb, 0, skb->len, 0))))
56414fb64e1SYOSHIFUJI Hideaki goto drop;
56514fb64e1SYOSHIFUJI Hideaki
56614fb64e1SYOSHIFUJI Hideaki /* check if the inner packet is destined to mcast group */
56714fb64e1SYOSHIFUJI Hideaki encap = (struct ipv6hdr *)(skb_transport_header(skb) +
56814fb64e1SYOSHIFUJI Hideaki sizeof(*pim));
56914fb64e1SYOSHIFUJI Hideaki
57014fb64e1SYOSHIFUJI Hideaki if (!ipv6_addr_is_multicast(&encap->daddr) ||
57114fb64e1SYOSHIFUJI Hideaki encap->payload_len == 0 ||
57214fb64e1SYOSHIFUJI Hideaki ntohs(encap->payload_len) + sizeof(*pim) > skb->len)
57314fb64e1SYOSHIFUJI Hideaki goto drop;
57414fb64e1SYOSHIFUJI Hideaki
5754c9483b2SDavid S. Miller if (ip6mr_fib_lookup(net, &fl6, &mrt) < 0)
576d1db275dSPatrick McHardy goto drop;
577d1db275dSPatrick McHardy
5786d086587SEric Dumazet /* Pairs with WRITE_ONCE() in mif6_add()/mif6_delete() */
5796d086587SEric Dumazet reg_vif_num = READ_ONCE(mrt->mroute_reg_vif_num);
58014fb64e1SYOSHIFUJI Hideaki if (reg_vif_num >= 0)
581ebc31979SEric Dumazet reg_dev = vif_dev_read(&mrt->vif_table[reg_vif_num]);
58214fb64e1SYOSHIFUJI Hideaki
58363159f29SIan Morris if (!reg_dev)
58414fb64e1SYOSHIFUJI Hideaki goto drop;
58514fb64e1SYOSHIFUJI Hideaki
58614fb64e1SYOSHIFUJI Hideaki skb->mac_header = skb->network_header;
58714fb64e1SYOSHIFUJI Hideaki skb_pull(skb, (u8 *)encap - skb->data);
58814fb64e1SYOSHIFUJI Hideaki skb_reset_network_header(skb);
5891d6e55f1SThomas Goff skb->protocol = htons(ETH_P_IPV6);
5903e49e6d5SCesar Eduardo Barros skb->ip_summed = CHECKSUM_NONE;
591d19d56ddSEric Dumazet
592ea23192eSNicolas Dichtel skb_tunnel_rx(skb, reg_dev, dev_net(reg_dev));
593d19d56ddSEric Dumazet
594caf586e5SEric Dumazet netif_rx(skb);
5958990f468SEric Dumazet
59614fb64e1SYOSHIFUJI Hideaki return 0;
59714fb64e1SYOSHIFUJI Hideaki drop:
59814fb64e1SYOSHIFUJI Hideaki kfree_skb(skb);
59914fb64e1SYOSHIFUJI Hideaki return 0;
60014fb64e1SYOSHIFUJI Hideaki }
60114fb64e1SYOSHIFUJI Hideaki
60241135cc8SAlexey Dobriyan static const struct inet6_protocol pim6_protocol = {
60314fb64e1SYOSHIFUJI Hideaki .handler = pim6_rcv,
60414fb64e1SYOSHIFUJI Hideaki };
60514fb64e1SYOSHIFUJI Hideaki
60614fb64e1SYOSHIFUJI Hideaki /* Service routines creating virtual interfaces: PIMREG */
60714fb64e1SYOSHIFUJI Hideaki
reg_vif_xmit(struct sk_buff * skb,struct net_device * dev)6086fef4c0cSStephen Hemminger static netdev_tx_t reg_vif_xmit(struct sk_buff *skb,
6096fef4c0cSStephen Hemminger struct net_device *dev)
61014fb64e1SYOSHIFUJI Hideaki {
6118229efdaSBenjamin Thery struct net *net = dev_net(dev);
612b70432f7SYuval Mintz struct mr_table *mrt;
6134c9483b2SDavid S. Miller struct flowi6 fl6 = {
6144c9483b2SDavid S. Miller .flowi6_oif = dev->ifindex,
6156a662719SCong Wang .flowi6_iif = skb->skb_iif ? : LOOPBACK_IFINDEX,
6164c9483b2SDavid S. Miller .flowi6_mark = skb->mark,
617d1db275dSPatrick McHardy };
618d1db275dSPatrick McHardy
619cb9f1b78SWillem de Bruijn if (!pskb_inet_may_pull(skb))
620cb9f1b78SWillem de Bruijn goto tx_err;
621cb9f1b78SWillem de Bruijn
622cb9f1b78SWillem de Bruijn if (ip6mr_fib_lookup(net, &fl6, &mrt) < 0)
623cb9f1b78SWillem de Bruijn goto tx_err;
6248229efdaSBenjamin Thery
6252fad1ba3SEric Dumazet DEV_STATS_ADD(dev, tx_bytes, skb->len);
6262fad1ba3SEric Dumazet DEV_STATS_INC(dev, tx_packets);
6273493a5b7SEric Dumazet rcu_read_lock();
6283493a5b7SEric Dumazet ip6mr_cache_report(mrt, skb, READ_ONCE(mrt->mroute_reg_vif_num),
6293493a5b7SEric Dumazet MRT6MSG_WHOLEPKT);
6303493a5b7SEric Dumazet rcu_read_unlock();
63114fb64e1SYOSHIFUJI Hideaki kfree_skb(skb);
6326ed10654SPatrick McHardy return NETDEV_TX_OK;
633cb9f1b78SWillem de Bruijn
634cb9f1b78SWillem de Bruijn tx_err:
6352fad1ba3SEric Dumazet DEV_STATS_INC(dev, tx_errors);
636cb9f1b78SWillem de Bruijn kfree_skb(skb);
637cb9f1b78SWillem de Bruijn return NETDEV_TX_OK;
63814fb64e1SYOSHIFUJI Hideaki }
63914fb64e1SYOSHIFUJI Hideaki
reg_vif_get_iflink(const struct net_device * dev)640ee9b9596SNicolas Dichtel static int reg_vif_get_iflink(const struct net_device *dev)
641ee9b9596SNicolas Dichtel {
642ee9b9596SNicolas Dichtel return 0;
643ee9b9596SNicolas Dichtel }
644ee9b9596SNicolas Dichtel
645007c3838SStephen Hemminger static const struct net_device_ops reg_vif_netdev_ops = {
646007c3838SStephen Hemminger .ndo_start_xmit = reg_vif_xmit,
647ee9b9596SNicolas Dichtel .ndo_get_iflink = reg_vif_get_iflink,
648007c3838SStephen Hemminger };
649007c3838SStephen Hemminger
reg_vif_setup(struct net_device * dev)65014fb64e1SYOSHIFUJI Hideaki static void reg_vif_setup(struct net_device *dev)
65114fb64e1SYOSHIFUJI Hideaki {
65214fb64e1SYOSHIFUJI Hideaki dev->type = ARPHRD_PIMREG;
65314fb64e1SYOSHIFUJI Hideaki dev->mtu = 1500 - sizeof(struct ipv6hdr) - 8;
65414fb64e1SYOSHIFUJI Hideaki dev->flags = IFF_NOARP;
655007c3838SStephen Hemminger dev->netdev_ops = ®_vif_netdev_ops;
656cf124db5SDavid S. Miller dev->needs_free_netdev = true;
657403dbb97STom Goff dev->features |= NETIF_F_NETNS_LOCAL;
65814fb64e1SYOSHIFUJI Hideaki }
65914fb64e1SYOSHIFUJI Hideaki
ip6mr_reg_vif(struct net * net,struct mr_table * mrt)660b70432f7SYuval Mintz static struct net_device *ip6mr_reg_vif(struct net *net, struct mr_table *mrt)
66114fb64e1SYOSHIFUJI Hideaki {
66214fb64e1SYOSHIFUJI Hideaki struct net_device *dev;
663d1db275dSPatrick McHardy char name[IFNAMSIZ];
66414fb64e1SYOSHIFUJI Hideaki
665d1db275dSPatrick McHardy if (mrt->id == RT6_TABLE_DFLT)
666d1db275dSPatrick McHardy sprintf(name, "pim6reg");
667d1db275dSPatrick McHardy else
668d1db275dSPatrick McHardy sprintf(name, "pim6reg%u", mrt->id);
669d1db275dSPatrick McHardy
670c835a677STom Gundersen dev = alloc_netdev(0, name, NET_NAME_UNKNOWN, reg_vif_setup);
67163159f29SIan Morris if (!dev)
67214fb64e1SYOSHIFUJI Hideaki return NULL;
67314fb64e1SYOSHIFUJI Hideaki
6748229efdaSBenjamin Thery dev_net_set(dev, net);
6758229efdaSBenjamin Thery
67614fb64e1SYOSHIFUJI Hideaki if (register_netdevice(dev)) {
67714fb64e1SYOSHIFUJI Hideaki free_netdev(dev);
67814fb64e1SYOSHIFUJI Hideaki return NULL;
67914fb64e1SYOSHIFUJI Hideaki }
68014fb64e1SYOSHIFUJI Hideaki
68100f54e68SPetr Machata if (dev_open(dev, NULL))
68214fb64e1SYOSHIFUJI Hideaki goto failure;
68314fb64e1SYOSHIFUJI Hideaki
6847af3db78SWang Chen dev_hold(dev);
68514fb64e1SYOSHIFUJI Hideaki return dev;
68614fb64e1SYOSHIFUJI Hideaki
68714fb64e1SYOSHIFUJI Hideaki failure:
68814fb64e1SYOSHIFUJI Hideaki unregister_netdevice(dev);
68914fb64e1SYOSHIFUJI Hideaki return NULL;
69014fb64e1SYOSHIFUJI Hideaki }
69114fb64e1SYOSHIFUJI Hideaki #endif
69214fb64e1SYOSHIFUJI Hideaki
call_ip6mr_vif_entry_notifiers(struct net * net,enum fib_event_type event_type,struct vif_device * vif,struct net_device * vif_dev,mifi_t vif_index,u32 tb_id)693088aa3eeSYuval Mintz static int call_ip6mr_vif_entry_notifiers(struct net *net,
694088aa3eeSYuval Mintz enum fib_event_type event_type,
695088aa3eeSYuval Mintz struct vif_device *vif,
696ebc31979SEric Dumazet struct net_device *vif_dev,
697088aa3eeSYuval Mintz mifi_t vif_index, u32 tb_id)
698088aa3eeSYuval Mintz {
699088aa3eeSYuval Mintz return mr_call_vif_notifiers(net, RTNL_FAMILY_IP6MR, event_type,
700ebc31979SEric Dumazet vif, vif_dev, vif_index, tb_id,
701088aa3eeSYuval Mintz &net->ipv6.ipmr_seq);
702088aa3eeSYuval Mintz }
7037bc570c8SYOSHIFUJI Hideaki
call_ip6mr_mfc_entry_notifiers(struct net * net,enum fib_event_type event_type,struct mfc6_cache * mfc,u32 tb_id)704088aa3eeSYuval Mintz static int call_ip6mr_mfc_entry_notifiers(struct net *net,
705088aa3eeSYuval Mintz enum fib_event_type event_type,
706088aa3eeSYuval Mintz struct mfc6_cache *mfc, u32 tb_id)
707088aa3eeSYuval Mintz {
708088aa3eeSYuval Mintz return mr_call_mfc_notifiers(net, RTNL_FAMILY_IP6MR, event_type,
709088aa3eeSYuval Mintz &mfc->_c, tb_id, &net->ipv6.ipmr_seq);
710088aa3eeSYuval Mintz }
711088aa3eeSYuval Mintz
712088aa3eeSYuval Mintz /* Delete a VIF entry */
mif6_delete(struct mr_table * mrt,int vifi,int notify,struct list_head * head)713b70432f7SYuval Mintz static int mif6_delete(struct mr_table *mrt, int vifi, int notify,
714723b929cSNikolay Aleksandrov struct list_head *head)
7157bc570c8SYOSHIFUJI Hideaki {
7166853f21fSYuval Mintz struct vif_device *v;
7177bc570c8SYOSHIFUJI Hideaki struct net_device *dev;
7181d6e55f1SThomas Goff struct inet6_dev *in6_dev;
7196bd52143SPatrick McHardy
7206bd52143SPatrick McHardy if (vifi < 0 || vifi >= mrt->maxvif)
7217bc570c8SYOSHIFUJI Hideaki return -EADDRNOTAVAIL;
7227bc570c8SYOSHIFUJI Hideaki
723b70432f7SYuval Mintz v = &mrt->vif_table[vifi];
7247bc570c8SYOSHIFUJI Hideaki
725ebc31979SEric Dumazet dev = rtnl_dereference(v->dev);
726ebc31979SEric Dumazet if (!dev)
7277bc570c8SYOSHIFUJI Hideaki return -EADDRNOTAVAIL;
728ebc31979SEric Dumazet
729ebc31979SEric Dumazet call_ip6mr_vif_entry_notifiers(read_pnet(&mrt->net),
730ebc31979SEric Dumazet FIB_EVENT_VIF_DEL, v, dev,
731ebc31979SEric Dumazet vifi, mrt->id);
732a96f7a6aSEric Dumazet spin_lock(&mrt_lock);
733ebc31979SEric Dumazet RCU_INIT_POINTER(v->dev, NULL);
7347bc570c8SYOSHIFUJI Hideaki
73514fb64e1SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_PIMSM_V2
7363493a5b7SEric Dumazet if (vifi == mrt->mroute_reg_vif_num) {
7373493a5b7SEric Dumazet /* Pairs with READ_ONCE() in ip6mr_cache_report() and reg_vif_xmit() */
7383493a5b7SEric Dumazet WRITE_ONCE(mrt->mroute_reg_vif_num, -1);
7393493a5b7SEric Dumazet }
74014fb64e1SYOSHIFUJI Hideaki #endif
74114fb64e1SYOSHIFUJI Hideaki
7426bd52143SPatrick McHardy if (vifi + 1 == mrt->maxvif) {
7437bc570c8SYOSHIFUJI Hideaki int tmp;
7447bc570c8SYOSHIFUJI Hideaki for (tmp = vifi - 1; tmp >= 0; tmp--) {
745b70432f7SYuval Mintz if (VIF_EXISTS(mrt, tmp))
7467bc570c8SYOSHIFUJI Hideaki break;
7477bc570c8SYOSHIFUJI Hideaki }
748db9eb7c8SEric Dumazet WRITE_ONCE(mrt->maxvif, tmp + 1);
7497bc570c8SYOSHIFUJI Hideaki }
7507bc570c8SYOSHIFUJI Hideaki
751a96f7a6aSEric Dumazet spin_unlock(&mrt_lock);
7527bc570c8SYOSHIFUJI Hideaki
7537bc570c8SYOSHIFUJI Hideaki dev_set_allmulti(dev, -1);
7547bc570c8SYOSHIFUJI Hideaki
7551d6e55f1SThomas Goff in6_dev = __in6_dev_get(dev);
756d67b8c61SNicolas Dichtel if (in6_dev) {
757145c7a79SEric Dumazet atomic_dec(&in6_dev->cnf.mc_forwarding);
75885b3daadSDavid Ahern inet6_netconf_notify_devconf(dev_net(dev), RTM_NEWNETCONF,
759d67b8c61SNicolas Dichtel NETCONFA_MC_FORWARDING,
760d67b8c61SNicolas Dichtel dev->ifindex, &in6_dev->cnf);
761d67b8c61SNicolas Dichtel }
7621d6e55f1SThomas Goff
763723b929cSNikolay Aleksandrov if ((v->flags & MIFF_REGISTER) && !notify)
764c871e664SEric Dumazet unregister_netdevice_queue(dev, head);
7657bc570c8SYOSHIFUJI Hideaki
766d62607c3SJakub Kicinski netdev_put(dev, &v->dev_tracker);
7677bc570c8SYOSHIFUJI Hideaki return 0;
7687bc570c8SYOSHIFUJI Hideaki }
7697bc570c8SYOSHIFUJI Hideaki
ip6mr_cache_free_rcu(struct rcu_head * head)77087c418bfSYuval Mintz static inline void ip6mr_cache_free_rcu(struct rcu_head *head)
77187c418bfSYuval Mintz {
772494fff56SYuval Mintz struct mr_mfc *c = container_of(head, struct mr_mfc, rcu);
77387c418bfSYuval Mintz
774494fff56SYuval Mintz kmem_cache_free(mrt_cachep, (struct mfc6_cache *)c);
77587c418bfSYuval Mintz }
77687c418bfSYuval Mintz
ip6mr_cache_free(struct mfc6_cache * c)77758701ad4SBenjamin Thery static inline void ip6mr_cache_free(struct mfc6_cache *c)
77858701ad4SBenjamin Thery {
779494fff56SYuval Mintz call_rcu(&c->_c.rcu, ip6mr_cache_free_rcu);
78058701ad4SBenjamin Thery }
78158701ad4SBenjamin Thery
7827bc570c8SYOSHIFUJI Hideaki /* Destroy an unresolved cache entry, killing queued skbs
7837bc570c8SYOSHIFUJI Hideaki and reporting error to netlink readers.
7847bc570c8SYOSHIFUJI Hideaki */
7857bc570c8SYOSHIFUJI Hideaki
ip6mr_destroy_unres(struct mr_table * mrt,struct mfc6_cache * c)786b70432f7SYuval Mintz static void ip6mr_destroy_unres(struct mr_table *mrt, struct mfc6_cache *c)
7877bc570c8SYOSHIFUJI Hideaki {
7886bd52143SPatrick McHardy struct net *net = read_pnet(&mrt->net);
7897bc570c8SYOSHIFUJI Hideaki struct sk_buff *skb;
7907bc570c8SYOSHIFUJI Hideaki
7916bd52143SPatrick McHardy atomic_dec(&mrt->cache_resolve_queue_len);
7927bc570c8SYOSHIFUJI Hideaki
793494fff56SYuval Mintz while ((skb = skb_dequeue(&c->_c.mfc_un.unres.unresolved)) != NULL) {
7947bc570c8SYOSHIFUJI Hideaki if (ipv6_hdr(skb)->version == 0) {
795af72868bSJohannes Berg struct nlmsghdr *nlh = skb_pull(skb,
796af72868bSJohannes Berg sizeof(struct ipv6hdr));
7977bc570c8SYOSHIFUJI Hideaki nlh->nlmsg_type = NLMSG_ERROR;
798573ce260SHong zhi guo nlh->nlmsg_len = nlmsg_msg_size(sizeof(struct nlmsgerr));
7997bc570c8SYOSHIFUJI Hideaki skb_trim(skb, nlh->nlmsg_len);
800573ce260SHong zhi guo ((struct nlmsgerr *)nlmsg_data(nlh))->error = -ETIMEDOUT;
80115e47304SEric W. Biederman rtnl_unicast(skb, net, NETLINK_CB(skb).portid);
8027bc570c8SYOSHIFUJI Hideaki } else
8037bc570c8SYOSHIFUJI Hideaki kfree_skb(skb);
8047bc570c8SYOSHIFUJI Hideaki }
8057bc570c8SYOSHIFUJI Hideaki
80658701ad4SBenjamin Thery ip6mr_cache_free(c);
8077bc570c8SYOSHIFUJI Hideaki }
8087bc570c8SYOSHIFUJI Hideaki
8097bc570c8SYOSHIFUJI Hideaki
810c476efbcSPatrick McHardy /* Timer process for all the unresolved queue. */
8117bc570c8SYOSHIFUJI Hideaki
ipmr_do_expire_process(struct mr_table * mrt)812b70432f7SYuval Mintz static void ipmr_do_expire_process(struct mr_table *mrt)
8137bc570c8SYOSHIFUJI Hideaki {
8147bc570c8SYOSHIFUJI Hideaki unsigned long now = jiffies;
8157bc570c8SYOSHIFUJI Hideaki unsigned long expires = 10 * HZ;
816494fff56SYuval Mintz struct mr_mfc *c, *next;
8177bc570c8SYOSHIFUJI Hideaki
818b70432f7SYuval Mintz list_for_each_entry_safe(c, next, &mrt->mfc_unres_queue, list) {
8197bc570c8SYOSHIFUJI Hideaki if (time_after(c->mfc_un.unres.expires, now)) {
8207bc570c8SYOSHIFUJI Hideaki /* not yet... */
8217bc570c8SYOSHIFUJI Hideaki unsigned long interval = c->mfc_un.unres.expires - now;
8227bc570c8SYOSHIFUJI Hideaki if (interval < expires)
8237bc570c8SYOSHIFUJI Hideaki expires = interval;
8247bc570c8SYOSHIFUJI Hideaki continue;
8257bc570c8SYOSHIFUJI Hideaki }
8267bc570c8SYOSHIFUJI Hideaki
827f30a7784SPatrick McHardy list_del(&c->list);
828494fff56SYuval Mintz mr6_netlink_event(mrt, (struct mfc6_cache *)c, RTM_DELROUTE);
829494fff56SYuval Mintz ip6mr_destroy_unres(mrt, (struct mfc6_cache *)c);
8307bc570c8SYOSHIFUJI Hideaki }
8317bc570c8SYOSHIFUJI Hideaki
832b70432f7SYuval Mintz if (!list_empty(&mrt->mfc_unres_queue))
8336bd52143SPatrick McHardy mod_timer(&mrt->ipmr_expire_timer, jiffies + expires);
8347bc570c8SYOSHIFUJI Hideaki }
8357bc570c8SYOSHIFUJI Hideaki
ipmr_expire_process(struct timer_list * t)836e99e88a9SKees Cook static void ipmr_expire_process(struct timer_list *t)
8377bc570c8SYOSHIFUJI Hideaki {
838b70432f7SYuval Mintz struct mr_table *mrt = from_timer(mrt, t, ipmr_expire_timer);
839c476efbcSPatrick McHardy
8407bc570c8SYOSHIFUJI Hideaki if (!spin_trylock(&mfc_unres_lock)) {
8416bd52143SPatrick McHardy mod_timer(&mrt->ipmr_expire_timer, jiffies + 1);
8427bc570c8SYOSHIFUJI Hideaki return;
8437bc570c8SYOSHIFUJI Hideaki }
8447bc570c8SYOSHIFUJI Hideaki
845b70432f7SYuval Mintz if (!list_empty(&mrt->mfc_unres_queue))
8466bd52143SPatrick McHardy ipmr_do_expire_process(mrt);
8477bc570c8SYOSHIFUJI Hideaki
8487bc570c8SYOSHIFUJI Hideaki spin_unlock(&mfc_unres_lock);
8497bc570c8SYOSHIFUJI Hideaki }
8507bc570c8SYOSHIFUJI Hideaki
851a96f7a6aSEric Dumazet /* Fill oifs list. It is called under locked mrt_lock. */
8527bc570c8SYOSHIFUJI Hideaki
ip6mr_update_thresholds(struct mr_table * mrt,struct mr_mfc * cache,unsigned char * ttls)853b70432f7SYuval Mintz static void ip6mr_update_thresholds(struct mr_table *mrt,
854494fff56SYuval Mintz struct mr_mfc *cache,
855b5aa30b1SPatrick McHardy unsigned char *ttls)
8567bc570c8SYOSHIFUJI Hideaki {
8577bc570c8SYOSHIFUJI Hideaki int vifi;
8587bc570c8SYOSHIFUJI Hideaki
8596ac7eb08SRami Rosen cache->mfc_un.res.minvif = MAXMIFS;
8607bc570c8SYOSHIFUJI Hideaki cache->mfc_un.res.maxvif = 0;
8616ac7eb08SRami Rosen memset(cache->mfc_un.res.ttls, 255, MAXMIFS);
8627bc570c8SYOSHIFUJI Hideaki
8636bd52143SPatrick McHardy for (vifi = 0; vifi < mrt->maxvif; vifi++) {
864b70432f7SYuval Mintz if (VIF_EXISTS(mrt, vifi) &&
8654e16880cSBenjamin Thery ttls[vifi] && ttls[vifi] < 255) {
8667bc570c8SYOSHIFUJI Hideaki cache->mfc_un.res.ttls[vifi] = ttls[vifi];
8677bc570c8SYOSHIFUJI Hideaki if (cache->mfc_un.res.minvif > vifi)
8687bc570c8SYOSHIFUJI Hideaki cache->mfc_un.res.minvif = vifi;
8697bc570c8SYOSHIFUJI Hideaki if (cache->mfc_un.res.maxvif <= vifi)
8707bc570c8SYOSHIFUJI Hideaki cache->mfc_un.res.maxvif = vifi + 1;
8717bc570c8SYOSHIFUJI Hideaki }
8727bc570c8SYOSHIFUJI Hideaki }
873*5960f4d8SEric Dumazet WRITE_ONCE(cache->mfc_un.res.lastuse, jiffies);
8747bc570c8SYOSHIFUJI Hideaki }
8757bc570c8SYOSHIFUJI Hideaki
mif6_add(struct net * net,struct mr_table * mrt,struct mif6ctl * vifc,int mrtsock)876b70432f7SYuval Mintz static int mif6_add(struct net *net, struct mr_table *mrt,
8776bd52143SPatrick McHardy struct mif6ctl *vifc, int mrtsock)
8787bc570c8SYOSHIFUJI Hideaki {
8797bc570c8SYOSHIFUJI Hideaki int vifi = vifc->mif6c_mifi;
880b70432f7SYuval Mintz struct vif_device *v = &mrt->vif_table[vifi];
8817bc570c8SYOSHIFUJI Hideaki struct net_device *dev;
8821d6e55f1SThomas Goff struct inet6_dev *in6_dev;
8835ae7b444SWang Chen int err;
8847bc570c8SYOSHIFUJI Hideaki
8857bc570c8SYOSHIFUJI Hideaki /* Is vif busy ? */
886b70432f7SYuval Mintz if (VIF_EXISTS(mrt, vifi))
8877bc570c8SYOSHIFUJI Hideaki return -EADDRINUSE;
8887bc570c8SYOSHIFUJI Hideaki
8897bc570c8SYOSHIFUJI Hideaki switch (vifc->mif6c_flags) {
89014fb64e1SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_PIMSM_V2
89114fb64e1SYOSHIFUJI Hideaki case MIFF_REGISTER:
89214fb64e1SYOSHIFUJI Hideaki /*
89314fb64e1SYOSHIFUJI Hideaki * Special Purpose VIF in PIM
89414fb64e1SYOSHIFUJI Hideaki * All the packets will be sent to the daemon
89514fb64e1SYOSHIFUJI Hideaki */
8966bd52143SPatrick McHardy if (mrt->mroute_reg_vif_num >= 0)
89714fb64e1SYOSHIFUJI Hideaki return -EADDRINUSE;
898d1db275dSPatrick McHardy dev = ip6mr_reg_vif(net, mrt);
89914fb64e1SYOSHIFUJI Hideaki if (!dev)
90014fb64e1SYOSHIFUJI Hideaki return -ENOBUFS;
9015ae7b444SWang Chen err = dev_set_allmulti(dev, 1);
9025ae7b444SWang Chen if (err) {
9035ae7b444SWang Chen unregister_netdevice(dev);
9047af3db78SWang Chen dev_put(dev);
9055ae7b444SWang Chen return err;
9065ae7b444SWang Chen }
90714fb64e1SYOSHIFUJI Hideaki break;
90814fb64e1SYOSHIFUJI Hideaki #endif
9097bc570c8SYOSHIFUJI Hideaki case 0:
9108229efdaSBenjamin Thery dev = dev_get_by_index(net, vifc->mif6c_pifi);
9117bc570c8SYOSHIFUJI Hideaki if (!dev)
9127bc570c8SYOSHIFUJI Hideaki return -EADDRNOTAVAIL;
9135ae7b444SWang Chen err = dev_set_allmulti(dev, 1);
9147af3db78SWang Chen if (err) {
9157af3db78SWang Chen dev_put(dev);
9165ae7b444SWang Chen return err;
9177af3db78SWang Chen }
9187bc570c8SYOSHIFUJI Hideaki break;
9197bc570c8SYOSHIFUJI Hideaki default:
9207bc570c8SYOSHIFUJI Hideaki return -EINVAL;
9217bc570c8SYOSHIFUJI Hideaki }
9227bc570c8SYOSHIFUJI Hideaki
9231d6e55f1SThomas Goff in6_dev = __in6_dev_get(dev);
924d67b8c61SNicolas Dichtel if (in6_dev) {
925145c7a79SEric Dumazet atomic_inc(&in6_dev->cnf.mc_forwarding);
92685b3daadSDavid Ahern inet6_netconf_notify_devconf(dev_net(dev), RTM_NEWNETCONF,
927d67b8c61SNicolas Dichtel NETCONFA_MC_FORWARDING,
928d67b8c61SNicolas Dichtel dev->ifindex, &in6_dev->cnf);
929d67b8c61SNicolas Dichtel }
9301d6e55f1SThomas Goff
9316853f21fSYuval Mintz /* Fill in the VIF structures */
9326853f21fSYuval Mintz vif_device_init(v, dev, vifc->vifc_rate_limit, vifc->vifc_threshold,
9336853f21fSYuval Mintz vifc->mif6c_flags | (!mrtsock ? VIFF_STATIC : 0),
9346853f21fSYuval Mintz MIFF_REGISTER);
9357bc570c8SYOSHIFUJI Hideaki
9367bc570c8SYOSHIFUJI Hideaki /* And finish update writing critical data */
937a96f7a6aSEric Dumazet spin_lock(&mrt_lock);
938ebc31979SEric Dumazet rcu_assign_pointer(v->dev, dev);
93942120a86SEric Dumazet netdev_tracker_alloc(dev, &v->dev_tracker, GFP_ATOMIC);
94014fb64e1SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_PIMSM_V2
94114fb64e1SYOSHIFUJI Hideaki if (v->flags & MIFF_REGISTER)
9423493a5b7SEric Dumazet WRITE_ONCE(mrt->mroute_reg_vif_num, vifi);
94314fb64e1SYOSHIFUJI Hideaki #endif
9446bd52143SPatrick McHardy if (vifi + 1 > mrt->maxvif)
945db9eb7c8SEric Dumazet WRITE_ONCE(mrt->maxvif, vifi + 1);
946a96f7a6aSEric Dumazet spin_unlock(&mrt_lock);
947088aa3eeSYuval Mintz call_ip6mr_vif_entry_notifiers(net, FIB_EVENT_VIF_ADD,
948ebc31979SEric Dumazet v, dev, vifi, mrt->id);
9497bc570c8SYOSHIFUJI Hideaki return 0;
9507bc570c8SYOSHIFUJI Hideaki }
9517bc570c8SYOSHIFUJI Hideaki
ip6mr_cache_find(struct mr_table * mrt,const struct in6_addr * origin,const struct in6_addr * mcastgrp)952b70432f7SYuval Mintz static struct mfc6_cache *ip6mr_cache_find(struct mr_table *mrt,
953b71d1d42SEric Dumazet const struct in6_addr *origin,
954b71d1d42SEric Dumazet const struct in6_addr *mcastgrp)
9557bc570c8SYOSHIFUJI Hideaki {
95687c418bfSYuval Mintz struct mfc6_cache_cmp_arg arg = {
95787c418bfSYuval Mintz .mf6c_origin = *origin,
95887c418bfSYuval Mintz .mf6c_mcastgrp = *mcastgrp,
95987c418bfSYuval Mintz };
9607bc570c8SYOSHIFUJI Hideaki
961845c9a7aSYuval Mintz return mr_mfc_find(mrt, &arg);
962660b26dcSNicolas Dichtel }
963660b26dcSNicolas Dichtel
964660b26dcSNicolas Dichtel /* Look for a (*,G) entry */
ip6mr_cache_find_any(struct mr_table * mrt,struct in6_addr * mcastgrp,mifi_t mifi)965b70432f7SYuval Mintz static struct mfc6_cache *ip6mr_cache_find_any(struct mr_table *mrt,
966660b26dcSNicolas Dichtel struct in6_addr *mcastgrp,
967660b26dcSNicolas Dichtel mifi_t mifi)
968660b26dcSNicolas Dichtel {
96987c418bfSYuval Mintz struct mfc6_cache_cmp_arg arg = {
97087c418bfSYuval Mintz .mf6c_origin = in6addr_any,
97187c418bfSYuval Mintz .mf6c_mcastgrp = *mcastgrp,
97287c418bfSYuval Mintz };
973660b26dcSNicolas Dichtel
974660b26dcSNicolas Dichtel if (ipv6_addr_any(mcastgrp))
975845c9a7aSYuval Mintz return mr_mfc_find_any_parent(mrt, mifi);
976845c9a7aSYuval Mintz return mr_mfc_find_any(mrt, mifi, &arg);
977660b26dcSNicolas Dichtel }
978660b26dcSNicolas Dichtel
97987c418bfSYuval Mintz /* Look for a (S,G,iif) entry if parent != -1 */
98087c418bfSYuval Mintz static struct mfc6_cache *
ip6mr_cache_find_parent(struct mr_table * mrt,const struct in6_addr * origin,const struct in6_addr * mcastgrp,int parent)981b70432f7SYuval Mintz ip6mr_cache_find_parent(struct mr_table *mrt,
98287c418bfSYuval Mintz const struct in6_addr *origin,
98387c418bfSYuval Mintz const struct in6_addr *mcastgrp,
98487c418bfSYuval Mintz int parent)
98587c418bfSYuval Mintz {
98687c418bfSYuval Mintz struct mfc6_cache_cmp_arg arg = {
98787c418bfSYuval Mintz .mf6c_origin = *origin,
98887c418bfSYuval Mintz .mf6c_mcastgrp = *mcastgrp,
98987c418bfSYuval Mintz };
99087c418bfSYuval Mintz
991845c9a7aSYuval Mintz return mr_mfc_find_parent(mrt, &arg, parent);
99287c418bfSYuval Mintz }
99387c418bfSYuval Mintz
994845c9a7aSYuval Mintz /* Allocate a multicast cache entry */
ip6mr_cache_alloc(void)995b5aa30b1SPatrick McHardy static struct mfc6_cache *ip6mr_cache_alloc(void)
9967bc570c8SYOSHIFUJI Hideaki {
99736cbac59SJoe Perches struct mfc6_cache *c = kmem_cache_zalloc(mrt_cachep, GFP_KERNEL);
99863159f29SIan Morris if (!c)
9997bc570c8SYOSHIFUJI Hideaki return NULL;
1000494fff56SYuval Mintz c->_c.mfc_un.res.last_assert = jiffies - MFC_ASSERT_THRESH - 1;
1001494fff56SYuval Mintz c->_c.mfc_un.res.minvif = MAXMIFS;
10028c13af2aSYuval Mintz c->_c.free = ip6mr_cache_free_rcu;
10038c13af2aSYuval Mintz refcount_set(&c->_c.mfc_un.res.refcount, 1);
10047bc570c8SYOSHIFUJI Hideaki return c;
10057bc570c8SYOSHIFUJI Hideaki }
10067bc570c8SYOSHIFUJI Hideaki
ip6mr_cache_alloc_unres(void)1007b5aa30b1SPatrick McHardy static struct mfc6_cache *ip6mr_cache_alloc_unres(void)
10087bc570c8SYOSHIFUJI Hideaki {
100936cbac59SJoe Perches struct mfc6_cache *c = kmem_cache_zalloc(mrt_cachep, GFP_ATOMIC);
101063159f29SIan Morris if (!c)
10117bc570c8SYOSHIFUJI Hideaki return NULL;
1012494fff56SYuval Mintz skb_queue_head_init(&c->_c.mfc_un.unres.unresolved);
1013494fff56SYuval Mintz c->_c.mfc_un.unres.expires = jiffies + 10 * HZ;
10147bc570c8SYOSHIFUJI Hideaki return c;
10157bc570c8SYOSHIFUJI Hideaki }
10167bc570c8SYOSHIFUJI Hideaki
10177bc570c8SYOSHIFUJI Hideaki /*
10187bc570c8SYOSHIFUJI Hideaki * A cache entry has gone into a resolved state from queued
10197bc570c8SYOSHIFUJI Hideaki */
10207bc570c8SYOSHIFUJI Hideaki
ip6mr_cache_resolve(struct net * net,struct mr_table * mrt,struct mfc6_cache * uc,struct mfc6_cache * c)1021b70432f7SYuval Mintz static void ip6mr_cache_resolve(struct net *net, struct mr_table *mrt,
10226bd52143SPatrick McHardy struct mfc6_cache *uc, struct mfc6_cache *c)
10237bc570c8SYOSHIFUJI Hideaki {
10247bc570c8SYOSHIFUJI Hideaki struct sk_buff *skb;
10257bc570c8SYOSHIFUJI Hideaki
10267bc570c8SYOSHIFUJI Hideaki /*
10277bc570c8SYOSHIFUJI Hideaki * Play the pending entries through our router
10287bc570c8SYOSHIFUJI Hideaki */
10297bc570c8SYOSHIFUJI Hideaki
1030494fff56SYuval Mintz while ((skb = __skb_dequeue(&uc->_c.mfc_un.unres.unresolved))) {
10317bc570c8SYOSHIFUJI Hideaki if (ipv6_hdr(skb)->version == 0) {
1032af72868bSJohannes Berg struct nlmsghdr *nlh = skb_pull(skb,
1033af72868bSJohannes Berg sizeof(struct ipv6hdr));
10347bc570c8SYOSHIFUJI Hideaki
10357b0db857SYuval Mintz if (mr_fill_mroute(mrt, skb, &c->_c,
10367b0db857SYuval Mintz nlmsg_data(nlh)) > 0) {
1037549e028dSYOSHIFUJI Hideaki nlh->nlmsg_len = skb_tail_pointer(skb) - (u8 *)nlh;
10387bc570c8SYOSHIFUJI Hideaki } else {
10397bc570c8SYOSHIFUJI Hideaki nlh->nlmsg_type = NLMSG_ERROR;
1040573ce260SHong zhi guo nlh->nlmsg_len = nlmsg_msg_size(sizeof(struct nlmsgerr));
10417bc570c8SYOSHIFUJI Hideaki skb_trim(skb, nlh->nlmsg_len);
1042573ce260SHong zhi guo ((struct nlmsgerr *)nlmsg_data(nlh))->error = -EMSGSIZE;
10437bc570c8SYOSHIFUJI Hideaki }
104415e47304SEric W. Biederman rtnl_unicast(skb, net, NETLINK_CB(skb).portid);
1045b07a9b26SIdo Schimmel } else {
1046b07a9b26SIdo Schimmel rcu_read_lock();
1047e4a38c0cSPatrick Ruddy ip6_mr_forward(net, mrt, skb->dev, skb, c);
1048b07a9b26SIdo Schimmel rcu_read_unlock();
1049b07a9b26SIdo Schimmel }
10507bc570c8SYOSHIFUJI Hideaki }
10517bc570c8SYOSHIFUJI Hideaki }
10527bc570c8SYOSHIFUJI Hideaki
10537bc570c8SYOSHIFUJI Hideaki /*
1054dd12d15cSJulien Gomes * Bounce a cache query up to pim6sd and netlink.
10557bc570c8SYOSHIFUJI Hideaki *
10563493a5b7SEric Dumazet * Called under rcu_read_lock()
10577bc570c8SYOSHIFUJI Hideaki */
10587bc570c8SYOSHIFUJI Hideaki
ip6mr_cache_report(const struct mr_table * mrt,struct sk_buff * pkt,mifi_t mifi,int assert)10593493a5b7SEric Dumazet static int ip6mr_cache_report(const struct mr_table *mrt, struct sk_buff *pkt,
10606bd52143SPatrick McHardy mifi_t mifi, int assert)
10617bc570c8SYOSHIFUJI Hideaki {
10628571ab47SYuval Mintz struct sock *mroute6_sk;
10637bc570c8SYOSHIFUJI Hideaki struct sk_buff *skb;
10647bc570c8SYOSHIFUJI Hideaki struct mrt6msg *msg;
10657bc570c8SYOSHIFUJI Hideaki int ret;
10667bc570c8SYOSHIFUJI Hideaki
106714fb64e1SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_PIMSM_V2
10684b340a5aSMobashshera Rasool if (assert == MRT6MSG_WHOLEPKT || assert == MRT6MSG_WRMIFWHOLE)
106914fb64e1SYOSHIFUJI Hideaki skb = skb_realloc_headroom(pkt, -skb_network_offset(pkt)
107014fb64e1SYOSHIFUJI Hideaki +sizeof(*msg));
107114fb64e1SYOSHIFUJI Hideaki else
107214fb64e1SYOSHIFUJI Hideaki #endif
10737bc570c8SYOSHIFUJI Hideaki skb = alloc_skb(sizeof(struct ipv6hdr) + sizeof(*msg), GFP_ATOMIC);
10747bc570c8SYOSHIFUJI Hideaki
10757bc570c8SYOSHIFUJI Hideaki if (!skb)
10767bc570c8SYOSHIFUJI Hideaki return -ENOBUFS;
10777bc570c8SYOSHIFUJI Hideaki
10787bc570c8SYOSHIFUJI Hideaki /* I suppose that internal messages
10797bc570c8SYOSHIFUJI Hideaki * do not require checksums */
10807bc570c8SYOSHIFUJI Hideaki
10817bc570c8SYOSHIFUJI Hideaki skb->ip_summed = CHECKSUM_UNNECESSARY;
10827bc570c8SYOSHIFUJI Hideaki
108314fb64e1SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_PIMSM_V2
10844b340a5aSMobashshera Rasool if (assert == MRT6MSG_WHOLEPKT || assert == MRT6MSG_WRMIFWHOLE) {
108514fb64e1SYOSHIFUJI Hideaki /* Ugly, but we have no choice with this interface.
108614fb64e1SYOSHIFUJI Hideaki Duplicate old header, fix length etc.
108714fb64e1SYOSHIFUJI Hideaki And all this only to mangle msg->im6_msgtype and
108814fb64e1SYOSHIFUJI Hideaki to set msg->im6_mbz to "mbz" :-)
108914fb64e1SYOSHIFUJI Hideaki */
109030e0191bSYue Haibing __skb_pull(skb, skb_network_offset(pkt));
109114fb64e1SYOSHIFUJI Hideaki
109214fb64e1SYOSHIFUJI Hideaki skb_push(skb, sizeof(*msg));
109314fb64e1SYOSHIFUJI Hideaki skb_reset_transport_header(skb);
109414fb64e1SYOSHIFUJI Hideaki msg = (struct mrt6msg *)skb_transport_header(skb);
109514fb64e1SYOSHIFUJI Hideaki msg->im6_mbz = 0;
10964b340a5aSMobashshera Rasool msg->im6_msgtype = assert;
10974b340a5aSMobashshera Rasool if (assert == MRT6MSG_WRMIFWHOLE)
10984b340a5aSMobashshera Rasool msg->im6_mif = mifi;
10994b340a5aSMobashshera Rasool else
11003493a5b7SEric Dumazet msg->im6_mif = READ_ONCE(mrt->mroute_reg_vif_num);
110114fb64e1SYOSHIFUJI Hideaki msg->im6_pad = 0;
11024e3fd7a0SAlexey Dobriyan msg->im6_src = ipv6_hdr(pkt)->saddr;
11034e3fd7a0SAlexey Dobriyan msg->im6_dst = ipv6_hdr(pkt)->daddr;
110414fb64e1SYOSHIFUJI Hideaki
110514fb64e1SYOSHIFUJI Hideaki skb->ip_summed = CHECKSUM_UNNECESSARY;
110614fb64e1SYOSHIFUJI Hideaki } else
110714fb64e1SYOSHIFUJI Hideaki #endif
110814fb64e1SYOSHIFUJI Hideaki {
11097bc570c8SYOSHIFUJI Hideaki /*
11107bc570c8SYOSHIFUJI Hideaki * Copy the IP header
11117bc570c8SYOSHIFUJI Hideaki */
11127bc570c8SYOSHIFUJI Hideaki
11137bc570c8SYOSHIFUJI Hideaki skb_put(skb, sizeof(struct ipv6hdr));
11147bc570c8SYOSHIFUJI Hideaki skb_reset_network_header(skb);
11157bc570c8SYOSHIFUJI Hideaki skb_copy_to_linear_data(skb, ipv6_hdr(pkt), sizeof(struct ipv6hdr));
11167bc570c8SYOSHIFUJI Hideaki
11177bc570c8SYOSHIFUJI Hideaki /*
11187bc570c8SYOSHIFUJI Hideaki * Add our header
11197bc570c8SYOSHIFUJI Hideaki */
11207bc570c8SYOSHIFUJI Hideaki skb_put(skb, sizeof(*msg));
11217bc570c8SYOSHIFUJI Hideaki skb_reset_transport_header(skb);
11227bc570c8SYOSHIFUJI Hideaki msg = (struct mrt6msg *)skb_transport_header(skb);
11237bc570c8SYOSHIFUJI Hideaki
11247bc570c8SYOSHIFUJI Hideaki msg->im6_mbz = 0;
11257bc570c8SYOSHIFUJI Hideaki msg->im6_msgtype = assert;
11266ac7eb08SRami Rosen msg->im6_mif = mifi;
11277bc570c8SYOSHIFUJI Hideaki msg->im6_pad = 0;
11284e3fd7a0SAlexey Dobriyan msg->im6_src = ipv6_hdr(pkt)->saddr;
11294e3fd7a0SAlexey Dobriyan msg->im6_dst = ipv6_hdr(pkt)->daddr;
11307bc570c8SYOSHIFUJI Hideaki
1131adf30907SEric Dumazet skb_dst_set(skb, dst_clone(skb_dst(pkt)));
11327bc570c8SYOSHIFUJI Hideaki skb->ip_summed = CHECKSUM_UNNECESSARY;
113314fb64e1SYOSHIFUJI Hideaki }
11347bc570c8SYOSHIFUJI Hideaki
1135b70432f7SYuval Mintz mroute6_sk = rcu_dereference(mrt->mroute_sk);
11368571ab47SYuval Mintz if (!mroute6_sk) {
11377bc570c8SYOSHIFUJI Hideaki kfree_skb(skb);
11387bc570c8SYOSHIFUJI Hideaki return -EINVAL;
11397bc570c8SYOSHIFUJI Hideaki }
11407bc570c8SYOSHIFUJI Hideaki
1141dd12d15cSJulien Gomes mrt6msg_netlink_event(mrt, skb);
1142dd12d15cSJulien Gomes
11438571ab47SYuval Mintz /* Deliver to user space multicast routing algorithms */
11448571ab47SYuval Mintz ret = sock_queue_rcv_skb(mroute6_sk, skb);
11453493a5b7SEric Dumazet
1146bd91b8bfSBenjamin Thery if (ret < 0) {
1147e87cc472SJoe Perches net_warn_ratelimited("mroute6: pending queue full, dropping entries\n");
11487bc570c8SYOSHIFUJI Hideaki kfree_skb(skb);
11497bc570c8SYOSHIFUJI Hideaki }
11507bc570c8SYOSHIFUJI Hideaki
11517bc570c8SYOSHIFUJI Hideaki return ret;
11527bc570c8SYOSHIFUJI Hideaki }
11537bc570c8SYOSHIFUJI Hideaki
1154494fff56SYuval Mintz /* Queue a packet for resolution. It gets locked cache entry! */
ip6mr_cache_unresolved(struct mr_table * mrt,mifi_t mifi,struct sk_buff * skb,struct net_device * dev)1155494fff56SYuval Mintz static int ip6mr_cache_unresolved(struct mr_table *mrt, mifi_t mifi,
1156e4a38c0cSPatrick Ruddy struct sk_buff *skb, struct net_device *dev)
11577bc570c8SYOSHIFUJI Hideaki {
1158494fff56SYuval Mintz struct mfc6_cache *c;
1159f30a7784SPatrick McHardy bool found = false;
11607bc570c8SYOSHIFUJI Hideaki int err;
11617bc570c8SYOSHIFUJI Hideaki
11627bc570c8SYOSHIFUJI Hideaki spin_lock_bh(&mfc_unres_lock);
1163494fff56SYuval Mintz list_for_each_entry(c, &mrt->mfc_unres_queue, _c.list) {
1164c476efbcSPatrick McHardy if (ipv6_addr_equal(&c->mf6c_mcastgrp, &ipv6_hdr(skb)->daddr) &&
1165f30a7784SPatrick McHardy ipv6_addr_equal(&c->mf6c_origin, &ipv6_hdr(skb)->saddr)) {
1166f30a7784SPatrick McHardy found = true;
11677bc570c8SYOSHIFUJI Hideaki break;
11687bc570c8SYOSHIFUJI Hideaki }
1169f30a7784SPatrick McHardy }
11707bc570c8SYOSHIFUJI Hideaki
1171f30a7784SPatrick McHardy if (!found) {
11727bc570c8SYOSHIFUJI Hideaki /*
11737bc570c8SYOSHIFUJI Hideaki * Create a new entry if allowable
11747bc570c8SYOSHIFUJI Hideaki */
11757bc570c8SYOSHIFUJI Hideaki
11760079ad8eSHangbin Liu c = ip6mr_cache_alloc_unres();
11770079ad8eSHangbin Liu if (!c) {
11787bc570c8SYOSHIFUJI Hideaki spin_unlock_bh(&mfc_unres_lock);
11797bc570c8SYOSHIFUJI Hideaki
11807bc570c8SYOSHIFUJI Hideaki kfree_skb(skb);
11817bc570c8SYOSHIFUJI Hideaki return -ENOBUFS;
11827bc570c8SYOSHIFUJI Hideaki }
11837bc570c8SYOSHIFUJI Hideaki
1184494fff56SYuval Mintz /* Fill in the new cache entry */
1185494fff56SYuval Mintz c->_c.mfc_parent = -1;
11867bc570c8SYOSHIFUJI Hideaki c->mf6c_origin = ipv6_hdr(skb)->saddr;
11877bc570c8SYOSHIFUJI Hideaki c->mf6c_mcastgrp = ipv6_hdr(skb)->daddr;
11887bc570c8SYOSHIFUJI Hideaki
11897bc570c8SYOSHIFUJI Hideaki /*
11907bc570c8SYOSHIFUJI Hideaki * Reflect first query at pim6sd
11917bc570c8SYOSHIFUJI Hideaki */
11926bd52143SPatrick McHardy err = ip6mr_cache_report(mrt, skb, mifi, MRT6MSG_NOCACHE);
11938229efdaSBenjamin Thery if (err < 0) {
11947bc570c8SYOSHIFUJI Hideaki /* If the report failed throw the cache entry
11957bc570c8SYOSHIFUJI Hideaki out - Brad Parker
11967bc570c8SYOSHIFUJI Hideaki */
11977bc570c8SYOSHIFUJI Hideaki spin_unlock_bh(&mfc_unres_lock);
11987bc570c8SYOSHIFUJI Hideaki
119958701ad4SBenjamin Thery ip6mr_cache_free(c);
12007bc570c8SYOSHIFUJI Hideaki kfree_skb(skb);
12017bc570c8SYOSHIFUJI Hideaki return err;
12027bc570c8SYOSHIFUJI Hideaki }
12037bc570c8SYOSHIFUJI Hideaki
12046bd52143SPatrick McHardy atomic_inc(&mrt->cache_resolve_queue_len);
1205494fff56SYuval Mintz list_add(&c->_c.list, &mrt->mfc_unres_queue);
1206812e44ddSNicolas Dichtel mr6_netlink_event(mrt, c, RTM_NEWROUTE);
12077bc570c8SYOSHIFUJI Hideaki
12086bd52143SPatrick McHardy ipmr_do_expire_process(mrt);
12097bc570c8SYOSHIFUJI Hideaki }
12107bc570c8SYOSHIFUJI Hideaki
1211494fff56SYuval Mintz /* See if we can append the packet */
1212494fff56SYuval Mintz if (c->_c.mfc_un.unres.unresolved.qlen > 3) {
12137bc570c8SYOSHIFUJI Hideaki kfree_skb(skb);
12147bc570c8SYOSHIFUJI Hideaki err = -ENOBUFS;
12157bc570c8SYOSHIFUJI Hideaki } else {
1216e4a38c0cSPatrick Ruddy if (dev) {
1217e4a38c0cSPatrick Ruddy skb->dev = dev;
1218e4a38c0cSPatrick Ruddy skb->skb_iif = dev->ifindex;
1219e4a38c0cSPatrick Ruddy }
1220494fff56SYuval Mintz skb_queue_tail(&c->_c.mfc_un.unres.unresolved, skb);
12217bc570c8SYOSHIFUJI Hideaki err = 0;
12227bc570c8SYOSHIFUJI Hideaki }
12237bc570c8SYOSHIFUJI Hideaki
12247bc570c8SYOSHIFUJI Hideaki spin_unlock_bh(&mfc_unres_lock);
12257bc570c8SYOSHIFUJI Hideaki return err;
12267bc570c8SYOSHIFUJI Hideaki }
12277bc570c8SYOSHIFUJI Hideaki
12287bc570c8SYOSHIFUJI Hideaki /*
12297bc570c8SYOSHIFUJI Hideaki * MFC6 cache manipulation by user space
12307bc570c8SYOSHIFUJI Hideaki */
12317bc570c8SYOSHIFUJI Hideaki
ip6mr_mfc_delete(struct mr_table * mrt,struct mf6cctl * mfc,int parent)1232b70432f7SYuval Mintz static int ip6mr_mfc_delete(struct mr_table *mrt, struct mf6cctl *mfc,
1233660b26dcSNicolas Dichtel int parent)
12347bc570c8SYOSHIFUJI Hideaki {
123587c418bfSYuval Mintz struct mfc6_cache *c;
12367bc570c8SYOSHIFUJI Hideaki
123787c418bfSYuval Mintz /* The entries are added/deleted only under RTNL */
123887c418bfSYuval Mintz rcu_read_lock();
123987c418bfSYuval Mintz c = ip6mr_cache_find_parent(mrt, &mfc->mf6cc_origin.sin6_addr,
124087c418bfSYuval Mintz &mfc->mf6cc_mcastgrp.sin6_addr, parent);
124187c418bfSYuval Mintz rcu_read_unlock();
124287c418bfSYuval Mintz if (!c)
124387c418bfSYuval Mintz return -ENOENT;
1244494fff56SYuval Mintz rhltable_remove(&mrt->mfc_hash, &c->_c.mnode, ip6mr_rht_params);
1245494fff56SYuval Mintz list_del_rcu(&c->_c.list);
12467bc570c8SYOSHIFUJI Hideaki
1247088aa3eeSYuval Mintz call_ip6mr_mfc_entry_notifiers(read_pnet(&mrt->net),
1248088aa3eeSYuval Mintz FIB_EVENT_ENTRY_DEL, c, mrt->id);
1249812e44ddSNicolas Dichtel mr6_netlink_event(mrt, c, RTM_DELROUTE);
12508c13af2aSYuval Mintz mr_cache_put(&c->_c);
12517bc570c8SYOSHIFUJI Hideaki return 0;
12527bc570c8SYOSHIFUJI Hideaki }
12537bc570c8SYOSHIFUJI Hideaki
ip6mr_device_event(struct notifier_block * this,unsigned long event,void * ptr)12547bc570c8SYOSHIFUJI Hideaki static int ip6mr_device_event(struct notifier_block *this,
12557bc570c8SYOSHIFUJI Hideaki unsigned long event, void *ptr)
12567bc570c8SYOSHIFUJI Hideaki {
1257351638e7SJiri Pirko struct net_device *dev = netdev_notifier_info_to_dev(ptr);
12588229efdaSBenjamin Thery struct net *net = dev_net(dev);
1259b70432f7SYuval Mintz struct mr_table *mrt;
12606853f21fSYuval Mintz struct vif_device *v;
12617bc570c8SYOSHIFUJI Hideaki int ct;
12627bc570c8SYOSHIFUJI Hideaki
12637bc570c8SYOSHIFUJI Hideaki if (event != NETDEV_UNREGISTER)
12647bc570c8SYOSHIFUJI Hideaki return NOTIFY_DONE;
12657bc570c8SYOSHIFUJI Hideaki
1266d1db275dSPatrick McHardy ip6mr_for_each_table(mrt, net) {
1267b70432f7SYuval Mintz v = &mrt->vif_table[0];
12686bd52143SPatrick McHardy for (ct = 0; ct < mrt->maxvif; ct++, v++) {
1269ebc31979SEric Dumazet if (rcu_access_pointer(v->dev) == dev)
1270723b929cSNikolay Aleksandrov mif6_delete(mrt, ct, 1, NULL);
12717bc570c8SYOSHIFUJI Hideaki }
1272d1db275dSPatrick McHardy }
1273c871e664SEric Dumazet
12747bc570c8SYOSHIFUJI Hideaki return NOTIFY_DONE;
12757bc570c8SYOSHIFUJI Hideaki }
12767bc570c8SYOSHIFUJI Hideaki
ip6mr_seq_read(struct net * net)1277088aa3eeSYuval Mintz static unsigned int ip6mr_seq_read(struct net *net)
1278088aa3eeSYuval Mintz {
1279088aa3eeSYuval Mintz ASSERT_RTNL();
1280088aa3eeSYuval Mintz
1281088aa3eeSYuval Mintz return net->ipv6.ipmr_seq + ip6mr_rules_seq_read(net);
1282088aa3eeSYuval Mintz }
1283088aa3eeSYuval Mintz
ip6mr_dump(struct net * net,struct notifier_block * nb,struct netlink_ext_ack * extack)1284b7a59557SJiri Pirko static int ip6mr_dump(struct net *net, struct notifier_block *nb,
1285b7a59557SJiri Pirko struct netlink_ext_ack *extack)
1286088aa3eeSYuval Mintz {
1287088aa3eeSYuval Mintz return mr_dump(net, nb, RTNL_FAMILY_IP6MR, ip6mr_rules_dump,
1288194366b2SEric Dumazet ip6mr_mr_table_iter, extack);
1289088aa3eeSYuval Mintz }
1290088aa3eeSYuval Mintz
12917bc570c8SYOSHIFUJI Hideaki static struct notifier_block ip6_mr_notifier = {
12927bc570c8SYOSHIFUJI Hideaki .notifier_call = ip6mr_device_event
12937bc570c8SYOSHIFUJI Hideaki };
12947bc570c8SYOSHIFUJI Hideaki
1295088aa3eeSYuval Mintz static const struct fib_notifier_ops ip6mr_notifier_ops_template = {
1296088aa3eeSYuval Mintz .family = RTNL_FAMILY_IP6MR,
1297088aa3eeSYuval Mintz .fib_seq_read = ip6mr_seq_read,
1298088aa3eeSYuval Mintz .fib_dump = ip6mr_dump,
1299088aa3eeSYuval Mintz .owner = THIS_MODULE,
1300088aa3eeSYuval Mintz };
13017bc570c8SYOSHIFUJI Hideaki
ip6mr_notifier_init(struct net * net)1302088aa3eeSYuval Mintz static int __net_init ip6mr_notifier_init(struct net *net)
1303088aa3eeSYuval Mintz {
1304088aa3eeSYuval Mintz struct fib_notifier_ops *ops;
1305088aa3eeSYuval Mintz
1306088aa3eeSYuval Mintz net->ipv6.ipmr_seq = 0;
1307088aa3eeSYuval Mintz
1308088aa3eeSYuval Mintz ops = fib_notifier_ops_register(&ip6mr_notifier_ops_template, net);
1309088aa3eeSYuval Mintz if (IS_ERR(ops))
1310088aa3eeSYuval Mintz return PTR_ERR(ops);
1311088aa3eeSYuval Mintz
1312088aa3eeSYuval Mintz net->ipv6.ip6mr_notifier_ops = ops;
1313088aa3eeSYuval Mintz
1314088aa3eeSYuval Mintz return 0;
1315088aa3eeSYuval Mintz }
1316088aa3eeSYuval Mintz
ip6mr_notifier_exit(struct net * net)1317088aa3eeSYuval Mintz static void __net_exit ip6mr_notifier_exit(struct net *net)
1318088aa3eeSYuval Mintz {
1319088aa3eeSYuval Mintz fib_notifier_ops_unregister(net->ipv6.ip6mr_notifier_ops);
1320088aa3eeSYuval Mintz net->ipv6.ip6mr_notifier_ops = NULL;
1321088aa3eeSYuval Mintz }
1322088aa3eeSYuval Mintz
1323088aa3eeSYuval Mintz /* Setup for IP multicast routing */
ip6mr_net_init(struct net * net)13244e16880cSBenjamin Thery static int __net_init ip6mr_net_init(struct net *net)
13254e16880cSBenjamin Thery {
1326d1db275dSPatrick McHardy int err;
1327f30a7784SPatrick McHardy
1328088aa3eeSYuval Mintz err = ip6mr_notifier_init(net);
1329088aa3eeSYuval Mintz if (err)
1330088aa3eeSYuval Mintz return err;
1331088aa3eeSYuval Mintz
1332d1db275dSPatrick McHardy err = ip6mr_rules_init(net);
1333d1db275dSPatrick McHardy if (err < 0)
1334088aa3eeSYuval Mintz goto ip6mr_rules_fail;
13358b90fc7eSBenjamin Thery
13368b90fc7eSBenjamin Thery #ifdef CONFIG_PROC_FS
13378b90fc7eSBenjamin Thery err = -ENOMEM;
1338c3506372SChristoph Hellwig if (!proc_create_net("ip6_mr_vif", 0, net->proc_net, &ip6mr_vif_seq_ops,
1339c3506372SChristoph Hellwig sizeof(struct mr_vif_iter)))
13408b90fc7eSBenjamin Thery goto proc_vif_fail;
1341c3506372SChristoph Hellwig if (!proc_create_net("ip6_mr_cache", 0, net->proc_net, &ipmr_mfc_seq_ops,
1342c3506372SChristoph Hellwig sizeof(struct mr_mfc_iter)))
13438b90fc7eSBenjamin Thery goto proc_cache_fail;
13448b90fc7eSBenjamin Thery #endif
13456bd52143SPatrick McHardy
13464a6258a0SBenjamin Thery return 0;
13474a6258a0SBenjamin Thery
13488b90fc7eSBenjamin Thery #ifdef CONFIG_PROC_FS
13498b90fc7eSBenjamin Thery proc_cache_fail:
1350ece31ffdSGao feng remove_proc_entry("ip6_mr_vif", net->proc_net);
13518b90fc7eSBenjamin Thery proc_vif_fail:
1352e2f736b7SEric Dumazet rtnl_lock();
1353d1db275dSPatrick McHardy ip6mr_rules_exit(net);
1354e2f736b7SEric Dumazet rtnl_unlock();
13558b90fc7eSBenjamin Thery #endif
1356088aa3eeSYuval Mintz ip6mr_rules_fail:
1357088aa3eeSYuval Mintz ip6mr_notifier_exit(net);
13584e16880cSBenjamin Thery return err;
13594e16880cSBenjamin Thery }
13604e16880cSBenjamin Thery
ip6mr_net_exit(struct net * net)13614e16880cSBenjamin Thery static void __net_exit ip6mr_net_exit(struct net *net)
13624e16880cSBenjamin Thery {
13638b90fc7eSBenjamin Thery #ifdef CONFIG_PROC_FS
1364ece31ffdSGao feng remove_proc_entry("ip6_mr_cache", net->proc_net);
1365ece31ffdSGao feng remove_proc_entry("ip6_mr_vif", net->proc_net);
13668b90fc7eSBenjamin Thery #endif
1367088aa3eeSYuval Mintz ip6mr_notifier_exit(net);
13684e16880cSBenjamin Thery }
13694e16880cSBenjamin Thery
ip6mr_net_exit_batch(struct list_head * net_list)1370e2f736b7SEric Dumazet static void __net_exit ip6mr_net_exit_batch(struct list_head *net_list)
1371e2f736b7SEric Dumazet {
1372e2f736b7SEric Dumazet struct net *net;
1373e2f736b7SEric Dumazet
1374e2f736b7SEric Dumazet rtnl_lock();
1375e2f736b7SEric Dumazet list_for_each_entry(net, net_list, exit_list)
1376e2f736b7SEric Dumazet ip6mr_rules_exit(net);
1377e2f736b7SEric Dumazet rtnl_unlock();
1378e2f736b7SEric Dumazet }
1379e2f736b7SEric Dumazet
13804e16880cSBenjamin Thery static struct pernet_operations ip6mr_net_ops = {
13814e16880cSBenjamin Thery .init = ip6mr_net_init,
13824e16880cSBenjamin Thery .exit = ip6mr_net_exit,
1383e2f736b7SEric Dumazet .exit_batch = ip6mr_net_exit_batch,
13844e16880cSBenjamin Thery };
13854e16880cSBenjamin Thery
ip6_mr_init(void)1386623d1a1aSWang Chen int __init ip6_mr_init(void)
13877bc570c8SYOSHIFUJI Hideaki {
1388623d1a1aSWang Chen int err;
1389623d1a1aSWang Chen
13907bc570c8SYOSHIFUJI Hideaki mrt_cachep = kmem_cache_create("ip6_mrt_cache",
13917bc570c8SYOSHIFUJI Hideaki sizeof(struct mfc6_cache),
13927bc570c8SYOSHIFUJI Hideaki 0, SLAB_HWCACHE_ALIGN,
13937bc570c8SYOSHIFUJI Hideaki NULL);
13947bc570c8SYOSHIFUJI Hideaki if (!mrt_cachep)
1395623d1a1aSWang Chen return -ENOMEM;
13967bc570c8SYOSHIFUJI Hideaki
13974e16880cSBenjamin Thery err = register_pernet_subsys(&ip6mr_net_ops);
13984e16880cSBenjamin Thery if (err)
13994e16880cSBenjamin Thery goto reg_pernet_fail;
14004e16880cSBenjamin Thery
1401623d1a1aSWang Chen err = register_netdevice_notifier(&ip6_mr_notifier);
1402623d1a1aSWang Chen if (err)
1403623d1a1aSWang Chen goto reg_notif_fail;
1404403dbb97STom Goff #ifdef CONFIG_IPV6_PIMSM_V2
1405403dbb97STom Goff if (inet6_add_protocol(&pim6_protocol, IPPROTO_PIM) < 0) {
1406f3213831SJoe Perches pr_err("%s: can't add PIM protocol\n", __func__);
1407403dbb97STom Goff err = -EAGAIN;
1408403dbb97STom Goff goto add_proto_fail;
1409403dbb97STom Goff }
1410403dbb97STom Goff #endif
1411a3fde2adSFlorian Westphal err = rtnl_register_module(THIS_MODULE, RTNL_FAMILY_IP6MR, RTM_GETROUTE,
1412d7c31cbdSDavid Lamparter ip6mr_rtm_getroute, ip6mr_rtm_dumproute, 0);
1413a3fde2adSFlorian Westphal if (err == 0)
1414623d1a1aSWang Chen return 0;
1415a3fde2adSFlorian Westphal
1416403dbb97STom Goff #ifdef CONFIG_IPV6_PIMSM_V2
1417a3fde2adSFlorian Westphal inet6_del_protocol(&pim6_protocol, IPPROTO_PIM);
1418403dbb97STom Goff add_proto_fail:
1419403dbb97STom Goff unregister_netdevice_notifier(&ip6_mr_notifier);
1420403dbb97STom Goff #endif
142187b30a65SBenjamin Thery reg_notif_fail:
14224e16880cSBenjamin Thery unregister_pernet_subsys(&ip6mr_net_ops);
14234e16880cSBenjamin Thery reg_pernet_fail:
142487b30a65SBenjamin Thery kmem_cache_destroy(mrt_cachep);
1425623d1a1aSWang Chen return err;
14267bc570c8SYOSHIFUJI Hideaki }
14277bc570c8SYOSHIFUJI Hideaki
ip6_mr_cleanup(void)1428623d1a1aSWang Chen void ip6_mr_cleanup(void)
1429623d1a1aSWang Chen {
1430ffb1388aSDuan Jiong rtnl_unregister(RTNL_FAMILY_IP6MR, RTM_GETROUTE);
1431ffb1388aSDuan Jiong #ifdef CONFIG_IPV6_PIMSM_V2
1432ffb1388aSDuan Jiong inet6_del_protocol(&pim6_protocol, IPPROTO_PIM);
1433ffb1388aSDuan Jiong #endif
1434623d1a1aSWang Chen unregister_netdevice_notifier(&ip6_mr_notifier);
14354e16880cSBenjamin Thery unregister_pernet_subsys(&ip6mr_net_ops);
1436623d1a1aSWang Chen kmem_cache_destroy(mrt_cachep);
1437623d1a1aSWang Chen }
14387bc570c8SYOSHIFUJI Hideaki
ip6mr_mfc_add(struct net * net,struct mr_table * mrt,struct mf6cctl * mfc,int mrtsock,int parent)1439b70432f7SYuval Mintz static int ip6mr_mfc_add(struct net *net, struct mr_table *mrt,
1440660b26dcSNicolas Dichtel struct mf6cctl *mfc, int mrtsock, int parent)
14417bc570c8SYOSHIFUJI Hideaki {
14426ac7eb08SRami Rosen unsigned char ttls[MAXMIFS];
144387c418bfSYuval Mintz struct mfc6_cache *uc, *c;
1444494fff56SYuval Mintz struct mr_mfc *_uc;
144587c418bfSYuval Mintz bool found;
144687c418bfSYuval Mintz int i, err;
14477bc570c8SYOSHIFUJI Hideaki
1448a50436f2SPatrick McHardy if (mfc->mf6cc_parent >= MAXMIFS)
1449a50436f2SPatrick McHardy return -ENFILE;
1450a50436f2SPatrick McHardy
14516ac7eb08SRami Rosen memset(ttls, 255, MAXMIFS);
14526ac7eb08SRami Rosen for (i = 0; i < MAXMIFS; i++) {
14537bc570c8SYOSHIFUJI Hideaki if (IF_ISSET(i, &mfc->mf6cc_ifset))
14547bc570c8SYOSHIFUJI Hideaki ttls[i] = 1;
14557bc570c8SYOSHIFUJI Hideaki }
14567bc570c8SYOSHIFUJI Hideaki
145787c418bfSYuval Mintz /* The entries are added/deleted only under RTNL */
145887c418bfSYuval Mintz rcu_read_lock();
145987c418bfSYuval Mintz c = ip6mr_cache_find_parent(mrt, &mfc->mf6cc_origin.sin6_addr,
146087c418bfSYuval Mintz &mfc->mf6cc_mcastgrp.sin6_addr, parent);
146187c418bfSYuval Mintz rcu_read_unlock();
146287c418bfSYuval Mintz if (c) {
1463a96f7a6aSEric Dumazet spin_lock(&mrt_lock);
1464494fff56SYuval Mintz c->_c.mfc_parent = mfc->mf6cc_parent;
1465494fff56SYuval Mintz ip6mr_update_thresholds(mrt, &c->_c, ttls);
14667bc570c8SYOSHIFUJI Hideaki if (!mrtsock)
1467494fff56SYuval Mintz c->_c.mfc_flags |= MFC_STATIC;
1468a96f7a6aSEric Dumazet spin_unlock(&mrt_lock);
1469088aa3eeSYuval Mintz call_ip6mr_mfc_entry_notifiers(net, FIB_EVENT_ENTRY_REPLACE,
1470088aa3eeSYuval Mintz c, mrt->id);
1471812e44ddSNicolas Dichtel mr6_netlink_event(mrt, c, RTM_NEWROUTE);
14727bc570c8SYOSHIFUJI Hideaki return 0;
14737bc570c8SYOSHIFUJI Hideaki }
14747bc570c8SYOSHIFUJI Hideaki
1475660b26dcSNicolas Dichtel if (!ipv6_addr_any(&mfc->mf6cc_mcastgrp.sin6_addr) &&
1476660b26dcSNicolas Dichtel !ipv6_addr_is_multicast(&mfc->mf6cc_mcastgrp.sin6_addr))
14777bc570c8SYOSHIFUJI Hideaki return -EINVAL;
14787bc570c8SYOSHIFUJI Hideaki
1479b5aa30b1SPatrick McHardy c = ip6mr_cache_alloc();
148063159f29SIan Morris if (!c)
14817bc570c8SYOSHIFUJI Hideaki return -ENOMEM;
14827bc570c8SYOSHIFUJI Hideaki
14837bc570c8SYOSHIFUJI Hideaki c->mf6c_origin = mfc->mf6cc_origin.sin6_addr;
14847bc570c8SYOSHIFUJI Hideaki c->mf6c_mcastgrp = mfc->mf6cc_mcastgrp.sin6_addr;
1485494fff56SYuval Mintz c->_c.mfc_parent = mfc->mf6cc_parent;
1486494fff56SYuval Mintz ip6mr_update_thresholds(mrt, &c->_c, ttls);
14877bc570c8SYOSHIFUJI Hideaki if (!mrtsock)
1488494fff56SYuval Mintz c->_c.mfc_flags |= MFC_STATIC;
14897bc570c8SYOSHIFUJI Hideaki
1490494fff56SYuval Mintz err = rhltable_insert_key(&mrt->mfc_hash, &c->cmparg, &c->_c.mnode,
149187c418bfSYuval Mintz ip6mr_rht_params);
149287c418bfSYuval Mintz if (err) {
149387c418bfSYuval Mintz pr_err("ip6mr: rhtable insert error %d\n", err);
149487c418bfSYuval Mintz ip6mr_cache_free(c);
149587c418bfSYuval Mintz return err;
149687c418bfSYuval Mintz }
1497494fff56SYuval Mintz list_add_tail_rcu(&c->_c.list, &mrt->mfc_cache_list);
14987bc570c8SYOSHIFUJI Hideaki
149987c418bfSYuval Mintz /* Check to see if we resolved a queued list. If so we
15007bc570c8SYOSHIFUJI Hideaki * need to send on the frames and tidy up.
15017bc570c8SYOSHIFUJI Hideaki */
1502f30a7784SPatrick McHardy found = false;
15037bc570c8SYOSHIFUJI Hideaki spin_lock_bh(&mfc_unres_lock);
1504494fff56SYuval Mintz list_for_each_entry(_uc, &mrt->mfc_unres_queue, list) {
1505494fff56SYuval Mintz uc = (struct mfc6_cache *)_uc;
1506c476efbcSPatrick McHardy if (ipv6_addr_equal(&uc->mf6c_origin, &c->mf6c_origin) &&
15077bc570c8SYOSHIFUJI Hideaki ipv6_addr_equal(&uc->mf6c_mcastgrp, &c->mf6c_mcastgrp)) {
1508494fff56SYuval Mintz list_del(&_uc->list);
15096bd52143SPatrick McHardy atomic_dec(&mrt->cache_resolve_queue_len);
1510f30a7784SPatrick McHardy found = true;
15117bc570c8SYOSHIFUJI Hideaki break;
15127bc570c8SYOSHIFUJI Hideaki }
15137bc570c8SYOSHIFUJI Hideaki }
1514b70432f7SYuval Mintz if (list_empty(&mrt->mfc_unres_queue))
15156bd52143SPatrick McHardy del_timer(&mrt->ipmr_expire_timer);
15167bc570c8SYOSHIFUJI Hideaki spin_unlock_bh(&mfc_unres_lock);
15177bc570c8SYOSHIFUJI Hideaki
1518f30a7784SPatrick McHardy if (found) {
15196bd52143SPatrick McHardy ip6mr_cache_resolve(net, mrt, uc, c);
152058701ad4SBenjamin Thery ip6mr_cache_free(uc);
15217bc570c8SYOSHIFUJI Hideaki }
1522088aa3eeSYuval Mintz call_ip6mr_mfc_entry_notifiers(net, FIB_EVENT_ENTRY_ADD,
1523088aa3eeSYuval Mintz c, mrt->id);
1524812e44ddSNicolas Dichtel mr6_netlink_event(mrt, c, RTM_NEWROUTE);
15257bc570c8SYOSHIFUJI Hideaki return 0;
15267bc570c8SYOSHIFUJI Hideaki }
15277bc570c8SYOSHIFUJI Hideaki
15287bc570c8SYOSHIFUJI Hideaki /*
15297bc570c8SYOSHIFUJI Hideaki * Close the multicast socket, and clear the vif tables etc
15307bc570c8SYOSHIFUJI Hideaki */
15317bc570c8SYOSHIFUJI Hideaki
mroute_clean_tables(struct mr_table * mrt,int flags)1532ca8d4794SCallum Sinclair static void mroute_clean_tables(struct mr_table *mrt, int flags)
15337bc570c8SYOSHIFUJI Hideaki {
1534494fff56SYuval Mintz struct mr_mfc *c, *tmp;
1535c871e664SEric Dumazet LIST_HEAD(list);
153687c418bfSYuval Mintz int i;
15377bc570c8SYOSHIFUJI Hideaki
153887c418bfSYuval Mintz /* Shut down all active vif entries */
1539ca8d4794SCallum Sinclair if (flags & (MRT6_FLUSH_MIFS | MRT6_FLUSH_MIFS_STATIC)) {
15406bd52143SPatrick McHardy for (i = 0; i < mrt->maxvif; i++) {
1541ca8d4794SCallum Sinclair if (((mrt->vif_table[i].flags & VIFF_STATIC) &&
1542ca8d4794SCallum Sinclair !(flags & MRT6_FLUSH_MIFS_STATIC)) ||
1543ca8d4794SCallum Sinclair (!(mrt->vif_table[i].flags & VIFF_STATIC) && !(flags & MRT6_FLUSH_MIFS)))
15444c698046SNikolay Aleksandrov continue;
1545723b929cSNikolay Aleksandrov mif6_delete(mrt, i, 0, &list);
15467bc570c8SYOSHIFUJI Hideaki }
1547c871e664SEric Dumazet unregister_netdevice_many(&list);
1548ca8d4794SCallum Sinclair }
15497bc570c8SYOSHIFUJI Hideaki
155087c418bfSYuval Mintz /* Wipe the cache */
1551ca8d4794SCallum Sinclair if (flags & (MRT6_FLUSH_MFC | MRT6_FLUSH_MFC_STATIC)) {
1552b70432f7SYuval Mintz list_for_each_entry_safe(c, tmp, &mrt->mfc_cache_list, list) {
1553ca8d4794SCallum Sinclair if (((c->mfc_flags & MFC_STATIC) && !(flags & MRT6_FLUSH_MFC_STATIC)) ||
1554ca8d4794SCallum Sinclair (!(c->mfc_flags & MFC_STATIC) && !(flags & MRT6_FLUSH_MFC)))
15557bc570c8SYOSHIFUJI Hideaki continue;
1556b70432f7SYuval Mintz rhltable_remove(&mrt->mfc_hash, &c->mnode, ip6mr_rht_params);
155787c418bfSYuval Mintz list_del_rcu(&c->list);
1558146820ccSNir Dotan call_ip6mr_mfc_entry_notifiers(read_pnet(&mrt->net),
1559146820ccSNir Dotan FIB_EVENT_ENTRY_DEL,
1560146820ccSNir Dotan (struct mfc6_cache *)c, mrt->id);
1561494fff56SYuval Mintz mr6_netlink_event(mrt, (struct mfc6_cache *)c, RTM_DELROUTE);
15628c13af2aSYuval Mintz mr_cache_put(c);
15637bc570c8SYOSHIFUJI Hideaki }
1564ca8d4794SCallum Sinclair }
15657bc570c8SYOSHIFUJI Hideaki
1566ca8d4794SCallum Sinclair if (flags & MRT6_FLUSH_MFC) {
15676bd52143SPatrick McHardy if (atomic_read(&mrt->cache_resolve_queue_len) != 0) {
15687bc570c8SYOSHIFUJI Hideaki spin_lock_bh(&mfc_unres_lock);
1569b70432f7SYuval Mintz list_for_each_entry_safe(c, tmp, &mrt->mfc_unres_queue, list) {
1570f30a7784SPatrick McHardy list_del(&c->list);
1571494fff56SYuval Mintz mr6_netlink_event(mrt, (struct mfc6_cache *)c,
1572494fff56SYuval Mintz RTM_DELROUTE);
1573494fff56SYuval Mintz ip6mr_destroy_unres(mrt, (struct mfc6_cache *)c);
15747bc570c8SYOSHIFUJI Hideaki }
15757bc570c8SYOSHIFUJI Hideaki spin_unlock_bh(&mfc_unres_lock);
15767bc570c8SYOSHIFUJI Hideaki }
15777bc570c8SYOSHIFUJI Hideaki }
1578ca8d4794SCallum Sinclair }
15797bc570c8SYOSHIFUJI Hideaki
ip6mr_sk_init(struct mr_table * mrt,struct sock * sk)1580b70432f7SYuval Mintz static int ip6mr_sk_init(struct mr_table *mrt, struct sock *sk)
15817bc570c8SYOSHIFUJI Hideaki {
15827bc570c8SYOSHIFUJI Hideaki int err = 0;
15838229efdaSBenjamin Thery struct net *net = sock_net(sk);
15847bc570c8SYOSHIFUJI Hideaki
15857bc570c8SYOSHIFUJI Hideaki rtnl_lock();
1586a96f7a6aSEric Dumazet spin_lock(&mrt_lock);
1587b70432f7SYuval Mintz if (rtnl_dereference(mrt->mroute_sk)) {
1588927265bcSEric Dumazet err = -EADDRINUSE;
15898571ab47SYuval Mintz } else {
1590b70432f7SYuval Mintz rcu_assign_pointer(mrt->mroute_sk, sk);
1591a366e300SEric Dumazet sock_set_flag(sk, SOCK_RCU_FREE);
1592145c7a79SEric Dumazet atomic_inc(&net->ipv6.devconf_all->mc_forwarding);
1593927265bcSEric Dumazet }
1594a96f7a6aSEric Dumazet spin_unlock(&mrt_lock);
1595927265bcSEric Dumazet
1596927265bcSEric Dumazet if (!err)
159785b3daadSDavid Ahern inet6_netconf_notify_devconf(net, RTM_NEWNETCONF,
159885b3daadSDavid Ahern NETCONFA_MC_FORWARDING,
1599d67b8c61SNicolas Dichtel NETCONFA_IFINDEX_ALL,
1600d67b8c61SNicolas Dichtel net->ipv6.devconf_all);
16017bc570c8SYOSHIFUJI Hideaki rtnl_unlock();
16027bc570c8SYOSHIFUJI Hideaki
16037bc570c8SYOSHIFUJI Hideaki return err;
16047bc570c8SYOSHIFUJI Hideaki }
16057bc570c8SYOSHIFUJI Hideaki
ip6mr_sk_done(struct sock * sk)16067bc570c8SYOSHIFUJI Hideaki int ip6mr_sk_done(struct sock *sk)
16077bc570c8SYOSHIFUJI Hideaki {
16088229efdaSBenjamin Thery struct net *net = sock_net(sk);
16097d9b1b57SEric Dumazet struct ipv6_devconf *devconf;
1610b70432f7SYuval Mintz struct mr_table *mrt;
16117d9b1b57SEric Dumazet int err = -EACCES;
16127bc570c8SYOSHIFUJI Hideaki
1613338d182fSFrancesco Ruggeri if (sk->sk_type != SOCK_RAW ||
1614338d182fSFrancesco Ruggeri inet_sk(sk)->inet_num != IPPROTO_ICMPV6)
1615338d182fSFrancesco Ruggeri return err;
1616338d182fSFrancesco Ruggeri
16177d9b1b57SEric Dumazet devconf = net->ipv6.devconf_all;
16187d9b1b57SEric Dumazet if (!devconf || !atomic_read(&devconf->mc_forwarding))
1619f2f2325eSEric Dumazet return err;
1620f2f2325eSEric Dumazet
16217bc570c8SYOSHIFUJI Hideaki rtnl_lock();
1622d1db275dSPatrick McHardy ip6mr_for_each_table(mrt, net) {
1623b70432f7SYuval Mintz if (sk == rtnl_dereference(mrt->mroute_sk)) {
1624a96f7a6aSEric Dumazet spin_lock(&mrt_lock);
1625b70432f7SYuval Mintz RCU_INIT_POINTER(mrt->mroute_sk, NULL);
1626a366e300SEric Dumazet /* Note that mroute_sk had SOCK_RCU_FREE set,
1627a366e300SEric Dumazet * so the RCU grace period before sk freeing
1628a366e300SEric Dumazet * is guaranteed by sk_destruct()
1629a366e300SEric Dumazet */
16307d9b1b57SEric Dumazet atomic_dec(&devconf->mc_forwarding);
1631a96f7a6aSEric Dumazet spin_unlock(&mrt_lock);
163285b3daadSDavid Ahern inet6_netconf_notify_devconf(net, RTM_NEWNETCONF,
1633d67b8c61SNicolas Dichtel NETCONFA_MC_FORWARDING,
1634d67b8c61SNicolas Dichtel NETCONFA_IFINDEX_ALL,
1635d67b8c61SNicolas Dichtel net->ipv6.devconf_all);
16367bc570c8SYOSHIFUJI Hideaki
1637ca8d4794SCallum Sinclair mroute_clean_tables(mrt, MRT6_FLUSH_MIFS | MRT6_FLUSH_MFC);
1638d1db275dSPatrick McHardy err = 0;
1639d1db275dSPatrick McHardy break;
1640d1db275dSPatrick McHardy }
1641d1db275dSPatrick McHardy }
16427bc570c8SYOSHIFUJI Hideaki rtnl_unlock();
16437bc570c8SYOSHIFUJI Hideaki
16447bc570c8SYOSHIFUJI Hideaki return err;
16457bc570c8SYOSHIFUJI Hideaki }
16467bc570c8SYOSHIFUJI Hideaki
mroute6_is_socket(struct net * net,struct sk_buff * skb)16478571ab47SYuval Mintz bool mroute6_is_socket(struct net *net, struct sk_buff *skb)
16486bd52143SPatrick McHardy {
1649b70432f7SYuval Mintz struct mr_table *mrt;
16504c9483b2SDavid S. Miller struct flowi6 fl6 = {
1651e374c618SJulian Anastasov .flowi6_iif = skb->skb_iif ? : LOOPBACK_IFINDEX,
16524c9483b2SDavid S. Miller .flowi6_oif = skb->dev->ifindex,
16534c9483b2SDavid S. Miller .flowi6_mark = skb->mark,
1654d1db275dSPatrick McHardy };
1655d1db275dSPatrick McHardy
16564c9483b2SDavid S. Miller if (ip6mr_fib_lookup(net, &fl6, &mrt) < 0)
1657d1db275dSPatrick McHardy return NULL;
16586bd52143SPatrick McHardy
1659b70432f7SYuval Mintz return rcu_access_pointer(mrt->mroute_sk);
16606bd52143SPatrick McHardy }
16618571ab47SYuval Mintz EXPORT_SYMBOL(mroute6_is_socket);
16626bd52143SPatrick McHardy
16637bc570c8SYOSHIFUJI Hideaki /*
16647bc570c8SYOSHIFUJI Hideaki * Socket options and virtual interface manipulation. The whole
16657bc570c8SYOSHIFUJI Hideaki * virtual interface system is a complete heap, but unfortunately
16667bc570c8SYOSHIFUJI Hideaki * that's how BSD mrouted happens to think. Maybe one day with a proper
16677bc570c8SYOSHIFUJI Hideaki * MOSPF/PIM router set up we can clean this up.
16687bc570c8SYOSHIFUJI Hideaki */
16697bc570c8SYOSHIFUJI Hideaki
ip6_mroute_setsockopt(struct sock * sk,int optname,sockptr_t optval,unsigned int optlen)1670b43c6153SChristoph Hellwig int ip6_mroute_setsockopt(struct sock *sk, int optname, sockptr_t optval,
1671b43c6153SChristoph Hellwig unsigned int optlen)
16727bc570c8SYOSHIFUJI Hideaki {
1673660b26dcSNicolas Dichtel int ret, parent = 0;
16747bc570c8SYOSHIFUJI Hideaki struct mif6ctl vif;
16757bc570c8SYOSHIFUJI Hideaki struct mf6cctl mfc;
16767bc570c8SYOSHIFUJI Hideaki mifi_t mifi;
16778229efdaSBenjamin Thery struct net *net = sock_net(sk);
1678b70432f7SYuval Mintz struct mr_table *mrt;
1679d1db275dSPatrick McHardy
168099253eb7SXin Long if (sk->sk_type != SOCK_RAW ||
168199253eb7SXin Long inet_sk(sk)->inet_num != IPPROTO_ICMPV6)
168299253eb7SXin Long return -EOPNOTSUPP;
168399253eb7SXin Long
1684d1db275dSPatrick McHardy mrt = ip6mr_get_table(net, raw6_sk(sk)->ip6mr_table ? : RT6_TABLE_DFLT);
168563159f29SIan Morris if (!mrt)
1686d1db275dSPatrick McHardy return -ENOENT;
16877bc570c8SYOSHIFUJI Hideaki
16887bc570c8SYOSHIFUJI Hideaki if (optname != MRT6_INIT) {
1689b70432f7SYuval Mintz if (sk != rcu_access_pointer(mrt->mroute_sk) &&
16908571ab47SYuval Mintz !ns_capable(net->user_ns, CAP_NET_ADMIN))
16917bc570c8SYOSHIFUJI Hideaki return -EACCES;
16927bc570c8SYOSHIFUJI Hideaki }
16937bc570c8SYOSHIFUJI Hideaki
16947bc570c8SYOSHIFUJI Hideaki switch (optname) {
16957bc570c8SYOSHIFUJI Hideaki case MRT6_INIT:
16967bc570c8SYOSHIFUJI Hideaki if (optlen < sizeof(int))
16977bc570c8SYOSHIFUJI Hideaki return -EINVAL;
16987bc570c8SYOSHIFUJI Hideaki
16996bd52143SPatrick McHardy return ip6mr_sk_init(mrt, sk);
17007bc570c8SYOSHIFUJI Hideaki
17017bc570c8SYOSHIFUJI Hideaki case MRT6_DONE:
17027bc570c8SYOSHIFUJI Hideaki return ip6mr_sk_done(sk);
17037bc570c8SYOSHIFUJI Hideaki
17047bc570c8SYOSHIFUJI Hideaki case MRT6_ADD_MIF:
17057bc570c8SYOSHIFUJI Hideaki if (optlen < sizeof(vif))
17067bc570c8SYOSHIFUJI Hideaki return -EINVAL;
1707b43c6153SChristoph Hellwig if (copy_from_sockptr(&vif, optval, sizeof(vif)))
17087bc570c8SYOSHIFUJI Hideaki return -EFAULT;
17096ac7eb08SRami Rosen if (vif.mif6c_mifi >= MAXMIFS)
17107bc570c8SYOSHIFUJI Hideaki return -ENFILE;
17117bc570c8SYOSHIFUJI Hideaki rtnl_lock();
17128571ab47SYuval Mintz ret = mif6_add(net, mrt, &vif,
1713b70432f7SYuval Mintz sk == rtnl_dereference(mrt->mroute_sk));
17147bc570c8SYOSHIFUJI Hideaki rtnl_unlock();
17157bc570c8SYOSHIFUJI Hideaki return ret;
17167bc570c8SYOSHIFUJI Hideaki
17177bc570c8SYOSHIFUJI Hideaki case MRT6_DEL_MIF:
17187bc570c8SYOSHIFUJI Hideaki if (optlen < sizeof(mifi_t))
17197bc570c8SYOSHIFUJI Hideaki return -EINVAL;
1720b43c6153SChristoph Hellwig if (copy_from_sockptr(&mifi, optval, sizeof(mifi_t)))
17217bc570c8SYOSHIFUJI Hideaki return -EFAULT;
17227bc570c8SYOSHIFUJI Hideaki rtnl_lock();
1723723b929cSNikolay Aleksandrov ret = mif6_delete(mrt, mifi, 0, NULL);
17247bc570c8SYOSHIFUJI Hideaki rtnl_unlock();
17257bc570c8SYOSHIFUJI Hideaki return ret;
17267bc570c8SYOSHIFUJI Hideaki
17277bc570c8SYOSHIFUJI Hideaki /*
17287bc570c8SYOSHIFUJI Hideaki * Manipulate the forwarding caches. These live
17297bc570c8SYOSHIFUJI Hideaki * in a sort of kernel/user symbiosis.
17307bc570c8SYOSHIFUJI Hideaki */
17317bc570c8SYOSHIFUJI Hideaki case MRT6_ADD_MFC:
17327bc570c8SYOSHIFUJI Hideaki case MRT6_DEL_MFC:
1733660b26dcSNicolas Dichtel parent = -1;
1734a8eceea8SJoe Perches fallthrough;
1735660b26dcSNicolas Dichtel case MRT6_ADD_MFC_PROXY:
1736660b26dcSNicolas Dichtel case MRT6_DEL_MFC_PROXY:
17377bc570c8SYOSHIFUJI Hideaki if (optlen < sizeof(mfc))
17387bc570c8SYOSHIFUJI Hideaki return -EINVAL;
1739b43c6153SChristoph Hellwig if (copy_from_sockptr(&mfc, optval, sizeof(mfc)))
17407bc570c8SYOSHIFUJI Hideaki return -EFAULT;
1741660b26dcSNicolas Dichtel if (parent == 0)
1742660b26dcSNicolas Dichtel parent = mfc.mf6cc_parent;
17437bc570c8SYOSHIFUJI Hideaki rtnl_lock();
1744660b26dcSNicolas Dichtel if (optname == MRT6_DEL_MFC || optname == MRT6_DEL_MFC_PROXY)
1745660b26dcSNicolas Dichtel ret = ip6mr_mfc_delete(mrt, &mfc, parent);
17467bc570c8SYOSHIFUJI Hideaki else
1747660b26dcSNicolas Dichtel ret = ip6mr_mfc_add(net, mrt, &mfc,
17488571ab47SYuval Mintz sk ==
1749b70432f7SYuval Mintz rtnl_dereference(mrt->mroute_sk),
17508571ab47SYuval Mintz parent);
17517bc570c8SYOSHIFUJI Hideaki rtnl_unlock();
17527bc570c8SYOSHIFUJI Hideaki return ret;
17537bc570c8SYOSHIFUJI Hideaki
1754ca8d4794SCallum Sinclair case MRT6_FLUSH:
1755ca8d4794SCallum Sinclair {
1756ca8d4794SCallum Sinclair int flags;
1757ca8d4794SCallum Sinclair
1758ca8d4794SCallum Sinclair if (optlen != sizeof(flags))
1759ca8d4794SCallum Sinclair return -EINVAL;
1760b43c6153SChristoph Hellwig if (copy_from_sockptr(&flags, optval, sizeof(flags)))
1761ca8d4794SCallum Sinclair return -EFAULT;
1762ca8d4794SCallum Sinclair rtnl_lock();
1763ca8d4794SCallum Sinclair mroute_clean_tables(mrt, flags);
1764ca8d4794SCallum Sinclair rtnl_unlock();
1765ca8d4794SCallum Sinclair return 0;
1766ca8d4794SCallum Sinclair }
1767ca8d4794SCallum Sinclair
17687bc570c8SYOSHIFUJI Hideaki /*
176914fb64e1SYOSHIFUJI Hideaki * Control PIM assert (to activate pim will activate assert)
177014fb64e1SYOSHIFUJI Hideaki */
177114fb64e1SYOSHIFUJI Hideaki case MRT6_ASSERT:
177214fb64e1SYOSHIFUJI Hideaki {
177314fb64e1SYOSHIFUJI Hideaki int v;
177403f52a0aSJoe Perches
177503f52a0aSJoe Perches if (optlen != sizeof(v))
177603f52a0aSJoe Perches return -EINVAL;
1777b43c6153SChristoph Hellwig if (copy_from_sockptr(&v, optval, sizeof(v)))
177814fb64e1SYOSHIFUJI Hideaki return -EFAULT;
177953d6841dSJoe Perches mrt->mroute_do_assert = v;
178014fb64e1SYOSHIFUJI Hideaki return 0;
178114fb64e1SYOSHIFUJI Hideaki }
178214fb64e1SYOSHIFUJI Hideaki
178314fb64e1SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_PIMSM_V2
178414fb64e1SYOSHIFUJI Hideaki case MRT6_PIM:
178514fb64e1SYOSHIFUJI Hideaki {
1786a3ebe92aSFlorian Westphal bool do_wrmifwhole;
1787a9f83bf3SYOSHIFUJI Hideaki int v;
178803f52a0aSJoe Perches
178903f52a0aSJoe Perches if (optlen != sizeof(v))
179003f52a0aSJoe Perches return -EINVAL;
1791b43c6153SChristoph Hellwig if (copy_from_sockptr(&v, optval, sizeof(v)))
179214fb64e1SYOSHIFUJI Hideaki return -EFAULT;
17934b340a5aSMobashshera Rasool
17944b340a5aSMobashshera Rasool do_wrmifwhole = (v == MRT6MSG_WRMIFWHOLE);
179514fb64e1SYOSHIFUJI Hideaki v = !!v;
179614fb64e1SYOSHIFUJI Hideaki rtnl_lock();
179714fb64e1SYOSHIFUJI Hideaki ret = 0;
17986bd52143SPatrick McHardy if (v != mrt->mroute_do_pim) {
17996bd52143SPatrick McHardy mrt->mroute_do_pim = v;
18006bd52143SPatrick McHardy mrt->mroute_do_assert = v;
18014b340a5aSMobashshera Rasool mrt->mroute_do_wrvifwhole = do_wrmifwhole;
180214fb64e1SYOSHIFUJI Hideaki }
180314fb64e1SYOSHIFUJI Hideaki rtnl_unlock();
180414fb64e1SYOSHIFUJI Hideaki return ret;
180514fb64e1SYOSHIFUJI Hideaki }
180614fb64e1SYOSHIFUJI Hideaki
180714fb64e1SYOSHIFUJI Hideaki #endif
1808d1db275dSPatrick McHardy #ifdef CONFIG_IPV6_MROUTE_MULTIPLE_TABLES
1809d1db275dSPatrick McHardy case MRT6_TABLE:
1810d1db275dSPatrick McHardy {
1811d1db275dSPatrick McHardy u32 v;
1812d1db275dSPatrick McHardy
1813d1db275dSPatrick McHardy if (optlen != sizeof(u32))
1814d1db275dSPatrick McHardy return -EINVAL;
1815b43c6153SChristoph Hellwig if (copy_from_sockptr(&v, optval, sizeof(v)))
1816d1db275dSPatrick McHardy return -EFAULT;
181775356a81SDan Carpenter /* "pim6reg%u" should not exceed 16 bytes (IFNAMSIZ) */
181875356a81SDan Carpenter if (v != RT_TABLE_DEFAULT && v >= 100000000)
181975356a81SDan Carpenter return -EINVAL;
1820b70432f7SYuval Mintz if (sk == rcu_access_pointer(mrt->mroute_sk))
1821d1db275dSPatrick McHardy return -EBUSY;
1822d1db275dSPatrick McHardy
1823d1db275dSPatrick McHardy rtnl_lock();
1824d1db275dSPatrick McHardy ret = 0;
1825e783bb00SSabrina Dubroca mrt = ip6mr_new_table(net, v);
1826e783bb00SSabrina Dubroca if (IS_ERR(mrt))
1827e783bb00SSabrina Dubroca ret = PTR_ERR(mrt);
1828848235edSSabrina Dubroca else
1829d1db275dSPatrick McHardy raw6_sk(sk)->ip6mr_table = v;
1830d1db275dSPatrick McHardy rtnl_unlock();
1831d1db275dSPatrick McHardy return ret;
1832d1db275dSPatrick McHardy }
1833d1db275dSPatrick McHardy #endif
183414fb64e1SYOSHIFUJI Hideaki /*
18357d120c55SRami Rosen * Spurious command, or MRT6_VERSION which you cannot
18367bc570c8SYOSHIFUJI Hideaki * set.
18377bc570c8SYOSHIFUJI Hideaki */
18387bc570c8SYOSHIFUJI Hideaki default:
18397bc570c8SYOSHIFUJI Hideaki return -ENOPROTOOPT;
18407bc570c8SYOSHIFUJI Hideaki }
18417bc570c8SYOSHIFUJI Hideaki }
18427bc570c8SYOSHIFUJI Hideaki
18437bc570c8SYOSHIFUJI Hideaki /*
18447bc570c8SYOSHIFUJI Hideaki * Getsock opt support for the multicast routing system.
18457bc570c8SYOSHIFUJI Hideaki */
18467bc570c8SYOSHIFUJI Hideaki
ip6_mroute_getsockopt(struct sock * sk,int optname,sockptr_t optval,sockptr_t optlen)18476dadbe4bSMartin KaFai Lau int ip6_mroute_getsockopt(struct sock *sk, int optname, sockptr_t optval,
18486dadbe4bSMartin KaFai Lau sockptr_t optlen)
18497bc570c8SYOSHIFUJI Hideaki {
18507bc570c8SYOSHIFUJI Hideaki int olr;
18517bc570c8SYOSHIFUJI Hideaki int val;
18528229efdaSBenjamin Thery struct net *net = sock_net(sk);
1853b70432f7SYuval Mintz struct mr_table *mrt;
1854d1db275dSPatrick McHardy
185599253eb7SXin Long if (sk->sk_type != SOCK_RAW ||
185699253eb7SXin Long inet_sk(sk)->inet_num != IPPROTO_ICMPV6)
185799253eb7SXin Long return -EOPNOTSUPP;
185899253eb7SXin Long
1859d1db275dSPatrick McHardy mrt = ip6mr_get_table(net, raw6_sk(sk)->ip6mr_table ? : RT6_TABLE_DFLT);
186063159f29SIan Morris if (!mrt)
1861d1db275dSPatrick McHardy return -ENOENT;
18627bc570c8SYOSHIFUJI Hideaki
18637bc570c8SYOSHIFUJI Hideaki switch (optname) {
18647bc570c8SYOSHIFUJI Hideaki case MRT6_VERSION:
18657bc570c8SYOSHIFUJI Hideaki val = 0x0305;
18667bc570c8SYOSHIFUJI Hideaki break;
186714fb64e1SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_PIMSM_V2
186814fb64e1SYOSHIFUJI Hideaki case MRT6_PIM:
18696bd52143SPatrick McHardy val = mrt->mroute_do_pim;
187014fb64e1SYOSHIFUJI Hideaki break;
187114fb64e1SYOSHIFUJI Hideaki #endif
187214fb64e1SYOSHIFUJI Hideaki case MRT6_ASSERT:
18736bd52143SPatrick McHardy val = mrt->mroute_do_assert;
187414fb64e1SYOSHIFUJI Hideaki break;
18757bc570c8SYOSHIFUJI Hideaki default:
18767bc570c8SYOSHIFUJI Hideaki return -ENOPROTOOPT;
18777bc570c8SYOSHIFUJI Hideaki }
18787bc570c8SYOSHIFUJI Hideaki
18796dadbe4bSMartin KaFai Lau if (copy_from_sockptr(&olr, optlen, sizeof(int)))
18807bc570c8SYOSHIFUJI Hideaki return -EFAULT;
18817bc570c8SYOSHIFUJI Hideaki
18827bc570c8SYOSHIFUJI Hideaki olr = min_t(int, olr, sizeof(int));
18837bc570c8SYOSHIFUJI Hideaki if (olr < 0)
18847bc570c8SYOSHIFUJI Hideaki return -EINVAL;
18857bc570c8SYOSHIFUJI Hideaki
18866dadbe4bSMartin KaFai Lau if (copy_to_sockptr(optlen, &olr, sizeof(int)))
18877bc570c8SYOSHIFUJI Hideaki return -EFAULT;
18886dadbe4bSMartin KaFai Lau if (copy_to_sockptr(optval, &val, olr))
18897bc570c8SYOSHIFUJI Hideaki return -EFAULT;
18907bc570c8SYOSHIFUJI Hideaki return 0;
18917bc570c8SYOSHIFUJI Hideaki }
18927bc570c8SYOSHIFUJI Hideaki
18937bc570c8SYOSHIFUJI Hideaki /*
18947bc570c8SYOSHIFUJI Hideaki * The IP multicast ioctl support routines.
18957bc570c8SYOSHIFUJI Hideaki */
ip6mr_ioctl(struct sock * sk,int cmd,void * arg)1896e1d001faSBreno Leitao int ip6mr_ioctl(struct sock *sk, int cmd, void *arg)
18977bc570c8SYOSHIFUJI Hideaki {
1898e1d001faSBreno Leitao struct sioc_sg_req6 *sr;
1899e1d001faSBreno Leitao struct sioc_mif_req6 *vr;
19006853f21fSYuval Mintz struct vif_device *vif;
19017bc570c8SYOSHIFUJI Hideaki struct mfc6_cache *c;
19028229efdaSBenjamin Thery struct net *net = sock_net(sk);
1903b70432f7SYuval Mintz struct mr_table *mrt;
1904d1db275dSPatrick McHardy
1905d1db275dSPatrick McHardy mrt = ip6mr_get_table(net, raw6_sk(sk)->ip6mr_table ? : RT6_TABLE_DFLT);
190663159f29SIan Morris if (!mrt)
1907d1db275dSPatrick McHardy return -ENOENT;
19087bc570c8SYOSHIFUJI Hideaki
19097bc570c8SYOSHIFUJI Hideaki switch (cmd) {
19107bc570c8SYOSHIFUJI Hideaki case SIOCGETMIFCNT_IN6:
1911e1d001faSBreno Leitao vr = (struct sioc_mif_req6 *)arg;
1912e1d001faSBreno Leitao if (vr->mifi >= mrt->maxvif)
19137bc570c8SYOSHIFUJI Hideaki return -EINVAL;
1914e1d001faSBreno Leitao vr->mifi = array_index_nospec(vr->mifi, mrt->maxvif);
1915638cf4a2SEric Dumazet rcu_read_lock();
1916e1d001faSBreno Leitao vif = &mrt->vif_table[vr->mifi];
1917e1d001faSBreno Leitao if (VIF_EXISTS(mrt, vr->mifi)) {
1918e1d001faSBreno Leitao vr->icount = READ_ONCE(vif->pkt_in);
1919e1d001faSBreno Leitao vr->ocount = READ_ONCE(vif->pkt_out);
1920e1d001faSBreno Leitao vr->ibytes = READ_ONCE(vif->bytes_in);
1921e1d001faSBreno Leitao vr->obytes = READ_ONCE(vif->bytes_out);
1922638cf4a2SEric Dumazet rcu_read_unlock();
19237bc570c8SYOSHIFUJI Hideaki return 0;
19247bc570c8SYOSHIFUJI Hideaki }
1925638cf4a2SEric Dumazet rcu_read_unlock();
19267bc570c8SYOSHIFUJI Hideaki return -EADDRNOTAVAIL;
19277bc570c8SYOSHIFUJI Hideaki case SIOCGETSGCNT_IN6:
1928e1d001faSBreno Leitao sr = (struct sioc_sg_req6 *)arg;
19297bc570c8SYOSHIFUJI Hideaki
193087c418bfSYuval Mintz rcu_read_lock();
1931e1d001faSBreno Leitao c = ip6mr_cache_find(mrt, &sr->src.sin6_addr,
1932e1d001faSBreno Leitao &sr->grp.sin6_addr);
19337bc570c8SYOSHIFUJI Hideaki if (c) {
1934*5960f4d8SEric Dumazet sr->pktcnt = atomic_long_read(&c->_c.mfc_un.res.pkt);
1935*5960f4d8SEric Dumazet sr->bytecnt = atomic_long_read(&c->_c.mfc_un.res.bytes);
1936*5960f4d8SEric Dumazet sr->wrong_if = atomic_long_read(&c->_c.mfc_un.res.wrong_if);
193787c418bfSYuval Mintz rcu_read_unlock();
19387bc570c8SYOSHIFUJI Hideaki return 0;
19397bc570c8SYOSHIFUJI Hideaki }
194087c418bfSYuval Mintz rcu_read_unlock();
19417bc570c8SYOSHIFUJI Hideaki return -EADDRNOTAVAIL;
19427bc570c8SYOSHIFUJI Hideaki default:
19437bc570c8SYOSHIFUJI Hideaki return -ENOIOCTLCMD;
19447bc570c8SYOSHIFUJI Hideaki }
19457bc570c8SYOSHIFUJI Hideaki }
19467bc570c8SYOSHIFUJI Hideaki
1947e2d57766SDavid S. Miller #ifdef CONFIG_COMPAT
1948e2d57766SDavid S. Miller struct compat_sioc_sg_req6 {
1949e2d57766SDavid S. Miller struct sockaddr_in6 src;
1950e2d57766SDavid S. Miller struct sockaddr_in6 grp;
1951e2d57766SDavid S. Miller compat_ulong_t pktcnt;
1952e2d57766SDavid S. Miller compat_ulong_t bytecnt;
1953e2d57766SDavid S. Miller compat_ulong_t wrong_if;
1954e2d57766SDavid S. Miller };
1955e2d57766SDavid S. Miller
1956e2d57766SDavid S. Miller struct compat_sioc_mif_req6 {
1957e2d57766SDavid S. Miller mifi_t mifi;
1958e2d57766SDavid S. Miller compat_ulong_t icount;
1959e2d57766SDavid S. Miller compat_ulong_t ocount;
1960e2d57766SDavid S. Miller compat_ulong_t ibytes;
1961e2d57766SDavid S. Miller compat_ulong_t obytes;
1962e2d57766SDavid S. Miller };
1963e2d57766SDavid S. Miller
ip6mr_compat_ioctl(struct sock * sk,unsigned int cmd,void __user * arg)1964e2d57766SDavid S. Miller int ip6mr_compat_ioctl(struct sock *sk, unsigned int cmd, void __user *arg)
1965e2d57766SDavid S. Miller {
1966e2d57766SDavid S. Miller struct compat_sioc_sg_req6 sr;
1967e2d57766SDavid S. Miller struct compat_sioc_mif_req6 vr;
19686853f21fSYuval Mintz struct vif_device *vif;
1969e2d57766SDavid S. Miller struct mfc6_cache *c;
1970e2d57766SDavid S. Miller struct net *net = sock_net(sk);
1971b70432f7SYuval Mintz struct mr_table *mrt;
1972e2d57766SDavid S. Miller
1973e2d57766SDavid S. Miller mrt = ip6mr_get_table(net, raw6_sk(sk)->ip6mr_table ? : RT6_TABLE_DFLT);
197463159f29SIan Morris if (!mrt)
1975e2d57766SDavid S. Miller return -ENOENT;
1976e2d57766SDavid S. Miller
1977e2d57766SDavid S. Miller switch (cmd) {
1978e2d57766SDavid S. Miller case SIOCGETMIFCNT_IN6:
1979e2d57766SDavid S. Miller if (copy_from_user(&vr, arg, sizeof(vr)))
1980e2d57766SDavid S. Miller return -EFAULT;
1981e2d57766SDavid S. Miller if (vr.mifi >= mrt->maxvif)
1982e2d57766SDavid S. Miller return -EINVAL;
198369d2c867SGustavo A. R. Silva vr.mifi = array_index_nospec(vr.mifi, mrt->maxvif);
1984638cf4a2SEric Dumazet rcu_read_lock();
1985b70432f7SYuval Mintz vif = &mrt->vif_table[vr.mifi];
1986b70432f7SYuval Mintz if (VIF_EXISTS(mrt, vr.mifi)) {
1987638cf4a2SEric Dumazet vr.icount = READ_ONCE(vif->pkt_in);
1988638cf4a2SEric Dumazet vr.ocount = READ_ONCE(vif->pkt_out);
1989638cf4a2SEric Dumazet vr.ibytes = READ_ONCE(vif->bytes_in);
1990638cf4a2SEric Dumazet vr.obytes = READ_ONCE(vif->bytes_out);
1991638cf4a2SEric Dumazet rcu_read_unlock();
1992e2d57766SDavid S. Miller
1993e2d57766SDavid S. Miller if (copy_to_user(arg, &vr, sizeof(vr)))
1994e2d57766SDavid S. Miller return -EFAULT;
1995e2d57766SDavid S. Miller return 0;
1996e2d57766SDavid S. Miller }
1997638cf4a2SEric Dumazet rcu_read_unlock();
1998e2d57766SDavid S. Miller return -EADDRNOTAVAIL;
1999e2d57766SDavid S. Miller case SIOCGETSGCNT_IN6:
2000e2d57766SDavid S. Miller if (copy_from_user(&sr, arg, sizeof(sr)))
2001e2d57766SDavid S. Miller return -EFAULT;
2002e2d57766SDavid S. Miller
200387c418bfSYuval Mintz rcu_read_lock();
2004e2d57766SDavid S. Miller c = ip6mr_cache_find(mrt, &sr.src.sin6_addr, &sr.grp.sin6_addr);
2005e2d57766SDavid S. Miller if (c) {
2006*5960f4d8SEric Dumazet sr.pktcnt = atomic_long_read(&c->_c.mfc_un.res.pkt);
2007*5960f4d8SEric Dumazet sr.bytecnt = atomic_long_read(&c->_c.mfc_un.res.bytes);
2008*5960f4d8SEric Dumazet sr.wrong_if = atomic_long_read(&c->_c.mfc_un.res.wrong_if);
200987c418bfSYuval Mintz rcu_read_unlock();
2010e2d57766SDavid S. Miller
2011e2d57766SDavid S. Miller if (copy_to_user(arg, &sr, sizeof(sr)))
2012e2d57766SDavid S. Miller return -EFAULT;
2013e2d57766SDavid S. Miller return 0;
2014e2d57766SDavid S. Miller }
201587c418bfSYuval Mintz rcu_read_unlock();
2016e2d57766SDavid S. Miller return -EADDRNOTAVAIL;
2017e2d57766SDavid S. Miller default:
2018e2d57766SDavid S. Miller return -ENOIOCTLCMD;
2019e2d57766SDavid S. Miller }
2020e2d57766SDavid S. Miller }
2021e2d57766SDavid S. Miller #endif
20227bc570c8SYOSHIFUJI Hideaki
ip6mr_forward2_finish(struct net * net,struct sock * sk,struct sk_buff * skb)20230c4b51f0SEric W. Biederman static inline int ip6mr_forward2_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
20247bc570c8SYOSHIFUJI Hideaki {
202587c11f1dSIdo Schimmel IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)),
2026483a47d2SDenis V. Lunev IPSTATS_MIB_OUTFORWDATAGRAMS);
202713206b6bSEric W. Biederman return dst_output(net, sk, skb);
20287bc570c8SYOSHIFUJI Hideaki }
20297bc570c8SYOSHIFUJI Hideaki
20307bc570c8SYOSHIFUJI Hideaki /*
20317bc570c8SYOSHIFUJI Hideaki * Processing handlers for ip6mr_forward
20327bc570c8SYOSHIFUJI Hideaki */
20337bc570c8SYOSHIFUJI Hideaki
ip6mr_forward2(struct net * net,struct mr_table * mrt,struct sk_buff * skb,int vifi)2034b70432f7SYuval Mintz static int ip6mr_forward2(struct net *net, struct mr_table *mrt,
2035f5c6dfdeSDavid Ahern struct sk_buff *skb, int vifi)
20367bc570c8SYOSHIFUJI Hideaki {
2037b70432f7SYuval Mintz struct vif_device *vif = &mrt->vif_table[vifi];
2038ebc31979SEric Dumazet struct net_device *vif_dev;
2039ebc31979SEric Dumazet struct ipv6hdr *ipv6h;
20407bc570c8SYOSHIFUJI Hideaki struct dst_entry *dst;
20414c9483b2SDavid S. Miller struct flowi6 fl6;
20427bc570c8SYOSHIFUJI Hideaki
2043ebc31979SEric Dumazet vif_dev = vif_dev_read(vif);
2044ebc31979SEric Dumazet if (!vif_dev)
20457bc570c8SYOSHIFUJI Hideaki goto out_free;
20467bc570c8SYOSHIFUJI Hideaki
204714fb64e1SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_PIMSM_V2
204814fb64e1SYOSHIFUJI Hideaki if (vif->flags & MIFF_REGISTER) {
2049638cf4a2SEric Dumazet WRITE_ONCE(vif->pkt_out, vif->pkt_out + 1);
2050638cf4a2SEric Dumazet WRITE_ONCE(vif->bytes_out, vif->bytes_out + skb->len);
20512fad1ba3SEric Dumazet DEV_STATS_ADD(vif_dev, tx_bytes, skb->len);
20522fad1ba3SEric Dumazet DEV_STATS_INC(vif_dev, tx_packets);
20536bd52143SPatrick McHardy ip6mr_cache_report(mrt, skb, vifi, MRT6MSG_WHOLEPKT);
20548da73b73SIlpo Järvinen goto out_free;
205514fb64e1SYOSHIFUJI Hideaki }
205614fb64e1SYOSHIFUJI Hideaki #endif
205714fb64e1SYOSHIFUJI Hideaki
20587bc570c8SYOSHIFUJI Hideaki ipv6h = ipv6_hdr(skb);
20597bc570c8SYOSHIFUJI Hideaki
20604c9483b2SDavid S. Miller fl6 = (struct flowi6) {
20614c9483b2SDavid S. Miller .flowi6_oif = vif->link,
20624c9483b2SDavid S. Miller .daddr = ipv6h->daddr,
20637bc570c8SYOSHIFUJI Hideaki };
20647bc570c8SYOSHIFUJI Hideaki
20654c9483b2SDavid S. Miller dst = ip6_route_output(net, NULL, &fl6);
20665095d64dSRongQing.Li if (dst->error) {
20675095d64dSRongQing.Li dst_release(dst);
20687bc570c8SYOSHIFUJI Hideaki goto out_free;
20695095d64dSRongQing.Li }
20707bc570c8SYOSHIFUJI Hideaki
2071adf30907SEric Dumazet skb_dst_drop(skb);
2072adf30907SEric Dumazet skb_dst_set(skb, dst);
20737bc570c8SYOSHIFUJI Hideaki
20747bc570c8SYOSHIFUJI Hideaki /*
20757bc570c8SYOSHIFUJI Hideaki * RFC1584 teaches, that DVMRP/PIM router must deliver packets locally
20767bc570c8SYOSHIFUJI Hideaki * not only before forwarding, but after forwarding on all output
20777bc570c8SYOSHIFUJI Hideaki * interfaces. It is clear, if mrouter runs a multicasting
20787bc570c8SYOSHIFUJI Hideaki * program, it should receive packets not depending to what interface
20797bc570c8SYOSHIFUJI Hideaki * program is joined.
20807bc570c8SYOSHIFUJI Hideaki * If we will not make it, the program will have to join on all
20817bc570c8SYOSHIFUJI Hideaki * interfaces. On the other hand, multihoming host (or router, but
20827bc570c8SYOSHIFUJI Hideaki * not mrouter) cannot join to more than one interface - it will
20837bc570c8SYOSHIFUJI Hideaki * result in receiving multiple packets.
20847bc570c8SYOSHIFUJI Hideaki */
2085ebc31979SEric Dumazet skb->dev = vif_dev;
2086638cf4a2SEric Dumazet WRITE_ONCE(vif->pkt_out, vif->pkt_out + 1);
2087638cf4a2SEric Dumazet WRITE_ONCE(vif->bytes_out, vif->bytes_out + skb->len);
20887bc570c8SYOSHIFUJI Hideaki
20897bc570c8SYOSHIFUJI Hideaki /* We are about to write */
20907bc570c8SYOSHIFUJI Hideaki /* XXX: extension headers? */
2091ebc31979SEric Dumazet if (skb_cow(skb, sizeof(*ipv6h) + LL_RESERVED_SPACE(vif_dev)))
20927bc570c8SYOSHIFUJI Hideaki goto out_free;
20937bc570c8SYOSHIFUJI Hideaki
20947bc570c8SYOSHIFUJI Hideaki ipv6h = ipv6_hdr(skb);
20957bc570c8SYOSHIFUJI Hideaki ipv6h->hop_limit--;
20967bc570c8SYOSHIFUJI Hideaki
20977bc570c8SYOSHIFUJI Hideaki IP6CB(skb)->flags |= IP6SKB_FORWARDED;
20987bc570c8SYOSHIFUJI Hideaki
209929a26a56SEric W. Biederman return NF_HOOK(NFPROTO_IPV6, NF_INET_FORWARD,
2100ebc31979SEric Dumazet net, NULL, skb, skb->dev, vif_dev,
21017bc570c8SYOSHIFUJI Hideaki ip6mr_forward2_finish);
21027bc570c8SYOSHIFUJI Hideaki
21037bc570c8SYOSHIFUJI Hideaki out_free:
21047bc570c8SYOSHIFUJI Hideaki kfree_skb(skb);
21057bc570c8SYOSHIFUJI Hideaki return 0;
21067bc570c8SYOSHIFUJI Hideaki }
21077bc570c8SYOSHIFUJI Hideaki
2108a96f7a6aSEric Dumazet /* Called with rcu_read_lock() */
ip6mr_find_vif(struct mr_table * mrt,struct net_device * dev)2109b70432f7SYuval Mintz static int ip6mr_find_vif(struct mr_table *mrt, struct net_device *dev)
21107bc570c8SYOSHIFUJI Hideaki {
21117bc570c8SYOSHIFUJI Hideaki int ct;
21126bd52143SPatrick McHardy
2113db9eb7c8SEric Dumazet /* Pairs with WRITE_ONCE() in mif6_delete()/mif6_add() */
2114db9eb7c8SEric Dumazet for (ct = READ_ONCE(mrt->maxvif) - 1; ct >= 0; ct--) {
2115ebc31979SEric Dumazet if (rcu_access_pointer(mrt->vif_table[ct].dev) == dev)
21167bc570c8SYOSHIFUJI Hideaki break;
21177bc570c8SYOSHIFUJI Hideaki }
21187bc570c8SYOSHIFUJI Hideaki return ct;
21197bc570c8SYOSHIFUJI Hideaki }
21207bc570c8SYOSHIFUJI Hideaki
21219b1c21d8SEric Dumazet /* Called under rcu_read_lock() */
ip6_mr_forward(struct net * net,struct mr_table * mrt,struct net_device * dev,struct sk_buff * skb,struct mfc6_cache * c)2122b70432f7SYuval Mintz static void ip6_mr_forward(struct net *net, struct mr_table *mrt,
2123e4a38c0cSPatrick Ruddy struct net_device *dev, struct sk_buff *skb,
2124e4a38c0cSPatrick Ruddy struct mfc6_cache *c)
21257bc570c8SYOSHIFUJI Hideaki {
21267bc570c8SYOSHIFUJI Hideaki int psend = -1;
21277bc570c8SYOSHIFUJI Hideaki int vif, ct;
2128e4a38c0cSPatrick Ruddy int true_vifi = ip6mr_find_vif(mrt, dev);
21297bc570c8SYOSHIFUJI Hideaki
2130494fff56SYuval Mintz vif = c->_c.mfc_parent;
2131*5960f4d8SEric Dumazet atomic_long_inc(&c->_c.mfc_un.res.pkt);
2132*5960f4d8SEric Dumazet atomic_long_add(skb->len, &c->_c.mfc_un.res.bytes);
2133*5960f4d8SEric Dumazet WRITE_ONCE(c->_c.mfc_un.res.lastuse, jiffies);
21347bc570c8SYOSHIFUJI Hideaki
2135494fff56SYuval Mintz if (ipv6_addr_any(&c->mf6c_origin) && true_vifi >= 0) {
2136660b26dcSNicolas Dichtel struct mfc6_cache *cache_proxy;
2137660b26dcSNicolas Dichtel
213840dc2ca3SFabian Frederick /* For an (*,G) entry, we only check that the incoming
2139660b26dcSNicolas Dichtel * interface is part of the static tree.
2140660b26dcSNicolas Dichtel */
2141845c9a7aSYuval Mintz cache_proxy = mr_mfc_find_any_parent(mrt, vif);
2142660b26dcSNicolas Dichtel if (cache_proxy &&
2143a7e555d4SEric Dumazet cache_proxy->_c.mfc_un.res.ttls[true_vifi] < 255)
2144660b26dcSNicolas Dichtel goto forward;
2145660b26dcSNicolas Dichtel }
2146660b26dcSNicolas Dichtel
214714fb64e1SYOSHIFUJI Hideaki /*
214814fb64e1SYOSHIFUJI Hideaki * Wrong interface: drop packet and (maybe) send PIM assert.
214914fb64e1SYOSHIFUJI Hideaki */
2150ebc31979SEric Dumazet if (rcu_access_pointer(mrt->vif_table[vif].dev) != dev) {
2151*5960f4d8SEric Dumazet atomic_long_inc(&c->_c.mfc_un.res.wrong_if);
215214fb64e1SYOSHIFUJI Hideaki
21536bd52143SPatrick McHardy if (true_vifi >= 0 && mrt->mroute_do_assert &&
215414fb64e1SYOSHIFUJI Hideaki /* pimsm uses asserts, when switching from RPT to SPT,
215514fb64e1SYOSHIFUJI Hideaki so that we cannot check that packet arrived on an oif.
215614fb64e1SYOSHIFUJI Hideaki It is bad, but otherwise we would need to move pretty
215714fb64e1SYOSHIFUJI Hideaki large chunk of pimd to kernel. Ough... --ANK
215814fb64e1SYOSHIFUJI Hideaki */
21596bd52143SPatrick McHardy (mrt->mroute_do_pim ||
2160494fff56SYuval Mintz c->_c.mfc_un.res.ttls[true_vifi] < 255) &&
216114fb64e1SYOSHIFUJI Hideaki time_after(jiffies,
2162494fff56SYuval Mintz c->_c.mfc_un.res.last_assert +
2163494fff56SYuval Mintz MFC_ASSERT_THRESH)) {
2164494fff56SYuval Mintz c->_c.mfc_un.res.last_assert = jiffies;
21656bd52143SPatrick McHardy ip6mr_cache_report(mrt, skb, true_vifi, MRT6MSG_WRONGMIF);
21664b340a5aSMobashshera Rasool if (mrt->mroute_do_wrvifwhole)
21674b340a5aSMobashshera Rasool ip6mr_cache_report(mrt, skb, true_vifi,
21684b340a5aSMobashshera Rasool MRT6MSG_WRMIFWHOLE);
216914fb64e1SYOSHIFUJI Hideaki }
217014fb64e1SYOSHIFUJI Hideaki goto dont_forward;
217114fb64e1SYOSHIFUJI Hideaki }
217214fb64e1SYOSHIFUJI Hideaki
2173660b26dcSNicolas Dichtel forward:
2174638cf4a2SEric Dumazet WRITE_ONCE(mrt->vif_table[vif].pkt_in,
2175638cf4a2SEric Dumazet mrt->vif_table[vif].pkt_in + 1);
2176638cf4a2SEric Dumazet WRITE_ONCE(mrt->vif_table[vif].bytes_in,
2177638cf4a2SEric Dumazet mrt->vif_table[vif].bytes_in + skb->len);
21787bc570c8SYOSHIFUJI Hideaki
21797bc570c8SYOSHIFUJI Hideaki /*
21807bc570c8SYOSHIFUJI Hideaki * Forward the frame
21817bc570c8SYOSHIFUJI Hideaki */
2182494fff56SYuval Mintz if (ipv6_addr_any(&c->mf6c_origin) &&
2183494fff56SYuval Mintz ipv6_addr_any(&c->mf6c_mcastgrp)) {
2184660b26dcSNicolas Dichtel if (true_vifi >= 0 &&
2185494fff56SYuval Mintz true_vifi != c->_c.mfc_parent &&
2186660b26dcSNicolas Dichtel ipv6_hdr(skb)->hop_limit >
2187494fff56SYuval Mintz c->_c.mfc_un.res.ttls[c->_c.mfc_parent]) {
2188660b26dcSNicolas Dichtel /* It's an (*,*) entry and the packet is not coming from
2189660b26dcSNicolas Dichtel * the upstream: forward the packet to the upstream
2190660b26dcSNicolas Dichtel * only.
2191660b26dcSNicolas Dichtel */
2192494fff56SYuval Mintz psend = c->_c.mfc_parent;
2193660b26dcSNicolas Dichtel goto last_forward;
2194660b26dcSNicolas Dichtel }
2195660b26dcSNicolas Dichtel goto dont_forward;
2196660b26dcSNicolas Dichtel }
2197494fff56SYuval Mintz for (ct = c->_c.mfc_un.res.maxvif - 1;
2198494fff56SYuval Mintz ct >= c->_c.mfc_un.res.minvif; ct--) {
2199660b26dcSNicolas Dichtel /* For (*,G) entry, don't forward to the incoming interface */
2200494fff56SYuval Mintz if ((!ipv6_addr_any(&c->mf6c_origin) || ct != true_vifi) &&
2201494fff56SYuval Mintz ipv6_hdr(skb)->hop_limit > c->_c.mfc_un.res.ttls[ct]) {
22027bc570c8SYOSHIFUJI Hideaki if (psend != -1) {
22037bc570c8SYOSHIFUJI Hideaki struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
22047bc570c8SYOSHIFUJI Hideaki if (skb2)
2205f5c6dfdeSDavid Ahern ip6mr_forward2(net, mrt, skb2, psend);
22067bc570c8SYOSHIFUJI Hideaki }
22077bc570c8SYOSHIFUJI Hideaki psend = ct;
22087bc570c8SYOSHIFUJI Hideaki }
22097bc570c8SYOSHIFUJI Hideaki }
2210660b26dcSNicolas Dichtel last_forward:
22117bc570c8SYOSHIFUJI Hideaki if (psend != -1) {
2212f5c6dfdeSDavid Ahern ip6mr_forward2(net, mrt, skb, psend);
22132b52c3adSRami Rosen return;
22147bc570c8SYOSHIFUJI Hideaki }
22157bc570c8SYOSHIFUJI Hideaki
221614fb64e1SYOSHIFUJI Hideaki dont_forward:
22177bc570c8SYOSHIFUJI Hideaki kfree_skb(skb);
22187bc570c8SYOSHIFUJI Hideaki }
22197bc570c8SYOSHIFUJI Hideaki
22207bc570c8SYOSHIFUJI Hideaki
22217bc570c8SYOSHIFUJI Hideaki /*
22227bc570c8SYOSHIFUJI Hideaki * Multicast packets for forwarding arrive here
22237bc570c8SYOSHIFUJI Hideaki */
22247bc570c8SYOSHIFUJI Hideaki
ip6_mr_input(struct sk_buff * skb)22257bc570c8SYOSHIFUJI Hideaki int ip6_mr_input(struct sk_buff *skb)
22267bc570c8SYOSHIFUJI Hideaki {
22277bc570c8SYOSHIFUJI Hideaki struct mfc6_cache *cache;
22288229efdaSBenjamin Thery struct net *net = dev_net(skb->dev);
2229b70432f7SYuval Mintz struct mr_table *mrt;
22304c9483b2SDavid S. Miller struct flowi6 fl6 = {
22314c9483b2SDavid S. Miller .flowi6_iif = skb->dev->ifindex,
22324c9483b2SDavid S. Miller .flowi6_mark = skb->mark,
2233d1db275dSPatrick McHardy };
2234d1db275dSPatrick McHardy int err;
2235e4a38c0cSPatrick Ruddy struct net_device *dev;
2236e4a38c0cSPatrick Ruddy
2237e4a38c0cSPatrick Ruddy /* skb->dev passed in is the master dev for vrfs.
2238e4a38c0cSPatrick Ruddy * Get the proper interface that does have a vif associated with it.
2239e4a38c0cSPatrick Ruddy */
2240e4a38c0cSPatrick Ruddy dev = skb->dev;
2241e4a38c0cSPatrick Ruddy if (netif_is_l3_master(skb->dev)) {
2242e4a38c0cSPatrick Ruddy dev = dev_get_by_index_rcu(net, IPCB(skb)->iif);
2243e4a38c0cSPatrick Ruddy if (!dev) {
2244e4a38c0cSPatrick Ruddy kfree_skb(skb);
2245e4a38c0cSPatrick Ruddy return -ENODEV;
2246e4a38c0cSPatrick Ruddy }
2247e4a38c0cSPatrick Ruddy }
2248d1db275dSPatrick McHardy
22494c9483b2SDavid S. Miller err = ip6mr_fib_lookup(net, &fl6, &mrt);
22502015de5fSBen Greear if (err < 0) {
22512015de5fSBen Greear kfree_skb(skb);
2252d1db275dSPatrick McHardy return err;
22532015de5fSBen Greear }
22547bc570c8SYOSHIFUJI Hideaki
22556bd52143SPatrick McHardy cache = ip6mr_cache_find(mrt,
22568229efdaSBenjamin Thery &ipv6_hdr(skb)->saddr, &ipv6_hdr(skb)->daddr);
225763159f29SIan Morris if (!cache) {
2258e4a38c0cSPatrick Ruddy int vif = ip6mr_find_vif(mrt, dev);
2259660b26dcSNicolas Dichtel
2260660b26dcSNicolas Dichtel if (vif >= 0)
2261660b26dcSNicolas Dichtel cache = ip6mr_cache_find_any(mrt,
2262660b26dcSNicolas Dichtel &ipv6_hdr(skb)->daddr,
2263660b26dcSNicolas Dichtel vif);
2264660b26dcSNicolas Dichtel }
22657bc570c8SYOSHIFUJI Hideaki
22667bc570c8SYOSHIFUJI Hideaki /*
22677bc570c8SYOSHIFUJI Hideaki * No usable cache entry
22687bc570c8SYOSHIFUJI Hideaki */
226963159f29SIan Morris if (!cache) {
22707bc570c8SYOSHIFUJI Hideaki int vif;
22717bc570c8SYOSHIFUJI Hideaki
2272e4a38c0cSPatrick Ruddy vif = ip6mr_find_vif(mrt, dev);
22737bc570c8SYOSHIFUJI Hideaki if (vif >= 0) {
2274e4a38c0cSPatrick Ruddy int err = ip6mr_cache_unresolved(mrt, vif, skb, dev);
22757bc570c8SYOSHIFUJI Hideaki
22767bc570c8SYOSHIFUJI Hideaki return err;
22777bc570c8SYOSHIFUJI Hideaki }
22787bc570c8SYOSHIFUJI Hideaki kfree_skb(skb);
22797bc570c8SYOSHIFUJI Hideaki return -ENODEV;
22807bc570c8SYOSHIFUJI Hideaki }
22817bc570c8SYOSHIFUJI Hideaki
2282e4a38c0cSPatrick Ruddy ip6_mr_forward(net, mrt, dev, skb, cache);
22837bc570c8SYOSHIFUJI Hideaki
22847bc570c8SYOSHIFUJI Hideaki return 0;
22857bc570c8SYOSHIFUJI Hideaki }
22867bc570c8SYOSHIFUJI Hideaki
ip6mr_get_route(struct net * net,struct sk_buff * skb,struct rtmsg * rtm,u32 portid)22872cf75070SNikolay Aleksandrov int ip6mr_get_route(struct net *net, struct sk_buff *skb, struct rtmsg *rtm,
2288fd61c6baSDavid Ahern u32 portid)
22897bc570c8SYOSHIFUJI Hideaki {
22907bc570c8SYOSHIFUJI Hideaki int err;
2291b70432f7SYuval Mintz struct mr_table *mrt;
22927bc570c8SYOSHIFUJI Hideaki struct mfc6_cache *cache;
2293797a4c1fSEric Dumazet struct rt6_info *rt = dst_rt6_info(skb_dst(skb));
22947bc570c8SYOSHIFUJI Hideaki
22956fa40a29SEric Dumazet rcu_read_lock();
2296bba7909bSPaolo Abeni mrt = __ip6mr_get_table(net, RT6_TABLE_DFLT);
2297bba7909bSPaolo Abeni if (!mrt) {
2298bba7909bSPaolo Abeni rcu_read_unlock();
2299bba7909bSPaolo Abeni return -ENOENT;
2300bba7909bSPaolo Abeni }
2301bba7909bSPaolo Abeni
23026bd52143SPatrick McHardy cache = ip6mr_cache_find(mrt, &rt->rt6i_src.addr, &rt->rt6i_dst.addr);
2303660b26dcSNicolas Dichtel if (!cache && skb->dev) {
2304660b26dcSNicolas Dichtel int vif = ip6mr_find_vif(mrt, skb->dev);
2305660b26dcSNicolas Dichtel
2306660b26dcSNicolas Dichtel if (vif >= 0)
2307660b26dcSNicolas Dichtel cache = ip6mr_cache_find_any(mrt, &rt->rt6i_dst.addr,
2308660b26dcSNicolas Dichtel vif);
2309660b26dcSNicolas Dichtel }
23107bc570c8SYOSHIFUJI Hideaki
23117bc570c8SYOSHIFUJI Hideaki if (!cache) {
23127bc570c8SYOSHIFUJI Hideaki struct sk_buff *skb2;
23137bc570c8SYOSHIFUJI Hideaki struct ipv6hdr *iph;
23147bc570c8SYOSHIFUJI Hideaki struct net_device *dev;
23157bc570c8SYOSHIFUJI Hideaki int vif;
23167bc570c8SYOSHIFUJI Hideaki
23177bc570c8SYOSHIFUJI Hideaki dev = skb->dev;
231863159f29SIan Morris if (!dev || (vif = ip6mr_find_vif(mrt, dev)) < 0) {
23196fa40a29SEric Dumazet rcu_read_unlock();
23207bc570c8SYOSHIFUJI Hideaki return -ENODEV;
23217bc570c8SYOSHIFUJI Hideaki }
23227bc570c8SYOSHIFUJI Hideaki
23237bc570c8SYOSHIFUJI Hideaki /* really correct? */
23247bc570c8SYOSHIFUJI Hideaki skb2 = alloc_skb(sizeof(struct ipv6hdr), GFP_ATOMIC);
23257bc570c8SYOSHIFUJI Hideaki if (!skb2) {
23266fa40a29SEric Dumazet rcu_read_unlock();
23277bc570c8SYOSHIFUJI Hideaki return -ENOMEM;
23287bc570c8SYOSHIFUJI Hideaki }
23297bc570c8SYOSHIFUJI Hideaki
23302cf75070SNikolay Aleksandrov NETLINK_CB(skb2).portid = portid;
23317bc570c8SYOSHIFUJI Hideaki skb_reset_transport_header(skb2);
23327bc570c8SYOSHIFUJI Hideaki
23337bc570c8SYOSHIFUJI Hideaki skb_put(skb2, sizeof(struct ipv6hdr));
23347bc570c8SYOSHIFUJI Hideaki skb_reset_network_header(skb2);
23357bc570c8SYOSHIFUJI Hideaki
23367bc570c8SYOSHIFUJI Hideaki iph = ipv6_hdr(skb2);
23377bc570c8SYOSHIFUJI Hideaki iph->version = 0;
23387bc570c8SYOSHIFUJI Hideaki iph->priority = 0;
23397bc570c8SYOSHIFUJI Hideaki iph->flow_lbl[0] = 0;
23407bc570c8SYOSHIFUJI Hideaki iph->flow_lbl[1] = 0;
23417bc570c8SYOSHIFUJI Hideaki iph->flow_lbl[2] = 0;
23427bc570c8SYOSHIFUJI Hideaki iph->payload_len = 0;
23437bc570c8SYOSHIFUJI Hideaki iph->nexthdr = IPPROTO_NONE;
23447bc570c8SYOSHIFUJI Hideaki iph->hop_limit = 0;
23454e3fd7a0SAlexey Dobriyan iph->saddr = rt->rt6i_src.addr;
23464e3fd7a0SAlexey Dobriyan iph->daddr = rt->rt6i_dst.addr;
23477bc570c8SYOSHIFUJI Hideaki
2348e4a38c0cSPatrick Ruddy err = ip6mr_cache_unresolved(mrt, vif, skb2, dev);
23496fa40a29SEric Dumazet rcu_read_unlock();
23507bc570c8SYOSHIFUJI Hideaki
23517bc570c8SYOSHIFUJI Hideaki return err;
23527bc570c8SYOSHIFUJI Hideaki }
23537bc570c8SYOSHIFUJI Hideaki
23547b0db857SYuval Mintz err = mr_fill_mroute(mrt, skb, &cache->_c, rtm);
23556fa40a29SEric Dumazet rcu_read_unlock();
23567bc570c8SYOSHIFUJI Hideaki return err;
23577bc570c8SYOSHIFUJI Hideaki }
23587bc570c8SYOSHIFUJI Hideaki
ip6mr_fill_mroute(struct mr_table * mrt,struct sk_buff * skb,u32 portid,u32 seq,struct mfc6_cache * c,int cmd,int flags)2359b70432f7SYuval Mintz static int ip6mr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb,
2360f518338bSNicolas Dichtel u32 portid, u32 seq, struct mfc6_cache *c, int cmd,
2361f518338bSNicolas Dichtel int flags)
23625b285cacSPatrick McHardy {
23635b285cacSPatrick McHardy struct nlmsghdr *nlh;
23645b285cacSPatrick McHardy struct rtmsg *rtm;
23651eb99af5SNicolas Dichtel int err;
23665b285cacSPatrick McHardy
2367f518338bSNicolas Dichtel nlh = nlmsg_put(skb, portid, seq, cmd, sizeof(*rtm), flags);
236863159f29SIan Morris if (!nlh)
23695b285cacSPatrick McHardy return -EMSGSIZE;
23705b285cacSPatrick McHardy
23715b285cacSPatrick McHardy rtm = nlmsg_data(nlh);
2372193c1e47SNicolas Dichtel rtm->rtm_family = RTNL_FAMILY_IP6MR;
23735b285cacSPatrick McHardy rtm->rtm_dst_len = 128;
23745b285cacSPatrick McHardy rtm->rtm_src_len = 128;
23755b285cacSPatrick McHardy rtm->rtm_tos = 0;
23765b285cacSPatrick McHardy rtm->rtm_table = mrt->id;
2377c78679e8SDavid S. Miller if (nla_put_u32(skb, RTA_TABLE, mrt->id))
2378c78679e8SDavid S. Miller goto nla_put_failure;
23791eb99af5SNicolas Dichtel rtm->rtm_type = RTN_MULTICAST;
23805b285cacSPatrick McHardy rtm->rtm_scope = RT_SCOPE_UNIVERSE;
2381494fff56SYuval Mintz if (c->_c.mfc_flags & MFC_STATIC)
23829a68ac72SNicolas Dichtel rtm->rtm_protocol = RTPROT_STATIC;
23839a68ac72SNicolas Dichtel else
23849a68ac72SNicolas Dichtel rtm->rtm_protocol = RTPROT_MROUTED;
23855b285cacSPatrick McHardy rtm->rtm_flags = 0;
23865b285cacSPatrick McHardy
2387930345eaSJiri Benc if (nla_put_in6_addr(skb, RTA_SRC, &c->mf6c_origin) ||
2388930345eaSJiri Benc nla_put_in6_addr(skb, RTA_DST, &c->mf6c_mcastgrp))
2389c78679e8SDavid S. Miller goto nla_put_failure;
23907b0db857SYuval Mintz err = mr_fill_mroute(mrt, skb, &c->_c, rtm);
23911eb99af5SNicolas Dichtel /* do not break the dump if cache is unresolved */
23921eb99af5SNicolas Dichtel if (err < 0 && err != -ENOENT)
23935b285cacSPatrick McHardy goto nla_put_failure;
23945b285cacSPatrick McHardy
2395053c095aSJohannes Berg nlmsg_end(skb, nlh);
2396053c095aSJohannes Berg return 0;
23975b285cacSPatrick McHardy
23985b285cacSPatrick McHardy nla_put_failure:
23995b285cacSPatrick McHardy nlmsg_cancel(skb, nlh);
24005b285cacSPatrick McHardy return -EMSGSIZE;
24015b285cacSPatrick McHardy }
24025b285cacSPatrick McHardy
_ip6mr_fill_mroute(struct mr_table * mrt,struct sk_buff * skb,u32 portid,u32 seq,struct mr_mfc * c,int cmd,int flags)24037b0db857SYuval Mintz static int _ip6mr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb,
24047b0db857SYuval Mintz u32 portid, u32 seq, struct mr_mfc *c,
24057b0db857SYuval Mintz int cmd, int flags)
24067b0db857SYuval Mintz {
24077b0db857SYuval Mintz return ip6mr_fill_mroute(mrt, skb, portid, seq, (struct mfc6_cache *)c,
24087b0db857SYuval Mintz cmd, flags);
24097b0db857SYuval Mintz }
24107b0db857SYuval Mintz
mr6_msgsize(bool unresolved,int maxvif)2411812e44ddSNicolas Dichtel static int mr6_msgsize(bool unresolved, int maxvif)
2412812e44ddSNicolas Dichtel {
2413812e44ddSNicolas Dichtel size_t len =
2414812e44ddSNicolas Dichtel NLMSG_ALIGN(sizeof(struct rtmsg))
2415812e44ddSNicolas Dichtel + nla_total_size(4) /* RTA_TABLE */
2416812e44ddSNicolas Dichtel + nla_total_size(sizeof(struct in6_addr)) /* RTA_SRC */
2417812e44ddSNicolas Dichtel + nla_total_size(sizeof(struct in6_addr)) /* RTA_DST */
2418812e44ddSNicolas Dichtel ;
2419812e44ddSNicolas Dichtel
2420812e44ddSNicolas Dichtel if (!unresolved)
2421812e44ddSNicolas Dichtel len = len
2422812e44ddSNicolas Dichtel + nla_total_size(4) /* RTA_IIF */
2423812e44ddSNicolas Dichtel + nla_total_size(0) /* RTA_MULTIPATH */
2424812e44ddSNicolas Dichtel + maxvif * NLA_ALIGN(sizeof(struct rtnexthop))
2425812e44ddSNicolas Dichtel /* RTA_MFC_STATS */
24263d6b66c1SNicolas Dichtel + nla_total_size_64bit(sizeof(struct rta_mfc_stats))
2427812e44ddSNicolas Dichtel ;
2428812e44ddSNicolas Dichtel
2429812e44ddSNicolas Dichtel return len;
2430812e44ddSNicolas Dichtel }
2431812e44ddSNicolas Dichtel
mr6_netlink_event(struct mr_table * mrt,struct mfc6_cache * mfc,int cmd)2432b70432f7SYuval Mintz static void mr6_netlink_event(struct mr_table *mrt, struct mfc6_cache *mfc,
2433812e44ddSNicolas Dichtel int cmd)
2434812e44ddSNicolas Dichtel {
2435812e44ddSNicolas Dichtel struct net *net = read_pnet(&mrt->net);
2436812e44ddSNicolas Dichtel struct sk_buff *skb;
2437812e44ddSNicolas Dichtel int err = -ENOBUFS;
2438812e44ddSNicolas Dichtel
2439494fff56SYuval Mintz skb = nlmsg_new(mr6_msgsize(mfc->_c.mfc_parent >= MAXMIFS, mrt->maxvif),
2440812e44ddSNicolas Dichtel GFP_ATOMIC);
244163159f29SIan Morris if (!skb)
2442812e44ddSNicolas Dichtel goto errout;
2443812e44ddSNicolas Dichtel
2444f518338bSNicolas Dichtel err = ip6mr_fill_mroute(mrt, skb, 0, 0, mfc, cmd, 0);
2445812e44ddSNicolas Dichtel if (err < 0)
2446812e44ddSNicolas Dichtel goto errout;
2447812e44ddSNicolas Dichtel
2448812e44ddSNicolas Dichtel rtnl_notify(skb, net, 0, RTNLGRP_IPV6_MROUTE, NULL, GFP_ATOMIC);
2449812e44ddSNicolas Dichtel return;
2450812e44ddSNicolas Dichtel
2451812e44ddSNicolas Dichtel errout:
2452812e44ddSNicolas Dichtel kfree_skb(skb);
2453812e44ddSNicolas Dichtel if (err < 0)
2454812e44ddSNicolas Dichtel rtnl_set_sk_err(net, RTNLGRP_IPV6_MROUTE, err);
2455812e44ddSNicolas Dichtel }
2456812e44ddSNicolas Dichtel
mrt6msg_netlink_msgsize(size_t payloadlen)2457dd12d15cSJulien Gomes static size_t mrt6msg_netlink_msgsize(size_t payloadlen)
2458dd12d15cSJulien Gomes {
2459dd12d15cSJulien Gomes size_t len =
2460dd12d15cSJulien Gomes NLMSG_ALIGN(sizeof(struct rtgenmsg))
2461dd12d15cSJulien Gomes + nla_total_size(1) /* IP6MRA_CREPORT_MSGTYPE */
2462dd12d15cSJulien Gomes + nla_total_size(4) /* IP6MRA_CREPORT_MIF_ID */
2463dd12d15cSJulien Gomes /* IP6MRA_CREPORT_SRC_ADDR */
2464dd12d15cSJulien Gomes + nla_total_size(sizeof(struct in6_addr))
2465dd12d15cSJulien Gomes /* IP6MRA_CREPORT_DST_ADDR */
2466dd12d15cSJulien Gomes + nla_total_size(sizeof(struct in6_addr))
2467dd12d15cSJulien Gomes /* IP6MRA_CREPORT_PKT */
2468dd12d15cSJulien Gomes + nla_total_size(payloadlen)
2469dd12d15cSJulien Gomes ;
2470dd12d15cSJulien Gomes
2471dd12d15cSJulien Gomes return len;
2472dd12d15cSJulien Gomes }
2473dd12d15cSJulien Gomes
mrt6msg_netlink_event(const struct mr_table * mrt,struct sk_buff * pkt)24743493a5b7SEric Dumazet static void mrt6msg_netlink_event(const struct mr_table *mrt, struct sk_buff *pkt)
2475dd12d15cSJulien Gomes {
2476dd12d15cSJulien Gomes struct net *net = read_pnet(&mrt->net);
2477dd12d15cSJulien Gomes struct nlmsghdr *nlh;
2478dd12d15cSJulien Gomes struct rtgenmsg *rtgenm;
2479dd12d15cSJulien Gomes struct mrt6msg *msg;
2480dd12d15cSJulien Gomes struct sk_buff *skb;
2481dd12d15cSJulien Gomes struct nlattr *nla;
2482dd12d15cSJulien Gomes int payloadlen;
2483dd12d15cSJulien Gomes
2484dd12d15cSJulien Gomes payloadlen = pkt->len - sizeof(struct mrt6msg);
2485dd12d15cSJulien Gomes msg = (struct mrt6msg *)skb_transport_header(pkt);
2486dd12d15cSJulien Gomes
2487dd12d15cSJulien Gomes skb = nlmsg_new(mrt6msg_netlink_msgsize(payloadlen), GFP_ATOMIC);
2488dd12d15cSJulien Gomes if (!skb)
2489dd12d15cSJulien Gomes goto errout;
2490dd12d15cSJulien Gomes
2491dd12d15cSJulien Gomes nlh = nlmsg_put(skb, 0, 0, RTM_NEWCACHEREPORT,
2492dd12d15cSJulien Gomes sizeof(struct rtgenmsg), 0);
2493dd12d15cSJulien Gomes if (!nlh)
2494dd12d15cSJulien Gomes goto errout;
2495dd12d15cSJulien Gomes rtgenm = nlmsg_data(nlh);
2496dd12d15cSJulien Gomes rtgenm->rtgen_family = RTNL_FAMILY_IP6MR;
2497dd12d15cSJulien Gomes if (nla_put_u8(skb, IP6MRA_CREPORT_MSGTYPE, msg->im6_msgtype) ||
2498dd12d15cSJulien Gomes nla_put_u32(skb, IP6MRA_CREPORT_MIF_ID, msg->im6_mif) ||
2499dd12d15cSJulien Gomes nla_put_in6_addr(skb, IP6MRA_CREPORT_SRC_ADDR,
2500dd12d15cSJulien Gomes &msg->im6_src) ||
2501dd12d15cSJulien Gomes nla_put_in6_addr(skb, IP6MRA_CREPORT_DST_ADDR,
2502dd12d15cSJulien Gomes &msg->im6_dst))
2503dd12d15cSJulien Gomes goto nla_put_failure;
2504dd12d15cSJulien Gomes
2505dd12d15cSJulien Gomes nla = nla_reserve(skb, IP6MRA_CREPORT_PKT, payloadlen);
2506dd12d15cSJulien Gomes if (!nla || skb_copy_bits(pkt, sizeof(struct mrt6msg),
2507dd12d15cSJulien Gomes nla_data(nla), payloadlen))
2508dd12d15cSJulien Gomes goto nla_put_failure;
2509dd12d15cSJulien Gomes
2510dd12d15cSJulien Gomes nlmsg_end(skb, nlh);
2511dd12d15cSJulien Gomes
2512dd12d15cSJulien Gomes rtnl_notify(skb, net, 0, RTNLGRP_IPV6_MROUTE_R, NULL, GFP_ATOMIC);
2513dd12d15cSJulien Gomes return;
2514dd12d15cSJulien Gomes
2515dd12d15cSJulien Gomes nla_put_failure:
2516dd12d15cSJulien Gomes nlmsg_cancel(skb, nlh);
2517dd12d15cSJulien Gomes errout:
2518dd12d15cSJulien Gomes kfree_skb(skb);
2519dd12d15cSJulien Gomes rtnl_set_sk_err(net, RTNLGRP_IPV6_MROUTE_R, -ENOBUFS);
2520dd12d15cSJulien Gomes }
2521dd12d15cSJulien Gomes
2522d7c31cbdSDavid Lamparter static const struct nla_policy ip6mr_getroute_policy[RTA_MAX + 1] = {
2523d7c31cbdSDavid Lamparter [RTA_SRC] = NLA_POLICY_EXACT_LEN(sizeof(struct in6_addr)),
2524d7c31cbdSDavid Lamparter [RTA_DST] = NLA_POLICY_EXACT_LEN(sizeof(struct in6_addr)),
2525d7c31cbdSDavid Lamparter [RTA_TABLE] = { .type = NLA_U32 },
2526d7c31cbdSDavid Lamparter };
2527d7c31cbdSDavid Lamparter
ip6mr_rtm_valid_getroute_req(struct sk_buff * skb,const struct nlmsghdr * nlh,struct nlattr ** tb,struct netlink_ext_ack * extack)2528d7c31cbdSDavid Lamparter static int ip6mr_rtm_valid_getroute_req(struct sk_buff *skb,
2529d7c31cbdSDavid Lamparter const struct nlmsghdr *nlh,
2530d7c31cbdSDavid Lamparter struct nlattr **tb,
2531d7c31cbdSDavid Lamparter struct netlink_ext_ack *extack)
2532d7c31cbdSDavid Lamparter {
2533d7c31cbdSDavid Lamparter struct rtmsg *rtm;
2534d7c31cbdSDavid Lamparter int err;
2535d7c31cbdSDavid Lamparter
2536d7c31cbdSDavid Lamparter err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, ip6mr_getroute_policy,
2537d7c31cbdSDavid Lamparter extack);
2538d7c31cbdSDavid Lamparter if (err)
2539d7c31cbdSDavid Lamparter return err;
2540d7c31cbdSDavid Lamparter
2541d7c31cbdSDavid Lamparter rtm = nlmsg_data(nlh);
2542d7c31cbdSDavid Lamparter if ((rtm->rtm_src_len && rtm->rtm_src_len != 128) ||
2543d7c31cbdSDavid Lamparter (rtm->rtm_dst_len && rtm->rtm_dst_len != 128) ||
2544d7c31cbdSDavid Lamparter rtm->rtm_tos || rtm->rtm_table || rtm->rtm_protocol ||
2545d7c31cbdSDavid Lamparter rtm->rtm_scope || rtm->rtm_type || rtm->rtm_flags) {
2546d7c31cbdSDavid Lamparter NL_SET_ERR_MSG_MOD(extack,
2547d7c31cbdSDavid Lamparter "Invalid values in header for multicast route get request");
2548d7c31cbdSDavid Lamparter return -EINVAL;
2549d7c31cbdSDavid Lamparter }
2550d7c31cbdSDavid Lamparter
2551d7c31cbdSDavid Lamparter if ((tb[RTA_SRC] && !rtm->rtm_src_len) ||
2552d7c31cbdSDavid Lamparter (tb[RTA_DST] && !rtm->rtm_dst_len)) {
2553d7c31cbdSDavid Lamparter NL_SET_ERR_MSG_MOD(extack, "rtm_src_len and rtm_dst_len must be 128 for IPv6");
2554d7c31cbdSDavid Lamparter return -EINVAL;
2555d7c31cbdSDavid Lamparter }
2556d7c31cbdSDavid Lamparter
2557d7c31cbdSDavid Lamparter return 0;
2558d7c31cbdSDavid Lamparter }
2559d7c31cbdSDavid Lamparter
ip6mr_rtm_getroute(struct sk_buff * in_skb,struct nlmsghdr * nlh,struct netlink_ext_ack * extack)2560d7c31cbdSDavid Lamparter static int ip6mr_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh,
2561d7c31cbdSDavid Lamparter struct netlink_ext_ack *extack)
2562d7c31cbdSDavid Lamparter {
2563d7c31cbdSDavid Lamparter struct net *net = sock_net(in_skb->sk);
2564d7c31cbdSDavid Lamparter struct in6_addr src = {}, grp = {};
2565d7c31cbdSDavid Lamparter struct nlattr *tb[RTA_MAX + 1];
2566d7c31cbdSDavid Lamparter struct mfc6_cache *cache;
2567d7c31cbdSDavid Lamparter struct mr_table *mrt;
2568d7c31cbdSDavid Lamparter struct sk_buff *skb;
2569d7c31cbdSDavid Lamparter u32 tableid;
2570d7c31cbdSDavid Lamparter int err;
2571d7c31cbdSDavid Lamparter
2572d7c31cbdSDavid Lamparter err = ip6mr_rtm_valid_getroute_req(in_skb, nlh, tb, extack);
2573d7c31cbdSDavid Lamparter if (err < 0)
2574d7c31cbdSDavid Lamparter return err;
2575d7c31cbdSDavid Lamparter
2576d7c31cbdSDavid Lamparter if (tb[RTA_SRC])
2577d7c31cbdSDavid Lamparter src = nla_get_in6_addr(tb[RTA_SRC]);
2578d7c31cbdSDavid Lamparter if (tb[RTA_DST])
2579d7c31cbdSDavid Lamparter grp = nla_get_in6_addr(tb[RTA_DST]);
2580d7c31cbdSDavid Lamparter tableid = tb[RTA_TABLE] ? nla_get_u32(tb[RTA_TABLE]) : 0;
2581d7c31cbdSDavid Lamparter
2582bba7909bSPaolo Abeni mrt = __ip6mr_get_table(net, tableid ?: RT_TABLE_DEFAULT);
2583d7c31cbdSDavid Lamparter if (!mrt) {
2584d7c31cbdSDavid Lamparter NL_SET_ERR_MSG_MOD(extack, "MR table does not exist");
2585d7c31cbdSDavid Lamparter return -ENOENT;
2586d7c31cbdSDavid Lamparter }
2587d7c31cbdSDavid Lamparter
2588d7c31cbdSDavid Lamparter /* entries are added/deleted only under RTNL */
2589d7c31cbdSDavid Lamparter rcu_read_lock();
2590d7c31cbdSDavid Lamparter cache = ip6mr_cache_find(mrt, &src, &grp);
2591d7c31cbdSDavid Lamparter rcu_read_unlock();
2592d7c31cbdSDavid Lamparter if (!cache) {
2593d7c31cbdSDavid Lamparter NL_SET_ERR_MSG_MOD(extack, "MR cache entry not found");
2594d7c31cbdSDavid Lamparter return -ENOENT;
2595d7c31cbdSDavid Lamparter }
2596d7c31cbdSDavid Lamparter
2597d7c31cbdSDavid Lamparter skb = nlmsg_new(mr6_msgsize(false, mrt->maxvif), GFP_KERNEL);
2598d7c31cbdSDavid Lamparter if (!skb)
2599d7c31cbdSDavid Lamparter return -ENOBUFS;
2600d7c31cbdSDavid Lamparter
2601d7c31cbdSDavid Lamparter err = ip6mr_fill_mroute(mrt, skb, NETLINK_CB(in_skb).portid,
2602d7c31cbdSDavid Lamparter nlh->nlmsg_seq, cache, RTM_NEWROUTE, 0);
2603d7c31cbdSDavid Lamparter if (err < 0) {
2604d7c31cbdSDavid Lamparter kfree_skb(skb);
2605d7c31cbdSDavid Lamparter return err;
2606d7c31cbdSDavid Lamparter }
2607d7c31cbdSDavid Lamparter
2608d7c31cbdSDavid Lamparter return rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
2609d7c31cbdSDavid Lamparter }
2610d7c31cbdSDavid Lamparter
ip6mr_rtm_dumproute(struct sk_buff * skb,struct netlink_callback * cb)26115b285cacSPatrick McHardy static int ip6mr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb)
26125b285cacSPatrick McHardy {
2613e8ba330aSDavid Ahern const struct nlmsghdr *nlh = cb->nlh;
26144724676dSDavid Ahern struct fib_dump_filter filter = {};
26154724676dSDavid Ahern int err;
2616e8ba330aSDavid Ahern
2617cb167893SDavid Ahern if (cb->strict_check) {
26184724676dSDavid Ahern err = ip_valid_fib_dump_req(sock_net(skb->sk), nlh,
2619effe6792SDavid Ahern &filter, cb);
2620e8ba330aSDavid Ahern if (err < 0)
2621e8ba330aSDavid Ahern return err;
2622e8ba330aSDavid Ahern }
2623e8ba330aSDavid Ahern
2624cb167893SDavid Ahern if (filter.table_id) {
2625cb167893SDavid Ahern struct mr_table *mrt;
2626cb167893SDavid Ahern
2627bba7909bSPaolo Abeni mrt = __ip6mr_get_table(sock_net(skb->sk), filter.table_id);
2628cb167893SDavid Ahern if (!mrt) {
262941b4bd98SSabrina Dubroca if (rtnl_msg_family(cb->nlh) != RTNL_FAMILY_IP6MR)
2630ae677bbbSDavid Ahern return skb->len;
2631ae677bbbSDavid Ahern
2632cb167893SDavid Ahern NL_SET_ERR_MSG_MOD(cb->extack, "MR table does not exist");
2633cb167893SDavid Ahern return -ENOENT;
2634cb167893SDavid Ahern }
2635cb167893SDavid Ahern err = mr_table_dump(mrt, skb, cb, _ip6mr_fill_mroute,
2636cb167893SDavid Ahern &mfc_unres_lock, &filter);
2637cb167893SDavid Ahern return skb->len ? : err;
2638cb167893SDavid Ahern }
2639cb167893SDavid Ahern
26407b0db857SYuval Mintz return mr_rtm_dumproute(skb, cb, ip6mr_mr_table_iter,
2641cb167893SDavid Ahern _ip6mr_fill_mroute, &mfc_unres_lock, &filter);
26425b285cacSPatrick McHardy }
2643