跟我一起学TCP/IP

Web、Mail、Ftp、DNS、Proxy、VPN、Samba、LDAP 等基础网络服务
723937936@qq.com
帖子: 51
注册时间: 2023-02-26 9:59
系统: ubuntu

跟我一起学TCP/IP

#1

帖子 723937936@qq.com » 2023-02-26 10:06

参考书:史蒂文斯先生的名著《TCP/IP Illustrated Volume 1: The Protocols》第一版
欢迎有兴趣的同学一起加入学习讨论
723937936@qq.com
帖子: 51
注册时间: 2023-02-26 9:59
系统: ubuntu

Re: 跟我一起学TCP/IP

#2

帖子 723937936@qq.com » 2023-02-26 10:56

第一章:介绍

关键概念强调:

主机(host):就理解成一台电脑
网络:将多台主机通过交换机(switch)连接在一起就组成一个网络
互联网(internet):将多个网络通过路由器连接在一起就组成了一个互联网,要区分internet和Internet,前者指将多个网络用路由器连接起来,后者是专有名词指英特网
ip地址:一个32位长的整数,32位分为两个部分,分别是netid和hostid,netid部分是由一个管理机构分配的(比如给某个公司分配一个B类网络),hostid是管理员(你自己)分配的;B类网络的hostid占16位,也就是有65536台主机,相当于一下子分配了65536个公网ip给这个公司
作者这里介绍的ip是说的是公网ip,如果是私有ip,那么netid和hostid都由管理员自己分配
netid标识一个网络,网络内的主机由hostid标识。参考书上图1.6的ip地址分类,像A类地址这样的,netid占7位,hostid占24位,那也就是说A类地址最多有128个网络,每个网络内可以有16777216台主机,这个网络太大了,所以就引入了另一个层次,在网络内再划分子网络,子网络也是网络,子网络之间也是通过路由器连接的,由路由器连接的所有子网络组成一个网络,网络与网络之间又通过路由器连接组成internet(首字母小写)
端口号:端口号用来标识进程。ip层负责将分组(packet)一跳一跳的传输到目的主机,分组到达目的主机后要交给哪个进程是由分组中携带的目的端口号信息决定的
udp数据报(udp datagram):udp层发送和接收的数据单元称为udp datagram
tcp段(tcp segment):tcp层发送和接收的数据单元称为tcp segment
ip数据报(ip datagram):ip层发送和接收的数据单元称为ip datagram
帧(frame):链路层发送和接收的数据单元称为frame
分组(packet):更一般的术语,基本等同于ip datagram,当一个路由器接收到一个ip datagram时,如果发现出口链路的mtu比较小,则需要执行分片操作,这样一个ip datagram可能变成2个或多个ip datagram,这些分片也称为分组,分组是一个一般的术语包含ip datagram和分片的ip datagram
723937936@qq.com
帖子: 51
注册时间: 2023-02-26 9:59
系统: ubuntu

Re: 跟我一起学TCP/IP

#3

帖子 723937936@qq.com » 2023-02-26 13:16

第二章:链路层(link layer)

这一章我们只关注以太网(ethernet)帧格式(见书上图2.1)

------------------------------------------------
| destination addr | source addr | type | data | crc |
------------------------------------------------
6 6 2 46-1500 4

以太网帧格式头(header)共14字节:包含目的mac地址、源mac地址、数据的类型
type字段:
0x0800 - ip datagram
0x0806 - arp request/reply
0x8035 - rarp request/reply

以太网帧对data的长度有要求:最低46字节,最高1500字节
也就是说最小的以太网帧长度为14+46=60字节,最大的以太网帧长度14+1500=1514字节(不考虑crc字段)

MTU(Maximum Transmission Unit):以太网帧可以携带的最大数据长度,即1500字节
Path MTU:链路层对传输数据的长度的限制是与具体链路层使用的技术有关,比如使用以太网技术组成的网络,那么在这个网络内部主机之间的链路上传输的数据单元最大为1500字节,但是如果使用其他的链路层技术组建网络,那这个网络的MTU则可能不同,也就说MTU是网络内部链路的属性。当使用路由器连接两个使用不同链路层技术组建的网络时,两个网络各自有自己的MTU限制,当两个网络里的主机相互通信时,通信链路的的MTU限制要使用最小的网络MTU(否则路由器在转发ip datagram时要执行分片操作),这个最小的MTU就是路径MTU。

发送主机要知道一条路线(route)的路径MTU,则需要一种发现技术,称为path MTU discovery mechanism
一种技术是发送主机在ip数据报头里指定DF(Dont Fragment)标志,然后发送较大的ip数据报,促使中间某个路由器需要执行分片却无法执行,进而丢弃该ip数据报,并向发送主机回送一个ICMP不可达消息。发送主机收到路由器回送的ICMP不可达消息后可以尝试减小发送的ip数据报长度,最终确定路径MTU的值。

或者反过来,发送主机先发送较小的ip数据报,然后不断增大ip数据报的大小直到收到ICMP不可达消息,则表示发送的ip数据报超过了路径MTU的大小。

主机需要知道路径MTU的目的是为了避免发送的ip数据报在中间的某个路由器被分片,因为分片被认为是不好的。

可以用netstat命令来获取接口的MTU

代码: 全选

$ netstat -i
Kernel Interface table
Iface      MTU    RX-OK RX-ERR RX-DRP RX-OVR    TX-OK TX-ERR TX-DRP TX-OVR 囗囗囗
enp0s3    1500  1397582      0      0 0        696458      0      0      0 BMRU
lo       65536   759726      0      0 0        759726      0      0      0 LRU
编程获取接口的MTU(参考UNP 17.7小节)

代码: 全选

#include <net/if.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/ioctl.h>
#include <sys/socket.h>

int main(int argc, char *argv[])
{
    if (argc != 2) {
        printf("usage: mtu interface\n");
        exit(1);
    }
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0) {
        perror("socket");
        exit(1);
    }
    struct ifreq req;
    bzero(&req, sizeof(req));
    strcpy(req.ifr_name, argv[1]);
    if (ioctl(sockfd, SIOCGIFMTU, &req) < 0) {
        perror("ioctl");
        exit(1);
    }
    printf("%ld\n", (long)req.ifr_mtu);
}

代码: 全选

$ cc mtu.cpp -o mtu
$ ./mtu enp0s3
1500
723937936@qq.com
帖子: 51
注册时间: 2023-02-26 9:59
系统: ubuntu

Re: 跟我一起学TCP/IP

#4

帖子 723937936@qq.com » 2023-02-26 20:13

观察以太网帧格式

UNP 29.4小节讲述链路层的访问接口,linux链路层访问接口的详细描述可参考man 7 packet

访问链路层的接口如下:

代码: 全选

int sockfd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
第一个参数指定协议族为AF_PACKET表示应用层直接访问链路层
第二个参数可以指定SOCK_RAW或SOCK_DGRAM
SOCK_RAW:通过sockfd接收的帧包含头
SOCK_DGRAM:通过sockfd接收的帧不包含头
第三个参数指定要接收的协议类型,协议类型定义在头文件<linux/if_ether.h>中
ETH_P_ALL:表示接收所有协议类型
ETH_P_IP:表示只接收ip协议类型
ETH_P_ARP:表示只接收arp协议类型

如果只想接收指定接口的帧,则需要调用bind函数指定接口索引,接口索引通过如下函数获得

代码: 全选

#include <net/if.h>
unsigned int if_nametoindex(const char *ifname);
完整的代码参见https://gitee.com/q723937936/tcpip/blob ... nkdump.cpp

执行示例:

代码: 全选

sudo ./linkdump enp0s3 | head -5
broadcast:60:FFFFFFFFFFFF2C6104BAFFFA080600010800060400012C6104BAFFFAC0A80001FFFFFFFFFFFFC0A80001000000000000000000000000000000000000
outgoing:238:909C4AC0BED0080027A2A34A0800451000E04D5E400040066AE5C0A8006EC0A800060016C2A8147D9ED4083A090B801801F5829700000101080A45FE
broadcast:60:FFFFFFFFFFFF2C6104BAFFFA080600010800060400012C6104BAFFFAC0A80001FFFFFFFFFFFFC0A800B3000000000000000000000000000000000000
host:66:080027A2A34A909C4AC0BED0080045480034000040004006B8B7C0A80006C0A8006EC2A80016083A090B147D9F80801007FD870C00000101080A2E1A
outgoing:502:909C4AC0BED0080027A2A34A0800451001E84D5F4000400669DCC0A8006EC0A800060016C2A8147D9F80083A090B801801F5839F00000101080A45FE
主要观察以太网帧格式的头,即前14个字节

上述输出的第一个帧是一个arp请求消息:
目的mac地址:FFFFFFFFFFFF 表示链路层的广播地址
源mac地址:2C6104BAFFFA
帧类型:0806 表示arp消息
帧长度为60字节,消息后面的一些00是填充字节,因为以太网帧最小长度是60字节
从上面的输出可以看出,应用接收到的以太网帧的尾部没有包含CRC字段(4个字节)

上述输出的第二个帧是一个ip数据报:
目的mac地址:909C4AC0BED0
源mac地址:080027A2A34A
帧类型:0800 表示ip数据报
帧长度为238字节,为了输出显得整洁,程序只打印了前60个字节
723937936@qq.com
帖子: 51
注册时间: 2023-02-26 9:59
系统: ubuntu

Re: 跟我一起学TCP/IP

#5

帖子 723937936@qq.com » 2023-02-27 23:13

第三章:IP协议

IP Header格式
Screen Shot 2023-02-27 at 10.01.38 PM.png
  • version:ip协议的版本,这里是ipv4,所以填4
  • header length:这个字段的单位是4字节,不带ip选项的ip数据报的ip header长度是20字节,所以这里填5,这个字段只有4位,最大值是15,所以ip header的最大长度是15*4=60字节,也就是说ip选项最大40字节
  • TOS:服务类型,这个字段先不管,填0
  • total length:这个字段填的是ip数据报的总长度
  • identification、flags、fragment offset:这三个字段跟ip数据报的分片有关,先不管
  • TTL:决定ip数据报可以经过的路由器数量,每经过一个路由器就会减1,当TTL为0时,路由器丢掉该ip数据报,traceroute命令利用这个特性来追踪ip数据报经过了哪些路由器
  • protocol:ip数据报承载的上层协议类型:tcp-6、udp-17、icmp-1、igmp-2
  • checksum:ip数据报头的校验和
  • source ip address:源ip地址
  • destination ip address:目的ip地址
  • options:ip选项,目前应该是没什么用,先不管
IP路由

IP路由机制:

当IP层收到一个ip数据报时,他搜索路由表,找到一个匹配条目,将该ip数据报发送给该条目指定的网关或直接发送给目的主机

IP层收到ip数据报有两种情况:
主机自己产生的ip数据报
从接口收到ip数据报(这种情况是主机被配置成一个路由器)

匹配路由条目是根据要发送的ip数据报的目的地址与路由表里的条目的Destination地址进行比较,Destination可能是一个主机地址也可能是一个网络地址
如果要发送的ip数据报的目的地址与某个条目的Destination地址完全匹配,则该条目指定一个主机的路由信息
如果要发送的ip数据报的目的地址的netid与某个条目的Destination地址完全匹配,则该条目指定一个网络的路由信息

下面根据具体路由表信息进行说明

路由表(routing table)查看命令

代码: 全选

$ netstat -rn
Kernel IP routing table
Destination     Gateway         Genmask         Flags   MSS Window  irtt Iface
0.0.0.0         192.168.0.1     0.0.0.0         UG        0 0          0 enp0s3
169.254.0.0     0.0.0.0         255.255.0.0     U         0 0          0 enp0s3
192.168.0.0     0.0.0.0         255.255.255.0   U         0 0          0 enp0s3
$
$ route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         192.168.0.1     0.0.0.0         UG    100    0        0 enp0s3
169.254.0.0     0.0.0.0         255.255.0.0     U     1000   0        0 enp0s3
192.168.0.0     0.0.0.0         255.255.255.0   U     100    0        0 enp0s3
上面两个命令都可以查看IP层维护的路由表信息

上面路由表的第一条目的Destination表示默认路由条目,Gateway为192.168.0.1是我的路由器的ip地址
上面路由表的第三条目的Destination表示一个网络(由Genmask 255.255.255.0表示),Gateway为0.0.0.0,表示我的主机连接在这个网络上,所以要发送ip数据报会直接发送给目的主机,不需要经过路由器
上面路由表的第二条目看起来和第三条目一样,Destination也表示一个网络,具体是什么网络,我也不清楚,先不管了
Iface字段表示要发送的ip数据报从哪个接口出去

下面添加两条主机路由条目

代码: 全选

$ sudo route add -host 192.168.0.139 dev enp0s3
$ sudo route add -host 110.242.68.66 gw 192.168.0.1 dev enp0s3
$ route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         192.168.0.1     0.0.0.0         UG    100    0        0 enp0s3
110.242.68.66   192.168.0.1     255.255.255.255 UGH   0      0        0 enp0s3
169.254.0.0     0.0.0.0         255.255.0.0     U     1000   0        0 enp0s3
192.168.0.0     0.0.0.0         255.255.255.0   U     100    0        0 enp0s3
192.168.0.139   0.0.0.0         255.255.255.255 UH    0      0        0 enp0s3
Flags里的H的表示Destination是主机地址,G表示Gateway是网关地址
Genmask为255.255.255.255也表示Destination是主机地址

编程接口

UNP 17.9小节介绍了使用ioctl来添加删除路由表的接口,但ioctl不支持读取路由表信息,route命令添加、删除路由表信息使用ioctl接口
UNP 18.4小节介绍了使用sysctl来读取路由表的接口,但Linux上sysctl接口不推荐使用
UNP 18章介绍的routing socket在Linux上不支持
Linux上可以通过/proc/net/route来获取路由表信息,netstat -r和route -n使用的方法
Linux添加、删除、获取路由表信息的推荐方法是使用netlink接口,该方法是新的软件包iproute2使用的方法,参考man 7 netlink和man 7 rtnetlink
723937936@qq.com
帖子: 51
注册时间: 2023-02-26 9:59
系统: ubuntu

Re: 跟我一起学TCP/IP

#6

帖子 723937936@qq.com » 2023-02-28 23:10

子网划分

回顾IP地址的分类
Screen Shot 2023-02-28 at 9.35.06 PM.png
从上图可以看出,A类和B类的IP地址给hostid分别分配了24位和16位,也就是说在A类网络内可以有2^24-2个主机,B类网络内可以有2^16-2个主机
很少有网络会连接这么多的主机,因此管理员一般会将hostid进一步划分成subnetid和hostid两部分。

上面说的网络内的主机数-2是因为hostid全零和全1是特殊的IP地址

划分子网后的网络拓扑结构如下:
Screen Shot 2023-02-28 at 10.37.24 PM.png
以B类网络举例:

B类网络的hostid有16位,我们给subnetid分8位,剩下8位给hostid
以书上的140.252这个B类网络为例,在140.252这个B类网络中有2^8-2个子网络,在每个子网络内有2^8-2个主机
因为划分子网络对于(父)网络是透明的,所以在(父)网络看来hostid(16位)全0和全1是无效的地址,所以子网络的个数也要减2

C类网络一般不划分子网络,或者说只包含一个子网络,也就是subnetid的位数为0,即只有2^0=1个子网络

对于连接(父)网络的路由器(后面称为外部路由器)来说,他的路由表包含许多路由条目(每个子网络对应一条),当外部路由器收到一个ip数据报,他检查目的ip属于哪个子网,然后将ip数据报转发到对应子网络的路由器,然后由子网络的路由器将ip数据报直接发送给目的主机


子网掩码(subnet mask)

每个网络接口除了需要配置一个ip地址,还需要配置一个子网掩码
从接口的ip地址可以知道主机属于哪个网络(根据ip的前几位可以确定ip属于哪类网络,确定网络的类后,可以根据相应的类,确定netid位数)
但是从ip地址无法确定主机属于哪个子网络,subnetid和hostid的边界需要用子网掩码来确定
子网掩码的全1部分对应ip地址的netid和subnetid,全0部分对应ip地址的hostid

同一个子网内的所有主机的子网掩码必须相同

从上面子网掩码的介绍,似乎子网掩码没什么作用,因为一个主机从接口收到ip数据报时,是直接比较ip数据报的目的ip地址与自己接口的ip地址,并不使用子网掩码,但是如果收到的ip数据报是一个面向子网的广播数据报(目的地址的hostid全1),那么接收主机就要比较netid和subnetid了,这时就需要使用子网掩码

在路由器的路由表条目里也有一个称为genmask的字段,该字段表示generality netmask的意思,因为外部中间路由器,他只将ip数据报转发到目的网络的外部路由器,这时候是没有子网概念的,所以不能叫subnet mask

广播IP地址
  • 全1的ip地址是limited broadcast地址,limited的意思是说目的地址为全1的ip数据报,路由器绝对不会转发
  • netid和subnetid不为0,hostid为1,面向子网的广播地址,这样的ip数据报会被广播到指定的子网的所有主机,路由器会转发
ifconfig命令

ifconfig命令可以用来查看接口信息和配置接口的ip地址和子网掩码

代码: 全选

// 配置ip地址和子网掩码
# ifconfig eth0 192.168.0.101 netmask 255.255.0.0
// 查看ip地址、子网掩码以及其他接口相关信息
$ ifconfig
ifconfig命令使用ioctl编程接口进行接口操作(UNP 17章有详细介绍)
723937936@qq.com
帖子: 51
注册时间: 2023-02-26 9:59
系统: ubuntu

Re: 跟我一起学TCP/IP

#7

帖子 723937936@qq.com » 2023-03-01 21:34

第四章:ARP协议

首先回顾一下子网掩码的作用,除了上一篇说的作用外,我发现设置接口的子网掩码,会自动更新路由表,
可能某个神秘的程序在监听接口状态(rtnetlink(7)接口可以监听接口状态变化事件),然后更新路由表

代码: 全选

$ route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         192.168.0.1     0.0.0.0         UG    100    0        0 enp0s3
169.254.0.0     0.0.0.0         255.255.0.0     U     1000   0        0 enp0s3
192.168.0.0     0.0.0.0         255.255.255.0   U     100    0        0 enp0s3
上面的最后一个条目描述的是主机所在的子网(192.168.0这个子网)的路线(route),也就说如果这台主机向同一个子网内的其他主机发送ip数据报,则ip路由模块会选择这条路线,并将IP数据报直接发送给目的主机(从enp0s3接口输出)

代码: 全选

// 我通过Ubuntu的网络接口配置界面配置子网掩码为255.255.255.128,后查看接口信息
$ ifconfig enp0s3
enp0s3: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.0.22  netmask 255.255.255.128  broadcast 192.168.0.127
        inet6 fe80::8253:abe9:8fab:a110  prefixlen 64  scopeid 0x20<link>
        ether 08:00:27:a2:a3:4a  txqueuelen 1000  (Ethernet)
        RX packets 1318603  bytes 300408996 (300.4 MB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 1195589  bytes 524945167 (524.9 MB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

// 查看路由表,发现Genmask一列变化了
$ route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         192.168.0.1     0.0.0.0         UG    100    0        0 enp0s3
169.254.0.0     0.0.0.0         255.255.0.0     U     1000   0        0 enp0s3
192.168.0.0     0.0.0.0         255.255.255.128 U     100    0        0 enp0s3
ip数据报在输出前进行路由选择时是将ip数据报的目的地址与Genmask进行&操作,然后与Destination字段进行比较,如果相同则通过Iface字段指定的接口输出,所以如果主机的子网掩码设置错误,可能会导致找不到匹配的路由条目,从而无法通信

一句话概括,主机接口的子网掩码,决定主机所在的子网,主机所在子网又决定了子网路由条目(就是上面路由表里的最后一条路由条目)


ARP协议帧格式
Screen Shot 2023-03-01 at 8.09.38 PM.png
ARP协议的作用是在局域网中,已知某台主机的IP地址查询那台主机的MAC地址
ARP协议是由IP层调用的,是IP层为了确定下一跳或下一个目的主机的硬件地址而发起的ARP查询请求,也就是说ARP请求发生在在IP路由之后,
如果IP在路由选择时无法在路由表里找到匹配的route,则不会发起ARP请求,因为此时,IP层不知道将IP数据报发送到哪里

字段说明:
前三个字段是以太网帧格式,共14字节,在这里frame type为0x0806表示ARP协议
hard type:这里是以太网,值为1
prot type:这里是ipv4,值为0x0800
hard size:以太网地址的长度,值为6,单位是字节
prot size:ipv4地址的长度,值为4,单位是字节
op:操作类型:1-arp request; 2-arp reply
sender Ethernet addr:发送者的以太网地址
sender IP addr:发送者的IP地址
target Ethernet addr:要查询的主机的硬件地址
target IP addr:要查询的主机的IP地址

在arp请求消息中,target Ethernet addr是未填充的,因为就是要查询目标主机的硬件地址
在arp应答消息中,target Ethernet addr被填充,并交换两个sender和target字段

纸上得来终觉浅,绝知此事要躬行,我们不适用tcpdump,而是通过编程的方式来加深记忆

使用packet socket抓取arp request/reply

代码地址:https://gitee.com/q723937936/tcpip/blob ... rpdump.cpp

起一个终端,使用arping 发送arp request

代码: 全选

$ arping 192.168.0.5
ARPING 192.168.0.5 from 192.168.0.110 enp0s3
Unicast reply from 192.168.0.5 [90:9C:4A:C0:BE:D0]  0.770ms
Unicast reply from 192.168.0.5 [90:9C:4A:C0:BE:D0]  1.130ms
Unicast reply from 192.168.0.5 [90:9C:4A:C0:BE:D0]  1.227ms
Unicast reply from 192.168.0.5 [90:9C:4A:C0:BE:D0]  1.328ms
再起一个终端,观察arp request/reply消息

代码: 全选

$ sudo ./arpdump | grep '192.168.0.5'
Request: who-has 192.168.0.5 tell 192.168.0.110
Reply: 192.168.0.5 is-at 90:9c:4a:c0:be:d0
Request: who-has 192.168.0.5 tell 192.168.0.110
Reply: 192.168.0.5 is-at 90:9c:4a:c0:be:d0
Request: who-has 192.168.0.5 tell 192.168.0.110
Reply: 192.168.0.5 is-at 90:9c:4a:c0:be:d0
Request: who-has 192.168.0.5 tell 192.168.0.110
Reply: 192.168.0.5 is-at 90:9c:4a:c0:be:d0
arping会不停的向指定主机发送arp请求,从arpdump的输出,可以确认目标主机返回的arp应答


Gratuitous ARP

免费ARP没有什么特殊之处,主要用途就是查询子网内有没有IP冲突,比如我想将我的主机ip设为为192.168.0.88,我可以先用
arping 192.168.0.88来查询这个IP是否被别的主机配置了,如果收到应答,则说明这个IP已经被使用了

arp命令

arp命令可以手工修改arp cache的内容

代码: 全选

// 查询arp cache
$ arp -n
Address                  HWtype  HWaddress           Flags Mask            Iface
192.168.0.2              ether   a4:45:19:6b:e4:d8   C                     enp0s3
192.168.0.5              ether   90:9c:4a:c0:be:d0   C                     enp0s3
192.168.0.1              ether   2c:61:04:ba:ff:fa   C                     enp0s3

// 删除arp条目
$ sudo arp -d 192.168.0.2
$ arp -n
Address                  HWtype  HWaddress           Flags Mask            Iface
192.168.0.5              ether   90:9c:4a:c0:be:d0   C                     enp0s3
192.168.0.1              ether   2c:61:04:ba:ff:fa   C                     enp0s3

// 添加arp条目
$ sudo arp -s 192.168.0.2 a4:45:19:6b:e4:d8
$ arp -n
Address                  HWtype  HWaddress           Flags Mask            Iface
192.168.0.2              ether   a4:45:19:6b:e4:d8   CM                    enp0s3
192.168.0.5              ether   90:9c:4a:c0:be:d0   C                     enp0s3
192.168.0.1              ether   2c:61:04:ba:ff:fa   C                     enp0s3
Flags Mask说明
C表示complete
M表示Permanent
默认手工添加的arp条目是永久有效的

arp这个命令是通过ioctl接口来添加删除arp条目的,UNP 17.8有详细介绍
723937936@qq.com
帖子: 51
注册时间: 2023-02-26 9:59
系统: ubuntu

Re: 跟我一起学TCP/IP

#8

帖子 723937936@qq.com » 2023-03-02 22:52

局域网主机扫描

arp-scan命令可以扫描局域网内的主机

代码: 全选

$ sudo arp-scan -I enp0s3 --localnet
Interface: enp0s3, datalink type: EN10MB (Ethernet)
Starting arp-scan 1.9 with 256 hosts (http://www.nta-monitor.com/tools/arp-scan/)
192.168.0.2	90:9c:4a:c0:be:d0	(Unknown)
192.168.0.1	2c:61:04:ba:ff:fa	(Unknown)
192.168.0.4	a4:45:19:6b:e4:d8	(Unknown)
192.168.0.110	08:00:27:a2:a3:4a	CADMUS COMPUTER SYSTEMS

5 packets received by filter, 0 packets dropped by kernel
Ending arp-scan 1.9: 256 hosts scanned in 2.656 seconds (96.39 hosts/sec). 4 responded
--localnet or -l 使用-I指定的接口的ip地址和子网掩码确定扫描的子网

也可以在命令行直接指定子网

代码: 全选

$ sudo arp-scan -I enp0s3 192.168.0.0/24
Interface: enp0s3, datalink type: EN10MB (Ethernet)
Starting arp-scan 1.9 with 256 hosts (http://www.nta-monitor.com/tools/arp-scan/)
192.168.0.2	90:9c:4a:c0:be:d0	(Unknown)
192.168.0.1	2c:61:04:ba:ff:fa	(Unknown)
192.168.0.4	a4:45:19:6b:e4:d8	(Unknown)
192.168.0.110	08:00:27:a2:a3:4a	CADMUS COMPUTER SYSTEMS

5 packets received by filter, 0 packets dropped by kernel
Ending arp-scan 1.9: 256 hosts scanned in 3.050 seconds (83.93 hosts/sec). 4 responded

arp防火墙

为防止arp攻击或者扫描,可以用arptables工具来过滤arp分组

查看接口的的mac地址

代码: 全选

$ ifconfig enp0s3
enp0s3: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.0.5  netmask 255.255.255.0  broadcast 192.168.0.255
        inet6 fe80::6d9c:c498:dc35:4ad5  prefixlen 64  scopeid 0x20<link>
        ether 08:00:27:ee:d5:16  txqueuelen 1000  (Ethernet)
        RX packets 36883  bytes 36477198 (36.4 MB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 22955  bytes 2436178 (2.4 MB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
配置防火墙

代码: 全选

$ sudo arptables -t filter -A OUTPUT --source-mac 08:00:27:ee:d5:16 -j DROP
上面这条命令是向filter表的OUTPUT链添加一条规则,该规则匹配arp请求和arp应答的源mac地址,如果匹配上就丢弃该分组
也就是说,禁止本机输出arp请求和arp应答

禁止arp请求后,如果本机的arp cache里没有缓存相关的arp条目,就无法通信
可以通过arp命令手动添加arp条目到arp cache里解决

arptables详细用法参考man arptables
723937936@qq.com
帖子: 51
注册时间: 2023-02-26 9:59
系统: ubuntu

Re: 跟我一起学TCP/IP

#9

帖子 723937936@qq.com » 2023-03-03 19:11

RARP协议

RARP(Reverse Address Resolution Protocol)逆地址解析协议
虽然这个协议的名字叫逆地址解析协议,但是他实际是用来获取主机IP地址的,我们知道在主机启动时(后)会配置网络接口的IP地址和子网掩码
一般有两种方法:
  • 从本地磁盘的配置文件里读取IP地址和子网掩码,然后调用ifconfig命令进行配置
  • 动态获取IP地址和子网掩码,然后调用ifconfig命令进行配置
RARP就是动态获取IP地址的方法
Screen Shot 2023-03-01 at 8.09.38 PM.png
RARP协议格式与ARP完全一样,只是下面两个字段的值与ARP协议不同
frame type:0x8035
op:3-request;4-reply

和ARP请求消息一样,RARP请求消息也是在子网内广播,RARP应答消息是单播
RARP请求消息就好像在说:hi everbody, i'm xx:xx:xx:xx:xx:xx, please allocate a ip for me
当子网中某个安装了RARP server程序的主机收到这条请求消息后,就从本地配置文件读取xx:xx:xx:xx:xx:xx对应的ip地址(配置文件中预先配置了子网内所有主机的mac地址和IP地址映射),并发送应答消息

RARP协议已经被淘汰了,他的主要缺点是:他只能获取接口的IP地址,无法获取接口的子网掩码
解决办法是通过ICMP类型17(地址掩码)获取接口的子网掩码,然而ICMP的地址掩码消息也被淘汰了
取而代之的是bootp协议(后面章节有介绍),然而bootp协议也被淘汰了,取而代之的是dhcp协议

网络接口的两个属性:IP地址和子网掩码的获取采用两个不同层次的协议,可能是历史原因(一个可能的原因是子网划分是后加入的特性)

RARP server其实并不复杂,在Linux上可以通过packet socket来实现
723937936@qq.com
帖子: 51
注册时间: 2023-02-26 9:59
系统: ubuntu

Re: 跟我一起学TCP/IP

#10

帖子 723937936@qq.com » 2023-03-04 23:15

第六章:ICMP协议

ICMP(Internet Control Message Protocol)协议的坐骑是IP协议,他主要用来报告错误,主要服务于IP、UDP、TCP模块

ICMP协议格式
Screen Shot 2023-03-04 at 8.16.32 PM.png
[*]type:消息类型,共15种
[*]code:有的消息类型有多种code,进一步区分不同的错误
[*]checksum:完整的icmp消息的校验和
[*]消息内容不固定,与type和code有关

ICMP消息类型
Screen Shot 2023-03-04 at 8.22.11 PM.png
ICMP消息分为两大类:
  1. query消息
  • error消息
从上面的图中可以看出:
query消息有:
[*]0、8:主要是ping程序使用(下一章学习)
[*]9、10:router advertisement/solicitation 获取gateway地址(第9章学习)
[*]13、14:timestamp request/reply 该消息最初的用途是用来校准系统时间,已被NTP协议取代(向一台macos主机发送该请求,无法收到应答)
[*]15、16:information request/reply 已废弃
[*]17、18:address mask request/reply 该消息最初的用途是向子网内的其他主机查询子网掩码,已废弃(向一台Linux主机发送该请求,无法收到应答)

注意:query消息的code都为0

error消息有:
[*]3:destination unreachable
[*]4:source quench
[*]5:redirect
[*]11:time exceeded
[*]12:parameter problem

注意:除了类型4,其他error消息的code都有多个值

这些错误消息在后面遇到时再进行学习

作者在本章介绍了三个消息,两个查询消息,一个错误消息
[*]17、18:address mask request/reply 已经废弃,不管了
[*]13、14:timestamp request/reply
[*]3[3]:port unreachable

ICMP查询消息示例:以timestamp request/reply消息为例
Screen Shot 2023-03-04 at 9.03.54 PM.png
identifier字段:请求一般设置为进程id,应答会返回该字段,以便接受者匹配请求和应答
sequence number字段:消息序号,对于该消息一般设为0
originate timestamp:由发送者填充,发送请求时填充距离midnight的UTC时间戳,单位ms
receive timestamp:由接受者填充,收到请求时填充距离midnight的UTC时间戳,单位ms
transmit timestamp:由接受者填充,接受者发送应答时填充(该值通常与receive timestamp相同)

示例:

在一个终端运行icmptime(192.168.0.1是我的路由器ip地址)

代码: 全选

$ sudo ./icmptime 192.168.0.1
orig = 51748027, recv = 51747219, xmit = 51747219, rtt = 3 ms
difference = -808 ms
同时在另一个终端运行icmpdump

代码: 全选

$ sudo ./icmpdump -x
IP 192.168.0.6 > 192.168.0.1: ICMP time stamp query, id 44883, seq 0, length 20
	0x0000:  2c61 04ba fffa 0800 27c2 f377 0800 4500
	0x0010:  0028 fd2b 4000 4001 bc51 c0a8 0006 c0a8
	0x0020:  0001 0d00 a3db af53 0000 0315 9cbb 0000
	0x0030:  0000 0000 0000
IP 192.168.0.1 > 192.168.0.6: ICMP time stamp reply, id 44883, seq 0, org 14:22:28.027, recv 14:22:27.219, xmit 14:22:27.219, length 20
	0x0000:  0800 27c2 f377 2c61 04ba fffa 0800 4500
	0x0010:  0028 f498 0000 4001 04e5 c0a8 0001 c0a8
	0x0020:  0006 0e00 698a af53 0000 0315 9cbb 0315
	0x0030:  9993 0315 9993 0000 0000 0000
从上面的输出看:
recv和xmit的值相同,通常如此
rtt为3ms,因为在一个子网内,所以很快
difference相差-808ms,说明路由器的时间与我的主机时间稍有差别

icmptime是使用raw socket,发送icmp消息,UNP 第28章有详细介绍,或参考man 7 raw
代码地址:https://gitee.com/q723937936/tcpip/blob ... mptime.cpp
icmpdump是使用packet socket,参考man 7 packet
代码地址:https://gitee.com/q723937936/tcpip/blob ... mpdump.cpp

ICMP错误消息示例:以port unreachable为例
Screen Shot 2023-03-04 at 10.32.20 PM.png
所有的ICMP错误消息的消息格式都一样:
header长度固定8个字节,数据部分是触发该ICMP错误消息的那个IP数据报的IP header+8个字节
其中IP header部分包含IP选项,那8个字节可能是tcp/udp/icmp消息的前8个字节,这8个字节里包含关键信息
注意icmp查询消息也是可能触发icmp错误消息的,因为icmp消息的坐骑也是IP协议,由IP数据报承载
但是icmp错误消息绝对不会触发icmp错误消息,避免无限循环

udp client向udp server发送udp数据报时,如果udp server没有启动,也就是在那个主机上没有监听相应端口,那么那个主机在收到udp数据报后因为不知道交给哪个进程处理,所以会生成一个ICMP port unreachable错误消息,并回送给udp client
tcp端口不可达不会触发ICMP错误消息,因为tcp是面向连接的,会回送一个带RST标志的segment

示例:

在一个终端上执行udpcli

代码: 全选

$ ./udpcli 192.168.0.1 8888
hello             // 输入hello,回车发送udp数据报

在另一个终端上执行icmpdump

代码: 全选

$ sudo ./icmpdump -x
IP 192.168.0.1 > 192.168.0.6: ICMP 192.168.0.1 udp port 8888 unreachable, length 41
	0x0000:  0800 27c2 f377 2c61 04ba fffa 0800 45c0
	0x0010:  003d d53d 0000 4001 236b c0a8 0001 c0a8
	0x0020:  0006 0303 7e73 0000 0000 4500 0021 04db
	0x0030:  4000 4011 b499 c0a8 0006 c0a8 0001 a819
	0x0040:  22b8 000d 6fd8 6865 6c6c 6f84
可以看到192.168.0.1回送了一个ICMP port unreachable消息,udp数据报的目的端口8888也被打印了出来(8888就是udp数据报头8个字节里的关键信息)

另外,udpcli调用的sendto函数并没有返回错误,这是因为在调用sendto时是不知道会不会发生错误的,只有等到udp数据报到达目的主机且触发回送ICMP port unreachable消息,然后udpcli所在主机收到该ICMP port unreachable后,才知道发生错误,但是默认并不会报告给调用进程

有两种方法可以让内核(IP模块)将ICMP port unreachable报告给进程
1. 调用connect连接
2. 设置SO_BSDCOMPAT套接字选项为0

udpcli测试程序有一个-c选项,会调用connect进行连接

代码: 全选

./udpcli -c 192.168.0.1 8888
hello

sendto: Connection refused
当socket描述符有pending error时,只有进程再次调用sendto或recvfrom时,才会返回错误

UNP 8.9小节对ICMP port unreachable错误也有说明
723937936@qq.com
帖子: 51
注册时间: 2023-02-26 9:59
系统: ubuntu

Re: 跟我一起学TCP/IP

#11

帖子 723937936@qq.com » 2023-03-05 21:42

第七章:Ping程序

ping程序用于测试目的主机的可达性,他发送ICMP echo request消息,并接收ICMP echo reply消息

ICMP echo消息格式
Screen Shot 2023-03-05 at 9.09.39 PM.png
前面的8个字节与ICMP timestamp消息完全相同
后面的可选数据内容完全由应用程序决定,ping程序发送的数据长度为56字节,实际只使用8个字节(发送时刻的时间:struct timeval结构)
ping程序在当收到echo reply时用当前时间减去发送时间来计算rtt
ping程序还打印echo reply消息的ttl,从ttl可以得到echo reply消息经过的路由器数量(64-ttl)

UNP 28.5小节详细介绍了ping程序的实现

因为防火墙的存在,在现在的广域网中使用ping程序测试主机可达性大概率已经不可行

比如,如果只是单纯的禁ping,可以用下面的命令完成

代码: 全选

$ sudo iptables -A INPUT -p icmp -m icmp --icmp-type echo-request -j DROP
上面的命令是在filter表的INPUT链中添加一条规则:该规则匹配icmp的echo-request类型消息,如果匹配上就丢弃该ip数据报

清除filter表里的所有规则

代码: 全选

$ sudo iptables -F
本章介绍的两个ip选项,目前已无用处,不管了
723937936@qq.com
帖子: 51
注册时间: 2023-02-26 9:59
系统: ubuntu

Re: 跟我一起学TCP/IP

#12

帖子 723937936@qq.com » 2023-03-06 22:36

traceroute程序

traceroute程序是用来追踪一个ip数据报经过了哪些路由器

例如:

代码: 全选

$ traceroute -n 222.190.59.9
traceroute to 222.190.59.9 (222.190.59.9), 30 hops max, 60 byte packets
 1  192.168.0.1  10.235 ms  10.193 ms  10.320 ms
 2  100.99.128.1  16.782 ms  16.771 ms  16.761 ms
 3  221.226.71.49  15.256 ms  15.233 ms  15.151 ms
 4  222.190.59.9  17.345 ms  17.334 ms  17.484 ms
他的原理是:
第一次发送的ip数据报的ttl设置为1,此时第一个路由器会丢掉该ip数据报,并回送一个ICMP time exceeded in-transit消息,因为回送的ICMP消息携带该路由器的ip地址,所以traceroute获得了第一个路由器的ip地址
第二次发送的ip数据报的ttl设置为2,此时第二个路由器会丢掉该ip数据报,并回送一个ICMP time exceeded in-transit消息,...
以此类推,直到该ip数据报到达最终目的主机,因为该ip数据报携带的是udp数据报,并且该udp数据报的目的端口是一个较大的端口(目的主机大概率没有监听该端口),所以目的主机会回送一个ICMP port unreachable消息,此时traceroute程序便得知该ip数据报已经到达目的主机

ICMP time exceeded消息格式
Screen Shot 2023-03-06 at 10.09.57 PM.png
type:11-time exceeded
code:0-time exceeded in transit; 1-fragment reassembly time exceeded

在router接收到的ip数据报的ttl为0或1时,router会丢弃该ip数据报,并回送ICMP time exceeded in-transit消息给源主机

UNP 28.6小节有traceroute的实现

开发一个udpclittl客户端,观察ICMP time exceeded in-transit错误消息

先开一个终端观察icmp消息

代码: 全选

$ sudo ./icmpdump
IP 192.168.0.1 > 192.168.0.6: ICMP time exceeded in-transit, length 37
IP 100.99.128.1 > 192.168.0.6: ICMP time exceeded in-transit, length 36
IP 218.2.127.213 > 192.168.0.6: ICMP 218.2.127.213 udp port 34567 unreachable, length 36
在另一个终端运行udpclittl,发送udp数据报

代码: 全选

$ ./udpclittl
usage: udpclittl host port ttl
$ ./udpclittl 218.2.127.213 34567 1
$ ./udpclittl 218.2.127.213 34567 2
$ ./udpclittl 218.2.127.213 34567 2
$ ./udpclittl 218.2.127.213 34567 3
从上面测试的例子看,并不是每次都能收到ICMP time exceeded in-transit消息,因为每次路线可能不一样,中间的某个路由器可能并不回送该ICMP消息

测试代码git仓库:https://gitee.com/q723937936/tcpip.git

书上介绍的两个source route IP选项,我认为没什么用处
723937936@qq.com
帖子: 51
注册时间: 2023-02-26 9:59
系统: ubuntu

Re: 跟我一起学TCP/IP

#13

帖子 723937936@qq.com » 2023-03-07 23:50

第九章:IP路由
Screen Shot 2023-03-07 at 9.23.02 PM.png
本章主要关注内核维护的一个数据结构:路由表(routing table)

对路由表的操作分为两类:
1. 读操作(上图中的IP output功能模块、netstat命令)
2. 写操作(上图中的routing daemon、route command、ICMP redirects)

routing mechanism:IP输出功能模块搜索路由表来决定将ip数据报发送到哪里(下一跳路由器or目的主机)
routing policy:如何更新路由表的规则(routing daemon实现路由策略)

IP输出功能模块执行路由机制,按顺序执行下列步骤,一旦找到匹配路线就立刻停止搜索
1. 搜所匹配的主机地址路线
2. 搜索匹配的网络地址路线
3. 搜索默认路线

主机路由表示例:

代码: 全选

netstat -rn
Kernel IP routing table
Destination     Gateway         Genmask         Flags   MSS Window  irtt Iface
0.0.0.0         192.168.0.1     0.0.0.0         UG        0 0          0 enp0s3
169.254.0.0     0.0.0.0         255.255.0.0     U         0 0          0 enp0s3
192.168.0.0     0.0.0.0         255.255.255.0   U         0 0          0 enp0s3
字段说明:
Destination: 指示该route通往一个主机还是一个网络(由H标志确定)特殊值0.0.0.0表示默认route
Gateway:如果设置了标志G,则表示下一跳的路由器ip地址,否则为0.0.0.0表示无意义
Genmask:当标志H设置时值为255.255.255.255,当H未设置时表示目的网络的netmask,特殊值0.0.0.0表示默认route
Iface:指定该路线的外出接口
Flags:
U (route is up)
H (target is a host) Destination表示一个主机的ip地址,如果H未设置,则Destination表示一个网络地址
G (use gateway) Gateway表示路由器的ip地址
D (dynamically installed by daemon or redirect)
M (modified from routing daemon or redirect)

直接路线(direct route):未设置标志G,表示目的主机与本主机在同一个(子)网络内(通过交换机相连)
间接路线(indirect route):设置标志G,表示目的主机与本主机不在同一个(子)网络内,需要通过路由器转发ip数据报

通过交换机连接的两台主机之间通信,ip数据报是直接发送到目的主机(链路层的目的mac地址为目的主机的mac的地址)
而需要路由器接力的两个主机通信,ip数据报先发送给路由器(链路层的目的mac地址为路由器的mac的地址)

强调:
标志G是区分直接路线和间接路线
标志H指示Destination列是一个主机地址还是一个网络地址
如果未指定标志G,也未指定标志H的路线,他的Genmask的值默认与接口的子网掩码相同(当修改接口子网掩码时,对应的接口所在子网的route的Genmask同步发生变化)

routing table初始化

代码: 全选

192.168.0.0     0.0.0.0         255.255.255.0   U         0 0          0 enp0s3
上面这条route是当配置enp0s3接口的ip地址和子网掩码时自动(who?)初始化的

找不到匹配的route的情况

当发送本地产生的ip数据报时,如果没有找到匹配的route,则会返回ENETUNREACH或EHOSTUNREACH

示例:

代码: 全选

$ netstat -rn
Kernel IP routing table
Destination     Gateway         Genmask         Flags   MSS Window  irtt Iface
0.0.0.0         192.168.0.1     0.0.0.0         UG        0 0          0 enp0s3
169.254.0.0     0.0.0.0         255.255.0.0     U         0 0          0 enp0s3
192.168.0.0     0.0.0.0         255.255.255.0   U         0 0          0 enp0s3
$ sudo route del default
$ netstat -rn
Kernel IP routing table
Destination     Gateway         Genmask         Flags   MSS Window  irtt Iface
169.254.0.0     0.0.0.0         255.255.0.0     U         0 0          0 enp0s3
192.168.0.0     0.0.0.0         255.255.255.0   U         0 0          0 enp0s3
$ ping 192.168.1.2
connect: Network is unreachable
$ errno --list | grep unreach
ENETUNREACH 101 Network is unreachable
UNP 4.3小节有如下说明
Notice that ENETUNREACH is not listed in Figure A.15, even when the error indicates that the destination network is unreachable. Network unreachables are considered obsolete, and applications should just treat ENETUNREACH and EHOSTUNREACH as the same error.
当路由器转发ip数据报时,如果没有找到匹配的route,则会向源主机回送ICMP host unreachable消息

首先将我的Ubuntu18.04系统配置成路由器

代码: 全选

# echo 1 > /proc/sys/net/ipv4/ip_forward
在我Ubuntu18.04(已经配置为路由器)上测试,找不到route默认并不会回送ICMP host unreachable消息
只有明确添加一条reject route才会回送ICMP host unreachable消息

代码: 全选

$ sudo route add -net 192.168.2.0 netmask 255.255.255.0 metric 1024 reject
$ netstat -rn
Kernel IP routing table
Destination     Gateway         Genmask         Flags   MSS Window  irtt Iface
169.254.0.0     0.0.0.0         255.255.0.0     U         0 0          0 enp0s3
192.168.0.0     0.0.0.0         255.255.255.0   U         0 0          0 enp0s3
192.168.2.0     -               255.255.255.0   !         - -          - -
在Ubuntu18.04上开一个终端窗口执行icmpdump

代码: 全选

$ sudo ./icmpdump
IP 192.168.0.6 > 192.168.0.3: ICMP host 192.168.2.2 unreachable, length 38
IP 192.168.0.6 > 192.168.0.3: ICMP host 192.168.2.2 unreachable, length 38
再另一台主机(macos,已配置默认网关未Ubuntu18.04的ip地址)发送ip数据报

代码: 全选

$ nc -u 192.168.2.2 8888
a
$ nc -u 192.168.2.2 8888
a
此时可观察到ICMP host unreachable消息
723937936@qq.com
帖子: 51
注册时间: 2023-02-26 9:59
系统: ubuntu

Re: 跟我一起学TCP/IP

#14

帖子 723937936@qq.com » 2023-03-08 21:17

ICMP Redirect Errors

ICMP Redirect消息的目的是帮助子网内的主机更新路由表

实验子网结构:
Screen Shot 2023-03-08 at 9.23.05 PM.png
上图中:
host(192.168.0.110)的默认网关设置为router1(192.168.0.6)
router1的默认网关设置为router2(192.168.0.1)


在host上执行ping baidu.com

代码: 全选

$ ping baidu.com
PING baidu.com (110.242.68.66) 56(84) bytes of data.
From _gateway (192.168.0.6): icmp_seq=1 Redirect Host(New nexthop: 192.168.0.1 (192.168.0.1))
64 bytes from 110.242.68.66 (110.242.68.66): icmp_seq=1 ttl=51 time=27.2 ms
64 bytes from 110.242.68.66 (110.242.68.66): icmp_seq=1 ttl=50 time=27.4 ms (DUP!)
64 bytes from 110.242.68.66 (110.242.68.66): icmp_seq=2 ttl=51 time=33.6 ms
64 bytes from 110.242.68.66 (110.242.68.66): icmp_seq=2 ttl=50 time=33.9 ms (DUP!)
64 bytes from 110.242.68.66 (110.242.68.66): icmp_seq=3 ttl=51 time=33.5 ms
64 bytes from 110.242.68.66 (110.242.68.66): icmp_seq=3 ttl=50 time=33.7 ms (DUP!)
^C
--- baidu.com ping statistics ---
3 packets transmitted, 3 received, +3 duplicates, 0% packet loss, time 2144ms
rtt min/avg/max/mdev = 27.220/31.615/33.929/3.023 ms
在router1上执行icmpdump

代码: 全选

$ sudo ./icmpdump
IP 192.168.0.110 > 110.242.68.66: ICMP echo query, id 3855, seq 1, length 64
IP 192.168.0.6 > 192.168.0.110: ICMP redirect 110.242.68.66 to host 192.168.0.1, length 92
IP 192.168.0.110 > 110.242.68.66: ICMP echo query, id 3855, seq 1, length 64
IP 192.168.0.6 > 114.114.114.114: ICMP redirect 192.168.0.110 to host 192.168.0.110, length 155
IP 110.242.68.66 > 192.168.0.110: ICMP echo reply, id 3855, seq 1, length 64
IP 192.168.0.6 > 110.242.68.66: ICMP redirect 192.168.0.110 to host 192.168.0.110, length 92
IP 110.242.68.66 > 192.168.0.110: ICMP echo reply, id 3855, seq 1, length 64
IP 192.168.0.6 > 114.114.114.114: ICMP redirect 192.168.0.110 to host 192.168.0.110, length 169
IP 110.242.68.66 > 192.168.0.110: ICMP echo reply, id 3855, seq 2, length 64
IP 192.168.0.6 > 110.242.68.66: ICMP redirect 192.168.0.110 to host 192.168.0.110, length 92
IP 110.242.68.66 > 192.168.0.110: ICMP echo reply, id 3855, seq 2, length 64
IP 110.242.68.66 > 192.168.0.110: ICMP echo reply, id 3855, seq 3, length 64
IP 192.168.0.6 > 110.242.68.66: ICMP redirect 192.168.0.110 to host 192.168.0.110, length 92
IP 110.242.68.66 > 192.168.0.110: ICMP echo reply, id 3855, seq 3, length 64
在icmpdump的输出的第二行显示router给host发送一条ICMP redirect消息,指示目的地址110.242.68.66应该使用192.168.0.1作为路由器

在host上查看路由表

代码: 全选

netstat -rn
Kernel IP routing table
Destination     Gateway         Genmask         Flags   MSS Window  irtt Iface
0.0.0.0         192.168.0.6     0.0.0.0         UG        0 0          0 enp0s3
169.254.0.0     0.0.0.0         255.255.0.0     U         0 0          0 enp0s3
192.168.0.0     0.0.0.0         255.255.255.0   U         0 0          0 enp0s3
发现路由表并没有更新(Linux默认忽略ICMP redirect的建议?)

ICMP router advertisement/solicitation消息

ICMP router advertisement/solicitation消息最初的用途是用来获取主机的默认网关的,但是从来没有广泛部署过,已被DHCP取代
723937936@qq.com
帖子: 51
注册时间: 2023-02-26 9:59
系统: ubuntu

Re: 跟我一起学TCP/IP

#15

帖子 723937936@qq.com » 2023-03-10 21:50

第十章:动态路由协议(Dynamic Routing Protocol)

对于小型网络可以通过route命令手工维护网络内的各个路由器的路由表,但是对于中大型网络,手工维护很多路由器的路由表既繁琐又易错
本章要学习的动态路由协议就是为了动态更新路由器的路由表

关键概念说明:

路由协议(Routing Protocol):用于路由器间相互通信的协议
路由守护进程(Routing Daemon):运行在路由器上,实现Routing Protocol
路由策略(Routing Policy):不同的路由协议使用不同的策略,这个策略决定哪些route会被添加到路由表
自治系统(Autonomous System):一个公司、大学内部的网络就是一个自治系统,Internet就是有许许多多自治系统连接在一起的
IGP(Interior Gateway Protocol):内部网关协议是指自治系统内部路由器之间通信的路由协议
EGP(Exterior Gateway Protocol):外部网关协议是指不同自治系统的路由器之间通信的路由协议


本章要学习的IGP有两个:
RIP:Routing Information Protocol
OSPF:Open Shortest Path First Protocol

本章要学习的EGP:
BGP:Border Gateway Protocol

Routing Information Protocol

RIP协议的坐骑是UDP协议,服务端口号 520
Screen Shot 2023-03-10 at 7.44.26 PM.png
Screen Shot 2023-03-10 at 7.44.43 PM.png
字段说明:
command:1-request;2-reply
version:对于RIPv1,该字段填1;对于RIPv2,该字段填2
address family:填2或0(address family为0时有特殊含义,下文有说明)
32-bit IP address:目的主机或网络的IP地址(netstat -rn命令输出的第一列)
metric:度量值表示要经过的路由器的跳数(hop count)

路由器之间的交互说明

对于发送方:

1. 当Routing daemon启动时,他向每个接口发送一个广播请求,该请求消息如下值:
command=1
address family=0
metric=16
该消息表示请求完整的路由表(也就是说该子网内其他路由器收到该请求,会回送(单播)自己的完整的路由表)

2. 定时(每30秒)广播自己的完整路由表,注意这个广播消息的command=2,因为这不是一个请求,而是通告,不需要接受者回送应答
3. 当自己的路由表里某个route的metric发生变化时,广播发生变化的route

对于接收方:

1. 如果收到的是请求,分如下两种情况:
①如果请求的是完整的路由表,则回送(单播)完整的路由表给请求者
②如果请求的不是完整的路由表,则根据自己的路由表内容设置相应的metric(16表示不可达)

上面的①就好比请求者说:邻居你好,把你们家的路由表发给我
上面的②就好比请求者说:邻居你好,你知道去往某地址的ip数据报怎么转发吗?(只需回答知道还是不知道,对应的地址的metric填16表示不知道)

2. 如果收到的是应答,则根据应答内容更新自己的路由表

组网分析
Screen Shot 2023-03-10 at 8.46.22 PM.png
上面图示是一个简单自治系统

假如router1上运行的routing daemon启动后向每个接口广播一个请求完整路由表的RIP消息,那么router2就会回送(单播)一个包含他的完整路由表的应答,此时router1收到应答,则根据应答内容更新自己的路由表(在自己的路由表里添加一条192.168.2.0 192.168.2.1 255.255.255.0 UGD)
反过来对于router2也是如此

RIP的缺点:
上面添加的那条route里的子网掩码设为255.255.255.0,实际上这并不是子网掩码,因为C类网络的hostid占8位,所以255.255.255.0实际上是C类网络的网络掩码
如果router2右边接口地址为192.168.2.17,接口的子网掩码是255.255.255.240(hostid占4位),子网是192.168.2.16,那么router1收到的IP地址是192.168.2.16,此时router1会认为这是一个主机地址,会添加如下route:
192.168.2.16 192.168.2.17 255.255.255.255 UGHD
显然这个route是错误的

备注:
192.168.2.17是1号子网的第一个可用IP地址

192.168.2是C类网络号,剩下的8位hostid分成两部分4位subnetid和4位hostid

0号子网
subnetid hostid
0000 0000 // hostid全0无效
0000 0001
...
0000 1111 // hostid全1是面向0号子网的广播地址

1号子网
0001 0000 // hostid全0无效
0001 0001 // 17
...
0001 1111 // hostid全1是面向1号子网的广播地址

RIP2扩展了RIP,支持传输子网掩码
回复