struct dst_entry *      (*negative_advice)(struct dst_entry *);
        void                    (*link_failure)(struct sk_buff *);
        void                    (*update_pmtu)(struct dst_entry *dst, u32 mtu);
+       int                     (*local_out)(struct sk_buff *skb);
        int                     entry_size;
 
        atomic_t                entries;
 
        unsigned int            family;
        unsigned int            proto;
        unsigned int            eth_proto;
+       unsigned int            nf_post_routing;
        struct module           *owner;
        struct xfrm_type        *type_map[IPPROTO_MAX];
        struct xfrm_mode        *mode_map[XFRM_MODE_MAX];
 
        .negative_advice =      ipv4_negative_advice,
        .link_failure =         ipv4_link_failure,
        .update_pmtu =          ip_rt_update_pmtu,
+       .local_out =            ip_local_out,
        .entry_size =           sizeof(struct rtable),
 };
 
 
                return err;
 
        memset(IPCB(skb), 0, sizeof(*IPCB(skb)));
-       IPCB(skb)->flags |= IPSKB_XFRM_TUNNEL_SIZE;
+       IPCB(skb)->flags |= IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED;
 
        skb->protocol = htons(ETH_P_IP);
 
 }
 EXPORT_SYMBOL(xfrm4_prepare_output);
 
-static inline int xfrm4_output_one(struct sk_buff *skb)
-{
-       int err;
-
-       err = xfrm_output(skb);
-       if (err)
-               goto error_nolock;
-
-       IPCB(skb)->flags |= IPSKB_XFRM_TRANSFORMED;
-       err = 0;
-
-out_exit:
-       return err;
-error_nolock:
-       kfree_skb(skb);
-       goto out_exit;
-}
-
-static int xfrm4_output_finish2(struct sk_buff *skb)
-{
-       int err;
-
-       while (likely((err = xfrm4_output_one(skb)) == 0)) {
-               nf_reset(skb);
-
-               err = __ip_local_out(skb);
-               if (unlikely(err != 1))
-                       break;
-
-               if (!skb->dst->xfrm)
-                       return dst_output(skb);
-
-               err = nf_hook(PF_INET, NF_IP_POST_ROUTING, skb, NULL,
-                             skb->dst->dev, xfrm4_output_finish2);
-               if (unlikely(err != 1))
-                       break;
-       }
-
-       return err;
-}
-
 static int xfrm4_output_finish(struct sk_buff *skb)
 {
-       struct sk_buff *segs;
-
 #ifdef CONFIG_NETFILTER
        if (!skb->dst->xfrm) {
                IPCB(skb)->flags |= IPSKB_REROUTED;
                return dst_output(skb);
        }
-#endif
 
-       if (!skb_is_gso(skb))
-               return xfrm4_output_finish2(skb);
+       IPCB(skb)->flags |= IPSKB_XFRM_TRANSFORMED;
+#endif
 
        skb->protocol = htons(ETH_P_IP);
-       segs = skb_gso_segment(skb, 0);
-       kfree_skb(skb);
-       if (unlikely(IS_ERR(segs)))
-               return PTR_ERR(segs);
-
-       do {
-               struct sk_buff *nskb = segs->next;
-               int err;
-
-               segs->next = NULL;
-               err = xfrm4_output_finish2(segs);
-
-               if (unlikely(err)) {
-                       while ((segs = nskb)) {
-                               nskb = segs->next;
-                               segs->next = NULL;
-                               kfree_skb(segs);
-                       }
-                       return err;
-               }
-
-               segs = nskb;
-       } while (segs);
-
-       return 0;
+       return xfrm_output(skb);
 }
 
 int xfrm4_output(struct sk_buff *skb)
 
        .update_pmtu =          xfrm4_update_pmtu,
        .destroy =              xfrm4_dst_destroy,
        .ifdown =               xfrm4_dst_ifdown,
+       .local_out =            __ip_local_out,
        .gc_thresh =            1024,
        .entry_size =           sizeof(struct xfrm_dst),
 };
 
 #include <net/xfrm.h>
 #include <linux/pfkeyv2.h>
 #include <linux/ipsec.h>
+#include <linux/netfilter_ipv4.h>
 
 static struct xfrm_state_afinfo xfrm4_state_afinfo;
 
        .family                 = AF_INET,
        .proto                  = IPPROTO_IPIP,
        .eth_proto              = htons(ETH_P_IP),
+       .nf_post_routing        = NF_IP_POST_ROUTING,
        .owner                  = THIS_MODULE,
        .init_flags             = xfrm4_init_flags,
        .init_tempsel           = __xfrm4_init_tempsel,
 
        .negative_advice        =       ip6_negative_advice,
        .link_failure           =       ip6_link_failure,
        .update_pmtu            =       ip6_rt_update_pmtu,
+       .local_out              =       ip6_local_out,
        .entry_size             =       sizeof(struct rt6_info),
 };
 
 
                return err;
 
        memset(IP6CB(skb), 0, sizeof(*IP6CB(skb)));
+#ifdef CONFIG_NETFILTER
+       IP6CB(skb)->flags |= IP6SKB_XFRM_TRANSFORMED;
+#endif
 
        skb->protocol = htons(ETH_P_IPV6);
 
 }
 EXPORT_SYMBOL(xfrm6_prepare_output);
 
-static inline int xfrm6_output_one(struct sk_buff *skb)
-{
-       int err;
-
-       err = xfrm_output(skb);
-       if (err)
-               goto error_nolock;
-
-       IP6CB(skb)->flags |= IP6SKB_XFRM_TRANSFORMED;
-       err = 0;
-
-out_exit:
-       return err;
-error_nolock:
-       kfree_skb(skb);
-       goto out_exit;
-}
-
-static int xfrm6_output_finish2(struct sk_buff *skb)
-{
-       int err;
-
-       while (likely((err = xfrm6_output_one(skb)) == 0)) {
-               nf_reset(skb);
-
-               err = __ip6_local_out(skb);
-               if (unlikely(err != 1))
-                       break;
-
-               if (!skb->dst->xfrm)
-                       return dst_output(skb);
-
-               err = nf_hook(PF_INET6, NF_IP6_POST_ROUTING, skb, NULL,
-                             skb->dst->dev, xfrm6_output_finish2);
-               if (unlikely(err != 1))
-                       break;
-       }
-
-       return err;
-}
-
 static int xfrm6_output_finish(struct sk_buff *skb)
 {
-       struct sk_buff *segs;
-
-       if (!skb_is_gso(skb))
-               return xfrm6_output_finish2(skb);
+#ifdef CONFIG_NETFILTER
+       IP6CB(skb)->flags |= IP6SKB_XFRM_TRANSFORMED;
+#endif
 
        skb->protocol = htons(ETH_P_IPV6);
-       segs = skb_gso_segment(skb, 0);
-       kfree_skb(skb);
-       if (unlikely(IS_ERR(segs)))
-               return PTR_ERR(segs);
-
-       do {
-               struct sk_buff *nskb = segs->next;
-               int err;
-
-               segs->next = NULL;
-               err = xfrm6_output_finish2(segs);
-
-               if (unlikely(err)) {
-                       while ((segs = nskb)) {
-                               nskb = segs->next;
-                               segs->next = NULL;
-                               kfree_skb(segs);
-                       }
-                       return err;
-               }
-
-               segs = nskb;
-       } while (segs);
-
-       return 0;
+       return xfrm_output(skb);
 }
 
 int xfrm6_output(struct sk_buff *skb)
 
        .update_pmtu =          xfrm6_update_pmtu,
        .destroy =              xfrm6_dst_destroy,
        .ifdown =               xfrm6_dst_ifdown,
+       .local_out =            __ip6_local_out,
        .gc_thresh =            1024,
        .entry_size =           sizeof(struct xfrm_dst),
 };
 
 #include <net/xfrm.h>
 #include <linux/pfkeyv2.h>
 #include <linux/ipsec.h>
+#include <linux/netfilter_ipv6.h>
 #include <net/dsfield.h>
 #include <net/ipv6.h>
 #include <net/addrconf.h>
        .family                 = AF_INET6,
        .proto                  = IPPROTO_IPV6,
        .eth_proto              = htons(ETH_P_IPV6),
+       .nf_post_routing        = NF_IP6_POST_ROUTING,
        .owner                  = THIS_MODULE,
        .init_tempsel           = __xfrm6_init_tempsel,
        .tmpl_sort              = __xfrm6_tmpl_sort,
 
 #include <linux/errno.h>
 #include <linux/module.h>
 #include <linux/netdevice.h>
+#include <linux/netfilter.h>
 #include <linux/skbuff.h>
 #include <linux/spinlock.h>
 #include <net/dst.h>
        return err;
 }
 
-int xfrm_output(struct sk_buff *skb)
+static int xfrm_output_one(struct sk_buff *skb)
 {
        struct dst_entry *dst = skb->dst;
        struct xfrm_state *x = dst->xfrm;
 
        err = 0;
 
-error_nolock:
+out_exit:
        return err;
 error:
        spin_unlock_bh(&x->lock);
-       goto error_nolock;
+error_nolock:
+       kfree_skb(skb);
+       goto out_exit;
+}
+
+static int xfrm_output2(struct sk_buff *skb)
+{
+       int err;
+
+       while (likely((err = xfrm_output_one(skb)) == 0)) {
+               struct xfrm_state *x;
+
+               nf_reset(skb);
+
+               err = skb->dst->ops->local_out(skb);
+               if (unlikely(err != 1))
+                       break;
+
+               x = skb->dst->xfrm;
+               if (!x)
+                       return dst_output(skb);
+
+               err = nf_hook(x->inner_mode->afinfo->family,
+                             x->inner_mode->afinfo->nf_post_routing, skb,
+                             NULL, skb->dst->dev, xfrm_output2);
+               if (unlikely(err != 1))
+                       break;
+       }
+
+       return err;
+}
+
+int xfrm_output(struct sk_buff *skb)
+{
+       struct sk_buff *segs;
+
+       if (!skb_is_gso(skb))
+               return xfrm_output2(skb);
+
+       segs = skb_gso_segment(skb, 0);
+       kfree_skb(skb);
+       if (unlikely(IS_ERR(segs)))
+               return PTR_ERR(segs);
+
+       do {
+               struct sk_buff *nskb = segs->next;
+               int err;
+
+               segs->next = NULL;
+               err = xfrm_output2(segs);
+
+               if (unlikely(err)) {
+                       while ((segs = nskb)) {
+                               nskb = segs->next;
+                               segs->next = NULL;
+                               kfree_skb(segs);
+                       }
+                       return err;
+               }
+
+               segs = nskb;
+       } while (segs);
+
+       return 0;
 }
 EXPORT_SYMBOL_GPL(xfrm_output);