]>
Commit | Line | Data |
---|---|---|
23b1539b PM |
1 | /* |
2 | * Filters: utility functions | |
3 | * | |
4 | * Copyright 1998 Pavel Machek <pavel@ucw.cz> | |
5 | * | |
6 | * Can be freely distributed and used under the terms of the GNU GPL. | |
29818140 | 7 | * |
23b1539b PM |
8 | */ |
9 | ||
2337ade7 PM |
10 | /** |
11 | * DOC: Filters | |
12 | * | |
725270cb MM |
13 | * You can find sources of the filter language in |filter/| |
14 | * directory. File |filter/config.Y| contains filter grammar and basically translates | |
15 | * the source from user into a tree of &f_inst structures. These trees are | |
16 | * later interpreted using code in |filter/filter.c|. | |
fe613ecd | 17 | * |
725270cb MM |
18 | * A filter is represented by a tree of &f_inst structures, one structure per |
19 | * "instruction". Each &f_inst contains @code, @aux value which is | |
20 | * usually the data type this instruction operates on and two generic | |
7f0ac737 MM |
21 | * arguments (@a[0], @a[1]). Some instructions contain pointer(s) to other |
22 | * instructions in their (@a[0], @a[1]) fields. | |
2337ade7 | 23 | * |
725270cb MM |
24 | * Filters use a &f_val structure for their data. Each &f_val |
25 | * contains type and value (types are constants prefixed with %T_). Few | |
26 | * of the types are special; %T_RETURN can be or-ed with a type to indicate | |
27 | * that return from a function or from the whole filter should be | |
28 | * forced. Important thing about &f_val's is that they may be copied | |
29 | * with a simple |=|. That's fine for all currently defined types: strings | |
2337ade7 | 30 | * are read-only (and therefore okay), paths are copied for each |
4c5f93d7 PM |
31 | * operation (okay too). |
32 | */ | |
2337ade7 | 33 | |
9a220cab | 34 | #undef LOCAL_DEBUG |
6b9fa320 | 35 | |
23b1539b PM |
36 | #include "nest/bird.h" |
37 | #include "lib/lists.h" | |
38 | #include "lib/resource.h" | |
39 | #include "lib/socket.h" | |
38506f71 | 40 | #include "lib/string.h" |
7f77e250 | 41 | #include "lib/unaligned.h" |
0264ccf6 PT |
42 | #include "lib/net.h" |
43 | #include "lib/ip.h" | |
23b1539b PM |
44 | #include "nest/route.h" |
45 | #include "nest/protocol.h" | |
46 | #include "nest/iface.h" | |
159fa4ce | 47 | #include "nest/attrs.h" |
23b1539b PM |
48 | #include "conf/conf.h" |
49 | #include "filter/filter.h" | |
8bdb05ed | 50 | #include "filter/f-inst.h" |
4f082dfa | 51 | #include "filter/data.h" |
23b1539b | 52 | |
a946317f JMM |
53 | /* Internal filter state, to be allocated on stack when executing filters */ |
54 | struct filter_state { | |
55 | struct rte **rte; | |
56 | struct rta *old_rta; | |
57 | struct ea_list **eattrs; | |
58 | struct linpool *pool; | |
59 | struct buffer buf; | |
60 | int flags; | |
61 | }; | |
62 | ||
4c553c5a | 63 | void (*bt_assert_hook)(int result, const struct f_line_item *assert); |
8f8671bc | 64 | |
a946317f | 65 | static inline void f_cache_eattrs(struct filter_state *fs) |
13c0be19 | 66 | { |
a946317f | 67 | fs->eattrs = &((*fs->rte)->attrs->eattrs); |
13c0be19 JMM |
68 | } |
69 | ||
a946317f | 70 | static inline void f_rte_cow(struct filter_state *fs) |
a03ede64 | 71 | { |
a946317f | 72 | if (!((*fs->rte)->flags & REF_COW)) |
13c0be19 JMM |
73 | return; |
74 | ||
a946317f | 75 | *fs->rte = rte_cow(*fs->rte); |
a03ede64 OZ |
76 | } |
77 | ||
4c5f93d7 | 78 | /* |
b093c328 PM |
79 | * rta_cow - prepare rta for modification by filter |
80 | */ | |
9831e591 | 81 | static void |
a946317f | 82 | f_rta_cow(struct filter_state *fs) |
26c09e1d | 83 | { |
a946317f | 84 | if (!rta_is_cached((*fs->rte)->attrs)) |
8d9eef17 OZ |
85 | return; |
86 | ||
87 | /* Prepare to modify rte */ | |
a946317f | 88 | f_rte_cow(fs); |
8d9eef17 OZ |
89 | |
90 | /* Store old rta to free it later, it stores reference from rte_cow() */ | |
a946317f | 91 | fs->old_rta = (*fs->rte)->attrs; |
8d9eef17 OZ |
92 | |
93 | /* | |
94 | * Get shallow copy of rta. Fields eattrs and nexthops of rta are shared | |
a946317f | 95 | * with fs->old_rta (they will be copied when the cached rta will be obtained |
8d9eef17 OZ |
96 | * at the end of f_run()), also the lock of hostentry is inherited (we |
97 | * suppose hostentry is not changed by filters). | |
98 | */ | |
a946317f | 99 | (*fs->rte)->attrs = rta_do_cow((*fs->rte)->attrs, fs->pool); |
13c0be19 JMM |
100 | |
101 | /* Re-cache the ea_list */ | |
a946317f | 102 | f_cache_eattrs(fs); |
26c09e1d PM |
103 | } |
104 | ||
4b135d09 | 105 | static char * |
4c553c5a | 106 | val_format_str(struct filter_state *fs, struct f_val *v) { |
4b135d09 PT |
107 | buffer b; |
108 | LOG_BUFFER_INIT(b); | |
109 | val_format(v, &b); | |
a946317f | 110 | return lp_strdup(fs->pool, b.start); |
4b135d09 PT |
111 | } |
112 | ||
1123e707 | 113 | static struct tbf rl_runtime_err = TBF_DEFAULT_LOG_LIMITS; |
cb530392 | 114 | |
b093c328 PM |
115 | /** |
116 | * interpret | |
a946317f | 117 | * @fs: filter state |
2e9b2421 | 118 | * @what: filter to interpret |
b093c328 | 119 | * |
4c5f93d7 | 120 | * Interpret given tree of filter instructions. This is core function |
b093c328 | 121 | * of filter system and does all the hard work. |
771ae456 PM |
122 | * |
123 | * Each instruction has 4 fields: code (which is instruction code), | |
124 | * aux (which is extension to instruction code, typically type), | |
125 | * arg1 and arg2 - arguments. Depending on instruction, arguments | |
315f23a0 | 126 | * are either integers, or pointers to instruction trees. Common |
771ae456 PM |
127 | * instructions like +, that have two expressions as arguments use |
128 | * TWOARGS macro to get both of them evaluated. | |
b093c328 | 129 | */ |
7afa1438 | 130 | static enum filter_return |
4c553c5a | 131 | interpret(struct filter_state *fs, const struct f_line *line, struct f_val *val) |
23b1539b | 132 | { |
8bdb05ed MM |
133 | |
134 | #define F_VAL_STACK_MAX 4096 | |
135 | /* Value stack for execution */ | |
136 | struct f_val_stack { | |
137 | uint cnt; /* Current stack size; 0 for empty */ | |
138 | struct f_val val[F_VAL_STACK_MAX]; /* The stack itself */ | |
139 | } vstk; | |
140 | ||
141 | /* The stack itself is intentionally kept as-is for performance reasons. | |
142 | * Do NOT rewrite this to initialization by struct literal. It's slow. | |
143 | */ | |
4c553c5a | 144 | vstk.cnt = 0; |
8bdb05ed MM |
145 | #define F_EXEC_STACK_MAX 4096 |
146 | ||
147 | /* Exception bits */ | |
148 | enum f_exception { | |
149 | FE_RETURN = 0x1, | |
150 | }; | |
23b1539b | 151 | |
8bdb05ed MM |
152 | /* Instruction stack for execution */ |
153 | struct f_exec_stack { | |
154 | struct { | |
155 | const struct f_line *line; /* The line that is being executed */ | |
156 | uint pos; /* Instruction index in the line */ | |
157 | uint ventry; /* Value stack depth on entry */ | |
158 | enum f_exception emask; /* Exception mask */ | |
159 | } item[F_EXEC_STACK_MAX]; | |
160 | uint cnt; /* Current stack size; 0 for empty */ | |
161 | } estk; | |
162 | ||
163 | /* The same as with the value stack. Not resetting the stack for performance reasons. */ | |
4c553c5a | 164 | estk.cnt = 1; |
8bdb05ed | 165 | estk.item[0].line = line; |
4c553c5a | 166 | estk.item[0].pos = 0; |
fc8df41e | 167 | |
4c553c5a MM |
168 | #define curline estk.item[estk.cnt-1] |
169 | ||
ea4f55e3 MM |
170 | #if DEBUGGING |
171 | debug("Interpreting line."); | |
172 | f_dump_line(line, 1); | |
173 | #endif | |
174 | ||
4c553c5a MM |
175 | while (estk.cnt > 0) { |
176 | while (curline.pos < curline.line->len) { | |
177 | const struct f_line_item *what = &(curline.line->items[curline.pos++]); | |
7afa1438 | 178 | |
4c553c5a MM |
179 | |
180 | switch (what->fi_code) { | |
181 | #define res vstk.val[vstk.cnt] | |
182 | #define v1 vstk.val[vstk.cnt] | |
183 | #define v2 vstk.val[vstk.cnt + 1] | |
184 | #define v3 vstk.val[vstk.cnt + 2] | |
7afa1438 | 185 | |
aca82639 | 186 | #define runtime(fmt, ...) do { \ |
a8740d6c MM |
187 | if (!(fs->flags & FF_SILENT)) \ |
188 | log_rl(&rl_runtime_err, L_ERR "filters, line %d: " fmt, what->lineno, ##__VA_ARGS__); \ | |
189 | return F_ERROR; \ | |
190 | } while(0) | |
aca82639 | 191 | |
a8740d6c | 192 | #define ACCESS_RTE do { if (!fs->rte) runtime("No route to access"); } while (0) |
a8740d6c | 193 | #define ACCESS_EATTRS do { if (!fs->eattrs) f_cache_eattrs(fs); } while (0) |
aca82639 | 194 | |
967b88d9 | 195 | #include "filter/f-inst-interpret.c" |
7afa1438 | 196 | #undef res |
4c553c5a MM |
197 | #undef v1 |
198 | #undef v2 | |
199 | #undef v3 | |
aca82639 | 200 | #undef runtime |
aca82639 JMM |
201 | #undef ACCESS_RTE |
202 | #undef ACCESS_EATTRS | |
4c553c5a | 203 | } |
a8740d6c | 204 | } |
4c553c5a | 205 | estk.cnt--; |
a8740d6c | 206 | } |
23b1539b | 207 | |
4c553c5a MM |
208 | switch (vstk.cnt) { |
209 | case 0: | |
210 | if (val) { | |
211 | log_rl(&rl_runtime_err, L_ERR "filters: No value left on stack"); | |
212 | return F_ERROR; | |
213 | } | |
214 | return F_NOP; | |
215 | case 1: | |
216 | if (val) { | |
217 | *val = vstk.val[0]; | |
218 | return F_NOP; | |
219 | } | |
220 | /* fallthrough */ | |
221 | default: | |
222 | log_rl(&rl_runtime_err, L_ERR "Too many items left on stack: %u", vstk.cnt); | |
223 | return F_ERROR; | |
224 | } | |
225 | } | |
0ec6b5ec | 226 | |
9a4037d4 | 227 | |
ff95080f | 228 | /** |
a03ede64 OZ |
229 | * f_run - run a filter for a route |
230 | * @filter: filter to run | |
231 | * @rte: route being filtered, may be modified | |
ff95080f | 232 | * @tmp_pool: all filter allocations go from this pool |
4c5f93d7 | 233 | * @flags: flags |
a03ede64 OZ |
234 | * |
235 | * If filter needs to modify the route, there are several | |
236 | * posibilities. @rte might be read-only (with REF_COW flag), in that | |
237 | * case rw copy is obtained by rte_cow() and @rte is replaced. If | |
238 | * @rte is originally rw, it may be directly modified (and it is never | |
239 | * copied). | |
240 | * | |
241 | * The returned rte may reuse the (possibly cached, cloned) rta, or | |
242 | * (if rta was modificied) contains a modified uncached rta, which | |
243 | * uses parts allocated from @tmp_pool and parts shared from original | |
244 | * rta. There is one exception - if @rte is rw but contains a cached | |
245 | * rta and that is modified, rta in returned rte is also cached. | |
246 | * | |
247 | * Ownership of cached rtas is consistent with rte, i.e. | |
248 | * if a new rte is returned, it has its own clone of cached rta | |
249 | * (and cached rta of read-only source rte is intact), if rte is | |
250 | * modified in place, old cached rta is possibly freed. | |
ff95080f | 251 | */ |
7afa1438 | 252 | enum filter_return |
4c553c5a | 253 | f_run(const struct filter *filter, struct rte **rte, struct linpool *tmp_pool, int flags) |
23b1539b | 254 | { |
36da2857 OZ |
255 | if (filter == FILTER_ACCEPT) |
256 | return F_ACCEPT; | |
257 | ||
258 | if (filter == FILTER_REJECT) | |
259 | return F_REJECT; | |
260 | ||
a03ede64 | 261 | int rte_cow = ((*rte)->flags & REF_COW); |
6b9fa320 | 262 | DBG( "Running filter `%s'...", filter->name ); |
23b1539b | 263 | |
a946317f JMM |
264 | struct filter_state fs = { |
265 | .rte = rte, | |
266 | .pool = tmp_pool, | |
267 | .flags = flags, | |
268 | }; | |
0d1b3c4c | 269 | |
a946317f | 270 | LOG_BUFFER_INIT(fs.buf); |
0e175f9f | 271 | |
4c553c5a | 272 | enum filter_return fret = interpret(&fs, filter->root, NULL); |
a03ede64 | 273 | |
a946317f | 274 | if (fs.old_rta) { |
a03ede64 | 275 | /* |
a946317f | 276 | * Cached rta was modified and fs->rte contains now an uncached one, |
a03ede64 | 277 | * sharing some part with the cached one. The cached rta should |
a946317f | 278 | * be freed (if rte was originally COW, fs->old_rta is a clone |
a03ede64 OZ |
279 | * obtained during rte_cow()). |
280 | * | |
281 | * This also implements the exception mentioned in f_run() | |
282 | * description. The reason for this is that rta reuses parts of | |
a946317f | 283 | * fs->old_rta, and these may be freed during rta_free(fs->old_rta). |
a03ede64 OZ |
284 | * This is not the problem if rte was COW, because original rte |
285 | * also holds the same rta. | |
286 | */ | |
287 | if (!rte_cow) | |
a946317f | 288 | (*fs.rte)->attrs = rta_lookup((*fs.rte)->attrs); |
a03ede64 | 289 | |
a946317f | 290 | rta_free(fs.old_rta); |
a03ede64 OZ |
291 | } |
292 | ||
0d1b3c4c | 293 | |
7afa1438 | 294 | if (fret < F_ACCEPT) { |
a946317f | 295 | if (!(fs.flags & FF_SILENT)) |
b9405791 | 296 | log_rl(&rl_runtime_err, L_ERR "Filter %s did not return accept nor reject. Make up your mind", filter->name); |
23b1539b | 297 | return F_ERROR; |
0b1cad81 | 298 | } |
52e030e1 | 299 | DBG( "done (%u)\n", res.val.i ); |
7afa1438 | 300 | return fret; |
23b1539b PM |
301 | } |
302 | ||
1321e12a OZ |
303 | /* TODO: perhaps we could integrate f_eval(), f_eval_rte() and f_run() */ |
304 | ||
7afa1438 | 305 | enum filter_return |
4c553c5a | 306 | f_eval_rte(const struct f_line *expr, struct rte **rte, struct linpool *tmp_pool) |
1321e12a | 307 | { |
1321e12a | 308 | |
a946317f JMM |
309 | struct filter_state fs = { |
310 | .rte = rte, | |
311 | .pool = tmp_pool, | |
312 | }; | |
1321e12a | 313 | |
a946317f | 314 | LOG_BUFFER_INIT(fs.buf); |
1321e12a OZ |
315 | |
316 | /* Note that in this function we assume that rte->attrs is private / uncached */ | |
4c553c5a | 317 | return interpret(&fs, expr, NULL); |
1321e12a OZ |
318 | } |
319 | ||
7afa1438 | 320 | enum filter_return |
4c553c5a | 321 | f_eval(const struct f_line *expr, struct linpool *tmp_pool, struct f_val *pres) |
1c20608e | 322 | { |
a946317f JMM |
323 | struct filter_state fs = { |
324 | .pool = tmp_pool, | |
325 | }; | |
0d1b3c4c | 326 | |
a946317f | 327 | LOG_BUFFER_INIT(fs.buf); |
0e175f9f | 328 | |
4c553c5a | 329 | enum filter_return fret = interpret(&fs, expr, pres); |
fc8df41e | 330 | return fret; |
508d9360 | 331 | } |
0d1b3c4c | 332 | |
52e030e1 | 333 | uint |
4c553c5a | 334 | f_eval_int(const struct f_line *expr) |
508d9360 OZ |
335 | { |
336 | /* Called independently in parse-time to eval expressions */ | |
fc8df41e JMM |
337 | struct filter_state fs = { |
338 | .pool = cfg_mem, | |
fc8df41e JMM |
339 | }; |
340 | ||
4c553c5a MM |
341 | struct f_val val; |
342 | ||
fc8df41e JMM |
343 | LOG_BUFFER_INIT(fs.buf); |
344 | ||
4c553c5a | 345 | if (interpret(&fs, expr, &val) > F_RETURN) |
7afa1438 | 346 | cf_error("Runtime error while evaluating expression"); |
0d1b3c4c | 347 | |
4c553c5a | 348 | if (val.type != T_INT) |
b1c9d871 | 349 | cf_error("Integer expression expected"); |
508d9360 | 350 | |
4c553c5a | 351 | return val.val.i; |
b1c9d871 | 352 | } |
1c20608e | 353 | |
52893045 MM |
354 | enum filter_return |
355 | f_eval_buf(const struct f_line *expr, struct linpool *tmp_pool, buffer *buf) | |
356 | { | |
357 | struct f_val val; | |
358 | enum filter_return fret = f_eval(expr, tmp_pool, &val); | |
359 | if (fret > F_RETURN) | |
360 | val_format(&val, buf); | |
361 | return fret; | |
362 | } | |
363 | ||
ff95080f PM |
364 | /** |
365 | * filter_same - compare two filters | |
366 | * @new: first filter to be compared | |
0b39b1cb | 367 | * @old: second filter to be compared |
ff95080f PM |
368 | * |
369 | * Returns 1 in case filters are same, otherwise 0. If there are | |
370 | * underlying bugs, it will rather say 0 on same filters than say | |
371 | * 1 on different. | |
372 | */ | |
30a6108c | 373 | int |
0b39b1cb | 374 | filter_same(const struct filter *new, const struct filter *old) |
30a6108c | 375 | { |
81ce667b MM |
376 | if (old == new) /* Handle FILTER_ACCEPT and FILTER_REJECT */ |
377 | return 1; | |
378 | if (old == FILTER_ACCEPT || old == FILTER_REJECT || | |
379 | new == FILTER_ACCEPT || new == FILTER_REJECT) | |
380 | return 0; | |
4c553c5a | 381 | return f_same(new->root, old->root); |
30a6108c | 382 | } |