通过PF_PACKET创建socket的时候,其中第三个参数protocol(socket(PF_PACKET, type, protocol))的值可以指定为ETH_P_ALL/ ETH_P_ARP/ ETH_P_IP/ 0。
1. 指定为0的时候,表示所创建的socket并不监听任何报文;
2. 指定为 ETH_P_ALL的时候,表示监听所有报文;
3. 指定为ETH_P_ARP的时候,表示监听ARP报文;
4. 指定为ETH_P_IP的时候,表示监听IP报文。
当创建这种类型的socket时,如果主要目的不是为了监听报文而是发送自己组装的报文,那么protocol的取值就变得不重要了。也就是说,如果纯粹的使用PF_PACKET类型的socket来进行报文发送的时候,这几个取值之间没有任何的本质区别。
用户程序在提交发送数据给内核以前 (sendto),必须完整的填写以太网头部,ARP头部及其数据(ARP报文),IP头部及其数据(IP报文)等等,并且其正确性和有效性也需要由应用程序来进行保证,内核是不会干预其中的内容,除了添加以太网的CRC以及发送数据以外。 通过bind系统调用,也可以改变应用程序的接收行为(或者称为监听行为)。
如在创建PF_PACKET的socket的时候,protocol的参数被指定为ETH_P_ALL,而在调用bind的时候(绑定socket到网卡),传递的protocol参数为ETH_P_ARP,那么内核就会将应用程序的接收行为同时修改为ETH_P_ARP(只监听ARP报文)。
以下给出bind的例子:
struct sockaddr_ll sll;
sll.sll_family = AF_PACKET;==>必须指定为AF_PACKET
sll.sll_protocol = htons(ETH_P_ALL);
sll.sll_ifindex = if_index;==> 绑定网卡的编号
bind(socket, (struct sockaddr *)&sll, sizeof (struct sockaddr_ll));==>进行绑定
当调用bind的时候,protocol为0,那么内核就不会转发任何数据报文给该socket。在发送报文的时候,如果已经进行了网卡绑定,那么sendto系统调用的const struct sockaddr *to参数可以指定为NULL,其长度指定为0。const struct sockaddr *to一般在以下两种情况下会被使用。
1)没有进行网卡绑定
2)已经进行网卡绑定,但所发送的数据是给另外一个不同网卡的时候。