使用带有原始以太网帧的sendmsg / sendmmsg

我试图用C通过sendmsg发送原始的以太网数据包。 这段代码成功地打开了一个原始数据包套接字,尝试用一个字节数组(char message [])填充一个结构iovec,然后用目标地址,地址长度和一个指向结构iovec的指针填充一个struct msghdr,消息。 sendmsg()为每个调用返回EINVAL,但我不知道哪些参数是无效的。 (我已经删除了一些perror()调用,使这个代码更简单的阅读;输出是“无效参数”)。

我一直无法findsendmsg()如何与原始套接字一起工作的示例,但是使用sendto()的类似代码按预期工作。 在那个代码中,我明确地形成了以太网帧,包括头文件和协议信息,但是据我的理解,sendmsg()调用并不是必须的。 我也试图让message.iov_base指向一个缓冲区,该缓冲区包含明确形成的以太网帧,包括14个字节的头,但是sendmsg()也是这样。

Sendmsg()和sendmmsg()可以与原始以太网帧一起工作吗? 有什么我错过了使它无效的iovec?

30 int main(void) { 32 unsigned char dest[ETH_ALEN] = {0x11, 0x11, 0x11, 0x11, 0x11, 0x11}; // destination MAC address 33 34 // Socket variables 35 int s; 36 unsigned short protocol = htons(ETH_P_802_EX1); 38 39 // Message variables 40 char message[] = {"Test message. Test message. Test message!\n"}; 41 size_t msg_len = strlen(message) + 1; // Message length includes null terminator 42 int e; // Error code 43 struct msghdr msg; 44 struct iovec msgvec; 45 46 // Setup source-side socket 47 s = socket(AF_PACKET, SOCK_RAW, protocol); 48 if (s < 0) { printf("%d: %s\n", errno, strerror(errno)); return EXIT_FAILURE; } 49 50 msgvec.iov_base = message; 51 msgvec.iov_len = msg_len; 52 memset(&msg, 0, sizeof(msg)); 53 msg.msg_name = dest; 54 msg.msg_namelen = ETH_ALEN; 55 msg.msg_control = NULL; 56 msg.msg_controllen = 0; 57 msg.msg_flags = 0; 65 msg.msg_iov = &msgvec; 66 msg.msg_iovlen = 1; 67 68 for (int i=0; i<10; i++) { 69 e = sendmsg(s, &msg, 0); 73 } 79 close(s); 80 return EXIT_SUCCESS; 81 } 

我做了一些调整后,得到这个代码工作。

sendmsg()手册页暗示的地址不是以字节串的形式发送,而是使用struct sockaddr_ll。 我还指出了iovec在包含完整的以太网帧包括头的缓冲区。 为什么man页面在sendto原型中明确指定了一个const struct sockaddr *,但是msghdr定义中的一个void *仍然不为我所知。

我在调用socket()后添加了这段代码:

  39 struct sockaddr_ll address; 40 struct ifreq buffer; // To get information with ioctl() 41 char ifname[] = {"eth0"}; 42 int ifindex; // Interface index, from ioctl() call 57 memset(&buffer, 0, sizeof(buffer)); // Getting interface index value 58 strncpy(buffer.ifr_name, ifname, IFNAMSIZ); 59 ioctl(s, SIOCGIFINDEX, &buffer); 60 ifindex = buffer.ifr_ifindex; 61 62 // Setup sockaddr_ll address 63 memset( (void*) &address, 0, sizeof(address) ); 64 address.sll_family = PF_PACKET; 65 address.sll_ifindex = ifindex; 66 address.sll_halen = ETH_ALEN; 67 memcpy( (void*) (address.sll_addr), (void*) dest, ETH_ALEN ); 

并替换为msghdr结构分配的这些代码行:

  81 msg.msg_name = &address; 82 msg.msg_namelen = sizeof(address);