/* * @(#) $Id: rsrr_unicast.c,v 4.4 1998/08/18 17:31:23 lindell Exp $ */ /************************ rsvp_unicast.c **************************** * * * System-dependent Unicast route lookup routine * * * *********************************************************************/ #define USYNCH 1 #include "rsvp_daemon.h" #include u_long bmp_unicast; int unicast_result_ready; int unicast_query_waiting; int asynchronous = 1; static int init_failed = 0; int locate_LIH(unsigned int, net_addr *); int locate_LIH(unsigned int ifidx, net_addr *addr) { int i; for (i = 0; i < if_num; i++) { if (IsNumAPI(i)) continue; if (ifidx == if_vec[i].if_index) { if (net_addr_equal(addr, &(NET_GET_IF_PHY_ADDR(&(if_vec[i].if_addr))))) return i; } } return -1; } #ifdef SOLARIS #include #define SOLARIS_2_6 6 /* * Routing socket indicies */ #define RTAX_DST 0 #define RTAX_GATEWAY 1 #define RTAX_NETMASK 2 #define RTAX_GENMASK 3 #define RTAX_IFP 4 #define RTAX_IFA 5 #define RTAX_AUTHOR 6 #define RTAX_BRD 7 #define RTAX_MAX 8 static int getSolarisRelease(void) { struct utsname n; int major, minor; char *period, *release; if (uname(&n) < 0) { perror("uname"); return (-1); } release = strdup(n.release); if ((period = strchr(release, '.'))) { *period = '\0'; minor = atoi(period + 1); } major = atoi(release); free (release); if (major != 5) return (-1); else return (minor); } #endif /* SOLARIS */ #if defined(PF_ROUTE) && !defined(sgi_53) int seq; /* routing socket sequence num */ int rskt; /* routing socket */ /* * This code will assumes a routing socket and will work on many newer * BSD-like machines * */ /* * Copyright 1995 Massachusetts Institute of Technology * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby * granted, provided that both the above copyright notice and this * permission notice appear in all copies, that both the above * copyright notice and this permission notice appear in all * supporting documentation, and that the name of M.I.T. not be used * in advertising or publicity pertaining to distribution of the * software without specific, written prior permission. M.I.T. makes * no representations about the suitability of this software for any * purpose. It is provided "as is" without express or implied * warranty. * * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT * SHALL M.I.T. 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. */ #include #include #include #include #include #include /* * Round up 'a' to next multiple of 'size' */ #define ROUNDUP(a, size) (((a) & ((size)-1)) ? (1 + ((a) | ((size)-1))) : (a)) /* * Step to next socket address structure; * if sa_len is 0, assume it is sizeof(u_long). */ #define NEXT_SA(ap) ap = (struct sockaddr *) \ ((caddr_t) ap + (ap->sa_len ? ROUNDUP(ap->sa_len, sizeof (u_long)) : \ sizeof(u_long))) #ifdef SOLARIS static int useRoutingSocket = 0; #endif /* * For now, we open a new socket every time we want to get a route, to * save some effort in parsing. Eventually we should keep it open and * listen for changes. For now, just open and close it to make sure that * we can. */ int unicast_init(void) { #ifdef SOLARIS static int unicast_init_specific(); int release; if ((release = getSolarisRelease()) < 0) { init_failed = 1; NoUnicast = 1; return (0); } if (release >= SOLARIS_2_6) { rskt = socket(PF_ROUTE, SOCK_RAW, AF_INET); if (rskt < 0) { log(LOG_INFO, 0, "Unicast routing information unavailable\n"); init_failed = 1; NoUnicast = 1; return (0); } useRoutingSocket++; } else if (!unicast_init_specific()) { init_failed = 1; NoUnicast = 1; return (0); } #else /* SOLARIS */ rskt = socket(PF_ROUTE, SOCK_RAW, PF_UNSPEC); if (rskt < 0) { init_failed = 1; NoUnicast = 1; log(LOG_INFO, 0, "Unicast routing information unavailable\n"); return 0; } #endif /* SOLARIS */ NoUnicast = 0; close(rskt); return 1; } #define RTM_BUFLEN 2048 /* XXX should be in header file */ #define WORD_BNDARY(x) (((x)+sizeof(long)-1) & ~(sizeof(long)-1)) #ifdef SBM u_long unicast_route_gw_routing_socket(u_long addr) { int s, one = 1, i, len; struct sockaddr *sa; #ifdef SOLARIS struct sockaddr *rti_info[RTA_NUMBITS]; #else struct sockaddr *rti_info[RTAX_MAX]; #endif struct sockaddr_in *isa; struct rt_msghdr *mhp; caddr_t cp; char buf[RTM_BUFLEN]; if ((s = socket(PF_ROUTE, SOCK_RAW, AF_INET)) < 0) { perror("socket"); return (-1); } setsockopt(s, SOL_SOCKET, SO_USELOOPBACK, (char *)&one, sizeof (one)); /* Set up GET message */ memset(buf, 0, sizeof buf); mhp = (struct rt_msghdr *)buf; cp = buf + sizeof(struct rt_msghdr); isa = (struct sockaddr_in *)cp; isa->sin_family = AF_INET; isa->sin_addr.s_addr = addr; mhp->rtm_version = RTM_VERSION; mhp->rtm_type = RTM_GET; mhp->rtm_addrs = RTA_GATEWAY; mhp->rtm_seq = ++seq; mhp->rtm_msglen = sizeof(struct rt_msghdr) + sizeof(struct sockaddr_in); /* Send the message */ if (write(s, buf, (size_t)mhp->rtm_msglen) < 0) { log(LOG_ERR, errno, "writing message on routing socket"); close(s); return -1; } /* Read the reply */ do { len = read(s, buf, sizeof buf); } while (len > 0 && mhp->rtm_seq != seq && mhp->rtm_pid != getpid()); close(s); if (len < 0 || mhp->rtm_errno) { if (IsDebug(DEBUG_ROUTE)) log(LOG_DEBUG, errno, "unicast route lookup failed"); return -1; } if (len == 0) { log(LOG_ERR, 0, "unicast route lookup is confused"); return -1; } sa = (struct sockaddr *) (mhp + 1); get_route_info(sa, rti_info, mhp); isa = NULL; for (i = 0; i < RTAX_MAX; i++) { switch(mhp->rtm_addrs & (1 << i)) { case RTA_GATEWAY: isa = (struct sockaddr_in *)rti_info[i]; break; } } if (!isa) return (-1); else return (isa->sin_addr.s_addr); } u_long unicast_route_gw(u_long addr) { #ifdef SOLARIS if (useRoutingSocket) return unicast_route_gw_routing_socket(addr); else return unicast_route_gw_specific(addr); #else unicast_route_gw_routing_socket(addr); #endif } static void get_route_info(struct sockaddr *sa, struct sockaddr **rti_info, struct rt_msghdr *mhp) { int i; #ifdef SOLARIS caddr_t cp; cp = (caddr_t)sa; for (i = 0; i < RTA_NUMBITS; i++) { switch (mhp->rtm_addrs & (1 << i)) { case RTA_DST: case RTA_GATEWAY: case RTA_NETMASK: case RTA_GENMASK: case RTA_AUTHOR: case RTA_IFA: rti_info[i] = (struct sockaddr *)cp; cp += sizeof (struct sockaddr_in); break; case RTA_IFP: rti_info[i] = (struct sockaddr *)cp; cp += sizeof (struct sockaddr_dl); break; } } #else for (i = 0; i < RTAX_MAX; i++) { if (mhp->rtm_addrs & (1 << i)) { rti_info[i] = sa; NEXT_SA(sa); } else rti_info[i] = NULL; } #endif } #endif #ifdef SOLARIS int unicast_route(net_addr *addr) { static int unicast_route_routing_socket(net_addr *); static int unicast_route_specific(u_long); switch (NET_GET_TYPE(addr)) { case NET_ADDR_IPv4: if (useRoutingSocket) return (unicast_route_routing_socket(addr)); else return(unicast_route_specific( NET_GET_ADDR_IPv4(addr).s_addr)); case NET_ADDR_UDP_IPv4: if (useRoutingSocket) return (unicast_route_routing_socket(addr)); else return(unicast_route_specific( NET_GET_ADDR_UDP_IPv4(addr).sin_addr.s_addr)); default: return(0); } } #endif /* SOLARIS */ /* * Find the kernel's idea of the route to destination DEST (in host order) */ int #ifdef SOLARIS unicast_route_routing_socket(net_addr *addr) #else unicast_route(net_addr *addr) #endif { struct sockaddr *sp; struct sockaddr_in *s4; struct rt_msghdr *mhp; net_addr *addr2; char buf[RTM_BUFLEN]; int one, sz; int sock; int pid; #ifdef USYNCH int s; int len; struct sockaddr *sa; #ifdef SOLARIS caddr_t cp; struct sockaddr *rti_info[RTA_NUMBITS]; #else struct sockaddr *rti_info[RTAX_MAX]; #endif net_addr nadr; struct sockaddr_dl *sdlp; char ifname[IFNAMSIZ]; int i; int ifidx = 0; #endif /* USYNCH */ #ifdef USE_IPV6 struct sockaddr_in6 *s6; #endif /* USE_IPV6 */ #ifdef USYNCH /* Open socket */ s = socket(PF_ROUTE, SOCK_RAW, PF_UNSPEC); if (s < 0) { log(LOG_ERR, errno, "Could not open routing socket"); return -1; } else sock = s; #else /* USYNCH */ sock = rskt; #endif /* !USYNCH */ one = 1; setsockopt(sock, SOL_SOCKET, SO_USELOOPBACK, (char *) &one, sizeof one); /* Set up GET message */ memset(buf, 0, sizeof buf); mhp = (struct rt_msghdr *)buf; sp = (struct sockaddr *) &buf[WORD_BNDARY(sizeof (struct rt_msghdr))]; addr2 = net_addr_ip(addr); switch (NET_GET_TYPE(addr2)) { case NET_ADDR_IPv4: sz = sizeof(struct sockaddr_in); s4 = (struct sockaddr_in *) sp; s4->sin_family = AF_INET; s4->sin_addr.s_addr = NET_GET_ADDR_IPv4(addr2).s_addr; s4->sin_port = 0; #ifdef SOCKADDR_LEN s4->sin_len = sz; #endif break; #ifdef USE_IPV6 case NET_ADDR_IPv6: sz = sizeof(struct sockaddr_in6); s6 = (struct sockaddr_in6 *) sp; s6->sin6_family = AF_INET6; s6->sin6_addr = NET_GET_ADDR_IPv6(addr2); s6->sin6_port = 0; #ifdef SOCKADDR_LEN s6->sin6_len = sz; #endif break; #endif /* USE_IPV6 */ default: return(-1); } mhp->rtm_version = RTM_VERSION; mhp->rtm_type = RTM_GET; /* mhp->rtm_addrs = RTA_DST|RTA_IFP; */ mhp->rtm_addrs = RTA_DST|RTA_IFP|RTA_IFA; mhp->rtm_seq = ++seq; mhp->rtm_pid = pid = getpid(); mhp->rtm_msglen = ((char *)(sp)) + sz - buf; /* Send the message */ if (write(sock, buf, (size_t)mhp->rtm_msglen) < 0) { log(LOG_ERR, errno, "writing message on routing socket"); #ifdef USYNCH close(sock); #endif /* USYNCH */ return -1; } #if !defined(USYNCH) asynchronous = 1; return 0; #else /* USYNCH */ /* Read the reply */ do { len = read(sock, buf, sizeof buf); } while (len > 0 && mhp->rtm_seq != seq && mhp->rtm_pid != pid); close(sock); if (len < 0 || mhp->rtm_errno) { if (IsDebug(DEBUG_ROUTE)) log(LOG_DEBUG, errno, "unicast route lookup failed"); return -1; } if (len == 0) { log(LOG_ERR, 0, "unicast route lookup is confused"); return -1; } sa = (struct sockaddr *) (mhp + 1); #ifdef SOLARIS cp = (caddr_t)sa; for (i = 0; i < RTA_NUMBITS; i++) { switch (mhp->rtm_addrs & (1 << i)) { case RTA_DST: case RTA_GATEWAY: case RTA_NETMASK: case RTA_GENMASK: case RTA_AUTHOR: case RTA_IFA: rti_info[i] = (struct sockaddr *)cp; cp += sizeof (struct sockaddr_in); break; case RTA_IFP: rti_info[i] = (struct sockaddr *)cp; cp += sizeof (struct sockaddr_dl); break; } } #else for (i = 0; i < RTAX_MAX; i++) { if (mhp->rtm_addrs & (1 << i)) { rti_info[i] = sa; NEXT_SA(sa); } else rti_info[i] = NULL; } #endif if ((sa = rti_info[RTAX_IFP]) != NULL) { sdlp = (struct sockaddr_dl *)sa; if (sdlp->sdl_family != AF_LINK || sdlp->sdl_nlen == 0) { if (IsDebug(DEBUG_ROUTE)) log(LOG_DEBUG, 0, "unicast route through weird ifp"); return -1; } strncpy(ifname, sdlp->sdl_data, sdlp->sdl_nlen); if (sdlp->sdl_nlen < IFNAMSIZ) ifname[sdlp->sdl_nlen] = '\0'; ifidx = if_nametoindex(ifname); } if ((sa = rti_info[RTAX_IFA]) != NULL) { switch (sa->sa_family) { case AF_INET: s4 = (struct sockaddr_in *)sa; NET_SET_ADDR_IPv4(&nadr, s4->sin_addr); break; #ifdef USE_IPV6 case AF_INET6: s6 = (struct sockaddr_in6 *)sa; NET_SET_ADDR_IPv6(&nadr, s6->sin6_addr); break; #endif /* USE_IPV6 */ default: return (-1); } } else return -1; asynchronous = 0; return (locate_LIH(ifidx, &nadr)); #endif /* USYNCH */ } # else /* * ifndef PF_ROUTE - no routing socket. OS-specific code required, */ #ifdef SOLARIS static int unicast_init_specific(); static int unicast_route_specific(u_long); int unicast_init(void) { return (unicast_init_specific()); } #ifdef SBM static int unicast_route_gw_specific(u_long); u_long unicast_route_gw(u_long addr) { return (unicast_route_gw_specific(addr)); } #endif int unicast_route(net_addr *addr) { switch (NET_GET_TYPE(addr)) { case NET_ADDR_IPv4: return(unicast_route_specific( NET_GET_ADDR_IPv4(addr).s_addr)); case NET_ADDR_UDP_IPv4: return(unicast_route_specific( NET_GET_ADDR_UDP_IPv4(addr).sin_addr.s_addr)); #ifdef USE_IPV6 #ifdef WORKAROUNDS case NET_ADDR_IPv6: return(local_v6); case NET_ADDR_UDP_IPv6: return(local_v6); #endif /* WORKAROUNDS */ #endif /* USE_IPV6 */ default: return(-1); } } #else /* end of SOLARIS */ int unicast_route(net_addr *addr) { switch (NET_GET_TYPE(addr)) { case NET_ADDR_IPv4: return(unicast_route1(NET_GET_ADDR_IPv4(addr).s_addr)); case NET_ADDR_UDP_IPv4: return(unicast_route1(NET_GET_ADDR_UDP_IPv4(addr) .sin_addr.s_addr)); #ifdef USE_IPV6 #ifdef WORKAROUNDS case NET_ADDR_IPv6: return(local_v6); case NET_ADDR_UDP_IPv6: return(local_v6); #endif /* WORKAROUNDS */ #endif /* USE_IPV6 */ default: return(-1); } } #ifdef sgi_53 /* Do not have support for access to unicast routing table in IRIX 5.3. */ unicast_init() { #ifdef HOST_ONLY return(0); #else return(1); #endif } int unicast_route1(u_long dest) { /* This dummy routine is here to allow pre-routing-socket code to * compile. It should never be called. */ assert(0); return(0); } #else /* end sgi_53 */ /* Following code should work for many systems derived from * older BSDs, but it is known to work for Sun OS. */ /* * Copyright (c) 1983 Regents of the University of California. * All rights reserved. The Berkeley software License Agreement * specifies the terms and conditions for redistribution. */ #include #include #include #include #include #include #include #include #include #include #include #include struct nlist nl[] = { #define N_RTHOST 0 { "_rthost" }, #define N_RTNET 1 { "_rtnet" }, #define N_RTHASHSIZE 2 { "_rthashsize" }, {""} }; kvm_t *kd; char usage[] = "[-n] host"; struct xroute { char ifname[IFNAMSIZ]; struct sockaddr_in netmask; struct sockaddr_in out_if; struct sockaddr_in dst; struct sockaddr_in gw; } routes[256]; int nhost = 0; int nroute = 0; struct xroute default_route; int have_default = 0; int unicast_fd; /* forward declarations */ int read_routes(); int net_match(struct sockaddr_in *, struct xroute *); int rtfindx(struct sockaddr_in *); int ifname_to_if(char *); int kread(unsigned long addr, char *buf, unsigned nbytes); static int inet_netmatch(struct sockaddr_in *, struct sockaddr_in *); int unicast_init() { if ((kd = kvm_open(NULL, NULL, NULL, O_RDONLY, NULL)) == NULL) { log(LOG_INFO, 0, "Unicast routing information unavailable\n"); init_failed = 1; NoUnicast = 1; return(0); } if (kvm_nlist(kd, nl) < 0) { log(LOG_ERR, 0, "netstat: bad namelist\n"); init_failed = 1; NoUnicast = 1; return(0); } unicast_fd = socket(AF_INET, SOCK_DGRAM, 0); if (unicast_fd < 0) { log(LOG_ERR, errno, "unicast socket"); init_failed = 1; NoUnicast = 1; return(0); } NoUnicast = 0; return(1); } int unicast_route1(u_long addr) { int i; struct sockaddr_in sin; if (init_failed) return(-1); if (read_routes() == -1) return(-1); memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_addr.s_addr = addr; i = rtfindx(&sin); if (i == -1 && have_default) { if (IsDebug(DEBUG_ROUTE)) { log(LOG_DEBUG, 0, "Using default route ...\n"); log(LOG_DEBUG, 0, "route out interface %s\n", default_route.ifname); } return(ifname_to_if(default_route.ifname)); } else if (i == -1) { if (IsDebug(DEBUG_ROUTE)) log(LOG_DEBUG, 0, "No route\n"); return(-1); } if (IsDebug(DEBUG_ROUTE)) log(LOG_DEBUG, 0, "route out interface %s\n", routes[i].ifname); return(ifname_to_if(routes[i].ifname)); } int ifname_to_if(char *name) { int i; for (i = 0; i < if_num; i++) if (strncmp(if_vec[i].if_name, name, IFNAMSIZ) == 0) return(i); log(LOG_ERR, 0, "Couldn't find interface %s\n", name); return(-1); } int read_routes() { struct mbuf mbf, *mbfp = &mbf; register struct mbuf *m; struct mbuf **rhash; int i, doinghost = 1; int hashsize; struct ifreq ifrbuf, *ifr = &ifrbuf; int status; kread(nl[N_RTHASHSIZE].n_value, (char *)&hashsize, sizeof (hashsize)); rhash = (struct mbuf **)malloc( hashsize*sizeof (struct mbuf *) ); kread(nl[N_RTHOST].n_value, (char *)rhash, hashsize*sizeof (struct mbuf *)); nroute = 0; again: for (i = 0; i < hashsize; i++) { if (rhash[i] == 0) continue; m = rhash[i]; while (m) { struct ifnet ifz, *ifp = &ifz; struct rtentry *rt; struct ifaddr ifx, *ifxp = &ifx; char ifbuf[IFNAMSIZ]; kread((u_long)m, (char *)mbfp, sizeof(struct mbuf)); rt = mtod(mbfp, struct rtentry *); assert(rt->rt_ifp); routes[nroute].dst = *(struct sockaddr_in *)&rt->rt_dst; routes[nroute].gw = *(struct sockaddr_in *)&rt->rt_gateway; kread((u_long)rt->rt_ifp, (char *)ifp, sizeof(struct ifnet)); assert(ifp->if_addrlist); kread((u_long)ifp->if_addrlist, (char *)ifxp, sizeof(struct ifaddr)); assert(ifxp->ifa_addr.sa_family == AF_INET); routes[nroute].out_if = *(struct sockaddr_in *)&ifx.ifa_addr; kread((u_long)ifp->if_name, ifbuf, IFNAMSIZ); sprintf(routes[nroute].ifname, "%s%d", ifbuf, ifp->if_unit); memcpy(ifr->ifr_name, routes[nroute].ifname, IFNAMSIZ); status = ioctl(unicast_fd, SIOCGIFNETMASK, ifr); if (status < 0) { perror("ioctl"); return(-1); } routes[nroute].netmask = *(struct sockaddr_in *)&ifr->ifr_addr; if (routes[nroute].dst.sin_addr.s_addr == INADDR_ANY) { default_route = routes[nroute]; have_default = 1; } else nroute++; assert(nroute < 256); m = mbfp->m_next; } } if (doinghost) { kread(nl[N_RTNET].n_value, (char *)rhash, hashsize*sizeof (struct mbuf *)); nhost = nroute; doinghost = 0; goto again; } free(rhash); return(1); } int kread(unsigned long addr, char *buf, unsigned nbytes) { return kvm_read(kd, addr, buf, nbytes); } /* * Find a route to dst as the kernel would. */ int rtfindx(struct sockaddr_in *dst) { int i; for (i = 0; i < nhost; i++) if (memcmp(&routes[i].dst, dst, sizeof(struct sockaddr)) == 0) return(i); for (i = nhost; i < nroute; i++) if (net_match(dst, &routes[i])) return(i); return(-1); } int net_match(struct sockaddr_in *dst, struct xroute *rp) { u_long nm = rp->netmask.sin_addr.s_addr; if (inet_netmatch(dst, &rp->dst)) { u_long ip1 = dst->sin_addr.s_addr; u_long ip2 = rp->dst.sin_addr.s_addr; if (inet_netmatch(dst, &rp->out_if)) { if ((ip1 & nm) == ip2) return(1); else return(0); } else return(1); } return(0); } int inet_netmatch(struct sockaddr_in *sin1, struct sockaddr_in *sin2) { return (inet_netof(sin1->sin_addr) == inet_netof(sin2->sin_addr)); } #endif /* ! sgi */ #endif /* ! SOLARIS */ #endif /* ! PF_ROUTE */ #ifdef SOLARIS /* * Copyright (c) Sun Microsystems, Inc. 1994. All rights reserved. * * License is granted to copy, to use, and to make and to use derivative works * for research and evaluation purposes, provided that Sun Microsystems is * acknowledged in all documentation pertaining to any such copy or * derivative work. Sun Microsystems grants no other licenses expressed or * implied. The Sun Microsystems trade name should not be used in any * advertising without its written permission. * * SUN MICROSYSTEMS MERCHANTABILITY OF THIS SOFTWARE OR THE SUITABILITY OF THIS * SOFTWARE FOR ANY PARTICULAR PURPOSE. The software is provided "as is" * without express or implied warranty of any kind. * * These notices must be retained in any copies of any part of this software. */ /* Portions of this code were dereived from Solaris route.c and netstat.c */ #include #include #include #include #include #include #include #include #include /* * Following Solaris code was supplied by Don Hoffman of Sun Microsystems. */ /* * Defines */ #define MAX_ROUTES (4 * 512) #ifndef T_CURRENT #define T_CURRENT MI_T_CURRENT #endif #define SOLARIS_2_6 6 static int t_current = T_CURRENT; /* * Structure definitions */ typedef struct mib_item_s { struct mib_item_s *next_item; long group; long mib_id; long length; char *valp; } mib_item_t; struct xroute { char ifname[IFNAMSIZ]; struct sockaddr_in netmask; struct sockaddr_in out_if; struct sockaddr_in dst; struct sockaddr_in gw; }; /* * Forward function declarations */ static mib_item_t *mibget(int sd); static void mibfree(mib_item_t *); static int is_host(mib2_ipRouteEntry_t * rp); static int is_gateway(mib2_ipRouteEntry_t * rp); int putmsg(int, struct strbuf *, struct strbuf *, int); int getmsg(int, struct strbuf *, struct strbuf *, int *); /* * Globals */ static int sd; static struct xroute routes[MAX_ROUTES]; static struct xroute default_route; static int have_default = 0; static int nhost = 0; static int nroute = 0; static int ifname_to_if(char *name) { int i; if (name) fprintf(stderr, "ifname is %s\n", name); for (i = 0; i < if_num; i++) if (strncmp(if_vec[i].if_name, name, IFNAMSIZ) == 0) return (i); log(LOG_ERR, 0, "Couldn't find interface %s\n", name); #ifdef SUNMOD return (0); #else return (-1); #endif } #ifdef SBM u_long unicast_route_gw_specific(u_long addr) { int i; struct sockaddr_in sin; if (read_routes() == -1) return (-1); memset(&sin, 0, sizeof (sin)); sin.sin_family = AF_INET; sin.sin_addr.s_addr = addr; i = rtfindx(&sin); if (i == -1 && have_default) { if (IsDebug(DEBUG_ROUTE)) { log(LOG_DEBUG, 0, "Using default route ...\n"); log(LOG_DEBUG, 0, "route out interface %s\n", default_route.ifname); } return (default_route.gw.sin_addr.s_addr); } else if (i >= 0) return (routes[i].gw.sin_addr.s_addr); else return -1; } #endif static int inet_netmatch(struct sockaddr_in * sin1, struct sockaddr_in * sin2) { return (inet_netof(sin1->sin_addr) == inet_netof(sin2->sin_addr)); } static int net_match(struct sockaddr_in * dst, struct xroute * rp) { u_long nm = rp->netmask.sin_addr.s_addr; if (inet_netmatch(dst, &rp->dst)) { u_long ip1 = dst->sin_addr.s_addr; u_long ip2 = rp->dst.sin_addr.s_addr; if (inet_netmatch(dst, &rp->out_if)) { if ((ip1 & nm) == ip2) return (1); else return (0); } else return (1); } return (0); } /* * Find a route to dst as the kernel would. */ static int rtfindx(struct sockaddr_in *dst) { struct sockaddr_in *sockp; int i; for (i = 0; i < nhost; i++) { sockp = &(routes[i].dst); #ifdef notdef log(LOG_DEBUG, 0, "routes[%d].dst, dst is %x,%x\n", i, sockp->sin_addr.s_addr, dst->sin_addr.s_addr); #endif if (memcmp(sockp, dst, sizeof (struct sockaddr)) == 0) return (i); } for (i = nhost; i < nroute; i++) if (net_match(dst, &routes[i])) return (i); return (-1); } static int read_routes() { mib_item_t *item; mib_item_t *first_item = nilp(mib_item_t); mib2_ipRouteEntry_t *rp; int ret_code = 1; int doinghost; int i; /* Get the route list. */ /* * TBD: don't need to do this every time. Only to a new mibget every X * seconds? */ if ((first_item = mibget(sd)) == nilp(mib_item_t)) { close(sd); ret_code = -1; goto leave; } /* Look up the entry */ have_default = 0; nroute = 0; nhost = 0; /* host routes first */ doinghost = 1; again: for (item = first_item; item; item = item->next_item) { /* skip all the other trash that comes up the mib stream */ if ((item->group != MIB2_IP) || (item->mib_id != MIB2_IP_21)) continue; rp = (mib2_ipRouteEntry_t *) item->valp; for (; (u_long) rp < (u_long)(item->valp + item->length); rp++) { /* skip routes that aren't what we want this pass */ if (doinghost) { if (!is_host(rp)) continue; } else { if (!is_gateway(rp)) continue; } /* fill in the blanks */ memset(&routes[nroute], 0, sizeof (struct xroute)); routes[nroute].dst.sin_family = AF_INET; routes[nroute].dst.sin_addr.s_addr = rp->ipRouteDest; routes[nroute].gw.sin_family = AF_INET; routes[nroute].gw.sin_addr.s_addr = rp->ipRouteNextHop; routes[nroute].netmask.sin_family = AF_INET; routes[nroute].netmask.sin_addr.s_addr = rp->ipRouteMask; routes[nroute].out_if.sin_family = AF_INET; routes[nroute].out_if.sin_addr.s_addr = rp->ipRouteInfo.re_src_addr; if (rp->ipRouteIfIndex.o_length >= IFNAMSIZ) rp->ipRouteIfIndex.o_length = IFNAMSIZ - 1; for (i = 0; i < rp->ipRouteIfIndex.o_length; i++) routes[nroute].ifname[i] = rp->ipRouteIfIndex.o_bytes[i]; routes[nroute].ifname[i] = '\0'; if (routes[nroute].dst.sin_addr.s_addr == INADDR_ANY) { default_route = routes[nroute]; have_default = 1; } else nroute++; } } /* net routes next */ if (doinghost) { nhost = nroute; doinghost = 0; goto again; } leave: mibfree(first_item); first_item = nilp(mib_item_t); return ret_code; } static int is_host(mib2_ipRouteEntry_t * rp) { if (rp->ipRouteMask == (IpAddress) - 1) return 1; else return 0; } static int is_gateway(mib2_ipRouteEntry_t * rp) { #ifndef PF_ROUTE if (rp->ipRouteInfo.re_ire_type == IRE_GATEWAY || rp->ipRouteInfo.re_ire_type == IRE_NET || rp->ipRouteInfo.re_ire_type == IRE_ROUTE_ASSOC || rp->ipRouteInfo.re_ire_type == IRE_ROUTE_REDIRECT) return 1; else return 0; #else if (rp->ipRouteInfo.re_ire_type == IRE_DEFAULT || rp->ipRouteInfo.re_ire_type == IRE_PREFIX || rp->ipRouteInfo.re_ire_type == IRE_HOST || rp->ipRouteInfo.re_ire_type == IRE_HOST_REDIRECT) return 1; else return 0; #endif } static mib_item_t * mibget (int sd) { char buf[512]; int flags; int i, j, getcode; struct strbuf ctlbuf, databuf; struct T_optmgmt_req *tor = (struct T_optmgmt_req *)buf; struct T_optmgmt_ack *toa = (struct T_optmgmt_ack *)buf; struct T_error_ack *tea = (struct T_error_ack *)buf; struct opthdr *req; mib_item_t *first_item = nilp(mib_item_t); mib_item_t *last_item = nilp(mib_item_t); mib_item_t *temp; int debug = 0; tor->PRIM_type = T_OPTMGMT_REQ; tor->OPT_offset = sizeof(struct T_optmgmt_req); tor->OPT_length = sizeof(struct opthdr); tor->MGMT_flags = t_current; req = (struct opthdr *)&tor[1]; req->level = MIB2_IP; /* any MIB2_xxx value ok here */ req->name = 0; req->len = 0; ctlbuf.buf = buf; ctlbuf.len = tor->OPT_length + tor->OPT_offset; flags = 0; if (putmsg(sd, &ctlbuf, nilp(struct strbuf), flags) == -1) { perror("mibget: putmsg(ctl) failed"); goto error_exit; } /* * each reply consists of a ctl part for one fixed structure * or table, as defined in mib2.h. The format is a T_OPTMGMT_ACK, * containing an opthdr structure. level/name identify the entry, * len is the size of the data part of the message. */ req = (struct opthdr *)&toa[1]; ctlbuf.maxlen = sizeof(buf); for (j=1; ; j++) { flags = 0; getcode = getmsg(sd, &ctlbuf, nilp(struct strbuf), &flags); if (getcode == -1) { perror("mibget getmsg(ctl) failed"); if (debug) { fprintf(stderr, "# level name len\n"); i = 0; for (last_item = first_item; last_item; last_item = last_item->next_item) printf("%d %4ld %5ld %ld\n", ++i, last_item->group, last_item->mib_id, last_item->length); } goto error_exit; } if (getcode == 0 && ctlbuf.len >= sizeof(struct T_optmgmt_ack) && toa->PRIM_type == T_OPTMGMT_ACK && toa->MGMT_flags == T_SUCCESS && req->len == 0) { if (debug) printf("mibget getmsg() %d returned EOD (level %ld, name %ld)\n", j, req->level, req->name); return first_item; /* this is EOD msg */ } if (ctlbuf.len >= sizeof(struct T_error_ack) && tea->PRIM_type == T_ERROR_ACK) { fprintf(stderr, "mibget %d gives (%d) T_ERROR_ACK: TLI_error = 0x%lx, UNIX_error = 0x%lx\n", j, getcode, tea->TLI_error, tea->UNIX_error); errno = (tea->TLI_error == TSYSERR) ? tea->UNIX_error : EPROTO; goto error_exit; } if (getcode != MOREDATA || ctlbuf.len < sizeof(struct T_optmgmt_ack) || toa->PRIM_type != T_OPTMGMT_ACK || toa->MGMT_flags != T_SUCCESS) { printf( "mibget getmsg(ctl) %d returned %d, ctlbuf.len = %d, PRIM_type = %ld\n", j, getcode, ctlbuf.len, toa->PRIM_type); if (toa->PRIM_type == T_OPTMGMT_ACK) printf( "T_OPTMGMT_ACK: MGMT_flags = 0x%lx, req->len = %ld\n", toa->MGMT_flags, req->len); errno = ENOMSG; goto error_exit; } temp = (mib_item_t *)malloc(sizeof(mib_item_t)); if (!temp) { perror("mibget malloc failed"); goto error_exit; } if (last_item) last_item->next_item = temp; else first_item = temp; last_item = temp; last_item->next_item = nilp(mib_item_t); last_item->group = req->level; last_item->mib_id = req->name; last_item->length = req->len; last_item->valp = (char *)malloc(req->len); if (debug) printf( "msg %d: group = %4ld mib_id = %5ld length = %ld\n", j, last_item->group, last_item->mib_id, last_item->length); databuf.maxlen = last_item->length; databuf.buf = last_item->valp; databuf.len = 0; flags = 0; getcode = getmsg(sd, nilp(struct strbuf), &databuf, &flags); if (getcode == -1) { perror("mibget getmsg(data) failed"); goto error_exit; } else if (getcode != 0) { printf( "mibget getmsg(data) returned %d, databuf.maxlen = %d, databuf.len = %d\n", getcode, databuf.maxlen, databuf.len); goto error_exit; } } error_exit:; while (first_item) { last_item = first_item; first_item = first_item->next_item; free(last_item); } return first_item; } static void mibfree(mib_item_t * first_item) { mib_item_t *last_item; while (first_item) { last_item = first_item; first_item = first_item->next_item; free(last_item); } } int unicast_init_specific(void) { int release; release = getSolarisRelease(); if (release < SOLARIS_2_6) { t_current = 0x100; } sd = open("/dev/ip", O_RDWR); if (sd == -1) { log(LOG_INFO, 0, "Unicast routing information unavailable\n"); init_failed = 1; return 0; } return (1); } int unicast_route_specific(u_long addr) { int i; struct sockaddr_in sin; if (init_failed) return (-1); if (read_routes() == -1) return (-1); memset(&sin, 0, sizeof (sin)); sin.sin_family = AF_INET; sin.sin_addr.s_addr = addr; i = rtfindx(&sin); if (i == -1 && have_default) { if (strlen(default_route.ifname) == 0) { /* * The interface name is not set on def route. Look it * up. */ if ((i = rtfindx(&default_route.gw)) == -1) return (-1); } if (IsDebug(DEBUG_ROUTE)) { log(LOG_DEBUG, 0, "Using default route ...\n"); log(LOG_DEBUG, 0, "route out interface %s\n", default_route.ifname); } return (ifname_to_if(default_route.ifname)); } else if (i == -1) { if (IsDebug(DEBUG_ROUTE)) log(LOG_DEBUG, 0, "No route\n"); return (-1); } if (strlen(routes[i].ifname) == 0) { /* * The interface name is not set on GW routes. Look it up. */ if ((i = rtfindx(&routes[i].gw)) == -1) return (-1); } if (IsDebug(DEBUG_ROUTE)) log(LOG_DEBUG, 0, "route out interface %s\n", routes[i].ifname); return (ifname_to_if(routes[i].ifname)); } #endif