]> git.hungrycats.org Git - linux/commitdiff
[IPSEC]: Split up XFRM Subsystem.
authorHideaki Yoshifuji <yoshfuji@linux-ipv6.org>
Sat, 22 Mar 2003 18:21:45 +0000 (10:21 -0800)
committerDavid S. Miller <davem@nuts.ninka.net>
Sat, 22 Mar 2003 18:21:45 +0000 (10:21 -0800)
29 files changed:
include/linux/xfrm.h
include/net/xfrm.h
net/ipv4/Makefile
net/ipv4/ah.c
net/ipv4/esp.c
net/ipv4/ip_forward.c
net/ipv4/ip_input.c
net/ipv4/raw.c
net/ipv4/route.c
net/ipv4/tcp_ipv4.c
net/ipv4/udp.c
net/ipv4/xfrm4_input.c [new file with mode: 0644]
net/ipv4/xfrm4_policy.c [new file with mode: 0644]
net/ipv4/xfrm4_state.c [new file with mode: 0644]
net/ipv4/xfrm_input.c
net/ipv4/xfrm_policy.c
net/ipv4/xfrm_state.c
net/ipv4/xfrm_user.c
net/ipv6/Makefile
net/ipv6/ah6.c
net/ipv6/esp6.c
net/ipv6/ipv6_syms.c
net/ipv6/route.c
net/ipv6/xfrm6_input.c [new file with mode: 0644]
net/ipv6/xfrm6_policy.c [new file with mode: 0644]
net/ipv6/xfrm6_state.c [new file with mode: 0644]
net/key/af_key.c
net/netsyms.c
net/sctp/input.c

index 7458d1f4947270ebe8ab7b14b5166c623b2ce536..9c39534d0267c52138754642e922e2b059546b48 100644 (file)
  */
 typedef union
 {
-       struct {
-               __u32   addr;
-               __u32   mask;   /* Use unused bits to cache mask. */
-       } a4;
-#define xfrm4_addr a4.addr
-#define xfrm4_mask a4.mask
+       __u32           a4;
        __u32           a6[4];
 } xfrm_address_t;
 
index d6ffb9aed327f883aec98b228aea103e58874304..572d9ee31f0a680a818157d73f052158bde47f66 100644 (file)
@@ -13,6 +13,7 @@
 #include <net/sock.h>
 #include <net/dst.h>
 #include <net/route.h>
+#include <net/ipv6.h>
 #include <net/ip6_fib.h>
 
 #define XFRM_ALIGN8(len)       (((len) + 7) & ~7)
@@ -145,6 +146,51 @@ enum {
        XFRM_STATE_DEAD
 };
 
+struct xfrm_type;
+struct xfrm_dst;
+struct xfrm_policy_afinfo {
+       unsigned short          family;
+       rwlock_t                lock;
+       struct xfrm_type_map    *type_map;
+       struct dst_ops          *dst_ops;
+       void                    (*garbage_collect)(void);
+       int                     (*dst_lookup)(struct xfrm_dst **dst, struct flowi *fl);
+       struct dst_entry        *(*find_bundle)(struct flowi *fl, struct rtable *rt, struct xfrm_policy *policy);
+       int                     (*bundle_create)(struct xfrm_policy *policy, 
+                                                struct xfrm_state **xfrm, 
+                                                int nx,
+                                                struct flowi *fl, 
+                                                struct dst_entry **dst_p);
+       void                    (*decode_session)(struct sk_buff *skb,
+                                                 struct flowi *fl);
+};
+
+extern int xfrm_policy_register_afinfo(struct xfrm_policy_afinfo *afinfo);
+extern int xfrm_policy_unregister_afinfo(struct xfrm_policy_afinfo *afinfo);
+extern struct xfrm_policy_afinfo *xfrm_policy_get_afinfo(unsigned short family);
+extern void xfrm_policy_put_afinfo(struct xfrm_policy_afinfo *afinfo);
+
+#define XFRM_ACQ_EXPIRES       30
+
+struct xfrm_tmpl;
+struct xfrm_state_afinfo {
+       unsigned short          family;
+       rwlock_t                lock;
+       struct list_head        *state_bydst;
+       struct list_head        *state_byspi;
+       void                    (*init_tempsel)(struct xfrm_state *x, struct flowi *fl,
+                                               struct xfrm_tmpl *tmpl,
+                                               xfrm_address_t *daddr, xfrm_address_t *saddr);
+       struct xfrm_state       *(*state_lookup)(xfrm_address_t *daddr, u32 spi, u8 proto);
+       struct xfrm_state       *(*find_acq)(u8 mode, u16 reqid, u8 proto, 
+                                            xfrm_address_t *daddr, xfrm_address_t *saddr, 
+                                            int create);
+};
+
+extern int xfrm_state_register_afinfo(struct xfrm_state_afinfo *afinfo);
+extern int xfrm_state_unregister_afinfo(struct xfrm_state_afinfo *afinfo);
+extern struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned short family);
+extern void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo);
 
 struct xfrm_type
 {
@@ -160,9 +206,14 @@ struct xfrm_type
        u32                     (*get_max_size)(struct xfrm_state *, int size);
 };
 
-extern int xfrm_register_type(struct xfrm_type *type);
-extern int xfrm_unregister_type(struct xfrm_type *type);
-extern struct xfrm_type *xfrm_get_type(u8 proto);
+struct xfrm_type_map {
+       rwlock_t                lock;
+       struct xfrm_type        *map[256];
+};
+
+extern int xfrm_register_type(struct xfrm_type *type, unsigned short family);
+extern int xfrm_unregister_type(struct xfrm_type *type, unsigned short family);
+extern struct xfrm_type *xfrm_get_type(u8 proto, unsigned short family);
 extern void xfrm_put_type(struct xfrm_type *type);
 
 struct xfrm_tmpl
@@ -233,6 +284,47 @@ extern int xfrm_register_km(struct xfrm_mgr *km);
 extern int xfrm_unregister_km(struct xfrm_mgr *km);
 
 
+#define XFRM_FLOWCACHE_HASH_SIZE       1024
+
+static inline u32 __flow_hash4(struct flowi *fl)
+{
+       u32 hash = fl->fl4_src ^ fl->uli_u.ports.sport;
+
+       hash = ((hash & 0xF0F0F0F0) >> 4) | ((hash & 0x0F0F0F0F) << 4);
+
+       hash ^= fl->fl4_dst ^ fl->uli_u.ports.dport;
+       hash ^= (hash >> 10);
+       hash ^= (hash >> 20);
+       return hash & (XFRM_FLOWCACHE_HASH_SIZE-1);
+}
+
+static inline u32 __flow_hash6(struct flowi *fl)
+{
+       u32 hash = fl->fl6_src->s6_addr32[2] ^
+                  fl->fl6_src->s6_addr32[3] ^ 
+                  fl->uli_u.ports.sport;
+
+       hash = ((hash & 0xF0F0F0F0) >> 4) | ((hash & 0x0F0F0F0F) << 4);
+
+       hash ^= fl->fl6_dst->s6_addr32[2] ^
+               fl->fl6_dst->s6_addr32[3] ^ 
+               fl->uli_u.ports.dport;
+       hash ^= (hash >> 10);
+       hash ^= (hash >> 20);
+       return hash & (XFRM_FLOWCACHE_HASH_SIZE-1);
+}
+
+static inline u32 flow_hash(struct flowi *fl, unsigned short family)
+{
+       switch (family) {
+       case AF_INET:
+               return __flow_hash4(fl);
+       case AF_INET6:
+               return __flow_hash6(fl);
+       }
+       return 0;       /*XXX*/
+}
+
 extern struct xfrm_policy *xfrm_policy_list[XFRM_POLICY_MAX*2];
 
 static inline void xfrm_pol_hold(struct xfrm_policy *policy)
@@ -249,6 +341,68 @@ static inline void xfrm_pol_put(struct xfrm_policy *policy)
                __xfrm_policy_destroy(policy);
 }
 
+#define XFRM_DST_HSIZE         1024
+
+static __inline__
+unsigned __xfrm4_dst_hash(xfrm_address_t *addr)
+{
+       unsigned h;
+       h = ntohl(addr->a4);
+       h = (h ^ (h>>16)) % XFRM_DST_HSIZE;
+       return h;
+}
+
+static __inline__
+unsigned __xfrm6_dst_hash(xfrm_address_t *addr)
+{
+       unsigned h;
+       h = ntohl(addr->a6[2]^addr->a6[3]);
+       h = (h ^ (h>>16)) % XFRM_DST_HSIZE;
+       return h;
+}
+
+static __inline__
+unsigned xfrm_dst_hash(xfrm_address_t *addr, unsigned short family)
+{
+       switch (family) {
+       case AF_INET:
+               return __xfrm4_dst_hash(addr);
+       case AF_INET6:
+               return __xfrm6_dst_hash(addr);
+       }
+       return 0;
+}
+
+static __inline__
+unsigned __xfrm4_spi_hash(xfrm_address_t *addr, u32 spi, u8 proto)
+{
+       unsigned h;
+       h = ntohl(addr->a4^spi^proto);
+       h = (h ^ (h>>10) ^ (h>>20)) % XFRM_DST_HSIZE;
+       return h;
+}
+
+static __inline__
+unsigned __xfrm6_spi_hash(xfrm_address_t *addr, u32 spi, u8 proto)
+{
+       unsigned h;
+       h = ntohl(addr->a6[2]^addr->a6[3]^spi^proto);
+       h = (h ^ (h>>10) ^ (h>>20)) % XFRM_DST_HSIZE;
+       return h;
+}
+
+static __inline__
+unsigned xfrm_spi_hash(xfrm_address_t *addr, u32 spi, u8 proto, unsigned short family)
+{
+       switch (family) {
+       case AF_INET:
+               return __xfrm4_spi_hash(addr, spi, proto);
+       case AF_INET6:
+               return __xfrm6_spi_hash(addr, spi, proto);
+       }
+       return 0;       /*XXX*/
+}
+
 extern void __xfrm_state_destroy(struct xfrm_state *);
 
 static inline void xfrm_state_put(struct xfrm_state *x)
@@ -262,15 +416,65 @@ static inline void xfrm_state_hold(struct xfrm_state *x)
        atomic_inc(&x->refcnt);
 }
 
+static __inline__ int addr_match(void *token1, void *token2, int prefixlen)
+{
+       __u32 *a1 = token1;
+       __u32 *a2 = token2;
+       int pdw;
+       int pbi;
+
+       pdw = prefixlen >> 5;     /* num of whole __u32 in prefix */
+       pbi = prefixlen &  0x1f;  /* num of bits in incomplete u32 in prefix */
+
+       if (pdw)
+               if (memcmp(a1, a2, pdw << 2))
+                       return 0;
+
+       if (pbi) {
+               __u32 mask;
+
+               mask = htonl((0xffffffff) << (32 - pbi));
+
+               if ((a1[pdw] ^ a2[pdw]) & mask)
+                       return 0;
+       }
+
+       return 1;
+}
+
+static inline int
+__xfrm4_selector_match(struct xfrm_selector *sel, struct flowi *fl)
+{
+       return  addr_match(&fl->fl4_dst, &sel->daddr, sel->prefixlen_d) &&
+               addr_match(&fl->fl4_src, &sel->saddr, sel->prefixlen_s) &&
+               !((fl->uli_u.ports.dport^sel->dport)&sel->dport_mask) &&
+               !((fl->uli_u.ports.sport^sel->sport)&sel->sport_mask) &&
+               (fl->proto == sel->proto || !sel->proto) &&
+               (fl->oif == sel->ifindex || !sel->ifindex);
+}
+
 static inline int
-xfrm4_selector_match(struct xfrm_selector *sel, struct flowi *fl)
+__xfrm6_selector_match(struct xfrm_selector *sel, struct flowi *fl)
 {
-       return  !((fl->fl4_dst^sel->daddr.xfrm4_addr)&sel->daddr.xfrm4_mask) &&
+       return  addr_match(fl->fl6_dst, &sel->daddr, sel->prefixlen_d) &&
+               addr_match(fl->fl6_src, &sel->saddr, sel->prefixlen_s) &&
                !((fl->uli_u.ports.dport^sel->dport)&sel->dport_mask) &&
                !((fl->uli_u.ports.sport^sel->sport)&sel->sport_mask) &&
                (fl->proto == sel->proto || !sel->proto) &&
-               (fl->oif == sel->ifindex || !sel->ifindex) &&
-               !((fl->fl4_src^sel->saddr.xfrm4_addr)&sel->saddr.xfrm4_mask);
+               (fl->oif == sel->ifindex || !sel->ifindex);
+}
+
+static inline int
+xfrm_selector_match(struct xfrm_selector *sel, struct flowi *fl,
+                   unsigned short family)
+{
+       switch (family) {
+       case AF_INET:
+               return __xfrm4_selector_match(sel, fl);
+       case AF_INET6:
+               return __xfrm6_selector_match(sel, fl);
+       }
+       return 0;
 }
 
 /* A struct encoding bundle of transformations to apply to some set of flow.
@@ -296,6 +500,7 @@ struct xfrm_dst
 
 struct sec_path
 {
+       kmem_cache_t            *pool;
        atomic_t                refcnt;
        int                     len;
        struct xfrm_state       *xvec[XFRM_MAX_DEPTH];
@@ -317,42 +522,73 @@ secpath_put(struct sec_path *sp)
        if (sp && atomic_dec_and_test(&sp->refcnt))
                __secpath_destroy(sp);
 }
+
+static inline int
+__xfrm4_state_addr_cmp(struct xfrm_tmpl *tmpl, struct xfrm_state *x)
+{
+       return  (tmpl->saddr.a4 &&
+                tmpl->saddr.a4 != x->props.saddr.a4);
+}
+
+static inline int
+__xfrm6_state_addr_cmp(struct xfrm_tmpl *tmpl, struct xfrm_state *x)
+{
+       return  (!ipv6_addr_any((struct in6_addr*)&tmpl->saddr) &&
+                ipv6_addr_cmp((struct in6_addr *)&tmpl->saddr, (struct in6_addr*)&x->props.saddr));
+}
+
+static inline int
+xfrm_state_addr_cmp(struct xfrm_tmpl *tmpl, struct xfrm_state *x, unsigned short family)
+{
+       switch (family) {
+       case AF_INET:
+               return __xfrm4_state_addr_cmp(tmpl, x);
+       case AF_INET6:
+               return __xfrm6_state_addr_cmp(tmpl, x);
+       }
+       return !0;
+}
+
 extern int __xfrm_policy_check(struct sock *, int dir, struct sk_buff *skb, unsigned short family);
 
-static inline int xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb)
+static inline int xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb, unsigned short family)
 {
        if (sk && sk->policy[XFRM_POLICY_IN])
-               return __xfrm_policy_check(sk, dir, skb, AF_INET);
+               return __xfrm_policy_check(sk, dir, skb, family);
                
        return  !xfrm_policy_list[dir] ||
                (skb->dst->flags & DST_NOPOLICY) ||
-               __xfrm_policy_check(sk, dir, skb, AF_INET);
+               __xfrm_policy_check(sk, dir, skb, family);
+}
+
+static inline int xfrm4_policy_check(struct sock *sk, int dir, struct sk_buff *skb)
+{
+       return xfrm_policy_check(sk, dir, skb, AF_INET);
 }
 
 static inline int xfrm6_policy_check(struct sock *sk, int dir, struct sk_buff *skb)
 {
-       if (sk && sk->policy[XFRM_POLICY_IN])
-               return __xfrm_policy_check(sk, dir, skb, AF_INET6);
-               
-       return  !xfrm_policy_list[dir] ||
-               (skb->dst->flags & DST_NOPOLICY) ||
-               __xfrm_policy_check(sk, dir, skb, AF_INET6);
+       return xfrm_policy_check(sk, dir, skb, AF_INET6);
 }
 
+
 extern int __xfrm_route_forward(struct sk_buff *skb, unsigned short family);
 
-static inline int xfrm_route_forward(struct sk_buff *skb)
+static inline int xfrm_route_forward(struct sk_buff *skb, unsigned short family)
 {
        return  !xfrm_policy_list[XFRM_POLICY_OUT] ||
                (skb->dst->flags & DST_NOXFRM) ||
-               __xfrm_route_forward(skb, AF_INET);
+               __xfrm_route_forward(skb, family);
+}
+
+static inline int xfrm4_route_forward(struct sk_buff *skb)
+{
+       return xfrm_route_forward(skb, AF_INET);
 }
 
 static inline int xfrm6_route_forward(struct sk_buff *skb)
 {
-       return  !xfrm_policy_list[XFRM_POLICY_OUT] ||
-               (skb->dst->flags & DST_NOXFRM) ||
-               __xfrm_route_forward(skb, AF_INET6);
+       return xfrm_route_forward(skb, AF_INET6);
 }
 
 extern int __xfrm_sk_clone_policy(struct sock *sk);
@@ -378,6 +614,66 @@ static inline void xfrm_sk_free_policy(struct sock *sk)
        }
 }
 
+static __inline__
+xfrm_address_t *xfrm_flowi_daddr(struct flowi *fl, unsigned short family)
+{
+       switch (family){
+       case AF_INET:
+               return (xfrm_address_t *)&fl->fl4_dst;
+       case AF_INET6:
+               return (xfrm_address_t *)fl->fl6_dst;
+       }
+       return NULL;
+}
+
+static __inline__
+xfrm_address_t *xfrm_flowi_saddr(struct flowi *fl, unsigned short family)
+{
+       switch (family){
+       case AF_INET:
+               return (xfrm_address_t *)&fl->fl4_src;
+       case AF_INET6:
+               return (xfrm_address_t *)fl->fl6_src;
+       }
+       return NULL;
+}
+
+static __inline__ int
+__xfrm4_state_addr_check(struct xfrm_state *x,
+                        xfrm_address_t *daddr, xfrm_address_t *saddr)
+{
+       if (daddr->a4 == x->id.daddr.a4 &&
+           (saddr->a4 == x->props.saddr.a4 || !saddr->a4 || !x->props.saddr.a4))
+               return 1;
+       return 0;
+}
+
+static __inline__ int
+__xfrm6_state_addr_check(struct xfrm_state *x,
+                        xfrm_address_t *daddr, xfrm_address_t *saddr)
+{
+       if (!ipv6_addr_cmp((struct in6_addr *)daddr, (struct in6_addr *)&x->id.daddr) &&
+           (!ipv6_addr_cmp((struct in6_addr *)saddr, (struct in6_addr *)&x->props.saddr)|| 
+            ipv6_addr_any((struct in6_addr *)saddr) || 
+            ipv6_addr_any((struct in6_addr *)&x->props.saddr)))
+               return 1;
+       return 0;
+}
+
+static __inline__ int
+xfrm_state_addr_check(struct xfrm_state *x,
+                     xfrm_address_t *daddr, xfrm_address_t *saddr,
+                     unsigned short family)
+{
+       switch (family) {
+       case AF_INET:
+               return __xfrm4_state_addr_check(x, daddr, saddr);
+       case AF_INET6:
+               return __xfrm6_state_addr_check(x, daddr, saddr);
+       }
+       return 0;
+}
+
 /*
  * xfrm algorithm information
  */
@@ -406,20 +702,27 @@ struct xfrm_algo_desc {
        struct sadb_alg desc;
 };
 
+extern void xfrm_init(void);
+extern void xfrm4_init(void);
+extern void xfrm4_fini(void);
+extern void xfrm6_init(void);
+extern void xfrm6_fini(void);
 extern void xfrm_state_init(void);
-extern void xfrm_input_init(void);
+extern void xfrm4_state_init(void);
+extern void xfrm4_state_fini(void);
+extern void xfrm6_state_init(void);
+extern void xfrm6_state_fini(void);
+
 extern int xfrm_state_walk(u8 proto, int (*func)(struct xfrm_state *, int, void*), void *);
 extern struct xfrm_state *xfrm_state_alloc(void);
-extern struct xfrm_state *xfrm4_state_find(u32 daddr, u32 saddr, struct flowi *fl, struct xfrm_tmpl *tmpl,
-                                          struct xfrm_policy *pol, int *err);
-extern struct xfrm_state *xfrm6_state_find(struct in6_addr *daddr, struct in6_addr *saddr,
-                                          struct flowi *fl, struct xfrm_tmpl *tmpl,
-                                          struct xfrm_policy *pol, int *err);
+extern struct xfrm_state *xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr, 
+                                         struct flowi *fl, struct xfrm_tmpl *tmpl,
+                                         struct xfrm_policy *pol, int *err,
+                                         unsigned short family);
 extern int xfrm_state_check_expire(struct xfrm_state *x);
 extern void xfrm_state_insert(struct xfrm_state *x);
 extern int xfrm_state_check_space(struct xfrm_state *x, struct sk_buff *skb);
-extern struct xfrm_state *xfrm4_state_lookup(u32 daddr, u32 spi, u8 proto);
-extern struct xfrm_state *xfrm6_state_lookup(struct in6_addr *daddr, u32 spi, u8 proto);
+extern struct xfrm_state *xfrm_state_lookup(xfrm_address_t *daddr, u32 spi, u8 proto, unsigned short family);
 extern struct xfrm_state *xfrm_find_acq_byseq(u32 seq);
 extern void xfrm_state_delete(struct xfrm_state *x);
 extern void xfrm_state_flush(u8 proto);
@@ -431,6 +734,9 @@ extern int xfrm6_rcv(struct sk_buff *skb);
 extern int xfrm6_clear_mutable_options(struct sk_buff *skb, u16 *nh_offset, int dir);
 extern int xfrm_user_policy(struct sock *sk, int optname, u8 *optval, int optlen);
 
+void xfrm_policy_init(void);
+void xfrm4_policy_init(void);
+void xfrm6_policy_init(void);
 struct xfrm_policy *xfrm_policy_alloc(int gfp);
 extern int xfrm_policy_walk(int (*func)(struct xfrm_policy *, int, int, void*), void *);
 struct xfrm_policy *xfrm_policy_lookup(int dir, struct flowi *fl, unsigned short family);
@@ -439,20 +745,25 @@ struct xfrm_policy *xfrm_policy_delete(int dir, struct xfrm_selector *sel);
 struct xfrm_policy *xfrm_policy_byid(int dir, u32 id, int delete);
 void xfrm_policy_flush(void);
 void xfrm_alloc_spi(struct xfrm_state *x, u32 minspi, u32 maxspi);
-struct xfrm_state * xfrm_find_acq(u8 mode, u16 reqid, u8 proto, u32 daddr, u32 saddr, int create);
-struct xfrm_state * xfrm6_find_acq(u8 mode, u16 reqid, u8 proto, struct in6_addr *daddr,
-                                  struct in6_addr *saddr, int create);
+struct xfrm_state * xfrm_find_acq(u8 mode, u16 reqid, u8 proto, 
+                                 xfrm_address_t *daddr, xfrm_address_t *saddr, 
+                                 int create, unsigned short family);
 extern void xfrm_policy_flush(void);
 extern void xfrm_policy_kill(struct xfrm_policy *);
 extern int xfrm_sk_policy_insert(struct sock *sk, int dir, struct xfrm_policy *pol);
 extern struct xfrm_policy *xfrm_sk_policy_lookup(struct sock *sk, int dir, struct flowi *fl);
 extern int xfrm_flush_bundles(struct xfrm_state *x);
+extern int xfrm_dst_lookup(struct xfrm_dst **dst, struct flowi *fl, unsigned short family);
 
 extern wait_queue_head_t km_waitq;
 extern void km_warn_expired(struct xfrm_state *x);
 extern void km_expired(struct xfrm_state *x);
 extern int km_query(struct xfrm_state *x, struct xfrm_tmpl *, struct xfrm_policy *pol);
 
+extern void xfrm4_input_init(void);
+extern void xfrm6_input_init(void);
+extern int xfrm_parse_spi(struct sk_buff *skb, u8 nexthdr, u32 *spi, u32 *seq);
+
 extern void xfrm_probe_algs(void);
 extern int xfrm_count_auth_supported(void);
 extern int xfrm_count_enc_supported(void);
@@ -466,52 +777,7 @@ extern struct xfrm_algo_desc *xfrm_aalg_get_byname(char *name);
 extern struct xfrm_algo_desc *xfrm_ealg_get_byname(char *name);
 extern struct xfrm_algo_desc *xfrm_calg_get_byname(char *name);
 
-static __inline__ int addr_match(void *token1, void *token2, int prefixlen)
-{
-       __u32 *a1 = token1;
-       __u32 *a2 = token2;
-       int pdw;
-       int pbi;
-
-       pdw = prefixlen >> 5;     /* num of whole __u32 in prefix */
-       pbi = prefixlen &  0x1f;  /* num of bits in incomplete u32 in prefix */
-
-       if (pdw)
-               if (memcmp(a1, a2, pdw << 2))
-                       return 0;
-
-       if (pbi) {
-               __u32 mask;
-
-               mask = htonl((0xffffffff) << (32 - pbi));
-
-               if ((a1[pdw] ^ a2[pdw]) & mask)
-                       return 0;
-       }
-
-       return 1;
-}
-
-static inline int
-xfrm6_selector_match(struct xfrm_selector *sel, struct flowi *fl)
-{
-       return  addr_match(fl->fl6_dst, &sel->daddr, sel->prefixlen_d) &&
-               addr_match(fl->fl6_src, &sel->saddr, sel->prefixlen_s) &&
-               !((fl->uli_u.ports.dport^sel->dport)&sel->dport_mask) &&
-               !((fl->uli_u.ports.sport^sel->sport)&sel->sport_mask) &&
-               (fl->proto == sel->proto || !sel->proto) &&
-               (fl->oif == sel->ifindex || !sel->ifindex);
-}
-
-extern int xfrm6_register_type(struct xfrm_type *type);
-extern int xfrm6_unregister_type(struct xfrm_type *type);
-extern struct xfrm_type *xfrm6_get_type(u8 proto);
-
 struct crypto_tfm;
 typedef void (icv_update_fn_t)(struct crypto_tfm *, struct scatterlist *, unsigned int);
 
-typedef int (xfrm_dst_lookup_t)(struct xfrm_dst **dst, struct flowi *fl);
-int xfrm_dst_lookup_register(xfrm_dst_lookup_t *dst_lookup, unsigned short family);
-void xfrm_dst_lookup_unregister(unsigned short family);
-
 #endif /* _NET_XFRM_H */
index 7ce979fcaae4551ff12b53e57f1244cd72177038..c4a400a34f73dd1081e98c2e6b9c9d79b022a646 100644 (file)
@@ -22,4 +22,4 @@ obj-$(CONFIG_IP_PNP) += ipconfig.o
 obj-$(CONFIG_NETFILTER)        += netfilter/
 obj-$(CONFIG_XFRM_USER) += xfrm_user.o
 
-obj-y += xfrm_policy.o xfrm_state.o xfrm_input.o xfrm_algo.o
+obj-y += xfrm_policy.o xfrm4_policy.o xfrm_state.o xfrm4_state.o xfrm_input.o xfrm4_input.o xfrm_algo.o
index efb9d1d8114ab050e5ab71b2e5d30028f0b3c42a..e7404e9ff2d7e25a3f63f0b6ec1941d6c31068b7 100644 (file)
@@ -92,8 +92,8 @@ static int ah_output(struct sk_buff *skb)
                top_iph->ttl = 0;
                top_iph->protocol = IPPROTO_AH;
                top_iph->check = 0;
-               top_iph->saddr = x->props.saddr.xfrm4_addr;
-               top_iph->daddr = x->id.daddr.xfrm4_addr;
+               top_iph->saddr = x->props.saddr.a4;
+               top_iph->daddr = x->id.daddr.a4;
                ah = (struct ip_auth_hdr*)(top_iph+1);
                ah->nexthdr = IPPROTO_IPIP;
        } else {
@@ -232,7 +232,7 @@ void ah4_err(struct sk_buff *skb, u32 info)
            skb->h.icmph->code != ICMP_FRAG_NEEDED)
                return;
 
-       x = xfrm4_state_lookup(iph->daddr, ah->spi, IPPROTO_AH);
+       x = xfrm_state_lookup((xfrm_address_t *)&iph->daddr, ah->spi, IPPROTO_AH, AF_INET);
        if (!x)
                return;
        printk(KERN_DEBUG "pmtu discvovery on SA AH/%08x/%08x\n",
@@ -338,13 +338,13 @@ static struct inet_protocol ah4_protocol = {
 static int __init ah4_init(void)
 {
        SET_MODULE_OWNER(&ah_type);
-       if (xfrm_register_type(&ah_type) < 0) {
+       if (xfrm_register_type(&ah_type, AF_INET) < 0) {
                printk(KERN_INFO "ip ah init: can't add xfrm type\n");
                return -EAGAIN;
        }
        if (inet_add_protocol(&ah4_protocol, IPPROTO_AH) < 0) {
                printk(KERN_INFO "ip ah init: can't add protocol\n");
-               xfrm_unregister_type(&ah_type);
+               xfrm_unregister_type(&ah_type, AF_INET);
                return -EAGAIN;
        }
        return 0;
@@ -354,7 +354,7 @@ static void __exit ah4_fini(void)
 {
        if (inet_del_protocol(&ah4_protocol, IPPROTO_AH) < 0)
                printk(KERN_INFO "ip ah close: can't remove protocol\n");
-       if (xfrm_unregister_type(&ah_type) < 0)
+       if (xfrm_unregister_type(&ah_type, AF_INET) < 0)
                printk(KERN_INFO "ip ah close: can't remove xfrm type\n");
 }
 
index 58817c2374e878890ab76003be8bd38bceaa8545..5464dc0c94e9a2de412221d82f1011b8a5d58c26 100644 (file)
@@ -91,8 +91,8 @@ int esp_output(struct sk_buff *skb)
                top_iph->ttl = iph->ttl;        /* TTL disclosed */
                top_iph->protocol = IPPROTO_ESP;
                top_iph->check = 0;
-               top_iph->saddr = x->props.saddr.xfrm4_addr;
-               top_iph->daddr = x->id.daddr.xfrm4_addr;
+               top_iph->saddr = x->props.saddr.a4;
+               top_iph->daddr = x->id.daddr.a4;
                memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options));
        } else {
                esph = (struct ip_esp_hdr*)skb_push(skb, x->props.header_len);
@@ -276,7 +276,7 @@ void esp4_err(struct sk_buff *skb, u32 info)
            skb->h.icmph->code != ICMP_FRAG_NEEDED)
                return;
 
-       x = xfrm4_state_lookup(iph->daddr, esph->spi, IPPROTO_ESP);
+       x = xfrm_state_lookup((xfrm_address_t *)&iph->daddr, esph->spi, IPPROTO_ESP, AF_INET);
        if (!x)
                return;
        printk(KERN_DEBUG "pmtu discvovery on SA ESP/%08x/%08x\n",
@@ -405,13 +405,13 @@ static struct inet_protocol esp4_protocol = {
 int __init esp4_init(void)
 {
        SET_MODULE_OWNER(&esp_type);
-       if (xfrm_register_type(&esp_type) < 0) {
+       if (xfrm_register_type(&esp_type, AF_INET) < 0) {
                printk(KERN_INFO "ip esp init: can't add xfrm type\n");
                return -EAGAIN;
        }
        if (inet_add_protocol(&esp4_protocol, IPPROTO_ESP) < 0) {
                printk(KERN_INFO "ip esp init: can't add protocol\n");
-               xfrm_unregister_type(&esp_type);
+               xfrm_unregister_type(&esp_type, AF_INET);
                return -EAGAIN;
        }
        return 0;
@@ -421,7 +421,7 @@ static void __exit esp4_fini(void)
 {
        if (inet_del_protocol(&esp4_protocol, IPPROTO_ESP) < 0)
                printk(KERN_INFO "ip esp close: can't remove protocol\n");
-       if (xfrm_unregister_type(&esp_type) < 0)
+       if (xfrm_unregister_type(&esp_type, AF_INET) < 0)
                printk(KERN_INFO "ip esp close: can't remove xfrm type\n");
 }
 
index ec94140ff2cf8f671fba3bf3016917b1a174f332..f26d67ca69eb2dd0af854dfd11a070a5242ced3a 100644 (file)
@@ -60,7 +60,7 @@ int ip_forward(struct sk_buff *skb)
        struct rtable *rt;      /* Route we use */
        struct ip_options * opt = &(IPCB(skb)->opt);
 
-       if (!xfrm_policy_check(NULL, XFRM_POLICY_FWD, skb))
+       if (!xfrm4_policy_check(NULL, XFRM_POLICY_FWD, skb))
                goto drop;
 
        if (IPCB(skb)->opt.router_alert && ip_call_ra_chain(skb))
@@ -82,7 +82,7 @@ int ip_forward(struct sk_buff *skb)
        if (iph->ttl <= 1)
                 goto too_many_hops;
 
-       if (!xfrm_route_forward(skb))
+       if (!xfrm4_route_forward(skb))
                goto drop;
 
        iph = skb->nh.iph;
index d1079ca00743e5a51fda181a0baa55c1e2ed0ce2..6131e3babfca01578ca831883652b2e07bee8531 100644 (file)
@@ -236,7 +236,7 @@ static inline int ip_local_deliver_finish(struct sk_buff *skb)
                        int ret;
 
                        if (!ipprot->no_policy &&
-                           !xfrm_policy_check(NULL, XFRM_POLICY_IN, skb)) {
+                           !xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) {
                                kfree_skb(skb);
                                return 0;
                        }
@@ -248,7 +248,7 @@ static inline int ip_local_deliver_finish(struct sk_buff *skb)
                        IP_INC_STATS_BH(IpInDelivers);
                } else {
                        if (!raw_sk) {
-                               if (xfrm_policy_check(NULL, XFRM_POLICY_IN, skb)) {
+                               if (xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) {
                                        IP_INC_STATS_BH(IpInUnknownProtos);
                                        icmp_send(skb, ICMP_DEST_UNREACH,
                                                  ICMP_PROT_UNREACH, 0);
index 96b160ea59e776df67be80ca9625350fdef1fab9..1afbef9cd6a720544c0a2d69a012b54c01880a43 100644 (file)
@@ -251,7 +251,7 @@ static int raw_rcv_skb(struct sock * sk, struct sk_buff * skb)
 
 int raw_rcv(struct sock *sk, struct sk_buff *skb)
 {
-       if (!xfrm_policy_check(sk, XFRM_POLICY_IN, skb)) {
+       if (!xfrm4_policy_check(sk, XFRM_POLICY_IN, skb)) {
                kfree_skb(skb);
                return NET_RX_DROP;
        }
index bd3c55086671b17c7e96267e99aa4527c3a793e4..a915ce6d5889328d35180d33e88986ebe365f894 100644 (file)
@@ -2600,13 +2600,6 @@ static int ip_rt_acct_read(char *buffer, char **start, off_t offset,
 #endif /* CONFIG_PROC_FS */
 #endif /* CONFIG_NET_CLS_ROUTE */
 
-int xfrm_dst_lookup(struct xfrm_dst **dst, struct flowi *fl)
-{
-        int err = 0;
-        err = __ip_route_output_key((struct rtable**)dst, fl);
-        return err;
-}
-
 int __init ip_rt_init(void)
 {
        int i, order, goal, rc = 0;
@@ -2688,7 +2681,6 @@ int __init ip_rt_init(void)
                                        ip_rt_gc_interval;
        add_timer(&rt_periodic_timer);
 
-       xfrm_dst_lookup_register(xfrm_dst_lookup, AF_INET);
 #ifdef CONFIG_PROC_FS
        if (rt_cache_proc_init())
                goto out_enomem;
@@ -2698,6 +2690,7 @@ int __init ip_rt_init(void)
 #endif
 #endif
        xfrm_init();
+       xfrm4_init();
 out:
        return rc;
 out_enomem:
index 384ae4f412dfc34b828ee46d7b04914993b22d2a..29c1012ad767e4455ebe742f064caf7915bbba57 100644 (file)
@@ -1798,7 +1798,7 @@ process:
        if (sk->state == TCP_TIME_WAIT)
                goto do_time_wait;
 
-       if (!xfrm_policy_check(sk, XFRM_POLICY_IN, skb))
+       if (!xfrm4_policy_check(sk, XFRM_POLICY_IN, skb))
                goto discard_and_relse;
 
        if (sk_filter(sk, skb, 0))
@@ -1820,7 +1820,7 @@ process:
        return ret;
 
 no_tcp_socket:
-       if (!xfrm_policy_check(NULL, XFRM_POLICY_IN, skb))
+       if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb))
                goto discard_it;
 
        if (skb->len < (th->doff << 2) || tcp_checksum_complete(skb)) {
@@ -1840,7 +1840,7 @@ discard_and_relse:
        goto discard_it;
 
 do_time_wait:
-       if (!xfrm_policy_check(NULL, XFRM_POLICY_IN, skb))
+       if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb))
                goto discard_and_relse;
 
        if (skb->len < (th->doff << 2) || tcp_checksum_complete(skb)) {
index a75c36ee477f3a83e29d92ae307736ad38536a65..1be52464ff655226ef1ef4211734e9c7645238b4 100644 (file)
@@ -946,7 +946,7 @@ static int udp_queue_rcv_skb(struct sock * sk, struct sk_buff *skb)
        /*
         *      Charge it to the socket, dropping if the queue is full.
         */
-       if (!xfrm_policy_check(sk, XFRM_POLICY_IN, skb)) {
+       if (!xfrm4_policy_check(sk, XFRM_POLICY_IN, skb)) {
                kfree_skb(skb);
                return -1;
        }
@@ -1077,7 +1077,7 @@ int udp_rcv(struct sk_buff *skb)
                return 0;
        }
 
-       if (!xfrm_policy_check(NULL, XFRM_POLICY_IN, skb))
+       if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb))
                goto drop;
 
        /* No socket. Drop packet silently, if checksum is wrong */
diff --git a/net/ipv4/xfrm4_input.c b/net/ipv4/xfrm4_input.c
new file mode 100644 (file)
index 0000000..59f4f8f
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * 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");
+}
+
diff --git a/net/ipv4/xfrm4_policy.c b/net/ipv4/xfrm4_policy.c
new file mode 100644 (file)
index 0000000..0266111
--- /dev/null
@@ -0,0 +1,277 @@
+/* 
+ * 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();
+}
+
diff --git a/net/ipv4/xfrm4_state.c b/net/ipv4/xfrm4_state.c
new file mode 100644 (file)
index 0000000..77be6d2
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * 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);
+}
+
index f8d5012b6adaf9785c6e7ef46d098e87cb8e4fa6..71fac0bca4e7893e08f4f00ce184eafa82ed60dd 100644 (file)
@@ -1,29 +1,26 @@
-/* Changes
+/*
+ * xfrm_input.c
  *
- *     Mitsuru KANDA @USAGI       : IPv6 Support 
- *     Kazunori MIYAZAWA @USAGI   :
- *     YOSHIFUJI Hideaki @USAGI   :
- *     Kunihiro Ishiguro          :
+ * Changes:
+ *     YOSHIFUJI Hideaki @USAGI
+ *             Split up af-specific portion
  *     
  */
 
 #include <net/ip.h>
-#include <net/ipv6.h>
 #include <net/xfrm.h>
 
-static kmem_cache_t *secpath_cachep;
-
 void __secpath_destroy(struct sec_path *sp)
 {
        int i;
        for (i = 0; i < sp->len; i++)
                xfrm_state_put(sp->xvec[i]);
-       kmem_cache_free(secpath_cachep, sp);
+       kmem_cache_free(sp->pool, sp);
 }
 
 /* Fetch spi and seq frpm ipsec header */
 
-static int xfrm_parse_spi(struct sk_buff *skb, u32 *spi, u32 *seq)
+int xfrm_parse_spi(struct sk_buff *skb, u8 nexthdr, u32 *spi, u32 *seq)
 {
        int offset, offset_seq;
 
@@ -53,402 +50,3 @@ static int xfrm_parse_spi(struct sk_buff *skb, u32 *spi, u32 *seq)
        *seq = *(u32*)(skb->h.raw + offset_seq);
        return 0;
 }
-
-
-
-int xfrm4_rcv(struct sk_buff *skb)
-{
-       int err;
-       u32 spi, seq;
-       struct xfrm_state *xfrm_vec[XFRM_MAX_DEPTH];
-       struct xfrm_state *x;
-       int xfrm_nr = 0;
-       int decaps = 0;
-
-       if ((err = xfrm_parse_spi(skb, &spi, &seq)) != 0)
-               goto drop;
-
-       do {
-               struct iphdr *iph = skb->nh.iph;
-
-               if (xfrm_nr == XFRM_MAX_DEPTH)
-                       goto drop;
-
-               x = xfrm4_state_lookup(iph->daddr, spi, iph->protocol);
-               if (x == NULL)
-                       goto drop;
-
-               spin_lock(&x->lock);
-               if (unlikely(x->km.state != XFRM_STATE_VALID))
-                       goto drop_unlock;
-
-               if (x->props.replay_window && xfrm_replay_check(x, seq))
-                       goto drop_unlock;
-
-               if (x->type->input(x, skb))
-                       goto drop_unlock;
-
-               if (x->props.replay_window)
-                       xfrm_replay_advance(x, seq);
-
-               x->curlft.bytes += skb->len;
-               x->curlft.packets++;
-
-               spin_unlock(&x->lock);
-
-               xfrm_vec[xfrm_nr++] = x;
-
-               iph = skb->nh.iph;
-
-               if (x->props.mode) {
-                       if (iph->protocol != IPPROTO_IPIP)
-                               goto drop;
-                       skb->nh.raw = skb->data;
-                       iph = skb->nh.iph;
-                       memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options));
-                       decaps = 1;
-                       break;
-               }
-
-               if ((err = xfrm_parse_spi(skb, &spi, &seq)) < 0)
-                       goto drop;
-       } while (!err);
-
-       /* Allocate new secpath or COW existing one. */
-
-       if (!skb->sp || atomic_read(&skb->sp->refcnt) != 1) {
-               struct sec_path *sp;
-               sp = kmem_cache_alloc(secpath_cachep, SLAB_ATOMIC);
-               if (!sp)
-                       goto drop;
-               if (skb->sp) {
-                       memcpy(sp, skb->sp, sizeof(struct sec_path));
-                       secpath_put(skb->sp);
-               } else
-                       sp->len = 0;
-               atomic_set(&sp->refcnt, 1);
-               skb->sp = sp;
-       }
-       if (xfrm_nr + skb->sp->len > XFRM_MAX_DEPTH)
-               goto drop;
-
-       memcpy(skb->sp->xvec+skb->sp->len, xfrm_vec, xfrm_nr*sizeof(void*));
-       skb->sp->len += xfrm_nr;
-
-       if (decaps) {
-               if (!(skb->dev->flags&IFF_LOOPBACK)) {
-                       dst_release(skb->dst);
-                       skb->dst = NULL;
-               }
-               netif_rx(skb);
-               return 0;
-       } else {
-               return -skb->nh.iph->protocol;
-       }
-
-drop_unlock:
-       spin_unlock(&x->lock);
-       xfrm_state_put(x);
-drop:
-       while (--xfrm_nr >= 0)
-               xfrm_state_put(xfrm_vec[xfrm_nr]);
-       kfree_skb(skb);
-       return 0;
-}
-
-
-void __init xfrm_input_init(void)
-{
-       secpath_cachep = kmem_cache_create("secpath_cache",
-                                          sizeof(struct sec_path),
-                                          0, SLAB_HWCACHE_ALIGN,
-                                          NULL, NULL);
-
-       if (!secpath_cachep)
-               panic("IP: failed to allocate secpath_cache\n");
-}
-
-#if defined (CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
-
-/* Fetch spi and seq frpm ipsec header */
-
-static int xfrm6_parse_spi(struct sk_buff *skb, u8 nexthdr, u32 *spi, u32 *seq)
-{
-       int offset, offset_seq;
-
-       switch (nexthdr) {
-       case IPPROTO_AH:
-               offset = offsetof(struct ip_auth_hdr, spi);
-               offset_seq = offsetof(struct ip_auth_hdr, seq_no);
-               break;
-       case IPPROTO_ESP:
-               offset = offsetof(struct ip_esp_hdr, spi);
-               offset_seq = offsetof(struct ip_esp_hdr, seq_no);
-               break;
-       case IPPROTO_COMP:
-               if (!pskb_may_pull(skb, 4))
-                       return -EINVAL;
-               *spi = ntohl(ntohs(*(u16*)(skb->h.raw + 2)));
-               *seq = 0;
-               return 0;
-       default:
-               return 1;
-       }
-
-       if (!pskb_may_pull(skb, 16))
-               return -EINVAL;
-
-       *spi = *(u32*)(skb->h.raw + offset);
-       *seq = *(u32*)(skb->h.raw + offset_seq);
-       return 0;
-}
-
-static int zero_out_mutable_opts(struct ipv6_opt_hdr *opthdr)
-{
-       u8 *opt = (u8 *)opthdr;
-       int len = ipv6_optlen(opthdr);
-       int off = 0;
-       int optlen = 0;
-
-       off += 2;
-       len -= 2;
-
-       while (len > 0) {
-
-               switch (opt[off]) {
-
-               case IPV6_TLV_PAD0:
-                       optlen = 1;
-                       break;
-               default:
-                       if (len < 2) 
-                               goto bad;
-                       optlen = opt[off+1]+2;
-                       if (len < optlen)
-                               goto bad;
-                       if (opt[off] & 0x20)
-                               memset(&opt[off+2], 0, opt[off+1]);
-                       break;
-               }
-
-               off += optlen;
-               len -= optlen;
-       }
-       if (len == 0)
-               return 1;
-
-bad:
-       return 0;
-}
-
-int xfrm6_clear_mutable_options(struct sk_buff *skb, u16 *nh_offset, int dir)
-{
-       u16 offset = sizeof(struct ipv6hdr);
-       struct ipv6_opt_hdr *exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset);
-       unsigned int packet_len = skb->tail - skb->nh.raw;
-       u8 nexthdr = skb->nh.ipv6h->nexthdr;
-       u8 nextnexthdr = 0;
-
-       *nh_offset = ((unsigned char *)&skb->nh.ipv6h->nexthdr) - skb->nh.raw;
-
-       while (offset + 1 <= packet_len) {
-
-               switch (nexthdr) {
-
-               case NEXTHDR_HOP:
-                       *nh_offset = offset;
-                       offset += ipv6_optlen(exthdr);
-                       if (!zero_out_mutable_opts(exthdr)) {
-                               if (net_ratelimit())
-                                       printk(KERN_WARNING "overrun hopopts\n"); 
-                               return 0;
-                       }
-                       nexthdr = exthdr->nexthdr;
-                       exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset);
-                       break;
-
-               case NEXTHDR_ROUTING:
-                       *nh_offset = offset;
-                       offset += ipv6_optlen(exthdr);
-                       ((struct ipv6_rt_hdr*)exthdr)->segments_left = 0; 
-                       nexthdr = exthdr->nexthdr;
-                       exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset);
-                       break;
-
-               case NEXTHDR_DEST:
-                       *nh_offset = offset;
-                       offset += ipv6_optlen(exthdr);
-                       if (!zero_out_mutable_opts(exthdr))  {
-                               if (net_ratelimit())
-                                       printk(KERN_WARNING "overrun destopt\n"); 
-                               return 0;
-                       }
-                       nexthdr = exthdr->nexthdr;
-                       exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset);
-                       break;
-
-               case NEXTHDR_AUTH:
-                       if (dir == XFRM_POLICY_OUT) {
-                               memset(((struct ipv6_auth_hdr*)exthdr)->auth_data, 0, 
-                                      (((struct ipv6_auth_hdr*)exthdr)->hdrlen - 1) << 2);
-                       }
-                       if (exthdr->nexthdr == NEXTHDR_DEST) {
-                               offset += (((struct ipv6_auth_hdr*)exthdr)->hdrlen + 2) << 2;
-                               exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset);
-                               nextnexthdr = exthdr->nexthdr;
-                               if (!zero_out_mutable_opts(exthdr)) {
-                                       if (net_ratelimit())
-                                               printk(KERN_WARNING "overrun destopt\n");
-                                       return 0;
-                               }
-                       }
-                       return nexthdr;
-               default :
-                       return nexthdr;
-               }
-       }
-
-       return nexthdr;
-}
-
-int xfrm6_rcv(struct sk_buff *skb)
-{
-       int err;
-       u32 spi, seq;
-       struct xfrm_state *xfrm_vec[XFRM_MAX_DEPTH];
-       struct xfrm_state *x;
-       int xfrm_nr = 0;
-       int decaps = 0;
-       struct ipv6hdr *hdr = skb->nh.ipv6h;
-       unsigned char *tmp_hdr = NULL;
-       int hdr_len = 0;
-       u16 nh_offset = 0;
-       u8 nexthdr = 0;
-
-       if (hdr->nexthdr == IPPROTO_AH || hdr->nexthdr == IPPROTO_ESP) {
-               nh_offset = ((unsigned char*)&skb->nh.ipv6h->nexthdr) - skb->nh.raw;
-               hdr_len = sizeof(struct ipv6hdr);
-       } else {
-               hdr_len = skb->h.raw - skb->nh.raw;
-       }
-
-       tmp_hdr = kmalloc(hdr_len, GFP_ATOMIC);
-       if (!tmp_hdr)
-               goto drop;
-       memcpy(tmp_hdr, skb->nh.raw, hdr_len);
-
-       nexthdr = xfrm6_clear_mutable_options(skb, &nh_offset, XFRM_POLICY_IN);
-       hdr->priority    = 0;
-       hdr->flow_lbl[0] = 0;
-       hdr->flow_lbl[1] = 0;
-       hdr->flow_lbl[2] = 0;
-       hdr->hop_limit   = 0;
-
-       if ((err = xfrm6_parse_spi(skb, nexthdr, &spi, &seq)) != 0)
-               goto drop;
-       
-       do {
-               struct ipv6hdr *iph = skb->nh.ipv6h;
-
-               if (xfrm_nr == XFRM_MAX_DEPTH)
-                       goto drop;
-
-               x = xfrm6_state_lookup(&iph->daddr, spi, nexthdr);
-               if (x == NULL)
-                       goto drop;
-               spin_lock(&x->lock);
-               if (unlikely(x->km.state != XFRM_STATE_VALID))
-                       goto drop_unlock;
-
-               if (x->props.replay_window && xfrm_replay_check(x, seq))
-                       goto drop_unlock;
-
-               nexthdr = x->type->input(x, skb);
-               if (nexthdr <= 0)
-                       goto drop_unlock;
-
-               if (x->props.replay_window)
-                       xfrm_replay_advance(x, seq);
-
-               x->curlft.bytes += skb->len;
-               x->curlft.packets++;
-
-               spin_unlock(&x->lock);
-
-               xfrm_vec[xfrm_nr++] = x;
-
-               iph = skb->nh.ipv6h; /* ??? */ 
-
-               if (nexthdr == NEXTHDR_DEST) {
-                       if (!pskb_may_pull(skb, (skb->h.raw-skb->data)+8) ||
-                       !pskb_may_pull(skb, (skb->h.raw-skb->data)+((skb->h.raw[1]+1)<<3))) {
-                               err = -EINVAL;
-                               goto drop;
-                       }
-                       nexthdr = skb->h.raw[0];
-                       nh_offset = skb->h.raw - skb->nh.raw;
-                       skb_pull(skb, (skb->h.raw[1]+1)<<3);
-                       skb->h.raw = skb->data;
-               }
-
-               if (x->props.mode) { /* XXX */
-                       if (iph->nexthdr != IPPROTO_IPV6)
-                               goto drop;
-                       skb->nh.raw = skb->data;
-                       iph = skb->nh.ipv6h;
-                       decaps = 1;
-                       break;
-               }
-
-               if ((err = xfrm6_parse_spi(skb, nexthdr, &spi, &seq)) < 0)
-                       goto drop;
-       } while (!err);
-
-       memcpy(skb->nh.raw, tmp_hdr, hdr_len);
-       skb->nh.raw[nh_offset] = nexthdr;
-       skb->nh.ipv6h->payload_len = htons(hdr_len + skb->len - sizeof(struct ipv6hdr));
-
-       /* Allocate new secpath or COW existing one. */
-       if (!skb->sp || atomic_read(&skb->sp->refcnt) != 1) {
-               struct sec_path *sp;
-               sp = kmem_cache_alloc(secpath_cachep, SLAB_ATOMIC);
-               if (!sp)
-                       goto drop;
-               if (skb->sp) {
-                       memcpy(sp, skb->sp, sizeof(struct sec_path));
-                       secpath_put(skb->sp);
-               } else
-                       sp->len = 0;
-               atomic_set(&sp->refcnt, 1);
-               skb->sp = sp;
-       }
-
-       if (xfrm_nr + skb->sp->len > XFRM_MAX_DEPTH)
-               goto drop;
-
-       memcpy(skb->sp->xvec+skb->sp->len, xfrm_vec, xfrm_nr*sizeof(void*));
-       skb->sp->len += xfrm_nr;
-
-       if (decaps) {
-               if (!(skb->dev->flags&IFF_LOOPBACK)) {
-                       dst_release(skb->dst);
-                       skb->dst = NULL;
-               }
-               netif_rx(skb);
-               return 0;
-       } else {
-               return -nexthdr;
-       }
-
-drop_unlock:
-       spin_unlock(&x->lock);
-       xfrm_state_put(x);
-drop:
-       if (tmp_hdr) kfree(tmp_hdr);
-       while (--xfrm_nr >= 0)
-               xfrm_state_put(xfrm_vec[xfrm_nr]);
-       kfree_skb(skb);
-       return 0;
-}
-
-#endif /* CONFIG_IPV6 || CONFIG_IPV6_MODULE */
index ed14bd1068560e1df4dc8bf12abfe60d1af1f5c7..055b7fffa49d3c612571ab230d4c0dd3975c0cbe 100644 (file)
@@ -1,16 +1,20 @@
-/* Changes
+/* 
+ * xfrm_policy.c
  *
- *     Mitsuru KANDA @USAGI       : IPv6 Support 
- *     Kazunori MIYAZAWA @USAGI   :
- *     Kunihiro Ishiguro          :
+ * Changes:
+ *     Mitsuru KANDA @USAGI
+ *     Kazunori MIYAZAWA @USAGI
+ *     Kunihiro Ishiguro
+ *             IPv6 support
+ *     Kazunori MIYAZAWA @USAGI
+ *     YOSHIFUJI Hideaki
+ *             Split up af-specific portion
  *     
  */
 
 #include <linux/config.h>
 #include <net/xfrm.h>
 #include <net/ip.h>
-#include <net/ipv6.h>
-#include <net/ip6_route.h>
 
 DECLARE_MUTEX(xfrm_cfg_sem);
 
@@ -19,12 +23,10 @@ static rwlock_t xfrm_policy_lock = RW_LOCK_UNLOCKED;
 
 struct xfrm_policy *xfrm_policy_list[XFRM_POLICY_MAX*2];
 
-extern struct dst_ops xfrm4_dst_ops;
-#if defined (CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
-extern struct dst_ops xfrm6_dst_ops;
-#endif
+static rwlock_t xfrm_policy_afinfo_lock = RW_LOCK_UNLOCKED;
+static struct xfrm_policy_afinfo *xfrm_policy_afinfo[NPROTO];
 
-static inline int xfrm_dst_lookup(struct xfrm_dst **dst, struct flowi *fl, unsigned short family);
+kmem_cache_t *xfrm_dst_cache;
 
 /* Limited flow cache. Its function now is to accelerate search for
  * policy rules.
@@ -49,40 +51,8 @@ static kmem_cache_t *flow_cachep;
 
 struct flow_entry **flow_table;
 
-#define FLOWCACHE_HASH_SIZE    1024
-
-static inline u32 flow_hash(struct flowi *fl)
-{
-       u32 hash = fl->fl4_src ^ fl->uli_u.ports.sport;
-
-       hash = ((hash & 0xF0F0F0F0) >> 4) | ((hash & 0x0F0F0F0F) << 4);
-
-       hash ^= fl->fl4_dst ^ fl->uli_u.ports.dport;
-       hash ^= (hash >> 10);
-       hash ^= (hash >> 20);
-       return hash & (FLOWCACHE_HASH_SIZE-1);
-}
-
-#if defined (CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
-static inline u32 flow_hash6(struct flowi *fl)
-{
-       u32 hash = fl->fl6_src->s6_addr32[2] ^
-                  fl->fl6_src->s6_addr32[3] ^ 
-                  fl->uli_u.ports.sport;
-
-       hash = ((hash & 0xF0F0F0F0) >> 4) | ((hash & 0x0F0F0F0F) << 4);
-
-       hash ^= fl->fl6_dst->s6_addr32[2] ^
-               fl->fl6_dst->s6_addr32[3] ^ 
-               fl->uli_u.ports.dport;
-       hash ^= (hash >> 10);
-       hash ^= (hash >> 20);
-       return hash & (FLOWCACHE_HASH_SIZE-1);
-}
-#endif
-
-static int flow_lwm = 2*FLOWCACHE_HASH_SIZE;
-static int flow_hwm = 4*FLOWCACHE_HASH_SIZE;
+static int flow_lwm = 2*XFRM_FLOWCACHE_HASH_SIZE;
+static int flow_hwm = 4*XFRM_FLOWCACHE_HASH_SIZE;
 
 static int flow_number[NR_CPUS] __cacheline_aligned;
 
@@ -92,11 +62,11 @@ static void flow_cache_shrink(int cpu)
 {
        int i;
        struct flow_entry *fle, **flp;
-       int shrink_to = flow_lwm/FLOWCACHE_HASH_SIZE;
+       int shrink_to = flow_lwm/XFRM_FLOWCACHE_HASH_SIZE;
 
-       for (i=0; i<FLOWCACHE_HASH_SIZE; i++) {
+       for (i=0; i<XFRM_FLOWCACHE_HASH_SIZE; i++) {
                int k = 0;
-               flp = &flow_table[cpu*FLOWCACHE_HASH_SIZE+i];
+               flp = &flow_table[cpu*XFRM_FLOWCACHE_HASH_SIZE+i];
                while ((fle=*flp) != NULL && k<shrink_to) {
                        k++;
                        flp = &fle->next;
@@ -118,23 +88,12 @@ struct xfrm_policy *flow_lookup(int dir, struct flowi *fl,
        u32 hash;
        int cpu;
 
-       switch (family) {
-       case AF_INET:
-               hash = flow_hash(fl);
-               break;
-#if defined (CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
-       case AF_INET6:
-               hash = flow_hash6(fl);
-               break;
-#endif
-       default:
-               return NULL;
-       }
+       hash = flow_hash(fl, family);
 
        local_bh_disable();
        cpu = smp_processor_id();
 
-       for (fle = flow_table[cpu*FLOWCACHE_HASH_SIZE+hash];
+       for (fle = flow_table[cpu*XFRM_FLOWCACHE_HASH_SIZE+hash];
             fle; fle = fle->next) {
                if (memcmp(fl, &fle->fl, sizeof(fle->fl)) == 0 &&
                    fle->dir == dir) {
@@ -172,8 +131,8 @@ struct xfrm_policy *flow_lookup(int dir, struct flowi *fl,
                        fle->pol = pol;
                        if (pol)
                                atomic_inc(&pol->refcnt);
-                       fle->next = flow_table[cpu*FLOWCACHE_HASH_SIZE+hash];
-                       flow_table[cpu*FLOWCACHE_HASH_SIZE+hash] = fle;
+                       fle->next = flow_table[cpu*XFRM_FLOWCACHE_HASH_SIZE+hash];
+                       flow_table[cpu*XFRM_FLOWCACHE_HASH_SIZE+hash] = fle;
                }
        }
        local_bh_enable();
@@ -193,7 +152,7 @@ void __init flow_cache_init(void)
                panic("NET: failed to allocate flow cache slab\n");
 
        for (order = 0;
-            (PAGE_SIZE<<order) < (NR_CPUS*sizeof(struct flow_entry *)*FLOWCACHE_HASH_SIZE);
+            (PAGE_SIZE<<order) < (NR_CPUS*sizeof(struct flow_entry *)*XFRM_FLOWCACHE_HASH_SIZE);
             order++)
                /* NOTHING */;
 
@@ -205,130 +164,82 @@ void __init flow_cache_init(void)
        memset(flow_table, 0, PAGE_SIZE<<order);
 }
 
-static struct xfrm_type *xfrm_type_map[256];
-static rwlock_t xfrm_type_lock = RW_LOCK_UNLOCKED;
-
-int xfrm_register_type(struct xfrm_type *type)
+int xfrm_register_type(struct xfrm_type *type, unsigned short family)
 {
+       struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family);
+       struct xfrm_type_map *typemap;
        int err = 0;
 
-       write_lock(&xfrm_type_lock);
-       if (xfrm_type_map[type->proto] == NULL)
-               xfrm_type_map[type->proto] = type;
+       if (unlikely(afinfo == NULL))
+               return -EAFNOSUPPORT;
+       typemap = afinfo->type_map;
+
+       write_lock(&typemap->lock);
+       if (likely(typemap->map[type->proto] == NULL))
+               typemap->map[type->proto] = type;
        else
                err = -EEXIST;
-       write_unlock(&xfrm_type_lock);
+       write_unlock(&typemap->lock);
+       xfrm_policy_put_afinfo(afinfo);
        return err;
 }
 
-int xfrm_unregister_type(struct xfrm_type *type)
+int xfrm_unregister_type(struct xfrm_type *type, unsigned short family)
 {
+       struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family);
+       struct xfrm_type_map *typemap;
        int err = 0;
 
-       write_lock(&xfrm_type_lock);
-       if (xfrm_type_map[type->proto] != type)
+       if (unlikely(afinfo == NULL))
+               return -EAFNOSUPPORT;
+       typemap = afinfo->type_map;
+
+       write_lock(&typemap->lock);
+       if (unlikely(typemap->map[type->proto] != type))
                err = -ENOENT;
        else
-               xfrm_type_map[type->proto] = NULL;
-       write_unlock(&xfrm_type_lock);
+               typemap->map[type->proto] = NULL;
+       write_unlock(&typemap->lock);
+       xfrm_policy_put_afinfo(afinfo);
        return err;
 }
 
-struct xfrm_type *xfrm_get_type(u8 proto)
+struct xfrm_type *xfrm_get_type(u8 proto, unsigned short family)
 {
+       struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family);
+       struct xfrm_type_map *typemap;
        struct xfrm_type *type;
 
-       read_lock(&xfrm_type_lock);
-       type = xfrm_type_map[proto];
-       if (type && !try_module_get(type->owner))
+       if (unlikely(afinfo == NULL))
+               return NULL;
+       typemap = afinfo->type_map;
+
+       read_lock(&typemap->lock);
+       type = typemap->map[proto];
+       if (unlikely(type && !try_module_get(type->owner)))
                type = NULL;
-       read_unlock(&xfrm_type_lock);
+       read_unlock(&typemap->lock);
+       xfrm_policy_put_afinfo(afinfo);
        return type;
 }
 
-static  xfrm_dst_lookup_t *__xfrm_dst_lookup[AF_MAX];
-rwlock_t xdl_lock = RW_LOCK_UNLOCKED;
-
-int xfrm_dst_lookup_register(xfrm_dst_lookup_t *dst_lookup, 
-                            unsigned short family)
+int xfrm_dst_lookup(struct xfrm_dst **dst, struct flowi *fl, 
+                   unsigned short family)
 {
+       struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family);
        int err = 0;
 
-       write_lock(&xdl_lock);
-       if (__xfrm_dst_lookup[family])
-               err = -ENOBUFS;
-       else { 
-               __xfrm_dst_lookup[family] = dst_lookup;
-       }
-       write_unlock(&xdl_lock);
+       if (unlikely(afinfo == NULL))
+               return -EAFNOSUPPORT;
 
-       return err;
-}
-
-void xfrm_dst_lookup_unregister(unsigned short family)
-{
-       write_lock(&xdl_lock);
-       if (__xfrm_dst_lookup[family])
-               __xfrm_dst_lookup[family] = 0;
-       write_unlock(&xdl_lock);
-}
-
-static inline int xfrm_dst_lookup(struct xfrm_dst **dst, struct flowi *fl, 
-                                 unsigned short family)
-{
-       int err = 0;
-       read_lock(&xdl_lock);
-       if (__xfrm_dst_lookup[family])
-               err = __xfrm_dst_lookup[family](dst, fl);
+       if (likely(afinfo->dst_lookup != NULL))
+               err = afinfo->dst_lookup(dst, fl);
        else
                err = -EINVAL;
-       read_unlock(&xdl_lock);
+       xfrm_policy_put_afinfo(afinfo);
        return err;
 }
 
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-static struct xfrm_type *xfrm6_type_map[256];
-static rwlock_t xfrm6_type_lock = RW_LOCK_UNLOCKED;
-
-int xfrm6_register_type(struct xfrm_type *type)
-{
-       int err = 0;
-
-       write_lock(&xfrm6_type_lock);
-       if (xfrm6_type_map[type->proto] == NULL)
-               xfrm6_type_map[type->proto] = type;
-       else
-               err = -EEXIST;
-       write_unlock(&xfrm6_type_lock);
-       return err;
-}
-
-int xfrm6_unregister_type(struct xfrm_type *type)
-{
-       int err = 0;
-
-       write_lock(&xfrm6_type_lock);
-       if (xfrm6_type_map[type->proto] != type)
-               err = -ENOENT;
-       else
-               xfrm6_type_map[type->proto] = NULL;
-       write_unlock(&xfrm6_type_lock);
-       return err;
-}
-
-struct xfrm_type *xfrm6_get_type(u8 proto)
-{
-       struct xfrm_type *type;
-
-       read_lock(&xfrm6_type_lock);
-       type = xfrm6_type_map[proto];
-       if (type && !try_module_get(type->owner))
-               type = NULL;
-       read_unlock(&xfrm6_type_lock);
-       return type;
-}
-#endif /* CONFIG_IPV6 || CONFIG_IPV6_MODULE */
-
 void xfrm_put_type(struct xfrm_type *type)
 {
        module_put(type->owner);
@@ -608,18 +519,7 @@ struct xfrm_policy *xfrm_policy_lookup(int dir, struct flowi *fl,
                if (pol->family != family)
                        continue;
 
-               switch (family) {
-               case AF_INET:
-                       match = xfrm4_selector_match(sel, fl);
-                       break;
-#if defined (CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
-               case AF_INET6:
-                       match = xfrm6_selector_match(sel, fl);
-                       break;
-#endif
-               default:
-                       match = 0;
-               }
+               match = xfrm_selector_match(sel, fl, family);
                if (match) {
                        atomic_inc(&pol->refcnt);
                        break;
@@ -637,18 +537,7 @@ struct xfrm_policy *xfrm_sk_policy_lookup(struct sock *sk, int dir, struct flowi
        if ((pol = sk->policy[dir]) != NULL) {
                int match;
 
-               switch (sk->family) {
-               case AF_INET:
-                       match = xfrm4_selector_match(&pol->selector, fl);
-                       break;
-#if defined (CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
-               case AF_INET6:
-                       match = xfrm6_selector_match(&pol->selector, fl);
-                       break;
-#endif
-               default:
-                       match = 0;
-               }
+               match = xfrm_selector_match(&pol->selector, fl, sk->family);
                if (match)
                        atomic_inc(&pol->refcnt);
                else
@@ -750,72 +639,27 @@ void __xfrm_sk_free_policy(struct xfrm_policy *pol, int dir)
 /* Resolve list of templates for the flow, given policy. */
 
 static int
-xfrm4_tmpl_resolve(struct xfrm_policy *policy, struct flowi *fl,
-                  struct xfrm_state **xfrm)
+xfrm_tmpl_resolve(struct xfrm_policy *policy, struct flowi *fl,
+                 struct xfrm_state **xfrm,
+                 unsigned short family)
 {
        int nx;
        int i, error;
-       u32 daddr = fl->fl4_dst;
-       u32 saddr = fl->fl4_src;
+       xfrm_address_t *daddr = xfrm_flowi_daddr(fl, family);
+       xfrm_address_t *saddr = xfrm_flowi_saddr(fl, family);
 
        for (nx=0, i = 0; i < policy->xfrm_nr; i++) {
                struct xfrm_state *x;
-               u32 remote = daddr;
-               u32 local = saddr;
-               struct xfrm_tmpl *tmpl = &policy->xfrm_vec[i];
-
-               if (tmpl->mode) {
-                       remote = tmpl->id.daddr.xfrm4_addr;
-                       local = tmpl->saddr.xfrm4_addr;
-               }
-
-               x = xfrm4_state_find(remote, local, fl, tmpl, policy, &error);
-
-               if (x && x->km.state == XFRM_STATE_VALID) {
-                       xfrm[nx++] = x;
-                       daddr = remote;
-                       saddr = local;
-                       continue;
-               }
-               if (x) {
-                       error = (x->km.state == XFRM_STATE_ERROR ?
-                                -EINVAL : -EAGAIN);
-                       xfrm_state_put(x);
-               }
-
-               if (!tmpl->optional)
-                       goto fail;
-       }
-       return nx;
-
-fail:
-       for (nx--; nx>=0; nx--)
-               xfrm_state_put(xfrm[nx]);
-       return error;
-}
-
-#if defined (CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
-static int
-xfrm6_tmpl_resolve(struct xfrm_policy *policy, struct flowi *fl,
-                 struct xfrm_state **xfrm)
-{
-       int nx;
-       int i, error;
-       struct in6_addr *daddr = fl->fl6_dst;
-       struct in6_addr *saddr = fl->fl6_src;
-
-       for (nx=0, i = 0; i < policy->xfrm_nr; i++) {
-               struct xfrm_state *x=NULL;
-               struct in6_addr *remote = daddr;
-               struct in6_addr *local = saddr;
+               xfrm_address_t *remote = daddr;
+               xfrm_address_t *local  = saddr;
                struct xfrm_tmpl *tmpl = &policy->xfrm_vec[i];
 
                if (tmpl->mode) {
-                       remote = (struct in6_addr*)&tmpl->id.daddr;
-                       local = (struct in6_addr*)&tmpl->saddr;
+                       remote = &tmpl->id.daddr;
+                       local = &tmpl->saddr;
                }
 
-               x = xfrm6_state_find(remote, local, fl, tmpl, policy, &error);
+               x = xfrm_state_find(remote, local, fl, tmpl, policy, &error, family);
 
                if (x && x->km.state == XFRM_STATE_VALID) {
                        xfrm[nx++] = x;
@@ -839,241 +683,40 @@ fail:
                xfrm_state_put(xfrm[nx]);
        return error;
 }
-#endif
 
 /* Check that the bundle accepts the flow and its components are
  * still valid.
  */
 
-static int xfrm_bundle_ok(struct xfrm_dst *xdst, struct flowi *fl)
+static struct dst_entry *
+xfrm_find_bundle(struct flowi *fl, struct rtable *rt, struct xfrm_policy *policy, unsigned short family)
 {
-       do {
-               if (xdst->u.dst.ops != &xfrm4_dst_ops)
-                       return 1;
-
-               if (!xfrm4_selector_match(&xdst->u.dst.xfrm->sel, fl))
-                       return 0;
-               if (xdst->u.dst.xfrm->km.state != XFRM_STATE_VALID ||
-                   xdst->u.dst.path->obsolete > 0)
-                       return 0;
-               xdst = (struct xfrm_dst*)xdst->u.dst.child;
-       } while (xdst);
-       return 0;
+       struct dst_entry *x;
+       struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family);
+       if (unlikely(afinfo == NULL))
+               return ERR_PTR(-EINVAL);
+       x = afinfo->find_bundle(fl, rt, policy);
+       xfrm_policy_put_afinfo(afinfo);
+       return x;
 }
 
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-static int xfrm6_bundle_ok(struct xfrm_dst *xdst, struct flowi *fl)
-{
-       do {
-               if (xdst->u.dst.ops != &xfrm6_dst_ops)
-                       return 1;
-
-               if (!xfrm6_selector_match(&xdst->u.dst.xfrm->sel, fl))
-                       return 0;
-               if (xdst->u.dst.xfrm->km.state != XFRM_STATE_VALID ||
-                   xdst->u.dst.path->obsolete > 0)
-                       return 0;
-               xdst = (struct xfrm_dst*)xdst->u.dst.child;
-       } while (xdst);
-       return 0;
-}
-#endif
-
-
 /* Allocate chain of dst_entry's, attach known xfrm's, calculate
  * all the metrics... Shortly, bundle a bundle.
  */
 
 static int
 xfrm_bundle_create(struct xfrm_policy *policy, struct xfrm_state **xfrm, int nx,
-                  struct flowi *fl, struct dst_entry **dst_p)
+                  struct flowi *fl, struct dst_entry **dst_p,
+                  unsigned short family)
 {
-       struct dst_entry *dst, *dst_prev;
-       struct rtable *rt0 = (struct rtable*)(*dst_p);
-       struct rtable *rt = rt0;
-       u32 remote = fl->fl4_dst;
-       u32 local  = fl->fl4_src;
-       int i;
        int err;
-       int header_len = 0;
-       int trailer_len = 0;
-
-       dst = dst_prev = NULL;
-
-       for (i = 0; i < nx; i++) {
-               struct dst_entry *dst1 = dst_alloc(&xfrm4_dst_ops);
-
-               if (unlikely(dst1 == NULL)) {
-                       err = -ENOBUFS;
-                       goto error;
-               }
-
-               dst1->xfrm = xfrm[i];
-               if (!dst)
-                       dst = dst1;
-               else {
-                       dst_prev->child = dst1;
-                       dst1->flags |= DST_NOHASH;
-                       dst_hold(dst1);
-               }
-               dst_prev = dst1;
-               if (xfrm[i]->props.mode) {
-                       remote = xfrm[i]->id.daddr.xfrm4_addr;
-                       local  = xfrm[i]->props.saddr.xfrm4_addr;
-               }
-               header_len += xfrm[i]->props.header_len;
-               trailer_len += xfrm[i]->props.trailer_len;
-       }
-
-       if (remote != fl->fl4_dst) {
-               struct flowi fl_tunnel = { .nl_u = { .ip4_u =
-                                                    { .daddr = remote,
-                                                      .saddr = local }
-                                                  }
-                                        };
-               err = xfrm_dst_lookup((struct xfrm_dst**)&rt, &fl_tunnel, AF_INET);
-               if (err)
-                       goto error;
-       } else {
-               dst_hold(&rt->u.dst);
-       }
-       dst_prev->child = &rt->u.dst;
-       for (dst_prev = dst; dst_prev != &rt->u.dst; dst_prev = dst_prev->child) {
-               struct xfrm_dst *x = (struct xfrm_dst*)dst_prev;
-               x->u.rt.fl = *fl;
-
-               dst_prev->dev = rt->u.dst.dev;
-               if (rt->u.dst.dev)
-                       dev_hold(rt->u.dst.dev);
-               dst_prev->obsolete      = -1;
-               dst_prev->flags        |= DST_HOST;
-               dst_prev->lastuse       = jiffies;
-               dst_prev->header_len    = header_len;
-               dst_prev->trailer_len   = trailer_len;
-               memcpy(&dst_prev->metrics, &rt->u.dst.metrics, sizeof(dst_prev->metrics));
-               dst_prev->path          = &rt->u.dst;
-
-               /* Copy neighbout for reachability confirmation */
-               dst_prev->neighbour     = neigh_clone(rt->u.dst.neighbour);
-               dst_prev->input         = rt->u.dst.input;
-               dst_prev->output        = dst_prev->xfrm->type->output;
-               if (rt->peer)
-                       atomic_inc(&rt->peer->refcnt);
-               x->u.rt.peer = rt->peer;
-               /* Sheit... I remember I did this right. Apparently,
-                * it was magically lost, so this code needs audit */
-               x->u.rt.rt_flags = rt0->rt_flags&(RTCF_BROADCAST|RTCF_MULTICAST|RTCF_LOCAL);
-               x->u.rt.rt_type = rt->rt_type;
-               x->u.rt.rt_src = rt0->rt_src;
-               x->u.rt.rt_dst = rt0->rt_dst;
-               x->u.rt.rt_gateway = rt->rt_gateway;
-               x->u.rt.rt_spec_dst = rt0->rt_spec_dst;
-               header_len -= x->u.dst.xfrm->props.header_len;
-               trailer_len -= x->u.dst.xfrm->props.trailer_len;
-       }
-       *dst_p = dst;
-       return 0;
-
-error:
-       if (dst)
-               dst_free(dst);
-       return err;
-}
-
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-static int
-xfrm6_bundle_create(struct xfrm_policy *policy, struct xfrm_state **xfrm, int nx,
-                  struct flowi *fl, struct dst_entry **dst_p)
-{
-       struct dst_entry *dst, *dst_prev;
-       struct rt6_info *rt0 = (struct rt6_info*)(*dst_p);
-       struct rt6_info *rt  = rt0;
-       struct in6_addr *remote = fl->fl6_dst;
-       struct in6_addr *local  = fl->fl6_src;
-       int i;
-       int err = 0;
-       int header_len = 0;
-       int trailer_len = 0;
-
-       dst = dst_prev = NULL;
-
-       for (i = 0; i < nx; i++) {
-               struct dst_entry *dst1 = dst_alloc(&xfrm6_dst_ops);
-
-               if (unlikely(dst1 == NULL)) {
-                       err = -ENOBUFS;
-                       goto error;
-               }
-
-               dst1->xfrm = xfrm[i];
-               if (!dst)
-                       dst = dst1;
-               else {
-                       dst_prev->child = dst1;
-                       dst1->flags |= DST_NOHASH;
-                       dst_clone(dst1);
-               }
-               dst_prev = dst1;
-               if (xfrm[i]->props.mode) {
-                       remote = (struct in6_addr*)&xfrm[i]->id.daddr;
-                       local  = (struct in6_addr*)&xfrm[i]->props.saddr;
-               }
-               header_len += xfrm[i]->props.header_len;
-               trailer_len += xfrm[i]->props.trailer_len;
-       }
-
-       if (ipv6_addr_cmp(remote, fl->fl6_dst)) {
-               struct flowi fl_tunnel = { .nl_u = { .ip6_u =
-                                                    { .daddr = remote,
-                                                      .saddr = local }
-                                                  }
-                                        };
-               err = xfrm_dst_lookup((struct xfrm_dst**)&dst, &fl_tunnel, AF_INET6);
-               if (err)
-                       goto error;
-       } else {
-               dst_clone(&rt->u.dst);
-       }
-       dst_prev->child = &rt->u.dst;
-       for (dst_prev = dst; dst_prev != &rt->u.dst; dst_prev = dst_prev->child) {
-               struct xfrm_dst *x = (struct xfrm_dst*)dst_prev;
-               x->u.rt.fl = *fl;
-
-               dst_prev->dev = rt->u.dst.dev;
-               if (rt->u.dst.dev)
-                       dev_hold(rt->u.dst.dev);
-               dst_prev->obsolete      = -1;
-               dst_prev->flags        |= DST_HOST;
-               dst_prev->lastuse       = jiffies;
-               dst_prev->header_len    = header_len;
-               dst_prev->trailer_len   = trailer_len;
-               memcpy(&dst_prev->metrics, &rt->u.dst.metrics, sizeof(dst_prev->metrics));
-               dst_prev->path          = &rt->u.dst;
-
-               /* Copy neighbout for reachability confirmation */
-               dst_prev->neighbour     = neigh_clone(rt->u.dst.neighbour);
-               dst_prev->input         = rt->u.dst.input;
-               dst_prev->output        = dst_prev->xfrm->type->output;
-               /* Sheit... I remember I did this right. Apparently,
-                * it was magically lost, so this code needs audit */
-               x->u.rt6.rt6i_flags    = rt0->rt6i_flags&(RTCF_BROADCAST|RTCF_MULTICAST|RTCF_LOCAL);
-               x->u.rt6.rt6i_metric   = rt0->rt6i_metric;
-               x->u.rt6.rt6i_node     = rt0->rt6i_node;
-               x->u.rt6.rt6i_hoplimit = rt0->rt6i_hoplimit;
-               x->u.rt6.rt6i_gateway  = rt0->rt6i_gateway;
-               memcpy(&x->u.rt6.rt6i_gateway, &rt0->rt6i_gateway, sizeof(x->u.rt6.rt6i_gateway)); 
-               header_len -= x->u.dst.xfrm->props.header_len;
-               trailer_len -= x->u.dst.xfrm->props.trailer_len;
-       }
-       *dst_p = dst;
-       return 0;
-
-error:
-       if (dst)
-               dst_free(dst);
+       struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family);
+       if (unlikely(afinfo == NULL))
+               return -EINVAL;
+       err = afinfo->bundle_create(policy, xfrm, nx, fl, dst_p);
+       xfrm_policy_put_afinfo(afinfo);
        return err;
 }
-#endif
 
 /* Main function: finds/creates a bundle for given flow.
  *
@@ -1141,46 +784,17 @@ restart:
                 * LATER: help from flow cache. It is optional, this
                 * is required only for output policy.
                 */
-               if (family == AF_INET) {
-                       read_lock_bh(&policy->lock);
-                       for (dst = policy->bundles; dst; dst = dst->next) {
-                               struct xfrm_dst *xdst = (struct xfrm_dst*)dst;
-                               if (xdst->u.rt.fl.fl4_dst == fl->fl4_dst &&
-                                   xdst->u.rt.fl.fl4_src == fl->fl4_src &&
-                                   xdst->u.rt.fl.oif == fl->oif &&
-                                   xfrm_bundle_ok(xdst, fl)) {
-                                       dst_clone(dst);
-                                       break;
-                               }
-                       }
-                       read_unlock_bh(&policy->lock);
-                       if (dst)
-                               break;
-                       nx = xfrm4_tmpl_resolve(policy, fl, xfrm);
-#if defined (CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
-               } else if (family == AF_INET6) {
-                       read_lock_bh(&policy->lock);
-                       for (dst = policy->bundles; dst; dst = dst->next) {
-                               struct xfrm_dst *xdst = (struct xfrm_dst*)dst;
-                               if (!ipv6_addr_cmp(&xdst->u.rt6.rt6i_dst.addr, fl->fl6_dst) &&
-                                   !ipv6_addr_cmp(&xdst->u.rt6.rt6i_src.addr, fl->fl6_src) &&
-                                   xfrm6_bundle_ok(xdst, fl)) {
-                                       dst_clone(dst);
-                                       break;
-                               }
-                       }
-                       read_unlock_bh(&policy->lock);
-                       if (dst)
-                               break;
-                       nx = xfrm6_tmpl_resolve(policy, fl, xfrm);
-#endif
-               } else {
-                       return -EINVAL;
+               dst = xfrm_find_bundle(fl, rt, policy, family);
+               if (IS_ERR(dst)) {
+                       xfrm_pol_put(policy);
+                       return PTR_ERR(dst);
                }
 
                if (dst)
                        break;
 
+               nx = xfrm_tmpl_resolve(policy, fl, xfrm, family);
+
                if (unlikely(nx<0)) {
                        err = nx;
                        if (err == -EAGAIN) {
@@ -1191,18 +805,7 @@ restart:
 
                                __set_task_state(tsk, TASK_INTERRUPTIBLE);
                                add_wait_queue(&km_waitq, &wait);
-                               switch (family) {
-                               case AF_INET:
-                                       err = xfrm4_tmpl_resolve(policy, fl, xfrm);
-                                       break;
-#if defined (CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
-                               case AF_INET6:
-                                       err = xfrm6_tmpl_resolve(policy, fl, xfrm);
-                                       break;
-#endif
-                               default:
-                                       err = -EINVAL;
-                               }
+                               err = xfrm_tmpl_resolve(policy, fl, xfrm, family);
                                if (err == -EAGAIN)
                                        schedule();
                                __set_task_state(tsk, TASK_RUNNING);
@@ -1225,19 +828,8 @@ restart:
                }
 
                dst = &rt->u.dst;
-               switch (family) {
-               case AF_INET:
-                       err = xfrm_bundle_create(policy, xfrm, nx, fl, &dst);
-                       break;
-#if defined (CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
-               case AF_INET6:
-                       err = xfrm6_bundle_create(policy, xfrm, nx, fl, &dst);
-                       break;
-#endif
-               default:
-                       err = -EINVAL;
-               }
-                       
+               err = xfrm_bundle_create(policy, xfrm, nx, fl, &dst, family);
+
                if (unlikely(err)) {
                        int i;
                        for (i=0; i<nx; i++)
@@ -1282,137 +874,39 @@ error:
  */
 
 static inline int
-xfrm_state_ok(struct xfrm_tmpl *tmpl, struct xfrm_state *x)
-{
-       return  x->id.proto == tmpl->id.proto &&
-               (x->id.spi == tmpl->id.spi || !tmpl->id.spi) &&
-               x->props.mode == tmpl->mode &&
-               (tmpl->aalgos & (1<<x->props.aalgo)) &&
-               (!x->props.mode || !tmpl->saddr.xfrm4_addr ||
-                tmpl->saddr.xfrm4_addr == x->props.saddr.xfrm4_addr);
-}
-
-static inline int
-xfrm_policy_ok(struct xfrm_tmpl *tmpl, struct sec_path *sp, int idx)
-{
-       for (; idx < sp->len; idx++) {
-               if (xfrm_state_ok(tmpl, sp->xvec[idx]))
-                       return ++idx;
-       }
-       return -1;
-}
-
-static void
-_decode_session4(struct sk_buff *skb, struct flowi *fl)
-{
-       struct iphdr *iph = skb->nh.iph;
-       u8 *xprth = skb->nh.raw + iph->ihl*4;
-
-       if (!(iph->frag_off & htons(IP_MF | IP_OFFSET))) {
-               switch (iph->protocol) {
-               case IPPROTO_UDP:
-               case IPPROTO_TCP:
-               case IPPROTO_SCTP:
-                       if (pskb_may_pull(skb, xprth + 4 - skb->data)) {
-                               u16 *ports = (u16 *)xprth;
-
-                               fl->uli_u.ports.sport = ports[0];
-                               fl->uli_u.ports.dport = ports[1];
-                       }
-                       break;
-
-               case IPPROTO_ESP:
-                       if (pskb_may_pull(skb, xprth + 4 - skb->data)) {
-                               u32 *ehdr = (u32 *)xprth;
-
-                               fl->uli_u.spi = ehdr[0];
-                       }
-                       break;
-
-               case IPPROTO_AH:
-                       if (pskb_may_pull(skb, xprth + 8 - skb->data)) {
-                               u32 *ah_hdr = (u32*)xprth;
-
-                               fl->uli_u.spi = ah_hdr[1];
-                       }
-                       break;
-
-               default:
-                       fl->uli_u.spi = 0;
-                       break;
-               };
-       } else {
-               memset(fl, 0, sizeof(struct flowi));
-       }
-       fl->proto = iph->protocol;
-       fl->fl4_dst = iph->daddr;
-       fl->fl4_src = iph->saddr;
-}
-
-#if defined (CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
-static inline int
-xfrm6_state_ok(struct xfrm_tmpl *tmpl, struct xfrm_state *x)
+xfrm_state_ok(struct xfrm_tmpl *tmpl, struct xfrm_state *x, 
+             unsigned short family)
 {
        return  x->id.proto == tmpl->id.proto &&
                (x->id.spi == tmpl->id.spi || !tmpl->id.spi) &&
                x->props.mode == tmpl->mode &&
                (tmpl->aalgos & (1<<x->props.aalgo)) &&
-               (!x->props.mode || !ipv6_addr_any((struct in6_addr*)&x->props.saddr) ||
-                !ipv6_addr_cmp((struct in6_addr *)&tmpl->saddr, (struct in6_addr*)&x->props.saddr));
+               !(x->props.mode && xfrm_state_addr_cmp(tmpl, x, family));
 }
 
 static inline int
-xfrm6_policy_ok(struct xfrm_tmpl *tmpl, struct sec_path *sp, int idx)
+xfrm_policy_ok(struct xfrm_tmpl *tmpl, struct sec_path *sp, int idx,
+              unsigned short family)
 {
        for (; idx < sp->len; idx++) {
-               if (xfrm6_state_ok(tmpl, sp->xvec[idx]))
+               if (xfrm_state_ok(tmpl, sp->xvec[idx], family))
                        return ++idx;
        }
        return -1;
 }
 
-static inline void
-_decode_session6(struct sk_buff *skb, struct flowi *fl)
+static int
+_decode_session(struct sk_buff *skb, struct flowi *fl, unsigned short family)
 {
-       u16 offset = sizeof(struct ipv6hdr);
-       struct ipv6hdr *hdr = skb->nh.ipv6h;
-       struct ipv6_opt_hdr *exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset);
-       u8 nexthdr = skb->nh.ipv6h->nexthdr;
-
-       fl->fl6_dst = &hdr->daddr;
-       fl->fl6_src = &hdr->saddr;
-
-       while (pskb_may_pull(skb, skb->nh.raw + offset + 1 - skb->data)) {
-               switch (nexthdr) {
-               case NEXTHDR_ROUTING:
-               case NEXTHDR_HOP:
-               case NEXTHDR_DEST:
-                       offset += ipv6_optlen(exthdr);
-                       nexthdr = exthdr->nexthdr;
-                       exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset);
-                       break;
+       struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family);
 
-               case IPPROTO_UDP:
-               case IPPROTO_TCP:
-               case IPPROTO_SCTP:
-                       if (pskb_may_pull(skb, skb->nh.raw + offset + 4 - skb->data)) {
-                               u16 *ports = (u16 *)exthdr;
+       if (unlikely(afinfo == NULL))
+               return -EAFNOSUPPORT;
 
-                               fl->uli_u.ports.sport = ports[0];
-                               fl->uli_u.ports.dport = ports[1];
-                       }
-                       return;
-
-               /* XXX Why are there these headers? */
-               case IPPROTO_AH:
-               case IPPROTO_ESP:
-               default:
-                       fl->uli_u.spi = 0;
-                       return;
-               };
-       }
+       afinfo->decode_session(skb, fl);
+       xfrm_policy_put_afinfo(afinfo);
+       return 0;
 }
-#endif
 
 int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb, 
                        unsigned short family)
@@ -1420,38 +914,15 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
        struct xfrm_policy *pol;
        struct flowi fl;
 
-       switch (family) {
-       case AF_INET:
-               _decode_session4(skb, &fl);
-               break;
-#if defined (CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
-       case AF_INET6:
-               _decode_session6(skb, &fl);
-               break;
-#endif
-       default :
+       if (_decode_session(skb, &fl, family) < 0)
                return 0;
-       }
 
        /* First, check used SA against their selectors. */
        if (skb->sp) {
                int i;
 
                for (i=skb->sp->len-1; i>=0; i--) {
-                       int match;
-                       switch (family) {
-                       case AF_INET:
-                               match = xfrm4_selector_match(&skb->sp->xvec[i]->sel, &fl);
-                               break;
-#if defined (CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
-                       case AF_INET6:
-                               match = xfrm6_selector_match(&skb->sp->xvec[i]->sel, &fl);
-                               break;
-#endif
-                       default:
-                               match = 0;
-                       }
-                       if (!match)
+                       if (!xfrm_selector_match(&skb->sp->xvec[i]->sel, &fl, family))
                                return 0;
                }
        }
@@ -1483,20 +954,7 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
                         * are implied between each two transformations.
                         */
                        for (i = pol->xfrm_nr-1, k = 0; i >= 0; i--) {
-                               if (pol->xfrm_vec[i].optional)
-                                       continue;
-                               switch (family) {
-                               case AF_INET:
-                                       k = xfrm_policy_ok(pol->xfrm_vec+i, sp, k);
-                                       break;
-#if defined (CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
-                               case AF_INET6:
-                                       k = xfrm6_policy_ok(pol->xfrm_vec+i, sp, k);
-                                       break;
-#endif
-                               default:
-                                       k = -1;
-                               }
+                               k = xfrm_policy_ok(pol->xfrm_vec+i, sp, k, family);
                                if (k < 0)
                                        goto reject;
                        }
@@ -1514,18 +972,8 @@ int __xfrm_route_forward(struct sk_buff *skb, unsigned short family)
 {
        struct flowi fl;
 
-       switch (family) {
-       case AF_INET:
-               _decode_session4(skb, &fl);
-               break;
-#if defined (CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
-       case AF_INET6:
-               _decode_session6(skb, &fl);
-               break;
-#endif
-       default:
+       if (_decode_session(skb, &fl, family) < 0)
                return 0;
-       }
 
        return xfrm_lookup(&skb->dst, &fl, NULL, 0) == 0;
 }
@@ -1603,20 +1051,6 @@ static void __xfrm_garbage_collect(void)
        }
 }
 
-static inline int xfrm4_garbage_collect(void)
-{
-       __xfrm_garbage_collect();
-       return (atomic_read(&xfrm4_dst_ops.entries) > xfrm4_dst_ops.gc_thresh*2);
-}
-
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-static inline int xfrm6_garbage_collect(void)
-{
-       __xfrm_garbage_collect();
-       return (atomic_read(&xfrm6_dst_ops.entries) > xfrm6_dst_ops.gc_thresh*2);
-}
-#endif
-
 static int bundle_depends_on(struct dst_entry *dst, struct xfrm_state *x)
 {
        do {
@@ -1660,29 +1094,6 @@ int xfrm_flush_bundles(struct xfrm_state *x)
        return 0;
 }
 
-static void xfrm4_update_pmtu(struct dst_entry *dst, u32 mtu)
-{
-       struct dst_entry *path = dst->path;
-
-       if (mtu < 68 + dst->header_len)
-               return;
-
-       path->ops->update_pmtu(path, mtu);
-}
-
-#if defined (CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
-static void xfrm6_update_pmtu(struct dst_entry *dst, u32 mtu)
-{
-       struct dst_entry *path = dst->path;
-
-       if (mtu >= 1280 && mtu < dst_pmtu(dst))
-               return;
-
-       path->ops->update_pmtu(path, mtu);
-}
-#endif
-
 /* Well... that's _TASK_. We need to scan through transformation
  * list and figure out what mss tcp should generate in order to
  * final datagram fit to mtu. Mama mia... :-)
@@ -1723,52 +1134,99 @@ static int xfrm_get_mss(struct dst_entry *dst, u32 mtu)
        return res + dst->header_len;
 }
 
-struct dst_ops xfrm4_dst_ops = {
-       .family =               AF_INET,
-       .protocol =             __constant_htons(ETH_P_IP),
-       .gc =                   xfrm4_garbage_collect,
-       .check =                xfrm_dst_check,
-       .destroy =              xfrm_dst_destroy,
-       .negative_advice =      xfrm_negative_advice,
-       .link_failure =         xfrm_link_failure,
-       .update_pmtu =          xfrm4_update_pmtu,
-       .get_mss =              xfrm_get_mss,
-       .gc_thresh =            1024,
-       .entry_size =           sizeof(struct xfrm_dst),
-};
+int xfrm_policy_register_afinfo(struct xfrm_policy_afinfo *afinfo)
+{
+       int err = 0;
+       if (unlikely(afinfo == NULL))
+               return -EINVAL;
+       if (unlikely(afinfo->family >= NPROTO))
+               return -EAFNOSUPPORT;
+       write_lock(&xfrm_policy_afinfo_lock);
+       if (unlikely(xfrm_policy_afinfo[afinfo->family] != NULL))
+               err = -ENOBUFS;
+       else {
+               struct dst_ops *dst_ops = afinfo->dst_ops;
+               if (likely(dst_ops->kmem_cachep == NULL))
+                       dst_ops->kmem_cachep = xfrm_dst_cache;
+               if (likely(dst_ops->check == NULL))
+                       dst_ops->check = xfrm_dst_check;
+               if (likely(dst_ops->destroy == NULL))
+                       dst_ops->destroy = xfrm_dst_destroy;
+               if (likely(dst_ops->negative_advice == NULL))
+                       dst_ops->negative_advice = xfrm_negative_advice;
+               if (likely(dst_ops->link_failure == NULL))
+                       dst_ops->link_failure = xfrm_link_failure;
+               if (likely(dst_ops->get_mss == NULL))
+                       dst_ops->get_mss = xfrm_get_mss;
+               if (likely(afinfo->garbage_collect == NULL))
+                       afinfo->garbage_collect = __xfrm_garbage_collect;
+               xfrm_policy_afinfo[afinfo->family] = afinfo;
+       }
+       write_unlock(&xfrm_policy_afinfo_lock);
+       return err;
+}
 
-#if defined (CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
-struct dst_ops xfrm6_dst_ops = {
-       .family =               AF_INET6,
-       .protocol =             __constant_htons(ETH_P_IPV6),
-       .gc =                   xfrm6_garbage_collect,
-       .check =                xfrm_dst_check,
-       .destroy =              xfrm_dst_destroy,
-       .negative_advice =      xfrm_negative_advice,
-       .link_failure =         xfrm_link_failure,
-       .update_pmtu =          xfrm6_update_pmtu,
-       .get_mss =              xfrm_get_mss,
-       .gc_thresh =            1024,
-       .entry_size =           sizeof(struct xfrm_dst),
-};
-#endif /* CONFIG_IPV6 || CONFIG_IPV6_MODULE */
+int xfrm_policy_unregister_afinfo(struct xfrm_policy_afinfo *afinfo)
+{
+       int err = 0;
+       if (unlikely(afinfo == NULL))
+               return -EINVAL;
+       if (unlikely(afinfo->family >= NPROTO))
+               return -EAFNOSUPPORT;
+       write_lock(&xfrm_policy_afinfo_lock);
+       if (likely(xfrm_policy_afinfo[afinfo->family] != NULL)) {
+               if (unlikely(xfrm_policy_afinfo[afinfo->family] != afinfo))
+                       err = -EINVAL;
+               else {
+                       struct dst_ops *dst_ops = afinfo->dst_ops;
+                       xfrm_policy_afinfo[afinfo->family] = NULL;
+                       dst_ops->kmem_cachep = NULL;
+                       dst_ops->check = NULL;
+                       dst_ops->destroy = NULL;
+                       dst_ops->negative_advice = NULL;
+                       dst_ops->link_failure = NULL;
+                       dst_ops->get_mss = NULL;
+                       afinfo->garbage_collect = NULL;
+               }
+       }
+       write_unlock(&xfrm_policy_afinfo_lock);
+       return err;
+}
 
-void __init xfrm_init(void)
+struct xfrm_policy_afinfo *xfrm_policy_get_afinfo(unsigned short family)
 {
-       xfrm4_dst_ops.kmem_cachep = kmem_cache_create("xfrm4_dst_cache",
-                                                     sizeof(struct xfrm_dst),
-                                                     0, SLAB_HWCACHE_ALIGN,
-                                                     NULL, NULL);
+       struct xfrm_policy_afinfo *afinfo;
+       if (unlikely(family >= NPROTO))
+               return NULL;
+       read_lock(&xfrm_policy_afinfo_lock);
+       afinfo = xfrm_policy_afinfo[family];
+       if (likely(afinfo != NULL))
+               read_lock(&afinfo->lock);
+       read_unlock(&xfrm_policy_afinfo_lock);
+       return afinfo;
+}
 
-       if (!xfrm4_dst_ops.kmem_cachep)
-               panic("IP: failed to allocate xfrm4_dst_cache\n");
+void xfrm_policy_put_afinfo(struct xfrm_policy_afinfo *afinfo)
+{
+       if (unlikely(afinfo == NULL))
+               return;
+       read_unlock(&afinfo->lock);
+}
 
-#if defined (CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
-       xfrm6_dst_ops.kmem_cachep = xfrm4_dst_ops.kmem_cachep;
-#endif
+void __init xfrm_policy_init(void)
+{
+       xfrm_dst_cache = kmem_cache_create("xfrm_dst_cache",
+                                          sizeof(struct xfrm_dst),
+                                          0, SLAB_HWCACHE_ALIGN,
+                                          NULL, NULL);
+       if (!xfrm_dst_cache)
+               panic("XFRM: failed to allocate xfrm_dst_cache\n");
+}
 
-       flow_cache_init();
+void __init xfrm_init(void)
+{
        xfrm_state_init();
-       xfrm_input_init();
+       flow_cache_init();
+       xfrm_policy_init();
 }
 
index a0aa440c8cd4dcf1365846bab2faa69f1edd2adf..2a67acf31358afc0f8b377f49cee04629d9b5825 100644 (file)
@@ -1,15 +1,19 @@
-/* Changes
+/*
+ * xfrm_state.c
  *
- *     Mitsuru KANDA @USAGI       : IPv6 Support 
- *     Kazunori MIYAZAWA @USAGI   :
- *     Kunihiro Ishiguro          :
+ * Changes:
+ *     Mitsuru KANDA @USAGI
+ *     Kazunori MIYAZAWA @USAGI
+ *     Kunihiro Ishiguro
+ *             IPv6 support
+ *     YOSHIFUJI Hideaki @USAGI
+ *             Split up af-specific functions
  *     
  */
 
 #include <net/xfrm.h>
 #include <linux/pfkeyv2.h>
 #include <linux/ipsec.h>
-#include <net/ipv6.h>
 
 /* Each xfrm_state may be linked to two tables:
 
@@ -20,8 +24,6 @@
 
 static spinlock_t xfrm_state_lock = SPIN_LOCK_UNLOCKED;
 
-#define XFRM_DST_HSIZE         1024
-
 /* Hash table to find appropriate SA towards given target (endpoint
  * of tunnel or destination of transport mode) allowed by selector.
  *
@@ -33,7 +35,8 @@ static struct list_head xfrm_state_byspi[XFRM_DST_HSIZE];
 
 DECLARE_WAIT_QUEUE_HEAD(km_waitq);
 
-#define ACQ_EXPIRES 30
+static rwlock_t xfrm_state_afinfo_lock = RW_LOCK_UNLOCKED;
+static struct xfrm_state_afinfo *xfrm_state_afinfo[NPROTO];
 
 static void __xfrm_state_delete(struct xfrm_state *x);
 
@@ -214,138 +217,37 @@ restart:
        wake_up(&km_waitq);
 }
 
-struct xfrm_state *
-xfrm4_state_find(u32 daddr, u32 saddr, struct flowi *fl, struct xfrm_tmpl *tmpl,
-                struct xfrm_policy *pol, int *err)
+static int
+xfrm_init_tempsel(struct xfrm_state *x, struct flowi *fl,
+                 struct xfrm_tmpl *tmpl,
+                 xfrm_address_t *daddr, xfrm_address_t *saddr,
+                 unsigned short family)
 {
-       unsigned h = ntohl(daddr);
-       struct xfrm_state *x;
-       int acquire_in_progress = 0;
-       int error = 0;
-       struct xfrm_state *best = NULL;
-
-       h = (h ^ (h>>16)) % XFRM_DST_HSIZE;
-
-       spin_lock_bh(&xfrm_state_lock);
-       list_for_each_entry(x, xfrm_state_bydst+h, bydst) {
-               if (x->props.family == AF_INET &&
-                   daddr == x->id.daddr.xfrm4_addr &&
-                   x->props.reqid == tmpl->reqid &&
-                   (saddr == x->props.saddr.xfrm4_addr || !saddr || !x->props.saddr.xfrm4_addr) &&
-                   tmpl->mode == x->props.mode &&
-                   tmpl->id.proto == x->id.proto) {
-                       /* Resolution logic:
-                          1. There is a valid state with matching selector.
-                             Done.
-                          2. Valid state with inappropriate selector. Skip.
-
-                          Entering area of "sysdeps".
-
-                          3. If state is not valid, selector is temporary,
-                             it selects only session which triggered
-                             previous resolution. Key manager will do
-                             something to install a state with proper
-                             selector.
-                        */
-                       if (x->km.state == XFRM_STATE_VALID) {
-                               if (!xfrm4_selector_match(&x->sel, fl))
-                                       continue;
-                               if (!best ||
-                                   best->km.dying > x->km.dying ||
-                                   (best->km.dying == x->km.dying &&
-                                    best->curlft.add_time < x->curlft.add_time))
-                                       best = x;
-                       } else if (x->km.state == XFRM_STATE_ACQ) {
-                               acquire_in_progress = 1;
-                       } else if (x->km.state == XFRM_STATE_ERROR ||
-                                  x->km.state == XFRM_STATE_EXPIRED) {
-                               if (xfrm4_selector_match(&x->sel, fl))
-                                       error = 1;
-                       }
-               }
-       }
-
-       if (best) {
-               atomic_inc(&best->refcnt);
-               spin_unlock_bh(&xfrm_state_lock);
-               return best;
-       }
-
-       x = NULL;
-       if (!error && !acquire_in_progress &&
-           ((x = xfrm_state_alloc()) != NULL)) {
-               /* Initialize temporary selector matching only
-                * to current session. */
-
-               x->sel.daddr.xfrm4_addr = fl->fl4_dst;
-               x->sel.daddr.xfrm4_mask = ~0;
-               x->sel.saddr.xfrm4_addr = fl->fl4_src;
-               x->sel.saddr.xfrm4_mask = ~0;
-               x->sel.dport = fl->uli_u.ports.dport;
-               x->sel.dport_mask = ~0;
-               x->sel.sport = fl->uli_u.ports.sport;
-               x->sel.sport_mask = ~0;
-               x->sel.prefixlen_d = 32;
-               x->sel.prefixlen_s = 32;
-               x->sel.proto = fl->proto;
-               x->sel.ifindex = fl->oif;
-               x->id = tmpl->id;
-               if (x->id.daddr.xfrm4_addr == 0)
-                       x->id.daddr.xfrm4_addr = daddr;
-               x->props.family = AF_INET;
-               x->props.saddr = tmpl->saddr;
-               if (x->props.saddr.xfrm4_addr == 0)
-                       x->props.saddr.xfrm4_addr = saddr;
-               x->props.mode = tmpl->mode;
-               x->props.reqid = tmpl->reqid;
-               x->props.family = AF_INET;
-
-               if (km_query(x, tmpl, pol) == 0) {
-                       x->km.state = XFRM_STATE_ACQ;
-                       list_add_tail(&x->bydst, xfrm_state_bydst+h);
-                       atomic_inc(&x->refcnt);
-                       if (x->id.spi) {
-                               h = ntohl(x->id.daddr.xfrm4_addr^x->id.spi^x->id.proto);
-                               h = (h ^ (h>>10) ^ (h>>20)) % XFRM_DST_HSIZE;
-                               list_add(&x->byspi, xfrm_state_byspi+h);
-                               atomic_inc(&x->refcnt);
-                       }
-                       x->lft.hard_add_expires_seconds = ACQ_EXPIRES;
-                       atomic_inc(&x->refcnt);
-                       mod_timer(&x->timer, ACQ_EXPIRES*HZ);
-               } else {
-                       x->km.state = XFRM_STATE_DEAD;
-                       xfrm_state_put(x);
-                       x = NULL;
-                       error = 1;
-               }
-       }
-       spin_unlock_bh(&xfrm_state_lock);
-       if (!x)
-               *err = acquire_in_progress ? -EAGAIN :
-                       (error ? -ESRCH : -ENOMEM);
-       return x;
+       struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
+       if (!afinfo)
+               return -1;
+       afinfo->init_tempsel(x, fl, tmpl, daddr, saddr);
+       xfrm_state_put_afinfo(afinfo);
+       return 0;
 }
 
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
 struct xfrm_state *
-xfrm6_state_find(struct in6_addr *daddr, struct in6_addr *saddr, struct flowi *fl, struct xfrm_tmpl *tmpl,
-               struct xfrm_policy *pol, int *err)
+xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr, 
+               struct flowi *fl, struct xfrm_tmpl *tmpl,
+               struct xfrm_policy *pol, int *err,
+               unsigned short family)
 {
-       unsigned h = ntohl(daddr->s6_addr32[2]^daddr->s6_addr32[3]);
+       unsigned h = xfrm_dst_hash(daddr, family);
        struct xfrm_state *x;
        int acquire_in_progress = 0;
        int error = 0;
        struct xfrm_state *best = NULL;
 
-       h = (h ^ (h>>16)) % XFRM_DST_HSIZE;
-
        spin_lock_bh(&xfrm_state_lock);
        list_for_each_entry(x, xfrm_state_bydst+h, bydst) {
-               if (x->props.family == AF_INET6&&
-                   !ipv6_addr_cmp(daddr, (struct in6_addr *)&x->id.daddr) &&
+               if (x->props.family == family &&
                    x->props.reqid == tmpl->reqid &&
-                   (!ipv6_addr_cmp(saddr, (struct in6_addr *)&x->props.saddr)|| ipv6_addr_any(saddr)) &&
+                   xfrm_state_addr_check(x, daddr, saddr, family) &&
                    tmpl->mode == x->props.mode &&
                    tmpl->id.proto == x->id.proto) {
                        /* Resolution logic:
@@ -362,7 +264,7 @@ xfrm6_state_find(struct in6_addr *daddr, struct in6_addr *saddr, struct flowi *f
                              selector.
                         */
                        if (x->km.state == XFRM_STATE_VALID) {
-                               if (!xfrm6_selector_match(&x->sel, fl))
+                               if (!xfrm_selector_match(&x->sel, fl, family))
                                        continue;
                                if (!best ||
                                    best->km.dying > x->km.dying ||
@@ -373,7 +275,7 @@ xfrm6_state_find(struct in6_addr *daddr, struct in6_addr *saddr, struct flowi *f
                                acquire_in_progress = 1;
                        } else if (x->km.state == XFRM_STATE_ERROR ||
                                   x->km.state == XFRM_STATE_EXPIRED) {
-                               if (xfrm6_selector_match(&x->sel, fl))
+                               if (xfrm_selector_match(&x->sel, fl, family))
                                        error = 1;
                        }
                }
@@ -384,45 +286,29 @@ xfrm6_state_find(struct in6_addr *daddr, struct in6_addr *saddr, struct flowi *f
                spin_unlock_bh(&xfrm_state_lock);
                return best;
        }
+
        x = NULL;
        if (!error && !acquire_in_progress &&
            ((x = xfrm_state_alloc()) != NULL)) {
                /* Initialize temporary selector matching only
                 * to current session. */
+               xfrm_init_tempsel(x, fl, tmpl, daddr, saddr, family);
+
                memcpy(&x->sel.daddr, fl->fl6_dst, sizeof(struct in6_addr));
                memcpy(&x->sel.saddr, fl->fl6_src, sizeof(struct in6_addr));
-               x->sel.dport = fl->uli_u.ports.dport;
-               x->sel.dport_mask = ~0;
-               x->sel.sport = fl->uli_u.ports.sport;
-               x->sel.sport_mask = ~0;
-               x->sel.prefixlen_d = 128;
-               x->sel.prefixlen_s = 128;
-               x->sel.proto = fl->proto;
-               x->sel.ifindex = fl->oif;
-               x->id = tmpl->id;
-               if (ipv6_addr_any((struct in6_addr*)&x->id.daddr))
-                       memcpy(&x->id.daddr, daddr, sizeof(x->sel.daddr));
-               memcpy(&x->props.saddr, &tmpl->saddr, sizeof(x->props.saddr));
-               if (ipv6_addr_any((struct in6_addr*)&x->props.saddr))
-                       memcpy(&x->props.saddr, saddr, sizeof(x->props.saddr));
-               x->props.mode = tmpl->mode;
-               x->props.reqid = tmpl->reqid;
-               x->props.family = AF_INET6;
 
                if (km_query(x, tmpl, pol) == 0) {
                        x->km.state = XFRM_STATE_ACQ;
                        list_add_tail(&x->bydst, xfrm_state_bydst+h);
                        atomic_inc(&x->refcnt);
                        if (x->id.spi) {
-                               struct in6_addr *addr = (struct in6_addr*)&x->id.daddr;
-                               h = ntohl((addr->s6_addr32[2]^addr->s6_addr32[3])^x->id.spi^x->id.proto);
-                               h = (h ^ (h>>10) ^ (h>>20)) % XFRM_DST_HSIZE;
+                               h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, family);
                                list_add(&x->byspi, xfrm_state_byspi+h);
                                atomic_inc(&x->refcnt);
                        }
-                       x->lft.hard_add_expires_seconds = ACQ_EXPIRES;
+                       x->lft.hard_add_expires_seconds = XFRM_ACQ_EXPIRES;
                        atomic_inc(&x->refcnt);
-                       mod_timer(&x->timer, ACQ_EXPIRES*HZ);
+                       mod_timer(&x->timer, XFRM_ACQ_EXPIRES*HZ);
                } else {
                        x->km.state = XFRM_STATE_DEAD;
                        xfrm_state_put(x);
@@ -436,36 +322,17 @@ xfrm6_state_find(struct in6_addr *daddr, struct in6_addr *saddr, struct flowi *f
                        (error ? -ESRCH : -ENOMEM);
        return x;
 }
-#endif /* CONFIG_IPV6 || CONFIG_IPV6_MODULE */
 
 void xfrm_state_insert(struct xfrm_state *x)
 {
-       unsigned h = 0;
-
-       switch (x->props.family) {
-       case AF_INET:
-               h = ntohl(x->id.daddr.xfrm4_addr);
-               break;
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-       case AF_INET6:
-               h = ntohl(x->id.daddr.a6[2]^x->id.daddr.a6[3]);
-               break;
-#endif
-       default:
-               return;
-       }
-
-       h = (h ^ (h>>16)) % XFRM_DST_HSIZE;
+       unsigned h = xfrm_dst_hash(&x->id.daddr, x->props.family);
 
        spin_lock_bh(&xfrm_state_lock);
        list_add(&x->bydst, xfrm_state_bydst+h);
        atomic_inc(&x->refcnt);
 
-       if (x->props.family == AF_INET)
-               h = ntohl(x->id.daddr.xfrm4_addr^x->id.spi^x->id.proto);
-       else
-               h = ntohl(x->id.daddr.a6[2]^x->id.daddr.a6[3]^x->id.spi^x->id.proto);
-       h = (h ^ (h>>10) ^ (h>>20)) % XFRM_DST_HSIZE;
+       h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, x->props.family);
+
        list_add(&x->byspi, xfrm_state_byspi+h);
        atomic_inc(&x->refcnt);
 
@@ -487,7 +354,7 @@ int xfrm_state_check_expire(struct xfrm_state *x)
        if (x->curlft.bytes >= x->lft.hard_byte_limit ||
            x->curlft.packets >= x->lft.hard_packet_limit) {
                km_expired(x);
-               if (!mod_timer(&x->timer, jiffies + ACQ_EXPIRES*HZ))
+               if (!mod_timer(&x->timer, jiffies + XFRM_ACQ_EXPIRES*HZ))
                        atomic_inc(&x->refcnt);
                return -EINVAL;
        }
@@ -512,158 +379,37 @@ int xfrm_state_check_space(struct xfrm_state *x, struct sk_buff *skb)
 }
 
 struct xfrm_state *
-xfrm4_state_lookup(u32 daddr, u32 spi, u8 proto)
+xfrm_state_lookup(xfrm_address_t *daddr, u32 spi, u8 proto,
+                 unsigned short family)
 {
-       unsigned h = ntohl(daddr^spi^proto);
        struct xfrm_state *x;
-
-       h = (h ^ (h>>10) ^ (h>>20)) % XFRM_DST_HSIZE;
+       struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
+       if (!afinfo)
+               return NULL;
 
        spin_lock_bh(&xfrm_state_lock);
-       list_for_each_entry(x, xfrm_state_byspi+h, byspi) {
-               if (x->props.family == AF_INET &&
-                   spi == x->id.spi &&
-                   daddr == x->id.daddr.xfrm4_addr &&
-                   proto == x->id.proto) {
-                       atomic_inc(&x->refcnt);
-                       spin_unlock_bh(&xfrm_state_lock);
-                       return x;
-               }
-       }
+       x = afinfo->state_lookup(daddr, spi, proto);
        spin_unlock_bh(&xfrm_state_lock);
-       return NULL;
+       xfrm_state_put_afinfo(afinfo);
+       return x;
 }
 
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
 struct xfrm_state *
-xfrm6_state_lookup(struct in6_addr *daddr, u32 spi, u8 proto)
+xfrm_find_acq(u8 mode, u16 reqid, u8 proto, 
+             xfrm_address_t *daddr, xfrm_address_t *saddr, 
+             int create, unsigned short family)
 {
-       unsigned h = ntohl(daddr->s6_addr32[2]^daddr->s6_addr32[3]^spi^proto);
        struct xfrm_state *x;
-
-       h = (h ^ (h>>10) ^ (h>>20)) % XFRM_DST_HSIZE;
+       struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
+       if (!afinfo)
+               return NULL;
 
        spin_lock_bh(&xfrm_state_lock);
-       list_for_each_entry(x, xfrm_state_byspi+h, byspi) {
-               if (x->props.family == AF_INET6 &&
-                   spi == x->id.spi &&
-                   !ipv6_addr_cmp(daddr, (struct in6_addr *)x->id.daddr.a6) &&
-                   proto == x->id.proto) {
-                       atomic_inc(&x->refcnt);
-                       spin_unlock_bh(&xfrm_state_lock);
-                       return x;
-               }
-       }
+       x = afinfo->find_acq(mode, reqid, proto, daddr, saddr, create);
        spin_unlock_bh(&xfrm_state_lock);
-       return NULL;
-}
-#endif
-
-struct xfrm_state *
-xfrm_find_acq(u8 mode, u16 reqid, u8 proto, u32 daddr, u32 saddr, int create)
-{
-       struct xfrm_state *x, *x0;
-       unsigned h = ntohl(daddr);
-
-       h = (h ^ (h>>16)) % XFRM_DST_HSIZE;
-       x0 = NULL;
-
-       spin_lock_bh(&xfrm_state_lock);
-       list_for_each_entry(x, xfrm_state_bydst+h, bydst) {
-               if (x->props.family == AF_INET &&
-                   daddr == x->id.daddr.xfrm4_addr &&
-                   mode == x->props.mode &&
-                   proto == x->id.proto &&
-                   saddr == x->props.saddr.xfrm4_addr &&
-                   reqid == x->props.reqid &&
-                   x->km.state == XFRM_STATE_ACQ) {
-                           if (!x0)
-                                   x0 = x;
-                           if (x->id.spi)
-                                   continue;
-                           x0 = x;
-                           break;
-                   }
-       }
-       if (x0) {
-               atomic_inc(&x0->refcnt);
-       } else if (create && (x0 = xfrm_state_alloc()) != NULL) {
-               x0->sel.daddr.xfrm4_addr = daddr;
-               x0->sel.daddr.xfrm4_mask = ~0;
-               x0->sel.saddr.xfrm4_addr = saddr;
-               x0->sel.saddr.xfrm4_mask = ~0;
-               x0->sel.prefixlen_d = 32;
-               x0->sel.prefixlen_s = 32;
-               x0->props.saddr.xfrm4_addr = saddr;
-               x0->km.state = XFRM_STATE_ACQ;
-               x0->id.daddr.xfrm4_addr = daddr;
-               x0->id.proto = proto;
-               x0->props.mode = mode;
-               x0->props.reqid = reqid;
-               x0->props.family = AF_INET;
-               x0->lft.hard_add_expires_seconds = ACQ_EXPIRES;
-               atomic_inc(&x0->refcnt);
-               mod_timer(&x0->timer, jiffies + ACQ_EXPIRES*HZ);
-               atomic_inc(&x0->refcnt);
-               list_add_tail(&x0->bydst, xfrm_state_bydst+h);
-               wake_up(&km_waitq);
-       }
-       spin_unlock_bh(&xfrm_state_lock);
-       return x0;
-}
-
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-struct xfrm_state *
-xfrm6_find_acq(u8 mode, u16 reqid, u8 proto, struct in6_addr *daddr, struct in6_addr *saddr, int create)
-{
-       struct xfrm_state *x, *x0;
-       unsigned h = ntohl(daddr->s6_addr32[2]^daddr->s6_addr32[3]);
-
-       h = (h ^ (h>>16)) % XFRM_DST_HSIZE;
-       x0 = NULL;
-
-       spin_lock_bh(&xfrm_state_lock);
-       list_for_each_entry(x, xfrm_state_bydst+h, bydst) {
-               if (x->props.family == AF_INET6 &&
-                   !ipv6_addr_cmp(daddr, (struct in6_addr *)x->id.daddr.a6) &&
-                   mode == x->props.mode &&
-                   proto == x->id.proto &&
-                   !ipv6_addr_cmp(saddr, (struct in6_addr *)x->props.saddr.a6) &&
-                   reqid == x->props.reqid &&
-                   x->km.state == XFRM_STATE_ACQ) {
-                           if (!x0)
-                                   x0 = x;
-                           if (x->id.spi)
-                                   continue;
-                           x0 = x;
-                           break;
-                   }
-       }
-       if (x0) {
-               atomic_inc(&x0->refcnt);
-       } else if (create && (x0 = xfrm_state_alloc()) != NULL) {
-               memcpy(x0->sel.daddr.a6, daddr, sizeof(struct in6_addr));
-               memcpy(x0->sel.saddr.a6, saddr, sizeof(struct in6_addr));
-               x0->sel.prefixlen_d = 128;
-               x0->sel.prefixlen_s = 128;
-               memcpy(x0->props.saddr.a6, saddr, sizeof(struct in6_addr));
-               x0->km.state = XFRM_STATE_ACQ;
-               memcpy(x0->id.daddr.a6, daddr, sizeof(struct in6_addr));
-               x0->id.proto = proto;
-               x0->props.family = AF_INET6;
-               x0->props.mode = mode;
-               x0->props.reqid = reqid;
-               x0->lft.hard_add_expires_seconds = ACQ_EXPIRES;
-               atomic_inc(&x0->refcnt);
-               mod_timer(&x0->timer, jiffies + ACQ_EXPIRES*HZ);
-               atomic_inc(&x0->refcnt);
-               list_add_tail(&x0->bydst, xfrm_state_bydst+h);
-               wake_up(&km_waitq);
-       }
-       spin_unlock_bh(&xfrm_state_lock);
-       return x0;
+       xfrm_state_put_afinfo(afinfo);
+       return x;
 }
-#endif
 
 /* Silly enough, but I'm lazy to build resolution list */
 
@@ -697,18 +443,7 @@ xfrm_alloc_spi(struct xfrm_state *x, u32 minspi, u32 maxspi)
                return;
 
        if (minspi == maxspi) {
-               switch(x->props.family) {
-               case AF_INET:
-                       x0 = xfrm4_state_lookup(x->id.daddr.xfrm4_addr, minspi, x->id.proto);
-                       break;
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-               case AF_INET6:
-                       x0 = xfrm6_state_lookup((struct in6_addr*)x->id.daddr.a6, minspi, x->id.proto);
-                       break;
-#endif
-               default:
-                       x0 = NULL;
-               }
+               x0 = xfrm_state_lookup(&x->id.daddr, minspi, x->id.proto, x->props.family);
                if (x0) {
                        xfrm_state_put(x0);
                        return;
@@ -720,18 +455,7 @@ xfrm_alloc_spi(struct xfrm_state *x, u32 minspi, u32 maxspi)
                maxspi = ntohl(maxspi);
                for (h=0; h<maxspi-minspi+1; h++) {
                        spi = minspi + net_random()%(maxspi-minspi+1);
-                       switch(x->props.family) {
-                       case AF_INET:
-                               x0 = xfrm4_state_lookup(x->id.daddr.xfrm4_addr, minspi, x->id.proto);
-                               break;
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-                       case AF_INET6:
-                               x0 = xfrm6_state_lookup((struct in6_addr*)x->id.daddr.a6, minspi, x->id.proto);
-                               break;
-#endif
-                       default:
-                               x0 = NULL;
-                       }
+                       x0 = xfrm_state_lookup(&x->id.daddr, minspi, x->id.proto, x->props.family);
                        if (x0 == NULL)
                                break;
                        xfrm_state_put(x0);
@@ -740,19 +464,7 @@ xfrm_alloc_spi(struct xfrm_state *x, u32 minspi, u32 maxspi)
        }
        if (x->id.spi) {
                spin_lock_bh(&xfrm_state_lock);
-               switch(x->props.family) {
-               case AF_INET:
-                       h = ntohl(x->id.daddr.xfrm4_addr^x->id.spi^x->id.proto);
-                       break;
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-               case AF_INET6:
-                       h = ntohl(x->id.daddr.a6[2]^x->id.daddr.a6[3]^x->id.spi^x->id.proto);
-                       break;
-#endif
-               default:
-                       h = 0;  /* XXX */
-               }
-               h = (h ^ (h>>10) ^ (h>>20)) % XFRM_DST_HSIZE;
+               h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, x->props.family);
                list_add(&x->byspi, xfrm_state_byspi+h);
                atomic_inc(&x->refcnt);
                spin_unlock_bh(&xfrm_state_lock);
@@ -845,18 +557,7 @@ int xfrm_check_selectors(struct xfrm_state **x, int n, struct flowi *fl)
 
        for (i=0; i<n; i++) {
                int match;
-               switch(x[i]->props.family) {
-               case AF_INET:
-                       match = xfrm4_selector_match(&x[i]->sel, fl);
-                       break;
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-               case AF_INET6:
-                       match = xfrm6_selector_match(&x[i]->sel, fl);
-                       break;
-#endif
-               default:
-                       match = 0;
-               }
+               match = xfrm_selector_match(&x[i]->sel, fl, x[i]->props.family);
                if (!match)
                        return -EINVAL;
        }
@@ -958,6 +659,66 @@ int xfrm_unregister_km(struct xfrm_mgr *km)
        return 0;
 }
 
+int xfrm_state_register_afinfo(struct xfrm_state_afinfo *afinfo)
+{
+       int err = 0;
+       if (unlikely(afinfo == NULL))
+               return -EINVAL;
+       if (unlikely(afinfo->family >= NPROTO))
+               return -EAFNOSUPPORT;
+       write_lock(&xfrm_state_afinfo_lock);
+       if (unlikely(xfrm_state_afinfo[afinfo->family] != NULL))
+               err = -ENOBUFS;
+       else {
+               afinfo->state_bydst = xfrm_state_bydst;
+               afinfo->state_byspi = xfrm_state_byspi;
+               xfrm_state_afinfo[afinfo->family] = afinfo;
+       }
+       write_unlock(&xfrm_state_afinfo_lock);
+       return err;
+}
+
+int xfrm_state_unregister_afinfo(struct xfrm_state_afinfo *afinfo)
+{
+       int err = 0;
+       if (unlikely(afinfo == NULL))
+               return -EINVAL;
+       if (unlikely(afinfo->family >= NPROTO))
+               return -EAFNOSUPPORT;
+       write_lock(&xfrm_state_afinfo_lock);
+       if (likely(xfrm_state_afinfo[afinfo->family] != NULL)) {
+               if (unlikely(xfrm_state_afinfo[afinfo->family] != afinfo))
+                       err = -EINVAL;
+               else {
+                       xfrm_state_afinfo[afinfo->family] = NULL;
+                       afinfo->state_byspi = NULL;
+                       afinfo->state_bydst = NULL;
+               }
+       }
+       write_unlock(&xfrm_state_afinfo_lock);
+       return err;
+}
+
+struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned short family)
+{
+       struct xfrm_state_afinfo *afinfo;
+       if (unlikely(family >= NPROTO))
+               return NULL;
+       read_lock(&xfrm_state_afinfo_lock);
+       afinfo = xfrm_state_afinfo[family];
+       if (likely(afinfo != NULL))
+               read_lock(&afinfo->lock);
+       read_unlock(&xfrm_state_afinfo_lock);
+       return afinfo;
+}
+
+void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo)
+{
+       if (unlikely(afinfo == NULL))
+               return;
+       read_unlock(&afinfo->lock);
+}
+
 void __init xfrm_state_init(void)
 {
        int i;
index ba50e3bf969b8b4913117653e01f124cc736c2a6..1a1d5be1a16b4153ba14feda61aa17b01a89f06e 100644 (file)
@@ -2,11 +2,11 @@
  *
  * Copyright (C) 2002 David S. Miller (davem@redhat.com)
  *
- * Changes
- *
- *     Mitsuru KANDA @USAGI       : IPv6 Support 
- *     Kazunori MIYAZAWA @USAGI   :
- *     Kunihiro Ishiguro          :
+ * Changes:
+ *     Mitsuru KANDA @USAGI
+ *     Kazunori MIYAZAWA @USAGI
+ *     Kunihiro Ishiguro
+ *             IPv6 support
  *     
  */
 
@@ -24,9 +24,6 @@
 #include <linux/ipsec.h>
 #include <linux/init.h>
 #include <linux/security.h>
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-#include <linux/in6.h>
-#endif
 #include <net/sock.h>
 #include <net/xfrm.h>
 
@@ -191,19 +188,7 @@ static struct xfrm_state *xfrm_state_construct(struct xfrm_usersa_info *p,
                goto error;
 
        err = -ENOENT;
-       switch (x->props.family) {
-       case AF_INET:
-               x->type = xfrm_get_type(x->id.proto);
-               break;
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-       case AF_INET6:
-               x->type = xfrm6_get_type(x->id.proto);
-               break;
-#endif
-       default:
-               x->type = NULL;
-               break;
-       }
+       x->type = xfrm_get_type(x->id.proto, x->props.family);
        if (x->type == NULL)
                goto error;
 
@@ -238,21 +223,7 @@ static int xfrm_add_sa(struct sk_buff *skb, struct nlmsghdr *nlh, void **xfrma)
        if (!x)
                return err;
 
-       switch (x->props.family) {
-       case AF_INET:
-               x1 = xfrm4_state_lookup(x->props.saddr.xfrm4_addr,
-                                       x->id.spi, x->id.proto);
-               break;
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-       case AF_INET6:
-               x1 = xfrm6_state_lookup((struct in6_addr*)x->props.saddr.a6,
-                                       x->id.spi, x->id.proto);
-               break;
-#endif
-       default:
-               x1 = NULL;
-               break;
-       }
+       x1 = xfrm_state_lookup(&x->props.saddr, x->id.spi, x->id.proto, x->props.family);
        if (x1) {
                xfrm_state_put(x);
                xfrm_state_put(x1);
@@ -269,19 +240,7 @@ static int xfrm_del_sa(struct sk_buff *skb, struct nlmsghdr *nlh, void **xfrma)
        struct xfrm_state *x;
        struct xfrm_usersa_id *p = NLMSG_DATA(nlh);
 
-       switch (p->family) {
-       case AF_INET:
-               x = xfrm4_state_lookup(p->saddr.xfrm4_addr, p->spi, p->proto);
-               break;
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-       case AF_INET6:
-               x = xfrm6_state_lookup((struct in6_addr*)p->saddr.a6, p->spi, p->proto);
-               break;
-#endif
-       default:
-               x = NULL;
-               break;
-       }
+       x = xfrm_state_lookup(&p->saddr, p->spi, p->proto, p->family);
        if (x == NULL)
                return -ESRCH;
 
@@ -399,19 +358,7 @@ static int xfrm_get_sa(struct sk_buff *skb, struct nlmsghdr *nlh, void **xfrma)
        struct sk_buff *resp_skb;
        int err;
 
-       switch (p->family) {
-       case AF_INET:
-               x = xfrm4_state_lookup(p->saddr.xfrm4_addr, p->spi, p->proto);
-               break;
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-       case AF_INET6:
-               x = xfrm6_state_lookup((struct in6_addr*)p->saddr.a6, p->spi, p->proto);
-               break;
-#endif
-       default:
-               x = NULL;
-               break;
-       }
+       x = xfrm_state_lookup(&p->saddr, p->spi, p->proto, p->family);
        err = -ESRCH;
        if (x == NULL)
                goto out_noput;
@@ -462,23 +409,10 @@ static int xfrm_alloc_userspi(struct sk_buff *skb, struct nlmsghdr *nlh, void **
        err = verify_userspi_info(p);
        if (err)
                goto out_noput;
-       switch (p->info.family) {
-       case AF_INET:
-               x = xfrm_find_acq(p->info.mode, p->info.reqid, p->info.id.proto,
-                                 p->info.sel.daddr.xfrm4_addr,
-                                 p->info.sel.saddr.xfrm4_addr, 1);
-               break;
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-       case AF_INET6:
-               x = xfrm6_find_acq(p->info.mode, p->info.reqid, p->info.id.proto,
-                                  (struct in6_addr*)p->info.sel.daddr.a6,
-                                  (struct in6_addr*)p->info.sel.saddr.a6, 1);
-               break;
-#endif
-       default:
-               x = NULL;
-               break;
-       }
+       x = xfrm_find_acq(p->info.mode, p->info.reqid, p->info.id.proto,
+                         &p->info.sel.daddr,
+                         &p->info.sel.saddr, 1,
+                         p->info.family);
        err = -ENOENT;
        if (x == NULL)
                goto out_noput;
index 3fec5c7e00938dc3b56b46a0fc16abb3e6d33056..d3080035e6c944df1ac60c43e264a9da024f6897 100644 (file)
@@ -8,7 +8,8 @@ ipv6-objs :=    af_inet6.o anycast.o ip6_output.o ip6_input.o addrconf.o sit.o \
                route.o ip6_fib.o ipv6_sockglue.o ndisc.o udp.o raw.o \
                protocol.o icmp.o mcast.o reassembly.o tcp_ipv6.o \
                exthdrs.o sysctl_net_ipv6.o datagram.o proc.o \
-               ip6_flowlabel.o ipv6_syms.o
+               ip6_flowlabel.o ipv6_syms.o \
+               xfrm6_policy.o xfrm6_state.o xfrm6_input.o
 
 obj-$(CONFIG_INET6_AH) += ah6.o
 obj-$(CONFIG_INET6_ESP) += esp6.o
index 468d94db3d298fdb9c7b13ce5fa47ee12e19801b..e35069d8e75dcc0512b6629335c5758b449e5a17 100644 (file)
@@ -228,7 +228,7 @@ void ah6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
            type != ICMPV6_PKT_TOOBIG)
                return;
 
-       x = xfrm6_state_lookup(&iph->daddr, ah->spi, IPPROTO_AH);
+       x = xfrm_state_lookup((xfrm_address_t *)&iph->daddr, ah->spi, IPPROTO_AH, AF_INET6);
        if (!x)
                return;
 
@@ -336,14 +336,14 @@ int __init ah6_init(void)
 {
        SET_MODULE_OWNER(&ah6_type);
 
-       if (xfrm6_register_type(&ah6_type) < 0) {
+       if (xfrm_register_type(&ah6_type, AF_INET6) < 0) {
                printk(KERN_INFO "ipv6 ah init: can't add xfrm type\n");
                return -EAGAIN;
        }
 
        if (inet6_add_protocol(&ah6_protocol, IPPROTO_AH) < 0) {
                printk(KERN_INFO "ipv6 ah init: can't add protocol\n");
-               xfrm6_unregister_type(&ah6_type);
+               xfrm_unregister_type(&ah6_type, AF_INET6);
                return -EAGAIN;
        }
 
@@ -355,7 +355,7 @@ static void __exit ah6_fini(void)
        if (inet6_del_protocol(&ah6_protocol, IPPROTO_AH) < 0)
                printk(KERN_INFO "ipv6 ah close: can't remove protocol\n");
 
-       if (xfrm6_unregister_type(&ah6_type) < 0)
+       if (xfrm_unregister_type(&ah6_type, AF_INET6) < 0)
                printk(KERN_INFO "ipv6 ah close: can't remove xfrm type\n");
 
 }
index 8dc3c0ebc083beec1738ae649fbe1c01fac981f4..19450a5025d278fcf36e33127ea21b4cc93c3feb 100644 (file)
@@ -377,7 +377,7 @@ void esp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
            type != ICMPV6_PKT_TOOBIG)
                return;
 
-       x = xfrm6_state_lookup(&iph->daddr, esph->spi, IPPROTO_ESP);
+       x = xfrm_state_lookup((xfrm_address_t *)&iph->daddr, esph->spi, IPPROTO_ESP, AF_INET6);
        if (!x)
                return;
        printk(KERN_DEBUG "pmtu discvovery on SA ESP/%08x/"
@@ -504,13 +504,13 @@ static struct inet6_protocol esp6_protocol = {
 int __init esp6_init(void)
 {
        SET_MODULE_OWNER(&esp6_type);
-       if (xfrm6_register_type(&esp6_type) < 0) {
+       if (xfrm_register_type(&esp6_type, AF_INET6) < 0) {
                printk(KERN_INFO "ipv6 esp init: can't add xfrm type\n");
                return -EAGAIN;
        }
        if (inet6_add_protocol(&esp6_protocol, IPPROTO_ESP) < 0) {
                printk(KERN_INFO "ipv6 esp init: can't add protocol\n");
-               xfrm6_unregister_type(&esp6_type);
+               xfrm_unregister_type(&esp6_type, AF_INET6);
                return -EAGAIN;
        }
 
@@ -521,7 +521,7 @@ static void __exit esp6_fini(void)
 {
        if (inet6_del_protocol(&esp6_protocol, IPPROTO_ESP) < 0)
                printk(KERN_INFO "ipv6 esp close: can't remove protocol\n");
-       if (xfrm6_unregister_type(&esp6_type) < 0)
+       if (xfrm_unregister_type(&esp6_type, AF_INET6) < 0)
                printk(KERN_INFO "ipv6 esp close: can't remove xfrm type\n");
 }
 
index e811463ebfb79f14cc03217efbd293263ada00aa..d8ead93cc7a2b871bd576190cc2f7440308b772b 100644 (file)
@@ -5,6 +5,7 @@
 #include <net/ipv6.h>
 #include <net/addrconf.h>
 #include <net/ip6_route.h>
+#include <net/xfrm.h>
 
 EXPORT_SYMBOL(ipv6_addr_type);
 EXPORT_SYMBOL(icmpv6_send);
@@ -31,3 +32,5 @@ EXPORT_SYMBOL(ipv6_get_saddr);
 EXPORT_SYMBOL(ipv6_chk_addr);
 EXPORT_SYMBOL(in6addr_any);
 EXPORT_SYMBOL(in6addr_loopback);
+EXPORT_SYMBOL(xfrm6_rcv);
+EXPORT_SYMBOL(xfrm6_clear_mutable_options);
index d0a19947ebf7412bf4d80154213ad8985ad0a9aa..85c6fe4fd0d96ae76bc15004e082a08025de2516 100644 (file)
@@ -1867,15 +1867,6 @@ ctl_table ipv6_route_table[] = {
 
 #endif
 
-int xfrm6_dst_lookup(struct xfrm_dst **dst, struct flowi *fl)
-{
-       int err = 0;
-       *dst = (struct xfrm_dst*)ip6_route_output(NULL, fl);
-       if (!*dst)
-               err = -ENETUNREACH;
-       return err;
-}
-
 void __init ip6_route_init(void)
 {
        ip6_dst_ops.kmem_cachep = kmem_cache_create("ip6_dst_cache",
@@ -1883,11 +1874,11 @@ void __init ip6_route_init(void)
                                                     0, SLAB_HWCACHE_ALIGN,
                                                     NULL, NULL);
        fib6_init();
-       xfrm_dst_lookup_register(xfrm6_dst_lookup, AF_INET6);
 #ifdef         CONFIG_PROC_FS
        proc_net_create("ipv6_route", 0, rt6_proc_info);
        proc_net_create("rt6_stats", 0, rt6_proc_stats);
 #endif
+       xfrm6_init();
 }
 
 #ifdef MODULE
@@ -1897,7 +1888,7 @@ void ip6_route_cleanup(void)
        proc_net_remove("ipv6_route");
        proc_net_remove("rt6_stats");
 #endif
-       xfrm_dst_lookup_unregister(AF_INET6);
+       xfrm6_fini();
        rt6_ifdown(NULL);
        fib6_gc_cleanup();
 }
diff --git a/net/ipv6/xfrm6_input.c b/net/ipv6/xfrm6_input.c
new file mode 100644 (file)
index 0000000..b498eef
--- /dev/null
@@ -0,0 +1,279 @@
+/*
+ * 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");
+}
+
diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c
new file mode 100644 (file)
index 0000000..9a31a22
--- /dev/null
@@ -0,0 +1,273 @@
+/*
+ * 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();
+}
diff --git a/net/ipv6/xfrm6_state.c b/net/ipv6/xfrm6_state.c
new file mode 100644 (file)
index 0000000..259ed5e
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * 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);
+}
+
index e9d2ebedb153867508c85baba3d3ac0191403715..257c153b4733cdc29a4207afa3e2b2dbbd1e6e03 100644 (file)
@@ -510,10 +510,8 @@ static int pfkey_sadb_addr2xfrm_addr(struct sadb_address *addr,
 {
        switch (((struct sockaddr*)(addr + 1))->sa_family) {
        case AF_INET:
-               xaddr->xfrm4_addr = 
+               xaddr->a4 = 
                        ((struct sockaddr_in *)(addr + 1))->sin_addr.s_addr;
-               if (addr->sadb_address_prefixlen)
-                       xaddr->xfrm4_mask = htonl(~0 << (32 - addr->sadb_address_prefixlen));
                return AF_INET;
 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
        case AF_INET6:
@@ -530,10 +528,11 @@ static int pfkey_sadb_addr2xfrm_addr(struct sadb_address *addr,
 
 static struct  xfrm_state *pfkey_xfrm_state_lookup(struct sadb_msg *hdr, void **ext_hdrs)
 {
-       struct xfrm_state *x;
        struct sadb_sa *sa;
        struct sadb_address *addr;
        uint16_t proto;
+       unsigned short family;
+       xfrm_address_t *xaddr;
 
        sa = (struct sadb_sa *) ext_hdrs[SADB_EXT_SA-1];
        if (sa == NULL)
@@ -548,23 +547,24 @@ static struct  xfrm_state *pfkey_xfrm_state_lookup(struct sadb_msg *hdr, void **
        if (addr == NULL)
                return NULL;
 
-       switch (((struct sockaddr *)(addr + 1))->sa_family) {
+       family = ((struct sockaddr *)(addr + 1))->sa_family;
+       switch (family) {
        case AF_INET:
-               x = xfrm4_state_lookup(((struct sockaddr_in *)(addr + 1))->sin_addr.s_addr,
-                                      sa->sadb_sa_spi, proto);
+               xaddr = (xfrm_address_t *)&((struct sockaddr_in *)(addr + 1))->sin_addr;
                break;
 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
        case AF_INET6:
-               x = xfrm6_state_lookup(&((struct sockaddr_in6 *)(addr + 1))->sin6_addr,
-                                      sa->sadb_sa_spi, proto);
+               xaddr = (xfrm_address_t *)&((struct sockaddr_in6 *)(addr + 1))->sin6_addr;
                break;
 #endif
        default:
-               x = NULL;
-               break;
+               xaddr = NULL;
        }
 
-       return x;
+       if (!xaddr)
+               return NULL;
+
+       return xfrm_state_lookup(xaddr, sa->sadb_sa_spi, proto, family);
 }
 
 #define PFKEY_ALIGN8(a) (1 + (((a) - 1) | (8 - 1)))
@@ -619,7 +619,7 @@ static struct sk_buff * pfkey_xfrm_state2msg(struct xfrm_state *x, int add_keys,
        /* identity & sensitivity */
 
        if ((x->props.family == AF_INET &&
-            x->sel.saddr.xfrm4_addr != x->props.saddr.xfrm4_addr)
+            x->sel.saddr.a4 != x->props.saddr.a4)
 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
            || (x->props.family == AF_INET6 &&
                memcmp (x->sel.saddr.a6, x->props.saddr.a6, sizeof (struct in6_addr)))
@@ -731,7 +731,7 @@ static struct sk_buff * pfkey_xfrm_state2msg(struct xfrm_state *x, int add_keys,
 
                sin = (struct sockaddr_in *) (addr + 1);
                sin->sin_family = AF_INET;
-               sin->sin_addr.s_addr = x->props.saddr.xfrm4_addr;
+               sin->sin_addr.s_addr = x->props.saddr.a4;
                sin->sin_port = 0;
                memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
        }
@@ -764,11 +764,11 @@ static struct sk_buff * pfkey_xfrm_state2msg(struct xfrm_state *x, int add_keys,
        if (x->props.family == AF_INET) {
                sin = (struct sockaddr_in *) (addr + 1);
                sin->sin_family = AF_INET;
-               sin->sin_addr.s_addr = x->id.daddr.xfrm4_addr;
+               sin->sin_addr.s_addr = x->id.daddr.a4;
                sin->sin_port = 0;
                memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
 
-               if (x->sel.saddr.xfrm4_addr != x->props.saddr.xfrm4_addr) {
+               if (x->sel.saddr.a4 != x->props.saddr.a4) {
                        addr = (struct sadb_address*) skb_put(skb, 
                                sizeof(struct sadb_address)+sockaddr_size);
                        addr->sadb_address_len = 
@@ -782,7 +782,7 @@ static struct sk_buff * pfkey_xfrm_state2msg(struct xfrm_state *x, int add_keys,
 
                        sin = (struct sockaddr_in *) (addr + 1);
                        sin->sin_family = AF_INET;
-                       sin->sin_addr.s_addr = x->sel.saddr.xfrm4_addr;
+                       sin->sin_addr.s_addr = x->sel.saddr.a4;
                        sin->sin_port = x->sel.sport;
                        memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
                }
@@ -1017,20 +1017,7 @@ static struct xfrm_state * pfkey_msg2xfrm_state(struct sadb_msg *hdr,
                x->sel.prefixlen_s = addr->sadb_address_prefixlen;
        }
 
-       switch (x->props.family) {
-       case AF_INET:
-               x->type = xfrm_get_type(proto);
-               break;
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-       case AF_INET6:
-               x->type = xfrm6_get_type(proto);
-               break;
-#endif
-       default:
-               x->type = NULL;
-               break;
-       }
-
+       x->type = xfrm_get_type(proto, x->props.family);
        if (x->type == NULL)
                goto out;
        if (x->type->init_state(x, NULL))
@@ -1061,10 +1048,12 @@ static int pfkey_getspi(struct sock *sk, struct sk_buff *skb, struct sadb_msg *h
        struct sadb_x_sa2 *sa2;
        struct sadb_address *saddr, *daddr;
        struct sadb_msg *out_hdr;
-       struct xfrm_state *x;
+       struct xfrm_state *x = NULL;
        u8 mode;
        u16 reqid;
        u8 proto;
+       unsigned short family;
+       xfrm_address_t *xsaddr = NULL, *xdaddr = NULL;
 
        if (!present_and_same_family(ext_hdrs[SADB_EXT_ADDRESS_SRC-1],
                                     ext_hdrs[SADB_EXT_ADDRESS_DST-1]))
@@ -1085,23 +1074,21 @@ static int pfkey_getspi(struct sock *sk, struct sk_buff *skb, struct sadb_msg *h
        saddr = ext_hdrs[SADB_EXT_ADDRESS_SRC-1];
        daddr = ext_hdrs[SADB_EXT_ADDRESS_DST-1];
 
-       switch (((struct sockaddr *)(saddr + 1))->sa_family) {
+       family = ((struct sockaddr *)(saddr + 1))->sa_family;
+       switch (family) {
        case AF_INET:
-               x = xfrm_find_acq(mode, reqid, proto,
-                                 ((struct sockaddr_in *)(daddr + 1))->sin_addr.s_addr,
-                                 ((struct sockaddr_in *)(saddr + 1))->sin_addr.s_addr, 1);
+               xdaddr = (xfrm_address_t *)&((struct sockaddr_in *)(daddr + 1))->sin_addr.s_addr;
+               xsaddr = (xfrm_address_t *)&((struct sockaddr_in *)(saddr + 1))->sin_addr.s_addr;
                break;
 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
        case AF_INET6:
-               x = xfrm6_find_acq(mode, reqid, proto,
-                                  &((struct sockaddr_in6 *)(daddr + 1))->sin6_addr,
-                                  &((struct sockaddr_in6 *)(saddr + 1))->sin6_addr, 1);
+               xdaddr = (xfrm_address_t *)&((struct sockaddr_in6 *)(daddr + 1))->sin6_addr;
+               xsaddr = (xfrm_address_t *)&((struct sockaddr_in6 *)(saddr + 1))->sin6_addr;
                break;
 #endif
-       default:
-               x = NULL;
-               break;
        }
+       if (xdaddr)
+               x = xfrm_find_acq(mode, reqid, proto, xdaddr, xsaddr, 1, family);
 
        if (x == NULL)
                return -ENOENT;
@@ -1188,23 +1175,9 @@ static int pfkey_add(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hdr,
        /* XXX there is race condition */
        x1 = pfkey_xfrm_state_lookup(hdr, ext_hdrs);
        if (!x1) {
-               switch (x->props.family) {
-               case AF_INET:
-                       x1 = xfrm_find_acq(x->props.mode, x->props.reqid, x->id.proto,
-                                          x->id.daddr.xfrm4_addr,
-                                          x->props.saddr.xfrm4_addr, 0);
-                       break;
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-               case AF_INET6:
-                       x1 = xfrm6_find_acq(x->props.mode, x->props.reqid, x->id.proto,
-                                           (struct in6_addr*)x->id.daddr.a6,
-                                           (struct in6_addr*)x->props.saddr.a6, 0);
-                       break;
-#endif
-               default:
-                       x1 = NULL;
-                       break;
-               }
+               x1 = xfrm_find_acq(x->props.mode, x->props.reqid, x->id.proto,
+                                  &x->id.daddr,
+                                  &x->props.saddr, 0, x->props.family);
                if (x1 && x1->id.spi != x->id.spi && x1->id.spi) {
                        xfrm_state_put(x1);
                        x1 = NULL;
@@ -1543,11 +1516,11 @@ parse_ipsecrequest(struct xfrm_policy *xp, struct sadb_x_ipsecrequest *rq)
                        sin = (void*)(rq+1);
                        if (sin->sin_family != AF_INET)
                                return -EINVAL;
-                       t->saddr.xfrm4_addr = sin->sin_addr.s_addr;
+                       t->saddr.a4 = sin->sin_addr.s_addr;
                        sin++;
                        if (sin->sin_family != AF_INET)
                                return -EINVAL;
-                       t->id.daddr.xfrm4_addr = sin->sin_addr.s_addr;
+                       t->id.daddr.a4 = sin->sin_addr.s_addr;
                        break;
 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
                case AF_INET6:
@@ -1654,7 +1627,7 @@ static void pfkey_xfrm_policy2msg(struct sk_buff *skb, struct xfrm_policy *xp, i
        if (xp->family == AF_INET) {
                sin = (struct sockaddr_in *) (addr + 1);
                sin->sin_family = AF_INET;
-               sin->sin_addr.s_addr = xp->selector.saddr.xfrm4_addr;
+               sin->sin_addr.s_addr = xp->selector.saddr.a4;
                sin->sin_port = xp->selector.sport;
                memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
        }
@@ -1685,7 +1658,7 @@ static void pfkey_xfrm_policy2msg(struct sk_buff *skb, struct xfrm_policy *xp, i
        if (xp->family == AF_INET) {
                sin = (struct sockaddr_in *) (addr + 1);
                sin->sin_family = AF_INET;
-               sin->sin_addr.s_addr = xp->selector.daddr.xfrm4_addr;
+               sin->sin_addr.s_addr = xp->selector.daddr.a4;
                sin->sin_port = xp->selector.dport;
                memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
        }
@@ -1773,12 +1746,12 @@ static void pfkey_xfrm_policy2msg(struct sk_buff *skb, struct xfrm_policy *xp, i
                        case AF_INET:
                                sin = (void*)(rq+1);
                                sin->sin_family = AF_INET;
-                               sin->sin_addr.s_addr = t->saddr.xfrm4_addr;
+                               sin->sin_addr.s_addr = t->saddr.a4;
                                sin->sin_port = 0;
                                memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
                                sin++;
                                sin->sin_family = AF_INET;
-                               sin->sin_addr.s_addr = t->id.daddr.xfrm4_addr;
+                               sin->sin_addr.s_addr = t->id.daddr.a4;
                                sin->sin_port = 0;
                                memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
                                break;
@@ -2362,7 +2335,7 @@ static int pfkey_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *t, struct
 
                sin = (struct sockaddr_in *) (addr + 1);
                sin->sin_family = AF_INET;
-               sin->sin_addr.s_addr = x->props.saddr.xfrm4_addr;
+               sin->sin_addr.s_addr = x->props.saddr.a4;
                sin->sin_port = 0;
                memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
        }
@@ -2396,7 +2369,7 @@ static int pfkey_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *t, struct
 
                sin = (struct sockaddr_in *) (addr + 1);
                sin->sin_family = AF_INET;
-               sin->sin_addr.s_addr = x->id.daddr.xfrm4_addr;
+               sin->sin_addr.s_addr = x->id.daddr.a4;
                sin->sin_port = 0;
                memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
        }
index 52b0b4ec114640c64d13588e1dc53aba56208b80..8c508c485c0fe3abc06ff140cf2c01ea4ae3d935 100644 (file)
@@ -303,14 +303,20 @@ EXPORT_SYMBOL(__xfrm_policy_check);
 EXPORT_SYMBOL(__xfrm_route_forward);
 EXPORT_SYMBOL(xfrm_state_alloc);
 EXPORT_SYMBOL(__xfrm_state_destroy);
-EXPORT_SYMBOL(xfrm4_state_find);
+EXPORT_SYMBOL(xfrm_state_find);
 EXPORT_SYMBOL(xfrm_state_insert);
 EXPORT_SYMBOL(xfrm_state_check_expire);
 EXPORT_SYMBOL(xfrm_state_check_space);
-EXPORT_SYMBOL(xfrm4_state_lookup);
+EXPORT_SYMBOL(xfrm_state_lookup);
+EXPORT_SYMBOL(xfrm_state_register_afinfo);
+EXPORT_SYMBOL(xfrm_state_unregister_afinfo);
+EXPORT_SYMBOL(xfrm_state_get_afinfo);
+EXPORT_SYMBOL(xfrm_state_put_afinfo);
 EXPORT_SYMBOL(xfrm_replay_check);
 EXPORT_SYMBOL(xfrm_replay_advance);
 EXPORT_SYMBOL(xfrm_check_selectors);
+EXPORT_SYMBOL(__secpath_destroy);
+EXPORT_SYMBOL(xfrm_parse_spi);
 EXPORT_SYMBOL(xfrm4_rcv);
 EXPORT_SYMBOL(xfrm_register_type);
 EXPORT_SYMBOL(xfrm_unregister_type);
@@ -331,18 +337,11 @@ EXPORT_SYMBOL(xfrm_policy_walk);
 EXPORT_SYMBOL(xfrm_policy_flush);
 EXPORT_SYMBOL(xfrm_policy_byid);
 EXPORT_SYMBOL(xfrm_policy_list);
-EXPORT_SYMBOL(xfrm_dst_lookup_register);
-EXPORT_SYMBOL(xfrm_dst_lookup_unregister);
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-EXPORT_SYMBOL(xfrm6_state_find);
-EXPORT_SYMBOL(xfrm6_rcv);
-EXPORT_SYMBOL(xfrm6_state_lookup);
-EXPORT_SYMBOL(xfrm6_find_acq);
-EXPORT_SYMBOL(xfrm6_register_type);
-EXPORT_SYMBOL(xfrm6_unregister_type);
-EXPORT_SYMBOL(xfrm6_get_type);
-EXPORT_SYMBOL(xfrm6_clear_mutable_options);
-#endif
+EXPORT_SYMBOL(xfrm_dst_lookup);
+EXPORT_SYMBOL(xfrm_policy_register_afinfo);
+EXPORT_SYMBOL(xfrm_policy_unregister_afinfo);
+EXPORT_SYMBOL(xfrm_policy_get_afinfo);
+EXPORT_SYMBOL(xfrm_policy_put_afinfo);
 
 EXPORT_SYMBOL_GPL(xfrm_probe_algs);
 EXPORT_SYMBOL_GPL(xfrm_count_auth_supported);
index 8e67351f419dc3d74924b11d5ee3dc825ed7ea28..5d10625416efeedf8a618755d6306961bff6e496 100644 (file)
@@ -110,6 +110,7 @@ int sctp_rcv(struct sk_buff *skb)
        struct sctphdr *sh;
        union sctp_addr src;
        union sctp_addr dest;
+       int family;
        struct sctp_af *af;
        int ret = 0;
 
@@ -129,7 +130,8 @@ int sctp_rcv(struct sk_buff *skb)
 
        skb_pull(skb, sizeof(struct sctphdr));  
 
-       af = sctp_get_af_specific(ipver2af(skb->nh.iph->version));
+       family = ipver2af(skb->nh.iph->version);
+       af = sctp_get_af_specific(family);
        if (unlikely(!af)) 
                goto bad_packet;
 
@@ -173,7 +175,7 @@ int sctp_rcv(struct sk_buff *skb)
        rcvr = asoc ? &asoc->base : &ep->base;
        sk = rcvr->sk;
 
-       if (!xfrm_policy_check(sk, XFRM_POLICY_IN, skb))
+       if (!xfrm_policy_check(sk, XFRM_POLICY_IN, skb, family))
                goto discard_release;
 
        ret = sk_filter(sk, skb, 1);