1
1
mirror of https://github.com/wader/fq.git synced 2024-11-24 11:16:09 +03:00
fq/format/inet/ipv6_packet.go
2022-04-11 23:01:45 +02:00

174 lines
4.6 KiB
Go

package inet
import (
"bytes"
"net"
"github.com/wader/fq/format"
"github.com/wader/fq/format/registry"
"github.com/wader/fq/internal/bitioextra"
"github.com/wader/fq/pkg/decode"
"github.com/wader/fq/pkg/scalar"
)
var ipv6IpPacketGroup decode.Group
func init() {
registry.MustRegister(decode.Format{
Name: format.IPV6_PACKET,
Description: "Internet protocol v6 packet",
Groups: []string{format.INET_PACKET},
Dependencies: []decode.Dependency{
{Names: []string{format.IP_PACKET}, Group: &ipv6IpPacketGroup},
},
DecodeFn: decodeIPv6,
})
}
const (
nextHeaderHopByHop = 0
nextHeaderRouting = 43
nextHeaderFragment = 44
nextHeaderEncapsulatingSecurityPayload = 50
nextHeaderAuthentication = 51
nextHeaderDestination = 60
nextHeaderMobility = 135
nextHeaderHostIdentity = 139
nextHeaderShim6 = 140
)
// TODO:
// 253 Use for experimentation and testing [RFC3692][RFC4727]
// 254 Use for experimentation and testing [RFC3692][RFC4727]
var nextHeaderNames = scalar.UToSymStr{
nextHeaderHopByHop: "hop_by_hop",
nextHeaderRouting: "routing",
nextHeaderFragment: "fragment",
nextHeaderEncapsulatingSecurityPayload: "encapsulating_security_payload",
nextHeaderAuthentication: "authentication",
nextHeaderDestination: "destination",
nextHeaderMobility: "mobility",
nextHeaderHostIdentity: "host_identity",
nextHeaderShim6: "shim6",
}
var nextHeaderMap = scalar.Fn(func(s scalar.S) (scalar.S, error) {
if isIpv6Option(s.ActualU()) {
return nextHeaderNames.MapScalar(s)
}
return format.IPv4ProtocolMap.MapScalar(s)
})
func isIpv6Option(n uint64) bool {
switch n {
case nextHeaderHopByHop,
nextHeaderRouting,
nextHeaderFragment,
nextHeaderEncapsulatingSecurityPayload,
nextHeaderAuthentication,
nextHeaderDestination,
nextHeaderMobility,
nextHeaderHostIdentity,
nextHeaderShim6:
return true
default:
return false
}
}
// from https://www.iana.org/assignments/ipv6-parameters/ipv6-parameters.xhtml#ipv6-parameters-2
var hopByHopTypeNames = scalar.UToSymStr{
0x00: "pad1",
0x01: "padn",
0xc2: "jumbo_payload",
0x23: "rpl_option",
0x04: "tunnel_encapsulation_limit",
0x05: "router_alert",
0x26: "quick_start",
0x07: "calipso",
0x08: "smf_dpd",
0xc9: "home_address",
0x8b: "ilnp_nonce",
0x8c: "line_identification_option",
0x4d: "deprecated",
0x6d: "mpl_option",
0xee: "ip_dff",
0x0f: "performance_and_diagnostin_metrics",
0x11: "ioam",
0x31: "ioam",
}
var mapUToIPv6Sym = scalar.Fn(func(s scalar.S) (scalar.S, error) {
b := &bytes.Buffer{}
if _, err := bitioextra.CopyBits(b, s.ActualBitBuf()); err != nil {
return s, err
}
s.Sym = net.IP(b.Bytes()).String()
return s, nil
})
func decodeIPv6(d *decode.D, in interface{}) interface{} {
if ipi, ok := in.(format.InetPacketIn); ok && ipi.EtherType != format.EtherTypeIPv6 {
d.Fatalf("incorrect ethertype %d", ipi.EtherType)
}
d.FieldU4("version")
d.FieldU6("ds")
d.FieldU2("ecn")
d.FieldU20("flow_label")
dataLength := d.FieldU16("payload_length")
nextHeader := d.FieldU8("next_header", nextHeaderMap)
d.FieldU8("hop_limit")
d.FieldRawLen("source_address", 128, mapUToIPv6Sym)
d.FieldRawLen("destination_address", 128, mapUToIPv6Sym)
extStart := d.Pos()
if isIpv6Option(nextHeader) {
// TODO: own format?
d.FieldArray("extensions", func(d *decode.D) {
for isIpv6Option(nextHeader) {
d.FieldStruct("extension", func(d *decode.D) {
currentHeader := nextHeader
nextHeader = d.FieldU8("next_header", nextHeaderMap)
extLen := d.FieldU8("length")
// whole header not including the first 8 octets
extLen += 6
d.FramedFn(int64(extLen)*8, func(d *decode.D) {
switch currentHeader {
case nextHeaderHopByHop:
d.FieldArray("options", func(d *decode.D) {
for !d.End() {
d.FieldStruct("option", func(d *decode.D) {
d.FieldU8("type", hopByHopTypeNames)
l := d.FieldU8("len")
d.FieldRawLen("data", int64(l)*8)
})
}
})
default:
d.FieldRawLen("payload", d.BitsLeft())
}
})
})
}
})
}
extEnd := d.Pos()
extLen := extEnd - extStart
// TODO: jumbo
// TODO: nextHeader 59 skip
payloadLen := int64(dataLength)*8 - extLen
d.FieldFormatOrRawLen(
"payload",
payloadLen,
ipv4IpPacketGroup,
format.IPPacketIn{Protocol: int(nextHeader)},
)
return nil
}