此问题在此处已有答案:
'SOCK_RAW' option in 'socket' system call(1个答案)
2天前关闭。
我尝试在Linux系统中通过C函数sendto()
发送一个以太网帧。该帧不包含任何IP级别信息,因为IP协议在我的应用程序中没有使用。
sendto() documentation提供以下信息:
函数sendto()的签名
ssize_t sendto(整数套接字fd、常量空值 * 缓冲区、大小_t长度、整数标志、常量结构套接字地址 * 目的地址、套接字长度_t地址长度);
要传输的缓冲区
对于send()和sendto(),消息位于buf中,长度为len。
目标地址
如果sendto()用于连接模式(SOCK_STREAM,SOCK_SEQPACKET)套接字,则忽略参数dest_addr和addrlen(当它们不为NULL和0时,可能会返回错误EISCONN),并且当套接字未实际连接时,会返回错误ENOTCONN。否则,目标地址由dest_addr提供,addrlen指定其大小。
在我的例子中,套接字是通过以下指令创建的:
int sck_dgram = socket(AF_PACKET, SOCK_DGRAM, 0);
因此它不是连接模式套接字。要设置目标地址,我使用以下指令:
struct sockaddr_ll dest_addr = {0};
dest_addr.sll_family = AF_PACKET;
dest_addr.sll_ifindex = ifindex;
dest_addr.sll_halen = ETHER_ADDR_LEN;
memcpy(dest_addr.sll_addr, mac_dest, ETHER_ADDR_LEN);
其中mac_dest
是包含MAC地址目的地的数组,ifindex
是用于发送的以太网接口的索引。
我的sendto()指令
总之,要发送buf[BUFFSIZE]
数据数组,我使用以下sendto()
指令:
sendto(sck_dgram, buf, BUFFSIZE, 0, (struct sockaddr*)&dest_addr, sizeof(dest_addr))
使用前面的指令,我只能通过dest_addr
设置目标MAC地址(帧的0到5字节),而不能设置帧的源MAC地址(帧的6到11字节)。源MAC地址自动设置为发送所用Ethernet接口的MAC地址。
我的问题
是否可以发送一个源MAC地址与用于发送的Ethernet接口的MAC地址不同的帧Ethernet?
**EDIT:**问题的解决方法是修改创建套接字的指令,用SOCK_RAW
替换SOCK_DGRAM
,正确的指令为:
int sck_raw = socket(AF_PACKET, SOCK_RAW, 0);
根据前面的指令,我必须修改sendto()
指令,使buf[]
包含14个八位字节,包括目标mac地址、源mac地址和以太网类型。这些数据必须放在buf[]
的前14个字节中。因此,我必须增加buf
的大小,如下所示:
char buf[BUFFSIZE+14];
// set buf[0] .. buf[5] with mac dest
// set buf[6] .. buf[11] with mac source
// set buf[12] and buf[13] with ether type (set to 0x0200 and this is the data lenght)
// sendto() becomes:
sendto(sck_raw, buf, BUFFSIZE+14, 0, (struct sockaddr*)&dest_addr, sizeof(dest_addr))
此Wikipedia link说明了以太网帧结构。
1条答案
按热度按时间lnlaulya1#
最简单的解决方案是改变以太网卡上的mac地址。
如果你愿意,你可以使用tc & iptables solution. iptables来设置包类,例如基于udp端口,然后你可以使用tc来更改包类的源mac地址。