Commit 61f1dcf7 authored by Hideaki Yoshifuji's avatar Hideaki Yoshifuji Committed by David S. Miller

[IPSEC]: Split up XFRM Subsystem.

parent bfe169a1
......@@ -12,12 +12,7 @@
*/
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;
......
......@@ -13,6 +13,7 @@
#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)
......@@ -145,6 +146,51 @@ enum {
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
{
......@@ -160,9 +206,14 @@ 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
......@@ -233,6 +284,47 @@ extern int xfrm_register_km(struct xfrm_mgr *km);
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)
......@@ -249,6 +341,68 @@ static inline void xfrm_pol_put(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)
......@@ -262,15 +416,65 @@ static inline void xfrm_state_hold(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.
......@@ -296,6 +500,7 @@ struct xfrm_dst
struct sec_path
{
kmem_cache_t *pool;
atomic_t refcnt;
int len;
struct xfrm_state *xvec[XFRM_MAX_DEPTH];
......@@ -317,42 +522,73 @@ secpath_put(struct sec_path *sp)
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);
......@@ -378,6 +614,66 @@ static inline void xfrm_sk_free_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
*/
......@@ -406,20 +702,27 @@ struct xfrm_algo_desc {
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);
......@@ -431,6 +734,9 @@ extern int xfrm6_rcv(struct sk_buff *skb);
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);
......@@ -439,20 +745,25 @@ struct xfrm_policy *xfrm_policy_delete(int dir, struct xfrm_selector *sel);
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);
......@@ -466,52 +777,7 @@ extern struct xfrm_algo_desc *xfrm_aalg_get_byname(char *name);
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 */
......@@ -22,4 +22,4 @@ obj-$(CONFIG_IP_PNP) += ipconfig.o
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
......@@ -92,8 +92,8 @@ static int ah_output(struct sk_buff *skb)
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 {
......@@ -232,7 +232,7 @@ void ah4_err(struct sk_buff *skb, u32 info)
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",
......@@ -338,13 +338,13 @@ static struct inet_protocol ah4_protocol = {
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;
......@@ -354,7 +354,7 @@ static void __exit ah4_fini(void)
{
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");
}
......
......@@ -91,8 +91,8 @@ int esp_output(struct sk_buff *skb)
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);
......@@ -276,7 +276,7 @@ void esp4_err(struct sk_buff *skb, u32 info)
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",
......@@ -405,13 +405,13 @@ static struct inet_protocol esp4_protocol = {
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;
......@@ -421,7 +421,7 @@ static void __exit esp4_fini(void)
{
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");
}
......
......@@ -60,7 +60,7 @@ int ip_forward(struct sk_buff *skb)
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))
......@@ -82,7 +82,7 @@ int ip_forward(struct sk_buff *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;
......
......@@ -236,7 +236,7 @@ static inline int ip_local_deliver_finish(struct sk_buff *skb)
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;
}
......@@ -248,7 +248,7 @@ static inline int ip_local_deliver_finish(struct sk_buff *skb)
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);
......
......@@ -251,7 +251,7 @@ static int raw_rcv_skb(struct sock * sk, struct sk_buff * skb)
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;
}
......
......@@ -2600,13 +2600,6 @@ static int ip_rt_acct_read(char *buffer, char **start, off_t offset,
#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;
......@@ -2688,7 +2681,6 @@ int __init ip_rt_init(void)
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;
......@@ -2698,6 +2690,7 @@ int __init ip_rt_init(void)
#endif
#endif
xfrm_init();
xfrm4_init();
out:
return rc;
out_enomem:
......
......@@ -1798,7 +1798,7 @@ int tcp_v4_rcv(struct sk_buff *skb)
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))
......@@ -1820,7 +1820,7 @@ int tcp_v4_rcv(struct sk_buff *skb)
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)) {
......@@ -1840,7 +1840,7 @@ int tcp_v4_rcv(struct sk_buff *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)) {
......
......@@ -946,7 +946,7 @@ static int udp_queue_rcv_skb(struct sock * sk, struct sk_buff *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;
}
......@@ -1077,7 +1077,7 @@ int udp_rcv(struct sk_buff *skb)
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 */
......
/*
* 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");
}
/*
* 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();
}
/*
* 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;
......@@ -53,402 +50,3 @@ static int xfrm_parse_spi(struct sk_buff *skb, u32 *spi, u32 *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);
......@@ -19,12 +23,10 @@ static rwlock_t xfrm_policy_lock = RW_LOCK_UNLOCKED;
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.
......@@ -49,40 +51,8 @@ static kmem_cache_t *flow_cachep;
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;
......@@ -92,11 +62,11 @@ static void flow_cache_shrink(int cpu)
{
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;
......@@ -118,23 +88,12 @@ struct xfrm_policy *flow_lookup(int dir, struct flowi *fl,
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) {
......@@ -172,8 +131,8 @@ struct xfrm_policy *flow_lookup(int dir, struct flowi *fl,
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();
......@@ -193,7 +152,7 @@ void __init flow_cache_init(void)
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 */;
......@@ -205,130 +164,82 @@ void __init flow_cache_init(void)
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);
......@@ -608,18 +519,7 @@ struct xfrm_policy *xfrm_policy_lookup(int dir, struct flowi *fl,
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;
......@@ -637,18 +537,7 @@ struct xfrm_policy *xfrm_sk_policy_lookup(struct sock *sk, int dir, struct flowi
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
......@@ -750,72 +639,27 @@ void __xfrm_sk_free_policy(struct xfrm_policy *pol, int dir)
/* 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;
......@@ -839,241 +683,40 @@ xfrm6_tmpl_resolve(struct xfrm_policy *policy, struct flowi *fl,
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.
*
......@@ -1141,46 +784,17 @@ int xfrm_lookup(struct dst_entry **dst_p, struct flowi *fl,
* 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) {
......@@ -1191,18 +805,7 @@ int xfrm_lookup(struct dst_entry **dst_p, struct flowi *fl,
__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);
......@@ -1225,19 +828,8 @@ int xfrm_lookup(struct dst_entry **dst_p, struct flowi *fl,
}
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++)
......@@ -1282,137 +874,39 @@ int xfrm_lookup(struct dst_entry **dst_p, struct flowi *fl,
*/
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)
......@@ -1420,38 +914,15 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
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;
}
}
......@@ -1483,20 +954,7 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
* 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;
}
......@@ -1514,18 +972,8 @@ int __xfrm_route_forward(struct sk_buff *skb, unsigned short family)
{
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;
}
......@@ -1603,20 +1051,6 @@ static void __xfrm_garbage_collect(void)
}
}
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 {
......@@ -1660,29 +1094,6 @@ int xfrm_flush_bundles(struct xfrm_state *x)
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... :-)
......@@ -1723,52 +1134,99 @@ static int xfrm_get_mss(struct dst_entry *dst, u32 mtu)
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:
......@@ -20,8 +24,6 @@
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.
*
......@@ -33,7 +35,8 @@ static struct list_head xfrm_state_byspi[XFRM_DST_HSIZE];
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);
......@@ -214,138 +217,37 @@ void xfrm_state_flush(u8 proto)
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:
......@@ -362,7 +264,7 @@ xfrm6_state_find(struct in6_addr *daddr, struct in6_addr *saddr, struct flowi *f
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 ||
......@@ -373,7 +275,7 @@ xfrm6_state_find(struct in6_addr *daddr, struct in6_addr *saddr, struct flowi *f
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;
}
}
......@@ -384,45 +286,29 @@ xfrm6_state_find(struct in6_addr *daddr, struct in6_addr *saddr, struct flowi *f
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);
......@@ -436,36 +322,17 @@ xfrm6_state_find(struct in6_addr *daddr, struct in6_addr *saddr, struct flowi *f
(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);
......@@ -487,7 +354,7 @@ int xfrm_state_check_expire(struct xfrm_state *x)
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;
}
......@@ -512,158 +379,37 @@ int xfrm_state_check_space(struct xfrm_state *x, struct sk_buff *skb)
}
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 */
......@@ -697,18 +443,7 @@ xfrm_alloc_spi(struct xfrm_state *x, u32 minspi, u32 maxspi)
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;
......@@ -720,18 +455,7 @@ xfrm_alloc_spi(struct xfrm_state *x, u32 minspi, u32 maxspi)
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);
......@@ -740,19 +464,7 @@ xfrm_alloc_spi(struct xfrm_state *x, u32 minspi, u32 maxspi)
}
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);
......@@ -845,18 +557,7 @@ int xfrm_check_selectors(struct xfrm_state **x, int n, struct flowi *fl)
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;
}
......@@ -958,6 +659,66 @@ int xfrm_unregister_km(struct xfrm_mgr *km)
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;
......
......@@ -2,11 +2,11 @@
*
* 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
*
*/
......@@ -24,9 +24,6 @@
#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>
......@@ -191,19 +188,7 @@ static struct xfrm_state *xfrm_state_construct(struct xfrm_usersa_info *p,
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;
......@@ -238,21 +223,7 @@ static int xfrm_add_sa(struct sk_buff *skb, struct nlmsghdr *nlh, void **xfrma)
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);
......@@ -269,19 +240,7 @@ static int xfrm_del_sa(struct sk_buff *skb, struct nlmsghdr *nlh, void **xfrma)
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;
......@@ -399,19 +358,7 @@ static int xfrm_get_sa(struct sk_buff *skb, struct nlmsghdr *nlh, void **xfrma)
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;
......@@ -462,23 +409,10 @@ static int xfrm_alloc_userspi(struct sk_buff *skb, struct nlmsghdr *nlh, void **
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;
......
......@@ -8,7 +8,8 @@ ipv6-objs := af_inet6.o anycast.o ip6_output.o ip6_input.o addrconf.o sit.o \
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
......
......@@ -228,7 +228,7 @@ void ah6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
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;
......@@ -336,14 +336,14 @@ int __init ah6_init(void)
{
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;
}
......@@ -355,7 +355,7 @@ static void __exit ah6_fini(void)
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");
}
......
......@@ -377,7 +377,7 @@ void esp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
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/"
......@@ -504,13 +504,13 @@ static struct inet6_protocol esp6_protocol = {
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;
}
......@@ -521,7 +521,7 @@ static void __exit esp6_fini(void)
{
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");
}
......
......@@ -5,6 +5,7 @@
#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);
......@@ -31,3 +32,5 @@ EXPORT_SYMBOL(ipv6_get_saddr);
EXPORT_SYMBOL(ipv6_chk_addr);
EXPORT_SYMBOL(in6addr_any);
EXPORT_SYMBOL(in6addr_loopback);
EXPORT_SYMBOL(xfrm6_rcv);
EXPORT_SYMBOL(xfrm6_clear_mutable_options);
......@@ -1867,15 +1867,6 @@ ctl_table ipv6_route_table[] = {
#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",
......@@ -1883,11 +1874,11 @@ void __init ip6_route_init(void)
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
......@@ -1897,7 +1888,7 @@ void ip6_route_cleanup(void)
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();
}
......
/*
* 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");
}
/*
* 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();
}
/*
* 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);
}
......@@ -510,10 +510,8 @@ static int pfkey_sadb_addr2xfrm_addr(struct sadb_address *addr,
{
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:
......@@ -530,10 +528,11 @@ static int pfkey_sadb_addr2xfrm_addr(struct sadb_address *addr,
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)
......@@ -548,23 +547,24 @@ static struct xfrm_state *pfkey_xfrm_state_lookup(struct sadb_msg *hdr, void **
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)))
......@@ -619,7 +619,7 @@ static struct sk_buff * pfkey_xfrm_state2msg(struct xfrm_state *x, int add_keys,
/* 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)))
......@@ -731,7 +731,7 @@ static struct sk_buff * pfkey_xfrm_state2msg(struct xfrm_state *x, int add_keys,
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));
}
......@@ -764,11 +764,11 @@ static struct sk_buff * pfkey_xfrm_state2msg(struct xfrm_state *x, int add_keys,
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 =
......@@ -782,7 +782,7 @@ static struct sk_buff * pfkey_xfrm_state2msg(struct xfrm_state *x, int add_keys,
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));
}
......@@ -1017,20 +1017,7 @@ static struct xfrm_state * pfkey_msg2xfrm_state(struct sadb_msg *hdr,
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))
......@@ -1061,10 +1048,12 @@ static int pfkey_getspi(struct sock *sk, struct sk_buff *skb, struct sadb_msg *h
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]))
......@@ -1085,23 +1074,21 @@ static int pfkey_getspi(struct sock *sk, struct sk_buff *skb, struct sadb_msg *h
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;
......@@ -1188,23 +1175,9 @@ static int pfkey_add(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hdr,
/* 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;
......@@ -1543,11 +1516,11 @@ parse_ipsecrequest(struct xfrm_policy *xp, struct sadb_x_ipsecrequest *rq)
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:
......@@ -1654,7 +1627,7 @@ static void pfkey_xfrm_policy2msg(struct sk_buff *skb, struct xfrm_policy *xp, i
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));
}
......@@ -1685,7 +1658,7 @@ static void pfkey_xfrm_policy2msg(struct sk_buff *skb, struct xfrm_policy *xp, i
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));
}
......@@ -1773,12 +1746,12 @@ static void pfkey_xfrm_policy2msg(struct sk_buff *skb, struct xfrm_policy *xp, i
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;
......@@ -2362,7 +2335,7 @@ static int pfkey_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *t, struct
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));
}
......@@ -2396,7 +2369,7 @@ static int pfkey_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *t, struct
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));
}
......
......@@ -303,14 +303,20 @@ EXPORT_SYMBOL(__xfrm_policy_check);
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);
......@@ -331,18 +337,11 @@ EXPORT_SYMBOL(xfrm_policy_walk);
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);
......
......@@ -110,6 +110,7 @@ int sctp_rcv(struct sk_buff *skb)
struct sctphdr *sh;
union sctp_addr src;
union sctp_addr dest;
int family;
struct sctp_af *af;
int ret = 0;
......@@ -129,7 +130,8 @@ int sctp_rcv(struct sk_buff *skb)
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;
......@@ -173,7 +175,7 @@ int sctp_rcv(struct sk_buff *skb)
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);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment