bgpd's parse.y uses a lot of STRING that is then further bisected in the actual rule. One good example are all communities. Now if someone wants to use macros in such arguments they do not work in all cases. e.g. large-community $someas:1:2 works but large-community 1:$someas:2 does not.
Right now macro expansion only happens at the start of a token but not inside a string token. The following diff changes this. It will also expand: large-community $someas:$otheras:42 This only works if the macro name ends on a not-allowed-in-macro-name character ([^a-zA-Z0-9_]). So while 'descr v4_$name' or 'descr $name-v4' works 'descr $name_v4' will not. Also no expansion happens inside quoted strings like 'descr "$name-v4"'. -- :wq Claudio Index: parse.y =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/parse.y,v retrieving revision 1.422 diff -u -p -r1.422 parse.y --- parse.y 23 Feb 2022 11:20:35 -0000 1.422 +++ parse.y 8 Mar 2022 09:25:55 -0000 @@ -48,6 +48,8 @@ #include "rde.h" #include "log.h" +#define MACRO_NAME_LEN 128 + TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files); static struct file { TAILQ_ENTRY(file) entry; @@ -74,6 +76,7 @@ int igetc(void); int lgetc(int); void lungetc(int); int findeol(void); +int expand_macro(void); TAILQ_HEAD(symhead, sym) symhead = TAILQ_HEAD_INITIALIZER(symhead); struct sym { @@ -380,17 +383,25 @@ yesno : STRING { varset : STRING '=' string { char *s = $1; + if (strlen($1) >= MACRO_NAME_LEN) { + yyerror("macro name to long, max %d characters", + MACRO_NAME_LEN - 1); + free($1); + free($3); + YYERROR; + } + do { + if (isalnum((unsigned char)*s) || *s == '_') + continue; + yyerror("macro name can only contain " + "alphanumerics and '_'"); + free($1); + free($3); + YYERROR; + } while (*++s); + if (cmd_opts & BGPD_OPT_VERBOSE) printf("%s = \"%s\"\n", $1, $3); - while (*s++) { - if (isspace((unsigned char)*s)) { - yyerror("macro name cannot contain " - "whitespace"); - free($1); - free($3); - YYERROR; - } - } if (symset($1, $3, 0) == -1) fatal("cannot store variable"); free($1); @@ -3169,10 +3180,46 @@ findeol(void) } int +expand_macro(void) +{ + char buf[MACRO_NAME_LEN]; + char *p, *val; + int c; + + p = buf; + while (1) { + if ((c = lgetc('$')) == EOF) + return (ERROR); + if (p + 1 >= buf + sizeof(buf) - 1) { + yyerror("macro name too long"); + return (ERROR); + } + if (isalnum(c) || c == '_') { + *p++ = c; + continue; + } + *p = '\0'; + lungetc(c); + break; + } + val = symget(buf); + if (val == NULL) + yyerror("macro '%s' not defined", buf); + p = val + strlen(val) - 1; + lungetc(DONE_EXPAND); + while (p >= val) { + lungetc((unsigned char)*p); + p--; + } + lungetc(START_EXPAND); + return (0); +} + +int yylex(void) { char buf[8096]; - char *p, *val; + char *p; int quotec, next, c; int token; @@ -3186,34 +3233,9 @@ top: while ((c = lgetc(0)) != '\n' && c != EOF) ; /* nothing */ if (c == '$' && !expanding) { - while (1) { - if ((c = lgetc(0)) == EOF) - return (0); - - if (p + 1 >= buf + sizeof(buf) - 1) { - yyerror("string too long"); - return (findeol()); - } - if (isalnum(c) || c == '_') { - *p++ = c; - continue; - } - *p = '\0'; - lungetc(c); - break; - } - val = symget(buf); - if (val == NULL) { - yyerror("macro '%s' not defined", buf); - return (findeol()); - } - p = val + strlen(val) - 1; - lungetc(DONE_EXPAND); - while (p >= val) { - lungetc((unsigned char)*p); - p--; - } - lungetc(START_EXPAND); + c = expand_macro(); + if (c != 0) + return (c); goto top; } @@ -3321,7 +3343,13 @@ nodigits: if (isalnum(c) || c == ':' || c == '_' || c == '*') { do { - *p++ = c; + if (c == '$' && !expanding) { + c = expand_macro(); + if (c != 0) + return (c); + } else + *p++ = c; + if ((size_t)(p-buf) >= sizeof(buf)) { yyerror("string too long"); return (findeol());