Packets received over the socket are forwarded to the user device. Implementation is UDP only. TCP will be added by a later patch. Note: no decryption/decapsulation exists yet, packets are forwarded as they arrive without much processing. Signed-off-by: Antonio Quartulli <antonio@xxxxxxxxxxx> --- drivers/net/ovpn/io.c | 64 ++++++++++++++++- drivers/net/ovpn/io.h | 2 + drivers/net/ovpn/main.c | 16 ++++- drivers/net/ovpn/ovpnpriv.h | 3 + drivers/net/ovpn/proto.h | 50 +++++++++++++- drivers/net/ovpn/socket.c | 13 +++- drivers/net/ovpn/socket.h | 11 ++- drivers/net/ovpn/udp.c | 162 ++++++++++++++++++++++++++++++++++++++++++++ drivers/net/ovpn/udp.h | 2 + net/ipv6/udp.c | 1 + 10 files changed, 319 insertions(+), 5 deletions(-) diff --git a/drivers/net/ovpn/io.c b/drivers/net/ovpn/io.c index 94b466bf2ef70d60d3e60d9820b64877c44f2e51..46ad27e8eb8425f810c7d2b6c63984ea008d90fa 100644 --- a/drivers/net/ovpn/io.c +++ b/drivers/net/ovpn/io.c @@ -9,15 +9,77 @@ #include <linux/netdevice.h> #include <linux/skbuff.h> +#include <net/gro_cells.h> #include <net/gso.h> -#include "io.h" #include "ovpnpriv.h" #include "peer.h" +#include "io.h" +#include "netlink.h" +#include "proto.h" #include "udp.h" #include "skb.h" #include "socket.h" +/* Called after decrypt to write the IP packet to the device. + * This method is expected to manage/free the skb. + */ +static void ovpn_netdev_write(struct ovpn_peer *peer, struct sk_buff *skb) +{ + unsigned int pkt_len; + int ret; + + /* we can't guarantee the packet wasn't corrupted before entering the + * VPN, therefore we give other layers a chance to check that + */ + skb->ip_summed = CHECKSUM_NONE; + + /* skb hash for transport packet no longer valid after decapsulation */ + skb_clear_hash(skb); + + /* post-decrypt scrub -- prepare to inject encapsulated packet onto the + * interface, based on __skb_tunnel_rx() in dst.h + */ + skb->dev = peer->ovpn->dev; + skb_set_queue_mapping(skb, 0); + skb_scrub_packet(skb, true); + + skb_reset_network_header(skb); + skb_reset_transport_header(skb); + skb_reset_inner_headers(skb); + + /* cause packet to be "received" by the interface */ + pkt_len = skb->len; + ret = gro_cells_receive(&peer->ovpn->gro_cells, skb); + if (likely(ret == NET_RX_SUCCESS)) + /* update RX stats with the size of decrypted packet */ + dev_sw_netstats_rx_add(peer->ovpn->dev, pkt_len); +} + +static void ovpn_decrypt_post(struct sk_buff *skb, int ret) +{ + struct ovpn_peer *peer = ovpn_skb_cb(skb)->peer; + + if (unlikely(ret < 0)) + goto drop; + + ovpn_netdev_write(peer, skb); + /* skb is passed to upper layer - don't free it */ + skb = NULL; +drop: + if (unlikely(skb)) + dev_core_stats_rx_dropped_inc(peer->ovpn->dev); + ovpn_peer_put(peer); + kfree_skb(skb); +} + +/* RX path entry point: decrypt packet and forward it to the device */ +void ovpn_recv(struct ovpn_peer *peer, struct sk_buff *skb) +{ + ovpn_skb_cb(skb)->peer = peer; + ovpn_decrypt_post(skb, 0); +} + static void ovpn_encrypt_post(struct sk_buff *skb, int ret) { struct ovpn_peer *peer = ovpn_skb_cb(skb)->peer; diff --git a/drivers/net/ovpn/io.h b/drivers/net/ovpn/io.h index afea5f81f5628dcb9afda9a78974bbf6f2101c13..1cfa66873a2d4840ce57e337f8b4e8143e8b8e79 100644 --- a/drivers/net/ovpn/io.h +++ b/drivers/net/ovpn/io.h @@ -21,4 +21,6 @@ netdev_tx_t ovpn_net_xmit(struct sk_buff *skb, struct net_device *dev); +void ovpn_recv(struct ovpn_peer *peer, struct sk_buff *skb); + #endif /* _NET_OVPN_OVPN_H_ */ diff --git a/drivers/net/ovpn/main.c b/drivers/net/ovpn/main.c index e58739d82da54001a346c38e5c5a882589eb3801..241f2345298ab149d79b2dc04a1e52cfcb52d12c 100644 --- a/drivers/net/ovpn/main.c +++ b/drivers/net/ovpn/main.c @@ -11,6 +11,7 @@ #include <linux/module.h> #include <linux/netdevice.h> #include <linux/inetdevice.h> +#include <net/gro_cells.h> #include <net/ip.h> #include <net/rtnetlink.h> #include <uapi/linux/if_arp.h> @@ -21,10 +22,20 @@ #include "io.h" #include "peer.h" #include "proto.h" +#include "udp.h" static int ovpn_net_init(struct net_device *dev) { - return 0; + struct ovpn_priv *ovpn = netdev_priv(dev); + + return gro_cells_init(&ovpn->gro_cells, dev); +} + +static void ovpn_net_uninit(struct net_device *dev) +{ + struct ovpn_priv *ovpn = netdev_priv(dev); + + gro_cells_destroy(&ovpn->gro_cells); } static int ovpn_net_open(struct net_device *dev) @@ -50,6 +61,7 @@ static int ovpn_net_stop(struct net_device *dev) static const struct net_device_ops ovpn_netdev_ops = { .ndo_init = ovpn_net_init, + .ndo_uninit = ovpn_net_uninit, .ndo_open = ovpn_net_open, .ndo_stop = ovpn_net_stop, .ndo_start_xmit = ovpn_net_xmit, @@ -226,6 +238,8 @@ static int __init ovpn_init(void) goto unreg_rtnl; } + ovpn_udp_init(); + return 0; unreg_rtnl: diff --git a/drivers/net/ovpn/ovpnpriv.h b/drivers/net/ovpn/ovpnpriv.h index fae2682b424b03222a5ce881a4a1b4518a7ff311..9d0640e9c71e7fd494e3d9df155732bd5d82463e 100644 --- a/drivers/net/ovpn/ovpnpriv.h +++ b/drivers/net/ovpn/ovpnpriv.h @@ -10,6 +10,7 @@ #ifndef _NET_OVPN_OVPNSTRUCT_H_ #define _NET_OVPN_OVPNSTRUCT_H_ +#include <net/gro_cells.h> #include <uapi/linux/if_link.h> #include <uapi/linux/ovpn.h> @@ -20,6 +21,7 @@ * @mode: device operation mode (i.e. p2p, mp, ..) * @lock: protect this object * @peer: in P2P mode, this is the only remote peer + * @gro_cells: pointer to the Generic Receive Offload cell */ struct ovpn_priv { struct net_device *dev; @@ -27,6 +29,7 @@ struct ovpn_priv { enum ovpn_mode mode; spinlock_t lock; /* protect writing to the ovpn_priv object */ struct ovpn_peer __rcu *peer; + struct gro_cells gro_cells; }; #endif /* _NET_OVPN_OVPNSTRUCT_H_ */ diff --git a/drivers/net/ovpn/proto.h b/drivers/net/ovpn/proto.h index 5f95a78bebd3702868ffeeab3ea4938e957d568c..591b97a9925fd9b91f996d6d591fac41b1aa6148 100644 --- a/drivers/net/ovpn/proto.h +++ b/drivers/net/ovpn/proto.h @@ -10,6 +10,11 @@ #ifndef _NET_OVPN_PROTO_H_ #define _NET_OVPN_PROTO_H_ +#include "main.h" + +#include <linux/bitfield.h> +#include <linux/skbuff.h> + /* When the OpenVPN protocol is ran in AEAD mode, use * the OpenVPN packet ID as the AEAD nonce: * @@ -34,5 +39,48 @@ #define OVPN_NONCE_WIRE_SIZE (OVPN_NONCE_SIZE - OVPN_NONCE_TAIL_SIZE) #define OVPN_OPCODE_SIZE 4 /* DATA_V2 opcode size */ +#define OVPN_OPCODE_KEYID_MASK 0x07000000 +#define OVPN_OPCODE_PKTTYPE_MASK 0xF8000000 +#define OVPN_OPCODE_PEERID_MASK 0x00FFFFFF + +/* packet opcodes of interest to us */ +#define OVPN_DATA_V1 6 /* data channel v1 packet */ +#define OVPN_DATA_V2 9 /* data channel v2 packet */ + +#define OVPN_PEER_ID_UNDEF 0x00FFFFFF + +/** + * ovpn_opcode_from_skb - extract OP code from skb at specified offset + * @skb: the packet to extract the OP code from + * @offset: the offset in the data buffer where the OP code is located + * + * Note: this function assumes that the skb head was pulled enough + * to access the first 4 bytes. + * + * Return: the OP code + */ +static inline u8 ovpn_opcode_from_skb(const struct sk_buff *skb, u16 offset) +{ + u32 opcode = be32_to_cpu(*(__be32 *)(skb->data + offset)); + + return FIELD_GET(OVPN_OPCODE_PKTTYPE_MASK, opcode); +} + +/** + * ovpn_peer_id_from_skb - extract peer ID from skb at specified offset + * @skb: the packet to extract the OP code from + * @offset: the offset in the data buffer where the OP code is located + * + * Note: this function assumes that the skb head was pulled enough + * to access the first 4 bytes. + * + * Return: the peer ID + */ +static inline u32 ovpn_peer_id_from_skb(const struct sk_buff *skb, u16 offset) +{ + u32 opcode = be32_to_cpu(*(__be32 *)(skb->data + offset)); + + return FIELD_GET(OVPN_OPCODE_PEERID_MASK, opcode); +} -#endif /* _NET_OVPN_PROTO_H_ */ +#endif /* _NET_OVPN_OVPNPROTO_H_ */ diff --git a/drivers/net/ovpn/socket.c b/drivers/net/ovpn/socket.c index a4a8686f2e2995dca163d5b96c6c897b86434967..6de1e8c85a3d35a5d4f179d595d2409aa34cdc01 100644 --- a/drivers/net/ovpn/socket.c +++ b/drivers/net/ovpn/socket.c @@ -23,8 +23,10 @@ static void ovpn_socket_release_kref(struct kref *kref) struct ovpn_socket *sock = container_of(kref, struct ovpn_socket, refcount); - if (sock->sock->sk->sk_protocol == IPPROTO_UDP) + if (sock->sock->sk->sk_protocol == IPPROTO_UDP) { ovpn_udp_socket_detach(sock); + netdev_put(sock->ovpn->dev, &sock->dev_tracker); + } kfree_rcu(sock, rcu); } @@ -200,6 +202,15 @@ struct ovpn_socket *ovpn_socket_new(struct socket *sock, struct ovpn_peer *peer) goto sock_release; } + if (sock->sk->sk_protocol == IPPROTO_UDP) { + /* in UDP we only link the ovpn instance since the socket is + * shared among multiple peers + */ + ovpn_sock->ovpn = peer->ovpn; + netdev_hold(peer->ovpn->dev, &ovpn_sock->dev_tracker, + GFP_KERNEL); + } + rcu_assign_sk_user_data(sock->sk, ovpn_sock); sock_release: release_sock(sock->sk); diff --git a/drivers/net/ovpn/socket.h b/drivers/net/ovpn/socket.h index ade8c94619d7b2f905b5284373dc73f590188399..e5b94afe0cfa59ecd41007779456d083d8c1555f 100644 --- a/drivers/net/ovpn/socket.h +++ b/drivers/net/ovpn/socket.h @@ -20,12 +20,21 @@ struct ovpn_peer; /** * struct ovpn_socket - a kernel socket referenced in the ovpn code * @ovpn: ovpn instance owning this socket (UDP only) + * @dev_tracker: reference tracker for associated dev (UDP only) + * @udp_prot: pointer to the original socket sk_proto (UDP only) * @sock: the low level sock object * @refcount: amount of contexts currently referencing this object * @rcu: member used to schedule RCU destructor callback */ struct ovpn_socket { - struct ovpn_priv *ovpn; + union { + struct { + struct ovpn_priv *ovpn; + netdevice_tracker dev_tracker; + struct proto *udp_prot; + }; + }; + struct socket *sock; struct kref refcount; struct rcu_head rcu; diff --git a/drivers/net/ovpn/udp.c b/drivers/net/ovpn/udp.c index ae76ae6d372f565715a39ee24e3fba14f1c1370e..81f0ec332fc885d53e5a357512628f245ef3774f 100644 --- a/drivers/net/ovpn/udp.c +++ b/drivers/net/ovpn/udp.c @@ -15,6 +15,7 @@ #include <net/dst_cache.h> #include <net/route.h> #include <net/ipv6_stubs.h> +#include <net/transp_v6.h> #include <net/udp.h> #include <net/udp_tunnel.h> @@ -23,9 +24,117 @@ #include "bind.h" #include "io.h" #include "peer.h" +#include "proto.h" #include "socket.h" #include "udp.h" +static struct proto ovpn_udp_prot __ro_after_init; +static struct proto ovpn_udp6_prot __ro_after_init; + +/* Retrieve the corresponding ovpn object from a UDP socket + * rcu_read_lock must be held on entry + */ +static struct ovpn_socket *ovpn_socket_from_udp_sock(struct sock *sk) +{ + struct ovpn_socket *ovpn_sock; + + if (unlikely(READ_ONCE(udp_sk(sk)->encap_type) != UDP_ENCAP_OVPNINUDP)) + return NULL; + + ovpn_sock = rcu_dereference_sk_user_data(sk); + if (unlikely(!ovpn_sock)) + return NULL; + + /* make sure that sk matches our stored transport socket */ + if (unlikely(!ovpn_sock->sock || sk != ovpn_sock->sock->sk)) + return NULL; + + return ovpn_sock; +} + +/** + * ovpn_udp_encap_recv - Start processing a received UDP packet. + * @sk: socket over which the packet was received + * @skb: the received packet + * + * If the first byte of the payload is: + * - DATA_V2 the packet is accepted for further processing, + * - DATA_V1 the packet is dropped as not supported, + * - anything else the packet is forwarded to the UDP stack for + * delivery to user space. + * + * Return: + * 0 if skb was consumed or dropped + * >0 if skb should be passed up to userspace as UDP (packet not consumed) + * <0 if skb should be resubmitted as proto -N (packet not consumed) + */ +static int ovpn_udp_encap_recv(struct sock *sk, struct sk_buff *skb) +{ + struct ovpn_socket *ovpn_sock; + struct ovpn_priv *ovpn; + struct ovpn_peer *peer; + u32 peer_id; + u8 opcode; + + ovpn_sock = ovpn_socket_from_udp_sock(sk); + if (unlikely(!ovpn_sock)) { + net_err_ratelimited("ovpn: %s invoked on non ovpn socket\n", + __func__); + goto drop_noovpn; + } + + ovpn = ovpn_sock->ovpn; + if (unlikely(!ovpn)) { + net_err_ratelimited("ovpn: cannot obtain ovpn object from UDP socket\n"); + goto drop_noovpn; + } + + /* Make sure the first 4 bytes of the skb data buffer after the UDP + * header are accessible. + * They are required to fetch the OP code, the key ID and the peer ID. + */ + if (unlikely(!pskb_may_pull(skb, sizeof(struct udphdr) + + OVPN_OPCODE_SIZE))) { + net_dbg_ratelimited("%s: packet too small from UDP socket\n", + netdev_name(ovpn->dev)); + goto drop; + } + + opcode = ovpn_opcode_from_skb(skb, sizeof(struct udphdr)); + if (unlikely(opcode != OVPN_DATA_V2)) { + /* DATA_V1 is not supported */ + if (opcode == OVPN_DATA_V1) + goto drop; + + /* unknown or control packet: let it bubble up to userspace */ + return 1; + } + + peer_id = ovpn_peer_id_from_skb(skb, sizeof(struct udphdr)); + /* some OpenVPN server implementations send data packets with the + * peer-id set to UNDEF. In this case we skip the peer lookup by peer-id + * and we try with the transport address + */ + if (peer_id == OVPN_PEER_ID_UNDEF) + peer = ovpn_peer_get_by_transp_addr(ovpn, skb); + else + peer = ovpn_peer_get_by_id(ovpn, peer_id); + + if (unlikely(!peer)) + goto drop; + + /* pop off outer UDP header */ + __skb_pull(skb, sizeof(struct udphdr)); + ovpn_recv(peer, skb); + return 0; + +drop: + dev_core_stats_rx_dropped_inc(ovpn->dev); +drop_noovpn: + kfree_skb(skb); + return 0; +} + /** * ovpn_udp4_output - send IPv4 packet over udp socket * @peer: the destination peer @@ -263,6 +372,10 @@ void ovpn_udp_send_skb(struct ovpn_peer *peer, struct socket *sock, int ovpn_udp_socket_attach(struct ovpn_socket *ovpn_sock, struct ovpn_priv *ovpn) { + struct udp_tunnel_sock_cfg cfg = { + .encap_type = UDP_ENCAP_OVPNINUDP, + .encap_rcv = ovpn_udp_encap_recv, + }; struct socket *sock = ovpn_sock->sock; struct ovpn_socket *old_data; int ret; @@ -273,6 +386,14 @@ int ovpn_udp_socket_attach(struct ovpn_socket *ovpn_sock, if (!old_data) { /* socket is currently unused - we can take it */ rcu_read_unlock(); + setup_udp_tunnel_sock(sock_net(sock->sk), sock, &cfg); + + ovpn_sock->udp_prot = sock->sk->sk_prot; + + if (sock->sk->sk_family == AF_INET) + sock->sk->sk_prot = &ovpn_udp_prot; + else + sock->sk->sk_prot = &ovpn_udp6_prot; return 0; } @@ -306,4 +427,45 @@ int ovpn_udp_socket_attach(struct ovpn_socket *ovpn_sock, */ void ovpn_udp_socket_detach(struct ovpn_socket *ovpn_sock) { + struct udp_tunnel_sock_cfg cfg = { }; + + setup_udp_tunnel_sock(sock_net(ovpn_sock->sock->sk), ovpn_sock->sock, + &cfg); + ovpn_sock->sock->sk->sk_prot = ovpn_sock->udp_prot; +} + +static void ovpn_udp_close(struct sock *sk, long timeout) +{ + struct ovpn_socket *sock; + struct ovpn_priv *ovpn; + + rcu_read_lock(); + sock = rcu_dereference_sk_user_data(sk); + if (!sock || !sock->ovpn) { + rcu_read_unlock(); + return; + } + ovpn = sock->ovpn; + rcu_read_unlock(); + + if (ovpn->mode == OVPN_MODE_P2P) + ovpn_peer_release_p2p(ovpn, sk, + OVPN_DEL_PEER_REASON_TRANSPORT_DISCONNECT); + sock->udp_prot->close(sk, timeout); +} + +static void ovpn_udp_build_protos(struct proto *new_prot, + const struct proto *orig_prot) +{ + memcpy(new_prot, orig_prot, sizeof(*new_prot)); + new_prot->close = ovpn_udp_close; +} + +void __init ovpn_udp_init(void) +{ + ovpn_udp_build_protos(&ovpn_udp_prot, &udp_prot); + +#if IS_ENABLED(CONFIG_IPV6) + ovpn_udp_build_protos(&ovpn_udp6_prot, &udpv6_prot); +#endif } diff --git a/drivers/net/ovpn/udp.h b/drivers/net/ovpn/udp.h index 9994eb6e04283247d8ffc729966345810f84b22b..ac71f2bcb2a774eedf31fa1e17a5e0d7f67c479f 100644 --- a/drivers/net/ovpn/udp.h +++ b/drivers/net/ovpn/udp.h @@ -15,6 +15,8 @@ struct ovpn_peer; struct ovpn_priv; struct socket; +void __init ovpn_udp_init(void); + int ovpn_udp_socket_attach(struct ovpn_socket *ovpn_sock, struct ovpn_priv *ovpn); void ovpn_udp_socket_detach(struct ovpn_socket *ovpn_sock); diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 3a0d6c5a8286b1685e8a1dec50365fe392ab9a87..8793b27273d6004dc3e9fbd459a076be822bdfea 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -1933,6 +1933,7 @@ struct proto udpv6_prot = { .h.udp_table = NULL, .diag_destroy = udp_abort, }; +EXPORT_SYMBOL_GPL(udpv6_prot); static struct inet_protosw udpv6_protosw = { .type = SOCK_DGRAM, -- 2.45.3