554 lines
14 KiB
Go
554 lines
14 KiB
Go
package snmp
|
||
|
||
import (
|
||
"flag"
|
||
"fmt"
|
||
"log"
|
||
"net"
|
||
"os"
|
||
"path/filepath"
|
||
"strings"
|
||
"time"
|
||
|
||
g "github.com/gosnmp/gosnmp"
|
||
"github.com/slayercat/GoSNMPServer"
|
||
"github.com/slayercat/GoSNMPServer/mibImps"
|
||
)
|
||
|
||
type SNMPService struct {
|
||
ListenAddr string
|
||
ListenPort uint16
|
||
UserName string
|
||
AuthPass string
|
||
AuthProto string
|
||
PrivPass string
|
||
PrivProto string
|
||
EngineID string
|
||
TrapPort uint16
|
||
TrapListen bool
|
||
TrapBool bool
|
||
TrapTick uint16
|
||
TimeOut uint16
|
||
TrapTarget string
|
||
|
||
ListenHost string
|
||
TrapHost string
|
||
|
||
SysName string
|
||
SysDescr string
|
||
SysLocation string
|
||
SysContact string
|
||
SysStatus string
|
||
SysService int
|
||
}
|
||
|
||
func (s *SNMPService) getAuthProto() g.SnmpV3AuthProtocol {
|
||
switch s.AuthProto {
|
||
case "NoAuth":
|
||
return g.NoAuth
|
||
case "MD5":
|
||
return g.MD5
|
||
case "SHA":
|
||
return g.SHA
|
||
default:
|
||
}
|
||
return g.MD5
|
||
}
|
||
|
||
func (s *SNMPService) getPrivProto() g.SnmpV3PrivProtocol {
|
||
switch s.PrivProto {
|
||
case "NoPriv":
|
||
return g.NoPriv
|
||
case "DES":
|
||
return g.DES
|
||
case "AES":
|
||
return g.AES
|
||
case "AES192":
|
||
return g.AES192
|
||
case "AES256":
|
||
return g.AES256
|
||
default:
|
||
}
|
||
return g.DES
|
||
}
|
||
|
||
func (s *SNMPService) setSecParamsList() []g.UsmSecurityParameters {
|
||
var secParamsList = []g.UsmSecurityParameters{
|
||
{
|
||
UserName: s.UserName,
|
||
AuthenticationProtocol: s.getAuthProto(),
|
||
AuthenticationPassphrase: s.AuthPass,
|
||
PrivacyProtocol: s.getPrivProto(),
|
||
PrivacyPassphrase: s.PrivPass,
|
||
AuthoritativeEngineID: s.EngineID,
|
||
},
|
||
// {
|
||
// UserName: "myuser2",
|
||
// AuthenticationProtocol: g.SHA,
|
||
// AuthenticationPassphrase: "mypassword2",
|
||
// PrivacyProtocol: g.DES,
|
||
// PrivacyPassphrase: "myprivacy2",
|
||
// AuthoritativeEngineID: s.EngineID,
|
||
// },
|
||
// {
|
||
// UserName: "myuser2",
|
||
// AuthenticationProtocol: g.MD5,
|
||
// AuthenticationPassphrase: "mypassword2",
|
||
// PrivacyProtocol: g.AES,
|
||
// PrivacyPassphrase: "myprivacy2",
|
||
// AuthoritativeEngineID: s.EngineID,
|
||
// },
|
||
}
|
||
return secParamsList
|
||
}
|
||
|
||
func (s *SNMPService) StartSNMPServer() {
|
||
// 设置引擎启动次数和引varvar
|
||
var engineBoots uint32 = 1
|
||
//var engineTime uint32 = uint32(time.Now().Unix() % 2147483647) // 使用当前时间初始化
|
||
//var engineTime uint32 = 3600 // 使用当前时间初始化
|
||
master := GoSNMPServer.MasterAgent{
|
||
Logger: GoSNMPServer.NewDefaultLogger(),
|
||
SecurityConfig: GoSNMPServer.SecurityConfig{
|
||
NoSecurity: true,
|
||
AuthoritativeEngineBoots: engineBoots,
|
||
// OnGetAuthoritativeEngineTime: func() uint32 {
|
||
// return engineTime
|
||
// },
|
||
//AuthoritativeEngineID: GoSNMPServer.SNMPEngineID{EngineIDData: "0x800007DB03360102101100"},
|
||
|
||
Users: s.setSecParamsList(),
|
||
},
|
||
SubAgents: []*GoSNMPServer.SubAgent{
|
||
{
|
||
UserErrorMarkPacket: false,
|
||
CommunityIDs: []string{"public", "private"}, // SNMPv1 and SNMPv2c community strings
|
||
OIDs: s.handleOIDs(),
|
||
//OIDs: mibImps.All(),
|
||
},
|
||
},
|
||
}
|
||
|
||
server := GoSNMPServer.NewSNMPServer(master)
|
||
err := server.ListenUDP("udp", s.ListenHost)
|
||
if err != nil {
|
||
log.Fatalf("Error in listen: %+v", err)
|
||
}
|
||
server.ServeForever()
|
||
}
|
||
|
||
func (s *SNMPService) handleOIDs() []*GoSNMPServer.PDUValueControlItem {
|
||
customOIDs := []*GoSNMPServer.PDUValueControlItem{
|
||
{
|
||
OID: "1.3.6.1.4.1.1379.2.3.3.3.1.1.1.0",
|
||
Type: g.OctetString,
|
||
OnGet: func() (value interface{}, err error) {
|
||
return s.SysName, nil
|
||
},
|
||
OnSet: func(value interface{}) error {
|
||
// 将[]uint8转换为string
|
||
if v, ok := value.([]uint8); ok {
|
||
s.SysName = string(v)
|
||
log.Printf("Set request for OID 1.3.6.1.4.1.1379.2.3.3.3.1.1.1.0 with value %v", s.SysName)
|
||
return nil
|
||
}
|
||
return nil
|
||
},
|
||
},
|
||
{
|
||
OID: ".1.3.6.1.4.1.1379.2.3.3.3.1.1.2.0",
|
||
Type: g.OctetString,
|
||
OnGet: func() (value interface{}, err error) {
|
||
return s.SysStatus, nil
|
||
},
|
||
OnSet: func(value interface{}) error {
|
||
// 将[]uint8转换为string
|
||
if v, ok := value.([]uint8); ok {
|
||
s.SysStatus = string(v)
|
||
log.Printf("Set request for OID 1.3.6.1.4.1.1379.2.3.3.3.1.1.2.0 with value %v", s.SysStatus)
|
||
return nil
|
||
}
|
||
return nil
|
||
},
|
||
},
|
||
{
|
||
OID: ".1.3.6.1.4.1.1379.2.3.3.3.1.1.3.0",
|
||
Type: g.OctetString,
|
||
OnGet: func() (value interface{}, err error) {
|
||
return s.SysDescr, nil
|
||
},
|
||
OnSet: func(value interface{}) error {
|
||
// 将[]uint8转换为string
|
||
if v, ok := value.([]uint8); ok {
|
||
s.SysDescr = string(v)
|
||
log.Printf("Set request for OID .1.3.6.1.4.1.1379.2.3.3.3.1.1.3.0 with value %v", s.SysDescr)
|
||
return nil
|
||
}
|
||
return nil
|
||
},
|
||
},
|
||
{
|
||
OID: ".1.3.6.1.4.1.1379.2.3.3.3.1.1.4.0",
|
||
Type: g.OctetString,
|
||
OnGet: func() (value interface{}, err error) {
|
||
return s.SysLocation, nil
|
||
},
|
||
OnSet: func(value interface{}) error {
|
||
// 将[]uint8转换为string
|
||
if v, ok := value.([]uint8); ok {
|
||
s.SysLocation = string(v)
|
||
log.Printf("Set request for OID .1.3.6.1.4.1.1379.2.3.3.3.1.1.4.0 with value %v", s.SysLocation)
|
||
return nil
|
||
}
|
||
return nil
|
||
},
|
||
},
|
||
{
|
||
OID: ".1.3.6.1.4.1.1379.2.3.3.3.1.1.5.0",
|
||
Type: g.OctetString,
|
||
OnGet: func() (value interface{}, err error) {
|
||
return s.SysContact, nil
|
||
},
|
||
OnSet: func(value interface{}) error {
|
||
// 将[]uint8转换为string
|
||
if v, ok := value.([]uint8); ok {
|
||
s.SysContact = string(v)
|
||
log.Printf("Set request for OID .1.3.6.1.4.1.1379.2.3.3.3.1.1.5.0 with value %v", s.SysContact)
|
||
return nil
|
||
}
|
||
return nil
|
||
},
|
||
},
|
||
{
|
||
OID: ".1.3.6.1.4.1.1379.2.3.3.3.1.1.7.0",
|
||
Type: g.TimeTicks,
|
||
OnGet: func() (value interface{}, err error) {
|
||
return uint32(time.Now().Unix()), nil
|
||
},
|
||
},
|
||
{
|
||
OID: ".1.3.6.1.4.1.1379.2.3.3.3.1.1.9.0",
|
||
Type: g.Integer,
|
||
OnGet: func() (value interface{}, err error) {
|
||
return s.SysService, nil
|
||
},
|
||
OnSet: func(value interface{}) error {
|
||
if v, ok := value.(int); ok {
|
||
s.SysService = v
|
||
log.Printf("Set request for OID .1.3.6.1.4.1.1379.2.3.3.3.1.1.9.0 with value %v", s.SysService)
|
||
return nil
|
||
}
|
||
return nil
|
||
},
|
||
},
|
||
}
|
||
// 为 GETBULK 新增处理 OIDs
|
||
bulkOIDs := &GoSNMPServer.PDUValueControlItem{
|
||
OID: ".1.3.6.1.4.1.1379.2.3.3.3.1.1", // 这里是您想要支持 GETBULK 的 OID 前缀
|
||
Type: g.OctetString,
|
||
OnGet: func() (value interface{}, err error) {
|
||
// 假设我们返回一百度值,您可以根据您的实现进行调整
|
||
values := []interface{}{s.SysName, s.SysStatus, s.SysDescr, s.SysLocation, s.SysContact, uint32(time.Now().Unix()), s.SysService} // 可以从其他结构中获取真实值
|
||
return values, nil
|
||
},
|
||
}
|
||
|
||
customOIDs = append(customOIDs, bulkOIDs)
|
||
|
||
// 获取mibImps.All()返回的OID列表
|
||
mibOIDs := mibImps.All()
|
||
|
||
// 使用Map来检测并移除重复的OID
|
||
oidMap := make(map[string]*GoSNMPServer.PDUValueControlItem)
|
||
for _, oid := range customOIDs {
|
||
oidMap[oid.OID] = oid
|
||
}
|
||
for _, oid := range mibOIDs {
|
||
if _, exists := oidMap[oid.OID]; !exists {
|
||
oidMap[oid.OID] = oid
|
||
} else {
|
||
log.Printf("Duplicate OID found: %s", oid.OID)
|
||
}
|
||
}
|
||
|
||
// 将Map转换为Slice
|
||
allOIDs := make([]*GoSNMPServer.PDUValueControlItem, 0, len(oidMap))
|
||
for _, oid := range oidMap {
|
||
allOIDs = append(allOIDs, oid)
|
||
}
|
||
|
||
return allOIDs
|
||
}
|
||
|
||
func (s *SNMPService) StartTrapServer() {
|
||
flag.Usage = func() {
|
||
fmt.Printf("Usage:\n")
|
||
fmt.Printf(" %s\n", filepath.Base(os.Args[0]))
|
||
flag.PrintDefaults()
|
||
}
|
||
|
||
tl := g.NewTrapListener()
|
||
tl.OnNewTrap = s.MyTrapHandler
|
||
|
||
usmTable := g.NewSnmpV3SecurityParametersTable(g.NewLogger(log.New(os.Stdout, "", 0)))
|
||
for i := range s.setSecParamsList() {
|
||
sp := &s.setSecParamsList()[i] // 使用指针
|
||
err := usmTable.Add(sp.UserName, sp)
|
||
if err != nil {
|
||
usmTable.Logger.Print(err)
|
||
}
|
||
}
|
||
|
||
// 设置引擎启动次数和引varvar
|
||
//var engineBoots uint32 = 1
|
||
// var engineTime uint32 = uint32(time.Now().Unix() % 2147483647) // 使用当前时间初始化
|
||
//var engineTime uint32 = 3600 // 使用当前时间初始化
|
||
gs := &g.GoSNMP{
|
||
Target: s.TrapTarget,
|
||
Port: s.TrapPort,
|
||
Transport: "udp",
|
||
Timeout: time.Duration(s.TimeOut) * time.Second, // 设置超时时间为x秒
|
||
Version: g.Version3, // Always using version3 for traps, only option that works with all SNMP versions simultaneously
|
||
MsgFlags: g.NoAuthNoPriv,
|
||
SecurityModel: g.UserSecurityModel,
|
||
SecurityParameters: &g.UsmSecurityParameters{
|
||
UserName: s.UserName,
|
||
AuthoritativeEngineID: s.EngineID,
|
||
AuthoritativeEngineBoots: 1,
|
||
//AuthoritativeEngineTime: 3600,
|
||
AuthenticationProtocol: s.getAuthProto(),
|
||
AuthenticationPassphrase: s.AuthPass,
|
||
PrivacyProtocol: s.getPrivProto(),
|
||
PrivacyPassphrase: s.PrivPass,
|
||
},
|
||
//TrapSecurityParametersTable: usmTable,
|
||
ContextEngineID: s.EngineID,
|
||
ContextName: "v3test",
|
||
}
|
||
tl.Params = gs
|
||
tl.Params.Logger = g.NewLogger(log.New(os.Stdout, "", 0))
|
||
|
||
// 定时发送Trap
|
||
if s.TrapBool {
|
||
go s.SendPeriodicTraps(gs)
|
||
}
|
||
go s.monitorNetwork(gs)
|
||
|
||
if s.TrapListen {
|
||
err := tl.Listen(s.TrapHost)
|
||
if err != nil {
|
||
log.Panicf("error in listen: %s", err)
|
||
}
|
||
}
|
||
}
|
||
|
||
func (s *SNMPService) MyTrapHandler(packet *g.SnmpPacket, addr *net.UDPAddr) {
|
||
log.Printf("got trapdata from %s\n", addr.IP)
|
||
for _, v := range packet.Variables {
|
||
switch v.Type {
|
||
case g.OctetString:
|
||
b := v.Value.([]byte)
|
||
fmt.Printf("OID: %s, string: %x\n", v.Name, b)
|
||
|
||
default:
|
||
log.Printf("trap: %+v\n", v)
|
||
}
|
||
}
|
||
}
|
||
|
||
func (s *SNMPService) SendPeriodicTraps(gs *g.GoSNMP) {
|
||
err := gs.Connect()
|
||
if err != nil {
|
||
log.Fatalf("Connect() err: %v", err)
|
||
}
|
||
defer gs.Conn.Close()
|
||
|
||
ticker := time.NewTicker(time.Duration(s.TrapTick) * time.Second) // 每10秒发送一次Trap
|
||
defer ticker.Stop()
|
||
|
||
for range ticker.C { // 每x秒发送一次Trap
|
||
trap := g.SnmpTrap{
|
||
Variables: []g.SnmpPDU{
|
||
{
|
||
Name: ".1.3.6.1.2.1.1.3.0",
|
||
Type: g.TimeTicks,
|
||
Value: uint32(time.Now().Unix()),
|
||
},
|
||
{
|
||
Name: ".1.3.6.1.6.3.1.1.4.1.0",
|
||
Type: g.ObjectIdentifier,
|
||
Value: ".1.3.6.1.6.3.1.1.5.1",
|
||
},
|
||
},
|
||
}
|
||
_, err = gs.SendTrap(trap)
|
||
if err != nil {
|
||
log.Printf("error sending trap: %s", err)
|
||
} else {
|
||
log.Printf("trap sent successfully")
|
||
}
|
||
}
|
||
}
|
||
|
||
// 1. 设备链路连接失败时发送Trap (LinkDown)
|
||
func (s *SNMPService) sendLinkDownTrap(gs *g.GoSNMP, ifIndex int, ifDescr string) {
|
||
err := gs.Connect()
|
||
if err != nil {
|
||
log.Fatalf("Connect() err: %v", err)
|
||
}
|
||
defer gs.Conn.Close()
|
||
|
||
trap := g.SnmpTrap{
|
||
Variables: []g.SnmpPDU{
|
||
{
|
||
Name: ".1.3.6.1.4.1.1379.2.3.3.3.3.1", // linkDown
|
||
Type: g.OctetString,
|
||
Value: ".1.3.6.1.4.1.1379.2.3.3.3.3.1",
|
||
},
|
||
{
|
||
Name: ".1.3.6.1.4.1.1379.2.3.3.3.3.1.1", // ifIndex
|
||
Type: g.Integer,
|
||
Value: ifIndex,
|
||
},
|
||
{
|
||
Name: ".1.3.6.1.4.1.1379.2.3.3.3.3.1.2", // ifDescr
|
||
Type: g.OctetString,
|
||
Value: ifDescr,
|
||
},
|
||
{
|
||
Name: ".1.3.6.1.4.1.1379.2.3.3.3.4", // severity OID
|
||
Type: g.Integer,
|
||
Value: 2, // event
|
||
},
|
||
},
|
||
}
|
||
|
||
_, err = gs.SendTrap(trap)
|
||
if err != nil {
|
||
log.Printf("error sending LinkDown trap: %s", err)
|
||
} else {
|
||
log.Printf("LinkDown trap sent successfully")
|
||
}
|
||
}
|
||
|
||
// 2. 设备链路恢复正常时发送Trap (LinkUp)
|
||
func (s *SNMPService) sendLinkUpTrap(gs *g.GoSNMP, ifIndex int, ifDescr string) {
|
||
err := gs.Connect()
|
||
if err != nil {
|
||
log.Fatalf("Connect() err: %v", err)
|
||
}
|
||
defer gs.Conn.Close()
|
||
|
||
trap := g.SnmpTrap{
|
||
Variables: []g.SnmpPDU{
|
||
{
|
||
Name: ".1.3.6.1.4.1.1379.2.3.3.3.3.2", // linkUp
|
||
Type: g.OctetString,
|
||
Value: ".1.3.6.1.4.1.1379.2.3.3.3.3.2",
|
||
},
|
||
{
|
||
Name: ".1.3.6.1.4.1.1379.2.3.3.3.3.2.1", // ifIndex
|
||
Type: g.Integer,
|
||
Value: ifIndex,
|
||
},
|
||
{
|
||
Name: ".1.3.6.1.4.1.1379.2.3.3.3.3.2.2", // ifDescr
|
||
Type: g.OctetString,
|
||
Value: ifDescr,
|
||
},
|
||
{
|
||
Name: ".1.3.6.1.4.1.1379.2.3.3.3.4", // severity OID
|
||
Type: g.Integer,
|
||
Value: 5, // event
|
||
},
|
||
},
|
||
}
|
||
|
||
_, err = gs.SendTrap(trap)
|
||
if err != nil {
|
||
log.Printf("error sending LinkUp trap: %s", err)
|
||
} else {
|
||
log.Printf("LinkUp trap sent successfully")
|
||
}
|
||
}
|
||
|
||
// 3. 设备鉴权失败时发送Trap (AuthenticationFailure)
|
||
func (s *SNMPService) sendAuthFailureTrap(gs *g.GoSNMP, username, descr string) {
|
||
err := gs.Connect()
|
||
if err != nil {
|
||
log.Fatalf("Connect() err: %v", err)
|
||
}
|
||
defer gs.Conn.Close()
|
||
|
||
trap := g.SnmpTrap{
|
||
Variables: []g.SnmpPDU{
|
||
{
|
||
Name: ".1.3.6.1.4.1.1379.2.3.3.3.3.3", // authenticationFailure
|
||
Type: g.OctetString,
|
||
Value: ".1.3.6.1.4.1.1379.2.3.3.3.3.3",
|
||
},
|
||
{
|
||
Name: ".1.3.6.1.4.1.1379.2.3.3.3.3.3.1", // 自定义OID,用于记录失败的用户名
|
||
Type: g.OctetString,
|
||
Value: username,
|
||
},
|
||
{
|
||
Name: ".1.3.6.1.4.1.1379.2.3.3.3.3.3.2", // 自定义OID,用于记录描述
|
||
Type: g.OctetString,
|
||
Value: descr,
|
||
},
|
||
{
|
||
Name: ".1.3.6.1.4.1.1379.2.3.3.3.4", // severity OID
|
||
Type: g.Integer,
|
||
Value: 4, // event
|
||
},
|
||
},
|
||
}
|
||
|
||
_, err = gs.SendTrap(trap)
|
||
if err != nil {
|
||
log.Printf("error sending AuthenticationFailure trap: %s", err)
|
||
} else {
|
||
log.Printf("AuthenticationFailure trap sent successfully")
|
||
}
|
||
}
|
||
|
||
func (s *SNMPService) monitorNetwork(gs *g.GoSNMP) {
|
||
// 假设有一个函数 checkLinkStatus 返回链路状态
|
||
for {
|
||
serviceStatus := s.checkServiceStatus()
|
||
switch strings.ToUpper(serviceStatus) {
|
||
case "LINK_DOWN":
|
||
index := 1
|
||
ifDescr := fmt.Sprintf("Link(index=%d) DOWN", index)
|
||
s.sendLinkDownTrap(gs, index, ifDescr) // 假设接口索引为1
|
||
s.SysService = 0
|
||
case "LINK_UP":
|
||
index := 1
|
||
ifDescr := fmt.Sprintf("Link(index=%d) UP", index)
|
||
s.sendLinkUpTrap(gs, index, ifDescr) // 假设接口索引为1
|
||
s.SysService = 0
|
||
case "AUTH_FAILURE":
|
||
descr := "Authentication Failure"
|
||
s.sendAuthFailureTrap(gs, s.UserName, descr)
|
||
s.SysService = 0
|
||
default:
|
||
}
|
||
|
||
time.Sleep(10 * time.Second) // 每10秒检查一次
|
||
}
|
||
}
|
||
|
||
func (s *SNMPService) checkServiceStatus() string {
|
||
switch s.SysService {
|
||
case 1:
|
||
return "LINK_DOWN"
|
||
case 2:
|
||
return "LINK_UP"
|
||
case 3:
|
||
return "AUTH_FAILURE"
|
||
default:
|
||
}
|
||
return "NORMAL"
|
||
}
|