]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/ngatm/snmp_atm/snmp_atm.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / ngatm / snmp_atm / snmp_atm.c
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
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 *
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 *
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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 *
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
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 }