netlink发送和接收

netlink发送和接收内核态1、初始化首先当然是调用netlink_kernel_create函数,具体参数参考前一篇文章。#defineNETLINK_ETRAINF17structsock*g_etraInfSock=NULL;g_etraInfSock=netlink_kernel_create(&init_net,NETLINK_ETRAINF,\ 0,EtraInf

内核态

1、初始化

首先当然是调用netlink_kernel_create函数,具体参数参考前一篇文章。

#define NETLINK_ETRAINF 17
struct sock * g_etraInfSock = NULL;
g_etraInfSock = netlink_kernel_create(&init_net, NETLINK_ETRAINF,\
			0, EtraInfRecv, NULL, THIS_MODULE);

其中

g_etraInfSock 

定义成全局变量,因为这个返回值还要作为其它函数的参数,稍后将会讲到。

2、接收

接收函数实现:

void EtraInfRecv(struct sk_buff *pSkb)
{
	struct nlmsghdr *pNlHdr = NULL;
	 int iLen = 0;//NLMSG_OK四字节对齐
	int iSecTimesFlag = 0;
	int iMarkFlag =  0;


	if (NULL == pSkb){
		STAT_INC(RECV_SKB_NULL);
		return;
	}

	pNlHdr = nlmsg_hdr(pSkb);             /*(struct nlmsghdr *)skb->data*/
	iLen  = pSkb->len;

	EF_INFO("[INFO:] recv sk_buff's length is %d\r\n",iLen);
	while (NULL != pNlHdr && NLMSG_OK(pNlHdr, iLen)){
		if (1 == iSecTimesFlag && 0 == iMarkFlag){
			//g_etraInfKernErrStat.iRecvOneBuffHaveMultiPack++;
			STAT_INC(RECV_MULTI_PACK);
			iSecTimesFlag = 0;
			iMarkFlag     = 1;
		}

		STAT_INC(RECV_PACK);//接收计数+1

		EF_HANDLE_RECV(EF_TYPE_TO_ENTRY(pNlHdr->nlmsg_type),pNlHdr);//根据type类型来选择对应的处理函数

		pNlHdr = NLMSG_NEXT(pNlHdr, iLen);
		iSecTimesFlag = 1;
	}

	return;
}

内核态的接收函数一般流程是这样的

net->genl_sock = netlink_kernel_create(net, NETLINK_GENERIC, 0,
					       genl_rcv, &genl_mutex,
					       THIS_MODULE);

static void genl_rcv(struct sk_buff *skb)
{
	genl_lock();
	netlink_rcv_skb(skb, &genl_rcv_msg);
	genl_unlock();
}

genl_rcv_msg函数用来解析接收来的数据

3、发送

int EtraInfSendMsg(MSG_NODE *pMsg)
{

	//int iUdcLen = sizeof(APP_MSG);
	char *p = (char *)pMsg->pUdcMsg;
	int iRet = ETERROR;
	int iUdcLen = sizeof(APP_MSG);


	

	STAT_INC(SEND_REQUEST_MSG_API);

	EF_INFO("[INFO:k] send msg len is %d\r\n",iUdcLen);

	iRet = EtraInfSend((char *)p, iUdcLen,ETRAINF_MSG);
	if (ETERROR != iRet){
		FreeMsgNode(pMsg);
		return ETOK;
	}else{
	FreeMsgNode(pMsg);
		return ETERROR;
		}

}
/*kernel*/
int EtraInfSend(char *pBuf, int iByteSize,unsigned short eType)
{
	struct sk_buff *pSkb = NULL;
	struct nlmsghdr *pNlMsgHdr = NULL;
	void *pData = NULL;
	int iPid=0;
	int iRet=0;

	if (0 == etraInitilized){
		STAT_INC(SEND_UNINIT_FAILED);        
        PrintfLog(LOG_ERROR, FID_ETRAINF_KERN,"etraInitilized is 0 \r\n");
		return ETERROR;
	}

	/*对数据合法性进行检测*/
	if (NULL == pBuf && 0 != iByteSize){        
		STAT_INC(SEND_PARA_CONFLICT);
        PrintfLog(LOG_ERROR, FID_ETRAINF_KERN,"pBuff == NULL or iByteSize is 0 \r\n");
		return ETERROR;
	}

	if (0 == iByteSize){
		STAT_INC(SEND_DATELEN_ZERO);
	}

	/*对发送数据长度检测,主要是由用户态的接收限制*/
	if (ETRAINF_MAX_PAYLOAD-sizeof(struct nlmsghdr) < iByteSize){
		EF_ERR("[ERROR:]the request data's length(%d) is over payload(%d)!",\
				iByteSize,ETRAINF_MAX_PAYLOAD-sizeof(struct nlmsghdr));
		STAT_INC(SEND_REQUEST_OVERLOAD);        
        PrintfLog(LOG_ERROR, FID_ETRAINF_KERN,"ETRAINF_MAX_PAYLOAD-sizeof(struct nlmsghdr) < iByteSize\r\n");
		return ETERROR;
	}

	/* nlmsg_new 会新申请一个socket buffer ,其大小为
			socket消息头大小+  netlink 消息头大小+ 用户消息大小*/
	pSkb = nlmsg_new(iByteSize,ETNO_WAIT);//GFP_KERNEL = 0
	if (NULL == pSkb){
		STAT_INC(SEND_GET_SKBUFF_FAILED);        
        PrintfLog(LOG_ERROR, FID_ETRAINF_KERN,"NULL == pSkb\r\n");
		return ETERROR;
	}
/*NLMSG_NEW该宏基本等同于nlmsg_put函数,填充netlink消息头的部分内容*/
	pNlMsgHdr = NLMSG_NEW(pSkb, 0, 0, eType, iByteSize, 0);
	pData = NLMSG_DATA(pNlMsgHdr);
	memcpy(pData, pBuf, iByteSize);/*填充用户区数据*/

	iPid = EF_CHANN_PID(eType);

/*该函数是内核向用户空间发送的核心函数*/
	/*需要特别注意的是: skb申请的空间会在这里面释放,
		 所以不能重复调用此接口发送同一个skb,会造成严重后果*/
	iRet = netlink_unicast(g_etraInfSock, pSkb, iPid, 1);
	if (0 > iRet){
		if (-111 == iRet){
			EF_WARN("[WARN:]netlink_unicast return %d cause app exception\r\n",iRet);
			STAT_INC(SEND_FAILED_APP_EXCEPTION);
            PrintfLog(LOG_ERROR, FID_ETRAINF_KERN,"-111 == iRet\r\n");
			goto nlmsg_failure;
		}
		if (-11 == iRet){
			EF_WARN("[WARN:]netlink_unicast return %d cause app so slow!\r\n",iRet);
			STAT_INC(SEND_FAILED_APP_SLOW);
            PrintfLog(LOG_ERROR, FID_ETRAINF_KERN,"-11 == iRet\r\n");
			goto nlmsg_failure;
		}
		EF_ERR("[ERROR:]netlink_unicast return %d\r\n",iRet);
		STAT_INC(SEND_FAILED);
        PrintfLog(LOG_ERROR, FID_ETRAINF_KERN,"0 > iRet, but not is -11 and -111\r\n");
		goto nlmsg_failure;

	}

	STAT_INC(SEND_SUCESS);

	switch (eType)
	{
	case ETRAINF_MSG:
		STAT_INC(SEND_MSG_TYPE);
		break;
	case ETRAINF_OAM:
		STAT_INC(SEND_OAM_TYPE);
		//FreeDspOamPoolNode((void *)pBuf);
		break;
	case ETRAINF_DATA:
		STAT_INC(SEND_DATA_TYPE);
		//FreeDspDataPoolNode((void *)pBuf);
		break;
	case ETRAINF_LOG:
		STAT_INC(SEND_LOG_TYPE);
		break;
			
		
	}


	return iRet;

nlmsg_failure:
	STAT_INC(SEND_NLMSG_FAILED);
    //add by zhouds 2014.9.15
    if (pSkb)
        kfree_skb(pSkb);
	return ETERROR;
}

用户态

/*
 * 该函数需要修改,每次传入的pid最好都做个bit位的检查,若相应pid注册过,那就得检查相应的接收处理函数是否一样,否则返回出错
 * 20120909
 */
EINT32 etraInfCreateSocket(int iPid)
{
	int iSockfd = 0;
	int iFlags  = 0;
	struct sockaddr_nl tSrcAddr;
	int iRet = 0;

	/*open socket*/
	iSockfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ETRAINF);
	if (0 > iSockfd){
		EF_ERR("[ERROR:] Open socket failed![error code is %d]\r\n",iSockfd);
		STAT_INC(INIT_OPEN_SOCKET_FAILED);
		return ETERROR;
	}


	/*If there are no connections pending on the socket’s queue, accept will block (so that the program won’t
      continue) until a client makes a connection. You may change this behavior by using the O_NONBLOCK flag
      on the socket file descriptor, using the fcntl function
	*/


	iFlags = fcntl(iSockfd, F_GETFL, 0);//取得文件描述符状态
	fcntl(iSockfd, F_SETFL, O_NONBLOCK | iFlags);//设置为非阻塞,为什么一定要设置成非阻塞


	/*initial the address*/
	tSrcAddr.nl_family = AF_NETLINK;
	tSrcAddr.nl_groups = 0;
	tSrcAddr.nl_pad    = 0;
	tSrcAddr.nl_pid    = iPid;//字段 nl_pid 实际上未必是进程 ID,它只是用于区分不同的接收者或发送者的一个标识,用户可以根据自己需要设置该字段


	/*bind socket*/
	iRet = bind(iSockfd, (struct sockaddr *)&tSrcAddr, sizeof(tSrcAddr));
	if (0 > iRet){
		EF_ERR("[ERROR:] bind socket failed![error code is %d]\r\n",iRet);
		//g_EtraInfErrStat.iBindSocketFailed++;
		STAT_INC(INIT_BIND_SOCKET_FAILED);
		close(iSockfd);//
		return ETERROR;
	}


	return iSockfd;
}

发送

/*
 * 依次从优先级队列中取数据
 */
void *EtraInfSendThread(void *pVoid)
{
	ETRAINF_NODE_t *ptDataNode = NULL;


	while (1)
	{

		//pthread_mutex_lock(&g_syncLock);
		ObtainSem(g_syncSem, ETWAIT_FOREVER);//控制线程
		while (1)
		{
			pthread_mutex_lock(&g_sendLock);//为什么要采取锁的形式?
			//ObtainSem(g_sendSem, ETWAIT_FOREVER);
			/*从队列中取得发送数据节点*/
			ptDataNode = EtraInfGetSendData();
			/*释放队列信号量*/
			pthread_mutex_unlock(&g_sendLock);
			//GiveSem(g_sendSem);
			if (NULL == ptDataNode){
				//ETRAINF_PRINT_ERROR_0(" break EtraInfSendFrame");
				break;
			}
			/*发送相应数据*/
			EtraInfSendFrame(ptDataNode);
			if (NULL != ptDataNode->pFreeExternMMFun){
				ptDataNode->pFreeExternMMFun(ptDataNode->pNeedFreeMM);
				EF_INFO("[info:]free free free free free free\r\n");
			}


			/*释放相应数据节点*/
			EtraInfPutNode(ptDataNode);
		}
	}
	return NULL;
}





int EtraInfSendFrame(ETRAINF_NODE_t *ptSend)
{
	int iSendLen = 0;
	struct sockaddr_nl tDstAddr;



	memset(&tDstAddr, 0x00, sizeof(tDstAddr));
	tDstAddr.nl_family = AF_NETLINK;
	tDstAddr.nl_groups = 0;
	tDstAddr.nl_pad    = 0;
	tDstAddr.nl_pid    = 0;//接收者是内核


	EF_INFO("[INFO:U]will send date bytesize is %d, pid is %d socket id is %d msgtype is %d\r\n",\
			ptSend->iByteSize,ptSend->iPid,ptSend->iSockfd,ptSend->eMsgType);

	/*print buff*/


	iSendLen = sendto(ptSend->iSockfd, ptSend->pBuf, ptSend->iByteSize, 0, (struct sockaddr *)&tDstAddr, sizeof(tDstAddr));
	if (ptSend->iByteSize != iSendLen){
		EF_ERR("[ERROR:U]request send date len is %d,but sendto return %d\r\n",ptSend->iByteSize,iSendLen);
		STAT_INC(SEND_SENDTO_FAILED);
	}


	STAT_INC(SEND_SUCESS);

	return iSendLen;
}

接收


void *EtraInfRecvThread(void *pVoid)
{
	fd_set socket;
	fd_set socketTmp;
	int iRet = 0;
	int iMaxFd = 0;
	int i = 0;

	FD_ZERO(&socket);


	for (i=0; i<EF_CHANN_NUM; ++i){
		FD_SET(ETRAINF_CHANN_SKFD(i), &socket);

		if (iMaxFd < ETRAINF_CHANN_SKFD(i))
			iMaxFd = ETRAINF_CHANN_SKFD(i);
	}
	iMaxFd += 1;

	while (1){
	
		socketTmp = socket;/*很贱的方式这样做的目的是重新赋值*/
		iRet = select(iMaxFd, &socketTmp, NULL, NULL, NULL);
		if (0 > iRet){
			continue;
		}		
	

		for (i=0; i<EF_CHANN_NUM; ++i){
			if (FD_ISSET(ETRAINF_CHANN_SKFD(i), &socketTmp)){
				 EF_HANDLE_RECV(i);

			}

		}


	}


	for (i=0; i<EF_CHANN_NUM; ++i)
		etraInfDestroySocket(ETRAINF_CHANN_SKFD(i));

	return NULL;
}

今天的文章netlink发送和接收分享到此就结束了,感谢您的阅读。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/12832.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注