]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/bsnmp/snmp_mibII/mibII_route.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / bsnmp / snmp_mibII / mibII_route.c
1 /*
2  * Copyright (c) 2001-2003
3  *      Fraunhofer Institute for Open Communication Systems (FhG Fokus).
4  *      All rights reserved.
5  *
6  * Author: Harti Brandt <harti@freebsd.org>
7  * 
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 
17  * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * $Begemot: bsnmp/snmp_mibII/mibII_route.c,v 1.9 2005/10/06 07:15:00 brandt_h Exp $
30  *
31  * Routing table
32  */
33
34 #ifdef HAVE_SYS_TREE_H
35 #include <sys/tree.h>
36 #else
37 #include "tree.h"
38 #endif
39
40 #include "mibII.h"
41 #include "mibII_oid.h"
42
43 struct sroute {
44         RB_ENTRY(sroute) link;
45         uint32_t        ifindex;
46         uint8_t         index[13];
47         uint8_t         type;
48         uint8_t         proto;
49 };
50 RB_HEAD(sroutes, sroute) sroutes = RB_INITIALIZER(&sroutes);
51
52 RB_PROTOTYPE(sroutes, sroute, link, sroute_compare);
53
54 #define ROUTE_UPDATE_INTERVAL   (100 * 60 * 10) /* 10 min */
55 static uint64_t route_tick;
56 static u_int route_total;
57
58 /*
59  * Compare two routes
60  */
61 static int
62 sroute_compare(struct sroute *s1, struct sroute *s2)
63 {
64
65         return (memcmp(s1->index, s2->index, 13));
66 }
67
68 static void
69 sroute_index_append(struct asn_oid *oid, u_int sub, const struct sroute *s)
70 {
71         int i;
72
73         oid->len = sub + 13;
74         for (i = 0; i < 13; i++)
75                 oid->subs[sub + i] = s->index[i];
76 }
77
78 #if 0
79 static void
80 sroute_print(const struct sroute *r)
81 {
82         u_int i;
83
84         for (i = 0; i < 13 - 1; i++)
85                 printf("%u.", r->index[i]);
86         printf("%u proto=%u type=%u", r->index[i], r->proto, r->type);
87 }
88 #endif
89
90 /*
91  * process routing message
92  */
93 void
94 mib_sroute_process(struct rt_msghdr *rtm, struct sockaddr *gw,
95     struct sockaddr *dst, struct sockaddr *mask)
96 {
97         struct sockaddr_in *in_dst, *in_gw;
98         struct in_addr in_mask;
99         struct mibif *ifp;
100         struct sroute key;
101         struct sroute *r, *r1;
102         in_addr_t ha;
103
104         if (dst == NULL || gw == NULL || dst->sa_family != AF_INET ||
105             gw->sa_family != AF_INET)
106                 return;
107
108         in_dst = (struct sockaddr_in *)(void *)dst;
109         in_gw = (struct sockaddr_in *)(void *)gw;
110
111         if (rtm->rtm_flags & RTF_HOST)
112                 in_mask.s_addr = 0xffffffff;
113         else if (mask == NULL || mask->sa_len == 0)
114                 in_mask.s_addr = 0;
115         else
116                 in_mask = ((struct sockaddr_in *)(void *)mask)->sin_addr;
117
118         /* build the index */
119         ha = ntohl(in_dst->sin_addr.s_addr);
120         key.index[0] = (ha >> 24) & 0xff;
121         key.index[1] = (ha >> 16) & 0xff;
122         key.index[2] = (ha >>  8) & 0xff;
123         key.index[3] = (ha >>  0) & 0xff;
124
125         ha = ntohl(in_mask.s_addr);
126         key.index[4] = (ha >> 24) & 0xff;
127         key.index[5] = (ha >> 16) & 0xff;
128         key.index[6] = (ha >>  8) & 0xff;
129         key.index[7] = (ha >>  0) & 0xff;
130
131         /* ToS */
132         key.index[8] = 0;
133
134         ha = ntohl(in_gw->sin_addr.s_addr);
135         key.index[9] = (ha >> 24) & 0xff;
136         key.index[10] = (ha >> 16) & 0xff;
137         key.index[11] = (ha >>  8) & 0xff;
138         key.index[12] = (ha >>  0) & 0xff;
139
140         if (rtm->rtm_type == RTM_DELETE) {
141                 r = RB_FIND(sroutes, &sroutes, &key);
142                 if (r == 0) {
143 #ifdef DEBUG_ROUTE
144                         syslog(LOG_WARNING, "%s: DELETE: %u.%u.%u.%u "
145                             "%u.%u.%u.%u %u %u.%u.%u.%u not found", __func__,
146                             key.index[0], key.index[1], key.index[2],
147                             key.index[3], key.index[4], key.index[5],
148                             key.index[6], key.index[7], key.index[8],
149                             key.index[9], key.index[10], key.index[11],
150                             key.index[12]);
151 #endif
152                         return;
153                 }
154                 RB_REMOVE(sroutes, &sroutes, r);
155                 free(r);
156                 route_total--;
157 #ifdef DEBUG_ROUTE
158                 printf("%s: DELETE: %u.%u.%u.%u "
159                     "%u.%u.%u.%u %u %u.%u.%u.%u\n", __func__,
160                     key.index[0], key.index[1], key.index[2],
161                     key.index[3], key.index[4], key.index[5],
162                     key.index[6], key.index[7], key.index[8],
163                     key.index[9], key.index[10], key.index[11],
164                     key.index[12]);
165 #endif
166                 return;
167         }
168
169         /* GET or ADD */
170         ifp = NULL;
171         if ((ifp = mib_find_if_sys(rtm->rtm_index)) == NULL) {
172                 if (rtm->rtm_type == RTM_ADD) {
173                         /* make it a get so the kernel fills the index */
174                         mib_send_rtmsg(rtm, gw, dst, mask);
175                         return;
176                 }
177                 mib_iflist_bad = 1;
178         }
179
180         if ((r = malloc(sizeof(*r))) == NULL) {
181                 syslog(LOG_ERR, "%m");
182                 return;
183         }
184
185         memcpy(r->index, key.index, sizeof(r->index));
186         r->ifindex = (ifp == NULL) ? 0 : ifp->index;
187
188         r->type = (rtm->rtm_flags & RTF_REJECT) ? 2 : 4;
189
190         /* cannot really know, what protocol it runs */
191         r->proto = (rtm->rtm_flags & RTF_LOCAL) ? 2 :
192             (rtm->rtm_flags & RTF_STATIC) ? 3 :
193             (rtm->rtm_flags & RTF_DYNAMIC) ? 4 : 10;
194
195         r1 = RB_INSERT(sroutes, &sroutes, r);
196         if (r1 != NULL) {
197 #ifdef DEBUG_ROUTE
198                 syslog(LOG_WARNING, "%s: %u.%u.%u.%u "
199                     "%u.%u.%u.%u %u %u.%u.%u.%u duplicate route", __func__,
200                     key.index[0], key.index[1], key.index[2],
201                     key.index[3], key.index[4], key.index[5],
202                     key.index[6], key.index[7], key.index[8],
203                     key.index[9], key.index[10], key.index[11],
204                     key.index[12]);
205 #endif
206                 r1->ifindex = r->ifindex;
207                 r1->type = r->type;
208                 r1->proto = r->proto;
209                 free(r);
210                 return;
211         }
212
213         route_total++;
214 #ifdef DEBUG_ROUTE
215         printf("%s: ADD/GET: %u.%u.%u.%u "
216             "%u.%u.%u.%u %u %u.%u.%u.%u\n", __func__,
217             key.index[0], key.index[1], key.index[2],
218             key.index[3], key.index[4], key.index[5],
219             key.index[6], key.index[7], key.index[8],
220             key.index[9], key.index[10], key.index[11],
221             key.index[12]);
222 #endif
223 }
224
225 int
226 mib_fetch_route(void)
227 {
228         u_char *rtab, *next;
229         size_t len;
230         struct sroute *r, *r1;
231         struct rt_msghdr *rtm;
232         struct sockaddr *addrs[RTAX_MAX];
233
234         if (route_tick != 0 && route_tick + ROUTE_UPDATE_INTERVAL > this_tick)
235                 return (0);
236
237         /*
238          * Remove all routes
239          */
240         r = RB_MIN(sroutes, &sroutes);
241         while (r != NULL) {
242                 r1 = RB_NEXT(sroutes, &sroutes, r);
243                 RB_REMOVE(sroutes, &sroutes, r);
244                 free(r);
245                 r = r1;
246         }
247         route_total = 0;
248
249         if ((rtab = mib_fetch_rtab(AF_INET, NET_RT_DUMP, 0, &len)) == NULL)
250                 return (-1);
251
252         next = rtab;
253         for (next = rtab; next < rtab + len; next += rtm->rtm_msglen) {
254                 rtm = (struct rt_msghdr *)(void *)next;
255                 if (rtm->rtm_type != RTM_GET ||
256                     !(rtm->rtm_flags & RTF_UP))
257                         continue;
258                 mib_extract_addrs(rtm->rtm_addrs, (u_char *)(rtm + 1), addrs);
259
260                 
261                 mib_sroute_process(rtm, addrs[RTAX_GATEWAY], addrs[RTAX_DST],
262                     addrs[RTAX_NETMASK]);
263         }
264
265 #if 0
266         u_int n = 0;
267         r = RB_MIN(sroutes, &sroutes);
268         while (r != NULL) {
269                 printf("%u: ", n++);
270                 sroute_print(r);
271                 printf("\n");
272                 r = RB_NEXT(sroutes, &sroutes, r);
273         }
274 #endif
275         free(rtab);
276         route_tick = get_ticks();
277
278         return (0);
279 }
280
281 /**
282  * Find a route in the table.
283  */
284 static struct sroute *
285 sroute_get(const struct asn_oid *oid, u_int sub)
286 {
287         struct sroute key;
288         int i;
289
290         if (oid->len - sub != 13)
291                 return (NULL);
292         for (i = 0; i < 13; i++)
293                 key.index[i] = oid->subs[sub + i];
294         return (RB_FIND(sroutes, &sroutes, &key));
295 }
296
297 /**
298  * Find next route in the table. There is no such RB_ macro, so must
299  * dig into the innards of the RB stuff.
300  */
301 static struct sroute *
302 sroute_getnext(struct asn_oid *oid, u_int sub)
303 {
304         u_int i;
305         int comp;
306         struct sroute key;
307         struct sroute *best;
308         struct sroute *s;
309
310         /*
311          * We now, that the OID is at least the tableEntry OID. If it is,
312          * the user wants the first route.
313          */
314         if (oid->len == sub)
315                 return (RB_MIN(sroutes, &sroutes));
316
317         /*
318          * This is also true for any index that consists of zeros and is
319          * shorter than the full index.
320          */
321         if (oid->len < sub + 13) {
322                 for (i = sub; i < oid->len; i++)
323                         if (oid->subs[i] != 0)
324                                 break;
325                 if (i == oid->len)
326                         return (RB_MIN(sroutes, &sroutes));
327
328                 /*
329                  * Now if the index is too short, we fill it with zeros and then
330                  * subtract one from the index. We can do this, because we now,
331                  * that there is at least one index element that is not zero.
332                  */
333                 for (i = oid->len; i < sub + 13; i++)
334                         oid->subs[i] = 0;
335
336                 for (i = sub + 13 - 1; i >= sub; i--) {
337                         if (oid->subs[i] != 0) {
338                                 oid->subs[i]--;
339                                 break;
340                         }
341                         oid->subs[i] = ASN_MAXID;
342                 }
343                 oid->len = sub + 13;
344         }
345
346         /* build the index */
347         for (i = sub; i < sub + 13; i++)
348                 key.index[i - sub] = oid->subs[i];
349
350         /* now find the element */
351         best = NULL;
352         s = RB_ROOT(&sroutes);
353
354         while (s != NULL) {
355                 comp = sroute_compare(&key, s);
356                 if (comp >= 0) {
357                         /* The current element is smaller than what we search.
358                          * Forget about it and move to the right subtree. */
359                         s = RB_RIGHT(s, link);
360                         continue;
361                 }
362                 /* the current element is larger than what we search.
363                  * forget about the right subtree (its even larger), but
364                  * the current element may be what we need. */
365                 if (best == NULL || sroute_compare(s, best) < 0)
366                         /* this one's better */
367                         best = s;
368
369                 s = RB_LEFT(s, link);
370         }
371         return (best);
372 }
373
374 /*
375  * Table
376  */
377 int
378 op_route_table(struct snmp_context *ctx __unused, struct snmp_value *value,
379     u_int sub, u_int iidx __unused, enum snmp_op op)
380 {
381         struct sroute *r;
382
383         if (mib_fetch_route() == -1)
384                 return (SNMP_ERR_GENERR);
385
386         switch (op) {
387
388           case SNMP_OP_GETNEXT:
389                 if ((r = sroute_getnext(&value->var, sub)) == NULL)
390                         return (SNMP_ERR_NOSUCHNAME);
391                 sroute_index_append(&value->var, sub, r);
392                 break;
393
394           case SNMP_OP_GET:
395                 if ((r = sroute_get(&value->var, sub)) == NULL)
396                         return (SNMP_ERR_NOSUCHNAME);
397                 break;
398
399           case SNMP_OP_SET:
400                 if ((r = sroute_get(&value->var, sub)) == NULL)
401                         return (SNMP_ERR_NOSUCHNAME);
402                 return (SNMP_ERR_NOT_WRITEABLE);
403
404           case SNMP_OP_ROLLBACK:
405           case SNMP_OP_COMMIT:
406                 abort();
407
408           default:
409                 abort();
410         }
411
412         switch (value->var.subs[sub - 1]) {
413
414           case LEAF_ipCidrRouteDest:
415                 value->v.ipaddress[0] = r->index[0];
416                 value->v.ipaddress[1] = r->index[1];
417                 value->v.ipaddress[2] = r->index[2];
418                 value->v.ipaddress[3] = r->index[3];
419                 break;
420
421           case LEAF_ipCidrRouteMask:
422                 value->v.ipaddress[0] = r->index[4];
423                 value->v.ipaddress[1] = r->index[5];
424                 value->v.ipaddress[2] = r->index[6];
425                 value->v.ipaddress[3] = r->index[7];
426                 break;
427
428           case LEAF_ipCidrRouteTos:
429                 value->v.integer = r->index[8];
430                 break;
431
432           case LEAF_ipCidrRouteNextHop:
433                 value->v.ipaddress[0] = r->index[9];
434                 value->v.ipaddress[1] = r->index[10];
435                 value->v.ipaddress[2] = r->index[11];
436                 value->v.ipaddress[3] = r->index[12];
437                 break;
438
439           case LEAF_ipCidrRouteIfIndex:
440                 value->v.integer = r->ifindex;
441                 break;
442
443           case LEAF_ipCidrRouteType:
444                 value->v.integer = r->type;
445                 break;
446
447           case LEAF_ipCidrRouteProto:
448                 value->v.integer = r->proto;
449                 break;
450
451           case LEAF_ipCidrRouteAge:
452                 value->v.integer = 0;
453                 break;
454
455           case LEAF_ipCidrRouteInfo:
456                 value->v.oid = oid_zeroDotZero;
457                 break;
458
459           case LEAF_ipCidrRouteNextHopAS:
460                 value->v.integer = 0;
461                 break;
462
463           case LEAF_ipCidrRouteMetric1:
464           case LEAF_ipCidrRouteMetric2:
465           case LEAF_ipCidrRouteMetric3:
466           case LEAF_ipCidrRouteMetric4:
467           case LEAF_ipCidrRouteMetric5:
468                 value->v.integer = -1;
469                 break;
470
471           case LEAF_ipCidrRouteStatus:
472                 value->v.integer = 1;
473                 break;
474         }
475         return (SNMP_ERR_NOERROR);
476 }
477
478 /*
479  * scalars
480  */
481 int
482 op_route(struct snmp_context *ctx __unused, struct snmp_value *value,
483     u_int sub, u_int iidx __unused, enum snmp_op op)
484 {
485         switch (op) {
486
487           case SNMP_OP_GETNEXT:
488                 abort();
489
490           case SNMP_OP_GET:
491                 break;
492
493           case SNMP_OP_SET:
494                 return (SNMP_ERR_NOT_WRITEABLE);
495
496           case SNMP_OP_ROLLBACK:
497           case SNMP_OP_COMMIT:
498                 abort();
499         }
500
501         if (mib_fetch_route() == -1)
502                 return (SNMP_ERR_GENERR);
503
504         switch (value->var.subs[sub - 1]) {
505
506           case LEAF_ipCidrRouteNumber:
507                 value->v.uint32 = route_total;
508                 break;
509
510         }
511         return (SNMP_ERR_NOERROR);
512 }
513
514 RB_GENERATE(sroutes, sroute, link, sroute_compare);