TaterLi's LazyBlog

自言自语,不喜绕路,科学上网,远离天国.

@TaterLi1周前

02/10
19:00
技术控

LwIP 代码分析(etharp_output再深入) – 第五集

之前说etharp_output远远不是终点,听起来很吓人啊,不过确实的,终点其实是low_level_output,不对,其实是ETH->DMATPDR = 0,但是我们只要看到low_level_output就行了,那里数据包已经全部构建完成了.

简化后就是这样:

err_t
etharp_output(struct netif *netif, struct pbuf *q, const ip4_addr_t *ipaddr)
{
    const struct eth_addr *dest;
    struct eth_addr mcastaddr;
    const ip4_addr_t *dst_addr = ipaddr;

    if (ip4_addr_isbroadcast(ipaddr, netif))
    {
        /* 如果是广播包.MAC设置成全0xFF就行. */
    }
    else if (ip4_addr_ismulticast(ipaddr))
    {
        /* 多播处理,OUI前三位设置成多播用就行. */
    }
    else     /* 否则正常包. */
    {
        s8_t i;

        /* 从ARP缓存找. */
        if ((arp_table[etharp_cached_entry].state >= ETHARP_STATE_STABLE) &&
                (ip4_addr_cmp(dst_addr, &arp_table[etharp_cached_entry].ipaddr)))
        {
            ETHARP_STATS_INC(etharp.cachehit);
            return etharp_output_to_arp_index(netif, q, etharp_cached_entry);
        }


        /* 从所有ARP列表再找(第一步找不到) */
        for (i = 0; i < ARP_TABLE_SIZE; i++)
        {
            if ((arp_table[i].state >= ETHARP_STATE_STABLE) &&
                    (ip4_addr_cmp(dst_addr, &arp_table[i].ipaddr)))
            {
                ETHARP_SET_HINT(netif, i);
                return etharp_output_to_arp_index(netif, q, i);
            }
        }

        /* 如果不行,只能去公网问了. */
        return etharp_query(netif, dst_addr, q);
    }

    /* 除了广播和多播,都不会来这里.因为早return了. */
    return ethernet_output(netif, q, (struct eth_addr *)(netif->hwaddr), dest, ETHTYPE_IP);
}

除了第一次查询,其后缓存中都会有,我们就假设都有缓存把.这就说明无论如何都会到etharp_output_to_arp_index.并且假设他们在ARP表中都是靠谱的.那么直接到ethernet_output.

一下子攻破了两个函数,最后红框处添加以太网头.

以太网头很简单,就是SRC和DST,然后和类型.然后整个帧就OK了.到netif->linkoutput(netif, p),也就是lowlevel_input.

如图,以太网帧.

IP帧

UDP帧

一个包,就这么发出去了.后面的00,是补充,这个后话再说了.

 

LwIP 代码分析(etharp_output再深入) – 第五集