diff options
Diffstat (limited to 'usr.bin/systat/cache.c')
-rw-r--r-- | usr.bin/systat/cache.c | 297 |
1 files changed, 297 insertions, 0 deletions
diff --git a/usr.bin/systat/cache.c b/usr.bin/systat/cache.c new file mode 100644 index 00000000000..136f8871435 --- /dev/null +++ b/usr.bin/systat/cache.c @@ -0,0 +1,297 @@ +/* $Id: cache.c,v 1.1 2008/06/12 22:26:01 canacar Exp $ */ +/* + * Copyright (c) 2001, 2007 Can Erkin Acar <canacar@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/socket.h> + +#include <net/if.h> +#include <netinet/in.h> + +#include <netinet/tcp_fsm.h> +#ifdef TEST_COMPAT +#include "pfvarmux.h" +#else +#include <net/pfvar.h> +#endif +#include <arpa/inet.h> + +#include <stdio.h> +#include <stdlib.h> + +#include <assert.h> + +#include "cache.h" + +/* prototypes */ +void update_state(struct sc_ent *, pf_state_t *, double); +struct sc_ent *cache_state(pf_state_t *); +static __inline int sc_cmp(struct sc_ent *s1, struct sc_ent *s2); + +/* initialize the tree and queue */ +RB_HEAD(sc_tree, sc_ent) sctree; +TAILQ_HEAD(sc_queue, sc_ent) scq1, scq2, scq_free; +RB_GENERATE(sc_tree, sc_ent, tlink, sc_cmp); + +struct sc_queue *scq_act = NULL; +struct sc_queue *scq_exp = NULL; + +int cache_max = 0; +int cache_size = 0; + +struct sc_ent *sc_store = NULL; + +/* preallocate the cache and insert into the 'free' queue */ +int +cache_init(int max) +{ + int n; + static int initialized = 0; + + if (max < 0 || initialized) + return (1); + + if (max == 0) { + sc_store = NULL; + } else { + sc_store = malloc(max * sizeof(struct sc_ent)); + if (sc_store == NULL) + return (1); + } + + RB_INIT(&sctree); + TAILQ_INIT(&scq1); + TAILQ_INIT(&scq2); + TAILQ_INIT(&scq_free); + + scq_act = &scq1; + scq_exp = &scq2; + + for (n = 0; n < max; n++) + TAILQ_INSERT_HEAD(&scq_free, sc_store + n, qlink); + + cache_size = cache_max = max; + initialized++; + + return (0); +} + +void +update_state(struct sc_ent *prev, pf_state_t *new, double rate) +{ + assert (prev != NULL && new != NULL); + prev->t = time(NULL); + prev->rate = rate; +#ifdef HAVE_INOUT_COUNT + prev->bytes = COUNTER(new->bytes[0]) + COUNTER(new->bytes[1]); +#else + prev->bytes = COUNTER(new->bytes); +#endif + if (prev->peak < rate) + prev->peak = rate; +} + +void +add_state(pf_state_t *st) +{ + struct sc_ent *ent; + assert(st != NULL); + + if (cache_max == 0) + return; + + if (TAILQ_EMPTY(&scq_free)) + return; + + ent = TAILQ_FIRST(&scq_free); + TAILQ_REMOVE(&scq_free, ent, qlink); + + cache_size--; + +#ifdef HAVE_PFSYNC_STATE + ent->id[0] = st->id[0]; + ent->id[1] = st->id[1]; +#else + ent->addr[0] = st->lan.addr; + ent->port[0] = st->lan.port; + ent->addr[1] = st->ext.addr; + ent->port[1] = st->ext.port; + ent->af = st->af; + ent->proto = st->proto; +#endif +#ifdef HAVE_INOUT_COUNT + ent->bytes = COUNTER(st->bytes[0]) + COUNTER(st->bytes[1]); +#else + ent->bytes = st->bytes; +#endif + ent->peak = 0; + ent->rate = 0; + ent->t = time(NULL); + + RB_INSERT(sc_tree, &sctree, ent); + TAILQ_INSERT_HEAD(scq_act, ent, qlink); +} + +/* must be called only once for each state before cache_endupdate */ +struct sc_ent * +cache_state(pf_state_t *st) +{ + struct sc_ent ent, *old; + double sd, td, r; + + if (cache_max == 0) + return (NULL); + +#ifdef HAVE_PFSYNC_STATE + ent.id[0] = st->id[0]; + ent.id[1] = st->id[1]; +#else + ent.addr[0] = st->lan.addr; + ent.port[0] = st->lan.port; + ent.addr[1] = st->ext.addr; + ent.port[1] = st->ext.port; + ent.af = st->af; + ent.proto = st->proto; +#endif + old = RB_FIND(sc_tree, &sctree, &ent); + + if (old == NULL) { + add_state(st); + return (NULL); + } + +#ifdef HAVE_INOUT_COUNT + if (COUNTER(st->bytes[0]) + COUNTER(st->bytes[1]) < old->bytes) + return (NULL); + + sd = COUNTER(st->bytes[0]) + COUNTER(st->bytes[1]) - old->bytes; +#else + if (st->bytes < old->bytes) + return (NULL); + + sd = st->bytes - old->bytes; +#endif + + td = time(NULL) - old->t; + + if (td > 0) { + r = sd/td; + update_state(old, st, r); + } + + /* move to active queue */ + TAILQ_REMOVE(scq_exp, old, qlink); + TAILQ_INSERT_HEAD(scq_act, old, qlink); + + return (old); +} + +/* remove the states that are not updated in this cycle */ +void +cache_endupdate(void) +{ + struct sc_queue *tmp; + struct sc_ent *ent; + + while (! TAILQ_EMPTY(scq_exp)) { + ent = TAILQ_FIRST(scq_exp); + TAILQ_REMOVE(scq_exp, ent, qlink); + RB_REMOVE(sc_tree, &sctree, ent); + TAILQ_INSERT_HEAD(&scq_free, ent, qlink); + cache_size++; + } + + tmp = scq_act; + scq_act = scq_exp; + scq_exp = tmp; +} + +static __inline int +sc_cmp(struct sc_ent *a, struct sc_ent *b) +{ +#ifdef HAVE_PFSYNC_STATE + if (a->id[0] > b->id[0]) + return (1); + if (a->id[0] < b->id[0]) + return (-1); + if (a->id[1] > b->id[1]) + return (1); + if (a->id[1] < b->id[1]) + return (-1); +#else + int diff; + + if ((diff = a->proto - b->proto) != 0) + return (diff); + if ((diff = a->af - b->af) != 0) + return (diff); + switch (a->af) { + case AF_INET: + if (a->addr[0].addr32[0] > b->addr[0].addr32[0]) + return (1); + if (a->addr[0].addr32[0] < b->addr[0].addr32[0]) + return (-1); + if (a->addr[1].addr32[0] > b->addr[1].addr32[0]) + return (1); + if (a->addr[1].addr32[0] < b->addr[1].addr32[0]) + return (-1); + break; + case AF_INET6: + if (a->addr[0].addr32[0] > b->addr[0].addr32[0]) + return (1); + if (a->addr[0].addr32[0] < b->addr[0].addr32[0]) + return (-1); + if (a->addr[0].addr32[1] > b->addr[0].addr32[1]) + return (1); + if (a->addr[0].addr32[1] < b->addr[0].addr32[1]) + return (-1); + if (a->addr[0].addr32[2] > b->addr[0].addr32[2]) + return (1); + if (a->addr[0].addr32[2] < b->addr[0].addr32[2]) + return (-1); + if (a->addr[0].addr32[3] > b->addr[0].addr32[3]) + return (1); + if (a->addr[0].addr32[3] < b->addr[0].addr32[3]) + return (-1); + if (a->addr[1].addr32[0] > b->addr[1].addr32[0]) + return (1); + if (a->addr[1].addr32[0] < b->addr[1].addr32[0]) + return (-1); + if (a->addr[1].addr32[1] > b->addr[1].addr32[1]) + return (1); + if (a->addr[1].addr32[1] < b->addr[1].addr32[1]) + return (-1); + if (a->addr[1].addr32[2] > b->addr[1].addr32[2]) + return (1); + if (a->addr[1].addr32[2] < b->addr[1].addr32[2]) + return (-1); + if (a->addr[1].addr32[3] > b->addr[1].addr32[3]) + return (1); + if (a->addr[1].addr32[3] < b->addr[1].addr32[3]) + return (-1); + break; + default: + return 1; + } + + if ((diff = a->port[0] - b->port[0]) != 0) + return (diff); + if ((diff = a->port[1] - b->port[1]) != 0) + return (diff); +#endif + return (0); +} |