1*3230bc44Sdlg /* $Id: cache.c,v 1.4 2011/11/29 10:17:52 dlg Exp $ */ 273baed14Scanacar /* 373baed14Scanacar * Copyright (c) 2001, 2007 Can Erkin Acar <canacar@openbsd.org> 473baed14Scanacar * 573baed14Scanacar * Permission to use, copy, modify, and distribute this software for any 673baed14Scanacar * purpose with or without fee is hereby granted, provided that the above 773baed14Scanacar * copyright notice and this permission notice appear in all copies. 873baed14Scanacar * 973baed14Scanacar * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 1073baed14Scanacar * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1173baed14Scanacar * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 1273baed14Scanacar * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1373baed14Scanacar * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1473baed14Scanacar * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1573baed14Scanacar * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 1673baed14Scanacar */ 1773baed14Scanacar 1873baed14Scanacar #include <sys/types.h> 1973baed14Scanacar #include <sys/ioctl.h> 2073baed14Scanacar #include <sys/socket.h> 2173baed14Scanacar 2273baed14Scanacar #include <net/if.h> 2373baed14Scanacar #include <netinet/in.h> 2473baed14Scanacar 2573baed14Scanacar #include <netinet/tcp_fsm.h> 2673baed14Scanacar #ifdef TEST_COMPAT 2773baed14Scanacar #include "pfvarmux.h" 2873baed14Scanacar #else 2973baed14Scanacar #include <net/pfvar.h> 3073baed14Scanacar #endif 3173baed14Scanacar #include <arpa/inet.h> 3273baed14Scanacar 3373baed14Scanacar #include <stdio.h> 3473baed14Scanacar #include <stdlib.h> 3573baed14Scanacar 3673baed14Scanacar #include <assert.h> 3773baed14Scanacar 3873baed14Scanacar #include "cache.h" 3973baed14Scanacar 4073baed14Scanacar /* prototypes */ 41d93808b7Scanacar void update_state(struct sc_ent *, struct pfsync_state *, double); 42d93808b7Scanacar struct sc_ent *cache_state(struct pfsync_state *); 4373baed14Scanacar static __inline int sc_cmp(struct sc_ent *s1, struct sc_ent *s2); 4473baed14Scanacar 4573baed14Scanacar /* initialize the tree and queue */ 4673baed14Scanacar RB_HEAD(sc_tree, sc_ent) sctree; 4773baed14Scanacar TAILQ_HEAD(sc_queue, sc_ent) scq1, scq2, scq_free; 4807fe0f2dScanacar RB_GENERATE(sc_tree, sc_ent, tlink, sc_cmp) 4973baed14Scanacar 5073baed14Scanacar struct sc_queue *scq_act = NULL; 5173baed14Scanacar struct sc_queue *scq_exp = NULL; 5273baed14Scanacar 5373baed14Scanacar int cache_max = 0; 5473baed14Scanacar int cache_size = 0; 5573baed14Scanacar 5673baed14Scanacar struct sc_ent *sc_store = NULL; 5773baed14Scanacar 5873baed14Scanacar /* preallocate the cache and insert into the 'free' queue */ 5973baed14Scanacar int 6073baed14Scanacar cache_init(int max) 6173baed14Scanacar { 6273baed14Scanacar int n; 6373baed14Scanacar static int initialized = 0; 6473baed14Scanacar 6573baed14Scanacar if (max < 0 || initialized) 6673baed14Scanacar return (1); 6773baed14Scanacar 6873baed14Scanacar if (max == 0) { 6973baed14Scanacar sc_store = NULL; 7073baed14Scanacar } else { 7173baed14Scanacar sc_store = malloc(max * sizeof(struct sc_ent)); 7273baed14Scanacar if (sc_store == NULL) 7373baed14Scanacar return (1); 7473baed14Scanacar } 7573baed14Scanacar 7673baed14Scanacar RB_INIT(&sctree); 7773baed14Scanacar TAILQ_INIT(&scq1); 7873baed14Scanacar TAILQ_INIT(&scq2); 7973baed14Scanacar TAILQ_INIT(&scq_free); 8073baed14Scanacar 8173baed14Scanacar scq_act = &scq1; 8273baed14Scanacar scq_exp = &scq2; 8373baed14Scanacar 8473baed14Scanacar for (n = 0; n < max; n++) 8573baed14Scanacar TAILQ_INSERT_HEAD(&scq_free, sc_store + n, qlink); 8673baed14Scanacar 8773baed14Scanacar cache_size = cache_max = max; 8873baed14Scanacar initialized++; 8973baed14Scanacar 9073baed14Scanacar return (0); 9173baed14Scanacar } 9273baed14Scanacar 9373baed14Scanacar void 94d93808b7Scanacar update_state(struct sc_ent *prev, struct pfsync_state *new, double rate) 9573baed14Scanacar { 9673baed14Scanacar assert (prev != NULL && new != NULL); 9773baed14Scanacar prev->t = time(NULL); 9873baed14Scanacar prev->rate = rate; 9973baed14Scanacar prev->bytes = COUNTER(new->bytes[0]) + COUNTER(new->bytes[1]); 10073baed14Scanacar if (prev->peak < rate) 10173baed14Scanacar prev->peak = rate; 10273baed14Scanacar } 10373baed14Scanacar 10473baed14Scanacar void 105d93808b7Scanacar add_state(struct pfsync_state *st) 10673baed14Scanacar { 10773baed14Scanacar struct sc_ent *ent; 10873baed14Scanacar assert(st != NULL); 10973baed14Scanacar 11073baed14Scanacar if (cache_max == 0) 11173baed14Scanacar return; 11273baed14Scanacar 11373baed14Scanacar if (TAILQ_EMPTY(&scq_free)) 11473baed14Scanacar return; 11573baed14Scanacar 11673baed14Scanacar ent = TAILQ_FIRST(&scq_free); 11773baed14Scanacar TAILQ_REMOVE(&scq_free, ent, qlink); 11873baed14Scanacar 11973baed14Scanacar cache_size--; 12073baed14Scanacar 121*3230bc44Sdlg ent->id = st->id; 122*3230bc44Sdlg ent->creatorid = st->creatorid; 12373baed14Scanacar ent->bytes = COUNTER(st->bytes[0]) + COUNTER(st->bytes[1]); 12473baed14Scanacar ent->peak = 0; 12573baed14Scanacar ent->rate = 0; 12673baed14Scanacar ent->t = time(NULL); 12773baed14Scanacar 12873baed14Scanacar RB_INSERT(sc_tree, &sctree, ent); 12973baed14Scanacar TAILQ_INSERT_HEAD(scq_act, ent, qlink); 13073baed14Scanacar } 13173baed14Scanacar 13273baed14Scanacar /* must be called only once for each state before cache_endupdate */ 13373baed14Scanacar struct sc_ent * 134d93808b7Scanacar cache_state(struct pfsync_state *st) 13573baed14Scanacar { 13673baed14Scanacar struct sc_ent ent, *old; 13773baed14Scanacar double sd, td, r; 13873baed14Scanacar 13973baed14Scanacar if (cache_max == 0) 14073baed14Scanacar return (NULL); 14173baed14Scanacar 142*3230bc44Sdlg ent.id = st->id; 143*3230bc44Sdlg ent.creatorid = st->creatorid; 14473baed14Scanacar old = RB_FIND(sc_tree, &sctree, &ent); 14573baed14Scanacar 14673baed14Scanacar if (old == NULL) { 14773baed14Scanacar add_state(st); 14873baed14Scanacar return (NULL); 14973baed14Scanacar } 15073baed14Scanacar 15173baed14Scanacar if (COUNTER(st->bytes[0]) + COUNTER(st->bytes[1]) < old->bytes) 15273baed14Scanacar return (NULL); 15373baed14Scanacar 15473baed14Scanacar sd = COUNTER(st->bytes[0]) + COUNTER(st->bytes[1]) - old->bytes; 15573baed14Scanacar td = time(NULL) - old->t; 15673baed14Scanacar 15773baed14Scanacar if (td > 0) { 15873baed14Scanacar r = sd/td; 15973baed14Scanacar update_state(old, st, r); 16073baed14Scanacar } 16173baed14Scanacar 16273baed14Scanacar /* move to active queue */ 16373baed14Scanacar TAILQ_REMOVE(scq_exp, old, qlink); 16473baed14Scanacar TAILQ_INSERT_HEAD(scq_act, old, qlink); 16573baed14Scanacar 16673baed14Scanacar return (old); 16773baed14Scanacar } 16873baed14Scanacar 16973baed14Scanacar /* remove the states that are not updated in this cycle */ 17073baed14Scanacar void 17173baed14Scanacar cache_endupdate(void) 17273baed14Scanacar { 17373baed14Scanacar struct sc_queue *tmp; 17473baed14Scanacar struct sc_ent *ent; 17573baed14Scanacar 17673baed14Scanacar while (! TAILQ_EMPTY(scq_exp)) { 17773baed14Scanacar ent = TAILQ_FIRST(scq_exp); 17873baed14Scanacar TAILQ_REMOVE(scq_exp, ent, qlink); 17973baed14Scanacar RB_REMOVE(sc_tree, &sctree, ent); 18073baed14Scanacar TAILQ_INSERT_HEAD(&scq_free, ent, qlink); 18173baed14Scanacar cache_size++; 18273baed14Scanacar } 18373baed14Scanacar 18473baed14Scanacar tmp = scq_act; 18573baed14Scanacar scq_act = scq_exp; 18673baed14Scanacar scq_exp = tmp; 18773baed14Scanacar } 18873baed14Scanacar 18973baed14Scanacar static __inline int 19073baed14Scanacar sc_cmp(struct sc_ent *a, struct sc_ent *b) 19173baed14Scanacar { 192*3230bc44Sdlg if (a->id > b->id) 19373baed14Scanacar return (1); 194*3230bc44Sdlg if (a->id < b->id) 19573baed14Scanacar return (-1); 196*3230bc44Sdlg if (a->creatorid > b->creatorid) 19773baed14Scanacar return (1); 198*3230bc44Sdlg if (a->creatorid < b->creatorid) 19973baed14Scanacar return (-1); 20073baed14Scanacar return (0); 20173baed14Scanacar } 202