链路层到网络层(eth->ip->udp)

来源:互联网 发布:淘宝限额5000怎么修改 编辑:程序博客网 时间:2024/06/07 06:32

0x01 缘由

     上章节已经简单的学习了从硬件驱动层到链路层的相关过程,计划这章接讲解arp_rcv,但是考虑整个学习计划还是先从ip_rcv为主线展开学习,还是跟着上次的调试节奏进入网络层的源码跟踪和学习。

0x02 整体调用栈

 

0x03 源码跟踪

1.跟踪ip_rcv

 int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev){    struct iphdr *iph;    u32 len;    /* 这个函数不会接收不是发给这个主机的数据包,如果主机是工作在混杂模式,这个数据包已经由        netif_receive_skb()去完成处理了。注意,这里所说的“不属于”这个主机,是指在这个包目标        主机的MAC地址不是本机,而不是L3层的ip地址。所以,它不包括路由的包。*/    if (skb->pkt_type == PACKET_OTHERHOST)        goto drop;    //相关统计    IP_UPD_PO_STATS_BH(dev_net(dev), IPSTATS_MIB_IN, skb->len);    /* 共享的检查,如果是共享的数据包,因为它可能需要修改skb中的信息,所以要先复制一个副本,        再作进一步的处理。*/    if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) {        IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_INDISCARDS);        goto out;    }    /*下面主要做一些校验检查:            1、数据包长度至少有一个ip header的长度,大于等于20;            2、版本号为4;            3、校验和正确;            4、没有多余的长度;    */    if (!pskb_may_pull(skb, sizeof(struct iphdr)))        goto inhdr_error;    /* 提取ip头结构,这个常在网络设备开发中要处理。*/    iph = ip_hdr(skb);    /* ip头小于5 * 4 (20),版本号不等于4 */    if (iph->ihl < 5 || iph->version != 4)        goto inhdr_error;  /* 处理数据填充*/    if (!pskb_may_pull(skb, iph->ihl*4))        goto inhdr_error;    /* 提取ip头结构*/    iph = ip_hdr(skb);    /* 快速校验和 */    if (unlikely(ip_fast_csum((u8 *)iph, iph->ihl)))        goto inhdr_error;    /* 数据包总长度 */    len = ntohs(iph->tot_len);    /* 如果skb的总长度小于len,说明存在问题 */    if (skb->len < len) {        IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_INTRUNCATEDPKTS);        goto drop;    } else if (len < (iph->ihl*4)) /* 如果长度小于头部长度,说明数据存在问题 */        goto inhdr_error;    /* 我们的传输介质可能已经填充了缓冲区。现在我们知道此包为ip包,        我们可以处理侦到真实长度。        注意现在这个包skb->len = ntohs(iph->tot_len)     */    if (pskb_trim_rcsum(skb, len)) {        IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_INDISCARDS);        goto drop;    }    /* Remove any debris in the socket control block */    memset(IPCB(skb), 0, sizeof(struct inet_skb_parm));    /* Must drop socket now because of tproxy. */    skb_orphan(skb);     /* 此处NF_HOOK 宏会先检测是否使用netfilter,如果下了netfiler 钩子函数,则走netfilter处理流程,否则走ip_rcv_finish */    return NF_HOOK(PF_INET, NF_INET_PRE_ROUTING, skb, dev, NULL, ip_rcv_finish); //此处调试不调试NF_HOOKinhdr_error:    IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_INHDRERRORS);drop:    kfree_skb(skb);out:    return NET_RX_DROP;}

2.跟踪ip_rcv_finish

   static int ip_rcv_finish(struct sk_buff *skb){    const struct iphdr *iph = ip_hdr(skb);    struct rtable *rt;    /*     *    初始化数据包的虚拟路径缓存。它表述数据包在Linux网络中遍历的路径。     *  刚进来没有路由信息,于是调用ip_route_input去初始化。     */    if (skb_dst(skb) == NULL) {        int err = ip_route_input(skb, iph->daddr, iph->saddr, iph->tos,                     skb->dev); //下次分析整个路由        if (unlikely(err)) {            if (err == -EHOSTUNREACH)                IP_INC_STATS_BH(dev_net(skb->dev),                        IPSTATS_MIB_INADDRERRORS);            else if (err == -ENETUNREACH)                IP_INC_STATS_BH(dev_net(skb->dev),                        IPSTATS_MIB_INNOROUTES);            goto drop;        }    }    /* 略 */    /* 头部长度大于20,说明存在ip选项,对ip选项进行处理。*/    if (iph->ihl > 5 && ip_rcv_options(skb))        goto drop;    /* 查找路由信息 */    rt = skb_rtable(skb);    if (rt->rt_type == RTN_MULTICAST) { //多播        IP_UPD_PO_STATS_BH(dev_net(rt->u.dst.dev), IPSTATS_MIB_INMCAST,                skb->len);    } else if (rt->rt_type == RTN_BROADCAST) //广播        IP_UPD_PO_STATS_BH(dev_net(rt->u.dst.dev), IPSTATS_MIB_INBCAST,                skb->len);    return dst_input(skb); //此处一个inline函数,进行下一步处理,如果是发往本地走ip_local_deliver,如果是转发到其他走ip_forward。drop:    kfree_skb(skb);    return NET_RX_DROP;}

3.跟踪ip_local_deliver

/* *     将包纵向转发到更高层的协议; */int ip_local_deliver(struct sk_buff *skb){    /*     *    重组ip分片,具体ip分片的详细细节暂不做分析.     */    if (ip_hdr(skb)->frag_off & htons(IP_MF | IP_OFFSET)) {        if (ip_defrag(skb, IP_DEFRAG_LOCAL_DELIVER))            return 0;    }    /* 此处是netfilter处理,如果存在钩子函数则处理钩子函数,否则直接调用ip_local_deliver_finish*/    return NF_HOOK(PF_INET, NF_INET_LOCAL_IN, skb, skb->dev, NULL,               ip_local_deliver_finish);}

4.跟踪ip_local_deliver_finish

static int ip_local_deliver_finish(struct sk_buff *skb){    struct net *net = dev_net(skb->dev);    __skb_pull(skb, ip_hdrlen(skb));    /* Point into the IP datagram, just past the header. */    skb_reset_transport_header(skb);    rcu_read_lock();    {        int protocol = ip_hdr(skb)->protocol;        int hash, raw;        const struct net_protocol *ipprot;    resubmit:        /* 对每个报文要先检查是否是 RAW 报文,检查的方式是从 raw_v4_hash 表中查找是否有对应的 RAW,             在 inet_create 函数中 RAW socket 曾经调用了自己 hash 函数,将自己挂在了此 hash 表中。             然而不是 RAW 的 socket 不会往此 hash 表中加数据。向上层分发收到的目的地址是本机的包,首             先分发原始套接字,注意:由于 ping 程序本身是 raw 应用,所以会进入到 raw_v4_input 中,但             是实际并没有通过这个函数将报文收到应用层,为什么呢?请参考该函数的讲解,而对于其它 RAW IP             的应用,比如 OSPF 就会通过此函数把报文发送到用户层*/        raw = raw_local_deliver(skb, protocol);        /*没有冲突的哈希表,直接定向到协议指针*/        hash = protocol & (MAX_INET_PROTOS - 1);        ipprot = rcu_dereference(inet_protos[hash]);        if (ipprot != NULL) {            int ret;            if (!net_eq(net, &init_net) && !ipprot->netns_ok) {                if (net_ratelimit())                    printk("%s: proto %d isn't netns-ready\n",                        __func__, protocol);                kfree_skb(skb);                goto out;            }            if (!ipprot->no_policy) {                if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) {                    kfree_skb(skb);                    goto out;                }                nf_reset(skb);            }            /* 对应的操作函数是 tcp_v4_rcv,icmp_rcv,igmp_rcv,udp_rcv,在本书目前的例子中,由于是                ping 应用程序,那么它应该是 icmp_rcv */            ret = ipprot->handler(skb);  //此处已经将报文传到上层,离开网络层。            if (ret < 0) {                protocol = -ret;                goto resubmit;            }            IP_INC_STATS_BH(net, IPSTATS_MIB_INDELIVERS);        } else {            if (!raw) {                /* 如果没有对应的高层协议处理此报文,要么发送目标不可达,要么释放该报文.*/                if (xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) {                    IP_INC_STATS_BH(net, IPSTATS_MIB_INUNKNOWNPROTOS);                    icmp_send(skb, ICMP_DEST_UNREACH,                          ICMP_PROT_UNREACH, 0);                }            } else                IP_INC_STATS_BH(net, IPSTATS_MIB_INDELIVERS);            kfree_skb(skb);        }    } out:    rcu_read_unlock();    return 0;}

0x04 总结

    上面的分析,忽略了路由的查找、ip分片的重组等信息,下章节分析路由相关信息。(学习过程,大神勿喷,多指正)
原创粉丝点击