/* * BIRD -- Aggregator configuration * * (c) 2023 Igor Putovny * (c) 2023 Maria Matejka * (c) 2023 CZ.NIC z.s.p.o. * * Can be freely distributed and used under the terms of the GNU GPL. */ CF_HDR #include "proto/aggregator/aggregator.h" CF_DEFINES #define AGGREGATOR_CFG ((struct aggregator_config *) this_proto) #define AGGR_ITEM_ALLOC ((struct aggr_item_node *) cfg_allocz(sizeof(struct aggr_item_node))) CF_DECLS CF_KEYWORDS(AGGREGATOR, AGGREGATE, ON, MERGE, BY) %type aggr_item aggr_list CF_GRAMMAR proto: aggregator_proto ; aggregator_proto_start: proto_start AGGREGATOR { this_proto = proto_config_new(&proto_aggregator, $1); this_channel = AGGREGATOR_CFG->src = channel_config_new(NULL, "source", 0, this_proto); AGGREGATOR_CFG->dst = channel_config_new(NULL, "destination", 0, this_proto); AGGREGATOR_CFG->src->ra_mode = AGGREGATOR_CFG->dst->ra_mode = RA_ANY; }; aggregator_proto_item: proto_item | channel_item_ | PEER TABLE rtable { AGGREGATOR_CFG->dst->table = $3; } | AGGREGATE ON { if (AGGREGATOR_CFG->aggr_on) cf_error("Only one aggregate on clause allowed"); cf_enter_filters(); cf_push_block_scope(new_config); } aggr_list { int count = new_config->current_scope->slots; cf_pop_block_scope(new_config); cf_exit_filters(); if (!AGGREGATOR_CFG->aggr_on_net) cf_error("aggregate on must be always include 'net'."); struct f_inst *rot = NULL; while ($4.begin) { struct f_inst *tmp = $4.begin->next; $4.begin->next = rot; rot = $4.begin; $4.begin = tmp; } AGGREGATOR_CFG->aggr_on_count = count; AGGREGATOR_CFG->aggr_on = f_linearize(rot, count); struct f_line *premerge = f_linearize($4.end, 0); premerge->args = count; AGGREGATOR_CFG->premerge = premerge; } | MERGE BY { cf_enter_filters(); cf_push_block_scope(new_config); f_predefined_variable(new_config, "routes", T_ROUTES_BLOCK); } function_body { cf_pop_block_scope(new_config); cf_exit_filters(); $4->args++; AGGREGATOR_CFG->merge_by = $4; } ; aggregator_proto_opts: /* empty */ | aggregator_proto_opts aggregator_proto_item ';' ; aggregator_proto: aggregator_proto_start proto_name '{' aggregator_proto_opts '}' ; aggr_list: aggr_item | aggr_list ',' aggr_item { if ($$.begin = $3.begin) $$.begin->next = $1.begin; else $$.begin = $1.begin; if ($$.end = $3.end) $$.end->next = $1.end; else $$.end = $1.end; } ; aggr_item: '(' term ')' { switch ($2->type) { case T_INT: case T_BOOL: case T_PAIR: case T_QUAD: case T_ENUM: case T_IP: case T_EC: case T_LC: case T_RD: /* Fits, OK */ break; default: cf_error("Expression evaluated to type %s unsupported by aggregator. Store this value as a custom attribute instead", f_type_name($2->type)); } $$.begin = $2; $$.end = NULL; f_new_var(new_config->current_scope); } | lvalue { $$.begin = f_lval_getter(&$1); int vari = f_new_var(new_config->current_scope); if ($1.type == F_LVAL_SA && $1.sa.sa_code == SA_NET) AGGREGATOR_CFG->aggr_on_net = 1; if (($1.type == F_LVAL_CONSTANT) || ($1.type == F_LVAL_SA && $1.sa.readonly)) $$.end = NULL; else { char varname[12]; bsnprintf(varname, 12, "!aggr%d", vari); $$.end = f_lval_setter(&$1, f_new_inst(FI_VAR_GET, cf_define_symbol( new_config, cf_get_symbol(new_config, varname), SYM_VARIABLE | $$.begin->type, offset, vari ))); } } ; CF_CODE CF_END