数据链路层sniffer(一) raw_socket使用总结

raw_socket介绍

raw socket中文叫原始套接字,它和其他的套接字的不同之处在于它工作在网络层或数据链路层,而其他类型的套接字工作在传输层,只能进行传输层数据操作。

raw_socket 创建

raw socket的创建和使用
1、像其他类型的socket一样,raw socket的创建非常简单,直接使用socket函数进行创建

 int socketfd = socket(AF_INET,SOCK_RAW,IPPROTO_ICMP);/*在网络层使用的原始套接字*/
 int socketfd = socket(PF_PACKET,SOCK_RAW,htons(ETH_P_IP));/*在链路层使用*/

这两句程序挑选一个,你就可以创建一个原始套接字.然而这种类型套接字的功能却与TCP或者UDP类型套接字的功能有很大的不同:TCP/UDP类型的套接字只能够访问传输层以及传输层以上的数据,因为当IP层把数据传递给传输层时,下层的数据包头已经被丢掉了.而原始套接字却可以访问传输层以下的数据,所以使用raw套接字你可以实现上至应用层的数据操作,也可以实现下至链路层的数据操作

参数说明:

int socket( int af, int type, int protocol);

af:一个地址描述。目前仅支持AF_INET格式,也就是说ARPA Internet地址格式。
type:指定socket类型。新套接口的类型描述类型,如TCP(SOCK_STREAM)和UDP(SOCK_DGRAM)。常用的socket类型有,SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等等。
protocol:顾名思义,就是指定协议。套接口所用的协议。如调用者不想指定,可用0。常用的协议有,IPPROTO_TCP、IPPROTO_UDP、IPPROTO_STCP、IPPROTO_TIPC等,它们分别对应TCP传输协议、UDP传输协议、STCP传输协议、TIPC传输协议。

例如:

  1. sock_fd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)
    第一个参数:
    AF_INET:抓网络层的IP数据报。
    第三个参数
    TYPE=IPPROTO_ICMP: 抓取ICMP包(网络层协议,封装在IP数据报上)。
    TYPE=IPPROTO_TCP: 抓取TCP包。
    TYPE==IPPROTO_UDP: 抓取UDP包。

  2. sock_fd = socket(PF_PACKET, SOCK_RAW, htons(x) )
    第一个参数:
    PF_PACKTE: 抓取数据链路层的MAC帧。
    第三个参数:
    x:常用的取值有:ETH_P_IP 、ETH_P_ARP 、ETH_P_RARP。(对应于MAC帧首部类型字段)
    ETH_P_IP 0x0800 只接收发往本机mac的ip类型的数据帧
    ETH_P_ARP 0x0806 只接受发往本机mac的arp类型的数据帧
    ETH_P_RARP 0x08035 只接受发往本机mac的rarp类型的数据帧
    ETH_P_ALL 0x3 接收发往本机mac的所有类型ip arp rarp的数据帧, 接收从本机发出的所有类型的数据帧.(混杂模式打开的情况下,会接收到非发往本地mac的数据帧)

数据包接收和发送

  • 网络层接收

    recvfrom(sd, buffer, sizeof(buffer), 0,(struct sockaddr *)&client_addr, &addrlen));//

后两个参数可以为null

接受的报文是从IP数据报的第一个字节开始的。

当内核有一个需要传递到原始套接字的IP数据报时,它将检查所有进程上的原始套接字,以寻找所有匹配的套接字。每个匹配到的套接字将被递送以该iP数据报的一个副本。(事实证明,如果进程过多,匹配的套接字过多,内核会忙于数据报的软过滤和分发,而实际的套接字却空闲,导致性能下降)
内核对每个原始套接字均执行如下3个测试,只有这三个测试为真,内核才把接收到的数据报递送到这个套接字。

【1】如果创建这个套接字时制订了非0的协议参数(socket的第三个参数),那么接受到的数据报的协议字段必须匹配该值,否则数据报不递送到这个套接字

【2】如果这个原始套接字已由bind调用绑定了某个本地IP地址,那么接受到的数据报的目的IP地址必须匹配这个绑定的地址,否则该数据报不递送到这个套接字。

【3】如果这个原始套接字已由connect调用指定了某个外地IP地址,那么接受到的数据报的源IP地址必须匹配这个已连接地址,否则该数据报不递送到这个套接字。

注意,如果一个原始套接字以0值协议参数创建的,而且没有bind和connect,那么该套接字将接收可由内核传递到原始套接字的每个原始数据报的一个副本。

  • 网络层发送
sendto(sd, buffer, request_length, 0, (sockaddr *)&client_addr, addrlen);

如果IP_HDRINCL未开启,由进程让内核发送的数据是从IP首部之后的第一个字节开始的,内核会自动构造合适的IP
如果IP_HDRINGL开启,进程需要自行构造IP包

  • 数据链路层接收
struct sockaddr_ll client;
socklen_t addr_length = sizeof(sockaddr_ll);
recvfrom(sock, buffer, 2048, 0, (sockaddr *)&client, &addr_length);//此时的地址是数据链路层的地址
  • 数据链路层发送

    sendto(sock, sendbuffer, n, 0, (struct sockaddr *) &client, sizeof(client));

猜你喜欢

转载自blog.csdn.net/qq_32350719/article/details/88689768