From 77a10e6f467b12f7c02f6c3633d28f8732eeb091 Mon Sep 17 00:00:00 2001 From: claudio Date: Mon, 17 Oct 2022 12:01:19 +0000 Subject: [PATCH] Implement openmetric output via bgpctl show metric command This adds most of the generic code to output the metrics with labels and implements some basic metrics. The code works but metrics may still change. OK tb@ --- usr.sbin/bgpctl/Makefile | 5 +- usr.sbin/bgpctl/bgpctl.c | 62 ++++- usr.sbin/bgpctl/bgpctl.h | 4 +- usr.sbin/bgpctl/ometric.c | 421 +++++++++++++++++++++++++++++++ usr.sbin/bgpctl/ometric.h | 49 ++++ usr.sbin/bgpctl/output.c | 4 +- usr.sbin/bgpctl/output_json.c | 4 +- usr.sbin/bgpctl/output_ometric.c | 339 +++++++++++++++++++++++++ usr.sbin/bgpctl/parser.c | 3 +- usr.sbin/bgpctl/parser.h | 3 +- 10 files changed, 874 insertions(+), 20 deletions(-) create mode 100644 usr.sbin/bgpctl/ometric.c create mode 100644 usr.sbin/bgpctl/ometric.h create mode 100644 usr.sbin/bgpctl/output_ometric.c diff --git a/usr.sbin/bgpctl/Makefile b/usr.sbin/bgpctl/Makefile index 106495df673..06032dc2f62 100644 --- a/usr.sbin/bgpctl/Makefile +++ b/usr.sbin/bgpctl/Makefile @@ -1,9 +1,10 @@ -# $OpenBSD: Makefile,v 1.17 2020/05/02 14:33:33 claudio Exp $ +# $OpenBSD: Makefile,v 1.18 2022/10/17 12:01:19 claudio Exp $ .PATH: ${.CURDIR}/../bgpd PROG= bgpctl -SRCS= bgpctl.c output.c output_json.c parser.c mrtparser.c util.c json.c +SRCS= bgpctl.c output.c output_json.c output_ometric.c parser.c \ + mrtparser.c util.c json.c ometric.c CFLAGS+= -Wall CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes CFLAGS+= -Wmissing-declarations diff --git a/usr.sbin/bgpctl/bgpctl.c b/usr.sbin/bgpctl/bgpctl.c index e41f6c97bd7..57613c98881 100644 --- a/usr.sbin/bgpctl/bgpctl.c +++ b/usr.sbin/bgpctl/bgpctl.c @@ -1,4 +1,4 @@ -/* $OpenBSD: bgpctl.c,v 1.284 2022/10/07 09:20:30 claudio Exp $ */ +/* $OpenBSD: bgpctl.c,v 1.285 2022/10/17 12:01:19 claudio Exp $ */ /* * Copyright (c) 2003 Henning Brauer @@ -79,7 +79,7 @@ int main(int argc, char *argv[]) { struct sockaddr_un sa_un; - int fd, n, done, ch, verbose = 0; + int fd, n, done, numdone, ch, verbose = 0; struct imsg imsg; struct network_config net; struct parse_result *res; @@ -256,6 +256,12 @@ main(int argc, char *argv[]) case SHOW_RIB_MEM: imsg_compose(ibuf, IMSG_CTL_SHOW_RIB_MEM, 0, 0, -1, NULL, 0); break; + case SHOW_METRIC: + output = &ometric_output; + numdone = 2; + imsg_compose(ibuf, IMSG_CTL_SHOW_NEIGHBOR, 0, 0, -1, NULL, 0); + imsg_compose(ibuf, IMSG_CTL_SHOW_RIB_MEM, 0, 0, -1, NULL, 0); + break; case RELOAD: imsg_compose(ibuf, IMSG_CTL_RELOAD, 0, 0, -1, res->reason, sizeof(res->reason)); @@ -366,18 +372,14 @@ main(int argc, char *argv[]) break; } + output->head(res); + + again: while (ibuf->w.queued) - if (msgbuf_write(&ibuf->w) <= 0 && errno != EAGAIN) + if (msgbuf_write(&ibuf->w) <= 0) err(1, "write error"); - output->head(res); - while (!done) { - if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) - err(1, "imsg_read error"); - if (n == 0) - errx(1, "pipe closed"); - while (!done) { if ((n = imsg_get(ibuf, &imsg)) == -1) err(1, "imsg_get error"); @@ -387,6 +389,20 @@ main(int argc, char *argv[]) done = show(&imsg, res); imsg_free(&imsg); } + + if (done) + break; + + if ((n = imsg_read(ibuf)) == -1) + err(1, "imsg_read error"); + if (n == 0) + errx(1, "pipe closed"); + + } + + if (res->action == SHOW_METRIC && --numdone > 0) { + done = 0; + goto again; } output->tail(); @@ -416,21 +432,29 @@ show(struct imsg *imsg, struct parse_result *res) switch (imsg->hdr.type) { case IMSG_CTL_SHOW_NEIGHBOR: + if (output->neighbor == NULL) + break; p = imsg->data; output->neighbor(p, res); break; case IMSG_CTL_SHOW_TIMER: if (imsg->hdr.len < IMSG_HEADER_SIZE + sizeof(t)) errx(1, "wrong imsg len"); + if (output->timer == NULL) + break; memcpy(&t, imsg->data, sizeof(t)); if (t.type > 0 && t.type < Timer_Max) output->timer(&t); break; case IMSG_CTL_SHOW_INTERFACE: + if (output->interface == NULL) + break; iface = imsg->data; output->interface(iface); break; case IMSG_CTL_SHOW_NEXTHOP: + if (output->nexthop == NULL) + break; nh = imsg->data; output->nexthop(nh); break; @@ -438,18 +462,24 @@ show(struct imsg *imsg, struct parse_result *res) case IMSG_CTL_SHOW_NETWORK: if (imsg->hdr.len < IMSG_HEADER_SIZE + sizeof(*kf)) errx(1, "wrong imsg len"); + if (output->fib == NULL) + break; kf = imsg->data; output->fib(kf); break; case IMSG_CTL_SHOW_FIB_TABLES: if (imsg->hdr.len < IMSG_HEADER_SIZE + sizeof(*kt)) errx(1, "wrong imsg len"); + if (output->fib_table == NULL) + break; kt = imsg->data; output->fib_table(kt); break; case IMSG_CTL_SHOW_RIB: if (imsg->hdr.len < IMSG_HEADER_SIZE + sizeof(rib)) errx(1, "wrong imsg len"); + if (output->rib == NULL) + break; memcpy(&rib, imsg->data, sizeof(rib)); aslen = imsg->hdr.len - IMSG_HEADER_SIZE - sizeof(rib); asdata = imsg->data; @@ -462,6 +492,8 @@ show(struct imsg *imsg, struct parse_result *res) warnx("bad IMSG_CTL_SHOW_RIB_COMMUNITIES received"); break; } + if (output->communities == NULL) + break; output->communities(imsg->data, ilen, res); break; case IMSG_CTL_SHOW_RIB_ATTR: @@ -470,23 +502,31 @@ show(struct imsg *imsg, struct parse_result *res) warnx("bad IMSG_CTL_SHOW_RIB_ATTR received"); break; } + if (output->attr == NULL) + break; output->attr(imsg->data, ilen, res->flags, 0); break; case IMSG_CTL_SHOW_RIB_MEM: if (imsg->hdr.len < IMSG_HEADER_SIZE + sizeof(stats)) errx(1, "wrong imsg len"); + if (output->rib_mem == NULL) + break; memcpy(&stats, imsg->data, sizeof(stats)); output->rib_mem(&stats); return (1); case IMSG_CTL_SHOW_SET: if (imsg->hdr.len < IMSG_HEADER_SIZE + sizeof(set)) errx(1, "wrong imsg len"); + if (output->set == NULL) + break; memcpy(&set, imsg->data, sizeof(set)); output->set(&set); break; case IMSG_CTL_SHOW_RTR: if (imsg->hdr.len < IMSG_HEADER_SIZE + sizeof(rtr)) errx(1, "wrong imsg len"); + if (output->rtr == NULL) + break; memcpy(&rtr, imsg->data, sizeof(rtr)); output->rtr(&rtr); break; @@ -495,6 +535,8 @@ show(struct imsg *imsg, struct parse_result *res) warnx("got IMSG_CTL_RESULT with wrong len"); break; } + if (output->result == NULL) + break; memcpy(&rescode, imsg->data, sizeof(rescode)); output->result(rescode); return (1); diff --git a/usr.sbin/bgpctl/bgpctl.h b/usr.sbin/bgpctl/bgpctl.h index 66a0f145809..7014095ba75 100644 --- a/usr.sbin/bgpctl/bgpctl.h +++ b/usr.sbin/bgpctl/bgpctl.h @@ -1,4 +1,4 @@ -/* $OpenBSD: bgpctl.h,v 1.16 2022/08/31 15:00:53 claudio Exp $ */ +/* $OpenBSD: bgpctl.h,v 1.17 2022/10/17 12:01:19 claudio Exp $ */ /* * Copyright (c) 2019 Claudio Jeker @@ -37,7 +37,7 @@ struct output { void (*tail)(void); }; -extern const struct output show_output, json_output; +extern const struct output show_output, json_output, ometric_output; extern const size_t pt_sizes[]; #define EOL0(flag) ((flag & F_CTL_SSV) ? ';' : '\n') diff --git a/usr.sbin/bgpctl/ometric.c b/usr.sbin/bgpctl/ometric.c new file mode 100644 index 00000000000..54b1bc6e137 --- /dev/null +++ b/usr.sbin/bgpctl/ometric.c @@ -0,0 +1,421 @@ +/* $OpenBSD: ometric.c,v 1.1 2022/10/17 12:01:19 claudio Exp $ */ + +/* + * Copyright (c) 2022 Claudio Jeker + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include "ometric.h" + +struct olabel { + STAILQ_ENTRY(olabel) entry; + const char *key; + char *value; +}; + +struct olabels { + STAILQ_HEAD(, olabel) labels; + struct olabels *next; + int refcnt; +}; + +enum ovalue_type { + OVT_INTEGER, + OVT_DOUBLE, +}; + +struct ovalue { + STAILQ_ENTRY(ovalue) entry; + struct olabels *labels; + union { + uint64_t i; + double f; + } value; + enum ovalue_type valtype; +}; + +STAILQ_HEAD(ovalues, ovalue); + +struct ometric { + STAILQ_ENTRY(ometric) entry; + struct ovalues vals; + const char *name; + const char *help; + const char *const *stateset; + size_t setsize; + enum ometric_type type; +}; + +STAILQ_HEAD(, ometric) ometrics = STAILQ_HEAD_INITIALIZER(ometrics); + +/* + * Allocate and return new ometric. The name and help string need to remain + * valid until the ometric is freed. Normally constant strings should be used. + */ +struct ometric * +ometric_new(enum ometric_type type, const char *name, const char *help) +{ + struct ometric *om; + + if ((om = calloc(1, sizeof(*om))) == NULL) + err(1, NULL); + + om->name = name; + om->help = help; + om->type = type; + STAILQ_INIT(&om->vals); + + STAILQ_INSERT_TAIL(&ometrics, om, entry); + + return om; +} + +/* + * Same as above but for a stateset. The states is an array of constant strings + * with statecnt elements. The states, name and help pointers need to remain + * valid until the ometric is freed. + */ +struct ometric * +ometric_new_state(const char * const *states, size_t statecnt, const char *name, + const char *help) +{ + struct ometric *om; + + if ((om = calloc(1, sizeof(*om))) == NULL) + err(1, NULL); + + om->name = name; + om->help = help; + om->type = OMT_STATESET; + om->stateset = states; + om->setsize = statecnt; + STAILQ_INIT(&om->vals); + + STAILQ_INSERT_TAIL(&ometrics, om, entry); + + return om; +} + +void +ometric_free_all(void) +{ + struct ometric *om; + struct ovalue *ov; + + while ((om = STAILQ_FIRST(&ometrics)) != NULL) { + STAILQ_REMOVE_HEAD(&ometrics, entry); + while ((ov = STAILQ_FIRST(&om->vals)) != NULL) { + STAILQ_REMOVE_HEAD(&om->vals, entry); + olabels_free(ov->labels); + free(ov); + } + free(om); + } +} + +static struct olabels * +olabels_ref(struct olabels *ol) +{ + struct olabels *x = ol; + + while (x != NULL) { + x->refcnt++; + x = x->next; + } + + return ol; +} + +/* + * Create a new set of labels based on keys and values arrays. + * keys must end in a NULL element. values needs to hold as many elements + * but the elements can be NULL. values are copied for the olabel but + * keys needs to point to constant memory. + */ +struct olabels * +olabels_new(const char * const *keys, const char **values) +{ + struct olabels *ol; + struct olabel *l; + + if ((ol = malloc(sizeof(*ol))) == NULL) + err(1, NULL); + STAILQ_INIT(&ol->labels); + ol->refcnt = 1; + ol->next = NULL; + + while (*keys != NULL) { + if (*values && **values != '\0') { + if ((l = malloc(sizeof(*l))) == NULL) + err(1, NULL); + l->key = *keys; + if ((l->value = strdup(*values)) == NULL) + err(1, NULL); + STAILQ_INSERT_TAIL(&ol->labels, l, entry); + } + + keys++; + values++; + } + + return ol; +} + +/* + * Free olables once nothing uses them anymore. + */ +void +olabels_free(struct olabels *ol) +{ + struct olabels *next; + struct olabel *l; + + for ( ; ol != NULL; ol = next) { + next = ol->next; + + if (--ol->refcnt == 0) { + while ((l = STAILQ_FIRST(&ol->labels)) != NULL) { + STAILQ_REMOVE_HEAD(&ol->labels, entry); + free(l->value); + free(l); + } + free(ol); + } + } +} + +/* + * Add one extra label onto the label stack. Once no longer used the + * value needs to be freed with olabels_free(). + */ +static struct olabels * +olabels_add_extra(struct olabels *ol, const char *key, const char *value) +{ + const char *keys[2] = { key, NULL }; + const char *values[2] = { value, NULL }; + struct olabels *new; + + if (value == NULL || *value == '\0') + return ol; + + new = olabels_new(keys, values); + new->next = olabels_ref(ol); + + return new; +} + +/* + * Output function called last. + */ +static const char * +ometric_type(enum ometric_type type) +{ + switch (type) { + case OMT_GAUGE: + return "gauge"; + case OMT_COUNTER: + return "counter"; + case OMT_STATESET: + return "stateset"; + case OMT_HISTOGRAM: + return "histogram"; + case OMT_SUMMARY: + return "summary"; + default: + return "unknown"; + } +} + +static void +ometric_output_labels(const struct olabels *ol) +{ + struct olabel *l; + const char *comma = ""; + + if (ol == NULL) { + printf(" "); + return; + } + + printf("{"); + + while (ol != NULL) { + STAILQ_FOREACH(l, &ol->labels, entry) { + printf("%s%s=\"%s\"", comma, l->key, l->value); + comma = ","; + } + ol = ol->next; + } + + printf("} "); +} + +static void +ometric_output_value(const struct ovalue *ov) +{ + switch (ov->valtype) { + case OVT_INTEGER: + printf("%llu", ov->value.i); + return; + case OVT_DOUBLE: + printf("%g", ov->value.f); + return; + } +} + +/* + * Output all metric values with TYPE and optional HELP strings. + */ +void +ometric_output_all(void) +{ + struct ometric *om; + struct ovalue *ov; + + STAILQ_FOREACH(om, &ometrics, entry) { + if (om->help) + printf("# HELP %s %s\n", om->name, om->help); + printf("# TYPE %s %s\n", om->name, ometric_type(om->type)); + + STAILQ_FOREACH(ov, &om->vals, entry) { + printf("%s", om->name); + ometric_output_labels(ov->labels); + ometric_output_value(ov); + printf("\n"); + } + } + + printf("# EOF\n"); +} + +/* + * Value setters + */ +static void +ometric_set_int_value(struct ometric *om, uint64_t val, struct olabels *ol) +{ + struct ovalue *ov; + + if ((ov = malloc(sizeof(*ov))) == NULL) + err(1, NULL); + + ov->value.i = val; + ov->valtype = OVT_INTEGER; + ov->labels = olabels_ref(ol); + + STAILQ_INSERT_TAIL(&om->vals, ov, entry); +} + +/* + * Set an integer value with label ol. ol can be NULL. + */ +void +ometric_set_int(struct ometric *om, uint64_t val, struct olabels *ol) +{ + if (om->type != OMT_COUNTER && om->type != OMT_GAUGE) + errx(1, "%s incorrect ometric type", __func__); + + ometric_set_int_value(om, val, ol); +} + +/* + * Set a floating point value with label ol. ol can be NULL. + */ +void +ometric_set_float(struct ometric *om, double val, struct olabels *ol) +{ + struct ovalue *ov; + + if (om->type != OMT_COUNTER && om->type != OMT_GAUGE) + errx(1, "%s incorrect ometric type", __func__); + + if ((ov = malloc(sizeof(*ov))) == NULL) + err(1, NULL); + + ov->value.f = val; + ov->valtype = OVT_DOUBLE; + ov->labels = olabels_ref(ol); + + STAILQ_INSERT_TAIL(&om->vals, ov, entry); +} + +/* + * Add an info value (which is the value 1 but with extra key-value pairs). + */ +void +ometric_set_info(struct ometric *om, const char **keys, const char **values, + struct olabels *ol) +{ + struct olabels *extra = NULL; + + if (om->type != OMT_INFO) + errx(1, "%s incorrect ometric type", __func__); + + if (keys != NULL) { + extra = olabels_new(keys, values); + extra->next = olabels_ref(ol); + } + + ometric_set_int_value(om, 1, extra != NULL ? extra : ol); + olabels_free(extra); +} + +/* + * Set a stateset to one of its states. + */ +void +ometric_set_state(struct ometric *om, const char *state, struct olabels *ol) +{ + struct olabels *extra; + size_t i; + int val; + + if (om->type != OMT_STATESET) + errx(1, "%s incorrect ometric type", __func__); + + for (i = 0; i < om->setsize; i++) { + if (strcasecmp(state, om->stateset[i]) == 0) + val = 1; + else + val = 0; + + extra = olabels_add_extra(ol, om->name, om->stateset[i]); + ometric_set_int_value(om, val, extra); + olabels_free(extra); + } +} + +/* + * Set a value with an extra label, the key should be a constant string while + * the value is copied into the extra label. + */ +void +ometric_set_int_with_label(struct ometric *om, uint64_t val, const char *key, + const char *value, struct olabels *ol) +{ + struct olabels *extra; + + extra = olabels_add_extra(ol, key, value); + ometric_set_int(om, val, extra); + olabels_free(extra); +} diff --git a/usr.sbin/bgpctl/ometric.h b/usr.sbin/bgpctl/ometric.h new file mode 100644 index 00000000000..01fc1fbc737 --- /dev/null +++ b/usr.sbin/bgpctl/ometric.h @@ -0,0 +1,49 @@ +/* $OpenBSD: ometric.h,v 1.1 2022/10/17 12:01:19 claudio Exp $ */ + +/* + * Copyright (c) 2022 Claudio Jeker + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +enum ometric_type { + OMT_UNKNOWN, + OMT_GAUGE, + OMT_COUNTER, + OMT_STATESET, + OMT_HISTOGRAM, + OMT_SUMMARY, + OMT_INFO, +}; + +struct ometric; +struct olabels; + +struct ometric *ometric_new(enum ometric_type, const char *, const char *); +struct ometric *ometric_new_state(const char * const *, size_t, const char *, + const char *); +void ometric_free_all(void); +struct olabels *olabels_new(const char * const *, const char **); +void olabels_free(struct olabels *); + +void ometric_output_all(void); + +/* XXX how to pass attributes */ +/* functions to set gauge and counter metrics */ +void ometric_set_int(struct ometric *, uint64_t, struct olabels *); +void ometric_set_float(struct ometric *, double, struct olabels *); +void ometric_set_info(struct ometric *, const char **, const char **, + struct olabels *); +void ometric_set_state(struct ometric *, const char *, struct olabels *); +void ometric_set_int_with_label(struct ometric *, uint64_t, const char *, + const char *, struct olabels *); diff --git a/usr.sbin/bgpctl/output.c b/usr.sbin/bgpctl/output.c index 04b9cc2c27e..34fe6268ed4 100644 --- a/usr.sbin/bgpctl/output.c +++ b/usr.sbin/bgpctl/output.c @@ -1,4 +1,4 @@ -/* $OpenBSD: output.c,v 1.29 2022/08/31 15:00:53 claudio Exp $ */ +/* $OpenBSD: output.c,v 1.30 2022/10/17 12:01:19 claudio Exp $ */ /* * Copyright (c) 2003 Henning Brauer @@ -1120,5 +1120,5 @@ const struct output show_output = { .set = show_rib_set, .rtr = show_rtr, .result = show_result, - .tail = show_tail + .tail = show_tail, }; diff --git a/usr.sbin/bgpctl/output_json.c b/usr.sbin/bgpctl/output_json.c index dfd443d55a5..6edfb86b087 100644 --- a/usr.sbin/bgpctl/output_json.c +++ b/usr.sbin/bgpctl/output_json.c @@ -1,4 +1,4 @@ -/* $OpenBSD: output_json.c,v 1.23 2022/08/31 15:00:53 claudio Exp $ */ +/* $OpenBSD: output_json.c,v 1.24 2022/10/17 12:01:19 claudio Exp $ */ /* * Copyright (c) 2020 Claudio Jeker @@ -1057,5 +1057,5 @@ const struct output json_output = { .set = json_rib_set, .rtr = json_rtr, .result = json_result, - .tail = json_tail + .tail = json_tail, }; diff --git a/usr.sbin/bgpctl/output_ometric.c b/usr.sbin/bgpctl/output_ometric.c new file mode 100644 index 00000000000..4f5e6e3809a --- /dev/null +++ b/usr.sbin/bgpctl/output_ometric.c @@ -0,0 +1,339 @@ +/* $OpenBSD: output_ometric.c,v 1.1 2022/10/17 12:01:19 claudio Exp $ */ + +/* + * Copyright (c) 2022 Claudio Jeker + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include + +#include "bgpd.h" +#include "session.h" +#include "rde.h" +#include "version.h" + +#include "bgpctl.h" +#include "parser.h" +#include "ometric.h" + +struct ometric *bgpd_info, *bgpd_scrape_time; +struct ometric *peer_info, *peer_state, *peer_state_raw, *peer_up_time, + *peer_down_time, *peer_last_read, *peer_last_write; +struct ometric *peer_prefixes_transmit, *peer_prefixes_receive; +struct ometric *peer_message_transmit, *peer_message_recieve; +struct ometric *peer_update_transmit, *peer_update_pending, + *peer_update_receive; +struct ometric *peer_withdraw_transmit, *peer_withdraw_pending, + *peer_withdraw_receive; +struct ometric *peer_rr_req_transmit, *peer_rr_req_receive; +struct ometric *peer_rr_borr_transmit, *peer_rr_borr_receive; +struct ometric *peer_rr_eorr_transmit, *peer_rr_eorr_receive; +struct ometric *rde_mem_size, *rde_mem_count, *rde_mem_ref_count; +struct ometric *rde_set_size, *rde_set_count, *rde_table_count; + +struct timeval start_time, end_time; + +static void +ometric_head(struct parse_result *arg) +{ + struct olabels *ol = NULL; + const char *keys[4] = { "nodename", "domainname", "release", NULL }; + const char *values[4]; + char hostname[HOST_NAME_MAX + 1]; + char *domainname; + + bgpd_info = ometric_new(OMT_INFO, "bgpd_info", "bgpd information"); + bgpd_scrape_time = ometric_new(OMT_GAUGE, "bgpd_scrape_seconds", + "bgpd scrape time in seconds"); + + gettimeofday(&start_time, NULL); + + if (gethostname(hostname, sizeof(hostname))) + err(1, "gethostname"); + if ((domainname = strchr(hostname, '.'))) + *domainname++ = '\0'; + + values[0] = hostname; + values[1] = domainname; + values[2] = BGPD_VERSION; + values[3] = NULL; + + ol = olabels_new(keys, values); + + ometric_set_info(bgpd_info, NULL, NULL, ol); + + olabels_free(ol); + + /* + * per neighbor stats: attrs will be remote_as, remote_addr, + * description and group + */ + peer_info = ometric_new(OMT_INFO, "bgpd_peer_info", + "peer information"); + peer_state = ometric_new_state(statenames, + sizeof(statenames) / sizeof(statenames[0]), "bgpd_peer_state", + "peer session state"); + peer_state_raw = ometric_new(OMT_GAUGE, "bgpd_peer_state_raw", + "peer session state raw int value"); + peer_up_time = ometric_new(OMT_GAUGE, "bgpd_peer_up_seconds", + "peer session up time in seconds"); + peer_down_time = ometric_new(OMT_GAUGE, "bgpd_peer_down_seconds", + "peer session down time in seconds"); + peer_last_read = ometric_new(OMT_GAUGE, "bgpd_peer_last_read_seconds", + "peer time since last read in seconds"); + peer_last_write = ometric_new(OMT_GAUGE, "bgpd_peer_last_write_seconds", + "peer time since last write in seconds"); + + peer_prefixes_transmit = ometric_new(OMT_GAUGE, + "bgpd_peer_prefixes_transmit", + "number of prefixes sent to peer"); + peer_prefixes_receive = ometric_new(OMT_GAUGE, + "bgpd_peer_prefixes_receive", + "number of prefixes received from peer"); + + peer_message_transmit = ometric_new(OMT_COUNTER, + "bgpd_peer_message_transmit_total", + "per message type count of tranmitted messages"); + peer_message_recieve = ometric_new(OMT_COUNTER, + "bgpd_peer_message_receive_total", + "per message type count of received messages"); + + peer_update_transmit = ometric_new(OMT_COUNTER, + "bgpd_peer_update_transmit_total", + "number of prefixes sent as update"); + peer_update_pending = ometric_new(OMT_COUNTER, + "bgpd_peer_update_pending_total", + "number of pending update prefixes"); + peer_update_receive = ometric_new(OMT_COUNTER, + "bgpd_peer_update_receive_total", + "number of prefixes received as update"); + + peer_withdraw_transmit = ometric_new(OMT_COUNTER, + "bgpd_peer_withdraw_transmit_total", + "number of witdrawn prefixes sent to peer"); + peer_withdraw_pending = ometric_new(OMT_COUNTER, + "bgpd_peer_withdraw_pending_total", + "number of pending withdrawn prefixes"); + peer_withdraw_receive = ometric_new(OMT_COUNTER, + "bgpd_peer_withdraw_receive_total", + "number of withdrawn prefixes received from peer"); + + peer_rr_req_transmit = ometric_new(OMT_COUNTER, + "bgpd_peer_route_refresh_req_transmit_total", + "number of route-refresh request transmitted to peer"); + peer_rr_req_receive = ometric_new(OMT_COUNTER, + "bgpd_peer_route_refresh_req_receive_total", + "number of route-refresh request received from peer"); + peer_rr_borr_transmit = ometric_new(OMT_COUNTER, + "bgpd_peer_route_refresh_borr_transmit_total", + "number of ext. route-refresh BORR messages transmitted to peer"); + peer_rr_borr_receive = ometric_new(OMT_COUNTER, + "bgpd_peer_route_refresh_borr_receive_total", + "number of ext. route-refresh BORR messages received from peer"); + peer_rr_eorr_transmit = ometric_new(OMT_COUNTER, + "bgpd_peer_route_refresh_eorr_transmit_total", + "number of ext. route-refresh EORR messages transmitted to peer"); + peer_rr_eorr_receive = ometric_new(OMT_COUNTER, + "bgpd_peer_route_refresh_eorr_receive_total", + "number of ext. route-refresh EORR messages received from peer"); + + /* RDE memory statistics */ + rde_mem_size = ometric_new(OMT_GAUGE, + "bgpd_rde_memory_usage_bytes", "memory usage in bytes"); + rde_mem_count = ometric_new(OMT_GAUGE, + "bgpd_rde_memory_count", "number of object in use"); + rde_mem_ref_count = ometric_new(OMT_GAUGE, + "bgpd_rde_memory_reference_count", "number of references held"); + + rde_set_size = ometric_new(OMT_GAUGE, + "bgpd_rde_set_usage_bytes", "memory usage of set in bytes"); + rde_set_count = ometric_new(OMT_GAUGE, + "bgpd_rde_set_count", "number of object in set"); + rde_table_count = ometric_new(OMT_GAUGE, + "bgpd_rde_table_count", "number of as_set tables"); +} + +static void +ometric_neighbor_stats(struct peer *p, struct parse_result *arg) +{ + struct olabels *ol = NULL; + const char *keys[5] = { + "remote_addr", "remote_as", "description", "group", NULL }; + const char *values[5]; + + /* skip neighbor templates */ + if (p->conf.template) + return; + + values[0] = log_addr(&p->conf.remote_addr); + values[1] = log_as(p->conf.remote_as); + values[2] = p->conf.descr; + values[3] = p->conf.group; + values[4] = NULL; + + ol = olabels_new(keys, values); + + ometric_set_info(peer_info, NULL, NULL, ol); + ometric_set_state(peer_state, statenames[p->state], ol); + ometric_set_int(peer_state_raw, p->state, ol); + + if (p->state == STATE_ESTABLISHED) { + ometric_set_int(peer_up_time, + get_monotime(p->stats.last_updown), ol); + ometric_set_int(peer_last_read, + get_monotime(p->stats.last_read), ol); + ometric_set_int(peer_last_write, + get_monotime(p->stats.last_write), ol); + } else if (p->stats.last_updown != 0) + ometric_set_int(peer_down_time, + get_monotime(p->stats.last_updown), ol); + + ometric_set_int(peer_prefixes_transmit, p->stats.prefix_out_cnt, ol); + ometric_set_int(peer_prefixes_receive, p->stats.prefix_cnt, ol); + + ometric_set_int_with_label(peer_message_transmit, + p->stats.msg_sent_open, "message", "open", ol); + ometric_set_int_with_label(peer_message_transmit, + p->stats.msg_sent_notification, "message", "notification", ol); + ometric_set_int_with_label(peer_message_transmit, + p->stats.msg_sent_update, "message", "update", ol); + ometric_set_int_with_label(peer_message_transmit, + p->stats.msg_sent_keepalive, "message", "keepalive", ol); + ometric_set_int_with_label(peer_message_transmit, + p->stats.msg_sent_rrefresh, "message", "route_refresh", ol); + + ometric_set_int_with_label(peer_message_recieve, + p->stats.msg_rcvd_open, "message", "open", ol); + ometric_set_int_with_label(peer_message_recieve, + p->stats.msg_rcvd_notification, "message", "notification", ol); + ometric_set_int_with_label(peer_message_recieve, + p->stats.msg_rcvd_update, "message", "update", ol); + ometric_set_int_with_label(peer_message_recieve, + p->stats.msg_rcvd_keepalive, "message", "keepalive", ol); + ometric_set_int_with_label(peer_message_recieve, + p->stats.msg_rcvd_rrefresh, "message", "route_refresh", ol); + + ometric_set_int(peer_update_transmit, p->stats.prefix_sent_update, ol); + ometric_set_int(peer_update_pending, p->stats.pending_update, ol); + ometric_set_int(peer_update_receive, p->stats.prefix_rcvd_update, ol); + ometric_set_int(peer_withdraw_transmit, p->stats.prefix_sent_withdraw, + ol); + ometric_set_int(peer_withdraw_pending, p->stats.pending_withdraw, ol); + ometric_set_int(peer_withdraw_receive, p->stats.prefix_rcvd_withdraw, + ol); + + ometric_set_int(peer_rr_req_transmit, p->stats.refresh_sent_req, ol); + ometric_set_int(peer_rr_req_receive, p->stats.refresh_rcvd_req, ol); + ometric_set_int(peer_rr_borr_transmit, p->stats.refresh_sent_borr, ol); + ometric_set_int(peer_rr_borr_receive, p->stats.refresh_rcvd_borr, ol); + ometric_set_int(peer_rr_eorr_transmit, p->stats.refresh_sent_eorr, ol); + ometric_set_int(peer_rr_eorr_receive, p->stats.refresh_rcvd_eorr, ol); + + olabels_free(ol); +} + +static void +ometric_rib_mem_element(const char *v, uint64_t count, uint64_t size, + uint64_t refs) +{ + if (count != UINT64_MAX) + ometric_set_int_with_label(rde_mem_count, count, "type", v, + NULL); + if (size != UINT64_MAX) + ometric_set_int_with_label(rde_mem_size, size, "type", v, NULL); + if (refs != UINT64_MAX) + ometric_set_int_with_label(rde_mem_ref_count, refs, "type", v, + NULL); +} + +static void +ometric_rib_mem(struct rde_memstats *stats) +{ + size_t pts = 0; + int i; + + for (i = 0; i < AID_MAX; i++) { + if (stats->pt_cnt[i] == 0) + continue; + pts += stats->pt_cnt[i] * pt_sizes[i]; + ometric_rib_mem_element(aid_vals[i].name, stats->pt_cnt[i], + stats->pt_cnt[i] * pt_sizes[i], UINT64_MAX); + } + ometric_rib_mem_element("rib", stats->rib_cnt, + stats->rib_cnt * sizeof(struct rib_entry), UINT64_MAX); + ometric_rib_mem_element("prefix", stats->prefix_cnt, + stats->prefix_cnt * sizeof(struct prefix), UINT64_MAX); + ometric_rib_mem_element("rde_aspath", stats->path_cnt, + stats->path_cnt * sizeof(struct rde_aspath), + stats->path_refs); + ometric_rib_mem_element("aspath", stats->aspath_cnt, + stats->aspath_size, UINT64_MAX); + ometric_rib_mem_element("community_entries", stats->comm_cnt, + stats->comm_cnt * sizeof(struct rde_community), UINT64_MAX); + ometric_rib_mem_element("community", stats->comm_nmemb, + stats->comm_size * sizeof(struct community), stats->comm_refs); + ometric_rib_mem_element("attributes_entries", stats->attr_cnt, + stats->attr_cnt * sizeof(struct attr), stats->attr_refs); + ometric_rib_mem_element("attributes", stats->attr_dcnt, + stats->attr_data, UINT64_MAX); + + ometric_rib_mem_element("total", UINT64_MAX, + pts + stats->prefix_cnt * sizeof(struct prefix) + + stats->rib_cnt * sizeof(struct rib_entry) + + stats->path_cnt * sizeof(struct rde_aspath) + + stats->aspath_size + stats->attr_cnt * sizeof(struct attr) + + stats->attr_data, UINT64_MAX); + + ometric_set_int(rde_table_count, stats->aset_cnt, NULL); + ometric_set_int_with_label(rde_set_size, stats->aset_size, + "type", "as_set", NULL); + ometric_set_int_with_label(rde_set_count, stats->aset_nmemb, + "type", "as_set", NULL); + ometric_set_int_with_label(rde_set_size, stats->pset_size, + "type", "prefix_set", NULL); + ometric_set_int_with_label(rde_set_count, stats->pset_cnt, + "type", "prefix_set", NULL); + ometric_rib_mem_element("set_total", UINT64_MAX, + stats->aset_size + stats->pset_size, UINT64_MAX); +} + +static void +ometric_tail(void) +{ + struct timeval elapsed_time; + double scrape; + + gettimeofday(&end_time, NULL); + timersub(&end_time, &start_time, &elapsed_time); + + scrape = (double)elapsed_time.tv_sec + + (double)elapsed_time.tv_usec / 1000000; + + ometric_set_float(bgpd_scrape_time, scrape, NULL); + ometric_output_all(); + + ometric_free_all(); +} + +const struct output ometric_output = { + .head = ometric_head, + .neighbor = ometric_neighbor_stats, + .rib_mem = ometric_rib_mem, + .tail = ometric_tail, +}; diff --git a/usr.sbin/bgpctl/parser.c b/usr.sbin/bgpctl/parser.c index 00682ff12b8..7838deb0dfc 100644 --- a/usr.sbin/bgpctl/parser.c +++ b/usr.sbin/bgpctl/parser.c @@ -1,4 +1,4 @@ -/* $OpenBSD: parser.c,v 1.114 2022/08/17 15:16:12 claudio Exp $ */ +/* $OpenBSD: parser.c,v 1.115 2022/10/17 12:01:19 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer @@ -141,6 +141,7 @@ static const struct token t_show[] = { { KEYWORD, "sets", SHOW_SET, NULL}, { KEYWORD, "rtr", SHOW_RTR, NULL}, { KEYWORD, "mrt", SHOW_MRT, t_show_mrt}, + { KEYWORD, "metric", SHOW_METRIC, NULL}, { ENDTOKEN, "", NONE, NULL} }; diff --git a/usr.sbin/bgpctl/parser.h b/usr.sbin/bgpctl/parser.h index 89634a2bc11..45fea196003 100644 --- a/usr.sbin/bgpctl/parser.h +++ b/usr.sbin/bgpctl/parser.h @@ -1,4 +1,4 @@ -/* $OpenBSD: parser.h,v 1.42 2022/02/06 09:52:32 claudio Exp $ */ +/* $OpenBSD: parser.h,v 1.43 2022/10/17 12:01:19 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer @@ -37,6 +37,7 @@ enum actions { SHOW_RIB_MEM, SHOW_NEXTHOP, SHOW_INTERFACE, + SHOW_METRIC, RELOAD, FIB, FIB_COUPLE, -- 2.20.1