Index | Thread | Search

From:
Rafael Sadowski <rafael@sizeofvoid.org>
Subject:
Re: relayd: support multiple resolveble addresses
To:
tech@openbsd.org
Date:
Sun, 17 May 2026 03:27:37 +0200

Download raw body.

Thread
  • Kirill A. Korinsky:

    relayd: support multiple resolveble addresses

    • Rafael Sadowski:

      relayd: support multiple resolveble addresses

  • On Sat May 16, 2026 at 04:30:31PM +0200, Kirill A. Korinsky wrote:
    > On Mon, 06 Apr 2026 11:33:21 +0200,
    > "Kirill A. Korinsky" <kirill@korins.ky> wrote:
    > > 
    > > tech@,
    > > 
    > > relayd supports interface name, DNS hostname or interface group at
    > > listen on but it binds only to the first discovered IP address which is
    > > usually IPv4.
    > > 
    > > Here I changed parser to create a dedicated listener for each discovered
    > > and confiugred on a local inerface address.
    > > 
    > > This means that listen on egress port 80 binds to all IPv4 and IPv6
    > > addresses on the egress group.
    > > 
    > 
    > Ok?
    
    Tested with "lo" with all regression tests. OK rsadowski@
    
    > 
    > Index: etc/examples/relayd.conf
    > ===================================================================
    > RCS file: /home/cvs/src/etc/examples/relayd.conf,v
    > diff -u -p -r1.6 relayd.conf
    > --- etc/examples/relayd.conf	29 Oct 2023 11:27:11 -0000	1.6
    > +++ etc/examples/relayd.conf	2 May 2026 11:41:39 -0000
    > @@ -2,7 +2,6 @@
    >  #
    >  # Macros
    >  #
    > -ext_addr="192.168.1.1"
    >  webhost1="10.0.0.1"
    >  webhost2="10.0.0.2"
    >  sshhost1="10.0.0.3"
    > @@ -24,7 +23,7 @@ table <fallback> { 127.0.0.1 }
    >  # Services will be mapped to a rdr rule.
    >  #
    >  redirect www {
    > -	listen on $ext_addr port http interface trunk0
    > +	listen on egress port http
    >  
    >  	# tag every packet that goes thru the rdr rule with RELAYD
    >  	pftag RELAYD
    > @@ -51,7 +50,7 @@ http protocol https {
    >  
    >  relay wwwtls {
    >  	# Run as a TLS accelerator
    > -	listen on $ext_addr port 443 tls
    > +	listen on egress port https tls
    >  	protocol https
    >  
    >  	# Forward to hosts in the webhosts table using a src/dst hash
    > @@ -69,7 +68,7 @@ protocol sshtcp {
    >  
    >  relay sshgw {
    >  	# Run as a simple TCP relay
    > -	listen on $ext_addr port 2222
    > +	listen on egress port 2222
    >  	protocol sshtcp
    >  
    >  	# Forward to the shared carp(4) address of an internal gateway
    > Index: usr.sbin/relayd/parse.y
    > ===================================================================
    > RCS file: /home/cvs/src/usr.sbin/relayd/parse.y,v
    > diff -u -p -r1.263 parse.y
    > --- usr.sbin/relayd/parse.y	15 May 2026 13:57:24 -0000	1.263
    > +++ usr.sbin/relayd/parse.y	16 May 2026 14:29:10 -0000
    > @@ -131,8 +131,10 @@ int		 host_dns(const char *, struct addr
    >  		    int, struct portrange *, const char *, int);
    >  int		 host_if(const char *, struct addresslist *,
    >  		    int, struct portrange *, const char *, int);
    > +int		 host_ifaddr(struct sockaddr_storage *, struct ifaddrs *);
    >  int		 host(const char *, struct addresslist *,
    >  		    int, struct portrange *, const char *, int);
    > +int		 host_local(struct addresslist *);
    >  void		 host_free(struct addresslist *);
    >  
    >  struct table	*table_inherit(struct table *);
    > @@ -1995,35 +1997,60 @@ relayopts_l	: relayopts_l relayoptsl nl
    >  relayoptsl	: LISTEN ON STRING port opttls {
    >  			struct addresslist	 al;
    >  			struct address		*h;
    > -			struct relay		*r;
    > +			struct relay		*nr, *r;
    > +			int			 cnt = 0;
    >  
    >  			if (rlay->rl_conf.ss.ss_family != AF_UNSPEC) {
    > -				if ((r = calloc(1, sizeof (*r))) == NULL)
    > +				if ((r = calloc(1, sizeof(*r))) == NULL)
    >  					fatal("out of memory");
    > -				TAILQ_INSERT_TAIL(&relays, r, rl_entry);
    >  			} else
    >  				r = rlay;
    >  			if ($4.op != PF_OP_EQ) {
    >  				yyerror("invalid port");
    >  				free($3);
    > +				if (r != rlay)
    > +					free(r);
    >  				YYERROR;
    >  			}
    >  
    >  			TAILQ_INIT(&al);
    > -			if (host($3, &al, 1, &$4, NULL, -1) <= 0) {
    > +			if (host($3, &al, SRV_MAX_VIRTS, &$4, NULL, -1) <= 0) {
    >  				yyerror("invalid listen ip: %s", $3);
    >  				free($3);
    > +				if (r != rlay)
    > +					free(r);
    > +				YYERROR;
    > +			}
    > +			if (host_local(&al) == 0) {
    > +				yyerror("no local listen ip: %s", $3);
    > +				free($3);
    > +				host_free(&al);
    > +				if (r != rlay)
    > +					free(r);
    >  				YYERROR;
    >  			}
    >  			free($3);
    > -			h = TAILQ_FIRST(&al);
    > -			bcopy(&h->ss, &r->rl_conf.ss, sizeof(r->rl_conf.ss));
    > -			r->rl_conf.port = h->port.val[0];
    > -			if ($5) {
    > -				r->rl_conf.flags |= F_TLS;
    > -				conf->sc_conf.flags |= F_TLS;
    > +			TAILQ_FOREACH(h, &al, entry) {
    > +				if (cnt == 0) {
    > +					nr = r;
    > +					if (nr != rlay)
    > +						TAILQ_INSERT_TAIL(&relays, nr,
    > +						    rl_entry);
    > +				} else {
    > +					if ((nr = calloc(1, sizeof(*nr))) == NULL)
    > +						fatal("out of memory");
    > +					TAILQ_INSERT_TAIL(&relays, nr, rl_entry);
    > +				}
    > +				bcopy(&h->ss, &nr->rl_conf.ss,
    > +				    sizeof(nr->rl_conf.ss));
    > +				nr->rl_conf.port = h->port.val[0];
    > +				if ($5)
    > +					nr->rl_conf.flags |= F_TLS;
    > +				cnt++;
    >  			}
    > -			tableport = h->port.val[0];
    > +			if ($5)
    > +				conf->sc_conf.flags |= F_TLS;
    > +			tableport = $4.val[0];
    >  			host_free(&al);
    >  		}
    >  		| forwardmode opttlsclient TO forwardspec dstaf optproxyproto {
    > @@ -3376,6 +3403,64 @@ host(const char *s, struct addresslist *
    >  
    >  	TAILQ_INSERT_HEAD(al, h, entry);
    >  	return (1);
    > +}
    > +
    > +int
    > +host_ifaddr(struct sockaddr_storage *ss, struct ifaddrs *ifap)
    > +{
    > +	struct ifaddrs		*p;
    > +	struct sockaddr_in	*sin;
    > +	struct sockaddr_in6	*sin6;
    > +
    > +	switch (ss->ss_family) {
    > +	case AF_INET:
    > +		sin = (struct sockaddr_in *)ss;
    > +		if (sin->sin_addr.s_addr == INADDR_ANY)
    > +			return (1);
    > +		break;
    > +	case AF_INET6:
    > +		sin6 = (struct sockaddr_in6 *)ss;
    > +		if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr))
    > +			return (1);
    > +		break;
    > +	default:
    > +		return (0);
    > +	}
    > +
    > +	for (p = ifap; p != NULL; p = p->ifa_next) {
    > +		if (p->ifa_addr == NULL ||
    > +		    p->ifa_addr->sa_family != ss->ss_family ||
    > +		    sockaddr_cmp((struct sockaddr *)ss, p->ifa_addr, -1) != 0)
    > +			continue;
    > +		bzero(ss, sizeof(*ss));
    > +		memcpy(ss, p->ifa_addr, p->ifa_addr->sa_len);
    > +		return (1);
    > +	}
    > +
    > +	return (0);
    > +}
    > +
    > +int
    > +host_local(struct addresslist *al)
    > +{
    > +	struct ifaddrs		*ifap;
    > +	struct address		*h, *next;
    > +	int			 cnt = 0;
    > +
    > +	if (getifaddrs(&ifap) == -1)
    > +		fatal("getifaddrs");
    > +
    > +	TAILQ_FOREACH_SAFE(h, al, entry, next) {
    > +		if (host_ifaddr(&h->ss, ifap)) {
    > +			cnt++;
    > +			continue;
    > +		}
    > +		TAILQ_REMOVE(al, h, entry);
    > +		free(h);
    > +	}
    > +
    > +	freeifaddrs(ifap);
    > +	return (cnt);
    >  }
    >  
    >  void
    > Index: usr.sbin/relayd/relayd.conf.5
    > ===================================================================
    > RCS file: /home/cvs/src/usr.sbin/relayd/relayd.conf.5,v
    > diff -u -p -r1.216 relayd.conf.5
    > --- usr.sbin/relayd/relayd.conf.5	15 May 2026 13:57:24 -0000	1.216
    > +++ usr.sbin/relayd/relayd.conf.5	16 May 2026 14:29:10 -0000
    > @@ -727,7 +727,19 @@ Like the previous directive, but for red
    >  .Op Ic tls
    >  .Xc
    >  Specify the address and port for the relay to listen on.
    > -The relay will accept incoming connections to the specified address.
    > +The relay will accept incoming connections to the specified address or
    > +addresses.
    > +If
    > +.Ar address
    > +resolves to multiple IPv4 or IPv6 addresses, such as an interface
    > +name, interface group, or DNS hostname,
    > +.Xr relayd 8
    > +will create a listener for each local address.
    > +For DNS hostnames, all resolved IPv4 and IPv6 addresses are considered,
    > +but only addresses configured on a local interface are used.
    > +Addresses that are not configured on a local interface are ignored.
    > +If none of the resolved addresses are local, the configuration is
    > +invalid.
    >  If the
    >  .Ic tls
    >  keyword is present, the relay will accept connections using the
    > 
    > 
    > -- 
    > wbr, Kirill
    
    
  • Kirill A. Korinsky:

    relayd: support multiple resolveble addresses