对应代码包传送门
IP协议及动态路由算法代码包及思科模拟器资料说明
相关文章
【计算机网络】中小型校园网构建与配置
【计算机网络】Socket通信编程与传输协议分析
【计算机网络】网络应用通信基本原理
目的:
1、掌握IP协议,IP分片,DHCP,NAT以及常用的ICMP测试工具。
2、理解路由基本原理与算法,掌握链路状态路由算法,距离向量路由算法,至少使用一种语言编写DV和LS算法。
原理:
1、网络层与IP协议
网络层核心功能是转发与路由
2、ICMP
互联网控制报文协议:支持主机或理由器进行差错报告以及网络探询。
ICMC协议常用的工具Ping和Tracer rotue。
3、DHCP
动态主机配置协议:客户端能够从配置了DHCP的服务器动态获取
4、动态路由算法
路由算法协议确定去往目的网络的最佳路径,转发表(路由表)确定在本路由器如何转发分组,主要分为静态路由和动态路由。
典型的动态路由算法:链路状态路由算法、距离向量路由算法。
过程与结果:
1、ping和tracer route测试
在windows系统ping www.ox.ac.uk,可以看出发生了四次32字节的Echo。使用Ping 探测该域名,Windows系统中默认发送4个32bytes的ECHO。Ping不能完全确保目标主机是否可访问,有些服务器为了防止通过Ping探测到,通过防火墙设置了禁止Ping或者在内核参数中禁止Ping。
由ping命令中得出,到达www.ox.ac.uk的TTL为49:
使用Traert 探测牛津大学的域名:
表示与www.ox.ac.uk服务器距离14跳
TTL为49,49+14-1 = 62,检查其Header的Server检索不到。
使用Ping 和 Tracer route,以及Nslookup并不能完全保证得到确切的网站对应的IP地址(很多WEB使用反向代理、CDN、负载均衡等技术),如上图Princedon.edu使用了Cloudflare基于反向代理的内容分发网络(CDN, Content Delivery Network)技术。
2、IP数据报分片计算、验证
IP数据报分片的计算和验证思路:应该先根据理论知识计算分片,再验证。IP分片时暂不考虑传输层分段,可以尝试用编写一个基于IP的Socket 也就是原始套接字(RAW),发送一个至少大于1480*2 bytes的包,捕捉报文验证,也可以使用ICMP 的经典应用Ping 命令,发送一个大于1480*2(比如5200)bytes的报文到IP地址,捕捉报文验证。
Ping的命令为 ping [-t] [-a] [-n count] [-l length] [-f] [-i ttl] [-v tos] [-r count] [-s count] [-j computer-list] | [-k computer-list] [-w timeout] destination-list,-l可以控制发送字节大小,-n控制次数,Echo和Echo reply 不包含发送的[-l length]其他数据为8bytes。
在这里我们设计使用ping 10.225.134.15 -l 5206 -n 1
计算IP分片 Ceiling(5206+8/1480)=4 DF=0
分片 | 数据 | 偏移/量 | MF | DF |
1 | 1480 | 0 | 1 | 0 |
2 | 1480 | 1480/8 | 1 | 0 |
3 | 1480 | 1480*2/8 | 1 | 0 |
4 | 760+8 | 1480*3/8 | 0 | 0 |
验证:开启wireshark捕捉,在cmd输入命令ping 运行
可以看到Echo被分为了4个分片,IP数据报格式和分片如上图所示。
3、IP编址计算
1.求子网划分
将192.168.20.0/24划分为4个等长子网
IP地址由网络号和主机号组成,由192.168.20.0/24可知掩码位为24位
①主机位:32-24=8位。
②划分4个子网,所以2N=4,N=2,需要向主机位借2位。
③M=26=64,所以每个子网的IP数是64。
192.168.20.0 = 11000000101010000001010000000000
因此划分后的网络为:192.168.20.0/26;192.168.20.64/26;192.168.20.128/26;192.168.20.192/26.
网络号 | 子网掩码(前26为全为1,后面全为0) | 第一个可用IP | 最后一个可用IP | 广播地址 |
192.168.20.0 | 255.255.255.192 | 192.168.20.1 | 192.168.20.62 | 192.168.20.63 |
192.168.20.64 | 255.255.255.192 | 192.168.20.65 | 192.168.20.126 | 192.168.20.127 |
192.168.20.128 | 255.255.255.192 | 192.168.20.129 | 192.168.20.190 | 192.168.20.191 |
192.168.20.192 | 255.255.255.192 | 192.168.20.193 | 192.168.20.254 | 192.168.20.255 |
实现子网划分部分代码:
b = [0]*40
ip = [0]*4
index = 0
index2 = 1
def binary(d):
temp = [0 for k in range(10)]
i, x = 0, 0
i = int(i)
x = int(x)
while d>0:
n = d%2
d //= 2
temp[i] = n
# print(temp)
i += 1
x += 1
while x<8:
temp[i] = 0
i += 1
x += 1
global index
for i in range(7,-1,-1):
b[index] = temp[i]
index += 1
return
def bin_dec(x, n):
if n==0:
return 1
return x*bin_dec(x, n-1)
def res(pos, num):
count = 0
global index2
if pos == num:
for i in range(8):
if b[i] == 1:
ip[0] += bin_dec(2,7-i)
for i in range(8,16):
if b[i] == 1:
ip[1] += bin_dec(2,15-i)
for i in range(16,24):
if b[i] == 1:
ip[2] += bin_dec(2,23-i)
for i in range(24,32):
if b[i] == 1:
ip[3] += bin_dec(2,31-i)
print("子网"+ str(index2) +"ip: " + str(ip[0]) + "." + str(ip[1]) + "." + str(ip[2]) + "." + str(ip[3]) + "/" + str(num))
index2 += 1
for i in range(len(ip)):
ip[i] = 0
return
b[pos] = 1
res(pos+1, num)
b[pos] = 0
res(pos+1, num)
d1, d2, d3, d4 = 0, 0, 0, 0
bp = [0]*40
prefix, ips, ip_num = 0, 0, 0
d1,d2,d3,d4 = input("请输入ip:").split('.')
prefix = input("请输入前缀:")
prefix = int(prefix)
ips = input("请输入划分子网个数:")
ips = int(ips)
ip_num = int(ip_num)
if ips%2 == 0:
while ips>1:
ips /= 2
ip_num += 1
else:
while ips >0:
ips /= 2
ip_num += 1
for i in range(prefix):
bp[i] = 1
binary(int(d1))
binary(int(d2))
binary(int(d3))
binary(int(d4))
res(prefix, prefix+ip_num)
代码输入及执行结果:
2.求网络号(子网地址)
试确定192.168.20.129/26的子网地址
将IP地址与子网掩码(点分十进制)每段依次从左侧按位进行“与”运算;
子网掩码为26位,即255.255.255.192,其中有3个字节是255,一个字节是192,即11000000,表示网络号占用了前26位,主机号占用了后6位。将IP地址和子网掩码进行与运算,即可得到子网地址。
首先将IP地址和子网掩码转换成二进制形式:
IP地址:11000000.10101000.00010100.10000001
子网掩码:11111111.11111111.11111111.11000000
进行与运算:
11000000.10101000.00010100.10000001
11111111.11111111.11111111.11000000
----------------------------------------------------------------------
11000000.10101000.00010100.10000000
因此,子网地址为192.168.20.128。
所以可得到:
子网地址:192.168.20.128(子网掩码:255.255.255.192)
地址范围:192.168.20.128 —192.168.20.191
(子网地址的主机位,即后6位,从全0到全1)
可分配地址范围:192.168.20.129 — 192.168.20.190
广播地址:192.168.20.191
根据上述内容,确定192.168.20.129/26的子网地址,子网掩码为255.255.255.192,代码实现求子网地址:
print(str(255&192)+'.'+str(255&168)+'.'+str(255&20)+'.'+str(192&129)) 代码执行结果:
3.网络汇总
试确定192.168.40.0/26、192.168.40.64/26、192.168.40.128/26路由总结
将多个子网聚合为一个较大的子网,称为路由总结或者路由汇总(Route aggregation)。
第1步:以二进制格式列出网络:
192.168.20.0/26 11000000.10101000.00010100 00000000
192.168.20.64/26 11000000.10101000.00010100 01000000
192.168.20.128/26 11000000.10101000.00010100 10000000
第2步:计算最左侧的匹配位数量来确定掩码:
如上,有24个匹配位序列是一致的,写成CIDR前缀为“ /24”或 点分十进制 “255.255.255.0”(11111111 11111111 11111111 00000000)
第3步:保留相同匹配位不变,序列右边添加二进制“0”致32位序列,确定总结网络地址:
11000000.10101000.00010100 00000000
所以路由总结的网络地址为192.168.20.0/24
实现求路由总结代码:
n = int(input())
for i in range(0, n):
a = input()
a1 = a.split('.')
a2 = '{:08b}'.format(int(a1[0])) + ' ' + '{:08b}'.format(int(a1[1])) \
+ ' ' + '{:08b}'.format(int(a1[2])) + ' ' + '{:08b}'.format(int(a1[3]))
print(a2)
代码输入及执行结果:
4、DHCP协议分析
动态主机配置协议,DHCP: Dynamic Host ConfigurationProtocol,客户端能够从配置了DHCP的服务器动态获取:IP 地址;子网掩码;默认网关地址;DNS 服务器名称与IP地址。想要在本地抓取到DHCP包,先要保证有可用的DHCP服务器,然后将主机IP地址获取方式设置为自动获取,因为主机在抓包之前已经联网,需要先断开主机的网络连接,然后再连接网络。
在cmd下使用命令ipconfig来完成网络断开与连接的过程:
ipconfig /release :断开当前的网络连接,主机IP变为0.0.0.0,主机与网络断开,不能访问网络。
ipconfig /renew :更新适配器信息,请求连接网络,这条命令结束之后,主机会获得一个可用的IP,再次接入网络。
捕捉到的报文通过bootp过滤:
分析DHCP Discover包:
Client端使用IP地址0.0.0.0发送了一个广播包发送到目的IP为255.255.255.255。事件ID号为0x04ac90a4,发送单播,DHCP属于应用层协议,它在传输层使用UDP协议,目的端口是67。
分析DHCP Offer包:
当DHCP服务器收到一条DHCP Discover数据包时,用一个DHCP Offerr包给予客户端响应。
分析DHCP Request包:
当Client收到了DHCP Offer包以后确认有可以和它交互的DHCP服务器存在,于是Client发送Request数据包,请求分配IP。来源IP及目的IP与Discover包一致。
分析DHCP ACK包
服务器用DHCP ACK包对DHCP Repuest进行响应回复
5、距离矢量算法 —— 动态路由RIP测试
1)、距离向量(Distance Vector)路由算法
距离向量(Distance Vector) 路由算法是一种分布式,异步迭代的算法。
分布式(每个结点只当DV变化时才通告给邻居),异步迭代(局部链路费用改变或邻居DV更新引发迭代。距离向量(Distance Vector) 路由算法验证,构造如图所示拓扑。计算R1-R7路径,应该为R1-R2-R3-R7。源代码参考Bellman-Ford算法。
拓扑图:
编写代码验证如下:
这个算法在这里可以简单看成求最短路径问题,因为没有负权回路的发生,所以常用的Dijkstra算法也可以求解,我的部分代码如下:
import matplotlib.pyplot as plt
import networkx as nx
# 192.172.1.920 防copy
G2 = nx.Graph()
# 向图中添加多条赋权边: (node1,node2,weight)
G2.add_weighted_edges_from([(1,2,8),(1,4,6),(2,3,8),(3,7,8),
(4,5,6),(5,6,6),(6,7,6)])
# 两个指定顶点之间的最短加权路径
start, end = input("输入路由源点及目标点:").split(' ')
start, end = int(start), int(end)
minWPath_start_end = nx.dijkstra_path(G2, source=start, target=end)
print("顶点 v1 到 顶点 v11 的最短加权路径: ", minWPath_start_end)
# 两个指定顶点之间的最短加权路径的长度
lMinWPath_start_end = nx.dijkstra_path_length(G2, source=start, target=end)
print("顶点 v1 到 顶点 v11 的最短加权路径长度: ", lMinWPath_start_end)
pos = nx.spring_layout(G2) # 用 FR算法排列节点
nx.draw(G2, pos, with_labels=True, alpha=0.5)
labels = nx.get_edge_attributes(G2,'weight')
nx.draw_networkx_edge_labels(G2, pos, edge_labels = labels)
plt.show()
这里我调用了matplotlib,通过输入多条赋权边实现拓扑图的复现,;通过调用networkx进行Dijkstra无向图的绘制,使用库函数dijkstra_path()、dijkstra_path_length()、spring_layout()等实现求取最短加权路径及长度。
以下是代码截图及代码执行结果:
6、链路状态 —— 动态路由OSPF测试
链路状态算法计算从一个源结点到达所有其他结点的最短路径,是一种全局迭代算法。全局(所有结点路由器掌握全局网络拓扑和链路费用);迭代 (k次迭代后,得到到达k个目的结点的最短路径) ,源代码参考Dijkstra算法。
拓扑图:
编写代码验证如下:
这个测试同样适用于求解最短路径,具有贪心的思想,本质上相同的,因此修改上文代码的参数即可。
import matplotlib.pyplot as plt
import networkx as nx
# 192.172.1.920 防copy
G2 = nx.Graph()
# 向图中添加多条赋权边: (node1,node2,weight)
G2.add_weighted_edges_from([(1,2,8),(1,4,6),(2,3,8),(3,7,8),
(4,5,6),(5,6,6),(6,7,6),(5,3,2)])
# 两个指定顶点之间的最短加权路径
start, end = input("输入路由源点及目标点:").split(' ')
start, end = int(start), int(end)
minWPath_start_end = nx.dijkstra_path(G2, source=start, target=end)
print("顶点 v1 到 顶点 v11 的最短加权路径: ", minWPath_start_end)
# 两个指定顶点之间的最短加权路径的长度
lMinWPath_start_end = nx.dijkstra_path_length(G2, source=start, target=end)
print("顶点 v1 到 顶点 v11 的最短加权路径长度: ", lMinWPath_start_end)
pos = nx.spring_layout(G2) # 用 FR算法排列节点
nx.draw(G2, pos, with_labels=True, alpha=0.5)
labels = nx.get_edge_attributes(G2,'weight')
nx.draw_networkx_edge_labels(G2, pos, edge_labels = labels)
plt.show()
代码截图及代码执行结果如下: