MUX层对数据的接收和处理
MUX层从END驱动中获得数据,并且根据报文的类型进行分发处理。
MUX层向END驱动注册处理回调函数
在网络协议栈初始化阶段,初始化模块(usrNetEndLibInit)遍历用户在驱动层定义的设备列表endDevTbl,并调用muxDevLoad加载用户定义的网络设备,指定接收函数以便接收驱动层传上来的数据。根据用户加载的END网络设备的类型,有不同的接收函数:
- END_STYLE_END类型的接收函数为muxReceive;
- END_STYLE_NPT类型的接收函数为:muxTkReceive;
- END_STYLE_MULTI类型的接收函数为:muxTkPktsReceive;
目前我们的END类型为END_STYLE_END,END驱动层的报文都是通过接口muxReceive进入mux层。
实际上,END层在加载用户定义的网络设备时,会生成一个END_OBJ对象,用pEnd表示,END_OBJ有一成员receiveRtn,在加载时,就是通过pEnd->receiveRtn=muxReceive把MUX层的接收函数注册到网络设备对应的END_OBJ对象中。
/* 注册mux层的接收函数到end驱动中 */
void * muxDevLoad()
{
/***/
pNew->receiveRtn = muxReceive;
if (pNew->endStyle == END_STYLE_NPT)
pNew->receiveRtn = (FUNCPTR) muxTkReceive;
else if (pNew->endStyle == END_STYLE_MULTI)
pNew->receiveRtn = (FUNCPTR) muxTkPktsReceive;
/***/
}
END驱动层调用END_RCV_RTN_CALL把数据交给MUX层,而实际上END_RCV_RTN_CALL展开之后就是pEnd->receiveRtn。
#define END_RCV_RTN_CALL(pEnd,pMblk) \
{ \
if ((pEnd)->receiveRtn) \
{ \
(pEnd)->receiveRtn ((pEnd), pMblk,NULL,NULL,NULL,NULL); \
} \
else \
netMblkClChainFree (pMblk); \
}
mux层对报文的分发处理
muxReceive()函数在接收到报文后,会对报文分发给相应的函数进行处理。分发的根据是报文的类型。
- 通过packetDataGet接口获取报文的协议类型pktType;
- 匹配网络设备对应END_OBJ中的协议列表,找到第一个接收函数不为空且协议类型相匹配的协议;
- 把数据包交给该协议处理,例如:如果是ETHERNET_ARP类型的,则交给APR协议的接收函数arpReceiveRtn来处理;如果是ETHERNET_IP类型的,则交给IP协议进行处理。
下面是获得报文协议类型的代码。所谓报文的协议类型,也就是报文的以太网头中表明的报文类型,例如arp报文的类型为0x0806,ip报文的类型为0x0800。
STATUS muxReceive()
{
/* ... */
if (pEnd->pFuncTable->packetDataGet == NULL ||
pEnd->pFuncTable->packetDataGet (pMblk, &llHdrInfo) != OK)
{
goto err_data_get;
}
type = llHdrInfo.pktType;
/* ... */
}
下面为根据报文类型进行分发处理的代码:
STATUS muxReceive()
{
/* ... */
for (pProto = pEnd->pSnarf; pProto < pEnd->pStop; pProto++)
{
if (pProto->rr.endRcv == NULL)
continue;
if (type == pProto->type ||
pProto->type == MUX_PROTO_SNARF ||
pProto->type == MUX_PROTO_PROMISC)
{
/* 报文被分发处理 */
/* 例如IP报文就是在这里被回调函数ipReceiveRtn()放入IP协议栈中处理 */
if (pProto->rr.endRcv (pProto->pBinding, type, pMblk,
&llHdrInfo, pProto->recvRtnArg))
goto out;
}
}
/* ... */
}
IP层对数据的接收和处理
如上所述,IP报文被回调函数ipReceiveRtn()从mux层转入IP协议栈中处理。
IP Stack的处理流程包括快速转发(fast_forward)处理流程、ip_input处理流程、ip_forward处理流程、ip_output处理流程以及本地协议栈(local_stack)的处理流程。
其中本地协议栈(local_stack)处理包括对发给本地的UDP、TCP、ICMP、IGMP、RIP等报文的处理,而UDP/TCP报文最终都是通过Socket把数据交给应用层,应此在后面都有涉及到。本地协议栈(local_stack)的对报文的分发使用函数指针pr_input,根据IP数据包中的不同的负载类型,对应不同的处理函数。例如,对于UDP报文处理函数为udp_input;对于TCP报文则处理函数为tcp_input; 对于RIP报文处理函数为rip_input等等。 rip_input()函数中的rip是raw ip(原始IP)的意思,并不是路由协议RIP。RIP报文属于udp报文,其处理函数为udp_input()函数。
void ip_input(struct mbuf *m)
{
/* ... */
/*
* Switch out to protocol's input routine.
*/
WV_NET_ADDRIN_EVENT_4_4 (NETD_IP4_DATAPATH_EVENT, WV_NETD_VERBOSE, 1,
11, ip->ip_src.s_addr, ip->ip_dst.s_addr,
WV_NETDEVENT_FINISH, WV_NETD_RECV,
ip_input, 0, htonl(ip->ip_src.s_addr),
htonl(ip->ip_dst.s_addr))
_ipstat.ips_delivered++;
/* 报文根据tcp,udp,rip等类型不同调用相应的函数进行处理 */
(*inetsw[ip_protox[ip->ip_p]].pr_input)(m, hlen, ip->ip_p);
return;
}
下图是摘自研究报告的一张报文在ip协议栈中的处理流程图。其中,红色为处理函数。
image/vxworks_tcp_ip/IP协议栈报文处理流程图.png
应用程序如何获得报文
下面以rip协议为例,说明应用程序如何获得报文。RIP报文为udp报文,下面为udp报文处理函数的片段。
void udp_input()
{
/* ... */
append_sa = (struct sockaddr *)&priv_udp_in;
/* sbappendaddr()函数将接收到的udp报文信息等放置到套接字接收队列中 */
if (sbappendaddr(&inp->inp_socket->so_rcv, append_sa, m, opts) == 0) {
WV_NET_PORTIN_EVENT_2 (NETD_IP4_DATAPATH_EVENT, WV_NETD_WARNING,
1, 9, uh->uh_sport, uh->uh_dport,
WV_NETDEVENT_WARNING, WV_NETD_RECV,
udp_input, WV_NETD_FULLSOCK)
udpStat.udps_fullsock++;
goto bad;
}
/* 唤醒套接字接收队列中的所有睡眠进程 */
sorwakeup(inp->inp_socket);
/* ... */
}