1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright 2018 Joyent, Inc.
  24  */
  25 
  26 /*
  27  * lxinit performs zone-specific initialization prior to handing control to the
  28  * guest Linux init.  This primarily consists of:
  29  *
  30  * - Starting ipmgmtd
  31  * - Configuring network interfaces
  32  * - Adding a default route
  33  */
  34 
  35 #include <ctype.h>
  36 #include <errno.h>
  37 #include <fcntl.h>
  38 #include <libgen.h>
  39 #include <limits.h>
  40 #include <net/if.h>
  41 #include <stdarg.h>
  42 #include <stdio.h>
  43 #include <stdlib.h>
  44 #include <string.h>
  45 #include <strings.h>
  46 #include <stropts.h>
  47 #include <sys/ioccom.h>
  48 #include <sys/stat.h>
  49 #include <sys/systeminfo.h>
  50 #include <sys/sockio.h>
  51 #include <sys/types.h>
  52 #include <sys/wait.h>
  53 #include <sys/varargs.h>
  54 #include <unistd.h>
  55 #include <libintl.h>
  56 #include <locale.h>
  57 #include <libcustr.h>
  58 
  59 #include <netinet/dhcp.h>
  60 #include <dhcpagent_util.h>
  61 #include <dhcpagent_ipc.h>
  62 
  63 #include <arpa/inet.h>
  64 #include <net/route.h>
  65 #include <libipadm.h>
  66 #include <libzonecfg.h>
  67 #include <libinetutil.h>
  68 #include <sys/lx_brand.h>
  69 
  70 #include "run_command.h"
  71 
  72 static void lxi_err(char *msg, ...) __NORETURN;
  73 static void lxi_err(char *msg, ...);
  74 
  75 #define IPMGMTD_PATH            "/lib/inet/ipmgmtd"
  76 #define IN_NDPD_PATH            "/usr/lib/inet/in.ndpd"
  77 #define HOOK_POSTNET_PATH       "/usr/lib/brand/lx/lx_hook_postnet"
  78 
  79 #define PREFIX_LOG_WARN "lx_init warn: "
  80 #define PREFIX_LOG_ERR  "lx_init err: "
  81 
  82 #define RTMBUFSZ        (sizeof (struct rt_msghdr) + \
  83                 (3 * sizeof (struct sockaddr_in)))
  84 
  85 ipadm_handle_t iph;
  86 
  87 static void
  88 lxi_err(char *msg, ...)
  89 {
  90         char buf[1024];
  91         int len;
  92         va_list ap;
  93 
  94         va_start(ap, msg);
  95         /*LINTED*/
  96         len = vsnprintf(buf, sizeof (buf), msg, ap);
  97         va_end(ap);
  98 
  99         (void) write(1, PREFIX_LOG_ERR, strlen(PREFIX_LOG_ERR));
 100         (void) write(1, buf, len);
 101         (void) write(1, "\n", 1);
 102 
 103         /*
 104          * Since a non-zero exit will cause the zone to reboot, a pause here
 105          * will prevent a mis-configured zone from spinning in a reboot loop.
 106          */
 107         pause();
 108         exit(1);
 109         /*NOTREACHED*/
 110 }
 111 
 112 static void
 113 lxi_warn(char *msg, ...)
 114 {
 115         char buf[1024];
 116         int len;
 117         va_list ap;
 118 
 119         va_start(ap, msg);
 120         /*LINTED*/
 121         len = vsnprintf(buf, sizeof (buf), msg, ap);
 122         va_end(ap);
 123 
 124         (void) write(1, PREFIX_LOG_WARN, strlen(PREFIX_LOG_WARN));
 125         (void) write(1, buf, len);
 126         (void) write(1, "\n", 1);
 127 }
 128 
 129 static void
 130 lxi_log_open()
 131 {
 132         int fd = open("/dev/console", O_WRONLY);
 133 
 134         if (fd < 0) {
 135                 /* hard to log at this point... */
 136                 exit(1);
 137         } else if (fd != 1) {
 138                 /*
 139                  * Use stdout as the log fd.  Init should start with no files
 140                  * open, so we should be required to perform this relocation
 141                  * every time.
 142                  */
 143                 if (dup2(fd, 1) != 1) {
 144                         exit(1);
 145                 }
 146         }
 147 }
 148 
 149 static void
 150 lxi_log_close()
 151 {
 152         (void) close(0);
 153         (void) close(1);
 154 }
 155 
 156 static zone_dochandle_t
 157 lxi_config_open()
 158 {
 159         zoneid_t zoneid;
 160         char zonename[ZONENAME_MAX];
 161         zone_dochandle_t handle;
 162         zone_iptype_t iptype;
 163         int res;
 164 
 165         zoneid = getzoneid();
 166         if (getzonenamebyid(zoneid, zonename, sizeof (zonename)) < 0) {
 167                 lxi_err("could not determine zone name");
 168         }
 169 
 170         if ((handle = zonecfg_init_handle()) == NULL)
 171                 lxi_err("internal libzonecfg.so.1 error", 0);
 172 
 173         if ((res = zonecfg_get_handle(zonename, handle)) != Z_OK) {
 174                 zonecfg_fini_handle(handle);
 175                 lxi_err("could not locate zone config %d", res);
 176         }
 177 
 178         /*
 179          * Only exclusive stack is supported.
 180          */
 181         if (zonecfg_get_iptype(handle, &iptype) != Z_OK ||
 182             iptype != ZS_EXCLUSIVE) {
 183                 zonecfg_fini_handle(handle);
 184                 lxi_err("lx zones do not support shared IP stacks");
 185         }
 186 
 187         return (handle);
 188 
 189 }
 190 
 191 static int
 192 zone_find_attr(struct zone_res_attrtab *attrs, const char *name,
 193     const char **result)
 194 {
 195         while (attrs != NULL) {
 196                 if (strncmp(attrs->zone_res_attr_name, name,
 197                     MAXNAMELEN) == 0) {
 198                         *result = attrs->zone_res_attr_value;
 199                         return (0);
 200                 }
 201                 attrs = attrs->zone_res_attr_next;
 202         }
 203         return (-1);
 204 }
 205 
 206 void
 207 lxi_svc_start(char *name, char *path, char *fmri)
 208 {
 209         pid_t pid;
 210         int status;
 211         char *const argv[] = {
 212                 name,
 213                 NULL
 214         };
 215         char *const envp[] = {
 216                 fmri,
 217                 NULL
 218         };
 219 
 220         pid = fork();
 221         if (pid == -1) {
 222                 lxi_err("fork() failed: %s", strerror(errno));
 223         }
 224 
 225         if (pid == 0) {
 226                 /* child */
 227                 const char *zroot = zone_get_nroot();
 228                 char cmd[MAXPATHLEN];
 229 
 230                 /*
 231                  * Construct the full path to the binary, including the native
 232                  * system root (e.g. "/native") if in use for this zone:
 233                  */
 234                 (void) snprintf(cmd, sizeof (cmd), "%s%s", zroot != NULL ?
 235                     zroot : "", path);
 236 
 237                 execve(cmd, argv, envp);
 238 
 239                 lxi_err("execve(%s) failed: %s", cmd, strerror(errno));
 240                 /* NOTREACHED */
 241         }
 242 
 243         /* parent */
 244         while (wait(&status) != pid) {
 245                 /* EMPTY */;
 246         }
 247 
 248         if (WIFEXITED(status)) {
 249                 if (WEXITSTATUS(status) != 0) {
 250                         lxi_err("%s[%d] exited: %d", name,
 251                             (int)pid, WEXITSTATUS(status));
 252                 }
 253         } else if (WIFSIGNALED(status)) {
 254                 lxi_err("%s[%d] died on signal: %d", name,
 255                     (int)pid, WTERMSIG(status));
 256         } else {
 257                 lxi_err("%s[%d] failed in unknown way", name,
 258                     (int)pid);
 259         }
 260 }
 261 
 262 void
 263 lxi_net_ipmgmtd_start()
 264 {
 265         lxi_svc_start("ipmgmtd", IPMGMTD_PATH,
 266             "SMF_FMRI=svc:/network/ip-interface-management:default");
 267 }
 268 
 269 void
 270 lxi_net_ndpd_start()
 271 {
 272         lxi_svc_start("in.ndpd", IN_NDPD_PATH,
 273             "SMF_FMRI=svc:/network/routing/ndp:default");
 274 }
 275 
 276 
 277 static void
 278 lxi_net_ipadm_open()
 279 {
 280         ipadm_status_t status;
 281 
 282         if ((status = ipadm_open(&iph, IPH_LEGACY)) != IPADM_SUCCESS) {
 283                 lxi_err("Error opening ipadm handle: %s",
 284                     ipadm_status2str(status));
 285         }
 286 }
 287 
 288 static void
 289 lxi_net_ipadm_close()
 290 {
 291         ipadm_close(iph);
 292 }
 293 
 294 void
 295 lxi_net_plumb(const char *iface)
 296 {
 297         ipadm_status_t status;
 298         char ifbuf[LIFNAMSIZ];
 299 
 300         /* ipadm_create_if stomps on ifbuf, so create a copy: */
 301         (void) strncpy(ifbuf, iface, sizeof (ifbuf));
 302 
 303         if ((status = ipadm_create_if(iph, ifbuf, AF_INET, IPADM_OPT_ACTIVE))
 304             != IPADM_SUCCESS) {
 305                 lxi_err("ipadm_create_if error %d: %s/v4: %s",
 306                     status, iface, ipadm_status2str(status));
 307         }
 308 
 309         if ((status = ipadm_create_if(iph, ifbuf, AF_INET6, IPADM_OPT_ACTIVE))
 310             != IPADM_SUCCESS) {
 311                 lxi_err("ipadm_create_if error %d: %s/v6: %s",
 312                     status, iface, ipadm_status2str(status));
 313         }
 314 }
 315 
 316 static int
 317 lxi_getif(int af, char *iface, int len, boolean_t first_ipv4_configured)
 318 {
 319         struct lifreq lifr;
 320         int s = socket(af, SOCK_DGRAM, 0);
 321         if (s < 0) {
 322                 lxi_warn("socket error %d: bringing up %s: %s",
 323                     errno, iface, strerror(errno));
 324                 return (-1);
 325         }
 326 
 327         /*
 328          * We need a new logical interface for every IP address we add, except
 329          * for the very first IPv4 address.
 330          */
 331         if (af == AF_INET6 || first_ipv4_configured) {
 332                 (void) strncpy(lifr.lifr_name, iface, sizeof (lifr.lifr_name));
 333                 (void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr));
 334                 if (ioctl(s, SIOCLIFADDIF, (caddr_t)&lifr) < 0) {
 335                         if (close(s) != 0) {
 336                                 lxi_warn("failed to close socket: %s\n",
 337                                     strerror(errno));
 338                         }
 339                         return (-1);
 340                 }
 341                 (void) strncpy(iface, lifr.lifr_name, len);
 342         }
 343 
 344         if (close(s) != 0) {
 345                 lxi_warn("failed to close socket: %s\n",
 346                     strerror(errno));
 347         }
 348         return (0);
 349 }
 350 
 351 static int
 352 lxi_iface_ip(const char *origiface, const char *addr,
 353     boolean_t *first_ipv4_configured)
 354 {
 355         static int addrnum = 0;
 356         ipadm_status_t status;
 357         ipadm_addrobj_t ipaddr = NULL;
 358         char iface[LIFNAMSIZ];
 359         char aobjname[IPADM_AOBJSIZ];
 360         int af, err = 0;
 361 
 362         (void) strncpy(iface, origiface, sizeof (iface));
 363 
 364         af = strstr(addr, ":") == NULL ? AF_INET : AF_INET6;
 365         if (lxi_getif(af, iface, sizeof (iface), *first_ipv4_configured) != 0) {
 366                 lxi_warn("failed to create new logical interface "
 367                     "on %s: %s", origiface, strerror(errno));
 368                 return (-1);
 369         }
 370 
 371         (void) snprintf(aobjname, IPADM_AOBJSIZ, "%s/addr%d", iface,
 372             addrnum++);
 373 
 374         if ((status = ipadm_create_addrobj(IPADM_ADDR_STATIC, aobjname,
 375             &ipaddr)) != IPADM_SUCCESS) {
 376                 lxi_warn("ipadm_create_addrobj error %d: addr %s, "
 377                     "interface %s: %s\n", status, addr, iface,
 378                     ipadm_status2str(status));
 379                 return (-2);
 380         }
 381 
 382         if ((status = ipadm_set_addr(ipaddr, addr, AF_UNSPEC))
 383             != IPADM_SUCCESS) {
 384                 lxi_warn("ipadm_set_addr error %d: addr %s"
 385                     ", interface %s: %s\n", status, addr,
 386                     iface, ipadm_status2str(status));
 387                 err = -3;
 388                 goto done;
 389         }
 390 
 391         if ((status = ipadm_create_addr(iph, ipaddr,
 392             IPADM_OPT_ACTIVE | IPADM_OPT_UP)) != IPADM_SUCCESS) {
 393                 lxi_warn("ipadm_create_addr error for %s: %s\n", iface,
 394                     ipadm_status2str(status));
 395                 err = -4;
 396                 goto done;
 397         }
 398 
 399         if (af == AF_INET) {
 400                 *first_ipv4_configured = B_TRUE;
 401         }
 402 
 403 done:
 404         ipadm_destroy_addrobj(ipaddr);
 405         return (err);
 406 }
 407 
 408 static int
 409 lxi_iface_dhcp(const char *origiface, boolean_t *first_ipv4_configured)
 410 {
 411         dhcp_ipc_request_t *dhcpreq = NULL;
 412         dhcp_ipc_reply_t *dhcpreply = NULL;
 413         int err = 0, timeout = 5;
 414         char iface[LIFNAMSIZ];
 415 
 416         (void) strncpy(iface, origiface, sizeof (iface));
 417 
 418         if (lxi_getif(AF_INET, iface, sizeof (iface), *first_ipv4_configured)
 419             != 0) {
 420                 lxi_warn("failed to create new logical interface "
 421                     "on %s: %s", origiface, strerror(errno));
 422                 return (-1);
 423         }
 424 
 425         if (dhcp_start_agent(timeout) != 0) {
 426                 lxi_err("Failed to start dhcpagent\n");
 427                 /* NOTREACHED */
 428         }
 429 
 430         dhcpreq = dhcp_ipc_alloc_request(DHCP_START, iface,
 431             NULL, 0, DHCP_TYPE_NONE);
 432         if (dhcpreq == NULL) {
 433                 lxi_warn("Unable to allocate memory "
 434                     "to start DHCP on %s\n", iface);
 435                 return (-1);
 436         }
 437 
 438         err = dhcp_ipc_make_request(dhcpreq, &dhcpreply, timeout);
 439         if (err != 0) {
 440                 free(dhcpreq);
 441                 lxi_warn("Failed to start DHCP on %s: %s\n", iface,
 442                     dhcp_ipc_strerror(err));
 443                 return (-1);
 444         }
 445         err = dhcpreply->return_code;
 446         if (err != 0) {
 447                 lxi_warn("Failed to start DHCP on %s: %s\n", iface,
 448                     dhcp_ipc_strerror(err));
 449                 goto done;
 450         }
 451 
 452         *first_ipv4_configured = B_TRUE;
 453 
 454 done:
 455         free(dhcpreq);
 456         free(dhcpreply);
 457         return (err);
 458 }
 459 
 460 /*
 461  * Initialize an IPv6 link-local address on a given interface
 462  */
 463 static int
 464 lxi_iface_ipv6_link_local(const char *iface)
 465 {
 466         struct lifreq lifr;
 467         int s;
 468 
 469         s = socket(AF_INET6, SOCK_DGRAM, 0);
 470         if (s == -1) {
 471                 lxi_warn("socket error %d: bringing up %s: %s",
 472                     errno, iface, strerror(errno));
 473         }
 474 
 475         (void) strncpy(lifr.lifr_name, iface, sizeof (lifr.lifr_name));
 476         if (ioctl(s, SIOCGLIFFLAGS, (caddr_t)&lifr) < 0) {
 477                 lxi_warn("SIOCGLIFFLAGS error %d: bringing up %s: %s",
 478                     errno, iface, strerror(errno));
 479                 return (-1);
 480         }
 481 
 482         lifr.lifr_flags |= IFF_UP;
 483         if (ioctl(s, SIOCSLIFFLAGS, (caddr_t)&lifr) < 0) {
 484                 lxi_warn("SIOCSLIFFLAGS error %d: bringing up %s: %s",
 485                     errno, iface, strerror(errno));
 486                 return (-1);
 487         }
 488 
 489         (void) close(s);
 490         return (0);
 491 }
 492 
 493 static int
 494 lxi_iface_gateway(const char *iface, const char *dst, int dstpfx,
 495     const char *gwaddr)
 496 {
 497         int idx, len, sockfd;
 498         char rtbuf[RTMBUFSZ];
 499         struct rt_msghdr *rtm = (struct rt_msghdr *)rtbuf;
 500         struct sockaddr_in *dst_sin = (struct sockaddr_in *)
 501             (rtbuf + sizeof (struct rt_msghdr));
 502         struct sockaddr_in *gw_sin = (struct sockaddr_in *)(dst_sin + 1);
 503         struct sockaddr_in *netmask_sin = (struct sockaddr_in *)(gw_sin + 1);
 504 
 505         (void) bzero(rtm, RTMBUFSZ);
 506         rtm->rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
 507         rtm->rtm_flags = RTF_UP | RTF_STATIC | RTF_GATEWAY;
 508         rtm->rtm_msglen = sizeof (rtbuf);
 509         rtm->rtm_pid = getpid();
 510         rtm->rtm_type = RTM_ADD;
 511         rtm->rtm_version = RTM_VERSION;
 512 
 513 
 514         /*
 515          * The destination and netmask components have already been zeroed,
 516          * which represents the default gateway.  If we were passed a more
 517          * specific destination network, use that instead.
 518          */
 519         dst_sin->sin_family = AF_INET;
 520         netmask_sin->sin_family = AF_INET;
 521         if (dst != NULL) {
 522                 struct sockaddr *mask = (struct sockaddr *)netmask_sin;
 523 
 524                 if ((inet_pton(AF_INET, dst, &(dst_sin->sin_addr))) != 1 ||
 525                     plen2mask(dstpfx, AF_INET, mask) != 0) {
 526                         lxi_warn("bad destination network %s/%d: %s", dst,
 527                             dstpfx, strerror(errno));
 528                         return (-1);
 529                 }
 530         }
 531 
 532         if ((inet_pton(AF_INET, gwaddr, &(gw_sin->sin_addr))) != 1) {
 533                 lxi_warn("bad gateway %s: %s", gwaddr, strerror(errno));
 534                 return (-1);
 535         }
 536 
 537         if (iface != NULL) {
 538                 if ((idx = if_nametoindex(iface)) == 0) {
 539                         lxi_warn("unable to get interface index for %s: %s\n",
 540                             iface, strerror(errno));
 541                         return (-1);
 542                 }
 543                 rtm->rtm_index = idx;
 544         }
 545 
 546         if ((sockfd = socket(PF_ROUTE, SOCK_RAW, AF_INET)) < 0) {
 547                 lxi_warn("socket(PF_ROUTE): %s\n", strerror(errno));
 548                 return (-1);
 549         }
 550 
 551         if ((len = write(sockfd, rtbuf, rtm->rtm_msglen)) < 0) {
 552                 lxi_warn("could not write rtmsg: %s", strerror(errno));
 553                 close(sockfd);
 554                 return (-1);
 555         } else if (len < rtm->rtm_msglen) {
 556                 lxi_warn("write() rtmsg incomplete");
 557                 close(sockfd);
 558                 return (-1);
 559         }
 560 
 561         close(sockfd);
 562         return (0);
 563 }
 564 
 565 static void
 566 lxi_net_loopback()
 567 {
 568         const char *iface = "lo0";
 569         boolean_t first_ipv4_configured = B_FALSE;
 570 
 571         lxi_net_plumb(iface);
 572         (void) lxi_iface_ip(iface, "127.0.0.1/8", &first_ipv4_configured);
 573         (void) lxi_iface_ipv6_link_local(iface);
 574 }
 575 
 576 
 577 /*
 578  * This function is used when the "ips" property doesn't exist in a zone's
 579  * configuration. It may be an older configuration, so we should search for
 580  * "ip" and "netmask" and convert them into the new format.
 581  */
 582 static int
 583 lxi_get_old_ip(struct zone_res_attrtab *attrs, const char **ipaddrs,
 584     char *cidraddr, int len)
 585 {
 586 
 587         const char *netmask;
 588         int prefixlen;
 589         struct sockaddr_in mask_sin;
 590 
 591         lxi_warn("Could not find \"ips\" property for zone. Looking "
 592             "for older \"ip\" and \"netmask\" properties, instead.");
 593 
 594         if (zone_find_attr(attrs, "ip", ipaddrs) != 0) {
 595                 return (-1);
 596         }
 597 
 598         if (strcmp(*ipaddrs, "dhcp") == 0) {
 599                 return (0);
 600         }
 601 
 602         if (zone_find_attr(attrs, "netmask", &netmask) != 0) {
 603                 lxi_err("could not find netmask for interface");
 604                 /* NOTREACHED */
 605         }
 606 
 607         /* Convert the netmask to a number */
 608         mask_sin.sin_family = AF_INET;
 609         if (inet_pton(AF_INET, netmask, &mask_sin.sin_addr) != 1) {
 610                 lxi_err("invalid netmask address: %s\n",
 611                     strerror(errno));
 612                 /* NOTREACHED */
 613         }
 614         prefixlen = mask2plen((struct sockaddr *)&mask_sin);
 615 
 616         /*
 617          * Write out the IP address in the new format and use
 618          * that instead
 619          */
 620         (void) snprintf(cidraddr, len, "%s/%d", *ipaddrs, prefixlen);
 621 
 622         *ipaddrs = cidraddr;
 623         return (0);
 624 }
 625 
 626 static void
 627 lxi_net_setup(zone_dochandle_t handle)
 628 {
 629         struct zone_nwiftab lookup;
 630         boolean_t do_addrconf = B_FALSE;
 631 
 632         if (zonecfg_setnwifent(handle) != Z_OK)
 633                 return;
 634         while (zonecfg_getnwifent(handle, &lookup) == Z_OK) {
 635                 const char *iface = lookup.zone_nwif_physical;
 636                 struct zone_res_attrtab *attrs = lookup.zone_nwif_attrp;
 637                 const char *ipaddrs, *primary, *gateway;
 638                 char ipaddrs_copy[MAXNAMELEN], cidraddr[BUFSIZ],
 639                     *ipaddr, *tmp, *lasts;
 640                 boolean_t first_ipv4_configured = B_FALSE;
 641                 boolean_t *ficp = &first_ipv4_configured;
 642 
 643                 lxi_net_plumb(iface);
 644                 if (zone_find_attr(attrs, "ips", &ipaddrs) != 0 &&
 645                     lxi_get_old_ip(attrs, &ipaddrs, cidraddr, BUFSIZ) != 0) {
 646                         lxi_warn("Could not find a valid network configuration "
 647                             "for the %s interface", iface);
 648                         continue;
 649                 }
 650 
 651                 if (lxi_iface_ipv6_link_local(iface) != 0) {
 652                         lxi_warn("unable to bring up link-local address on "
 653                             "interface %s", iface);
 654                 }
 655 
 656                 /*
 657                  * If we're going to be doing DHCP, we have to do it first since
 658                  * dhcpagent doesn't like to operate on non-zero logical
 659                  * interfaces.
 660                  */
 661                 if (strstr(ipaddrs, "dhcp") != NULL &&
 662                     lxi_iface_dhcp(iface, ficp) != 0) {
 663                         lxi_warn("Failed to start DHCP on %s\n", iface);
 664                 }
 665 
 666                 /*
 667                  * Copy the ipaddrs string, since strtok_r will write NUL
 668                  * characters into it.
 669                  */
 670                 (void) strlcpy(ipaddrs_copy, ipaddrs, MAXNAMELEN);
 671                 tmp = ipaddrs_copy;
 672 
 673                 /*
 674                  * Iterate over each IP and then set it up on the interface.
 675                  */
 676                 while ((ipaddr = strtok_r(tmp, ",", &lasts)) != NULL) {
 677                         tmp = NULL;
 678                         if (strcmp(ipaddr, "addrconf") == 0) {
 679                                 do_addrconf = B_TRUE;
 680                         } else if (strcmp(ipaddr, "dhcp") == 0) {
 681                                 continue;
 682                         } else if (lxi_iface_ip(iface, ipaddr, ficp) < 0) {
 683                                 lxi_warn("Unable to add new IP address (%s) "
 684                                     "to interface %s", ipaddr, iface);
 685                         }
 686                 }
 687 
 688                 if (zone_find_attr(attrs, "primary", &primary) == 0 &&
 689                     strncmp(primary, "true", MAXNAMELEN) == 0 &&
 690                     zone_find_attr(attrs, "gateway", &gateway) == 0) {
 691                         lxi_iface_gateway(iface, NULL, 0, gateway);
 692                 }
 693         }
 694 
 695         if (do_addrconf) {
 696                 lxi_net_ndpd_start();
 697         }
 698 
 699         (void) zonecfg_endnwifent(handle);
 700 }
 701 
 702 static void
 703 lxi_net_static_route(const char *line)
 704 {
 705         /*
 706          * Each static route line is a string of the form:
 707          *
 708          *      "10.77.77.2|10.1.1.0/24|false"
 709          *
 710          * i.e. gateway address, destination network, and whether this is
 711          * a "link local" route or a next hop route.
 712          */
 713         custr_t *cu = NULL;
 714         char *gw = NULL, *dst = NULL;
 715         int pfx = -1;
 716         int i;
 717 
 718         if (custr_alloc(&cu) != 0) {
 719                 lxi_err("custr_alloc failure");
 720         }
 721 
 722         for (i = 0; line[i] != '\0'; i++) {
 723                 if (gw == NULL) {
 724                         if (line[i] == '|') {
 725                                 if ((gw = strdup(custr_cstr(cu))) == NULL) {
 726                                         lxi_err("strdup failure");
 727                                 }
 728                                 custr_reset(cu);
 729                         } else {
 730                                 if (custr_appendc(cu, line[i]) != 0) {
 731                                         lxi_err("custr_appendc failure");
 732                                 }
 733                         }
 734                         continue;
 735                 }
 736 
 737                 if (dst == NULL) {
 738                         if (line[i] == '/') {
 739                                 if ((dst = strdup(custr_cstr(cu))) == NULL) {
 740                                         lxi_err("strdup failure");
 741                                 }
 742                                 custr_reset(cu);
 743                         } else {
 744                                 if (custr_appendc(cu, line[i]) != 0) {
 745                                         lxi_err("custr_appendc failure");
 746                                 }
 747                         }
 748                         continue;
 749                 }
 750 
 751                 if (pfx == -1) {
 752                         if (line[i] == '|') {
 753                                 pfx = atoi(custr_cstr(cu));
 754                                 custr_reset(cu);
 755                         } else {
 756                                 if (custr_appendc(cu, line[i]) != 0) {
 757                                         lxi_err("custr_appendc failure");
 758                                 }
 759                         }
 760                         continue;
 761                 }
 762 
 763                 if (custr_appendc(cu, line[i]) != 0) {
 764                         lxi_err("custr_appendc failure");
 765                 }
 766         }
 767 
 768         /*
 769          * We currently only support "next hop" routes, so ensure that
 770          * "linklocal" is false:
 771          */
 772         if (strcmp(custr_cstr(cu), "false") != 0) {
 773                 lxi_warn("invalid static route: %s", line);
 774         }
 775 
 776         if (lxi_iface_gateway(NULL, dst, pfx, gw) != 0) {
 777                 lxi_err("failed to add route: %s/%d -> %s", dst, pfx, gw);
 778         }
 779 
 780         custr_free(cu);
 781         free(gw);
 782         free(dst);
 783 }
 784 
 785 static void
 786 lxi_net_static_routes(void)
 787 {
 788         const char *cmd = "/native/usr/lib/brand/lx/routeinfo";
 789         char *const argv[] = { "routeinfo", NULL };
 790         char *const envp[] = { NULL };
 791         int code;
 792         struct stat st;
 793         char errbuf[512];
 794 
 795         if (stat(cmd, &st) != 0 || !S_ISREG(st.st_mode)) {
 796                 /*
 797                  * This binary is (potentially) shipped from another
 798                  * consolidation.  If it does not exist, then the platform does
 799                  * not currently support static routes for LX-branded zones.
 800                  */
 801                 return;
 802         }
 803 
 804         /*
 805          * Run the command, firing the callback for each line that it
 806          * outputs.  When this function returns, static route processing
 807          * is complete.
 808          */
 809         if (run_command(cmd, argv, envp, errbuf, sizeof (errbuf),
 810             lxi_net_static_route, &code) != 0 || code != 0) {
 811                 lxi_err("failed to run \"%s\": %s", cmd, errbuf);
 812         }
 813 }
 814 
 815 static void
 816 lxi_config_close(zone_dochandle_t handle)
 817 {
 818         zonecfg_fini_handle(handle);
 819 }
 820 
 821 static void
 822 lxi_hook_postnet()
 823 {
 824         char cmd[MAXPATHLEN];
 825         const char *zroot = zone_get_nroot();
 826         pid_t pid;
 827         int status;
 828 
 829         (void) snprintf(cmd, sizeof (cmd), "%s%s", zroot, HOOK_POSTNET_PATH);
 830         if (access(cmd, X_OK) != 0) {
 831                 /* If no suitable script is present, soldier on. */
 832                 return;
 833         }
 834 
 835         if ((pid = fork()) < 0) {
 836                 lxi_err("fork() failed: %s", strerror(errno));
 837         }
 838         if (pid == 0) {
 839                 char *const argv[] = { cmd, NULL };
 840                 char *const envp[] = { NULL };
 841 
 842                 /* wire up stderr first, in case the hook wishes to use it */
 843                 if (dup2(1, 2) < 0) {
 844                         lxi_err("dup2() failed: %s", cmd, strerror(errno));
 845                 }
 846 
 847                 /* child executes the hook */
 848                 execve(cmd, argv, envp);
 849 
 850                 /*
 851                  * Since this is running as root, access(2) is less strict than
 852                  * necessary to ensure a successful exec.  If the permissions
 853                  * on the hook are busted, ignore the failure and move on.
 854                  */
 855                 if (errno == EACCES) {
 856                         exit(0);
 857                 }
 858 
 859                 lxi_err("execve(%s) failed: %s", cmd, strerror(errno));
 860                 /* NOTREACHED */
 861         }
 862 
 863         /* Parent waits for the hook to complete */
 864         while (wait(&status) != pid) {
 865                 /* EMPTY */;
 866         }
 867         if (WIFEXITED(status)) {
 868                 if (WEXITSTATUS(status) != 0) {
 869                         lxi_err("%s[%d] exited: %d", cmd, (int)pid,
 870                             WEXITSTATUS(status));
 871                 }
 872         } else if (WIFSIGNALED(status)) {
 873                 lxi_err("%s[%d] died on signal: %d", cmd, (int)pid,
 874                     WTERMSIG(status));
 875         } else {
 876                 lxi_err("%s[%d] failed in unknown way", cmd, (int)pid);
 877         }
 878 }
 879 
 880 static void
 881 lxi_init_exec(char **argv)
 882 {
 883         const char *cmd = "/sbin/init";
 884         char *const envp[] = { "container=zone", NULL };
 885         int e;
 886 
 887         argv[0] = "init";
 888 
 889         /*
 890          * systemd uses the 'container' env var to determine it is running
 891          * inside a container. It only supports a few well-known types and
 892          * treats anything else as 'other' but this is enough to make it
 893          * behave better inside a zone. See 'detect_container' in systemd.
 894          */
 895         execve(cmd, argv, envp);
 896         e = errno;
 897 
 898         /*
 899          * Because stdout was closed prior to exec, it must be opened again in
 900          * the face of failure to log the error.
 901          */
 902         lxi_log_open();
 903         lxi_err("execve(%s) failed: %s", cmd, strerror(e));
 904 }
 905 
 906 /*ARGSUSED*/
 907 int
 908 main(int argc, char *argv[])
 909 {
 910         zone_dochandle_t handle;
 911 
 912         lxi_log_open();
 913 
 914         lxi_net_ipmgmtd_start();
 915         lxi_net_ipadm_open();
 916 
 917         handle = lxi_config_open();
 918         lxi_net_loopback();
 919         lxi_net_setup(handle);
 920         lxi_config_close(handle);
 921 
 922         lxi_net_static_routes();
 923 
 924         lxi_net_ipadm_close();
 925 
 926         lxi_hook_postnet();
 927 
 928         lxi_log_close();
 929 
 930         lxi_init_exec(argv);
 931 
 932         /* NOTREACHED */
 933         return (0);
 934 }