我正在开发一个使用Enea OSE的项目,这是一个实时的嵌入式操作系统。我猜你们很多人都不熟悉这个操作系统,但我认为无论如何你都可以回答我的问题。我想使用接口“eth1”在2张卡之间发送原始以太网帧,这些接口通过以太网电缆(无交换机或任何东西)直接连接到另一个。 “eth1”接口未分配任何IP地址。
我可以使用
简单地声明一个套接字int s = socket(AF_INET, RAW_SOCKET, ...)
然后我可以使用以下命令将套接字绑定到相应的设备接口:
setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, "eth1", 4);
为了将数据从一张卡发送到另一张卡,我的计划是手动填写带有源MAC地址和目标MAC地址的以太网缓冲区阵列。然后使用sendto(....)
发送数据然而 sendto 例程还需要一个带有信息的 sockaddr 结构:
sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
在互联网上找到的示例中,当知道ip地址时使用 sockaddr_in ,对于原始帧使用 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
};
我也尝试使用NULL,当调用sendto()时,对于sockaddr字段,发送失败。这让我想到了我的问题。我可以使用简单的sockaddr结构来填充目标MAC地址而不使用任何IP吗?那怎么样? (我没有看到任何显示此代码的代码)。为什么我们甚至必须提供 sockaddr 结构?以太网数据缓冲区是否已包含此信息?最后,如果没有为网络设备分配IP地址,我是否正在努力实现这一目标?
我知道我想要做的事情似乎很奇怪,但有理由这样做:)如果你能告诉我如何在不使用 sockaddr_ll struct。
答案 0 :(得分:1)
据我所知,Enea OSE附带了这些标题
#include <sys/socket.h>
#include <netpacket/packet.h>
后者声明struct sockaddr_ll
,它应该允许您将以太网数据包发送到类型为
int sockfd = socket(AF_PACKET, type, protocol);
类型可以是SOCK_DGRAM
或SOCK_RAW
(您需要自己创建以太网标头)。
我找不到以太网协议的正确标头。您需要在标头中搜索ETH_*
定义并找到正确的以太网标头
答案 1 :(得分:1)
对以前模糊的回答感到抱歉。 这是我的完整答案。希望它有所帮助。
回答你的问题:是的,你可以只使用MAC地址发送。
像这样声明套接字:
self._interpolation.before_read
出错时,作为套接字文件描述符的sockfd将返回-1。
以太网帧的结构可以这样构造:
int sockfd = socket(AF_PACKET, SOCK_RAW, IPPROTO_RAW)
请注意我如何将不同长度的char数组放在框架内。您可以根据需要进行设置。要获取要发送的接口的MAC地址:
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
};
然后获取网络设备的索引:
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);
希望有所帮助。 我使用了这个link并对代码进行了改进以实现此目的。它对我有用。
答案 2 :(得分:-1)
我没有一个很好的答案但是在你绑定地址之后在Linux中你可以使用write。 那么你为什么要使用sendto()?
首先在Linux中发送原始数据包(我不知道您的RTOS,但您应该查找该操作系统提供的包含文件中缺少的定义) 您需要成为系统的root用户或使用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
不包含任何目标信息,为什么还要麻烦?