]>
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" | |
50 | ||
38506f71 PM |
51 | #define CMP_ERROR 999 |
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 OZ |
64 | |
65 | /* Special undef value for paths and clists */ | |
66 | static inline int | |
67 | undef_value(struct f_val v) | |
68 | { | |
69 | return ((v.type == T_PATH) || (v.type == T_CLIST) || | |
70 | (v.type == T_ECLIST) || (v.type == T_LCLIST)) && | |
4c553c5a | 71 | (v.val.ad == &null_adata); |
8f8671bc OZ |
72 | } |
73 | ||
4c553c5a MM |
74 | const struct f_val f_const_empty_path = { |
75 | .type = T_PATH, | |
76 | .val.ad = &null_adata, | |
77 | }, f_const_empty_clist = { | |
78 | .type = T_CLIST, | |
79 | .val.ad = &null_adata, | |
80 | }, f_const_empty_eclist = { | |
81 | .type = T_ECLIST, | |
82 | .val.ad = &null_adata, | |
83 | }, f_const_empty_lclist = { | |
84 | .type = T_LCLIST, | |
85 | .val.ad = &null_adata, | |
86 | }; | |
87 | ||
9831e591 | 88 | static struct adata * |
42a0c054 | 89 | adata_empty(struct linpool *pool, int l) |
ad9074e9 | 90 | { |
42a0c054 OZ |
91 | struct adata *res = lp_alloc(pool, sizeof(struct adata) + l); |
92 | res->length = l; | |
ad9074e9 PM |
93 | return res; |
94 | } | |
95 | ||
11cb6202 | 96 | static void |
4c553c5a | 97 | pm_format(const struct f_path_mask *p, buffer *buf) |
11cb6202 | 98 | { |
0e175f9f | 99 | buffer_puts(buf, "[= "); |
11cb6202 | 100 | |
4c553c5a | 101 | for (uint i=0; i<p->len; i++) |
0e175f9f | 102 | { |
4c553c5a | 103 | switch(p->item[i].kind) |
11cb6202 | 104 | { |
0e175f9f | 105 | case PM_ASN: |
4c553c5a | 106 | buffer_print(buf, "%u ", p->item[i].asn); |
0e175f9f | 107 | break; |
92a72a4c | 108 | |
0e175f9f OZ |
109 | case PM_QUESTION: |
110 | buffer_puts(buf, "? "); | |
111 | break; | |
92a72a4c | 112 | |
0e175f9f OZ |
113 | case PM_ASTERISK: |
114 | buffer_puts(buf, "* "); | |
115 | break; | |
11cb6202 | 116 | |
a0fe1944 | 117 | case PM_ASN_RANGE: |
4c553c5a | 118 | buffer_print(buf, "%u..%u ", p->item[i].from, p->item[i].to); |
a0fe1944 OF |
119 | break; |
120 | ||
0e175f9f | 121 | case PM_ASN_EXPR: |
e8bc64e3 | 122 | ASSERT(0); |
11cb6202 OZ |
123 | } |
124 | ||
0e175f9f OZ |
125 | } |
126 | ||
127 | buffer_puts(buf, "=]"); | |
11cb6202 OZ |
128 | } |
129 | ||
4c553c5a MM |
130 | static inline int val_is_ip4(const struct f_val *v) |
131 | { return (v->type == T_IP) && ipa_is_ip4(v->val.ip); } | |
42a0c054 | 132 | |
66dbdbd9 OZ |
133 | static inline int |
134 | lcomm_cmp(lcomm v1, lcomm v2) | |
135 | { | |
136 | if (v1.asn != v2.asn) | |
137 | return (v1.asn > v2.asn) ? 1 : -1; | |
138 | if (v1.ldp1 != v2.ldp1) | |
139 | return (v1.ldp1 > v2.ldp1) ? 1 : -1; | |
140 | if (v1.ldp2 != v2.ldp2) | |
141 | return (v1.ldp2 > v2.ldp2) ? 1 : -1; | |
142 | return 0; | |
143 | } | |
144 | ||
8dcf2544 | 145 | /** |
3e82b32d | 146 | * val_compare - compare two values |
8dcf2544 PM |
147 | * @v1: first value |
148 | * @v2: second value | |
149 | * | |
28a10f84 OZ |
150 | * Compares two values and returns -1, 0, 1 on <, =, > or CMP_ERROR on |
151 | * error. Tree module relies on this giving consistent results so | |
152 | * that it can be used for building balanced trees. | |
b093c328 | 153 | */ |
38506f71 | 154 | int |
4c553c5a | 155 | val_compare(const struct f_val *v1, const struct f_val *v2) |
38506f71 | 156 | { |
4c553c5a MM |
157 | if (v1->type != v2->type) { |
158 | if (v1->type == T_VOID) /* Hack for else */ | |
70c57805 | 159 | return -1; |
4c553c5a | 160 | if (v2->type == T_VOID) |
70c57805 OZ |
161 | return 1; |
162 | ||
126683fe | 163 | /* IP->Quad implicit conversion */ |
4c553c5a MM |
164 | if ((v1->type == T_QUAD) && val_is_ip4(v2)) |
165 | return uint_cmp(v1->val.i, ipa_to_u32(v2->val.ip)); | |
166 | if (val_is_ip4(v1) && (v2->type == T_QUAD)) | |
167 | return uint_cmp(ipa_to_u32(v1->val.ip), v2->val.i); | |
126683fe | 168 | |
f71bded6 | 169 | debug( "Types do not match in val_compare\n" ); |
7db7b7db | 170 | return CMP_ERROR; |
f71bded6 | 171 | } |
28a10f84 | 172 | |
4c553c5a | 173 | switch (v1->type) { |
28a10f84 OZ |
174 | case T_VOID: |
175 | return 0; | |
f4536657 | 176 | case T_ENUM: |
a6c9f064 OF |
177 | case T_INT: |
178 | case T_BOOL: | |
d3dd620b | 179 | case T_PAIR: |
126683fe | 180 | case T_QUAD: |
4c553c5a | 181 | return uint_cmp(v1->val.i, v2->val.i); |
42a0c054 | 182 | case T_EC: |
8c9986d3 | 183 | case T_RD: |
4c553c5a | 184 | return u64_cmp(v1->val.ec, v2->val.ec); |
66dbdbd9 | 185 | case T_LC: |
4c553c5a | 186 | return lcomm_cmp(v1->val.lc, v2->val.lc); |
43fc099b | 187 | case T_IP: |
4c553c5a | 188 | return ipa_compare(v1->val.ip, v2->val.ip); |
5e173e9f | 189 | case T_NET: |
4c553c5a | 190 | return net_compare(v1->val.net, v2->val.net); |
e29fa06e | 191 | case T_STRING: |
4c553c5a | 192 | return strcmp(v1->val.s, v2->val.s); |
3076b5ae MM |
193 | default: |
194 | return CMP_ERROR; | |
38506f71 PM |
195 | } |
196 | } | |
197 | ||
28a10f84 | 198 | static int |
4c553c5a | 199 | pm_same(const struct f_path_mask *m1, const struct f_path_mask *m2) |
dfd48621 | 200 | { |
4c553c5a MM |
201 | if (m1->len != m2->len) |
202 | ||
203 | for (uint i=0; i<m1->len; i++) | |
28a10f84 | 204 | { |
4c553c5a | 205 | if (m1->item[i].kind != m2->item[i].kind) |
28a10f84 OZ |
206 | return 0; |
207 | ||
4c553c5a MM |
208 | switch (m1->item[i].kind) { |
209 | case PM_ASN: | |
210 | if (m1->item[i].asn != m2->item[i].asn) | |
211 | return 0; | |
212 | break; | |
213 | case PM_ASN_EXPR: | |
214 | if (!f_same(m1->item[i].expr, m2->item[i].expr)) | |
215 | return 0; | |
216 | break; | |
217 | case PM_ASN_RANGE: | |
218 | if (m1->item[i].from != m2->item[i].from) | |
219 | return 0; | |
220 | if (m1->item[i].to != m2->item[i].to) | |
221 | return 0; | |
222 | break; | |
122deb6d | 223 | } |
28a10f84 OZ |
224 | } |
225 | ||
4c553c5a | 226 | return 1; |
28a10f84 OZ |
227 | } |
228 | ||
229 | /** | |
230 | * val_same - compare two values | |
231 | * @v1: first value | |
232 | * @v2: second value | |
233 | * | |
234 | * Compares two values and returns 1 if they are same and 0 if not. | |
235 | * Comparison of values of different types is valid and returns 0. | |
236 | */ | |
237 | int | |
4c553c5a | 238 | val_same(const struct f_val *v1, const struct f_val *v2) |
dfd48621 | 239 | { |
28a10f84 OZ |
240 | int rc; |
241 | ||
242 | rc = val_compare(v1, v2); | |
243 | if (rc != CMP_ERROR) | |
244 | return !rc; | |
245 | ||
4c553c5a | 246 | if (v1->type != v2->type) |
28a10f84 OZ |
247 | return 0; |
248 | ||
4c553c5a | 249 | switch (v1->type) { |
28a10f84 | 250 | case T_PATH_MASK: |
4c553c5a | 251 | return pm_same(v1->val.path_mask, v2->val.path_mask); |
28a10f84 OZ |
252 | case T_PATH: |
253 | case T_CLIST: | |
254 | case T_ECLIST: | |
66dbdbd9 | 255 | case T_LCLIST: |
4c553c5a | 256 | return adata_same(v1->val.ad, v2->val.ad); |
28a10f84 | 257 | case T_SET: |
4c553c5a | 258 | return same_tree(v1->val.t, v2->val.t); |
28a10f84 | 259 | case T_PREFIX_SET: |
4c553c5a | 260 | return trie_same(v1->val.ti, v2->val.ti); |
28a10f84 | 261 | default: |
4c553c5a | 262 | bug("Invalid type in val_same(): %x", v1->type); |
28a10f84 | 263 | } |
dfd48621 | 264 | } |
b1a597e0 | 265 | |
ba5c0057 | 266 | static int |
4c553c5a | 267 | clist_set_type(const struct f_tree *set, struct f_val *v) |
ba5c0057 | 268 | { |
e92a4b85 OZ |
269 | switch (set->from.type) |
270 | { | |
ba5c0057 OZ |
271 | case T_PAIR: |
272 | v->type = T_PAIR; | |
273 | return 1; | |
e92a4b85 | 274 | |
ba5c0057 | 275 | case T_QUAD: |
ba5c0057 OZ |
276 | v->type = T_QUAD; |
277 | return 1; | |
e92a4b85 OZ |
278 | |
279 | case T_IP: | |
4c553c5a | 280 | if (val_is_ip4(&(set->from)) && val_is_ip4(&(set->to))) |
e92a4b85 OZ |
281 | { |
282 | v->type = T_QUAD; | |
283 | return 1; | |
284 | } | |
285 | /* Fall through */ | |
ba5c0057 OZ |
286 | default: |
287 | v->type = T_VOID; | |
288 | return 0; | |
289 | } | |
290 | } | |
291 | ||
42a0c054 | 292 | static inline int |
4c553c5a | 293 | eclist_set_type(const struct f_tree *set) |
42a0c054 OZ |
294 | { return set->from.type == T_EC; } |
295 | ||
66dbdbd9 | 296 | static inline int |
4c553c5a | 297 | lclist_set_type(const struct f_tree *set) |
66dbdbd9 OZ |
298 | { return set->from.type == T_LC; } |
299 | ||
ba5c0057 | 300 | static int |
4c553c5a | 301 | clist_match_set(const struct adata *clist, const struct f_tree *set) |
ba5c0057 OZ |
302 | { |
303 | if (!clist) | |
304 | return 0; | |
305 | ||
306 | struct f_val v; | |
307 | if (!clist_set_type(set, &v)) | |
308 | return CMP_ERROR; | |
309 | ||
310 | u32 *l = (u32 *) clist->data; | |
311 | u32 *end = l + clist->length/4; | |
42a0c054 | 312 | |
ba5c0057 OZ |
313 | while (l < end) { |
314 | v.val.i = *l++; | |
4c553c5a | 315 | if (find_tree(set, &v)) |
ba5c0057 OZ |
316 | return 1; |
317 | } | |
318 | return 0; | |
319 | } | |
320 | ||
42a0c054 | 321 | static int |
4c553c5a | 322 | eclist_match_set(const struct adata *list, const struct f_tree *set) |
42a0c054 OZ |
323 | { |
324 | if (!list) | |
325 | return 0; | |
326 | ||
327 | if (!eclist_set_type(set)) | |
328 | return CMP_ERROR; | |
329 | ||
330 | struct f_val v; | |
331 | u32 *l = int_set_get_data(list); | |
332 | int len = int_set_get_size(list); | |
333 | int i; | |
334 | ||
335 | v.type = T_EC; | |
336 | for (i = 0; i < len; i += 2) { | |
337 | v.val.ec = ec_get(l, i); | |
4c553c5a | 338 | if (find_tree(set, &v)) |
42a0c054 OZ |
339 | return 1; |
340 | } | |
341 | ||
342 | return 0; | |
343 | } | |
344 | ||
66dbdbd9 | 345 | static int |
4c553c5a | 346 | lclist_match_set(const struct adata *list, const struct f_tree *set) |
66dbdbd9 OZ |
347 | { |
348 | if (!list) | |
349 | return 0; | |
350 | ||
351 | if (!lclist_set_type(set)) | |
352 | return CMP_ERROR; | |
353 | ||
354 | struct f_val v; | |
355 | u32 *l = int_set_get_data(list); | |
356 | int len = int_set_get_size(list); | |
357 | int i; | |
358 | ||
359 | v.type = T_LC; | |
360 | for (i = 0; i < len; i += 3) { | |
361 | v.val.lc = lc_get(l, i); | |
4c553c5a | 362 | if (find_tree(set, &v)) |
66dbdbd9 OZ |
363 | return 1; |
364 | } | |
365 | ||
366 | return 0; | |
367 | } | |
368 | ||
4c553c5a MM |
369 | static const struct adata * |
370 | clist_filter(struct linpool *pool, const struct adata *list, const struct f_val *set, int pos) | |
ba5c0057 | 371 | { |
0888a737 | 372 | if (!list) |
ba5c0057 OZ |
373 | return NULL; |
374 | ||
4c553c5a | 375 | int tree = (set->type == T_SET); /* 1 -> set is T_SET, 0 -> set is T_CLIST */ |
ba5c0057 | 376 | struct f_val v; |
0888a737 | 377 | if (tree) |
4c553c5a | 378 | clist_set_type(set->val.t, &v); |
0888a737 OZ |
379 | else |
380 | v.type = T_PAIR; | |
ba5c0057 | 381 | |
0888a737 OZ |
382 | int len = int_set_get_size(list); |
383 | u32 *l = int_set_get_data(list); | |
384 | u32 tmp[len]; | |
ba5c0057 | 385 | u32 *k = tmp; |
0888a737 | 386 | u32 *end = l + len; |
ba5c0057 OZ |
387 | |
388 | while (l < end) { | |
389 | v.val.i = *l++; | |
0888a737 | 390 | /* pos && member(val, set) || !pos && !member(val, set), member() depends on tree */ |
4c553c5a | 391 | if ((tree ? !!find_tree(set->val.t, &v) : int_set_contains(set->val.ad, v.val.i)) == pos) |
ba5c0057 OZ |
392 | *k++ = v.val.i; |
393 | } | |
394 | ||
3e236955 | 395 | uint nl = (k - tmp) * sizeof(u32); |
0888a737 OZ |
396 | if (nl == list->length) |
397 | return list; | |
ba5c0057 | 398 | |
42a0c054 OZ |
399 | struct adata *res = adata_empty(pool, nl); |
400 | memcpy(res->data, tmp, nl); | |
401 | return res; | |
402 | } | |
403 | ||
4c553c5a MM |
404 | static const struct adata * |
405 | eclist_filter(struct linpool *pool, const struct adata *list, const struct f_val *set, int pos) | |
42a0c054 OZ |
406 | { |
407 | if (!list) | |
408 | return NULL; | |
409 | ||
4c553c5a | 410 | int tree = (set->type == T_SET); /* 1 -> set is T_SET, 0 -> set is T_CLIST */ |
42a0c054 OZ |
411 | struct f_val v; |
412 | ||
413 | int len = int_set_get_size(list); | |
414 | u32 *l = int_set_get_data(list); | |
415 | u32 tmp[len]; | |
416 | u32 *k = tmp; | |
417 | int i; | |
418 | ||
419 | v.type = T_EC; | |
420 | for (i = 0; i < len; i += 2) { | |
421 | v.val.ec = ec_get(l, i); | |
0888a737 | 422 | /* pos && member(val, set) || !pos && !member(val, set), member() depends on tree */ |
4c553c5a | 423 | if ((tree ? !!find_tree(set->val.t, &v) : ec_set_contains(set->val.ad, v.val.ec)) == pos) { |
42a0c054 OZ |
424 | *k++ = l[i]; |
425 | *k++ = l[i+1]; | |
426 | } | |
427 | } | |
428 | ||
3e236955 | 429 | uint nl = (k - tmp) * sizeof(u32); |
42a0c054 OZ |
430 | if (nl == list->length) |
431 | return list; | |
432 | ||
433 | struct adata *res = adata_empty(pool, nl); | |
ba5c0057 OZ |
434 | memcpy(res->data, tmp, nl); |
435 | return res; | |
436 | } | |
437 | ||
4c553c5a MM |
438 | static const struct adata * |
439 | lclist_filter(struct linpool *pool, const struct adata *list, const struct f_val *set, int pos) | |
66dbdbd9 OZ |
440 | { |
441 | if (!list) | |
442 | return NULL; | |
443 | ||
4c553c5a | 444 | int tree = (set->type == T_SET); /* 1 -> set is T_SET, 0 -> set is T_CLIST */ |
66dbdbd9 OZ |
445 | struct f_val v; |
446 | ||
447 | int len = int_set_get_size(list); | |
448 | u32 *l = int_set_get_data(list); | |
449 | u32 tmp[len]; | |
450 | u32 *k = tmp; | |
451 | int i; | |
452 | ||
453 | v.type = T_LC; | |
454 | for (i = 0; i < len; i += 3) { | |
455 | v.val.lc = lc_get(l, i); | |
456 | /* pos && member(val, set) || !pos && !member(val, set), member() depends on tree */ | |
4c553c5a | 457 | if ((tree ? !!find_tree(set->val.t, &v) : lc_set_contains(set->val.ad, v.val.lc)) == pos) |
66dbdbd9 OZ |
458 | k = lc_copy(k, l+i); |
459 | } | |
460 | ||
3e236955 | 461 | uint nl = (k - tmp) * sizeof(u32); |
42a0c054 OZ |
462 | if (nl == list->length) |
463 | return list; | |
464 | ||
465 | struct adata *res = adata_empty(pool, nl); | |
ba5c0057 OZ |
466 | memcpy(res->data, tmp, nl); |
467 | return res; | |
468 | } | |
469 | ||
8dcf2544 | 470 | /** |
3e82b32d | 471 | * val_in_range - implement |~| operator |
8dcf2544 PM |
472 | * @v1: element |
473 | * @v2: set | |
474 | * | |
b655596d | 475 | * Checks if @v1 is element (|~| operator) of @v2. |
b093c328 | 476 | */ |
9831e591 | 477 | static int |
4c553c5a | 478 | val_in_range(const struct f_val *v1, const struct f_val *v2) |
7db7b7db | 479 | { |
4c553c5a MM |
480 | if ((v1->type == T_PATH) && (v2->type == T_PATH_MASK)) |
481 | return as_path_match(v1->val.ad, v2->val.path_mask); | |
6dc7a0cb | 482 | |
4c553c5a MM |
483 | if ((v1->type == T_INT) && (v2->type == T_PATH)) |
484 | return as_path_contains(v2->val.ad, v1->val.i, 1); | |
6dc7a0cb | 485 | |
4c553c5a MM |
486 | if (((v1->type == T_PAIR) || (v1->type == T_QUAD)) && (v2->type == T_CLIST)) |
487 | return int_set_contains(v2->val.ad, v1->val.i); | |
b655596d | 488 | /* IP->Quad implicit conversion */ |
4c553c5a MM |
489 | if (val_is_ip4(v1) && (v2->type == T_CLIST)) |
490 | return int_set_contains(v2->val.ad, ipa_to_u32(v1->val.ip)); | |
b1a597e0 | 491 | |
4c553c5a MM |
492 | if ((v1->type == T_EC) && (v2->type == T_ECLIST)) |
493 | return ec_set_contains(v2->val.ad, v1->val.ec); | |
ba5c0057 | 494 | |
4c553c5a MM |
495 | if ((v1->type == T_LC) && (v2->type == T_LCLIST)) |
496 | return lc_set_contains(v2->val.ad, v1->val.lc); | |
66dbdbd9 | 497 | |
4c553c5a MM |
498 | if ((v1->type == T_STRING) && (v2->type == T_STRING)) |
499 | return patmatch(v2->val.s, v1->val.s); | |
42a0c054 | 500 | |
4c553c5a MM |
501 | if ((v1->type == T_IP) && (v2->type == T_NET)) |
502 | return ipa_in_netX(v1->val.ip, v2->val.net); | |
7db7b7db | 503 | |
4c553c5a MM |
504 | if ((v1->type == T_NET) && (v2->type == T_NET)) |
505 | return net_in_netX(v1->val.net, v2->val.net); | |
0d1b3c4c | 506 | |
4c553c5a MM |
507 | if ((v1->type == T_NET) && (v2->type == T_PREFIX_SET)) |
508 | return trie_match_net(v2->val.ti, v1->val.net); | |
0d1b3c4c | 509 | |
4c553c5a | 510 | if (v2->type != T_SET) |
b655596d | 511 | return CMP_ERROR; |
0d1b3c4c | 512 | |
b655596d | 513 | /* With integrated Quad<->IP implicit conversion */ |
4c553c5a MM |
514 | if ((v1->type == v2->val.t->from.type) || |
515 | ((v1->type == T_QUAD) && val_is_ip4(&(v2->val.t->from)) && val_is_ip4(&(v2->val.t->to)))) | |
516 | return !!find_tree(v2->val.t, v1); | |
0d1b3c4c | 517 | |
4c553c5a MM |
518 | if (v1->type == T_CLIST) |
519 | return clist_match_set(v1->val.ad, v2->val.t); | |
0d1b3c4c | 520 | |
4c553c5a MM |
521 | if (v1->type == T_ECLIST) |
522 | return eclist_match_set(v1->val.ad, v2->val.t); | |
42a0c054 | 523 | |
4c553c5a MM |
524 | if (v1->type == T_LCLIST) |
525 | return lclist_match_set(v1->val.ad, v2->val.t); | |
66dbdbd9 | 526 | |
4c553c5a MM |
527 | if (v1->type == T_PATH) |
528 | return as_path_match_set(v1->val.ad, v2->val.t); | |
cc31b75a | 529 | |
7db7b7db | 530 | return CMP_ERROR; |
38506f71 PM |
531 | } |
532 | ||
4c5f93d7 | 533 | /* |
0e175f9f | 534 | * val_format - format filter value |
b093c328 | 535 | */ |
508d9360 | 536 | void |
4c553c5a | 537 | val_format(const struct f_val *v, buffer *buf) |
38506f71 | 538 | { |
ecd25633 | 539 | char buf2[1024]; |
4c553c5a | 540 | switch (v->type) |
0e175f9f OZ |
541 | { |
542 | case T_VOID: buffer_puts(buf, "(void)"); return; | |
4c553c5a MM |
543 | case T_BOOL: buffer_puts(buf, v->val.i ? "TRUE" : "FALSE"); return; |
544 | case T_INT: buffer_print(buf, "%u", v->val.i); return; | |
545 | case T_STRING: buffer_print(buf, "%s", v->val.s); return; | |
546 | case T_IP: buffer_print(buf, "%I", v->val.ip); return; | |
547 | case T_NET: buffer_print(buf, "%N", v->val.net); return; | |
548 | case T_PAIR: buffer_print(buf, "(%u,%u)", v->val.i >> 16, v->val.i & 0xffff); return; | |
549 | case T_QUAD: buffer_print(buf, "%R", v->val.i); return; | |
550 | case T_EC: ec_format(buf2, v->val.ec); buffer_print(buf, "%s", buf2); return; | |
551 | case T_LC: lc_format(buf2, v->val.lc); buffer_print(buf, "%s", buf2); return; | |
552 | case T_RD: rd_format(v->val.ec, buf2, 1024); buffer_print(buf, "%s", buf2); return; | |
553 | case T_PREFIX_SET: trie_format(v->val.ti, buf); return; | |
554 | case T_SET: tree_format(v->val.t, buf); return; | |
555 | case T_ENUM: buffer_print(buf, "(enum %x)%u", v->type, v->val.i); return; | |
556 | case T_PATH: as_path_format(v->val.ad, buf2, 1000); buffer_print(buf, "(path %s)", buf2); return; | |
557 | case T_CLIST: int_set_format(v->val.ad, 1, -1, buf2, 1000); buffer_print(buf, "(clist %s)", buf2); return; | |
558 | case T_ECLIST: ec_set_format(v->val.ad, -1, buf2, 1000); buffer_print(buf, "(eclist %s)", buf2); return; | |
559 | case T_LCLIST: lc_set_format(v->val.ad, -1, buf2, 1000); buffer_print(buf, "(lclist %s)", buf2); return; | |
560 | case T_PATH_MASK: pm_format(v->val.path_mask, buf); return; | |
561 | default: buffer_print(buf, "[unknown type %x]", v->type); return; | |
38506f71 | 562 | } |
38506f71 PM |
563 | } |
564 | ||
36bbfc70 | 565 | |
a946317f | 566 | static inline void f_cache_eattrs(struct filter_state *fs) |
13c0be19 | 567 | { |
a946317f | 568 | fs->eattrs = &((*fs->rte)->attrs->eattrs); |
13c0be19 JMM |
569 | } |
570 | ||
a946317f | 571 | static inline void f_rte_cow(struct filter_state *fs) |
a03ede64 | 572 | { |
a946317f | 573 | if (!((*fs->rte)->flags & REF_COW)) |
13c0be19 JMM |
574 | return; |
575 | ||
a946317f | 576 | *fs->rte = rte_cow(*fs->rte); |
a03ede64 OZ |
577 | } |
578 | ||
4c5f93d7 | 579 | /* |
b093c328 PM |
580 | * rta_cow - prepare rta for modification by filter |
581 | */ | |
9831e591 | 582 | static void |
a946317f | 583 | f_rta_cow(struct filter_state *fs) |
26c09e1d | 584 | { |
a946317f | 585 | if (!rta_is_cached((*fs->rte)->attrs)) |
8d9eef17 OZ |
586 | return; |
587 | ||
588 | /* Prepare to modify rte */ | |
a946317f | 589 | f_rte_cow(fs); |
8d9eef17 OZ |
590 | |
591 | /* Store old rta to free it later, it stores reference from rte_cow() */ | |
a946317f | 592 | fs->old_rta = (*fs->rte)->attrs; |
8d9eef17 OZ |
593 | |
594 | /* | |
595 | * Get shallow copy of rta. Fields eattrs and nexthops of rta are shared | |
a946317f | 596 | * with fs->old_rta (they will be copied when the cached rta will be obtained |
8d9eef17 OZ |
597 | * at the end of f_run()), also the lock of hostentry is inherited (we |
598 | * suppose hostentry is not changed by filters). | |
599 | */ | |
a946317f | 600 | (*fs->rte)->attrs = rta_do_cow((*fs->rte)->attrs, fs->pool); |
13c0be19 JMM |
601 | |
602 | /* Re-cache the ea_list */ | |
a946317f | 603 | f_cache_eattrs(fs); |
26c09e1d PM |
604 | } |
605 | ||
4b135d09 | 606 | static char * |
4c553c5a | 607 | val_format_str(struct filter_state *fs, struct f_val *v) { |
4b135d09 PT |
608 | buffer b; |
609 | LOG_BUFFER_INIT(b); | |
610 | val_format(v, &b); | |
a946317f | 611 | return lp_strdup(fs->pool, b.start); |
4b135d09 PT |
612 | } |
613 | ||
1123e707 | 614 | static struct tbf rl_runtime_err = TBF_DEFAULT_LOG_LIMITS; |
cb530392 | 615 | |
4c553c5a MM |
616 | static uint |
617 | inst_line_size(const struct f_inst *what) | |
618 | { | |
619 | uint cnt = 0; | |
620 | for ( ; what; what = what->next) { | |
621 | switch (what->fi_code) { | |
622 | #include "filter/f-inst-line-size.c" | |
623 | } | |
624 | } | |
625 | return cnt; | |
626 | } | |
627 | ||
628 | #if DEBUGGING | |
629 | #define INDENT (((const char *) f_dump_line_indent_str) + sizeof(f_dump_line_indent_str) - (indent) - 1) | |
630 | static const char f_dump_line_indent_str[] = " "; | |
631 | ||
632 | static char val_dump_buffer[1024]; | |
633 | ||
634 | static const char * | |
635 | val_dump(const struct f_val *v) { | |
636 | static buffer b = { | |
637 | .start = val_dump_buffer, | |
638 | .end = val_dump_buffer + 1024, | |
639 | }; | |
640 | b.pos = b.start; | |
641 | val_format(v, &b); | |
642 | return val_dump_buffer; | |
643 | } | |
644 | ||
645 | static void f_dump_line(const struct f_line *dest, int indent); | |
646 | ||
647 | static void | |
648 | f_dump_line_item(const struct f_line_item *item, int indent) | |
649 | { | |
650 | debug("%sInstruction %s at line %u\n", INDENT, f_instruction_name(item->fi_code), item->lineno); | |
651 | switch (item->fi_code) { | |
652 | #include "filter/f-inst-dump.c" | |
653 | } | |
654 | } | |
655 | ||
656 | static void | |
657 | f_dump_line(const struct f_line *dest, int indent) | |
658 | { | |
659 | if (!dest) { | |
660 | debug("%sNo filter line (NULL)\n", INDENT); | |
661 | return; | |
662 | } | |
663 | debug("%sFilter line %p (len=%u)\n", INDENT, dest, dest->len); | |
664 | for (uint i=0; i<dest->len; i++) | |
665 | f_dump_line_item(&dest->items[i], indent+1); | |
666 | debug("%sFilter line %p dump done\n", INDENT, dest); | |
667 | #undef INDENT | |
668 | } | |
669 | #else | |
670 | #define f_dump_line(...) | |
671 | #endif | |
672 | ||
673 | static uint | |
674 | postfixify(struct f_line *dest, const struct f_inst *what, uint pos) | |
675 | { | |
676 | for ( ; what; what = what->next) { | |
677 | switch (what->fi_code) { | |
678 | #include "filter/f-inst-postfixify.c" | |
679 | } | |
680 | pos++; | |
681 | } | |
682 | return pos; | |
683 | } | |
684 | ||
685 | struct f_line * | |
686 | f_postfixify_concat(struct f_inst *first, ...) | |
687 | { | |
688 | va_list args; | |
689 | va_list argd; | |
690 | va_start(args, first); | |
691 | va_copy(argd, args); | |
692 | ||
693 | uint len = 0; | |
694 | for (struct f_inst *what = first; what; what = va_arg(args, struct f_inst *)) | |
695 | len += inst_line_size(what); | |
696 | ||
697 | va_end(args); | |
698 | ||
699 | struct f_line *out = cfg_allocz(sizeof(struct f_line) + sizeof(struct f_line_item)*len); | |
700 | ||
701 | for (struct f_inst *what = first; what; what = va_arg(argd, struct f_inst *)) | |
702 | out->len = postfixify(out, what, out->len); | |
703 | ||
704 | f_dump_line(out, 0); | |
705 | return out; | |
706 | } | |
707 | ||
b093c328 PM |
708 | /** |
709 | * interpret | |
a946317f | 710 | * @fs: filter state |
2e9b2421 | 711 | * @what: filter to interpret |
b093c328 | 712 | * |
4c5f93d7 | 713 | * Interpret given tree of filter instructions. This is core function |
b093c328 | 714 | * of filter system and does all the hard work. |
771ae456 PM |
715 | * |
716 | * Each instruction has 4 fields: code (which is instruction code), | |
717 | * aux (which is extension to instruction code, typically type), | |
718 | * arg1 and arg2 - arguments. Depending on instruction, arguments | |
315f23a0 | 719 | * are either integers, or pointers to instruction trees. Common |
771ae456 PM |
720 | * instructions like +, that have two expressions as arguments use |
721 | * TWOARGS macro to get both of them evaluated. | |
b093c328 | 722 | */ |
7afa1438 | 723 | static enum filter_return |
4c553c5a | 724 | interpret(struct filter_state *fs, const struct f_line *line, struct f_val *val) |
23b1539b | 725 | { |
4c553c5a MM |
726 | struct f_val_stack vstk; |
727 | vstk.cnt = 0; | |
23b1539b | 728 | |
4c553c5a MM |
729 | struct f_exec_stack estk; |
730 | estk.cnt = 1; | |
731 | estk.item[0].line = line; | |
732 | estk.item[0].pos = 0; | |
fc8df41e | 733 | |
4c553c5a MM |
734 | #define curline estk.item[estk.cnt-1] |
735 | ||
736 | while (estk.cnt > 0) { | |
737 | while (curline.pos < curline.line->len) { | |
738 | const struct f_line_item *what = &(curline.line->items[curline.pos++]); | |
7afa1438 | 739 | |
4c553c5a MM |
740 | |
741 | switch (what->fi_code) { | |
742 | #define res vstk.val[vstk.cnt] | |
743 | #define v1 vstk.val[vstk.cnt] | |
744 | #define v2 vstk.val[vstk.cnt + 1] | |
745 | #define v3 vstk.val[vstk.cnt + 2] | |
7afa1438 | 746 | |
aca82639 | 747 | #define runtime(fmt, ...) do { \ |
a8740d6c MM |
748 | if (!(fs->flags & FF_SILENT)) \ |
749 | log_rl(&rl_runtime_err, L_ERR "filters, line %d: " fmt, what->lineno, ##__VA_ARGS__); \ | |
750 | return F_ERROR; \ | |
751 | } while(0) | |
aca82639 | 752 | |
a8740d6c | 753 | #define ACCESS_RTE do { if (!fs->rte) runtime("No route to access"); } while (0) |
a8740d6c | 754 | #define ACCESS_EATTRS do { if (!fs->eattrs) f_cache_eattrs(fs); } while (0) |
aca82639 | 755 | |
967b88d9 | 756 | #include "filter/f-inst-interpret.c" |
7afa1438 | 757 | #undef res |
4c553c5a MM |
758 | #undef v1 |
759 | #undef v2 | |
760 | #undef v3 | |
aca82639 | 761 | #undef runtime |
aca82639 JMM |
762 | #undef ACCESS_RTE |
763 | #undef ACCESS_EATTRS | |
4c553c5a | 764 | } |
a8740d6c | 765 | } |
4c553c5a | 766 | estk.cnt--; |
a8740d6c | 767 | } |
23b1539b | 768 | |
4c553c5a MM |
769 | switch (vstk.cnt) { |
770 | case 0: | |
771 | if (val) { | |
772 | log_rl(&rl_runtime_err, L_ERR "filters: No value left on stack"); | |
773 | return F_ERROR; | |
774 | } | |
775 | return F_NOP; | |
776 | case 1: | |
777 | if (val) { | |
778 | *val = vstk.val[0]; | |
779 | return F_NOP; | |
780 | } | |
781 | /* fallthrough */ | |
782 | default: | |
783 | log_rl(&rl_runtime_err, L_ERR "Too many items left on stack: %u", vstk.cnt); | |
784 | return F_ERROR; | |
785 | } | |
786 | } | |
0ec6b5ec | 787 | |
9a4037d4 | 788 | |
4c5f93d7 | 789 | /* |
4c553c5a | 790 | * f_same - function that does real comparing of instruction trees, you should call filter_same from outside |
b093c328 | 791 | */ |
9a4037d4 | 792 | int |
4c553c5a | 793 | f_same(const struct f_line *fl1, const struct f_line *fl2) |
9a4037d4 | 794 | { |
4c553c5a | 795 | if ((!fl1) && (!fl2)) |
d4d75628 | 796 | return 1; |
4c553c5a | 797 | if ((!fl1) || (!fl2)) |
9a4037d4 | 798 | return 0; |
4c553c5a | 799 | if (fl1->len != fl2->len) |
9a4037d4 | 800 | return 0; |
4c553c5a MM |
801 | for (uint i=0; i<fl1->len; i++) { |
802 | #define f1 (&(fl1->items[i])) | |
803 | #define f2 (&(fl2->items[i])) | |
804 | if (f1->fi_code != f2->fi_code) | |
805 | return 0; | |
806 | if (f1->flags != f2->flags) | |
807 | return 0; | |
9a4037d4 | 808 | |
4c553c5a MM |
809 | switch(f1->fi_code) { |
810 | #include "filter/f-inst-same.c" | |
811 | } | |
812 | } | |
813 | return 1; | |
814 | } | |
815 | ||
816 | #if 0 | |
74bfd2f9 | 817 | case FI_ADD: /* fall through */ |
5a14df39 MJM |
818 | case FI_SUBTRACT: |
819 | case FI_MULTIPLY: | |
820 | case FI_DIVIDE: | |
821 | case FI_OR: | |
822 | case FI_AND: | |
823 | case FI_PAIR_CONSTRUCT: | |
824 | case FI_EC_CONSTRUCT: | |
825 | case FI_NEQ: | |
826 | case FI_EQ: | |
827 | case FI_LT: | |
828 | case FI_LTE: TWOARGS; break; | |
829 | ||
7f0ac737 | 830 | case FI_PATHMASK_CONSTRUCT: if (!pm_same(f1->a[0].p, f2->a[0].p)) return 0; break; |
e8bc64e3 | 831 | |
5a14df39 MJM |
832 | case FI_NOT: ONEARG; break; |
833 | case FI_NOT_MATCH: | |
834 | case FI_MATCH: TWOARGS; break; | |
835 | case FI_DEFINED: ONEARG; break; | |
d1ba927b | 836 | case FI_TYPE: ONEARG; break; |
5a14df39 MJM |
837 | |
838 | case FI_LC_CONSTRUCT: | |
478f9bab | 839 | THREEARGS; |
66dbdbd9 OZ |
840 | break; |
841 | ||
5a14df39 | 842 | case FI_SET: |
0ec6b5ec | 843 | ARG(2); |
9a4037d4 PM |
844 | { |
845 | struct symbol *s1, *s2; | |
7f0ac737 MM |
846 | s1 = f1->a[0].p; |
847 | s2 = f2->a[0].p; | |
9a4037d4 PM |
848 | if (strcmp(s1->name, s2->name)) |
849 | return 0; | |
850 | if (s1->class != s2->class) | |
851 | return 0; | |
852 | } | |
853 | break; | |
854 | ||
5a14df39 | 855 | case FI_CONSTANT: |
b1a597e0 OZ |
856 | switch (f1->aux) { |
857 | ||
858 | case T_PREFIX_SET: | |
7f0ac737 | 859 | if (!trie_same(f1->a[1].p, f2->a[1].p)) |
b1a597e0 | 860 | return 0; |
9be1086d | 861 | break; |
b1a597e0 OZ |
862 | |
863 | case T_SET: | |
7f0ac737 | 864 | if (!same_tree(f1->a[1].p, f2->a[1].p)) |
4bb18dd2 | 865 | return 0; |
9be1086d | 866 | break; |
b1a597e0 | 867 | |
4bb18dd2 | 868 | case T_STRING: |
7f0ac737 | 869 | if (strcmp(f1->a[1].p, f2->a[1].p)) |
4bb18dd2 PM |
870 | return 0; |
871 | break; | |
b1a597e0 | 872 | |
4bb18dd2 PM |
873 | default: |
874 | A2_SAME; | |
875 | } | |
876 | break; | |
507e182a | 877 | |
5a14df39 | 878 | case FI_CONSTANT_INDIRECT: |
7f0ac737 | 879 | if (!val_same(* (struct f_val *) f1->a[0].p, * (struct f_val *) f2->a[0].p)) |
9a4037d4 PM |
880 | return 0; |
881 | break; | |
28a10f84 | 882 | |
5a14df39 | 883 | case FI_VARIABLE: |
7f0ac737 | 884 | if (strcmp((char *) f1->a[1].p, (char *) f2->a[1].p)) |
9be1086d OF |
885 | return 0; |
886 | break; | |
5a14df39 | 887 | case FI_PRINT: case FI_LENGTH: ONEARG; break; |
224b77d4 | 888 | case FI_CONDITION: THREEARGS; break; |
5a14df39 MJM |
889 | case FI_NOP: case FI_EMPTY: break; |
890 | case FI_PRINT_AND_DIE: ONEARG; A2_SAME; break; | |
891 | case FI_PREF_GET: | |
892 | case FI_RTA_GET: A2_SAME; break; | |
893 | case FI_EA_GET: A2_SAME; break; | |
894 | case FI_PREF_SET: | |
895 | case FI_RTA_SET: | |
896 | case FI_EA_SET: ONEARG; A2_SAME; break; | |
897 | ||
898 | case FI_RETURN: ONEARG; break; | |
823ad121 JMM |
899 | case FI_ROA_MAXLEN: ONEARG; break; |
900 | case FI_ROA_ASN: ONEARG; break; | |
b24b7811 | 901 | case FI_SADR_SRC: ONEARG; break; |
5a14df39 | 902 | case FI_IP: ONEARG; break; |
823ad121 | 903 | case FI_IS_V4: ONEARG; break; |
d1ba927b | 904 | case FI_ROUTE_DISTINGUISHER: ONEARG; break; |
5a14df39 | 905 | case FI_CALL: /* Call rewriting trickery to avoid exponential behaviour */ |
315f23a0 | 906 | ONEARG; |
7f0ac737 | 907 | if (!i_same(f1->a[1].p, f2->a[1].p)) |
315f23a0 | 908 | return 0; |
7f0ac737 | 909 | f2->a[1].p = f1->a[1].p; |
d4d75628 | 910 | break; |
5a14df39 | 911 | case FI_CLEAR_LOCAL_VARS: break; /* internal instruction */ |
7f0ac737 | 912 | case FI_SWITCH: ONEARG; if (!same_tree(f1->a[1].p, f2->a[1].p)) return 0; break; |
5a14df39 MJM |
913 | case FI_IP_MASK: TWOARGS; break; |
914 | case FI_PATH_PREPEND: TWOARGS; break; | |
915 | case FI_CLIST_ADD_DEL: TWOARGS; break; | |
916 | case FI_AS_PATH_FIRST: | |
917 | case FI_AS_PATH_LAST: | |
918 | case FI_AS_PATH_LAST_NAG: ONEARG; break; | |
919 | case FI_ROA_CHECK: | |
af582c48 | 920 | TWOARGS; |
84360407 MM |
921 | /* FIXME: ROA check results may change anyway */ |
922 | if (strcmp(f1->a[2].rtc->name, | |
923 | f2->a[2].rtc->name)) | |
af582c48 OZ |
924 | return 0; |
925 | break; | |
823ad121 JMM |
926 | case FI_FORMAT: ONEARG; break; |
927 | case FI_ASSERT: ONEARG; break; | |
9a4037d4 | 928 | default: |
5a14df39 | 929 | bug( "Unknown instruction %d in same (%c)", f1->fi_code, f1->fi_code & 0xff); |
4c553c5a | 930 | #endif |
9a4037d4 | 931 | |
ff95080f | 932 | /** |
a03ede64 OZ |
933 | * f_run - run a filter for a route |
934 | * @filter: filter to run | |
935 | * @rte: route being filtered, may be modified | |
ff95080f | 936 | * @tmp_pool: all filter allocations go from this pool |
4c5f93d7 | 937 | * @flags: flags |
a03ede64 OZ |
938 | * |
939 | * If filter needs to modify the route, there are several | |
940 | * posibilities. @rte might be read-only (with REF_COW flag), in that | |
941 | * case rw copy is obtained by rte_cow() and @rte is replaced. If | |
942 | * @rte is originally rw, it may be directly modified (and it is never | |
943 | * copied). | |
944 | * | |
945 | * The returned rte may reuse the (possibly cached, cloned) rta, or | |
946 | * (if rta was modificied) contains a modified uncached rta, which | |
947 | * uses parts allocated from @tmp_pool and parts shared from original | |
948 | * rta. There is one exception - if @rte is rw but contains a cached | |
949 | * rta and that is modified, rta in returned rte is also cached. | |
950 | * | |
951 | * Ownership of cached rtas is consistent with rte, i.e. | |
952 | * if a new rte is returned, it has its own clone of cached rta | |
953 | * (and cached rta of read-only source rte is intact), if rte is | |
954 | * modified in place, old cached rta is possibly freed. | |
ff95080f | 955 | */ |
7afa1438 | 956 | enum filter_return |
4c553c5a | 957 | f_run(const struct filter *filter, struct rte **rte, struct linpool *tmp_pool, int flags) |
23b1539b | 958 | { |
36da2857 OZ |
959 | if (filter == FILTER_ACCEPT) |
960 | return F_ACCEPT; | |
961 | ||
962 | if (filter == FILTER_REJECT) | |
963 | return F_REJECT; | |
964 | ||
a03ede64 | 965 | int rte_cow = ((*rte)->flags & REF_COW); |
6b9fa320 | 966 | DBG( "Running filter `%s'...", filter->name ); |
23b1539b | 967 | |
a946317f JMM |
968 | struct filter_state fs = { |
969 | .rte = rte, | |
970 | .pool = tmp_pool, | |
971 | .flags = flags, | |
972 | }; | |
0d1b3c4c | 973 | |
a946317f | 974 | LOG_BUFFER_INIT(fs.buf); |
0e175f9f | 975 | |
4c553c5a | 976 | enum filter_return fret = interpret(&fs, filter->root, NULL); |
a03ede64 | 977 | |
a946317f | 978 | if (fs.old_rta) { |
a03ede64 | 979 | /* |
a946317f | 980 | * Cached rta was modified and fs->rte contains now an uncached one, |
a03ede64 | 981 | * sharing some part with the cached one. The cached rta should |
a946317f | 982 | * be freed (if rte was originally COW, fs->old_rta is a clone |
a03ede64 OZ |
983 | * obtained during rte_cow()). |
984 | * | |
985 | * This also implements the exception mentioned in f_run() | |
986 | * description. The reason for this is that rta reuses parts of | |
a946317f | 987 | * fs->old_rta, and these may be freed during rta_free(fs->old_rta). |
a03ede64 OZ |
988 | * This is not the problem if rte was COW, because original rte |
989 | * also holds the same rta. | |
990 | */ | |
991 | if (!rte_cow) | |
a946317f | 992 | (*fs.rte)->attrs = rta_lookup((*fs.rte)->attrs); |
a03ede64 | 993 | |
a946317f | 994 | rta_free(fs.old_rta); |
a03ede64 OZ |
995 | } |
996 | ||
0d1b3c4c | 997 | |
7afa1438 | 998 | if (fret < F_ACCEPT) { |
a946317f | 999 | if (!(fs.flags & FF_SILENT)) |
b9405791 | 1000 | log_rl(&rl_runtime_err, L_ERR "Filter %s did not return accept nor reject. Make up your mind", filter->name); |
23b1539b | 1001 | return F_ERROR; |
0b1cad81 | 1002 | } |
52e030e1 | 1003 | DBG( "done (%u)\n", res.val.i ); |
7afa1438 | 1004 | return fret; |
23b1539b PM |
1005 | } |
1006 | ||
1321e12a OZ |
1007 | /* TODO: perhaps we could integrate f_eval(), f_eval_rte() and f_run() */ |
1008 | ||
7afa1438 | 1009 | enum filter_return |
4c553c5a | 1010 | f_eval_rte(const struct f_line *expr, struct rte **rte, struct linpool *tmp_pool) |
1321e12a | 1011 | { |
1321e12a | 1012 | |
a946317f JMM |
1013 | struct filter_state fs = { |
1014 | .rte = rte, | |
1015 | .pool = tmp_pool, | |
1016 | }; | |
1321e12a | 1017 | |
a946317f | 1018 | LOG_BUFFER_INIT(fs.buf); |
1321e12a OZ |
1019 | |
1020 | /* Note that in this function we assume that rte->attrs is private / uncached */ | |
4c553c5a | 1021 | return interpret(&fs, expr, NULL); |
1321e12a OZ |
1022 | } |
1023 | ||
7afa1438 | 1024 | enum filter_return |
4c553c5a | 1025 | f_eval(const struct f_line *expr, struct linpool *tmp_pool, struct f_val *pres) |
1c20608e | 1026 | { |
a946317f JMM |
1027 | struct filter_state fs = { |
1028 | .pool = tmp_pool, | |
1029 | }; | |
0d1b3c4c | 1030 | |
a946317f | 1031 | LOG_BUFFER_INIT(fs.buf); |
0e175f9f | 1032 | |
4c553c5a | 1033 | enum filter_return fret = interpret(&fs, expr, pres); |
fc8df41e | 1034 | return fret; |
508d9360 | 1035 | } |
0d1b3c4c | 1036 | |
52e030e1 | 1037 | uint |
4c553c5a | 1038 | f_eval_int(const struct f_line *expr) |
508d9360 OZ |
1039 | { |
1040 | /* Called independently in parse-time to eval expressions */ | |
fc8df41e JMM |
1041 | struct filter_state fs = { |
1042 | .pool = cfg_mem, | |
fc8df41e JMM |
1043 | }; |
1044 | ||
4c553c5a MM |
1045 | struct f_val val; |
1046 | ||
fc8df41e JMM |
1047 | LOG_BUFFER_INIT(fs.buf); |
1048 | ||
4c553c5a | 1049 | if (interpret(&fs, expr, &val) > F_RETURN) |
7afa1438 | 1050 | cf_error("Runtime error while evaluating expression"); |
0d1b3c4c | 1051 | |
4c553c5a | 1052 | if (val.type != T_INT) |
b1c9d871 | 1053 | cf_error("Integer expression expected"); |
508d9360 | 1054 | |
4c553c5a | 1055 | return val.val.i; |
b1c9d871 | 1056 | } |
1c20608e | 1057 | |
ff95080f PM |
1058 | /** |
1059 | * filter_same - compare two filters | |
1060 | * @new: first filter to be compared | |
1061 | * @old: second filter to be compared, notice that this filter is | |
1062 | * damaged while comparing. | |
1063 | * | |
1064 | * Returns 1 in case filters are same, otherwise 0. If there are | |
1065 | * underlying bugs, it will rather say 0 on same filters than say | |
1066 | * 1 on different. | |
1067 | */ | |
30a6108c MM |
1068 | int |
1069 | filter_same(struct filter *new, struct filter *old) | |
1070 | { | |
81ce667b MM |
1071 | if (old == new) /* Handle FILTER_ACCEPT and FILTER_REJECT */ |
1072 | return 1; | |
1073 | if (old == FILTER_ACCEPT || old == FILTER_REJECT || | |
1074 | new == FILTER_ACCEPT || new == FILTER_REJECT) | |
1075 | return 0; | |
4c553c5a | 1076 | return f_same(new->root, old->root); |
30a6108c | 1077 | } |