0
0
mirror of https://gitlab.nic.cz/labs/bird.git synced 2024-12-23 02:01:55 +00:00
bird/proto/snmp/subagent.c
2022-08-10 17:31:32 +02:00

418 lines
9.3 KiB
C

/*
* BIRD -- Simple Network Management Protocol (SNMP)
*
* (c) 2022 Vojtech Vilimek <vojtech.vilimek@nic.cz>
* (c) 2022 CZ.NIC z.s.p.o
*
* Can be freely distributed and used under the terms of the GNU GPL.
*
*/
#include "lib/unaligned.h"
#include "subagent.h"
static int parse_response(struct snmp_proto *p, byte *buf, uint size);
static void header_update_len(byte *buf, u32 len);
static uint oid_size(struct oid* o);
static const char * const snmp_errs[] = {
#define SNMP_ERR_SHIFT 256
[AGENTX_RES_OPEN_FAILED - SNMP_ERR_SHIFT] = "Open failed",
[AGENTX_RES_NOT_OPEN - SNMP_ERR_SHIFT] = "Not open",
[AGENTX_RES_INDEX_WRONG_TYPE - SNMP_ERR_SHIFT] = "Index wrong type",
[AGENTX_RES_INDEX_ALREADY_ALLOC - SNMP_ERR_SHIFT] = "Index already allocated",
[AGENTX_RES_INDEX_NONE_AVAIL - SNMP_ERR_SHIFT] = "Index none availlable",
[AGENTX_RES_NOT_ALLOCATED - SNMP_ERR_SHIFT] = "Not allocated",
[AGENTX_RES_UNSUPPORTED_CONTEXT - SNMP_ERR_SHIFT] = "Unsupported contex",
[AGENTX_RES_DUPLICATE_REGISTR - SNMP_ERR_SHIFT] = "Duplicate registration",
[AGENTX_RES_UNKNOWN_REGISTR - SNMP_ERR_SHIFT] = "Unknown registration",
[AGENTX_RES_UNKNOWN_AGENT_CAPS - SNMP_ERR_SHIFT] = "Unknown agent caps",
[AGENTX_RES_PARSE_ERROR - SNMP_ERR_SHIFT] = "Parse error",
[AGENTX_RES_REQUEST_DENIED - SNMP_ERR_SHIFT] = "Request denied",
[AGENTX_RES_PROCESSING_ERR - SNMP_ERR_SHIFT] = "Processing error",
};
static int
put_str(byte *buf, const char *str, uint *size)
{
uint len = strlen(str);
uint pkt_len = BIRD_ALIGN(len, 4);
if (len > MAX_STR)
return -1;
put_u32(buf, len);
memcpy(buf + 4, str, len);
// make the value 32-bit aligned
for (uint i = 0; i < pkt_len - len; i++)
buf[len + i] = 0x00; // PADDING
*size += (4 + pkt_len);
return 0;
}
static void
put_blank(byte *buf, uint *size)
{
buf[0] = buf[1] = buf[2] = buf[3] = 0;
*size += 4;
}
static void
put_oid(byte *buf, struct oid *oid, uint *size)
{
put_u8(buf, oid->n_subid);
put_u8(buf + 1, oid->prefix);
put_u8(buf + 2, oid->include);
put_u8(buf + 3, 0); // PADDING
put_u32s(buf + 4, oid->subid.ids, oid->subid.len);
*size += (4 + oid->subid.len);
}
/* paste data at first byte in message
* with 3B of padding
*/
static void
paste_fbyte(byte *buf, u8 data, uint *size)
{
buf[0] = data;
buf[1] = buf[2] = buf[3] = 0x00; // PADDING
*size += 4;
}
static u32
store_in_order(u32 val, int order)
{
/* AGENTX_BIG_ENDIAN */
if (order)
{
}
else
{
}
return 0;
}
static void
open_pdu(struct snmp_proto *p, struct oid *oid)
{
sock *sk = p->sock;
byte *buf, *pkt, *end;
buf = pkt = sk->tbuf;
uint size = sk->tbsize;
// should be configurable
const char *str = "bird";
uint pkt_size = 0;
uint slen = BIRD_ALIGN(strlen(str), 4);
/* +8 - header of oid (4) and octet string length (4) */
if (size > AGENTX_HEADER_SIZE + oid->subid.len + slen + 8)
{
log(L_INFO "open_pdu() sufficient size nw order: %u",
AGENTX_NETWORK_BYTE_ORDER);
PASTE_HEADER(pkt, AGENTX_OPEN_PDU, AGENTX_NETWORK_BYTE_ORDER, size);
// use random num instead
put_u32(&h->session_id, 1);
put_u32(&h->transaction_id, 1);
put_u32(&h->packet_id, 1);
paste_fbyte(pkt, p->timeout, &pkt_size);
ADVANCE(pkt, size, 4);
put_oid(pkt, oid, &pkt_size);
ADVANCE(pkt, size, oid_size(oid));
/* paste description */
put_str(pkt, str, &pkt_size);
ADVANCE(pkt, size, slen);
header_update_len(buf, pkt_size);
log(L_INFO "sk_send()-ing %u", AGENTX_HEADER_SIZE + pkt_size);
int ret = sk_send(sk, AGENTX_HEADER_SIZE + pkt_size);
if (ret == 0)
log(L_INFO "sleep");
else if (ret < 0)
log(L_INFO "err %d", ret);
else
log(L_INFO "ok !!! ");
}
else
log(L_INFO "open_pdu() insufficient size, %u <= %u ",
size, AGENTX_HEADER_SIZE + oid->subid.len + slen + 8);
}
static void
close_pdu(struct snmp_proto *p, u8 reason)
{
sock *sk = p->sock;
byte *buf, *pkt;
buf = pkt = sk->tbuf;
uint size = sk->tbsize;
log(L_INFO "close_pdu() size: %u %c %u", size, (size > AGENTX_HEADER_SIZE + 4)
? '>':'<', AGENTX_HEADER_SIZE);
/* +4B for reason */
if (size > AGENTX_HEADER_SIZE + 4)
{
PASTE_HEADER(buf, AGENTX_CLOSE_PDU, AGENTX_NETWORK_BYTE_ORDER, size);
log(L_INFO "session_id %u", p->session_id);
h->session_id = p->session_id;
p->transaction_id++;
p->transaction_id = get_u32(&p->transaction_id);
put_u32(&h->transaction_id, p->transaction_id);
put_u32(&h->packet_id, 1);
ADVANCE(pkt, size, sizeof(struct agentx_header));
paste_fbyte(pkt, reason, &size);
ADVANCE(pkt, size, 4);
/* 4 - reason size */
header_update_len(sk->tbuf, 4);
int ret = sk_send(sk, sizeof(struct agentx_header) + 4);
if (ret == 0)
log(L_INFO "sleep");
else if (ret < 0)
log(L_INFO "err");
else
log(L_INFO, "ok !! ");
}
}
static int
parse_pkt(struct snmp_proto *p, byte *buf, uint size)
{
if (size < AGENTX_HEADER_SIZE)
return 0;
struct agentx_header *h = (void *) buf;
switch (h->type)
{
case AGENTX_RESPONSE_PDU:
return parse_response(p, buf, size);
break;
/* should not happen */
default:
die("unknown packet type");
}
}
static int
parse_response(struct snmp_proto *p, byte *buf, uint size)
{
if (size < sizeof(struct agentx_response))
return 0;
struct agentx_response *r = (void *) buf;
struct agentx_header *h = &r->h;
log(L_INFO "endianity: %s, session %u", (h->flags & AGENTX_NETWORK_BYTE_ORDER) ? "big end":
"little end", h->session_id);
p->session_id = h->session_id;
p->transaction_id = h->transaction_id;
p->packet_id = h->packet_id;
log(L_INFO "size %u", get_u32(&h->payload));
log(L_INFO "uptime: %u s", get_u32(&r->uptime));
switch (r->err)
{
case AGENTX_RES_NO_ERROR:
break;
default:
log(L_INFO "an error occured: '%s'", snmp_errs[get_u16(&r->err) -
SNMP_ERR_SHIFT]);
break;
}
proto_notify_state(&p->p, PS_UP);
return 1;
}
static void
header_update_len(byte *buf, u32 len)
{
struct agentx_header *h = (void *) buf;
put_u32(&h->payload, len);
log(L_INFO "header_update_len() %d 0x%02X 0x%02X 0x%02X 0x%02X", len, *((unsigned char
*) &h->payload), *(((unsigned char *) &h->payload) + 1), *(((unsigned char *)
&h->payload) + 2), *(((unsigned char *) &h->payload) + 3));
}
void
snmp_start_subagent(struct snmp_proto *p)
{
log(L_INFO "snmp_start_subagent() starting subagent");
/* blank oid means unsupported */
struct oid *o = mb_allocz(p->p.pool, sizeof(struct oid));
open_pdu(p, o);
mb_free(o);
}
void
snmp_stop_subagent(struct snmp_proto *p)
{
sock *sk = p->sock;
close_pdu(p, AGENTX_CLOSE_SHUTDOWN);
sk->rx_hook = snmp_stop_ack;
}
static uint
oid_size(struct oid *o)
{
return 4 + o->subid.len;
}
int
snmp_rx(sock *sk, uint size)
{
log(L_INFO "snmp_rx()");
struct snmp_proto *p = sk->data;
byte *pkt = sk->rbuf;
byte *end = pkt + size;
parse_pkt(p, pkt, size);
/*
while (end >= ptk + AGENTX_HEADER_SIZE)
{
parse_header(p);
parse_pkt(p, );
}
*/
return 0;
// 1 means all done
}
void
ping_pdu(struct snmp_proto *p)
{
/* this does not support non-default context */
sock *sk = p->sock;
byte *buf = sk->tbuf;
uint size = sk->tbsize;
PASTE_HEADER(buf, AGENTX_PING_PDU, AGENTX_NETWORK_BYTE_ORDER, size);
put_u32(&h->session_id, p->session_id);
p->transaction_id++;
put_u32(&h->transaction_id, p->transaction_id);
put_u32(&h->packet_id, 1);
put_u32(&h->payload, 0);
sk_send(sk, AGENTX_HEADER_SIZE);
}
/*
* cont is optional context
* upp_b is upper_bond
*/
int
snmp_register_oid(sock *sk, struct oid *subtree, u8 range, const char *cont, u32 upp_b)
{
struct snmp_proto *p = sk->data;
byte *buf = sk->tbuf;
uint size = sk->tbsize;
log(L_INFO "snmp_register_oid() ");
u8 flags = AGENTX_NETWORK_BYTE_ORDER | ((cont) ? AGENTX_NON_DEFAULT_CONTEXT :
0);
PASTE_HEADER(buf, AGENTX_REGISTER_PDU, flags, size);
if (cont)
put_str(buf, cont, &size);
put_u8(buf, p->timeout);
put_u8(buf + 1, AGENTX_PRIORITY);
put_u8(buf + 2, range);
put_u8(buf + 3, 0); // PADDING
ADVANCE(buf, size, 4);
put_oid(buf, subtree, &size);
ADVANCE(buf, size, oid_size(subtree));
if (upp_b)
{
put_u32(buf, upp_b);
ADVANCE(buf, size, 4);
}
header_update_len(sk->tbuf, buf - sk->tbuf + AGENTX_HEADER_SIZE);
sk_send(sk, buf - sk->tbuf);
}
/*
* cont is optional context nullable
* upp_b is upper_bond
*/
int
snmp_unregister_oid(sock *sk, struct oid *subtree, const char *cont, u32 upp_b)
{
byte *buf = sk->tbuf;
uint size = sk->tbsize;
log(L_INFO "snmp_unregister_oid()");
u8 flags = AGENTX_NETWORK_BYTE_ORDER | ((cont) ? AGENTX_NON_DEFAULT_CONTEXT :
0);
PASTE_HEADER(buf, AGENTX_UNREGISTER_PDU, flags, size);
if (cont)
{
put_str(buf, cont, &size);
ADVANCE(buf, size, strlen(cont) + 4);
}
put_oid(buf, subtree, &size);
ADVANCE(buf, size, oid_size(subtree));
if (upp_b)
{
put_u32(buf, upp_b);
ADVANCE(buf, size, 4);
}
sk_send(sk, buf - sk->tbuf);
}
static void
snmp_stop_ack(sock *sk, uint size)
{
struct snmp_proto *p = sk->data;
byte *buf = sk->rbuf;
if (size < AGENTX_HEADER_SIZE)
return 0;
if (parse_response(p, buf, size))
{
p->p.disabled = 1;
proto_notify_state(&p->p, PS_DOWN);
}
}
/*
void
snmp_agent_reconfigure(void)
{
}
*/
#undef SNMP_ERR_SHIFT