abbel/packet/packet.go

112 lines
2.1 KiB
Go

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 Listen(group string, port uint16, ifaces ...string) (Conn, error) {
conn, err := ListenPort(port)
if err != nil {
return Conn{}, err
}
for _, iface := range ifaces {
if err = conn.JoinGroup(iface, group); err != nil {
conn.Close()
return Conn{}, err
}
}
return conn, nil
}
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|ipv6.FlagTrafficClass|ipv6.FlagHopLimit|ipv6.FlagPathMTU,
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 Validate(b []byte) ([]byte, error) {
n := len(b)
if n < BabelPacketHeaderSize {
return nil, 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, fmt.Errorf("Invalid magic number %d", magic)
}
if version != BabelVersion {
return nil, fmt.Errorf("Unsupported version number %d", version)
}
if int(length) > len(b) {
return nil, fmt.Errorf("Invalid length for packet of size %d: %d", n, length)
}
return b, nil
}
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, err = Validate(b[:n])
if err != nil {
return nil, netaddr.IP{}, 0, err
}
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
}