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