/*- * Copyright (c) 2010 The FreeBSD Foundation * All rights reserved. * * This software was developed by Shteryana Sotirova Shopova under * sponsorship from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include "asn1.h" #include "snmp.h" #include "snmpmod.h" #include "target_tree.h" #include "target_oid.h" static struct lmodule *target_module; /* For the registration. */ static const struct asn_oid oid_target = OIDX_snmpTargetMIB; static const struct asn_oid oid_notification = OIDX_snmpNotificationMIB; static uint reg_target; static uint reg_notification; static int32_t target_lock; static const struct asn_oid oid_udp_domain = OIDX_snmpUDPDomain; /* * Internal datastructures and forward declarations. */ static void target_append_index(struct asn_oid *, uint, const char *); static int target_decode_index(const struct asn_oid *, uint, char *); static struct target_address *target_get_address(const struct asn_oid *, uint); static struct target_address *target_get_next_address(const struct asn_oid *, uint); static struct target_param *target_get_param(const struct asn_oid *, uint); static struct target_param *target_get_next_param(const struct asn_oid *, uint); static struct target_notify *target_get_notify(const struct asn_oid *, uint); static struct target_notify *target_get_next_notify(const struct asn_oid *, uint); int op_snmp_target(struct snmp_context *ctx __unused, struct snmp_value *val, uint32_t sub, uint32_t iidx __unused, enum snmp_op op) { struct snmpd_target_stats *ctx_stats; if (val->var.subs[sub - 1] == LEAF_snmpTargetSpinLock) { switch (op) { case SNMP_OP_GET: if (++target_lock == INT32_MAX) target_lock = 0; val->v.integer = target_lock; break; case SNMP_OP_GETNEXT: abort(); case SNMP_OP_SET: if (val->v.integer != target_lock) return (SNMP_ERR_INCONS_VALUE); break; case SNMP_OP_ROLLBACK: /* FALLTHROUGH */ case SNMP_OP_COMMIT: break; } return (SNMP_ERR_NOERROR); } else if (op == SNMP_OP_SET) return (SNMP_ERR_NOT_WRITEABLE); if ((ctx_stats = bsnmpd_get_target_stats()) == NULL) return (SNMP_ERR_GENERR); if (op == SNMP_OP_GET) { switch (val->var.subs[sub - 1]) { case LEAF_snmpUnavailableContexts: val->v.uint32 = ctx_stats->unavail_contexts; break; case LEAF_snmpUnknownContexts: val->v.uint32 = ctx_stats->unknown_contexts; break; default: return (SNMP_ERR_NOSUCHNAME); } return (SNMP_ERR_NOERROR); } abort(); } int op_snmp_target_addrs(struct snmp_context *ctx __unused, struct snmp_value *val, uint32_t sub, uint32_t iidx __unused, enum snmp_op op) { char aname[SNMP_ADM_STR32_SIZ]; struct target_address *addrs; switch (op) { case SNMP_OP_GET: if ((addrs = target_get_address(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_GETNEXT: if ((addrs = target_get_next_address(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); target_append_index(&val->var, sub, addrs->name); break; case SNMP_OP_SET: if ((addrs = target_get_address(&val->var, sub)) == NULL && (val->var.subs[sub - 1] != LEAF_snmpTargetAddrRowStatus || val->v.integer != RowStatus_createAndWait)) return (SNMP_ERR_NOSUCHNAME); if (addrs != NULL) { if (community != COMM_INITIALIZE && addrs->type == StorageType_readOnly) return (SNMP_ERR_NOT_WRITEABLE); if (addrs->status == RowStatus_active && val->v.integer != RowStatus_destroy) return (SNMP_ERR_INCONS_VALUE); } switch (val->var.subs[sub - 1]) { case LEAF_snmpTargetAddrTDomain: return (SNMP_ERR_INCONS_VALUE); case LEAF_snmpTargetAddrTAddress: if (val->v.octetstring.len != SNMP_UDP_ADDR_SIZ) return (SNMP_ERR_INCONS_VALUE); ctx->scratch->ptr1 = malloc(SNMP_UDP_ADDR_SIZ); if (ctx->scratch->ptr1 == NULL) return (SNMP_ERR_GENERR); memcpy(ctx->scratch->ptr1, addrs->address, SNMP_UDP_ADDR_SIZ); memcpy(addrs->address, val->v.octetstring.octets, SNMP_UDP_ADDR_SIZ); break; case LEAF_snmpTargetAddrTagList: if (val->v.octetstring.len >= SNMP_TAG_SIZ) return (SNMP_ERR_INCONS_VALUE); ctx->scratch->int1 = strlen(addrs->taglist) + 1; ctx->scratch->ptr1 = malloc(ctx->scratch->int1); if (ctx->scratch->ptr1 == NULL) return (SNMP_ERR_GENERR); strlcpy(ctx->scratch->ptr1, addrs->taglist, ctx->scratch->int1); memcpy(addrs->taglist, val->v.octetstring.octets, val->v.octetstring.len); addrs->taglist[val->v.octetstring.len] = '\0'; break; case LEAF_snmpTargetAddrParams: if (val->v.octetstring.len >= SNMP_ADM_STR32_SIZ) return (SNMP_ERR_INCONS_VALUE); ctx->scratch->int1 = strlen(addrs->paramname) + 1; ctx->scratch->ptr1 = malloc(ctx->scratch->int1); if (ctx->scratch->ptr1 == NULL) return (SNMP_ERR_GENERR); strlcpy(ctx->scratch->ptr1, addrs->paramname, ctx->scratch->int1); memcpy(addrs->paramname, val->v.octetstring.octets, val->v.octetstring.len); addrs->paramname[val->v.octetstring.len] = '\0'; break; case LEAF_snmpTargetAddrRetryCount: ctx->scratch->int1 = addrs->retry; addrs->retry = val->v.integer; break; case LEAF_snmpTargetAddrTimeout: ctx->scratch->int1 = addrs->timeout; addrs->timeout = val->v.integer / 10; break; case LEAF_snmpTargetAddrStorageType: return (SNMP_ERR_INCONS_VALUE); case LEAF_snmpTargetAddrRowStatus: if (addrs != NULL) { if (val->v.integer != RowStatus_active && val->v.integer != RowStatus_destroy) return (SNMP_ERR_INCONS_VALUE); if (val->v.integer == RowStatus_active && (addrs->address[0] == 0 || strlen(addrs->taglist) == 0 || strlen(addrs->paramname) == 0)) return (SNMP_ERR_INCONS_VALUE); ctx->scratch->int1 = addrs->status; addrs->status = val->v.integer; return (SNMP_ERR_NOERROR); } if (val->v.integer != RowStatus_createAndWait || target_decode_index(&val->var, sub, aname) < 0) return (SNMP_ERR_INCONS_VALUE); if ((addrs = target_new_address(aname)) == NULL) return (SNMP_ERR_GENERR); addrs->status = RowStatus_destroy; if (community != COMM_INITIALIZE) addrs->type = StorageType_volatile; else addrs->type = StorageType_readOnly; break; } return (SNMP_ERR_NOERROR); case SNMP_OP_COMMIT: switch (val->var.subs[sub - 1]) { case LEAF_snmpTargetAddrTAddress: case LEAF_snmpTargetAddrTagList: case LEAF_snmpTargetAddrParams: free(ctx->scratch->ptr1); break; case LEAF_snmpTargetAddrRowStatus: if ((addrs = target_get_address(&val->var, sub)) == NULL) return (SNMP_ERR_GENERR); if (val->v.integer == RowStatus_destroy) return (target_delete_address(addrs)); else if (val->v.integer == RowStatus_active) return (target_activate_address(addrs)); break; default: break; } return (SNMP_ERR_NOERROR); case SNMP_OP_ROLLBACK: if ((addrs = target_get_address(&val->var, sub)) == NULL) return (SNMP_ERR_GENERR); switch (val->var.subs[sub - 1]) { case LEAF_snmpTargetAddrTAddress: memcpy(addrs->address, ctx->scratch->ptr1, SNMP_UDP_ADDR_SIZ); free(ctx->scratch->ptr1); break; case LEAF_snmpTargetAddrTagList: strlcpy(addrs->taglist, ctx->scratch->ptr1, ctx->scratch->int1); free(ctx->scratch->ptr1); break; case LEAF_snmpTargetAddrParams: strlcpy(addrs->paramname, ctx->scratch->ptr1, ctx->scratch->int1); free(ctx->scratch->ptr1); break; case LEAF_snmpTargetAddrRetryCount: addrs->retry = ctx->scratch->int1; break; case LEAF_snmpTargetAddrTimeout: addrs->timeout = ctx->scratch->int1; break; case LEAF_snmpTargetAddrRowStatus: if (ctx->scratch->int1 == RowStatus_destroy) return (target_delete_address(addrs)); break; default: break; } default: abort(); } switch (val->var.subs[sub - 1]) { case LEAF_snmpTargetAddrTDomain: return (oid_get(val, &oid_udp_domain)); case LEAF_snmpTargetAddrTAddress: return (string_get(val, addrs->address, SNMP_UDP_ADDR_SIZ)); case LEAF_snmpTargetAddrTimeout: val->v.integer = addrs->timeout; break; case LEAF_snmpTargetAddrRetryCount: val->v.integer = addrs->retry; break; case LEAF_snmpTargetAddrTagList: return (string_get(val, addrs->taglist, -1)); case LEAF_snmpTargetAddrParams: return (string_get(val, addrs->paramname, -1)); case LEAF_snmpTargetAddrStorageType: val->v.integer = addrs->type; break; case LEAF_snmpTargetAddrRowStatus: val->v.integer = addrs->status; break; default: abort(); } return (SNMP_ERR_NOERROR); } int op_snmp_target_params(struct snmp_context *ctx __unused, struct snmp_value *val, uint32_t sub, uint32_t iidx __unused, enum snmp_op op) { char pname[SNMP_ADM_STR32_SIZ]; struct target_param *param; switch (op) { case SNMP_OP_GET: if ((param = target_get_param(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_GETNEXT: if ((param = target_get_next_param(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); target_append_index(&val->var, sub, param->name); break; case SNMP_OP_SET: if ((param = target_get_param(&val->var, sub)) == NULL && (val->var.subs[sub - 1] != LEAF_snmpTargetParamsRowStatus || val->v.integer != RowStatus_createAndWait)) return (SNMP_ERR_NOSUCHNAME); if (param != NULL) { if (community != COMM_INITIALIZE && param->type == StorageType_readOnly) return (SNMP_ERR_NOT_WRITEABLE); if (param->status == RowStatus_active && val->v.integer != RowStatus_destroy) return (SNMP_ERR_INCONS_VALUE); } switch (val->var.subs[sub - 1]) { case LEAF_snmpTargetParamsMPModel: if (val->v.integer != SNMP_MPM_SNMP_V1 && val->v.integer != SNMP_MPM_SNMP_V2c && val->v.integer != SNMP_MPM_SNMP_V3) return (SNMP_ERR_INCONS_VALUE); ctx->scratch->int1 = param->mpmodel; param->mpmodel = val->v.integer; break; case LEAF_snmpTargetParamsSecurityModel: if (val->v.integer != SNMP_SECMODEL_SNMPv1 && val->v.integer != SNMP_SECMODEL_SNMPv2c && val->v.integer != SNMP_SECMODEL_USM) return (SNMP_ERR_INCONS_VALUE); ctx->scratch->int1 = param->sec_model; param->sec_model = val->v.integer; break; case LEAF_snmpTargetParamsSecurityName: if (val->v.octetstring.len >= SNMP_ADM_STR32_SIZ) return (SNMP_ERR_INCONS_VALUE); ctx->scratch->int1 = strlen(param->secname) + 1; ctx->scratch->ptr1 = malloc(ctx->scratch->int1); if (ctx->scratch->ptr1 == NULL) return (SNMP_ERR_GENERR); strlcpy(ctx->scratch->ptr1, param->secname, ctx->scratch->int1); memcpy(param->secname, val->v.octetstring.octets, val->v.octetstring.len); param->secname[val->v.octetstring.len] = '\0'; break; case LEAF_snmpTargetParamsSecurityLevel: if (val->v.integer != SNMP_noAuthNoPriv && val->v.integer != SNMP_authNoPriv && val->v.integer != SNMP_authPriv) return (SNMP_ERR_INCONS_VALUE); ctx->scratch->int1 = param->sec_level; param->sec_level = val->v.integer; break; case LEAF_snmpTargetParamsStorageType: return (SNMP_ERR_INCONS_VALUE); case LEAF_snmpTargetParamsRowStatus: if (param != NULL) { if (val->v.integer != RowStatus_active && val->v.integer != RowStatus_destroy) return (SNMP_ERR_INCONS_VALUE); if (val->v.integer == RowStatus_active && (param->sec_model == 0 || param->sec_level == 0 || strlen(param->secname) == 0)) return (SNMP_ERR_INCONS_VALUE); ctx->scratch->int1 = param->status; param->status = val->v.integer; return (SNMP_ERR_NOERROR); } if (val->v.integer != RowStatus_createAndWait || target_decode_index(&val->var, sub, pname) < 0) return (SNMP_ERR_INCONS_VALUE); if ((param = target_new_param(pname)) == NULL) return (SNMP_ERR_GENERR); param->status = RowStatus_destroy; if (community != COMM_INITIALIZE) param->type = StorageType_volatile; else param->type = StorageType_readOnly; break; } return (SNMP_ERR_NOERROR); case SNMP_OP_COMMIT: switch (val->var.subs[sub - 1]) { case LEAF_snmpTargetParamsSecurityName: free(ctx->scratch->ptr1); break; case LEAF_snmpTargetParamsRowStatus: if ((param = target_get_param(&val->var, sub)) == NULL) return (SNMP_ERR_GENERR); if (val->v.integer == RowStatus_destroy) return (target_delete_param(param)); break; default: break; } return (SNMP_ERR_NOERROR); case SNMP_OP_ROLLBACK: if ((param = target_get_param(&val->var, sub)) == NULL && (val->var.subs[sub - 1] != LEAF_snmpTargetParamsRowStatus || val->v.integer != RowStatus_createAndWait)) return (SNMP_ERR_GENERR); switch (val->var.subs[sub - 1]) { case LEAF_snmpTargetParamsMPModel: param->mpmodel = ctx->scratch->int1; break; case LEAF_snmpTargetParamsSecurityModel: param->sec_model = ctx->scratch->int1; break; case LEAF_snmpTargetParamsSecurityName: strlcpy(param->secname, ctx->scratch->ptr1, sizeof(param->secname)); free(ctx->scratch->ptr1); break; case LEAF_snmpTargetParamsSecurityLevel: param->sec_level = ctx->scratch->int1; break; case LEAF_snmpTargetParamsRowStatus: if (ctx->scratch->int1 == RowStatus_destroy) return (target_delete_param(param)); break; default: break; } return (SNMP_ERR_NOERROR); default: abort(); } switch (val->var.subs[sub - 1]) { case LEAF_snmpTargetParamsMPModel: val->v.integer = param->mpmodel; break; case LEAF_snmpTargetParamsSecurityModel: val->v.integer = param->sec_model; break; case LEAF_snmpTargetParamsSecurityName: return (string_get(val, param->secname, -1)); case LEAF_snmpTargetParamsSecurityLevel: val->v.integer = param->sec_level; break; case LEAF_snmpTargetParamsStorageType: val->v.integer = param->type; break; case LEAF_snmpTargetParamsRowStatus: val->v.integer = param->status; break; default: abort(); } return (SNMP_ERR_NOERROR); } int op_snmp_notify(struct snmp_context *ctx __unused, struct snmp_value *val, uint32_t sub, uint32_t iidx __unused, enum snmp_op op) { char nname[SNMP_ADM_STR32_SIZ]; struct target_notify *notify; switch (op) { case SNMP_OP_GET: if ((notify = target_get_notify(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_GETNEXT: if ((notify = target_get_next_notify(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); target_append_index(&val->var, sub, notify->name); break; case SNMP_OP_SET: if ((notify = target_get_notify(&val->var, sub)) == NULL && (val->var.subs[sub - 1] != LEAF_snmpNotifyRowStatus || val->v.integer != RowStatus_createAndGo)) return (SNMP_ERR_NOSUCHNAME); if (notify != NULL) { if (community != COMM_INITIALIZE && notify->type == StorageType_readOnly) return (SNMP_ERR_NOT_WRITEABLE); } switch (val->var.subs[sub - 1]) { case LEAF_snmpNotifyTag: if (val->v.octetstring.len >= SNMP_TAG_SIZ) return (SNMP_ERR_INCONS_VALUE); ctx->scratch->int1 = strlen(notify->taglist) + 1; ctx->scratch->ptr1 = malloc(ctx->scratch->int1); if (ctx->scratch->ptr1 == NULL) return (SNMP_ERR_GENERR); strlcpy(ctx->scratch->ptr1, notify->taglist, ctx->scratch->int1); memcpy(notify->taglist, val->v.octetstring.octets, val->v.octetstring.len); notify->taglist[val->v.octetstring.len] = '\0'; break; case LEAF_snmpNotifyType: /* FALLTHROUGH */ case LEAF_snmpNotifyStorageType: return (SNMP_ERR_INCONS_VALUE); case LEAF_snmpNotifyRowStatus: if (notify != NULL) { if (val->v.integer != RowStatus_active && val->v.integer != RowStatus_destroy) return (SNMP_ERR_INCONS_VALUE); ctx->scratch->int1 = notify->status; notify->status = val->v.integer; return (SNMP_ERR_NOERROR); } if (val->v.integer != RowStatus_createAndGo || target_decode_index(&val->var, sub, nname) < 0) return (SNMP_ERR_INCONS_VALUE); if ((notify = target_new_notify(nname)) == NULL) return (SNMP_ERR_GENERR); notify->status = RowStatus_destroy; if (community != COMM_INITIALIZE) notify->type = StorageType_volatile; else notify->type = StorageType_readOnly; break; } return (SNMP_ERR_NOERROR); case SNMP_OP_COMMIT: switch (val->var.subs[sub - 1]) { case LEAF_snmpNotifyTag: free(ctx->scratch->ptr1); break; case LEAF_snmpNotifyRowStatus: notify = target_get_notify(&val->var, sub); if (notify == NULL) return (SNMP_ERR_GENERR); if (val->v.integer == RowStatus_destroy) return (target_delete_notify(notify)); else notify->status = RowStatus_active; break; default: break; } return (SNMP_ERR_NOERROR); case SNMP_OP_ROLLBACK: if ((notify = target_get_notify(&val->var, sub)) == NULL) return (SNMP_ERR_GENERR); switch (val->var.subs[sub - 1]) { case LEAF_snmpNotifyTag: strlcpy(notify->taglist, ctx->scratch->ptr1, ctx->scratch->int1); free(ctx->scratch->ptr1); break; case LEAF_snmpNotifyRowStatus: if (ctx->scratch->int1 == RowStatus_destroy) return (target_delete_notify(notify)); break; default: break; } default: abort(); } switch (val->var.subs[sub - 1]) { case LEAF_snmpNotifyTag: return (string_get(val, notify->taglist, -1)); case LEAF_snmpNotifyType: val->v.integer = snmpNotifyType_trap; break; case LEAF_snmpNotifyStorageType: val->v.integer = notify->type; break; case LEAF_snmpNotifyRowStatus: val->v.integer = notify->status; break; default: abort(); } return (SNMP_ERR_NOERROR); } static void target_append_index(struct asn_oid *oid, uint sub, const char *name) { uint32_t i; oid->len = sub + strlen(name); for (i = 0; i < strlen(name); i++) oid->subs[sub + i] = name[i]; } static int target_decode_index(const struct asn_oid *oid, uint sub, char *name) { uint32_t i, len; if ((len = oid->len - sub) >= SNMP_ADM_STR32_SIZ) return (-1); for (i = 0; i < len; i++) name[i] = oid->subs[sub + i]; name[i] = '\0'; return (0); } static struct target_address * target_get_address(const struct asn_oid *oid, uint sub) { char aname[SNMP_ADM_STR32_SIZ]; struct target_address *addrs; if (target_decode_index(oid, sub, aname) < 0) return (NULL); for (addrs = target_first_address(); addrs != NULL; addrs = target_next_address(addrs)) if (strcmp(aname, addrs->name) == 0) return (addrs); return (NULL); } static struct target_address * target_get_next_address(const struct asn_oid * oid, uint sub) { char aname[SNMP_ADM_STR32_SIZ]; struct target_address *addrs; if (oid->len - sub == 0) return (target_first_address()); if (target_decode_index(oid, sub, aname) < 0) return (NULL); for (addrs = target_first_address(); addrs != NULL; addrs = target_next_address(addrs)) if (strcmp(aname, addrs->name) == 0) return (target_next_address(addrs)); return (NULL); } static struct target_param * target_get_param(const struct asn_oid *oid, uint sub) { char pname[SNMP_ADM_STR32_SIZ]; struct target_param *param; if (target_decode_index(oid, sub, pname) < 0) return (NULL); for (param = target_first_param(); param != NULL; param = target_next_param(param)) if (strcmp(pname, param->name) == 0) return (param); return (NULL); } static struct target_param * target_get_next_param(const struct asn_oid *oid, uint sub) { char pname[SNMP_ADM_STR32_SIZ]; struct target_param *param; if (oid->len - sub == 0) return (target_first_param()); if (target_decode_index(oid, sub, pname) < 0) return (NULL); for (param = target_first_param(); param != NULL; param = target_next_param(param)) if (strcmp(pname, param->name) == 0) return (target_next_param(param)); return (NULL); } static struct target_notify * target_get_notify(const struct asn_oid *oid, uint sub) { char nname[SNMP_ADM_STR32_SIZ]; struct target_notify *notify; if (target_decode_index(oid, sub, nname) < 0) return (NULL); for (notify = target_first_notify(); notify != NULL; notify = target_next_notify(notify)) if (strcmp(nname, notify->name) == 0) return (notify); return (NULL); } static struct target_notify * target_get_next_notify(const struct asn_oid *oid, uint sub) { char nname[SNMP_ADM_STR32_SIZ]; struct target_notify *notify; if (oid->len - sub == 0) return (target_first_notify()); if (target_decode_index(oid, sub, nname) < 0) return (NULL); for (notify = target_first_notify(); notify != NULL; notify = target_next_notify(notify)) if (strcmp(nname, notify->name) == 0) return (target_next_notify(notify)); return (NULL); } static int target_init(struct lmodule *mod, int argc __unused, char *argv[] __unused) { target_module = mod; target_lock = random(); return (0); } static int target_fini(void) { target_flush_all(); or_unregister(reg_target); or_unregister(reg_notification); return (0); } static void target_start(void) { reg_target = or_register(&oid_target, "The MIB module for managing SNMP Management Targets.", target_module); reg_notification = or_register(&oid_notification, "The MIB module for configuring generation of SNMP notifications.", target_module); } static void target_dump(void) { /* XXX: dump the module stats & list of mgmt targets */ } const char target_comment[] = \ "This module implements SNMP Management Target MIB Module defined in RFC 3413."; const struct snmp_module config = { .comment = target_comment, .init = target_init, .fini = target_fini, .start = target_start, .tree = target_ctree, .dump = target_dump, .tree_size = target_CTREE_SIZE, };