87 lines
1.7 KiB
Go
87 lines
1.7 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 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
|
||
|
|
||
|
}
|