ovpn already advertises software GSO support and segments GSO skbs in
its transmit path. However, without checksum offload in the device
features, the networking core has to segment GSO packets before they
reach ovpn because TCP GSO packets normally carry CHECKSUM_PARTIAL
state.

Advertise NETIF_F_HW_CSUM so the stack can pass such packets to ovpn.
Complete partial checksums after any GSO segmentation and before
submitting packets for encryption, since the inner packet checksum can
no longer be fixed after the packet has been encrypted.

Also pass the ovpn feature set to skb_gso_segment with GSO capabilities
masked out: this forces software segmentation, but still lets the
segmenter preserve supported non-GSO properties such as non-linear skb
data instead of needlessly linearizing.

Account peer packet statistics for every segment which survives
segmentation rather than once for the original GSO skb.

Signed-off-by: Ralf Lici <[email protected]>
---
Changes since v2 
https://lore.kernel.org/openvpn-devel/[email protected]/
- Account peer packet statistics for every segment which survives GSO
  segmentation rather than once for the original GSO skb. (Sashiko)

No changes since v1 
https://lore.kernel.org/openvpn-devel/1bbfde37488ade61928554db3a119525adf7b608.1790843153.git.r...@mandelbit.com/

 drivers/net/ovpn/io.c    | 23 ++++++++++++++++++++---
 drivers/net/ovpn/main.c  |  2 +-
 drivers/net/ovpn/stats.h | 10 +++++++++-
 3 files changed, 30 insertions(+), 5 deletions(-)

diff --git a/drivers/net/ovpn/io.c b/drivers/net/ovpn/io.c
index f14eb4e6c46a..3b0c9b30e879 100644
--- a/drivers/net/ovpn/io.c
+++ b/drivers/net/ovpn/io.c
@@ -356,6 +356,7 @@ netdev_tx_t ovpn_net_xmit(struct sk_buff *skb, struct 
net_device *dev)
        struct ovpn_priv *ovpn = netdev_priv(dev);
        struct sk_buff *segments, *curr, *next;
        struct sk_buff_head skb_list;
+       netdev_features_t features;
        unsigned int tx_bytes = 0;
        struct ovpn_peer *peer;
        __be16 proto;
@@ -390,8 +391,13 @@ netdev_tx_t ovpn_net_xmit(struct sk_buff *skb, struct 
net_device *dev)
        skb_dst_drop(skb);
 
        if (skb_is_gso(skb)) {
-               segments = skb_gso_segment(skb, 0);
-               if (IS_ERR(segments)) {
+               /* force software segmentation, but keep ovpn's non-GSO feature
+                * bits so the generated segments can preserve non-linear skb
+                * data where possible
+                */
+               features = netif_skb_features(skb);
+               segments = skb_gso_segment(skb, features & ~NETIF_F_GSO_MASK);
+               if (IS_ERR_OR_NULL(segments)) {
                        ret = PTR_ERR(segments);
                        net_err_ratelimited("%s: cannot segment payload packet: 
%d\n",
                                            netdev_name(dev), ret);
@@ -416,6 +422,16 @@ netdev_tx_t ovpn_net_xmit(struct sk_buff *skb, struct 
net_device *dev)
                        continue;
                }
 
+               /* NETIF_F_HW_CSUM requires completing partial checksums */
+               if (unlikely(curr->ip_summed == CHECKSUM_PARTIAL &&
+                            skb_checksum_help(curr) < 0)) {
+                       net_err_ratelimited("%s: skb_checksum_help failed for 
payload packet\n",
+                                           netdev_name(dev));
+                       ovpn_dev_dstats_tx_dropped(ovpn->dev);
+                       kfree_skb(curr);
+                       continue;
+               }
+
                /* only count what we actually send */
                tx_bytes += curr->len;
                __skb_queue_tail(&skb_list, curr);
@@ -430,7 +446,8 @@ netdev_tx_t ovpn_net_xmit(struct sk_buff *skb, struct 
net_device *dev)
        }
        skb_list.prev->next = NULL;
 
-       ovpn_peer_stats_increment_tx(&peer->vpn_stats, tx_bytes);
+       ovpn_peer_stats_add_tx(&peer->vpn_stats, tx_bytes,
+                              skb_queue_len(&skb_list));
        ovpn_send(ovpn, skb_list.next, peer);
 
        return NETDEV_TX_OK;
diff --git a/drivers/net/ovpn/main.c b/drivers/net/ovpn/main.c
index 0708249e9607..28e1eb06e127 100644
--- a/drivers/net/ovpn/main.c
+++ b/drivers/net/ovpn/main.c
@@ -157,7 +157,7 @@ static const struct ethtool_ops ovpn_ethtool_ops = {
 
 static void ovpn_setup(struct net_device *dev)
 {
-       netdev_features_t feat = NETIF_F_SG | NETIF_F_GSO |
+       netdev_features_t feat = NETIF_F_HW_CSUM | NETIF_F_SG | NETIF_F_GSO |
                                 NETIF_F_GSO_SOFTWARE | NETIF_F_HIGHDMA;
 
        dev->needs_free_netdev = true;
diff --git a/drivers/net/ovpn/stats.h b/drivers/net/ovpn/stats.h
index 3a45b97c0056..9331f5e9179b 100644
--- a/drivers/net/ovpn/stats.h
+++ b/drivers/net/ovpn/stats.h
@@ -40,10 +40,18 @@ static inline void ovpn_peer_stats_increment_rx(struct 
ovpn_peer_stats *stats,
        ovpn_peer_stats_increment(&stats->rx, n);
 }
 
+static inline void ovpn_peer_stats_add_tx(struct ovpn_peer_stats *stats,
+                                         const unsigned int bytes,
+                                         unsigned int packets)
+{
+       atomic64_add(bytes, &stats->tx.bytes);
+       atomic64_add(packets, &stats->tx.packets);
+}
+
 static inline void ovpn_peer_stats_increment_tx(struct ovpn_peer_stats *stats,
                                                const unsigned int n)
 {
-       ovpn_peer_stats_increment(&stats->tx, n);
+       ovpn_peer_stats_add_tx(stats, n, 1);
 }
 
 static inline void ovpn_dev_dstats_tx_dropped(struct net_device *dev)
-- 
2.56.0



_______________________________________________
Openvpn-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/openvpn-devel

Reply via email to