diff options
Diffstat (limited to 'src/betab.c')
-rw-r--r-- | src/betab.c | 482 |
1 files changed, 294 insertions, 188 deletions
diff --git a/src/betab.c b/src/betab.c index 49e02f7..388083b 100644 --- a/src/betab.c +++ b/src/betab.c @@ -1,3 +1,3 @@ /* This file is part of varnish-mib - Copyright (C) 2014 Sergey Poznyakoff + Copyright (C) 2014, 2018 Sergey Poznyakoff @@ -17,59 +17,22 @@ #include "varnish_mib.h" +#include <varnish/vas.h> +#include <varnish/vqueue.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netdb.h> #include <arpa/inet.h> +#include <netinet/in.h> -unsigned backendTable_timeout = 5; - -void -varnish_backend_table_timeout_parser(const char *token, char *line) -{ - varnish_mib_timeout_parser(token, line, &backendTable_timeout); -} - -/* - * create a new row in the table - */ -static struct backendTable_entry * -create_entry(netsnmp_tdata *table_data, long idx, - struct backendTable_entry *ent) -{ - struct backendTable_entry *entry; - netsnmp_tdata_row *row; - - entry = SNMP_MALLOC_TYPEDEF(struct backendTable_entry); - if (!entry) - return NULL; - - row = netsnmp_tdata_create_row(); - if (!row) { - SNMP_FREE(entry); - return NULL; - } - row->data = entry; - *entry = *ent; - - entry->vbeIndex = idx; - netsnmp_tdata_row_add_index(row, ASN_INTEGER, - &entry->vbeIndex, - sizeof(entry->vbeIndex)); - if (table_data) - netsnmp_tdata_add_row(table_data, row); - return entry; -} +#include "backend.h" -#define VSC_POINT_TYPE(p) ((p)->section->fantom->type) -#define VSC_POINT_IDENT(p) ((p)->section->fantom->ident) -#define VSC_POINT_NAME(p) ((p)->desc->name) -#if VARNISHAPI_MINOR == 0 -# define VSC_POINT_FMT(p) ((p)->desc->fmt) -#elif VARNISHAPI_MINOR == 1 -# define VSC_POINT_FMT(p) ((p)->desc->ctype) -#else -# error "unsupported Varnish API minor number" -#endif - -struct betab_priv { - int err; - long idx; - struct backendTable_entry ent; - netsnmp_tdata *table; +enum { + vbeHappyProbes, + vbeRequestHeaderBytes, + vbeRequestBodyBytes, + vbeResponseHeaderBytes, + vbeResponseBodyBytes, + vbePipeHeaderBytes, + vbePipeIn, + vbePipeOut, + VBE_MAX }; @@ -77,3 +40,3 @@ struct betab_priv { struct betab_trans { - const char *name; + char const *param; size_t off; @@ -81,5 +44,8 @@ struct betab_trans { -static struct betab_trans betab_trans[] = { +/* { "vcls", offsetof(struct backendTable_entry, vbeVcls) }, +*/ + +static struct betab_trans betab_trans[] = { { "happy", @@ -103,166 +69,306 @@ static struct betab_trans betab_trans[] = { -static int -identcmp(struct betab_priv *bp, const char *ident) -{ - size_t len; - size_t i; +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; - if (bp->idx == -1) - return 1; - for (i = 0; i < bp->ent.vbeIdent_len; i++, ident++) - if (bp->ent.vbeIdent[i] != *ident) - return 1; - if (*ident == '(' || *ident == 0) - return 0; - return 1; -} + const struct VSC_point *vpt[VBE_MAX]; + + VTAILQ_ENTRY(backend_dfn) list; +}; -static void -uint32_to_bytes (unsigned char *bytes, uint32_t u) -{ - int i; +static VTAILQ_HEAD(, backend_dfn) backends + = VTAILQ_HEAD_INITIALIZER(backends); - for (i = 0; i < 4; i++) - { - bytes[i] = u & 0xff; - u >>= 8; - } +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); + } } -static void -scanbuf(const char *s, struct backendTable_entry *ent) +void +backend_register(char const *name, size_t len, char const *param, + const struct VSC_point *vpt) { - char ipv4buf[16]; - char ipv6buf[81]; - unsigned long port; - char *p; - union { - struct in_addr in; - struct in6_addr in6; - } v; + int i = 0; + struct backend_dfn *dfn; - if (*s != '(') - return; - ++s; - p = ipv4buf; - while (p < ipv4buf + sizeof(ipv4buf) && *s && *s != ',') - *p++ = *s++; - if (*s != ',') - return; - *p = 0; + 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; + break; + } + } +} - ++s; - p = ipv6buf; - while (p < ipv6buf + sizeof(ipv6buf) && *s && *s != ',') - *p++ = *s++; - *p = 0; +int +backendTable_load(netsnmp_cache *cache, void *vmagic) +{ + netsnmp_tdata *table_data = (netsnmp_tdata *) vmagic; + struct backend_dfn *dfn; - ++s; - port = strtoul(s, &p, 10); - if (*p != ')' || port > USHRT_MAX) - return; + DEBUGMSGTL(("varnish_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 = *(const volatile uint64_t*)dfn->vpt[i]->ptr; + 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; - if (ipv4buf[0] && inet_pton(AF_INET, ipv4buf, &v)) { - ent->vbeIPv4_len = 4; - uint32_to_bytes(ent->vbeIPv4, v.in.s_addr); + DEBUGMSGTL(("varnish_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); } +} + +unsigned backendTable_timeout = 5; + +void +varnish_backend_table_timeout_parser(const char *token, char *line) +{ + varnish_mib_timeout_parser(token, line, &backendTable_timeout); +} + +static struct backend_dfn * +dfn_lookup(be_string_t *vclname, be_string_t *label) +{ + size_t namelen; + struct backend_dfn *dfn; - if (ipv6buf[0] && inet_pton(AF_INET6, ipv6buf, &v)) { - ent->vbeIPv6_len = 16; - memcpy(ent->vbeIPv6, &v, ent->vbeIPv6_len); + 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; } - ent->vbePort = port; -} - -/* Process a single statistics point. See comment below. */ -static int -create_entry_cb(void *priv, const struct VSC_point *const pt) + return NULL; +} + +static u_long +to_port(be_string_t *str) { - struct betab_priv *bp = priv; - struct betab_trans *tp; - - if (bp->err || !pt || strcmp(VSC_POINT_TYPE(pt), "VBE") || - strcmp(VSC_POINT_FMT(pt), "uint64_t")) - return 0; - if (identcmp(bp, VSC_POINT_IDENT(pt))) { - const char *full_id; - - if (bp->idx != -1 - && !create_entry(bp->table, bp->idx, &bp->ent)) { - snmp_log(LOG_ERR, "out of memory\n"); - bp->err = SNMP_ERR_GENERR; + 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; +} - memset(&bp->ent, 0, sizeof(bp->ent)); - bp->ent.vbeIndex = ++bp->idx; +static void +uint32_to_bytes (unsigned char *bytes, uint32_t u) +{ + int i; - full_id = VSC_POINT_IDENT(pt); - bp->ent.vbeIdent_len = strcspn(full_id, "("); - bp->ent.vbeIdent = malloc(bp->ent.vbeIdent_len); - if (!bp->ent.vbeIdent) { - snmp_log(LOG_ERR, "out of memory\n"); - bp->err = SNMP_ERR_GENERR; - return 0; - } - memcpy(bp->ent.vbeIdent, full_id, bp->ent.vbeIdent_len); - full_id += bp->ent.vbeIdent_len; - scanbuf(full_id, &bp->ent); + for (i = 0; i < 4; i++) { + bytes[i] = u & 0xff; + u >>= 8; } +} - for (tp = betab_trans; tp->name; tp++) { - if (strcmp(VSC_POINT_NAME(pt), tp->name) == 0) { - U64 *u = (U64*)((char*)&bp->ent + tp->off); - uint64_t n = *(const volatile uint64_t*)pt->ptr; - u->high = n >> 32; - u->low = n & 0xffffffff; - break; +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); } - return 0; } -/* Varnish API does not provide access to struct VSC_C_vbe, so the only - way to backend statistics is to iterate over all statistics data, selecting - the entries marked as VBE. That's what this function does. - */ -int -backendTable_load(netsnmp_cache *cache, void *vmagic) +static int +backend_parse(struct vcli_conn *conn, be_string_t *name) { - struct betab_priv bp; - struct VSM_data *vd = varnish_get_vsm_data(); + if (vcli_asprintf(conn, "vcl.show %*.*s\n", + (int)name->len, (int)name->len, + name->start) + || vcli_write(conn)) + return SNMP_ERR_GENERR; - if (!vd) - return SNMP_ERR_NOSUCHNAME; + if (vcli_read_response(conn)) + return SNMP_ERR_GENERR; - bp.idx = -1; - bp.err = 0; - bp.table = (netsnmp_tdata *) vmagic; - memset(&bp.ent, 0, sizeof(bp.ent)); - - DEBUGMSGTL(("varnish_ban", "loading backend table\n")); - VSC_Iter(vd, NULL, create_entry_cb, &bp); - /* FIXME: perhaps handle bp.err separately */ - if (bp.idx != -1) { - DEBUGMSGTL(("varnish_ban", "loaded %lu backend entries\n", - bp.idx + 1)); - if (!create_entry(bp.table, bp.idx, &bp.ent)) - snmp_log(LOG_ERR, "out of memory\n"); + 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); -void -backendTable_free(netsnmp_cache *cache, void *vmagic) +static void +backend_name_add(struct backend_name_list *namelist, + char const *str, size_t len) { - netsnmp_tdata *table = (netsnmp_tdata *) vmagic; - netsnmp_tdata_row *row; + 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); +} - DEBUGMSGTL(("varnish_ban", "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); +int +backend_collect_addr(void) +{ + struct backend_dfn *dfn; + struct vcli_conn conn; + struct vsm *vsm; + int rc; + struct backend_name_list namelist + = VTAILQ_HEAD_INITIALIZER(namelist); + + vsm = varnish_get_vsm_data(); + if (!vsm) + return SNMP_ERR_GENERR; + DEBUGMSGTL(("varnish_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; } |