priv.c revision 1.24
1/*	$OpenBSD: priv.c,v 1.24 2024/01/18 14:49:59 claudio 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/types.h>
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", "bridge", "veb", 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	struct vmop_addr_req	 vareq;
95	struct vmop_addr_result	 varesult;
96	char			 type[IF_NAMESIZE];
97	int			 ifd;
98
99	switch (imsg->hdr.type) {
100	case IMSG_VMDOP_PRIV_IFDESCR:
101	case IMSG_VMDOP_PRIV_IFRDOMAIN:
102	case IMSG_VMDOP_PRIV_IFEXISTS:
103	case IMSG_VMDOP_PRIV_IFADD:
104	case IMSG_VMDOP_PRIV_IFUP:
105	case IMSG_VMDOP_PRIV_IFDOWN:
106	case IMSG_VMDOP_PRIV_IFGROUP:
107	case IMSG_VMDOP_PRIV_IFADDR:
108	case IMSG_VMDOP_PRIV_IFADDR6:
109		IMSG_SIZE_CHECK(imsg, &vfr);
110		memcpy(&vfr, imsg->data, sizeof(vfr));
111
112		/* We should not get malicious requests from the parent */
113		if (priv_getiftype(vfr.vfr_name, type, NULL) == -1 ||
114		    priv_findname(type, desct) == -1)
115			fatalx("%s: rejected priv operation on interface: %s",
116			    __func__, vfr.vfr_name);
117		break;
118	case IMSG_VMDOP_CONFIG:
119	case IMSG_CTL_RESET:
120	case IMSG_VMDOP_PRIV_GET_ADDR:
121		break;
122	default:
123		return (-1);
124	}
125
126	switch (imsg->hdr.type) {
127	case IMSG_VMDOP_PRIV_IFDESCR:
128		/* Set the interface description */
129		strlcpy(ifr.ifr_name, vfr.vfr_name, sizeof(ifr.ifr_name));
130		ifr.ifr_data = (caddr_t)vfr.vfr_value;
131		if (ioctl(env->vmd_fd, SIOCSIFDESCR, &ifr) == -1)
132			log_warn("SIOCSIFDESCR");
133		break;
134	case IMSG_VMDOP_PRIV_IFRDOMAIN:
135		strlcpy(ifr.ifr_name, vfr.vfr_name, sizeof(ifr.ifr_name));
136		ifr.ifr_rdomainid = vfr.vfr_id;
137		if (ioctl(env->vmd_fd, SIOCSIFRDOMAIN, &ifr) == -1)
138			log_warn("SIOCSIFRDOMAIN");
139		break;
140	case IMSG_VMDOP_PRIV_IFADD:
141		if (priv_getiftype(vfr.vfr_value, type, NULL) == -1)
142			fatalx("%s: rejected to add interface: %s",
143			    __func__, vfr.vfr_value);
144
145		/* Attach the device to the bridge */
146		strlcpy(ifbr.ifbr_name, vfr.vfr_name,
147		    sizeof(ifbr.ifbr_name));
148		strlcpy(ifbr.ifbr_ifsname, vfr.vfr_value,
149		    sizeof(ifbr.ifbr_ifsname));
150		if (ioctl(env->vmd_fd, SIOCBRDGADD, &ifbr) == -1 &&
151		    errno != EEXIST)
152			log_warn("SIOCBRDGADD");
153		break;
154	case IMSG_VMDOP_PRIV_IFEXISTS:
155		/* Determine if bridge exists */
156		strlcpy(ifr.ifr_name, vfr.vfr_name, sizeof(ifr.ifr_name));
157		if (ioctl(env->vmd_fd, SIOCGIFFLAGS, &ifr) == -1)
158			fatalx("%s: bridge \"%s\" does not exist",
159			    __func__, vfr.vfr_name);
160		break;
161	case IMSG_VMDOP_PRIV_IFUP:
162	case IMSG_VMDOP_PRIV_IFDOWN:
163		/* Set the interface status */
164		strlcpy(ifr.ifr_name, vfr.vfr_name, sizeof(ifr.ifr_name));
165		if (ioctl(env->vmd_fd, SIOCGIFFLAGS, &ifr) == -1) {
166			log_warn("SIOCGIFFLAGS");
167			break;
168		}
169		if (imsg->hdr.type == IMSG_VMDOP_PRIV_IFUP)
170			ifr.ifr_flags |= IFF_UP;
171		else
172			ifr.ifr_flags &= ~IFF_UP;
173		if (ioctl(env->vmd_fd, SIOCSIFFLAGS, &ifr) == -1)
174			log_warn("SIOCSIFFLAGS");
175		break;
176	case IMSG_VMDOP_PRIV_IFGROUP:
177		if (priv_validgroup(vfr.vfr_value) == -1)
178			fatalx("%s: invalid group name", __func__);
179
180		if (strlcpy(ifgr.ifgr_name, vfr.vfr_name,
181		    sizeof(ifgr.ifgr_name)) >= sizeof(ifgr.ifgr_name) ||
182		    strlcpy(ifgr.ifgr_group, vfr.vfr_value,
183		    sizeof(ifgr.ifgr_group)) >= sizeof(ifgr.ifgr_group))
184			fatalx("%s: group name too long", __func__);
185
186		if (ioctl(env->vmd_fd, SIOCAIFGROUP, &ifgr) == -1 &&
187		    errno != EEXIST)
188			log_warn("SIOCAIFGROUP");
189		break;
190	case IMSG_VMDOP_PRIV_IFADDR:
191		memset(&ifra, 0, sizeof(ifra));
192
193		if (vfr.vfr_addr.ss_family != AF_INET ||
194		    vfr.vfr_addr.ss_family != vfr.vfr_mask.ss_family)
195			fatalx("%s: invalid address family", __func__);
196
197		/* Set the interface address */
198		strlcpy(ifra.ifra_name, vfr.vfr_name, sizeof(ifra.ifra_name));
199
200		ifra.ifra_addr.sa_len =
201		    ifra.ifra_mask.sa_len =
202		    sizeof(struct sockaddr_in);
203
204		memcpy(&ifra.ifra_addr, &vfr.vfr_addr,
205		    ifra.ifra_addr.sa_len);
206		memcpy(&ifra.ifra_mask, &vfr.vfr_mask,
207		    ifra.ifra_mask.sa_len);
208
209		if (ioctl(env->vmd_fd, SIOCAIFADDR, &ifra) == -1)
210			log_warn("SIOCAIFADDR");
211		break;
212	case IMSG_VMDOP_PRIV_IFADDR6:
213		memset(&ifar, 0, sizeof(ifar));
214		memset(&in6_ifra, 0, sizeof(in6_ifra));
215
216		if (vfr.vfr_addr.ss_family != AF_INET6 ||
217		    vfr.vfr_addr.ss_family != vfr.vfr_mask.ss_family)
218			fatalx("%s: invalid address family", __func__);
219
220		/* First enable IPv6 on this interface */
221		strlcpy(ifar.ifar_name, vfr.vfr_name,
222		    sizeof(ifar.ifar_name));
223		ifar.ifar_af = AF_INET6;
224		if (ioctl(env->vmd_fd, SIOCIFAFATTACH, (caddr_t)&ifar) == -1)
225			log_warn("SIOCIFAFATTACH");
226
227		/* Set the interface address */
228		strlcpy(in6_ifra.ifra_name, vfr.vfr_name,
229		    sizeof(in6_ifra.ifra_name));
230
231		in6_ifra.ifra_addr.sin6_len =
232		    in6_ifra.ifra_prefixmask.sin6_len =
233		    sizeof(struct sockaddr_in6);
234
235		memcpy(&in6_ifra.ifra_addr, &vfr.vfr_addr,
236		    in6_ifra.ifra_addr.sin6_len);
237		memcpy(&in6_ifra.ifra_prefixmask, &vfr.vfr_mask,
238		    in6_ifra.ifra_prefixmask.sin6_len);
239		in6_ifra.ifra_prefixmask.sin6_scope_id = 0;
240
241		in6_ifra.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME;
242		in6_ifra.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME;
243
244		if (ioctl(env->vmd_fd6, SIOCDIFADDR_IN6, &in6_ifra) == -1 &&
245		    errno != EADDRNOTAVAIL)
246			log_warn("SIOCDIFADDR_IN6");
247
248		if (ioctl(env->vmd_fd6, SIOCAIFADDR_IN6, &in6_ifra) == -1)
249			log_warn("SIOCAIFADDR_IN6");
250		break;
251	case IMSG_VMDOP_PRIV_GET_ADDR:
252		IMSG_SIZE_CHECK(imsg, &vareq);
253		memcpy(&vareq, imsg->data, sizeof(vareq));
254
255		varesult.var_vmid = vareq.var_vmid;
256		varesult.var_nic_idx = vareq.var_nic_idx;
257
258		ifd = imsg_get_fd(imsg);
259		/* resolve lladdr for the tap(4) and send back to parent */
260		if (ioctl(ifd, SIOCGIFADDR, &varesult.var_addr) != 0)
261			log_warn("SIOCGIFADDR");
262		else
263			proc_compose_imsg(ps, PROC_PARENT, -1,
264			    IMSG_VMDOP_PRIV_GET_ADDR_RESPONSE, imsg->hdr.peerid,
265			    -1, &varesult, sizeof(varesult));
266		close(ifd);
267		break;
268	case IMSG_VMDOP_CONFIG:
269		config_getconfig(env, imsg);
270		break;
271	case IMSG_CTL_RESET:
272		config_getreset(env, imsg);
273		break;
274	default:
275		return (-1);
276	}
277
278	return (0);
279}
280
281int
282priv_getiftype(char *ifname, char *type, unsigned int *unitptr)
283{
284	const char	*errstr;
285	size_t		 span;
286	unsigned int	 unit;
287
288	/* Extract the name part */
289	span = strcspn(ifname, "0123456789");
290	if (span == 0 || span >= strlen(ifname) || span >= (IF_NAMESIZE - 1))
291		return (-1);
292	memcpy(type, ifname, span);
293	type[span] = 0;
294
295	/* Now parse the unit (we don't strictly validate the format here) */
296	unit = strtonum(ifname + span, 0, UINT_MAX, &errstr);
297	if (errstr != NULL)
298		return (-1);
299	if (unitptr != NULL)
300		*unitptr = unit;
301
302	return (0);
303}
304
305int
306priv_findname(const char *name, const char **names)
307{
308	unsigned int	 i;
309
310	for (i = 0; names[i] != NULL; i++) {
311		if (strcmp(name, names[i]) == 0)
312			return (0);
313	}
314
315	return (-1);
316}
317
318int
319priv_validgroup(const char *name)
320{
321	const size_t len = strnlen(name, IF_NAMESIZE);
322
323	if (len == IF_NAMESIZE)
324		return (-1);
325	/* Group can not end with a digit */
326	if (len > 0 && isdigit((unsigned char)name[len - 1]))
327		return (-1);
328	return (0);
329}
330
331/*
332 * Called from the Parent process to setup vm interface(s)
333 * - ensure the interface has the description set (tracking purposes)
334 * - if interface is to be attached to a switch, attach it
335 * - check if rdomain is set on interface and switch
336 *   - if interface only or both, use interface rdomain
337 *   - if switch only, use switch rdomain
338 * - check if group is set on interface and switch
339 *   - if interface, add it
340 *   - if switch, add it
341 * - ensure the interface is up/down
342 * - if local interface, set address
343 */
344int
345vm_priv_ifconfig(struct privsep *ps, struct vmd_vm *vm)
346{
347	char			 name[64];
348	struct vmd		*env = ps->ps_env;
349	struct vm_create_params	*vcp = &vm->vm_params.vmc_params;
350	struct vmd_if		*vif;
351	struct vmd_switch	*vsw;
352	unsigned int		 i;
353	struct vmop_ifreq	 vfr, vfbr;
354	struct sockaddr_in	*sin4;
355	struct sockaddr_in6	*sin6;
356
357	for (i = 0; i < VM_MAX_NICS_PER_VM; i++) {
358		vif = &vm->vm_ifs[i];
359
360		if (vif->vif_name == NULL)
361			break;
362
363		memset(&vfr, 0, sizeof(vfr));
364		if (strlcpy(vfr.vfr_name, vif->vif_name,
365		    sizeof(vfr.vfr_name)) >= sizeof(vfr.vfr_name))
366			return (-1);
367
368		/* Description can be truncated */
369		(void)snprintf(vfr.vfr_value, sizeof(vfr.vfr_value),
370		    "vm%u-if%u-%s", vm->vm_vmid, i, vcp->vcp_name);
371
372		log_debug("%s: interface %s description %s", __func__,
373		    vfr.vfr_name, vfr.vfr_value);
374
375		proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFDESCR,
376		    &vfr, sizeof(vfr));
377
378		/* set default rdomain */
379		vfr.vfr_id = getrtable();
380
381		vsw = switch_getbyname(vif->vif_switch);
382
383		/* Check if switch should exist */
384		if (vsw == NULL && vif->vif_switch != NULL)
385			log_warnx("switch \"%s\" not found", vif->vif_switch);
386
387		/* Add interface to switch and set proper rdomain */
388		if (vsw != NULL) {
389			memset(&vfbr, 0, sizeof(vfbr));
390
391			if (strlcpy(vfbr.vfr_name, vsw->sw_ifname,
392			    sizeof(vfbr.vfr_name)) >= sizeof(vfbr.vfr_name))
393				return (-1);
394			if (strlcpy(vfbr.vfr_value, vif->vif_name,
395			    sizeof(vfbr.vfr_value)) >= sizeof(vfbr.vfr_value))
396				return (-1);
397
398			log_debug("%s: switch \"%s\" interface %s add %s",
399			    __func__, vsw->sw_name, vfbr.vfr_name,
400			    vfbr.vfr_value);
401
402			proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFADD,
403			    &vfbr, sizeof(vfbr));
404
405			/* Check rdomain properties */
406			if (vif->vif_flags & VMIFF_RDOMAIN)
407				vfr.vfr_id = vif->vif_rdomain;
408			else if (vsw->sw_flags & VMIFF_RDOMAIN)
409				vfr.vfr_id = vsw->sw_rdomain;
410		} else {
411			/* No switch to attach case */
412			if (vif->vif_flags & VMIFF_RDOMAIN)
413				vfr.vfr_id = vif->vif_rdomain;
414		}
415
416		/* Set rdomain on interface */
417		if (vfr.vfr_id != 0)
418			log_debug("%s: interface %s rdomain %u", __func__,
419			    vfr.vfr_name, vfr.vfr_id);
420
421		proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFRDOMAIN,
422		    &vfr, sizeof(vfr));
423
424		/* First group is defined per-interface */
425		if (vif->vif_group) {
426			if (strlcpy(vfr.vfr_value, vif->vif_group,
427			    sizeof(vfr.vfr_value)) >= sizeof(vfr.vfr_value))
428				return (-1);
429
430			log_debug("%s: interface %s group %s", __func__,
431			    vfr.vfr_name, vfr.vfr_value);
432
433			proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFGROUP,
434			    &vfr, sizeof(vfr));
435		}
436
437		/* The second group is defined per-switch */
438		if (vsw != NULL && vsw->sw_group != NULL) {
439			if (strlcpy(vfr.vfr_value, vsw->sw_group,
440			    sizeof(vfr.vfr_value)) >= sizeof(vfr.vfr_value))
441				return (-1);
442
443			log_debug("%s: interface %s group %s switch \"%s\"",
444			    __func__, vfr.vfr_name, vfr.vfr_value,
445			    vsw->sw_name);
446
447			proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFGROUP,
448			    &vfr, sizeof(vfr));
449		}
450
451		/* Set the new interface status to up or down */
452		proc_compose(ps, PROC_PRIV, (vif->vif_flags & VMIFF_UP) ?
453		    IMSG_VMDOP_PRIV_IFUP : IMSG_VMDOP_PRIV_IFDOWN,
454		    &vfr, sizeof(vfr));
455
456		/* Set interface address if it is a local interface */
457		if (vm->vm_params.vmc_ifflags[i] & VMIFF_LOCAL) {
458			memset(&vfr.vfr_mask, 0, sizeof(vfr.vfr_mask));
459			memset(&vfr.vfr_addr, 0, sizeof(vfr.vfr_addr));
460
461			/* local IPv4 address with a /31 mask */
462			sin4 = (struct sockaddr_in *)&vfr.vfr_mask;
463			sin4->sin_family = AF_INET;
464			sin4->sin_len = sizeof(*sin4);
465			sin4->sin_addr.s_addr = htonl(0xfffffffe);
466
467			sin4 = (struct sockaddr_in *)&vfr.vfr_addr;
468			sin4->sin_family = AF_INET;
469			sin4->sin_len = sizeof(*sin4);
470			if ((sin4->sin_addr.s_addr =
471			    vm_priv_addr(&env->vmd_cfg.cfg_localprefix,
472			    vm->vm_vmid, i, 0)) == 0)
473				return (-1);
474
475			inet_ntop(AF_INET, &sin4->sin_addr,
476			    name, sizeof(name));
477			log_debug("%s: interface %s address %s/31",
478			    __func__, vfr.vfr_name, name);
479
480			proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFADDR,
481			    &vfr, sizeof(vfr));
482		}
483		if ((vm->vm_params.vmc_ifflags[i] & VMIFF_LOCAL) &&
484		    (env->vmd_cfg.cfg_flags & VMD_CFG_INET6)) {
485			memset(&vfr.vfr_mask, 0, sizeof(vfr.vfr_mask));
486			memset(&vfr.vfr_addr, 0, sizeof(vfr.vfr_addr));
487
488			/* local IPv6 address with a /96 mask */
489			sin6 = ss2sin6(&vfr.vfr_mask);
490			sin6->sin6_family = AF_INET6;
491			sin6->sin6_len = sizeof(*sin6);
492			memset(&sin6->sin6_addr.s6_addr[0], 0xff, 12);
493			memset(&sin6->sin6_addr.s6_addr[12], 0, 4);
494
495			sin6 = ss2sin6(&vfr.vfr_addr);
496			sin6->sin6_family = AF_INET6;
497			sin6->sin6_len = sizeof(*sin6);
498			if (vm_priv_addr6(&env->vmd_cfg.cfg_localprefix,
499			    vm->vm_vmid, i, 0, &sin6->sin6_addr) == -1)
500				return (-1);
501
502			inet_ntop(AF_INET6, &sin6->sin6_addr,
503			    name, sizeof(name));
504			log_debug("%s: interface %s address %s/96",
505			    __func__, vfr.vfr_name, name);
506
507			proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFADDR6,
508			    &vfr, sizeof(vfr));
509		}
510	}
511
512	return (0);
513}
514
515/*
516 * Called from the Parent process to setup underlying switch interface
517 * - ensure the interface exists
518 * - ensure the interface has the correct rdomain set
519 * - ensure the interface has the description set (tracking purposes)
520 * - ensure the interface is up/down
521 */
522int
523vm_priv_brconfig(struct privsep *ps, struct vmd_switch *vsw)
524{
525	struct vmop_ifreq	 vfr;
526
527	memset(&vfr, 0, sizeof(vfr));
528
529	if (strlcpy(vfr.vfr_name, vsw->sw_ifname,
530	    sizeof(vfr.vfr_name)) >= sizeof(vfr.vfr_name))
531		return (-1);
532
533	/* ensure bridge exists */
534	proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFEXISTS,
535	    &vfr, sizeof(vfr));
536
537	/* Use the configured rdomain or get it from the process */
538	if (vsw->sw_flags & VMIFF_RDOMAIN)
539		vfr.vfr_id = vsw->sw_rdomain;
540	else
541		vfr.vfr_id = getrtable();
542	if (vfr.vfr_id != 0)
543		log_debug("%s: interface %s rdomain %u", __func__,
544		    vfr.vfr_name, vfr.vfr_id);
545
546	/* ensure bridge has the correct rdomain */
547	proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFRDOMAIN,
548	    &vfr, sizeof(vfr));
549
550	/* Description can be truncated */
551	(void)snprintf(vfr.vfr_value, sizeof(vfr.vfr_value),
552	    "switch%u-%s", vsw->sw_id, vsw->sw_name);
553
554	log_debug("%s: interface %s description %s", __func__,
555	    vfr.vfr_name, vfr.vfr_value);
556
557	proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFDESCR,
558	    &vfr, sizeof(vfr));
559
560	/* Set the new interface status to up or down */
561	proc_compose(ps, PROC_PRIV, (vsw->sw_flags & VMIFF_UP) ?
562	    IMSG_VMDOP_PRIV_IFUP : IMSG_VMDOP_PRIV_IFDOWN,
563	    &vfr, sizeof(vfr));
564
565	vsw->sw_running = 1;
566	return (0);
567}
568
569uint32_t
570vm_priv_addr(struct local_prefix *p, uint32_t vmid, int idx, int isvm)
571{
572	in_addr_t		 addr;
573
574	/* Encode the VM ID as a per-VM subnet range N, 100.64.N.0/24. */
575	addr = vmid << 8;
576
577	/*
578	 * Assign a /31 subnet M per VM interface, 100.64.N.M/31.
579	 * Each subnet contains exactly two IP addresses; skip the
580	 * first subnet to avoid a gateway address ending with .0.
581	 */
582	addr |= (idx + 1) * 2;
583
584	/* Use the first address for the gateway, the second for the VM. */
585	if (isvm)
586		addr++;
587
588	/* Convert to network byte order and add the prefix. */
589	addr = htonl(addr) | p->lp_in.s_addr;
590
591	/*
592	 * Validate the results:
593	 * - the address should not exceed the prefix (eg. VM ID to high).
594	 * - up to 126 interfaces can be encoded per VM.
595	 */
596	if (p->lp_in.s_addr != (addr & p->lp_mask.s_addr) || idx >= 0x7f) {
597		log_warnx("%s: dhcp address range exceeded,"
598		    " vm id %u interface %d", __func__, vmid, idx);
599		return (0);
600	}
601
602	return (addr);
603}
604
605int
606vm_priv_addr6(struct local_prefix *p, uint32_t vmid, int idx, int isvm,
607    struct in6_addr *out)
608{
609	struct in6_addr		 addr;
610	in_addr_t		 addr4;
611
612	/* Start with the IPv6 prefix. */
613	memcpy(&addr, &p->lp_in6, sizeof(addr));
614
615	/* Encode the VM IPv4 address as subnet, fd00::NN:NN:0:0/96. */
616	if ((addr4 = vm_priv_addr(p, vmid, idx, 1)) == 0)
617		return (0);
618	memcpy(&addr.s6_addr[8], &addr4, sizeof(addr4));
619
620	/*
621	 * Set the last octet to 1 (host) or 2 (VM).
622	 * The latter is currently not used inside vmd as we don't
623	 * answer rtsol requests ourselves.
624	 */
625	if (!isvm)
626		addr.s6_addr[15] = 1;
627	else
628		addr.s6_addr[15] = 2;
629
630	memcpy(out, &addr, sizeof(*out));
631
632	return (0);
633}
634