diff options
Diffstat (limited to 'contrib/traceroute/findsaddr-mib.c')
-rw-r--r-- | contrib/traceroute/findsaddr-mib.c | 378 |
1 files changed, 378 insertions, 0 deletions
diff --git a/contrib/traceroute/findsaddr-mib.c b/contrib/traceroute/findsaddr-mib.c new file mode 100644 index 000000000000..e5fc1d7186db --- /dev/null +++ b/contrib/traceroute/findsaddr-mib.c @@ -0,0 +1,378 @@ +/* + * Copyright (c) 2000 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the Computer Systems + * Engineering Group at Lawrence Berkeley Laboratory. + * 4. Neither the name of the University nor of the Laboratory may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* Seriously complex Solaris mib2 code */ + +#ifndef lint +static const char rcsid[] = + "@(#) $Id: findsaddr-mib.c,v 1.2 2000/12/13 21:31:49 leres Exp $ (LBL)"; +#endif + +#include <sys/param.h> +#include <sys/file.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#ifdef HAVE_SYS_SOCKIO_H +#include <sys/sockio.h> +#endif +#include <sys/time.h> /* concession to AIX */ +#include <sys/stream.h> +#include <sys/tihdr.h> +#include <sys/tiuser.h> + +#if __STDC__ +struct mbuf; +struct rtentry; +#endif + +#include <net/if.h> +#include <net/route.h> + +#include <netinet/in.h> + +#include <inet/common.h> +#include <inet/mib2.h> +#include <inet/ip.h> +#include <inet/arp.h> + +#include <arpa/inet.h> + +#include <errno.h> +#include <fcntl.h> +#ifdef HAVE_MALLOC_H +#include <malloc.h> +#endif +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stropts.h> +#include <unistd.h> + +#include "gnuc.h" +#ifdef HAVE_OS_PROTO_H +#include "os-proto.h" +#endif + +#include "findsaddr.h" + +/* Compatibility with older versions of Solaris */ +#ifndef IRE_CACHE +#define IRE_CACHE IRE_ROUTE +#endif + +#ifndef T_CURRENT +#define T_CURRENT MI_T_CURRENT +#endif + +struct routelist { + struct routelist *next; + u_int32_t dest; + u_int32_t mask; + u_int32_t gate; + char ifname[64]; +}; + +/* Forwards */ +static struct routelist *getroutelist(char *); +static void freeroutelist(struct routelist *); + +/* + * Return the source address for the given destination address + * + * Since solaris doesn't report the interface associated with every + * route, we have to make two passes over the routing table. The + * first pass should yield a host, net, default or interface route. + * If we find an interface route we're done. If not, we need to + * make a second pass to find the interface route for the gateway + * in the host, net, default route we found in the first pass. + * + * So instead of making a single pass through the tables as they + * are retrieved from the kernel, we must build a linked list... + */ +const char * +findsaddr(register const struct sockaddr_in *to, + register struct sockaddr_in *from) +{ + register struct routelist *rl, *rl2, *routelist; + static char errbuf[512]; + u_int32_t mask, gate; + + /* Get the routing table */ + routelist = getroutelist(errbuf); + if (routelist == NULL) + return (errbuf); + + /* First pass; look for a route that matches */ + mask = 0; + rl2 = NULL; + for (rl = routelist; rl != NULL; rl = rl->next) { + if ((to->sin_addr.s_addr & rl->mask) == rl->dest && + (rl->mask > mask || mask == 0) && + rl->gate != 0) { + mask = rl->mask; + rl2 = rl; + } + } + if (rl2 == NULL) { + freeroutelist(routelist); + sprintf(errbuf, "%s: %.128s", + inet_ntoa(to->sin_addr), strerror(EHOSTUNREACH)); + return (errbuf); + } + + /* We're done if we got one with an interface */ + if (rl2->ifname[0] != '\0') { + freeroutelist(routelist); + from->sin_addr.s_addr = rl2->gate; + return (NULL); + } + + /* First pass; look for a route that matches the gateway we found */ + mask = 0; + gate = rl2->gate; + rl2 = NULL; + for (rl = routelist; rl != NULL; rl = rl->next) { + if ((gate & rl->mask) == rl->dest && + (rl->mask > mask || mask == 0) && + rl->gate != 0 && + rl->ifname[0] != '\0') { + mask = rl->mask; + rl2 = rl; + } + } + if (rl2 == NULL) { + freeroutelist(routelist); + sprintf(errbuf, "%s: %.128s (second pass)", + inet_ntoa(to->sin_addr), strerror(EHOSTUNREACH)); + return (errbuf); + } + + from->sin_addr.s_addr = rl2->gate; + freeroutelist(routelist); + return (NULL); +} + +/* Request mib */ +struct mibrq { + struct T_optmgmt_req req; + struct opthdr hdr; +}; + +/* Reply mib */ +struct mibrep { + struct T_optmgmt_ack ack; + struct opthdr hdr; + char buf[512]; +}; + +static struct mibrq mibrq = { + { T_OPTMGMT_REQ, sizeof(mibrq.hdr), sizeof(mibrq.req), T_CURRENT }, + { MIB2_IP } +}; + +static struct mibrep mibrep = { + { 0, 0, 0 }, + { 0 }, + { 0 } +}; + +static struct strbuf rqbuf = { + 0, sizeof(mibrq), (char *)&mibrq +}; + +static struct strbuf repbuf = { + sizeof(mibrep.buf), sizeof(mibrep.ack) + sizeof(mibrep.hdr), + (char *)&mibrep +}; + +static const char devip[] = "/dev/ip"; + +/* + * Construct the list of routes + */ +static struct routelist * +getroutelist(char *errbuf) +{ + register int s, stat, i; + register char *cp; + register struct T_optmgmt_ack *ackp; + register struct T_error_ack *eackp; + register struct opthdr *hp; + register mib2_ipRouteEntry_t *rp, *rp2; + register struct routelist *rl, *rl2, *routelist; + int flags; + struct strbuf repbuf2; + + s = open(devip, O_RDWR, 0); + if (s < 0) { + sprintf(errbuf, "open %s: %.128s", devip, strerror(errno)); + return (NULL); + } + + if ((cp = "arp", ioctl(s, I_PUSH, cp) < 0) || + (cp = "tcp", ioctl(s, I_PUSH, cp) < 0) || + (cp = "udp", ioctl(s, I_PUSH, cp) < 0)) { + sprintf(errbuf, "I_PUSH %s: %.128s", cp, strerror(errno)); + close(s); + return (NULL); + } + + flags = 0; + if (putmsg(s, &rqbuf, NULL, flags) < 0) { + sprintf(errbuf, "putmsg: %.128s", strerror(errno)); + close(s); + return (NULL); + } + + routelist= NULL; + rl2 = NULL; + + rp = NULL; + + ackp = &mibrep.ack; + hp = &mibrep.hdr; + eackp = (struct T_error_ack *)ackp; + for (;;) { + flags = 0; + memset(repbuf.buf, 0, repbuf.len); + stat = getmsg(s, &repbuf, NULL, &flags); + if (stat < 0) { + sprintf(errbuf, "getmsg: %.128s", strerror(errno)); + goto bail; + } + if (stat == 0 && repbuf.len >= sizeof(*ackp) && + ackp->PRIM_type == T_OPTMGMT_ACK && + ackp->MGMT_flags == T_SUCCESS && + hp->len == 0) { + /* All done! */ + goto done; + } + if (repbuf.len >= sizeof(*eackp) && + eackp->PRIM_type == T_ERROR_ACK) { + sprintf(errbuf, "getmsg err: %.128s", + strerror((eackp->TLI_error == TSYSERR) ? + eackp->UNIX_error : EPROTO)); + goto bail; + } + if (stat != MOREDATA || + repbuf.len < sizeof(*ackp) || + ackp->PRIM_type != T_OPTMGMT_ACK || + ackp->MGMT_flags != T_SUCCESS) { + strcpy(errbuf, "unknown getmsg err"); + goto bail; + } + + memset(&repbuf2, 0, sizeof(repbuf2)); + repbuf2.maxlen = hp->len; + rp = malloc(hp->len); + if (rp == NULL) { + sprintf(errbuf, "malloc: %.128s", strerror(errno)); + goto bail; + } + repbuf2.buf = (char *)rp; + + flags = 0; + memset(repbuf2.buf, 0, repbuf2.len); + stat = getmsg(s, NULL, &repbuf2, &flags); + if (stat < 0) { + sprintf(errbuf, "getmsg2: %.128s", strerror(errno)); + goto bail; + } + + /* Spin through the routes */ + rp2 = rp; + for (rp2 = rp; (char *)rp2 < (char *)rp + repbuf2.len; ++rp2) { + if (hp->level != MIB2_IP || hp->name != MIB2_IP_21) + continue; + + if (rp2->ipRouteInfo.re_ire_type == IRE_CACHE || + rp2->ipRouteInfo.re_ire_type == IRE_BROADCAST) + continue; + + /* Got one we want to keep */ + rl = malloc(sizeof(*rl)); + if (rl == NULL) { + sprintf(errbuf, + "malloc 2: %.128s", strerror(errno)); + goto bail; + } + memset(rl, 0, sizeof(*rl)); + + rl->mask = rp2->ipRouteMask; + rl->dest = rp2->ipRouteDest; + rl->gate = rp2->ipRouteNextHop; + if (rp2->ipRouteIfIndex.o_length > 0) { + i = rp2->ipRouteIfIndex.o_length; + if (i > sizeof(rl->ifname) - 1) + i = sizeof(rl->ifname) - 1; + strncpy(rl->ifname, + rp2->ipRouteIfIndex.o_bytes, i); + rl->ifname[i] = '\0'; + } + + /* Keep in order (just for fun) */ + if (routelist == NULL) + routelist = rl; + if (rl2 != NULL) + rl2->next = rl; + rl2 = rl; + } + free(rp); + rp = NULL; + } + + strcpy(errbuf, "failed!"); + +bail: + if (routelist != NULL) { + freeroutelist(routelist); + routelist = NULL; + } +done: + if (rp != NULL) + free(rp); + close(s); + return (routelist); +} + +static void +freeroutelist(register struct routelist *rl) +{ + register struct routelist *rl2; + + while (rl != NULL) { + rl2 = rl->next; + free(rl); + rl = rl2; + } +} |