package dhcpd import ( "net" "github.com/joomcode/errorx" "golang.org/x/net/ipv4" ) // filterConn listens to 0.0.0.0:67, but accepts packets only from specific interface // This is necessary for DHCP daemon to work, since binding to IP address doesn't // us access to see Discover/Request packets from clients. // // TODO: on windows, controlmessage does not work, try to find out another way // https://github.com/golang/net/blob/master/ipv4/payload.go#L13 type filterConn struct { iface net.Interface conn *ipv4.PacketConn } func newFilterConn(iface net.Interface, address string) (*filterConn, error) { c, err := net.ListenPacket("udp4", address) if err != nil { return nil, errorx.Decorate(err, "Couldn't listen to %s on UDP4", address) } p := ipv4.NewPacketConn(c) err = p.SetControlMessage(ipv4.FlagInterface, true) if err != nil { c.Close() return nil, errorx.Decorate(err, "Couldn't set control message FlagInterface on connection") } return &filterConn{iface: iface, conn: p}, nil } func (f *filterConn) ReadFrom(b []byte) (int, net.Addr, error) { for { // read until we find a suitable packet n, cm, addr, err := f.conn.ReadFrom(b) if err != nil { return 0, addr, errorx.Decorate(err, "Error when reading from socket") } if cm == nil { // no controlmessage was passed, so pass the packet to the caller return n, addr, nil } if cm.IfIndex == f.iface.Index { return n, addr, nil } // packet doesn't match criteria, drop it } } func (f *filterConn) WriteTo(b []byte, addr net.Addr) (int, error) { cm := ipv4.ControlMessage{ IfIndex: f.iface.Index, } return f.conn.WriteTo(b, &cm, addr) } func (f *filterConn) Close() error { return f.conn.Close() }