]>
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 | * |
0da06b71 MM |
18 | * A filter is represented by a tree of &f_inst structures, later translated |
19 | * into lists called &f_line. All the instructions are defined and documented | |
20 | * in |filter/f-inst.c| definition file. | |
2337ade7 | 21 | * |
725270cb | 22 | * Filters use a &f_val structure for their data. Each &f_val |
0da06b71 MM |
23 | * contains type and value (types are constants prefixed with %T_). |
24 | * Look into |filter/data.h| for more information and appropriate calls. | |
4c5f93d7 | 25 | */ |
2337ade7 | 26 | |
9a220cab | 27 | #undef LOCAL_DEBUG |
6b9fa320 | 28 | |
23b1539b PM |
29 | #include "nest/bird.h" |
30 | #include "lib/lists.h" | |
31 | #include "lib/resource.h" | |
32 | #include "lib/socket.h" | |
38506f71 | 33 | #include "lib/string.h" |
7f77e250 | 34 | #include "lib/unaligned.h" |
0264ccf6 | 35 | #include "lib/ip.h" |
ff2ca10c OZ |
36 | #include "lib/net.h" |
37 | #include "lib/flowspec.h" | |
23b1539b PM |
38 | #include "nest/route.h" |
39 | #include "nest/protocol.h" | |
40 | #include "nest/iface.h" | |
159fa4ce | 41 | #include "nest/attrs.h" |
23b1539b PM |
42 | #include "conf/conf.h" |
43 | #include "filter/filter.h" | |
8bdb05ed | 44 | #include "filter/f-inst.h" |
4f082dfa | 45 | #include "filter/data.h" |
23b1539b | 46 | |
1757a6fc | 47 | |
a84b8b6e MM |
48 | /* Exception bits */ |
49 | enum f_exception { | |
50 | FE_RETURN = 0x1, | |
51 | }; | |
52 | ||
c73343de MM |
53 | |
54 | struct filter_stack { | |
55 | /* Value stack for execution */ | |
56 | #define F_VAL_STACK_MAX 4096 | |
57 | uint vcnt; /* Current value stack size; 0 for empty */ | |
58 | uint ecnt; /* Current execute stack size; 0 for empty */ | |
59 | ||
60 | struct f_val vstk[F_VAL_STACK_MAX]; /* The stack itself */ | |
61 | ||
62 | /* Instruction stack for execution */ | |
63 | #define F_EXEC_STACK_MAX 4096 | |
64 | struct { | |
65 | const struct f_line *line; /* The line that is being executed */ | |
66 | uint pos; /* Instruction index in the line */ | |
67 | uint ventry; /* Value stack depth on entry */ | |
68 | uint vbase; /* Where to index variable positions from */ | |
69 | enum f_exception emask; /* Exception mask */ | |
70 | } estk[F_EXEC_STACK_MAX]; | |
1757a6fc MM |
71 | }; |
72 | ||
a946317f | 73 | /* Internal filter state, to be allocated on stack when executing filters */ |
6479e403 | 74 | struct filter_state { |
1757a6fc | 75 | /* Stacks needed for execution */ |
c73343de | 76 | struct filter_stack *stack; |
1757a6fc | 77 | |
20c6ea70 | 78 | /* The route we are processing. This may be NULL to indicate no route available. */ |
a946317f | 79 | struct rte **rte; |
20c6ea70 MM |
80 | |
81 | /* The old rta to be freed after filters are done. */ | |
a946317f | 82 | struct rta *old_rta; |
20c6ea70 MM |
83 | |
84 | /* Cached pointer to ea_list */ | |
a946317f | 85 | struct ea_list **eattrs; |
aa6c5f4d MM |
86 | |
87 | /* Linpool for adata allocation */ | |
a946317f | 88 | struct linpool *pool; |
aa6c5f4d MM |
89 | |
90 | /* Buffer for log output */ | |
a946317f | 91 | struct buffer buf; |
aa6c5f4d | 92 | |
977b82fb IP |
93 | /* Pointers to routes we are aggregating */ |
94 | const struct f_val *val; | |
95 | ||
aa6c5f4d | 96 | /* Filter execution flags */ |
a946317f | 97 | int flags; |
6479e403 MM |
98 | }; |
99 | ||
6479e403 | 100 | _Thread_local static struct filter_state filter_state; |
c73343de | 101 | _Thread_local static struct filter_stack filter_stack; |
a946317f | 102 | |
4c553c5a | 103 | void (*bt_assert_hook)(int result, const struct f_line_item *assert); |
8f8671bc | 104 | |
a946317f | 105 | static inline void f_cache_eattrs(struct filter_state *fs) |
13c0be19 | 106 | { |
a946317f | 107 | fs->eattrs = &((*fs->rte)->attrs->eattrs); |
13c0be19 MM |
108 | } |
109 | ||
a946317f | 110 | static inline void f_rte_cow(struct filter_state *fs) |
a03ede64 | 111 | { |
a946317f | 112 | if (!((*fs->rte)->flags & REF_COW)) |
13c0be19 MM |
113 | return; |
114 | ||
a946317f | 115 | *fs->rte = rte_cow(*fs->rte); |
a03ede64 OZ |
116 | } |
117 | ||
4c5f93d7 | 118 | /* |
b093c328 PM |
119 | * rta_cow - prepare rta for modification by filter |
120 | */ | |
9831e591 | 121 | static void |
a946317f | 122 | f_rta_cow(struct filter_state *fs) |
26c09e1d | 123 | { |
a946317f | 124 | if (!rta_is_cached((*fs->rte)->attrs)) |
8d9eef17 OZ |
125 | return; |
126 | ||
127 | /* Prepare to modify rte */ | |
a946317f | 128 | f_rte_cow(fs); |
8d9eef17 OZ |
129 | |
130 | /* Store old rta to free it later, it stores reference from rte_cow() */ | |
a946317f | 131 | fs->old_rta = (*fs->rte)->attrs; |
8d9eef17 OZ |
132 | |
133 | /* | |
134 | * Get shallow copy of rta. Fields eattrs and nexthops of rta are shared | |
a946317f | 135 | * with fs->old_rta (they will be copied when the cached rta will be obtained |
8d9eef17 OZ |
136 | * at the end of f_run()), also the lock of hostentry is inherited (we |
137 | * suppose hostentry is not changed by filters). | |
138 | */ | |
a946317f | 139 | (*fs->rte)->attrs = rta_do_cow((*fs->rte)->attrs, fs->pool); |
13c0be19 MM |
140 | |
141 | /* Re-cache the ea_list */ | |
a946317f | 142 | f_cache_eattrs(fs); |
26c09e1d PM |
143 | } |
144 | ||
1123e707 | 145 | static struct tbf rl_runtime_err = TBF_DEFAULT_LOG_LIMITS; |
cb530392 | 146 | |
b093c328 PM |
147 | /** |
148 | * interpret | |
a946317f | 149 | * @fs: filter state |
2e9b2421 | 150 | * @what: filter to interpret |
b093c328 | 151 | * |
4c5f93d7 | 152 | * Interpret given tree of filter instructions. This is core function |
b093c328 | 153 | * of filter system and does all the hard work. |
771ae456 PM |
154 | * |
155 | * Each instruction has 4 fields: code (which is instruction code), | |
156 | * aux (which is extension to instruction code, typically type), | |
157 | * arg1 and arg2 - arguments. Depending on instruction, arguments | |
315f23a0 | 158 | * are either integers, or pointers to instruction trees. Common |
771ae456 PM |
159 | * instructions like +, that have two expressions as arguments use |
160 | * TWOARGS macro to get both of them evaluated. | |
b093c328 | 161 | */ |
7afa1438 | 162 | static enum filter_return |
977b82fb | 163 | interpret(struct filter_state *fs, const struct f_line *line, uint argc, const struct f_val *argv, struct f_val *val) |
23b1539b | 164 | { |
96d757c1 | 165 | /* No arguments allowed */ |
977b82fb | 166 | ASSERT_DIE(line->args == argc); |
8bdb05ed | 167 | |
1757a6fc | 168 | /* Initialize the filter stack */ |
c73343de | 169 | struct filter_stack *fstk = fs->stack; |
96d757c1 | 170 | |
977b82fb IP |
171 | /* Set the arguments and top-level variables */ |
172 | fstk->vcnt = line->vars + line->args; | |
173 | memcpy(fstk->vstk, argv, sizeof(struct f_val) * line->args); | |
174 | memset(fstk->vstk + line->args, 0, sizeof(struct f_val) * line->vars); | |
8bdb05ed | 175 | |
977b82fb | 176 | /* The same as with the value stack. Not resetting the stack completely for performance reasons. */ |
1757a6fc | 177 | fstk->ecnt = 1; |
c73343de MM |
178 | fstk->estk[0].line = line; |
179 | fstk->estk[0].pos = 0; | |
fc8df41e | 180 | |
1757a6fc | 181 | #define curline fstk->estk[fstk->ecnt-1] |
21faa54e | 182 | #define prevline fstk->estk[fstk->ecnt-2] |
4c553c5a | 183 | |
1757a6fc | 184 | while (fstk->ecnt > 0) { |
4c553c5a MM |
185 | while (curline.pos < curline.line->len) { |
186 | const struct f_line_item *what = &(curline.line->items[curline.pos++]); | |
7afa1438 | 187 | |
4c553c5a | 188 | switch (what->fi_code) { |
1757a6fc | 189 | #define res fstk->vstk[fstk->vcnt] |
c0999a14 MM |
190 | #define vv(i) fstk->vstk[fstk->vcnt + (i)] |
191 | #define v1 vv(0) | |
192 | #define v2 vv(1) | |
193 | #define v3 vv(2) | |
7afa1438 | 194 | |
a84b8b6e MM |
195 | #define runtime(fmt, ...) do { \ |
196 | if (!(fs->flags & FF_SILENT)) \ | |
197 | log_rl(&rl_runtime_err, L_ERR "filters, line %d: " fmt, what->lineno, ##__VA_ARGS__); \ | |
198 | return F_ERROR; \ | |
199 | } while(0) | |
200 | ||
b40c0f02 MM |
201 | #define falloc(size) lp_alloc(fs->pool, size) |
202 | #define fpool fs->pool | |
203 | ||
a84b8b6e MM |
204 | #define ACCESS_EATTRS do { if (!fs->eattrs) f_cache_eattrs(fs); } while (0) |
205 | ||
d1039926 | 206 | #include "filter/inst-interpret.c" |
7afa1438 | 207 | #undef res |
4c553c5a MM |
208 | #undef v1 |
209 | #undef v2 | |
210 | #undef v3 | |
aca82639 | 211 | #undef runtime |
b40c0f02 MM |
212 | #undef falloc |
213 | #undef fpool | |
aca82639 | 214 | #undef ACCESS_EATTRS |
4c553c5a | 215 | } |
a8740d6c | 216 | } |
bfa15a64 | 217 | |
96d757c1 | 218 | /* End of current line. Drop local variables before exiting. */ |
a2527ee5 | 219 | fstk->vcnt = curline.ventry + curline.line->results; |
1757a6fc | 220 | fstk->ecnt--; |
a8740d6c | 221 | } |
23b1539b | 222 | |
1757a6fc | 223 | if (fstk->vcnt == 0) { |
96d757c1 MM |
224 | if (val) { |
225 | log_rl(&rl_runtime_err, L_ERR "filters: No value left on stack"); | |
4c553c5a | 226 | return F_ERROR; |
96d757c1 MM |
227 | } |
228 | return F_NOP; | |
4c553c5a | 229 | } |
96d757c1 | 230 | |
1757a6fc MM |
231 | if (val && (fstk->vcnt == 1)) { |
232 | *val = fstk->vstk[0]; | |
96d757c1 MM |
233 | return F_NOP; |
234 | } | |
235 | ||
1757a6fc | 236 | log_rl(&rl_runtime_err, L_ERR "Too many items left on stack: %u", fstk->vcnt); |
96d757c1 | 237 | return F_ERROR; |
4c553c5a | 238 | } |
0ec6b5ec | 239 | |
ff95080f | 240 | /** |
a03ede64 OZ |
241 | * f_run - run a filter for a route |
242 | * @filter: filter to run | |
243 | * @rte: route being filtered, may be modified | |
ff95080f | 244 | * @tmp_pool: all filter allocations go from this pool |
4c5f93d7 | 245 | * @flags: flags |
a03ede64 OZ |
246 | * |
247 | * If filter needs to modify the route, there are several | |
248 | * posibilities. @rte might be read-only (with REF_COW flag), in that | |
249 | * case rw copy is obtained by rte_cow() and @rte is replaced. If | |
250 | * @rte is originally rw, it may be directly modified (and it is never | |
251 | * copied). | |
252 | * | |
253 | * The returned rte may reuse the (possibly cached, cloned) rta, or | |
20c6ea70 | 254 | * (if rta was modified) contains a modified uncached rta, which |
a03ede64 OZ |
255 | * uses parts allocated from @tmp_pool and parts shared from original |
256 | * rta. There is one exception - if @rte is rw but contains a cached | |
257 | * rta and that is modified, rta in returned rte is also cached. | |
258 | * | |
259 | * Ownership of cached rtas is consistent with rte, i.e. | |
260 | * if a new rte is returned, it has its own clone of cached rta | |
261 | * (and cached rta of read-only source rte is intact), if rte is | |
262 | * modified in place, old cached rta is possibly freed. | |
ff95080f | 263 | */ |
7afa1438 | 264 | enum filter_return |
4c553c5a | 265 | f_run(const struct filter *filter, struct rte **rte, struct linpool *tmp_pool, int flags) |
23b1539b | 266 | { |
36da2857 OZ |
267 | if (filter == FILTER_ACCEPT) |
268 | return F_ACCEPT; | |
269 | ||
270 | if (filter == FILTER_REJECT) | |
271 | return F_REJECT; | |
272 | ||
977b82fb IP |
273 | return f_run_args(filter, rte, tmp_pool, 0, NULL, flags); |
274 | } | |
275 | ||
276 | enum filter_return | |
277 | f_run_args(const struct filter *filter, struct rte **rte, struct linpool *tmp_pool, uint argc, const struct f_val *argv, int flags) | |
278 | { | |
a03ede64 | 279 | int rte_cow = ((*rte)->flags & REF_COW); |
6b9fa320 | 280 | DBG( "Running filter `%s'...", filter->name ); |
23b1539b | 281 | |
20c6ea70 | 282 | /* Initialize the filter state */ |
3782454e | 283 | filter_state = (struct filter_state) { |
c73343de | 284 | .stack = &filter_stack, |
3782454e MM |
285 | .rte = rte, |
286 | .pool = tmp_pool, | |
287 | .flags = flags, | |
288 | }; | |
0d1b3c4c | 289 | |
20c6ea70 | 290 | LOG_BUFFER_INIT(filter_state.buf); |
0e175f9f | 291 | |
20c6ea70 | 292 | /* Run the interpreter itself */ |
977b82fb | 293 | enum filter_return fret = interpret(&filter_state, filter->root, argc, argv, NULL); |
a03ede64 | 294 | |
20c6ea70 | 295 | if (filter_state.old_rta) { |
a03ede64 | 296 | /* |
20c6ea70 | 297 | * Cached rta was modified and filter_state->rte contains now an uncached one, |
a03ede64 | 298 | * sharing some part with the cached one. The cached rta should |
20c6ea70 | 299 | * be freed (if rte was originally COW, filter_state->old_rta is a clone |
a03ede64 OZ |
300 | * obtained during rte_cow()). |
301 | * | |
302 | * This also implements the exception mentioned in f_run() | |
303 | * description. The reason for this is that rta reuses parts of | |
20c6ea70 | 304 | * filter_state->old_rta, and these may be freed during rta_free(filter_state->old_rta). |
a03ede64 OZ |
305 | * This is not the problem if rte was COW, because original rte |
306 | * also holds the same rta. | |
307 | */ | |
20c6ea70 MM |
308 | if (!rte_cow) { |
309 | /* Cache the new attrs */ | |
310 | (*filter_state.rte)->attrs = rta_lookup((*filter_state.rte)->attrs); | |
a03ede64 | 311 | |
20c6ea70 MM |
312 | /* Drop cached ea_list pointer */ |
313 | filter_state.eattrs = NULL; | |
314 | } | |
a03ede64 | 315 | |
20c6ea70 MM |
316 | /* Uncache the old attrs and drop the pointer as it is invalid now. */ |
317 | rta_free(filter_state.old_rta); | |
318 | filter_state.old_rta = NULL; | |
319 | } | |
0d1b3c4c | 320 | |
20c6ea70 | 321 | /* Process the filter output, log it and return */ |
7afa1438 | 322 | if (fret < F_ACCEPT) { |
20c6ea70 | 323 | if (!(filter_state.flags & FF_SILENT)) |
f249d0b8 | 324 | log_rl(&rl_runtime_err, L_ERR "Filter %s did not return accept nor reject. Make up your mind", filter_name(filter)); |
23b1539b | 325 | return F_ERROR; |
0b1cad81 | 326 | } |
52e030e1 | 327 | DBG( "done (%u)\n", res.val.i ); |
7afa1438 | 328 | return fret; |
23b1539b PM |
329 | } |
330 | ||
20c6ea70 | 331 | /** |
bfa15a64 | 332 | * f_eval_rte - run a filter line for an uncached route |
20c6ea70 MM |
333 | * @expr: filter line to run |
334 | * @rte: route being filtered, may be modified | |
335 | * @tmp_pool: all filter allocations go from this pool | |
336 | * | |
337 | * This specific filter entry point runs the given filter line | |
338 | * (which must not have any arguments) on the given route. | |
339 | * | |
340 | * The route MUST NOT have REF_COW set and its attributes MUST NOT | |
341 | * be cached by rta_lookup(). | |
342 | */ | |
1321e12a | 343 | |
7afa1438 | 344 | enum filter_return |
977b82fb | 345 | f_eval_rte(const struct f_line *expr, struct rte **rte, struct linpool *tmp_pool, uint argc, const struct f_val *argv, struct f_val *pres) |
1321e12a | 346 | { |
3782454e | 347 | filter_state = (struct filter_state) { |
c73343de | 348 | .stack = &filter_stack, |
3782454e MM |
349 | .rte = rte, |
350 | .pool = tmp_pool, | |
351 | }; | |
1321e12a | 352 | |
20c6ea70 MM |
353 | LOG_BUFFER_INIT(filter_state.buf); |
354 | ||
977b82fb | 355 | return interpret(&filter_state, expr, argc, argv, pres); |
1321e12a OZ |
356 | } |
357 | ||
20c6ea70 | 358 | /* |
bfa15a64 | 359 | * f_eval - get a value of a term |
20c6ea70 MM |
360 | * @expr: filter line containing the term |
361 | * @tmp_pool: long data may get allocated from this pool | |
362 | * @pres: here the output will be stored | |
363 | */ | |
7afa1438 | 364 | enum filter_return |
4c553c5a | 365 | f_eval(const struct f_line *expr, struct linpool *tmp_pool, struct f_val *pres) |
1c20608e | 366 | { |
3782454e | 367 | filter_state = (struct filter_state) { |
c73343de | 368 | .stack = &filter_stack, |
3782454e MM |
369 | .pool = tmp_pool, |
370 | }; | |
0d1b3c4c | 371 | |
20c6ea70 | 372 | LOG_BUFFER_INIT(filter_state.buf); |
0e175f9f | 373 | |
977b82fb | 374 | enum filter_return fret = interpret(&filter_state, expr, 0, NULL, pres); |
fc8df41e | 375 | return fret; |
508d9360 | 376 | } |
0d1b3c4c | 377 | |
20c6ea70 | 378 | /* |
0dbcc927 | 379 | * cf_eval - evaluate a value of a term and check its type |
20c6ea70 MM |
380 | * Called internally from the config parser, uses its internal memory pool |
381 | * for allocations. Do not call in other cases. | |
382 | */ | |
0dbcc927 AZ |
383 | struct f_val |
384 | cf_eval(const struct f_inst *inst, int type) | |
508d9360 | 385 | { |
4c553c5a MM |
386 | struct f_val val; |
387 | ||
0dbcc927 | 388 | if (f_eval(f_linearize(inst, 1), cfg_mem, &val) > F_RETURN) |
547be53b | 389 | cf_error("Runtime error while evaluating expression; see log for details"); |
0d1b3c4c | 390 | |
0dbcc927 AZ |
391 | if (type != T_VOID && val.type != type) |
392 | cf_error("Expression of type %s expected", f_type_name(type)); | |
508d9360 | 393 | |
0dbcc927 | 394 | return val; |
b1c9d871 | 395 | } |
1c20608e | 396 | |
20c6ea70 | 397 | /* |
bfa15a64 | 398 | * f_eval_buf - get a value of a term and print it to the supplied buffer |
20c6ea70 | 399 | */ |
52893045 MM |
400 | enum filter_return |
401 | f_eval_buf(const struct f_line *expr, struct linpool *tmp_pool, buffer *buf) | |
402 | { | |
403 | struct f_val val; | |
404 | enum filter_return fret = f_eval(expr, tmp_pool, &val); | |
ea0917bc | 405 | if (fret <= F_RETURN) |
52893045 MM |
406 | val_format(&val, buf); |
407 | return fret; | |
408 | } | |
409 | ||
ff95080f PM |
410 | /** |
411 | * filter_same - compare two filters | |
412 | * @new: first filter to be compared | |
0b39b1cb | 413 | * @old: second filter to be compared |
ff95080f PM |
414 | * |
415 | * Returns 1 in case filters are same, otherwise 0. If there are | |
416 | * underlying bugs, it will rather say 0 on same filters than say | |
417 | * 1 on different. | |
418 | */ | |
30a6108c | 419 | int |
0b39b1cb | 420 | filter_same(const struct filter *new, const struct filter *old) |
30a6108c | 421 | { |
81ce667b MM |
422 | if (old == new) /* Handle FILTER_ACCEPT and FILTER_REJECT */ |
423 | return 1; | |
424 | if (old == FILTER_ACCEPT || old == FILTER_REJECT || | |
425 | new == FILTER_ACCEPT || new == FILTER_REJECT) | |
426 | return 0; | |
f249d0b8 MM |
427 | |
428 | if ((!old->sym) && (!new->sym)) | |
429 | return f_same(new->root, old->root); | |
430 | ||
431 | if ((!old->sym) || (!new->sym)) | |
432 | return 0; | |
433 | ||
434 | if (strcmp(old->sym->name, new->sym->name)) | |
435 | return 0; | |
436 | ||
437 | return new->sym->flags & SYM_FLAG_SAME; | |
438 | } | |
439 | ||
440 | /** | |
441 | * filter_commit - do filter comparisons on all the named functions and filters | |
442 | */ | |
443 | void | |
87c82334 | 444 | filter_commit(struct config *new, struct config *old) |
f249d0b8 MM |
445 | { |
446 | if (!old) | |
447 | return; | |
448 | ||
449 | struct symbol *sym, *osym; | |
450 | WALK_LIST(sym, new->symbols) | |
451 | switch (sym->class) { | |
452 | case SYM_FUNCTION: | |
453 | if ((osym = cf_find_symbol(old, sym->name)) && | |
454 | (osym->class == SYM_FUNCTION) && | |
455 | f_same(sym->function, osym->function)) | |
456 | sym->flags |= SYM_FLAG_SAME; | |
457 | else | |
458 | sym->flags &= ~SYM_FLAG_SAME; | |
459 | break; | |
460 | ||
461 | case SYM_FILTER: | |
462 | if ((osym = cf_find_symbol(old, sym->name)) && | |
463 | (osym->class == SYM_FILTER) && | |
464 | f_same(sym->filter->root, osym->filter->root)) | |
465 | sym->flags |= SYM_FLAG_SAME; | |
466 | else | |
467 | sym->flags &= ~SYM_FLAG_SAME; | |
468 | break; | |
469 | } | |
30a6108c | 470 | } |
84ac62d3 | 471 | |
da8a2327 | 472 | void filters_dump_all(struct dump_request *dreq) |
84ac62d3 MM |
473 | { |
474 | struct symbol *sym; | |
475 | WALK_LIST(sym, config->symbols) { | |
476 | switch (sym->class) { | |
477 | case SYM_FILTER: | |
da8a2327 MM |
478 | RDUMP("Named filter %s:\n", sym->name); |
479 | f_dump_line(dreq, sym->filter->root, 1); | |
84ac62d3 MM |
480 | break; |
481 | case SYM_FUNCTION: | |
da8a2327 MM |
482 | RDUMP("Function %s:\n", sym->name); |
483 | f_dump_line(dreq, sym->function, 1); | |
84ac62d3 MM |
484 | break; |
485 | case SYM_PROTO: | |
486 | { | |
da8a2327 | 487 | RDUMP("Protocol %s:\n", sym->name); |
84ac62d3 MM |
488 | struct channel *c; |
489 | WALK_LIST(c, sym->proto->proto->channels) { | |
da8a2327 | 490 | RDUMP(" Channel %s (%s) IMPORT", c->name, net_label[c->net_type]); |
84ac62d3 | 491 | if (c->in_filter == FILTER_ACCEPT) |
da8a2327 | 492 | RDUMP(" ALL\n"); |
84ac62d3 | 493 | else if (c->in_filter == FILTER_REJECT) |
da8a2327 | 494 | RDUMP(" NONE\n"); |
84ac62d3 | 495 | else if (c->in_filter == FILTER_UNDEF) |
da8a2327 | 496 | RDUMP(" UNDEF\n"); |
84ac62d3 MM |
497 | else if (c->in_filter->sym) { |
498 | ASSERT(c->in_filter->sym->filter == c->in_filter); | |
da8a2327 | 499 | RDUMP(" named filter %s\n", c->in_filter->sym->name); |
84ac62d3 | 500 | } else { |
da8a2327 MM |
501 | RDUMP("\n"); |
502 | f_dump_line(dreq, c->in_filter->root, 2); | |
84ac62d3 MM |
503 | } |
504 | } | |
505 | } | |
506 | } | |
507 | } | |
508 | } |