UDP 数据包丢失

UDP 数据包丢失

据我所知,UDP数据包的接收流程是

  1. 检查 udp 标头是否有错误
  2. 将目的地与套接字匹配
  3. 如果没有这样的套接字,则发送错误消息
  4. 将数据包放入适当的套接字接收队列
  5. 唤醒进程等待该套接字的数据

但在上述阶段,什么才算/proc/net/udp是下降呢?上述任何一步失败是否构成掉落?或者仅当接收队列/缓冲区已满时才执行?

答案1

在 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);
    
  2. 错误的校验和帧

    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);
    
  3. 无法阅读

    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);
    
  4. 接收期间失败

    static int udp_queue_rcv_one_skb(struct sock *sk, struct sk_buff *skb)
    {
            ...
    drop:
            ...
            atomic_inc(&sk->sk_drops);
    
  5. 尝试克隆消息时多播处理期间失败

    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);
    
    

相关内容