cregit-Linux how code gets into the kernel

Release 4.11 net/ipv6/ip6mr.c

Directory: net/ipv6
/*
 *      Linux IPv6 multicast routing support for BSD pim6sd
 *      Based on net/ipv4/ipmr.c.
 *
 *      (c) 2004 Mickael Hoerdt, <hoerdt@clarinet.u-strasbg.fr>
 *              LSIIT Laboratory, Strasbourg, France
 *      (c) 2004 Jean-Philippe Andriot, <jean-philippe.andriot@6WIND.com>
 *              6WIND, Paris, France
 *      Copyright (C)2007,2008 USAGI/WIDE Project
 *              YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
 *
 *      This program is free software; you can redistribute it and/or
 *      modify it under the terms of the GNU General Public License
 *      as published by the Free Software Foundation; either version
 *      2 of the License, or (at your option) any later version.
 *
 */

#include <linux/uaccess.h>
#include <linux/types.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/timer.h>
#include <linux/mm.h>
#include <linux/kernel.h>
#include <linux/fcntl.h>
#include <linux/stat.h>
#include <linux/socket.h>
#include <linux/inet.h>
#include <linux/netdevice.h>
#include <linux/inetdevice.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/compat.h>
#include <net/protocol.h>
#include <linux/skbuff.h>
#include <net/sock.h>
#include <net/raw.h>
#include <linux/notifier.h>
#include <linux/if_arp.h>
#include <net/checksum.h>
#include <net/netlink.h>
#include <net/fib_rules.h>

#include <net/ipv6.h>
#include <net/ip6_route.h>
#include <linux/mroute6.h>
#include <linux/pim.h>
#include <net/addrconf.h>
#include <linux/netfilter_ipv6.h>
#include <linux/export.h>
#include <net/ip6_checksum.h>
#include <linux/netconf.h>


struct mr6_table {
	
struct list_head	list;
	
possible_net_t		net;
	
u32			id;
	
struct sock		*mroute6_sk;
	
struct timer_list	ipmr_expire_timer;
	
struct list_head	mfc6_unres_queue;
	
struct list_head	mfc6_cache_array[MFC6_LINES];
	
struct mif_device	vif6_table[MAXMIFS];
	
int			maxvif;
	
atomic_t		cache_resolve_queue_len;
	
bool			mroute_do_assert;
	
bool			mroute_do_pim;
#ifdef CONFIG_IPV6_PIMSM_V2
	
int			mroute_reg_vif_num;
#endif
};


struct ip6mr_rule {
	
struct fib_rule		common;
};


struct ip6mr_result {
	
struct mr6_table	*mrt;
};

/* Big lock, protecting vif table, mrt cache and mroute socket state.
   Note that the changes are semaphored via rtnl_lock.
 */

static DEFINE_RWLOCK(mrt_lock);

/*
 *      Multicast router control variables
 */


#define MIF_EXISTS(_mrt, _idx) ((_mrt)->vif6_table[_idx].dev != NULL)

/* Special spinlock for queue of unresolved entries */
static DEFINE_SPINLOCK(mfc_unres_lock);

/* We return to original Alan's scheme. Hash table of resolved
   entries is changed only in process context and protected
   with weak lock mrt_lock. Queue of unresolved entries is protected
   with strong spinlock mfc_unres_lock.

   In this case data path is free of exclusive locks at all.
 */


static struct kmem_cache *mrt_cachep __read_mostly;

static struct mr6_table *ip6mr_new_table(struct net *net, u32 id);
static void ip6mr_free_table(struct mr6_table *mrt);

static void ip6_mr_forward(struct net *net, struct mr6_table *mrt,
			   struct sk_buff *skb, struct mfc6_cache *cache);
static int ip6mr_cache_report(struct mr6_table *mrt, struct sk_buff *pkt,
			      mifi_t mifi, int assert);
static int __ip6mr_fill_mroute(struct mr6_table *mrt, struct sk_buff *skb,
			       struct mfc6_cache *c, struct rtmsg *rtm);
static void mr6_netlink_event(struct mr6_table *mrt, struct mfc6_cache *mfc,
			      int cmd);
static int ip6mr_rtm_dumproute(struct sk_buff *skb,
			       struct netlink_callback *cb);
static void mroute_clean_tables(struct mr6_table *mrt, bool all);
static void ipmr_expire_process(unsigned long arg);

#ifdef CONFIG_IPV6_MROUTE_MULTIPLE_TABLES

#define ip6mr_for_each_table(mrt, net) \
	list_for_each_entry_rcu(mrt, &net->ipv6.mr6_tables, list)


static struct mr6_table *ip6mr_get_table(struct net *net, u32 id) { struct mr6_table *mrt; ip6mr_for_each_table(mrt, net) { if (mrt->id == id) return mrt; } return NULL; }

Contributors

PersonTokensPropCommitsCommitProp
Patrick McHardy43100.00%1100.00%
Total43100.00%1100.00%


static int ip6mr_fib_lookup(struct net *net, struct flowi6 *flp6, struct mr6_table **mrt) { int err; struct ip6mr_result res; struct fib_lookup_arg arg = { .result = &res, .flags = FIB_LOOKUP_NOREF, }; err = fib_rules_lookup(net->ipv6.mr6_rules_ops, flowi6_to_flowi(flp6), 0, &arg); if (err < 0) return err; *mrt = res.mrt; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Patrick McHardy7182.56%133.33%
Hannes Frederic Sowa910.47%133.33%
David S. Miller66.98%133.33%
Total86100.00%3100.00%


static int ip6mr_rule_action(struct fib_rule *rule, struct flowi *flp, int flags, struct fib_lookup_arg *arg) { struct ip6mr_result *res = arg->result; struct mr6_table *mrt; switch (rule->action) { case FR_ACT_TO_TBL: break; case FR_ACT_UNREACHABLE: return -ENETUNREACH; case FR_ACT_PROHIBIT: return -EACCES; case FR_ACT_BLACKHOLE: default: return -EINVAL; } mrt = ip6mr_get_table(rule->fr_net, rule->table); if (!mrt) return -EAGAIN; res->mrt = mrt; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Patrick McHardy10299.03%150.00%
Ian Morris10.97%150.00%
Total103100.00%2100.00%


static int ip6mr_rule_match(struct fib_rule *rule, struct flowi *flp, int flags) { return 1; }

Contributors

PersonTokensPropCommitsCommitProp
Patrick McHardy22100.00%1100.00%
Total22100.00%1100.00%

static const struct nla_policy ip6mr_rule_policy[FRA_MAX + 1] = { FRA_GENERIC_POLICY, };
static int ip6mr_rule_configure(struct fib_rule *rule, struct sk_buff *skb, struct fib_rule_hdr *frh, struct nlattr **tb) { return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Patrick McHardy30100.00%1100.00%
Total30100.00%1100.00%


static int ip6mr_rule_compare(struct fib_rule *rule, struct fib_rule_hdr *frh, struct nlattr **tb) { return 1; }

Contributors

PersonTokensPropCommitsCommitProp
Patrick McHardy25100.00%1100.00%
Total25100.00%1100.00%


static int ip6mr_rule_fill(struct fib_rule *rule, struct sk_buff *skb, struct fib_rule_hdr *frh) { frh->dst_len = 0; frh->src_len = 0; frh->tos = 0; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Patrick McHardy42100.00%1100.00%
Total42100.00%1100.00%

static const struct fib_rules_ops __net_initconst ip6mr_rules_ops_template = { .family = RTNL_FAMILY_IP6MR, .rule_size = sizeof(struct ip6mr_rule), .addr_size = sizeof(struct in6_addr), .action = ip6mr_rule_action, .match = ip6mr_rule_match, .configure = ip6mr_rule_configure, .compare = ip6mr_rule_compare, .fill = ip6mr_rule_fill, .nlgroup = RTNLGRP_IPV6_RULE, .policy = ip6mr_rule_policy, .owner = THIS_MODULE, };
static int __net_init ip6mr_rules_init(struct net *net) { struct fib_rules_ops *ops; struct mr6_table *mrt; int err; ops = fib_rules_register(&ip6mr_rules_ops_template, net); if (IS_ERR(ops)) return PTR_ERR(ops); INIT_LIST_HEAD(&net->ipv6.mr6_tables); mrt = ip6mr_new_table(net, RT6_TABLE_DFLT); if (!mrt) { err = -ENOMEM; goto err1; } err = fib_default_rule_add(ops, 0x7fff, RT6_TABLE_DFLT, 0); if (err < 0) goto err2; net->ipv6.mr6_rules_ops = ops; return 0; err2: ip6mr_free_table(mrt); err1: fib_rules_unregister(ops); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Patrick McHardy13098.48%133.33%
Ian Morris10.76%133.33%
Américo Wang10.76%133.33%
Total132100.00%3100.00%


static void __net_exit ip6mr_rules_exit(struct net *net) { struct mr6_table *mrt, *next; rtnl_lock(); list_for_each_entry_safe(mrt, next, &net->ipv6.mr6_tables, list) { list_del(&mrt->list); ip6mr_free_table(mrt); } fib_rules_unregister(net->ipv6.mr6_rules_ops); rtnl_unlock(); }

Contributors

PersonTokensPropCommitsCommitProp
Patrick McHardy4473.33%125.00%
Eric Dumazet1016.67%125.00%
Américo Wang35.00%125.00%
Hannes Frederic Sowa35.00%125.00%
Total60100.00%4100.00%

#else #define ip6mr_for_each_table(mrt, net) \ for (mrt = net->ipv6.mrt6; mrt; mrt = NULL)
static struct mr6_table *ip6mr_get_table(struct net *net, u32 id) { return net->ipv6.mrt6; }

Contributors

PersonTokensPropCommitsCommitProp
Patrick McHardy23100.00%1100.00%
Total23100.00%1100.00%


static int ip6mr_fib_lookup(struct net *net, struct flowi6 *flp6, struct mr6_table **mrt) { *mrt = net->ipv6.mrt6; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Patrick McHardy3294.12%150.00%
David S. Miller25.88%150.00%
Total34100.00%2100.00%


static int __net_init ip6mr_rules_init(struct net *net) { net->ipv6.mrt6 = ip6mr_new_table(net, RT6_TABLE_DFLT); return net->ipv6.mrt6 ? 0 : -ENOMEM; }

Contributors

PersonTokensPropCommitsCommitProp
Patrick McHardy37100.00%1100.00%
Total37100.00%1100.00%


static void __net_exit ip6mr_rules_exit(struct net *net) { rtnl_lock(); ip6mr_free_table(net->ipv6.mrt6); net->ipv6.mrt6 = NULL; rtnl_unlock(); }

Contributors

PersonTokensPropCommitsCommitProp
Patrick McHardy2160.00%150.00%
Hannes Frederic Sowa1440.00%150.00%
Total35100.00%2100.00%

#endif
static struct mr6_table *ip6mr_new_table(struct net *net, u32 id) { struct mr6_table *mrt; unsigned int i; mrt = ip6mr_get_table(net, id); if (mrt) return mrt; mrt = kzalloc(sizeof(*mrt), GFP_KERNEL); if (!mrt) return NULL; mrt->id = id; write_pnet(&mrt->net, net); /* Forwarding cache */ for (i = 0; i < MFC6_LINES; i++) INIT_LIST_HEAD(&mrt->mfc6_cache_array[i]); INIT_LIST_HEAD(&mrt->mfc6_unres_queue); setup_timer(&mrt->ipmr_expire_timer, ipmr_expire_process, (unsigned long)mrt); #ifdef CONFIG_IPV6_PIMSM_V2 mrt->mroute_reg_vif_num = -1; #endif #ifdef CONFIG_IPV6_MROUTE_MULTIPLE_TABLES list_add_tail_rcu(&mrt->list, &net->ipv6.mr6_tables); #endif return mrt; }

Contributors

PersonTokensPropCommitsCommitProp
Patrick McHardy16199.38%150.00%
Ian Morris10.62%150.00%
Total162100.00%2100.00%


static void ip6mr_free_table(struct mr6_table *mrt) { del_timer_sync(&mrt->ipmr_expire_timer); mroute_clean_tables(mrt, true); kfree(mrt); }

Contributors

PersonTokensPropCommitsCommitProp
Patrick McHardy2890.32%250.00%
Nikolay Aleksandrov26.45%125.00%
Américo Wang13.23%125.00%
Total31100.00%4100.00%

#ifdef CONFIG_PROC_FS struct ipmr_mfc_iter { struct seq_net_private p; struct mr6_table *mrt; struct list_head *cache; int ct; };
static struct mfc6_cache *ipmr_mfc_seq_idx(struct net *net, struct ipmr_mfc_iter *it, loff_t pos) { struct mr6_table *mrt = it->mrt; struct mfc6_cache *mfc; read_lock(&mrt_lock); for (it->ct = 0; it->ct < MFC6_LINES; it->ct++) { it->cache = &mrt->mfc6_cache_array[it->ct]; list_for_each_entry(mfc, it->cache, list) if (pos-- == 0) return mfc; } read_unlock(&mrt_lock); spin_lock_bh(&mfc_unres_lock); it->cache = &mrt->mfc6_unres_queue; list_for_each_entry(mfc, it->cache, list) if (pos-- == 0) return mfc; spin_unlock_bh(&mfc_unres_lock); it->cache = NULL; return NULL; }

Contributors

PersonTokensPropCommitsCommitProp
Hideaki Yoshifuji / 吉藤英明10168.24%114.29%
Patrick McHardy4027.03%457.14%
Benjamin Thery74.73%228.57%
Total148100.00%7100.00%

/* * The /proc interfaces to multicast routing /proc/ip6_mr_cache /proc/ip6_mr_vif */ struct ipmr_vif_iter { struct seq_net_private p; struct mr6_table *mrt; int ct; };
static struct mif_device *ip6mr_vif_seq_idx(struct net *net, struct ipmr_vif_iter *iter, loff_t pos) { struct mr6_table *mrt = iter->mrt; for (iter->ct = 0; iter->ct < mrt->maxvif; ++iter->ct) { if (!MIF_EXISTS(mrt, iter->ct)) continue; if (pos-- == 0) return &mrt->vif6_table[iter->ct]; } return NULL; }

Contributors

PersonTokensPropCommitsCommitProp
Hideaki Yoshifuji / 吉藤英明6777.01%120.00%
Patrick McHardy1213.79%240.00%
Benjamin Thery89.20%240.00%
Total87100.00%5100.00%


static void *ip6mr_vif_seq_start(struct seq_file *seq, loff_t *pos) __acquires (mrt_lock) { struct ipmr_vif_iter *iter = seq->private; struct net *net = seq_file_net(seq); struct mr6_table *mrt; mrt = ip6mr_get_table(net, RT6_TABLE_DFLT); if (!mrt) return ERR_PTR(-ENOENT); iter->mrt = mrt; read_lock(&mrt_lock); return *pos ? ip6mr_vif_seq_idx(net, seq->private, *pos - 1) : SEQ_START_TOKEN; }

Contributors

PersonTokensPropCommitsCommitProp
Hideaki Yoshifuji / 吉藤英明4445.36%125.00%
Patrick McHardy4041.24%125.00%
Benjamin Thery1212.37%125.00%
Ian Morris11.03%125.00%
Total97100.00%4100.00%


static void *ip6mr_vif_seq_next(struct seq_file *seq, void *v, loff_t *pos) { struct ipmr_vif_iter *iter = seq->private; struct net *net = seq_file_net(seq); struct mr6_table *mrt = iter->mrt; ++*pos; if (v == SEQ_START_TOKEN) return ip6mr_vif_seq_idx(net, iter, 0); while (++iter->ct < mrt->maxvif) { if (!MIF_EXISTS(mrt, iter->ct)) continue; return &mrt->vif6_table[iter->ct]; } return NULL; }

Contributors

PersonTokensPropCommitsCommitProp
Hideaki Yoshifuji / 吉藤英明8175.00%120.00%
Benjamin Thery1513.89%240.00%
Patrick McHardy1211.11%240.00%
Total108100.00%5100.00%


static void ip6mr_vif_seq_stop(struct seq_file *seq, void *v) __releases (mrt_lock) { read_unlock(&mrt_lock); }

Contributors

PersonTokensPropCommitsCommitProp
Hideaki Yoshifuji / 吉藤英明25100.00%1100.00%
Total25100.00%1100.00%


static int ip6mr_vif_seq_show(struct seq_file *seq, void *v) { struct ipmr_vif_iter *iter = seq->private; struct mr6_table *mrt = iter->mrt; if (v == SEQ_START_TOKEN) { seq_puts(seq, "Interface BytesIn PktsIn BytesOut PktsOut Flags\n"); } else { const struct mif_device *vif = v; const char *name = vif->dev ? vif->dev->name : "none"; seq_printf(seq, "%2td %-10s %8ld %7ld %8ld %7ld %05X\n", vif - mrt->vif6_table, name, vif->bytes_in, vif->pkt_in, vif->bytes_out, vif->pkt_out, vif->flags); } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Hideaki Yoshifuji / 吉藤英明9381.58%120.00%
Patrick McHardy1412.28%240.00%
Benjamin Thery65.26%120.00%
Al Viro10.88%120.00%
Total114100.00%5100.00%

static const struct seq_operations ip6mr_vif_seq_ops = { .start = ip6mr_vif_seq_start, .next = ip6mr_vif_seq_next, .stop = ip6mr_vif_seq_stop, .show = ip6mr_vif_seq_show, };
static int ip6mr_vif_open(struct inode *inode, struct file *file) { return seq_open_net(inode, file, &ip6mr_vif_seq_ops, sizeof(struct ipmr_vif_iter)); }

Contributors

PersonTokensPropCommitsCommitProp
Hideaki Yoshifuji / 吉藤英明3090.91%150.00%
Benjamin Thery39.09%150.00%
Total33100.00%2100.00%

static const struct file_operations ip6mr_vif_fops = { .owner = THIS_MODULE, .open = ip6mr_vif_open, .read = seq_read, .llseek = seq_lseek, .release = seq_release_net, };
static void *ipmr_mfc_seq_start(struct seq_file *seq, loff_t *pos) { struct ipmr_mfc_iter *it = seq->private; struct net *net = seq_file_net(seq); struct mr6_table *mrt; mrt = ip6mr_get_table(net, RT6_TABLE_DFLT); if (!mrt) return ERR_PTR(-ENOENT); it->mrt = mrt; return *pos ? ipmr_mfc_seq_idx(net, seq->private, *pos - 1) : SEQ_START_TOKEN; }

Contributors

PersonTokensPropCommitsCommitProp
Patrick McHardy4045.98%125.00%
Hideaki Yoshifuji / 吉藤英明3439.08%125.00%
Benjamin Thery1213.79%125.00%
Ian Morris11.15%125.00%
Total87100.00%4100.00%


static void *ipmr_mfc_seq_next(struct seq_file *seq, void *v, loff_t *pos) { struct mfc6_cache *mfc = v; struct ipmr_mfc_iter *it = seq->private; struct net *net = seq_file_net(seq); struct mr6_table *mrt = it->mrt; ++*pos; if (v == SEQ_START_TOKEN) return ipmr_mfc_seq_idx(net, seq->private, 0); if (mfc->list.next != it->cache) return list_entry(mfc->list.next, struct mfc6_cache, list); if (it->cache == &mrt->mfc6_unres_queue) goto end_of_list; BUG_ON(it->cache != &mrt->mfc6_cache_array[it->ct]); while (++it->ct < MFC6_LINES) { it->cache = &mrt->mfc6_cache_array[it->ct]; if (list_empty(it->cache)) continue; return list_first_entry(it->cache, struct mfc6_cache, list); } /* exhausted cache_array, show unresolved */ read_unlock(&mrt_lock); it->cache = &mrt->mfc6_unres_queue; it->ct = 0; spin_lock_bh(&mfc_unres_lock); if (!list_empty(it->cache)) return list_first_entry(it->cache, struct mfc6_cache, list); end_of_list: spin_unlock_bh(&mfc_unres_lock); it->cache = NULL; return NULL; }

Contributors

PersonTokensPropCommitsCommitProp
Hideaki Yoshifuji / 吉藤英明15762.55%114.29%
Patrick McHardy7931.47%457.14%
Benjamin Thery155.98%228.57%
Total251100.00%7100.00%


static void ipmr_mfc_seq_stop(struct seq_file *seq, void *v) { struct ipmr_mfc_iter *it = seq->private; struct mr6_table *mrt = it->mrt; if (it->cache == &mrt->mfc6_unres_queue) spin_unlock_bh(&mfc_unres_lock); else if (it->cache == &mrt->mfc6_cache_array[it->ct]) read_unlock(&mrt_lock); }

Contributors

PersonTokensPropCommitsCommitProp
Hideaki Yoshifuji / 吉藤英明5372.60%116.67%
Patrick McHardy1216.44%350.00%
Richard Laing68.22%116.67%
Benjamin Thery22.74%116.67%
Total73100.00%6100.00%


static int ipmr_mfc_seq_show(struct seq_file *seq, void *v) { int n; if (v == SEQ_START_TOKEN) { seq_puts(seq, "Group " "Origin " "Iif Pkts Bytes Wrong Oifs\n"); } else { const struct mfc6_cache *mfc = v; const struct ipmr_mfc_iter *it = seq->private; struct mr6_table *mrt = it->mrt; seq_printf(seq, "%pI6 %pI6 %-3hd", &mfc->mf6c_mcastgrp, &mfc->mf6c_origin, mfc->mf6c_parent); if (it->cache != &mrt->mfc6_unres_queue) { seq_printf(seq, " %8lu %8lu %8lu", mfc->mfc_un.res.pkt, mfc->mfc_un.res.bytes, mfc->mfc_un.res.wrong_if); for (n = mfc->mfc_un.res.minvif; n < mfc->mfc_un.res.maxvif; n++) { if (MIF_EXISTS(mrt, n) && mfc->mfc_un.res.ttls[n] < 255) seq_printf(seq, " %2d:%-3d", n, mfc->mfc_un.res.ttls[n]); } } else { /* unresolved mfc_caches don't contain * pkt, bytes and wrong_if values */ seq_printf(seq, " %8lu %8lu %8lu", 0ul, 0ul, 0ul); } seq_putc(seq, '\n'); } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Hideaki Yoshifuji / 吉藤英明17677.88%112.50%
Benjamin Thery3515.49%337.50%
Patrick McHardy135.75%337.50%
Harvey Harrison20.88%112.50%
Total226100.00%8100.00%

static const struct seq_operations ipmr_mfc_seq_ops = { .start = ipmr_mfc_seq_start, .next = ipmr_mfc_seq_next, .stop = ipmr_mfc_seq_stop, .show = ipmr_mfc_seq_show, };
static int ipmr_mfc_open(struct inode *inode, struct file *file) { return seq_open_net(inode, file, &ipmr_mfc_seq_ops, sizeof(struct ipmr_mfc_iter)); }

Contributors

PersonTokensPropCommitsCommitProp
Hideaki Yoshifuji / 吉藤英明3090.91%150.00%
Benjamin Thery39.09%150.00%
Total33100.00%2100.00%

static const struct file_operations ip6mr_mfc_fops = { .owner = THIS_MODULE, .open = ipmr_mfc_open, .read = seq_read, .llseek = seq_lseek, .release = seq_release_net, }; #endif #ifdef CONFIG_IPV6_PIMSM_V2
static int pim6_rcv(struct sk_buff *skb) { struct pimreghdr *pim; struct ipv6hdr *encap; struct net_device *reg_dev = NULL; struct net *net = dev_net(skb->dev); struct mr6_table *mrt; struct flowi6 fl6 = { .flowi6_iif = skb->dev->ifindex, .flowi6_mark = skb->mark, }; int reg_vif_num; if (!pskb_may_pull(skb, sizeof(*pim) + sizeof(*encap))) goto drop; pim = (struct pimreghdr *)skb_transport_header(skb); if (pim->type != ((PIM_VERSION << 4) | PIM_TYPE_REGISTER)