summaryrefslogtreecommitdiff
path: root/drivers/net/ovpn/io.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/ovpn/io.c')
-rw-r--r--drivers/net/ovpn/io.c64
1 files changed, 63 insertions, 1 deletions
diff --git a/drivers/net/ovpn/io.c b/drivers/net/ovpn/io.c
index 93b128827b6d..22fa92b9aeeb 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_dstats_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_dstats_rx_dropped(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;