Files
nmon/internal/simagent/v3.go

84 lines
3.3 KiB
Go

package simagent
import (
"encoding/binary"
"fmt"
"time"
"github.com/gosnmp/gosnmp"
)
const timeWindow = 150 // seconds, RFC 3414
func (a *Agent) initUsers() error {
tbl := gosnmp.NewSnmpV3SecurityParametersTable(gosnmp.Logger{})
// The empty user decodes engine-ID discovery probes.
probe := &gosnmp.UsmSecurityParameters{AuthenticationProtocol: gosnmp.NoAuth, PrivacyProtocol: gosnmp.NoPriv}
if err := tbl.Add("", probe); err != nil {
return err
}
for _, u := range a.cfg.V3 {
if u.Name == "" || u.AuthPass == "" || u.PrivPass == "" {
return fmt.Errorf("simagent: v3 user %q needs a name, auth and priv passphrases", u.Name)
}
sp := &gosnmp.UsmSecurityParameters{
AuthoritativeEngineID: a.engineID, UserName: u.Name,
AuthenticationProtocol: u.AuthProto, AuthenticationPassphrase: u.AuthPass,
PrivacyProtocol: u.PrivProto, PrivacyPassphrase: u.PrivPass,
}
if err := tbl.Add(u.Name, sp); err != nil {
return fmt.Errorf("simagent: v3 user %q: %w", u.Name, err)
}
}
a.dec.SetTrapSecurityParametersTable(tbl)
return nil
}
func engineTime(elapsed time.Duration) uint32 { return uint32(elapsed / time.Second) }
func (a *Agent) inTimeWindow(req *gosnmp.SnmpPacket, elapsed time.Duration) bool {
sp := req.SecurityParameters.(*gosnmp.UsmSecurityParameters)
d := int64(sp.AuthoritativeEngineTime) - int64(engineTime(elapsed))
return sp.AuthoritativeEngineBoots == engineBoots && d >= -timeWindow && d <= timeWindow
}
// usmFor returns the request's security parameters (user, keys) restamped
// with this engine's identity and time.
func (a *Agent) usmFor(req *gosnmp.SnmpPacket, elapsed time.Duration, salt bool) *gosnmp.UsmSecurityParameters {
sp := req.SecurityParameters.(*gosnmp.UsmSecurityParameters).Copy().(*gosnmp.UsmSecurityParameters)
sp.AuthoritativeEngineID = a.engineID
sp.AuthoritativeEngineBoots = engineBoots
sp.AuthoritativeEngineTime = engineTime(elapsed)
sp.AuthenticationParameters = ""
sp.PrivacyParameters = nil
if salt {
sp.PrivacyParameters = binary.BigEndian.AppendUint64(nil, a.salt.Add(1))
}
return sp
}
func (a *Agent) report(
req *gosnmp.SnmpPacket, flags gosnmp.SnmpV3MsgFlags, sp *gosnmp.UsmSecurityParameters, oid string, n uint64,
) *gosnmp.SnmpPacket {
return &gosnmp.SnmpPacket{
Version: gosnmp.Version3, MsgID: req.MsgID, MsgFlags: flags, SecurityModel: gosnmp.UserSecurityModel,
SecurityParameters: sp, ContextEngineID: a.engineID, ContextName: req.ContextName,
PDUType: gosnmp.Report, RequestID: req.RequestID,
Variables: []gosnmp.SnmpPDU{{Name: oid, Type: gosnmp.Counter32, Value: uint32(n)}},
}
}
// discoveryReport answers an engine-ID discovery probe (RFC 3414 4).
func (a *Agent) discoveryReport(req *gosnmp.SnmpPacket, elapsed time.Duration) *gosnmp.SnmpPacket {
sp := &gosnmp.UsmSecurityParameters{
AuthoritativeEngineID: a.engineID, AuthoritativeEngineBoots: engineBoots, AuthoritativeEngineTime: engineTime(elapsed),
}
return a.report(req, gosnmp.NoAuthNoPriv, sp, usmStatsUnknownEngine, a.st.disc.Load())
}
// timeReport is the authenticated notInTimeWindow report that lets the
// client re-sync its cached engine time.
func (a *Agent) timeReport(req *gosnmp.SnmpPacket, elapsed time.Duration) *gosnmp.SnmpPacket {
return a.report(req, gosnmp.AuthNoPriv, a.usmFor(req, elapsed, false), usmStatsNotInTime, a.st.notInTime.Load())
}