Release 4.11 net/ipv6/ip6_fib.c
/*
* Linux INET6 implementation
* Forwarding Information Database
*
* Authors:
* Pedro Roque <roque@di.fc.ul.pt>
*
* 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.
*
* Changes:
* Yuji SEKIYA @USAGI: Support default route on router node;
* remove ip6_null_entry from the top of
* routing table.
* Ville Nuorvala: Fixed routing subtrees.
*/
#define pr_fmt(fmt) "IPv6: " fmt
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/net.h>
#include <linux/route.h>
#include <linux/netdevice.h>
#include <linux/in6.h>
#include <linux/init.h>
#include <linux/list.h>
#include <linux/slab.h>
#include <net/ipv6.h>
#include <net/ndisc.h>
#include <net/addrconf.h>
#include <net/lwtunnel.h>
#include <net/ip6_fib.h>
#include <net/ip6_route.h>
#define RT6_DEBUG 2
#if RT6_DEBUG >= 3
#define RT6_TRACE(x...) pr_debug(x)
#else
#define RT6_TRACE(x...) do { ; } while (0)
#endif
static struct kmem_cache *fib6_node_kmem __read_mostly;
struct fib6_cleaner {
struct fib6_walker w;
struct net *net;
int (*func)(struct rt6_info *, void *arg);
int sernum;
void *arg;
};
#ifdef CONFIG_IPV6_SUBTREES
#define FWS_INIT FWS_S
#else
#define FWS_INIT FWS_L
#endif
static void fib6_prune_clones(struct net *net, struct fib6_node *fn);
static struct rt6_info *fib6_find_prefix(struct net *net, struct fib6_node *fn);
static struct fib6_node *fib6_repair_tree(struct net *net, struct fib6_node *fn);
static int fib6_walk(struct net *net, struct fib6_walker *w);
static int fib6_walk_continue(struct fib6_walker *w);
/*
* A routing update causes an increase of the serial number on the
* affected subtree. This allows for cached routes to be asynchronously
* tested when modifications are made to the destination cache as a
* result of redirects, path MTU changes, etc.
*/
static void fib6_gc_timer_cb(unsigned long arg);
#define FOR_WALKERS(net, w) \
list_for_each_entry(w, &(net)->ipv6.fib6_walkers, lh)
static void fib6_walker_link(struct net *net, struct fib6_walker *w)
{
write_lock_bh(&net->ipv6.fib6_walker_lock);
list_add(&w->lh, &net->ipv6.fib6_walkers);
write_unlock_bh(&net->ipv6.fib6_walker_lock);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Adrian Bunk | 26 | 50.98% | 1 | 25.00% |
Michal Kubeček | 17 | 33.33% | 1 | 25.00% |
Alexey Dobriyan | 7 | 13.73% | 1 | 25.00% |
Hannes Frederic Sowa | 1 | 1.96% | 1 | 25.00% |
Total | 51 | 100.00% | 4 | 100.00% |
static void fib6_walker_unlink(struct net *net, struct fib6_walker *w)
{
write_lock_bh(&net->ipv6.fib6_walker_lock);
list_del(&w->lh);
write_unlock_bh(&net->ipv6.fib6_walker_lock);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Adrian Bunk | 25 | 56.82% | 1 | 25.00% |
Michal Kubeček | 13 | 29.55% | 1 | 25.00% |
Alexey Dobriyan | 5 | 11.36% | 1 | 25.00% |
Hannes Frederic Sowa | 1 | 2.27% | 1 | 25.00% |
Total | 44 | 100.00% | 4 | 100.00% |
static int fib6_new_sernum(struct net *net)
{
int new, old;
do {
old = atomic_read(&net->ipv6.fib6_sernum);
new = old < INT_MAX ? old + 1 : 1;
} while (atomic_cmpxchg(&net->ipv6.fib6_sernum,
old, new) != old);
return new;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Hannes Frederic Sowa | 52 | 80.00% | 2 | 50.00% |
Linus Torvalds (pre-git) | 13 | 20.00% | 2 | 50.00% |
Total | 65 | 100.00% | 4 | 100.00% |
enum {
FIB6_NO_SERNUM_CHANGE = 0,
};
/*
* Auxiliary address test functions for the radix tree.
*
* These assume a 32bit processor (although it will work on
* 64bit processors)
*/
/*
* test bit
*/
#if defined(__LITTLE_ENDIAN)
# define BITOP_BE32_SWIZZLE (0x1F & ~7)
#else
# define BITOP_BE32_SWIZZLE 0
#endif
static __be32 addr_bit_set(const void *token, int fn_bit)
{
const __be32 *addr = token;
/*
* Here,
* 1 << ((~fn_bit ^ BITOP_BE32_SWIZZLE) & 0x1f)
* is optimized version of
* htonl(1 << ((~fn_bit)&0x1F))
* See include/asm-generic/bitops/le.h.
*/
return (__force __be32)(1 << ((~fn_bit ^ BITOP_BE32_SWIZZLE) & 0x1f)) &
addr[fn_bit >> 5];
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Linus Torvalds (pre-git) | 34 | 69.39% | 2 | 28.57% |
Eric Dumazet | 8 | 16.33% | 2 | 28.57% |
Hideaki Yoshifuji / 吉藤英明 | 4 | 8.16% | 1 | 14.29% |
Al Viro | 2 | 4.08% | 1 | 14.29% |
Wang Yufen | 1 | 2.04% | 1 | 14.29% |
Total | 49 | 100.00% | 7 | 100.00% |
static struct fib6_node *node_alloc(void)
{
struct fib6_node *fn;
fn = kmem_cache_zalloc(fib6_node_kmem, GFP_ATOMIC);
return fn;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Linus Torvalds (pre-git) | 14 | 51.85% | 2 | 40.00% |
Hannes Frederic Sowa | 10 | 37.04% | 1 | 20.00% |
Robert P. J. Day | 2 | 7.41% | 1 | 20.00% |
Christoph Lameter | 1 | 3.70% | 1 | 20.00% |
Total | 27 | 100.00% | 5 | 100.00% |
static void node_free(struct fib6_node *fn)
{
kmem_cache_free(fib6_node_kmem, fn);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Linus Torvalds (pre-git) | 17 | 94.44% | 2 | 66.67% |
Robert P. J. Day | 1 | 5.56% | 1 | 33.33% |
Total | 18 | 100.00% | 3 | 100.00% |
static void rt6_rcu_free(struct rt6_info *rt)
{
call_rcu(&rt->dst.rcu_head, dst_rcu_free);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Martin KaFai Lau | 23 | 100.00% | 1 | 100.00% |
Total | 23 | 100.00% | 1 | 100.00% |
static void rt6_free_pcpu(struct rt6_info *non_pcpu_rt)
{
int cpu;
if (!non_pcpu_rt->rt6i_pcpu)
return;
for_each_possible_cpu(cpu) {
struct rt6_info **ppcpu_rt;
struct rt6_info *pcpu_rt;
ppcpu_rt = per_cpu_ptr(non_pcpu_rt->rt6i_pcpu, cpu);
pcpu_rt = *ppcpu_rt;
if (pcpu_rt) {
rt6_rcu_free(pcpu_rt);
*ppcpu_rt = NULL;
}
}
free_percpu(non_pcpu_rt->rt6i_pcpu);
non_pcpu_rt->rt6i_pcpu = NULL;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Martin KaFai Lau | 84 | 100.00% | 4 | 100.00% |
Total | 84 | 100.00% | 4 | 100.00% |
static void rt6_release(struct rt6_info *rt)
{
if (atomic_dec_and_test(&rt->rt6i_ref)) {
rt6_free_pcpu(rt);
rt6_rcu_free(rt);
}
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Linus Torvalds (pre-git) | 25 | 75.76% | 2 | 50.00% |
Martin KaFai Lau | 8 | 24.24% | 2 | 50.00% |
Total | 33 | 100.00% | 4 | 100.00% |
static void fib6_link_table(struct net *net, struct fib6_table *tb)
{
unsigned int h;
/*
* Initialize table lock at a single place to give lockdep a key,
* tables aren't visible prior to being linked to the list.
*/
rwlock_init(&tb->tb6_lock);
h = tb->tb6_id & (FIB6_TABLE_HASHSZ - 1);
/*
* No protection necessary, this is the only list mutatation
* operation, tables never disappear once they exist.
*/
hlist_add_head_rcu(&tb->tb6_hlist, &net->ipv6.fib_table_hash[h]);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Patrick McHardy | 41 | 68.33% | 1 | 25.00% |
Thomas Graf | 9 | 15.00% | 1 | 25.00% |
Daniel Lezcano | 9 | 15.00% | 1 | 25.00% |
Neil Horman | 1 | 1.67% | 1 | 25.00% |
Total | 60 | 100.00% | 4 | 100.00% |
#ifdef CONFIG_IPV6_MULTIPLE_TABLES
static struct fib6_table *fib6_alloc_table(struct net *net, u32 id)
{
struct fib6_table *table;
table = kzalloc(sizeof(*table), GFP_ATOMIC);
if (table) {
table->tb6_id = id;
table->tb6_root.leaf = net->ipv6.ip6_null_entry;
table->tb6_root.fn_flags = RTN_ROOT | RTN_TL_ROOT | RTN_RTINFO;
inet_peer_base_init(&table->tb6_peers);
}
return table;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Thomas Graf | 64 | 79.01% | 1 | 33.33% |
Daniel Lezcano | 9 | 11.11% | 1 | 33.33% |
David S. Miller | 8 | 9.88% | 1 | 33.33% |
Total | 81 | 100.00% | 3 | 100.00% |
struct fib6_table *fib6_new_table(struct net *net, u32 id)
{
struct fib6_table *tb;
if (id == 0)
id = RT6_TABLE_MAIN;
tb = fib6_get_table(net, id);
if (tb)
return tb;
tb = fib6_alloc_table(net, id);
if (tb)
fib6_link_table(net, tb);
return tb;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Thomas Graf | 58 | 84.06% | 1 | 33.33% |
Daniel Lezcano | 11 | 15.94% | 2 | 66.67% |
Total | 69 | 100.00% | 3 | 100.00% |
EXPORT_SYMBOL_GPL(fib6_new_table);
struct fib6_table *fib6_get_table(struct net *net, u32 id)
{
struct fib6_table *tb;
struct hlist_head *head;
unsigned int h;
if (id == 0)
id = RT6_TABLE_MAIN;
h = id & (FIB6_TABLE_HASHSZ - 1);
rcu_read_lock();
head = &net->ipv6.fib_table_hash[h];
hlist_for_each_entry_rcu(tb, head, tb6_hlist) {
if (tb->tb6_id == id) {
rcu_read_unlock();
return tb;
}
}
rcu_read_unlock();
return NULL;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Patrick McHardy | 46 | 47.92% | 1 | 25.00% |
Thomas Graf | 27 | 28.12% | 1 | 25.00% |
Daniel Lezcano | 22 | 22.92% | 1 | 25.00% |
Neil Horman | 1 | 1.04% | 1 | 25.00% |
Total | 96 | 100.00% | 4 | 100.00% |
EXPORT_SYMBOL_GPL(fib6_get_table);
static void __net_init fib6_tables_init(struct net *net)
{
fib6_link_table(net, net->ipv6.fib6_main_tbl);
fib6_link_table(net, net->ipv6.fib6_local_tbl);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Patrick McHardy | 17 | 50.00% | 1 | 33.33% |
Daniel Lezcano | 16 | 47.06% | 1 | 33.33% |
Alexey Dobriyan | 1 | 2.94% | 1 | 33.33% |
Total | 34 | 100.00% | 3 | 100.00% |
#else
struct fib6_table *fib6_new_table(struct net *net, u32 id)
{
return fib6_get_table(net, id);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Patrick McHardy | 16 | 69.57% | 1 | 50.00% |
Daniel Lezcano | 7 | 30.43% | 1 | 50.00% |
Total | 23 | 100.00% | 2 | 100.00% |
struct fib6_table *fib6_get_table(struct net *net, u32 id)
{
return net->ipv6.fib6_main_tbl;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Patrick McHardy | 13 | 59.09% | 1 | 50.00% |
Daniel Lezcano | 9 | 40.91% | 1 | 50.00% |
Total | 22 | 100.00% | 2 | 100.00% |
struct dst_entry *fib6_rule_lookup(struct net *net, struct flowi6 *fl6,
int flags, pol_lookup_t lookup)
{
struct rt6_info *rt;
rt = lookup(net, net->ipv6.fib6_main_tbl, fl6, flags);
if (rt->rt6i_flags & RTF_REJECT &&
rt->dst.error == -EAGAIN) {
ip6_rt_put(rt);
rt = net->ipv6.ip6_null_entry;
dst_hold(&rt->dst);
}
return &rt->dst;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Xin Long | 53 | 58.24% | 1 | 20.00% |
Patrick McHardy | 24 | 26.37% | 1 | 20.00% |
Daniel Lezcano | 11 | 12.09% | 2 | 40.00% |
David S. Miller | 3 | 3.30% | 1 | 20.00% |
Total | 91 | 100.00% | 5 | 100.00% |
static void __net_init fib6_tables_init(struct net *net)
{
fib6_link_table(net, net->ipv6.fib6_main_tbl);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Patrick McHardy | 12 | 52.17% | 1 | 33.33% |
Daniel Lezcano | 10 | 43.48% | 1 | 33.33% |
Alexey Dobriyan | 1 | 4.35% | 1 | 33.33% |
Total | 23 | 100.00% | 3 | 100.00% |
#endif
static int fib6_dump_node(struct fib6_walker *w)
{
int res;
struct rt6_info *rt;
for (rt = w->leaf; rt; rt = rt->dst.rt6_next) {
res = rt6_dump_route(rt, w->args);
if (res < 0) {
/* Frame is full, suspend walking */
w->leaf = rt;
return 1;
}
/* Multipath routes are dumped in one route with the
* RTA_MULTIPATH attribute. Jump 'rt' to point to the
* last sibling of this route (no need to dump the
* sibling routes again)
*/
if (rt->rt6i_nsiblings)
rt = list_last_entry(&rt->rt6i_siblings,
struct rt6_info,
rt6i_siblings);
}
w->leaf = NULL;
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Patrick McHardy | 73 | 73.74% | 1 | 25.00% |
David Ahern | 22 | 22.22% | 1 | 25.00% |
Eric Dumazet | 3 | 3.03% | 1 | 25.00% |
Hannes Frederic Sowa | 1 | 1.01% | 1 | 25.00% |
Total | 99 | 100.00% | 4 | 100.00% |
static void fib6_dump_end(struct netlink_callback *cb)
{
struct net *net = sock_net(cb->skb->sk);
struct fib6_walker *w = (void *)cb->args[2];
if (w) {
if (cb->args[4]) {
cb->args[4] = 0;
fib6_walker_unlink(net, w);
}
cb->args[2] = 0;
kfree(w);
}
cb->done = (void *)cb->args[3];
cb->args[1] = 3;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Patrick McHardy | 70 | 62.50% | 1 | 25.00% |
Herbert Xu | 25 | 22.32% | 1 | 25.00% |
Michal Kubeček | 16 | 14.29% | 1 | 25.00% |
Hannes Frederic Sowa | 1 | 0.89% | 1 | 25.00% |
Total | 112 | 100.00% | 4 | 100.00% |
static int fib6_dump_done(struct netlink_callback *cb)
{
fib6_dump_end(cb);
return cb->done ? cb->done(cb) : 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Patrick McHardy | 30 | 100.00% | 1 | 100.00% |
Total | 30 | 100.00% | 1 | 100.00% |
static int fib6_dump_table(struct fib6_table *table, struct sk_buff *skb,
struct netlink_callback *cb)
{
struct net *net = sock_net(skb->sk);
struct fib6_walker *w;
int res;
w = (void *)cb->args[2];
w->root = &table->tb6_root;
if (cb->args[4] == 0) {
w->count = 0;
w->skip = 0;
read_lock_bh(&table->tb6_lock);
res = fib6_walk(net, w);
read_unlock_bh(&table->tb6_lock);
if (res > 0) {
cb->args[4] = 1;
cb->args[5] = w->root->fn_sernum;
}
} else {
if (cb->args[5] != w->root->fn_sernum) {
/* Begin at the root if the tree changed */
cb->args[5] = w->root->fn_sernum;
w->state = FWS_INIT;
w->node = w->root;
w->skip = w->count;
} else
w->skip = 0;
read_lock_bh(&table->tb6_lock);
res = fib6_walk_continue(w);
read_unlock_bh(&table->tb6_lock);
if (res <= 0) {
fib6_walker_unlink(net, w);
cb->args[4] = 0;
}
}
return res;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Patrick McHardy | 237 | 92.58% | 2 | 40.00% |
Michal Kubeček | 16 | 6.25% | 1 | 20.00% |
Herbert Xu | 2 | 0.78% | 1 | 20.00% |
Hannes Frederic Sowa | 1 | 0.39% | 1 | 20.00% |
Total | 256 | 100.00% | 5 | 100.00% |
static int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb)
{
struct net *net = sock_net(skb->sk);
unsigned int h, s_h;
unsigned int e = 0, s_e;
struct rt6_rtnl_dump_arg arg;
struct fib6_walker *w;
struct fib6_table *tb;
struct hlist_head *head;
int res = 0;
s_h = cb->args[0];
s_e = cb->args[1];
w = (void *)cb->args[2];
if (!w) {
/* New dump:
*
* 1. hook callback destructor.
*/
cb->args[3] = (long)cb->done;
cb->done = fib6_dump_done;
/*
* 2. allocate and initialize walker.
*/
w = kzalloc(sizeof(*w), GFP_ATOMIC);
if (!w)
return -ENOMEM;
w->func = fib6_dump_node;
cb->args[2] = (long)w;
}
arg.skb = skb;
arg.cb = cb;
arg.net = net;
w->args = &arg;
rcu_read_lock();
for (h = s_h; h < FIB6_TABLE_HASHSZ; h++, s_e = 0) {
e = 0;
head = &net->ipv6.fib_table_hash[h];
hlist_for_each_entry_rcu(tb, head, tb6_hlist) {
if (e < s_e)
goto next;
res = fib6_dump_table(tb, skb, cb);
if (res != 0)
goto out;
next:
e++;
}
}
out:
rcu_read_unlock();
cb->args[1] = e;
cb->args[0] = h;
res = res < 0 ? res : skb->len;
if (res <= 0)
fib6_dump_end(cb);
return res;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Patrick McHardy | 248 | 77.02% | 1 | 8.33% |
Thomas Graf | 28 | 8.70% | 3 | 25.00% |
Daniel Lezcano | 17 | 5.28% | 1 | 8.33% |
Denis V. Lunev | 9 | 2.80% | 1 | 8.33% |
Eric Dumazet | 7 | 2.17% | 1 | 8.33% |
Brian Haley | 6 | 1.86% | 1 | 8.33% |
Hideaki Yoshifuji / 吉藤英明 | 3 | 0.93% | 1 | 8.33% |
David S. Miller | 2 | 0.62% | 1 | 8.33% |
Hannes Frederic Sowa | 1 | 0.31% | 1 | 8.33% |
Neil Horman | 1 | 0.31% | 1 | 8.33% |
Total | 322 | 100.00% | 12 | 100.00% |
/*
* Routing Table
*
* return the appropriate node for a routing tree "add" operation
* by either creating and inserting or by returning an existing
* node.
*/
static struct fib6_node *fib6_add_1(struct fib6_node *root,
struct in6_addr *addr, int plen,
int offset, int allow_create,
int replace_required, int sernum)
{
struct fib6_node *fn, *in, *ln;
struct fib6_node *pn = NULL;
struct rt6key *key;
int bit;
__be32 dir = 0;
RT6_TRACE("fib6_add_1\n");
/* insert node in tree */
fn = root;
do {
key = (struct rt6key *)((u8 *)fn->leaf + offset);
/*
* Prefix match
*/
if (plen < fn->fn_bit ||
!ipv6_prefix_equal(&key->addr, addr, fn->fn_bit)) {
if (!allow_create) {
if (replace_required) {
pr_warn("Can't replace route, no match found\n");
return ERR_PTR(-ENOENT);
}
pr_warn("NLM_F_CREATE should be set when creating new route\n");
}
goto insert_above;
}
/*
* Exact match ?
*/
if (plen == fn->fn_bit) {
/* clean up an intermediate node */
if (!(fn->fn_flags & RTN_RTINFO)) {
rt6_release(fn->leaf);
fn->leaf = NULL;
}
fn->fn_sernum = sernum;
return fn;
}
/*
* We have more bits to go
*/
/* Try to walk down on tree. */
fn->fn_sernum = sernum;
dir = addr_bit_set(addr, fn->fn_bit);
pn = fn;
fn = dir ? fn->right : fn->left;
} while (fn);
if (!allow_create) {
/* We should not create new node because
* NLM_F_REPLACE was specified without NLM_F_CREATE
* I assume it is safe to require NLM_F_CREATE when
* REPLACE flag is used! Later we may want to remove the
* check for replace_required, because according
* to netlink specification, NLM_F_CREATE
* MUST be specified if new route is created.
* That would keep IPv6 consistent with IPv4
*/
if (replace_required) {
pr_warn("Can't replace route, no match found\n");
return ERR_PTR(-ENOENT);
}
pr_warn("NLM_F_CREATE should be set when creating new route\n");
}
/*
* We walked to the bottom of tree.
* Create new leaf node without children.
*/
ln = node_alloc();
if (!ln)
return ERR_PTR(-ENOMEM);
ln->fn_bit = plen;
ln->parent = pn;
ln->fn_sernum = sernum;
if (dir)
pn->right = ln;
else
pn->left = ln;
return ln;
insert_above:
/*
* split since we don't have a common prefix anymore or
* we have a less significant route.
* we've to insert an intermediate node on the list
* this new node will point to the one we need to create
* and the current
*/
pn = fn->parent;
/* find 1st bit in difference between the 2 addrs.
See comment in __ipv6_addr_diff: bit may be an invalid value,
but if it is >= plen, the value is ignored in any case.
*/
bit = __ipv6_addr_diff(addr, &key->addr, sizeof(*addr));
/*
* (intermediate)[in]
* / \
* (new leaf node)[ln] (old node)[fn]
*/
if (plen > bit) {
in = node_alloc();
ln = node_alloc();
if (!in || !ln) {
if (in)
node_free(in);
if (ln)
node_free(ln);
return ERR_PTR(-ENOMEM);
}
/*
* new intermediate node.
* RTN_RTINFO will
* be off since that an address that chooses one of
* the branches would not match less specific routes
* in the other branch
*/
in->fn_bit = bit;
in->parent = pn;
in->leaf = fn->leaf;
atomic_inc(&in->leaf->rt6i_ref);
in->fn_sernum = sernum;
/* update parent pointer */
if (dir)
pn->right = in;
else
pn->left = in;
ln->fn_bit = plen;
ln->parent = in;
fn->parent = in;
ln->fn_sernum = sernum;
if (addr_bit_set(addr, bit)) {
in->right = ln;
in->left = fn;
} else {
in->left = ln;
in->right = fn;
}
} else { /* plen <= bit */
/*
* (new leaf node)[ln]
* / \
* (old node)[fn] NULL
*/
ln = node_alloc();
if (!ln)
return ERR_PTR(-ENOMEM);
ln->fn_bit = plen;
ln->parent = pn;
ln->fn_sernum = sernum;
if (dir)
pn->right = ln;
else
pn->left = ln;
if (addr_bit_set(&key->addr, plen))
ln->right = fn;
else
ln->left = fn;
fn->parent = ln;
}
return ln;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Linus Torvalds (pre-git) | 511 | 82.55% | 3 | 20.00% |
Matti Vaittinen | 62 | 10.02% | 2 | 13.33% |
Lin Ming | 15 | 2.42% | 1 | 6.67% |
David S. Miller | 9 | 1.45% | 2 | 13.33% |
Hideaki Yoshifuji / 吉藤英明 | 7 | 1.13% | 3 | 20.00% |
Fan Du | 7 | 1.13% | 1 | 6.67% |
Joe Perches | 4 | 0.65% | 1 | 6.67% |
Hannes Frederic Sowa | 3 | 0.48% | 1 | 6.67% |
Al Viro | 1 | 0.16% | 1 | 6.67% |
Total | 619 | 100.00% | 15 | 100.00% |
static bool rt6_qualify_for_ecmp(struct rt6_info *rt)
{
return (rt->rt6i_flags & (RTF_GATEWAY|RTF_ADDRCONF|RTF_DYNAMIC)) ==
RTF_GATEWAY;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Hannes Frederic Sowa | 28 | 100.00% | 1 | 100.00% |
Total | 28 | 100.00% | 1 | 100.00% |
static void fib6_copy_metrics(u32 *mp, const struct mx6_config *mxc)
{
int i;
for (i = 0; i < RTAX_MAX; i++) {
if (test_bit(i, mxc->mx_valid))
mp[i] = mxc->mx[i];
}
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Florian Westphal | 57 | 100.00% | 1 | 100.00% |
Total | 57 | 100.00% | 1 | 100.00% |
static int fib6_commit_metrics(struct dst_entry *dst, struct mx6_config *mxc)
{
if (!mxc->mx)
return 0;
if (dst->flags & DST_HOST) {
u32 *mp = dst_metrics_write_ptr(dst);
if (unlikely(!mp))
return -ENOMEM;
fib6_copy_metrics(mp, mxc);
} else {
dst_init_metrics(dst, mxc->mx, false);
/* We've stolen mx now. */
mxc->mx = NULL;
}
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Michal Kubeček | 45 | 51.14% | 1 | 33.33% |
Florian Westphal | 35 | 39.77% | 1 | 33.33% |
Daniel Borkmann | 8 | 9.09% | 1 | 33.33% |
Total | 88 | 100.00% | 3 | 100.00% |
static void fib6_purge_rt(struct rt6_info *rt, struct fib6_node *fn,
struct net *net)
{
if (atomic_read(&rt->rt6i_ref) != 1) {
/* This route is used as dummy address holder in some split
* nodes. It is not leaked, but it still holds other resources,
* which must be released in time. So, scan ascendant nodes
* and replace dummy references to this route with references
* to still alive ones.
*/
while (fn) {
if (!(fn->fn_flags & RTN_RTINFO) && fn->leaf == rt) {
fn->leaf = fib6_find_prefix(net, fn);
atomic_inc(&fn->leaf->rt6i_ref);
rt6_release(rt);
}
fn = fn->parent;
}
/* No more references are possible at this point. */
BUG_ON(atomic_read(&rt->rt6i_ref) != 1);
}
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Hannes Frederic Sowa | 107 | 100.00% | 1 | 100.00% |
Total | 107 | 100.00% | 1 | 100.00% |
/*
* Insert routing information in a node.
*/
static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt,
struct nl_info *info, struct mx6_config *mxc)
{
struct rt6_info *iter = NULL;
struct rt6_info **ins;
struct rt6_info **fallback_ins = NULL;
int replace = (info->nlh &&