数据包套接字未接收自定义协议ID的数据

时间:2015-11-02 18:50:44

标签: c linux sockets

我正在尝试使用我自己的自定义协议ID在同一台计算机上发送和接收SOCK_RAW类型的数据包PF_SOCKET。这是我的发件人和收件人示例代码 -

sender.c

#include<sys/socket.h>
#include<linux/if_packet.h>
#include<linux/if_ether.h>
#include<linux/if_arp.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>

#define CUSTOM_PROTO 0xB588

int main ()
{
    int sockfd = -1;
    struct sockaddr_ll dest_addr = {0}, src_addr={0};
    char *buffer = NULL;
    struct ethhdr *eh;

    sockfd = socket(PF_PACKET, SOCK_RAW, htons(CUSTOM_PROTO) );

    if ( sockfd == -1 )
    {
        perror("socket");
        return -1;
    }
    buffer = malloc(1518);
    eh = (struct ethhdr *)buffer;

    dest_addr.sll_ifindex  = if_nametoindex("eth0");
    dest_addr.sll_addr[0]  = 0x0;
    dest_addr.sll_addr[1]  = 0xc;
    dest_addr.sll_addr[2]  = 0x29;
    dest_addr.sll_addr[3]  = 0x49;
    dest_addr.sll_addr[4]  = 0x3f;
    dest_addr.sll_addr[5]  = 0x5b;
    dest_addr.sll_addr[6]  = 0x0;
    dest_addr.sll_addr[7]  = 0x0;

    //other host MAC address
    unsigned char dest_mac[6] = {0x0, 0xc, 0x29, 0x49, 0x3f, 0x5b};

    /*set the frame header*/
    memcpy((void*)buffer, (void*)dest_mac, ETH_ALEN);
    memcpy((void*)(buffer+ETH_ALEN), (void*)dest_mac, ETH_ALEN);

    eh->h_proto = htons(PAVAN_PROTO);

    memcpy((void*)(buffer+ETH_ALEN+ETH_ALEN + 2), "Pavan", 6 );

    int send = sendto(sockfd, buffer, 1514, 0, (struct sockaddr*)&dest_addr,
                      sizeof(dest_addr) );
    if ( send == -1 )
    {
        perror("sendto");
        return -1;
    }
    return 0;
}

receiver.c

#include<sys/socket.h>
#include<linux/if_packet.h>
#include<linux/if_ether.h>
#include<linux/if_arp.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>

#define CUSTOM_PROTO 0xB588

int main ()
{
    int sockfd = -1;
    struct sockaddr_ll dest_addr = {0}, src_addr={0};
    char *recvbuf = malloc(1514);

    sockfd = socket(PF_PACKET, SOCK_RAW, htons(CUSTOM_PROTO) );

    if ( sockfd == -1 )
    {
        perror("socket");
        return -1;
    }
    int len = recvfrom(sockfd, recvbuf, 1514, 0, NULL, NULL);
    printf("I received: \n");

    return 0;
}

发件人和收件人都在Ubuntu Virtualbox上运行。问题是接收器在recvfrom中挂起。但在receiver.c中,如果我将htons(CUSTOM_PROTO)更改为htons(ETH_P_ALL),则接收器工作正常。

为什么内核没有将带有自定义协议ID的数据包传送到我的自定义协议ID套接字?

我在GDB中验证了当我收到htons(ETH_P_ALL)

的数据包时,以太网网头是否正确形成

更新:而不是接口eth0及其对应的MAC,如果我选择本地环回lo且MAC地址为00:00:00:00:00:00,{{1}工作得很好!

如果发件人和收件人位于不同的计算机上,

更新2 CUSTOM_PROTO可以正常工作。此发现和prev更新使我怀疑在CUSTOM_PROTO发送的数据包未被同一台机器接收。但是eth0在同一台机器上工作的事实,驳斥了我的怀疑。

2 个答案:

答案 0 :(得分:7)

ETH_P_ALL与任何其他协议

协议ETH_P_ALL具有捕获传出数据包的特殊作用。

任何协议不等于ETH_P_ALL的接收器套接字都会收到来自设备驱动程序的协议数据包。

协议ETH_P_ALL的套接字在将传出数据包发送到设备驱动程序之前接收所有数据包,并从设备驱动程序接收所有传入数据包。

环回设备与以太网设备

发送到环回设备的数据包从该设备发出,然后从设备接收相同的数据包作为传入。 因此,当CUSTOM_PROTO与loopback一起使用时,套接字会使用自定义协议捕获数据包作为传入。

请注意,如果ETH_P_ALL与环回设备一起使用,则每个数据包都会被接收两次。一旦它被捕获为传出,第二次被传入。

eth0的情况下,数据包从设备传输。因此,这些数据包会转到设备驱动程序,然后可以在物理以太网端口的另一端看到它们。例如,使用VirtualBox“仅主机”网络适配器,主机系统中的某些嗅探器可以捕获这些数据包。

但是,传输到物理端口(或其仿真)的数据包不会重定向回该端口。因此,它们不会从设备接收到。这就是为什么这些数据包只能在传出方向上被ETH_P_ALL捕获,而CUSTOM_PROTO在传入方向上无法看到它们。

从技术上讲,应该可以准备特殊设置来进行外部数据包环回(来自设备端口的数据包应该被发送回该端口)。在这种情况下,行为应该类似于环回设备。

内核实现

查看内核文件net/core/dev.c。有两个不同的列表:

struct list_head ptype_base[PTYPE_HASH_SIZE] __read_mostly;
struct list_head ptype_all __read_mostly;   /* Taps */

列表ptype_all适用于协议ETH_P_ALL的套接字处理程序。列表ptype_base适用于具有正常协议的处理程序。

来自xmit_one()dev_hard_start_xmit()传出数据包的挂钩:

    if (!list_empty(&ptype_all))
        dev_queue_xmit_nit(skb, dev);

对于传出数据包,调用函数dev_queue_xmit_nit()ETH_P_ALL处理ptype_all的每个项目。最后,具有协议AF_SOCKET的{​​{1}}类型的套接字捕获该传出数据包。

因此,观察到的行为与任何自定义协议无关。使用ETH_P_ALL可以观察到相同的行为。在这种情况下,接收器能够捕获所有传入的IP数据包,但它无法捕获ETH_P_IPsender.c发送到"eth0"设备的MAC地址的IP数据包。

"eth0"也可以看到它。如果调用tcpdump并且选项仅捕获传入的数据包(不同版本的tcpdump使用不同的命令行参数来启用此类过滤),则不会捕获发送方发送的数据包。

可以使用tcpdump解决在同一台机器上按协议ID区分数据包所需的初始任务。接收方应捕获所有数据包并检查协议,例如:

ETH_P_ALL

有用的参考"kernel_flow",附图很好http://www.linuxfoundation.org/images/1/1c/Network_data_flow_through_kernel.png

它基于2.6.20内核,但在现代内核中while (1) { int len = recvfrom(sockfd, recvbuf, 1514, 0, NULL, NULL); if (ntohs(*(uint16_t*)(recvbuf + ETH_ALEN + ETH_ALEN)) == CUSTOM_PROTO) { printf("I received: \n"); break; } } 的处理方式相同。

答案 1 :(得分:0)

When packets with same source nad destination MAC address are transmitted from real network device ethX and physically looped back.

If protocol ETH_P_ALL is specified, packet is captured twice:

  • first packet with socket_address.sll_pkttype is PACKET_OUTGOING
  • and second packet with socket_address.sll_pkttype is PACKET_HOST

If specific protocol is specified CUSTOM_PROTO, packet is captured once:

  • in the case of normal packet: socket_address.sll_pkttypeis PACKET_HOST.
  • in the case of VLAN packet: socket_address.sll_pkttypeis PACKET_OTHERHOST.