src/usr.sbin/ospfctl/ospfctl.c

577 lines
13 KiB
C

/* $OpenBSD: ospfctl.c,v 1.68 2020/05/20 11:11:24 denis Exp $ */
/*
* 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 "ospf.h"
#include "ospfd.h"
#include "ospfctl.h"
#include "ospfe.h"
#include "parser.h"
__dead void usage(void);
int show(struct imsg *, struct parse_result *);
struct imsgbuf *ibuf;
const struct output *output = &show_output;
__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, r;
int done = 0;
int n, verbose = 0;
int ch;
char *sockname;
r = getrtable();
if (asprintf(&sockname, "%s.%d", OSPFD_SOCKET, r) == -1)
err(1, "asprintf");
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 ospfd control socket */
if ((ctl_sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
err(1, "socket");
bzero(&sun, 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_SUM:
imsg_compose(ibuf, IMSG_CTL_SHOW_SUM, 0, 0, -1, NULL, 0);
break;
case SHOW_IFACE:
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:
case SHOW_NBR_DTAIL:
imsg_compose(ibuf, IMSG_CTL_SHOW_NBR, 0, 0, -1, NULL, 0);
break;
case SHOW_DB:
imsg_compose(ibuf, IMSG_CTL_SHOW_DATABASE, 0, 0, -1, NULL, 0);
break;
case SHOW_DBBYAREA:
imsg_compose(ibuf, IMSG_CTL_SHOW_DATABASE, 0, 0, -1,
&res->addr, sizeof(res->addr));
break;
case SHOW_DBEXT:
imsg_compose(ibuf, IMSG_CTL_SHOW_DB_EXT, 0, 0, -1, NULL, 0);
break;
case SHOW_DBNET:
imsg_compose(ibuf, IMSG_CTL_SHOW_DB_NET, 0, 0, -1, NULL, 0);
break;
case SHOW_DBRTR:
imsg_compose(ibuf, IMSG_CTL_SHOW_DB_RTR, 0, 0, -1, NULL, 0);
break;
case SHOW_DBSELF:
imsg_compose(ibuf, IMSG_CTL_SHOW_DB_SELF, 0, 0, -1, NULL, 0);
break;
case SHOW_DBSUM:
imsg_compose(ibuf, IMSG_CTL_SHOW_DB_SUM, 0, 0, -1, NULL, 0);
break;
case SHOW_DBASBR:
imsg_compose(ibuf, IMSG_CTL_SHOW_DB_ASBR, 0, 0, -1, NULL, 0);
break;
case SHOW_DBOPAQ:
imsg_compose(ibuf, IMSG_CTL_SHOW_DB_OPAQ, 0, 0, -1, NULL, 0);
break;
case SHOW_RIB:
case SHOW_RIB_DTAIL:
imsg_compose(ibuf, IMSG_CTL_SHOW_RIB, 0, 0, -1, NULL, 0);
break;
case SHOW_FIB:
if (!res->addr.s_addr)
imsg_compose(ibuf, IMSG_CTL_KROUTE, 0, 0, -1,
&res->flags, sizeof(res->flags));
else
imsg_compose(ibuf, IMSG_CTL_KROUTE_ADDR, 0, 0, -1,
&res->addr, sizeof(res->addr));
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);
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 FIB_RELOAD:
imsg_compose(ibuf, IMSG_CTL_FIB_RELOAD, 0, 0, -1, NULL, 0);
printf("reload 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");
/* no output for certain commands such as log verbose */
if (!done) {
output->head(res);
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;
done = show(&imsg, res);
imsg_free(&imsg);
}
}
output->tail();
}
close(ctl_sock);
free(ibuf);
return (0);
}
int
show(struct imsg *imsg, struct parse_result *res)
{
struct ctl_sum *sum;
struct ctl_sum_area *sumarea;
struct ctl_iface *ctliface;
struct ctl_nbr *nbr;
struct ctl_rt *rt;
struct kroute *k;
struct kif *kif;
static struct in_addr area_id;
struct area *area;
static u_int8_t lasttype;
static char ifname[IF_NAMESIZE];
struct iface *iface;
struct lsa *lsa;
struct lsa_hdr *lsa_hdr;
switch (imsg->hdr.type) {
case IMSG_CTL_SHOW_SUM:
sum = imsg->data;
output->summary(sum);
break;
case IMSG_CTL_SHOW_SUM_AREA:
sumarea = imsg->data;
output->summary_area(sumarea);
break;
case IMSG_CTL_SHOW_INTERFACE:
ctliface = imsg->data;
if(res->action == SHOW_IFACE_DTAIL)
output->interface(ctliface, 1);
else
output->interface(ctliface, 0);
break;
case IMSG_CTL_SHOW_NBR:
nbr = imsg->data;
if(res->action == SHOW_NBR_DTAIL)
output->neighbor(nbr, 1);
else
output->neighbor(nbr, 0);
break;
case IMSG_CTL_SHOW_RIB:
rt = imsg->data;
if(res->action == SHOW_RIB_DTAIL)
output->rib(rt, 1);
else
output->rib(rt, 0);
break;
case IMSG_CTL_KROUTE:
if (imsg->hdr.len < IMSG_HEADER_SIZE + sizeof(struct kroute))
errx(1, "wrong imsg len");
k = imsg->data;
output->fib(k);
break;
case IMSG_CTL_IFINFO:
kif = imsg->data;
output->fib_interface(kif);
break;
case IMSG_CTL_SHOW_DB_EXT:
case IMSG_CTL_SHOW_DB_NET:
case IMSG_CTL_SHOW_DB_RTR:
case IMSG_CTL_SHOW_DB_SUM:
case IMSG_CTL_SHOW_DB_ASBR:
case IMSG_CTL_SHOW_DB_OPAQ:
lsa = imsg->data;
output->db(lsa, area_id, lasttype, ifname);
lasttype = lsa->hdr.type;
break;
case IMSG_CTL_SHOW_DATABASE:
case IMSG_CTL_SHOW_DB_SELF:
lsa_hdr = imsg->data;
output->db_simple(lsa_hdr, area_id, lasttype, ifname);
lasttype = lsa_hdr->type;
break;
case IMSG_CTL_AREA:
area = imsg->data;
area_id = area->id;
lasttype = 0;
break;
case IMSG_CTL_IFACE:
iface = imsg->data;
strlcpy(ifname, iface->name, sizeof(ifname));
lasttype = 0;
break;
case IMSG_CTL_END:
return (1);
default:
warnx("unknown imsg %d received", imsg->hdr.type);
break;
}
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);
}
}
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);
}
const char *
log_id(u_int32_t id)
{
static char buf[48];
struct in_addr addr;
addr.s_addr = id;
if (inet_ntop(AF_INET, &addr, buf, sizeof(buf)) == NULL)
return ("?");
else
return (buf);
}
const char *
log_adv_rtr(u_int32_t adv_rtr)
{
static char buf[48];
struct in_addr addr;
addr.s_addr = adv_rtr;
if (inet_ntop(AF_INET, &addr, buf, sizeof(buf)) == NULL)
return ("?");
else
return (buf);
}
/* prototype defined in ospfd.h and shared with the kroute.c version */
u_int8_t
mask2prefixlen(in_addr_t ina)
{
if (ina == 0)
return (0);
else
return (33 - ffs(ntohl(ina)));
}
char *
print_ls_type(u_int8_t type)
{
switch (type) {
case LSA_TYPE_ROUTER:
return ("Router");
case LSA_TYPE_NETWORK:
return ("Network");
case LSA_TYPE_SUM_NETWORK:
return ("Summary (Network)");
case LSA_TYPE_SUM_ROUTER:
return ("Summary (Router)");
case LSA_TYPE_EXTERNAL:
return ("AS External");
case LSA_TYPE_LINK_OPAQ:
return ("Type-9 Opaque");
case LSA_TYPE_AREA_OPAQ:
return ("Type-10 Opaque");
case LSA_TYPE_AS_OPAQ:
return ("Type-11 Opaque");
default:
return ("Unknown");
}
}
char *
print_rtr_link_type(u_int8_t type)
{
switch (type) {
case LINK_TYPE_POINTTOPOINT:
return ("Point-to-Point");
case LINK_TYPE_TRANSIT_NET:
return ("Transit Network");
case LINK_TYPE_STUB_NET:
return ("Stub Network");
case LINK_TYPE_VIRTUAL:
return ("Virtual Link");
default:
return ("Unknown");
}
}
const char *
print_ospf_flags(u_int8_t opts)
{
static char optbuf[32];
snprintf(optbuf, sizeof(optbuf), "*|*|*|*|*|%s|%s|%s",
opts & OSPF_RTR_V ? "V" : "-",
opts & OSPF_RTR_E ? "E" : "-",
opts & OSPF_RTR_B ? "B" : "-");
return (optbuf);
}
const char *
print_ospf_options(u_int8_t opts)
{
static char optbuf[32];
snprintf(optbuf, sizeof(optbuf), "%s|%s|%s|%s|%s|%s|%s|%s",
opts & OSPF_OPTION_DN ? "DN" : "-",
opts & OSPF_OPTION_O ? "O" : "-",
opts & OSPF_OPTION_DC ? "DC" : "-",
opts & OSPF_OPTION_EA ? "EA" : "-",
opts & OSPF_OPTION_NP ? "N/P" : "-",
opts & OSPF_OPTION_MC ? "MC" : "-",
opts & OSPF_OPTION_E ? "E" : "-",
opts & OSPF_OPTION_MT ? "MT" : "-");
return (optbuf);
}
const char *
print_ospf_rtr_flags(u_int8_t opts)
{
static char optbuf[32];
snprintf(optbuf, sizeof(optbuf), "%s%s%s",
opts & OSPF_RTR_E ? "AS" : "",
opts & OSPF_RTR_E && opts & OSPF_RTR_B ? "+" : "",
opts & OSPF_RTR_B ? "ABR" : "");
return (optbuf);
}
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);
}
const char *
print_baudrate(u_int64_t baudrate)
{
static char buf[32];
if (baudrate > IF_Gbps(1))
snprintf(buf, sizeof(buf), "%llu GBit/s", baudrate / IF_Gbps(1));
else if (baudrate > IF_Mbps(1))
snprintf(buf, sizeof(buf), "%llu MBit/s", baudrate / IF_Mbps(1));
else if (baudrate > IF_Kbps(1))
snprintf(buf, sizeof(buf), "%llu KBit/s", baudrate / IF_Kbps(1));
else
snprintf(buf, sizeof(buf), "%llu Bit/s", baudrate);
return (buf);
}