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); +}