概述
应用程序可以绕过传输层而直接使用IPv4和IPv6,这称为原始套接口(raw socket)。http://www.cnblogs.com/nufangrensheng/p/3583435.html。
原始套接口是一种对原始网络报文进行处理的套接口。原始套接口主要应用在底层网络编程上,同时也是网络黑客的必备手段。例如sniffer、拒绝服务(DoS)、IP地址欺骗等都需要在原始套接字的基础上实现。
与原始套接字对应,之前的TCP/UDP的套接字称为标准套接字,如下图所示,为标准套接字与原始套接字之间的关系。标准套接字与网络协议栈的TCP、UDP层打交道,而原始套接字则与IP层级网络协议栈核心打交道。
原始网络套接口提供普通的TCP和UDP套接口不提供的以下3个功能:
1、有了原始套接口,进程可以读与写ICMPv4、IGMPv4和ICMPv6等分组。
2、有了原始套接口,进程可以读与写内核不处理其协议字段的IPv4数据报。
3、有了原始套接口,进程还可以使用IP_HDRINCL套接口选项自行构造IPv4头部。
原始套接口的创建
创建一个原始套接口涉及如下步骤:
1、把第2个参数指定为SOCK_RAW调用socket函数,以创建一个原始套接口。第3个参数(协议类型)通常不为0。
只有超级用户才能创建原始套接口。
2、可以在这个原始套接口上如下开启IP_HDRINCL套接口选项:
const int on = 1; if(setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on)) < 0) 出错处理
3、可以在这个原始套接口上调用bind函数,不过比较少见。bind函数仅仅设置本地地址,因为原始套接口不存在端口的概念。
4、可以在这个原始套接口上调用connect函数,不过也比较少见。connect函数仅仅设置远地地址,同样因为原始套接字不存在端口的概念。
原始套接口输出
原始套接口的输出遵循以下规则:
1、普通输出通过调用sendto或sendmsg并指定宿IP地址完成。如果套接口已经连接,那么也可以调用write、writev或send。
2、如果IP_HDRINCL套接口选项未开启,那么由进程让内核发送的数据的起始地址指的是IP头部之后的第一个字节,因为内核将构造IP头部并把它置于来自进程的数据之前。内核把所构造的IPv4头部的协议字段设置成来自socket调用的第3个参数。
3、如果IP_HDRINCL套接口选项已开启,那么由进程让内核发送的数据的起始地址指的是IP头部的第一个字节。进程调用输出函数写出的数据量必须包括IP头部的大小。整个IP头部由进程构造,不过:(a)IPv4标识字段可置为0,从而告知内核设置该值;(b)IPv4头部校验和字段总是由内核计算并存储;(c)IPv4选项字段是可选的。
4、内核对于超出外出接口MTU的原始分组执行分片。
原始套接口输入
原始套接口输入遵循以下规则:
1、接收到的UDP分组和TCP分组绝不传递到任何原始套接口。如果一个进程想要读取含有UDP分组或TCP分组的IP数据报,它就必须在数据链路层读取这些分组。
2、大多数ICMP分组在内核完成处理其中的ICMP消息后传递到原始套接口。
3、所有IGMP分组在内核完成处理其中的IGMP消息后传递到原始套接口。
4、内核不认识其协议字段的所有IP数据报传递到原始套接口。内核对这些分组执行的唯一处理是针对某些IP头部字段的最小验证:IP版本、IPv4头部校验和、头部长度以及宿IP地址。
5、如果某个数据报以片段形式到达,那么在它的所有片段均到达且重组出该数据报之前,不传递任何片段分组到原始套接口。