/* * Copyright (c) 2001-2003 * Fraunhofer Institute for Open Communication Systems (FhG Fokus). * All rights reserved. * * Author: Harti Brandt * * 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. * * THIS SOFTWARE IS PROVIDED BY AUTHOR 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 AUTHOR 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. * * $Begemot: bsnmp/snmp_mibII/mibII_route.c,v 1.9 2005/10/06 07:15:00 brandt_h Exp $ * * Routing table */ #include "support.h" #ifdef HAVE_SYS_TREE_H #include #else #include "tree.h" #endif #include "mibII.h" #include "mibII_oid.h" struct sroute { RB_ENTRY(sroute) link; uint32_t ifindex; uint8_t index[13]; uint8_t type; uint8_t proto; }; RB_HEAD(sroutes, sroute) sroutes = RB_INITIALIZER(&sroutes); RB_PROTOTYPE(sroutes, sroute, link, sroute_compare); #define ROUTE_UPDATE_INTERVAL (100 * 60 * 10) /* 10 min */ static uint64_t route_tick; static u_int route_total; /* * Compare two routes */ static int sroute_compare(struct sroute *s1, struct sroute *s2) { return (memcmp(s1->index, s2->index, 13)); } static void sroute_index_append(struct asn_oid *oid, u_int sub, const struct sroute *s) { int i; oid->len = sub + 13; for (i = 0; i < 13; i++) oid->subs[sub + i] = s->index[i]; } #if 0 static void sroute_print(const struct sroute *r) { u_int i; for (i = 0; i < 13 - 1; i++) printf("%u.", r->index[i]); printf("%u proto=%u type=%u", r->index[i], r->proto, r->type); } #endif /* * process routing message */ void mib_sroute_process(struct rt_msghdr *rtm, struct sockaddr *gw, struct sockaddr *dst, struct sockaddr *mask) { struct sockaddr_in *in_dst, *in_gw; struct in_addr in_mask; struct mibif *ifp; struct sroute key; struct sroute *r, *r1; in_addr_t ha; if (dst == NULL || gw == NULL || dst->sa_family != AF_INET || gw->sa_family != AF_INET) return; in_dst = (struct sockaddr_in *)(void *)dst; in_gw = (struct sockaddr_in *)(void *)gw; if (rtm->rtm_flags & RTF_HOST) in_mask.s_addr = 0xffffffff; else if (mask == NULL || mask->sa_len == 0) in_mask.s_addr = 0; else in_mask = ((struct sockaddr_in *)(void *)mask)->sin_addr; /* build the index */ ha = ntohl(in_dst->sin_addr.s_addr); key.index[0] = (ha >> 24) & 0xff; key.index[1] = (ha >> 16) & 0xff; key.index[2] = (ha >> 8) & 0xff; key.index[3] = (ha >> 0) & 0xff; ha = ntohl(in_mask.s_addr); key.index[4] = (ha >> 24) & 0xff; key.index[5] = (ha >> 16) & 0xff; key.index[6] = (ha >> 8) & 0xff; key.index[7] = (ha >> 0) & 0xff; /* ToS */ key.index[8] = 0; ha = ntohl(in_gw->sin_addr.s_addr); key.index[9] = (ha >> 24) & 0xff; key.index[10] = (ha >> 16) & 0xff; key.index[11] = (ha >> 8) & 0xff; key.index[12] = (ha >> 0) & 0xff; if (rtm->rtm_type == RTM_DELETE) { r = RB_FIND(sroutes, &sroutes, &key); if (r == 0) { #ifdef DEBUG_ROUTE syslog(LOG_WARNING, "%s: DELETE: %u.%u.%u.%u " "%u.%u.%u.%u %u %u.%u.%u.%u not found", __func__, key.index[0], key.index[1], key.index[2], key.index[3], key.index[4], key.index[5], key.index[6], key.index[7], key.index[8], key.index[9], key.index[10], key.index[11], key.index[12]); #endif return; } RB_REMOVE(sroutes, &sroutes, r); free(r); route_total--; #ifdef DEBUG_ROUTE printf("%s: DELETE: %u.%u.%u.%u " "%u.%u.%u.%u %u %u.%u.%u.%u\n", __func__, key.index[0], key.index[1], key.index[2], key.index[3], key.index[4], key.index[5], key.index[6], key.index[7], key.index[8], key.index[9], key.index[10], key.index[11], key.index[12]); #endif return; } /* GET or ADD */ ifp = NULL; if ((ifp = mib_find_if_sys(rtm->rtm_index)) == NULL) { if (rtm->rtm_type == RTM_ADD) { /* make it a get so the kernel fills the index */ mib_send_rtmsg(rtm, gw, dst, mask); return; } mib_iflist_bad = 1; } if ((r = malloc(sizeof(*r))) == NULL) { syslog(LOG_ERR, "%m"); return; } memcpy(r->index, key.index, sizeof(r->index)); r->ifindex = (ifp == NULL) ? 0 : ifp->index; r->type = (rtm->rtm_flags & RTF_REJECT) ? 2 : 4; /* cannot really know, what protocol it runs */ r->proto = (rtm->rtm_flags & RTF_LOCAL) ? 2 : (rtm->rtm_flags & RTF_STATIC) ? 3 : (rtm->rtm_flags & RTF_DYNAMIC) ? 4 : 10; r1 = RB_INSERT(sroutes, &sroutes, r); if (r1 != NULL) { #ifdef DEBUG_ROUTE syslog(LOG_WARNING, "%s: %u.%u.%u.%u " "%u.%u.%u.%u %u %u.%u.%u.%u duplicate route", __func__, key.index[0], key.index[1], key.index[2], key.index[3], key.index[4], key.index[5], key.index[6], key.index[7], key.index[8], key.index[9], key.index[10], key.index[11], key.index[12]); #endif r1->ifindex = r->ifindex; r1->type = r->type; r1->proto = r->proto; free(r); return; } route_total++; #ifdef DEBUG_ROUTE printf("%s: ADD/GET: %u.%u.%u.%u " "%u.%u.%u.%u %u %u.%u.%u.%u\n", __func__, key.index[0], key.index[1], key.index[2], key.index[3], key.index[4], key.index[5], key.index[6], key.index[7], key.index[8], key.index[9], key.index[10], key.index[11], key.index[12]); #endif } int mib_fetch_route(void) { u_char *rtab, *next; size_t len; struct sroute *r, *r1; struct rt_msghdr *rtm; struct sockaddr *addrs[RTAX_MAX]; if (route_tick != 0 && route_tick + ROUTE_UPDATE_INTERVAL > this_tick) return (0); /* * Remove all routes */ r = RB_MIN(sroutes, &sroutes); while (r != NULL) { r1 = RB_NEXT(sroutes, &sroutes, r); RB_REMOVE(sroutes, &sroutes, r); free(r); r = r1; } route_total = 0; if ((rtab = mib_fetch_rtab(AF_INET, NET_RT_DUMP, 0, &len)) == NULL) return (-1); next = rtab; for (next = rtab; next < rtab + len; next += rtm->rtm_msglen) { rtm = (struct rt_msghdr *)(void *)next; if (rtm->rtm_type != RTM_GET || !(rtm->rtm_flags & RTF_UP)) continue; mib_extract_addrs(rtm->rtm_addrs, (u_char *)(rtm + 1), addrs); mib_sroute_process(rtm, addrs[RTAX_GATEWAY], addrs[RTAX_DST], addrs[RTAX_NETMASK]); } #if 0 u_int n = 0; r = RB_MIN(sroutes, &sroutes); while (r != NULL) { printf("%u: ", n++); sroute_print(r); printf("\n"); r = RB_NEXT(sroutes, &sroutes, r); } #endif free(rtab); route_tick = get_ticks(); return (0); } /** * Find a route in the table. */ static struct sroute * sroute_get(const struct asn_oid *oid, u_int sub) { struct sroute key; int i; if (oid->len - sub != 13) return (NULL); for (i = 0; i < 13; i++) key.index[i] = oid->subs[sub + i]; return (RB_FIND(sroutes, &sroutes, &key)); } /** * Find next route in the table. There is no such RB_ macro, so must * dig into the innards of the RB stuff. */ static struct sroute * sroute_getnext(struct asn_oid *oid, u_int sub) { u_int i; int comp; struct sroute key; struct sroute *best; struct sroute *s; /* * We now, that the OID is at least the tableEntry OID. If it is, * the user wants the first route. */ if (oid->len == sub) return (RB_MIN(sroutes, &sroutes)); /* * This is also true for any index that consists of zeros and is * shorter than the full index. */ if (oid->len < sub + 13) { for (i = sub; i < oid->len; i++) if (oid->subs[i] != 0) break; if (i == oid->len) return (RB_MIN(sroutes, &sroutes)); /* * Now if the index is too short, we fill it with zeros and then * subtract one from the index. We can do this, because we now, * that there is at least one index element that is not zero. */ for (i = oid->len; i < sub + 13; i++) oid->subs[i] = 0; for (i = sub + 13 - 1; i >= sub; i--) { if (oid->subs[i] != 0) { oid->subs[i]--; break; } oid->subs[i] = ASN_MAXID; } oid->len = sub + 13; } /* build the index */ for (i = sub; i < sub + 13; i++) key.index[i - sub] = oid->subs[i]; /* now find the element */ best = NULL; s = RB_ROOT(&sroutes); while (s != NULL) { comp = sroute_compare(&key, s); if (comp >= 0) { /* The current element is smaller than what we search. * Forget about it and move to the right subtree. */ s = RB_RIGHT(s, link); continue; } /* the current element is larger than what we search. * forget about the right subtree (its even larger), but * the current element may be what we need. */ if (best == NULL || sroute_compare(s, best) < 0) /* this one's better */ best = s; s = RB_LEFT(s, link); } return (best); } /* * Table */ int op_route_table(struct snmp_context *ctx __unused, struct snmp_value *value, u_int sub, u_int iidx __unused, enum snmp_op op) { struct sroute *r; if (mib_fetch_route() == -1) return (SNMP_ERR_GENERR); switch (op) { case SNMP_OP_GETNEXT: if ((r = sroute_getnext(&value->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); sroute_index_append(&value->var, sub, r); break; case SNMP_OP_GET: if ((r = sroute_get(&value->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_SET: if ((r = sroute_get(&value->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); return (SNMP_ERR_NOT_WRITEABLE); case SNMP_OP_ROLLBACK: case SNMP_OP_COMMIT: abort(); default: abort(); } switch (value->var.subs[sub - 1]) { case LEAF_ipCidrRouteDest: value->v.ipaddress[0] = r->index[0]; value->v.ipaddress[1] = r->index[1]; value->v.ipaddress[2] = r->index[2]; value->v.ipaddress[3] = r->index[3]; break; case LEAF_ipCidrRouteMask: value->v.ipaddress[0] = r->index[4]; value->v.ipaddress[1] = r->index[5]; value->v.ipaddress[2] = r->index[6]; value->v.ipaddress[3] = r->index[7]; break; case LEAF_ipCidrRouteTos: value->v.integer = r->index[8]; break; case LEAF_ipCidrRouteNextHop: value->v.ipaddress[0] = r->index[9]; value->v.ipaddress[1] = r->index[10]; value->v.ipaddress[2] = r->index[11]; value->v.ipaddress[3] = r->index[12]; break; case LEAF_ipCidrRouteIfIndex: value->v.integer = r->ifindex; break; case LEAF_ipCidrRouteType: value->v.integer = r->type; break; case LEAF_ipCidrRouteProto: value->v.integer = r->proto; break; case LEAF_ipCidrRouteAge: value->v.integer = 0; break; case LEAF_ipCidrRouteInfo: value->v.oid = oid_zeroDotZero; break; case LEAF_ipCidrRouteNextHopAS: value->v.integer = 0; break; case LEAF_ipCidrRouteMetric1: case LEAF_ipCidrRouteMetric2: case LEAF_ipCidrRouteMetric3: case LEAF_ipCidrRouteMetric4: case LEAF_ipCidrRouteMetric5: value->v.integer = -1; break; case LEAF_ipCidrRouteStatus: value->v.integer = 1; break; } return (SNMP_ERR_NOERROR); } /* * scalars */ int op_route(struct snmp_context *ctx __unused, struct snmp_value *value, u_int sub, u_int iidx __unused, enum snmp_op op) { switch (op) { case SNMP_OP_GETNEXT: abort(); case SNMP_OP_GET: break; case SNMP_OP_SET: return (SNMP_ERR_NOT_WRITEABLE); case SNMP_OP_ROLLBACK: case SNMP_OP_COMMIT: abort(); } if (mib_fetch_route() == -1) return (SNMP_ERR_GENERR); switch (value->var.subs[sub - 1]) { case LEAF_ipCidrRouteNumber: value->v.uint32 = route_total; break; } return (SNMP_ERR_NOERROR); } RB_GENERATE(sroutes, sroute, link, sroute_compare);