Hi,

"local interface" (-L) is an amazing feature and I use it every day;
but it is IPv4-only and now I realized that I need IPv6 too.

The attached diff implements IPv6 support for local interfaces.

A few notes and limitations:

- Unlike the embedded IPv4 DHCP server, it does not implement a
DHCPv6/rtsol responder in vmd.  It relies on a rad(8) change that I've
sent earlier today.  Configuring rad is easy enough and IPv6 users are
used to jumping though extra hoops: use my rad diff and run the daemon
with "interface tap" in /etc/rad.conf.

- It is disabled by default.  You can enable it with a global option
"local inet6" (to get a runtime random fd00::/8 ULA prefix) or "local
inet6 prefix xxx::/64" (to configure your own prefix).  For
simplicity, the prefix is a global and not a per-VM option.

- Once enabled, IPv6 will be enabled and an additional IPv6 address
configured on the host's VM tap(4) interface whenever you create it
with "local interface" / -L.

- The IPv6 address is derived from the configured prefix and the IPv4
address of the local interface on the VM side.  This way it embeds the
VM and interface Id and you can even pf af-to it to IPv4 again!

```
vm_priv_ifconfig: interface tap0 address 100.64.9.2/31
vm_priv_ifconfig: interface tap0 address fdfc:6be5:806:930a:6440:903:0:1/96
                                                            ^^^^^^^^
                                                            100.64.9.3
```

- The resulting address is suitable for rad(8) - just run "ifconfig
vio0 inet6 autoconf" in the guest and you'll get your /96 IPv6
address.

```
vio0: 
flags=208b43<UP,BROADCAST,RUNNING,PROMISC,ALLMULTI,SIMPLEX,MULTICAST,AUTOCONF6> 
mtu 1500
        lladdr fe:e1:bb:d1:88:4f
        index 1 priority 0 llprio 3
        groups: egress
        media: Ethernet autoselect
        status: active
        inet 100.64.9.3 netmask 0xfffffffe
        inet6 fe80::7a1f:6128:505d:4ea5%vio0 prefixlen 64 scopeid 0x1
        inet6 fdfc:6be5:806:930a:6440:903:b11c:516 prefixlen 96 autoconf 
autoconfprivacy pltime 86063 vltime 604794
        inet6 fdfc:6be5:806:930a:6440:903:d457:347a prefixlen 96 autoconf 
pltime 604794 vltime 2591994
```

- The only problem is that the IPv6 address is nondeterministic where
you cannot guess the VM's IPv6 address "from the outside" (32 bits of
entropy for the guest IP).  It tried it with a /127 prefix but
slaacd/rad don't handle this very well as it has a 50% chance of
creating a duplicate with the host's IP.  I didn't attempt to "fix" it
as it would probably be incompatible with other rtsol clients.  So I
eventually decided that this is not important as I would still use the
IPv4 address to log in - the IPv6 address is primarily used for
outbound connections.

OK?

Reyk

Index: usr.sbin/vmd/config.c
===================================================================
RCS file: /cvs/src/usr.sbin/vmd/config.c,v
retrieving revision 1.54
diff -u -p -u -p -r1.54 config.c
--- usr.sbin/vmd/config.c       26 Oct 2018 11:24:45 -0000      1.54
+++ usr.sbin/vmd/config.c       16 Nov 2018 15:53:10 -0000
@@ -42,17 +42,40 @@
 /* Supported bridge types */
 const char *vmd_descsw[] = { "switch", "bridge", NULL };
 
+int     config_init_localprefix(struct vmd_config *);
+
+int
+config_init_localprefix(struct vmd_config *cfg)
+{
+       struct sockaddr_in6     *sin6;
+
+       if (host(VMD_DHCP_PREFIX, &cfg->cfg_localprefix) == -1)
+               return (-1);
+
+       if (host(VMD_ULA_PREFIX, &cfg->cfg_localprefix6) == -1)
+               return (-1);
+       /* Randomize the 56 bits "Global ID" and "Subnet ID" */
+       sin6 = ss2sin6(&cfg->cfg_localprefix6.ss);
+       arc4random_buf(&sin6->sin6_addr.s6_addr[1], 7);
+
+       /* IPv6 is disabled by default */
+       cfg->cfg_flags &= ~VMD_CFG_INET6;
+
+       return (0);
+}
+
 int
 config_init(struct vmd *env)
 {
-       struct privsep  *ps = &env->vmd_ps;
-       unsigned int     what;
+       struct privsep          *ps = &env->vmd_ps;
+       unsigned int             what;
 
        /* Global configuration */
        ps->ps_what[PROC_PARENT] = CONFIG_ALL;
        ps->ps_what[PROC_VMM] = CONFIG_VMS;
 
-       if (host(VMD_DHCP_PREFIX, &env->vmd_cfg.cfg_localprefix) == -1)
+       /* Local prefix */
+       if (config_init_localprefix(&env->vmd_cfg) == -1)
                return (-1);
 
        /* Other configuration */
@@ -90,7 +113,7 @@ config_purge(struct vmd *env, unsigned i
            __func__, ps->ps_title[privsep_process]);
 
        /* Reset global configuration (prefix was verified before) */
-       (void)host(VMD_DHCP_PREFIX, &env->vmd_cfg.cfg_localprefix);
+       config_init_localprefix(&env->vmd_cfg);
 
        /* Reset other configuration */
        what = ps->ps_what[privsep_process] & reset;
Index: usr.sbin/vmd/dhcp.c
===================================================================
RCS file: /cvs/src/usr.sbin/vmd/dhcp.c,v
retrieving revision 1.5
diff -u -p -u -p -r1.5 dhcp.c
--- usr.sbin/vmd/dhcp.c 17 Aug 2018 07:12:28 -0000      1.5
+++ usr.sbin/vmd/dhcp.c 16 Nov 2018 15:53:10 -0000
@@ -109,7 +109,7 @@ dhcp_request(struct vionet_dev *dev, cha
        resp.xid = req.xid;
 
        if ((client_addr.s_addr =
-           vm_priv_addr(&env->vmd_cfg.cfg_localprefix,
+           vm_priv_addr(&env->vmd_cfg,
            dev->vm_vmid, dev->idx, 1)) == 0)
                return (-1);
        memcpy(&resp.yiaddr, &client_addr,
@@ -119,7 +119,7 @@ dhcp_request(struct vionet_dev *dev, cha
        ss2sin(&pc.pc_dst)->sin_port = htons(CLIENT_PORT);
 
        if ((server_addr.s_addr =
-           vm_priv_addr(&env->vmd_cfg.cfg_localprefix,
+           vm_priv_addr(&env->vmd_cfg,
            dev->vm_vmid, dev->idx, 0)) == 0)
                return (-1);
        memcpy(&resp.siaddr, &server_addr,
Index: usr.sbin/vmd/parse.y
===================================================================
RCS file: /cvs/src/usr.sbin/vmd/parse.y,v
retrieving revision 1.48
diff -u -p -u -p -r1.48 parse.y
--- usr.sbin/vmd/parse.y        1 Nov 2018 00:18:44 -0000       1.48
+++ usr.sbin/vmd/parse.y        16 Nov 2018 15:53:10 -0000
@@ -120,9 +120,9 @@ typedef struct {
 
 
 %token INCLUDE ERROR
-%token ADD ALLOW BOOT CDROM DISABLE DISK DOWN ENABLE FORMAT GROUP INSTANCE
-%token INTERFACE LLADDR LOCAL LOCKED MEMORY NIFS OWNER PATH PREFIX RDOMAIN
-%token SIZE SOCKET SWITCH UP VM VMID
+%token ADD ALLOW BOOT CDROM DISABLE DISK DOWN ENABLE FORMAT GROUP INET6
+%token INSTANCE INTERFACE LLADDR LOCAL LOCKED MEMORY NIFS OWNER PATH PREFIX
+%token RDOMAIN SIZE SOCKET SWITCH UP VM VMID
 %token <v.number>      NUMBER
 %token <v.string>      STRING
 %type  <v.lladdr>      lladdr
@@ -181,10 +181,26 @@ varset            : STRING '=' STRING             {
                }
                ;
 
-main           : LOCAL PREFIX STRING {
+main           : LOCAL INET6 {
+                       env->vmd_cfg.cfg_flags |= VMD_CFG_INET6;
+               }
+               | LOCAL INET6 PREFIX STRING {
+                       struct address   h;
+
+                       if (host($4, &h) == -1 ||
+                           h.ss.ss_family != AF_INET6 ||
+                           h.prefixlen > 64 || h.prefixlen < 0) {
+                               yyerror("invalid local inet6 prefix: %s", $4);
+                               free($4);
+                               YYERROR;
+                       }
+
+                       env->vmd_cfg.cfg_flags |= VMD_CFG_INET6;
+                       memcpy(&env->vmd_cfg.cfg_localprefix6, &h, sizeof(h));
+               }
+               | LOCAL PREFIX STRING {
                        struct address   h;
 
-                       /* The local prefix is IPv4-only */
                        if (host($3, &h) == -1 ||
                            h.ss.ss_family != AF_INET ||
                            h.prefixlen > 32 || h.prefixlen < 0) {
@@ -747,6 +763,7 @@ lookup(char *s)
                { "group",              GROUP },
                { "id",                 VMID },
                { "include",            INCLUDE },
+               { "inet6",              INET6 },
                { "instance",           INSTANCE },
                { "interface",          INTERFACE },
                { "interfaces",         NIFS },
Index: usr.sbin/vmd/priv.c
===================================================================
RCS file: /cvs/src/usr.sbin/vmd/priv.c,v
retrieving revision 1.13
diff -u -p -u -p -r1.13 priv.c
--- usr.sbin/vmd/priv.c 11 Nov 2017 02:50:07 -0000      1.13
+++ usr.sbin/vmd/priv.c 16 Nov 2018 15:53:10 -0000
@@ -27,6 +27,8 @@
 #include <net/if.h>
 #include <netinet/in.h>
 #include <netinet/if_ether.h>
+#include <netinet6/in6_var.h>
+#include <netinet6/nd6.h>
 #include <net/if_bridge.h>
 
 #include <arpa/inet.h>
@@ -70,6 +72,10 @@ priv_run(struct privsep *ps, struct priv
        /* Open our own socket for generic interface ioctls */
        if ((env->vmd_fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
                fatal("socket");
+
+       /* But we need a different fd for IPv6 */
+       if ((env->vmd_fd6 = socket(AF_INET6, SOCK_DGRAM, 0)) == -1)
+               fatal("socket6");
 }
 
 int
@@ -83,6 +89,8 @@ priv_dispatch_parent(int fd, struct priv
        struct ifbreq            ifbr;
        struct ifgroupreq        ifgr;
        struct ifaliasreq        ifra;
+       struct in6_aliasreq      in6_ifra;
+       struct if_afreq          ifar;
        char                     type[IF_NAMESIZE];
 
        switch (imsg->hdr.type) {
@@ -94,6 +102,7 @@ priv_dispatch_parent(int fd, struct priv
        case IMSG_VMDOP_PRIV_IFDOWN:
        case IMSG_VMDOP_PRIV_IFGROUP:
        case IMSG_VMDOP_PRIV_IFADDR:
+       case IMSG_VMDOP_PRIV_IFADDR6:
                IMSG_SIZE_CHECK(imsg, &vfr);
                memcpy(&vfr, imsg->data, sizeof(vfr));
 
@@ -177,22 +186,64 @@ priv_dispatch_parent(int fd, struct priv
        case IMSG_VMDOP_PRIV_IFADDR:
                memset(&ifra, 0, sizeof(ifra));
 
+               if (vfr.vfr_addr.ss_family != AF_INET ||
+                   vfr.vfr_addr.ss_family != vfr.vfr_mask.ss_family)
+                       fatalx("%s: invalid address family", __func__);
+
                /* Set the interface address */
                strlcpy(ifra.ifra_name, vfr.vfr_name, sizeof(ifra.ifra_name));
 
-               memcpy(&ifra.ifra_addr, &vfr.vfr_ifra.ifra_addr,
-                   sizeof(ifra.ifra_addr));
-               ifra.ifra_addr.sa_family = AF_INET;
-               ifra.ifra_addr.sa_len = sizeof(struct sockaddr_in);
-
-               memcpy(&ifra.ifra_mask, &vfr.vfr_ifra.ifra_mask,
-                   sizeof(ifra.ifra_mask));
-               ifra.ifra_mask.sa_family = AF_INET;
-               ifra.ifra_mask.sa_len = sizeof(struct sockaddr_in);
+               ifra.ifra_addr.sa_len =
+                   ifra.ifra_mask.sa_len =
+                   sizeof(struct sockaddr_in);
+
+               memcpy(&ifra.ifra_addr, &vfr.vfr_addr,
+                   ifra.ifra_addr.sa_len);
+               memcpy(&ifra.ifra_mask, &vfr.vfr_mask,
+                   ifra.ifra_mask.sa_len);
 
                if (ioctl(env->vmd_fd, SIOCAIFADDR, &ifra) < 0)
                        log_warn("SIOCAIFADDR");
                break;
+       case IMSG_VMDOP_PRIV_IFADDR6:
+               memset(&ifar, 0, sizeof(ifar));
+               memset(&in6_ifra, 0, sizeof(in6_ifra));
+
+               if (vfr.vfr_addr.ss_family != AF_INET6 ||
+                   vfr.vfr_addr.ss_family != vfr.vfr_mask.ss_family)
+                       fatalx("%s: invalid address family", __func__);
+
+               /* First enable IPv6 on this interface */
+               strlcpy(ifar.ifar_name, vfr.vfr_name,
+                   sizeof(ifar.ifar_name));
+               ifar.ifar_af = AF_INET6;
+               if (ioctl(env->vmd_fd, SIOCIFAFATTACH, (caddr_t)&ifar) < 0)
+                       log_warn("SIOCIFAFATTACH");
+
+               /* Set the interface address */
+               strlcpy(in6_ifra.ifra_name, vfr.vfr_name,
+                   sizeof(in6_ifra.ifra_name));
+
+               in6_ifra.ifra_addr.sin6_len =
+                   in6_ifra.ifra_prefixmask.sin6_len =
+                   sizeof(struct sockaddr_in6);
+
+               memcpy(&in6_ifra.ifra_addr, &vfr.vfr_addr,
+                   in6_ifra.ifra_addr.sin6_len);
+               memcpy(&in6_ifra.ifra_prefixmask, &vfr.vfr_mask,
+                   in6_ifra.ifra_prefixmask.sin6_len);
+               in6_ifra.ifra_prefixmask.sin6_scope_id = 0;
+
+               in6_ifra.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME;
+               in6_ifra.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME;
+
+               if (ioctl(env->vmd_fd6, SIOCDIFADDR_IN6, &in6_ifra) < 0 &&
+                   errno != EADDRNOTAVAIL)
+                       log_warn("SIOCDIFADDR_IN6");
+
+               if (ioctl(env->vmd_fd6, SIOCAIFADDR_IN6, &in6_ifra) < 0)
+                       log_warn("SIOCAIFADDR_IN6");
+               break;
        case IMSG_VMDOP_CONFIG:
                config_getconfig(env, imsg);
                break;
@@ -270,6 +321,7 @@ priv_validgroup(const char *name)
 int
 vm_priv_ifconfig(struct privsep *ps, struct vmd_vm *vm)
 {
+       char                     name[64];
        struct vmd              *env = ps->ps_env;
        struct vm_create_params *vcp = &vm->vm_params.vmc_params;
        struct vmd_if           *vif;
@@ -277,6 +329,7 @@ vm_priv_ifconfig(struct privsep *ps, str
        unsigned int             i;
        struct vmop_ifreq        vfr, vfbr;
        struct sockaddr_in      *sin4;
+       struct sockaddr_in6     *sin6;
 
        for (i = 0; i < VMM_MAX_NICS_PER_VM; i++) {
                vif = &vm->vm_ifs[i];
@@ -284,6 +337,7 @@ vm_priv_ifconfig(struct privsep *ps, str
                if (vif->vif_name == NULL)
                        break;
 
+               memset(&vfr, 0, sizeof(vfr));
                if (strlcpy(vfr.vfr_name, vif->vif_name,
                    sizeof(vfr.vfr_name)) >= sizeof(vfr.vfr_name))
                        return (-1);
@@ -378,26 +432,58 @@ vm_priv_ifconfig(struct privsep *ps, str
 
                /* Set interface address if it is a local interface */
                if (vm->vm_params.vmc_ifflags[i] & VMIFF_LOCAL) {
-                       sin4 = (struct sockaddr_in *)&vfr.vfr_ifra.ifra_mask;
+                       memset(&vfr.vfr_mask, 0, sizeof(vfr.vfr_mask));
+                       memset(&vfr.vfr_addr, 0, sizeof(vfr.vfr_addr));
+
+                       /* IPv4 */
+                       sin4 = (struct sockaddr_in *)&vfr.vfr_mask;
                        sin4->sin_family = AF_INET;
                        sin4->sin_len = sizeof(*sin4);
                        sin4->sin_addr.s_addr = htonl(0xfffffffe);
 
-                       sin4 = (struct sockaddr_in *)&vfr.vfr_ifra.ifra_addr;
+                       sin4 = (struct sockaddr_in *)&vfr.vfr_addr;
                        sin4->sin_family = AF_INET;
                        sin4->sin_len = sizeof(*sin4);
                        if ((sin4->sin_addr.s_addr =
-                           vm_priv_addr(&env->vmd_cfg.cfg_localprefix,
+                           vm_priv_addr(&env->vmd_cfg,
                            vm->vm_vmid, i, 0)) == 0)
                                return (-1);
 
+                       inet_ntop(AF_INET, &sin4->sin_addr,
+                           name, sizeof(name));
                        log_debug("%s: interface %s address %s/31",
-                           __func__, vfr.vfr_name,
-                           inet_ntoa(sin4->sin_addr));
+                           __func__, vfr.vfr_name, name);
 
                        proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFADDR,
                            &vfr, sizeof(vfr));
                }
+               if ((vm->vm_params.vmc_ifflags[i] & VMIFF_LOCAL) &&
+                   (env->vmd_cfg.cfg_flags & VMD_CFG_INET6)) {
+                       memset(&vfr.vfr_mask, 0, sizeof(vfr.vfr_mask));
+                       memset(&vfr.vfr_addr, 0, sizeof(vfr.vfr_addr));
+
+                       /* IPv6 */
+                       sin6 = ss2sin6(&vfr.vfr_mask);
+                       sin6->sin6_family = AF_INET6;
+                       sin6->sin6_len = sizeof(*sin6);
+                       memset(&sin6->sin6_addr.s6_addr[0], 0xff, 12);
+                       memset(&sin6->sin6_addr.s6_addr[12], 0, 4);
+
+                       sin6 = ss2sin6(&vfr.vfr_addr);
+                       sin6->sin6_family = AF_INET6;
+                       sin6->sin6_len = sizeof(*sin6);
+                       if (vm_priv_addr6(&env->vmd_cfg,
+                           vm->vm_vmid, i, 0, &sin6->sin6_addr) == -1)
+                               return (-1);
+
+                       inet_ntop(AF_INET6, &sin6->sin6_addr,
+                           name, sizeof(name));
+                       log_debug("%s: interface %s address %s/96",
+                           __func__, vfr.vfr_name, name);
+
+                       proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFADDR6,
+                           &vfr, sizeof(vfr));
+               }
        }
 
        return (0);
@@ -458,9 +544,10 @@ vm_priv_brconfig(struct privsep *ps, str
 }
 
 uint32_t
-vm_priv_addr(struct address *h, uint32_t vmid, int idx, int isvm)
+vm_priv_addr(struct vmd_config *cfg, uint32_t vmid, int idx, int isvm)
 {
-       in_addr_t               prefix, mask, addr;
+       struct address          *h = &cfg->cfg_localprefix;
+       in_addr_t                prefix, mask, addr;
 
        /*
         * 1. Set the address prefix and mask, 100.64.0.0/10 by default.
@@ -500,4 +587,39 @@ vm_priv_addr(struct address *h, uint32_t
        }
 
        return (addr);
+}
+
+int
+vm_priv_addr6(struct vmd_config *cfg, uint32_t vmid,
+    int idx, int isvm, struct in6_addr *in6_addr)
+{
+       struct address          *h = &cfg->cfg_localprefix6;
+       struct in6_addr          addr, mask;
+       uint32_t                 addr4;
+
+       /* 1. Set the address prefix and mask, fd00::/8 by default. */
+       if (h->ss.ss_family != AF_INET6 ||
+           h->prefixlen < 0 || h->prefixlen > 128)
+               fatal("local prefix6");
+       addr = ss2sin6(&h->ss)->sin6_addr;
+       prefixlen2mask6(h->prefixlen, &mask);
+
+       /* 2. Encode the VM IPv4 address as subnet, fd00::NN:NN:0:0/96. */
+       if ((addr4 = vm_priv_addr(cfg, vmid, idx, 1)) == 0)
+               return (0);
+       memcpy(&addr.s6_addr[8], &addr4, sizeof(addr4));
+
+       /*
+        * 3. Set the last octet to 1 (host) or 2 (VM).
+        * The latter is currently not used inside vmd as we don't
+        * answer rtsol requests ourselves.
+        */
+       if (!isvm)
+               addr.s6_addr[15] = 1;
+       else
+               addr.s6_addr[15] = 2;
+
+       memcpy(in6_addr, &addr, sizeof(*in6_addr));
+
+       return (0);
 }
Index: usr.sbin/vmd/vm.conf.5
===================================================================
RCS file: /cvs/src/usr.sbin/vmd/vm.conf.5,v
retrieving revision 1.39
diff -u -p -u -p -r1.39 vm.conf.5
--- usr.sbin/vmd/vm.conf.5      30 Oct 2018 17:56:54 -0000      1.39
+++ usr.sbin/vmd/vm.conf.5      16 Nov 2018 15:53:10 -0000
@@ -100,6 +100,13 @@ in the
 section below.
 The default is
 .Ar 100.64.0.0/10 .
+.It Ic local inet6 Op Ic prefix Ar address Ns Li / Ns Ar prefix
+Enable IPv6 on local interfaces and allocate routable subnets.
+If the prefix is not specified,
+a random unique local address prefix
+.Pq Ar fd00::/8
+will be generated on startup.
+The prefix length must be /64 or smaller.
 .It Cm socket owner Ar user Ns Op : Ns Ar group
 Set the control socket owner to the specified user or group.
 Users with access to the control socket will be allowed to use
@@ -217,6 +224,19 @@ interface will auto-generate an IPv4 sub
 configure a gateway address on the VM host side,
 and run a simple DHCP/BOOTP server for the VM.
 This option can be used for layer 3 mode without configuring a switch.
+.Pp
+If the global
+.Cm local inet6
+option is enabled, a routable IPv6 gateway address will be generated
+on the host side.
+Unlike the IPv4 option,
+.Nm vmd
+does not respond to DHCPv6 or router solicitation messages itself.
+Use
+.Xr rad 8
+listening on the interface group, e.g.
+.Ar interface tap
+for auto-configuring the VMs accordingly.
 .It Cm interfaces Ar count
 Optional minimum number of network interfaces to add to the VM.
 If the
Index: usr.sbin/vmd/vmd.c
===================================================================
RCS file: /cvs/src/usr.sbin/vmd/vmd.c,v
retrieving revision 1.104
diff -u -p -u -p -r1.104 vmd.c
--- usr.sbin/vmd/vmd.c  15 Oct 2018 10:35:41 -0000      1.104
+++ usr.sbin/vmd/vmd.c  16 Nov 2018 15:53:12 -0000
@@ -1923,6 +1923,25 @@ prefixlen2mask(uint8_t prefixlen)
 }
 
 void
+prefixlen2mask6(uint8_t prefixlen, struct in6_addr *mask)
+{
+       struct in6_addr  s6;
+       int                     i;
+
+       if (prefixlen > 128)
+               prefixlen = 128;
+
+       memset(&s6, 0, sizeof(s6));
+       for (i = 0; i < prefixlen / 8; i++)
+               s6.s6_addr[i] = 0xff;
+       i = prefixlen % 8;
+       if (i)
+               s6.s6_addr[prefixlen / 8] = 0xff00 >> i;
+
+       memcpy(mask, &s6, sizeof(s6));
+}
+
+void
 getmonotime(struct timeval *tv)
 {
        struct timespec  ts;
Index: usr.sbin/vmd/vmd.h
===================================================================
RCS file: /cvs/src/usr.sbin/vmd/vmd.h,v
retrieving revision 1.84
diff -u -p -u -p -r1.84 vmd.h
--- usr.sbin/vmd/vmd.h  19 Oct 2018 10:12:39 -0000      1.84
+++ usr.sbin/vmd/vmd.h  16 Nov 2018 15:53:12 -0000
@@ -25,6 +25,7 @@
 #include <net/if.h>
 #include <netinet/in.h>
 #include <netinet/if_ether.h>
+#include <netinet6/in6_var.h>
 
 #include <limits.h>
 #include <stdio.h>
@@ -78,6 +79,9 @@
 /* 100.64.0.0/10 from rfc6598 (IPv4 Prefix for Shared Address Space) */
 #define VMD_DHCP_PREFIX                "100.64.0.0/10"
 
+/* Unique local address for IPv6 */
+#define VMD_ULA_PREFIX         "fd00::/8"
+
 enum imsg_type {
        IMSG_VMDOP_START_VM_REQUEST = IMSG_PROC_MAX,
        IMSG_VMDOP_START_VM_CDROM,
@@ -109,6 +113,7 @@ enum imsg_type {
        IMSG_VMDOP_PRIV_IFDOWN,
        IMSG_VMDOP_PRIV_IFGROUP,
        IMSG_VMDOP_PRIV_IFADDR,
+       IMSG_VMDOP_PRIV_IFADDR6,
        IMSG_VMDOP_PRIV_IFRDOMAIN,
        IMSG_VMDOP_VM_SHUTDOWN,
        IMSG_VMDOP_VM_REBOOT,
@@ -140,10 +145,11 @@ struct vmop_id {
 };
 
 struct vmop_ifreq {
-       uint32_t                 vfr_id;
-       char                     vfr_name[IF_NAMESIZE];
-       char                     vfr_value[VM_NAME_MAX];
-       struct ifaliasreq        vfr_ifra;
+       uint32_t                         vfr_id;
+       char                             vfr_name[IF_NAMESIZE];
+       char                             vfr_value[VM_NAME_MAX];
+       struct sockaddr_storage          vfr_addr;
+       struct sockaddr_storage          vfr_mask;
 };
 
 struct vmop_owner {
@@ -292,7 +298,11 @@ struct address {
 TAILQ_HEAD(addresslist, address);
 
 struct vmd_config {
+       unsigned int             cfg_flags;
+#define VMD_CFG_INET6          0x1
+
        struct address           cfg_localprefix;
+       struct address           cfg_localprefix6;
 };
 
 struct vmd {
@@ -313,6 +323,7 @@ struct vmd {
        struct userlist         *vmd_users;
 
        int                      vmd_fd;
+       int                      vmd_fd6;
        int                      vmd_ptmfd;
 };
 
@@ -372,6 +383,7 @@ void         user_inc(struct vm_create_params *
 int     user_checklimit(struct vmd_user *, struct vm_create_params *);
 char   *get_string(uint8_t *, size_t);
 uint32_t prefixlen2mask(uint8_t);
+void    prefixlen2mask6(u_int8_t, struct in6_addr *);
 void    getmonotime(struct timeval *);
 
 /* priv.c */
@@ -381,7 +393,9 @@ int  priv_findname(const char *, const c
 int     priv_validgroup(const char *);
 int     vm_priv_ifconfig(struct privsep *, struct vmd_vm *);
 int     vm_priv_brconfig(struct privsep *, struct vmd_switch *);
-uint32_t vm_priv_addr(struct address *, uint32_t, int, int);
+uint32_t vm_priv_addr(struct vmd_config *, uint32_t, int, int);
+int     vm_priv_addr6(struct vmd_config *, uint32_t, int, int,
+           struct in6_addr *);
 
 /* vmm.c */
 struct iovec;

Reply via email to