Message ID | 20240827120805.13681-12-antonio@openvpn.net (mailing list archive) |
---|---|
State | Changes Requested |
Delegated to: | Netdev Maintainers |
Headers | show |
Series | Introducing OpenVPN Data Channel Offload | expand |
2024-08-27, 14:07:51 +0200, Antonio Quartulli wrote: > +static void ovpn_netdev_write(struct ovpn_peer *peer, struct sk_buff *skb) > +{ > + /* 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_probe_transport_header(skb); > + skb_reset_inner_headers(skb); > + > + memset(skb->cb, 0, sizeof(skb->cb)); > + > + /* cause packet to be "received" by the interface */ > + if (likely(gro_cells_receive(&peer->ovpn->gro_cells, > + skb) == NET_RX_SUCCESS)) > + /* update RX stats with the size of decrypted packet */ > + dev_sw_netstats_rx_add(peer->ovpn->dev, skb->len); I don't think accessing skb->len after passing the skb to gro_cells_receive is safe, see c7cc9200e9b4 ("macsec: avoid use-after-free in macsec_handle_frame()") [...] > static void ovpn_struct_free(struct net_device *net) > { > + struct ovpn_struct *ovpn = netdev_priv(net); > + > + gro_cells_destroy(&ovpn->gro_cells); > + rcu_barrier(); What's the purpose of this rcu_barrier? I expect it in module_exit, not when removing one netdevice. > diff --git a/drivers/net/ovpn/skb.h b/drivers/net/ovpn/skb.h > index 7966a10d915f..e070fe6f448c 100644 > --- a/drivers/net/ovpn/skb.h > +++ b/drivers/net/ovpn/skb.h > @@ -18,10 +18,7 @@ > #include <linux/types.h> > > struct ovpn_cb { > - struct aead_request *req; > struct ovpn_peer *peer; > - struct ovpn_crypto_key_slot *ks; > - unsigned int payload_offset; Squashed into the wrong patch? [...] > +struct ovpn_struct *ovpn_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); [1] > + 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; > +} > +static int ovpn_udp_encap_recv(struct sock *sk, struct sk_buff *skb) > +{ > + struct ovpn_peer *peer = NULL; > + struct ovpn_struct *ovpn; > + u32 peer_id; > + u8 opcode; > + > + ovpn = ovpn_from_udp_sock(sk); > + if (unlikely(!ovpn)) { > + net_err_ratelimited("%s: cannot obtain ovpn object from UDP socket\n", > + __func__); > + goto drop; > + } [...] > + /* pop off outer UDP header */ > + __skb_pull(skb, sizeof(struct udphdr)); > + ovpn_recv(peer, skb); > + return 0; > + > +drop: > + if (peer) > + ovpn_peer_put(peer); > + dev_core_stats_rx_dropped_inc(ovpn->dev); If we get here from the first goto, ovpn is NULL. You could add a drop_noovpn label right here to just do the free+return. > + kfree_skb(skb); > + return 0; > +} > + > /** > * ovpn_udp4_output - send IPv4 packet over udp socket > * @ovpn: the openvpn instance > @@ -257,8 +342,13 @@ void ovpn_udp_send_skb(struct ovpn_struct *ovpn, struct ovpn_peer *peer, > */ > int ovpn_udp_socket_attach(struct socket *sock, struct ovpn_struct *ovpn) > { > + struct udp_tunnel_sock_cfg cfg = { > + .sk_user_data = ovpn, > + .encap_type = UDP_ENCAP_OVPNINUDP, > + .encap_rcv = ovpn_udp_encap_recv, > + }; > struct ovpn_socket *old_data; > - int ret = 0; > + int ret; > > /* sanity check */ > if (sock->sk->sk_protocol != IPPROTO_UDP) { > @@ -272,6 +362,7 @@ int ovpn_udp_socket_attach(struct socket *sock, struct ovpn_struct *ovpn) > if (!old_data) { > /* socket is currently unused - we can take it */ > rcu_read_unlock(); > + setup_udp_tunnel_sock(sock_net(sock->sk), sock, &cfg); This will set sk_user_data to the ovpn_struct, but ovpn_from_udp_sock expects the ovpn_socket [1], which is stored into sk_user_data a little bit later by ovpn_socket_new. If a packet reaches ovpn_udp_encap_recv -> ovpn_from_udp_sock before ovpn_socket_new overwrites sk_user_data, bad things probably happen.
On 02/09/2024 13:22, Sabrina Dubroca wrote: > 2024-08-27, 14:07:51 +0200, Antonio Quartulli wrote: >> +static void ovpn_netdev_write(struct ovpn_peer *peer, struct sk_buff *skb) >> +{ >> + /* 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_probe_transport_header(skb); >> + skb_reset_inner_headers(skb); >> + >> + memset(skb->cb, 0, sizeof(skb->cb)); >> + >> + /* cause packet to be "received" by the interface */ >> + if (likely(gro_cells_receive(&peer->ovpn->gro_cells, >> + skb) == NET_RX_SUCCESS)) >> + /* update RX stats with the size of decrypted packet */ >> + dev_sw_netstats_rx_add(peer->ovpn->dev, skb->len); > > I don't think accessing skb->len after passing the skb to > gro_cells_receive is safe, see > c7cc9200e9b4 ("macsec: avoid use-after-free in macsec_handle_frame()") Thanks for spotting this! It's basically the same issue (but symmetric) as patch 10/25. > > > [...] >> static void ovpn_struct_free(struct net_device *net) >> { >> + struct ovpn_struct *ovpn = netdev_priv(net); >> + >> + gro_cells_destroy(&ovpn->gro_cells); >> + rcu_barrier(); > > What's the purpose of this rcu_barrier? I expect it in module_exit, > not when removing one netdevice. Good question. I presume it's a leftover from previous tests. I think it's harmless, but it should not be needed at all. I will remove it. > > >> diff --git a/drivers/net/ovpn/skb.h b/drivers/net/ovpn/skb.h >> index 7966a10d915f..e070fe6f448c 100644 >> --- a/drivers/net/ovpn/skb.h >> +++ b/drivers/net/ovpn/skb.h >> @@ -18,10 +18,7 @@ >> #include <linux/types.h> >> >> struct ovpn_cb { >> - struct aead_request *req; >> struct ovpn_peer *peer; >> - struct ovpn_crypto_key_slot *ks; >> - unsigned int payload_offset; > > Squashed into the wrong patch? Darn yes. Sorry for this. > > > [...] >> +struct ovpn_struct *ovpn_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); > > [1] > >> + 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; >> +} > > >> +static int ovpn_udp_encap_recv(struct sock *sk, struct sk_buff *skb) >> +{ >> + struct ovpn_peer *peer = NULL; >> + struct ovpn_struct *ovpn; >> + u32 peer_id; >> + u8 opcode; >> + >> + ovpn = ovpn_from_udp_sock(sk); >> + if (unlikely(!ovpn)) { >> + net_err_ratelimited("%s: cannot obtain ovpn object from UDP socket\n", >> + __func__); >> + goto drop; >> + } > [...] >> + /* pop off outer UDP header */ >> + __skb_pull(skb, sizeof(struct udphdr)); >> + ovpn_recv(peer, skb); >> + return 0; >> + >> +drop: >> + if (peer) >> + ovpn_peer_put(peer); >> + dev_core_stats_rx_dropped_inc(ovpn->dev); > > If we get here from the first goto, ovpn is NULL. You could add a > drop_noovpn label right here to just do the free+return. Right. Weird though that no static analysis tool complained about ovpn possibly being NULL. Will add the extra label. > >> + kfree_skb(skb); >> + return 0; >> +} >> + >> /** >> * ovpn_udp4_output - send IPv4 packet over udp socket >> * @ovpn: the openvpn instance >> @@ -257,8 +342,13 @@ void ovpn_udp_send_skb(struct ovpn_struct *ovpn, struct ovpn_peer *peer, >> */ >> int ovpn_udp_socket_attach(struct socket *sock, struct ovpn_struct *ovpn) >> { >> + struct udp_tunnel_sock_cfg cfg = { >> + .sk_user_data = ovpn, >> + .encap_type = UDP_ENCAP_OVPNINUDP, >> + .encap_rcv = ovpn_udp_encap_recv, >> + }; >> struct ovpn_socket *old_data; >> - int ret = 0; >> + int ret; >> >> /* sanity check */ >> if (sock->sk->sk_protocol != IPPROTO_UDP) { >> @@ -272,6 +362,7 @@ int ovpn_udp_socket_attach(struct socket *sock, struct ovpn_struct *ovpn) >> if (!old_data) { >> /* socket is currently unused - we can take it */ >> rcu_read_unlock(); >> + setup_udp_tunnel_sock(sock_net(sock->sk), sock, &cfg); > > This will set sk_user_data to the ovpn_struct, but ovpn_from_udp_sock > expects the ovpn_socket [1], which is stored into sk_user_data a > little bit later by ovpn_socket_new. If a packet reaches > ovpn_udp_encap_recv -> ovpn_from_udp_sock before ovpn_socket_new > overwrites sk_user_data, bad things probably happen. Wow - this is a very nice catch. I think this wrong cfg.sk_user_data initialization was there this since the first prototype, but it just passed unnoticed. I will drop the field initialization, so that the sk_user_data stays NULL until it gets assigned the ovpn_sock. Thanks a lot! Cheers,
On Tue, Aug 27, 2024 at 02:07:51PM +0200, Antonio Quartulli wrote: > 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@openvpn.net> ... > +/** > + * 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 further processed, > + * otherwise it 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_peer *peer = NULL; > + struct ovpn_struct *ovpn; > + u32 peer_id; > + u8 opcode; > + > + ovpn = ovpn_from_udp_sock(sk); > + if (unlikely(!ovpn)) { > + net_err_ratelimited("%s: cannot obtain ovpn object from UDP socket\n", > + __func__); > + goto drop; Hi Antonio, Here ovpn is NULL. But jumping to drop will result in ovpn being dereferenced. Flagged by Smatch. > + } > + > + /* 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_OP_SIZE_V2))) { > + net_dbg_ratelimited("%s: packet too small\n", __func__); > + 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_id(ovpn, peer_id); > + if (!peer) { > + net_err_ratelimited("%s: received data from unknown peer (id: %d)\n", > + __func__, peer_id); > + goto drop; > + } > + } > + > + if (!peer) { > + /* data packet with undef peer-id */ > + peer = ovpn_peer_get_by_transp_addr(ovpn, skb); > + if (unlikely(!peer)) { > + net_dbg_ratelimited("%s: received data with undef peer-id from unknown source\n", > + __func__); > + goto drop; > + } > + } > + > + /* pop off outer UDP header */ > + __skb_pull(skb, sizeof(struct udphdr)); > + ovpn_recv(peer, skb); > + return 0; > + > +drop: > + if (peer) > + ovpn_peer_put(peer); > + dev_core_stats_rx_dropped_inc(ovpn->dev); > + kfree_skb(skb); > + return 0; > +} > + ...
On 06/09/2024 21:18, Simon Horman wrote: > On Tue, Aug 27, 2024 at 02:07:51PM +0200, Antonio Quartulli wrote: >> 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@openvpn.net> > > ... > >> +/** >> + * 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 further processed, >> + * otherwise it 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_peer *peer = NULL; >> + struct ovpn_struct *ovpn; >> + u32 peer_id; >> + u8 opcode; >> + >> + ovpn = ovpn_from_udp_sock(sk); >> + if (unlikely(!ovpn)) { >> + net_err_ratelimited("%s: cannot obtain ovpn object from UDP socket\n", >> + __func__); >> + goto drop; > > Hi Antonio, > > Here ovpn is NULL. But jumping to drop will result in ovpn being dereferenced. > > Flagged by Smatch. You are correct, thanks for the report. Will fix it in the next version. Cheers, > > >> + } >> + >> + /* 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_OP_SIZE_V2))) { >> + net_dbg_ratelimited("%s: packet too small\n", __func__); >> + 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_id(ovpn, peer_id); >> + if (!peer) { >> + net_err_ratelimited("%s: received data from unknown peer (id: %d)\n", >> + __func__, peer_id); >> + goto drop; >> + } >> + } >> + >> + if (!peer) { >> + /* data packet with undef peer-id */ >> + peer = ovpn_peer_get_by_transp_addr(ovpn, skb); >> + if (unlikely(!peer)) { >> + net_dbg_ratelimited("%s: received data with undef peer-id from unknown source\n", >> + __func__); >> + goto drop; >> + } >> + } >> + >> + /* pop off outer UDP header */ >> + __skb_pull(skb, sizeof(struct udphdr)); >> + ovpn_recv(peer, skb); >> + return 0; >> + >> +drop: >> + if (peer) >> + ovpn_peer_put(peer); >> + dev_core_stats_rx_dropped_inc(ovpn->dev); >> + kfree_skb(skb); >> + return 0; >> +} >> + > > ...
diff --git a/drivers/net/ovpn/io.c b/drivers/net/ovpn/io.c index cbb42086f1fc..ad986c66dbdf 100644 --- a/drivers/net/ovpn/io.c +++ b/drivers/net/ovpn/io.c @@ -9,14 +9,75 @@ #include <linux/netdevice.h> #include <linux/skbuff.h> +#include <net/gro_cells.h> #include <net/gso.h> -#include "io.h" #include "ovpnstruct.h" #include "peer.h" +#include "io.h" +#include "netlink.h" +#include "proto.h" #include "udp.h" #include "skb.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) +{ + /* 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_probe_transport_header(skb); + skb_reset_inner_headers(skb); + + memset(skb->cb, 0, sizeof(skb->cb)); + + /* cause packet to be "received" by the interface */ + if (likely(gro_cells_receive(&peer->ovpn->gro_cells, + skb) == NET_RX_SUCCESS)) + /* update RX stats with the size of decrypted packet */ + dev_sw_netstats_rx_add(peer->ovpn->dev, skb->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); + kfree_skb(skb); + ovpn_peer_put(peer); +} + +/* pick next packet from RX queue, decrypt 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 aa259be66441..9667a0a470e0 100644 --- a/drivers/net/ovpn/io.h +++ b/drivers/net/ovpn/io.h @@ -12,4 +12,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 7cc55d34844f..caa38f6bee2a 100644 --- a/drivers/net/ovpn/main.c +++ b/drivers/net/ovpn/main.c @@ -13,6 +13,7 @@ #include <linux/inetdevice.h> //#include <linux/rcupdate.h> #include <linux/version.h> +#include <net/gro_cells.h> #include <net/ip.h> #include <net/rtnetlink.h> #include <uapi/linux/if_arp.h> @@ -49,11 +50,17 @@ static int ovpn_struct_init(struct net_device *dev, enum ovpn_mode mode) static void ovpn_struct_free(struct net_device *net) { + struct ovpn_struct *ovpn = netdev_priv(net); + + gro_cells_destroy(&ovpn->gro_cells); + rcu_barrier(); } static int ovpn_net_init(struct net_device *dev) { - return 0; + struct ovpn_struct *ovpn = netdev_priv(dev); + + return gro_cells_init(&ovpn->gro_cells, dev); } static int ovpn_net_open(struct net_device *dev) diff --git a/drivers/net/ovpn/ovpnstruct.h b/drivers/net/ovpn/ovpnstruct.h index 25f4837b798b..65497ce115aa 100644 --- a/drivers/net/ovpn/ovpnstruct.h +++ b/drivers/net/ovpn/ovpnstruct.h @@ -10,6 +10,7 @@ #ifndef _NET_OVPN_OVPNSTRUCT_H_ #define _NET_OVPN_OVPNSTRUCT_H_ +#include <net/gro_cells.h> #include <net/net_trackers.h> #include <uapi/linux/ovpn.h> @@ -22,6 +23,7 @@ * @lock: protect this object * @peer: in P2P mode, this is the only remote peer * @dev_list: entry for the module wide device list + * @gro_cells: pointer to the Generic Receive Offload cell */ struct ovpn_struct { struct net_device *dev; @@ -31,6 +33,7 @@ struct ovpn_struct { spinlock_t lock; /* protect writing to the ovpn_struct object */ struct ovpn_peer __rcu *peer; struct list_head dev_list; + struct gro_cells gro_cells; }; #endif /* _NET_OVPN_OVPNSTRUCT_H_ */ diff --git a/drivers/net/ovpn/proto.h b/drivers/net/ovpn/proto.h new file mode 100644 index 000000000000..69604cf26bbf --- /dev/null +++ b/drivers/net/ovpn/proto.h @@ -0,0 +1,75 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* OpenVPN data channel offload + * + * Copyright (C) 2020-2024 OpenVPN, Inc. + * + * Author: Antonio Quartulli <antonio@openvpn.net> + * James Yonan <james@openvpn.net> + */ + +#ifndef _NET_OVPN_OVPNPROTO_H_ +#define _NET_OVPN_OVPNPROTO_H_ + +#include "main.h" + +#include <linux/skbuff.h> + +/* Methods for operating on the initial command + * byte of the OpenVPN protocol. + */ + +/* packet opcode (high 5 bits) and key-id (low 3 bits) are combined in + * one byte + */ +#define OVPN_KEY_ID_MASK 0x07 +#define OVPN_OPCODE_SHIFT 3 +#define OVPN_OPCODE_MASK 0x1F +/* upper bounds on opcode and key ID */ +#define OVPN_KEY_ID_MAX (OVPN_KEY_ID_MASK + 1) +#define OVPN_OPCODE_MAX (OVPN_OPCODE_MASK + 1) +/* packet opcodes of interest to us */ +#define OVPN_DATA_V1 6 /* data channel V1 packet */ +#define OVPN_DATA_V2 9 /* data channel V2 packet */ +/* size of initial packet opcode */ +#define OVPN_OP_SIZE_V1 1 +#define OVPN_OP_SIZE_V2 4 +#define OVPN_PEER_ID_MASK 0x00FFFFFF +#define OVPN_PEER_ID_UNDEF 0x00FFFFFF +/* first byte of keepalive message */ +#define OVPN_KEEPALIVE_FIRST_BYTE 0x2a +/* first byte of exit message */ +#define OVPN_EXPLICIT_EXIT_NOTIFY_FIRST_BYTE 0x28 + +/** + * 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 byte. + * + * Return: the OP code + */ +static inline u8 ovpn_opcode_from_skb(const struct sk_buff *skb, u16 offset) +{ + u8 byte = *(skb->data + offset); + + return byte >> OVPN_OPCODE_SHIFT; +} + +/** + * 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) +{ + return ntohl(*(__be32 *)(skb->data + offset)) & OVPN_PEER_ID_MASK; +} + +#endif /* _NET_OVPN_OVPNPROTO_H_ */ diff --git a/drivers/net/ovpn/skb.h b/drivers/net/ovpn/skb.h index 7966a10d915f..e070fe6f448c 100644 --- a/drivers/net/ovpn/skb.h +++ b/drivers/net/ovpn/skb.h @@ -18,10 +18,7 @@ #include <linux/types.h> struct ovpn_cb { - struct aead_request *req; struct ovpn_peer *peer; - struct ovpn_crypto_key_slot *ks; - unsigned int payload_offset; }; static inline struct ovpn_cb *ovpn_skb_cb(struct sk_buff *skb) diff --git a/drivers/net/ovpn/socket.c b/drivers/net/ovpn/socket.c index 090a3232ab0e..964b566de69f 100644 --- a/drivers/net/ovpn/socket.c +++ b/drivers/net/ovpn/socket.c @@ -22,6 +22,9 @@ static void ovpn_socket_detach(struct socket *sock) if (!sock) return; + if (sock->sk->sk_protocol == IPPROTO_UDP) + ovpn_udp_socket_detach(sock); + sockfd_put(sock); } @@ -71,6 +74,27 @@ static int ovpn_socket_attach(struct socket *sock, struct ovpn_peer *peer) return ret; } +/* Retrieve the corresponding ovpn object from a UDP socket + * rcu_read_lock must be held on entry + */ +struct ovpn_struct *ovpn_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; +} + /** * ovpn_socket_new - create a new socket and initialize it * @sock: the kernel socket to embed diff --git a/drivers/net/ovpn/udp.c b/drivers/net/ovpn/udp.c index 0b1a541732a2..8aea99520929 100644 --- a/drivers/net/ovpn/udp.c +++ b/drivers/net/ovpn/udp.c @@ -21,9 +21,94 @@ #include "bind.h" #include "io.h" #include "peer.h" +#include "proto.h" #include "socket.h" #include "udp.h" +/** + * 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 further processed, + * otherwise it 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_peer *peer = NULL; + struct ovpn_struct *ovpn; + u32 peer_id; + u8 opcode; + + ovpn = ovpn_from_udp_sock(sk); + if (unlikely(!ovpn)) { + net_err_ratelimited("%s: cannot obtain ovpn object from UDP socket\n", + __func__); + goto drop; + } + + /* 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_OP_SIZE_V2))) { + net_dbg_ratelimited("%s: packet too small\n", __func__); + 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_id(ovpn, peer_id); + if (!peer) { + net_err_ratelimited("%s: received data from unknown peer (id: %d)\n", + __func__, peer_id); + goto drop; + } + } + + if (!peer) { + /* data packet with undef peer-id */ + peer = ovpn_peer_get_by_transp_addr(ovpn, skb); + if (unlikely(!peer)) { + net_dbg_ratelimited("%s: received data with undef peer-id from unknown source\n", + __func__); + goto drop; + } + } + + /* pop off outer UDP header */ + __skb_pull(skb, sizeof(struct udphdr)); + ovpn_recv(peer, skb); + return 0; + +drop: + if (peer) + ovpn_peer_put(peer); + dev_core_stats_rx_dropped_inc(ovpn->dev); + kfree_skb(skb); + return 0; +} + /** * ovpn_udp4_output - send IPv4 packet over udp socket * @ovpn: the openvpn instance @@ -257,8 +342,13 @@ void ovpn_udp_send_skb(struct ovpn_struct *ovpn, struct ovpn_peer *peer, */ int ovpn_udp_socket_attach(struct socket *sock, struct ovpn_struct *ovpn) { + struct udp_tunnel_sock_cfg cfg = { + .sk_user_data = ovpn, + .encap_type = UDP_ENCAP_OVPNINUDP, + .encap_rcv = ovpn_udp_encap_recv, + }; struct ovpn_socket *old_data; - int ret = 0; + int ret; /* sanity check */ if (sock->sk->sk_protocol != IPPROTO_UDP) { @@ -272,6 +362,7 @@ int ovpn_udp_socket_attach(struct socket *sock, struct ovpn_struct *ovpn) if (!old_data) { /* socket is currently unused - we can take it */ rcu_read_unlock(); + setup_udp_tunnel_sock(sock_net(sock->sk), sock, &cfg); return 0; } @@ -300,3 +391,14 @@ int ovpn_udp_socket_attach(struct socket *sock, struct ovpn_struct *ovpn) return ret; } + +/** + * ovpn_udp_socket_detach - clean udp-tunnel status for this socket + * @sock: the socket to clean + */ +void ovpn_udp_socket_detach(struct socket *sock) +{ + struct udp_tunnel_sock_cfg cfg = { }; + + setup_udp_tunnel_sock(sock_net(sock->sk), sock, &cfg); +} diff --git a/drivers/net/ovpn/udp.h b/drivers/net/ovpn/udp.h index e60f8cd2b4ac..fecb68464896 100644 --- a/drivers/net/ovpn/udp.h +++ b/drivers/net/ovpn/udp.h @@ -18,8 +18,9 @@ struct sk_buff; struct socket; int ovpn_udp_socket_attach(struct socket *sock, struct ovpn_struct *ovpn); - +void ovpn_udp_socket_detach(struct socket *sock); void ovpn_udp_send_skb(struct ovpn_struct *ovpn, struct ovpn_peer *peer, struct sk_buff *skb); +struct ovpn_struct *ovpn_from_udp_sock(struct sock *sk); #endif /* _NET_OVPN_UDP_H_ */
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@openvpn.net> --- drivers/net/ovpn/io.c | 63 +++++++++++++++++++- drivers/net/ovpn/io.h | 2 + drivers/net/ovpn/main.c | 9 ++- drivers/net/ovpn/ovpnstruct.h | 3 + drivers/net/ovpn/proto.h | 75 ++++++++++++++++++++++++ drivers/net/ovpn/skb.h | 3 - drivers/net/ovpn/socket.c | 24 ++++++++ drivers/net/ovpn/udp.c | 104 +++++++++++++++++++++++++++++++++- drivers/net/ovpn/udp.h | 3 +- 9 files changed, 279 insertions(+), 7 deletions(-) create mode 100644 drivers/net/ovpn/proto.h