/* This file is part of varnish-mib
Copyright (C) 2014, 2018 Sergey Poznyakoff
Varnish-mib is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
Varnish-mib is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with varnish-mib. If not, see .
*/
#include "varnish_mib.h"
#include
#include
#include
#include
#include
#include
#include
#include "backend.h"
enum {
vbeHappyProbes,
vbeRequestHeaderBytes,
vbeRequestBodyBytes,
vbeResponseHeaderBytes,
vbeResponseBodyBytes,
vbePipeHeaderBytes,
vbePipeIn,
vbePipeOut,
VBE_MAX
};
struct betab_trans {
char const *param;
size_t off;
};
/*
{ "vcls",
offsetof(struct backendTable_entry, vbeVcls) },
*/
static struct betab_trans betab_trans[] = {
{ "happy",
offsetof(struct backendTable_entry, vbeHappyProbes) },
{ "bereq_hdrbytes",
offsetof(struct backendTable_entry, vbeRequestHeaderBytes) },
{ "bereq_bodybytes",
offsetof(struct backendTable_entry, vbeRequestBodyBytes) },
{ "beresp_hdrbytes",
offsetof(struct backendTable_entry, vbeResponseHeaderBytes) },
{ "beresp_bodybytes",
offsetof(struct backendTable_entry, vbeResponseBodyBytes) },
{ "pipe_hdrbytes",
offsetof(struct backendTable_entry, vbePipeHeaderBytes) },
{ "pipe_in",
offsetof(struct backendTable_entry, vbePipeIn) },
{ "pipe_out",
offsetof(struct backendTable_entry, vbePipeOut) },
{ NULL }
};
struct backend_dfn {
long vbeIndex;
char *vbeIdent;
size_t vbeIdent_len;
char vbeIPv4[4];
size_t vbeIPv4_len;
char vbeIPv6[16];
size_t vbeIPv6_len;
u_long vbePort;
const volatile uint64_t *vpt[VBE_MAX];
VTAILQ_ENTRY(backend_dfn) list;
};
static VTAILQ_HEAD(, backend_dfn) backends
= VTAILQ_HEAD_INITIALIZER(backends);
void
backend_clear(void)
{
while (!VTAILQ_EMPTY(&backends)) {
struct backend_dfn *dfn = VTAILQ_FIRST(&backends);
VTAILQ_REMOVE(&backends, dfn, list);
free(dfn->vbeIdent);
SNMP_FREE(dfn);
}
}
void
backend_register(char const *name, size_t len, char const *param,
const struct VSC_point *vpt)
{
int i = 0;
struct backend_dfn *dfn;
VTAILQ_FOREACH(dfn, &backends, list) {
if (dfn->vbeIdent_len == len
&& memcmp(dfn->vbeIdent, name, len) == 0)
break;
i++;
}
if (!dfn) {
dfn = SNMP_MALLOC_TYPEDEF(struct backend_dfn);
AN(dfn);
dfn->vbeIndex = i;
dfn->vbeIdent = malloc(len + 1);
AN(dfn->vbeIdent);
memcpy(dfn->vbeIdent, name, len);
dfn->vbeIdent[len] = 0;
dfn->vbeIdent_len = len;
VTAILQ_INSERT_TAIL(&backends, dfn, list);
}
for (i = 0; betab_trans[i].param; i++) {
if (strcmp(betab_trans[i].param, param) == 0) {
dfn->vpt[i] = vpt->ptr;
break;
}
}
}
int
backendTable_load(netsnmp_cache *cache, void *vmagic)
{
netsnmp_tdata *table_data = (netsnmp_tdata *) vmagic;
struct backend_dfn *dfn;
DEBUGMSGTL(("varnish_mib:backend", "loading backend table\n"));
VTAILQ_FOREACH(dfn, &backends, list) {
int i;
netsnmp_tdata_row *row;
struct backendTable_entry *ent;
ent = SNMP_MALLOC_TYPEDEF(struct backendTable_entry);
ent->vbeIndex = dfn->vbeIndex;
ent->vbeIdent = malloc(dfn->vbeIdent_len);
memcpy(ent->vbeIdent, dfn->vbeIdent, dfn->vbeIdent_len);
ent->vbeIdent_len = dfn->vbeIdent_len;
memset(ent->vbeIPv4, 0, sizeof ent->vbeIPv4);
memcpy(ent->vbeIPv4, dfn->vbeIPv4, dfn->vbeIPv4_len);
ent->vbeIPv4_len = dfn->vbeIPv4_len;
memcpy(ent->vbeIPv6, dfn->vbeIPv6, ent->vbeIPv6_len);
ent->vbeIPv6_len = dfn->vbeIPv6_len;
ent->vbePort = dfn->vbePort;
for (i = 0; i < VBE_MAX; i++) {
U64 *u = (U64*)((char*)ent + betab_trans[i].off);
uint64_t n = *dfn->vpt[i];
u->high = n >> 32;
u->low = n & 0xffffffff;
}
row = netsnmp_tdata_create_row();
if (!row)
break;
row->data = ent;
netsnmp_tdata_row_add_index(row, ASN_INTEGER,
&ent->vbeIndex,
sizeof(ent->vbeIndex));
netsnmp_tdata_add_row(table_data, row);
}
return 0;
}
void
backendTable_free(netsnmp_cache *cache, void *vmagic)
{
netsnmp_tdata *table = (netsnmp_tdata *) vmagic;
netsnmp_tdata_row *row;
DEBUGMSGTL(("varnish_mib:backend", "freeing backend table\n"));
while ((row = netsnmp_tdata_row_first(table))) {
struct backendTable_entry *entry = row->data;
free(entry->vbeIdent);
SNMP_FREE(entry);
netsnmp_tdata_remove_and_delete_row(table, row);
}
}
static struct backend_dfn *
dfn_lookup(be_string_t *vclname, be_string_t *label)
{
size_t namelen;
struct backend_dfn *dfn;
namelen = vclname->len + label->len + 1;
VTAILQ_FOREACH(dfn, &backends, list) {
if (dfn->vbeIdent_len == namelen
&& memcmp(dfn->vbeIdent, vclname->start, vclname->len) == 0
&& dfn->vbeIdent[vclname->len] == '.'
&& memcmp(dfn->vbeIdent + vclname->len + 1,
label->start, label->len) == 0)
return dfn;
}
return NULL;
}
static u_long
to_port(be_string_t *str)
{
int i;
u_long v = 0;
for (i = 0; i < str->len; i++) {
static char dig[] = "0123456789";
char *p = strchr(dig, str->start[i]);
if (!p)
return 0;
v = v * 10 + p - dig;
}
return v;
}
static void
uint32_to_bytes (unsigned char *bytes, uint32_t u)
{
int i;
for (i = 0; i < 4; i++) {
bytes[i] = u & 0xff;
u >>= 8;
}
}
static void
update_dfn(be_string_t *label, be_string_t *host, be_string_t *port,
void *data)
{
struct backend_dfn *dfn;
dfn = dfn_lookup(data, label);
if (!dfn)
return;
dfn->vbePort = to_port(port);
if (host->len) {
struct addrinfo *res, *ap;
int rc;
char *node;
node = malloc(host->len+1);
AN(node);
memcpy(node, host->start, host->len);
node[host->len] = 0;
rc = getaddrinfo(node, NULL, NULL, &res);
free(node);
if (rc)
return;
for (ap = res; ap; ap = ap->ai_next) {
if (ap->ai_addr->sa_family == AF_INET) {
if (dfn->vbeIPv4_len == 0) {
struct sockaddr_in *s =
(struct sockaddr_in *)ap->ai_addr;
uint32_to_bytes((unsigned char*)dfn->vbeIPv4,
s->sin_addr.s_addr);
dfn->vbeIPv4_len = 4;
}
} else if (ap->ai_addr->sa_family == AF_INET6) {
if (dfn->vbeIPv4_len == 0) {
struct sockaddr_in6 *s =
(struct sockaddr_in6 *)ap->ai_addr;
dfn->vbeIPv6_len = 16;
memcpy(dfn->vbeIPv6, &s->sin6_addr,
dfn->vbeIPv6_len);
}
}
}
freeaddrinfo(res);
}
}
static int
backend_parse(struct vcli_conn *conn, be_string_t *name)
{
if (vcli_asprintf(conn, "vcl.show %*.*s\n",
(int)name->len, (int)name->len,
name->start)
|| vcli_write(conn))
return SNMP_ERR_GENERR;
if (vcli_read_response(conn))
return SNMP_ERR_GENERR;
if (conn->resp != CLIS_OK) {
snmp_log(LOG_ERR, "vcl.show command rejected: %u %s\n",
conn->resp, conn->base);
return SNMP_ERR_GENERR;
}
backend_parser(conn->base, conn->bufsize, update_dfn, name);
return 0;
}
struct backend_name {
be_string_t str;
VTAILQ_ENTRY(backend_name) list;
};
VTAILQ_HEAD(backend_name_list, backend_name);
static void
backend_name_add(struct backend_name_list *namelist,
char const *str, size_t len)
{
struct backend_name *name;
VTAILQ_FOREACH(name, namelist, list) {
if (name->str.len == len
&& memcmp(name->str.start, str, len) == 0)
return;
}
name = SNMP_MALLOC_TYPEDEF(struct backend_name);
AN(name);
name->str.start = str;
name->str.len = len;
VTAILQ_INSERT_TAIL(namelist, name, list);
}
int
backend_collect_addr(struct vsm *vsm)
{
struct backend_dfn *dfn;
struct vcli_conn conn;
int rc;
struct backend_name_list namelist
= VTAILQ_HEAD_INITIALIZER(namelist);
DEBUGMSGTL(("varnish_mib:backend", "getting backend info\n"));
rc = vcli_connect(vsm, &conn);
if (rc != SNMP_ERR_NOERROR)
return rc;
/* Select backend names */
VTAILQ_FOREACH(dfn, &backends, list) {
int i;
for (i = 0; i < dfn->vbeIdent_len; i++)
if (dfn->vbeIdent[i] == '.')
break;
if (i == dfn->vbeIdent_len)
continue;
backend_name_add(&namelist, dfn->vbeIdent, i);
}
while (!VTAILQ_EMPTY(&namelist)) {
struct backend_name *name = VTAILQ_FIRST(&namelist);
VTAILQ_REMOVE(&namelist, name, list);
backend_parse(&conn, &name->str);
}
vcli_disconnect(&conn);
return 0;
}