xref: /freebsd/contrib/bsnmp/snmp_mibII/mibII_route.c (revision b70d2a2aa5003027b422e62435ab5bb9390d543c)
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);
515