本章介绍IP分组的结构和主要的IP处理过程,包含输入,转发和输出。

下图显示了IP层常见的组织形式。

在之前的文章中。我们看到了网络接口怎样把到达的IP分组放到IP输入队列ipintrq中去,并怎样调用一个软件中断,例如以下图所看到的:

由于硬件中断的优先级比软件中断的要高,所以在发生一次软件中断之前。有的分组可能会被放到队列中。在软件中断中,ipintr

函数不断从ipintrq中移走和处理分组,直到对垒为空。在终于的目的地,IP把分组重装为数据包,并通过函数调用把该数据包直接

传给适当的运输层协议。假设分组没有到达最后的目的地,而且假设主机被配置成一个路由器,则IP把分组传给ip_forward。传输

协议和ip_forward把要输出的分组传给ip_output。由ip_output完毕ip首部,选择输出接口以及在必要时对分组分片。终于的分组

被传给合适的网络接口输出函数。

当产生差错时,IP丢弃该分组,并在某些条件下向分组的源站发出一个差错报文。这些报文是ICMP的一部分。

1.IP分组

我们把传输层协议交给IP的数据称为报文。典型的报文包括一个传输层首部和应用程序数据。下图所看到的的传输协议时UDP。IP在报

文的首部前加上它自己的首部形成一个数据包。假设在选定的网络中。数据报长度太大。IP就把数据包分裂成几个分片,每一个分片

包括它自己的IP首部和一段原来的数据报的数据。

下图显示了IP首部的结构。

下图包括了IP结构中的各成员的名字

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvVE9ERDkxMQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvVE9ERDkxMQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">

标准的IP首部长度是20个字节,所以ip_hl必须大于等于5.大于5表示IP选项紧跟在标准首部后。如ip_hl的最大值为15,同意最多

40各字节的选项。ip_hl是以4字节为单位计算的。

2.输入处理:ipintr函数

当接口把分组放到ipintrq上排队后。通过schednetisr调用一个软中断。当该软中断发生时,假设IP处理过程已经由schednetisr

调度。则内核调用ipintr。在调用ipintr之前。cpu的优先级被改成splnet。

ipintr是一个大函数,主要分4部分讨论:

1.对到达分组验证

2.选项处理及转发

3.分组重装

4.分用

当中选项处理和分用重装比較复杂,会在以后作为单独的章节进行说明。

2.1.验证

把分组从ipintrq中取出,验证他们的内容。损坏和有差错的分组被自己主动丢弃。
IP版本号:
假设网络接口没有指派IP地址,则必须丢掉全部的IP分组。
在ipintr訪问不论什么IP首部字段之前,它必须证实ip_v是4.
IP首部检验和:
ipintr把由in_cksum计算出来的检验和保存首部的ip_sum字段中。

一个未被破坏的首部应该具有0检验和。假设为非0,则该

分组被自己主动丢弃。

以下的章节详细介绍in_cksum。

3.字节顺序
NTOHS宏将IP首部中的全部16bit的值从网络字节序转换成主机字节序:分组长度(ip_len)、数据报标识符(ip_id)和分片
偏移(ip_off)。
4.分组长度
假设分组的逻辑长度(ip_len)比存储在mbuf中的数据量(m_pkthdr.len)大。而且有些字节被丢失了。就必须丢弃该分组,
假设mbuf比分组大,则去掉多余的字节。

丢失字节的一个常见原因是由于数据到达某个没有或仅仅有非常少缓存的串口设备,设备丢弃到达的字节。

多余字节可能产生,如在某个以太网设备上,当一个IP分组的大小比以太网要求的最小长度还要小时。发送该帧是加上的多余
字节就在这里被丢弃。

2.2.选项处理和转发

接下来调用ip_dooptions来处理IP选项,然后决定分组是否到达它最后的目的地。

假设分组没有到达最后目的地,则Net/3会

尝试转发该分组;假设分组到达最后目的地,就被交付给合适的传输层协议。

1.选项处理

ip_dooptions处理选项,假设ip_dooptions返回0,ipintr将继续处理该分组;否则ip_dooptions通过转发或丢弃分组完毕对该

分组的处理。ipintr能够处理输入队列中的下一个分组。

处理完选项后,ipintr通过把ip首部内的ip_dst与配置的全部本地接口的ip地址比較。以决定分组是否已经到达终于目的地。

ipintr

必须考虑与接口相关的几个广播地址、一个或多个单播地址以及随意多个多播地址。

2.转发

假设ip_dst与全部地址都不匹配,分组还没有达到终于目的地。

假设不准备转发,则丢弃分组,否则。ip_forward尝试把分组路由

到它的终于目的地。

2.3.分组重装和分用

ipintr函数最后进行分组的重组和分组。在后面的章节会具体进行介绍。

最后ipintr调用选定protosw结构中的pr_input函数来处理数据报包括的运输报文,当pr_input返回时,ipintr继续处理ipintrq中的

下一个分组。

3.IP头部检验:in_cksum函数

IP首部校验和的计算方法:
  1.把校验和字段清零。

  2.然后对每16位(2字节)进行二进制反码求和,反码求和的意思是先对每16位求和,再将得到的和转为反码。
  接下来具体描写叙述反码求和的步骤:看以下的代码
  基本算法:

SHORT checksum(USHORT* buffer, int size)
{
unsigned long cksum = 0;
while(size>1)
{
cksum += *buffer++;
size -= sizeof(USHORT);
}
if(size)
{
cksum += *(UCHAR*)buffer;
}
cksum = (cksum>>16) + (cksum&0xffff);
cksum += (cksum>>16);
return (USHORT)(~cksum);
}

  參数buffer是指向16位整数的指针,刚開始指向的是IP首部的起始地址,參数size是IP首部的大小。while循环是将IP首部的内容
以16位为单元加在一起。假设没有整除(即size还有余下的不足16位的部分)。则加上余下的部分,此时的cksum就是相加后的结
果。这个结果往往超出了16位。由于校验和是16位的,所以要将高16位和计算得到的cksum再加工。
  再加工第一步:cksum = (cksum>>16) + (cksum&0xffff); sum>>16是将高16位移位到低16位,sum&0xffff是取出低16位,相加得到新的cksum。
  再加工第二步:cksum += (cksum>>16); 第一步相加时非常可能会产生进位,因此要再次把进位移到低16位进行相加。  
  这样就加工好了,接下来就是取反。并强制转换为16位,这样就得到了终于的校验和。

  校验和计算出来了,接下来就是该怎样校验:
  接收方进行校验时,也是对每16位进行二进制反码求和。

接收方计算校验和时的首部与发送方计算校验和时的首部相比。多了一

个发送方计算出来的校验和。

因此,假设首部在传输过程中没有发生差错。那么接收方计算的结果应该为全一。由于接收方计算除

校验和以外的部分得到值是校验和的反码。再加多出来的校验和当然是全一了。

  最后对上述过程举个样例:
  IP头:
  45 00    00 31
  89 F5    00 00
  6E 06    00 00(校验字段)
  DE B7   45 5D       ->    222.183.69.93
  C0 A8   00 DC     ->    192.168.0.220
  计算:  
  4500 + 0031 +89F5 + 0000 + 6e06+ 0000 + DEB7 + 455D + C0A8 + 00DC =3 22C4
  0003 + 22C4 = 22C7
  ~22C7 = DD38      ->即为应填充的校验和
  当接受到IP数据包时,要检查IP头是否正确,则对IP头进行检验,方法同上:
  计算:
  4500 + 0031 +89F5 + 0000 + 6E06+ DD38 + DEB7 + 455D + C0A8 + 00DC =3 FFFC
  0003 + FFFC = FFFF
  得到的结果是全一。正确。

《TCP/IP具体解释卷2:实现》笔记--IP:网际协议的更多相关文章

  1. 《TCP/IP具体解释卷2:实现》笔记--IP多播

    D类IP地址(224.0.0.0到239.255.255.255)不识别互联网内的单个接口,但识别接口组,被称为多播组. 单个网络上的组成员利用IGMP协议在系统之间通信. 多播路由器用多播选录协议. ...

  2. 《TCP/IP具体解释卷2:实现》笔记--UDP:用户数据报协议

    用户数据报协议.即UDP,是一个面向数据报的简单运输层协议:进程的每次输出操作仅仅产生一个UDP数据报,从而发送 一个IP数据报. 进程通过创建一个Internet域内的SOCK_DGRAM类型的插口 ...

  3. 《TCP/IP具体解释卷2:实现》笔记--协议控制块

    协议层使用协议控制块(PCB)存放各UDP和TCP插口所要求的多个信息片.Internet协议维护Internet协议控制块 (internet protocol control block)和TCP ...

  4. 《TCP/IP具体解释卷2:实现》笔记--ICMP:Internet控制报文协议

    ICMP在IP系统间传递差错和管理报文,是不论什么IP实现必须和要求的组成部分.能够把ICMP分成两类:差错和查询.查询报文 是用一对请求和回答定义的.差错报文通常包括了引起错误的IP包的第一个分片的 ...

  5. TCP/IP具体解释--UDP数据报中的IP分片

    1.UDP首部 2.UDP分片 在第二章,讲链路层是,提到过以太网.刨除数据帧帧头.最多传输的长度为1500.也就是说,假设一个ip数据报,长度大于1500,则须要分片. 分片方法: 在ip头中3位标 ...

  6. 《TCP/IP具体解释卷2:实现》笔记--接口层

    接口层包含在本地网上发送和接收分组的硬件与软件. 我们用设备驱动程序来表示与硬件及网络接口通信的软件,网络接口是指在一个特定网络上硬件与设备驱动器之间的接口. Net/3接口层试图在网络协议和连接到一 ...

  7. 《TCP/IP具体解释卷2:实现》笔记--选路请求和选路消息

    内核的各种协议并不直接使用前面提供的函数来訪问选路树,而是调用几个函数:rtalloc和rtallocl是完毕路由表查询的两个 函数:rtrequest函数用于加入和删除路由表项:另外大多数接口在接口 ...

  8. 《TCP/IP具体解释卷2:实现》笔记--IP的分片和重装

    IP首部内有三个字段实现分片和重装:标识字段(ip_id).标志字段(ip_off的3个高位比特)和偏移字段(ip_off的13个低位 比特).标志字段由3个1bit标志组成.比特0是保留的必须为0, ...

  9. 《TCP/IP具体解释卷2:实现》笔记--域和协议

    Net/3组把协议关联到一个域,而且用一个协议族常量来标识每一个域.Net/3还通过全部的编址方法将协议分组. 在一个域中 的每一个协议使用同类地址.而且每种地址仅仅被一个域使用.作为结果,一个域能通 ...

随机推荐

  1. tomcat数据源配置DBCP

    原文件: https://www.cnblogs.com/sicd/p/4053780.html DBCP object created 日期 by the following code was ne ...

  2. nginx php mysql日志配置

    1.编辑mysql的配置文件my.cnf,这个文件通常在/etc目录下,但我用rpm装mysql的时候这个配置文件是在/usr目录下,但我测试过,无论是放在/etc目录下,还是放在     /usr目 ...

  3. python之uinttest单元测试框架

    unittest,是python中针对单元测试的一个测试框架 相当于python版的junit 简单举个例子: 如图,使用时,测试类需要继承单元测试TestCase这个类 必须要有setUp()和te ...

  4. JAVA随笔(三)

    私有是针对类的,而不是对象. static 函数,其实是类函数.之前一直不太理解每个类中的static main是什么意思,为什么main中不能直接调用非静态的变量:因为main是 类函数,不是属于某 ...

  5. Pg168-1

    1.Pg168-1 package org.hanqi.pn0120; public class Computer { private double neicundx=10; public doubl ...

  6. 淘宝IP地址库API地址

    淘宝IP地址库:http://ip.taobao.com/instructions.php   接口说明 1. 请求接口(GET): http://ip.taobao.com/service/getI ...

  7. 【AtCoder】ARC095 C-F题解

    我居然每道题都能想出来 虽然不是每道题都能写对,debug了很久/facepalm C - Many Medians 排序后前N/2个数的中位数时排序后第N/2 + 1的数 其余的中位数都是排序后第N ...

  8. doc元素select 取值

  9. Python 多线程 实例

    多线程实例 import threading import time def eat(): eatTime = time.time() for i in range(30): print('count ...

  10. @ControllerAdvice + @ExceptionHandler 处理 全部Controller层异常

    对于与数据库相关的 Spring MVC 项目,我们通常会把 事务 配置在 Service层,当数据库操作失败时让 Service 层抛出运行时异常,Spring 事物管理器就会进行回滚. 如此一来, ...