]> git.ipfire.org Git - thirdparty/bird.git/blame - filter/config.Y
GDB pretty printers: f_inst and f_val.
[thirdparty/bird.git] / filter / config.Y
CommitLineData
b9d70dc8
PM
1/*
2 * BIRD - filters
3 *
1c20608e 4 * Copyright 1998--2000 Pavel Machek
b9d70dc8
PM
5 *
6 * Can be freely distributed and used under the terms of the GNU GPL.
c9f8c1a8 7 *
1877dab2 8 FIXME: priority of ! should be lower
b9d70dc8
PM
9 */
10
11CF_HDR
12
2edb31b0
MM
13CF_DEFINES
14
b8cc390e
OZ
15static inline u32 pair(u32 a, u32 b) { return (a << 16) | b; }
16static inline u32 pair_a(u32 p) { return p >> 16; }
17static inline u32 pair_b(u32 p) { return p & 0xFFFF; }
18
9b46748d
MM
19#define f_generate_complex(fi_code, da, arg) \
20 f_new_inst(FI_EA_SET, da, f_new_inst(fi_code, f_new_inst(FI_EA_GET, da), arg))
b8cc390e
OZ
21
22/*
23 * Sets and their items are during parsing handled as lists, linked
24 * through left ptr. The first item in a list also contains a pointer
25 * to the last item in a list (right ptr). For convenience, even items
26 * are handled as one-item lists. Lists are merged by f_merge_items().
27 */
b2f00837
OZ
28static int
29f_valid_set_type(int type)
30{
31 switch (type)
32 {
33 case T_INT:
34 case T_PAIR:
35 case T_QUAD:
36 case T_ENUM:
37 case T_IP:
38 case T_EC:
66dbdbd9 39 case T_LC:
83715aa8 40 case T_RD:
b2f00837
OZ
41 return 1;
42
43 default:
44 return 0;
45 }
46}
b8cc390e
OZ
47
48static inline struct f_tree *
49f_new_item(struct f_val from, struct f_val to)
92a72a4c 50{
b8cc390e
OZ
51 struct f_tree *t = f_new_tree();
52 t->right = t;
53 t->from = from;
54 t->to = to;
55 return t;
56}
92a72a4c 57
b8cc390e
OZ
58static inline struct f_tree *
59f_merge_items(struct f_tree *a, struct f_tree *b)
60{
61 if (!a) return b;
62 a->right->left = b;
63 a->right = b->right;
64 b->right = NULL;
65 return a;
66}
92a72a4c 67
b8cc390e
OZ
68static inline struct f_tree *
69f_new_pair_item(int fa, int ta, int fb, int tb)
70{
60566c5c
OZ
71 check_u16(fa);
72 check_u16(ta);
73 check_u16(fb);
74 check_u16(tb);
75
76 if ((ta < fa) || (tb < fb))
77 cf_error( "From value cannot be higher that To value in pair sets");
78
b8cc390e
OZ
79 struct f_tree *t = f_new_tree();
80 t->right = t;
81 t->from.type = t->to.type = T_PAIR;
82 t->from.val.i = pair(fa, fb);
83 t->to.val.i = pair(ta, tb);
84 return t;
92a72a4c
OZ
85}
86
b8cc390e
OZ
87static inline struct f_tree *
88f_new_pair_set(int fa, int ta, int fb, int tb)
4fc36f39 89{
60566c5c
OZ
90 check_u16(fa);
91 check_u16(ta);
92 check_u16(fb);
93 check_u16(tb);
c454872f 94
b8cc390e
OZ
95 if ((ta < fa) || (tb < fb))
96 cf_error( "From value cannot be higher that To value in pair sets");
c454872f 97
60566c5c
OZ
98 struct f_tree *lst = NULL;
99 int i;
100
b8cc390e
OZ
101 for (i = fa; i <= ta; i++)
102 lst = f_merge_items(lst, f_new_pair_item(i, i, fb, tb));
103
104 return lst;
4fc36f39
OF
105}
106
60566c5c 107#define CC_ALL 0xFFFF
42a0c054 108#define EC_ALL 0xFFFFFFFF
60566c5c 109#define LC_ALL 0xFFFFFFFF
42a0c054
OZ
110
111static struct f_tree *
112f_new_ec_item(u32 kind, u32 ipv4_used, u32 key, u32 vf, u32 vt)
113{
114 u64 fm, to;
115
e46128fb 116 if ((kind != EC_GENERIC) && (ipv4_used || (key >= 0x10000))) {
42a0c054
OZ
117 check_u16(vf);
118 if (vt == EC_ALL)
119 vt = 0xFFFF;
120 else
121 check_u16(vt);
122 }
123
124 if (kind == EC_GENERIC) {
125 fm = ec_generic(key, vf);
126 to = ec_generic(key, vt);
127 }
128 else if (ipv4_used) {
129 fm = ec_ip4(kind, key, vf);
130 to = ec_ip4(kind, key, vt);
131 }
132 else if (key < 0x10000) {
133 fm = ec_as2(kind, key, vf);
134 to = ec_as2(kind, key, vt);
135 }
136 else {
137 fm = ec_as4(kind, key, vf);
138 to = ec_as4(kind, key, vt);
139 }
140
141 struct f_tree *t = f_new_tree();
142 t->right = t;
143 t->from.type = t->to.type = T_EC;
144 t->from.val.ec = fm;
145 t->to.val.ec = to;
146 return t;
147}
148
60566c5c
OZ
149static struct f_tree *
150f_new_lc_item(u32 f1, u32 t1, u32 f2, u32 t2, u32 f3, u32 t3)
151{
152 struct f_tree *t = f_new_tree();
153 t->right = t;
154 t->from.type = t->to.type = T_LC;
155 t->from.val.lc = (lcomm) {f1, f2, f3};
156 t->to.val.lc = (lcomm) {t1, t2, t3};
157 return t;
158}
159
42a0c054 160static inline struct f_inst *
5a14df39 161f_generate_empty(struct f_dynamic_attr dyn)
5e173e9f 162{
9b46748d 163 struct f_val empty;
42a0c054 164
5a14df39 165 switch (dyn.type & EAF_TYPE_MASK) {
42a0c054 166 case EAF_TYPE_AS_PATH:
9b46748d 167 empty = f_const_empty_path;
42a0c054
OZ
168 break;
169 case EAF_TYPE_INT_SET:
9b46748d 170 empty = f_const_empty_clist;
42a0c054
OZ
171 break;
172 case EAF_TYPE_EC_SET:
9b46748d 173 empty = f_const_empty_eclist;
42a0c054 174 break;
66dbdbd9 175 case EAF_TYPE_LC_SET:
9b46748d 176 empty = f_const_empty_lclist;
66dbdbd9 177 break;
42a0c054
OZ
178 default:
179 cf_error("Can't empty that attribute");
180 }
181
9b46748d 182 return f_new_inst(FI_EA_SET, dyn, f_new_inst(FI_CONSTANT, empty));
42a0c054
OZ
183}
184
9b46748d 185#if 0
42a0c054
OZ
186
187static inline struct f_inst *
188f_generate_dpair(struct f_inst *t1, struct f_inst *t2)
189{
190 struct f_inst *rv;
191
5a14df39 192 if ((t1->fi_code == FI_CONSTANT) && (t2->fi_code == FI_CONSTANT)) {
ca2ee91a 193 if ((t1->val.type != T_INT) || (t2->val.type != T_INT))
42a0c054
OZ
194 cf_error( "Can't operate with value of non-integer type in pair constructor");
195
7f0ac737
MM
196 check_u16(t1->a[1].i);
197 check_u16(t2->a[1].i);
42a0c054 198
5a14df39 199 rv = f_new_inst(FI_CONSTANT);
ca2ee91a
MM
200 rv->val = (struct f_val) {
201 .type = T_PAIR,
202 .val.i = pair(t1->a[1].i, t2->a[1].i),
203 };
42a0c054
OZ
204 }
205 else {
5a14df39 206 rv = f_new_inst(FI_PAIR_CONSTRUCT);
7f0ac737
MM
207 rv->a[0].p = t1;
208 rv->a[1].p = t2;
42a0c054
OZ
209 }
210
211 return rv;
212}
213
214static inline struct f_inst *
215f_generate_ec(u16 kind, struct f_inst *tk, struct f_inst *tv)
216{
217 struct f_inst *rv;
218 int c1 = 0, c2 = 0, ipv4_used = 0;
219 u32 key = 0, val2 = 0;
220
5a14df39 221 if (tk->fi_code == FI_CONSTANT) {
42a0c054 222 c1 = 1;
ca2ee91a 223 struct f_val *val = &(tk->val);
1103b32e
OZ
224
225 if (val->type == T_INT) {
226 ipv4_used = 0; key = val->val.i;
227 }
ca2ee91a 228 else if (tk->val.type == T_QUAD) {
1103b32e
OZ
229 ipv4_used = 1; key = val->val.i;
230 }
5e173e9f
JMM
231 else if ((val->type == T_IP) && ipa_is_ip4(val->val.ip)) {
232 ipv4_used = 1; key = ipa_to_u32(val->val.ip);
42a0c054
OZ
233 }
234 else
235 cf_error("Can't operate with key of non-integer/IPv4 type in EC constructor");
236 }
42a0c054 237
5a14df39 238 if (tv->fi_code == FI_CONSTANT) {
ca2ee91a 239 if (tv->val.type != T_INT)
42a0c054
OZ
240 cf_error("Can't operate with value of non-integer type in EC constructor");
241 c2 = 1;
ca2ee91a 242 val2 = tv->val.val.i;
42a0c054
OZ
243 }
244
245 if (c1 && c2) {
246 u64 ec;
5e173e9f 247
42a0c054
OZ
248 if (kind == EC_GENERIC) {
249 ec = ec_generic(key, val2);
250 }
251 else if (ipv4_used) {
252 check_u16(val2);
253 ec = ec_ip4(kind, key, val2);
254 }
255 else if (key < 0x10000) {
256 ec = ec_as2(kind, key, val2);
257 }
258 else {
259 check_u16(val2);
260 ec = ec_as4(kind, key, val2);
261 }
262
ca2ee91a
MM
263 rv = f_new_inst(FI_CONSTANT);
264 rv->val = (struct f_val) {
265 .type = T_EC,
266 .val.ec = ec,
267 };
42a0c054
OZ
268 }
269 else {
5a14df39 270 rv = f_new_inst(FI_EC_CONSTRUCT);
42a0c054 271 rv->aux = kind;
7f0ac737
MM
272 rv->a[0].p = tk;
273 rv->a[1].p = tv;
42a0c054
OZ
274 }
275
276 return rv;
78e33c29 277}
42a0c054 278
66dbdbd9
OZ
279static inline struct f_inst *
280f_generate_lc(struct f_inst *t1, struct f_inst *t2, struct f_inst *t3)
281{
282 struct f_inst *rv;
283
5a14df39 284 if ((t1->fi_code == FI_CONSTANT) && (t2->fi_code == FI_CONSTANT) && (t3->fi_code == FI_CONSTANT)) {
ca2ee91a 285 if ((t1->val.type != T_INT) || (t2->val.type != T_INT) || (t3->val.type != T_INT))
66dbdbd9
OZ
286 cf_error( "LC - Can't operate with value of non-integer type in tuple constructor");
287
ca2ee91a
MM
288 rv = f_new_inst(FI_CONSTANT);
289 rv->val = (struct f_val) {
290 .type = T_LC,
291 .val.lc = (lcomm) { t1->a[1].i, t2->a[1].i, t3->a[1].i },
292 };
66dbdbd9
OZ
293 }
294 else
295 {
478f9bab 296 rv = f_new_inst(FI_LC_CONSTRUCT);
7f0ac737
MM
297 rv->a[0].p = t1;
298 rv->a[1].p = t2;
299 rv->a[2].p = t3;
66dbdbd9
OZ
300 }
301
302 return rv;
303}
304
e8bc64e3 305static inline struct f_inst *
4c553c5a 306f_generate_path_mask(struct f_inst *t)
e8bc64e3 307{
4c553c5a
MM
308 uint len = 0;
309 uint dyn = 0;
310 for (const struct f_inst *tt = t; tt; tt = tt->next) {
311 if (tt->fi_code != FI_CONSTANT)
312 dyn++;
313 len++;
314 }
315
316 if (dyn) {
317 struct f_inst *pmc = f_new_inst(FI_PATHMASK_CONSTRUCT);
318 pmc->a[0].p = t;
319 pmc->a[1].i = len;
320 return pmc;
e8bc64e3
JMM
321 }
322
4c553c5a 323 struct f_path_mask *pm = cfg_allocz(sizeof(struct f_path_mask) + len * sizeof(struct f_path_mask_item));
e8bc64e3 324
4c553c5a
MM
325 uint i = 0;
326 for (const struct f_inst *tt = t; tt; tt = tt->next)
327 pm->item[i++] = tt->val.val.pmi;
328
329 pm->len = i;
330 struct f_inst *pmc = f_new_inst(FI_CONSTANT);
331 pmc->val = (struct f_val) { .type = T_PATH_MASK, .val.path_mask = pm, };
332
333 return pmc;
e8bc64e3 334}
42a0c054 335
9b46748d
MM
336#endif
337
9b0a0ba9
OZ
338/*
339 * Remove all new lines and doubled whitespaces
340 * and convert all tabulators to spaces
341 * and return a copy of string
342 */
343char *
344assert_copy_expr(const char *start, size_t len)
345{
346 /* XXX: Allocates maybe a little more memory than we really finally need */
347 char *str = cfg_alloc(len + 1);
348
349 char *dst = str;
350 const char *src = start - 1;
351 const char *end = start + len;
352 while (++src < end)
353 {
354 if (*src == '\n')
355 continue;
356
357 /* Skip doubled whitespaces */
358 if (src != start)
359 {
360 const char *prev = src - 1;
361 if ((*src == ' ' || *src == '\t') && (*prev == ' ' || *prev == '\t'))
362 continue;
363 }
364
365 if (*src == '\t')
366 *dst = ' ';
367 else
368 *dst = *src;
369
370 dst++;
371 }
372 *dst = '\0';
373
374 return str;
375}
376
377/*
378 * assert_done - create f_instruction of bt_assert
379 * @expr: expression in bt_assert()
380 * @start: pointer to first char of test expression
381 * @end: pointer to the last char of test expression
382 */
383static struct f_inst *
384assert_done(struct f_inst *expr, const char *start, const char *end)
385{
9b46748d
MM
386 return f_new_inst(FI_ASSERT, expr,
387 (end >= start) ?
388 assert_copy_expr(start, end - start + 1)
389 : "???");
9b0a0ba9 390}
42a0c054 391
b9d70dc8
PM
392CF_DECLS
393
e4a73dbf 394CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
ba921648 395 ACCEPT, REJECT, ERROR, QUITBIRD,
8c9986d3 396 INT, BOOL, IP, TYPE, PREFIX, RD, PAIR, QUAD, EC, LC,
66dbdbd9 397 SET, STRING, BGPMASK, BGPPATH, CLIST, ECLIST, LCLIST,
7db7b7db 398 IF, THEN, ELSE, CASE,
42a0c054 399 TRUE, FALSE, RT, RO, UNKNOWN, GENERIC,
5b208e29 400 FROM, GW, NET, MASK, PROTO, SOURCE, SCOPE, DEST, IFNAME, IFINDEX,
a5fc5958 401 PREFERENCE,
b24b7811 402 ROA_CHECK, ASN, SRC,
61e501da 403 IS_V4, IS_V6,
e58f8c28 404 LEN, MAXLEN,
f4536657 405 DEFINED,
7f77e250 406 ADD, DELETE, CONTAINS, RESET,
9c9cc35c 407 PREPEND, FIRST, LAST, LAST_NONAGGREGATED, MATCH,
afc54517 408 EMPTY,
265419a3 409 FILTER, WHERE, EVAL, ATTRIBUTE,
4b135d09 410 BT_ASSERT, BT_TEST_SUITE, FORMAT)
b9d70dc8 411
f4536657 412%nonassoc THEN
4ed8718a 413%nonassoc ELSE
f4536657 414
9b46748d
MM
415%type <xc> function_params declsn
416%type <xp> cmds_int function_body
417%type <x> term block cmd cmds constant constructor print_one print_list var_list var_listn function_call symbol bgp_path_expr bgp_path bgp_path_tail one_decl decls
5a14df39
MJM
418%type <fda> dynamic_attr
419%type <fsa> static_attr
430da60f 420%type <f> filter filter_body where_filter
9b46748d
MM
421%type <i> type
422%type <ecs> ec_kind
423%type <fret> break_command
60566c5c
OZ
424%type <i32> cnum
425%type <e> pair_item ec_item lc_item set_item switch_item set_items switch_items switch_body
b1a597e0 426%type <trie> fprefix_set
5e173e9f
JMM
427%type <v> set_atom switch_atom fipa
428%type <px> fprefix
9b0a0ba9 429%type <t> get_cf_position
b9d70dc8
PM
430
431CF_GRAMMAR
432
f851f0d7 433conf: filter_def ;
e0f2e42f 434filter_def:
b2b7bbfc
OZ
435 FILTER SYM { $2 = cf_define_symbol($2, SYM_FILTER, NULL); cf_push_scope( $2 ); }
436 filter_body {
437 $2->def = $4;
ae3e1af2 438 $4->name = $2->name;
d4d75628 439 DBG( "We have new filter defined (%s)\n", $2->name );
ae3e1af2 440 cf_pop_scope();
b9d70dc8
PM
441 }
442 ;
443
f851f0d7 444conf: filter_eval ;
1c20608e 445filter_eval:
4c553c5a 446 EVAL term { f_eval_int(f_postfixify($2)); }
1c20608e
MM
447 ;
448
265419a3
MM
449conf: custom_attr ;
450custom_attr: ATTRIBUTE type SYM ';' {
451 cf_define_symbol($3, SYM_ATTRIBUTE, ca_lookup(new_config->pool, $3->name, $2)->fda);
452};
453
f851f0d7 454conf: bt_test_suite ;
9b0a0ba9
OZ
455bt_test_suite:
456 BT_TEST_SUITE '(' SYM ',' text ')' {
457 if (!($3->class & SYM_FUNCTION))
458 cf_error("Function expected");
459
460 struct f_bt_test_suite *t = cfg_alloc(sizeof(struct f_bt_test_suite));
461 t->fn = $3->def;
462 t->fn_name = $3->name;
463 t->dsc = $5;
464
465 add_tail(&new_config->tests, &t->n);
466 }
467 ;
468
ba921648
PM
469type:
470 INT { $$ = T_INT; }
471 | BOOL { $$ = T_BOOL; }
472 | IP { $$ = T_IP; }
8c9986d3 473 | RD { $$ = T_RD; }
5e173e9f 474 | PREFIX { $$ = T_NET; }
ba921648 475 | PAIR { $$ = T_PAIR; }
126683fe 476 | QUAD { $$ = T_QUAD; }
42a0c054 477 | EC { $$ = T_EC; }
66dbdbd9 478 | LC { $$ = T_LC; }
ba921648 479 | STRING { $$ = T_STRING; }
10a53608
PM
480 | BGPMASK { $$ = T_PATH_MASK; }
481 | BGPPATH { $$ = T_PATH; }
482 | CLIST { $$ = T_CLIST; }
42a0c054 483 | ECLIST { $$ = T_ECLIST; }
66dbdbd9 484 | LCLIST { $$ = T_LCLIST; }
c8cafc8e 485 | type SET {
ba921648 486 switch ($1) {
b1a597e0 487 case T_INT:
b1a597e0 488 case T_PAIR:
126683fe 489 case T_QUAD:
42a0c054 490 case T_EC:
66dbdbd9 491 case T_LC:
83715aa8 492 case T_RD:
126683fe 493 case T_IP:
b1a597e0
OZ
494 $$ = T_SET;
495 break;
496
5e173e9f 497 case T_NET:
b1a597e0
OZ
498 $$ = T_PREFIX_SET;
499 break;
500
ba921648 501 default:
a5a947d4 502 cf_error( "You can't create sets of this type." );
ba921648 503 }
b1a597e0 504 }
ba921648
PM
505 ;
506
6dc7a0cb
PM
507one_decl:
508 type SYM {
4ee39ff2
OZ
509 struct f_val * val = cfg_alloc(sizeof(struct f_val));
510 val->type = T_VOID;
083c43e2 511 $2 = cf_define_symbol($2, SYM_VARIABLE | $1, val);
d4d75628 512 DBG( "New variable %s type %x\n", $2->name, $1 );
9b46748d 513 $$ = f_new_inst(FI_SET, NULL, $2);
ba921648
PM
514 }
515 ;
516
6dc7a0cb
PM
517/* Decls with ';' at the end */
518decls: /* EMPTY */ { $$ = NULL; }
519 | one_decl ';' decls {
520 $$ = $1;
9b46748d 521 f_inst_next($$, $3);
6dc7a0cb
PM
522 }
523 ;
524
9b46748d
MM
525/* Declarations that have no ';' at the end. Beware; these are reversed. */
526declsn: one_decl { $$.inst = $1; $$.count = 1; }
736fd730 527 | one_decl ';' declsn {
9b46748d
MM
528 $$ = $3;
529 $$.count++;
530 f_inst_next($$.inst, $1);
6dc7a0cb
PM
531 }
532 ;
533
e0f2e42f 534filter_body:
ba921648 535 function_body {
9b46748d
MM
536 $$ = cfg_alloc(sizeof(struct filter));
537 $$->name = NULL;
538 if ($1[0]) {
539 const struct f_inst *inst[2] = { $1[0], $1[1] };
540 $$->root = f_postfixify_concat(inst, 2);
541 }
542 else
543 $$->root = f_postfixify($1[1]);
e0f2e42f
MM
544 }
545 ;
546
547filter:
548 SYM {
a5a947d4 549 if ($1->class != SYM_FILTER) cf_error("No such filter.");
e0f2e42f
MM
550 $$ = $1->def;
551 }
552 | filter_body
553 ;
554
430da60f
MM
555where_filter:
556 WHERE term {
224b77d4 557 /* Construct 'IF term THEN { ACCEPT; } ELSE { REJECT; }' */
9b46748d 558 $$ = f_new_where($2);
4c553c5a 559 }
430da60f
MM
560 ;
561
ba921648 562function_params:
9b46748d
MM
563 '(' declsn ')' { $$ = $2; }
564 | '(' ')' { $$.inst = NULL; $$.count = 0; }
ba921648 565 ;
b9d70dc8 566
ba921648
PM
567function_body:
568 decls '{' cmds '}' {
9b46748d
MM
569 $$[0] = $1 ? f_clear_local_vars($1) : NULL;
570 $$[1] = $3;
84c7e194 571 }
ba921648
PM
572 ;
573
f851f0d7 574conf: function_def ;
ba921648 575function_def:
bf3eb98e
MM
576 FUNCTION SYM { DBG( "Beginning of function %s\n", $2->name );
577 $2 = cf_define_symbol($2, SYM_FUNCTION, NULL);
578 cf_push_scope($2);
579 } function_params function_body {
9b46748d
MM
580 const struct f_inst *catlist[4];
581 uint count = 0;
582
583 /* Argument setters */
584 if ($4.inst)
585 catlist[count++] = $4.inst;
586
587 /* Local var clearers */
588 if ($5[0])
589 catlist[count++] = $5[0];
590
591 /* Return void if no return is needed */
592 catlist[count++] = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_VOID });
593
594 /* Function body itself */
595 if ($5[1])
596 catlist[count++] = $5[1];
597
598 $2->def = f_postfixify_concat(catlist, count);
599 $2->aux2 = $4.count;
c8cafc8e 600 DBG("Hmm, we've got one function here - %s\n", $2->name);
ae3e1af2 601 cf_pop_scope();
ba921648
PM
602 }
603 ;
604
605/* Programs */
606
607cmds: /* EMPTY */ { $$ = NULL; }
9b46748d 608 | cmds_int { $$ = $1[0]; }
5f47c4c1
OZ
609 ;
610
9b46748d
MM
611cmds_int: cmd { $$[0] = $$[1] = $1; }
612 | cmds_int cmd { $$[1] = $2; f_inst_next($1[1], $2); $$[0] = $1[0]; }
84c7e194
PM
613 ;
614
2575593e 615block:
ba921648 616 cmd {
2575593e
PM
617 $$=$1;
618 }
619 | '{' cmds '}' {
620 $$=$2;
621 }
622 ;
623
d3dd620b
PM
624/*
625 * Complex types, their bison value is struct f_val
626 */
e3f2d5fc 627fipa:
04632fd7
OZ
628 IP4 %prec PREFIX_DUMMY { $$.type = T_IP; $$.val.ip = ipa_from_ip4($1); }
629 | IP6 %prec PREFIX_DUMMY { $$.type = T_IP; $$.val.ip = ipa_from_ip6($1); }
d3dd620b
PM
630 ;
631
b8cc390e
OZ
632
633
634/*
635 * Set constants. They are also used in switch cases. We use separate
636 * nonterminals for switch (set_atom/switch_atom, set_item/switch_item ...)
637 * to elude a collision between symbol (in expr) in set_atom and symbol
638 * as a function call in switch case cmds.
639 */
640
38506f71 641set_atom:
83715aa8
OZ
642 NUM { $$.type = T_INT; $$.val.i = $1; }
643 | fipa { $$ = $1; }
644 | VPN_RD { $$.type = T_RD; $$.val.ec = $1; }
645 | ENUM { $$.type = pair_a($1); $$.val.i = pair_b($1); }
b2f00837 646 | '(' term ')' {
4c553c5a 647 if (f_eval(f_postfixify($2), cfg_mem, &($$)) > F_RETURN) cf_error("Runtime error");
b2f00837
OZ
648 if (!f_valid_set_type($$.type)) cf_error("Set-incompatible type");
649 }
650 | SYM {
651 if (!cf_symbol_is_constant($1)) cf_error("%s: constant expected", $1->name);
652 if (!f_valid_set_type(SYM_TYPE($1))) cf_error("%s: set-incompatible type", $1->name);
653 $$ = *(struct f_val *)($1->def);
654 }
b8cc390e 655 ;
38506f71 656
b8cc390e
OZ
657switch_atom:
658 NUM { $$.type = T_INT; $$.val.i = $1; }
4c553c5a 659 | '(' term ')' { $$.type = T_INT; $$.val.i = f_eval_int(f_postfixify($2)); }
b8cc390e
OZ
660 | fipa { $$ = $1; }
661 | ENUM { $$.type = pair_a($1); $$.val.i = pair_b($1); }
662 ;
663
60566c5c 664cnum:
4c553c5a 665 term { $$ = f_eval_int(f_postfixify($1)); }
b8cc390e
OZ
666
667pair_item:
60566c5c
OZ
668 '(' cnum ',' cnum ')' { $$ = f_new_pair_item($2, $2, $4, $4); }
669 | '(' cnum ',' cnum DDOT cnum ')' { $$ = f_new_pair_item($2, $2, $4, $6); }
670 | '(' cnum ',' '*' ')' { $$ = f_new_pair_item($2, $2, 0, CC_ALL); }
671 | '(' cnum DDOT cnum ',' cnum ')' { $$ = f_new_pair_set($2, $4, $6, $6); }
672 | '(' cnum DDOT cnum ',' cnum DDOT cnum ')' { $$ = f_new_pair_set($2, $4, $6, $8); }
673 | '(' cnum DDOT cnum ',' '*' ')' { $$ = f_new_pair_item($2, $4, 0, CC_ALL); }
674 | '(' '*' ',' cnum ')' { $$ = f_new_pair_set(0, CC_ALL, $4, $4); }
675 | '(' '*' ',' cnum DDOT cnum ')' { $$ = f_new_pair_set(0, CC_ALL, $4, $6); }
676 | '(' '*' ',' '*' ')' { $$ = f_new_pair_item(0, CC_ALL, 0, CC_ALL); }
677 | '(' cnum ',' cnum ')' DDOT '(' cnum ',' cnum ')'
678 { $$ = f_new_pair_item($2, $8, $4, $10); }
38506f71
PM
679 ;
680
42a0c054
OZ
681ec_kind:
682 RT { $$ = EC_RT; }
683 | RO { $$ = EC_RO; }
684 | UNKNOWN NUM { $$ = $2; }
685 | GENERIC { $$ = EC_GENERIC; }
686 ;
687
688ec_item:
60566c5c
OZ
689 '(' ec_kind ',' cnum ',' cnum ')' { $$ = f_new_ec_item($2, 0, $4, $6, $6); }
690 | '(' ec_kind ',' cnum ',' cnum DDOT cnum ')' { $$ = f_new_ec_item($2, 0, $4, $6, $8); }
691 | '(' ec_kind ',' cnum ',' '*' ')' { $$ = f_new_ec_item($2, 0, $4, 0, EC_ALL); }
42a0c054
OZ
692 ;
693
60566c5c
OZ
694lc_item:
695 '(' cnum ',' cnum ',' cnum ')' { $$ = f_new_lc_item($2, $2, $4, $4, $6, $6); }
696 | '(' cnum ',' cnum ',' cnum DDOT cnum ')' { $$ = f_new_lc_item($2, $2, $4, $4, $6, $8); }
697 | '(' cnum ',' cnum ',' '*' ')' { $$ = f_new_lc_item($2, $2, $4, $4, 0, LC_ALL); }
698 | '(' cnum ',' cnum DDOT cnum ',' '*' ')' { $$ = f_new_lc_item($2, $2, $4, $6, 0, LC_ALL); }
699 | '(' cnum ',' '*' ',' '*' ')' { $$ = f_new_lc_item($2, $2, 0, LC_ALL, 0, LC_ALL); }
700 | '(' cnum DDOT cnum ',' '*' ',' '*' ')' { $$ = f_new_lc_item($2, $4, 0, LC_ALL, 0, LC_ALL); }
701 | '(' '*' ',' '*' ',' '*' ')' { $$ = f_new_lc_item(0, LC_ALL, 0, LC_ALL, 0, LC_ALL); }
702 | '(' cnum ',' cnum ',' cnum ')' DDOT '(' cnum ',' cnum ',' cnum ')'
703 { $$ = f_new_lc_item($2, $10, $4, $12, $6, $14); }
704;
42a0c054 705
b8cc390e
OZ
706set_item:
707 pair_item
42a0c054 708 | ec_item
60566c5c 709 | lc_item
b8cc390e
OZ
710 | set_atom { $$ = f_new_item($1, $1); }
711 | set_atom DDOT set_atom { $$ = f_new_item($1, $3); }
712 ;
713
714switch_item:
715 pair_item
42a0c054 716 | ec_item
60566c5c 717 | lc_item
b8cc390e
OZ
718 | switch_atom { $$ = f_new_item($1, $1); }
719 | switch_atom DDOT switch_atom { $$ = f_new_item($1, $3); }
720 ;
721
38506f71 722set_items:
b8cc390e
OZ
723 set_item
724 | set_items ',' set_item { $$ = f_merge_items($1, $3); }
725 ;
726
727switch_items:
728 switch_item
729 | switch_items ',' switch_item { $$ = f_merge_items($1, $3); }
38506f71
PM
730 ;
731
b1a597e0 732fprefix:
04632fd7
OZ
733 net_ip_ { $$.net = $1; $$.lo = $1.pxlen; $$.hi = $1.pxlen; }
734 | net_ip_ '+' { $$.net = $1; $$.lo = $1.pxlen; $$.hi = net_max_prefix_length[$1.type]; }
735 | net_ip_ '-' { $$.net = $1; $$.lo = 0; $$.hi = $1.pxlen; }
736 | net_ip_ '{' NUM ',' NUM '}' {
5e173e9f 737 $$.net = $1; $$.lo = $3; $$.hi = $5;
6aaaa635
OZ
738 if (($3 > $5) || ($5 > net_max_prefix_length[$1.type]))
739 cf_error("Invalid prefix pattern range: {%u, %u}", $3, $5);
b1a597e0
OZ
740 }
741 ;
742
743fprefix_set:
04632fd7
OZ
744 fprefix { $$ = f_new_trie(cfg_mem, sizeof(struct f_trie_node)); trie_add_prefix($$, &($1.net), $1.lo, $1.hi); }
745 | fprefix_set ',' fprefix { $$ = $1; trie_add_prefix($$, &($3.net), $3.lo, $3.hi); }
b1a597e0
OZ
746 ;
747
41be4444 748switch_body: /* EMPTY */ { $$ = NULL; }
b8cc390e
OZ
749 | switch_body switch_items ':' cmds {
750 /* Fill data fields */
751 struct f_tree *t;
4c553c5a 752 struct f_line *line = f_postfixify($4);
b8cc390e 753 for (t = $2; t; t = t->left)
4c553c5a 754 t->data = line;
b8cc390e 755 $$ = f_merge_items($1, $2);
41be4444 756 }
5e173e9f 757 | switch_body ELSECOL cmds {
b8cc390e
OZ
758 struct f_tree *t = f_new_tree();
759 t->from.type = t->to.type = T_VOID;
760 t->right = t;
4c553c5a 761 t->data = f_postfixify($3);
b8cc390e
OZ
762 $$ = f_merge_items($1, t);
763 }
41be4444 764 ;
d3dd620b 765
92a72a4c 766bgp_path_expr:
c8cafc8e 767 symbol { $$ = $1; }
92a72a4c
OZ
768 | '(' term ')' { $$ = $2; }
769 ;
770
f9491630 771bgp_path:
3e52d112 772 PO bgp_path_tail PC { $$ = $2; }
f9491630
OZ
773 ;
774
3e52d112 775bgp_path_tail:
9b46748d
MM
776 NUM bgp_path_tail { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_PATH_MASK_ITEM, .val.pmi = { .asn = $1, .kind = PM_ASN, }, }); f_inst_next($$, $2); }
777 | NUM DDOT NUM bgp_path_tail { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_PATH_MASK_ITEM, .val.pmi = { .from = $1, .to = $3, .kind = PM_ASN_RANGE }, }); f_inst_next($$, $4); }
778 | '*' bgp_path_tail { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_PATH_MASK_ITEM, .val.pmi = { .kind = PM_ASTERISK }, }); f_inst_next($$, $2); }
779 | '?' bgp_path_tail { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_PATH_MASK_ITEM, .val.pmi = { .kind = PM_QUESTION }, }); f_inst_next($$, $2); }
780 | bgp_path_expr bgp_path_tail { $$ = $1; f_inst_next($$, $2); }
122deb6d 781 | { $$ = NULL; }
f9491630 782 ;
77de6882 783
23b1539b 784constant:
9b46748d
MM
785 NUM { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_INT, .val.i = $1, }); }
786 | TRUE { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_BOOL, .val.i = 1, }); }
787 | FALSE { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_BOOL, .val.i = 0, }); }
788 | TEXT { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_STRING, .val.s = $1, }); }
789 | fipa { $$ = f_new_inst(FI_CONSTANT, $1); }
790 | VPN_RD { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_RD, .val.ec = $1, }); }
791 | net_ { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_NET, .val.net = $1, }); }
ca2ee91a
MM
792 | '[' set_items ']' {
793 DBG( "We've got a set here..." );
9b46748d 794 $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_SET, .val.t = build_tree($2), });
ca2ee91a
MM
795 DBG( "ook\n" );
796 }
9b46748d
MM
797 | '[' fprefix_set ']' { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_PREFIX_SET, .val.ti = $2, }); }
798 | ENUM { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = $1 >> 16, .val.i = $1 & 0xffff, }); }
23b1539b
PM
799 ;
800
42a0c054 801constructor:
9b46748d
MM
802 '(' term ',' term ')' { $$ = f_new_inst(FI_PAIR_CONSTRUCT, $2, $4); }
803 | '(' ec_kind ',' term ',' term ')' { $$ = f_new_inst(FI_EC_CONSTRUCT, $4, $6, $2); }
804 | '(' term ',' term ',' term ')' { $$ = f_new_inst(FI_LC_CONSTRUCT, $2, $4, $6); }
805 | bgp_path { $$ = f_new_inst(FI_PATHMASK_CONSTRUCT, $1, 0); }
42a0c054
OZ
806 ;
807
92a72a4c 808
2e18b87d 809rtadot: /* EMPTY, we are not permitted RTA. prefix */
6c14255d 810 ;
f4536657 811
2d496d20
PM
812function_call:
813 SYM '(' var_list ')' {
9b46748d 814 $$ = f_new_inst(FI_CALL, $1, $3);
2d496d20
PM
815 }
816 ;
817
92a72a4c
OZ
818symbol:
819 SYM {
265419a3
MM
820 switch ($1->class & 0xffff) {
821 case SYM_CONSTANT_RANGE:
9b46748d 822 $$ = f_new_inst(FI_CONSTANT_INDIRECT, $1->def);
4c553c5a 823 break;
265419a3 824 case SYM_VARIABLE_RANGE:
9b46748d 825 $$ = f_new_inst(FI_VARIABLE, $1);
265419a3
MM
826 break;
827 case SYM_ATTRIBUTE:
9b46748d 828 $$ = f_new_inst(FI_EA_GET, *((struct f_dynamic_attr *) $1->def));
265419a3
MM
829 break;
830 default:
831 cf_error("%s: variable expected.", $1->name);
92a72a4c
OZ
832 }
833 }
834
2bdb5e00 835static_attr:
4c553c5a
MM
836 FROM { $$ = f_new_static_attr(T_IP, SA_FROM, 0); }
837 | GW { $$ = f_new_static_attr(T_IP, SA_GW, 0); }
838 | NET { $$ = f_new_static_attr(T_NET, SA_NET, 1); }
839 | PROTO { $$ = f_new_static_attr(T_STRING, SA_PROTO, 1); }
840 | SOURCE { $$ = f_new_static_attr(T_ENUM_RTS, SA_SOURCE, 1); }
841 | SCOPE { $$ = f_new_static_attr(T_ENUM_SCOPE, SA_SCOPE, 0); }
842 | DEST { $$ = f_new_static_attr(T_ENUM_RTD, SA_DEST, 0); }
843 | IFNAME { $$ = f_new_static_attr(T_STRING, SA_IFNAME, 0); }
844 | IFINDEX { $$ = f_new_static_attr(T_INT, SA_IFINDEX, 1); }
2bdb5e00
PM
845 ;
846
84c7e194 847term:
5a14df39 848 '(' term ')' { $$ = $2; }
9b46748d
MM
849 | term '+' term { $$ = f_new_inst(FI_ADD, $1, $3); }
850 | term '-' term { $$ = f_new_inst(FI_SUBTRACT, $1, $3); }
851 | term '*' term { $$ = f_new_inst(FI_MULTIPLY, $1, $3); }
852 | term '/' term { $$ = f_new_inst(FI_DIVIDE, $1, $3); }
853 | term AND term { $$ = f_new_inst(FI_AND, $1, $3); }
854 | term OR term { $$ = f_new_inst(FI_OR, $1, $3); }
855 | term '=' term { $$ = f_new_inst(FI_EQ, $1, $3); }
856 | term NEQ term { $$ = f_new_inst(FI_NEQ, $1, $3); }
857 | term '<' term { $$ = f_new_inst(FI_LT, $1, $3); }
858 | term LEQ term { $$ = f_new_inst(FI_LTE, $1, $3); }
859 | term '>' term { $$ = f_new_inst(FI_LT, $3, $1); }
860 | term GEQ term { $$ = f_new_inst(FI_LTE, $3, $1); }
861 | term '~' term { $$ = f_new_inst(FI_MATCH, $1, $3); }
862 | term NMA term { $$ = f_new_inst(FI_NOT_MATCH, $1, $3); }
863 | '!' term { $$ = f_new_inst(FI_NOT, $2); }
864 | DEFINED '(' term ')' { $$ = f_new_inst(FI_DEFINED, $3); }
23b1539b 865
92a72a4c 866 | symbol { $$ = $1; }
1183b6b2 867 | constant { $$ = $1; }
42a0c054 868 | constructor { $$ = $1; }
4515bdba 869
5a14df39 870 | PREFERENCE { $$ = f_new_inst(FI_PREF_GET); }
2bdb5e00 871
9b46748d 872 | rtadot static_attr { $$ = f_new_inst(FI_RTA_GET, $2); }
fe613ecd 873
9b46748d 874 | rtadot dynamic_attr { $$ = f_new_inst(FI_EA_GET, $2); }
36bbfc70 875
9b46748d
MM
876 | term '.' IS_V4 { $$ = f_new_inst(FI_IS_V4, $1); }
877 | term '.' TYPE { $$ = f_new_inst(FI_TYPE, $1); }
878 | term '.' IP { $$ = f_new_inst(FI_IP, $1); }
879 | term '.' RD { $$ = f_new_inst(FI_ROUTE_DISTINGUISHER, $1); }
880 | term '.' LEN { $$ = f_new_inst(FI_LENGTH, $1); }
881 | term '.' MAXLEN { $$ = f_new_inst(FI_ROA_MAXLEN, $1); }
882 | term '.' ASN { $$ = f_new_inst(FI_ROA_ASN, $1); }
883 | term '.' SRC { $$ = f_new_inst(FI_SADR_SRC, $1); }
884 | term '.' MASK '(' term ')' { $$ = f_new_inst(FI_IP_MASK, $1, $5); }
885 | term '.' FIRST { $$ = f_new_inst(FI_AS_PATH_FIRST, $1); }
886 | term '.' LAST { $$ = f_new_inst(FI_AS_PATH_LAST, $1); }
887 | term '.' LAST_NONAGGREGATED { $$ = f_new_inst(FI_AS_PATH_LAST_NAG, $1); }
7f77e250
PM
888
889/* Communities */
10a53608
PM
890/* This causes one shift/reduce conflict
891 | rtadot dynamic_attr '.' ADD '(' term ')' { }
892 | rtadot dynamic_attr '.' DELETE '(' term ')' { }
893 | rtadot dynamic_attr '.' CONTAINS '(' term ')' { }
a2d15746 894 | rtadot dynamic_attr '.' RESET{ }
10a53608 895*/
7f77e250 896
9b46748d
MM
897 | '+' EMPTY '+' { $$ = f_new_inst(FI_CONSTANT, f_const_empty_path); }
898 | '-' EMPTY '-' { $$ = f_new_inst(FI_CONSTANT, f_const_empty_clist); }
899 | '-' '-' EMPTY '-' '-' { $$ = f_new_inst(FI_CONSTANT, f_const_empty_eclist); }
900 | '-' '-' '-' EMPTY '-' '-' '-' { $$ = f_new_inst(FI_CONSTANT, f_const_empty_lclist); }
901 | PREPEND '(' term ',' term ')' { $$ = f_new_inst(FI_PATH_PREPEND, $3, $5); }
902 | ADD '(' term ',' term ')' { $$ = f_new_inst(FI_CLIST_ADD, $3, $5); }
903 | DELETE '(' term ',' term ')' { $$ = f_new_inst(FI_CLIST_DEL, $3, $5); }
904 | FILTER '(' term ',' term ')' { $$ = f_new_inst(FI_CLIST_FILTER, $3, $5); }
afc54517 905
9b46748d
MM
906 | ROA_CHECK '(' rtable ')' { $$ = f_new_inst(FI_ROA_CHECK_IMPLICIT, $3); }
907 | ROA_CHECK '(' rtable ',' term ',' term ')' { $$ = f_new_inst(FI_ROA_CHECK_EXPLICIT, $5, $7, $3); }
af582c48 908
9b46748d 909 | FORMAT '(' term ')' { $$ = f_new_inst(FI_FORMAT, $3); }
9b0a0ba9 910
a2d15746 911/* | term '.' LEN { $$->code = P('P','l'); } */
7f77e250 912
4c553c5a 913 | function_call
ba921648
PM
914 ;
915
916break_command:
03e3d184
MM
917 QUITBIRD { $$ = F_QUITBIRD; }
918 | ACCEPT { $$ = F_ACCEPT; }
919 | REJECT { $$ = F_REJECT; }
920 | ERROR { $$ = F_ERROR; }
921 | PRINT { $$ = F_NOP; }
922 | PRINTN { $$ = F_NONL; }
ba921648
PM
923 ;
924
23b1539b 925print_one:
9b46748d 926 term { $$ = f_new_inst(FI_PRINT, $1); }
23b1539b
PM
927 ;
928
929print_list: /* EMPTY */ { $$ = NULL; }
995e5894
PM
930 | print_one { $$ = $1; }
931 | print_one ',' print_list {
23b1539b 932 if ($1) {
9b46748d 933 f_inst_next($1, $3);
23b1539b 934 $$ = $1;
995e5894 935 } else $$ = $3;
23b1539b
PM
936 }
937 ;
938
c8cafc8e 939var_listn: term {
9b46748d 940 $$ = $1;
d3dd620b 941 }
6dc7a0cb 942 | term ',' var_listn {
9b46748d
MM
943 $$ = $1;
944 f_inst_next($$, $3);
6542ece9
PM
945 }
946 ;
947
6dc7a0cb
PM
948var_list: /* EMPTY */ { $$ = NULL; }
949 | var_listn { $$ = $1; }
950 ;
951
23b1539b 952cmd:
49955645 953 IF term THEN block {
9b46748d 954 $$ = f_new_inst(FI_CONDITION, $2, $4, NULL);
23b1539b 955 }
49955645 956 | IF term THEN block ELSE block {
9b46748d 957 $$ = f_new_inst(FI_CONDITION, $2, $4, $6);
23b1539b 958 }
ba921648 959 | SYM '=' term ';' {
d4d75628 960 DBG( "Ook, we'll set value\n" );
265419a3 961 if ($1->class == SYM_ATTRIBUTE) {
9b46748d 962 $$ = f_new_inst(FI_EA_SET, *((struct f_dynamic_attr *) $1->def), $3);
265419a3 963 } else if (($1->class & ~T_MASK) == SYM_VARIABLE) {
9b46748d 964 $$ = f_new_inst(FI_SET, $3, $1);
265419a3
MM
965 } else
966 cf_error( "Symbol `%s' is read-only.", $1->name );
b9d70dc8 967 }
2d496d20 968 | RETURN term ';' {
d4d75628 969 DBG( "Ook, we'll return the value\n" );
9b46748d 970 $$ = f_new_inst(FI_RETURN, $2);
2d496d20 971 }
db1326aa 972 | rtadot dynamic_attr '=' term ';' {
9b46748d 973 $$ = f_new_inst(FI_EA_SET, $2, $4);
f31156ca 974 }
0dc4431c 975 | rtadot static_attr '=' term ';' {
9b46748d 976 if ($2.readonly)
0dc4431c 977 cf_error( "This static attribute is read-only.");
9b46748d 978 $$ = f_new_inst(FI_RTA_SET, $2, $4);
0dc4431c
PM
979 }
980 | PREFERENCE '=' term ';' {
9b46748d 981 $$ = f_new_inst(FI_PREF_SET, $3);
c8cafc8e 982 }
db1326aa 983 | UNSET '(' rtadot dynamic_attr ')' ';' {
9b46748d 984 $$ = f_new_inst(FI_EA_UNSET, $4);
c7b43f33 985 }
9b46748d
MM
986 | break_command print_list ';' { $$ = f_new_inst(FI_PRINT_AND_DIE, $2, $1); }
987 | function_call ';' { $$ = f_new_inst(FI_DROP_RESULT, $1); }
7db7b7db 988 | CASE term '{' switch_body '}' {
9b46748d 989 $$ = f_new_inst(FI_SWITCH, $2, build_tree($4));
7db7b7db 990 }
7d6eebae 991
42a0c054 992 | rtadot dynamic_attr '.' EMPTY ';' { $$ = f_generate_empty($2); }
4c553c5a
MM
993 | rtadot dynamic_attr '.' PREPEND '(' term ')' ';' { $$ = f_generate_complex( FI_PATH_PREPEND, $2, $6 ); }
994 | rtadot dynamic_attr '.' ADD '(' term ')' ';' { $$ = f_generate_complex( FI_CLIST_ADD, $2, $6 ); }
995 | rtadot dynamic_attr '.' DELETE '(' term ')' ';' { $$ = f_generate_complex( FI_CLIST_DEL, $2, $6 ); }
996 | rtadot dynamic_attr '.' FILTER '(' term ')' ';' { $$ = f_generate_complex( FI_CLIST_FILTER, $2, $6 ); }
3ec0bedc 997 | BT_ASSERT '(' get_cf_position term get_cf_position ')' ';' { $$ = assert_done($4, $3 + 1, $5 - 1); }
9b0a0ba9
OZ
998 ;
999
1000get_cf_position:
1001{
1002 $$ = cf_text;
1003};
1004
1005
b9d70dc8 1006CF_END