From: Alexander Bluhm Subject: Re: remove net.inet6.ip6.soiikey sysctl To: tech Date: Tue, 5 Aug 2025 13:30:06 +0200 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 > > -#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=$( -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=$( + 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 > #include > > +#include > #include > #include > #include > @@ -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.