1*4e55220cSkn /* $OpenBSD: priv.c,v 1.27 2024/11/24 10:44:59 kn Exp $ */ 25921535cSreyk 35921535cSreyk /* 45921535cSreyk * Copyright (c) 2016 Reyk Floeter <reyk@openbsd.org> 55921535cSreyk * 65921535cSreyk * Permission to use, copy, modify, and distribute this software for any 75921535cSreyk * purpose with or without fee is hereby granted, provided that the above 85921535cSreyk * copyright notice and this permission notice appear in all copies. 95921535cSreyk * 105921535cSreyk * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 115921535cSreyk * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 125921535cSreyk * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 135921535cSreyk * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 145921535cSreyk * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 155921535cSreyk * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 165921535cSreyk * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 175921535cSreyk */ 185921535cSreyk 1924fb43d0Sderaadt #include <sys/types.h> 205921535cSreyk #include <sys/socket.h> 215921535cSreyk #include <sys/ioctl.h> 225921535cSreyk 235921535cSreyk #include <net/if.h> 24789e0822Sreyk #include <netinet/in.h> 25789e0822Sreyk #include <netinet/if_ether.h> 26723f86d2Sreyk #include <netinet6/in6_var.h> 27723f86d2Sreyk #include <netinet6/nd6.h> 28789e0822Sreyk #include <net/if_bridge.h> 295921535cSreyk 30470adcf5Sreyk #include <arpa/inet.h> 31470adcf5Sreyk 325921535cSreyk #include <errno.h> 335921535cSreyk #include <stdlib.h> 345921535cSreyk #include <stdio.h> 355921535cSreyk #include <string.h> 365921535cSreyk #include <unistd.h> 372b519c1fSreyk #include <ctype.h> 385921535cSreyk 395921535cSreyk #include "proc.h" 405921535cSreyk #include "vmd.h" 415921535cSreyk 425921535cSreyk int priv_dispatch_parent(int, struct privsep_proc *, struct imsg *); 435921535cSreyk void priv_run(struct privsep *, struct privsep_proc *, void *); 445921535cSreyk 455921535cSreyk static struct privsep_proc procs[] = { 465921535cSreyk { "parent", PROC_PARENT, priv_dispatch_parent } 475921535cSreyk }; 485921535cSreyk 495921535cSreyk void 505921535cSreyk priv(struct privsep *ps, struct privsep_proc *p) 515921535cSreyk { 525921535cSreyk proc_run(ps, p, procs, nitems(procs), priv_run, NULL); 535921535cSreyk } 545921535cSreyk 555921535cSreyk void 565921535cSreyk priv_run(struct privsep *ps, struct privsep_proc *p, void *arg) 575921535cSreyk { 585921535cSreyk struct vmd *env = ps->ps_env; 595921535cSreyk 605921535cSreyk /* 615921535cSreyk * no pledge(2) in the "priv" process: 625921535cSreyk * write ioctls are not permitted by pledge. 635921535cSreyk */ 645921535cSreyk 655921535cSreyk /* Open our own socket for generic interface ioctls */ 665921535cSreyk if ((env->vmd_fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) 675921535cSreyk fatal("socket"); 68723f86d2Sreyk 69723f86d2Sreyk /* But we need a different fd for IPv6 */ 70723f86d2Sreyk if ((env->vmd_fd6 = socket(AF_INET6, SOCK_DGRAM, 0)) == -1) 71723f86d2Sreyk fatal("socket6"); 725921535cSreyk } 735921535cSreyk 745921535cSreyk int 755921535cSreyk priv_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg) 765921535cSreyk { 7740d1acf5Ssthen const char *desct[] = { "tap", "bridge", "veb", NULL }; 785921535cSreyk struct privsep *ps = p->p_ps; 795921535cSreyk struct vmop_ifreq vfr; 805921535cSreyk struct vmd *env = ps->ps_env; 815921535cSreyk struct ifreq ifr; 82789e0822Sreyk struct ifbreq ifbr; 832b519c1fSreyk struct ifgroupreq ifgr; 84470adcf5Sreyk struct ifaliasreq ifra; 85723f86d2Sreyk struct in6_aliasreq in6_ifra; 8697f33f1dSdv struct vmop_addr_req vareq; 8797f33f1dSdv struct vmop_addr_result varesult; 885921535cSreyk char type[IF_NAMESIZE]; 8953027660Sclaudio int ifd; 905921535cSreyk 915921535cSreyk switch (imsg->hdr.type) { 925921535cSreyk case IMSG_VMDOP_PRIV_IFDESCR: 93f418e70cSreyk case IMSG_VMDOP_PRIV_IFRDOMAIN: 94a123de80Smlarkin case IMSG_VMDOP_PRIV_IFEXISTS: 95789e0822Sreyk case IMSG_VMDOP_PRIV_IFADD: 96789e0822Sreyk case IMSG_VMDOP_PRIV_IFUP: 97789e0822Sreyk case IMSG_VMDOP_PRIV_IFDOWN: 982b519c1fSreyk case IMSG_VMDOP_PRIV_IFGROUP: 99470adcf5Sreyk case IMSG_VMDOP_PRIV_IFADDR: 100723f86d2Sreyk case IMSG_VMDOP_PRIV_IFADDR6: 1015921535cSreyk IMSG_SIZE_CHECK(imsg, &vfr); 1025921535cSreyk memcpy(&vfr, imsg->data, sizeof(vfr)); 1035921535cSreyk 1045921535cSreyk /* We should not get malicious requests from the parent */ 105789e0822Sreyk if (priv_getiftype(vfr.vfr_name, type, NULL) == -1 || 1065921535cSreyk priv_findname(type, desct) == -1) 1075921535cSreyk fatalx("%s: rejected priv operation on interface: %s", 1085921535cSreyk __func__, vfr.vfr_name); 109789e0822Sreyk break; 110c48cfcf4Sreyk case IMSG_VMDOP_CONFIG: 111c48cfcf4Sreyk case IMSG_CTL_RESET: 11297f33f1dSdv case IMSG_VMDOP_PRIV_GET_ADDR: 113c48cfcf4Sreyk break; 114789e0822Sreyk default: 115789e0822Sreyk return (-1); 116789e0822Sreyk } 1175921535cSreyk 118789e0822Sreyk switch (imsg->hdr.type) { 119789e0822Sreyk case IMSG_VMDOP_PRIV_IFDESCR: 120789e0822Sreyk /* Set the interface description */ 1215921535cSreyk strlcpy(ifr.ifr_name, vfr.vfr_name, sizeof(ifr.ifr_name)); 1225921535cSreyk ifr.ifr_data = (caddr_t)vfr.vfr_value; 123df69c215Sderaadt if (ioctl(env->vmd_fd, SIOCSIFDESCR, &ifr) == -1) 1245921535cSreyk log_warn("SIOCSIFDESCR"); 1255921535cSreyk break; 126f418e70cSreyk case IMSG_VMDOP_PRIV_IFRDOMAIN: 127f418e70cSreyk strlcpy(ifr.ifr_name, vfr.vfr_name, sizeof(ifr.ifr_name)); 128f418e70cSreyk ifr.ifr_rdomainid = vfr.vfr_id; 129df69c215Sderaadt if (ioctl(env->vmd_fd, SIOCSIFRDOMAIN, &ifr) == -1) 130f418e70cSreyk log_warn("SIOCSIFRDOMAIN"); 131f418e70cSreyk break; 132789e0822Sreyk case IMSG_VMDOP_PRIV_IFADD: 133789e0822Sreyk if (priv_getiftype(vfr.vfr_value, type, NULL) == -1) 134789e0822Sreyk fatalx("%s: rejected to add interface: %s", 135789e0822Sreyk __func__, vfr.vfr_value); 136789e0822Sreyk 137789e0822Sreyk /* Attach the device to the bridge */ 138789e0822Sreyk strlcpy(ifbr.ifbr_name, vfr.vfr_name, 139789e0822Sreyk sizeof(ifbr.ifbr_name)); 140789e0822Sreyk strlcpy(ifbr.ifbr_ifsname, vfr.vfr_value, 141789e0822Sreyk sizeof(ifbr.ifbr_ifsname)); 142df69c215Sderaadt if (ioctl(env->vmd_fd, SIOCBRDGADD, &ifbr) == -1 && 143789e0822Sreyk errno != EEXIST) 144789e0822Sreyk log_warn("SIOCBRDGADD"); 145789e0822Sreyk break; 146a123de80Smlarkin case IMSG_VMDOP_PRIV_IFEXISTS: 14740d1acf5Ssthen /* Determine if bridge exists */ 148a123de80Smlarkin strlcpy(ifr.ifr_name, vfr.vfr_name, sizeof(ifr.ifr_name)); 149df69c215Sderaadt if (ioctl(env->vmd_fd, SIOCGIFFLAGS, &ifr) == -1) 150a123de80Smlarkin fatalx("%s: bridge \"%s\" does not exist", 151a123de80Smlarkin __func__, vfr.vfr_name); 152a123de80Smlarkin break; 153789e0822Sreyk case IMSG_VMDOP_PRIV_IFUP: 154789e0822Sreyk case IMSG_VMDOP_PRIV_IFDOWN: 155789e0822Sreyk /* Set the interface status */ 156789e0822Sreyk strlcpy(ifr.ifr_name, vfr.vfr_name, sizeof(ifr.ifr_name)); 157df69c215Sderaadt if (ioctl(env->vmd_fd, SIOCGIFFLAGS, &ifr) == -1) { 158789e0822Sreyk log_warn("SIOCGIFFLAGS"); 159789e0822Sreyk break; 160789e0822Sreyk } 161789e0822Sreyk if (imsg->hdr.type == IMSG_VMDOP_PRIV_IFUP) 162789e0822Sreyk ifr.ifr_flags |= IFF_UP; 163789e0822Sreyk else 164789e0822Sreyk ifr.ifr_flags &= ~IFF_UP; 165df69c215Sderaadt if (ioctl(env->vmd_fd, SIOCSIFFLAGS, &ifr) == -1) 166789e0822Sreyk log_warn("SIOCSIFFLAGS"); 167789e0822Sreyk break; 1682b519c1fSreyk case IMSG_VMDOP_PRIV_IFGROUP: 1692b519c1fSreyk if (priv_validgroup(vfr.vfr_value) == -1) 1702b519c1fSreyk fatalx("%s: invalid group name", __func__); 1712b519c1fSreyk 1722b519c1fSreyk if (strlcpy(ifgr.ifgr_name, vfr.vfr_name, 1732b519c1fSreyk sizeof(ifgr.ifgr_name)) >= sizeof(ifgr.ifgr_name) || 1742b519c1fSreyk strlcpy(ifgr.ifgr_group, vfr.vfr_value, 1752b519c1fSreyk sizeof(ifgr.ifgr_group)) >= sizeof(ifgr.ifgr_group)) 1762b519c1fSreyk fatalx("%s: group name too long", __func__); 1772b519c1fSreyk 178df69c215Sderaadt if (ioctl(env->vmd_fd, SIOCAIFGROUP, &ifgr) == -1 && 1792b519c1fSreyk errno != EEXIST) 1802b519c1fSreyk log_warn("SIOCAIFGROUP"); 1812b519c1fSreyk break; 182470adcf5Sreyk case IMSG_VMDOP_PRIV_IFADDR: 183470adcf5Sreyk memset(&ifra, 0, sizeof(ifra)); 184470adcf5Sreyk 185723f86d2Sreyk if (vfr.vfr_addr.ss_family != AF_INET || 186723f86d2Sreyk vfr.vfr_addr.ss_family != vfr.vfr_mask.ss_family) 187723f86d2Sreyk fatalx("%s: invalid address family", __func__); 188723f86d2Sreyk 189470adcf5Sreyk /* Set the interface address */ 190470adcf5Sreyk strlcpy(ifra.ifra_name, vfr.vfr_name, sizeof(ifra.ifra_name)); 191470adcf5Sreyk 192723f86d2Sreyk memcpy(&ifra.ifra_addr, &vfr.vfr_addr, 193*4e55220cSkn sizeof(ifra.ifra_addr)); 194723f86d2Sreyk memcpy(&ifra.ifra_mask, &vfr.vfr_mask, 195*4e55220cSkn sizeof(ifra.ifra_mask)); 196470adcf5Sreyk 197df69c215Sderaadt if (ioctl(env->vmd_fd, SIOCAIFADDR, &ifra) == -1) 198470adcf5Sreyk log_warn("SIOCAIFADDR"); 199470adcf5Sreyk break; 200723f86d2Sreyk case IMSG_VMDOP_PRIV_IFADDR6: 201723f86d2Sreyk memset(&in6_ifra, 0, sizeof(in6_ifra)); 202723f86d2Sreyk 203723f86d2Sreyk if (vfr.vfr_addr.ss_family != AF_INET6 || 204723f86d2Sreyk vfr.vfr_addr.ss_family != vfr.vfr_mask.ss_family) 205723f86d2Sreyk fatalx("%s: invalid address family", __func__); 206723f86d2Sreyk 207723f86d2Sreyk /* Set the interface address */ 208723f86d2Sreyk strlcpy(in6_ifra.ifra_name, vfr.vfr_name, 209723f86d2Sreyk sizeof(in6_ifra.ifra_name)); 210723f86d2Sreyk 211723f86d2Sreyk memcpy(&in6_ifra.ifra_addr, &vfr.vfr_addr, 212*4e55220cSkn sizeof(in6_ifra.ifra_addr)); 213723f86d2Sreyk memcpy(&in6_ifra.ifra_prefixmask, &vfr.vfr_mask, 214*4e55220cSkn sizeof(in6_ifra.ifra_prefixmask)); 215723f86d2Sreyk in6_ifra.ifra_prefixmask.sin6_scope_id = 0; 216723f86d2Sreyk 217723f86d2Sreyk in6_ifra.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME; 218723f86d2Sreyk in6_ifra.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME; 219723f86d2Sreyk 220df69c215Sderaadt if (ioctl(env->vmd_fd6, SIOCDIFADDR_IN6, &in6_ifra) == -1 && 221723f86d2Sreyk errno != EADDRNOTAVAIL) 222723f86d2Sreyk log_warn("SIOCDIFADDR_IN6"); 223723f86d2Sreyk 224df69c215Sderaadt if (ioctl(env->vmd_fd6, SIOCAIFADDR_IN6, &in6_ifra) == -1) 225723f86d2Sreyk log_warn("SIOCAIFADDR_IN6"); 226723f86d2Sreyk break; 22797f33f1dSdv case IMSG_VMDOP_PRIV_GET_ADDR: 22897f33f1dSdv IMSG_SIZE_CHECK(imsg, &vareq); 22997f33f1dSdv memcpy(&vareq, imsg->data, sizeof(vareq)); 23097f33f1dSdv 23197f33f1dSdv varesult.var_vmid = vareq.var_vmid; 23297f33f1dSdv varesult.var_nic_idx = vareq.var_nic_idx; 23397f33f1dSdv 23453027660Sclaudio ifd = imsg_get_fd(imsg); 23597f33f1dSdv /* resolve lladdr for the tap(4) and send back to parent */ 23653027660Sclaudio if (ioctl(ifd, SIOCGIFADDR, &varesult.var_addr) != 0) 23797f33f1dSdv log_warn("SIOCGIFADDR"); 23897f33f1dSdv else 23997f33f1dSdv proc_compose_imsg(ps, PROC_PARENT, -1, 24097f33f1dSdv IMSG_VMDOP_PRIV_GET_ADDR_RESPONSE, imsg->hdr.peerid, 24197f33f1dSdv -1, &varesult, sizeof(varesult)); 24253027660Sclaudio close(ifd); 24397f33f1dSdv break; 244c48cfcf4Sreyk case IMSG_VMDOP_CONFIG: 245c48cfcf4Sreyk config_getconfig(env, imsg); 246c48cfcf4Sreyk break; 247c48cfcf4Sreyk case IMSG_CTL_RESET: 248c48cfcf4Sreyk config_getreset(env, imsg); 249c48cfcf4Sreyk break; 2505921535cSreyk default: 2515921535cSreyk return (-1); 2525921535cSreyk } 2535921535cSreyk 2545921535cSreyk return (0); 2555921535cSreyk } 2565921535cSreyk 2575921535cSreyk int 2585921535cSreyk priv_getiftype(char *ifname, char *type, unsigned int *unitptr) 2595921535cSreyk { 2605921535cSreyk const char *errstr; 2615921535cSreyk size_t span; 2625921535cSreyk unsigned int unit; 2635921535cSreyk 2645921535cSreyk /* Extract the name part */ 2655921535cSreyk span = strcspn(ifname, "0123456789"); 2665921535cSreyk if (span == 0 || span >= strlen(ifname) || span >= (IF_NAMESIZE - 1)) 2675921535cSreyk return (-1); 2685921535cSreyk memcpy(type, ifname, span); 2695921535cSreyk type[span] = 0; 2705921535cSreyk 2715921535cSreyk /* Now parse the unit (we don't strictly validate the format here) */ 2725921535cSreyk unit = strtonum(ifname + span, 0, UINT_MAX, &errstr); 2735921535cSreyk if (errstr != NULL) 2745921535cSreyk return (-1); 2755921535cSreyk if (unitptr != NULL) 2765921535cSreyk *unitptr = unit; 2775921535cSreyk 2785921535cSreyk return (0); 2795921535cSreyk } 2805921535cSreyk 2815921535cSreyk int 2825921535cSreyk priv_findname(const char *name, const char **names) 2835921535cSreyk { 2845921535cSreyk unsigned int i; 2855921535cSreyk 2865921535cSreyk for (i = 0; names[i] != NULL; i++) { 2875921535cSreyk if (strcmp(name, names[i]) == 0) 2885921535cSreyk return (0); 2895921535cSreyk } 2905921535cSreyk 2915921535cSreyk return (-1); 2925921535cSreyk } 2935921535cSreyk 2942b519c1fSreyk int 2952b519c1fSreyk priv_validgroup(const char *name) 2962b519c1fSreyk { 2971c6718c9Smillert const size_t len = strnlen(name, IF_NAMESIZE); 2981c6718c9Smillert 2991c6718c9Smillert if (len == IF_NAMESIZE) 3002b519c1fSreyk return (-1); 3012b519c1fSreyk /* Group can not end with a digit */ 3021c6718c9Smillert if (len > 0 && isdigit((unsigned char)name[len - 1])) 3032b519c1fSreyk return (-1); 3042b519c1fSreyk return (0); 3052b519c1fSreyk } 3062b519c1fSreyk 3075921535cSreyk /* 308e330d69cSmlarkin * Called from the Parent process to setup vm interface(s) 309e330d69cSmlarkin * - ensure the interface has the description set (tracking purposes) 310e330d69cSmlarkin * - if interface is to be attached to a switch, attach it 311e330d69cSmlarkin * - check if rdomain is set on interface and switch 312e330d69cSmlarkin * - if interface only or both, use interface rdomain 313e330d69cSmlarkin * - if switch only, use switch rdomain 314e330d69cSmlarkin * - check if group is set on interface and switch 315e330d69cSmlarkin * - if interface, add it 316e330d69cSmlarkin * - if switch, add it 317e330d69cSmlarkin * - ensure the interface is up/down 318e330d69cSmlarkin * - if local interface, set address 3195921535cSreyk */ 3205921535cSreyk int 3215921535cSreyk vm_priv_ifconfig(struct privsep *ps, struct vmd_vm *vm) 3225921535cSreyk { 323723f86d2Sreyk char name[64]; 324c48cfcf4Sreyk struct vmd *env = ps->ps_env; 325ea9c30d9Sedd struct vm_create_params *vcp = &vm->vm_params.vmc_params; 326789e0822Sreyk struct vmd_if *vif; 327789e0822Sreyk struct vmd_switch *vsw; 3285921535cSreyk unsigned int i; 329789e0822Sreyk struct vmop_ifreq vfr, vfbr; 330470adcf5Sreyk struct sockaddr_in *sin4; 331723f86d2Sreyk struct sockaddr_in6 *sin6; 3325921535cSreyk 333d489aa7eSdv for (i = 0; i < VM_MAX_NICS_PER_VM; i++) { 334789e0822Sreyk vif = &vm->vm_ifs[i]; 335789e0822Sreyk 336789e0822Sreyk if (vif->vif_name == NULL) 3375921535cSreyk break; 3385921535cSreyk 339723f86d2Sreyk memset(&vfr, 0, sizeof(vfr)); 340789e0822Sreyk if (strlcpy(vfr.vfr_name, vif->vif_name, 3415921535cSreyk sizeof(vfr.vfr_name)) >= sizeof(vfr.vfr_name)) 3425921535cSreyk return (-1); 3435921535cSreyk 3445921535cSreyk /* Description can be truncated */ 3455921535cSreyk (void)snprintf(vfr.vfr_value, sizeof(vfr.vfr_value), 3465921535cSreyk "vm%u-if%u-%s", vm->vm_vmid, i, vcp->vcp_name); 3475921535cSreyk 348789e0822Sreyk log_debug("%s: interface %s description %s", __func__, 349789e0822Sreyk vfr.vfr_name, vfr.vfr_value); 350789e0822Sreyk 3515921535cSreyk proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFDESCR, 3525921535cSreyk &vfr, sizeof(vfr)); 3535921535cSreyk 354e330d69cSmlarkin /* set default rdomain */ 355e330d69cSmlarkin vfr.vfr_id = getrtable(); 356e330d69cSmlarkin 357e330d69cSmlarkin vsw = switch_getbyname(vif->vif_switch); 358e330d69cSmlarkin 359e330d69cSmlarkin /* Check if switch should exist */ 360e330d69cSmlarkin if (vsw == NULL && vif->vif_switch != NULL) 361e330d69cSmlarkin log_warnx("switch \"%s\" not found", vif->vif_switch); 362e330d69cSmlarkin 363e330d69cSmlarkin /* Add interface to switch and set proper rdomain */ 364e330d69cSmlarkin if (vsw != NULL) { 365f418e70cSreyk memset(&vfbr, 0, sizeof(vfbr)); 366f418e70cSreyk 367789e0822Sreyk if (strlcpy(vfbr.vfr_name, vsw->sw_ifname, 368789e0822Sreyk sizeof(vfbr.vfr_name)) >= sizeof(vfbr.vfr_name)) 369789e0822Sreyk return (-1); 370789e0822Sreyk if (strlcpy(vfbr.vfr_value, vif->vif_name, 371789e0822Sreyk sizeof(vfbr.vfr_value)) >= sizeof(vfbr.vfr_value)) 372789e0822Sreyk return (-1); 373789e0822Sreyk 374e330d69cSmlarkin log_debug("%s: switch \"%s\" interface %s add %s", 375e330d69cSmlarkin __func__, vsw->sw_name, vfbr.vfr_name, 376e330d69cSmlarkin vfbr.vfr_value); 377789e0822Sreyk 378789e0822Sreyk proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFADD, 379789e0822Sreyk &vfbr, sizeof(vfbr)); 380e330d69cSmlarkin 381e330d69cSmlarkin /* Check rdomain properties */ 382e330d69cSmlarkin if (vif->vif_flags & VMIFF_RDOMAIN) 383e330d69cSmlarkin vfr.vfr_id = vif->vif_rdomain; 384e330d69cSmlarkin else if (vsw->sw_flags & VMIFF_RDOMAIN) 385e330d69cSmlarkin vfr.vfr_id = vsw->sw_rdomain; 386e330d69cSmlarkin } else { 387e330d69cSmlarkin /* No switch to attach case */ 388e330d69cSmlarkin if (vif->vif_flags & VMIFF_RDOMAIN) 389e330d69cSmlarkin vfr.vfr_id = vif->vif_rdomain; 390e330d69cSmlarkin } 391e330d69cSmlarkin 392e330d69cSmlarkin /* Set rdomain on interface */ 393e330d69cSmlarkin if (vfr.vfr_id != 0) 394e330d69cSmlarkin log_debug("%s: interface %s rdomain %u", __func__, 395e330d69cSmlarkin vfr.vfr_name, vfr.vfr_id); 396e330d69cSmlarkin 397e330d69cSmlarkin proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFRDOMAIN, 398e330d69cSmlarkin &vfr, sizeof(vfr)); 399789e0822Sreyk 4004629ffa0Sreyk /* First group is defined per-interface */ 4014629ffa0Sreyk if (vif->vif_group) { 4024629ffa0Sreyk if (strlcpy(vfr.vfr_value, vif->vif_group, 4034629ffa0Sreyk sizeof(vfr.vfr_value)) >= sizeof(vfr.vfr_value)) 4044629ffa0Sreyk return (-1); 4054629ffa0Sreyk 4064629ffa0Sreyk log_debug("%s: interface %s group %s", __func__, 4074629ffa0Sreyk vfr.vfr_name, vfr.vfr_value); 4084629ffa0Sreyk 4094629ffa0Sreyk proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFGROUP, 4104629ffa0Sreyk &vfr, sizeof(vfr)); 4114629ffa0Sreyk } 4124629ffa0Sreyk 4134629ffa0Sreyk /* The second group is defined per-switch */ 4144629ffa0Sreyk if (vsw != NULL && vsw->sw_group != NULL) { 4154629ffa0Sreyk if (strlcpy(vfr.vfr_value, vsw->sw_group, 4164629ffa0Sreyk sizeof(vfr.vfr_value)) >= sizeof(vfr.vfr_value)) 4174629ffa0Sreyk return (-1); 4184629ffa0Sreyk 419e330d69cSmlarkin log_debug("%s: interface %s group %s switch \"%s\"", 4204629ffa0Sreyk __func__, vfr.vfr_name, vfr.vfr_value, 4214629ffa0Sreyk vsw->sw_name); 4224629ffa0Sreyk 4234629ffa0Sreyk proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFGROUP, 4244629ffa0Sreyk &vfr, sizeof(vfr)); 4254629ffa0Sreyk } 4264629ffa0Sreyk 427789e0822Sreyk /* Set the new interface status to up or down */ 4282b2a5f0dSreyk proc_compose(ps, PROC_PRIV, (vif->vif_flags & VMIFF_UP) ? 429789e0822Sreyk IMSG_VMDOP_PRIV_IFUP : IMSG_VMDOP_PRIV_IFDOWN, 430789e0822Sreyk &vfr, sizeof(vfr)); 431470adcf5Sreyk 432e330d69cSmlarkin /* Set interface address if it is a local interface */ 433470adcf5Sreyk if (vm->vm_params.vmc_ifflags[i] & VMIFF_LOCAL) { 434723f86d2Sreyk memset(&vfr.vfr_mask, 0, sizeof(vfr.vfr_mask)); 435723f86d2Sreyk memset(&vfr.vfr_addr, 0, sizeof(vfr.vfr_addr)); 436723f86d2Sreyk 437723f86d2Sreyk /* local IPv4 address with a /31 mask */ 438723f86d2Sreyk sin4 = (struct sockaddr_in *)&vfr.vfr_mask; 439470adcf5Sreyk sin4->sin_family = AF_INET; 440470adcf5Sreyk sin4->sin_len = sizeof(*sin4); 441470adcf5Sreyk sin4->sin_addr.s_addr = htonl(0xfffffffe); 442470adcf5Sreyk 443723f86d2Sreyk sin4 = (struct sockaddr_in *)&vfr.vfr_addr; 444470adcf5Sreyk sin4->sin_family = AF_INET; 445470adcf5Sreyk sin4->sin_len = sizeof(*sin4); 446470adcf5Sreyk if ((sin4->sin_addr.s_addr = 4472272e586Sdv vm_priv_addr(&env->vmd_cfg.cfg_localprefix, 448c48cfcf4Sreyk vm->vm_vmid, i, 0)) == 0) 449470adcf5Sreyk return (-1); 450470adcf5Sreyk 451723f86d2Sreyk inet_ntop(AF_INET, &sin4->sin_addr, 452723f86d2Sreyk name, sizeof(name)); 453470adcf5Sreyk log_debug("%s: interface %s address %s/31", 454723f86d2Sreyk __func__, vfr.vfr_name, name); 455470adcf5Sreyk 456470adcf5Sreyk proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFADDR, 457470adcf5Sreyk &vfr, sizeof(vfr)); 458470adcf5Sreyk } 459723f86d2Sreyk if ((vm->vm_params.vmc_ifflags[i] & VMIFF_LOCAL) && 460723f86d2Sreyk (env->vmd_cfg.cfg_flags & VMD_CFG_INET6)) { 461723f86d2Sreyk memset(&vfr.vfr_mask, 0, sizeof(vfr.vfr_mask)); 462723f86d2Sreyk memset(&vfr.vfr_addr, 0, sizeof(vfr.vfr_addr)); 463723f86d2Sreyk 464723f86d2Sreyk /* local IPv6 address with a /96 mask */ 465723f86d2Sreyk sin6 = ss2sin6(&vfr.vfr_mask); 466723f86d2Sreyk sin6->sin6_family = AF_INET6; 467723f86d2Sreyk sin6->sin6_len = sizeof(*sin6); 468723f86d2Sreyk memset(&sin6->sin6_addr.s6_addr[0], 0xff, 12); 469723f86d2Sreyk memset(&sin6->sin6_addr.s6_addr[12], 0, 4); 470723f86d2Sreyk 471723f86d2Sreyk sin6 = ss2sin6(&vfr.vfr_addr); 472723f86d2Sreyk sin6->sin6_family = AF_INET6; 473723f86d2Sreyk sin6->sin6_len = sizeof(*sin6); 4742272e586Sdv if (vm_priv_addr6(&env->vmd_cfg.cfg_localprefix, 475723f86d2Sreyk vm->vm_vmid, i, 0, &sin6->sin6_addr) == -1) 476723f86d2Sreyk return (-1); 477723f86d2Sreyk 478723f86d2Sreyk inet_ntop(AF_INET6, &sin6->sin6_addr, 479723f86d2Sreyk name, sizeof(name)); 480723f86d2Sreyk log_debug("%s: interface %s address %s/96", 481723f86d2Sreyk __func__, vfr.vfr_name, name); 482723f86d2Sreyk 483723f86d2Sreyk proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFADDR6, 484723f86d2Sreyk &vfr, sizeof(vfr)); 485723f86d2Sreyk } 4865921535cSreyk } 4875921535cSreyk 4885921535cSreyk return (0); 4895921535cSreyk } 490789e0822Sreyk 491e330d69cSmlarkin /* 492e330d69cSmlarkin * Called from the Parent process to setup underlying switch interface 493e330d69cSmlarkin * - ensure the interface exists 494e330d69cSmlarkin * - ensure the interface has the correct rdomain set 495e330d69cSmlarkin * - ensure the interface has the description set (tracking purposes) 496e330d69cSmlarkin * - ensure the interface is up/down 497e330d69cSmlarkin */ 498789e0822Sreyk int 499789e0822Sreyk vm_priv_brconfig(struct privsep *ps, struct vmd_switch *vsw) 500789e0822Sreyk { 501789e0822Sreyk struct vmop_ifreq vfr; 502789e0822Sreyk 503f418e70cSreyk memset(&vfr, 0, sizeof(vfr)); 504f418e70cSreyk 505789e0822Sreyk if (strlcpy(vfr.vfr_name, vsw->sw_ifname, 506789e0822Sreyk sizeof(vfr.vfr_name)) >= sizeof(vfr.vfr_name)) 507789e0822Sreyk return (-1); 508789e0822Sreyk 50940d1acf5Ssthen /* ensure bridge exists */ 510a123de80Smlarkin proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFEXISTS, 511789e0822Sreyk &vfr, sizeof(vfr)); 512789e0822Sreyk 513e08715daSreyk /* Use the configured rdomain or get it from the process */ 514f418e70cSreyk if (vsw->sw_flags & VMIFF_RDOMAIN) 515e08715daSreyk vfr.vfr_id = vsw->sw_rdomain; 516e08715daSreyk else 517e08715daSreyk vfr.vfr_id = getrtable(); 518e08715daSreyk if (vfr.vfr_id != 0) 519e08715daSreyk log_debug("%s: interface %s rdomain %u", __func__, 520e08715daSreyk vfr.vfr_name, vfr.vfr_id); 521e08715daSreyk 52240d1acf5Ssthen /* ensure bridge has the correct rdomain */ 523f418e70cSreyk proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFRDOMAIN, 524f418e70cSreyk &vfr, sizeof(vfr)); 525f418e70cSreyk 526789e0822Sreyk /* Description can be truncated */ 527789e0822Sreyk (void)snprintf(vfr.vfr_value, sizeof(vfr.vfr_value), 528789e0822Sreyk "switch%u-%s", vsw->sw_id, vsw->sw_name); 529789e0822Sreyk 530789e0822Sreyk log_debug("%s: interface %s description %s", __func__, 531789e0822Sreyk vfr.vfr_name, vfr.vfr_value); 532789e0822Sreyk 533789e0822Sreyk proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFDESCR, 534789e0822Sreyk &vfr, sizeof(vfr)); 535789e0822Sreyk 536789e0822Sreyk /* Set the new interface status to up or down */ 5372b2a5f0dSreyk proc_compose(ps, PROC_PRIV, (vsw->sw_flags & VMIFF_UP) ? 538789e0822Sreyk IMSG_VMDOP_PRIV_IFUP : IMSG_VMDOP_PRIV_IFDOWN, 539789e0822Sreyk &vfr, sizeof(vfr)); 540789e0822Sreyk 541ea9c30d9Sedd vsw->sw_running = 1; 542789e0822Sreyk return (0); 543789e0822Sreyk } 544470adcf5Sreyk 545470adcf5Sreyk uint32_t 5462272e586Sdv vm_priv_addr(struct local_prefix *p, uint32_t vmid, int idx, int isvm) 547470adcf5Sreyk { 5482272e586Sdv in_addr_t addr; 549470adcf5Sreyk 5502272e586Sdv /* Encode the VM ID as a per-VM subnet range N, 100.64.N.0/24. */ 551470adcf5Sreyk addr = vmid << 8; 552470adcf5Sreyk 553470adcf5Sreyk /* 5542272e586Sdv * Assign a /31 subnet M per VM interface, 100.64.N.M/31. 555470adcf5Sreyk * Each subnet contains exactly two IP addresses; skip the 556470adcf5Sreyk * first subnet to avoid a gateway address ending with .0. 557470adcf5Sreyk */ 558470adcf5Sreyk addr |= (idx + 1) * 2; 559470adcf5Sreyk 5602272e586Sdv /* Use the first address for the gateway, the second for the VM. */ 561470adcf5Sreyk if (isvm) 562470adcf5Sreyk addr++; 563470adcf5Sreyk 5642272e586Sdv /* Convert to network byte order and add the prefix. */ 5652272e586Sdv addr = htonl(addr) | p->lp_in.s_addr; 566470adcf5Sreyk 567470adcf5Sreyk /* 568470adcf5Sreyk * Validate the results: 569470adcf5Sreyk * - the address should not exceed the prefix (eg. VM ID to high). 570470adcf5Sreyk * - up to 126 interfaces can be encoded per VM. 571470adcf5Sreyk */ 5722272e586Sdv if (p->lp_in.s_addr != (addr & p->lp_mask.s_addr) || idx >= 0x7f) { 573470adcf5Sreyk log_warnx("%s: dhcp address range exceeded," 574470adcf5Sreyk " vm id %u interface %d", __func__, vmid, idx); 575470adcf5Sreyk return (0); 576470adcf5Sreyk } 577470adcf5Sreyk 578470adcf5Sreyk return (addr); 579470adcf5Sreyk } 580723f86d2Sreyk 581723f86d2Sreyk int 5822272e586Sdv vm_priv_addr6(struct local_prefix *p, uint32_t vmid, int idx, int isvm, 5832272e586Sdv struct in6_addr *out) 584723f86d2Sreyk { 5852272e586Sdv struct in6_addr addr; 5862272e586Sdv in_addr_t addr4; 587723f86d2Sreyk 5882272e586Sdv /* Start with the IPv6 prefix. */ 5892272e586Sdv memcpy(&addr, &p->lp_in6, sizeof(addr)); 590723f86d2Sreyk 5912272e586Sdv /* Encode the VM IPv4 address as subnet, fd00::NN:NN:0:0/96. */ 5922272e586Sdv if ((addr4 = vm_priv_addr(p, vmid, idx, 1)) == 0) 593723f86d2Sreyk return (0); 594723f86d2Sreyk memcpy(&addr.s6_addr[8], &addr4, sizeof(addr4)); 595723f86d2Sreyk 596723f86d2Sreyk /* 5972272e586Sdv * Set the last octet to 1 (host) or 2 (VM). 598723f86d2Sreyk * The latter is currently not used inside vmd as we don't 599723f86d2Sreyk * answer rtsol requests ourselves. 600723f86d2Sreyk */ 601723f86d2Sreyk if (!isvm) 602723f86d2Sreyk addr.s6_addr[15] = 1; 603723f86d2Sreyk else 604723f86d2Sreyk addr.s6_addr[15] = 2; 605723f86d2Sreyk 6062272e586Sdv memcpy(out, &addr, sizeof(*out)); 607723f86d2Sreyk 608723f86d2Sreyk return (0); 609723f86d2Sreyk } 610