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.6 2005/05/23 09:03:41 brandt_h Exp $
30  *
31  * Routing table
32  */
33 #include "mibII.h"
34 #include "mibII_oid.h"
35 
36 struct sroute {
37 	TAILQ_ENTRY(sroute) link;
38 	struct asn_oid	index;
39 	u_int		ifindex;
40 	u_int		type;
41 	u_int		proto;
42 };
43 static TAILQ_HEAD(, sroute) sroute_list = TAILQ_HEAD_INITIALIZER(sroute_list);
44 
45 static uint64_t route_tick;
46 static u_int route_total;
47 
48 static int
49 fetch_route(void)
50 {
51 	u_char *rtab, *next;
52 	size_t len;
53 	struct sroute *r;
54 	struct rt_msghdr *rtm;
55 	struct sockaddr *addrs[RTAX_MAX];
56 	struct sockaddr_in *sa, *gw;
57 	struct in_addr mask, nhop;
58 	in_addr_t ha;
59 	struct mibif *ifp;
60 
61 	while ((r = TAILQ_FIRST(&sroute_list)) != NULL) {
62 		TAILQ_REMOVE(&sroute_list, r, link);
63 		free(r);
64 	}
65 	route_total = 0;
66 
67 	if ((rtab = mib_fetch_rtab(AF_INET, NET_RT_DUMP, 0, &len)) == NULL)
68 		return (-1);
69 
70 	next = rtab;
71 	for (next = rtab; next < rtab + len; next += rtm->rtm_msglen) {
72 		rtm = (struct rt_msghdr *)(void *)next;
73 		if (rtm->rtm_type != RTM_GET ||
74 		    !(rtm->rtm_flags & RTF_UP))
75 			continue;
76 		mib_extract_addrs(rtm->rtm_addrs, (u_char *)(rtm + 1), addrs);
77 
78 		if (addrs[RTAX_DST] == NULL || addrs[RTAX_GATEWAY] == NULL ||
79 		    addrs[RTAX_DST]->sa_family != AF_INET)
80 			continue;
81 
82 		sa = (struct sockaddr_in *)(void *)addrs[RTAX_DST];
83 
84 		if (rtm->rtm_flags & RTF_HOST) {
85 			mask.s_addr = 0xffffffff;
86 		} else {
87 			if (addrs[RTAX_NETMASK] == NULL ||
88 			    addrs[RTAX_NETMASK]->sa_len == 0)
89 				mask.s_addr = 0;
90 			else
91 				mask = ((struct sockaddr_in *)(void *)
92 				    addrs[RTAX_NETMASK])->sin_addr;
93 		}
94 		if (addrs[RTAX_GATEWAY] == NULL) {
95 			nhop.s_addr = 0;
96 		} else if (rtm->rtm_flags & RTF_LLINFO) {
97 			nhop = sa->sin_addr;
98 		} else {
99 			gw = (struct sockaddr_in *)(void *)addrs[RTAX_GATEWAY];
100 			if (gw->sin_family != AF_INET)
101 				continue;
102 			nhop = gw->sin_addr;
103 		}
104 		if ((ifp = mib_find_if_sys(rtm->rtm_index)) == NULL) {
105 			mib_iflist_bad = 1;
106 			continue;
107 		}
108 
109 		if ((r = malloc(sizeof(*r))) == NULL) {
110 			syslog(LOG_ERR, "%m");
111 			continue;
112 		}
113 
114 		route_total++;
115 
116 		r->index.len = 13;
117 		ha = ntohl(sa->sin_addr.s_addr);
118 		r->index.subs[0] = (ha >> 24) & 0xff;
119 		r->index.subs[1] = (ha >> 16) & 0xff;
120 		r->index.subs[2] = (ha >>  8) & 0xff;
121 		r->index.subs[3] = (ha >>  0) & 0xff;
122 		ha = ntohl(mask.s_addr);
123 		r->index.subs[4] = (ha >> 24) & 0xff;
124 		r->index.subs[5] = (ha >> 16) & 0xff;
125 		r->index.subs[6] = (ha >>  8) & 0xff;
126 		r->index.subs[7] = (ha >>  0) & 0xff;
127 
128 		r->index.subs[8] = 0;
129 
130 		ha = ntohl(nhop.s_addr);
131 		r->index.subs[9] = (ha >> 24) & 0xff;
132 		r->index.subs[10] = (ha >> 16) & 0xff;
133 		r->index.subs[11] = (ha >>  8) & 0xff;
134 		r->index.subs[12] = (ha >>  0) & 0xff;
135 
136 		r->ifindex = ifp->index;
137 
138 		r->type = (rtm->rtm_flags & RTF_LLINFO) ? 3 :
139 		    (rtm->rtm_flags & RTF_REJECT) ? 2 : 4;
140 
141 		/* cannot really know, what protocol it runs */
142 		r->proto = (rtm->rtm_flags & RTF_LOCAL) ? 2 :
143 		    (rtm->rtm_flags & RTF_STATIC) ? 3 :
144 		    (rtm->rtm_flags & RTF_DYNAMIC) ? 4 : 10;
145 
146 		INSERT_OBJECT_OID(r, &sroute_list);
147 	}
148 
149 	free(rtab);
150 	route_tick = get_ticks();
151 
152 	return (0);
153 }
154 
155 /*
156  * Table
157  */
158 int
159 op_route_table(struct snmp_context *ctx __unused, struct snmp_value *value,
160     u_int sub, u_int iidx __unused, enum snmp_op op)
161 {
162 	static struct sroute *r;
163 
164 	if (route_tick < this_tick)
165 		if (fetch_route() == -1)
166 			return (SNMP_ERR_GENERR);
167 
168 	switch (op) {
169 
170 	  case SNMP_OP_GETNEXT:
171 		if ((r = NEXT_OBJECT_OID(&sroute_list, &value->var, sub)) == NULL)
172 			return (SNMP_ERR_NOSUCHNAME);
173 		index_append(&value->var, sub, &r->index);
174 		break;
175 
176 	  case SNMP_OP_GET:
177 		if ((r = FIND_OBJECT_OID(&sroute_list, &value->var, sub)) == NULL)
178 			return (SNMP_ERR_NOSUCHNAME);
179 		break;
180 
181 	  case SNMP_OP_SET:
182 		if ((r = FIND_OBJECT_OID(&sroute_list, &value->var, sub)) == NULL)
183 			return (SNMP_ERR_NO_CREATION);
184 		return (SNMP_ERR_NOT_WRITEABLE);
185 
186 	  case SNMP_OP_ROLLBACK:
187 	  case SNMP_OP_COMMIT:
188 		abort();
189 
190 	  default:
191 		abort();
192 	}
193 
194 	switch (value->var.subs[sub - 1]) {
195 
196 	  case LEAF_ipCidrRouteDest:
197 		value->v.ipaddress[0] = r->index.subs[0];
198 		value->v.ipaddress[1] = r->index.subs[1];
199 		value->v.ipaddress[2] = r->index.subs[2];
200 		value->v.ipaddress[3] = r->index.subs[3];
201 		break;
202 
203 	  case LEAF_ipCidrRouteMask:
204 		value->v.ipaddress[0] = r->index.subs[4];
205 		value->v.ipaddress[1] = r->index.subs[5];
206 		value->v.ipaddress[2] = r->index.subs[6];
207 		value->v.ipaddress[3] = r->index.subs[7];
208 		break;
209 
210 	  case LEAF_ipCidrRouteTos:
211 		value->v.integer = r->index.subs[8];
212 		break;
213 
214 	  case LEAF_ipCidrRouteNextHop:
215 		value->v.ipaddress[0] = r->index.subs[9];
216 		value->v.ipaddress[1] = r->index.subs[10];
217 		value->v.ipaddress[2] = r->index.subs[11];
218 		value->v.ipaddress[3] = r->index.subs[12];
219 		break;
220 
221 	  case LEAF_ipCidrRouteIfIndex:
222 		value->v.integer = r->ifindex;
223 		break;
224 
225 	  case LEAF_ipCidrRouteType:
226 		value->v.integer = r->type;
227 		break;
228 
229 	  case LEAF_ipCidrRouteProto:
230 		value->v.integer = r->proto;
231 		break;
232 
233 	  case LEAF_ipCidrRouteAge:
234 		value->v.integer = 0;
235 		break;
236 
237 	  case LEAF_ipCidrRouteInfo:
238 		value->v.oid = oid_zeroDotZero;
239 		break;
240 
241 	  case LEAF_ipCidrRouteNextHopAS:
242 		value->v.integer = 0;
243 		break;
244 
245 	  case LEAF_ipCidrRouteMetric1:
246 	  case LEAF_ipCidrRouteMetric2:
247 	  case LEAF_ipCidrRouteMetric3:
248 	  case LEAF_ipCidrRouteMetric4:
249 	  case LEAF_ipCidrRouteMetric5:
250 		value->v.integer = -1;
251 		break;
252 
253 	  case LEAF_ipCidrRouteStatus:
254 		value->v.integer = 1;
255 		break;
256 	}
257 	return (SNMP_ERR_NOERROR);
258 }
259 
260 /*
261  * scalars
262  */
263 int
264 op_route(struct snmp_context *ctx __unused, struct snmp_value *value,
265     u_int sub, u_int iidx __unused, enum snmp_op op)
266 {
267 	switch (op) {
268 
269 	  case SNMP_OP_GETNEXT:
270 		abort();
271 
272 	  case SNMP_OP_GET:
273 		break;
274 
275 	  case SNMP_OP_SET:
276 		return (SNMP_ERR_NOT_WRITEABLE);
277 
278 	  case SNMP_OP_ROLLBACK:
279 	  case SNMP_OP_COMMIT:
280 		abort();
281 	}
282 
283 	if (route_tick < this_tick)
284 		if (fetch_route() == -1)
285 			return (SNMP_ERR_GENERR);
286 
287 	switch (value->var.subs[sub - 1]) {
288 
289 	  case LEAF_ipCidrRouteNumber:
290 		value->v.uint32 = route_total;
291 		break;
292 
293 	}
294 	return (SNMP_ERR_NOERROR);
295 }
296