文章详情

  • 游戏榜单
  • 软件榜单
关闭导航
热搜榜
热门下载
热门标签
php爱好者> php文档>udp_rcv分析

udp_rcv分析

时间:2011-01-05  来源:njupted

int udp_rcv(struct sk_buff *skb)
{
    return __udp4_lib_rcv(skb, udp_hash, IPPROTO_UDP);
}

int __udp4_lib_rcv(struct sk_buff *skb, struct hlist_head udptable[], int proto)
{
    struct sock *sk;
    struct udphdr *uh = udp_hdr(skb); //获取skb的UDP header的指针;

    unsigned short ulen;
    struct rtable *rt = (struct rtable*)skb->dst;
    //routing table, 这里强制类型转换, 保证内存的分配大小为sizeof(struct rtable). 注意struct rtalbe的定义中第一个便是strcut dst_entry. 这牵扯到C语言中的多层继承, 详见http://blog.chinaunix.net/u3/114767/showart_2282422.html

    
    __be32 saddr = ip_hdr(skb)->saddr; //源地址

    __be32 daddr = ip_hdr(skb)->daddr; //目的地址


    /*
     *    Validate the packet
     */
    if (!pskb_may_pull(skb, sizeof(struct udphdr))) //判断是否有足够的数据用于pull.

        goto drop;    /* No space for header. */

    ulen = ntohs(uh->len);
    if (ulen > skb->len)
        goto short_packet; //判断UDP头是否超过数据长度, AKA, 数据部分太短


    if (proto == IPPROTO_UDP) {
        /* UDP validates ulen. */
        if (ulen <sizeof(*uh) || pskb_trim_rcsum(skb, ulen)) //判断udphdr->len是否小于udphdr长度, 或者pskb_trim_rcsum();

            goto short_packet;
        uh = udp_hdr(skb);
    }

    if (udp4_csum_init(skb, uh, proto)) //初始化udp的checksum, 成功返回0.

        goto csum_error;

    if (rt->rt_flags & (RTCF_BROADCAST|RTCF_MULTICAST))
        return __udp4_lib_mcast_deliver(skb, uh, saddr, daddr, udptable); //检查是否多播和广播数据


    sk = __udp4_lib_lookup(dev_net(skb->dev), saddr, uh->source, daddr, uh->dest, inet_iif(skb), udptable);//查找此UDP包对应的socket, 主要是查找源目的地址和端口号都符合的socket


    if (sk != NULL) {
        int ret = 0;
        bh_lock_sock_nested(sk); //调用自旋锁

        if (!sock_owned_by_user(sk)) //然后判断当前sock是否被用户进程占用

            ret = udp_queue_rcv_skb(sk, skb); //

        else
            sk_add_backlog(sk, skb);
        bh_ulock_sock(sk);
        sock_put(sk);

        if (ret > 0)
            return -ret;
        return 0;
    }

    if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb))
        goto drop;
    nf_reset(skb);

    /* No socket. Drop packet silently, if chechsum is wrong */
    if (udp_lib_checksum_complete(skb))
        goto csum_error;

    UDP_INC_STATS_BH(UDP_MIB_NOPORTS, proto == IPPROTO_UDPLITE);
    icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);

    kfree_skb(skb);
    return 0;

short_packet:
    ...
csum_error:
    ...
drop:
    UDP_INC_STATS_BH(UDP_MIB_INERRORS, proto == IPPROTO_UDPLITE);
    kfree_skb(skb);
    return 0;
}


static inline struct udphdr *udp_hdr(const struct sk_buff *skb)
{
    return (struct udphdr *)skb_transport header(skb);
}

static inline unsigned char *skb_transport_header(const struct sk_buff *skb)
{
    return skb->head + skb->transport_header;
}

static inline unsigned char *skb_transport_header(const struct sk_buff *skb)
{
    return skb->transport_header;
}

两个skb_transport_header()函数的定义分别用于64位和32位操作系统, 依赖于宏NET_SKBUFF_DATA_USES_OFFSET是否定义. 有定义则使用64位模式, 无定义则使用32位模式.
在某些情况下如果udp_hdr()无法解析出UDPheader那么仍然可以使用ip_hdr(skb)->ihl*4找到UDP的偏移
Reference:  http://blog.sina.com.cn/s/blog_52355d840100arr7.html http://hi.baidu.com/jssfy/blog/item/2bf2571166f656caa7ef3f32.html

static inline int pskb_may_pull(struct sk_buff *skb, unsigned int len)
{
    if (likely(len <= skb_headlen(skb)))
        return 1;
    if (unlikely(len > skb->len))
        return 0;
    return __pskb_pull_tail(skb, len-skb_headlen(skb)) != NULL;
}

该函数判断是否有足够的数据用于pull. 如果线性buffer足够pull, 则返回1; 如果需要pull的数据超过skb->len则返回0.

static inline int pskb_trim_rcsum(struct sk_buff *skb, unsigned int len)
{
    if (likeliy(len >= skb->len))
        return 0;
    if (skb->ip_summed == CHECKSUM_COMPLETE)
        skb->ip_summed = CHECKSUM_NONE;
    return __pskb_trim(skb, len);
}

此函数用于裁剪skb底部的若干数据并重新计算checksum.  Reference: http://sh-neo.spaces.live.com/blog/cns!1E3CA285E5F9E122!632.entry

static inline int udp4_csum_init(struct sk_buff *skb, struct udphdr *uh, int proto)
{
    const struct iphdr *iph;
    int err;

    UDP_SKB_CB(skb)->partial_cov = 0; //操作skb->cb关于UDP的内容http://linux.chinaunix.net/bbs/archiver/tid-1043528.html

    UDP_SKB_CB(skb)->cscov = skb->len;

    if (proto == IPPROTO_UDPLITE) {
        err = udplite_checksum_init(skb, uh);
        if (err)
            return err;
    }
    
    iph = ip_hdr(skb);
    if (uh->check == 0) {
        skb->ip_summed = CHECKSUM_UNNECESSARY;
    } else if (skb->ip_summed == CHECKSUM_COMPLETE) {
        if (!csum_tcpudp_magic(iph->saddr, iph->daddr, skb->len, proto, skb->csum))
            skb->ip_summed = CHECKSUM_UNNECESSARY;
    }
    if (!skb_csum_unnecessary(skb))
        skb->csum = csum_tcpudp_nofold(iph->saddr, iph->daddr, skb->len, proto, 0);
    
    return 0;
}

此函数用于初始化UDP的checksum. 成功返回0. CHECKSUM_UNNECESSARY表示不需要再checksum了, 不然, 需要checksum包括udp头部在内的数据包内容并记录在skb->csum中.
__udp4_lib_lookup() 如果查找到了对应的socket, 则调用udp_queue_rcv_skb将skb放入udp队列, 然后返回0. 如果没有查找到对应的socket, 要向源地址发送一个ICMP不可到达消息.
相关阅读 更多 +
排行榜 更多 +
辰域智控app

辰域智控app

系统工具 下载
网医联盟app

网医联盟app

运动健身 下载
汇丰汇选App

汇丰汇选App

金融理财 下载