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>