priv.c revision 1.15
1/*	$OpenBSD: priv.c,v 1.15 2019/06/28 13:32:51 deraadt Exp $	*/
2
3/*
4 * Copyright (c) 2016 Reyk Floeter <reyk@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#include <sys/param.h>	/* nitems */
20#include <sys/queue.h>
21#include <sys/stat.h>
22#include <sys/socket.h>
23#include <sys/un.h>
24#include <sys/ioctl.h>
25#include <sys/tree.h>
26
27#include <net/if.h>
28#include <netinet/in.h>
29#include <netinet/if_ether.h>
30#include <netinet6/in6_var.h>
31#include <netinet6/nd6.h>
32#include <net/if_bridge.h>
33
34#include <arpa/inet.h>
35
36#include <errno.h>
37#include <event.h>
38#include <fcntl.h>
39#include <stdlib.h>
40#include <stdio.h>
41#include <string.h>
42#include <unistd.h>
43#include <signal.h>
44#include <ctype.h>
45
46#include "proc.h"
47#include "vmd.h"
48
49int	 priv_dispatch_parent(int, struct privsep_proc *, struct imsg *);
50void	 priv_run(struct privsep *, struct privsep_proc *, void *);
51
52static struct privsep_proc procs[] = {
53	{ "parent",	PROC_PARENT,	priv_dispatch_parent }
54};
55
56void
57priv(struct privsep *ps, struct privsep_proc *p)
58{
59	proc_run(ps, p, procs, nitems(procs), priv_run, NULL);
60}
61
62void
63priv_run(struct privsep *ps, struct privsep_proc *p, void *arg)
64{
65	struct vmd		*env = ps->ps_env;
66
67	/*
68	 * no pledge(2) in the "priv" process:
69	 * write ioctls are not permitted by pledge.
70	 */
71
72	/* Open our own socket for generic interface ioctls */
73	if ((env->vmd_fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
74		fatal("socket");
75
76	/* But we need a different fd for IPv6 */
77	if ((env->vmd_fd6 = socket(AF_INET6, SOCK_DGRAM, 0)) == -1)
78		fatal("socket6");
79}
80
81int
82priv_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg)
83{
84	const char		*desct[] = { "tap", "switch", "bridge", NULL };
85	struct privsep		*ps = p->p_ps;
86	struct vmop_ifreq	 vfr;
87	struct vmd		*env = ps->ps_env;
88	struct ifreq		 ifr;
89	struct ifbreq		 ifbr;
90	struct ifgroupreq	 ifgr;
91	struct ifaliasreq	 ifra;
92	struct in6_aliasreq	 in6_ifra;
93	struct if_afreq		 ifar;
94	char			 type[IF_NAMESIZE];
95
96	switch (imsg->hdr.type) {
97	case IMSG_VMDOP_PRIV_IFDESCR:
98	case IMSG_VMDOP_PRIV_IFRDOMAIN:
99	case IMSG_VMDOP_PRIV_IFEXISTS:
100	case IMSG_VMDOP_PRIV_IFADD:
101	case IMSG_VMDOP_PRIV_IFUP:
102	case IMSG_VMDOP_PRIV_IFDOWN:
103	case IMSG_VMDOP_PRIV_IFGROUP:
104	case IMSG_VMDOP_PRIV_IFADDR:
105	case IMSG_VMDOP_PRIV_IFADDR6:
106		IMSG_SIZE_CHECK(imsg, &vfr);
107		memcpy(&vfr, imsg->data, sizeof(vfr));
108
109		/* We should not get malicious requests from the parent */
110		if (priv_getiftype(vfr.vfr_name, type, NULL) == -1 ||
111		    priv_findname(type, desct) == -1)
112			fatalx("%s: rejected priv operation on interface: %s",
113			    __func__, vfr.vfr_name);
114		break;
115	case IMSG_VMDOP_CONFIG:
116	case IMSG_CTL_RESET:
117		break;
118	default:
119		return (-1);
120	}
121
122	switch (imsg->hdr.type) {
123	case IMSG_VMDOP_PRIV_IFDESCR:
124		/* Set the interface description */
125		strlcpy(ifr.ifr_name, vfr.vfr_name, sizeof(ifr.ifr_name));
126		ifr.ifr_data = (caddr_t)vfr.vfr_value;
127		if (ioctl(env->vmd_fd, SIOCSIFDESCR, &ifr) == -1)
128			log_warn("SIOCSIFDESCR");
129		break;
130	case IMSG_VMDOP_PRIV_IFRDOMAIN:
131		strlcpy(ifr.ifr_name, vfr.vfr_name, sizeof(ifr.ifr_name));
132		ifr.ifr_rdomainid = vfr.vfr_id;
133		if (ioctl(env->vmd_fd, SIOCSIFRDOMAIN, &ifr) == -1)
134			log_warn("SIOCSIFRDOMAIN");
135		break;
136	case IMSG_VMDOP_PRIV_IFADD:
137		if (priv_getiftype(vfr.vfr_value, type, NULL) == -1)
138			fatalx("%s: rejected to add interface: %s",
139			    __func__, vfr.vfr_value);
140
141		/* Attach the device to the bridge */
142		strlcpy(ifbr.ifbr_name, vfr.vfr_name,
143		    sizeof(ifbr.ifbr_name));
144		strlcpy(ifbr.ifbr_ifsname, vfr.vfr_value,
145		    sizeof(ifbr.ifbr_ifsname));
146		if (ioctl(env->vmd_fd, SIOCBRDGADD, &ifbr) == -1 &&
147		    errno != EEXIST)
148			log_warn("SIOCBRDGADD");
149		break;
150	case IMSG_VMDOP_PRIV_IFEXISTS:
151		/* Determine if bridge/switch exists */
152		strlcpy(ifr.ifr_name, vfr.vfr_name, sizeof(ifr.ifr_name));
153		if (ioctl(env->vmd_fd, SIOCGIFFLAGS, &ifr) == -1)
154			fatalx("%s: bridge \"%s\" does not exist",
155			    __func__, vfr.vfr_name);
156		break;
157	case IMSG_VMDOP_PRIV_IFUP:
158	case IMSG_VMDOP_PRIV_IFDOWN:
159		/* Set the interface status */
160		strlcpy(ifr.ifr_name, vfr.vfr_name, sizeof(ifr.ifr_name));
161		if (ioctl(env->vmd_fd, SIOCGIFFLAGS, &ifr) == -1) {
162			log_warn("SIOCGIFFLAGS");
163			break;
164		}
165		if (imsg->hdr.type == IMSG_VMDOP_PRIV_IFUP)
166			ifr.ifr_flags |= IFF_UP;
167		else
168			ifr.ifr_flags &= ~IFF_UP;
169		if (ioctl(env->vmd_fd, SIOCSIFFLAGS, &ifr) == -1)
170			log_warn("SIOCSIFFLAGS");
171		break;
172	case IMSG_VMDOP_PRIV_IFGROUP:
173		if (priv_validgroup(vfr.vfr_value) == -1)
174			fatalx("%s: invalid group name", __func__);
175
176		if (strlcpy(ifgr.ifgr_name, vfr.vfr_name,
177		    sizeof(ifgr.ifgr_name)) >= sizeof(ifgr.ifgr_name) ||
178		    strlcpy(ifgr.ifgr_group, vfr.vfr_value,
179		    sizeof(ifgr.ifgr_group)) >= sizeof(ifgr.ifgr_group))
180			fatalx("%s: group name too long", __func__);
181
182		if (ioctl(env->vmd_fd, SIOCAIFGROUP, &ifgr) == -1 &&
183		    errno != EEXIST)
184			log_warn("SIOCAIFGROUP");
185		break;
186	case IMSG_VMDOP_PRIV_IFADDR:
187		memset(&ifra, 0, sizeof(ifra));
188
189		if (vfr.vfr_addr.ss_family != AF_INET ||
190		    vfr.vfr_addr.ss_family != vfr.vfr_mask.ss_family)
191			fatalx("%s: invalid address family", __func__);
192
193		/* Set the interface address */
194		strlcpy(ifra.ifra_name, vfr.vfr_name, sizeof(ifra.ifra_name));
195
196		ifra.ifra_addr.sa_len =
197		    ifra.ifra_mask.sa_len =
198		    sizeof(struct sockaddr_in);
199
200		memcpy(&ifra.ifra_addr, &vfr.vfr_addr,
201		    ifra.ifra_addr.sa_len);
202		memcpy(&ifra.ifra_mask, &vfr.vfr_mask,
203		    ifra.ifra_mask.sa_len);
204
205		if (ioctl(env->vmd_fd, SIOCAIFADDR, &ifra) == -1)
206			log_warn("SIOCAIFADDR");
207		break;
208	case IMSG_VMDOP_PRIV_IFADDR6:
209		memset(&ifar, 0, sizeof(ifar));
210		memset(&in6_ifra, 0, sizeof(in6_ifra));
211
212		if (vfr.vfr_addr.ss_family != AF_INET6 ||
213		    vfr.vfr_addr.ss_family != vfr.vfr_mask.ss_family)
214			fatalx("%s: invalid address family", __func__);
215
216		/* First enable IPv6 on this interface */
217		strlcpy(ifar.ifar_name, vfr.vfr_name,
218		    sizeof(ifar.ifar_name));
219		ifar.ifar_af = AF_INET6;
220		if (ioctl(env->vmd_fd, SIOCIFAFATTACH, (caddr_t)&ifar) == -1)
221			log_warn("SIOCIFAFATTACH");
222
223		/* Set the interface address */
224		strlcpy(in6_ifra.ifra_name, vfr.vfr_name,
225		    sizeof(in6_ifra.ifra_name));
226
227		in6_ifra.ifra_addr.sin6_len =
228		    in6_ifra.ifra_prefixmask.sin6_len =
229		    sizeof(struct sockaddr_in6);
230
231		memcpy(&in6_ifra.ifra_addr, &vfr.vfr_addr,
232		    in6_ifra.ifra_addr.sin6_len);
233		memcpy(&in6_ifra.ifra_prefixmask, &vfr.vfr_mask,
234		    in6_ifra.ifra_prefixmask.sin6_len);
235		in6_ifra.ifra_prefixmask.sin6_scope_id = 0;
236
237		in6_ifra.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME;
238		in6_ifra.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME;
239
240		if (ioctl(env->vmd_fd6, SIOCDIFADDR_IN6, &in6_ifra) == -1 &&
241		    errno != EADDRNOTAVAIL)
242			log_warn("SIOCDIFADDR_IN6");
243
244		if (ioctl(env->vmd_fd6, SIOCAIFADDR_IN6, &in6_ifra) == -1)
245			log_warn("SIOCAIFADDR_IN6");
246		break;
247	case IMSG_VMDOP_CONFIG:
248		config_getconfig(env, imsg);
249		break;
250	case IMSG_CTL_RESET:
251		config_getreset(env, imsg);
252		break;
253	default:
254		return (-1);
255	}
256
257	return (0);
258}
259
260int
261priv_getiftype(char *ifname, char *type, unsigned int *unitptr)
262{
263	const char	*errstr;
264	size_t		 span;
265	unsigned int	 unit;
266
267	/* Extract the name part */
268	span = strcspn(ifname, "0123456789");
269	if (span == 0 || span >= strlen(ifname) || span >= (IF_NAMESIZE - 1))
270		return (-1);
271	memcpy(type, ifname, span);
272	type[span] = 0;
273
274	/* Now parse the unit (we don't strictly validate the format here) */
275	unit = strtonum(ifname + span, 0, UINT_MAX, &errstr);
276	if (errstr != NULL)
277		return (-1);
278	if (unitptr != NULL)
279		*unitptr = unit;
280
281	return (0);
282}
283
284int
285priv_findname(const char *name, const char **names)
286{
287	unsigned int	 i;
288
289	for (i = 0; names[i] != NULL; i++) {
290		if (strcmp(name, names[i]) == 0)
291			return (0);
292	}
293
294	return (-1);
295}
296
297int
298priv_validgroup(const char *name)
299{
300	if (strlen(name) >= IF_NAMESIZE)
301		return (-1);
302	/* Group can not end with a digit */
303	if (name[0] && isdigit(name[strlen(name) - 1]))
304		return (-1);
305	return (0);
306}
307
308/*
309 * Called from the Parent process to setup vm interface(s)
310 * - ensure the interface has the description set (tracking purposes)
311 * - if interface is to be attached to a switch, attach it
312 * - check if rdomain is set on interface and switch
313 *   - if interface only or both, use interface rdomain
314 *   - if switch only, use switch rdomain
315 * - check if group is set on interface and switch
316 *   - if interface, add it
317 *   - if switch, add it
318 * - ensure the interface is up/down
319 * - if local interface, set address
320 */
321int
322vm_priv_ifconfig(struct privsep *ps, struct vmd_vm *vm)
323{
324	char			 name[64];
325	struct vmd		*env = ps->ps_env;
326	struct vm_create_params	*vcp = &vm->vm_params.vmc_params;
327	struct vmd_if		*vif;
328	struct vmd_switch	*vsw;
329	unsigned int		 i;
330	struct vmop_ifreq	 vfr, vfbr;
331	struct sockaddr_in	*sin4;
332	struct sockaddr_in6	*sin6;
333
334	for (i = 0; i < VMM_MAX_NICS_PER_VM; i++) {
335		vif = &vm->vm_ifs[i];
336
337		if (vif->vif_name == NULL)
338			break;
339
340		memset(&vfr, 0, sizeof(vfr));
341		if (strlcpy(vfr.vfr_name, vif->vif_name,
342		    sizeof(vfr.vfr_name)) >= sizeof(vfr.vfr_name))
343			return (-1);
344
345		/* Description can be truncated */
346		(void)snprintf(vfr.vfr_value, sizeof(vfr.vfr_value),
347		    "vm%u-if%u-%s", vm->vm_vmid, i, vcp->vcp_name);
348
349		log_debug("%s: interface %s description %s", __func__,
350		    vfr.vfr_name, vfr.vfr_value);
351
352		proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFDESCR,
353		    &vfr, sizeof(vfr));
354
355		/* set default rdomain */
356		vfr.vfr_id = getrtable();
357
358		vsw = switch_getbyname(vif->vif_switch);
359
360		/* Check if switch should exist */
361		if (vsw == NULL && vif->vif_switch != NULL)
362			log_warnx("switch \"%s\" not found", vif->vif_switch);
363
364		/* Add interface to switch and set proper rdomain */
365		if (vsw != NULL) {
366			memset(&vfbr, 0, sizeof(vfbr));
367
368			if (strlcpy(vfbr.vfr_name, vsw->sw_ifname,
369			    sizeof(vfbr.vfr_name)) >= sizeof(vfbr.vfr_name))
370				return (-1);
371			if (strlcpy(vfbr.vfr_value, vif->vif_name,
372			    sizeof(vfbr.vfr_value)) >= sizeof(vfbr.vfr_value))
373				return (-1);
374
375			log_debug("%s: switch \"%s\" interface %s add %s",
376			    __func__, vsw->sw_name, vfbr.vfr_name,
377			    vfbr.vfr_value);
378
379			proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFADD,
380			    &vfbr, sizeof(vfbr));
381
382			/* Check rdomain properties */
383			if (vif->vif_flags & VMIFF_RDOMAIN)
384				vfr.vfr_id = vif->vif_rdomain;
385			else if (vsw->sw_flags & VMIFF_RDOMAIN)
386				vfr.vfr_id = vsw->sw_rdomain;
387		} else {
388			/* No switch to attach case */
389			if (vif->vif_flags & VMIFF_RDOMAIN)
390				vfr.vfr_id = vif->vif_rdomain;
391		}
392
393		/* Set rdomain on interface */
394		if (vfr.vfr_id != 0)
395			log_debug("%s: interface %s rdomain %u", __func__,
396			    vfr.vfr_name, vfr.vfr_id);
397
398		proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFRDOMAIN,
399		    &vfr, sizeof(vfr));
400
401		/* First group is defined per-interface */
402		if (vif->vif_group) {
403			if (strlcpy(vfr.vfr_value, vif->vif_group,
404			    sizeof(vfr.vfr_value)) >= sizeof(vfr.vfr_value))
405				return (-1);
406
407			log_debug("%s: interface %s group %s", __func__,
408			    vfr.vfr_name, vfr.vfr_value);
409
410			proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFGROUP,
411			    &vfr, sizeof(vfr));
412		}
413
414		/* The second group is defined per-switch */
415		if (vsw != NULL && vsw->sw_group != NULL) {
416			if (strlcpy(vfr.vfr_value, vsw->sw_group,
417			    sizeof(vfr.vfr_value)) >= sizeof(vfr.vfr_value))
418				return (-1);
419
420			log_debug("%s: interface %s group %s switch \"%s\"",
421			    __func__, vfr.vfr_name, vfr.vfr_value,
422			    vsw->sw_name);
423
424			proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFGROUP,
425			    &vfr, sizeof(vfr));
426		}
427
428		/* Set the new interface status to up or down */
429		proc_compose(ps, PROC_PRIV, (vif->vif_flags & VMIFF_UP) ?
430		    IMSG_VMDOP_PRIV_IFUP : IMSG_VMDOP_PRIV_IFDOWN,
431		    &vfr, sizeof(vfr));
432
433		/* Set interface address if it is a local interface */
434		if (vm->vm_params.vmc_ifflags[i] & VMIFF_LOCAL) {
435			memset(&vfr.vfr_mask, 0, sizeof(vfr.vfr_mask));
436			memset(&vfr.vfr_addr, 0, sizeof(vfr.vfr_addr));
437
438			/* local IPv4 address with a /31 mask */
439			sin4 = (struct sockaddr_in *)&vfr.vfr_mask;
440			sin4->sin_family = AF_INET;
441			sin4->sin_len = sizeof(*sin4);
442			sin4->sin_addr.s_addr = htonl(0xfffffffe);
443
444			sin4 = (struct sockaddr_in *)&vfr.vfr_addr;
445			sin4->sin_family = AF_INET;
446			sin4->sin_len = sizeof(*sin4);
447			if ((sin4->sin_addr.s_addr =
448			    vm_priv_addr(&env->vmd_cfg,
449			    vm->vm_vmid, i, 0)) == 0)
450				return (-1);
451
452			inet_ntop(AF_INET, &sin4->sin_addr,
453			    name, sizeof(name));
454			log_debug("%s: interface %s address %s/31",
455			    __func__, vfr.vfr_name, name);
456
457			proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFADDR,
458			    &vfr, sizeof(vfr));
459		}
460		if ((vm->vm_params.vmc_ifflags[i] & VMIFF_LOCAL) &&
461		    (env->vmd_cfg.cfg_flags & VMD_CFG_INET6)) {
462			memset(&vfr.vfr_mask, 0, sizeof(vfr.vfr_mask));
463			memset(&vfr.vfr_addr, 0, sizeof(vfr.vfr_addr));
464
465			/* local IPv6 address with a /96 mask */
466			sin6 = ss2sin6(&vfr.vfr_mask);
467			sin6->sin6_family = AF_INET6;
468			sin6->sin6_len = sizeof(*sin6);
469			memset(&sin6->sin6_addr.s6_addr[0], 0xff, 12);
470			memset(&sin6->sin6_addr.s6_addr[12], 0, 4);
471
472			sin6 = ss2sin6(&vfr.vfr_addr);
473			sin6->sin6_family = AF_INET6;
474			sin6->sin6_len = sizeof(*sin6);
475			if (vm_priv_addr6(&env->vmd_cfg,
476			    vm->vm_vmid, i, 0, &sin6->sin6_addr) == -1)
477				return (-1);
478
479			inet_ntop(AF_INET6, &sin6->sin6_addr,
480			    name, sizeof(name));
481			log_debug("%s: interface %s address %s/96",
482			    __func__, vfr.vfr_name, name);
483
484			proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFADDR6,
485			    &vfr, sizeof(vfr));
486		}
487	}
488
489	return (0);
490}
491
492/*
493 * Called from the Parent process to setup underlying switch interface
494 * - ensure the interface exists
495 * - ensure the interface has the correct rdomain set
496 * - ensure the interface has the description set (tracking purposes)
497 * - ensure the interface is up/down
498 */
499int
500vm_priv_brconfig(struct privsep *ps, struct vmd_switch *vsw)
501{
502	struct vmop_ifreq	 vfr;
503
504	memset(&vfr, 0, sizeof(vfr));
505
506	if (strlcpy(vfr.vfr_name, vsw->sw_ifname,
507	    sizeof(vfr.vfr_name)) >= sizeof(vfr.vfr_name))
508		return (-1);
509
510	/* ensure bridge/switch exists */
511	proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFEXISTS,
512	    &vfr, sizeof(vfr));
513
514	/* Use the configured rdomain or get it from the process */
515	if (vsw->sw_flags & VMIFF_RDOMAIN)
516		vfr.vfr_id = vsw->sw_rdomain;
517	else
518		vfr.vfr_id = getrtable();
519	if (vfr.vfr_id != 0)
520		log_debug("%s: interface %s rdomain %u", __func__,
521		    vfr.vfr_name, vfr.vfr_id);
522
523	/* ensure switch has the correct rodmain */
524	proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFRDOMAIN,
525	    &vfr, sizeof(vfr));
526
527	/* Description can be truncated */
528	(void)snprintf(vfr.vfr_value, sizeof(vfr.vfr_value),
529	    "switch%u-%s", vsw->sw_id, vsw->sw_name);
530
531	log_debug("%s: interface %s description %s", __func__,
532	    vfr.vfr_name, vfr.vfr_value);
533
534	proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFDESCR,
535	    &vfr, sizeof(vfr));
536
537	/* Set the new interface status to up or down */
538	proc_compose(ps, PROC_PRIV, (vsw->sw_flags & VMIFF_UP) ?
539	    IMSG_VMDOP_PRIV_IFUP : IMSG_VMDOP_PRIV_IFDOWN,
540	    &vfr, sizeof(vfr));
541
542	vsw->sw_running = 1;
543	return (0);
544}
545
546uint32_t
547vm_priv_addr(struct vmd_config *cfg, uint32_t vmid, int idx, int isvm)
548{
549	struct address		*h = &cfg->cfg_localprefix;
550	in_addr_t		 prefix, mask, addr;
551
552	/*
553	 * 1. Set the address prefix and mask, 100.64.0.0/10 by default.
554	 */
555	if (h->ss.ss_family != AF_INET ||
556	    h->prefixlen < 0 || h->prefixlen > 32)
557		fatal("local prefix");
558	prefix = ss2sin(&h->ss)->sin_addr.s_addr;
559	mask = prefixlen2mask(h->prefixlen);
560
561	/* 2. Encode the VM ID as a per-VM subnet range N, 100.64.N.0/24. */
562	addr = vmid << 8;
563
564	/*
565	 * 3. Assign a /31 subnet M per VM interface, 100.64.N.M/31.
566	 * Each subnet contains exactly two IP addresses; skip the
567	 * first subnet to avoid a gateway address ending with .0.
568	 */
569	addr |= (idx + 1) * 2;
570
571	/* 4. Use the first address for the gateway, the second for the VM. */
572	if (isvm)
573		addr++;
574
575	/* 5. Convert to network byte order and add the prefix. */
576	addr = htonl(addr) | prefix;
577
578	/*
579	 * Validate the results:
580	 * - the address should not exceed the prefix (eg. VM ID to high).
581	 * - up to 126 interfaces can be encoded per VM.
582	 */
583	if (prefix != (addr & mask) || idx >= 0x7f) {
584		log_warnx("%s: dhcp address range exceeded,"
585		    " vm id %u interface %d", __func__, vmid, idx);
586		return (0);
587	}
588
589	return (addr);
590}
591
592int
593vm_priv_addr6(struct vmd_config *cfg, uint32_t vmid,
594    int idx, int isvm, struct in6_addr *in6_addr)
595{
596	struct address		*h = &cfg->cfg_localprefix6;
597	struct in6_addr		 addr, mask;
598	uint32_t		 addr4;
599
600	/* 1. Set the address prefix and mask, fd00::/8 by default. */
601	if (h->ss.ss_family != AF_INET6 ||
602	    h->prefixlen < 0 || h->prefixlen > 128)
603		fatal("local prefix6");
604	addr = ss2sin6(&h->ss)->sin6_addr;
605	prefixlen2mask6(h->prefixlen, &mask);
606
607	/* 2. Encode the VM IPv4 address as subnet, fd00::NN:NN:0:0/96. */
608	if ((addr4 = vm_priv_addr(cfg, vmid, idx, 1)) == 0)
609		return (0);
610	memcpy(&addr.s6_addr[8], &addr4, sizeof(addr4));
611
612	/*
613	 * 3. Set the last octet to 1 (host) or 2 (VM).
614	 * The latter is currently not used inside vmd as we don't
615	 * answer rtsol requests ourselves.
616	 */
617	if (!isvm)
618		addr.s6_addr[15] = 1;
619	else
620		addr.s6_addr[15] = 2;
621
622	memcpy(in6_addr, &addr, sizeof(*in6_addr));
623
624	return (0);
625}
626