From ff695a595678f8200f5bda483d3c2bac23727d43 Mon Sep 17 00:00:00 2001 From: Johannes Kimmel Date: Mon, 13 Dec 2021 06:55:52 +0100 Subject: [PATCH] complete tlv parser --- go.mod | 7 +- go.sum | 29 +- main.go | 108 +++++-- tlv/subtype_string.go | 36 +++ tlv/tlv.go | 727 ++++++++++++++++++++++++++++++++++++++++-- tlv/tlv_string.go | 35 -- tlv/tlv_test.go | 41 +++ tlv/type_string.go | 35 ++ tlv/update.go | 64 ---- 9 files changed, 923 insertions(+), 159 deletions(-) create mode 100644 tlv/subtype_string.go delete mode 100644 tlv/tlv_string.go create mode 100644 tlv/tlv_test.go create mode 100644 tlv/type_string.go delete mode 100644 tlv/update.go diff --git a/go.mod b/go.mod index a510e4e..93b0213 100644 --- a/go.mod +++ b/go.mod @@ -1,5 +1,8 @@ module git.freifunk-franken.de/jkimmel/abbel -go 1.15 +go 1.0 -require golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 +require ( + golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 + inet.af/netaddr v0.0.0-20211027220019-c74959edd3b6 +) diff --git a/go.sum b/go.sum index bea54c7..1bec611 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,34 @@ +github.com/dvyukov/go-fuzz v0.0.0-20210103155950-6a8e9d1f2415/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go4.org/intern v0.0.0-20211027215823-ae77deb06f29 h1:UXLjNohABv4S58tHmeuIZDO6e3mHpW2Dx33gaNt03LE= +go4.org/intern v0.0.0-20211027215823-ae77deb06f29/go.mod h1:cS2ma+47FKrLPdXFpr7CuxiTW3eyJbWew4qx0qtQWDA= +go4.org/unsafe/assume-no-moving-gc v0.0.0-20211027215541-db492cf91b37 h1:Tx9kY6yUkLge/pFG7IEMwDZy6CS2ajFc9TvQdPCW0uA= +go4.org/unsafe/assume-no-moving-gc v0.0.0-20211027215541-db492cf91b37/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4 h1:myAQVi0cGEoqQVR5POX+8RR2mrocKqNN1hmeMqhX27k= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +inet.af/netaddr v0.0.0-20211027220019-c74959edd3b6 h1:acCzuUSQ79tGsM/O50VRFySfMm19IoMKL+sZztZkCxw= +inet.af/netaddr v0.0.0-20211027220019-c74959edd3b6/go.mod h1:y3MGhcFMlh0KZPMuXXow8mpjxxAk3yoDNsp4cQz54i8= diff --git a/main.go b/main.go index 77279aa..f9c68ec 100644 --- a/main.go +++ b/main.go @@ -7,6 +7,7 @@ import ( "git.freifunk-franken.de/jkimmel/abbel/tlv" "golang.org/x/net/ipv6" + "inet.af/netaddr" ) type options struct { @@ -30,6 +31,35 @@ func parseOpts() (options, error) { return opt, err } +func readBabelPacket(up *ipv6.PacketConn, b []byte) ([]byte, netaddr.IP, error) { + n, rcm, src, err := up.ReadFrom(b) + if err != nil { + return nil, netaddr.IP{}, err + } + + _ = rcm + + b = b[:n] + if len(b) < 4 { + fmt.Println("packet too short:", len(b)) + } + magic := b[0] + version := b[1] + length := uint16(b[2])<<8 + uint16(b[3]) + b = b[4:] + + if magic != 42 { + return nil, netaddr.IP{}, fmt.Errorf("Invalid magic number %d", magic) + } + if int(length) > len(b) { + fmt.Println("invalid length:", length) + } + + fmt.Println(src, rcm, magic, version, length, n) + srcipport, err := netaddr.ParseIPPort(src.String()) + return b, srcipport.IP(), err +} + func run(opt options) error { var err error uc, err := net.ListenUDP("udp6", &net.UDPAddr{Port: int(opt.Port)}) @@ -49,50 +79,66 @@ func run(opt options) error { } } - b := make([]byte, 1500) + buf := [4096]byte{} for { - n, rcm, src, err := up.ReadFrom(b) + b, src, err := readBabelPacket(up, buf[:]) if err != nil { - return err - } - - _ = rcm - - magic := b[0] - version := b[1] - length := uint16(b[2])<<8 + uint16(b[3]) - - if magic != 42 { + fmt.Println("Skipping packet:", err) continue } - if int(length)+4 > n { - fmt.Println("invalid length") - } - fmt.Println(src, rcm, magic, version, length, n) - - var u tlv.PacketUpdate var s tlv.PacketDecoder - ts := tlv.NewTLVScanner(b[4 : 4+length]) - for ts.Scan() { - t, v := ts.TLV() - switch t { + s.Reset(b, src) + for s.Scan() { + var subtlv []byte + switch t := s.TLV().(type) { + case tlv.NextHop: + fmt.Printf("%12s %s\n", t.T(), t.Address) + case tlv.Hello: + fmt.Printf("% 12s %+v\n", t.T(), t) case tlv.Update: - u, err = s.UpdateFromBytes(v) if err != nil { - fmt.Println(u.PacketUpdateHeader, &u.Prefix, err) + fmt.Println(t, t.Prefix, err) } else { - if u.PacketUpdateHeader.Metric == 0xFFFF { - fmt.Print("\x1B[1m") + if t.Metric == 0xFFFF { + fmt.Print("\x1B[31m") + } else { + fmt.Print("\x1B[36m") } - fmt.Printf("%43s: %+v\x1B[0m\n", &u.Prefix, u.PacketUpdateHeader) + fmt.Printf("% 12s %-43s%s\x1B[0m\n", t.T(), t.Prefix, t.FormatHeader()) } + case tlv.RouterID: + fmt.Printf("%12s %s\n", t.T(), t) + case tlv.RouteRequest: + fmt.Printf("%12s %s\n", t.T(), t.Prefix) + case tlv.Raw: + fmt.Printf(" T: %12s (%2d), L: %3d\n", t.T(), t.T(), len(t.V())) + case tlv.IHU: + fmt.Printf("%12s %-43sRxcost %5d Interval %4d \n", t.T(), t.Address, t.Rxcost, t.Interval) default: - fmt.Printf(" T: %12s (%2d), L: %3d\n", t, t, len(v)) + if t != nil { + fmt.Println("Unknown TLV", t.T(), t) + } else { + fmt.Println("got nil TLV") + } + } + for len(subtlv) > 0 { + switch tlv.SubType(subtlv[0]) { + case tlv.SubTypeSourcePrefix: + var pfx tlv.SourcePrefix + pfx, subtlv, err = tlv.SourcePrefixFromBytes(subtlv[2:]) + fmt.Print("\x1B[7;36m") + fmt.Printf("%12s %s\x1B[0m\n", pfx.T(), pfx) + case tlv.SubTypeDiversity, tlv.SubTypePad1, tlv.SubTypePadN, tlv.SubTypeTimestamp: + fmt.Print("\x1B[7m") + fmt.Printf("%12s: %v\x1B[0m\n", "Subtlv", subtlv) + subtlv = nil + default: + panic("unknown subtlv type") + } } - } - if ts.Err() != nil { + if s.Err() != nil { fmt.Println(err) } @@ -111,8 +157,6 @@ func run(opt options) error { // fmt.Println(n, len(bbuf.Bytes())) //} } - - return nil } func main() { diff --git a/tlv/subtype_string.go b/tlv/subtype_string.go new file mode 100644 index 0000000..16d1162 --- /dev/null +++ b/tlv/subtype_string.go @@ -0,0 +1,36 @@ +// Code generated by "stringer -type=SubType -trimprefix SubType"; DO NOT EDIT. + +package tlv + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[SubTypePad1-0] + _ = x[SubTypePadN-1] + _ = x[SubTypeDiversity-2] + _ = x[SubTypeTimestamp-3] + _ = x[SubTypeSourcePrefix-128] +} + +const ( + _SubType_name_0 = "Pad1PadNDiversityTimestamp" + _SubType_name_1 = "SourcePrefix" +) + +var ( + _SubType_index_0 = [...]uint8{0, 4, 8, 17, 26} +) + +func (i SubType) String() string { + switch { + case i <= 3: + return _SubType_name_0[_SubType_index_0[i]:_SubType_index_0[i+1]] + case i == 128: + return _SubType_name_1 + default: + return "SubType(" + strconv.FormatInt(int64(i), 10) + ")" + } +} diff --git a/tlv/tlv.go b/tlv/tlv.go index 27b5ca3..8daab3c 100644 --- a/tlv/tlv.go +++ b/tlv/tlv.go @@ -1,30 +1,57 @@ -//go:generate stringer -type=TLV +//go:generate stringer -type=Type -trimprefix Type +//go:generate stringer -type=SubType -trimprefix SubType package tlv -import "fmt" +import ( + "bytes" + "errors" + "fmt" -type TLV uint8 + "inet.af/netaddr" +) + +type Type uint8 const ( - Pad1 TLV = iota - PadN - AckReq - Ack - Hello - IHU - RouterID - NextHop - Update - RouteRequest - RouteUpdate - TSPC - HMAC + TypePad1 = Type(0) + TypePadN = Type(1) + TypeAckReq = Type(2) + TypeAck = Type(3) + TypeHello = Type(4) + TypeIHU = Type(5) + TypeRouterID = Type(6) + TypeNextHop = Type(7) + TypeUpdate = Type(8) + TypeRouteRequest = Type(9) + TypeSeqnoRequest = Type(10) + TypeTSPC = Type(11) + TypeHMAC = Type(12) ) +type TLV interface { + T() Type + L() uint8 +} + +type SubType uint8 + +const ( + SubTypePad1 = SubType(0) + SubTypePadN = SubType(1) + SubTypeDiversity = SubType(2) + SubTypeTimestamp = SubType(3) + SubTypeSourcePrefix = SubType(128) +) + +type SubTLV interface { + T() SubType + L() uint8 +} + type Scanner struct { buf []byte err error - t TLV + t Type l uint8 } @@ -34,6 +61,11 @@ func NewTLVScanner(buf []byte) *Scanner { } } +func (ts *Scanner) Reset(buf []byte) { + *ts = Scanner{} + ts.buf = buf +} + func (ts *Scanner) Scan() bool { if ts.err != nil { return false @@ -45,12 +77,23 @@ func (ts *Scanner) Scan() bool { // move beginning of the buffer to the next TLV ts.buf = ts.buf[ts.l:] - ts.t, ts.l = TLV(ts.buf[0]), 0 + ts.t, ts.l = Type(ts.buf[0]), 0 switch ts.t { - case Pad1: + case TypePad1: ts.buf = ts.buf[1:] - default: + case TypePadN, + TypeAckReq, + TypeAck, + TypeHello, + TypeIHU, + TypeRouterID, + TypeNextHop, + TypeUpdate, + TypeRouteRequest, + TypeSeqnoRequest, + TypeTSPC, + TypeHMAC: ts.l = ts.buf[1] if 2+int(ts.l) > len(ts.buf) { ts.err = fmt.Errorf("Invalid length: type %d, length %d, size %d", ts.t, ts.l, len(ts.buf)) @@ -58,7 +101,10 @@ func (ts *Scanner) Scan() bool { } // move beginning of the buffer behind the header ts.buf = ts.buf[2:] + default: + panic("unknown type") } + return true } @@ -66,9 +112,640 @@ func (ts *Scanner) Err() error { return ts.err } -func (ts *Scanner) TLV() (TLV, []byte) { - if ts.err != nil { - return 0, nil - } - return ts.t, ts.buf[:ts.l] +func (ts *Scanner) Raw() Raw { + return Raw{ts.t, ts.l, ts.buf[:ts.l:ts.l]} +} + +type PacketDecoder struct { + tlvscanner Scanner + + // parser state + routerID RouterID + nexthopv6 netaddr.IP + nexthopv4 netaddr.IP + v6 [16]byte + v4 [4]byte + + tlv TLV + subtlv []byte + err error +} + +func (s *PacketDecoder) Reset(b []byte, nexthop netaddr.IP) { + *s = PacketDecoder{} + s.tlvscanner.Reset(b) + switch { + case nexthop.Is6(): + s.nexthopv6 = nexthop + case nexthop.Is4(): + s.nexthopv4 = nexthop + } +} +func (s *PacketDecoder) Scan() bool { + if s.err != nil { + return false + } + + if !s.tlvscanner.Scan() { + if err := s.tlvscanner.Err(); err != nil { + s.err = err + } + return false + } + + raw := s.tlvscanner.Raw() + + switch raw.T() { + case TypePad1: + s.tlv, s.subtlv, s.err = Pad1FromBytes(raw.V()) + case TypePadN: + s.tlv, s.subtlv, s.err = PadNFromBytes(raw.V()) + case TypeAckReq: + s.tlv, s.subtlv, s.err = AckReqFromBytes(raw.V()) + case TypeAck: + s.tlv, s.subtlv, s.err = AckFromBytes(raw.V()) + case TypeHello: + s.tlv, s.subtlv, s.err = HelloFromBytes(raw.V()) + case TypeIHU: + s.tlv, s.subtlv, s.err = IHUFromBytes(raw.V()) + case TypeRouterID: + var rid RouterID + rid, s.subtlv, s.err = RouterIDFromBytes(raw.V()) + if s.err != nil { + break + } + + s.tlv = rid + s.routerID = rid + case TypeNextHop: + var nh NextHop + nh, s.subtlv, s.err = NextHopFromBytes(raw.V()) + if s.err != nil { + break + } + + s.tlv = nh + + switch AEFromIP(nh.Address) { + case 1: + s.nexthopv4 = nh.Address + case 2, 3: + s.nexthopv6 = nh.Address + } + case TypeUpdate: + s.tlv, s.subtlv, s.err = s.UpdateFromBytes(raw.V()) + case TypeRouteRequest: + s.tlv, s.subtlv, s.err = RouteRequestFromBytes(raw.V()) + case TypeSeqnoRequest: + s.tlv, s.subtlv, s.err = SeqnoRequestFromBytes(raw.V()) + case TypeTSPC: + s.tlv = raw + s.subtlv = nil + case TypeHMAC: + s.tlv = raw + s.subtlv = nil + default: + s.err = fmt.Errorf("Unknown TLV Type: %+v", raw) + } + + return s.err == nil +} +func (s *PacketDecoder) TLV() TLV { + if s.err != nil { + return nil + } + return s.tlv +} +func (s *PacketDecoder) Err() error { + return s.err +} + +type Raw struct { + t Type + l uint8 + v []byte +} + +func (t Raw) T() Type { + return t.t +} + +func (t Raw) L() uint8 { + return t.l +} + +func (t Raw) V() []byte { + return t.v +} + +type ErrTypeMismatch struct { + want Type + got Type +} + +func (e ErrTypeMismatch) Error() string { + return fmt.Sprintf("Error parsing TLV: Wrong type %s, wanted %s", e.got, e.want) +} + +type ErrTLVLength struct { + l int + want int + t Type +} + +func (e ErrTLVLength) Error() string { + return fmt.Sprintf("Invalid TLV length: %d, Type %q needs at least %d", e.l, e.t, e.want) +} +func assertLengthGreater(b []byte, t Type, l int) error { + if len(b) < l { + return ErrTLVLength{len(b), l, t} + } + return nil +} + +type ErrSubTLVLength struct { + l int + want int + t SubType +} + +func (e ErrSubTLVLength) Error() string { + return fmt.Sprintf("Invalid SubTLV length: %d, Type %q needs at least %d", e.l, e.t, e.want) +} +func assertSubLengthGreater(b []byte, t SubType, l int) error { + if len(b) < l { + return ErrSubTLVLength{len(b), l, t} + } + return nil +} + +type Pad1 struct{} + +func (Pad1) T() Type { + return TypePad1 +} +func (Pad1) L() uint8 { + return 8 +} +func Pad1FromBytes(b []byte) (Pad1, []byte, error) { + if err := assertLengthGreater(b, TypePad1, 0); err != nil { + return Pad1{}, b, err + } + return Pad1{}, b[1:], nil +} + +type PadN uint8 + +func (PadN) T() Type { + return TypePadN +} +func (p PadN) L() uint8 { + return 2 + uint8(p) // TODO: overflow? +} +func PadNFromBytes(b []byte) (PadN, []byte, error) { + if err := assertLengthGreater(b, TypePadN, 0); err != nil { + return PadN(0), b, err + } + return PadN(b[1]), b[1:], nil +} + +type AckReq struct { + Opaque [2]byte + Interval uint16 +} + +func (AckReq) T() Type { + return TypeAckReq +} +func (a AckReq) L() uint8 { + return 8 +} +func AckReqFromBytes(b []byte) (AckReq, []byte, error) { + if err := assertLengthGreater(b, TypeAckReq, 6); err != nil { + return AckReq{}, b, err + } + var ar AckReq + + ar.Opaque[0] = b[2] + ar.Opaque[1] = b[3] + ar.Interval = uint16(b[4])<<8 | uint16(b[5]) + + return ar, b[6:], nil +} + +type Ack struct { + Opaque uint16 +} + +func (Ack) T() Type { + return TypeAck +} + +func (Ack) L() uint8 { + return 4 +} + +func AckFromBytes(b []byte) (Ack, []byte, error) { + if err := assertLengthGreater(b, TypeAck, 2); err != nil { + return Ack{}, b, err + } + + return Ack{uint16(b[0])<<8 | uint16(b[1])}, b[4:], nil +} + +type Hello struct { + Flags uint16 + Seqno uint16 + Interval uint16 +} + +func (Hello) T() Type { + return TypeHello +} +func (Hello) L() uint8 { + return 8 +} +func HelloFromBytes(b []byte) (Hello, []byte, error) { + if err := assertLengthGreater(b, TypeHello, 6); err != nil { + return Hello{}, b, err + } + var h Hello + + h.Flags = uint16(b[0])<<8 | uint16(b[1]) + h.Seqno = uint16(b[2])<<8 | uint16(b[3]) + h.Interval = uint16(b[4])<<8 | uint16(b[5]) + + return h, b[6:], nil +} + +type IHU struct { + // AE uint8 + // reserved uint8 + Rxcost uint16 + Interval uint16 + Address netaddr.IP +} + +func (IHU) T() Type { + return TypeIHU +} +func (i IHU) AE() uint8 { + return AEFromIP(i.Address) +} +func (i IHU) L() uint8 { + switch i.AE() { + case 0: + return 6 // Header only + case 1: + return 6 + 4 // IPv4 - no compression + case 2: + return 6 + 16 // IPv6 - no compression + case 3: + return 6 + 8 // IPv6 Link Local + } + panic("invalid AE field") +} +func IHUFromBytes(b []byte) (IHU, []byte, error) { + if err := assertLengthGreater(b, TypeIHU, 6); err != nil { + return IHU{}, b, err + } + + var ihu IHU + var err error + + ae := b[0] + ihu.Rxcost = uint16(b[2])<<8 | uint16(b[3]) + ihu.Interval = uint16(b[4])<<8 | uint16(b[5]) + ihu.Address, b, err = ipFromBytes(ae, b[6:]) + + return ihu, b, err +} + +type RouterID [8]byte + +var ( + ErrRouterIDZeros = errors.New("Invalid RouterID: consists of all zeros") + ErrRouterIDOnes = errors.New("Invalid RouterID: consists of all ones") +) + +func RouterIDFromBytes(b []byte) (RouterID, []byte, error) { + if err := assertLengthGreater(b, TypeRouterID, 10); err != nil { + return RouterID{}, b, err + } + + // skip 2 reserved bytes + var rid RouterID + copy(rid[:], b[2:]) + b = b[10:] + + if rid == (RouterID{}) { + return RouterID{}, b, ErrRouterIDZeros + } + if rid == (RouterID{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}) { + return RouterID{}, b, ErrRouterIDOnes + } + + return rid, b, nil +} + +func (RouterID) T() Type { + return TypeRouterID +} + +func (RouterID) L() uint8 { + return 8 +} + +func (r RouterID) String() string { + var buf bytes.Buffer + buf.Grow(8*3 - 1) + for i, b := range r { + if i > 0 { + buf.WriteByte(':') + } + fmt.Fprintf(&buf, "%02x", b) + } + return buf.String() +} + +type NextHop struct { + // AE uint8 + // reserved uint8 + Address netaddr.IP +} + +func (NextHop) T() Type { + return TypeNextHop +} +func (n NextHop) L() uint8 { + switch AEFromIP(n.Address) { + case 1: + return 2 + 4 // IPv4 - no compression + case 2: + return 2 + 16 // IPv6 - no compression + case 3: + return 2 + 8 // IPv6 Link Local + } + panic("invalid AE field") +} +func NextHopFromBytes(b []byte) (NextHop, []byte, error) { + if err := assertLengthGreater(b, TypeNextHop, 6); err != nil { + return NextHop{}, b, err + } + + var nh NextHop + var err error + ae := b[0] + nh.Address, b, err = ipFromBytes(ae, b[2:]) + return nh, b, err +} + +type Update struct { + // AE uint8 + Flags uint8 + // Plen uint8 + Omitted uint8 + Interval uint16 + Seqno uint16 + Metric uint16 + Prefix netaddr.IPPrefix + NextHop netaddr.IP +} + +func (Update) T() Type { + return TypeUpdate +} + +func (u Update) L() uint8 { + return 10 + psizeFromPlen(u.Prefix.Bits()) +} + +func (u Update) FormatHeader() string { + return fmt.Sprintf("Flags 0x%02x Omitted %2d Interval %4d Seqno %5d Metric %5d NextHop %s", + u.Flags, u.Omitted, u.Interval, u.Seqno, u.Metric, u.NextHop, + ) +} + +func (s *PacketDecoder) UpdateFromBytes(b []byte) (Update, []byte, error) { + if err := assertLengthGreater(b, TypeUpdate, 10); err != nil { + return Update{}, b, err + } + var u Update + var err error + + ae := b[0] + u.Flags = b[1] + plen := b[2] + u.Omitted = b[3] + u.Interval = uint16(b[4])<<8 | uint16(b[5]) + u.Seqno = uint16(b[6])<<8 | uint16(b[7]) + u.Metric = uint16(b[8])<<8 | uint16(b[8]) + + b = b[10:] + switch ae { + case 0: + case 1: + u.Prefix, b, err = prefixV4Default(s.v4, plen, u.Omitted, b) + if u.Flags&0x80 > 0 { + s.v4 = u.Prefix.IP().As4() + } + u.NextHop = s.nexthopv4 + case 2: + u.Prefix, b, err = prefixV6Default(s.v6, plen, u.Omitted, b) + if u.Flags&0x80 > 0 { + s.v6 = u.Prefix.IP().As16() + } + u.NextHop = s.nexthopv6 + case 3: + u.Prefix, b, err = prefixV6LL(b) + u.NextHop = s.nexthopv6 + } + + return u, b, err +} + +func AEFromPrefix(p netaddr.IPPrefix) uint8 { + return AEFromIP(p.IP()) +} +func AEFromIP(p netaddr.IP) uint8 { + if p.IsZero() { + return 0 + } + if p.Is4() { + return 1 + } + if p.IsLinkLocalUnicast() { + return 3 + } + if p.Is6() { + return 2 + } + panic("unknown AE") +} + +type RouteRequest struct { + // AE uint8 + // plen uint8 + Prefix netaddr.IPPrefix +} + +func (r RouteRequest) AE() uint8 { + return AEFromPrefix(r.Prefix) +} +func (RouteRequest) T() Type { + return TypeRouteRequest +} +func (r RouteRequest) L() uint8 { + return 4 + psizeFromPlen(r.Prefix.Bits()) +} +func RouteRequestFromBytes(b []byte) (RouteRequest, []byte, error) { + if err := assertLengthGreater(b, TypeRouteRequest, 2); err != nil { + return RouteRequest{}, b, err + } + var rr RouteRequest + var err error + + rr.Prefix, b, err = prefixUncompressed(b[0], b[1], b[2:]) + + return rr, b, err +} + +type SeqnoRequest struct { + // AE uint8 + // Plen uint8 + Seqno uint16 + HopCount uint8 + RouterID RouterID + Prefix netaddr.IPPrefix +} + +func (SeqnoRequest) T() Type { + return TypeSeqnoRequest +} +func (r SeqnoRequest) L() uint8 { + return 14 + psizeFromPlen(r.Prefix.Bits()) +} +func SeqnoRequestFromBytes(b []byte) (SeqnoRequest, []byte, error) { + if err := assertLengthGreater(b, TypeSeqnoRequest, 14); err != nil { + return SeqnoRequest{}, b, err + } + + var err error + var sr SeqnoRequest + + ae := b[0] + plen := b[1] + sr.Seqno = uint16(b[2])<<8 | uint16(b[3]) + sr.HopCount = b[4] + copy(sr.RouterID[:], b[6:]) + sr.Prefix, b, err = prefixUncompressed(ae, plen, b[14:]) + + return sr, b, err +} + +type SourcePrefix struct { + netaddr.IPPrefix +} + +func (SourcePrefix) T() SubType { + return SubTypeSourcePrefix +} +func (s SourcePrefix) L() uint8 { + return psizeFromPlen(s.Bits()) +} +func SourcePrefixFromBytes(b []byte) (SourcePrefix, []byte, error) { + if err := assertSubLengthGreater(b, SubTypeSourcePrefix, 2); err != nil { + return SourcePrefix{}, b, err + } + + plen := b[0] // TODO: error on 0 + b = b[1:] + pfx, b, err := prefixUncompressed(2, plen, b) + + return SourcePrefix{pfx}, b, err +} + +func psizeFromPlen(plen uint8) uint8 { + return uint8((uint16(plen) + 7) / 8) +} +func prefixUncompressed(ae uint8, plen uint8, b []byte) (netaddr.IPPrefix, []byte, error) { + if len(b) < int(psizeFromPlen(plen)) { + return netaddr.IPPrefix{}, b, fmt.Errorf("plen too large") + } + + switch ae { + case 0: + return prefixWildcard() + case 1: + return prefixV4(plen, b) + case 2: + return prefixV6(plen, b) + case 3: + return prefixV6LL(b) + default: + return netaddr.IPPrefix{}, b, fmt.Errorf("Invalid AE %d", ae) + } +} + +func prefixWildcard() (netaddr.IPPrefix, []byte, error) { + return netaddr.IPPrefix{}, nil, nil +} + +func prefixV4(plen uint8, b []byte) (netaddr.IPPrefix, []byte, error) { + return prefixV4Default([4]byte{}, plen, 0, b) +} +func prefixV4Default(ip4default [4]byte, plen uint8, omit uint8, b []byte) (netaddr.IPPrefix, []byte, error) { + var ip4 [4]byte + psize := psizeFromPlen(plen) - omit + copy(ip4[:], ip4default[:omit]) + copy(ip4[omit:], b[:psize]) + return netaddr.IPPrefixFrom(netaddr.IPFrom4(ip4), plen), b[psize:], nil +} +func prefixV6(plen uint8, b []byte) (netaddr.IPPrefix, []byte, error) { + return prefixV6Default([16]byte{}, plen, 0, b) +} +func prefixV6Default(ip6default [16]byte, plen uint8, omit uint8, b []byte) (netaddr.IPPrefix, []byte, error) { + var ip6 [16]byte + psize := psizeFromPlen(plen) - omit + copy(ip6[:], ip6default[:omit]) + copy(ip6[omit:], b[:psize]) + return netaddr.IPPrefixFrom(netaddr.IPv6Raw(ip6), plen), b[psize:], nil +} +func prefixV6LL(b []byte) (netaddr.IPPrefix, []byte, error) { + var ip6ll [16]byte + ip6ll[0] = 0xfe + ip6ll[1] = 0x80 + copy(ip6ll[8:], b[:8]) + return netaddr.IPPrefixFrom(netaddr.IPv6Raw(ip6ll), 8), b[8:], nil +} +func ipFromBytes(ae uint8, b []byte) (netaddr.IP, []byte, error) { + switch ae { + case 0: + return netaddr.IP{}, b, nil + case 1: + if len(b) < 4 { + return netaddr.IP{}, b, fmt.Errorf("Not enough bytes for v4 address: %d", len(b)) + } + var ip4 [4]byte + copy(ip4[:], b[:4]) + return netaddr.IPFrom4(ip4), b[4:], nil + case 2: + if len(b) < 16 { + return netaddr.IP{}, b, fmt.Errorf("Not enough bytes for v6 address: %d", len(b)) + } + var ip6 [16]byte + copy(ip6[:], b[:16]) + return netaddr.IPv6Raw(ip6), b[16:], nil + case 3: + if len(b) < 8 { + return netaddr.IP{}, b, fmt.Errorf("Not enough bytes for v6ll address: %d", len(b)) + } + var ip6ll [16]byte + ip6ll[0] = 0xfe + ip6ll[1] = 0x80 + copy(ip6ll[8:], b[:8]) + return netaddr.IPv6Raw(ip6ll), b[8:], nil + default: + return netaddr.IP{}, b, fmt.Errorf("Invalid AE %d", ae) + } } diff --git a/tlv/tlv_string.go b/tlv/tlv_string.go deleted file mode 100644 index 167b85e..0000000 --- a/tlv/tlv_string.go +++ /dev/null @@ -1,35 +0,0 @@ -// Code generated by "stringer -type=TLV"; DO NOT EDIT. - -package tlv - -import "strconv" - -func _() { - // An "invalid array index" compiler error signifies that the constant values have changed. - // Re-run the stringer command to generate them again. - var x [1]struct{} - _ = x[Pad1-0] - _ = x[PadN-1] - _ = x[AckReq-2] - _ = x[Ack-3] - _ = x[Hello-4] - _ = x[IHU-5] - _ = x[RouterID-6] - _ = x[NextHop-7] - _ = x[Update-8] - _ = x[RouteRequest-9] - _ = x[RouteUpdate-10] - _ = x[TSPC-11] - _ = x[HMAC-12] -} - -const _TLV_name = "Pad1PadNAckReqAckHelloIHURouterIDNextHopUpdateRouteRequestRouteUpdateTSPCHMAC" - -var _TLV_index = [...]uint8{0, 4, 8, 14, 17, 22, 25, 33, 40, 46, 58, 69, 73, 77} - -func (i TLV) String() string { - if i >= TLV(len(_TLV_index)-1) { - return "TLV(" + strconv.FormatInt(int64(i), 10) + ")" - } - return _TLV_name[_TLV_index[i]:_TLV_index[i+1]] -} diff --git a/tlv/tlv_test.go b/tlv/tlv_test.go new file mode 100644 index 0000000..3946f08 --- /dev/null +++ b/tlv/tlv_test.go @@ -0,0 +1,41 @@ +package tlv + +import ( + "testing" +) + +func TestRouterIDFailZeros(t *testing.T) { + zeros := [10]byte{} + if _, _, err := RouterIDFromBytes(zeros[:]); err != ErrRouterIDZeros { + t.Error("RouterIDFromBytes should fail with ErrRouterIDZeros") + } +} + +func TestRouterIDFailOnes(t *testing.T) { + ones := [10]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff} + if _, _, err := RouterIDFromBytes(ones[:]); err != ErrRouterIDOnes { + t.Error("RouterIDFromBytes should fail with ErrRouterIDOnes") + } +} + +func TestRouterIDFailLength(t *testing.T) { + testbytes := [300]byte{} + for i := 0; i <= len(testbytes); i++ { + _, _, err := RouterIDFromBytes(testbytes[:i]) + l, isErrRouterIDLength := err.(ErrTLVLength) + + switch { + case i >= 10: + if isErrRouterIDLength { + t.Error("RouterIDFromBytes returns ErrRouterIDLength for correct length data") + } + default: + if l.l != i { + t.Errorf("RouterIDFromBytes returns wrong length %d, expected %d", l, i) + } + if !isErrRouterIDLength { + t.Errorf("RouterIDFromBytes returns wrong error for packet of wrong length %d", i) + } + } + } +} diff --git a/tlv/type_string.go b/tlv/type_string.go new file mode 100644 index 0000000..76bdc65 --- /dev/null +++ b/tlv/type_string.go @@ -0,0 +1,35 @@ +// Code generated by "stringer -type=Type -trimprefix Type"; DO NOT EDIT. + +package tlv + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[TypePad1-0] + _ = x[TypePadN-1] + _ = x[TypeAckReq-2] + _ = x[TypeAck-3] + _ = x[TypeHello-4] + _ = x[TypeIHU-5] + _ = x[TypeRouterID-6] + _ = x[TypeNextHop-7] + _ = x[TypeUpdate-8] + _ = x[TypeRouteRequest-9] + _ = x[TypeSeqnoRequest-10] + _ = x[TypeTSPC-11] + _ = x[TypeHMAC-12] +} + +const _Type_name = "Pad1PadNAckReqAckHelloIHURouterIDNextHopUpdateRouteRequestSeqnoRequestTSPCHMAC" + +var _Type_index = [...]uint8{0, 4, 8, 14, 17, 22, 25, 33, 40, 46, 58, 70, 74, 78} + +func (i Type) String() string { + if i >= Type(len(_Type_index)-1) { + return "Type(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _Type_name[_Type_index[i]:_Type_index[i+1]] +} diff --git a/tlv/update.go b/tlv/update.go deleted file mode 100644 index 7680e51..0000000 --- a/tlv/update.go +++ /dev/null @@ -1,64 +0,0 @@ -package tlv - -import ( - "bytes" - "encoding/binary" - "net" -) - -type PacketUpdateHeader struct { - AE uint8 - Flags uint8 - Plen uint8 - Omitted uint8 - Interval uint16 - Seqno uint16 - Metric uint16 -} - -type PacketUpdate struct { - PacketUpdateHeader - Prefix net.IPNet -} - -type PacketDecoder struct { - routerID [8]byte - v4 [4]byte - v6 [16]byte -} - -func (s *PacketDecoder) UpdateFromBytes(b []byte) (PacketUpdate, error) { - var u PacketUpdate - - r := bytes.NewReader(b[:10]) - err := binary.Read(r, binary.BigEndian, &u.PacketUpdateHeader) - - b = b[10:] - - psize := (u.Plen+7)/8 - u.Omitted - switch u.AE { - case 0: - case 1: - u.Prefix.Mask = net.CIDRMask(int(u.Plen), 4*8) - u.Prefix.IP = append([]byte{}, s.v4[:]...) - copy(u.Prefix.IP[u.Omitted:], b[:psize]) - if u.Flags&0x80 > 0 { - copy(s.v4[:], u.Prefix.IP) - } - case 2: - u.Prefix.Mask = net.CIDRMask(int(u.Plen), 16*8) - u.Prefix.IP = append([]byte{}, s.v6[:]...) - copy(u.Prefix.IP[u.Omitted:], b[:psize]) - if u.Flags&0x80 > 0 { - copy(s.v6[:], u.Prefix.IP) - } - case 3: - u.Prefix.Mask = net.CIDRMask(8, 16*8) - u.Prefix.IP = make([]byte, 16) - u.Prefix.IP[0] = 0xfe - u.Prefix.IP[1] = 0x80 - copy(u.Prefix.IP[8:], b[:8]) - } - - return u, err -}