do {
err = xfrm_state_check_space(x, skb);
- if (err)
+ if (err) {
+ XFRM_INC_STATS(LINUX_MIB_XFRMOUTERROR);
goto error_nolock;
+ }
err = x->outer_mode->output(x, skb);
- if (err)
+ if (err) {
+ XFRM_INC_STATS(LINUX_MIB_XFRMOUTSTATEMODEERROR);
goto error_nolock;
+ }
spin_lock_bh(&x->lock);
err = xfrm_state_check_expire(x);
- if (err)
+ if (err) {
+ XFRM_INC_STATS(LINUX_MIB_XFRMOUTSTATEEXPIRED);
goto error;
+ }
if (x->type->flags & XFRM_TYPE_REPLAY_PROT) {
- XFRM_SKB_CB(skb)->seq = ++x->replay.oseq;
+ XFRM_SKB_CB(skb)->seq.output = ++x->replay.oseq;
+ if (unlikely(x->replay.oseq == 0)) {
+ XFRM_INC_STATS(LINUX_MIB_XFRMOUTSTATESEQERROR);
+ x->replay.oseq--;
+ xfrm_audit_state_replay_overflow(x, skb);
+ err = -EOVERFLOW;
+ goto error;
+ }
if (xfrm_aevent_is_on())
xfrm_replay_notify(x, XFRM_REPLAY_UPDATE);
}
spin_unlock_bh(&x->lock);
err = x->type->output(x, skb);
+ if (err == -EINPROGRESS)
+ goto out_exit;
resume:
if (err) {
if (!x)
return dst_output(skb);
- err = nf_hook(x->inner_mode->afinfo->family,
+ err = nf_hook(skb->dst->ops->family,
NF_INET_POST_ROUTING, skb,
NULL, skb->dst->dev, xfrm_output2);
if (unlikely(err != 1))
return xfrm_output2(skb);
}
+
+int xfrm_inner_extract_output(struct xfrm_state *x, struct sk_buff *skb)
+{
+ struct xfrm_mode *inner_mode;
+ if (x->sel.family == AF_UNSPEC)
+ inner_mode = xfrm_ip2inner_mode(x,
+ xfrm_af2proto(skb->dst->ops->family));
+ else
+ inner_mode = x->inner_mode;
+
+ if (inner_mode == NULL)
+ return -EAFNOSUPPORT;
+ return inner_mode->afinfo->extract_output(x, skb);
+}
+
EXPORT_SYMBOL_GPL(xfrm_output);
+EXPORT_SYMBOL_GPL(xfrm_inner_extract_output);