]>
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 | * |
720d911d | 8 | * Notice that pair is stored as integer: first << 16 | second |
2f702671 | 9 | * |
23b1539b PM |
10 | */ |
11 | ||
78c6217c | 12 | #define LOCAL_DEBUG |
6b9fa320 | 13 | |
23b1539b PM |
14 | #include "nest/bird.h" |
15 | #include "lib/lists.h" | |
16 | #include "lib/resource.h" | |
17 | #include "lib/socket.h" | |
38506f71 | 18 | #include "lib/string.h" |
7f77e250 | 19 | #include "lib/unaligned.h" |
23b1539b PM |
20 | #include "nest/route.h" |
21 | #include "nest/protocol.h" | |
22 | #include "nest/iface.h" | |
23 | #include "conf/conf.h" | |
24 | #include "filter/filter.h" | |
25 | ||
2d496d20 PM |
26 | #define P(a,b) ((a<<8) | b) |
27 | ||
23b1539b PM |
28 | struct f_inst *startup_func = NULL; |
29 | ||
38506f71 PM |
30 | #define CMP_ERROR 999 |
31 | ||
32 | /* Compare two values, returns -1, 0, 1 compared, ERROR 999 */ | |
33 | int | |
34 | val_compare(struct f_val v1, struct f_val v2) | |
35 | { | |
41be4444 PM |
36 | if ((v1.type == T_VOID) && (v2.type == T_VOID)) |
37 | return 0; | |
38 | if (v1.type == T_VOID) /* Hack for else */ | |
39 | return -1; | |
40 | if (v2.type == T_VOID) | |
41 | return 1; | |
42 | ||
7db7b7db PM |
43 | if (v1.type != v2.type) |
44 | return CMP_ERROR; | |
38506f71 | 45 | switch (v1.type) { |
f4536657 | 46 | case T_ENUM: |
38506f71 | 47 | case T_INT: |
d3dd620b | 48 | case T_PAIR: |
38506f71 PM |
49 | if (v1.val.i == v2.val.i) return 0; |
50 | if (v1.val.i < v2.val.i) return -1; | |
51 | return 1; | |
43fc099b | 52 | case T_IP: |
6dc7a0cb PM |
53 | case T_PREFIX: |
54 | return ipa_compare(v1.val.px.ip, v2.val.px.ip); | |
3076b5ae MM |
55 | default: |
56 | return CMP_ERROR; | |
38506f71 PM |
57 | } |
58 | } | |
59 | ||
6dc7a0cb PM |
60 | int |
61 | val_simple_in_range(struct f_val v1, struct f_val v2) | |
62 | { | |
10a53608 | 63 | if ((v1.type == T_PATH) && (v2.type == T_PATH_MASK)) |
2803c9dd | 64 | return path_match(v1.val.ad->data, v1.val.ad->length, v2.val.path_mask); |
10a53608 | 65 | |
6dc7a0cb PM |
66 | if ((v1.type == T_IP) && (v2.type == T_PREFIX)) |
67 | return !(ipa_compare(ipa_and(v2.val.px.ip, ipa_mkmask(v2.val.px.len)), ipa_and(v1.val.px.ip, ipa_mkmask(v2.val.px.len)))); | |
68 | ||
69 | if ((v1.type == T_PREFIX) && (v2.type == T_PREFIX)) { | |
70 | ip_addr mask; | |
71 | if (v1.val.px.len & (LEN_PLUS | LEN_MINUS | LEN_RANGE)) | |
72 | return CMP_ERROR; | |
73 | mask = ipa_mkmask( v2.val.px.len & LEN_MASK ); | |
74 | if (ipa_compare(ipa_and(v2.val.px.ip, mask), ipa_and(v1.val.px.ip, mask))) | |
75 | return 0; | |
8f013d9c | 76 | |
6dc7a0cb PM |
77 | if ((v2.val.px.len & LEN_MINUS) && (v1.val.px.len <= (v2.val.px.len & LEN_MASK))) |
78 | return 0; | |
79 | if ((v2.val.px.len & LEN_PLUS) && (v1.val.px.len < (v2.val.px.len & LEN_MASK))) | |
80 | return 0; | |
81 | if ((v2.val.px.len & LEN_RANGE) && ((v1.val.px.len < (0xff & (v2.val.px.len >> 16))) | |
82 | || (v1.val.px.len > (0xff & (v2.val.px.len >> 8))))) | |
83 | return 0; | |
84 | return 1; | |
85 | } | |
86 | return CMP_ERROR; | |
87 | } | |
88 | ||
7db7b7db PM |
89 | int |
90 | val_in_range(struct f_val v1, struct f_val v2) | |
91 | { | |
6dc7a0cb PM |
92 | int res; |
93 | ||
94 | res = val_simple_in_range(v1, v2); | |
95 | ||
96 | if (res != CMP_ERROR) | |
97 | return res; | |
98 | ||
2f702671 | 99 | if (((v1.type == T_INT) || ((v1.type == T_IP) || (v1.type == T_PREFIX)) && (v2.type == T_SET))) { |
6dc7a0cb PM |
100 | struct f_tree *n; |
101 | n = find_tree(v2.val.t, v1); | |
102 | if (!n) | |
103 | return 0; | |
104 | return !! (val_simple_in_range(v1, n->from)); /* We turn CMP_ERROR into compared ok, and that's fine */ | |
105 | } | |
7db7b7db PM |
106 | return CMP_ERROR; |
107 | } | |
108 | ||
38506f71 PM |
109 | static void |
110 | tree_print(struct f_tree *t) | |
111 | { | |
112 | if (!t) { | |
3cf4a2e2 | 113 | debug( "() " ); |
38506f71 PM |
114 | return; |
115 | } | |
3cf4a2e2 | 116 | debug( "[ " ); |
38506f71 | 117 | tree_print( t->left ); |
3cf4a2e2 | 118 | debug( ", " ); val_print( t->from ); debug( ".." ); val_print( t->to ); debug( ", " ); |
38506f71 | 119 | tree_print( t->right ); |
3cf4a2e2 | 120 | debug( "] " ); |
38506f71 PM |
121 | } |
122 | ||
123 | void | |
124 | val_print(struct f_val v) | |
125 | { | |
126 | char buf[2048]; | |
127 | #define PRINTF(a...) bsnprintf( buf, 2040, a ) | |
128 | buf[0] = 0; | |
129 | switch (v.type) { | |
130 | case T_VOID: PRINTF( "(void)" ); break; | |
131 | case T_BOOL: PRINTF( v.val.i ? "TRUE" : "FALSE" ); break; | |
132 | case T_INT: PRINTF( "%d ", v.val.i ); break; | |
133 | case T_STRING: PRINTF( "%s", v.val.s ); break; | |
6dc7a0cb | 134 | case T_IP: PRINTF( "%I", v.val.px.ip ); break; |
720d911d PM |
135 | case T_PREFIX: PRINTF( "%I/%d", v.val.px.ip, v.val.px.len ); break; |
136 | case T_PAIR: PRINTF( "(%d,%d)", v.val.i >> 16, v.val.i & 0xffff ); break; | |
38506f71 | 137 | case T_SET: tree_print( v.val.t ); PRINTF( "\n" ); break; |
346a12c2 | 138 | case T_ENUM: PRINTF( "(enum %x)%d", v.type, v.val.i ); break; |
2803c9dd | 139 | case T_PATH: PRINTF( "%s", path_format(v.val.ad->data, v.val.ad->length)); break; |
dcab7890 | 140 | case T_PATH_MASK: debug( "(path " ); { struct f_path_mask *p = v.val.s; while (p) { debug("%d ", p->val); p=p->next; } debug(")" ); } break; |
38506f71 | 141 | default: PRINTF( "[unknown type %x]", v.type ); |
7f77e250 | 142 | #undef PRINTF |
38506f71 | 143 | } |
3cf4a2e2 | 144 | debug( buf ); |
38506f71 PM |
145 | } |
146 | ||
48f9e019 | 147 | static struct rte **f_rte, *f_rte_old; |
f31156ca | 148 | static struct linpool *f_pool; |
31e79264 | 149 | static struct ea_list **f_tmp_attrs; |
0a06a9b8 | 150 | static int f_flags; |
36bbfc70 | 151 | |
9a4037d4 PM |
152 | #define runtime(x) do { \ |
153 | log( L_ERR x ); \ | |
154 | res.type = T_RETURN; \ | |
155 | res.val.i = F_ERROR; \ | |
156 | return res; \ | |
157 | } while(0) | |
158 | ||
159 | #define ARG(x,y) \ | |
160 | x = interpret(what->y); \ | |
2d496d20 | 161 | if (x.type & T_RETURN) \ |
9a4037d4 PM |
162 | return x; |
163 | ||
164 | #define ONEARG ARG(v1, a1.p) | |
165 | #define TWOARGS ARG(v1, a1.p) \ | |
166 | ARG(v2, a2.p) | |
167 | #define TWOARGS_C TWOARGS \ | |
168 | if (v1.type != v2.type) \ | |
169 | runtime( "Can not operate with values of incompatible types" ); | |
7db7b7db | 170 | |
23b1539b PM |
171 | static struct f_val |
172 | interpret(struct f_inst *what) | |
173 | { | |
174 | struct symbol *sym; | |
175 | struct f_val v1, v2, res; | |
38506f71 | 176 | int i,j,k; |
23b1539b PM |
177 | |
178 | res.type = T_VOID; | |
179 | if (!what) | |
180 | return res; | |
181 | ||
182 | switch(what->code) { | |
183 | case ',': | |
184 | TWOARGS; | |
185 | break; | |
186 | ||
187 | /* Binary operators */ | |
188 | case '+': | |
189 | TWOARGS_C; | |
190 | switch (res.type = v1.type) { | |
191 | case T_VOID: runtime( "Can not operate with values of type void" ); | |
192 | case T_INT: res.val.i = v1.val.i + v2.val.i; break; | |
193 | default: runtime( "Usage of unknown type" ); | |
194 | } | |
195 | break; | |
196 | case '/': | |
197 | TWOARGS_C; | |
198 | switch (res.type = v1.type) { | |
199 | case T_VOID: runtime( "Can not operate with values of type void" ); | |
200 | case T_INT: res.val.i = v1.val.i / v2.val.i; break; | |
201 | case T_IP: if (v2.type != T_INT) | |
202 | runtime( "Operator / is <ip>/<int>" ); | |
203 | break; | |
204 | default: runtime( "Usage of unknown type" ); | |
205 | } | |
206 | break; | |
207 | ||
208 | /* Relational operators */ | |
38506f71 PM |
209 | |
210 | #define COMPARE(x) \ | |
211 | TWOARGS_C; \ | |
212 | res.type = T_BOOL; \ | |
213 | i = val_compare(v1, v2); \ | |
214 | if (i==CMP_ERROR) \ | |
215 | runtime( "Error in comparation" ); \ | |
216 | res.val.i = (x); \ | |
23b1539b | 217 | break; |
38506f71 | 218 | |
2d496d20 PM |
219 | case P('!','='): COMPARE(i!=0); |
220 | case P('=','='): COMPARE(i==0); | |
38506f71 | 221 | case '<': COMPARE(i==-1); |
2d496d20 | 222 | case P('<','='): COMPARE(i!=1); |
38506f71 | 223 | |
995e5894 PM |
224 | case '!': |
225 | ONEARG; | |
226 | if (v1.type != T_BOOL) | |
227 | runtime( "not applied to non-boolean" ); | |
228 | res = v1; | |
229 | res.val.i = !res.val.i; | |
230 | break; | |
231 | ||
38506f71 PM |
232 | case '~': |
233 | TWOARGS; | |
23b1539b | 234 | res.type = T_BOOL; |
7db7b7db PM |
235 | res.val.i = val_in_range(v1, v2); |
236 | if (res.val.i == CMP_ERROR) | |
237 | runtime( "~ applied on unknown type pair" ); | |
23b1539b | 238 | break; |
2d496d20 | 239 | case P('d','e'): |
f4536657 PM |
240 | ONEARG; |
241 | res.type = T_BOOL; | |
242 | res.val.i = (v1.type != T_VOID); | |
243 | break; | |
23b1539b | 244 | |
d3dd620b | 245 | /* Set to indirect value, a1 = variable, a2 = value */ |
23b1539b | 246 | case 's': |
2db3b288 PM |
247 | ARG(v2, a2.p); |
248 | sym = what->a1.p; | |
23b1539b PM |
249 | switch (res.type = v2.type) { |
250 | case T_VOID: runtime( "Can not assign void values" ); | |
f4536657 | 251 | case T_ENUM: |
23b1539b | 252 | case T_INT: |
d3dd620b PM |
253 | case T_IP: |
254 | case T_PREFIX: | |
255 | case T_PAIR: | |
dcab7890 | 256 | case T_PATH_MASK: |
d3dd620b | 257 | if (sym->class != (SYM_VARIABLE | v2.type)) |
23b1539b | 258 | runtime( "Variable of bad type" ); |
d3dd620b | 259 | * (struct f_val *) sym->aux2 = v2; |
23b1539b | 260 | break; |
d3dd620b | 261 | default: |
3076b5ae | 262 | bug( "Set to invalid type" ); |
23b1539b PM |
263 | } |
264 | break; | |
265 | ||
d3dd620b | 266 | case 'c': /* integer (or simple type) constant */ |
c7b43f33 | 267 | res.type = what->aux; |
d3dd620b | 268 | res.val.i = what->a2.i; |
23b1539b | 269 | break; |
38506f71 PM |
270 | case 'C': |
271 | res = * ((struct f_val *) what->a1.p); | |
272 | break; | |
23b1539b PM |
273 | case 'p': |
274 | ONEARG; | |
38506f71 | 275 | val_print(v1); |
23b1539b PM |
276 | break; |
277 | case '?': /* ? has really strange error value, so we can implement if ... else nicely :-) */ | |
278 | ONEARG; | |
279 | if (v1.type != T_BOOL) | |
280 | runtime( "If requires bool expression" ); | |
281 | if (v1.val.i) { | |
2db3b288 | 282 | ARG(res,a2.p); |
23b1539b PM |
283 | res.val.i = 0; |
284 | } else res.val.i = 1; | |
285 | res.type = T_BOOL; | |
286 | break; | |
287 | case '0': | |
3cf4a2e2 | 288 | debug( "No operation\n" ); |
23b1539b | 289 | break; |
2d496d20 | 290 | case P('p',','): |
23b1539b | 291 | ONEARG; |
798df5b1 | 292 | if (what->a2.i == F_NOP || (what->a2.i != F_NONL && what->a1.p)) |
3cf4a2e2 | 293 | debug( "\n" ); |
23b1539b | 294 | |
2db3b288 | 295 | switch (what->a2.i) { |
23b1539b PM |
296 | case F_QUITBIRD: |
297 | die( "Filter asked me to die" ); | |
298 | case F_ACCEPT: | |
299 | /* Should take care about turning ACCEPT into MODIFY */ | |
300 | case F_ERROR: | |
2ad6dcdb | 301 | case F_REJECT: /* FIXME (noncritical) Should print complete route along with reason to reject route */ |
23b1539b | 302 | res.type = T_RETURN; |
2ad6dcdb | 303 | res.val.i = what->a2.i; |
7e1f9971 | 304 | return res; /* We have to return now, no more processing. */ |
d3dd620b | 305 | case F_NONL: |
23b1539b PM |
306 | case F_NOP: |
307 | break; | |
308 | default: | |
309 | bug( "unknown return type: can not happen"); | |
310 | } | |
311 | break; | |
36bbfc70 PM |
312 | case 'a': /* rta access */ |
313 | { | |
314 | struct rta *rta = (*f_rte)->attrs; | |
c7b43f33 | 315 | res.type = what->aux; |
36bbfc70 PM |
316 | switch(res.type) { |
317 | case T_IP: | |
6dc7a0cb | 318 | res.val.px.ip = * (ip_addr *) ((char *) rta + what->a2.i); |
36bbfc70 | 319 | break; |
c7b43f33 PM |
320 | case T_ENUM: |
321 | res.val.i = * ((char *) rta + what->a2.i); | |
322 | break; | |
36bbfc70 PM |
323 | case T_PREFIX: /* Warning: this works only for prefix of network */ |
324 | { | |
325 | res.val.px.ip = (*f_rte)->net->n.prefix; | |
326 | res.val.px.len = (*f_rte)->net->n.pxlen; | |
327 | break; | |
328 | } | |
329 | default: | |
3076b5ae | 330 | bug( "Invalid type for rta access (%x)", res.type ); |
36bbfc70 PM |
331 | } |
332 | } | |
333 | break; | |
2d496d20 | 334 | case P('e','a'): /* Access to extended attributes */ |
91447965 | 335 | { |
0a06a9b8 | 336 | eattr *e = NULL; |
3076b5ae | 337 | if (!(f_flags & FF_FORCE_TMPATTR)) |
0a06a9b8 | 338 | e = ea_find( (*f_rte)->attrs->eattrs, what->a2.i ); |
31e79264 PM |
339 | if (!e) |
340 | e = ea_find( (*f_tmp_attrs), what->a2.i ); | |
3076b5ae | 341 | if ((!e) && (f_flags & FF_FORCE_TMPATTR)) |
0a06a9b8 PM |
342 | e = ea_find( (*f_rte)->attrs->eattrs, what->a2.i ); |
343 | ||
91447965 PM |
344 | if (!e) { |
345 | res.type = T_VOID; | |
346 | break; | |
347 | } | |
10a53608 | 348 | res.type = what->aux; /* FIXME: should check type? */ |
2803c9dd | 349 | switch (what->aux) { |
91447965 PM |
350 | case T_INT: |
351 | res.val.i = e->u.data; | |
352 | break; | |
10a53608 PM |
353 | case T_PATH: |
354 | res.val.ad = e->u.ptr; | |
355 | break; | |
2803c9dd MM |
356 | default: |
357 | bug("Unknown type in e,a\n"); | |
91447965 PM |
358 | } |
359 | } | |
6dc7a0cb | 360 | break; |
2d496d20 | 361 | case P('e','S'): |
f31156ca PM |
362 | ONEARG; |
363 | if (v1.type != what->aux) | |
3076b5ae | 364 | runtime("Wrong type when setting dynamic attribute"); |
f31156ca | 365 | |
f31156ca PM |
366 | { |
367 | struct ea_list *l = lp_alloc(f_pool, sizeof(struct ea_list) + sizeof(eattr)); | |
368 | ||
369 | l->next = NULL; | |
370 | l->flags = EALF_SORTED; | |
371 | l->count = 1; | |
372 | l->attrs[0].id = what->a2.i; | |
373 | l->attrs[0].flags = 0; | |
31e79264 PM |
374 | l->attrs[0].type = what->aux; |
375 | switch (what->aux & EAF_TYPE_MASK) { | |
376 | case EAF_TYPE_INT: | |
377 | if (v1.type != T_INT) | |
378 | runtime( "Setting int attribute to non-int value" ); | |
f31156ca PM |
379 | l->attrs[0].u.data = v1.val.i; |
380 | break; | |
10a53608 PM |
381 | case EAF_TYPE_AS_PATH: |
382 | if (v1.type != T_PATH) | |
383 | runtime( "Setting path attribute to non-path value" ); | |
384 | l->attrs[0].u.ptr = v1.val.ad; | |
385 | break; | |
31e79264 PM |
386 | case EAF_TYPE_UNDEF: |
387 | if (v1.type != T_VOID) | |
388 | runtime( "Setting void attribute to non-void value" ); | |
48f9e019 PM |
389 | l->attrs[0].u.data = 0; |
390 | break; | |
f31156ca | 391 | } |
31e79264 | 392 | |
3076b5ae MM |
393 | if (!(what->aux & EAF_TEMP) && (!(f_flags & FF_FORCE_TMPATTR))) { |
394 | *f_rte = rte_cow(*f_rte); | |
31e79264 PM |
395 | l->next = (*f_rte)->attrs->eattrs; |
396 | (*f_rte)->attrs->eattrs = l; | |
397 | } else { | |
398 | l->next = (*f_tmp_attrs); | |
399 | (*f_tmp_attrs) = l; | |
400 | } | |
f31156ca | 401 | } |
f31156ca | 402 | break; |
48f9e019 | 403 | |
2d496d20 | 404 | case P('c','p'): /* Convert prefix to ... */ |
36bbfc70 PM |
405 | ONEARG; |
406 | if (v1.type != T_PREFIX) | |
407 | runtime( "Can not convert non-prefix this way" ); | |
c7b43f33 | 408 | res.type = what->aux; |
36bbfc70 PM |
409 | switch(res.type) { |
410 | case T_INT: res.val.i = v1.val.px.len; break; | |
6dc7a0cb | 411 | case T_IP: res.val.px.ip = v1.val.px.ip; break; |
3076b5ae | 412 | default: bug( "Unknown prefix to conversion" ); |
36bbfc70 PM |
413 | } |
414 | break; | |
2d496d20 PM |
415 | case 'r': |
416 | ONEARG; | |
417 | res = v1; | |
418 | res.type |= T_RETURN; | |
419 | break; | |
420 | case P('c','a'): /* CALL: this is special: if T_RETURN and returning some value, mask it out */ | |
6542ece9 PM |
421 | ONEARG; |
422 | res = interpret(what->a2.p); | |
2d496d20 PM |
423 | if (res.type == T_RETURN) |
424 | return res; | |
425 | res.type &= ~T_RETURN; | |
6542ece9 | 426 | break; |
2d496d20 | 427 | case P('S','W'): |
7db7b7db | 428 | ONEARG; |
41be4444 PM |
429 | { |
430 | struct f_tree *t = find_tree(what->a2.p, v1); | |
431 | if (!t) { | |
432 | v1.type = T_VOID; | |
433 | t = find_tree(what->a2.p, v1); | |
434 | if (!t) { | |
3cf4a2e2 | 435 | debug( "No else statement?\n "); |
41be4444 PM |
436 | break; |
437 | } | |
438 | } | |
439 | if (!t->data) | |
3076b5ae | 440 | bug( "Impossible: no code associated!" ); |
41be4444 PM |
441 | return interpret(t->data); |
442 | } | |
7db7b7db | 443 | break; |
2d496d20 | 444 | case P('i','M'): /* IP.MASK(val) */ |
f4536657 PM |
445 | TWOARGS; |
446 | if (v2.type != T_INT) | |
447 | runtime( "Can not use this type for mask."); | |
448 | if (v1.type != T_IP) | |
449 | runtime( "You can mask only IP addresses." ); | |
450 | { | |
451 | ip_addr mask = ipa_mkmask(v2.val.i); | |
452 | res.type = T_IP; | |
453 | res.val.px.ip = ipa_and(mask, v1.val.px.ip); | |
454 | } | |
d3dd620b | 455 | break; |
afc54517 PM |
456 | |
457 | case 'E': /* Create empty attribute */ | |
458 | res.type = what->aux; | |
459 | res.val.ad = adata_empty(f_pool); | |
460 | break; | |
461 | case P('A','p'): /* Path prepend */ | |
462 | TWOARGS; | |
463 | if (v1.type != T_PATH) | |
464 | runtime("Can't prepend to non-path"); | |
465 | if (v2.type != T_INT) | |
466 | runtime("Can't prepend non-integer"); | |
467 | ||
468 | res.type = T_PATH; | |
469 | res.val.ad = as_path_prepend(f_pool, v1.val.ad, v2.val.i); | |
470 | break; | |
471 | ||
23b1539b PM |
472 | default: |
473 | bug( "Unknown instruction %d (%c)", what->code, what->code & 0xff); | |
474 | } | |
475 | if (what->next) | |
476 | return interpret(what->next); | |
477 | return res; | |
478 | } | |
479 | ||
2d496d20 | 480 | #undef ARG |
9a4037d4 PM |
481 | #define ARG(x,y) \ |
482 | if (!i_same(f1->y, f2->y)) \ | |
483 | return 0; | |
484 | ||
485 | #define ONEARG ARG(v1, a1.p) | |
486 | #define TWOARGS ARG(v1, a1.p) \ | |
487 | ARG(v2, a2.p) | |
488 | ||
489 | #define A2_SAME if (f1->a2.i != f2->a2.i) return 0; | |
490 | ||
491 | int | |
492 | i_same(struct f_inst *f1, struct f_inst *f2) | |
493 | { | |
9a4037d4 PM |
494 | if ((!!f1) != (!!f2)) |
495 | return 0; | |
d4d75628 PM |
496 | if (!f1) |
497 | return 1; | |
9a4037d4 PM |
498 | if (f1->aux != f2->aux) |
499 | return 0; | |
500 | if (f1->code != f2->code) | |
501 | return 0; | |
d4d75628 PM |
502 | if (f1 == f2) /* It looks strange, but it is possible with call rewriting trickery */ |
503 | return 1; | |
9a4037d4 PM |
504 | |
505 | switch(f1->code) { | |
506 | case ',': /* fall through */ | |
507 | case '+': | |
508 | case '/': | |
2d496d20 PM |
509 | case P('!','='): |
510 | case P('=','='): | |
9a4037d4 | 511 | case '<': |
2d496d20 | 512 | case P('<','='): TWOARGS; break; |
9a4037d4 | 513 | |
995e5894 | 514 | case '!': ONEARG; break; |
9a4037d4 | 515 | case '~': TWOARGS; break; |
2d496d20 | 516 | case P('d','e'): ONEARG; break; |
9a4037d4 PM |
517 | |
518 | case 's': | |
519 | ARG(v2, a2.p); | |
520 | { | |
521 | struct symbol *s1, *s2; | |
522 | s1 = f1->a1.p; | |
523 | s2 = f2->a1.p; | |
524 | if (strcmp(s1->name, s2->name)) | |
525 | return 0; | |
526 | if (s1->class != s2->class) | |
527 | return 0; | |
528 | } | |
529 | break; | |
530 | ||
531 | case 'c': A2_SAME; break; | |
532 | case 'C': | |
533 | if (val_compare(* (struct f_val *) f1->a1.p, * (struct f_val *) f2->a2.p)) | |
534 | return 0; | |
535 | break; | |
536 | case 'p': ONEARG; break; | |
537 | case '?': TWOARGS; break; | |
afc54517 | 538 | case '0': case 'E': break; |
2d496d20 | 539 | case P('p',','): ONEARG; A2_SAME; break; |
9a4037d4 | 540 | case 'a': A2_SAME; break; |
2d496d20 PM |
541 | case P('e','a'): A2_SAME; break; |
542 | case P('e','S'): ONEARG; A2_SAME; break; | |
9a4037d4 | 543 | |
2d496d20 PM |
544 | case 'r': ONEARG; break; |
545 | case P('c','p'): ONEARG; break; | |
d4d75628 PM |
546 | case P('c','a'): /* Call rewriting trickery to avoid exponential behaviour */ |
547 | ONEARG; | |
548 | if (!i_same(f1->a2.p, f2->a2.p)) | |
549 | return 0; | |
550 | f2->a2.p = f1->a2.p; | |
551 | break; | |
2d496d20 PM |
552 | case P('S','W'): ONEARG; if (!same_tree(f1->a2.p, f2->a2.p)) return 0; break; |
553 | case P('i','M'): TWOARGS; break; | |
afc54517 | 554 | case P('A','p'): TWOARGS; break; |
9a4037d4 PM |
555 | default: |
556 | bug( "Unknown instruction %d in same (%c)", f1->code, f1->code & 0xff); | |
557 | } | |
558 | return i_same(f1->next, f2->next); | |
559 | } | |
560 | ||
23b1539b | 561 | int |
0a06a9b8 | 562 | f_run(struct filter *filter, struct rte **rte, struct ea_list **tmp_attrs, struct linpool *tmp_pool, int flags) |
23b1539b PM |
563 | { |
564 | struct f_inst *inst; | |
565 | struct f_val res; | |
6b9fa320 | 566 | DBG( "Running filter `%s'...", filter->name ); |
23b1539b | 567 | |
0a06a9b8 | 568 | f_flags = flags; |
31e79264 | 569 | f_tmp_attrs = tmp_attrs; |
36bbfc70 | 570 | f_rte = rte; |
48f9e019 | 571 | f_rte_old = *rte; |
f31156ca | 572 | f_pool = tmp_pool; |
23b1539b PM |
573 | inst = filter->root; |
574 | res = interpret(inst); | |
575 | if (res.type != T_RETURN) | |
576 | return F_ERROR; | |
6b9fa320 | 577 | DBG( "done (%d)\n", res.val.i ); |
23b1539b PM |
578 | return res.val.i; |
579 | } | |
580 | ||
23b1539b PM |
581 | void |
582 | filters_postconfig(void) | |
583 | { | |
584 | struct f_val res; | |
8ba2cc06 | 585 | if (startup_func) { |
3cf4a2e2 | 586 | debug( "Launching startup function...\n" ); |
23b1539b | 587 | res = interpret(startup_func); |
bad631e0 | 588 | if (res.type == F_ERROR) |
8ba2cc06 | 589 | die( "Startup function resulted in error." ); |
3cf4a2e2 | 590 | debug( "done\n" ); |
8ba2cc06 | 591 | } |
7f77e250 | 592 | self_test(); |
23b1539b | 593 | } |
30a6108c MM |
594 | |
595 | int | |
596 | filter_same(struct filter *new, struct filter *old) | |
597 | { | |
81ce667b MM |
598 | if (old == new) /* Handle FILTER_ACCEPT and FILTER_REJECT */ |
599 | return 1; | |
600 | if (old == FILTER_ACCEPT || old == FILTER_REJECT || | |
601 | new == FILTER_ACCEPT || new == FILTER_REJECT) | |
602 | return 0; | |
9a4037d4 | 603 | return i_same(new->root, old->root); |
30a6108c | 604 | } |
7f77e250 | 605 | |
9196e9f8 PM |
606 | /* This should end up far away from here! |
607 | * | |
608 | * FIXME: It should take struct adata *, not u8 * + length; but that makes it a little more difficult to test. | |
609 | * Or maybe both versions are usefull? | |
610 | */ | |
7f77e250 PM |
611 | |
612 | int | |
613 | path_getlen(u8 *p, int len) | |
614 | { | |
615 | int res = 0; | |
616 | u8 *q = p+len; | |
617 | while (p<q) { | |
618 | switch (*p++) { | |
619 | case 1: len = *p++; res++; p += 2*len; break; | |
620 | case 2: len = *p++; res+=len; p += 2*len; break; | |
621 | default: bug("This should not be in path"); | |
622 | } | |
623 | } | |
624 | return res; | |
625 | } | |
626 | ||
627 | ||
78c6217c | 628 | #define PRINTF(a...) { int l; bsnprintf( s, bigbuf+4090-s, a ); s += strlen(s); } |
7f77e250 PM |
629 | #define COMMA if (first) first = 0; else PRINTF( ", " ); |
630 | char * | |
631 | path_format(u8 *p, int len) | |
632 | { | |
633 | char bigbuf[4096]; /* Keep it smaller than buf */ | |
78c6217c | 634 | char *s = bigbuf; |
7f77e250 PM |
635 | int first = 1; |
636 | int i; | |
637 | u8 *q = p+len; | |
7f77e250 PM |
638 | while (p<q) { |
639 | switch (*p++) { | |
640 | case 1: /* This is a set */ | |
641 | len = *p++; | |
642 | COMMA; | |
78c6217c | 643 | PRINTF( "{" ); |
7f77e250 PM |
644 | { |
645 | int first = 1; | |
646 | for (i=0; i<len; i++) { | |
647 | COMMA; | |
648 | PRINTF( "%d", get_u16(p)); | |
649 | p+=2; | |
650 | } | |
651 | } | |
78c6217c | 652 | PRINTF( "}" ); |
7f77e250 PM |
653 | break; |
654 | ||
655 | case 2: /* This is a sequence */ | |
656 | len = *p++; | |
657 | for (i=0; i<len; i++) { | |
658 | int l; | |
659 | COMMA; | |
660 | PRINTF( "%d", get_u16(p)); | |
661 | p+=2; | |
662 | } | |
663 | break; | |
664 | ||
665 | default: | |
666 | bug("This should not be in path"); | |
667 | } | |
668 | } | |
2803c9dd | 669 | return strdup(bigbuf); /* FIXME: who frees this? */ |
7f77e250 PM |
670 | } |
671 | #undef PRINTF | |
672 | #undef COMMA | |
673 | ||
674 | #define PM_END -1 | |
675 | #define PM_ASTERIX -2 | |
676 | ||
dcab7890 PM |
677 | #define MASK_PLUS do { mask = mask->next; if (mask->val == PM_END) return next == q; \ |
678 | asterix = (mask->val == PM_ASTERIX); \ | |
7f77e250 | 679 | printf( "Asterix now %d\n", asterix ); \ |
dcab7890 | 680 | if (asterix) { mask = mask->next; if (mask->val == PM_END) { printf( "Quick exit\n" ); return 1; } } \ |
7f77e250 | 681 | } while(0) |
dcab7890 | 682 | |
7f77e250 | 683 | int |
dcab7890 | 684 | path_match(u8 *p, int len, struct f_path_mask *mask) |
7f77e250 PM |
685 | { |
686 | int i; | |
687 | int asterix = 0; | |
688 | u8 *q = p+len; | |
689 | u8 *next; | |
690 | ||
691 | while (p<q) { | |
692 | switch (*p++) { | |
693 | case 1: /* This is a set */ | |
694 | len = *p++; | |
695 | { | |
696 | u8 *p_save = p; | |
697 | next = p_save + 2*len; | |
698 | retry: | |
699 | p = p_save; | |
700 | for (i=0; i<len; i++) { | |
dcab7890 | 701 | if (asterix && (get_u16(p) == mask->val)) { |
7f77e250 PM |
702 | MASK_PLUS; |
703 | goto retry; | |
704 | } | |
dcab7890 | 705 | if (!asterix && (get_u16(p) == mask->val)) { |
7f77e250 PM |
706 | p = next; |
707 | MASK_PLUS; | |
708 | goto okay; | |
709 | } | |
710 | p+=2; | |
711 | } | |
712 | if (!asterix) | |
713 | return 0; | |
714 | okay: | |
715 | } | |
716 | break; | |
717 | ||
718 | case 2: /* This is a sequence */ | |
719 | len = *p++; | |
720 | for (i=0; i<len; i++) { | |
721 | next = p+2; | |
dcab7890 | 722 | if (asterix && (get_u16(p) == mask->val)) |
7f77e250 PM |
723 | MASK_PLUS; |
724 | else if (!asterix) { | |
dcab7890 | 725 | if (get_u16(p) != mask->val) |
7f77e250 PM |
726 | return 0; |
727 | MASK_PLUS; | |
728 | } | |
729 | p+=2; | |
730 | } | |
731 | break; | |
732 | ||
733 | default: | |
734 | bug("This should not be in path"); | |
735 | } | |
736 | } | |
737 | return 0; | |
738 | } | |
739 | ||
740 | struct adata * | |
741 | comlist_add(struct linpool *pool, struct adata *list, u32 val) | |
742 | { | |
743 | struct adata *res = lp_alloc(pool, list->length + sizeof(struct adata) + 4); | |
744 | res->length = list->length+4; | |
745 | * (u32 *) res->data = val; | |
746 | memcpy((char *) res->data + 4, list->data, list->length); | |
747 | return res; | |
748 | } | |
749 | ||
750 | struct adata * | |
751 | comlist_contains(struct adata *list, u32 val) | |
752 | { | |
753 | u32 *l = &(list->data); | |
754 | int i; | |
755 | for (i=0; i<list->length/4; i++) | |
756 | if (*l++ == val) | |
757 | return 1; | |
758 | return 0; | |
759 | } | |
760 | ||
761 | struct adata * | |
762 | comlist_del(struct linpool *pool, struct adata *list, u32 val) | |
763 | { | |
764 | struct adata *res; | |
765 | u32 *l, *k; | |
766 | int i; | |
767 | ||
768 | if (!comlist_contains(list, val)) | |
769 | return list; | |
770 | ||
771 | res = lp_alloc(pool, list->length + sizeof(struct adata) - 4); | |
772 | res->length = list->length-4; | |
773 | ||
774 | l = &(list->data); | |
775 | k = &(res->data); | |
776 | for (i=0; i<list->length/4; i++) | |
777 | if (l[i] != val) | |
778 | *k++ = l[i]; | |
779 | ||
780 | return res; | |
781 | } | |
782 | ||
783 | struct adata * | |
0a40e973 | 784 | adata_empty(struct linpool *pool) |
7f77e250 PM |
785 | { |
786 | struct adata *res = lp_alloc(pool, sizeof(struct adata)); | |
787 | res->length = 0; | |
788 | return res; | |
789 | } | |
790 | ||
791 | void | |
792 | self_test(void) | |
793 | { | |
794 | char path1[] = { 2, 5, 0, 5, 0, 4, 0, 3, 0, 2, 0, 1 }; | |
795 | char path2[] = { 2, 5, 0, 5, 0, 4, 0, 3, 0, 2, 0, 1, 1, 5, 0, 5, 0, 4, 0, 3, 0, 2, 0, 1 }; | |
796 | s32 match[] = { 5, PM_ASTERIX, 2, PM_ASTERIX, 1, 3, PM_END }; | |
797 | ||
798 | DBG( "Filters self-testing:\n" ); | |
799 | DBG( "%s\n", path_format(path1, sizeof(path1)) ); | |
800 | DBG( "%s\n", path_format(path2, sizeof(path2)) ); | |
801 | DBG( "5, 6 = %d, %d\n", path_getlen(path1, sizeof(path1)), path_getlen(path2, sizeof(path2)) ); | |
dcab7890 PM |
802 | // DBG( "%d\n", path_match(path1, sizeof(path1), match)); |
803 | // DBG( "%d\n", path_match(path2, sizeof(path2), match)); | |
7f77e250 PM |
804 | // die( "okay" ); |
805 | } |