原始套接字的特点

原始套接字(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. 手机站点开发及手机中图片加速显示img的Canvas方法

    随着手机开发越来越流行,手机开发的非常多框架也应运而生,比較好用的手机站点开发框架推荐例如以下: 1.zeptojs.里面封装了非常多手机特有方法,比如touch.js等等. 和jquery使用方法差 ...

  2. [置顶] Ruby,Scala和JavaScript中的函数式编程(一)

    函数式编程(英语:Functional programming)或者函数程序设计,又称泛函编程,是一种编程范型,它将电脑运算视为数学上的函数计算,并且避免使用程序状态以及易变对象.函数编程语言最重要的 ...

  3. Computer Vision and Machine Learning Competitions

    一.ImageNet Object Detection, Object Classification+Localization 二.COCO Image Captioning 三.LFW Face R ...

  4. MVC jsp+servlet+javabean 连接Mysql数据库測试demo

    本文介绍的是怎样使用MVC架构去实现jsp+servlet+javabean连接数据库 首先我们应该了解什么是MVC: MVC包含三个部分 : ①View:由各种JSP页面组成. ②Controlle ...

  5. MFC中全局变量的定义及使用

    用MFC制作的工程由很多文件构成,它不能象一般C++程序那样随意在类外定义全局变量,在这里要想定义能被工程内多个文件共享的全局变量和函数必须用一些特殊方法才行.实际上有多种方法可以实现,这里只介绍两种 ...

  6. 在Mac中安装.Net Core的开发环境

    在mac中部署dotnet core开发环境,我的MacOS版本号为OSX EI Capitan 10.11.6 1.安装brew homebrew官网推荐的安装命令如下: /usr/bin/ruby ...

  7. 自定义绘制View

    Paint(画笔)   Canvas(画布)         The Canvas class holds the "draw" calls.          To draw s ...

  8. sql 常用语法汇总

    Sql常用语法 SQL分类: DDL—数据定义语言(CREATE,ALTER,DROP,DECLARE) DML—数据操纵语言(SELECT,DELETE,UPDATE,INSERT) DCL—数据控 ...

  9. jquery 滚动加载

    <html xmlns="http://www.w3.org/1999/xhtml"><head runat="server"> < ...

  10. HTML基本概念

    什么是 HTML? HTML 是用来描述网页的一种语言. HTML 指的是超文本标记语言 (Hyper Text Markup Language) HTML 不是一种编程语言,而是一种标记语言 (ma ...