/* $OpenBSD: bgpctl.c,v 1.61 2004/08/20 15:49:35 henning Exp $ */ /* * 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 <net/if.h> #include <net/if_media.h> #include <net/if_types.h> #include <err.h> #include <netdb.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include "bgpd.h" #include "session.h" #include "rde.h" #include "log.h" #include "parser.h" enum neighbor_views { NV_DEFAULT, NV_TIMERS }; void usage(void); int main(int, char *[]); void show_summary_head(void); int show_summary_msg(struct imsg *); int show_neighbor_msg(struct imsg *, enum neighbor_views); void print_neighbor_capa_mp_safi(u_int8_t); void print_neighbor_msgstats(struct peer *); void print_neighbor_timers(struct peer *); void print_timer(const char *, time_t, u_int); static char *fmt_timeframe(time_t t); static char *fmt_timeframe_core(time_t t); void show_fib_head(void); void show_network_head(void); int show_fib_msg(struct imsg *); void show_nexthop_head(void); int show_nexthop_msg(struct imsg *); void show_interface_head(void); const char * get_media_descr(int); const char * get_linkstate(int, int); void print_baudrate(u_long); int show_interface_msg(struct imsg *); void show_rib_summary_head(void); void print_prefix(struct bgpd_addr *, u_int8_t, u_int8_t); const char * print_origin(u_int8_t, int); int show_rib_summary_msg(struct imsg *); struct imsgbuf ibuf; void usage(void) { extern char *__progname; fprintf(stderr, "usage: %s <command> [arg [...]]\n", __progname); exit(1); } int main(int argc, char *argv[]) { struct sockaddr_un sun; int fd, n, done; struct imsg imsg; struct network_config net; struct parse_result *res; if ((res = parse(argc, argv)) == NULL) exit(1); if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) err(1, "control_init: socket"); bzero(&sun, sizeof(sun)); sun.sun_family = AF_UNIX; strlcpy(sun.sun_path, SOCKET_NAME, sizeof(sun.sun_path)); if (connect(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) err(1, "connect: %s", SOCKET_NAME); imsg_init(&ibuf, fd); done = 0; switch (res->action) { case NONE: usage(); /* not reached */ case SHOW: case SHOW_SUMMARY: imsg_compose(&ibuf, IMSG_CTL_SHOW_NEIGHBOR, 0, NULL, 0); show_summary_head(); break; case SHOW_FIB: if (!res->addr.af) imsg_compose(&ibuf, IMSG_CTL_KROUTE, 0, &res->flags, sizeof(res->flags)); else imsg_compose(&ibuf, IMSG_CTL_KROUTE_ADDR, 0, &res->addr, sizeof(res->addr)); show_fib_head(); break; case SHOW_NEXTHOP: imsg_compose(&ibuf, IMSG_CTL_SHOW_NEXTHOP, 0, NULL, 0); show_nexthop_head(); break; case SHOW_INTERFACE: imsg_compose(&ibuf, IMSG_CTL_SHOW_INTERFACE, 0, NULL, 0); show_interface_head(); break; case SHOW_NEIGHBOR: case SHOW_NEIGHBOR_TIMERS: if (res->addr.af) imsg_compose(&ibuf, IMSG_CTL_SHOW_NEIGHBOR, 0, &res->addr, sizeof(res->addr)); else imsg_compose(&ibuf, IMSG_CTL_SHOW_NEIGHBOR, 0, NULL, 0); break; case SHOW_RIB: if (res->as.type != AS_NONE) imsg_compose(&ibuf, IMSG_CTL_SHOW_RIB_AS, 0, &res->as, sizeof(res->as)); else if (res->addr.af) { struct ctl_show_rib_prefix msg; bzero(&msg, sizeof(msg)); memcpy(&msg.prefix, &res->addr, sizeof(res->addr)); msg.prefixlen = res->prefixlen; msg.flags = res->flags; imsg_compose(&ibuf, IMSG_CTL_SHOW_RIB_PREFIX, 0, &msg, sizeof(msg)); } else imsg_compose(&ibuf, IMSG_CTL_SHOW_RIB, 0, NULL, 0); show_rib_summary_head(); break; case RELOAD: imsg_compose(&ibuf, IMSG_CTL_RELOAD, 0, NULL, 0); printf("reload request sent.\n"); done = 1; break; case FIB: errx(1, "action==FIB"); break; case FIB_COUPLE: imsg_compose(&ibuf, IMSG_CTL_FIB_COUPLE, 0, NULL, 0); printf("couple request sent.\n"); done = 1; break; case FIB_DECOUPLE: imsg_compose(&ibuf, IMSG_CTL_FIB_DECOUPLE, 0, NULL, 0); printf("decouple request sent.\n"); done = 1; break; case NEIGHBOR: errx(1, "action==NEIGHBOR"); break; case NEIGHBOR_UP: imsg_compose(&ibuf, IMSG_CTL_NEIGHBOR_UP, 0, &res->addr, sizeof(res->addr)); printf("request sent.\n"); done = 1; break; case NEIGHBOR_DOWN: imsg_compose(&ibuf, IMSG_CTL_NEIGHBOR_DOWN, 0, &res->addr, sizeof(res->addr)); printf("request sent.\n"); done = 1; break; case NEIGHBOR_CLEAR: imsg_compose(&ibuf, IMSG_CTL_NEIGHBOR_CLEAR, 0, &res->addr, sizeof(res->addr)); printf("request sent.\n"); done = 1; break; case NETWORK_ADD: case NETWORK_REMOVE: bzero(&net, sizeof(net)); memcpy(&net.prefix, &res->addr, sizeof(res->addr)); net.prefixlen = res->prefixlen; /* attribute sets are not supported */ if (res->action == NETWORK_ADD) imsg_compose(&ibuf, IMSG_NETWORK_ADD, 0, &net, sizeof(net)); else imsg_compose(&ibuf, IMSG_NETWORK_REMOVE, 0, &net, sizeof(net)); printf("request sent.\n"); done = 1; break; case NETWORK_FLUSH: imsg_compose(&ibuf, IMSG_NETWORK_FLUSH, 0, NULL, 0); printf("request sent.\n"); done = 1; break; case NETWORK_SHOW: imsg_compose(&ibuf, IMSG_CTL_SHOW_NETWORK, 0, NULL, 0); show_network_head(); break; } while (ibuf.w.queued) if (msgbuf_write(&ibuf.w) < 0) 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_SUMMARY: done = show_summary_msg(&imsg); break; case SHOW_FIB: done = show_fib_msg(&imsg); break; case SHOW_NEXTHOP: done = show_nexthop_msg(&imsg); break; case SHOW_INTERFACE: done = show_interface_msg(&imsg); break; case SHOW_NEIGHBOR: done = show_neighbor_msg(&imsg, NV_DEFAULT); break; case SHOW_NEIGHBOR_TIMERS: done = show_neighbor_msg(&imsg, NV_TIMERS); break; case SHOW_RIB: done = show_rib_summary_msg(&imsg); break; case NETWORK_SHOW: done = show_fib_msg(&imsg); break; case NONE: case RELOAD: case FIB: case FIB_COUPLE: case FIB_DECOUPLE: case NEIGHBOR: case NEIGHBOR_UP: case NEIGHBOR_DOWN: case NEIGHBOR_CLEAR: case NETWORK_ADD: case NETWORK_REMOVE: case NETWORK_FLUSH: break; } imsg_free(&imsg); } } close(fd); exit(0); } void show_summary_head(void) { printf("%-20s %-5s %-10s %-10s %-5s %-8s %s\n", "Neighbor", "AS", "MsgRcvd", "MsgSent", "OutQ", "Up/Down", "State/PrefixRcvd"); } int show_summary_msg(struct imsg *imsg) { struct peer *p; char *s; switch (imsg->hdr.type) { case IMSG_CTL_SHOW_NEIGHBOR: p = imsg->data; if ((p->conf.remote_addr.af == AF_INET && p->conf.remote_masklen != 32) || (p->conf.remote_addr.af == AF_INET6 && p->conf.remote_masklen != 128)) { if (asprintf(&s, "%s/%u", log_addr(&p->conf.remote_addr), p->conf.remote_masklen) == -1) err(1, NULL); } else if ((s = strdup(log_addr(&p->conf.remote_addr))) == NULL) err(1, NULL); printf("%-20s %5u %10llu %10llu %5u %-8s ", s, p->conf.remote_as, p->stats.msg_rcvd_open + p->stats.msg_rcvd_notification + p->stats.msg_rcvd_update + p->stats.msg_rcvd_keepalive + p->stats.msg_rcvd_rrefresh, p->stats.msg_sent_open + p->stats.msg_sent_notification + p->stats.msg_sent_update + p->stats.msg_sent_keepalive + p->stats.msg_sent_rrefresh, p->wbuf.queued, fmt_timeframe(p->stats.last_updown)); if (p->state == STATE_ESTABLISHED) { printf("%u", p->stats.prefix_cnt); if (p->conf.max_prefix != 0) printf("/%u", p->conf.max_prefix); } else printf("%s", statenames[p->state]); printf("\n"); free(s); break; case IMSG_CTL_END: return (1); default: break; } return (0); } int show_neighbor_msg(struct imsg *imsg, enum neighbor_views nv) { struct peer *p; struct in_addr ina; char buf[NI_MAXHOST], pbuf[NI_MAXSERV], *s; switch (imsg->hdr.type) { case IMSG_CTL_SHOW_NEIGHBOR: p = imsg->data; if ((p->conf.remote_addr.af == AF_INET && p->conf.remote_masklen != 32) || (p->conf.remote_addr.af == AF_INET6 && p->conf.remote_masklen != 128)) { if (asprintf(&s, "%s/%u", log_addr(&p->conf.remote_addr), p->conf.remote_masklen) == -1) err(1, NULL); } else s = strdup(log_addr(&p->conf.remote_addr)); ina.s_addr = p->remote_bgpid; printf("BGP neighbor is %s, ", s); free(s); if (p->conf.remote_as == 0 && p->conf.template) printf("remote AS: accept any"); else printf("remote AS %u", p->conf.remote_as); if (p->conf.template) printf(", Template"); if (p->conf.cloned) printf(", Cloned"); printf("\n"); if (p->conf.descr[0]) printf(" Description: %s\n", p->conf.descr); printf(" BGP version 4, remote router-id %s\n", inet_ntoa(ina)); printf(" BGP state = %s", statenames[p->state]); if (p->stats.last_updown != 0) printf(", %s for %s", p->state == STATE_ESTABLISHED ? "up" : "down", fmt_timeframe(p->stats.last_updown)); printf("\n"); printf(" Last read %s, holdtime %us, keepalive interval %us\n", fmt_timeframe(p->stats.last_read), p->holdtime, p->holdtime/3); if (p->capa.mp_v4 || p->capa.mp_v6 || p->capa.refresh) { printf(" Neighbor capabilities:\n"); if (p->capa.mp_v4) { printf(" Multiprotocol extensions: IPv4"); print_neighbor_capa_mp_safi(p->capa.mp_v4); } if (p->capa.mp_v6) { printf(" Multiprotocol extensions: IPv6"); print_neighbor_capa_mp_safi(p->capa.mp_v6); } if (p->capa.refresh) printf(" Route Refresh\n"); } printf("\n"); switch (nv) { case NV_DEFAULT: print_neighbor_msgstats(p); break; case NV_TIMERS: print_neighbor_timers(p); break; } printf("\n"); if (getnameinfo((struct sockaddr *)&p->sa_local, (socklen_t)p->sa_local.ss_len, buf, sizeof(buf), pbuf, sizeof(pbuf), NI_NUMERICHOST | NI_NUMERICSERV)) { strlcpy(buf, "(unknown)", sizeof(buf)); strlcpy(pbuf, "", sizeof(pbuf)); } printf(" Local host: %20s, Local port: %5s\n", buf, pbuf); if (getnameinfo((struct sockaddr *)&p->sa_remote, (socklen_t)p->sa_remote.ss_len, buf, sizeof(buf), pbuf, sizeof(pbuf), NI_NUMERICHOST | NI_NUMERICSERV)) { strlcpy(buf, "(unknown)", sizeof(buf)); strlcpy(pbuf, "", sizeof(pbuf)); } printf(" Remote host: %20s, Remote port: %5s\n", buf, pbuf); printf("\n"); break; case IMSG_CTL_END: return (1); break; default: break; } return (0); } void print_neighbor_capa_mp_safi(u_int8_t safi) { switch (safi) { case SAFI_UNICAST: printf(" Unicast"); break; case SAFI_MULTICAST: printf(" Multicast"); break; case SAFI_BOTH: printf(" Unicast and Multicast"); break; default: printf(" unknown (%u)", safi); break; } printf("\n"); } void print_neighbor_msgstats(struct peer *p) { printf(" Message statistics:\n"); printf(" %-15s %-10s %-10s\n", "", "Sent", "Received"); printf(" %-15s %10llu %10llu\n", "Opens", p->stats.msg_sent_open, p->stats.msg_rcvd_open); printf(" %-15s %10llu %10llu\n", "Notifications", p->stats.msg_sent_notification, p->stats.msg_rcvd_notification); printf(" %-15s %10llu %10llu\n", "Updates", p->stats.msg_sent_update, p->stats.msg_rcvd_update); printf(" %-15s %10llu %10llu\n", "Keepalives", p->stats.msg_sent_keepalive, p->stats.msg_rcvd_keepalive); printf(" %-15s %10llu %10llu\n", "Route Refresh", p->stats.msg_sent_rrefresh, p->stats.msg_rcvd_rrefresh); printf(" %-15s %10llu %10llu\n", "Total", p->stats.msg_sent_open + p->stats.msg_sent_notification + p->stats.msg_sent_update + p->stats.msg_sent_keepalive + p->stats.msg_sent_rrefresh, p->stats.msg_rcvd_open + p->stats.msg_rcvd_notification + p->stats.msg_rcvd_update + p->stats.msg_rcvd_keepalive + p->stats.msg_rcvd_rrefresh); } void print_neighbor_timers(struct peer *p) { print_timer("IdleHoldTimer:", p->IdleHoldTimer, p->IdleHoldTime); print_timer("ConnectRetryTimer:", p->ConnectRetryTimer, INTERVAL_CONNECTRETRY); print_timer("HoldTimer:", p->HoldTimer, (u_int)p->holdtime); print_timer("KeepaliveTimer:", p->KeepaliveTimer, (u_int)p->holdtime/3); } void print_timer(const char *name, time_t val, u_int interval) { int d; d = val - time(NULL); printf(" %-20s ", name); if (val == 0) printf("%-20s", "not running"); else if (d <= 0) printf("%-20s", "due"); else printf("due in %-13s", fmt_timeframe_core(d)); printf("Interval: %5us\n", interval); } #define TF_BUFS 8 #define TF_LEN 9 static char * fmt_timeframe(time_t t) { if (t == 0) return ("Never"); else return (fmt_timeframe_core(time(NULL) - t)); } static char * fmt_timeframe_core(time_t t) { char *buf; static char tfbuf[TF_BUFS][TF_LEN]; /* ring buffer */ static int idx = 0; unsigned sec, min, hrs, day, week; 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, "%02uw%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); } void show_fib_head(void) { printf("flags: * = valid, B = BGP, C = Connected, S = Static\n"); printf(" N = BGP Nexthop reachable via this route\n\n"); printf("flags destination gateway\n"); } void show_network_head(void) { printf("flags: S = Static\n"); printf("flags destination\n"); } int show_fib_msg(struct imsg *imsg) { struct kroute *k; char *p; switch (imsg->hdr.type) { case IMSG_CTL_KROUTE: case IMSG_CTL_SHOW_NETWORK: if (imsg->hdr.len < IMSG_HEADER_SIZE + sizeof(struct kroute)) errx(1, "wrong imsg len"); k = imsg->data; if (k->flags & F_DOWN) printf(" "); else printf("*"); if (k->flags & F_BGPD_INSERTED) printf("B"); else if (k->flags & F_CONNECTED) printf("C"); else if (k->flags & F_KERNEL) printf("S"); else printf(" "); if (k->flags & F_NEXTHOP) printf("N"); else printf(" "); printf(" "); if (asprintf(&p, "%s/%u", inet_ntoa(k->prefix), k->prefixlen) == -1) err(1, NULL); printf("%-20s ", p); free(p); if (k->nexthop.s_addr) printf("%s", inet_ntoa(k->nexthop)); else if (k->flags & F_CONNECTED) printf("link#%u", k->ifindex); printf("\n"); break; case IMSG_CTL_END: return (1); break; default: break; } return (0); } void show_nexthop_head(void) { printf("%-20s %s\n", "Nexthop", "State"); } int show_nexthop_msg(struct imsg *imsg) { struct ctl_show_nexthop *p; switch (imsg->hdr.type) { case IMSG_CTL_SHOW_NEXTHOP: p = imsg->data; printf("%-20s %s\n", inet_ntoa(p->addr.v4), p->valid ? "valid" : "invalid"); break; case IMSG_CTL_END: return (1); break; default: break; } return (0); } void show_interface_head(void) { printf("%-15s%-15s%-15s%s\n", "Interface", "Nexthop state", "Flags", "Link state"); } const int ifm_status_valid_list[] = IFM_STATUS_VALID_LIST; const struct ifmedia_status_description ifm_status_descriptions[] = IFM_STATUS_DESCRIPTIONS; const struct ifmedia_description ifm_type_descriptions[] = IFM_TYPE_DESCRIPTIONS; const char * get_media_descr(int 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 media"); } const char * get_linkstate(int media_type, int link_state) { const struct ifmedia_status_description *p; int i; if (link_state == LINK_STATE_UNKNOWN) return ("unknown"); for (i = 0; ifm_status_valid_list[i] != 0; i++) for (p = ifm_status_descriptions; p->ifms_valid != 0; p++) { if (p->ifms_type != media_type || p->ifms_valid != ifm_status_valid_list[i]) continue; return (p->ifms_string[link_state == LINK_STATE_UP]); } return ("unknown link state"); } void print_baudrate(u_long baudrate) { if (baudrate > IF_Gbps(1)) printf("%lu GBit/s", baudrate / IF_Gbps(1)); else if (baudrate > IF_Mbps(1)) printf("%lu MBit/s", baudrate / IF_Mbps(1)); else if (baudrate > IF_Kbps(1)) printf("%lu KBit/s", baudrate / IF_Kbps(1)); else printf("%lu Bit/s", baudrate); } int show_interface_msg(struct imsg *imsg) { struct kif *k; int ifms_type; switch (imsg->hdr.type) { case IMSG_CTL_SHOW_INTERFACE: k = imsg->data; printf("%-15s", k->ifname); printf("%-15s", k->nh_reachable ? "ok" : "invalid"); printf("%-15s", k->flags & IFF_UP ? "UP" : ""); switch (k->media_type) { case IFT_ETHER: ifms_type = IFM_ETHER; break; case IFT_FDDI: ifms_type = IFM_FDDI; break; case IFT_ISO88025: ifms_type = IFM_TOKEN; break; default: ifms_type = 0; break; } if (ifms_type) printf("%s, %s", get_media_descr(ifms_type), get_linkstate(ifms_type, k->link_state)); else if (k->link_state == LINK_STATE_UNKNOWN) printf("unknown"); else printf("link state %u", 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: return (1); break; default: break; } return (0); } void show_rib_summary_head(void) { printf( "flags: * = Valid, > = Selected, I = via IBGP, A = Announced\n"); printf("origin: i = IGP, e = EGP, ? = Incomplete\n\n"); printf("%-4s %-20s%-15s %5s %5s %s\n", "flags", "destination", "gateway", "lpref", "med", "aspath origin"); } void print_prefix(struct bgpd_addr *prefix, u_int8_t prefixlen, u_int8_t flags) { char flagstr[5]; char *p; p = flagstr; if (flags & F_RIB_ANNOUNCE) *p++ = 'A'; if (flags & F_RIB_INTERNAL) *p++ = 'I'; if (flags & F_RIB_ELIGIBLE) *p++ = '*'; if (flags & F_RIB_ACTIVE) *p++ = '>'; *p = '\0'; if (asprintf(&p, "%s/%u", inet_ntoa(prefix->v4), prefixlen) == -1) err(1, NULL); printf("%-4s %-20s", flagstr, p); free(p); } const char * print_origin(u_int8_t origin, int sum) { switch (origin) { case ORIGIN_IGP: return (sum ? "i" : "IGP"); case ORIGIN_EGP: return (sum ? "e" : "EGP"); case ORIGIN_INCOMPLETE: return (sum ? "?" : "incomplete"); default: return (sum ? "X" : "bad origin"); } } char *aspath = NULL; struct ctl_show_rib *rib = NULL; int show_rib_summary_msg(struct imsg *imsg) { struct ctl_show_rib_prefix *p; u_char *asdata; switch (imsg->hdr.type) { case IMSG_CTL_SHOW_RIB: if (rib != NULL) { free(rib); rib = NULL; } if (aspath != NULL) { free(aspath); aspath = NULL; } if ((rib = malloc(imsg->hdr.len - IMSG_HEADER_SIZE)) == NULL) err(1, NULL); memcpy(rib, imsg->data, imsg->hdr.len - IMSG_HEADER_SIZE); print_prefix(&rib->prefix, rib->prefixlen, rib->flags); printf("%-15s ", inet_ntoa(rib->nexthop.v4)); printf(" %5u %5u ", rib->local_pref, rib->med); asdata = imsg->data; asdata += sizeof(struct ctl_show_rib); if (aspath_asprint(&aspath, asdata, rib->aspath_len) == -1) err(1, NULL); if (strlen(aspath) > 0) printf("%s ", aspath); printf("%s\n", print_origin(rib->origin, 1)); break; case IMSG_CTL_SHOW_RIB_PREFIX: p = imsg->data; if (rib == NULL) /* unexpected packet */ return (0); print_prefix(&p->prefix, p->prefixlen, p->flags); printf("%-15s ", inet_ntoa(rib->nexthop.v4)); printf(" %5u %5u ", rib->local_pref, rib->med); if (strlen(aspath) > 0) printf("%s ", aspath); printf("%s\n", print_origin(rib->origin, 1)); break; case IMSG_CTL_END: if (rib != NULL) { free(rib); rib = NULL; } if (aspath != NULL) { free(aspath); aspath = NULL; } return (1); default: if (rib != NULL) { free(rib); rib = NULL; } if (aspath != NULL) { free(aspath); aspath = NULL; } break; } return (0); }