原始套接字的特点

原始套接字(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. 基于wax的lua IOS插件开发

    作者:朱克锋 邮箱:zhukefeng@iboxpay.com 转载请注明出处:http://blog.csdn.net/linux_zkf Objective-C的运行时支持新增类型和方法,但是由于 ...

  2. Spark Streaming与kafka整合实践之WordCount

    本次实践使用kafka console作为消息的生产者,Spark Streaming作为消息的消费者,具体实践代码如下 首先启动kafka server .\bin\windows\kafka-se ...

  3. 网页JavaScript4

    表单验证:一.非空验证:1.内容是不是空的.判断值的长度是不是0.length属性.压缩空格的函数. 2.内容是不是改变了. 二.对比验证:1.验证两个控件值的关系(相同,大小) 2.验证控件的值与某 ...

  4. nyoj 42

    #include <iostream> #include <stdio.h> #include <cstring> #include <algorithm&g ...

  5. jquery ajax 跨域处理

    今天使用JQuery Ajax 在本地电脑获取远程服务器数据的时候,发现使用$.ajax,$.getJSON,$.get这些都没有反应,后来再统一个网站下测试了一下,代码写得没有问题.后来想了想好想, ...

  6. 历史执行Sql语句性能分析 CPU资源占用时间分析

    SELECT     HIGHEST_CPU_QUERIES.PLAN_HANDLE,     HIGHEST_CPU_QUERIES.TOTAL_WORKER_TIME,     Q.DBID,   ...

  7. MySQL数据库my.cnf配置文件注释详解

    我们知道,在MySQL数据库安装完成后,要对my.cnf配置文件进行适当的修改才能充分利用MySQL数据库的功能.但是对于初学者来说,修改my.cnf配置文件似乎是一个比较难的过程.为了解决这个问题, ...

  8. WinAPI——模拟正弦波

    /*************************** * * 程序名称 : 模拟正弦波 * 作 者 : doodle777 * 版 本 : 1.1 * 日 期 : 2012-10-19 * 说 明 ...

  9. 关于char与varchar,varchar2的区别

    http://zhidao.baidu.com/question/220360696.html?qbl=relate_question_0&word=char%BA%CDvarchar2%B5 ...

  10. 使用WebFrom来模拟一些MVC的MODEL与View的数据交互功能

    MVC中有一点非常闪瞎人眼的功能就是,可以根据Model与View视图来直接将页面也数据模型进行绑定,那么我们在想客户端发送页面时不需要进行各种控件赋值,不需要操心怎么渲染,当客户提交表单给服务器时也 ...