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