【问题标题】:Is there a limit how many character can be send over udp/snmp?通过 udp/snmp 可以发送多少个字符有限制吗?
【发布时间】:2017-08-25 07:39:52
【问题描述】:

我正在尝试通过 UDP/SNMP 将字符串从服务器发送到客户端。但是,如果我将长度为 86 的字符串复制到 pdu packet->value_value 一切正常,我会在客户端得到字符串。如果我在字符串中再添加一个字符,长度现在为87,则数据包无法传递给客户端。数据包大小正确增加。可以发送多少个字符有限制吗?我的数据包小于 1500。

这段代码 sn-p 显示了我如何将字符串复制到packet->value_value

value_value = "asd asd asd asd asd asd asd asd asd asd asd asd asd asd asd asd asd asd asd asd asd as";
printf("String length: %d\n",strlen(value_value)); // 86 OK, 87or greater NOK
packet->value_length = strlen(value_value);
packet->value_value = malloc(packet->value_length);
strcpy(packet->value_value, value_value);

对于一些信息,这里是一个终端输出,其中包含一些关于数据包长度的更多信息

字符串长度为86的输出

src/agent.c:69:parse_request(): Input length=43
src/agent.c:116:perform_snmp_request(): OID is: 1.3.6.1.2.1.1.1.0
String length: 86
src/mibservice.c:90:snmpget(): packet->value_type=04
src/mibservice.c:91:snmpget(): packet->value_length=56
src/mibservice.c:93:snmpget(): packet->value_value=61
...
src/mibservice.c:93:snmpget(): packet->value_value=73
src/mibservice.c:95:snmpget(): Before packet->length=41
src/mibservice.c:97:snmpget(): After packet->length=127
src/mibservice.c:99:snmpget(): Before packet->pdu_length=28
src/mibservice.c:101:snmpget(): After packet->pdu_length=114
src/mibservice.c:103:snmpget(): Before packet->variablebindings_length=14
src/mibservice.c:105:snmpget(): After packet->variablebindings_length=100
src/mibservice.c:107:snmpget(): Before packet->varbind_length=12
src/mibservice.c:109:snmpget(): After packet->varbind_length=98
src/agent.c:96:create_response(): Output length=129

字符串长度为87的输出

src/agent.c:69:parse_request(): Input length=43
src/agent.c:116:perform_snmp_request(): OID is: 1.3.6.1.2.1.1.1.0
String length: 87
src/mibservice.c:90:snmpget(): packet->value_type=04
src/mibservice.c:91:snmpget(): packet->value_length=57
src/mibservice.c:93:snmpget(): packet->value_value=61
...
src/mibservice.c:93:snmpget(): packet->value_value=64
src/mibservice.c:95:snmpget(): Before packet->length=41
src/mibservice.c:97:snmpget(): After packet->length=128
src/mibservice.c:99:snmpget(): Before packet->pdu_length=28
src/mibservice.c:101:snmpget(): After packet->pdu_length=115
src/mibservice.c:103:snmpget(): Before packet->variablebindings_length=14
src/mibservice.c:105:snmpget(): After packet->variablebindings_length=101
src/mibservice.c:107:snmpget(): Before packet->varbind_length=12
src/mibservice.c:109:snmpget(): After packet->varbind_length=99
src/agent.c:96:create_response(): Output length=130

更新 这是一个关于我的问题的可运行示例。数据包out_buf_0 代表一个有效的 SNMP 数据包,可以通过 UDP 发送。包out_buf_1out_buf_0 是同一个包,最后多了一个字符0x64。此外,由于附加字符,我提高了所有长度 + 1。为什么out_buf_1 不是有效的 SNMP 数据包/为什么不能通过 UDP 发送?注意:由于客户端的request idout_buf_0out_buf_1不同,SNMP请求无法在终端显示,请查看wireshark查看请求/响应。

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <netdb.h>
#include <sys/socket.h>
#include <arpa/inet.h>

#define MESSAGE_MAX_LEN  1500 /* MTU, IEEE Std 802.3TM-2015 */
#define PORT 161 /* RFC 1157 */

int out_buf_0_len = 129; /* 0x7f + 2 */
char out_buf_0[] = {
0x30, /* SNMP Packet start */
0x7f, /* SNMP Packet length */
0x02, 0x01, 0x00, /* Version */
0x04, 0x06, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, /* Community*/
0xa2, /* GetResponsePDU */
0x72, /* GetResponsePDU Length */
0x02, 0x04, 0x2c, 0x80, 0x7e, 0x2f, /* Request id */
0x02, 0x01, 0x00, /*Error status */
0x02, 0x01, 0x00, /*Error index */
0x30, /* Varbind list start */
0x64, /* Varbind list length*/
0x30, /* Varbind value start */
0x62, /* Varbind value length */
0x06, 0x08, 0x2b, 0x06, 0x01, 0x02, 0x01, 0x01, 0x01, 0x00, /* OID */
0x04,  /* Value start, type octet-string*/
0x56, /* Value length */
0x61, 0x73, 0x64, 0x20, 0x61, /* Value */
0x73, 0x64, 0x20, 0x61, 0x73, 0x64, 0x20, 0x61,
0x73, 0x64, 0x20, 0x61, 0x73, 0x64, 0x20, 0x61,
0x73, 0x64, 0x20, 0x61, 0x73, 0x64, 0x20, 0x61,
0x73, 0x64, 0x20, 0x61, 0x73, 0x64, 0x20, 0x61,
0x73, 0x64, 0x20, 0x61, 0x73, 0x64, 0x20, 0x61,
0x73, 0x64, 0x20, 0x61, 0x73, 0x64, 0x20, 0x61,
0x73, 0x64, 0x20, 0x61, 0x73, 0x64, 0x20, 0x61,
0x73, 0x64, 0x20, 0x61, 0x73, 0x64, 0x20, 0x61,
0x73, 0x64, 0x20, 0x61, 0x73, 0x64, 0x20, 0x61,
0x73, 0x64, 0x20, 0x61, 0x73, 0x64, 0x20, 0x61,
0x73 };

int out_buf_1_len = 130; /* 0x80 + 2 */
char out_buf_1[] = {
0x30, /* SNMP Packet start */
0x80, /* SNMP Packet length */
0x02, 0x01, 0x00, /* Version */
0x04, 0x06, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, /* Community*/
0xa2, /* GetResponsePDU */
0x73, /* GetResponsePDU Length */
0x02, 0x04, 0x2c, 0x80, 0x7e, 0x2f, /* Request id */
0x02, 0x01, 0x00, /*Error status */
0x02, 0x01, 0x00, /*Error index */
0x30, /* Varbind list start */
0x65, /* Varbind list length*/
0x30, /* Varbind value start */
0x63, /* Varbind value length */
0x06, 0x08, 0x2b, 0x06, 0x01, 0x02, 0x01, 0x01, 0x01, 0x00, /* OID */
0x04,  /* Value start, type octet-string*/
0x57, /* Value length */
0x61, 0x73, 0x64, 0x20, 0x61, /* Value */
0x73, 0x64, 0x20, 0x61, 0x73, 0x64, 0x20, 0x61,
0x73, 0x64, 0x20, 0x61, 0x73, 0x64, 0x20, 0x61,
0x73, 0x64, 0x20, 0x61, 0x73, 0x64, 0x20, 0x61,
0x73, 0x64, 0x20, 0x61, 0x73, 0x64, 0x20, 0x61,
0x73, 0x64, 0x20, 0x61, 0x73, 0x64, 0x20, 0x61,
0x73, 0x64, 0x20, 0x61, 0x73, 0x64, 0x20, 0x61,
0x73, 0x64, 0x20, 0x61, 0x73, 0x64, 0x20, 0x61,
0x73, 0x64, 0x20, 0x61, 0x73, 0x64, 0x20, 0x61,
0x73, 0x64, 0x20, 0x61, 0x73, 0x64, 0x20, 0x61,
0x73, 0x64, 0x20, 0x61, 0x73, 0x64, 0x20, 0x61,
0x73, 0x64 };

int my_socket;
struct sockaddr_in remote_addr;
int socket_create()
{
    printf("Create socket\n");
    struct sockaddr_in socket_addr;
    if ((my_socket = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
    {
        printf("Cannot create socket. Exit.\n");
        return -1;
    }
    memset((char *)&socket_addr, 0, sizeof(socket_addr));
    socket_addr.sin_family = AF_INET;
    socket_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    socket_addr.sin_port = htons(PORT);
    if (bind(my_socket, (struct sockaddr *)&socket_addr, sizeof(socket_addr)) < 0)
    {
        printf("Bind failed. Exit.\n");
        return - 1;
    }
    printf("Listen on: %s:%d\n", inet_ntoa(socket_addr.sin_addr), PORT);
    return 0;
}

socklen_t addr_len = sizeof(remote_addr);
void socket_listen(char *in_buf)
{
    int  recv_len; /* Bytes received */
    int  nbyt; /* Bytes count */
    char *out_buf[MESSAGE_MAX_LEN];
    int  out_len = 0;

    for (;;) { /* Receive snmp message from snmp manager */
        recv_len = recvfrom(my_socket, in_buf, MESSAGE_MAX_LEN, 0, (struct sockaddr *)&remote_addr, &addr_len);
        if (recv_len > 0)
            if (sendto(my_socket, out_buf_1, out_buf_1_len, 0, (struct sockaddr *)&remote_addr, addr_len) < 0)
                printf("Cannot send data to destination.\n");
    }
}

/* Disable SNMP on local machine. # systemctl stop snmpd 
 * Execute main(): gcc <filename>.c && ./a.out
 * Run SNMP Request: $ snmpget -v 1 -c public 0.0.0.0:161 1.3.6.1.2.1.1.1.0
 */
char in_buf[MESSAGE_MAX_LEN];
int main(int argc, char **argv)
{
    if (socket_create() == -1)
        exit(2);
    socket_listen(in_buf);
}

包括我的 SNMPv1 数据包out_buf_0 在内的整个帧长度是 1368 位,out_buf_1 应该是 1376 位。

【问题讨论】:

  • 减1? malloc(packet-&gt;value_length + 1);strcpy 之前添加 1 以作为 NUL 终止符。或者也许是packet-&gt;value_length = strlen(value_value) + 1;
  • @WeatherVane 问题是一样的,如果我添加 +1 值长度是错误的。
  • 你的程序超出了内存分配,导致了UB。
  • 回答问题的标题,UDP头中的长度字段是一个无符号的16位字段,包含头的长度加上消息的字节长度。理论上这是 65,535 个字节。实际上,MTU 大小将成为您的限制因素,通常为 1500 字节。如果你在环回上做所有事情,那么 MTU 大小并不重要(至少在我的 linux 系统上)。看看here。但是发送一个 87 长的字符串应该没有问题。 (假设字节=8 位)
  • @WeatherVane 我现在能做什么。什么是 UB?

标签: c snmp


【解决方案1】:

正如有人在 cmets 中指出的那样,strcpy 是邪恶的,永远不应该用于任何事情。您的 malloc 实际上分配了一个小于原始数组的数组,因为您没有考虑尾随空值。当您使用 strcpy 时,您在分配数组的末尾写了结尾的 null。

由于您没有包含与数据包结构相关的代码,我不知道它是如何使用的,但如果这之后的任何代码需要 C 空终止字符串,它可能不会按预期工作。

【讨论】:

  • strcpy 不再是邪恶的,比任何其他库函数都邪恶。 (strncpy 确实没有将所有未使用的空间归零的效率低下)。问题在于正确计算所需的所有字符,包括 nul-terminating 字符。 (这是 C 字符串字符数组 的区别)。如果没有 nul-terminating 字符,strcpy 将愉快地继续在堆栈中复制值,直到遇到第一个流浪的 0。使用正确的 nul-terminated 字符串 strcpy 非常好。
【解决方案2】:

SNMP数据包长度0x80错误,见here

0x80 的 ASN.1 长度字段错误。解码器看到第一个 您的数据包的两个八位字节是 0x30 0x80 并从该 0x80 确定 后面不应该有更多的八位位组。还有更多 八位字节,因此它是无效的编码,而且不是有效的 SNMP 信息。这里的 0x80 并不意味着 128 个八位字节的内容 打算,这意味着您的长度字段是 0x80 & ~0x80 == 0 字节长 跟随这个八位字节。相反, 0x81 意味着您的长度字段是 0x81 & ~0x80 == 1 在八位字节之后,以及在 0x80 八位字节之后 将指示内容长度为 128。

这就是 SNMP 数据包的样子,

char out_buf_1[] = {
0x30, /* SNMP Packet start */
0x81, 0x80, /* SNMP Packet length */
0x02, 0x01, 0x00, /* Version */
0x04, 0x06, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, /* Community*/
0xa2, /* GetResponsePDU */
0x73, /* GetResponsePDU Length */
0x02, 0x04, 0x2c, 0x80, 0x7e, 0x2f, /* Request id */
0x02, 0x01, 0x00, /*Error status */
0x02, 0x01, 0x00, /*Error index */
0x30, /* Varbind list start */
0x65, /* Varbind list length*/
0x30, /* Varbind value start */
0x63, /* Varbind value length */
0x06, 0x08, 0x2b, 0x06, 0x01, 0x02, 0x01, 0x01, 0x01, 0x00, /* OID */
0x04,  /* Value start, type octet-string*/
0x57, /* Value length */
0x61, 0x73, 0x64, 0x20, 0x61, /* Value */
0x73, 0x64, 0x20, 0x61, 0x73, 0x64, 0x20, 0x61,
0x73, 0x64, 0x20, 0x61, 0x73, 0x64, 0x20, 0x61,
0x73, 0x64, 0x20, 0x61, 0x73, 0x64, 0x20, 0x61,
0x73, 0x64, 0x20, 0x61, 0x73, 0x64, 0x20, 0x61,
0x73, 0x64, 0x20, 0x61, 0x73, 0x64, 0x20, 0x61,
0x73, 0x64, 0x20, 0x61, 0x73, 0x64, 0x20, 0x61,
0x73, 0x64, 0x20, 0x61, 0x73, 0x64, 0x20, 0x61,
0x73, 0x64, 0x20, 0x61, 0x73, 0x64, 0x20, 0x61,
0x73, 0x64, 0x20, 0x61, 0x73, 0x64, 0x20, 0x61,
0x73, 0x64, 0x20, 0x61, 0x73, 0x64, 0x20, 0x61,
0x73, 0x64 };

【讨论】:

    猜你喜欢
    • 2011-11-22
    • 1970-01-01
    • 2023-03-28
    • 1970-01-01
    • 1970-01-01
    • 2016-04-01
    • 2012-10-10
    • 2014-08-24
    • 1970-01-01
    相关资源
    最近更新 更多