diff options
-rw-r--r-- | src/.gitignore | 1 | ||||
-rw-r--r-- | src/Makefile.am | 5 | ||||
-rw-r--r-- | src/VARNISH-MIB.txt | 83 | ||||
-rw-r--r-- | src/ban.c | 191 | ||||
-rw-r--r-- | src/varnish_mib.h | 52 | ||||
-rw-r--r-- | src/varnish_mib.mib2c | 258 |
6 files changed, 503 insertions, 87 deletions
diff --git a/src/.gitignore b/src/.gitignore index 49f397c..61958b7 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -1 +1,2 @@ varnish_mib.c +varnish_mib.h diff --git a/src/Makefile.am b/src/Makefile.am index a0e7e72..4643fde 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -26,11 +26,12 @@ varnish_mib_la_SOURCES = \ varnish_mib.h\ vcli.c BUILT_SOURCES = \ - varnish_mib.c + varnish_mib.c\ + varnish_mib.h -varnish_mib.c: varnish_mib.mib2c VARNISH-MIB.txt +varnish_mib.c varnish_mib.h: varnish_mib.mib2c VARNISH-MIB.txt .mib2c.c: MIBDIRS=${top_srcdir}/src:${NET_SNMP_MIBDIRS} MIBS="+VARNISH-MIB" \ mib2c -c $< -f $@ varnish diff --git a/src/VARNISH-MIB.txt b/src/VARNISH-MIB.txt index cde935a..47e39c8 100644 --- a/src/VARNISH-MIB.txt +++ b/src/VARNISH-MIB.txt @@ -10,18 +10,20 @@ VARNISH-MIB DEFINITIONS ::= BEGIN IMPORTS MODULE-IDENTITY, OBJECT-TYPE, enterprises, Counter64, TimeTicks FROM SNMPv2-SMI + DateAndTime + FROM SNMPv2-TC OBJECT-GROUP, MODULE-COMPLIANCE FROM SNMPv2-CONF; varnishMIB MODULE-IDENTITY - LAST-UPDATED "201411241848Z" + LAST-UPDATED "201411260759Z" ORGANIZATION "Shared Autonomous sYstems" CONTACT-INFO "mhe@say.no" DESCRIPTION "This MIB module defines objects for Varnish reverse web proxy." - REVISION "201411241848Z" + REVISION "201411260759Z" DESCRIPTION "Initial version." ::= { varnish 0 } @@ -73,10 +75,16 @@ clientCacheMisses OBJECT-TYPE DESCRIPTION "Cache misses" ::= { client 5 } +VarnishBanString ::= TEXTUAL-CONVENTION + DISPLAY-HINT "1024t" + STATUS current + DESCRIPTION "" + SYNTAX OCTET STRING (SIZE (0..1024)) + clientBan OBJECT-TYPE - SYNTAX OCTET STRING (SIZE(0..1024)) + SYNTAX VarnishBanString MAX-ACCESS read-write STATUS current DESCRIPTION "When set, invalidates the cache using the supplied value as @@ -526,10 +534,65 @@ bansPersistedFragmentation OBJECT-TYPE STATUS current DESCRIPTION "Extra bytes in persisted ban lists due to fragmentation" ::= { bans 17 } - +banTable OBJECT-TYPE + SYNTAX SEQUENCE OF VarnishBanEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The (conceptual) table listing available bans" + ::= { bans 18 } + +varnishBanEntry OBJECT-TYPE + SYNTAX VarnishBanEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "An entry (conceptual row) representing a ban" + INDEX { banIndex } + ::= { banTable 1 } + +VarnishBanEntry ::= SEQUENCE { + banIndex Integer32, + banTime DateAndTime, + banRefCount Counter64, + banExpression VarnishBanString +} + +banIndex OBJECT-TYPE + SYNTAX Integer32 + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A number uniquely identifying each ban" + ::= { varnishBanEntry 1 } + +banTime OBJECT-TYPE + SYNTAX DateAndTime + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Time when the ban was created" + ::= { varnishBanEntry 2 } + +banRefCount OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of references to this ban" + ::= { varnishBanEntry 3 } + +banExpression OBJECT-TYPE + SYNTAX VarnishBanString + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "VCL expression of the ban" + ::= { varnishBanEntry 4 } + -- -- Groups and Compliance -- varnishConfs OBJECT IDENTIFIER ::= { varnish 100 } @@ -584,10 +647,15 @@ varnishGroup OBJECT-GROUP threadsLimitHits, threadsCreated, threadsDestroyed, threadsFailed, - threadsQueueLength - + threadsQueueLength, + banTable, + varnishBanEntry, + banIndex, + banTime, + banRefCount, + banExpression } STATUS current DESCRIPTION "Group of Varnish objects" @@ -601,10 +669,9 @@ varnishStandardComplianceV1 MODULE-COMPLIANCE DESCRIPTION "Compliance for an Varnish agent" MODULE - MANDATORY-GROUPS { - varnishGroup} + MANDATORY-GROUPS { varnishGroup } ::= { varnishCompl 1 } END @@ -15,8 +15,9 @@ along with varnish-mib. If not, see <http://www.gnu.org/licenses/>. */ #include "varnish_mib.h" +#include <ctype.h> static int send_ban_cmd(vcli_conn_t *conn, const char *expr) { @@ -49,14 +50,200 @@ varnish_ban(netsnmp_agent_request_info *reqinfo, return SNMP_ERR_GENERR; } memcpy(expr, requests->requestvb->val.string, len); expr[len] = 0; - DEBUGMSGTL(("vcli_mib", "ban %s\n", expr)); + DEBUGMSGTL(("varnish_ban", "setting ban %s\n", expr)); rc = vcli_connect(vd, &conn); if (rc == SNMP_ERR_NOERROR) { rc = send_ban_cmd(&conn, expr); - vcli_disconnect(&conn); + vcli_disconnect(&conn); } free(expr); return rc ? SNMP_ERR_GENERR : SNMP_ERR_NOERROR; } + +unsigned banTable_timeout = 60; +/* + * create a new row in the table + */ +static struct banTable_entry * +create_entry(netsnmp_tdata *table_data, long idx, struct banTable_entry *ent) +{ + struct banTable_entry *entry; + netsnmp_tdata_row *row; + + entry = SNMP_MALLOC_TYPEDEF(struct banTable_entry); + if (!entry) + return NULL; + + row = netsnmp_tdata_create_row(); + if (!row) { + SNMP_FREE(entry); + return NULL; + } + row->data = entry; + *entry = *ent; + + entry->banIndex = idx; + netsnmp_tdata_row_add_index(row, ASN_INTEGER, + &entry->banIndex, + sizeof(entry->banIndex)); + if (table_data) + netsnmp_tdata_add_row(table_data, row); + return entry; +} + +#define TMSEC(t) (((t)->tm_hour * 60 + (t)->tm_min) * 60 + (t)->tm_sec) + +static int +utc_offset (void) +{ + time_t t = time (NULL); + struct tm ltm = *localtime (&t); + struct tm gtm = *gmtime (&t); + int d = TMSEC (<m) - TMSEC (>m); + if (!(ltm.tm_year = gtm.tm_year && ltm.tm_mon == gtm.tm_mon)) + d += 86400; + return d / 60; +} + +/* Refill the ban table */ +int +banTable_load(netsnmp_cache *cache, void *vmagic) +{ + netsnmp_tdata *table = (netsnmp_tdata *) vmagic; + long idx = 0; + int rc; + struct vcli_conn conn; + char *p; + struct VSM_data *vd; + + DEBUGMSGTL(("varnish_ban", "reloading ban table")); + vd = varnish_get_vsm_data(); + rc = vcli_connect(vd, &conn); + if (rc != SNMP_ERR_NOERROR) + return rc; + + if (vcli_asprintf(&conn, "ban.list\n") || 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, "ban.list command rejected: %u %s\n", + conn.resp, conn.base); + return SNMP_ERR_GENERR; + } + + p = conn.base; + while (p < conn.base + conn.bufsize) { + char *q; + struct banTable_entry e; + struct tm *tm; + time_t t; + int n; + + if (*p == '\n') { + ++p; + continue; + } + e.banIndex = idx; + t = strtoul(p, &q, 10); + if (*q != '.') { + p = strchr(p, '\n'); + if (!p) + break; + continue; + } + ++q; + + e.banTime_len = 11; + e.banTime = malloc(e.banTime_len + 1); + if (!e.banTime) { + vcli_disconnect(&conn); + snmp_log(LOG_ERR, "out of memory\n"); + return SNMP_ERR_GENERR; + } + tm = localtime(&t); + /* A date-time specification. + + field octets contents range + ----- ------ -------- ----- + 1 1-2 year* 0..65536 + 2 3 month 1..12 + 3 4 day 1..31 + 4 5 hour 0..23 + 5 6 minutes 0..59 + 6 7 seconds 0..60 + (use 60 for leap-second) + 7 8 deci-seconds 0..9 + 8 9 direction from UTC '+' / '-' + 9 10 hours from UTC* 0..13 + 10 11 minutes from UTC 0..59 + + * Notes: + - the value of year is in network-byte order + */ + n = tm->tm_year % 100; + e.banTime[0] = n >> 8; + e.banTime[1] = n & 0xff; + e.banTime[2] = tm->tm_mon + 1; + e.banTime[3] = tm->tm_mday; + e.banTime[4] = tm->tm_hour; + e.banTime[5] = tm->tm_min; + e.banTime[6] = tm->tm_sec; + e.banTime[7] = *q - '0'; + n = utc_offset(); + if (n < 0) { + e.banTime[8] = '-'; + n = - n; + } else + e.banTime[8] = '+'; + e.banTime[9] = n / 60; + e.banTime[10] = n % 60; + + while (*q && isdigit(*q)) + ++q; + while (*q && isspace(*q)) + ++q; + e.banRefCount = strtoul(q, &q, 10); + + while (*q && isspace(*q)) + ++q; + + e.banExpression_len = strcspn(q, "\n"); + e.banExpression = malloc(e.banExpression_len); + if (!e.banExpression) { + vcli_disconnect(&conn); + free(e.banTime); + snmp_log(LOG_ERR, "out of memory\n"); + return SNMP_ERR_GENERR; + } + memcpy(e.banExpression, q, e.banExpression_len); + + create_entry(table, idx, &e); + ++idx; + q += e.banExpression_len; + p = q; + } + vcli_disconnect(&conn); + DEBUGMSGTL(("varnish_ban", "loaded %ld ban entries", idx)); + return 0; +} + +void +banTable_free(netsnmp_cache *cache, void *vmagic) +{ + netsnmp_tdata *table = (netsnmp_tdata *) vmagic; + netsnmp_tdata_row *row; + + DEBUGMSGTL(("varnish_ban", "freeing ban table")); + while ((row = netsnmp_tdata_row_first(table))) { + struct banTable_entry *entry = row->data; + free(entry->banExpression); + free(entry->banTime); + SNMP_FREE(entry); + netsnmp_tdata_remove_and_delete_row(table, row); + } +} diff --git a/src/varnish_mib.h b/src/varnish_mib.h deleted file mode 100644 index 31e3de1..0000000 --- a/src/varnish_mib.h +++ /dev/null @@ -1,52 +0,0 @@ -/* This file is part of varnish-mib -*- c -*- - Copyright (C) 2014 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 <http://www.gnu.org/licenses/>. -*/ -#include <config.h> -#include <stdlib.h> -#include <stdint.h> - -#include <vapi/vsc.h> -#include <vapi/vsm.h> -#include <vcli.h> - -#include <net-snmp/net-snmp-config.h> -#include <net-snmp/net-snmp-includes.h> -#include <net-snmp/agent/net-snmp-agent-includes.h> - -typedef struct vcli_conn { - int fd; - char *secret; - int resp; - char *base; - size_t bufmax; - size_t bufsize; -} vcli_conn_t; - -int vcli_write(vcli_conn_t *conn); -int vcli_read_response(vcli_conn_t *conn); -int vcli_vasprintf(vcli_conn_t *conn, const char *fmt, va_list ap); -int vcli_asprintf(vcli_conn_t *conn, const char *fmt, ...); -void vcli_disconnect(vcli_conn_t *conn); -int vcli_connect(struct VSM_data *vd, vcli_conn_t *conn); - - -int varnish_auth_response(const char *file, const char *challenge, - char response[CLI_AUTH_RESPONSE_LEN + 1]); - -int varnish_ban(netsnmp_agent_request_info *reqinfo, - netsnmp_request_info *requests, - struct VSM_data *vd); - diff --git a/src/varnish_mib.mib2c b/src/varnish_mib.mib2c index 9825728..34f0f16 100644 --- a/src/varnish_mib.mib2c +++ b/src/varnish_mib.mib2c @@ -22,30 +22,8 @@ End: vi: set ro: */ @enddefine@ -@open ${name}@ -/* THIS FILE IS GENERATED AUTOMATICALLY. PLEASE DO NOT EDIT. */ - -#include "varnish_mib.h" - -static struct VSM_data *vd; - -void -varnish_snmp_init(void) -{ - vd = VSM_New(); - - if (VSM_Open(vd)) - exit(1); -} - -void -varnish_snmp_deinit(void) -{ - VSM_Close(vd); -} - @startperl@ $vars{'varnish_translate'} = sub { my $name = shift; my %trans = ( @@ -140,12 +118,85 @@ $vars{'varnish_translate'} = sub { $vars{'modulename'} = $vars{'name'}; $vars{'modulename'} =~ s#.*/##; $vars{'modulename'} =~ s/\.c$//; -print "$vars{'modulename'}\n"; +#print "$vars{'modulename'}\n"; 0; @endperl@ +@open ${modulename}.h@ +/* THIS FILE IS GENERATED AUTOMATICALLY. PLEASE DO NOT EDIT. */ +#include <config.h> +#include <stdlib.h> +#include <stdint.h> + +#include <vapi/vsc.h> +#include <vapi/vsm.h> +#include <vcli.h> + +#include <net-snmp/net-snmp-config.h> +#include <net-snmp/net-snmp-includes.h> +#include <net-snmp/agent/net-snmp-agent-includes.h> + +typedef struct vcli_conn { + int fd; + char *secret; + int resp; + char *base; + size_t bufmax; + size_t bufsize; +} vcli_conn_t; + +int vcli_write(vcli_conn_t *conn); +int vcli_read_response(vcli_conn_t *conn); +int vcli_vasprintf(vcli_conn_t *conn, const char *fmt, va_list ap); +int vcli_asprintf(vcli_conn_t *conn, const char *fmt, ...); +void vcli_disconnect(vcli_conn_t *conn); +int vcli_connect(struct VSM_data *vd, vcli_conn_t *conn); + +struct VSM_data *varnish_get_vsm_data(void); + +int varnish_auth_response(const char *file, const char *challenge, + char response[CLI_AUTH_RESPONSE_LEN + 1]); + +int varnish_ban(netsnmp_agent_request_info *reqinfo, + netsnmp_request_info *requests, + struct VSM_data *vd); + +extern unsigned banTable_timeout; + +@open ${name}@ +/* THIS FILE IS GENERATED AUTOMATICALLY. PLEASE DO NOT EDIT. */ + +#include "varnish_mib.h" + +static struct VSM_data *vd; + +void +varnish_snmp_init(void) +{ + vd = VSM_New(); + + if (VSM_Open(vd)) + exit(1); +} + +void +varnish_snmp_deinit(void) +{ + VSM_Close(vd); +} + +struct VSM_data * +varnish_get_vsm_data() +{ + if (VSM_Abandoned(vd)) { + DEBUGMSGTL(("$modulename", "reopening vd\n")); + VSM_Close(vd); + VSM_Open(vd); + } + return vd; +} /* Variable handlers. An instance handler only hands us one request at a time, unwrapping @@ -272,8 +323,166 @@ handle_$i(netsnmp_mib_handler *handler, return SNMP_ERR_NOERROR; } @end@ +@foreach $i table@ + ## Determine the first/last column names + @eval $first_column = "-"@ + @eval $last_column = "-"@ + @foreach $c column@ + @if $c.readable@ + @if "$first_column" eq "-"@ + @eval $first_column = $c@ + @end@ + @eval $last_column = $c@ + @end@ + @end@ + +@push@ +@append ${modulename}.h@ +/* column number definitions for table $i */ + @foreach $c column@ +#define COLUMN_$c.uc $c.subid + @end@ + +struct ${i}_entry { + /* Index values */ + @foreach $idx index@ + @if $idx.needlength@ + $idx.decl *$idx; + size_t ${idx}_len; + @else@ + $idx.decl $idx; + @end@ + @end@ + + /* Column values */ + @foreach $c nonindex@ + @if $c.readable@ + @if $c.needlength@ + $c.decl *$c; + size_t ${c}_len; + @else@ + $c.decl $c; + @end@ + @end@ + @end@ +}; + +int ${i}_load(netsnmp_cache *cache, void *vmagic); +void ${i}_free(netsnmp_cache *cache, void *vmagic); +@pop@ + +/** handles requests for the $i table */ +static int +handle_table_${i}( + netsnmp_mib_handler *handler, + netsnmp_handler_registration *reginfo, + netsnmp_agent_request_info *reqinfo, + netsnmp_request_info *requests) +{ + netsnmp_request_info *request; + netsnmp_table_request_info *table_info; + struct ${i}_entry *table_entry; + + switch (reqinfo->mode) { + case MODE_GET: + for (request = requests; request; request = request->next) { + table_entry = (struct ${i}_entry *) + netsnmp_tdata_extract_entry(request); + table_info = netsnmp_extract_table_info(request); + + switch (table_info->colnum) { + @foreach $c column@ + @if $c.readable@ + case COLUMN_$c.uc: + if (!table_entry) { + netsnmp_set_request_error(reqinfo, request, + SNMP_NOSUCHINSTANCE); + continue; + } + @if $c.needlength@ + snmp_set_var_typed_value(request->requestvb, $c.type, + table_entry->$c, + table_entry->${c}_len); + @else@ + snmp_set_var_typed_integer(request->requestvb, $c.type, + table_entry->$c); + @end@ + break; + @end@ + @end@ + default: + netsnmp_set_request_error(reqinfo, request, + SNMP_NOSUCHOBJECT); + break; + } + } + break; + default: + /* we should never get here, so this is a really bad error */ + snmp_log(LOG_ERR, "unknown mode (%d) in handle_table_${i}\n", + reqinfo->mode); + return SNMP_ERR_GENERR; + + } + return SNMP_ERR_NOERROR; +} + +/** Initialize the $i table by defining its contents and how it's structured */ +static void +initialize_table_$i(void) +{ + const oid ${i}_oid[] = {$i.commaoid}; + const size_t ${i}_oid_len = OID_LENGTH(${i}_oid); + netsnmp_handler_registration *reg; + netsnmp_tdata *table_data; + netsnmp_table_registration_info *table_info; + netsnmp_cache *cache; + + DEBUGMSGTL(("${name}:init", "initializing table $i\n")); + + reg = netsnmp_create_handler_registration("$i", handle_table_${i}, + ${i}_oid, ${i}_oid_len, + HANDLER_CAN_RONLY); + + table_data = netsnmp_tdata_create_table("$i", 0); + if (!table_data) { + snmp_log(LOG_ERR,"error creating tdata table for $i\n"); + return; + } + cache = netsnmp_cache_create(${i}_timeout, + ${i}_load, ${i}_free, + ${i}_oid, ${i}_oid_len); + if (!cache) { + snmp_log(LOG_ERR,"error creating cache for $i\n"); + } else + cache->magic = (void *)table_data; + table_info = SNMP_MALLOC_TYPEDEF(netsnmp_table_registration_info); + if (!table_info) { + snmp_log(LOG_ERR,"error creating table info for $i\n"); + return; + } + netsnmp_table_helper_add_indexes(table_info, + @foreach $idx index@ + $idx.type, /* index: $idx */ + @end@ + 0); + + table_info->min_column = COLUMN_$first_column.uc; + table_info->max_column = COLUMN_$last_column.uc; + + netsnmp_tdata_register(reg, table_data, table_info); + if (cache) + netsnmp_inject_handler(reg, netsnmp_cache_handler_get(cache)); +/* + netsnmp_inject_handler_before(reg, netsnmp_cache_handler_get(cache), + TABLE_TDATA_NAME); +*/ +} + +@end@ + /** Initializes the $name module */ void init_$modulename(void) { @@ -294,8 +503,11 @@ init_$modulename(void) HANDLER_CAN_RWRITE @end@ )); @end@ + @foreach $i table@ + initialize_table_$i(); + @end@ varnish_snmp_init(); } void |