1 /*-
2  * Copyright (c) 2006 Shteryana Shopova <syrinx@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * Bridge MIB implementation for SNMPd.
27  *
28  * $FreeBSD$
29  */
30 
31 #include <sys/param.h>
32 #include <sys/queue.h>
33 #include <sys/socket.h>
34 #include <sys/types.h>
35 
36 #include <net/ethernet.h>
37 #include <net/if.h>
38 #include <net/if_mib.h>
39 #include <net/if_types.h>
40 
41 #include <errno.h>
42 #include <stdarg.h>
43 #include <stdlib.h>
44 #include <stdio.h>
45 #include <string.h>
46 #include <syslog.h>
47 
48 #include <bsnmp/snmpmod.h>
49 #include <bsnmp/snmp_mibII.h>
50 
51 #include "bridge_tree.h"
52 #include "bridge_snmp.h"
53 #include "bridge_oid.h"
54 
55 static struct lmodule *bridge_module;
56 
57 /* For the registration. */
58 static const struct asn_oid oid_dot1Bridge = OIDX_dot1dBridge;
59 /* The registration. */
60 static uint reg_bridge;
61 
62 /* Periodic timer for polling all bridges' data. */
63 static void *bridge_data_timer;
64 static void *bridge_tc_timer;
65 
66 static int bridge_data_maxage = SNMP_BRIDGE_DATA_MAXAGE;
67 static int bridge_poll_ticks = SNMP_BRIDGE_POLL_INTERVAL * 100;
68 static int bridge_tc_poll_ticks = SNMP_BRIDGE_TC_POLL_INTERVAL * 100;
69 
70 /*
71  * Our default bridge, whose info will be visible under
72  * the dot1dBridge subtree and functions to set/fetch it.
73  */
74 static char bif_default_name[IFNAMSIZ] = "bridge0";
75 static struct bridge_if *bif_default;
76 
77 struct bridge_if *
78 bridge_get_default(void)
79 {
80 	struct mibif *ifp;
81 
82 	if (bif_default != NULL) {
83 
84 		/* Walk through the mibII interface list. */
85 		for (ifp = mib_first_if(); ifp != NULL; ifp = mib_next_if(ifp))
86 			if (strcmp(ifp->name, bif_default->bif_name) == 0)
87 				break;
88 
89 		if (ifp == NULL)
90 			bif_default = NULL;
91 	}
92 
93 	return (bif_default);
94 }
95 
96 void
97 bridge_set_default(struct bridge_if *bif)
98 {
99 	bif_default = bif;
100 
101 	syslog(LOG_ERR, "Set default bridge interface to: %s",
102 	    bif == NULL ? "(none)" : bif->bif_name);
103 }
104 
105 const char *
106 bridge_get_default_name(void)
107 {
108 	return (bif_default_name);
109 }
110 
111 static int
112 bridge_set_default_name(const char *bif_name, uint len)
113 {
114 	struct bridge_if *bif;
115 
116 	if (len >= IFNAMSIZ)
117 		return (-1);
118 
119 	bcopy(bif_name, bif_default_name, len);
120 	bif_default_name[len] = '\0';
121 
122 	if ((bif = bridge_if_find_ifname(bif_default_name)) == NULL) {
123 		bif_default = NULL;
124 		return (0);
125 	}
126 
127 	bif_default = bif;
128 	return (1);
129 }
130 
131 int
132 bridge_get_data_maxage(void)
133 {
134 	return (bridge_data_maxage);
135 }
136 
137 static void
138 bridge_set_poll_ticks(int poll_ticks)
139 {
140 	if (bridge_data_timer != NULL)
141 		timer_stop(bridge_data_timer);
142 
143 	bridge_poll_ticks = poll_ticks;
144 	bridge_data_timer = timer_start_repeat(bridge_poll_ticks,
145 	    bridge_poll_ticks, bridge_update_all, NULL, bridge_module);
146 }
147 /*
148  * The bridge module configuration via SNMP.
149  */
150 static int
151 bridge_default_name_save(struct snmp_context *ctx, const char *bridge_default)
152 {
153 	if ((ctx->scratch->int1 = strlen(bridge_default)) >= IFNAMSIZ)
154 		return (-1);
155 
156 	if ((ctx->scratch->ptr1 = malloc(IFNAMSIZ)) == NULL)
157 		return (-1);
158 
159 	strncpy(ctx->scratch->ptr1, bridge_default, ctx->scratch->int1);
160 	return (0);
161 }
162 
163 int
164 op_begemot_bridge_config(struct snmp_context *ctx, struct snmp_value *val,
165     uint sub, uint iidx __unused, enum snmp_op op)
166 {
167 	switch (op) {
168 	    case SNMP_OP_GET:
169 		switch (val->var.subs[sub - 1]) {
170 		    case LEAF_begemotBridgeDefaultBridgeIf:
171 			return (string_get(val, bridge_get_default_name(), -1));
172 
173 		    case LEAF_begemotBridgeDataUpdate:
174 			val->v.integer = bridge_data_maxage;
175 			return (SNMP_ERR_NOERROR);
176 
177 		    case LEAF_begemotBridgeDataPoll:
178 			val->v.integer = bridge_poll_ticks / 100;
179 			return (SNMP_ERR_NOERROR);
180 		}
181 		abort();
182 
183 	    case SNMP_OP_GETNEXT:
184 		abort();
185 
186 	    case SNMP_OP_SET:
187 		switch (val->var.subs[sub - 1]) {
188 		    case LEAF_begemotBridgeDefaultBridgeIf:
189 			/*
190 			 * Cannot use string_save() here - requires either
191 			 * a fixed-sized or var-length string - not less
192 			 * than or equal.
193 			 */
194 			if (bridge_default_name_save(ctx,
195 			    bridge_get_default_name()) < 0)
196 				return (SNMP_ERR_RES_UNAVAIL);
197 
198 			if (bridge_set_default_name(val->v.octetstring.octets,
199 			    val->v.octetstring.len) < 0)
200 				return (SNMP_ERR_BADVALUE);
201 			return (SNMP_ERR_NOERROR);
202 
203 		    case LEAF_begemotBridgeDataUpdate:
204 			if (val->v.integer < SNMP_BRIDGE_DATA_MAXAGE_MIN ||
205 			    val->v.integer > SNMP_BRIDGE_DATA_MAXAGE_MAX)
206 				return (SNMP_ERR_WRONG_VALUE);
207 			ctx->scratch->int1 = bridge_data_maxage;
208 			bridge_data_maxage = val->v.integer;
209 			return (SNMP_ERR_NOERROR);
210 
211 		    case LEAF_begemotBridgeDataPoll:
212 			if (val->v.integer < SNMP_BRIDGE_POLL_INTERVAL_MIN ||
213 			    val->v.integer > SNMP_BRIDGE_POLL_INTERVAL_MAX)
214 				return (SNMP_ERR_WRONG_VALUE);
215 			ctx->scratch->int1 = val->v.integer;
216 			return (SNMP_ERR_NOERROR);
217 		}
218 		abort();
219 
220 	    case SNMP_OP_ROLLBACK:
221 		switch (val->var.subs[sub - 1]) {
222 		    case LEAF_begemotBridgeDefaultBridgeIf:
223 			bridge_set_default_name(ctx->scratch->ptr1,
224 			    ctx->scratch->int1);
225 			free(ctx->scratch->ptr1);
226 			break;
227 		    case LEAF_begemotBridgeDataUpdate:
228 			bridge_data_maxage = ctx->scratch->int1;
229 			break;
230 		}
231 		return (SNMP_ERR_NOERROR);
232 
233 	    case SNMP_OP_COMMIT:
234 		switch (val->var.subs[sub - 1]) {
235 		    case LEAF_begemotBridgeDefaultBridgeIf:
236 			free(ctx->scratch->ptr1);
237 			break;
238 		    case LEAF_begemotBridgeDataPoll:
239 			bridge_set_poll_ticks(ctx->scratch->int1 * 100);
240 			break;
241 		}
242 		return (SNMP_ERR_NOERROR);
243 	}
244 
245 	abort();
246 }
247 
248 /*
249  * Bridge mib module initialization hook.
250  * Returns 0 on success, < 0 on error.
251  */
252 static int
253 bridge_init(struct lmodule * mod, int argc __unused, char *argv[] __unused)
254 {
255 	bridge_module = mod;
256 
257 	if (bridge_kmod_load() < 0)
258 		return (-1);
259 
260 	if (bridge_ioctl_init() < 0)
261 		return (-1);
262 
263 	/* Register to get creation messages for bridge interfaces. */
264 	if (mib_register_newif(bridge_attach_newif, bridge_module)) {
265 		syslog(LOG_ERR, "Cannot register newif function: %s",
266 		    strerror(errno));
267 		return (-1);
268 	}
269 
270 	return (0);
271 }
272 
273 /*
274  * Bridge mib module finalization hook.
275  */
276 static int
277 bridge_fini(void)
278 {
279 	mib_unregister_newif(bridge_module);
280 	or_unregister(reg_bridge);
281 
282 	if (bridge_data_timer != NULL) {
283 		timer_stop(bridge_data_timer);
284 		bridge_data_timer = NULL;
285 	}
286 
287 	if (bridge_tc_timer != NULL) {
288 		timer_stop(bridge_tc_timer);
289 		bridge_tc_timer = NULL;
290 	}
291 
292 	bridge_ifs_fini();
293 	bridge_ports_fini();
294 	bridge_addrs_fini();
295 
296 	return (0);
297 }
298 
299 /*
300  * Bridge mib module start operation.
301  */
302 static void
303 bridge_start(void)
304 {
305 	reg_bridge = or_register(&oid_dot1Bridge,
306 	    "The IETF MIB for Bridges (RFC 4188).", bridge_module);
307 
308 	bridge_data_timer = timer_start_repeat(bridge_poll_ticks,
309 	    bridge_poll_ticks, bridge_update_all, NULL, bridge_module);
310 
311 	bridge_tc_timer = timer_start_repeat(bridge_tc_poll_ticks,
312 	    bridge_tc_poll_ticks, bridge_update_tc_time, NULL, bridge_module);
313 }
314 
315 static void
316 bridge_dump(void)
317 {
318 	struct bridge_if *bif;
319 
320 	if ((bif = bridge_get_default()) == NULL)
321 		syslog(LOG_ERR, "Dump: no default bridge interface");
322 	else
323 		syslog(LOG_ERR, "Dump: default bridge interface %s",
324 		     bif->bif_name);
325 
326 	bridge_ifs_dump();
327 	bridge_pf_dump();
328 }
329 
330 const struct snmp_module config = {
331 	.comment = "This module implements the bridge mib (RFC 4188).",
332 	.init =		bridge_init,
333 	.fini =		bridge_fini,
334 	.start =	bridge_start,
335 	.tree =		bridge_ctree,
336 	.dump =		bridge_dump,
337 	.tree_size =	bridge_CTREE_SIZE,
338 };
339