Skip to content

Support to provide a SNMP mock server #603

Open
@LinuxSuRen

Description

@LinuxSuRen

Below is a sample code:

package main

import (
	"encoding/binary"
	"fmt"
	"net"
	"strconv"
	"strings"

	"github.com/gosnmp/gosnmp"
)

const (
	// SNMP 版本号
	SNMPVersion = 1

	// 固定的 OID 和值
	OID   = ".1.3.6.1.4.1.5105.100.1.9.4.15.0" // sysDescr
	Value = "My SNMP Server"
)

// sendSNMPRequest 发送 SNMP 请求并接收响应
func sendSNMPRequest(target string, community string, oid string) ([]byte, error) {
	// 创建 SNMP 客户端
	gosnmp.Default.Target = target
	gosnmp.Default.Port = 161
	gosnmp.Default.Community = community
	gosnmp.Default.Version = gosnmp.Version2c

	// 创建 PDU
	pdu := gosnmp.SnmpPDU{
		Name:  oid,
		Type:  gosnmp.OctetString,
		Value: Value,
	}

	// 创建请求
	request := &gosnmp.SnmpPacket{
		Version:    gosnmp.Version2c,
		Community:  community,
		Error:      gosnmp.NoError,
		ErrorIndex: 0,
		PDUType:    gosnmp.GetRequest,
		Variables:  []gosnmp.SnmpPDU{pdu},
	}

	// 编码请求
	req, err := request.MarshalMsg()
	if err != nil {
		return nil, fmt.Errorf("failed to marshal SNMP request: %v", err)
	}

	// 发送请求并接收响应
	conn, err := net.Dial("udp", fmt.Sprintf("%s:%d", target, 161))
	if err != nil {
		return nil, fmt.Errorf("failed to dial UDP: %v", err)
	}
	defer conn.Close()

	_, err = conn.Write(req)
	if err != nil {
		return nil, fmt.Errorf("failed to send SNMP request: %v", err)
	}

	buffer := make([]byte, 4096) // Increased buffer size
	n, err := conn.Read(buffer)
	if err != nil {
		return nil, fmt.Errorf("failed to read SNMP response: %v", err)
	}

	return buffer[:n], nil
}

func main() {
	pc, err := net.ListenPacket("udp", ":161")
	if err != nil {
		fmt.Println("Failed to listen on UDP port 161:", err)
		return
	}
	defer pc.Close()

	fmt.Println("SNMP server listening on UDP port 161...")

	buffer := make([]byte, 4096) // Increased buffer size
	for {
		// 读取客户端请求
		n, addr, err := pc.ReadFrom(buffer)
		if err != nil {
			fmt.Println("Failed to read UDP packet:", err)
			continue
		}

		// 解析 SNMP 请求
		req := buffer[:n]
		fmt.Printf("Received SNMP request from %s: %X\n", addr, req)

		// 生成 SNMP 响应
		resp := generateSNMPResponse(req)
		if resp == nil {
			fmt.Println("Failed to generate SNMP response")
			continue
		}

		// 发送响应
		_, err = pc.WriteTo(resp, addr)
		if err != nil {
			fmt.Println("Failed to send SNMP response:", err)
			continue
		} else {
			fmt.Printf("Sent SNMP response to %s: %X\n", addr, resp)
		}
	}

	// 测试发送 SNMP 请求
	//resp, err := sendSNMPRequest("192.168.12.109", "public", ".1.3.6.1.4.1.5105.100.1.9.4.15.0")
	//if err != nil {
	//	fmt.Println("Failed to send SNMP request:", err)
	//} else {
	//	fmt.Printf("Received SNMP response: %X\n", resp)
	//}
}

// generateSNMPResponse 生成 SNMP 响应数据
func generateSNMPResponse(req []byte) []byte {
	// 解析请求
	// 这里仅处理简单的 GET 请求
	if len(req) < 4 {
		fmt.Println("Invalid SNMP request length")
		return nil
	}

	// 提取社区字符串
	communityLen := req[6]
	community := string(req[7 : 7+communityLen]) // 提取社区字符串
	fmt.Printf("Received SNMP request with community: %s\n", community)

	// 提取请求 ID
	requestId := binary.BigEndian.Uint32(req[7+communityLen+2+2 : 7+communityLen+2+2+4])
	fmt.Printf("Received SNMP request with ID %d\n", requestId)

	// 使用 gosnmp 库构建响应
	gosnmp.Default.Target = "192.168.123.58"
	gosnmp.Default.Port = 161
	gosnmp.Default.Community = community
	gosnmp.Default.Version = gosnmp.Version2c

	// 创建 PDU
	pdu := gosnmp.SnmpPDU{
		Name:  OID,
		Type:  gosnmp.OctetString,
		Value: Value,
	}

	// 创建响应
	response := &gosnmp.SnmpPacket{
		Version:   gosnmp.Version2c,
		Community: community,
		//ErrorStatus: gosnmp.NoError,
		Error:      gosnmp.NoError,
		ErrorIndex: 0,
		RequestID:  requestId,
		PDUType:    gosnmp.GetResponse,
		Variables:  []gosnmp.SnmpPDU{pdu},
	}

	// 编码响应
	resp, err := response.MarshalMsg()
	if err != nil {
		fmt.Println("Failed to marshal SNMP response:", err)
		return nil
	}

	fmt.Printf("Generated SNMP response: %X\n", resp)
	return resp
}

// encodeOID 编码 OID
func encodeOID(oid string) []byte {
	// 使用 BER 编码来编码 OID
	parts := splitOID(oid)
	encoded := make([]byte, 0)
	first := parts[0]*40 + parts[1]
	encoded = append(encoded, byte(first))
	for _, part := range parts[2:] {
		encoded = append(encoded, encodeVarInt(part)...)
	}
	return encoded
}

// encodeVarInt 使用 BER 编码来编码一个整数
func encodeVarInt(value int) []byte {
	if value < 128 {
		return []byte{byte(value)}
	}
	encoded := make([]byte, 0)
	for value > 0 {
		encoded = append([]byte{byte(value%128 | 0x80)}, encoded...)
		value /= 128
	}
	encoded[len(encoded)-1] &= 0x7F
	return encoded
}

// encodeValue 编码值
func encodeValue(value string) []byte {
	// 使用 BER 编码来编码值
	encoded := make([]byte, 0)
	encoded = append(encoded, byte(0x04))       // 字符串类型
	encoded = append(encoded, byte(len(value))) // 字符串长度
	encoded = append(encoded, []byte(value)...) // 字符串值
	return encoded
}

// splitOID 分割 OID 字符串为整数数组
func splitOID(oid string) []int {
	parts := make([]int, 0)
	for _, part := range strings.Split(oid, ".") {
		num, _ := strconv.Atoi(part)
		parts = append(parts, num)
	}
	return parts
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions