第 1 章 - Azure RTOS NetX Duo SNMP 简介

简单网络管理协议 (SNMP) 是为在 Internet 上管理设备而设计的协议。 SNMP 是一项利用无连接用户数据报协议 (UDP) 服务来执行其管理功能的协议。 Azure RTOS NetX Duo SNMP 的实现是 SNMP 代理的实现。 代理负责响应 SNMP 管理器的命令并发送事件驱动的陷阱。

NetX Duo SNMP 支持与 SNMP 管理器的 IPv4 和 IPv6 通信。 NetX SNMP 应用程序应在 NetX Duo SNMP 中编译和运行。 但建议开发人员将现有 SNMP 应用程序移植到使用等效的“Duo”服务中。 例如,如果发送 SNMP 陷阱消息,以下“Duo”服务应替换其 NetX 等效项:

nxd_snmp_object_trap_send

nxd_snmp_object_trapv2_send

nxd_snmp_object_trapv3_send

有关更多详细信息,请参阅本用户指南其他部分的“SNMP 代理服务说明”。

NetX Duo SNMP 代理要求

NetX Duo SNMP 包要求已创建 IP 实例。 此外,必须在同一 IP 实例上启用 UDP。

NetX Duo SNMP 代理具有几个附加要求。 首先,它需要访问端口 161 才能处理所有 SNMP 管理器请求。 还需要访问端口 162 才能向管理器发送陷阱消息。

要通过 IPv6 使用 NetX Duo SNMP 代理,并获取 IPv6 对象,必须在 NetX Duo 中启用 IPv6。 有关为 IPv6 服务启用 IP 实例的详细信息,请参阅“NetX Duo 用户指南”

NetX Duo SNMP 约束

NetX Duo SNMP 协议实现 SNMP 第 1、2 和 3版。 SNMPv3 实现支持 MD5 和 SHA 身份验证以及 DES 加密。 此版本的 NetX Duo SNMP 代理具有以下约束:

  1. 每个 NetX IP 实例有一个 SNMP 代理
  2. 不支持 RMON
  3. 不支持 SNMPv3 通知消息
  4. 不支持 OPAQUE 和 NSAP 数据类型
  5. IPv6 地址定义为八进制字符串,格式检查由应用程序执行。

SNMP 对象名称

SNMP 协议用于在 Internet 上管理设备。 为实现此目的,每个 SNMP 受管理设备都有一组 RFC 1155 定义的管理信息结构对象 (SMI) 定义的对象。 该结构是分层结构树类型的结构,如下所示:

Diagram of the Structure of Management Information.

树中的每个节点都是一个对象。 树中的“dod”对象由表示法 1.3.6 标识,而树中的“Internet”对象则由表示法 1.3.6.1 标识。 所有 SNMP 对象名称都以表示法 1.3.6 开头。

SNMP 管理器使用此对象表示法来指定设备中要获取或设置的对象。 NetX Duo SNMP 代理会解释此类管理器请求,并为应用程序提供执行请求操作的机制。

SNMP 管理器请求

SNMP 具有管理设备的简单机制。 有一组标准 SNMP 命令,由 SNMP 管理器向端口 161 的 SNMP 设备发出。 下面显示了一些基本 SNMP 管理器命令:

SNMP 命令 含义
GET 获取指定的对象
GETNEXT 获取指定对象 ID 后的下一个逻辑对象
GETBULK 获取指定对象 ID 后的多个逻辑对象
SET 设置指定的对象

这些命令采用 Abstract Syntax Notation One (ASN.1) 格式编码,并驻留在 SNMP 管理器发送的 UDP 数据包的有效负载中。 NetX Duo SNMP 代理处理请求,然后调用 nx_snmp_agent_create 调用中指定的相应处理例程

NetX Duo SNMP 代理陷阱

NetX Duo SNMP 代理还能够向 SNMP 管理器异步发出事件的警报。 这可通过 SNMP 陷阱命令完成。 SNMP 的每个版本都有一个唯一的 API,用于将陷阱发送到 SNMP 管理器。 默认情况下,陷阱将在端口 162 发送到 SNMP 管理器。

NetX Duo SNMP 代理为 SNMPv3 陷阱消息提供单独的安全密钥。 为此,SNMP 应用程序必须创建一组单独的密钥(与应用于响应管理器请求的密钥不同)。 陷阱安全性使 SNMP 代理可以使用相同或不同的密码进行身份验证和保护隐私。 有关创建安全密钥的详细信息,请参阅下一部分中的“NetX Duo SNMP 身份验证和加密”。

在 nxd_snmp.h 的顶部对标准 SNMP 陷阱变量列表进行了枚举:

变量 “值”
#define NX_SNMP_TRAP_COLDSTART 0
#define NX_SNMP_TRAP_WARMSTART 1
#define NX_SNMP_TRAP_LINKDOWN 2
#define NX_SNMP_TRAP_LINKUP 3
#define NX_SNMP_TRAP_AUTHENTICATE_FAILURE 4
#define NX_SNMP_TRAP_EGPNEIGHBORLOSS 5
#define NX_SNMP_TRAP_ENTERPRISESPECIFIC 6

若要在陷阱消息中包含这些变量,将 nx_snmp_agent_trapv2_send (SNMPv2) 或 nx_snmp_agent_trapv3_send (SNMPv3) 中的 trap_type 输入参数设置为这些变量的枚举值 。 下面的示例显示 SNMPv2 通知 SNMP 管理器冷启动事件:

UINT trap_type = NX_SNMP_TRAP_COLDSTART;

status = nx_snmp_agent_trapv2_send(&my_agent, MIB_IP_ADDRESS,
                                  (UCHAR *)"public", trap_type,
                                  tx_time_get(), NX_NULL);

若要在陷阱消息中包含专有变量,将 trap_type 输入参数设置为 NX_SNMP_TRAP_CUSTOM,且陷阱列表输入参数包含专有数据。 请注意,陷阱消息将包含系统运行时间 (1.3.6.1.6.3.1.1.4.1.0)。 下面显示了 SNMPv2 的示例:

NX_SNMP_TRAP_OBJECT trap_list[3];
NX_SNMP_OBJECT_DATA trap_data0;

    /* Load the data into the OBJECT. */
    nx_snmp_object_id_get((void*)"1.3.6.1.4.1.7428.1.3.2.0", &trap_data0);

    /* Update the Trap Object with the object and OID. */
    trap_list[0].nx_snmp_object_string_ptr = (UCHAR *)"1.3.6.1.6.3.1.1.4.0";
    trap_list[0].nx_snmp_object_data  = &trap_data0;

    /* Null terminate the trap list. */
    trap_list[1].nx_snmp_object_string_ptr = NX_NULL;

    status = nx_snmp_agent_trapv2_send(&my_agent, MIB_IP_ADDRESS,
                                      (UCHAR *)"trapduo",
                                      NX_SNMP_TRAP_CUSTOM,
                                      tx_time_get(), trap_list);

NetX Duo SNMP 身份验证和加密

有两种形式的身份验证,即基本身份验证和摘要式身份验证 。 基本身份验证等效于在许多协议中找到的简单纯文本用户名身份验证。 在 SNMP 基本身份验证中,用户只需验证提供的用户名是否对执行 SNMP 操作有效。 基本身份验证是适用于 SNMP 版本 1 和 2 的唯一选项。

基本身份验证的主要缺点是以纯文本格式传输用户名。 SNMPv3 摘要式身份验证不会以纯文本格式传输用户名,从而解决了此问题。 而是使用一种算法从用户名、上下文引擎和其他信息中派生 96位的“摘要”。 NetX Duo SNMP 代理支持 MD5 和 SHA 摘要算法。

若要启用身份验证,SNMP 代理必须使用 nx_snmp_agent_context_engine_set 服务设置器上下文引擎 ID。 上下文引擎 ID 在创建身份验证密钥时使用。

使用 DES 算法可以加密 SNMPv3 数据。 加密需要启用该身份验证(不设置身份验证参数就无法加密数据)。

创建身份验证和隐私密钥,请使用以下 API:

UINT  _nx_snmp_agent_md5_key_create(NX_SNMP_AGENT *agent_ptr,
                                    UCHAR *password, NX_SNMP_SECURITY_KEY
                                   *destination_key)

UINT  _nx_snmp_agent_sha_key_create(NX_SNMP_AGENT *agent_ptr,
                                    UCHAR *password, NX_SNMP_SECURITY_KEY
                                   *destination_key)

接下来,必须将 SNMP 代理配置为使用这些密钥。 向 SNMP 代理注册密钥,请使用以下 API:

UINT  _nx_snmp_agent_authenticate_key_use(NX_SNMP_AGENT *agent_ptr,
                                          NX_SNMP_SECURITY_KEY *key)

UINT  _nx_snmp_agent_privacy_key_use(NX_SNMP_AGENT *agent_ptr,
                                    NX_SNMP_SECURITY_KEY *key)

可以为陷阱消息创建单独的密钥。 应用陷阱消息的密钥,可以使用以下 API:

UINT  _nx_snmp_agent_auth_trap_key_use(NX_SNMP_AGENT *agent_ptr,
                                       NX_SNMP_SECURITY_KEY *key)

UINT  _nx_snmp_agent_priv_trap_key_use(NX_SNMP_AGENT *agent_ptr,
                                       NX_SNMP_SECURITY_KEY *key)

若要对响应消息和发送陷阱禁用身份验证或加密,请在密钥指针输入设置为 NULL 的情况下使用这些服务。

NetX Duo SNMP 团体字符串

NetX Duo SNMP 代理支持公共和专用的团体字符串。 公共字符串是使用 nx_snmp_agent _public_string_set 服务设置的。 NetX Duo SNMP 代理专用字符串是使用 nx_snmp_agent_private_string_set 服务设置的。

NetX Duo SNMP 用户名回调

NetX Duo SNMP 代理包允许应用程序指定(通过 nx_snmp_agent_create 调用)用户名回调(在开始处理每个 SNMP 客户端请求时调用)

回调例程为 NetX Duo SNMP 代理提供用户名。 如果提供的用户名有效,或者响应请求时不需要用户名检查,则用户名回调应返回 NX_SUCCESS 值。 否则,例程应返回 NX_SNMP_ERROR 以指示指定的用户名无效。

应用程序用户名回调例程的格式定义如下所示:

UINT nx_snmp_agent_username_process(NX_SNMP_AGENT *agent_ptr,
                                    UCHAR *username);

输入参数定义如下:

参数 含义
agent_ptr 指向调用 SNMP 代理的指针
username 指向所需用户名的指针的目标

对于 SNMPv1 和 SNMPv2/v2C 会话,应用程序需要检查传入的 SNMP 请求上的团体字符串,以确定 SNMP 请求是否包含有效的团体字符串。 SNMP 应用程序有多项服务可执行此操作。

SNMP 应用程序可以使用此服务来查询当前的 SNMP 管理器请求是 GET(例如 GET、GETNEXT 或 GETBULK)还是 SET 类型请求:

UINT nx_snmp_agent_request_get_type_test(NX_SNMP_AGENT *agent_ptr,
                                         UINT *is_get_type);

如果请求是 GET 类型,则应用程序需要将输入团体字符串与 SNMP 代理的公共字符串进行比较:

UINT nx_snmp_agent_public_string_test(NX_SNMP_AGENT *agent_ptr,
                                      UCHAR *username,
                                      UINT *is_public);

同样,如果请求是 SET 类型,应用程序需要将输入团体字符串与 SNMP 代理的专用字符串进行比较:

UINT nx_snmp_agent_private_string_test(NX_SNMP_AGENT *agent_ptr,
                                       UCHAR *username,
                                       UINT *is_private);

如果输入团体字符串是有效的公共或专用团体字符串,则分别指示 is_public 和 is_private 返回值。

用户名回调例程的返回值指示用户名是否有效。 如果用户名有效,则返回 NX_SUCCESS 值;如果用户名无效,则返回 NX_SNMP_ERROR 值 。

NetX Duo SNMP 代理 GET 回调

应用程序必须设置用于处理 SNMP 管理器中的 GET 对象请求的回调例程。 回调检索在请求中指定的对象的值。

应用程序 GET 请求回调例程定义如下:

UINT nx_snmp_agent_get_process(NX_SNMP_AGENT *agent_ptr,
                               UCHAR *object_requested,
                               NX_SNMP_OBJECT_DATA *object_data);

输入参数定义如下:

参数 含义
agent_ptr 指向调用 SNMP 代理的指针
object_requested 表示 GET 操作的对象 ID 的 ASCII 字符串。
object_data 用于保存回调检索的值的数据结构。 这可以使用下面所述的一系列 NetX Duo SNMP API 来设置。

注意

对于八进制字符串,必须为对象分配长度,使内部函数能够获取长度(因为回调本身没有长度参数):

object_data -> nx_snmp_object_octet_string_size = mib2_mib[i].length;

由于 GET 回调对数据类型未知,因此不需要检查数据类型。 长度不会对以 null 分隔的数值类型或字符串产生任何影响。

然后调用内部函数:

status = mib2_mib[i].object_get_callback)
                   (mib2_mib[i].object_value_ptr, object_data);

如果回调函数无法找到请求的对象,应返回 NX_SNMP_ERROR_NOSUCHNAME 错误代码。 如果检测到任何其他错误,应返回 NX_SNMP_ERROR。

NetX Duo SNMP 代理 GETNEXT 回调

应用程序还必须为 SNMP 管理器中的 GETNEXT 对象请求设置回调例程。 GETNEXT 回调检索由请求指定的下一个对象的值。

应用程序 GETNEXT 请求回调例程定义如下:

UINT nx_snmp_agent_getnext_process(NX_SNMP_AGENT *agent_ptr,
                                   UCHAR *object_requested,
                                   NX_SNMP_OBJECT_DATA *object_data);

输入参数定义如下:

参数 含义
agent_ptr 指向调用 SNMP 代理的指针
object_requested 表示 GETNEXT 操作的对象 ID 的 ASCII 字符串。
object_data 用于保存回调检索的值的数据结构。 这可以使用下面所述的一系列 NetX Duo SNMP API 来设置。

与 GET 回调的情况相同,必须为具有八进制字符串数据的对象分配长度,使内部函数能够获取长度(因为回调本身没有长度参数):

object_data -> nx_snmp_object_octet_string_size = mib2_mib[i].length;

由于 GET 回调对数据类型未知,因此不需要检查数据类型。 长度不会对以 null 分隔的数值类型或字符串产生任何影响。

然后调用内部函数:

status = mib2_mib[i].object_get_callback)
                   (mib2_mib[i].object_value_ptr, object_data);

如果回调函数无法找到请求的对象,应返回 NX_SNMP_ERROR_NOSUCHNAME 错误代码。 如果检测到任何其他错误,应返回 NX_SNMP_ERROR。

NetX Duo SNMP 代理 SET 回调

应用程序应设置用于处理 SNMP 管理器中的 SET 对象请求的回调例程。 SET 回调设置由请求指定的对象的值。

应用程序 SET 请求回调例程定义如下:

UINT nx_snmp_agent_set_process(NX_SNMP_AGENT *agent_ptr,
                               UCHAR *object_requested,
                               NX_SNMP_OBJECT_DATA *object_data);

输入参数定义如下:

参数 含义
agent_ptr 指向调用 SNMP 代理的指针
object_requested 表示 SET 操作的对象 ID 的 ASCII 字符串。
object_data 包含指定对象的新值的数据结构。 可以使用下面所述的 NetX Duo SNMP API 来执行实际操作。

请注意,对于八进制字符串,SET 回调应更新包含数据长度的 MIB 表(因为 SNMP 代理分析了数据,知道类型和长度):

if (object_data -> nx_snmp_object_data_type ==
                           NX_SNMP_ANS1_OCTET_STRING)
{
    mib2_mib[i].length =
        object_data -> nx_snmp_object_octet_string_size;
}

object_data -> nx_snmp_object_octet_string_size =
                                 mib2_mib[i].length;

如果回调函数无法找到请求的对象,应返回 NX_SNMP_ERROR_NOSUCHNAME 错误代码。

如果 NetX Duo SNMP 主机已创建专用团体字符串,并且 SET 请求的 SNMP 发送方没有匹配的专用字符串,则它可能会返回 NX_SNMP_ERROR_NOACCESS 错误。 如果检测到任何其他错误,应返回 NX_SNMP_ERROR。

注意

尽管 NetX Duo SNMP 代理提供了一个包含分发的 SNMP MIB 数据库,但它主要用于测试和开发目的。 开发人员很可能需要用于专业 SNMP 应用程序的专有 MIB 数据库。

在运行时更改 SNMP 版本

SNMP 代理主机在运行时可以使用 nx_snmp_agent_set_version 服务为三个版本中的每一个版本更改 SNMP 版本。 如果在 nx_snmp_agent_create 中创建了 SNMP 代理,则默认为这三个版本启用 SNMP 代理。 但是,应用程序可以将其限制为所有版本的子集。

注意

如果定义了配置选项 NX_SNMP_DISABLE_V1, NX_SNMP_DISABLE_V2 和/或 NX_SNMP_DISABLE_V3,此函数将不会对启用受影响的版本产生影响。

SNMP 代理可以使用 nx_snmp_agent_get_current_version 服务检索最新 SNMP 数据包的 SNMP 版本。

SNMPv3 发现

如果为 SNMPv3 启用了 SNMP 代理,则 SNMP 代理将响应 SNMP 管理器中的发现请求。 此类请求包含安全参数数据,其中权威引擎 ID、用户名、启动计数和启动时间为 null 值。 身份验证参数不会应用于发现消息。 请求中的变量绑定列表为空(包含零个项)。 SNMP 代理响应为零启动时间和计数,以及包含 1 项(usmStatsUnknownEngineIDs,即收到的带有未知 [null] 引擎 ID 的请求计数)的变量绑定列表。 在来自浏览器/管理器的后续 GETNEXT 请求中,仅当启用了安全性时才会填写启动数据和安全参数。 如果启用了,它还将在 PDU 中发送 NotInTime 数据更新。 安全参数,例如,身份验证会向管理器证明代理的身份。

有关 SNMPv3 身份验证的更多详细信息,请访问 RFC 3414“简单网络管理协议第 3 版 (SNMPv3) 基于用户的安全模型 (USM)”。

NetX Duo SNMP RFC

NetX Duo SNMP 符合 RFC1155、RFC1157、RFC1215、RFC1901、RFC1905、RFC1906、RFC1907、RFC1908、RFC2571、RFC2572、RFC2574、RFC2575、RFC 3414 和相关 RFC。