net: selectively purge error queue in IP_RECVERR / IPV6_RECVERR
authorEric Dumazet <edumazet@google.com>
Fri, 18 Aug 2023 17:41:45 +0000 (17:41 +0000)
committerDavid S. Miller <davem@davemloft.net>
Sun, 20 Aug 2023 14:17:47 +0000 (15:17 +0100)
Setting IP_RECVERR and IPV6_RECVERR options to zero currently
purges the socket error queue, which was probably not expected
for zerocopy and tx_timestamp users.

I discovered this issue while preparing commit 6b5f43ea0815
("inet: move inet->recverr to inet->inet_flags"), I presume this
change does not need to be backported to stable kernels.

Add skb_errqueue_purge() helper to purge error messages only.

Signed-off-by: Eric Dumazet <edumazet@google.com>
Cc: Willem de Bruijn <willemb@google.com>
Cc: Soheil Hassas Yeganeh <soheil@google.com>
Reviewed-by: Willem de Bruijn <willemb@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/linux/skbuff.h
net/core/skbuff.c
net/ipv4/ip_sockglue.c
net/ipv6/ipv6_sockglue.c

index 9aec136..4174c4b 100644 (file)
@@ -3180,6 +3180,7 @@ static inline void skb_queue_purge(struct sk_buff_head *list)
 }
 
 unsigned int skb_rbtree_purge(struct rb_root *root);
+void skb_errqueue_purge(struct sk_buff_head *list);
 
 void *__netdev_alloc_frag_align(unsigned int fragsz, unsigned int align_mask);
 
index 117d36b..faa6c86 100644 (file)
@@ -3745,6 +3745,27 @@ unsigned int skb_rbtree_purge(struct rb_root *root)
        return sum;
 }
 
+void skb_errqueue_purge(struct sk_buff_head *list)
+{
+       struct sk_buff *skb, *next;
+       struct sk_buff_head kill;
+       unsigned long flags;
+
+       __skb_queue_head_init(&kill);
+
+       spin_lock_irqsave(&list->lock, flags);
+       skb_queue_walk_safe(list, skb, next) {
+               if (SKB_EXT_ERR(skb)->ee.ee_origin == SO_EE_ORIGIN_ZEROCOPY ||
+                   SKB_EXT_ERR(skb)->ee.ee_origin == SO_EE_ORIGIN_TIMESTAMPING)
+                       continue;
+               __skb_unlink(skb, list);
+               __skb_queue_tail(&kill, skb);
+       }
+       spin_unlock_irqrestore(&list->lock, flags);
+       __skb_queue_purge(&kill);
+}
+EXPORT_SYMBOL(skb_errqueue_purge);
+
 /**
  *     skb_queue_head - queue a buffer at the list head
  *     @list: list to use
index 61b2e7b..54ad0f0 100644 (file)
@@ -976,7 +976,7 @@ int do_ip_setsockopt(struct sock *sk, int level, int optname,
        case IP_RECVERR:
                inet_assign_bit(RECVERR, sk, val);
                if (!val)
-                       skb_queue_purge(&sk->sk_error_queue);
+                       skb_errqueue_purge(&sk->sk_error_queue);
                return 0;
        case IP_RECVERR_RFC4884:
                if (val < 0 || val > 1)
index d19577a..0e2a084 100644 (file)
@@ -923,7 +923,7 @@ done:
                        goto e_inval;
                np->recverr = valbool;
                if (!val)
-                       skb_queue_purge(&sk->sk_error_queue);
+                       skb_errqueue_purge(&sk->sk_error_queue);
                retv = 0;
                break;
        case IPV6_FLOWINFO_SEND: