|
@@ -559,6 +559,158 @@ static void xfrm_replay_advance_esn(struct xfrm_state *x, __be32 net_seq)
|
|
x->repl->notify(x, XFRM_REPLAY_UPDATE);
|
|
x->repl->notify(x, XFRM_REPLAY_UPDATE);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+#ifdef CONFIG_XFRM_OFFLOAD
|
|
|
|
+static int xfrm_replay_overflow_offload(struct xfrm_state *x, struct sk_buff *skb)
|
|
|
|
+{
|
|
|
|
+ int err = 0;
|
|
|
|
+ struct net *net = xs_net(x);
|
|
|
|
+ struct xfrm_offload *xo = xfrm_offload(skb);
|
|
|
|
+ __u32 oseq = x->replay.oseq;
|
|
|
|
+
|
|
|
|
+ if (!xo)
|
|
|
|
+ return xfrm_replay_overflow(x, skb);
|
|
|
|
+
|
|
|
|
+ if (x->type->flags & XFRM_TYPE_REPLAY_PROT) {
|
|
|
|
+ if (!skb_is_gso(skb)) {
|
|
|
|
+ XFRM_SKB_CB(skb)->seq.output.low = ++oseq;
|
|
|
|
+ xo->seq.low = oseq;
|
|
|
|
+ } else {
|
|
|
|
+ XFRM_SKB_CB(skb)->seq.output.low = oseq + 1;
|
|
|
|
+ xo->seq.low = oseq + 1;
|
|
|
|
+ oseq += skb_shinfo(skb)->gso_segs;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ XFRM_SKB_CB(skb)->seq.output.hi = 0;
|
|
|
|
+ xo->seq.hi = 0;
|
|
|
|
+ if (unlikely(oseq < x->replay.oseq)) {
|
|
|
|
+ xfrm_audit_state_replay_overflow(x, skb);
|
|
|
|
+ err = -EOVERFLOW;
|
|
|
|
+
|
|
|
|
+ return err;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ x->replay.oseq = oseq;
|
|
|
|
+
|
|
|
|
+ if (xfrm_aevent_is_on(net))
|
|
|
|
+ x->repl->notify(x, XFRM_REPLAY_UPDATE);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return err;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int xfrm_replay_overflow_offload_bmp(struct xfrm_state *x, struct sk_buff *skb)
|
|
|
|
+{
|
|
|
|
+ int err = 0;
|
|
|
|
+ struct xfrm_offload *xo = xfrm_offload(skb);
|
|
|
|
+ struct xfrm_replay_state_esn *replay_esn = x->replay_esn;
|
|
|
|
+ struct net *net = xs_net(x);
|
|
|
|
+ __u32 oseq = replay_esn->oseq;
|
|
|
|
+
|
|
|
|
+ if (!xo)
|
|
|
|
+ return xfrm_replay_overflow_bmp(x, skb);
|
|
|
|
+
|
|
|
|
+ if (x->type->flags & XFRM_TYPE_REPLAY_PROT) {
|
|
|
|
+ if (!skb_is_gso(skb)) {
|
|
|
|
+ XFRM_SKB_CB(skb)->seq.output.low = ++oseq;
|
|
|
|
+ xo->seq.low = oseq;
|
|
|
|
+ } else {
|
|
|
|
+ XFRM_SKB_CB(skb)->seq.output.low = oseq + 1;
|
|
|
|
+ xo->seq.low = oseq + 1;
|
|
|
|
+ oseq += skb_shinfo(skb)->gso_segs;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ XFRM_SKB_CB(skb)->seq.output.hi = 0;
|
|
|
|
+ xo->seq.hi = 0;
|
|
|
|
+ if (unlikely(oseq < replay_esn->oseq)) {
|
|
|
|
+ xfrm_audit_state_replay_overflow(x, skb);
|
|
|
|
+ err = -EOVERFLOW;
|
|
|
|
+
|
|
|
|
+ return err;
|
|
|
|
+ } else {
|
|
|
|
+ replay_esn->oseq = oseq;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (xfrm_aevent_is_on(net))
|
|
|
|
+ x->repl->notify(x, XFRM_REPLAY_UPDATE);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return err;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int xfrm_replay_overflow_offload_esn(struct xfrm_state *x, struct sk_buff *skb)
|
|
|
|
+{
|
|
|
|
+ int err = 0;
|
|
|
|
+ struct xfrm_offload *xo = xfrm_offload(skb);
|
|
|
|
+ struct xfrm_replay_state_esn *replay_esn = x->replay_esn;
|
|
|
|
+ struct net *net = xs_net(x);
|
|
|
|
+ __u32 oseq = replay_esn->oseq;
|
|
|
|
+ __u32 oseq_hi = replay_esn->oseq_hi;
|
|
|
|
+
|
|
|
|
+ if (!xo)
|
|
|
|
+ return xfrm_replay_overflow_esn(x, skb);
|
|
|
|
+
|
|
|
|
+ if (x->type->flags & XFRM_TYPE_REPLAY_PROT) {
|
|
|
|
+ if (!skb_is_gso(skb)) {
|
|
|
|
+ XFRM_SKB_CB(skb)->seq.output.low = ++oseq;
|
|
|
|
+ XFRM_SKB_CB(skb)->seq.output.hi = oseq_hi;
|
|
|
|
+ xo->seq.low = oseq;
|
|
|
|
+ xo->seq.hi = oseq_hi;
|
|
|
|
+ } else {
|
|
|
|
+ XFRM_SKB_CB(skb)->seq.output.low = oseq + 1;
|
|
|
|
+ XFRM_SKB_CB(skb)->seq.output.hi = oseq_hi;
|
|
|
|
+ xo->seq.low = oseq = oseq + 1;
|
|
|
|
+ xo->seq.hi = oseq_hi;
|
|
|
|
+ oseq += skb_shinfo(skb)->gso_segs;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (unlikely(oseq < replay_esn->oseq)) {
|
|
|
|
+ XFRM_SKB_CB(skb)->seq.output.hi = ++oseq_hi;
|
|
|
|
+ xo->seq.hi = oseq_hi;
|
|
|
|
+
|
|
|
|
+ if (replay_esn->oseq_hi == 0) {
|
|
|
|
+ replay_esn->oseq--;
|
|
|
|
+ replay_esn->oseq_hi--;
|
|
|
|
+ xfrm_audit_state_replay_overflow(x, skb);
|
|
|
|
+ err = -EOVERFLOW;
|
|
|
|
+
|
|
|
|
+ return err;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ replay_esn->oseq = oseq;
|
|
|
|
+ replay_esn->oseq_hi = oseq_hi;
|
|
|
|
+
|
|
|
|
+ if (xfrm_aevent_is_on(net))
|
|
|
|
+ x->repl->notify(x, XFRM_REPLAY_UPDATE);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return err;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static const struct xfrm_replay xfrm_replay_legacy = {
|
|
|
|
+ .advance = xfrm_replay_advance,
|
|
|
|
+ .check = xfrm_replay_check,
|
|
|
|
+ .recheck = xfrm_replay_check,
|
|
|
|
+ .notify = xfrm_replay_notify,
|
|
|
|
+ .overflow = xfrm_replay_overflow_offload,
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static const struct xfrm_replay xfrm_replay_bmp = {
|
|
|
|
+ .advance = xfrm_replay_advance_bmp,
|
|
|
|
+ .check = xfrm_replay_check_bmp,
|
|
|
|
+ .recheck = xfrm_replay_check_bmp,
|
|
|
|
+ .notify = xfrm_replay_notify_bmp,
|
|
|
|
+ .overflow = xfrm_replay_overflow_offload_bmp,
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static const struct xfrm_replay xfrm_replay_esn = {
|
|
|
|
+ .advance = xfrm_replay_advance_esn,
|
|
|
|
+ .check = xfrm_replay_check_esn,
|
|
|
|
+ .recheck = xfrm_replay_recheck_esn,
|
|
|
|
+ .notify = xfrm_replay_notify_esn,
|
|
|
|
+ .overflow = xfrm_replay_overflow_offload_esn,
|
|
|
|
+};
|
|
|
|
+#else
|
|
static const struct xfrm_replay xfrm_replay_legacy = {
|
|
static const struct xfrm_replay xfrm_replay_legacy = {
|
|
.advance = xfrm_replay_advance,
|
|
.advance = xfrm_replay_advance,
|
|
.check = xfrm_replay_check,
|
|
.check = xfrm_replay_check,
|
|
@@ -582,6 +734,7 @@ static const struct xfrm_replay xfrm_replay_esn = {
|
|
.notify = xfrm_replay_notify_esn,
|
|
.notify = xfrm_replay_notify_esn,
|
|
.overflow = xfrm_replay_overflow_esn,
|
|
.overflow = xfrm_replay_overflow_esn,
|
|
};
|
|
};
|
|
|
|
+#endif
|
|
|
|
|
|
int xfrm_init_replay(struct xfrm_state *x)
|
|
int xfrm_init_replay(struct xfrm_state *x)
|
|
{
|
|
{
|
|
@@ -596,10 +749,12 @@ int xfrm_init_replay(struct xfrm_state *x)
|
|
if (replay_esn->replay_window == 0)
|
|
if (replay_esn->replay_window == 0)
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
x->repl = &xfrm_replay_esn;
|
|
x->repl = &xfrm_replay_esn;
|
|
- } else
|
|
|
|
|
|
+ } else {
|
|
x->repl = &xfrm_replay_bmp;
|
|
x->repl = &xfrm_replay_bmp;
|
|
- } else
|
|
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
x->repl = &xfrm_replay_legacy;
|
|
x->repl = &xfrm_replay_legacy;
|
|
|
|
+ }
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|