snmp_atm.c revision 133488
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 <harti@freebsd.org>
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.2 2004/08/06 17:30:40 brandt 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
52struct lmodule *module;
53
54/* list of all (known) ATM interfaces */
55struct atmif_list atmif_list = TAILQ_HEAD_INITIALIZER(atmif_list);
56
57/* whether we are started or not */
58static int started;
59
60/* last time table was changed */
61static uint32_t last_change;
62
63/* for the registration */
64static const struct asn_oid oid_begemotAtm = OIDX_begemotAtm;
65
66/* the registration */
67static u_int reg_atm;
68
69/*
70 * Find an ATM interface by name
71 */
72struct atmif *
73atm_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 */
86struct atmif *
87atm_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 */
100void
101atmif_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 */
117static void
118atmif_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 */
144static void
145atmif_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 */
160void
161atmif_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 */
190static int
191atmif_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 */
214static int
215atmif_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 */
250static void
251attach_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
275	if (atmif_sys_attach_if(aif)) {
276		free(aif);
277		return;
278	}
279
280	aif->ifpreg = mibif_notify(ifp, module, atmif_notify, aif);
281
282	aif->pub.carrier = ATMIF_CARRIER_UNKNOWN;
283	atmif_check_carrier(aif);
284	(void)atmif_get_mode(aif);
285
286	INSERT_OBJECT_INT(aif, &atmif_list);
287
288	last_change = this_tick;
289
290	return;
291}
292
293/*
294 * Function gets called when a new interface is created. If this is an
295 * ATM interface, hook in. Claim the interface in any case even when
296 * the creation of our data structures fails.
297 */
298static int
299new_if(struct mibif *ifp)
300{
301	if (!started || ifp->mib.ifmd_data.ifi_type != IFT_ATM ||
302	    ifp->xnotify != NULL)
303		return (0);
304
305	attach_if(ifp);
306	return (1);
307}
308
309/*
310 * Start the module
311 */
312static void
313atm_start(void)
314{
315	struct mibif *ifp;
316
317	reg_atm = or_register(&oid_begemotAtm,
318	    "The Begemot MIB for ATM interfaces.", module);
319
320	started = 1;
321	for (ifp = mib_first_if(); ifp != NULL; ifp = mib_next_if(ifp))
322		if (ifp->mib.ifmd_data.ifi_type == IFT_ATM &&
323		    ifp->xnotify == NULL)
324			attach_if(ifp);
325}
326
327/*
328 * Called when modules is loaded
329 */
330static int
331atm_init(struct lmodule *mod, int argc __unused, char *argv[] __unused)
332{
333	module = mod;
334
335	/* register to get creation messages for ATM interfaces */
336	if (mib_register_newif(new_if, module)) {
337		syslog(LOG_ERR, "cannot register newif function: %m");
338		return (-1);
339	}
340
341	return (0);
342}
343
344/*
345 * Called when module gets unloaded - free all resources
346 */
347static int
348atm_fini(void)
349{
350	struct atmif_priv *aif;
351
352	while ((aif = TAILQ_FIRST(&atmif_list)) != NULL)
353		atmif_destroy(aif);
354
355	mib_unregister_newif(module);
356	or_unregister(reg_atm);
357
358	return (0);
359}
360
361/*
362 * Other module unloaded/loaded
363 */
364static void
365atm_loading(const struct lmodule *mod, int loading)
366{
367	struct atmif_priv *aif;
368	struct atmif_reg *r0, *r1;
369
370	if (!loading) {
371		/* remove notifications for this module */
372		TAILQ_FOREACH(aif, &atmif_list, link)
373			TAILQ_FOREACH_SAFE(r0, &aif->notify, link, r1) {
374				if (r0->mod == mod) {
375					TAILQ_REMOVE(&aif->notify, r0, link);
376					free(r0);
377				}
378			}
379	}
380}
381
382const struct snmp_module config = {
383	.comment = "This module implements a private MIB for ATM interfaces.",
384	.init =		atm_init,
385	.fini =		atm_fini,
386	.start =	atm_start,
387	.tree =		atm_ctree,
388	.tree_size =	atm_CTREE_SIZE,
389	.loading =	atm_loading
390};
391
392/*
393 * Get the interface point for a table access
394 */
395int
396atmif_get_aif(struct snmp_value *value, u_int sub, enum snmp_op op,
397    struct atmif_priv **aifp)
398{
399	switch (op) {
400
401	  case SNMP_OP_GETNEXT:
402		if ((*aifp = NEXT_OBJECT_INT(&atmif_list,
403		    &value->var, sub)) == NULL)
404			return (SNMP_ERR_NOSUCHNAME);
405		value->var.len = sub + 1;
406		value->var.subs[sub] = (*aifp)->index;
407		break;
408
409	  case SNMP_OP_GET:
410		if ((*aifp = FIND_OBJECT_INT(&atmif_list,
411		    &value->var, sub)) == NULL)
412			return (SNMP_ERR_NOSUCHNAME);
413		break;
414
415	  case SNMP_OP_SET:
416		if ((*aifp = FIND_OBJECT_INT(&atmif_list,
417		    &value->var, sub)) == NULL)
418			return (SNMP_ERR_NO_CREATION);
419		break;
420
421	  case SNMP_OP_ROLLBACK:
422	  case SNMP_OP_COMMIT:
423		if ((*aifp = FIND_OBJECT_INT(&atmif_list,
424		    &value->var, sub)) == NULL)
425			abort();
426		return (SNMP_ERR_NOERROR);
427	}
428
429	if ((*aifp)->pub.mib->pcr == 0) {
430		mib_fetch_ifmib((*aifp)->pub.ifp);
431		atmif_sys_fill_mib(*aifp);
432		atmif_check_carrier(*aifp);
433	}
434
435	return (SNMP_ERR_NOERROR);
436}
437
438/*
439 * Table of all ATM interfaces
440 */
441int
442op_atmif(struct snmp_context *ctx __unused, struct snmp_value *value,
443    u_int sub, u_int vindex __unused, enum snmp_op op)
444{
445	struct atmif_priv *aif;
446	int err;
447
448	if ((err = atmif_get_aif(value, sub, op, &aif)) != SNMP_ERR_NOERROR)
449		return (err);
450
451	if (op == SNMP_OP_SET) {
452		switch (value->var.subs[sub - 1]) {
453
454		  default:
455			return (SNMP_ERR_NOT_WRITEABLE);
456
457		  case LEAF_begemotAtmIfMode:
458			if ((err = atmif_get_mode(aif)) != SNMP_ERR_NOERROR)
459				return (err);
460			if (aif->pub.mode == ATMIF_SUNI_MODE_UNKNOWN)
461				return (SNMP_ERR_INCONS_VALUE);
462			if (value->v.integer != ATMIF_SUNI_MODE_SONET &&
463			    value->v.integer != ATMIF_SUNI_MODE_SDH)
464				return (SNMP_ERR_WRONG_VALUE);
465			if ((u_int)value->v.integer == aif->pub.mode)
466				return (SNMP_ERR_NOERROR);
467			return (atmif_set_mode(aif, value->v.integer));
468		}
469		abort();
470	}
471
472	switch (value->var.subs[sub - 1]) {
473
474	  case LEAF_begemotAtmIfName:
475		return (string_get(value, aif->pub.ifp->name, -1));
476
477	  case LEAF_begemotAtmIfPcr:
478		value->v.uint32 = aif->pub.mib->pcr;
479		return (SNMP_ERR_NOERROR);
480
481	  case LEAF_begemotAtmIfMedia:
482		value->v.integer = aif->pub.mib->media;
483		return (SNMP_ERR_NOERROR);
484
485	  case LEAF_begemotAtmIfVpiBits:
486		value->v.uint32 = aif->pub.mib->vpi_bits;
487		return (SNMP_ERR_NOERROR);
488
489	  case LEAF_begemotAtmIfVciBits:
490		value->v.uint32 = aif->pub.mib->vci_bits;
491		return (SNMP_ERR_NOERROR);
492
493	  case LEAF_begemotAtmIfMaxVpcs:
494		value->v.uint32 = aif->pub.mib->max_vpcs;
495		return (SNMP_ERR_NOERROR);
496
497	  case LEAF_begemotAtmIfMaxVccs:
498		value->v.uint32 = aif->pub.mib->max_vccs;
499		return (SNMP_ERR_NOERROR);
500
501	  case LEAF_begemotAtmIfEsi:
502		return (string_get(value, aif->pub.mib->esi, 6));
503
504	  case LEAF_begemotAtmIfCarrierStatus:
505		value->v.integer = aif->pub.carrier;
506		return (SNMP_ERR_NOERROR);
507
508	  case LEAF_begemotAtmIfMode:
509		if ((err = atmif_get_mode(aif)) != SNMP_ERR_NOERROR)
510			return (err);
511		value->v.integer = aif->pub.mode;
512		return (SNMP_ERR_NOERROR);
513	}
514	abort();
515}
516
517/*
518 * Hardware table
519 */
520int
521op_atmhw(struct snmp_context *ctx __unused, struct snmp_value *value,
522    u_int sub, u_int vindex __unused, enum snmp_op op)
523{
524	struct atmif_priv *aif;
525	int err;
526
527	if ((err = atmif_get_aif(value, sub, op, &aif)) != SNMP_ERR_NOERROR)
528		return (err);
529	if (op == SNMP_OP_SET)
530		return (SNMP_ERR_NOT_WRITEABLE);
531
532	switch (value->var.subs[sub - 1]) {
533
534	  case LEAF_begemotAtmHWVendor:
535		return (atm_sys_get_hw_vendor(aif, value));
536
537	  case LEAF_begemotAtmHWDevice:
538		return (atm_sys_get_hw_device(aif, value));
539
540	  case LEAF_begemotAtmHWSerial:
541		value->v.uint32 = aif->pub.mib->serial;
542		return (SNMP_ERR_NOERROR);
543
544	  case LEAF_begemotAtmHWVersion:
545		value->v.uint32 = aif->pub.mib->hw_version;
546		return (SNMP_ERR_NOERROR);
547
548	  case LEAF_begemotAtmHWSoftVersion:
549		value->v.uint32 = aif->pub.mib->sw_version;
550		return (SNMP_ERR_NOERROR);
551
552	}
553	abort();
554}
555
556/*
557 * Scalars
558 */
559int
560op_atm(struct snmp_context *ctx __unused, struct snmp_value *value,
561    u_int sub, u_int vindex __unused, enum snmp_op op)
562{
563	switch (op) {
564
565	  case SNMP_OP_GETNEXT:
566		abort();
567
568	  case SNMP_OP_GET:
569		switch (value->var.subs[sub - 1]) {
570
571		  case LEAF_begemotAtmIfTableLastChange:
572			value->v.uint32 =
573			    (last_change == 0 ? 0 : last_change - start_tick);
574			return (SNMP_ERR_NOERROR);
575		}
576		abort();
577
578	  case SNMP_OP_SET:
579		return (SNMP_ERR_NOT_WRITEABLE);
580
581	  case SNMP_OP_ROLLBACK:
582	  case SNMP_OP_COMMIT:
583		abort();
584	}
585	abort();
586}
587
588/*
589 * Register for interface notifications
590 */
591void *
592atm_notify_aif(struct atmif *pub, const struct lmodule *mod,
593    atmif_event_f func, void *arg)
594{
595	struct atmif_priv *aif = (struct atmif_priv *)pub;
596	struct atmif_reg *r0;
597
598	if ((r0 = malloc(sizeof(*r0))) == NULL) {
599		syslog(LOG_CRIT, "out of memory");
600		return (NULL);
601	}
602	r0->func = func;
603	r0->mod = mod;
604	r0->data = arg;
605	r0->aif = aif;
606
607	TAILQ_INSERT_TAIL(&aif->notify, r0, link);
608
609	return (r0);
610}
611
612/*
613 * Unregister it
614 */
615void
616atm_unnotify_aif(void *arg)
617{
618	struct atmif_reg *r0 = arg;
619
620	TAILQ_REMOVE(&r0->aif->notify, r0, link);
621	free(r0);
622}
623