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.
136 lines
4.7 KiB
136 lines
4.7 KiB
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
|
|
}
|
|
|