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 : 420 : static int _bf_ns_info_init(struct bf_ns_info *info, const char *name,
32 : : int dir_fd)
33 : : {
34 : 420 : _cleanup_close_ int fd = -1;
35 : : struct stat stats;
36 : : int r;
37 : :
38 : : assert(info);
39 : : assert(name);
40 : :
41 : 420 : fd = openat(dir_fd, name, O_RDONLY, 0);
42 [ - + ]: 420 : if (fd < 0)
43 : 0 : return -errno;
44 : :
45 : 420 : r = fstat(fd, &stats);
46 [ - + ]: 420 : if (r)
47 : 0 : return -errno;
48 : :
49 : 420 : info->fd = TAKE_FD(fd);
50 : 420 : info->inode = stats.st_ino;
51 : :
52 : 420 : return 0;
53 : : }
54 : :
55 : 211 : int bf_ns_init(struct bf_ns *ns, pid_t pid)
56 : : {
57 : 211 : _clean_bf_ns_ struct bf_ns _ns = bf_ns_default();
58 : 211 : _cleanup_close_ int dirfd = -1;
59 : : char ns_dir_path[NS_DIR_PATH_LEN];
60 : : int r;
61 : :
62 : : assert(ns);
63 : :
64 : : /// @todo What if ``/proc`` is not readable?
65 : 211 : (void)snprintf(ns_dir_path, NS_DIR_PATH_LEN, "/proc/%d/ns", pid);
66 : 211 : dirfd = open(ns_dir_path, O_DIRECTORY, O_RDONLY);
67 [ + + ]: 211 : if (dirfd < 0)
68 [ + - ]: 1 : return bf_err_r(errno, "failed to open ns directory '%s'", ns_dir_path);
69 : :
70 : 210 : r = _bf_ns_info_init(&_ns.net, "net", dirfd);
71 [ - + ]: 210 : if (r) {
72 [ # # ]: 0 : return bf_err_r(r, "failed to read 'net' namespace in '%s'",
73 : : ns_dir_path);
74 : : }
75 : :
76 : 210 : r = _bf_ns_info_init(&_ns.mnt, "mnt", dirfd);
77 [ - + ]: 210 : if (r) {
78 [ # # ]: 0 : return bf_err_r(r, "failed to read 'mnt' namespace in '%s'",
79 : : ns_dir_path);
80 : : }
81 : :
82 : 210 : *ns = bf_ns_move(_ns);
83 : :
84 : 210 : return 0;
85 : : }
86 : :
87 : 450 : void bf_ns_clean(struct bf_ns *ns)
88 : : {
89 : : assert(ns);
90 : :
91 : 450 : closep(&ns->net.fd);
92 : 450 : closep(&ns->mnt.fd);
93 : 450 : }
94 : :
95 : 99 : int bf_ns_set(const struct bf_ns *ns, const struct bf_ns *oldns)
96 : : {
97 : : int r;
98 : :
99 [ + + + + ]: 99 : if (!oldns || ns->net.inode != oldns->net.inode) {
100 : 2 : r = setns(ns->net.fd, CLONE_NEWNET);
101 [ - + ]: 2 : if (r)
102 [ # # ]: 0 : return bf_err_r(r, "failed to switch to a network namespace");
103 : : }
104 : :
105 [ + + + + ]: 99 : if (!oldns || ns->mnt.inode != oldns->mnt.inode) {
106 : 2 : r = setns(ns->mnt.fd, CLONE_NEWNS);
107 [ + - ]: 2 : if (r)
108 [ # # ]: 0 : return bf_err_r(r, "failed to switch to a mount namespace");
109 : : }
110 : :
111 : : return 0;
112 : : }
|