/* $OpenBSD: eigrpctl.c,v 1.9 2017/02/22 14:18:25 renato Exp $ */ /* * Copyright (c) 2015 Renato Westphal * Copyright (c) 2005 Claudio Jeker * Copyright (c) 2004, 2005 Esben Norby * Copyright (c) 2003 Henning Brauer * * 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 #include #include #include #include #include #include "eigrp.h" #include "eigrpd.h" #include "eigrpe.h" #include "rde.h" #include "log.h" #include "parser.h" __dead void usage(void); uint64_t get_ifms_type(uint8_t); int show_interface_msg(struct imsg *, struct parse_result *); int show_interface_detail_msg(struct imsg *, struct parse_result *); const char *print_link(int); const char *fmt_timeframe_core(time_t); int show_nbr_msg(struct imsg *, struct parse_result *); int show_topology_msg(struct imsg *, struct parse_result *); int show_topology_detail_msg(struct imsg *, struct parse_result *); void show_fib_head(void); int show_fib_msg(struct imsg *, struct parse_result *); void show_interface_head(void); const char * get_media_descr(uint64_t); const char * get_linkstate(uint8_t, int); void print_baudrate(uint64_t); int show_fib_interface_msg(struct imsg *); int show_stats_msg(struct imsg *, struct parse_result *); struct imsgbuf *ibuf; __dead void usage(void) { extern char *__progname; fprintf(stderr, "usage: %s [-s socket] command [argument ...]\n", __progname); exit(1); } int main(int argc, char *argv[]) { struct sockaddr_un sun; struct parse_result *res; struct imsg imsg; unsigned int ifidx = 0; int ctl_sock; int done = 0; int n, verbose = 0; int ch; char *sockname; struct ctl_show_topology_req treq; struct ctl_nbr nbr; sockname = EIGRPD_SOCKET; while ((ch = getopt(argc, argv, "s:")) != -1) { switch (ch) { case 's': sockname = optarg; break; default: usage(); /* NOTREACHED */ } } argc -= optind; argv += optind; /* parse options */ if ((res = parse(argc, argv)) == NULL) exit(1); /* connect to eigrpd control socket */ if ((ctl_sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) err(1, "socket"); memset(&sun, 0, sizeof(sun)); sun.sun_family = AF_UNIX; strlcpy(sun.sun_path, sockname, sizeof(sun.sun_path)); if (connect(ctl_sock, (struct sockaddr *)&sun, sizeof(sun)) == -1) err(1, "connect: %s", sockname); if (pledge("stdio", NULL) == -1) err(1, "pledge"); if ((ibuf = malloc(sizeof(struct imsgbuf))) == NULL) err(1, NULL); imsg_init(ibuf, ctl_sock); done = 0; /* process user request */ switch (res->action) { case NONE: usage(); /* not reached */ case SHOW: case SHOW_IFACE: printf("%-4s %-5s %-11s %-18s %-10s %-8s %3s\n", "AF", "AS", "Interface", "Address", "Linkstate", "Uptime", "nc"); /*FALLTHROUGH*/ case SHOW_IFACE_DTAIL: if (*res->ifname) { ifidx = if_nametoindex(res->ifname); if (ifidx == 0) errx(1, "no such interface %s", res->ifname); } imsg_compose(ibuf, IMSG_CTL_SHOW_INTERFACE, 0, 0, -1, &ifidx, sizeof(ifidx)); break; case SHOW_NBR: printf("%-4s %-5s %-18s %-11s %-10s %8s\n", "AF", "AS", "Address", "Iface", "Holdtime", "Uptime"); imsg_compose(ibuf, IMSG_CTL_SHOW_NBR, 0, 0, -1, NULL, 0); break; case SHOW_TOPOLOGY: memset(&treq, 0, sizeof(treq)); treq.af = res->family; memcpy(&treq.prefix, &res->addr, sizeof(res->addr)); treq.prefixlen = res->prefixlen; treq.flags = res->flags; if (!eigrp_addrisset(res->family, &res->addr)) printf(" %-4s %-5s %-18s %-15s %-12s %s\n", "AF", "AS", "Destination", "Nexthop", "Interface", "Distance"); imsg_compose(ibuf, IMSG_CTL_SHOW_TOPOLOGY, 0, 0, -1, &treq, sizeof(treq)); break; case SHOW_FIB: show_fib_head(); imsg_compose(ibuf, IMSG_CTL_KROUTE, 0, 0, -1, &res->flags, sizeof(res->flags)); break; case SHOW_FIB_IFACE: if (*res->ifname) imsg_compose(ibuf, IMSG_CTL_IFINFO, 0, 0, -1, res->ifname, sizeof(res->ifname)); else imsg_compose(ibuf, IMSG_CTL_IFINFO, 0, 0, -1, NULL, 0); show_interface_head(); break; case SHOW_STATS: imsg_compose(ibuf, IMSG_CTL_SHOW_STATS, 0, 0, -1, NULL, 0); break; case CLEAR_NBR: memset(&nbr, 0, sizeof(nbr)); nbr.af = res->family; nbr.as = res->as; memcpy(&nbr.addr, &res->addr, sizeof(res->addr)); imsg_compose(ibuf, IMSG_CTL_CLEAR_NBR, 0, 0, -1, &nbr, sizeof(nbr)); done = 1; break; case FIB: errx(1, "fib couple|decouple"); break; case FIB_COUPLE: imsg_compose(ibuf, IMSG_CTL_FIB_COUPLE, 0, 0, -1, NULL, 0); printf("couple request sent.\n"); done = 1; break; case FIB_DECOUPLE: imsg_compose(ibuf, IMSG_CTL_FIB_DECOUPLE, 0, 0, -1, NULL, 0); printf("decouple request sent.\n"); done = 1; break; case LOG_VERBOSE: verbose = 1; /* FALLTHROUGH */ case LOG_BRIEF: imsg_compose(ibuf, IMSG_CTL_LOG_VERBOSE, 0, 0, -1, &verbose, sizeof(verbose)); printf("logging request sent.\n"); done = 1; break; case RELOAD: imsg_compose(ibuf, IMSG_CTL_RELOAD, 0, 0, -1, NULL, 0); printf("reload request sent.\n"); done = 1; break; } while (ibuf->w.queued) if (msgbuf_write(&ibuf->w) <= 0 && errno != EAGAIN) err(1, "write error"); while (!done) { if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) errx(1, "imsg_read error"); if (n == 0) errx(1, "pipe closed"); while (!done) { if ((n = imsg_get(ibuf, &imsg)) == -1) errx(1, "imsg_get error"); if (n == 0) break; switch (res->action) { case SHOW: case SHOW_IFACE: done = show_interface_msg(&imsg, res); break; case SHOW_IFACE_DTAIL: done = show_interface_detail_msg(&imsg, res); break; case SHOW_NBR: done = show_nbr_msg(&imsg, res); break; case SHOW_TOPOLOGY: if (eigrp_addrisset(res->family, &res->addr)) done = show_topology_detail_msg(&imsg, res); else done = show_topology_msg(&imsg, res); break; case SHOW_FIB: done = show_fib_msg(&imsg, res); break; case SHOW_FIB_IFACE: done = show_fib_interface_msg(&imsg); break; case SHOW_STATS: done = show_stats_msg(&imsg, res); break; case CLEAR_NBR: case NONE: case FIB: case FIB_COUPLE: case FIB_DECOUPLE: case LOG_VERBOSE: case LOG_BRIEF: case RELOAD: break; } imsg_free(&imsg); } } close(ctl_sock); free(ibuf); return (0); } uint64_t get_ifms_type(uint8_t if_type) { switch (if_type) { case IFT_ETHER: return (IFM_ETHER); case IFT_FDDI: return (IFM_FDDI); case IFT_CARP: return (IFM_CARP); case IFT_PPP: return (IFM_TDM); default: return (0); } } int show_interface_msg(struct imsg *imsg, struct parse_result *res) { struct ctl_iface *iface; char *addr; switch (imsg->hdr.type) { case IMSG_CTL_SHOW_INTERFACE: if (imsg->hdr.len < IMSG_HEADER_SIZE + sizeof(struct ctl_iface)) errx(1, "wrong imsg len"); iface = imsg->data; if (res->family != AF_UNSPEC && res->family != iface->af) break; if (res->as != 0 && res->as != iface->as) break; if (asprintf(&addr, "%s/%d", log_addr(iface->af, &iface->addr), iface->prefixlen) == -1) err(1, NULL); printf("%-4s %-5u %-11s %-18s", af_name(iface->af), iface->as, iface->name, addr); if (strlen(addr) > 18) printf("\n%41s", " "); printf(" %-10s %-8s %3u\n", get_linkstate(iface->if_type, iface->linkstate), fmt_timeframe_core(iface->uptime), iface->nbr_cnt); free(addr); break; case IMSG_CTL_END: printf("\n"); return (1); default: break; } return (0); } int show_interface_detail_msg(struct imsg *imsg, struct parse_result *res) { struct ctl_iface *iface; switch (imsg->hdr.type) { case IMSG_CTL_SHOW_INTERFACE: if (imsg->hdr.len < IMSG_HEADER_SIZE + sizeof(struct ctl_iface)) errx(1, "wrong imsg len"); iface = imsg->data; if (res->family != AF_UNSPEC && res->family != iface->af) break; if (res->as != 0 && res->as != iface->as) break; printf("\n"); printf("Interface %s, line protocol is %s\n", iface->name, print_link(iface->flags)); printf(" Autonomous System %u, Address Family %s\n", iface->as, af_name(iface->af)); printf(" Internet address %s/%d\n", log_addr(iface->af, &iface->addr), iface->prefixlen); printf(" Linkstate %s, network type %s\n", get_linkstate(iface->if_type, iface->linkstate), if_type_name(iface->type)); printf(" Delay %u usec, Bandwidth %u Kbit/sec\n", iface->delay, iface->bandwidth); if (iface->passive) printf(" Passive interface (No Hellos)\n"); else { printf(" Hello interval %u, Hello holdtime %u\n", iface->hello_interval, iface->hello_holdtime); printf(" Split-horizon %s\n", (iface->splithorizon) ? "enabled" : "disabled"); printf(" Neighbor count is %d\n", iface->nbr_cnt); } printf(" Uptime %s\n", fmt_timeframe_core(iface->uptime)); break; case IMSG_CTL_END: printf("\n"); return (1); default: break; } return (0); } const char * print_link(int state) { if (state & IFF_UP) return ("UP"); else return ("DOWN"); } #define TF_BUFS 8 #define TF_LEN 9 const char * fmt_timeframe_core(time_t t) { char *buf; static char tfbuf[TF_BUFS][TF_LEN]; /* ring buffer */ static int idx = 0; unsigned int sec, min, hrs, day; unsigned long long week; if (t == 0) return ("00:00:00"); buf = tfbuf[idx++]; if (idx == TF_BUFS) idx = 0; week = t; sec = week % 60; week /= 60; min = week % 60; week /= 60; hrs = week % 24; week /= 24; day = week % 7; week /= 7; if (week > 0) snprintf(buf, TF_LEN, "%02lluw%01ud%02uh", week, day, hrs); else if (day > 0) snprintf(buf, TF_LEN, "%01ud%02uh%02um", day, hrs, min); else snprintf(buf, TF_LEN, "%02u:%02u:%02u", hrs, min, sec); return (buf); } int show_nbr_msg(struct imsg *imsg, struct parse_result *res) { struct ctl_nbr *nbr; const char *addr; switch (imsg->hdr.type) { case IMSG_CTL_SHOW_NBR: if (imsg->hdr.len < IMSG_HEADER_SIZE + sizeof(struct ctl_nbr)) errx(1, "wrong imsg len"); nbr = imsg->data; if (res->family != AF_UNSPEC && res->family != nbr->af) break; if (res->as != 0 && res->as != nbr->as) break; addr = log_addr(nbr->af, &nbr->addr); printf("%-4s %-5u %-18s", af_name(nbr->af), nbr->as, addr); if (strlen(addr) > 18) printf("\n%29s", " "); printf(" %-11s %-10u %8s\n", nbr->ifname, nbr->hello_holdtime, fmt_timeframe_core(nbr->uptime)); break; case IMSG_CTL_END: printf("\n"); return (1); default: break; } return (0); } static int connected_check(int af, union eigrpd_addr *addr) { switch (af) { case AF_INET: if (addr->v4.s_addr == INADDR_ANY) return (1); break; case AF_INET6: if (IN6_IS_ADDR_UNSPECIFIED(&addr->v6)) return (1); break; default: break; } return (0); } int show_topology_msg(struct imsg *imsg, struct parse_result *res) { struct ctl_rt *rt; char *dstnet, *nexthop, *rdistance; char flag; switch (imsg->hdr.type) { case IMSG_CTL_SHOW_TOPOLOGY: if (imsg->hdr.len < IMSG_HEADER_SIZE + sizeof(struct ctl_rt)) errx(1, "wrong imsg len"); rt = imsg->data; if (res->family != AF_UNSPEC && res->family != rt->af) break; if (res->as != 0 && res->as != rt->as) break; if (rt->state & DUAL_STA_ACTIVE_ALL) flag = 'A'; else if (rt->flags & F_CTL_RT_SUCCESSOR) flag = 'S'; else if (rt->flags & F_CTL_RT_FSUCCESSOR) flag = 'F'; else flag = ' '; if (asprintf(&dstnet, "%s/%d", log_addr(rt->af, &rt->prefix), rt->prefixlen) == -1) err(1, NULL); if (connected_check(rt->af, &rt->nexthop)) { if (asprintf(&nexthop, "Connected") == -1) err(1, NULL); if (asprintf(&rdistance, "-") == -1) err(1, NULL); } else { if (asprintf(&nexthop, "%s", log_addr(rt->af, &rt->nexthop)) == -1) err(1, NULL); if (asprintf(&rdistance, "%u", rt->rdistance) == -1) err(1, NULL); } printf("%c %-4s %-5u %-18s", flag, af_name(rt->af), rt->as, dstnet); if (strlen(dstnet) > 18) printf("\n%31s", " "); printf(" %-15s", nexthop); if (strlen(nexthop) > 15) printf("\n%47s", " "); printf(" %-12s %u/%s\n", rt->ifname, rt->distance, rdistance); free(dstnet); free(nexthop); free(rdistance); break; case IMSG_CTL_END: printf("\n"); return (1); default: break; } return (0); } int show_topology_detail_msg(struct imsg *imsg, struct parse_result *res) { struct ctl_rt *rt; char *dstnet = NULL, *state = NULL, *type, *nexthop; struct in_addr addr; switch (imsg->hdr.type) { case IMSG_CTL_SHOW_TOPOLOGY: if (imsg->hdr.len < IMSG_HEADER_SIZE + sizeof(struct ctl_rt)) errx(1, "wrong imsg len"); rt = imsg->data; if (res->family != AF_UNSPEC && res->family != rt->af) break; if (res->as != 0 && res->as != rt->as) break; if (rt->flags & F_CTL_RT_FIRST) { if (asprintf(&dstnet, "%s/%d", log_addr(rt->af, &rt->prefix), rt->prefixlen) == -1) err(1, NULL); if (rt->state & DUAL_STA_ACTIVE_ALL) { if (asprintf(&state, "Active") == -1) err(1, NULL); } else { if (asprintf(&state, "Passive") == -1) err(1, NULL); } } if (rt->type == EIGRP_ROUTE_INTERNAL) { if (asprintf(&type, "Internal") == -1) err(1, NULL); } else { if (asprintf(&type, "External") == -1) err(1, NULL); } if (connected_check(rt->af, &rt->nexthop)) { if (asprintf(&nexthop, "Connected") == -1) err(1, NULL); } else { if (asprintf(&nexthop, "Neighbor %s", log_addr(rt->af, &rt->nexthop)) == -1) err(1, NULL); } if (rt->flags & F_CTL_RT_FIRST) { printf("Network %s\n", dstnet); printf("Autonomous System %u, Address Family %s\n", rt->as, af_name(rt->af)); printf("DUAL State: %s, Feasible Distance: %u\n", state, rt->fdistance); printf("Routes:\n"); } printf(" Interface %s - %s\n", rt->ifname, nexthop); printf(" Distance: %u", rt->distance); if (!connected_check(rt->af, &rt->nexthop)) printf(", Reported Distance: %u", rt->rdistance); printf(", route is %s\n", type); printf(" Vector metric:\n"); printf(" Minimum bandwidth is %u Kbit\n", rt->metric.bandwidth); printf(" Total delay is %u microseconds\n", rt->metric.delay); printf(" Reliability is %u/255\n", rt->metric.reliability); printf(" Load is %u/255\n", rt->metric.load); printf(" Minimum MTU is %u\n", rt->metric.mtu); printf(" Hop count is %u\n", rt->metric.hop_count); if (rt->type == EIGRP_ROUTE_EXTERNAL) { addr.s_addr = htonl(rt->emetric.routerid); printf(" External data:\n"); printf(" Originating router is %s\n", inet_ntoa(addr)); printf(" AS number of route is %u\n", rt->emetric.as); printf(" External protocol is %s\n", ext_proto_name(rt->emetric.protocol)); printf(" External metric is %u\n", rt->emetric.metric); printf(" Administrator tag is %u\n", rt->emetric.tag); } printf("\n"); free(dstnet); free(state); free(type); free(nexthop); break; case IMSG_CTL_END: return (1); default: break; } return (0); } void show_fib_head(void) { printf("flags: * = valid, D = EIGRP, C = Connected, S = Static\n"); printf("%-6s %-4s %-20s %-17s\n", "Flags", "Prio", "Destination", "Nexthop"); } int show_fib_msg(struct imsg *imsg, struct parse_result *res) { struct kroute *k; char *p; switch (imsg->hdr.type) { case IMSG_CTL_KROUTE: if (imsg->hdr.len < IMSG_HEADER_SIZE + sizeof(struct kroute)) errx(1, "wrong imsg len"); k = imsg->data; if (res->family != AF_UNSPEC && res->family != k->af) break; if (k->flags & F_DOWN) printf(" "); else printf("*"); if (!(k->flags & F_KERNEL)) printf("D"); else if (k->flags & F_CONNECTED) printf("C"); else if (k->flags & F_STATIC) printf("S"); else printf(" "); printf("%-5s", (k->flags & F_CTL_EXTERNAL) ? " EX" : ""); printf("%4d ", k->priority); if (asprintf(&p, "%s/%u", log_addr(k->af, &k->prefix), k->prefixlen) == -1) err(1, NULL); printf("%-20s ", p); if (strlen(p) > 20) printf("\n%33s", " "); free(p); if (eigrp_addrisset(k->af, &k->nexthop)) { switch (k->af) { case AF_INET: printf("%s", log_addr(k->af, &k->nexthop)); break; case AF_INET6: printf("%s", log_in6addr_scope(&k->nexthop.v6, k->ifindex)); break; default: break; } } else if (k->flags & F_CONNECTED) printf("link#%u", k->ifindex); printf("\n"); break; case IMSG_CTL_END: printf("\n"); return (1); default: break; } return (0); } void show_interface_head(void) { printf("%-15s%-15s%s\n", "Interface", "Flags", "Link state"); } const struct if_status_description if_status_descriptions[] = LINK_STATE_DESCRIPTIONS; const struct ifmedia_description ifm_type_descriptions[] = IFM_TYPE_DESCRIPTIONS; const char * get_media_descr(uint64_t media_type) { const struct ifmedia_description *p; for (p = ifm_type_descriptions; p->ifmt_string != NULL; p++) if (media_type == p->ifmt_word) return (p->ifmt_string); return ("unknown"); } const char * get_linkstate(uint8_t if_type, int link_state) { const struct if_status_description *p; static char buf[8]; for (p = if_status_descriptions; p->ifs_string != NULL; p++) { if (LINK_STATE_DESC_MATCH(p, if_type, link_state)) return (p->ifs_string); } snprintf(buf, sizeof(buf), "[#%d]", link_state); return (buf); } void print_baudrate(uint64_t baudrate) { if (baudrate > IF_Gbps(1)) printf("%llu GBit/s", baudrate / IF_Gbps(1)); else if (baudrate > IF_Mbps(1)) printf("%llu MBit/s", baudrate / IF_Mbps(1)); else if (baudrate > IF_Kbps(1)) printf("%llu KBit/s", baudrate / IF_Kbps(1)); else printf("%llu Bit/s", baudrate); } int show_fib_interface_msg(struct imsg *imsg) { struct kif *k; uint64_t ifms_type; switch (imsg->hdr.type) { case IMSG_CTL_IFINFO: if (imsg->hdr.len < IMSG_HEADER_SIZE + sizeof(struct kif)) errx(1, "wrong imsg len"); k = imsg->data; printf("%-15s", k->ifname); printf("%-15s", k->flags & IFF_UP ? "UP" : ""); ifms_type = get_ifms_type(k->if_type); if (ifms_type) printf("%s, ", get_media_descr(ifms_type)); printf("%s", get_linkstate(k->if_type, k->link_state)); if (k->link_state != LINK_STATE_DOWN && k->baudrate > 0) { printf(", "); print_baudrate(k->baudrate); } printf("\n"); break; case IMSG_CTL_END: printf("\n"); return (1); default: break; } return (0); } int show_stats_msg(struct imsg *imsg, struct parse_result *res) { struct ctl_stats *cs; switch (imsg->hdr.type) { case IMSG_CTL_SHOW_STATS: if (imsg->hdr.len < IMSG_HEADER_SIZE + sizeof(struct ctl_stats)) errx(1, "wrong imsg len"); cs = imsg->data; if (res->family != AF_UNSPEC && res->family != cs->af) break; if (res->as != 0 && res->as != cs->as) break; printf("Address Family %s, Autonomous System %u\n", af_name(cs->af), cs->as); printf(" Hellos sent/received: %u/%u\n", cs->stats.hellos_sent, cs->stats.hellos_recv); printf(" Updates sent/received: %u/%u\n", cs->stats.updates_sent, cs->stats.updates_recv); printf(" Queries sent/received: %u/%u\n", cs->stats.queries_sent, cs->stats.queries_recv); printf(" Replies sent/received: %u/%u\n", cs->stats.replies_sent, cs->stats.replies_recv); printf(" Acks sent/received: %u/%u\n", cs->stats.acks_sent, cs->stats.acks_recv); printf(" SIA-Queries sent/received: %u/%u\n", cs->stats.squeries_sent, cs->stats.squeries_recv); printf(" SIA-Replies sent/received: %u/%u\n", cs->stats.sreplies_sent, cs->stats.sreplies_recv); break; case IMSG_CTL_END: printf("\n"); return (1); default: break; } return (0); }