netdev
[Top] [All Lists]

[PATH] IPv6 IPsec support

To: davem@xxxxxxxxxx, kuznet@xxxxxxxxxxxxx
Subject: [PATH] IPv6 IPsec support
From: Kazunori Miyazawa <kazunori@xxxxxxxxxxxx>
Date: Wed, 5 Mar 2003 23:30:25 +0900
Cc: linux-kernel@xxxxxxxxxxxxxxx, netdev@xxxxxxxxxxx, usagi-core@xxxxxxxxxxxxxx
Sender: netdev-bounce@xxxxxxxxxxx
Hello, 

I submit the patch to let the kernel support ipv6 ipsec again.
It is able to comple ipv6 as module.

This patch incldes a couple of clean-up and
changes of function name.

Sorry, this patch is for linux-2.5.63.

Best Regards,

--Kazunori Miyazawa (Yokogawa Electric Corporation)


Patch-Name: IPsec
Patch-Id: IPSEC_2_5_63_ALL-20030304
Patch-Author: Kazunori Miyazawa <miyazawa@xxxxxxxxxxxxxx>
Credit: Kazunori Miyazawa <miyazawa@xxxxxxxxxxxxxx>,
        Mitsuru Kanda <kanda@xxxxxxxxxx>,
        YOSHIFUJI Hideaki <yoshfuji@xxxxxxxxxxxxxx>,
        Kunihiro Ishiguro

This patch make the kernel process IPv6 packet with IPsec.

- We've introduced a function pointer (xfrm_dst_lookup) for looking up
  routing table of each address family to comple ipv6 as module.

- We moved some common functions among protocols such as skb_icv_walk()
  in net/ipv4/{ah.c,esp.c} to net/ipv4/xfrm_algo.c.  This is for
  compling ah / esp and ah6 / esp6 as modules.

- We renamed some IPv4 specific xfrm_XXX() functions to xfrm4_XXX().


diff -ruN -x CVS linux-2.5.63/include/linux/ipv6.h linux25/include/linux/ipv6.h
--- linux-2.5.63/include/linux/ipv6.h   2003-02-25 04:05:38.000000000 +0900
+++ linux25/include/linux/ipv6.h        2003-03-05 11:30:34.000000000 +0900
@@ -74,6 +74,21 @@
 #define rt0_type               rt_hdr.type;
 };
 
+struct ipv6_auth_hdr {
+       __u8  nexthdr;
+       __u8  hdrlen;           /* This one is measured in 32 bit units! */
+       __u16 reserved;
+       __u32 spi;
+       __u32 seq_no;           /* Sequence number */
+       __u8  auth_data[4];     /* Length variable but >=4. Mind the 64 bit 
alignment! */
+};
+
+struct ipv6_esp_hdr {
+       __u32 spi;
+       __u32 seq_no;           /* Sequence number */
+       __u8  enc_data[8];      /* Length variable but >=8. Mind the 64 bit 
alignment! */
+};
+
 /*
  *     IPv6 fixed header
  *
diff -ruN -x CVS linux-2.5.63/include/net/dst.h linux25/include/net/dst.h
--- linux-2.5.63/include/net/dst.h      2003-02-25 04:05:44.000000000 +0900
+++ linux25/include/net/dst.h   2003-03-05 17:49:50.000000000 +0900
@@ -247,7 +247,10 @@
 struct flowi;
 extern int xfrm_lookup(struct dst_entry **dst_p, struct flowi *fl,
                       struct sock *sk, int flags);
+extern int xfrm6_lookup(struct dst_entry **dst_p, struct flowi *fl,
+                      struct sock *sk, int flags);
 extern void xfrm_init(void);
+extern void xfrm6_init(void);
 
 #endif
 
diff -ruN -x CVS linux-2.5.63/include/net/ip6_route.h 
linux25/include/net/ip6_route.h
--- linux-2.5.63/include/net/ip6_route.h        2003-02-25 04:05:12.000000000 
+0900
+++ linux25/include/net/ip6_route.h     2003-03-04 20:38:14.000000000 +0900
@@ -38,6 +38,7 @@
 extern int                     ipv6_route_ioctl(unsigned int cmd, void *arg);
 
 extern int                     ip6_route_add(struct in6_rtmsg *rtmsg);
+extern int                     ip6_route_del(struct in6_rtmsg *rtmsg);
 extern int                     ip6_del_rt(struct rt6_info *);
 
 extern int                     ip6_rt_addr_add(struct in6_addr *addr,
@@ -57,6 +58,8 @@
                                            struct in6_addr *saddr,
                                            int oif, int flags);
 
+extern struct rt6_info         *ndisc_get_dummy_rt(void);
+
 /*
  *     support functions for ND
  *
diff -ruN -x CVS linux-2.5.63/include/net/xfrm.h linux25/include/net/xfrm.h
--- linux-2.5.63/include/net/xfrm.h     2003-02-25 04:05:41.000000000 +0900
+++ linux25/include/net/xfrm.h  2003-03-05 17:49:51.000000000 +0900
@@ -12,6 +12,7 @@
 
 #include <net/dst.h>
 #include <net/route.h>
+#include <net/ip6_fib.h>
 
 #define XFRM_ALIGN8(len)       (((len) + 7) & ~7)
 
@@ -282,6 +283,7 @@
                struct xfrm_dst         *next;
                struct dst_entry        dst;
                struct rtable           rt;
+               struct rt6_info         rt6;
        } u;
 };
 
@@ -308,26 +310,42 @@
        if (sp && atomic_dec_and_test(&sp->refcnt))
                __secpath_destroy(sp);
 }
-
-extern int __xfrm_policy_check(struct sock *, int dir, struct sk_buff *skb);
+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)
 {
        if (sk && sk->policy[XFRM_POLICY_IN])
-               return __xfrm_policy_check(sk, dir, skb);
+               return __xfrm_policy_check(sk, dir, skb, AF_INET);
                
        return  !xfrm_policy_list[dir] ||
                (skb->dst->flags & DST_NOPOLICY) ||
-               __xfrm_policy_check(sk, dir, skb);
+               __xfrm_policy_check(sk, dir, skb, AF_INET);
 }
 
-extern int __xfrm_route_forward(struct sk_buff *skb);
+static inline int xfrm6_policy_check(struct sock *sk, int dir, struct sk_buff 
*skb)
+{
+       if (sk && sk->policy[XFRM_POLICY_IN])
+               return __xfrm_policy_check(sk, dir, skb, AF_INET6);
+               
+       return  !xfrm_policy_list[dir] ||
+               (skb->dst->flags & DST_NOPOLICY) ||
+               __xfrm_policy_check(sk, dir, skb, AF_INET6);
+}
+
+extern int __xfrm_route_forward(struct sk_buff *skb, unsigned short family);
 
 static inline int xfrm_route_forward(struct sk_buff *skb)
 {
        return  !xfrm_policy_list[XFRM_POLICY_OUT] ||
                (skb->dst->flags & DST_NOXFRM) ||
-               __xfrm_route_forward(skb);
+               __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);
 }
 
 extern int __xfrm_sk_clone_policy(struct sock *sk);
@@ -380,12 +398,16 @@
 extern void xfrm_input_init(void);
 extern int xfrm_state_walk(u8 proto, int (*func)(struct xfrm_state *, int, 
void*), void *);
 extern struct xfrm_state *xfrm_state_alloc(void);
-extern struct xfrm_state *xfrm_state_find(u32 daddr, u32 saddr, struct flowi 
*fl, struct xfrm_tmpl *tmpl,
-                                         struct xfrm_policy *pol, int *err);
+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 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 *xfrm_state_lookup(u32 daddr, u32 spi, u8 proto);
+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_find_acq_byseq(u32 seq);
 extern void xfrm_state_delete(struct xfrm_state *x);
 extern void xfrm_state_flush(u8 proto);
@@ -393,17 +415,21 @@
 extern void xfrm_replay_advance(struct xfrm_state *x, u32 seq);
 extern int xfrm_check_selectors(struct xfrm_state **x, int n, struct flowi 
*fl);
 extern int xfrm4_rcv(struct sk_buff *skb);
+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);
 
 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);
+struct xfrm_policy *xfrm_policy_lookup(int dir, struct flowi *fl, unsigned 
short family);
 int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl);
 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);
 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);
@@ -425,23 +451,129 @@
 extern struct xfrm_algo_desc *xfrm_aalg_get_byname(char *name);
 extern struct xfrm_algo_desc *xfrm_ealg_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  !memcmp(fl->fl6_dst, sel->daddr.a6, sizeof(struct in6_addr)) &&
-              !((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) &&
-              !memcmp(fl->fl6_src, sel->saddr.a6, sizeof(struct in6_addr));
+       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);
 
-extern struct xfrm_state *xfrm6_state_lookup(struct in6_addr *daddr, u32 spi, 
u8 proto);
-struct xfrm_state * xfrm6_find_acq(u8 mode, u16 reqid, u8 proto, struct 
in6_addr *daddr, struct in6_addr *saddr, int create);
-void xfrm6_alloc_spi(struct xfrm_state *x, u32 minspi, u32 maxspi);
+struct ah_data
+{
+       u8                      *key;
+       int                     key_len;
+       u8                      *work_icv;
+       int                     icv_full_len;
+       int                     icv_trunc_len;
+
+       void                    (*icv)(struct ah_data*,
+                                      struct sk_buff *skb, u8 *icv);
+
+       struct crypto_tfm       *tfm;
+};
+
+struct esp_data
+{
+       /* Confidentiality */
+       struct {
+               u8                      *key;           /* Key */
+               int                     key_len;        /* Key length */
+               u8                      *ivec;          /* ivec buffer */
+               /* ivlen is offset from enc_data, where encrypted data start.
+                * It is logically different of crypto_tfm_alg_ivsize(tfm).
+                * We assume that it is either zero (no ivec), or
+                * >= crypto_tfm_alg_ivsize(tfm). */
+               int                     ivlen;
+               int                     padlen;         /* 0..255 */
+               struct crypto_tfm       *tfm;           /* crypto handle */
+       } conf;
+
+       /* Integrity. It is active when icv_full_len != 0 */
+       struct {
+               u8                      *key;           /* Key */
+               int                     key_len;        /* Length of the key */
+               u8                      *work_icv;
+               int                     icv_full_len;
+               int                     icv_trunc_len;
+               void                    (*icv)(struct esp_data*,
+                                              struct sk_buff *skb,
+                                              int offset, int len, u8 *icv);
+               struct crypto_tfm       *tfm;
+       } auth;
+};
+
+typedef void (icv_update_fn_t)(struct crypto_tfm *, struct scatterlist *, 
unsigned int);
+extern void skb_ah_walk(const struct sk_buff *skb,
+                        struct crypto_tfm *tfm, icv_update_fn_t icv_update);
+extern void skb_icv_walk(const struct sk_buff *skb, struct crypto_tfm *tfm,
+                       int offset, int len, icv_update_fn_t icv_update);
+extern int skb_to_sgvec(struct sk_buff *skb, struct scatterlist *sg, int 
offset, int len);
+extern int skb_cow_data(struct sk_buff *skb, int tailbits, struct sk_buff 
**trailer);
+extern void *pskb_put(struct sk_buff *skb, struct sk_buff *tail, int len);
+
+static inline void
+ah_hmac_digest(struct ah_data *ahp, struct sk_buff *skb, u8 *auth_data)
+{
+       struct crypto_tfm *tfm = ahp->tfm;
+
+       memset(auth_data, 0, ahp->icv_trunc_len);
+       crypto_hmac_init(tfm, ahp->key, &ahp->key_len);
+       skb_ah_walk(skb, tfm, crypto_hmac_update);
+       crypto_hmac_final(tfm, ahp->key, &ahp->key_len, ahp->work_icv);
+       memcpy(auth_data, ahp->work_icv, ahp->icv_trunc_len);
+}
+
+static inline void
+esp_hmac_digest(struct esp_data *esp, struct sk_buff *skb, int offset,
+                int len, u8 *auth_data)
+{
+       struct crypto_tfm *tfm = esp->auth.tfm;
+       char *icv = esp->auth.work_icv;
+
+       memset(auth_data, 0, esp->auth.icv_trunc_len);
+       crypto_hmac_init(tfm, esp->auth.key, &esp->auth.key_len);
+       skb_icv_walk(skb, tfm, offset, len, crypto_hmac_update);
+       crypto_hmac_final(tfm, esp->auth.key, &esp->auth.key_len, icv);
+       memcpy(auth_data, icv, esp->auth.icv_trunc_len);
+}
+
+
+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 */
diff -ruN -x CVS linux-2.5.63/net/ipv4/ah.c linux25/net/ipv4/ah.c
--- linux-2.5.63/net/ipv4/ah.c  2003-02-25 04:05:42.000000000 +0900
+++ linux25/net/ipv4/ah.c       2003-03-05 17:49:52.000000000 +0900
@@ -7,25 +7,8 @@
 #include <net/icmp.h>
 #include <asm/scatterlist.h>
 
-#define AH_HLEN_NOICV  12
-
-typedef void (icv_update_fn_t)(struct crypto_tfm *,
-                               struct scatterlist *, unsigned int);
-
-struct ah_data
-{
-       u8                      *key;
-       int                     key_len;
-       u8                      *work_icv;
-       int                     icv_full_len;
-       int                     icv_trunc_len;
-
-       void                    (*icv)(struct ah_data*,
-                                      struct sk_buff *skb, u8 *icv);
-
-       struct crypto_tfm       *tfm;
-};
 
+#define AH_HLEN_NOICV  12
 
 /* Clear mutable options and find final destination to substitute
  * into IP header for icv calculation. Options are already checked
@@ -71,92 +54,6 @@
        return 0;
 }
 
-static void skb_ah_walk(const struct sk_buff *skb,
-                        struct crypto_tfm *tfm, icv_update_fn_t icv_update)
-{
-       int offset = 0;
-       int len = skb->len;
-       int start = skb->len - skb->data_len;
-       int i, copy = start - offset;
-       struct scatterlist sg;
-
-       /* Checksum header. */
-       if (copy > 0) {
-               if (copy > len)
-                       copy = len;
-               
-               sg.page = virt_to_page(skb->data + offset);
-               sg.offset = (unsigned long)(skb->data + offset) % PAGE_SIZE;
-               sg.length = copy;
-               
-               icv_update(tfm, &sg, 1);
-               
-               if ((len -= copy) == 0)
-                       return;
-               offset += copy;
-       }
-
-       for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
-               int end;
-
-               BUG_TRAP(start <= offset + len);
-
-               end = start + skb_shinfo(skb)->frags[i].size;
-               if ((copy = end - offset) > 0) {
-                       skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
-
-                       if (copy > len)
-                               copy = len;
-                       
-                       sg.page = frag->page;
-                       sg.offset = frag->page_offset + offset-start;
-                       sg.length = copy;
-                       
-                       icv_update(tfm, &sg, 1);
-                       
-                       if (!(len -= copy))
-                               return;
-                       offset += copy;
-               }
-               start = end;
-       }
-
-       if (skb_shinfo(skb)->frag_list) {
-               struct sk_buff *list = skb_shinfo(skb)->frag_list;
-
-               for (; list; list = list->next) {
-                       int end;
-
-                       BUG_TRAP(start <= offset + len);
-
-                       end = start + list->len;
-                       if ((copy = end - offset) > 0) {
-                               if (copy > len)
-                                       copy = len;
-                               skb_ah_walk(list, tfm, icv_update);
-                               if ((len -= copy) == 0)
-                                       return;
-                               offset += copy;
-                       }
-                       start = end;
-               }
-       }
-       if (len)
-               BUG();
-}
-
-static void
-ah_hmac_digest(struct ah_data *ahp, struct sk_buff *skb, u8 *auth_data)
-{
-       struct crypto_tfm *tfm = ahp->tfm;
-
-       memset(auth_data, 0, ahp->icv_trunc_len);
-       crypto_hmac_init(tfm, ahp->key, &ahp->key_len);
-       skb_ah_walk(skb, tfm, crypto_hmac_update);
-       crypto_hmac_final(tfm, ahp->key, &ahp->key_len, ahp->work_icv);
-       memcpy(auth_data, ahp->work_icv, ahp->icv_trunc_len);
-}
-
 static int ah_output(struct sk_buff *skb)
 {
        int err;
@@ -330,7 +227,7 @@
            skb->h.icmph->code != ICMP_FRAG_NEEDED)
                return;
 
-       x = xfrm_state_lookup(iph->daddr, ah->spi, IPPROTO_AH);
+       x = xfrm4_state_lookup(iph->daddr, ah->spi, IPPROTO_AH);
        if (!x)
                return;
        printk(KERN_DEBUG "pmtu discvovery on SA AH/%08x/%08x\n",
diff -ruN -x CVS linux-2.5.63/net/ipv4/esp.c linux25/net/ipv4/esp.c
--- linux-2.5.63/net/ipv4/esp.c 2003-02-25 04:05:34.000000000 +0900
+++ linux25/net/ipv4/esp.c      2003-03-05 17:49:52.000000000 +0900
@@ -8,312 +8,8 @@
 #include <linux/random.h>
 #include <net/icmp.h>
 
-#define MAX_SG_ONSTACK 4
-
-typedef void (icv_update_fn_t)(struct crypto_tfm *,
-                               struct scatterlist *, unsigned int);
-
-/* BUGS:
- * - we assume replay seqno is always present.
- */
-
-struct esp_data
-{
-       /* Confidentiality */
-       struct {
-               u8                      *key;           /* Key */
-               int                     key_len;        /* Key length */
-               u8                      *ivec;          /* ivec buffer */
-               /* ivlen is offset from enc_data, where encrypted data start.
-                * It is logically different of crypto_tfm_alg_ivsize(tfm).
-                * We assume that it is either zero (no ivec), or
-                * >= crypto_tfm_alg_ivsize(tfm). */
-               int                     ivlen;
-               int                     padlen;         /* 0..255 */
-               struct crypto_tfm       *tfm;           /* crypto handle */
-       } conf;
-
-       /* Integrity. It is active when icv_full_len != 0 */
-       struct {
-               u8                      *key;           /* Key */
-               int                     key_len;        /* Length of the key */
-               u8                      *work_icv;
-               int                     icv_full_len;
-               int                     icv_trunc_len;
-               void                    (*icv)(struct esp_data*,
-                                              struct sk_buff *skb,
-                                              int offset, int len, u8 *icv);
-               struct crypto_tfm       *tfm;
-       } auth;
-};
-
-/* Move to common area: it is shared with AH. */
-
-void skb_icv_walk(const struct sk_buff *skb, struct crypto_tfm *tfm,
-                 int offset, int len, icv_update_fn_t icv_update)
-{
-       int start = skb->len - skb->data_len;
-       int i, copy = start - offset;
-       struct scatterlist sg;
-
-       /* Checksum header. */
-       if (copy > 0) {
-               if (copy > len)
-                       copy = len;
-               
-               sg.page = virt_to_page(skb->data + offset);
-               sg.offset = (unsigned long)(skb->data + offset) % PAGE_SIZE;
-               sg.length = copy;
-               
-               icv_update(tfm, &sg, 1);
-               
-               if ((len -= copy) == 0)
-                       return;
-               offset += copy;
-       }
-
-       for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
-               int end;
-
-               BUG_TRAP(start <= offset + len);
-
-               end = start + skb_shinfo(skb)->frags[i].size;
-               if ((copy = end - offset) > 0) {
-                       skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
-
-                       if (copy > len)
-                               copy = len;
-                       
-                       sg.page = frag->page;
-                       sg.offset = frag->page_offset + offset-start;
-                       sg.length = copy;
-                       
-                       icv_update(tfm, &sg, 1);
-
-                       if (!(len -= copy))
-                               return;
-                       offset += copy;
-               }
-               start = end;
-       }
-
-       if (skb_shinfo(skb)->frag_list) {
-               struct sk_buff *list = skb_shinfo(skb)->frag_list;
-
-               for (; list; list = list->next) {
-                       int end;
-
-                       BUG_TRAP(start <= offset + len);
-
-                       end = start + list->len;
-                       if ((copy = end - offset) > 0) {
-                               if (copy > len)
-                                       copy = len;
-                               skb_icv_walk(list, tfm, offset-start, copy, 
icv_update);
-                               if ((len -= copy) == 0)
-                                       return;
-                               offset += copy;
-                       }
-                       start = end;
-               }
-       }
-       if (len)
-               BUG();
-}
-
-
-/* Looking generic it is not used in another places. */
-
-int
-skb_to_sgvec(struct sk_buff *skb, struct scatterlist *sg, int offset, int len)
-{
-       int start = skb->len - skb->data_len;
-       int i, copy = start - offset;
-       int elt = 0;
-
-       if (copy > 0) {
-               if (copy > len)
-                       copy = len;
-               sg[elt].page = virt_to_page(skb->data + offset);
-               sg[elt].offset = (unsigned long)(skb->data + offset) % 
PAGE_SIZE;
-               sg[elt].length = copy;
-               elt++;
-               if ((len -= copy) == 0)
-                       return elt;
-               offset += copy;
-       }
-
-       for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
-               int end;
-
-               BUG_TRAP(start <= offset + len);
-
-               end = start + skb_shinfo(skb)->frags[i].size;
-               if ((copy = end - offset) > 0) {
-                       skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
-
-                       if (copy > len)
-                               copy = len;
-                       sg[elt].page = frag->page;
-                       sg[elt].offset = frag->page_offset+offset-start;
-                       sg[elt].length = copy;
-                       elt++;
-                       if (!(len -= copy))
-                               return elt;
-                       offset += copy;
-               }
-               start = end;
-       }
-
-       if (skb_shinfo(skb)->frag_list) {
-               struct sk_buff *list = skb_shinfo(skb)->frag_list;
-
-               for (; list; list = list->next) {
-                       int end;
-
-                       BUG_TRAP(start <= offset + len);
-
-                       end = start + list->len;
-                       if ((copy = end - offset) > 0) {
-                               if (copy > len)
-                                       copy = len;
-                               elt += skb_to_sgvec(list, sg+elt, offset - 
start, copy);
-                               if ((len -= copy) == 0)
-                                       return elt;
-                               offset += copy;
-                       }
-                       start = end;
-               }
-       }
-       if (len)
-               BUG();
-       return elt;
-}
-
-/* Common with AH after some work on arguments. */
-
-static void
-esp_hmac_digest(struct esp_data *esp, struct sk_buff *skb, int offset,
-               int len, u8 *auth_data)
-{
-       struct crypto_tfm *tfm = esp->auth.tfm;
-       char *icv = esp->auth.work_icv;
-
-       memset(auth_data, 0, esp->auth.icv_trunc_len);
-       crypto_hmac_init(tfm, esp->auth.key, &esp->auth.key_len);
-       skb_icv_walk(skb, tfm, offset, len, crypto_hmac_update);
-       crypto_hmac_final(tfm, esp->auth.key, &esp->auth.key_len, icv);
-       memcpy(auth_data, icv, esp->auth.icv_trunc_len);
-}
-
-/* Check that skb data bits are writable. If they are not, copy data
- * to newly created private area. If "tailbits" is given, make sure that
- * tailbits bytes beyond current end of skb are writable.
- *
- * Returns amount of elements of scatterlist to load for subsequent
- * transformations and pointer to writable trailer skb.
- */
-
-int skb_cow_data(struct sk_buff *skb, int tailbits, struct sk_buff **trailer)
-{
-       int copyflag;
-       int elt;
-       struct sk_buff *skb1, **skb_p;
-
-       /* If skb is cloned or its head is paged, reallocate
-        * head pulling out all the pages (pages are considered not writable
-        * at the moment even if they are anonymous).
-        */
-       if ((skb_cloned(skb) || skb_shinfo(skb)->nr_frags) &&
-           __pskb_pull_tail(skb, skb_pagelen(skb)-skb_headlen(skb)) == NULL)
-               return -ENOMEM;
-
-       /* Easy case. Most of packets will go this way. */
-       if (!skb_shinfo(skb)->frag_list) {
-               /* A little of trouble, not enough of space for trailer.
-                * This should not happen, when stack is tuned to generate
-                * good frames. OK, on miss we reallocate and reserve even more
-                * space, 128 bytes is fair. */
-
-               if (skb_tailroom(skb) < tailbits &&
-                   pskb_expand_head(skb, 0, tailbits-skb_tailroom(skb)+128, 
GFP_ATOMIC))
-                       return -ENOMEM;
-
-               /* Voila! */
-               *trailer = skb;
-               return 1;
-       }
-
-       /* Misery. We are in troubles, going to mincer fragments... */
 
-       elt = 1;
-       skb_p = &skb_shinfo(skb)->frag_list;
-       copyflag = 0;
-
-       while ((skb1 = *skb_p) != NULL) {
-               int ntail = 0;
-
-               /* The fragment is partially pulled by someone,
-                * this can happen on input. Copy it and everything
-                * after it. */
-
-               if (skb_shared(skb1))
-                       copyflag = 1;
-
-               /* If the skb is the last, worry about trailer. */
-
-               if (skb1->next == NULL && tailbits) {
-                       if (skb_shinfo(skb1)->nr_frags ||
-                           skb_shinfo(skb1)->frag_list ||
-                           skb_tailroom(skb1) < tailbits)
-                               ntail = tailbits + 128;
-               }
-
-               if (copyflag ||
-                   skb_cloned(skb1) ||
-                   ntail ||
-                   skb_shinfo(skb1)->nr_frags ||
-                   skb_shinfo(skb1)->frag_list) {
-                       struct sk_buff *skb2;
-
-                       /* Fuck, we are miserable poor guys... */
-                       if (ntail == 0)
-                               skb2 = skb_copy(skb1, GFP_ATOMIC);
-                       else
-                               skb2 = skb_copy_expand(skb1,
-                                                      skb_headroom(skb1),
-                                                      ntail,
-                                                      GFP_ATOMIC);
-                       if (unlikely(skb2 == NULL))
-                               return -ENOMEM;
-
-                       if (skb1->sk)
-                               skb_set_owner_w(skb, skb1->sk);
-
-                       /* Looking around. Are we still alive?
-                        * OK, link new skb, drop old one */
-
-                       skb2->next = skb1->next;
-                       *skb_p = skb2;
-                       kfree_skb(skb1);
-                       skb1 = skb2;
-               }
-               elt++;
-               *trailer = skb1;
-               skb_p = &skb1->next;
-       }
-
-       return elt;
-}
-
-void *pskb_put(struct sk_buff *skb, struct sk_buff *tail, int len)
-{
-       if (tail != skb) {
-               skb->data_len += len;
-               skb->len += len;
-       }
-       return skb_put(tail, len);
-}
+#define MAX_SG_ONSTACK 4
 
 int esp_output(struct sk_buff *skb)
 {
@@ -575,7 +271,7 @@
            skb->h.icmph->code != ICMP_FRAG_NEEDED)
                return;
 
-       x = xfrm_state_lookup(iph->daddr, esph->spi, IPPROTO_ESP);
+       x = xfrm4_state_lookup(iph->daddr, esph->spi, IPPROTO_ESP);
        if (!x)
                return;
        printk(KERN_DEBUG "pmtu discvovery on SA ESP/%08x/%08x\n",
diff -ruN -x CVS linux-2.5.63/net/ipv4/route.c linux25/net/ipv4/route.c
--- linux-2.5.63/net/ipv4/route.c       2003-02-25 04:06:01.000000000 +0900
+++ linux25/net/ipv4/route.c    2003-03-04 20:38:15.000000000 +0900
@@ -96,6 +96,7 @@
 #include <net/arp.h>
 #include <net/tcp.h>
 #include <net/icmp.h>
+#include <net/xfrm.h>
 #ifdef CONFIG_SYSCTL
 #include <linux/sysctl.h>
 #endif
@@ -2599,6 +2600,13 @@
 #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;
@@ -2680,6 +2688,7 @@
                                        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;
diff -ruN -x CVS linux-2.5.63/net/ipv4/xfrm_algo.c linux25/net/ipv4/xfrm_algo.c
--- linux-2.5.63/net/ipv4/xfrm_algo.c   2003-02-25 04:05:16.000000000 +0900
+++ linux25/net/ipv4/xfrm_algo.c        2003-03-04 20:38:16.000000000 +0900
@@ -8,9 +8,11 @@
  * Software Foundation; either version 2 of the License, or (at your option) 
  * any later version.
  */
+#include <linux/config.h>
 #include <linux/kernel.h>
 #include <linux/pfkeyv2.h>
 #include <net/xfrm.h>
+#include <asm/scatterlist.h>
 
 /*
  * Algorithms supported by IPsec.  These entries contain properties which
@@ -348,3 +350,333 @@
                        n++;
        return n;
 }
+
+#if defined(CONFIG_INET_AH) || defined(CONFIG_INET_AH_MODULE) || 
defined(CONFIG_INET6_AH) || defined(CONFIG_INET6_AH_MODULE)
+void skb_ah_walk(const struct sk_buff *skb,
+                        struct crypto_tfm *tfm, icv_update_fn_t icv_update)
+{
+       int offset = 0;
+       int len = skb->len;
+       int start = skb->len - skb->data_len;
+       int i, copy = start - offset;
+       struct scatterlist sg;
+
+       /* Checksum header. */
+       if (copy > 0) {
+               if (copy > len)
+                       copy = len;
+               
+               sg.page = virt_to_page(skb->data + offset);
+               sg.offset = (unsigned long)(skb->data + offset) % PAGE_SIZE;
+               sg.length = copy;
+               
+               icv_update(tfm, &sg, 1);
+               
+               if ((len -= copy) == 0)
+                       return;
+               offset += copy;
+       }
+
+       for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+               int end;
+
+               BUG_TRAP(start <= offset + len);
+
+               end = start + skb_shinfo(skb)->frags[i].size;
+               if ((copy = end - offset) > 0) {
+                       skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+
+                       if (copy > len)
+                               copy = len;
+                       
+                       sg.page = frag->page;
+                       sg.offset = frag->page_offset + offset-start;
+                       sg.length = copy;
+                       
+                       icv_update(tfm, &sg, 1);
+                       
+                       if (!(len -= copy))
+                               return;
+                       offset += copy;
+               }
+               start = end;
+       }
+
+       if (skb_shinfo(skb)->frag_list) {
+               struct sk_buff *list = skb_shinfo(skb)->frag_list;
+
+               for (; list; list = list->next) {
+                       int end;
+
+                       BUG_TRAP(start <= offset + len);
+
+                       end = start + list->len;
+                       if ((copy = end - offset) > 0) {
+                               if (copy > len)
+                                       copy = len;
+                               skb_ah_walk(list, tfm, icv_update);
+                               if ((len -= copy) == 0)
+                                       return;
+                               offset += copy;
+                       }
+                       start = end;
+               }
+       }
+       if (len)
+               BUG();
+}
+#endif
+
+#if defined(CONFIG_INET_ESP) || defined(CONFIG_INET_ESP_MODULE) || 
defined(CONFIG_INET6_ESP) || defined(CONFIG_INET6_ESP_MODULE)
+/* Move to common area: it is shared with AH. */
+
+void skb_icv_walk(const struct sk_buff *skb, struct crypto_tfm *tfm,
+                 int offset, int len, icv_update_fn_t icv_update)
+{
+       int start = skb->len - skb->data_len;
+       int i, copy = start - offset;
+       struct scatterlist sg;
+
+       /* Checksum header. */
+       if (copy > 0) {
+               if (copy > len)
+                       copy = len;
+               
+               sg.page = virt_to_page(skb->data + offset);
+               sg.offset = (unsigned long)(skb->data + offset) % PAGE_SIZE;
+               sg.length = copy;
+               
+               icv_update(tfm, &sg, 1);
+               
+               if ((len -= copy) == 0)
+                       return;
+               offset += copy;
+       }
+
+       for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+               int end;
+
+               BUG_TRAP(start <= offset + len);
+
+               end = start + skb_shinfo(skb)->frags[i].size;
+               if ((copy = end - offset) > 0) {
+                       skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+
+                       if (copy > len)
+                               copy = len;
+                       
+                       sg.page = frag->page;
+                       sg.offset = frag->page_offset + offset-start;
+                       sg.length = copy;
+                       
+                       icv_update(tfm, &sg, 1);
+
+                       if (!(len -= copy))
+                               return;
+                       offset += copy;
+               }
+               start = end;
+       }
+
+       if (skb_shinfo(skb)->frag_list) {
+               struct sk_buff *list = skb_shinfo(skb)->frag_list;
+
+               for (; list; list = list->next) {
+                       int end;
+
+                       BUG_TRAP(start <= offset + len);
+
+                       end = start + list->len;
+                       if ((copy = end - offset) > 0) {
+                               if (copy > len)
+                                       copy = len;
+                               skb_icv_walk(list, tfm, offset-start, copy, 
icv_update);
+                               if ((len -= copy) == 0)
+                                       return;
+                               offset += copy;
+                       }
+                       start = end;
+               }
+       }
+       if (len)
+               BUG();
+}
+
+
+/* Looking generic it is not used in another places. */
+
+int
+skb_to_sgvec(struct sk_buff *skb, struct scatterlist *sg, int offset, int len)
+{
+       int start = skb->len - skb->data_len;
+       int i, copy = start - offset;
+       int elt = 0;
+
+       if (copy > 0) {
+               if (copy > len)
+                       copy = len;
+               sg[elt].page = virt_to_page(skb->data + offset);
+               sg[elt].offset = (unsigned long)(skb->data + offset) % 
PAGE_SIZE;
+               sg[elt].length = copy;
+               elt++;
+               if ((len -= copy) == 0)
+                       return elt;
+               offset += copy;
+       }
+
+       for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+               int end;
+
+               BUG_TRAP(start <= offset + len);
+
+               end = start + skb_shinfo(skb)->frags[i].size;
+               if ((copy = end - offset) > 0) {
+                       skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+
+                       if (copy > len)
+                               copy = len;
+                       sg[elt].page = frag->page;
+                       sg[elt].offset = frag->page_offset+offset-start;
+                       sg[elt].length = copy;
+                       elt++;
+                       if (!(len -= copy))
+                               return elt;
+                       offset += copy;
+               }
+               start = end;
+       }
+
+       if (skb_shinfo(skb)->frag_list) {
+               struct sk_buff *list = skb_shinfo(skb)->frag_list;
+
+               for (; list; list = list->next) {
+                       int end;
+
+                       BUG_TRAP(start <= offset + len);
+
+                       end = start + list->len;
+                       if ((copy = end - offset) > 0) {
+                               if (copy > len)
+                                       copy = len;
+                               elt += skb_to_sgvec(list, sg+elt, offset - 
start, copy);
+                               if ((len -= copy) == 0)
+                                       return elt;
+                               offset += copy;
+                       }
+                       start = end;
+               }
+       }
+       if (len)
+               BUG();
+       return elt;
+}
+
+/* Check that skb data bits are writable. If they are not, copy data
+ * to newly created private area. If "tailbits" is given, make sure that
+ * tailbits bytes beyond current end of skb are writable.
+ *
+ * Returns amount of elements of scatterlist to load for subsequent
+ * transformations and pointer to writable trailer skb.
+ */
+
+int skb_cow_data(struct sk_buff *skb, int tailbits, struct sk_buff **trailer)
+{
+       int copyflag;
+       int elt;
+       struct sk_buff *skb1, **skb_p;
+
+       /* If skb is cloned or its head is paged, reallocate
+        * head pulling out all the pages (pages are considered not writable
+        * at the moment even if they are anonymous).
+        */
+       if ((skb_cloned(skb) || skb_shinfo(skb)->nr_frags) &&
+           __pskb_pull_tail(skb, skb_pagelen(skb)-skb_headlen(skb)) == NULL)
+               return -ENOMEM;
+
+       /* Easy case. Most of packets will go this way. */
+       if (!skb_shinfo(skb)->frag_list) {
+               /* A little of trouble, not enough of space for trailer.
+                * This should not happen, when stack is tuned to generate
+                * good frames. OK, on miss we reallocate and reserve even more
+                * space, 128 bytes is fair. */
+
+               if (skb_tailroom(skb) < tailbits &&
+                   pskb_expand_head(skb, 0, tailbits-skb_tailroom(skb)+128, 
GFP_ATOMIC))
+                       return -ENOMEM;
+
+               /* Voila! */
+               *trailer = skb;
+               return 1;
+       }
+
+       /* Misery. We are in troubles, going to mincer fragments... */
+
+       elt = 1;
+       skb_p = &skb_shinfo(skb)->frag_list;
+       copyflag = 0;
+
+       while ((skb1 = *skb_p) != NULL) {
+               int ntail = 0;
+
+               /* The fragment is partially pulled by someone,
+                * this can happen on input. Copy it and everything
+                * after it. */
+
+               if (skb_shared(skb1))
+                       copyflag = 1;
+
+               /* If the skb is the last, worry about trailer. */
+
+               if (skb1->next == NULL && tailbits) {
+                       if (skb_shinfo(skb1)->nr_frags ||
+                           skb_shinfo(skb1)->frag_list ||
+                           skb_tailroom(skb1) < tailbits)
+                               ntail = tailbits + 128;
+               }
+
+               if (copyflag ||
+                   skb_cloned(skb1) ||
+                   ntail ||
+                   skb_shinfo(skb1)->nr_frags ||
+                   skb_shinfo(skb1)->frag_list) {
+                       struct sk_buff *skb2;
+
+                       /* Fuck, we are miserable poor guys... */
+                       if (ntail == 0)
+                               skb2 = skb_copy(skb1, GFP_ATOMIC);
+                       else
+                               skb2 = skb_copy_expand(skb1,
+                                                      skb_headroom(skb1),
+                                                      ntail,
+                                                      GFP_ATOMIC);
+                       if (unlikely(skb2 == NULL))
+                               return -ENOMEM;
+
+                       if (skb1->sk)
+                               skb_set_owner_w(skb, skb1->sk);
+
+                       /* Looking around. Are we still alive?
+                        * OK, link new skb, drop old one */
+
+                       skb2->next = skb1->next;
+                       *skb_p = skb2;
+                       kfree_skb(skb1);
+                       skb1 = skb2;
+               }
+               elt++;
+               *trailer = skb1;
+               skb_p = &skb1->next;
+       }
+
+       return elt;
+}
+
+void *pskb_put(struct sk_buff *skb, struct sk_buff *tail, int len)
+{
+       if (tail != skb) {
+               skb->data_len += len;
+               skb->len += len;
+       }
+       return skb_put(tail, len);
+}
+#endif
diff -ruN -x CVS linux-2.5.63/net/ipv4/xfrm_input.c 
linux25/net/ipv4/xfrm_input.c
--- linux-2.5.63/net/ipv4/xfrm_input.c  2003-02-25 04:05:05.000000000 +0900
+++ linux25/net/ipv4/xfrm_input.c       2003-03-05 17:49:52.000000000 +0900
@@ -1,4 +1,14 @@
+/* Changes
+ *
+ *     Mitsuru KANDA @USAGI       : IPv6 Support 
+ *     Kazunori MIYAZAWA @USAGI   :
+ *     YOSHIFUJI Hideaki @USAGI   :
+ *     Kunihiro Ishiguro          :
+ *     
+ */
+
 #include <net/ip.h>
+#include <net/ipv6.h>
 #include <net/xfrm.h>
 
 static kmem_cache_t *secpath_cachep;
@@ -64,7 +74,7 @@
                if (xfrm_nr == XFRM_MAX_DEPTH)
                        goto drop;
 
-               x = xfrm_state_lookup(iph->daddr, spi, iph->protocol);
+               x = xfrm4_state_lookup(iph->daddr, spi, iph->protocol);
                if (x == NULL)
                        goto drop;
 
@@ -157,3 +167,288 @@
        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 = *(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 */
diff -ruN -x CVS linux-2.5.63/net/ipv4/xfrm_policy.c 
linux25/net/ipv4/xfrm_policy.c
--- linux-2.5.63/net/ipv4/xfrm_policy.c 2003-02-25 04:05:32.000000000 +0900
+++ linux25/net/ipv4/xfrm_policy.c      2003-03-05 17:49:52.000000000 +0900
@@ -1,6 +1,16 @@
+/* Changes
+ *
+ *     Mitsuru KANDA @USAGI       : IPv6 Support 
+ *     Kazunori MIYAZAWA @USAGI   :
+ *     Kunihiro Ishiguro          :
+ *     
+ */
+
 #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);
 
@@ -10,6 +20,11 @@
 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 inline int xfrm_dst_lookup(struct xfrm_dst **dst, struct flowi *fl, 
unsigned short family);
 
 /* Limited flow cache. Its function now is to accelerate search for
  * policy rules.
@@ -48,6 +63,24 @@
        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;
 
@@ -77,13 +110,27 @@
        }
 }
 
-struct xfrm_policy *flow_lookup(int dir, struct flowi *fl)
+struct xfrm_policy *flow_lookup(int dir, struct flowi *fl, 
+                               unsigned short family)
 {
-       struct xfrm_policy *pol;
+       struct xfrm_policy *pol = NULL;
        struct flow_entry *fle;
-       u32 hash = flow_hash(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;
+       }
+
        local_bh_disable();
        cpu = smp_processor_id();
 
@@ -101,7 +148,7 @@
                }
        }
 
-       pol = xfrm_policy_lookup(dir, fl);
+       pol = xfrm_policy_lookup(dir, fl, family);
 
        if (fle) {
                /* Stale flow entry found. Update it. */
@@ -199,6 +246,46 @@
        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 err = 0;
+
+       write_lock(&xdl_lock);
+       if (__xfrm_dst_lookup[family])
+               err = -ENOBUFS;
+       else { 
+               __xfrm_dst_lookup[family] = dst_lookup;
+       }
+       write_unlock(&xdl_lock);
+
+       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);
+       else
+               err = -EINVAL;
+       read_unlock(&xdl_lock);
+       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;
@@ -506,15 +593,32 @@
 
 /* Find policy to apply to this flow. */
 
-struct xfrm_policy *xfrm_policy_lookup(int dir, struct flowi *fl)
+struct xfrm_policy *xfrm_policy_lookup(int dir, struct flowi *fl, 
+                                      unsigned short family)
 {
        struct xfrm_policy *pol;
 
        read_lock_bh(&xfrm_policy_lock);
        for (pol = xfrm_policy_list[dir]; pol; pol = pol->next) {
                struct xfrm_selector *sel = &pol->selector;
+               int match;
+
+               if (pol->family != family)
+                       continue;
 
-               if (xfrm4_selector_match(sel, fl)) {
+               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;
+               }
+               if (match) {
                        atomic_inc(&pol->refcnt);
                        break;
                }
@@ -529,7 +633,21 @@
 
        read_lock_bh(&xfrm_policy_lock);
        if ((pol = sk->policy[dir]) != NULL) {
-               if (xfrm4_selector_match(&pol->selector, fl))
+               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;
+               }
+               if (match)
                        atomic_inc(&pol->refcnt);
                else
                        pol = NULL;
@@ -630,8 +748,8 @@
 /* Resolve list of templates for the flow, given policy. */
 
 static int
-xfrm_tmpl_resolve(struct xfrm_policy *policy, struct flowi *fl,
-                 struct xfrm_state **xfrm)
+xfrm4_tmpl_resolve(struct xfrm_policy *policy, struct flowi *fl,
+                  struct xfrm_state **xfrm)
 {
        int nx;
        int i, error;
@@ -649,7 +767,53 @@
                        local = tmpl->saddr.xfrm4_addr;
                }
 
-               x = xfrm_state_find(remote, local, fl, tmpl, policy, &error);
+               x = xfrm4_state_find(remote, local, fl, tmpl, policy, &error);
+
+               if (x && x->km.state == XFRM_STATE_VALID) {
+                       xfrm[nx++] = x;
+                       daddr = remote;
+                       saddr = local;
+                       continue;
+               }
+               if (x) {
+                       error = (x->km.state == XFRM_STATE_ERROR ?
+                                -EINVAL : -EAGAIN);
+                       xfrm_state_put(x);
+               }
+
+               if (!tmpl->optional)
+                       goto fail;
+       }
+       return nx;
+
+fail:
+       for (nx--; nx>=0; nx--)
+               xfrm_state_put(xfrm[nx]);
+       return error;
+}
+
+#if defined (CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
+static int
+xfrm6_tmpl_resolve(struct xfrm_policy *policy, struct flowi *fl,
+                 struct xfrm_state **xfrm)
+{
+       int nx;
+       int i, error;
+       struct in6_addr *daddr = fl->fl6_dst;
+       struct in6_addr *saddr = fl->fl6_src;
+
+       for (nx=0, i = 0; i < policy->xfrm_nr; i++) {
+               struct xfrm_state *x=NULL;
+               struct in6_addr *remote = daddr;
+               struct in6_addr *local = saddr;
+               struct xfrm_tmpl *tmpl = &policy->xfrm_vec[i];
+
+               if (tmpl->mode) {
+                       remote = (struct in6_addr*)&tmpl->id.daddr;
+                       local = (struct in6_addr*)&tmpl->saddr;
+               }
+
+               x = xfrm6_state_find(remote, local, fl, tmpl, policy, &error);
 
                if (x && x->km.state == XFRM_STATE_VALID) {
                        xfrm[nx++] = x;
@@ -673,6 +837,7 @@
                xfrm_state_put(xfrm[nx]);
        return error;
 }
+#endif
 
 /* Check that the bundle accepts the flow and its components are
  * still valid.
@@ -694,6 +859,24 @@
        return 0;
 }
 
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+static int xfrm6_bundle_ok(struct xfrm_dst *xdst, struct flowi *fl)
+{
+       do {
+               if (xdst->u.dst.ops != &xfrm6_dst_ops)
+                       return 1;
+
+               if (!xfrm6_selector_match(&xdst->u.dst.xfrm->sel, fl))
+                       return 0;
+               if (xdst->u.dst.xfrm->km.state != XFRM_STATE_VALID ||
+                   xdst->u.dst.path->obsolete > 0)
+                       return 0;
+               xdst = (struct xfrm_dst*)xdst->u.dst.child;
+       } while (xdst);
+       return 0;
+}
+#endif
+
 
 /* Allocate chain of dst_entry's, attach known xfrm's, calculate
  * all the metrics... Shortly, bundle a bundle.
@@ -744,7 +927,7 @@
                                                       .saddr = local }
                                                   }
                                         };
-               err = __ip_route_output_key(&rt, &fl_tunnel);
+               err = xfrm_dst_lookup((struct xfrm_dst**)&rt, &fl_tunnel, 
AF_INET);
                if (err)
                        goto error;
        } else {
@@ -791,6 +974,97 @@
        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;
+
+       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;
+       }
+
+       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;
+               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;
+       }
+       *dst_p = dst;
+       return 0;
+
+error:
+       if (dst)
+               dst_free(dst);
+       return err;
+}
+#endif
+
 /* Main function: finds/creates a bundle for given flow.
  *
  * At the moment we eat a raw IP route. Mostly to speed up lookups
@@ -806,9 +1080,7 @@
        int nx = 0;
        int err;
        u32 genid;
-
-       fl->oif = rt->u.dst.dev->ifindex;
-       fl->fl4_src = rt->rt_src;
+       u16 family = (*dst_p)->ops->family;
 
 restart:
        genid = xfrm_policy_genid;
@@ -821,11 +1093,12 @@
                if ((rt->u.dst.flags & DST_NOXFRM) || 
!xfrm_policy_list[XFRM_POLICY_OUT])
                        return 0;
 
-               policy = flow_lookup(XFRM_POLICY_OUT, fl);
-               if (!policy)
-                       return 0;
+               policy = flow_lookup(XFRM_POLICY_OUT, fl, family);
        }
 
+       if (!policy)
+               return 0;
+
        policy->curlft.use_time = (unsigned long)xtime.tv_sec;
 
        switch (policy->action) {
@@ -846,23 +1119,48 @@
                 * LATER: help from flow cache. It is optional, this
                 * is required only for output policy.
                 */
-               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);
+               if (family == AF_INET) {
+                       fl->oif = rt->u.dst.dev->ifindex;
+                       fl->fl4_src = rt->rt_src;
+                       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;
                }
-               read_unlock_bh(&policy->lock);
 
                if (dst)
                        break;
 
-               nx = xfrm_tmpl_resolve(policy, fl, xfrm);
                if (unlikely(nx<0)) {
                        err = nx;
                        if (err == -EAGAIN) {
@@ -873,7 +1171,18 @@
 
                                __set_task_state(tsk, TASK_INTERRUPTIBLE);
                                add_wait_queue(&km_waitq, &wait);
-                               err = xfrm_tmpl_resolve(policy, fl, xfrm);
+                               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;
+                               }
                                if (err == -EAGAIN)
                                        schedule();
                                __set_task_state(tsk, TASK_RUNNING);
@@ -896,7 +1205,19 @@
                }
 
                dst = &rt->u.dst;
-               err = xfrm_bundle_create(policy, xfrm, nx, fl, &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;
+               }
+                       
                if (unlikely(err)) {
                        int i;
                        for (i=0; i<nx; i++)
@@ -962,7 +1283,7 @@
 }
 
 static void
-_decode_session(struct sk_buff *skb, struct flowi *fl)
+_decode_session4(struct sk_buff *skb, struct flowi *fl)
 {
        struct iphdr *iph = skb->nh.iph;
        u8 *xprth = skb->nh.raw + iph->ihl*4;
@@ -1008,18 +1329,109 @@
        fl->fl4_src = iph->saddr;
 }
 
-int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb)
+#if defined (CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
+static inline int
+xfrm6_state_ok(struct xfrm_tmpl *tmpl, struct xfrm_state *x)
+{
+       return  x->id.proto == tmpl->id.proto &&
+               (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));
+}
+
+static inline int
+xfrm6_policy_ok(struct xfrm_tmpl *tmpl, struct sec_path *sp, int idx)
+{
+       for (; idx < sp->len; idx++) {
+               if (xfrm6_state_ok(tmpl, sp->xvec[idx]))
+                       return ++idx;
+       }
+       return -1;
+}
+
+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;
+               };
+       }
+}
+#endif
+
+int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb, 
+                       unsigned short family)
 {
        struct xfrm_policy *pol;
        struct flowi fl;
 
-       _decode_session(skb, &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 :
+               return 0;
+       }
 
        /* First, check used SA against their selectors. */
        if (skb->sp) {
                int i;
+
                for (i=skb->sp->len-1; i>=0; i--) {
-                       if (!xfrm4_selector_match(&skb->sp->xvec[i]->sel, &fl))
+                       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)
                                return 0;
                }
        }
@@ -1029,7 +1441,7 @@
                pol = xfrm_sk_policy_lookup(sk, dir, &fl);
 
        if (!pol)
-               pol = flow_lookup(dir, &fl);
+               pol = flow_lookup(dir, &fl, family);
 
        if (!pol)
                return 1;
@@ -1050,7 +1462,18 @@
                         * are implied between each two transformations.
                         */
                        for (i = pol->xfrm_nr-1, k = 0; i >= 0; i--) {
-                               k = xfrm_policy_ok(pol->xfrm_vec+i, sp, k);
+                               switch (family) {
+                               case AF_INET:
+                                       k = xfrm_policy_ok(pol->xfrm_vec+i, sp, 
k);
+                                       break;
+#if defined (CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
+                               case AF_INET6:
+                                       k = xfrm6_policy_ok(pol->xfrm_vec+i, 
sp, k);
+                                       break;
+#endif
+                               default:
+                                       k = -1;
+                               }
                                if (k < 0)
                                        goto reject;
                        }
@@ -1064,18 +1487,29 @@
        return 0;
 }
 
-int __xfrm_route_forward(struct sk_buff *skb)
+int __xfrm_route_forward(struct sk_buff *skb, unsigned short family)
 {
        struct flowi fl;
 
-       _decode_session(skb, &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:
+               return 0;
+       }
 
        return xfrm_lookup(&skb->dst, &fl, NULL, 0) == 0;
 }
 
 /* Optimize later using cookies and generation ids. */
 
-static struct dst_entry *xfrm4_dst_check(struct dst_entry *dst, u32 cookie)
+static struct dst_entry *xfrm_dst_check(struct dst_entry *dst, u32 cookie)
 {
        struct dst_entry *child = dst;
 
@@ -1091,19 +1525,19 @@
        return dst;
 }
 
-static void xfrm4_dst_destroy(struct dst_entry *dst)
+static void xfrm_dst_destroy(struct dst_entry *dst)
 {
        xfrm_state_put(dst->xfrm);
        dst->xfrm = NULL;
 }
 
-static void xfrm4_link_failure(struct sk_buff *skb)
+static void xfrm_link_failure(struct sk_buff *skb)
 {
        /* Impossible. Such dst must be popped before reaches point of failure. 
*/
        return;
 }
 
-static struct dst_entry *xfrm4_negative_advice(struct dst_entry *dst)
+static struct dst_entry *xfrm_negative_advice(struct dst_entry *dst)
 {
        if (dst) {
                if (dst->obsolete) {
@@ -1114,8 +1548,7 @@
        return dst;
 }
 
-
-static int xfrm4_garbage_collect(void)
+static void __xfrm_garbage_collect(void)
 {
        int i;
        struct xfrm_policy *pol;
@@ -1145,10 +1578,22 @@
                gc_list = dst->next;
                dst_free(dst);
        }
+}
 
+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 {
@@ -1192,7 +1637,7 @@
        return 0;
 }
 
-
+ 
 static void xfrm4_update_pmtu(struct dst_entry *dst, u32 mtu)
 {
        struct dst_entry *path = dst->path;
@@ -1203,6 +1648,18 @@
        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... :-)
@@ -1212,7 +1669,7 @@
  *
  * Consider this function as something like dark humour. :-)
  */
-static int xfrm4_get_mss(struct dst_entry *dst, u32 mtu)
+static int xfrm_get_mss(struct dst_entry *dst, u32 mtu)
 {
        int res = mtu - dst->header_len;
 
@@ -1247,16 +1704,32 @@
        .family =               AF_INET,
        .protocol =             __constant_htons(ETH_P_IP),
        .gc =                   xfrm4_garbage_collect,
-       .check =                xfrm4_dst_check,
-       .destroy =              xfrm4_dst_destroy,
-       .negative_advice =      xfrm4_negative_advice,
-       .link_failure =         xfrm4_link_failure,
+       .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 =              xfrm4_get_mss,
+       .get_mss =              xfrm_get_mss,
        .gc_thresh =            1024,
        .entry_size =           sizeof(struct xfrm_dst),
 };
 
+#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 */
+
 void __init xfrm_init(void)
 {
        xfrm4_dst_ops.kmem_cachep = kmem_cache_create("xfrm4_dst_cache",
@@ -1267,8 +1740,12 @@
        if (!xfrm4_dst_ops.kmem_cachep)
                panic("IP: failed to allocate xfrm4_dst_cache\n");
 
-       flow_cache_init();
+#if defined (CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
+       xfrm6_dst_ops.kmem_cachep = xfrm4_dst_ops.kmem_cachep;
+#endif
 
+       flow_cache_init();
        xfrm_state_init();
        xfrm_input_init();
 }
+
diff -ruN -x CVS linux-2.5.63/net/ipv4/xfrm_state.c 
linux25/net/ipv4/xfrm_state.c
--- linux-2.5.63/net/ipv4/xfrm_state.c  2003-02-25 04:05:38.000000000 +0900
+++ linux25/net/ipv4/xfrm_state.c       2003-03-05 20:17:33.000000000 +0900
@@ -1,3 +1,11 @@
+/* Changes
+ *
+ *     Mitsuru KANDA @USAGI       : IPv6 Support 
+ *     Kazunori MIYAZAWA @USAGI   :
+ *     Kunihiro Ishiguro          :
+ *     
+ */
+
 #include <net/xfrm.h>
 #include <linux/pfkeyv2.h>
 #include <linux/ipsec.h>
@@ -207,8 +215,8 @@
 }
 
 struct xfrm_state *
-xfrm_state_find(u32 daddr, u32 saddr, struct flowi *fl, struct xfrm_tmpl *tmpl,
-               struct xfrm_policy *pol, int *err)
+xfrm4_state_find(u32 daddr, u32 saddr, struct flowi *fl, struct xfrm_tmpl 
*tmpl,
+                struct xfrm_policy *pol, int *err)
 {
        unsigned h = ntohl(daddr);
        struct xfrm_state *x;
@@ -290,6 +298,7 @@
                        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;
@@ -318,14 +327,133 @@
        return x;
 }
 
+#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)
+{
+       unsigned h = ntohl(daddr->s6_addr32[2]^daddr->s6_addr32[3]);
+       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) &&
+                   x->props.reqid == tmpl->reqid &&
+                   (!ipv6_addr_cmp(saddr, (struct in6_addr 
*)&x->props.saddr)|| ipv6_addr_any(saddr)) &&
+                   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 (!xfrm6_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 (xfrm6_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. */
+               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->sel.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;
+                               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;
+}
+#endif /* CONFIG_IPV6 || CONFIG_IPV6_MODULE */
+
 void xfrm_state_insert(struct xfrm_state *x)
 {
        unsigned h = 0;
 
-       if (x->props.family == AF_INET)
+       switch (x->props.family) {
+       case AF_INET:
                h = ntohl(x->id.daddr.xfrm4_addr);
-       else if (x->props.family == AF_INET6)
+               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;
 
@@ -384,7 +512,7 @@
 }
 
 struct xfrm_state *
-xfrm_state_lookup(u32 daddr, u32 spi, u8 proto)
+xfrm4_state_lookup(u32 daddr, u32 spi, u8 proto)
 {
        unsigned h = ntohl(daddr^spi^proto);
        struct xfrm_state *x;
@@ -406,6 +534,31 @@
        return NULL;
 }
 
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+struct xfrm_state *
+xfrm6_state_lookup(struct in6_addr *daddr, u32 spi, u8 proto)
+{
+       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;
+
+       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;
+               }
+       }
+       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)
 {
@@ -445,7 +598,59 @@
                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;
@@ -458,6 +663,7 @@
        spin_unlock_bh(&xfrm_state_lock);
        return x0;
 }
+#endif
 
 /* Silly enough, but I'm lazy to build resolution list */
 
@@ -491,7 +697,18 @@
                return;
 
        if (minspi == maxspi) {
-               x0 = xfrm_state_lookup(x->id.daddr.xfrm4_addr, minspi, 
x->id.proto);
+               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;
+               }
                if (x0) {
                        xfrm_state_put(x0);
                        return;
@@ -503,7 +720,18 @@
                maxspi = ntohl(maxspi);
                for (h=0; h<maxspi-minspi+1; h++) {
                        spi = minspi + net_random()%(maxspi-minspi+1);
-                       x0 = xfrm_state_lookup(x->id.daddr.xfrm4_addr, 
htonl(spi), x->id.proto);
+                       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;
+                       }
                        if (x0 == NULL)
                                break;
                        xfrm_state_put(x0);
@@ -512,7 +740,18 @@
        }
        if (x->id.spi) {
                spin_lock_bh(&xfrm_state_lock);
-               h = ntohl(x->id.daddr.xfrm4_addr^x->id.spi^x->id.proto);
+               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;
                list_add(&x->byspi, xfrm_state_byspi+h);
                atomic_inc(&x->refcnt);
@@ -605,14 +844,21 @@
        int i;
 
        for (i=0; i<n; i++) {
-               if (x[i]->props.family == AF_INET &&
-                   !xfrm4_selector_match(&x[i]->sel, fl))
-                       return -EINVAL;
+               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)
-               if (x[i]->props.family == AF_INET6 &&
-                   !xfrm6_selector_match(&x[i]->sel, fl))
-                       return -EINVAL;
+               case AF_INET6:
+                       match = xfrm6_selector_match(&x[i]->sel, fl);
+                       break;
 #endif
+               default:
+                       match = 0;
+               }
+               if (!match)
+                       return -EINVAL;
        }
        return 0;
 }
@@ -722,118 +968,3 @@
        }
 }
 
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-struct xfrm_state *
-xfrm6_state_lookup(struct in6_addr *daddr, u32 spi, u8 proto)
-{
-       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;
-
-       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;
-               }
-       }
-       spin_unlock_bh(&xfrm_state_lock);
-       return NULL;
-}
-
-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 &&
-                   !memcmp(daddr, x->id.daddr.a6, sizeof(struct in6_addr)) &&
-                   mode == x->props.mode &&
-                   proto == x->id.proto &&
-                   !memcmp(saddr, x->props.saddr.a6, sizeof(struct in6_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) {
-               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;
-}
-
-void
-xfrm6_alloc_spi(struct xfrm_state *x, u32 minspi, u32 maxspi)
-{
-       u32 h;
-       struct xfrm_state *x0;
-
-       if (x->id.spi)
-               return;
-
-       if (minspi == maxspi) {
-               x0 = xfrm6_state_lookup((struct in6_addr*)x->id.daddr.a6, 
minspi, x->id.proto);
-               if (x0) {
-                       xfrm_state_put(x0);
-                       return;
-               }
-               x->id.spi = minspi;
-       } else {
-               u32 spi = 0;
-               minspi = ntohl(minspi);
-               maxspi = ntohl(maxspi);
-               for (h=0; h<maxspi-minspi+1; h++) {
-                       spi = minspi + net_random()%(maxspi-minspi+1);
-                       x0 = xfrm6_state_lookup((struct 
in6_addr*)x->id.daddr.a6, htonl(spi), x->id.proto);
-                       if (x0 == NULL)
-                               break;
-                       xfrm_state_put(x0);
-               }
-               x->id.spi = htonl(spi);
-       }
-       if (x->id.spi) {
-               spin_lock_bh(&xfrm_state_lock);
-               h = 
ntohl(x->id.daddr.a6[2]^x->id.daddr.a6[3]^x->id.spi^x->id.proto);
-               h = (h ^ (h>>10) ^ (h>>20)) % XFRM_DST_HSIZE;
-               list_add(&x->byspi, xfrm_state_byspi+h);
-               atomic_inc(&x->refcnt);
-               spin_unlock_bh(&xfrm_state_lock);
-               wake_up(&km_waitq);
-       }
-}
-#endif /* CONFIG_IPV6 || CONFIG_IPV6_MODULE */
diff -ruN -x CVS linux-2.5.63/net/ipv4/xfrm_user.c linux25/net/ipv4/xfrm_user.c
--- linux-2.5.63/net/ipv4/xfrm_user.c   2003-02-25 04:05:34.000000000 +0900
+++ linux25/net/ipv4/xfrm_user.c        2003-03-04 20:38:16.000000000 +0900
@@ -234,8 +234,8 @@
 
        switch (x->props.family) {
        case AF_INET:
-               x1 = xfrm_state_lookup(x->props.saddr.xfrm4_addr,
-                                      x->id.spi, x->id.proto);
+               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:
@@ -265,7 +265,7 @@
 
        switch (p->family) {
        case AF_INET:
-               x = xfrm_state_lookup(p->saddr.xfrm4_addr, p->spi, p->proto);
+               x = xfrm4_state_lookup(p->saddr.xfrm4_addr, p->spi, p->proto);
                break;
 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
        case AF_INET6:
@@ -395,7 +395,7 @@
 
        switch (p->family) {
        case AF_INET:
-               x = xfrm_state_lookup(p->saddr.xfrm4_addr, p->spi, p->proto);
+               x = xfrm4_state_lookup(p->saddr.xfrm4_addr, p->spi, p->proto);
                break;
 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
        case AF_INET6:
diff -ruN -x CVS linux-2.5.63/net/ipv6/Kconfig linux25/net/ipv6/Kconfig
--- linux-2.5.63/net/ipv6/Kconfig       2003-02-25 04:05:32.000000000 +0900
+++ linux25/net/ipv6/Kconfig    2003-03-04 20:38:16.000000000 +0900
@@ -17,5 +17,18 @@
 
          See <file:Documentation/networking/ip-sysctl.txt> for details.
 
-source "net/ipv6/netfilter/Kconfig"
+config INET6_AH
+       tristate "IPv6: AH transformation"
+       ---help---
+         Support for IPsec AH.
+
+         If unsure, say Y.
+
+config INET6_ESP
+       tristate "IPv6: ESP transformation"
+       ---help---
+         Support for IPsec ESP.
 
+         If unsure, say Y.
+
+source "net/ipv6/netfilter/Kconfig"
diff -ruN -x CVS linux-2.5.63/net/ipv6/Makefile linux25/net/ipv6/Makefile
--- linux-2.5.63/net/ipv6/Makefile      2003-02-25 04:05:39.000000000 +0900
+++ linux25/net/ipv6/Makefile   2003-03-05 00:28:59.000000000 +0900
@@ -10,4 +10,6 @@
                exthdrs.o sysctl_net_ipv6.o datagram.o proc.o \
                ip6_flowlabel.o ipv6_syms.o
 
+obj-$(CONFIG_INET6_AH) += ah6.o
+obj-$(CONFIG_INET6_ESP) += esp6.o
 obj-$(CONFIG_NETFILTER)        += netfilter/
diff -ruN -x CVS linux-2.5.63/net/ipv6/ah6.c linux25/net/ipv6/ah6.c
--- linux-2.5.63/net/ipv6/ah6.c 1970-01-01 09:00:00.000000000 +0900
+++ linux25/net/ipv6/ah6.c      2003-03-05 11:32:51.000000000 +0900
@@ -0,0 +1,361 @@
+/*
+ * Copyright (C)2002 USAGI/WIDE Project
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Authors
+ *
+ *     Mitsuru KANDA @USAGI       : IPv6 Support 
+ *     Kazunori MIYAZAWA @USAGI   :
+ *     Kunihiro Ishiguro          :
+ *     
+ *     This file is derived from net/ipv4/ah.c.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <net/ip.h>
+#include <net/xfrm.h>
+#include <linux/crypto.h>
+#include <linux/pfkeyv2.h>
+#include <net/icmp.h>
+#include <net/ipv6.h>
+#include <net/xfrm.h>
+#include <asm/scatterlist.h>
+
+#define AH_HLEN_NOICV  12
+
+/* XXX no ipv6 ah specific */
+#define NIP6(addr) \
+       ntohs((addr).s6_addr16[0]),\
+       ntohs((addr).s6_addr16[1]),\
+       ntohs((addr).s6_addr16[2]),\
+       ntohs((addr).s6_addr16[3]),\
+       ntohs((addr).s6_addr16[4]),\
+       ntohs((addr).s6_addr16[5]),\
+       ntohs((addr).s6_addr16[6]),\
+       ntohs((addr).s6_addr16[7])
+
+int ah6_output(struct sk_buff *skb)
+{
+       int err;
+       int hdr_len = sizeof(struct ipv6hdr);
+       struct dst_entry *dst = skb->dst;
+       struct xfrm_state *x  = dst->xfrm;
+       struct ipv6hdr *iph = NULL;
+       struct ip_auth_hdr *ah;
+       struct ah_data *ahp;
+       u16 nh_offset = 0;
+       u8 nexthdr;
+printk(KERN_DEBUG "%s\n", __FUNCTION__);
+       if (skb->ip_summed == CHECKSUM_HW && skb_checksum_help(skb) == NULL)
+               return -EINVAL;
+
+       spin_lock_bh(&x->lock);
+       if ((err = xfrm_state_check_expire(x)) != 0)
+               goto error;
+       if ((err = xfrm_state_check_space(x, skb)) != 0)
+               goto error;
+
+       if (x->props.mode) {
+               iph = skb->nh.ipv6h;
+               skb->nh.ipv6h = (struct ipv6hdr*)skb_push(skb, 
x->props.header_len);
+               skb->nh.ipv6h->version = 6;
+               skb->nh.ipv6h->payload_len = htons(skb->len - sizeof(struct 
ipv6hdr));
+               skb->nh.ipv6h->nexthdr = IPPROTO_AH;
+               memcpy(&skb->nh.ipv6h->saddr, &x->props.saddr, sizeof(struct 
in6_addr));
+               memcpy(&skb->nh.ipv6h->daddr, &x->id.daddr, sizeof(struct 
in6_addr));
+               ah = (struct ip_auth_hdr*)(skb->nh.ipv6h+1);
+               ah->nexthdr = IPPROTO_IPV6;
+       } else {
+               hdr_len = skb->h.raw - skb->nh.raw;
+               iph = kmalloc(hdr_len, GFP_ATOMIC);
+               if (!iph) {
+                       err = -ENOMEM;
+                       goto error;
+               }
+               memcpy(iph, skb->data, hdr_len);
+               skb->nh.ipv6h = (struct ipv6hdr*)skb_push(skb, 
x->props.header_len);
+               memcpy(skb->nh.ipv6h, iph, hdr_len);
+               nexthdr = xfrm6_clear_mutable_options(skb, &nh_offset, 
XFRM_POLICY_OUT);
+               if (nexthdr == 0)
+                       goto error;
+
+               skb->nh.raw[nh_offset] = IPPROTO_AH;
+               skb->nh.ipv6h->payload_len = htons(skb->len - sizeof(struct 
ipv6hdr));
+               ah = (struct ip_auth_hdr*)(skb->nh.raw+hdr_len);
+               skb->h.raw = (unsigned char*) ah;
+               ah->nexthdr = nexthdr;
+       }
+
+       skb->nh.ipv6h->priority    = 0;
+       skb->nh.ipv6h->flow_lbl[0] = 0;
+       skb->nh.ipv6h->flow_lbl[1] = 0;
+       skb->nh.ipv6h->flow_lbl[2] = 0;
+       skb->nh.ipv6h->hop_limit    = 0;
+
+       ahp = x->data;
+       ah->hdrlen  = (XFRM_ALIGN8(ahp->icv_trunc_len +
+               AH_HLEN_NOICV) >> 2) - 2;
+
+       ah->reserved = 0;
+       ah->spi = x->id.spi;
+       ah->seq_no = htonl(++x->replay.oseq);
+       ahp->icv(ahp, skb, ah->auth_data);
+
+       if (x->props.mode) {
+               skb->nh.ipv6h->hop_limit   = iph->hop_limit;
+               skb->nh.ipv6h->priority    = iph->priority;     
+               skb->nh.ipv6h->flow_lbl[0] = iph->flow_lbl[0];
+               skb->nh.ipv6h->flow_lbl[1] = iph->flow_lbl[1];
+               skb->nh.ipv6h->flow_lbl[2] = iph->flow_lbl[2];
+       } else {
+               memcpy(skb->nh.ipv6h, iph, hdr_len);
+               skb->nh.raw[nh_offset] = IPPROTO_AH;
+               skb->nh.ipv6h->payload_len = htons(skb->len - sizeof(struct 
ipv6hdr));
+               kfree (iph);
+       }
+
+       skb->nh.raw = skb->data;
+
+       x->curlft.bytes += skb->len;
+       x->curlft.packets++;
+       spin_unlock_bh(&x->lock);
+       if ((skb->dst = dst_pop(dst)) == NULL)
+               goto error_nolock;
+       return NET_XMIT_BYPASS;
+error:
+       spin_unlock_bh(&x->lock);
+error_nolock:
+       kfree_skb(skb);
+       return err;
+}
+
+int ah6_input(struct xfrm_state *x, struct sk_buff *skb)
+{
+       int ah_hlen;
+       struct ipv6hdr *iph;
+       struct ipv6_auth_hdr *ah;
+       struct ah_data *ahp;
+       unsigned char *tmp_hdr = NULL;
+       int hdr_len = skb->h.raw - skb->nh.raw;
+       u8 nexthdr = 0;
+
+       if (!pskb_may_pull(skb, sizeof(struct ip_auth_hdr)))
+               goto out;
+
+       ah = (struct ipv6_auth_hdr*)skb->data;
+       ahp = x->data;
+        ah_hlen = (ah->hdrlen + 2) << 2;
+
+        if (ah_hlen != XFRM_ALIGN8(ahp->icv_full_len + AH_HLEN_NOICV) &&
+            ah_hlen != XFRM_ALIGN8(ahp->icv_trunc_len + AH_HLEN_NOICV))
+                goto out;
+
+       if (!pskb_may_pull(skb, ah_hlen))
+               goto out;
+
+       /* We are going to _remove_ AH header to keep sockets happy,
+        * so... Later this can change. */
+       if (skb_cloned(skb) &&
+           pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
+               goto out;
+
+       tmp_hdr = kmalloc(hdr_len, GFP_ATOMIC);
+       if (!tmp_hdr)
+               goto out;
+       memcpy(tmp_hdr, skb->nh.raw, hdr_len);
+       ah = (struct ipv6_auth_hdr*)skb->data;
+       iph = skb->nh.ipv6h;
+
+        {
+               u8 auth_data[ahp->icv_trunc_len];
+
+               memcpy(auth_data, ah->auth_data, ahp->icv_trunc_len);
+               skb_push(skb, skb->data - skb->nh.raw);
+               ahp->icv(ahp, skb, ah->auth_data);
+               if (memcmp(ah->auth_data, auth_data, ahp->icv_trunc_len)) {
+                       if (net_ratelimit())
+                               printk(KERN_WARNING "ipsec ah authentication 
error\n");
+                       x->stats.integrity_failed++;
+                       goto free_out;
+               }
+       }
+
+       nexthdr = ah->nexthdr;
+       skb->nh.raw = skb_pull(skb, (ah->hdrlen+2)<<2);
+       memcpy(skb->nh.raw, tmp_hdr, hdr_len);
+       skb->nh.ipv6h->payload_len = htons(skb->len - sizeof(struct ipv6hdr));
+       skb_pull(skb, hdr_len);
+       skb->h.raw = skb->data;
+
+
+       kfree(tmp_hdr);
+
+       return nexthdr;
+
+free_out:
+       kfree(tmp_hdr);
+out:
+       return -EINVAL;
+}
+
+void ah6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, 
+        int type, int code, int offset, __u32 info)
+{
+       struct ipv6hdr *iph = (struct ipv6hdr*)skb->data;
+       struct ip_auth_hdr *ah = (struct ip_auth_hdr*)(skb->data+offset);
+       struct xfrm_state *x;
+
+       if (type != ICMPV6_DEST_UNREACH ||
+           type != ICMPV6_PKT_TOOBIG)
+               return;
+
+       x = xfrm6_state_lookup(&iph->daddr, ah->spi, IPPROTO_AH);
+       if (!x)
+               return;
+
+       printk(KERN_DEBUG "pmtu discvovery on SA AH/%08x/"
+                       "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+              ntohl(ah->spi), NIP6(iph->daddr));
+
+       xfrm_state_put(x);
+}
+
+static int ah6_init_state(struct xfrm_state *x, void *args)
+{
+       struct ah_data *ahp = NULL;
+       struct xfrm_algo_desc *aalg_desc;
+
+       /* null auth can use a zero length key */
+       if (x->aalg->alg_key_len > 512)
+               goto error;
+
+       ahp = kmalloc(sizeof(*ahp), GFP_KERNEL);
+       if (ahp == NULL)
+               return -ENOMEM;
+
+       memset(ahp, 0, sizeof(*ahp));
+
+       ahp->key = x->aalg->alg_key;
+       ahp->key_len = (x->aalg->alg_key_len+7)/8;
+       ahp->tfm = crypto_alloc_tfm(x->aalg->alg_name, 0);
+       if (!ahp->tfm)
+               goto error;
+       ahp->icv = ah_hmac_digest;
+       
+       /*
+        * Lookup the algorithm description maintained by xfrm_algo,
+        * verify crypto transform properties, and store information
+        * we need for AH processing.  This lookup cannot fail here
+        * after a successful crypto_alloc_tfm().
+        */
+       aalg_desc = xfrm_aalg_get_byname(x->aalg->alg_name);
+       BUG_ON(!aalg_desc);
+
+       if (aalg_desc->uinfo.auth.icv_fullbits/8 !=
+           crypto_tfm_alg_digestsize(ahp->tfm)) {
+               printk(KERN_INFO "AH: %s digestsize %u != %hu\n",
+                      x->aalg->alg_name, crypto_tfm_alg_digestsize(ahp->tfm),
+                      aalg_desc->uinfo.auth.icv_fullbits/8);
+               goto error;
+       }
+       
+       ahp->icv_full_len = aalg_desc->uinfo.auth.icv_fullbits/8;
+       ahp->icv_trunc_len = aalg_desc->uinfo.auth.icv_truncbits/8;
+       
+       ahp->work_icv = kmalloc(ahp->icv_full_len, GFP_KERNEL);
+       if (!ahp->work_icv)
+               goto error;
+       
+       x->props.header_len = XFRM_ALIGN8(ahp->icv_trunc_len + AH_HLEN_NOICV);
+       if (x->props.mode)
+               x->props.header_len += 20;
+       x->data = ahp;
+
+       return 0;
+
+error:
+       if (ahp) {
+               if (ahp->work_icv)
+                       kfree(ahp->work_icv);
+               if (ahp->tfm)
+                       crypto_free_tfm(ahp->tfm);
+               kfree(ahp);
+       }
+       return -EINVAL;
+}
+
+static void ah6_destroy(struct xfrm_state *x)
+{
+       struct ah_data *ahp = x->data;
+
+       if (ahp->work_icv) {
+               kfree(ahp->work_icv);
+               ahp->work_icv = NULL;
+       }
+       if (ahp->tfm) {
+               crypto_free_tfm(ahp->tfm);
+               ahp->tfm = NULL;
+       }
+}
+
+static struct xfrm_type ah6_type =
+{
+       .description    = "AH6",
+       .proto          = IPPROTO_AH,
+       .init_state     = ah6_init_state,
+       .destructor     = ah6_destroy,
+       .input          = ah6_input,
+       .output         = ah6_output
+};
+
+static struct inet6_protocol ah6_protocol = {
+       .handler        =       xfrm6_rcv,
+       .err_handler    =       ah6_err,
+};
+
+int __init ah6_init(void)
+{
+       SET_MODULE_OWNER(&ah6_type);
+
+       if (xfrm6_register_type(&ah6_type) < 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);
+               return -EAGAIN;
+       }
+
+       return 0;
+}
+
+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)
+               printk(KERN_INFO "ipv6 ah close: can't remove xfrm type\n");
+
+}
+
+module_init(ah6_init);
+module_exit(ah6_fini);
+
+MODULE_LICENSE("GPL");
diff -ruN -x CVS linux-2.5.63/net/ipv6/esp6.c linux25/net/ipv6/esp6.c
--- linux-2.5.63/net/ipv6/esp6.c        1970-01-01 09:00:00.000000000 +0900
+++ linux25/net/ipv6/esp6.c     2003-03-05 11:33:20.000000000 +0900
@@ -0,0 +1,526 @@
+/*
+ * Copyright (C)2002 USAGI/WIDE Project
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Authors
+ *
+ *     Mitsuru KANDA @USAGI       : IPv6 Support 
+ *     Kazunori MIYAZAWA @USAGI   :
+ *     Kunihiro Ishiguro          :
+ *     
+ *     This file is derived from net/ipv4/esp.c
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <net/ip.h>
+#include <net/xfrm.h>
+#include <asm/scatterlist.h>
+#include <linux/crypto.h>
+#include <linux/pfkeyv2.h>
+#include <linux/random.h>
+#include <net/icmp.h>
+#include <net/ipv6.h>
+#include <linux/icmpv6.h>
+
+#define MAX_SG_ONSTACK 4
+
+/* BUGS:
+ * - we assume replay seqno is always present.
+ */
+
+/* Move to common area: it is shared with AH. */
+/* Common with AH after some work on arguments. */
+
+/* XXX no ipv6 esp specific */
+#define NIP6(addr) \
+       ntohs((addr).s6_addr16[0]),\
+       ntohs((addr).s6_addr16[1]),\
+       ntohs((addr).s6_addr16[2]),\
+       ntohs((addr).s6_addr16[3]),\
+       ntohs((addr).s6_addr16[4]),\
+       ntohs((addr).s6_addr16[5]),\
+       ntohs((addr).s6_addr16[6]),\
+       ntohs((addr).s6_addr16[7])
+
+static int get_offset(u8 *packet, u32 packet_len, u8 *nexthdr, struct 
ipv6_opt_hdr **prevhdr)
+{
+       u16 offset = sizeof(struct ipv6hdr);
+       struct ipv6_opt_hdr *exthdr = (struct ipv6_opt_hdr*)(packet + offset);
+       u8 nextnexthdr;
+
+       *nexthdr = ((struct ipv6hdr*)packet)->nexthdr;
+
+       while (offset + 1 < packet_len) {
+
+               switch (*nexthdr) {
+
+               case NEXTHDR_HOP:
+               case NEXTHDR_ROUTING:
+                       offset += ipv6_optlen(exthdr);
+                       *nexthdr = exthdr->nexthdr;
+                       *prevhdr = exthdr;
+                       exthdr = (struct ipv6_opt_hdr*)(packet + offset);
+                       break;
+
+               case NEXTHDR_DEST:
+                       nextnexthdr =
+                               ((struct ipv6_opt_hdr*)(packet + offset + 
ipv6_optlen(exthdr)))->nexthdr;
+                       /* XXX We know the option is inner dest opt
+                          with next next header check. */
+                       if (nextnexthdr != NEXTHDR_HOP &&
+                           nextnexthdr != NEXTHDR_ROUTING &&
+                           nextnexthdr != NEXTHDR_DEST) {
+                                       return offset;
+                       }
+                       offset += ipv6_optlen(exthdr);
+                       *nexthdr = exthdr->nexthdr;
+                       *prevhdr = exthdr;
+                       exthdr = (struct ipv6_opt_hdr*)(packet + offset);
+                       break;
+
+               default :
+                       return offset;
+               }
+       }
+
+       return offset;
+}
+
+int esp6_output(struct sk_buff *skb)
+{
+       int err;
+       int hdr_len = 0;
+       struct dst_entry *dst = skb->dst;
+       struct xfrm_state *x  = dst->xfrm;
+       struct ipv6hdr *iph = NULL, *top_iph;
+       struct ip_esp_hdr *esph;
+       struct crypto_tfm *tfm;
+       struct esp_data *esp;
+       struct sk_buff *trailer;
+       struct ipv6_opt_hdr *prevhdr = NULL;
+       int blksize;
+       int clen;
+       int alen;
+       int nfrags;
+       u8 nexthdr;
+printk(KERN_DEBUG "%s\n", __FUNCTION__);
+       /* First, if the skb is not checksummed, complete checksum. */
+       if (skb->ip_summed == CHECKSUM_HW && skb_checksum_help(skb) == NULL)
+               return -EINVAL;
+
+       spin_lock_bh(&x->lock);
+       if ((err = xfrm_state_check_expire(x)) != 0)
+               goto error;
+       if ((err = xfrm_state_check_space(x, skb)) != 0)
+               goto error;
+
+       err = -ENOMEM;
+
+       /* Strip IP header in transport mode. Save it. */
+
+       if (!x->props.mode) {
+               hdr_len = get_offset(skb->nh.raw, skb->len, &nexthdr, &prevhdr);
+               iph = kmalloc(hdr_len, GFP_ATOMIC);
+               if (!iph) {
+                       err = -ENOMEM;
+                       goto error;
+               }
+               memcpy(iph, skb->nh.raw, hdr_len);
+               __skb_pull(skb, hdr_len);
+       }
+
+       /* Now skb is pure payload to encrypt */
+
+       /* Round to block size */
+       clen = skb->len;
+
+       esp = x->data;
+       alen = esp->auth.icv_trunc_len;
+       tfm = esp->conf.tfm;
+       blksize = crypto_tfm_alg_blocksize(tfm);
+       clen = (clen + 2 + blksize-1)&~(blksize-1);
+       if (esp->conf.padlen)
+               clen = (clen + esp->conf.padlen-1)&~(esp->conf.padlen-1);
+
+       if ((nfrags = skb_cow_data(skb, clen-skb->len+alen, &trailer)) < 0) {
+               if (!x->props.mode && iph) kfree(iph);
+               goto error;
+       }
+
+       /* Fill padding... */
+       do {
+               int i;
+               for (i=0; i<clen-skb->len - 2; i++)
+                       *(u8*)(trailer->tail + i) = i+1;
+       } while (0);
+       *(u8*)(trailer->tail + clen-skb->len - 2) = (clen - skb->len)-2;
+       pskb_put(skb, trailer, clen - skb->len);
+
+       if (x->props.mode) {
+               iph = skb->nh.ipv6h;
+               top_iph = (struct ipv6hdr*)skb_push(skb, x->props.header_len);
+               esph = (struct ip_esp_hdr*)(top_iph+1);
+               *(u8*)(trailer->tail - 1) = IPPROTO_IPV6;
+               top_iph->version = 6;
+               top_iph->priority = iph->priority;
+               top_iph->flow_lbl[0] = iph->flow_lbl[0];
+               top_iph->flow_lbl[1] = iph->flow_lbl[1];
+               top_iph->flow_lbl[2] = iph->flow_lbl[2];
+               top_iph->nexthdr = IPPROTO_ESP;
+               top_iph->payload_len = htons(skb->len + alen);
+               top_iph->hop_limit = iph->hop_limit;
+               memcpy(&top_iph->saddr, (struct in6_addr *)&x->props.saddr, 
sizeof(struct ipv6hdr));
+               memcpy(&top_iph->daddr, (struct in6_addr *)&x->id.daddr, 
sizeof(struct ipv6hdr));
+       } else { 
+               /* XXX exthdr */
+               esph = (struct ip_esp_hdr*)skb_push(skb, x->props.header_len);
+               skb->h.raw = (unsigned char*)esph;
+               top_iph = (struct ipv6hdr*)skb_push(skb, hdr_len);
+               memcpy(top_iph, iph, hdr_len);
+               kfree(iph);
+               top_iph->payload_len = htons(skb->len + alen - sizeof(struct 
ipv6hdr));
+               if (prevhdr) {
+                       prevhdr->nexthdr = IPPROTO_ESP;
+               } else {
+                       top_iph->nexthdr = IPPROTO_ESP;
+               }
+               *(u8*)(trailer->tail - 1) = nexthdr;
+       }
+
+       esph->spi = x->id.spi;
+       esph->seq_no = htonl(++x->replay.oseq);
+
+       if (esp->conf.ivlen)
+               crypto_cipher_set_iv(tfm, esp->conf.ivec, 
crypto_tfm_alg_ivsize(tfm));
+
+       do {
+               struct scatterlist sgbuf[nfrags>MAX_SG_ONSTACK ? 0 : nfrags];
+               struct scatterlist *sg = sgbuf;
+
+               if (unlikely(nfrags > MAX_SG_ONSTACK)) {
+                       sg = kmalloc(sizeof(struct scatterlist)*nfrags, 
GFP_ATOMIC);
+                       if (!sg)
+                               goto error;
+               }
+               skb_to_sgvec(skb, sg, esph->enc_data+esp->conf.ivlen-skb->data, 
clen);
+               crypto_cipher_encrypt(tfm, sg, sg, clen);
+               if (unlikely(sg != sgbuf))
+                       kfree(sg);
+       } while (0);
+
+       if (esp->conf.ivlen) {
+               memcpy(esph->enc_data, esp->conf.ivec, 
crypto_tfm_alg_ivsize(tfm));
+               crypto_cipher_get_iv(tfm, esp->conf.ivec, 
crypto_tfm_alg_ivsize(tfm));
+       }
+
+       if (esp->auth.icv_full_len) {
+               esp->auth.icv(esp, skb, (u8*)esph-skb->data,
+                       8+esp->conf.ivlen+clen, trailer->tail);
+               pskb_put(skb, trailer, alen);
+       }
+
+       skb->nh.raw = skb->data;
+
+       x->curlft.bytes += skb->len;
+       x->curlft.packets++;
+       spin_unlock_bh(&x->lock);
+       if ((skb->dst = dst_pop(dst)) == NULL)
+               goto error_nolock;
+       return NET_XMIT_BYPASS;
+
+error:
+       spin_unlock_bh(&x->lock);
+error_nolock:
+       kfree_skb(skb);
+       return err;
+}
+
+int esp6_input(struct xfrm_state *x, struct sk_buff *skb)
+{
+       struct ipv6hdr *iph;
+       struct ip_esp_hdr *esph;
+       struct esp_data *esp = x->data;
+       struct sk_buff *trailer;
+       int blksize = crypto_tfm_alg_blocksize(esp->conf.tfm);
+       int alen = esp->auth.icv_trunc_len;
+       int elen = skb->len - 8 - esp->conf.ivlen - alen;
+
+       int hdr_len = skb->h.raw - skb->nh.raw;
+       int nfrags;
+       u8 ret_nexthdr = 0;
+       unsigned char *tmp_hdr = NULL;
+
+       if (!pskb_may_pull(skb, sizeof(struct ip_esp_hdr)))
+               goto out;
+
+       if (elen <= 0 || (elen & (blksize-1)))
+               goto out;
+
+       tmp_hdr = kmalloc(hdr_len, GFP_ATOMIC);
+       if (!tmp_hdr)
+               goto out;
+       memcpy(tmp_hdr, skb->nh.raw, hdr_len);
+
+       /* If integrity check is required, do this. */
+        if (esp->auth.icv_full_len) {
+               u8 sum[esp->auth.icv_full_len];
+               u8 sum1[alen];
+
+               esp->auth.icv(esp, skb, 0, skb->len-alen, sum);
+
+               if (skb_copy_bits(skb, skb->len-alen, sum1, alen))
+                       BUG();
+
+               if (unlikely(memcmp(sum, sum1, alen))) {
+                       x->stats.integrity_failed++;
+                       goto out;
+               }
+       }
+
+       if ((nfrags = skb_cow_data(skb, 0, &trailer)) < 0)
+               goto out;
+
+       skb->ip_summed = CHECKSUM_NONE;
+
+       esph = (struct ip_esp_hdr*)skb->data;
+       iph = skb->nh.ipv6h;
+
+       /* Get ivec. This can be wrong, check against another impls. */
+       if (esp->conf.ivlen)
+               crypto_cipher_set_iv(esp->conf.tfm, esph->enc_data, 
crypto_tfm_alg_ivsize(esp->conf.tfm));
+
+        {
+               u8 nexthdr[2];
+               struct scatterlist sgbuf[nfrags>MAX_SG_ONSTACK ? 0 : nfrags];
+               struct scatterlist *sg = sgbuf;
+               u8 padlen;
+
+               if (unlikely(nfrags > MAX_SG_ONSTACK)) {
+                       sg = kmalloc(sizeof(struct scatterlist)*nfrags, 
GFP_ATOMIC);
+                       if (!sg)
+                               goto out;
+               }
+               skb_to_sgvec(skb, sg, 8+esp->conf.ivlen, elen);
+               crypto_cipher_decrypt(esp->conf.tfm, sg, sg, elen);
+               if (unlikely(sg != sgbuf))
+                       kfree(sg);
+
+               if (skb_copy_bits(skb, skb->len-alen-2, nexthdr, 2))
+                       BUG();
+
+               padlen = nexthdr[0];
+               if (padlen+2 >= elen) {
+                       if (net_ratelimit()) {
+                               printk(KERN_WARNING "ipsec esp packet is 
garbage padlen=%d, elen=%d\n", padlen+2, elen);
+                       }
+                       goto out;
+               }
+               /* ... check padding bits here. Silly. :-) */ 
+
+               ret_nexthdr = nexthdr[1];
+               pskb_trim(skb, skb->len - alen - padlen - 2);
+               skb->h.raw = skb_pull(skb, 8 + esp->conf.ivlen);
+               skb->nh.raw += 8 + esp->conf.ivlen;
+               memcpy(skb->nh.raw, tmp_hdr, hdr_len);
+       }
+       kfree(tmp_hdr);
+       return ret_nexthdr;
+
+out:
+       return -EINVAL;
+}
+
+static u32 esp6_get_max_size(struct xfrm_state *x, int mtu)
+{
+       struct esp_data *esp = x->data;
+       u32 blksize = crypto_tfm_alg_blocksize(esp->conf.tfm);
+
+       if (x->props.mode) {
+               mtu = (mtu + 2 + blksize-1)&~(blksize-1);
+       } else {
+               /* The worst case. */
+               mtu += 2 + blksize;
+       }
+       if (esp->conf.padlen)
+               mtu = (mtu + esp->conf.padlen-1)&~(esp->conf.padlen-1);
+
+       return mtu + x->props.header_len + esp->auth.icv_full_len;
+}
+
+void esp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
+               int type, int code, int offset, __u32 info)
+{
+       struct ipv6hdr *iph = (struct ipv6hdr*)skb->data;
+       struct ip_esp_hdr *esph = (struct ip_esp_hdr*)(skb->data+offset);
+       struct xfrm_state *x;
+
+       if (type != ICMPV6_DEST_UNREACH ||
+           type != ICMPV6_PKT_TOOBIG)
+               return;
+
+       x = xfrm6_state_lookup(&iph->daddr, esph->spi, IPPROTO_ESP);
+       if (!x)
+               return;
+       printk(KERN_DEBUG "pmtu discvovery on SA ESP/%08x/"
+                       "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n", 
+                       ntohl(esph->spi), NIP6(iph->daddr));
+       xfrm_state_put(x);
+}
+
+void esp6_destroy(struct xfrm_state *x)
+{
+       struct esp_data *esp = x->data;
+
+       if (esp->conf.tfm) {
+               crypto_free_tfm(esp->conf.tfm);
+               esp->conf.tfm = NULL;
+       }
+       if (esp->conf.ivec) {
+               kfree(esp->conf.ivec);
+               esp->conf.ivec = NULL;
+       }
+       if (esp->auth.tfm) {
+               crypto_free_tfm(esp->auth.tfm);
+               esp->auth.tfm = NULL;
+       }
+       if (esp->auth.work_icv) {
+               kfree(esp->auth.work_icv);
+               esp->auth.work_icv = NULL;
+       }
+}
+
+int esp6_init_state(struct xfrm_state *x, void *args)
+{
+       struct esp_data *esp = NULL;
+
+       if (x->aalg) {
+               if (x->aalg->alg_key_len == 0 || x->aalg->alg_key_len > 512)
+                       goto error;
+       }
+       if (x->ealg == NULL || x->ealg->alg_key_len == 0)
+               goto error;
+
+       esp = kmalloc(sizeof(*esp), GFP_KERNEL);
+       if (esp == NULL)
+               return -ENOMEM;
+
+       memset(esp, 0, sizeof(*esp));
+
+       if (x->aalg) {
+               struct xfrm_algo_desc *aalg_desc;
+
+               esp->auth.key = x->aalg->alg_key;
+               esp->auth.key_len = (x->aalg->alg_key_len+7)/8;
+               esp->auth.tfm = crypto_alloc_tfm(x->aalg->alg_name, 0);
+               if (esp->auth.tfm == NULL)
+                       goto error;
+               esp->auth.icv = esp_hmac_digest;
+ 
+               aalg_desc = xfrm_aalg_get_byname(x->aalg->alg_name);
+               BUG_ON(!aalg_desc);
+ 
+               if (aalg_desc->uinfo.auth.icv_fullbits/8 !=
+                       crypto_tfm_alg_digestsize(esp->auth.tfm)) {
+                               printk(KERN_INFO "ESP: %s digestsize %u != 
%hu\n",
+                                       x->aalg->alg_name,
+                                       
crypto_tfm_alg_digestsize(esp->auth.tfm),
+                                       aalg_desc->uinfo.auth.icv_fullbits/8);
+                               goto error;
+               }
+ 
+               esp->auth.icv_full_len = aalg_desc->uinfo.auth.icv_fullbits/8;
+               esp->auth.icv_trunc_len = aalg_desc->uinfo.auth.icv_truncbits/8;
+ 
+               esp->auth.work_icv = kmalloc(esp->auth.icv_full_len, 
GFP_KERNEL);
+               if (!esp->auth.work_icv)
+                       goto error;
+       }
+       esp->conf.key = x->ealg->alg_key;
+       esp->conf.key_len = (x->ealg->alg_key_len+7)/8;
+       esp->conf.tfm = crypto_alloc_tfm(x->ealg->alg_name, 
CRYPTO_TFM_MODE_CBC);
+       if (esp->conf.tfm == NULL)
+               goto error;
+       esp->conf.ivlen = crypto_tfm_alg_ivsize(esp->conf.tfm);
+       esp->conf.padlen = 0;
+       if (esp->conf.ivlen) {
+               esp->conf.ivec = kmalloc(esp->conf.ivlen, GFP_KERNEL);
+               get_random_bytes(esp->conf.ivec, esp->conf.ivlen);
+       }
+       crypto_cipher_setkey(esp->conf.tfm, esp->conf.key, esp->conf.key_len);
+       x->props.header_len = 8 + esp->conf.ivlen;
+       if (x->props.mode)
+               x->props.header_len += 40;  /* XXX ext hdr */
+       x->data = esp;
+       return 0;
+
+error:
+       if (esp) {
+               if (esp->auth.tfm)
+                       crypto_free_tfm(esp->auth.tfm);
+               if (esp->auth.work_icv)
+                       kfree(esp->auth.work_icv);
+               if (esp->conf.tfm)
+                       crypto_free_tfm(esp->conf.tfm);
+               kfree(esp);
+       }
+       return -EINVAL;
+}
+
+static struct xfrm_type esp6_type =
+{
+       .description    = "ESP6",
+       .proto          = IPPROTO_ESP,
+       .init_state     = esp6_init_state,
+       .destructor     = esp6_destroy,
+       .get_max_size   = esp6_get_max_size,
+       .input          = esp6_input,
+       .output         = esp6_output
+};
+
+static struct inet6_protocol esp6_protocol = {
+       .handler        =       xfrm6_rcv,
+       .err_handler    =       esp6_err,
+};
+
+int __init esp6_init(void)
+{
+       SET_MODULE_OWNER(&esp6_type);
+       if (xfrm6_register_type(&esp6_type) < 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);
+               return -EAGAIN;
+       }
+
+       return 0;
+}
+
+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)
+               printk(KERN_INFO "ipv6 esp close: can't remove xfrm type\n");
+}
+
+module_init(esp6_init);
+module_exit(esp6_fini);
+
+MODULE_LICENSE("GPL");
diff -ruN -x CVS linux-2.5.63/net/ipv6/ip6_input.c linux25/net/ipv6/ip6_input.c
--- linux-2.5.63/net/ipv6/ip6_input.c   2003-02-25 04:05:39.000000000 +0900
+++ linux25/net/ipv6/ip6_input.c        2003-03-04 20:38:16.000000000 +0900
@@ -150,7 +150,8 @@
           It would be stupid to detect for optional headers,
           which are missing with probability of 200%
         */
-       if (nexthdr != IPPROTO_TCP && nexthdr != IPPROTO_UDP) {
+       if (nexthdr != IPPROTO_TCP && nexthdr != IPPROTO_UDP &&
+           nexthdr != NEXTHDR_AUTH && nexthdr != NEXTHDR_ESP) {
                nhoff = ipv6_parse_exthdrs(&skb, nhoff);
                if (nhoff < 0)
                        return 0;
diff -ruN -x CVS linux-2.5.63/net/ipv6/ip6_output.c 
linux25/net/ipv6/ip6_output.c
--- linux-2.5.63/net/ipv6/ip6_output.c  2003-02-25 04:05:06.000000000 +0900
+++ linux25/net/ipv6/ip6_output.c       2003-03-04 20:38:16.000000000 +0900
@@ -192,6 +192,11 @@
        int seg_len = skb->len;
        int hlimit;
        u32 mtu;
+       int err = 0;
+
+       if ((err = xfrm_lookup(&skb->dst, fl, sk, 0)) < 0) {
+               return err;
+       }
 
        if (opt) {
                int head_room;
@@ -576,6 +581,13 @@
        }
        pktlength = length;
 
+        if (dst) {
+               if ((err = xfrm_lookup(&dst, fl, sk, 0)) < 0) {
+                       dst_release(dst);       
+                       return -ENETUNREACH;
+               }
+        }
+
        if (hlimit < 0) {
                if (ipv6_addr_is_multicast(fl->fl6_dst))
                        hlimit = np->mcast_hops;
@@ -630,10 +642,8 @@
                err = 0;
                if (flags&MSG_PROBE)
                        goto out;
-
-               skb = sock_alloc_send_skb(sk, pktlength + 15 +
-                                         dev->hard_header_len,
-                                         flags & MSG_DONTWAIT, &err);
+               /* alloc skb with mtu as we do in the IPv4 stack for IPsec */
+               skb = sock_alloc_send_skb(sk, mtu, flags & MSG_DONTWAIT, &err);
 
                if (skb == NULL) {
                        IP6_INC_STATS(Ip6OutDiscards);
@@ -663,6 +673,8 @@
                err = getfrag(data, &hdr->saddr,
                              ((char *) hdr) + (pktlength - length),
                              0, length);
+               if (!opt || !opt->dst1opt)
+                       skb->h.raw = ((char *) hdr) + (pktlength - length);
 
                if (!err) {
                        IP6_INC_STATS(Ip6OutRequests);
diff -ruN -x CVS linux-2.5.63/net/ipv6/ndisc.c linux25/net/ipv6/ndisc.c
--- linux-2.5.63/net/ipv6/ndisc.c       2003-02-25 04:05:34.000000000 +0900
+++ linux25/net/ipv6/ndisc.c    2003-03-05 11:30:41.000000000 +0900
@@ -71,6 +71,7 @@
 #include <net/addrconf.h>
 #include <net/icmp.h>
 
+#include <net/flow.h>
 #include <net/checksum.h>
 #include <linux/proc_fs.h>
 
@@ -335,8 +336,6 @@
        unsigned char ha[MAX_ADDR_LEN];
        unsigned char *h_dest = NULL;
 
-       skb_reserve(skb, (dev->hard_header_len + 15) & ~15);
-
        if (dev->hard_header) {
                if (ipv6_addr_type(daddr) & IPV6_ADDR_MULTICAST) {
                        ndisc_mc_map(daddr, ha, dev, 1);
@@ -373,10 +372,50 @@
  *     Send a Neighbour Advertisement
  */
 
+int ndisc_output(struct sk_buff *skb)
+{
+       if (skb) {
+               struct neighbour *neigh = (skb->dst ? skb->dst->neighbour : 
NULL);
+               if (ndisc_build_ll_hdr(skb, skb->dev, &skb->nh.ipv6h->daddr, 
neigh, skb->len) == 0) {
+                       kfree_skb(skb);
+                       return -EINVAL;
+               }
+               dev_queue_xmit(skb);
+               return 0;
+       }
+       return -EINVAL;
+}
+
+static inline void ndisc_rt_init(struct rt6_info *rt, struct net_device *dev,
+                           struct neighbour *neigh)
+{
+       rt->rt6i_dev      = dev;
+       rt->rt6i_nexthop  = neigh;
+       rt->rt6i_expires  = 0;
+       rt->rt6i_flags    = RTF_LOCAL;
+       rt->rt6i_metric   = 0;
+       rt->rt6i_hoplimit = 255;
+       rt->u.dst.output  = ndisc_output;
+}
+
+static inline void ndisc_flow_init(struct flowi *fl, u8 type,
+                           struct in6_addr *saddr, struct in6_addr *daddr)
+{
+       memset(fl, 0, sizeof(*fl));
+       fl->fl6_src             = saddr;
+       fl->fl6_dst             = daddr;
+       fl->proto               = IPPROTO_ICMPV6;
+       fl->uli_u.icmpt.type    = type;
+       fl->uli_u.icmpt.code    = 0;
+}
+
 static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
                   struct in6_addr *daddr, struct in6_addr *solicited_addr,
-                  int router, int solicited, int override, int inc_opt) 
+                  int router, int solicited, int override, int inc_opt) 
 {
+       struct flowi fl;
+       struct rt6_info *rt = NULL;
+       struct dst_entry* dst;
         struct sock *sk = ndisc_socket->sk;
         struct nd_msg *msg;
         int len;
@@ -385,6 +424,22 @@
 
        len = sizeof(struct icmp6hdr) + sizeof(struct in6_addr);
 
+       rt = ndisc_get_dummy_rt();
+       if (!rt) 
+               return;
+
+       ndisc_flow_init(&fl, NDISC_NEIGHBOUR_ADVERTISEMENT, solicited_addr, 
daddr);
+       ndisc_rt_init(rt, dev, neigh);  
+
+       dst = (struct dst_entry*)rt;
+       dst_clone(dst);
+
+       err = xfrm_lookup(&dst, &fl, NULL, 0);
+       if (err < 0) {
+               dst_release(dst);
+               return;
+       }
+
        if (inc_opt) {
                if (dev->addr_len)
                        len += NDISC_OPT_SPACE(dev->addr_len);
@@ -400,14 +455,10 @@
                return;
        }
 
-       if (ndisc_build_ll_hdr(skb, dev, daddr, neigh, len) == 0) {
-               kfree_skb(skb);
-               return;
-       }
-
+       skb_reserve(skb, (dev->hard_header_len + 15) & ~15);
        ip6_nd_hdr(sk, skb, dev, solicited_addr, daddr, IPPROTO_ICMPV6, len);
 
-       msg = (struct nd_msg *) skb_put(skb, len);
+       skb->h.raw = (unsigned char*) msg = (struct nd_msg *) skb_put(skb, len);
 
         msg->icmph.icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT;
         msg->icmph.icmp6_code = 0;
@@ -430,7 +481,9 @@
                                                 csum_partial((__u8 *) msg, 
                                                              len, 0));
 
-       dev_queue_xmit(skb);
+       dst_clone(dst);
+       skb->dst = dst;
+       dst_output(skb);
 
        ICMP6_INC_STATS(Icmp6OutNeighborAdvertisements);
        ICMP6_INC_STATS(Icmp6OutMsgs);
@@ -440,6 +493,9 @@
                   struct in6_addr *solicit,
                   struct in6_addr *daddr, struct in6_addr *saddr) 
 {
+       struct flowi fl;
+       struct rt6_info *rt = NULL;
+       struct dst_entry* dst;
         struct sock *sk = ndisc_socket->sk;
         struct sk_buff *skb;
         struct nd_msg *msg;
@@ -454,6 +510,22 @@
                saddr = &addr_buf;
        }
 
+       rt = ndisc_get_dummy_rt();
+       if (!rt) 
+               return;
+
+       ndisc_flow_init(&fl, NDISC_NEIGHBOUR_SOLICITATION, saddr, daddr);
+       ndisc_rt_init(rt, dev, neigh);  
+
+       dst = (struct dst_entry*)rt;
+       dst_clone(dst);
+
+       err = xfrm_lookup(&dst, &fl, NULL, 0);
+       if (err < 0) {
+               dst_release(dst);
+               return;
+       }
+
        len = sizeof(struct icmp6hdr) + sizeof(struct in6_addr);
        send_llinfo = dev->addr_len && ipv6_addr_type(saddr) != IPV6_ADDR_ANY;
        if (send_llinfo)
@@ -466,14 +538,10 @@
                return;
        }
 
-       if (ndisc_build_ll_hdr(skb, dev, daddr, neigh, len) == 0) {
-               kfree_skb(skb);
-               return;
-       }
-
+       skb_reserve(skb, (dev->hard_header_len + 15) & ~15);
        ip6_nd_hdr(sk, skb, dev, saddr, daddr, IPPROTO_ICMPV6, len);
 
-       msg = (struct nd_msg *)skb_put(skb, len);
+       skb->h.raw = (unsigned char*) msg = (struct nd_msg *)skb_put(skb, len);
        msg->icmph.icmp6_type = NDISC_NEIGHBOUR_SOLICITATION;
        msg->icmph.icmp6_code = 0;
        msg->icmph.icmp6_cksum = 0;
@@ -492,7 +560,9 @@
                                                 csum_partial((__u8 *) msg, 
                                                              len, 0));
        /* send it! */
-       dev_queue_xmit(skb);
+       dst_clone(dst);
+       skb->dst = dst;
+       dst_output(skb);
 
        ICMP6_INC_STATS(Icmp6OutNeighborSolicits);
        ICMP6_INC_STATS(Icmp6OutMsgs);
@@ -501,6 +571,9 @@
 void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr,
                   struct in6_addr *daddr)
 {
+       struct flowi fl;
+       struct rt6_info *rt = NULL;
+       struct dst_entry* dst;
        struct sock *sk = ndisc_socket->sk;
         struct sk_buff *skb;
         struct icmp6hdr *hdr;
@@ -508,6 +581,22 @@
         int len;
        int err;
 
+       rt = ndisc_get_dummy_rt();
+       if (!rt) 
+               return;
+
+       ndisc_flow_init(&fl, NDISC_ROUTER_SOLICITATION, saddr, daddr);
+       ndisc_rt_init(rt, dev, NULL);
+
+       dst = (struct dst_entry*)rt;
+       dst_clone(dst);
+
+       err = xfrm_lookup(&dst, &fl, NULL, 0);
+       if (err < 0) {
+               dst_release(dst);
+               return;
+       }
+
        len = sizeof(struct icmp6hdr);
        if (dev->addr_len)
                len += NDISC_OPT_SPACE(dev->addr_len);
@@ -519,14 +608,10 @@
                return;
        }
 
-       if (ndisc_build_ll_hdr(skb, dev, daddr, NULL, len) == 0) {
-               kfree_skb(skb);
-               return;
-       }
-
+       skb_reserve(skb, (dev->hard_header_len + 15) & ~15);
        ip6_nd_hdr(sk, skb, dev, saddr, daddr, IPPROTO_ICMPV6, len);
 
-        hdr = (struct icmp6hdr *) skb_put(skb, len);
+        skb->h.raw = (unsigned char*) hdr = (struct icmp6hdr *) skb_put(skb, 
len);
         hdr->icmp6_type = NDISC_ROUTER_SOLICITATION;
         hdr->icmp6_code = 0;
         hdr->icmp6_cksum = 0;
@@ -543,7 +628,9 @@
                                           csum_partial((__u8 *) hdr, len, 0));
 
        /* send it! */
-       dev_queue_xmit(skb);
+       dst_clone(dst);
+       skb->dst = dst;
+       dst_output(skb);
 
        ICMP6_INC_STATS(Icmp6OutRouterSolicits);
        ICMP6_INC_STATS(Icmp6OutMsgs);
@@ -1125,6 +1212,8 @@
        struct in6_addr *addrp;
        struct net_device *dev;
        struct rt6_info *rt;
+       struct dst_entry *dst;
+       struct flowi fl;
        u8 *opt;
        int rd_len;
        int err;
@@ -1136,6 +1225,22 @@
        if (rt == NULL)
                return;
 
+       dst = (struct dst_entry*)rt;
+
+       if (ipv6_get_lladdr(dev, &saddr_buf)) {
+               ND_PRINTK1("redirect: no link_local addr for dev\n");
+               return;
+       }
+
+       ndisc_flow_init(&fl, NDISC_REDIRECT, &saddr_buf, &skb->nh.ipv6h->saddr);
+
+       dst_clone(dst);
+       err = xfrm_lookup(&dst, &fl, NULL, 0);
+       if (err) {
+               dst_release(dst);
+               return;
+       }
+
        if (rt->rt6i_flags & RTF_GATEWAY) {
                ND_PRINTK1("ndisc_send_redirect: not a neighbour\n");
                dst_release(&rt->u.dst);
@@ -1164,11 +1269,6 @@
        rd_len &= ~0x7;
        len += rd_len;
 
-       if (ipv6_get_lladdr(dev, &saddr_buf)) {
-               ND_PRINTK1("redirect: no link_local addr for dev\n");
-               return;
-       }
-
        buff = sock_alloc_send_skb(sk, MAX_HEADER + len + dev->hard_header_len 
+ 15,
                                   0, &err);
        if (buff == NULL) {
@@ -1178,15 +1278,11 @@
 
        hlen = 0;
 
-       if (ndisc_build_ll_hdr(buff, dev, &skb->nh.ipv6h->saddr, NULL, len) == 
0) {
-               kfree_skb(buff);
-               return;
-       }
-
+       skb_reserve(skb, (dev->hard_header_len + 15) & ~15);
        ip6_nd_hdr(sk, buff, dev, &saddr_buf, &skb->nh.ipv6h->saddr,
                   IPPROTO_ICMPV6, len);
 
-       icmph = (struct icmp6hdr *) skb_put(buff, len);
+       skb->h.raw = (unsigned char*) icmph = (struct icmp6hdr *) skb_put(buff, 
len);
 
        memset(icmph, 0, sizeof(struct icmp6hdr));
        icmph->icmp6_type = NDISC_REDIRECT;
@@ -1224,7 +1320,8 @@
                                             len, IPPROTO_ICMPV6,
                                             csum_partial((u8 *) icmph, len, 
0));
 
-       dev_queue_xmit(buff);
+       skb->dst = dst;
+       dst_output(skb);
 
        ICMP6_INC_STATS(Icmp6OutRedirects);
        ICMP6_INC_STATS(Icmp6OutMsgs);
diff -ruN -x CVS linux-2.5.63/net/ipv6/raw.c linux25/net/ipv6/raw.c
--- linux-2.5.63/net/ipv6/raw.c 2003-02-25 04:05:16.000000000 +0900
+++ linux25/net/ipv6/raw.c      2003-03-04 20:38:16.000000000 +0900
@@ -45,6 +45,7 @@
 #include <net/inet_common.h>
 
 #include <net/rawv6.h>
+#include <net/xfrm.h>
 
 struct sock *raw_v6_htable[RAWV6_HTABLE_SIZE];
 rwlock_t raw_v6_lock = RW_LOCK_UNLOCKED;
@@ -304,6 +305,11 @@
        struct inet_opt *inet = inet_sk(sk);
        struct raw6_opt *raw_opt = raw6_sk(sk);
 
+        if (!xfrm6_policy_check(sk, XFRM_POLICY_IN, skb)) {
+                kfree_skb(skb);
+                return NET_RX_DROP;
+        }
+
        if (!raw_opt->checksum)
                skb->ip_summed = CHECKSUM_UNNECESSARY;
 
diff -ruN -x CVS linux-2.5.63/net/ipv6/route.c linux25/net/ipv6/route.c
--- linux-2.5.63/net/ipv6/route.c       2003-02-25 04:05:39.000000000 +0900
+++ linux25/net/ipv6/route.c    2003-03-05 11:30:41.000000000 +0900
@@ -49,6 +49,8 @@
 #include <net/addrconf.h>
 #include <net/tcp.h>
 #include <linux/rtnetlink.h>
+#include <net/dst.h>
+#include <net/xfrm.h>
 
 #include <asm/uaccess.h>
 
@@ -128,6 +130,12 @@
 rwlock_t rt6_lock = RW_LOCK_UNLOCKED;
 
 
+/*     Dummy rt for ndisc */
+struct rt6_info *ndisc_get_dummy_rt()
+{
+       return dst_alloc(&ip6_dst_ops);
+}
+
 /*
  *     Route lookup. Any rt6_lock is implied.
  */
@@ -1809,6 +1817,14 @@
 
 #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)
 {
@@ -1817,6 +1833,7 @@
                                                     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);
@@ -1830,7 +1847,7 @@
        proc_net_remove("ipv6_route");
        proc_net_remove("rt6_stats");
 #endif
-
+       xfrm_dst_lookup_unregister(AF_INET6);
        rt6_ifdown(NULL);
        fib6_gc_cleanup();
 }
diff -ruN -x CVS linux-2.5.63/net/ipv6/tcp_ipv6.c linux25/net/ipv6/tcp_ipv6.c
--- linux-2.5.63/net/ipv6/tcp_ipv6.c    2003-02-25 04:05:33.000000000 +0900
+++ linux25/net/ipv6/tcp_ipv6.c 2003-03-04 20:38:16.000000000 +0900
@@ -50,6 +50,7 @@
 #include <net/ip6_route.h>
 #include <net/inet_ecn.h>
 #include <net/protocol.h>
+#include <net/xfrm.h>
 
 #include <asm/uaccess.h>
 
@@ -677,6 +678,9 @@
                fl.nl_u.ip6_u.daddr = rt0->addr;
        }
 
+       if (!fl.fl6_src)
+               fl.fl6_src = &np->saddr;
+
        dst = ip6_route_output(sk, &fl);
 
        if ((err = dst->error) != 0) {
@@ -1637,6 +1641,9 @@
        if (sk_filter(sk, skb, 0))
                goto discard_and_relse;
 
+       if (!xfrm6_policy_check(sk, XFRM_POLICY_IN, skb))
+               goto discard_it;
+
        skb->dev = NULL;
 
        bh_lock_sock(sk);
@@ -1652,6 +1659,9 @@
        return ret;
 
 no_tcp_socket:
+       if (!xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb))
+               goto discard_and_relse;
+
        if (skb->len < (th->doff<<2) || tcp_checksum_complete(skb)) {
 bad_packet:
                TCP_INC_STATS_BH(TcpInErrs);
@@ -1671,8 +1681,11 @@
 discard_and_relse:
        sock_put(sk);
        goto discard_it;
-                
+
 do_time_wait:
+       if (!xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb))
+               goto discard_and_relse;
+
        if (skb->len < (th->doff<<2) || tcp_checksum_complete(skb)) {
                TCP_INC_STATS_BH(TcpInErrs);
                sock_put(sk);
diff -ruN -x CVS linux-2.5.63/net/ipv6/udp.c linux25/net/ipv6/udp.c
--- linux-2.5.63/net/ipv6/udp.c 2003-02-25 04:05:40.000000000 +0900
+++ linux25/net/ipv6/udp.c      2003-03-04 20:38:16.000000000 +0900
@@ -50,6 +50,7 @@
 #include <net/inet_common.h>
 
 #include <net/checksum.h>
+#include <net/xfrm.h>
 
 DEFINE_SNMP_STAT(struct udp_mib, udp_stats_in6);
 
@@ -541,6 +542,11 @@
 
 static inline int udpv6_queue_rcv_skb(struct sock * sk, struct sk_buff *skb)
 {
+       if (!xfrm6_policy_check(sk, XFRM_POLICY_IN, skb)) {
+               kfree_skb(skb);
+               return -1;
+       }
+
 #if defined(CONFIG_FILTER)
        if (sk->filter && skb->ip_summed != CHECKSUM_UNNECESSARY) {
                if ((unsigned short)csum_fold(skb_checksum(skb, 0, skb->len, 
skb->csum))) {
@@ -646,6 +652,9 @@
        if (!pskb_may_pull(skb, sizeof(struct udphdr)))
                goto short_packet;
 
+       if (!xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb))
+                goto discard;
+
        saddr = &skb->nh.ipv6h->saddr;
        daddr = &skb->nh.ipv6h->daddr;
        uh = skb->h.uh;
diff -ruN -x CVS linux-2.5.63/net/key/af_key.c linux25/net/key/af_key.c
--- linux-2.5.63/net/key/af_key.c       2003-02-25 04:05:13.000000000 +0900
+++ linux25/net/key/af_key.c    2003-03-04 20:38:16.000000000 +0900
@@ -550,8 +550,8 @@
 
        switch (((struct sockaddr *)(addr + 1))->sa_family) {
        case AF_INET:
-               x = xfrm_state_lookup(((struct sockaddr_in *)(addr + 
1))->sin_addr.s_addr,
-                                     sa->sadb_sa_spi, proto);
+               x = xfrm4_state_lookup(((struct sockaddr_in *)(addr + 
1))->sin_addr.s_addr,
+                                      sa->sadb_sa_spi, proto);
                break;
 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
        case AF_INET6:
@@ -1097,18 +1097,7 @@
                        min_spi = htonl(0x100);
                        max_spi = htonl(0x0fffffff);
                }
-               switch (x->props.family) {
-               case AF_INET:
-                       xfrm_alloc_spi(x, min_spi, max_spi);
-                       break;
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-               case AF_INET6:
-                       xfrm6_alloc_spi(x, min_spi, max_spi);
-                       break;
-#endif
-               default:
-                       break;
-               }
+               xfrm_alloc_spi(x, min_spi, max_spi);
                if (x->id.spi)
                        resp_skb = pfkey_xfrm_state2msg(x, 0, 3);
        }
diff -ruN -x CVS linux-2.5.63/net/netsyms.c linux25/net/netsyms.c
--- linux-2.5.63/net/netsyms.c  2003-02-25 04:05:16.000000000 +0900
+++ linux25/net/netsyms.c       2003-03-04 20:38:15.000000000 +0900
@@ -296,11 +296,11 @@
 EXPORT_SYMBOL(__xfrm_route_forward);
 EXPORT_SYMBOL(xfrm_state_alloc);
 EXPORT_SYMBOL(__xfrm_state_destroy);
-EXPORT_SYMBOL(xfrm_state_find);
+EXPORT_SYMBOL(xfrm4_state_find);
 EXPORT_SYMBOL(xfrm_state_insert);
 EXPORT_SYMBOL(xfrm_state_check_expire);
 EXPORT_SYMBOL(xfrm_state_check_space);
-EXPORT_SYMBOL(xfrm_state_lookup);
+EXPORT_SYMBOL(xfrm4_state_lookup);
 EXPORT_SYMBOL(xfrm_replay_check);
 EXPORT_SYMBOL(xfrm_replay_advance);
 EXPORT_SYMBOL(xfrm_check_selectors);
@@ -324,13 +324,17 @@
 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_alloc_spi);
 EXPORT_SYMBOL(xfrm6_register_type);
 EXPORT_SYMBOL(xfrm6_unregister_type);
 EXPORT_SYMBOL(xfrm6_get_type);
+EXPORT_SYMBOL(xfrm6_clear_mutable_options);
 #endif
 
 EXPORT_SYMBOL_GPL(xfrm_probe_algs);
@@ -342,6 +346,15 @@
 EXPORT_SYMBOL_GPL(xfrm_ealg_get_byid);
 EXPORT_SYMBOL_GPL(xfrm_aalg_get_byname);
 EXPORT_SYMBOL_GPL(xfrm_ealg_get_byname);
+#if defined(CONFIG_INET_AH) || defined(CONFIG_INET_AH_MODULE) || 
defined(CONFIG_INET6_AH) || defined(CONFIG_INET6_AH_MODULE)
+EXPORT_SYMBOL_GPL(skb_ah_walk);
+#endif
+#if defined(CONFIG_INET_ESP) || defined(CONFIG_INET_ESP_MODULE) || 
defined(CONFIG_INET6_ESP) || defined(CONFIG_INET6_ESP_MODULE)
+EXPORT_SYMBOL_GPL(skb_cow_data);
+EXPORT_SYMBOL_GPL(pskb_put);
+EXPORT_SYMBOL_GPL(skb_icv_walk);
+EXPORT_SYMBOL_GPL(skb_to_sgvec);
+#endif
 
 #if defined (CONFIG_IPV6_MODULE) || defined (CONFIG_IP_SCTP_MODULE)
 /* inet functions common to v4 and v6 */








<Prev in Thread] Current Thread [Next in Thread>