SOCK_PACKET和PF_PACKET

Linux: SOCK_PACKET and PF_PACKET

There are two methods of receiving packets from the datalink layer under Linux. The original method, which is more widely available but less flexible, is to create a socket of type SOCK_PACKET. The newer method, which introduces more filtering and performance features, is to create a socket of family PF_PACKET. To do either, we must have sufficient privileges (similar to creating a raw socket), and the third argument to socket must be a nonzero value specifying the Ethernet frame type. When using PF_PACKET sockets, the second argument to socket can be SOCK_DGRAM, for “cooked” packets with the link-layer header removed, or SOCK_RAW, for the complete link-layer packet. SOCK_PACKET sockets only return the complete link layer packet. For example, to receive all frames from the datalink, we write

fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); /* newer systems*/

or

fd = socket(AF_INET, SOCK_PACKET, htons(ETH_P_ALL)); /* older systems*/

This would return frames for all protocols that the datalink receives.

If we wanted only IPv4 frames, the call would be

fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP)); /* newer systems */

or

fd = socket(AF_INET, SOCK_PACKET, htons(ETH_P_IP)); /* older systems */

Other constants for the final argument are ETH_P_ARP and ETH_P_IPV6, for example.

Specifying a protocol of ETH_P_xxx tells the datalink which frame types to pass to the socket for the frames the datalink receives. If the datalink supports a promiscuous mode (e.g., an Ethernet), then the device must also be put into a promiscuous mode, if desired. This is done with a PACKET_ADD_MEMBERSHIP socket option, using a packet_mreq structure specifying an interface and an action of PACKET_MR_PROMISC. On older systems, this is done instead by an ioctl of SIOCGIFFLAGS to fetch the flags, setting the IFF_PROMISC flag, and then storing the flags with SIOCSIFFLAGS. Unfortunately, with this method, multiple promiscuous listeners can interfere with each other and a buggy program can leave promiscuous mode on even after it exits.

Some differences are evident when comparing this Linux feature to BPF and DLPI:

  • The Linux feature provides no kernel buffering and kernel filtering is only available on newer systems (via the SO_ATTACH_FILTER socket option). There is a normal socket receive buffer, but multiple frames cannot be buffered together and passed to the application with a single read. This increases the overhead involved in copying the potentially voluminous amounts of data from the kernel to the application.
  • SOCK_PACKET provides no filtering by device. (PF_PACKET sockets can be linked to a device by calling bind.) If ETH_P_IP is specified in the call to socket, then all IPv4 packets from all devices (Ethernets, PPP links, SLIP links, and the loopback device, for example) are passed to the socket. A generic socket address structure is returned by recvfrom, and the sa_data member contains the device name (e.g., eth0). The application must then discard data from any device in which it is not interested. The problem again is too much data can be returned to the application, which can get in the way when monitoring a high-speed network.

原文链接:http://www.masterraghu.com/subjects/np/introduction/unix_network_programming_v1.3/ch29lev1sec4.html

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
可以使用PF_PACKETSOCK_DGRAM发送VLAN。在Linux系统中,可以使用socket API创建一个PF_PACKET类型的socket,并在发送数据时使用SOCK_DGRAM类型。在发送数据包时,需要将VLAN标签添加到数据包的头部。可以使用struct vlanhdr结构体来构建VLAN标签。以下是一个示例代码片段,演示了如何使用PF_PACKETSOCK_DGRAM发送带有VLAN标签的数据包: ``` #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <linux/if_packet.h> #include <linux/if_ether.h> #include <linux/if_vlan.h> #include <net/if.h> #define ETH_P_8021Q 0x8100 int main(int argc, char *argv[]) { int sockfd; struct sockaddr_ll dest_addr; char *iface; unsigned char src_mac[6] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55}; unsigned char dst_mac[6] = {0x00, 0x66, 0x77, 0x88, 0x99, 0xaa}; unsigned short vlan_tag = 100; // 创建PF_PACKET类型的socket if ((sockfd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_8021Q))) < 0) { perror("socket create error"); exit(1); } // 设置目标MAC地址 memset(&dest_addr, 0, sizeof(struct sockaddr_ll)); dest_addr.sll_family = AF_PACKET; dest_addr.sll_protocol = htons(ETH_P_8021Q); dest_addr.sll_halen = ETH_ALEN; dest_addr.sll_ifindex = if_nametoindex(iface); memcpy(dest_addr.sll_addr, dst_mac, 6); // 构造VLAN标签 struct vlanhdr vlan; vlan.h_vlan_TCI = htons(vlan_tag); vlan.h_vlan_encapsulated_proto = htons(ETH_P_IP); // 构造数据包 char payload[] = "hello world"; char packet[ETH_FRAME_LEN]; memcpy(packet, dst_mac, 6); memcpy(packet + 6, src_mac, 6); memcpy(packet + 12, &vlan, sizeof(struct vlanhdr)); memcpy(packet + 16, payload, strlen(payload)); // 发送数据包 if (sendto(sockfd, packet, sizeof(packet), 0, (struct sockaddr *)&dest_addr, sizeof(struct sockaddr_ll)) < 0) { perror("sendto error"); exit(1); } close(sockfd); return 0; } ``` 请注意,该示例代码中的iface变量需要设置为发送接口的名称,例如eth0。在发送数据包时,可以使用sendto函数将数据包发送到目标MAC地址。在构造数据包时,需要按照以下顺序填充数据包的头部:目标MAC地址、源MAC地址、VLAN标签、有效载荷。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值