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 }