diff options
author | Eelco Chaudron <echaudro@redhat.com> | 2021-07-05 07:57:41 -0400 |
---|---|---|
committer | Ilya Maximets <i.maximets@ovn.org> | 2021-07-09 21:35:20 +0200 |
commit | 1aa1f591422ec3e34ae9becf257081645500de6c (patch) | |
tree | 6ce36c8ade72f93bdb50ef19a0b3daeef296960b | |
parent | d127fa6d2bd7b5679cafab1f92b1237f374664bc (diff) | |
download | openvswitch-1aa1f591422ec3e34ae9becf257081645500de6c.tar.gz |
netdev-linux: Ignore TSO packets when TSO is not enabled for userspace.
When TSO is disabled from a userspace forwarding datapath perspective,
but TSO has been wrongly enabled on the kernel side, log a warning
message, and drop the packet. With the current implementation,
OVS will crash.
[i.maximets]:
The call stack looks like this:
0 dp_packet_set_size (b=0x0, b=0x0, v=13028) at lib/dp-packet.h:578
1 netdev_linux_batch_rxq_recv_sock at lib/netdev-linux.c:1310
2 netdev_linux_rxq_recv at lib/netdev-linux.c
3 netdev_rxq_recv at lib/netdev.c
4 dp_netdev_process_rxq_port at lib/dpif-netdev.c
The problem is that the code assumes that (mmsgs[i].msg_len > std_len)
can only be true if userpace-tso is enabled and additional buffers are
provided to the kernel. However, since recvmmsg() is called with
MSG_TRUNC, the resulting msg_len reflects the original packet size
before truncation, and it can be larger than the buffer if TSO / GRO
is enabled on the network interface. If TSO support for user space is
not enabled in OVS, the aux_bufs are not allocated and are left NULL,
resulting in a crash.
Fixes: 73858f9dbe83 ("netdev-linux: Prepend the std packet in the TSO packet")
Fixes: 2109841b7984 ("Use batch process recv for tap and raw socket in netdev datapath")
Signed-off-by: Eelco Chaudron <echaudro@redhat.com>
Acked-by: Flavio Leitner <fbl@sysclose.org>
Signed-off-by: Ilya Maximets <i.maximets@ovn.org>
-rw-r--r-- | lib/netdev-linux.c | 20 |
1 files changed, 17 insertions, 3 deletions
diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c index 5a747f73c..557c3f5e9 100644 --- a/lib/netdev-linux.c +++ b/lib/netdev-linux.c @@ -1277,14 +1277,28 @@ netdev_linux_batch_rxq_recv_sock(struct netdev_rxq_linux *rx, int mtu, for (i = 0; i < retval; i++) { struct dp_packet *pkt; - if (mmsgs[i].msg_len < ETH_HEADER_LEN) { + if (mmsgs[i].msg_hdr.msg_flags & MSG_TRUNC + || mmsgs[i].msg_len < ETH_HEADER_LEN) { struct netdev *netdev_ = netdev_rxq_get_netdev(&rx->up); struct netdev_linux *netdev = netdev_linux_cast(netdev_); + /* The rx->aux_bufs[i] will be re-used next time. */ dp_packet_delete(buffers[i]); netdev->rx_dropped += 1; - VLOG_WARN_RL(&rl, "%s: Dropped packet: less than ether hdr size", - netdev_get_name(netdev_)); + if (mmsgs[i].msg_hdr.msg_flags & MSG_TRUNC) { + /* Data is truncated, so the packet is corrupted, and needs + * to be dropped. This can happen if TSO/GRO is enabled in + * the kernel, but not in userspace, i.e. there is no dp + * buffer to store the full packet. */ + VLOG_WARN_RL(&rl, + "%s: Dropped packet: Too big. GRO/TSO enabled?", + netdev_get_name(netdev_)); + } else { + VLOG_WARN_RL(&rl, + "%s: Dropped packet: less than ether hdr size", + netdev_get_name(netdev_)); + } + continue; } |