X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=net%2Fxfrm%2Fxfrm_policy.c;h=47219f98053f86217027245dc81b42fbdf4d57e5;hb=f9166e736e516a4b1de16577b5428afd0cffe325;hp=3d516d57b5b2dc70538a9b74b969662b95670c7b;hpb=815f4e57e9fc67456624ecde0515a901368c78d2;p=linux-2.6 diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 3d516d57b5..47219f9805 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -24,14 +24,23 @@ #include #include #include +#include #include #include #include +#ifdef CONFIG_XFRM_STATISTICS +#include +#endif #include "xfrm_hash.h" int sysctl_xfrm_larval_drop __read_mostly; +#ifdef CONFIG_XFRM_STATISTICS +DEFINE_SNMP_STAT(struct linux_xfrm_mib, xfrm_statistics) __read_mostly; +EXPORT_SYMBOL(xfrm_statistics); +#endif + DEFINE_MUTEX(xfrm_cfg_mutex); EXPORT_SYMBOL(xfrm_cfg_mutex); @@ -212,7 +221,7 @@ EXPORT_SYMBOL(xfrm_policy_alloc); /* Destroy xfrm_policy: descendant resources must be released to this moment. */ -void __xfrm_policy_destroy(struct xfrm_policy *policy) +void xfrm_policy_destroy(struct xfrm_policy *policy) { BUG_ON(!policy->dead); @@ -224,7 +233,7 @@ void __xfrm_policy_destroy(struct xfrm_policy *policy) security_xfrm_policy_free(policy); kfree(policy); } -EXPORT_SYMBOL(__xfrm_policy_destroy); +EXPORT_SYMBOL(xfrm_policy_destroy); static void xfrm_policy_gc_kill(struct xfrm_policy *policy) { @@ -1266,6 +1275,23 @@ static inline struct xfrm_dst *xfrm_alloc_dst(int family) return xdst; } +static inline int xfrm_init_path(struct xfrm_dst *path, struct dst_entry *dst, + int nfheader_len) +{ + struct xfrm_policy_afinfo *afinfo = + xfrm_policy_get_afinfo(dst->ops->family); + int err; + + if (!afinfo) + return -EINVAL; + + err = afinfo->init_path(path, dst, nfheader_len); + + xfrm_policy_put_afinfo(afinfo); + + return err; +} + static inline int xfrm_fill_dst(struct xfrm_dst *xdst, struct net_device *dev) { struct xfrm_policy_afinfo *afinfo = @@ -1298,6 +1324,7 @@ static struct dst_entry *xfrm_bundle_create(struct xfrm_policy *policy, int i = 0; int err; int header_len = 0; + int nfheader_len = 0; int trailer_len = 0; int tos; int family = policy->selector.family; @@ -1352,6 +1379,8 @@ static struct dst_entry *xfrm_bundle_create(struct xfrm_policy *policy, dst_prev = dst1; header_len += xfrm[i]->props.header_len; + if (xfrm[i]->type->flags & XFRM_TYPE_NON_FRAGMENT) + nfheader_len += xfrm[i]->props.header_len; trailer_len += xfrm[i]->props.trailer_len; } @@ -1366,6 +1395,7 @@ static struct dst_entry *xfrm_bundle_create(struct xfrm_policy *policy, /* Copy neighbout for reachability confirmation */ dst0->neighbour = neigh_clone(dst->neighbour); + xfrm_init_path((struct xfrm_dst *)dst0, dst, nfheader_len); xfrm_init_pmtu(dst_prev); for (dst_prev = dst0; dst_prev != dst; dst_prev = dst_prev->child) { @@ -1465,36 +1495,46 @@ restart: if (sk && sk->sk_policy[XFRM_POLICY_OUT]) { policy = xfrm_sk_policy_lookup(sk, XFRM_POLICY_OUT, fl); err = PTR_ERR(policy); - if (IS_ERR(policy)) + if (IS_ERR(policy)) { + XFRM_INC_STATS(LINUX_MIB_XFRMOUTPOLERROR); goto dropdst; + } } if (!policy) { /* To accelerate a bit... */ if ((dst_orig->flags & DST_NOXFRM) || !xfrm_policy_count[XFRM_POLICY_OUT]) - return 0; + goto nopol; policy = flow_cache_lookup(fl, dst_orig->ops->family, dir, xfrm_policy_lookup); err = PTR_ERR(policy); - if (IS_ERR(policy)) + if (IS_ERR(policy)) { + XFRM_INC_STATS(LINUX_MIB_XFRMOUTPOLERROR); goto dropdst; + } } if (!policy) - return 0; + goto nopol; family = dst_orig->ops->family; - policy->curlft.use_time = get_seconds(); pols[0] = policy; npols ++; xfrm_nr += pols[0]->xfrm_nr; + err = -ENOENT; + if ((flags & XFRM_LOOKUP_ICMP) && !(policy->flags & XFRM_POLICY_ICMP)) + goto error; + + policy->curlft.use_time = get_seconds(); + switch (policy->action) { default: case XFRM_POLICY_BLOCK: /* Prohibit the flow */ + XFRM_INC_STATS(LINUX_MIB_XFRMOUTPOLBLOCK); err = -EPERM; goto error; @@ -1514,6 +1554,7 @@ restart: */ dst = xfrm_find_bundle(fl, policy, family); if (IS_ERR(dst)) { + XFRM_INC_STATS(LINUX_MIB_XFRMOUTBUNDLECHECKERROR); err = PTR_ERR(dst); goto error; } @@ -1528,10 +1569,12 @@ restart: XFRM_POLICY_OUT); if (pols[1]) { if (IS_ERR(pols[1])) { + XFRM_INC_STATS(LINUX_MIB_XFRMOUTPOLERROR); err = PTR_ERR(pols[1]); goto error; } if (pols[1]->action == XFRM_POLICY_BLOCK) { + XFRM_INC_STATS(LINUX_MIB_XFRMOUTPOLBLOCK); err = -EPERM; goto error; } @@ -1562,6 +1605,7 @@ restart: /* EREMOTE tells the caller to generate * a one-shot blackhole route. */ + XFRM_INC_STATS(LINUX_MIB_XFRMOUTNOSTATES); xfrm_pol_put(policy); return -EREMOTE; } @@ -1577,6 +1621,7 @@ restart: nx = xfrm_tmpl_resolve(pols, npols, fl, xfrm, family); if (nx == -EAGAIN && signal_pending(current)) { + XFRM_INC_STATS(LINUX_MIB_XFRMOUTNOSTATES); err = -ERESTART; goto error; } @@ -1587,8 +1632,10 @@ restart: } err = nx; } - if (err < 0) + if (err < 0) { + XFRM_INC_STATS(LINUX_MIB_XFRMOUTNOSTATES); goto error; + } } if (nx == 0) { /* Flow passes not transformed. */ @@ -1598,8 +1645,10 @@ restart: dst = xfrm_bundle_create(policy, xfrm, nx, fl, dst_orig); err = PTR_ERR(dst); - if (IS_ERR(dst)) + if (IS_ERR(dst)) { + XFRM_INC_STATS(LINUX_MIB_XFRMOUTBUNDLEGENERROR); goto error; + } for (pi = 0; pi < npols; pi++) { read_lock_bh(&pols[pi]->lock); @@ -1618,6 +1667,10 @@ restart: if (dst) dst_free(dst); + if (pol_dead) + XFRM_INC_STATS(LINUX_MIB_XFRMOUTPOLDEAD); + else + XFRM_INC_STATS(LINUX_MIB_XFRMOUTBUNDLECHECKERROR); err = -EHOSTUNREACH; goto error; } @@ -1630,6 +1683,7 @@ restart: write_unlock_bh(&policy->lock); if (dst) dst_free(dst); + XFRM_INC_STATS(LINUX_MIB_XFRMOUTBUNDLECHECKERROR); goto error; } @@ -1649,6 +1703,12 @@ dropdst: dst_release(dst_orig); *dst_p = NULL; return err; + +nopol: + err = -ENOENT; + if (flags & XFRM_LOOKUP_ICMP) + goto dropdst; + return 0; } EXPORT_SYMBOL(__xfrm_lookup); @@ -1732,8 +1792,8 @@ xfrm_policy_ok(struct xfrm_tmpl *tmpl, struct sec_path *sp, int start, return start; } -int -xfrm_decode_session(struct sk_buff *skb, struct flowi *fl, unsigned short family) +int __xfrm_decode_session(struct sk_buff *skb, struct flowi *fl, + unsigned int family, int reverse) { struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family); int err; @@ -1741,12 +1801,12 @@ xfrm_decode_session(struct sk_buff *skb, struct flowi *fl, unsigned short family if (unlikely(afinfo == NULL)) return -EAFNOSUPPORT; - afinfo->decode_session(skb, fl); + afinfo->decode_session(skb, fl, reverse); err = security_xfrm_decode_session(skb, &fl->secid); xfrm_policy_put_afinfo(afinfo); return err; } -EXPORT_SYMBOL(xfrm_decode_session); +EXPORT_SYMBOL(__xfrm_decode_session); static inline int secpath_has_nontransport(struct sec_path *sp, int k, int *idxp) { @@ -1768,12 +1828,20 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb, int npols = 0; int xfrm_nr; int pi; + int reverse; struct flowi fl; - u8 fl_dir = policy_to_flow_dir(dir); + u8 fl_dir; int xerr_idx = -1; - if (xfrm_decode_session(skb, &fl, family) < 0) + reverse = dir & ~XFRM_POLICY_MASK; + dir &= XFRM_POLICY_MASK; + fl_dir = policy_to_flow_dir(dir); + + if (__xfrm_decode_session(skb, &fl, family, reverse) < 0) { + XFRM_INC_STATS(LINUX_MIB_XFRMINHDRERROR); return 0; + } + nf_nat_decode_session(skb, &fl, family); /* First, check used SA against their selectors. */ @@ -1782,28 +1850,35 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb, for (i=skb->sp->len-1; i>=0; i--) { struct xfrm_state *x = skb->sp->xvec[i]; - if (!xfrm_selector_match(&x->sel, &fl, family)) + if (!xfrm_selector_match(&x->sel, &fl, family)) { + XFRM_INC_STATS(LINUX_MIB_XFRMINSTATEMISMATCH); return 0; + } } } pol = NULL; if (sk && sk->sk_policy[dir]) { pol = xfrm_sk_policy_lookup(sk, dir, &fl); - if (IS_ERR(pol)) + if (IS_ERR(pol)) { + XFRM_INC_STATS(LINUX_MIB_XFRMINPOLERROR); return 0; + } } if (!pol) pol = flow_cache_lookup(&fl, family, fl_dir, xfrm_policy_lookup); - if (IS_ERR(pol)) + if (IS_ERR(pol)) { + XFRM_INC_STATS(LINUX_MIB_XFRMINPOLERROR); return 0; + } if (!pol) { if (skb->sp && secpath_has_nontransport(skb->sp, 0, &xerr_idx)) { xfrm_secpath_reject(xerr_idx, skb, &fl); + XFRM_INC_STATS(LINUX_MIB_XFRMINNOPOLS); return 0; } return 1; @@ -1819,8 +1894,10 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb, &fl, family, XFRM_POLICY_IN); if (pols[1]) { - if (IS_ERR(pols[1])) + if (IS_ERR(pols[1])) { + XFRM_INC_STATS(LINUX_MIB_XFRMINPOLERROR); return 0; + } pols[1]->curlft.use_time = get_seconds(); npols ++; } @@ -1841,10 +1918,14 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb, for (pi = 0; pi < npols; pi++) { if (pols[pi] != pol && - pols[pi]->action != XFRM_POLICY_ALLOW) + pols[pi]->action != XFRM_POLICY_ALLOW) { + XFRM_INC_STATS(LINUX_MIB_XFRMINPOLBLOCK); goto reject; - if (ti + pols[pi]->xfrm_nr >= XFRM_MAX_DEPTH) + } + if (ti + pols[pi]->xfrm_nr >= XFRM_MAX_DEPTH) { + XFRM_INC_STATS(LINUX_MIB_XFRMINBUFFERERROR); goto reject_error; + } for (i = 0; i < pols[pi]->xfrm_nr; i++) tpp[ti++] = &pols[pi]->xfrm_vec[i]; } @@ -1866,16 +1947,20 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb, if (k < -1) /* "-2 - errored_index" returned */ xerr_idx = -(2+k); + XFRM_INC_STATS(LINUX_MIB_XFRMINTMPLMISMATCH); goto reject; } } - if (secpath_has_nontransport(sp, k, &xerr_idx)) + if (secpath_has_nontransport(sp, k, &xerr_idx)) { + XFRM_INC_STATS(LINUX_MIB_XFRMINTMPLMISMATCH); goto reject; + } xfrm_pols_put(pols, npols); return 1; } + XFRM_INC_STATS(LINUX_MIB_XFRMINPOLBLOCK); reject: xfrm_secpath_reject(xerr_idx, skb, &fl); @@ -1889,8 +1974,11 @@ int __xfrm_route_forward(struct sk_buff *skb, unsigned short family) { struct flowi fl; - if (xfrm_decode_session(skb, &fl, family) < 0) + if (xfrm_decode_session(skb, &fl, family) < 0) { + /* XXX: we should have something like FWDHDRERROR here. */ + XFRM_INC_STATS(LINUX_MIB_XFRMINHDRERROR); return 0; + } return xfrm_lookup(&skb->dst, &fl, NULL, 0) == 0; } @@ -2221,6 +2309,16 @@ static struct notifier_block xfrm_dev_notifier = { 0 }; +#ifdef CONFIG_XFRM_STATISTICS +static int __init xfrm_statistics_init(void) +{ + if (snmp_mib_init((void **)xfrm_statistics, + sizeof(struct linux_xfrm_mib)) < 0) + return -ENOMEM; + return 0; +} +#endif + static void __init xfrm_policy_init(void) { unsigned int hmask, sz; @@ -2257,14 +2355,20 @@ static void __init xfrm_policy_init(void) void __init xfrm_init(void) { +#ifdef CONFIG_XFRM_STATISTICS + xfrm_statistics_init(); +#endif xfrm_state_init(); xfrm_policy_init(); xfrm_input_init(); +#ifdef CONFIG_XFRM_STATISTICS + xfrm_proc_init(); +#endif } #ifdef CONFIG_AUDITSYSCALL -static inline void xfrm_audit_common_policyinfo(struct xfrm_policy *xp, - struct audit_buffer *audit_buf) +static void xfrm_audit_common_policyinfo(struct xfrm_policy *xp, + struct audit_buffer *audit_buf) { struct xfrm_sec_ctx *ctx = xp->security; struct xfrm_selector *sel = &xp->selector; @@ -2301,35 +2405,31 @@ static inline void xfrm_audit_common_policyinfo(struct xfrm_policy *xp, } } -void -xfrm_audit_policy_add(struct xfrm_policy *xp, int result, u32 auid, u32 sid) +void xfrm_audit_policy_add(struct xfrm_policy *xp, int result, + u32 auid, u32 secid) { struct audit_buffer *audit_buf; - extern int audit_enabled; - if (audit_enabled == 0) - return; - audit_buf = xfrm_audit_start(auid, sid); + audit_buf = xfrm_audit_start("SPD-add"); if (audit_buf == NULL) return; - audit_log_format(audit_buf, " op=SPD-add res=%u", result); + xfrm_audit_helper_usrinfo(auid, secid, audit_buf); + audit_log_format(audit_buf, " res=%u", result); xfrm_audit_common_policyinfo(xp, audit_buf); audit_log_end(audit_buf); } EXPORT_SYMBOL_GPL(xfrm_audit_policy_add); -void -xfrm_audit_policy_delete(struct xfrm_policy *xp, int result, u32 auid, u32 sid) +void xfrm_audit_policy_delete(struct xfrm_policy *xp, int result, + u32 auid, u32 secid) { struct audit_buffer *audit_buf; - extern int audit_enabled; - if (audit_enabled == 0) - return; - audit_buf = xfrm_audit_start(auid, sid); + audit_buf = xfrm_audit_start("SPD-delete"); if (audit_buf == NULL) return; - audit_log_format(audit_buf, " op=SPD-delete res=%u", result); + xfrm_audit_helper_usrinfo(auid, secid, audit_buf); + audit_log_format(audit_buf, " res=%u", result); xfrm_audit_common_policyinfo(xp, audit_buf); audit_log_end(audit_buf); }