【LWIP】AF_PACKET套接字分析

收录于:

【LWIP】LWIP协议|相关知识汇总|LWIP学习笔记


之前我们学习了原始套接字(SOCK_RAW),通过原始套接字可以越过传输层,直接在IP层进行数据的发送和接收。
通过原始套接字,可以构建自定义的IP包。

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


使用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套接字工作流程图如下:
这里写图片描述

  • 2
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Evan_ZGYF丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值