【问题标题】:C Program to receive and send the same packets out over another interfaceC程序通过另一个接口接收和发送相同的数据包
【发布时间】:2015-01-10 13:44:07
【问题描述】:

我有一个带有两个物理接口的 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;
}

假设只有 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);
    }

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

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

【问题讨论】:

  • I only want to put the packets back on the wire - delivery is not important...... 这是什么意思,顺便说一句?
  • 我只是想将数据包发送出去,我不在乎它们是否到达目的地。使用wireshark我可以检查它们是否已经发送出去。
  • 如果您能够通过原始接口处理接收到的数据,请将数据放入单独的缓冲区,然后将缓冲区sendto() 发送到另一个目标地址。 [假设两个 i/fs 在 diff n/w..].
  • 为什么就像一个中间人发送需要在raw套接字上?需要相同的目的地地址[从您的问题中不清楚]?
  • 即使我使用从 IP 标头中提取的目标地址打开一个新套接字,我仍然会收到“无效参数”错误。它不必是原始套接字 - 您可以建议任何看起来正确的更改。

标签: c sockets networking packet-capture


【解决方案1】:

关于编辑2:如果设置桥接设备是一种选择,为什么您不能在桥接设备上使用嗅探工具(tcpdump、wireshark、您自己创建的基于libpcap)而不必关心转发自己包?

【讨论】:

  • 因为稍后我想有选择地仅转发我在程序中选择的那些数据包。网桥会不加选择地转发所有数据包。现在我只想抓住然后把数据包扔出去。
【解决方案2】:

我终于让它工作了!我能够将在 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;
}

感谢所有回复的人。

【讨论】:

  • 能否添加 //Huge 代码来处理数据包(可选)我正在尝试做同样的事情但不知道如何分析和编辑数据包。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-06-25
  • 2011-09-13
  • 1970-01-01
  • 2012-10-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多