next try
- sha512
- fixed key size
- /etc/soii.key
- man page tweaks from sthen & jmc and rewording by me to get rid of 48 bits
- link local address is updated when soii flag is toggled

If this is the final version I can cut up the diff and send parts if
people prefer. But I also take OKs for the big one :)

diff --git etc/rc etc/rc
index 48e5671335f..47dc78362c2 100644
--- etc/rc
+++ etc/rc
@@ -47,6 +47,14 @@ update_limit() {
        done
 }
 
+# Apply soii.key settings.
+soii_key() {
+       stripcom /etc/soii.key |
+       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
+       soii_key
 }
 
 # Apply mixerctl.conf(5) settings.
@@ -154,6 +163,11 @@ make_keys() {
        fi
 
        ssh-keygen -A
+
+       if [[ ! -f /etc/soii.key ]]; then
+               openssl rand -hex 16 > /etc/soii.key && \
+                   chmod 600 /etc/soii.key && soii_key
+       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..3f0dd8f373b 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,22 @@ 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.
+The purpose of these identifiers is to make discovery of hosts by
+scanning a whole prefix more difficult.
+SOIIs use the whole 64 bits of the host part while SLAAC addresses are
+formed from MAC addresses which can lower the entropy to 24 bits if
+the host is running in a virtualization environment or the hardware
+manufacturer is known.
+See RFC 8064 for details.
+SOIIs are enabled by default.
+.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/engine.c sbin/slaacd/engine.c
index 394df48eeb6..5fecf6e8779 100644
--- sbin/slaacd/engine.c
+++ sbin/slaacd/engine.c
@@ -64,6 +64,8 @@
 #include <netinet6/nd6.h>
 #include <netinet/icmp6.h>
 
+#include <crypto/sha2.h>
+
 #include <errno.h>
 #include <event.h>
 #include <imsg.h>
@@ -180,6 +182,7 @@ struct address_proposal {
        uint8_t                          prefix_len;
        uint32_t                         vltime;
        uint32_t                         pltime;
+       uint8_t                          soiikey[SLAACD_SOIIKEY_LEN];
 };
 
 struct dfr_proposal {
@@ -205,8 +208,10 @@ struct slaacd_iface {
        uint32_t                         if_index;
        int                              running;
        int                              autoconfprivacy;
+       int                              soii;
        struct ether_addr                hw_address;
        struct sockaddr_in6              ll_address;
+       uint8_t                          soiikey[SLAACD_SOIIKEY_LEN];
        LIST_HEAD(, radv)                radvs;
        LIST_HEAD(, address_proposal)    addr_proposals;
        LIST_HEAD(, dfr_proposal)        dfr_proposals;
@@ -615,12 +620,15 @@ engine_dispatch_main(int fd, short event, void *bula)
                                        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);
@@ -635,6 +643,14 @@ engine_dispatch_main(int fd, short event, void *bula)
                                            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) {
@@ -643,6 +659,14 @@ engine_dispatch_main(int fd, short event, void *bula)
                                            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)
@@ -699,6 +723,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));
@@ -1149,7 +1174,10 @@ void
 gen_addr(struct slaacd_iface *iface, struct radv_prefix *prefix, struct
     address_proposal *addr_proposal, int privacy)
 {
+       SHA2_CTX ctx;
        struct in6_addr priv_in6;
+       int dad_counter = 0; /* XXX not used */
+       u_int8_t digest[SHA512_DIGEST_LENGTH];
 
        /* from in6_ifadd() in nd6_rtr.c */
        /* XXX from in6.h, guarded by #ifdef _KERNEL   XXX nonstandard */
@@ -1196,20 +1224,33 @@ 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) {
+                       SHA512Init(&ctx);
+                       SHA512Update(&ctx, &prefix->prefix,
+                           sizeof(prefix->prefix));
+                       SHA512Update(&ctx, &iface->hw_address,
+                           sizeof(iface->hw_address));
+                       SHA512Update(&ctx, &dad_counter, sizeof(dad_counter));
+                       SHA512Update(&ctx, addr_proposal->soiikey,
+                           sizeof(addr_proposal->soiikey));
+                       SHA512Final(digest, &ctx);
+                       memcpy(&addr_proposal->addr.sin6_addr.s6_addr[8],
+                           digest, 8);
+               } 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
 }
 
@@ -1576,6 +1617,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
@@ -1716,6 +1762,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 8bd49e2f1ad..9b74a9312cf 100644
--- sbin/slaacd/frontend.c
+++ sbin/slaacd/frontend.c
@@ -504,6 +504,7 @@ 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,
diff --git sbin/slaacd/slaacd.c sbin/slaacd/slaacd.c
index 91cb9c143a2..8f75c66e59f 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>
 
@@ -94,6 +95,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(uint8_t *);
 
 static int     main_imsg_send_ipc_sockets(struct imsgbuf *, struct imsgbuf *);
 
@@ -411,8 +413,11 @@ main_dispatch_frontend(int fd, short event, void *bula)
                                fatal("%s: IMSG_UPDATE_IF wrong length: %d",
                                    __func__, imsg.hdr.len);
                        memcpy(&imsg_ifinfo, imsg.data, sizeof(imsg_ifinfo));
-                       main_imsg_compose_engine(IMSG_UPDATE_IF, 0,
-                           &imsg_ifinfo, 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__,
@@ -784,3 +789,12 @@ delete_gateway(struct imsg_configure_dfr *dfr)
 {
        configure_gateway(dfr, RTM_DELETE);
 }
+
+int
+get_soiikey(uint8_t *key)
+{
+       int      mib[4] = {CTL_NET, PF_INET6, IPPROTO_IPV6, IPV6CTL_SOIIKEY};
+       size_t   size = SLAACD_SOIIKEY_LEN;
+
+       return sysctl(mib, sizeof(mib) / sizeof(mib[0]), key, &size, NULL, 0);
+}
diff --git sbin/slaacd/slaacd.h sbin/slaacd/slaacd.h
index ecc192ce1da..b9dfa00f312 100644
--- sbin/slaacd/slaacd.h
+++ sbin/slaacd/slaacd.h
@@ -27,6 +27,8 @@
 #define SLAACD_MAXTEXT         256
 #define SLAACD_MAXGROUPNAME    16
 
+#define SLAACD_SOIIKEY_LEN     16
+
 /* MAXDNAME from arpa/namesr.h */
 #define SLAACD_MAX_DNSSL       1025
 
@@ -92,6 +94,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 +163,10 @@ struct imsg_ifinfo {
        uint32_t                if_index;
        int                     running;
        int                     autoconfprivacy;
+       int                     soii;
        struct ether_addr       hw_address;
        struct sockaddr_in6     ll_address;
+       uint8_t                 soiikey[SLAACD_SOIIKEY_LEN];
 };
 
 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.c sys/net/if.c
index 7477fa5606a..b11414c2266 100644
--- sys/net/if.c
+++ sys/net/if.c
@@ -1827,6 +1827,9 @@ ifioctl(struct socket *so, u_long cmd, caddr_t data, 
struct proc *p)
        char ifdescrbuf[IFDESCRSIZE];
        char ifrtlabelbuf[RTLABEL_LEN];
        int s, error = 0;
+#ifdef INET6
+       int soii_changed = 0;
+#endif /* INET6 */
        size_t bytesdone;
        short oif_flags;
        const char *label;
@@ -1952,6 +1955,12 @@ ifioctl(struct socket *so, u_long cmd, caddr_t data, 
struct proc *p)
                        if (error != 0)
                                return (error);
                }
+
+               if ((ISSET(ifr->ifr_flags, IFXF_INET6_NOSOII) &&
+                   !ISSET(ifp->if_xflags, IFXF_INET6_NOSOII)) ||
+                   (!ISSET(ifr->ifr_flags, IFXF_INET6_NOSOII) &&
+                   ISSET(ifp->if_xflags, IFXF_INET6_NOSOII)))
+                       soii_changed = 1;
 #endif /* INET6 */
 
 #ifdef MPLS
@@ -2002,6 +2011,10 @@ ifioctl(struct socket *so, u_long cmd, caddr_t data, 
struct proc *p)
                ifp->if_xflags = (ifp->if_xflags & IFXF_CANTCHANGE) |
                        (ifr->ifr_flags & ~IFXF_CANTCHANGE);
                rtm_ifchg(ifp);
+#ifdef INET6
+               if (soii_changed)
+                       in6_soiiupdate(ifp);
+#endif /* INET6 */
                break;
 
        case SIOCSIFMETRIC:
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 ac0120e4217..549cf0c5159 100644
--- sys/netinet6/in6.h
+++ sys/netinet6/in6.h
@@ -590,7 +590,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 */
@@ -650,6 +651,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..dd6085d0b9f 100644
--- sys/netinet6/in6_ifattach.c
+++ sys/netinet6/in6_ifattach.c
@@ -58,6 +58,7 @@
 
 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 *);
 
@@ -72,6 +73,26 @@ int in6_ifattach_loopback(struct ifnet *);
 #define IFID_LOCAL(in6)                (!EUI64_LOCAL(in6))
 #define IFID_UNIVERSAL(in6)    (!EUI64_UNIVERSAL(in6))
 
+void
+in6_soiiupdate(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);
+}
+
 /*
  * Generate a last-resort interface identifier, when the machine has no
  * IEEE802/EUI64 address sources.
@@ -231,6 +252,45 @@ 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)
+{
+       SHA2_CTX ctx;
+       u_int8_t digest[SHA512_DIGEST_LENGTH];
+       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);
+
+       SHA512Init(&ctx);
+
+       SHA512Update(&ctx, &prefix, sizeof(prefix));
+       SHA512Update(&ctx, addr, sdl->sdl_alen);
+       SHA512Update(&ctx, &dad_counter, sizeof(dad_counter));
+       SHA512Update(&ctx, ip6_soiikey, sizeof(ip6_soiikey));
+       SHA512Final(digest, &ctx);
+
+       bcopy(digest, &in6->s6_addr[8], 8);
+
+       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 +300,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/in6_ifattach.h sys/netinet6/in6_ifattach.h
index 6160fb984a6..00aad7dbc10 100644
--- sys/netinet6/in6_ifattach.h
+++ sys/netinet6/in6_ifattach.h
@@ -38,6 +38,7 @@ int in6_ifattach(struct ifnet *);
 void in6_ifdetach(struct ifnet *);
 int in6_nigroup(struct ifnet *, const char *, int, struct sockaddr_in6 *);
 int in6_ifattach_linklocal(struct ifnet *, struct in6_addr *);
+void in6_soiiupdate(struct ifnet *);
 #endif /* _KERNEL */
 
 #endif /* _NETINET6_IN6_IFATTACH_H_ */
diff --git sys/netinet6/ip6_input.c sys/netinet6/ip6_input.c
index ed8702fa71a..758151c5571 100644
--- sys/netinet6/ip6_input.c
+++ sys/netinet6/ip6_input.c
@@ -92,6 +92,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 +119,8 @@ struct niqueue ip6intrq = NIQUEUE_INITIALIZER(IFQ_MAXLEN, 
NETISR_IPV6);
 
 struct cpumem *ip6counters;
 
+uint8_t ip6_soiikey[IP6_SOIIKEY_LEN];
+
 int ip6_ours(struct mbuf **, int *, int, int);
 int ip6_local(struct mbuf **, int *, int, int);
 int ip6_check_rh0hdr(struct mbuf *, int *);
@@ -1376,6 +1379,33 @@ 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;
+       uint8_t oldkey[16];
+       int error;
+
+       error = suser(curproc, 0);
+       if (error != 0)
+               return (error);
+
+       memcpy(oldkey, ip6_soiikey, sizeof(oldkey));
+
+       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;
+                       in6_soiiupdate(ifp);
+               }
+       }
+
+       return (error);
+}
+
+int
 ip6_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp,
     void *newp, size_t newlen)
 {
@@ -1429,6 +1459,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 sys/netinet6/ip6_var.h sys/netinet6/ip6_var.h
index 2b9f86cab2f..f1ae3680b81 100644
--- sys/netinet6/ip6_var.h
+++ sys/netinet6/ip6_var.h
@@ -294,6 +294,9 @@ extern int  ip6_dad_pending;        /* number of currently 
running DADs */
 extern int ip6_auto_flowlabel;
 extern int ip6_auto_linklocal;
 
+#define        IP6_SOIIKEY_LEN 16
+extern uint8_t ip6_soiikey[IP6_SOIIKEY_LEN];
+
 struct in6pcb;
 struct inpcb;
 
diff --git usr.sbin/slaacctl/slaacctl.c usr.sbin/slaacctl/slaacctl.c
index 6668ab10d2b..e701a066ebf 100644
--- usr.sbin/slaacctl/slaacctl.c
+++ usr.sbin/slaacctl/slaacctl.c
@@ -193,7 +193,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