嵌入式技术论坛
直播中

刘秀英

7年用户 1295经验值
私信 关注
[经验]

一文解析RTT+LWIP icmp流

一直以来都想写个东西来看看一下晦涩的TCPIP协议那么今天下午我终于搞明白了这个晦涩的底层接口抽象了跟我来吧…
首先是看架构。操作系统初始化的时候开辟三个线程,也就是TCPIP主线程,以太网接收线程,以太网发送线程。整个TCPIP都是有这三个线程搞定的。

他们的描述分别是eth_rx_thread eth_tx_thread tcpip_thread 同时创建了俩邮箱用来通信。eth_tx_thread_mb eth_rx_thread_mb,其实还有一个,就是TCPIP的那个,这里不说了。

我从ICMP报文下手,因为PING通是网络的第一步。也是关键一步
首先我们假设有一台主机向我们发送ICMP报文,那么硬件首先接受到数据,并触发中断,如下

pk_counter = spi_read(EPKTCNT);
if (pk_counter)
{
/ a frame has been received /
eth_device_ready((struct eth_device*)&(enc28j60_dev->parent));

//这个表明硬件有数据,然后进入eth_device_ready这个函数中。

rt_err_t eth_device_ready(struct eth_device dev)
{
/将消息发送到以太网线程/
return rt_mb_send(ð_rx_thread_mb, (rt_uint32_t)dev);
}
//另外上可以看到就是发送了一个邮箱向RX线程发送的,RX本来是挂起的,因为没有邮箱,所以此时RX激活
p = device->eth_rx(&(device->parent) );
if (p != RT_NULL)
{
/通知上层 */

eth_input(p, device->netif); 
        }```

这里的 device->eth_rx是一个指针函数,指向了一个硬件的SPI读取硬件数据到PBUF中,然后返回P
然后到这里就已经完成了数据包的交付,实现了数据包被传进进出的数据包,紧接着调用了eth_input(p, device->netif);这里有两个参数
非常的参数第一个 P 的数据的 PBUF 表示接受第二个数据显示报告的第二个数据报告重要的接口类。 。
再来看

err_t eth_input(struct pbuf p, struct netif inp )
{
struct eth_hdr *ethhdr;

if(p != RT_NULL)
{
ifdef LINK_STATS
LINK_STATS_INC(link.recv);
endif / LINK_STATS /
ethhdr = p->payload; //取出来物理的地址头
switch(htons(ethhdr->type)) //判断包的类型
{
case ETHTYPE_IP: //IP报文
etharp_ip_input(inp, p); //输入arp层处理 Updates the ARP table using the given IP packet.
pbuf_header(p, -((rt_int16_t)sizeof(struct eth_hdr))); //取出ip报文头从接收的缓冲区中
if (tcpip_input(p, inp) != ERR_OK) //输入到TCPIP处理 这里就是构建一则消息,然后发给TCPip线程处理
{
/* discard packet */
pbuf_free(p); //如果失败释放接收缓存
}
break;
case ETHTYPE_ARP: //arp报文
etharp_arp_input(inp, (struct eth_addr *)inp->hwaddr, p);
break;
default: //其他的报文忽略
pbuf_free(p); //释放p
p = RT_NULL; //释放指针
break;
}
}
return ERR_OK;
}看到了这里对ARP和IP包分类了,也就是说arp这种无序TCPip干预,而是调用了ARP处理,此处咯去。 我们的是ICMP所以是IP报文,继续走tcpip_input(struct pbuf p, struct netif inp)
{
struct tcpip_msg *msg;

if (mbox != SYS_MBOX_NULL) {
msg = memp_malloc(MEMP_TCPIP_MSG_INPKT);
if (msg == NULL) {
return ERR_MEM;

msg->type = TCPIP_MSG_INPKT;
msg->msg.inp.p = p;
msg->msg.inp.netif = inp;
if (sys_mbox_trypost(mbox, msg) != ERR_OK) {
memp_free(MEMP_TCPIP_MSG_INPKT, msg);
return ERR_MEM;
}
return ERR_OK;
}
return ERR_VAL;
}```

这里就是向TCPIP发送邮箱了,可以看到邮箱的内容是lwip封装的一则消息。这消息里面包含了所有的网络接口,物理数据的位置,什么样的消息等信息

他发完邮箱他就被剥夺了控制权,这时候TCPIP线程被激活了,继续看

while (1) { / MAIN Loop /
sys_mbox_fetch(mbox, (void *)&msg);
switch (msg->type) {

if LWIP_NETCONN
case TCPIP_MSG_API:
LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: API message %p
“, (void *)msg));
msg->msg.apimsg->function(&(msg->msg.apimsg->msg));
break;

endif / LWIP_NETCONN /
case TCPIP_MSG_INPKT:
LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: PACKET %p
“, (void *)msg));

if LWIP_ARP //如果是ARP报文,那么调用ARP处理
if (msg->msg.inp.netif->flags & NETIF_FLAG_ETHARP) {
ethernet_input(msg->msg.inp.p, msg->msg.inp.netif);
} else
endif / LWIP_ARP / //如果不是,那么继续输入到ip_input,IP层处理 ,参数是接收到数据的头指针和网络接口
{ ip_input(msg->msg.inp.p, msg->msg.inp.netif);
}
memp_free(MEMP_TCPIP_MSG_INPKT, msg);
break;
if LWIP_NETIF_API
case TCPIP_MSG_NETIFAPI:
LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: Netif API message %p
“, (void *)msg));
msg->msg.netifapimsg->function(&(msg->msg.netifapimsg->msg));
break;

endif / LWIP_NETIF_API /
case TCPIP_MSG_CALLBACK:
LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: CALLBACK %p
“, (void *)msg));
msg->msg.cb.f(msg->msg.cb.ctx);
memp_free(MEMP_TCPIP_MSG_API, msg);
break;

case TCPIP_MSG_TIMEOUT:
LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: TIMEOUT %p
“, (void )msg));
sys_timeout(msg->msg.tmo.msecs, msg->msg.tmo.h, msg->msg.tmo.arg);
memp_free(MEMP_TCPIP_MSG_API, msg);
break;
case TCPIP_MSG_UNTIMEOUT:
LWIP_DEBUGF(TCPIP_DEBUG, (“tcpip_thread: UNTIMEOUT %p
“, (void )msg));
sys_untimeout(msg->msg.tmo.h, msg->msg.tmo.arg);
memp_free(MEMP_TCPIP_MSG_API, msg);
break;

default:
break;
}
}
}

这里看到我们的报文应该走这个

case TCPIP_MSG_INPKT:
LWIP_DEBUGF(TCPIP_DEBUG, (“tcpip_thread: PACKET %p
“, (void *)msg));

if LWIP_ARP //如果是ARP报文,那么调用ARP处理
if (msg->msg.inp.netif->flags & NETIF_FLAG_ETHARP) {
ethernet_input(msg->msg.inp.p, msg->msg.inp.netif);
} else
endif / LWIP_ARP / //如果不是,那么继续输入到ip_input,IP层处理 ,参数是接收到数据的头指针和网络接口
{ ip_input(msg->msg.inp.p, msg->msg.inp.netif);
}
memp_free(MEMP_TCPIP_MSG_INPKT, msg);
break;

所以继续向下就是

err_t
ip_input(struct pbuf p, struct netif inp)
{
struct ip_hdr iphdr;
struct netif netif;
u16_t iphdr_hlen;
u16_t iphdr_len;

if LWIP_DHCP
int check_ip_src=1;

endif / LWIP_DHCP /
IP_STATS_INC(ip.recv);
snmp_inc_ipinreceives();

/ identify the IP header / //判断MAc地址是否正确,不正确丢弃
iphdr = p->payload;
if (IPH_V(iphdr) != 4) {
LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_WARNING, (“IP packet dropped due to bad version number %”U16_F”
“, IPH_V(iphdr)));
ip_debug_print(p);
pbuf_free(p);
IP_STATS_INC(ip.err);
IP_STATS_INC(ip.drop);
snmp_inc_ipinhdrerrors();
return ERR_OK;
}

/ obtain IP header length in number of 32-bit words /
iphdr_hlen = IPH_HL(iphdr);
/ calculate IP header length in bytes /
iphdr_hlen = 4;
/ obtain ip length in bytes /
iphdr_len = ntohs(IPH_LEN(iphdr));
//长度不匹配释放
/ header length exceeds first pbuf length, or ip length exceeds total pbuf length? /
if ((iphdr_hlen > p->len) || (iphdr_len > p->tot_len)) {
if (iphdr_hlen > p->len) {
LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
(“IP header (len %”U16_F”) does not fit in first pbuf (len %”U16_F”), IP packet dropped.
“,
iphdr_hlen, p->len));
}
if (iphdr_len > p->tot_len) {
LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
(“IP (len %”U16_F”) is longer than pbuf (len %”U16_F”), IP packet dropped.
“,
iphdr_len, p->tot_len));
}
/ free (drop) packet pbufs */
pbuf_free(p);
IP_STATS_INC(ip.lenerr);
IP_STATS_INC(ip.drop);
snmp_inc_ipindiscards();
return ERR_OK;
}

/ verify checksum /

if CHECKSUM_CHECK_IP //校验失败也释放
if (inet_chksum(iphdr, iphdr_hlen) != 0) {

LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
("Checksum (0x%"X16_F") failed, IP packet dropped.
“, inet_chksum(iphdr, iphdr_hlen)));
ip_debug_print(p);
pbuf_free(p);
IP_STATS_INC(ip.chkerr);
IP_STATS_INC(ip.drop);
snmp_inc_ipinhdrerrors();
return ERR_OK;
}

endif
/* Trim pbuf. This should have been done at the netif layer,

but we’ll do it anyway just to be sure that its done. */
pbuf_realloc(p, iphdr_len);

/ match packet against an interface, i.e. is this packet for us? /

if LWIP_IGMP
if (ip_addr_ismulticast(&(iphdr->dest))) {
if ((inp->flags & NETIF_FLAG_IGMP) && (igmp_lookfor_group(inp, &(iphdr->dest)))) {
netif = inp;
} else {
netif = NULL;
}
} else

endif / LWIP_IGMP /
{
/ start trying with inp. if that’s not acceptable, start walking the
list of configured netifs.
‘first’ is used as a boolean to mark whether we started walking the list /
int first = 1;
netif = inp;
do {
LWIP_DEBUGF(IP_DEBUG, (“ip_input: iphdr->dest 0x%”X32_F” netif->ip_addr 0x%”X32_F” (0x%”X32_F”, 0x%”X32_F”, 0x%”X32_F”)
“,

iphdr->dest.addr, netif->ip_addr.addr,
iphdr->dest.addr & netif->netmask.addr,
netif->ip_addr.addr & netif->netmask.addr,
iphdr->dest.addr & ~(netif->netmask.addr)));
/ interface is up and configured? /
if ((netif_is_up(netif)) && (!ip_addr_isany(&(netif->ip_addr)))) {
/ unicast to this interface address? /
if (ip_addr_cmp(&(iphdr->dest), &(netif->ip_addr)) ||

/* or broadcast on this interface network address? */
ip_addr_isbroadcast(&(iphdr->dest), netif)) {
LWIP_DEBUGF(IP_DEBUG, ("ip_input: packet accepted on interface %c%c
“,

netif->name[0], netif->name[1]));

/* break out of for loop */
break;
}
}
if (first) {
first = 0;
netif = netif_list;
} else {
netif = netif->next;
}
if (netif == inp) {
netif = netif->next;
}
} while(netif != NULL);
}

if LWIP_DHCP
/* Pass DHCP messages regardless of destination address. DHCP traffic is addressed

using link layer addressing (such as Ethernet MAC) so we must not filter on IP.
According to RFC 1542 section 3.1.1, referred by RFC 2131).
/
if (netif == NULL) {
/ remote port is DHCP server? /
if (IPH_PROTO(iphdr) == IP_PROTO_UDP) {
LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE, (“ip_input: UDP packet to DHCP client port %”U16_F”
“,
ntohs(((struct udp_hdr )((u8_t )iphdr + iphdr_hlen))->dest)));
if (ntohs(((struct udp_hdr )((u8_t *)iphdr + iphdr_hlen))->dest) == DHCP_CLIENT_PORT) {
LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE, (“ip_input: DHCP packet accepted.
“));
netif = inp;
check_ip_src = 0;
}
}
}

endif / LWIP_DHCP /
/ broadcast or multicast packet source address? Compliant with RFC 1122: 3.2.1.3 /

if LWIP_DHCP
/ DHCP servers need 0.0.0.0 to be allowed as source address (RFC 1.1.2.2: 3.2.1.3/a) /
if (check_ip_src && (iphdr->src.addr != 0))

endif / LWIP_DHCP /
{ if ((ip_addr_isbroadcast(&(iphdr->src), inp)) ||

(ip_addr_ismulticast(&(iphdr->src)))) {
/ packet source is not valid /
LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, (“ip_input: packet source is not valid.
“));
/ free (drop) packet pbufs /
pbuf_free(p);
IP_STATS_INC(ip.drop);
snmp_inc_ipinaddrerrors();
snmp_inc_ipindiscards();
return ERR_OK;
}
}

/ packet not for us? /
if (netif == NULL) {
/ packet not for us, route or discard /
LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE, (“ip_input: packet not for us.
“));

if IP_FORWARD
/ non-broadcast packet? /
if (!ip_addr_isbroadcast(&(iphdr->dest), inp)) {
/ try to forward IP packet on (other) interfaces /
ip_forward(p, iphdr, inp);
} else

endif / IP_FORWARD /
{
snmp_inc_ipinaddrerrors();
snmp_inc_ipindiscards();
}
pbuf_free(p);
return ERR_OK;
}
/ packet consists of multiple fragments? /
if ((IPH_OFFSET(iphdr) & htons(IP_OFFMASK | IP_MF)) != 0) {

if IP_REASSEMBLY / packet fragment reassembly code present? /
LWIP_DEBUGF(IP_DEBUG, (“IP packet is a fragment (id=0x%04”X16_F” tot_len=%”U16_F” len=%”U16_F” MF=%”U16_F” offset=%”U16_F”), calling ip_reass()
“,
ntohs(IPH_ID(iphdr)), p->tot_len, ntohs(IPH_LEN(iphdr)), !!(IPH_OFFSET(iphdr) & htons(IP_MF)), (ntohs(IPH_OFFSET(iphdr)) & IP_OFFMASK)8));
/ reassemble the packet/
p = ip_reass(p);
/ packet not fully reassembled yet? */
if (p == NULL) {
return ERR_OK;
}
iphdr = p->payload;

else / IP_REASSEMBLY == 0, no packet fragment reassembly code present /
pbuf_free(p);
LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, (“IP packet dropped since it was fragmented (0x%”X16_F”) (while IP_REASSEMBLY == 0).
“,
ntohs(IPH_OFFSET(iphdr))));
IP_STATS_INC(ip.opterr);
IP_STATS_INC(ip.drop);
/ unsupported protocol feature /
snmp_inc_ipinunknownprotos();
return ERR_OK;

endif / IP_REASSEMBLY /
}

if IP_OPTIONS_ALLOWED == 0 / no support for IP options in the IP header? /
if LWIP_IGMP
/ there is an extra “router alert” option in IGMP messages which we allow for but do not police /
if((iphdr_hlen > IP_HLEN && (IPH_PROTO(iphdr) != IP_PROTO_IGMP)) {

else
if (iphdr_hlen > IP_HLEN) {

endif / LWIP_IGMP /
LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("IP packet dropped since there were IP options (while IP_OPTIONS_ALLOWED == 0).
“));
pbuf_free(p);
IP_STATS_INC(ip.opterr);
IP_STATS_INC(ip.drop);
/ unsupported protocol feature /
snmp_inc_ipinunknownprotos();
return ERR_OK;
}

endif / IP_OPTIONS_ALLOWED == 0 /
/ send to upper layers /
LWIP_DEBUGF(IP_DEBUG, (“ip_input:
“));
ip_debug_print(p);
LWIP_DEBUGF(IP_DEBUG, (“ip_input: p->len %”U16_F” p->tot_len %”U16_F”
“, p->len, p->tot_len));

current_netif = inp;
current_header = iphdr;

if LWIP_RAW
/ raw input did not eat the packet? /
if (raw_input(p, inp) == 0)

endif / LWIP_RAW /
{

switch (IPH_PROTO(iphdr)) {
if LWIP_UDP
case IP_PROTO_UDP:
if LWIP_UDPLITE
case IP_PROTO_UDPLITE:
endif / LWIP_UDPLITE /
snmp_inc_ipindelivers();
udp_input(p, inp);
break;
endif / LWIP_UDP /
if LWIP_TCP
case IP_PROTO_TCP:
snmp_inc_ipindelivers();
tcp_input(p, inp);
break;
endif / LWIP_TCP /
if LWIP_ICMP
case IP_PROTO_ICMP:
snmp_inc_ipindelivers();
icmp_input(p, inp);
break;
endif / LWIP_ICMP /
if LWIP_IGMP
case IP_PROTO_IGMP:
igmp_input(p,inp,&(iphdr->dest));
break;
endif / LWIP_IGMP /
default:
if LWIP_ICMP
/* send ICMP destination protocol unreachable unless is was a broadcast */
if (!ip_addr_isbroadcast(&(iphdr->dest), inp) &&
!ip_addr_ismulticast(&(iphdr->dest))) {
p->payload = iphdr;
icmp_dest_unreach(p, ICMP_DUR_PROTO);
}
endif / LWIP_ICMP /
pbuf_free(p);
LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("Unsupported transport protocol %"U16_F"
“, IPH_PROTO(iphdr)));

IP_STATS_INC(ip.proterr);
IP_STATS_INC(ip.drop);
snmp_inc_ipinunknownprotos();
}
}

current_netif = NULL;
current_header = NULL;

return ERR_OK;
}

我们的报文走这里

if LWIP_ICMP
/* send ICMP destination protocol unreachable unless is was a broadcast */
if (!ip_addr_isbroadcast(&(iphdr->dest), inp) &&
!ip_addr_ismulticast(&(iphdr->dest))) {
p->payload = iphdr;
icmp_dest_unreach(p, ICMP_DUR_PROTO);
}
endif / LWIP_ICMP /
可以看到,ICMP报文的回复在这里生成继续向下
void
icmp_dest_unreach(struct pbuf p, enum icmp_dur_type t)
{
icmp_send_response(p, ICMP_DUR, t);
}
可以看到后面还有,那好继续向下,其实这个就是ICMP回复了
static void
icmp_send_response(struct pbuf p, u8_t type, u8_t code)
{
struct pbuf q;
struct ip_hdr iphdr;
/ we can use the echo header here /
struct icmp_echo_hdr *icmphdr;

/ ICMP header + IP header + 8 bytes of data /
q = pbuf_alloc(PBUF_IP, sizeof(struct icmp_echo_hdr) + IP_HLEN + ICMP_DEST_UNREACH_DATASIZE,
PBUF_RAM);
if (q == NULL) {
LWIP_DEBUGF(ICMP_DEBUG, (“icmp_time_exceeded: failed to allocate pbuf for ICMP packet.
“));
return;
}
LWIP_ASSERT(“check that first pbuf can hold icmp message”,
(q->len >= (sizeof(struct icmp_echo_hdr) + IP_HLEN + ICMP_DEST_UNREACH_DATASIZE)));

iphdr = p->payload;
LWIP_DEBUGF(ICMP_DEBUG, (“icmp_time_exceeded from “));
ip_addr_debug_print(ICMP_DEBUG, &(iphdr->src));
LWIP_DEBUGF(ICMP_DEBUG, (“ to “));
ip_addr_debug_print(ICMP_DEBUG, &(iphdr->dest));
LWIP_DEBUGF(ICMP_DEBUG, (“
“));

icmphdr = q->payload;
icmphdr->type = type;
icmphdr->code = code;
icmphdr->id = 0;
icmphdr->seqno = 0;

/ copy fields from original packet /
SMEMCPY((u8_t )q->payload + sizeof(struct icmp_echo_hdr), (u8_t )p->payload,
IP_HLEN + ICMP_DEST_UNREACH_DATASIZE);

/ calculate checksum /
icmphdr->chksum = 0;
icmphdr->chksum = inet_chksum(icmphdr, q->len);
ICMP_STATS_INC(icmp.xmit);
/ increase number of messages attempted to send /
snmp_inc_icmpoutmsgs();
/ increase number of destination unreachable messages attempted to send /
snmp_inc_icmpouttimeexcds();
ip_output(q, NULL, &(iphdr->src), ICMP_TTL, 0, IP_PROTO_ICMP);
pbuf_free(q);
}
终于到了真正的实现的地方了,这里完成了ICMP的封装,然后继续向下就是ip_output(q, NULL, &(iphdr->src), ICMP_TTL, 0, IP_PROTO_ICMP);
err_t
ip_output(struct pbuf p, struct ip_addr src, struct ip_addr dest,
u8_t ttl, u8_t tos, u8_t proto)
{
struct netif netif;

if ((netif = ip_route(dest)) == NULL) {
LWIP_DEBUGF(IP_DEBUG, (“ip_output: No route to 0x%”X32_F”
“, dest->addr));
IP_STATS_INC(ip.rterr);
return ERR_RTE;
}

return ip_output_if(p, src, dest, ttl, tos, proto, netif);
}
检测啊检测,继续向下ip_output_if
err_t
ip_output_if(struct pbuf p, struct ip_addr src, struct ip_addr dest,
u8_t ttl, u8_t tos,
u8_t proto, struct netif netif)
{

if IP_OPTIONS_SEND
return ip_output_if_opt(p, src, dest, ttl, tos, proto, netif, NULL, 0);
}

哎呦,封装啊封装

err_t ip_output_if_opt(struct pbuf p, struct ip_addr src, struct ip_addr dest,
u8_t ttl, u8_t tos, u8_t proto, struct netif netif, void *ip_options,
u16_t optlen)
{

endif / IP_OPTIONS_SEND /
struct ip_hdr *iphdr;
static u16_t ip_id = 0;

snmp_inc_ipoutrequests();

/ Should the IP header be generated or is it already included in p? /
if (dest != IP_HDRINCL) {
u16_t ip_hlen = IP_HLEN;

if IP_OPTIONS_SEND
u16_t optlen_aligned = 0;
if (optlen != 0) {
/* round up to a multiple of 4 /
optlen_aligned = ((optlen + 3) & ~3);
ip_hlen += optlen_aligned;
/
First write in the IP options /
if (pbuf_header(p, optlen_aligned)) {
LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip_output_if_opt: not enough room for IP options in pbuf
“));
IP_STATS_INC(ip.err);
snmp_inc_ipoutdiscards();
return ERR_BUF;
}
MEMCPY(p->payload, ip_options, optlen);
if (optlen < optlen_aligned) {
/ zero the remaining bytes /
memset(((char
)p->payload) + optlen, 0, optlen_aligned - optlen);
}
}

endif / IP_OPTIONS_SEND /
/* generate IP header */
if (pbuf_header(p, IP_HLEN)) {
LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip_output: not enough room for IP header in pbuf
“));

IP_STATS_INC(ip.err);
snmp_inc_ipoutdiscards();
return ERR_BUF;
}
iphdr = p->payload;
LWIP_ASSERT("check that first pbuf can hold struct ip_hdr",
(p->len >= sizeof(struct ip_hdr)));
IPH_TTL_SET(iphdr, ttl);
IPH_PROTO_SET(iphdr, proto);
ip_addr_set(&(iphdr->dest), dest);
IPH_VHLTOS_SET(iphdr, 4, ip_hlen / 4, tos);
IPH_LEN_SET(iphdr, htons(p->tot_len));
IPH_OFFSET_SET(iphdr, 0);
IPH_ID_SET(iphdr, htons(ip_id));
++ip_id;
if (ip_addr_isany(src)) {
ip_addr_set(&(iphdr->src), &(netif->ip_addr));
} else {
ip_addr_set(&(iphdr->src), src);
}
IPH_CHKSUM_SET(iphdr, 0);
if CHECKSUM_GEN_IP
IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, ip_hlen));
endif
} else {
/ IP header already included in p /
iphdr = p->payload;
dest = &(iphdr->dest);
}

IP_STATS_INC(ip.xmit);

LWIP_DEBUGF(IP_DEBUG, (“ip_output_if: %c%c%”U16_F”
“, netif->name[0], netif->name[1], netif->num));
ip_debug_print(p);

if ENABLE_LOOPBACK
if (ip_addr_cmp(dest, &netif->ip_addr)) {
/ Packet to self, enqueue it for loopback /
LWIP_DEBUGF(IP_DEBUG, (“netif_loop_output()”));
return netif_loop_output(netif, p, dest);
}

endif / ENABLE_LOOPBACK /
if IP_FRAG
/ don’t fragment if interface has mtu set to 0 [loopif] /
if (netif->mtu && (p->tot_len > netif->mtu)) {
return ip_frag(p,netif,dest);
}

endif
LWIP_DEBUGF(IP_DEBUG, (“netif->output()”));
return netif->output(netif, p, dest);
}

前面都是相面添加数据封装起来,只有这个最后一句是关键return netif->output(netif, p, dest);
他是谁?啊哈,他是网络接口类里面的一个子项,并且他是一个指针函数,看看他的真实面目吧如下
/ set output /
netif->output = ethernetif_output;
netif->linkoutput = ethernetif_linkoutput;
这就是他的真实面目,他指向了ethernetif_output,接续
err_t ethernetif_output(struct netif netif, struct pbuf p, struct ip_addr ipaddr)
{
return etharp_output(netif, p, ipaddr);
}
我操又调用了一层,一个ICMP比特么的放个屁还费劲啊。继续
err_t
etharp_output(struct netif netif, struct pbuf q, struct ip_addr ipaddr)
{
struct eth_addr *dest, mcastaddr;

/ make room for Ethernet header - should not fail /
if (pbuf_header(q, sizeof(struct eth_hdr)) != 0) {
/ bail out /
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
(“etharp_output: could not allocate room for header.
“));
LINK_STATS_INC(link.lenerr);
return ERR_BUF;
}

/ assume unresolved Ethernet address /
dest = NULL;
/* Determine on destination hardware address. Broadcasts and multicasts

are special, other IP addresses are looked up in the ARP table. */

/ broadcast destination IP address? /
if (ip_addr_isbroadcast(ipaddr, netif)) {
/ broadcast on Ethernet also /
dest = (struct eth_addr )ðbroadcast;
/ multicast destination IP address? /
} else if (ip_addr_ismulticast(ipaddr)) {
/ Hash IP multicast address to MAC address./
mcastaddr.addr[0] = 0x01;
mcastaddr.addr[1] = 0x00;
mcastaddr.addr[2] = 0x5e;
mcastaddr.addr[3] = ip4_addr2(ipaddr) & 0x7f;
mcastaddr.addr[4] = ip4_addr3(ipaddr);
mcastaddr.addr[5] = ip4_addr4(ipaddr);
/ destination Ethernet address is multicast /
dest = &mcastaddr;
/ unicast destination IP address? /
} else {
/ outside local network? /
if (!ip_addr_netcmp(ipaddr, &(netif->ip_addr), &(netif->netmask))) {
/ interface has default gateway? /
if (netif->gw.addr != 0) {
/ send to hardware address of default gateway IP address /
ipaddr = &(netif->gw);
/ no default gateway available /
} else {
/ no route to destination error (default gateway missing) /
return ERR_RTE;
}
}
/ queue on destination Ethernet address belonging to ipaddr */
return etharp_query(netif, ipaddr, q);
}

/ continuation for multicast/broadcast destinations /
/ obtain source Ethernet address of the given interface /
/ send packet directly on the link /
return etharp_send_ip(netif, q, (struct eth_addr*)(netif->hwaddr), dest);
}

啊呀我操,还是封装啊,注意这里已经到了以太网层了,加入了MAC地址继续。
static err_t
etharp_send_ip(struct netif netif, struct pbuf p, struct eth_addr src, struct eth_addr dst)
{
struct eth_hdr *ethhdr = p->payload;
u8_t k;

LWIP_ASSERT(“netif->hwaddr_len must be the same as ETHARP_HWADDR_LEN for etharp!”,
(netif->hwaddr_len == ETHARP_HWADDR_LEN));
k = ETHARP_HWADDR_LEN;
while(k > 0) {
k—;
ethhdr->dest.addr[k] = dst->addr[k];
ethhdr->src.addr[k] = src->addr[k];
}
ethhdr->type = htons(ETHTYPE_IP);
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, (“etharp_send_ip: sending packet %p
“, (void )p));
/ send the packet */
return netif->linkoutput(netif, p);
}

又他妈的是一个继续看netif->linkoutput(,很显然他暴漏了看这里

/ set output /
netif->output = ethernetif_output;
netif->linkoutput = ethernetif_linkoutput;

哈哈我们继续找到他ethernetif_linkoutput,他也是个指针函数,指向这小子
err_t ethernetif_linkoutput(struct netif *netif, struct pbuf p)
{
struct eth_tx_msg msg;
struct eth_device
enetif;

enetif = (struct eth_device*)netif->state;
/* send a message to eth tx thread /
msg.netif = netif;
msg.buf = p;
if (rt_mb_send(ð_tx_thread_mb, (rt_uint32_t) &msg) == RT_EOK)
{
/
waiting for ack */
rt_sem_take(&(enetif->tx_ack), RT_WAITING_FOREVER);
}
return ERR_OK;
}

到这里我们可以出口气了,终于不封装了,哎呀我操!上面干了啥呢,很明显是向以太网的TX线程发了邮箱,但是同志们为啥在下面有个rt_sem_take(&(enetif->tx_ack), RT_WAITING_FOREVER);这个吊玩意为啥会出现在这里?哈哈,这个是表示TCPIP线程要主动挂起哦,要不然以太网怎么会得到时间片呢?,他用信号量来释放了CPU时间,好的

继续,现在轮到TC线程工作了哼哼

if (rt_mb_recv(ð_tx_thread_mb, (rt_uint32_t)&msg, RT_WAITING_FOREVER) == RT_EOK)
{
struct eth_device enetif;

RT_ASSERT(msg->netif != RT_NULL);
    RT_ASSERT(msg->buf   != RT_NULL);
    enetif = (struct eth_device*)msg->netif->state;
    if (enetif != RT_NULL)
    {
        /* call driver's interface */
        if (enetif->eth_tx(&(enetif->parent), msg->buf) != RT_EOK)
        {
            rt_kprintf("transmit eth packet failed

“);
}
}

/* send ack */
    rt_sem_release(&(enetif->tx_ack));    //发送完了释放信号量,让在等待发送完毕的TCPip线程得到时间片运行。
}

这个enenetif_tx(又是一个指针enc28_eth_tx(又是一个指针)
enc28_j60_dev_entry.parent.eth_tx;
enc2860_dev_entry.parent.eth_tx = enc280_j60_txj60_dev_entry.parent.eth_tx

好了这就OK了,最最后看rt_sem_release(&(enetif->tx_ack));他释放了,因为他已经完成了他的使命,可以给TCPIP线程时间了,于是乎他释放了信号量,
注意这个信号量在这里创建。
struct netif* netif;
netif = (struct netif*) rt_malloc (sizeof(struct netif));
if (netif == RT_NULL)
{
rt_kprintf("malloc netif failed
“);
返回-RT_ERROR;
}
rt_memset(netif, 0, sizeof(struct netif));

/* set netif /
dev->netif = netif;
/
register to rt-thread device manager /
rt_device_register(&(dev->parent), name, RT_DEVICE_FLAG_RDWR);
dev->parent.type = RT_Device_Class_NetIf;
rt_sem_init(&(dev->tx_ack), name, 0, RT_IPC_FLAG_FIFO);//创建这个信号量.....
/
set name */
netif->name[0] = name[0];
netif->name[1] = name[1];

好了至于底层我就不说了,网卡芯片多得是....
ICMP报文就死这样传递的。麻雀虽小五脏俱全,这里面我看到最重要的是PBUF和NETIF这个俩结构,所有的操作都是围绕他们进行的,

PBUF我当成malloc,NETIF就是和RTT的驱动挂在一起,只不过RTT又疯了一层。我是这么认为的!!!

原作者:小ARM菜菜

更多回帖

发帖
×
20
完善资料,
赚取积分