*/
typedef union
{
- struct {
- __u32 addr;
- __u32 mask; /* Use unused bits to cache mask. */
- } a4;
-#define xfrm4_addr a4.addr
-#define xfrm4_mask a4.mask
+ __u32 a4;
__u32 a6[4];
} xfrm_address_t;
#include <net/sock.h>
#include <net/dst.h>
#include <net/route.h>
+#include <net/ipv6.h>
#include <net/ip6_fib.h>
#define XFRM_ALIGN8(len) (((len) + 7) & ~7)
XFRM_STATE_DEAD
};
+struct xfrm_type;
+struct xfrm_dst;
+struct xfrm_policy_afinfo {
+ unsigned short family;
+ rwlock_t lock;
+ struct xfrm_type_map *type_map;
+ struct dst_ops *dst_ops;
+ void (*garbage_collect)(void);
+ int (*dst_lookup)(struct xfrm_dst **dst, struct flowi *fl);
+ struct dst_entry *(*find_bundle)(struct flowi *fl, struct rtable *rt, struct xfrm_policy *policy);
+ int (*bundle_create)(struct xfrm_policy *policy,
+ struct xfrm_state **xfrm,
+ int nx,
+ struct flowi *fl,
+ struct dst_entry **dst_p);
+ void (*decode_session)(struct sk_buff *skb,
+ struct flowi *fl);
+};
+
+extern int xfrm_policy_register_afinfo(struct xfrm_policy_afinfo *afinfo);
+extern int xfrm_policy_unregister_afinfo(struct xfrm_policy_afinfo *afinfo);
+extern struct xfrm_policy_afinfo *xfrm_policy_get_afinfo(unsigned short family);
+extern void xfrm_policy_put_afinfo(struct xfrm_policy_afinfo *afinfo);
+
+#define XFRM_ACQ_EXPIRES 30
+
+struct xfrm_tmpl;
+struct xfrm_state_afinfo {
+ unsigned short family;
+ rwlock_t lock;
+ struct list_head *state_bydst;
+ struct list_head *state_byspi;
+ void (*init_tempsel)(struct xfrm_state *x, struct flowi *fl,
+ struct xfrm_tmpl *tmpl,
+ xfrm_address_t *daddr, xfrm_address_t *saddr);
+ struct xfrm_state *(*state_lookup)(xfrm_address_t *daddr, u32 spi, u8 proto);
+ struct xfrm_state *(*find_acq)(u8 mode, u16 reqid, u8 proto,
+ xfrm_address_t *daddr, xfrm_address_t *saddr,
+ int create);
+};
+
+extern int xfrm_state_register_afinfo(struct xfrm_state_afinfo *afinfo);
+extern int xfrm_state_unregister_afinfo(struct xfrm_state_afinfo *afinfo);
+extern struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned short family);
+extern void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo);
struct xfrm_type
{
u32 (*get_max_size)(struct xfrm_state *, int size);
};
-extern int xfrm_register_type(struct xfrm_type *type);
-extern int xfrm_unregister_type(struct xfrm_type *type);
-extern struct xfrm_type *xfrm_get_type(u8 proto);
+struct xfrm_type_map {
+ rwlock_t lock;
+ struct xfrm_type *map[256];
+};
+
+extern int xfrm_register_type(struct xfrm_type *type, unsigned short family);
+extern int xfrm_unregister_type(struct xfrm_type *type, unsigned short family);
+extern struct xfrm_type *xfrm_get_type(u8 proto, unsigned short family);
extern void xfrm_put_type(struct xfrm_type *type);
struct xfrm_tmpl
extern int xfrm_unregister_km(struct xfrm_mgr *km);
+#define XFRM_FLOWCACHE_HASH_SIZE 1024
+
+static inline u32 __flow_hash4(struct flowi *fl)
+{
+ u32 hash = fl->fl4_src ^ fl->uli_u.ports.sport;
+
+ hash = ((hash & 0xF0F0F0F0) >> 4) | ((hash & 0x0F0F0F0F) << 4);
+
+ hash ^= fl->fl4_dst ^ fl->uli_u.ports.dport;
+ hash ^= (hash >> 10);
+ hash ^= (hash >> 20);
+ return hash & (XFRM_FLOWCACHE_HASH_SIZE-1);
+}
+
+static inline u32 __flow_hash6(struct flowi *fl)
+{
+ u32 hash = fl->fl6_src->s6_addr32[2] ^
+ fl->fl6_src->s6_addr32[3] ^
+ fl->uli_u.ports.sport;
+
+ hash = ((hash & 0xF0F0F0F0) >> 4) | ((hash & 0x0F0F0F0F) << 4);
+
+ hash ^= fl->fl6_dst->s6_addr32[2] ^
+ fl->fl6_dst->s6_addr32[3] ^
+ fl->uli_u.ports.dport;
+ hash ^= (hash >> 10);
+ hash ^= (hash >> 20);
+ return hash & (XFRM_FLOWCACHE_HASH_SIZE-1);
+}
+
+static inline u32 flow_hash(struct flowi *fl, unsigned short family)
+{
+ switch (family) {
+ case AF_INET:
+ return __flow_hash4(fl);
+ case AF_INET6:
+ return __flow_hash6(fl);
+ }
+ return 0; /*XXX*/
+}
+
extern struct xfrm_policy *xfrm_policy_list[XFRM_POLICY_MAX*2];
static inline void xfrm_pol_hold(struct xfrm_policy *policy)
__xfrm_policy_destroy(policy);
}
+#define XFRM_DST_HSIZE 1024
+
+static __inline__
+unsigned __xfrm4_dst_hash(xfrm_address_t *addr)
+{
+ unsigned h;
+ h = ntohl(addr->a4);
+ h = (h ^ (h>>16)) % XFRM_DST_HSIZE;
+ return h;
+}
+
+static __inline__
+unsigned __xfrm6_dst_hash(xfrm_address_t *addr)
+{
+ unsigned h;
+ h = ntohl(addr->a6[2]^addr->a6[3]);
+ h = (h ^ (h>>16)) % XFRM_DST_HSIZE;
+ return h;
+}
+
+static __inline__
+unsigned xfrm_dst_hash(xfrm_address_t *addr, unsigned short family)
+{
+ switch (family) {
+ case AF_INET:
+ return __xfrm4_dst_hash(addr);
+ case AF_INET6:
+ return __xfrm6_dst_hash(addr);
+ }
+ return 0;
+}
+
+static __inline__
+unsigned __xfrm4_spi_hash(xfrm_address_t *addr, u32 spi, u8 proto)
+{
+ unsigned h;
+ h = ntohl(addr->a4^spi^proto);
+ h = (h ^ (h>>10) ^ (h>>20)) % XFRM_DST_HSIZE;
+ return h;
+}
+
+static __inline__
+unsigned __xfrm6_spi_hash(xfrm_address_t *addr, u32 spi, u8 proto)
+{
+ unsigned h;
+ h = ntohl(addr->a6[2]^addr->a6[3]^spi^proto);
+ h = (h ^ (h>>10) ^ (h>>20)) % XFRM_DST_HSIZE;
+ return h;
+}
+
+static __inline__
+unsigned xfrm_spi_hash(xfrm_address_t *addr, u32 spi, u8 proto, unsigned short family)
+{
+ switch (family) {
+ case AF_INET:
+ return __xfrm4_spi_hash(addr, spi, proto);
+ case AF_INET6:
+ return __xfrm6_spi_hash(addr, spi, proto);
+ }
+ return 0; /*XXX*/
+}
+
extern void __xfrm_state_destroy(struct xfrm_state *);
static inline void xfrm_state_put(struct xfrm_state *x)
atomic_inc(&x->refcnt);
}
+static __inline__ int addr_match(void *token1, void *token2, int prefixlen)
+{
+ __u32 *a1 = token1;
+ __u32 *a2 = token2;
+ int pdw;
+ int pbi;
+
+ pdw = prefixlen >> 5; /* num of whole __u32 in prefix */
+ pbi = prefixlen & 0x1f; /* num of bits in incomplete u32 in prefix */
+
+ if (pdw)
+ if (memcmp(a1, a2, pdw << 2))
+ return 0;
+
+ if (pbi) {
+ __u32 mask;
+
+ mask = htonl((0xffffffff) << (32 - pbi));
+
+ if ((a1[pdw] ^ a2[pdw]) & mask)
+ return 0;
+ }
+
+ return 1;
+}
+
+static inline int
+__xfrm4_selector_match(struct xfrm_selector *sel, struct flowi *fl)
+{
+ return addr_match(&fl->fl4_dst, &sel->daddr, sel->prefixlen_d) &&
+ addr_match(&fl->fl4_src, &sel->saddr, sel->prefixlen_s) &&
+ !((fl->uli_u.ports.dport^sel->dport)&sel->dport_mask) &&
+ !((fl->uli_u.ports.sport^sel->sport)&sel->sport_mask) &&
+ (fl->proto == sel->proto || !sel->proto) &&
+ (fl->oif == sel->ifindex || !sel->ifindex);
+}
+
static inline int
-xfrm4_selector_match(struct xfrm_selector *sel, struct flowi *fl)
+__xfrm6_selector_match(struct xfrm_selector *sel, struct flowi *fl)
{
- return !((fl->fl4_dst^sel->daddr.xfrm4_addr)&sel->daddr.xfrm4_mask) &&
+ return addr_match(fl->fl6_dst, &sel->daddr, sel->prefixlen_d) &&
+ addr_match(fl->fl6_src, &sel->saddr, sel->prefixlen_s) &&
!((fl->uli_u.ports.dport^sel->dport)&sel->dport_mask) &&
!((fl->uli_u.ports.sport^sel->sport)&sel->sport_mask) &&
(fl->proto == sel->proto || !sel->proto) &&
- (fl->oif == sel->ifindex || !sel->ifindex) &&
- !((fl->fl4_src^sel->saddr.xfrm4_addr)&sel->saddr.xfrm4_mask);
+ (fl->oif == sel->ifindex || !sel->ifindex);
+}
+
+static inline int
+xfrm_selector_match(struct xfrm_selector *sel, 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 0;
}
/* A struct encoding bundle of transformations to apply to some set of flow.
struct sec_path
{
+ kmem_cache_t *pool;
atomic_t refcnt;
int len;
struct xfrm_state *xvec[XFRM_MAX_DEPTH];
if (sp && atomic_dec_and_test(&sp->refcnt))
__secpath_destroy(sp);
}
+
+static inline int
+__xfrm4_state_addr_cmp(struct xfrm_tmpl *tmpl, struct xfrm_state *x)
+{
+ return (tmpl->saddr.a4 &&
+ tmpl->saddr.a4 != x->props.saddr.a4);
+}
+
+static inline int
+__xfrm6_state_addr_cmp(struct xfrm_tmpl *tmpl, struct xfrm_state *x)
+{
+ return (!ipv6_addr_any((struct in6_addr*)&tmpl->saddr) &&
+ ipv6_addr_cmp((struct in6_addr *)&tmpl->saddr, (struct in6_addr*)&x->props.saddr));
+}
+
+static inline int
+xfrm_state_addr_cmp(struct xfrm_tmpl *tmpl, struct xfrm_state *x, unsigned short family)
+{
+ switch (family) {
+ case AF_INET:
+ return __xfrm4_state_addr_cmp(tmpl, x);
+ case AF_INET6:
+ return __xfrm6_state_addr_cmp(tmpl, x);
+ }
+ return !0;
+}
+
extern int __xfrm_policy_check(struct sock *, int dir, struct sk_buff *skb, unsigned short family);
-static inline int xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb)
+static inline int xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb, unsigned short family)
{
if (sk && sk->policy[XFRM_POLICY_IN])
- return __xfrm_policy_check(sk, dir, skb, AF_INET);
+ return __xfrm_policy_check(sk, dir, skb, family);
return !xfrm_policy_list[dir] ||
(skb->dst->flags & DST_NOPOLICY) ||
- __xfrm_policy_check(sk, dir, skb, AF_INET);
+ __xfrm_policy_check(sk, dir, skb, family);
+}
+
+static inline int xfrm4_policy_check(struct sock *sk, int dir, struct sk_buff *skb)
+{
+ return xfrm_policy_check(sk, dir, skb, AF_INET);
}
static inline int xfrm6_policy_check(struct sock *sk, int dir, struct sk_buff *skb)
{
- if (sk && sk->policy[XFRM_POLICY_IN])
- return __xfrm_policy_check(sk, dir, skb, AF_INET6);
-
- return !xfrm_policy_list[dir] ||
- (skb->dst->flags & DST_NOPOLICY) ||
- __xfrm_policy_check(sk, dir, skb, AF_INET6);
+ return xfrm_policy_check(sk, dir, skb, AF_INET6);
}
+
extern int __xfrm_route_forward(struct sk_buff *skb, unsigned short family);
-static inline int xfrm_route_forward(struct sk_buff *skb)
+static inline int xfrm_route_forward(struct sk_buff *skb, unsigned short family)
{
return !xfrm_policy_list[XFRM_POLICY_OUT] ||
(skb->dst->flags & DST_NOXFRM) ||
- __xfrm_route_forward(skb, AF_INET);
+ __xfrm_route_forward(skb, family);
+}
+
+static inline int xfrm4_route_forward(struct sk_buff *skb)
+{
+ return xfrm_route_forward(skb, AF_INET);
}
static inline int xfrm6_route_forward(struct sk_buff *skb)
{
- return !xfrm_policy_list[XFRM_POLICY_OUT] ||
- (skb->dst->flags & DST_NOXFRM) ||
- __xfrm_route_forward(skb, AF_INET6);
+ return xfrm_route_forward(skb, AF_INET6);
}
extern int __xfrm_sk_clone_policy(struct sock *sk);
}
}
+static __inline__
+xfrm_address_t *xfrm_flowi_daddr(struct flowi *fl, unsigned short family)
+{
+ switch (family){
+ case AF_INET:
+ return (xfrm_address_t *)&fl->fl4_dst;
+ case AF_INET6:
+ return (xfrm_address_t *)fl->fl6_dst;
+ }
+ return NULL;
+}
+
+static __inline__
+xfrm_address_t *xfrm_flowi_saddr(struct flowi *fl, unsigned short family)
+{
+ switch (family){
+ case AF_INET:
+ return (xfrm_address_t *)&fl->fl4_src;
+ case AF_INET6:
+ return (xfrm_address_t *)fl->fl6_src;
+ }
+ return NULL;
+}
+
+static __inline__ int
+__xfrm4_state_addr_check(struct xfrm_state *x,
+ xfrm_address_t *daddr, xfrm_address_t *saddr)
+{
+ if (daddr->a4 == x->id.daddr.a4 &&
+ (saddr->a4 == x->props.saddr.a4 || !saddr->a4 || !x->props.saddr.a4))
+ return 1;
+ return 0;
+}
+
+static __inline__ int
+__xfrm6_state_addr_check(struct xfrm_state *x,
+ xfrm_address_t *daddr, xfrm_address_t *saddr)
+{
+ if (!ipv6_addr_cmp((struct in6_addr *)daddr, (struct in6_addr *)&x->id.daddr) &&
+ (!ipv6_addr_cmp((struct in6_addr *)saddr, (struct in6_addr *)&x->props.saddr)||
+ ipv6_addr_any((struct in6_addr *)saddr) ||
+ ipv6_addr_any((struct in6_addr *)&x->props.saddr)))
+ return 1;
+ return 0;
+}
+
+static __inline__ int
+xfrm_state_addr_check(struct xfrm_state *x,
+ xfrm_address_t *daddr, xfrm_address_t *saddr,
+ unsigned short family)
+{
+ switch (family) {
+ case AF_INET:
+ return __xfrm4_state_addr_check(x, daddr, saddr);
+ case AF_INET6:
+ return __xfrm6_state_addr_check(x, daddr, saddr);
+ }
+ return 0;
+}
+
/*
* xfrm algorithm information
*/
struct sadb_alg desc;
};
+extern void xfrm_init(void);
+extern void xfrm4_init(void);
+extern void xfrm4_fini(void);
+extern void xfrm6_init(void);
+extern void xfrm6_fini(void);
extern void xfrm_state_init(void);
-extern void xfrm_input_init(void);
+extern void xfrm4_state_init(void);
+extern void xfrm4_state_fini(void);
+extern void xfrm6_state_init(void);
+extern void xfrm6_state_fini(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 struct xfrm_state *xfrm4_state_find(u32 daddr, u32 saddr, struct flowi *fl, struct xfrm_tmpl *tmpl,
- struct xfrm_policy *pol, int *err);
-extern struct xfrm_state *xfrm6_state_find(struct in6_addr *daddr, struct in6_addr *saddr,
- struct flowi *fl, struct xfrm_tmpl *tmpl,
- struct xfrm_policy *pol, int *err);
+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,
+ unsigned short family);
extern int xfrm_state_check_expire(struct xfrm_state *x);
extern void xfrm_state_insert(struct xfrm_state *x);
extern int xfrm_state_check_space(struct xfrm_state *x, struct sk_buff *skb);
-extern struct xfrm_state *xfrm4_state_lookup(u32 daddr, u32 spi, u8 proto);
-extern struct xfrm_state *xfrm6_state_lookup(struct in6_addr *daddr, u32 spi, u8 proto);
+extern struct xfrm_state *xfrm_state_lookup(xfrm_address_t *daddr, u32 spi, u8 proto, unsigned short family);
extern struct xfrm_state *xfrm_find_acq_byseq(u32 seq);
extern void xfrm_state_delete(struct xfrm_state *x);
extern void xfrm_state_flush(u8 proto);
extern int xfrm6_clear_mutable_options(struct sk_buff *skb, u16 *nh_offset, int dir);
extern int xfrm_user_policy(struct sock *sk, int optname, u8 *optval, int optlen);
+void xfrm_policy_init(void);
+void xfrm4_policy_init(void);
+void xfrm6_policy_init(void);
struct xfrm_policy *xfrm_policy_alloc(int gfp);
extern int xfrm_policy_walk(int (*func)(struct xfrm_policy *, int, int, void*), void *);
struct xfrm_policy *xfrm_policy_lookup(int dir, struct flowi *fl, unsigned short family);
struct xfrm_policy *xfrm_policy_byid(int dir, u32 id, int delete);
void xfrm_policy_flush(void);
void xfrm_alloc_spi(struct xfrm_state *x, u32 minspi, u32 maxspi);
-struct xfrm_state * xfrm_find_acq(u8 mode, u16 reqid, u8 proto, u32 daddr, u32 saddr, int create);
-struct xfrm_state * xfrm6_find_acq(u8 mode, u16 reqid, u8 proto, struct in6_addr *daddr,
- struct in6_addr *saddr, int create);
+struct xfrm_state * xfrm_find_acq(u8 mode, u16 reqid, u8 proto,
+ xfrm_address_t *daddr, xfrm_address_t *saddr,
+ int create, unsigned short family);
extern void xfrm_policy_flush(void);
extern void xfrm_policy_kill(struct xfrm_policy *);
extern int xfrm_sk_policy_insert(struct sock *sk, int dir, struct xfrm_policy *pol);
extern struct xfrm_policy *xfrm_sk_policy_lookup(struct sock *sk, int dir, struct flowi *fl);
extern int xfrm_flush_bundles(struct xfrm_state *x);
+extern int xfrm_dst_lookup(struct xfrm_dst **dst, struct flowi *fl, unsigned short family);
extern wait_queue_head_t km_waitq;
extern void km_warn_expired(struct xfrm_state *x);
extern void km_expired(struct xfrm_state *x);
extern int km_query(struct xfrm_state *x, struct xfrm_tmpl *, struct xfrm_policy *pol);
+extern void xfrm4_input_init(void);
+extern void xfrm6_input_init(void);
+extern int xfrm_parse_spi(struct sk_buff *skb, u8 nexthdr, u32 *spi, u32 *seq);
+
extern void xfrm_probe_algs(void);
extern int xfrm_count_auth_supported(void);
extern int xfrm_count_enc_supported(void);
extern struct xfrm_algo_desc *xfrm_ealg_get_byname(char *name);
extern struct xfrm_algo_desc *xfrm_calg_get_byname(char *name);
-static __inline__ int addr_match(void *token1, void *token2, int prefixlen)
-{
- __u32 *a1 = token1;
- __u32 *a2 = token2;
- int pdw;
- int pbi;
-
- pdw = prefixlen >> 5; /* num of whole __u32 in prefix */
- pbi = prefixlen & 0x1f; /* num of bits in incomplete u32 in prefix */
-
- if (pdw)
- if (memcmp(a1, a2, pdw << 2))
- return 0;
-
- if (pbi) {
- __u32 mask;
-
- mask = htonl((0xffffffff) << (32 - pbi));
-
- if ((a1[pdw] ^ a2[pdw]) & mask)
- return 0;
- }
-
- return 1;
-}
-
-static inline int
-xfrm6_selector_match(struct xfrm_selector *sel, struct flowi *fl)
-{
- return addr_match(fl->fl6_dst, &sel->daddr, sel->prefixlen_d) &&
- addr_match(fl->fl6_src, &sel->saddr, sel->prefixlen_s) &&
- !((fl->uli_u.ports.dport^sel->dport)&sel->dport_mask) &&
- !((fl->uli_u.ports.sport^sel->sport)&sel->sport_mask) &&
- (fl->proto == sel->proto || !sel->proto) &&
- (fl->oif == sel->ifindex || !sel->ifindex);
-}
-
-extern int xfrm6_register_type(struct xfrm_type *type);
-extern int xfrm6_unregister_type(struct xfrm_type *type);
-extern struct xfrm_type *xfrm6_get_type(u8 proto);
-
struct crypto_tfm;
typedef void (icv_update_fn_t)(struct crypto_tfm *, struct scatterlist *, unsigned int);
-typedef int (xfrm_dst_lookup_t)(struct xfrm_dst **dst, struct flowi *fl);
-int xfrm_dst_lookup_register(xfrm_dst_lookup_t *dst_lookup, unsigned short family);
-void xfrm_dst_lookup_unregister(unsigned short family);
-
#endif /* _NET_XFRM_H */
obj-$(CONFIG_NETFILTER) += netfilter/
obj-$(CONFIG_XFRM_USER) += xfrm_user.o
-obj-y += xfrm_policy.o xfrm_state.o xfrm_input.o xfrm_algo.o
+obj-y += xfrm_policy.o xfrm4_policy.o xfrm_state.o xfrm4_state.o xfrm_input.o xfrm4_input.o xfrm_algo.o
top_iph->ttl = 0;
top_iph->protocol = IPPROTO_AH;
top_iph->check = 0;
- top_iph->saddr = x->props.saddr.xfrm4_addr;
- top_iph->daddr = x->id.daddr.xfrm4_addr;
+ top_iph->saddr = x->props.saddr.a4;
+ top_iph->daddr = x->id.daddr.a4;
ah = (struct ip_auth_hdr*)(top_iph+1);
ah->nexthdr = IPPROTO_IPIP;
} else {
skb->h.icmph->code != ICMP_FRAG_NEEDED)
return;
- x = xfrm4_state_lookup(iph->daddr, ah->spi, IPPROTO_AH);
+ x = xfrm_state_lookup((xfrm_address_t *)&iph->daddr, ah->spi, IPPROTO_AH, AF_INET);
if (!x)
return;
printk(KERN_DEBUG "pmtu discvovery on SA AH/%08x/%08x\n",
static int __init ah4_init(void)
{
SET_MODULE_OWNER(&ah_type);
- if (xfrm_register_type(&ah_type) < 0) {
+ if (xfrm_register_type(&ah_type, AF_INET) < 0) {
printk(KERN_INFO "ip ah init: can't add xfrm type\n");
return -EAGAIN;
}
if (inet_add_protocol(&ah4_protocol, IPPROTO_AH) < 0) {
printk(KERN_INFO "ip ah init: can't add protocol\n");
- xfrm_unregister_type(&ah_type);
+ xfrm_unregister_type(&ah_type, AF_INET);
return -EAGAIN;
}
return 0;
{
if (inet_del_protocol(&ah4_protocol, IPPROTO_AH) < 0)
printk(KERN_INFO "ip ah close: can't remove protocol\n");
- if (xfrm_unregister_type(&ah_type) < 0)
+ if (xfrm_unregister_type(&ah_type, AF_INET) < 0)
printk(KERN_INFO "ip ah close: can't remove xfrm type\n");
}
top_iph->ttl = iph->ttl; /* TTL disclosed */
top_iph->protocol = IPPROTO_ESP;
top_iph->check = 0;
- top_iph->saddr = x->props.saddr.xfrm4_addr;
- top_iph->daddr = x->id.daddr.xfrm4_addr;
+ top_iph->saddr = x->props.saddr.a4;
+ top_iph->daddr = x->id.daddr.a4;
memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options));
} else {
esph = (struct ip_esp_hdr*)skb_push(skb, x->props.header_len);
skb->h.icmph->code != ICMP_FRAG_NEEDED)
return;
- x = xfrm4_state_lookup(iph->daddr, esph->spi, IPPROTO_ESP);
+ x = xfrm_state_lookup((xfrm_address_t *)&iph->daddr, esph->spi, IPPROTO_ESP, AF_INET);
if (!x)
return;
printk(KERN_DEBUG "pmtu discvovery on SA ESP/%08x/%08x\n",
int __init esp4_init(void)
{
SET_MODULE_OWNER(&esp_type);
- if (xfrm_register_type(&esp_type) < 0) {
+ if (xfrm_register_type(&esp_type, AF_INET) < 0) {
printk(KERN_INFO "ip esp init: can't add xfrm type\n");
return -EAGAIN;
}
if (inet_add_protocol(&esp4_protocol, IPPROTO_ESP) < 0) {
printk(KERN_INFO "ip esp init: can't add protocol\n");
- xfrm_unregister_type(&esp_type);
+ xfrm_unregister_type(&esp_type, AF_INET);
return -EAGAIN;
}
return 0;
{
if (inet_del_protocol(&esp4_protocol, IPPROTO_ESP) < 0)
printk(KERN_INFO "ip esp close: can't remove protocol\n");
- if (xfrm_unregister_type(&esp_type) < 0)
+ if (xfrm_unregister_type(&esp_type, AF_INET) < 0)
printk(KERN_INFO "ip esp close: can't remove xfrm type\n");
}
struct rtable *rt; /* Route we use */
struct ip_options * opt = &(IPCB(skb)->opt);
- if (!xfrm_policy_check(NULL, XFRM_POLICY_FWD, skb))
+ if (!xfrm4_policy_check(NULL, XFRM_POLICY_FWD, skb))
goto drop;
if (IPCB(skb)->opt.router_alert && ip_call_ra_chain(skb))
if (iph->ttl <= 1)
goto too_many_hops;
- if (!xfrm_route_forward(skb))
+ if (!xfrm4_route_forward(skb))
goto drop;
iph = skb->nh.iph;
int ret;
if (!ipprot->no_policy &&
- !xfrm_policy_check(NULL, XFRM_POLICY_IN, skb)) {
+ !xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) {
kfree_skb(skb);
return 0;
}
IP_INC_STATS_BH(IpInDelivers);
} else {
if (!raw_sk) {
- if (xfrm_policy_check(NULL, XFRM_POLICY_IN, skb)) {
+ if (xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) {
IP_INC_STATS_BH(IpInUnknownProtos);
icmp_send(skb, ICMP_DEST_UNREACH,
ICMP_PROT_UNREACH, 0);
int raw_rcv(struct sock *sk, struct sk_buff *skb)
{
- if (!xfrm_policy_check(sk, XFRM_POLICY_IN, skb)) {
+ if (!xfrm4_policy_check(sk, XFRM_POLICY_IN, skb)) {
kfree_skb(skb);
return NET_RX_DROP;
}
#endif /* CONFIG_PROC_FS */
#endif /* CONFIG_NET_CLS_ROUTE */
-int xfrm_dst_lookup(struct xfrm_dst **dst, struct flowi *fl)
-{
- int err = 0;
- err = __ip_route_output_key((struct rtable**)dst, fl);
- return err;
-}
-
int __init ip_rt_init(void)
{
int i, order, goal, rc = 0;
ip_rt_gc_interval;
add_timer(&rt_periodic_timer);
- xfrm_dst_lookup_register(xfrm_dst_lookup, AF_INET);
#ifdef CONFIG_PROC_FS
if (rt_cache_proc_init())
goto out_enomem;
#endif
#endif
xfrm_init();
+ xfrm4_init();
out:
return rc;
out_enomem:
if (sk->state == TCP_TIME_WAIT)
goto do_time_wait;
- if (!xfrm_policy_check(sk, XFRM_POLICY_IN, skb))
+ if (!xfrm4_policy_check(sk, XFRM_POLICY_IN, skb))
goto discard_and_relse;
if (sk_filter(sk, skb, 0))
return ret;
no_tcp_socket:
- if (!xfrm_policy_check(NULL, XFRM_POLICY_IN, skb))
+ if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb))
goto discard_it;
if (skb->len < (th->doff << 2) || tcp_checksum_complete(skb)) {
goto discard_it;
do_time_wait:
- if (!xfrm_policy_check(NULL, XFRM_POLICY_IN, skb))
+ if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb))
goto discard_and_relse;
if (skb->len < (th->doff << 2) || tcp_checksum_complete(skb)) {
/*
* Charge it to the socket, dropping if the queue is full.
*/
- if (!xfrm_policy_check(sk, XFRM_POLICY_IN, skb)) {
+ if (!xfrm4_policy_check(sk, XFRM_POLICY_IN, skb)) {
kfree_skb(skb);
return -1;
}
return 0;
}
- if (!xfrm_policy_check(NULL, XFRM_POLICY_IN, skb))
+ if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb))
goto drop;
/* No socket. Drop packet silently, if checksum is wrong */
--- /dev/null
+/*
+ * xfrm4_input.c
+ *
+ * Changes:
+ * YOSHIFUJI Hideaki @USAGI
+ * Split up af-specific portion
+ *
+ */
+
+#include <net/ip.h>
+#include <net/xfrm.h>
+
+static kmem_cache_t *secpath_cachep;
+
+int xfrm4_rcv(struct sk_buff *skb)
+{
+ int err;
+ u32 spi, seq;
+ struct xfrm_state *xfrm_vec[XFRM_MAX_DEPTH];
+ struct xfrm_state *x;
+ int xfrm_nr = 0;
+ int decaps = 0;
+
+ if ((err = xfrm_parse_spi(skb, skb->nh.iph->protocol, &spi, &seq)) != 0)
+ goto drop;
+
+ do {
+ struct iphdr *iph = skb->nh.iph;
+
+ if (xfrm_nr == XFRM_MAX_DEPTH)
+ goto drop;
+
+ x = xfrm_state_lookup((xfrm_address_t *)&iph->daddr, spi, iph->protocol, AF_INET);
+ if (x == NULL)
+ goto drop;
+
+ spin_lock(&x->lock);
+ if (unlikely(x->km.state != XFRM_STATE_VALID))
+ goto drop_unlock;
+
+ if (x->props.replay_window && xfrm_replay_check(x, seq))
+ goto drop_unlock;
+
+ if (x->type->input(x, skb))
+ goto drop_unlock;
+
+ if (x->props.replay_window)
+ xfrm_replay_advance(x, seq);
+
+ x->curlft.bytes += skb->len;
+ x->curlft.packets++;
+
+ spin_unlock(&x->lock);
+
+ xfrm_vec[xfrm_nr++] = x;
+
+ iph = skb->nh.iph;
+
+ if (x->props.mode) {
+ if (iph->protocol != IPPROTO_IPIP)
+ goto drop;
+ skb->nh.raw = skb->data;
+ iph = skb->nh.iph;
+ memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options));
+ decaps = 1;
+ break;
+ }
+
+ if ((err = xfrm_parse_spi(skb, skb->nh.iph->protocol, &spi, &seq)) < 0)
+ goto drop;
+ } while (!err);
+
+ /* Allocate new secpath or COW existing one. */
+
+ if (!skb->sp || atomic_read(&skb->sp->refcnt) != 1) {
+ kmem_cache_t *pool = skb->sp ? skb->sp->pool : secpath_cachep;
+ struct sec_path *sp;
+ sp = kmem_cache_alloc(pool, SLAB_ATOMIC);
+ if (!sp)
+ goto drop;
+ if (skb->sp) {
+ memcpy(sp, skb->sp, sizeof(struct sec_path));
+ secpath_put(skb->sp);
+ } else {
+ sp->pool = pool;
+ sp->len = 0;
+ }
+ atomic_set(&sp->refcnt, 1);
+ skb->sp = sp;
+ }
+ if (xfrm_nr + skb->sp->len > XFRM_MAX_DEPTH)
+ goto drop;
+
+ memcpy(skb->sp->xvec+skb->sp->len, xfrm_vec, xfrm_nr*sizeof(void*));
+ skb->sp->len += xfrm_nr;
+
+ if (decaps) {
+ if (!(skb->dev->flags&IFF_LOOPBACK)) {
+ dst_release(skb->dst);
+ skb->dst = NULL;
+ }
+ netif_rx(skb);
+ return 0;
+ } else {
+ return -skb->nh.iph->protocol;
+ }
+
+drop_unlock:
+ spin_unlock(&x->lock);
+ xfrm_state_put(x);
+drop:
+ while (--xfrm_nr >= 0)
+ xfrm_state_put(xfrm_vec[xfrm_nr]);
+ kfree_skb(skb);
+ return 0;
+}
+
+
+void __init xfrm4_input_init(void)
+{
+ secpath_cachep = kmem_cache_create("secpath4_cache",
+ sizeof(struct sec_path),
+ 0, SLAB_HWCACHE_ALIGN,
+ NULL, NULL);
+
+ if (!secpath_cachep)
+ panic("IP: failed to allocate secpath4_cache\n");
+}
+
--- /dev/null
+/*
+ * xfrm4_policy.c
+ *
+ * Changes:
+ * Kazunori MIYAZAWA @USAGI
+ * YOSHIFUJI Hideaki @USAGI
+ * Split up af-specific portion
+ *
+ */
+
+#include <linux/config.h>
+#include <net/xfrm.h>
+#include <net/ip.h>
+
+extern struct dst_ops xfrm4_dst_ops;
+extern struct xfrm_policy_afinfo xfrm4_policy_afinfo;
+
+static struct xfrm_type_map xfrm4_type_map = { .lock = RW_LOCK_UNLOCKED };
+
+static int xfrm4_dst_lookup(struct xfrm_dst **dst, struct flowi *fl)
+{
+ return __ip_route_output_key((struct rtable**)dst, fl);
+}
+
+/* Check that the bundle accepts the flow and its components are
+ * still valid.
+ */
+
+static int __xfrm4_bundle_ok(struct xfrm_dst *xdst, struct flowi *fl)
+{
+ do {
+ if (xdst->u.dst.ops != &xfrm4_dst_ops)
+ return 1;
+
+ if (!xfrm_selector_match(&xdst->u.dst.xfrm->sel, fl, AF_INET))
+ return 0;
+ if (xdst->u.dst.xfrm->km.state != XFRM_STATE_VALID ||
+ xdst->u.dst.path->obsolete > 0)
+ return 0;
+ xdst = (struct xfrm_dst*)xdst->u.dst.child;
+ } while (xdst);
+ return 0;
+}
+
+static struct dst_entry *
+__xfrm4_find_bundle(struct flowi *fl, struct rtable *rt, struct xfrm_policy *policy)
+{
+ struct dst_entry *dst;
+
+ if (!fl->fl4_src)
+ fl->fl4_src = rt->rt_src;
+ if (!fl->fl4_dst)
+ fl->fl4_dst = rt->rt_dst;
+ read_lock_bh(&policy->lock);
+ for (dst = policy->bundles; dst; dst = dst->next) {
+ struct xfrm_dst *xdst = (struct xfrm_dst*)dst;
+ if (xdst->u.rt.fl.oif == fl->oif && /*XXX*/
+ xdst->u.rt.fl.fl4_dst == fl->fl4_dst &&
+ xdst->u.rt.fl.fl4_src == fl->fl4_src &&
+ __xfrm4_bundle_ok(xdst, fl)) {
+ dst_clone(dst);
+ break;
+ }
+ }
+ read_unlock_bh(&policy->lock);
+ return dst;
+}
+
+/* Allocate chain of dst_entry's, attach known xfrm's, calculate
+ * all the metrics... Shortly, bundle a bundle.
+ */
+
+static int
+__xfrm4_bundle_create(struct xfrm_policy *policy, struct xfrm_state **xfrm, int nx,
+ struct flowi *fl, struct dst_entry **dst_p)
+{
+ struct dst_entry *dst, *dst_prev;
+ struct rtable *rt0 = (struct rtable*)(*dst_p);
+ struct rtable *rt = rt0;
+ u32 remote = fl->fl4_dst;
+ u32 local = fl->fl4_src;
+ int i;
+ int err;
+ int header_len = 0;
+ int trailer_len = 0;
+
+ dst = dst_prev = NULL;
+
+ for (i = 0; i < nx; i++) {
+ struct dst_entry *dst1 = dst_alloc(&xfrm4_dst_ops);
+
+ if (unlikely(dst1 == NULL)) {
+ err = -ENOBUFS;
+ goto error;
+ }
+
+ dst1->xfrm = xfrm[i];
+ if (!dst)
+ dst = dst1;
+ else {
+ dst_prev->child = dst1;
+ dst1->flags |= DST_NOHASH;
+ dst_clone(dst1);
+ }
+ dst_prev = dst1;
+ if (xfrm[i]->props.mode) {
+ remote = xfrm[i]->id.daddr.a4;
+ local = xfrm[i]->props.saddr.a4;
+ }
+ header_len += xfrm[i]->props.header_len;
+ trailer_len += xfrm[i]->props.trailer_len;
+ }
+
+ if (remote != fl->fl4_dst) {
+ struct flowi fl_tunnel = { .nl_u = { .ip4_u =
+ { .daddr = remote,
+ .saddr = local }
+ }
+ };
+ err = xfrm_dst_lookup((struct xfrm_dst**)&rt, &fl_tunnel, AF_INET);
+ if (err)
+ goto error;
+ } else {
+ dst_hold(&rt->u.dst);
+ }
+ dst_prev->child = &rt->u.dst;
+ for (dst_prev = dst; dst_prev != &rt->u.dst; dst_prev = dst_prev->child) {
+ struct xfrm_dst *x = (struct xfrm_dst*)dst_prev;
+ x->u.rt.fl = *fl;
+
+ dst_prev->dev = rt->u.dst.dev;
+ if (rt->u.dst.dev)
+ dev_hold(rt->u.dst.dev);
+ dst_prev->obsolete = -1;
+ dst_prev->flags |= DST_HOST;
+ dst_prev->lastuse = jiffies;
+ dst_prev->header_len = header_len;
+ dst_prev->trailer_len = trailer_len;
+ memcpy(&dst_prev->metrics, &rt->u.dst.metrics, sizeof(dst_prev->metrics));
+ dst_prev->path = &rt->u.dst;
+
+ /* Copy neighbout for reachability confirmation */
+ dst_prev->neighbour = neigh_clone(rt->u.dst.neighbour);
+ dst_prev->input = rt->u.dst.input;
+ dst_prev->output = dst_prev->xfrm->type->output;
+ if (rt->peer)
+ atomic_inc(&rt->peer->refcnt);
+ x->u.rt.peer = rt->peer;
+ /* Sheit... I remember I did this right. Apparently,
+ * it was magically lost, so this code needs audit */
+ x->u.rt.rt_flags = rt0->rt_flags&(RTCF_BROADCAST|RTCF_MULTICAST|RTCF_LOCAL);
+ x->u.rt.rt_type = rt->rt_type;
+ x->u.rt.rt_src = rt0->rt_src;
+ x->u.rt.rt_dst = rt0->rt_dst;
+ x->u.rt.rt_gateway = rt->rt_gateway;
+ x->u.rt.rt_spec_dst = rt0->rt_spec_dst;
+ header_len -= x->u.dst.xfrm->props.header_len;
+ trailer_len -= x->u.dst.xfrm->props.trailer_len;
+ }
+ *dst_p = dst;
+ return 0;
+
+error:
+ if (dst)
+ dst_free(dst);
+ return err;
+}
+
+static void
+_decode_session4(struct sk_buff *skb, struct flowi *fl)
+{
+ struct iphdr *iph = skb->nh.iph;
+ u8 *xprth = skb->nh.raw + iph->ihl*4;
+
+ if (!(iph->frag_off & htons(IP_MF | IP_OFFSET))) {
+ switch (iph->protocol) {
+ case IPPROTO_UDP:
+ case IPPROTO_TCP:
+ case IPPROTO_SCTP:
+ if (pskb_may_pull(skb, xprth + 4 - skb->data)) {
+ u16 *ports = (u16 *)xprth;
+
+ fl->uli_u.ports.sport = ports[0];
+ fl->uli_u.ports.dport = ports[1];
+ }
+ break;
+
+ case IPPROTO_ESP:
+ if (pskb_may_pull(skb, xprth + 4 - skb->data)) {
+ u32 *ehdr = (u32 *)xprth;
+
+ fl->uli_u.spi = ehdr[0];
+ }
+ break;
+
+ case IPPROTO_AH:
+ if (pskb_may_pull(skb, xprth + 8 - skb->data)) {
+ u32 *ah_hdr = (u32*)xprth;
+
+ fl->uli_u.spi = ah_hdr[1];
+ }
+ break;
+
+ default:
+ fl->uli_u.spi = 0;
+ break;
+ };
+ } else {
+ memset(fl, 0, sizeof(struct flowi));
+ }
+ fl->proto = iph->protocol;
+ fl->fl4_dst = iph->daddr;
+ fl->fl4_src = iph->saddr;
+}
+
+static inline int xfrm4_garbage_collect(void)
+{
+ read_lock(&xfrm4_policy_afinfo.lock);
+ xfrm4_policy_afinfo.garbage_collect();
+ read_unlock(&xfrm4_policy_afinfo.lock);
+ return (atomic_read(&xfrm4_dst_ops.entries) > xfrm4_dst_ops.gc_thresh*2);
+}
+
+static void xfrm4_update_pmtu(struct dst_entry *dst, u32 mtu)
+{
+ struct dst_entry *path = dst->path;
+
+ if (mtu < 68 + dst->header_len)
+ return;
+
+ path->ops->update_pmtu(path, mtu);
+}
+
+struct dst_ops xfrm4_dst_ops = {
+ .family = AF_INET,
+ .protocol = __constant_htons(ETH_P_IP),
+ .gc = xfrm4_garbage_collect,
+ .update_pmtu = xfrm4_update_pmtu,
+ .gc_thresh = 1024,
+ .entry_size = sizeof(struct xfrm_dst),
+};
+
+struct xfrm_policy_afinfo xfrm4_policy_afinfo = {
+ .family = AF_INET,
+ .lock = RW_LOCK_UNLOCKED,
+ .type_map = &xfrm4_type_map,
+ .dst_ops = &xfrm4_dst_ops,
+ .dst_lookup = xfrm4_dst_lookup,
+ .find_bundle = __xfrm4_find_bundle,
+ .bundle_create = __xfrm4_bundle_create,
+ .decode_session = _decode_session4,
+};
+
+void __init xfrm4_policy_init(void)
+{
+ xfrm_policy_register_afinfo(&xfrm4_policy_afinfo);
+}
+
+void __exit xfrm4_policy_fini(void)
+{
+ xfrm_policy_unregister_afinfo(&xfrm4_policy_afinfo);
+}
+
+void __init xfrm4_init(void)
+{
+ xfrm4_state_init();
+ xfrm4_policy_init();
+ xfrm4_input_init();
+}
+
+void __exit xfrm4_fini(void)
+{
+ //xfrm4_input_fini();
+ xfrm4_policy_fini();
+ xfrm4_state_fini();
+}
+
--- /dev/null
+/*
+ * xfrm4_state.c
+ *
+ * Changes:
+ * YOSHIFUJI Hideaki @USAGI
+ * Split up af-specific portion
+ *
+ */
+
+#include <net/xfrm.h>
+#include <linux/pfkeyv2.h>
+#include <linux/ipsec.h>
+
+extern struct xfrm_state_afinfo xfrm4_state_afinfo;
+
+static void
+__xfrm4_init_tempsel(struct xfrm_state *x, struct flowi *fl,
+ struct xfrm_tmpl *tmpl,
+ xfrm_address_t *daddr, xfrm_address_t *saddr)
+{
+ x->sel.daddr.a4 = fl->fl4_dst;
+ x->sel.saddr.a4 = fl->fl4_src;
+ x->sel.dport = fl->uli_u.ports.dport;
+ x->sel.dport_mask = ~0;
+ x->sel.sport = fl->uli_u.ports.sport;
+ x->sel.sport_mask = ~0;
+ x->sel.prefixlen_d = 32;
+ x->sel.prefixlen_s = 32;
+ x->sel.proto = fl->proto;
+ x->sel.ifindex = fl->oif;
+ x->id = tmpl->id;
+ if (x->id.daddr.a4 == 0)
+ x->id.daddr.a4 = daddr->a4;
+ x->props.saddr = tmpl->saddr;
+ if (x->props.saddr.a4 == 0)
+ x->props.saddr.a4 = saddr->a4;
+ x->props.mode = tmpl->mode;
+ x->props.reqid = tmpl->reqid;
+ x->props.family = AF_INET;
+}
+
+static struct xfrm_state *
+__xfrm4_state_lookup(xfrm_address_t *daddr, u32 spi, u8 proto)
+{
+ unsigned h = __xfrm4_spi_hash(daddr, spi, proto);
+ struct xfrm_state *x;
+
+ list_for_each_entry(x, xfrm4_state_afinfo.state_byspi+h, byspi) {
+ if (x->props.family == AF_INET &&
+ spi == x->id.spi &&
+ daddr->a4 == x->id.daddr.a4 &&
+ proto == x->id.proto) {
+ atomic_inc(&x->refcnt);
+ return x;
+ }
+ }
+ return NULL;
+}
+
+static struct xfrm_state *
+__xfrm4_find_acq(u8 mode, u16 reqid, u8 proto,
+ xfrm_address_t *daddr, xfrm_address_t *saddr,
+ int create)
+{
+ struct xfrm_state *x, *x0;
+ unsigned h = __xfrm4_dst_hash(daddr);
+
+ x0 = NULL;
+
+ list_for_each_entry(x, xfrm4_state_afinfo.state_bydst+h, bydst) {
+ if (x->props.family == AF_INET &&
+ daddr->a4 == x->id.daddr.a4 &&
+ mode == x->props.mode &&
+ proto == x->id.proto &&
+ saddr->a4 == x->props.saddr.a4 &&
+ reqid == x->props.reqid &&
+ x->km.state == XFRM_STATE_ACQ) {
+ if (!x0)
+ x0 = x;
+ if (x->id.spi)
+ continue;
+ x0 = x;
+ break;
+ }
+ }
+ if (x0) {
+ atomic_inc(&x0->refcnt);
+ } else if (create && (x0 = xfrm_state_alloc()) != NULL) {
+ x0->sel.daddr.a4 = daddr->a4;
+ x0->sel.saddr.a4 = saddr->a4;
+ x0->sel.prefixlen_d = 32;
+ x0->sel.prefixlen_s = 32;
+ x0->props.saddr.a4 = saddr->a4;
+ x0->km.state = XFRM_STATE_ACQ;
+ x0->id.daddr.a4 = daddr->a4;
+ x0->id.proto = proto;
+ x0->props.family = AF_INET;
+ x0->props.mode = mode;
+ x0->props.reqid = reqid;
+ x0->props.family = AF_INET;
+ x0->lft.hard_add_expires_seconds = XFRM_ACQ_EXPIRES;
+ atomic_inc(&x0->refcnt);
+ mod_timer(&x0->timer, jiffies + XFRM_ACQ_EXPIRES*HZ);
+ atomic_inc(&x0->refcnt);
+ list_add_tail(&x0->bydst, xfrm4_state_afinfo.state_bydst+h);
+ wake_up(&km_waitq);
+ }
+ return x0;
+}
+
+static struct xfrm_state_afinfo xfrm4_state_afinfo = {
+ .family = AF_INET,
+ .lock = RW_LOCK_UNLOCKED,
+ .init_tempsel = __xfrm4_init_tempsel,
+ .state_lookup = __xfrm4_state_lookup,
+ .find_acq = __xfrm4_find_acq,
+};
+
+void __init xfrm4_state_init(void)
+{
+ xfrm_state_register_afinfo(&xfrm4_state_afinfo);
+}
+
+void __exit xfrm4_state_fini(void)
+{
+ xfrm_state_unregister_afinfo(&xfrm4_state_afinfo);
+}
+
-/* Changes
+/*
+ * xfrm_input.c
*
- * Mitsuru KANDA @USAGI : IPv6 Support
- * Kazunori MIYAZAWA @USAGI :
- * YOSHIFUJI Hideaki @USAGI :
- * Kunihiro Ishiguro :
+ * Changes:
+ * YOSHIFUJI Hideaki @USAGI
+ * Split up af-specific portion
*
*/
#include <net/ip.h>
-#include <net/ipv6.h>
#include <net/xfrm.h>
-static kmem_cache_t *secpath_cachep;
-
void __secpath_destroy(struct sec_path *sp)
{
int i;
for (i = 0; i < sp->len; i++)
xfrm_state_put(sp->xvec[i]);
- kmem_cache_free(secpath_cachep, sp);
+ kmem_cache_free(sp->pool, sp);
}
/* Fetch spi and seq frpm ipsec header */
-static int xfrm_parse_spi(struct sk_buff *skb, u32 *spi, u32 *seq)
+int xfrm_parse_spi(struct sk_buff *skb, u8 nexthdr, u32 *spi, u32 *seq)
{
int offset, offset_seq;
*seq = *(u32*)(skb->h.raw + offset_seq);
return 0;
}
-
-
-
-int xfrm4_rcv(struct sk_buff *skb)
-{
- int err;
- u32 spi, seq;
- struct xfrm_state *xfrm_vec[XFRM_MAX_DEPTH];
- struct xfrm_state *x;
- int xfrm_nr = 0;
- int decaps = 0;
-
- if ((err = xfrm_parse_spi(skb, &spi, &seq)) != 0)
- goto drop;
-
- do {
- struct iphdr *iph = skb->nh.iph;
-
- if (xfrm_nr == XFRM_MAX_DEPTH)
- goto drop;
-
- x = xfrm4_state_lookup(iph->daddr, spi, iph->protocol);
- if (x == NULL)
- goto drop;
-
- spin_lock(&x->lock);
- if (unlikely(x->km.state != XFRM_STATE_VALID))
- goto drop_unlock;
-
- if (x->props.replay_window && xfrm_replay_check(x, seq))
- goto drop_unlock;
-
- if (x->type->input(x, skb))
- goto drop_unlock;
-
- if (x->props.replay_window)
- xfrm_replay_advance(x, seq);
-
- x->curlft.bytes += skb->len;
- x->curlft.packets++;
-
- spin_unlock(&x->lock);
-
- xfrm_vec[xfrm_nr++] = x;
-
- iph = skb->nh.iph;
-
- if (x->props.mode) {
- if (iph->protocol != IPPROTO_IPIP)
- goto drop;
- skb->nh.raw = skb->data;
- iph = skb->nh.iph;
- memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options));
- decaps = 1;
- break;
- }
-
- if ((err = xfrm_parse_spi(skb, &spi, &seq)) < 0)
- 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 = kmem_cache_alloc(secpath_cachep, SLAB_ATOMIC);
- if (!sp)
- goto drop;
- if (skb->sp) {
- memcpy(sp, skb->sp, sizeof(struct sec_path));
- secpath_put(skb->sp);
- } else
- sp->len = 0;
- atomic_set(&sp->refcnt, 1);
- skb->sp = sp;
- }
- if (xfrm_nr + skb->sp->len > XFRM_MAX_DEPTH)
- goto drop;
-
- memcpy(skb->sp->xvec+skb->sp->len, xfrm_vec, xfrm_nr*sizeof(void*));
- skb->sp->len += xfrm_nr;
-
- if (decaps) {
- if (!(skb->dev->flags&IFF_LOOPBACK)) {
- dst_release(skb->dst);
- skb->dst = NULL;
- }
- netif_rx(skb);
- return 0;
- } else {
- return -skb->nh.iph->protocol;
- }
-
-drop_unlock:
- spin_unlock(&x->lock);
- xfrm_state_put(x);
-drop:
- while (--xfrm_nr >= 0)
- xfrm_state_put(xfrm_vec[xfrm_nr]);
- kfree_skb(skb);
- return 0;
-}
-
-
-void __init xfrm_input_init(void)
-{
- secpath_cachep = kmem_cache_create("secpath_cache",
- sizeof(struct sec_path),
- 0, SLAB_HWCACHE_ALIGN,
- NULL, NULL);
-
- if (!secpath_cachep)
- panic("IP: failed to allocate secpath_cache\n");
-}
-
-#if defined (CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
-
-/* Fetch spi and seq frpm ipsec header */
-
-static int xfrm6_parse_spi(struct sk_buff *skb, u8 nexthdr, u32 *spi, u32 *seq)
-{
- int offset, offset_seq;
-
- switch (nexthdr) {
- case IPPROTO_AH:
- offset = offsetof(struct ip_auth_hdr, spi);
- offset_seq = offsetof(struct ip_auth_hdr, seq_no);
- break;
- case IPPROTO_ESP:
- offset = offsetof(struct ip_esp_hdr, spi);
- offset_seq = offsetof(struct ip_esp_hdr, seq_no);
- break;
- case IPPROTO_COMP:
- if (!pskb_may_pull(skb, 4))
- return -EINVAL;
- *spi = ntohl(ntohs(*(u16*)(skb->h.raw + 2)));
- *seq = 0;
- return 0;
- default:
- return 1;
- }
-
- if (!pskb_may_pull(skb, 16))
- return -EINVAL;
-
- *spi = *(u32*)(skb->h.raw + offset);
- *seq = *(u32*)(skb->h.raw + offset_seq);
- return 0;
-}
-
-static int zero_out_mutable_opts(struct ipv6_opt_hdr *opthdr)
-{
- u8 *opt = (u8 *)opthdr;
- int len = ipv6_optlen(opthdr);
- int off = 0;
- int optlen = 0;
-
- off += 2;
- len -= 2;
-
- while (len > 0) {
-
- switch (opt[off]) {
-
- case IPV6_TLV_PAD0:
- optlen = 1;
- break;
- default:
- if (len < 2)
- goto bad;
- optlen = opt[off+1]+2;
- if (len < optlen)
- goto bad;
- if (opt[off] & 0x20)
- memset(&opt[off+2], 0, opt[off+1]);
- break;
- }
-
- off += optlen;
- len -= optlen;
- }
- if (len == 0)
- return 1;
-
-bad:
- return 0;
-}
-
-int xfrm6_clear_mutable_options(struct sk_buff *skb, u16 *nh_offset, int dir)
-{
- u16 offset = sizeof(struct ipv6hdr);
- struct ipv6_opt_hdr *exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset);
- unsigned int packet_len = skb->tail - skb->nh.raw;
- u8 nexthdr = skb->nh.ipv6h->nexthdr;
- u8 nextnexthdr = 0;
-
- *nh_offset = ((unsigned char *)&skb->nh.ipv6h->nexthdr) - skb->nh.raw;
-
- while (offset + 1 <= packet_len) {
-
- switch (nexthdr) {
-
- case NEXTHDR_HOP:
- *nh_offset = offset;
- offset += ipv6_optlen(exthdr);
- if (!zero_out_mutable_opts(exthdr)) {
- if (net_ratelimit())
- printk(KERN_WARNING "overrun hopopts\n");
- return 0;
- }
- nexthdr = exthdr->nexthdr;
- exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset);
- break;
-
- case NEXTHDR_ROUTING:
- *nh_offset = offset;
- offset += ipv6_optlen(exthdr);
- ((struct ipv6_rt_hdr*)exthdr)->segments_left = 0;
- nexthdr = exthdr->nexthdr;
- exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset);
- break;
-
- case NEXTHDR_DEST:
- *nh_offset = offset;
- offset += ipv6_optlen(exthdr);
- if (!zero_out_mutable_opts(exthdr)) {
- if (net_ratelimit())
- printk(KERN_WARNING "overrun destopt\n");
- return 0;
- }
- nexthdr = exthdr->nexthdr;
- exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset);
- break;
-
- case NEXTHDR_AUTH:
- if (dir == XFRM_POLICY_OUT) {
- memset(((struct ipv6_auth_hdr*)exthdr)->auth_data, 0,
- (((struct ipv6_auth_hdr*)exthdr)->hdrlen - 1) << 2);
- }
- if (exthdr->nexthdr == NEXTHDR_DEST) {
- offset += (((struct ipv6_auth_hdr*)exthdr)->hdrlen + 2) << 2;
- exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset);
- nextnexthdr = exthdr->nexthdr;
- if (!zero_out_mutable_opts(exthdr)) {
- if (net_ratelimit())
- printk(KERN_WARNING "overrun destopt\n");
- return 0;
- }
- }
- return nexthdr;
- default :
- return nexthdr;
- }
- }
-
- return nexthdr;
-}
-
-int xfrm6_rcv(struct sk_buff *skb)
-{
- int err;
- u32 spi, seq;
- struct xfrm_state *xfrm_vec[XFRM_MAX_DEPTH];
- struct xfrm_state *x;
- int xfrm_nr = 0;
- int decaps = 0;
- struct ipv6hdr *hdr = skb->nh.ipv6h;
- unsigned char *tmp_hdr = NULL;
- int hdr_len = 0;
- u16 nh_offset = 0;
- u8 nexthdr = 0;
-
- if (hdr->nexthdr == IPPROTO_AH || hdr->nexthdr == IPPROTO_ESP) {
- nh_offset = ((unsigned char*)&skb->nh.ipv6h->nexthdr) - skb->nh.raw;
- hdr_len = sizeof(struct ipv6hdr);
- } else {
- hdr_len = skb->h.raw - skb->nh.raw;
- }
-
- tmp_hdr = kmalloc(hdr_len, GFP_ATOMIC);
- if (!tmp_hdr)
- goto drop;
- memcpy(tmp_hdr, skb->nh.raw, hdr_len);
-
- nexthdr = xfrm6_clear_mutable_options(skb, &nh_offset, XFRM_POLICY_IN);
- hdr->priority = 0;
- hdr->flow_lbl[0] = 0;
- hdr->flow_lbl[1] = 0;
- hdr->flow_lbl[2] = 0;
- hdr->hop_limit = 0;
-
- if ((err = xfrm6_parse_spi(skb, nexthdr, &spi, &seq)) != 0)
- goto drop;
-
- do {
- struct ipv6hdr *iph = skb->nh.ipv6h;
-
- if (xfrm_nr == XFRM_MAX_DEPTH)
- goto drop;
-
- x = xfrm6_state_lookup(&iph->daddr, spi, nexthdr);
- if (x == NULL)
- goto drop;
- spin_lock(&x->lock);
- if (unlikely(x->km.state != XFRM_STATE_VALID))
- goto drop_unlock;
-
- if (x->props.replay_window && xfrm_replay_check(x, seq))
- goto drop_unlock;
-
- nexthdr = x->type->input(x, skb);
- if (nexthdr <= 0)
- goto drop_unlock;
-
- if (x->props.replay_window)
- xfrm_replay_advance(x, seq);
-
- x->curlft.bytes += skb->len;
- x->curlft.packets++;
-
- spin_unlock(&x->lock);
-
- xfrm_vec[xfrm_nr++] = x;
-
- iph = skb->nh.ipv6h; /* ??? */
-
- if (nexthdr == NEXTHDR_DEST) {
- if (!pskb_may_pull(skb, (skb->h.raw-skb->data)+8) ||
- !pskb_may_pull(skb, (skb->h.raw-skb->data)+((skb->h.raw[1]+1)<<3))) {
- err = -EINVAL;
- goto drop;
- }
- nexthdr = skb->h.raw[0];
- nh_offset = skb->h.raw - skb->nh.raw;
- skb_pull(skb, (skb->h.raw[1]+1)<<3);
- skb->h.raw = skb->data;
- }
-
- if (x->props.mode) { /* XXX */
- if (iph->nexthdr != IPPROTO_IPV6)
- goto drop;
- skb->nh.raw = skb->data;
- iph = skb->nh.ipv6h;
- decaps = 1;
- break;
- }
-
- if ((err = xfrm6_parse_spi(skb, nexthdr, &spi, &seq)) < 0)
- goto drop;
- } while (!err);
-
- memcpy(skb->nh.raw, tmp_hdr, hdr_len);
- skb->nh.raw[nh_offset] = nexthdr;
- skb->nh.ipv6h->payload_len = htons(hdr_len + skb->len - sizeof(struct ipv6hdr));
-
- /* Allocate new secpath or COW existing one. */
- if (!skb->sp || atomic_read(&skb->sp->refcnt) != 1) {
- struct sec_path *sp;
- sp = kmem_cache_alloc(secpath_cachep, SLAB_ATOMIC);
- if (!sp)
- goto drop;
- if (skb->sp) {
- memcpy(sp, skb->sp, sizeof(struct sec_path));
- secpath_put(skb->sp);
- } else
- sp->len = 0;
- atomic_set(&sp->refcnt, 1);
- skb->sp = sp;
- }
-
- if (xfrm_nr + skb->sp->len > XFRM_MAX_DEPTH)
- goto drop;
-
- memcpy(skb->sp->xvec+skb->sp->len, xfrm_vec, xfrm_nr*sizeof(void*));
- skb->sp->len += xfrm_nr;
-
- if (decaps) {
- if (!(skb->dev->flags&IFF_LOOPBACK)) {
- dst_release(skb->dst);
- skb->dst = NULL;
- }
- netif_rx(skb);
- return 0;
- } else {
- return -nexthdr;
- }
-
-drop_unlock:
- spin_unlock(&x->lock);
- xfrm_state_put(x);
-drop:
- if (tmp_hdr) kfree(tmp_hdr);
- while (--xfrm_nr >= 0)
- xfrm_state_put(xfrm_vec[xfrm_nr]);
- kfree_skb(skb);
- return 0;
-}
-
-#endif /* CONFIG_IPV6 || CONFIG_IPV6_MODULE */
-/* Changes
+/*
+ * xfrm_policy.c
*
- * Mitsuru KANDA @USAGI : IPv6 Support
- * Kazunori MIYAZAWA @USAGI :
- * Kunihiro Ishiguro :
+ * Changes:
+ * Mitsuru KANDA @USAGI
+ * Kazunori MIYAZAWA @USAGI
+ * Kunihiro Ishiguro
+ * IPv6 support
+ * Kazunori MIYAZAWA @USAGI
+ * YOSHIFUJI Hideaki
+ * Split up af-specific portion
*
*/
#include <linux/config.h>
#include <net/xfrm.h>
#include <net/ip.h>
-#include <net/ipv6.h>
-#include <net/ip6_route.h>
DECLARE_MUTEX(xfrm_cfg_sem);
struct xfrm_policy *xfrm_policy_list[XFRM_POLICY_MAX*2];
-extern struct dst_ops xfrm4_dst_ops;
-#if defined (CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
-extern struct dst_ops xfrm6_dst_ops;
-#endif
+static rwlock_t xfrm_policy_afinfo_lock = RW_LOCK_UNLOCKED;
+static struct xfrm_policy_afinfo *xfrm_policy_afinfo[NPROTO];
-static inline int xfrm_dst_lookup(struct xfrm_dst **dst, struct flowi *fl, unsigned short family);
+kmem_cache_t *xfrm_dst_cache;
/* Limited flow cache. Its function now is to accelerate search for
* policy rules.
struct flow_entry **flow_table;
-#define FLOWCACHE_HASH_SIZE 1024
-
-static inline u32 flow_hash(struct flowi *fl)
-{
- u32 hash = fl->fl4_src ^ fl->uli_u.ports.sport;
-
- hash = ((hash & 0xF0F0F0F0) >> 4) | ((hash & 0x0F0F0F0F) << 4);
-
- hash ^= fl->fl4_dst ^ fl->uli_u.ports.dport;
- hash ^= (hash >> 10);
- hash ^= (hash >> 20);
- return hash & (FLOWCACHE_HASH_SIZE-1);
-}
-
-#if defined (CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
-static inline u32 flow_hash6(struct flowi *fl)
-{
- u32 hash = fl->fl6_src->s6_addr32[2] ^
- fl->fl6_src->s6_addr32[3] ^
- fl->uli_u.ports.sport;
-
- hash = ((hash & 0xF0F0F0F0) >> 4) | ((hash & 0x0F0F0F0F) << 4);
-
- hash ^= fl->fl6_dst->s6_addr32[2] ^
- fl->fl6_dst->s6_addr32[3] ^
- fl->uli_u.ports.dport;
- hash ^= (hash >> 10);
- hash ^= (hash >> 20);
- return hash & (FLOWCACHE_HASH_SIZE-1);
-}
-#endif
-
-static int flow_lwm = 2*FLOWCACHE_HASH_SIZE;
-static int flow_hwm = 4*FLOWCACHE_HASH_SIZE;
+static int flow_lwm = 2*XFRM_FLOWCACHE_HASH_SIZE;
+static int flow_hwm = 4*XFRM_FLOWCACHE_HASH_SIZE;
static int flow_number[NR_CPUS] __cacheline_aligned;
{
int i;
struct flow_entry *fle, **flp;
- int shrink_to = flow_lwm/FLOWCACHE_HASH_SIZE;
+ int shrink_to = flow_lwm/XFRM_FLOWCACHE_HASH_SIZE;
- for (i=0; i<FLOWCACHE_HASH_SIZE; i++) {
+ for (i=0; i<XFRM_FLOWCACHE_HASH_SIZE; i++) {
int k = 0;
- flp = &flow_table[cpu*FLOWCACHE_HASH_SIZE+i];
+ flp = &flow_table[cpu*XFRM_FLOWCACHE_HASH_SIZE+i];
while ((fle=*flp) != NULL && k<shrink_to) {
k++;
flp = &fle->next;
u32 hash;
int cpu;
- switch (family) {
- case AF_INET:
- hash = flow_hash(fl);
- break;
-#if defined (CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
- case AF_INET6:
- hash = flow_hash6(fl);
- break;
-#endif
- default:
- return NULL;
- }
+ hash = flow_hash(fl, family);
local_bh_disable();
cpu = smp_processor_id();
- for (fle = flow_table[cpu*FLOWCACHE_HASH_SIZE+hash];
+ for (fle = flow_table[cpu*XFRM_FLOWCACHE_HASH_SIZE+hash];
fle; fle = fle->next) {
if (memcmp(fl, &fle->fl, sizeof(fle->fl)) == 0 &&
fle->dir == dir) {
fle->pol = pol;
if (pol)
atomic_inc(&pol->refcnt);
- fle->next = flow_table[cpu*FLOWCACHE_HASH_SIZE+hash];
- flow_table[cpu*FLOWCACHE_HASH_SIZE+hash] = fle;
+ fle->next = flow_table[cpu*XFRM_FLOWCACHE_HASH_SIZE+hash];
+ flow_table[cpu*XFRM_FLOWCACHE_HASH_SIZE+hash] = fle;
}
}
local_bh_enable();
panic("NET: failed to allocate flow cache slab\n");
for (order = 0;
- (PAGE_SIZE<<order) < (NR_CPUS*sizeof(struct flow_entry *)*FLOWCACHE_HASH_SIZE);
+ (PAGE_SIZE<<order) < (NR_CPUS*sizeof(struct flow_entry *)*XFRM_FLOWCACHE_HASH_SIZE);
order++)
/* NOTHING */;
memset(flow_table, 0, PAGE_SIZE<<order);
}
-static struct xfrm_type *xfrm_type_map[256];
-static rwlock_t xfrm_type_lock = RW_LOCK_UNLOCKED;
-
-int xfrm_register_type(struct xfrm_type *type)
+int xfrm_register_type(struct xfrm_type *type, unsigned short family)
{
+ struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family);
+ struct xfrm_type_map *typemap;
int err = 0;
- write_lock(&xfrm_type_lock);
- if (xfrm_type_map[type->proto] == NULL)
- xfrm_type_map[type->proto] = type;
+ if (unlikely(afinfo == NULL))
+ return -EAFNOSUPPORT;
+ typemap = afinfo->type_map;
+
+ write_lock(&typemap->lock);
+ if (likely(typemap->map[type->proto] == NULL))
+ typemap->map[type->proto] = type;
else
err = -EEXIST;
- write_unlock(&xfrm_type_lock);
+ write_unlock(&typemap->lock);
+ xfrm_policy_put_afinfo(afinfo);
return err;
}
-int xfrm_unregister_type(struct xfrm_type *type)
+int xfrm_unregister_type(struct xfrm_type *type, unsigned short family)
{
+ struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family);
+ struct xfrm_type_map *typemap;
int err = 0;
- write_lock(&xfrm_type_lock);
- if (xfrm_type_map[type->proto] != type)
+ if (unlikely(afinfo == NULL))
+ return -EAFNOSUPPORT;
+ typemap = afinfo->type_map;
+
+ write_lock(&typemap->lock);
+ if (unlikely(typemap->map[type->proto] != type))
err = -ENOENT;
else
- xfrm_type_map[type->proto] = NULL;
- write_unlock(&xfrm_type_lock);
+ typemap->map[type->proto] = NULL;
+ write_unlock(&typemap->lock);
+ xfrm_policy_put_afinfo(afinfo);
return err;
}
-struct xfrm_type *xfrm_get_type(u8 proto)
+struct xfrm_type *xfrm_get_type(u8 proto, unsigned short family)
{
+ struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family);
+ struct xfrm_type_map *typemap;
struct xfrm_type *type;
- read_lock(&xfrm_type_lock);
- type = xfrm_type_map[proto];
- if (type && !try_module_get(type->owner))
+ if (unlikely(afinfo == NULL))
+ return NULL;
+ typemap = afinfo->type_map;
+
+ read_lock(&typemap->lock);
+ type = typemap->map[proto];
+ if (unlikely(type && !try_module_get(type->owner)))
type = NULL;
- read_unlock(&xfrm_type_lock);
+ read_unlock(&typemap->lock);
+ xfrm_policy_put_afinfo(afinfo);
return type;
}
-static xfrm_dst_lookup_t *__xfrm_dst_lookup[AF_MAX];
-rwlock_t xdl_lock = RW_LOCK_UNLOCKED;
-
-int xfrm_dst_lookup_register(xfrm_dst_lookup_t *dst_lookup,
- unsigned short family)
+int xfrm_dst_lookup(struct xfrm_dst **dst, struct flowi *fl,
+ unsigned short family)
{
+ struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family);
int err = 0;
- write_lock(&xdl_lock);
- if (__xfrm_dst_lookup[family])
- err = -ENOBUFS;
- else {
- __xfrm_dst_lookup[family] = dst_lookup;
- }
- write_unlock(&xdl_lock);
+ if (unlikely(afinfo == NULL))
+ return -EAFNOSUPPORT;
- return err;
-}
-
-void xfrm_dst_lookup_unregister(unsigned short family)
-{
- write_lock(&xdl_lock);
- if (__xfrm_dst_lookup[family])
- __xfrm_dst_lookup[family] = 0;
- write_unlock(&xdl_lock);
-}
-
-static inline int xfrm_dst_lookup(struct xfrm_dst **dst, struct flowi *fl,
- unsigned short family)
-{
- int err = 0;
- read_lock(&xdl_lock);
- if (__xfrm_dst_lookup[family])
- err = __xfrm_dst_lookup[family](dst, fl);
+ if (likely(afinfo->dst_lookup != NULL))
+ err = afinfo->dst_lookup(dst, fl);
else
err = -EINVAL;
- read_unlock(&xdl_lock);
+ xfrm_policy_put_afinfo(afinfo);
return err;
}
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-static struct xfrm_type *xfrm6_type_map[256];
-static rwlock_t xfrm6_type_lock = RW_LOCK_UNLOCKED;
-
-int xfrm6_register_type(struct xfrm_type *type)
-{
- int err = 0;
-
- write_lock(&xfrm6_type_lock);
- if (xfrm6_type_map[type->proto] == NULL)
- xfrm6_type_map[type->proto] = type;
- else
- err = -EEXIST;
- write_unlock(&xfrm6_type_lock);
- return err;
-}
-
-int xfrm6_unregister_type(struct xfrm_type *type)
-{
- int err = 0;
-
- write_lock(&xfrm6_type_lock);
- if (xfrm6_type_map[type->proto] != type)
- err = -ENOENT;
- else
- xfrm6_type_map[type->proto] = NULL;
- write_unlock(&xfrm6_type_lock);
- return err;
-}
-
-struct xfrm_type *xfrm6_get_type(u8 proto)
-{
- struct xfrm_type *type;
-
- read_lock(&xfrm6_type_lock);
- type = xfrm6_type_map[proto];
- if (type && !try_module_get(type->owner))
- type = NULL;
- read_unlock(&xfrm6_type_lock);
- return type;
-}
-#endif /* CONFIG_IPV6 || CONFIG_IPV6_MODULE */
-
void xfrm_put_type(struct xfrm_type *type)
{
module_put(type->owner);
if (pol->family != family)
continue;
- switch (family) {
- case AF_INET:
- match = xfrm4_selector_match(sel, fl);
- break;
-#if defined (CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
- case AF_INET6:
- match = xfrm6_selector_match(sel, fl);
- break;
-#endif
- default:
- match = 0;
- }
+ match = xfrm_selector_match(sel, fl, family);
if (match) {
atomic_inc(&pol->refcnt);
break;
if ((pol = sk->policy[dir]) != NULL) {
int match;
- switch (sk->family) {
- case AF_INET:
- match = xfrm4_selector_match(&pol->selector, fl);
- break;
-#if defined (CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
- case AF_INET6:
- match = xfrm6_selector_match(&pol->selector, fl);
- break;
-#endif
- default:
- match = 0;
- }
+ match = xfrm_selector_match(&pol->selector, fl, sk->family);
if (match)
atomic_inc(&pol->refcnt);
else
/* Resolve list of templates for the flow, given policy. */
static int
-xfrm4_tmpl_resolve(struct xfrm_policy *policy, struct flowi *fl,
- struct xfrm_state **xfrm)
+xfrm_tmpl_resolve(struct xfrm_policy *policy, struct flowi *fl,
+ struct xfrm_state **xfrm,
+ unsigned short family)
{
int nx;
int i, error;
- u32 daddr = fl->fl4_dst;
- u32 saddr = fl->fl4_src;
+ xfrm_address_t *daddr = xfrm_flowi_daddr(fl, family);
+ xfrm_address_t *saddr = xfrm_flowi_saddr(fl, family);
for (nx=0, i = 0; i < policy->xfrm_nr; i++) {
struct xfrm_state *x;
- u32 remote = daddr;
- u32 local = saddr;
- struct xfrm_tmpl *tmpl = &policy->xfrm_vec[i];
-
- if (tmpl->mode) {
- remote = tmpl->id.daddr.xfrm4_addr;
- local = tmpl->saddr.xfrm4_addr;
- }
-
- x = xfrm4_state_find(remote, local, fl, tmpl, policy, &error);
-
- 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;
-}
-
-#if defined (CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
-static int
-xfrm6_tmpl_resolve(struct xfrm_policy *policy, struct flowi *fl,
- struct xfrm_state **xfrm)
-{
- int nx;
- int i, error;
- struct in6_addr *daddr = fl->fl6_dst;
- struct in6_addr *saddr = fl->fl6_src;
-
- for (nx=0, i = 0; i < policy->xfrm_nr; i++) {
- struct xfrm_state *x=NULL;
- struct in6_addr *remote = daddr;
- struct in6_addr *local = saddr;
+ xfrm_address_t *remote = daddr;
+ xfrm_address_t *local = saddr;
struct xfrm_tmpl *tmpl = &policy->xfrm_vec[i];
if (tmpl->mode) {
- remote = (struct in6_addr*)&tmpl->id.daddr;
- local = (struct in6_addr*)&tmpl->saddr;
+ remote = &tmpl->id.daddr;
+ local = &tmpl->saddr;
}
- x = xfrm6_state_find(remote, local, fl, tmpl, policy, &error);
+ x = xfrm_state_find(remote, local, fl, tmpl, policy, &error, family);
if (x && x->km.state == XFRM_STATE_VALID) {
xfrm[nx++] = x;
xfrm_state_put(xfrm[nx]);
return error;
}
-#endif
/* Check that the bundle accepts the flow and its components are
* still valid.
*/
-static int xfrm_bundle_ok(struct xfrm_dst *xdst, struct flowi *fl)
+static struct dst_entry *
+xfrm_find_bundle(struct flowi *fl, struct rtable *rt, struct xfrm_policy *policy, unsigned short family)
{
- do {
- if (xdst->u.dst.ops != &xfrm4_dst_ops)
- return 1;
-
- if (!xfrm4_selector_match(&xdst->u.dst.xfrm->sel, fl))
- return 0;
- if (xdst->u.dst.xfrm->km.state != XFRM_STATE_VALID ||
- xdst->u.dst.path->obsolete > 0)
- return 0;
- xdst = (struct xfrm_dst*)xdst->u.dst.child;
- } while (xdst);
- return 0;
+ struct dst_entry *x;
+ struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family);
+ if (unlikely(afinfo == NULL))
+ return ERR_PTR(-EINVAL);
+ x = afinfo->find_bundle(fl, rt, policy);
+ xfrm_policy_put_afinfo(afinfo);
+ return x;
}
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-static int xfrm6_bundle_ok(struct xfrm_dst *xdst, struct flowi *fl)
-{
- do {
- if (xdst->u.dst.ops != &xfrm6_dst_ops)
- return 1;
-
- if (!xfrm6_selector_match(&xdst->u.dst.xfrm->sel, fl))
- return 0;
- if (xdst->u.dst.xfrm->km.state != XFRM_STATE_VALID ||
- xdst->u.dst.path->obsolete > 0)
- return 0;
- xdst = (struct xfrm_dst*)xdst->u.dst.child;
- } while (xdst);
- return 0;
-}
-#endif
-
-
/* Allocate chain of dst_entry's, attach known xfrm's, calculate
* all the metrics... Shortly, bundle a bundle.
*/
static int
xfrm_bundle_create(struct xfrm_policy *policy, struct xfrm_state **xfrm, int nx,
- struct flowi *fl, struct dst_entry **dst_p)
+ struct flowi *fl, struct dst_entry **dst_p,
+ unsigned short family)
{
- struct dst_entry *dst, *dst_prev;
- struct rtable *rt0 = (struct rtable*)(*dst_p);
- struct rtable *rt = rt0;
- u32 remote = fl->fl4_dst;
- u32 local = fl->fl4_src;
- int i;
int err;
- int header_len = 0;
- int trailer_len = 0;
-
- dst = dst_prev = NULL;
-
- for (i = 0; i < nx; i++) {
- struct dst_entry *dst1 = dst_alloc(&xfrm4_dst_ops);
-
- if (unlikely(dst1 == NULL)) {
- err = -ENOBUFS;
- goto error;
- }
-
- dst1->xfrm = xfrm[i];
- if (!dst)
- dst = dst1;
- else {
- dst_prev->child = dst1;
- dst1->flags |= DST_NOHASH;
- dst_hold(dst1);
- }
- dst_prev = dst1;
- if (xfrm[i]->props.mode) {
- remote = xfrm[i]->id.daddr.xfrm4_addr;
- local = xfrm[i]->props.saddr.xfrm4_addr;
- }
- header_len += xfrm[i]->props.header_len;
- trailer_len += xfrm[i]->props.trailer_len;
- }
-
- if (remote != fl->fl4_dst) {
- struct flowi fl_tunnel = { .nl_u = { .ip4_u =
- { .daddr = remote,
- .saddr = local }
- }
- };
- err = xfrm_dst_lookup((struct xfrm_dst**)&rt, &fl_tunnel, AF_INET);
- if (err)
- goto error;
- } else {
- dst_hold(&rt->u.dst);
- }
- dst_prev->child = &rt->u.dst;
- for (dst_prev = dst; dst_prev != &rt->u.dst; dst_prev = dst_prev->child) {
- struct xfrm_dst *x = (struct xfrm_dst*)dst_prev;
- x->u.rt.fl = *fl;
-
- dst_prev->dev = rt->u.dst.dev;
- if (rt->u.dst.dev)
- dev_hold(rt->u.dst.dev);
- dst_prev->obsolete = -1;
- dst_prev->flags |= DST_HOST;
- dst_prev->lastuse = jiffies;
- dst_prev->header_len = header_len;
- dst_prev->trailer_len = trailer_len;
- memcpy(&dst_prev->metrics, &rt->u.dst.metrics, sizeof(dst_prev->metrics));
- dst_prev->path = &rt->u.dst;
-
- /* Copy neighbout for reachability confirmation */
- dst_prev->neighbour = neigh_clone(rt->u.dst.neighbour);
- dst_prev->input = rt->u.dst.input;
- dst_prev->output = dst_prev->xfrm->type->output;
- if (rt->peer)
- atomic_inc(&rt->peer->refcnt);
- x->u.rt.peer = rt->peer;
- /* Sheit... I remember I did this right. Apparently,
- * it was magically lost, so this code needs audit */
- x->u.rt.rt_flags = rt0->rt_flags&(RTCF_BROADCAST|RTCF_MULTICAST|RTCF_LOCAL);
- x->u.rt.rt_type = rt->rt_type;
- x->u.rt.rt_src = rt0->rt_src;
- x->u.rt.rt_dst = rt0->rt_dst;
- x->u.rt.rt_gateway = rt->rt_gateway;
- x->u.rt.rt_spec_dst = rt0->rt_spec_dst;
- header_len -= x->u.dst.xfrm->props.header_len;
- trailer_len -= x->u.dst.xfrm->props.trailer_len;
- }
- *dst_p = dst;
- return 0;
-
-error:
- if (dst)
- dst_free(dst);
- return err;
-}
-
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-static int
-xfrm6_bundle_create(struct xfrm_policy *policy, struct xfrm_state **xfrm, int nx,
- struct flowi *fl, struct dst_entry **dst_p)
-{
- struct dst_entry *dst, *dst_prev;
- struct rt6_info *rt0 = (struct rt6_info*)(*dst_p);
- struct rt6_info *rt = rt0;
- struct in6_addr *remote = fl->fl6_dst;
- struct in6_addr *local = fl->fl6_src;
- int i;
- int err = 0;
- int header_len = 0;
- int trailer_len = 0;
-
- dst = dst_prev = NULL;
-
- for (i = 0; i < nx; i++) {
- struct dst_entry *dst1 = dst_alloc(&xfrm6_dst_ops);
-
- if (unlikely(dst1 == NULL)) {
- err = -ENOBUFS;
- goto error;
- }
-
- dst1->xfrm = xfrm[i];
- if (!dst)
- dst = dst1;
- else {
- dst_prev->child = dst1;
- dst1->flags |= DST_NOHASH;
- dst_clone(dst1);
- }
- dst_prev = dst1;
- if (xfrm[i]->props.mode) {
- remote = (struct in6_addr*)&xfrm[i]->id.daddr;
- local = (struct in6_addr*)&xfrm[i]->props.saddr;
- }
- header_len += xfrm[i]->props.header_len;
- trailer_len += xfrm[i]->props.trailer_len;
- }
-
- if (ipv6_addr_cmp(remote, fl->fl6_dst)) {
- struct flowi fl_tunnel = { .nl_u = { .ip6_u =
- { .daddr = remote,
- .saddr = local }
- }
- };
- err = xfrm_dst_lookup((struct xfrm_dst**)&dst, &fl_tunnel, AF_INET6);
- if (err)
- goto error;
- } else {
- dst_clone(&rt->u.dst);
- }
- dst_prev->child = &rt->u.dst;
- for (dst_prev = dst; dst_prev != &rt->u.dst; dst_prev = dst_prev->child) {
- struct xfrm_dst *x = (struct xfrm_dst*)dst_prev;
- x->u.rt.fl = *fl;
-
- dst_prev->dev = rt->u.dst.dev;
- if (rt->u.dst.dev)
- dev_hold(rt->u.dst.dev);
- dst_prev->obsolete = -1;
- dst_prev->flags |= DST_HOST;
- dst_prev->lastuse = jiffies;
- dst_prev->header_len = header_len;
- dst_prev->trailer_len = trailer_len;
- memcpy(&dst_prev->metrics, &rt->u.dst.metrics, sizeof(dst_prev->metrics));
- dst_prev->path = &rt->u.dst;
-
- /* Copy neighbout for reachability confirmation */
- dst_prev->neighbour = neigh_clone(rt->u.dst.neighbour);
- dst_prev->input = rt->u.dst.input;
- dst_prev->output = dst_prev->xfrm->type->output;
- /* Sheit... I remember I did this right. Apparently,
- * it was magically lost, so this code needs audit */
- x->u.rt6.rt6i_flags = rt0->rt6i_flags&(RTCF_BROADCAST|RTCF_MULTICAST|RTCF_LOCAL);
- x->u.rt6.rt6i_metric = rt0->rt6i_metric;
- x->u.rt6.rt6i_node = rt0->rt6i_node;
- x->u.rt6.rt6i_hoplimit = rt0->rt6i_hoplimit;
- x->u.rt6.rt6i_gateway = rt0->rt6i_gateway;
- memcpy(&x->u.rt6.rt6i_gateway, &rt0->rt6i_gateway, sizeof(x->u.rt6.rt6i_gateway));
- header_len -= x->u.dst.xfrm->props.header_len;
- trailer_len -= x->u.dst.xfrm->props.trailer_len;
- }
- *dst_p = dst;
- return 0;
-
-error:
- if (dst)
- dst_free(dst);
+ struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family);
+ if (unlikely(afinfo == NULL))
+ return -EINVAL;
+ err = afinfo->bundle_create(policy, xfrm, nx, fl, dst_p);
+ xfrm_policy_put_afinfo(afinfo);
return err;
}
-#endif
/* Main function: finds/creates a bundle for given flow.
*
* LATER: help from flow cache. It is optional, this
* is required only for output policy.
*/
- if (family == AF_INET) {
- read_lock_bh(&policy->lock);
- for (dst = policy->bundles; dst; dst = dst->next) {
- struct xfrm_dst *xdst = (struct xfrm_dst*)dst;
- if (xdst->u.rt.fl.fl4_dst == fl->fl4_dst &&
- xdst->u.rt.fl.fl4_src == fl->fl4_src &&
- xdst->u.rt.fl.oif == fl->oif &&
- xfrm_bundle_ok(xdst, fl)) {
- dst_clone(dst);
- break;
- }
- }
- read_unlock_bh(&policy->lock);
- if (dst)
- break;
- nx = xfrm4_tmpl_resolve(policy, fl, xfrm);
-#if defined (CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
- } else if (family == AF_INET6) {
- read_lock_bh(&policy->lock);
- for (dst = policy->bundles; dst; dst = dst->next) {
- struct xfrm_dst *xdst = (struct xfrm_dst*)dst;
- if (!ipv6_addr_cmp(&xdst->u.rt6.rt6i_dst.addr, fl->fl6_dst) &&
- !ipv6_addr_cmp(&xdst->u.rt6.rt6i_src.addr, fl->fl6_src) &&
- xfrm6_bundle_ok(xdst, fl)) {
- dst_clone(dst);
- break;
- }
- }
- read_unlock_bh(&policy->lock);
- if (dst)
- break;
- nx = xfrm6_tmpl_resolve(policy, fl, xfrm);
-#endif
- } else {
- return -EINVAL;
+ dst = xfrm_find_bundle(fl, rt, policy, family);
+ if (IS_ERR(dst)) {
+ xfrm_pol_put(policy);
+ return PTR_ERR(dst);
}
if (dst)
break;
+ nx = xfrm_tmpl_resolve(policy, fl, xfrm, family);
+
if (unlikely(nx<0)) {
err = nx;
if (err == -EAGAIN) {
__set_task_state(tsk, TASK_INTERRUPTIBLE);
add_wait_queue(&km_waitq, &wait);
- switch (family) {
- case AF_INET:
- err = xfrm4_tmpl_resolve(policy, fl, xfrm);
- break;
-#if defined (CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
- case AF_INET6:
- err = xfrm6_tmpl_resolve(policy, fl, xfrm);
- break;
-#endif
- default:
- err = -EINVAL;
- }
+ err = xfrm_tmpl_resolve(policy, fl, xfrm, family);
if (err == -EAGAIN)
schedule();
__set_task_state(tsk, TASK_RUNNING);
}
dst = &rt->u.dst;
- switch (family) {
- case AF_INET:
- err = xfrm_bundle_create(policy, xfrm, nx, fl, &dst);
- break;
-#if defined (CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
- case AF_INET6:
- err = xfrm6_bundle_create(policy, xfrm, nx, fl, &dst);
- break;
-#endif
- default:
- err = -EINVAL;
- }
-
+ err = xfrm_bundle_create(policy, xfrm, nx, fl, &dst, family);
+
if (unlikely(err)) {
int i;
for (i=0; i<nx; i++)
*/
static inline int
-xfrm_state_ok(struct xfrm_tmpl *tmpl, struct xfrm_state *x)
-{
- return x->id.proto == tmpl->id.proto &&
- (x->id.spi == tmpl->id.spi || !tmpl->id.spi) &&
- x->props.mode == tmpl->mode &&
- (tmpl->aalgos & (1<<x->props.aalgo)) &&
- (!x->props.mode || !tmpl->saddr.xfrm4_addr ||
- tmpl->saddr.xfrm4_addr == x->props.saddr.xfrm4_addr);
-}
-
-static inline int
-xfrm_policy_ok(struct xfrm_tmpl *tmpl, struct sec_path *sp, int idx)
-{
- for (; idx < sp->len; idx++) {
- if (xfrm_state_ok(tmpl, sp->xvec[idx]))
- return ++idx;
- }
- return -1;
-}
-
-static void
-_decode_session4(struct sk_buff *skb, struct flowi *fl)
-{
- struct iphdr *iph = skb->nh.iph;
- u8 *xprth = skb->nh.raw + iph->ihl*4;
-
- if (!(iph->frag_off & htons(IP_MF | IP_OFFSET))) {
- switch (iph->protocol) {
- case IPPROTO_UDP:
- case IPPROTO_TCP:
- case IPPROTO_SCTP:
- if (pskb_may_pull(skb, xprth + 4 - skb->data)) {
- u16 *ports = (u16 *)xprth;
-
- fl->uli_u.ports.sport = ports[0];
- fl->uli_u.ports.dport = ports[1];
- }
- break;
-
- case IPPROTO_ESP:
- if (pskb_may_pull(skb, xprth + 4 - skb->data)) {
- u32 *ehdr = (u32 *)xprth;
-
- fl->uli_u.spi = ehdr[0];
- }
- break;
-
- case IPPROTO_AH:
- if (pskb_may_pull(skb, xprth + 8 - skb->data)) {
- u32 *ah_hdr = (u32*)xprth;
-
- fl->uli_u.spi = ah_hdr[1];
- }
- break;
-
- default:
- fl->uli_u.spi = 0;
- break;
- };
- } else {
- memset(fl, 0, sizeof(struct flowi));
- }
- fl->proto = iph->protocol;
- fl->fl4_dst = iph->daddr;
- fl->fl4_src = iph->saddr;
-}
-
-#if defined (CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
-static inline int
-xfrm6_state_ok(struct xfrm_tmpl *tmpl, struct xfrm_state *x)
+xfrm_state_ok(struct xfrm_tmpl *tmpl, struct xfrm_state *x,
+ unsigned short family)
{
return x->id.proto == tmpl->id.proto &&
(x->id.spi == tmpl->id.spi || !tmpl->id.spi) &&
x->props.mode == tmpl->mode &&
(tmpl->aalgos & (1<<x->props.aalgo)) &&
- (!x->props.mode || !ipv6_addr_any((struct in6_addr*)&x->props.saddr) ||
- !ipv6_addr_cmp((struct in6_addr *)&tmpl->saddr, (struct in6_addr*)&x->props.saddr));
+ !(x->props.mode && xfrm_state_addr_cmp(tmpl, x, family));
}
static inline int
-xfrm6_policy_ok(struct xfrm_tmpl *tmpl, struct sec_path *sp, int idx)
+xfrm_policy_ok(struct xfrm_tmpl *tmpl, struct sec_path *sp, int idx,
+ unsigned short family)
{
for (; idx < sp->len; idx++) {
- if (xfrm6_state_ok(tmpl, sp->xvec[idx]))
+ if (xfrm_state_ok(tmpl, sp->xvec[idx], family))
return ++idx;
}
return -1;
}
-static inline void
-_decode_session6(struct sk_buff *skb, struct flowi *fl)
+static int
+_decode_session(struct sk_buff *skb, struct flowi *fl, unsigned short family)
{
- u16 offset = sizeof(struct ipv6hdr);
- struct ipv6hdr *hdr = skb->nh.ipv6h;
- struct ipv6_opt_hdr *exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset);
- u8 nexthdr = skb->nh.ipv6h->nexthdr;
-
- fl->fl6_dst = &hdr->daddr;
- fl->fl6_src = &hdr->saddr;
-
- while (pskb_may_pull(skb, skb->nh.raw + offset + 1 - skb->data)) {
- switch (nexthdr) {
- case NEXTHDR_ROUTING:
- case NEXTHDR_HOP:
- case NEXTHDR_DEST:
- offset += ipv6_optlen(exthdr);
- nexthdr = exthdr->nexthdr;
- exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset);
- break;
+ struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family);
- case IPPROTO_UDP:
- case IPPROTO_TCP:
- case IPPROTO_SCTP:
- if (pskb_may_pull(skb, skb->nh.raw + offset + 4 - skb->data)) {
- u16 *ports = (u16 *)exthdr;
+ if (unlikely(afinfo == NULL))
+ return -EAFNOSUPPORT;
- fl->uli_u.ports.sport = ports[0];
- fl->uli_u.ports.dport = ports[1];
- }
- return;
-
- /* XXX Why are there these headers? */
- case IPPROTO_AH:
- case IPPROTO_ESP:
- default:
- fl->uli_u.spi = 0;
- return;
- };
- }
+ afinfo->decode_session(skb, fl);
+ xfrm_policy_put_afinfo(afinfo);
+ return 0;
}
-#endif
int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
unsigned short family)
struct xfrm_policy *pol;
struct flowi fl;
- switch (family) {
- case AF_INET:
- _decode_session4(skb, &fl);
- break;
-#if defined (CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
- case AF_INET6:
- _decode_session6(skb, &fl);
- break;
-#endif
- default :
+ if (_decode_session(skb, &fl, family) < 0)
return 0;
- }
/* First, check used SA against their selectors. */
if (skb->sp) {
int i;
for (i=skb->sp->len-1; i>=0; i--) {
- int match;
- switch (family) {
- case AF_INET:
- match = xfrm4_selector_match(&skb->sp->xvec[i]->sel, &fl);
- break;
-#if defined (CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
- case AF_INET6:
- match = xfrm6_selector_match(&skb->sp->xvec[i]->sel, &fl);
- break;
-#endif
- default:
- match = 0;
- }
- if (!match)
+ if (!xfrm_selector_match(&skb->sp->xvec[i]->sel, &fl, family))
return 0;
}
}
* are implied between each two transformations.
*/
for (i = pol->xfrm_nr-1, k = 0; i >= 0; i--) {
- if (pol->xfrm_vec[i].optional)
- continue;
- switch (family) {
- case AF_INET:
- k = xfrm_policy_ok(pol->xfrm_vec+i, sp, k);
- break;
-#if defined (CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
- case AF_INET6:
- k = xfrm6_policy_ok(pol->xfrm_vec+i, sp, k);
- break;
-#endif
- default:
- k = -1;
- }
+ k = xfrm_policy_ok(pol->xfrm_vec+i, sp, k, family);
if (k < 0)
goto reject;
}
{
struct flowi fl;
- switch (family) {
- case AF_INET:
- _decode_session4(skb, &fl);
- break;
-#if defined (CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
- case AF_INET6:
- _decode_session6(skb, &fl);
- break;
-#endif
- default:
+ if (_decode_session(skb, &fl, family) < 0)
return 0;
- }
return xfrm_lookup(&skb->dst, &fl, NULL, 0) == 0;
}
}
}
-static inline int xfrm4_garbage_collect(void)
-{
- __xfrm_garbage_collect();
- return (atomic_read(&xfrm4_dst_ops.entries) > xfrm4_dst_ops.gc_thresh*2);
-}
-
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-static inline int xfrm6_garbage_collect(void)
-{
- __xfrm_garbage_collect();
- return (atomic_read(&xfrm6_dst_ops.entries) > xfrm6_dst_ops.gc_thresh*2);
-}
-#endif
-
static int bundle_depends_on(struct dst_entry *dst, struct xfrm_state *x)
{
do {
return 0;
}
-
-static void xfrm4_update_pmtu(struct dst_entry *dst, u32 mtu)
-{
- struct dst_entry *path = dst->path;
-
- if (mtu < 68 + dst->header_len)
- return;
-
- path->ops->update_pmtu(path, mtu);
-}
-
-#if defined (CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
-static void xfrm6_update_pmtu(struct dst_entry *dst, u32 mtu)
-{
- struct dst_entry *path = dst->path;
-
- if (mtu >= 1280 && mtu < dst_pmtu(dst))
- return;
-
- path->ops->update_pmtu(path, mtu);
-}
-#endif
-
/* Well... that's _TASK_. We need to scan through transformation
* list and figure out what mss tcp should generate in order to
* final datagram fit to mtu. Mama mia... :-)
return res + dst->header_len;
}
-struct dst_ops xfrm4_dst_ops = {
- .family = AF_INET,
- .protocol = __constant_htons(ETH_P_IP),
- .gc = xfrm4_garbage_collect,
- .check = xfrm_dst_check,
- .destroy = xfrm_dst_destroy,
- .negative_advice = xfrm_negative_advice,
- .link_failure = xfrm_link_failure,
- .update_pmtu = xfrm4_update_pmtu,
- .get_mss = xfrm_get_mss,
- .gc_thresh = 1024,
- .entry_size = sizeof(struct xfrm_dst),
-};
+int xfrm_policy_register_afinfo(struct xfrm_policy_afinfo *afinfo)
+{
+ int err = 0;
+ if (unlikely(afinfo == NULL))
+ return -EINVAL;
+ if (unlikely(afinfo->family >= NPROTO))
+ return -EAFNOSUPPORT;
+ write_lock(&xfrm_policy_afinfo_lock);
+ if (unlikely(xfrm_policy_afinfo[afinfo->family] != NULL))
+ err = -ENOBUFS;
+ else {
+ struct dst_ops *dst_ops = afinfo->dst_ops;
+ if (likely(dst_ops->kmem_cachep == NULL))
+ dst_ops->kmem_cachep = xfrm_dst_cache;
+ if (likely(dst_ops->check == NULL))
+ dst_ops->check = xfrm_dst_check;
+ if (likely(dst_ops->destroy == NULL))
+ dst_ops->destroy = xfrm_dst_destroy;
+ if (likely(dst_ops->negative_advice == NULL))
+ dst_ops->negative_advice = xfrm_negative_advice;
+ if (likely(dst_ops->link_failure == NULL))
+ dst_ops->link_failure = xfrm_link_failure;
+ if (likely(dst_ops->get_mss == NULL))
+ dst_ops->get_mss = xfrm_get_mss;
+ if (likely(afinfo->garbage_collect == NULL))
+ afinfo->garbage_collect = __xfrm_garbage_collect;
+ xfrm_policy_afinfo[afinfo->family] = afinfo;
+ }
+ write_unlock(&xfrm_policy_afinfo_lock);
+ return err;
+}
-#if defined (CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
-struct dst_ops xfrm6_dst_ops = {
- .family = AF_INET6,
- .protocol = __constant_htons(ETH_P_IPV6),
- .gc = xfrm6_garbage_collect,
- .check = xfrm_dst_check,
- .destroy = xfrm_dst_destroy,
- .negative_advice = xfrm_negative_advice,
- .link_failure = xfrm_link_failure,
- .update_pmtu = xfrm6_update_pmtu,
- .get_mss = xfrm_get_mss,
- .gc_thresh = 1024,
- .entry_size = sizeof(struct xfrm_dst),
-};
-#endif /* CONFIG_IPV6 || CONFIG_IPV6_MODULE */
+int xfrm_policy_unregister_afinfo(struct xfrm_policy_afinfo *afinfo)
+{
+ int err = 0;
+ if (unlikely(afinfo == NULL))
+ return -EINVAL;
+ if (unlikely(afinfo->family >= NPROTO))
+ return -EAFNOSUPPORT;
+ write_lock(&xfrm_policy_afinfo_lock);
+ if (likely(xfrm_policy_afinfo[afinfo->family] != NULL)) {
+ if (unlikely(xfrm_policy_afinfo[afinfo->family] != afinfo))
+ err = -EINVAL;
+ else {
+ struct dst_ops *dst_ops = afinfo->dst_ops;
+ xfrm_policy_afinfo[afinfo->family] = NULL;
+ dst_ops->kmem_cachep = NULL;
+ dst_ops->check = NULL;
+ dst_ops->destroy = NULL;
+ dst_ops->negative_advice = NULL;
+ dst_ops->link_failure = NULL;
+ dst_ops->get_mss = NULL;
+ afinfo->garbage_collect = NULL;
+ }
+ }
+ write_unlock(&xfrm_policy_afinfo_lock);
+ return err;
+}
-void __init xfrm_init(void)
+struct xfrm_policy_afinfo *xfrm_policy_get_afinfo(unsigned short family)
{
- xfrm4_dst_ops.kmem_cachep = kmem_cache_create("xfrm4_dst_cache",
- sizeof(struct xfrm_dst),
- 0, SLAB_HWCACHE_ALIGN,
- NULL, NULL);
+ struct xfrm_policy_afinfo *afinfo;
+ if (unlikely(family >= NPROTO))
+ return NULL;
+ read_lock(&xfrm_policy_afinfo_lock);
+ afinfo = xfrm_policy_afinfo[family];
+ if (likely(afinfo != NULL))
+ read_lock(&afinfo->lock);
+ read_unlock(&xfrm_policy_afinfo_lock);
+ return afinfo;
+}
- if (!xfrm4_dst_ops.kmem_cachep)
- panic("IP: failed to allocate xfrm4_dst_cache\n");
+void xfrm_policy_put_afinfo(struct xfrm_policy_afinfo *afinfo)
+{
+ if (unlikely(afinfo == NULL))
+ return;
+ read_unlock(&afinfo->lock);
+}
-#if defined (CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
- xfrm6_dst_ops.kmem_cachep = xfrm4_dst_ops.kmem_cachep;
-#endif
+void __init xfrm_policy_init(void)
+{
+ xfrm_dst_cache = kmem_cache_create("xfrm_dst_cache",
+ sizeof(struct xfrm_dst),
+ 0, SLAB_HWCACHE_ALIGN,
+ NULL, NULL);
+ if (!xfrm_dst_cache)
+ panic("XFRM: failed to allocate xfrm_dst_cache\n");
+}
- flow_cache_init();
+void __init xfrm_init(void)
+{
xfrm_state_init();
- xfrm_input_init();
+ flow_cache_init();
+ xfrm_policy_init();
}
-/* Changes
+/*
+ * xfrm_state.c
*
- * Mitsuru KANDA @USAGI : IPv6 Support
- * Kazunori MIYAZAWA @USAGI :
- * Kunihiro Ishiguro :
+ * Changes:
+ * Mitsuru KANDA @USAGI
+ * Kazunori MIYAZAWA @USAGI
+ * Kunihiro Ishiguro
+ * IPv6 support
+ * YOSHIFUJI Hideaki @USAGI
+ * Split up af-specific functions
*
*/
#include <net/xfrm.h>
#include <linux/pfkeyv2.h>
#include <linux/ipsec.h>
-#include <net/ipv6.h>
/* Each xfrm_state may be linked to two tables:
static spinlock_t xfrm_state_lock = SPIN_LOCK_UNLOCKED;
-#define XFRM_DST_HSIZE 1024
-
/* Hash table to find appropriate SA towards given target (endpoint
* of tunnel or destination of transport mode) allowed by selector.
*
DECLARE_WAIT_QUEUE_HEAD(km_waitq);
-#define ACQ_EXPIRES 30
+static rwlock_t xfrm_state_afinfo_lock = RW_LOCK_UNLOCKED;
+static struct xfrm_state_afinfo *xfrm_state_afinfo[NPROTO];
static void __xfrm_state_delete(struct xfrm_state *x);
wake_up(&km_waitq);
}
-struct xfrm_state *
-xfrm4_state_find(u32 daddr, u32 saddr, struct flowi *fl, struct xfrm_tmpl *tmpl,
- struct xfrm_policy *pol, int *err)
+static int
+xfrm_init_tempsel(struct xfrm_state *x, struct flowi *fl,
+ struct xfrm_tmpl *tmpl,
+ xfrm_address_t *daddr, xfrm_address_t *saddr,
+ unsigned short family)
{
- unsigned h = ntohl(daddr);
- struct xfrm_state *x;
- int acquire_in_progress = 0;
- int error = 0;
- struct xfrm_state *best = NULL;
-
- h = (h ^ (h>>16)) % XFRM_DST_HSIZE;
-
- spin_lock_bh(&xfrm_state_lock);
- list_for_each_entry(x, xfrm_state_bydst+h, bydst) {
- if (x->props.family == AF_INET &&
- daddr == x->id.daddr.xfrm4_addr &&
- x->props.reqid == tmpl->reqid &&
- (saddr == x->props.saddr.xfrm4_addr || !saddr || !x->props.saddr.xfrm4_addr) &&
- tmpl->mode == x->props.mode &&
- tmpl->id.proto == x->id.proto) {
- /* Resolution logic:
- 1. There is a valid state with matching selector.
- Done.
- 2. Valid state with inappropriate selector. Skip.
-
- Entering area of "sysdeps".
-
- 3. If state is not valid, selector is temporary,
- it selects only session which triggered
- previous resolution. Key manager will do
- something to install a state with proper
- selector.
- */
- if (x->km.state == XFRM_STATE_VALID) {
- if (!xfrm4_selector_match(&x->sel, fl))
- continue;
- if (!best ||
- best->km.dying > x->km.dying ||
- (best->km.dying == x->km.dying &&
- best->curlft.add_time < x->curlft.add_time))
- best = x;
- } else if (x->km.state == XFRM_STATE_ACQ) {
- acquire_in_progress = 1;
- } else if (x->km.state == XFRM_STATE_ERROR ||
- x->km.state == XFRM_STATE_EXPIRED) {
- if (xfrm4_selector_match(&x->sel, fl))
- error = 1;
- }
- }
- }
-
- if (best) {
- atomic_inc(&best->refcnt);
- spin_unlock_bh(&xfrm_state_lock);
- return best;
- }
-
- x = NULL;
- if (!error && !acquire_in_progress &&
- ((x = xfrm_state_alloc()) != NULL)) {
- /* Initialize temporary selector matching only
- * to current session. */
-
- x->sel.daddr.xfrm4_addr = fl->fl4_dst;
- x->sel.daddr.xfrm4_mask = ~0;
- x->sel.saddr.xfrm4_addr = fl->fl4_src;
- x->sel.saddr.xfrm4_mask = ~0;
- x->sel.dport = fl->uli_u.ports.dport;
- x->sel.dport_mask = ~0;
- x->sel.sport = fl->uli_u.ports.sport;
- x->sel.sport_mask = ~0;
- x->sel.prefixlen_d = 32;
- x->sel.prefixlen_s = 32;
- x->sel.proto = fl->proto;
- x->sel.ifindex = fl->oif;
- x->id = tmpl->id;
- if (x->id.daddr.xfrm4_addr == 0)
- x->id.daddr.xfrm4_addr = daddr;
- x->props.family = AF_INET;
- x->props.saddr = tmpl->saddr;
- if (x->props.saddr.xfrm4_addr == 0)
- x->props.saddr.xfrm4_addr = saddr;
- x->props.mode = tmpl->mode;
- x->props.reqid = tmpl->reqid;
- x->props.family = AF_INET;
-
- if (km_query(x, tmpl, pol) == 0) {
- x->km.state = XFRM_STATE_ACQ;
- list_add_tail(&x->bydst, xfrm_state_bydst+h);
- atomic_inc(&x->refcnt);
- if (x->id.spi) {
- h = ntohl(x->id.daddr.xfrm4_addr^x->id.spi^x->id.proto);
- h = (h ^ (h>>10) ^ (h>>20)) % XFRM_DST_HSIZE;
- list_add(&x->byspi, xfrm_state_byspi+h);
- atomic_inc(&x->refcnt);
- }
- x->lft.hard_add_expires_seconds = ACQ_EXPIRES;
- atomic_inc(&x->refcnt);
- mod_timer(&x->timer, ACQ_EXPIRES*HZ);
- } else {
- x->km.state = XFRM_STATE_DEAD;
- xfrm_state_put(x);
- x = NULL;
- error = 1;
- }
- }
- spin_unlock_bh(&xfrm_state_lock);
- if (!x)
- *err = acquire_in_progress ? -EAGAIN :
- (error ? -ESRCH : -ENOMEM);
- return x;
+ struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
+ if (!afinfo)
+ return -1;
+ afinfo->init_tempsel(x, fl, tmpl, daddr, saddr);
+ xfrm_state_put_afinfo(afinfo);
+ return 0;
}
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
struct xfrm_state *
-xfrm6_state_find(struct in6_addr *daddr, struct in6_addr *saddr, struct flowi *fl, struct xfrm_tmpl *tmpl,
- struct xfrm_policy *pol, int *err)
+xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
+ struct flowi *fl, struct xfrm_tmpl *tmpl,
+ struct xfrm_policy *pol, int *err,
+ unsigned short family)
{
- unsigned h = ntohl(daddr->s6_addr32[2]^daddr->s6_addr32[3]);
+ unsigned h = xfrm_dst_hash(daddr, family);
struct xfrm_state *x;
int acquire_in_progress = 0;
int error = 0;
struct xfrm_state *best = NULL;
- h = (h ^ (h>>16)) % XFRM_DST_HSIZE;
-
spin_lock_bh(&xfrm_state_lock);
list_for_each_entry(x, xfrm_state_bydst+h, bydst) {
- if (x->props.family == AF_INET6&&
- !ipv6_addr_cmp(daddr, (struct in6_addr *)&x->id.daddr) &&
+ if (x->props.family == family &&
x->props.reqid == tmpl->reqid &&
- (!ipv6_addr_cmp(saddr, (struct in6_addr *)&x->props.saddr)|| ipv6_addr_any(saddr)) &&
+ xfrm_state_addr_check(x, daddr, saddr, family) &&
tmpl->mode == x->props.mode &&
tmpl->id.proto == x->id.proto) {
/* Resolution logic:
selector.
*/
if (x->km.state == XFRM_STATE_VALID) {
- if (!xfrm6_selector_match(&x->sel, fl))
+ if (!xfrm_selector_match(&x->sel, fl, family))
continue;
if (!best ||
best->km.dying > x->km.dying ||
acquire_in_progress = 1;
} else if (x->km.state == XFRM_STATE_ERROR ||
x->km.state == XFRM_STATE_EXPIRED) {
- if (xfrm6_selector_match(&x->sel, fl))
+ if (xfrm_selector_match(&x->sel, fl, family))
error = 1;
}
}
spin_unlock_bh(&xfrm_state_lock);
return best;
}
+
x = NULL;
if (!error && !acquire_in_progress &&
((x = xfrm_state_alloc()) != NULL)) {
/* Initialize temporary selector matching only
* to current session. */
+ xfrm_init_tempsel(x, fl, tmpl, daddr, saddr, family);
+
memcpy(&x->sel.daddr, fl->fl6_dst, sizeof(struct in6_addr));
memcpy(&x->sel.saddr, fl->fl6_src, sizeof(struct in6_addr));
- x->sel.dport = fl->uli_u.ports.dport;
- x->sel.dport_mask = ~0;
- x->sel.sport = fl->uli_u.ports.sport;
- x->sel.sport_mask = ~0;
- x->sel.prefixlen_d = 128;
- x->sel.prefixlen_s = 128;
- x->sel.proto = fl->proto;
- x->sel.ifindex = fl->oif;
- x->id = tmpl->id;
- if (ipv6_addr_any((struct in6_addr*)&x->id.daddr))
- memcpy(&x->id.daddr, daddr, sizeof(x->sel.daddr));
- memcpy(&x->props.saddr, &tmpl->saddr, sizeof(x->props.saddr));
- if (ipv6_addr_any((struct in6_addr*)&x->props.saddr))
- memcpy(&x->props.saddr, saddr, sizeof(x->props.saddr));
- x->props.mode = tmpl->mode;
- x->props.reqid = tmpl->reqid;
- x->props.family = AF_INET6;
if (km_query(x, tmpl, pol) == 0) {
x->km.state = XFRM_STATE_ACQ;
list_add_tail(&x->bydst, xfrm_state_bydst+h);
atomic_inc(&x->refcnt);
if (x->id.spi) {
- struct in6_addr *addr = (struct in6_addr*)&x->id.daddr;
- h = ntohl((addr->s6_addr32[2]^addr->s6_addr32[3])^x->id.spi^x->id.proto);
- h = (h ^ (h>>10) ^ (h>>20)) % XFRM_DST_HSIZE;
+ h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, family);
list_add(&x->byspi, xfrm_state_byspi+h);
atomic_inc(&x->refcnt);
}
- x->lft.hard_add_expires_seconds = ACQ_EXPIRES;
+ x->lft.hard_add_expires_seconds = XFRM_ACQ_EXPIRES;
atomic_inc(&x->refcnt);
- mod_timer(&x->timer, ACQ_EXPIRES*HZ);
+ mod_timer(&x->timer, XFRM_ACQ_EXPIRES*HZ);
} else {
x->km.state = XFRM_STATE_DEAD;
xfrm_state_put(x);
(error ? -ESRCH : -ENOMEM);
return x;
}
-#endif /* CONFIG_IPV6 || CONFIG_IPV6_MODULE */
void xfrm_state_insert(struct xfrm_state *x)
{
- unsigned h = 0;
-
- switch (x->props.family) {
- case AF_INET:
- h = ntohl(x->id.daddr.xfrm4_addr);
- break;
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
- case AF_INET6:
- h = ntohl(x->id.daddr.a6[2]^x->id.daddr.a6[3]);
- break;
-#endif
- default:
- return;
- }
-
- h = (h ^ (h>>16)) % XFRM_DST_HSIZE;
+ unsigned h = xfrm_dst_hash(&x->id.daddr, x->props.family);
spin_lock_bh(&xfrm_state_lock);
list_add(&x->bydst, xfrm_state_bydst+h);
atomic_inc(&x->refcnt);
- if (x->props.family == AF_INET)
- h = ntohl(x->id.daddr.xfrm4_addr^x->id.spi^x->id.proto);
- else
- h = ntohl(x->id.daddr.a6[2]^x->id.daddr.a6[3]^x->id.spi^x->id.proto);
- h = (h ^ (h>>10) ^ (h>>20)) % XFRM_DST_HSIZE;
+ h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, x->props.family);
+
list_add(&x->byspi, xfrm_state_byspi+h);
atomic_inc(&x->refcnt);
if (x->curlft.bytes >= x->lft.hard_byte_limit ||
x->curlft.packets >= x->lft.hard_packet_limit) {
km_expired(x);
- if (!mod_timer(&x->timer, jiffies + ACQ_EXPIRES*HZ))
+ if (!mod_timer(&x->timer, jiffies + XFRM_ACQ_EXPIRES*HZ))
atomic_inc(&x->refcnt);
return -EINVAL;
}
}
struct xfrm_state *
-xfrm4_state_lookup(u32 daddr, u32 spi, u8 proto)
+xfrm_state_lookup(xfrm_address_t *daddr, u32 spi, u8 proto,
+ unsigned short family)
{
- unsigned h = ntohl(daddr^spi^proto);
struct xfrm_state *x;
-
- h = (h ^ (h>>10) ^ (h>>20)) % XFRM_DST_HSIZE;
+ struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
+ if (!afinfo)
+ return NULL;
spin_lock_bh(&xfrm_state_lock);
- list_for_each_entry(x, xfrm_state_byspi+h, byspi) {
- if (x->props.family == AF_INET &&
- spi == x->id.spi &&
- daddr == x->id.daddr.xfrm4_addr &&
- proto == x->id.proto) {
- atomic_inc(&x->refcnt);
- spin_unlock_bh(&xfrm_state_lock);
- return x;
- }
- }
+ x = afinfo->state_lookup(daddr, spi, proto);
spin_unlock_bh(&xfrm_state_lock);
- return NULL;
+ xfrm_state_put_afinfo(afinfo);
+ return x;
}
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
struct xfrm_state *
-xfrm6_state_lookup(struct in6_addr *daddr, u32 spi, u8 proto)
+xfrm_find_acq(u8 mode, u16 reqid, u8 proto,
+ xfrm_address_t *daddr, xfrm_address_t *saddr,
+ int create, unsigned short family)
{
- unsigned h = ntohl(daddr->s6_addr32[2]^daddr->s6_addr32[3]^spi^proto);
struct xfrm_state *x;
-
- h = (h ^ (h>>10) ^ (h>>20)) % XFRM_DST_HSIZE;
+ struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
+ if (!afinfo)
+ return NULL;
spin_lock_bh(&xfrm_state_lock);
- list_for_each_entry(x, xfrm_state_byspi+h, byspi) {
- if (x->props.family == AF_INET6 &&
- spi == x->id.spi &&
- !ipv6_addr_cmp(daddr, (struct in6_addr *)x->id.daddr.a6) &&
- proto == x->id.proto) {
- atomic_inc(&x->refcnt);
- spin_unlock_bh(&xfrm_state_lock);
- return x;
- }
- }
+ x = afinfo->find_acq(mode, reqid, proto, daddr, saddr, create);
spin_unlock_bh(&xfrm_state_lock);
- return NULL;
-}
-#endif
-
-struct xfrm_state *
-xfrm_find_acq(u8 mode, u16 reqid, u8 proto, u32 daddr, u32 saddr, int create)
-{
- struct xfrm_state *x, *x0;
- unsigned h = ntohl(daddr);
-
- h = (h ^ (h>>16)) % XFRM_DST_HSIZE;
- x0 = NULL;
-
- spin_lock_bh(&xfrm_state_lock);
- list_for_each_entry(x, xfrm_state_bydst+h, bydst) {
- if (x->props.family == AF_INET &&
- daddr == x->id.daddr.xfrm4_addr &&
- mode == x->props.mode &&
- proto == x->id.proto &&
- saddr == x->props.saddr.xfrm4_addr &&
- reqid == x->props.reqid &&
- x->km.state == XFRM_STATE_ACQ) {
- if (!x0)
- x0 = x;
- if (x->id.spi)
- continue;
- x0 = x;
- break;
- }
- }
- if (x0) {
- atomic_inc(&x0->refcnt);
- } else if (create && (x0 = xfrm_state_alloc()) != NULL) {
- x0->sel.daddr.xfrm4_addr = daddr;
- x0->sel.daddr.xfrm4_mask = ~0;
- x0->sel.saddr.xfrm4_addr = saddr;
- x0->sel.saddr.xfrm4_mask = ~0;
- x0->sel.prefixlen_d = 32;
- x0->sel.prefixlen_s = 32;
- x0->props.saddr.xfrm4_addr = saddr;
- x0->km.state = XFRM_STATE_ACQ;
- x0->id.daddr.xfrm4_addr = daddr;
- x0->id.proto = proto;
- x0->props.mode = mode;
- x0->props.reqid = reqid;
- x0->props.family = AF_INET;
- x0->lft.hard_add_expires_seconds = ACQ_EXPIRES;
- atomic_inc(&x0->refcnt);
- mod_timer(&x0->timer, jiffies + ACQ_EXPIRES*HZ);
- atomic_inc(&x0->refcnt);
- list_add_tail(&x0->bydst, xfrm_state_bydst+h);
- wake_up(&km_waitq);
- }
- spin_unlock_bh(&xfrm_state_lock);
- return x0;
-}
-
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-struct xfrm_state *
-xfrm6_find_acq(u8 mode, u16 reqid, u8 proto, struct in6_addr *daddr, struct in6_addr *saddr, int create)
-{
- struct xfrm_state *x, *x0;
- unsigned h = ntohl(daddr->s6_addr32[2]^daddr->s6_addr32[3]);
-
- h = (h ^ (h>>16)) % XFRM_DST_HSIZE;
- x0 = NULL;
-
- spin_lock_bh(&xfrm_state_lock);
- list_for_each_entry(x, xfrm_state_bydst+h, bydst) {
- if (x->props.family == AF_INET6 &&
- !ipv6_addr_cmp(daddr, (struct in6_addr *)x->id.daddr.a6) &&
- mode == x->props.mode &&
- proto == x->id.proto &&
- !ipv6_addr_cmp(saddr, (struct in6_addr *)x->props.saddr.a6) &&
- reqid == x->props.reqid &&
- x->km.state == XFRM_STATE_ACQ) {
- if (!x0)
- x0 = x;
- if (x->id.spi)
- continue;
- x0 = x;
- break;
- }
- }
- if (x0) {
- atomic_inc(&x0->refcnt);
- } else if (create && (x0 = xfrm_state_alloc()) != NULL) {
- memcpy(x0->sel.daddr.a6, daddr, sizeof(struct in6_addr));
- memcpy(x0->sel.saddr.a6, saddr, sizeof(struct in6_addr));
- x0->sel.prefixlen_d = 128;
- x0->sel.prefixlen_s = 128;
- memcpy(x0->props.saddr.a6, saddr, sizeof(struct in6_addr));
- x0->km.state = XFRM_STATE_ACQ;
- memcpy(x0->id.daddr.a6, daddr, sizeof(struct in6_addr));
- x0->id.proto = proto;
- x0->props.family = AF_INET6;
- x0->props.mode = mode;
- x0->props.reqid = reqid;
- x0->lft.hard_add_expires_seconds = ACQ_EXPIRES;
- atomic_inc(&x0->refcnt);
- mod_timer(&x0->timer, jiffies + ACQ_EXPIRES*HZ);
- atomic_inc(&x0->refcnt);
- list_add_tail(&x0->bydst, xfrm_state_bydst+h);
- wake_up(&km_waitq);
- }
- spin_unlock_bh(&xfrm_state_lock);
- return x0;
+ xfrm_state_put_afinfo(afinfo);
+ return x;
}
-#endif
/* Silly enough, but I'm lazy to build resolution list */
return;
if (minspi == maxspi) {
- switch(x->props.family) {
- case AF_INET:
- x0 = xfrm4_state_lookup(x->id.daddr.xfrm4_addr, minspi, x->id.proto);
- break;
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
- case AF_INET6:
- x0 = xfrm6_state_lookup((struct in6_addr*)x->id.daddr.a6, minspi, x->id.proto);
- break;
-#endif
- default:
- x0 = NULL;
- }
+ x0 = xfrm_state_lookup(&x->id.daddr, minspi, x->id.proto, x->props.family);
if (x0) {
xfrm_state_put(x0);
return;
maxspi = ntohl(maxspi);
for (h=0; h<maxspi-minspi+1; h++) {
spi = minspi + net_random()%(maxspi-minspi+1);
- switch(x->props.family) {
- case AF_INET:
- x0 = xfrm4_state_lookup(x->id.daddr.xfrm4_addr, minspi, x->id.proto);
- break;
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
- case AF_INET6:
- x0 = xfrm6_state_lookup((struct in6_addr*)x->id.daddr.a6, minspi, x->id.proto);
- break;
-#endif
- default:
- x0 = NULL;
- }
+ x0 = xfrm_state_lookup(&x->id.daddr, minspi, x->id.proto, x->props.family);
if (x0 == NULL)
break;
xfrm_state_put(x0);
}
if (x->id.spi) {
spin_lock_bh(&xfrm_state_lock);
- switch(x->props.family) {
- case AF_INET:
- h = ntohl(x->id.daddr.xfrm4_addr^x->id.spi^x->id.proto);
- break;
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
- case AF_INET6:
- h = ntohl(x->id.daddr.a6[2]^x->id.daddr.a6[3]^x->id.spi^x->id.proto);
- break;
-#endif
- default:
- h = 0; /* XXX */
- }
- h = (h ^ (h>>10) ^ (h>>20)) % XFRM_DST_HSIZE;
+ h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, x->props.family);
list_add(&x->byspi, xfrm_state_byspi+h);
atomic_inc(&x->refcnt);
spin_unlock_bh(&xfrm_state_lock);
for (i=0; i<n; i++) {
int match;
- switch(x[i]->props.family) {
- case AF_INET:
- match = xfrm4_selector_match(&x[i]->sel, fl);
- break;
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
- case AF_INET6:
- match = xfrm6_selector_match(&x[i]->sel, fl);
- break;
-#endif
- default:
- match = 0;
- }
+ match = xfrm_selector_match(&x[i]->sel, fl, x[i]->props.family);
if (!match)
return -EINVAL;
}
return 0;
}
+int xfrm_state_register_afinfo(struct xfrm_state_afinfo *afinfo)
+{
+ int err = 0;
+ if (unlikely(afinfo == NULL))
+ return -EINVAL;
+ if (unlikely(afinfo->family >= NPROTO))
+ return -EAFNOSUPPORT;
+ write_lock(&xfrm_state_afinfo_lock);
+ if (unlikely(xfrm_state_afinfo[afinfo->family] != NULL))
+ err = -ENOBUFS;
+ else {
+ afinfo->state_bydst = xfrm_state_bydst;
+ afinfo->state_byspi = xfrm_state_byspi;
+ xfrm_state_afinfo[afinfo->family] = afinfo;
+ }
+ write_unlock(&xfrm_state_afinfo_lock);
+ return err;
+}
+
+int xfrm_state_unregister_afinfo(struct xfrm_state_afinfo *afinfo)
+{
+ int err = 0;
+ if (unlikely(afinfo == NULL))
+ return -EINVAL;
+ if (unlikely(afinfo->family >= NPROTO))
+ return -EAFNOSUPPORT;
+ write_lock(&xfrm_state_afinfo_lock);
+ if (likely(xfrm_state_afinfo[afinfo->family] != NULL)) {
+ if (unlikely(xfrm_state_afinfo[afinfo->family] != afinfo))
+ err = -EINVAL;
+ else {
+ xfrm_state_afinfo[afinfo->family] = NULL;
+ afinfo->state_byspi = NULL;
+ afinfo->state_bydst = NULL;
+ }
+ }
+ write_unlock(&xfrm_state_afinfo_lock);
+ return err;
+}
+
+struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned short family)
+{
+ struct xfrm_state_afinfo *afinfo;
+ if (unlikely(family >= NPROTO))
+ return NULL;
+ read_lock(&xfrm_state_afinfo_lock);
+ afinfo = xfrm_state_afinfo[family];
+ if (likely(afinfo != NULL))
+ read_lock(&afinfo->lock);
+ read_unlock(&xfrm_state_afinfo_lock);
+ return afinfo;
+}
+
+void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo)
+{
+ if (unlikely(afinfo == NULL))
+ return;
+ read_unlock(&afinfo->lock);
+}
+
void __init xfrm_state_init(void)
{
int i;
*
* Copyright (C) 2002 David S. Miller (davem@redhat.com)
*
- * Changes
- *
- * Mitsuru KANDA @USAGI : IPv6 Support
- * Kazunori MIYAZAWA @USAGI :
- * Kunihiro Ishiguro :
+ * Changes:
+ * Mitsuru KANDA @USAGI
+ * Kazunori MIYAZAWA @USAGI
+ * Kunihiro Ishiguro
+ * IPv6 support
*
*/
#include <linux/ipsec.h>
#include <linux/init.h>
#include <linux/security.h>
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-#include <linux/in6.h>
-#endif
#include <net/sock.h>
#include <net/xfrm.h>
goto error;
err = -ENOENT;
- switch (x->props.family) {
- case AF_INET:
- x->type = xfrm_get_type(x->id.proto);
- break;
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
- case AF_INET6:
- x->type = xfrm6_get_type(x->id.proto);
- break;
-#endif
- default:
- x->type = NULL;
- break;
- }
+ x->type = xfrm_get_type(x->id.proto, x->props.family);
if (x->type == NULL)
goto error;
if (!x)
return err;
- switch (x->props.family) {
- case AF_INET:
- x1 = xfrm4_state_lookup(x->props.saddr.xfrm4_addr,
- x->id.spi, x->id.proto);
- break;
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
- case AF_INET6:
- x1 = xfrm6_state_lookup((struct in6_addr*)x->props.saddr.a6,
- x->id.spi, x->id.proto);
- break;
-#endif
- default:
- x1 = NULL;
- break;
- }
+ x1 = xfrm_state_lookup(&x->props.saddr, x->id.spi, x->id.proto, x->props.family);
if (x1) {
xfrm_state_put(x);
xfrm_state_put(x1);
struct xfrm_state *x;
struct xfrm_usersa_id *p = NLMSG_DATA(nlh);
- switch (p->family) {
- case AF_INET:
- x = xfrm4_state_lookup(p->saddr.xfrm4_addr, p->spi, p->proto);
- break;
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
- case AF_INET6:
- x = xfrm6_state_lookup((struct in6_addr*)p->saddr.a6, p->spi, p->proto);
- break;
-#endif
- default:
- x = NULL;
- break;
- }
+ x = xfrm_state_lookup(&p->saddr, p->spi, p->proto, p->family);
if (x == NULL)
return -ESRCH;
struct sk_buff *resp_skb;
int err;
- switch (p->family) {
- case AF_INET:
- x = xfrm4_state_lookup(p->saddr.xfrm4_addr, p->spi, p->proto);
- break;
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
- case AF_INET6:
- x = xfrm6_state_lookup((struct in6_addr*)p->saddr.a6, p->spi, p->proto);
- break;
-#endif
- default:
- x = NULL;
- break;
- }
+ x = xfrm_state_lookup(&p->saddr, p->spi, p->proto, p->family);
err = -ESRCH;
if (x == NULL)
goto out_noput;
err = verify_userspi_info(p);
if (err)
goto out_noput;
- switch (p->info.family) {
- case AF_INET:
- x = xfrm_find_acq(p->info.mode, p->info.reqid, p->info.id.proto,
- p->info.sel.daddr.xfrm4_addr,
- p->info.sel.saddr.xfrm4_addr, 1);
- break;
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
- case AF_INET6:
- x = xfrm6_find_acq(p->info.mode, p->info.reqid, p->info.id.proto,
- (struct in6_addr*)p->info.sel.daddr.a6,
- (struct in6_addr*)p->info.sel.saddr.a6, 1);
- break;
-#endif
- default:
- x = NULL;
- break;
- }
+ x = xfrm_find_acq(p->info.mode, p->info.reqid, p->info.id.proto,
+ &p->info.sel.daddr,
+ &p->info.sel.saddr, 1,
+ p->info.family);
err = -ENOENT;
if (x == NULL)
goto out_noput;
route.o ip6_fib.o ipv6_sockglue.o ndisc.o udp.o raw.o \
protocol.o icmp.o mcast.o reassembly.o tcp_ipv6.o \
exthdrs.o sysctl_net_ipv6.o datagram.o proc.o \
- ip6_flowlabel.o ipv6_syms.o
+ ip6_flowlabel.o ipv6_syms.o \
+ xfrm6_policy.o xfrm6_state.o xfrm6_input.o
obj-$(CONFIG_INET6_AH) += ah6.o
obj-$(CONFIG_INET6_ESP) += esp6.o
type != ICMPV6_PKT_TOOBIG)
return;
- x = xfrm6_state_lookup(&iph->daddr, ah->spi, IPPROTO_AH);
+ x = xfrm_state_lookup((xfrm_address_t *)&iph->daddr, ah->spi, IPPROTO_AH, AF_INET6);
if (!x)
return;
{
SET_MODULE_OWNER(&ah6_type);
- if (xfrm6_register_type(&ah6_type) < 0) {
+ if (xfrm_register_type(&ah6_type, AF_INET6) < 0) {
printk(KERN_INFO "ipv6 ah init: can't add xfrm type\n");
return -EAGAIN;
}
if (inet6_add_protocol(&ah6_protocol, IPPROTO_AH) < 0) {
printk(KERN_INFO "ipv6 ah init: can't add protocol\n");
- xfrm6_unregister_type(&ah6_type);
+ xfrm_unregister_type(&ah6_type, AF_INET6);
return -EAGAIN;
}
if (inet6_del_protocol(&ah6_protocol, IPPROTO_AH) < 0)
printk(KERN_INFO "ipv6 ah close: can't remove protocol\n");
- if (xfrm6_unregister_type(&ah6_type) < 0)
+ if (xfrm_unregister_type(&ah6_type, AF_INET6) < 0)
printk(KERN_INFO "ipv6 ah close: can't remove xfrm type\n");
}
type != ICMPV6_PKT_TOOBIG)
return;
- x = xfrm6_state_lookup(&iph->daddr, esph->spi, IPPROTO_ESP);
+ x = xfrm_state_lookup((xfrm_address_t *)&iph->daddr, esph->spi, IPPROTO_ESP, AF_INET6);
if (!x)
return;
printk(KERN_DEBUG "pmtu discvovery on SA ESP/%08x/"
int __init esp6_init(void)
{
SET_MODULE_OWNER(&esp6_type);
- if (xfrm6_register_type(&esp6_type) < 0) {
+ if (xfrm_register_type(&esp6_type, AF_INET6) < 0) {
printk(KERN_INFO "ipv6 esp init: can't add xfrm type\n");
return -EAGAIN;
}
if (inet6_add_protocol(&esp6_protocol, IPPROTO_ESP) < 0) {
printk(KERN_INFO "ipv6 esp init: can't add protocol\n");
- xfrm6_unregister_type(&esp6_type);
+ xfrm_unregister_type(&esp6_type, AF_INET6);
return -EAGAIN;
}
{
if (inet6_del_protocol(&esp6_protocol, IPPROTO_ESP) < 0)
printk(KERN_INFO "ipv6 esp close: can't remove protocol\n");
- if (xfrm6_unregister_type(&esp6_type) < 0)
+ if (xfrm_unregister_type(&esp6_type, AF_INET6) < 0)
printk(KERN_INFO "ipv6 esp close: can't remove xfrm type\n");
}
#include <net/ipv6.h>
#include <net/addrconf.h>
#include <net/ip6_route.h>
+#include <net/xfrm.h>
EXPORT_SYMBOL(ipv6_addr_type);
EXPORT_SYMBOL(icmpv6_send);
EXPORT_SYMBOL(ipv6_chk_addr);
EXPORT_SYMBOL(in6addr_any);
EXPORT_SYMBOL(in6addr_loopback);
+EXPORT_SYMBOL(xfrm6_rcv);
+EXPORT_SYMBOL(xfrm6_clear_mutable_options);
#endif
-int xfrm6_dst_lookup(struct xfrm_dst **dst, struct flowi *fl)
-{
- int err = 0;
- *dst = (struct xfrm_dst*)ip6_route_output(NULL, fl);
- if (!*dst)
- err = -ENETUNREACH;
- return err;
-}
-
void __init ip6_route_init(void)
{
ip6_dst_ops.kmem_cachep = kmem_cache_create("ip6_dst_cache",
0, SLAB_HWCACHE_ALIGN,
NULL, NULL);
fib6_init();
- xfrm_dst_lookup_register(xfrm6_dst_lookup, AF_INET6);
#ifdef CONFIG_PROC_FS
proc_net_create("ipv6_route", 0, rt6_proc_info);
proc_net_create("rt6_stats", 0, rt6_proc_stats);
#endif
+ xfrm6_init();
}
#ifdef MODULE
proc_net_remove("ipv6_route");
proc_net_remove("rt6_stats");
#endif
- xfrm_dst_lookup_unregister(AF_INET6);
+ xfrm6_fini();
rt6_ifdown(NULL);
fib6_gc_cleanup();
}
--- /dev/null
+/*
+ * xfrm6_input.c: based on net/ipv4/xfrm4_input.c
+ *
+ * Authors:
+ * Mitsuru KANDA @USAGI
+ * Kazunori MIYAZAWA @USAGI
+ * Kunihiro Ishiguro
+ * YOSHIFUJI Hideaki @USAGI
+ * IPv6 support
+ */
+
+#include <net/ip.h>
+#include <net/ipv6.h>
+#include <net/xfrm.h>
+
+static kmem_cache_t *secpath_cachep;
+
+static int zero_out_mutable_opts(struct ipv6_opt_hdr *opthdr)
+{
+ u8 *opt = (u8 *)opthdr;
+ int len = ipv6_optlen(opthdr);
+ int off = 0;
+ int optlen = 0;
+
+ off += 2;
+ len -= 2;
+
+ while (len > 0) {
+
+ switch (opt[off]) {
+
+ case IPV6_TLV_PAD0:
+ optlen = 1;
+ break;
+ default:
+ if (len < 2)
+ goto bad;
+ optlen = opt[off+1]+2;
+ if (len < optlen)
+ goto bad;
+ if (opt[off] & 0x20)
+ memset(&opt[off+2], 0, opt[off+1]);
+ break;
+ }
+
+ off += optlen;
+ len -= optlen;
+ }
+ if (len == 0)
+ return 1;
+
+bad:
+ return 0;
+}
+
+int xfrm6_clear_mutable_options(struct sk_buff *skb, u16 *nh_offset, int dir)
+{
+ u16 offset = sizeof(struct ipv6hdr);
+ struct ipv6_opt_hdr *exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset);
+ unsigned int packet_len = skb->tail - skb->nh.raw;
+ u8 nexthdr = skb->nh.ipv6h->nexthdr;
+ u8 nextnexthdr = 0;
+
+ *nh_offset = ((unsigned char *)&skb->nh.ipv6h->nexthdr) - skb->nh.raw;
+
+ while (offset + 1 <= packet_len) {
+
+ switch (nexthdr) {
+
+ case NEXTHDR_HOP:
+ *nh_offset = offset;
+ offset += ipv6_optlen(exthdr);
+ if (!zero_out_mutable_opts(exthdr)) {
+ if (net_ratelimit())
+ printk(KERN_WARNING "overrun hopopts\n");
+ return 0;
+ }
+ nexthdr = exthdr->nexthdr;
+ exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset);
+ break;
+
+ case NEXTHDR_ROUTING:
+ *nh_offset = offset;
+ offset += ipv6_optlen(exthdr);
+ ((struct ipv6_rt_hdr*)exthdr)->segments_left = 0;
+ nexthdr = exthdr->nexthdr;
+ exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset);
+ break;
+
+ case NEXTHDR_DEST:
+ *nh_offset = offset;
+ offset += ipv6_optlen(exthdr);
+ if (!zero_out_mutable_opts(exthdr)) {
+ if (net_ratelimit())
+ printk(KERN_WARNING "overrun destopt\n");
+ return 0;
+ }
+ nexthdr = exthdr->nexthdr;
+ exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset);
+ break;
+
+ case NEXTHDR_AUTH:
+ if (dir == XFRM_POLICY_OUT) {
+ memset(((struct ipv6_auth_hdr*)exthdr)->auth_data, 0,
+ (((struct ipv6_auth_hdr*)exthdr)->hdrlen - 1) << 2);
+ }
+ if (exthdr->nexthdr == NEXTHDR_DEST) {
+ offset += (((struct ipv6_auth_hdr*)exthdr)->hdrlen + 2) << 2;
+ exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset);
+ nextnexthdr = exthdr->nexthdr;
+ if (!zero_out_mutable_opts(exthdr)) {
+ if (net_ratelimit())
+ printk(KERN_WARNING "overrun destopt\n");
+ return 0;
+ }
+ }
+ return nexthdr;
+ default :
+ return nexthdr;
+ }
+ }
+
+ return nexthdr;
+}
+
+int xfrm6_rcv(struct sk_buff *skb)
+{
+ int err;
+ u32 spi, seq;
+ struct xfrm_state *xfrm_vec[XFRM_MAX_DEPTH];
+ struct xfrm_state *x;
+ int xfrm_nr = 0;
+ int decaps = 0;
+ struct ipv6hdr *hdr = skb->nh.ipv6h;
+ unsigned char *tmp_hdr = NULL;
+ int hdr_len = 0;
+ u16 nh_offset = 0;
+ u8 nexthdr = 0;
+
+ if (hdr->nexthdr == IPPROTO_AH || hdr->nexthdr == IPPROTO_ESP) {
+ nh_offset = ((unsigned char*)&skb->nh.ipv6h->nexthdr) - skb->nh.raw;
+ hdr_len = sizeof(struct ipv6hdr);
+ } else {
+ hdr_len = skb->h.raw - skb->nh.raw;
+ }
+
+ tmp_hdr = kmalloc(hdr_len, GFP_ATOMIC);
+ if (!tmp_hdr)
+ goto drop;
+ memcpy(tmp_hdr, skb->nh.raw, hdr_len);
+
+ nexthdr = xfrm6_clear_mutable_options(skb, &nh_offset, XFRM_POLICY_IN);
+ hdr->priority = 0;
+ hdr->flow_lbl[0] = 0;
+ hdr->flow_lbl[1] = 0;
+ hdr->flow_lbl[2] = 0;
+ hdr->hop_limit = 0;
+
+ if ((err = xfrm_parse_spi(skb, nexthdr, &spi, &seq)) != 0)
+ goto drop;
+
+ do {
+ struct ipv6hdr *iph = skb->nh.ipv6h;
+
+ if (xfrm_nr == XFRM_MAX_DEPTH)
+ goto drop;
+
+ x = xfrm_state_lookup((xfrm_address_t *)&iph->daddr, spi, nexthdr, AF_INET6);
+ if (x == NULL)
+ goto drop;
+ spin_lock(&x->lock);
+ if (unlikely(x->km.state != XFRM_STATE_VALID))
+ goto drop_unlock;
+
+ if (x->props.replay_window && xfrm_replay_check(x, seq))
+ goto drop_unlock;
+
+ nexthdr = x->type->input(x, skb);
+ if (nexthdr <= 0)
+ goto drop_unlock;
+
+ if (x->props.replay_window)
+ xfrm_replay_advance(x, seq);
+
+ x->curlft.bytes += skb->len;
+ x->curlft.packets++;
+
+ spin_unlock(&x->lock);
+
+ xfrm_vec[xfrm_nr++] = x;
+
+ iph = skb->nh.ipv6h; /* ??? */
+
+ if (nexthdr == NEXTHDR_DEST) {
+ if (!pskb_may_pull(skb, (skb->h.raw-skb->data)+8) ||
+ !pskb_may_pull(skb, (skb->h.raw-skb->data)+((skb->h.raw[1]+1)<<3))) {
+ err = -EINVAL;
+ goto drop;
+ }
+ nexthdr = skb->h.raw[0];
+ nh_offset = skb->h.raw - skb->nh.raw;
+ skb_pull(skb, (skb->h.raw[1]+1)<<3);
+ skb->h.raw = skb->data;
+ }
+
+ if (x->props.mode) { /* XXX */
+ if (iph->nexthdr != IPPROTO_IPV6)
+ goto drop;
+ skb->nh.raw = skb->data;
+ iph = skb->nh.ipv6h;
+ decaps = 1;
+ break;
+ }
+
+ if ((err = xfrm_parse_spi(skb, nexthdr, &spi, &seq)) < 0)
+ goto drop;
+ } while (!err);
+
+ memcpy(skb->nh.raw, tmp_hdr, hdr_len);
+ skb->nh.raw[nh_offset] = nexthdr;
+ skb->nh.ipv6h->payload_len = htons(hdr_len + skb->len - sizeof(struct ipv6hdr));
+
+ /* Allocate new secpath or COW existing one. */
+ if (!skb->sp || atomic_read(&skb->sp->refcnt) != 1) {
+ kmem_cache_t *pool = skb->sp ? skb->sp->pool : secpath_cachep;
+ struct sec_path *sp;
+ sp = kmem_cache_alloc(pool, SLAB_ATOMIC);
+ if (!sp)
+ goto drop;
+ if (skb->sp) {
+ memcpy(sp, skb->sp, sizeof(struct sec_path));
+ secpath_put(skb->sp);
+ } else {
+ sp->pool = pool;
+ sp->len = 0;
+ }
+ atomic_set(&sp->refcnt, 1);
+ skb->sp = sp;
+ }
+
+ if (xfrm_nr + skb->sp->len > XFRM_MAX_DEPTH)
+ goto drop;
+
+ memcpy(skb->sp->xvec+skb->sp->len, xfrm_vec, xfrm_nr*sizeof(void*));
+ skb->sp->len += xfrm_nr;
+
+ if (decaps) {
+ if (!(skb->dev->flags&IFF_LOOPBACK)) {
+ dst_release(skb->dst);
+ skb->dst = NULL;
+ }
+ netif_rx(skb);
+ return 0;
+ } else {
+ return -nexthdr;
+ }
+
+drop_unlock:
+ spin_unlock(&x->lock);
+ xfrm_state_put(x);
+drop:
+ if (tmp_hdr) kfree(tmp_hdr);
+ while (--xfrm_nr >= 0)
+ xfrm_state_put(xfrm_vec[xfrm_nr]);
+ kfree_skb(skb);
+ return 0;
+}
+
+void __init xfrm6_input_init(void)
+{
+ secpath_cachep = kmem_cache_create("secpath6_cache",
+ sizeof(struct sec_path),
+ 0, SLAB_HWCACHE_ALIGN,
+ NULL, NULL);
+
+ if (!secpath_cachep)
+ panic("IPv6: failed to allocate secpath6_cache\n");
+}
+
--- /dev/null
+/*
+ * xfrm6_policy.c: based on xfrm4_policy.c
+ *
+ * Authors:
+ * Mitsuru KANDA @USAGI
+ * Kazunori MIYAZAWA @USAGI
+ * Kunihiro Ishiguro
+ * IPv6 support
+ * YOSHIFUJI Hideaki
+ * Split up af-specific portion
+ *
+ */
+
+#include <linux/config.h>
+#include <net/xfrm.h>
+#include <net/ip.h>
+#include <net/ipv6.h>
+#include <net/ip6_route.h>
+
+extern struct dst_ops xfrm6_dst_ops;
+extern struct xfrm_policy_afinfo xfrm6_policy_afinfo;
+
+static struct xfrm_type_map xfrm6_type_map = { .lock = RW_LOCK_UNLOCKED };
+
+int xfrm6_dst_lookup(struct xfrm_dst **dst, struct flowi *fl)
+{
+ int err = 0;
+ *dst = (struct xfrm_dst*)ip6_route_output(NULL, fl);
+ if (!*dst)
+ err = -ENETUNREACH;
+ return err;
+}
+
+/* Check that the bundle accepts the flow and its components are
+ * still valid.
+ */
+
+static int __xfrm6_bundle_ok(struct xfrm_dst *xdst, struct flowi *fl)
+{
+ do {
+ if (xdst->u.dst.ops != &xfrm6_dst_ops)
+ return 1;
+
+ if (!xfrm_selector_match(&xdst->u.dst.xfrm->sel, fl, AF_INET6))
+ return 0;
+ if (xdst->u.dst.xfrm->km.state != XFRM_STATE_VALID ||
+ xdst->u.dst.path->obsolete > 0)
+ return 0;
+ xdst = (struct xfrm_dst*)xdst->u.dst.child;
+ } while (xdst);
+ return 0;
+}
+
+static struct dst_entry *
+__xfrm6_find_bundle(struct flowi *fl, struct rtable *rt, struct xfrm_policy *policy)
+{
+ struct dst_entry *dst;
+
+ /* Still not clear if we should set fl->fl6_{src,dst}... */
+ read_lock_bh(&policy->lock);
+ for (dst = policy->bundles; dst; dst = dst->next) {
+ struct xfrm_dst *xdst = (struct xfrm_dst*)dst;
+ if (!ipv6_addr_cmp(&xdst->u.rt6.rt6i_dst.addr, fl->fl6_dst) &&
+ !ipv6_addr_cmp(&xdst->u.rt6.rt6i_src.addr, fl->fl6_src) &&
+ __xfrm6_bundle_ok(xdst, fl)) {
+ dst_clone(dst);
+ break;
+ }
+ }
+ read_unlock_bh(&policy->lock);
+ return dst;
+}
+
+/* Allocate chain of dst_entry's, attach known xfrm's, calculate
+ * all the metrics... Shortly, bundle a bundle.
+ */
+
+static int
+__xfrm6_bundle_create(struct xfrm_policy *policy, struct xfrm_state **xfrm, int nx,
+ struct flowi *fl, struct dst_entry **dst_p)
+{
+ struct dst_entry *dst, *dst_prev;
+ struct rt6_info *rt0 = (struct rt6_info*)(*dst_p);
+ struct rt6_info *rt = rt0;
+ struct in6_addr *remote = fl->fl6_dst;
+ struct in6_addr *local = fl->fl6_src;
+ int i;
+ int err = 0;
+ int header_len = 0;
+ int trailer_len = 0;
+
+ dst = dst_prev = NULL;
+
+ for (i = 0; i < nx; i++) {
+ struct dst_entry *dst1 = dst_alloc(&xfrm6_dst_ops);
+
+ if (unlikely(dst1 == NULL)) {
+ err = -ENOBUFS;
+ goto error;
+ }
+
+ dst1->xfrm = xfrm[i];
+ if (!dst)
+ dst = dst1;
+ else {
+ dst_prev->child = dst1;
+ dst1->flags |= DST_NOHASH;
+ dst_clone(dst1);
+ }
+ dst_prev = dst1;
+ if (xfrm[i]->props.mode) {
+ remote = (struct in6_addr*)&xfrm[i]->id.daddr;
+ local = (struct in6_addr*)&xfrm[i]->props.saddr;
+ }
+ header_len += xfrm[i]->props.header_len;
+ trailer_len += xfrm[i]->props.trailer_len;
+ }
+
+ if (ipv6_addr_cmp(remote, fl->fl6_dst)) {
+ struct flowi fl_tunnel = { .nl_u = { .ip6_u =
+ { .daddr = remote,
+ .saddr = local }
+ }
+ };
+ err = xfrm_dst_lookup((struct xfrm_dst**)&rt, &fl_tunnel, AF_INET6);
+ if (err)
+ goto error;
+ } else {
+ dst_hold(&rt->u.dst);
+ }
+ dst_prev->child = &rt->u.dst;
+ for (dst_prev = dst; dst_prev != &rt->u.dst; dst_prev = dst_prev->child) {
+ struct xfrm_dst *x = (struct xfrm_dst*)dst_prev;
+ x->u.rt.fl = *fl;
+
+ dst_prev->dev = rt->u.dst.dev;
+ if (rt->u.dst.dev)
+ dev_hold(rt->u.dst.dev);
+ dst_prev->obsolete = -1;
+ dst_prev->flags |= DST_HOST;
+ dst_prev->lastuse = jiffies;
+ dst_prev->header_len = header_len;
+ dst_prev->trailer_len = trailer_len;
+ memcpy(&dst_prev->metrics, &rt->u.dst.metrics, sizeof(dst_prev->metrics));
+ dst_prev->path = &rt->u.dst;
+
+ /* Copy neighbout for reachability confirmation */
+ dst_prev->neighbour = neigh_clone(rt->u.dst.neighbour);
+ dst_prev->input = rt->u.dst.input;
+ dst_prev->output = dst_prev->xfrm->type->output;
+ /* Sheit... I remember I did this right. Apparently,
+ * it was magically lost, so this code needs audit */
+ x->u.rt6.rt6i_flags = rt0->rt6i_flags&(RTCF_BROADCAST|RTCF_MULTICAST|RTCF_LOCAL);
+ x->u.rt6.rt6i_metric = rt0->rt6i_metric;
+ x->u.rt6.rt6i_node = rt0->rt6i_node;
+ x->u.rt6.rt6i_hoplimit = rt0->rt6i_hoplimit;
+ x->u.rt6.rt6i_gateway = rt0->rt6i_gateway;
+ memcpy(&x->u.rt6.rt6i_gateway, &rt0->rt6i_gateway, sizeof(x->u.rt6.rt6i_gateway));
+ header_len -= x->u.dst.xfrm->props.header_len;
+ trailer_len -= x->u.dst.xfrm->props.trailer_len;
+ }
+ *dst_p = dst;
+ return 0;
+
+error:
+ if (dst)
+ dst_free(dst);
+ return err;
+}
+
+static inline void
+_decode_session6(struct sk_buff *skb, struct flowi *fl)
+{
+ u16 offset = sizeof(struct ipv6hdr);
+ struct ipv6hdr *hdr = skb->nh.ipv6h;
+ struct ipv6_opt_hdr *exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset);
+ u8 nexthdr = skb->nh.ipv6h->nexthdr;
+
+ fl->fl6_dst = &hdr->daddr;
+ fl->fl6_src = &hdr->saddr;
+
+ while (pskb_may_pull(skb, skb->nh.raw + offset + 1 - skb->data)) {
+ switch (nexthdr) {
+ case NEXTHDR_ROUTING:
+ case NEXTHDR_HOP:
+ case NEXTHDR_DEST:
+ offset += ipv6_optlen(exthdr);
+ nexthdr = exthdr->nexthdr;
+ exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset);
+ break;
+
+ case IPPROTO_UDP:
+ case IPPROTO_TCP:
+ case IPPROTO_SCTP:
+ if (pskb_may_pull(skb, skb->nh.raw + offset + 4 - skb->data)) {
+ u16 *ports = (u16 *)exthdr;
+
+ fl->uli_u.ports.sport = ports[0];
+ fl->uli_u.ports.dport = ports[1];
+ }
+ return;
+
+ /* XXX Why are there these headers? */
+ case IPPROTO_AH:
+ case IPPROTO_ESP:
+ default:
+ fl->uli_u.spi = 0;
+ return;
+ };
+ }
+}
+
+static inline int xfrm6_garbage_collect(void)
+{
+ read_lock(&xfrm6_policy_afinfo.lock);
+ xfrm6_policy_afinfo.garbage_collect();
+ read_unlock(&xfrm6_policy_afinfo.lock);
+ return (atomic_read(&xfrm6_dst_ops.entries) > xfrm6_dst_ops.gc_thresh*2);
+}
+
+static void xfrm6_update_pmtu(struct dst_entry *dst, u32 mtu)
+{
+ struct dst_entry *path = dst->path;
+
+ if (mtu >= 1280 && mtu < dst_pmtu(dst))
+ return;
+
+ path->ops->update_pmtu(path, mtu);
+}
+
+struct dst_ops xfrm6_dst_ops = {
+ .family = AF_INET6,
+ .protocol = __constant_htons(ETH_P_IPV6),
+ .gc = xfrm6_garbage_collect,
+ .update_pmtu = xfrm6_update_pmtu,
+ .gc_thresh = 1024,
+ .entry_size = sizeof(struct xfrm_dst),
+};
+
+struct xfrm_policy_afinfo xfrm6_policy_afinfo = {
+ .family = AF_INET6,
+ .lock = RW_LOCK_UNLOCKED,
+ .type_map = &xfrm6_type_map,
+ .dst_ops = &xfrm6_dst_ops,
+ .dst_lookup = xfrm6_dst_lookup,
+ .find_bundle = __xfrm6_find_bundle,
+ .bundle_create = __xfrm6_bundle_create,
+ .decode_session = _decode_session6,
+};
+
+void __init xfrm6_policy_init(void)
+{
+ xfrm_policy_register_afinfo(&xfrm6_policy_afinfo);
+}
+
+void __exit xfrm6_policy_fini(void)
+{
+ xfrm_policy_unregister_afinfo(&xfrm6_policy_afinfo);
+}
+
+void __init xfrm6_init(void)
+{
+ xfrm6_policy_init();
+ xfrm6_state_init();
+ xfrm6_input_init();
+}
+
+void __exit xfrm6_fini(void)
+{
+ //xfrm6_input_fini();
+ xfrm6_policy_fini();
+ xfrm6_state_fini();
+}
--- /dev/null
+/*
+ * xfrm6_state.c: based on xfrm4_state.c
+ *
+ * Authors:
+ * Mitsuru KANDA @USAGI
+ * Kazunori MIYAZAWA @USAGI
+ * Kunihiro Ishiguro
+ * IPv6 support
+ * YOSHIFUJI Hideaki @USAGI
+ * Split up af-specific portion
+ *
+ */
+
+#include <net/xfrm.h>
+#include <linux/pfkeyv2.h>
+#include <linux/ipsec.h>
+#include <net/ipv6.h>
+
+extern struct xfrm_state_afinfo xfrm6_state_afinfo;
+
+static void
+__xfrm6_init_tempsel(struct xfrm_state *x, struct flowi *fl,
+ struct xfrm_tmpl *tmpl,
+ xfrm_address_t *daddr, xfrm_address_t *saddr)
+{
+ /* Initialize temporary selector matching only
+ * to current session. */
+ memcpy(&x->sel.daddr, fl->fl6_dst, sizeof(struct in6_addr));
+ memcpy(&x->sel.saddr, fl->fl6_src, sizeof(struct in6_addr));
+ x->sel.dport = fl->uli_u.ports.dport;
+ x->sel.dport_mask = ~0;
+ x->sel.sport = fl->uli_u.ports.sport;
+ x->sel.sport_mask = ~0;
+ x->sel.prefixlen_d = 128;
+ x->sel.prefixlen_s = 128;
+ x->sel.proto = fl->proto;
+ x->sel.ifindex = fl->oif;
+ x->id = tmpl->id;
+ if (ipv6_addr_any((struct in6_addr*)&x->id.daddr))
+ memcpy(&x->id.daddr, daddr, sizeof(x->sel.daddr));
+ memcpy(&x->props.saddr, &tmpl->saddr, sizeof(x->props.saddr));
+ if (ipv6_addr_any((struct in6_addr*)&x->props.saddr))
+ memcpy(&x->props.saddr, saddr, sizeof(x->props.saddr));
+ x->props.mode = tmpl->mode;
+ x->props.reqid = tmpl->reqid;
+ x->props.family = AF_INET6;
+}
+
+static struct xfrm_state *
+__xfrm6_state_lookup(xfrm_address_t *daddr, u32 spi, u8 proto)
+{
+ unsigned h = __xfrm6_spi_hash(daddr, spi, proto);
+ struct xfrm_state *x;
+
+ list_for_each_entry(x, xfrm6_state_afinfo.state_byspi+h, byspi) {
+ if (x->props.family == AF_INET6 &&
+ spi == x->id.spi &&
+ !ipv6_addr_cmp((struct in6_addr *)daddr, (struct in6_addr *)x->id.daddr.a6) &&
+ proto == x->id.proto) {
+ atomic_inc(&x->refcnt);
+ return x;
+ }
+ }
+ return NULL;
+}
+
+static struct xfrm_state *
+__xfrm6_find_acq(u8 mode, u16 reqid, u8 proto,
+ xfrm_address_t *daddr, xfrm_address_t *saddr,
+ int create)
+{
+ struct xfrm_state *x, *x0;
+ unsigned h = __xfrm6_dst_hash(daddr);
+
+ x0 = NULL;
+
+ list_for_each_entry(x, xfrm6_state_afinfo.state_bydst+h, bydst) {
+ if (x->props.family == AF_INET6 &&
+ !ipv6_addr_cmp((struct in6_addr *)daddr, (struct in6_addr *)x->id.daddr.a6) &&
+ mode == x->props.mode &&
+ proto == x->id.proto &&
+ !ipv6_addr_cmp((struct in6_addr *)saddr, (struct in6_addr *)x->props.saddr.a6) &&
+ reqid == x->props.reqid &&
+ x->km.state == XFRM_STATE_ACQ) {
+ if (!x0)
+ x0 = x;
+ if (x->id.spi)
+ continue;
+ x0 = x;
+ break;
+ }
+ }
+ if (x0) {
+ atomic_inc(&x0->refcnt);
+ } else if (create && (x0 = xfrm_state_alloc()) != NULL) {
+ memcpy(x0->sel.daddr.a6, daddr, sizeof(struct in6_addr));
+ memcpy(x0->sel.saddr.a6, saddr, sizeof(struct in6_addr));
+ x0->sel.prefixlen_d = 128;
+ x0->sel.prefixlen_s = 128;
+ memcpy(x0->props.saddr.a6, saddr, sizeof(struct in6_addr));
+ x0->km.state = XFRM_STATE_ACQ;
+ memcpy(x0->id.daddr.a6, daddr, sizeof(struct in6_addr));
+ x0->id.proto = proto;
+ x0->props.family = AF_INET6;
+ x0->props.mode = mode;
+ x0->props.reqid = reqid;
+ x0->lft.hard_add_expires_seconds = XFRM_ACQ_EXPIRES;
+ atomic_inc(&x0->refcnt);
+ mod_timer(&x0->timer, jiffies + XFRM_ACQ_EXPIRES*HZ);
+ atomic_inc(&x0->refcnt);
+ list_add_tail(&x0->bydst, xfrm6_state_afinfo.state_bydst+h);
+ wake_up(&km_waitq);
+ }
+ return x0;
+}
+
+static struct xfrm_state_afinfo xfrm6_state_afinfo = {
+ .family = AF_INET6,
+ .lock = RW_LOCK_UNLOCKED,
+ .init_tempsel = __xfrm6_init_tempsel,
+ .state_lookup = __xfrm6_state_lookup,
+ .find_acq = __xfrm6_find_acq,
+};
+
+void __init xfrm6_state_init(void)
+{
+ xfrm_state_register_afinfo(&xfrm6_state_afinfo);
+}
+
+void __exit xfrm6_state_fini(void)
+{
+ xfrm_state_unregister_afinfo(&xfrm6_state_afinfo);
+}
+
{
switch (((struct sockaddr*)(addr + 1))->sa_family) {
case AF_INET:
- xaddr->xfrm4_addr =
+ xaddr->a4 =
((struct sockaddr_in *)(addr + 1))->sin_addr.s_addr;
- if (addr->sadb_address_prefixlen)
- xaddr->xfrm4_mask = htonl(~0 << (32 - addr->sadb_address_prefixlen));
return AF_INET;
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
case AF_INET6:
static struct xfrm_state *pfkey_xfrm_state_lookup(struct sadb_msg *hdr, void **ext_hdrs)
{
- struct xfrm_state *x;
struct sadb_sa *sa;
struct sadb_address *addr;
uint16_t proto;
+ unsigned short family;
+ xfrm_address_t *xaddr;
sa = (struct sadb_sa *) ext_hdrs[SADB_EXT_SA-1];
if (sa == NULL)
if (addr == NULL)
return NULL;
- switch (((struct sockaddr *)(addr + 1))->sa_family) {
+ family = ((struct sockaddr *)(addr + 1))->sa_family;
+ switch (family) {
case AF_INET:
- x = xfrm4_state_lookup(((struct sockaddr_in *)(addr + 1))->sin_addr.s_addr,
- sa->sadb_sa_spi, proto);
+ xaddr = (xfrm_address_t *)&((struct sockaddr_in *)(addr + 1))->sin_addr;
break;
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
case AF_INET6:
- x = xfrm6_state_lookup(&((struct sockaddr_in6 *)(addr + 1))->sin6_addr,
- sa->sadb_sa_spi, proto);
+ xaddr = (xfrm_address_t *)&((struct sockaddr_in6 *)(addr + 1))->sin6_addr;
break;
#endif
default:
- x = NULL;
- break;
+ xaddr = NULL;
}
- return x;
+ if (!xaddr)
+ return NULL;
+
+ return xfrm_state_lookup(xaddr, sa->sadb_sa_spi, proto, family);
}
#define PFKEY_ALIGN8(a) (1 + (((a) - 1) | (8 - 1)))
/* identity & sensitivity */
if ((x->props.family == AF_INET &&
- x->sel.saddr.xfrm4_addr != x->props.saddr.xfrm4_addr)
+ x->sel.saddr.a4 != x->props.saddr.a4)
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
|| (x->props.family == AF_INET6 &&
memcmp (x->sel.saddr.a6, x->props.saddr.a6, sizeof (struct in6_addr)))
sin = (struct sockaddr_in *) (addr + 1);
sin->sin_family = AF_INET;
- sin->sin_addr.s_addr = x->props.saddr.xfrm4_addr;
+ sin->sin_addr.s_addr = x->props.saddr.a4;
sin->sin_port = 0;
memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
}
if (x->props.family == AF_INET) {
sin = (struct sockaddr_in *) (addr + 1);
sin->sin_family = AF_INET;
- sin->sin_addr.s_addr = x->id.daddr.xfrm4_addr;
+ sin->sin_addr.s_addr = x->id.daddr.a4;
sin->sin_port = 0;
memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
- if (x->sel.saddr.xfrm4_addr != x->props.saddr.xfrm4_addr) {
+ if (x->sel.saddr.a4 != x->props.saddr.a4) {
addr = (struct sadb_address*) skb_put(skb,
sizeof(struct sadb_address)+sockaddr_size);
addr->sadb_address_len =
sin = (struct sockaddr_in *) (addr + 1);
sin->sin_family = AF_INET;
- sin->sin_addr.s_addr = x->sel.saddr.xfrm4_addr;
+ sin->sin_addr.s_addr = x->sel.saddr.a4;
sin->sin_port = x->sel.sport;
memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
}
x->sel.prefixlen_s = addr->sadb_address_prefixlen;
}
- switch (x->props.family) {
- case AF_INET:
- x->type = xfrm_get_type(proto);
- break;
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
- case AF_INET6:
- x->type = xfrm6_get_type(proto);
- break;
-#endif
- default:
- x->type = NULL;
- break;
- }
-
+ x->type = xfrm_get_type(proto, x->props.family);
if (x->type == NULL)
goto out;
if (x->type->init_state(x, NULL))
struct sadb_x_sa2 *sa2;
struct sadb_address *saddr, *daddr;
struct sadb_msg *out_hdr;
- struct xfrm_state *x;
+ struct xfrm_state *x = NULL;
u8 mode;
u16 reqid;
u8 proto;
+ unsigned short family;
+ xfrm_address_t *xsaddr = NULL, *xdaddr = NULL;
if (!present_and_same_family(ext_hdrs[SADB_EXT_ADDRESS_SRC-1],
ext_hdrs[SADB_EXT_ADDRESS_DST-1]))
saddr = ext_hdrs[SADB_EXT_ADDRESS_SRC-1];
daddr = ext_hdrs[SADB_EXT_ADDRESS_DST-1];
- switch (((struct sockaddr *)(saddr + 1))->sa_family) {
+ family = ((struct sockaddr *)(saddr + 1))->sa_family;
+ switch (family) {
case AF_INET:
- x = xfrm_find_acq(mode, reqid, proto,
- ((struct sockaddr_in *)(daddr + 1))->sin_addr.s_addr,
- ((struct sockaddr_in *)(saddr + 1))->sin_addr.s_addr, 1);
+ xdaddr = (xfrm_address_t *)&((struct sockaddr_in *)(daddr + 1))->sin_addr.s_addr;
+ xsaddr = (xfrm_address_t *)&((struct sockaddr_in *)(saddr + 1))->sin_addr.s_addr;
break;
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
case AF_INET6:
- x = xfrm6_find_acq(mode, reqid, proto,
- &((struct sockaddr_in6 *)(daddr + 1))->sin6_addr,
- &((struct sockaddr_in6 *)(saddr + 1))->sin6_addr, 1);
+ xdaddr = (xfrm_address_t *)&((struct sockaddr_in6 *)(daddr + 1))->sin6_addr;
+ xsaddr = (xfrm_address_t *)&((struct sockaddr_in6 *)(saddr + 1))->sin6_addr;
break;
#endif
- default:
- x = NULL;
- break;
}
+ if (xdaddr)
+ x = xfrm_find_acq(mode, reqid, proto, xdaddr, xsaddr, 1, family);
if (x == NULL)
return -ENOENT;
/* XXX there is race condition */
x1 = pfkey_xfrm_state_lookup(hdr, ext_hdrs);
if (!x1) {
- switch (x->props.family) {
- case AF_INET:
- x1 = xfrm_find_acq(x->props.mode, x->props.reqid, x->id.proto,
- x->id.daddr.xfrm4_addr,
- x->props.saddr.xfrm4_addr, 0);
- break;
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
- case AF_INET6:
- x1 = xfrm6_find_acq(x->props.mode, x->props.reqid, x->id.proto,
- (struct in6_addr*)x->id.daddr.a6,
- (struct in6_addr*)x->props.saddr.a6, 0);
- break;
-#endif
- default:
- x1 = NULL;
- break;
- }
+ x1 = xfrm_find_acq(x->props.mode, x->props.reqid, x->id.proto,
+ &x->id.daddr,
+ &x->props.saddr, 0, x->props.family);
if (x1 && x1->id.spi != x->id.spi && x1->id.spi) {
xfrm_state_put(x1);
x1 = NULL;
sin = (void*)(rq+1);
if (sin->sin_family != AF_INET)
return -EINVAL;
- t->saddr.xfrm4_addr = sin->sin_addr.s_addr;
+ t->saddr.a4 = sin->sin_addr.s_addr;
sin++;
if (sin->sin_family != AF_INET)
return -EINVAL;
- t->id.daddr.xfrm4_addr = sin->sin_addr.s_addr;
+ t->id.daddr.a4 = sin->sin_addr.s_addr;
break;
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
case AF_INET6:
if (xp->family == AF_INET) {
sin = (struct sockaddr_in *) (addr + 1);
sin->sin_family = AF_INET;
- sin->sin_addr.s_addr = xp->selector.saddr.xfrm4_addr;
+ sin->sin_addr.s_addr = xp->selector.saddr.a4;
sin->sin_port = xp->selector.sport;
memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
}
if (xp->family == AF_INET) {
sin = (struct sockaddr_in *) (addr + 1);
sin->sin_family = AF_INET;
- sin->sin_addr.s_addr = xp->selector.daddr.xfrm4_addr;
+ sin->sin_addr.s_addr = xp->selector.daddr.a4;
sin->sin_port = xp->selector.dport;
memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
}
case AF_INET:
sin = (void*)(rq+1);
sin->sin_family = AF_INET;
- sin->sin_addr.s_addr = t->saddr.xfrm4_addr;
+ sin->sin_addr.s_addr = t->saddr.a4;
sin->sin_port = 0;
memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
sin++;
sin->sin_family = AF_INET;
- sin->sin_addr.s_addr = t->id.daddr.xfrm4_addr;
+ sin->sin_addr.s_addr = t->id.daddr.a4;
sin->sin_port = 0;
memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
break;
sin = (struct sockaddr_in *) (addr + 1);
sin->sin_family = AF_INET;
- sin->sin_addr.s_addr = x->props.saddr.xfrm4_addr;
+ sin->sin_addr.s_addr = x->props.saddr.a4;
sin->sin_port = 0;
memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
}
sin = (struct sockaddr_in *) (addr + 1);
sin->sin_family = AF_INET;
- sin->sin_addr.s_addr = x->id.daddr.xfrm4_addr;
+ sin->sin_addr.s_addr = x->id.daddr.a4;
sin->sin_port = 0;
memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
}
EXPORT_SYMBOL(__xfrm_route_forward);
EXPORT_SYMBOL(xfrm_state_alloc);
EXPORT_SYMBOL(__xfrm_state_destroy);
-EXPORT_SYMBOL(xfrm4_state_find);
+EXPORT_SYMBOL(xfrm_state_find);
EXPORT_SYMBOL(xfrm_state_insert);
EXPORT_SYMBOL(xfrm_state_check_expire);
EXPORT_SYMBOL(xfrm_state_check_space);
-EXPORT_SYMBOL(xfrm4_state_lookup);
+EXPORT_SYMBOL(xfrm_state_lookup);
+EXPORT_SYMBOL(xfrm_state_register_afinfo);
+EXPORT_SYMBOL(xfrm_state_unregister_afinfo);
+EXPORT_SYMBOL(xfrm_state_get_afinfo);
+EXPORT_SYMBOL(xfrm_state_put_afinfo);
EXPORT_SYMBOL(xfrm_replay_check);
EXPORT_SYMBOL(xfrm_replay_advance);
EXPORT_SYMBOL(xfrm_check_selectors);
+EXPORT_SYMBOL(__secpath_destroy);
+EXPORT_SYMBOL(xfrm_parse_spi);
EXPORT_SYMBOL(xfrm4_rcv);
EXPORT_SYMBOL(xfrm_register_type);
EXPORT_SYMBOL(xfrm_unregister_type);
EXPORT_SYMBOL(xfrm_policy_flush);
EXPORT_SYMBOL(xfrm_policy_byid);
EXPORT_SYMBOL(xfrm_policy_list);
-EXPORT_SYMBOL(xfrm_dst_lookup_register);
-EXPORT_SYMBOL(xfrm_dst_lookup_unregister);
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-EXPORT_SYMBOL(xfrm6_state_find);
-EXPORT_SYMBOL(xfrm6_rcv);
-EXPORT_SYMBOL(xfrm6_state_lookup);
-EXPORT_SYMBOL(xfrm6_find_acq);
-EXPORT_SYMBOL(xfrm6_register_type);
-EXPORT_SYMBOL(xfrm6_unregister_type);
-EXPORT_SYMBOL(xfrm6_get_type);
-EXPORT_SYMBOL(xfrm6_clear_mutable_options);
-#endif
+EXPORT_SYMBOL(xfrm_dst_lookup);
+EXPORT_SYMBOL(xfrm_policy_register_afinfo);
+EXPORT_SYMBOL(xfrm_policy_unregister_afinfo);
+EXPORT_SYMBOL(xfrm_policy_get_afinfo);
+EXPORT_SYMBOL(xfrm_policy_put_afinfo);
EXPORT_SYMBOL_GPL(xfrm_probe_algs);
EXPORT_SYMBOL_GPL(xfrm_count_auth_supported);
struct sctphdr *sh;
union sctp_addr src;
union sctp_addr dest;
+ int family;
struct sctp_af *af;
int ret = 0;
skb_pull(skb, sizeof(struct sctphdr));
- af = sctp_get_af_specific(ipver2af(skb->nh.iph->version));
+ family = ipver2af(skb->nh.iph->version);
+ af = sctp_get_af_specific(family);
if (unlikely(!af))
goto bad_packet;
rcvr = asoc ? &asoc->base : &ep->base;
sk = rcvr->sk;
- if (!xfrm_policy_check(sk, XFRM_POLICY_IN, skb))
+ if (!xfrm_policy_check(sk, XFRM_POLICY_IN, skb, family))
goto discard_release;
ret = sk_filter(sk, skb, 1);