之前说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,是补充,这个后话再说了.