Branch data Line data Source code
1 : : /* SPDX-License-Identifier: GPL-2.0-only */
2 : : /*
3 : : * Copyright (c) 2023 Meta Platforms, Inc. and affiliates.
4 : : */
5 : :
6 : : #define _GNU_SOURCE
7 : :
8 : : #include "bpfilter/ns.h"
9 : :
10 : : #include <errno.h>
11 : : #include <fcntl.h>
12 : : #include <sched.h>
13 : : #include <stdio.h>
14 : : #include <sys/stat.h>
15 : : #include <unistd.h>
16 : :
17 : : #include "bpfilter/helper.h"
18 : : #include "bpfilter/logger.h"
19 : :
20 : : #define NS_DIR_PATH_LEN 32
21 : :
22 : : /**
23 : : * Initialize a `bf_ns_info` structure for a given namespace.
24 : : *
25 : : * @param info `bf_ns_info` object to initialise. On failure, this parameter is
26 : : * unchanged. Can't be NULL.
27 : : * @param name Name of the namespace to open. Can't be NULL.
28 : : * @param dir_fd File descriptor of the directory to open the namespace from.
29 : : * @return 0 on success, or a negative errno value on failure.
30 : : */
31 : 252 : static int _bf_ns_info_init(struct bf_ns_info *info, const char *name,
32 : : int dir_fd)
33 : : {
34 : 252 : _cleanup_close_ int fd = -1;
35 : : struct stat stats;
36 : : int r;
37 : :
38 : : bf_assert(info && name);
39 : :
40 : 252 : fd = openat(dir_fd, name, O_RDONLY, 0);
41 [ - + ]: 252 : if (fd < 0)
42 : 0 : return -errno;
43 : :
44 : 252 : r = fstat(fd, &stats);
45 [ - + ]: 252 : if (r)
46 : 0 : return -errno;
47 : :
48 : 252 : info->fd = TAKE_FD(fd);
49 : 252 : info->inode = stats.st_ino;
50 : :
51 : 252 : return 0;
52 : : }
53 : :
54 : 127 : int bf_ns_init(struct bf_ns *ns, pid_t pid)
55 : : {
56 : 127 : _clean_bf_ns_ struct bf_ns _ns = bf_ns_default();
57 : 127 : _cleanup_close_ int dirfd = -1;
58 : : char ns_dir_path[NS_DIR_PATH_LEN];
59 : : int r;
60 : :
61 : : bf_assert(ns);
62 : :
63 : : /// @todo What if ``/proc`` is not readable?
64 : 127 : (void)snprintf(ns_dir_path, NS_DIR_PATH_LEN, "/proc/%d/ns", pid);
65 : 127 : dirfd = open(ns_dir_path, O_DIRECTORY, O_RDONLY);
66 [ + + ]: 127 : if (dirfd < 0)
67 [ + - ]: 1 : return bf_err_r(errno, "failed to open ns directory '%s'", ns_dir_path);
68 : :
69 : 126 : r = _bf_ns_info_init(&_ns.net, "net", dirfd);
70 [ - + ]: 126 : if (r) {
71 [ # # ]: 0 : return bf_err_r(r, "failed to read 'net' namespace in '%s'",
72 : : ns_dir_path);
73 : : }
74 : :
75 : 126 : r = _bf_ns_info_init(&_ns.mnt, "mnt", dirfd);
76 [ - + ]: 126 : if (r) {
77 [ # # ]: 0 : return bf_err_r(r, "failed to read 'mnt' namespace in '%s'",
78 : : ns_dir_path);
79 : : }
80 : :
81 : 126 : *ns = bf_ns_move(_ns);
82 : :
83 : 126 : return 0;
84 : : }
85 : :
86 : 273 : void bf_ns_clean(struct bf_ns *ns)
87 : : {
88 : : bf_assert(ns);
89 : :
90 : 273 : closep(&ns->net.fd);
91 : 273 : closep(&ns->mnt.fd);
92 : 273 : }
93 : :
94 : 49 : int bf_ns_set(const struct bf_ns *ns, const struct bf_ns *oldns)
95 : : {
96 : : int r;
97 : :
98 [ + + + + ]: 49 : if (!oldns || ns->net.inode != oldns->net.inode) {
99 : 2 : r = setns(ns->net.fd, CLONE_NEWNET);
100 [ - + ]: 2 : if (r)
101 [ # # ]: 0 : return bf_err_r(r, "failed to switch to a network namespace");
102 : : }
103 : :
104 [ + + + + ]: 49 : if (!oldns || ns->mnt.inode != oldns->mnt.inode) {
105 : 2 : r = setns(ns->mnt.fd, CLONE_NEWNS);
106 [ + - ]: 2 : if (r)
107 [ # # ]: 0 : return bf_err_r(r, "failed to switch to a mount namespace");
108 : : }
109 : :
110 : : return 0;
111 : : }
|