diff --git a/include/linux/fib_rules.h b/include/linux/fib_rules.h index 8270aac..2bbfa87 100644 --- a/include/linux/fib_rules.h +++ b/include/linux/fib_rules.h @@ -5,8 +5,11 @@ #include /* rule is permanent, and cannot be deleted */ -#define FIB_RULE_PERMANENT 1 -#define FIB_RULE_INVERT 2 +#define FIB_RULE_PERMANENT 0x00000001 +#define FIB_RULE_INVERT 0x00000002 + +/* try to find source address in routing lookups */ +#define FIB_RULE_FIND_SADDR 0x00010000 struct fib_rule_hdr { diff --git a/include/linux/snmp.h b/include/linux/snmp.h index 854aa6b..14098de 100644 --- a/include/linux/snmp.h +++ b/include/linux/snmp.h @@ -232,4 +232,30 @@ enum __LINUX_MIB_MAX }; +/* xfrm mib definitions */ +enum +{ + XFRM_MIB_NUM = 0, + XFRM_MIB_INERROR, /* XfrmInError */ + XFRM_MIB_INHDRERROR, /* XfrmInHdrError */ + XFRM_MIB_INSTATEPROTOERROR, /* XfrmInStateProtoError */ + XFRM_MIB_INSTATEMODEERROR, /* XfrmInStateModeError */ + XFRM_MIB_INSEQOUTOFWINDOW, /* XfrmInSeqOutOfWindow */ + XFRM_MIB_INSTATEEXPIRED, /* XfrmInStateExpired */ + XFRM_MIB_INSTATEINVALID, /* XfrmInStateInvalid */ + XFRM_MIB_INNOSTATES, /* XfrmInNoStates */ + XFRM_MIB_INTMPLMISMATCH, /* XfrmInTmplMismatch */ + XFRM_MIB_INPOLBLOCK, /* XfrmInPolBlock */ + XFRM_MIB_INNOPOLS, /* XfrmInNoPols */ + XFRM_MIB_OUTERROR, /* XfrmOutError */ + XFRM_MIB_OUTLENGTHERROR, /* XfrmOutLengthError */ + XFRM_MIB_OUTSTATEPROTOERROR, /* XfrmOutStateProtoError */ + XFRM_MIB_OUTSTATEMODEERROR, /* XfrmOutStateModeError */ + XFRM_MIB_OUTSTATEEXPIRED, /* XfrmOutStateExpired */ + XFRM_MIB_OUTNOSTATES, /* XfrmOutNoStates */ + XFRM_MIB_OUTBUNDLEERROR, /* XfrmOutBundleError */ + XFRM_MIB_OUTPOLBLOCK, /* XfrmOutPolBlock */ + __XFRM_MIB_MAX +}; + #endif /* _LINUX_SNMP_H */ diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index 2c5fb38..9b403d5 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -438,6 +438,7 @@ enum NET_CIPSOV4_RBM_STRICTVALID=121, NET_TCP_AVAIL_CONG_CONTROL=122, NET_TCP_ALLOWED_CONG_CONTROL=123, + NET_IPV4_TUNNEL_SEND_ICMP_ERROR=124, }; enum { @@ -541,6 +542,7 @@ enum { NET_IPV6_IP6FRAG_TIME=23, NET_IPV6_IP6FRAG_SECRET_INTERVAL=24, NET_IPV6_MLD_MAX_MSF=25, + NET_IPV6_TUNNEL_SEND_ICMP_ERROR=26, }; enum { diff --git a/include/linux/xfrm.h b/include/linux/xfrm.h index 15ca89e..931ab6e 100644 --- a/include/linux/xfrm.h +++ b/include/linux/xfrm.h @@ -315,7 +315,8 @@ struct xfrm_userpolicy_info { #define XFRM_POLICY_ALLOW 0 #define XFRM_POLICY_BLOCK 1 __u8 flags; -#define XFRM_POLICY_LOCALOK 1 /* Allow user to override global policy */ +#define XFRM_POLICY_LOCALOK 0x01 /* Allow user to override global policy */ +#define XFRM_POLICY_X_MULTILOCAL 0x02 /* Exclude inbound flow with multilocal state */ __u8 share; }; diff --git a/include/net/flow.h b/include/net/flow.h index ce4b10d..1dce23a 100644 --- a/include/net/flow.h +++ b/include/net/flow.h @@ -49,6 +49,7 @@ struct flowi { __u8 proto; __u8 flags; #define FLOWI_FLAG_MULTIPATHOLDROUTE 0x01 +#define FLOWI_FLAG_MULTILOCAL 0x02 union { struct { __be16 sport; @@ -97,4 +98,10 @@ extern void *flow_cache_lookup(struct flowi *key, u16 family, u8 dir, extern void flow_cache_flush(void); extern atomic_t flow_cache_genid; +static inline int flow_cache_uli_match(struct flowi *fl1, struct flowi *fl2) +{ + return (fl1->proto == fl2->proto && + !memcmp(&fl1->uli_u, &fl2->uli_u, sizeof(fl1->uli_u))); +} + #endif diff --git a/include/net/snmp.h b/include/net/snmp.h index 464970e..a4519e9 100644 --- a/include/net/snmp.h +++ b/include/net/snmp.h @@ -106,8 +106,14 @@ struct linux_mib { unsigned long mibs[LINUX_MIB_MAX]; }; +/* Xfrm */ +#define XFRM_MIB_MAX __XFRM_MIB_MAX +struct xfrm_mib { + unsigned long mibs[XFRM_MIB_MAX]; +}; -/* + +/* * FIXME: On x86 and some other CPUs the split into user and softirq parts * is not needed because addl $1,memory is atomic against interrupts (but * atomic_inc would be overkill because of the lock cycles). Wants new diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 5a00aa8..29755ba 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -23,6 +23,17 @@ #define MODULE_ALIAS_XFRM_MODE(family, encap) \ MODULE_ALIAS("xfrm-mode-" __stringify(family) "-" __stringify(encap)) +#ifdef CONFIG_XFRM_STATISTICS +DECLARE_SNMP_STAT(struct xfrm_mib, xfrm_statistics); +#define XFRM_INC_STATS(field) SNMP_INC_STATS(xfrm_statistics, field) +#define XFRM_INC_STATS_BH(field) SNMP_INC_STATS_BH(xfrm_statistics, field) +#define XFRM_INC_STATS_USER(field) SNMP_INC_STATS_USER(xfrm_statistics, field) +#else +#define XFRM_INC_STATS(field) +#define XFRM_INC_STATS_BH(field) +#define XFRM_INC_STATS_USER(field) +#endif + extern struct sock *xfrm_nl; extern u32 sysctl_xfrm_aevent_etime; extern u32 sysctl_xfrm_aevent_rseqth; @@ -269,6 +280,7 @@ struct xfrm_type __u8 proto; __u8 flags; #define XFRM_TYPE_NON_FRAGMENT 1 +#define XFRM_TYPE_MULTILOCAL 2 int (*init_state)(struct xfrm_state *x); void (*destructor)(struct xfrm_state *); @@ -584,6 +596,12 @@ struct xfrm_dst struct rt6_info rt6; } u; struct dst_entry *route; +#ifdef CONFIG_XFRM_SUB_POLICY + u8 flags; +#define XFRM_DST_MULTIPOLICY 1 + struct flowi origin; + struct xfrm_selector partner; +#endif u32 genid; u32 route_mtu_cached; u32 child_mtu_cached; @@ -904,9 +922,12 @@ extern void xfrm_state_init(void); extern void xfrm4_state_init(void); extern void xfrm6_state_init(void); extern void xfrm6_state_fini(void); +extern int xfrm_proc_init(void); extern int xfrm_state_walk(u8 proto, int (*func)(struct xfrm_state *, int, void*), void *); extern struct xfrm_state *xfrm_state_alloc(void); +extern int xfrm_state_trigger(struct flowi *fl, struct xfrm_policy *pol, + struct xfrm_tmpl *tmpl, unsigned short family); extern struct xfrm_state *xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr, struct flowi *fl, struct xfrm_tmpl *tmpl, struct xfrm_policy *pol, int *err, diff --git a/net/ipv4/tunnel4.c b/net/ipv4/tunnel4.c index a794a8c..dc65f57 100644 --- a/net/ipv4/tunnel4.c +++ b/net/ipv4/tunnel4.c @@ -8,6 +8,9 @@ #include #include #include +#ifdef CONFIG_SYSCTL +#include +#endif #include #include #include @@ -17,6 +20,44 @@ static struct xfrm_tunnel *tunnel4_handlers; static struct xfrm_tunnel *tunnel64_handlers; static DEFINE_MUTEX(tunnel4_mutex); +static int sysctl_tunnel4_send_icmp_error __read_mostly = 1; + +#ifdef CONFIG_SYSCTL +static struct ctl_table_header *tunnel4_sysctl_header; + +static ctl_table tunnel4_table[] = { + { + .ctl_name = NET_IPV4_TUNNEL_SEND_ICMP_ERROR, + .procname = "tunnel_send_icmp_error", + .data = &sysctl_tunnel4_send_icmp_error, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec + }, + { .ctl_name = 0 } +}; + +static ctl_table ipv4_net_table[] = { + { + .ctl_name = NET_IPV4, + .procname = "ipv4", + .mode = 0555, + .child = tunnel4_table + }, + { .ctl_name = 0 } +}; + +static ctl_table ipv4_root_table[] = { + { + .ctl_name = CTL_NET, + .procname = "net", + .mode = 0555, + .child = ipv4_net_table + }, + { .ctl_name = 0 } +}; +#endif + int xfrm4_tunnel_register(struct xfrm_tunnel *handler, unsigned short family) { struct xfrm_tunnel **pprev; @@ -82,7 +123,8 @@ static int tunnel4_rcv(struct sk_buff *skb) if (!handler->handler(skb)) return 0; - icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0); + if (sysctl_tunnel4_send_icmp_error) + icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0); drop: kfree_skb(skb); @@ -134,6 +176,10 @@ static struct net_protocol tunnel64_protocol = { static int __init tunnel4_init(void) { +#ifdef CONFIG_SYSCTL + tunnel4_sysctl_header = register_sysctl_table(ipv4_root_table); +#endif + if (inet_add_protocol(&tunnel4_protocol, IPPROTO_IPIP)) { printk(KERN_ERR "tunnel4 init: can't add protocol\n"); return -EAGAIN; @@ -156,6 +202,10 @@ static void __exit tunnel4_fini(void) #endif if (inet_del_protocol(&tunnel4_protocol, IPPROTO_IPIP)) printk(KERN_ERR "tunnel4 close: can't remove protocol\n"); + +#ifdef CONFIG_SYSCTL + unregister_sysctl_table(tunnel4_sysctl_header); +#endif } module_init(tunnel4_init); diff --git a/net/ipv4/xfrm4_input.c b/net/ipv4/xfrm4_input.c index 78e80de..96afb09 100644 --- a/net/ipv4/xfrm4_input.c +++ b/net/ipv4/xfrm4_input.c @@ -62,35 +62,51 @@ int xfrm4_rcv_encap(struct sk_buff *skb, __u16 encap_type) int xfrm_nr = 0; int decaps = 0; - if ((err = xfrm4_parse_spi(skb, skb->nh.iph->protocol, &spi, &seq)) != 0) + if ((err = xfrm4_parse_spi(skb, skb->nh.iph->protocol, &spi, &seq)) != 0) { + XFRM_INC_STATS(XFRM_MIB_INHDRERROR); goto drop; + } do { struct iphdr *iph = skb->nh.iph; - if (xfrm_nr == XFRM_MAX_DEPTH) + if (xfrm_nr == XFRM_MAX_DEPTH) { + XFRM_INC_STATS(XFRM_MIB_INERROR); goto drop; + } x = xfrm_state_lookup((xfrm_address_t *)&iph->daddr, spi, iph->protocol != IPPROTO_IPV6 ? iph->protocol : IPPROTO_IPIP, AF_INET); - if (x == NULL) + if (x == NULL) { + XFRM_INC_STATS(XFRM_MIB_INNOSTATES); goto drop; + } spin_lock(&x->lock); - if (unlikely(x->km.state != XFRM_STATE_VALID)) + if (unlikely(x->km.state != XFRM_STATE_VALID)) { + XFRM_INC_STATS(XFRM_MIB_INSTATEINVALID); goto drop_unlock; + } - if ((x->encap ? x->encap->encap_type : 0) != encap_type) + if ((x->encap ? x->encap->encap_type : 0) != encap_type) { + XFRM_INC_STATS(XFRM_MIB_INSTATEINVALID); goto drop_unlock; + } - if (x->props.replay_window && xfrm_replay_check(x, seq)) + if (x->props.replay_window && xfrm_replay_check(x, seq)) { + XFRM_INC_STATS(XFRM_MIB_INSEQOUTOFWINDOW); goto drop_unlock; + } - if (xfrm_state_check_expire(x)) + if (xfrm_state_check_expire(x)) { + XFRM_INC_STATS(XFRM_MIB_INSTATEEXPIRED); goto drop_unlock; + } - if (x->type->input(x, skb)) + if (x->type->input(x, skb)) { + XFRM_INC_STATS(XFRM_MIB_INSTATEPROTOERROR); goto drop_unlock; + } /* only the first xfrm gets the encap type */ encap_type = 0; @@ -105,16 +121,20 @@ int xfrm4_rcv_encap(struct sk_buff *skb, __u16 encap_type) xfrm_vec[xfrm_nr++] = x; - if (x->mode->input(x, skb)) + if (x->mode->input(x, skb)) { + XFRM_INC_STATS(XFRM_MIB_INSTATEMODEERROR); goto drop; + } if (x->props.mode == XFRM_MODE_TUNNEL) { decaps = 1; break; } - if ((err = xfrm_parse_spi(skb, skb->nh.iph->protocol, &spi, &seq)) < 0) + if ((err = xfrm_parse_spi(skb, skb->nh.iph->protocol, &spi, &seq)) < 0) { + XFRM_INC_STATS(XFRM_MIB_INHDRERROR); goto drop; + } } while (!err); /* Allocate new secpath or COW existing one. */ @@ -122,14 +142,18 @@ int xfrm4_rcv_encap(struct sk_buff *skb, __u16 encap_type) if (!skb->sp || atomic_read(&skb->sp->refcnt) != 1) { struct sec_path *sp; sp = secpath_dup(skb->sp); - if (!sp) + if (!sp) { + XFRM_INC_STATS(XFRM_MIB_INERROR); goto drop; + } if (skb->sp) secpath_put(skb->sp); skb->sp = sp; } - if (xfrm_nr + skb->sp->len > XFRM_MAX_DEPTH) + if (xfrm_nr + skb->sp->len > XFRM_MAX_DEPTH) { + XFRM_INC_STATS(XFRM_MIB_INERROR); goto drop; + } memcpy(skb->sp->xvec + skb->sp->len, xfrm_vec, xfrm_nr * sizeof(xfrm_vec[0])); diff --git a/net/ipv4/xfrm4_output.c b/net/ipv4/xfrm4_output.c index 038ca16..a16dd1f 100644 --- a/net/ipv4/xfrm4_output.c +++ b/net/ipv4/xfrm4_output.c @@ -50,14 +50,18 @@ static int xfrm4_output_one(struct sk_buff *skb) if (skb->ip_summed == CHECKSUM_PARTIAL) { err = skb_checksum_help(skb); - if (err) + if (err) { + XFRM_INC_STATS(XFRM_MIB_OUTERROR); goto error_nolock; + } } if (x->props.mode == XFRM_MODE_TUNNEL) { err = xfrm4_tunnel_check_size(skb); - if (err) + if (err) { + XFRM_INC_STATS(XFRM_MIB_OUTLENGTHERROR); goto error_nolock; + } } do { @@ -67,12 +71,16 @@ static int xfrm4_output_one(struct sk_buff *skb) goto error; err = x->mode->output(x, skb); - if (err) + if (err) { + XFRM_INC_STATS(XFRM_MIB_OUTSTATEMODEERROR); goto error; + } err = x->type->output(x, skb); - if (err) + if (err) { + XFRM_INC_STATS(XFRM_MIB_OUTSTATEPROTOERROR); goto error; + } x->curlft.bytes += skb->len; x->curlft.packets++; @@ -80,6 +88,7 @@ static int xfrm4_output_one(struct sk_buff *skb) spin_unlock_bh(&x->lock); if (!(skb->dst = dst_pop(dst))) { + XFRM_INC_STATS(XFRM_MIB_OUTERROR); err = -EHOSTUNREACH; goto error_nolock; } diff --git a/net/ipv6/fib6_rules.c b/net/ipv6/fib6_rules.c index 0862809..48c6d32 100644 --- a/net/ipv6/fib6_rules.c +++ b/net/ipv6/fib6_rules.c @@ -17,6 +17,7 @@ #include #include +#include #include #include @@ -95,8 +96,27 @@ static int fib6_rule_action(struct fib_rule *rule, struct flowi *flp, if (table) rt = lookup(table, flp, flags); - if (rt != &ip6_null_entry) + if (rt != &ip6_null_entry) { + struct fib6_rule *r = (struct fib6_rule *)rule; + + /* + * If we need to find a source address for this traffic, + * we check the result if it meets requirement of the rule. + */ + if ((rule->flags & FIB_RULE_FIND_SADDR) && + r->src.plen && !(flags & RT6_LOOKUP_F_HAS_SADDR)) { + struct in6_addr saddr; + if (ipv6_get_saddr(&rt->u.dst, &flp->fl6_dst, + &saddr)) + goto again; + if (!ipv6_prefix_equal(&saddr, &r->src.addr, + r->src.plen)) + goto again; + ipv6_addr_copy(&flp->fl6_src, &saddr); + } goto out; + } +again: dst_release(&rt->u.dst); rt = NULL; goto out; @@ -117,9 +137,17 @@ static int fib6_rule_match(struct fib_rule *rule, struct flowi *fl, int flags) !ipv6_prefix_equal(&fl->fl6_dst, &r->dst.addr, r->dst.plen)) return 0; + /* + * If FIB_RULE_FIND_SADDR is set and we do not have a + * source address for the traffic, we defer check for + * source address. + */ if (r->src.plen) { - if (!(flags & RT6_LOOKUP_F_HAS_SADDR) || - !ipv6_prefix_equal(&fl->fl6_src, &r->src.addr, r->src.plen)) + if (flags & RT6_LOOKUP_F_HAS_SADDR) { + if (!ipv6_prefix_equal(&fl->fl6_src, &r->src.addr, + r->src.plen)) + return 0; + } else if (!(r->common.flags & FIB_RULE_FIND_SADDR)) return 0; } diff --git a/net/ipv6/mip6.c b/net/ipv6/mip6.c index 0afcabd..716c7bc 100644 --- a/net/ipv6/mip6.c +++ b/net/ipv6/mip6.c @@ -454,7 +454,7 @@ static struct xfrm_type mip6_rthdr_type = .description = "MIP6RT", .owner = THIS_MODULE, .proto = IPPROTO_ROUTING, - .flags = XFRM_TYPE_NON_FRAGMENT, + .flags = XFRM_TYPE_NON_FRAGMENT | XFRM_TYPE_MULTILOCAL, .init_state = mip6_rthdr_init_state, .destructor = mip6_rthdr_destroy, .input = mip6_rthdr_input, diff --git a/net/ipv6/sysctl_net_ipv6.c b/net/ipv6/sysctl_net_ipv6.c index 3fb4427..6f40ba1 100644 --- a/net/ipv6/sysctl_net_ipv6.c +++ b/net/ipv6/sysctl_net_ipv6.c @@ -80,6 +80,7 @@ static ctl_table ipv6_table[] = { .mode = 0644, .proc_handler = &proc_dointvec }, + { .ctl_name = 0 } }; diff --git a/net/ipv6/tunnel6.c b/net/ipv6/tunnel6.c index 23e2809..f6c4503 100644 --- a/net/ipv6/tunnel6.c +++ b/net/ipv6/tunnel6.c @@ -25,6 +25,9 @@ #include #include #include +#ifdef CONFIG_SYSCTL +#include +#endif #include #include #include @@ -33,6 +36,44 @@ static struct xfrm6_tunnel *tunnel6_handlers; static struct xfrm6_tunnel *tunnel46_handlers; static DEFINE_MUTEX(tunnel6_mutex); +static int sysctl_tunnel6_send_icmp_error __read_mostly = 1; + +#ifdef CONFIG_SYSCTL +static struct ctl_table_header *tunnel6_sysctl_header; + +static ctl_table tunnel6_table[] = { + { + .ctl_name = NET_IPV6_TUNNEL_SEND_ICMP_ERROR, + .procname = "tunnel_send_icmp_error", + .data = &sysctl_tunnel6_send_icmp_error, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec + }, + { .ctl_name = 0 } +}; + +static ctl_table ipv6_net_table[] = { + { + .ctl_name = NET_IPV6, + .procname = "ipv6", + .mode = 0555, + .child = tunnel6_table + }, + { .ctl_name = 0 } +}; + +static ctl_table ipv6_root_table[] = { + { + .ctl_name = CTL_NET, + .procname = "net", + .mode = 0555, + .child = ipv6_net_table + }, + { .ctl_name = 0 } +}; +#endif + int xfrm6_tunnel_register(struct xfrm6_tunnel *handler, unsigned short family) { struct xfrm6_tunnel **pprev; @@ -99,7 +140,9 @@ static int tunnel6_rcv(struct sk_buff **pskb) if (!handler->handler(skb)) return 0; - icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0, skb->dev); + if (sysctl_tunnel6_send_icmp_error) + icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, + 0, skb->dev); drop: kfree_skb(skb); @@ -149,6 +192,10 @@ static struct inet6_protocol tunnel46_protocol = { static int __init tunnel6_init(void) { +#ifdef CONFIG_SYSCTL + tunnel6_sysctl_header = register_sysctl_table(ipv6_root_table); +#endif + if (inet6_add_protocol(&tunnel6_protocol, IPPROTO_IPV6)) { printk(KERN_ERR "tunnel6 init(): can't add protocol\n"); return -EAGAIN; @@ -167,6 +214,10 @@ static void __exit tunnel6_fini(void) printk(KERN_ERR "tunnel6 close: can't remove protocol\n"); if (inet6_del_protocol(&tunnel6_protocol, IPPROTO_IPV6)) printk(KERN_ERR "tunnel6 close: can't remove protocol\n"); + +#ifdef CONFIG_SYSCTL + unregister_sysctl_table(tunnel6_sysctl_header); +#endif } module_init(tunnel6_init); diff --git a/net/ipv6/xfrm6_input.c b/net/ipv6/xfrm6_input.c index 31f651f..7b46f6a 100644 --- a/net/ipv6/xfrm6_input.c +++ b/net/ipv6/xfrm6_input.c @@ -31,32 +31,47 @@ int xfrm6_rcv_spi(struct sk_buff *skb, __be32 spi) nexthdr = skb->nh.raw[nhoff]; seq = 0; - if (!spi && (err = xfrm_parse_spi(skb, nexthdr, &spi, &seq)) != 0) + if (!spi && (err = xfrm_parse_spi(skb, nexthdr, &spi, &seq)) != 0) { + XFRM_INC_STATS(XFRM_MIB_INHDRERROR); goto drop; + } do { struct ipv6hdr *iph = skb->nh.ipv6h; - if (xfrm_nr == XFRM_MAX_DEPTH) + if (xfrm_nr == XFRM_MAX_DEPTH) { + XFRM_INC_STATS(XFRM_MIB_INERROR); goto drop; + } x = xfrm_state_lookup((xfrm_address_t *)&iph->daddr, spi, nexthdr != IPPROTO_IPIP ? nexthdr : IPPROTO_IPV6, AF_INET6); - if (x == NULL) + if (x == NULL) { + XFRM_INC_STATS(XFRM_MIB_INNOSTATES); goto drop; + } + spin_lock(&x->lock); - if (unlikely(x->km.state != XFRM_STATE_VALID)) + if (unlikely(x->km.state != XFRM_STATE_VALID)) { + XFRM_INC_STATS(XFRM_MIB_INSTATEINVALID); goto drop_unlock; + } - if (x->props.replay_window && xfrm_replay_check(x, seq)) + if (x->props.replay_window && xfrm_replay_check(x, seq)) { + XFRM_INC_STATS(XFRM_MIB_INSEQOUTOFWINDOW); goto drop_unlock; + } - if (xfrm_state_check_expire(x)) + if (xfrm_state_check_expire(x)) { + XFRM_INC_STATS(XFRM_MIB_INSTATEEXPIRED); goto drop_unlock; + } nexthdr = x->type->input(x, skb); - if (nexthdr <= 0) + if (nexthdr <= 0) { + XFRM_INC_STATS(XFRM_MIB_INSTATEPROTOERROR); goto drop_unlock; + } skb->nh.raw[nhoff] = nexthdr; @@ -70,31 +85,39 @@ int xfrm6_rcv_spi(struct sk_buff *skb, __be32 spi) xfrm_vec[xfrm_nr++] = x; - if (x->mode->input(x, skb)) + if (x->mode->input(x, skb)) { + XFRM_INC_STATS(XFRM_MIB_INSTATEMODEERROR); goto drop; + } if (x->props.mode == XFRM_MODE_TUNNEL) { /* XXX */ decaps = 1; break; } - if ((err = xfrm_parse_spi(skb, nexthdr, &spi, &seq)) < 0) + if ((err = xfrm_parse_spi(skb, nexthdr, &spi, &seq)) < 0) { + XFRM_INC_STATS(XFRM_MIB_INHDRERROR); goto drop; + } } while (!err); /* Allocate new secpath or COW existing one. */ if (!skb->sp || atomic_read(&skb->sp->refcnt) != 1) { struct sec_path *sp; sp = secpath_dup(skb->sp); - if (!sp) + if (!sp) { + XFRM_INC_STATS(XFRM_MIB_INERROR); goto drop; + } if (skb->sp) secpath_put(skb->sp); skb->sp = sp; } - if (xfrm_nr + skb->sp->len > XFRM_MAX_DEPTH) + if (xfrm_nr + skb->sp->len > XFRM_MAX_DEPTH) { + XFRM_INC_STATS(XFRM_MIB_INERROR); goto drop; + } memcpy(skb->sp->xvec + skb->sp->len, xfrm_vec, xfrm_nr * sizeof(xfrm_vec[0])); @@ -221,22 +244,28 @@ int xfrm6_input_addr(struct sk_buff *skb, xfrm_address_t *daddr, break; } - if (!xfrm_vec_one) + if (!xfrm_vec_one) { + XFRM_INC_STATS(XFRM_MIB_INNOSTATES); goto drop; + } /* Allocate new secpath or COW existing one. */ if (!skb->sp || atomic_read(&skb->sp->refcnt) != 1) { struct sec_path *sp; sp = secpath_dup(skb->sp); - if (!sp) + if (!sp) { + XFRM_INC_STATS(XFRM_MIB_INERROR); goto drop; + } if (skb->sp) secpath_put(skb->sp); skb->sp = sp; } - if (1 + skb->sp->len > XFRM_MAX_DEPTH) + if (1 + skb->sp->len > XFRM_MAX_DEPTH) { + XFRM_INC_STATS(XFRM_MIB_INERROR); goto drop; + } skb->sp->xvec[skb->sp->len] = xfrm_vec_one; skb->sp->len ++; diff --git a/net/ipv6/xfrm6_output.c b/net/ipv6/xfrm6_output.c index d6d786b..bedb6ca 100644 --- a/net/ipv6/xfrm6_output.c +++ b/net/ipv6/xfrm6_output.c @@ -49,14 +49,18 @@ static int xfrm6_output_one(struct sk_buff *skb) if (skb->ip_summed == CHECKSUM_PARTIAL) { err = skb_checksum_help(skb); - if (err) + if (err) { + XFRM_INC_STATS(XFRM_MIB_OUTERROR); goto error_nolock; + } } if (x->props.mode == XFRM_MODE_TUNNEL) { err = xfrm6_tunnel_check_size(skb); - if (err) + if (err) { + XFRM_INC_STATS(XFRM_MIB_OUTLENGTHERROR); goto error_nolock; + } } do { @@ -66,12 +70,16 @@ static int xfrm6_output_one(struct sk_buff *skb) goto error; err = x->mode->output(x, skb); - if (err) + if (err) { + XFRM_INC_STATS(XFRM_MIB_OUTSTATEMODEERROR); goto error; + } err = x->type->output(x, skb); - if (err) + if (err) { + XFRM_INC_STATS(XFRM_MIB_OUTSTATEPROTOERROR); goto error; + } x->curlft.bytes += skb->len; x->curlft.packets++; @@ -83,6 +91,7 @@ static int xfrm6_output_one(struct sk_buff *skb) skb->nh.raw = skb->data; if (!(skb->dst = dst_pop(dst))) { + XFRM_INC_STATS(XFRM_MIB_OUTERROR); err = -EHOSTUNREACH; goto error_nolock; } diff --git a/net/xfrm/Kconfig b/net/xfrm/Kconfig index 577a4f8..b4fb2e4 100644 --- a/net/xfrm/Kconfig +++ b/net/xfrm/Kconfig @@ -35,6 +35,16 @@ config XFRM_MIGRATE If unsure, say N. +config XFRM_STATISTICS + bool "Transformation statistics (EXPERIMENTAL)" + depends on INET && XFRM && PROC_FS && EXPERIMENTAL + ---help--- + This statistics is not a SNMP/MIB specification but shows + statistics about transformation error (or almost error) factor + for developer. + + If unsure, say N. + config NET_KEY tristate "PF_KEY sockets" select XFRM diff --git a/net/xfrm/Makefile b/net/xfrm/Makefile index de3c1a6..2d97a18 100644 --- a/net/xfrm/Makefile +++ b/net/xfrm/Makefile @@ -3,6 +3,6 @@ # obj-$(CONFIG_XFRM) := xfrm_policy.o xfrm_state.o xfrm_hash.o \ - xfrm_input.o xfrm_algo.o + xfrm_input.o xfrm_algo.o xfrm_proc.o obj-$(CONFIG_XFRM_USER) += xfrm_user.o diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 785c3e3..2573617 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -941,7 +941,9 @@ static int xfrm_policy_match(struct xfrm_policy *pol, struct flowi *fl, int match, ret = -ESRCH; if (pol->family != family || - pol->type != type) + pol->type != type || + ((pol->flags & XFRM_POLICY_X_MULTILOCAL) && + (fl->flags & FLOWI_FLAG_MULTILOCAL))) return ret; match = xfrm_selector_match(sel, fl, family); @@ -1349,6 +1351,9 @@ int xfrm_lookup(struct dst_entry **dst_p, struct flowi *fl, int pi; struct xfrm_state *xfrm[XFRM_MAX_DEPTH]; struct dst_entry *dst, *dst_orig = *dst_p; +#ifdef CONFIG_XFRM_SUB_POLICY + struct xfrm_dst *xdst; +#endif int nx = 0; int err; u32 genid; @@ -1394,6 +1399,7 @@ restart: switch (policy->action) { case XFRM_POLICY_BLOCK: /* Prohibit the flow */ + XFRM_INC_STATS(XFRM_MIB_OUTPOLBLOCK); err = -EPERM; goto error; @@ -1413,6 +1419,7 @@ restart: */ dst = xfrm_find_bundle(fl, policy, family); if (IS_ERR(dst)) { + XFRM_INC_STATS(XFRM_MIB_OUTBUNDLEERROR); err = PTR_ERR(dst); goto error; } @@ -1431,6 +1438,7 @@ restart: goto error; } if (pols[1]->action == XFRM_POLICY_BLOCK) { + XFRM_INC_STATS(XFRM_MIB_OUTPOLBLOCK); err = -EPERM; goto error; } @@ -1469,6 +1477,7 @@ restart: nx = xfrm_tmpl_resolve(pols, npols, fl, xfrm, family); if (nx == -EAGAIN && signal_pending(current)) { + XFRM_INC_STATS(XFRM_MIB_OUTNOSTATES); err = -ERESTART; goto error; } @@ -1479,8 +1488,10 @@ restart: } err = nx; } - if (err < 0) + if (err < 0) { + XFRM_INC_STATS(XFRM_MIB_OUTNOSTATES); goto error; + } } if (nx == 0) { /* Flow passes not transformed. */ @@ -1495,6 +1506,7 @@ restart: int i; for (i=0; i 1) { + memcpy(&xdst->partner, &pols[1]->selector, + sizeof(xdst->partner)); + xdst->flags |= XFRM_DST_MULTIPOLICY; + } else + memcpy(&xdst->origin, fl, sizeof(xdst->origin)); +#endif dst->next = policy->bundles; policy->bundles = dst; dst_hold(dst); @@ -1583,7 +1605,8 @@ xfrm_state_ok(struct xfrm_tmpl *tmpl, struct xfrm_state *x, * Otherwise "-2 - errored_index" is returned. */ static inline int -xfrm_policy_ok(struct xfrm_tmpl *tmpl, struct sec_path *sp, int start, +xfrm_policy_ok(struct flowi *fl, struct xfrm_policy *pol, + struct xfrm_tmpl *tmpl, struct sec_path *sp, int start, unsigned short family) { int idx = start; @@ -1591,6 +1614,8 @@ xfrm_policy_ok(struct xfrm_tmpl *tmpl, struct sec_path *sp, int start, if (tmpl->optional) { if (tmpl->mode == XFRM_MODE_TRANSPORT) return start; + else if (tmpl->mode == XFRM_MODE_IN_TRIGGER) + xfrm_state_trigger(fl, pol, tmpl, family); } else start = -1; for (; idx < sp->len; idx++) { @@ -1645,19 +1670,33 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb, u8 fl_dir = policy_to_flow_dir(dir); int xerr_idx = -1; - if (xfrm_decode_session(skb, &fl, family) < 0) + if (xfrm_decode_session(skb, &fl, family) < 0) { + XFRM_INC_STATS(XFRM_MIB_INHDRERROR); return 0; + } + nf_nat_decode_session(skb, &fl, family); /* First, check used SA against their selectors. */ if (skb->sp) { + int ml = 0; int i; 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(XFRM_MIB_INSTATEINVALID); return 0; + } + + if (!ml && (x->type->flags & XFRM_TYPE_MULTILOCAL)) + ml = 1; + else if (ml == 1 && x->props.mode == XFRM_MODE_TUNNEL) + ml = -1; } + + if (ml == 1) + fl.flags |= FLOWI_FLAG_MULTILOCAL; } pol = NULL; @@ -1677,6 +1716,7 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb, if (!pol) { if (skb->sp && secpath_has_nontransport(skb->sp, 0, &xerr_idx)) { xfrm_secpath_reject(xerr_idx, skb, &fl); + XFRM_INC_STATS(XFRM_MIB_INNOPOLS); return 0; } return 1; @@ -1714,10 +1754,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(XFRM_MIB_INPOLBLOCK); goto reject; - if (ti + pols[pi]->xfrm_nr >= XFRM_MAX_DEPTH) + } + if (ti + pols[pi]->xfrm_nr >= XFRM_MAX_DEPTH) { + XFRM_INC_STATS(XFRM_MIB_INERROR); goto reject_error; + } for (i = 0; i < pols[pi]->xfrm_nr; i++) tpp[ti++] = &pols[pi]->xfrm_vec[i]; } @@ -1734,21 +1778,25 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb, * are implied between each two transformations. */ for (i = xfrm_nr-1, k = 0; i >= 0; i--) { - k = xfrm_policy_ok(tpp[i], sp, k, family); + k = xfrm_policy_ok(&fl, pol, tpp[i], sp, k, family); if (k < 0) { if (k < -1) /* "-2 - errored_index" returned */ xerr_idx = -(2+k); + XFRM_INC_STATS(XFRM_MIB_INTMPLMISMATCH); goto reject; } } - if (secpath_has_nontransport(sp, k, &xerr_idx)) + if (secpath_has_nontransport(sp, k, &xerr_idx)) { + XFRM_INC_STATS(XFRM_MIB_INTMPLMISMATCH); goto reject; + } xfrm_pols_put(pols, npols); return 1; } + XFRM_INC_STATS(XFRM_MIB_INPOLBLOCK); reject: xfrm_secpath_reject(xerr_idx, skb, &fl); @@ -1933,6 +1981,17 @@ int xfrm_bundle_ok(struct xfrm_policy *pol, struct xfrm_dst *first, if (!dst_check(dst->path, ((struct xfrm_dst *)dst)->path_cookie) || (dst->dev && !netif_running(dst->dev))) return 0; +#ifdef CONFIG_XFRM_SUB_POLICY + if (fl) { + if (first->flags & XFRM_DST_MULTIPOLICY) { + if (!xfrm_selector_match(&first->partner, fl, family)) + return 0; + } else { + if (!flow_cache_uli_match(&first->origin, fl)) + return 0; + } + } +#endif last = NULL; @@ -2110,6 +2169,43 @@ void xfrm_audit_log(uid_t auid, u32 sid, int type, int result, EXPORT_SYMBOL(xfrm_audit_log); #endif /* CONFIG_AUDITSYSCALL */ +#ifdef CONFIG_XFRM_STATISTICS +#include + +DEFINE_SNMP_STAT(struct xfrm_mib, xfrm_statistics) __read_mostly; +EXPORT_SYMBOL(xfrm_statistics); + +static int xfrm_statistics_init(void) +{ + xfrm_statistics[0] = alloc_percpu(struct xfrm_mib); + if (!xfrm_statistics[0]) + goto err0; + + xfrm_statistics[1] = alloc_percpu(struct xfrm_mib); + if (!xfrm_statistics[1]) + goto err1; + + return 0; + +err1: + free_percpu(xfrm_statistics[0]); + xfrm_statistics[0] = NULL; +err0: + return -ENOMEM; +} + +#if 0 +static void xfrm_statistics_cleanup(void) +{ + if (xfrm_statistics[0]) + free_percpu(xfrm_statistics[0]); + if (xfrm_statistics[1]) + free_percpu(xfrm_statistics[1]); + xfrm_statistics[0] = xfrm_statistics[1] = NULL; +} +#endif +#endif + int xfrm_policy_register_afinfo(struct xfrm_policy_afinfo *afinfo) { int err = 0; @@ -2250,9 +2346,13 @@ 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(); + xfrm_proc_init(); } #ifdef CONFIG_XFRM_MIGRATE diff --git a/net/xfrm/xfrm_proc.c b/net/xfrm/xfrm_proc.c new file mode 100644 index 0000000..1453aa9 --- /dev/null +++ b/net/xfrm/xfrm_proc.c @@ -0,0 +1,397 @@ +/* + * Author: Masahide NAKAMURA + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include +#include +#include +#include +#include +#include +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) +#include +#endif + +#define XFRM_SEQ_NUM_BUF 32 +#define XFRM_PROC_DEBUG + +#ifdef CONFIG_XFRM_STATISTICS +#include + +static struct snmp_mib xfrm_mib_list[] = { + SNMP_MIB_ITEM("XfrmInError", XFRM_MIB_INERROR), + SNMP_MIB_ITEM("XfrmInHdrError", XFRM_MIB_INHDRERROR), + SNMP_MIB_ITEM("XfrmInStateProtoError", XFRM_MIB_INSTATEPROTOERROR), + SNMP_MIB_ITEM("XfrmInStateModeError", XFRM_MIB_INSTATEMODEERROR), + SNMP_MIB_ITEM("XfrmInSeqOutOfWindow", XFRM_MIB_INSEQOUTOFWINDOW), + SNMP_MIB_ITEM("XfrmInStateExpired", XFRM_MIB_INSTATEEXPIRED), + SNMP_MIB_ITEM("XfrmInStateInvalid", XFRM_MIB_INSTATEINVALID), + SNMP_MIB_ITEM("XfrmInNoStates", XFRM_MIB_INNOSTATES), + SNMP_MIB_ITEM("XfrmInTmplMismatch", XFRM_MIB_INTMPLMISMATCH), + SNMP_MIB_ITEM("XfrmInPolBlock", XFRM_MIB_INPOLBLOCK), + SNMP_MIB_ITEM("XfrmInNoPols", XFRM_MIB_INNOPOLS), + SNMP_MIB_ITEM("XfrmOutError", XFRM_MIB_OUTERROR), + SNMP_MIB_ITEM("XfrmOutLengthError", XFRM_MIB_OUTLENGTHERROR), + SNMP_MIB_ITEM("XfrmOutStateProtoError", XFRM_MIB_OUTSTATEPROTOERROR), + SNMP_MIB_ITEM("XfrmOutStateModeError", XFRM_MIB_OUTSTATEMODEERROR), + SNMP_MIB_ITEM("XfrmOutStateExpired", XFRM_MIB_OUTSTATEEXPIRED), + SNMP_MIB_ITEM("XfrmOutNoStates", XFRM_MIB_OUTNOSTATES), + SNMP_MIB_ITEM("XfrmOutPolBlock", XFRM_MIB_OUTPOLBLOCK), + SNMP_MIB_ITEM("XfrmOutBundleError", XFRM_MIB_OUTBUNDLEERROR), + SNMP_MIB_SENTINEL +}; + +static unsigned long +fold_field(void *mib[], int offt) +{ + unsigned long res = 0; + int i; + + for_each_possible_cpu(i) { + res += *(((unsigned long *)per_cpu_ptr(mib[0], i)) + offt); + res += *(((unsigned long *)per_cpu_ptr(mib[1], i)) + offt); + } + return res; +} + +static int xfrm_statistics_seq_show(struct seq_file *seq, void *v) +{ + int i; + for (i=0; xfrm_mib_list[i].name; i++) + seq_printf(seq, "%-24s\t%lu\n", xfrm_mib_list[i].name, + fold_field((void **)xfrm_statistics, + xfrm_mib_list[i].entry)); + return 0; +} + +static int xfrm_statistics_seq_open(struct inode *inode, struct file *file) +{ + return single_open(file, xfrm_statistics_seq_show, NULL); +} + +static struct file_operations proc_net_xfrm_stats_seq_fops = { + .owner = THIS_MODULE, + .open = xfrm_statistics_seq_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static struct proc_dir_entry *proc_net_xfrm_stats; + +#endif /* CONFIG_XFRM_STATISTICS */ + +#ifdef XFRM_PROC_DEBUG +#include +#include +#endif + +#ifdef CONFIG_PROC_FS + +static char* xfrm_seq_sprintf(char *buf, const char *fmt, ...) +{ + va_list args; + int i; + + va_start(args, fmt); + i = vsnprintf(buf, INT_MAX, fmt, args); + va_end(args); + return buf; +} + +#ifdef XFRM_PROC_DEBUG +static int __xfrm_bundle_neigh_seq_print(struct seq_file *seq, + struct neighbour *n) +{ + if (n->tbl) { + switch (n->tbl->family) { + case AF_INET: + { + struct in_addr *addr; + if (n->tbl->key_len >= sizeof(*addr)) { + addr = (struct in_addr *)n->primary_key; + seq_printf(seq, NIPQUAD_FMT, NIPQUAD(*addr)); + } + break; + } +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) + case AF_INET6: + { + struct in6_addr *addr; + if (n->tbl->key_len >= sizeof(*addr)) { + addr = (struct in6_addr *)n->primary_key; + seq_printf(seq, NIP6_FMT, NIP6(*addr)); + } + break; + } +#endif + default: + break; + } + } + + if (n->dev) { + int i; + + seq_printf(seq, " dev %s", n->dev->name); + + switch (n->type) { + case ARPHRD_ETHER: + seq_printf(seq, " lladdr "); + for (i = 0; i < n->dev->addr_len; i++) { + if (i > 0) + seq_printf(seq, ":"); + seq_printf(seq, "%02x", n->ha[i]); + } + break; + /* XXX: other types should be written */ + default: + seq_printf(seq, " %u", n->type); + break; + } + } + + return 0; +} +#endif + +static int __xfrm_bundle_state_seq_print(struct seq_file *seq, + struct xfrm_state *x) +{ + char buf[XFRM_SEQ_NUM_BUF]; + + seq_printf(seq, "%-3u %s", x->id.proto, + ((x->props.mode == XFRM_MODE_TRANSPORT) ? "transport" : + (x->props.mode == XFRM_MODE_TUNNEL) ? "tunnel" : + (x->props.mode == XFRM_MODE_ROUTEOPTIMIZATION) ? "ro" : + (x->props.mode == XFRM_MODE_IN_TRIGGER) ? "in_trigger" : + (x->props.mode == XFRM_MODE_BEET) ? "beet" : + xfrm_seq_sprintf(buf, "%u", x->props.mode))); + + switch (x->props.family) { + case AF_INET: + seq_printf(seq, " " NIPQUAD_FMT " " NIPQUAD_FMT, + NIPQUAD(*(struct in_addr *)&x->props.saddr), + NIPQUAD(*(struct in_addr *)&x->id.daddr)); + if (x->coaddr) + seq_printf(seq, " coa " NIPQUAD_FMT, + NIPQUAD(*(struct in_addr *)x->coaddr)); + break; +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) + case AF_INET6: + seq_printf(seq, " " NIP6_FMT " " NIP6_FMT, + NIP6(*(struct in6_addr *)&x->props.saddr), + NIP6(*(struct in6_addr *)&x->id.daddr)); + if (x->coaddr) + seq_printf(seq, " coa " NIP6_FMT, + NIP6(*(struct in6_addr *)x->coaddr)); + break; +#endif + default: + break; + } + + if (xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY)) + seq_printf(seq, " spi 0x%08x", ntohl(x->id.spi)); + + seq_printf(seq, " ref %u", atomic_read(&x->refcnt)); + + return 0; +} + +static int __xfrm_bundle_xdst_seq_print(struct seq_file *seq, + struct xfrm_dst *xdst) +{ + if (!xdst->u.dst.ops) + return 0; + + switch (xdst->u.dst.ops->family) { + case AF_INET: + seq_printf(seq, " rt"); + /* XXX: should be written */ + break; +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) + case AF_INET6: + { + struct rt6_info *rt6i = &xdst->u.rt6; + + seq_printf(seq, " rt6"); + + seq_printf(seq, " " NIP6_FMT "/%d", NIP6(rt6i->rt6i_dst.addr), + rt6i->rt6i_dst.plen); + if (rt6i->rt6i_src.plen) { + seq_printf(seq, " src " NIP6_FMT "/%d", + NIP6(rt6i->rt6i_src.addr), + rt6i->rt6i_src.plen); + } + seq_printf(seq, " via " NIP6_FMT, + NIP6(rt6i->rt6i_gateway)); + seq_printf(seq, " dev %s %s", + ((rt6i->rt6i_idev && rt6i->rt6i_idev->dev) ? + rt6i->rt6i_idev->dev->name : "null"), + ((rt6i->rt6i_flags&RTF_CACHE)?"cache":"")); + seq_printf(seq, " sernum %u", + (rt6i->rt6i_node ? rt6i->rt6i_node->fn_sernum : 0)); + seq_printf(seq, " ref %u", atomic_read(&rt6i->rt6i_ref)); + break; + } +#endif + default: + break; + } + + return 0; +} + +static int __xfrm_bundle_seq_show_dst(struct seq_file *seq, + struct xfrm_dst *xdst, int gen) +{ + seq_printf(seq, " gen %d", gen); + seq_printf(seq, " ref %u", atomic_read(&xdst->u.dst.__refcnt)); + if (!gen) { + seq_printf(seq, " rtcook %u", xdst->route_cookie); + seq_printf(seq, " ptcook %u", xdst->path_cookie); + } + seq_printf(seq, "\n"); + + if (xdst->u.dst.ops) { + seq_printf(seq, " dst "); + __xfrm_bundle_xdst_seq_print(seq, xdst); + seq_printf(seq, "\n"); + +#ifdef XFRM_PROC_DEBUG + if (xdst->u.dst.neighbour) { + struct neighbour *n = xdst->u.dst.neighbour; + seq_printf(seq, " neigh "); + read_lock(&n->lock); + __xfrm_bundle_neigh_seq_print(seq, n); + read_unlock(&n->lock); + seq_printf(seq, "\n"); + } +#endif + } + if (xdst->u.dst.xfrm) { + struct xfrm_state *x = xdst->u.dst.xfrm; + seq_printf(seq, " xfrm "); + spin_lock(&x->lock); + __xfrm_bundle_state_seq_print(seq, x); + spin_unlock(&x->lock); + seq_printf(seq, "\n"); + } + return 0; +} + +static int xfrm_bundle_seq_show_one(struct xfrm_policy *xp, int dir, int count, + void *data) +{ + struct seq_file *seq = (struct seq_file *)data; + char buf[XFRM_SEQ_NUM_BUF]; + struct dst_entry *dst; + + read_lock_bh(&xp->lock); + + if (!xp->bundles) + goto out; + + seq_printf(seq, "%-4s %011u %-3s\n", + ((xp->type == XFRM_POLICY_TYPE_MAIN) ? "main" : + (xp->type == XFRM_POLICY_TYPE_SUB) ? "sub" : + xfrm_seq_sprintf(buf, "%u", xp->type)), + xp->index, + ((dir == XFRM_POLICY_IN) ? "in" : + (dir == XFRM_POLICY_OUT) ? "out" : + (dir == XFRM_POLICY_FWD) ? "fwd" : + xfrm_seq_sprintf(buf, "%u", dir))); + + for (dst = xp->bundles; dst; dst = dst->next) { + struct dst_entry *d = dst; + int gen = 0; + + for (d = dst; d; d = d->child) { + struct xfrm_dst *xdst = (struct xfrm_dst*)d; + __xfrm_bundle_seq_show_dst(seq, xdst, gen++); + } + seq_printf(seq, "\n"); /* for delimiter */ + } + + out: + read_unlock_bh(&xp->lock); + return 0; +} + +static int xfrm_bundle_seq_show(struct seq_file *seq, void *v) +{ + xfrm_policy_walk(XFRM_POLICY_TYPE_MAIN, xfrm_bundle_seq_show_one, seq); +#ifdef CONFIG_XFRM_SUB_POLICY + xfrm_policy_walk(XFRM_POLICY_TYPE_SUB, xfrm_bundle_seq_show_one, seq); +#endif + return 0; +} + +static int xfrm_bundle_seq_open(struct inode *inode, struct file *file) +{ + return single_open(file, xfrm_bundle_seq_show, NULL); +} + +static struct file_operations proc_net_xfrm_bundle_seq_fops = { + .owner = THIS_MODULE, + .open = xfrm_bundle_seq_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static struct proc_dir_entry *proc_net_xfrm; +static struct proc_dir_entry *proc_net_xfrm_bundle; + +int __init xfrm_proc_init(void) +{ + int rc = 0; + + proc_net_xfrm = proc_mkdir("xfrm", proc_net); + if (!proc_net_xfrm) + goto proc_net_xfrm_fail; + + proc_net_xfrm_bundle = create_proc_entry("bundle", S_IRUGO, + proc_net_xfrm); + if (!proc_net_xfrm_bundle) + goto proc_net_xfrm_bundle_fail; + + proc_net_xfrm_bundle->proc_fops = &proc_net_xfrm_bundle_seq_fops; + +#ifdef CONFIG_XFRM_STATISTICS + proc_net_xfrm_stats = create_proc_entry("stats", S_IRUGO, + proc_net_xfrm); + if (!proc_net_xfrm_stats) + goto proc_net_xfrm_stats_fail; + + proc_net_xfrm_stats->proc_fops = &proc_net_xfrm_stats_seq_fops; +#endif + + out: + return rc; + +#ifdef CONFIG_XFRM_STATISTICS + proc_net_xfrm_stats_fail: + remove_proc_entry("bundle", proc_net_xfrm); +#endif + proc_net_xfrm_bundle_fail: + remove_proc_entry("xfrm", proc_net); + proc_net_xfrm_fail: + rc = -ENOMEM; + goto out; +} + +#if 0 +void xfrm_proc_exit(void) +{ + remove_proc_entry("bundle", proc_net_xfrm); + remove_proc_entry("xfrm", proc_net); +} +#endif + +#endif /* CONFIG_PROC_FS */ diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index 5c5f6dc..473804f 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -522,6 +522,85 @@ static void xfrm_hash_grow_check(int have_hash_collision) schedule_work(&xfrm_hash_work); } +static inline int +__xfrm_state_query(struct flowi *fl, struct xfrm_policy *pol, + struct xfrm_tmpl *tmpl, xfrm_address_t *daddr, + xfrm_address_t *saddr, unsigned short family, + struct xfrm_state **statep) +{ + struct xfrm_state *x; + unsigned int h; + int error = 0; + + x = xfrm_state_alloc(); + if (x == NULL) { + error = -ENOMEM; + goto out; + } + /* Initialize temporary selector matching only + * to current session. */ + xfrm_init_tempsel(x, fl, tmpl, daddr, saddr, family); + + error = security_xfrm_state_alloc_acquire(x, pol->security, fl->secid); + if (error) { + x->km.state = XFRM_STATE_DEAD; + xfrm_state_put(x); + x = NULL; + goto out; + } + + error = km_query(x, tmpl, pol); + if (error) { + x->km.state = XFRM_STATE_DEAD; + xfrm_state_put(x); + x = NULL; + error = -ESRCH; + goto out; + } + + x->km.state = XFRM_STATE_ACQ; + h = xfrm_dst_hash(daddr, saddr, tmpl->reqid, family); + hlist_add_head(&x->bydst, xfrm_state_bydst+h); + h = xfrm_src_hash(daddr, saddr, family); + hlist_add_head(&x->bysrc, xfrm_state_bysrc+h); + if (x->id.spi) { + h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, family); + hlist_add_head(&x->byspi, xfrm_state_byspi+h); + } + x->lft.hard_add_expires_seconds = XFRM_ACQ_EXPIRES; + x->timer.expires = jiffies + XFRM_ACQ_EXPIRES*HZ; + add_timer(&x->timer); + xfrm_state_num++; + xfrm_hash_grow_check(x->bydst.next != NULL); + + out: + if (statep) + *statep = x; + return error; + +} + +int xfrm_state_trigger(struct flowi *fl, struct xfrm_policy *pol, + struct xfrm_tmpl *tmpl, unsigned short family) +{ + xfrm_address_t *daddr = xfrm_flowi_daddr(fl, family); + xfrm_address_t *saddr = xfrm_flowi_saddr(fl, family); + struct xfrm_state *x0; + int err; + + x0 = __xfrm_state_lookup_byaddr(daddr, saddr, tmpl->id.proto, family); + if (x0 != NULL) { + xfrm_state_put(x0); + err = -EEXIST; + goto out; + } + + err = __xfrm_state_query(fl, pol, tmpl, daddr, saddr, family, NULL); + + out: + return err; +} + struct xfrm_state * xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr, struct flowi *fl, struct xfrm_tmpl *tmpl, @@ -586,43 +665,9 @@ xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr, error = -EEXIST; goto out; } - x = xfrm_state_alloc(); - if (x == NULL) { - error = -ENOMEM; - goto out; - } - /* Initialize temporary selector matching only - * to current session. */ - xfrm_init_tempsel(x, fl, tmpl, daddr, saddr, family); - - error = security_xfrm_state_alloc_acquire(x, pol->security, fl->secid); - if (error) { - x->km.state = XFRM_STATE_DEAD; - xfrm_state_put(x); - x = NULL; - goto out; - } - if (km_query(x, tmpl, pol) == 0) { - x->km.state = XFRM_STATE_ACQ; - hlist_add_head(&x->bydst, xfrm_state_bydst+h); - h = xfrm_src_hash(daddr, saddr, family); - hlist_add_head(&x->bysrc, xfrm_state_bysrc+h); - if (x->id.spi) { - h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, family); - hlist_add_head(&x->byspi, xfrm_state_byspi+h); - } - x->lft.hard_add_expires_seconds = XFRM_ACQ_EXPIRES; - x->timer.expires = jiffies + XFRM_ACQ_EXPIRES*HZ; - add_timer(&x->timer); - xfrm_state_num++; - xfrm_hash_grow_check(x->bydst.next != NULL); - } else { - x->km.state = XFRM_STATE_DEAD; - xfrm_state_put(x); - x = NULL; - error = -ESRCH; - } + error = __xfrm_state_query(fl, pol, tmpl, daddr, saddr, family, + &x); } out: if (x) @@ -1088,9 +1133,13 @@ static int xfrm_state_check_space(struct xfrm_state *x, struct sk_buff *skb) int xfrm_state_check(struct xfrm_state *x, struct sk_buff *skb) { int err = xfrm_state_check_expire(x); - if (err < 0) + if (err < 0) { + XFRM_INC_STATS(XFRM_MIB_OUTSTATEEXPIRED); goto err; + } err = xfrm_state_check_space(x, skb); + if (err) + XFRM_INC_STATS(XFRM_MIB_OUTLENGTHERROR); err: return err; }