diff mbox series

[ipsec-next,v10,1/3] xfrm: Add Direction to the SA in or out

Message ID 0e0d997e634261fcdf16cf9f07c97d97af7370b6.1712828282.git.antony.antony@secunet.com (mailing list archive)
State Awaiting Upstream
Delegated to: Netdev Maintainers
Headers show
Series [ipsec-next,v10,1/3] xfrm: Add Direction to the SA in or out | expand

Checks

Context Check Description
netdev/series_format warning Series does not have a cover letter; Target tree name not specified in the subject
netdev/tree_selection success Guessed tree name to be net-next
netdev/ynl success Generated files up to date; no warnings/errors; no diff in generated;
netdev/fixes_present success Fixes tag not required for -next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 5023 this patch: 5023
netdev/build_tools success Errors and warnings before: 0 this patch: 0
netdev/cc_maintainers success CCed 6 of 6 maintainers
netdev/build_clang success Errors and warnings before: 1066 this patch: 1066
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 5299 this patch: 5299
netdev/checkpatch warning WARNING: line length of 100 exceeds 80 columns WARNING: line length of 81 exceeds 80 columns WARNING: line length of 84 exceeds 80 columns WARNING: line length of 89 exceeds 80 columns WARNING: line length of 93 exceeds 80 columns WARNING: line length of 94 exceeds 80 columns WARNING: line length of 96 exceeds 80 columns WARNING: line length of 99 exceeds 80 columns
netdev/build_clang_rust success No Rust files in patch. Skipping build
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0
netdev/contest fail net-next-2024-04-11--12-00 (tests: 960)

Commit Message

Antony Antony April 11, 2024, 9:40 a.m. UTC
This patch introduces the 'dir' attribute, 'in' or 'out', to the
xfrm_state, SA, enhancing usability by delineating the scope of values
based on direction. An input SA will now exclusively encompass values
pertinent to input, effectively segregating them from output-related
values. This change aims to streamline the configuration process and
improve the overall clarity of SA attributes.

This feature sets the groundwork for future patches, including
the upcoming IP-TFS patch.

Signed-off-by: Antony Antony <antony.antony@secunet.com>
---
v9->v10:
 - add more direction specific validations
	XFRM_STATE_NOPMTUDISC, XFRM_SA_XFLAG_DONT_ENCAP_DSCP
	XFRMA_MTIMER_THRESH
 - refactor validations into a fuction.
 - add dir to ALLOCSPI to support strongSwan updating SPI for "in" state
v8->v9:
 - add validation XFRM_STATE_ICMP not allowed on OUT SA.

v7->v8:
 - add extra validation check on replay window and seq
 - XFRM_MSG_UPDSA old and new SA should match "dir"

v6->v7:
 - add replay-window check non-esn 0 and ESN 1.
 - remove :XFRMA_SA_DIR only allowed with HW OFFLOAD

v5->v6:
 - XFRMA_SA_DIR only allowed with HW OFFLOAD

v4->v5:
 - add details to commit message

v3->v4:
 - improve HW OFFLOAD DIR check add the other direction

v2->v3:
 - delete redundant XFRM_SA_DIR_USE
 - use u8 for "dir"
 - fix HW OFFLOAD DIR check

v1->v2:
 - use .strict_start_type in struct nla_policy xfrma_policy
 - delete redundant XFRM_SA_DIR_MAX enum
---
 include/net/xfrm.h        |   1 +
 include/uapi/linux/xfrm.h |   6 ++
 net/xfrm/xfrm_compat.c    |   7 +-
 net/xfrm/xfrm_device.c    |   6 ++
 net/xfrm/xfrm_state.c     |   4 ++
 net/xfrm/xfrm_user.c      | 139 ++++++++++++++++++++++++++++++++++++--
 6 files changed, 157 insertions(+), 6 deletions(-)

--
2.30.2

Comments

Leon Romanovsky April 11, 2024, 11:41 a.m. UTC | #1
On Thu, Apr 11, 2024 at 11:40:59AM +0200, Antony Antony wrote:
> This patch introduces the 'dir' attribute, 'in' or 'out', to the
> xfrm_state, SA, enhancing usability by delineating the scope of values
> based on direction. An input SA will now exclusively encompass values
> pertinent to input, effectively segregating them from output-related
> values. This change aims to streamline the configuration process and
> improve the overall clarity of SA attributes.
> 
> This feature sets the groundwork for future patches, including
> the upcoming IP-TFS patch.
> 
> Signed-off-by: Antony Antony <antony.antony@secunet.com>
> ---
> v9->v10:
>  - add more direction specific validations
> 	XFRM_STATE_NOPMTUDISC, XFRM_SA_XFLAG_DONT_ENCAP_DSCP
> 	XFRMA_MTIMER_THRESH
>  - refactor validations into a fuction.
>  - add dir to ALLOCSPI to support strongSwan updating SPI for "in" state
> v8->v9:
>  - add validation XFRM_STATE_ICMP not allowed on OUT SA.
> 
> v7->v8:
>  - add extra validation check on replay window and seq
>  - XFRM_MSG_UPDSA old and new SA should match "dir"

I asked it on one of the previous versions, but why do we need this limitation?
Update SA is actually add and delete, so if user wants to update
direction, it should be allowed.

Thanks
Paul Wouters April 11, 2024, 4:20 p.m. UTC | #2
On Apr 11, 2024, at 07:41, Leon Romanovsky via Devel <devel@linux-ipsec.org> wrote:
> 
> 
> I asked it on one of the previous versions, but why do we need this limitation?
> Update SA is actually add and delete, so if user wants to update
> direction, it should be allowed.

An SA can never change direction without dealing with updated SPIs. Logically, it makes no sense. Sure, if you treat SA’s as Linux lego bricks that can be turned into anything, then yeah why not. Why not turn the SA into block device or magic flute?

If you keep to the RFC, an SA is uni directional. More things might apply too, eg the mode (transport vs tunnel) should not change, sequence numbers can only increase, etc.

Paul
Christian Hopps April 11, 2024, 4:40 p.m. UTC | #3
Paul Wouters via Devel <devel@linux-ipsec.org> writes:

> On Apr 11, 2024, at 07:41, Leon Romanovsky via Devel <devel@linux-ipsec.org> wrote:
>>
>>
>> I asked it on one of the previous versions, but why do we need this limitation?
>> Update SA is actually add and delete, so if user wants to update
>> direction, it should be allowed.
>
> An SA can never change direction without dealing with updated SPIs. Logically,
> it makes no sense. Sure, if you treat SA’s as Linux lego bricks that can be
> turned into anything, then yeah why not. Why not turn the SA into block device
> or magic flute?
>
> If you keep to the RFC, an SA is uni directional. More things might apply too,
> eg the mode (transport vs tunnel) should not change, sequence numbers can only
> increase, etc.

I have to agree. When we add new direction specific attributes (e.g., iptfs send-rate, pkt-size, etc, for "out", reorder window size, drop-time for "in") changing direction would mean removing invalidating these attributes and possibly setting new different ones, deleting old send state adding new receive state, etc.. this doesn't feel like an SA update IMO, it feels like a new SA. I say we should apply KISS and just require a new SA for a different direction attribute.

Thanks,
Chris.


>
> Paul
Leon Romanovsky April 11, 2024, 5:05 p.m. UTC | #4
On Thu, Apr 11, 2024 at 12:20:31PM -0400, Paul Wouters wrote:
> On Apr 11, 2024, at 07:41, Leon Romanovsky via Devel <devel@linux-ipsec.org> wrote:
> > 
> > 
> > I asked it on one of the previous versions, but why do we need this limitation?
> > Update SA is actually add and delete, so if user wants to update
> > direction, it should be allowed.
> 
> An SA can never change direction without dealing with updated SPIs. Logically, it makes no sense. Sure, if you treat SA’s as Linux lego bricks that can be turned into anything, then yeah why not. Why not turn the SA into block device or magic flute?
> 
> If you keep to the RFC, an SA is uni directional. More things might apply too, eg the mode (transport vs tunnel) should not change, sequence numbers can only increase, etc.

Right, and it looks like preventing change of direction is only small
part of larger task - "improve validation of SA updates".

Thanks

> 
> Paul
>
Sabrina Dubroca April 15, 2024, 12:21 p.m. UTC | #5
2024-04-11, 11:40:59 +0200, Antony Antony wrote:
> diff --git a/net/xfrm/xfrm_compat.c b/net/xfrm/xfrm_compat.c
> index 655fe4ff8621..007dee03b1bc 100644
> --- a/net/xfrm/xfrm_compat.c
> +++ b/net/xfrm/xfrm_compat.c
> @@ -98,6 +98,7 @@ static const int compat_msg_min[XFRM_NR_MSGTYPES] = {
>  };
> 
>  static const struct nla_policy compat_policy[XFRMA_MAX+1] = {
> +	[XFRMA_UNSPEC]          = { .strict_start_type = XFRMA_SA_DIR },
>  	[XFRMA_SA]		= { .len = XMSGSIZE(compat_xfrm_usersa_info)},
>  	[XFRMA_POLICY]		= { .len = XMSGSIZE(compat_xfrm_userpolicy_info)},
>  	[XFRMA_LASTUSED]	= { .type = NLA_U64},
> @@ -129,6 +130,7 @@ static const struct nla_policy compat_policy[XFRMA_MAX+1] = {
>  	[XFRMA_SET_MARK_MASK]	= { .type = NLA_U32 },
>  	[XFRMA_IF_ID]		= { .type = NLA_U32 },
>  	[XFRMA_MTIMER_THRESH]	= { .type = NLA_U32 },
> +	[XFRMA_SA_DIR]          = { .type = NLA_U8}

nit: <...> },

(space before } and , afterwards)

See below for a comment on the policy itself.


> diff --git a/net/xfrm/xfrm_device.c b/net/xfrm/xfrm_device.c
> index 6346690d5c69..2455a76a1cff 100644
> --- a/net/xfrm/xfrm_device.c
> +++ b/net/xfrm/xfrm_device.c
> @@ -253,6 +253,12 @@ int xfrm_dev_state_add(struct net *net, struct xfrm_state *x,
>  		return -EINVAL;
>  	}
> 
> +	if ((xuo->flags & XFRM_OFFLOAD_INBOUND && x->dir == XFRM_SA_DIR_OUT) ||
> +	    (!(xuo->flags & XFRM_OFFLOAD_INBOUND) && x->dir == XFRM_SA_DIR_IN)) {
> +		NL_SET_ERR_MSG(extack, "Mismatched SA and offload direction");
> +		return -EINVAL;
> +	}

It would be nice to set x->dir to match the flag, but then I guess the
validation in xfrm_state_update would fail if userspaces tries an
update without providing XFRMA_SA_DIR. (or not because we already went
through this code by the time we get to xfrm_state_update?)


> diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c
> index 810b520493f3..df141edbe8d1 100644
> --- a/net/xfrm/xfrm_user.c
> +++ b/net/xfrm/xfrm_user.c
[...]
> @@ -779,6 +793,77 @@ static struct xfrm_state *xfrm_state_construct(struct net *net,
>  	return NULL;
>  }
> 
> +static int verify_sa_dir(const struct xfrm_state *x, struct netlink_ext_ack *extack)
> +{
> +	if (x->dir == XFRM_SA_DIR_OUT)  {
> +		if (x->props.replay_window > 0) {
> +			NL_SET_ERR_MSG(extack, "Replay window should not be set for OUT SA");
> +			return -EINVAL;
> +		}
> +
> +		if (x->replay.seq || x->replay.bitmap) {
> +			NL_SET_ERR_MSG(extack,
> +				       "Replay seq, or bitmap should not be set for OUT SA with ESN");

I thought x->replay was for non-ESN, since we have x->replay_esn.

> +			return -EINVAL;
> +		}
> +
> +		if (x->replay_esn) {
> +			if (x->replay_esn->replay_window > 1) {
> +				NL_SET_ERR_MSG(extack,
> +					       "Replay window should be 1 for OUT SA with ESN");

I don't think that we should introduce something we know doesn't make
sense (replay window = 1 on output). It will be API and we won't be
able to fix it up later. We get a chance to make things nice and
reasonable with this new attribute, let's not waste it.

As I said, AFAICT replay_esn->replay_window isn't used on output, so
unless I missed something, it should just be a matter of changing the
validation. The additional checks in this version should guarantee we
don't have dir==OUT SAs in the packet input path, so this should work.

> +				return -EINVAL;
> +			}
> +
> +			if (x->replay_esn->seq || x->replay_esn->seq_hi || x->replay_esn->bmp_len) {
> +				NL_SET_ERR_MSG(extack,
> +					       "Replay seq, seq_hi, bmp_len should not be set for OUT SA with ESN");
> +				return -EINVAL;
> +			}
> +		}
> +
> +		if (x->props.flags & XFRM_STATE_DECAP_DSCP) {
> +			NL_SET_ERR_MSG(extack, "Flag NDECAP_DSCP should not be set for OUT SA");

                                                     ^ extra N?

> +			return -EINVAL;
> +		}
> +

[...]
>  static int xfrm_add_sa(struct sk_buff *skb, struct nlmsghdr *nlh,
>  		       struct nlattr **attrs, struct netlink_ext_ack *extack)
>  {
> @@ -796,6 +881,16 @@ static int xfrm_add_sa(struct sk_buff *skb, struct nlmsghdr *nlh,
>  	if (!x)
>  		return err;
> 
> +	if (x->dir) {
> +		err = verify_sa_dir(x, extack);
> +		if (err) {
> +			x->km.state = XFRM_STATE_DEAD;
> +			xfrm_dev_state_delete(x);
> +			xfrm_state_put(x);
> +			return err;

That's not very nice. We're creating a state and just throwing it away
immediately. How hard would it be to validate all that directly from
verify_newsa_info instead?


[...]
> @@ -3018,6 +3137,7 @@ EXPORT_SYMBOL_GPL(xfrm_msg_min);
>  #undef XMSGSIZE
> 
>  const struct nla_policy xfrma_policy[XFRMA_MAX+1] = {
> +	[XFRMA_UNSPEC]		= { .strict_start_type = XFRMA_SA_DIR },
>  	[XFRMA_SA]		= { .len = sizeof(struct xfrm_usersa_info)},
>  	[XFRMA_POLICY]		= { .len = sizeof(struct xfrm_userpolicy_info)},
>  	[XFRMA_LASTUSED]	= { .type = NLA_U64},
> @@ -3049,6 +3169,7 @@ const struct nla_policy xfrma_policy[XFRMA_MAX+1] = {
>  	[XFRMA_SET_MARK_MASK]	= { .type = NLA_U32 },
>  	[XFRMA_IF_ID]		= { .type = NLA_U32 },
>  	[XFRMA_MTIMER_THRESH]   = { .type = NLA_U32 },
> +	[XFRMA_SA_DIR]          = { .type = NLA_U8 }

With

    .type = NLA_POLICY_RANGE(NLA_U8, XFRM_SA_DIR_IN, XFRM_SA_DIR_OUT) },

you wouldn't need to validate the attribute's values in
verify_newsa_info and xfrm_alloc_userspi. And same for the xfrm_compat
version of this.

(also a nit on the formatting: a "," after the } would be nice, so
that the next addition doesn't need to touch this line)


And as we discussed, I'd really like XFRMA_SA_DIR to be rejected in
commands that don't use its value.


Thanks.
Antony Antony April 16, 2024, 7:10 a.m. UTC | #6
On Mon, Apr 15, 2024 at 02:21:50PM +0200, Sabrina Dubroca via Devel wrote:
> 2024-04-11, 11:40:59 +0200, Antony Antony wrote:
> > diff --git a/net/xfrm/xfrm_compat.c b/net/xfrm/xfrm_compat.c
> > index 655fe4ff8621..007dee03b1bc 100644
> > --- a/net/xfrm/xfrm_compat.c
> > +++ b/net/xfrm/xfrm_compat.c
> > @@ -98,6 +98,7 @@ static const int compat_msg_min[XFRM_NR_MSGTYPES] = {
> >  };
> > 
> >  static const struct nla_policy compat_policy[XFRMA_MAX+1] = {
> > +	[XFRMA_UNSPEC]          = { .strict_start_type = XFRMA_SA_DIR },
> >  	[XFRMA_SA]		= { .len = XMSGSIZE(compat_xfrm_usersa_info)},
> >  	[XFRMA_POLICY]		= { .len = XMSGSIZE(compat_xfrm_userpolicy_info)},
> >  	[XFRMA_LASTUSED]	= { .type = NLA_U64},
> > @@ -129,6 +130,7 @@ static const struct nla_policy compat_policy[XFRMA_MAX+1] = {
> >  	[XFRMA_SET_MARK_MASK]	= { .type = NLA_U32 },
> >  	[XFRMA_IF_ID]		= { .type = NLA_U32 },
> >  	[XFRMA_MTIMER_THRESH]	= { .type = NLA_U32 },
> > +	[XFRMA_SA_DIR]          = { .type = NLA_U8}
> 
> nit: <...> },
> 
> (space before } and , afterwards)
> 
> See below for a comment on the policy itself.

fixed in v11.

> 
> 
> > diff --git a/net/xfrm/xfrm_device.c b/net/xfrm/xfrm_device.c
> > index 6346690d5c69..2455a76a1cff 100644
> > --- a/net/xfrm/xfrm_device.c
> > +++ b/net/xfrm/xfrm_device.c
> > @@ -253,6 +253,12 @@ int xfrm_dev_state_add(struct net *net, struct xfrm_state *x,
> >  		return -EINVAL;
> >  	}
> > 
> > +	if ((xuo->flags & XFRM_OFFLOAD_INBOUND && x->dir == XFRM_SA_DIR_OUT) ||
> > +	    (!(xuo->flags & XFRM_OFFLOAD_INBOUND) && x->dir == XFRM_SA_DIR_IN)) {
> > +		NL_SET_ERR_MSG(extack, "Mismatched SA and offload direction");
> > +		return -EINVAL;
> > +	}
> 
> It would be nice to set x->dir to match the flag, but then I guess the
> validation in xfrm_state_update would fail if userspaces tries an
> update without providing XFRMA_SA_DIR. (or not because we already went
> through this code by the time we get to xfrm_state_update?)

this code already executed from xfrm_state_construct.
We could set the in flag in xuo when x->dir == XFRM_SA_DIR_IN, let me think 
again.  May be we can do that later:)

> > diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c
> > index 810b520493f3..df141edbe8d1 100644
> > --- a/net/xfrm/xfrm_user.c
> > +++ b/net/xfrm/xfrm_user.c
> [...]
> > @@ -779,6 +793,77 @@ static struct xfrm_state *xfrm_state_construct(struct net *net,
> >  	return NULL;
> >  }
> > 
> > +static int verify_sa_dir(const struct xfrm_state *x, struct netlink_ext_ack *extack)
> > +{
> > +	if (x->dir == XFRM_SA_DIR_OUT)  {
> > +		if (x->props.replay_window > 0) {
> > +			NL_SET_ERR_MSG(extack, "Replay window should not be set for OUT SA");
> > +			return -EINVAL;
> > +		}
> > +
> > +		if (x->replay.seq || x->replay.bitmap) {
> > +			NL_SET_ERR_MSG(extack,
> > +				       "Replay seq, or bitmap should not be set for OUT SA with ESN");
> 
> I thought x->replay was for non-ESN, since we have x->replay_esn.
> 
you are right. It is a wrong text due to copy paste.
Fixed in v11.

> > +			return -EINVAL;
> > +		}
> > +
> > +		if (x->replay_esn) {
> > +			if (x->replay_esn->replay_window > 1) {
> > +				NL_SET_ERR_MSG(extack,
> > +					       "Replay window should be 1 for OUT SA with ESN");
> 
> I don't think that we should introduce something we know doesn't make
> sense (replay window = 1 on output). It will be API and we won't be
> able to fix it up later. We get a chance to make things nice and
> reasonable with this new attribute, let's not waste it.


>
> As I said, AFAICT replay_esn->replay_window isn't used on output, so
> unless I missed something, it should just be a matter of changing the
> validation. The additional checks in this version should guarantee we
> don't have dir==OUT SAs in the packet input path, so this should work.

I agree. Your message and Steffen's message helped me figure out,
how to allow replay-window zero for output SA;
It is in v11.

> > +				return -EINVAL;
> > +			}
> > +
> > +			if (x->replay_esn->seq || x->replay_esn->seq_hi || x->replay_esn->bmp_len) {
> > +				NL_SET_ERR_MSG(extack,
> > +					       "Replay seq, seq_hi, bmp_len should not be set for OUT SA with ESN");
> > +				return -EINVAL;
> > +			}
> > +		}
> > +
> > +		if (x->props.flags & XFRM_STATE_DECAP_DSCP) {
> > +			NL_SET_ERR_MSG(extack, "Flag NDECAP_DSCP should not be set for OUT SA");
> 
>                                                      ^ extra N?
> 
> > +			return -EINVAL;
> > +		}
> > +
> 
> [...]
> >  static int xfrm_add_sa(struct sk_buff *skb, struct nlmsghdr *nlh,
> >  		       struct nlattr **attrs, struct netlink_ext_ack *extack)
> >  {
> > @@ -796,6 +881,16 @@ static int xfrm_add_sa(struct sk_buff *skb, struct nlmsghdr *nlh,
> >  	if (!x)
> >  		return err;
> > 
> > +	if (x->dir) {
> > +		err = verify_sa_dir(x, extack);
> > +		if (err) {
> > +			x->km.state = XFRM_STATE_DEAD;
> > +			xfrm_dev_state_delete(x);
> > +			xfrm_state_put(x);
> > +			return err;
> 
> That's not very nice. We're creating a state and just throwing it away
> immediately. How hard would it be to validate all that directly from
> verify_newsa_info instead?

Your proposal would introduce redundant code, requiring accessing attributes 
in verify_newsa_info() and other functions.

The way I propsed, a state x,  xfrm_state, is created but it remains 
km.stae=XFRM_STATE_VOID.
Newely added verify is before auditing and generating new genid changes, 
xfrm_state_add() or xfrm_state_update() would be called later. So deleteing 
a state just after xfrm_staet_constructi() is not  bad!

So I think the current code is cleaner, avoiding the need redundant code in 
verify_newsa_info().

> 
> [...]
> > @@ -3018,6 +3137,7 @@ EXPORT_SYMBOL_GPL(xfrm_msg_min);
> >  #undef XMSGSIZE
> > 
> >  const struct nla_policy xfrma_policy[XFRMA_MAX+1] = {
> > +	[XFRMA_UNSPEC]		= { .strict_start_type = XFRMA_SA_DIR },
> >  	[XFRMA_SA]		= { .len = sizeof(struct xfrm_usersa_info)},
> >  	[XFRMA_POLICY]		= { .len = sizeof(struct xfrm_userpolicy_info)},
> >  	[XFRMA_LASTUSED]	= { .type = NLA_U64},
> > @@ -3049,6 +3169,7 @@ const struct nla_policy xfrma_policy[XFRMA_MAX+1] = {
> >  	[XFRMA_SET_MARK_MASK]	= { .type = NLA_U32 },
> >  	[XFRMA_IF_ID]		= { .type = NLA_U32 },
> >  	[XFRMA_MTIMER_THRESH]   = { .type = NLA_U32 },
> > +	[XFRMA_SA_DIR]          = { .type = NLA_U8 }
> 
> With
> 
>     .type = NLA_POLICY_RANGE(NLA_U8, XFRM_SA_DIR_IN, XFRM_SA_DIR_OUT) },
> 
> you wouldn't need to validate the attribute's values in
> verify_newsa_info and xfrm_alloc_userspi. And same for the xfrm_compat
> version of this.

thanks, this is much better.

 
> (also a nit on the formatting: a "," after the } would be nice, so
> that the next addition doesn't need to touch this line)
> 
> 
> And as we discussed, I'd really like XFRMA_SA_DIR to be rejected in
> commands that don't use its value.

I still don't see how to add such a check to about 20 functions. A burte 
force method would be 18-20 times copy code bellow, with different extack 
message.

+++ b/net/xfrm/xfrm_user.c
@@ -957,6 +957,11 @@ static int xfrm_del_sa(struct sk_buff *skb, struct nlmsghdr *nlh,
        struct km_event c;
        struct xfrm_usersa_id *p = nlmsg_data(nlh);

+       if (attrs[XFRMA_SA_DIR]) {
+               NL_SET_ERR_MSG(extack, "Delete should not have dir attribute set");
+               return -ESRCH;
+       }
+

I am still trying to figure out netlink examples, including the ones you 
pointed out : rtnl_valid_dump_net_req, rtnl_net_valid_getid_req.
I wonder if there is a way to specifiy rejeced attributes per method.

may be there is  way to call nlmsg_parse_deprecated_strict()
with .type = NLA_REJECT.

And also this looks like a general cleanup up to me. I wonder how Steffen 
would add such a check for the upcoming PCPU attribute! Should that be 
prohibited DELSA or XFRM_MSG_FLUSHSA or DELSA?

-antony
Sabrina Dubroca April 16, 2024, 8:36 a.m. UTC | #7
2024-04-16, 09:10:25 +0200, Antony Antony wrote:
> On Mon, Apr 15, 2024 at 02:21:50PM +0200, Sabrina Dubroca via Devel wrote:
> > 2024-04-11, 11:40:59 +0200, Antony Antony wrote:
> > > diff --git a/net/xfrm/xfrm_device.c b/net/xfrm/xfrm_device.c
> > > index 6346690d5c69..2455a76a1cff 100644
> > > --- a/net/xfrm/xfrm_device.c
> > > +++ b/net/xfrm/xfrm_device.c
> > > @@ -253,6 +253,12 @@ int xfrm_dev_state_add(struct net *net, struct xfrm_state *x,
> > >  		return -EINVAL;
> > >  	}
> > > 
> > > +	if ((xuo->flags & XFRM_OFFLOAD_INBOUND && x->dir == XFRM_SA_DIR_OUT) ||
> > > +	    (!(xuo->flags & XFRM_OFFLOAD_INBOUND) && x->dir == XFRM_SA_DIR_IN)) {
> > > +		NL_SET_ERR_MSG(extack, "Mismatched SA and offload direction");
> > > +		return -EINVAL;
> > > +	}
> > 
> > It would be nice to set x->dir to match the flag, but then I guess the
> > validation in xfrm_state_update would fail if userspaces tries an
> > update without providing XFRMA_SA_DIR. (or not because we already went
> > through this code by the time we get to xfrm_state_update?)
> 
> this code already executed from xfrm_state_construct.
> We could set the in flag in xuo when x->dir == XFRM_SA_DIR_IN, let me think 
> again.  May be we can do that later:)

I mean setting x->dir, not setting xuo, ie adding something like this
to xfrm_dev_state_add:

    x->dir = xuo->flags & XFRM_OFFLOAD_INBOUND ? XFRM_SA_DIR_IN : XFRM_SA_DIR_OUT;

xuo will already be set correctly when we're using offload, and won't
be present if we're not.


> > > diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c
> > > index 810b520493f3..df141edbe8d1 100644
> > > --- a/net/xfrm/xfrm_user.c
> > > +++ b/net/xfrm/xfrm_user.c
> > > +			return -EINVAL;
> > > +		}
> > > +
> > > +		if (x->replay_esn) {
> > > +			if (x->replay_esn->replay_window > 1) {
> > > +				NL_SET_ERR_MSG(extack,
> > > +					       "Replay window should be 1 for OUT SA with ESN");
> > 
> > I don't think that we should introduce something we know doesn't make
> > sense (replay window = 1 on output). It will be API and we won't be
> > able to fix it up later. We get a chance to make things nice and
> > reasonable with this new attribute, let's not waste it.
> >
> > As I said, AFAICT replay_esn->replay_window isn't used on output, so
> > unless I missed something, it should just be a matter of changing the
> > validation. The additional checks in this version should guarantee we
> > don't have dir==OUT SAs in the packet input path, so this should work.
> 
> I agree. Your message and Steffen's message helped me figure out,
> how to allow replay-window zero for output SA;
> It is in v11.

Nice, thanks.

> > [...]
> > >  static int xfrm_add_sa(struct sk_buff *skb, struct nlmsghdr *nlh,
> > >  		       struct nlattr **attrs, struct netlink_ext_ack *extack)
> > >  {
> > > @@ -796,6 +881,16 @@ static int xfrm_add_sa(struct sk_buff *skb, struct nlmsghdr *nlh,
> > >  	if (!x)
> > >  		return err;
> > > 
> > > +	if (x->dir) {
> > > +		err = verify_sa_dir(x, extack);
> > > +		if (err) {
> > > +			x->km.state = XFRM_STATE_DEAD;
> > > +			xfrm_dev_state_delete(x);
> > > +			xfrm_state_put(x);
> > > +			return err;
> > 
> > That's not very nice. We're creating a state and just throwing it away
> > immediately. How hard would it be to validate all that directly from
> > verify_newsa_info instead?
> 
> Your proposal would introduce redundant code, requiring accessing attributes 
> in verify_newsa_info() and other functions.
> 
> The way I propsed, a state x,  xfrm_state, is created but it remains 
> km.stae=XFRM_STATE_VOID.
> Newely added verify is before auditing and generating new genid changes, 
> xfrm_state_add() or xfrm_state_update() would be called later. So deleteing 
> a state just after xfrm_staet_constructi() is not  bad!
> 
> So I think the current code is cleaner, avoiding the need redundant code in 
> verify_newsa_info().

Avoids a few easy accesses to the netlink attributes, but allocating a
state and all its associated info just to throw it away almost
immediately is not "cleaner" IMO.


> > And as we discussed, I'd really like XFRMA_SA_DIR to be rejected in
> > commands that don't use its value.
> 
> I still don't see how to add such a check to about 20 functions. A burte 
> force method would be 18-20 times copy code bellow, with different extack 
> message.

Yes, I think with the current netlink infrastructure and a single
shared policy for all netlink message types, that's what we have to
do. Doing it in the netlink core (or with help of the netlink core)
seems difficult, as only the caller (xfrm_user) has all the
information about which attributes are acceptable with each message
type.

> +++ b/net/xfrm/xfrm_user.c
> @@ -957,6 +957,11 @@ static int xfrm_del_sa(struct sk_buff *skb, struct nlmsghdr *nlh,
>         struct km_event c;
>         struct xfrm_usersa_id *p = nlmsg_data(nlh);
> 
> +       if (attrs[XFRMA_SA_DIR]) {
> +               NL_SET_ERR_MSG(extack, "Delete should not have dir attribute set");
> +               return -ESRCH;
> +       }
> +
> 
> I am still trying to figure out netlink examples, including the ones you 
> pointed out : rtnl_valid_dump_net_req, rtnl_net_valid_getid_req.

These do pretty much what you wrote.

> I wonder if there is a way to specifiy rejeced attributes per method.
>
> may be there is  way to call nlmsg_parse_deprecated_strict()
> with .type = NLA_REJECT.

For that, we'd have to separate the policies for each netlink
command. Otherwise NLA_REJECT will reject the SA_DIR attribute for all
commands, which is not what we want.

> And also this looks like a general cleanup up to me. I wonder how Steffen 
> would add such a check for the upcoming PCPU attribute! Should that be 
> prohibited DELSA or XFRM_MSG_FLUSHSA or DELSA?

IMO, new attributes should be rejected in any handler that doesn't use
them. That's not a general cleanup because it's a new attribute, and
the goal is to allow us to decide later if we want to use that
attribute in DELSA etc. Maybe in one year, we want to make DELSA able
to match on SA_DIR. If we don't reject SA_DIR from DELSA now, we won't
be able to do that. That's why I'm insisting on this.
Antony Antony April 21, 2024, 10:04 p.m. UTC | #8
Hi Sabrina,

On Tue, Apr 16, 2024 at 10:36:16AM +0200, Sabrina Dubroca wrote:
> 2024-04-16, 09:10:25 +0200, Antony Antony wrote:
> > On Mon, Apr 15, 2024 at 02:21:50PM +0200, Sabrina Dubroca via Devel wrote:
> > > 2024-04-11, 11:40:59 +0200, Antony Antony wrote:
> > > > diff --git a/net/xfrm/xfrm_device.c b/net/xfrm/xfrm_device.c
> > > > index 6346690d5c69..2455a76a1cff 100644
> > > > --- a/net/xfrm/xfrm_device.c
> > > > +++ b/net/xfrm/xfrm_device.c
> > > > @@ -253,6 +253,12 @@ int xfrm_dev_state_add(struct net *net, struct xfrm_state *x,
> > > >  		return -EINVAL;
> > > >  	}
> > > > 
> > > > +	if ((xuo->flags & XFRM_OFFLOAD_INBOUND && x->dir == XFRM_SA_DIR_OUT) ||
> > > > +	    (!(xuo->flags & XFRM_OFFLOAD_INBOUND) && x->dir == XFRM_SA_DIR_IN)) {
> > > > +		NL_SET_ERR_MSG(extack, "Mismatched SA and offload direction");
> > > > +		return -EINVAL;
> > > > +	}
> > > 
> > > It would be nice to set x->dir to match the flag, but then I guess the
> > > validation in xfrm_state_update would fail if userspaces tries an
> > > update without providing XFRMA_SA_DIR. (or not because we already went
> > > through this code by the time we get to xfrm_state_update?)
> > 
> > this code already executed from xfrm_state_construct.
> > We could set the in flag in xuo when x->dir == XFRM_SA_DIR_IN, let me think 
> > again.  May be we can do that later:)
> 
> I mean setting x->dir, not setting xuo, ie adding something like this
> to xfrm_dev_state_add:
> 
>     x->dir = xuo->flags & XFRM_OFFLOAD_INBOUND ? XFRM_SA_DIR_IN : XFRM_SA_DIR_OUT;
> 
> xuo will already be set correctly when we're using offload, and won't
> be present if we're not.

Updating with older tools may fail validation. For instance, if a user creates
an SA using an older iproute2 with offload and XFRM_OFFLOAD_INBOUND flag 
set, the kernel sets x->dir = XFRM_SA_DIR_IN. Then, if the user wants to 
update this SA using the same older iproute2, which doesn't allow setting 
dir, the update will fail.

However, as I proposed, if SA dir "in" and offload is enabled, the kernel
could set xuo->flags &= XFRM_OFFLOAD_INBOUND to avoid double typing. I've
considered this, but I'm unsure of any side effects. Also this could be 
added later, which is why I've ignored it for now:)

> > > > diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c
> > > > index 810b520493f3..df141edbe8d1 100644
> > > > --- a/net/xfrm/xfrm_user.c
> > > > +++ b/net/xfrm/xfrm_user.c
> > > > +			return -EINVAL;
> > > > +		}
> > > > +
> > > > +		if (x->replay_esn) {
> > > > +			if (x->replay_esn->replay_window > 1) {
> > > > +				NL_SET_ERR_MSG(extack,
> > > > +					       "Replay window should be 1 for OUT SA with ESN");
> > > 
> > > I don't think that we should introduce something we know doesn't make
> > > sense (replay window = 1 on output). It will be API and we won't be
> > > able to fix it up later. We get a chance to make things nice and
> > > reasonable with this new attribute, let's not waste it.
> > >
> > > As I said, AFAICT replay_esn->replay_window isn't used on output, so
> > > unless I missed something, it should just be a matter of changing the
> > > validation. The additional checks in this version should guarantee we
> > > don't have dir==OUT SAs in the packet input path, so this should work.
> > 
> > I agree. Your message and Steffen's message helped me figure out,
> > how to allow replay-window zero for output SA;
> > It is in v11.
> 
> Nice, thanks.
> 
> > > [...]
> > > >  static int xfrm_add_sa(struct sk_buff *skb, struct nlmsghdr *nlh,
> > > >  		       struct nlattr **attrs, struct netlink_ext_ack *extack)
> > > >  {
> > > > @@ -796,6 +881,16 @@ static int xfrm_add_sa(struct sk_buff *skb, struct nlmsghdr *nlh,
> > > >  	if (!x)
> > > >  		return err;
> > > > 
> > > > +	if (x->dir) {
> > > > +		err = verify_sa_dir(x, extack);
> > > > +		if (err) {
> > > > +			x->km.state = XFRM_STATE_DEAD;
> > > > +			xfrm_dev_state_delete(x);
> > > > +			xfrm_state_put(x);
> > > > +			return err;
> > > 
> > > That's not very nice. We're creating a state and just throwing it away
> > > immediately. How hard would it be to validate all that directly from
> > > verify_newsa_info instead?
> > 
> > Your proposal would introduce redundant code, requiring accessing attributes 
> > in verify_newsa_info() and other functions.
> > 
> > The way I propsed, a state x,  xfrm_state, is created but it remains 
> > km.stae=XFRM_STATE_VOID.
> > Newely added verify is before auditing and generating new genid changes, 
> > xfrm_state_add() or xfrm_state_update() would be called later. So deleteing 
> > a state just after xfrm_staet_constructi() is not  bad!
> > 
> > So I think the current code is cleaner, avoiding the need redundant code in 
> > verify_newsa_info().
> 
> Avoids a few easy accesses to the netlink attributes, but allocating a
> state and all its associated info just to throw it away almost
> immediately is not "cleaner" IMO.
> > > And as we discussed, I'd really like XFRMA_SA_DIR to be rejected in
> > > commands that don't use its value.
> > 
> > I still don't see how to add such a check to about 20 functions. A burte 
> > force method would be 18-20 times copy code bellow, with different extack 
> > message.
> 
> Yes, I think with the current netlink infrastructure and a single
> shared policy for all netlink message types, that's what we have to
> do. Doing it in the netlink core (or with help of the netlink core)
> seems difficult, as only the caller (xfrm_user) has all the
> information about which attributes are acceptable with each message
> type.

yes. If we use netlink infrastructure to reject attributes in some methods
shared policy is not ideal. I tried NLA_POLICY_VALIDATE_FN() that function 
does not get message type as an arguent. So add a seperate function in v11.

> 
> > +++ b/net/xfrm/xfrm_user.c
> > @@ -957,6 +957,11 @@ static int xfrm_del_sa(struct sk_buff *skb, struct nlmsghdr *nlh,
> >         struct km_event c;
> >         struct xfrm_usersa_id *p = nlmsg_data(nlh);
> > 
> > +       if (attrs[XFRMA_SA_DIR]) {
> > +               NL_SET_ERR_MSG(extack, "Delete should not have dir attribute set");
> > +               return -ESRCH;
> > +       }
> > +
> > 
> > I am still trying to figure out netlink examples, including the ones you 
> > pointed out : rtnl_valid_dump_net_req, rtnl_net_valid_getid_req.
> 
> These do pretty much what you wrote. 

> 
> > I wonder if there is a way to specifiy rejeced attributes per method.
> >
> > may be there is  way to call nlmsg_parse_deprecated_strict()
> > with .type = NLA_REJECT.
> 
> For that, we'd have to separate the policies for each netlink
> command. Otherwise NLA_REJECT will reject the SA_DIR attribute for all
> commands, which is not what we want.
> 
> > And also this looks like a general cleanup up to me. I wonder how Steffen 
> > would add such a check for the upcoming PCPU attribute! Should that be 
> > prohibited DELSA or XFRM_MSG_FLUSHSA or DELSA?
> 
> IMO, new attributes should be rejected in any handler that doesn't use
> them. That's not a general cleanup because it's a new attribute, and
> the goal is to allow us to decide later if we want to use that
> attribute in DELSA etc. Maybe in one year, we want to make DELSA able
> to match on SA_DIR. If we don't reject SA_DIR from DELSA now, we won't
> be able to do that. That's why I'm insisting on this.

I have implemented a method to reject in v11, even though it is not my 
preference:) My argument xfrm has no precedence of limiting unused 
attributes in most types. We are not enforcing on all attributes such as 
upcoming PCPU. That is why I think it is a general clean up.

I have also tweaked error messages. Removed ESN from it, in v11 verifications
for ESN and non-ESN are similar. Since output SA with ESN allows replay window
0.

-antony
Sabrina Dubroca April 22, 2024, 9:16 a.m. UTC | #9
2024-04-22, 00:04:55 +0200, Antony Antony wrote:
> Hi Sabrina,
> 
> On Tue, Apr 16, 2024 at 10:36:16AM +0200, Sabrina Dubroca wrote:
> > 2024-04-16, 09:10:25 +0200, Antony Antony wrote:
> > > On Mon, Apr 15, 2024 at 02:21:50PM +0200, Sabrina Dubroca via Devel wrote:
> > > > 2024-04-11, 11:40:59 +0200, Antony Antony wrote:
> > > > > diff --git a/net/xfrm/xfrm_device.c b/net/xfrm/xfrm_device.c
> > > > > index 6346690d5c69..2455a76a1cff 100644
> > > > > --- a/net/xfrm/xfrm_device.c
> > > > > +++ b/net/xfrm/xfrm_device.c
> > > > > @@ -253,6 +253,12 @@ int xfrm_dev_state_add(struct net *net, struct xfrm_state *x,
> > > > >  		return -EINVAL;
> > > > >  	}
> > > > > 
> > > > > +	if ((xuo->flags & XFRM_OFFLOAD_INBOUND && x->dir == XFRM_SA_DIR_OUT) ||
> > > > > +	    (!(xuo->flags & XFRM_OFFLOAD_INBOUND) && x->dir == XFRM_SA_DIR_IN)) {
> > > > > +		NL_SET_ERR_MSG(extack, "Mismatched SA and offload direction");
> > > > > +		return -EINVAL;
> > > > > +	}
> > > > 
> > > > It would be nice to set x->dir to match the flag, but then I guess the
> > > > validation in xfrm_state_update would fail if userspaces tries an
> > > > update without providing XFRMA_SA_DIR. (or not because we already went
> > > > through this code by the time we get to xfrm_state_update?)
> > > 
> > > this code already executed from xfrm_state_construct.
> > > We could set the in flag in xuo when x->dir == XFRM_SA_DIR_IN, let me think 
> > > again.  May be we can do that later:)
> > 
> > I mean setting x->dir, not setting xuo, ie adding something like this
> > to xfrm_dev_state_add:
> > 
> >     x->dir = xuo->flags & XFRM_OFFLOAD_INBOUND ? XFRM_SA_DIR_IN : XFRM_SA_DIR_OUT;
> > 
> > xuo will already be set correctly when we're using offload, and won't
> > be present if we're not.
> 
> Updating with older tools may fail validation. For instance, if a user creates
> an SA using an older iproute2 with offload and XFRM_OFFLOAD_INBOUND flag 
> set, the kernel sets x->dir = XFRM_SA_DIR_IN. Then, if the user wants to 
> update this SA using the same older iproute2, which doesn't allow setting 
> dir, the update will fail.

I'm not sure it would, since as you said xfrm_state_construct would
have set x->dir based on XFRM_OFFLOAD_INBOUND. But if that's the case,
then that can be added later, because it would not change any behavior.

> However, as I proposed, if SA dir "in" and offload is enabled, the kernel
> could set xuo->flags &= XFRM_OFFLOAD_INBOUND to avoid double typing.

Do you mean in iproute?

On the kernel side, xuo has to be provided when offloading, and the
meaning of (xuo->flags & XFRM_OFFLOAD_INBOUND) is well defined (0 =
out, !0 = in). xuo->flags & XFRM_OFFLOAD_INBOUND == 0 with SA_DIR ==
IN must remain an invalid config.


> > > And also this looks like a general cleanup up to me. I wonder how Steffen 
> > > would add such a check for the upcoming PCPU attribute! Should that be 
> > > prohibited DELSA or XFRM_MSG_FLUSHSA or DELSA?
> > 
> > IMO, new attributes should be rejected in any handler that doesn't use
> > them. That's not a general cleanup because it's a new attribute, and
> > the goal is to allow us to decide later if we want to use that
> > attribute in DELSA etc. Maybe in one year, we want to make DELSA able
> > to match on SA_DIR. If we don't reject SA_DIR from DELSA now, we won't
> > be able to do that. That's why I'm insisting on this.
> 
> I have implemented a method to reject in v11, even though it is not my 
> preference:) My argument xfrm has no precedence of limiting unused 
> attributes in most types. We are not enforcing on all attributes such as 
> upcoming PCPU.

I'll ask Steffen to enforce it there as well :)
I think it's a mistake that old netlink APIs were too friendly to invalid input.
Nicolas Dichtel April 22, 2024, 9:54 a.m. UTC | #10
Le 22/04/2024 à 11:16, Sabrina Dubroca a écrit :
[snip]
>>>> And also this looks like a general cleanup up to me. I wonder how Steffen 
>>>> would add such a check for the upcoming PCPU attribute! Should that be 
>>>> prohibited DELSA or XFRM_MSG_FLUSHSA or DELSA?
>>>
>>> IMO, new attributes should be rejected in any handler that doesn't use
>>> them. That's not a general cleanup because it's a new attribute, and
>>> the goal is to allow us to decide later if we want to use that
>>> attribute in DELSA etc. Maybe in one year, we want to make DELSA able
>>> to match on SA_DIR. If we don't reject SA_DIR from DELSA now, we won't
>>> be able to do that. That's why I'm insisting on this.
>>
>> I have implemented a method to reject in v11, even though it is not my 
>> preference:) My argument xfrm has no precedence of limiting unused 
>> attributes in most types. We are not enforcing on all attributes such as 
>> upcoming PCPU.
> 
> I'll ask Steffen to enforce it there as well :)
> I think it's a mistake that old netlink APIs were too friendly to invalid input.
+1

This is an old problem in Netlink. There has been work during the last years to
be more strict about new attributes.

For example, see
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=56738f4608417
Antony Antony April 23, 2024, 12:42 p.m. UTC | #11
Hi Sabrina,
On Mon, Apr 22, 2024 at 11:16:31AM +0200, Sabrina Dubroca via Devel wrote:
> 2024-04-22, 00:04:55 +0200, Antony Antony wrote:
> > Hi Sabrina,
> > 
> > On Tue, Apr 16, 2024 at 10:36:16AM +0200, Sabrina Dubroca wrote:
> > > 2024-04-16, 09:10:25 +0200, Antony Antony wrote:
> > > > On Mon, Apr 15, 2024 at 02:21:50PM +0200, Sabrina Dubroca via Devel wrote:
> > > > > 2024-04-11, 11:40:59 +0200, Antony Antony wrote:
> > > > > > diff --git a/net/xfrm/xfrm_device.c b/net/xfrm/xfrm_device.c
> > > > > > index 6346690d5c69..2455a76a1cff 100644
> > > > > > --- a/net/xfrm/xfrm_device.c
> > > > > > +++ b/net/xfrm/xfrm_device.c
> > > > > > @@ -253,6 +253,12 @@ int xfrm_dev_state_add(struct net *net, struct xfrm_state *x,
> > > > > >  		return -EINVAL;
> > > > > >  	}
> > > > > > 
> > > > > > +	if ((xuo->flags & XFRM_OFFLOAD_INBOUND && x->dir == XFRM_SA_DIR_OUT) ||
> > > > > > +	    (!(xuo->flags & XFRM_OFFLOAD_INBOUND) && x->dir == XFRM_SA_DIR_IN)) {
> > > > > > +		NL_SET_ERR_MSG(extack, "Mismatched SA and offload direction");
> > > > > > +		return -EINVAL;
> > > > > > +	}
> > > > > 
> > > > > It would be nice to set x->dir to match the flag, but then I guess the
> > > > > validation in xfrm_state_update would fail if userspaces tries an
> > > > > update without providing XFRMA_SA_DIR. (or not because we already went
> > > > > through this code by the time we get to xfrm_state_update?)
> > > > 
> > > > this code already executed from xfrm_state_construct.
> > > > We could set the in flag in xuo when x->dir == XFRM_SA_DIR_IN, let me think 
> > > > again.  May be we can do that later:)
> > > 
> > > I mean setting x->dir, not setting xuo, ie adding something like this
> > > to xfrm_dev_state_add:
> > > 
> > >     x->dir = xuo->flags & XFRM_OFFLOAD_INBOUND ? XFRM_SA_DIR_IN : XFRM_SA_DIR_OUT;
> > > 
> > > xuo will already be set correctly when we're using offload, and won't
> > > be present if we're not.
> > 
> > Updating with older tools may fail validation. For instance, if a user creates
> > an SA using an older iproute2 with offload and XFRM_OFFLOAD_INBOUND flag 
> > set, the kernel sets x->dir = XFRM_SA_DIR_IN. Then, if the user wants to 
> > update this SA using the same older iproute2, which doesn't allow setting 
> > dir, the update will fail.
> 
> I'm not sure it would, since as you said xfrm_state_construct would
> have set x->dir based on XFRM_OFFLOAD_INBOUND. But if that's the case,
> then that can be added later, because it would not change any behavior.
> 
> > However, as I proposed, if SA dir "in" and offload is enabled, the kernel
> > could set xuo->flags &= XFRM_OFFLOAD_INBOUND to avoid double typing.
> 
> Do you mean in iproute?

I was thinking kernel. Then the API would be simple iproute2 or *swans.

ip xfrm state .... dir in offload dev mlx0 dir in

notice  "dir in" twice.  this can be tweaked later:) However, this would 
work only with newer kernels.

> 
> On the kernel side, xuo has to be provided when offloading, and the
> meaning of (xuo->flags & XFRM_OFFLOAD_INBOUND) is well defined (0 =
> out, !0 = in). xuo->flags & XFRM_OFFLOAD_INBOUND == 0 with SA_DIR ==
> IN must remain an invalid config.

yes I agree.
diff mbox series

Patch

diff --git a/include/net/xfrm.h b/include/net/xfrm.h
index 57c743b7e4fe..7c9be06f8302 100644
--- a/include/net/xfrm.h
+++ b/include/net/xfrm.h
@@ -291,6 +291,7 @@  struct xfrm_state {
 	/* Private data of this transformer, format is opaque,
 	 * interpreted by xfrm_type methods. */
 	void			*data;
+	u8			dir;
 };

 static inline struct net *xs_net(struct xfrm_state *x)
diff --git a/include/uapi/linux/xfrm.h b/include/uapi/linux/xfrm.h
index 6a77328be114..18ceaba8486e 100644
--- a/include/uapi/linux/xfrm.h
+++ b/include/uapi/linux/xfrm.h
@@ -141,6 +141,11 @@  enum {
 	XFRM_POLICY_MAX	= 3
 };

+enum xfrm_sa_dir {
+	XFRM_SA_DIR_IN	= 1,
+	XFRM_SA_DIR_OUT = 2
+};
+
 enum {
 	XFRM_SHARE_ANY,		/* No limitations */
 	XFRM_SHARE_SESSION,	/* For this session only */
@@ -315,6 +320,7 @@  enum xfrm_attr_type_t {
 	XFRMA_SET_MARK_MASK,	/* __u32 */
 	XFRMA_IF_ID,		/* __u32 */
 	XFRMA_MTIMER_THRESH,	/* __u32 in seconds for input SA */
+	XFRMA_SA_DIR,		/* __u8 */
 	__XFRMA_MAX

 #define XFRMA_OUTPUT_MARK XFRMA_SET_MARK	/* Compatibility */
diff --git a/net/xfrm/xfrm_compat.c b/net/xfrm/xfrm_compat.c
index 655fe4ff8621..007dee03b1bc 100644
--- a/net/xfrm/xfrm_compat.c
+++ b/net/xfrm/xfrm_compat.c
@@ -98,6 +98,7 @@  static const int compat_msg_min[XFRM_NR_MSGTYPES] = {
 };

 static const struct nla_policy compat_policy[XFRMA_MAX+1] = {
+	[XFRMA_UNSPEC]          = { .strict_start_type = XFRMA_SA_DIR },
 	[XFRMA_SA]		= { .len = XMSGSIZE(compat_xfrm_usersa_info)},
 	[XFRMA_POLICY]		= { .len = XMSGSIZE(compat_xfrm_userpolicy_info)},
 	[XFRMA_LASTUSED]	= { .type = NLA_U64},
@@ -129,6 +130,7 @@  static const struct nla_policy compat_policy[XFRMA_MAX+1] = {
 	[XFRMA_SET_MARK_MASK]	= { .type = NLA_U32 },
 	[XFRMA_IF_ID]		= { .type = NLA_U32 },
 	[XFRMA_MTIMER_THRESH]	= { .type = NLA_U32 },
+	[XFRMA_SA_DIR]          = { .type = NLA_U8}
 };

 static struct nlmsghdr *xfrm_nlmsg_put_compat(struct sk_buff *skb,
@@ -277,9 +279,10 @@  static int xfrm_xlate64_attr(struct sk_buff *dst, const struct nlattr *src)
 	case XFRMA_SET_MARK_MASK:
 	case XFRMA_IF_ID:
 	case XFRMA_MTIMER_THRESH:
+	case XFRMA_SA_DIR:
 		return xfrm_nla_cpy(dst, src, nla_len(src));
 	default:
-		BUILD_BUG_ON(XFRMA_MAX != XFRMA_MTIMER_THRESH);
+		BUILD_BUG_ON(XFRMA_MAX != XFRMA_SA_DIR);
 		pr_warn_once("unsupported nla_type %d\n", src->nla_type);
 		return -EOPNOTSUPP;
 	}
@@ -434,7 +437,7 @@  static int xfrm_xlate32_attr(void *dst, const struct nlattr *nla,
 	int err;

 	if (type > XFRMA_MAX) {
-		BUILD_BUG_ON(XFRMA_MAX != XFRMA_MTIMER_THRESH);
+		BUILD_BUG_ON(XFRMA_MAX != XFRMA_SA_DIR);
 		NL_SET_ERR_MSG(extack, "Bad attribute");
 		return -EOPNOTSUPP;
 	}
diff --git a/net/xfrm/xfrm_device.c b/net/xfrm/xfrm_device.c
index 6346690d5c69..2455a76a1cff 100644
--- a/net/xfrm/xfrm_device.c
+++ b/net/xfrm/xfrm_device.c
@@ -253,6 +253,12 @@  int xfrm_dev_state_add(struct net *net, struct xfrm_state *x,
 		return -EINVAL;
 	}

+	if ((xuo->flags & XFRM_OFFLOAD_INBOUND && x->dir == XFRM_SA_DIR_OUT) ||
+	    (!(xuo->flags & XFRM_OFFLOAD_INBOUND) && x->dir == XFRM_SA_DIR_IN)) {
+		NL_SET_ERR_MSG(extack, "Mismatched SA and offload direction");
+		return -EINVAL;
+	}
+
 	is_packet_offload = xuo->flags & XFRM_OFFLOAD_PACKET;

 	/* We don't yet support UDP encapsulation and TFC padding. */
diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
index 0c306473a79d..f7771a69ae2e 100644
--- a/net/xfrm/xfrm_state.c
+++ b/net/xfrm/xfrm_state.c
@@ -1744,6 +1744,7 @@  static struct xfrm_state *xfrm_state_clone(struct xfrm_state *orig,
 	x->lastused = orig->lastused;
 	x->new_mapping = 0;
 	x->new_mapping_sport = 0;
+	x->dir = orig->dir;

 	return x;

@@ -1857,6 +1858,9 @@  int xfrm_state_update(struct xfrm_state *x)
 	if (!x1)
 		goto out;

+	if (x1->dir != x->dir)
+		goto out;
+
 	if (xfrm_state_kern(x1)) {
 		to_put = x1;
 		err = -EEXIST;
diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c
index 810b520493f3..df141edbe8d1 100644
--- a/net/xfrm/xfrm_user.c
+++ b/net/xfrm/xfrm_user.c
@@ -360,6 +360,16 @@  static int verify_newsa_info(struct xfrm_usersa_info *p,
 		}
 	}

+	if (attrs[XFRMA_SA_DIR]) {
+		u8 sa_dir = nla_get_u8(attrs[XFRMA_SA_DIR]);
+
+		if (sa_dir != XFRM_SA_DIR_IN && sa_dir != XFRM_SA_DIR_OUT)  {
+			NL_SET_ERR_MSG(extack, "XFRMA_SA_DIR attribute is out of range");
+			err = -EINVAL;
+			goto out;
+		}
+	}
+
 out:
 	return err;
 }
@@ -627,6 +637,7 @@  static void xfrm_update_ae_params(struct xfrm_state *x, struct nlattr **attrs,
 	struct nlattr *et = attrs[XFRMA_ETIMER_THRESH];
 	struct nlattr *rt = attrs[XFRMA_REPLAY_THRESH];
 	struct nlattr *mt = attrs[XFRMA_MTIMER_THRESH];
+	struct nlattr *dir = attrs[XFRMA_SA_DIR];

 	if (re && x->replay_esn && x->preplay_esn) {
 		struct xfrm_replay_state_esn *replay_esn;
@@ -661,6 +672,9 @@  static void xfrm_update_ae_params(struct xfrm_state *x, struct nlattr **attrs,

 	if (mt)
 		x->mapping_maxage = nla_get_u32(mt);
+
+	if (dir)
+		x->dir = nla_get_u8(dir);
 }

 static void xfrm_smark_init(struct nlattr **attrs, struct xfrm_mark *m)
@@ -779,6 +793,77 @@  static struct xfrm_state *xfrm_state_construct(struct net *net,
 	return NULL;
 }

+static int verify_sa_dir(const struct xfrm_state *x, struct netlink_ext_ack *extack)
+{
+	if (x->dir == XFRM_SA_DIR_OUT)  {
+		if (x->props.replay_window > 0) {
+			NL_SET_ERR_MSG(extack, "Replay window should not be set for OUT SA");
+			return -EINVAL;
+		}
+
+		if (x->replay.seq || x->replay.bitmap) {
+			NL_SET_ERR_MSG(extack,
+				       "Replay seq, or bitmap should not be set for OUT SA with ESN");
+			return -EINVAL;
+		}
+
+		if (x->replay_esn) {
+			if (x->replay_esn->replay_window > 1) {
+				NL_SET_ERR_MSG(extack,
+					       "Replay window should be 1 for OUT SA with ESN");
+				return -EINVAL;
+			}
+
+			if (x->replay_esn->seq || x->replay_esn->seq_hi || x->replay_esn->bmp_len) {
+				NL_SET_ERR_MSG(extack,
+					       "Replay seq, seq_hi, bmp_len should not be set for OUT SA with ESN");
+				return -EINVAL;
+			}
+		}
+
+		if (x->props.flags & XFRM_STATE_DECAP_DSCP) {
+			NL_SET_ERR_MSG(extack, "Flag NDECAP_DSCP should not be set for OUT SA");
+			return -EINVAL;
+		}
+
+		if (x->props.flags & XFRM_STATE_ICMP) {
+			NL_SET_ERR_MSG(extack, "Flag ICMP should not be set for OUT SA");
+			return -EINVAL;
+		}
+
+		if (x->mapping_maxage) {
+			NL_SET_ERR_MSG(extack, "MTIMER_THRESH should not be set for OUT SA");
+			return -EINVAL;
+		}
+	} else {
+		if (x->replay.oseq) {
+			NL_SET_ERR_MSG(extack,
+				       "Replay oseq should not be set for IN SA");
+			return -EINVAL;
+		}
+
+		if (x->replay_esn) {
+			if (x->replay_esn->oseq || x->replay_esn->oseq_hi) {
+				NL_SET_ERR_MSG(extack,
+					       "Replay oseq and oseq_hi should not be set for IN SA with ESN");
+				return -EINVAL;
+			}
+		}
+
+		if (x->props.flags & XFRM_STATE_NOPMTUDISC) {
+			NL_SET_ERR_MSG(extack, "Flag NOPMTUDISC should not be set for IN SA");
+			return -EINVAL;
+		}
+
+		if (x->xflags & XFRM_SA_XFLAG_DONT_ENCAP_DSCP) {
+			NL_SET_ERR_MSG(extack, "Flag DONT_ENCAP_DSCP should not be set for IN SA");
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
 static int xfrm_add_sa(struct sk_buff *skb, struct nlmsghdr *nlh,
 		       struct nlattr **attrs, struct netlink_ext_ack *extack)
 {
@@ -796,6 +881,16 @@  static int xfrm_add_sa(struct sk_buff *skb, struct nlmsghdr *nlh,
 	if (!x)
 		return err;

+	if (x->dir) {
+		err = verify_sa_dir(x, extack);
+		if (err) {
+			x->km.state = XFRM_STATE_DEAD;
+			xfrm_dev_state_delete(x);
+			xfrm_state_put(x);
+			return err;
+		}
+	}
+
 	xfrm_state_hold(x);
 	if (nlh->nlmsg_type == XFRM_MSG_NEWSA)
 		err = xfrm_state_add(x);
@@ -1182,8 +1277,13 @@  static int copy_to_user_state_extra(struct xfrm_state *x,
 		if (ret)
 			goto out;
 	}
-	if (x->mapping_maxage)
+	if (x->mapping_maxage) {
 		ret = nla_put_u32(skb, XFRMA_MTIMER_THRESH, x->mapping_maxage);
+		if (ret)
+			goto out;
+	}
+	if (x->dir)
+		ret = nla_put_u8(skb, XFRMA_SA_DIR, x->dir);
 out:
 	return ret;
 }
@@ -1579,12 +1679,22 @@  static int xfrm_alloc_userspi(struct sk_buff *skb, struct nlmsghdr *nlh,
 	u32 mark;
 	struct xfrm_mark m;
 	u32 if_id = 0;
+	u8 sa_dir = 0;

 	p = nlmsg_data(nlh);
 	err = verify_spi_info(p->info.id.proto, p->min, p->max, extack);
 	if (err)
 		goto out_noput;

+	if (attrs[XFRMA_SA_DIR]) {
+		sa_dir = nla_get_u8(attrs[XFRMA_SA_DIR]);
+		if (sa_dir != XFRM_SA_DIR_IN && sa_dir != XFRM_SA_DIR_OUT)  {
+			NL_SET_ERR_MSG(extack, "XFRMA_SA_DIR attribute is out of range");
+			err = -EINVAL;
+			goto out_noput;
+		}
+	}
+
 	family = p->info.family;
 	daddr = &p->info.id.daddr;

@@ -1618,6 +1728,8 @@  static int xfrm_alloc_userspi(struct sk_buff *skb, struct nlmsghdr *nlh,
 	if (err)
 		goto out;

+	x->dir = sa_dir;
+
 	resp_skb = xfrm_state_netlink(skb, x, nlh->nlmsg_seq);
 	if (IS_ERR(resp_skb)) {
 		err = PTR_ERR(resp_skb);
@@ -2402,7 +2514,8 @@  static inline unsigned int xfrm_aevent_msgsize(struct xfrm_state *x)
 	       + nla_total_size_64bit(sizeof(struct xfrm_lifetime_cur))
 	       + nla_total_size(sizeof(struct xfrm_mark))
 	       + nla_total_size(4) /* XFRM_AE_RTHR */
-	       + nla_total_size(4); /* XFRM_AE_ETHR */
+	       + nla_total_size(4) /* XFRM_AE_ETHR */
+	       + nla_total_size(sizeof(x->dir)); /* XFRMA_SA_DIR */
 }

 static int build_aevent(struct sk_buff *skb, struct xfrm_state *x, const struct km_event *c)
@@ -2459,6 +2572,12 @@  static int build_aevent(struct sk_buff *skb, struct xfrm_state *x, const struct
 	if (err)
 		goto out_cancel;

+	if (x->dir) {
+		err = nla_put_u8(skb, XFRMA_SA_DIR, x->dir);
+		if (err)
+			goto out_cancel;
+	}
+
 	nlmsg_end(skb, nlh);
 	return 0;

@@ -3018,6 +3137,7 @@  EXPORT_SYMBOL_GPL(xfrm_msg_min);
 #undef XMSGSIZE

 const struct nla_policy xfrma_policy[XFRMA_MAX+1] = {
+	[XFRMA_UNSPEC]		= { .strict_start_type = XFRMA_SA_DIR },
 	[XFRMA_SA]		= { .len = sizeof(struct xfrm_usersa_info)},
 	[XFRMA_POLICY]		= { .len = sizeof(struct xfrm_userpolicy_info)},
 	[XFRMA_LASTUSED]	= { .type = NLA_U64},
@@ -3049,6 +3169,7 @@  const struct nla_policy xfrma_policy[XFRMA_MAX+1] = {
 	[XFRMA_SET_MARK_MASK]	= { .type = NLA_U32 },
 	[XFRMA_IF_ID]		= { .type = NLA_U32 },
 	[XFRMA_MTIMER_THRESH]   = { .type = NLA_U32 },
+	[XFRMA_SA_DIR]          = { .type = NLA_U8 }
 };
 EXPORT_SYMBOL_GPL(xfrma_policy);

@@ -3189,8 +3310,9 @@  static void xfrm_netlink_rcv(struct sk_buff *skb)

 static inline unsigned int xfrm_expire_msgsize(void)
 {
-	return NLMSG_ALIGN(sizeof(struct xfrm_user_expire))
-	       + nla_total_size(sizeof(struct xfrm_mark));
+	return NLMSG_ALIGN(sizeof(struct xfrm_user_expire)) +
+	       nla_total_size(sizeof(struct xfrm_mark)) +
+	       nla_total_size(sizeof_field(struct xfrm_state, dir));
 }

 static int build_expire(struct sk_buff *skb, struct xfrm_state *x, const struct km_event *c)
@@ -3217,6 +3339,12 @@  static int build_expire(struct sk_buff *skb, struct xfrm_state *x, const struct
 	if (err)
 		return err;

+	if (x->dir) {
+		err = nla_put_u8(skb, XFRMA_SA_DIR, x->dir);
+		if (err)
+			return err;
+	}
+
 	nlmsg_end(skb, nlh);
 	return 0;
 }
@@ -3324,6 +3452,9 @@  static inline unsigned int xfrm_sa_len(struct xfrm_state *x)
 	if (x->mapping_maxage)
 		l += nla_total_size(sizeof(x->mapping_maxage));

+	if (x->dir)
+		l += nla_total_size(sizeof(x->dir));
+
 	return l;
 }