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