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 ---