basic functionality for renaming network interfaces
Costa Tsaousis (ktsaou) committed
Jul 31, 2017 at 00:46 UTC
19a3ac9e03c46986f3fa487e8fb2ee9ac61aafce
5 files changed
+315
-52
src/cgroup-network.c
+41
-3
@@ -176,6 +176,9 @@ int switch_namespace(const char *prefix, pid_t pid) {
176
// 2 passes - found it at nsenter source code
177
// this is related CLONE_NEWUSER functionality
178
179
+ // FIXME: this code cannot switch user namespace
180
+ // Fortunately, we don't need it.
181
+
182
int pass, errors = 0;
183
for(pass = 0; pass < 2 ;pass++) {
184
for(i = 0; all_ns[i].name ; i++) {
@@ -237,8 +240,35 @@ int switch_namespace(const char *prefix, pid_t pid) {
240
#endif
241
}
242
243
+pid_t read_pid_from_cgroup(const char *path) {
244
+ char buffer[FILENAME_MAX + 1];
245
+
246
+ snprintfz(buffer, FILENAME_MAX, "%s/cgroup.procs", path);
247
+ FILE *fp = fopen(buffer, "r");
248
+ if(!fp) {
249
+ error("Cannot read file '%s'.", buffer);
250
+ snprintfz(buffer, FILENAME_MAX, "%s/tasks", path);
251
+ fp = fopen(buffer, "r");
252
+ }
253
+
254
+ if(!fp) {
255
+ error("Cannot read file '%s'.", buffer);
256
+ return 0;
257
+ }
258
+
259
+ pid_t pid = 0;
260
+ char *s;
261
+ while((s = fgets(buffer, FILENAME_MAX, fp))) {
262
+ pid = atoi(s);
263
+ if(pid > 0) break;
264
+ }
265
+
266
+ fclose(fp);
267
+ return pid;
268
+}
269
+
270
void usage(void) {
241
- fprintf(stderr, "%s -p PID\n", program_name);
271
+ fprintf(stderr, "%s [ -p PID | --pid PID | --cgroup /path/to/cgroup ]\n", program_name);
272
exit(1);
273
}
274
@@ -254,10 +284,18 @@ int main(int argc, char **argv) {
284
exit(0);
285
}
286
257
- if(argc != 3 || strcmp(argv[1], "-p") != 0)
287
+ if(argc != 3)
288
+ usage();
289
+
290
+ if(!strcmp(argv[1], "-p") || !strcmp(argv[1], "--pid")) {
291
+ pid = atoi(argv[2]);
292
+ }
293
+ else if(!strcmp(argv[1], "--cgroup")) {
294
+ pid = read_pid_from_cgroup(argv[2]);
295
+ }
296
+ else
297
usage();
298
260
- pid = atoi(argv[2]);
299
if(pid <= 0)
300
fatal("Invalid pid %d", (int)pid);
301
src/freebsd_getifaddrs.c
+11
-11
@@ -2,7 +2,7 @@
2
3
#include <ifaddrs.h>
4
5
-struct network_interface {
5
+struct cgroup_network_interface {
6
char *name;
7
uint32_t hash;
8
size_t len;
@@ -41,14 +41,14 @@ struct network_interface {
41
RRDSET *st_events;
42
RRDDIM *rd_events_coll;
43
44
- struct network_interface *next;
44
+ struct cgroup_network_interface *next;
45
};
46
47
-static struct network_interface *network_interfaces_root = NULL, *network_interfaces_last_used = NULL;
47
+static struct cgroup_network_interface *network_interfaces_root = NULL, *network_interfaces_last_used = NULL;
48
49
static size_t network_interfaces_added = 0, network_interfaces_found = 0;
50
51
-static void network_interface_free(struct network_interface *ifm) {
51
+static void network_interface_free(struct cgroup_network_interface *ifm) {
52
if (likely(ifm->st_bandwidth))
53
rrdset_is_obsolete(ifm->st_bandwidth);
54
if (likely(ifm->st_packets))
@@ -68,7 +68,7 @@ static void network_interface_free(struct network_interface *ifm) {
68
static void network_interfaces_cleanup() {
69
if (likely(network_interfaces_found == network_interfaces_added)) return;
70
71
- struct network_interface *ifm = network_interfaces_root, *last = NULL;
71
+ struct cgroup_network_interface *ifm = network_interfaces_root, *last = NULL;
72
while(ifm) {
73
if (unlikely(!ifm->updated)) {
74
// info("Removing network interface '%s', linked after '%s'", ifm->name, last?last->name:"ROOT");
@@ -76,7 +76,7 @@ static void network_interfaces_cleanup() {
76
if (network_interfaces_last_used == ifm)
77
network_interfaces_last_used = last;
78
79
- struct network_interface *t = ifm;
79
+ struct cgroup_network_interface *t = ifm;
80
81
if (ifm == network_interfaces_root || !last)
82
network_interfaces_root = ifm = ifm->next;
@@ -95,8 +95,8 @@ static void network_interfaces_cleanup() {
95
}
96
}
97
98
-static struct network_interface *get_network_interface(const char *name) {
99
- struct network_interface *ifm;
98
+static struct cgroup_network_interface *get_network_interface(const char *name) {
99
+ struct cgroup_network_interface *ifm;
100
101
uint32_t hash = simple_hash(name);
102
@@ -117,7 +117,7 @@ static struct network_interface *get_network_interface(const char *name) {
117
}
118
119
// create a new one
120
- ifm = callocz(1, sizeof(struct network_interface));
120
+ ifm = callocz(1, sizeof(struct cgroup_network_interface));
121
ifm->name = strdupz(name);
122
ifm->hash = simple_hash(ifm->name);
123
ifm->len = strlen(ifm->name);
@@ -125,7 +125,7 @@ static struct network_interface *get_network_interface(const char *name) {
125
126
// link it to the end
127
if (network_interfaces_root) {
128
- struct network_interface *e;
128
+ struct cgroup_network_interface *e;
129
for(e = network_interfaces_root; e->next ; e = e->next) ;
130
e->next = ifm;
131
}
@@ -288,7 +288,7 @@ int do_getifaddrs(int update_every, usec_t dt) {
288
if (ifa->ifa_addr->sa_family != AF_LINK)
289
continue;
290
291
- struct network_interface *ifm = get_network_interface(ifa->ifa_name);
291
+ struct cgroup_network_interface *ifm = get_network_interface(ifa->ifa_name);
292
ifm->updated = 1;
293
network_interfaces_found++;
294
src/plugin_proc.h
+4
@@ -32,4 +32,8 @@ extern int get_numa_node_count(void);
32
// metrics that need to be shared among data collectors
33
extern unsigned long long tcpext_TCPSynRetrans;
34
35
+// netdev renames
36
+extern void netdev_rename_device_add(const char *host_device, const char *container_device, const char *container_name);
37
+extern void netdev_rename_device_del(const char *host_device);
38
+
39
#endif /* NETDATA_PLUGIN_PROC_H */
src/proc_net_dev.c
+171
-38
@@ -1,5 +1,8 @@
1
#include "common.h"
2
3
+// ----------------------------------------------------------------------------
4
+// netdev list
5
+
6
struct netdev {
7
char *name;
8
uint32_t hash;
@@ -18,6 +21,20 @@ struct netdev {
21
int do_compressed;
22
int do_events;
23
24
+ const char *chart_type_net_bytes;
25
+ const char *chart_type_net_packets;
26
+ const char *chart_type_net_errors;
27
+ const char *chart_type_net_fifo;
28
+ const char *chart_type_net_events;
29
+ const char *chart_type_net_drops;
30
+ const char *chart_type_net_compressed;
31
+
32
+ const char *chart_family;
33
+ const char *chart_name;
34
+ const char *chart_id;
35
+
36
+ unsigned long priority;
37
+
38
// data collected
39
kernel_uint_t rbytes;
40
kernel_uint_t rpackets;
@@ -68,8 +85,101 @@ struct netdev {
85
struct netdev *next;
86
};
87
71
-static struct netdev *netdev_root = NULL, *netdev_last_used = NULL;
88
+// ----------------------------------------------------------------------------
89
+// netdev renames
90
+
91
+static struct netdev_rename {
92
+ const char *host_device;
93
+ uint32_t hash;
94
+
95
+ const char *container_device;
96
+ const char *container_name;
97
+
98
+ int processed;
99
100
+ struct netdev_rename *next;
101
+} *netdev_rename_root = NULL;
102
+
103
+static int netdev_pending_renames = 0;
104
+static netdata_mutex_t netdev_rename_mutex = NETDATA_MUTEX_INITIALIZER;
105
+
106
+static struct netdev_rename *netdev_rename_find(const char *host_device, uint32_t hash) {
107
+ struct netdev_rename *r;
108
+
109
+ for(r = netdev_rename_root; r ; r = r->next)
110
+ if(r->hash == hash && !strcmp(host_device, r->host_device))
111
+ return r;
112
+
113
+ return NULL;
114
+}
115
+
116
+// other threads can call this function to register a rename to a netdev
117
+void netdev_rename_device_add(const char *host_device, const char *container_device, const char *container_name) {
118
+ netdata_mutex_lock(&netdev_rename_mutex);
119
+
120
+ uint32_t hash = simple_hash(host_device);
121
+ struct netdev_rename *r = netdev_rename_find(host_device, hash);
122
+ if(!r) {
123
+ r = callocz(1, sizeof(struct netdev_rename));
124
+ r->host_device = strdupz(host_device);
125
+ r->container_device = strdupz(container_device);
126
+ r->container_name = strdupz(container_name);
127
+ r->hash = hash;
128
+ r->next = netdev_rename_root;
129
+ netdev_rename_root = r;
130
+ netdev_pending_renames++;
131
+ info("CGROUP: registered network interface rename for '%s' as '%s' under '%s'", r->host_device, r->container_device, r->container_name);
132
+ }
133
+ else {
134
+ if(strcmp(r->container_device, container_device) != 0 || strcmp(r->container_name, container_name) != 0) {
135
+ freez((void *) r->container_device);
136
+ freez((void *) r->container_name);
137
+
138
+ r->container_device = strdupz(container_device);
139
+ r->container_name = strdupz(container_name);
140
+ r->processed = 0;
141
+ netdev_pending_renames++;
142
+ info("CGROUP: altered network interface rename for '%s' as '%s' under '%s'", r->host_device, r->container_device, r->container_name);
143
+ }
144
+ }
145
+
146
+ netdata_mutex_unlock(&netdev_rename_mutex);
147
+}
148
+
149
+// other threads can call this function to delete a rename to a netdev
150
+void netdev_rename_device_del(const char *host_device) {
151
+ netdata_mutex_lock(&netdev_rename_mutex);
152
+
153
+ struct netdev_rename *r, *last = NULL;
154
+
155
+ uint32_t hash = simple_hash(host_device);
156
+ for(r = netdev_rename_root; r ; last = r, r = r->next) {
157
+ if (r->hash == hash && !strcmp(host_device, r->host_device)) {
158
+ if (netdev_rename_root == r)
159
+ netdev_rename_root = r->next;
160
+ else if (last)
161
+ last->next = r->next;
162
+
163
+ if(!r->processed)
164
+ netdev_pending_renames--;
165
+
166
+ info("CGROUP: unregistered network interface rename for '%s' as '%s' under '%s'", r->host_device, r->container_device, r->container_name);
167
+
168
+ freez((void *) r->host_device);
169
+ freez((void *) r->container_name);
170
+ freez((void *) r->container_device);
171
+ freez((void *) r);
172
+ break;
173
+ }
174
+ }
175
+
176
+ netdata_mutex_unlock(&netdev_rename_mutex);
177
+}
178
+
179
+// ----------------------------------------------------------------------------
180
+// netdev data collection
181
+
182
+static struct netdev *netdev_root = NULL, *netdev_last_used = NULL;
183
static size_t netdev_added = 0, netdev_found = 0;
184
185
static void netdev_free(struct netdev *d) {
@@ -82,8 +192,18 @@ static void netdev_free(struct netdev *d) {
192
if(d->st_events) rrdset_is_obsolete(d->st_events);
193
194
netdev_added--;
85
- freez(d->name);
86
- freez(d);
195
+ freez((void *)d->chart_type_net_bytes);
196
+ freez((void *)d->chart_type_net_compressed);
197
+ freez((void *)d->chart_type_net_drops);
198
+ freez((void *)d->chart_type_net_errors);
199
+ freez((void *)d->chart_type_net_events);
200
+ freez((void *)d->chart_type_net_fifo);
201
+ freez((void *)d->chart_type_net_packets);
202
+ freez((void *)d->chart_family);
203
+ freez((void *)d->chart_name);
204
+ freez((void *)d->chart_id);
205
+ freez((void *)d->name);
206
+ freez((void *)d);
207
}
208
209
static void netdev_cleanup() {
@@ -144,6 +264,19 @@ static struct netdev *get_netdev(const char *name) {
264
d->name = strdupz(name);
265
d->hash = simple_hash(d->name);
266
d->len = strlen(d->name);
267
+
268
+ d->chart_type_net_bytes = strdupz("net");
269
+ d->chart_type_net_compressed = strdupz("net_compressed");
270
+ d->chart_type_net_drops = strdupz("net_drops");
271
+ d->chart_type_net_errors = strdupz("net_errors");
272
+ d->chart_type_net_events = strdupz("net_events");
273
+ d->chart_type_net_fifo = strdupz("net_fifo");
274
+ d->chart_type_net_packets = strdupz("net_packets");
275
+ d->chart_family = strdupz(d->name);
276
+ d->chart_name = NULL;
277
+ d->chart_id = strdupz(d->name);
278
+ d->priority = 7000;
279
+
280
netdev_added++;
281
282
// link it to the end
@@ -278,14 +411,14 @@ int do_proc_net_dev(int update_every, usec_t dt) {
411
if(unlikely(!d->st_bandwidth)) {
412
413
d->st_bandwidth = rrdset_create_localhost(
281
- "net"
282
- , d->name
283
- , NULL
284
- , d->name
414
+ d->chart_type_net_bytes
415
+ , d->chart_id
416
+ , d->chart_name
417
+ , d->chart_family
418
, "net.net"
419
, "Bandwidth"
420
, "kilobits/s"
288
- , 7000
421
+ , d->priority
422
, update_every
423
, RRDSET_TYPE_AREA
424
);
@@ -309,14 +442,14 @@ int do_proc_net_dev(int update_every, usec_t dt) {
442
if(unlikely(!d->st_packets)) {
443
444
d->st_packets = rrdset_create_localhost(
312
- "net_packets"
313
- , d->name
314
- , NULL
315
- , d->name
445
+ d->chart_type_net_packets
446
+ , d->chart_id
447
+ , d->chart_name
448
+ , d->chart_family
449
, "net.packets"
450
, "Packets"
451
, "packets/s"
319
- , 7001
452
+ , d->priority + 1
453
, update_every
454
, RRDSET_TYPE_LINE
455
);
@@ -344,14 +477,14 @@ int do_proc_net_dev(int update_every, usec_t dt) {
477
if(unlikely(!d->st_errors)) {
478
479
d->st_errors = rrdset_create_localhost(
347
- "net_errors"
348
- , d->name
349
- , NULL
350
- , d->name
480
+ d->chart_type_net_errors
481
+ , d->chart_id
482
+ , d->chart_name
483
+ , d->chart_family
484
, "net.errors"
485
, "Interface Errors"
486
, "errors/s"
354
- , 7002
487
+ , d->priority + 2
488
, update_every
489
, RRDSET_TYPE_LINE
490
);
@@ -377,14 +510,14 @@ int do_proc_net_dev(int update_every, usec_t dt) {
510
if(unlikely(!d->st_drops)) {
511
512
d->st_drops = rrdset_create_localhost(
380
- "net_drops"
381
- , d->name
382
- , NULL
383
- , d->name
513
+ d->chart_type_net_drops
514
+ , d->chart_id
515
+ , d->chart_name
516
+ , d->chart_family
517
, "net.drops"
518
, "Interface Drops"
519
, "drops/s"
387
- , 7003
520
+ , d->priority + 3
521
, update_every
522
, RRDSET_TYPE_LINE
523
);
@@ -410,14 +543,14 @@ int do_proc_net_dev(int update_every, usec_t dt) {
543
if(unlikely(!d->st_fifo)) {
544
545
d->st_fifo = rrdset_create_localhost(
413
- "net_fifo"
414
- , d->name
415
- , NULL
416
- , d->name
546
+ d->chart_type_net_fifo
547
+ , d->chart_id
548
+ , d->chart_name
549
+ , d->chart_family
550
, "net.fifo"
551
, "Interface FIFO Buffer Errors"
552
, "errors"
420
- , 7004
553
+ , d->priority + 4
554
, update_every
555
, RRDSET_TYPE_LINE
556
);
@@ -443,14 +576,14 @@ int do_proc_net_dev(int update_every, usec_t dt) {
576
if(unlikely(!d->st_compressed)) {
577
578
d->st_compressed = rrdset_create_localhost(
446
- "net_compressed"
447
- , d->name
448
- , NULL
449
- , d->name
579
+ d->chart_type_net_compressed
580
+ , d->chart_id
581
+ , d->chart_name
582
+ , d->chart_family
583
, "net.compressed"
584
, "Compressed Packets"
585
, "packets/s"
453
- , 7005
586
+ , d->priority + 5
587
, update_every
588
, RRDSET_TYPE_LINE
589
);
@@ -476,14 +609,14 @@ int do_proc_net_dev(int update_every, usec_t dt) {
609
if(unlikely(!d->st_events)) {
610
611
d->st_events = rrdset_create_localhost(
479
- "net_events"
480
- , d->name
481
- , NULL
482
- , d->name
612
+ d->chart_type_net_events
613
+ , d->chart_id
614
+ , d->chart_name
615
+ , d->chart_family
616
, "net.events"
617
, "Network Interface Events"
618
, "events/s"
486
- , 7006
619
+ , d->priority + 6
620
, update_every
621
, RRDSET_TYPE_LINE
622
);
src/sys_fs_cgroup.c
+88
@@ -50,6 +50,7 @@ static SIMPLE_PATTERN *enabled_cgroup_renames = NULL;
50
static SIMPLE_PATTERN *systemd_services_cgroups = NULL;
51
52
static char *cgroups_rename_script = NULL;
53
+static char *cgroups_network_interface_script = NULL;
54
55
static int cgroups_check = 0;
56
@@ -191,6 +192,9 @@ void read_cgroup_plugin_configuration() {
192
snprintfz(filename, FILENAME_MAX, "%s/cgroup-name.sh", netdata_configured_plugins_dir);
193
cgroups_rename_script = config_get("plugin:cgroups", "script to get cgroup names", filename);
194
195
+ snprintfz(filename, FILENAME_MAX, "%s/cgroup-network", netdata_configured_plugins_dir);
196
+ cgroups_network_interface_script = config_get("plugin:cgroups", "script to get cgroup network interfaces", filename);
197
+
198
enabled_cgroup_renames = simple_pattern_create(
199
config_get("plugin:cgroups", "run script to rename cgroups matching",
200
" *.scope "
@@ -329,6 +333,12 @@ struct cpuacct_usage {
333
unsigned long long *cpu_percpu;
334
};
335
336
+struct cgroup_network_interface {
337
+ const char *host_device;
338
+ const char *container_device;
339
+ struct cgroup_network_interface *next;
340
+};
341
+
342
#define CGROUP_OPTIONS_DISABLED_DUPLICATE 0x00000001
343
#define CGROUP_OPTIONS_SYSTEM_SLICE_SERVICE 0x00000002
344
@@ -360,6 +370,8 @@ struct cgroup {
370
struct blkio io_merged; // operations
371
struct blkio io_queued; // operations
372
373
+ struct cgroup_network_interface *interfaces;
374
+
375
// per cgroup charts
376
RRDSET *st_cpu;
377
RRDSET *st_cpu_per_core;
@@ -717,6 +729,77 @@ static inline void read_all_cgroups(struct cgroup *root) {
729
cgroup_read(cg);
730
}
731
732
+// ----------------------------------------------------------------------------
733
+// cgroup network interfaces
734
+
735
+#define CGROUP_NETWORK_INTERFACE_MAX_LINE 2048
736
+static inline void read_cgroup_network_interfaces(struct cgroup *cg) {
737
+ debug(D_CGROUP, "looking for the network interfaces of cgroup '%s' with chart id '%s' and title '%s'", cg->id, cg->chart_id, cg->chart_title);
738
+
739
+ pid_t cgroup_pid;
740
+ char buffer[CGROUP_NETWORK_INTERFACE_MAX_LINE + 1];
741
+
742
+ snprintfz(buffer, CGROUP_NETWORK_INTERFACE_MAX_LINE, "exec %s --cgroup '%s%s'", cgroups_network_interface_script, cgroup_cpuacct_base, cg->id);
743
+
744
+ debug(D_CGROUP, "executing command '%s' for cgroup '%s'", buffer, cg->id);
745
+ FILE *fp = mypopen(buffer, &cgroup_pid);
746
+ if(fp) {
747
+ char *s;
748
+ while((s = fgets(buffer, CGROUP_NETWORK_INTERFACE_MAX_LINE, fp))) {
749
+ trim(s);
750
+
751
+ if(*s && *s != '\n') {
752
+ char *t = s;
753
+ while(*t && *t != ' ') t++;
754
+ if(*t == ' ') {
755
+ *t = '\0';
756
+ t++;
757
+ }
758
+
759
+ if(!*s) {
760
+ error("CGROUP: empty host interface returned by script");
761
+ continue;
762
+ }
763
+
764
+ if(!*t) {
765
+ error("CGROUP: empty container interface returned by script");
766
+ continue;
767
+ }
768
+
769
+ struct cgroup_network_interface *i = callocz(1, sizeof(struct cgroup_network_interface));
770
+ i->host_device = strdupz(s);
771
+ i->container_device = strdupz(t);
772
+ i->next = cg->interfaces;
773
+ cg->interfaces = i;
774
+
775
+ info("CGROUP: cgroup '%s' has network interface '%s' as '%s'", cg->id, i->host_device, i->container_device);
776
+
777
+ // register a device rename to proc_net_dev.c
778
+ netdev_rename_device_add(i->host_device, i->container_device, cg->chart_id);
779
+ }
780
+ }
781
+
782
+ mypclose(fp, cgroup_pid);
783
+ // debug(D_CGROUP, "closed command for cgroup '%s'", cg->id);
784
+ }
785
+ else
786
+ error("CGROUP: cannot popen(\"%s\", \"r\").", buffer);
787
+}
788
+
789
+static inline void free_cgroup_network_interfaces(struct cgroup *cg) {
790
+ while(cg->interfaces) {
791
+ struct cgroup_network_interface *i = cg->interfaces;
792
+ cg->interfaces = i->next;
793
+
794
+ // delete the registration of proc_net_dev rename
795
+ netdev_rename_device_del(i->host_device);
796
+
797
+ freez((void *)i->host_device);
798
+ freez((void *)i->container_device);
799
+ freez((void *)i);
800
+ }
801
+}
802
+
803
// ----------------------------------------------------------------------------
804
// add/remove/find cgroup objects
805
@@ -893,6 +976,9 @@ static inline struct cgroup *cgroup_add(const char *id) {
976
}
977
}
978
979
+ if(cg->enabled && !(cg->options & CGROUP_OPTIONS_SYSTEM_SLICE_SERVICE))
980
+ read_cgroup_network_interfaces(cg);
981
+
982
debug(D_CGROUP, "ADDED CGROUP: '%s' with chart id '%s' and title '%s' as %s (default was %s)", cg->id, cg->chart_id, cg->chart_title, (cg->enabled)?"enabled":"disabled", (def)?"enabled":"disabled");
983
984
return cg;
@@ -916,6 +1002,8 @@ static inline void cgroup_free(struct cgroup *cg) {
1002
if(cg->st_queued_ops) rrdset_is_obsolete(cg->st_queued_ops);
1003
if(cg->st_merged_ops) rrdset_is_obsolete(cg->st_merged_ops);
1004
1005
+ free_cgroup_network_interfaces(cg);
1006
+
1007
freez(cg->cpuacct_usage.cpu_percpu);
1008
1009
freez(cg->cpuacct_stat.filename);