@cryptotaxi247 / netdata-1 / commits / 012d456f3

added cgroup-network that can find the network interface of a cgroup, given a pid in it

Costa Tsaousis (ktsaou) committed Jul 30, 2017 at 19:29 UTC 012d456f385f1d629d9110bd0a0aa5bcd52c59e7
9 files changed +364 -4
.gitignore
+2
@@ -109,3 +109,5 @@ diagrams/plantuml.jar
109 netdata.cppcheck
110
111 profile/statsd-stress
112 +src/cgroup-network
113 +vgcore.*
CMakeLists.txt
+19
@@ -188,6 +188,22 @@ set(FREEIPMI_PLUGIN_SOURCE_FILES
188 config.h
189 )
190
191 +set(CGROUP_NETWORK_SOURCE_FILES
192 + src/cgroup-network.c
193 + src/common.c
194 + src/common.h
195 + src/clocks.c
196 + src/clocks.h
197 + src/inlined.h
198 + src/log.c
199 + src/log.h
200 + src/procfile.c
201 + src/procfile.h
202 + src/web_buffer.c
203 + src/web_buffer.h
204 + config.h
205 + )
206 +
207 include_directories(AFTER .)
208
209 add_definitions(-DHAVE_CONFIG_H -DCACHE_DIR="/var/cache/netdata" -DCONFIG_DIR="/etc/netdata" -DLOG_DIR="/var/log/netdata" -DPLUGINS_DIR="/usr/libexec/netdata" -DWEB_DIR="/usr/share/netdata" -DVARLIB_DIR="/var/lib/netdata")
@@ -200,3 +216,6 @@ target_link_libraries (apps.plugin m ${CMAKE_THREAD_LIBS_INIT})
216
217 add_executable(freeipmi.plugin ${FREEIPMI_PLUGIN_SOURCE_FILES})
218 target_link_libraries (freeipmi.plugin ipmimonitoring)
219 +
220 +add_executable(cgroup-network ${CGROUP_NETWORK_SOURCE_FILES})
221 +target_link_libraries (cgroup-network m ${CMAKE_THREAD_LIBS_INIT})
configure.ac
+15 -1
@@ -148,7 +148,6 @@ AC_HEADER_RESOLV
148
149 AC_CHECK_HEADERS_ONCE([sys/prctl.h])
150
151 -
151 # -----------------------------------------------------------------------------
152 # operating system detection
153
@@ -425,6 +424,21 @@ AC_MSG_RESULT([${enable_plugin_nfacct}])
424 AM_CONDITIONAL([ENABLE_PLUGIN_NFACCT], [test "${enable_plugin_nfacct}" = "yes"])
425
426
427 +# -----------------------------------------------------------------------------
428 +# check for setns() - cgroup-network
429 +
430 +AC_CHECK_FUNC([setns])
431 +AC_MSG_CHECKING([if cgroup-network can be enabled])
432 +if test "$ac_cv_func_setns" = "yes" ; then
433 + have_setns="yes"
434 + AC_DEFINE([HAVE_SETNS], [1], [Define 1 if you have setns() function])
435 +else
436 + have_setns="no"
437 +fi
438 +AC_MSG_RESULT([${have_setns}])
439 +AM_CONDITIONAL([ENABLE_PLUGIN_CGROUP_NETWORK], [test "${have_setns}" = "yes"])
440 +
441 +
442 # -----------------------------------------------------------------------------
443 # Link-Time-Optimization
444
makeself/install-or-update.sh
+1 -1
@@ -138,7 +138,7 @@ run chown -R ${NETDATA_USER}:${NETDATA_GROUP} /opt/netdata
138 # -----------------------------------------------------------------------------
139 progress "fix plugin permissions"
140
141 -for x in apps.plugin freeipmi.plugin
141 +for x in apps.plugin freeipmi.plugin cgroup-network
142 do
143 f="usr/libexec/netdata/plugins.d/${x}"
144
netdata-installer.sh
+6
@@ -782,6 +782,12 @@ if [ ${UID} -eq 0 ]
782 run chmod 4755 "${NETDATA_PREFIX}/usr/libexec/netdata/plugins.d/freeipmi.plugin"
783 fi
784
785 + if [ -f "${NETDATA_PREFIX}/usr/libexec/netdata/plugins.d/cgroup-network" ]
786 + then
787 + run chown root "${NETDATA_PREFIX}/usr/libexec/netdata/plugins.d/cgroup-network"
788 + run chmod 4755 "${NETDATA_PREFIX}/usr/libexec/netdata/plugins.d/cgroup-network"
789 + fi
790 +
791 else
792 run chown "${NETDATA_USER}:${NETDATA_USER}" "${NETDATA_LOG_DIR}"
793 run chown -R "${NETDATA_USER}:${NETDATA_USER}" "${NETDATA_PREFIX}/usr/libexec/netdata"
src/Makefile.am
+19
@@ -37,6 +37,10 @@ if ENABLE_PLUGIN_FREEIPMI
37 plugins_PROGRAMS += freeipmi.plugin
38 endif
39
40 +if ENABLE_PLUGIN_CGROUP_NETWORK
41 +plugins_PROGRAMS += cgroup-network
42 +endif
43 +
44 netdata_SOURCES = \
45 adaptive_resortable_list.c \
46 adaptive_resortable_list.h \
@@ -244,3 +248,18 @@ freeipmi_plugin_SOURCES = \
248 freeipmi_plugin_LDADD = \
249 $(OPTIONAL_IPMIMONITORING_LIBS) \
250 $(NULL)
251 +
252 +cgroup_network_SOURCES = \
253 + cgroup-network.c \
254 + clocks.c clocks.h \
255 + common.c common.h \
256 + inlined.h \
257 + log.c log.h \
258 + procfile.c procfile.h \
259 + web_buffer.c web_buffer.h \
260 + $(NULL)
261 +
262 +cgroup_network_LDADD = \
263 + $(OPTIONAL_MATH_LIBS) \
264 + $(OPTIONAL_LIBCAP_LIBS) \
265 + $(NULL)
src/apps_plugin.c
+1 -1
@@ -3197,7 +3197,7 @@ static void parse_args(int argc, char **argv)
3197 }
3198 }
3199
3200 - if(strcmp("version", argv[i]) == 0 || strcmp("-v", argv[i]) == 0 || strcmp("-V", argv[i]) == 0) {
3200 + if(strcmp("version", argv[i]) == 0 || strcmp("-version", argv[i]) == 0 || strcmp("--version", argv[i]) == 0 || strcmp("-v", argv[i]) == 0 || strcmp("-V", argv[i]) == 0) {
3201 printf("apps.plugin %s\n", VERSION);
3202 exit(0);
3203 }
src/cgroup-network.c new
+300
@@ -0,0 +1,300 @@
1 +#include "common.h"
2 +
3 +#ifdef HAVE_SETNS
4 +#ifndef _GNU_SOURCE
5 +#define _GNU_SOURCE /* See feature_test_macros(7) */
6 +#endif
7 +#include <sched.h>
8 +#endif
9 +
10 +// ----------------------------------------------------------------------------
11 +// callback required by fatal()
12 +
13 +void netdata_cleanup_and_exit(int ret) {
14 + exit(ret);
15 +}
16 +
17 +struct iface {
18 + const char *device;
19 + uint32_t hash;
20 +
21 + unsigned int ifindex;
22 + unsigned int iflink;
23 +
24 + struct iface *next;
25 +};
26 +
27 +unsigned int read_iface_iflink(const char *prefix, const char *iface) {
28 + char filename[FILENAME_MAX + 1];
29 + snprintfz(filename, FILENAME_MAX, "%s/sys/class/net/%s/iflink", prefix?prefix:"", iface);
30 +
31 + unsigned long long iflink = 0;
32 + int ret = read_single_number_file(filename, &iflink);
33 + if(ret) error("Cannot read '%s'.", filename);
34 +
35 + return (unsigned int)iflink;
36 +}
37 +
38 +unsigned int read_iface_ifindex(const char *prefix, const char *iface) {
39 + char filename[FILENAME_MAX + 1];
40 + snprintfz(filename, FILENAME_MAX, "%s/sys/class/net/%s/ifindex", prefix?prefix:"", iface);
41 +
42 + unsigned long long ifindex = 0;
43 + int ret = read_single_number_file(filename, &ifindex);
44 + if(ret) error("Cannot read '%s'.", filename);
45 +
46 + return (unsigned int)ifindex;
47 +}
48 +
49 +struct iface *read_proc_net_dev(const char *prefix) {
50 + procfile *ff = NULL;
51 + char filename[FILENAME_MAX + 1];
52 +
53 + snprintfz(filename, FILENAME_MAX, "%s%s", prefix?prefix:"", "/proc/net/dev");
54 + ff = procfile_open(filename, " \t,:|", PROCFILE_FLAG_DEFAULT);
55 + if(unlikely(!ff)) {
56 + error("Cannot open file '%s'", filename);
57 + return NULL;
58 + }
59 +
60 + ff = procfile_readall(ff);
61 + if(unlikely(!ff)) {
62 + error("Cannot read file '%s'", filename);
63 + return NULL;
64 + }
65 +
66 + size_t lines = procfile_lines(ff), l;
67 + struct iface *root = NULL;
68 + for(l = 2; l < lines ;l++) {
69 + if (unlikely(procfile_linewords(ff, l) < 1)) continue;
70 +
71 + struct iface *t = callocz(1, sizeof(struct iface));
72 + t->device = strdupz(procfile_lineword(ff, l, 0));
73 + t->hash = simple_hash(t->device);
74 + t->ifindex = read_iface_ifindex(prefix, t->device);
75 + t->iflink = read_iface_iflink(prefix, t->device);
76 + t->next = root;
77 + root = t;
78 + }
79 +
80 + return root;
81 +}
82 +
83 +inline int iface_is_eligible(struct iface *iface) {
84 + if(iface->iflink != iface->ifindex)
85 + return 1;
86 +
87 + return 0;
88 +}
89 +
90 +int eligible_ifaces(struct iface *root) {
91 + int eligible = 0;
92 +
93 + struct iface *t;
94 + for(t = root; t ; t = t->next)
95 + if(iface_is_eligible(t))
96 + eligible++;
97 +
98 + return eligible;
99 +}
100 +
101 +static void continue_as_child(void) {
102 + pid_t child = fork();
103 + int status;
104 + pid_t ret;
105 +
106 + if (child < 0)
107 + error("fork() failed");
108 +
109 + /* Only the child returns */
110 + if (child == 0)
111 + return;
112 +
113 + for (;;) {
114 + ret = waitpid(child, &status, WUNTRACED);
115 + if ((ret == child) && (WIFSTOPPED(status))) {
116 + /* The child suspended so suspend us as well */
117 + kill(getpid(), SIGSTOP);
118 + kill(child, SIGCONT);
119 + } else {
120 + break;
121 + }
122 + }
123 +
124 + /* Return the child's exit code if possible */
125 + if (WIFEXITED(status)) {
126 + exit(WEXITSTATUS(status));
127 + } else if (WIFSIGNALED(status)) {
128 + kill(getpid(), WTERMSIG(status));
129 + }
130 +
131 + exit(EXIT_FAILURE);
132 +}
133 +
134 +int proc_pid_fd(const char *prefix, const char *ns, pid_t pid) {
135 + char filename[FILENAME_MAX + 1];
136 + snprintfz(filename, FILENAME_MAX, "%s/proc/%d/%s", prefix?prefix:"", (int)pid, ns);
137 + int fd = open(filename, O_RDONLY);
138 +
139 + if(fd == -1)
140 + error("Cannot open file '%s'", filename);
141 +
142 + return fd;
143 +}
144 +
145 +static struct ns {
146 + int nstype;
147 + int fd;
148 + int status;
149 + const char *name;
150 + const char *path;
151 +} all_ns[] = {
152 + // { .nstype = CLONE_NEWUSER, .fd = -1, .status = -1, .name = "user", .path = "ns/user" },
153 + // { .nstype = CLONE_NEWCGROUP, .fd = -1, .status = -1, .name = "cgroup", .path = "ns/cgroup" },
154 + // { .nstype = CLONE_NEWIPC, .fd = -1, .status = -1, .name = "ipc", .path = "ns/ipc" },
155 + // { .nstype = CLONE_NEWUTS, .fd = -1, .status = -1, .name = "uts", .path = "ns/uts" },
156 + { .nstype = CLONE_NEWNET, .fd = -1, .status = -1, .name = "network", .path = "ns/net" },
157 + { .nstype = CLONE_NEWPID, .fd = -1, .status = -1, .name = "pid", .path = "ns/pid" },
158 + { .nstype = CLONE_NEWNS, .fd = -1, .status = -1, .name = "mount", .path = "ns/mnt" },
159 +
160 + // terminator
161 + { .nstype = 0, .fd = -1, .status = -1, .name = NULL, .path = NULL }
162 +};
163 +
164 +int switch_namespace(const char *prefix, pid_t pid) {
165 +#ifdef HAVE_SETNS
166 +
167 + int i;
168 + for(i = 0; all_ns[i].name ; i++)
169 + all_ns[i].fd = proc_pid_fd(prefix, all_ns[i].path, pid);
170 +
171 + int root_fd = proc_pid_fd(prefix, "root", pid);
172 + int cwd_fd = proc_pid_fd(prefix, "cwd", pid);
173 +
174 + setgroups(0, NULL);
175 +
176 + // 2 passes - found it at nsenter source code
177 + // this is related CLONE_NEWUSER functionality
178 +
179 + int pass, errors = 0;
180 + for(pass = 0; pass < 2 ;pass++) {
181 + for(i = 0; all_ns[i].name ; i++) {
182 + if (all_ns[i].fd != -1 && all_ns[i].status == -1) {
183 + if(setns(all_ns[i].fd, all_ns[i].nstype) == -1) {
184 + if(pass == 1) {
185 + all_ns[i].status = 0;
186 + error("Cannot switch to %s namespace of pid %d", all_ns[i].name, (int) pid);
187 + errors++;
188 + }
189 + }
190 + else
191 + all_ns[i].status = 1;
192 + }
193 + }
194 + }
195 +
196 + setgroups(0, NULL);
197 +
198 + if(root_fd != -1) {
199 + if(fchdir(root_fd) < 0)
200 + error("Cannot fchdir() to pid %d root directory", (int)pid);
201 +
202 + if(chroot(".") < 0)
203 + error("Cannot chroot() to pid %d root directory", (int)pid);
204 +
205 + close(root_fd);
206 + }
207 +
208 + if(cwd_fd != -1) {
209 + if(fchdir(cwd_fd) < 0)
210 + error("Cannot fchdir() to pid %d current working directory", (int)pid);
211 +
212 + close(cwd_fd);
213 + }
214 +
215 + int do_fork = 0;
216 + for(i = 0; all_ns[i].name ; i++)
217 + if(all_ns[i].fd != -1) {
218 +
219 + // CLONE_NEWPID requires a fork() to become effective
220 + if(all_ns[i].nstype == CLONE_NEWPID && all_ns[i].status)
221 + do_fork = 1;
222 +
223 + close(all_ns[i].fd);
224 + }
225 +
226 + if(do_fork)
227 + continue_as_child();
228 +
229 + return 0;
230 +
231 +#else
232 +
233 + errno = ENOSYS;
234 + error("setns() is missing on this system.");
235 + return 1;
236 +
237 +#endif
238 +}
239 +
240 +void usage(void) {
241 + fprintf(stderr, "%s -p PID\n", program_name);
242 + exit(1);
243 +}
244 +
245 +int main(int argc, char **argv) {
246 + pid_t pid = 0;
247 +
248 + program_name = argv[0];
249 + program_version = VERSION;
250 + error_log_syslog = 0;
251 +
252 + if(argc == 2 && (!strcmp(argv[1], "version") || !strcmp(argv[1], "-version") || !strcmp(argv[1], "--version") || !strcmp(argv[1], "-v") || !strcmp(argv[1], "-V"))) {
253 + fprintf(stderr, "cgroup-network %s\n", VERSION);
254 + exit(0);
255 + }
256 +
257 + if(argc != 3 || strcmp(argv[1], "-p") != 0)
258 + usage();
259 +
260 + pid = atoi(argv[2]);
261 + if(pid <= 0)
262 + fatal("Invalid pid %d", (int)pid);
263 +
264 + struct iface *host, *cgroup, *h, *c;
265 + const char *prefix = getenv("NETDATA_HOST_PREFIX");
266 +
267 + host = read_proc_net_dev(prefix);
268 + if(!host)
269 + fatal("cannot read host interface list.");
270 +
271 + if(!eligible_ifaces(host))
272 + fatal("there are no double-linked host interfaces available.");
273 +
274 + if(switch_namespace(prefix, pid))
275 + fatal("cannot switch to the namespace of pid %u", (unsigned int)pid);
276 +
277 + cgroup = read_proc_net_dev(NULL);
278 + if(!cgroup)
279 + fatal("cannot read cgroup interface list.");
280 +
281 + if(!eligible_ifaces(cgroup))
282 + fatal("there are not double-linked cgroup interfaces available.");
283 +
284 + int found = 0;
285 + for(h = host; h ; h = h->next) {
286 + if(iface_is_eligible(h)) {
287 + for (c = cgroup; c; c = c->next) {
288 + if(iface_is_eligible(c) && h->ifindex == c->iflink && h->iflink == c->ifindex) {
289 + printf("%s\n", h->device);
290 + found++;
291 + }
292 + }
293 + }
294 + }
295 +
296 + if(!found)
297 + return 1;
298 +
299 + return 0;
300 +}
src/freeipmi_plugin.c
+1 -1
@@ -1433,7 +1433,7 @@ int main (int argc, char **argv) {
1433 continue;
1434 }
1435 }
1436 - else if(strcmp("version", argv[i]) == 0 || strcmp("-v", argv[i]) == 0 || strcmp("-V", argv[i]) == 0) {
1436 + else if(strcmp("version", argv[i]) == 0 || strcmp("-version", argv[i]) == 0 || strcmp("--version", argv[i]) == 0 || strcmp("-v", argv[i]) == 0 || strcmp("-V", argv[i]) == 0) {
1437 printf("freeipmi.plugin %s\n", VERSION);
1438 exit(0);
1439 }