/* 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; struct VSC_point *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); int i; for (i = 0; i < VBE_MAX; i++) VSC_Destroy_Point(&dfn->vpt[i]); 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] = VSC_Clone_Point(vpt); 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 = *(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; 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; }