cregit-Linux how code gets into the kernel

Release 4.11 net/ipv4/fib_semantics.c

Directory: net/ipv4
/*
 * INET         An implementation of the TCP/IP protocol suite for the LINUX
 *              operating system.  INET is implemented using the  BSD Socket
 *              interface as the means of communication with the user level.
 *
 *              IPv4 Forwarding Information Base: semantics.
 *
 * Authors:     Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
 *
 *              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/bitops.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/jiffies.h>
#include <linux/mm.h>
#include <linux/string.h>
#include <linux/socket.h>
#include <linux/sockios.h>
#include <linux/errno.h>
#include <linux/in.h>
#include <linux/inet.h>
#include <linux/inetdevice.h>
#include <linux/netdevice.h>
#include <linux/if_arp.h>
#include <linux/proc_fs.h>
#include <linux/skbuff.h>
#include <linux/init.h>
#include <linux/slab.h>

#include <net/arp.h>
#include <net/ip.h>
#include <net/protocol.h>
#include <net/route.h>
#include <net/tcp.h>
#include <net/sock.h>
#include <net/ip_fib.h>
#include <net/netlink.h>
#include <net/nexthop.h>
#include <net/lwtunnel.h>

#include "fib_lookup.h"

static DEFINE_SPINLOCK(fib_info_lock);

static struct hlist_head *fib_info_hash;

static struct hlist_head *fib_info_laddrhash;

static unsigned int fib_info_hash_size;

static unsigned int fib_info_cnt;


#define DEVINDEX_HASHBITS 8

#define DEVINDEX_HASHSIZE (1U << DEVINDEX_HASHBITS)

static struct hlist_head fib_info_devhash[DEVINDEX_HASHSIZE];

#ifdef CONFIG_IP_ROUTE_MULTIPATH

u32 fib_multipath_secret __read_mostly;


#define for_nexthops(fi) {                                               \
        int nhsel; const struct fib_nh *nh;                             \
        for (nhsel = 0, nh = (fi)->fib_nh;                              \
             nhsel < (fi)->fib_nhs;                                     \
             nh++, nhsel++)


#define change_nexthops(fi) {                                               \
        int nhsel; struct fib_nh *nexthop_nh;                           \
        for (nhsel = 0, nexthop_nh = (struct fib_nh *)((fi)->fib_nh);   \
             nhsel < (fi)->fib_nhs;                                     \
             nexthop_nh++, nhsel++)

#else /* CONFIG_IP_ROUTE_MULTIPATH */

/* Hope, that gcc will optimize it to get rid of dummy loop */


#define for_nexthops(fi) {                                               \
        int nhsel; const struct fib_nh *nh = (fi)->fib_nh;              \
        for (nhsel = 0; nhsel < 1; nhsel++)


#define change_nexthops(fi) {                                               \
        int nhsel;                                                      \
        struct fib_nh *nexthop_nh = (struct fib_nh *)((fi)->fib_nh);    \
        for (nhsel = 0; nhsel < 1; nhsel++)

#endif /* CONFIG_IP_ROUTE_MULTIPATH */


#define endfor_nexthops(fi) }



const struct fib_prop fib_props[RTN_MAX + 1] = {
	[RTN_UNSPEC] = {
		.error	= 0,
		.scope	= RT_SCOPE_NOWHERE,
        },
	[RTN_UNICAST] = {
		.error	= 0,
		.scope	= RT_SCOPE_UNIVERSE,
        },
	[RTN_LOCAL] = {
		.error	= 0,
		.scope	= RT_SCOPE_HOST,
        },
	[RTN_BROADCAST] = {
		.error	= 0,
		.scope	= RT_SCOPE_LINK,
        },
	[RTN_ANYCAST] = {
		.error	= 0,
		.scope	= RT_SCOPE_LINK,
        },
	[RTN_MULTICAST] = {
		.error	= 0,
		.scope	= RT_SCOPE_UNIVERSE,
        },
	[RTN_BLACKHOLE] = {
		.error	= -EINVAL,
		.scope	= RT_SCOPE_UNIVERSE,
        },
	[RTN_UNREACHABLE] = {
		.error	= -EHOSTUNREACH,
		.scope	= RT_SCOPE_UNIVERSE,
        },
	[RTN_PROHIBIT] = {
		.error	= -EACCES,
		.scope	= RT_SCOPE_UNIVERSE,
        },
	[RTN_THROW] = {
		.error	= -EAGAIN,
		.scope	= RT_SCOPE_UNIVERSE,
        },
	[RTN_NAT] = {
		.error	= -EINVAL,
		.scope	= RT_SCOPE_NOWHERE,
        },
	[RTN_XRESOLVE] = {
		.error	= -EINVAL,
		.scope	= RT_SCOPE_NOWHERE,
        },
};


static void rt_fibinfo_free(struct rtable __rcu **rtp) { struct rtable *rt = rcu_dereference_protected(*rtp, 1); if (!rt) return; /* Not even needed : RCU_INIT_POINTER(*rtp, NULL); * because we waited an RCU grace period before calling * free_fib_info_rcu() */ dst_free(&rt->dst); }

Contributors

PersonTokensPropCommitsCommitProp
David S. Miller41100.00%1100.00%
Total41100.00%1100.00%


static void free_nh_exceptions(struct fib_nh *nh) { struct fnhe_hash_bucket *hash; int i; hash = rcu_dereference_protected(nh->nh_exceptions, 1); if (!hash) return; for (i = 0; i < FNHE_HASH_SIZE; i++) { struct fib_nh_exception *fnhe; fnhe = rcu_dereference_protected(hash[i].chain, 1); while (fnhe) { struct fib_nh_exception *next; next = rcu_dereference_protected(fnhe->fnhe_next, 1); rt_fibinfo_free(&fnhe->fnhe_rth_input); rt_fibinfo_free(&fnhe->fnhe_rth_output); kfree(fnhe); fnhe = next; } } kfree(hash); }

Contributors

PersonTokensPropCommitsCommitProp
David S. Miller9073.77%240.00%
Eric Dumazet2318.85%240.00%
Timo Teräs97.38%120.00%
Total122100.00%5100.00%


static void rt_fibinfo_free_cpus(struct rtable __rcu * __percpu *rtp) { int cpu; if (!rtp) return; for_each_possible_cpu(cpu) { struct rtable *rt; rt = rcu_dereference_protected(*per_cpu_ptr(rtp, cpu), 1); if (rt) dst_free(&rt->dst); } free_percpu(rtp); }

Contributors

PersonTokensPropCommitsCommitProp
Eric Dumazet6598.48%150.00%
David S. Miller11.52%150.00%
Total66100.00%2100.00%

/* Release a nexthop info record */
static void free_fib_info_rcu(struct rcu_head *head) { struct fib_info *fi = container_of(head, struct fib_info, rcu); change_nexthops(fi) { if (nexthop_nh->nh_dev) dev_put(nexthop_nh->nh_dev); lwtstate_put(nexthop_nh->nh_lwtstate); free_nh_exceptions(nexthop_nh); rt_fibinfo_free_cpus(nexthop_nh->nh_pcpu_rth_output); rt_fibinfo_free(&nexthop_nh->nh_rth_input); } endfor_nexthops(fi); if (fi->fib_metrics != (u32 *) dst_default_metrics) kfree(fi->fib_metrics); kfree(fi); }

Contributors

PersonTokensPropCommitsCommitProp
Zheng Yan5049.50%111.11%
Yanmin Zhang2423.76%111.11%
David S. Miller1918.81%444.44%
Roopa Prabhu65.94%111.11%
Nicolas Dichtel10.99%111.11%
Eric Dumazet10.99%111.11%
Total101100.00%9100.00%


void free_fib_info(struct fib_info *fi) { if (fi->fib_dead == 0) { pr_warn("Freeing alive fib_info %p\n", fi); return; } fib_info_cnt--; #ifdef CONFIG_IP_ROUTE_CLASSID change_nexthops(fi) { if (nexthop_nh->nh_tclassid) fi->fib_net->ipv4.fib_num_tclassid_users--; } endfor_nexthops(fi); #endif call_rcu(&fi->rcu, free_fib_info_rcu); }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)3447.22%116.67%
David S. Miller3143.06%233.33%
Zheng Yan56.94%116.67%
Joe Perches11.39%116.67%
Lai Jiangshan11.39%116.67%
Total72100.00%6100.00%

EXPORT_SYMBOL_GPL(free_fib_info);
void fib_release_info(struct fib_info *fi) { spin_lock_bh(&fib_info_lock); if (fi && --fi->fib_treeref == 0) { hlist_del(&fi->fib_hash); if (fi->fib_prefsrc) hlist_del(&fi->fib_lhash); change_nexthops(fi) { if (!nexthop_nh->nh_dev) continue; hlist_del(&nexthop_nh->nh_hash); } endfor_nexthops(fi) fi->fib_dead = 1; fib_info_put(fi); } spin_unlock_bh(&fib_info_lock); }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)4750.00%228.57%
David S. Miller3840.43%342.86%
Christian Ehrhardt77.45%114.29%
Stephen Hemminger22.13%114.29%
Total94100.00%7100.00%


static inline int nh_comp(const struct fib_info *fi, const struct fib_info *ofi) { const struct fib_nh *onh = ofi->fib_nh; for_nexthops(fi) { if (nh->nh_oif != onh->nh_oif || nh->nh_gw != onh->nh_gw || nh->nh_scope != onh->nh_scope || #ifdef CONFIG_IP_ROUTE_MULTIPATH nh->nh_weight != onh->nh_weight || #endif #ifdef CONFIG_IP_ROUTE_CLASSID nh->nh_tclassid != onh->nh_tclassid || #endif lwtunnel_cmp_encap(nh->nh_lwtstate, onh->nh_lwtstate) || ((nh->nh_flags ^ onh->nh_flags) & ~RTNH_COMPARE_MASK)) return -1; onh++; } endfor_nexthops(fi); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)11388.28%337.50%
Roopa Prabhu118.59%112.50%
Linus Torvalds10.78%112.50%
Patrick McHardy10.78%112.50%
Andy Gospodarek10.78%112.50%
Eric Dumazet10.78%112.50%
Total128100.00%8100.00%


static inline unsigned int fib_devindex_hashfn(unsigned int val) { unsigned int mask = DEVINDEX_HASHSIZE - 1; return (val ^ (val >> DEVINDEX_HASHBITS) ^ (val >> (DEVINDEX_HASHBITS * 2))) & mask; }

Contributors

PersonTokensPropCommitsCommitProp
David S. Miller43100.00%1100.00%
Total43100.00%1100.00%


static inline unsigned int fib_info_hashfn(const struct fib_info *fi) { unsigned int mask = (fib_info_hash_size - 1); unsigned int val = fi->fib_nhs; val ^= (fi->fib_protocol << 8) | fi->fib_scope; val ^= (__force u32)fi->fib_prefsrc; val ^= fi->fib_priority; for_nexthops(fi) { val ^= fib_devindex_hashfn(nh->nh_oif); } endfor_nexthops(fi) return (val ^ (val >> 7) ^ (val >> 12)) & mask; }

Contributors

PersonTokensPropCommitsCommitProp
David S. Miller9696.00%480.00%
Al Viro44.00%120.00%
Total100100.00%5100.00%


static struct fib_info *fib_find_info(const struct fib_info *nfi) { struct hlist_head *head; struct fib_info *fi; unsigned int hash; hash = fib_info_hashfn(nfi); head = &fib_info_hash[hash]; hlist_for_each_entry(fi, head, fib_hash) { if (!net_eq(fi->fib_net, nfi->fib_net)) continue; if (fi->fib_nhs != nfi->fib_nhs) continue; if (nfi->fib_protocol == fi->fib_protocol && nfi->fib_scope == fi->fib_scope && nfi->fib_prefsrc == fi->fib_prefsrc && nfi->fib_priority == fi->fib_priority && nfi->fib_type == fi->fib_type && memcmp(nfi->fib_metrics, fi->fib_metrics, sizeof(u32) * RTAX_MAX) == 0 && !((nfi->fib_flags ^ fi->fib_flags) & ~RTNH_COMPARE_MASK) && (nfi->fib_nhs == 0 || nh_comp(fi, nfi) == 0)) return fi; } return NULL; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)9351.67%327.27%
David S. Miller5430.00%218.18%
Eric Dumazet116.11%218.18%
Denis V. Lunev105.56%19.09%
Octavian Purdila52.78%19.09%
Linus Torvalds52.78%19.09%
Andy Gospodarek21.11%19.09%
Total180100.00%11100.00%

/* Check, that the gateway is already configured. * Used only by redirect accept routine. */
int ip_fib_check_default(__be32 gw, struct net_device *dev) { struct hlist_head *head; struct fib_nh *nh; unsigned int hash; spin_lock(&fib_info_lock); hash = fib_devindex_hashfn(dev->ifindex); head = &fib_info_devhash[hash]; hlist_for_each_entry(nh, head, nh_hash) { if (nh->nh_dev == dev && nh->nh_gw == gw && !(nh->nh_flags & RTNH_F_DEAD)) { spin_unlock(&fib_info_lock); return 0; } } spin_unlock(&fib_info_lock); return -1; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)5149.04%240.00%
David S. Miller4947.12%120.00%
Stephen Hemminger32.88%120.00%
Al Viro10.96%120.00%
Total104100.00%5100.00%


static inline size_t fib_nlmsg_size(struct fib_info *fi) { size_t payload = NLMSG_ALIGN(sizeof(struct rtmsg)) + nla_total_size(4) /* RTA_TABLE */ + nla_total_size(4) /* RTA_DST */ + nla_total_size(4) /* RTA_PRIORITY */ + nla_total_size(4) /* RTA_PREFSRC */ + nla_total_size(TCP_CA_NAME_MAX); /* RTAX_CC_ALGO */ /* space for nested metrics */ payload += nla_total_size((RTAX_MAX * nla_total_size(4))); if (fi->fib_nhs) { size_t nh_encapsize = 0; /* Also handles the special case fib_nhs == 1 */ /* each nexthop is packed in an attribute */ size_t nhsize = nla_total_size(sizeof(struct rtnexthop)); /* may contain flow and gateway attribute */ nhsize += 2 * nla_total_size(4); /* grab encap info */ for_nexthops(fi) { if (nh->nh_lwtstate) { /* RTA_ENCAP_TYPE */ nh_encapsize += lwtunnel_get_encap_size( nh->nh_lwtstate); /* RTA_ENCAP */ nh_encapsize += nla_total_size(2); } } endfor_nexthops(fi); /* all nexthops are packed in a nested attribute */ payload += nla_total_size((fi->fib_nhs * nhsize) + nh_encapsize); } return payload; }

Contributors

PersonTokensPropCommitsCommitProp
Thomas Graf10966.87%133.33%
Roopa Prabhu4728.83%133.33%
Daniel Borkmann74.29%133.33%
Total163100.00%3100.00%


void rtmsg_fib(int event, __be32 key, struct fib_alias *fa, int dst_len, u32 tb_id, const struct nl_info *info, unsigned int nlm_flags) { struct sk_buff *skb; u32 seq = info->nlh ? info->nlh->nlmsg_seq : 0; int err = -ENOBUFS; skb = nlmsg_new(fib_nlmsg_size(fa->fa_info), GFP_KERNEL); if (!skb) goto errout; err = fib_dump_info(skb, info->portid, seq, event, tb_id, fa->fa_type, key, dst_len, fa->fa_tos, fa->fa_info, nlm_flags); if (err < 0) { /* -EMSGSIZE implies BUG in fib_nlmsg_size() */ WARN_ON(err == -EMSGSIZE); kfree_skb(skb); goto errout; } rtnl_notify(skb, info->nl_net, info->portid, RTNLGRP_IPV4_ROUTE, info->nlh, GFP_KERNEL); return; errout: if (err < 0) rtnl_set_sk_err(info->nl_net, RTNLGRP_IPV4_ROUTE, err); }

Contributors

PersonTokensPropCommitsCommitProp
David S. Miller8345.86%16.25%
Thomas Graf5630.94%318.75%
Patrick McHardy2212.15%318.75%
Denis V. Lunev84.42%212.50%
Milan Kocian52.76%16.25%
Eric W. Biedermann21.10%16.25%
Ian Morris10.55%16.25%
Joe Perches10.55%16.25%
Jamal Hadi Salim10.55%16.25%
Al Viro10.55%16.25%
Pablo Neira Ayuso10.55%16.25%
Total181100.00%16100.00%


static int fib_detect_death(struct fib_info *fi, int order, struct fib_info **last_resort, int *last_idx, int dflt) { struct neighbour *n; int state = NUD_NONE; n = neigh_lookup(&arp_tbl, &fi->fib_nh[0].nh_gw, fi->fib_dev); if (n) { state = n->nud_state; neigh_release(n); } else { return 0; } if (state == NUD_REACHABLE) return 0; if ((state & NUD_VALID) && order != dflt) return 0; if ((state & NUD_VALID) || (*last_idx < 0 && order > dflt && state != NUD_INCOMPLETE)) { *last_resort = fi; *last_idx = order; } return 1; }

Contributors

PersonTokensPropCommitsCommitProp
Robert Olsson13392.36%133.33%
Julian Anastasov106.94%133.33%
Stephen Hemminger10.69%133.33%
Total144100.00%3100.00%

#ifdef CONFIG_IP_ROUTE_MULTIPATH
static int fib_count_nexthops(struct rtnexthop *rtnh, int remaining) { int nhs = 0; while (rtnh_ok(rtnh, remaining)) { nhs++; rtnh = rtnh_next(rtnh, &remaining); } /* leftover implies invalid nexthop configuration, discard it */ return remaining > 0 ? 0 : nhs; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)3158.49%150.00%
Thomas Graf2241.51%150.00%
Total53100.00%2100.00%


static int fib_get_nhs(struct fib_info *fi, struct rtnexthop *rtnh, int remaining, struct fib_config *cfg) { int ret; change_nexthops(fi) { int attrlen; if (!rtnh_ok(rtnh, remaining)) return -EINVAL; if (rtnh->rtnh_flags & (RTNH_F_DEAD | RTNH_F_LINKDOWN)) return -EINVAL; nexthop_nh->nh_flags = (cfg->fc_flags & ~0xFF) | rtnh->rtnh_flags; nexthop_nh->nh_oif = rtnh->rtnh_ifindex; nexthop_nh->nh_weight = rtnh->rtnh_hops + 1; attrlen = rtnh_attrlen(rtnh); if (attrlen > 0) { struct nlattr *nla, *attrs = rtnh_attrs(rtnh); nla = nla_find(attrs, attrlen, RTA_GATEWAY); nexthop_nh->nh_gw = nla ? nla_get_in_addr(nla) : 0; #ifdef CONFIG_IP_ROUTE_CLASSID nla = nla_find(attrs, attrlen, RTA_FLOW); nexthop_nh->nh_tclassid = nla ? nla_get_u32(nla) : 0; if (nexthop_nh->nh_tclassid) fi->fib_net->ipv4.fib_num_tclassid_users++; #endif nla = nla_find(attrs, attrlen, RTA_ENCAP); if (nla) { struct lwtunnel_state *lwtstate; struct nlattr *nla_entype; nla_entype = nla_find(attrs, attrlen, RTA_ENCAP_TYPE); if (!nla_entype) goto err_inval; ret = lwtunnel_build_state(nla_get_u16( nla_entype), nla, AF_INET, cfg, &lwtstate); if (ret) goto errout; nexthop_nh->nh_lwtstate = lwtstate_get(lwtstate); } } rtnh = rtnh_next(rtnh, &remaining); } endfor_nexthops(fi); return 0; err_inval: ret = -EINVAL; errout: return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)9831.82%323.08%
Roopa Prabhu8928.90%17.69%
Thomas Graf7624.68%17.69%
David S. Miller206.49%323.08%
Julian Anastasov165.19%17.69%
Tom Herbert41.30%17.69%
Nicolas Dichtel30.97%17.69%
Jiri Benc10.32%17.69%
Patrick McHardy10.32%17.69%
Total308100.00%13100.00%


static void fib_rebalance(struct fib_info *fi) { int total; int w; struct in_device *in_dev; if (fi->fib_nhs < 2) return; total = 0; for_nexthops(fi) { if (nh->nh_flags & RTNH_F_DEAD) continue; in_dev = __in_dev_get_rtnl(nh->nh_dev); if (in_dev && IN_DEV_IGNORE_ROUTES_WITH_LINKDOWN(in_dev) && nh->nh_flags & RTNH_F_LINKDOWN) continue; total += nh->nh_weight; } endfor_nexthops(fi); w = 0; change_nexthops(fi) { int upper_bound; in_dev = __in_dev_get_rtnl(nexthop_nh->nh_dev); if (nexthop_nh->nh_flags & RTNH_F_DEAD) { upper_bound = -1; } else if (in_dev && IN_DEV_IGNORE_ROUTES_WITH_LINKDOWN(in_dev) && nexthop_nh->nh_flags & RTNH_F_LINKDOWN) { upper_bound = -1; } else { w += nexthop_nh->nh_weight; upper_bound = DIV_ROUND_CLOSEST_ULL((u64)w << 31, total) - 1; } atomic_set(&nexthop_nh->nh_upper_bound, upper_bound); } endfor_nexthops(fi); net_get_random_once(&fib_multipath_secret, sizeof(fib_multipath_secret)); }

Contributors

PersonTokensPropCommitsCommitProp
Peter Christensen19598.98%266.67%
David Ahern21.02%133.33%
Total197100.00%3100.00%


static inline void fib_add_weight(struct fib_info *fi, const struct fib_nh *nh) { fi->fib_weight += nh->nh_weight; }

Contributors

PersonTokensPropCommitsCommitProp
Peter Christensen26100.00%1100.00%
Total26100.00%1100.00%

#else /* CONFIG_IP_ROUTE_MULTIPATH */ #define fib_rebalance(fi) do { } while (0) #define fib_add_weight(fi, nh) do { } while (0) #endif /* CONFIG_IP_ROUTE_MULTIPATH */
static int fib_encap_match(u16 encap_type, struct nlattr *encap, const struct fib_nh *nh, const struct fib_config *cfg) { struct lwtunnel_state *lwtstate; int ret, result = 0; if (encap_type == LWTUNNEL_ENCAP_NONE) return 0; ret = lwtunnel_build_state(encap_type, encap, AF_INET, cfg, &lwtstate); if (!ret) { result = lwtunnel_cmp_encap(lwtstate, nh->nh_lwtstate); lwtstate_free(lwtstate); } return result; }

Contributors

PersonTokensPropCommitsCommitProp
Roopa Prabhu6370.79%125.00%
Jiri Benc1516.85%125.00%
Tom Herbert1011.24%125.00%
Ying Xue11.12%125.00%
Total89100.00%4100.00%


int fib_nh_match(struct fib_config *cfg, struct fib_info *fi) { #ifdef CONFIG_IP_ROUTE_MULTIPATH struct rtnexthop *rtnh; int remaining; #endif if (cfg->fc_priority && cfg->fc_priority != fi->fib_priority) return 1; if (cfg->fc_oif || cfg->fc_gw) { if (cfg->fc_encap) { if (fib_encap_match(cfg->fc_encap_type, cfg->fc_encap, fi->fib_nh, cfg)) return 1; } if ((!cfg->fc_oif || cfg->fc_oif == fi->fib_nh->nh_oif) && (!cfg->fc_gw || cfg->fc_gw == fi->fib_nh->nh_gw)) return 0; return 1; } #ifdef CONFIG_IP_ROUTE_MULTIPATH if (!cfg->fc_mp) return 0; rtnh = cfg->fc_mp; remaining = cfg->fc_mp_len; for_nexthops(fi) { int attrlen; if (!rtnh_ok(rtnh, remaining)) return -EINVAL; if (rtnh->rtnh_ifindex && rtnh->rtnh_ifindex != nh->nh_oif) return 1; attrlen = rtnh_attrlen(rtnh); if (attrlen > 0) { struct nlattr *nla, *attrs = rtnh_attrs(rtnh); nla = nla_find(attrs, attrlen, RTA_GATEWAY); if (nla && nla_get_in_addr(nla) != nh->nh_gw) return 1; #ifdef CONFIG_IP_ROUTE_CLASSID nla = nla_find(attrs, attrlen, RTA_FLOW); if (nla && nla_get_u32(nla) != nh->nh_tclassid) return 1; #endif } rtnh = rtnh_next(rtnh, &remaining); } endfor_nexthops(fi); #endif return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)18762.13%436.36%
Thomas Graf8026.58%19.09%
Roopa Prabhu289.30%19.09%
Tom Herbert20.66%19.09%
Jiri Pirko10.33%19.09%
Patrick McHardy10.33%19.09%
Jiri Benc10.33%19.09%
Ian Morris10.33%19.09%
Total301100.00%11100.00%

/* * Picture * ------- * * Semantics of nexthop is very messy by historical reasons. * We have to take into account, that: * a) gateway can be actually local interface address, * so that gatewayed route is direct. * b) gateway must be on-link address, possibly * described not by an ifaddr, but also by a direct route. * c) If both gateway and interface are specified, they should not * contradict. * d) If we use tunnel routes, gateway could be not on-link. * * Attempt to reconcile all of these (alas, self-contradictory) conditions * results in pretty ugly and hairy code with obscure logic. * * I chose to generalized it instead, so that the size * of code does not increase practically, but it becomes * much more general. * Every prefix is assigned a "scope" value: "host" is local address, * "link" is direct route, * [ ... "site" ... "interior" ... ] * and "universe" is true gateway route with global meaning. * * Every prefix refers to a set of "nexthop"s (gw, oif), * where gw must have narrower scope. This recursion stops * when gw has LOCAL scope or if "nexthop" is declared ONLINK, * which means that gw is forced to be on link. * * Code is still hairy, but now it is apparently logically * consistent and very flexible. F.e. as by-product it allows * to co-exists in peace independent exterior and interior * routing processes. * * Normally it looks as following. * * {universe prefix} -> (gw, oif) [scope link] * | * |-> {link prefix} -> (gw, oif) [scope local] * | * |-> {local prefix} (terminal node) */
static int fib_check_nh(struct fib_config *cfg, struct fib_info *fi, struct fib_nh *nh) { int err = 0; struct net *net; struct net_device *dev; net = cfg->fc_nlinfo.nl_net; if (nh->nh_gw) { struct fib_result res; if (nh->nh_flags & RTNH_F_ONLINK) { unsigned int addr_type; if (cfg->fc_scope >= RT_SCOPE_LINK) return -EINVAL; dev = __dev_get_by_index(net, nh->