Fold kfree_skb and consume_skb for tun_put_user into tun_put_user and rework kfree_skb to take a drop reason. Add drop reason to all drop sites and ensure that all failing paths properly increment drop counter. Signed-off-by: Jon Kohler --- drivers/net/tun.c | 51 +++++++++++++++++++++++++++++++---------------- 1 file changed, 34 insertions(+), 17 deletions(-) diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 68ad46ab04a4..e0f5e1fe4bd0 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -2035,6 +2035,7 @@ static ssize_t tun_put_user(struct tun_struct *tun, struct sk_buff *skb, struct iov_iter *iter) { + enum skb_drop_reason drop_reason = SKB_DROP_REASON_NOT_SPECIFIED; struct tun_pi pi = { 0, skb->protocol }; ssize_t total; int vlan_offset = 0; @@ -2051,8 +2052,11 @@ static ssize_t tun_put_user(struct tun_struct *tun, total = skb->len + vlan_hlen + vnet_hdr_sz; if (!(tun->flags & IFF_NO_PI)) { - if (iov_iter_count(iter) < sizeof(pi)) - return -EINVAL; + if (iov_iter_count(iter) < sizeof(pi)) { + ret = -EINVAL; + drop_reason = SKB_DROP_REASON_PKT_TOO_SMALL; + goto drop; + } total += sizeof(pi); if (iov_iter_count(iter) < total) { @@ -2060,8 +2064,11 @@ static ssize_t tun_put_user(struct tun_struct *tun, pi.flags |= TUN_PKT_STRIP; } - if (copy_to_iter(&pi, sizeof(pi), iter) != sizeof(pi)) - return -EFAULT; + if (copy_to_iter(&pi, sizeof(pi), iter) != sizeof(pi)) { + ret = -EFAULT; + drop_reason = SKB_DROP_REASON_SKB_UCOPY_FAULT; + goto drop; + } } if (vnet_hdr_sz) { @@ -2070,8 +2077,10 @@ static ssize_t tun_put_user(struct tun_struct *tun, ret = tun_vnet_hdr_tnl_from_skb(tun->flags, tun->dev, skb, &hdr); - if (ret) - return ret; + if (ret) { + drop_reason = SKB_DROP_REASON_DEV_HDR; + goto drop; + } /* * Drop the packet if the configured header size is too small @@ -2080,8 +2089,10 @@ static ssize_t tun_put_user(struct tun_struct *tun, gso = (struct virtio_net_hdr *)&hdr; ret = __tun_vnet_hdr_put(vnet_hdr_sz, tun->dev->features, iter, gso); - if (ret) - return ret; + if (ret) { + drop_reason = SKB_DROP_REASON_DEV_HDR; + goto drop; + } } if (vlan_hlen) { @@ -2094,23 +2105,33 @@ static ssize_t tun_put_user(struct tun_struct *tun, vlan_offset = offsetof(struct vlan_ethhdr, h_vlan_proto); ret = skb_copy_datagram_iter(skb, 0, iter, vlan_offset); - if (ret || !iov_iter_count(iter)) - goto done; + if (ret || !iov_iter_count(iter)) { + drop_reason = SKB_DROP_REASON_DEV_HDR; + goto drop; + } ret = copy_to_iter(&veth, sizeof(veth), iter); - if (ret != sizeof(veth) || !iov_iter_count(iter)) - goto done; + if (ret != sizeof(veth) || !iov_iter_count(iter)) { + drop_reason = SKB_DROP_REASON_DEV_HDR; + goto drop; + } } skb_copy_datagram_iter(skb, vlan_offset, iter, skb->len - vlan_offset); -done: /* caller is in process context, */ preempt_disable(); dev_sw_netstats_tx_add(tun->dev, 1, skb->len + vlan_hlen); preempt_enable(); + consume_skb(skb); + return total; + +drop: + dev_core_stats_tx_dropped_inc(tun->dev); + kfree_skb_reason(skb, drop_reason); + return ret; } static void *tun_ring_recv(struct tun_file *tfile, int noblock, int *err) @@ -2182,10 +2203,6 @@ static ssize_t tun_do_read(struct tun_struct *tun, struct tun_file *tfile, struct sk_buff *skb = ptr; ret = tun_put_user(tun, tfile, skb, to); - if (unlikely(ret < 0)) - kfree_skb(skb); - else - consume_skb(skb); } return ret; -- 2.43.0