1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /*
13 * Copyright (c) 2021 Joyent, Inc.
14 */
15
16 /*
17 * virtual arp daemon -- varpd
18 *
19 * The virtual arp daemon is the user land counterpart to the overlay driver. To
20 * truly understand its purpose and how it fits into things, you should read the
21 * overlay big theory statement in uts/common/io/overlay/overlay.c.
22 *
23 * varpd's purpose it to provide a means for looking up the destination on the
24 * underlay network for a host on an overlay network and to also be a door
25 * server such that dladm(8) via libdladm can configure and get useful status
26 * information. The heavy lifting is all done by libvarpd and the various lookup
27 * plugins.
28 *
29 * When varpd first starts up, we take care of chdiring into /var/run/varpd,
30 * which is also where we create /var/run/varpd/varpd.door, our door server.
31 * After that we daemonize and only after we daemonize do we go ahead and load
32 * plugins. The reason that we don't load plugins before daemonizing is that
33 * they could very well be creating threads and thus lose them all. In general,
34 * we want to make things easier on our children and not require them to be
35 * fork safe.
36 *
37 * Once it's spun up, the main varpd thread sits in sigsuspend and really just
38 * hangs out waiting for something, libvarpd handles everything else.
39 */
40
41 #include <libvarpd.h>
42 #include <stdio.h>
43 #include <unistd.h>
44 #include <string.h>
45 #include <signal.h>
46 #include <sys/types.h>
47 #include <sys/stat.h>
48 #include <fcntl.h>
49 #include <errno.h>
50 #include <libgen.h>
51 #include <stdarg.h>
52 #include <stdlib.h>
53 #include <paths.h>
54 #include <limits.h>
55 #include <sys/corectl.h>
56 #include <signal.h>
57 #include <strings.h>
58 #include <sys/wait.h>
59 #include <unistd.h>
60 #include <thread.h>
61 #include <priv.h>
62 #include <libscf.h>
63
64 #define VARPD_EXIT_REQUESTED SMF_EXIT_OK
65 #define VARPD_EXIT_FATAL SMF_EXIT_ERR_FATAL
66 #define VARPD_EXIT_USAGE SMF_EXIT_ERR_CONFIG
67
68 #define VARPD_RUNDIR "/var/run/varpd"
69 #define VARPD_DEFAULT_DOOR "/var/run/varpd/varpd.door"
70
71 #define VARPD_PG "varpd"
72 #define VARPD_PROP_INC "include_path"
73
74 static varpd_handle_t *varpd_handle;
75 static const char *varpd_pname;
76 static volatile boolean_t varpd_exit = B_FALSE;
77
78 /*
79 * Debug builds are automatically wired up for umem debugging.
80 */
81 #ifdef DEBUG
82 const char *
_umem_debug_init()83 _umem_debug_init()
84 {
85 return ("default,verbose");
86 }
87
88 const char *
_umem_logging_init(void)89 _umem_logging_init(void)
90 {
91 return ("fail,contents");
92 }
93 #endif /* DEBUG */
94
95 static void
varpd_vwarn(FILE * out,const char * fmt,va_list ap)96 varpd_vwarn(FILE *out, const char *fmt, va_list ap)
97 {
98 int error = errno;
99
100 (void) fprintf(out, "%s: ", varpd_pname);
101 (void) vfprintf(out, fmt, ap);
102
103 if (fmt[strlen(fmt) - 1] != '\n')
104 (void) fprintf(out, ": %s\n", strerror(error));
105 }
106
107 static void
varpd_fatal(const char * fmt,...)108 varpd_fatal(const char *fmt, ...)
109 {
110 va_list ap;
111
112 va_start(ap, fmt);
113 varpd_vwarn(stderr, fmt, ap);
114 va_end(ap);
115
116 exit(VARPD_EXIT_FATAL);
117 }
118
119 static void
varpd_dfatal(int dfd,const char * fmt,...)120 varpd_dfatal(int dfd, const char *fmt, ...)
121 {
122 int status = VARPD_EXIT_FATAL;
123 va_list ap;
124
125 va_start(ap, fmt);
126 varpd_vwarn(stdout, fmt, ap);
127 va_end(ap);
128
129 /* Take a single shot at this */
130 (void) write(dfd, &status, sizeof (status));
131 exit(status);
132 }
133
134 /* ARGSUSED */
135 static int
varpd_plugin_walk_cb(varpd_handle_t * vph,const char * name,void * unused)136 varpd_plugin_walk_cb(varpd_handle_t *vph, const char *name, void *unused)
137 {
138 (void) printf("loaded %s!\n", name);
139 return (0);
140 }
141
142 static int
varpd_dir_setup(void)143 varpd_dir_setup(void)
144 {
145 int fd;
146
147 if (mkdir(VARPD_RUNDIR, 0700) != 0) {
148 if (errno != EEXIST)
149 varpd_fatal("failed to create %s: %s", VARPD_RUNDIR,
150 strerror(errno));
151 }
152
153 fd = open(VARPD_RUNDIR, O_RDONLY);
154 if (fd < 0)
155 varpd_fatal("failed to open %s: %s", VARPD_RUNDIR,
156 strerror(errno));
157
158 if (fchown(fd, UID_NETADM, GID_NETADM) != 0)
159 varpd_fatal("failed to chown %s: %s\n", VARPD_RUNDIR,
160 strerror(errno));
161
162 return (fd);
163 }
164
165 /*
166 * Because varpd is generally run under SMF, we opt to keep its stdout and
167 * stderr to be whatever our parent set them up to be.
168 */
169 static void
varpd_fd_setup(void)170 varpd_fd_setup(void)
171 {
172 int dupfd;
173
174 closefrom(STDERR_FILENO + 1);
175 dupfd = open(_PATH_DEVNULL, O_RDONLY);
176 if (dupfd < 0)
177 varpd_fatal("failed to open %s: %s", _PATH_DEVNULL,
178 strerror(errno));
179 if (dup2(dupfd, STDIN_FILENO) == -1)
180 varpd_fatal("failed to dup out stdin: %s", strerror(errno));
181 }
182
183 /*
184 * We borrow fmd's daemonization style. Basically, the parent waits for the
185 * child to successfully set up a door and recover all of the old configurations
186 * before we say that we're good to go.
187 */
188 static int
varpd_daemonize(int dirfd)189 varpd_daemonize(int dirfd)
190 {
191 char path[PATH_MAX];
192 struct rlimit rlim;
193 sigset_t set, oset;
194 int estatus, pfds[2];
195 pid_t child;
196 priv_set_t *pset;
197
198 /*
199 * Set a per-process core path to be inside of /var/run/varpd. Make sure
200 * that we aren't limited in our dump size.
201 */
202 (void) snprintf(path, sizeof (path),
203 "/var/run/varpd/core.%s.%%p", varpd_pname);
204 (void) core_set_process_path(path, strlen(path) + 1, getpid());
205
206 rlim.rlim_cur = RLIM_INFINITY;
207 rlim.rlim_max = RLIM_INFINITY;
208 (void) setrlimit(RLIMIT_CORE, &rlim);
209
210 /*
211 * Claim as many file descriptors as the system will let us.
212 */
213 if (getrlimit(RLIMIT_NOFILE, &rlim) == 0) {
214 rlim.rlim_cur = rlim.rlim_max;
215 (void) setrlimit(RLIMIT_NOFILE, &rlim);
216 }
217
218 /*
219 * chdir /var/run/varpd
220 */
221 if (fchdir(dirfd) != 0)
222 varpd_fatal("failed to chdir to %s", VARPD_RUNDIR);
223
224
225 /*
226 * At this point block all signals going in so we don't have the parent
227 * mistakingly exit when the child is running, but never block SIGABRT.
228 */
229 if (sigfillset(&set) != 0)
230 abort();
231 if (sigdelset(&set, SIGABRT) != 0)
232 abort();
233 if (sigprocmask(SIG_BLOCK, &set, &oset) != 0)
234 abort();
235
236 /*
237 * Do the fork+setsid dance.
238 */
239 if (pipe(pfds) != 0)
240 varpd_fatal("failed to create pipe for daemonizing");
241
242 if ((child = fork()) == -1)
243 varpd_fatal("failed to fork for daemonizing");
244
245 if (child != 0) {
246 /* We'll be exiting shortly, so allow for silent failure */
247 (void) close(pfds[1]);
248 if (read(pfds[0], &estatus, sizeof (estatus)) ==
249 sizeof (estatus))
250 _exit(estatus);
251
252 if (waitpid(child, &estatus, 0) == child && WIFEXITED(estatus))
253 _exit(WEXITSTATUS(estatus));
254
255 _exit(VARPD_EXIT_FATAL);
256 }
257
258 /*
259 * Drop privileges here.
260 *
261 * We should make sure we keep around PRIV_NET_PRIVADDR and
262 * PRIV_SYS_DLCONFIG, but drop everything else; however, keep basic
263 * privs and have our child drop them.
264 *
265 * We should also run as netadm:netadm and drop all of our groups.
266 */
267 if (setgroups(0, NULL) != 0)
268 abort();
269 if (setgid(GID_NETADM) == -1 || seteuid(UID_NETADM) == -1)
270 abort();
271 if ((pset = priv_allocset()) == NULL)
272 abort();
273 priv_basicset(pset);
274 if (priv_delset(pset, PRIV_PROC_EXEC) == -1 ||
275 priv_delset(pset, PRIV_PROC_INFO) == -1 ||
276 priv_delset(pset, PRIV_PROC_FORK) == -1 ||
277 priv_delset(pset, PRIV_PROC_SESSION) == -1 ||
278 priv_delset(pset, PRIV_FILE_LINK_ANY) == -1 ||
279 priv_addset(pset, PRIV_SYS_DL_CONFIG) == -1 ||
280 priv_addset(pset, PRIV_NET_PRIVADDR) == -1) {
281 abort();
282 }
283 /*
284 * Remove privs from the permitted set. That will cause them to be
285 * removed from the effective set. We want to make sure that in the case
286 * of a vulnerability, something can't get back in here and wreak more
287 * havoc. But if we want non-basic privs in the effective set, we have
288 * to request them explicitly.
289 */
290 if (setppriv(PRIV_SET, PRIV_PERMITTED, pset) == -1)
291 abort();
292 if (setppriv(PRIV_SET, PRIV_EFFECTIVE, pset) == -1)
293 abort();
294
295 priv_freeset(pset);
296
297 if (close(pfds[0]) != 0)
298 abort();
299 if (setsid() == -1)
300 abort();
301 if (sigprocmask(SIG_SETMASK, &oset, NULL) != 0)
302 abort();
303 (void) umask(0022);
304
305 return (pfds[1]);
306 }
307
308 static int
varpd_setup_lookup_threads(void)309 varpd_setup_lookup_threads(void)
310 {
311 int ret;
312 long i, ncpus = sysconf(_SC_NPROCESSORS_ONLN) * 2 + 1;
313
314 if (ncpus <= 0)
315 abort();
316 for (i = 0; i < ncpus; i++) {
317 thread_t thr;
318
319 ret = thr_create(NULL, 0, libvarpd_overlay_lookup_run,
320 varpd_handle, THR_DETACHED | THR_DAEMON, &thr);
321 if (ret != 0)
322 return (ret);
323 }
324
325 return (0);
326 }
327
328 static void
varpd_cleanup(int signal __unused)329 varpd_cleanup(int signal __unused)
330 {
331 varpd_exit = B_TRUE;
332 }
333
334 /*
335 * Load default information from SMF and apply any of if necessary. We recognize
336 * the following properties:
337 *
338 * varpd/include_path Treat these as a series of -i options.
339 *
340 * If we're not under SMF, just move on.
341 */
342 static void
varpd_load_smf(int dfd)343 varpd_load_smf(int dfd)
344 {
345 char *fmri, *inc;
346 scf_simple_prop_t *prop;
347
348 if ((fmri = getenv("SMF_FMRI")) == NULL)
349 return;
350
351 if ((prop = scf_simple_prop_get(NULL, fmri, VARPD_PG,
352 VARPD_PROP_INC)) == NULL)
353 return;
354
355 while ((inc = scf_simple_prop_next_astring(prop)) != NULL) {
356 int err = libvarpd_plugin_load(varpd_handle, inc);
357 if (err != 0) {
358 varpd_dfatal(dfd, "failed to load from %s: %s\n",
359 inc, strerror(err));
360 }
361 }
362
363 scf_simple_prop_free(prop);
364 }
365
366 /*
367 * There are a bunch of things we need to do to be a proper daemon here.
368 *
369 * o Ensure that /var/run/varpd exists or create it
370 * o make stdin /dev/null (stdout?)
371 * o Ensure any other fds that we somehow inherited are closed, eg.
372 * closefrom()
373 * o Properly daemonize
374 * o Mask all signals except sigabrt before creating our first door -- all
375 * other doors will inherit from that.
376 * o Have the main thread sigsuspend looking for most things that are
377 * actionable...
378 */
379 int
main(int argc,char * argv[])380 main(int argc, char *argv[])
381 {
382 int err, c, dirfd, dfd, i;
383 const char *doorpath = VARPD_DEFAULT_DOOR;
384 sigset_t set;
385 struct sigaction act;
386 int nincpath = 0, nextincpath = 0;
387 char **incpath = NULL;
388
389 varpd_pname = basename(argv[0]);
390
391 /*
392 * We want to clean up our file descriptors before we do anything else
393 * as we can't assume that libvarpd won't open file descriptors, etc.
394 */
395 varpd_fd_setup();
396
397 if ((err = libvarpd_create(&varpd_handle)) != 0) {
398 varpd_fatal("failed to open a libvarpd handle");
399 return (1);
400 }
401
402 while ((c = getopt(argc, argv, ":i:d:")) != -1) {
403 switch (c) {
404 case 'i':
405 if (nextincpath == nincpath) {
406 if (nincpath == 0)
407 nincpath = 16;
408 else
409 nincpath *= 2;
410 incpath = realloc(incpath, sizeof (char *) *
411 nincpath);
412 if (incpath == NULL) {
413 (void) fprintf(stderr, "failed to "
414 "allocate memory for the %dth "
415 "-I option: %s\n", nextincpath + 1,
416 strerror(errno));
417 }
418
419 }
420 incpath[nextincpath] = optarg;
421 nextincpath++;
422 break;
423 case 'd':
424 doorpath = optarg;
425 break;
426 default:
427 (void) fprintf(stderr, "unknown option: %c\n", c);
428 return (1);
429 }
430 }
431
432 dirfd = varpd_dir_setup();
433
434 (void) libvarpd_plugin_walk(varpd_handle, varpd_plugin_walk_cb, NULL);
435
436 dfd = varpd_daemonize(dirfd);
437
438 /*
439 * Now that we're in the child, go ahead and load all of our plug-ins.
440 * We do this, in part, because these plug-ins may need threads of their
441 * own and fork won't preserve those and we'd rather the plug-ins don't
442 * have to learn about fork-handlers.
443 */
444 for (i = 0; i < nextincpath; i++) {
445 err = libvarpd_plugin_load(varpd_handle, incpath[i]);
446 if (err != 0) {
447 varpd_dfatal(dfd, "failed to load from %s: %s\n",
448 incpath[i], strerror(err));
449 }
450 }
451
452 varpd_load_smf(dfd);
453
454 if ((err = libvarpd_persist_enable(varpd_handle, VARPD_RUNDIR)) != 0)
455 varpd_dfatal(dfd, "failed to enable varpd persistence: %s\n",
456 strerror(err));
457
458 if ((err = libvarpd_persist_restore(varpd_handle)) != 0)
459 varpd_dfatal(dfd, "failed to enable varpd persistence: %s\n",
460 strerror(err));
461
462 /*
463 * The ur-door thread will inherit from this signal mask. So set it to
464 * what we want before doing anything else. In addition, so will our
465 * threads that handle varpd lookups.
466 */
467 if (sigfillset(&set) != 0)
468 varpd_dfatal(dfd, "failed to fill a signal set...");
469
470 if (sigdelset(&set, SIGABRT) != 0)
471 varpd_dfatal(dfd, "failed to unmask SIGABRT");
472
473 if (sigprocmask(SIG_BLOCK, &set, NULL) != 0)
474 varpd_dfatal(dfd, "failed to set our door signal mask");
475
476 if ((err = varpd_setup_lookup_threads()) != 0)
477 varpd_dfatal(dfd, "failed to create lookup threads: %s\n",
478 strerror(err));
479
480 if ((err = libvarpd_door_server_create(varpd_handle, doorpath)) != 0)
481 varpd_dfatal(dfd, "failed to create door server at %s: %s\n",
482 doorpath, strerror(err));
483
484 /*
485 * At this point, finish up signal initialization and finally go ahead,
486 * notify the parent that we're okay, and enter the sigsuspend loop.
487 */
488 bzero(&act, sizeof (struct sigaction));
489 act.sa_handler = varpd_cleanup;
490 if (sigfillset(&act.sa_mask) != 0)
491 varpd_dfatal(dfd, "failed to fill sigaction mask");
492 act.sa_flags = 0;
493 if (sigaction(SIGHUP, &act, NULL) != 0)
494 varpd_dfatal(dfd, "failed to register HUP handler");
495 if (sigdelset(&set, SIGHUP) != 0)
496 varpd_dfatal(dfd, "failed to remove HUP from mask");
497 if (sigaction(SIGQUIT, &act, NULL) != 0)
498 varpd_dfatal(dfd, "failed to register QUIT handler");
499 if (sigdelset(&set, SIGQUIT) != 0)
500 varpd_dfatal(dfd, "failed to remove QUIT from mask");
501 if (sigaction(SIGINT, &act, NULL) != 0)
502 varpd_dfatal(dfd, "failed to register INT handler");
503 if (sigdelset(&set, SIGINT) != 0)
504 varpd_dfatal(dfd, "failed to remove INT from mask");
505 if (sigaction(SIGTERM, &act, NULL) != 0)
506 varpd_dfatal(dfd, "failed to register TERM handler");
507 if (sigdelset(&set, SIGTERM) != 0)
508 varpd_dfatal(dfd, "failed to remove TERM from mask");
509
510 err = 0;
511 (void) write(dfd, &err, sizeof (err));
512 (void) close(dfd);
513
514 for (;;) {
515 if (sigsuspend(&set) == -1)
516 if (errno == EFAULT)
517 abort();
518 if (varpd_exit == B_TRUE)
519 break;
520 }
521
522 libvarpd_door_server_destroy(varpd_handle);
523 libvarpd_destroy(varpd_handle);
524
525 return (VARPD_EXIT_REQUESTED);
526 }
527