MUX层对数据的接收和处理

MUX层从END驱动中获得数据,并且根据报文的类型进行分发处理。

MUX层向END驱动注册处理回调函数

在网络协议栈初始化阶段,初始化模块(usrNetEndLibInit)遍历用户在驱动层定义的设备列表endDevTbl,并调用muxDevLoad加载用户定义的网络设备,指定接收函数以便接收驱动层传上来的数据。根据用户加载的END网络设备的类型,有不同的接收函数:

目前我们的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()函数在接收到报文后,会对报文分发给相应的函数进行处理。分发的根据是报文的类型。

  1. 通过packetDataGet接口获取报文的协议类型pktType;
  2. 匹配网络设备对应END_OBJ中的协议列表,找到第一个接收函数不为空且协议类型相匹配的协议;
  3. 把数据包交给该协议处理,例如:如果是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);

	/* ... */
}