上节讲到的RAW socket虽然已经能处理IP层的数据了,但是在实际的应用中可能需要获取更加底层的报文信息。这就需要用到另一种更加强大的socket创建方式——packet socket,创建之后直接可以操作包括以太层在内的报文数据。
packet socket的创建方式如下:
socket(AF_PACKET, int socket_type, int protocol);
其中socket_type有SOCK_RAW 和 SOCK_DGRAM两种类型,它们的区别如下:
使用SOCK_RAW参数创建的packet socket收到的数据包含以太头及以太头以上的数据;
使用SOCK_DGRAM参数创建的packet socket收到的数据不包含以太头。
protocol参数常用的有ETH_P_IP,ETH_P_ARP和ETH_P_ALL三种,指定为ETH_P_IP后可以抓取ARP报文,指定ETH_P_ARP后可以抓取ARP报文,指定ETH_P_ALL后可以抓取所有报文。需要注意的是这个参数在指定时需要将协议类型转换为网络序传入,如htons(ETH_P_ALL),否则会导致无法抓取到预期的报文。更多的以太层协议类型可以在linux/if_ether.h头文件中找到。
创建了packet socket后,默认情况下所有网口的报文都能收到,如果只想收到指定网口的报文,需要使用bind接口传入struct sockaddr_ll结构来设置。struct sockaddr_ll结构定义如下: