X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=net%2Fxfrm%2Fxfrm_state.c;h=9880b792e6a52bbf4c4032f41468cf8b817c99b0;hb=061964fb988ca51087948975da66ff523b3a5852;hp=ee1e697539321d20de26511cf4927d4a7411eae6;hpb=61441ed4e498f8f74f25116a465a73ca7fa2ac72;p=linux-2.6 diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index ee1e697539..9880b792e6 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include "xfrm_hash.h" @@ -49,6 +50,7 @@ static DEFINE_SPINLOCK(xfrm_state_lock); * Main use is finding SA after policy selected tunnel or transport mode. * Also, it can be used by ah/esp icmp error handler to find offending SA. */ +static LIST_HEAD(xfrm_state_all); static struct hlist_head *xfrm_state_bydst __read_mostly; static struct hlist_head *xfrm_state_bysrc __read_mostly; static struct hlist_head *xfrm_state_byspi __read_mostly; @@ -60,6 +62,13 @@ static unsigned int xfrm_state_genid; static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family); static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo); +#ifdef CONFIG_AUDITSYSCALL +static void xfrm_audit_state_replay(struct xfrm_state *x, + struct sk_buff *skb, __be32 net_seq); +#else +#define xfrm_audit_state_replay(x, s, sq) do { ; } while (0) +#endif /* CONFIG_AUDITSYSCALL */ + static inline unsigned int xfrm_dst_hash(xfrm_address_t *daddr, xfrm_address_t *saddr, u32 reqid, @@ -203,14 +212,15 @@ static struct xfrm_state_afinfo *xfrm_state_lock_afinfo(unsigned int family) } static void xfrm_state_unlock_afinfo(struct xfrm_state_afinfo *afinfo) + __releases(xfrm_state_afinfo_lock) { write_unlock_bh(&xfrm_state_afinfo_lock); } -int xfrm_register_type(struct xfrm_type *type, unsigned short family) +int xfrm_register_type(const struct xfrm_type *type, unsigned short family) { struct xfrm_state_afinfo *afinfo = xfrm_state_lock_afinfo(family); - struct xfrm_type **typemap; + const struct xfrm_type **typemap; int err = 0; if (unlikely(afinfo == NULL)) @@ -226,10 +236,10 @@ int xfrm_register_type(struct xfrm_type *type, unsigned short family) } EXPORT_SYMBOL(xfrm_register_type); -int xfrm_unregister_type(struct xfrm_type *type, unsigned short family) +int xfrm_unregister_type(const struct xfrm_type *type, unsigned short family) { struct xfrm_state_afinfo *afinfo = xfrm_state_lock_afinfo(family); - struct xfrm_type **typemap; + const struct xfrm_type **typemap; int err = 0; if (unlikely(afinfo == NULL)) @@ -245,11 +255,11 @@ int xfrm_unregister_type(struct xfrm_type *type, unsigned short family) } EXPORT_SYMBOL(xfrm_unregister_type); -static struct xfrm_type *xfrm_get_type(u8 proto, unsigned short family) +static const struct xfrm_type *xfrm_get_type(u8 proto, unsigned short family) { struct xfrm_state_afinfo *afinfo; - struct xfrm_type **typemap; - struct xfrm_type *type; + const struct xfrm_type **typemap; + const struct xfrm_type *type; int modload_attempted = 0; retry: @@ -272,7 +282,7 @@ retry: return type; } -static void xfrm_put_type(struct xfrm_type *type) +static void xfrm_put_type(const struct xfrm_type *type) { module_put(type->owner); } @@ -484,7 +494,7 @@ expired: km_state_expired(x, 1, 0); xfrm_audit_state_delete(x, err ? 0 : 1, - audit_get_loginuid(current->audit_context), 0); + audit_get_loginuid(current), 0); out: spin_unlock(&x->lock); @@ -501,15 +511,13 @@ struct xfrm_state *xfrm_state_alloc(void) if (x) { atomic_set(&x->refcnt, 1); atomic_set(&x->tunnel_users, 0); + INIT_LIST_HEAD(&x->all); INIT_HLIST_NODE(&x->bydst); INIT_HLIST_NODE(&x->bysrc); INIT_HLIST_NODE(&x->byspi); - init_timer(&x->timer); - x->timer.function = xfrm_timer_handler; - x->timer.data = (unsigned long)x; - init_timer(&x->rtimer); - x->rtimer.function = xfrm_replay_timer_handler; - x->rtimer.data = (unsigned long)x; + setup_timer(&x->timer, xfrm_timer_handler, (unsigned long)x); + setup_timer(&x->rtimer, xfrm_replay_timer_handler, + (unsigned long)x); x->curlft.add_time = get_seconds(); x->lft.soft_byte_limit = XFRM_INF; x->lft.soft_packet_limit = XFRM_INF; @@ -527,6 +535,10 @@ void __xfrm_state_destroy(struct xfrm_state *x) { BUG_TRAP(x->km.state == XFRM_STATE_DEAD); + spin_lock_bh(&xfrm_state_lock); + list_del(&x->all); + spin_unlock_bh(&xfrm_state_lock); + spin_lock_bh(&xfrm_state_gc_lock); hlist_add_head(&x->bydst, &xfrm_state_gc_list); spin_unlock_bh(&xfrm_state_gc_lock); @@ -759,7 +771,7 @@ xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr, struct xfrm_policy *pol, int *err, unsigned short family) { - unsigned int h = xfrm_dst_hash(daddr, saddr, tmpl->reqid, family); + unsigned int h; struct hlist_node *entry; struct xfrm_state *x, *x0; int acquire_in_progress = 0; @@ -767,6 +779,7 @@ xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr, struct xfrm_state *best = NULL; spin_lock_bh(&xfrm_state_lock); + h = xfrm_dst_hash(daddr, saddr, tmpl->reqid, family); hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) { if (x->props.family == family && x->props.reqid == tmpl->reqid && @@ -868,11 +881,12 @@ struct xfrm_state * xfrm_stateonly_find(xfrm_address_t *daddr, xfrm_address_t *saddr, unsigned short family, u8 mode, u8 proto, u32 reqid) { - unsigned int h = xfrm_dst_hash(daddr, saddr, reqid, family); + unsigned int h; struct xfrm_state *rx = NULL, *x = NULL; struct hlist_node *entry; spin_lock(&xfrm_state_lock); + h = xfrm_dst_hash(daddr, saddr, reqid, family); hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) { if (x->props.family == family && x->props.reqid == reqid && @@ -901,6 +915,8 @@ static void __xfrm_state_insert(struct xfrm_state *x) x->genid = ++xfrm_state_genid; + list_add_tail(&x->all, &xfrm_state_all); + h = xfrm_dst_hash(&x->id.daddr, &x->props.saddr, x->props.reqid, x->props.family); hlist_add_head(&x->bydst, xfrm_state_bydst+h); @@ -1092,7 +1108,7 @@ out: EXPORT_SYMBOL(xfrm_state_add); #ifdef CONFIG_XFRM_MIGRATE -struct xfrm_state *xfrm_state_clone(struct xfrm_state *orig, int *errp) +static struct xfrm_state *xfrm_state_clone(struct xfrm_state *orig, int *errp) { int err = -ENOMEM; struct xfrm_state *x = xfrm_state_alloc(); @@ -1167,7 +1183,6 @@ struct xfrm_state *xfrm_state_clone(struct xfrm_state *orig, int *errp) kfree(x); return NULL; } -EXPORT_SYMBOL(xfrm_state_clone); /* xfrm_state_lock is held */ struct xfrm_state * xfrm_migrate_state_find(struct xfrm_migrate *m) @@ -1511,36 +1526,47 @@ unlock: } EXPORT_SYMBOL(xfrm_alloc_spi); -int xfrm_state_walk(u8 proto, int (*func)(struct xfrm_state *, int, void*), +int xfrm_state_walk(struct xfrm_state_walk *walk, + int (*func)(struct xfrm_state *, int, void*), void *data) { - int i; - struct xfrm_state *x, *last = NULL; - struct hlist_node *entry; - int count = 0; + struct xfrm_state *old, *x, *last = NULL; int err = 0; + if (walk->state == NULL && walk->count != 0) + return 0; + + old = x = walk->state; + walk->state = NULL; spin_lock_bh(&xfrm_state_lock); - for (i = 0; i <= xfrm_state_hmask; i++) { - hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) { - if (!xfrm_id_proto_match(x->id.proto, proto)) - continue; - if (last) { - err = func(last, count, data); - if (err) - goto out; + if (x == NULL) + x = list_first_entry(&xfrm_state_all, struct xfrm_state, all); + list_for_each_entry_from(x, &xfrm_state_all, all) { + if (x->km.state == XFRM_STATE_DEAD) + continue; + if (!xfrm_id_proto_match(x->id.proto, walk->proto)) + continue; + if (last) { + err = func(last, walk->count, data); + if (err) { + xfrm_state_hold(last); + walk->state = last; + goto out; } - last = x; - count++; } + last = x; + walk->count++; } - if (count == 0) { + if (walk->count == 0) { err = -ENOENT; goto out; } - err = func(last, 0, data); + if (last) + err = func(last, 0, data); out: spin_unlock_bh(&xfrm_state_lock); + if (old != NULL) + xfrm_state_put(old); return err; } EXPORT_SYMBOL(xfrm_state_walk); @@ -1609,13 +1635,14 @@ static void xfrm_replay_timer_handler(unsigned long data) spin_unlock(&x->lock); } -int xfrm_replay_check(struct xfrm_state *x, __be32 net_seq) +int xfrm_replay_check(struct xfrm_state *x, + struct sk_buff *skb, __be32 net_seq) { u32 diff; u32 seq = ntohl(net_seq); if (unlikely(seq == 0)) - return -EINVAL; + goto err; if (likely(seq > x->replay.seq)) return 0; @@ -1624,16 +1651,19 @@ int xfrm_replay_check(struct xfrm_state *x, __be32 net_seq) if (diff >= min_t(unsigned int, x->props.replay_window, sizeof(x->replay.bitmap) * 8)) { x->stats.replay_window++; - return -EINVAL; + goto err; } if (x->replay.bitmap & (1U << diff)) { x->stats.replay++; - return -EINVAL; + goto err; } return 0; + +err: + xfrm_audit_state_replay(x, skb, net_seq); + return -EINVAL; } -EXPORT_SYMBOL(xfrm_replay_check); void xfrm_replay_advance(struct xfrm_state *x, __be32 net_seq) { @@ -1655,9 +1685,8 @@ void xfrm_replay_advance(struct xfrm_state *x, __be32 net_seq) if (xfrm_aevent_is_on()) xfrm_replay_notify(x, XFRM_REPLAY_UPDATE); } -EXPORT_SYMBOL(xfrm_replay_advance); -static struct list_head xfrm_km_list = LIST_HEAD_INIT(xfrm_km_list); +static LIST_HEAD(xfrm_km_list); static DEFINE_RWLOCK(xfrm_km_lock); void km_policy_notify(struct xfrm_policy *xp, int dir, struct km_event *c) @@ -1749,6 +1778,7 @@ void km_policy_expired(struct xfrm_policy *pol, int dir, int hard, u32 pid) } EXPORT_SYMBOL(km_policy_expired); +#ifdef CONFIG_XFRM_MIGRATE int km_migrate(struct xfrm_selector *sel, u8 dir, u8 type, struct xfrm_migrate *m, int num_migrate) { @@ -1768,6 +1798,7 @@ int km_migrate(struct xfrm_selector *sel, u8 dir, u8 type, return err; } EXPORT_SYMBOL(km_migrate); +#endif int km_report(u8 proto, struct xfrm_selector *sel, xfrm_address_t *addr) { @@ -1895,6 +1926,7 @@ static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family) } static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo) + __releases(xfrm_state_afinfo_lock) { read_unlock(&xfrm_state_afinfo_lock); } @@ -1994,73 +2026,172 @@ void __init xfrm_state_init(void) } #ifdef CONFIG_AUDITSYSCALL -static inline void xfrm_audit_common_stateinfo(struct xfrm_state *x, - struct audit_buffer *audit_buf) +static void xfrm_audit_helper_sainfo(struct xfrm_state *x, + struct audit_buffer *audit_buf) { - if (x->security) + struct xfrm_sec_ctx *ctx = x->security; + u32 spi = ntohl(x->id.spi); + + if (ctx) audit_log_format(audit_buf, " sec_alg=%u sec_doi=%u sec_obj=%s", - x->security->ctx_alg, x->security->ctx_doi, - x->security->ctx_str); + ctx->ctx_alg, ctx->ctx_doi, ctx->ctx_str); switch(x->props.family) { case AF_INET: - audit_log_format(audit_buf, " src=%u.%u.%u.%u dst=%u.%u.%u.%u", + audit_log_format(audit_buf, + " src=" NIPQUAD_FMT " dst=" NIPQUAD_FMT, NIPQUAD(x->props.saddr.a4), NIPQUAD(x->id.daddr.a4)); break; case AF_INET6: - { - struct in6_addr saddr6, daddr6; - - memcpy(&saddr6, x->props.saddr.a6, - sizeof(struct in6_addr)); - memcpy(&daddr6, x->id.daddr.a6, - sizeof(struct in6_addr)); - audit_log_format(audit_buf, - " src=" NIP6_FMT " dst=" NIP6_FMT, - NIP6(saddr6), NIP6(daddr6)); - } + audit_log_format(audit_buf, + " src=" NIP6_FMT " dst=" NIP6_FMT, + NIP6(*(struct in6_addr *)x->props.saddr.a6), + NIP6(*(struct in6_addr *)x->id.daddr.a6)); + break; + } + + audit_log_format(audit_buf, " spi=%u(0x%x)", spi, spi); +} + +static void xfrm_audit_helper_pktinfo(struct sk_buff *skb, u16 family, + struct audit_buffer *audit_buf) +{ + struct iphdr *iph4; + struct ipv6hdr *iph6; + + switch (family) { + case AF_INET: + iph4 = ip_hdr(skb); + audit_log_format(audit_buf, + " src=" NIPQUAD_FMT " dst=" NIPQUAD_FMT, + NIPQUAD(iph4->saddr), + NIPQUAD(iph4->daddr)); + break; + case AF_INET6: + iph6 = ipv6_hdr(skb); + audit_log_format(audit_buf, + " src=" NIP6_FMT " dst=" NIP6_FMT + " flowlbl=0x%x%x%x", + NIP6(iph6->saddr), + NIP6(iph6->daddr), + iph6->flow_lbl[0] & 0x0f, + iph6->flow_lbl[1], + iph6->flow_lbl[2]); break; } } -void -xfrm_audit_state_add(struct xfrm_state *x, int result, u32 auid, u32 sid) +void xfrm_audit_state_add(struct xfrm_state *x, int result, + u32 auid, u32 secid) +{ + struct audit_buffer *audit_buf; + + audit_buf = xfrm_audit_start("SAD-add"); + if (audit_buf == NULL) + return; + xfrm_audit_helper_usrinfo(auid, secid, audit_buf); + xfrm_audit_helper_sainfo(x, audit_buf); + audit_log_format(audit_buf, " res=%u", result); + audit_log_end(audit_buf); +} +EXPORT_SYMBOL_GPL(xfrm_audit_state_add); + +void xfrm_audit_state_delete(struct xfrm_state *x, int result, + u32 auid, u32 secid) { struct audit_buffer *audit_buf; - u32 spi; - extern int audit_enabled; - if (audit_enabled == 0) + audit_buf = xfrm_audit_start("SAD-delete"); + if (audit_buf == NULL) return; - audit_buf = xfrm_audit_start(auid, sid); + xfrm_audit_helper_usrinfo(auid, secid, audit_buf); + xfrm_audit_helper_sainfo(x, audit_buf); + audit_log_format(audit_buf, " res=%u", result); + audit_log_end(audit_buf); +} +EXPORT_SYMBOL_GPL(xfrm_audit_state_delete); + +void xfrm_audit_state_replay_overflow(struct xfrm_state *x, + struct sk_buff *skb) +{ + struct audit_buffer *audit_buf; + u32 spi; + + audit_buf = xfrm_audit_start("SA-replay-overflow"); if (audit_buf == NULL) return; - audit_log_format(audit_buf, " op=SAD-add res=%u",result); - xfrm_audit_common_stateinfo(x, audit_buf); + xfrm_audit_helper_pktinfo(skb, x->props.family, audit_buf); + /* don't record the sequence number because it's inherent in this kind + * of audit message */ spi = ntohl(x->id.spi); audit_log_format(audit_buf, " spi=%u(0x%x)", spi, spi); audit_log_end(audit_buf); } -EXPORT_SYMBOL_GPL(xfrm_audit_state_add); +EXPORT_SYMBOL_GPL(xfrm_audit_state_replay_overflow); -void -xfrm_audit_state_delete(struct xfrm_state *x, int result, u32 auid, u32 sid) +static void xfrm_audit_state_replay(struct xfrm_state *x, + struct sk_buff *skb, __be32 net_seq) { struct audit_buffer *audit_buf; u32 spi; - extern int audit_enabled; - if (audit_enabled == 0) - return; - audit_buf = xfrm_audit_start(auid, sid); + audit_buf = xfrm_audit_start("SA-replayed-pkt"); if (audit_buf == NULL) return; - audit_log_format(audit_buf, " op=SAD-delete res=%u",result); - xfrm_audit_common_stateinfo(x, audit_buf); + xfrm_audit_helper_pktinfo(skb, x->props.family, audit_buf); spi = ntohl(x->id.spi); - audit_log_format(audit_buf, " spi=%u(0x%x)", spi, spi); + audit_log_format(audit_buf, " spi=%u(0x%x) seqno=%u", + spi, spi, ntohl(net_seq)); audit_log_end(audit_buf); } -EXPORT_SYMBOL_GPL(xfrm_audit_state_delete); + +void xfrm_audit_state_notfound_simple(struct sk_buff *skb, u16 family) +{ + struct audit_buffer *audit_buf; + + audit_buf = xfrm_audit_start("SA-notfound"); + if (audit_buf == NULL) + return; + xfrm_audit_helper_pktinfo(skb, family, audit_buf); + audit_log_end(audit_buf); +} +EXPORT_SYMBOL_GPL(xfrm_audit_state_notfound_simple); + +void xfrm_audit_state_notfound(struct sk_buff *skb, u16 family, + __be32 net_spi, __be32 net_seq) +{ + struct audit_buffer *audit_buf; + u32 spi; + + audit_buf = xfrm_audit_start("SA-notfound"); + if (audit_buf == NULL) + return; + xfrm_audit_helper_pktinfo(skb, family, audit_buf); + spi = ntohl(net_spi); + audit_log_format(audit_buf, " spi=%u(0x%x) seqno=%u", + spi, spi, ntohl(net_seq)); + audit_log_end(audit_buf); +} +EXPORT_SYMBOL_GPL(xfrm_audit_state_notfound); + +void xfrm_audit_state_icvfail(struct xfrm_state *x, + struct sk_buff *skb, u8 proto) +{ + struct audit_buffer *audit_buf; + __be32 net_spi; + __be32 net_seq; + + audit_buf = xfrm_audit_start("SA-icv-failure"); + if (audit_buf == NULL) + return; + xfrm_audit_helper_pktinfo(skb, x->props.family, audit_buf); + if (xfrm_parse_spi(skb, proto, &net_spi, &net_seq) == 0) { + u32 spi = ntohl(net_spi); + audit_log_format(audit_buf, " spi=%u(0x%x) seqno=%u", + spi, spi, ntohl(net_seq)); + } + audit_log_end(audit_buf); +} +EXPORT_SYMBOL_GPL(xfrm_audit_state_icvfail); #endif /* CONFIG_AUDITSYSCALL */