说明

本系统模拟实现了一个路由器与两个主机节点。该路由器将接收原始以太网帧,并像真正的路由器一样处理它们:将它们转发到正确的传出接口,处理以太网帧,处理 IPv4 分组,处理 ARP分组,处理 ICMP 分组,创建新帧等。这个路由器将模拟实现路由器处理主机节点发送的以太网帧的操作。

运行展示

源代码

import queue

import threading

import time

import socket

import uuid

# 十进制转二进制

def dec_to_bin_str(num):

return str(bin(num))[2:]

# 二进制转十进制

def bin_to_dec_str(binary):

return str(int(binary, 2))

# IP地址转二进制

def ipv4_to_binary(ipv4):

binary_list = ['{0:08b}'.format(int(num)) for num in ipv4.split('.')]

return ''.join(binary_list)

# 二进制地址转点分十进制

def binary_to_ipv4(binary_str):

if len(binary_str) != 32:

return "Invalid binary string"

segments = [binary_str[i:i+8] for i in range(0, 32, 8)]

ipv4_address = ".".join(str(int(segment, 2)) for segment in segments)

return ipv4_address

# 验证校验和

def validate_ip_checksum(data):

# 初始化总合为0

total = 0

# 每16位为单位进行遍历

for i in range(0, len(data), 16):

word = int(data[i:i + 16], 2)

total += word

# 按位与操作和右移操作,在总和的低16位中和高16位中分别加上结果

total = (total & 0xffff) + (total >> 16)

# 再次按位与操作和右移操作,对结果再次进行相加

total = (total + (total >> 16)) & 0xffff

# 将总和与0xffff进行异或运算,并将结果转换为16位二进制数

checksum = '{:016b}'.format(total ^ 0xffff)

return checksum

# IP数据报的格式(IPv4格式)

class IPv4Message:

def __init__(self, version, header_length, service, total_length, identification, flags, fragment_offset, ttl, protocol, checksum, source_address, destination_address, data):

self.version = version # 版本

self.header_length = header_length # 首部长度

self.service = service # 区分服务

self.total_length = total_length # 总长度

self.identification = identification # 标识

self.flags = flags # 标志

self.fragment_offset = fragment_offset # 片偏移

self.ttl = ttl # 生存时间

self.protocol = protocol # 协议

self.checksum = checksum # 校验和

self.source_address = source_address # 源地址

self.destination_address = destination_address # 目的地址

self.data = data # 数据

# 以太网MAC帧的格式

class MACMessage:

def __init__(self, mac_destination, mac_source, protocol_type, data):

self.mac_destination = mac_destination

self.mac_source = mac_source

self.protocol_type = protocol_type # 为IPv4

self.data = data

# ICMP差错报文格式

class ICMPError:

def __init__(self, message_type, checksum, data):

self.type = message_type # 1-主机不可达

self.checksum = checksum

self.data = data

# ICMP回送请求或回答报文格式

class ICMPMessage:

def __init__(self, message_type, checksum, data):

self.type = message_type # 类型字段,8-请求,0-回答

self.checksum = checksum

self.data = data

# ARP广播格式

class ARPBroadcast:

def __init__(self):

self.mac = "FF:FF:FF:FF:FF:FF"

# 获取本机IP与本机MAC

my_ip = socket.gethostbyname(socket.gethostname())

my_mac = ':'.join(['{:02x}'.format((uuid.getnode() >> elements) & 0xff) for elements in range(0, 2 * 6, 2)][::-1])

# ARP表

arp_table = {my_ip: my_mac, '172.26.213.121': '00:11:22:33:44:55', '172.26.213.64': '00:aa:bb:cc:dd:ee'}

# 路由表

route_table = {my_ip: 'direct', '172.26.213.121': 'direct', '0.0.0.0': '172.26.213.64'}

# 路由器类

class Router:

def __init__(self):

self.ip = my_ip

self.mac = my_mac

self.arp_table = arp_table

self.routing_table = route_table

self.frame_queue = queue.Queue()

def route(self):

while True:

# 拆开frame获取IP数据报

frame_message = self.frame_queue.get()

if frame_message == "":

continue

ip_message = frame_message.split("@")[-1]

ip_header = ip_message[0:160]

# 计算校验和

checksum = ip_header[80:96]

rest = ip_header[-64:]

print("路由器:计算的校验和为:" + checksum)

ip_message_t = ip_header[0:80]

ip_message_t += "0000000000000000"

ip_message_t += rest

if not checksum == validate_ip_checksum(ip_message_t):

print("路由器:校验和验证失败")

# 发送一个ICMP响应报文

print("路由器:发送一个ICMP响应报文告知主机")

continue

print("路由器:校验和验证成功")

# 验证TTL合法性

ttl = int(bin_to_dec_str(ip_header[64:72]))

if ttl <= 0:

print("TTL值不合法")

# 发送一个ICMP响应报文

print("路由器:发送一个ICMP响应报文告知主机")

continue

else:

# 自减

ttl -= 1

# 拆解IP首部

ip_source = binary_to_ipv4((ip_message[0:160])[-64:-32])

ip_destination = binary_to_ipv4((ip_message[0:160])[-32:])

print("路由器:源IP:" + ip_source + ",目的IP:" + ip_destination)

# 转发的frame

new_frame_message = ""

# 根据路由表查目的IP地址

for ip, move in route_table.items():

if ip == ip_destination:

# 修改IP头部,源地址改变但目的地址不变

if move == "direct":

print("路由器:目标可直达,直接转发")

elif move == "upper":

print("路由器:目标为本身,交付上层")

break

else:

ip_header = ip_header[0:-32]

ip_header += ipv4_to_binary(route_table["0.0.0.0"])

print("路由器:新的IP数据报已生成,下一跳IP地址为:" + route_table["0.0.0.0"])

# 根据ARP表获取下一跳MAC,封装成帧,转发

mac_source = self.mac

for m_ip, mac in arp_table.items():

if ip_header[-32:] == ipv4_to_binary(m_ip):

mac_destination = mac

new_frame_message += mac_destination + "@" + mac_source + "@IPv4@" + ip_message

print("路由器:路由器已转发,下一跳MAC为:", mac)

# 主机节点类

class Host:

def __init__(self, ip, mac):

self.ip = ip

self.mac = mac

# 获取目的MAC地址(广播ARP请求分组并接收ARP响应分组)

def get_dest_mac(self, dest_ip):

print("主机:正在进行ARP广播")

dest_mac = "FF:FF:FF:FF:FF:FF"

for ip, mac in arp_table.items():

if ip == dest_ip:

dest_mac = my_mac

print("主机:目标MAC已获取" + dest_mac)

return dest_mac

# 初始化校验和

def make_checksum(self, ip_header, dest_ip):

ip_header += "0000000000000000"

ip_header += ipv4_to_binary(self.ip) + ipv4_to_binary(dest_ip)

data = ip_header

total = 0

for i in range(0, len(data), 16):

word = int(data[i:i + 16], 2)

total += word

total = (total & 0xffff) + (total >> 16)

total = (total + (total >> 16)) & 0xffff

checksum = '{:016b}'.format(total ^ 0xffff)

print("主机:生成的校验和为:" + checksum)

return checksum

# 发送数据帧

def send_frame(self, dest_ip, data):

# 组装IP数据报

ip_header = "0100" # 版本号为4

ip_header += "0101" # 首部长度20B

ip_header += "00000000" # 区分服务

ip_header += dec_to_bin_str(len(data) + 20).zfill(16) # 总长度

ip_header += "0000000000000000" # 标识

ip_header += "000" # 标志

ip_header += "0000000000000" # 片偏移

ip_header += "00000000" # 生存时间

ip_header += "00000000" # 协议

ip_header += self.make_checksum(ip_header, dest_ip) # 校验和

ip_header += ipv4_to_binary(self.ip) # 源地址

ip_header += ipv4_to_binary(dest_ip) # 目的地址

ip_message = ip_header + data # 组装成IP数据报

# 组装数据帧

frame_head = self.get_dest_mac(dest_ip)

frame_head += "@" + self.mac + "@IPv4@"

frame_message = frame_head + ip_message

# 发送给路由器

print("主机:数据帧发送完毕")

my_router.frame_queue.put(frame_message)

# 路由器与主机节点

my_router = Router()

my_host1 = Host("172.26.213.121", "00:11:22:33:44:55")

my_host2 = Host("172.26.213.122", "00:11:22:33:44:55")

# 打开线程

router_thread = threading.Thread(target=my_router.route)

router_thread.start()

# 标志位

flag = True

# 打印本机IP与MAC,即路由器IP、MAC

print("本机IP:" + my_ip + " 本机MAC:" + my_mac)

# 轮询输入

while True:

message = input("主机:请输入要发送的消息:")

if message == "exit":

print("主机已关闭")

break

if message == "shift":

flag = not flag

continue

if flag:

my_host1.send_frame(my_ip, message)

else:

my_host2.send_frame("172.26.21.12", message)

time.sleep(1)

# 释放资源,关闭线程

router_thread.join()

文章链接

评论可见,请评论后查看内容,谢谢!!!评论后请刷新页面。