C 程序通过另一个接口接收和发送相同的数据包

ano*_*_16 5 c sockets networking packet-capture

我有一个带有两个物理接口的 linux 系统。我需要通过一个接口拦截(读取)传入的数据包,读取(或处理)数据并按原样通过另一个接口将其发送出去 - 就像中间人一样。我能够从数据包中提取所有标头字段和有效载荷数据,但我无法再次将其放回线路上。如何在通过另一个接口的途中发送数据包?

// All #includes 

struct sockaddr_in source,dest;
int i,j,k;
int main()
{
    int saddr_size , data_size;
    struct sockaddr_in saddr;
    unsigned char *buffer=malloc(65535);


    int sock_raw = socket( AF_PACKET , SOCK_RAW , htons(ETH_P_ALL)) ;

     if(sock_raw < 0)
        perror("setsockopt");

    setsockopt(sock_raw , SOL_SOCKET , SO_BINDTODEVICE , "eth0" , strlen("eth0")+ 1 );

    if(sock_raw < 0)
    {
        perror("Socket Error");
        return 1;
    }

    while(1)
    {
        saddr_size = sizeof (struct sockaddr);
        //Receive a packet
        data_size = recvfrom(sock_raw , buffer , 65536 , 0 ,(struct sockaddr *) &saddr , (socklen_t*)&saddr_size);

        if(data_size <0 )
        {
            printf("Recvfrom error , failed to get packets\n");
            return 1;
        }
        else{
        printf("Received %d bytes\n",data_size);

        //Huge code to process the packet

        //Send it out through "eth1" here 

        }
    }
    close(sock_raw);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

假设只有 UDP 或 ICMP 数据包更容易解释(可能使用简单的“sendto”功能) - 我可以处理排序。不要担心预期的目的地,我只想将数据包放回线路上 - 交付并不重要。

编辑1

如果我这样做,它会给我一个运行时错误,提示“无效参数”。我是否发送缓冲区甚至“Hello World”都没有关系。

bytes_sent=sendto(sock_raw, buffer, 65536, 0,(struct sockaddr *) &saddr ,saddr_size);
if (bytes_sent < 0) {
    perror("sendto");
    exit(1);
    }
Run Code Online (Sandbox Code Playgroud)

编辑 2:让我简单点 - 我有两个管道 A 和 B。球从 A 滚入,我收到了它们。我只想把它们放在管道 B 中,然后将它们送去。以太网桥接器以类似的方式工作——只是通过所有涉及的接口发送所有数据包。如果我不必从数据包头中获取一些基本信息,我肯定会使用网桥。而且我不擅长修改内核桥驱动程序。

编辑 3:我会最后一次尝试一个不同的问题。如果我收到了一个完整的原始数据包,其中包含在标头中的源/目标地址以及所有内容,我如何使用 sendto 简单地将它发送到任何地方(我不在乎在哪里)?我应该在 sendto 调用中向“struct sockaddr”添加任何信息,还是可以简单地使用我在 recvfrom 调用中所做的相同的信息?

ano*_*_16 7

我终于让它工作了!我能够将 eth0 上收到的数据包发送到 eth1 不变。

更正:

  1. "write" 而不是 "sendto" ,尽管它是不可取的。
  2. 仅用于发送的新套接字。
  3. 似乎使用setsockopt的SO_BINDTODEVICE不能正常工作,所以我不得不使用绑定函数将套接字绑定到接口。
  4. struct sockaddr_instruct sockaddr_ll

我匆忙把它放在一起,所以那里可能会有一些多余的线。我不知道daddr是否是必需的,但它确实有效。这是完整的代码:

// All #includes 

int main()
{
    int saddr_size , data_size, daddr_size, bytes_sent;
    struct sockaddr_ll saddr, daddr;
    unsigned char *buffer=malloc(65535);

    int sock_raw = socket( AF_PACKET , SOCK_RAW , htons(ETH_P_ALL)) ; //For receiving
    int sock = socket( PF_PACKET , SOCK_RAW , IPPROTO_RAW) ;            //For sending

    memset(&saddr, 0, sizeof(struct sockaddr_ll));
    saddr.sll_family = AF_PACKET;
    saddr.sll_protocol = htons(ETH_P_ALL);
    saddr.sll_ifindex = if_nametoindex("eth0");
    if (bind(sock_raw, (struct sockaddr*) &saddr, sizeof(saddr)) < 0) {
        perror("bind failed\n");
        close(sock_raw);
    }

    memset(&daddr, 0, sizeof(struct sockaddr_ll));
    daddr.sll_family = AF_PACKET;
    daddr.sll_protocol = htons(ETH_P_ALL);
    daddr.sll_ifindex = if_nametoindex("eth1");
    if (bind(sock, (struct sockaddr*) &daddr, sizeof(daddr)) < 0) {
      perror("bind failed\n");
      close(sock);
    }
    struct ifreq ifr;
    memset(&ifr, 0, sizeof(ifr));
    snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "eth1");
    if (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof(ifr)) < 0) {
        perror("bind to eth1");
        }

    while(1)
    {
        saddr_size = sizeof (struct sockaddr);
        daddr_size = sizeof (struct sockaddr);
        //Receive a packet
        data_size = recvfrom(sock_raw , buffer , 65536 , 0 ,(struct sockaddr *) &saddr , (socklen_t*)&saddr_size);

        if(data_size <0 )
        {
            printf("Recvfrom error , failed to get packets\n");
            return 1;
        }
        else{
        printf("Received %d bytes\n",data_size);

        //Huge code to process the packet (optional)

        //Send the same packet out
        bytes_sent=write(sock,buffer,data_size);
        printf("Sent %d bytes\n",bytes_sent);
         if (bytes_sent < 0) {
            perror("sendto");
            exit(1);
         }

        }
    }
    close(sock_raw);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

感谢所有回复的人。