From ec0d36ee55709f1df76f977823722a4b53cf27ed Mon Sep 17 00:00:00 2001 From: Stef Walter Date: Sat, 3 May 2008 02:36:31 +0000 Subject: A whole bunch of changes including: * Measure disk space * Support mulitple IPs per jail. --- module/bsnmp-jails.c | 499 ++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 437 insertions(+), 62 deletions(-) (limited to 'module/bsnmp-jails.c') diff --git a/module/bsnmp-jails.c b/module/bsnmp-jails.c index 9e88d5f..87e5136 100644 --- a/module/bsnmp-jails.c +++ b/module/bsnmp-jails.c @@ -49,6 +49,7 @@ #include #include #include +#include #include #include @@ -72,6 +73,31 @@ #include "jails_tree.h" #include "jails_oid.h" +/* ------------------------------------------------------------------- + * COMPAT + */ + +struct xprison_v1 { + int pr_version; + int pr_id; + char pr_path[MAXPATHLEN]; + char pr_host[MAXHOSTNAMELEN]; + u_int32_t pr_ip; +}; + +struct xprison_v2 { + int pr_version; + int pr_id; + char pr_path[MAXPATHLEN]; + char pr_host[MAXHOSTNAMELEN]; + u_int32_t pr_ips[JAIL_MAX_IPS]; + u_int pr_nips; +}; + +/* ------------------------------------------------------------------- + * DECLARATIONS + */ + #define SNAP_LEN 48 /* our module handle */ @@ -109,10 +135,13 @@ struct jaildat { int jailid; char *host; char *path; - struct sockaddr_in addr; - /* Network monitor */ - struct monitor *monitor; + /* Monitor pointers for each address */ + char n_addrs; + struct { + struct sockaddr_in ip; + struct monitor *monitor; + } addrs[JAIL_MAX_IPS]; /* Stats gathered */ uint64_t in_octets; @@ -120,6 +149,8 @@ struct jaildat { uint64_t out_octets; uint64_t out_packets; uint32_t n_processes; + uint64_t disk_space; + uint64_t disk_files; }; TAILQ_HEAD(jaildat_list, jaildat); @@ -129,10 +160,14 @@ static struct jaildat_list jaildats = TAILQ_HEAD_INITIALIZER (jaildats); /* number of if jail structures */ static u_int jaildat_count = 0; +static u_int jaildat_index = 0; /* Hash of jail structures by id */ static hsh_t *jaildat_by_host = NULL; +/* Hash of jail structures by path */ +static hsh_t *jaildat_by_path = NULL; + /* Hash of jail structures by address */ static hsh_t *jaildat_by_address = NULL; @@ -142,15 +177,21 @@ static hsh_t *jaildat_by_id = NULL; /* Timer for refreshing the jaildat info */ static void *timer_refresh = NULL; +/* Refresh interval in SNMP ticks */ +static int refresh_interval = 3 * 100; + +/* Timer for measuring the jails */ +static void *timer_measure = NULL; + +/* Measure interval in SNMP ticks */ +static int measure_interval = 1800 * 100; + /* The monitor network filter */ static u_char *network_filter = NULL; /* The KVM interface handle */ static kvm_t *kvm_handle = NULL; -/* Refresh interval in SNMP ticks */ -static int refresh_interval = 300; - /* ----------------------------------------------------------------------------- * HELPERS */ @@ -161,6 +202,7 @@ emsg(const char *format, ...) va_list va; va_start (va, format); vsyslog (LOG_ERR, format, va); + vfprintf (stderr, format, va); va_end (va); } @@ -407,7 +449,7 @@ monitor_create (const char *device) TAILQ_INSERT_TAIL(&monitors, mon, link); mon->device = strdup (device); - mon->handle = pcap_open_live (mon->device, SNAP_LEN, 0, 100, errbuf); + mon->handle = pcap_open_live (mon->device, SNAP_LEN, 1, 100, errbuf); if (!mon->handle) { emsg ("couldn't open monitor on %s: %s", mon->device, errbuf); goto cleanup; @@ -546,6 +588,198 @@ monitor_test_filter (const char *filter) return ret; } +/* ----------------------------------------------------------------------------- + * MEASURE JAILS + */ + +static void *measure_out_watch = NULL; +static int measure_out_fd = -1; + +static void +measure_parse (char *line) +{ + unsigned long long value; + struct jaildat *jail; + char *p; + + /* Find the first colon */ + p = strchr (line, ':'); + if (!p) { + emsg ("invalid output from measure process: %s", line); + return; + } + + /* Skip past the colon and spaces */ + *p = 0; + ++p; + while (isspace (*p)) + ++p; + + /* Parse the number */ + value = strtoull (p, &p, 10); + + /* Skip past any more spaces */ + while (isspace (*p)) + ++p; + + /* Find a jail with this path */ + jail = hsh_get (jaildat_by_path, p, HSH_KEY_STRING); + if (!jail) + return; + + /* Update the various values */ + if (strcmp (line, "jail-space") == 0) + jail->disk_space = value; + else if (strcmp (line, "jail-files") == 0) + jail->disk_files = value; +} + +static void +measure_io (int fd, void *unused) +{ + char buffer[2048]; + char *line, *next; + int r; + + ASSERT (measure_out_fd == fd); + + r = read (fd, buffer, sizeof (buffer) - 1); + + /* Error conditions */ + if (r < 0) { + if (errno == EAGAIN || errno == EINTR) + return; + emsg ("couldn't read from jail measure: %s", strerror (errno)); + } + + /* Close and disconnect */ + if (r <= 0) { + fd_deselect (measure_out_watch); + measure_out_watch = NULL; + close (measure_out_fd); + measure_out_fd = -1; + return; + } + + /* Null terminate the line */ + ASSERT (r < sizeof (buffer)); + buffer[r] = 0; + line = buffer; + + /* And parse each line received */ + while (line) { + next = strchr (line, '\n'); + if (next) + *(next++) = 0; + while (isspace (*line)) + ++line; + if (*line) + measure_parse (line); + line = next; + } +} + +static char** +measure_get_command (void) +{ + struct jaildat *jail; + char **args; + int i; + + args = calloc (jaildat_count + 4, sizeof (char*)); + if (!args) { + emsg ("out of memory"); + return NULL; + } + + args[0] = JAIL_MEASURE_PATH; + args[1] = "-l"; + args[2] = "11"; /* ie: LOG_ERR | LOG_DAEMON */ + + i = 3; + TAILQ_FOREACH (jail, &jaildats, link) + args[i++] = jail->path; + + return args; +} + +static void +measure_all (void *unused) +{ + char **args; + int pip[2]; + pid_t pid; + int status; + + if (measure_out_watch) { + ASSERT (measure_out_fd != -1); + emsg ("jail measure already in progress, skipping"); + return; + } + + args = measure_get_command (); + if (!args) + return; + + if (pipe (pip) < 0) { + emsg ("couldn't create pipe to do jail measure: %s", strerror (errno)); + free (args); + return; + } + + pid = fork (); + switch (pid) { + case -1: + emsg ("couldn't fork a measure process: %s", strerror (errno)); + free (args); + close (pip[0]); + close (pip[1]); + return; + + case 0: + /* Detach this new process */ + if (daemon (1, 1) < 0) { + emsg ("couldn't daemonize the measure process: %s", strerror (errno)); + exit (1); + } + + /* Dup pipe into stdout for process */ + close (pip[0]); + dup2 (pip[1], 1); + + /* Run the measure process */ + execv (args[0], args); + emsg ("couldn't execute the measure process: %s", strerror (errno)); + exit (1); + break; + + default: + break; + }; + + /* We no longer need this end */ + waitpid (pid, &status, 0); + close (pip[1]); + free (args); + + measure_out_watch = fd_select (pip[0], measure_io, NULL, module); + if (!measure_out_watch) { + emsg ("couldn't watch the measure process output: %s", strerror (errno)); + close (pip[0]); + } + + measure_out_fd = pip[0]; +} + +static void +measure_start (void *unused) +{ + ASSERT (timer_measure); + timer_measure = timer_start_repeat (measure_interval, measure_interval, + measure_all, NULL, module); + measure_all (NULL); +} + /* ----------------------------------------------------------------------------- * PROCESS LOOKUPS */ @@ -588,6 +822,8 @@ process_refresh_all (void) static void jail_free (struct jaildat *jail) { + int i; + ASSERT (jail); if (jail->jailid) @@ -599,16 +835,21 @@ jail_free (struct jaildat *jail) jail->host = NULL; } - hsh_rem (jaildat_by_address, &(jail->addr), jail->addr.sin_len); + /* Remove all addresses and release monitors */ + for (i = 0; i < jail->n_addrs; ++i) { + hsh_rem (jaildat_by_address, &(jail->addrs[i].ip), + jail->addrs[i].ip.sin_len); + if (jail->addrs[i].monitor) + monitor_unref (jail->addrs[i].monitor); + memset (&jail->addrs[i], 0, sizeof (jail->addrs[i])); + } - if (jail->path) + if (jail->path) { + hsh_rem (jaildat_by_path, jail->path, HSH_KEY_STRING); free (jail->path); + } jail->path = NULL; - if (jail->monitor) - monitor_unref (jail->monitor); - jail->monitor = NULL; - if (jail->index) { TAILQ_REMOVE (&jaildats, jail, link); jaildat_count--; @@ -619,19 +860,23 @@ jail_free (struct jaildat *jail) static int jail_update (struct jaildat *jail, int jailid, const char *host, - const char *path, struct in_addr *addr) + const char *path, u_int32_t *ips, u_int n_ips) { - struct monitor *mon; + struct monitor *mon = NULL; + struct sockaddr_in addr; char *dup; + int i, top; ASSERT (jail); + /* Do the jail id */ if (jail->jailid != jailid) { hsh_rem (jaildat_by_id, &jail->jailid, sizeof (jail->jailid)); jail->jailid = jailid; hsh_set (jaildat_by_id, &jail->jailid, sizeof (jail->jailid), jail); } + /* Do the host */ if (!host) host = ""; @@ -647,40 +892,78 @@ jail_update (struct jaildat *jail, int jailid, const char *host, hsh_set (jaildat_by_host, jail->host, HSH_KEY_STRING, jail); } + /* Do the path */ if (!path) path = ""; + if (!jail->path || strcmp (jail->path, path) != 0) { dup = strdup (path); if (!dup) return -1; - if (jail->path) + if (jail->path) { + hsh_rem (jaildat_by_path, jail->path, HSH_KEY_STRING); free (jail->path); + } jail->path = dup; + hsh_set (jaildat_by_path, jail->path, HSH_KEY_STRING, jail); } - if (memcmp (&jail->addr.sin_addr, addr, sizeof (jail->addr.sin_addr)) != 0) { - if (jail->addr.sin_len) - hsh_rem (jaildat_by_address, &(jail->addr), jail->addr.sin_len); - jail->addr.sin_family = AF_INET; - jail->addr.sin_len = sizeof (jail->addr); - jail->addr.sin_port = 0; - memcpy (&jail->addr.sin_addr, addr, sizeof (jail->addr.sin_addr)); - hsh_set (jaildat_by_address, &(jail->addr), jail->addr.sin_len, jail); - - mon = monitor_for_address ((struct sockaddr*)&jail->addr); - if (mon && mon != jail->monitor) { - monitor_ref (mon); - if (jail->monitor) - monitor_unref (jail->monitor); - jail->monitor = mon; + /* Do the addresses */ + top = (jail->n_addrs > n_ips ? jail->n_addrs : n_ips); + for (i = 0; i < top; ++i) { + + /* Setup the address */ + if (i < n_ips) { + memset (&addr, 0, sizeof (addr)); + addr.sin_family = AF_INET; + addr.sin_len = sizeof (addr); + addr.sin_port = 0; + addr.sin_addr.s_addr = ntohl (ips[i]); + + /* Same? skip. */ + if (i < jail->n_addrs && + memcmp (&jail->addrs[i].ip, &addr, sizeof (addr)) == 0) + continue; + } + + /* Remove old address */ + if (i < jail->n_addrs) { + hsh_rem (jaildat_by_address, &(jail->addrs[i].ip), + jail->addrs[i].ip.sin_len); + memset (&jail->addrs[i].ip, 0, sizeof (jail->addrs[i].ip)); + } + + /* Add new address */ + if (i < n_ips) { + memcpy (&jail->addrs[i].ip, &addr, sizeof (addr)); + hsh_set (jaildat_by_address, &(jail->addrs[i].ip), + jail->addrs[i].ip.sin_len, jail); + } + + /* Register new monitor */ + mon = NULL; + if (i < n_ips) { + mon = monitor_for_address ((struct sockaddr*)&addr); + if (mon) + monitor_ref (mon); } + + /* Free the old monitor */ + if (i < jail->n_addrs && jail->addrs[i].monitor) + monitor_unref (jail->addrs[i].monitor); + + /* Assign the monitor */ + jail->addrs[i].monitor = mon; } + jail->n_addrs = n_ips; + return 0; } static struct jaildat* -jail_alloc (int jailid, const char *host, const char *path, struct in_addr *addr) +jail_alloc (int jailid, const char *host, const char *path, + u_int32_t *ips, u_int n_ips) { struct jaildat *jail; @@ -688,25 +971,85 @@ jail_alloc (int jailid, const char *host, const char *path, struct in_addr *addr if (!jail) return NULL; - if (jail_update (jail, jailid, host, path, addr) < 0) { + if (jail_update (jail, jailid, host, path, ips, n_ips) < 0) { jail_free (jail); return NULL; } jaildat_count++; - jail->index = jaildat_count; + jail->index = ++jaildat_index; INSERT_OBJECT_INT (jail, &jaildats); return jail; } +static void +jail_refresh_v1 (struct xprison_v1 *xp, size_t len) +{ + struct jaildat *jail; + int i; + + /* Make sure its kosher */ + if (len < sizeof (*xp) || len % sizeof (*xp)) { + emsg ("not a valid v1 xprison. kernel and userland out of sync?"); + return; + } + + /* + * Allocate new jails, and update old ones. We iterate backwards + * so that if there are multiple jails with the same host name + * (some possibly zombies), then we'll see the most recent one. + */ + for (i = (len / sizeof (*xp)) - 1; i >= 0; --i) { + jail = hsh_get (jaildat_by_host, xp[i].pr_host, HSH_KEY_STRING); + if (!jail) + jail = jail_alloc (xp[i].pr_id, xp[i].pr_host, xp[i].pr_path, + &xp[i].pr_ip, 1); + else + jail_update (jail, xp[i].pr_id, xp[i].pr_host, xp[i].pr_path, + &xp[i].pr_ip, 1); + if (jail) + jail->mark = 0; + } +} + +static void +jail_refresh_v2 (struct xprison_v2 *xp, size_t len) +{ + struct jaildat *jail; + int i; + + /* Make sure its kosher */ + if (len < sizeof (*xp) || len % sizeof (*xp)) { + emsg ("not a valid v2 xprison. kernel and userland out of sync?"); + return; + } + + /* + * Allocate new jails, and update old ones. We iterate backwards + * so that if there are multiple jails with the same host name + * (some possibly zombies), then we'll see the most recent one. + */ + for (i = (len / sizeof (*xp)) - 1; i >= 0; --i) { + jail = hsh_get (jaildat_by_host, xp[i].pr_host, HSH_KEY_STRING); + if (!jail) + jail = jail_alloc (xp[i].pr_id, xp[i].pr_host, xp[i].pr_path, + xp[i].pr_ips, xp[i].pr_nips); + else + jail_update (jail, xp[i].pr_id, xp[i].pr_host, xp[i].pr_path, + xp[i].pr_ips, xp[i].pr_nips); + if (jail) + jail->mark = 0; + } +} + static void jail_refresh_all (void* unused) { - struct xprison *sxp, *xp; - struct in_addr in; + struct xprison *sxp = NULL; struct jaildat *jail, *tmp; - size_t i, len; + size_t len; + int i; /* Get the length of the list */ if (sysctlbyname ("security.jail.list", NULL, &len, NULL, 0) == -1) { @@ -717,14 +1060,15 @@ jail_refresh_all (void* unused) /* Retrieve actual data */ for (i = 0; i < 4; i++) { if (len <= 0) - return; - sxp = xp = malloc (len); + break; + + sxp = malloc (len); if (sxp == NULL) { emsg ("out of memory"); return; } - if (sysctlbyname ("security.jail.list", xp, &len, NULL, 0) == -1) { + if (sysctlbyname ("security.jail.list", sxp, &len, NULL, 0) == -1) { free (sxp); sxp = NULL; if (errno == ENOMEM) @@ -739,28 +1083,19 @@ jail_refresh_all (void* unused) break; } - /* Make sure its kosher */ - if (len < sizeof (*xp) || len % sizeof (*xp) || xp->pr_version != XPRISON_VERSION) { - emsg ("kernel and userland out of sync"); - free (sxp); - return; - } - /* Mark and prepare for sweep below */ TAILQ_FOREACH (jail, &jaildats, link) jail->mark = 1; - /* Allocate new jails, and update old ones */ - for (i = 0; i < len / sizeof (*xp); ++i) { - in.s_addr = ntohl (xp->pr_ip); - jail = hsh_get (jaildat_by_host, xp->pr_host, HSH_KEY_STRING); - if (!jail) - jail = jail_alloc (xp->pr_id, xp->pr_host, xp->pr_path, &in); + if (len > 0) { + if (sxp->pr_version == 1) + jail_refresh_v1 ((struct xprison_v1*)sxp, len); + else if (sxp->pr_version == 2) + jail_refresh_v2 ((struct xprison_v2*)sxp, len); else - jail_update (jail, xp->pr_id, xp->pr_host, xp->pr_path, &in); + emsg ("unsupported kernel structure version: %d\n", sxp->pr_version); - jail->mark = 0; - xp++; + free (sxp); } /* Sweep any jails that are no longer */ @@ -769,8 +1104,6 @@ jail_refresh_all (void* unused) jail_free (jail); } - free (sxp); - process_refresh_all (); /* Remove any jails that have no processes (kernel anomally) */ @@ -798,6 +1131,9 @@ op_jailconfig (struct snmp_context *ctx, struct snmp_value *value, case LEAF_jailRefreshInterval: value->v.uint32 = refresh_interval; return SNMP_ERR_NOERROR; + case LEAF_jailMeasureInterval: + value->v.uint32 = measure_interval; + return SNMP_ERR_NOERROR; default: ASSERT (0); return -1; @@ -848,6 +1184,22 @@ op_jailconfig (struct snmp_context *ctx, struct snmp_value *value, }; break; + case LEAF_jailMeasureInterval: + switch (op) { + case SNMP_OP_SET: + ctx->scratch->int1 = measure_interval; + measure_interval = value->v.uint32; + return SNMP_ERR_NOERROR; + case SNMP_OP_ROLLBACK: + measure_interval = ctx->scratch->int1; + return SNMP_ERR_NOERROR; + case SNMP_OP_COMMIT: + return SNMP_ERR_NOERROR; + default: + ASSERT (0); + return SNMP_ERR_GENERR; + }; + break; default: ASSERT (0); return SNMP_ERR_GENERR; @@ -893,20 +1245,26 @@ op_jailentry (struct snmp_context *ctx, struct snmp_value *value, case LEAF_jailHost: return string_get (value, jail->host, -1); case LEAF_jailInOctets: - value->v.uint32 = jail->in_octets; + value->v.counter64 = jail->in_octets; return SNMP_ERR_NOERROR; case LEAF_jailInPackets: - value->v.uint32 = jail->in_packets; + value->v.counter64 = jail->in_packets; return SNMP_ERR_NOERROR; case LEAF_jailOutOctets: - value->v.uint32 = jail->out_octets; + value->v.counter64 = jail->out_octets; return SNMP_ERR_NOERROR; case LEAF_jailOutPackets: - value->v.uint32 = jail->out_packets; + value->v.counter64 = jail->out_packets; return SNMP_ERR_NOERROR; case LEAF_jailProcesses: value->v.integer = jail->n_processes; return SNMP_ERR_NOERROR; + case LEAF_jailDiskSpace: + value->v.counter64 = jail->disk_space; + return SNMP_ERR_NOERROR; + case LEAF_jailDiskFiles: + value->v.counter64 = jail->disk_files; + return SNMP_ERR_NOERROR; default: ASSERT (0); return SNMP_ERR_NOSUCHNAME; @@ -982,6 +1340,10 @@ module_fini (void) hsh_free (jaildat_by_host); jaildat_by_host = NULL; + if (jaildat_by_path) + hsh_free (jaildat_by_path); + jaildat_by_path = NULL; + while ((jail = TAILQ_FIRST(&jaildats)) != NULL) jail_free (jail); @@ -992,6 +1354,10 @@ module_fini (void) timer_stop (timer_refresh); timer_refresh = NULL; + if (timer_measure) + timer_stop (timer_measure); + timer_measure = NULL; + return 0; } @@ -1031,6 +1397,10 @@ module_init (struct lmodule *mod, int argc, char *argv[]) if (!jaildat_by_host) goto cleanup; + jaildat_by_path = hsh_create (); + if (!jaildat_by_path) + goto cleanup; + jaildat_by_address = hsh_create (); if (!jaildat_by_address) goto cleanup; @@ -1059,6 +1429,11 @@ module_start (void) jail_refresh_all, NULL, module); if (!timer_refresh) emsg ("couldn't setup timer to refresh jails"); + + /* Wait for some time before first measure */ + timer_measure = timer_start (measure_interval / 5, measure_start, NULL, module); + if (!timer_measure) + emsg ("couldn't setup timer to measure jails"); } const struct snmp_module config = { -- cgit v1.2.3