want lxinit to support link-local routes

   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 lxi_route_send_msg(struct rt_msghdr * rtm)
 494 {
 495     int sockfd, len;
 496 
 497     if ((sockfd = socket(PF_ROUTE, SOCK_RAW, AF_INET)) < 0) {
 498         lxi_warn("socket(PF_ROUTE): %s\n", strerror(errno));
 499         return (-1);
 500     }
 501 
 502     if ((len = write(sockfd, (const void *)rtm, rtm->rtm_msglen)) < 0) {
 503         lxi_warn("could not write rtmsg: %s", strerror(errno));
 504         close(sockfd);
 505         return (-1);
 506     } else if (len < rtm->rtm_msglen) {
 507         lxi_warn("write() rtmsg incomplete");
 508         close(sockfd);
 509         return (-1);
 510     }
 511 
 512     close(sockfd);
 513     return (0);
 514 }
 515 
 516 static int
 517 lxi_iface_gateway(const char *iface, const char *dst, int dstpfx,
 518     const char *gwaddr, int llroute)
 519 {
 520         int idx;
 521         char rtbuf[RTMBUFSZ];
 522         struct rt_msghdr *rtm = (struct rt_msghdr *)rtbuf;
 523         struct sockaddr_in *dst_sin = (struct sockaddr_in *)
 524             (rtbuf + sizeof (struct rt_msghdr));
 525         struct sockaddr_in *gw_sin = (struct sockaddr_in *)(dst_sin + 1);
 526         struct sockaddr_in *netmask_sin = (struct sockaddr_in *)(gw_sin + 1);
 527 
 528         (void) bzero(rtm, RTMBUFSZ);
 529         rtm->rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
 530         
 531         if(!llroute) 
 532           rtm->rtm_flags = RTF_UP | RTF_STATIC | RTF_GATEWAY;
 533         
 534         rtm->rtm_msglen = sizeof (rtbuf);
 535         rtm->rtm_pid = getpid();
 536         rtm->rtm_type = RTM_ADD;
 537         rtm->rtm_version = RTM_VERSION;
 538 
 539 
 540         /*
 541          * The destination and netmask components have already been zeroed,
 542          * which represents the default gateway.  If we were passed a more
 543          * specific destination network, use that instead.
 544          */
 545 
 546         if(dstpfx == -1) {
 547           /*
 548            * no prefix was specified; assume a prefix length of 32,
 549            * which seems in line with the behavior of vmadm. 
 550            */
 551            dstpfx = 32;
 552         }
 553 
 554         dst_sin->sin_family = AF_INET;
 555         netmask_sin->sin_family = AF_INET;
 556         if (dst != NULL) {
 557                 struct sockaddr *mask = (struct sockaddr *)netmask_sin;
 558 
 559                 if ((inet_pton(AF_INET, dst, &(dst_sin->sin_addr))) != 1 ||
 560                     plen2mask(dstpfx, AF_INET, mask) != 0) {
 561                         lxi_warn("bad destination network %s/%d: %s", dst,
 562                             dstpfx, strerror(errno));
 563                         return (-1);
 564                 }
 565         }
 566 
 567         if ((inet_pton(AF_INET, gwaddr, &(gw_sin->sin_addr))) != 1) {
 568                 lxi_warn("bad gateway %s: %s", gwaddr, strerror(errno));
 569                 return (-1);
 570         }
 571 
 572         if (iface != NULL) {
 573                 if ((idx = if_nametoindex(iface)) == 0) {
 574                         lxi_warn("unable to get interface index for %s: %s\n",
 575                             iface, strerror(errno));
 576                         return (-1);
 577                 }
 578                 rtm->rtm_index = idx;
 579         }
 580 
 581         return lxi_route_send_msg(rtm);
















 582 }
 583 
 584 static void
 585 lxi_net_loopback()
 586 {
 587         const char *iface = "lo0";
 588         boolean_t first_ipv4_configured = B_FALSE;
 589 
 590         lxi_net_plumb(iface);
 591         (void) lxi_iface_ip(iface, "127.0.0.1/8", &first_ipv4_configured);
 592         (void) lxi_iface_ipv6_link_local(iface);
 593 }
 594 
 595 
 596 /*
 597  * This function is used when the "ips" property doesn't exist in a zone's
 598  * configuration. It may be an older configuration, so we should search for
 599  * "ip" and "netmask" and convert them into the new format.
 600  */
 601 static int
 602 lxi_get_old_ip(struct zone_res_attrtab *attrs, const char **ipaddrs,
 603     char *cidraddr, int len)
 604 {
 605 
 606         const char *netmask;
 607         int prefixlen;
 608         struct sockaddr_in mask_sin;
 609 
 610         lxi_warn("Could not find \"ips\" property for zone. Looking "
 611             "for older \"ip\" and \"netmask\" properties, instead.");
 612 
 613         if (zone_find_attr(attrs, "ip", ipaddrs) != 0) {
 614                 return (-1);
 615         }
 616 
 617         if (strcmp(*ipaddrs, "dhcp") == 0) {
 618                 return (0);
 619         }
 620 
 621         if (zone_find_attr(attrs, "netmask", &netmask) != 0) {
 622                 lxi_err("could not find netmask for interface");
 623                 /* NOTREACHED */
 624         }
 625 
 626         /* Convert the netmask to a number */
 627         mask_sin.sin_family = AF_INET;
 628         if (inet_pton(AF_INET, netmask, &mask_sin.sin_addr) != 1) {
 629                 lxi_err("invalid netmask address: %s\n",
 630                     strerror(errno));
 631                 /* NOTREACHED */
 632         }
 633         prefixlen = mask2plen((struct sockaddr *)&mask_sin);
 634 
 635         /*
 636          * Write out the IP address in the new format and use
 637          * that instead
 638          */
 639         (void) snprintf(cidraddr, len, "%s/%d", *ipaddrs, prefixlen);
 640 
 641         *ipaddrs = cidraddr;
 642         return (0);
 643 }
 644 
 645 static void
 646 lxi_net_setup(zone_dochandle_t handle)
 647 {
 648         struct zone_nwiftab lookup;
 649         boolean_t do_addrconf = B_FALSE;
 650 
 651         if (zonecfg_setnwifent(handle) != Z_OK)
 652                 return;
 653         while (zonecfg_getnwifent(handle, &lookup) == Z_OK) {
 654                 const char *iface = lookup.zone_nwif_physical;
 655                 struct zone_res_attrtab *attrs = lookup.zone_nwif_attrp;
 656                 const char *ipaddrs;
 657                 char ipaddrs_copy[MAXNAMELEN], cidraddr[BUFSIZ],
 658                     *ipaddr, *tmp, *lasts;
 659                 boolean_t first_ipv4_configured = B_FALSE;
 660                 boolean_t *ficp = &first_ipv4_configured;
 661 
 662                 lxi_net_plumb(iface);
 663                 if (zone_find_attr(attrs, "ips", &ipaddrs) != 0 &&
 664                     lxi_get_old_ip(attrs, &ipaddrs, cidraddr, BUFSIZ) != 0) {
 665                         lxi_warn("Could not find a valid network configuration "
 666                             "for the %s interface", iface);
 667                         continue;
 668                 }
 669 
 670                 if (lxi_iface_ipv6_link_local(iface) != 0) {
 671                         lxi_warn("unable to bring up link-local address on "
 672                             "interface %s", iface);
 673                 }
 674 
 675                 /*
 676                  * If we're going to be doing DHCP, we have to do it first since
 677                  * dhcpagent doesn't like to operate on non-zero logical
 678                  * interfaces.
 679                  */
 680                 if (strstr(ipaddrs, "dhcp") != NULL &&
 681                     lxi_iface_dhcp(iface, ficp) != 0) {
 682                         lxi_warn("Failed to start DHCP on %s\n", iface);
 683                 }
 684 
 685                 /*
 686                  * Copy the ipaddrs string, since strtok_r will write NUL
 687                  * characters into it.
 688                  */
 689                 (void) strlcpy(ipaddrs_copy, ipaddrs, MAXNAMELEN);
 690                 tmp = ipaddrs_copy;
 691 
 692                 /*
 693                  * Iterate over each IP and then set it up on the interface.
 694                  */
 695                 while ((ipaddr = strtok_r(tmp, ",", &lasts)) != NULL) {
 696                         tmp = NULL;
 697                         if (strcmp(ipaddr, "addrconf") == 0) {
 698                                 do_addrconf = B_TRUE;
 699                         } else if (strcmp(ipaddr, "dhcp") == 0) {
 700                                 continue;
 701                         } else if (lxi_iface_ip(iface, ipaddr, ficp) < 0) {
 702                                 lxi_warn("Unable to add new IP address (%s) "
 703                                     "to interface %s", ipaddr, iface);
 704                         }
 705                 }





 706         }

 707 
 708         if (do_addrconf) {
 709                 lxi_net_ndpd_start();
 710         }
 711 
 712         (void) zonecfg_endnwifent(handle);
 713 }
 714 
 715 static void
 716 lxi_net_setup_gateways(zone_dochandle_t handle)
 717 {
 718     struct zone_nwiftab lookup;











 719     
 720     if (zonecfg_setnwifent(handle) != Z_OK)
 721         return;
 722     while (zonecfg_getnwifent(handle, &lookup) == Z_OK) {
 723         const char *iface = lookup.zone_nwif_physical;
 724         struct zone_res_attrtab *attrs = lookup.zone_nwif_attrp;
 725         const char *primary, *gateway;
 726 
 727         if (zone_find_attr(attrs, "primary", &primary) == 0 &&
 728             strncmp(primary, "true", MAXNAMELEN) == 0 &&
 729             zone_find_attr(attrs, "gateway", &gateway) == 0) {
 730             lxi_iface_gateway(iface, NULL, 0, gateway, 0);
 731         }
 732     }
 733 
 734     (void) zonecfg_endnwifent(handle);
 735 }
 736 
 737 static void
 738 lxi_parse_route_line(const char *line, custr_t *cu, char *gw, char *dst, int* pfx, int gwlen, int dstlen)
 739 {
 740      int i;
 741      int havegw = 0, havedst = 0, nopfx = 0;
 742 
 743      for (i = 0; line[i] != '\0'; i++) {
 744         if (!havegw) {
 745             if (line[i] == '|') {
 746                 if ((strlcpy(gw, custr_cstr(cu), gwlen) ) == 0) {
 747                     lxi_err("strlcpy failure");
 748                 } else {
 749                     havegw = 1;
 750                 }
 751                 custr_reset(cu);
 752             } else {
 753                 if (custr_appendc(cu, line[i]) != 0) {
 754                     lxi_err("custr_appendc failure");
 755                 }
 756             }
 757             continue;
 758         }
 759 
 760         if (!havedst) {
 761             if (line[i] == '/' || line[i] == '|') {
 762                 if ((strlcpy(dst, custr_cstr(cu), dstlen)) == 0) {
 763                     lxi_err("strlcpy failure");
 764                 } else {
 765                     havedst = 1;
 766                     if(line[i] == '|') {
 767                       nopfx = 1;
 768                     }
 769                 }
 770                 custr_reset(cu);
 771             } else {
 772                 if (custr_appendc(cu, line[i]) != 0) {
 773                     lxi_err("custr_appendc failure");
 774                 }
 775             }
 776             continue;
 777         }
 778 
 779         if (*pfx == -1 && !nopfx) {
 780             if (line[i] == '|') {
 781                 *pfx = atoi(custr_cstr(cu));
 782                 custr_reset(cu);
 783             } else {
 784                 if (custr_appendc(cu, line[i]) != 0) {
 785                     lxi_err("custr_appendc failure");
 786                 }
 787             }
 788             continue;
 789         }
 790 
 791         if (custr_appendc(cu, line[i]) != 0) {
 792             lxi_err("custr_appendc failure");
 793         }
 794     }
 795 
 796     return;
 797 }
 798 
 799 static void
 800 lxi_net_process_route_line(const char *line, int llonly)
 801 {
 802         /*
 803          * Each static route line is a string of the form:
 804          *
 805          *      "10.77.77.2|10.1.1.0/24|false"
 806          *
 807          * i.e. gateway address, destination network, and whether this is
 808          * a "link local" route or a next hop route.
 809          *
 810          * passing a true value  to the llonly argument to this method causes only 
 811          * those lines with the link-local flag to be processed and vice-versa
 812          */
 813         custr_t *cu = NULL;
 814         int pfx = -1;
 815         char gw[INET6_ADDRSTRLEN];
 816         char dst[INET6_ADDRSTRLEN];
 817 
 818         if (custr_alloc(&cu) != 0) {
 819                 lxi_err("custr_alloc failure");
 820         }
 821 
 822         lxi_parse_route_line(line, cu, gw, dst, &pfx, sizeof(gw), sizeof(dst));
 823 
 824         if (llonly && strcmp(custr_cstr(cu), "true") == 0) {
 825           if (lxi_iface_gateway(NULL, dst, pfx, gw, 1) != 0) {
 826                 lxi_err("failed to add link-local route: %s/%d -> %s", dst, pfx, gw);
 827           }
 828         } else if(!llonly && strcmp(custr_cstr(cu), "false") == 0) {
 829           if (lxi_iface_gateway(NULL, dst, pfx, gw, 0) != 0) {
 830                 lxi_err("failed to add next-hop route: %s/%d -> %s", dst, pfx, gw);
 831           }
 832         } else if(strcmp(custr_cstr(cu), "true") != 0 && strcmp(custr_cstr(cu), "false") != 0) {
 833           /*
 834            * try to be helpful when we encounter something we don't expect.
 835            */
 836           lxi_warn("skipping unknown static route defined in line %s, parsed link-local flag=%s", line, custr_cstr(cu));
 837         }
 838 
 839         custr_free(cu);


 840 }
 841 
 842 static void
 843 lxi_net_static_route(const char * line) {
 844         /* 
 845          * process only those lines where the link-local field is false.
 846          */   
 847         lxi_net_process_route_line(line, 0);
 848 }
 849 
 850 static void 
 851 lxi_net_linklocal_route(const char * line) {
 852         /*
 853          * processes only those lines where the link-local field is true.
 854          */
 855         lxi_net_process_route_line(line, 1); 
 856 }
 857 
 858 static void
 859 lxi_run_routeinfo(void * callback)
 860 {
 861     const char *cmd = "/native/usr/lib/brand/lx/routeinfo";
 862     char *const argv[] = { "routeinfo", NULL };
 863     char *const envp[] = { NULL };
 864     int code;
 865     struct stat st;
 866     char errbuf[512];
 867     
 868     if (stat(cmd, &st) != 0 || !S_ISREG(st.st_mode)) {
 869         /*
 870          * This binary is (potentially) shipped from another
 871          * consolidation.  If it does not exist, then the platform does
 872          * not currently support link-local or static routes for LX-branded zones.
 873          */
 874         return;
 875     }
 876 
 877     /*
 878      * Run the command, firing the callback for each line that it
 879      * outputs.  When this function returns, static route processing
 880      * is complete.
 881      */
 882     if (run_command(cmd, argv, envp, errbuf, sizeof (errbuf),
 883                     callback, &code) != 0 || code != 0) {
 884         lxi_err("failed to run \"%s\": %s", cmd, errbuf);
 885     }
 886 }
 887 
 888 static void
 889 lxi_net_linklocal_routes(void)
 890 {
 891         lxi_run_routeinfo(lxi_net_linklocal_route);
 892 }
 893 
 894 
 895 static void
 896 lxi_net_static_routes(void)
 897 {
 898         lxi_run_routeinfo(lxi_net_static_route);
 899 }
 900 
 901 static void
 902 lxi_config_close(zone_dochandle_t handle)
 903 {
 904         zonecfg_fini_handle(handle);
 905 }
 906 
 907 static void
 908 lxi_hook_postnet()
 909 {
 910         char cmd[MAXPATHLEN];
 911         const char *zroot = zone_get_nroot();
 912         pid_t pid;
 913         int status;
 914 
 915         (void) snprintf(cmd, sizeof (cmd), "%s%s", zroot, HOOK_POSTNET_PATH);
 916         if (access(cmd, X_OK) != 0) {
 917                 /* If no suitable script is present, soldier on. */
 918                 return;
 919         }
 920 
 921         if ((pid = fork()) < 0) {
 922                 lxi_err("fork() failed: %s", strerror(errno));
 923         }
 924         if (pid == 0) {
 925                 char *const argv[] = { cmd, NULL };
 926                 char *const envp[] = { NULL };
 927 
 928                 /* wire up stderr first, in case the hook wishes to use it */
 929                 if (dup2(1, 2) < 0) {
 930                         lxi_err("dup2() failed: %s", cmd, strerror(errno));
 931                 }
 932 
 933                 /* child executes the hook */
 934                 execve(cmd, argv, envp);
 935 
 936                 /*
 937                  * Since this is running as root, access(2) is less strict than
 938                  * necessary to ensure a successful exec.  If the permissions
 939                  * on the hook are busted, ignore the failure and move on.
 940                  */
 941                 if (errno == EACCES) {
 942                         exit(0);
 943                 }
 944 
 945                 lxi_err("execve(%s) failed: %s", cmd, strerror(errno));
 946                 /* NOTREACHED */
 947         }
 948 
 949         /* Parent waits for the hook to complete */
 950         while (wait(&status) != pid) {
 951                 /* EMPTY */;
 952         }
 953         if (WIFEXITED(status)) {
 954                 if (WEXITSTATUS(status) != 0) {
 955                         lxi_err("%s[%d] exited: %d", cmd, (int)pid,
 956                             WEXITSTATUS(status));
 957                 }
 958         } else if (WIFSIGNALED(status)) {
 959                 lxi_err("%s[%d] died on signal: %d", cmd, (int)pid,
 960                     WTERMSIG(status));
 961         } else {
 962                 lxi_err("%s[%d] failed in unknown way", cmd, (int)pid);
 963         }
 964 }
 965 
 966 static void
 967 lxi_init_exec(char **argv)
 968 {
 969         const char *cmd = "/sbin/init";
 970         char *const envp[] = { "container=zone", NULL };
 971         int e;
 972 
 973         argv[0] = "init";
 974 
 975         /*
 976          * systemd uses the 'container' env var to determine it is running
 977          * inside a container. It only supports a few well-known types and
 978          * treats anything else as 'other' but this is enough to make it
 979          * behave better inside a zone. See 'detect_container' in systemd.
 980          */
 981         execve(cmd, argv, envp);
 982         e = errno;
 983 
 984         /*
 985          * Because stdout was closed prior to exec, it must be opened again in
 986          * the face of failure to log the error.
 987          */
 988         lxi_log_open();
 989         lxi_err("execve(%s) failed: %s", cmd, strerror(e));
 990 }
 991 
 992 /*ARGSUSED*/
 993 int
 994 main(int argc, char *argv[])
 995 {
 996         zone_dochandle_t handle;
 997 
 998         lxi_log_open();
 999 
1000         lxi_net_ipmgmtd_start();
1001         lxi_net_ipadm_open();
1002 
1003         handle = lxi_config_open();
1004         lxi_net_loopback();
1005         lxi_net_setup(handle);
1006 
1007         lxi_net_linklocal_routes();
1008         lxi_net_setup_gateways(handle);
1009 
1010         lxi_config_close(handle);
1011 
1012         lxi_net_static_routes();
1013 
1014         lxi_net_ipadm_close();
1015 
1016         lxi_hook_postnet();
1017 
1018         lxi_log_close();
1019 
1020         lxi_init_exec(argv);
1021 
1022         /* NOTREACHED */
1023         return (0);
1024 }
--- EOF ---