426355: Cannot set source agent address for SNMP traps Author: Jan Safranek <jsafrane@redhat.com> Introduce "v1trapaddress" snmpd config option, which defines agent address set in SNMPv1 traps, i.e. inside the SNMPv1 TRAP-PDU, not UDP packet source address. The agent sets arbitrary local address to the TRAP PDU when this option is ommited. diff -up net-snmp-5.3.2.2/agent/agent_read_config.c.trap-agent-addr net-snmp-5.3.2.2/agent/agent_read_config.c --- net-snmp-5.3.2.2/agent/agent_read_config.c.trap-agent-addr 2008-08-05 15:07:57.000000000 +0200 +++ net-snmp-5.3.2.2/agent/agent_read_config.c 2008-08-05 15:07:57.000000000 +0200 @@ -240,6 +240,9 @@ init_agent_read_config(const char *app) snmpd_free_trapcommunity, "community-string"); #endif /* support for community based SNMP */ + netsnmp_ds_register_config(ASN_OCTET_STR, app, "v1trapaddress", + NETSNMP_DS_APPLICATION_ID, + NETSNMP_DS_AGENT_TRAP_ADDR); #ifdef HAVE_UNISTD_H register_app_config_handler("agentuser", snmpd_set_agent_user, NULL, "userid"); diff -up net-snmp-5.3.2.2/agent/agent_trap.c.trap-agent-addr net-snmp-5.3.2.2/agent/agent_trap.c --- net-snmp-5.3.2.2/agent/agent_trap.c.trap-agent-addr 2007-03-12 20:40:42.000000000 +0100 +++ net-snmp-5.3.2.2/agent/agent_trap.c 2008-08-05 15:07:57.000000000 +0200 @@ -58,6 +58,7 @@ #include <net-snmp/utilities.h> #include <net-snmp/net-snmp-includes.h> +#include <net-snmp/agent/net-snmp-agent-includes.h> #include <net-snmp/agent/agent_trap.h> #include <net-snmp/agent/snmp_agent.h> #include <net-snmp/agent/agent_callbacks.h> @@ -632,6 +633,8 @@ netsnmp_send_traps(int trap, int specifi in_addr_t *pdu_in_addr_t; u_long uptime; struct trap_sink *sink; + const char *v1trapaddress; + int res; DEBUGMSGTL(( "trap", "send_trap %d %d ", trap, specific)); DEBUGMSGOID(("trap", enterprise, enterprise_length)); @@ -785,7 +788,18 @@ netsnmp_send_traps(int trap, int specifi * Ensure that the v1 trap PDU includes the local IP address */ pdu_in_addr_t = (in_addr_t *) template_v1pdu->agent_addr; - *pdu_in_addr_t = get_myaddr(); + v1trapaddress = netsnmp_ds_get_string(NETSNMP_DS_APPLICATION_ID, + NETSNMP_DS_AGENT_TRAP_ADDR); + if (v1trapaddress != NULL) { + /* "v1trapaddress" was specified in config, try to resolve it */ + res = netsnmp_gethostbyname_v4(v1trapaddress, pdu_in_addr_t); + } + if (v1trapaddress == NULL || res < 0) { + /* "v1trapaddress" was not specified in config or the resolution failed, + * try any local address */ + *pdu_in_addr_t = get_myaddr(); + } + } diff -up net-snmp-5.3.2.2/apps/snmptrap.c.trap-agent-addr net-snmp-5.3.2.2/apps/snmptrap.c --- net-snmp-5.3.2.2/apps/snmptrap.c.trap-agent-addr 2005-04-01 00:42:20.000000000 +0200 +++ net-snmp-5.3.2.2/apps/snmptrap.c 2008-08-05 15:07:57.000000000 +0200 @@ -104,26 +104,6 @@ snmp_input(int operation, return 1; } -in_addr_t -parse_address(char *address) -{ - in_addr_t addr; - struct sockaddr_in saddr; - struct hostent *hp; - - if ((addr = inet_addr(address)) != -1) - return addr; - hp = gethostbyname(address); - if (hp == NULL) { - fprintf(stderr, "unknown host: %s\n", address); - exit(1); - } else { - memcpy(&saddr.sin_addr, hp->h_addr, hp->h_length); - return saddr.sin_addr.s_addr; - } - -} - static void optProc(int argc, char *const *argv, int opt) { @@ -289,7 +269,11 @@ main(int argc, char *argv[]) } agent = argv[arg]; if (agent != NULL && strlen(agent) != 0) { - *pdu_in_addr_t = parse_address(agent); + int ret = netsnmp_gethostbyname_v4(agent, pdu_in_addr_t); + if (ret < 0) { + fprintf(stderr, "unknown host: %s\n", agent); + exit(1); + } } else { *pdu_in_addr_t = get_myaddr(); } diff -up net-snmp-5.3.2.2/include/net-snmp/agent/ds_agent.h.trap-agent-addr net-snmp-5.3.2.2/include/net-snmp/agent/ds_agent.h --- net-snmp-5.3.2.2/include/net-snmp/agent/ds_agent.h.trap-agent-addr 2008-08-05 15:07:57.000000000 +0200 +++ net-snmp-5.3.2.2/include/net-snmp/agent/ds_agent.h 2008-08-05 15:07:57.000000000 +0200 @@ -35,6 +35,7 @@ #define NETSNMP_DS_AGENT_PERL_INIT_FILE 4 /* used by embedded perl */ #define NETSNMP_DS_SMUX_SOCKET 5 /* ip:port socket addr */ #define NETSNMP_DS_NOTIF_LOG_CTX 6 /* "" | "snmptrapd" */ +#define NETSNMP_DS_AGENT_TRAP_ADDR 7 /* used as v1 trap agent addres */ /* * integers diff -up net-snmp-5.3.2.2/include/net-snmp/library/system.h.trap-agent-addr net-snmp-5.3.2.2/include/net-snmp/library/system.h --- net-snmp-5.3.2.2/include/net-snmp/library/system.h.trap-agent-addr 2004-10-21 01:52:36.000000000 +0200 +++ net-snmp-5.3.2.2/include/net-snmp/library/system.h 2008-08-05 15:07:57.000000000 +0200 @@ -107,6 +107,11 @@ SOFTWARE. #include <net-snmp/types.h> /* For definition of in_addr_t */ + /* Simply resolve a hostname and return first IPv4 address. + * Returns -1 on error */ + int netsnmp_gethostbyname_v4(const char* name, + in_addr_t *addr_out); + in_addr_t get_myaddr(void); long get_uptime(void); diff -up net-snmp-5.3.2.2/man/snmpd.conf.5.def.trap-agent-addr net-snmp-5.3.2.2/man/snmpd.conf.5.def --- net-snmp-5.3.2.2/man/snmpd.conf.5.def.trap-agent-addr 2008-08-05 15:07:57.000000000 +0200 +++ net-snmp-5.3.2.2/man/snmpd.conf.5.def 2008-08-05 15:07:57.000000000 +0200 @@ -629,6 +629,12 @@ Ordinarily the corresponding MIB object (\fCsnmpEnableAuthenTraps.0\fR) is read-write, but specifying this directive makes this object read-only, and attempts to set the value via SET requests will result in a \fInotWritable\fR error response. +.RE +.IP "v1trapaddress HOST" +defines the agent address, which is inserted into SNMPv1 TRAPs. Arbitrary local +IPv4 address is chosen if this option is ommited. This option is useful mainly +when the agent is visible from outside world by specific address only (e.g. +because of network address translation or firewall). .SS "DisMan Event MIB" The previous directives can be used to configure where traps should be sent, but are not concerned with \fIwhen\fR to send such traps diff -up net-snmp-5.3.2.2/perl/SNMP/SNMP.xs.trap-agent-addr net-snmp-5.3.2.2/perl/SNMP/SNMP.xs --- net-snmp-5.3.2.2/perl/SNMP/SNMP.xs.trap-agent-addr 2008-08-05 15:07:57.000000000 +0200 +++ net-snmp-5.3.2.2/perl/SNMP/SNMP.xs 2008-08-05 15:07:57.000000000 +0200 @@ -108,7 +108,6 @@ typedef struct snmp_xs_cb_data { static void __recalc_timeout _((struct timeval*,struct timeval*, struct timeval*,struct timeval*, int* )); -static in_addr_t __parse_address _((char*)); static int __is_numeric_oid _((char*)); static int __is_leaf _((struct tree*)); static int __translate_appl_type _((char*)); @@ -285,26 +284,6 @@ int *block; } } -static in_addr_t -__parse_address(address) -char *address; -{ - in_addr_t addr; - struct sockaddr_in saddr; - struct hostent *hp; - - if ((addr = inet_addr(address)) != -1) - return addr; - hp = gethostbyname(address); - if (hp == NULL){ - return (-1); /* error value */ - } else { - memcpy(&saddr.sin_addr, hp->h_addr, hp->h_length); - return saddr.sin_addr.s_addr; - } - -} - static int __is_numeric_oid (oidstr) char* oidstr; @@ -4290,12 +4269,12 @@ snmp_trapV1(sess_ref,enterprise,agent,ge agent-address field to that. Otherwise set it to our address. */ if (agent && strlen(agent)) { - if (__parse_address(agent) == -1 && verbose) { - warn("error:trap:invalid agent address: %s", agent); - goto err; - } else { - *((in_addr_t *)pdu->agent_addr) = __parse_address(agent); - } + if (0 > netsnmp_gethostbyname_v4(agent, + (in_addr_t *)pdu->agent_addr)){ + if (verbose) + warn("error:trap:invalid agent address: %s", agent); + goto err; + } } else { *((in_addr_t *)pdu->agent_addr) = get_myaddr(); } diff -up net-snmp-5.3.2.2/snmplib/snmpUDPDomain.c.trap-agent-addr net-snmp-5.3.2.2/snmplib/snmpUDPDomain.c --- net-snmp-5.3.2.2/snmplib/snmpUDPDomain.c.trap-agent-addr 2008-08-05 15:07:57.000000000 +0200 +++ net-snmp-5.3.2.2/snmplib/snmpUDPDomain.c 2008-08-05 15:16:08.000000000 +0200 @@ -847,31 +847,14 @@ netsnmp_sockaddr_in(struct sockaddr_in * /* * Well, it must be a hostname then. */ -#ifdef HAVE_GETHOSTBYNAME - struct hostent *hp = gethostbyname(peername); - if (hp == NULL) { + int ret; + ret = netsnmp_gethostbyname_v4(peername, & addr->sin_addr.s_addr); + if (ret < 0) { DEBUGMSGTL(("netsnmp_sockaddr_in", "hostname (couldn't resolve)\n")); free(peername); return 0; - } else { - if (hp->h_addrtype != AF_INET) { - DEBUGMSGTL(("netsnmp_sockaddr_in", - "hostname (not AF_INET!)\n")); - free(peername); - return 0; - } else { - DEBUGMSGTL(("netsnmp_sockaddr_in", - "hostname (resolved okay)\n")); - memcpy(&(addr->sin_addr), hp->h_addr, hp->h_length); - } } -#else /*HAVE_GETHOSTBYNAME */ - DEBUGMSGTL(("netsnmp_sockaddr_in", - "hostname (no gethostbyname)\n")); - free(peername); - return 0; -#endif /*HAVE_GETHOSTBYNAME */ } } else { DEBUGMSGTL(("netsnmp_sockaddr_in", "NULL peername")); @@ -994,25 +977,11 @@ netsnmp_udp_parse_security(const char *t /* * Nope, wasn't a dotted quad. Must be a hostname. */ -#ifdef HAVE_GETHOSTBYNAME - struct hostent *hp = gethostbyname(source); - if (hp == NULL) { - config_perror("bad source address"); + int ret = netsnmp_gethostbyname_v4(source, &network); + if (ret < 0) { + config_perror("cannot resolve source hostname"); return; - } else { - if (hp->h_addrtype != AF_INET) { - config_perror("no IP address for source hostname"); - return; - } - network = *((in_addr_t *) hp->h_addr); } -#else /*HAVE_GETHOSTBYNAME */ - /* - * Oh dear. - */ - config_perror("cannot resolve source hostname"); - return; -#endif /*HAVE_GETHOSTBYNAME */ } } diff -up net-snmp-5.3.2.2/snmplib/system.c.trap-agent-addr net-snmp-5.3.2.2/snmplib/system.c --- net-snmp-5.3.2.2/snmplib/system.c.trap-agent-addr 2005-08-30 02:29:24.000000000 +0200 +++ net-snmp-5.3.2.2/snmplib/system.c 2008-08-05 15:07:57.000000000 +0200 @@ -77,6 +77,10 @@ SOFTWARE. #if HAVE_NET_IF_H #include <net/if.h> #endif +#if HAVE_NETDB_H +#include <netdb.h> +#endif + #if HAVE_SYS_SOCKIO_H #include <sys/sockio.h> @@ -802,6 +806,84 @@ get_uptime(void) #endif /* ! WIN32 */ /*******************************************************************/ +int +netsnmp_gethostbyname_v4(const char* name, in_addr_t *addr_out) +{ + +#if HAVE_GETADDRINFO + struct addrinfo *addrs = NULL; + struct addrinfo hint; + int err; + + memset(&hint, 0, sizeof hint); + hint.ai_flags = 0; + hint.ai_family = PF_INET; + hint.ai_socktype = SOCK_DGRAM; + hint.ai_protocol = 0; + + err = getaddrinfo(name, NULL, &hint, &addrs); + if (err != 0) { +#if HAVE_GAI_STRERROR + snmp_log(LOG_ERR, "getaddrinfo: %s %s\n", name, + gai_strerror(err)); +#else + snmp_log(LOG_ERR, "getaddrinfo: %s (error %d)\n", name, + err); +#endif + return -1; + } + if (addrs != NULL) { + memcpy(addr_out, + &((struct sockaddr_in *) addrs->ai_addr)->sin_addr, + sizeof(in_addr_t)); + freeaddrinfo(addrs); + } else { + DEBUGMSGTL(("get_thisaddr", + "Failed to resolve IPv4 hostname\n")); + } + return 0; + +#elif HAVE_GETHOSTBYNAME + struct hostent *hp = NULL; + + hp = gethostbyname(host); + if (hp == NULL) { + DEBUGMSGTL(("get_thisaddr", + "hostname (couldn't resolve)\n")); + return -1; + } else if (hp->h_addrtype != AF_INET) { + DEBUGMSGTL(("get_thisaddr", + "hostname (not AF_INET!)\n")); + return -1; + } else { + DEBUGMSGTL(("get_thisaddr", + "hostname (resolved okay)\n")); + memcpy(addr_out, hp->h_addr, sizeof(in_addr_t)); + } + return 0; + +#elif HAVE_GETIPNODEBYNAME + struct hostent *hp = NULL; + int err; + + hp = getipnodebyname(peername, AF_INET, 0, &err); + if (hp == NULL) { + DEBUGMSGTL(("get_thisaddr", + "hostname (couldn't resolve = %d)\n", err)); + return -1; + } + DEBUGMSGTL(("get_thisaddr", + "hostname (resolved okay)\n")); + memcpy(addr_out, hp->h_addr, sizeof(in_addr_t)); + return 0; + +#else /* HAVE_GETIPNODEBYNAME */ + return -1; +#endif +} + +/*******************************************************************/ + #ifndef HAVE_STRNCASECMP /*