cregit-Linux how code gets into the kernel

Release 4.8 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 __rcu *xfrm_policy_afinfo[NPROTO]
						
__read_mostly;


static struct kmem_cache *xfrm_dst_cache __read_mostly;

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 __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 mortonandrew morton10372.03%112.50%
david s. millerdavid s. miller3423.78%675.00%
alexey dobriyanalexey 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 mortonandrew morton10976.22%114.29%
david s. millerdavid 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 xuherbert xu3873.08%233.33%
andrew mortonandrew morton1019.23%116.67%
david s. millerdavid s. miller47.69%350.00%
Total52100.00%6100.00%


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

Contributors

PersonTokensPropCommitsCommitProp
eric dumazeteric dumazet56100.00%1100.00%
Total56100.00%1100.00%


static void xfrm_policy_put_afinfo(struct xfrm_policy_afinfo *afinfo) { rcu_read_unlock(); }

Contributors

PersonTokensPropCommitsCommitProp
eric dumazeteric dumazet14100.00%1100.00%
Total14100.00%1100.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) { 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); xfrm_policy_put_afinfo(afinfo); return dst; }

Contributors

PersonTokensPropCommitsCommitProp
hideaki yoshifujihideaki yoshifuji7784.62%125.00%
alexey dobriyanalexey dobriyan77.69%125.00%
david aherndavid ahern55.49%125.00%
david s. millerdavid s. miller22.20%125.00%
Total91100.00%4100.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 xuherbert xu10156.11%457.14%
hideaki yoshifujihideaki yoshifuji6234.44%114.29%
alexey dobriyanalexey dobriyan126.67%114.29%
david aherndavid 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 yoshifujihideaki yoshifuji2161.76%150.00%
kazunori miyazawakazunori 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 xuherbert xu24062.66%642.86%
hideaki yoshifujihideaki yoshifuji8321.67%17.14%
alexey kuznetsovalexey kuznetsov277.05%17.14%
david s. millerdavid s. miller174.44%17.14%
kazunori miyazawakazunori miyazawa61.57%17.14%
jamal hadi salimjamal hadi salim41.04%17.14%
james morrisjames morris30.78%214.29%
timo terastimo teras30.78%17.14%
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 terastimo teras52100.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 terastimo teras34100.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 terastimo teras24100.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 kuznetsovalexey kuznetsov5637.58%428.57%
steffen klassertsteffen klassert2818.79%17.14%
david s. millerdavid s. miller2013.42%17.14%
timo terastimo teras1610.74%214.29%
alexey dobriyanalexey dobriyan1510.07%17.14%
pavel emelianovpavel emelianov53.36%17.14%
andrew mortonandrew morton42.68%17.14%
herbert xuherbert xu32.01%17.14%
al viroal viro10.67%17.14%
panagiotis issaris*panagiotis issaris*10.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 dumazeteric 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 kuznetsovalexey kuznetsov2954.72%222.22%
fan dufan du1018.87%111.11%
eric dumazeteric dumazet59.43%111.11%
kris katterjohnkris katterjohn35.66%111.11%
herbert xuherbert xu23.77%111.11%
trent jaegertrent jaeger23.77%111.11%
americo wangamerico wang11.89%111.11%
paul moorepaul moore11.89%111.11%
Total53100.00%9100.00%

EXPORT_SYMBOL(xfrm_policy_destroy); /* Rule must be locked. Release descentant 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 xuherbert xu2939.19%333.33%
steffen klassertsteffen klassert2635.14%222.22%
timo terastimo teras1317.57%111.11%
alexey kuznetsovalexey kuznetsov45.41%111.11%
christoph hellwigchristoph hellwig11.35%111.11%
li rongqingli rongqing11.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. millerdavid s. miller1760.71%125.00%
alexey dobriyanalexey dobriyan1035.71%250.00%
masahide nakamuramasahide 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 gouaultchristophe 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); return (hash == hmask + 1 ? &net->xfrm.policy_inexact[dir] : net->xfrm.policy_bydst[dir].table + hash); }

Contributors

PersonTokensPropCommitsCommitProp
david s. millerdavid s. miller6456.64%233.33%
christophe gouaultchristophe gouault2925.66%116.67%
alexey dobriyanalexey dobriyan2017.70%350.00%
Total113100.00%6100.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 net->xfrm.policy_bydst[dir].table + hash; }

Contributors

PersonTokensPropCommitsCommitProp
david s. millerdavid s. miller5756.44%240.00%
christophe gouaultchristophe gouault2928.71%120.00%
alexey dobriyanalexey dobriyan1514.85%240.00%
Total101100.00%5100.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(&pol->bydst); hlist_add_head(&pol->bydst, ndsttable+h); h0 = h; } else { if (h != h0) continue; hlist_del(&pol->bydst); hlist_add_behind(&pol->bydst, entry0); } entry0 = &pol->bydst; } if (!hlist_empty(list)) { entry0 = NULL; goto redo; } }

Contributors

PersonTokensPropCommitsCommitProp
david s. millerdavid s. miller8341.09%120.00%
hideaki yoshifujihideaki yoshifuji6934.16%120.00%
christophe gouaultchristophe gouault3517.33%120.00%
sasha levinsasha levin125.94%120.00%
ken heliasken helias31.49%120.00%
Total202100.00%5100.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. millerdavid 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. millerdavid 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 *odst = net->xfrm.policy_bydst[dir].table; struct hlist_head *ndst = xfrm_hash_alloc(nsize); int i; if (!ndst) return; write_lock_bh(&net->xfrm.xfrm_policy_lock); for (i = hmask; i >= 0; i--) xfrm_dst_hash_transfer(net, odst + i, ndst, nhashmask, dir); net->xfrm.policy_bydst[dir].table = ndst; net->xfrm.policy_bydst[dir].hmask = nhashmask; write_unlock_bh(&net->xfrm.xfrm_policy_lock); xfrm_hash_free(odst, (hmask + 1) * sizeof(struct hlist_head)); }

Contributors

PersonTokensPropCommitsCommitProp
david s. millerdavid s. miller14379.44%233.33%
alexey dobriyanalexey dobriyan2513.89%233.33%
fan dufan du84.44%116.67%
christophe gouaultchristophe gouault42.22%116.67%
Total180100.00%6100.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; write_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; write_unlock_bh(&net->xfrm.xfrm_policy_lock); xfrm_hash_free(oidx, (hmask + 1) * sizeof(struct hlist_head)); }

Contributors

PersonTokensPropCommitsCommitProp
david s. millerdavid s. miller12378.85%233.33%
alexey dobriyanalexey dobriyan2516.03%350.00%
fan dufan du85.13%116.67%
Total156100.00%6100.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. millerdavid s. miller6180.26%125.00%
alexey dobriyanalexey 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. millerdavid s. miller3577.78%133.33%
alexey dobriyanalexey dobriyan1022.22%266.67%
Total45100.00%3100.00%


void xfrm_spd_getinfo(struct net *net, struct xfrmk_spdinfo *si) { read_lock_bh(&net->xfrm.xfrm_policy_lock); si->incnt = net->xfrm.policy_count[XFRM_POLICY_IN]; si->outcnt = net->xfrm.policy_count[XFRM_POLICY_OUT]; si->fwdcnt = net->xfrm.policy_count[XFRM_POLICY_FWD];