只是一个简单的计网课设
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
eruhs/core/parser/ipv4.go

137 lines
4.7 KiB

8 months ago
package parser
import (
"encoding/binary"
"errors"
"fmt"
"net"
)
const (
IPv4ProtoICMP = 1 //互联网控制消息协议
IPv4ProtoIGMP = 2 //互联网组管理协议
IPv4ProtoTCP = 6 //传输控制协议
IPv4ProtoUDP = 17 //用户数据报协议
IPv4ProtoENCAP = 41 //IPv6封装
IPv4ProtoOSPF = 89 //开放式最短路径优先
IPv4ProtoSCTP = 132 //流控制传输协议
)
type IPv4 struct {
BaseLayer
Version uint8 // 版本
IHL uint8 // 首部长度
DS uint8 // 区分服务
TotalLength uint16 // 总长
Id uint16 // 标识符
Flags uint8 // 标志
FragOffset uint16 // 分片偏移
TTL uint8 // 存活时间
Protocol uint8 // 协议
Checksum uint16 // 首部校验和
SrcIP net.IP // 源地址
DstIP net.IP // 目的地址
Options []IPv4Option // 选项字段
Padding []byte // 填充
}
type IPv4Option struct {
OptionType uint8
OptionLength uint8
OptionData []byte
}
func (I *IPv4) LayerType() int {
return LayerTypeIPv4
}
// DecodeIPV4 ref: https://zh.wikipedia.org/wiki/IPv4#%E6%8A%A5%E6%96%87%E7%BB%93%E6%9E%84
func DecodeIPV4(data []byte) (*IPv4, error) {
// 至少要有20bytes
if len(data) < 20 {
return nil, fmt.Errorf("[ipv4] 无效的长度:%d, 至少需要20", len(data))
}
ipv4 := IPv4{}
ipv4.Version = data[0] >> 4
ipv4.IHL = uint8(data[0]) & 0x0F
ipv4.DS = data[1]
ipv4.TotalLength = binary.BigEndian.Uint16(data[2:4])
ipv4.Id = binary.BigEndian.Uint16(data[4:6])
flagsFrags := binary.BigEndian.Uint16(data[6:8])
ipv4.Flags = uint8(flagsFrags >> 13)
ipv4.FragOffset = flagsFrags & 0x1FFF
ipv4.TTL = data[8]
ipv4.Protocol = data[9]
ipv4.Checksum = binary.BigEndian.Uint16(data[10:12])
ipv4.SrcIP = data[12:16]
ipv4.DstIP = data[16:20]
if ipv4.TotalLength == 0 {
ipv4.TotalLength = uint16(len(data))
}
// 检查数据
if ipv4.TotalLength < 20 {
return nil, fmt.Errorf("[IPv4] 数据包长度太小 (%d < 20)", ipv4.TotalLength)
} else if ipv4.IHL < 5 {
return nil, fmt.Errorf("[IPv4] 标头长度太小 (%d < 5)", ipv4.IHL)
} else if int(ipv4.IHL*4) > int(ipv4.TotalLength) {
return nil, fmt.Errorf("[IPv4] 标头长度 > IP 总长度 (%d > %d)", ipv4.IHL, ipv4.TotalLength)
}
// 检查数据实际长度是否大于声明长度,大于(有padding)则除去padding拿到实际长度的payload
if cmp := len(data) - int(ipv4.TotalLength); cmp > 0 {
data = data[:ipv4.TotalLength]
} else if cmp < 0 {
// 如果声明的头部长度比整体数据还大,异常
if int(ipv4.IHL)*4 > len(data) {
return nil, errors.New("[ipv4] 数据包标头长度大于总数据包长度,标头长度无效或某些数据丢失")
}
}
ipv4.Header = data[:ipv4.IHL*4]
ipv4.Payload = data[ipv4.IHL*4:]
// 20bytes固定头部往后到IHL都是选项字段了
data = data[20 : ipv4.IHL*4]
// 解析可选字段,如果data[20 : ipv4.IHL*4]之间还有数据,说明选项字段有内容
for len(data) > 0 {
if ipv4.Options == nil {
// Pre-allocate to avoid growing the slice too much.
ipv4.Options = make([]IPv4Option, 0, 4)
}
opt := IPv4Option{OptionType: data[0]}
switch opt.OptionType {
case 0: // option字段结束,EOL标志
opt.OptionLength = 1
ipv4.Options = append(ipv4.Options, opt)
ipv4.Padding = data[1:]
goto FINISH
case 1: // 1byte填充
opt.OptionLength = 1
data = data[1:]
ipv4.Options = append(ipv4.Options, opt)
default:
// 字段未结束且不是1byte填充字段长度不足2byte
if len(data) < 2 {
return nil, fmt.Errorf("[IPv4] 选项长度 %d 小于2", len(data))
}
opt.OptionLength = data[1]
if len(data) < int(opt.OptionLength) {
return nil, fmt.Errorf("[IPv4] 可选字段长度超过剩余 IP 标头大小,选项类型:%v, 长度:%v", opt.OptionType, opt.OptionLength)
}
if opt.OptionLength <= 2 {
return nil, fmt.Errorf("[IPv4] 可选字段长度小于2,选项类型:%v 长度:%d", opt.OptionType, opt.OptionLength)
}
opt.OptionData = data[2:opt.OptionLength]
data = data[opt.OptionLength:]
ipv4.Options = append(ipv4.Options, opt)
}
}
FINISH:
return &ipv4, nil
}