ec7941c644
[ Upstream commit 3d010c8031e39f5fa1e8b13ada77e0321091011f ] When rx-udp-gro-forwarding is enabled UDP packets might be GROed when being forwarded. If such packets might land in a tunnel this can cause various issues and udp_gro_receive makes sure this isn't the case by looking for a matching socket. This is performed in udp4/6_gro_lookup_skb but only in the current netns. This is an issue with tunneled packets when the endpoint is in another netns. In such cases the packets will be GROed at the UDP level, which leads to various issues later on. The same thing can happen with rx-gro-list. We saw this with geneve packets being GROed at the UDP level. In such case gso_size is set; later the packet goes through the geneve rx path, the geneve header is pulled, the offset are adjusted and frag_list skbs are not adjusted with regard to geneve. When those skbs hit skb_fragment, it will misbehave. Different outcomes are possible depending on what the GROed skbs look like; from corrupted packets to kernel crashes. One example is a BUG_ON[1] triggered in skb_segment while processing the frag_list. Because gso_size is wrong (geneve header was pulled) skb_segment thinks there is "geneve header size" of data in frag_list, although it's in fact the next packet. The BUG_ON itself has nothing to do with the issue. This is only one of the potential issues. Looking up for a matching socket in udp_gro_receive is fragile: the lookup could be extended to all netns (not speaking about performances) but nothing prevents those packets from being modified in between and we could still not find a matching socket. It's OK to keep the current logic there as it should cover most cases but we also need to make sure we handle tunnel packets being GROed too early. This is done by extending the checks in udp_unexpected_gso: GSO packets lacking the SKB_GSO_UDP_TUNNEL/_CSUM bits and landing in a tunnel must be segmented. [1] kernel BUG at net/core/skbuff.c:4408! RIP: 0010:skb_segment+0xd2a/0xf70 __udp_gso_segment+0xaa/0x560 Fixes: 9fd1ff5d2ac7 ("udp: Support UDP fraglist GRO/GSO.") Fixes: 36707061d6ba ("udp: allow forwarding of plain (non-fraglisted) UDP GRO packets") Signed-off-by: Antoine Tenart <atenart@kernel.org> Reviewed-by: Willem de Bruijn <willemb@google.com> Signed-off-by: David S. Miller <davem@davemloft.net> Signed-off-by: Sasha Levin <sashal@kernel.org>
184 lines
5.1 KiB
C
Executable file
184 lines
5.1 KiB
C
Executable file
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
/*
|
|
* INET An implementation of the TCP/IP protocol suite for the LINUX
|
|
* operating system. INET is implemented using the BSD Socket
|
|
* interface as the means of communication with the user level.
|
|
*
|
|
* Definitions for the UDP protocol.
|
|
*
|
|
* Version: @(#)udp.h 1.0.2 04/28/93
|
|
*
|
|
* Author: Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
|
|
*/
|
|
#ifndef _LINUX_UDP_H
|
|
#define _LINUX_UDP_H
|
|
|
|
#include <net/inet_sock.h>
|
|
#include <linux/skbuff.h>
|
|
#include <net/netns/hash.h>
|
|
#include <uapi/linux/udp.h>
|
|
|
|
static inline struct udphdr *udp_hdr(const struct sk_buff *skb)
|
|
{
|
|
return (struct udphdr *)skb_transport_header(skb);
|
|
}
|
|
|
|
static inline struct udphdr *inner_udp_hdr(const struct sk_buff *skb)
|
|
{
|
|
return (struct udphdr *)skb_inner_transport_header(skb);
|
|
}
|
|
|
|
#define UDP_HTABLE_SIZE_MIN (CONFIG_BASE_SMALL ? 128 : 256)
|
|
|
|
static inline u32 udp_hashfn(const struct net *net, u32 num, u32 mask)
|
|
{
|
|
return (num + net_hash_mix(net)) & mask;
|
|
}
|
|
|
|
struct udp_sock {
|
|
/* inet_sock has to be the first member */
|
|
struct inet_sock inet;
|
|
#define udp_port_hash inet.sk.__sk_common.skc_u16hashes[0]
|
|
#define udp_portaddr_hash inet.sk.__sk_common.skc_u16hashes[1]
|
|
#define udp_portaddr_node inet.sk.__sk_common.skc_portaddr_node
|
|
int pending; /* Any pending frames ? */
|
|
unsigned int corkflag; /* Cork is required */
|
|
__u8 encap_type; /* Is this an Encapsulation socket? */
|
|
unsigned char no_check6_tx:1,/* Send zero UDP6 checksums on TX? */
|
|
no_check6_rx:1,/* Allow zero UDP6 checksums on RX? */
|
|
encap_enabled:1, /* This socket enabled encap
|
|
* processing; UDP tunnels and
|
|
* different encapsulation layer set
|
|
* this
|
|
*/
|
|
gro_enabled:1, /* Request GRO aggregation */
|
|
accept_udp_l4:1,
|
|
accept_udp_fraglist:1;
|
|
/*
|
|
* Following member retains the information to create a UDP header
|
|
* when the socket is uncorked.
|
|
*/
|
|
__u16 len; /* total length of pending frames */
|
|
__u16 gso_size;
|
|
/*
|
|
* Fields specific to UDP-Lite.
|
|
*/
|
|
__u16 pcslen;
|
|
__u16 pcrlen;
|
|
/* indicator bits used by pcflag: */
|
|
#define UDPLITE_BIT 0x1 /* set by udplite proto init function */
|
|
#define UDPLITE_SEND_CC 0x2 /* set via udplite setsockopt */
|
|
#define UDPLITE_RECV_CC 0x4 /* set via udplite setsocktopt */
|
|
__u8 pcflag; /* marks socket as UDP-Lite if > 0 */
|
|
__u8 unused[3];
|
|
/*
|
|
* For encapsulation sockets.
|
|
*/
|
|
int (*encap_rcv)(struct sock *sk, struct sk_buff *skb);
|
|
int (*encap_err_lookup)(struct sock *sk, struct sk_buff *skb);
|
|
void (*encap_destroy)(struct sock *sk);
|
|
|
|
/* GRO functions for UDP socket */
|
|
struct sk_buff * (*gro_receive)(struct sock *sk,
|
|
struct list_head *head,
|
|
struct sk_buff *skb);
|
|
int (*gro_complete)(struct sock *sk,
|
|
struct sk_buff *skb,
|
|
int nhoff);
|
|
|
|
/* udp_recvmsg try to use this before splicing sk_receive_queue */
|
|
struct sk_buff_head reader_queue ____cacheline_aligned_in_smp;
|
|
|
|
/* This field is dirtied by udp_recvmsg() */
|
|
int forward_deficit;
|
|
};
|
|
|
|
#define UDP_MAX_SEGMENTS (1 << 6UL)
|
|
|
|
static inline struct udp_sock *udp_sk(const struct sock *sk)
|
|
{
|
|
return (struct udp_sock *)sk;
|
|
}
|
|
|
|
static inline void udp_set_no_check6_tx(struct sock *sk, bool val)
|
|
{
|
|
udp_sk(sk)->no_check6_tx = val;
|
|
}
|
|
|
|
static inline void udp_set_no_check6_rx(struct sock *sk, bool val)
|
|
{
|
|
udp_sk(sk)->no_check6_rx = val;
|
|
}
|
|
|
|
static inline bool udp_get_no_check6_tx(struct sock *sk)
|
|
{
|
|
return udp_sk(sk)->no_check6_tx;
|
|
}
|
|
|
|
static inline bool udp_get_no_check6_rx(struct sock *sk)
|
|
{
|
|
return udp_sk(sk)->no_check6_rx;
|
|
}
|
|
|
|
static inline void udp_cmsg_recv(struct msghdr *msg, struct sock *sk,
|
|
struct sk_buff *skb)
|
|
{
|
|
int gso_size;
|
|
|
|
if (skb_shinfo(skb)->gso_type & SKB_GSO_UDP_L4) {
|
|
gso_size = skb_shinfo(skb)->gso_size;
|
|
put_cmsg(msg, SOL_UDP, UDP_GRO, sizeof(gso_size), &gso_size);
|
|
}
|
|
}
|
|
|
|
DECLARE_STATIC_KEY_FALSE(udp_encap_needed_key);
|
|
#if IS_ENABLED(CONFIG_IPV6)
|
|
DECLARE_STATIC_KEY_FALSE(udpv6_encap_needed_key);
|
|
#endif
|
|
|
|
static inline bool udp_encap_needed(void)
|
|
{
|
|
if (static_branch_unlikely(&udp_encap_needed_key))
|
|
return true;
|
|
|
|
#if IS_ENABLED(CONFIG_IPV6)
|
|
if (static_branch_unlikely(&udpv6_encap_needed_key))
|
|
return true;
|
|
#endif
|
|
|
|
return false;
|
|
}
|
|
|
|
static inline bool udp_unexpected_gso(struct sock *sk, struct sk_buff *skb)
|
|
{
|
|
if (!skb_is_gso(skb))
|
|
return false;
|
|
|
|
if (skb_shinfo(skb)->gso_type & SKB_GSO_UDP_L4 && !udp_sk(sk)->accept_udp_l4)
|
|
return true;
|
|
|
|
if (skb_shinfo(skb)->gso_type & SKB_GSO_FRAGLIST && !udp_sk(sk)->accept_udp_fraglist)
|
|
return true;
|
|
|
|
/* GSO packets lacking the SKB_GSO_UDP_TUNNEL/_CSUM bits might still
|
|
* land in a tunnel as the socket check in udp_gro_receive cannot be
|
|
* foolproof.
|
|
*/
|
|
if (udp_encap_needed() &&
|
|
READ_ONCE(udp_sk(sk)->encap_rcv) &&
|
|
!(skb_shinfo(skb)->gso_type &
|
|
(SKB_GSO_UDP_TUNNEL | SKB_GSO_UDP_TUNNEL_CSUM)))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
#define udp_portaddr_for_each_entry(__sk, list) \
|
|
hlist_for_each_entry(__sk, list, __sk_common.skc_portaddr_node)
|
|
|
|
#define udp_portaddr_for_each_entry_rcu(__sk, list) \
|
|
hlist_for_each_entry_rcu(__sk, list, __sk_common.skc_portaddr_node)
|
|
|
|
#define IS_UDPLITE(__sk) (__sk->sk_protocol == IPPROTO_UDPLITE)
|
|
|
|
#endif /* _LINUX_UDP_H */
|