📄 xfrm_policy.c
字号:
ret = NULL; hlist_for_each_entry(pol, entry, chain, bydst) { if (pol->type == type && !selector_cmp(sel, &pol->selector) && xfrm_sec_ctx_match(ctx, pol->security)) { xfrm_pol_hold(pol); if (delete) { *err = security_xfrm_policy_delete(pol); if (*err) { write_unlock_bh(&xfrm_policy_lock); return pol; } hlist_del(&pol->bydst); hlist_del(&pol->byidx); xfrm_policy_count[dir]--; } ret = pol; break; } } write_unlock_bh(&xfrm_policy_lock); if (ret && delete) { atomic_inc(&flow_cache_genid); xfrm_policy_kill(ret); } return ret;}EXPORT_SYMBOL(xfrm_policy_bysel_ctx);struct xfrm_policy *xfrm_policy_byid(u8 type, int dir, u32 id, int delete, int *err){ struct xfrm_policy *pol, *ret; struct hlist_head *chain; struct hlist_node *entry; *err = -ENOENT; if (xfrm_policy_id2dir(id) != dir) return NULL; *err = 0; write_lock_bh(&xfrm_policy_lock); chain = xfrm_policy_byidx + idx_hash(id); ret = NULL; hlist_for_each_entry(pol, entry, chain, byidx) { if (pol->type == type && pol->index == id) { xfrm_pol_hold(pol); if (delete) { *err = security_xfrm_policy_delete(pol); if (*err) { write_unlock_bh(&xfrm_policy_lock); return pol; } hlist_del(&pol->bydst); hlist_del(&pol->byidx); xfrm_policy_count[dir]--; } ret = pol; break; } } write_unlock_bh(&xfrm_policy_lock); if (ret && delete) { atomic_inc(&flow_cache_genid); xfrm_policy_kill(ret); } return ret;}EXPORT_SYMBOL(xfrm_policy_byid);#ifdef CONFIG_SECURITY_NETWORK_XFRMstatic inline intxfrm_policy_flush_secctx_check(u8 type, struct xfrm_audit *audit_info){ int dir, err = 0; for (dir = 0; dir < XFRM_POLICY_MAX; dir++) { struct xfrm_policy *pol; struct hlist_node *entry; int i; hlist_for_each_entry(pol, entry, &xfrm_policy_inexact[dir], bydst) { if (pol->type != type) continue; err = security_xfrm_policy_delete(pol); if (err) { xfrm_audit_policy_delete(pol, 0, audit_info->loginuid, audit_info->secid); return err; } } for (i = xfrm_policy_bydst[dir].hmask; i >= 0; i--) { hlist_for_each_entry(pol, entry, xfrm_policy_bydst[dir].table + i, bydst) { if (pol->type != type) continue; err = security_xfrm_policy_delete(pol); if (err) { xfrm_audit_policy_delete(pol, 0, audit_info->loginuid, audit_info->secid); return err; } } } } return err;}#elsestatic inline intxfrm_policy_flush_secctx_check(u8 type, struct xfrm_audit *audit_info){ return 0;}#endifint xfrm_policy_flush(u8 type, struct xfrm_audit *audit_info){ int dir, err = 0; write_lock_bh(&xfrm_policy_lock); err = xfrm_policy_flush_secctx_check(type, audit_info); if (err) goto out; for (dir = 0; dir < XFRM_POLICY_MAX; dir++) { struct xfrm_policy *pol; struct hlist_node *entry; int i, killed; killed = 0; again1: hlist_for_each_entry(pol, entry, &xfrm_policy_inexact[dir], bydst) { if (pol->type != type) continue; hlist_del(&pol->bydst); hlist_del(&pol->byidx); write_unlock_bh(&xfrm_policy_lock); xfrm_audit_policy_delete(pol, 1, audit_info->loginuid, audit_info->secid); xfrm_policy_kill(pol); killed++; write_lock_bh(&xfrm_policy_lock); goto again1; } for (i = xfrm_policy_bydst[dir].hmask; i >= 0; i--) { again2: hlist_for_each_entry(pol, entry, xfrm_policy_bydst[dir].table + i, bydst) { if (pol->type != type) continue; hlist_del(&pol->bydst); hlist_del(&pol->byidx); write_unlock_bh(&xfrm_policy_lock); xfrm_audit_policy_delete(pol, 1, audit_info->loginuid, audit_info->secid); xfrm_policy_kill(pol); killed++; write_lock_bh(&xfrm_policy_lock); goto again2; } } xfrm_policy_count[dir] -= killed; } atomic_inc(&flow_cache_genid);out: write_unlock_bh(&xfrm_policy_lock); return err;}EXPORT_SYMBOL(xfrm_policy_flush);int xfrm_policy_walk(u8 type, int (*func)(struct xfrm_policy *, int, int, void*), void *data){ struct xfrm_policy *pol, *last = NULL; struct hlist_node *entry; int dir, last_dir = 0, count, error; read_lock_bh(&xfrm_policy_lock); count = 0; for (dir = 0; dir < 2*XFRM_POLICY_MAX; dir++) { struct hlist_head *table = xfrm_policy_bydst[dir].table; int i; hlist_for_each_entry(pol, entry, &xfrm_policy_inexact[dir], bydst) { if (pol->type != type) continue; if (last) { error = func(last, last_dir % XFRM_POLICY_MAX, count, data); if (error) goto out; } last = pol; last_dir = dir; count++; } for (i = xfrm_policy_bydst[dir].hmask; i >= 0; i--) { hlist_for_each_entry(pol, entry, table + i, bydst) { if (pol->type != type) continue; if (last) { error = func(last, last_dir % XFRM_POLICY_MAX, count, data); if (error) goto out; } last = pol; last_dir = dir; count++; } } } if (count == 0) { error = -ENOENT; goto out; } error = func(last, last_dir % XFRM_POLICY_MAX, 0, data);out: read_unlock_bh(&xfrm_policy_lock); return error;}EXPORT_SYMBOL(xfrm_policy_walk);/* * Find policy to apply to this flow. * * Returns 0 if policy found, else an -errno. */static int xfrm_policy_match(struct xfrm_policy *pol, struct flowi *fl, u8 type, u16 family, int dir){ struct xfrm_selector *sel = &pol->selector; int match, ret = -ESRCH; if (pol->family != family || pol->type != type) return ret; match = xfrm_selector_match(sel, fl, family); if (match) ret = security_xfrm_policy_lookup(pol, fl->secid, dir); return ret;}static struct xfrm_policy *xfrm_policy_lookup_bytype(u8 type, struct flowi *fl, u16 family, u8 dir){ int err; struct xfrm_policy *pol, *ret; xfrm_address_t *daddr, *saddr; struct hlist_node *entry; struct hlist_head *chain; u32 priority = ~0U; daddr = xfrm_flowi_daddr(fl, family); saddr = xfrm_flowi_saddr(fl, family); if (unlikely(!daddr || !saddr)) return NULL; read_lock_bh(&xfrm_policy_lock); chain = policy_hash_direct(daddr, saddr, family, dir); ret = NULL; hlist_for_each_entry(pol, entry, chain, bydst) { err = xfrm_policy_match(pol, fl, type, family, dir); if (err) { if (err == -ESRCH) continue; else { ret = ERR_PTR(err); goto fail; } } else { ret = pol; priority = ret->priority; break; } } chain = &xfrm_policy_inexact[dir]; hlist_for_each_entry(pol, entry, chain, bydst) { err = xfrm_policy_match(pol, fl, type, family, dir); if (err) { if (err == -ESRCH) continue; else { ret = ERR_PTR(err); goto fail; } } else if (pol->priority < priority) { ret = pol; break; } } if (ret) xfrm_pol_hold(ret);fail: read_unlock_bh(&xfrm_policy_lock); return ret;}static int xfrm_policy_lookup(struct flowi *fl, u16 family, u8 dir, void **objp, atomic_t **obj_refp){ struct xfrm_policy *pol; int err = 0;#ifdef CONFIG_XFRM_SUB_POLICY pol = xfrm_policy_lookup_bytype(XFRM_POLICY_TYPE_SUB, fl, family, dir); if (IS_ERR(pol)) { err = PTR_ERR(pol); pol = NULL; } if (pol || err) goto end;#endif pol = xfrm_policy_lookup_bytype(XFRM_POLICY_TYPE_MAIN, fl, family, dir); if (IS_ERR(pol)) { err = PTR_ERR(pol); pol = NULL; }#ifdef CONFIG_XFRM_SUB_POLICYend:#endif if ((*objp = (void *) pol) != NULL) *obj_refp = &pol->refcnt; return err;}static inline int policy_to_flow_dir(int dir){ if (XFRM_POLICY_IN == FLOW_DIR_IN && XFRM_POLICY_OUT == FLOW_DIR_OUT && XFRM_POLICY_FWD == FLOW_DIR_FWD) return dir; switch (dir) { default: case XFRM_POLICY_IN: return FLOW_DIR_IN; case XFRM_POLICY_OUT: return FLOW_DIR_OUT; case XFRM_POLICY_FWD: return FLOW_DIR_FWD; }}static struct xfrm_policy *xfrm_sk_policy_lookup(struct sock *sk, int dir, struct flowi *fl){ struct xfrm_policy *pol; read_lock_bh(&xfrm_policy_lock); if ((pol = sk->sk_policy[dir]) != NULL) { int match = xfrm_selector_match(&pol->selector, fl, sk->sk_family); int err = 0; if (match) { err = security_xfrm_policy_lookup(pol, fl->secid, policy_to_flow_dir(dir)); if (!err) xfrm_pol_hold(pol); else if (err == -ESRCH) pol = NULL; else pol = ERR_PTR(err); } else pol = NULL; } read_unlock_bh(&xfrm_policy_lock); return pol;}static void __xfrm_policy_link(struct xfrm_policy *pol, int dir){ struct hlist_head *chain = policy_hash_bysel(&pol->selector, pol->family, dir); hlist_add_head(&pol->bydst, chain); hlist_add_head(&pol->byidx, xfrm_policy_byidx+idx_hash(pol->index)); xfrm_policy_count[dir]++; xfrm_pol_hold(pol); if (xfrm_bydst_should_resize(dir, NULL)) schedule_work(&xfrm_hash_work);}static struct xfrm_policy *__xfrm_policy_unlink(struct xfrm_policy *pol, int dir){ if (hlist_unhashed(&pol->bydst)) return NULL; hlist_del(&pol->bydst); hlist_del(&pol->byidx); xfrm_policy_count[dir]--; return pol;}int xfrm_policy_delete(struct xfrm_policy *pol, int dir){ write_lock_bh(&xfrm_policy_lock); pol = __xfrm_policy_unlink(pol, dir); write_unlock_bh(&xfrm_policy_lock); if (pol) { if (dir < XFRM_POLICY_MAX) atomic_inc(&flow_cache_genid); xfrm_policy_kill(pol); return 0; } return -ENOENT;}EXPORT_SYMBOL(xfrm_policy_delete);int xfrm_sk_policy_insert(struct sock *sk, int dir, struct xfrm_policy *pol){ struct xfrm_policy *old_pol;#ifdef CONFIG_XFRM_SUB_POLICY if (pol && pol->type != XFRM_POLICY_TYPE_MAIN) return -EINVAL;#endif write_lock_bh(&xfrm_policy_lock); old_pol = sk->sk_policy[dir]; sk->sk_policy[dir] = pol; if (pol) { pol->curlft.add_time = get_seconds(); pol->index = xfrm_gen_index(pol->type, XFRM_POLICY_MAX+dir); __xfrm_policy_link(pol, XFRM_POLICY_MAX+dir); } if (old_pol) __xfrm_policy_unlink(old_pol, XFRM_POLICY_MAX+dir); write_unlock_bh(&xfrm_policy_lock); if (old_pol) { xfrm_policy_kill(old_pol); } return 0;}static struct xfrm_policy *clone_policy(struct xfrm_policy *old, int dir){ struct xfrm_policy *newp = xfrm_policy_alloc(GFP_ATOMIC); if (newp) { newp->selector = old->selector; if (security_xfrm_policy_clone(old, newp)) { kfree(newp); return NULL; /* ENOMEM */ } newp->lft = old->lft; newp->curlft = old->curlft; newp->action = old->action; newp->flags = old->flags; newp->xfrm_nr = old->xfrm_nr; newp->index = old->index; newp->type = old->type; memcpy(newp->xfrm_vec, old->xfrm_vec, newp->xfrm_nr*sizeof(struct xfrm_tmpl)); write_lock_bh(&xfrm_policy_lock); __xfrm_policy_link(newp, XFRM_POLICY_MAX+dir); write_unlock_bh(&xfrm_policy_lock); xfrm_pol_put(newp); } return newp;}int __xfrm_sk_clone_policy(struct sock *sk){ struct xfrm_policy *p0 = sk->sk_policy[0], *p1 = sk->sk_policy[1]; sk->sk_policy[0] = sk->sk_policy[1] = NULL; if (p0 && (sk->sk_policy[0] = clone_policy(p0, 0)) == NULL) return -ENOMEM; if (p1 && (sk->sk_policy[1] = clone_policy(p1, 1)) == NULL) return -ENOMEM; return 0;}static intxfrm_get_saddr(xfrm_address_t *local, xfrm_address_t *remote, unsigned short family){ int err; struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family); if (unlikely(afinfo == NULL)) return -EINVAL; err = afinfo->get_saddr(local, remote); xfrm_policy_put_afinfo(afinfo); return err;}/* Resolve list of templates for the flow, given policy. */static intxfrm_tmpl_resolve_one(struct xfrm_policy *policy, struct flowi *fl, struct xfrm_state **xfrm, unsigned short family){ int nx; int i, error; xfrm_address_t *daddr = xfrm_flowi_daddr(fl, family); xfrm_address_t *saddr = xfrm_flowi_saddr(fl, family); xfrm_address_t tmp; for (nx=0, i = 0; i < policy->xfrm_nr; i++) { struct xfrm_state *x; xfrm_address_t *remote = daddr; xfrm_address_t *local = saddr; struct xfrm_tmpl *tmpl = &policy->xfrm_vec[i]; if (tmpl->mode == XFRM_MODE_TUNNEL || tmpl->mode == XFRM_MODE_BEET) { remote = &tmpl->id.daddr; local = &tmpl->saddr; family = tmpl->encap_family; if (xfrm_addr_any(local, family)) { error = xfrm_get_saddr(&tmp, remote, family); if (error) goto fail; local = &tmp; } } x = xfrm_state_find(remote, local, fl, tmpl, policy, &error, family); if (x && x->km.state == XFRM_STATE_VALID) { xfrm[nx++] = x; daddr = remote; saddr = local; continue; } if (x) { error = (x->km.state == XFRM_STATE_ERROR ? -EINVAL : -EAGAIN); xfrm_state_put(x); } if (!tmpl->optional) goto fail; } return nx;fail: for (nx--; nx>=0; nx--) xfrm_state_put(xfrm[nx]); return error;}static intxfrm_tmpl_resolve(struct xfrm_policy **pols, int npols, struct flowi *fl, struct xfrm_state **xfrm, unsigned short family){ struct xfrm_state *tp[XFRM_MAX_DEPTH]; struct xfrm_state **tpp = (npols > 1) ? tp : xfrm; int cnx = 0; int error; int ret; int i; for (i = 0; i < npols; i++) { if (cnx + pols[i]->xfrm_nr >= XFRM_MAX_DEPTH) { error = -ENOBUFS; goto fail; } ret = xfrm_tmpl_resolve_one(pols[i], fl, &tpp[cnx], family); if (ret < 0) { error = ret; goto fail; } else cnx += ret; } /* found states are sorted for outbound processing */ if (npols > 1) xfrm_state_sort(xfrm, tpp, cnx, family); return cnx; fail: for (cnx--; cnx>=0; cnx--) xfrm_state_put(tpp[cnx]);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -