Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/usr.sbin/bsnmpd/modules/snmp_netgraph/snmp_netgraph.c
105686 views
1
/*
2
* Copyright (c) 2001-2003
3
* Fraunhofer Institute for Open Communication Systems (FhG Fokus).
4
* All rights reserved.
5
*
6
* Author: Harti Brandt <[email protected]>
7
*
8
* Redistribution of this software and documentation and use in source and
9
* binary forms, with or without modification, are permitted provided that
10
* the following conditions are met:
11
*
12
* 1. Redistributions of source code or documentation must retain the above
13
* copyright notice, this list of conditions and the following disclaimer.
14
* 2. Redistributions in binary form must reproduce the above copyright
15
* notice, this list of conditions and the following disclaimer in the
16
* documentation and/or other materials provided with the distribution.
17
*
18
* THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY FRAUNHOFER FOKUS
19
* AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
20
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
21
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
22
* FRAUNHOFER FOKUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
25
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
28
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
*
30
* Netgraph interface for SNMPd.
31
*/
32
#include <sys/types.h>
33
#include <sys/param.h>
34
#include <sys/linker.h>
35
#include <sys/socket.h>
36
#include <sys/syslog.h>
37
#include <sys/queue.h>
38
#include <sys/sysctl.h>
39
#include <stdio.h>
40
#include <stdlib.h>
41
#include <errno.h>
42
#include <unistd.h>
43
#include <string.h>
44
#include <netgraph.h>
45
#include <bsnmp/snmpmod.h>
46
#include "snmp_netgraph.h"
47
#include "netgraph_tree.h"
48
#include "netgraph_oid.h"
49
50
/* maximum message size */
51
#define RESBUFSIZ 20000
52
53
/* default node name */
54
#define NODENAME "NgSnmpd"
55
56
/* my node Id */
57
ng_ID_t snmp_node;
58
u_char *snmp_nodename;
59
60
/* the Object Resource registration index */
61
static u_int reg_index;
62
static const struct asn_oid oid_begemotNg = OIDX_begemotNg;
63
64
/* configuration */
65
/* this must be smaller than int32_t because the functions in libnetgraph
66
* falsely return an int */
67
static size_t resbufsiz = RESBUFSIZ;
68
static u_int timeout = 1000;
69
static u_int debug_level;
70
71
/* number of microseconds per clock tick */
72
static struct clockinfo clockinfo;
73
74
/* Csock buffers. Communication on the csock is asynchronuous. This means
75
* if we wait for a specific response, we may get other messages. Put these
76
* into a queue and execute them when we are idle. */
77
struct csock_buf {
78
STAILQ_ENTRY(csock_buf) link;
79
struct ng_mesg *mesg;
80
char path[NG_PATHSIZ];
81
};
82
static STAILQ_HEAD(, csock_buf) csock_bufs =
83
STAILQ_HEAD_INITIALIZER(csock_bufs);
84
85
/*
86
* We dispatch unsolicieted messages by node cookies and ids.
87
* So we must keep a list of hook names and dispatch functions.
88
*/
89
struct msgreg {
90
u_int32_t cookie;
91
ng_ID_t id;
92
ng_cookie_f *func;
93
void *arg;
94
const struct lmodule *mod;
95
SLIST_ENTRY(msgreg) link;
96
};
97
static SLIST_HEAD(, msgreg) msgreg_list =
98
SLIST_HEAD_INITIALIZER(msgreg_list);
99
100
/*
101
* Data messages are dispatched by hook names.
102
*/
103
struct datareg {
104
char hook[NG_HOOKSIZ];
105
ng_hook_f *func;
106
void *arg;
107
const struct lmodule *mod;
108
SLIST_ENTRY(datareg) link;
109
};
110
static SLIST_HEAD(, datareg) datareg_list =
111
SLIST_HEAD_INITIALIZER(datareg_list);
112
113
/* the netgraph sockets */
114
static int csock, dsock;
115
static void *csock_fd, *dsock_fd;
116
117
/* our module handle */
118
static struct lmodule *module;
119
120
/* statistics */
121
static u_int32_t stats[LEAF_begemotNgTooLargeDatas+1];
122
123
/* netgraph type list */
124
struct ngtype {
125
char name[NG_TYPESIZ];
126
struct asn_oid index;
127
TAILQ_ENTRY(ngtype) link;
128
};
129
TAILQ_HEAD(ngtype_list, ngtype);
130
131
static struct ngtype_list ngtype_list;
132
static uint64_t ngtype_tick;
133
134
135
/*
136
* Register a function to receive unsolicited messages
137
*/
138
void *
139
ng_register_cookie(const struct lmodule *mod, u_int32_t cookie, ng_ID_t id,
140
ng_cookie_f *func, void *arg)
141
{
142
struct msgreg *d;
143
144
if ((d = malloc(sizeof(*d))) == NULL)
145
return (NULL);
146
147
d->cookie = cookie;
148
d->id = id;
149
d->func = func;
150
d->arg = arg;
151
d->mod = mod;
152
153
SLIST_INSERT_HEAD(&msgreg_list, d, link);
154
155
return (d);
156
}
157
158
/*
159
* Remove a registration.
160
*/
161
void
162
ng_unregister_cookie(void *dd)
163
{
164
struct msgreg *d = dd;
165
166
SLIST_REMOVE(&msgreg_list, d, msgreg, link);
167
free(d);
168
}
169
170
/*
171
* Register a function for hook data.
172
*/
173
void *
174
ng_register_hook(const struct lmodule *mod, const char *hook,
175
ng_hook_f *func, void *arg)
176
{
177
struct datareg *d;
178
179
if ((d = malloc(sizeof(*d))) == NULL)
180
return (NULL);
181
182
strcpy(d->hook, hook);
183
d->func = func;
184
d->arg = arg;
185
d->mod = mod;
186
187
SLIST_INSERT_HEAD(&datareg_list, d, link);
188
189
return (d);
190
}
191
192
/*
193
* Unregister a hook function
194
*/
195
void
196
ng_unregister_hook(void *dd)
197
{
198
struct datareg *d = dd;
199
200
SLIST_REMOVE(&datareg_list, d, datareg, link);
201
free(d);
202
}
203
204
/*
205
* Unregister all hooks and cookies for that module. Note: doesn't disconnect
206
* any hooks!
207
*/
208
void
209
ng_unregister_module(const struct lmodule *mod)
210
{
211
struct msgreg *m, *m1;
212
struct datareg *d, *d1;
213
214
m = SLIST_FIRST(&msgreg_list);
215
while (m != NULL) {
216
m1 = SLIST_NEXT(m, link);
217
if (m->mod == mod) {
218
SLIST_REMOVE(&msgreg_list, m, msgreg, link);
219
free(m);
220
}
221
m = m1;
222
}
223
224
d = SLIST_FIRST(&datareg_list);
225
while (d != NULL) {
226
d1 = SLIST_NEXT(d, link);
227
if (d->mod == mod) {
228
SLIST_REMOVE(&datareg_list, d, datareg, link);
229
free(d);
230
}
231
d = d1;
232
}
233
}
234
235
/*
236
* Dispatch a message to the correct module and delete it. More than one
237
* module can get a message.
238
*/
239
static void
240
csock_handle(struct ng_mesg *mesg, const char *path)
241
{
242
struct msgreg *d, *d1;
243
u_int id;
244
int len;
245
246
if (sscanf(path, "[%x]:%n", &id, &len) != 1 ||
247
(u_int)len != strlen(path)) {
248
syslog(LOG_ERR, "cannot parse message path '%s'", path);
249
id = 0;
250
}
251
252
d = SLIST_FIRST(&msgreg_list);
253
while (d != NULL) {
254
d1 = SLIST_NEXT(d, link);
255
if (d->cookie == mesg->header.typecookie &&
256
(d->id == 0 || d->id == id || id == 0))
257
(*d->func)(mesg, path, id, d->arg);
258
d = d1;
259
}
260
free(mesg);
261
}
262
263
/*
264
* Input from the control socket.
265
*/
266
static struct ng_mesg *
267
csock_read(char *path)
268
{
269
struct ng_mesg *mesg;
270
int ret, err;
271
272
if ((mesg = malloc(resbufsiz + 1)) == NULL) {
273
stats[LEAF_begemotNgNoMems]++;
274
syslog(LOG_CRIT, "out of memory");
275
errno = ENOMEM;
276
return (NULL);
277
}
278
if ((ret = NgRecvMsg(csock, mesg, resbufsiz + 1, path)) < 0) {
279
err = errno;
280
free(mesg);
281
if (errno == EWOULDBLOCK) {
282
errno = err;
283
return (NULL);
284
}
285
stats[LEAF_begemotNgMsgReadErrs]++;
286
syslog(LOG_WARNING, "read from csock: %m");
287
errno = err;
288
return (NULL);
289
}
290
if (ret == 0) {
291
syslog(LOG_DEBUG, "node closed -- exiting");
292
exit(0);
293
}
294
if ((size_t)ret > resbufsiz) {
295
stats[LEAF_begemotNgTooLargeMsgs]++;
296
syslog(LOG_WARNING, "ng message too large");
297
free(mesg);
298
errno = EFBIG;
299
return (NULL);
300
}
301
return (mesg);
302
}
303
304
static void
305
csock_input(int fd __unused, void *udata __unused)
306
{
307
struct ng_mesg *mesg;
308
char path[NG_PATHSIZ];
309
310
if ((mesg = csock_read(path)) == NULL)
311
return;
312
313
csock_handle(mesg, path);
314
}
315
316
/*
317
* Write a message to a node.
318
*/
319
int
320
ng_output(const char *path, u_int cookie, u_int opcode,
321
const void *arg, size_t arglen)
322
{
323
return (NgSendMsg(csock, path, (int)cookie, (int)opcode, arg, arglen));
324
}
325
int
326
ng_output_node(const char *node, u_int cookie, u_int opcode,
327
const void *arg, size_t arglen)
328
{
329
char path[NG_PATHSIZ];
330
331
sprintf(path, "%s:", node);
332
return (ng_output(path, cookie, opcode, arg, arglen));
333
}
334
int
335
ng_output_id(ng_ID_t node, u_int cookie, u_int opcode,
336
const void *arg, size_t arglen)
337
{
338
char path[NG_PATHSIZ];
339
340
sprintf(path, "[%x]:", node);
341
return (ng_output(path, cookie, opcode, arg, arglen));
342
}
343
344
345
346
/*
347
* Execute a synchronuous dialog with the csock. All message we receive, that
348
* do not match our request, are queue until the next call to the IDLE function.
349
*/
350
struct ng_mesg *
351
ng_dialog(const char *path, u_int cookie, u_int opcode,
352
const void *arg, size_t arglen)
353
{
354
int token, err;
355
struct ng_mesg *mesg;
356
char rpath[NG_PATHSIZ];
357
struct csock_buf *b;
358
struct timeval end, tv;
359
360
if ((token = ng_output(path, cookie, opcode, arg, arglen)) < 0)
361
return (NULL);
362
363
if (csock_fd)
364
fd_suspend(csock_fd);
365
366
gettimeofday(&end, NULL);
367
tv.tv_sec = timeout / 1000;
368
tv.tv_usec = (timeout % 1000) * 1000;
369
timeradd(&end, &tv, &end);
370
for (;;) {
371
mesg = NULL;
372
gettimeofday(&tv, NULL);
373
if (timercmp(&tv, &end, >=)) {
374
block:
375
syslog(LOG_WARNING, "no response for request %u/%u",
376
cookie, opcode);
377
errno = EWOULDBLOCK;
378
break;
379
}
380
timersub(&end, &tv, &tv);
381
if (tv.tv_sec == 0 && tv.tv_usec < clockinfo.tick)
382
goto block;
383
384
if (setsockopt(csock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1)
385
syslog(LOG_WARNING, "setsockopt(SO_RCVTIMEO): %m");
386
if ((mesg = csock_read(rpath)) == NULL) {
387
if (errno == EWOULDBLOCK)
388
continue;
389
break;
390
}
391
if (mesg->header.token == (u_int)token)
392
break;
393
if ((b = malloc(sizeof(*b))) == NULL) {
394
stats[LEAF_begemotNgNoMems]++;
395
syslog(LOG_ERR, "out of memory");
396
free(mesg);
397
continue;
398
}
399
b->mesg = mesg;
400
strcpy(b->path, rpath);
401
STAILQ_INSERT_TAIL(&csock_bufs, b, link);
402
}
403
404
tv.tv_sec = 0;
405
tv.tv_usec = 0;
406
if (setsockopt(csock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1)
407
syslog(LOG_WARNING, "setsockopt(SO_RCVTIMEO,0): %m");
408
409
if (csock_fd) {
410
err = errno;
411
fd_resume(csock_fd);
412
errno = err;
413
}
414
415
return (mesg);
416
}
417
struct ng_mesg *
418
ng_dialog_node(const char *node, u_int cookie, u_int opcode,
419
const void *arg, size_t arglen)
420
{
421
char path[NG_PATHSIZ];
422
423
sprintf(path, "%s:", node);
424
return (ng_dialog(path, cookie, opcode, arg, arglen));
425
}
426
struct ng_mesg *
427
ng_dialog_id(ng_ID_t id, u_int cookie, u_int opcode,
428
const void *arg, size_t arglen)
429
{
430
char path[NG_PATHSIZ];
431
432
sprintf(path, "[%x]:", id);
433
return (ng_dialog(path, cookie, opcode, arg, arglen));
434
}
435
436
437
/*
438
* Send a data message to a given hook.
439
*/
440
int
441
ng_send_data(const char *hook, const void *sndbuf, size_t sndlen)
442
{
443
return (NgSendData(dsock, hook, sndbuf, sndlen));
444
}
445
446
/*
447
* Input from a data socket. Dispatch to the function for that hook.
448
*/
449
static void
450
dsock_input(int fd __unused, void *udata __unused)
451
{
452
u_char *resbuf, embuf[100];
453
ssize_t len;
454
char hook[NG_HOOKSIZ];
455
struct datareg *d, *d1;
456
457
if ((resbuf = malloc(resbufsiz + 1)) == NULL) {
458
stats[LEAF_begemotNgNoMems]++;
459
syslog(LOG_CRIT, "out of memory");
460
(void)NgRecvData(fd, embuf, sizeof(embuf), hook);
461
errno = ENOMEM;
462
return;
463
}
464
if ((len = NgRecvData(fd, resbuf, resbufsiz + 1, hook)) == -1) {
465
stats[LEAF_begemotNgDataReadErrs]++;
466
syslog(LOG_ERR, "reading message: %m");
467
free(resbuf);
468
return;
469
}
470
if (len == 0) {
471
free(resbuf);
472
return;
473
}
474
if ((size_t)len == resbufsiz + 1) {
475
stats[LEAF_begemotNgTooLargeDatas]++;
476
syslog(LOG_WARNING, "message too long");
477
free(resbuf);
478
return;
479
}
480
481
/*
482
* Dispatch message. Maybe dispatched to more than one function.
483
*/
484
d = SLIST_FIRST(&datareg_list);
485
while (d != NULL) {
486
d1 = SLIST_NEXT(d, link);
487
if (strcmp(hook, d->hook) == 0)
488
(*d->func)(hook, resbuf, len, d->arg);
489
d = d1;
490
}
491
492
free(resbuf);
493
}
494
495
/*
496
* The SNMP daemon is about to wait for an event. Look whether we have
497
* netgraph messages waiting. If yes, drain the queue.
498
*/
499
static void
500
ng_idle(void)
501
{
502
struct csock_buf *b;
503
504
/* execute waiting csock_bufs */
505
while ((b = STAILQ_FIRST(&csock_bufs)) != NULL) {
506
STAILQ_REMOVE_HEAD(&csock_bufs, link);
507
csock_handle(b->mesg, b->path);
508
free(b);
509
}
510
}
511
512
/*
513
* Called when the module is loaded. Returning a non-zero value means,
514
* rejecting the initialisation.
515
*
516
* We make the netgraph socket.
517
*/
518
static int
519
ng_init(struct lmodule *mod, int argc, char *argv[])
520
{
521
int name[2];
522
size_t len;
523
524
module = mod;
525
526
if (argc == 0) {
527
if ((snmp_nodename = malloc(strlen(NODENAME) + 1)) == NULL)
528
return (ENOMEM);
529
strcpy(snmp_nodename, NODENAME);
530
} else {
531
if ((snmp_nodename = malloc(NG_NODESIZ)) == NULL)
532
return (ENOMEM);
533
strlcpy(snmp_nodename, argv[0], NG_NODESIZ);
534
}
535
536
/* fetch clockinfo (for the number of microseconds per tick) */
537
name[0] = CTL_KERN;
538
name[1] = KERN_CLOCKRATE;
539
len = sizeof(clockinfo);
540
if (sysctl(name, 2, &clockinfo, &len, NULL, 0) == -1)
541
return (errno);
542
543
TAILQ_INIT(&ngtype_list);
544
545
return (0);
546
}
547
548
/*
549
* Get the node Id/name/type of a node.
550
*/
551
ng_ID_t
552
ng_node_id(const char *path)
553
{
554
struct ng_mesg *resp;
555
ng_ID_t id;
556
557
if ((resp = ng_dialog(path, NGM_GENERIC_COOKIE, NGM_NODEINFO,
558
NULL, 0)) == NULL)
559
return (0);
560
id = ((struct nodeinfo *)(void *)resp->data)->id;
561
free(resp);
562
return (id);
563
}
564
ng_ID_t
565
ng_node_id_node(const char *node)
566
{
567
struct ng_mesg *resp;
568
ng_ID_t id;
569
570
if ((resp = ng_dialog_node(node, NGM_GENERIC_COOKIE, NGM_NODEINFO,
571
NULL, 0)) == NULL)
572
return (0);
573
id = ((struct nodeinfo *)(void *)resp->data)->id;
574
free(resp);
575
return (id);
576
}
577
ng_ID_t
578
ng_node_name(ng_ID_t id, char *name)
579
{
580
struct ng_mesg *resp;
581
582
if ((resp = ng_dialog_id(id, NGM_GENERIC_COOKIE, NGM_NODEINFO,
583
NULL, 0)) == NULL)
584
return (0);
585
strcpy(name, ((struct nodeinfo *)(void *)resp->data)->name);
586
free(resp);
587
return (id);
588
589
}
590
ng_ID_t
591
ng_node_type(ng_ID_t id, char *type)
592
{
593
struct ng_mesg *resp;
594
595
if ((resp = ng_dialog_id(id, NGM_GENERIC_COOKIE, NGM_NODEINFO,
596
NULL, 0)) == NULL)
597
return (0);
598
strcpy(type, ((struct nodeinfo *)(void *)resp->data)->type);
599
free(resp);
600
return (id);
601
}
602
603
/*
604
* Connect our node to some other node
605
*/
606
int
607
ng_connect_node(const char *node, const char *ourhook, const char *peerhook)
608
{
609
struct ngm_connect conn;
610
611
snprintf(conn.path, NG_PATHSIZ, "%s:", node);
612
strlcpy(conn.ourhook, ourhook, NG_HOOKSIZ);
613
strlcpy(conn.peerhook, peerhook, NG_HOOKSIZ);
614
return (NgSendMsg(csock, ".:",
615
NGM_GENERIC_COOKIE, NGM_CONNECT, &conn, sizeof(conn)));
616
}
617
int
618
ng_connect_id(ng_ID_t id, const char *ourhook, const char *peerhook)
619
{
620
struct ngm_connect conn;
621
622
snprintf(conn.path, NG_PATHSIZ, "[%x]:", id);
623
strlcpy(conn.ourhook, ourhook, NG_HOOKSIZ);
624
strlcpy(conn.peerhook, peerhook, NG_HOOKSIZ);
625
return (NgSendMsg(csock, ".:",
626
NGM_GENERIC_COOKIE, NGM_CONNECT, &conn, sizeof(conn)));
627
}
628
629
int
630
ng_connect2_id(ng_ID_t id, ng_ID_t peer, const char *ourhook,
631
const char *peerhook)
632
{
633
struct ngm_connect conn;
634
char path[NG_PATHSIZ];
635
636
snprintf(path, NG_PATHSIZ, "[%x]:", id);
637
638
snprintf(conn.path, NG_PATHSIZ, "[%x]:", peer);
639
strlcpy(conn.ourhook, ourhook, NG_HOOKSIZ);
640
strlcpy(conn.peerhook, peerhook, NG_HOOKSIZ);
641
return (NgSendMsg(csock, path,
642
NGM_GENERIC_COOKIE, NGM_CONNECT, &conn, sizeof(conn)));
643
}
644
645
int
646
ng_connect2_tee_id(ng_ID_t id, ng_ID_t peer, const char *ourhook,
647
const char *peerhook)
648
{
649
struct ngm_connect conn;
650
char path[NG_PATHSIZ];
651
ng_ID_t tee;
652
653
if ((tee = ng_mkpeer_id(id, NULL, "tee", ourhook, "left")) == 0)
654
return (-1);
655
656
snprintf(path, NG_PATHSIZ, "[%x]:", tee);
657
658
snprintf(conn.path, NG_PATHSIZ, "[%x]:", peer);
659
strlcpy(conn.ourhook, "right", NG_HOOKSIZ);
660
strlcpy(conn.peerhook, peerhook, NG_HOOKSIZ);
661
return (NgSendMsg(csock, path,
662
NGM_GENERIC_COOKIE, NGM_CONNECT, &conn, sizeof(conn)));
663
}
664
665
/*
666
* Ensure that a node of type 'type' is connected to 'hook' of 'node'
667
* and return its node id. tee nodes between node and the target node
668
* are skipped. If the type is wrong, or the hook is a dead-end return 0.
669
* If type is NULL, it is not checked.
670
*/
671
static ng_ID_t
672
ng_next_node_id_internal(ng_ID_t node, const char *type, const char *hook,
673
int skip_tee)
674
{
675
struct ng_mesg *resp;
676
struct hooklist *hooklist;
677
u_int i;
678
679
if ((resp = ng_dialog_id(node, NGM_GENERIC_COOKIE, NGM_LISTHOOKS,
680
NULL, 0)) == NULL) {
681
syslog(LOG_ERR, "get hook list: %m");
682
exit(1);
683
}
684
hooklist = (struct hooklist *)(void *)resp->data;
685
686
for (i = 0; i < hooklist->nodeinfo.hooks; i++)
687
if (strcmp(hooklist->link[i].ourhook, hook) == 0)
688
break;
689
690
if (i == hooklist->nodeinfo.hooks) {
691
free(resp);
692
return (0);
693
}
694
695
node = hooklist->link[i].nodeinfo.id;
696
697
if (skip_tee && strcmp(hooklist->link[i].nodeinfo.type, "tee") == 0) {
698
if (strcmp(hooklist->link[i].peerhook, "left") == 0)
699
node = ng_next_node_id(node, type, "right");
700
else if (strcmp(hooklist->link[i].peerhook, "right") == 0)
701
node = ng_next_node_id(node, type, "left");
702
else if (type != NULL &&
703
strcmp(hooklist->link[i].nodeinfo.type, type) != 0)
704
node = 0;
705
706
} else if (type != NULL &&
707
strcmp(hooklist->link[i].nodeinfo.type, type) != 0)
708
node = 0;
709
710
free(resp);
711
712
return (node);
713
}
714
715
/*
716
* Ensure that a node of type 'type' is connected to 'hook' of 'node'
717
* and return its node id. tee nodes between node and the target node
718
* are skipped. If the type is wrong, or the hook is a dead-end return 0.
719
* If type is NULL, it is not checked.
720
*/
721
ng_ID_t
722
ng_next_node_id(ng_ID_t node, const char *type, const char *hook)
723
{
724
return (ng_next_node_id_internal(node, type, hook, 1));
725
}
726
727
ng_ID_t
728
ng_mkpeer_id(ng_ID_t id, const char *nodename, const char *type,
729
const char *hook, const char *peerhook)
730
{
731
char path[NG_PATHSIZ];
732
struct ngm_mkpeer mkpeer;
733
struct ngm_name name;
734
735
strlcpy(mkpeer.type, type, NG_TYPESIZ);
736
strlcpy(mkpeer.ourhook, hook, NG_HOOKSIZ);
737
strlcpy(mkpeer.peerhook, peerhook, NG_HOOKSIZ);
738
739
sprintf(path, "[%x]:", id);
740
if (NgSendMsg(csock, path, NGM_GENERIC_COOKIE, NGM_MKPEER,
741
&mkpeer, sizeof(mkpeer)) == -1)
742
return (0);
743
744
if ((id = ng_next_node_id_internal(id, NULL, hook, 0)) == 0)
745
return (0);
746
747
if (nodename != NULL) {
748
strcpy(name.name, nodename);
749
sprintf(path, "[%x]:", id);
750
if (NgSendMsg(csock, path, NGM_GENERIC_COOKIE, NGM_NAME,
751
&name, sizeof(name)) == -1)
752
return (0);
753
}
754
return (id);
755
}
756
757
/*
758
* SHutdown node
759
*/
760
int
761
ng_shutdown_id(ng_ID_t id)
762
{
763
char path[NG_PATHSIZ];
764
765
snprintf(path, NG_PATHSIZ, "[%x]:", id);
766
return (NgSendMsg(csock, path, NGM_GENERIC_COOKIE,
767
NGM_SHUTDOWN, NULL, 0));
768
}
769
770
/*
771
* Disconnect one of our hooks
772
*/
773
int
774
ng_rmhook(const char *ourhook)
775
{
776
struct ngm_rmhook rmhook;
777
778
strlcpy(rmhook.ourhook, ourhook, NG_HOOKSIZ);
779
return (NgSendMsg(csock, ".:",
780
NGM_GENERIC_COOKIE, NGM_RMHOOK, &rmhook, sizeof(rmhook)));
781
}
782
783
/*
784
* Disconnect a hook of a node
785
*/
786
int
787
ng_rmhook_id(ng_ID_t id, const char *hook)
788
{
789
struct ngm_rmhook rmhook;
790
char path[NG_PATHSIZ];
791
792
strlcpy(rmhook.ourhook, hook, NG_HOOKSIZ);
793
snprintf(path, NG_PATHSIZ, "[%x]:", id);
794
return (NgSendMsg(csock, path,
795
NGM_GENERIC_COOKIE, NGM_RMHOOK, &rmhook, sizeof(rmhook)));
796
}
797
798
/*
799
* Disconnect a hook and shutdown all tee nodes that were connected to that
800
* hook.
801
*/
802
int
803
ng_rmhook_tee_id(ng_ID_t node, const char *hook)
804
{
805
struct ng_mesg *resp;
806
struct hooklist *hooklist;
807
u_int i;
808
int first = 1;
809
ng_ID_t next_node;
810
const char *next_hook;
811
812
again:
813
/* if we have just shutdown a tee node, which had no other hooks
814
* connected, the node id may already be wrong here. */
815
if ((resp = ng_dialog_id(node, NGM_GENERIC_COOKIE, NGM_LISTHOOKS,
816
NULL, 0)) == NULL)
817
return (0);
818
819
hooklist = (struct hooklist *)(void *)resp->data;
820
821
for (i = 0; i < hooklist->nodeinfo.hooks; i++)
822
if (strcmp(hooklist->link[i].ourhook, hook) == 0)
823
break;
824
825
if (i == hooklist->nodeinfo.hooks) {
826
free(resp);
827
return (0);
828
}
829
830
next_node = 0;
831
next_hook = NULL;
832
if (strcmp(hooklist->link[i].nodeinfo.type, "tee") == 0) {
833
if (strcmp(hooklist->link[i].peerhook, "left") == 0) {
834
next_node = hooklist->link[i].nodeinfo.id;
835
next_hook = "right";
836
} else if (strcmp(hooklist->link[i].peerhook, "right") == 0) {
837
next_node = hooklist->link[i].nodeinfo.id;
838
next_hook = "left";
839
}
840
}
841
free(resp);
842
843
if (first) {
844
ng_rmhook_id(node, hook);
845
first = 0;
846
} else {
847
ng_shutdown_id(node);
848
}
849
if ((node = next_node) == 0)
850
return (0);
851
hook = next_hook;
852
853
goto again;
854
}
855
856
/*
857
* Get the peer hook of a hook on a given node. Skip any tee nodes in between
858
*/
859
int
860
ng_peer_hook_id(ng_ID_t node, const char *hook, char *peerhook)
861
{
862
struct ng_mesg *resp;
863
struct hooklist *hooklist;
864
u_int i;
865
int ret;
866
867
if ((resp = ng_dialog_id(node, NGM_GENERIC_COOKIE, NGM_LISTHOOKS,
868
NULL, 0)) == NULL) {
869
syslog(LOG_ERR, "get hook list: %m");
870
exit(1);
871
}
872
hooklist = (struct hooklist *)(void *)resp->data;
873
874
for (i = 0; i < hooklist->nodeinfo.hooks; i++)
875
if (strcmp(hooklist->link[i].ourhook, hook) == 0)
876
break;
877
878
if (i == hooklist->nodeinfo.hooks) {
879
free(resp);
880
return (-1);
881
}
882
883
node = hooklist->link[i].nodeinfo.id;
884
885
ret = 0;
886
if (strcmp(hooklist->link[i].nodeinfo.type, "tee") == 0) {
887
if (strcmp(hooklist->link[i].peerhook, "left") == 0)
888
ret = ng_peer_hook_id(node, "right", peerhook);
889
else if (strcmp(hooklist->link[i].peerhook, "right") == 0)
890
ret = ng_peer_hook_id(node, "left", peerhook);
891
else
892
strcpy(peerhook, hooklist->link[i].peerhook);
893
894
} else
895
strcpy(peerhook, hooklist->link[i].peerhook);
896
897
free(resp);
898
899
return (ret);
900
}
901
902
903
/*
904
* Now the module is started. Select on the sockets, so that we can get
905
* unsolicited input.
906
*/
907
static void
908
ng_start(void)
909
{
910
if (snmp_node == 0) {
911
if (NgMkSockNode(snmp_nodename, &csock, &dsock) < 0) {
912
syslog(LOG_ERR, "NgMkSockNode: %m");
913
exit(1);
914
}
915
snmp_node = ng_node_id(".:");
916
}
917
918
if ((csock_fd = fd_select(csock, csock_input, NULL, module)) == NULL) {
919
syslog(LOG_ERR, "fd_select failed on csock: %m");
920
return;
921
}
922
if ((dsock_fd = fd_select(dsock, dsock_input, NULL, module)) == NULL) {
923
syslog(LOG_ERR, "fd_select failed on dsock: %m");
924
return;
925
}
926
927
reg_index = or_register(&oid_begemotNg,
928
"The MIB for the NetGraph access module for SNMP.", module);
929
}
930
931
/*
932
* Called, when the module is to be unloaded after it was successfully loaded
933
*/
934
static int
935
ng_fini(void)
936
{
937
struct ngtype *t;
938
939
while ((t = TAILQ_FIRST(&ngtype_list)) != NULL) {
940
TAILQ_REMOVE(&ngtype_list, t, link);
941
free(t);
942
}
943
944
if (csock_fd != NULL)
945
fd_deselect(csock_fd);
946
(void)close(csock);
947
948
if (dsock_fd != NULL)
949
fd_deselect(dsock_fd);
950
(void)close(dsock);
951
952
free(snmp_nodename);
953
954
or_unregister(reg_index);
955
956
return (0);
957
}
958
959
const struct snmp_module config = {
960
"This module implements access to the netgraph sub-system",
961
ng_init,
962
ng_fini,
963
ng_idle,
964
NULL,
965
NULL,
966
ng_start,
967
NULL,
968
netgraph_ctree,
969
netgraph_CTREE_SIZE,
970
NULL
971
};
972
973
int
974
op_ng_config(struct snmp_context *ctx, struct snmp_value *value,
975
u_int sub, u_int iidx __unused, enum snmp_op op)
976
{
977
asn_subid_t which = value->var.subs[sub - 1];
978
int ret;
979
980
switch (op) {
981
982
case SNMP_OP_GETNEXT:
983
abort();
984
985
case SNMP_OP_GET:
986
/*
987
* Come here for GET, GETNEXT and COMMIT
988
*/
989
switch (which) {
990
991
case LEAF_begemotNgControlNodeName:
992
return (string_get(value, snmp_nodename, -1));
993
994
case LEAF_begemotNgResBufSiz:
995
value->v.integer = resbufsiz;
996
break;
997
998
case LEAF_begemotNgTimeout:
999
value->v.integer = timeout;
1000
break;
1001
1002
case LEAF_begemotNgDebugLevel:
1003
value->v.uint32 = debug_level;
1004
break;
1005
1006
default:
1007
abort();
1008
}
1009
return (SNMP_ERR_NOERROR);
1010
1011
case SNMP_OP_SET:
1012
switch (which) {
1013
1014
case LEAF_begemotNgControlNodeName:
1015
/* only at initialisation */
1016
if (community != COMM_INITIALIZE)
1017
return (SNMP_ERR_NOT_WRITEABLE);
1018
1019
if (snmp_node != 0)
1020
return (SNMP_ERR_NOT_WRITEABLE);
1021
1022
if ((ret = string_save(value, ctx, -1, &snmp_nodename))
1023
!= SNMP_ERR_NOERROR)
1024
return (ret);
1025
1026
if (NgMkSockNode(snmp_nodename, &csock, &dsock) < 0) {
1027
syslog(LOG_ERR, "NgMkSockNode: %m");
1028
string_rollback(ctx, &snmp_nodename);
1029
return (SNMP_ERR_GENERR);
1030
}
1031
snmp_node = ng_node_id(".:");
1032
1033
return (SNMP_ERR_NOERROR);
1034
1035
case LEAF_begemotNgResBufSiz:
1036
ctx->scratch->int1 = resbufsiz;
1037
if (value->v.integer < 1024 ||
1038
value->v.integer > 0x10000)
1039
return (SNMP_ERR_WRONG_VALUE);
1040
resbufsiz = value->v.integer;
1041
return (SNMP_ERR_NOERROR);
1042
1043
case LEAF_begemotNgTimeout:
1044
ctx->scratch->int1 = timeout;
1045
if (value->v.integer < 10 ||
1046
value->v.integer > 10000)
1047
return (SNMP_ERR_WRONG_VALUE);
1048
timeout = value->v.integer;
1049
return (SNMP_ERR_NOERROR);
1050
1051
case LEAF_begemotNgDebugLevel:
1052
ctx->scratch->int1 = debug_level;
1053
debug_level = value->v.uint32;
1054
NgSetDebug(debug_level);
1055
return (SNMP_ERR_NOERROR);
1056
}
1057
abort();
1058
1059
case SNMP_OP_ROLLBACK:
1060
switch (which) {
1061
1062
case LEAF_begemotNgControlNodeName:
1063
string_rollback(ctx, &snmp_nodename);
1064
close(csock);
1065
close(dsock);
1066
snmp_node = 0;
1067
return (SNMP_ERR_NOERROR);
1068
1069
case LEAF_begemotNgResBufSiz:
1070
resbufsiz = ctx->scratch->int1;
1071
return (SNMP_ERR_NOERROR);
1072
1073
case LEAF_begemotNgTimeout:
1074
timeout = ctx->scratch->int1;
1075
return (SNMP_ERR_NOERROR);
1076
1077
case LEAF_begemotNgDebugLevel:
1078
debug_level = ctx->scratch->int1;
1079
NgSetDebug(debug_level);
1080
return (SNMP_ERR_NOERROR);
1081
}
1082
abort();
1083
1084
case SNMP_OP_COMMIT:
1085
switch (which) {
1086
1087
case LEAF_begemotNgControlNodeName:
1088
string_commit(ctx);
1089
return (SNMP_ERR_NOERROR);
1090
1091
case LEAF_begemotNgResBufSiz:
1092
case LEAF_begemotNgTimeout:
1093
case LEAF_begemotNgDebugLevel:
1094
return (SNMP_ERR_NOERROR);
1095
}
1096
abort();
1097
}
1098
abort();
1099
}
1100
1101
int
1102
op_ng_stats(struct snmp_context *ctx __unused, struct snmp_value *value,
1103
u_int sub, u_int iidx __unused, enum snmp_op op)
1104
{
1105
switch (op) {
1106
1107
case SNMP_OP_GETNEXT:
1108
abort();
1109
1110
case SNMP_OP_GET:
1111
value->v.uint32 = stats[value->var.subs[sub - 1] - 1];
1112
return (SNMP_ERR_NOERROR);
1113
1114
case SNMP_OP_SET:
1115
return (SNMP_ERR_NOT_WRITEABLE);
1116
1117
case SNMP_OP_ROLLBACK:
1118
case SNMP_OP_COMMIT:
1119
abort();
1120
}
1121
abort();
1122
}
1123
1124
/*
1125
* Netgraph type table
1126
*/
1127
static int
1128
fetch_types(void)
1129
{
1130
struct ngtype *t;
1131
struct typelist *typelist;
1132
struct ng_mesg *resp;
1133
u_int u, i;
1134
1135
if (this_tick <= ngtype_tick)
1136
return (0);
1137
1138
while ((t = TAILQ_FIRST(&ngtype_list)) != NULL) {
1139
TAILQ_REMOVE(&ngtype_list, t, link);
1140
free(t);
1141
}
1142
1143
if ((resp = ng_dialog_id(snmp_node, NGM_GENERIC_COOKIE,
1144
NGM_LISTTYPES, NULL, 0)) == NULL)
1145
return (SNMP_ERR_GENERR);
1146
typelist = (struct typelist *)(void *)resp->data;
1147
1148
for (u = 0; u < typelist->numtypes; u++) {
1149
if ((t = malloc(sizeof(*t))) == NULL) {
1150
free(resp);
1151
return (SNMP_ERR_GENERR);
1152
}
1153
strcpy(t->name, typelist->typeinfo[u].type_name);
1154
t->index.subs[0] = strlen(t->name);
1155
t->index.len = t->index.subs[0] + 1;
1156
for (i = 0; i < t->index.subs[0]; i++)
1157
t->index.subs[i + 1] = t->name[i];
1158
1159
INSERT_OBJECT_OID(t, &ngtype_list);
1160
}
1161
1162
ngtype_tick = this_tick;
1163
1164
free(resp);
1165
return (0);
1166
}
1167
1168
/*
1169
* Try to load the netgraph type with the given name. We assume, that
1170
* type 'type' is implemented in the kernel module 'ng_type'.
1171
*/
1172
static int
1173
ngtype_load(const u_char *name, size_t namelen)
1174
{
1175
char *mod;
1176
int ret;
1177
1178
if ((mod = malloc(namelen + 4)) == NULL)
1179
return (-1);
1180
strcpy(mod, "ng_");
1181
strncpy(mod + 3, name, namelen);
1182
mod[namelen + 3] = '\0';
1183
1184
ret = kldload(mod);
1185
free(mod);
1186
return (ret);
1187
}
1188
1189
/*
1190
* Unload a netgraph type.
1191
*/
1192
static int
1193
ngtype_unload(const u_char *name, size_t namelen)
1194
{
1195
char *mod;
1196
int id;
1197
1198
if ((mod = malloc(namelen + 4)) == NULL)
1199
return (-1);
1200
strcpy(mod, "ng_");
1201
strncpy(mod + 3, name, namelen);
1202
mod[namelen + 3] = '\0';
1203
1204
if ((id = kldfind(mod)) == -1) {
1205
free(mod);
1206
return (-1);
1207
}
1208
free(mod);
1209
return (kldunload(id));
1210
}
1211
1212
int
1213
op_ng_type(struct snmp_context *ctx, struct snmp_value *value,
1214
u_int sub, u_int iidx, enum snmp_op op)
1215
{
1216
asn_subid_t which = value->var.subs[sub - 1];
1217
struct ngtype *t;
1218
u_char *name;
1219
size_t namelen;
1220
int status = 1;
1221
int ret;
1222
1223
switch (op) {
1224
1225
case SNMP_OP_GETNEXT:
1226
if ((ret = fetch_types()) != 0)
1227
return (ret);
1228
if ((t = NEXT_OBJECT_OID(&ngtype_list, &value->var, sub)) == NULL)
1229
return (SNMP_ERR_NOSUCHNAME);
1230
index_append(&value->var, sub, &t->index);
1231
break;
1232
1233
case SNMP_OP_GET:
1234
if ((ret = fetch_types()) != 0)
1235
return (ret);
1236
if ((t = FIND_OBJECT_OID(&ngtype_list, &value->var, sub)) == NULL)
1237
return (SNMP_ERR_NOSUCHNAME);
1238
break;
1239
1240
case SNMP_OP_SET:
1241
if (index_decode(&value->var, sub, iidx, &name, &namelen))
1242
return (SNMP_ERR_NO_CREATION);
1243
if (namelen == 0 || namelen >= NG_TYPESIZ) {
1244
free(name);
1245
return (SNMP_ERR_NO_CREATION);
1246
}
1247
if ((ret = fetch_types()) != 0) {
1248
free(name);
1249
return (ret);
1250
}
1251
t = FIND_OBJECT_OID(&ngtype_list, &value->var, sub);
1252
1253
if (which != LEAF_begemotNgTypeStatus) {
1254
free(name);
1255
if (t != NULL)
1256
return (SNMP_ERR_NOT_WRITEABLE);
1257
return (SNMP_ERR_NO_CREATION);
1258
}
1259
if (!TRUTH_OK(value->v.integer)) {
1260
free(name);
1261
return (SNMP_ERR_WRONG_VALUE);
1262
}
1263
ctx->scratch->int1 = TRUTH_GET(value->v.integer);
1264
ctx->scratch->int1 |= (t != NULL) << 1;
1265
ctx->scratch->ptr2 = name;
1266
ctx->scratch->int2 = namelen;
1267
1268
if (t == NULL) {
1269
/* type not loaded */
1270
if (ctx->scratch->int1 & 1) {
1271
/* request to load */
1272
if (ngtype_load(name, namelen) == -1) {
1273
free(name);
1274
if (errno == ENOENT)
1275
return (SNMP_ERR_INCONS_NAME);
1276
else
1277
return (SNMP_ERR_GENERR);
1278
}
1279
}
1280
} else {
1281
/* is type loaded */
1282
if (!(ctx->scratch->int1 & 1)) {
1283
/* request to unload */
1284
if (ngtype_unload(name, namelen) == -1) {
1285
free(name);
1286
return (SNMP_ERR_GENERR);
1287
}
1288
}
1289
}
1290
return (SNMP_ERR_NOERROR);
1291
1292
case SNMP_OP_ROLLBACK:
1293
ret = SNMP_ERR_NOERROR;
1294
if (!(ctx->scratch->int1 & 2)) {
1295
/* did not exist */
1296
if (ctx->scratch->int1 & 1) {
1297
/* request to load - unload */
1298
if (ngtype_unload(ctx->scratch->ptr2,
1299
ctx->scratch->int2) == -1)
1300
ret = SNMP_ERR_UNDO_FAILED;
1301
}
1302
} else {
1303
/* did exist */
1304
if (!(ctx->scratch->int1 & 1)) {
1305
/* request to unload - reload */
1306
if (ngtype_load(ctx->scratch->ptr2,
1307
ctx->scratch->int2) == -1)
1308
ret = SNMP_ERR_UNDO_FAILED;
1309
}
1310
}
1311
free(ctx->scratch->ptr2);
1312
return (ret);
1313
1314
case SNMP_OP_COMMIT:
1315
free(ctx->scratch->ptr2);
1316
return (SNMP_ERR_NOERROR);
1317
1318
default:
1319
abort();
1320
}
1321
1322
/*
1323
* Come here for GET and COMMIT
1324
*/
1325
switch (which) {
1326
1327
case LEAF_begemotNgTypeStatus:
1328
value->v.integer = status;
1329
break;
1330
1331
default:
1332
abort();
1333
}
1334
return (SNMP_ERR_NOERROR);
1335
}
1336
1337
/*
1338
* Implement the node table
1339
*/
1340
static int
1341
find_node(const struct asn_oid *oid, u_int sub, struct nodeinfo *info)
1342
{
1343
ng_ID_t id = oid->subs[sub];
1344
struct ng_mesg *resp;
1345
1346
if ((resp = ng_dialog_id(id, NGM_GENERIC_COOKIE, NGM_NODEINFO,
1347
NULL, 0)) == NULL)
1348
return (-1);
1349
1350
*info = *(struct nodeinfo *)(void *)resp->data;
1351
free(resp);
1352
return (0);
1353
}
1354
1355
static int
1356
ncmp(const void *p1, const void *p2)
1357
{
1358
const struct nodeinfo *i1 = p1;
1359
const struct nodeinfo *i2 = p2;
1360
1361
if (i1->id < i2->id)
1362
return (-1);
1363
if (i1->id > i2->id)
1364
return (+1);
1365
return (0);
1366
}
1367
1368
static int
1369
find_node_next(const struct asn_oid *oid, u_int sub, struct nodeinfo *info)
1370
{
1371
u_int idxlen = oid->len - sub;
1372
struct ng_mesg *resp;
1373
struct namelist *list;
1374
ng_ID_t id;
1375
u_int i;
1376
1377
if ((resp = ng_dialog_id(snmp_node, NGM_GENERIC_COOKIE, NGM_LISTNODES,
1378
NULL, 0)) == NULL)
1379
return (-1);
1380
list = (struct namelist *)(void *)resp->data;
1381
1382
qsort(list->nodeinfo, list->numnames, sizeof(list->nodeinfo[0]), ncmp);
1383
1384
if (idxlen == 0) {
1385
if (list->numnames == 0) {
1386
free(resp);
1387
return (-1);
1388
}
1389
*info = list->nodeinfo[0];
1390
free(resp);
1391
return (0);
1392
}
1393
id = oid->subs[sub];
1394
1395
for (i = 0; i < list->numnames; i++)
1396
if (list->nodeinfo[i].id > id) {
1397
*info = list->nodeinfo[i];
1398
free(resp);
1399
return (0);
1400
}
1401
1402
free(resp);
1403
return (-1);
1404
}
1405
1406
int
1407
op_ng_node(struct snmp_context *ctx __unused, struct snmp_value *value,
1408
u_int sub, u_int iidx __unused, enum snmp_op op)
1409
{
1410
asn_subid_t which = value->var.subs[sub - 1];
1411
u_int idxlen = value->var.len - sub;
1412
struct nodeinfo nodeinfo;
1413
1414
switch (op) {
1415
1416
case SNMP_OP_GETNEXT:
1417
if (find_node_next(&value->var, sub, &nodeinfo) == -1)
1418
return (SNMP_ERR_NOSUCHNAME);
1419
value->var.len = sub + 1;
1420
value->var.subs[sub] = nodeinfo.id;
1421
break;
1422
1423
case SNMP_OP_GET:
1424
if (idxlen != 1)
1425
return (SNMP_ERR_NOSUCHNAME);
1426
if (find_node(&value->var, sub, &nodeinfo) == -1)
1427
return (SNMP_ERR_NOSUCHNAME);
1428
break;
1429
1430
case SNMP_OP_SET:
1431
if (idxlen != 1)
1432
return (SNMP_ERR_NO_CREATION);
1433
if (find_node(&value->var, sub, &nodeinfo) == -1)
1434
return (SNMP_ERR_NO_CREATION);
1435
return (SNMP_ERR_NOT_WRITEABLE);
1436
1437
case SNMP_OP_ROLLBACK:
1438
case SNMP_OP_COMMIT:
1439
default:
1440
abort();
1441
}
1442
1443
/*
1444
* Come here for GET and COMMIT
1445
*/
1446
switch (which) {
1447
1448
case LEAF_begemotNgNodeStatus:
1449
value->v.integer = 1;
1450
break;
1451
case LEAF_begemotNgNodeName:
1452
return (string_get(value, nodeinfo.name, -1));
1453
case LEAF_begemotNgNodeType:
1454
return (string_get(value, nodeinfo.type, -1));
1455
case LEAF_begemotNgNodeHooks:
1456
value->v.uint32 = nodeinfo.hooks;
1457
break;
1458
1459
default:
1460
abort();
1461
}
1462
return (SNMP_ERR_NOERROR);
1463
}
1464
1465
/*
1466
* Implement the hook table
1467
*/
1468
static int
1469
find_hook(int32_t id, const u_char *hook, size_t hooklen, struct linkinfo *info)
1470
{
1471
struct ng_mesg *resp;
1472
struct hooklist *list;
1473
u_int i;
1474
1475
if ((resp = ng_dialog_id(id, NGM_GENERIC_COOKIE,
1476
NGM_LISTHOOKS, NULL, 0)) == NULL)
1477
return (-1);
1478
1479
list = (struct hooklist *)(void *)resp->data;
1480
1481
for (i = 0; i < list->nodeinfo.hooks; i++) {
1482
if (strlen(list->link[i].ourhook) == hooklen &&
1483
strncmp(list->link[i].ourhook, hook, hooklen) == 0) {
1484
*info = list->link[i];
1485
free(resp);
1486
return (0);
1487
}
1488
}
1489
free(resp);
1490
return (-1);
1491
}
1492
1493
static int
1494
hook_cmp(const void *p1, const void *p2)
1495
{
1496
const struct linkinfo *i1 = p1;
1497
const struct linkinfo *i2 = p2;
1498
1499
if (strlen(i1->ourhook) < strlen(i2->ourhook))
1500
return (-1);
1501
if (strlen(i1->ourhook) > strlen(i2->ourhook))
1502
return (+1);
1503
return (strcmp(i1->ourhook, i2->ourhook));
1504
}
1505
1506
static int
1507
find_hook_next(const struct asn_oid *oid, u_int sub, struct nodeinfo *nodeinfo,
1508
struct linkinfo *linkinfo)
1509
{
1510
u_int idxlen = oid->len - sub;
1511
struct namelist *list;
1512
struct ng_mesg *resp;
1513
struct hooklist *hooks;
1514
struct ng_mesg *resp1;
1515
u_int node_index;
1516
struct asn_oid idx;
1517
u_int i, j;
1518
1519
/*
1520
* Get and sort Node list
1521
*/
1522
if ((resp = ng_dialog_id(snmp_node, NGM_GENERIC_COOKIE, NGM_LISTNODES,
1523
NULL, 0)) == NULL)
1524
return (-1);
1525
list = (struct namelist *)(void *)resp->data;
1526
1527
qsort(list->nodeinfo, list->numnames, sizeof(list->nodeinfo[0]), ncmp);
1528
1529
/*
1530
* If we have no index, take the first node and return the
1531
* first hook.
1532
*/
1533
if (idxlen == 0) {
1534
node_index = 0;
1535
goto return_first_hook;
1536
}
1537
1538
/*
1539
* Locate node
1540
*/
1541
for (node_index = 0; node_index < list->numnames; node_index++)
1542
if (list->nodeinfo[node_index].id >= oid->subs[sub])
1543
break;
1544
1545
/*
1546
* If we have only the node part of the index take, or
1547
* there is no node with that Id, take the first hook of that node.
1548
*/
1549
if (idxlen == 1 || node_index >= list->numnames ||
1550
list->nodeinfo[node_index].id > oid->subs[sub])
1551
goto return_first_hook;
1552
1553
/*
1554
* We had an exact match on the node id and have (at last part)
1555
* of the hook name index. Loop through the hooks of the node
1556
* and find the next one.
1557
*/
1558
if ((resp1 = ng_dialog_id(list->nodeinfo[node_index].id,
1559
NGM_GENERIC_COOKIE, NGM_LISTHOOKS, NULL, 0)) == NULL) {
1560
free(resp);
1561
return (-1);
1562
}
1563
hooks = (struct hooklist *)(void *)resp1->data;
1564
if (hooks->nodeinfo.hooks > 0) {
1565
qsort(hooks->link, hooks->nodeinfo.hooks,
1566
sizeof(hooks->link[0]), hook_cmp);
1567
for (i = 0; i < hooks->nodeinfo.hooks; i++) {
1568
idx.len = strlen(hooks->link[i].ourhook) + 1;
1569
idx.subs[0] = idx.len - 1;
1570
for (j = 0; j < idx.len; j++)
1571
idx.subs[j + 1] = hooks->link[i].ourhook[j];
1572
if (index_compare(oid, sub + 1, &idx) < 0)
1573
break;
1574
}
1575
if (i < hooks->nodeinfo.hooks) {
1576
*nodeinfo = hooks->nodeinfo;
1577
*linkinfo = hooks->link[i];
1578
1579
free(resp);
1580
free(resp1);
1581
return (0);
1582
}
1583
}
1584
1585
/* no hook found larger than the index on the index node - take
1586
* first hook of next node */
1587
free(resp1);
1588
node_index++;
1589
1590
return_first_hook:
1591
while (node_index < list->numnames) {
1592
if ((resp1 = ng_dialog_id(list->nodeinfo[node_index].id,
1593
NGM_GENERIC_COOKIE, NGM_LISTHOOKS, NULL, 0)) == NULL)
1594
break;
1595
hooks = (struct hooklist *)(void *)resp1->data;
1596
if (hooks->nodeinfo.hooks > 0) {
1597
qsort(hooks->link, hooks->nodeinfo.hooks,
1598
sizeof(hooks->link[0]), hook_cmp);
1599
1600
*nodeinfo = hooks->nodeinfo;
1601
*linkinfo = hooks->link[0];
1602
1603
free(resp);
1604
free(resp1);
1605
return (0);
1606
}
1607
1608
/* if we don't have hooks, try next node */
1609
free(resp1);
1610
node_index++;
1611
}
1612
1613
free(resp);
1614
return (-1);
1615
}
1616
1617
int
1618
op_ng_hook(struct snmp_context *ctx __unused, struct snmp_value *value,
1619
u_int sub, u_int iidx, enum snmp_op op)
1620
{
1621
asn_subid_t which = value->var.subs[sub - 1];
1622
struct linkinfo linkinfo;
1623
struct nodeinfo nodeinfo;
1624
u_int32_t lid;
1625
u_char *hook;
1626
size_t hooklen;
1627
u_int i;
1628
1629
switch (op) {
1630
1631
case SNMP_OP_GETNEXT:
1632
if (find_hook_next(&value->var, sub, &nodeinfo, &linkinfo) == -1)
1633
return (SNMP_ERR_NOSUCHNAME);
1634
1635
value->var.len = sub + 1 + 1 + strlen(linkinfo.ourhook);
1636
value->var.subs[sub] = nodeinfo.id;
1637
value->var.subs[sub + 1] = strlen(linkinfo.ourhook);
1638
for (i = 0; i < strlen(linkinfo.ourhook); i++)
1639
value->var.subs[sub + i + 2] =
1640
linkinfo.ourhook[i];
1641
break;
1642
1643
case SNMP_OP_GET:
1644
if (index_decode(&value->var, sub, iidx, &lid,
1645
&hook, &hooklen))
1646
return (SNMP_ERR_NOSUCHNAME);
1647
if (find_hook(lid, hook, hooklen, &linkinfo) == -1) {
1648
free(hook);
1649
return (SNMP_ERR_NOSUCHNAME);
1650
}
1651
free(hook);
1652
break;
1653
1654
case SNMP_OP_SET:
1655
if (index_decode(&value->var, sub, iidx, &lid,
1656
&hook, &hooklen))
1657
return (SNMP_ERR_NO_CREATION);
1658
if (find_hook(lid, hook, hooklen, &linkinfo) == -1) {
1659
free(hook);
1660
return (SNMP_ERR_NO_CREATION);
1661
}
1662
free(hook);
1663
return (SNMP_ERR_NOT_WRITEABLE);
1664
1665
case SNMP_OP_ROLLBACK:
1666
case SNMP_OP_COMMIT:
1667
default:
1668
abort();
1669
1670
}
1671
1672
switch (which) {
1673
1674
case LEAF_begemotNgHookStatus:
1675
value->v.integer = 1;
1676
break;
1677
case LEAF_begemotNgHookPeerNodeId:
1678
value->v.uint32 = linkinfo.nodeinfo.id;
1679
break;
1680
case LEAF_begemotNgHookPeerHook:
1681
return (string_get(value, linkinfo.peerhook, -1));
1682
case LEAF_begemotNgHookPeerType:
1683
return (string_get(value, linkinfo.nodeinfo.type, -1));
1684
default:
1685
abort();
1686
}
1687
return (SNMP_ERR_NOERROR);
1688
}
1689
1690