+error_nolock:
+ kfree_skb(skb);
+ goto out_exit;
+}
+
+int xfrm_output_resume(struct sk_buff *skb, int err)
+{
+ while (likely((err = xfrm_output_one(skb, err)) == 0)) {
+ struct xfrm_state *x;
+
+ nf_reset(skb);
+
+ err = skb->dst->ops->local_out(skb);
+ if (unlikely(err != 1))
+ goto out;
+
+ x = skb->dst->xfrm;
+ if (!x)
+ return dst_output(skb);
+
+ err = nf_hook(x->inner_mode->afinfo->family,
+ NF_INET_POST_ROUTING, skb,
+ NULL, skb->dst->dev, xfrm_output2);
+ if (unlikely(err != 1))
+ goto out;
+ }
+
+ if (err == -EINPROGRESS)
+ err = 0;
+
+out:
+ return err;
+}
+EXPORT_SYMBOL_GPL(xfrm_output_resume);
+
+static int xfrm_output2(struct sk_buff *skb)
+{
+ return xfrm_output_resume(skb, 1);
+}
+
+static int xfrm_output_gso(struct sk_buff *skb)
+{
+ struct sk_buff *segs;
+
+ 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;
+}
+
+int xfrm_output(struct sk_buff *skb)
+{
+ int err;
+
+ if (skb_is_gso(skb))
+ return xfrm_output_gso(skb);
+
+ if (skb->ip_summed == CHECKSUM_PARTIAL) {
+ err = skb_checksum_help(skb);
+ if (err) {
+ XFRM_INC_STATS(LINUX_MIB_XFRMOUTERROR);
+ kfree_skb(skb);
+ return err;
+ }
+ }
+
+ return xfrm_output2(skb);