mirror of
https://github.com/clearlinux/uwsgi.git
synced 2026-08-18 11:39:05 +00:00
464 lines
9.3 KiB
C
464 lines
9.3 KiB
C
#include <uwsgi.h>
|
|
|
|
extern struct uwsgi_server uwsgi;
|
|
|
|
#define SNMP_SEQUENCE 0x30
|
|
#define SNMP_INTEGER 0x02
|
|
#define SNMP_STRING 0x04
|
|
#define SNMP_NULL 0x05
|
|
#define SNMP_GET 0xA0
|
|
#define SNMP_RES 0xA2
|
|
#define SNMP_OID 0x06
|
|
|
|
#define SNMP_WATERMARK (127-8)
|
|
|
|
/* 1.3.6.1.4.1.35156.17.X.X */
|
|
#define SNMP_UWSGI_BASE "\x2B\x06\x01\x04\x01\x82\x92\x54\x11"
|
|
|
|
static int get_snmp_integer(uint8_t *, uint64_t *);
|
|
|
|
static uint64_t get_uwsgi_snmp_value(uint64_t, uint8_t *);
|
|
static uint64_t get_uwsgi_custom_snmp_value(uint64_t, uint8_t *);
|
|
|
|
static uint8_t snmp_int_to_snmp(uint64_t, uint8_t, uint8_t *);
|
|
|
|
static ssize_t build_snmp_response(uint8_t, uint8_t, uint8_t *, int, uint8_t *, uint8_t *, uint8_t *);
|
|
static ssize_t build_snmp_metric_response(int64_t, uint8_t, uint8_t *, int, uint8_t *, uint8_t *, uint8_t *);
|
|
|
|
void manage_snmp(int fd, uint8_t * buffer, int size, struct sockaddr_in *client_addr) {
|
|
|
|
uint16_t asnlen;
|
|
uint16_t oidlen;
|
|
|
|
uint8_t oid_part[2];
|
|
|
|
int ptrdelta;
|
|
|
|
uint8_t *ptr = buffer, *seq1, *seq2, *seq3;
|
|
|
|
uint8_t community_len;
|
|
|
|
uint64_t snmp_int = 0;
|
|
uint64_t request_id = 0;
|
|
uint64_t version = 0;
|
|
|
|
|
|
// KISS for memory management
|
|
if (size > SNMP_WATERMARK)
|
|
return;
|
|
ptr++;
|
|
|
|
|
|
// check total sequence size
|
|
if (*ptr > SNMP_WATERMARK || *ptr < 13)
|
|
return;
|
|
ptr++;
|
|
|
|
#ifdef UWSGI_DEBUG
|
|
uwsgi_debug("SNMP packet size: %d\n", size);
|
|
#endif
|
|
|
|
// check snmp version
|
|
if (*ptr != SNMP_INTEGER)
|
|
return;
|
|
ptr++;
|
|
|
|
ptrdelta = get_snmp_integer(ptr, &version);
|
|
if (version > 2)
|
|
return;
|
|
ptr += ptrdelta;
|
|
|
|
#ifdef UWSGI_DEBUG
|
|
uwsgi_debug("SNMP version: %d\n", version);
|
|
#endif
|
|
|
|
// check for community string (this must be set from the api using uwsgi.snmp_community or with --snmp-community arg)
|
|
if (*ptr != SNMP_STRING)
|
|
return;
|
|
ptr++;
|
|
|
|
community_len = *ptr;
|
|
|
|
if (community_len > 72 || community_len < 1)
|
|
return;
|
|
ptr++;
|
|
|
|
// check for community string
|
|
if (strlen(uwsgi.shared->snmp_community) != community_len)
|
|
return;
|
|
if (memcmp(ptr, uwsgi.shared->snmp_community, community_len))
|
|
return;
|
|
|
|
#ifdef UWSGI_DEBUG
|
|
uwsgi_debug("SNMP community: %.*s\n", community_len, ptr);
|
|
#endif
|
|
|
|
ptr += community_len;
|
|
|
|
// check for get request
|
|
if (*ptr != SNMP_GET)
|
|
return;
|
|
|
|
*ptr = SNMP_RES;
|
|
ptr++;
|
|
seq1 = ptr;
|
|
|
|
|
|
if (*ptr != ((size - community_len) - 9))
|
|
return;
|
|
ptr++;
|
|
|
|
// get request_id
|
|
if (*ptr != SNMP_INTEGER)
|
|
return;
|
|
ptr++;
|
|
|
|
ptrdelta = get_snmp_integer(ptr, &request_id);
|
|
|
|
if (ptrdelta <= 0)
|
|
return;
|
|
|
|
#ifdef UWSGI_DEBUG
|
|
uwsgi_debug("SNMP request id: %d %p\n", request_id, ptr);
|
|
#endif
|
|
|
|
// check here
|
|
if (ptr + ptrdelta >= buffer + size)
|
|
return;
|
|
ptr += ptrdelta;
|
|
|
|
|
|
// get error
|
|
if (*ptr != SNMP_INTEGER)
|
|
return;
|
|
|
|
ptr++;
|
|
snmp_int = 0;
|
|
ptrdelta = get_snmp_integer(ptr, &snmp_int);
|
|
|
|
if (ptrdelta <= 0)
|
|
return;
|
|
if (ptr + ptrdelta >= buffer + size)
|
|
return;
|
|
if (snmp_int != 0)
|
|
return;
|
|
|
|
ptr += ptrdelta;
|
|
|
|
// get index
|
|
if (*ptr != SNMP_INTEGER)
|
|
return;
|
|
ptr++;
|
|
snmp_int = 0;
|
|
ptrdelta = get_snmp_integer(ptr, &snmp_int);
|
|
if (ptrdelta <= 0)
|
|
return;
|
|
if (ptr + ptrdelta >= buffer + size)
|
|
return;
|
|
|
|
if (snmp_int != 0)
|
|
return;
|
|
ptr += ptrdelta;
|
|
|
|
#ifdef UWSGI_DEBUG
|
|
uwsgi_debug("SNMP int [1]: %d\n", snmp_int);
|
|
#endif
|
|
|
|
// check for sequence
|
|
if (*ptr != SNMP_SEQUENCE)
|
|
return;
|
|
ptr++;
|
|
|
|
|
|
if (*ptr > SNMP_WATERMARK)
|
|
return;
|
|
seq2 = ptr;
|
|
ptr++;
|
|
|
|
// now the interesting stuff: OID management
|
|
if (*ptr != SNMP_SEQUENCE)
|
|
return;
|
|
ptr++;
|
|
|
|
// check for normal OID uWSGI size: |1.3|.6|.1|.4|.1.|35156|.17|.1/2|.x| + OID_NULL
|
|
asnlen = *ptr;
|
|
if (asnlen < 15)
|
|
return;
|
|
seq3 = ptr;
|
|
ptr++;
|
|
|
|
#ifdef UWSGI_DEBUG
|
|
uwsgi_debug("SNMP ASN len: %d\n", asnlen);
|
|
#endif
|
|
|
|
// is it an OID ?
|
|
if (*ptr != SNMP_OID)
|
|
return;
|
|
ptr++;
|
|
|
|
oidlen = *ptr;
|
|
if (oidlen < 11)
|
|
return;
|
|
ptr++;
|
|
|
|
// and now parse the OID !!!
|
|
if (memcmp(ptr, SNMP_UWSGI_BASE, 9))
|
|
return;
|
|
|
|
ptr += 9;
|
|
|
|
oid_part[0] = *ptr;
|
|
|
|
// old-style SNMP metrics
|
|
if (oid_part[0] == 1 || oid_part[0] == 2) {
|
|
ptr++;
|
|
|
|
oid_part[1] = *ptr;
|
|
if (oid_part[1] < 1 || oid_part[1] > 100)
|
|
return;
|
|
ptr++;
|
|
|
|
// check for null
|
|
if (memcmp((char *) ptr, "\x05\x00", 2))
|
|
return;
|
|
ptr += 2;
|
|
size = build_snmp_response(oid_part[0], oid_part[1], buffer, size, seq1, seq2, seq3);
|
|
}
|
|
// metrics subsystem
|
|
else {
|
|
size_t metric_asn_len = oidlen - 9;
|
|
char *metric_asn = (char *) ptr;
|
|
struct uwsgi_metric *um = uwsgi_metric_find_by_asn(metric_asn, metric_asn_len);
|
|
if (!um) return;
|
|
uwsgi_rlock(uwsgi.metrics_lock);
|
|
int64_t value = *um->value;
|
|
uwsgi_rwunlock(uwsgi.metrics_lock);
|
|
size = build_snmp_metric_response(value, um->type, buffer, size, seq1, seq2, seq3);
|
|
}
|
|
|
|
|
|
if (size > 0) {
|
|
if (sendto(fd, buffer, size, 0, (struct sockaddr *) client_addr, sizeof(struct sockaddr_in)) < 0) {
|
|
uwsgi_error("sendto()");
|
|
}
|
|
}
|
|
}
|
|
|
|
static uint64_t get_uwsgi_snmp_value(uint64_t val, uint8_t * oid_t) {
|
|
|
|
val--;
|
|
|
|
if (uwsgi.shared->snmp_gvalue[val].type) {
|
|
*oid_t = uwsgi.shared->snmp_gvalue[val].type;
|
|
return *uwsgi.shared->snmp_gvalue[val].val;
|
|
}
|
|
|
|
*oid_t = SNMP_NULL;
|
|
return 0;
|
|
}
|
|
|
|
static uint64_t get_uwsgi_custom_snmp_value(uint64_t val, uint8_t * oid_t) {
|
|
|
|
val--;
|
|
uwsgi_wlock(uwsgi.snmp_lock);
|
|
if (uwsgi.shared->snmp_value[val].type) {
|
|
*oid_t = uwsgi.shared->snmp_value[val].type;
|
|
uwsgi_rwunlock(uwsgi.snmp_lock);
|
|
return uwsgi.shared->snmp_value[val].val;
|
|
}
|
|
|
|
uwsgi_rwunlock(uwsgi.snmp_lock);
|
|
*oid_t = SNMP_NULL;
|
|
return 0;
|
|
}
|
|
|
|
static int get_snmp_integer(uint8_t * ptr, uint64_t * val) {
|
|
|
|
uint16_t tlen;
|
|
int i, j;
|
|
|
|
uint8_t *cval = (uint8_t *) val;
|
|
|
|
tlen = *ptr;
|
|
|
|
if (tlen > 4)
|
|
return -1;
|
|
|
|
#ifdef UWSGI_DEBUG
|
|
uwsgi_debug("SNMP get integer TLEN %d %p\n", tlen, ptr);
|
|
#endif
|
|
|
|
j = 0;
|
|
#ifdef __BIG_ENDIAN__
|
|
for (i = 0; i < tlen; i++) {
|
|
#else
|
|
for (i = tlen - 1; i >= 0; i--) {
|
|
#endif
|
|
cval[j] = ptr[1 + i];
|
|
j++;
|
|
}
|
|
|
|
return tlen + 1;
|
|
}
|
|
|
|
static uint8_t snmp_int_to_snmp(uint64_t snmp_val, uint8_t oid_type, uint8_t * buffer) {
|
|
uint8_t tlen;
|
|
int i, j;
|
|
uint8_t *ptr = (uint8_t *) & snmp_val;
|
|
int32_t val32 = (int32_t) snmp_val;
|
|
|
|
// check for counter, counter64 or gauge
|
|
|
|
if (oid_type == SNMP_COUNTER64) {
|
|
tlen = 8;
|
|
}
|
|
else if (oid_type == SNMP_NULL || oid_type == 0) {
|
|
tlen = 0;
|
|
}
|
|
else {
|
|
tlen = 4;
|
|
ptr = (uint8_t *) &val32;
|
|
}
|
|
|
|
buffer[0] = tlen;
|
|
|
|
j = 1;
|
|
#ifdef __BIG_ENDIAN__
|
|
for (i = 0; i < tlen; i++) {
|
|
#else
|
|
for (i = tlen - 1; i >= 0; i--) {
|
|
#endif
|
|
buffer[j] = ptr[i];
|
|
j++;
|
|
}
|
|
|
|
return tlen + 1;
|
|
}
|
|
|
|
static ssize_t build_snmp_response(uint8_t oid1, uint8_t oid2, uint8_t * buffer, int size, uint8_t * seq1, uint8_t * seq2, uint8_t * seq3) {
|
|
uint64_t snmp_val;
|
|
uint8_t oid_sz;
|
|
uint8_t oid_type;
|
|
|
|
if (oid1 == 1) {
|
|
snmp_val = get_uwsgi_snmp_value(oid2, &oid_type);
|
|
}
|
|
else if (oid1 == 2) {
|
|
snmp_val = get_uwsgi_custom_snmp_value(oid2, &oid_type);
|
|
}
|
|
else {
|
|
return -1;
|
|
}
|
|
|
|
buffer[size - 2] = oid_type;
|
|
oid_sz = snmp_int_to_snmp(snmp_val, oid_type, buffer + (size - 1));
|
|
|
|
if (oid_sz < 1)
|
|
return -1;
|
|
|
|
oid_sz--;
|
|
|
|
buffer[1] += oid_sz;
|
|
*seq1 += oid_sz;
|
|
*seq2 += oid_sz;
|
|
*seq3 += oid_sz;
|
|
|
|
return size + oid_sz;
|
|
|
|
}
|
|
|
|
static ssize_t build_snmp_metric_response(int64_t value, uint8_t type, uint8_t * buffer, int size, uint8_t * seq1, uint8_t * seq2, uint8_t * seq3) {
|
|
uint8_t oid_sz;
|
|
|
|
if (type == UWSGI_METRIC_GAUGE) {
|
|
buffer[size - 2] = SNMP_GAUGE;
|
|
oid_sz = snmp_int_to_snmp(value, SNMP_GAUGE, buffer + (size - 1));
|
|
}
|
|
else {
|
|
buffer[size - 2] = SNMP_COUNTER64;
|
|
oid_sz = snmp_int_to_snmp(value, SNMP_COUNTER64, buffer + (size - 1));
|
|
}
|
|
|
|
if (oid_sz < 1)
|
|
return -1;
|
|
|
|
oid_sz--;
|
|
|
|
buffer[1] += oid_sz;
|
|
*seq1 += oid_sz;
|
|
*seq2 += oid_sz;
|
|
*seq3 += oid_sz;
|
|
|
|
return size + oid_sz;
|
|
|
|
}
|
|
|
|
void uwsgi_opt_snmp(char *opt, char *value, void *foobar) {
|
|
uwsgi.snmp = 1;
|
|
if (value) {
|
|
uwsgi.snmp_addr = value;
|
|
uwsgi.master_process = 1;
|
|
}
|
|
|
|
}
|
|
|
|
void uwsgi_opt_snmp_community(char *opt, char *value, void *foobar) {
|
|
uwsgi.snmp = 1;
|
|
uwsgi.snmp_community = value;
|
|
}
|
|
|
|
int uwsgi_setup_snmp(void) {
|
|
int snmp_fd = -1;
|
|
int i;
|
|
if (uwsgi.snmp) {
|
|
if (uwsgi.snmp_community) {
|
|
if (strlen(uwsgi.snmp_community) > 72) {
|
|
uwsgi_log("*** warning the supplied SNMP community string will be truncated to 72 chars ***\n");
|
|
memcpy(uwsgi.shared->snmp_community, uwsgi.snmp_community, 72);
|
|
}
|
|
else {
|
|
memcpy(uwsgi.shared->snmp_community, uwsgi.snmp_community, strlen(uwsgi.snmp_community) + 1);
|
|
}
|
|
}
|
|
|
|
uwsgi.shared->snmp_gvalue[0].type = SNMP_COUNTER64;
|
|
uwsgi.shared->snmp_gvalue[0].val = &uwsgi.workers[0].requests;
|
|
|
|
for (i = 0; i < uwsgi.numproc; i++) {
|
|
uwsgi.shared->snmp_gvalue[30 + i].type = SNMP_COUNTER64;
|
|
uwsgi.shared->snmp_gvalue[30 + i].val = &uwsgi.workers[i + 1].requests;
|
|
}
|
|
|
|
if (uwsgi.snmp_addr) {
|
|
snmp_fd = bind_to_udp(uwsgi.snmp_addr, 0, 0);
|
|
if (snmp_fd < 0) {
|
|
uwsgi_log("unable to bind to udp socket. SNMP service will be disabled.\n");
|
|
}
|
|
else {
|
|
uwsgi_log("SNMP server enabled on %s\n", uwsgi.snmp_addr);
|
|
event_queue_add_fd_read(uwsgi.master_queue, snmp_fd);
|
|
}
|
|
}
|
|
else {
|
|
uwsgi_log("SNMP agent enabled.\n");
|
|
}
|
|
|
|
}
|
|
|
|
return snmp_fd;
|
|
}
|
|
|
|
void uwsgi_master_manage_snmp(int snmp_fd) {
|
|
struct sockaddr_in udp_client;
|
|
socklen_t udp_len = sizeof(udp_client);
|
|
ssize_t rlen = recvfrom(snmp_fd, uwsgi.wsgi_req->buffer, uwsgi.buffer_size, 0, (struct sockaddr *) &udp_client, &udp_len);
|
|
|
|
if (rlen < 0) {
|
|
uwsgi_error("recvfrom()");
|
|
}
|
|
else if (rlen > 0) {
|
|
manage_snmp(snmp_fd, (uint8_t *) uwsgi.wsgi_req->buffer, rlen, &udp_client);
|
|
}
|
|
}
|
|
|