cregit-Linux how code gets into the kernel

Release 4.8 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 <asm/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. millerdavid 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. millerdavid s. miller9073.77%240.00%
eric dumazeteric dumazet2318.85%240.00%
timo terastimo teras97.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 dumazeteric dumazet6598.48%150.00%
david s. millerdavid 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 yanzheng yan5049.50%111.11%
yanmin zhangyanmin zhang2423.76%111.11%
david s. millerdavid s. miller1918.81%444.44%
roopa prabhuroopa prabhu65.94%111.11%
nicolas dichtelnicolas dichtel10.99%111.11%
eric dumazeteric 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
pre-gitpre-git3447.22%116.67%
david s. millerdavid s. miller3143.06%233.33%
zheng yanzheng yan56.94%116.67%
joe perchesjoe perches11.39%116.67%
lai jiangshanlai jiangshan11.39%116.67%
Total72100.00%6100.00%


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
pre-gitpre-git4750.00%228.57%
david s. millerdavid s. miller3840.43%342.86%
christian ehrhardtchristian ehrhardt77.45%114.29%
stephen hemmingerstephen 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
pre-gitpre-git11388.28%337.50%
roopa prabhuroopa prabhu118.59%112.50%
linus torvaldslinus torvalds10.78%112.50%
eric dumazeteric dumazet10.78%112.50%
andy gospodarekandy gospodarek10.78%112.50%
patrick mchardypatrick mchardy10.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. millerdavid 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. millerdavid s. miller9696.00%480.00%
al viroal 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
pre-gitpre-git9351.67%327.27%
david s. millerdavid s. miller5430.00%218.18%
eric dumazeteric dumazet116.11%218.18%
denis v. lunevdenis v. lunev105.56%19.09%
octavian purdilaoctavian purdila52.78%19.09%
linus torvaldslinus torvalds52.78%19.09%
andy gospodarekandy 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
pre-gitpre-git5149.04%240.00%
david s. millerdavid s. miller4947.12%120.00%
stephen hemmingerstephen hemminger32.88%120.00%
al viroal 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 grafthomas graf10966.87%133.33%
roopa prabhuroopa prabhu4728.83%133.33%
daniel borkmanndaniel 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. millerdavid s. miller8345.86%16.25%
thomas grafthomas graf5630.94%318.75%
patrick mchardypatrick mchardy2212.15%318.75%
denis v. lunevdenis v. lunev84.42%212.50%
milan kocianmilan kocian52.76%16.25%
eric w. biedermaneric w. biederman21.10%16.25%
ian morrisian morris10.55%16.25%
pablo neira ayusopablo neira ayuso10.55%16.25%
al viroal viro10.55%16.25%
jamal hadi salimjamal hadi salim10.55%16.25%
joe perchesjoe perches10.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 olssonrobert olsson13392.36%133.33%
julian anastasovjulian anastasov106.94%133.33%
stephen hemmingerstephen 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
pre-gitpre-git3158.49%150.00%
thomas grafthomas 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) { struct net *net = cfg->fc_nlinfo.nl_net; 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 net_device *dev = NULL; struct nlattr *nla_entype; nla_entype = nla_find(attrs, attrlen, RTA_ENCAP_TYPE); if (!nla_entype) goto err_inval; if (cfg->fc_oif) dev = __dev_get_by_index(net, cfg->fc_oif); ret = lwtunnel_build_state(dev, 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
roopa prabhuroopa prabhu12636.52%17.69%
pre-gitpre-git9828.41%323.08%
thomas grafthomas graf7622.03%17.69%
david s. millerdavid s. miller205.80%323.08%
julian anastasovjulian anastasov164.64%17.69%
tom herberttom herbert41.16%17.69%
nicolas dichtelnicolas dichtel30.87%17.69%
jiri bencjiri benc10.29%17.69%
patrick mchardypatrick mchardy10.29%17.69%
Total345100.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 christensenpeter christensen19598.98%266.67%
david aherndavid 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 christensenpeter 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(struct net *net, u16 encap_type, struct nlattr *encap, int oif, const struct fib_nh *nh, const struct fib_config *cfg) { struct lwtunnel_state *lwtstate; struct net_device *dev = NULL; int ret, result = 0; if (encap_type == LWTUNNEL_ENCAP_NONE) return 0; if (oif) dev = __dev_get_by_index(net, oif); ret = lwtunnel_build_state(dev, 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 prabhuroopa prabhu9378.15%125.00%
jiri bencjiri benc1512.61%125.00%
tom herberttom herbert108.40%125.00%
ying xueying xue10.84%125.00%
Total119100.00%4100.00%


int fib_nh_match(struct fib_config *cfg, struct fib_info *fi) { struct net *net = cfg->fc_nlinfo.nl_net; #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(net, cfg->fc_encap_type, cfg->fc_encap, cfg->fc_oif, 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