Index | Thread | Search

From:
renaud@openbsd.org
Subject:
Re: tell pfctl(8) route-to no longer expects network interface
To:
Alexandr Nedvedicky <sashan@fastmail.net>, tech@openbsd.org
Cc:
mvs@openbsd.org
Date:
Sat, 2 May 2026 17:41:35 +0200

Download raw body.

Thread

On 02/05/2026 16:03, Alexandr Nedvedicky wrote:
> Hello,
> 
> Consider rule as follows:
> 
>      pass in on vio0 from vio0:network to ! (vio0) route-to wg1
> 
> When pfctl(8) in current resolves 'wg1' option at route-to action
> it attempts to interpret it also as interface name. This is wrong.
> When wg1 interface happens to be plumbed to system the parser
> accepts the rule with no error and uses wg1 IP address as next-hop
> for route-to action. Such configuration is invalid/unexpected because
> matching packets will be looped back to local IP stack.
> 
> since 6.9 route-to action expects next-hop/destination IP address where
> matching packets should be sent to.
> 
> diff below prevents parser to prevent route-to parameter as interface name.
> 
> The issue has been noticed and reported off-list by Vitaliy Makkoveev mvs@
> 
> OK to commit?
> 


It seems this breaks correct simple rules

amd64# echo 'match out on em0 inet nat-to (em0)' > /tmp/test.conf
amd64# /home/r/pfctl-patched -nvf /tmp/test.conf 
  

/tmp/test.conf:1: syntax error


> thanks and
> regards
> sashan
> 
> --------8<---------------8<---------------8<------------------8<--------
> diff --git a/sbin/pfctl/parse.y b/sbin/pfctl/parse.y
> index 92764edcf3b..74b0ebaa43b 100644
> --- a/sbin/pfctl/parse.y
> +++ b/sbin/pfctl/parse.y
> @@ -567,7 +567,7 @@ int	parseport(char *, struct range *r, int);
>   %type	<v.number>		reticmpspec reticmp6spec
>   %type	<v.fromto>		fromto
>   %type	<v.peer>		ipportspec from to
> -%type	<v.host>		ipspec xhost host dynaddr host_list
> +%type	<v.host>		ipspec xhost host dynaddr host_list redirhost
>   %type	<v.host>		table_host_list tablespec
>   %type	<v.host>		redir_host_list redirspec
>   %type	<v.os>			os xos os_list
> @@ -4032,7 +4032,7 @@ portstar	: numberstring			{
>   		}
>   		;
>   
> -redirspec	: host optweight		{
> +redirspec	: redirhost optweight		{
>   			if ($2 > 0) {
>   				struct node_host	*n;
>   				for (n = $1; n != NULL; n = n->next)
> @@ -4043,7 +4043,7 @@ redirspec	: host optweight		{
>   		| '{' optnl redir_host_list '}'	{ $$ = $3; }
>   		;
>   
> -redir_host_list	: host optweight optnl			{
> +redir_host_list	: redirhost optweight optnl			{
>   			if ($1->addr.type != PF_ADDR_ADDRMASK) {
>   				free($1);
>   				yyerror("only addresses can be listed for "
> @@ -4057,7 +4057,7 @@ redir_host_list	: host optweight optnl			{
>   			}
>   			$$ = $1;
>   		}
> -		| redir_host_list comma host optweight optnl {
> +		| redir_host_list comma redirhost optweight optnl {
>   			$1->tail->next = $3;
>   			$1->tail = $3->tail;
>   			if ($4 > 0) {
> @@ -4085,6 +4085,17 @@ redirpool	: redirspec		{
>   		}
>   		;
>   
> +redirhost	: STRING {
> +			$$ = host($1, pf->opts | PF_OPT_NOIFNAME);
> +			if ($$ == NULL)  {
> +				free($1);
> +				yyerror("could not parse host specification");
> +				YYERROR;
> +			}
> +			free($1);
> +                }
> +		;
> +
>   hashkey		: /* empty */
>   		{
>   			$$ = calloc(1, sizeof(struct pf_poolhashkey));
> diff --git a/sbin/pfctl/pfctl_parser.c b/sbin/pfctl/pfctl_parser.c
> index f47ee3e8568..0905d5bf15c 100644
> --- a/sbin/pfctl/pfctl_parser.c
> +++ b/sbin/pfctl/pfctl_parser.c
> @@ -77,7 +77,7 @@ void		 print_scspec(const char *, struct pf_queue_scspec *);
>   int		 ifa_skip_if(const char *filter, struct node_host *p);
>   
>   struct node_host	*ifa_grouplookup(const char *, int);
> -struct node_host	*host_if(const char *, int);
> +struct node_host	*host_if(const char *, int, int);
>   struct node_host	*host_ip(const char *, int);
>   struct node_host	*host_dns(const char *, int, int);
>   
> @@ -1713,7 +1713,7 @@ host(const char *s, int opts)
>   		p[0] = '\0';
>   	}
>   
> -	if ((h = host_if(ps, mask)) == NULL &&
> +	if ((h = host_if(ps, mask, (opts & PF_OPT_NOIFNAME))) == NULL &&
>   	    (h = host_ip(ps, mask)) == NULL &&
>   	    (h = host_dns(ps, mask, (opts & PF_OPT_NODNS))) == NULL) {
>   		fprintf(stderr, "no IP address found for %s\n", s);
> @@ -1731,12 +1731,15 @@ error:
>   }
>   
>   struct node_host *
> -host_if(const char *s, int mask)
> +host_if(const char *s, int mask, int reject_iface)
>   {
>   	struct node_host	*n, *h = NULL;
>   	char			*p, *ps;
>   	int			 flags = 0;
>   
> +	if (reject_iface)
> +		return (NULL);
> +
>   	if ((ps = strdup(s)) == NULL)
>   		err(1, "host_if: strdup");
>   	while ((p = strrchr(ps, ':')) != NULL) {
> diff --git a/sbin/pfctl/pfctl_parser.h b/sbin/pfctl/pfctl_parser.h
> index c65a805ad90..dc52f195a9f 100644
> --- a/sbin/pfctl/pfctl_parser.h
> +++ b/sbin/pfctl/pfctl_parser.h
> @@ -53,6 +53,7 @@
>   #define PF_OPT_PORTNAMES	0x08000
>   #define PF_OPT_IGNFAIL		0x10000
>   #define PF_OPT_CALLSHOW		0x20000
> +#define PF_OPT_NOIFNAME		0x40000
>   
>   #define PF_TH_ALL		0xFF
>   
>