201-src-add-support-to-add-flowtables.patch 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515
  1. From: Pablo Neira Ayuso <[email protected]>
  2. Date: Thu, 18 Jan 2018 08:43:23 +0100
  3. Subject: [PATCH] src: add support to add flowtables
  4. This patch allows you to create flowtable:
  5. # nft add table x
  6. # nft add flowtable x m { hook ingress priority 10\; devices = { eth0, wlan0 }\; }
  7. You have to specify hook and priority. So far, only the ingress hook is
  8. supported. The priority represents where this flowtable is placed in the
  9. ingress hook, which is registered to the devices that the user
  10. specifies.
  11. You can also use the 'create' command instead to bail out in case that
  12. there is an existing flowtable with this name.
  13. Signed-off-by: Pablo Neira Ayuso <[email protected]>
  14. ---
  15. --- a/include/expression.h
  16. +++ b/include/expression.h
  17. @@ -407,6 +407,8 @@ extern struct expr *prefix_expr_alloc(co
  18. extern struct expr *range_expr_alloc(const struct location *loc,
  19. struct expr *low, struct expr *high);
  20. +extern struct expr *compound_expr_alloc(const struct location *loc,
  21. + const struct expr_ops *ops);
  22. extern void compound_expr_add(struct expr *compound, struct expr *expr);
  23. extern void compound_expr_remove(struct expr *compound, struct expr *expr);
  24. extern void list_expr_sort(struct list_head *head);
  25. --- a/include/mnl.h
  26. +++ b/include/mnl.h
  27. @@ -92,6 +92,10 @@ int mnl_nft_obj_batch_del(struct nftnl_o
  28. struct nftnl_flowtable_list *
  29. mnl_nft_flowtable_dump(struct netlink_ctx *ctx, int family, const char *table);
  30. +int mnl_nft_flowtable_batch_add(struct nftnl_flowtable *flo,
  31. + struct nftnl_batch *batch, unsigned int flags,
  32. + uint32_t seqnum);
  33. +
  34. struct nftnl_ruleset *mnl_nft_ruleset_dump(struct netlink_ctx *ctx,
  35. uint32_t family);
  36. int mnl_nft_event_listener(struct mnl_socket *nf_sock, unsigned int debug_mask,
  37. --- a/include/netlink.h
  38. +++ b/include/netlink.h
  39. @@ -7,6 +7,7 @@
  40. #include <libnftnl/expr.h>
  41. #include <libnftnl/set.h>
  42. #include <libnftnl/object.h>
  43. +#include <libnftnl/flowtable.h>
  44. #include <linux/netlink.h>
  45. #include <linux/netfilter/nf_tables.h>
  46. @@ -182,6 +183,9 @@ extern int netlink_delete_obj(struct net
  47. extern int netlink_list_flowtables(struct netlink_ctx *ctx,
  48. const struct handle *h,
  49. const struct location *loc);
  50. +extern int netlink_add_flowtable(struct netlink_ctx *ctx,
  51. + const struct handle *h, struct flowtable *ft,
  52. + uint32_t flags);
  53. extern void netlink_dump_chain(const struct nftnl_chain *nlc,
  54. struct netlink_ctx *ctx);
  55. --- a/include/rule.h
  56. +++ b/include/rule.h
  57. @@ -322,10 +322,13 @@ uint32_t obj_type_to_cmd(uint32_t type);
  58. struct flowtable {
  59. struct list_head list;
  60. struct handle handle;
  61. + struct scope scope;
  62. struct location location;
  63. + const char * hookstr;
  64. unsigned int hooknum;
  65. int priority;
  66. const char **dev_array;
  67. + struct expr *dev_expr;
  68. int dev_array_len;
  69. unsigned int refcnt;
  70. };
  71. @@ -383,6 +386,8 @@ enum cmd_ops {
  72. * @CMD_OBJ_CHAIN: chain
  73. * @CMD_OBJ_CHAINS: multiple chains
  74. * @CMD_OBJ_TABLE: table
  75. + * @CMD_OBJ_FLOWTABLE: flowtable
  76. + * @CMD_OBJ_FLOWTABLES: flowtables
  77. * @CMD_OBJ_RULESET: ruleset
  78. * @CMD_OBJ_EXPR: expression
  79. * @CMD_OBJ_MONITOR: monitor
  80. @@ -422,6 +427,7 @@ enum cmd_obj {
  81. CMD_OBJ_CT_HELPERS,
  82. CMD_OBJ_LIMIT,
  83. CMD_OBJ_LIMITS,
  84. + CMD_OBJ_FLOWTABLE,
  85. CMD_OBJ_FLOWTABLES,
  86. };
  87. @@ -481,6 +487,7 @@ struct cmd {
  88. struct rule *rule;
  89. struct chain *chain;
  90. struct table *table;
  91. + struct flowtable *flowtable;
  92. struct monitor *monitor;
  93. struct markup *markup;
  94. struct obj *object;
  95. --- a/src/evaluate.c
  96. +++ b/src/evaluate.c
  97. @@ -2897,6 +2897,24 @@ static int set_evaluate(struct eval_ctx
  98. return 0;
  99. }
  100. +static uint32_t str2hooknum(uint32_t family, const char *hook);
  101. +
  102. +static int flowtable_evaluate(struct eval_ctx *ctx, struct flowtable *ft)
  103. +{
  104. + struct table *table;
  105. +
  106. + table = table_lookup_global(ctx);
  107. + if (table == NULL)
  108. + return cmd_error(ctx, "Could not process rule: Table '%s' does not exist",
  109. + ctx->cmd->handle.table);
  110. +
  111. + ft->hooknum = str2hooknum(NFPROTO_NETDEV, ft->hookstr);
  112. + if (ft->hooknum == NF_INET_NUMHOOKS)
  113. + return chain_error(ctx, ft, "invalid hook %s", ft->hookstr);
  114. +
  115. + return 0;
  116. +}
  117. +
  118. static int rule_evaluate(struct eval_ctx *ctx, struct rule *rule)
  119. {
  120. struct stmt *stmt, *tstmt = NULL;
  121. @@ -3069,6 +3087,14 @@ static int cmd_evaluate_add(struct eval_
  122. return chain_evaluate(ctx, cmd->chain);
  123. case CMD_OBJ_TABLE:
  124. return table_evaluate(ctx, cmd->table);
  125. + case CMD_OBJ_FLOWTABLE:
  126. + ret = cache_update(ctx->nf_sock, ctx->cache, cmd->op,
  127. + ctx->msgs, ctx->debug_mask & NFT_DEBUG_NETLINK, ctx->octx);
  128. + if (ret < 0)
  129. + return ret;
  130. +
  131. + handle_merge(&cmd->flowtable->handle, &cmd->handle);
  132. + return flowtable_evaluate(ctx, cmd->flowtable);
  133. case CMD_OBJ_COUNTER:
  134. case CMD_OBJ_QUOTA:
  135. case CMD_OBJ_CT_HELPER:
  136. --- a/src/expression.c
  137. +++ b/src/expression.c
  138. @@ -663,8 +663,8 @@ struct expr *range_expr_alloc(const stru
  139. return expr;
  140. }
  141. -static struct expr *compound_expr_alloc(const struct location *loc,
  142. - const struct expr_ops *ops)
  143. +struct expr *compound_expr_alloc(const struct location *loc,
  144. + const struct expr_ops *ops)
  145. {
  146. struct expr *expr;
  147. --- a/src/mnl.c
  148. +++ b/src/mnl.c
  149. @@ -1011,6 +1011,22 @@ err:
  150. return NULL;
  151. }
  152. +int mnl_nft_flowtable_batch_add(struct nftnl_flowtable *flo,
  153. + struct nftnl_batch *batch, unsigned int flags,
  154. + uint32_t seqnum)
  155. +{
  156. + struct nlmsghdr *nlh;
  157. +
  158. + nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(batch),
  159. + NFT_MSG_NEWFLOWTABLE,
  160. + nftnl_flowtable_get_u32(flo, NFTNL_FLOWTABLE_FAMILY),
  161. + NLM_F_CREATE | flags, seqnum);
  162. + nftnl_flowtable_nlmsg_build_payload(nlh, flo);
  163. + mnl_nft_batch_continue(batch);
  164. +
  165. + return 0;
  166. +}
  167. +
  168. /*
  169. * ruleset
  170. */
  171. --- a/src/netlink.c
  172. +++ b/src/netlink.c
  173. @@ -1773,6 +1773,64 @@ static struct obj *netlink_delinearize_o
  174. return obj;
  175. }
  176. +static struct nftnl_flowtable *alloc_nftnl_flowtable(const struct handle *h,
  177. + const struct flowtable *ft)
  178. +{
  179. + struct nftnl_flowtable *flo;
  180. +
  181. + flo = nftnl_flowtable_alloc();
  182. + if (flo == NULL)
  183. + memory_allocation_error();
  184. +
  185. + nftnl_flowtable_set_u32(flo, NFTNL_FLOWTABLE_FAMILY, h->family);
  186. + nftnl_flowtable_set_str(flo, NFTNL_FLOWTABLE_TABLE, h->table);
  187. + if (h->flowtable != NULL)
  188. + nftnl_flowtable_set_str(flo, NFTNL_FLOWTABLE_NAME, h->flowtable);
  189. +
  190. + return flo;
  191. +}
  192. +
  193. +static void netlink_dump_flowtable(struct nftnl_flowtable *flo,
  194. + struct netlink_ctx *ctx)
  195. +{
  196. + FILE *fp = ctx->octx->output_fp;
  197. +
  198. + if (!(ctx->debug_mask & NFT_DEBUG_NETLINK) || !fp)
  199. + return;
  200. +
  201. + nftnl_flowtable_fprintf(fp, flo, 0, 0);
  202. + fprintf(fp, "\n");
  203. +}
  204. +
  205. +int netlink_add_flowtable(struct netlink_ctx *ctx, const struct handle *h,
  206. + struct flowtable *ft, uint32_t flags)
  207. +{
  208. + struct nftnl_flowtable *flo;
  209. + const char *dev_array[8];
  210. + struct expr *expr;
  211. + int i = 0, err;
  212. +
  213. + flo = alloc_nftnl_flowtable(h, ft);
  214. + nftnl_flowtable_set_u32(flo, NFTNL_FLOWTABLE_HOOKNUM, ft->hooknum);
  215. + nftnl_flowtable_set_u32(flo, NFTNL_FLOWTABLE_PRIO, ft->priority);
  216. +
  217. + list_for_each_entry(expr, &ft->dev_expr->expressions, list)
  218. + dev_array[i++] = expr->identifier;
  219. +
  220. + dev_array[i] = NULL;
  221. + nftnl_flowtable_set_array(flo, NFTNL_FLOWTABLE_DEVICES, dev_array);
  222. +
  223. + netlink_dump_flowtable(flo, ctx);
  224. +
  225. + err = mnl_nft_flowtable_batch_add(flo, ctx->batch, flags, ctx->seqnum);
  226. + if (err < 0)
  227. + netlink_io_error(ctx, &ft->location, "Could not add flowtable: %s",
  228. + strerror(errno));
  229. + nftnl_flowtable_free(flo);
  230. +
  231. + return err;
  232. +}
  233. +
  234. static int list_obj_cb(struct nftnl_obj *nls, void *arg)
  235. {
  236. struct netlink_ctx *ctx = arg;
  237. --- a/src/parser_bison.y
  238. +++ b/src/parser_bison.y
  239. @@ -145,6 +145,7 @@ int nft_lex(void *, void *, void *);
  240. struct expr *expr;
  241. struct set *set;
  242. struct obj *obj;
  243. + struct flowtable *flowtable;
  244. struct counter *counter;
  245. struct quota *quota;
  246. struct ct *ct;
  247. @@ -189,6 +190,7 @@ int nft_lex(void *, void *, void *);
  248. %token HOOK "hook"
  249. %token DEVICE "device"
  250. +%token DEVICES "devices"
  251. %token TABLE "table"
  252. %token TABLES "tables"
  253. %token CHAIN "chain"
  254. @@ -200,6 +202,7 @@ int nft_lex(void *, void *, void *);
  255. %token ELEMENT "element"
  256. %token MAP "map"
  257. %token MAPS "maps"
  258. +%token FLOWTABLE "flowtable"
  259. %token HANDLE "handle"
  260. %token RULESET "ruleset"
  261. %token TRACE "trace"
  262. @@ -500,9 +503,9 @@ int nft_lex(void *, void *, void *);
  263. %type <cmd> base_cmd add_cmd replace_cmd create_cmd insert_cmd delete_cmd list_cmd reset_cmd flush_cmd rename_cmd export_cmd monitor_cmd describe_cmd import_cmd
  264. %destructor { cmd_free($$); } base_cmd add_cmd replace_cmd create_cmd insert_cmd delete_cmd list_cmd reset_cmd flush_cmd rename_cmd export_cmd monitor_cmd describe_cmd import_cmd
  265. -%type <handle> table_spec chain_spec chain_identifier ruleid_spec handle_spec position_spec rule_position ruleset_spec
  266. -%destructor { handle_free(&$$); } table_spec chain_spec chain_identifier ruleid_spec handle_spec position_spec rule_position ruleset_spec
  267. -%type <handle> set_spec set_identifier obj_spec obj_identifier
  268. +%type <handle> table_spec chain_spec flowtable_spec chain_identifier ruleid_spec handle_spec position_spec rule_position ruleset_spec
  269. +%destructor { handle_free(&$$); } table_spec chain_spec flowtable_spec chain_identifier ruleid_spec handle_spec position_spec rule_position ruleset_spec
  270. +%type <handle> set_spec set_identifier flowtable_identifier obj_spec obj_identifier
  271. %destructor { handle_free(&$$); } set_spec set_identifier obj_spec obj_identifier
  272. %type <val> family_spec family_spec_explicit chain_policy prio_spec
  273. @@ -526,6 +529,9 @@ int nft_lex(void *, void *, void *);
  274. %type <set> map_block_alloc map_block
  275. %destructor { set_free($$); } map_block_alloc
  276. +%type <flowtable> flowtable_block_alloc flowtable_block
  277. +%destructor { flowtable_free($$); } flowtable_block_alloc
  278. +
  279. %type <obj> obj_block_alloc counter_block quota_block ct_helper_block limit_block
  280. %destructor { obj_free($$); } obj_block_alloc
  281. @@ -606,8 +612,8 @@ int nft_lex(void *, void *, void *);
  282. %type <expr> verdict_map_expr verdict_map_list_expr verdict_map_list_member_expr
  283. %destructor { expr_free($$); } verdict_map_expr verdict_map_list_expr verdict_map_list_member_expr
  284. -%type <expr> set_expr set_block_expr set_list_expr set_list_member_expr
  285. -%destructor { expr_free($$); } set_expr set_block_expr set_list_expr set_list_member_expr
  286. +%type <expr> set_expr set_block_expr set_list_expr set_list_member_expr flowtable_expr flowtable_list_expr flowtable_expr_member
  287. +%destructor { expr_free($$); } set_expr set_block_expr set_list_expr set_list_member_expr flowtable_expr flowtable_list_expr flowtable_expr_member
  288. %type <expr> set_elem_expr set_elem_expr_alloc set_lhs_expr set_rhs_expr
  289. %destructor { expr_free($$); } set_elem_expr set_elem_expr_alloc set_lhs_expr set_rhs_expr
  290. %type <expr> set_elem_expr_stmt set_elem_expr_stmt_alloc
  291. @@ -872,6 +878,13 @@ add_cmd : TABLE table_spec
  292. {
  293. $$ = cmd_alloc(CMD_ADD, CMD_OBJ_SETELEM, &$2, &@$, $3);
  294. }
  295. + | FLOWTABLE flowtable_spec flowtable_block_alloc
  296. + '{' flowtable_block '}'
  297. + {
  298. + $5->location = @5;
  299. + handle_merge(&$3->handle, &$2);
  300. + $$ = cmd_alloc(CMD_ADD, CMD_OBJ_FLOWTABLE, &$2, &@$, $5);
  301. + }
  302. | COUNTER obj_spec
  303. {
  304. struct obj *obj;
  305. @@ -947,6 +960,13 @@ create_cmd : TABLE table_spec
  306. {
  307. $$ = cmd_alloc(CMD_CREATE, CMD_OBJ_SETELEM, &$2, &@$, $3);
  308. }
  309. + | FLOWTABLE flowtable_spec flowtable_block_alloc
  310. + '{' flowtable_block '}'
  311. + {
  312. + $5->location = @5;
  313. + handle_merge(&$3->handle, &$2);
  314. + $$ = cmd_alloc(CMD_CREATE, CMD_OBJ_FLOWTABLE, &$2, &@$, $5);
  315. + }
  316. | COUNTER obj_spec
  317. {
  318. struct obj *obj;
  319. @@ -1317,6 +1337,17 @@ table_block : /* empty */ { $$ = $<tabl
  320. list_add_tail(&$4->list, &$1->sets);
  321. $$ = $1;
  322. }
  323. +
  324. + | table_block FLOWTABLE flowtable_identifier
  325. + flowtable_block_alloc '{' flowtable_block '}'
  326. + stmt_separator
  327. + {
  328. + $4->location = @3;
  329. + handle_merge(&$4->handle, &$3);
  330. + handle_free(&$3);
  331. + list_add_tail(&$4->list, &$1->flowtables);
  332. + $$ = $1;
  333. + }
  334. | table_block COUNTER obj_identifier
  335. obj_block_alloc '{' counter_block '}'
  336. stmt_separator
  337. @@ -1512,6 +1543,62 @@ set_policy_spec : PERFORMANCE { $$ = NF
  338. | MEMORY { $$ = NFT_SET_POL_MEMORY; }
  339. ;
  340. +flowtable_block_alloc : /* empty */
  341. + {
  342. + $$ = flowtable_alloc(NULL);
  343. + }
  344. + ;
  345. +
  346. +flowtable_block : /* empty */ { $$ = $<flowtable>-1; }
  347. + | flowtable_block common_block
  348. + | flowtable_block stmt_separator
  349. + | flowtable_block HOOK STRING PRIORITY prio_spec stmt_separator
  350. + {
  351. + $$->hookstr = chain_hookname_lookup($3);
  352. + if ($$->hookstr == NULL) {
  353. + erec_queue(error(&@3, "unknown chain hook %s", $3),
  354. + state->msgs);
  355. + xfree($3);
  356. + YYERROR;
  357. + }
  358. + xfree($3);
  359. +
  360. + $$->priority = $5;
  361. + }
  362. + | flowtable_block DEVICES '=' flowtable_expr stmt_separator
  363. + {
  364. + $$->dev_expr = $4;
  365. + }
  366. + ;
  367. +
  368. +flowtable_expr : '{' flowtable_list_expr '}'
  369. + {
  370. + $2->location = @$;
  371. + $$ = $2;
  372. + }
  373. + ;
  374. +
  375. +flowtable_list_expr : flowtable_expr_member
  376. + {
  377. + $$ = compound_expr_alloc(&@$, NULL);
  378. + compound_expr_add($$, $1);
  379. + }
  380. + | flowtable_list_expr COMMA flowtable_expr_member
  381. + {
  382. + compound_expr_add($1, $3);
  383. + $$ = $1;
  384. + }
  385. + | flowtable_list_expr COMMA opt_newline
  386. + ;
  387. +
  388. +flowtable_expr_member : STRING
  389. + {
  390. + $$ = symbol_expr_alloc(&@$, SYMBOL_VALUE,
  391. + current_scope(state),
  392. + $1);
  393. + }
  394. + ;
  395. +
  396. data_type_atom_expr : type_identifier
  397. {
  398. const struct datatype *dtype = datatype_lookup_byname($1);
  399. @@ -1720,6 +1807,21 @@ set_identifier : identifier
  400. }
  401. ;
  402. +
  403. +flowtable_spec : table_spec identifier
  404. + {
  405. + $$ = $1;
  406. + $$.flowtable = $2;
  407. + }
  408. + ;
  409. +
  410. +flowtable_identifier : identifier
  411. + {
  412. + memset(&$$, 0, sizeof($$));
  413. + $$.flowtable = $1;
  414. + }
  415. + ;
  416. +
  417. obj_spec : table_spec identifier
  418. {
  419. $$ = $1;
  420. --- a/src/rule.c
  421. +++ b/src/rule.c
  422. @@ -45,6 +45,8 @@ void handle_merge(struct handle *dst, co
  423. dst->chain = xstrdup(src->chain);
  424. if (dst->set == NULL && src->set != NULL)
  425. dst->set = xstrdup(src->set);
  426. + if (dst->flowtable == NULL && src->flowtable != NULL)
  427. + dst->flowtable = xstrdup(src->flowtable);
  428. if (dst->obj == NULL && src->obj != NULL)
  429. dst->obj = xstrdup(src->obj);
  430. if (dst->handle.id == 0)
  431. @@ -857,6 +859,7 @@ struct cmd *cmd_alloc(enum cmd_ops op, e
  432. void nft_cmd_expand(struct cmd *cmd)
  433. {
  434. struct list_head new_cmds;
  435. + struct flowtable *ft;
  436. struct table *table;
  437. struct chain *chain;
  438. struct rule *rule;
  439. @@ -896,6 +899,14 @@ void nft_cmd_expand(struct cmd *cmd)
  440. &set->location, set_get(set));
  441. list_add_tail(&new->list, &new_cmds);
  442. }
  443. + list_for_each_entry(ft, &table->flowtables, list) {
  444. + handle_merge(&ft->handle, &table->handle);
  445. + memset(&h, 0, sizeof(h));
  446. + handle_merge(&h, &ft->handle);
  447. + new = cmd_alloc(CMD_ADD, CMD_OBJ_FLOWTABLE, &h,
  448. + &ft->location, flowtable_get(ft));
  449. + list_add_tail(&new->list, &new_cmds);
  450. + }
  451. list_for_each_entry(chain, &table->chains, list) {
  452. list_for_each_entry(rule, &chain->rules, list) {
  453. memset(&h, 0, sizeof(h));
  454. @@ -982,6 +993,9 @@ void cmd_free(struct cmd *cmd)
  455. case CMD_OBJ_LIMIT:
  456. obj_free(cmd->object);
  457. break;
  458. + case CMD_OBJ_FLOWTABLE:
  459. + flowtable_free(cmd->flowtable);
  460. + break;
  461. default:
  462. BUG("invalid command object type %u\n", cmd->obj);
  463. }
  464. @@ -1071,6 +1085,9 @@ static int do_command_add(struct netlink
  465. case CMD_OBJ_CT_HELPER:
  466. case CMD_OBJ_LIMIT:
  467. return netlink_add_obj(ctx, &cmd->handle, cmd->object, flags);
  468. + case CMD_OBJ_FLOWTABLE:
  469. + return netlink_add_flowtable(ctx, &cmd->handle, cmd->flowtable,
  470. + flags);
  471. default:
  472. BUG("invalid command object type %u\n", cmd->obj);
  473. }
  474. --- a/src/scanner.l
  475. +++ b/src/scanner.l
  476. @@ -238,6 +238,7 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr
  477. "hook" { return HOOK; }
  478. "device" { return DEVICE; }
  479. +"devices" { return DEVICES; }
  480. "table" { return TABLE; }
  481. "tables" { return TABLES; }
  482. "chain" { return CHAIN; }
  483. @@ -249,6 +250,7 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr
  484. "element" { return ELEMENT; }
  485. "map" { return MAP; }
  486. "maps" { return MAPS; }
  487. +"flowtable" { return FLOWTABLE; }
  488. "handle" { return HANDLE; }
  489. "ruleset" { return RULESET; }
  490. "trace" { return TRACE; }