This commit is contained in:
Johannes Kimmel 2021-12-08 11:41:29 +01:00
commit 1d913cb5a7
7 changed files with 339 additions and 0 deletions

28
.gitignore vendored Normal file
View File

@ -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

5
go.mod Normal file
View File

@ -0,0 +1,5 @@
module git.freifunk-franken.de/jkimmel/abbel
go 1.15
require golang.org/x/net v0.0.0-20210226172049-e18ecbb05110

7
go.sum Normal file
View File

@ -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=

126
main.go Normal file
View File

@ -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)
}
}

74
tlv/tlv.go Normal file
View File

@ -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]
}

35
tlv/tlv_string.go Normal file
View File

@ -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]]
}

64
tlv/update.go Normal file
View File

@ -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
}