]>
Commit | Line | Data |
---|---|---|
f142750d MM |
1 | /* |
2 | * BIRD -- Configuration Parser Top | |
3 | * | |
aee539f2 | 4 | * (c) 1998--2000 Martin Mares <mj@ucw.cz> |
f142750d MM |
5 | * |
6 | * Can be freely distributed and used under the terms of the GNU GPL. | |
7 | */ | |
8 | ||
9 | CF_HDR | |
10 | ||
93e868c7 OZ |
11 | #define PARSER 1 |
12 | ||
f142750d MM |
13 | #include "nest/bird.h" |
14 | #include "conf/conf.h" | |
c74c0e3c MM |
15 | #include "lib/resource.h" |
16 | #include "lib/socket.h" | |
f047271c | 17 | #include "lib/timer.h" |
221135d6 | 18 | #include "lib/string.h" |
c74c0e3c | 19 | #include "nest/protocol.h" |
50d8424a | 20 | #include "nest/iface.h" |
166b9c49 | 21 | #include "nest/route.h" |
9d3fc306 | 22 | #include "nest/bfd.h" |
bc2fb680 | 23 | #include "nest/cli.h" |
b9d70dc8 | 24 | #include "filter/filter.h" |
f142750d | 25 | |
f2c6c80a MM |
26 | /* FIXME: Turn on YYERROR_VERBOSE and work around lots of bison bugs? */ |
27 | ||
b8cc390e OZ |
28 | CF_DEFINES |
29 | ||
30 | static void | |
6aaaa635 | 31 | check_u16(uint val) |
b8cc390e OZ |
32 | { |
33 | if (val > 0xFFFF) | |
6aaaa635 | 34 | cf_error("Value %u out of range (0-65535)", val); |
b8cc390e OZ |
35 | } |
36 | ||
9eef9c64 MM |
37 | #define cf_assert(cond, ...) do { if (!(cond)) cf_error(__VA_ARGS__); } while (0) |
38 | static inline void cf_assert_symbol(const struct symbol *sym, uint class) { | |
39 | switch (class) { | |
40 | case SYM_PROTO: cf_assert(sym->class == SYM_PROTO, "Protocol name required"); break; | |
41 | case SYM_TEMPLATE: cf_assert(sym->class == SYM_TEMPLATE, "Protocol template name required"); break; | |
42 | case SYM_FUNCTION: cf_assert(sym->class == SYM_FUNCTION, "Function name required"); break; | |
43 | case SYM_FILTER: cf_assert(sym->class == SYM_FILTER, "Filter name required"); break; | |
44 | case SYM_TABLE: cf_assert(sym->class == SYM_TABLE, "Table name required"); break; | |
45 | case SYM_ATTRIBUTE: cf_assert(sym->class == SYM_ATTRIBUTE, "Custom attribute name required"); break; | |
333ddd4f OZ |
46 | case SYM_MPLS_DOMAIN: cf_assert(sym->class == SYM_MPLS_DOMAIN, "MPLS domain name required"); break; |
47 | case SYM_MPLS_RANGE: cf_assert(sym->class == SYM_MPLS_RANGE, "MPLS range name required"); break; | |
9eef9c64 MM |
48 | case SYM_VARIABLE: cf_assert((sym->class & ~0xff) == SYM_VARIABLE, "Variable name required"); break; |
49 | case SYM_CONSTANT: cf_assert((sym->class & ~0xff) == SYM_CONSTANT, "Constant name required"); break; | |
50 | default: bug("This shall not happen"); | |
51 | } | |
52 | } | |
53 | ||
f142750d MM |
54 | CF_DECLS |
55 | ||
56 | %union { | |
6aaaa635 | 57 | uint i; |
dce26783 | 58 | u32 i32; |
d311368b | 59 | u64 i64; |
f142750d | 60 | ip_addr a; |
fe9f1a6d OZ |
61 | ip4_addr ip4; |
62 | ip6_addr ip6; | |
04632fd7 | 63 | net_addr net; |
d44e686e | 64 | net_addr *net_ptr; |
f142750d | 65 | struct symbol *s; |
a8a64ca0 | 66 | struct keyword *kw; |
fd9f0c06 | 67 | const char *t; |
0e02abfd | 68 | struct rtable_config *r; |
f4a60a9b | 69 | struct channel_config *cc; |
61dae32b | 70 | struct channel *c; |
e0f2e42f | 71 | struct f_inst *x; |
0206c070 MM |
72 | struct { |
73 | struct f_inst *begin, *end; | |
74 | } xp; | |
9b46748d MM |
75 | enum filter_return fret; |
76 | enum ec_subtype ecs; | |
5a14df39 MM |
77 | struct f_dynamic_attr fda; |
78 | struct f_static_attr fsa; | |
c0e958e0 | 79 | struct f_lval flv; |
96d757c1 | 80 | struct f_line *fl; |
93d6096c | 81 | struct f_arg *fa; |
0b39b1cb | 82 | const struct filter *f; |
38506f71 | 83 | struct f_tree *e; |
b1a597e0 | 84 | struct f_trie *trie; |
38506f71 | 85 | struct f_val v; |
9d79fec8 | 86 | struct password_item *p; |
730f2e2c | 87 | struct rt_show_data *ra; |
0f808c06 | 88 | struct sym_show_data *sd; |
20ab192b | 89 | struct lsadb_show_data *ld; |
863ecfc7 | 90 | struct mrt_dump_data *md; |
8e9e013b | 91 | struct mpls_show_ranges_cmd *msrc; |
26dd61ee | 92 | struct bfd_show_sessions_cmd *bssc; |
69a8259c | 93 | struct iface *iface; |
4ab5331c | 94 | void *g; |
f047271c | 95 | btime time; |
5e173e9f | 96 | struct f_prefix px; |
e304fd4b | 97 | struct proto_spec ps; |
f4a60a9b | 98 | struct channel_limit cl; |
c37e7851 | 99 | struct timeformat *tf; |
3c744164 | 100 | mpls_label_stack *mls; |
bb8e2824 | 101 | const struct adata *bs; |
977b82fb | 102 | struct aggr_item_node *ai; |
f142750d MM |
103 | } |
104 | ||
b8cc390e | 105 | %token END CLI_MARKER INVALID_TOKEN ELSECOL DDOT |
fc4398b4 | 106 | %token GEQ LEQ NEQ AND OR IMP |
cf186034 | 107 | %token PO PC |
944f008a | 108 | %token <i> NUM ENUM |
fe9f1a6d OZ |
109 | %token <ip4> IP4 |
110 | %token <ip6> IP6 | |
d311368b | 111 | %token <i64> VPN_RD |
fc9d471b | 112 | %token <s> CF_SYM_KNOWN CF_SYM_UNDEFINED CF_SYM_METHOD_BARE CF_SYM_METHOD_ARGS |
f142750d | 113 | %token <t> TEXT |
fc354788 | 114 | %token <bs> BYTETEXT |
69a8259c | 115 | %type <iface> ipa_scope |
f142750d | 116 | |
04632fd7 | 117 | %type <i> expr bool pxlen4 |
ee528fbd | 118 | %type <time> expr_us time |
04632fd7 | 119 | %type <a> ipa |
141fb51f | 120 | %type <net> net_ip4_ net_ip4 net_ip6_ net_ip6 net_ip_ net_ip net_or_ipa |
be17805c | 121 | %type <net_ptr> net_ net_any net_vpn4_ net_vpn6_ net_vpn_ net_roa4_ net_roa6_ net_roa_ net_ip6_sadr_ net_mpls_ |
3c744164 | 122 | %type <mls> label_stack_start label_stack |
d44e686e OZ |
123 | |
124 | %type <t> text opttext | |
f411a19b | 125 | %type <bs> bytestring |
8e177cf3 | 126 | %type <s> symbol symbol_known |
0b62c3a7 | 127 | |
116285f2 | 128 | %type <v> bytestring_text text_or_ipa |
f411a19b OZ |
129 | %type <x> bytestring_expr |
130 | ||
60de3356 | 131 | %nonassoc PREFIX_DUMMY |
1960d203 | 132 | %left AND OR |
fc4398b4 | 133 | %nonassoc '=' '<' '>' '~' GEQ LEQ NEQ NMA IMP PO PC |
1960d203 | 134 | %left '+' '-' |
0b62c3a7 | 135 | %left '*' '/' '%' |
23b1539b | 136 | %left '!' |
112d71a7 | 137 | %nonassoc '.' |
0b62c3a7 | 138 | |
9eef9c64 MM |
139 | %start config |
140 | ||
f86c86b7 | 141 | CF_KEYWORDS(DEFINE, ON, OFF, YES, NO, S, MS, US, PORT, VPN, MPLS, FROM, MAX, AS) |
0b62c3a7 | 142 | |
f142750d MM |
143 | CF_GRAMMAR |
144 | ||
0b62c3a7 MM |
145 | /* Basic config file structure */ |
146 | ||
bc2fb680 | 147 | config: conf_entries END { return 0; } |
ffb59d24 | 148 | | CLI_MARKER cli_cmd { return 0; } |
f142750d MM |
149 | ; |
150 | ||
151 | conf_entries: | |
152 | /* EMPTY */ | |
7f400d1c | 153 | | conf_entries conf |
f142750d MM |
154 | ; |
155 | ||
f851f0d7 | 156 | conf: ';' ; |
f142750d | 157 | |
1103b32e | 158 | |
72380a34 | 159 | /* Constant expressions */ |
0b62c3a7 | 160 | |
f851f0d7 MM |
161 | conf: definition ; |
162 | ||
1103b32e | 163 | definition: |
2de1e206 | 164 | DEFINE symbol '=' term ';' { |
30b84682 | 165 | struct f_val *val = cfg_allocz(sizeof(struct f_val)); |
0dbcc927 | 166 | *val = cf_eval($4, T_VOID); |
51f2e7af | 167 | cf_define_symbol(new_config, $2, SYM_CONSTANT | val->type, val, val); |
1103b32e OZ |
168 | } |
169 | ; | |
170 | ||
c9b66706 | 171 | expr: |
0b62c3a7 | 172 | NUM |
0dbcc927 | 173 | | '(' term ')' { $$ = cf_eval_int($2); } |
8e177cf3 | 174 | | symbol_known { |
9eef9c64 | 175 | if ($1->class != (SYM_CONSTANT | T_INT)) cf_error("Number constant expected"); |
1103b32e | 176 | $$ = SYM_VAL($1).i; } |
0b62c3a7 MM |
177 | ; |
178 | ||
6a8d3f1c | 179 | expr_us: |
ee528fbd OZ |
180 | expr S { $$ = $1 S_; } |
181 | | expr MS { $$ = $1 MS_; } | |
182 | | expr US { $$ = $1 US_; } | |
6a8d3f1c OZ |
183 | ; |
184 | ||
8e177cf3 | 185 | symbol: CF_SYM_UNDEFINED | CF_SYM_KNOWN | KEYWORD ; |
c0231b09 | 186 | symbol_known: CF_SYM_KNOWN ; |
c0e958e0 | 187 | |
166b9c49 MM |
188 | /* Switches */ |
189 | ||
190 | bool: | |
c8cafc8e | 191 | expr { $$ = !!$1; } |
166b9c49 MM |
192 | | ON { $$ = 1; } |
193 | | YES { $$ = 1; } | |
194 | | OFF { $$ = 0; } | |
195 | | NO { $$ = 0; } | |
196 | | /* Silence means agreement */ { $$ = 1; } | |
197 | ; | |
198 | ||
e3f2d5fc | 199 | |
04632fd7 | 200 | /* Addresses */ |
fe9f1a6d | 201 | |
e3f2d5fc | 202 | ipa: |
04632fd7 OZ |
203 | IP4 { $$ = ipa_from_ip4($1); } |
204 | | IP6 { $$ = ipa_from_ip6($1); } | |
9eef9c64 MM |
205 | | CF_SYM_KNOWN { |
206 | if ($1->class != (SYM_CONSTANT | T_IP)) cf_error("IP address constant expected"); | |
5e173e9f | 207 | $$ = SYM_VAL($1).ip; |
e3f2d5fc MM |
208 | } |
209 | ; | |
89d2355d | 210 | |
69a8259c OZ |
211 | ipa_scope: |
212 | /* empty */ { $$ = NULL; } | |
c0e958e0 | 213 | | '%' symbol { $$ = if_get_by_name($2->name); } |
69a8259c OZ |
214 | ; |
215 | ||
d44e686e | 216 | |
04632fd7 | 217 | /* Networks - internal */ |
d44e686e | 218 | |
04632fd7 OZ |
219 | pxlen4: |
220 | '/' NUM { | |
6aaaa635 | 221 | if ($2 > IP4_MAX_PREFIX_LENGTH) cf_error("Invalid prefix length %u", $2); |
04632fd7 OZ |
222 | $$ = $2; |
223 | } | |
04632fd7 | 224 | ; |
d44e686e | 225 | |
04632fd7 OZ |
226 | net_ip4_: IP4 pxlen4 |
227 | { | |
228 | net_fill_ip4(&($$), $1, $2); | |
0705a1c5 PT |
229 | |
230 | net_addr_ip4 *n = (void *) &($$); | |
231 | if (!net_validate_ip4(n)) | |
232 | cf_error("Invalid IPv4 prefix %I4/%d, maybe you wanted %I4/%d", | |
233 | n->prefix, n->pxlen, ip4_and(n->prefix, ip4_mkmask(n->pxlen)), n->pxlen); | |
04632fd7 | 234 | }; |
d44e686e | 235 | |
04632fd7 OZ |
236 | net_ip6_: IP6 '/' NUM |
237 | { | |
6aaaa635 OZ |
238 | if ($3 > IP6_MAX_PREFIX_LENGTH) |
239 | cf_error("Invalid prefix length %u", $3); | |
0705a1c5 PT |
240 | |
241 | net_fill_ip6(&($$), $1, $3); | |
242 | ||
243 | net_addr_ip6 *n = (void *) &($$); | |
244 | if (!net_validate_ip6(n)) | |
245 | cf_error("Invalid IPv6 prefix %I6/%d, maybe you wanted %I6/%d", | |
246 | n->prefix, n->pxlen, ip6_and(n->prefix, ip6_mkmask(n->pxlen)), n->pxlen); | |
04632fd7 | 247 | }; |
d44e686e | 248 | |
be17805c OZ |
249 | net_ip6_sadr_: IP6 '/' NUM FROM IP6 '/' NUM |
250 | { | |
251 | if ($3 > IP6_MAX_PREFIX_LENGTH) | |
252 | cf_error("Invalid prefix length %u", $3); | |
253 | ||
254 | if ($7 > IP6_MAX_PREFIX_LENGTH) | |
255 | cf_error("Invalid prefix length %u", $7); | |
256 | ||
257 | $$ = cfg_alloc(sizeof(net_addr_ip6_sadr)); | |
258 | net_fill_ip6_sadr($$, $1, $3, $5, $7); | |
259 | ||
260 | net_addr_ip6_sadr *n = (void *) $$; | |
261 | if (!net_validate_ip6_sadr(n)) | |
262 | cf_error("Invalid SADR IPv6 prefix %I6/%d from %I6/%d, maybe you wanted %I6/%d from %I6/%d", | |
263 | n->dst_prefix, n->dst_pxlen, n->src_prefix, n->src_pxlen, | |
264 | ip6_and(n->dst_prefix, ip6_mkmask(n->dst_pxlen)), n->dst_pxlen, | |
265 | ip6_and(n->src_prefix, ip6_mkmask(n->src_pxlen)), n->src_pxlen); | |
266 | }; | |
267 | ||
d311368b MM |
268 | net_vpn4_: VPN_RD net_ip4_ |
269 | { | |
270 | $$ = cfg_alloc(sizeof(net_addr_vpn4)); | |
62e64905 | 271 | net_fill_vpn4($$, net4_prefix(&$2), net4_pxlen(&$2), $1); |
d311368b MM |
272 | } |
273 | ||
274 | net_vpn6_: VPN_RD net_ip6_ | |
275 | { | |
276 | $$ = cfg_alloc(sizeof(net_addr_vpn6)); | |
62e64905 | 277 | net_fill_vpn6($$, net6_prefix(&$2), net6_pxlen(&$2), $1); |
d311368b MM |
278 | } |
279 | ||
513ad0a8 PT |
280 | net_roa4_: net_ip4_ MAX NUM AS NUM |
281 | { | |
a4caa1c0 | 282 | $$ = cfg_alloc(sizeof(net_addr_roa4)); |
286e2011 | 283 | net_fill_roa4($$, net4_prefix(&$1), net4_pxlen(&$1), $3, $5); |
6aaaa635 OZ |
284 | if ($3 < net4_pxlen(&$1) || $3 > IP4_MAX_PREFIX_LENGTH) |
285 | cf_error("Invalid max prefix length %u", $3); | |
513ad0a8 PT |
286 | }; |
287 | ||
288 | net_roa6_: net_ip6_ MAX NUM AS NUM | |
289 | { | |
a4caa1c0 | 290 | $$ = cfg_alloc(sizeof(net_addr_roa6)); |
286e2011 | 291 | net_fill_roa6($$, net6_prefix(&$1), net6_pxlen(&$1), $3, $5); |
6aaaa635 OZ |
292 | if ($3 < net6_pxlen(&$1) || $3 > IP6_MAX_PREFIX_LENGTH) |
293 | cf_error("Invalid max prefix length %u", $3); | |
513ad0a8 PT |
294 | }; |
295 | ||
66acbc8d OZ |
296 | net_mpls_: MPLS NUM |
297 | { | |
a740054d | 298 | $$ = cfg_alloc(sizeof(net_addr_mpls)); |
66acbc8d OZ |
299 | net_fill_mpls($$, $2); |
300 | } | |
301 | ||
04632fd7 | 302 | net_ip_: net_ip4_ | net_ip6_ ; |
d311368b | 303 | net_vpn_: net_vpn4_ | net_vpn6_ ; |
62e64905 | 304 | net_roa_: net_roa4_ | net_roa6_ ; |
513ad0a8 | 305 | |
a4caa1c0 PT |
306 | net_: |
307 | net_ip_ { $$ = cfg_alloc($1.length); net_copy($$, &($1)); } | |
d311368b | 308 | | net_vpn_ |
a4caa1c0 | 309 | | net_roa_ |
77234bbb | 310 | | net_flow_ |
be17805c | 311 | | net_ip6_sadr_ |
66acbc8d | 312 | | net_mpls_ |
a4caa1c0 | 313 | ; |
d44e686e OZ |
314 | |
315 | ||
04632fd7 OZ |
316 | /* Networks - regular */ |
317 | ||
141fb51f MM |
318 | net_ip4: |
319 | net_ip4_ | |
320 | | CF_SYM_KNOWN { | |
321 | if (($1->class != (SYM_CONSTANT | T_NET)) || (SYM_VAL($1).net->type != NET_IP4)) | |
322 | cf_error("IPv4 network constant expected"); | |
323 | $$ = * SYM_VAL($1).net; | |
324 | } | |
325 | ; | |
326 | ||
04632fd7 OZ |
327 | net_ip6: |
328 | net_ip6_ | |
9eef9c64 | 329 | | CF_SYM_KNOWN { |
04632fd7 | 330 | if (($1->class != (SYM_CONSTANT | T_NET)) || (SYM_VAL($1).net->type != NET_IP6)) |
9eef9c64 | 331 | cf_error("IPv6 network constant expected"); |
04632fd7 | 332 | $$ = * SYM_VAL($1).net; |
d7661fbe | 333 | } |
04632fd7 OZ |
334 | ; |
335 | ||
336 | net_ip: | |
337 | net_ip_ | |
9eef9c64 | 338 | | CF_SYM_KNOWN { |
04632fd7 | 339 | if (($1->class != (SYM_CONSTANT | T_NET)) || !net_is_ip(SYM_VAL($1).net)) |
9eef9c64 | 340 | cf_error("IP network constant expected"); |
04632fd7 | 341 | $$ = * SYM_VAL($1).net; |
758458be MM |
342 | } |
343 | ; | |
344 | ||
04632fd7 OZ |
345 | net_any: |
346 | net_ | |
9eef9c64 | 347 | | CF_SYM_KNOWN { |
04632fd7 | 348 | if ($1->class != (SYM_CONSTANT | T_NET)) |
9eef9c64 | 349 | cf_error("Network constant expected"); |
04632fd7 OZ |
350 | $$ = (net_addr *) SYM_VAL($1).net; /* Avoid const warning */ |
351 | } | |
352 | ; | |
353 | ||
354 | net_or_ipa: | |
355 | net_ip4_ | |
356 | | net_ip6_ | |
357 | | IP4 { net_fill_ip4(&($$), $1, IP4_MAX_PREFIX_LENGTH); } | |
358 | | IP6 { net_fill_ip6(&($$), $1, IP6_MAX_PREFIX_LENGTH); } | |
9eef9c64 | 359 | | CF_SYM_KNOWN { |
04632fd7 OZ |
360 | if ($1->class == (SYM_CONSTANT | T_IP)) |
361 | net_fill_ip_host(&($$), SYM_VAL($1).ip); | |
362 | else if (($1->class == (SYM_CONSTANT | T_NET)) && net_is_ip(SYM_VAL($1).net)) | |
363 | $$ = * SYM_VAL($1).net; | |
364 | else | |
9eef9c64 | 365 | cf_error("IP address or network constant expected"); |
89d2355d | 366 | } |
89d2355d MM |
367 | ; |
368 | ||
f2010f9c MM |
369 | label_stack_start: NUM |
370 | { | |
3c744164 MM |
371 | $$ = cfg_allocz(sizeof(mpls_label_stack)); |
372 | $$->len = 1; | |
373 | $$->stack[0] = $1; | |
f2010f9c MM |
374 | }; |
375 | ||
376 | label_stack: | |
377 | label_stack_start | |
378 | | label_stack '/' NUM { | |
3c744164 | 379 | if ($1->len >= MPLS_MAX_LABEL_STACK) |
62e64905 | 380 | cf_error("Too many labels in stack"); |
3c744164 | 381 | $1->stack[$1->len++] = $3; |
f2010f9c MM |
382 | $$ = $1; |
383 | } | |
384 | ; | |
04632fd7 | 385 | |
f047271c | 386 | time: |
aee539f2 | 387 | TEXT { |
f047271c | 388 | $$ = tm_parse_time($1); |
aee539f2 | 389 | if (!$$) |
f047271c | 390 | cf_error("Invalid date/time"); |
aee539f2 | 391 | } |
9d79fec8 PM |
392 | ; |
393 | ||
9eceab33 OZ |
394 | text: |
395 | TEXT | |
9eef9c64 MM |
396 | | CF_SYM_KNOWN { |
397 | if ($1->class != (SYM_CONSTANT | T_STRING)) cf_error("String constant expected"); | |
9eceab33 OZ |
398 | $$ = SYM_VAL($1).s; |
399 | } | |
400 | ; | |
401 | ||
fe9f1a6d OZ |
402 | opttext: |
403 | TEXT | |
404 | | /* empty */ { $$ = NULL; } | |
405 | ; | |
406 | ||
116285f2 OZ |
407 | text_or_ipa: |
408 | TEXT { $$.type = T_STRING; $$.val.s = $1; } | |
409 | | IP4 { $$.type = T_IP; $$.val.ip = ipa_from_ip4($1); } | |
410 | | IP6 { $$.type = T_IP; $$.val.ip = ipa_from_ip6($1); } | |
411 | | CF_SYM_KNOWN { | |
412 | if (($1->class == (SYM_CONSTANT | T_STRING)) || | |
413 | ($1->class == (SYM_CONSTANT | T_IP))) | |
414 | $$ = *($1->val); | |
415 | else | |
416 | cf_error("String or IP constant expected"); | |
417 | } | |
418 | | '(' term ')' { | |
419 | $$ = cf_eval($2, T_VOID); | |
420 | if (($$.type != T_BYTESTRING) && ($$.type != T_STRING)) | |
421 | cf_error("Bytestring or string value expected"); | |
422 | } | |
423 | ; | |
424 | ||
f411a19b OZ |
425 | bytestring: |
426 | BYTETEXT | |
427 | | bytestring_expr { $$ = cf_eval($1, T_BYTESTRING).val.bs; } | |
428 | ; | |
429 | ||
430 | bytestring_text: | |
431 | BYTETEXT { $$.type = T_BYTESTRING; $$.val.bs = $1; } | |
432 | | TEXT { $$.type = T_STRING; $$.val.s = $1; } | |
433 | | bytestring_expr { | |
434 | $$ = cf_eval($1, T_VOID); | |
435 | if (($$.type != T_BYTESTRING) && ($$.type != T_STRING)) | |
436 | cf_error("Bytestring or string value expected"); | |
437 | } | |
438 | ; | |
439 | ||
440 | bytestring_expr: | |
441 | symbol_value | |
442 | | term_bs | |
443 | | '(' term ')' { $$ = $2; } | |
444 | ; | |
445 | ||
fe9f1a6d | 446 | |
f142750d MM |
447 | CF_CODE |
448 | ||
449 | CF_END |