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