1 /*
2  * Copyright (c) 2001-2002
3  *	Fraunhofer Institute for Open Communication Systems (FhG Fokus).
4  *	All rights reserved.
5  * Copyright (c) 2003-2004
6  *	Hartmut Brandt.
7  *	All rights reserved.
8  *
9  * Author: Hartmut Brandt <[email protected]>
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  * $Begemot: libunimsg/snmp_atm/snmp_atm.c,v 1.3 2005/05/23 11:46:46 brandt_h Exp $
33  *
34  * SNMP module for ATM hardware interfaces.
35  */
36 
37 #include "atm.h"
38 #include "atm_tree.h"
39 #include "atm_oid.h"
40 
41 #include <sys/ioctl.h>
42 
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <errno.h>
47 #include <syslog.h>
48 #include <net/if_types.h>
49 #include <net/if_media.h>
50 #include <net/if_atm.h>
51 
52 struct lmodule *module;
53 
54 /* list of all (known) ATM interfaces */
55 struct atmif_list atmif_list = TAILQ_HEAD_INITIALIZER(atmif_list);
56 
57 /* whether we are started or not */
58 static int started;
59 
60 /* last time table was changed */
61 static uint64_t last_change;
62 
63 /* for the registration */
64 static const struct asn_oid oid_begemotAtm = OIDX_begemotAtm;
65 
66 /* the registration */
67 static u_int reg_atm;
68 
69 /*
70  * Find an ATM interface by name
71  */
72 struct atmif *
atm_find_if_name(const char * name)73 atm_find_if_name(const char *name)
74 {
75 	struct atmif_priv *aif;
76 
77 	TAILQ_FOREACH(aif, &atmif_list, link)
78 		if (strcmp(aif->pub.ifp->name, name) == 0)
79 			return (&aif->pub);
80 	return (NULL);
81 }
82 
83 /*
84  * get the interface from the interface index
85  */
86 struct atmif *
atm_find_if(u_int ifindex)87 atm_find_if(u_int ifindex)
88 {
89 	struct atmif_priv *aif;
90 
91 	TAILQ_FOREACH(aif, &atmif_list, link)
92 		if (aif->index == ifindex)
93 			return (&aif->pub);
94 	return (NULL);
95 }
96 
97 /*
98  * Send notification to all listeners.
99  */
100 void
atmif_send_notification(struct atmif_priv * aif,enum atmif_notify code,uintptr_t arg)101 atmif_send_notification(struct atmif_priv *aif, enum atmif_notify code,
102     uintptr_t arg)
103 {
104 	struct atmif_reg *r0, *r1;
105 
106 	r0 = TAILQ_FIRST(&aif->notify);
107 	while (r0 != NULL) {
108 		r1 = TAILQ_NEXT(r0, link);
109 		r0->func(&aif->pub, code, arg, r0->data);
110 		r0 = r1;
111 	}
112 }
113 
114 /*
115  * Destroy an interface
116  */
117 static void
atmif_destroy(struct atmif_priv * aif)118 atmif_destroy(struct atmif_priv *aif)
119 {
120 	struct atmif_reg *r0;
121 
122 	atmif_send_notification(aif, ATMIF_NOTIFY_DESTROY,
123 	    (uintptr_t)0);
124 
125 	atmif_sys_destroy(aif);
126 
127 	if (aif->ifpreg != NULL)
128 		mibif_unnotify(aif->ifpreg);
129 
130 	while ((r0 = TAILQ_FIRST(&aif->notify)) != NULL) {
131 		TAILQ_REMOVE(&aif->notify, r0, link);
132 		free(r0);
133 	}
134 
135 	TAILQ_REMOVE(&atmif_list, aif, link);
136 	free(aif);
137 
138 	last_change = this_tick;
139 }
140 
141 /*
142  * Function gets called from the MIB-II module for events on that interface
143  */
144 static void
atmif_notify(struct mibif * ifp __unused,enum mibif_notify event,void * data)145 atmif_notify(struct mibif *ifp __unused, enum mibif_notify event, void *data)
146 {
147 	struct atmif_priv *aif = data;
148 
149 	switch (event) {
150 
151 	  case MIBIF_NOTIFY_DESTROY:
152 		atmif_destroy(aif);
153 		break;
154 	}
155 }
156 
157 /*
158  * Check the carrier state of the interface
159  */
160 void
atmif_check_carrier(struct atmif_priv * aif)161 atmif_check_carrier(struct atmif_priv *aif)
162 {
163 	struct ifmediareq ifmr;
164 	enum atmif_carrier_state ost = aif->pub.carrier;
165 
166 	memset(&ifmr, 0, sizeof(ifmr));
167 	strcpy(ifmr.ifm_name, aif->pub.ifp->name);
168 
169 	if (ioctl(mib_netsock, SIOCGIFMEDIA, &ifmr) == -1) {
170 		aif->pub.carrier = ATMIF_CARRIER_UNKNOWN;
171 		return;
172 	}
173 	if (!(ifmr.ifm_status & IFM_AVALID)) {
174 		aif->pub.carrier = ATMIF_CARRIER_UNKNOWN;
175 		return;
176 	}
177 	if (ifmr.ifm_status & IFM_ACTIVE)
178 		aif->pub.carrier = ATMIF_CARRIER_ON;
179 	else
180 		aif->pub.carrier = ATMIF_CARRIER_OFF;
181 
182 	if (ost != aif->pub.carrier)
183 		atmif_send_notification(aif, ATMIF_NOTIFY_CARRIER,
184 		    (uintptr_t)ost);
185 }
186 
187 /*
188  * Retrieve the SUNI mode
189  */
190 static int
atmif_get_mode(struct atmif_priv * aif)191 atmif_get_mode(struct atmif_priv *aif)
192 {
193 	struct ifmediareq ifmr;
194 
195 	memset(&ifmr, 0, sizeof(ifmr));
196 	strcpy(ifmr.ifm_name, aif->pub.ifp->name);
197 
198 	if (ioctl(mib_netsock, SIOCGIFMEDIA, &ifmr) < 0) {
199 		syslog(LOG_ERR, "SIOCGIFMEDIA: %m");
200 		aif->pub.mode = ATMIF_SUNI_MODE_UNKNOWN;
201 		return (SNMP_ERR_GENERR);
202 	}
203 	if (ifmr.ifm_current & IFM_ATM_SDH)
204 		aif->pub.mode = ATMIF_SUNI_MODE_SDH;
205 	else
206 		aif->pub.mode = ATMIF_SUNI_MODE_SONET;
207 
208 	return (SNMP_ERR_NOERROR);
209 }
210 
211 /*
212  * Change the SUNI mod
213  */
214 static int
atmif_set_mode(struct atmif_priv * aif,int newmode)215 atmif_set_mode(struct atmif_priv *aif, int newmode)
216 {
217 	struct ifmediareq ifmr;
218 	struct ifreq ifr;
219 
220 	memset(&ifmr, 0, sizeof(ifmr));
221 	strcpy(ifmr.ifm_name, aif->pub.ifp->name);
222 
223 	/* get current mode */
224 	if (ioctl(mib_netsock, SIOCGIFMEDIA, &ifmr) < 0) {
225 		syslog(LOG_ERR, "SIOCGIFMEDIA: %m");
226 		return (SNMP_ERR_GENERR);
227 	}
228 
229 	memset(&ifr, 0, sizeof(ifr));
230 	strcpy(ifr.ifr_name, aif->pub.ifp->name);
231 
232 	ifr.ifr_media = ifmr.ifm_current;
233 	if (newmode == ATMIF_SUNI_MODE_SDH)
234 		ifr.ifr_media |= IFM_ATM_SDH;
235 	else
236 		ifr.ifr_media &= ~IFM_ATM_SDH;
237 
238 	if (ioctl(mib_netsock, SIOCSIFMEDIA, &ifr) < 0) {
239 		syslog(LOG_ERR, "SIOCSIFMEDIA: %m");
240 		return (SNMP_ERR_GENERR);
241 	}
242 
243 	aif->pub.mode = newmode;
244 	return (SNMP_ERR_NOERROR);
245 }
246 
247 /*
248  * Attach to an ATM interface
249  */
250 static void
attach_if(struct mibif * ifp)251 attach_if(struct mibif *ifp)
252 {
253 	struct atmif_priv *aif;
254 
255 	/* we should not know it */
256 	TAILQ_FOREACH(aif, &atmif_list, link)
257 		if (aif->pub.ifp == ifp) {
258 			syslog(LOG_CRIT, "new ATM if already known '%s'",
259 			    ifp->name);
260 			return;
261 		}
262 
263 	/*
264 	 * tap it
265 	 */
266 	if ((aif = malloc(sizeof(*aif))) == NULL) {
267 		syslog(LOG_ERR, "new atmif: %m");
268 		return;
269 	}
270 	memset(aif, 0, sizeof(*aif));
271 
272 	aif->pub.ifp = ifp;
273 	aif->index = ifp->index;
274 	TAILQ_INIT(&aif->notify);
275 
276 	if (atmif_sys_attach_if(aif)) {
277 		free(aif);
278 		return;
279 	}
280 
281 	aif->ifpreg = mibif_notify(ifp, module, atmif_notify, aif);
282 
283 	aif->pub.carrier = ATMIF_CARRIER_UNKNOWN;
284 	atmif_check_carrier(aif);
285 	(void)atmif_get_mode(aif);
286 
287 	INSERT_OBJECT_INT(aif, &atmif_list);
288 
289 	last_change = this_tick;
290 
291 	return;
292 }
293 
294 /*
295  * Function gets called when a new interface is created. If this is an
296  * ATM interface, hook in. Claim the interface in any case even when
297  * the creation of our data structures fails.
298  */
299 static int
new_if(struct mibif * ifp)300 new_if(struct mibif *ifp)
301 {
302 	if (!started || ifp->mib.ifmd_data.ifi_type != IFT_ATM ||
303 	    ifp->xnotify != NULL)
304 		return (0);
305 
306 	attach_if(ifp);
307 	return (1);
308 }
309 
310 /*
311  * Start the module
312  */
313 static void
atm_start(void)314 atm_start(void)
315 {
316 	struct mibif *ifp;
317 
318 	reg_atm = or_register(&oid_begemotAtm,
319 	    "The Begemot MIB for ATM interfaces.", module);
320 
321 	started = 1;
322 	for (ifp = mib_first_if(); ifp != NULL; ifp = mib_next_if(ifp))
323 		if (ifp->mib.ifmd_data.ifi_type == IFT_ATM &&
324 		    ifp->xnotify == NULL)
325 			attach_if(ifp);
326 }
327 
328 /*
329  * Called when modules is loaded
330  */
331 static int
atm_init(struct lmodule * mod,int argc __unused,char * argv[]__unused)332 atm_init(struct lmodule *mod, int argc __unused, char *argv[] __unused)
333 {
334 	module = mod;
335 
336 	/* register to get creation messages for ATM interfaces */
337 	if (mib_register_newif(new_if, module)) {
338 		syslog(LOG_ERR, "cannot register newif function: %m");
339 		return (-1);
340 	}
341 
342 	return (0);
343 }
344 
345 /*
346  * Called when module gets unloaded - free all resources
347  */
348 static int
atm_fini(void)349 atm_fini(void)
350 {
351 	struct atmif_priv *aif;
352 
353 	while ((aif = TAILQ_FIRST(&atmif_list)) != NULL)
354 		atmif_destroy(aif);
355 
356 	mib_unregister_newif(module);
357 	or_unregister(reg_atm);
358 
359 	return (0);
360 }
361 
362 /*
363  * Other module unloaded/loaded
364  */
365 static void
atm_loading(const struct lmodule * mod,int loading)366 atm_loading(const struct lmodule *mod, int loading)
367 {
368 	struct atmif_priv *aif;
369 	struct atmif_reg *r0, *r1;
370 
371 	if (!loading) {
372 		/* remove notifications for this module */
373 		TAILQ_FOREACH(aif, &atmif_list, link)
374 			TAILQ_FOREACH_SAFE(r0, &aif->notify, link, r1) {
375 				if (r0->mod == mod) {
376 					TAILQ_REMOVE(&aif->notify, r0, link);
377 					free(r0);
378 				}
379 			}
380 	}
381 }
382 
383 const struct snmp_module config = {
384 	.comment = "This module implements a private MIB for ATM interfaces.",
385 	.init =		atm_init,
386 	.fini =		atm_fini,
387 	.start =	atm_start,
388 	.tree =		atm_ctree,
389 	.tree_size =	atm_CTREE_SIZE,
390 	.loading =	atm_loading
391 };
392 
393 /*
394  * Get the interface point for a table access
395  */
396 int
atmif_get_aif(struct snmp_value * value,u_int sub,enum snmp_op op,struct atmif_priv ** aifp)397 atmif_get_aif(struct snmp_value *value, u_int sub, enum snmp_op op,
398     struct atmif_priv **aifp)
399 {
400 	switch (op) {
401 
402 	  case SNMP_OP_GETNEXT:
403 		if ((*aifp = NEXT_OBJECT_INT(&atmif_list,
404 		    &value->var, sub)) == NULL)
405 			return (SNMP_ERR_NOSUCHNAME);
406 		value->var.len = sub + 1;
407 		value->var.subs[sub] = (*aifp)->index;
408 		break;
409 
410 	  case SNMP_OP_GET:
411 		if ((*aifp = FIND_OBJECT_INT(&atmif_list,
412 		    &value->var, sub)) == NULL)
413 			return (SNMP_ERR_NOSUCHNAME);
414 		break;
415 
416 	  case SNMP_OP_SET:
417 		if ((*aifp = FIND_OBJECT_INT(&atmif_list,
418 		    &value->var, sub)) == NULL)
419 			return (SNMP_ERR_NO_CREATION);
420 		break;
421 
422 	  case SNMP_OP_ROLLBACK:
423 	  case SNMP_OP_COMMIT:
424 		if ((*aifp = FIND_OBJECT_INT(&atmif_list,
425 		    &value->var, sub)) == NULL)
426 			abort();
427 		return (SNMP_ERR_NOERROR);
428 	}
429 
430 	if ((*aifp)->pub.mib->pcr == 0) {
431 		mib_fetch_ifmib((*aifp)->pub.ifp);
432 		atmif_sys_fill_mib(*aifp);
433 		atmif_check_carrier(*aifp);
434 	}
435 
436 	return (SNMP_ERR_NOERROR);
437 }
438 
439 /*
440  * Table of all ATM interfaces
441  */
442 int
op_atmif(struct snmp_context * ctx __unused,struct snmp_value * value,u_int sub,u_int vindex __unused,enum snmp_op op)443 op_atmif(struct snmp_context *ctx __unused, struct snmp_value *value,
444     u_int sub, u_int vindex __unused, enum snmp_op op)
445 {
446 	struct atmif_priv *aif;
447 	int err;
448 
449 	if ((err = atmif_get_aif(value, sub, op, &aif)) != SNMP_ERR_NOERROR)
450 		return (err);
451 
452 	if (op == SNMP_OP_SET) {
453 		switch (value->var.subs[sub - 1]) {
454 
455 		  default:
456 			return (SNMP_ERR_NOT_WRITEABLE);
457 
458 		  case LEAF_begemotAtmIfMode:
459 			if ((err = atmif_get_mode(aif)) != SNMP_ERR_NOERROR)
460 				return (err);
461 			if (aif->pub.mode == ATMIF_SUNI_MODE_UNKNOWN)
462 				return (SNMP_ERR_INCONS_VALUE);
463 			if (value->v.integer != ATMIF_SUNI_MODE_SONET &&
464 			    value->v.integer != ATMIF_SUNI_MODE_SDH)
465 				return (SNMP_ERR_WRONG_VALUE);
466 			if ((u_int)value->v.integer == aif->pub.mode)
467 				return (SNMP_ERR_NOERROR);
468 			return (atmif_set_mode(aif, value->v.integer));
469 		}
470 		abort();
471 	}
472 
473 	switch (value->var.subs[sub - 1]) {
474 
475 	  case LEAF_begemotAtmIfName:
476 		return (string_get(value, aif->pub.ifp->name, -1));
477 
478 	  case LEAF_begemotAtmIfPcr:
479 		value->v.uint32 = aif->pub.mib->pcr;
480 		return (SNMP_ERR_NOERROR);
481 
482 	  case LEAF_begemotAtmIfMedia:
483 		value->v.integer = aif->pub.mib->media;
484 		return (SNMP_ERR_NOERROR);
485 
486 	  case LEAF_begemotAtmIfVpiBits:
487 		value->v.uint32 = aif->pub.mib->vpi_bits;
488 		return (SNMP_ERR_NOERROR);
489 
490 	  case LEAF_begemotAtmIfVciBits:
491 		value->v.uint32 = aif->pub.mib->vci_bits;
492 		return (SNMP_ERR_NOERROR);
493 
494 	  case LEAF_begemotAtmIfMaxVpcs:
495 		value->v.uint32 = aif->pub.mib->max_vpcs;
496 		return (SNMP_ERR_NOERROR);
497 
498 	  case LEAF_begemotAtmIfMaxVccs:
499 		value->v.uint32 = aif->pub.mib->max_vccs;
500 		return (SNMP_ERR_NOERROR);
501 
502 	  case LEAF_begemotAtmIfEsi:
503 		return (string_get(value, aif->pub.mib->esi, 6));
504 
505 	  case LEAF_begemotAtmIfCarrierStatus:
506 		value->v.integer = aif->pub.carrier;
507 		return (SNMP_ERR_NOERROR);
508 
509 	  case LEAF_begemotAtmIfMode:
510 		if ((err = atmif_get_mode(aif)) != SNMP_ERR_NOERROR)
511 			return (err);
512 		value->v.integer = aif->pub.mode;
513 		return (SNMP_ERR_NOERROR);
514 	}
515 	abort();
516 }
517 
518 /*
519  * Hardware table
520  */
521 int
op_atmhw(struct snmp_context * ctx __unused,struct snmp_value * value,u_int sub,u_int vindex __unused,enum snmp_op op)522 op_atmhw(struct snmp_context *ctx __unused, struct snmp_value *value,
523     u_int sub, u_int vindex __unused, enum snmp_op op)
524 {
525 	struct atmif_priv *aif;
526 	int err;
527 
528 	if ((err = atmif_get_aif(value, sub, op, &aif)) != SNMP_ERR_NOERROR)
529 		return (err);
530 	if (op == SNMP_OP_SET)
531 		return (SNMP_ERR_NOT_WRITEABLE);
532 
533 	switch (value->var.subs[sub - 1]) {
534 
535 	  case LEAF_begemotAtmHWVendor:
536 		return (atm_sys_get_hw_vendor(aif, value));
537 
538 	  case LEAF_begemotAtmHWDevice:
539 		return (atm_sys_get_hw_device(aif, value));
540 
541 	  case LEAF_begemotAtmHWSerial:
542 		value->v.uint32 = aif->pub.mib->serial;
543 		return (SNMP_ERR_NOERROR);
544 
545 	  case LEAF_begemotAtmHWVersion:
546 		value->v.uint32 = aif->pub.mib->hw_version;
547 		return (SNMP_ERR_NOERROR);
548 
549 	  case LEAF_begemotAtmHWSoftVersion:
550 		value->v.uint32 = aif->pub.mib->sw_version;
551 		return (SNMP_ERR_NOERROR);
552 
553 	}
554 	abort();
555 }
556 
557 /*
558  * Scalars
559  */
560 int
op_atm(struct snmp_context * ctx __unused,struct snmp_value * value,u_int sub,u_int vindex __unused,enum snmp_op op)561 op_atm(struct snmp_context *ctx __unused, struct snmp_value *value,
562     u_int sub, u_int vindex __unused, enum snmp_op op)
563 {
564 	switch (op) {
565 
566 	  case SNMP_OP_GETNEXT:
567 		abort();
568 
569 	  case SNMP_OP_GET:
570 		switch (value->var.subs[sub - 1]) {
571 
572 		  case LEAF_begemotAtmIfTableLastChange:
573 			value->v.uint32 =
574 			    (last_change == 0 ? 0 : last_change - start_tick);
575 			return (SNMP_ERR_NOERROR);
576 		}
577 		abort();
578 
579 	  case SNMP_OP_SET:
580 		return (SNMP_ERR_NOT_WRITEABLE);
581 
582 	  case SNMP_OP_ROLLBACK:
583 	  case SNMP_OP_COMMIT:
584 		abort();
585 	}
586 	abort();
587 }
588 
589 /*
590  * Register for interface notifications
591  */
592 void *
atm_notify_aif(struct atmif * pub,const struct lmodule * mod,atmif_event_f func,void * arg)593 atm_notify_aif(struct atmif *pub, const struct lmodule *mod,
594     atmif_event_f func, void *arg)
595 {
596 	struct atmif_priv *aif = (struct atmif_priv *)pub;
597 	struct atmif_reg *r0;
598 
599 	if ((r0 = malloc(sizeof(*r0))) == NULL) {
600 		syslog(LOG_CRIT, "out of memory");
601 		return (NULL);
602 	}
603 	r0->func = func;
604 	r0->mod = mod;
605 	r0->data = arg;
606 	r0->aif = aif;
607 
608 	TAILQ_INSERT_TAIL(&aif->notify, r0, link);
609 
610 	return (r0);
611 }
612 
613 /*
614  * Unregister it
615  */
616 void
atm_unnotify_aif(void * arg)617 atm_unnotify_aif(void *arg)
618 {
619 	struct atmif_reg *r0 = arg;
620 
621 	TAILQ_REMOVE(&r0->aif->notify, r0, link);
622 	free(r0);
623 }
624