哈工大计算机网络实验四&五:IPV4分组收发实验

实验四:IPv4分组收发实验

完整代码在最后

实验要求:

IPv4 分组的基本接收处理,能够检测出接收到的IP 在如下错误:校验和错、TTL错、版本号错、头部长度错、错误 目标地址,当存在多种错误时,其他错误类型优先于错误目标地址。

注:不要求实现IPv4协议中的选项和分片处理功能,目标地址 错误为IP分组的目的地址非本机IP地址。本机IP地址:192.168. 214.138

基本上就是处理pcap包,解析其中的ipv4头部信息,对头部信息进行检测是否有错。整体来说比较简单,处理解析pcap包部分需要一些处理,最后需要一个txt文本文件,提交到系统上进行验证。

实验过程:

解析pcap包:

pcap包结构:

PcapHeader:24个字节

PacketHeader:16个字节,里面有数据部分长度,可以用来检测数据包是否完整,获得数据帧的位置

PacketData:数据包部分

我们要解析的IPV4头部信息就在PacketData中

此处我们先跳过PcapHeader部分(24个字节),然后读取PacketHeader中存储数据包长度的部分,检测是否完整,同时可以获得数据帧的位置:

pcap_file = f"./propacket/pro{i}.pcap"
        # print("Processing:", pcap_file)

        with open(pcap_file, 'rb') as f:
            # 跳过pcapHeader部分
            f.seek(24)
            packet_header = f.read(16)  # 读取packetHeader部分
            incl_len = struct.unpack('<I', packet_header[8:12])[0]  # 解析获取数据包长度
            data = f.read(incl_len)   # 读取数据包内容——packetData部分

        if len(data) < 14: #以太网帧头部一般有14个字节长
            print("数据包太短")
            continue

之后得到数据包data后,首先可以对以太网头部进行解析,其中可以获取上层(网络层)使用的协议,也可以在IPV4头部中获得协议版本。

eth_header = data[:14]   # 获得以太网帧头部分
        eth_type = struct.unpack("!H", eth_header[12:14])[0]   # 头部14个字节中最后两个存储上层协议类型

提取和判断:

首先附上头部信息:

如果是ipv4协议开始对ipv4头部进行解析,版本号,首都长度,ttl等信息。

if eth_type == 0x0800:   # 如果是IPv4数据包
            ip_header_offset = 14  # IP头部在以太网数据包中的偏移量
            ip_header_len = (data[ip_header_offset] & 0x0F) * 4#首部长度
            if ip_header_len ==20:
                print("首部长度正确")
                # file.write("首部长度正确\n")
            else:
                print("首部长度错误")
                file.write("头部长度错\n")
                istrue=False
                continue
            if len(data) < ip_header_offset + ip_header_len:
                print("ip头部有缺失")
                continue

            ip_header = data[ip_header_offset:ip_header_offset + ip_header_len]
            ip_version = (ip_header[0])>>4#版本号
            ip_ttl=ip_header[8]#寿命
            dst_ip=socket.inet_ntoa(ip_header[16:20])#目的ip

下面具体说一下校验和的计算,在接收端(发送端下面会说)的时候只需要将首部20个字节,每两个字节(两个两位十六进制数合为一个四位十六进制数)加起来进行计算,如果有溢出的情况(进位),则加1到原数中,进的位抹除,如此重复,知道加完这10个数,如果是0xffff,则校验和计算正确。

# 计算校验和
            checksum = 0
            tempsum=0
            for i in range(0, len(ip_header), 2):
                tempsum=(ip_header[i] << 8) + ip_header[i+1]#第一个字节的数左移8位加上第二个字节的数,实现“合成”
                checksum=checksum+tempsum
                # 处理溢出
            while checksum>0xffff:
                temp=checksum>>16
                checksum=checksum&0xffff
                checksum=checksum+temp
            calculated_checksum=checksum

之后就是一些简单的判断

if ip_version !=4:
                print("版本号错误")
                file.write("版本号错\n")
                istrue=False
                continue
                # continue
            else:
                print("版本号正确")
                # file.write("版本号正确\n")
            if ip_ttl <=0:
                print("TTL错误")
                file.write("TTL错\n")
                istrue=False
                continue
            else:
                print("TTL正确")
                # file.write("TTL正确\n")

剩余的就是对写入文件的处理,比较简单就不单说了。

实验五:IPv4分组转发实验

实验要求:

在前面IPv4分组收发实验的基础上,增加分组转发功能。具体来说, 对于每一个到达本机的IPv4 分组,根据其目的IPv4地址 决定分组的处理行为: 1) “接收”目的地址为本机地址的分组; 2) 根据路由查找结果,“丢弃”查不到路由的分组; 3) 根据路由查找结果,“转发”不是本机接收的分组。

实验过程:

实验五其实就是在实验四的基础上多加了对目的地址的处理,其中需要设置一个简单的路由表,如果目的ip在路由表的设置中,则转发,如果是本地ip,则接收,否则丢弃。

设置路由表

因为路由条目比较简单,所以直接用一个列表就可以了

forward_ips = ['192.168.214.2', '192.168.214.1', '192.168.214.3', '43.168.142.77', '192.168.213.2', '192.168.142.2', '192.168.142.77', '43.168.142.78', '43.168.142.65', '43.168.142.66', '97.43.142.226', '97.43.142.225', '97.43.142.229']

目的ip处理

目的ip处理上面已经说过,如果是需要转发的ip需要对ip头部进行修改:ttl-1,校验和需要重新计算,具体计算方法如下:

发送端:将校验和字段先赋值位零,然后依然是每两个字节“合成”为一个数,求和处理,中间溢出(进位)的情况处理相同,都是加一然后进的位抹除,最后的和按位取反。

if dst_ip==localIp:
                print("正确 接收")
                file.write("正确 接收\n")
            elif dst_ip in forward_ips:
                newttl=ip_ttl-1
                newchecksum=0
                # 重新计算校验和
                for i in range(0,len(ip_header),2):
                    if i==10:
                        tempsum=0
                    elif i==8:
                        tempsum=((ip_header[i]-1)<<8)+ip_header[i+1]
                    else:
                        tempsum=(ip_header[i]<<8)+ip_header[i+1]
                    newchecksum=newchecksum+tempsum
                # 处理溢出
                while newchecksum>0xffff:
                    temp=newchecksum>>16
                    newchecksum=newchecksum&0xffff
                    newchecksum=newchecksum+temp
                newchecksum=~newchecksum
                newchecksum=0xffff&newchecksum
                file.write(f"正确 转发 {newttl} {hex(newchecksum)}\n")
            else:
                print("目的ip错误")
                file.write("错误目标地址 丢弃\n")
                istrue=False
                continue

完整代码

实验四

import socket
import struct
# 设置好的本机ip
localIp='192.168.110.138'
count = 0
with open("./output/output.txt","w",encoding="utf-8") as file:
    for i in range(100):
        istrue= True
        pcap_file = f"./propacket/pro{i}.pcap"
        # print("Processing:", pcap_file)

        with open(pcap_file, 'rb') as f:
            # 跳过pcapHeader部分
            f.seek(24)
            packet_header = f.read(16)  # 读取packetHeader部分
            incl_len = struct.unpack('<I', packet_header[8:12])[0]  # 解析获取数据包长度
            data = f.read(incl_len)   # 读取数据包内容——packetData部分

        if len(data) < 14: #以太网帧头部一般有14个字节长
            print("数据包太短")
            continue

        eth_header = data[:14]   # 获得以太网帧头部分
        eth_type = struct.unpack("!H", eth_header[12:14])[0]   # 头部14个字节中最后两个存储上层协议类型

        if eth_type == 0x0800:   # 如果是IPv4数据包
            ip_header_offset = 14  # IP头部在以太网数据包中的偏移量
            ip_header_len = (data[ip_header_offset] & 0x0F) * 4#首部长度
            if ip_header_len ==20:
                print("首部长度正确")
                # file.write("首部长度正确\n")
            else:
                print("首部长度错误")
                file.write("头部长度错\n")
                istrue=False
                continue
            if len(data) < ip_header_offset + ip_header_len:
                print("ip头部有缺失")
                continue

            ip_header = data[ip_header_offset:ip_header_offset + ip_header_len]
            ip_version = (ip_header[0])>>4#版本号
            ip_ttl=ip_header[8]#寿命
            dst_ip=socket.inet_ntoa(ip_header[16:20])#目的ip

            # 解析 IPv4 头部的校验和字段
            ip_checksum = struct.unpack('!H', ip_header[10:12])[0]
            # 计算校验和
            checksum = 0
            tempsum=0
            for i in range(0, len(ip_header), 2):
                tempsum=(ip_header[i] << 8) + ip_header[i+1]#第一个字节的数左移8位加上第二个字节的数,实现“合成”
                checksum=checksum+tempsum
                # 处理溢出
            while checksum>0xffff:
                temp=checksum>>16
                checksum=checksum&0xffff
                checksum=checksum+temp
            calculated_checksum=checksum

            print("--------下面是IPv4头部信息--------")
            print("Header Checksum:", hex(ip_checksum))  # 打印原始数据包中的校验和
            print("Calculated Checksum:", hex(calculated_checksum))  # 打印计算得到的校验和
            print("length of header is ",ip_header_len)
            print("ttl is ",ip_ttl)
            print("目的ip:",dst_ip)
            print("------------------输出:---------------")


            if ip_version !=4:
                print("版本号错误")
                file.write("版本号错\n")
                istrue=False
                continue
                # continue
            else:
                print("版本号正确")
                # file.write("版本号正确\n")
            if ip_ttl <=0:
                print("TTL错误")
                file.write("TTL错\n")
                istrue=False
                continue
            else:
                print("TTL正确")
                # file.write("TTL正确\n")
            if calculated_checksum==0xffff:
                count=count+1
                print("校验和正确")
                # file.write("校验和正确\n")
            else:
                print("校验和错误")
                file.write("校验和错\n")
                istrue=False
                continue
            if dst_ip==localIp:
                print("目的ip正确")
                # file.write("目的ip正确\n")
            else:
                print("目的ip错误")
                file.write("错误目标地址\n")
                istrue=False
                continue
            if istrue==True:
                file.write("正确\n")


        else:
            print("不是ipv4协议,版本号错误")

        print("-" * 33)
    print("count is ",count)

实验五

import socket
import struct

localIp='192.168.110.138'

forward_ips = ['192.168.214.2', '192.168.214.1', '192.168.214.3', '43.168.142.77', '192.168.213.2', '192.168.142.2', '192.168.142.77', '43.168.142.78', '43.168.142.65', '43.168.142.66', '97.43.142.226', '97.43.142.225', '97.43.142.229']
count = 0
with open("./output/output2.txt","w",encoding="utf-8") as file:
    for i in range(100):
        istrue= True
        pcap_file = f"./propacket/pro{i}.pcap"
        # print("Processing:", pcap_file)

        with open(pcap_file, 'rb') as f:
            # 跳过pcapHeader部分
            f.seek(24)
            pcap_header = f.read(16)  # 读取packetHeader部分
            incl_len = struct.unpack('<I', pcap_header[8:12])[0]  # 解析获取数据包长度
            data = f.read(incl_len)   # 读取数据包内容——packetData部分

        if len(data) < 14: #以太网帧头部一般有14个字节长
            print("数据包太短")
            continue

        eth_header = data[:14]   # 获得以太网帧头部分
        eth_type = struct.unpack("!H", eth_header[12:14])[0]   # 头部14个字节中最后两个存储上层协议类型

        if eth_type == 0x0800:   # 如果是IPv4数据包
            ip_header_offset = 14  # IP头部在以太网数据包中的偏移量
            ip_header_len = (data[ip_header_offset] & 0x0F) * 4#首部长度
            if ip_header_len ==20:
                print("首部长度正确")
                # file.write("首部长度正确\n")
            else:
                print("首部长度错误")
                file.write("头部长度错 丢弃\n")
                istrue=False
                continue
            if len(data) < ip_header_offset + ip_header_len:
                print("头部信息有缺失")
                continue

            ip_header = data[ip_header_offset:ip_header_offset + ip_header_len]
            ip_version = (ip_header[0])>>4#版本号
            ip_ttl=ip_header[8]#寿命
            dst_ip=socket.inet_ntoa(ip_header[16:20])#目的ip

            # 解析 IPv4 头部的校验和字段
            ip_checksum = struct.unpack('!H', ip_header[10:12])[0]
            # print("原校验和为 ",hex(ip_checksum))
            # print("ip_checksum is ",ip_checksum)

            # 计算 IPv4 头部的校验和
            checksum = 0
            tempsum=0
            for i in range(0, len(ip_header), 2):
                # print("每加:",hex((ip_header[i] << 8) + ip_header[i+1]))
                tempsum=(ip_header[i] << 8) + ip_header[i+1]
                checksum=checksum+tempsum
            # 处理溢出,一般两次就可以
            while checksum>0xffff:
                temp=checksum>>16
                checksum=checksum&0xffff
                checksum=checksum+temp
            # checksum=checksum&0xffff
            calculated_checksum=checksum

            print("--------下面是IPv4头部信息--------")
            print("Header Checksum:", hex(ip_checksum))  # 打印原始数据包中的校验和
            print("Calculated Checksum:", hex(calculated_checksum))  # 打印计算得到的校验和
            print("length of header is ",ip_header_len)
            print("ttl is ",ip_ttl)
            print("目的ip:",dst_ip)
            print("------------------输出:---------------")


            if ip_version !=4:
                print("版本号错误")
                file.write("版本号错 丢弃\n")
                istrue=False
                continue
                # continue
            else:
                print("版本号正确")
                # file.write("版本号正确\n")
            if ip_ttl <=0:
                print("TTL错误")
                file.write("TTL错 丢弃\n")
                istrue=False
                continue
            else:
                print("TTL正确")
                # file.write("TTL正确\n")
            if calculated_checksum==0xffff:
                count=count+1
                print("校验和正确")
                # file.write("校验和正确\n")
            else:
                print("校验和错误")
                file.write("校验和错 丢弃\n")
                istrue=False
                continue
            if dst_ip==localIp:
                print("正确 接收")
                file.write("正确 接收\n")
            elif dst_ip in forward_ips:
                newttl=ip_ttl-1
                newchecksum=0
                # 重新计算校验和
                for i in range(0,len(ip_header),2):
                    if i==10:
                        tempsum=0
                    elif i==8:
                        tempsum=((ip_header[i]-1)<<8)+ip_header[i+1]
                    else:
                        tempsum=(ip_header[i]<<8)+ip_header[i+1]
                    newchecksum=newchecksum+tempsum
                # 处理溢出
                while newchecksum>0xffff:
                    temp=newchecksum>>16
                    newchecksum=newchecksum&0xffff
                    newchecksum=newchecksum+temp
                newchecksum=~newchecksum
                newchecksum=0xffff&newchecksum
                file.write(f"正确 转发 {newttl} {hex(newchecksum)}\n")
            else:
                print("目的ip错误")
                file.write("错误目标地址 丢弃\n")
                istrue=False
                continue
        else:
            print("不是ipv4协议,版本号错误")

        print("-" * 33)
    print("count is ",count)

参考链接:https://blog.csdn.net/weixin_40950781/article/details/117632309

  • 8
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值