jcs's openbsd hax
openbsd
1/* $OpenBSD: cache.c,v 1.8 2019/01/17 05:56:29 tedu Exp $ */
2/*
3 * Copyright (c) 2001, 2007 Can Erkin Acar <canacar@openbsd.org>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18#include <sys/types.h>
19#include <sys/ioctl.h>
20#include <sys/socket.h>
21
22#include <net/if.h>
23#include <netinet/in.h>
24
25#include <netinet/tcp_fsm.h>
26#ifdef TEST_COMPAT
27#include "pfvarmux.h"
28#else
29#include <net/pfvar.h>
30#endif
31#include <arpa/inet.h>
32
33#include <stdio.h>
34#include <stdlib.h>
35#include <time.h>
36
37#include <assert.h>
38
39#include "cache.h"
40
41/* prototypes */
42void update_state(struct sc_ent *, struct pfsync_state *, double);
43struct sc_ent *cache_state(struct pfsync_state *);
44static __inline int sc_cmp(struct sc_ent *s1, struct sc_ent *s2);
45
46/* initialize the tree and queue */
47RB_HEAD(sc_tree, sc_ent) sctree;
48TAILQ_HEAD(sc_queue, sc_ent) scq1, scq2, scq_free;
49RB_GENERATE(sc_tree, sc_ent, tlink, sc_cmp)
50
51struct sc_queue *scq_act = NULL;
52struct sc_queue *scq_exp = NULL;
53
54int cache_max = 0;
55int cache_size = 0;
56
57struct sc_ent *sc_store = NULL;
58
59/* preallocate the cache and insert into the 'free' queue */
60int
61cache_init(int max)
62{
63 int n;
64 static int initialized = 0;
65
66 if (max < 0 || initialized)
67 return (1);
68
69 if (max == 0) {
70 sc_store = NULL;
71 } else {
72 sc_store = reallocarray(NULL, max, sizeof(struct sc_ent));
73 if (sc_store == NULL)
74 return (1);
75 }
76
77 RB_INIT(&sctree);
78 TAILQ_INIT(&scq1);
79 TAILQ_INIT(&scq2);
80 TAILQ_INIT(&scq_free);
81
82 scq_act = &scq1;
83 scq_exp = &scq2;
84
85 for (n = 0; n < max; n++)
86 TAILQ_INSERT_HEAD(&scq_free, sc_store + n, qlink);
87
88 cache_size = cache_max = max;
89 initialized++;
90
91 return (0);
92}
93
94void
95update_state(struct sc_ent *prev, struct pfsync_state *new, double rate)
96{
97 assert (prev != NULL && new != NULL);
98 prev->t = time(NULL);
99 prev->rate = rate;
100 prev->bytes = COUNTER(new->bytes[0]) + COUNTER(new->bytes[1]);
101 if (prev->peak < rate)
102 prev->peak = rate;
103}
104
105void
106add_state(struct pfsync_state *st)
107{
108 struct sc_ent *ent;
109 assert(st != NULL);
110
111 if (cache_max == 0)
112 return;
113
114 if (TAILQ_EMPTY(&scq_free))
115 return;
116
117 ent = TAILQ_FIRST(&scq_free);
118 TAILQ_REMOVE(&scq_free, ent, qlink);
119
120 cache_size--;
121
122 ent->id = st->id;
123 ent->creatorid = st->creatorid;
124 ent->bytes = COUNTER(st->bytes[0]) + COUNTER(st->bytes[1]);
125 ent->peak = 0;
126 ent->rate = 0;
127 ent->t = 0;
128
129 RB_INSERT(sc_tree, &sctree, ent);
130 TAILQ_INSERT_HEAD(scq_act, ent, qlink);
131}
132
133/* must be called only once for each state before cache_endupdate */
134struct sc_ent *
135cache_state(struct pfsync_state *st)
136{
137 struct sc_ent ent, *old;
138 double sd, td, r;
139
140 if (cache_max == 0)
141 return (NULL);
142
143 ent.id = st->id;
144 ent.creatorid = st->creatorid;
145 old = RB_FIND(sc_tree, &sctree, &ent);
146
147 if (old == NULL) {
148 add_state(st);
149 return (NULL);
150 }
151
152 if (COUNTER(st->bytes[0]) + COUNTER(st->bytes[1]) < old->bytes)
153 return (NULL);
154
155 sd = COUNTER(st->bytes[0]) + COUNTER(st->bytes[1]) - old->bytes;
156 td = time(NULL) - old->t;
157
158 if (td > 0) {
159 r = sd/td;
160 update_state(old, st, r);
161 }
162
163 /* move to active queue */
164 TAILQ_REMOVE(scq_exp, old, qlink);
165 TAILQ_INSERT_HEAD(scq_act, old, qlink);
166
167 return (old);
168}
169
170/* remove the states that are not updated in this cycle */
171void
172cache_endupdate(void)
173{
174 struct sc_queue *tmp;
175 struct sc_ent *ent;
176
177 while (! TAILQ_EMPTY(scq_exp)) {
178 ent = TAILQ_FIRST(scq_exp);
179 TAILQ_REMOVE(scq_exp, ent, qlink);
180 RB_REMOVE(sc_tree, &sctree, ent);
181 TAILQ_INSERT_HEAD(&scq_free, ent, qlink);
182 cache_size++;
183 }
184
185 tmp = scq_act;
186 scq_act = scq_exp;
187 scq_exp = tmp;
188}
189
190static __inline int
191sc_cmp(struct sc_ent *a, struct sc_ent *b)
192{
193 if (a->id > b->id)
194 return (1);
195 if (a->id < b->id)
196 return (-1);
197 if (a->creatorid > b->creatorid)
198 return (1);
199 if (a->creatorid < b->creatorid)
200 return (-1);
201 return (0);
202}