UNP Chapter 25 - 原始套接口(icmp的实现)

25.1. 概述

原始套接口提供以下三种TCP及UDP套接口一般不提供的功能。
1. 使用原始套接口可以读写ICMPv4,IGMPv4,ICMPv6分组。例如:Ping程序,就使用原始套接口发送ICMP回射请求,并接受ICMP回射应答。用于多播路由的守护进程:mrouted,同样利用原始套接口来发送和接收IGMPv4分组。上述功能同样允许使用ICMP或IGMP构造的应用程序完成作为用户进程处理,而不必在增加过多的内核编码。例如,路由器发现守护进程(在Solaris 2.x下名为in.rdisc,TCPv1的附录F说明如何获得公开可得版本的源代码)即以这种方式构造。它处理内核完全不知道的两个ICMP消息(路由器通告和路由器征求)。

2. 使用原始套接口可以读写特殊的IPv4数据报,内核不处理这些数据报的IPv4协议字段。

3. 利用原始套接口,好似用IP_HDRINCL套接口选项可以构造自己的IPv4头部。

25.2. 原始套接口创建

创建一个原始套接口涉及以下几步:

1. 当第二个参数是SOCK_RAW时,调用socket函数创建一个原始套接口。第三个参数(协议)一般不应为0,例如,为了创建一个IPv4原始套接口,我们可以这样写:

int sockfd;
sockfd = socket(AF_INET, SOCK_RAW, protocol);
其中protocol参数值为形如IPPROTO_xxx的常值,由<netinet/in.h>头文件定义,如IPPROTO_IGMP。但要注意,头文件里定义了一个协议名,如IPPROTO_EGP,并不意味这内核肯定支持它。

为了防止普通用户向网络写自己的IP数据报,只有超级用户才有权创建原始套接口。

2. 可以设置IP_HDRINCL套接口选项:
const int on = 1;
if ( setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on)) < 0 )
我们将在下一节介绍这个选项的效果。

3. 可以对原始套接口调用bind函数,但并不常用。该函数仅用来设置本地地址:对于一个原始套接口而言端口号没有什么意义。当进行输出的时候,bind设置在原始套接口上所发送的数据报中将用到的源IP地址(仅当IP_HDRINCL套接口选项未设置时),若不调用bind,则由内核将源IP地址设成外出接口的主IP地址。

4. 在原始套接口上可调用connect函数,但也不常用。connect函数仅设置目的地址,再重申一遍:端口号对原始套接口而言没有意义。对于输出而言,调用connect之后,由于目的地址已经指定,我们可以调用write或send,而不是sendto了。

25.3. 原始套接口输出

原始套接口的输出遵循以下规则:
1. 普通输出通过调用sendto或sendmsg并指定目的IP地址来完成。如果套接口已经连接,也可以调用write,writev或send

2. 如果IP_HDRINCL选项未设置,则内核写的数据起始地址指IP头部之后的第一个字节。因为这种情况下,内核将构造IP头部,并将它安在来自进程的数据之前。内核将IPv4头部的协议字段设置成用户在调用socket函数时所给的第三个参数。
3. 如果IP_HDRINCL选项已设置,则内核写的数据起始地址指IP头部的第一个字节。用户所提供的数据大小值必须包括头部的字节数,此时进程构造除了以下两项以外的整个IP头部:(a)IPv4标识字段可以设为0,要求内核设置该值。 (b)IPv4头部的校验和由内核来计算和存储。
4. 对于超出外出接口MTU的分组,内核将其分片。


IPv6的原始套接口与IPv4相比有以下几点不同:
1. IPv6原始套接口发送和接收的协议头部内,所有字段均使用网络字节序。
2. IPv6不存在类似于IPv4中IP_HDRINCL这样的套接口选项。使用IPv6原始套机口无法读写整个IPv6分组(包括扩展头部),不过通过套接口选项及辅助数据,我们几乎可得到IPv6头部的所有字段和所有扩展头部。但要读写整个IPv6数据报,仍必须使用数据链路访问。
3. IPv6原始套接口的校验和处理有差异
对于ICMPv6原始套接口而言,总是由内核来计算并在ICMPv6头部内存储其校验和。这一点与ICMPv4原始套接口有所不同,ICMPv4原始套接口由应用进程来计算并存储校验和。


25.4. 原始套接口输入

对于原始套接口输入,我们要回答的第一个问题是:接收到的哪些IP分组将传递给原始套接口。浙江遵循如下规则:

1. 接收到的TCP分组和UDP分组决不会传递给任何原始套接口,如果一个进程希望读取包括TCP或UDP分组的IP数据报,那么它们必须在数据链路层读入
2. 当内核处理完ICMP消息之后,绝大部分ICMP分组将传递给原始套接口。对源自Berkeley的实现而言,除了回射请求,时间戳请求和地址掩码请求将完全由内核处理以外,所有收到的ICMP分组都将传递给某个原始套接口。
3. 当内核处理完IGMP消息之后,所有IGMP分组都将传递给某个原始套接口。
4. 所有带有内核不能识别的协议字段的IP数据报都将传递给某个原始套机口。内核对这些分组唯一做的就是检验IP头部中的某些字段:IP版本,IPv4头部校验和,头部长度以及目的IP地址。
5. 如果数据报以片段形式到达,则该分组将在所有片段到达并重组后才传给原始套接口。
当内核准备好一个待传递的数据报之后,内核将对所有进程的原始套接口进行检查,以寻找所有匹配的套接口。每个匹配的套接口都将收到一个该IP数据报的拷贝。以下是对每个原始套接口所做的三个测试,只有当这三个测试都为真时,数据报才会递送给该套接口。


1. 如果在创建原始套接口时,所指定的protocol参数不为零(socket的第三个参数),则接收到的数据报的协议字段应与该值匹配。否则该数据报将不递送给该套接口。
2. 如果此原始套接口之上绑定了一个本地IP地址,那么接收到的数据报的目的IP地址应与该绑定地址相匹配,否则该数据报将不递送给该套接口。
3. 如果此原始套接口通过调用connect指定了一个对方IP地址,那么接收到的数据报的源IP地址应与该连接地址相匹配&#
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值