diff options
-rw-r--r-- | usr.sbin/eigrpctl/Makefile | 16 | ||||
-rw-r--r-- | usr.sbin/eigrpctl/eigrpctl.8 | 142 | ||||
-rw-r--r-- | usr.sbin/eigrpctl/eigrpctl.c | 782 | ||||
-rw-r--r-- | usr.sbin/eigrpctl/parser.c | 454 | ||||
-rw-r--r-- | usr.sbin/eigrpctl/parser.h | 62 |
5 files changed, 1456 insertions, 0 deletions
diff --git a/usr.sbin/eigrpctl/Makefile b/usr.sbin/eigrpctl/Makefile new file mode 100644 index 00000000000..60b1a7dd966 --- /dev/null +++ b/usr.sbin/eigrpctl/Makefile @@ -0,0 +1,16 @@ +# $OpenBSD: Makefile,v 1.1 2015/10/02 04:31:52 renato Exp $ + +.PATH: ${.CURDIR}/../eigrpd + +PROG= eigrpctl +SRCS= util.c log.c eigrpctl.c parser.c +CFLAGS+= -Wall +CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes +CFLAGS+= -Wshadow -Wpointer-arith -Wcast-qual +CFLAGS+= -Wsign-compare +CFLAGS+= -I${.CURDIR} -I${.CURDIR}/../eigrpd +LDADD= -lutil +DPADD= ${LIBUTIL} +MAN= eigrpctl.8 + +.include <bsd.prog.mk> diff --git a/usr.sbin/eigrpctl/eigrpctl.8 b/usr.sbin/eigrpctl/eigrpctl.8 new file mode 100644 index 00000000000..032d88a6b6c --- /dev/null +++ b/usr.sbin/eigrpctl/eigrpctl.8 @@ -0,0 +1,142 @@ +.\" $OpenBSD: eigrpctl.8,v 1.1 2015/10/02 04:31:52 renato Exp $ +.\" +.\" Copyright (c) 2015 Renato Westphal <renato@openbsd.org> +.\" Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org> +.\" +.\" 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. +.\" +.Dd $Mdocdate: October 2 2015 $ +.Dt EIGRPCTL 8 +.Os +.Sh NAME +.Nm eigrpctl +.Nd control the Enhanced Interior Gateway Routing Protocol daemon +.Sh SYNOPSIS +.Nm +.Ar command +.Op Ar argument ... +.Sh DESCRIPTION +The +.Nm +program controls the +.Xr eigrpd 8 +daemon. +Commands may be abbreviated to the minimum unambiguous prefix; for example, +.Cm s i +for +.Cm show interfaces . +.Pp +The following commands are available: +.Bl -tag -width Ds +.It Cm fib couple +Insert the learned routes into the Forwarding Information Base +a.k.a. the kernel routing table. +.It Cm fib decouple +Remove the learned routes from the Forwarding Information Base +a.k.a. the kernel routing table. +Decoupling the FIB from an EIGRP router may create routing loops and could cause +major routing issues in the complete EIGRP cloud. +Only routers with just one link to the EIGRP cloud can safely decouple the FIB. +.It Cm log brief +Disable verbose debug logging. +.It Cm log verbose +Enable verbose debug logging. +.It Cm reload +Reload the configuration file. +.It Xo +.Cm show fib +.Op Cm family Ar family +.Op Ar filter +.Xc +Show the Forwarding Information Base. +.Ar family , +if given, limit the output to the given address family. +.Ar filter +can be any of the following: +.Pp +.Bl -tag -width "interfaceXXinterfaceXX" -compact +.It Cm connected +Show only connected routes. +.It Cm interface Op Ar interface +Show only interfaces or the specified +.Ar interface . +.It Cm eigrp +Show only EIGRP routes. +.It Cm static +Show only static routes. +.El +.Pp +.Cm connected , +.Cm eigrp +and +.Cm static +may be specified together. +.It Xo +.Cm show interfaces +.Op Cm family Ar family +.Op Cm as Ar as +.Op Ar interface +.Xc +Show details for all EIGRP enabled interfaces or the specified +.Ar interface . +.Ar family +and +.Ar as , +if given, limit the output to the given address family and/or autonomous system. +.It Xo +.Cm show neighbor +.Op Cm family Ar family +.Op Cm as Ar as +.Xc +Show neighbors. +.Ar family +and +.Ar as , +if given, limit the output to the given address family and/or autonomous system. +.It Xo +.Cm show topology +.Op Cm family Ar family +.Op Cm as Ar as +.Op Ar prefix | filter +.Xc +Show the topology table. +.Ar family +and +.Ar as , +if given, limit the output to the given address family and/or autonomous system. +.Ar prefix +can be specified to show the entries matching a destination prefix. +.Ar filter +can be any of the following: +.Pp +.Bl -tag -width "interfaceXXinterfaceXX" -compact +.It Cm active +Show only active entries. +.It Cm all-links +Show all entries. +.El +.Sh FILES +.Bl -tag -width "/var/run/eigrpd.sockXX" -compact +.It Pa /var/run/eigrpd.sock +.Ux Ns -domain +socket used for communication with +.Xr eigrpd 8 . +.El +.Sh SEE ALSO +.Xr eigrpd.conf 5 , +.Xr eigrpd 8 +.Sh HISTORY +The +.Nm +program first appeared in +.Ox 5.8 . diff --git a/usr.sbin/eigrpctl/eigrpctl.c b/usr.sbin/eigrpctl/eigrpctl.c new file mode 100644 index 00000000000..1810dbd1047 --- /dev/null +++ b/usr.sbin/eigrpctl/eigrpctl.c @@ -0,0 +1,782 @@ +/* $OpenBSD: eigrpctl.c,v 1.1 2015/10/02 04:31:52 renato Exp $ */ + +/* + * Copyright (c) 2015 Renato Westphal <renato@openbsd.org> + * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org> + * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org> + * Copyright (c) 2003 Henning Brauer <henning@openbsd.org> + * + * 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 <sys/types.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <net/if_media.h> +#include <net/if_types.h> + +#include <err.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#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 *); + +struct imsgbuf *ibuf; + +__dead void +usage(void) +{ + extern char *__progname; + + fprintf(stderr, "usage: %s 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; + struct ctl_show_topology_req treq; + + /* parse options */ + if ((res = parse(argc - 1, argv + 1)) == 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, EIGRPD_SOCKET, sizeof(sun.sun_path)); + if (connect(ctl_sock, (struct sockaddr *)&sun, sizeof(sun)) == -1) + err(1, "connect: %s", EIGRPD_SOCKET); + + 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 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) + 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 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"); + if (dstnet) + free(dstnet); + if (state) + 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); +} diff --git a/usr.sbin/eigrpctl/parser.c b/usr.sbin/eigrpctl/parser.c new file mode 100644 index 00000000000..ec940790272 --- /dev/null +++ b/usr.sbin/eigrpctl/parser.c @@ -0,0 +1,454 @@ +/* $OpenBSD: parser.c,v 1.1 2015/10/02 04:31:52 renato Exp $ */ + +/* + * Copyright (c) 2015 Renato Westphal <renato@openbsd.org> + * Copyright (c) 2004 Esben Norby <norby@openbsd.org> + * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> + * + * 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 <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <err.h> +#include <errno.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <netdb.h> + +#include "eigrpd.h" + +#include "parser.h" + +enum token_type { + NOTOKEN, + ENDTOKEN, + KEYWORD, + FAMILY, + ASNUM, + ADDRESS, + FLAG, + PREFIX, + IFNAME +}; + +struct token { + enum token_type type; + const char *keyword; + int value; + const struct token *next; +}; + +static const struct token t_main[]; +static const struct token t_fib[]; +static const struct token t_show[]; +static const struct token t_show_iface[]; +static const struct token t_show_iface_af[]; +static const struct token t_show_iface_as[]; +static const struct token t_show_nbr[]; +static const struct token t_show_nbr_af[]; +static const struct token t_show_nbr_as[]; +static const struct token t_show_topology[]; +static const struct token t_show_topology_af[]; +static const struct token t_show_topology_as[]; +static const struct token t_show_fib[]; +static const struct token t_show_fib_af[]; +static const struct token t_log[]; + +static const struct token t_main[] = { + {KEYWORD, "reload", RELOAD, NULL}, + {KEYWORD, "fib", FIB, t_fib}, + {KEYWORD, "show", SHOW, t_show}, + {KEYWORD, "log", NONE, t_log}, + {ENDTOKEN, "", NONE, NULL} +}; + +static const struct token t_fib[] = { + { KEYWORD, "couple", FIB_COUPLE, NULL}, + { KEYWORD, "decouple", FIB_DECOUPLE, NULL}, + { ENDTOKEN, "", NONE, NULL} +}; + +static const struct token t_show[] = { + {NOTOKEN, "", NONE, NULL}, + {KEYWORD, "interfaces", SHOW_IFACE, t_show_iface}, + {KEYWORD, "neighbor", SHOW_NBR, t_show_nbr}, + {KEYWORD, "topology", SHOW_TOPOLOGY, t_show_topology}, + {KEYWORD, "fib", SHOW_FIB, t_show_fib}, + {ENDTOKEN, "", NONE, NULL} +}; + +static const struct token t_show_iface[] = { + {NOTOKEN, "", NONE, NULL}, + {KEYWORD, "family", NONE, t_show_iface_af}, + {KEYWORD, "as", NONE, t_show_iface_as}, + {KEYWORD, "detail", SHOW_IFACE_DTAIL, NULL}, + {IFNAME, "", SHOW_IFACE_DTAIL, NULL}, + {ENDTOKEN, "", NONE, NULL} +}; + +static const struct token t_show_iface_af[] = { + {FAMILY, "", NONE, t_show_iface}, + {ENDTOKEN, "", NONE, NULL} +}; + +static const struct token t_show_iface_as[] = { + {ASNUM, "", NONE, t_show_iface}, + {ENDTOKEN, "", NONE, NULL} +}; + +static const struct token t_show_nbr[] = { + {NOTOKEN, "", NONE, NULL}, + {KEYWORD, "family", NONE, t_show_nbr_af}, + {KEYWORD, "as", NONE, t_show_nbr_as}, + {ENDTOKEN, "", NONE, NULL} +}; + +static const struct token t_show_nbr_af[] = { + {FAMILY, "", NONE, t_show_nbr}, + {ENDTOKEN, "", NONE, NULL} +}; + +static const struct token t_show_nbr_as[] = { + {ASNUM, "", NONE, t_show_nbr}, + {ENDTOKEN, "", NONE, NULL} +}; + +static const struct token t_show_topology[] = { + {NOTOKEN, "", NONE, NULL}, + {KEYWORD, "family", NONE, t_show_topology_af}, + {KEYWORD, "as", NONE, t_show_topology_as}, + {PREFIX, "", NONE, NULL}, + {FLAG, "active", F_CTL_ACTIVE, NONE}, + {FLAG, "all-links", F_CTL_ALLLINKS, NONE}, + {ENDTOKEN, "", NONE, NULL} +}; + +static const struct token t_show_topology_af[] = { + {FAMILY, "", NONE, t_show_topology}, + {ENDTOKEN, "", NONE, NULL} +}; + +static const struct token t_show_topology_as[] = { + {ASNUM, "", NONE, t_show_topology}, + {ENDTOKEN, "", NONE, NULL} +}; + +static const struct token t_show_fib[] = { + {NOTOKEN, "", NONE, NULL}, + {KEYWORD, "family", NONE, t_show_fib_af}, + {KEYWORD, "interface", SHOW_FIB_IFACE, t_show_iface}, + {FLAG, "connected", F_CONNECTED, t_show_fib}, + {FLAG, "static", F_STATIC, t_show_fib}, + {FLAG, "eigrp", F_EIGRPD_INSERTED, t_show_fib}, + {ENDTOKEN, "", NONE, NULL} +}; + +static const struct token t_show_fib_af[] = { + {FAMILY, "", NONE, t_show_fib}, + {ENDTOKEN, "", NONE, NULL} +}; + +static const struct token t_log[] = { + {KEYWORD, "verbose", LOG_VERBOSE, NULL}, + {KEYWORD, "brief", LOG_BRIEF, NULL}, + {ENDTOKEN, "", NONE, NULL} +}; + +static const struct token *match_token(const char *, const struct token *, + struct parse_result *); +static void show_valid_args(const struct token *); + +struct parse_result * +parse(int argc, char *argv[]) +{ + static struct parse_result res; + const struct token *table = t_main; + const struct token *match; + + memset(&res, 0, sizeof(res)); + + while (argc >= 0) { + if ((match = match_token(argv[0], table, &res)) == NULL) { + fprintf(stderr, "valid commands/args:\n"); + show_valid_args(table); + return (NULL); + } + + argc--; + argv++; + + if (match->type == NOTOKEN || match->next == NULL) + break; + + table = match->next; + } + + if (argc > 0) { + fprintf(stderr, "superfluous argument: %s\n", argv[0]); + return (NULL); + } + + return (&res); +} + +static const struct token * +match_token(const char *word, const struct token *table, + struct parse_result *res) +{ + unsigned int i, match; + const struct token *t = NULL; + + match = 0; + + for (i = 0; table[i].type != ENDTOKEN; i++) { + switch (table[i].type) { + case NOTOKEN: + if (word == NULL || strlen(word) == 0) { + match++; + t = &table[i]; + } + break; + case KEYWORD: + if (word != NULL && strncmp(word, table[i].keyword, + strlen(word)) == 0) { + match++; + t = &table[i]; + if (t->value) + res->action = t->value; + } + break; + case FLAG: + if (word != NULL && strncmp(word, table[i].keyword, + strlen(word)) == 0) { + match++; + t = &table[i]; + res->flags |= t->value; + } + break; + case FAMILY: + if (word == NULL) + break; + if (!strcmp(word, "inet") || + !strcasecmp(word, "IPv4")) { + match++; + t = &table[i]; + res->family = AF_INET; + } + if (!strcmp(word, "inet6") || + !strcasecmp(word, "IPv6")) { + match++; + t = &table[i]; + res->family = AF_INET6; + } + break; + case ASNUM: + if (parse_asnum(word, &res->as)) { + match++; + t = &table[i]; + } + break; + case ADDRESS: + if (parse_addr(word, &res->family, &res->addr)) { + match++; + t = &table[i]; + if (t->value) + res->action = t->value; + } + break; + case PREFIX: + if (parse_prefix(word, &res->family, &res->addr, + &res->prefixlen)) { + match++; + t = &table[i]; + if (t->value) + res->action = t->value; + } + break; + case IFNAME: + if (!match && word != NULL && strlen(word) > 0) { + if (strlcpy(res->ifname, word, + sizeof(res->ifname)) >= + sizeof(res->ifname)) + err(1, "interface name too long"); + match++; + t = &table[i]; + if (t->value) + res->action = t->value; + } + break; + + case ENDTOKEN: + break; + } + } + + if (match != 1) { + if (word == NULL) + fprintf(stderr, "missing argument:\n"); + else if (match > 1) + fprintf(stderr, "ambiguous argument: %s\n", word); + else if (match < 1) + fprintf(stderr, "unknown argument: %s\n", word); + return (NULL); + } + + return (t); +} + +static void +show_valid_args(const struct token *table) +{ + int i; + + for (i = 0; table[i].type != ENDTOKEN; i++) { + switch (table[i].type) { + case NOTOKEN: + fprintf(stderr, " <cr>\n"); + break; + case KEYWORD: + case FLAG: + fprintf(stderr, " %s\n", table[i].keyword); + break; + case FAMILY: + fprintf(stderr, " [ inet | inet6 | IPv4 | IPv6 ]\n"); + break; + case ASNUM: + fprintf(stderr, " <asnum>\n"); + break; + case ADDRESS: + fprintf(stderr, " <address>\n"); + break; + case PREFIX: + fprintf(stderr, " <address>[/<len>]\n"); + break; + case IFNAME: + fprintf(stderr, " <interface>\n"); + break; + case ENDTOKEN: + break; + } + } +} + +int +parse_asnum(const char *word, uint16_t *asnum) +{ + const char *errstr; + uint32_t uval; + + if (word == NULL) + return (0); + + uval = strtonum(word, EIGRP_MIN_AS, EIGRP_MAX_AS, &errstr); + if (errstr) + errx(1, "AS number is %s: %s", errstr, word); + + *asnum = uval; + return (1); +} + +int +parse_addr(const char *word, int *family, union eigrpd_addr *addr) +{ + struct in_addr ina; + struct addrinfo hints, *r; + struct sockaddr_in6 *sa_in6; + + if (word == NULL) + return (0); + + memset(addr, 0, sizeof(union eigrpd_addr)); + memset(&ina, 0, sizeof(ina)); + + if (inet_net_pton(AF_INET, word, &ina, sizeof(ina)) != -1) { + *family = AF_INET; + addr->v4.s_addr = ina.s_addr; + return (1); + } + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET6; + hints.ai_socktype = SOCK_DGRAM; /*dummy*/ + hints.ai_flags = AI_NUMERICHOST; + if (getaddrinfo(word, "0", &hints, &r) == 0) { + sa_in6 = (struct sockaddr_in6 *)r->ai_addr; + *family = AF_INET6; + addr->v6 = sa_in6->sin6_addr; + freeaddrinfo(r); + return (1); + } + + return (0); +} + +int +parse_prefix(const char *word, int *family, union eigrpd_addr *addr, + uint8_t *prefixlen) +{ + char *p, *ps; + const char *errstr; + size_t wordlen; + int mask = -1; + + if (word == NULL) + return (0); + wordlen = strlen(word); + + memset(addr, 0, sizeof(union eigrpd_addr)); + + if ((p = strrchr(word, '/')) != NULL) { + size_t plen = strlen(p); + mask = strtonum(p + 1, 0, 128, &errstr); + if (errstr) + errx(1, "netmask %s", errstr); + + if ((ps = malloc(wordlen - plen + 1)) == NULL) + err(1, "parse_prefix: malloc"); + strlcpy(ps, word, wordlen - plen + 1); + + if (parse_addr(ps, family, addr) == 0) { + free(ps); + return (0); + } + + free(ps); + } else + if (parse_addr(word, family, addr) == 0) + return (0); + + switch (*family) { + case AF_INET: + if (mask == UINT8_MAX) + mask = 32; + if (mask > 32) + errx(1, "invalid netmask: too large"); + break; + case AF_INET6: + if (mask == UINT8_MAX) + mask = 128; + if (mask > 128) + errx(1, "invalid netmask: too large"); + break; + default: + return (0); + } + eigrp_applymask(*family, addr, addr, mask); + *prefixlen = mask; + + return (1); +} diff --git a/usr.sbin/eigrpctl/parser.h b/usr.sbin/eigrpctl/parser.h new file mode 100644 index 00000000000..3c44b005a1f --- /dev/null +++ b/usr.sbin/eigrpctl/parser.h @@ -0,0 +1,62 @@ +/* $OpenBSD: parser.h,v 1.1 2015/10/02 04:31:52 renato Exp $ */ + +/* + * Copyright (c) 2015 Renato Westphal <renato@openbsd.org> + * Copyright (c) 2004 Esben Norby <norby@openbsd.org> + * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> + * + * 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. + */ + +#ifndef _PARSER_H_ +#define _PARSER_H_ + +#include <sys/types.h> +#include <net/if.h> +#include <netinet/in.h> + +enum actions { + NONE, + FIB, + FIB_COUPLE, + FIB_DECOUPLE, + LOG_VERBOSE, + LOG_BRIEF, + SHOW, + SHOW_IFACE, + SHOW_IFACE_DTAIL, + SHOW_NBR, + SHOW_TOPOLOGY, + SHOW_FIB, + SHOW_FIB_IFACE, + RELOAD +}; + +struct parse_result { + int family; + uint16_t as; + union eigrpd_addr addr; + uint8_t prefixlen; + char ifname[IF_NAMESIZE]; + int flags; + enum actions action; +}; + +struct parse_result *parse(int, char *[]); +int parse_asnum(const char *, uint16_t *); +int parse_addr(const char *, int *, + union eigrpd_addr *); +int parse_prefix(const char *, int *, + union eigrpd_addr *, uint8_t *); + +#endif /* _PARSER_H_ */ |