priv.c revision 1.14
1/* $OpenBSD: priv.c,v 1.14 2018/11/21 12:31:47 reyk 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) < 0) 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) < 0) 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) < 0 && 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) < 0) 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) < 0) { 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) < 0) 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) < 0 && 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) < 0) 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) < 0) 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) < 0 && 241 errno != EADDRNOTAVAIL) 242 log_warn("SIOCDIFADDR_IN6"); 243 244 if (ioctl(env->vmd_fd6, SIOCAIFADDR_IN6, &in6_ifra) < 0) 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