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