2019-05-30 02:57:49 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2011-03-08 03:08:32 +03:00
/*
* xfrm_replay . c - xfrm replay detection , derived from xfrm_state . c .
2011-03-08 03:09:09 +03:00
*
* Copyright ( C ) 2010 secunet Security Networks AG
* Copyright ( C ) 2010 Steffen Klassert < steffen . klassert @ secunet . com >
2011-03-08 03:08:32 +03:00
*/
2011-07-15 19:47:34 +04:00
# include <linux/export.h>
2011-03-08 03:08:32 +03:00
# include <net/xfrm.h>
2011-03-08 03:09:51 +03:00
u32 xfrm_replay_seqhi ( struct xfrm_state * x , __be32 net_seq )
{
u32 seq , seq_hi , bottom ;
struct xfrm_replay_state_esn * replay_esn = x - > replay_esn ;
if ( ! ( x - > props . flags & XFRM_STATE_ESN ) )
return 0 ;
seq = ntohl ( net_seq ) ;
seq_hi = replay_esn - > seq_hi ;
bottom = replay_esn - > seq - replay_esn - > replay_window + 1 ;
if ( likely ( replay_esn - > seq > = replay_esn - > replay_window - 1 ) ) {
/* A. same subspace */
if ( unlikely ( seq < bottom ) )
seq_hi + + ;
} else {
/* B. window spans two subspaces */
if ( unlikely ( seq > = bottom ) )
seq_hi - - ;
}
return seq_hi ;
}
2017-04-14 11:06:50 +03:00
EXPORT_SYMBOL ( xfrm_replay_seqhi ) ;
2021-06-18 16:51:56 +03:00
static void xfrm_replay_notify_bmp ( struct xfrm_state * x , int event ) ;
static void xfrm_replay_notify_esn ( struct xfrm_state * x , int event ) ;
void xfrm_replay_notify ( struct xfrm_state * x , int event )
2011-03-08 03:08:32 +03:00
{
struct km_event c ;
/* we send notify messages in case
* 1. we updated on of the sequence numbers , and the seqno difference
* is at least x - > replay_maxdiff , in this case we also update the
* timeout of our timer function
* 2. if x - > replay_maxage has elapsed since last update ,
* and there were changes
*
* The state structure must be locked !
*/
2021-06-18 16:51:56 +03:00
switch ( x - > repl_mode ) {
case XFRM_REPLAY_MODE_LEGACY :
break ;
case XFRM_REPLAY_MODE_BMP :
xfrm_replay_notify_bmp ( x , event ) ;
return ;
case XFRM_REPLAY_MODE_ESN :
xfrm_replay_notify_esn ( x , event ) ;
return ;
}
2011-03-08 03:08:32 +03:00
switch ( event ) {
case XFRM_REPLAY_UPDATE :
2013-09-19 15:19:19 +04:00
if ( ! x - > replay_maxdiff | |
( ( x - > replay . seq - x - > preplay . seq < x - > replay_maxdiff ) & &
( x - > replay . oseq - x - > preplay . oseq < x - > replay_maxdiff ) ) ) {
2011-03-08 03:08:32 +03:00
if ( x - > xflags & XFRM_TIME_DEFER )
event = XFRM_REPLAY_TIMEOUT ;
else
return ;
}
break ;
case XFRM_REPLAY_TIMEOUT :
if ( memcmp ( & x - > replay , & x - > preplay ,
sizeof ( struct xfrm_replay_state ) ) = = 0 ) {
x - > xflags | = XFRM_TIME_DEFER ;
return ;
}
break ;
}
memcpy ( & x - > preplay , & x - > replay , sizeof ( struct xfrm_replay_state ) ) ;
c . event = XFRM_MSG_NEWAE ;
c . data . aevent = event ;
km_state_notify ( x , & c ) ;
if ( x - > replay_maxage & &
! mod_timer ( & x - > rtimer , jiffies + x - > replay_maxage ) )
x - > xflags & = ~ XFRM_TIME_DEFER ;
}
2021-06-18 16:52:00 +03:00
static int __xfrm_replay_overflow ( struct xfrm_state * x , struct sk_buff * skb )
2011-03-08 03:08:32 +03:00
{
int err = 0 ;
struct net * net = xs_net ( x ) ;
if ( x - > type - > flags & XFRM_TYPE_REPLAY_PROT ) {
XFRM_SKB_CB ( skb ) - > seq . output . low = + + x - > replay . oseq ;
2015-05-20 19:38:12 +03:00
XFRM_SKB_CB ( skb ) - > seq . output . hi = 0 ;
2020-05-30 15:39:12 +03:00
if ( unlikely ( x - > replay . oseq = = 0 ) & &
! ( x - > props . extra_flags & XFRM_SA_XFLAG_OSEQ_MAY_WRAP ) ) {
2011-03-08 03:08:32 +03:00
x - > replay . oseq - - ;
xfrm_audit_state_replay_overflow ( x , skb ) ;
err = - EOVERFLOW ;
return err ;
}
if ( xfrm_aevent_is_on ( net ) )
2021-06-18 16:51:56 +03:00
xfrm_replay_notify ( x , XFRM_REPLAY_UPDATE ) ;
2011-03-08 03:08:32 +03:00
}
return err ;
}
2021-06-18 16:51:59 +03:00
static int xfrm_replay_check_legacy ( struct xfrm_state * x ,
struct sk_buff * skb , __be32 net_seq )
2011-03-08 03:08:32 +03:00
{
u32 diff ;
u32 seq = ntohl ( net_seq ) ;
2011-03-28 23:45:52 +04:00
if ( ! x - > props . replay_window )
return 0 ;
2011-03-08 03:08:32 +03:00
if ( unlikely ( seq = = 0 ) )
goto err ;
if ( likely ( seq > x - > replay . seq ) )
return 0 ;
diff = x - > replay . seq - seq ;
2013-09-17 11:14:13 +04:00
if ( diff > = x - > props . replay_window ) {
2011-03-08 03:08:32 +03:00
x - > stats . replay_window + + ;
goto err ;
}
if ( x - > replay . bitmap & ( 1U < < diff ) ) {
x - > stats . replay + + ;
goto err ;
}
return 0 ;
err :
xfrm_audit_state_replay ( x , skb , net_seq ) ;
return - EINVAL ;
}
2021-06-18 16:51:57 +03:00
static void xfrm_replay_advance_bmp ( struct xfrm_state * x , __be32 net_seq ) ;
static void xfrm_replay_advance_esn ( struct xfrm_state * x , __be32 net_seq ) ;
void xfrm_replay_advance ( struct xfrm_state * x , __be32 net_seq )
2011-03-08 03:08:32 +03:00
{
2021-06-18 16:51:57 +03:00
u32 diff , seq ;
switch ( x - > repl_mode ) {
case XFRM_REPLAY_MODE_LEGACY :
break ;
case XFRM_REPLAY_MODE_BMP :
return xfrm_replay_advance_bmp ( x , net_seq ) ;
case XFRM_REPLAY_MODE_ESN :
return xfrm_replay_advance_esn ( x , net_seq ) ;
}
2011-03-08 03:08:32 +03:00
if ( ! x - > props . replay_window )
return ;
2021-06-18 16:51:57 +03:00
seq = ntohl ( net_seq ) ;
2011-03-08 03:08:32 +03:00
if ( seq > x - > replay . seq ) {
diff = seq - x - > replay . seq ;
if ( diff < x - > props . replay_window )
x - > replay . bitmap = ( ( x - > replay . bitmap ) < < diff ) | 1 ;
else
x - > replay . bitmap = 1 ;
x - > replay . seq = seq ;
} else {
diff = x - > replay . seq - seq ;
x - > replay . bitmap | = ( 1U < < diff ) ;
}
if ( xfrm_aevent_is_on ( xs_net ( x ) ) )
2021-06-18 16:51:56 +03:00
xfrm_replay_notify ( x , XFRM_REPLAY_UPDATE ) ;
2011-03-08 03:08:32 +03:00
}
2011-03-08 03:09:09 +03:00
static int xfrm_replay_overflow_bmp ( struct xfrm_state * x , struct sk_buff * skb )
{
int err = 0 ;
struct xfrm_replay_state_esn * replay_esn = x - > replay_esn ;
struct net * net = xs_net ( x ) ;
if ( x - > type - > flags & XFRM_TYPE_REPLAY_PROT ) {
XFRM_SKB_CB ( skb ) - > seq . output . low = + + replay_esn - > oseq ;
2015-05-20 19:38:12 +03:00
XFRM_SKB_CB ( skb ) - > seq . output . hi = 0 ;
2020-05-30 15:39:12 +03:00
if ( unlikely ( replay_esn - > oseq = = 0 ) & &
! ( x - > props . extra_flags & XFRM_SA_XFLAG_OSEQ_MAY_WRAP ) ) {
2011-03-08 03:09:09 +03:00
replay_esn - > oseq - - ;
xfrm_audit_state_replay_overflow ( x , skb ) ;
err = - EOVERFLOW ;
return err ;
}
if ( xfrm_aevent_is_on ( net ) )
2021-06-18 16:51:56 +03:00
xfrm_replay_notify ( x , XFRM_REPLAY_UPDATE ) ;
2011-03-08 03:09:09 +03:00
}
return err ;
}
static int xfrm_replay_check_bmp ( struct xfrm_state * x ,
struct sk_buff * skb , __be32 net_seq )
{
unsigned int bitnr , nr ;
struct xfrm_replay_state_esn * replay_esn = x - > replay_esn ;
2011-03-28 23:45:52 +04:00
u32 pos ;
2011-03-08 03:09:09 +03:00
u32 seq = ntohl ( net_seq ) ;
u32 diff = replay_esn - > seq - seq ;
2011-03-28 23:45:52 +04:00
if ( ! replay_esn - > replay_window )
return 0 ;
2011-03-08 03:09:09 +03:00
if ( unlikely ( seq = = 0 ) )
goto err ;
if ( likely ( seq > replay_esn - > seq ) )
return 0 ;
if ( diff > = replay_esn - > replay_window ) {
x - > stats . replay_window + + ;
goto err ;
}
2011-10-11 05:58:37 +04:00
pos = ( replay_esn - > seq - 1 ) % replay_esn - > replay_window ;
if ( pos > = diff )
2011-03-08 03:09:09 +03:00
bitnr = ( pos - diff ) % replay_esn - > replay_window ;
2011-10-11 05:58:37 +04:00
else
2011-03-08 03:09:09 +03:00
bitnr = replay_esn - > replay_window - ( diff - pos ) ;
2011-10-11 05:58:37 +04:00
nr = bitnr > > 5 ;
bitnr = bitnr & 0x1F ;
if ( replay_esn - > bmp [ nr ] & ( 1U < < bitnr ) )
goto err_replay ;
2011-03-08 03:09:09 +03:00
return 0 ;
err_replay :
x - > stats . replay + + ;
err :
xfrm_audit_state_replay ( x , skb , net_seq ) ;
return - EINVAL ;
}
static void xfrm_replay_advance_bmp ( struct xfrm_state * x , __be32 net_seq )
{
unsigned int bitnr , nr , i ;
u32 diff ;
struct xfrm_replay_state_esn * replay_esn = x - > replay_esn ;
u32 seq = ntohl ( net_seq ) ;
2013-01-17 22:58:28 +04:00
u32 pos ;
2011-03-08 03:09:09 +03:00
if ( ! replay_esn - > replay_window )
return ;
2013-01-17 22:58:28 +04:00
pos = ( replay_esn - > seq - 1 ) % replay_esn - > replay_window ;
2011-03-08 03:09:09 +03:00
if ( seq > replay_esn - > seq ) {
diff = seq - replay_esn - > seq ;
if ( diff < replay_esn - > replay_window ) {
for ( i = 1 ; i < diff ; i + + ) {
bitnr = ( pos + i ) % replay_esn - > replay_window ;
nr = bitnr > > 5 ;
bitnr = bitnr & 0x1F ;
replay_esn - > bmp [ nr ] & = ~ ( 1U < < bitnr ) ;
}
} else {
2011-06-06 00:46:03 +04:00
nr = ( replay_esn - > replay_window - 1 ) > > 5 ;
2011-03-08 03:09:09 +03:00
for ( i = 0 ; i < = nr ; i + + )
replay_esn - > bmp [ i ] = 0 ;
}
2011-10-11 05:58:37 +04:00
bitnr = ( pos + diff ) % replay_esn - > replay_window ;
2011-03-08 03:09:09 +03:00
replay_esn - > seq = seq ;
} else {
diff = replay_esn - > seq - seq ;
2011-10-11 05:58:37 +04:00
if ( pos > = diff )
2011-03-08 03:09:09 +03:00
bitnr = ( pos - diff ) % replay_esn - > replay_window ;
2011-10-11 05:58:37 +04:00
else
2011-03-08 03:09:09 +03:00
bitnr = replay_esn - > replay_window - ( diff - pos ) ;
}
2011-10-11 05:58:37 +04:00
nr = bitnr > > 5 ;
bitnr = bitnr & 0x1F ;
replay_esn - > bmp [ nr ] | = ( 1U < < bitnr ) ;
2011-03-08 03:09:09 +03:00
if ( xfrm_aevent_is_on ( xs_net ( x ) ) )
2021-06-18 16:51:56 +03:00
xfrm_replay_notify ( x , XFRM_REPLAY_UPDATE ) ;
2011-03-08 03:09:09 +03:00
}
static void xfrm_replay_notify_bmp ( struct xfrm_state * x , int event )
{
struct km_event c ;
struct xfrm_replay_state_esn * replay_esn = x - > replay_esn ;
struct xfrm_replay_state_esn * preplay_esn = x - > preplay_esn ;
/* we send notify messages in case
* 1. we updated on of the sequence numbers , and the seqno difference
* is at least x - > replay_maxdiff , in this case we also update the
* timeout of our timer function
* 2. if x - > replay_maxage has elapsed since last update ,
* and there were changes
*
* The state structure must be locked !
*/
switch ( event ) {
case XFRM_REPLAY_UPDATE :
2013-09-19 15:19:19 +04:00
if ( ! x - > replay_maxdiff | |
( ( replay_esn - > seq - preplay_esn - > seq < x - > replay_maxdiff ) & &
( replay_esn - > oseq - preplay_esn - > oseq
< x - > replay_maxdiff ) ) ) {
2011-03-08 03:09:09 +03:00
if ( x - > xflags & XFRM_TIME_DEFER )
event = XFRM_REPLAY_TIMEOUT ;
else
return ;
}
break ;
case XFRM_REPLAY_TIMEOUT :
if ( memcmp ( x - > replay_esn , x - > preplay_esn ,
xfrm_replay_state_esn_len ( replay_esn ) ) = = 0 ) {
x - > xflags | = XFRM_TIME_DEFER ;
return ;
}
break ;
}
memcpy ( x - > preplay_esn , x - > replay_esn ,
xfrm_replay_state_esn_len ( replay_esn ) ) ;
c . event = XFRM_MSG_NEWAE ;
c . data . aevent = event ;
km_state_notify ( x , & c ) ;
if ( x - > replay_maxage & &
! mod_timer ( & x - > rtimer , jiffies + x - > replay_maxage ) )
x - > xflags & = ~ XFRM_TIME_DEFER ;
}
2013-03-11 13:32:16 +04:00
static void xfrm_replay_notify_esn ( struct xfrm_state * x , int event )
{
u32 seq_diff , oseq_diff ;
struct km_event c ;
struct xfrm_replay_state_esn * replay_esn = x - > replay_esn ;
struct xfrm_replay_state_esn * preplay_esn = x - > preplay_esn ;
/* we send notify messages in case
* 1. we updated on of the sequence numbers , and the seqno difference
* is at least x - > replay_maxdiff , in this case we also update the
* timeout of our timer function
* 2. if x - > replay_maxage has elapsed since last update ,
* and there were changes
*
* The state structure must be locked !
*/
switch ( event ) {
case XFRM_REPLAY_UPDATE :
2013-09-19 15:19:19 +04:00
if ( x - > replay_maxdiff ) {
if ( replay_esn - > seq_hi = = preplay_esn - > seq_hi )
seq_diff = replay_esn - > seq - preplay_esn - > seq ;
else
seq_diff = ~ preplay_esn - > seq + replay_esn - > seq
+ 1 ;
2013-03-11 13:32:16 +04:00
2013-09-19 15:19:19 +04:00
if ( replay_esn - > oseq_hi = = preplay_esn - > oseq_hi )
oseq_diff = replay_esn - > oseq
- preplay_esn - > oseq ;
2013-03-11 13:32:16 +04:00
else
2013-09-19 15:19:19 +04:00
oseq_diff = ~ preplay_esn - > oseq
+ replay_esn - > oseq + 1 ;
if ( seq_diff > = x - > replay_maxdiff | |
oseq_diff > = x - > replay_maxdiff )
break ;
2013-03-11 13:32:16 +04:00
}
2013-09-19 15:19:19 +04:00
if ( x - > xflags & XFRM_TIME_DEFER )
event = XFRM_REPLAY_TIMEOUT ;
else
return ;
2013-03-11 13:32:16 +04:00
break ;
case XFRM_REPLAY_TIMEOUT :
if ( memcmp ( x - > replay_esn , x - > preplay_esn ,
xfrm_replay_state_esn_len ( replay_esn ) ) = = 0 ) {
x - > xflags | = XFRM_TIME_DEFER ;
return ;
}
break ;
}
memcpy ( x - > preplay_esn , x - > replay_esn ,
xfrm_replay_state_esn_len ( replay_esn ) ) ;
c . event = XFRM_MSG_NEWAE ;
c . data . aevent = event ;
km_state_notify ( x , & c ) ;
if ( x - > replay_maxage & &
! mod_timer ( & x - > rtimer , jiffies + x - > replay_maxage ) )
x - > xflags & = ~ XFRM_TIME_DEFER ;
}
2011-03-08 03:09:51 +03:00
static int xfrm_replay_overflow_esn ( struct xfrm_state * x , struct sk_buff * skb )
{
int err = 0 ;
struct xfrm_replay_state_esn * replay_esn = x - > replay_esn ;
struct net * net = xs_net ( x ) ;
if ( x - > type - > flags & XFRM_TYPE_REPLAY_PROT ) {
XFRM_SKB_CB ( skb ) - > seq . output . low = + + replay_esn - > oseq ;
XFRM_SKB_CB ( skb ) - > seq . output . hi = replay_esn - > oseq_hi ;
if ( unlikely ( replay_esn - > oseq = = 0 ) ) {
XFRM_SKB_CB ( skb ) - > seq . output . hi = + + replay_esn - > 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 ;
}
}
if ( xfrm_aevent_is_on ( net ) )
2021-06-18 16:51:56 +03:00
xfrm_replay_notify ( x , XFRM_REPLAY_UPDATE ) ;
2011-03-08 03:09:51 +03:00
}
return err ;
}
static int xfrm_replay_check_esn ( struct xfrm_state * x ,
struct sk_buff * skb , __be32 net_seq )
{
unsigned int bitnr , nr ;
u32 diff ;
struct xfrm_replay_state_esn * replay_esn = x - > replay_esn ;
2011-03-28 23:45:52 +04:00
u32 pos ;
2011-03-08 03:09:51 +03:00
u32 seq = ntohl ( net_seq ) ;
u32 wsize = replay_esn - > replay_window ;
u32 top = replay_esn - > seq ;
u32 bottom = top - wsize + 1 ;
2011-03-28 23:45:52 +04:00
if ( ! wsize )
return 0 ;
2011-03-08 03:09:51 +03:00
if ( unlikely ( seq = = 0 & & replay_esn - > seq_hi = = 0 & &
( replay_esn - > seq < replay_esn - > replay_window - 1 ) ) )
goto err ;
diff = top - seq ;
if ( likely ( top > = wsize - 1 ) ) {
/* A. same subspace */
if ( likely ( seq > top ) | | seq < bottom )
return 0 ;
} else {
/* B. window spans two subspaces */
if ( likely ( seq > top & & seq < bottom ) )
return 0 ;
if ( seq > = bottom )
diff = ~ seq + top + 1 ;
}
if ( diff > = replay_esn - > replay_window ) {
x - > stats . replay_window + + ;
goto err ;
}
2011-10-11 05:58:37 +04:00
pos = ( replay_esn - > seq - 1 ) % replay_esn - > replay_window ;
if ( pos > = diff )
2011-03-08 03:09:51 +03:00
bitnr = ( pos - diff ) % replay_esn - > replay_window ;
2011-10-11 05:58:37 +04:00
else
2011-03-08 03:09:51 +03:00
bitnr = replay_esn - > replay_window - ( diff - pos ) ;
2011-10-11 05:58:37 +04:00
nr = bitnr > > 5 ;
bitnr = bitnr & 0x1F ;
if ( replay_esn - > bmp [ nr ] & ( 1U < < bitnr ) )
goto err_replay ;
2011-03-08 03:09:51 +03:00
return 0 ;
err_replay :
x - > stats . replay + + ;
err :
xfrm_audit_state_replay ( x , skb , net_seq ) ;
return - EINVAL ;
}
2021-06-18 16:51:59 +03:00
int xfrm_replay_check ( struct xfrm_state * x ,
struct sk_buff * skb , __be32 net_seq )
{
switch ( x - > repl_mode ) {
case XFRM_REPLAY_MODE_LEGACY :
break ;
case XFRM_REPLAY_MODE_BMP :
return xfrm_replay_check_bmp ( x , skb , net_seq ) ;
case XFRM_REPLAY_MODE_ESN :
return xfrm_replay_check_esn ( x , skb , net_seq ) ;
}
return xfrm_replay_check_legacy ( x , skb , net_seq ) ;
}
2012-09-04 04:03:29 +04:00
static int xfrm_replay_recheck_esn ( struct xfrm_state * x ,
struct sk_buff * skb , __be32 net_seq )
{
if ( unlikely ( XFRM_SKB_CB ( skb ) - > seq . input . hi ! =
htonl ( xfrm_replay_seqhi ( x , net_seq ) ) ) ) {
x - > stats . replay_window + + ;
return - EINVAL ;
}
return xfrm_replay_check_esn ( x , skb , net_seq ) ;
}
2021-06-18 16:51:58 +03:00
int xfrm_replay_recheck ( struct xfrm_state * x ,
struct sk_buff * skb , __be32 net_seq )
{
switch ( x - > repl_mode ) {
case XFRM_REPLAY_MODE_LEGACY :
break ;
case XFRM_REPLAY_MODE_BMP :
/* no special recheck treatment */
return xfrm_replay_check_bmp ( x , skb , net_seq ) ;
case XFRM_REPLAY_MODE_ESN :
return xfrm_replay_recheck_esn ( x , skb , net_seq ) ;
}
2021-06-18 16:51:59 +03:00
return xfrm_replay_check_legacy ( x , skb , net_seq ) ;
2021-06-18 16:51:58 +03:00
}
2011-03-08 03:09:51 +03:00
static void xfrm_replay_advance_esn ( struct xfrm_state * x , __be32 net_seq )
{
unsigned int bitnr , nr , i ;
int wrap ;
u32 diff , pos , seq , seq_hi ;
struct xfrm_replay_state_esn * replay_esn = x - > replay_esn ;
if ( ! replay_esn - > replay_window )
return ;
seq = ntohl ( net_seq ) ;
pos = ( replay_esn - > seq - 1 ) % replay_esn - > replay_window ;
seq_hi = xfrm_replay_seqhi ( x , net_seq ) ;
wrap = seq_hi - replay_esn - > seq_hi ;
if ( ( ! wrap & & seq > replay_esn - > seq ) | | wrap > 0 ) {
if ( likely ( ! wrap ) )
diff = seq - replay_esn - > seq ;
else
diff = ~ replay_esn - > seq + seq + 1 ;
if ( diff < replay_esn - > replay_window ) {
for ( i = 1 ; i < diff ; i + + ) {
bitnr = ( pos + i ) % replay_esn - > replay_window ;
nr = bitnr > > 5 ;
bitnr = bitnr & 0x1F ;
replay_esn - > bmp [ nr ] & = ~ ( 1U < < bitnr ) ;
}
} else {
2011-06-06 00:46:03 +04:00
nr = ( replay_esn - > replay_window - 1 ) > > 5 ;
2011-03-08 03:09:51 +03:00
for ( i = 0 ; i < = nr ; i + + )
replay_esn - > bmp [ i ] = 0 ;
}
2011-10-11 05:58:37 +04:00
bitnr = ( pos + diff ) % replay_esn - > replay_window ;
2011-03-08 03:09:51 +03:00
replay_esn - > seq = seq ;
if ( unlikely ( wrap > 0 ) )
replay_esn - > seq_hi + + ;
} else {
diff = replay_esn - > seq - seq ;
2011-10-11 05:58:37 +04:00
if ( pos > = diff )
2011-03-08 03:09:51 +03:00
bitnr = ( pos - diff ) % replay_esn - > replay_window ;
2011-10-11 05:58:37 +04:00
else
2011-03-08 03:09:51 +03:00
bitnr = replay_esn - > replay_window - ( diff - pos ) ;
}
2018-01-14 12:39:10 +03:00
xfrm_dev_state_advance_esn ( x ) ;
2011-10-11 05:58:37 +04:00
nr = bitnr > > 5 ;
bitnr = bitnr & 0x1F ;
replay_esn - > bmp [ nr ] | = ( 1U < < bitnr ) ;
2011-03-08 03:09:51 +03:00
if ( xfrm_aevent_is_on ( xs_net ( x ) ) )
2021-06-18 16:51:56 +03:00
xfrm_replay_notify ( x , XFRM_REPLAY_UPDATE ) ;
2011-03-08 03:09:51 +03:00
}
2017-04-14 11:07:01 +03:00
# 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 )
2021-06-18 16:52:00 +03:00
return __xfrm_replay_overflow ( x , skb ) ;
2017-04-14 11:07:01 +03:00
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 ;
2020-05-30 15:39:12 +03:00
if ( unlikely ( oseq < x - > replay . oseq ) & &
! ( x - > props . extra_flags & XFRM_SA_XFLAG_OSEQ_MAY_WRAP ) ) {
2017-04-14 11:07:01 +03:00
xfrm_audit_state_replay_overflow ( x , skb ) ;
err = - EOVERFLOW ;
return err ;
}
x - > replay . oseq = oseq ;
if ( xfrm_aevent_is_on ( net ) )
2021-06-18 16:51:56 +03:00
xfrm_replay_notify ( x , XFRM_REPLAY_UPDATE ) ;
2017-04-14 11:07:01 +03:00
}
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 ;
2020-05-30 15:39:12 +03:00
if ( unlikely ( oseq < replay_esn - > oseq ) & &
! ( x - > props . extra_flags & XFRM_SA_XFLAG_OSEQ_MAY_WRAP ) ) {
2017-04-14 11:07:01 +03:00
xfrm_audit_state_replay_overflow ( x , skb ) ;
err = - EOVERFLOW ;
return err ;
} else {
replay_esn - > oseq = oseq ;
}
if ( xfrm_aevent_is_on ( net ) )
2021-06-18 16:51:56 +03:00
xfrm_replay_notify ( x , XFRM_REPLAY_UPDATE ) ;
2017-04-14 11:07:01 +03:00
}
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 ;
2018-02-28 11:23:19 +03:00
xo - > seq . low = oseq + 1 ;
2017-04-14 11:07:01 +03:00
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 ;
2017-11-28 12:49:28 +03:00
replay_esn - > oseq_hi = oseq_hi ;
2017-04-14 11:07:01 +03:00
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 ;
if ( xfrm_aevent_is_on ( net ) )
2021-06-18 16:51:56 +03:00
xfrm_replay_notify ( x , XFRM_REPLAY_UPDATE ) ;
2017-04-14 11:07:01 +03:00
}
return err ;
}
2021-06-18 16:52:00 +03:00
int xfrm_replay_overflow ( struct xfrm_state * x , struct sk_buff * skb )
{
switch ( x - > repl_mode ) {
case XFRM_REPLAY_MODE_LEGACY :
break ;
case XFRM_REPLAY_MODE_BMP :
return xfrm_replay_overflow_offload_bmp ( x , skb ) ;
case XFRM_REPLAY_MODE_ESN :
return xfrm_replay_overflow_offload_esn ( x , skb ) ;
}
2017-04-14 11:07:01 +03:00
2021-06-18 16:52:00 +03:00
return xfrm_replay_overflow_offload ( x , skb ) ;
}
2017-04-14 11:07:01 +03:00
# else
2021-06-18 16:52:00 +03:00
int xfrm_replay_overflow ( struct xfrm_state * x , struct sk_buff * skb )
{
switch ( x - > repl_mode ) {
case XFRM_REPLAY_MODE_LEGACY :
break ;
case XFRM_REPLAY_MODE_BMP :
return xfrm_replay_overflow_bmp ( x , skb ) ;
case XFRM_REPLAY_MODE_ESN :
return xfrm_replay_overflow_esn ( x , skb ) ;
}
2011-03-08 03:09:09 +03:00
2021-06-18 16:52:00 +03:00
return __xfrm_replay_overflow ( x , skb ) ;
}
2017-04-14 11:07:01 +03:00
# endif
2011-03-08 03:09:51 +03:00
2011-03-08 03:08:32 +03:00
int xfrm_init_replay ( struct xfrm_state * x )
{
2011-03-08 03:09:09 +03:00
struct xfrm_replay_state_esn * replay_esn = x - > replay_esn ;
if ( replay_esn ) {
if ( replay_esn - > replay_window >
2011-04-25 23:39:24 +04:00
replay_esn - > bmp_len * sizeof ( __u32 ) * 8 )
2011-03-08 03:09:09 +03:00
return - EINVAL ;
2012-11-08 14:15:44 +04:00
if ( x - > props . flags & XFRM_STATE_ESN ) {
if ( replay_esn - > replay_window = = 0 )
return - EINVAL ;
2021-06-18 16:51:56 +03:00
x - > repl_mode = XFRM_REPLAY_MODE_ESN ;
2017-04-14 11:07:01 +03:00
} else {
2021-06-18 16:51:56 +03:00
x - > repl_mode = XFRM_REPLAY_MODE_BMP ;
2017-04-14 11:07:01 +03:00
}
} else {
2021-06-18 16:51:56 +03:00
x - > repl_mode = XFRM_REPLAY_MODE_LEGACY ;
2017-04-14 11:07:01 +03:00
}
2011-03-08 03:09:09 +03:00
2011-03-08 03:08:32 +03:00
return 0 ;
}
EXPORT_SYMBOL ( xfrm_init_replay ) ;