diff --git a/main.go b/main.go index 414f469..5a58d4e 100644 --- a/main.go +++ b/main.go @@ -3,92 +3,52 @@ package main import ( "fmt" "log" - "net" + "git.freifunk-franken.de/jkimmel/abbel/packet" "git.freifunk-franken.de/jkimmel/abbel/tlv" - "golang.org/x/net/ipv6" - "inet.af/netaddr" ) type options struct { - Group net.IP + Group string Port uint16 - Ifs []net.Interface + Ifs []string } 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 = options{ + Group: "ff02:0:0:0:0:0:1:6", + Port: 6696, + Ifs: []string{"babel"}, } - opt.Ifs = []net.Interface{*ifi} 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)}) + conn, err := packet.ListenPort(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 { + defer conn.Close() + for _, iface := range opt.Ifs { + if err = conn.JoinGroup(iface, opt.Group); err != nil { return err } } buf := [4096]byte{} + var s tlv.PacketDecoder for { - b, src, err := readBabelPacket(up, buf[:]) + b, src, ifindex, err := conn.ReadFrom(buf[:]) if err != nil { fmt.Println("Skipping packet:", err) continue } - var s tlv.PacketDecoder - s.Reset(b, src) + s.Reset(b, src, ifindex) for s.Scan() { switch t := s.TLV().(type) { case tlv.NextHop: diff --git a/packet/packet.go b/packet/packet.go new file mode 100644 index 0000000..37f4369 --- /dev/null +++ b/packet/packet.go @@ -0,0 +1,86 @@ +package packet + +import ( + "fmt" + "net" + + "golang.org/x/net/ipv6" + "inet.af/netaddr" +) + +const ( + BabelMagic = 42 + BabelVersion = 2 + BabelPacketHeaderSize = 4 +) + +type Conn struct { + v6pc *ipv6.PacketConn +} + +func ListenPort(port uint16) (Conn, error) { + var c Conn + + uc, err := net.ListenUDP("udp6", &net.UDPAddr{Port: int(port)}) + if err != nil { + return c, err + } + c.v6pc = ipv6.NewPacketConn(uc) + + return c, c.v6pc.SetControlMessage(ipv6.FlagDst, true) +} + +func (c Conn) Close() error { + return c.v6pc.Close() +} + +func (c Conn) JoinGroup(ifname string, addr string) error { + ifi, err := net.InterfaceByName(ifname) + if err != nil { + return err + } + + ip, err := netaddr.ParseIP(addr) + if err != nil { + return err + } + + return c.v6pc.JoinGroup(ifi, &net.UDPAddr{IP: ip.IPAddr().IP}) +} + +func (c Conn) ReadFrom(b []byte) (body []byte, src netaddr.IP, ifindex int, err error) { + n, rcm, _, err := c.v6pc.ReadFrom(b) + if err != nil { + return nil, netaddr.IP{}, 0, err + } + + b = b[:n] + if len(b) < BabelPacketHeaderSize { + return nil, netaddr.IP{}, 0, fmt.Errorf("Packet too short: %d", len(b)) + } + + magic := b[0] + version := b[1] + length := uint16(b[2])<<8 + uint16(b[3]) + b = b[4:] + + if magic != BabelMagic { + return nil, netaddr.IP{}, 0, fmt.Errorf("Invalid magic number %d", magic) + } + if version != BabelVersion { + return nil, netaddr.IP{}, 0, fmt.Errorf("Unsupported version number %d", version) + } + if int(length) > len(b) { + return nil, netaddr.IP{}, 0, fmt.Errorf("Invalid length for packet of size %d: %d", n, length) + } + + var ok bool + src, ok = netaddr.FromStdIPRaw(rcm.Src) + + if !ok { + return nil, netaddr.IP{}, 0, fmt.Errorf("Invalid src address %q", rcm.Src) + } + + return b, src, rcm.IfIndex, err + +} diff --git a/tlv/tlv.go b/tlv/tlv.go index 74a4523..3082a91 100644 --- a/tlv/tlv.go +++ b/tlv/tlv.go @@ -183,7 +183,7 @@ type PacketDecoder struct { } // Reset clears the parser state and sets a new byte slice to work on. -func (s *PacketDecoder) Reset(b []byte, nexthop netaddr.IP) { +func (s *PacketDecoder) Reset(b []byte, nexthop netaddr.IP, ifindex int) { *s = PacketDecoder{} s.tlvscanner.Reset(b) switch {