Annotation of embedaddon/bmon/src/out_distribution.c, revision 1.1

1.1     ! misho       1: /*
        !             2:  * bmon_distr.c            Bandwidth Monitor
        !             3:  *
        !             4:  * Copyright (c) 2001-2004 Thomas Graf <tgraf@suug.ch>
        !             5:  *
        !             6:  * Permission is hereby granted, free of charge, to any person obtaining a
        !             7:  * copy of this software and associated documentation files (the "Software"),
        !             8:  * to deal in the Software without restriction, including without limitation
        !             9:  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
        !            10:  * and/or sell copies of the Software, and to permit persons to whom the
        !            11:  * Software is furnished to do so, subject to the following conditions:
        !            12:  *
        !            13:  * The above copyright notice and this permission notice shall be included
        !            14:  * in all copies or substantial portions of the Software.
        !            15:  *
        !            16:  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
        !            17:  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        !            18:  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
        !            19:  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        !            20:  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
        !            21:  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
        !            22:  * DEALINGS IN THE SOFTWARE.
        !            23:  */
        !            24: 
        !            25: #include <bmon/bmon.h>
        !            26: #include <bmon/output.h>
        !            27: #include <bmon/node.h>
        !            28: #include <bmon/item.h>
        !            29: #include <bmon/input.h>
        !            30: #include <bmon/distribution.h>
        !            31: #include <bmon/utils.h>
        !            32: 
        !            33: #include <netdb.h>
        !            34: 
        !            35: static int send_fd;
        !            36: static int c_ipv6;
        !            37: static char *c_ip = "224.0.0.1";
        !            38: static char *c_port = "2048";
        !            39: static int c_forward = 0;
        !            40: static int c_errignore = 0;
        !            41: static int c_debug = 0;
        !            42: static int c_send_all = 15;
        !            43: static int send_all_rem = 1;
        !            44: 
        !            45: static void * build_opts(item_t *intf, size_t *size)
        !            46: {
        !            47:        void *buf;
        !            48:        size_t optsize = 0, off = 0;
        !            49:        struct distr_msg_ifopt *ip;
        !            50: 
        !            51:        if (intf->i_handle)
        !            52:                optsize += sizeof(*ip) + 4;
        !            53: 
        !            54:        if (intf->i_parent)
        !            55:                optsize += sizeof(*ip);
        !            56: 
        !            57:        if (intf->i_link)
        !            58:                optsize += sizeof(*ip);
        !            59: 
        !            60:        if (intf->i_level)
        !            61:                optsize += sizeof(*ip);
        !            62: 
        !            63:        if (intf->i_rx_usage >= 0)
        !            64:                optsize += sizeof(*ip) + 4;
        !            65: 
        !            66:        if (intf->i_tx_usage >= 0)
        !            67:                optsize += sizeof(*ip) + 4;
        !            68: 
        !            69:        if (intf->i_desc)
        !            70:                optsize += sizeof(*ip) + ((strlen(intf->i_desc)+4) & ~3);
        !            71: 
        !            72:        if (0 == optsize) {
        !            73:                *size = 0;
        !            74:                return NULL;
        !            75:        }
        !            76: 
        !            77:        buf = xcalloc(1, optsize);
        !            78: 
        !            79:        if (intf->i_handle) {
        !            80:                ip = buf + off;
        !            81:                ip->io_type = IFOPT_HANDLE;
        !            82:                ip->io_len = 4;
        !            83:                *((uint32_t *) (buf + off + sizeof(*ip)))  = htonl(intf->i_handle);
        !            84:                off += sizeof(*ip) + 4;
        !            85:        }
        !            86: 
        !            87:        if (intf->i_parent) {
        !            88:                ip = buf + off;
        !            89:                ip->io_type = IFOPT_PARENT;
        !            90:                ip->io_len = 0;
        !            91:                ip->io_pad = htons(intf->i_parent);
        !            92:                off += sizeof(*ip);
        !            93:        }
        !            94: 
        !            95:        if (intf->i_link) {
        !            96:                ip = buf + off;
        !            97:                ip->io_type = IFOPT_LINK;
        !            98:                ip->io_len = 0;
        !            99:                ip->io_pad = htons(intf->i_link);
        !           100:                off += sizeof(*ip);
        !           101:        }
        !           102: 
        !           103:        if (intf->i_level) {
        !           104:                ip = buf + off;
        !           105:                ip->io_type = IFOPT_LEVEL;
        !           106:                ip->io_len = 0;
        !           107:                ip->io_pad = htons(intf->i_level);
        !           108:                off += sizeof(*ip);
        !           109:        }
        !           110: 
        !           111:        if (intf->i_rx_usage >= 0) {
        !           112:                ip = buf + off;
        !           113:                ip->io_type = IFOPT_RX_USAGE;
        !           114:                ip->io_len = 4;
        !           115:                *((uint32_t *) (buf+off+sizeof(*ip))) = htonl(intf->i_rx_usage);
        !           116:                off += sizeof(*ip) + 4;
        !           117:        }
        !           118: 
        !           119:        if (intf->i_tx_usage >= 0) {
        !           120:                ip = buf + off;
        !           121:                ip->io_type = IFOPT_TX_USAGE;
        !           122:                ip->io_len = 4;
        !           123:                *((uint32_t *) (buf+off+sizeof(*ip))) = htonl(intf->i_tx_usage);
        !           124:                off += sizeof(*ip) + 4;
        !           125:        }
        !           126: 
        !           127:        if (intf->i_desc) {
        !           128:                ip = buf + off;
        !           129:                ip->io_type = IFOPT_DESC;
        !           130:                ip->io_len = (strlen(intf->i_desc)+4) & ~3;
        !           131:                strcpy(buf+off+sizeof(*ip), intf->i_desc);
        !           132:                off += sizeof(*ip) + ip->io_len;
        !           133:        }
        !           134: 
        !           135:        *size = optsize;
        !           136:        return buf;
        !           137: }
        !           138: 
        !           139: static inline int worth_sending(stat_attr_t *a)
        !           140: {
        !           141:        if (send_all_rem == 0)
        !           142:                return 1;
        !           143: 
        !           144:        if (!(a->a_flags & ATTR_FLAG_RX_ENABLED) &&
        !           145:            !(a->a_flags & ATTR_FLAG_TX_ENABLED)) {
        !           146:                if (c_debug)
        !           147:                        fprintf(stderr, "Attribute %s not worth sending, no data\n",
        !           148:                            type2name(a->a_type));
        !           149:                return 0;
        !           150:        }
        !           151: 
        !           152:        if (!attr_get_rx(a) && !attr_get_tx(a)) {
        !           153:                if (c_debug)
        !           154:                        fprintf(stderr, "Attribute %s not worth sending, still 0\n",
        !           155:                            type2name(a->a_type));
        !           156:                return 0;
        !           157:        }
        !           158:        
        !           159:        if (a->a_last_distribution.tv_sec == a->a_updated.tv_sec) {
        !           160:                if (a->a_last_distribution.tv_usec == a->a_updated.tv_usec) {
        !           161:                        if (c_debug)
        !           162:                                fprintf(stderr, "Attribute %s not worth sending, no update\n",
        !           163:                                    type2name(a->a_type));
        !           164:                        return 0;
        !           165:                }
        !           166:        }
        !           167: 
        !           168:        return 1;
        !           169: }
        !           170: 
        !           171: static void * build_item_msg(item_t *intf, size_t *size)
        !           172: {
        !           173:        int i, off = 0;
        !           174:        void *buf, *opts;
        !           175:        size_t msgsize = 0, namelen, nattrs = 0, optsize;
        !           176:        struct distr_msg_item *ip;
        !           177:        struct distr_msg_attr *ap;
        !           178: 
        !           179:        namelen = (strlen(intf->i_name) + 5) & ~3; /* 5 because of \0 */
        !           180:        opts = build_opts(intf, &optsize);
        !           181: 
        !           182:        for (i = 0; i < ATTR_HASH_MAX; i++) {
        !           183:                stat_attr_t *a;
        !           184:                for (a = intf->i_attrs[i]; a; a = a->a_next)
        !           185:                        if (worth_sending(a))
        !           186:                                nattrs++;
        !           187:        }
        !           188:        
        !           189:        msgsize = sizeof(*ip) + namelen + optsize + (nattrs * sizeof(*ap));
        !           190: 
        !           191:        ip = buf = xcalloc(1, msgsize);
        !           192: 
        !           193:        ip->i_index = htons(intf->i_index);
        !           194:        ip->i_offset = htons(msgsize);
        !           195:        ip->i_namelen = namelen;
        !           196:        ip->i_optslen = optsize;
        !           197:        ip->i_flags = htons((intf->i_flags & ITEM_FLAG_IS_CHILD) ? IF_IS_CHILD : 0);
        !           198: 
        !           199:        off = sizeof(*ip);
        !           200:        memcpy(buf + off, intf->i_name, strlen(intf->i_name));
        !           201:        off += namelen;
        !           202: 
        !           203:        if (opts) {
        !           204:                memcpy(buf + off, opts, optsize);
        !           205:                off += optsize;
        !           206:        }
        !           207: 
        !           208:        for (i = 0; i < ATTR_HASH_MAX; i++) {
        !           209:                stat_attr_t *a;
        !           210:                for (a = intf->i_attrs[i]; a; a = a->a_next) {
        !           211:                        if (worth_sending(a)) {
        !           212:                                struct distr_msg_attr am = {
        !           213:                                        .a_type = htons(a->a_type),
        !           214:                                        .a_rx = xhtonll(attr_get_rx(a)),
        !           215:                                        .a_tx = xhtonll(attr_get_tx(a)),
        !           216:                                        .a_flags = htons(
        !           217:                                            (a->a_flags & ATTR_FLAG_RX_ENABLED ? ATTR_RX_PROVIDED : 0) |
        !           218:                                            (a->a_flags & ATTR_FLAG_TX_ENABLED ? ATTR_TX_PROVIDED : 0)),
        !           219:                                };
        !           220: 
        !           221:                                COPY_TS(&a->a_last_distribution, &a->a_updated);
        !           222:                                memcpy(buf + off, &am, sizeof(am));
        !           223:                                off += sizeof(am);
        !           224:                        }
        !           225:                }
        !           226:        }
        !           227: 
        !           228:        *size = msgsize;
        !           229:        return buf;
        !           230: }
        !           231: 
        !           232: static void * build_item_group(node_t *node, size_t *size)
        !           233: {
        !           234:        size_t grpsize = sizeof(struct distr_msg_grp);
        !           235:        struct distr_msg_grp *gp;
        !           236:        void *group;
        !           237:        int i;
        !           238: 
        !           239:        gp = group = xcalloc(1, grpsize);
        !           240:        
        !           241:        for (i = 0; i < node->n_nitems; i++) {
        !           242:                if (node->n_items[i].i_name[0]) {
        !           243:                        size_t size;
        !           244:                        void *im = build_item_msg(&node->n_items[i], &size);
        !           245:                        int goff = grpsize;
        !           246: 
        !           247:                        grpsize += size;
        !           248:                        gp = group = xrealloc(group, grpsize);
        !           249:                        memcpy(group + goff, im, size);
        !           250: 
        !           251:                        xfree(im);
        !           252:                }
        !           253:        }
        !           254: 
        !           255:        gp->g_type = htons(BMON_GRP_IF);
        !           256:        gp->g_offset = htons(grpsize);
        !           257: 
        !           258:        *size = grpsize;
        !           259:        return group;
        !           260: }
        !           261: 
        !           262: static void distribute_node(node_t *node, void *arg)
        !           263: {
        !           264:        void *buf;
        !           265:        struct distr_msg_hdr *hdr;
        !           266:        size_t grpsize;
        !           267:        void *grp = build_item_group(node, &grpsize);
        !           268:        size_t nodenamelen = (strlen(node->n_name) + 5) & ~3; /* 5 because of \0 */
        !           269:        size_t msgsize = sizeof(*hdr) + nodenamelen + grpsize;
        !           270: 
        !           271:        hdr = buf = xcalloc(1, msgsize);
        !           272: 
        !           273:        hdr->h_magic = BMON_MAGIC;
        !           274:        hdr->h_ver = BMON_VERSION;
        !           275:        hdr->h_offset = sizeof(*hdr) + nodenamelen;
        !           276:        hdr->h_len = htons(msgsize);
        !           277:        hdr->h_ts_sec = htonl(rtiming.rt_last_read.tv_sec);
        !           278:        hdr->h_ts_usec = htonl(rtiming.rt_last_read.tv_usec);
        !           279:        memcpy(buf + sizeof(*hdr), node->n_name, strlen(node->n_name));
        !           280:        memcpy(buf + sizeof(*hdr) + nodenamelen, grp, grpsize);
        !           281: 
        !           282:        if (send(send_fd, buf, msgsize, 0) < 0) {
        !           283:                if (!c_errignore)
        !           284:                        quit("send() failed: %s\n", strerror(errno));
        !           285:                else if (c_debug)
        !           286:                        fprintf(stderr, "Ignoring error %s\n", strerror(errno));
        !           287:        }
        !           288:        
        !           289:        xfree(grp);
        !           290:        xfree(buf);
        !           291: }
        !           292: 
        !           293: static void distribute_nodes(void)
        !           294: {
        !           295:        send_all_rem--;
        !           296:        
        !           297:        if (c_forward)
        !           298:                foreach_node(distribute_node, NULL);
        !           299:        else
        !           300:                distribute_node(get_local_node(), NULL);
        !           301: 
        !           302:        if (send_all_rem <= 0)
        !           303:                send_all_rem = c_send_all;
        !           304: }
        !           305: 
        !           306: static void print_module_help(void)
        !           307: {
        !           308:        printf(
        !           309:        "Distribution of statistic over a network\n" \
        !           310:        "\n" \
        !           311:        "  Sends all local statistics to the specified ip or to the\n" \
        !           312:        "  multicast address all-hosts if none was specified. The\n" \
        !           313:        "  protocol is optimized for size, therefore only counters\n" \
        !           314:        "  that have changed will get distributed. The complete list\n" \
        !           315:        "  of attribute will be distributed once in a while to make\n" \
        !           316:        "  clients also list zero counters.\n" \
        !           317:        "\n" \
        !           318:        "  You may want to set the option `errignore' while in unicast\n" \
        !           319:        "  mode to prevent send() from failing if the destination has\n" \
        !           320:        "  gone down for a while (f.e. due to a reboot).\n" \
        !           321:        "\n" \
        !           322:        "  Remotely collected statistics can be distributed in forwarding\n" \
        !           323:        "  mode. However, you must make sure to not create loops as there\n" \
        !           324:        "  is no duplicate check on the receiving side and thus would\n" \
        !           325:        "  result in statistic corruption.\n" \
        !           326:        "\n"
        !           327:        "  Author: Thomas Graf <tgraf@suug.ch>\n" \
        !           328:        "\n" \
        !           329:        "  Options:\n" \
        !           330:        "    ip=ADDR          Destination address (default: 224.0.0.1/ff01::1)\n" \
        !           331:        "    port=NUM         Destination port (default: 2048)\n" \
        !           332:        "    ipv6             Prefer IPv6\n" \
        !           333:        "    forward          Forwarding mode, also distribute non-local nodes\n" \
        !           334:        "    errignore        Ignore ICMP error messages while sending\n" \
        !           335:        "    debug            Verbose output for debugging\n" \
        !           336:        "    sendall          Send interval of complete attribute list (default: 15)\n" \
        !           337:        "    help             Print this help text\n");
        !           338: }
        !           339: 
        !           340: static void distribution_set_opts(tv_t *attrs)
        !           341: {
        !           342:        while (attrs) {
        !           343:                if (!strcasecmp(attrs->type, "ip") && attrs->value)
        !           344:                        c_ip = attrs->value;
        !           345:                else if (!strcasecmp(attrs->type, "port") && attrs->value)
        !           346:                        c_port = attrs->value;
        !           347:                else if (!strcasecmp(attrs->type, "ipv6"))
        !           348:                        c_ipv6 = 1;
        !           349:                else if (!strcasecmp(attrs->type, "forward"))
        !           350:                        c_forward = 1;
        !           351:                else if (!strcasecmp(attrs->type, "errignore"))
        !           352:                        c_errignore = 1;
        !           353:                else if (!strcasecmp(attrs->type, "debug"))
        !           354:                        c_debug = 1;
        !           355:                else if (!strcasecmp(attrs->type, "sendall")) {
        !           356:                        if (attrs->value)
        !           357:                                c_send_all = strtol(attrs->value, NULL, 0);
        !           358:                        else
        !           359:                                c_send_all = 1;
        !           360:                } else if (!strcasecmp(attrs->type, "help")) {
        !           361:                        print_module_help();
        !           362:                        exit(0);
        !           363:                }
        !           364:                attrs = attrs->next;
        !           365:        }
        !           366: }
        !           367: 
        !           368: static int distribution_probe(void)
        !           369: {
        !           370:        int err;
        !           371:        struct addrinfo *t, *res = NULL;
        !           372:        struct addrinfo hints = {
        !           373:                .ai_socktype = SOCK_DGRAM,
        !           374:                .ai_family = c_ipv6 ? PF_INET6 : PF_INET,
        !           375:        };
        !           376:        char s[INET6_ADDRSTRLEN];
        !           377: 
        !           378:        if (c_ipv6 && !strcmp(c_ip, "224.0.0.1"))
        !           379:                c_ip = "ff01::1";
        !           380:        
        !           381:        if ((err = getaddrinfo(c_ip, c_port, &hints, &res)) < 0)
        !           382:                quit("getaddrinfo failed: %s\n", gai_strerror(err));
        !           383:        
        !           384:        
        !           385:        for (t = res; t; t = t->ai_next) {
        !           386:                if (c_debug) {
        !           387:                        const char *x = xinet_ntop(t->ai_addr, s, sizeof(s));
        !           388:                        fprintf(stderr, "Trying %s...", x ? x : "null");
        !           389:                }
        !           390:                send_fd = socket(t->ai_family, t->ai_socktype, 0);
        !           391: 
        !           392:                if (send_fd < 0) {
        !           393:                        if (c_debug)
        !           394:                                fprintf(stderr, "socket() failed: %s\n", strerror(errno));
        !           395:                        continue;
        !           396:                }
        !           397: 
        !           398:                if (connect(send_fd, t->ai_addr, t->ai_addrlen) < 0) {
        !           399:                        if (c_debug)
        !           400:                                fprintf(stderr, "connect() failed: %s\n", strerror(errno));
        !           401:                        continue;
        !           402:                }
        !           403: 
        !           404:                if (c_debug)
        !           405:                        fprintf(stderr, "OK\n");
        !           406:                return 1;
        !           407:        }
        !           408: 
        !           409:        fprintf(stderr, "Could not create and connect a datagram " \
        !           410:                "socket, tried:\n");
        !           411: 
        !           412:        for (t = res; t; t = t->ai_next) {
        !           413:                const char *x = xinet_ntop(t->ai_addr, s, sizeof(s));
        !           414:                fprintf(stderr, "\t%s\n", x ? x : "null");
        !           415:        }
        !           416: 
        !           417:        quit("Last error message was: %s\n", strerror(errno));
        !           418:        return 0;
        !           419: }
        !           420: 
        !           421: static struct output_module distribution_ops = {
        !           422:        .om_name = "distribution",
        !           423:        .om_draw = distribute_nodes,
        !           424:        .om_set_opts = distribution_set_opts,
        !           425:        .om_probe = distribution_probe,
        !           426: };
        !           427: 
        !           428: static void __init do_distribution_init(void)
        !           429: {
        !           430:        register_secondary_output_module(&distribution_ops);
        !           431: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>