原始套接字的特点

原始套接字(SOCK_RAW)可以用来自行组装IP数据包,然后将数据包发送到其他终端。也就是说原始套接字是基于IP数据包的编程(SOCK_PACKET是基于数据链路层的编程)。另外,必须在管理员权限下才能使用原始套接字。

原始套接口提供了普通TCP和UDP socket不能提供的3个能力:

1、进程使用raw socket 可以读写ICMP、IGMP等分组。这个能力还使得使用ICMP或IGMP构造的应用程序能够完全作为用户进程处理,而不必往内核中添加额外代码。

2、大多数内核只处理IPv4数据报中一个名为协议的8位字段的值为1(ICMP)、2(IGMP)、6(TCP)、17(UDP)四种情况。然而该字段的值还有许多其他值。进程使用raw socket 就可以读写那些内核不处理的IPv4数据报了。因此,可以使用原始套接字定义用户自己的协议格式。

3、通过使用raw socket ,进程可以使用IP_HDRINCL套接口选项自行构造IP头部。这个能力可用于构造特定类型的TCP或UDP分组等。

原始套接字的创建

   int sockfd = socket (AF_INET, SOCK_RAW, protocol);

protocol常用参数值如下(定义在netinet/in.h):

       IPPROTO_IP = 0,   /* Dummyprotocol for TCP.  *///这个协议的Dummy的意思是系统什么也不做。
IPPROTO_ICMP = 1, /* InternetControl Message Protocol. */
IPPROTO_IGMP = 2, /* InternetGroup Management Protocol. */
IPPROTO_TCP = 6, /* TransmissionControl Protocol. */
IPPROTO_UDP = 17, /* UserDatagram Protocol. */
IPPROTO_RAW = 255, /* Raw IPpackets. */

如果指定protocol为0(IPPROTO_IP)时,原始套接字可以接收内核传递给原始套接字的任何IP数据包

bind和 connect 函数说明

原始套接字直接使用IP协议的套接字,所以是非面向连接的,使用sendto和recvfrom函数。在这个套接字上能够调用connect和bind函数(一般不这么用),分别执行绑定对方和本地地址。

bind函数:调用bind函数后,发送数据包的源IP地址将是bind函数指定的地址。该函数仅仅设置本地地址,因为原始套接口不存在端口的概念。如是不调用bind,则内核将以发接口的主IP地址填充。假如配置了IP_HDRINCL,那么必须手工填充每个发送数据包的源IP地址。

connetc函数:调用connect函数后,能够用write和send发送数据包。调用该函数仅仅设置远地地址,同样因为原始套接口不存在端口号的概念。内核将用这个绑定的地址填充IP数据包的目的IP地址。

原始套接字的输出

如果IP_HDRINCL套接字选项未开启,那么由进程让内核发送的数据的起始位置指的是IP首部之后的第一个字节,因为内核将构造IP首部并把它置于来自进程的数据之前。内核把所有构造IPv4首部的协议字段设置成来之sock调用的第三个参数。

如果IP_HDRINCL套接字选项已开启,那么由进程让内核发送的数据的起始位置指的是IP首部的第一个字节。进程调用输出函数写出的数据量必须包括IP首部的大小。整个IP首部都是由进程构造,不过IP的标识字段可以设置为0,从而告知内核设置该值;IP首部校验和字段总是由内核计算并存储;IPv4的选项字段也是可选的。

另外,内核会对超出外出接口MTU的原始分组进行分片。

开启IP_HDRINCL的代码是:

       const int on =1;
if (setsockopt (sockfd, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on))< 0)
{
printf("setsockopt error!\n");
}

原始套接字的输入

首先要考虑内核将哪些接收到的IP数据报传递到原始套接字?这要遵循下面的规则:

1、接收到的UDP或者TCP分组绝不传递到任何原始套接字,如果一个进程想要读取含有UDP分组或TCP分组的IP数据报,它就必须在数据链路层读取这些分组(即使用IPPROTO_IP选项读取整个IP包)。

2、大多数ICMP分组在内核处理完其中的ICMP消息后传递到原始套接字。IGMP亦是如此。

3、内核把不认识其协议字段的所有IP数据报传递给原始套接字。内核把这些分组执行的唯一处理是针对某些IP首部字段的最小验证:IP版本,IPv4校验和,首部长度,以及目的地址。

4、如果某个数据报以片段的形式到达,那么在它的所有片段均到达且重组出该数据报之前,不传递任何片段分组给原始套接字。

当内核有一个需要传递到原始套接字的数据报时,它将检查所有进程上的所有原始套接字,以寻找所有匹配的套接字。每个匹配的套接字将被传递送以该IP数据报的一个副本。内核对每个原始套接字均执行以下3个测试,只有这三个测试均为真,内核才把接收到的数据报发送给这个套接字。

1、如果创建这个原始套接字时指定了非0的协议参数(socket的第三个参数),那么接收到的数据报协议字段必须匹配该值。

2、如果这个套接字已由bind调用绑定了某个IP地址,那么接收到的数据报的目的地址必须匹配这个绑定地址。

3、若该套接字调用了connect,那么接收到的数据报的源地址必须匹配这个已连接地址。

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

无论何时往一个原始IPv4套接字上递送一个IP数据报,传递到该套接字所在进程的都是包括IP首部在内的完整数据报。

需要注意的地方

1、使用 IPPROTO_TCP 和 IPPROTO_UDP选项的原始套接字时,只能发TCP或者UDP数据包(是否需要对IP头部的操作由 IP_HDRINCL 决定),而不能接收TCP或者UDP协议的数据包,因为TCP和UDP数据包由内核进行协议的判断,并查找IP地址和端口号相匹配的socket连接来递交数据包,而原始套接字没有端口的概念,因此不能接收TCP或者UDP的数据包。原始套接字只能通过IPPROTO_IP来获得整个IP数据包,然后从中提取TCP和UDP的数据。

2、使用 IPPROTO_IP 选项时,必须要设置IP_HDRINCL,因为内核自动合成IP数据包头部时,并不知道协议字段是什么,所以必须要用户自己来构建IP包头。

Linux系统编程(37)—— socket编程之原始套接字的更多相关文章

  1. 原始套接字-自定义IP首部和TCP首部

    /* ===================================================================================== * * Filenam ...

  2. Linux Socket 原始套接字编程

    对于linux网络编程来说,可以简单的分为标准套接字编程和原始套接字编程,标准套接字主要就是应用层数据的传输,原始套接字则是可以获得不止是应用层的其他层不同协议的数据.与标准套接字相区别的主要是要开发 ...

  3. Linux系统C语言socket tcp套接字编程

    1.套接字的地址结构: typedef uint32_t in_addr_t; //32位无符号整数,用于表示网络地址 struct in_addr{ in_addr_t s_addr; //32位 ...

  4. 关于linux 原始套接字编程

    关于linux 网络编程最权威的书是<<unix网络编程>>,但是看这本书时有些内容你可能理解的不是很深刻,或者说只知其然而不知其所以然,那么如果你想搞懂的话那么我建议你可以看 ...

  5. ZT Linux系统环境下的Socket编程详细解析

    Linux系统环境下的Socket编程详细解析 来自: http://blog.163.com/jiangh_1982/blog/static/121950520082881457775/ 什么是So ...

  6. linux网络编程之socket编程(一)

    今天开始,继续来学习linux编程,这次主要是研究下linux下的网络编程,而网络编程中最基本的需从socket编程开始,下面正式开始学习: 什么是socket: 在学习套接口之前,先要回顾一下Tcp ...

  7. Linux下的C Socket编程 -- 获取对方IP地址

    Linux下的C Socket编程(二) 获取域名对应的IP地址 经过上面的讨论,如果我们想要连接到远程的服务器,我们需要知道对方的IP地址,系统函数gethostbyname便能够实现这个目的.它能 ...

  8. Linux下的C Socket编程 -- 简介与client端的处理

    Linux下的C Socket编程(一) 介绍 Socket是进程间通信的方式之一,是进程间的通信.这里说的进程并不一定是在同一台机器上也有可能是通过网络连接的不同机器上.只要他们之间建立起了sock ...

  9. Linux网络编程——原始套接字实例:MAC 头部报文分析

    通过<Linux网络编程——原始套接字编程>得知,我们可以通过原始套接字以及 recvfrom( ) 可以获取链路层的数据包,那我们接收的链路层数据包到底长什么样的呢? 链路层封包格式 M ...

随机推荐

  1. JBoss 系列九十六:JBoss MSC - 简介及一个简单演示样例

    什么是 JBoss MSC JBoss MSC 即 JBoss Modular Service Container,是第三代 JBoss 产品 JBoss 7和WildFfly的内核,JBoss MS ...

  2. python使用正則表達式

    python中使用正則表達式 1. 匹配字符 正則表達式中的元字符有 .  ^  $ *   +  ?  { }  [ ]  \  | ( ) 匹配字符用的模式有 \d 匹配随意数字 \D 匹配随意非 ...

  3. linux 网络状态工具ss命令

    ss命令用于显示socket状态. 他可以显示PACKET sockets, TCP sockets, UDP sockets, DCCP sockets, RAW sockets, Unix dom ...

  4. 计算方法(一)用C#实现数值迭代

    平时,经常会遇到解方程,计算方法中常用的有二分法(精度太低,迭代次数多,一般没人用),牛顿迭代法,弦截法,网上大多都是C++或者Java的实现代码,很少有C#的,我在本科毕业论文中用到了这些,那时也需 ...

  5. 粘帖屏幕截图到web页面插件 screenshot-paste

    在很多场合下,我们可能有这样的需求:提供个屏幕截图上传到系统,作为一个凭证.传统的操作方式是:屏幕截图,保存文件到本地,在web页面上选择本地文件并上传,这里至少需要三步.有没有可能直接将截图粘帖到w ...

  6. 读取XML文件的几种方式的效率分析

    第一种:使用XmlReader来读取. Stopwatch sw = Stopwatch.StartNew(); List<Dictionary<string, string>> ...

  7. nat123 与微信公众号开发者测试账号配合调试

    由于公司本身是做互联网 电商行业的,微信也是一个大块,近期开始花费时间在整合,总结自己的经验,看看之前的实现是否有明显的问题. 花了点钱(8块钱)充值了nat123,进行了内网穿透.之前也有使用花生壳 ...

  8. zepto源码研究 - ajax.js(请求过程中的各个事件分析)

    简要:ajax请求具有能够触发各类事件的功能,包括:触发全局事件,请求发送前事件,请求开始事件,请求结束事件等等,贯穿整个ajax请求过程,这是非常有用的,我们可以利用这些事件来做一些非常有意思的事情 ...

  9. XML Schema (2)

    定义元素 <!-- 1.定义元素book --> <element name="book"></element> <!-- 2.定义元素包 ...

  10. java数据流

    DataInputStream和DataOutputStream提供了可以直接存取java基本类型(int,double等)的方法.对于存取基本类型,在效率上比普通字节流要快很多.它们分别继承inpu ...