您的位置:首页 > 其它

[LWIP学习]--SylixOS AF_PACKET socket套接字分析

2017-08-31 20:25 781 查看
之前我们学习了原始套接字(SOCK_RAW),通过原始套接字可以越过传输层,直接在IP层进行数据的发送和接收。

通过原始套接字,可以构建自定义的IP包。

其实,还有一种套接字比它更厉害,可以构建自定义以太网包–AF_PACKET套接字

在SylixOS中,

使用socket(AF_PACKET, SOCK_RAW, ETHTYPE_*)创建的套接字可以接收链路层报文。

那为什么AF_PACKET协议域的套接字可以接收链路层报文呢?

今日追踪了一下:

1.首先在socket函数中,对不同的协议域设置了不同入口。

本文进入的是packet_socket函数。

/* 选择协议域 */
switch (domain) {
case AF_UNIX:     /*  UNIX 域协议                 */
pafunix = unix_socket(domain, type, protocol);
...
case AF_PACKET:   /*  PACKET                      */
pafpacket = packet_socket(domain, type, protocol);
...
case AF_INET:
case AF_INET6:    /*  IPv4 / v6                   */
iLwipFd = lwip_socket(domain, type, protocol);
...
default:
}


在packet_socket->__packetCreate函数中,创建AF_PACKET控制块,并加入AF_PACKET链表。

static AF_PACKET_T  *__packetCreate (INT  iType, INT  iProtocol)
{
AF_PACKET_T *pafpacket;

pafpacket = (AF_PACKET_T *)__SHEAP_ALLOC(sizeof(AF_PACKET_T));
...
pafpacket->PACKET_iType         = iType;
pafpacket->PACKET_iProtocol     = iProtocol;
...
/* 将AF_PACKET控制块加入全局链表 */
_List_Line_Add_Ahead(&pafpacket->PACKET_lineManage, &_G_plineAfPacket);
...
return  (pafpacket);
}


2.通过recvfrom函数接收数据。

选择AF_PACKET协议域,进入packet_recvfrom函数。

ssize_t  recvfrom (int s, void *mem, size_t len, int flags,
struct sockaddr *from, socklen_t *fromlen)
{
...
switch (psock->SOCK_iFamily) {
case AF_UNIX:   /*  UNIX 域协议                 */
sstRet = (ssize_t)unix_recvfrom(psock->SOCK_pafunix, mem, len, flags, from, fromlen);
break;
case AF_PACKET: /*  PACKET                      */
sstRet = (ssize_t)packet_recvfrom(psock->SOCK_pafpacket, mem, len, flags, from, fromlen);
break;
default:
sstRet = (ssize_t)lwip_recvfrom(psock->SOCK_iLwipFd, mem, len, flags, from, fromlen);
break;
}
...
}


在packet_recvfrom 函数中判断AF_PACKET控制块是否获取到数据,并读取。

ssize_t  packet_recvfrom (AF_PACKET_T *pafpacket, void *mem, size_t len, int flags,
struct sockaddr *from, socklen_t *fromlen)
{
...
/* 当前节点是否有数据 */
if (__packetCanRead(pafpacket, flags, len)) {
/* 接收一个数据包 */
sstTotal = __packetBufRecv(pafpacket, mem, len,
(struct sockaddr_ll *)from, flags);
}
...
}


3.获取到数据的源头在tcpip.c文件中,tcpip_input函数是LWIP协议栈的入口,在tcpip_input函数中添加了一个回调函数,用于AF_PACKET获取链路层报文。

tcpip_input(struct pbuf *p, struct netif *inp)
{
#if defined(SYLIXOS) && defined(LWIP_HOOK_LINK_INPUT)
/* SylixOS 添加的回调函数(AF_PACKET获取链路层报文) */
if (LWIP_HOOK_LINK_INPUT(p, inp)) {
pbuf_free(p);
return ERR_OK;
}
...
}


4.通过sendto函数发送数据。

和之前一样,选择AF_PACKET协议域,进入packet_sendto函数。

在packet_sendto->__packetEthRawSendto函数中,组装一个以太网报文,并通过netif->linkoutput函数将数据通过网卡驱动发送出去。

errno_t  __packetEthRawSendto (CPVOID                pvPacket,
size_t                stBytes,
struct sockaddr_ll   *psockaddrll)
{
...
/* 获取网络接口 */
pnetif = (struct netif *)netif_get_by_index((UINT)psockaddrll->sll_ifindex);
...
/* 分配带有 PAD 的以太网报头 pbuf */
pbuf_hdr = pbuf_alloc(PBUF_RAW, ETH_HLEN + ETH_PAD_SIZE, PBUF_RAM);
...
/* 拷贝数据至pbuf */
lib_memcpy(((u8_t *)pbuf_hdr->payload) + ETH_PAD_SIZE, pvPacket, ETH_HLEN);
...
/* 通过网卡驱动发送函数,将数据发送出去 */
err = pnetif->linkoutput(pnetif, pbuf_hdr);
...
}


至此,AF_PACKET套接字的创建即使用介绍完毕。

AF_PACKET套接字工作流程图如下:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  socket