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.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
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 uint64_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	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 */
299static int
300new_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 */
313static void
314atm_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 */
331static int
332atm_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 */
348static int
349atm_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 */
365static void
366atm_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
383const 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 */
396int
397atmif_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 */
442int
443op_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 */
521int
522op_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 */
560int
561op_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 */
592void *
593atm_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 */
616void
617atm_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