2014-01-28 42 views
2

我正在使用Enea OSE这个实时嵌入式操作系统的项目。我想你们中许多人对这个操作系统并不熟悉,但我认为你可以回答我的问题。我想在两张卡之间使用接口“eth1”发送一个原始以太网帧,这些接口直接通过以太网电缆连接到另一个卡(无交换机或任何其他设备)。 “eth1”接口未分配任何IP地址。如何使用sendto通过原始以太网套接字发送数据而不使用sockaddr_ll?

我可以简单地宣布用

int s = socket(AF_INET, RAW_SOCKET, ...) 

一个插座,然后我可以使用绑定套接字到相应的设备接口:

setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, "eth1", 4); 

什么我的计划是这样做,才能发送数据从一张卡到另一张卡,手动到用源和目标MAC地址填充以太网缓冲区阵列。

sendto(int sockfd, const void *buf, size_t len, int flags, 
      const struct sockaddr *dest_addr, socklen_t addrlen); 

在这些例子中我已经在互联网上使用发现:然后使用的sendto(....)

然而SENDTO程序也需要 SOCKADDR结构与信息发送数据sockaddr_in当ip地址是已知的和 sockaddr_ll为原始帧。但OSE不提供 sockaddr_ll结构。但它提供了 sockaddr,我不知道我是否可以用于原始以太网帧?

struct sockaddr { 
    unsigned short sa_family; // address family, AF_xxx 
    char    sa_data[14]; // 14 bytes of protocol address 
}; 

我也尝试过在调用sendto()时使用NULL作为sockaddr字段,发送失败。这使我想到了我的问题。我可以使用简单的sockaddr结构填充目标MAC地址而不使用任何IP?那看起来怎么样? (我还没有看到任何代码显示这个)。为什么我们甚至不得不提供一个sockaddr结构?以太网数据缓冲区是否已经包含这些信息?最后,我试图完成甚至可能没有为网络设备分配IP地址?

我知道什么,我试图做的似乎奇怪,但有它的一个原因:)我会很高兴,如果你会告诉我如何做到这一点,而无需使用 sockaddr_ll结构。

回答

1

据我所知的Enea OSE带有这些头

#include <sys/socket.h> 
#include <netpacket/packet.h> 

后者宣称struct sockaddr_ll它应该让你的以太网数据包发送到类型的插座

int sockfd = socket(AF_PACKET, type, protocol); 

类型可SOCK_DGRAMSOCK_RAW(您将需要自己创建以太网报头)。

我找不到以太网协议的正确标头。您将需要搜索您的标头ETH_*定义并找到正确的以太网标头

+0

我找不到库中的头文件。这是Enea OSE5.6,或许它不包含在这个版本中?我也无法在OSE应用程序编程手册中找到它。那么我可以使用简单的sockaddr结构体还是必须使用sockaddr_ll? – Euklides

+0

'struct sockaddr'只是一个通用的占位符。不管你输入什么,它都必须由套接字协议栈进行解释。所以如果你的内核实现了原始/数据报以太网通信,那么必须有一个头部来定义地址族AF_ *',该协议和合适的结构来保存协议控制信息。 –

-1

我没有一个很好的答案,但在Linux中绑定地址后显示您可以使用写入。 那么你为什么要使用sendto()?

第一关在Linux发送原始数据包(我不知道你的RTOS,但是你应该寻找在由OS提供的包含文件丢失的定义) ,您需要在您的系统或使用上的根该setcap命令添加原料的能力 例如:

sudo setcap 'CAP_NET_RAW+eip CAP_NET_ADMIN+eip' /path/to/your/executable 

然后在你的程序:

#include <stdlib.h> 
#include <ctype.h> 
#include <unistd.h> 
#include <string.h> 

#include <sys/socket.h> 
#include <sys/ioctl.h> 
#include <linux/if.h> 
#include <linux/if_packet.h> 
#include <linux/if_ether.h> 

int main() 
{ 
    char* ifname = "eth0"; 
    unsigned char srcMac[6]; 
    unsigned char dstMac[6] = {0, 0xDE, 0xAD, 0xBE, 0xEF, 0}; 
    int fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); 

    /* bind to interface */ 
    struct ifreq req; 
    memset(&req, 0, sizeof(req)); 
    strcpy((char*)req.ifr_name, (char*)ifname); 

    if (ioctl(fd, SIOCGIFINDEX, &req) < 0) 
    { 
     perror("init: ioctl"); 
     close(fd); 
     return -1; 
    } 

    struct sockaddr_ll addr; 
    memset(&addr, 0, sizeof(addr)); 
    addr.sll_family = PF_PACKET; 
    addr.sll_protocol = 0; 
    addr.sll_ifindex = req.ifr_ifindex; 

    if (bind(fd, (const struct sockaddr*)&addr, sizeof(addr)) < 0) 
    { 
     perror("init: bind fails"); 
     close(fd); 
     return -1; 
    } 

    if (ioctl(fd, SIOCGIFHWADDR, &req) < 0) 
    { 
     perror("init: ioctl SIOCGIFHWADDR"); 
     close(fd); 
     return -1; 
    } 

    /* store your mac address somewhere you'll need it! in your packet */ 
    memcpy(srcMac, (unsigned char*)req.ifr_hwaddr.sa_data, ETH_ALEN); 

    int length = 60; 
    unsigned char txPkt[ length ]; 
    unsigned char* pkt = txPkt; 
    memset(txPkt, 0, sizeof(txPkt)); 

    //destination mac address 
    memcpy(pkt, dstMac, 6); 
    pkt += 6; 

    // source mac address 
    memcpy(pkt, srcMac, 6); 
    pkt += 6; 

    // add more data, like a type field! etc 

    int bytes = write(fd, txPkt, length); 
} 

当你写你就会有一个原始数据包放于MAC地址等自己。 FCS可以省略(应由hw处理),所以最小包大小为60字节,我相信你必须发送60个字节。 如果您在传出卡上进行捕获,您将看不到FCS。

我想你应该能够使用sendto()函数来代替写入,但请注意,struct sockaddr_ll不包含任何目标信息,那么为什么要麻烦?

+1

显然这个问题不是关于linux。 – nos

1

对不起以前的含糊答案。 这是我的完整答案。希望它有帮助。

回答你的问题:是的,你可以使用MAC地址发送。

声明插座这样的:

int sockfd = socket(AF_PACKET, SOCK_RAW, IPPROTO_RAW) 

上的错误,它的sockfd是套接字文件描述符将返回-1。

用于以太网帧的结构可以被构造是这样的:

struct ethernet_msg{ 
     char destination_mac[6]; 
     char source_mac[6]; 
     char transport_protocol[2]; 
     char data1[6]; 
     char data2[6]; 
     char data3[8]; 
     char data4[8]; 
     char data5[8]; 
     char data6[2]; 
     char data7[2]; 
     char data8[6]; 
     char data9[4]; 
     char data10[6]; 
}; 
struct ethernet_msg my_msg = { 
     {0x91, 0xe0, 0xf0, 0x01, 0x00, 0x00},//destination_mac 
     {0x08, 0x00, 0x27, 0x90, 0x5f, 0xae},//source_mac 
     {0x22, 0xf0},//transport_protocol 
     {0xfc, 0x06, 0x00, 0x2c, 0x00, 0x00},//data1 
     {0x00, 0x00, 0x00, 0x00, 0x00, 0x00},//data2 
     {0x08, 0x00, 0x27, 0xff, 0xfe, 0x90, 0x5f, 0xae},//data3 
     {0xd8, 0x80, 0x39, 0xff, 0xfe, 0xd0, 0xac, 0xb5},//data4 
     {0xd8, 0x80, 0x39, 0xff, 0xfe, 0xd0, 0x9b, 0xc8},//data5 
     {0x00, 0x00},//data6 
     {0x00, 0x00},//data7 
     {0x00, 0x00, 0x00, 0x00, 0x00, 0x00},//data8 
     {0x00, 0x00, 0x00, 0x5f},//data9 
     {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}//data10 
}; 

注我怎样放置在框架内不同长度的字符数组。您可以根据您的需要进行设置。要获取接口的MAC地址发送于:

void get_interface_MAC(char interface_name[IFNAMSIZ], int sockfd) 
{ 
     struct ifreq if_mac; 
     memset(&if_mac, 0, sizeof(struct ifreq)); 
     strncpy(if_mac.ifr_name, interface_name, IFNAMSIZ-1); 
     if (ioctl(sockfd, SIOCGIFHWADDR, &if_mac) < 0) 
       perror("SIOCGIFHWADDR"); 
} 

然后获取网络设备的索引:

/* Get the index of the interface to send on */ 
int get_interface_index(char interface_name[IFNAMSIZ], int sockfd) 
{ 
     struct ifreq if_idx; 
     memset(&if_idx, 0, sizeof(struct ifreq)); 
     strncpy(if_idx.ifr_name, interface_name, IFNAMSIZ-1); 
     if (ioctl(sockfd, SIOCGIFINDEX, &if_idx) < 0) 
       perror("SIOCGIFINDEX"); 

     return if_idx.ifr_ifindex; 
} 

socket_address.sll_ifindex = get_interface_index(interface_name, sockfd); 
/* Address length*/ 
socket_address.sll_halen = ETH_ALEN; 
/* Destination MAC */ 
memcpy(socket_address.sll_addr, avdecc_msg.destination_mac, ETH_ALEN); 

然后到最后发送此通过套接字:

/* Send packet */ 
if (sendto(sockfd, &avdecc_msg, sizeof(avdecc_msg), 0, (struct sockaddr*)&socket_address, sizeof(struct sockaddr_ll)) < 0) 
       printf("Send failed\n"); 

希望有帮助。 我用这个link并改进了代码来达到此目的。它为我工作。

相关问题