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 (c) 2010, Oracle and/or its affiliates. All rights reserved. 24 * Copyright 2021, Tintri by DDN. All rights reserved. 25 */ 26 27 /* 28 * The ipmgmtd daemon is started by ip-interface-management SMF service. This 29 * daemon is used to manage, mapping of 'address object' to 'interface name' and 30 * 'logical interface number', on which the address is created. It also provides 31 * a means to update the ipadm persistent data-store. 32 * 33 * The daemon tracks the <addrobj, lifname> mapping in-memory using a linked 34 * list `aobjmap'. Access to this list is synchronized using a readers-writers 35 * lock. The active <addrobj, lifname> mapping is kept in 36 * /etc/svc/volatile/ipadm/aobjmap.conf cache file, so that the mapping can be 37 * recovered when ipmgmtd exits for some reason (e.g., when ipmgmtd is restarted 38 * using svcadm or accidentally killed). 39 * 40 * Today, the persistent configuration of interfaces, addresses and protocol 41 * properties is kept in /etc/ipadm/ipadm.conf. The access to the persistent 42 * data store is synchronized using reader-writers lock `ipmgmt_dbconf_lock'. 43 * 44 * The communication between the library, libipadm.so and the daemon, is through 45 * doors RPC. The library interacts with the daemon using the commands defined 46 * by `ipmgmt_door_cmd_type_t'. Further any 'write' operation would require 47 * the `NETWORK_INTERFACE_CONFIG_AUTH' authorization. 48 * 49 * On reboot, the aforementioned SMF service starts the daemon before any other 50 * networking service that configures network IP interfaces is started. 51 * Afterwards, the network/physical SMF script instantiates the persisted 52 * network interfaces, interface properties and addresses. 53 */ 54 55 #include <errno.h> 56 #include <fcntl.h> 57 #include <priv_utils.h> 58 #include <signal.h> 59 #include <stdlib.h> 60 #include <stdio.h> 61 #include <strings.h> 62 #include <sys/param.h> 63 #include <sys/stat.h> 64 #include <unistd.h> 65 #include "ipmgmt_impl.h" 66 #include <zone.h> 67 #include <libipadm.h> 68 #include <libdladm.h> 69 #include <libdllink.h> 70 #include <net/route.h> 71 #include <ipadm_ipmgmt.h> 72 #include <sys/brand.h> 73 74 const char *progname; 75 76 /* readers-writers lock for reading/writing daemon data store */ 77 pthread_rwlock_t ipmgmt_dbconf_lock = PTHREAD_RWLOCK_INITIALIZER; 78 79 /* tracks address object to {ifname|logical number|interface id} mapping */ 80 ipmgmt_aobjmap_list_t aobjmap; 81 82 /* used to communicate failure to parent process, which spawned the daemon */ 83 static int pfds[2]; 84 85 /* file descriptor to IPMGMT_DOOR */ 86 static int ipmgmt_door_fd = -1; 87 88 static void ipmgmt_exit(int); 89 static int ipmgmt_init(); 90 static int ipmgmt_init_privileges(); 91 static void ipmgmt_ngz_persist_if(); 92 93 static ipadm_handle_t iph; 94 typedef struct ipmgmt_pif_s { 95 struct ipmgmt_pif_s *pif_next; 96 char pif_ifname[LIFNAMSIZ]; 97 boolean_t pif_v4; 98 boolean_t pif_v6; 99 } ipmgmt_pif_t; 100 101 static ipmgmt_pif_t *ngz_pifs; 102 103 static int 104 ipmgmt_db_init() 105 { 106 int fd, err, scferr; 107 scf_resources_t res; 108 boolean_t upgrade = B_TRUE; 109 110 /* 111 * Check to see if we need to upgrade the data-store. We need to 112 * upgrade, if the version of the data-store does not match with 113 * IPADM_DB_VERSION. Further, if we cannot determine the current 114 * version of the data-store, we always err on the side of caution 115 * and upgrade the data-store to current version. 116 */ 117 if ((scferr = ipmgmt_create_scf_resources(IPMGMTD_FMRI, &res)) == 0) 118 upgrade = ipmgmt_needs_upgrade(&res); 119 if (upgrade) { 120 err = ipmgmt_db_walk(ipmgmt_db_upgrade, NULL, IPADM_DB_WRITE); 121 if (err != 0) { 122 ipmgmt_log(LOG_ERR, "could not upgrade the " 123 "ipadm data-store: %s", strerror(err)); 124 err = 0; 125 } else { 126 /* 127 * upgrade was success, let's update SCF with the 128 * current data-store version number. 129 */ 130 if (scferr == 0) 131 ipmgmt_update_dbver(&res); 132 } 133 } 134 if (scferr == 0) 135 ipmgmt_release_scf_resources(&res); 136 137 /* creates the address object data store, if it doesn't exist */ 138 if ((fd = open(ADDROBJ_MAPPING_DB_FILE, O_CREAT|O_RDONLY, 139 IPADM_FILE_MODE)) == -1) { 140 err = errno; 141 ipmgmt_log(LOG_ERR, "could not open %s: %s", 142 ADDROBJ_MAPPING_DB_FILE, strerror(err)); 143 return (err); 144 } 145 (void) close(fd); 146 147 aobjmap.aobjmap_head = NULL; 148 (void) pthread_rwlock_init(&aobjmap.aobjmap_rwlock, NULL); 149 150 /* 151 * If the daemon is recovering from a crash or restart, read the 152 * address object to logical interface mapping and build an in-memory 153 * representation of the mapping. That is, build `aobjmap' structure 154 * from address object data store. 155 */ 156 if ((err = ipadm_rw_db(ipmgmt_aobjmap_init, NULL, 157 ADDROBJ_MAPPING_DB_FILE, 0, IPADM_DB_READ)) != 0) { 158 /* if there was nothing to initialize, it's fine */ 159 if (err != ENOENT) 160 return (err); 161 err = 0; 162 } 163 164 ipmgmt_ngz_persist_if(); /* create persistent interface info for NGZ */ 165 166 return (err); 167 } 168 169 static int 170 ipmgmt_door_init() 171 { 172 int fd; 173 int err; 174 175 /* create the door file for ipmgmtd */ 176 if ((fd = open(IPMGMT_DOOR, O_CREAT|O_RDONLY, IPADM_FILE_MODE)) == -1) { 177 err = errno; 178 ipmgmt_log(LOG_ERR, "could not open %s: %s", 179 IPMGMT_DOOR, strerror(err)); 180 return (err); 181 } 182 (void) close(fd); 183 184 if ((ipmgmt_door_fd = door_create(ipmgmt_handler, NULL, 185 DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) == -1) { 186 err = errno; 187 ipmgmt_log(LOG_ERR, "failed to create door: %s", strerror(err)); 188 return (err); 189 } 190 /* 191 * fdetach first in case a previous daemon instance exited 192 * ungracefully. 193 */ 194 (void) fdetach(IPMGMT_DOOR); 195 if (fattach(ipmgmt_door_fd, IPMGMT_DOOR) != 0) { 196 err = errno; 197 ipmgmt_log(LOG_ERR, "failed to attach door to %s: %s", 198 IPMGMT_DOOR, strerror(err)); 199 goto fail; 200 } 201 return (0); 202 fail: 203 (void) door_revoke(ipmgmt_door_fd); 204 ipmgmt_door_fd = -1; 205 return (err); 206 } 207 208 static void 209 ipmgmt_door_fini() 210 { 211 if (ipmgmt_door_fd == -1) 212 return; 213 214 (void) fdetach(IPMGMT_DOOR); 215 if (door_revoke(ipmgmt_door_fd) == -1) { 216 ipmgmt_log(LOG_ERR, "failed to revoke access to door %s: %s", 217 IPMGMT_DOOR, strerror(errno)); 218 } 219 } 220 221 static int 222 ipmgmt_init() 223 { 224 int err; 225 226 if (signal(SIGTERM, ipmgmt_exit) == SIG_ERR || 227 signal(SIGINT, ipmgmt_exit) == SIG_ERR) { 228 err = errno; 229 ipmgmt_log(LOG_ERR, "signal() for SIGTERM/INT failed: %s", 230 strerror(err)); 231 return (err); 232 } 233 if ((err = ipmgmt_db_init()) != 0 || (err = ipmgmt_door_init()) != 0) 234 return (err); 235 return (0); 236 } 237 238 /* 239 * This is called by the child process to inform the parent process to 240 * exit with the given return value. 241 */ 242 static void 243 ipmgmt_inform_parent_exit(int rv) 244 { 245 if (write(pfds[1], &rv, sizeof (int)) != sizeof (int)) { 246 ipmgmt_log(LOG_WARNING, 247 "failed to inform parent process of status: %s", 248 strerror(errno)); 249 (void) close(pfds[1]); 250 exit(EXIT_FAILURE); 251 } 252 (void) close(pfds[1]); 253 } 254 255 /*ARGSUSED*/ 256 static void 257 ipmgmt_exit(int signo) 258 { 259 (void) close(pfds[1]); 260 ipmgmt_door_fini(); 261 exit(EXIT_FAILURE); 262 } 263 264 /* 265 * On the first reboot after installation of an ipkg zone, 266 * ipmgmt_persist_if_cb() is used in non-global zones to track the interfaces 267 * that have IP address configuration assignments from the global zone. 268 * Persistent configuration for the interfaces is created on the first boot 269 * by ipmgmtd, and the addresses assigned to the interfaces by the GZ 270 * will be subsequently configured when the interface is enabled. 271 * Note that ipmgmt_persist_if_cb() only sets up a list of interfaces 272 * that need to be persisted- the actual update of the ipadm data-store happens 273 * in ipmgmt_persist_if() after the appropriate privs/uid state has been set up. 274 */ 275 static void 276 ipmgmt_persist_if_cb(char *ifname, boolean_t v4, boolean_t v6) 277 { 278 ipmgmt_pif_t *pif; 279 280 pif = calloc(1, sizeof (*pif)); 281 if (pif == NULL) { 282 ipmgmt_log(LOG_WARNING, 283 "Could not allocate memory to configure %s", ifname); 284 return; 285 } 286 (void) strlcpy(pif->pif_ifname, ifname, sizeof (pif->pif_ifname)); 287 pif->pif_v4 = v4; 288 pif->pif_v6 = v6; 289 pif->pif_next = ngz_pifs; 290 ngz_pifs = pif; 291 } 292 293 /* 294 * ipmgmt_ngz_init() initializes exclusive-IP stack non-global zones by 295 * extracting configuration that has been saved in the kernel and applying 296 * it at zone boot. 297 */ 298 static void 299 ipmgmt_ngz_init() 300 { 301 zoneid_t zoneid; 302 boolean_t firstboot = B_TRUE, s10c = B_FALSE; 303 char brand[MAXNAMELEN]; 304 ipadm_status_t ipstatus; 305 306 zoneid = getzoneid(); 307 if (zoneid != GLOBAL_ZONEID) { 308 309 if (zone_getattr(zoneid, ZONE_ATTR_BRAND, brand, 310 sizeof (brand)) < 0) { 311 ipmgmt_log(LOG_ERR, "Could not get brand name"); 312 return; 313 } 314 /* 315 * firstboot is always true for S10C zones, where ipadm is not 316 * available for restoring persistent configuration. 317 */ 318 if (strcmp(brand, NATIVE_BRAND_NAME) == 0) 319 firstboot = ipmgmt_ngz_firstboot_postinstall(); 320 else 321 s10c = B_TRUE; 322 323 if (!firstboot) 324 return; 325 326 ipstatus = ipadm_open(&iph, IPH_IPMGMTD); 327 if (ipstatus != IPADM_SUCCESS) { 328 ipmgmt_log(LOG_ERR, "could not open ipadm handle", 329 ipadm_status2str(ipstatus)); 330 return; 331 } 332 /* 333 * Only pass down the callback to persist the interface 334 * for NATIVE (ipkg) zones. 335 */ 336 (void) ipadm_init_net_from_gz(iph, NULL, 337 (s10c ? NULL : ipmgmt_persist_if_cb)); 338 ipadm_close(iph); 339 } 340 } 341 342 /* 343 * Set the uid of this daemon to the "netadm" user. Finish the following 344 * operations before setuid() because they need root privileges: 345 * 346 * - create the /etc/svc/volatile/ipadm directory; 347 * - change its uid/gid to "netadm"/"netadm"; 348 */ 349 static int 350 ipmgmt_init_privileges() 351 { 352 struct stat statbuf; 353 int err; 354 355 /* create the IPADM_TMPFS_DIR directory */ 356 if (stat(IPADM_TMPFS_DIR, &statbuf) < 0) { 357 if (mkdir(IPADM_TMPFS_DIR, (mode_t)0755) < 0) { 358 err = errno; 359 goto fail; 360 } 361 } else { 362 if ((statbuf.st_mode & S_IFMT) != S_IFDIR) { 363 err = ENOTDIR; 364 goto fail; 365 } 366 } 367 368 if ((chmod(IPADM_TMPFS_DIR, 0755) < 0) || 369 (chown(IPADM_TMPFS_DIR, UID_NETADM, GID_NETADM) < 0)) { 370 err = errno; 371 goto fail; 372 } 373 374 /* 375 * initialize any NGZ specific network information before dropping 376 * privileges. We need these privileges to plumb IP interfaces handed 377 * down from the GZ (for dlpi_open() etc.) and also to configure the 378 * address itself (for any IPI_PRIV ioctls like SLIFADDR) 379 */ 380 ipmgmt_ngz_init(); 381 382 /* 383 * Apply all protocol module properties. We need to apply all protocol 384 * properties before we drop root privileges. 385 */ 386 ipmgmt_init_prop(); 387 388 /* 389 * limit the privileges of this daemon and set the uid of this 390 * daemon to UID_NETADM 391 */ 392 if (__init_daemon_priv(PU_RESETGROUPS|PU_CLEARLIMITSET, UID_NETADM, 393 GID_NETADM, NULL) == -1) { 394 err = EPERM; 395 goto fail; 396 } 397 398 return (0); 399 fail: 400 (void) ipmgmt_log(LOG_ERR, "failed to initialize the daemon: %s", 401 strerror(err)); 402 return (err); 403 } 404 405 /* 406 * Keep the pfds fd open, close other fds. 407 */ 408 /*ARGSUSED*/ 409 static int 410 closefunc(void *arg, int fd) 411 { 412 if (fd != pfds[1]) 413 (void) close(fd); 414 return (0); 415 } 416 417 /* 418 * We cannot use libc's daemon() because the door we create is associated with 419 * the process ID. If we create the door before the call to daemon(), it will 420 * be associated with the parent and it's incorrect. On the other hand if we 421 * create the door later, after the call to daemon(), parent process exits 422 * early and gives a false notion to SMF that 'ipmgmtd' is up and running, 423 * which is incorrect. So, we have our own daemon() equivalent. 424 */ 425 static boolean_t 426 ipmgmt_daemonize(void) 427 { 428 pid_t pid; 429 int rv; 430 431 if (pipe(pfds) < 0) { 432 (void) fprintf(stderr, "%s: pipe() failed: %s\n", 433 progname, strerror(errno)); 434 exit(EXIT_FAILURE); 435 } 436 437 if ((pid = fork()) == -1) { 438 (void) fprintf(stderr, "%s: fork() failed: %s\n", 439 progname, strerror(errno)); 440 exit(EXIT_FAILURE); 441 } else if (pid > 0) { /* Parent */ 442 (void) close(pfds[1]); 443 444 /* 445 * Parent should not exit early, it should wait for the child 446 * to return Success/Failure. If the parent exits early, then 447 * SMF will think 'ipmgmtd' is up and would start all the 448 * depended services. 449 * 450 * If the child process exits unexpectedly, read() returns -1. 451 */ 452 if (read(pfds[0], &rv, sizeof (int)) != sizeof (int)) { 453 (void) kill(pid, SIGKILL); 454 rv = EXIT_FAILURE; 455 } 456 457 (void) close(pfds[0]); 458 exit(rv); 459 } 460 461 /* Child */ 462 (void) close(pfds[0]); 463 (void) setsid(); 464 465 /* close all files except pfds[1] */ 466 (void) fdwalk(closefunc, NULL); 467 (void) chdir("/"); 468 openlog(progname, LOG_PID, LOG_DAEMON); 469 return (B_TRUE); 470 } 471 472 int 473 main(int argc, char *argv[]) 474 { 475 int opt; 476 boolean_t fg = B_FALSE; 477 478 progname = strrchr(argv[0], '/'); 479 if (progname != NULL) 480 progname++; 481 else 482 progname = argv[0]; 483 484 /* Process options */ 485 while ((opt = getopt(argc, argv, "f")) != EOF) { 486 switch (opt) { 487 case 'f': 488 fg = B_TRUE; 489 break; 490 default: 491 (void) fprintf(stderr, "Usage: %s [-f]\n", progname); 492 return (EXIT_FAILURE); 493 } 494 } 495 496 if (!fg && getenv("SMF_FMRI") == NULL) { 497 (void) fprintf(stderr, 498 "ipmgmtd is a smf(5) managed service and cannot be run " 499 "from the command line.\n"); 500 return (EINVAL); 501 } 502 503 if (!fg && !ipmgmt_daemonize()) 504 return (EXIT_FAILURE); 505 506 if (ipmgmt_init_privileges() != 0) 507 goto child_out; 508 509 if (ipmgmt_init() != 0) 510 goto child_out; 511 512 /* Inform the parent process that it can successfully exit */ 513 ipmgmt_inform_parent_exit(EXIT_SUCCESS); 514 515 for (;;) 516 (void) pause(); 517 518 child_out: 519 /* return from main() forcibly exits an MT process */ 520 ipmgmt_inform_parent_exit(EXIT_FAILURE); 521 return (EXIT_FAILURE); 522 } 523 524 /* 525 * Return TRUE if `ifname' has persistent configuration for the `af' address 526 * family in the datastore 527 */ 528 static boolean_t 529 ipmgmt_persist_if_exists(char *ifname, sa_family_t af) 530 { 531 ipmgmt_getif_cbarg_t cbarg; 532 boolean_t exists = B_FALSE; 533 ipadm_if_info_t *ifp; 534 535 bzero(&cbarg, sizeof (cbarg)); 536 cbarg.cb_ifname = ifname; 537 (void) ipmgmt_db_walk(ipmgmt_db_getif, &cbarg, IPADM_DB_READ); 538 if (cbarg.cb_ifinfo != NULL) { 539 ifp = &cbarg.cb_ifinfo->ifil_ifi; 540 if ((af == AF_INET && (ifp->ifi_pflags & IFIF_IPV4)) || 541 (af == AF_INET6 && (ifp->ifi_pflags & IFIF_IPV6))) { 542 exists = B_TRUE; 543 } 544 } 545 free(cbarg.cb_ifinfo); 546 return (exists); 547 } 548 549 /* 550 * Persist any NGZ interfaces assigned to us from the global zone if they do 551 * not already exist in the persistent db. We need to 552 * do this before any calls to ipadm_enable_if() can succeed (i.e., 553 * before opening up for door_calls), and after setuid to 'netadm' so that 554 * the persistent db is created with the right permissions. 555 */ 556 static void 557 ipmgmt_ngz_persist_if() 558 { 559 ipmgmt_pif_t *pif, *next; 560 ipmgmt_if_arg_t ifarg; 561 562 for (pif = ngz_pifs; pif != NULL; pif = next) { 563 next = pif->pif_next; 564 bzero(&ifarg, sizeof (ifarg)); 565 (void) strlcpy(ifarg.ia_ifname, pif->pif_ifname, 566 sizeof (ifarg.ia_ifname)); 567 ifarg.ia_flags = IPMGMT_PERSIST; 568 if (pif->pif_v4 && 569 !ipmgmt_persist_if_exists(pif->pif_ifname, AF_INET)) { 570 ifarg.ia_family = AF_INET; 571 (void) ipmgmt_persist_if(&ifarg); 572 } 573 if (pif->pif_v6 && 574 !ipmgmt_persist_if_exists(pif->pif_ifname, AF_INET6)) { 575 ifarg.ia_family = AF_INET6; 576 (void) ipmgmt_persist_if(&ifarg); 577 } 578 free(pif); 579 } 580 ngz_pifs = NULL; /* no red herrings */ 581 } 582