cregit-Linux how code gets into the kernel

Release 4.11 net/xfrm/xfrm_policy.c

Directory: net/xfrm
/*
 * xfrm_policy.c
 *
 * Changes:
 *      Mitsuru KANDA @USAGI
 *      Kazunori MIYAZAWA @USAGI
 *      Kunihiro Ishiguro <kunihiro@ipinfusion.com>
 *              IPv6 support
 *      Kazunori MIYAZAWA @USAGI
 *      YOSHIFUJI Hideaki
 *              Split up af-specific portion
 *      Derek Atkins <derek@ihtfp.com>          Add the post_input processor
 *
 */

#include <linux/err.h>
#include <linux/slab.h>
#include <linux/kmod.h>
#include <linux/list.h>
#include <linux/spinlock.h>
#include <linux/workqueue.h>
#include <linux/notifier.h>
#include <linux/netdevice.h>
#include <linux/netfilter.h>
#include <linux/module.h>
#include <linux/cache.h>
#include <linux/audit.h>
#include <net/dst.h>
#include <net/flow.h>
#include <net/xfrm.h>
#include <net/ip.h>
#ifdef CONFIG_XFRM_STATISTICS
#include <net/snmp.h>
#endif

#include "xfrm_hash.h"


#define XFRM_QUEUE_TMO_MIN ((unsigned)(HZ/10))

#define XFRM_QUEUE_TMO_MAX ((unsigned)(60*HZ))

#define XFRM_MAX_QUEUE_LEN	100


struct xfrm_flo {
	
struct dst_entry *dst_orig;
	
u8 flags;
};

static DEFINE_SPINLOCK(xfrm_policy_afinfo_lock);
static struct xfrm_policy_afinfo const __rcu *xfrm_policy_afinfo[AF_INET6 + 1]
						
__read_mostly;


static struct kmem_cache *xfrm_dst_cache __read_mostly;

static __read_mostly seqcount_t xfrm_policy_hash_generation;

static void xfrm_init_pmtu(struct dst_entry *dst);
static int stale_bundle(struct dst_entry *dst);
static int xfrm_bundle_ok(struct xfrm_dst *xdst);
static void xfrm_policy_queue_process(unsigned long arg);

static void __xfrm_policy_link(struct xfrm_policy *pol, int dir);
static struct xfrm_policy *__xfrm_policy_unlink(struct xfrm_policy *pol,
						int dir);


static inline bool xfrm_pol_hold_rcu(struct xfrm_policy *policy) { return atomic_inc_not_zero(&policy->refcnt); }

Contributors

PersonTokensPropCommitsCommitProp
Florian Westphal21100.00%1100.00%
Total21100.00%1100.00%


static inline bool __xfrm4_selector_match(const struct xfrm_selector *sel, const struct flowi *fl) { const struct flowi4 *fl4 = &fl->u.ip4; return addr4_match(fl4->daddr, sel->daddr.a4, sel->prefixlen_d) && addr4_match(fl4->saddr, sel->saddr.a4, sel->prefixlen_s) && !((xfrm_flowi_dport(fl, &fl4->uli) ^ sel->dport) & sel->dport_mask) && !((xfrm_flowi_sport(fl, &fl4->uli) ^ sel->sport) & sel->sport_mask) && (fl4->flowi4_proto == sel->proto || !sel->proto) && (fl4->flowi4_oif == sel->ifindex || !sel->ifindex); }

Contributors

PersonTokensPropCommitsCommitProp
Andrew Morton10372.03%112.50%
David S. Miller3423.78%675.00%
Alexey Dobriyan64.20%112.50%
Total143100.00%8100.00%


static inline bool __xfrm6_selector_match(const struct xfrm_selector *sel, const struct flowi *fl) { const struct flowi6 *fl6 = &fl->u.ip6; return addr_match(&fl6->daddr, &sel->daddr, sel->prefixlen_d) && addr_match(&fl6->saddr, &sel->saddr, sel->prefixlen_s) && !((xfrm_flowi_dport(fl, &fl6->uli) ^ sel->dport) & sel->dport_mask) && !((xfrm_flowi_sport(fl, &fl6->uli) ^ sel->sport) & sel->sport_mask) && (fl6->flowi6_proto == sel->proto || !sel->proto) && (fl6->flowi6_oif == sel->ifindex || !sel->ifindex); }

Contributors

PersonTokensPropCommitsCommitProp
Andrew Morton10976.22%114.29%
David S. Miller3423.78%685.71%
Total143100.00%7100.00%


bool xfrm_selector_match(const struct xfrm_selector *sel, const struct flowi *fl, unsigned short family) { switch (family) { case AF_INET: return __xfrm4_selector_match(sel, fl); case AF_INET6: return __xfrm6_selector_match(sel, fl); } return false; }

Contributors

PersonTokensPropCommitsCommitProp
Herbert Xu3873.08%233.33%
Andrew Morton1019.23%116.67%
David S. Miller47.69%350.00%
Total52100.00%6100.00%


static const struct xfrm_policy_afinfo *xfrm_policy_get_afinfo(unsigned short family) { const struct xfrm_policy_afinfo *afinfo; if (unlikely(family >= ARRAY_SIZE(xfrm_policy_afinfo))) return NULL; rcu_read_lock(); afinfo = rcu_dereference(xfrm_policy_afinfo[family]); if (unlikely(!afinfo)) rcu_read_unlock(); return afinfo; }

Contributors

PersonTokensPropCommitsCommitProp
Eric Dumazet5590.16%150.00%
Florian Westphal69.84%150.00%
Total61100.00%2100.00%


static inline struct dst_entry *__xfrm_dst_lookup(struct net *net, int tos, int oif, const xfrm_address_t *saddr, const xfrm_address_t *daddr, int family) { const struct xfrm_policy_afinfo *afinfo; struct dst_entry *dst; afinfo = xfrm_policy_get_afinfo(family); if (unlikely(afinfo == NULL)) return ERR_PTR(-EAFNOSUPPORT); dst = afinfo->dst_lookup(net, tos, oif, saddr, daddr); rcu_read_unlock(); return dst; }

Contributors

PersonTokensPropCommitsCommitProp
Hideaki Yoshifuji / 吉藤英明7381.11%116.67%
Alexey Dobriyan77.78%116.67%
David Ahern55.56%116.67%
Florian Westphal33.33%233.33%
David S. Miller22.22%116.67%
Total90100.00%6100.00%


static inline struct dst_entry *xfrm_dst_lookup(struct xfrm_state *x, int tos, int oif, xfrm_address_t *prev_saddr, xfrm_address_t *prev_daddr, int family) { struct net *net = xs_net(x); xfrm_address_t *saddr = &x->props.saddr; xfrm_address_t *daddr = &x->id.daddr; struct dst_entry *dst; if (x->type->flags & XFRM_TYPE_LOCAL_COADDR) { saddr = x->coaddr; daddr = prev_daddr; } if (x->type->flags & XFRM_TYPE_REMOTE_COADDR) { saddr = prev_saddr; daddr = x->coaddr; } dst = __xfrm_dst_lookup(net, tos, oif, saddr, daddr, family); if (!IS_ERR(dst)) { if (prev_saddr != saddr) memcpy(prev_saddr, saddr, sizeof(*prev_saddr)); if (prev_daddr != daddr) memcpy(prev_daddr, daddr, sizeof(*prev_daddr)); } return dst; }

Contributors

PersonTokensPropCommitsCommitProp
Herbert Xu10156.11%457.14%
Hideaki Yoshifuji / 吉藤英明6234.44%114.29%
Alexey Dobriyan126.67%114.29%
David Ahern52.78%114.29%
Total180100.00%7100.00%


static inline unsigned long make_jiffies(long secs) { if (secs >= (MAX_SCHEDULE_TIMEOUT-1)/HZ) return MAX_SCHEDULE_TIMEOUT-1; else return secs*HZ; }

Contributors

PersonTokensPropCommitsCommitProp
Hideaki Yoshifuji / 吉藤英明2161.76%150.00%
Kazunori Miyazawa1338.24%150.00%
Total34100.00%2100.00%


static void xfrm_policy_timer(unsigned long data) { struct xfrm_policy *xp = (struct xfrm_policy *)data; unsigned long now = get_seconds(); long next = LONG_MAX; int warn = 0; int dir; read_lock(&xp->lock); if (unlikely(xp->walk.dead)) goto out; dir = xfrm_policy_id2dir(xp->index); if (xp->lft.hard_add_expires_seconds) { long tmo = xp->lft.hard_add_expires_seconds + xp->curlft.add_time - now; if (tmo <= 0) goto expired; if (tmo < next) next = tmo; } if (xp->lft.hard_use_expires_seconds) { long tmo = xp->lft.hard_use_expires_seconds + (xp->curlft.use_time ? : xp->curlft.add_time) - now; if (tmo <= 0) goto expired; if (tmo < next) next = tmo; } if (xp->lft.soft_add_expires_seconds) { long tmo = xp->lft.soft_add_expires_seconds + xp->curlft.add_time - now; if (tmo <= 0) { warn = 1; tmo = XFRM_KM_TIMEOUT; } if (tmo < next) next = tmo; } if (xp->lft.soft_use_expires_seconds) { long tmo = xp->lft.soft_use_expires_seconds + (xp->curlft.use_time ? : xp->curlft.add_time) - now; if (tmo <= 0) { warn = 1; tmo = XFRM_KM_TIMEOUT; } if (tmo < next) next = tmo; } if (warn) km_policy_expired(xp, dir, 0, 0); if (next != LONG_MAX && !mod_timer(&xp->timer, jiffies + make_jiffies(next))) xfrm_pol_hold(xp); out: read_unlock(&xp->lock); xfrm_pol_put(xp); return; expired: read_unlock(&xp->lock); if (!xfrm_policy_delete(xp, dir)) km_policy_expired(xp, dir, 1, 0); xfrm_pol_put(xp); }

Contributors

PersonTokensPropCommitsCommitProp
Herbert Xu24062.66%642.86%
Hideaki Yoshifuji / 吉藤英明8321.67%17.14%
Alexey Kuznetsov277.05%17.14%
David S. Miller174.44%17.14%
Kazunori Miyazawa61.57%17.14%
Jamal Hadi Salim41.04%17.14%
Timo Teräs30.78%17.14%
James Morris30.78%214.29%
Total383100.00%14100.00%


static struct flow_cache_object *xfrm_policy_flo_get(struct flow_cache_object *flo) { struct xfrm_policy *pol = container_of(flo, struct xfrm_policy, flo); if (unlikely(pol->walk.dead)) flo = NULL; else xfrm_pol_hold(pol); return flo; }

Contributors

PersonTokensPropCommitsCommitProp
Timo Teräs52100.00%1100.00%
Total52100.00%1100.00%


static int xfrm_policy_flo_check(struct flow_cache_object *flo) { struct xfrm_policy *pol = container_of(flo, struct xfrm_policy, flo); return !pol->walk.dead; }

Contributors

PersonTokensPropCommitsCommitProp
Timo Teräs34100.00%1100.00%
Total34100.00%1100.00%


static void xfrm_policy_flo_delete(struct flow_cache_object *flo) { xfrm_pol_put(container_of(flo, struct xfrm_policy, flo)); }

Contributors

PersonTokensPropCommitsCommitProp
Timo Teräs24100.00%1100.00%
Total24100.00%1100.00%

static const struct flow_cache_ops xfrm_policy_fc_ops = { .get = xfrm_policy_flo_get, .check = xfrm_policy_flo_check, .delete = xfrm_policy_flo_delete, }; /* Allocate xfrm_policy. Not used here, it is supposed to be used by pfkeyv2 * SPD calls. */
struct xfrm_policy *xfrm_policy_alloc(struct net *net, gfp_t gfp) { struct xfrm_policy *policy; policy = kzalloc(sizeof(struct xfrm_policy), gfp); if (policy) { write_pnet(&policy->xp_net, net); INIT_LIST_HEAD(&policy->walk.all); INIT_HLIST_NODE(&policy->bydst); INIT_HLIST_NODE(&policy->byidx); rwlock_init(&policy->lock); atomic_set(&policy->refcnt, 1); skb_queue_head_init(&policy->polq.hold_queue); setup_timer(&policy->timer, xfrm_policy_timer, (unsigned long)policy); setup_timer(&policy->polq.hold_timer, xfrm_policy_queue_process, (unsigned long)policy); policy->flo.ops = &xfrm_policy_fc_ops; } return policy; }

Contributors

PersonTokensPropCommitsCommitProp
Alexey Kuznetsov5637.58%428.57%
Steffen Klassert2818.79%17.14%
David S. Miller2013.42%17.14%
Timo Teräs1610.74%214.29%
Alexey Dobriyan1510.07%17.14%
Pavel Emelyanov53.36%17.14%
Andrew Morton42.68%17.14%
Herbert Xu32.01%17.14%
Panagiotis Issaris10.67%17.14%
Al Viro10.67%17.14%
Total149100.00%14100.00%

EXPORT_SYMBOL(xfrm_policy_alloc);
static void xfrm_policy_destroy_rcu(struct rcu_head *head) { struct xfrm_policy *policy = container_of(head, struct xfrm_policy, rcu); security_xfrm_policy_free(policy->security); kfree(policy); }

Contributors

PersonTokensPropCommitsCommitProp
Eric Dumazet38100.00%1100.00%
Total38100.00%1100.00%

/* Destroy xfrm_policy: descendant resources must be released to this moment. */
void xfrm_policy_destroy(struct xfrm_policy *policy) { BUG_ON(!policy->walk.dead); if (del_timer(&policy->timer) || del_timer(&policy->polq.hold_timer)) BUG(); call_rcu(&policy->rcu, xfrm_policy_destroy_rcu); }

Contributors

PersonTokensPropCommitsCommitProp
Alexey Kuznetsov2954.72%222.22%
Fan Du1018.87%111.11%
Eric Dumazet59.43%111.11%
Kris Katterjohn35.66%111.11%
Trent Jaeger23.77%111.11%
Herbert Xu23.77%111.11%
Paul Moore11.89%111.11%
Américo Wang11.89%111.11%
Total53100.00%9100.00%

EXPORT_SYMBOL(xfrm_policy_destroy); /* Rule must be locked. Release descendant resources, announce * entry dead. The rule must be unlinked from lists to the moment. */
static void xfrm_policy_kill(struct xfrm_policy *policy) { policy->walk.dead = 1; atomic_inc(&policy->genid); if (del_timer(&policy->polq.hold_timer)) xfrm_pol_put(policy); skb_queue_purge(&policy->polq.hold_queue); if (del_timer(&policy->timer)) xfrm_pol_put(policy); xfrm_pol_put(policy); }

Contributors

PersonTokensPropCommitsCommitProp
Herbert Xu2939.19%333.33%
Steffen Klassert2635.14%222.22%
Timo Teräs1317.57%111.11%
Alexey Kuznetsov45.41%111.11%
Li RongQing11.35%111.11%
Christoph Hellwig11.35%111.11%
Total74100.00%9100.00%

static unsigned int xfrm_policy_hashmax __read_mostly = 1 * 1024 * 1024;
static inline unsigned int idx_hash(struct net *net, u32 index) { return __idx_hash(index, net->xfrm.policy_idx_hmask); }

Contributors

PersonTokensPropCommitsCommitProp
David S. Miller1760.71%125.00%
Alexey Dobriyan1035.71%250.00%
Masahide Nakamura13.57%125.00%
Total28100.00%4100.00%

/* calculate policy hash thresholds */
static void __get_hash_thresh(struct net *net, unsigned short family, int dir, u8 *dbits, u8 *sbits) { switch (family) { case AF_INET: *dbits = net->xfrm.policy_bydst[dir].dbits4; *sbits = net->xfrm.policy_bydst[dir].sbits4; break; case AF_INET6: *dbits = net->xfrm.policy_bydst[dir].dbits6; *sbits = net->xfrm.policy_bydst[dir].sbits6; break; default: *dbits = 0; *sbits = 0; } }

Contributors

PersonTokensPropCommitsCommitProp
Christophe Gouault107100.00%1100.00%
Total107100.00%1100.00%


static struct hlist_head *policy_hash_bysel(struct net *net, const struct xfrm_selector *sel, unsigned short family, int dir) { unsigned int hmask = net->xfrm.policy_bydst[dir].hmask; unsigned int hash; u8 dbits; u8 sbits; __get_hash_thresh(net, family, dir, &dbits, &sbits); hash = __sel_hash(sel, family, hmask, dbits, sbits); if (hash == hmask + 1) return &net->xfrm.policy_inexact[dir]; return rcu_dereference_check(net->xfrm.policy_bydst[dir].table, lockdep_is_held(&net->xfrm.xfrm_policy_lock)) + hash; }

Contributors

PersonTokensPropCommitsCommitProp
David S. Miller5946.46%228.57%
Christophe Gouault2922.83%114.29%
Alexey Dobriyan2015.75%342.86%
Florian Westphal1914.96%114.29%
Total127100.00%7100.00%


static struct hlist_head *policy_hash_direct(struct net *net, const xfrm_address_t *daddr, const xfrm_address_t *saddr, unsigned short family, int dir) { unsigned int hmask = net->xfrm.policy_bydst[dir].hmask; unsigned int hash; u8 dbits; u8 sbits; __get_hash_thresh(net, family, dir, &dbits, &sbits); hash = __addr_hash(daddr, saddr, family, hmask, dbits, sbits); return rcu_dereference_check(net->xfrm.policy_bydst[dir].table, lockdep_is_held(&net->xfrm.xfrm_policy_lock)) + hash; }

Contributors

PersonTokensPropCommitsCommitProp
David S. Miller5750.00%233.33%
Christophe Gouault2925.44%116.67%
Alexey Dobriyan1513.16%233.33%
Florian Westphal1311.40%116.67%
Total114100.00%6100.00%


static void xfrm_dst_hash_transfer(struct net *net, struct hlist_head *list, struct hlist_head *ndsttable, unsigned int nhashmask, int dir) { struct hlist_node *tmp, *entry0 = NULL; struct xfrm_policy *pol; unsigned int h0 = 0; u8 dbits; u8 sbits; redo: hlist_for_each_entry_safe(pol, tmp, list, bydst) { unsigned int h; __get_hash_thresh(net, pol->family, dir, &dbits, &sbits); h = __addr_hash(&pol->selector.daddr, &pol->selector.saddr, pol->family, nhashmask, dbits, sbits); if (!entry0) { hlist_del_rcu(&pol->bydst); hlist_add_head_rcu(&pol->bydst, ndsttable + h); h0 = h; } else { if (h != h0) continue; hlist_del_rcu(&pol->bydst); hlist_add_behind_rcu(&pol->bydst, entry0); } entry0 = &pol->bydst; } if (!hlist_empty(list)) { entry0 = NULL; goto redo; } }

Contributors

PersonTokensPropCommitsCommitProp
David S. Miller8240.59%116.67%
Hideaki Yoshifuji / 吉藤英明6733.17%116.67%
Christophe Gouault3517.33%116.67%
Sasha Levin125.94%116.67%
Florian Westphal41.98%116.67%
Ken Helias20.99%116.67%
Total202100.00%6100.00%


static void xfrm_idx_hash_transfer(struct hlist_head *list, struct hlist_head *nidxtable, unsigned int nhashmask) { struct hlist_node *tmp; struct xfrm_policy *pol; hlist_for_each_entry_safe(pol, tmp, list, byidx) { unsigned int h; h = __idx_hash(pol->index, nhashmask); hlist_add_head(&pol->byidx, nidxtable+h); } }

Contributors

PersonTokensPropCommitsCommitProp
David S. Miller69100.00%1100.00%
Total69100.00%1100.00%


static unsigned long xfrm_new_hash_mask(unsigned int old_hmask) { return ((old_hmask + 1) << 1) - 1; }

Contributors

PersonTokensPropCommitsCommitProp
David S. Miller24100.00%1100.00%
Total24100.00%1100.00%


static void xfrm_bydst_resize(struct net *net, int dir) { unsigned int hmask = net->xfrm.policy_bydst[dir].hmask; unsigned int nhashmask = xfrm_new_hash_mask(hmask); unsigned int nsize = (nhashmask + 1) * sizeof(struct hlist_head); struct hlist_head *ndst = xfrm_hash_alloc(nsize); struct hlist_head *odst; int i; if (!ndst) return; spin_lock_bh(&net->xfrm.xfrm_policy_lock); write_seqcount_begin(&xfrm_policy_hash_generation); odst = rcu_dereference_protected(net->xfrm.policy_bydst[dir].table, lockdep_is_held(&net->xfrm.xfrm_policy_lock)); odst = rcu_dereference_protected(net->xfrm.policy_bydst[dir].table, lockdep_is_held(&net->xfrm.xfrm_policy_lock)); for (i = hmask; i >= 0; i--) xfrm_dst_hash_transfer(net, odst + i, ndst, nhashmask, dir); rcu_assign_pointer(net->xfrm.policy_bydst[dir].table, ndst); net->xfrm.policy_bydst[dir].hmask = nhashmask; write_seqcount_end(&xfrm_policy_hash_generation); spin_unlock_bh(&net->xfrm.xfrm_policy_lock); synchronize_rcu(); xfrm_hash_free(odst, (hmask + 1) * sizeof(struct hlist_head)); }

Contributors

PersonTokensPropCommitsCommitProp
David S. Miller12953.97%222.22%
Florian Westphal7832.64%333.33%
Alexey Dobriyan208.37%222.22%
Fan Du83.35%111.11%
Christophe Gouault41.67%111.11%
Total239100.00%9100.00%


static void xfrm_byidx_resize(struct net *net, int total) { unsigned int hmask = net->xfrm.policy_idx_hmask; unsigned int nhashmask = xfrm_new_hash_mask(hmask); unsigned int nsize = (nhashmask + 1) * sizeof(struct hlist_head); struct hlist_head *oidx = net->xfrm.policy_byidx; struct hlist_head *nidx = xfrm_hash_alloc(nsize); int i; if (!nidx) return; spin_lock_bh(&net->xfrm.xfrm_policy_lock); for (i = hmask; i >= 0; i--) xfrm_idx_hash_transfer(oidx + i, nidx, nhashmask); net->xfrm.policy_byidx = nidx; net->xfrm.policy_idx_hmask = nhashmask; spin_unlock_bh(&net->xfrm.xfrm_policy_lock); xfrm_hash_free(oidx, (hmask + 1) * sizeof(struct hlist_head)); }

Contributors

PersonTokensPropCommitsCommitProp
David S. Miller12177.56%228.57%
Alexey Dobriyan2516.03%342.86%
Fan Du85.13%114.29%
Florian Westphal21.28%114.29%
Total156100.00%7100.00%


static inline int xfrm_bydst_should_resize(struct net *net, int dir, int *total) { unsigned int cnt = net->xfrm.policy_count[dir]; unsigned int hmask = net->xfrm.policy_bydst[dir].hmask; if (total) *total += cnt; if ((hmask + 1) < xfrm_policy_hashmax && cnt > hmask) return 1; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
David S. Miller6180.26%125.00%
Alexey Dobriyan1519.74%375.00%
Total76100.00%4100.00%


static inline int xfrm_byidx_should_resize(struct net *net, int total) { unsigned int hmask = net->xfrm.policy_idx_hmask; if ((hmask + 1) < xfrm_policy_hashmax && total > hmask) return 1; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
David S. Miller3577.78%133.33%
Alexey Dobriyan1022.22%266.67%
Total45100.00%3100.00%


void xfrm_spd_getinfo(