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