net-snmp  5.4.1
agent_trap.c
00001 /*
00002  * agent_trap.c
00003  */
00004 /* Portions of this file are subject to the following copyright(s).  See
00005  * the Net-SNMP's COPYING file for more details and other copyrights
00006  * that may apply:
00007  */
00008 /*
00009  * Portions of this file are copyrighted by:
00010  * Copyright © 2003 Sun Microsystems, Inc. All rights reserved.
00011  * Use is subject to license terms specified in the COPYING file
00012  * distributed with the Net-SNMP package.
00013  */
00020 #include <net-snmp/net-snmp-config.h>
00021 
00022 #if HAVE_UNISTD_H
00023 #include <unistd.h>
00024 #endif
00025 #if HAVE_NETDB_H
00026 #include <netdb.h>
00027 #endif
00028 #if HAVE_STDLIB_H
00029 #include <stdlib.h>
00030 #endif
00031 #if HAVE_STRING_H
00032 #include <string.h>
00033 #else
00034 #include <strings.h>
00035 #endif
00036 #if TIME_WITH_SYS_TIME
00037 # ifdef WIN32
00038 #  include <sys/timeb.h>
00039 # else
00040 #  include <sys/time.h>
00041 # endif
00042 # include <time.h>
00043 #else
00044 # if HAVE_SYS_TIME_H
00045 #  include <sys/time.h>
00046 # else
00047 #  include <time.h>
00048 # endif
00049 #endif
00050 #if HAVE_SYS_SOCKET_H
00051 #include <sys/socket.h>
00052 #elif HAVE_WINSOCK_H
00053 #include <winsock.h>
00054 #endif
00055 #if HAVE_NETINET_IN_H
00056 #include <netinet/in.h>
00057 #endif
00058 #include <net-snmp/utilities.h>
00059 
00060 #include <net-snmp/net-snmp-includes.h>
00061 #include <net-snmp/agent/agent_trap.h>
00062 #include <net-snmp/agent/snmp_agent.h>
00063 #include <net-snmp/agent/agent_callbacks.h>
00064 
00065 #include <net-snmp/agent/agent_module_config.h>
00066 #include <net-snmp/agent/mib_module_config.h>
00067 
00068 #ifdef USING_AGENTX_PROTOCOL_MODULE
00069 #include "agentx/protocol.h"
00070 #endif
00071 
00072 struct trap_sink {
00073     netsnmp_session *sesp;
00074     struct trap_sink *next;
00075     int             pdutype;
00076     int             version;
00077 };
00078 
00079 struct trap_sink *sinks = NULL;
00080 
00081 extern struct timeval starttime;
00082 
00083 oid             objid_enterprisetrap[] = { NETSNMP_NOTIFICATION_MIB };
00084 oid             trap_version_id[] = { NETSNMP_SYSTEM_MIB };
00085 int             enterprisetrap_len;
00086 int             trap_version_id_len;
00087 
00088 #define SNMPV2_TRAPS_PREFIX     SNMP_OID_SNMPMODULES,1,1,5
00089 oid             trap_prefix[]    = { SNMPV2_TRAPS_PREFIX };
00090 oid             cold_start_oid[] = { SNMPV2_TRAPS_PREFIX, 1 };  /* SNMPv2-MIB */
00091 oid             warm_start_oid[] = { SNMPV2_TRAPS_PREFIX, 2 };  /* SNMPv2-MIB */
00092 oid             link_down_oid[]  = { SNMPV2_TRAPS_PREFIX, 3 };  /* IF-MIB */
00093 oid             link_up_oid[]    = { SNMPV2_TRAPS_PREFIX, 4 };  /* IF-MIB */
00094 oid             auth_fail_oid[]  = { SNMPV2_TRAPS_PREFIX, 5 };  /* SNMPv2-MIB */
00095 oid             egp_xxx_oid[]    = { SNMPV2_TRAPS_PREFIX, 99 }; /* ??? */
00096 
00097 #define SNMPV2_TRAP_OBJS_PREFIX SNMP_OID_SNMPMODULES,1,1,4
00098 oid             snmptrap_oid[] = { SNMPV2_TRAP_OBJS_PREFIX, 1, 0 };
00099 oid             snmptrapenterprise_oid[] =
00100     { SNMPV2_TRAP_OBJS_PREFIX, 3, 0 };
00101 oid             sysuptime_oid[] = { SNMP_OID_MIB2, 1, 3, 0 };
00102 size_t          snmptrap_oid_len;
00103 size_t          snmptrapenterprise_oid_len;
00104 size_t          sysuptime_oid_len;
00105 
00106 #define SNMPV2_COMM_OBJS_PREFIX SNMP_OID_SNMPMODULES,18,1
00107 oid             agentaddr_oid[] = { SNMPV2_COMM_OBJS_PREFIX, 3, 0 };
00108 size_t          agentaddr_oid_len;
00109 oid             community_oid[] = { SNMPV2_COMM_OBJS_PREFIX, 4, 0 };
00110 size_t          community_oid_len;
00111 #if !defined(NETSNMP_DISABLE_SNMPV1) || !defined(NETSNMP_DISABLE_SNMPV2C)
00112 char           *snmp_trapcommunity = NULL;
00113 #endif
00114 
00115 
00116 #define SNMP_AUTHENTICATED_TRAPS_ENABLED        1
00117 #define SNMP_AUTHENTICATED_TRAPS_DISABLED       2
00118 
00119 int             snmp_enableauthentraps = SNMP_AUTHENTICATED_TRAPS_DISABLED;
00120 int             snmp_enableauthentrapsset = 0;
00121 
00122 /*
00123  * Prototypes 
00124  */
00125  /*
00126   * static int create_v1_trap_session (const char *, u_short, const char *);
00127   * static int create_v2_trap_session (const char *, u_short, const char *);
00128   * static int create_v2_inform_session (const char *, u_short, const char *);
00129   * static void free_trap_session (struct trap_sink *sp);
00130   * static void send_v1_trap (netsnmp_session *, int, int);
00131   * static void send_v2_trap (netsnmp_session *, int, int, int);
00132   */
00133 
00134 
00135         /*******************
00136          *
00137          * Trap session handling
00138          *
00139          *******************/
00140 
00141 void
00142 init_traps(void)
00143 {
00144     enterprisetrap_len  = OID_LENGTH(objid_enterprisetrap);
00145     trap_version_id_len = OID_LENGTH(trap_version_id);
00146     snmptrap_oid_len    = OID_LENGTH(snmptrap_oid);
00147     snmptrapenterprise_oid_len = OID_LENGTH(snmptrapenterprise_oid);
00148     sysuptime_oid_len   = OID_LENGTH(sysuptime_oid);
00149     agentaddr_oid_len   = OID_LENGTH(agentaddr_oid);
00150     community_oid_len   = OID_LENGTH(community_oid);
00151 }
00152 
00153 static void
00154 free_trap_session(struct trap_sink *sp)
00155 {
00156     snmp_close(sp->sesp);
00157     free(sp);
00158 }
00159 
00160 int
00161 add_trap_session(netsnmp_session * ss, int pdutype, int confirm,
00162                  int version)
00163 {
00164     if (snmp_callback_available(SNMP_CALLBACK_APPLICATION,
00165                                 SNMPD_CALLBACK_REGISTER_NOTIFICATIONS) ==
00166         SNMPERR_SUCCESS) {
00167         /*
00168          * something else wants to handle notification registrations 
00169          */
00170         struct agent_add_trap_args args;
00171         DEBUGMSGTL(("trap", "adding callback trap sink\n"));
00172         args.ss = ss;
00173         args.confirm = confirm;
00174         snmp_call_callbacks(SNMP_CALLBACK_APPLICATION,
00175                             SNMPD_CALLBACK_REGISTER_NOTIFICATIONS,
00176                             (void *) &args);
00177     } else {
00178         /*
00179          * no other support exists, handle it ourselves. 
00180          */
00181         struct trap_sink *new_sink;
00182 
00183         DEBUGMSGTL(("trap", "adding internal trap sink\n"));
00184         new_sink = (struct trap_sink *) malloc(sizeof(*new_sink));
00185         if (new_sink == NULL)
00186             return 0;
00187 
00188         new_sink->sesp = ss;
00189         new_sink->pdutype = pdutype;
00190         new_sink->version = version;
00191         new_sink->next = sinks;
00192         sinks = new_sink;
00193     }
00194     return 1;
00195 }
00196 
00197 int
00198 remove_trap_session(netsnmp_session * ss)
00199 {
00200     struct trap_sink *sp = sinks, *prev = 0;
00201 
00202     while (sp) {
00203         if (sp->sesp == ss) {
00204             if (prev) {
00205                 prev->next = sp->next;
00206             } else {
00207                 sinks = sp->next;
00208             }
00209             /*
00210              * I don't believe you *really* want to close the session here;
00211              * it may still be in use for other purposes.  In particular this
00212              * is awkward for AgentX, since we want to call this function
00213              * from the session's callback.  Let's just free the trapsink
00214              * data structure.  [jbpn]  
00215              */
00216             /*
00217              * free_trap_session(sp);  
00218              */
00219             free(sp);
00220             return 1;
00221         }
00222         prev = sp;
00223         sp = sp->next;
00224     }
00225     return 0;
00226 }
00227 
00228 #if !defined(NETSNMP_DISABLE_SNMPV1) || !defined(NETSNMP_DISABLE_SNMPV2C)
00229 static int
00230 create_trap_session2(const char *sink, const char* sinkport,
00231                      char *com, int version, int pdutype)
00232 {
00233     netsnmp_transport *t;
00234     netsnmp_session session, *sesp;
00235 
00236     memset(&session, 0, sizeof(netsnmp_session));
00237     session.version = version;
00238     if (com) {
00239         session.community = (u_char *) com;
00240         session.community_len = strlen(com);
00241     }
00242 
00243     /*
00244      * for informs, set retries to default
00245      */
00246     if (SNMP_MSG_INFORM == pdutype) {
00247         session.timeout = SNMP_DEFAULT_TIMEOUT;
00248         session.retries = SNMP_DEFAULT_RETRIES;
00249     }
00250 
00251     /*
00252      * if the sink is localhost, bind to localhost, to reduce open ports.
00253      */
00254     if ((NULL == netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID,
00255                                        NETSNMP_DS_LIB_CLIENT_ADDR)) && 
00256         ((0 == strcmp("localhost",sink)) || (0 == strcmp("127.0.0.1",sink))))
00257         session.localname = "localhost";
00258 
00259     t = netsnmp_tdomain_transport_full("snmptrap", sink, 0, NULL, sinkport);
00260     if (t != NULL) {
00261         sesp = snmp_add(&session, t, NULL, NULL);
00262 
00263         if (sesp) {
00264             return add_trap_session(sesp, pdutype,
00265                                     (pdutype == SNMP_MSG_INFORM), version);
00266         }
00267     }
00268     /*
00269      * diagnose snmp_open errors with the input netsnmp_session pointer 
00270      */
00271     snmp_sess_perror("snmpd: create_trap_session", &session);
00272     return 0;
00273 }
00274 
00275 int
00276 create_trap_session(char *sink, u_short sinkport,
00277                     char *com, int version, int pdutype)
00278 {
00279     char buf[sizeof(sinkport) * 3 + 2];
00280     if (sinkport != 0) {
00281         sprintf(buf, ":%hu", sinkport);
00282         snmp_log(LOG_NOTICE,
00283                  "Using a separate port number is deprecated, please correct "
00284                  "the sink specification instead");
00285     }
00286     return create_trap_session2(sink, sinkport ? buf : NULL, com, version,
00287                                 pdutype);
00288 }
00289 
00290 #endif /* support for community based SNMP */
00291 
00292 #ifndef NETSNMP_DISABLE_SNMPV1
00293 static int
00294 create_v1_trap_session(char *sink, const char *sinkport, char *com)
00295 {
00296     return create_trap_session2(sink, sinkport, com,
00297                                 SNMP_VERSION_1, SNMP_MSG_TRAP);
00298 }
00299 #endif
00300 
00301 #ifndef NETSNMP_DISABLE_SNMPV2C
00302 static int
00303 create_v2_trap_session(const char *sink, const char *sinkport, char *com)
00304 {
00305     return create_trap_session2(sink, sinkport, com,
00306                                 SNMP_VERSION_2c, SNMP_MSG_TRAP2);
00307 }
00308 
00309 static int
00310 create_v2_inform_session(const char *sink, const char *sinkport, char *com)
00311 {
00312     return create_trap_session2(sink, sinkport, com,
00313                                 SNMP_VERSION_2c, SNMP_MSG_INFORM);
00314 }
00315 #endif
00316 
00317 void
00318 snmpd_free_trapsinks(void)
00319 {
00320     struct trap_sink *sp = sinks;
00321     while (sp) {
00322         sinks = sinks->next;
00323         free_trap_session(sp);
00324         sp = sinks;
00325     }
00326 }
00327 
00328         /*******************
00329          *
00330          * Trap handling
00331          *
00332          *******************/
00333 
00334 
00335 netsnmp_pdu*
00336 convert_v2pdu_to_v1( netsnmp_pdu* template_v2pdu )
00337 {
00338     netsnmp_pdu           *template_v1pdu;
00339     netsnmp_variable_list *first_vb, *vblist;
00340     netsnmp_variable_list *var;
00341     size_t                 len;
00342 
00343     /*
00344      * Make a copy of the v2 Trap PDU
00345      *   before starting to convert this
00346      *   into a v1 Trap PDU.
00347      */
00348     template_v1pdu = snmp_clone_pdu( template_v2pdu);
00349     if (!template_v1pdu) {
00350         snmp_log(LOG_WARNING,
00351                  "send_trap: failed to copy v1 template PDU\n");
00352         return NULL;
00353     }
00354     template_v1pdu->command = SNMP_MSG_TRAP;
00355     first_vb = template_v1pdu->variables;
00356     vblist   = template_v1pdu->variables;
00357 
00358     /*
00359      * The first varbind should be the system uptime.
00360      */
00361     if (!vblist ||
00362         snmp_oid_compare(vblist->name,  vblist->name_length,
00363                          sysuptime_oid, sysuptime_oid_len)) {
00364         snmp_log(LOG_WARNING,
00365                  "send_trap: no v2 sysUptime varbind to set from\n");
00366         snmp_free_pdu(template_v1pdu);
00367         return NULL;
00368     }
00369     template_v1pdu->time = *vblist->val.integer;
00370     vblist = vblist->next_variable;
00371             
00372     /*
00373      * The second varbind should be the snmpTrapOID.
00374      */
00375     if (!vblist ||
00376         snmp_oid_compare(vblist->name, vblist->name_length,
00377                          snmptrap_oid, snmptrap_oid_len)) {
00378         snmp_log(LOG_WARNING,
00379                  "send_trap: no v2 trapOID varbind to set from\n");
00380         snmp_free_pdu(template_v1pdu);
00381         return NULL;
00382     }
00383 
00384     /*
00385      * Check the v2 varbind list for any varbinds
00386      *  that are not valid in an SNMPv1 trap.
00387      *  This basically means Counter64 values.
00388      *
00389      * RFC 2089 said to omit such varbinds from the list.
00390      * RFC 2576/3584 say to drop the trap completely.
00391      */
00392     for (var = vblist->next_variable; var; var = var->next_variable) {
00393         if ( var->type == ASN_COUNTER64 ) {
00394             snmp_log(LOG_WARNING,
00395                      "send_trap: v1 traps can't carry Counter64 varbinds\n");
00396             snmp_free_pdu(template_v1pdu);
00397             return NULL;
00398         }
00399     }
00400 
00401     /*
00402      * Set the generic & specific trap types,
00403      *    and the enterprise field from the v2 varbind list.
00404      * If there's an agentIPAddress varbind, set the agent_addr too
00405      */
00406     if (!snmp_oid_compare(vblist->val.objid, OID_LENGTH(trap_prefix),
00407                           trap_prefix,       OID_LENGTH(trap_prefix))) {
00408         /*
00409          * For 'standard' traps, extract the generic trap type
00410          *   from the snmpTrapOID value, and take the enterprise
00411          *   value from the 'snmpEnterprise' varbind.
00412          */
00413         template_v1pdu->trap_type =
00414             vblist->val.objid[OID_LENGTH(trap_prefix)] - 1;
00415         template_v1pdu->specific_type = 0;
00416 
00417         var = find_varbind_in_list( vblist,
00418                              snmptrapenterprise_oid,
00419                              snmptrapenterprise_oid_len);
00420         if (var) {
00421             memdup((u_char**)&template_v1pdu->enterprise,
00422                    (const u_char*)var->val.objid, var->val_len);
00423             template_v1pdu->enterprise_length = var->val_len/sizeof(oid);
00424         } else {
00425             template_v1pdu->enterprise        = NULL;
00426             template_v1pdu->enterprise_length = 0;              /* XXX ??? */
00427         }
00428     } else {
00429         /*
00430          * For enterprise-specific traps, split the snmpTrapOID value
00431          *   into enterprise and specific trap
00432          */
00433         len = vblist->val_len / sizeof(oid);
00434         if ( len <= 2 ) {
00435             snmp_log(LOG_WARNING,
00436                      "send_trap: v2 trapOID too short (%d)\n", len);
00437             snmp_free_pdu(template_v1pdu);
00438             return NULL;
00439         }
00440         template_v1pdu->trap_type     = SNMP_TRAP_ENTERPRISESPECIFIC;
00441         template_v1pdu->specific_type = vblist->val.objid[len - 1];
00442         len--;
00443         if (vblist->val.objid[len-1] == 0)
00444             len--;
00445         SNMP_FREE(template_v1pdu->enterprise);
00446         memdup((u_char**)&template_v1pdu->enterprise,
00447                (u_char *)vblist->val.objid, len*sizeof(oid));
00448         template_v1pdu->enterprise_length = len;
00449     }
00450     var = find_varbind_in_list( vblist, agentaddr_oid,
00451                                         agentaddr_oid_len);
00452     if (var) {
00453         memcpy(template_v1pdu->agent_addr,
00454                var->val.string, 4);
00455     }
00456 
00457     /*
00458      * The remainder of the v2 varbind list is kept
00459      * as the v2 varbind list.  Update the PDU and
00460      * free the two redundant varbinds.
00461      */
00462     template_v1pdu->variables = vblist->next_variable;
00463     vblist->next_variable = NULL;
00464     snmp_free_varbind( first_vb );
00465             
00466     return template_v1pdu;
00467 }
00468 
00469 netsnmp_pdu*
00470 convert_v1pdu_to_v2( netsnmp_pdu* template_v1pdu )
00471 {
00472     netsnmp_pdu           *template_v2pdu;
00473     netsnmp_variable_list *first_vb;
00474     netsnmp_variable_list *var;
00475     oid                    enterprise[MAX_OID_LEN];
00476     size_t                 enterprise_len;
00477 
00478     /*
00479      * Make a copy of the v1 Trap PDU
00480      *   before starting to convert this
00481      *   into a v2 Trap PDU.
00482      */
00483     template_v2pdu = snmp_clone_pdu( template_v1pdu);
00484     if (!template_v2pdu) {
00485         snmp_log(LOG_WARNING,
00486                  "send_trap: failed to copy v2 template PDU\n");
00487         return NULL;
00488     }
00489     template_v2pdu->command = SNMP_MSG_TRAP2;
00490     first_vb = template_v2pdu->variables;
00491 
00492     /*
00493      * Insert an snmpTrapOID varbind before the original v1 varbind list
00494      *   either using one of the standard defined trap OIDs,
00495      *   or constructing this from the PDU enterprise & specific trap fields
00496      */
00497     if (template_v1pdu->trap_type == SNMP_TRAP_ENTERPRISESPECIFIC) {
00498         memcpy(enterprise, template_v1pdu->enterprise,
00499                            template_v1pdu->enterprise_length*sizeof(oid));
00500         enterprise_len               = template_v1pdu->enterprise_length;
00501         enterprise[enterprise_len++] = 0;
00502         enterprise[enterprise_len++] = template_v1pdu->specific_type;
00503     } else {
00504         memcpy(enterprise, cold_start_oid, sizeof(cold_start_oid));
00505         enterprise[9]  = template_v1pdu->trap_type+1;
00506         enterprise_len = sizeof(cold_start_oid)/sizeof(oid);
00507     }
00508 
00509     var = NULL;
00510     if (!snmp_varlist_add_variable( &var,
00511              snmptrap_oid, snmptrap_oid_len,
00512              ASN_OBJECT_ID,
00513              (u_char*)enterprise, enterprise_len*sizeof(oid))) {
00514         snmp_log(LOG_WARNING,
00515                  "send_trap: failed to insert copied snmpTrapOID varbind\n");
00516         snmp_free_pdu(template_v2pdu);
00517         return NULL;
00518     }
00519     var->next_variable        = template_v2pdu->variables;
00520     template_v2pdu->variables = var;
00521 
00522     /*
00523      * Insert a sysUptime varbind at the head of the v2 varbind list
00524      */
00525     var = NULL;
00526     if (!snmp_varlist_add_variable( &var,
00527              sysuptime_oid, sysuptime_oid_len,
00528              ASN_TIMETICKS,
00529              (u_char*)&(template_v1pdu->time), 
00530              sizeof(template_v1pdu->time))) {
00531         snmp_log(LOG_WARNING,
00532                  "send_trap: failed to insert copied sysUptime varbind\n");
00533         snmp_free_pdu(template_v2pdu);
00534         return NULL;
00535     }
00536     var->next_variable        = template_v2pdu->variables;
00537     template_v2pdu->variables = var;
00538 
00539     /*
00540      * Append the other three conversion varbinds,
00541      *  (snmpTrapAgentAddr, snmpTrapCommunity & snmpTrapEnterprise)
00542      *  if they're not already present.
00543      *  But don't bomb out completely if there are problems.
00544      */
00545     var = find_varbind_in_list( template_v2pdu->variables,
00546                                 agentaddr_oid, agentaddr_oid_len);
00547     if (!var && (template_v1pdu->agent_addr[0]
00548               || template_v1pdu->agent_addr[1]
00549               || template_v1pdu->agent_addr[2]
00550               || template_v1pdu->agent_addr[3])) {
00551         if (!snmp_varlist_add_variable( &(template_v2pdu->variables),
00552                  agentaddr_oid, agentaddr_oid_len,
00553                  ASN_IPADDRESS,
00554                  (u_char*)&(template_v1pdu->agent_addr), 
00555                  sizeof(template_v1pdu->agent_addr)))
00556             snmp_log(LOG_WARNING,
00557                  "send_trap: failed to append snmpTrapAddr varbind\n");
00558     }
00559     var = find_varbind_in_list( template_v2pdu->variables,
00560                                 community_oid, community_oid_len);
00561     if (!var && template_v1pdu->community) {
00562         if (!snmp_varlist_add_variable( &(template_v2pdu->variables),
00563                  community_oid, community_oid_len,
00564                  ASN_OCTET_STR,
00565                  template_v1pdu->community, 
00566                  template_v1pdu->community_len))
00567             snmp_log(LOG_WARNING,
00568                  "send_trap: failed to append snmpTrapCommunity varbind\n");
00569     }
00570     var = find_varbind_in_list( template_v2pdu->variables,
00571                                 snmptrapenterprise_oid,
00572                                 snmptrapenterprise_oid_len);
00573     if (!var) {
00574         if (!snmp_varlist_add_variable( &(template_v2pdu->variables),
00575                  snmptrapenterprise_oid, snmptrapenterprise_oid_len,
00576                  ASN_OBJECT_ID,
00577                  (u_char*)template_v1pdu->enterprise, 
00578                  template_v1pdu->enterprise_length*sizeof(oid)))
00579             snmp_log(LOG_WARNING,
00580                  "send_trap: failed to append snmpEnterprise varbind\n");
00581     }
00582     return template_v2pdu;
00583 }
00584 
00628 int
00629 netsnmp_send_traps(int trap, int specific,
00630                           oid * enterprise, int enterprise_length,
00631                           netsnmp_variable_list * vars,
00632                           char * context, int flags)
00633 {
00634     netsnmp_pdu           *template_v1pdu;
00635     netsnmp_pdu           *template_v2pdu;
00636     netsnmp_variable_list *vblist = NULL;
00637     netsnmp_variable_list *trap_vb;
00638     netsnmp_variable_list *var;
00639     in_addr_t             *pdu_in_addr_t;
00640     u_long                 uptime;
00641     struct trap_sink *sink;
00642 
00643     DEBUGMSGTL(( "trap", "send_trap %d %d ", trap, specific));
00644     DEBUGMSGOID(("trap", enterprise, enterprise_length));
00645     DEBUGMSG(( "trap", "\n"));
00646 
00647     if (vars) {
00648         vblist = snmp_clone_varbind( vars );
00649         if (!vblist) {
00650             snmp_log(LOG_WARNING,
00651                      "send_trap: failed to clone varbind list\n");
00652             return -1;
00653         }
00654     }
00655 
00656     if ( trap == -1 ) {
00657         /*
00658          * Construct the SNMPv2-style notification PDU
00659          */
00660         if (!vblist) {
00661             snmp_log(LOG_WARNING,
00662                      "send_trap: called with NULL v2 information\n");
00663             return -1;
00664         }
00665         template_v2pdu = snmp_pdu_create(SNMP_MSG_TRAP2);
00666         if (!template_v2pdu) {
00667             snmp_log(LOG_WARNING,
00668                      "send_trap: failed to construct v2 template PDU\n");
00669             snmp_free_varbind(vblist);
00670             return -1;
00671         }
00672 
00673         /*
00674          * Check the varbind list we've been given.
00675          * If it starts with a 'sysUptime.0' varbind, then use that.
00676          * Otherwise, prepend a suitable 'sysUptime.0' varbind.
00677          */
00678         if (!snmp_oid_compare( vblist->name,    vblist->name_length,
00679                                sysuptime_oid, sysuptime_oid_len )) {
00680             template_v2pdu->variables = vblist;
00681             trap_vb  = vblist->next_variable;
00682         } else {
00683             uptime   = netsnmp_get_agent_uptime();
00684             var = NULL;
00685             snmp_varlist_add_variable( &var,
00686                            sysuptime_oid, sysuptime_oid_len,
00687                            ASN_TIMETICKS, (u_char*)&uptime, sizeof(uptime));
00688             if (!var) {
00689                 snmp_log(LOG_WARNING,
00690                      "send_trap: failed to insert sysUptime varbind\n");
00691                 snmp_free_pdu(template_v2pdu);
00692                 snmp_free_varbind(vblist);
00693                 return -1;
00694             }
00695             template_v2pdu->variables = var;
00696             var->next_variable        = vblist;
00697             trap_vb  = vblist;
00698         }
00699 
00700         /*
00701          * 'trap_vb' should point to the snmpTrapOID.0 varbind,
00702          *   identifying the requested trap.  If not then bomb out.
00703          * If it's a 'standard' trap, then we need to append an
00704          *   snmpEnterprise varbind (if there isn't already one).
00705          */
00706         if (!trap_vb ||
00707             snmp_oid_compare(trap_vb->name, trap_vb->name_length,
00708                              snmptrap_oid,  snmptrap_oid_len)) {
00709             snmp_log(LOG_WARNING,
00710                      "send_trap: no v2 trapOID varbind provided\n");
00711             snmp_free_pdu(template_v2pdu);
00712             return -1;
00713         }
00714         if (!snmp_oid_compare(vblist->val.objid, OID_LENGTH(trap_prefix),
00715                               trap_prefix,       OID_LENGTH(trap_prefix))) {
00716             var = find_varbind_in_list( template_v2pdu->variables,
00717                                         snmptrapenterprise_oid,
00718                                         snmptrapenterprise_oid_len);
00719             if (!var &&
00720                 !snmp_varlist_add_variable( &(template_v2pdu->variables),
00721                      snmptrapenterprise_oid, snmptrapenterprise_oid_len,
00722                      ASN_OBJECT_ID,
00723                      (char*)enterprise, enterprise_length*sizeof(oid))) {
00724                 snmp_log(LOG_WARNING,
00725                      "send_trap: failed to add snmpEnterprise to v2 trap\n");
00726                 snmp_free_pdu(template_v2pdu);
00727                 return -1;
00728             }
00729         }
00730             
00731 
00732         /*
00733          * If everything's OK, convert the v2 template into an SNMPv1 trap PDU.
00734          */
00735         template_v1pdu = convert_v2pdu_to_v1( template_v2pdu );
00736         if (!template_v1pdu) {
00737             snmp_log(LOG_WARNING,
00738                      "send_trap: failed to convert v2->v1 template PDU\n");
00739         }
00740 
00741     } else {
00742         /*
00743          * Construct the SNMPv1 trap PDU....
00744          */
00745         template_v1pdu = snmp_pdu_create(SNMP_MSG_TRAP);
00746         if (!template_v1pdu) {
00747             snmp_log(LOG_WARNING,
00748                      "send_trap: failed to construct v1 template PDU\n");
00749             snmp_free_varbind(vblist);
00750             return -1;
00751         }
00752         template_v1pdu->trap_type     = trap;
00753         template_v1pdu->specific_type = specific;
00754         template_v1pdu->time          = netsnmp_get_agent_uptime();
00755 
00756         if (snmp_clone_mem((void **) &template_v1pdu->enterprise,
00757                        enterprise, enterprise_length * sizeof(oid))) {
00758             snmp_log(LOG_WARNING,
00759                      "send_trap: failed to set v1 enterprise OID\n");
00760             snmp_free_varbind(vblist);
00761             snmp_free_pdu(template_v1pdu);
00762             return -1;
00763         }
00764         template_v1pdu->enterprise_length = enterprise_length;
00765 
00766         template_v1pdu->flags    |= UCD_MSG_FLAG_FORCE_PDU_COPY;
00767         template_v1pdu->variables = vblist;
00768 
00769         /*
00770          * ... and convert it into an SNMPv2-style notification PDU.
00771          */
00772 
00773         template_v2pdu = convert_v1pdu_to_v2( template_v1pdu );
00774         if (!template_v2pdu) {
00775             snmp_log(LOG_WARNING,
00776                      "send_trap: failed to convert v1->v2 template PDU\n");
00777         }
00778     }
00779 
00780     /*
00781      * Check whether we're ignoring authFail traps
00782      */
00783     if (template_v1pdu) {
00784       if (template_v1pdu->trap_type == SNMP_TRAP_AUTHFAIL &&
00785         snmp_enableauthentraps == SNMP_AUTHENTICATED_TRAPS_DISABLED) {
00786         snmp_free_pdu(template_v1pdu);
00787         snmp_free_pdu(template_v2pdu);
00788         return 0;
00789       }
00790 
00791     /*
00792      * Ensure that the v1 trap PDU includes the local IP address
00793      */
00794        pdu_in_addr_t = (in_addr_t *) template_v1pdu->agent_addr;
00795       *pdu_in_addr_t = get_myaddr();
00796     }
00797 
00798 
00799     /*
00800      *  Now loop through the list of trap sinks
00801      *   and call the trap callback routines,
00802      *   providing an appropriately formatted PDU in each case
00803      */
00804     for (sink = sinks; sink; sink = sink->next) {
00805 #ifndef NETSNMP_DISABLE_SNMPV1
00806         if (sink->version == SNMP_VERSION_1) {
00807           if (template_v1pdu) {
00808             send_trap_to_sess(sink->sesp, template_v1pdu);
00809           }
00810         } else {
00811 #endif
00812           if (template_v2pdu) {
00813             template_v2pdu->command = sink->pdutype;
00814             send_trap_to_sess(sink->sesp, template_v2pdu);
00815           }
00816 #ifndef NETSNMP_DISABLE_SNMPV1
00817         }
00818 #endif
00819     }
00820     if (template_v1pdu)
00821         snmp_call_callbacks(SNMP_CALLBACK_APPLICATION,
00822                         SNMPD_CALLBACK_SEND_TRAP1, template_v1pdu);
00823     if (template_v2pdu)
00824         snmp_call_callbacks(SNMP_CALLBACK_APPLICATION,
00825                         SNMPD_CALLBACK_SEND_TRAP2, template_v2pdu);
00826     snmp_free_pdu(template_v1pdu);
00827     snmp_free_pdu(template_v2pdu);
00828     return 0;
00829 }
00830 
00831 
00832 void
00833 send_enterprise_trap_vars(int trap,
00834                           int specific,
00835                           oid * enterprise, int enterprise_length,
00836                           netsnmp_variable_list * vars)
00837 {
00838     netsnmp_send_traps(trap, specific,
00839                        enterprise, enterprise_length,
00840                        vars, NULL, 0);
00841     return;
00842 }
00843 
00849 int
00850 handle_inform_response(int op, netsnmp_session * session,
00851                        int reqid, netsnmp_pdu *pdu,
00852                        void *magic)
00853 {
00854     /* XXX: possibly stats update */
00855     switch (op) {
00856 
00857     case NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE:
00858         snmp_increment_statistic(STAT_SNMPINPKTS);
00859         DEBUGMSGTL(("trap", "received the inform response for reqid=%d\n",
00860                     reqid));
00861         break;
00862 
00863     case NETSNMP_CALLBACK_OP_TIMED_OUT:
00864         DEBUGMSGTL(("trap",
00865                     "received a timeout sending an inform for reqid=%d\n",
00866                     reqid));
00867         break;
00868 
00869     case NETSNMP_CALLBACK_OP_SEND_FAILED:
00870         DEBUGMSGTL(("trap",
00871                     "failed to send an inform for reqid=%d\n",
00872                     reqid));
00873         break;
00874 
00875     default:
00876         DEBUGMSGTL(("trap", "received op=%d for reqid=%d when trying to send an inform\n", op, reqid));
00877     }
00878 
00879     return 1;
00880 }
00881 
00882 
00883 /*
00884  * send_trap_to_sess: sends a trap to a session but assumes that the
00885  * pdu is constructed correctly for the session type. 
00886  */
00887 void
00888 send_trap_to_sess(netsnmp_session * sess, netsnmp_pdu *template_pdu)
00889 {
00890     netsnmp_pdu    *pdu;
00891     int            result;
00892     char           tmp[SPRINT_MAX_LEN];
00893     int            len;
00894 
00895 
00896     if (!sess || !template_pdu)
00897         return;
00898 
00899     DEBUGMSGTL(("trap", "sending trap type=%d, version=%d\n",
00900                 template_pdu->command, sess->version));
00901 
00902 #ifndef NETSNMP_DISABLE_SNMPV1
00903     if (sess->version == SNMP_VERSION_1 &&
00904         (template_pdu->command != SNMP_MSG_TRAP))
00905         return;                 /* Skip v1 sinks for v2 only traps */
00906     if (sess->version != SNMP_VERSION_1 &&
00907         (template_pdu->command == SNMP_MSG_TRAP))
00908         return;                 /* Skip v2+ sinks for v1 only traps */
00909 #endif
00910     template_pdu->version = sess->version;
00911     pdu = snmp_clone_pdu(template_pdu);
00912     pdu->sessid = sess->sessid; /* AgentX only ? */
00913 
00914     if ( template_pdu->command == SNMP_MSG_INFORM
00915 #ifdef USING_AGENTX_PROTOCOL_MODULE
00916          || template_pdu->command == AGENTX_MSG_NOTIFY
00917 #endif
00918        ) {
00919         result =
00920             snmp_async_send(sess, pdu, &handle_inform_response, NULL);
00921         
00922     } else {
00923         if ((sess->version == SNMP_VERSION_3) &&
00924                 (pdu->command == SNMP_MSG_TRAP2) &&
00925                 (pdu->securityEngineIDLen == 0)) {
00926             len = snmpv3_get_engineID(tmp, sizeof(tmp));
00927             memdup(&pdu->securityEngineID, tmp, len);
00928             pdu->securityEngineIDLen = len;
00929         }
00930 
00931         result = snmp_send(sess, pdu);
00932     }
00933 
00934     if (result == 0) {
00935         snmp_sess_perror("snmpd: send_trap", sess);
00936         snmp_free_pdu(pdu);
00937     } else {
00938         snmp_increment_statistic(STAT_SNMPOUTTRAPS);
00939         snmp_increment_statistic(STAT_SNMPOUTPKTS);
00940     }
00941 }
00942 
00943 void
00944 send_trap_vars(int trap, int specific, netsnmp_variable_list * vars)
00945 {
00946     if (trap == SNMP_TRAP_ENTERPRISESPECIFIC)
00947         send_enterprise_trap_vars(trap, specific, objid_enterprisetrap,
00948                                   OID_LENGTH(objid_enterprisetrap), vars);
00949     else
00950         send_enterprise_trap_vars(trap, specific, trap_version_id,
00951                                   OID_LENGTH(trap_version_id), vars);
00952 }
00953 
00977 void
00978 send_easy_trap(int trap, int specific)
00979 {
00980     send_trap_vars(trap, specific, NULL);
00981 }
00982 
01006 void
01007 send_v2trap(netsnmp_variable_list * vars)
01008 {
01009     send_trap_vars(-1, -1, vars);
01010 }
01011 
01012 void
01013 send_trap_pdu(netsnmp_pdu *pdu)
01014 {
01015     send_trap_vars(-1, -1, pdu->variables);
01016 }
01017 
01018 
01019 
01020         /*******************
01021          *
01022          * Config file handling
01023          *
01024          *******************/
01025 
01026 void
01027 snmpd_parse_config_authtrap(const char *token, char *cptr)
01028 {
01029     int             i;
01030 
01031     i = atoi(cptr);
01032     if (i == 0) {
01033         if (strcmp(cptr, "enable") == 0) {
01034             i = SNMP_AUTHENTICATED_TRAPS_ENABLED;
01035         } else if (strcmp(cptr, "disable") == 0) {
01036             i = SNMP_AUTHENTICATED_TRAPS_DISABLED;
01037         }
01038     }
01039     if (i < 1 || i > 2) {
01040         config_perror("authtrapenable must be 1 or 2");
01041     } else {
01042         if (strcmp(token, "pauthtrapenable") == 0) {
01043             if (snmp_enableauthentrapsset < 0) {
01044                 /*
01045                  * This is bogus (and shouldn't happen anyway) -- the value
01046                  * of snmpEnableAuthenTraps.0 is already configured
01047                  * read-only.  
01048                  */
01049                 snmp_log(LOG_WARNING,
01050                          "ignoring attempted override of read-only snmpEnableAuthenTraps.0\n");
01051                 return;
01052             } else {
01053                 snmp_enableauthentrapsset++;
01054             }
01055         } else {
01056             if (snmp_enableauthentrapsset > 0) {
01057                 /*
01058                  * This is bogus (and shouldn't happen anyway) -- we already
01059                  * read a persistent value of snmpEnableAuthenTraps.0, which
01060                  * we should ignore in favour of this one.  
01061                  */
01062                 snmp_log(LOG_WARNING,
01063                          "ignoring attempted override of read-only snmpEnableAuthenTraps.0\n");
01064                 /*
01065                  * Fall through and copy in this value.  
01066                  */
01067             }
01068             snmp_enableauthentrapsset = -1;
01069         }
01070         snmp_enableauthentraps = i;
01071     }
01072 }
01073 
01074 #ifndef NETSNMP_DISABLE_SNMPV1
01075 void
01076 snmpd_parse_config_trapsink(const char *token, char *cptr)
01077 {
01078     char            tmpbuf[1024];
01079     char           *sp, *cp, *pp = NULL;
01080     char            *st;
01081 
01082     if (!snmp_trapcommunity)
01083         snmp_trapcommunity = strdup("public");
01084     sp = strtok_r(cptr, " \t\n", &st);
01085     cp = strtok_r(NULL, " \t\n", &st);
01086     if (cp)
01087         pp = strtok_r(NULL, " \t\n", &st);
01088     if (pp)
01089         config_pwarn("The separate port argument to trapsink is deprecated");
01090     if (create_v1_trap_session(sp, pp, cp ? cp : snmp_trapcommunity) == 0) {
01091         snprintf(tmpbuf, sizeof(tmpbuf), "cannot create trapsink: %s", cptr);
01092         tmpbuf[sizeof(tmpbuf)-1] = '\0';
01093         config_perror(tmpbuf);
01094     }
01095 }
01096 #endif
01097 
01098 #ifndef NETSNMP_DISABLE_SNMPV2C
01099 void
01100 snmpd_parse_config_trap2sink(const char *word, char *cptr)
01101 {
01102     char            tmpbuf[1024];
01103     char           *sp, *cp, *pp = NULL;
01104     int             sinkport;
01105     char            *st;
01106 
01107     if (!snmp_trapcommunity)
01108         snmp_trapcommunity = strdup("public");
01109     sp = strtok_r(cptr, " \t\n", &st);
01110     cp = strtok_r(NULL, " \t\n", &st);
01111     if (cp)
01112         pp = strtok_r(NULL, " \t\n", &st);
01113     if (pp)
01114         config_pwarn("The separate port argument to trapsink2 is deprecated");
01115     if (create_v2_trap_session(sp, pp, cp ? cp : snmp_trapcommunity) == 0) {
01116         snprintf(tmpbuf, sizeof(tmpbuf), "cannot create trap2sink: %s", cptr);
01117         tmpbuf[sizeof(tmpbuf)-1] = '\0';
01118         config_perror(tmpbuf);
01119     }
01120 }
01121 
01122 void
01123 snmpd_parse_config_informsink(const char *word, char *cptr)
01124 {
01125     char            tmpbuf[1024];
01126     char           *sp, *cp, *pp = NULL;
01127     int             sinkport;
01128     char            *st;
01129 
01130     if (!snmp_trapcommunity)
01131         snmp_trapcommunity = strdup("public");
01132     sp = strtok_r(cptr, " \t\n", &st);
01133     cp = strtok_r(NULL, " \t\n", &st);
01134     if (cp)
01135         pp = strtok_r(NULL, " \t\n", &st);
01136     if (pp)
01137         config_pwarn("The separate port argument to informsink is deprecated");
01138     if (create_v2_inform_session(sp, pp, cp ? cp : snmp_trapcommunity) == 0) {
01139         snprintf(tmpbuf, sizeof(tmpbuf), "cannot create informsink: %s", cptr);
01140         tmpbuf[sizeof(tmpbuf)-1] = '\0';
01141         config_perror(tmpbuf);
01142     }
01143 }
01144 #endif
01145 
01146 /*
01147  * this must be standardized somewhere, right? 
01148  */
01149 #define MAX_ARGS 128
01150 
01151 static int      traptype;
01152 
01153 static void
01154 trapOptProc(int argc, char *const *argv, int opt)
01155 {
01156     switch (opt) {
01157     case 'C':
01158         while (*optarg) {
01159             switch (*optarg++) {
01160             case 'i':
01161                 traptype = SNMP_MSG_INFORM;
01162                 break;
01163             default:
01164                 config_perror("unknown argument passed to -C");
01165                 break;
01166             }
01167         }
01168         break;
01169     }
01170 }
01171 
01172 
01173 void
01174 snmpd_parse_config_trapsess(const char *word, char *cptr)
01175 {
01176     char           *argv[MAX_ARGS], *cp = cptr, tmp[SPRINT_MAX_LEN];
01177     int             argn, arg;
01178     netsnmp_session session, *ss;
01179     size_t          len;
01180 
01181     /*
01182      * inform or trap?  default to trap 
01183      */
01184     traptype = SNMP_MSG_TRAP2;
01185 
01186     /*
01187      * create the argv[] like array 
01188      */
01189     argv[0] = strdup("snmpd-trapsess"); /* bogus entry for getopt() */
01190     for (argn = 1; cp && argn < MAX_ARGS; argn++) {
01191         cp = copy_nword(cp, tmp, SPRINT_MAX_LEN);
01192         argv[argn] = strdup(tmp);
01193     }
01194 
01195     arg = snmp_parse_args(argn, argv, &session, "C:", trapOptProc);
01196 
01197     ss = snmp_add(&session,
01198                   netsnmp_transport_open_client("snmptrap", session.peername),
01199                   NULL, NULL);
01200     for (; argn > 0; argn--) {
01201         free(argv[argn - 1]);
01202     }
01203 
01204     if (!ss) {
01205         config_perror
01206             ("snmpd: failed to parse this line or the remote trap receiver is down.  Possible cause:");
01207         snmp_sess_perror("snmpd: snmpd_parse_config_trapsess()", &session);
01208         return;
01209     }
01210 
01211     /*
01212      * If this is an SNMPv3 TRAP session, then the agent is
01213      *   the authoritative engine, so set the engineID accordingly
01214      */
01215     if (ss->version == SNMP_VERSION_3 &&
01216         traptype != SNMP_MSG_INFORM   &&
01217         ss->securityEngineIDLen == 0) {
01218             len = snmpv3_get_engineID( tmp, sizeof(tmp));
01219             memdup(&ss->securityEngineID, tmp, len);
01220             ss->securityEngineIDLen = len;
01221     }
01222 
01223 #ifndef NETSNMP_DISABLE_SNMPV1
01224     if (ss->version == SNMP_VERSION_1) {
01225         add_trap_session(ss, SNMP_MSG_TRAP, 0, SNMP_VERSION_1);
01226     } else {
01227 #endif
01228         add_trap_session(ss, traptype, (traptype == SNMP_MSG_INFORM),
01229                          ss->version);
01230 #ifndef NETSNMP_DISABLE_SNMPV1
01231     }
01232 #endif
01233 }
01234 
01235 #if !defined(NETSNMP_DISABLE_SNMPV1) || !defined(NETSNMP_DISABLE_SNMPV2C)
01236 void
01237 snmpd_parse_config_trapcommunity(const char *word, char *cptr)
01238 {
01239     if (snmp_trapcommunity != NULL) {
01240         free(snmp_trapcommunity);
01241     }
01242     snmp_trapcommunity = (char *) malloc(strlen(cptr) + 1);
01243     if (snmp_trapcommunity != NULL) {
01244         copy_nword(cptr, snmp_trapcommunity, strlen(cptr) + 1);
01245     }
01246 }
01247 
01248 void
01249 snmpd_free_trapcommunity(void)
01250 {
01251     if (snmp_trapcommunity) {
01252         free(snmp_trapcommunity);
01253         snmp_trapcommunity = NULL;
01254     }
01255 }
01256 #endif
01257