Networking

UDP 丟包

  • April 15, 2021

我了解 UDP 數據包的接收流是

  1. 檢查 udp 標頭是否有錯誤
  2. 將目標與套接字匹配
  3. 如果沒有這樣的套接字,則返回錯誤消息
  4. 將數據包放入適當的套接字接收隊列
  5. 喚醒程序等待該套接字的數據

但在上述階段,什麼/proc/net/udp算作下降?上述任何步驟的失敗是否構成下降?還是僅當接收隊列/緩衝區已滿時?

在 Linux 5.4.66 中,偽文件/proc/net/udp由以下函式生成net/ipv4/udp.c

int udp4_seq_show(struct seq_file *seq, void *v)
{
       seq_setwidth(seq, 127);
       if (v == SEQ_START_TOKEN)
               seq_puts(seq, "  sl  local_address rem_address   st tx_queue "
                          "rx_queue tr tm->when retrnsmt   uid  timeout "
                          "inode ref pointer drops");
       else {
               struct udp_iter_state *state = seq->private;

               udp4_format_sock(v, seq, state->bucket);
       }
       seq_pad(seq, '\n');
       return 0;
}

udp4_format_sock()在同一個文件中呼叫:

static void udp4_format_sock(struct sock *sp, struct seq_file *f,
               int bucket)
{
       struct inet_sock *inet = inet_sk(sp);
       __be32 dest = inet->inet_daddr;
       __be32 src  = inet->inet_rcv_saddr;
       __u16 destp       = ntohs(inet->inet_dport);
       __u16 srcp        = ntohs(inet->inet_sport);

       seq_printf(f, "%5d: %08X:%04X %08X:%04X"
               " %02X %08X:%08X %02X:%08lX %08X %5u %8d %lu %d %pK %u",
               bucket, src, srcp, dest, destp, sp->sk_state,
               sk_wmem_alloc_get(sp),
               udp_rqueue_get(sp),
               0, 0L, 0,
               from_kuid_munged(seq_user_ns(f), sock_i_uid(sp)),
               0, sock_i_ino(sp),
               refcount_read(&sp->sk_refcnt), sp,
               atomic_read(&sp->sk_drops));
}

請注意,該drops欄位的值來自atomic_read(&sp->sk_drops). atomic_inc(&sk->sk_drops)在幾個地方使用該值會增加。

  1. 接收隊列已滿
int __udp_enqueue_schedule_skb(struct sock *sk, struct sk_buff *skb)
{
        ...
       /* try to avoid the costly atomic add/sub pair when the receive
        * queue is full; always allow at least a packet
        */
       rmem = atomic_read(&sk->sk_rmem_alloc);
       if (rmem > sk->sk_rcvbuf)
                goto drop;
      ...
drop:
       atomic_inc(&sk->sk_drops);
  1. 錯誤的校驗和幀
static struct sk_buff *__first_packet_length(struct sock *sk,
                                            struct sk_buff_head *rcvq,
                                            int *total)
{
       struct sk_buff *skb;

       while ((skb = skb_peek(rcvq)) != NULL) {
               if (udp_lib_checksum_complete(skb)) {
                       ...
                       atomic_inc(&sk->sk_drops);
  1. 閱讀失敗
int udp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int noblock,
               int flags, int *addr_len)
{
       ...
       if (checksum_valid || udp_skb_csum_unnecessary(skb)) {
               if (udp_skb_is_linear(skb))
                       err = copy_linear_skb(skb, copied, off, &msg->msg_iter);
               else
                       err = skb_copy_datagram_msg(skb, off, msg, copied);
       } else {
               err = skb_copy_and_csum_datagram_msg(skb, off, msg);

               if (err == -EINVAL)
                       goto csum_copy_err;
       }

       if (unlikely(err)) {
               if (!peeking) {
                       atomic_inc(&sk->sk_drops);
  1. 接收失敗
static int udp_queue_rcv_one_skb(struct sock *sk, struct sk_buff *skb)
{
       ...
drop:
       ...
       atomic_inc(&sk->sk_drops);
  1. 嘗試複製消息時多播處理期間的失敗
static int __udp4_lib_mcast_deliver(struct net *net, struct sk_buff *skb,
                                   struct udphdr  *uh,
                                   __be32 saddr, __be32 daddr,
                                   struct udp_table *udptable,
                                   int proto)
{
       ...
       sk_for_each_entry_offset_rcu(sk, node, &hslot->head, offset) {
               ...
               nskb = skb_clone(skb, GFP_ATOMIC);

               if (unlikely(!nskb)) {
                       atomic_inc(&sk->sk_drops);

引用自:https://unix.stackexchange.com/questions/611981