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);