Index | Thread | Search

From:
Alexander Bluhm <bluhm@openbsd.org>
Subject:
Re: remove net.inet6.ip6.soiikey sysctl
To:
tech <tech@openbsd.org>
Date:
Tue, 5 Aug 2025 13:30:06 +0200

Download raw body.

Thread
On Sun, Aug 03, 2025 at 08:04:40PM +0200, Florian Obser wrote:
> RFC 7217 - "A Method for Generating Semantically Opaque Interface
> Identifiers with IPv6 Stateless Address Autoconfiguration (SLAAC)"
> applies to global addresses as well as to link-local address.
> 
> However, shortly after implementing it we found out that there are a
> bunch of hosting providers that
> a) hand out IPv6 addresses via slaac
> b) know which mac-address their rental servers have
> c) calculate an expect link-local address from the known mac-address
> d) only route to the expect link-local address.
> 
> Implementing 7217 makes things not work, so we quickly disabled it for
> link-local addresses. But that also means we no longer need the soiikey
> in the kernel. This diff rips it all out and makes slaacd(8) read
> /etc/soii.key file.
> 
> I can think of two behavioural changes, both are not important.
> 1. previously one could set a new key using sysctl, down & up the
> interface and slaacd would calculate a new IPv6 address and configure
> it. With the diff slaacd only reads soii.key on startup. A restart of
> slaacd would get the old behaviour back. I consider this an
> implementation detail.
> 2. The installer no longer sees soii.key during upgrade. So slaacd will
> create an IPv6 address with a soii.key of all zero. It did so before and
> *additionally* create a 2nd IPv6 address with the soii.key read from
> /mnt/etc/soii.key. I don't think anyone could have dependent on this,
> given the complicated IPv6 source address selection, picking the
> "correct" address would be dumb luck.
> 
> It's probably best to first commit userland and then a week later or so
> the kernel bits. I've successfully built a release with the full diff.

Please commit slaacd well before removing kernel support.  Especially
removing the pledge exception in kernel before new daemons have
been distibuted may hurt people.

> OK?

If the key is too short parse_hex_string() should fill dst with \0
until dstlen.  Or read_soiikey() should insist that parse_hex_string()
returns sizeof(soiikey).

read_soiikey() leaks the file descriptor in the successful case.

Otherwise OK bluhm@

> diff --git distrib/miniroot/install.sub distrib/miniroot/install.sub
> index c7eea4df0ff..b2a6844b297 100644
> --- distrib/miniroot/install.sub
> +++ distrib/miniroot/install.sub
> @@ -2653,9 +2653,6 @@ enable_network() {
>  	echo "127.0.0.1\tlocalhost" >/tmp/i/hosts
>  	echo "::1\t\tlocalhost" >>/tmp/i/hosts
>  
> -	_f=/mnt/etc/soii.key
> -	[[ -f $_f ]] && sysctl "net.inet6.ip6.soiikey=$(<$_f)"
> -
>  	enable_ifs
>  }
>  
> diff --git distrib/special/sysctl/sysctl.c distrib/special/sysctl/sysctl.c
> index 41d19856120..074b1d2b5f1 100644
> --- distrib/special/sysctl/sysctl.c
> +++ distrib/special/sysctl/sysctl.c
> @@ -33,8 +33,6 @@
>  
>  #include <machine/cpu.h>
>  
> -#define SOIIKEY_LEN 16
> -
>  struct var {
>  	char *name;
>  	int (*print)(struct var *);
> @@ -103,39 +101,6 @@ pstring(struct var *v)
>  	return (1);
>  }
>  
> -int
> -parse_hex_char(char ch)
> -{
> -	if (ch >= '0' && ch <= '9')
> -		return (ch - '0');
> -
> -	ch = tolower((unsigned char)ch);
> -	if (ch >= 'a' && ch <= 'f')
> -		return (ch - 'a' + 10);
> -
> -	return (-1);
> -}
> -
> -int
> -set_soii_key(char *src)
> -{
> -	uint8_t key[SOIIKEY_LEN];
> -	int mib[4] = {CTL_NET, PF_INET6, IPPROTO_IPV6, IPV6CTL_SOIIKEY};
> -	int i, c;
> -
> -	for(i = 0; i < SOIIKEY_LEN; i++) {
> -		if ((c = parse_hex_char(src[2 * i])) == -1)
> -			return (-1);
> -		key[i] = c << 4;
> -		if ((c = parse_hex_char(src[2 * i + 1])) == -1)
> -			return (-1);
> -		key[i] |= c;
> -	}
> -
> -	return sysctl(mib, sizeof(mib) / sizeof(mib[0]), NULL, NULL, key,
> -	    SOIIKEY_LEN);
> -}
> -
>  int
>  main(int argc, char *argv[])
>  {
> @@ -163,17 +128,6 @@ main(int argc, char *argv[])
>  
>  	while (argc--) {
>  		name = *argv++;
> -		/*
> -		 * strlen("net.inet6.ip6.soiikey="
> -		 *     "00000000000000000000000000000000") == 54
> -		 * strlen("net.inet6.ip6.soiikey=") == 22
> -		 */
> -		if (strlen(name) == 54 && strncmp(name,
> -		    "net.inet6.ip6.soiikey=", 22) == 0) {
> -			set_soii_key(name + 22);
> -			continue;
> -		}
> -
>  		for (i = 0; i < sizeof(vars)/sizeof(vars[0]); i++) {
>  			if (strcmp(name, vars[i].name) == 0) {
>  				(vars[i].print)(&vars[i]);
> diff --git etc/netstart etc/netstart
> index af4866f909e..105d5a977cf 100644
> --- etc/netstart
> +++ etc/netstart
> @@ -360,13 +360,6 @@ if ifconfig lo0 inet6 >/dev/null 2>&1; then
>  	IP6KERNEL=true
>  fi
>  
> -# Load key material for the generation of IPv6 Semantically Opaque Interface
> -# Identifiers (SOII) used for SLAAC addresses.
> -if $IP6KERNEL && ! $PRINT_ONLY; then
> -	[[ -f /etc/soii.key ]] &&
> -		sysctl -q "net.inet6.ip6.soiikey=$(</etc/soii.key)"
> -fi
> -
>  # If we were invoked with a list of interface names, just reconfigure these
>  # interfaces (or bridges), add default routes and return.
>  # Create virtual interfaces upfront to make ifconfig commands depending on
> diff --git etc/rc etc/rc
> index 13de0c06603..e3061b2b5d0 100644
> --- etc/rc
> +++ etc/rc
> @@ -163,9 +163,7 @@ make_keys() {
>  	    (read sz fp comm type && echo "sshd: $type $fp")
>  
>  	if [[ ! -f /etc/soii.key ]]; then
> -		openssl rand -hex 16 > /etc/soii.key &&
> -		    chmod 600 /etc/soii.key && sysctl -q \
> -		    "net.inet6.ip6.soiikey=$(</etc/soii.key)"
> +		openssl rand -hex 16 > /etc/soii.key && chmod 600 /etc/soii.key
>  	fi
>  }
>  
> diff --git lib/libc/sys/sysctl.2 lib/libc/sys/sysctl.2
> index 596b603b7b8..2e030bd9a32 100644
> --- lib/libc/sys/sysctl.2
> +++ lib/libc/sys/sysctl.2
> @@ -1859,7 +1859,6 @@ The currently defined protocols and names are:
>  .It ip6 Ta multipath Ta integer Ta yes
>  .It ip6 Ta neighborgcthresh Ta integer Ta yes
>  .It ip6 Ta redirect Ta integer Ta yes
> -.It ip6 Ta soiikey Ta uint8_t[IP6_SOIIKEY_LEN] Ta yes
>  .It ip6 Ta use_deprecated Ta integer Ta yes
>  .El
>  .Pp
> @@ -2030,14 +2029,6 @@ Returns 1 when ICMPv6 redirects may be sent by the node.
>  This option is ignored unless the node is routing IP packets,
>  and should normally be enabled on all systems.
>  .Pp
> -.It Li ip6.soii Pq Va net.inet6.ip6.soiikey
> -This variable configures the secret key for the RFC 7217 algorithm to
> -calculate a persistent Semantically Opaque Interface Identifier (SOII)
> -for IPv6 Stateless Address Autoconfiguration (SLAAC) addresses.
> -It must be
> -.Dv IP6_SOIIKEY_LEN
> -bytes long.
> -.Pp
>  .It Li ip6.use_deprecated Pq Va net.inet6.ip6.use_deprecated
>  This variable controls the use of deprecated addresses, specified in
>  RFC 4862 5.5.4.
> diff --git sbin/slaacd/slaacd.c sbin/slaacd/slaacd.c
> index 9e81a3994d8..e6b65d45876 100644
> --- sbin/slaacd/slaacd.c
> +++ sbin/slaacd/slaacd.c
> @@ -34,6 +34,7 @@
>  #include <netinet6/in6_var.h>
>  #include <netinet/icmp6.h>
>  
> +#include <ctype.h>
>  #include <err.h>
>  #include <errno.h>
>  #include <fcntl.h>
> @@ -76,7 +77,9 @@ void	configure_gateway(struct imsg_configure_dfr *, uint8_t);
>  void	add_gateway(struct imsg_configure_dfr *);
>  void	delete_gateway(struct imsg_configure_dfr *);
>  void	send_rdns_proposal(struct imsg_propose_rdns *);
> -int	get_soiikey(uint8_t *);
> +void	read_soiikey(void);
> +int	parse_hex_char(char);
> +ssize_t	parse_hex_string(unsigned char *, size_t, const char *);
>  
>  static int	main_imsg_send_ipc_sockets(struct imsgbuf *, struct imsgbuf *);
>  int		main_imsg_compose_frontend(int, int, void *, uint16_t);
> @@ -89,6 +92,7 @@ pid_t			 frontend_pid;
>  pid_t			 engine_pid;
>  
>  int			 routesock, ioctl_sock, rtm_seq = 0;
> +uint8_t			 soiikey[SLAACD_SOIIKEY_LEN];
>  
>  void
>  main_sig_handler(int sig, short event, void *arg)
> @@ -274,6 +278,8 @@ main(int argc, char *argv[])
>  #ifndef SMALL
>  	if ((control_fd = control_init(csock)) == -1)
>  		warnx("control socket setup failed");
> +
> +	read_soiikey();
>  #endif /* SMALL */
>  
>  	if (pledge("stdio inet sendfd wroute", NULL) == -1)
> @@ -439,11 +445,10 @@ main_dispatch_frontend(int fd, short event, void *bula)
>  			    sizeof(imsg_ifinfo)) == -1)
>  				fatalx("%s: invalid %s", __func__, i2s(type));
>  
> -			if (get_soiikey(imsg_ifinfo.soiikey) == -1)
> -				log_warn("get_soiikey");
> -			else
> -				main_imsg_compose_engine(IMSG_UPDATE_IF, 0,
> -				    &imsg_ifinfo, sizeof(imsg_ifinfo));
> +			memcpy(imsg_ifinfo.soiikey, soiikey,
> +			    SLAACD_SOIIKEY_LEN);
> +			main_imsg_compose_engine(IMSG_UPDATE_IF, 0,
> +			    &imsg_ifinfo, sizeof(imsg_ifinfo));
>  			break;
>  		default:
>  			log_debug("%s: error handling imsg %d", __func__, type);
> @@ -874,17 +879,76 @@ sin6_to_str(struct sockaddr_in6 *sin6)
>  	}
>  	return hbuf;
>  }
> -#endif	/* SMALL */
>  
>  int
> -get_soiikey(uint8_t *key)
> +parse_hex_char(char ch)
>  {
> -	int	 mib[4] = {CTL_NET, PF_INET6, IPPROTO_IPV6, IPV6CTL_SOIIKEY};
> -	size_t	 size = SLAACD_SOIIKEY_LEN;
> +	if (ch >= '0' && ch <= '9')
> +		return (ch - '0');
>  
> -	return sysctl(mib, sizeof(mib) / sizeof(mib[0]), key, &size, NULL, 0);
> +	ch = tolower((unsigned char)ch);
> +	if (ch >= 'a' && ch <= 'f')
> +		return (ch - 'a' + 10);
> +
> +	return (-1);
>  }
>  
> +ssize_t
> +parse_hex_string(unsigned char *dst, size_t dstlen, const char *src)
> +{
> +	size_t len = 0;
> +	int digit;
> +
> +	while (len < dstlen) {
> +		if (*src == '\0')
> +			return (len);
> +
> +		digit = parse_hex_char(*src++);
> +		if (digit == -1)
> +			return (-1);
> +		dst[len] = digit << 4;
> +
> +		digit = parse_hex_char(*src++);
> +		if (digit == -1)
> +			return (-1);
> +
> +		dst[len] |= digit;
> +		len++;
> +	}
> +
> +	while (*src != '\0') {
> +		if (parse_hex_char(*src++) == -1 ||
> +		    parse_hex_char(*src++) == -1)
> +			return (-1);
> +
> +		len++;
> +	}
> +
> +	return (len);
> +}
> +
> +void
> +read_soiikey(void)
> +{
> +	int	 fd = -1;
> +	char	 buf[33];
> +
> +	if ((fd = open("/etc/soii.key", O_RDONLY)) == -1)
> +		goto err;
> +	memset(buf, 0, sizeof(buf));
> +	if (read(fd, buf, sizeof(buf) - 1) == -1)
> +		goto err;
> +	if (parse_hex_string(soiikey, sizeof(soiikey), buf) == -1)
> +		goto err;
> +	return;
> + err:
> +	memset(soiikey, 0, sizeof(soiikey));
> +	if (fd != -1)
> +		close(fd);
> +}
> +
> +#endif	/* SMALL */
> +
>  void
>  open_icmp6sock(int rdomain)
>  {
> diff --git sbin/sysctl/sysctl.c sbin/sysctl/sysctl.c
> index b2dd40151e7..70d5bfe5390 100644
> --- sbin/sysctl/sysctl.c
> +++ sbin/sysctl/sysctl.c
> @@ -693,10 +693,6 @@ parse(char *string, int flags)
>  			if (len < 0)
>  				return;
>  
> -			if (mib[2] == IPPROTO_IPV6 &&
> -			    mib[3] == IPV6CTL_SOIIKEY)
> -				special |= HEX;
> -
>  			if ((mib[2] == IPPROTO_IPV6 && mib[3] == IPV6CTL_MRTMFC) ||
>  			    (mib[2] == IPPROTO_IPV6 && mib[3] == IPV6CTL_MRTMIF)) {
>  				if (flags == 0)
> diff --git sys/kern/kern_pledge.c sys/kern/kern_pledge.c
> index a418aca22b3..375920ad357 100644
> --- sys/kern/kern_pledge.c
> +++ sys/kern/kern_pledge.c
> @@ -832,13 +832,6 @@ pledge_sysctl(struct proc *p, int miblen, int *mib, void *new)
>  			return (0);
>  	}
>  
> -	if ((pledge & PLEDGE_WROUTE)) {
> -		if (miblen == 4 &&
> -		    mib[0] == CTL_NET && mib[1] == PF_INET6 &&
> -		    mib[2] == IPPROTO_IPV6 && mib[3] == IPV6CTL_SOIIKEY)
> -			return (0);
> -	}
> -
>  	if (pledge & (PLEDGE_PS | PLEDGE_VMINFO)) {
>  		if (miblen == 2 &&		/* kern.fscale */
>  		    mib[0] == CTL_KERN && mib[1] == KERN_FSCALE)
> diff --git sys/netinet6/in6.h sys/netinet6/in6.h
> index 8a997b563ba..074fde1f792 100644
> --- sys/netinet6/in6.h
> +++ sys/netinet6/in6.h
> @@ -597,8 +597,7 @@ ifatoia6(struct ifaddr *ifa)
>  #define IPV6CTL_IFQUEUE		51
>  #define IPV6CTL_MRTMIF		52
>  #define IPV6CTL_MRTMFC		53
> -#define IPV6CTL_SOIIKEY		54
> -#define IPV6CTL_MAXID		55
> +#define IPV6CTL_MAXID		54
>  
>  /* New entries should be added here from current IPV6CTL_MAXID value. */
>  /* to define items, should talk with KAME guys first, for *BSD compatibility */
> @@ -658,7 +657,6 @@ ifatoia6(struct ifaddr *ifa)
>  	{ "ifq", CTLTYPE_NODE }, \
>  	{ "mrtmif", CTLTYPE_STRUCT }, \
>  	{ "mrtmfc", CTLTYPE_STRUCT }, \
> -	{ "soiikey", CTLTYPE_STRING }, /* binary string */ \
>  }
>  
>  __BEGIN_DECLS
> diff --git sys/netinet6/ip6_input.c sys/netinet6/ip6_input.c
> index e6a5fbc0404..6e042a797ff 100644
> --- sys/netinet6/ip6_input.c
> +++ sys/netinet6/ip6_input.c
> @@ -109,14 +109,11 @@ struct niqueue ip6intrq = NIQUEUE_INITIALIZER(IPQ_MAXLEN, NETISR_IPV6);
>  
>  struct cpumem *ip6counters;
>  
> -uint8_t ip6_soiikey[IP6_SOIIKEY_LEN];
> -
>  int ip6_ours(struct mbuf **, int *, int, int, int, struct netstack *);
>  int ip6_check_rh0hdr(struct mbuf *, int *);
>  int ip6_hbhchcheck(struct mbuf **, int *, int *, int);
>  int ip6_hopopts_input(struct mbuf **, int *, u_int32_t *, u_int32_t *);
>  struct mbuf *ip6_pullexthdr(struct mbuf *, size_t, int);
> -int ip6_sysctl_soiikey(void *, size_t *, void *, size_t);
>  
>  static struct mbuf_queue	ip6send_mq;
>  
> @@ -1481,32 +1478,6 @@ ip6_sysctl_ip6stat(void *oldp, size_t *oldlenp, void *newp)
>  }
>  #endif /* SMALL_KERNEL */
>  
> -int
> -ip6_sysctl_soiikey(void *oldp, size_t *oldlenp, void *newp, size_t newlen)
> -{
> -	uint8_t soiikey[sizeof(ip6_soiikey)];
> -	int error;
> -
> -	error = suser(curproc);
> -	if (error != 0)
> -		return (error);
> -
> -	rw_enter_read(&sysctl_lock);
> -	memcpy(soiikey, ip6_soiikey, sizeof(ip6_soiikey));
> -	rw_exit_read(&sysctl_lock);
> -
> -	error = sysctl_struct(oldp, oldlenp, newp, newlen, soiikey,
> -	    sizeof(soiikey));
> -
> -	if (error == 0 && newp) {
> -		rw_enter_write(&sysctl_lock);
> -		memcpy(ip6_soiikey, soiikey, sizeof(soiikey));
> -		rw_exit_write(&sysctl_lock);
> -	}
> -
> -	return (error);
> -}
> -
>  int
>  ip6_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp,
>      void *newp, size_t newlen)
> @@ -1516,8 +1487,6 @@ ip6_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp,
>  		return (ENOTDIR);
>  
>  	switch (name[0]) {
> -	case IPV6CTL_SOIIKEY:
> -		return (ip6_sysctl_soiikey(oldp, oldlenp, newp, newlen));
>  #ifndef SMALL_KERNEL
>  	case IPV6CTL_STATS:
>  		return (ip6_sysctl_ip6stat(oldp, oldlenp, newp));
> diff --git sys/netinet6/ip6_var.h sys/netinet6/ip6_var.h
> index e5353caa50c..c9ad11832bb 100644
> --- sys/netinet6/ip6_var.h
> +++ sys/netinet6/ip6_var.h
> @@ -298,9 +298,6 @@ extern int	ip6_dad_pending;	/* number of currently running DADs */
>  
>  extern int ip6_auto_flowlabel;
>  
> -#define	IP6_SOIIKEY_LEN 16
> -extern uint8_t	ip6_soiikey[IP6_SOIIKEY_LEN];
> -
>  extern const struct pr_usrreqs rip6_usrreqs;
>  
>  struct inpcb;
> 
> 
> -- 
> In my defence, I have been left unsupervised.