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 @@ ...@@ -12,12 +12,7 @@
*/ */
typedef union typedef union
{ {
struct { __u32 a4;
__u32 addr;
__u32 mask; /* Use unused bits to cache mask. */
} a4;
#define xfrm4_addr a4.addr
#define xfrm4_mask a4.mask
__u32 a6[4]; __u32 a6[4];
} xfrm_address_t; } xfrm_address_t;
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include <net/sock.h> #include <net/sock.h>
#include <net/dst.h> #include <net/dst.h>
#include <net/route.h> #include <net/route.h>
#include <net/ipv6.h>
#include <net/ip6_fib.h> #include <net/ip6_fib.h>
#define XFRM_ALIGN8(len) (((len) + 7) & ~7) #define XFRM_ALIGN8(len) (((len) + 7) & ~7)
...@@ -145,6 +146,51 @@ enum { ...@@ -145,6 +146,51 @@ enum {
XFRM_STATE_DEAD 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 struct xfrm_type
{ {
...@@ -160,9 +206,14 @@ struct xfrm_type ...@@ -160,9 +206,14 @@ struct xfrm_type
u32 (*get_max_size)(struct xfrm_state *, int size); u32 (*get_max_size)(struct xfrm_state *, int size);
}; };
extern int xfrm_register_type(struct xfrm_type *type); struct xfrm_type_map {
extern int xfrm_unregister_type(struct xfrm_type *type); rwlock_t lock;
extern struct xfrm_type *xfrm_get_type(u8 proto); 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); extern void xfrm_put_type(struct xfrm_type *type);
struct xfrm_tmpl struct xfrm_tmpl
...@@ -233,6 +284,47 @@ extern int xfrm_register_km(struct xfrm_mgr *km); ...@@ -233,6 +284,47 @@ extern int xfrm_register_km(struct xfrm_mgr *km);
extern int xfrm_unregister_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]; extern struct xfrm_policy *xfrm_policy_list[XFRM_POLICY_MAX*2];
static inline void xfrm_pol_hold(struct xfrm_policy *policy) static inline void xfrm_pol_hold(struct xfrm_policy *policy)
...@@ -249,6 +341,68 @@ static inline void xfrm_pol_put(struct xfrm_policy *policy) ...@@ -249,6 +341,68 @@ static inline void xfrm_pol_put(struct xfrm_policy *policy)
__xfrm_policy_destroy(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 *); extern void __xfrm_state_destroy(struct xfrm_state *);
static inline void xfrm_state_put(struct xfrm_state *x) static inline void xfrm_state_put(struct xfrm_state *x)
...@@ -262,15 +416,65 @@ static inline void xfrm_state_hold(struct xfrm_state *x) ...@@ -262,15 +416,65 @@ static inline void xfrm_state_hold(struct xfrm_state *x)
atomic_inc(&x->refcnt); 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 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.dport^sel->dport)&sel->dport_mask) &&
!((fl->uli_u.ports.sport^sel->sport)&sel->sport_mask) && !((fl->uli_u.ports.sport^sel->sport)&sel->sport_mask) &&
(fl->proto == sel->proto || !sel->proto) && (fl->proto == sel->proto || !sel->proto) &&
(fl->oif == sel->ifindex || !sel->ifindex) && (fl->oif == sel->ifindex || !sel->ifindex);
!((fl->fl4_src^sel->saddr.xfrm4_addr)&sel->saddr.xfrm4_mask); }
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. /* A struct encoding bundle of transformations to apply to some set of flow.
...@@ -296,6 +500,7 @@ struct xfrm_dst ...@@ -296,6 +500,7 @@ struct xfrm_dst
struct sec_path struct sec_path
{ {
kmem_cache_t *pool;
atomic_t refcnt; atomic_t refcnt;
int len; int len;
struct xfrm_state *xvec[XFRM_MAX_DEPTH]; struct xfrm_state *xvec[XFRM_MAX_DEPTH];
...@@ -317,42 +522,73 @@ secpath_put(struct sec_path *sp) ...@@ -317,42 +522,73 @@ secpath_put(struct sec_path *sp)
if (sp && atomic_dec_and_test(&sp->refcnt)) if (sp && atomic_dec_and_test(&sp->refcnt))
__secpath_destroy(sp); __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); 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]) 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] || return !xfrm_policy_list[dir] ||
(skb->dst->flags & DST_NOPOLICY) || (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) 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_check(sk, dir, skb, AF_INET6);
return !xfrm_policy_list[dir] ||
(skb->dst->flags & DST_NOPOLICY) ||
__xfrm_policy_check(sk, dir, skb, AF_INET6);
} }
extern int __xfrm_route_forward(struct sk_buff *skb, unsigned short family); 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] || return !xfrm_policy_list[XFRM_POLICY_OUT] ||
(skb->dst->flags & DST_NOXFRM) || (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) static inline int xfrm6_route_forward(struct sk_buff *skb)
{ {
return !xfrm_policy_list[XFRM_POLICY_OUT] || return xfrm_route_forward(skb, AF_INET6);
(skb->dst->flags & DST_NOXFRM) ||
__xfrm_route_forward(skb, AF_INET6);
} }
extern int __xfrm_sk_clone_policy(struct sock *sk); extern int __xfrm_sk_clone_policy(struct sock *sk);
...@@ -378,6 +614,66 @@ static inline void xfrm_sk_free_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 * xfrm algorithm information
*/ */
...@@ -406,20 +702,27 @@ struct xfrm_algo_desc { ...@@ -406,20 +702,27 @@ struct xfrm_algo_desc {
struct sadb_alg 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_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 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 *xfrm_state_alloc(void);
extern struct xfrm_state *xfrm4_state_find(u32 daddr, u32 saddr, struct flowi *fl, struct xfrm_tmpl *tmpl, extern struct xfrm_state *xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
struct xfrm_policy *pol, int *err); struct flowi *fl, struct xfrm_tmpl *tmpl,
extern struct xfrm_state *xfrm6_state_find(struct in6_addr *daddr, struct in6_addr *saddr, struct xfrm_policy *pol, int *err,
struct flowi *fl, struct xfrm_tmpl *tmpl, unsigned short family);
struct xfrm_policy *pol, int *err);
extern int xfrm_state_check_expire(struct xfrm_state *x); extern int xfrm_state_check_expire(struct xfrm_state *x);
extern void xfrm_state_insert(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 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 *xfrm_state_lookup(xfrm_address_t *daddr, u32 spi, u8 proto, unsigned short family);
extern struct xfrm_state *xfrm6_state_lookup(struct in6_addr *daddr, u32 spi, u8 proto);
extern struct xfrm_state *xfrm_find_acq_byseq(u32 seq); extern struct xfrm_state *xfrm_find_acq_byseq(u32 seq);
extern void xfrm_state_delete(struct xfrm_state *x); extern void xfrm_state_delete(struct xfrm_state *x);
extern void xfrm_state_flush(u8 proto); extern void xfrm_state_flush(u8 proto);
...@@ -431,6 +734,9 @@ extern int xfrm6_rcv(struct sk_buff *skb); ...@@ -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 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); 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); struct xfrm_policy *xfrm_policy_alloc(int gfp);
extern int xfrm_policy_walk(int (*func)(struct xfrm_policy *, int, int, void*), void *); extern int xfrm_policy_walk(int (*func)(struct xfrm_policy *, int, int, void*), void *);
struct xfrm_policy *xfrm_policy_lookup(int dir, struct flowi *fl, unsigned short family); struct xfrm_policy *xfrm_policy_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); ...@@ -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); struct xfrm_policy *xfrm_policy_byid(int dir, u32 id, int delete);
void xfrm_policy_flush(void); void xfrm_policy_flush(void);
void xfrm_alloc_spi(struct xfrm_state *x, u32 minspi, u32 maxspi); 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 * xfrm_find_acq(u8 mode, u16 reqid, u8 proto,
struct xfrm_state * xfrm6_find_acq(u8 mode, u16 reqid, u8 proto, struct in6_addr *daddr, xfrm_address_t *daddr, xfrm_address_t *saddr,
struct in6_addr *saddr, int create); int create, unsigned short family);
extern void xfrm_policy_flush(void); extern void xfrm_policy_flush(void);
extern void xfrm_policy_kill(struct xfrm_policy *); extern void xfrm_policy_kill(struct xfrm_policy *);
extern int xfrm_sk_policy_insert(struct sock *sk, int dir, struct xfrm_policy *pol); 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 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_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 wait_queue_head_t km_waitq;
extern void km_warn_expired(struct xfrm_state *x); extern void km_warn_expired(struct xfrm_state *x);
extern void km_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 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 void xfrm_probe_algs(void);
extern int xfrm_count_auth_supported(void); extern int xfrm_count_auth_supported(void);
extern int xfrm_count_enc_supported(void); extern int xfrm_count_enc_supported(void);
...@@ -466,52 +777,7 @@ extern struct xfrm_algo_desc *xfrm_aalg_get_byname(char *name); ...@@ -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_ealg_get_byname(char *name);
extern struct xfrm_algo_desc *xfrm_calg_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; struct crypto_tfm;
typedef void (icv_update_fn_t)(struct crypto_tfm *, struct scatterlist *, unsigned int); 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 */ #endif /* _NET_XFRM_H */
...@@ -22,4 +22,4 @@ obj-$(CONFIG_IP_PNP) += ipconfig.o ...@@ -22,4 +22,4 @@ obj-$(CONFIG_IP_PNP) += ipconfig.o
obj-$(CONFIG_NETFILTER) += netfilter/ obj-$(CONFIG_NETFILTER) += netfilter/
obj-$(CONFIG_XFRM_USER) += xfrm_user.o 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) ...@@ -92,8 +92,8 @@ static int ah_output(struct sk_buff *skb)
top_iph->ttl = 0; top_iph->ttl = 0;
top_iph->protocol = IPPROTO_AH; top_iph->protocol = IPPROTO_AH;
top_iph->check = 0; top_iph->check = 0;
top_iph->saddr = x->props.saddr.xfrm4_addr; top_iph->saddr = x->props.saddr.a4;
top_iph->daddr = x->id.daddr.xfrm4_addr; top_iph->daddr = x->id.daddr.a4;
ah = (struct ip_auth_hdr*)(top_iph+1); ah = (struct ip_auth_hdr*)(top_iph+1);
ah->nexthdr = IPPROTO_IPIP; ah->nexthdr = IPPROTO_IPIP;
} else { } else {
...@@ -232,7 +232,7 @@ void ah4_err(struct sk_buff *skb, u32 info) ...@@ -232,7 +232,7 @@ void ah4_err(struct sk_buff *skb, u32 info)
skb->h.icmph->code != ICMP_FRAG_NEEDED) skb->h.icmph->code != ICMP_FRAG_NEEDED)
return; 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) if (!x)
return; return;
printk(KERN_DEBUG "pmtu discvovery on SA AH/%08x/%08x\n", printk(KERN_DEBUG "pmtu discvovery on SA AH/%08x/%08x\n",
...@@ -338,13 +338,13 @@ static struct inet_protocol ah4_protocol = { ...@@ -338,13 +338,13 @@ static struct inet_protocol ah4_protocol = {
static int __init ah4_init(void) static int __init ah4_init(void)
{ {
SET_MODULE_OWNER(&ah_type); 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"); printk(KERN_INFO "ip ah init: can't add xfrm type\n");
return -EAGAIN; return -EAGAIN;
} }
if (inet_add_protocol(&ah4_protocol, IPPROTO_AH) < 0) { if (inet_add_protocol(&ah4_protocol, IPPROTO_AH) < 0) {
printk(KERN_INFO "ip ah init: can't add protocol\n"); 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 -EAGAIN;
} }
return 0; return 0;
...@@ -354,7 +354,7 @@ static void __exit ah4_fini(void) ...@@ -354,7 +354,7 @@ static void __exit ah4_fini(void)
{ {
if (inet_del_protocol(&ah4_protocol, IPPROTO_AH) < 0) if (inet_del_protocol(&ah4_protocol, IPPROTO_AH) < 0)
printk(KERN_INFO "ip ah close: can't remove protocol\n"); 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"); printk(KERN_INFO "ip ah close: can't remove xfrm type\n");
} }
......
...@@ -91,8 +91,8 @@ int esp_output(struct sk_buff *skb) ...@@ -91,8 +91,8 @@ int esp_output(struct sk_buff *skb)
top_iph->ttl = iph->ttl; /* TTL disclosed */ top_iph->ttl = iph->ttl; /* TTL disclosed */
top_iph->protocol = IPPROTO_ESP; top_iph->protocol = IPPROTO_ESP;
top_iph->check = 0; top_iph->check = 0;
top_iph->saddr = x->props.saddr.xfrm4_addr; top_iph->saddr = x->props.saddr.a4;
top_iph->daddr = x->id.daddr.xfrm4_addr; top_iph->daddr = x->id.daddr.a4;
memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options)); memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options));
} else { } else {
esph = (struct ip_esp_hdr*)skb_push(skb, x->props.header_len); 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) ...@@ -276,7 +276,7 @@ void esp4_err(struct sk_buff *skb, u32 info)
skb->h.icmph->code != ICMP_FRAG_NEEDED) skb->h.icmph->code != ICMP_FRAG_NEEDED)
return; 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) if (!x)
return; return;
printk(KERN_DEBUG "pmtu discvovery on SA ESP/%08x/%08x\n", printk(KERN_DEBUG "pmtu discvovery on SA ESP/%08x/%08x\n",
...@@ -405,13 +405,13 @@ static struct inet_protocol esp4_protocol = { ...@@ -405,13 +405,13 @@ static struct inet_protocol esp4_protocol = {
int __init esp4_init(void) int __init esp4_init(void)
{ {
SET_MODULE_OWNER(&esp_type); 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"); printk(KERN_INFO "ip esp init: can't add xfrm type\n");
return -EAGAIN; return -EAGAIN;
} }
if (inet_add_protocol(&esp4_protocol, IPPROTO_ESP) < 0) { if (inet_add_protocol(&esp4_protocol, IPPROTO_ESP) < 0) {
printk(KERN_INFO "ip esp init: can't add protocol\n"); 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 -EAGAIN;
} }
return 0; return 0;
...@@ -421,7 +421,7 @@ static void __exit esp4_fini(void) ...@@ -421,7 +421,7 @@ static void __exit esp4_fini(void)
{ {
if (inet_del_protocol(&esp4_protocol, IPPROTO_ESP) < 0) if (inet_del_protocol(&esp4_protocol, IPPROTO_ESP) < 0)
printk(KERN_INFO "ip esp close: can't remove protocol\n"); 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"); printk(KERN_INFO "ip esp close: can't remove xfrm type\n");
} }
......
...@@ -60,7 +60,7 @@ int ip_forward(struct sk_buff *skb) ...@@ -60,7 +60,7 @@ int ip_forward(struct sk_buff *skb)
struct rtable *rt; /* Route we use */ struct rtable *rt; /* Route we use */
struct ip_options * opt = &(IPCB(skb)->opt); 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; goto drop;
if (IPCB(skb)->opt.router_alert && ip_call_ra_chain(skb)) if (IPCB(skb)->opt.router_alert && ip_call_ra_chain(skb))
...@@ -82,7 +82,7 @@ int ip_forward(struct sk_buff *skb) ...@@ -82,7 +82,7 @@ int ip_forward(struct sk_buff *skb)
if (iph->ttl <= 1) if (iph->ttl <= 1)
goto too_many_hops; goto too_many_hops;
if (!xfrm_route_forward(skb)) if (!xfrm4_route_forward(skb))
goto drop; goto drop;
iph = skb->nh.iph; iph = skb->nh.iph;
......
...@@ -236,7 +236,7 @@ static inline int ip_local_deliver_finish(struct sk_buff *skb) ...@@ -236,7 +236,7 @@ static inline int ip_local_deliver_finish(struct sk_buff *skb)
int ret; int ret;
if (!ipprot->no_policy && if (!ipprot->no_policy &&
!xfrm_policy_check(NULL, XFRM_POLICY_IN, skb)) { !xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) {
kfree_skb(skb); kfree_skb(skb);
return 0; return 0;
} }
...@@ -248,7 +248,7 @@ static inline int ip_local_deliver_finish(struct sk_buff *skb) ...@@ -248,7 +248,7 @@ static inline int ip_local_deliver_finish(struct sk_buff *skb)
IP_INC_STATS_BH(IpInDelivers); IP_INC_STATS_BH(IpInDelivers);
} else { } else {
if (!raw_sk) { 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); IP_INC_STATS_BH(IpInUnknownProtos);
icmp_send(skb, ICMP_DEST_UNREACH, icmp_send(skb, ICMP_DEST_UNREACH,
ICMP_PROT_UNREACH, 0); ICMP_PROT_UNREACH, 0);
......
...@@ -251,7 +251,7 @@ static int raw_rcv_skb(struct sock * sk, struct sk_buff * skb) ...@@ -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) 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); kfree_skb(skb);
return NET_RX_DROP; return NET_RX_DROP;
} }
......
...@@ -2600,13 +2600,6 @@ static int ip_rt_acct_read(char *buffer, char **start, off_t offset, ...@@ -2600,13 +2600,6 @@ static int ip_rt_acct_read(char *buffer, char **start, off_t offset,
#endif /* CONFIG_PROC_FS */ #endif /* CONFIG_PROC_FS */
#endif /* CONFIG_NET_CLS_ROUTE */ #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 __init ip_rt_init(void)
{ {
int i, order, goal, rc = 0; int i, order, goal, rc = 0;
...@@ -2688,7 +2681,6 @@ int __init ip_rt_init(void) ...@@ -2688,7 +2681,6 @@ int __init ip_rt_init(void)
ip_rt_gc_interval; ip_rt_gc_interval;
add_timer(&rt_periodic_timer); add_timer(&rt_periodic_timer);
xfrm_dst_lookup_register(xfrm_dst_lookup, AF_INET);
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
if (rt_cache_proc_init()) if (rt_cache_proc_init())
goto out_enomem; goto out_enomem;
...@@ -2698,6 +2690,7 @@ int __init ip_rt_init(void) ...@@ -2698,6 +2690,7 @@ int __init ip_rt_init(void)
#endif #endif
#endif #endif
xfrm_init(); xfrm_init();
xfrm4_init();
out: out:
return rc; return rc;
out_enomem: out_enomem:
......
...@@ -1798,7 +1798,7 @@ int tcp_v4_rcv(struct sk_buff *skb) ...@@ -1798,7 +1798,7 @@ int tcp_v4_rcv(struct sk_buff *skb)
if (sk->state == TCP_TIME_WAIT) if (sk->state == TCP_TIME_WAIT)
goto do_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; goto discard_and_relse;
if (sk_filter(sk, skb, 0)) if (sk_filter(sk, skb, 0))
...@@ -1820,7 +1820,7 @@ int tcp_v4_rcv(struct sk_buff *skb) ...@@ -1820,7 +1820,7 @@ int tcp_v4_rcv(struct sk_buff *skb)
return ret; return ret;
no_tcp_socket: no_tcp_socket:
if (!xfrm_policy_check(NULL, XFRM_POLICY_IN, skb)) if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb))
goto discard_it; goto discard_it;
if (skb->len < (th->doff << 2) || tcp_checksum_complete(skb)) { if (skb->len < (th->doff << 2) || tcp_checksum_complete(skb)) {
...@@ -1840,7 +1840,7 @@ int tcp_v4_rcv(struct sk_buff *skb) ...@@ -1840,7 +1840,7 @@ int tcp_v4_rcv(struct sk_buff *skb)
goto discard_it; goto discard_it;
do_time_wait: 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; goto discard_and_relse;
if (skb->len < (th->doff << 2) || tcp_checksum_complete(skb)) { 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) ...@@ -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. * 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); kfree_skb(skb);
return -1; return -1;
} }
...@@ -1077,7 +1077,7 @@ int udp_rcv(struct sk_buff *skb) ...@@ -1077,7 +1077,7 @@ int udp_rcv(struct sk_buff *skb)
return 0; return 0;
} }
if (!xfrm_policy_check(NULL, XFRM_POLICY_IN, skb)) if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb))
goto drop; goto drop;
/* No socket. Drop packet silently, if checksum is wrong */ /* 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 * Changes:
* Kazunori MIYAZAWA @USAGI : * YOSHIFUJI Hideaki @USAGI
* YOSHIFUJI Hideaki @USAGI : * Split up af-specific portion
* Kunihiro Ishiguro :
* *
*/ */
#include <net/ip.h> #include <net/ip.h>
#include <net/ipv6.h>
#include <net/xfrm.h> #include <net/xfrm.h>
static kmem_cache_t *secpath_cachep;
void __secpath_destroy(struct sec_path *sp) void __secpath_destroy(struct sec_path *sp)
{ {
int i; int i;
for (i = 0; i < sp->len; i++) for (i = 0; i < sp->len; i++)
xfrm_state_put(sp->xvec[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 */ /* 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; int offset, offset_seq;
...@@ -53,402 +50,3 @@ static int xfrm_parse_spi(struct sk_buff *skb, u32 *spi, u32 *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); *seq = *(u32*)(skb->h.raw + offset_seq);
return 0; 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 * Changes:
* Kazunori MIYAZAWA @USAGI : * Mitsuru KANDA @USAGI
* Kunihiro Ishiguro : * Kazunori MIYAZAWA @USAGI
* Kunihiro Ishiguro
* IPv6 support
* Kazunori MIYAZAWA @USAGI
* YOSHIFUJI Hideaki
* Split up af-specific portion
* *
*/ */
#include <linux/config.h> #include <linux/config.h>
#include <net/xfrm.h> #include <net/xfrm.h>
#include <net/ip.h> #include <net/ip.h>
#include <net/ipv6.h>
#include <net/ip6_route.h>
DECLARE_MUTEX(xfrm_cfg_sem); DECLARE_MUTEX(xfrm_cfg_sem);
...@@ -19,12 +23,10 @@ static rwlock_t xfrm_policy_lock = RW_LOCK_UNLOCKED; ...@@ -19,12 +23,10 @@ static rwlock_t xfrm_policy_lock = RW_LOCK_UNLOCKED;
struct xfrm_policy *xfrm_policy_list[XFRM_POLICY_MAX*2]; struct xfrm_policy *xfrm_policy_list[XFRM_POLICY_MAX*2];
extern struct dst_ops xfrm4_dst_ops; static rwlock_t xfrm_policy_afinfo_lock = RW_LOCK_UNLOCKED;
#if defined (CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) static struct xfrm_policy_afinfo *xfrm_policy_afinfo[NPROTO];
extern struct dst_ops xfrm6_dst_ops;
#endif
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 /* Limited flow cache. Its function now is to accelerate search for
* policy rules. * policy rules.
...@@ -49,40 +51,8 @@ static kmem_cache_t *flow_cachep; ...@@ -49,40 +51,8 @@ static kmem_cache_t *flow_cachep;
struct flow_entry **flow_table; struct flow_entry **flow_table;
#define FLOWCACHE_HASH_SIZE 1024 static int flow_lwm = 2*XFRM_FLOWCACHE_HASH_SIZE;
static int flow_hwm = 4*XFRM_FLOWCACHE_HASH_SIZE;
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_number[NR_CPUS] __cacheline_aligned; static int flow_number[NR_CPUS] __cacheline_aligned;
...@@ -92,11 +62,11 @@ static void flow_cache_shrink(int cpu) ...@@ -92,11 +62,11 @@ static void flow_cache_shrink(int cpu)
{ {
int i; int i;
struct flow_entry *fle, **flp; 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; 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) { while ((fle=*flp) != NULL && k<shrink_to) {
k++; k++;
flp = &fle->next; flp = &fle->next;
...@@ -118,23 +88,12 @@ struct xfrm_policy *flow_lookup(int dir, struct flowi *fl, ...@@ -118,23 +88,12 @@ struct xfrm_policy *flow_lookup(int dir, struct flowi *fl,
u32 hash; u32 hash;
int cpu; int cpu;
switch (family) { hash = flow_hash(fl, 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;
}
local_bh_disable(); local_bh_disable();
cpu = smp_processor_id(); 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) { fle; fle = fle->next) {
if (memcmp(fl, &fle->fl, sizeof(fle->fl)) == 0 && if (memcmp(fl, &fle->fl, sizeof(fle->fl)) == 0 &&
fle->dir == dir) { fle->dir == dir) {
...@@ -172,8 +131,8 @@ struct xfrm_policy *flow_lookup(int dir, struct flowi *fl, ...@@ -172,8 +131,8 @@ struct xfrm_policy *flow_lookup(int dir, struct flowi *fl,
fle->pol = pol; fle->pol = pol;
if (pol) if (pol)
atomic_inc(&pol->refcnt); atomic_inc(&pol->refcnt);
fle->next = flow_table[cpu*FLOWCACHE_HASH_SIZE+hash]; fle->next = flow_table[cpu*XFRM_FLOWCACHE_HASH_SIZE+hash];
flow_table[cpu*FLOWCACHE_HASH_SIZE+hash] = fle; flow_table[cpu*XFRM_FLOWCACHE_HASH_SIZE+hash] = fle;
} }
} }
local_bh_enable(); local_bh_enable();
...@@ -193,7 +152,7 @@ void __init flow_cache_init(void) ...@@ -193,7 +152,7 @@ void __init flow_cache_init(void)
panic("NET: failed to allocate flow cache slab\n"); panic("NET: failed to allocate flow cache slab\n");
for (order = 0; 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++) order++)
/* NOTHING */; /* NOTHING */;
...@@ -205,130 +164,82 @@ void __init flow_cache_init(void) ...@@ -205,130 +164,82 @@ void __init flow_cache_init(void)
memset(flow_table, 0, PAGE_SIZE<<order); memset(flow_table, 0, PAGE_SIZE<<order);
} }
static struct xfrm_type *xfrm_type_map[256]; int xfrm_register_type(struct xfrm_type *type, unsigned short family)
static rwlock_t xfrm_type_lock = RW_LOCK_UNLOCKED;
int xfrm_register_type(struct xfrm_type *type)
{ {
struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family);
struct xfrm_type_map *typemap;
int err = 0; int err = 0;
write_lock(&xfrm_type_lock); if (unlikely(afinfo == NULL))
if (xfrm_type_map[type->proto] == NULL) return -EAFNOSUPPORT;
xfrm_type_map[type->proto] = type; typemap = afinfo->type_map;
write_lock(&typemap->lock);
if (likely(typemap->map[type->proto] == NULL))
typemap->map[type->proto] = type;
else else
err = -EEXIST; err = -EEXIST;
write_unlock(&xfrm_type_lock); write_unlock(&typemap->lock);
xfrm_policy_put_afinfo(afinfo);
return err; 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; int err = 0;
write_lock(&xfrm_type_lock); if (unlikely(afinfo == NULL))
if (xfrm_type_map[type->proto] != type) return -EAFNOSUPPORT;
typemap = afinfo->type_map;
write_lock(&typemap->lock);
if (unlikely(typemap->map[type->proto] != type))
err = -ENOENT; err = -ENOENT;
else else
xfrm_type_map[type->proto] = NULL; typemap->map[type->proto] = NULL;
write_unlock(&xfrm_type_lock); write_unlock(&typemap->lock);
xfrm_policy_put_afinfo(afinfo);
return err; 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; struct xfrm_type *type;
read_lock(&xfrm_type_lock); if (unlikely(afinfo == NULL))
type = xfrm_type_map[proto]; return NULL;
if (type && !try_module_get(type->owner)) typemap = afinfo->type_map;
read_lock(&typemap->lock);
type = typemap->map[proto];
if (unlikely(type && !try_module_get(type->owner)))
type = NULL; type = NULL;
read_unlock(&xfrm_type_lock); read_unlock(&typemap->lock);
xfrm_policy_put_afinfo(afinfo);
return type; return type;
} }
static xfrm_dst_lookup_t *__xfrm_dst_lookup[AF_MAX]; int xfrm_dst_lookup(struct xfrm_dst **dst, struct flowi *fl,
rwlock_t xdl_lock = RW_LOCK_UNLOCKED; unsigned short family)
int xfrm_dst_lookup_register(xfrm_dst_lookup_t *dst_lookup,
unsigned short family)
{ {
struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family);
int err = 0; int err = 0;
write_lock(&xdl_lock); if (unlikely(afinfo == NULL))
if (__xfrm_dst_lookup[family]) return -EAFNOSUPPORT;
err = -ENOBUFS;
else {
__xfrm_dst_lookup[family] = dst_lookup;
}
write_unlock(&xdl_lock);
return err; if (likely(afinfo->dst_lookup != NULL))
} err = afinfo->dst_lookup(dst, fl);
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);
else else
err = -EINVAL; err = -EINVAL;
read_unlock(&xdl_lock); xfrm_policy_put_afinfo(afinfo);
return err; 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) void xfrm_put_type(struct xfrm_type *type)
{ {
module_put(type->owner); module_put(type->owner);
...@@ -608,18 +519,7 @@ struct xfrm_policy *xfrm_policy_lookup(int dir, struct flowi *fl, ...@@ -608,18 +519,7 @@ struct xfrm_policy *xfrm_policy_lookup(int dir, struct flowi *fl,
if (pol->family != family) if (pol->family != family)
continue; continue;
switch (family) { match = xfrm_selector_match(sel, fl, 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;
}
if (match) { if (match) {
atomic_inc(&pol->refcnt); atomic_inc(&pol->refcnt);
break; break;
...@@ -637,18 +537,7 @@ struct xfrm_policy *xfrm_sk_policy_lookup(struct sock *sk, int dir, struct flowi ...@@ -637,18 +537,7 @@ struct xfrm_policy *xfrm_sk_policy_lookup(struct sock *sk, int dir, struct flowi
if ((pol = sk->policy[dir]) != NULL) { if ((pol = sk->policy[dir]) != NULL) {
int match; int match;
switch (sk->family) { match = xfrm_selector_match(&pol->selector, fl, 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;
}
if (match) if (match)
atomic_inc(&pol->refcnt); atomic_inc(&pol->refcnt);
else else
...@@ -750,72 +639,27 @@ void __xfrm_sk_free_policy(struct xfrm_policy *pol, int dir) ...@@ -750,72 +639,27 @@ void __xfrm_sk_free_policy(struct xfrm_policy *pol, int dir)
/* Resolve list of templates for the flow, given policy. */ /* Resolve list of templates for the flow, given policy. */
static int static int
xfrm4_tmpl_resolve(struct xfrm_policy *policy, struct flowi *fl, xfrm_tmpl_resolve(struct xfrm_policy *policy, struct flowi *fl,
struct xfrm_state **xfrm) struct xfrm_state **xfrm,
unsigned short family)
{ {
int nx; int nx;
int i, error; int i, error;
u32 daddr = fl->fl4_dst; xfrm_address_t *daddr = xfrm_flowi_daddr(fl, family);
u32 saddr = fl->fl4_src; xfrm_address_t *saddr = xfrm_flowi_saddr(fl, family);
for (nx=0, i = 0; i < policy->xfrm_nr; i++) { for (nx=0, i = 0; i < policy->xfrm_nr; i++) {
struct xfrm_state *x; struct xfrm_state *x;
u32 remote = daddr; xfrm_address_t *remote = daddr;
u32 local = saddr; xfrm_address_t *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;
struct xfrm_tmpl *tmpl = &policy->xfrm_vec[i]; struct xfrm_tmpl *tmpl = &policy->xfrm_vec[i];
if (tmpl->mode) { if (tmpl->mode) {
remote = (struct in6_addr*)&tmpl->id.daddr; remote = &tmpl->id.daddr;
local = (struct in6_addr*)&tmpl->saddr; 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) { if (x && x->km.state == XFRM_STATE_VALID) {
xfrm[nx++] = x; xfrm[nx++] = x;
...@@ -839,241 +683,40 @@ xfrm6_tmpl_resolve(struct xfrm_policy *policy, struct flowi *fl, ...@@ -839,241 +683,40 @@ xfrm6_tmpl_resolve(struct xfrm_policy *policy, struct flowi *fl,
xfrm_state_put(xfrm[nx]); xfrm_state_put(xfrm[nx]);
return error; return error;
} }
#endif
/* Check that the bundle accepts the flow and its components are /* Check that the bundle accepts the flow and its components are
* still valid. * 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 { struct dst_entry *x;
if (xdst->u.dst.ops != &xfrm4_dst_ops) struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family);
return 1; if (unlikely(afinfo == NULL))
return ERR_PTR(-EINVAL);
if (!xfrm4_selector_match(&xdst->u.dst.xfrm->sel, fl)) x = afinfo->find_bundle(fl, rt, policy);
return 0; xfrm_policy_put_afinfo(afinfo);
if (xdst->u.dst.xfrm->km.state != XFRM_STATE_VALID || return x;
xdst->u.dst.path->obsolete > 0)
return 0;
xdst = (struct xfrm_dst*)xdst->u.dst.child;
} while (xdst);
return 0;
} }
#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 /* Allocate chain of dst_entry's, attach known xfrm's, calculate
* all the metrics... Shortly, bundle a bundle. * all the metrics... Shortly, bundle a bundle.
*/ */
static int static int
xfrm_bundle_create(struct xfrm_policy *policy, struct xfrm_state **xfrm, int nx, 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 err;
int header_len = 0; struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family);
int trailer_len = 0; if (unlikely(afinfo == NULL))
return -EINVAL;
dst = dst_prev = NULL; err = afinfo->bundle_create(policy, xfrm, nx, fl, dst_p);
xfrm_policy_put_afinfo(afinfo);
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);
return err; return err;
} }
#endif
/* Main function: finds/creates a bundle for given flow. /* Main function: finds/creates a bundle for given flow.
* *
...@@ -1141,46 +784,17 @@ int xfrm_lookup(struct dst_entry **dst_p, struct flowi *fl, ...@@ -1141,46 +784,17 @@ int xfrm_lookup(struct dst_entry **dst_p, struct flowi *fl,
* LATER: help from flow cache. It is optional, this * LATER: help from flow cache. It is optional, this
* is required only for output policy. * is required only for output policy.
*/ */
if (family == AF_INET) { dst = xfrm_find_bundle(fl, rt, policy, family);
read_lock_bh(&policy->lock); if (IS_ERR(dst)) {
for (dst = policy->bundles; dst; dst = dst->next) { xfrm_pol_put(policy);
struct xfrm_dst *xdst = (struct xfrm_dst*)dst; return PTR_ERR(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;
} }
if (dst) if (dst)
break; break;
nx = xfrm_tmpl_resolve(policy, fl, xfrm, family);
if (unlikely(nx<0)) { if (unlikely(nx<0)) {
err = nx; err = nx;
if (err == -EAGAIN) { if (err == -EAGAIN) {
...@@ -1191,18 +805,7 @@ int xfrm_lookup(struct dst_entry **dst_p, struct flowi *fl, ...@@ -1191,18 +805,7 @@ int xfrm_lookup(struct dst_entry **dst_p, struct flowi *fl,
__set_task_state(tsk, TASK_INTERRUPTIBLE); __set_task_state(tsk, TASK_INTERRUPTIBLE);
add_wait_queue(&km_waitq, &wait); add_wait_queue(&km_waitq, &wait);
switch (family) { err = xfrm_tmpl_resolve(policy, fl, xfrm, 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;
}
if (err == -EAGAIN) if (err == -EAGAIN)
schedule(); schedule();
__set_task_state(tsk, TASK_RUNNING); __set_task_state(tsk, TASK_RUNNING);
...@@ -1225,19 +828,8 @@ int xfrm_lookup(struct dst_entry **dst_p, struct flowi *fl, ...@@ -1225,19 +828,8 @@ int xfrm_lookup(struct dst_entry **dst_p, struct flowi *fl,
} }
dst = &rt->u.dst; dst = &rt->u.dst;
switch (family) { err = xfrm_bundle_create(policy, xfrm, nx, fl, &dst, 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;
}
if (unlikely(err)) { if (unlikely(err)) {
int i; int i;
for (i=0; i<nx; i++) for (i=0; i<nx; i++)
...@@ -1282,137 +874,39 @@ int xfrm_lookup(struct dst_entry **dst_p, struct flowi *fl, ...@@ -1282,137 +874,39 @@ int xfrm_lookup(struct dst_entry **dst_p, struct flowi *fl,
*/ */
static inline int static inline int
xfrm_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 || !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)
{ {
return x->id.proto == tmpl->id.proto && return x->id.proto == tmpl->id.proto &&
(x->id.spi == tmpl->id.spi || !tmpl->id.spi) && (x->id.spi == tmpl->id.spi || !tmpl->id.spi) &&
x->props.mode == tmpl->mode && x->props.mode == tmpl->mode &&
(tmpl->aalgos & (1<<x->props.aalgo)) && (tmpl->aalgos & (1<<x->props.aalgo)) &&
(!x->props.mode || !ipv6_addr_any((struct in6_addr*)&x->props.saddr) || !(x->props.mode && xfrm_state_addr_cmp(tmpl, x, family));
!ipv6_addr_cmp((struct in6_addr *)&tmpl->saddr, (struct in6_addr*)&x->props.saddr));
} }
static inline int 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++) { 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 ++idx;
} }
return -1; return -1;
} }
static inline void static int
_decode_session6(struct sk_buff *skb, struct flowi *fl) _decode_session(struct sk_buff *skb, struct flowi *fl, unsigned short family)
{ {
u16 offset = sizeof(struct ipv6hdr); struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family);
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: if (unlikely(afinfo == NULL))
case IPPROTO_TCP: return -EAFNOSUPPORT;
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]; afinfo->decode_session(skb, fl);
fl->uli_u.ports.dport = ports[1]; xfrm_policy_put_afinfo(afinfo);
} return 0;
return;
/* XXX Why are there these headers? */
case IPPROTO_AH:
case IPPROTO_ESP:
default:
fl->uli_u.spi = 0;
return;
};
}
} }
#endif
int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb, int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
unsigned short family) unsigned short family)
...@@ -1420,38 +914,15 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb, ...@@ -1420,38 +914,15 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
struct xfrm_policy *pol; struct xfrm_policy *pol;
struct flowi fl; struct flowi fl;
switch (family) { if (_decode_session(skb, &fl, family) < 0)
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 :
return 0; return 0;
}
/* First, check used SA against their selectors. */ /* First, check used SA against their selectors. */
if (skb->sp) { if (skb->sp) {
int i; int i;
for (i=skb->sp->len-1; i>=0; i--) { for (i=skb->sp->len-1; i>=0; i--) {
int match; if (!xfrm_selector_match(&skb->sp->xvec[i]->sel, &fl, family))
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)
return 0; return 0;
} }
} }
...@@ -1483,20 +954,7 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb, ...@@ -1483,20 +954,7 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
* are implied between each two transformations. * are implied between each two transformations.
*/ */
for (i = pol->xfrm_nr-1, k = 0; i >= 0; i--) { for (i = pol->xfrm_nr-1, k = 0; i >= 0; i--) {
if (pol->xfrm_vec[i].optional) k = xfrm_policy_ok(pol->xfrm_vec+i, sp, k, family);
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;
}
if (k < 0) if (k < 0)
goto reject; goto reject;
} }
...@@ -1514,18 +972,8 @@ int __xfrm_route_forward(struct sk_buff *skb, unsigned short family) ...@@ -1514,18 +972,8 @@ int __xfrm_route_forward(struct sk_buff *skb, unsigned short family)
{ {
struct flowi fl; struct flowi fl;
switch (family) { if (_decode_session(skb, &fl, family) < 0)
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:
return 0; return 0;
}
return xfrm_lookup(&skb->dst, &fl, NULL, 0) == 0; return xfrm_lookup(&skb->dst, &fl, NULL, 0) == 0;
} }
...@@ -1603,20 +1051,6 @@ static void __xfrm_garbage_collect(void) ...@@ -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) static int bundle_depends_on(struct dst_entry *dst, struct xfrm_state *x)
{ {
do { do {
...@@ -1660,29 +1094,6 @@ int xfrm_flush_bundles(struct xfrm_state *x) ...@@ -1660,29 +1094,6 @@ int xfrm_flush_bundles(struct xfrm_state *x)
return 0; 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 /* Well... that's _TASK_. We need to scan through transformation
* list and figure out what mss tcp should generate in order to * list and figure out what mss tcp should generate in order to
* final datagram fit to mtu. Mama mia... :-) * final datagram fit to mtu. Mama mia... :-)
...@@ -1723,52 +1134,99 @@ static int xfrm_get_mss(struct dst_entry *dst, u32 mtu) ...@@ -1723,52 +1134,99 @@ static int xfrm_get_mss(struct dst_entry *dst, u32 mtu)
return res + dst->header_len; return res + dst->header_len;
} }
struct dst_ops xfrm4_dst_ops = { int xfrm_policy_register_afinfo(struct xfrm_policy_afinfo *afinfo)
.family = AF_INET, {
.protocol = __constant_htons(ETH_P_IP), int err = 0;
.gc = xfrm4_garbage_collect, if (unlikely(afinfo == NULL))
.check = xfrm_dst_check, return -EINVAL;
.destroy = xfrm_dst_destroy, if (unlikely(afinfo->family >= NPROTO))
.negative_advice = xfrm_negative_advice, return -EAFNOSUPPORT;
.link_failure = xfrm_link_failure, write_lock(&xfrm_policy_afinfo_lock);
.update_pmtu = xfrm4_update_pmtu, if (unlikely(xfrm_policy_afinfo[afinfo->family] != NULL))
.get_mss = xfrm_get_mss, err = -ENOBUFS;
.gc_thresh = 1024, else {
.entry_size = sizeof(struct xfrm_dst), 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) int xfrm_policy_unregister_afinfo(struct xfrm_policy_afinfo *afinfo)
struct dst_ops xfrm6_dst_ops = { {
.family = AF_INET6, int err = 0;
.protocol = __constant_htons(ETH_P_IPV6), if (unlikely(afinfo == NULL))
.gc = xfrm6_garbage_collect, return -EINVAL;
.check = xfrm_dst_check, if (unlikely(afinfo->family >= NPROTO))
.destroy = xfrm_dst_destroy, return -EAFNOSUPPORT;
.negative_advice = xfrm_negative_advice, write_lock(&xfrm_policy_afinfo_lock);
.link_failure = xfrm_link_failure, if (likely(xfrm_policy_afinfo[afinfo->family] != NULL)) {
.update_pmtu = xfrm6_update_pmtu, if (unlikely(xfrm_policy_afinfo[afinfo->family] != afinfo))
.get_mss = xfrm_get_mss, err = -EINVAL;
.gc_thresh = 1024, else {
.entry_size = sizeof(struct xfrm_dst), struct dst_ops *dst_ops = afinfo->dst_ops;
}; xfrm_policy_afinfo[afinfo->family] = NULL;
#endif /* CONFIG_IPV6 || CONFIG_IPV6_MODULE */ 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", struct xfrm_policy_afinfo *afinfo;
sizeof(struct xfrm_dst), if (unlikely(family >= NPROTO))
0, SLAB_HWCACHE_ALIGN, return NULL;
NULL, 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) void xfrm_policy_put_afinfo(struct xfrm_policy_afinfo *afinfo)
panic("IP: failed to allocate xfrm4_dst_cache\n"); {
if (unlikely(afinfo == NULL))
return;
read_unlock(&afinfo->lock);
}
#if defined (CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) void __init xfrm_policy_init(void)
xfrm6_dst_ops.kmem_cachep = xfrm4_dst_ops.kmem_cachep; {
#endif 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_state_init();
xfrm_input_init(); flow_cache_init();
xfrm_policy_init();
} }
/* Changes /*
* xfrm_state.c
* *
* Mitsuru KANDA @USAGI : IPv6 Support * Changes:
* Kazunori MIYAZAWA @USAGI : * Mitsuru KANDA @USAGI
* Kunihiro Ishiguro : * Kazunori MIYAZAWA @USAGI
* Kunihiro Ishiguro
* IPv6 support
* YOSHIFUJI Hideaki @USAGI
* Split up af-specific functions
* *
*/ */
#include <net/xfrm.h> #include <net/xfrm.h>
#include <linux/pfkeyv2.h> #include <linux/pfkeyv2.h>
#include <linux/ipsec.h> #include <linux/ipsec.h>
#include <net/ipv6.h>
/* Each xfrm_state may be linked to two tables: /* Each xfrm_state may be linked to two tables:
...@@ -20,8 +24,6 @@ ...@@ -20,8 +24,6 @@
static spinlock_t xfrm_state_lock = SPIN_LOCK_UNLOCKED; static spinlock_t xfrm_state_lock = SPIN_LOCK_UNLOCKED;
#define XFRM_DST_HSIZE 1024
/* Hash table to find appropriate SA towards given target (endpoint /* Hash table to find appropriate SA towards given target (endpoint
* of tunnel or destination of transport mode) allowed by selector. * of tunnel or destination of transport mode) allowed by selector.
* *
...@@ -33,7 +35,8 @@ static struct list_head xfrm_state_byspi[XFRM_DST_HSIZE]; ...@@ -33,7 +35,8 @@ static struct list_head xfrm_state_byspi[XFRM_DST_HSIZE];
DECLARE_WAIT_QUEUE_HEAD(km_waitq); 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); static void __xfrm_state_delete(struct xfrm_state *x);
...@@ -214,138 +217,37 @@ void xfrm_state_flush(u8 proto) ...@@ -214,138 +217,37 @@ void xfrm_state_flush(u8 proto)
wake_up(&km_waitq); wake_up(&km_waitq);
} }
struct xfrm_state * static int
xfrm4_state_find(u32 daddr, u32 saddr, struct flowi *fl, struct xfrm_tmpl *tmpl, xfrm_init_tempsel(struct xfrm_state *x, struct flowi *fl,
struct xfrm_policy *pol, int *err) struct xfrm_tmpl *tmpl,
xfrm_address_t *daddr, xfrm_address_t *saddr,
unsigned short family)
{ {
unsigned h = ntohl(daddr); struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
struct xfrm_state *x; if (!afinfo)
int acquire_in_progress = 0; return -1;
int error = 0; afinfo->init_tempsel(x, fl, tmpl, daddr, saddr);
struct xfrm_state *best = NULL; xfrm_state_put_afinfo(afinfo);
return 0;
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;
} }
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
struct xfrm_state * struct xfrm_state *
xfrm6_state_find(struct in6_addr *daddr, struct in6_addr *saddr, struct flowi *fl, struct xfrm_tmpl *tmpl, xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
struct xfrm_policy *pol, int *err) 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; struct xfrm_state *x;
int acquire_in_progress = 0; int acquire_in_progress = 0;
int error = 0; int error = 0;
struct xfrm_state *best = NULL; struct xfrm_state *best = NULL;
h = (h ^ (h>>16)) % XFRM_DST_HSIZE;
spin_lock_bh(&xfrm_state_lock); spin_lock_bh(&xfrm_state_lock);
list_for_each_entry(x, xfrm_state_bydst+h, bydst) { list_for_each_entry(x, xfrm_state_bydst+h, bydst) {
if (x->props.family == AF_INET6&& if (x->props.family == family &&
!ipv6_addr_cmp(daddr, (struct in6_addr *)&x->id.daddr) &&
x->props.reqid == tmpl->reqid && 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->mode == x->props.mode &&
tmpl->id.proto == x->id.proto) { tmpl->id.proto == x->id.proto) {
/* Resolution logic: /* Resolution logic:
...@@ -362,7 +264,7 @@ xfrm6_state_find(struct in6_addr *daddr, struct in6_addr *saddr, struct flowi *f ...@@ -362,7 +264,7 @@ xfrm6_state_find(struct in6_addr *daddr, struct in6_addr *saddr, struct flowi *f
selector. selector.
*/ */
if (x->km.state == XFRM_STATE_VALID) { if (x->km.state == XFRM_STATE_VALID) {
if (!xfrm6_selector_match(&x->sel, fl)) if (!xfrm_selector_match(&x->sel, fl, family))
continue; continue;
if (!best || if (!best ||
best->km.dying > x->km.dying || best->km.dying > x->km.dying ||
...@@ -373,7 +275,7 @@ xfrm6_state_find(struct in6_addr *daddr, struct in6_addr *saddr, struct flowi *f ...@@ -373,7 +275,7 @@ xfrm6_state_find(struct in6_addr *daddr, struct in6_addr *saddr, struct flowi *f
acquire_in_progress = 1; acquire_in_progress = 1;
} else if (x->km.state == XFRM_STATE_ERROR || } else if (x->km.state == XFRM_STATE_ERROR ||
x->km.state == XFRM_STATE_EXPIRED) { x->km.state == XFRM_STATE_EXPIRED) {
if (xfrm6_selector_match(&x->sel, fl)) if (xfrm_selector_match(&x->sel, fl, family))
error = 1; error = 1;
} }
} }
...@@ -384,45 +286,29 @@ xfrm6_state_find(struct in6_addr *daddr, struct in6_addr *saddr, struct flowi *f ...@@ -384,45 +286,29 @@ xfrm6_state_find(struct in6_addr *daddr, struct in6_addr *saddr, struct flowi *f
spin_unlock_bh(&xfrm_state_lock); spin_unlock_bh(&xfrm_state_lock);
return best; return best;
} }
x = NULL; x = NULL;
if (!error && !acquire_in_progress && if (!error && !acquire_in_progress &&
((x = xfrm_state_alloc()) != NULL)) { ((x = xfrm_state_alloc()) != NULL)) {
/* Initialize temporary selector matching only /* Initialize temporary selector matching only
* to current session. */ * 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.daddr, fl->fl6_dst, sizeof(struct in6_addr));
memcpy(&x->sel.saddr, fl->fl6_src, 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) { if (km_query(x, tmpl, pol) == 0) {
x->km.state = XFRM_STATE_ACQ; x->km.state = XFRM_STATE_ACQ;
list_add_tail(&x->bydst, xfrm_state_bydst+h); list_add_tail(&x->bydst, xfrm_state_bydst+h);
atomic_inc(&x->refcnt); atomic_inc(&x->refcnt);
if (x->id.spi) { if (x->id.spi) {
struct in6_addr *addr = (struct in6_addr*)&x->id.daddr; h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, family);
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;
list_add(&x->byspi, xfrm_state_byspi+h); list_add(&x->byspi, xfrm_state_byspi+h);
atomic_inc(&x->refcnt); 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); atomic_inc(&x->refcnt);
mod_timer(&x->timer, ACQ_EXPIRES*HZ); mod_timer(&x->timer, XFRM_ACQ_EXPIRES*HZ);
} else { } else {
x->km.state = XFRM_STATE_DEAD; x->km.state = XFRM_STATE_DEAD;
xfrm_state_put(x); xfrm_state_put(x);
...@@ -436,36 +322,17 @@ xfrm6_state_find(struct in6_addr *daddr, struct in6_addr *saddr, struct flowi *f ...@@ -436,36 +322,17 @@ xfrm6_state_find(struct in6_addr *daddr, struct in6_addr *saddr, struct flowi *f
(error ? -ESRCH : -ENOMEM); (error ? -ESRCH : -ENOMEM);
return x; return x;
} }
#endif /* CONFIG_IPV6 || CONFIG_IPV6_MODULE */
void xfrm_state_insert(struct xfrm_state *x) void xfrm_state_insert(struct xfrm_state *x)
{ {
unsigned h = 0; unsigned h = xfrm_dst_hash(&x->id.daddr, x->props.family);
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;
spin_lock_bh(&xfrm_state_lock); spin_lock_bh(&xfrm_state_lock);
list_add(&x->bydst, xfrm_state_bydst+h); list_add(&x->bydst, xfrm_state_bydst+h);
atomic_inc(&x->refcnt); atomic_inc(&x->refcnt);
if (x->props.family == AF_INET) h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, x->props.family);
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;
list_add(&x->byspi, xfrm_state_byspi+h); list_add(&x->byspi, xfrm_state_byspi+h);
atomic_inc(&x->refcnt); atomic_inc(&x->refcnt);
...@@ -487,7 +354,7 @@ int xfrm_state_check_expire(struct xfrm_state *x) ...@@ -487,7 +354,7 @@ int xfrm_state_check_expire(struct xfrm_state *x)
if (x->curlft.bytes >= x->lft.hard_byte_limit || if (x->curlft.bytes >= x->lft.hard_byte_limit ||
x->curlft.packets >= x->lft.hard_packet_limit) { x->curlft.packets >= x->lft.hard_packet_limit) {
km_expired(x); 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); atomic_inc(&x->refcnt);
return -EINVAL; return -EINVAL;
} }
...@@ -512,158 +379,37 @@ int xfrm_state_check_space(struct xfrm_state *x, struct sk_buff *skb) ...@@ -512,158 +379,37 @@ int xfrm_state_check_space(struct xfrm_state *x, struct sk_buff *skb)
} }
struct xfrm_state * 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; struct xfrm_state *x;
struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
h = (h ^ (h>>10) ^ (h>>20)) % XFRM_DST_HSIZE; if (!afinfo)
return NULL;
spin_lock_bh(&xfrm_state_lock); spin_lock_bh(&xfrm_state_lock);
list_for_each_entry(x, xfrm_state_byspi+h, byspi) { x = afinfo->state_lookup(daddr, spi, proto);
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;
}
}
spin_unlock_bh(&xfrm_state_lock); 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 * 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; struct xfrm_state *x;
struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
h = (h ^ (h>>10) ^ (h>>20)) % XFRM_DST_HSIZE; if (!afinfo)
return NULL;
spin_lock_bh(&xfrm_state_lock); spin_lock_bh(&xfrm_state_lock);
list_for_each_entry(x, xfrm_state_byspi+h, byspi) { x = afinfo->find_acq(mode, reqid, proto, daddr, saddr, create);
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;
}
}
spin_unlock_bh(&xfrm_state_lock); spin_unlock_bh(&xfrm_state_lock);
return NULL; xfrm_state_put_afinfo(afinfo);
} return x;
#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;
} }
#endif
/* Silly enough, but I'm lazy to build resolution list */ /* 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) ...@@ -697,18 +443,7 @@ xfrm_alloc_spi(struct xfrm_state *x, u32 minspi, u32 maxspi)
return; return;
if (minspi == maxspi) { if (minspi == maxspi) {
switch(x->props.family) { x0 = xfrm_state_lookup(&x->id.daddr, minspi, x->id.proto, 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;
}
if (x0) { if (x0) {
xfrm_state_put(x0); xfrm_state_put(x0);
return; return;
...@@ -720,18 +455,7 @@ xfrm_alloc_spi(struct xfrm_state *x, u32 minspi, u32 maxspi) ...@@ -720,18 +455,7 @@ xfrm_alloc_spi(struct xfrm_state *x, u32 minspi, u32 maxspi)
maxspi = ntohl(maxspi); maxspi = ntohl(maxspi);
for (h=0; h<maxspi-minspi+1; h++) { for (h=0; h<maxspi-minspi+1; h++) {
spi = minspi + net_random()%(maxspi-minspi+1); spi = minspi + net_random()%(maxspi-minspi+1);
switch(x->props.family) { x0 = xfrm_state_lookup(&x->id.daddr, minspi, x->id.proto, 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;
}
if (x0 == NULL) if (x0 == NULL)
break; break;
xfrm_state_put(x0); xfrm_state_put(x0);
...@@ -740,19 +464,7 @@ xfrm_alloc_spi(struct xfrm_state *x, u32 minspi, u32 maxspi) ...@@ -740,19 +464,7 @@ xfrm_alloc_spi(struct xfrm_state *x, u32 minspi, u32 maxspi)
} }
if (x->id.spi) { if (x->id.spi) {
spin_lock_bh(&xfrm_state_lock); spin_lock_bh(&xfrm_state_lock);
switch(x->props.family) { h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, 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;
list_add(&x->byspi, xfrm_state_byspi+h); list_add(&x->byspi, xfrm_state_byspi+h);
atomic_inc(&x->refcnt); atomic_inc(&x->refcnt);
spin_unlock_bh(&xfrm_state_lock); spin_unlock_bh(&xfrm_state_lock);
...@@ -845,18 +557,7 @@ int xfrm_check_selectors(struct xfrm_state **x, int n, struct flowi *fl) ...@@ -845,18 +557,7 @@ int xfrm_check_selectors(struct xfrm_state **x, int n, struct flowi *fl)
for (i=0; i<n; i++) { for (i=0; i<n; i++) {
int match; int match;
switch(x[i]->props.family) { match = xfrm_selector_match(&x[i]->sel, fl, 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;
}
if (!match) if (!match)
return -EINVAL; return -EINVAL;
} }
...@@ -958,6 +659,66 @@ int xfrm_unregister_km(struct xfrm_mgr *km) ...@@ -958,6 +659,66 @@ int xfrm_unregister_km(struct xfrm_mgr *km)
return 0; 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) void __init xfrm_state_init(void)
{ {
int i; int i;
......
...@@ -2,11 +2,11 @@ ...@@ -2,11 +2,11 @@
* *
* Copyright (C) 2002 David S. Miller (davem@redhat.com) * Copyright (C) 2002 David S. Miller (davem@redhat.com)
* *
* Changes * Changes:
* * Mitsuru KANDA @USAGI
* Mitsuru KANDA @USAGI : IPv6 Support * Kazunori MIYAZAWA @USAGI
* Kazunori MIYAZAWA @USAGI : * Kunihiro Ishiguro
* Kunihiro Ishiguro : * IPv6 support
* *
*/ */
...@@ -24,9 +24,6 @@ ...@@ -24,9 +24,6 @@
#include <linux/ipsec.h> #include <linux/ipsec.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/security.h> #include <linux/security.h>
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
#include <linux/in6.h>
#endif
#include <net/sock.h> #include <net/sock.h>
#include <net/xfrm.h> #include <net/xfrm.h>
...@@ -191,19 +188,7 @@ static struct xfrm_state *xfrm_state_construct(struct xfrm_usersa_info *p, ...@@ -191,19 +188,7 @@ static struct xfrm_state *xfrm_state_construct(struct xfrm_usersa_info *p,
goto error; goto error;
err = -ENOENT; err = -ENOENT;
switch (x->props.family) { x->type = xfrm_get_type(x->id.proto, 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;
}
if (x->type == NULL) if (x->type == NULL)
goto error; goto error;
...@@ -238,21 +223,7 @@ static int xfrm_add_sa(struct sk_buff *skb, struct nlmsghdr *nlh, void **xfrma) ...@@ -238,21 +223,7 @@ static int xfrm_add_sa(struct sk_buff *skb, struct nlmsghdr *nlh, void **xfrma)
if (!x) if (!x)
return err; return err;
switch (x->props.family) { x1 = xfrm_state_lookup(&x->props.saddr, x->id.spi, x->id.proto, 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;
}
if (x1) { if (x1) {
xfrm_state_put(x); xfrm_state_put(x);
xfrm_state_put(x1); xfrm_state_put(x1);
...@@ -269,19 +240,7 @@ static int xfrm_del_sa(struct sk_buff *skb, struct nlmsghdr *nlh, void **xfrma) ...@@ -269,19 +240,7 @@ static int xfrm_del_sa(struct sk_buff *skb, struct nlmsghdr *nlh, void **xfrma)
struct xfrm_state *x; struct xfrm_state *x;
struct xfrm_usersa_id *p = NLMSG_DATA(nlh); struct xfrm_usersa_id *p = NLMSG_DATA(nlh);
switch (p->family) { x = xfrm_state_lookup(&p->saddr, p->spi, p->proto, 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;
}
if (x == NULL) if (x == NULL)
return -ESRCH; return -ESRCH;
...@@ -399,19 +358,7 @@ static int xfrm_get_sa(struct sk_buff *skb, struct nlmsghdr *nlh, void **xfrma) ...@@ -399,19 +358,7 @@ static int xfrm_get_sa(struct sk_buff *skb, struct nlmsghdr *nlh, void **xfrma)
struct sk_buff *resp_skb; struct sk_buff *resp_skb;
int err; int err;
switch (p->family) { x = xfrm_state_lookup(&p->saddr, p->spi, p->proto, 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;
}
err = -ESRCH; err = -ESRCH;
if (x == NULL) if (x == NULL)
goto out_noput; goto out_noput;
...@@ -462,23 +409,10 @@ static int xfrm_alloc_userspi(struct sk_buff *skb, struct nlmsghdr *nlh, void ** ...@@ -462,23 +409,10 @@ static int xfrm_alloc_userspi(struct sk_buff *skb, struct nlmsghdr *nlh, void **
err = verify_userspi_info(p); err = verify_userspi_info(p);
if (err) if (err)
goto out_noput; goto out_noput;
switch (p->info.family) { x = xfrm_find_acq(p->info.mode, p->info.reqid, p->info.id.proto,
case AF_INET: &p->info.sel.daddr,
x = xfrm_find_acq(p->info.mode, p->info.reqid, p->info.id.proto, &p->info.sel.saddr, 1,
p->info.sel.daddr.xfrm4_addr, p->info.family);
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;
}
err = -ENOENT; err = -ENOENT;
if (x == NULL) if (x == NULL)
goto out_noput; goto out_noput;
......
...@@ -8,7 +8,8 @@ ipv6-objs := af_inet6.o anycast.o ip6_output.o ip6_input.o addrconf.o sit.o \ ...@@ -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 \ 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 \ protocol.o icmp.o mcast.o reassembly.o tcp_ipv6.o \
exthdrs.o sysctl_net_ipv6.o datagram.o proc.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_AH) += ah6.o
obj-$(CONFIG_INET6_ESP) += esp6.o obj-$(CONFIG_INET6_ESP) += esp6.o
......
...@@ -228,7 +228,7 @@ void ah6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, ...@@ -228,7 +228,7 @@ void ah6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
type != ICMPV6_PKT_TOOBIG) type != ICMPV6_PKT_TOOBIG)
return; 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) if (!x)
return; return;
...@@ -336,14 +336,14 @@ int __init ah6_init(void) ...@@ -336,14 +336,14 @@ int __init ah6_init(void)
{ {
SET_MODULE_OWNER(&ah6_type); 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"); printk(KERN_INFO "ipv6 ah init: can't add xfrm type\n");
return -EAGAIN; return -EAGAIN;
} }
if (inet6_add_protocol(&ah6_protocol, IPPROTO_AH) < 0) { if (inet6_add_protocol(&ah6_protocol, IPPROTO_AH) < 0) {
printk(KERN_INFO "ipv6 ah init: can't add protocol\n"); 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; return -EAGAIN;
} }
...@@ -355,7 +355,7 @@ static void __exit ah6_fini(void) ...@@ -355,7 +355,7 @@ static void __exit ah6_fini(void)
if (inet6_del_protocol(&ah6_protocol, IPPROTO_AH) < 0) if (inet6_del_protocol(&ah6_protocol, IPPROTO_AH) < 0)
printk(KERN_INFO "ipv6 ah close: can't remove protocol\n"); 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"); 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, ...@@ -377,7 +377,7 @@ void esp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
type != ICMPV6_PKT_TOOBIG) type != ICMPV6_PKT_TOOBIG)
return; 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) if (!x)
return; return;
printk(KERN_DEBUG "pmtu discvovery on SA ESP/%08x/" printk(KERN_DEBUG "pmtu discvovery on SA ESP/%08x/"
...@@ -504,13 +504,13 @@ static struct inet6_protocol esp6_protocol = { ...@@ -504,13 +504,13 @@ static struct inet6_protocol esp6_protocol = {
int __init esp6_init(void) int __init esp6_init(void)
{ {
SET_MODULE_OWNER(&esp6_type); 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"); printk(KERN_INFO "ipv6 esp init: can't add xfrm type\n");
return -EAGAIN; return -EAGAIN;
} }
if (inet6_add_protocol(&esp6_protocol, IPPROTO_ESP) < 0) { if (inet6_add_protocol(&esp6_protocol, IPPROTO_ESP) < 0) {
printk(KERN_INFO "ipv6 esp init: can't add protocol\n"); 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; return -EAGAIN;
} }
...@@ -521,7 +521,7 @@ static void __exit esp6_fini(void) ...@@ -521,7 +521,7 @@ static void __exit esp6_fini(void)
{ {
if (inet6_del_protocol(&esp6_protocol, IPPROTO_ESP) < 0) if (inet6_del_protocol(&esp6_protocol, IPPROTO_ESP) < 0)
printk(KERN_INFO "ipv6 esp close: can't remove protocol\n"); 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"); printk(KERN_INFO "ipv6 esp close: can't remove xfrm type\n");
} }
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#include <net/ipv6.h> #include <net/ipv6.h>
#include <net/addrconf.h> #include <net/addrconf.h>
#include <net/ip6_route.h> #include <net/ip6_route.h>
#include <net/xfrm.h>
EXPORT_SYMBOL(ipv6_addr_type); EXPORT_SYMBOL(ipv6_addr_type);
EXPORT_SYMBOL(icmpv6_send); EXPORT_SYMBOL(icmpv6_send);
...@@ -31,3 +32,5 @@ EXPORT_SYMBOL(ipv6_get_saddr); ...@@ -31,3 +32,5 @@ EXPORT_SYMBOL(ipv6_get_saddr);
EXPORT_SYMBOL(ipv6_chk_addr); EXPORT_SYMBOL(ipv6_chk_addr);
EXPORT_SYMBOL(in6addr_any); EXPORT_SYMBOL(in6addr_any);
EXPORT_SYMBOL(in6addr_loopback); EXPORT_SYMBOL(in6addr_loopback);
EXPORT_SYMBOL(xfrm6_rcv);
EXPORT_SYMBOL(xfrm6_clear_mutable_options);
...@@ -1867,15 +1867,6 @@ ctl_table ipv6_route_table[] = { ...@@ -1867,15 +1867,6 @@ ctl_table ipv6_route_table[] = {
#endif #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) void __init ip6_route_init(void)
{ {
ip6_dst_ops.kmem_cachep = kmem_cache_create("ip6_dst_cache", ip6_dst_ops.kmem_cachep = kmem_cache_create("ip6_dst_cache",
...@@ -1883,11 +1874,11 @@ void __init ip6_route_init(void) ...@@ -1883,11 +1874,11 @@ void __init ip6_route_init(void)
0, SLAB_HWCACHE_ALIGN, 0, SLAB_HWCACHE_ALIGN,
NULL, NULL); NULL, NULL);
fib6_init(); fib6_init();
xfrm_dst_lookup_register(xfrm6_dst_lookup, AF_INET6);
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
proc_net_create("ipv6_route", 0, rt6_proc_info); proc_net_create("ipv6_route", 0, rt6_proc_info);
proc_net_create("rt6_stats", 0, rt6_proc_stats); proc_net_create("rt6_stats", 0, rt6_proc_stats);
#endif #endif
xfrm6_init();
} }
#ifdef MODULE #ifdef MODULE
...@@ -1897,7 +1888,7 @@ void ip6_route_cleanup(void) ...@@ -1897,7 +1888,7 @@ void ip6_route_cleanup(void)
proc_net_remove("ipv6_route"); proc_net_remove("ipv6_route");
proc_net_remove("rt6_stats"); proc_net_remove("rt6_stats");
#endif #endif
xfrm_dst_lookup_unregister(AF_INET6); xfrm6_fini();
rt6_ifdown(NULL); rt6_ifdown(NULL);
fib6_gc_cleanup(); 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, ...@@ -510,10 +510,8 @@ static int pfkey_sadb_addr2xfrm_addr(struct sadb_address *addr,
{ {
switch (((struct sockaddr*)(addr + 1))->sa_family) { switch (((struct sockaddr*)(addr + 1))->sa_family) {
case AF_INET: case AF_INET:
xaddr->xfrm4_addr = xaddr->a4 =
((struct sockaddr_in *)(addr + 1))->sin_addr.s_addr; ((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; return AF_INET;
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
case AF_INET6: case AF_INET6:
...@@ -530,10 +528,11 @@ static int pfkey_sadb_addr2xfrm_addr(struct sadb_address *addr, ...@@ -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) 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_sa *sa;
struct sadb_address *addr; struct sadb_address *addr;
uint16_t proto; uint16_t proto;
unsigned short family;
xfrm_address_t *xaddr;
sa = (struct sadb_sa *) ext_hdrs[SADB_EXT_SA-1]; sa = (struct sadb_sa *) ext_hdrs[SADB_EXT_SA-1];
if (sa == NULL) if (sa == NULL)
...@@ -548,23 +547,24 @@ static struct xfrm_state *pfkey_xfrm_state_lookup(struct sadb_msg *hdr, void ** ...@@ -548,23 +547,24 @@ static struct xfrm_state *pfkey_xfrm_state_lookup(struct sadb_msg *hdr, void **
if (addr == NULL) if (addr == NULL)
return NULL; return NULL;
switch (((struct sockaddr *)(addr + 1))->sa_family) { family = ((struct sockaddr *)(addr + 1))->sa_family;
switch (family) {
case AF_INET: case AF_INET:
x = xfrm4_state_lookup(((struct sockaddr_in *)(addr + 1))->sin_addr.s_addr, xaddr = (xfrm_address_t *)&((struct sockaddr_in *)(addr + 1))->sin_addr;
sa->sadb_sa_spi, proto);
break; break;
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
case AF_INET6: case AF_INET6:
x = xfrm6_state_lookup(&((struct sockaddr_in6 *)(addr + 1))->sin6_addr, xaddr = (xfrm_address_t *)&((struct sockaddr_in6 *)(addr + 1))->sin6_addr;
sa->sadb_sa_spi, proto);
break; break;
#endif #endif
default: default:
x = NULL; xaddr = NULL;
break;
} }
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))) #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, ...@@ -619,7 +619,7 @@ static struct sk_buff * pfkey_xfrm_state2msg(struct xfrm_state *x, int add_keys,
/* identity & sensitivity */ /* identity & sensitivity */
if ((x->props.family == AF_INET && 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) #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
|| (x->props.family == AF_INET6 && || (x->props.family == AF_INET6 &&
memcmp (x->sel.saddr.a6, x->props.saddr.a6, sizeof (struct in6_addr))) 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, ...@@ -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 = (struct sockaddr_in *) (addr + 1);
sin->sin_family = AF_INET; 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; sin->sin_port = 0;
memset(sin->sin_zero, 0, sizeof(sin->sin_zero)); 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, ...@@ -764,11 +764,11 @@ static struct sk_buff * pfkey_xfrm_state2msg(struct xfrm_state *x, int add_keys,
if (x->props.family == AF_INET) { if (x->props.family == AF_INET) {
sin = (struct sockaddr_in *) (addr + 1); sin = (struct sockaddr_in *) (addr + 1);
sin->sin_family = AF_INET; 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; sin->sin_port = 0;
memset(sin->sin_zero, 0, sizeof(sin->sin_zero)); 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, addr = (struct sadb_address*) skb_put(skb,
sizeof(struct sadb_address)+sockaddr_size); sizeof(struct sadb_address)+sockaddr_size);
addr->sadb_address_len = addr->sadb_address_len =
...@@ -782,7 +782,7 @@ static struct sk_buff * pfkey_xfrm_state2msg(struct xfrm_state *x, int add_keys, ...@@ -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 = (struct sockaddr_in *) (addr + 1);
sin->sin_family = AF_INET; 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; sin->sin_port = x->sel.sport;
memset(sin->sin_zero, 0, sizeof(sin->sin_zero)); memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
} }
...@@ -1017,20 +1017,7 @@ static struct xfrm_state * pfkey_msg2xfrm_state(struct sadb_msg *hdr, ...@@ -1017,20 +1017,7 @@ static struct xfrm_state * pfkey_msg2xfrm_state(struct sadb_msg *hdr,
x->sel.prefixlen_s = addr->sadb_address_prefixlen; x->sel.prefixlen_s = addr->sadb_address_prefixlen;
} }
switch (x->props.family) { x->type = xfrm_get_type(proto, 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;
}
if (x->type == NULL) if (x->type == NULL)
goto out; goto out;
if (x->type->init_state(x, NULL)) 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 ...@@ -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_x_sa2 *sa2;
struct sadb_address *saddr, *daddr; struct sadb_address *saddr, *daddr;
struct sadb_msg *out_hdr; struct sadb_msg *out_hdr;
struct xfrm_state *x; struct xfrm_state *x = NULL;
u8 mode; u8 mode;
u16 reqid; u16 reqid;
u8 proto; u8 proto;
unsigned short family;
xfrm_address_t *xsaddr = NULL, *xdaddr = NULL;
if (!present_and_same_family(ext_hdrs[SADB_EXT_ADDRESS_SRC-1], if (!present_and_same_family(ext_hdrs[SADB_EXT_ADDRESS_SRC-1],
ext_hdrs[SADB_EXT_ADDRESS_DST-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 ...@@ -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]; saddr = ext_hdrs[SADB_EXT_ADDRESS_SRC-1];
daddr = ext_hdrs[SADB_EXT_ADDRESS_DST-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: case AF_INET:
x = xfrm_find_acq(mode, reqid, proto, xdaddr = (xfrm_address_t *)&((struct sockaddr_in *)(daddr + 1))->sin_addr.s_addr;
((struct sockaddr_in *)(daddr + 1))->sin_addr.s_addr, xsaddr = (xfrm_address_t *)&((struct sockaddr_in *)(saddr + 1))->sin_addr.s_addr;
((struct sockaddr_in *)(saddr + 1))->sin_addr.s_addr, 1);
break; break;
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
case AF_INET6: case AF_INET6:
x = xfrm6_find_acq(mode, reqid, proto, xdaddr = (xfrm_address_t *)&((struct sockaddr_in6 *)(daddr + 1))->sin6_addr;
&((struct sockaddr_in6 *)(daddr + 1))->sin6_addr, xsaddr = (xfrm_address_t *)&((struct sockaddr_in6 *)(saddr + 1))->sin6_addr;
&((struct sockaddr_in6 *)(saddr + 1))->sin6_addr, 1);
break; break;
#endif #endif
default:
x = NULL;
break;
} }
if (xdaddr)
x = xfrm_find_acq(mode, reqid, proto, xdaddr, xsaddr, 1, family);
if (x == NULL) if (x == NULL)
return -ENOENT; return -ENOENT;
...@@ -1188,23 +1175,9 @@ static int pfkey_add(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hdr, ...@@ -1188,23 +1175,9 @@ static int pfkey_add(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hdr,
/* XXX there is race condition */ /* XXX there is race condition */
x1 = pfkey_xfrm_state_lookup(hdr, ext_hdrs); x1 = pfkey_xfrm_state_lookup(hdr, ext_hdrs);
if (!x1) { if (!x1) {
switch (x->props.family) { x1 = xfrm_find_acq(x->props.mode, x->props.reqid, x->id.proto,
case AF_INET: &x->id.daddr,
x1 = xfrm_find_acq(x->props.mode, x->props.reqid, x->id.proto, &x->props.saddr, 0, x->props.family);
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;
}
if (x1 && x1->id.spi != x->id.spi && x1->id.spi) { if (x1 && x1->id.spi != x->id.spi && x1->id.spi) {
xfrm_state_put(x1); xfrm_state_put(x1);
x1 = NULL; x1 = NULL;
...@@ -1543,11 +1516,11 @@ parse_ipsecrequest(struct xfrm_policy *xp, struct sadb_x_ipsecrequest *rq) ...@@ -1543,11 +1516,11 @@ parse_ipsecrequest(struct xfrm_policy *xp, struct sadb_x_ipsecrequest *rq)
sin = (void*)(rq+1); sin = (void*)(rq+1);
if (sin->sin_family != AF_INET) if (sin->sin_family != AF_INET)
return -EINVAL; return -EINVAL;
t->saddr.xfrm4_addr = sin->sin_addr.s_addr; t->saddr.a4 = sin->sin_addr.s_addr;
sin++; sin++;
if (sin->sin_family != AF_INET) if (sin->sin_family != AF_INET)
return -EINVAL; return -EINVAL;
t->id.daddr.xfrm4_addr = sin->sin_addr.s_addr; t->id.daddr.a4 = sin->sin_addr.s_addr;
break; break;
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
case AF_INET6: case AF_INET6:
...@@ -1654,7 +1627,7 @@ static void pfkey_xfrm_policy2msg(struct sk_buff *skb, struct xfrm_policy *xp, i ...@@ -1654,7 +1627,7 @@ static void pfkey_xfrm_policy2msg(struct sk_buff *skb, struct xfrm_policy *xp, i
if (xp->family == AF_INET) { if (xp->family == AF_INET) {
sin = (struct sockaddr_in *) (addr + 1); sin = (struct sockaddr_in *) (addr + 1);
sin->sin_family = AF_INET; 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; sin->sin_port = xp->selector.sport;
memset(sin->sin_zero, 0, sizeof(sin->sin_zero)); 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 ...@@ -1685,7 +1658,7 @@ static void pfkey_xfrm_policy2msg(struct sk_buff *skb, struct xfrm_policy *xp, i
if (xp->family == AF_INET) { if (xp->family == AF_INET) {
sin = (struct sockaddr_in *) (addr + 1); sin = (struct sockaddr_in *) (addr + 1);
sin->sin_family = AF_INET; 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; sin->sin_port = xp->selector.dport;
memset(sin->sin_zero, 0, sizeof(sin->sin_zero)); 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 ...@@ -1773,12 +1746,12 @@ static void pfkey_xfrm_policy2msg(struct sk_buff *skb, struct xfrm_policy *xp, i
case AF_INET: case AF_INET:
sin = (void*)(rq+1); sin = (void*)(rq+1);
sin->sin_family = AF_INET; 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; sin->sin_port = 0;
memset(sin->sin_zero, 0, sizeof(sin->sin_zero)); memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
sin++; sin++;
sin->sin_family = AF_INET; 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; sin->sin_port = 0;
memset(sin->sin_zero, 0, sizeof(sin->sin_zero)); memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
break; break;
...@@ -2362,7 +2335,7 @@ static int pfkey_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *t, struct ...@@ -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 = (struct sockaddr_in *) (addr + 1);
sin->sin_family = AF_INET; 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; sin->sin_port = 0;
memset(sin->sin_zero, 0, sizeof(sin->sin_zero)); 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 ...@@ -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 = (struct sockaddr_in *) (addr + 1);
sin->sin_family = AF_INET; 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; sin->sin_port = 0;
memset(sin->sin_zero, 0, sizeof(sin->sin_zero)); memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
} }
......
...@@ -303,14 +303,20 @@ EXPORT_SYMBOL(__xfrm_policy_check); ...@@ -303,14 +303,20 @@ EXPORT_SYMBOL(__xfrm_policy_check);
EXPORT_SYMBOL(__xfrm_route_forward); EXPORT_SYMBOL(__xfrm_route_forward);
EXPORT_SYMBOL(xfrm_state_alloc); EXPORT_SYMBOL(xfrm_state_alloc);
EXPORT_SYMBOL(__xfrm_state_destroy); EXPORT_SYMBOL(__xfrm_state_destroy);
EXPORT_SYMBOL(xfrm4_state_find); EXPORT_SYMBOL(xfrm_state_find);
EXPORT_SYMBOL(xfrm_state_insert); EXPORT_SYMBOL(xfrm_state_insert);
EXPORT_SYMBOL(xfrm_state_check_expire); EXPORT_SYMBOL(xfrm_state_check_expire);
EXPORT_SYMBOL(xfrm_state_check_space); 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_check);
EXPORT_SYMBOL(xfrm_replay_advance); EXPORT_SYMBOL(xfrm_replay_advance);
EXPORT_SYMBOL(xfrm_check_selectors); EXPORT_SYMBOL(xfrm_check_selectors);
EXPORT_SYMBOL(__secpath_destroy);
EXPORT_SYMBOL(xfrm_parse_spi);
EXPORT_SYMBOL(xfrm4_rcv); EXPORT_SYMBOL(xfrm4_rcv);
EXPORT_SYMBOL(xfrm_register_type); EXPORT_SYMBOL(xfrm_register_type);
EXPORT_SYMBOL(xfrm_unregister_type); EXPORT_SYMBOL(xfrm_unregister_type);
...@@ -331,18 +337,11 @@ EXPORT_SYMBOL(xfrm_policy_walk); ...@@ -331,18 +337,11 @@ EXPORT_SYMBOL(xfrm_policy_walk);
EXPORT_SYMBOL(xfrm_policy_flush); EXPORT_SYMBOL(xfrm_policy_flush);
EXPORT_SYMBOL(xfrm_policy_byid); EXPORT_SYMBOL(xfrm_policy_byid);
EXPORT_SYMBOL(xfrm_policy_list); EXPORT_SYMBOL(xfrm_policy_list);
EXPORT_SYMBOL(xfrm_dst_lookup_register); EXPORT_SYMBOL(xfrm_dst_lookup);
EXPORT_SYMBOL(xfrm_dst_lookup_unregister); EXPORT_SYMBOL(xfrm_policy_register_afinfo);
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) EXPORT_SYMBOL(xfrm_policy_unregister_afinfo);
EXPORT_SYMBOL(xfrm6_state_find); EXPORT_SYMBOL(xfrm_policy_get_afinfo);
EXPORT_SYMBOL(xfrm6_rcv); EXPORT_SYMBOL(xfrm_policy_put_afinfo);
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_GPL(xfrm_probe_algs); EXPORT_SYMBOL_GPL(xfrm_probe_algs);
EXPORT_SYMBOL_GPL(xfrm_count_auth_supported); EXPORT_SYMBOL_GPL(xfrm_count_auth_supported);
......
...@@ -110,6 +110,7 @@ int sctp_rcv(struct sk_buff *skb) ...@@ -110,6 +110,7 @@ int sctp_rcv(struct sk_buff *skb)
struct sctphdr *sh; struct sctphdr *sh;
union sctp_addr src; union sctp_addr src;
union sctp_addr dest; union sctp_addr dest;
int family;
struct sctp_af *af; struct sctp_af *af;
int ret = 0; int ret = 0;
...@@ -129,7 +130,8 @@ int sctp_rcv(struct sk_buff *skb) ...@@ -129,7 +130,8 @@ int sctp_rcv(struct sk_buff *skb)
skb_pull(skb, sizeof(struct sctphdr)); 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)) if (unlikely(!af))
goto bad_packet; goto bad_packet;
...@@ -173,7 +175,7 @@ int sctp_rcv(struct sk_buff *skb) ...@@ -173,7 +175,7 @@ int sctp_rcv(struct sk_buff *skb)
rcvr = asoc ? &asoc->base : &ep->base; rcvr = asoc ? &asoc->base : &ep->base;
sk = rcvr->sk; 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; goto discard_release;
ret = sk_filter(sk, skb, 1); 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