From 1d913cb5a742dd60f7e521fc91542bf8063039f4 Mon Sep 17 00:00:00 2001 From: Johannes Kimmel Date: Wed, 8 Dec 2021 11:41:29 +0100 Subject: [PATCH] init --- .gitignore | 28 +++++++++++ go.mod | 5 ++ go.sum | 7 +++ main.go | 126 ++++++++++++++++++++++++++++++++++++++++++++++ tlv/tlv.go | 74 +++++++++++++++++++++++++++ tlv/tlv_string.go | 35 +++++++++++++ tlv/update.go | 64 +++++++++++++++++++++++ 7 files changed, 339 insertions(+) create mode 100644 .gitignore create mode 100644 go.mod create mode 100644 go.sum create mode 100644 main.go create mode 100644 tlv/tlv.go create mode 100644 tlv/tlv_string.go create mode 100644 tlv/update.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c2f26d0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,28 @@ + +# Created by https://www.toptal.com/developers/gitignore/api/go +# Edit at https://www.toptal.com/developers/gitignore?templates=go + +### Go ### +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +# vendor/ + +### Go Patch ### +/vendor/ +/Godeps/ + +# End of https://www.toptal.com/developers/gitignore/api/go + +abbel diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..a510e4e --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module git.freifunk-franken.de/jkimmel/abbel + +go 1.15 + +require golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..bea54c7 --- /dev/null +++ b/go.sum @@ -0,0 +1,7 @@ +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/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +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= diff --git a/main.go b/main.go new file mode 100644 index 0000000..77279aa --- /dev/null +++ b/main.go @@ -0,0 +1,126 @@ +package main + +import ( + "fmt" + "log" + "net" + + "git.freifunk-franken.de/jkimmel/abbel/tlv" + "golang.org/x/net/ipv6" +) + +type options struct { + Group net.IP + Port uint16 + Ifs []net.Interface +} + +func parseOpts() (options, error) { + var opt options + var err error + + opt.Group = net.ParseIP("ff02::1:6") + opt.Port = 6696 + ifi, err := net.InterfaceByName("babel") + if err != nil { + return opt, err + } + opt.Ifs = []net.Interface{*ifi} + + return opt, err +} + +func run(opt options) error { + var err error + uc, err := net.ListenUDP("udp6", &net.UDPAddr{Port: int(opt.Port)}) + if err != nil { + return err + } + defer uc.Close() + up := ipv6.NewPacketConn(uc) + + if err := up.SetControlMessage(ipv6.FlagDst, true); err != nil { + return err + } + + for ifi := range opt.Ifs { + if err := up.JoinGroup(&opt.Ifs[ifi], &net.UDPAddr{IP: opt.Group}); err != nil { + return err + } + } + + b := make([]byte, 1500) + for { + n, rcm, src, err := up.ReadFrom(b) + if err != nil { + return err + } + + _ = rcm + + magic := b[0] + version := b[1] + length := uint16(b[2])<<8 + uint16(b[3]) + + if magic != 42 { + 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 { + case tlv.Update: + u, err = s.UpdateFromBytes(v) + if err != nil { + fmt.Println(u.PacketUpdateHeader, &u.Prefix, err) + } else { + if u.PacketUpdateHeader.Metric == 0xFFFF { + fmt.Print("\x1B[1m") + } + fmt.Printf("%43s: %+v\x1B[0m\n", &u.Prefix, u.PacketUpdateHeader) + } + default: + fmt.Printf(" T: %12s (%2d), L: %3d\n", t, t, len(v)) + } + + } + if ts.Err() != nil { + fmt.Println(err) + } + + //{ + // var bbuf bytes.Buffer + // lzww := lzw.NewWriter(&bbuf, lzw.LSB, 8) + // lzww.Write(b[:n]) + // lzww.Close() + // fmt.Println(n, len(bbuf.Bytes())) + //} + //{ + // var bbuf bytes.Buffer + // gzw, _ := gzip.NewWriterLevel(&bbuf, 9) + // gzw.Write(b[:n]) + // gzw.Close() + // fmt.Println(n, len(bbuf.Bytes())) + //} + } + + return nil +} + +func main() { + opt, err := parseOpts() + if err != nil { + log.Fatal(err) + } + if err := run(opt); err != nil { + log.Fatal(err) + } +} diff --git a/tlv/tlv.go b/tlv/tlv.go new file mode 100644 index 0000000..27b5ca3 --- /dev/null +++ b/tlv/tlv.go @@ -0,0 +1,74 @@ +//go:generate stringer -type=TLV +package tlv + +import "fmt" + +type TLV uint8 + +const ( + Pad1 TLV = iota + PadN + AckReq + Ack + Hello + IHU + RouterID + NextHop + Update + RouteRequest + RouteUpdate + TSPC + HMAC +) + +type Scanner struct { + buf []byte + err error + t TLV + l uint8 +} + +func NewTLVScanner(buf []byte) *Scanner { + return &Scanner{ + buf: buf, + } +} + +func (ts *Scanner) Scan() bool { + if ts.err != nil { + return false + } + + if len(ts.buf) <= int(ts.l) { + return false + } + + // move beginning of the buffer to the next TLV + ts.buf = ts.buf[ts.l:] + ts.t, ts.l = TLV(ts.buf[0]), 0 + + switch ts.t { + case Pad1: + ts.buf = ts.buf[1:] + default: + 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)) + return false + } + // move beginning of the buffer behind the header + ts.buf = ts.buf[2:] + } + return true +} + +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] +} diff --git a/tlv/tlv_string.go b/tlv/tlv_string.go new file mode 100644 index 0000000..167b85e --- /dev/null +++ b/tlv/tlv_string.go @@ -0,0 +1,35 @@ +// 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/update.go b/tlv/update.go new file mode 100644 index 0000000..7680e51 --- /dev/null +++ b/tlv/update.go @@ -0,0 +1,64 @@ +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 +}