Download raw body.
remove net.inet6.ip6.soiikey sysctl
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.
remove net.inet6.ip6.soiikey sysctl