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

Reply via email to