Python实现一个简单的主机-路由器结构(计算机网络)

news2024/11/15 23:28:37

说明

本系统模拟实现了一个路由器与两个主机节点。该路由器将接收原始以太网帧,并像真正的路由器一样处理它们:将它们转发到正确的传出接口,处理以太网帧,处理 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()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1502838.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

太阳辐射环境模拟系统系统

太阳辐射环境模拟系统是一种高度专业化的设备&#xff0c;用于模拟太阳光的全谱段辐射&#xff0c;包括紫外线、可见光和红外线。这种系统的核心功能是在实验室条件下复制太阳的辐射条件&#xff0c;以评估材料、产品或设备在实际太阳辐射影响下的性能和耐久性。 应用领域&…

2024-03-05 linux 分区老显示满,Use 100%,原因是SquashFS 是一种只读文件系统,它在创建时就已经被填满,所有空间都被使用。

一、这两天一直纠结一个问题&#xff0c;无论怎么修改&#xff0c;linux 分区老显示满&#xff0c;Use 100%&#xff0c;全部沾满。如下图的oem分区。 二、导致出现上面的原因是&#xff1a;SquashFS文件系统里的空间利用率总是显示为100%。 三、SDK里面也说明SquashFS文件系统…

【洛谷 P8668】[蓝桥杯 2018 省 B] 螺旋折线 题解(数学+平面几何)

[蓝桥杯 2018 省 B] 螺旋折线 题目描述 如图所示的螺旋折线经过平面上所有整点恰好一次。 对于整点 ( X , Y ) (X, Y) (X,Y)&#xff0c;我们定义它到原点的距离 dis ( X , Y ) \text{dis}(X, Y) dis(X,Y) 是从原点到 ( X , Y ) (X, Y) (X,Y) 的螺旋折线段的长度。 例如 …

武汉灰京文化:手机游戏行业创新与机遇引领着无限潜力的繁荣

近年来&#xff0c;手机游戏行业迎来了前所未有的繁荣&#xff0c;这主要得益于新技术的飞速发展和硬件的不断升级。市场研究机构预测&#xff0c;到2025年&#xff0c;全球手机游戏市场将超过2000亿美元。这一惊人的增长数据充分体现了手机游戏行业的活力和无限潜力。手机游戏…

appium2的一些配置

appium-desktop不再维护之后&#xff0c;需要使用appium2。 1、安装appium2 命令行输入npm i -g appium。安装之后输入appium或者appium-server即可启动appium 2、安装安卓/ios的驱动 安卓&#xff1a;appium driver install uiautomator2 iOS&#xff1a;appium driver i…

21 easy 1. 两数之和

//给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标值 target 的那 两个 整数&#xff0c;并返回它们的数组下标。 // // 你可以假设每种输入只会对应一个答案。但是&#xff0c;数组中同一个元素在答案里不能重复出现。 // // 你可以…

代码随想录 回溯算法-棋盘问题

目录 51.N皇后 37.解数独 51.N皇后 51. N 皇后 困难 按照国际象棋的规则&#xff0c;皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。 n 皇后问题 研究的是如何将 n 个皇后放置在 nn 的棋盘上&#xff0c;并且使皇后彼此之间不能相互攻击。 给你一个整数 n &…

GESP5级T1真题 [202309] 因数分解——O(sqrt(n))的时间复杂度,值得一看

描述 每个正整数都可以分解成素数的乘积&#xff0c;例如&#xff1a;62*3、2022 *5 现在&#xff0c;给定一个正整数N&#xff0c;请按要求输出它的因数分解式。 输入描述 输入第一行&#xff0c;包含一个正整数N。约定2<N<10^12 输出描述 输出一行&#xff0c;为N…

Web APIs 6 正则表达式

Web APIs 6 正则表达式1、介绍2、语法方法一方法二 3、元字符分类① 边界符② 量词③ 字符类 案例&#xff1a;用户名验证 4、修饰符案例&#xff1a;过滤敏感字 案例&#xff1a;小兔鲜注册注册模块 正则表达式 1、介绍 正则表达式&#xff08;Regular Expression&#xff0…

OpenAI (ChatGPT)中国免费试用地址

GitHub - click33/chatgpt---mirror-station-summary: 汇总所有 chatgpt 镜像站&#xff0c;免费、付费、多模态、国内外大模型汇总等等 持续更新中…… 个人能力有限&#xff0c;搜集到的不多&#xff0c;求大家多多贡献啊&#xff01;众人拾柴火焰高&#xff01;汇总所有 cha…

uniapp模仿下拉框实现文字联想功能 - uniapp输入联想(官方样式-附源码)

一、效果 废话不多说&#xff0c;上效果图&#xff1a; 在下方的&#xff1a; 在上方的&#xff1a; 二、源码 一般是个输入框&#xff0c;输入关键词&#xff0c;下拉一个搜索列表。 ElementUI有提供<el-autocomplete>&#xff0c;但uniapp官网没提供这么细&#x…

基于Vue的实时影片资讯APP设计与实现

目 录 摘 要 I Abstract II 引 言 1 1 相关技术 3 1.1 Vue框架 3 1.2 MVVM 3 1.3 Mongodb数据库 4 1.4 Axios请求 4 1.5 H5、CSS3和JavaScript 5 1.6 本章小结 5 2 系统分析 7 2.1 功能需求 7 2.2 用例分析 7 2.3 用户功能 8 2.4本章小结 8 3 Vue影片票务服务APP设计 9 3.1 页…

基于华为atlas的unet分割模型探索

Unet模型使用官方基于kaggle Carvana Image Masking Challenge数据集训练的模型。 模型输入为572*572*3&#xff0c;输出为572*572*2。分割目标分别为&#xff0c;0&#xff1a;背景&#xff0c;1&#xff1a;汽车。 Pytorch的pth模型转化onnx模型&#xff1a; import torchf…

【蓝桥杯】单词分析 (BF)

一.题目描述 二.问题分析 //单词分析 #include <iostream> using namespace std;const int N1e42; char s[N]; int c[26]{0};int main(int argc, const char * argv[]) {ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);cin>>s;int max0,i0;char xa;while(s[i]){…

【Python使用】python高级进阶知识md总结第1篇:My Awesome Book【附代码文档】

python高级进阶全知识知识笔记总结完整教程&#xff08;附代码资料&#xff09;主要内容讲述&#xff1a;My Awesome Book&#xff0c;My Awesome Book。My Awesome Book&#xff0c;MySQL数据库。My Awesome Book&#xff0c;聚合函数。My Awesome Book&#xff0c;创建表并给…

Mysql深入学习 基础篇 Ss.06 事务

青青子衿&#xff0c;悠悠我心 纵我不往&#xff0c;子宁不嗣音 —— 24.3.9 事务&#xff1a; 事务简介 事务操作 事务四大特性 并发事务问题 事务隔离级别 一、事务简介 事务是一组操作的集合&#xff0c;它是一个不可分割的工作单位&#xff0c;事务会把所有的操作作为一个整…

开源的Java图片处理库介绍

在 Java 生态系统中&#xff0c;有几个流行的开源库可以用于图片处理。这些库提供了丰富的功能&#xff0c;如图像缩放、裁剪、颜色调整、格式转换等。以下是几个常用的 Java 图片处理库的介绍&#xff0c;包括它们的核心类、主要作用和应用场景&#xff0c;以及一些简单的例子…

MATLAB2020a安装编译器mingw-64(6.3.0)

MATLAB2020a指定安装mingw-64&#xff08;6.3.0&#xff09;版本编译器 记录一下几个要点 mingw-64&#xff08;6.3.0&#xff09; 找到对应的mingw-64安装包 设置mingw的bin文件路径到环境变量 变量名&#xff1a;MW_MINGW64_LOC MATLAB设置路径

java 哨兵线性搜索

顾名思义&#xff0c;哨兵线性搜索是线性搜索的一种&#xff0c;与传统线性搜索相比&#xff0c;比较次数减少了。在传统的线性搜索中&#xff0c;仅进行N次比较&#xff0c;而在哨兵线性搜索中&#xff0c;哨兵值用于避免任何越界比较&#xff0c;但没有专门针对正在搜索的元素…

快速瓦斯封孔器请满载希望出发

不论昨天如何&#xff0c;今天请满载希望出发&#xff01;每一个微笑、每一次服务&#xff0c;都是我们通往成功巅峰的阶梯。 一、 用途&#xff1a; CKF&#xff0d;I型快速瓦斯封孔器用以快速封闭采面卸压抽放钻孔&#xff0c;具有重量轻、速度快、操作简便的特点&#xff1…