diff mbox series

[net-next,v7,04/25] ovpn: add basic netlink support

Message ID 20240917010734.1905-5-antonio@openvpn.net (mailing list archive)
State Deferred
Delegated to: Netdev Maintainers
Headers show
Series Introducing OpenVPN Data Channel Offload | expand

Checks

Context Check Description
netdev/series_format fail Series longer than 15 patches
netdev/tree_selection success Clearly marked for net-next, async
netdev/ynl success Generated files up to date; no warnings/errors; GEN HAS DIFF 2 files changed, 2619 insertions(+);
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: 16 this patch: 16
netdev/build_tools success Errors and warnings before: 0 this patch: 0
netdev/cc_maintainers warning 1 maintainers not CCed: openvpn-devel@lists.sourceforge.net
netdev/build_clang success Errors and warnings before: 17 this patch: 17
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: 22 this patch: 22
netdev/checkpatch warning WARNING: line length of 81 exceeds 80 columns WARNING: line length of 82 exceeds 80 columns WARNING: line length of 83 exceeds 80 columns WARNING: line length of 84 exceeds 80 columns WARNING: line length of 86 exceeds 80 columns WARNING: line length of 97 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 success net-next-2024-09-17--15-00 (tests: 764)

Commit Message

Antonio Quartulli Sept. 17, 2024, 1:07 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 | 328 ++++++++++++++++++++++++++
 MAINTAINERS                           |   1 +
 drivers/net/ovpn/Makefile             |   2 +
 drivers/net/ovpn/main.c               |  14 ++
 drivers/net/ovpn/netlink-gen.c        | 206 ++++++++++++++++
 drivers/net/ovpn/netlink-gen.h        |  41 ++++
 drivers/net/ovpn/netlink.c            | 153 ++++++++++++
 drivers/net/ovpn/netlink.h            |  15 ++
 drivers/net/ovpn/ovpnstruct.h         |  21 ++
 include/uapi/linux/ovpn.h             | 108 +++++++++
 10 files changed, 889 insertions(+)
 create mode 100644 Documentation/netlink/specs/ovpn.yaml
 create mode 100644 drivers/net/ovpn/netlink-gen.c
 create mode 100644 drivers/net/ovpn/netlink-gen.h
 create mode 100644 drivers/net/ovpn/netlink.c
 create mode 100644 drivers/net/ovpn/netlink.h
 create mode 100644 drivers/net/ovpn/ovpnstruct.h
 create mode 100644 include/uapi/linux/ovpn.h

Comments

Donald Hunter Sept. 17, 2024, 1:23 p.m. UTC | #1
Antonio Quartulli <antonio@openvpn.net> writes:

> 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

Hi Antonio,

net-next is currently closed so my guess is that you'll have to resend
this when net-next reopens at the end of the month:

https://netdev.bots.linux.dev/net-next.html

I have read through the YAML spec and I have few comments (and nits)
below.

Thanks,
Donald.

> diff --git a/Documentation/netlink/specs/ovpn.yaml b/Documentation/netlink/specs/ovpn.yaml
> new file mode 100644
> index 000000000000..456ac3747d27
> --- /dev/null
> +++ b/Documentation/netlink/specs/ovpn.yaml
> @@ -0,0 +1,328 @@
> +# 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 ]

Nit: Is there any reason for the underscore in chacha20_poly1305 and the
mixed use of dash / underscore in various identifiers throughout the
spec? The YNL convention is to use dashes throughout.

> +  -
> +    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: sockaddr-remote
> +        type: binary
> +        doc: |
> +          The sockaddr_in/in6 object identifying the remote address/port of the
> +          peer

The use of structs as attribute values is strongly discouraged. There
should be separate attributes for port and ipv[46]-address.

https://docs.kernel.org/userspace-api/netlink/intro.html#fixed-metadata-and-structures

> +      -
> +        name: socket
> +        type: u32
> +        doc: The socket to be used to communicate with the peer
> +      -
> +        name: vpn-ipv4
> +        type: u32
> +        doc: The IPv4 assigned to the peer by the server

nit: IPv4 address

> +        display-hint: ipv4
> +      -
> +        name: vpn-ipv6
> +        type: binary
> +        doc: The IPv6 assigned to the peer by the server

nit: IPv6 address

> +        display-hint: ipv6
> +        checks:
> +          exact-len: 16
> +      -
> +        name: local-ip
> +        type: binary
> +        doc: The local IP to be used to send packets to the peer (UDP only)
> +        checks:
> +          max-len: 16

It might be better to have separate attrs fopr local-ipv4 and
local-ipv6, to be consistent with vpn-ipv4 / vpn-ipv6

> +      -
> +        name: local-port
> +        type: u32
> +        doc: The local port to be used to send packets to the peer (UDP only)
> +        checks:
> +          min: 1
> +          max: u16-max
> +      -
> +        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: keyconf
> +        type: nest
> +        doc: Peer specific cipher configuration
> +        nested-attributes: keyconf

Perhaps keyconf should just be used as a top-level attribute-set. The
only attr you'd need to duplicate would be peer-id? There are separate
ops for setting peers and for key configuration, right?

> +      -
> +        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: slot
> +        type: u32
> +        doc: The slot where the key should be stored
> +        enum: key-slot
> +      -
> +        name: key-id
> +        doc: |
> +          The unique ID for 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
> +
> +operations:
> +  list:
> +    -
> +      name: new-iface

This might be better called 'dev' or 'link' to be consistent with the
existing netlink UAPIs.

> +      attribute-set: ovpn
> +      flags: [ admin-perm ]
> +      doc: Create a new interface
> +      do:
> +        request:
> +          attributes:
> +            - ifname
> +            - mode
> +        reply:
> +          attributes:
> +            - ifname
> +            - ifindex
> +    -
> +      name: del-iface
> +      attribute-set: ovpn
> +      flags: [ admin-perm ]
> +      doc: Delete existing interface
> +      do:
> +        pre: ovpn-nl-pre-doit
> +        post: ovpn-nl-post-doit
> +        request:
> +          attributes:
> +            - ifindex
> +    -
> +      name: set-peer
> +      attribute-set: ovpn
> +      flags: [ admin-perm ]
> +      doc: Add or modify a remote peer
> +      do:
> +        pre: ovpn-nl-pre-doit
> +        post: ovpn-nl-post-doit
> +        request:
> +          attributes:
> +            - ifindex
> +            - peer
> +    -
> +      name: get-peer
> +      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: del-peer
> +      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

I think you need to add an op for 'del-peer-notify' to specify the
notification, not reuse the 'del-peer' command.

> +    -
> +      name: set-key
> +      attribute-set: ovpn
> +      flags: [ admin-perm ]
> +      doc: Add or modify a cipher key for a specific peer
> +      do:
> +        pre: ovpn-nl-pre-doit
> +        post: ovpn-nl-post-doit
> +        request:
> +          attributes:
> +            - ifindex
> +            - peer
> +    -
> +      name: swap-keys
> +      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
> +            - peer

Same for swap-keys notifications.

> +    -
> +      name: del-key
> +      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
> +            - peer
> +
> +mcast-groups:
> +  list:
> +    -
> +      name: peers
Antonio Quartulli Sept. 17, 2024, 9:28 p.m. UTC | #2
Hi Donald,

On 17/09/2024 15:23, Donald Hunter wrote:
> Antonio Quartulli <antonio@openvpn.net> writes:
> 
>> 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
> 
> Hi Antonio,
> 
> net-next is currently closed so my guess is that you'll have to resend
> this when net-next reopens at the end of the month:
> 
> https://netdev.bots.linux.dev/net-next.html

I quickly discussed this point with Sabrina and the conclusion was that 
posting shouldn't hurt as this patchset is not truly "a new submission".
In any case, I'll see if more comments come through or not first.

> 
> I have read through the YAML spec and I have few comments (and nits)
> below.

Thanks a lot. My replies are inline.

> 
> Thanks,
> Donald.
> 
>> diff --git a/Documentation/netlink/specs/ovpn.yaml b/Documentation/netlink/specs/ovpn.yaml
>> new file mode 100644
>> index 000000000000..456ac3747d27
>> --- /dev/null
>> +++ b/Documentation/netlink/specs/ovpn.yaml
>> @@ -0,0 +1,328 @@
>> +# 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 ]
> 
> Nit: Is there any reason for the underscore in chacha20_poly1305 and the
> mixed use of dash / underscore in various identifiers throughout the
> spec? The YNL convention is to use dashes throughout.

No real reason, I must have not realized that I Was mixing the two.
I will convert them all to dashes.


> 
>> +  -
>> +    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: sockaddr-remote
>> +        type: binary
>> +        doc: |
>> +          The sockaddr_in/in6 object identifying the remote address/port of the
>> +          peer
> 
> The use of structs as attribute values is strongly discouraged. There
> should be separate attributes for port and ipv[46]-address.
> 
> https://docs.kernel.org/userspace-api/netlink/intro.html#fixed-metadata-and-structures

Thanks a lot for the pointer! I didn't know that.
This means I have to change the netlink attributes as per your 
suggestion (address, port, scope_id) and recreate the sockaddr on the 
kernel side.

> 
>> +      -
>> +        name: socket
>> +        type: u32
>> +        doc: The socket to be used to communicate with the peer
>> +      -
>> +        name: vpn-ipv4
>> +        type: u32
>> +        doc: The IPv4 assigned to the peer by the server
> 
> nit: IPv4 address

ACK

> 
>> +        display-hint: ipv4
>> +      -
>> +        name: vpn-ipv6
>> +        type: binary
>> +        doc: The IPv6 assigned to the peer by the server
> 
> nit: IPv6 address

ACK

> 
>> +        display-hint: ipv6
>> +        checks:
>> +          exact-len: 16
>> +      -
>> +        name: local-ip
>> +        type: binary
>> +        doc: The local IP to be used to send packets to the peer (UDP only)
>> +        checks:
>> +          max-len: 16
> 
> It might be better to have separate attrs fopr local-ipv4 and
> local-ipv6, to be consistent with vpn-ipv4 / vpn-ipv6

while it is possible for a peer to be dual stack and have both an IPv4 
and IPv6 address assigned to the VPN tunnel, the local transport 
endpoint can only be one (either v4 or v6).
This is why we have only one local_ip.
Does it make sense?

> 
>> +      -
>> +        name: local-port
>> +        type: u32
>> +        doc: The local port to be used to send packets to the peer (UDP only)
>> +        checks:
>> +          min: 1
>> +          max: u16-max
>> +      -
>> +        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: keyconf
>> +        type: nest
>> +        doc: Peer specific cipher configuration
>> +        nested-attributes: keyconf
> 
> Perhaps keyconf should just be used as a top-level attribute-set. The
> only attr you'd need to duplicate would be peer-id? There are separate
> ops for setting peers and for key configuration, right?

This is indeed a good point.
Yes, SET_PEER and SET_KEY are separate ops.

I could go with SET_PEER only, and let the user specify a keyconf within 
a peer (like now).

Or I could keep to SET_KEY, but then do as you suggest and move KEYCONF 
to the root level.

Is there any preferred approach?

> 
>> +      -
>> +        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: slot
>> +        type: u32
>> +        doc: The slot where the key should be stored
>> +        enum: key-slot
>> +      -
>> +        name: key-id
>> +        doc: |
>> +          The unique ID for 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
>> +
>> +operations:
>> +  list:
>> +    -
>> +      name: new-iface
> 
> This might be better called 'dev' or 'link' to be consistent with the
> existing netlink UAPIs.

hm ok, then I think I will s/IFACE/DEV/

> 
>> +      attribute-set: ovpn
>> +      flags: [ admin-perm ]
>> +      doc: Create a new interface
>> +      do:
>> +        request:
>> +          attributes:
>> +            - ifname
>> +            - mode
>> +        reply:
>> +          attributes:
>> +            - ifname
>> +            - ifindex
>> +    -
>> +      name: del-iface
>> +      attribute-set: ovpn
>> +      flags: [ admin-perm ]
>> +      doc: Delete existing interface
>> +      do:
>> +        pre: ovpn-nl-pre-doit
>> +        post: ovpn-nl-post-doit
>> +        request:
>> +          attributes:
>> +            - ifindex
>> +    -
>> +      name: set-peer
>> +      attribute-set: ovpn
>> +      flags: [ admin-perm ]
>> +      doc: Add or modify a remote peer
>> +      do:
>> +        pre: ovpn-nl-pre-doit
>> +        post: ovpn-nl-post-doit
>> +        request:
>> +          attributes:
>> +            - ifindex
>> +            - peer
>> +    -
>> +      name: get-peer
>> +      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: del-peer
>> +      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
> 
> I think you need to add an op for 'del-peer-notify' to specify the
> notification, not reuse the 'del-peer' command.

my idea was to use CMD_DEL_PEER and then send back a very very short 
PEER object.
I took inspiration from nl80211 that sends CMD_NEW_STATION and 
CMD_DEL_STATION when a wifi host connects or disconnect. In that case 
the full STATION object is also delivered (maybe I should do the same?)

Or is there some other technical reason for not reusing CMD_DEL_PEER?

> 
>> +    -
>> +      name: set-key
>> +      attribute-set: ovpn
>> +      flags: [ admin-perm ]
>> +      doc: Add or modify a cipher key for a specific peer
>> +      do:
>> +        pre: ovpn-nl-pre-doit
>> +        post: ovpn-nl-post-doit
>> +        request:
>> +          attributes:
>> +            - ifindex
>> +            - peer
>> +    -
>> +      name: swap-keys
>> +      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
>> +            - peer
> 
> Same for swap-keys notifications.

Yeah, here I can understand. My rationale was: tell userspace that now 
we truly need a SWAP_KEYS. Do you think this can create problems/confusion?

> 
>> +    -
>> +      name: del-key
>> +      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
>> +            - peer
>> +
>> +mcast-groups:
>> +  list:
>> +    -
>> +      name: peers

Thanks a lot for your comments, Donald!

Regards,
Donald Hunter Sept. 18, 2024, 10:07 a.m. UTC | #3
Antonio Quartulli <antonio@openvpn.net> writes:
>>> +      -
>>> +        name: local-ip
>>> +        type: binary
>>> +        doc: The local IP to be used to send packets to the peer (UDP only)
>>> +        checks:
>>> +          max-len: 16
>> It might be better to have separate attrs fopr local-ipv4 and
>> local-ipv6, to be consistent with vpn-ipv4 / vpn-ipv6
>
> while it is possible for a peer to be dual stack and have both an IPv4 and IPv6 address assigned
> to the VPN tunnel, the local transport endpoint can only be one (either v4 or v6).
> This is why we have only one local_ip.
> Does it make sense?

I was thinking that the two attributes would be mutually exclusive. You
could accept local-ipv4 OR local-ipv6. If both are provided then you can
report an extack error.

>>
>>> +      -
>>> +        name: keyconf
>>> +        type: nest
>>> +        doc: Peer specific cipher configuration
>>> +        nested-attributes: keyconf
>> Perhaps keyconf should just be used as a top-level attribute-set. The
>> only attr you'd need to duplicate would be peer-id? There are separate
>> ops for setting peers and for key configuration, right?
>
> This is indeed a good point.
> Yes, SET_PEER and SET_KEY are separate ops.
>
> I could go with SET_PEER only, and let the user specify a keyconf within a peer (like now).
>
> Or I could keep to SET_KEY, but then do as you suggest and move KEYCONF to the root level.
>
> Is there any preferred approach?

I liked the separate ops for key management because the sematics are
explicit and it is very obvious that there is no op for reading keys. If
you also keep keyconf attrs separate from the peer attrs then it would be
obvious that the peer ops would never expose any keyconf attrs.

>>
>>> +    -
>>> +      name: del-peer
>>> +      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
>> I think you need to add an op for 'del-peer-notify' to specify the
>> notification, not reuse the 'del-peer' command.
>
> my idea was to use CMD_DEL_PEER and then send back a very very short PEER object.
> I took inspiration from nl80211 that sends CMD_NEW_STATION and CMD_DEL_STATION when a wifi host
> connects or disconnect. In that case the full STATION object is also delivered (maybe I should
> do the same?)
>
> Or is there some other technical reason for not reusing CMD_DEL_PEER?

nl80211 is maybe not a good example to follow because it predates the
ynl specs and code generation. The netdev.yaml spec is a good example of
a modern genetlink spec. It specifies ops for 'dev-add-ntf' and
'dev-del-ntf' that both reuse the definition from 'dev-get' with the
'notify: dev-get' attribute:

    -
      name: dev-get
      doc: Get / dump information about a netdev.
      attribute-set: dev
      do:
        request:
          attributes:
            - ifindex
        reply: &dev-all
          attributes:
            - ifindex
            - xdp-features
            - xdp-zc-max-segs
            - xdp-rx-metadata-features
            - xsk-features
      dump:
        reply: *dev-all
    -
      name: dev-add-ntf
      doc: Notification about device appearing.
      notify: dev-get
      mcgrp: mgmt
    -
      name: dev-del-ntf
      doc: Notification about device disappearing.
      notify: dev-get
      mcgrp: mgmt

The notify ops get distinct ids so they should never be confused with
normal command responses.

>> 
>>> +    -
>>> +      name: set-key
>>> +      attribute-set: ovpn
>>> +      flags: [ admin-perm ]
>>> +      doc: Add or modify a cipher key for a specific peer
>>> +      do:
>>> +        pre: ovpn-nl-pre-doit
>>> +        post: ovpn-nl-post-doit
>>> +        request:
>>> +          attributes:
>>> +            - ifindex
>>> +            - peer
>>> +    -
>>> +      name: swap-keys
>>> +      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
>>> +            - peer
>> Same for swap-keys notifications.
>
> Yeah, here I can understand. My rationale was: tell userspace that now we truly need a
> SWAP_KEYS. Do you think this can create problems/confusion?

Right, so this is a notification to user space that it is time to swap
keys, not that a swap-keys operation has happened? If the payload is
unique to this notification then you should probably use the 'event' op
format. For example:

    -
      name: swap-keys-ntf
      doc: Notify user space that a swap-keys op is due.
      attribute-set: ovpn
      event:
        attributes:
          - ifindex
          - peer
      mcgrp: peers

>> 
>>> +    -
>>> +      name: del-key
>>> +      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
>>> +            - peer
>>> +
>>> +mcast-groups:
>>> +  list:
>>> +    -
>>> +      name: peers
>
> Thanks a lot for your comments, Donald!
>
> Regards,
Antonio Quartulli Sept. 18, 2024, 11:16 a.m. UTC | #4
On 18/09/2024 12:07, Donald Hunter wrote:
> Antonio Quartulli <antonio@openvpn.net> writes:
>>>> +      -
>>>> +        name: local-ip
>>>> +        type: binary
>>>> +        doc: The local IP to be used to send packets to the peer (UDP only)
>>>> +        checks:
>>>> +          max-len: 16
>>> It might be better to have separate attrs fopr local-ipv4 and
>>> local-ipv6, to be consistent with vpn-ipv4 / vpn-ipv6
>>
>> while it is possible for a peer to be dual stack and have both an IPv4 and IPv6 address assigned
>> to the VPN tunnel, the local transport endpoint can only be one (either v4 or v6).
>> This is why we have only one local_ip.
>> Does it make sense?
> 
> I was thinking that the two attributes would be mutually exclusive. You
> could accept local-ipv4 OR local-ipv6. If both are provided then you can
> report an extack error.

Ok then, I'll split the local-ip in two attrs.

It also gets cleaner as we have an explicit type definition, while right 
now we infer the type from the data length.

> 
>>>
>>>> +      -
>>>> +        name: keyconf
>>>> +        type: nest
>>>> +        doc: Peer specific cipher configuration
>>>> +        nested-attributes: keyconf
>>> Perhaps keyconf should just be used as a top-level attribute-set. The
>>> only attr you'd need to duplicate would be peer-id? There are separate
>>> ops for setting peers and for key configuration, right?
>>
>> This is indeed a good point.
>> Yes, SET_PEER and SET_KEY are separate ops.
>>
>> I could go with SET_PEER only, and let the user specify a keyconf within a peer (like now).
>>
>> Or I could keep to SET_KEY, but then do as you suggest and move KEYCONF to the root level.
>>
>> Is there any preferred approach?
> 
> I liked the separate ops for key management because the sematics are
> explicit and it is very obvious that there is no op for reading keys. If
> you also keep keyconf attrs separate from the peer attrs then it would be
> obvious that the peer ops would never expose any keyconf attrs.

Ok, will move KEYCONF to the root level and will duplicate the PEER_ID.

> 
>>>
>>>> +    -
>>>> +      name: del-peer
>>>> +      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
>>> I think you need to add an op for 'del-peer-notify' to specify the
>>> notification, not reuse the 'del-peer' command.
>>
>> my idea was to use CMD_DEL_PEER and then send back a very very short PEER object.
>> I took inspiration from nl80211 that sends CMD_NEW_STATION and CMD_DEL_STATION when a wifi host
>> connects or disconnect. In that case the full STATION object is also delivered (maybe I should
>> do the same?)
>>
>> Or is there some other technical reason for not reusing CMD_DEL_PEER?
> 
> nl80211 is maybe not a good example to follow because it predates the
> ynl specs and code generation. The netdev.yaml spec is a good example of
> a modern genetlink spec. It specifies ops for 'dev-add-ntf' and
> 'dev-del-ntf' that both reuse the definition from 'dev-get' with the
> 'notify: dev-get' attribute:
> 
>      -
>        name: dev-get
>        doc: Get / dump information about a netdev.
>        attribute-set: dev
>        do:
>          request:
>            attributes:
>              - ifindex
>          reply: &dev-all
>            attributes:
>              - ifindex
>              - xdp-features
>              - xdp-zc-max-segs
>              - xdp-rx-metadata-features
>              - xsk-features
>        dump:
>          reply: *dev-all
>      -
>        name: dev-add-ntf
>        doc: Notification about device appearing.
>        notify: dev-get
>        mcgrp: mgmt
>      -
>        name: dev-del-ntf
>        doc: Notification about device disappearing.
>        notify: dev-get
>        mcgrp: mgmt
> 
> The notify ops get distinct ids so they should never be confused with
> normal command responses.

Interesting. I will do the same then.

> 
>>>
>>>> +    -
>>>> +      name: set-key
>>>> +      attribute-set: ovpn
>>>> +      flags: [ admin-perm ]
>>>> +      doc: Add or modify a cipher key for a specific peer
>>>> +      do:
>>>> +        pre: ovpn-nl-pre-doit
>>>> +        post: ovpn-nl-post-doit
>>>> +        request:
>>>> +          attributes:
>>>> +            - ifindex
>>>> +            - peer
>>>> +    -
>>>> +      name: swap-keys
>>>> +      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
>>>> +            - peer
>>> Same for swap-keys notifications.
>>
>> Yeah, here I can understand. My rationale was: tell userspace that now we truly need a
>> SWAP_KEYS. Do you think this can create problems/confusion?
> 
> Right, so this is a notification to user space that it is time to swap
> keys, not that a swap-keys operation has happened?

Correct. It is delivered when the current key cannot be used anymore and 
we need userspace to inject a new one.

> If the payload is
> unique to this notification then you should probably use the 'event' op
> format. For example:
> 
>      -
>        name: swap-keys-ntf
>        doc: Notify user space that a swap-keys op is due.
>        attribute-set: ovpn
>        event:
>          attributes:
>            - ifindex
>            - peer
>        mcgrp: peers

make sense. Will create the new op.
Since we're moving the KEYCONF to the root level, we can just send that 
instead of the PEER.

> 
>>>
>>>> +    -
>>>> +      name: del-key
>>>> +      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
>>>> +            - peer
>>>> +
>>>> +mcast-groups:
>>>> +  list:
>>>> +    -
>>>> +      name: peers

Thanks!
Sergey Ryazanov Sept. 22, 2024, 10:24 p.m. UTC | #5
Hello Antonio, Donald,

On 18.09.2024 14:16, Antonio Quartulli wrote:
> On 18/09/2024 12:07, Donald Hunter wrote:
>> Antonio Quartulli <antonio@openvpn.net> writes:
>>>>> +      -
>>>>> +        name: local-ip
>>>>> +        type: binary
>>>>> +        doc: The local IP to be used to send packets to the peer 
>>>>> (UDP only)
>>>>> +        checks:
>>>>> +          max-len: 16
>>>> It might be better to have separate attrs fopr local-ipv4 and
>>>> local-ipv6, to be consistent with vpn-ipv4 / vpn-ipv6
>>>
>>> while it is possible for a peer to be dual stack and have both an 
>>> IPv4 and IPv6 address assigned
>>> to the VPN tunnel, the local transport endpoint can only be one 
>>> (either v4 or v6).
>>> This is why we have only one local_ip.
>>> Does it make sense?
>>
>> I was thinking that the two attributes would be mutually exclusive. You
>> could accept local-ipv4 OR local-ipv6. If both are provided then you can
>> report an extack error.
> 
> Ok then, I'll split the local-ip in two attrs.
> 
> It also gets cleaner as we have an explicit type definition, while right 
> now we infer the type from the data length.
> 
>>
>>>>
>>>>> +      -
>>>>> +        name: keyconf
>>>>> +        type: nest
>>>>> +        doc: Peer specific cipher configuration
>>>>> +        nested-attributes: keyconf
>>>> Perhaps keyconf should just be used as a top-level attribute-set. The
>>>> only attr you'd need to duplicate would be peer-id? There are separate
>>>> ops for setting peers and for key configuration, right?
>>>
>>> This is indeed a good point.
>>> Yes, SET_PEER and SET_KEY are separate ops.
>>>
>>> I could go with SET_PEER only, and let the user specify a keyconf 
>>> within a peer (like now).
>>>
>>> Or I could keep to SET_KEY, but then do as you suggest and move 
>>> KEYCONF to the root level.
>>>
>>> Is there any preferred approach?
>>
>> I liked the separate ops for key management because the sematics are
>> explicit and it is very obvious that there is no op for reading keys. If
>> you also keep keyconf attrs separate from the peer attrs then it would be
>> obvious that the peer ops would never expose any keyconf attrs.
> 
> Ok, will move KEYCONF to the root level and will duplicate the PEER_ID.

Nice idea! A was about to suggest the same. Besides making semantic 
simple, what somehow subjective, it should make parsing way simple. Two 
nested attributes parsing calls will be saved.

--
Sergey
Sergey Ryazanov Sept. 22, 2024, 11:20 p.m. UTC | #6
On 17.09.2024 04:07, Antonio Quartulli wrote:
> 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 | 328 ++++++++++++++++++++++++++
>   MAINTAINERS                           |   1 +
>   drivers/net/ovpn/Makefile             |   2 +
>   drivers/net/ovpn/main.c               |  14 ++
>   drivers/net/ovpn/netlink-gen.c        | 206 ++++++++++++++++
>   drivers/net/ovpn/netlink-gen.h        |  41 ++++
>   drivers/net/ovpn/netlink.c            | 153 ++++++++++++
>   drivers/net/ovpn/netlink.h            |  15 ++
>   drivers/net/ovpn/ovpnstruct.h         |  21 ++
>   include/uapi/linux/ovpn.h             | 108 +++++++++
>   10 files changed, 889 insertions(+)
>   create mode 100644 Documentation/netlink/specs/ovpn.yaml
>   create mode 100644 drivers/net/ovpn/netlink-gen.c
>   create mode 100644 drivers/net/ovpn/netlink-gen.h
>   create mode 100644 drivers/net/ovpn/netlink.c
>   create mode 100644 drivers/net/ovpn/netlink.h
>   create mode 100644 drivers/net/ovpn/ovpnstruct.h
>   create mode 100644 include/uapi/linux/ovpn.h
> 
> diff --git a/Documentation/netlink/specs/ovpn.yaml b/Documentation/netlink/specs/ovpn.yaml
> new file mode 100644
> index 000000000000..456ac3747d27
> --- /dev/null
> +++ b/Documentation/netlink/specs/ovpn.yaml
> @@ -0,0 +1,328 @@
> +# 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: sockaddr-remote
> +        type: binary
> +        doc: |
> +          The sockaddr_in/in6 object identifying the remote address/port of the
> +          peer
> +      -
> +        name: socket
> +        type: u32
> +        doc: The socket to be used to communicate with the peer
> +      -
> +        name: vpn-ipv4
> +        type: u32
> +        doc: The IPv4 assigned to the peer by the server
> +        display-hint: ipv4
> +      -
> +        name: vpn-ipv6
> +        type: binary
> +        doc: The IPv6 assigned to the peer by the server
> +        display-hint: ipv6
> +        checks:
> +          exact-len: 16
> +      -
> +        name: local-ip
> +        type: binary
> +        doc: The local IP to be used to send packets to the peer (UDP only)
> +        checks:
> +          max-len: 16
> +      -
> +        name: local-port
> +        type: u32
> +        doc: The local port to be used to send packets to the peer (UDP only)
> +        checks:
> +          min: 1
> +          max: u16-max
> +      -
> +        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: keyconf
> +        type: nest
> +        doc: Peer specific cipher configuration
> +        nested-attributes: keyconf
> +      -
> +        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: slot
> +        type: u32
> +        doc: The slot where the key should be stored
> +        enum: key-slot
> +      -
> +        name: key-id
> +        doc: |
> +          The unique ID for 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
> +
> +operations:
> +  list:
> +    -
> +      name: new-iface
> +      attribute-set: ovpn
> +      flags: [ admin-perm ]
> +      doc: Create a new interface
> +      do:
> +        request:
> +          attributes:
> +            - ifname
> +            - mode
> +        reply:
> +          attributes:
> +            - ifname
> +            - ifindex
> +    -
> +      name: del-iface
> +      attribute-set: ovpn
> +      flags: [ admin-perm ]
> +      doc: Delete existing interface
> +      do:
> +        pre: ovpn-nl-pre-doit
> +        post: ovpn-nl-post-doit
> +        request:
> +          attributes:
> +            - ifindex
> +    -
> +      name: set-peer
> +      attribute-set: ovpn
> +      flags: [ admin-perm ]
> +      doc: Add or modify a remote peer

As Donald already mentioned, the typical approach to manage objects via 
Netlink is to provide an interface with four commands: New, Set, Get, 
Del. Here, peer created implicitely using the "set" comand. Out of 
curiosity, what the reason to create peers in the such way?

Is the reason to create keys also implicitly same?

> +      do:
> +        pre: ovpn-nl-pre-doit
> +        post: ovpn-nl-post-doit
> +        request:
> +          attributes:
> +            - ifindex
> +            - peer
> +    -
> +      name: get-peer
> +      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: del-peer
> +      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: set-key
> +      attribute-set: ovpn
> +      flags: [ admin-perm ]
> +      doc: Add or modify a cipher key for a specific peer
> +      do:
> +        pre: ovpn-nl-pre-doit
> +        post: ovpn-nl-post-doit
> +        request:
> +          attributes:
> +            - ifindex
> +            - peer
> +    -
> +      name: swap-keys
> +      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
> +            - peer
> +    -
> +      name: del-key
> +      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
> +            - peer
> +

--
Sergey
Antonio Quartulli Sept. 23, 2024, 12:59 p.m. UTC | #7
On 23/09/2024 01:20, Sergey Ryazanov wrote:
> On 17.09.2024 04:07, Antonio Quartulli wrote:
>> +    -
>> +      name: set-peer
>> +      attribute-set: ovpn
>> +      flags: [ admin-perm ]
>> +      doc: Add or modify a remote peer
> 
> As Donald already mentioned, the typical approach to manage objects via 
> Netlink is to provide an interface with four commands: New, Set, Get, 
> Del. Here, peer created implicitely using the "set" comand. Out of 
> curiosity, what the reason to create peers in the such way?

To be honest, I just wanted to keep the API as concise as possible and 
having ADD and SET looked like duplicating methods, from a conceptual 
perspective.

What userspace wants is "ensure we have a peer with ID X and these 
attributes". If this ID was already known is not extremely important.

I can understand in other contexts knowing if an object already exists 
can be crucial.

> 
> Is the reason to create keys also implicitly same?

basically yes: userspace tells kernelspace "this is what I have 
configured in my slots - make sure to have the same"
(this statement also goes back to the other reply I have sent regarding 
changing the KEY APIs)

Cheers,

> 
>> +      do:
>> +        pre: ovpn-nl-pre-doit
>> +        post: ovpn-nl-post-doit
>> +        request:
>> +          attributes:
>> +            - ifindex
>> +            - peer
>> +    -
>> +      name: get-peer
>> +      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: del-peer
>> +      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: set-key
>> +      attribute-set: ovpn
>> +      flags: [ admin-perm ]
>> +      doc: Add or modify a cipher key for a specific peer
>> +      do:
>> +        pre: ovpn-nl-pre-doit
>> +        post: ovpn-nl-post-doit
>> +        request:
>> +          attributes:
>> +            - ifindex
>> +            - peer
>> +    -
>> +      name: swap-keys
>> +      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
>> +            - peer
>> +    -
>> +      name: del-key
>> +      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
>> +            - peer
>> +
> 
> -- 
> Sergey
>
Sergey Ryazanov Sept. 24, 2024, 10:10 p.m. UTC | #8
Hi Antonio,

On 23.09.2024 15:59, Antonio Quartulli wrote:
> On 23/09/2024 01:20, Sergey Ryazanov wrote:
>> On 17.09.2024 04:07, Antonio Quartulli wrote:
>>> +    -
>>> +      name: set-peer
>>> +      attribute-set: ovpn
>>> +      flags: [ admin-perm ]
>>> +      doc: Add or modify a remote peer
>>
>> As Donald already mentioned, the typical approach to manage objects 
>> via Netlink is to provide an interface with four commands: New, Set, 
>> Get, Del. Here, peer created implicitely using the "set" comand. Out 
>> of curiosity, what the reason to create peers in the such way?
> 
> To be honest, I just wanted to keep the API as concise as possible and 
> having ADD and SET looked like duplicating methods, from a conceptual 
> perspective.

Could you elaborate, what is wrong with separated NEW and SET method 
conceptually?

 From the implementation point of view I can see that both methods can 
setup a same set of object properties. What can be resolved using a 
shared (between NEW and SET) peer configuration method.

> What userspace wants is "ensure we have a peer with ID X and these 
> attributes". If this ID was already known is not extremely important.
> 
> I can understand in other contexts knowing if an object already exists 
> can be crucial.

Looks like you want a "self synchronizing" API that automatically 
recovers synchronization between userspace and kernel.

On one hand this approach can mask potential bug. E.g. management 
application assumes that a peer was not configured and trying to 
configure it and kernel quietly reconfigure earlier known peer. Shall we 
in that case loudly inform everyone that something already went wrong?

On another hand, I see that current implementation does not do this. The 
SET method handler works differently depending on prior peer existence. 
The SET method will not allow an existing peer reconfiguration since it 
will trigger error due to inability to update "VPN" IPv4/IPv6 address. 
So looks like we have two different methods merged into the single 
function with complex behaviour.

BTW, if you want an option to recreate a peer, did you consider the 
NLM_F_REPLACE flag support in the NEW method?

>> Is the reason to create keys also implicitly same?
> 
> basically yes: userspace tells kernelspace "this is what I have 
> configured in my slots - make sure to have the same"
> (this statement also goes back to the other reply I have sent regarding 
> changing the KEY APIs)

If we save the current conception of slots, then yes it make sense.

>>> +      do:
>>> +        pre: ovpn-nl-pre-doit
>>> +        post: ovpn-nl-post-doit
>>> +        request:
>>> +          attributes:
>>> +            - ifindex
>>> +            - peer
>>> +    -
>>> +      name: get-peer
>>> +      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: del-peer
>>> +      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: set-key
>>> +      attribute-set: ovpn
>>> +      flags: [ admin-perm ]
>>> +      doc: Add or modify a cipher key for a specific peer
>>> +      do:
>>> +        pre: ovpn-nl-pre-doit
>>> +        post: ovpn-nl-post-doit
>>> +        request:
>>> +          attributes:
>>> +            - ifindex
>>> +            - peer
>>> +    -
>>> +      name: swap-keys
>>> +      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
>>> +            - peer
>>> +    -
>>> +      name: del-key
>>> +      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
>>> +            - peer
>>> +

--
Sergey
Antonio Quartulli Sept. 25, 2024, 12:01 a.m. UTC | #9
On 25/09/2024 00:10, Sergey Ryazanov wrote:
> Hi Antonio,
> 
> On 23.09.2024 15:59, Antonio Quartulli wrote:
>> On 23/09/2024 01:20, Sergey Ryazanov wrote:
>>> On 17.09.2024 04:07, Antonio Quartulli wrote:
>>>> +    -
>>>> +      name: set-peer
>>>> +      attribute-set: ovpn
>>>> +      flags: [ admin-perm ]
>>>> +      doc: Add or modify a remote peer
>>>
>>> As Donald already mentioned, the typical approach to manage objects 
>>> via Netlink is to provide an interface with four commands: New, Set, 
>>> Get, Del. Here, peer created implicitely using the "set" comand. Out 
>>> of curiosity, what the reason to create peers in the such way?
>>
>> To be honest, I just wanted to keep the API as concise as possible and 
>> having ADD and SET looked like duplicating methods, from a conceptual 
>> perspective.
> 
> Could you elaborate, what is wrong with separated NEW and SET method 
> conceptually?

I don't think it is wrong, I think it's just a matter of preference.
In the ovpn context SET and NEW would kinda do the same thing, except 
that NEW should be called for a non existing peer-id, while SET needs it 
to exist.

Given the above, I just preferred to have one op only and avoid possible 
confusion.

> 
>  From the implementation point of view I can see that both methods can 
> setup a same set of object properties. What can be resolved using a 
> shared (between NEW and SET) peer configuration method.
> 
>> What userspace wants is "ensure we have a peer with ID X and these 
>> attributes". If this ID was already known is not extremely important.
>>
>> I can understand in other contexts knowing if an object already exists 
>> can be crucial.
> 
> Looks like you want a "self synchronizing" API that automatically 
> recovers synchronization between userspace and kernel.

Consider that userspace and kernelspace must always be in sync, 
therefore keeping it easy allows to avoid desynchronization.

> 
> On one hand this approach can mask potential bug. E.g. management 
> application assumes that a peer was not configured and trying to 
> configure it and kernel quietly reconfigure earlier known peer. Shall we 
> in that case loudly inform everyone that something already went wrong?

I get your point, but I am not sure this can truly happen, since 
userspace will always issue a DEL_PEER if it believes the peer is 
gone/should go.

Assuming this can really be the case, the solution would either be to 
self-destroy everything or to try deleting and re-adding the peer.
That's what the SET would already achieve.
After all, the goal of SET is truly to tell ovpn to mirror what usespace 
knows about a certain peer-id.

> 
> On another hand, I see that current implementation does not do this. The 
> SET method handler works differently depending on prior peer existence. 
> The SET method will not allow an existing peer reconfiguration since it 
> will trigger error due to inability to update "VPN" IPv4/IPv6 address. 
> So looks like we have two different methods merged into the single 
> function with complex behaviour.

This is a leftover that I am going to change in v8.
I will use hlist_nulls to happily rehash peers upon IP change.
This is a must do because we'll want to support client IP change at runtime.

> 
> BTW, if you want an option to recreate a peer, did you consider the 
> NLM_F_REPLACE flag support in the NEW method?

you mean supporting NLM_F_REPLACE in SET_PEER and returning -EEXIST if 
peer-id is known and the flag was not passed?

I am not sure it'd be truly helpful.
I am pretty sure userspace will just end up passing that flag all the 
time :-D "just to be safe, since it doesn't break anything"

Cheers,

> 
>>> Is the reason to create keys also implicitly same?
>>
>> basically yes: userspace tells kernelspace "this is what I have 
>> configured in my slots - make sure to have the same"
>> (this statement also goes back to the other reply I have sent 
>> regarding changing the KEY APIs)
> 
> If we save the current conception of slots, then yes it make sense.
> 
>>>> +      do:
>>>> +        pre: ovpn-nl-pre-doit
>>>> +        post: ovpn-nl-post-doit
>>>> +        request:
>>>> +          attributes:
>>>> +            - ifindex
>>>> +            - peer
>>>> +    -
>>>> +      name: get-peer
>>>> +      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: del-peer
>>>> +      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: set-key
>>>> +      attribute-set: ovpn
>>>> +      flags: [ admin-perm ]
>>>> +      doc: Add or modify a cipher key for a specific peer
>>>> +      do:
>>>> +        pre: ovpn-nl-pre-doit
>>>> +        post: ovpn-nl-post-doit
>>>> +        request:
>>>> +          attributes:
>>>> +            - ifindex
>>>> +            - peer
>>>> +    -
>>>> +      name: swap-keys
>>>> +      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
>>>> +            - peer
>>>> +    -
>>>> +      name: del-key
>>>> +      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
>>>> +            - peer
>>>> +
> 
> -- 
> Sergey
Antonio Quartulli Sept. 25, 2024, 11:36 a.m. UTC | #10
Donald,

On 18/09/2024 12:07, Donald Hunter wrote:
[...]
> nl80211 is maybe not a good example to follow because it predates the
> ynl specs and code generation. The netdev.yaml spec is a good example of
> a modern genetlink spec. It specifies ops for 'dev-add-ntf' and
> 'dev-del-ntf' that both reuse the definition from 'dev-get' with the
> 'notify: dev-get' attribute:
> 
>      -
>        name: dev-get
>        doc: Get / dump information about a netdev.
>        attribute-set: dev
>        do:
>          request:
>            attributes:
>              - ifindex
>          reply: &dev-all
>            attributes:
>              - ifindex
>              - xdp-features
>              - xdp-zc-max-segs
>              - xdp-rx-metadata-features
>              - xsk-features
>        dump:
>          reply: *dev-all
>      -
>        name: dev-add-ntf
>        doc: Notification about device appearing.
>        notify: dev-get
>        mcgrp: mgmt
>      -
>        name: dev-del-ntf
>        doc: Notification about device disappearing.
>        notify: dev-get
>        mcgrp: mgmt
> 
> The notify ops get distinct ids so they should never be confused with
> normal command responses.

I see most (if not all) modules have named ops dev-del/add/get, while in 
ovpn I am going with new/del-dev (action and object are inverted).

Do you think it'd make sense to change all the op names to follow the 
convention used by the other modules?

Cheers,
Donald Hunter Sept. 26, 2024, 3:06 p.m. UTC | #11
On Wed, 25 Sept 2024 at 12:36, Antonio Quartulli <antonio@openvpn.net> wrote:
>
> Donald,
>
> I see most (if not all) modules have named ops dev-del/add/get, while in
> ovpn I am going with new/del-dev (action and object are inverted).
>
> Do you think it'd make sense to change all the op names to follow the
> convention used by the other modules?

It's a good question. I'm not sure there's much consistency for either format:

Total ops: 231
Starts with (new|get|del): 51
Ends with (new|get|del): 63
Exactly (new|get|del): 11

For the legacy and raw specs that I have written, I followed whatever
convention was used for the enums in the UAPI, e.g. getroute from
RTM_GETROUTE. The newer genetlink specs like netdev.yaml mostly favour
the dev-get form so maybe that's the convention we should try to
establish going forward?

Cheers,
Donald.
Antonio Quartulli Sept. 27, 2024, 7:52 a.m. UTC | #12
On 26/09/2024 17:06, Donald Hunter wrote:
> On Wed, 25 Sept 2024 at 12:36, Antonio Quartulli <antonio@openvpn.net> wrote:
>>
>> Donald,
>>
>> I see most (if not all) modules have named ops dev-del/add/get, while in
>> ovpn I am going with new/del-dev (action and object are inverted).
>>
>> Do you think it'd make sense to change all the op names to follow the
>> convention used by the other modules?
> 
> It's a good question. I'm not sure there's much consistency for either format:
> 
> Total ops: 231
> Starts with (new|get|del): 51
> Ends with (new|get|del): 63
> Exactly (new|get|del): 11
> 
> For the legacy and raw specs that I have written, I followed whatever
> convention was used for the enums in the UAPI, e.g. getroute from
> RTM_GETROUTE. The newer genetlink specs like netdev.yaml mostly favour
> the dev-get form so maybe that's the convention we should try to
> establish going forward?

If netdev went with that format, I presume that's where the preference 
is, therefore I'll follow that one.

I'll modify the op names then.

Thanks a lot.

Cheers,

> 
> Cheers,
> Donald.
diff mbox series

Patch

diff --git a/Documentation/netlink/specs/ovpn.yaml b/Documentation/netlink/specs/ovpn.yaml
new file mode 100644
index 000000000000..456ac3747d27
--- /dev/null
+++ b/Documentation/netlink/specs/ovpn.yaml
@@ -0,0 +1,328 @@ 
+# 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: sockaddr-remote
+        type: binary
+        doc: |
+          The sockaddr_in/in6 object identifying the remote address/port of the
+          peer
+      -
+        name: socket
+        type: u32
+        doc: The socket to be used to communicate with the peer
+      -
+        name: vpn-ipv4
+        type: u32
+        doc: The IPv4 assigned to the peer by the server
+        display-hint: ipv4
+      -
+        name: vpn-ipv6
+        type: binary
+        doc: The IPv6 assigned to the peer by the server
+        display-hint: ipv6
+        checks:
+          exact-len: 16
+      -
+        name: local-ip
+        type: binary
+        doc: The local IP to be used to send packets to the peer (UDP only)
+        checks:
+          max-len: 16
+      -
+        name: local-port
+        type: u32
+        doc: The local port to be used to send packets to the peer (UDP only)
+        checks:
+          min: 1
+          max: u16-max
+      -
+        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: keyconf
+        type: nest
+        doc: Peer specific cipher configuration
+        nested-attributes: keyconf
+      -
+        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: slot
+        type: u32
+        doc: The slot where the key should be stored
+        enum: key-slot
+      -
+        name: key-id
+        doc: |
+          The unique ID for 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
+
+operations:
+  list:
+    -
+      name: new-iface
+      attribute-set: ovpn
+      flags: [ admin-perm ]
+      doc: Create a new interface
+      do:
+        request:
+          attributes:
+            - ifname
+            - mode
+        reply:
+          attributes:
+            - ifname
+            - ifindex
+    -
+      name: del-iface
+      attribute-set: ovpn
+      flags: [ admin-perm ]
+      doc: Delete existing interface
+      do:
+        pre: ovpn-nl-pre-doit
+        post: ovpn-nl-post-doit
+        request:
+          attributes:
+            - ifindex
+    -
+      name: set-peer
+      attribute-set: ovpn
+      flags: [ admin-perm ]
+      doc: Add or modify a remote peer
+      do:
+        pre: ovpn-nl-pre-doit
+        post: ovpn-nl-post-doit
+        request:
+          attributes:
+            - ifindex
+            - peer
+    -
+      name: get-peer
+      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: del-peer
+      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: set-key
+      attribute-set: ovpn
+      flags: [ admin-perm ]
+      doc: Add or modify a cipher key for a specific peer
+      do:
+        pre: ovpn-nl-pre-doit
+        post: ovpn-nl-post-doit
+        request:
+          attributes:
+            - ifindex
+            - peer
+    -
+      name: swap-keys
+      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
+            - peer
+    -
+      name: del-key
+      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
+            - peer
+
+mcast-groups:
+  list:
+    -
+      name: peers
diff --git a/MAINTAINERS b/MAINTAINERS
index 53b6350d95be..e6ac09ef5f6e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -17269,6 +17269,7 @@  L:	openvpn-devel@lists.sourceforge.net (moderated for non-subscribers)
 L:	netdev@vger.kernel.org
 S:	Maintained
 F:	drivers/net/ovpn/
+F:	include/uapi/linux/ovpn.h
 
 P54 WIRELESS DRIVER
 M:	Christian Lamparter <chunkeey@googlemail.com>
diff --git a/drivers/net/ovpn/Makefile b/drivers/net/ovpn/Makefile
index 53fb197027d7..201dc001419f 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 8a90319e4600..7c35697cb596 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 */
@@ -34,6 +38,7 @@  bool ovpn_dev_is_valid(const struct net_device *dev)
  * therefore ifaces should be destroyed when exiting a netns
  */
 static struct rtnl_link_ops ovpn_link_ops = {
+	.kind = OVPN_FAMILY_NAME,
 };
 
 static int ovpn_netdev_notifier_call(struct notifier_block *nb,
@@ -86,8 +91,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 +108,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 000000000000..f7c4c448b263
--- /dev/null
+++ b/drivers/net/ovpn/netlink-gen.c
@@ -0,0 +1,206 @@ 
+// 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_peer_local_port_range = {
+	.min	= 1ULL,
+	.max	= 65535ULL,
+};
+
+/* Common nested types */
+const struct nla_policy ovpn_keyconf_nl_policy[OVPN_A_KEYCONF_DECRYPT_DIR + 1] = {
+	[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_SOCKADDR_REMOTE] = { .type = NLA_BINARY, },
+	[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_IP] = NLA_POLICY_MAX_LEN(16),
+	[OVPN_A_PEER_LOCAL_PORT] = NLA_POLICY_FULL_RANGE(NLA_U32, &ovpn_a_peer_local_port_range),
+	[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_KEYCONF] = NLA_POLICY_NESTED(ovpn_keyconf_nl_policy),
+	[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_NEW_IFACE - do */
+static const struct nla_policy ovpn_new_iface_nl_policy[OVPN_A_MODE + 1] = {
+	[OVPN_A_IFNAME] = { .type = NLA_NUL_STRING, },
+	[OVPN_A_MODE] = NLA_POLICY_MAX(NLA_U32, 1),
+};
+
+/* OVPN_CMD_DEL_IFACE - do */
+static const struct nla_policy ovpn_del_iface_nl_policy[OVPN_A_IFINDEX + 1] = {
+	[OVPN_A_IFINDEX] = { .type = NLA_U32, },
+};
+
+/* OVPN_CMD_SET_PEER - do */
+static const struct nla_policy ovpn_set_peer_nl_policy[OVPN_A_PEER + 1] = {
+	[OVPN_A_IFINDEX] = { .type = NLA_U32, },
+	[OVPN_A_PEER] = NLA_POLICY_NESTED(ovpn_peer_nl_policy),
+};
+
+/* OVPN_CMD_GET_PEER - do */
+static const struct nla_policy ovpn_get_peer_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_GET_PEER - dump */
+static const struct nla_policy ovpn_get_peer_dump_nl_policy[OVPN_A_IFINDEX + 1] = {
+	[OVPN_A_IFINDEX] = { .type = NLA_U32, },
+};
+
+/* OVPN_CMD_DEL_PEER - do */
+static const struct nla_policy ovpn_del_peer_nl_policy[OVPN_A_PEER + 1] = {
+	[OVPN_A_IFINDEX] = { .type = NLA_U32, },
+	[OVPN_A_PEER] = NLA_POLICY_NESTED(ovpn_peer_nl_policy),
+};
+
+/* OVPN_CMD_SET_KEY - do */
+static const struct nla_policy ovpn_set_key_nl_policy[OVPN_A_PEER + 1] = {
+	[OVPN_A_IFINDEX] = { .type = NLA_U32, },
+	[OVPN_A_PEER] = NLA_POLICY_NESTED(ovpn_peer_nl_policy),
+};
+
+/* OVPN_CMD_SWAP_KEYS - do */
+static const struct nla_policy ovpn_swap_keys_nl_policy[OVPN_A_PEER + 1] = {
+	[OVPN_A_IFINDEX] = { .type = NLA_U32, },
+	[OVPN_A_PEER] = NLA_POLICY_NESTED(ovpn_peer_nl_policy),
+};
+
+/* OVPN_CMD_DEL_KEY - do */
+static const struct nla_policy ovpn_del_key_nl_policy[OVPN_A_PEER + 1] = {
+	[OVPN_A_IFINDEX] = { .type = NLA_U32, },
+	[OVPN_A_PEER] = NLA_POLICY_NESTED(ovpn_peer_nl_policy),
+};
+
+/* Ops table for ovpn */
+static const struct genl_split_ops ovpn_nl_ops[] = {
+	{
+		.cmd		= OVPN_CMD_NEW_IFACE,
+		.doit		= ovpn_nl_new_iface_doit,
+		.policy		= ovpn_new_iface_nl_policy,
+		.maxattr	= OVPN_A_MODE,
+		.flags		= GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
+	},
+	{
+		.cmd		= OVPN_CMD_DEL_IFACE,
+		.pre_doit	= ovpn_nl_pre_doit,
+		.doit		= ovpn_nl_del_iface_doit,
+		.post_doit	= ovpn_nl_post_doit,
+		.policy		= ovpn_del_iface_nl_policy,
+		.maxattr	= OVPN_A_IFINDEX,
+		.flags		= GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
+	},
+	{
+		.cmd		= OVPN_CMD_SET_PEER,
+		.pre_doit	= ovpn_nl_pre_doit,
+		.doit		= ovpn_nl_set_peer_doit,
+		.post_doit	= ovpn_nl_post_doit,
+		.policy		= ovpn_set_peer_nl_policy,
+		.maxattr	= OVPN_A_PEER,
+		.flags		= GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
+	},
+	{
+		.cmd		= OVPN_CMD_GET_PEER,
+		.pre_doit	= ovpn_nl_pre_doit,
+		.doit		= ovpn_nl_get_peer_doit,
+		.post_doit	= ovpn_nl_post_doit,
+		.policy		= ovpn_get_peer_do_nl_policy,
+		.maxattr	= OVPN_A_PEER,
+		.flags		= GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
+	},
+	{
+		.cmd		= OVPN_CMD_GET_PEER,
+		.dumpit		= ovpn_nl_get_peer_dumpit,
+		.policy		= ovpn_get_peer_dump_nl_policy,
+		.maxattr	= OVPN_A_IFINDEX,
+		.flags		= GENL_ADMIN_PERM | GENL_CMD_CAP_DUMP,
+	},
+	{
+		.cmd		= OVPN_CMD_DEL_PEER,
+		.pre_doit	= ovpn_nl_pre_doit,
+		.doit		= ovpn_nl_del_peer_doit,
+		.post_doit	= ovpn_nl_post_doit,
+		.policy		= ovpn_del_peer_nl_policy,
+		.maxattr	= OVPN_A_PEER,
+		.flags		= GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
+	},
+	{
+		.cmd		= OVPN_CMD_SET_KEY,
+		.pre_doit	= ovpn_nl_pre_doit,
+		.doit		= ovpn_nl_set_key_doit,
+		.post_doit	= ovpn_nl_post_doit,
+		.policy		= ovpn_set_key_nl_policy,
+		.maxattr	= OVPN_A_PEER,
+		.flags		= GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
+	},
+	{
+		.cmd		= OVPN_CMD_SWAP_KEYS,
+		.pre_doit	= ovpn_nl_pre_doit,
+		.doit		= ovpn_nl_swap_keys_doit,
+		.post_doit	= ovpn_nl_post_doit,
+		.policy		= ovpn_swap_keys_nl_policy,
+		.maxattr	= OVPN_A_PEER,
+		.flags		= GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
+	},
+	{
+		.cmd		= OVPN_CMD_DEL_KEY,
+		.pre_doit	= ovpn_nl_pre_doit,
+		.doit		= ovpn_nl_del_key_doit,
+		.post_doit	= ovpn_nl_post_doit,
+		.policy		= ovpn_del_key_nl_policy,
+		.maxattr	= OVPN_A_PEER,
+		.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 000000000000..ce11f74e1b56
--- /dev/null
+++ b/drivers/net/ovpn/netlink-gen.h
@@ -0,0 +1,41 @@ 
+/* 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_new_iface_doit(struct sk_buff *skb, struct genl_info *info);
+int ovpn_nl_del_iface_doit(struct sk_buff *skb, struct genl_info *info);
+int ovpn_nl_set_peer_doit(struct sk_buff *skb, struct genl_info *info);
+int ovpn_nl_get_peer_doit(struct sk_buff *skb, struct genl_info *info);
+int ovpn_nl_get_peer_dumpit(struct sk_buff *skb, struct netlink_callback *cb);
+int ovpn_nl_del_peer_doit(struct sk_buff *skb, struct genl_info *info);
+int ovpn_nl_set_key_doit(struct sk_buff *skb, struct genl_info *info);
+int ovpn_nl_swap_keys_doit(struct sk_buff *skb, struct genl_info *info);
+int ovpn_nl_del_key_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 000000000000..b5b53c06d64a
--- /dev/null
+++ b/drivers/net/ovpn/netlink.c
@@ -0,0 +1,153 @@ 
+// 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_new_iface_doit(struct sk_buff *skb, struct genl_info *info)
+{
+	return -EOPNOTSUPP;
+}
+
+int ovpn_nl_del_iface_doit(struct sk_buff *skb, struct genl_info *info)
+{
+	return -EOPNOTSUPP;
+}
+
+int ovpn_nl_set_peer_doit(struct sk_buff *skb, struct genl_info *info)
+{
+	return -EOPNOTSUPP;
+}
+
+int ovpn_nl_get_peer_doit(struct sk_buff *skb, struct genl_info *info)
+{
+	return -EOPNOTSUPP;
+}
+
+int ovpn_nl_get_peer_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
+{
+	return -EOPNOTSUPP;
+}
+
+int ovpn_nl_del_peer_doit(struct sk_buff *skb, struct genl_info *info)
+{
+	return -EOPNOTSUPP;
+}
+
+int ovpn_nl_set_key_doit(struct sk_buff *skb, struct genl_info *info)
+{
+	return -EOPNOTSUPP;
+}
+
+int ovpn_nl_swap_keys_doit(struct sk_buff *skb, struct genl_info *info)
+{
+	return -EOPNOTSUPP;
+}
+
+int ovpn_nl_del_key_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 000000000000..9e87cf11d1e9
--- /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 000000000000..ff248cad1401
--- /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 000000000000..677a8105b61b
--- /dev/null
+++ b/include/uapi/linux/ovpn.h
@@ -0,0 +1,108 @@ 
+/* 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_SOCKADDR_REMOTE,
+	OVPN_A_PEER_SOCKET,
+	OVPN_A_PEER_VPN_IPV4,
+	OVPN_A_PEER_VPN_IPV6,
+	OVPN_A_PEER_LOCAL_IP,
+	OVPN_A_PEER_LOCAL_PORT,
+	OVPN_A_PEER_KEEPALIVE_INTERVAL,
+	OVPN_A_PEER_KEEPALIVE_TIMEOUT,
+	OVPN_A_PEER_DEL_REASON,
+	OVPN_A_PEER_KEYCONF,
+	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_SLOT = 1,
+	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_MAX,
+	OVPN_A_MAX = (__OVPN_A_MAX - 1)
+};
+
+enum {
+	OVPN_CMD_NEW_IFACE = 1,
+	OVPN_CMD_DEL_IFACE,
+	OVPN_CMD_SET_PEER,
+	OVPN_CMD_GET_PEER,
+	OVPN_CMD_DEL_PEER,
+	OVPN_CMD_SET_KEY,
+	OVPN_CMD_SWAP_KEYS,
+	OVPN_CMD_DEL_KEY,
+
+	__OVPN_CMD_MAX,
+	OVPN_CMD_MAX = (__OVPN_CMD_MAX - 1)
+};
+
+#define OVPN_MCGRP_PEERS	"peers"
+
+#endif /* _UAPI_LINUX_OVPN_H */