Annotation of embedaddon/bmon/src/out_distribution.c, revision 1.1.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>