| /* $OpenBSD: route.c,v 1.66 2004/11/17 01:47:20 itojun Exp $ */ |
| /* $NetBSD: route.c,v 1.15 1996/05/07 02:55:06 thorpej Exp $ */ |
| |
| /* |
| * Copyright (c) 1983, 1988, 1993 |
| * 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. Neither the name of the University nor the names of its contributors |
| * 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. |
| */ |
| |
| #ifdef INHERITED_CODE |
| #ifndef lint |
| #if 0 |
| static char sccsid[] = "from: @(#)route.c 8.3 (Berkeley) 3/9/94"; |
| #else |
| static char *rcsid = "$OpenBSD: route.c,v 1.66 2004/11/17 01:47:20 itojun Exp $"; |
| #endif |
| #endif /* not lint */ |
| #endif |
| |
| #include <net-snmp/net-snmp-config.h> |
| |
| #if HAVE_SYS_TYPES_H |
| #include <sys/types.h> |
| #endif |
| |
| #if HAVE_NETINET_IN_H |
| #include <netinet/in.h> |
| #endif |
| #if HAVE_ARPA_INET_H |
| #include <arpa/inet.h> |
| #endif |
| |
| #include <net-snmp/net-snmp-includes.h> |
| |
| #if HAVE_UNISTD_H |
| #include <unistd.h> |
| #endif |
| #if HAVE_NETDB_H |
| #include <netdb.h> |
| #endif |
| |
| #ifndef INET |
| #define INET |
| #endif |
| |
| #include "main.h" |
| #include "netstat.h" |
| #if HAVE_WINSOCK_H |
| #include "winstub.h" |
| #endif |
| |
| /* inetCidrRouteTable */ |
| #define SET_IFNO 0x01 |
| #define SET_TYPE 0x02 |
| #define SET_PRTO 0x04 |
| #define SET_AGE 0x08 |
| #define SET_AS 0x10 |
| #define SET_MET1 0x20 |
| #define SET_ALL 0x3f |
| |
| /* ip6RouteTable */ |
| #define SET_HOP 0x40 |
| #define SET_INVALID 0x80 |
| #define SET_ALL6 0x67 |
| /* not invalid, and only the columns that we fetch */ |
| |
| struct route_entry { |
| int af; |
| struct sockaddr_storage dst; |
| struct sockaddr_storage hop; |
| int mask; |
| int ifNumber; |
| int type; |
| int proto; |
| int age; |
| int as; |
| int metric1; |
| int set_bits; |
| char ifname[64]; |
| }; |
| |
| |
| static void pr_rtxhdr(int af, const char *table); |
| static void p_rtnodex( struct route_entry *rp ); |
| |
| /* |
| * Print routing tables. |
| */ |
| int |
| routexpr(int af) |
| { |
| struct route_entry route, *rp = &route; |
| oid rtcol_oid[] = { 1,3,6,1,2,1,4,24,7,1,0 }; /* inetCidrRouteEntry */ |
| size_t rtcol_len = OID_LENGTH( rtcol_oid ); |
| netsnmp_variable_list *var = NULL, *vp; |
| int hdr_af = AF_UNSPEC; |
| int printed = 0; |
| |
| #define ADD_RTVAR( x ) rtcol_oid[ rtcol_len-1 ] = x; \ |
| snmp_varlist_add_variable( &var, rtcol_oid, rtcol_len, ASN_NULL, NULL, 0) |
| ADD_RTVAR( 7 ); /* inetCidrRouteIfIndex */ |
| ADD_RTVAR( 8 ); /* inetCidrRouteType */ |
| ADD_RTVAR( 9 ); /* inetCidrRouteProto */ |
| ADD_RTVAR( 10 ); /* inetCidrRouteAge */ |
| ADD_RTVAR( 11 ); /* inetCidrRouteNextHopAS */ |
| ADD_RTVAR( 12 ); /* inetCidrRouteMetric1 */ |
| #undef ADD_RTVAR |
| |
| /* |
| * Now walk the inetCidrRouteTable, reporting the various route entries |
| */ |
| while ( 1 ) { |
| oid *op; |
| unsigned char *cp; |
| int i; |
| |
| if (netsnmp_query_getnext( var, ss ) != SNMP_ERR_NOERROR) |
| break; |
| rtcol_oid[ rtcol_len-1 ] = 7; /* ifRouteIfIndex */ |
| if ( snmp_oid_compare( rtcol_oid, rtcol_len, |
| var->name, rtcol_len) != 0 ) |
| break; /* End of Table */ |
| if (var->type == SNMP_NOSUCHOBJECT || |
| var->type == SNMP_NOSUCHINSTANCE || |
| var->type == SNMP_ENDOFMIBVIEW) |
| break; |
| memset( &route, 0, sizeof( struct route_entry )); |
| /* Extract inetCidrRouteDest, inetCidrRoutePfxLen, |
| * inetCidrRouteNextHop from index */ |
| switch (var->name[rtcol_len]) { |
| case 1: |
| { struct sockaddr_in *sin = (struct sockaddr_in *)&route.dst; |
| int len; |
| route.af = AF_INET; |
| sin->sin_family = AF_INET; |
| op = var->name+rtcol_len+1; |
| len = *op++; |
| cp = (unsigned char *)&sin->sin_addr; |
| for (i = 0; i < len; i++) *cp++ = *op++; |
| route.mask = *op++; |
| op += *op+1; |
| op++; /* addrType */ |
| op++; /* addrLen */ |
| sin = (struct sockaddr_in *)&route.hop; |
| sin->sin_family = AF_INET; |
| cp = (unsigned char *)&sin->sin_addr; |
| for (i = 0; i < len; i++) *cp++ = *op++; |
| break; |
| } |
| case 2: |
| { struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&route.dst; |
| int len; |
| route.af = AF_INET6; |
| sin6->sin6_family = AF_INET6; |
| op = var->name+rtcol_len+1; |
| len = *op++; |
| cp = (unsigned char *)&sin6->sin6_addr; |
| for (i = 0; i < len; i++) *cp++ = *op++; |
| route.mask = *op++; |
| op += *op+1; |
| op++; /* addrType */ |
| op++; /* addrLen */ |
| sin6 = (struct sockaddr_in6 *)&route.hop; |
| sin6->sin6_family = AF_INET6; |
| cp = (unsigned char *)&sin6->sin6_addr; |
| for (i = 0; i < len; i++) *cp++ = *op++; |
| break; |
| } |
| default: |
| fprintf(stderr, "Bad address type: %d\n", (int)var->name[rtcol_len]); |
| exit(1); |
| } |
| /* Extract ipRouteDest index value */ |
| |
| for ( vp=var; vp; vp=vp->next_variable ) { |
| switch ( vp->name[ rtcol_len - 1 ] ) { |
| case 7: /* inetCidrRouteIfIndex */ |
| rp->ifNumber = *vp->val.integer; |
| rp->set_bits |= SET_IFNO; |
| break; |
| case 8: /* inetCidrRouteType */ |
| rp->type = *vp->val.integer; |
| rp->set_bits |= SET_TYPE; |
| break; |
| case 9: /* inetCidrRouteProto */ |
| rp->proto = *vp->val.integer; |
| rp->set_bits |= SET_PRTO; |
| break; |
| case 10: /* inetCidrRouteAge */ |
| rp->age = *vp->val.integer; |
| rp->set_bits |= SET_AGE; |
| break; |
| case 11: /* inetCidrRouteNextHopAS */ |
| rp->as = *vp->val.integer; |
| rp->set_bits |= SET_AS; |
| break; |
| case 12: /* inetCidrRouteMetric1 */ |
| rp->metric1 = *vp->val.integer; |
| rp->set_bits |= SET_MET1; |
| break; |
| } |
| } |
| if (rp->set_bits != SET_ALL) { |
| continue; /* Incomplete query */ |
| } |
| |
| if (af != AF_UNSPEC && rp->af != af) |
| continue; |
| |
| if (hdr_af != rp->af) { |
| if (hdr_af != AF_UNSPEC) |
| printf("\n"); |
| hdr_af = rp->af; |
| pr_rtxhdr(hdr_af, "inetCidrRouteTable"); |
| } |
| p_rtnodex( rp ); |
| printed++; |
| } |
| return printed; |
| } |
| |
| /* |
| * Backwards-compatibility for the IPV6-MIB |
| */ |
| int |
| route6pr(int af) |
| { |
| struct route_entry route, *rp = &route; |
| oid rtcol_oid[] = { 1,3,6,1,2,1,55,1,11,1,0 }; /* ipv6RouteEntry */ |
| size_t rtcol_len = OID_LENGTH( rtcol_oid ); |
| netsnmp_variable_list *var = NULL, *vp; |
| int printed = 0; |
| int hdr_af = AF_UNSPEC; |
| int i; |
| |
| if (af != AF_UNSPEC && af != AF_INET6) |
| return 0; |
| |
| #define ADD_RTVAR( x ) rtcol_oid[ rtcol_len-1 ] = x; \ |
| snmp_varlist_add_variable( &var, rtcol_oid, rtcol_len, ASN_NULL, NULL, 0) |
| ADD_RTVAR( 4 ); /* ipv6RouteIfIndex */ |
| ADD_RTVAR( 5 ); /* ipv6RouteNextHop */ |
| ADD_RTVAR( 6 ); /* ipv6RouteType */ |
| ADD_RTVAR( 7 ); /* ipv6RouteProto */ |
| ADD_RTVAR( 11 ); /* ipv6RouteMetric */ |
| ADD_RTVAR( 14 ); /* ipv6RouteValid */ |
| #undef ADD_RTVAR |
| |
| /* |
| * Now walk the ipv6RouteTable, reporting the various route entries |
| */ |
| while ( 1 ) { |
| oid *op; |
| unsigned char *cp, *cp1; |
| struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&route.dst; |
| |
| if (netsnmp_query_getnext( var, ss ) != SNMP_ERR_NOERROR) |
| break; |
| rtcol_oid[ rtcol_len-1 ] = 4; /* ipv6RouteIfIndex */ |
| if ( snmp_oid_compare( rtcol_oid, rtcol_len, |
| var->name, rtcol_len) != 0 ) |
| break; /* End of Table */ |
| if (var->type == SNMP_NOSUCHOBJECT || |
| var->type == SNMP_NOSUCHINSTANCE || |
| var->type == SNMP_ENDOFMIBVIEW) |
| break; |
| memset( &route, 0, sizeof( struct route_entry )); |
| rp->af = AF_INET6; |
| sin6->sin6_family = AF_INET6; |
| op = var->name+rtcol_len; |
| cp = (unsigned char *)&sin6->sin6_addr; |
| for (i = 0; i < 16; i++) *cp++ = *op++; |
| route.mask = *op++; |
| |
| for ( vp=var; vp; vp=vp->next_variable ) { |
| switch ( vp->name[ rtcol_len - 1 ] ) { |
| case 4: /* ipv6RouteIfIndex */ |
| rp->ifNumber = *vp->val.integer; |
| /* |
| * This is, technically, an Ipv6IfIndex, which |
| * could maybe be different than the IfIndex |
| * for the same interface. We ignore this |
| * possibility for now, in the hopes that |
| * nobody actually allocates these numbers |
| * differently. |
| */ |
| rp->set_bits |= SET_IFNO; |
| break; |
| case 5: /* ipv6RouteNextHop */ |
| cp1 = (unsigned char *)vp->val.string; |
| sin6 = (struct sockaddr_in6 *)&rp->hop; |
| sin6->sin6_family = AF_INET6; |
| cp = (unsigned char *)&sin6->sin6_addr; |
| for (i = 0; i < 16; i++) *cp++ = *cp1++; |
| rp->set_bits |= SET_HOP; |
| case 6: /* ipv6RouteType */ |
| rp->type = *vp->val.integer; |
| /* This enum maps to similar values in inetCidrRouteType */ |
| rp->set_bits |= SET_TYPE; |
| break; |
| case 7: /* ipv6RouteProtocol */ |
| rp->proto = *vp->val.integer; |
| /* TODO: this does not map directly to the |
| * inetCidrRouteProtocol values. If we use |
| * rp->proto more, we will have to manage this. */ |
| rp->set_bits |= SET_PRTO; |
| break; |
| case 11: /* ipv6RouteMetric */ |
| rp->metric1 = *vp->val.integer; |
| rp->set_bits |= SET_MET1; |
| break; |
| case 14: /* ipv6RouteValid */ |
| if (*vp->val.integer == 2) |
| rp->set_bits |= SET_INVALID; |
| break; |
| } |
| } |
| if (rp->set_bits != SET_ALL6) { |
| continue; /* Incomplete query */ |
| } |
| |
| if (hdr_af != rp->af) { |
| if (hdr_af != AF_UNSPEC) |
| printf("\n"); |
| hdr_af = rp->af; |
| pr_rtxhdr(AF_INET6, "ip6RouteTable"); |
| } |
| p_rtnodex( rp ); |
| printed++; |
| } |
| return printed; |
| } |
| |
| |
| /* column widths; each followed by one space */ |
| #ifndef NETSNMP_ENABLE_IPV6 |
| #define WID_DST(af) 26 /* width of destination column */ |
| #define WID_GW(af) 18 /* width of gateway column */ |
| #else |
| /* width of destination/gateway column */ |
| /* strlen("fe80::aaaa:bbbb:cccc:dddd@gif0") == 30, strlen("/128") == 4 */ |
| #define WID_DST(af) ((af) == AF_INET6 ? (nflag ? 34 : 26) : 26) |
| #define WID_GW(af) ((af) == AF_INET6 ? (nflag ? 39 : 26) : 26) |
| #endif /* NETSNMP_ENABLE_IPV6 */ |
| |
| /* |
| * Print header for routing table columns. |
| */ |
| static void |
| pr_rtxhdr(int af, const char *table) |
| { |
| switch (af) { |
| case AF_INET: |
| printf("IPv4 Routing tables (inetCidrRouteTable)\n"); |
| break; |
| case AF_INET6: |
| printf("IPv6 Routing tables (%s)\n", table); |
| break; |
| } |
| printf("%-*.*s ", |
| WID_DST(af), WID_DST(af), "Destination"); |
| printf("%-*.*s %-6.6s %s\n", |
| WID_GW(af), WID_GW(af), "Gateway", |
| "Flags", "Interface"); |
| } |
| |
| #ifndef HAVE_INET_NTOP |
| /* MSVC and MinGW */ |
| #define inet_ntop netsnmp_inet_ntop |
| static const char * |
| netsnmp_inet_ntop(int af, const void *src, char *dst, size_t size) |
| { |
| DWORD out_len = size; |
| |
| switch (af) { |
| case AF_INET: |
| { |
| struct sockaddr_in in; |
| |
| memset(&in, 0, sizeof(in)); |
| in.sin_family = af; |
| memcpy(&in.sin_addr, src, 4); |
| if (WSAAddressToString((struct sockaddr *)&in, sizeof(in), NULL, dst, |
| &out_len) == 0) |
| return dst; |
| } |
| break; |
| case AF_INET6: |
| { |
| struct sockaddr_in6 in6; |
| |
| memset(&in6, 0, sizeof(in6)); |
| in6.sin6_family = af; |
| memcpy(&in6.sin6_addr, src, 16); |
| if (WSAAddressToString((struct sockaddr *)&in6, sizeof(in6), NULL, dst, |
| &out_len) == 0) |
| return dst; |
| } |
| break; |
| } |
| return NULL; |
| } |
| #endif |
| |
| /* |
| * Return the name of the network whose address is given. |
| * The address is assumed to be that of a net or subnet, not a host. |
| */ |
| static char * |
| netxname(struct sockaddr_storage *in, int mask) |
| { |
| static char host[MAXHOSTNAMELEN]; |
| static char line[MAXHOSTNAMELEN]; |
| struct sockaddr_in *sin = (struct sockaddr_in *)in; |
| struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)in; |
| |
| switch (in->ss_family) { |
| case AF_INET: |
| inet_ntop(in->ss_family, &sin->sin_addr, host, sizeof(host)); |
| if (mask == 32) strcpy(line, host); |
| else snprintf(line, sizeof(line), "%s/%d", host, mask); |
| break; |
| case AF_INET6: |
| inet_ntop(in->ss_family, &sin6->sin6_addr, host, sizeof(host)); |
| if (mask == 128) strcpy(line, host); |
| else snprintf(line, sizeof(line), "%s/%d", host, mask); |
| break; |
| } |
| return line; |
| } |
| |
| static char * |
| routexname(struct sockaddr_storage *in) |
| { |
| char *cp; |
| static char line[MAXHOSTNAMELEN]; |
| struct hostent *hp = NULL; |
| static char domain[MAXHOSTNAMELEN]; |
| static int first = 1; |
| struct sockaddr_in *sin = (struct sockaddr_in *)in; |
| struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)in; |
| |
| if (first) { |
| first = 0; |
| if (gethostname(line, sizeof line) == 0 && |
| (cp = strchr(line, '.'))) |
| (void) strlcpy(domain, cp + 1, sizeof domain); |
| else |
| domain[0] = '\0'; |
| } |
| cp = NULL; |
| if (!nflag) { |
| switch (in->ss_family) { |
| case AF_INET: |
| hp = netsnmp_gethostbyaddr(&sin->sin_addr, |
| sizeof (struct in_addr), AF_INET); |
| break; |
| case AF_INET6: |
| hp = netsnmp_gethostbyaddr(&sin6->sin6_addr, |
| sizeof (struct in6_addr), AF_INET6); |
| break; |
| } |
| if (hp) { |
| if ((cp = strchr(hp->h_name, '.')) && !strcmp(cp + 1, domain)) |
| *cp = '\0'; |
| cp = hp->h_name; |
| } |
| } |
| if (cp) { |
| strlcpy(line, cp, sizeof(line)); |
| } else { |
| switch (in->ss_family) { |
| case AF_INET: |
| inet_ntop(sin->sin_family, &sin->sin_addr, line, sizeof(line)); |
| break; |
| case AF_INET6: |
| inet_ntop(sin6->sin6_family, &sin6->sin6_addr, line, sizeof(line)); |
| break; |
| } |
| } |
| return (line); |
| } |
| |
| |
| static char * |
| s_rtflagsx( struct route_entry *rp ) |
| { |
| static char flag_buf[10]; |
| char *cp = flag_buf; |
| |
| *cp++ = '<'; |
| *cp++ = 'U'; /* route is in use */ |
| if ((rp->af == AF_INET && rp->mask == 32) || |
| (rp->af == AF_INET6 && rp->mask == 128)) |
| *cp++ = 'H'; /* host */ |
| if (rp->proto == 4) |
| *cp++ = 'D'; /* ICMP redirect */ |
| if (rp->type == 4) |
| *cp++ = 'G'; /* remote destination/net */ |
| *cp++ = '>'; |
| *cp = 0; |
| return flag_buf; |
| } |
| |
| static void |
| p_rtnodex( struct route_entry *rp ) |
| { |
| get_ifname(rp->ifname, rp->ifNumber); |
| if (rp->af == AF_INET) { |
| struct sockaddr_in *sin = (struct sockaddr_in *)&rp->dst; |
| printf("%-*s ", |
| WID_DST(AF_INET), |
| (sin->sin_addr.s_addr == INADDR_ANY) ? "default" : |
| (rp->mask == 32 ? |
| routexname(&rp->dst) : |
| netxname(&rp->dst, rp->mask))); |
| } |
| else if (rp->af == AF_INET6) { |
| struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&rp->dst; |
| struct in6_addr in6_addr_any = IN6ADDR_ANY_INIT; |
| printf("%-*s ", |
| WID_DST(AF_INET6), |
| memcmp(&sin6->sin6_addr, &in6_addr_any, sizeof(in6_addr_any)) == 0 ? "default" : |
| (rp->mask == 128 ? |
| routexname(&rp->dst) : |
| netxname(&rp->dst, rp->mask))); |
| } |
| printf("%-*s %-6.6s %s", |
| WID_GW(rp->af), |
| 1 ? routexname(&rp->hop) : "*", |
| s_rtflagsx(rp), rp->ifname); |
| if ((rp->set_bits & SET_AS) && rp->as != 0) |
| printf(" (AS %d)", rp->as); |
| printf("\n"); |
| } |