Mercurial > hg > freeDiameter
diff extensions/rt_rewrite/rt_rewrite_conf.y @ 1341:b0401251d8c0
rt_rewrite: new extension
This extension allows rewriting messages: putting data from one AVP
into another, or removing AVPs altogether.
Written for Effortel Technologies SA and published with their consent.
author | Thomas Klausner <tk@giga.or.at> |
---|---|
date | Tue, 09 Apr 2019 16:01:29 +0200 |
parents | |
children | edfb2b662b91 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extensions/rt_rewrite/rt_rewrite_conf.y Tue Apr 09 16:01:29 2019 +0200 @@ -0,0 +1,421 @@ +/********************************************************************************************************* + * Software License Agreement (BSD License) * + * Author: Thomas Klausner <tk@giga.or.at> * + * * + * Copyright (c) 2018, Thomas Klausner * + * All rights reserved. * + * * + * Written under contract by Effortel Technologies SA, http://effortel.com/ * + * * + * Redistribution and use of this software in source and binary forms, with or without modification, are * + * permitted provided that the following conditions are met: * + * * + * * Redistributions of source code must retain the above * + * copyright notice, this list of conditions and the * + * following disclaimer. * + * * + * * Redistributions in binary form must reproduce the above * + * copyright notice, this list of conditions and the * + * following disclaimer in the documentation and/or other * + * materials provided with the distribution. * + * * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED * + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * + *********************************************************************************************************/ + +/* Yacc extension's configuration parser. + */ + +/* For development only : */ +%debug +%error-verbose + +/* The parser receives the configuration file filename as parameter */ +%parse-param {char * conffile} + +/* Keep track of location */ +%locations +%pure-parser + +%{ +#include "rt_rewrite.h" +#include "rt_rewrite_conf.tab.h" /* bison is not smart enough to define the YYLTYPE before including this code, so... */ + +/* Forward declaration */ +int yyparse(char * conffile); +void rt_rewrite_confrestart(FILE *input_file); + +/* copied from libfdproto/dictionary.c because the symbol is not public */ +static const char * type_base_name[] = { /* must keep in sync with dict_avp_basetype */ + "Grouped", /* AVP_TYPE_GROUPED */ + "Octetstring", /* AVP_TYPE_OCTETSTRING */ + "Integer32", /* AVP_TYPE_INTEGER32 */ + "Integer64", /* AVP_TYPE_INTEGER64 */ + "Unsigned32", /* AVP_TYPE_UNSIGNED32 */ + "Unsigned64", /* AVP_TYPE_UNSIGNED64 */ + "Float32", /* AVP_TYPE_FLOAT32 */ + "Float64" /* AVP_TYPE_FLOAT64 */ + }; + +static struct avp_match *avp_match_new(char *name); + +static struct avp_match *source_target = NULL, *drop_target = NULL; +static struct avp_target *dest_target = NULL; + +static void print_target(struct avp_target *target, char *prefix) +{ + char *output = NULL; + if (asprintf(&output, "%s -> /TOP/%s", prefix, target->name) == -1) { + fd_log_error("rt_rewrite: print_target: setup: asprintf failed: %s", strerror(errno)); + return; + } + for (target=target->child; target != NULL; target=target->child) { + char *new_output = NULL; + if (asprintf(&new_output, "%s/%s", output, target->name) == -1) { + fd_log_error("rt_rewrite: print_target: asprintf failed: %s", strerror(errno)); + free(output); + return; + } + free(output); + output = new_output; + new_output = NULL; + } + fd_log_debug(output); + free(output); + return; +} + +static void compare_avp_type(const char *source, const char *dest) +{ + struct dict_object *model_source, *model_dest; + struct dict_avp_data dictdata_source, dictdata_dest; + + if (fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME_ALL_VENDORS, source, &model_source, ENOENT) != 0) { + fd_log_error("Unable to find '%s' AVP in the loaded dictionaries", source); + return; + } + if (fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME_ALL_VENDORS, dest, &model_dest, ENOENT) != 0) { + fd_log_error("Unable to find '%s' AVP in the loaded dictionaries", dest); + return; + } + fd_dict_getval(model_source, &dictdata_source); + fd_dict_getval(model_dest, &dictdata_dest); + if (dictdata_source.avp_basetype != dictdata_dest.avp_basetype) { + fd_log_notice("rt_rewrite: type mismatch: %s (type %s) mapped to %s (type %s) (continuing anyway)", source, type_base_name[dictdata_source.avp_basetype], dest, type_base_name[dictdata_dest.avp_basetype]); + } + return; +} + +static void compare_avp_types(struct avp_match *start) +{ + struct avp_match *iter; + for (iter=start; iter != NULL; iter=iter->next) { + compare_avp_types(iter->children); + if (iter->target) { + struct avp_target *final; + final = iter->target; + while (final->child) { + final = final->child; + } + compare_avp_type(iter->name, final->name); + } + } + return; +} + +static void dump_config(struct avp_match *start, char *prefix) +{ + char *new_prefix = NULL; + struct avp_match *iter; + for (iter=start; iter != NULL; iter=iter->next) { + if (asprintf(&new_prefix, "%s/%s", prefix, iter->name) == -1) { + fd_log_error("rt_rewrite: dump_config: asprintf failed: %s", strerror(errno)); + return; + } + dump_config(iter->children, new_prefix); + if (iter->target) { + print_target(iter->target, new_prefix); + } + if (iter->drop) { + fd_log_debug("%s -> DROP", new_prefix); + } + free(new_prefix); + new_prefix = NULL; + } + return; +} + +/* Parse the configuration file */ +int rt_rewrite_conf_handle(char * conffile) +{ + extern FILE * rt_rewrite_confin; + int ret; + char *top; + + TRACE_ENTRY("%p", conffile); + + TRACE_DEBUG (FULL, "Parsing configuration file: '%s'", conffile); + + /* to match other entries */ + if ((top=strdup("TOP")) == NULL) { + fd_log_error("strdup error: %s", strerror(errno)); + return EINVAL; + } + if ((avp_match_start=avp_match_new(top)) == NULL) { + fd_log_error("malloc error: %s", strerror(errno)); + free(top); + return EINVAL; + } + rt_rewrite_confin = fopen(conffile, "r"); + if (rt_rewrite_confin == NULL) { + ret = errno; + fd_log_debug("Unable to open extension configuration file '%s' for reading: %s", conffile, strerror(ret)); + TRACE_DEBUG (INFO, "rt_rewrite: error occurred, message logged -- configuration file."); + avp_match_free(avp_match_start); + avp_match_start = NULL; + return ret; + } + + rt_rewrite_confrestart(rt_rewrite_confin); + ret = yyparse(conffile); + + fclose(rt_rewrite_confin); + + if (ret != 0) { + TRACE_DEBUG(INFO, "rt_rewrite: unable to parse the configuration file."); + avp_match_free(avp_match_start); + avp_match_start = NULL; + return EINVAL; + } + + compare_avp_types(avp_match_start); + dump_config(avp_match_start, ""); + + return 0; +} + +static int verify_avp(const char *name) +{ + struct dict_object *model; + struct dict_avp_data dictdata; + + if (fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME_ALL_VENDORS, name, &model, ENOENT) != 0) { + fd_log_error("Unable to find '%s' AVP in the loaded dictionaries", name); + return -1; + } + fd_dict_getval(model, &dictdata); + if (dictdata.avp_basetype == AVP_TYPE_GROUPED) { + return 1; + } + return 0; +} + +static struct avp_match *avp_match_new(char *name) { + struct avp_match *ret; + + if ((ret=malloc(sizeof(*ret))) == NULL) { + fd_log_error("malloc error"); + return NULL; + } + ret->name = name; + ret->next = NULL; + ret->children = NULL; + ret->target = NULL; + ret->drop = 0; + return ret; +} + +static void avp_target_free(struct avp_target *target) { + struct avp_target *iter; + + for (iter=target; iter != NULL; ) { + struct avp_target *next; + free(iter->name); + next = iter->child; + free(iter); + iter = next; + } +} + +void avp_match_free(struct avp_match *match) { + struct avp_match *iter; + + for (iter=match; iter != NULL; ) { + struct avp_match *next; + free(iter->name); + next = iter->next; + avp_match_free(iter->children); + avp_target_free(iter->target); + free(iter); + iter = next; + } +} + +static struct avp_target *target_new(char *name) { + struct avp_target *ret; + + if ((ret=malloc(sizeof(*ret))) == NULL) { + fd_log_error("malloc error"); + return NULL; + } + ret->name = name; + ret->child = NULL; + return ret; +} + +static struct avp_match *add_avp_next_to(char *name, struct avp_match *target) +{ + struct avp_match *iter, *prev; + + if (target == NULL) { + return avp_match_new(name); + } + + for (prev=iter=target; iter != NULL; iter=iter->next) { + if (strcmp(iter->name, name) == 0) { + return iter; + } + prev = iter; + } + prev->next = avp_match_new(name); + return prev->next; +} + +static int add(struct avp_match **target, char *name) +{ + struct avp_match *temp; + if (verify_avp(name) < 0) { + return -1; + } + temp = add_avp_next_to(name, (*target)->children); + if ((*target)->children == NULL) { + (*target)->children = temp; + } + *target = temp; + return 0; +} + +/* build tree for source */ +static int source_add(char *name) +{ + if (source_target == NULL) { + source_target = avp_match_start; + } + return add(&source_target, name); +} + +/* build tree for destination */ +static int dest_add(char *name) +{ + struct avp_target *temp; + + if (verify_avp(name) < 0) { + return -1; + } + if ((temp=target_new(name)) == NULL) { + dest_target = NULL; + source_target = NULL; + return -1; + } + if (dest_target == NULL) { + dest_target = temp; + source_target->target = dest_target; + source_target = NULL; + return 0; + } + dest_target->child = temp; + dest_target = temp; + return 0; +} + +static void dest_finish(void) +{ + dest_target = NULL; +} + +/* same as source_add, but for drop */ +static int drop_add(char *name) +{ + if (drop_target == NULL) { + drop_target = avp_match_start; + } + return add(&drop_target, name); +} + +/* mark as to-drop */ +static void drop_finish(void) +{ + drop_target->drop = 1; + drop_target = NULL; +} + +/* The Lex parser prototype */ +int rt_rewrite_conflex(YYSTYPE *lvalp, YYLTYPE *llocp); + +/* Function to report the errors */ +void yyerror (YYLTYPE *ploc, char * conffile, char const *s) +{ + TRACE_DEBUG(INFO, "rt_rewrite: error in configuration parsing"); + + if (ploc->first_line != ploc->last_line) + fd_log_debug("%s:%d.%d-%d.%d : %s", conffile, ploc->first_line, ploc->first_column, ploc->last_line, ploc->last_column, s); + else if (ploc->first_column != ploc->last_column) + fd_log_debug("%s:%d.%d-%d : %s", conffile, ploc->first_line, ploc->first_column, ploc->last_column, s); + else + fd_log_debug("%s:%d.%d : %s", conffile, ploc->first_line, ploc->first_column, s); +} + +%} + +/* Values returned by lex for token */ +%union { + char *string; /* The string is allocated by strdup in lex.*/ +} + +/* In case of error in the lexical analysis */ +%token LEX_ERROR + +/* A (de)quoted string (malloc'd in lex parser; it must be freed after use) */ +%token <string> QSTRING + +/* Tokens */ +%token MAP +%token DROP + + +/* -------------------------------------- */ +%% + + /* The grammar definition */ +rules: /* empty ok */ + | rules map + | rules drop + ; + +/* source -> destination mapping */ +map: MAP '=' source_part '>' dest_part { dest_finish(); } + ';' + ; + +source_part: source_part ':' QSTRING { if (source_add($3) < 0) { YYERROR; } } + | QSTRING { if (source_add($1) < 0) { YYERROR; } } + ; + +dest_part: dest_part ':' QSTRING { if (dest_add($3) < 0) { YYERROR; } } + | QSTRING { if (dest_add($1) < 0) { YYERROR; } } + ; + +/* for dropping an AVP */ +drop: DROP '=' drop_part { drop_finish(); } + ';' + ; + +drop_part: drop_part ':' QSTRING { if (drop_add($3) < 0) { YYERROR; } } + | QSTRING { if (drop_add($1) < 0) { YYERROR; } } + ;