]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
vxlan: Pull inner IP header in vxlan_rcv().
authorGuillaume Nault <gnault@redhat.com>
Tue, 30 Apr 2024 16:50:13 +0000 (18:50 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 17 May 2024 10:02:06 +0000 (12:02 +0200)
[ Upstream commit f7789419137b18e3847d0cc41afd788c3c00663d ]

Ensure the inner IP header is part of skb's linear data before reading
its ECN bits. Otherwise we might read garbage.
One symptom is the system erroneously logging errors like
"vxlan: non-ECT from xxx.xxx.xxx.xxx with TOS=xxxx".

Similar bugs have been fixed in geneve, ip_tunnel and ip6_tunnel (see
commit 1ca1ba465e55 ("geneve: make sure to pull inner header in
geneve_rx()") for example). So let's reuse the same code structure for
consistency. Maybe we'll can add a common helper in the future.

Fixes: d342894c5d2f ("vxlan: virtual extensible lan")
Signed-off-by: Guillaume Nault <gnault@redhat.com>
Reviewed-by: Ido Schimmel <idosch@nvidia.com>
Reviewed-by: Eric Dumazet <edumazet@google.com>
Reviewed-by: Nikolay Aleksandrov <razor@blackwall.org>
Reviewed-by: Sabrina Dubroca <sd@queasysnail.net>
Link: https://lore.kernel.org/r/1239c8db54efec341dd6455c77e0380f58923a3c.1714495737.git.gnault@redhat.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/net/vxlan/vxlan_core.c

index 7e5e60318045ae4270a40aff5b09e8cfd5ad1bcc..f98069920e27f6df42135656f4c0a5fef3471507 100644 (file)
@@ -1674,6 +1674,7 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb)
        bool raw_proto = false;
        void *oiph;
        __be32 vni = 0;
+       int nh;
 
        /* Need UDP and VXLAN header to be present */
        if (!pskb_may_pull(skb, VXLAN_HLEN))
@@ -1762,9 +1763,25 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb)
                skb->pkt_type = PACKET_HOST;
        }
 
-       oiph = skb_network_header(skb);
+       /* Save offset of outer header relative to skb->head,
+        * because we are going to reset the network header to the inner header
+        * and might change skb->head.
+        */
+       nh = skb_network_header(skb) - skb->head;
+
        skb_reset_network_header(skb);
 
+       if (!pskb_inet_may_pull(skb)) {
+               DEV_STATS_INC(vxlan->dev, rx_length_errors);
+               DEV_STATS_INC(vxlan->dev, rx_errors);
+               vxlan_vnifilter_count(vxlan, vni, vninode,
+                                     VXLAN_VNI_STATS_RX_ERRORS, 0);
+               goto drop;
+       }
+
+       /* Get the outer header. */
+       oiph = skb->head + nh;
+
        if (!vxlan_ecn_decapsulate(vs, oiph, skb)) {
                DEV_STATS_INC(vxlan->dev, rx_frame_errors);
                DEV_STATS_INC(vxlan->dev, rx_errors);