Linux系统编程(37)—— socket编程之原始套接字
原始套接字的特点
原始套接字(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编程之原始套接字的更多相关文章
- 原始套接字-自定义IP首部和TCP首部
/* ===================================================================================== * * Filenam ...
- Linux Socket 原始套接字编程
对于linux网络编程来说,可以简单的分为标准套接字编程和原始套接字编程,标准套接字主要就是应用层数据的传输,原始套接字则是可以获得不止是应用层的其他层不同协议的数据.与标准套接字相区别的主要是要开发 ...
- Linux系统C语言socket tcp套接字编程
1.套接字的地址结构: typedef uint32_t in_addr_t; //32位无符号整数,用于表示网络地址 struct in_addr{ in_addr_t s_addr; //32位 ...
- 关于linux 原始套接字编程
关于linux 网络编程最权威的书是<<unix网络编程>>,但是看这本书时有些内容你可能理解的不是很深刻,或者说只知其然而不知其所以然,那么如果你想搞懂的话那么我建议你可以看 ...
- ZT Linux系统环境下的Socket编程详细解析
Linux系统环境下的Socket编程详细解析 来自: http://blog.163.com/jiangh_1982/blog/static/121950520082881457775/ 什么是So ...
- linux网络编程之socket编程(一)
今天开始,继续来学习linux编程,这次主要是研究下linux下的网络编程,而网络编程中最基本的需从socket编程开始,下面正式开始学习: 什么是socket: 在学习套接口之前,先要回顾一下Tcp ...
- Linux下的C Socket编程 -- 获取对方IP地址
Linux下的C Socket编程(二) 获取域名对应的IP地址 经过上面的讨论,如果我们想要连接到远程的服务器,我们需要知道对方的IP地址,系统函数gethostbyname便能够实现这个目的.它能 ...
- Linux下的C Socket编程 -- 简介与client端的处理
Linux下的C Socket编程(一) 介绍 Socket是进程间通信的方式之一,是进程间的通信.这里说的进程并不一定是在同一台机器上也有可能是通过网络连接的不同机器上.只要他们之间建立起了sock ...
- Linux网络编程——原始套接字实例:MAC 头部报文分析
通过<Linux网络编程——原始套接字编程>得知,我们可以通过原始套接字以及 recvfrom( ) 可以获取链路层的数据包,那我们接收的链路层数据包到底长什么样的呢? 链路层封包格式 M ...
随机推荐
- 搭建完整邮件系统(postfix+dovecot+clamAV+Spamassassin+amavisd-new)
============================ 相关软件: 1. 发送邮件 --- postfix 2. 身份认证 --- sasl2 3. 接收邮件 --- dovecot 4. 防病毒邮 ...
- Object-C变量作用域 -- 笔记
@interface Dog: NSObject { @public int age; @protected int ID; @Private float price; } @end 字段作用域解析: ...
- Python使用mechanize模拟浏览器
Python使用mechanize模拟浏览器 之前我使用自带的urllib2模拟浏览器去进行訪问网页等操作,非常多站点都会出错误,还会返回乱码.之后使用了 mechanize模拟浏览器,这些情况都没出 ...
- HUNNU--湖师大--11410--Eligibility
[I] Eligibility Regional Contest Director Osama Ismail knows that an individual cannot participate i ...
- js window.open()弹出窗口参数说明及居中设置
window.open()可以弹出一个新的窗口,并且通过参数控制窗口的各项属性. 最基本的弹出窗口代码 window.open('httP://codeo.cn/'); window.open()各参 ...
- 27个Jupyter Notebook使用技巧及快捷键(翻译版)
Jupyter Notebook Jupyter Notebook 以前被称为IPython notebook.Jupyter Notebook是一款能集各种分析包括代码.图片.注释.公式及自己画的图 ...
- 【移动开发】Android中WIFI开发总结(二)
搞了好几天终于有点眉目了,这里接着总结一下Android中WiFi的使用,在前面(http://smallwoniu.blog.51cto.com/3911954/1334951)我们已经简单了解了W ...
- Java基础知识强化96:Calendar类之获取任意年份的2月有多少天的案例
1. 分析: (1)键盘录入任意的年份 (2)设置日历对象的年月日 年:就是(1)输入的数据 月:是2(3月份) 日:是1 (3)把 ...
- hdu 2190
//hdu2190 水题 题意是给一个n*3的教室,用1*1,2*2的砖去铺满,有多少种铺法,一开始没发现这个规律,想了一下,应该是递归. #include <iostream> usi ...
- Dapper基本增删改查
说明: 1.在using语句块中不用dbConnection.Open(),因为Execute方法中会Open,并且在执行完成会Close. 2.在Ado.Net中要手动Open,在Using语句块中 ...