diff mbox series

[RFC,nf-next] Introducing stateful object update operation

Message ID 20190806102945.728-1-ffmancera@riseup.net
State RFC
Delegated to: Pablo Neira
Headers show
Series [RFC,nf-next] Introducing stateful object update operation | expand

Commit Message

Fernando F. Mancera Aug. 6, 2019, 10:29 a.m. UTC
I have been thinking of a way to update a quota object. i.e raise or lower the
quota limit of an existing object. I think it would be ideal to implement the
operations of updating objects in the API in a generic way.

Therefore, we could easily give update support to each object type by adding an
update operation in the "nft_object_ops" struct. This is a conceptual patch so
it does not work.

Signed-off-by: Fernando Fernandez Mancera <ffmancera@riseup.net>
---
 include/net/netfilter/nf_tables.h        |  4 ++++
 include/uapi/linux/netfilter/nf_tables.h |  2 ++
 net/netfilter/nf_tables_api.c            | 22 ++++++++++++++++++++++
 3 files changed, 28 insertions(+)

Comments

Florian Westphal Aug. 6, 2019, 10:44 a.m. UTC | #1
Fernando Fernandez Mancera <ffmancera@riseup.net> wrote:
> I have been thinking of a way to update a quota object. i.e raise or lower the
> quota limit of an existing object. I think it would be ideal to implement the
> operations of updating objects in the API in a generic way.
> 
> Therefore, we could easily give update support to each object type by adding an
> update operation in the "nft_object_ops" struct. This is a conceptual patch so
> it does not work.
> 
> Signed-off-by: Fernando Fernandez Mancera <ffmancera@riseup.net>
> ---
>  include/net/netfilter/nf_tables.h        |  4 ++++
>  include/uapi/linux/netfilter/nf_tables.h |  2 ++
>  net/netfilter/nf_tables_api.c            | 22 ++++++++++++++++++++++
>  3 files changed, 28 insertions(+)
> 
> diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h
> index 9b624566b82d..bd1e6c19d23f 100644
> --- a/include/net/netfilter/nf_tables.h
> +++ b/include/net/netfilter/nf_tables.h
> @@ -1101,6 +1101,7 @@ struct nft_object_type {
>   *	@eval: stateful object evaluation function
>   *	@size: stateful object size
>   *	@init: initialize object from netlink attributes
> + *	@update: update object from netlink attributes
>   *	@destroy: release existing stateful object
>   *	@dump: netlink dump stateful object
>   */
> @@ -1112,6 +1113,9 @@ struct nft_object_ops {
>  	int				(*init)(const struct nft_ctx *ctx,
>  						const struct nlattr *const tb[],
>  						struct nft_object *obj);
> +	int				(*update)(const struct nft_ctx *ctx,
> +						  const struct nlattr *const tb[],
> +						  struct nft_object *obj);
>  	void				(*destroy)(const struct nft_ctx *ctx,
>  						   struct nft_object *obj);
>  	int				(*dump)(struct sk_buff *skb,
> diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h
> index 82abaa183fc3..8b0a012e9177 100644
> --- a/include/uapi/linux/netfilter/nf_tables.h
> +++ b/include/uapi/linux/netfilter/nf_tables.h
> @@ -92,6 +92,7 @@ enum nft_verdicts {
>   * @NFT_MSG_NEWOBJ: create a stateful object (enum nft_obj_attributes)
>   * @NFT_MSG_GETOBJ: get a stateful object (enum nft_obj_attributes)
>   * @NFT_MSG_DELOBJ: delete a stateful object (enum nft_obj_attributes)
> + * @NFT_MSG_UPDOBJ: update a stateful object (enum nft_obj_attributes)
>   * @NFT_MSG_GETOBJ_RESET: get and reset a stateful object (enum nft_obj_attributes)
>   * @NFT_MSG_NEWFLOWTABLE: add new flow table (enum nft_flowtable_attributes)
>   * @NFT_MSG_GETFLOWTABLE: get flow table (enum nft_flowtable_attributes)
> @@ -119,6 +120,7 @@ enum nf_tables_msg_types {
>  	NFT_MSG_NEWOBJ,
>  	NFT_MSG_GETOBJ,
>  	NFT_MSG_DELOBJ,
> +	NFT_MSG_UPDOBJ,

This breaks ABI, new enums need to be added at the end.

But I wonder if we can't just re-use NEWOBJ and teach it to update
the object if it exists already.

Userspace can already pass EXCL flag to bail out for the 'exists' case.

I agree that such feature (object update) is a good idea.
Pablo Neira Ayuso Aug. 6, 2019, 11:06 a.m. UTC | #2
On Tue, Aug 06, 2019 at 12:29:45PM +0200, Fernando Fernandez Mancera wrote:
> I have been thinking of a way to update a quota object. i.e raise or lower the
> quota limit of an existing object. I think it would be ideal to implement the
> operations of updating objects in the API in a generic way.
> 
> Therefore, we could easily give update support to each object type by adding an
> update operation in the "nft_object_ops" struct. This is a conceptual patch so
> it does not work.
> 
> Signed-off-by: Fernando Fernandez Mancera <ffmancera@riseup.net>
> ---
>  include/net/netfilter/nf_tables.h        |  4 ++++
>  include/uapi/linux/netfilter/nf_tables.h |  2 ++
>  net/netfilter/nf_tables_api.c            | 22 ++++++++++++++++++++++
>  3 files changed, 28 insertions(+)
> 
> diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h
> index 9b624566b82d..bd1e6c19d23f 100644
> --- a/include/net/netfilter/nf_tables.h
> +++ b/include/net/netfilter/nf_tables.h
> @@ -1101,6 +1101,7 @@ struct nft_object_type {
>   *	@eval: stateful object evaluation function
>   *	@size: stateful object size
>   *	@init: initialize object from netlink attributes
> + *	@update: update object from netlink attributes
>   *	@destroy: release existing stateful object
>   *	@dump: netlink dump stateful object
>   */
> @@ -1112,6 +1113,9 @@ struct nft_object_ops {
>  	int				(*init)(const struct nft_ctx *ctx,
>  						const struct nlattr *const tb[],
>  						struct nft_object *obj);
> +	int				(*update)(const struct nft_ctx *ctx,
> +						  const struct nlattr *const tb[],
> +						  struct nft_object *obj);
>  	void				(*destroy)(const struct nft_ctx *ctx,
>  						   struct nft_object *obj);
>  	int				(*dump)(struct sk_buff *skb,
> diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h
> index 82abaa183fc3..8b0a012e9177 100644
> --- a/include/uapi/linux/netfilter/nf_tables.h
> +++ b/include/uapi/linux/netfilter/nf_tables.h
> @@ -92,6 +92,7 @@ enum nft_verdicts {
>   * @NFT_MSG_NEWOBJ: create a stateful object (enum nft_obj_attributes)
>   * @NFT_MSG_GETOBJ: get a stateful object (enum nft_obj_attributes)
>   * @NFT_MSG_DELOBJ: delete a stateful object (enum nft_obj_attributes)
> + * @NFT_MSG_UPDOBJ: update a stateful object (enum nft_obj_attributes)
>   * @NFT_MSG_GETOBJ_RESET: get and reset a stateful object (enum nft_obj_attributes)
>   * @NFT_MSG_NEWFLOWTABLE: add new flow table (enum nft_flowtable_attributes)
>   * @NFT_MSG_GETFLOWTABLE: get flow table (enum nft_flowtable_attributes)
> @@ -119,6 +120,7 @@ enum nf_tables_msg_types {
>  	NFT_MSG_NEWOBJ,
>  	NFT_MSG_GETOBJ,
>  	NFT_MSG_DELOBJ,
> +	NFT_MSG_UPDOBJ,

No need for this new message type, see below.

>  	NFT_MSG_GETOBJ_RESET,
>  	NFT_MSG_NEWFLOWTABLE,
>  	NFT_MSG_GETFLOWTABLE,
> diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
> index 605a7cfe7ca7..c7267f418808 100644
> --- a/net/netfilter/nf_tables_api.c
> +++ b/net/netfilter/nf_tables_api.c
> @@ -5420,6 +5420,16 @@ static void nft_obj_destroy(const struct nft_ctx *ctx, struct nft_object *obj)
>  	kfree(obj);
>  }
>  
> +static int nf_tables_updobj(struct net *net, struct sock *nlsk,
> +			    struct sk_buff *skb, const struct nlmsghdr *nlh,
> +			    const struct nlattr * const nla[],
> +			    struct netlink_ext_ack *extack)
> +{
> +	/* Placeholder function, here we would need to check if the object
> +	 * exists. Then init the context and update the object.*/
> +	return 1;

Use the existing nf_tables_newobj(), if NLM_F_EXCL is not set on and
the object exists, then this is an update.
Fernando F. Mancera Aug. 6, 2019, 1:40 p.m. UTC | #3
On 8/6/19 1:06 PM, Pablo Neira Ayuso wrote:
> On Tue, Aug 06, 2019 at 12:29:45PM +0200, Fernando Fernandez Mancera wrote:
>> I have been thinking of a way to update a quota object. i.e raise or lower the
>> quota limit of an existing object. I think it would be ideal to implement the
>> operations of updating objects in the API in a generic way.
>>
>> Therefore, we could easily give update support to each object type by adding an
>> update operation in the "nft_object_ops" struct. This is a conceptual patch so
>> it does not work.
>>
>> Signed-off-by: Fernando Fernandez Mancera <ffmancera@riseup.net>
>> ---
>>  include/net/netfilter/nf_tables.h        |  4 ++++
>>  include/uapi/linux/netfilter/nf_tables.h |  2 ++
>>  net/netfilter/nf_tables_api.c            | 22 ++++++++++++++++++++++
>>  3 files changed, 28 insertions(+)
>>
>> diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h
>> index 9b624566b82d..bd1e6c19d23f 100644
>> --- a/include/net/netfilter/nf_tables.h
>> +++ b/include/net/netfilter/nf_tables.h
>> @@ -1101,6 +1101,7 @@ struct nft_object_type {
>>   *	@eval: stateful object evaluation function
>>   *	@size: stateful object size
>>   *	@init: initialize object from netlink attributes
>> + *	@update: update object from netlink attributes
>>   *	@destroy: release existing stateful object
>>   *	@dump: netlink dump stateful object
>>   */
>> @@ -1112,6 +1113,9 @@ struct nft_object_ops {
>>  	int				(*init)(const struct nft_ctx *ctx,
>>  						const struct nlattr *const tb[],
>>  						struct nft_object *obj);
>> +	int				(*update)(const struct nft_ctx *ctx,
>> +						  const struct nlattr *const tb[],
>> +						  struct nft_object *obj);
>>  	void				(*destroy)(const struct nft_ctx *ctx,
>>  						   struct nft_object *obj);
>>  	int				(*dump)(struct sk_buff *skb,
>> diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h
>> index 82abaa183fc3..8b0a012e9177 100644
>> --- a/include/uapi/linux/netfilter/nf_tables.h
>> +++ b/include/uapi/linux/netfilter/nf_tables.h
>> @@ -92,6 +92,7 @@ enum nft_verdicts {
>>   * @NFT_MSG_NEWOBJ: create a stateful object (enum nft_obj_attributes)
>>   * @NFT_MSG_GETOBJ: get a stateful object (enum nft_obj_attributes)
>>   * @NFT_MSG_DELOBJ: delete a stateful object (enum nft_obj_attributes)
>> + * @NFT_MSG_UPDOBJ: update a stateful object (enum nft_obj_attributes)
>>   * @NFT_MSG_GETOBJ_RESET: get and reset a stateful object (enum nft_obj_attributes)
>>   * @NFT_MSG_NEWFLOWTABLE: add new flow table (enum nft_flowtable_attributes)
>>   * @NFT_MSG_GETFLOWTABLE: get flow table (enum nft_flowtable_attributes)
>> @@ -119,6 +120,7 @@ enum nf_tables_msg_types {
>>  	NFT_MSG_NEWOBJ,
>>  	NFT_MSG_GETOBJ,
>>  	NFT_MSG_DELOBJ,
>> +	NFT_MSG_UPDOBJ,
> 
> No need for this new message type, see below.
> 
>>  	NFT_MSG_GETOBJ_RESET,
>>  	NFT_MSG_NEWFLOWTABLE,
>>  	NFT_MSG_GETFLOWTABLE,
>> diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
>> index 605a7cfe7ca7..c7267f418808 100644
>> --- a/net/netfilter/nf_tables_api.c
>> +++ b/net/netfilter/nf_tables_api.c
>> @@ -5420,6 +5420,16 @@ static void nft_obj_destroy(const struct nft_ctx *ctx, struct nft_object *obj)
>>  	kfree(obj);
>>  }
>>  
>> +static int nf_tables_updobj(struct net *net, struct sock *nlsk,
>> +			    struct sk_buff *skb, const struct nlmsghdr *nlh,
>> +			    const struct nlattr * const nla[],
>> +			    struct netlink_ext_ack *extack)
>> +{
>> +	/* Placeholder function, here we would need to check if the object
>> +	 * exists. Then init the context and update the object.*/
>> +	return 1;
> 
> Use the existing nf_tables_newobj(), if NLM_F_EXCL is not set on and
> the object exists, then this is an update.
> 

I agree on that. But I think that if we use the NFT_MSG_NEWOBJ there
will be some issues in the commit and the abort phase. That is why I
think "NFT_MSG_UPDOBJ" would be needed.

Thanks!
Florian Westphal Aug. 6, 2019, 1:48 p.m. UTC | #4
Fernando Fernandez Mancera <ffmancera@riseup.net> wrote:
> > Use the existing nf_tables_newobj(), if NLM_F_EXCL is not set on and
> > the object exists, then this is an update.
> 
> I agree on that. But I think that if we use the NFT_MSG_NEWOBJ there
> will be some issues in the commit and the abort phase. That is why I
> think "NFT_MSG_UPDOBJ" would be needed.

See e.g. 'nft_trans_table_update()' -- we already do this for
other structures/entities.  You would need to extend the object handling
to not remove an already-existed-object in case of an update if an abort
is triggered.
diff mbox series

Patch

diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h
index 9b624566b82d..bd1e6c19d23f 100644
--- a/include/net/netfilter/nf_tables.h
+++ b/include/net/netfilter/nf_tables.h
@@ -1101,6 +1101,7 @@  struct nft_object_type {
  *	@eval: stateful object evaluation function
  *	@size: stateful object size
  *	@init: initialize object from netlink attributes
+ *	@update: update object from netlink attributes
  *	@destroy: release existing stateful object
  *	@dump: netlink dump stateful object
  */
@@ -1112,6 +1113,9 @@  struct nft_object_ops {
 	int				(*init)(const struct nft_ctx *ctx,
 						const struct nlattr *const tb[],
 						struct nft_object *obj);
+	int				(*update)(const struct nft_ctx *ctx,
+						  const struct nlattr *const tb[],
+						  struct nft_object *obj);
 	void				(*destroy)(const struct nft_ctx *ctx,
 						   struct nft_object *obj);
 	int				(*dump)(struct sk_buff *skb,
diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h
index 82abaa183fc3..8b0a012e9177 100644
--- a/include/uapi/linux/netfilter/nf_tables.h
+++ b/include/uapi/linux/netfilter/nf_tables.h
@@ -92,6 +92,7 @@  enum nft_verdicts {
  * @NFT_MSG_NEWOBJ: create a stateful object (enum nft_obj_attributes)
  * @NFT_MSG_GETOBJ: get a stateful object (enum nft_obj_attributes)
  * @NFT_MSG_DELOBJ: delete a stateful object (enum nft_obj_attributes)
+ * @NFT_MSG_UPDOBJ: update a stateful object (enum nft_obj_attributes)
  * @NFT_MSG_GETOBJ_RESET: get and reset a stateful object (enum nft_obj_attributes)
  * @NFT_MSG_NEWFLOWTABLE: add new flow table (enum nft_flowtable_attributes)
  * @NFT_MSG_GETFLOWTABLE: get flow table (enum nft_flowtable_attributes)
@@ -119,6 +120,7 @@  enum nf_tables_msg_types {
 	NFT_MSG_NEWOBJ,
 	NFT_MSG_GETOBJ,
 	NFT_MSG_DELOBJ,
+	NFT_MSG_UPDOBJ,
 	NFT_MSG_GETOBJ_RESET,
 	NFT_MSG_NEWFLOWTABLE,
 	NFT_MSG_GETFLOWTABLE,
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
index 605a7cfe7ca7..c7267f418808 100644
--- a/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -5420,6 +5420,16 @@  static void nft_obj_destroy(const struct nft_ctx *ctx, struct nft_object *obj)
 	kfree(obj);
 }
 
+static int nf_tables_updobj(struct net *net, struct sock *nlsk,
+			    struct sk_buff *skb, const struct nlmsghdr *nlh,
+			    const struct nlattr * const nla[],
+			    struct netlink_ext_ack *extack)
+{
+	/* Placeholder function, here we would need to check if the object
+	 * exists. Then init the context and update the object.*/
+	return 1;
+}
+
 static int nf_tables_delobj(struct net *net, struct sock *nlsk,
 			    struct sk_buff *skb, const struct nlmsghdr *nlh,
 			    const struct nlattr * const nla[],
@@ -6323,6 +6333,11 @@  static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = {
 		.attr_count	= NFTA_OBJ_MAX,
 		.policy		= nft_obj_policy,
 	},
+	[NFT_MSG_UPDOBJ] = {
+		.call_rcu	= nf_tables_updobj,
+		.attr_count	= NFTA_OBJ_MAX,
+		.policy		= nft_obj_policy,
+	},
 	[NFT_MSG_DELOBJ] = {
 		.call_batch	= nf_tables_delobj,
 		.attr_count	= NFTA_OBJ_MAX,
@@ -6791,6 +6806,10 @@  static int nf_tables_commit(struct net *net, struct sk_buff *skb)
 					     NFT_MSG_NEWOBJ);
 			nft_trans_destroy(trans);
 			break;
+		case NFT_MSG_UPDOBJ:
+			nf_tables_obj_notify(&trans->ctx, nft_trans_obj(trans),
+					     NFT_MSG_UPDOBJ);
+			break;
 		case NFT_MSG_DELOBJ:
 			nft_obj_del(nft_trans_obj(trans));
 			nf_tables_obj_notify(&trans->ctx, nft_trans_obj(trans),
@@ -6939,6 +6958,9 @@  static int __nf_tables_abort(struct net *net)
 			trans->ctx.table->use--;
 			nft_obj_del(nft_trans_obj(trans));
 			break;
+		case NFT_MSG_UPDOBJ:
+			nft_trans_destroy(trans);
+			break;
 		case NFT_MSG_DELOBJ:
 			trans->ctx.table->use++;
 			nft_clear(trans->ctx.net, nft_trans_obj(trans));