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());