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