this has all the bells and whistles notably the installer and
documentation for the net.inet6.ip6.soiikey sysctl are missing.

the sysctl implementation is from dlg, all the mistakes are probably
tweaks by me ;)

It switches the hash function to SipHash24 from sha512 as suggested by dlg

Comments, tests, improvments, OKs?

diff --git etc/rc etc/rc
index 48e5671335f..fda8a620bbb 100644
--- etc/rc
+++ etc/rc
@@ -47,6 +47,14 @@ update_limit() {
        done
 }
 
+# Apply soiikey.conf settings.
+soiikey_conf() {
+       stripcom /etc/soiikey.conf |
+       while read _line; do
+               sysctl -q "net.inet6.ip6.soiikey=$_line"
+       done
+}
+
 # Apply sysctl.conf(5) settings.
 sysctl_conf() {
        stripcom /etc/sysctl.conf |
@@ -60,6 +68,7 @@ sysctl_conf() {
                        update_limit -n openfiles;;
                esac
        done
+       soiikey_conf
 }
 
 # Apply mixerctl.conf(5) settings.
@@ -154,6 +163,11 @@ make_keys() {
        fi
 
        ssh-keygen -A
+
+       if [[ ! -f /etc/soiikey.conf ]]; then
+               openssl rand -hex 16 > /etc/soiikey.conf && \
+                   chmod 600 /etc/soiikey.conf && soiikey_conf
+       fi
 }
 
 # Re-link libraries, placing the objects in a random order.
diff --git sbin/ifconfig/brconfig.h sbin/ifconfig/brconfig.h
index ee68feb411b..09c871b352b 100644
--- sbin/ifconfig/brconfig.h
+++ sbin/ifconfig/brconfig.h
@@ -73,7 +73,7 @@ void switch_portno(const char *, const char *);
        "\024\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5POINTOPOINT\6NOTRAILERS" \
        "\7RUNNING\10NOARP\11PROMISC\12ALLMULTI\13OACTIVE\14SIMPLEX"    \
        "\15LINK0\16LINK1\17LINK2\20MULTICAST"  \
-       "\23INET6_NOPRIVACY\24MPLS\25WOL\26AUTOCONF6"
+       "\23INET6_NOPRIVACY\24MPLS\25WOL\26AUTOCONF6\27INET6_NOSOII"
 
 void printb(char *, unsigned int, unsigned char *);
 
diff --git sbin/ifconfig/ifconfig.8 sbin/ifconfig/ifconfig.8
index cac8eafc2cb..30721e41ad4 100644
--- sbin/ifconfig/ifconfig.8
+++ sbin/ifconfig/ifconfig.8
@@ -1070,6 +1070,7 @@ protocol when supported by the access point.
 .Op Oo Fl Oc Ns Cm autoconfprivacy
 .Op Cm eui64
 .Op Cm pltime Ar n
+.Op Oo Fl Oc Ns Cm soii
 .Op Oo Fl Oc Ns Cm tentative
 .Op Cm vltime Ar n
 .Ek
@@ -1127,6 +1128,21 @@ Fill the interface index
 automatically.
 .It Cm pltime Ar n
 Set preferred lifetime for the address.
+.It Cm soii
+Enable persistent Semantically Opaque Interface Identifiers (SOIIs),
+as per RFC 7217, for link local and SLAAC addresses on the interface.
+These identifiers are enabled by default.
+The purpose of these identifiers is to make discovery of hosts by
+scanning a whole prefix more difficult.
+SOOIs use the whole 64 bit of the host part while SLAAC addresses are
+formed from MAC addresses and have 48 bits of entropy at most.
+In reality entropy can be  as low as 24 bit.
+See RFC 8064 for details.
+.It Cm -soii
+Disable IPv6 persistent Semantically Opaque Interface Identifiers on the
+interface.
+Currently configured addresses will not be removed until they become
+invalid.
 .It Cm tentative
 Set the IPv6 tentative address bit.
 .It Cm -tentative
diff --git sbin/ifconfig/ifconfig.c sbin/ifconfig/ifconfig.c
index d99bcb34871..bd7ce506398 100644
--- sbin/ifconfig/ifconfig.c
+++ sbin/ifconfig/ifconfig.c
@@ -388,6 +388,8 @@ const struct        cmd {
        { "eui64",      0,              0,              setia6eui64 },
        { "autoconfprivacy",    -IFXF_INET6_NOPRIVACY,  0,      setifxflags },
        { "-autoconfprivacy",   IFXF_INET6_NOPRIVACY,   0,      setifxflags },
+       { "soii",       -IFXF_INET6_NOSOII,     0,      setifxflags },
+       { "-soii",      IFXF_INET6_NOSOII,      0,      setifxflags },
 #ifndef SMALL
        { "hwfeatures", NEXTARG0,       0,              printifhwfeatures },
        { "metric",     NEXTARG,        0,              setifmetric },
diff --git sbin/slaacd/control.c sbin/slaacd/control.c
index 76b0f3b15ea..4f38d26331c 100644
--- sbin/slaacd/control.c
+++ sbin/slaacd/control.c
@@ -30,6 +30,7 @@
 #include <event.h>
 #include <imsg.h>
 #include <md5.h>
+#include <siphash.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
diff --git sbin/slaacd/engine.c sbin/slaacd/engine.c
index e02cb6ec3d0..cc7abd68512 100644
--- sbin/slaacd/engine.c
+++ sbin/slaacd/engine.c
@@ -70,6 +70,7 @@
 #include <netdb.h>
 #include <pwd.h>
 #include <signal.h>
+#include <siphash.h>
 #include <stddef.h>
 #include <stdlib.h>
 #include <string.h>
@@ -180,6 +181,7 @@ struct address_proposal {
        uint8_t                          prefix_len;
        uint32_t                         vltime;
        uint32_t                         pltime;
+       SIPHASH_KEY                      soiikey;
 };
 
 struct dfr_proposal {
@@ -205,8 +207,10 @@ struct slaacd_iface {
        uint32_t                         if_index;
        int                              running;
        int                              autoconfprivacy;
+       int                              soii;
        struct ether_addr                hw_address;
        struct sockaddr_in6              ll_address;
+       SIPHASH_KEY                      soiikey;
        LIST_HEAD(, radv)                radvs;
        LIST_HEAD(, address_proposal)    addr_proposals;
        LIST_HEAD(, dfr_proposal)        dfr_proposals;
@@ -378,7 +382,6 @@ engine_dispatch_frontend(int fd, short event, void *bula)
        struct imsg                      imsg;
        struct slaacd_iface             *iface;
        struct imsg_ra                   ra;
-       struct imsg_ifinfo               imsg_ifinfo;
        struct imsg_proposal_ack         proposal_ack;
        struct address_proposal         *addr_proposal = NULL;
        struct dfr_proposal             *dfr_proposal = NULL;
@@ -424,73 +427,6 @@ engine_dispatch_frontend(int fd, short event, void *bula)
                        engine_showinfo_ctl(&imsg, if_index);
                        break;
 #endif /* SMALL */
-               case IMSG_UPDATE_IF:
-                       if (imsg.hdr.len != IMSG_HEADER_SIZE +
-                           sizeof(imsg_ifinfo))
-                               fatal("%s: IMSG_UPDATE_IF wrong length: %d",
-                                   __func__, imsg.hdr.len);
-                       memcpy(&imsg_ifinfo, imsg.data, sizeof(imsg_ifinfo));
-
-                       iface = get_slaacd_iface_by_id(imsg_ifinfo.if_index);
-                       if (iface == NULL) {
-                               if ((iface = calloc(1, sizeof(*iface))) == NULL)
-                                       fatal("calloc");
-                               evtimer_set(&iface->timer, iface_timeout,
-                                   iface);
-                               iface->if_index = imsg_ifinfo.if_index;
-                               iface->running = imsg_ifinfo.running;
-                               if (iface->running)
-                                       start_probe(iface);
-                               else
-                                       iface->state = IF_DOWN;
-                               iface->autoconfprivacy =
-                                   imsg_ifinfo.autoconfprivacy;
-                               memcpy(&iface->hw_address,
-                                   &imsg_ifinfo.hw_address,
-                                   sizeof(struct ether_addr));
-                               memcpy(&iface->ll_address,
-                                   &imsg_ifinfo.ll_address,
-                                   sizeof(struct sockaddr_in6));
-                               LIST_INIT(&iface->radvs);
-                               LIST_INSERT_HEAD(&slaacd_interfaces,
-                                   iface, entries);
-                               LIST_INIT(&iface->addr_proposals);
-                               LIST_INIT(&iface->dfr_proposals);
-                       } else {
-                               int need_refresh = 0;
-
-                               if (iface->autoconfprivacy !=
-                                   imsg_ifinfo.autoconfprivacy) {
-                                       iface->autoconfprivacy =
-                                           imsg_ifinfo.autoconfprivacy;
-                                       need_refresh = 1;
-                               }
-                               if (memcmp(&iface->hw_address,
-                                           &imsg_ifinfo.hw_address,
-                                           sizeof(struct ether_addr)) != 0) {
-                                       memcpy(&iface->hw_address,
-                                           &imsg_ifinfo.hw_address,
-                                           sizeof(struct ether_addr));
-                                       need_refresh = 1;
-                               }
-
-                               if (iface->state != IF_DOWN &&
-                                   imsg_ifinfo.running && need_refresh)
-                                       start_probe(iface);
-
-                               iface->running = imsg_ifinfo.running;
-                               if (!iface->running) {
-                                       iface->state = IF_DOWN;
-                                       if (evtimer_pending(&iface->timer,
-                                           NULL))
-                                               evtimer_del(&iface->timer);
-                               }
-
-                               memcpy(&iface->ll_address,
-                                   &imsg_ifinfo.ll_address,
-                                   sizeof(struct sockaddr_in6));
-                       }
-                       break;
                case IMSG_REMOVE_IF:
                        if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(if_index))
                                fatal("%s: IMSG_REMOVE_IF wrong length: %d",
@@ -605,6 +541,8 @@ engine_dispatch_main(int fd, short event, void *bula)
        struct imsg              imsg;
        struct imsgev           *iev = bula;
        struct imsgbuf          *ibuf = &iev->ibuf;
+       struct imsg_ifinfo       imsg_ifinfo;
+       struct slaacd_iface     *iface;
        ssize_t                  n;
        int                      shut = 0;
 
@@ -660,6 +598,92 @@ engine_dispatch_main(int fd, short event, void *bula)
                        if (pledge("stdio", NULL) == -1)
                                fatal("pledge");
                        break;
+               case IMSG_UPDATE_IF:
+                       if (imsg.hdr.len != IMSG_HEADER_SIZE +
+                           sizeof(imsg_ifinfo))
+                               fatal("%s: IMSG_UPDATE_IF wrong length: %d",
+                                   __func__, imsg.hdr.len);
+                       memcpy(&imsg_ifinfo, imsg.data, sizeof(imsg_ifinfo));
+
+                       iface = get_slaacd_iface_by_id(imsg_ifinfo.if_index);
+                       if (iface == NULL) {
+                               if ((iface = calloc(1, sizeof(*iface))) == NULL)
+                                       fatal("calloc");
+                               evtimer_set(&iface->timer, iface_timeout,
+                                   iface);
+                               iface->if_index = imsg_ifinfo.if_index;
+                               iface->running = imsg_ifinfo.running;
+                               if (iface->running)
+                                       start_probe(iface);
+                               else
+                                       iface->state = IF_DOWN;
+                               iface->autoconfprivacy =
+                                   imsg_ifinfo.autoconfprivacy;
+                               iface->soii = imsg_ifinfo.soii;
+                               memcpy(&iface->hw_address,
+                                   &imsg_ifinfo.hw_address,
+                                   sizeof(struct ether_addr));
+                               memcpy(&iface->ll_address,
+                                   &imsg_ifinfo.ll_address,
+                                   sizeof(struct sockaddr_in6));
+                               memcpy(&iface->soiikey, &imsg_ifinfo.soiikey,
+                                   sizeof(iface->soiikey));
+                               LIST_INIT(&iface->radvs);
+                               LIST_INSERT_HEAD(&slaacd_interfaces,
+                                   iface, entries);
+                               LIST_INIT(&iface->addr_proposals);
+                               LIST_INIT(&iface->dfr_proposals);
+                       } else {
+                               int need_refresh = 0;
+
+                               if (iface->autoconfprivacy !=
+                                   imsg_ifinfo.autoconfprivacy) {
+                                       iface->autoconfprivacy =
+                                           imsg_ifinfo.autoconfprivacy;
+                                       need_refresh = 1;
+                               }
+
+                               if (iface->soii !=
+                                   imsg_ifinfo.soii) {
+                                       iface->soii =
+                                           imsg_ifinfo.soii;
+                                       need_refresh = 1;
+                               }
+
+                               if (memcmp(&iface->hw_address,
+                                           &imsg_ifinfo.hw_address,
+                                           sizeof(struct ether_addr)) != 0) {
+                                       memcpy(&iface->hw_address,
+                                           &imsg_ifinfo.hw_address,
+                                           sizeof(struct ether_addr));
+                                       need_refresh = 1;
+                               }
+                               if (memcmp(&iface->soiikey,
+                                           &imsg_ifinfo.soiikey,
+                                           sizeof(iface->soiikey)) != 0) {
+                                       memcpy(&iface->soiikey,
+                                           &imsg_ifinfo.soiikey,
+                                           sizeof(iface->soiikey));
+                                       need_refresh = 1;
+                               }
+
+                               if (iface->state != IF_DOWN &&
+                                   imsg_ifinfo.running && need_refresh)
+                                       start_probe(iface);
+
+                               iface->running = imsg_ifinfo.running;
+                               if (!iface->running) {
+                                       iface->state = IF_DOWN;
+                                       if (evtimer_pending(&iface->timer,
+                                           NULL))
+                                               evtimer_del(&iface->timer);
+                               }
+
+                               memcpy(&iface->ll_address,
+                                   &imsg_ifinfo.ll_address,
+                                   sizeof(struct sockaddr_in6));
+                       }
+                       break;
                default:
                        log_debug("%s: unexpected imsg %d", __func__,
                            imsg.hdr.type);
@@ -698,6 +722,7 @@ send_interface_info(struct slaacd_iface *iface, pid_t pid)
        cei.if_index = iface->if_index;
        cei.running = iface->running;
        cei.autoconfprivacy = iface->autoconfprivacy;
+       cei.soii = iface->soii;
        memcpy(&cei.hw_address, &iface->hw_address, sizeof(struct ether_addr));
        memcpy(&cei.ll_address, &iface->ll_address,
            sizeof(struct sockaddr_in6));
@@ -1148,7 +1173,9 @@ void
 gen_addr(struct slaacd_iface *iface, struct radv_prefix *prefix, struct
     address_proposal *addr_proposal, int privacy)
 {
+       SIPHASH_CTX ctx;
        struct in6_addr priv_in6;
+       int dad_counter = 0; /* XXX not used */
 
        /* from in6_ifadd() in nd6_rtr.c */
        /* XXX from in6.h, guarded by #ifdef _KERNEL   XXX nonstandard */
@@ -1195,20 +1222,32 @@ gen_addr(struct slaacd_iface *iface, struct radv_prefix 
*prefix, struct
                addr_proposal->addr.sin6_addr.s6_addr32[3] |=
                    (priv_in6.s6_addr32[3] & ~addr_proposal->mask.s6_addr32[3]);
        } else {
-               addr_proposal->addr.sin6_addr.s6_addr32[0] |=
-                   (iface->ll_address.sin6_addr.s6_addr32[0] &
-                   ~addr_proposal->mask.s6_addr32[0]);
-               addr_proposal->addr.sin6_addr.s6_addr32[1] |=
-                   (iface->ll_address.sin6_addr.s6_addr32[1] &
-                   ~addr_proposal->mask.s6_addr32[1]);
-               addr_proposal->addr.sin6_addr.s6_addr32[2] |=
-                   (iface->ll_address.sin6_addr.s6_addr32[2] &
-                   ~addr_proposal->mask.s6_addr32[2]);
-               addr_proposal->addr.sin6_addr.s6_addr32[3] |=
-                   (iface->ll_address.sin6_addr.s6_addr32[3] &
-                   ~addr_proposal->mask.s6_addr32[3]);
+               if (iface->soii) {
+                       SipHash24_Init(&ctx, &addr_proposal->soiikey);
+                       SipHash24_Update(&ctx, &prefix->prefix,
+                           sizeof(prefix->prefix));
+                       SipHash24_Update(&ctx, &iface->hw_address,
+                           sizeof(iface->hw_address));
+                       SipHash24_Update(&ctx, &dad_counter,
+                           sizeof(dad_counter));
+
+                       SipHash24_Final(&addr_proposal->
+                           addr.sin6_addr.s6_addr[8], &ctx);
+               } else {
+                       addr_proposal->addr.sin6_addr.s6_addr32[0] |=
+                           (iface->ll_address.sin6_addr.s6_addr32[0] &
+                           ~addr_proposal->mask.s6_addr32[0]);
+                       addr_proposal->addr.sin6_addr.s6_addr32[1] |=
+                           (iface->ll_address.sin6_addr.s6_addr32[1] &
+                           ~addr_proposal->mask.s6_addr32[1]);
+                       addr_proposal->addr.sin6_addr.s6_addr32[2] |=
+                           (iface->ll_address.sin6_addr.s6_addr32[2] &
+                           ~addr_proposal->mask.s6_addr32[2]);
+                       addr_proposal->addr.sin6_addr.s6_addr32[3] |=
+                           (iface->ll_address.sin6_addr.s6_addr32[3] &
+                           ~addr_proposal->mask.s6_addr32[3]);
+               }
        }
-
 #undef s6_addr32
 }
 
@@ -1575,6 +1614,11 @@ void update_iface_ra(struct slaacd_iface *iface, struct 
radv *ra)
                                    sizeof(addr_proposal->hw_address)) != 0)
                                        continue;
 
+                               if (memcmp(&addr_proposal->soiikey,
+                                   &iface->soiikey,
+                                   sizeof(addr_proposal->soiikey)) != 0)
+                                       continue;
+
                                if (addr_proposal->privacy) {
                                        /*
                                         * create new privacy address if old
@@ -1715,6 +1759,8 @@ gen_address_proposal(struct slaacd_iface *iface, struct 
radv *ra, struct
        addr_proposal->if_index = iface->if_index;
        memcpy(&addr_proposal->hw_address, &iface->hw_address,
            sizeof(addr_proposal->hw_address));
+       memcpy(&addr_proposal->soiikey, &iface->soiikey,
+           sizeof(addr_proposal->soiikey));
        addr_proposal->privacy = privacy;
        memcpy(&addr_proposal->prefix, &prefix->prefix,
            sizeof(addr_proposal->prefix));
diff --git sbin/slaacd/frontend.c sbin/slaacd/frontend.c
index 28524e41454..f6a161b4918 100644
--- sbin/slaacd/frontend.c
+++ sbin/slaacd/frontend.c
@@ -44,6 +44,7 @@
 #include <imsg.h>
 #include <pwd.h>
 #include <signal.h>
+#include <siphash.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -504,12 +505,13 @@ update_iface(uint32_t if_index, char* if_name)
        imsg_ifinfo.running = (flags & (IFF_UP | IFF_RUNNING)) == (IFF_UP |
            IFF_RUNNING);
        imsg_ifinfo.autoconfprivacy = !(xflags & IFXF_INET6_NOPRIVACY);
+       imsg_ifinfo.soii = !(xflags & IFXF_INET6_NOSOII);
        get_lladdr(if_name, &imsg_ifinfo.hw_address, &imsg_ifinfo.ll_address);
 
        memcpy(&nd_opt_source_link_addr, &imsg_ifinfo.hw_address,
            sizeof(nd_opt_source_link_addr));
 
-       frontend_imsg_compose_engine(IMSG_UPDATE_IF, 0, 0, &imsg_ifinfo,
+       frontend_imsg_compose_main(IMSG_UPDATE_IF, 0, &imsg_ifinfo,
            sizeof(imsg_ifinfo));
 }
 
diff --git sbin/slaacd/slaacd.c sbin/slaacd/slaacd.c
index 482ebf4e5e9..3c81afa3dde 100644
--- sbin/slaacd/slaacd.c
+++ sbin/slaacd/slaacd.c
@@ -23,6 +23,7 @@
 #include <sys/queue.h>
 #include <sys/socket.h>
 #include <sys/syslog.h>
+#include <sys/sysctl.h>
 #include <sys/uio.h>
 #include <sys/wait.h>
 
@@ -42,6 +43,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <signal.h>
+#include <siphash.h>
 #include <unistd.h>
 
 #include "log.h"
@@ -94,6 +96,7 @@ void  configure_interface(struct imsg_configure_address *);
 void   configure_gateway(struct imsg_configure_dfr *, uint8_t);
 void   add_gateway(struct imsg_configure_dfr *);
 void   delete_gateway(struct imsg_configure_dfr *);
+int    get_soiikey(SIPHASH_KEY *);
 
 static int     main_imsg_send_ipc_sockets(struct imsgbuf *, struct imsgbuf *);
 
@@ -369,6 +372,7 @@ main_dispatch_frontend(int fd, short event, void *bula)
        struct imsgev           *iev = bula;
        struct imsgbuf          *ibuf;
        struct imsg              imsg;
+       struct imsg_ifinfo       imsg_ifinfo;
        ssize_t                  n;
        int                      shut = 0;
 #ifndef        SMALL
@@ -404,6 +408,18 @@ main_dispatch_frontend(int fd, short event, void *bula)
                        log_setverbose(verbose);
                        break;
 #endif /* SMALL */
+               case IMSG_UPDATE_IF:
+                       if (imsg.hdr.len != IMSG_HEADER_SIZE +
+                           sizeof(imsg_ifinfo))
+                               fatal("%s: IMSG_UPDATE_IF wrong length: %d",
+                                   __func__, imsg.hdr.len);
+                       memcpy(&imsg_ifinfo, imsg.data, sizeof(imsg_ifinfo));
+                       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));
+                       break;
                default:
                        log_debug("%s: error handling imsg %d", __func__,
                            imsg.hdr.type);
@@ -774,3 +790,12 @@ delete_gateway(struct imsg_configure_dfr *dfr)
 {
        configure_gateway(dfr, RTM_DELETE);
 }
+
+int
+get_soiikey(SIPHASH_KEY *key)
+{
+       int      mib[4] = {CTL_NET, PF_INET6, IPPROTO_IPV6, IPV6CTL_SOIIKEY};
+       size_t   size = sizeof(*key);
+
+       return sysctl(mib, sizeof(mib) / sizeof(mib[0]), key, &size, NULL, 0);
+}
diff --git sbin/slaacd/slaacd.h sbin/slaacd/slaacd.h
index ecc192ce1da..8054f017cfd 100644
--- sbin/slaacd/slaacd.h
+++ sbin/slaacd/slaacd.h
@@ -92,6 +92,7 @@ struct ctl_engine_info {
        uint32_t                if_index;
        int                     running;
        int                     autoconfprivacy;
+       int                     soii;
        struct ether_addr       hw_address;
        struct sockaddr_in6     ll_address;
 };
@@ -160,8 +161,10 @@ struct imsg_ifinfo {
        uint32_t                if_index;
        int                     running;
        int                     autoconfprivacy;
+       int                     soii;
        struct ether_addr       hw_address;
        struct sockaddr_in6     ll_address;
+       SIPHASH_KEY             soiikey;
 };
 
 struct imsg_del_addr {
diff --git sbin/sysctl/sysctl.c sbin/sysctl/sysctl.c
index 94f78c0d673..cbacaca19d2 100644
--- sbin/sysctl/sysctl.c
+++ sbin/sysctl/sysctl.c
@@ -212,7 +212,7 @@ int sysctl_chipset(char *, char **, int *, int, int *);
 #endif
 void vfsinit(void);
 
-char *equ = "=";
+const char *equ = "=";
 
 int
 main(int argc, char *argv[])
@@ -286,6 +286,53 @@ listall(char *prefix, struct list *lp)
        }
 }
 
+int
+parse_hex_char(char ch)
+{
+       if (ch >= '0' && ch <= '9')
+               return (ch - '0');
+       if (ch >= 'a' && ch <= 'f')
+               return (ch - 'a' + 10);
+       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)
+{
+       ssize_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);
+}
+
 /*
  * Parse a name into a MIB entry.
  * Lookup and print out the MIB entry if it exists.
@@ -302,6 +349,7 @@ parse(char *string, int flags)
        struct list *lp;
        int mib[CTL_MAXNAME];
        char *cp, *bufp, buf[SYSCTL_BUFSIZ];
+       unsigned char hex[SYSCTL_BUFSIZ];
 
        (void)strlcpy(buf, string, sizeof(buf));
        bufp = buf;
@@ -566,6 +614,9 @@ parse(char *string, int flags)
                        len = sysctl_inet6(string, &bufp, mib, flags, &type);
                        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) ||
@@ -717,6 +768,27 @@ parse(char *string, int flags)
                        newval = &quadval;
                        newsize = sizeof(quadval);
                        break;
+               case CTLTYPE_STRING:
+                       if (special & HEX) {
+                               ssize_t len;
+
+                               len = parse_hex_string(hex, sizeof(hex),
+                                   newval);
+                               if (len == -1) {
+                                       warnx("%s: hex string %s: invalid",
+                                           string, newval);
+                                       return;
+                               }
+                               if (len > sizeof(hex)) {
+                                       warnx("%s: hex string %s: too long",
+                                           string, newval);
+                                       return;
+                               }
+
+                               newval = hex;
+                               newsize = len;
+                       }
+                       break;
                }
        }
        size = (special & SMALLBUF) ? 512 : SYSCTL_BUFSIZ;
@@ -936,13 +1008,30 @@ parse(char *string, int flags)
                if (newval == NULL) {
                        if (!nflag)
                                (void)printf("%s%s", string, equ);
-                       (void)puts(buf);
-               } else {
-                       if (!qflag) {
-                               if (!nflag)
-                                       (void)printf("%s: %s -> ", string, buf);
-                               (void)puts((char *)newval);
+                       if (special & HEX) {
+                               size_t i;
+                               for (i = 0; i < size; i++) {
+                                       (void)printf("%02x",
+                                           (unsigned char)buf[i]);
+                               }
+                               (void)printf("\n");
+                       } else
+                               (void)puts(buf);
+               } else if (!qflag) {
+                       if (!nflag) {
+                               (void)printf("%s: ", string);
+                               if (special & HEX) {
+                                       size_t i;
+                                       for (i = 0; i < size; i++) {
+                                               (void)printf("%02x",
+                                                   (unsigned char)buf[i]);
+                                       }
+                               } else
+                                       (void)printf("%s", cp);
+
+                               (void)printf(" -> ");
                        }
+                       (void)puts(cp);
                }
                return;
 
diff --git sys/net/if.h sys/net/if.h
index 89867eac340..370c7928492 100644
--- sys/net/if.h
+++ sys/net/if.h
@@ -212,6 +212,7 @@ struct if_status_description {
 #define        IFXF_MPLS               0x8             /* supports MPLS */
 #define        IFXF_WOL                0x10            /* wake on lan enabled 
*/
 #define        IFXF_AUTOCONF6          0x20            /* v6 autoconf enabled 
*/
+#define IFXF_INET6_NOSOII      0x40            /* don't do RFC 7217 */
 
 #define        IFXF_CANTCHANGE \
        (IFXF_MPSAFE|IFXF_CLONED)
diff --git sys/netinet6/in6.h sys/netinet6/in6.h
index 1aac6581916..729100b9c00 100644
--- sys/netinet6/in6.h
+++ sys/netinet6/in6.h
@@ -592,7 +592,8 @@ ifatoia6(struct ifaddr *ifa)
 #define IPV6CTL_IFQUEUE                51
 #define IPV6CTL_MRTMIF         52
 #define IPV6CTL_MRTMFC         53
-#define IPV6CTL_MAXID          54
+#define IPV6CTL_SOIIKEY                54
+#define IPV6CTL_MAXID          55
 
 /* New entries should be added here from current IPV6CTL_MAXID value. */
 /* to define items, should talk with KAME guys first, for *BSD compatibility */
@@ -652,6 +653,7 @@ ifatoia6(struct ifaddr *ifa)
        { "ifq", CTLTYPE_NODE }, \
        { "mrtmif", CTLTYPE_STRUCT }, \
        { "mrtmfc", CTLTYPE_STRUCT }, \
+       { "soiikey", CTLTYPE_STRING }, /* binary string */ \
 }
 
 #define IPV6CTL_VARS { \
diff --git sys/netinet6/in6_ifattach.c sys/netinet6/in6_ifattach.c
index 89acde9c6a4..4117eaee6cf 100644
--- sys/netinet6/in6_ifattach.c
+++ sys/netinet6/in6_ifattach.c
@@ -38,6 +38,7 @@
 #include <sys/syslog.h>
 
 #include <crypto/sha2.h>
+#include <crypto/siphash.h>
 
 #include <net/if.h>
 #include <net/if_var.h>
@@ -56,8 +57,11 @@
 #include <netinet6/ip6_mroute.h>
 #endif
 
+extern SIPHASH_KEY ip6_soiikey;
+
 int get_last_resort_ifid(struct ifnet *, struct in6_addr *);
 int get_hw_ifid(struct ifnet *, struct in6_addr *);
+int get_soii_ifid(struct ifnet *, struct in6_addr *);
 int get_ifid(struct ifnet *, struct in6_addr *);
 int in6_ifattach_loopback(struct ifnet *);
 
@@ -231,6 +235,42 @@ get_hw_ifid(struct ifnet *ifp, struct in6_addr *in6)
 }
 
 /*
+ * Generate a Semantically Opaque Interface Identifier according to RFC 7217
+ *
+ * in6 - upper 64bits are preserved
+ */
+int
+get_soii_ifid(struct ifnet *ifp, struct in6_addr *in6)
+{
+       SIPHASH_CTX ctx;
+       struct in6_addr prefix;
+       struct sockaddr_dl *sdl;
+       int dad_counter = 0; /* XXX not used */
+       char *addr;
+
+       if (ifp->if_xflags & IFXF_INET6_NOSOII)
+               return -1;
+
+       sdl = ifp->if_sadl;
+       if (sdl == NULL || sdl->sdl_alen == 0)
+               return -1;
+
+       memset(&prefix, 0, sizeof(prefix));
+       prefix.s6_addr16[0] = htons(0xfe80);
+       addr = LLADDR(sdl);
+
+       SipHash24_Init(&ctx, &ip6_soiikey);
+
+       SipHash24_Update(&ctx, &prefix, sizeof(prefix));
+       SipHash24_Update(&ctx, addr, sdl->sdl_alen);
+       SipHash24_Update(&ctx, &dad_counter, sizeof(dad_counter));
+
+       SipHash24_Final(&in6->s6_addr[8], &ctx);
+
+       return 0;
+}
+
+/*
  * Get interface identifier for the specified interface.  If it is not
  * available on ifp0, borrow interface identifier from other information
  * sources.
@@ -240,7 +280,14 @@ get_ifid(struct ifnet *ifp0, struct in6_addr *in6)
 {
        struct ifnet *ifp;
 
-       /* first, try to get it from the interface itself */
+       /* first, try to generate a Semantically Opaque Interface Identifier */
+       if (get_soii_ifid(ifp0, in6) == 0) {
+               nd6log((LOG_DEBUG, "%s: got Semantically Opaque Interface "
+                   "Identifier\n", ifp0->if_xname));
+               goto success;
+       }
+
+       /* next, try to get it from the interface itself */
        if (get_hw_ifid(ifp0, in6) == 0) {
                nd6log((LOG_DEBUG, "%s: got interface identifier from itself\n",
                    ifp0->if_xname));
diff --git sys/netinet6/ip6_input.c sys/netinet6/ip6_input.c
index ed8702fa71a..ec7a746422a 100644
--- sys/netinet6/ip6_input.c
+++ sys/netinet6/ip6_input.c
@@ -79,6 +79,8 @@
 #include <sys/syslog.h>
 #include <sys/task.h>
 
+#include <crypto/siphash.h>
+
 #include <net/if.h>
 #include <net/if_var.h>
 #include <net/if_types.h>
@@ -92,6 +94,7 @@
 #include <netinet/in_pcb.h>
 #include <netinet/ip_var.h>
 #include <netinet6/in6_var.h>
+#include <netinet6/in6_ifattach.h>
 #include <netinet/ip6.h>
 #include <netinet6/ip6_var.h>
 #include <netinet/icmp6.h>
@@ -118,6 +121,8 @@ struct niqueue ip6intrq = NIQUEUE_INITIALIZER(IFQ_MAXLEN, 
NETISR_IPV6);
 
 struct cpumem *ip6counters;
 
+SIPHASH_KEY ip6_soiikey;
+
 int ip6_ours(struct mbuf **, int *, int, int);
 int ip6_local(struct mbuf **, int *, int, int);
 int ip6_check_rh0hdr(struct mbuf *, int *);
@@ -130,6 +135,7 @@ static struct mbuf_queue    ip6send_mq;
 static void ip6_send_dispatch(void *);
 static struct task ip6send_task =
        TASK_INITIALIZER(ip6_send_dispatch, &ip6send_mq);
+void ifnewsoiikey(struct ifnet *);
 
 /*
  * IP6 initialization: fill in IP6 protocol switch table.
@@ -1376,6 +1382,51 @@ ip6_sysctl_ip6stat(void *oldp, size_t *oldlenp, void 
*newp)
 }
 
 int
+ip6_sysctl_soiikey(void *oldp, size_t *oldlenp, void *newp, size_t newlen)
+{
+       struct ifnet *ifp;
+       SIPHASH_KEY oldkey = ip6_soiikey;
+       int error;
+
+       error = suser(curproc, 0);
+       if (error != 0)
+               return (error);
+
+       error = sysctl_struct(oldp, oldlenp, newp, newlen, &ip6_soiikey,
+           sizeof(ip6_soiikey));
+
+       if (!error && memcmp(&ip6_soiikey, &oldkey, sizeof(oldkey)) != 0) {
+               TAILQ_FOREACH(ifp, &ifnet, if_list) {
+                       if (ifp->if_flags & IFF_LOOPBACK)
+                               continue;
+                       ifnewsoiikey(ifp);
+               }
+       }
+
+       return (error);
+}
+
+void
+ifnewsoiikey(struct ifnet *ifp)
+{
+       struct ifaddr *ifa;
+       int s;
+
+       s = splnet();
+
+       /*
+        * Update the link-local address.
+        */
+       ifa = &in6ifa_ifpforlinklocal(ifp, 0)->ia_ifa;
+       if (ifa) {
+               in6_purgeaddr(ifa);
+               dohooks(ifp->if_addrhooks, 0);
+               in6_ifattach(ifp);
+       }
+       splx(s);
+}
+
+int
 ip6_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp,
     void *newp, size_t newlen)
 {
@@ -1429,6 +1480,8 @@ ip6_sysctl(int *name, u_int namelen, void *oldp, size_t 
*oldlenp,
        case IPV6CTL_IFQUEUE:
                return (sysctl_niq(name + 1, namelen - 1,
                    oldp, oldlenp, newp, newlen, &ip6intrq));
+       case IPV6CTL_SOIIKEY:
+               return (ip6_sysctl_soiikey(oldp, oldlenp, newp, newlen));
        default:
                if (name[0] < IPV6CTL_MAXID)
                        return (sysctl_int_arr(ipv6ctl_vars, name, namelen,
diff --git usr.sbin/slaacctl/parser.c usr.sbin/slaacctl/parser.c
index f4a1858cc87..913bd78f629 100644
--- usr.sbin/slaacctl/parser.c
+++ usr.sbin/slaacctl/parser.c
@@ -32,6 +32,7 @@
 #include <event.h>
 #include <imsg.h>
 #include <limits.h>
+#include <siphash.h> /* XXX SIPHASH_KEY in slaacd.h */
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
diff --git usr.sbin/slaacctl/slaacctl.c usr.sbin/slaacctl/slaacctl.c
index 6668ab10d2b..a972a460165 100644
--- usr.sbin/slaacctl/slaacctl.c
+++ usr.sbin/slaacctl/slaacctl.c
@@ -36,6 +36,7 @@
 #include <event.h>
 #include <imsg.h>
 #include <netdb.h>
+#include <siphash.h> /* XXX SIPHASH_KEY in slaacd.h */
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -193,7 +194,8 @@ show_interface_msg(struct imsg *imsg)
                printf("%s:\n", bufp != NULL ? bufp : "unknown");
                printf("\t index: %3u ", cei->if_index);
                printf("running: %3s ", cei->running ? "yes" : "no");
-               printf("privacy: %3s\n", cei->autoconfprivacy ? "yes" : "no");
+               printf("privacy: %3s ", cei->autoconfprivacy ? "yes" : "no");
+               printf("soii: %3s\n", cei->soii ? "yes" : "no");
                printf("\tlladdr: %s\n", ether_ntoa(&cei->hw_address));
                if (getnameinfo((struct sockaddr *)&cei->ll_address,
                    cei->ll_address.sin6_len, hbuf, sizeof(hbuf), NULL, 0,



-- 
I'm not entirely sure you are real.

Reply via email to