【问题标题】:Trouble with implementing a C program that sends and receives raw ethernet frames实现发送和接收原始以太网帧的 C 程序时遇到问题
【发布时间】:2013-04-15 06:55:58
【问题描述】:

首先,我对以太网和数据链路层的主题相当陌生,我正在尝试编写一个允许发送和接收以太网帧的 C 程序。

我正在尝试让我的程序将以太网帧发送给我自己,因此该程序将具有相同的源 MAC 地址和目标 MAC 地址。

我正在使用的程序是来自http://hacked10bits.blogspot.com/2011/12/sending-raw-ethernet-frames-in-6-easy.html的代码的修改版本

我一直在使用 recvfrom() 函数时遇到问题,它似乎只是阻塞并且程序没有终止。从代码中,我尝试发送以太网帧并让 recvfrom() 函数能够检索帧并将其存储在缓冲区中。但是因为 recvfrom() 函数只是阻塞,所以在我看来 sendto() 函数无法成功发送帧。

有什么办法可以解决这个问题吗?

这是我的代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>    /* Must precede if*.h */
#include <linux/if.h>
#include <linux/if_ether.h>
#include <linux/if_packet.h>
#include <sys/ioctl.h>

union ethframe
{
  struct
  {
    struct ethhdr    header;
    unsigned char    data[ETH_DATA_LEN];
  } field;
  unsigned char    buffer[ETH_FRAME_LEN];
};

int main(int argc, char **argv) {
  char *iface = "eth0";
  unsigned char dest[ETH_ALEN]
           = { 0x00, 0x12, 0x34, 0x56, 0x78, 0x90 };
  unsigned short proto = 0x1234;
  unsigned char *data = "hello world";
  unsigned short data_len = strlen(data);

  int s;
  if ((s = socket(AF_PACKET, SOCK_RAW, htons(proto))) < 0) {
    printf("Error: could not open socket\n");
    return -1;
  }

  struct ifreq buffer;
  int ifindex;
  memset(&buffer, 0x00, sizeof(buffer));
  strncpy(buffer.ifr_name, iface, IFNAMSIZ);
  if (ioctl(s, SIOCGIFINDEX, &buffer) < 0) {
    printf("Error: could not get interface index\n");
    close(s);
    return -1;
  }
  ifindex = buffer.ifr_ifindex;

  unsigned char source[ETH_ALEN];
  if (ioctl(s, SIOCGIFHWADDR, &buffer) < 0) {
    printf("Error: could not get interface address\n");
    close(s);
    return -1;
  }

  memcpy((void*)source, (void*)(buffer.ifr_hwaddr.sa_data),
         ETH_ALEN);

  //edited part... here we have it so that the destination mac address is
  //the same as the source mac address
  memcpy((void*)dest, (void*)(buffer.ifr_hwaddr.sa_data),
       ETH_ALEN);

  //probe source mac address
  int k;
  for(k = 0; k !=ETH_ALEN; k++)
  {
    printf("%x\n",source[k]);
    printf("%x\n",dest[k]);
  }

  union ethframe frame;
  memcpy(frame.field.header.h_dest, dest, ETH_ALEN);
  memcpy(frame.field.header.h_source, source, ETH_ALEN);
  frame.field.header.h_proto = htons(proto);
  memcpy(frame.field.data, data, data_len);

  unsigned int frame_len = data_len + ETH_HLEN;

  struct sockaddr_ll saddrll;
  memset((void*)&saddrll, 0, sizeof(saddrll));
  saddrll.sll_family = PF_PACKET;   
  saddrll.sll_ifindex = ifindex;
  saddrll.sll_halen = ETH_ALEN;
  memcpy((void*)(saddrll.sll_addr), (void*)dest, ETH_ALEN);

  if (sendto(s, frame.buffer, frame_len, 0,
             (struct sockaddr*)&saddrll, sizeof(saddrll)) > 0)
    printf("Frame successfully sent!\n");
  else
    printf("Error, could not send\n");


  struct sockaddr_ll saddrll_receive;
  memset((void*)&saddrll_receive, 0, sizeof(saddrll_receive));
  socklen_t sll_len = (socklen_t)sizeof(saddrll_receive);

  int recv_result;
  char buffer_receive[ETH_FRAME_LEN];
  recv_result = recvfrom(s, buffer_receive, ETH_FRAME_LEN, 0,
                 (struct sockaddr*)&saddrll_receive, &sll_len);


  close(s);

  return 0;
}

这当然是在 Linux 机器上。

【问题讨论】:

  • 我知道这是一个旧线程,但是嘿.. 有可能您的框架已形成甚至传输,但您的操作系统中的驱动程序不够聪明,无法“包装”回框架;据它所知,它是出站的,仅此而已。如果你有一个交换机连接到端口,交换机可能会为你反弹回来。

标签: c ethernet frames


【解决方案1】:

您必须具有 root 权限才能使用原始套接字。

尝试将套接字设置为非阻塞模式:

flags = fcntl (sock, F_GETFL);
fcntl (sock, F_SETFL, flags | O_NONBLOCK);

【讨论】:

  • 是的,我知道。我已经在 root 权限下运行了该程序。 sendto() 命令返回值 >= 0。但是当调用 recvfrom() 函数时,它只是阻塞。我不确定是什么问题。
  • 您是否尝试将打开的套接字设置为 O_NONBLOCK(man fcntl,请参阅我编辑的答案)?
  • 我不明白您编辑的答案中的语法。 flag 是否应该是 recvfrom 函数的输入?
  • 不,使用 F_GETFL 可以获得打开的文件描述符(您的套接字)的当前“设置”标志,使用 F_SETFL 设置“设置”。因此,首先,您获取当前标志,然后使用 fcntl (sock, F_SETFL, flags | O_NONBLOCK) 设置非阻塞模式;并使用 fcntl (sock, F_SETFL, flags & ~O_NONBLOCK) 移除阻塞模式;
【解决方案2】:

您的代码 sn-p 与发布在博客上的代码相同!首先,您必须确保您的接口名称是“eth0”。然后您必须将目标 MAC 地址设置为您计算机的 MAC 地址 - 因为您似乎想在您的机器中发送和接收数据。目前您的代码正在使用假设的目标 MAC 地址。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-27
    • 1970-01-01
    • 2014-03-10
    • 2018-09-14
    • 2017-12-26
    • 1970-01-01
    相关资源
    最近更新 更多