udp: cope with UDP GRO packet misdirection

In some scenarios, the GRO engine can assemble an UDP GRO packet
that ultimately lands on a non GRO-enabled socket.
This patch tries to address the issue explicitly checking for the UDP
socket features before enqueuing the packet, and eventually segmenting
the unexpected GRO packet, as needed.

We must also cope with re-insertion requests: after segmentation the
UDP code calls the helper introduced by the previous patches, as needed.

Segmentation is performed by a common helper, which takes care of
updating socket and protocol stats is case of failure.

rfc v3 -> v1
 - fix compile issues with rxrpc
 - when gso_segment returns NULL, treat is as an error
 - added 'ipv4' argument to udp_rcv_segment()

rfc v2 -> rfc v3
 - moved udp_rcv_segment() into net/udp.h, account errors to socket
   and ns, always return NULL or segs list

Signed-off-by: Paolo Abeni <pabeni@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Paolo Abeni
2018-11-07 12:38:33 +01:00
committed by David S. Miller
parent 80bde363f9
commit cf329aa42b
4 changed files with 88 additions and 10 deletions

View File

@@ -417,17 +417,24 @@ static inline int copy_linear_skb(struct sk_buff *skb, int len, int off,
} while(0)
#if IS_ENABLED(CONFIG_IPV6)
#define __UDPX_INC_STATS(sk, field) \
do { \
if ((sk)->sk_family == AF_INET) \
__UDP_INC_STATS(sock_net(sk), field, 0); \
else \
__UDP6_INC_STATS(sock_net(sk), field, 0); \
} while (0)
#define __UDPX_MIB(sk, ipv4) \
({ \
ipv4 ? (IS_UDPLITE(sk) ? sock_net(sk)->mib.udplite_statistics : \
sock_net(sk)->mib.udp_statistics) : \
(IS_UDPLITE(sk) ? sock_net(sk)->mib.udplite_stats_in6 : \
sock_net(sk)->mib.udp_stats_in6); \
})
#else
#define __UDPX_INC_STATS(sk, field) __UDP_INC_STATS(sock_net(sk), field, 0)
#define __UDPX_MIB(sk, ipv4) \
({ \
IS_UDPLITE(sk) ? sock_net(sk)->mib.udplite_statistics : \
sock_net(sk)->mib.udp_statistics; \
})
#endif
#define __UDPX_INC_STATS(sk, field) \
__SNMP_INC_STATS(__UDPX_MIB(sk, (sk)->sk_family == AF_INET), field)
#ifdef CONFIG_PROC_FS
struct udp_seq_afinfo {
sa_family_t family;
@@ -461,4 +468,26 @@ DECLARE_STATIC_KEY_FALSE(udpv6_encap_needed_key);
void udpv6_encap_enable(void);
#endif
static inline struct sk_buff *udp_rcv_segment(struct sock *sk,
struct sk_buff *skb, bool ipv4)
{
struct sk_buff *segs;
/* the GSO CB lays after the UDP one, no need to save and restore any
* CB fragment
*/
segs = __skb_gso_segment(skb, NETIF_F_SG, false);
if (unlikely(IS_ERR_OR_NULL(segs))) {
int segs_nr = skb_shinfo(skb)->gso_segs;
atomic_add(segs_nr, &sk->sk_drops);
SNMP_ADD_STATS(__UDPX_MIB(sk, ipv4), UDP_MIB_INERRORS, segs_nr);
kfree_skb(skb);
return NULL;
}
consume_skb(skb);
return segs;
}
#endif /* _UDP_H */