Why is my snmp packet not valid

2019-09-16 03:16发布

问题:

I wrote a similiar question here but did not get an answer which solve my problem, so I write the question again on stackoverflow and hope that somebody can help me.

Here is a runnable example about my problem. Packet out_buf_0 represent a valid SNMP packet and can send via UDP. Packet out_buf_1 is the same packet like out_buf_0 with one character more at the end 0x64. Further, I raised all length + 1 because of the additional character. Why is out_buf_1 not a valid SNMPv1 packet/why can it not send via UDP? Note: The SNMP request can not display in terminal because the request id from client is different from out_buf_0 and out_buf_1, take a look in wireshark to see the request/response. The whole frame length including my SNMPv1 packet out_buf_0 is 1368 bits, out_buf_1 should 1376 bits.

#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);
}

回答1:

Under ASN.1 Basic Encoding Rules (BER), an ASN.1 encoding consists of a single octet tag value, one or more length octets, and zero or more content octets. The length part of an encoding takes one of two forms: a single-octet form or a multi-octet form. The high order bit indicates which form the length field takes: unset is a single-octet length, in which the single octet (of range 00..7F) indicates the content length, and set is a multi-octet length (80..FF), in which the remaining bits of the first octet specify how many following octets comprise the actual length of contents.

int out_buf_1_len = 130; /* 0x80 + 2 */
char out_buf_1[] = {
0x30, /* SNMP Packet start */
0x80, /* SNMP Packet length */
...

What you have here is a length octet of 0x80, which indicates it is a multi-byte length field where the length of the length field itself is 0, so the length of expected contents is 0. What you would want at this point is 0x81 (the 0x80 bit here indicating multi-byte length, and the 0x01 part here indicating one octet follows for content length) followed by a 0x80 octet (indicating your length of contents) before the rest of your contents.

I stopped analyzing there.