root/luci/trunk/modules/admin-full/src/luci-bwc.c @ 6959

Revision 6959, 9.2 KB (checked in by jow, 2 years ago)

modules/admin-full: don't count TIME_WAIT connections in luci-bwc

Line 
1/*
2 * luci-bwc - Very simple bandwidth collector cache for LuCI realtime graphs
3 *
4 *   Copyright (C) 2010 Jo-Philipp Wich <xm@subsignal.org>
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 *  http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18
19#include <stdlib.h>
20#include <stdio.h>
21#include <string.h>
22#include <stdint.h>
23#include <inttypes.h>
24#include <fcntl.h>
25#include <time.h>
26#include <errno.h>
27#include <unistd.h>
28
29#include <sys/stat.h>
30#include <sys/mman.h>
31#include <arpa/inet.h>
32
33
34#define STEP_COUNT  60
35#define STEP_TIME   1
36
37#define DB_PATH     "/var/lib/luci-bwc"
38#define DB_IF_FILE  DB_PATH "/if/%s"
39#define DB_CN_FILE  DB_PATH "/connections"
40#define DB_LD_FILE  DB_PATH "/load"
41
42#define IF_SCAN_PATTERN \
43    " %[^ :]:%" SCNu64 " %" SCNu64 \
44    " %*d %*d %*d %*d %*d %*d" \
45    " %" SCNu64 " %" SCNu64
46
47#define LD_SCAN_PATTERN \
48    "%f %f %f"
49
50
51struct file_map {
52    int fd;
53    int size;
54    char *mmap;
55};
56
57struct traffic_entry {
58    uint64_t time;
59    uint64_t rxb;
60    uint64_t rxp;
61    uint64_t txb;
62    uint64_t txp;
63};
64
65struct conn_entry {
66    uint64_t time;
67    uint32_t udp;
68    uint32_t tcp;
69    uint32_t other;
70};
71
72struct load_entry {
73    uint64_t time;
74    uint16_t load1;
75    uint16_t load5;
76    uint16_t load15;
77};
78
79
80static uint64_t htonll(uint64_t value)
81{
82    int num = 1;
83
84    if (*(char *)&num == 1)
85        return htonl((uint32_t)(value & 0xFFFFFFFF)) |
86               htonl((uint32_t)(value >> 32));
87
88    return value;
89}
90
91#define ntohll htonll
92
93
94static int init_directory(char *path)
95{
96    char *p = path;
97
98    for (p = &path[1]; *p; p++)
99    {
100        if (*p == '/')
101        {
102            *p = 0;
103
104            if (mkdir(path, 0700) && (errno != EEXIST))
105                return -1;
106
107            *p = '/';
108        }
109    }
110
111    return 0;
112}
113
114static int init_file(char *path, int esize)
115{
116    int i, file;
117    char buf[sizeof(struct traffic_entry)] = { 0 };
118
119    if (init_directory(path))
120        return -1;
121
122    if ((file = open(path, O_WRONLY | O_CREAT, 0600)) >= 0)
123    {
124        for (i = 0; i < STEP_COUNT; i++)
125        {
126            if (write(file, buf, esize) < 0)
127                break;
128        }
129
130        close(file);
131
132        return 0;
133    }
134
135    return -1;
136}
137
138static int update_file(const char *path, void *entry, int esize)
139{
140    int rv = -1;
141    int file;
142    char *map;
143
144    if ((file = open(path, O_RDWR)) >= 0)
145    {
146        map = mmap(NULL, esize * STEP_COUNT, PROT_READ | PROT_WRITE,
147                   MAP_SHARED | MAP_LOCKED, file, 0);
148
149        if ((map != NULL) && (map != MAP_FAILED))
150        {
151            memmove(map, map + esize, esize * (STEP_COUNT-1));
152            memcpy(map + esize * (STEP_COUNT-1), entry, esize);
153
154            munmap(map, esize * STEP_COUNT);
155
156            rv = 0;
157        }
158
159        close(file);
160    }
161
162    return rv;
163}
164
165static int mmap_file(const char *path, int esize, struct file_map *m)
166{
167    m->fd   = -1;
168    m->size = -1;
169    m->mmap = NULL;
170
171    if ((m->fd = open(path, O_RDONLY)) >= 0)
172    {
173        m->size = STEP_COUNT * esize;
174        m->mmap = mmap(NULL, m->size, PROT_READ,
175                       MAP_SHARED | MAP_LOCKED, m->fd, 0);
176
177        if ((m->mmap != NULL) && (m->mmap != MAP_FAILED))
178            return 0;
179    }
180
181    return -1;
182}
183
184static void umap_file(struct file_map *m)
185{
186    if ((m->mmap != NULL) && (m->mmap != MAP_FAILED))
187        munmap(m->mmap, m->size);
188
189    if (m->fd > -1)
190        close(m->fd);
191}
192
193
194static int update_ifstat(
195    const char *ifname, uint64_t rxb, uint64_t rxp, uint64_t txb, uint64_t txp
196) {
197    char path[1024];
198
199    struct stat s;
200    struct traffic_entry e;
201
202    snprintf(path, sizeof(path), DB_IF_FILE, ifname);
203
204    if (stat(path, &s))
205    {
206        if (init_file(path, sizeof(struct traffic_entry)))
207        {
208            fprintf(stderr, "Failed to init %s: %s\n",
209                    path, strerror(errno));
210
211            return -1;
212        }
213    }
214
215    e.time = htonll(time(NULL));
216    e.rxb  = htonll(rxb);
217    e.rxp  = htonll(rxp);
218    e.txb  = htonll(txb);
219    e.txp  = htonll(txp);
220
221    return update_file(path, &e, sizeof(struct traffic_entry));
222}
223
224static int update_cnstat(uint32_t udp, uint32_t tcp, uint32_t other)
225{
226    char path[1024];
227
228    struct stat s;
229    struct conn_entry e;
230
231    snprintf(path, sizeof(path), DB_CN_FILE);
232
233    if (stat(path, &s))
234    {
235        if (init_file(path, sizeof(struct conn_entry)))
236        {
237            fprintf(stderr, "Failed to init %s: %s\n",
238                    path, strerror(errno));
239
240            return -1;
241        }
242    }
243
244    e.time  = htonll(time(NULL));
245    e.udp   = htonl(udp);
246    e.tcp   = htonl(tcp);
247    e.other = htonl(other);
248
249    return update_file(path, &e, sizeof(struct conn_entry));
250}
251
252static int update_ldstat(uint16_t load1, uint16_t load5, uint16_t load15)
253{
254    char path[1024];
255
256    struct stat s;
257    struct load_entry e;
258
259    snprintf(path, sizeof(path), DB_LD_FILE);
260
261    if (stat(path, &s))
262    {
263        if (init_file(path, sizeof(struct load_entry)))
264        {
265            fprintf(stderr, "Failed to init %s: %s\n",
266                    path, strerror(errno));
267
268            return -1;
269        }
270    }
271
272    e.time   = htonll(time(NULL));
273    e.load1  = htons(load1);
274    e.load5  = htons(load5);
275    e.load15 = htons(load15);
276
277    return update_file(path, &e, sizeof(struct load_entry));
278}
279
280static int run_daemon(int nofork)
281{
282    FILE *info;
283    uint64_t rxb, txb, rxp, txp;
284    uint32_t udp, tcp, other;
285    float lf1, lf5, lf15;
286    char line[1024];
287    char ifname[16];
288
289    struct stat s;
290    const char *ipc = stat("/proc/net/nf_conntrack", &s)
291        ? "/proc/net/ip_conntrack" : "/proc/net/nf_conntrack";
292
293    if (!nofork)
294    {
295        switch (fork())
296        {
297            case -1:
298                perror("fork()");
299                return -1;
300
301            case 0:
302                if (chdir("/") < 0)
303                {
304                    perror("chdir()");
305                    exit(1);
306                }
307
308                close(0);
309                close(1);
310                close(2);
311                break;
312
313            default:
314                exit(0);
315        }
316    }
317
318
319    /* go */
320    while (1)
321    {
322        if ((info = fopen("/proc/net/dev", "r")) != NULL)
323        {
324            while (fgets(line, sizeof(line), info))
325            {
326                if (strchr(line, '|'))
327                    continue;
328
329                if (sscanf(line, IF_SCAN_PATTERN, ifname, &rxb, &rxp, &txb, &txp))
330                {
331                    if (strncmp(ifname, "lo", sizeof(ifname)))
332                        update_ifstat(ifname, rxb, rxp, txb, txp);
333                }
334            }
335
336            fclose(info);
337        }
338
339        if ((info = fopen(ipc, "r")) != NULL)
340        {
341            udp   = 0;
342            tcp   = 0;
343            other = 0;
344
345            while (fgets(line, sizeof(line), info))
346            {
347                if (strstr(line, "TIME_WAIT"))
348                    continue;
349
350                if (sscanf(line, "%*s %*d %s", ifname) || sscanf(line, "%s %*d", ifname))
351                {
352                    if (!strcmp(ifname, "tcp"))
353                        tcp++;
354                    else if (!strcmp(ifname, "udp"))
355                        udp++;
356                    else
357                        other++;
358                }
359            }
360
361            update_cnstat(udp, tcp, other);
362
363            fclose(info);
364        }
365
366        if ((info = fopen("/proc/loadavg", "r")) != NULL)
367        {
368            if (fscanf(info, LD_SCAN_PATTERN, &lf1, &lf5, &lf15))
369            {
370                update_ldstat((uint16_t)(lf1  * 100),
371                              (uint16_t)(lf5  * 100),
372                              (uint16_t)(lf15 * 100));
373            }
374
375            fclose(info);
376        }
377
378        sleep(STEP_TIME);
379    }
380}
381
382static int run_dump_ifname(const char *ifname)
383{
384    int i;
385    char path[1024];
386    struct file_map m;
387    struct traffic_entry *e;
388
389    snprintf(path, sizeof(path), DB_IF_FILE, ifname);
390
391    if (mmap_file(path, sizeof(struct traffic_entry), &m))
392    {
393        fprintf(stderr, "Failed to open %s: %s\n", path, strerror(errno));
394        return 1;
395    }
396
397    for (i = 0; i < m.size; i += sizeof(struct traffic_entry))
398    {
399        e = (struct traffic_entry *) &m.mmap[i];
400
401        if (!e->time)
402            continue;
403
404        printf("[ %" PRIu64 ", %" PRIu64 ", %" PRIu64
405               ", %" PRIu64 ", %" PRIu64 " ]%s\n",
406            ntohll(e->time),
407            ntohll(e->rxb), ntohll(e->rxp),
408            ntohll(e->txb), ntohll(e->txp),
409            ((i + sizeof(struct traffic_entry)) < m.size) ? "," : "");
410    }
411
412    umap_file(&m);
413
414    return 0;
415}
416
417static int run_dump_conns(void)
418{
419    int i;
420    char path[1024];
421    struct file_map m;
422    struct conn_entry *e;
423
424    snprintf(path, sizeof(path), DB_CN_FILE);
425
426    if (mmap_file(path, sizeof(struct conn_entry), &m))
427    {
428        fprintf(stderr, "Failed to open %s: %s\n", path, strerror(errno));
429        return 1;
430    }
431
432    for (i = 0; i < m.size; i += sizeof(struct conn_entry))
433    {
434        e = (struct conn_entry *) &m.mmap[i];
435
436        if (!e->time)
437            continue;
438
439        printf("[ %" PRIu64 ", %u, %u, %u ]%s\n",
440            ntohll(e->time), ntohl(e->udp),
441            ntohl(e->tcp), ntohl(e->other),
442            ((i + sizeof(struct conn_entry)) < m.size) ? "," : "");
443    }
444
445    umap_file(&m);
446
447    return 0;
448}
449
450static int run_dump_load(void)
451{
452    int i;
453    char path[1024];
454    struct file_map m;
455    struct load_entry *e;
456
457    snprintf(path, sizeof(path), DB_LD_FILE);
458
459    if (mmap_file(path, sizeof(struct load_entry), &m))
460    {
461        fprintf(stderr, "Failed to open %s: %s\n", path, strerror(errno));
462        return 1;
463    }
464
465    for (i = 0; i < m.size; i += sizeof(struct load_entry))
466    {
467        e = (struct load_entry *) &m.mmap[i];
468
469        if (!e->time)
470            continue;
471
472        printf("[ %" PRIu64 ", %u, %u, %u ]%s\n",
473            ntohll(e->time),
474            ntohs(e->load1), ntohs(e->load5), ntohs(e->load15),
475            ((i + sizeof(struct load_entry)) < m.size) ? "," : "");
476    }
477
478    umap_file(&m);
479
480    return 0;
481}
482
483
484int main(int argc, char *argv[])
485{
486    int opt;
487    int daemon = 0;
488    int nofork = 0;
489
490    while ((opt = getopt(argc, argv, "dfi:cl")) > -1)
491    {
492        switch (opt)
493        {
494            case 'd':
495                daemon = 1;
496                break;
497
498            case 'f':
499                nofork = 1;
500                break;
501
502            case 'i':
503                if (optarg)
504                    return run_dump_ifname(optarg);
505                break;
506
507            case 'c':
508                return run_dump_conns();
509
510            case 'l':
511                return run_dump_load();
512
513            default:
514                break;
515        }
516    }
517
518    if (daemon)
519        return run_daemon(nofork);
520
521    else
522        fprintf(stderr,
523            "Usage:\n"
524            "   %s -d [-f]\n"
525            "   %s -i ifname\n"
526            "   %s -c\n"
527            "   %s -l\n",
528                argv[0], argv[0], argv[0], argv[0]
529        );
530
531    return 1;
532}
Note: See TracBrowser for help on using the browser.