netboot/dhcp6/conn.go
2018-02-05 12:33:17 -08:00

129 lines
2.7 KiB
Go

package dhcp6
import (
"io"
"net"
"time"
"golang.org/x/net/ipv6"
"fmt"
)
type conn interface {
io.Closer
Recv([]byte) (b []byte, addr *net.UDPAddr, ifidx int, err error)
Send(b []byte, addr *net.UDPAddr, ifidx int) error
SetReadDeadline(t time.Time) error
SetWriteDeadline(t time.Time) error
}
type Conn struct {
conn *ipv6.PacketConn
group net.IP
ifi *net.Interface
listenAddress string
listenPort string
}
func NewConn(addr, port string) (*Conn, error) {
ifi, err := InterfaceByAddress(addr)
if err != nil {
return nil, err
}
group := net.ParseIP("ff02::1:2")
c, err := net.ListenPacket("udp6", "[::]:" + port)
if err != nil {
return nil, err
}
pc := ipv6.NewPacketConn(c)
if err := pc.JoinGroup(ifi, &net.UDPAddr{IP: group}); err != nil {
pc.Close()
return nil, err
}
if err := pc.SetControlMessage(ipv6.FlagSrc | ipv6.FlagDst, true); err != nil {
pc.Close()
return nil, err
}
return &Conn{
conn: pc,
group: group,
ifi: ifi,
listenAddress: addr,
listenPort: port,
}, nil
}
func (c *Conn) Close() error {
return c.conn.Close()
}
func InterfaceByAddress(ifAddr string) (*net.Interface, error) {
allIfis, err := net.Interfaces()
if err != nil {
return nil, fmt.Errorf("Error getting network interface information: %s", err)
}
for _, ifi := range allIfis {
addrs, err := ifi.Addrs()
if err != nil {
return nil, fmt.Errorf("Error getting network interface address information: %s", err)
}
for _, addr := range addrs {
if addrToIP(addr).String() == ifAddr {
return &ifi, nil
}
}
}
return nil, fmt.Errorf("Couldn't find an interface with address %s", ifAddr)
}
func addrToIP(a net.Addr) net.IP {
var ip net.IP
switch v := a.(type) {
case *net.IPAddr:
ip = v.IP
case *net.IPNet:
ip = v.IP
}
return ip
}
func (c *Conn) RecvDHCP() (*Packet, net.IP, error) {
b := make([]byte, 1500)
for {
n, rcm, _, err := c.conn.ReadFrom(b)
if err != nil {
return nil, nil, err
}
if c.ifi.Index != 0 && rcm.IfIndex != c.ifi.Index {
continue
}
if !rcm.Dst.IsMulticast() || !rcm.Dst.Equal(c.group) {
continue // unknown group, discard
}
pkt, err := MakePacket(b, n)
if err != nil {
return nil, nil, err
}
return pkt, rcm.Src, nil
}
}
func (c *Conn) SendDHCP(dst net.IP, p []byte) error {
dstAddr, err := net.ResolveUDPAddr("udp6", fmt.Sprintf("[%s]:%s", dst.String() + "%en0", "546"))
if err != nil {
return fmt.Errorf("Error resolving ipv6 address %s: %s", dst.String(), err)
}
_, err = c.conn.WriteTo(p, nil, dstAddr)
if err != nil {
return fmt.Errorf("Error sending a reply to %s: %s", dst.String(), err)
}
return nil
}
func (c *Conn) SourceHardwareAddress() net.HardwareAddr {
return c.ifi.HardwareAddr
}