diff mbox series

[net-next,v8,03/24] ovpn: add basic netlink support

Message ID 20241002-b4-ovpn-v8-3-37ceffcffbde@openvpn.net (mailing list archive)
State New
Headers show
Series Introducing OpenVPN Data Channel Offload | expand

Commit Message

Antonio Quartulli Oct. 2, 2024, 9:02 a.m. UTC
This commit introduces basic netlink support with family
registration/unregistration functionalities and stub pre/post-doit.

More importantly it introduces the YAML uAPI description along
with its auto-generated files:
- include/uapi/linux/ovpn.h
- drivers/net/ovpn/netlink-gen.c
- drivers/net/ovpn/netlink-gen.h

Cc: donald.hunter@gmail.com
Signed-off-by: Antonio Quartulli <antonio@openvpn.net>
---
 Documentation/netlink/specs/ovpn.yaml | 387 ++++++++++++++++++++++++++++++++++
 MAINTAINERS                           |   2 +
 drivers/net/ovpn/Makefile             |   2 +
 drivers/net/ovpn/main.c               |  15 +-
 drivers/net/ovpn/netlink-gen.c        | 224 ++++++++++++++++++++
 drivers/net/ovpn/netlink-gen.h        |  42 ++++
 drivers/net/ovpn/netlink.c            | 158 ++++++++++++++
 drivers/net/ovpn/netlink.h            |  15 ++
 drivers/net/ovpn/ovpnstruct.h         |  21 ++
 include/uapi/linux/ovpn.h             | 116 ++++++++++
 10 files changed, 981 insertions(+), 1 deletion(-)

Comments

kernel test robot Oct. 2, 2024, 2:13 p.m. UTC | #1
Hi Antonio,

kernel test robot noticed the following build warnings:

[auto build test WARNING on 44badc908f2c85711cb18e45e13119c10ad3a05f]

url:    https://github.com/intel-lab-lkp/linux/commits/Antonio-Quartulli/netlink-add-NLA_POLICY_MAX_LEN-macro/20241002-172734
base:   44badc908f2c85711cb18e45e13119c10ad3a05f
patch link:    https://lore.kernel.org/r/20241002-b4-ovpn-v8-3-37ceffcffbde%40openvpn.net
patch subject: [PATCH net-next v8 03/24] ovpn: add basic netlink support
reproduce: (https://download.01.org/0day-ci/archive/20241002/202410022156.mxbRG3on-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202410022156.mxbRG3on-lkp@intel.com/

All warnings (new ones prefixed by >>):

   Warning: Documentation/devicetree/bindings/regulator/siliconmitus,sm5703-regulator.yaml references a file that doesn't exist: Documentation/devicetree/bindings/mfd/siliconmitus,sm5703.yaml
   Warning: Documentation/hwmon/g762.rst references a file that doesn't exist: Documentation/devicetree/bindings/hwmon/g762.txt
   Warning: MAINTAINERS references a file that doesn't exist: Documentation/devicetree/bindings/reserved-memory/qcom
>> Warning: MAINTAINERS references a file that doesn't exist: Documentation/netlink/spec/ovpn.yaml
   Warning: MAINTAINERS references a file that doesn't exist: Documentation/devicetree/bindings/misc/fsl,qoriq-mc.txt
   Using alabaster theme
Donald Hunter Oct. 4, 2024, 4:13 p.m. UTC | #2
On Wed, 2 Oct 2024 at 10:03, Antonio Quartulli <antonio@openvpn.net> wrote:
>
> +definitions:
> +  -
> +    type: const
> +    name: nonce-tail-size
> +    value: 8
> +  -
> +    type: enum
> +    name: cipher-alg
> +    value-start: 0

value-start defaults to 0 for enum so this is unnecessary. Same for
the following enum definitions.

> +    entries: [ none, aes-gcm, chacha20-poly1305 ]
> +  -
> +    type: enum
> +    name: del-peer-reason
> +    value-start: 0
> +    entries: [ teardown, userspace, expired, transport-error, transport-disconnect ]
> +  -
> +    type: enum
> +    name: key-slot
> +    value-start: 0
> +    entries: [ primary, secondary ]
> +  -
> +    type: enum
> +    name: mode
> +    value-start: 0
> +    entries: [ p2p, mp ]
> +

[...]

> +operations:
> +  list:
> +    -
> +      name: dev-new
> +      attribute-set: ovpn
> +      flags: [ admin-perm ]
> +      doc: Create a new interface of type ovpn
> +      do:
> +        request:
> +          attributes:
> +            - ifname
> +            - mode
> +        reply:
> +          attributes:
> +            - ifname
> +            - ifindex
> +    -
> +      name: dev-del
> +      attribute-set: ovpn
> +      flags: [ admin-perm ]
> +      doc: Delete existing interface of type ovpn
> +      do:
> +        pre: ovpn-nl-pre-doit
> +        post: ovpn-nl-post-doit
> +        request:
> +          attributes:
> +            - ifindex

There's no dev-get do/dump op. I think there should be one for
diagnostics and metrics.

> +    -
> +      name: key-new
> +      attribute-set: ovpn
> +      flags: [ admin-perm ]
> +      doc: Add a cipher key for a specific peer
> +      do:
> +        pre: ovpn-nl-pre-doit
> +        post: ovpn-nl-post-doit
> +        request:
> +          attributes:
> +            - ifindex
> +            - keyconf
> +    -
> +      name: key-swap
> +      attribute-set: ovpn
> +      flags: [ admin-perm ]
> +      doc: Swap primary and secondary session keys for a specific peer
> +      do:
> +        pre: ovpn-nl-pre-doit
> +        post: ovpn-nl-post-doit
> +        request:
> +          attributes:
> +            - ifindex
> +            - keyconf
> +    -
> +      name: key-swap-ntf
> +      notify: key-new

This doesn't work because key-new doesn't have a reply. You should
define it with an event: block instead. You can see the build errors
here:

make -C tools/net/ynl

CC ovpn-user.o
In file included from ovpn-user.c:8:
ovpn-user.h:1194:33: error: field ‘obj’ has incomplete type
 1194 |         struct ovpn_key_new_rsp obj __attribute__((aligned(8)));
      |                                 ^~~
ovpn-user.c:835:35: error: ‘ovpn_key_new_rsp_parse’ undeclared here
(not in a function); did you mean ‘ovpn_dev_new_rsp_parse’?
  835 |                 .cb             = ovpn_key_new_rsp_parse,
      |                                   ^~~~~~~~~~~~~~~~~~~~~~
      |                                   ovpn_dev_new_rsp_parse
make[1]: *** [Makefile:41: ovpn-user.o] Error 1

> +      doc: |
> +        Notification about key having exhausted its IV space and requiring
> +        renegotiation
> +      mcgrp: peers
> +    -
> +      name: key-del
> +      attribute-set: ovpn
> +      flags: [ admin-perm ]
> +      doc: Delete cipher key for a specific peer
> +      do:
> +        pre: ovpn-nl-pre-doit
> +        post: ovpn-nl-post-doit
> +        request:
> +          attributes:
> +            - ifindex
> +            - keyconf
> +
> +mcast-groups:
> +  list:
> +    -
> +      name: peers
Antonio Quartulli Oct. 7, 2024, 10:57 a.m. UTC | #3
On 04/10/2024 18:13, Donald Hunter wrote:
> On Wed, 2 Oct 2024 at 10:03, Antonio Quartulli <antonio@openvpn.net> wrote:
>>
>> +definitions:
>> +  -
>> +    type: const
>> +    name: nonce-tail-size
>> +    value: 8
>> +  -
>> +    type: enum
>> +    name: cipher-alg
>> +    value-start: 0
> 
> value-start defaults to 0 for enum so this is unnecessary. Same for
> the following enum definitions.

ACK

> 
>> +    entries: [ none, aes-gcm, chacha20-poly1305 ]
>> +  -
>> +    type: enum
>> +    name: del-peer-reason
>> +    value-start: 0
>> +    entries: [ teardown, userspace, expired, transport-error, transport-disconnect ]
>> +  -
>> +    type: enum
>> +    name: key-slot
>> +    value-start: 0
>> +    entries: [ primary, secondary ]
>> +  -
>> +    type: enum
>> +    name: mode
>> +    value-start: 0
>> +    entries: [ p2p, mp ]
>> +
> 
> [...]
> 
>> +operations:
>> +  list:
>> +    -
>> +      name: dev-new
>> +      attribute-set: ovpn
>> +      flags: [ admin-perm ]
>> +      doc: Create a new interface of type ovpn
>> +      do:
>> +        request:
>> +          attributes:
>> +            - ifname
>> +            - mode
>> +        reply:
>> +          attributes:
>> +            - ifname
>> +            - ifindex
>> +    -
>> +      name: dev-del
>> +      attribute-set: ovpn
>> +      flags: [ admin-perm ]
>> +      doc: Delete existing interface of type ovpn
>> +      do:
>> +        pre: ovpn-nl-pre-doit
>> +        post: ovpn-nl-post-doit
>> +        request:
>> +          attributes:
>> +            - ifindex
> 
> There's no dev-get do/dump op. I think there should be one for
> diagnostics and metrics.

I am not sure how much information it can provide (as of now we only 
have the 'mode' that is being set upon creation).

In any case, I am not against implementing the op now and extend it 
later as we see fit.

> 
>> +    -
>> +      name: key-new
>> +      attribute-set: ovpn
>> +      flags: [ admin-perm ]
>> +      doc: Add a cipher key for a specific peer
>> +      do:
>> +        pre: ovpn-nl-pre-doit
>> +        post: ovpn-nl-post-doit
>> +        request:
>> +          attributes:
>> +            - ifindex
>> +            - keyconf
>> +    -
>> +      name: key-swap
>> +      attribute-set: ovpn
>> +      flags: [ admin-perm ]
>> +      doc: Swap primary and secondary session keys for a specific peer
>> +      do:
>> +        pre: ovpn-nl-pre-doit
>> +        post: ovpn-nl-post-doit
>> +        request:
>> +          attributes:
>> +            - ifindex
>> +            - keyconf
>> +    -
>> +      name: key-swap-ntf
>> +      notify: key-new
> 
> This doesn't work because key-new doesn't have a reply. You should
> define it with an event: block instead. You can see the build errors
> here:
> 
> make -C tools/net/ynl

Oh, I wasn't aware of this subfolder.
Thanks for pointing it out!

I am thinking that it may make sense to implement a key-get op to 
extract non-sensible data about the keys (i.e. what cipher was 
configured). This may be useful for debugging as well.

At that point the key-swap-ntf can re-use the key-get as notify.


Cheers,

> 
> CC ovpn-user.o
> In file included from ovpn-user.c:8:
> ovpn-user.h:1194:33: error: field ‘obj’ has incomplete type
>   1194 |         struct ovpn_key_new_rsp obj __attribute__((aligned(8)));
>        |                                 ^~~
> ovpn-user.c:835:35: error: ‘ovpn_key_new_rsp_parse’ undeclared here
> (not in a function); did you mean ‘ovpn_dev_new_rsp_parse’?
>    835 |                 .cb             = ovpn_key_new_rsp_parse,
>        |                                   ^~~~~~~~~~~~~~~~~~~~~~
>        |                                   ovpn_dev_new_rsp_parse
> make[1]: *** [Makefile:41: ovpn-user.o] Error 1
> 
>> +      doc: |
>> +        Notification about key having exhausted its IV space and requiring
>> +        renegotiation
>> +      mcgrp: peers
>> +    -
>> +      name: key-del
>> +      attribute-set: ovpn
>> +      flags: [ admin-perm ]
>> +      doc: Delete cipher key for a specific peer
>> +      do:
>> +        pre: ovpn-nl-pre-doit
>> +        post: ovpn-nl-post-doit
>> +        request:
>> +          attributes:
>> +            - ifindex
>> +            - keyconf
>> +
>> +mcast-groups:
>> +  list:
>> +    -
>> +      name: peers
Jiri Pirko Oct. 7, 2024, 3:32 p.m. UTC | #4
Wed, Oct 02, 2024 at 11:02:17AM CEST, antonio@openvpn.net wrote:

[...]


>+operations:
>+  list:
>+    -
>+      name: dev-new
>+      attribute-set: ovpn
>+      flags: [ admin-perm ]
>+      doc: Create a new interface of type ovpn
>+      do:
>+        request:
>+          attributes:
>+            - ifname
>+            - mode
>+        reply:
>+          attributes:
>+            - ifname
>+            - ifindex
>+    -
>+      name: dev-del

Why you expose new and del here in ovn specific generic netlink iface?
Why can't you use the exising RTNL api which is used for creation and
destruction of other types of devices?


ip link add [link DEV | parentdev NAME] [ name ] NAME
		    [ txqueuelen PACKETS ]
		    [ address LLADDR ]
		    [ broadcast LLADDR ]
		    [ mtu MTU ] [index IDX ]
		    [ numtxqueues QUEUE_COUNT ]
		    [ numrxqueues QUEUE_COUNT ]
		    [ netns { PID | NETNSNAME | NETNSFILE } ]
		    type TYPE [ ARGS ]

ip link delete { DEVICE | dev DEVICE | group DEVGROUP } type TYPE [ ARGS ]

Lots of examples of existing types creation is for example here:
https://developers.redhat.com/blog/2018/10/22/introduction-to-linux-interfaces-for-virtual-networking



>+      attribute-set: ovpn
>+      flags: [ admin-perm ]
>+      doc: Delete existing interface of type ovpn
>+      do:
>+        pre: ovpn-nl-pre-doit
>+        post: ovpn-nl-post-doit
>+        request:
>+          attributes:
>+            - ifindex

[...]
Antonio Quartulli Oct. 8, 2024, 8:01 a.m. UTC | #5
Hi,

On 07/10/24 17:32, Jiri Pirko wrote:
> Wed, Oct 02, 2024 at 11:02:17AM CEST, antonio@openvpn.net wrote:
> 
> [...]
> 
> 
>> +operations:
>> +  list:
>> +    -
>> +      name: dev-new
>> +      attribute-set: ovpn
>> +      flags: [ admin-perm ]
>> +      doc: Create a new interface of type ovpn
>> +      do:
>> +        request:
>> +          attributes:
>> +            - ifname
>> +            - mode
>> +        reply:
>> +          attributes:
>> +            - ifname
>> +            - ifindex
>> +    -
>> +      name: dev-del
> 
> Why you expose new and del here in ovn specific generic netlink iface?
> Why can't you use the exising RTNL api which is used for creation and
> destruction of other types of devices?

That was my original approach in v1, but it was argued that an ovpn 
interface needs a userspace program to be configured and used in a 
meaningful way, therefore it was decided to concentrate all iface mgmt 
APIs along with the others in the netlink family and to not expose any 
RTNL ops.

However, recently we decided to add a dellink implementation for better 
integration with network namespaces and to allow the user to wipe a 
dangling interface.

In the future we are planning to also add the possibility to create a 
"persistent interface", that is an interface created before launching 
any userspace program and that survives when the latter is stopped.
I can guess this functionality may be better suited for RTNL, but I am 
not sure yet.

@Jiri: do you have any particular opinion why we should use RTNL ops and 
not netlink for creating/destroying interfaces? I feel this is mostly a 
matter of taste, but maybe there are technical reasons we should consider.

Thanks a lot for your contribution.

Regards,


> 
> 
> ip link add [link DEV | parentdev NAME] [ name ] NAME
> 		    [ txqueuelen PACKETS ]
> 		    [ address LLADDR ]
> 		    [ broadcast LLADDR ]
> 		    [ mtu MTU ] [index IDX ]
> 		    [ numtxqueues QUEUE_COUNT ]
> 		    [ numrxqueues QUEUE_COUNT ]
> 		    [ netns { PID | NETNSNAME | NETNSFILE } ]
> 		    type TYPE [ ARGS ]
> 
> ip link delete { DEVICE | dev DEVICE | group DEVGROUP } type TYPE [ ARGS ]
> 
> Lots of examples of existing types creation is for example here:
> https://developers.redhat.com/blog/2018/10/22/introduction-to-linux-interfaces-for-virtual-networking
> 
> 
> 
>> +      attribute-set: ovpn
>> +      flags: [ admin-perm ]
>> +      doc: Delete existing interface of type ovpn
>> +      do:
>> +        pre: ovpn-nl-pre-doit
>> +        post: ovpn-nl-post-doit
>> +        request:
>> +          attributes:
>> +            - ifindex
> 
> [...]
Jiri Pirko Oct. 8, 2024, 8:58 a.m. UTC | #6
Tue, Oct 08, 2024 at 10:01:40AM CEST, antonio@openvpn.net wrote:
>Hi,
>
>On 07/10/24 17:32, Jiri Pirko wrote:
>> Wed, Oct 02, 2024 at 11:02:17AM CEST, antonio@openvpn.net wrote:
>> 
>> [...]
>> 
>> 
>> > +operations:
>> > +  list:
>> > +    -
>> > +      name: dev-new
>> > +      attribute-set: ovpn
>> > +      flags: [ admin-perm ]
>> > +      doc: Create a new interface of type ovpn
>> > +      do:
>> > +        request:
>> > +          attributes:
>> > +            - ifname
>> > +            - mode
>> > +        reply:
>> > +          attributes:
>> > +            - ifname
>> > +            - ifindex
>> > +    -
>> > +      name: dev-del
>> 
>> Why you expose new and del here in ovn specific generic netlink iface?
>> Why can't you use the exising RTNL api which is used for creation and
>> destruction of other types of devices?
>
>That was my original approach in v1, but it was argued that an ovpn interface
>needs a userspace program to be configured and used in a meaningful way,
>therefore it was decided to concentrate all iface mgmt APIs along with the
>others in the netlink family and to not expose any RTNL ops.

Can you please point me to the message id?


>
>However, recently we decided to add a dellink implementation for better
>integration with network namespaces and to allow the user to wipe a dangling
>interface.

Hmm, one more argument to have symmetric add/del impletentation in RTNL


>
>In the future we are planning to also add the possibility to create a
>"persistent interface", that is an interface created before launching any
>userspace program and that survives when the latter is stopped.
>I can guess this functionality may be better suited for RTNL, but I am not
>sure yet.

That would be quite confusing to have RTNL and genetlink iface to
add/del device. From what you described above, makes more sent to have
it just in RTNL

>
>@Jiri: do you have any particular opinion why we should use RTNL ops and not
>netlink for creating/destroying interfaces? I feel this is mostly a matter of
>taste, but maybe there are technical reasons we should consider.

Well. technically, you can probabaly do both. But it is quite common
that you can add/delete these kind of devices over RTNL. Lots of
examples. People are used to it, aligns with existing flows.

>
>Thanks a lot for your contribution.
>
>Regards,
>
>
>> 
>> 
>> ip link add [link DEV | parentdev NAME] [ name ] NAME
>> 		    [ txqueuelen PACKETS ]
>> 		    [ address LLADDR ]
>> 		    [ broadcast LLADDR ]
>> 		    [ mtu MTU ] [index IDX ]
>> 		    [ numtxqueues QUEUE_COUNT ]
>> 		    [ numrxqueues QUEUE_COUNT ]
>> 		    [ netns { PID | NETNSNAME | NETNSFILE } ]
>> 		    type TYPE [ ARGS ]
>> 
>> ip link delete { DEVICE | dev DEVICE | group DEVGROUP } type TYPE [ ARGS ]
>> 
>> Lots of examples of existing types creation is for example here:
>> https://developers.redhat.com/blog/2018/10/22/introduction-to-linux-interfaces-for-virtual-networking
>> 
>> 
>> 
>> > +      attribute-set: ovpn
>> > +      flags: [ admin-perm ]
>> > +      doc: Delete existing interface of type ovpn
>> > +      do:
>> > +        pre: ovpn-nl-pre-doit
>> > +        post: ovpn-nl-post-doit
>> > +        request:
>> > +          attributes:
>> > +            - ifindex
>> 
>> [...]
>
>-- 
>Antonio Quartulli
>OpenVPN Inc.
Antonio Quartulli Oct. 8, 2024, 9:16 a.m. UTC | #7
On 08/10/2024 10:58, Jiri Pirko wrote:
> Tue, Oct 08, 2024 at 10:01:40AM CEST, antonio@openvpn.net wrote:
>> Hi,
>>
>> On 07/10/24 17:32, Jiri Pirko wrote:
>>> Wed, Oct 02, 2024 at 11:02:17AM CEST, antonio@openvpn.net wrote:
>>>
>>> [...]
>>>
>>>
>>>> +operations:
>>>> +  list:
>>>> +    -
>>>> +      name: dev-new
>>>> +      attribute-set: ovpn
>>>> +      flags: [ admin-perm ]
>>>> +      doc: Create a new interface of type ovpn
>>>> +      do:
>>>> +        request:
>>>> +          attributes:
>>>> +            - ifname
>>>> +            - mode
>>>> +        reply:
>>>> +          attributes:
>>>> +            - ifname
>>>> +            - ifindex
>>>> +    -
>>>> +      name: dev-del
>>>
>>> Why you expose new and del here in ovn specific generic netlink iface?
>>> Why can't you use the exising RTNL api which is used for creation and
>>> destruction of other types of devices?
>>
>> That was my original approach in v1, but it was argued that an ovpn interface
>> needs a userspace program to be configured and used in a meaningful way,
>> therefore it was decided to concentrate all iface mgmt APIs along with the
>> others in the netlink family and to not expose any RTNL ops.
> 
> Can you please point me to the message id?

<CAHNKnsQnHAdxC-XhC9RP-cFp0d-E4YGb+7ie3WymXVL9N-QS6A@mail.gmail.com> 
from Sergey and subsequent replies.
RTNL vs NL topic starts right after the definition of 'ovpn_link_ops'

Recently Kuniyuki commented on this topic as well in:
<20240919055259.17622-1-kuniyu@amazon.com>
and that is why I added a default dellink implemetation.

> 
> 
>>
>> However, recently we decided to add a dellink implementation for better
>> integration with network namespaces and to allow the user to wipe a dangling
>> interface.
> 
> Hmm, one more argument to have symmetric add/del impletentation in RTNL
> 
> 
>>
>> In the future we are planning to also add the possibility to create a
>> "persistent interface", that is an interface created before launching any
>> userspace program and that survives when the latter is stopped.
>> I can guess this functionality may be better suited for RTNL, but I am not
>> sure yet.
> 
> That would be quite confusing to have RTNL and genetlink iface to
> add/del device. From what you described above, makes more sent to have
> it just in RTNL

All in all I tend to agree.

> 
>>
>> @Jiri: do you have any particular opinion why we should use RTNL ops and not
>> netlink for creating/destroying interfaces? I feel this is mostly a matter of
>> taste, but maybe there are technical reasons we should consider.
> 
> Well. technically, you can probabaly do both. But it is quite common
> that you can add/delete these kind of devices over RTNL. Lots of
> examples. People are used to it, aligns with existing flows.

The only counterargument I see is the one brought by Sergey: "the ovpn 
interface is not usable after creation, if no openvpn process is running".

However, allowing to create "persistent interfaces" will define a 
use-case for having an ovpn device without any userspace process.

@Sergey what is your opinion here? I am not sure persistent interfaces 
were discussed at the time you brought your point about RTNL vs NL.


Regards,


> 
>>
>> Thanks a lot for your contribution.
>>
>> Regards,
>>
>>
>>>
>>>
>>> ip link add [link DEV | parentdev NAME] [ name ] NAME
>>> 		    [ txqueuelen PACKETS ]
>>> 		    [ address LLADDR ]
>>> 		    [ broadcast LLADDR ]
>>> 		    [ mtu MTU ] [index IDX ]
>>> 		    [ numtxqueues QUEUE_COUNT ]
>>> 		    [ numrxqueues QUEUE_COUNT ]
>>> 		    [ netns { PID | NETNSNAME | NETNSFILE } ]
>>> 		    type TYPE [ ARGS ]
>>>
>>> ip link delete { DEVICE | dev DEVICE | group DEVGROUP } type TYPE [ ARGS ]
>>>
>>> Lots of examples of existing types creation is for example here:
>>> https://developers.redhat.com/blog/2018/10/22/introduction-to-linux-interfaces-for-virtual-networking
>>>
>>>
>>>
>>>> +      attribute-set: ovpn
>>>> +      flags: [ admin-perm ]
>>>> +      doc: Delete existing interface of type ovpn
>>>> +      do:
>>>> +        pre: ovpn-nl-pre-doit
>>>> +        post: ovpn-nl-post-doit
>>>> +        request:
>>>> +          attributes:
>>>> +            - ifindex
>>>
>>> [...]
>>
>> -- 
>> Antonio Quartulli
>> OpenVPN Inc.
Jiri Pirko Oct. 8, 2024, 12:52 p.m. UTC | #8
Tue, Oct 08, 2024 at 11:16:01AM CEST, antonio@openvpn.net wrote:
>On 08/10/2024 10:58, Jiri Pirko wrote:
>> Tue, Oct 08, 2024 at 10:01:40AM CEST, antonio@openvpn.net wrote:
>> > Hi,
>> > 
>> > On 07/10/24 17:32, Jiri Pirko wrote:
>> > > Wed, Oct 02, 2024 at 11:02:17AM CEST, antonio@openvpn.net wrote:
>> > > 
>> > > [...]
>> > > 
>> > > 
>> > > > +operations:
>> > > > +  list:
>> > > > +    -
>> > > > +      name: dev-new
>> > > > +      attribute-set: ovpn
>> > > > +      flags: [ admin-perm ]
>> > > > +      doc: Create a new interface of type ovpn
>> > > > +      do:
>> > > > +        request:
>> > > > +          attributes:
>> > > > +            - ifname
>> > > > +            - mode
>> > > > +        reply:
>> > > > +          attributes:
>> > > > +            - ifname
>> > > > +            - ifindex
>> > > > +    -
>> > > > +      name: dev-del
>> > > 
>> > > Why you expose new and del here in ovn specific generic netlink iface?
>> > > Why can't you use the exising RTNL api which is used for creation and
>> > > destruction of other types of devices?
>> > 
>> > That was my original approach in v1, but it was argued that an ovpn interface
>> > needs a userspace program to be configured and used in a meaningful way,
>> > therefore it was decided to concentrate all iface mgmt APIs along with the
>> > others in the netlink family and to not expose any RTNL ops.
>> 
>> Can you please point me to the message id?
>
><CAHNKnsQnHAdxC-XhC9RP-cFp0d-E4YGb+7ie3WymXVL9N-QS6A@mail.gmail.com> from
>Sergey and subsequent replies.
>RTNL vs NL topic starts right after the definition of 'ovpn_link_ops'

Yeah, does not make sense to me. All devices should implement common
rtnl ops, the extra-config, if needed, could be on a separate channel.
I don't find Sergey's argumentation valid.


>
>Recently Kuniyuki commented on this topic as well in:
><20240919055259.17622-1-kuniyu@amazon.com>
>and that is why I added a default dellink implemetation.

Having dellink without newlink implemented is just wrong.


>
>> 
>> 
>> > 
>> > However, recently we decided to add a dellink implementation for better
>> > integration with network namespaces and to allow the user to wipe a dangling
>> > interface.
>> 
>> Hmm, one more argument to have symmetric add/del impletentation in RTNL
>> 
>> 
>> > 
>> > In the future we are planning to also add the possibility to create a
>> > "persistent interface", that is an interface created before launching any
>> > userspace program and that survives when the latter is stopped.
>> > I can guess this functionality may be better suited for RTNL, but I am not
>> > sure yet.
>> 
>> That would be quite confusing to have RTNL and genetlink iface to
>> add/del device. From what you described above, makes more sent to have
>> it just in RTNL
>
>All in all I tend to agree.
>
>> 
>> > 
>> > @Jiri: do you have any particular opinion why we should use RTNL ops and not
>> > netlink for creating/destroying interfaces? I feel this is mostly a matter of
>> > taste, but maybe there are technical reasons we should consider.
>> 
>> Well. technically, you can probabaly do both. But it is quite common
>> that you can add/delete these kind of devices over RTNL. Lots of
>> examples. People are used to it, aligns with existing flows.
>
>The only counterargument I see is the one brought by Sergey: "the ovpn
>interface is not usable after creation, if no openvpn process is running".
>
>However, allowing to create "persistent interfaces" will define a use-case
>for having an ovpn device without any userspace process.
>
>@Sergey what is your opinion here? I am not sure persistent interfaces were
>discussed at the time you brought your point about RTNL vs NL.
>
>
>Regards,
>
>
>> 
>> > 
>> > Thanks a lot for your contribution.
>> > 
>> > Regards,
>> > 
>> > 
>> > > 
>> > > 
>> > > ip link add [link DEV | parentdev NAME] [ name ] NAME
>> > > 		    [ txqueuelen PACKETS ]
>> > > 		    [ address LLADDR ]
>> > > 		    [ broadcast LLADDR ]
>> > > 		    [ mtu MTU ] [index IDX ]
>> > > 		    [ numtxqueues QUEUE_COUNT ]
>> > > 		    [ numrxqueues QUEUE_COUNT ]
>> > > 		    [ netns { PID | NETNSNAME | NETNSFILE } ]
>> > > 		    type TYPE [ ARGS ]
>> > > 
>> > > ip link delete { DEVICE | dev DEVICE | group DEVGROUP } type TYPE [ ARGS ]
>> > > 
>> > > Lots of examples of existing types creation is for example here:
>> > > https://developers.redhat.com/blog/2018/10/22/introduction-to-linux-interfaces-for-virtual-networking
>> > > 
>> > > 
>> > > 
>> > > > +      attribute-set: ovpn
>> > > > +      flags: [ admin-perm ]
>> > > > +      doc: Delete existing interface of type ovpn
>> > > > +      do:
>> > > > +        pre: ovpn-nl-pre-doit
>> > > > +        post: ovpn-nl-post-doit
>> > > > +        request:
>> > > > +          attributes:
>> > > > +            - ifindex
>> > > 
>> > > [...]
>> > 
>> > -- 
>> > Antonio Quartulli
>> > OpenVPN Inc.
>
>-- 
>Antonio Quartulli
>OpenVPN Inc.
Antonio Quartulli Oct. 8, 2024, 1:21 p.m. UTC | #9
On 08/10/2024 14:52, Jiri Pirko wrote:
> Tue, Oct 08, 2024 at 11:16:01AM CEST, antonio@openvpn.net wrote:
>> On 08/10/2024 10:58, Jiri Pirko wrote:
>>> Tue, Oct 08, 2024 at 10:01:40AM CEST, antonio@openvpn.net wrote:
>>>> Hi,
>>>>
>>>> On 07/10/24 17:32, Jiri Pirko wrote:
>>>>> Wed, Oct 02, 2024 at 11:02:17AM CEST, antonio@openvpn.net wrote:
>>>>>
>>>>> [...]
>>>>>
>>>>>
>>>>>> +operations:
>>>>>> +  list:
>>>>>> +    -
>>>>>> +      name: dev-new
>>>>>> +      attribute-set: ovpn
>>>>>> +      flags: [ admin-perm ]
>>>>>> +      doc: Create a new interface of type ovpn
>>>>>> +      do:
>>>>>> +        request:
>>>>>> +          attributes:
>>>>>> +            - ifname
>>>>>> +            - mode
>>>>>> +        reply:
>>>>>> +          attributes:
>>>>>> +            - ifname
>>>>>> +            - ifindex
>>>>>> +    -
>>>>>> +      name: dev-del
>>>>>
>>>>> Why you expose new and del here in ovn specific generic netlink iface?
>>>>> Why can't you use the exising RTNL api which is used for creation and
>>>>> destruction of other types of devices?
>>>>
>>>> That was my original approach in v1, but it was argued that an ovpn interface
>>>> needs a userspace program to be configured and used in a meaningful way,
>>>> therefore it was decided to concentrate all iface mgmt APIs along with the
>>>> others in the netlink family and to not expose any RTNL ops.
>>>
>>> Can you please point me to the message id?
>>
>> <CAHNKnsQnHAdxC-XhC9RP-cFp0d-E4YGb+7ie3WymXVL9N-QS6A@mail.gmail.com> from
>> Sergey and subsequent replies.
>> RTNL vs NL topic starts right after the definition of 'ovpn_link_ops'
> 
> Yeah, does not make sense to me. All devices should implement common
> rtnl ops, the extra-config, if needed, could be on a separate channel.
> I don't find Sergey's argumentation valid.

Thanks a lot for taking the time to read our conversation.

Ok, considering all points we have discussed so far (including future 
developments already on the roadmap), I think it makes sense to go back 
to RTNL and drop the new/del-dev ops from the netlink family.

Will do that in v9.

Regards,

> 
> 
>>
>> Recently Kuniyuki commented on this topic as well in:
>> <20240919055259.17622-1-kuniyu@amazon.com>
>> and that is why I added a default dellink implemetation.
> 
> Having dellink without newlink implemented is just wrong.
> 
> 
>>
>>>
>>>
>>>>
>>>> However, recently we decided to add a dellink implementation for better
>>>> integration with network namespaces and to allow the user to wipe a dangling
>>>> interface.
>>>
>>> Hmm, one more argument to have symmetric add/del impletentation in RTNL
>>>
>>>
>>>>
>>>> In the future we are planning to also add the possibility to create a
>>>> "persistent interface", that is an interface created before launching any
>>>> userspace program and that survives when the latter is stopped.
>>>> I can guess this functionality may be better suited for RTNL, but I am not
>>>> sure yet.
>>>
>>> That would be quite confusing to have RTNL and genetlink iface to
>>> add/del device. From what you described above, makes more sent to have
>>> it just in RTNL
>>
>> All in all I tend to agree.
>>
>>>
>>>>
>>>> @Jiri: do you have any particular opinion why we should use RTNL ops and not
>>>> netlink for creating/destroying interfaces? I feel this is mostly a matter of
>>>> taste, but maybe there are technical reasons we should consider.
>>>
>>> Well. technically, you can probabaly do both. But it is quite common
>>> that you can add/delete these kind of devices over RTNL. Lots of
>>> examples. People are used to it, aligns with existing flows.
>>
>> The only counterargument I see is the one brought by Sergey: "the ovpn
>> interface is not usable after creation, if no openvpn process is running".
>>
>> However, allowing to create "persistent interfaces" will define a use-case
>> for having an ovpn device without any userspace process.
>>
>> @Sergey what is your opinion here? I am not sure persistent interfaces were
>> discussed at the time you brought your point about RTNL vs NL.
>>
>>
>> Regards,
>>
>>
>>>
>>>>
>>>> Thanks a lot for your contribution.
>>>>
>>>> Regards,
>>>>
>>>>
>>>>>
>>>>>
>>>>> ip link add [link DEV | parentdev NAME] [ name ] NAME
>>>>> 		    [ txqueuelen PACKETS ]
>>>>> 		    [ address LLADDR ]
>>>>> 		    [ broadcast LLADDR ]
>>>>> 		    [ mtu MTU ] [index IDX ]
>>>>> 		    [ numtxqueues QUEUE_COUNT ]
>>>>> 		    [ numrxqueues QUEUE_COUNT ]
>>>>> 		    [ netns { PID | NETNSNAME | NETNSFILE } ]
>>>>> 		    type TYPE [ ARGS ]
>>>>>
>>>>> ip link delete { DEVICE | dev DEVICE | group DEVGROUP } type TYPE [ ARGS ]
>>>>>
>>>>> Lots of examples of existing types creation is for example here:
>>>>> https://developers.redhat.com/blog/2018/10/22/introduction-to-linux-interfaces-for-virtual-networking
>>>>>
>>>>>
>>>>>
>>>>>> +      attribute-set: ovpn
>>>>>> +      flags: [ admin-perm ]
>>>>>> +      doc: Delete existing interface of type ovpn
>>>>>> +      do:
>>>>>> +        pre: ovpn-nl-pre-doit
>>>>>> +        post: ovpn-nl-post-doit
>>>>>> +        request:
>>>>>> +          attributes:
>>>>>> +            - ifindex
>>>>>
>>>>> [...]
>>>>
>>>> -- 
>>>> Antonio Quartulli
>>>> OpenVPN Inc.
>>
>> -- 
>> Antonio Quartulli
>> OpenVPN Inc.
Sergey Ryazanov Nov. 1, 2024, 12:17 a.m. UTC | #10
Hello Jiri,

Sorry for the late reply. Could you elaborate a bit reasons for the RTNL 
interface implementation? Please find the questions inlined.

On 08.10.2024 15:52, Jiri Pirko wrote:
> Tue, Oct 08, 2024 at 11:16:01AM CEST, antonio@openvpn.net wrote:
>> On 08/10/2024 10:58, Jiri Pirko wrote:
>>> Tue, Oct 08, 2024 at 10:01:40AM CEST, antonio@openvpn.net wrote:
>>>> Hi,
>>>>
>>>> On 07/10/24 17:32, Jiri Pirko wrote:
>>>>> Wed, Oct 02, 2024 at 11:02:17AM CEST, antonio@openvpn.net wrote:
>>>>>
>>>>> [...]
>>>>>
>>>>>
>>>>>> +operations:
>>>>>> +  list:
>>>>>> +    -
>>>>>> +      name: dev-new
>>>>>> +      attribute-set: ovpn
>>>>>> +      flags: [ admin-perm ]
>>>>>> +      doc: Create a new interface of type ovpn
>>>>>> +      do:
>>>>>> +        request:
>>>>>> +          attributes:
>>>>>> +            - ifname
>>>>>> +            - mode
>>>>>> +        reply:
>>>>>> +          attributes:
>>>>>> +            - ifname
>>>>>> +            - ifindex
>>>>>> +    -
>>>>>> +      name: dev-del
>>>>>
>>>>> Why you expose new and del here in ovn specific generic netlink iface?
>>>>> Why can't you use the exising RTNL api which is used for creation and
>>>>> destruction of other types of devices?
>>>>
>>>> That was my original approach in v1, but it was argued that an ovpn interface
>>>> needs a userspace program to be configured and used in a meaningful way,
>>>> therefore it was decided to concentrate all iface mgmt APIs along with the
>>>> others in the netlink family and to not expose any RTNL ops.
>>>
>>> Can you please point me to the message id?
>>
>> <CAHNKnsQnHAdxC-XhC9RP-cFp0d-E4YGb+7ie3WymXVL9N-QS6A@mail.gmail.com> from
>> Sergey and subsequent replies.
>> RTNL vs NL topic starts right after the definition of 'ovpn_link_ops'
> 
> Yeah, does not make sense to me. All devices should implement common
> rtnl ops, the extra-config, if needed, could be on a separate channel.
> I don't find Sergey's argumentation valid.

Do we consider word *should* in terms of RFC 2119:

    SHOULD   This word, or the adjective "RECOMMENDED", mean that there
    may exist valid reasons in particular circumstances to ignore a
    particular item, but the full implications must be understood and
    carefully weighed before choosing a different course.

I am asking because rtnl_link_register() allows ops without .newlink 
implementation. What makes .newlink implementation as least optional and 
gives a freedom in design.

Let me briefly summarize my argumentation from the referenced thread. We 
have two classes of links point-to-point and point-to-multipoint. The 
major class is PtP and RTNL is perfectly suited to manage it. While PtMP 
is a minor class and it is an obstacle for RTNL due to need to manage 
multiple peers. What requires a different interface to manage these 
peers. Lets call it GENL interface. A PtMP-class netdev without any 
configured peer is useless, what makes GENL interface for peers 
management mandatory. Mandatory to implement in both user- and kernel-space.

Link creation can be implemented using any of these (RTNL or GENL) 
interfaces. GENL interface is already mandatory to implement in a 
user-space software, while RTNL can be considered optional to implement. 
So, implementing the link creation using GENL requires only a new 
message support implementation. While implementing the the link creation 
using RTNL requires a whole new interface implementation (socket 
read/write, messages demux, etc.).

My point is, GENL-only management gives us consolidated and clear 
solution, while GENL+RTNL requires code duplication and causes a 
complexity. That's it.


Jiri, do you see big flaws in this reasoning?


>> Recently Kuniyuki commented on this topic as well in:
>> <20240919055259.17622-1-kuniyu@amazon.com>
>> and that is why I added a default dellink implemetation.
> 
> Having dellink without newlink implemented is just wrong.

Could you clarify this statement please? I can not recall any 
documentation or a code block that enforces .newlink implementation in 
case of the .dellink presence.


Generally speaking, I can understand a feel of irregularity when looking 
at code implementing delete operation without a link creation 
counterpart. This confusion can be resolved taking into consideration a 
difference in a nature of these operations. A new link can not be 
created automatically while an existing link can be removed 
automatically without any extra inputs.

.newlink designated only for fulfilling user's requests since it 
requires extra information unavailable inside the kernel. While .dellink 
has two semantics: (a) user's requests fulfilling, (b) automatic cleanup 
of unneeded remainders.

 From that perspective, having an option to implement .dellink without 
.newlink implementation looks reasonable.


>>>> However, recently we decided to add a dellink implementation for better
>>>> integration with network namespaces and to allow the user to wipe a dangling
>>>> interface.
>>>
>>> Hmm, one more argument to have symmetric add/del impletentation in RTNL
>>>
>>>
>>>>
>>>> In the future we are planning to also add the possibility to create a
>>>> "persistent interface", that is an interface created before launching any
>>>> userspace program and that survives when the latter is stopped.
>>>> I can guess this functionality may be better suited for RTNL, but I am not
>>>> sure yet.
>>>
>>> That would be quite confusing to have RTNL and genetlink iface to
>>> add/del device. From what you described above, makes more sent to have
>>> it just in RTNL
>>
>> All in all I tend to agree.
>>
>>>
>>>>
>>>> @Jiri: do you have any particular opinion why we should use RTNL ops and not
>>>> netlink for creating/destroying interfaces? I feel this is mostly a matter of
>>>> taste, but maybe there are technical reasons we should consider.
>>>
>>> Well. technically, you can probabaly do both. But it is quite common
>>> that you can add/delete these kind of devices over RTNL. Lots of
>>> examples. People are used to it, aligns with existing flows.
>>
>> The only counterargument I see is the one brought by Sergey: "the ovpn
>> interface is not usable after creation, if no openvpn process is running".
>>
>> However, allowing to create "persistent interfaces" will define a use-case
>> for having an ovpn device without any userspace process.
>>
>> @Sergey what is your opinion here? I am not sure persistent interfaces were
>> discussed at the time you brought your point about RTNL vs NL.
>>
>>
>> Regards,
>>
>>
>>>
>>>>
>>>> Thanks a lot for your contribution.
>>>>
>>>> Regards,
>>>>
>>>>
>>>>>
>>>>>
>>>>> ip link add [link DEV | parentdev NAME] [ name ] NAME
>>>>> 		    [ txqueuelen PACKETS ]
>>>>> 		    [ address LLADDR ]
>>>>> 		    [ broadcast LLADDR ]
>>>>> 		    [ mtu MTU ] [index IDX ]
>>>>> 		    [ numtxqueues QUEUE_COUNT ]
>>>>> 		    [ numrxqueues QUEUE_COUNT ]
>>>>> 		    [ netns { PID | NETNSNAME | NETNSFILE } ]
>>>>> 		    type TYPE [ ARGS ]
>>>>>
>>>>> ip link delete { DEVICE | dev DEVICE | group DEVGROUP } type TYPE [ ARGS ]
>>>>>
>>>>> Lots of examples of existing types creation is for example here:
>>>>> https://developers.redhat.com/blog/2018/10/22/introduction-to-linux-interfaces-for-virtual-networking
>>>>>
>>>>>
>>>>>
>>>>>> +      attribute-set: ovpn
>>>>>> +      flags: [ admin-perm ]
>>>>>> +      doc: Delete existing interface of type ovpn
>>>>>> +      do:
>>>>>> +        pre: ovpn-nl-pre-doit
>>>>>> +        post: ovpn-nl-post-doit
>>>>>> +        request:
>>>>>> +          attributes:
>>>>>> +            - ifindex

--
Sergey
diff mbox series

Patch

diff --git a/Documentation/netlink/specs/ovpn.yaml b/Documentation/netlink/specs/ovpn.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..36983f293ecbd59532114808a20737005e91ad72
--- /dev/null
+++ b/Documentation/netlink/specs/ovpn.yaml
@@ -0,0 +1,387 @@ 
+# SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)
+#
+# Author: Antonio Quartulli <antonio@openvpn.net>
+#
+# Copyright (c) 2024, OpenVPN Inc.
+#
+
+name: ovpn
+
+protocol: genetlink
+
+doc: Netlink protocol to control OpenVPN network devices
+
+definitions:
+  -
+    type: const
+    name: nonce-tail-size
+    value: 8
+  -
+    type: enum
+    name: cipher-alg
+    value-start: 0
+    entries: [ none, aes-gcm, chacha20-poly1305 ]
+  -
+    type: enum
+    name: del-peer-reason
+    value-start: 0
+    entries: [ teardown, userspace, expired, transport-error, transport-disconnect ]
+  -
+    type: enum
+    name: key-slot
+    value-start: 0
+    entries: [ primary, secondary ]
+  -
+    type: enum
+    name: mode
+    value-start: 0
+    entries: [ p2p, mp ]
+
+attribute-sets:
+  -
+    name: peer
+    attributes:
+      -
+        name: id
+        type: u32
+        doc: |
+          The unique ID of the peer. To be used to identify peers during
+          operations
+        checks:
+          max: 0xFFFFFF
+      -
+        name: remote-ipv4
+        type: u32
+        doc: The remote IPv4 address of the peer
+        byte-order: big-endian
+        display-hint: ipv4
+      -
+        name: remote-ipv6
+        type: binary
+        doc: The remote IPv6 address of the peer
+        display-hint: ipv6
+        checks:
+          exact-len: 16
+      -
+        name: remote-ipv6-scope-id
+        type: u32
+        doc: The scope id of the remote IPv6 address of the peer (RFC2553)
+      -
+        name: remote-port
+        type: u16
+        doc: The remote port of the peer
+        byte-order: big-endian
+        checks:
+          min: 1
+      -
+        name: socket
+        type: u32
+        doc: The socket to be used to communicate with the peer
+      -
+        name: vpn-ipv4
+        type: u32
+        doc: The IPv4 address assigned to the peer by the server
+        byte-order: big-endian
+        display-hint: ipv4
+      -
+        name: vpn-ipv6
+        type: binary
+        doc: The IPv6 address assigned to the peer by the server
+        display-hint: ipv6
+        checks:
+          exact-len: 16
+      -
+        name: local-ipv4
+        type: u32
+        doc: The local IPv4 to be used to send packets to the peer (UDP only)
+        byte-order: big-endian
+        display-hint: ipv4
+      -
+        name: local-ipv6
+        type: binary
+        doc: The local IPv6 to be used to send packets to the peer (UDP only)
+        display-hint: ipv6
+        checks:
+          exact-len: 16
+      -
+        name: local-port
+        type: u16
+        doc: The local port to be used to send packets to the peer (UDP only)
+        byte-order: big-endian
+        checks:
+          min: 1
+      -
+        name: keepalive-interval
+        type: u32
+        doc: |
+          The number of seconds after which a keep alive message is sent to the
+          peer
+      -
+        name: keepalive-timeout
+        type: u32
+        doc: |
+          The number of seconds from the last activity after which the peer is
+          assumed dead
+      -
+        name: del-reason
+        type: u32
+        doc: The reason why a peer was deleted
+        enum: del-peer-reason
+      -
+        name: vpn-rx-bytes
+        type: uint
+        doc: Number of bytes received over the tunnel
+      -
+        name: vpn-tx-bytes
+        type: uint
+        doc: Number of bytes transmitted over the tunnel
+      -
+        name: vpn-rx-packets
+        type: uint
+        doc: Number of packets received over the tunnel
+      -
+        name: vpn-tx-packets
+        type: uint
+        doc: Number of packets transmitted over the tunnel
+      -
+        name: link-rx-bytes
+        type: uint
+        doc: Number of bytes received at the transport level
+      -
+        name: link-tx-bytes
+        type: uint
+        doc: Number of bytes transmitted at the transport level
+      -
+        name: link-rx-packets
+        type: u32
+        doc: Number of packets received at the transport level
+      -
+        name: link-tx-packets
+        type: u32
+        doc: Number of packets transmitted at the transport level
+  -
+    name: keyconf
+    attributes:
+      -
+        name: peer-id
+        type: u32
+        doc: |
+          The unique ID of the peer. To be used to identify peers during
+          key operations
+        checks:
+          max: 0xFFFFFF
+      -
+        name: slot
+        type: u32
+        doc: The slot where the key should be stored
+        enum: key-slot
+      -
+        name: key-id
+        doc: |
+          The unique ID of the key. Used to fetch the correct key upon
+          decryption
+        type: u32
+        checks:
+          max: 7
+      -
+        name: cipher-alg
+        type: u32
+        doc: The cipher to be used when communicating with the peer
+        enum: cipher-alg
+      -
+        name: encrypt-dir
+        type: nest
+        doc: Key material for encrypt direction
+        nested-attributes: keydir
+      -
+        name: decrypt-dir
+        type: nest
+        doc: Key material for decrypt direction
+        nested-attributes: keydir
+  -
+    name: keydir
+    attributes:
+      -
+        name: cipher-key
+        type: binary
+        doc: The actual key to be used by the cipher
+        checks:
+         max-len: 256
+      -
+        name: nonce-tail
+        type: binary
+        doc: |
+          Random nonce to be concatenated to the packet ID, in order to
+          obtain the actua cipher IV
+        checks:
+         exact-len: nonce-tail-size
+  -
+    name: ovpn
+    attributes:
+      -
+        name: ifindex
+        type: u32
+        doc: Index of the ovpn interface to operate on
+      -
+        name: ifname
+        type: string
+        doc: Name of the ovpn interface that is being created
+      -
+        name: mode
+        type: u32
+        enum: mode
+        doc: |
+          Oper mode instructing an interface to act as Point2Point or
+          MultiPoint
+      -
+        name: peer
+        type: nest
+        doc: |
+          The peer object containing the attributed of interest for the specific
+          operation
+        nested-attributes: peer
+      -
+        name: keyconf
+        type: nest
+        doc: Peer specific cipher configuration
+        nested-attributes: keyconf
+
+operations:
+  list:
+    -
+      name: dev-new
+      attribute-set: ovpn
+      flags: [ admin-perm ]
+      doc: Create a new interface of type ovpn
+      do:
+        request:
+          attributes:
+            - ifname
+            - mode
+        reply:
+          attributes:
+            - ifname
+            - ifindex
+    -
+      name: dev-del
+      attribute-set: ovpn
+      flags: [ admin-perm ]
+      doc: Delete existing interface of type ovpn
+      do:
+        pre: ovpn-nl-pre-doit
+        post: ovpn-nl-post-doit
+        request:
+          attributes:
+            - ifindex
+    -
+      name: peer-new
+      attribute-set: ovpn
+      flags: [ admin-perm ]
+      doc: Add a remote peer
+      do:
+        pre: ovpn-nl-pre-doit
+        post: ovpn-nl-post-doit
+        request:
+          attributes:
+            - ifindex
+            - peer
+    -
+      name: peer-set
+      attribute-set: ovpn
+      flags: [ admin-perm ]
+      doc: modify a remote peer
+      do:
+        pre: ovpn-nl-pre-doit
+        post: ovpn-nl-post-doit
+        request:
+          attributes:
+            - ifindex
+            - peer
+    -
+      name: peer-get
+      attribute-set: ovpn
+      flags: [ admin-perm ]
+      doc: Retrieve data about existing remote peers (or a specific one)
+      do:
+        pre: ovpn-nl-pre-doit
+        post: ovpn-nl-post-doit
+        request:
+          attributes:
+            - ifindex
+            - peer
+        reply:
+          attributes:
+            - peer
+      dump:
+        request:
+          attributes:
+            - ifindex
+        reply:
+          attributes:
+            - peer
+    -
+      name: peer-del
+      attribute-set: ovpn
+      flags: [ admin-perm ]
+      doc: Delete existing remote peer
+      do:
+        pre: ovpn-nl-pre-doit
+        post: ovpn-nl-post-doit
+        request:
+          attributes:
+            - ifindex
+            - peer
+    -
+      name: peer-del-ntf
+      doc: Notification about a peer being deleted
+      notify: peer-get
+      mcgrp: peers
+
+    -
+      name: key-new
+      attribute-set: ovpn
+      flags: [ admin-perm ]
+      doc: Add a cipher key for a specific peer
+      do:
+        pre: ovpn-nl-pre-doit
+        post: ovpn-nl-post-doit
+        request:
+          attributes:
+            - ifindex
+            - keyconf
+    -
+      name: key-swap
+      attribute-set: ovpn
+      flags: [ admin-perm ]
+      doc: Swap primary and secondary session keys for a specific peer
+      do:
+        pre: ovpn-nl-pre-doit
+        post: ovpn-nl-post-doit
+        request:
+          attributes:
+            - ifindex
+            - keyconf
+    -
+      name: key-swap-ntf
+      notify: key-new
+      doc: |
+        Notification about key having exhausted its IV space and requiring
+        renegotiation
+      mcgrp: peers
+    -
+      name: key-del
+      attribute-set: ovpn
+      flags: [ admin-perm ]
+      doc: Delete cipher key for a specific peer
+      do:
+        pre: ovpn-nl-pre-doit
+        post: ovpn-nl-post-doit
+        request:
+          attributes:
+            - ifindex
+            - keyconf
+
+mcast-groups:
+  list:
+    -
+      name: peers
diff --git a/MAINTAINERS b/MAINTAINERS
index 0f02c9e1664fddb5ad45232e46b02c42aa27f1b6..f753060d4e2467a786778ddd4f835861a603ce02 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -17455,6 +17455,8 @@  L:	netdev@vger.kernel.org
 S:	Maintained
 T:	git https://github.com/OpenVPN/linux-kernel-ovpn.git
 F:	drivers/net/ovpn/
+F:	include/uapi/linux/ovpn.h
+F:	Documentation/netlink/spec/ovpn.yaml
 
 P54 WIRELESS DRIVER
 M:	Christian Lamparter <chunkeey@googlemail.com>
diff --git a/drivers/net/ovpn/Makefile b/drivers/net/ovpn/Makefile
index 53fb197027d787d6683e9056d3d341abf6ed38e4..201dc001419f1d99ae95c0ee0f96e68f8a4eac16 100644
--- a/drivers/net/ovpn/Makefile
+++ b/drivers/net/ovpn/Makefile
@@ -9,3 +9,5 @@ 
 obj-$(CONFIG_OVPN) := ovpn.o
 ovpn-y += main.o
 ovpn-y += io.o
+ovpn-y += netlink.o
+ovpn-y += netlink-gen.o
diff --git a/drivers/net/ovpn/main.c b/drivers/net/ovpn/main.c
index 2a9a8e11c5111eb36fab1fb47d6c4c6dfdf56a78..0274ddddbcd25080d7f85347f79aad0259771508 100644
--- a/drivers/net/ovpn/main.c
+++ b/drivers/net/ovpn/main.c
@@ -7,12 +7,16 @@ 
  *		James Yonan <james@openvpn.net>
  */
 
+#include <linux/genetlink.h>
 #include <linux/module.h>
 #include <linux/netdevice.h>
 #include <linux/version.h>
 #include <net/rtnetlink.h>
+#include <uapi/linux/ovpn.h>
 
+#include "ovpnstruct.h"
 #include "main.h"
+#include "netlink.h"
 #include "io.h"
 
 /* Driver info */
@@ -31,7 +35,7 @@  bool ovpn_dev_is_valid(const struct net_device *dev)
 }
 
 static struct rtnl_link_ops ovpn_link_ops = {
-	.kind = "ovpn",
+	.kind = OVPN_FAMILY_NAME,
 	.netns_refund = false,
 	.dellink = unregister_netdevice_queue,
 };
@@ -86,8 +90,16 @@  static int __init ovpn_init(void)
 		goto unreg_netdev;
 	}
 
+	err = ovpn_nl_register();
+	if (err) {
+		pr_err("ovpn: can't register netlink family: %d\n", err);
+		goto unreg_rtnl;
+	}
+
 	return 0;
 
+unreg_rtnl:
+	rtnl_link_unregister(&ovpn_link_ops);
 unreg_netdev:
 	unregister_netdevice_notifier(&ovpn_netdev_notifier);
 	return err;
@@ -95,6 +107,7 @@  static int __init ovpn_init(void)
 
 static __exit void ovpn_cleanup(void)
 {
+	ovpn_nl_unregister();
 	rtnl_link_unregister(&ovpn_link_ops);
 	unregister_netdevice_notifier(&ovpn_netdev_notifier);
 
diff --git a/drivers/net/ovpn/netlink-gen.c b/drivers/net/ovpn/netlink-gen.c
new file mode 100644
index 0000000000000000000000000000000000000000..9912706a2d68282e7d483404002cd0eeb1140ba6
--- /dev/null
+++ b/drivers/net/ovpn/netlink-gen.c
@@ -0,0 +1,224 @@ 
+// SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)
+/* Do not edit directly, auto-generated from: */
+/*	Documentation/netlink/specs/ovpn.yaml */
+/* YNL-GEN kernel source */
+
+#include <net/netlink.h>
+#include <net/genetlink.h>
+
+#include "netlink-gen.h"
+
+#include <uapi/linux/ovpn.h>
+
+/* Integer value ranges */
+static const struct netlink_range_validation ovpn_a_peer_id_range = {
+	.max	= 16777215ULL,
+};
+
+static const struct netlink_range_validation ovpn_a_keyconf_peer_id_range = {
+	.max	= 16777215ULL,
+};
+
+/* Common nested types */
+const struct nla_policy ovpn_keyconf_nl_policy[OVPN_A_KEYCONF_DECRYPT_DIR + 1] = {
+	[OVPN_A_KEYCONF_PEER_ID] = NLA_POLICY_FULL_RANGE(NLA_U32, &ovpn_a_keyconf_peer_id_range),
+	[OVPN_A_KEYCONF_SLOT] = NLA_POLICY_MAX(NLA_U32, 1),
+	[OVPN_A_KEYCONF_KEY_ID] = NLA_POLICY_MAX(NLA_U32, 7),
+	[OVPN_A_KEYCONF_CIPHER_ALG] = NLA_POLICY_MAX(NLA_U32, 2),
+	[OVPN_A_KEYCONF_ENCRYPT_DIR] = NLA_POLICY_NESTED(ovpn_keydir_nl_policy),
+	[OVPN_A_KEYCONF_DECRYPT_DIR] = NLA_POLICY_NESTED(ovpn_keydir_nl_policy),
+};
+
+const struct nla_policy ovpn_keydir_nl_policy[OVPN_A_KEYDIR_NONCE_TAIL + 1] = {
+	[OVPN_A_KEYDIR_CIPHER_KEY] = NLA_POLICY_MAX_LEN(256),
+	[OVPN_A_KEYDIR_NONCE_TAIL] = NLA_POLICY_EXACT_LEN(OVPN_NONCE_TAIL_SIZE),
+};
+
+const struct nla_policy ovpn_peer_nl_policy[OVPN_A_PEER_LINK_TX_PACKETS + 1] = {
+	[OVPN_A_PEER_ID] = NLA_POLICY_FULL_RANGE(NLA_U32, &ovpn_a_peer_id_range),
+	[OVPN_A_PEER_REMOTE_IPV4] = { .type = NLA_U32, },
+	[OVPN_A_PEER_REMOTE_IPV6] = NLA_POLICY_EXACT_LEN(16),
+	[OVPN_A_PEER_REMOTE_IPV6_SCOPE_ID] = { .type = NLA_U32, },
+	[OVPN_A_PEER_REMOTE_PORT] = NLA_POLICY_MIN(NLA_U16, 1),
+	[OVPN_A_PEER_SOCKET] = { .type = NLA_U32, },
+	[OVPN_A_PEER_VPN_IPV4] = { .type = NLA_U32, },
+	[OVPN_A_PEER_VPN_IPV6] = NLA_POLICY_EXACT_LEN(16),
+	[OVPN_A_PEER_LOCAL_IPV4] = { .type = NLA_U32, },
+	[OVPN_A_PEER_LOCAL_IPV6] = NLA_POLICY_EXACT_LEN(16),
+	[OVPN_A_PEER_LOCAL_PORT] = NLA_POLICY_MIN(NLA_U16, 1),
+	[OVPN_A_PEER_KEEPALIVE_INTERVAL] = { .type = NLA_U32, },
+	[OVPN_A_PEER_KEEPALIVE_TIMEOUT] = { .type = NLA_U32, },
+	[OVPN_A_PEER_DEL_REASON] = NLA_POLICY_MAX(NLA_U32, 4),
+	[OVPN_A_PEER_VPN_RX_BYTES] = { .type = NLA_UINT, },
+	[OVPN_A_PEER_VPN_TX_BYTES] = { .type = NLA_UINT, },
+	[OVPN_A_PEER_VPN_RX_PACKETS] = { .type = NLA_UINT, },
+	[OVPN_A_PEER_VPN_TX_PACKETS] = { .type = NLA_UINT, },
+	[OVPN_A_PEER_LINK_RX_BYTES] = { .type = NLA_UINT, },
+	[OVPN_A_PEER_LINK_TX_BYTES] = { .type = NLA_UINT, },
+	[OVPN_A_PEER_LINK_RX_PACKETS] = { .type = NLA_U32, },
+	[OVPN_A_PEER_LINK_TX_PACKETS] = { .type = NLA_U32, },
+};
+
+/* OVPN_CMD_DEV_NEW - do */
+static const struct nla_policy ovpn_dev_new_nl_policy[OVPN_A_MODE + 1] = {
+	[OVPN_A_IFNAME] = { .type = NLA_NUL_STRING, },
+	[OVPN_A_MODE] = NLA_POLICY_MAX(NLA_U32, 1),
+};
+
+/* OVPN_CMD_DEV_DEL - do */
+static const struct nla_policy ovpn_dev_del_nl_policy[OVPN_A_IFINDEX + 1] = {
+	[OVPN_A_IFINDEX] = { .type = NLA_U32, },
+};
+
+/* OVPN_CMD_PEER_NEW - do */
+static const struct nla_policy ovpn_peer_new_nl_policy[OVPN_A_PEER + 1] = {
+	[OVPN_A_IFINDEX] = { .type = NLA_U32, },
+	[OVPN_A_PEER] = NLA_POLICY_NESTED(ovpn_peer_nl_policy),
+};
+
+/* OVPN_CMD_PEER_SET - do */
+static const struct nla_policy ovpn_peer_set_nl_policy[OVPN_A_PEER + 1] = {
+	[OVPN_A_IFINDEX] = { .type = NLA_U32, },
+	[OVPN_A_PEER] = NLA_POLICY_NESTED(ovpn_peer_nl_policy),
+};
+
+/* OVPN_CMD_PEER_GET - do */
+static const struct nla_policy ovpn_peer_get_do_nl_policy[OVPN_A_PEER + 1] = {
+	[OVPN_A_IFINDEX] = { .type = NLA_U32, },
+	[OVPN_A_PEER] = NLA_POLICY_NESTED(ovpn_peer_nl_policy),
+};
+
+/* OVPN_CMD_PEER_GET - dump */
+static const struct nla_policy ovpn_peer_get_dump_nl_policy[OVPN_A_IFINDEX + 1] = {
+	[OVPN_A_IFINDEX] = { .type = NLA_U32, },
+};
+
+/* OVPN_CMD_PEER_DEL - do */
+static const struct nla_policy ovpn_peer_del_nl_policy[OVPN_A_PEER + 1] = {
+	[OVPN_A_IFINDEX] = { .type = NLA_U32, },
+	[OVPN_A_PEER] = NLA_POLICY_NESTED(ovpn_peer_nl_policy),
+};
+
+/* OVPN_CMD_KEY_NEW - do */
+static const struct nla_policy ovpn_key_new_nl_policy[OVPN_A_KEYCONF + 1] = {
+	[OVPN_A_IFINDEX] = { .type = NLA_U32, },
+	[OVPN_A_KEYCONF] = NLA_POLICY_NESTED(ovpn_keyconf_nl_policy),
+};
+
+/* OVPN_CMD_KEY_SWAP - do */
+static const struct nla_policy ovpn_key_swap_nl_policy[OVPN_A_KEYCONF + 1] = {
+	[OVPN_A_IFINDEX] = { .type = NLA_U32, },
+	[OVPN_A_KEYCONF] = NLA_POLICY_NESTED(ovpn_keyconf_nl_policy),
+};
+
+/* OVPN_CMD_KEY_DEL - do */
+static const struct nla_policy ovpn_key_del_nl_policy[OVPN_A_KEYCONF + 1] = {
+	[OVPN_A_IFINDEX] = { .type = NLA_U32, },
+	[OVPN_A_KEYCONF] = NLA_POLICY_NESTED(ovpn_keyconf_nl_policy),
+};
+
+/* Ops table for ovpn */
+static const struct genl_split_ops ovpn_nl_ops[] = {
+	{
+		.cmd		= OVPN_CMD_DEV_NEW,
+		.doit		= ovpn_nl_dev_new_doit,
+		.policy		= ovpn_dev_new_nl_policy,
+		.maxattr	= OVPN_A_MODE,
+		.flags		= GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
+	},
+	{
+		.cmd		= OVPN_CMD_DEV_DEL,
+		.pre_doit	= ovpn_nl_pre_doit,
+		.doit		= ovpn_nl_dev_del_doit,
+		.post_doit	= ovpn_nl_post_doit,
+		.policy		= ovpn_dev_del_nl_policy,
+		.maxattr	= OVPN_A_IFINDEX,
+		.flags		= GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
+	},
+	{
+		.cmd		= OVPN_CMD_PEER_NEW,
+		.pre_doit	= ovpn_nl_pre_doit,
+		.doit		= ovpn_nl_peer_new_doit,
+		.post_doit	= ovpn_nl_post_doit,
+		.policy		= ovpn_peer_new_nl_policy,
+		.maxattr	= OVPN_A_PEER,
+		.flags		= GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
+	},
+	{
+		.cmd		= OVPN_CMD_PEER_SET,
+		.pre_doit	= ovpn_nl_pre_doit,
+		.doit		= ovpn_nl_peer_set_doit,
+		.post_doit	= ovpn_nl_post_doit,
+		.policy		= ovpn_peer_set_nl_policy,
+		.maxattr	= OVPN_A_PEER,
+		.flags		= GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
+	},
+	{
+		.cmd		= OVPN_CMD_PEER_GET,
+		.pre_doit	= ovpn_nl_pre_doit,
+		.doit		= ovpn_nl_peer_get_doit,
+		.post_doit	= ovpn_nl_post_doit,
+		.policy		= ovpn_peer_get_do_nl_policy,
+		.maxattr	= OVPN_A_PEER,
+		.flags		= GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
+	},
+	{
+		.cmd		= OVPN_CMD_PEER_GET,
+		.dumpit		= ovpn_nl_peer_get_dumpit,
+		.policy		= ovpn_peer_get_dump_nl_policy,
+		.maxattr	= OVPN_A_IFINDEX,
+		.flags		= GENL_ADMIN_PERM | GENL_CMD_CAP_DUMP,
+	},
+	{
+		.cmd		= OVPN_CMD_PEER_DEL,
+		.pre_doit	= ovpn_nl_pre_doit,
+		.doit		= ovpn_nl_peer_del_doit,
+		.post_doit	= ovpn_nl_post_doit,
+		.policy		= ovpn_peer_del_nl_policy,
+		.maxattr	= OVPN_A_PEER,
+		.flags		= GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
+	},
+	{
+		.cmd		= OVPN_CMD_KEY_NEW,
+		.pre_doit	= ovpn_nl_pre_doit,
+		.doit		= ovpn_nl_key_new_doit,
+		.post_doit	= ovpn_nl_post_doit,
+		.policy		= ovpn_key_new_nl_policy,
+		.maxattr	= OVPN_A_KEYCONF,
+		.flags		= GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
+	},
+	{
+		.cmd		= OVPN_CMD_KEY_SWAP,
+		.pre_doit	= ovpn_nl_pre_doit,
+		.doit		= ovpn_nl_key_swap_doit,
+		.post_doit	= ovpn_nl_post_doit,
+		.policy		= ovpn_key_swap_nl_policy,
+		.maxattr	= OVPN_A_KEYCONF,
+		.flags		= GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
+	},
+	{
+		.cmd		= OVPN_CMD_KEY_DEL,
+		.pre_doit	= ovpn_nl_pre_doit,
+		.doit		= ovpn_nl_key_del_doit,
+		.post_doit	= ovpn_nl_post_doit,
+		.policy		= ovpn_key_del_nl_policy,
+		.maxattr	= OVPN_A_KEYCONF,
+		.flags		= GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
+	},
+};
+
+static const struct genl_multicast_group ovpn_nl_mcgrps[] = {
+	[OVPN_NLGRP_PEERS] = { "peers", },
+};
+
+struct genl_family ovpn_nl_family __ro_after_init = {
+	.name		= OVPN_FAMILY_NAME,
+	.version	= OVPN_FAMILY_VERSION,
+	.netnsok	= true,
+	.parallel_ops	= true,
+	.module		= THIS_MODULE,
+	.split_ops	= ovpn_nl_ops,
+	.n_split_ops	= ARRAY_SIZE(ovpn_nl_ops),
+	.mcgrps		= ovpn_nl_mcgrps,
+	.n_mcgrps	= ARRAY_SIZE(ovpn_nl_mcgrps),
+};
diff --git a/drivers/net/ovpn/netlink-gen.h b/drivers/net/ovpn/netlink-gen.h
new file mode 100644
index 0000000000000000000000000000000000000000..1b7aeb80af1091d5fe968c886fb019ba6f9a19a8
--- /dev/null
+++ b/drivers/net/ovpn/netlink-gen.h
@@ -0,0 +1,42 @@ 
+/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
+/* Do not edit directly, auto-generated from: */
+/*	Documentation/netlink/specs/ovpn.yaml */
+/* YNL-GEN kernel header */
+
+#ifndef _LINUX_OVPN_GEN_H
+#define _LINUX_OVPN_GEN_H
+
+#include <net/netlink.h>
+#include <net/genetlink.h>
+
+#include <uapi/linux/ovpn.h>
+
+/* Common nested types */
+extern const struct nla_policy ovpn_keyconf_nl_policy[OVPN_A_KEYCONF_DECRYPT_DIR + 1];
+extern const struct nla_policy ovpn_keydir_nl_policy[OVPN_A_KEYDIR_NONCE_TAIL + 1];
+extern const struct nla_policy ovpn_peer_nl_policy[OVPN_A_PEER_LINK_TX_PACKETS + 1];
+
+int ovpn_nl_pre_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
+		     struct genl_info *info);
+void
+ovpn_nl_post_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
+		  struct genl_info *info);
+
+int ovpn_nl_dev_new_doit(struct sk_buff *skb, struct genl_info *info);
+int ovpn_nl_dev_del_doit(struct sk_buff *skb, struct genl_info *info);
+int ovpn_nl_peer_new_doit(struct sk_buff *skb, struct genl_info *info);
+int ovpn_nl_peer_set_doit(struct sk_buff *skb, struct genl_info *info);
+int ovpn_nl_peer_get_doit(struct sk_buff *skb, struct genl_info *info);
+int ovpn_nl_peer_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb);
+int ovpn_nl_peer_del_doit(struct sk_buff *skb, struct genl_info *info);
+int ovpn_nl_key_new_doit(struct sk_buff *skb, struct genl_info *info);
+int ovpn_nl_key_swap_doit(struct sk_buff *skb, struct genl_info *info);
+int ovpn_nl_key_del_doit(struct sk_buff *skb, struct genl_info *info);
+
+enum {
+	OVPN_NLGRP_PEERS,
+};
+
+extern struct genl_family ovpn_nl_family;
+
+#endif /* _LINUX_OVPN_GEN_H */
diff --git a/drivers/net/ovpn/netlink.c b/drivers/net/ovpn/netlink.c
new file mode 100644
index 0000000000000000000000000000000000000000..7b6b4d03b845eeb8654e37ac3495e8172ac3f291
--- /dev/null
+++ b/drivers/net/ovpn/netlink.c
@@ -0,0 +1,158 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/*  OpenVPN data channel offload
+ *
+ *  Copyright (C) 2020-2024 OpenVPN, Inc.
+ *
+ *  Author:	Antonio Quartulli <antonio@openvpn.net>
+ */
+
+#include <linux/netdevice.h>
+#include <net/genetlink.h>
+
+#include <uapi/linux/ovpn.h>
+
+#include "ovpnstruct.h"
+#include "main.h"
+#include "io.h"
+#include "netlink.h"
+#include "netlink-gen.h"
+
+MODULE_ALIAS_GENL_FAMILY(OVPN_FAMILY_NAME);
+
+/**
+ * ovpn_get_dev_from_attrs - retrieve the netdevice a netlink message is
+ *                           targeting
+ * @net: network namespace where to look for the interface
+ * @info: generic netlink info from the user request
+ *
+ * Return: the netdevice, if found, or an error otherwise
+ */
+static struct net_device *
+ovpn_get_dev_from_attrs(struct net *net, const struct genl_info *info)
+{
+	struct net_device *dev;
+	int ifindex;
+
+	if (GENL_REQ_ATTR_CHECK(info, OVPN_A_IFINDEX))
+		return ERR_PTR(-EINVAL);
+
+	ifindex = nla_get_u32(info->attrs[OVPN_A_IFINDEX]);
+
+	dev = dev_get_by_index(net, ifindex);
+	if (!dev) {
+		NL_SET_ERR_MSG_MOD(info->extack,
+				   "ifindex does not match any interface");
+		return ERR_PTR(-ENODEV);
+	}
+
+	if (!ovpn_dev_is_valid(dev))
+		goto err_put_dev;
+
+	return dev;
+
+err_put_dev:
+	netdev_put(dev, NULL);
+
+	NL_SET_ERR_MSG_MOD(info->extack, "specified interface is not ovpn");
+	NL_SET_BAD_ATTR(info->extack, info->attrs[OVPN_A_IFINDEX]);
+
+	return ERR_PTR(-EINVAL);
+}
+
+int ovpn_nl_pre_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
+		     struct genl_info *info)
+{
+	struct net *net = genl_info_net(info);
+	struct net_device *dev = ovpn_get_dev_from_attrs(net, info);
+
+	if (IS_ERR(dev))
+		return PTR_ERR(dev);
+
+	info->user_ptr[0] = netdev_priv(dev);
+
+	return 0;
+}
+
+void ovpn_nl_post_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
+		       struct genl_info *info)
+{
+	struct ovpn_struct *ovpn = info->user_ptr[0];
+
+	if (ovpn)
+		netdev_put(ovpn->dev, NULL);
+}
+
+int ovpn_nl_dev_new_doit(struct sk_buff *skb, struct genl_info *info)
+{
+	return -EOPNOTSUPP;
+}
+
+int ovpn_nl_dev_del_doit(struct sk_buff *skb, struct genl_info *info)
+{
+	return -EOPNOTSUPP;
+}
+
+int ovpn_nl_peer_new_doit(struct sk_buff *skb, struct genl_info *info)
+{
+	return -EOPNOTSUPP;
+}
+
+int ovpn_nl_peer_set_doit(struct sk_buff *skb, struct genl_info *info)
+{
+	return -EOPNOTSUPP;
+}
+
+int ovpn_nl_peer_get_doit(struct sk_buff *skb, struct genl_info *info)
+{
+	return -EOPNOTSUPP;
+}
+
+int ovpn_nl_peer_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
+{
+	return -EOPNOTSUPP;
+}
+
+int ovpn_nl_peer_del_doit(struct sk_buff *skb, struct genl_info *info)
+{
+	return -EOPNOTSUPP;
+}
+
+int ovpn_nl_key_new_doit(struct sk_buff *skb, struct genl_info *info)
+{
+	return -EOPNOTSUPP;
+}
+
+int ovpn_nl_key_swap_doit(struct sk_buff *skb, struct genl_info *info)
+{
+	return -EOPNOTSUPP;
+}
+
+int ovpn_nl_key_del_doit(struct sk_buff *skb, struct genl_info *info)
+{
+	return -EOPNOTSUPP;
+}
+
+/**
+ * ovpn_nl_register - perform any needed registration in the NL subsustem
+ *
+ * Return: 0 on success, a negative error code otherwise
+ */
+int __init ovpn_nl_register(void)
+{
+	int ret = genl_register_family(&ovpn_nl_family);
+
+	if (ret) {
+		pr_err("ovpn: genl_register_family failed: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+/**
+ * ovpn_nl_unregister - undo any module wide netlink registration
+ */
+void ovpn_nl_unregister(void)
+{
+	genl_unregister_family(&ovpn_nl_family);
+}
diff --git a/drivers/net/ovpn/netlink.h b/drivers/net/ovpn/netlink.h
new file mode 100644
index 0000000000000000000000000000000000000000..9e87cf11d1e9813b7a75ddf3705ab7d5fabe899f
--- /dev/null
+++ b/drivers/net/ovpn/netlink.h
@@ -0,0 +1,15 @@ 
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*  OpenVPN data channel offload
+ *
+ *  Copyright (C) 2020-2024 OpenVPN, Inc.
+ *
+ *  Author:	Antonio Quartulli <antonio@openvpn.net>
+ */
+
+#ifndef _NET_OVPN_NETLINK_H_
+#define _NET_OVPN_NETLINK_H_
+
+int ovpn_nl_register(void);
+void ovpn_nl_unregister(void);
+
+#endif /* _NET_OVPN_NETLINK_H_ */
diff --git a/drivers/net/ovpn/ovpnstruct.h b/drivers/net/ovpn/ovpnstruct.h
new file mode 100644
index 0000000000000000000000000000000000000000..ff248cad1401d00c5bf0aadf9341e40d0a4aee4b
--- /dev/null
+++ b/drivers/net/ovpn/ovpnstruct.h
@@ -0,0 +1,21 @@ 
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*  OpenVPN data channel offload
+ *
+ *  Copyright (C) 2019-2024 OpenVPN, Inc.
+ *
+ *  Author:	James Yonan <james@openvpn.net>
+ *		Antonio Quartulli <antonio@openvpn.net>
+ */
+
+#ifndef _NET_OVPN_OVPNSTRUCT_H_
+#define _NET_OVPN_OVPNSTRUCT_H_
+
+/**
+ * struct ovpn_struct - per ovpn interface state
+ * @dev: the actual netdev representing the tunnel
+ */
+struct ovpn_struct {
+	struct net_device *dev;
+};
+
+#endif /* _NET_OVPN_OVPNSTRUCT_H_ */
diff --git a/include/uapi/linux/ovpn.h b/include/uapi/linux/ovpn.h
new file mode 100644
index 0000000000000000000000000000000000000000..adf4cafd9624308cc7bf1935bba55fb38bb6ba8c
--- /dev/null
+++ b/include/uapi/linux/ovpn.h
@@ -0,0 +1,116 @@ 
+/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
+/* Do not edit directly, auto-generated from: */
+/*	Documentation/netlink/specs/ovpn.yaml */
+/* YNL-GEN uapi header */
+
+#ifndef _UAPI_LINUX_OVPN_H
+#define _UAPI_LINUX_OVPN_H
+
+#define OVPN_FAMILY_NAME	"ovpn"
+#define OVPN_FAMILY_VERSION	1
+
+#define OVPN_NONCE_TAIL_SIZE	8
+
+enum ovpn_cipher_alg {
+	OVPN_CIPHER_ALG_NONE,
+	OVPN_CIPHER_ALG_AES_GCM,
+	OVPN_CIPHER_ALG_CHACHA20_POLY1305,
+};
+
+enum ovpn_del_peer_reason {
+	OVPN_DEL_PEER_REASON_TEARDOWN,
+	OVPN_DEL_PEER_REASON_USERSPACE,
+	OVPN_DEL_PEER_REASON_EXPIRED,
+	OVPN_DEL_PEER_REASON_TRANSPORT_ERROR,
+	OVPN_DEL_PEER_REASON_TRANSPORT_DISCONNECT,
+};
+
+enum ovpn_key_slot {
+	OVPN_KEY_SLOT_PRIMARY,
+	OVPN_KEY_SLOT_SECONDARY,
+};
+
+enum ovpn_mode {
+	OVPN_MODE_P2P,
+	OVPN_MODE_MP,
+};
+
+enum {
+	OVPN_A_PEER_ID = 1,
+	OVPN_A_PEER_REMOTE_IPV4,
+	OVPN_A_PEER_REMOTE_IPV6,
+	OVPN_A_PEER_REMOTE_IPV6_SCOPE_ID,
+	OVPN_A_PEER_REMOTE_PORT,
+	OVPN_A_PEER_SOCKET,
+	OVPN_A_PEER_VPN_IPV4,
+	OVPN_A_PEER_VPN_IPV6,
+	OVPN_A_PEER_LOCAL_IPV4,
+	OVPN_A_PEER_LOCAL_IPV6,
+	OVPN_A_PEER_LOCAL_PORT,
+	OVPN_A_PEER_KEEPALIVE_INTERVAL,
+	OVPN_A_PEER_KEEPALIVE_TIMEOUT,
+	OVPN_A_PEER_DEL_REASON,
+	OVPN_A_PEER_VPN_RX_BYTES,
+	OVPN_A_PEER_VPN_TX_BYTES,
+	OVPN_A_PEER_VPN_RX_PACKETS,
+	OVPN_A_PEER_VPN_TX_PACKETS,
+	OVPN_A_PEER_LINK_RX_BYTES,
+	OVPN_A_PEER_LINK_TX_BYTES,
+	OVPN_A_PEER_LINK_RX_PACKETS,
+	OVPN_A_PEER_LINK_TX_PACKETS,
+
+	__OVPN_A_PEER_MAX,
+	OVPN_A_PEER_MAX = (__OVPN_A_PEER_MAX - 1)
+};
+
+enum {
+	OVPN_A_KEYCONF_PEER_ID = 1,
+	OVPN_A_KEYCONF_SLOT,
+	OVPN_A_KEYCONF_KEY_ID,
+	OVPN_A_KEYCONF_CIPHER_ALG,
+	OVPN_A_KEYCONF_ENCRYPT_DIR,
+	OVPN_A_KEYCONF_DECRYPT_DIR,
+
+	__OVPN_A_KEYCONF_MAX,
+	OVPN_A_KEYCONF_MAX = (__OVPN_A_KEYCONF_MAX - 1)
+};
+
+enum {
+	OVPN_A_KEYDIR_CIPHER_KEY = 1,
+	OVPN_A_KEYDIR_NONCE_TAIL,
+
+	__OVPN_A_KEYDIR_MAX,
+	OVPN_A_KEYDIR_MAX = (__OVPN_A_KEYDIR_MAX - 1)
+};
+
+enum {
+	OVPN_A_IFINDEX = 1,
+	OVPN_A_IFNAME,
+	OVPN_A_MODE,
+	OVPN_A_PEER,
+	OVPN_A_KEYCONF,
+
+	__OVPN_A_MAX,
+	OVPN_A_MAX = (__OVPN_A_MAX - 1)
+};
+
+enum {
+	OVPN_CMD_DEV_NEW = 1,
+	OVPN_CMD_DEV_DEL,
+	OVPN_CMD_PEER_NEW,
+	OVPN_CMD_PEER_SET,
+	OVPN_CMD_PEER_GET,
+	OVPN_CMD_PEER_DEL,
+	OVPN_CMD_PEER_DEL_NTF,
+	OVPN_CMD_KEY_NEW,
+	OVPN_CMD_KEY_SWAP,
+	OVPN_CMD_KEY_SWAP_NTF,
+	OVPN_CMD_KEY_DEL,
+
+	__OVPN_CMD_MAX,
+	OVPN_CMD_MAX = (__OVPN_CMD_MAX - 1)
+};
+
+#define OVPN_MCGRP_PEERS	"peers"
+
+#endif /* _UAPI_LINUX_OVPN_H */