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

Revision 9511, 14.6 KB (checked in by jow, 6 months ago)

modules/admin-full: filter localhost<->localhost connections live connection graph

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#include <signal.h>
29
30#include <sys/stat.h>
31#include <sys/mman.h>
32#include <arpa/inet.h>
33
34#include <dlfcn.h>
35
36#define STEP_COUNT  60
37#define STEP_TIME   1
38#define TIMEOUT     10
39
40#define PID_PATH    "/var/run/luci-bwc.pid"
41
42#define DB_PATH     "/var/lib/luci-bwc"
43#define DB_IF_FILE  DB_PATH "/if/%s"
44#define DB_RD_FILE  DB_PATH "/radio/%s"
45#define DB_CN_FILE  DB_PATH "/connections"
46#define DB_LD_FILE  DB_PATH "/load"
47
48#define IF_SCAN_PATTERN \
49    " %[^ :]:%u %u" \
50    " %*d %*d %*d %*d %*d %*d" \
51    " %u %u"
52
53#define LD_SCAN_PATTERN \
54    "%f %f %f"
55
56
57struct file_map {
58    int fd;
59    int size;
60    char *mmap;
61};
62
63struct traffic_entry {
64    uint32_t time;
65    uint32_t rxb;
66    uint32_t rxp;
67    uint32_t txb;
68    uint32_t txp;
69};
70
71struct conn_entry {
72    uint32_t time;
73    uint32_t udp;
74    uint32_t tcp;
75    uint32_t other;
76};
77
78struct load_entry {
79    uint32_t time;
80    uint16_t load1;
81    uint16_t load5;
82    uint16_t load15;
83};
84
85struct radio_entry {
86    uint32_t time;
87    uint16_t rate;
88    uint8_t  rssi;
89    uint8_t  noise;
90};
91
92static int readpid(void)
93{
94    int fd;
95    int pid = -1;
96    char buf[9] = { 0 };
97
98    if ((fd = open(PID_PATH, O_RDONLY)) > -1)
99    {
100        if (read(fd, buf, sizeof(buf)))
101        {
102            buf[8] = 0;
103            pid = atoi(buf);
104        }
105
106        close(fd);
107    }
108
109    return pid;
110}
111
112static int writepid(void)
113{
114    int fd;
115    int wlen;
116    char buf[9] = { 0 };
117
118    if ((fd = open(PID_PATH, O_WRONLY | O_CREAT | O_TRUNC, 0600)) > -1)
119    {
120        wlen = snprintf(buf, sizeof(buf), "%i", getpid());
121        write(fd, buf, wlen);
122        close(fd);
123
124        return 0;
125    }
126
127    return -1;
128}
129
130static int timeout = TIMEOUT;
131static int countdown = -1;
132
133static void reset_countdown(int sig)
134{
135    countdown = timeout;
136
137}
138
139
140static char *progname;
141static int prognamelen;
142
143static int (*iw_get_rate)(const char *, int *) = NULL;
144static int (*iw_get_rssi)(const char *, int *) = NULL;
145static int (*iw_get_noise)(const char *, int *) = NULL;
146
147
148static int init_directory(char *path)
149{
150    char *p = path;
151
152    for (p = &path[1]; *p; p++)
153    {
154        if (*p == '/')
155        {
156            *p = 0;
157
158            if (mkdir(path, 0700) && (errno != EEXIST))
159                return -1;
160
161            *p = '/';
162        }
163    }
164
165    return 0;
166}
167
168static int init_file(char *path, int esize)
169{
170    int i, file;
171    char buf[sizeof(struct traffic_entry)] = { 0 };
172
173    if (init_directory(path))
174        return -1;
175
176    if ((file = open(path, O_WRONLY | O_CREAT, 0600)) >= 0)
177    {
178        for (i = 0; i < STEP_COUNT; i++)
179        {
180            if (write(file, buf, esize) < 0)
181                break;
182        }
183
184        close(file);
185
186        return 0;
187    }
188
189    return -1;
190}
191
192static inline uint32_t timeof(void *entry)
193{
194    return ntohl(((struct traffic_entry *)entry)->time);
195}
196
197static int update_file(const char *path, void *entry, int esize)
198{
199    int rv = -1;
200    int file;
201    char *map;
202
203    if ((file = open(path, O_RDWR)) >= 0)
204    {
205        map = mmap(NULL, esize * STEP_COUNT, PROT_READ | PROT_WRITE,
206                   MAP_SHARED | MAP_LOCKED, file, 0);
207
208        if ((map != NULL) && (map != MAP_FAILED))
209        {
210            if (timeof(entry) > timeof(map + esize * (STEP_COUNT-1)))
211            {
212                memmove(map, map + esize, esize * (STEP_COUNT-1));
213                memcpy(map + esize * (STEP_COUNT-1), entry, esize);
214            }
215
216            munmap(map, esize * STEP_COUNT);
217
218            rv = 0;
219        }
220
221        close(file);
222    }
223
224    return rv;
225}
226
227static int mmap_file(const char *path, int esize, struct file_map *m)
228{
229    m->fd   = -1;
230    m->size = -1;
231    m->mmap = NULL;
232
233    if ((m->fd = open(path, O_RDONLY)) >= 0)
234    {
235        m->size = STEP_COUNT * esize;
236        m->mmap = mmap(NULL, m->size, PROT_READ,
237                       MAP_SHARED | MAP_LOCKED, m->fd, 0);
238
239        if ((m->mmap != NULL) && (m->mmap != MAP_FAILED))
240            return 0;
241    }
242
243    return -1;
244}
245
246static void umap_file(struct file_map *m)
247{
248    if ((m->mmap != NULL) && (m->mmap != MAP_FAILED))
249        munmap(m->mmap, m->size);
250
251    if (m->fd > -1)
252        close(m->fd);
253}
254
255static void * iwinfo_open(void)
256{
257    return dlopen("/usr/lib/libiwinfo.so", RTLD_LAZY);
258}
259
260static int iwinfo_update(
261    void *iw, const char *ifname, uint16_t *rate, uint8_t *rssi, uint8_t *noise
262) {
263    int (*probe)(const char *);
264    int val;
265
266    if (!iw_get_rate)
267    {
268        if ((probe = dlsym(iw, "nl80211_probe")) != NULL && probe(ifname))
269        {
270            iw_get_rate  = dlsym(iw, "nl80211_get_bitrate");
271            iw_get_rssi  = dlsym(iw, "nl80211_get_signal");
272            iw_get_noise = dlsym(iw, "nl80211_get_noise");
273        }
274        else if ((probe = dlsym(iw, "madwifi_probe")) != NULL && probe(ifname))
275        {
276            iw_get_rate  = dlsym(iw, "madwifi_get_bitrate");
277            iw_get_rssi  = dlsym(iw, "madwifi_get_signal");
278            iw_get_noise = dlsym(iw, "madwifi_get_noise");
279        }
280        else if ((probe = dlsym(iw, "wl_probe")) != NULL && probe(ifname))
281        {
282            iw_get_rate  = dlsym(iw, "wl_get_bitrate");
283            iw_get_rssi  = dlsym(iw, "wl_get_signal");
284            iw_get_noise = dlsym(iw, "wl_get_noise");
285        }
286        else
287        {
288            return 0;
289        }
290    }
291
292    *rate = (iw_get_rate && !iw_get_rate(ifname, &val)) ? val : 0;
293    *rssi = (iw_get_rssi && !iw_get_rssi(ifname, &val)) ? val : 0;
294    *noise = (iw_get_noise && !iw_get_noise(ifname, &val)) ? val : 0;
295
296    return 1;
297}
298
299static void iwinfo_close(void *iw)
300{
301    void (*do_close)(void);
302
303    if ((do_close = dlsym(iw, "nl80211_close")) != NULL) do_close();
304    if ((do_close = dlsym(iw, "madwifi_close")) != NULL) do_close();
305    if ((do_close = dlsym(iw, "wl_close"))      != NULL) do_close();
306    if ((do_close = dlsym(iw, "wext_close"))    != NULL) do_close();
307    if ((do_close = dlsym(iw, "iwinfo_close"))  != NULL) do_close();
308
309    dlclose(iw);
310}
311
312
313static int update_ifstat(
314    const char *ifname, uint32_t rxb, uint32_t rxp, uint32_t txb, uint32_t txp
315) {
316    char path[1024];
317
318    struct stat s;
319    struct traffic_entry e;
320
321    snprintf(path, sizeof(path), DB_IF_FILE, ifname);
322
323    if (stat(path, &s))
324    {
325        if (init_file(path, sizeof(struct traffic_entry)))
326        {
327            fprintf(stderr, "Failed to init %s: %s\n",
328                    path, strerror(errno));
329
330            return -1;
331        }
332    }
333
334    e.time = htonl(time(NULL));
335    e.rxb  = htonl(rxb);
336    e.rxp  = htonl(rxp);
337    e.txb  = htonl(txb);
338    e.txp  = htonl(txp);
339
340    return update_file(path, &e, sizeof(struct traffic_entry));
341}
342
343static int update_radiostat(
344    const char *ifname, uint16_t rate, uint8_t rssi, uint8_t noise
345) {
346    char path[1024];
347
348    struct stat s;
349    struct radio_entry e;
350
351    snprintf(path, sizeof(path), DB_RD_FILE, ifname);
352
353    if (stat(path, &s))
354    {
355        if (init_file(path, sizeof(struct radio_entry)))
356        {
357            fprintf(stderr, "Failed to init %s: %s\n",
358                    path, strerror(errno));
359
360            return -1;
361        }
362    }
363
364    e.time  = htonl(time(NULL));
365    e.rate  = htons(rate);
366    e.rssi  = rssi;
367    e.noise = noise;
368
369    return update_file(path, &e, sizeof(struct radio_entry));
370}
371
372static int update_cnstat(uint32_t udp, uint32_t tcp, uint32_t other)
373{
374    char path[1024];
375
376    struct stat s;
377    struct conn_entry e;
378
379    snprintf(path, sizeof(path), DB_CN_FILE);
380
381    if (stat(path, &s))
382    {
383        if (init_file(path, sizeof(struct conn_entry)))
384        {
385            fprintf(stderr, "Failed to init %s: %s\n",
386                    path, strerror(errno));
387
388            return -1;
389        }
390    }
391
392    e.time  = htonl(time(NULL));
393    e.udp   = htonl(udp);
394    e.tcp   = htonl(tcp);
395    e.other = htonl(other);
396
397    return update_file(path, &e, sizeof(struct conn_entry));
398}
399
400static int update_ldstat(uint16_t load1, uint16_t load5, uint16_t load15)
401{
402    char path[1024];
403
404    struct stat s;
405    struct load_entry e;
406
407    snprintf(path, sizeof(path), DB_LD_FILE);
408
409    if (stat(path, &s))
410    {
411        if (init_file(path, sizeof(struct load_entry)))
412        {
413            fprintf(stderr, "Failed to init %s: %s\n",
414                    path, strerror(errno));
415
416            return -1;
417        }
418    }
419
420    e.time   = htonl(time(NULL));
421    e.load1  = htons(load1);
422    e.load5  = htons(load5);
423    e.load15 = htons(load15);
424
425    return update_file(path, &e, sizeof(struct load_entry));
426}
427
428static int run_daemon(void)
429{
430    FILE *info;
431    uint32_t rxb, txb, rxp, txp;
432    uint32_t udp, tcp, other;
433    uint16_t rate;
434    uint8_t rssi, noise;
435    float lf1, lf5, lf15;
436    char line[1024];
437    char ifname[16];
438    int i;
439    void *iw;
440    struct sigaction sa;
441
442    struct stat s;
443    const char *ipc = stat("/proc/net/nf_conntrack", &s)
444        ? "/proc/net/ip_conntrack" : "/proc/net/nf_conntrack";
445
446    switch (fork())
447    {
448        case -1:
449            perror("fork()");
450            return -1;
451
452        case 0:
453            if (chdir("/") < 0)
454            {
455                perror("chdir()");
456                exit(1);
457            }
458
459            close(0);
460            close(1);
461            close(2);
462            break;
463
464        default:
465            return 0;
466    }
467
468    /* setup USR1 signal handler to reset timer */
469    sa.sa_handler = reset_countdown;
470    sa.sa_flags   = SA_RESTART;
471    sigemptyset(&sa.sa_mask);
472    sigaction(SIGUSR1, &sa, NULL);
473
474    /* write pid */
475    if (writepid())
476    {
477        fprintf(stderr, "Failed to write pid file: %s\n", strerror(errno));
478        return 1;
479    }
480
481    /* initialize iwinfo */
482    iw = iwinfo_open();
483
484    /* go */
485    for (reset_countdown(0); countdown >= 0; countdown--)
486    {
487        /* alter progname for ps, top */
488        memset(progname, 0, prognamelen);
489        snprintf(progname, prognamelen, "luci-bwc %d", countdown);
490
491        if ((info = fopen("/proc/net/dev", "r")) != NULL)
492        {
493            while (fgets(line, sizeof(line), info))
494            {
495                if (strchr(line, '|'))
496                    continue;
497
498                if (sscanf(line, IF_SCAN_PATTERN, ifname, &rxb, &rxp, &txb, &txp))
499                {
500                    if (strncmp(ifname, "lo", sizeof(ifname)))
501                        update_ifstat(ifname, rxb, rxp, txb, txp);
502                }
503            }
504
505            fclose(info);
506        }
507
508        if (iw)
509        {
510            for (i = 0; i < 5; i++)
511            {
512#define iwinfo_checkif(pattern) \
513                do {                                                      \
514                    snprintf(ifname, sizeof(ifname), pattern, i);         \
515                    if (iwinfo_update(iw, ifname, &rate, &rssi, &noise))  \
516                    {                                                     \
517                        update_radiostat(ifname, rate, rssi, noise);      \
518                        continue;                                         \
519                    }                                                     \
520                } while(0)
521
522                iwinfo_checkif("wlan%d");
523                iwinfo_checkif("ath%d");
524                iwinfo_checkif("wl%d");
525            }
526        }
527
528        if ((info = fopen(ipc, "r")) != NULL)
529        {
530            udp   = 0;
531            tcp   = 0;
532            other = 0;
533
534            while (fgets(line, sizeof(line), info))
535            {
536                if (strstr(line, "TIME_WAIT"))
537                    continue;
538
539                if (strstr(line, "src=127.0.0.1 ") &&
540                    strstr(line, "dst=127.0.0.1 "))
541                    continue;
542
543                if (sscanf(line, "%*s %*d %s", ifname) || sscanf(line, "%s %*d", ifname))
544                {
545                    if (!strcmp(ifname, "tcp"))
546                        tcp++;
547                    else if (!strcmp(ifname, "udp"))
548                        udp++;
549                    else
550                        other++;
551                }
552            }
553
554            update_cnstat(udp, tcp, other);
555
556            fclose(info);
557        }
558
559        if ((info = fopen("/proc/loadavg", "r")) != NULL)
560        {
561            if (fscanf(info, LD_SCAN_PATTERN, &lf1, &lf5, &lf15))
562            {
563                update_ldstat((uint16_t)(lf1  * 100),
564                              (uint16_t)(lf5  * 100),
565                              (uint16_t)(lf15 * 100));
566            }
567
568            fclose(info);
569        }
570
571        sleep(STEP_TIME);
572    }
573
574    unlink(PID_PATH);
575
576    if (iw)
577        iwinfo_close(iw);
578
579    return 0;
580}
581
582static void check_daemon(void)
583{
584    int pid;
585
586    if ((pid = readpid()) < 0 || kill(pid, 0) < 0)
587    {
588        /* daemon ping failed, try to start it up */
589        if (run_daemon())
590        {
591            fprintf(stderr,
592                "Failed to ping daemon and unable to start it up: %s\n",
593                strerror(errno));
594
595            exit(1);
596        }
597    }
598    else if (kill(pid, SIGUSR1))
599    {
600        fprintf(stderr, "Failed to send signal: %s\n", strerror(errno));
601        exit(2);
602    }
603}
604
605static int run_dump_ifname(const char *ifname)
606{
607    int i;
608    char path[1024];
609    struct file_map m;
610    struct traffic_entry *e;
611
612    check_daemon();
613    snprintf(path, sizeof(path), DB_IF_FILE, ifname);
614
615    if (mmap_file(path, sizeof(struct traffic_entry), &m))
616    {
617        fprintf(stderr, "Failed to open %s: %s\n", path, strerror(errno));
618        return 1;
619    }
620
621    for (i = 0; i < m.size; i += sizeof(struct traffic_entry))
622    {
623        e = (struct traffic_entry *) &m.mmap[i];
624
625        if (!e->time)
626            continue;
627
628        printf("[ %u, %u, %" PRIu32
629               ", %u, %u ]%s\n",
630            ntohl(e->time),
631            ntohl(e->rxb), ntohl(e->rxp),
632            ntohl(e->txb), ntohl(e->txp),
633            ((i + sizeof(struct traffic_entry)) < m.size) ? "," : "");
634    }
635
636    umap_file(&m);
637
638    return 0;
639}
640
641static int run_dump_radio(const char *ifname)
642{
643    int i;
644    char path[1024];
645    struct file_map m;
646    struct radio_entry *e;
647
648    check_daemon();
649    snprintf(path, sizeof(path), DB_RD_FILE, ifname);
650
651    if (mmap_file(path, sizeof(struct radio_entry), &m))
652    {
653        fprintf(stderr, "Failed to open %s: %s\n", path, strerror(errno));
654        return 1;
655    }
656
657    for (i = 0; i < m.size; i += sizeof(struct radio_entry))
658    {
659        e = (struct radio_entry *) &m.mmap[i];
660
661        if (!e->time)
662            continue;
663
664        printf("[ %u, %d, %d, %d ]%s\n",
665            ntohl(e->time),
666            e->rate, e->rssi, e->noise,
667            ((i + sizeof(struct radio_entry)) < m.size) ? "," : "");
668    }
669
670    umap_file(&m);
671
672    return 0;
673}
674
675static int run_dump_conns(void)
676{
677    int i;
678    char path[1024];
679    struct file_map m;
680    struct conn_entry *e;
681
682    check_daemon();
683    snprintf(path, sizeof(path), DB_CN_FILE);
684
685    if (mmap_file(path, sizeof(struct conn_entry), &m))
686    {
687        fprintf(stderr, "Failed to open %s: %s\n", path, strerror(errno));
688        return 1;
689    }
690
691    for (i = 0; i < m.size; i += sizeof(struct conn_entry))
692    {
693        e = (struct conn_entry *) &m.mmap[i];
694
695        if (!e->time)
696            continue;
697
698        printf("[ %u, %u, %u, %u ]%s\n",
699            ntohl(e->time), ntohl(e->udp),
700            ntohl(e->tcp), ntohl(e->other),
701            ((i + sizeof(struct conn_entry)) < m.size) ? "," : "");
702    }
703
704    umap_file(&m);
705
706    return 0;
707}
708
709static int run_dump_load(void)
710{
711    int i;
712    char path[1024];
713    struct file_map m;
714    struct load_entry *e;
715
716    check_daemon();
717    snprintf(path, sizeof(path), DB_LD_FILE);
718
719    if (mmap_file(path, sizeof(struct load_entry), &m))
720    {
721        fprintf(stderr, "Failed to open %s: %s\n", path, strerror(errno));
722        return 1;
723    }
724
725    for (i = 0; i < m.size; i += sizeof(struct load_entry))
726    {
727        e = (struct load_entry *) &m.mmap[i];
728
729        if (!e->time)
730            continue;
731
732        printf("[ %u, %u, %u, %u ]%s\n",
733            ntohl(e->time),
734            ntohs(e->load1), ntohs(e->load5), ntohs(e->load15),
735            ((i + sizeof(struct load_entry)) < m.size) ? "," : "");
736    }
737
738    umap_file(&m);
739
740    return 0;
741}
742
743
744int main(int argc, char *argv[])
745{
746    int opt;
747
748    progname = argv[0];
749    prognamelen = -1;
750
751    for (opt = 0; opt < argc; opt++)
752        prognamelen += 1 + strlen(argv[opt]);
753
754    while ((opt = getopt(argc, argv, "t:i:r:cl")) > -1)
755    {
756        switch (opt)
757        {
758            case 't':
759                timeout = atoi(optarg);
760                break;
761
762            case 'i':
763                if (optarg)
764                    return run_dump_ifname(optarg);
765                break;
766
767            case 'r':
768                if (optarg)
769                    return run_dump_radio(optarg);
770                break;
771
772            case 'c':
773                return run_dump_conns();
774
775            case 'l':
776                return run_dump_load();
777
778            default:
779                break;
780        }
781    }
782
783    fprintf(stderr,
784        "Usage:\n"
785        "   %s [-t timeout] -i ifname\n"
786        "   %s [-t timeout] -r radiodev\n"
787        "   %s [-t timeout] -c\n"
788        "   %s [-t timeout] -l\n",
789            argv[0], argv[0], argv[0], argv[0]
790    );
791
792    return 1;
793}
Note: See TracBrowser for help on using the browser.