when you add a pf rule with a "on rdomain n" with nonexisting rdomain n,
the load will fail with the error

  pfctl: DIOCADDRULE: Device busy

with no information which rule caused the problem and no indication that the
problem is the rdomain <nonexisting>.

So lets check if the rdomain really exists when parsing the config.

Also parsing doesnot have to stop when this occurs, we can go on and
stop before actually loading the config and that way parse the complete
pf.conf and find more errors. Same goes for the rdomain range check, remove
YYERROR there too.

ok?

/Benno

(benno_pfctl_rdomain_check.diff)

diff --git sbin/pfctl/parse.y sbin/pfctl/parse.y
index fba07e2ea43..08797ebaabc 100644
--- sbin/pfctl/parse.y
+++ sbin/pfctl/parse.y
@@ -30,6 +30,7 @@
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <sys/stat.h>
+#include <sys/sysctl.h>
 #include <net/if.h>
 #include <netinet/in.h>
 #include <netinet/ip.h>
@@ -389,6 +390,7 @@ int  invalid_redirect(struct node_host *, sa_family_t);
 u_int16_t parseicmpspec(char *, sa_family_t);
 int     kw_casecmp(const void *, const void *);
 int     map_tos(char *string, int *);
+int     rdomain_exists(u_int);
 
 TAILQ_HEAD(loadanchorshead, loadanchors)
     loadanchorshead = TAILQ_HEAD_INITIALIZER(loadanchorshead);
@@ -2561,10 +2563,11 @@ if_item         : STRING                        {
                        $$->tail = $$;
                }
                | RDOMAIN NUMBER                {
-                       if ($2 < 0 || $2 > RT_TABLEID_MAX) {
-                               yyerror("rdomain outside range");
-                               YYERROR;
-                       }
+                       if ($2 < 0 || $2 > RT_TABLEID_MAX)
+                               yyerror("rdomain %lld outside range", $2);
+                       if (rdomain_exists($2) != 1)
+                               yyerror("rdomain %lld does not exist", $2);
+
                        $$ = calloc(1, sizeof(struct node_if));
                        if ($$ == NULL)
                                err(1, "if_item: calloc");
@@ -5950,3 +5953,35 @@ map_tos(char *s, int *val)
        }
        return (0);
 }
+
+int
+rdomain_exists(u_int rdomain)
+{
+       size_t                   len;
+       struct rt_tableinfo      info;
+       int                      mib[6];
+       static u_int             found[RT_TABLEID_MAX];
+
+       if (found[rdomain] == 1)
+               return (1);
+
+       mib[0] = CTL_NET;
+       mib[1] = PF_ROUTE;
+       mib[2] = 0;
+       mib[3] = 0;
+       mib[4] = NET_RT_TABLE;
+       mib[5] = rdomain;
+
+       len = sizeof(info);
+       if (sysctl(mib, 6, &info, &len, NULL, 0) == -1) {
+               if (errno == ENOENT)
+                       /* table nonexistent */
+                       return (0);
+               err(1, "sysctl");
+       }
+       if (info.rti_domainid == rdomain) {
+               found[rdomain] = 1;
+               return (1);
+       }
+       return (0);
+}

Reply via email to