ports/156178: Fix for net-mgmt/net-snmp in multihomed environments

Nikolay Denev ndenev at gmail.com
Mon Apr 4 15:00:28 UTC 2011


>Number:         156178
>Category:       ports
>Synopsis:       Fix for net-mgmt/net-snmp in multihomed environments
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    freebsd-ports-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          change-request
>Submitter-Id:   current-users
>Arrival-Date:   Mon Apr 04 15:00:21 UTC 2011
>Closed-Date:
>Last-Modified:
>Originator:     Nikolay Denev
>Release:        8.2-STABLE
>Organization:
>Environment:
FreeBSD nas.totalterror.net 8.2-STABLE FreeBSD 8.2-STABLE #120: Wed Mar 30 10:26:07 EEST 2011     ndenev at nas.totalterror.net:/usr/obj/usr/src/sys/NAS  amd64
>Description:
Net-Snmp uses a Linux feature IP_PKTINFO to handle multihomed snmp enabled hosts, and make sure that
the snmp packets in response to a snmp query will be returned with source the same IP that was queried. 
On FreeBSD the issue is that when queried the host will respond with the IP assigned by the IP stack -  the address of the interface that has the route to the host that sent the query. 
The snmp client tools handle this, but if one uses statefull firewall in between the answer can be dropped.

So basically this makes net-snmp on freebsd to use IP_RECVDSTADDR to provide the same "predictable" behavior as on Linux.

I've posted this patch to the net-snmp developers but got no response :
http://sourceforge.net/tracker/?func=detail&aid=3175640&group_id=12694&atid=312694
>How-To-Repeat:
                         |
                         |       
     +--(igb0 10.0.0.1/24)-+
     |                                          |
     |            ROUTER                |
     |                                          |
     +-(igb1 10.10.0.1/24)-+
                         |
                         |
     +-(igb0 10.10.0.2/24)-+
     |                                          |
     |                NMS                  |
     |                                          |
     +-----------------+


When the host "NMS" tries to query the host "ROUTER" on it's external address 10.0.0.1, 
the response will get from it's internal address 10.10.0.1.
>Fix:
Apply the provided patch.

Patch attached with submission follows:

diff -ruN net-snmp.orig/files/patch-snmpUDPDomain.c net-snmp/files/patch-snmpUDPDomain.c
--- net-snmp.orig/files/patch-snmpUDPDomain.c	1970-01-01 02:00:00.000000000 +0200
+++ net-snmp/files/patch-snmpUDPDomain.c	2011-04-04 17:35:58.273758907 +0300
@@ -0,0 +1,137 @@
+--- snmplib/snmpUDPDomain.c.orig	2009-07-08 17:12:01.000000000 +0200
++++ snmplib/snmpUDPDomain.c	2011-02-08 12:49:30.000000000 +0100
+@@ -128,14 +128,20 @@
+ 
+ 
+ #if defined(linux) && defined(IP_PKTINFO)
+-
+-# define netsnmp_dstaddr(x) (&(((struct in_pktinfo *)(CMSG_DATA(x)))->ipi_addr))
++#define netsnmp_dstaddr(x) (&(((struct in_pktinfo *)(CMSG_DATA(x)))->ipi_addr))
++#elif defined(IP_RECVDSTADDR)
++#define netsnmp_dstaddr(x) (&(struct cmsghdr *)(CMSG_DATA(x)))
++#endif
+ 
+ int netsnmp_udp_recvfrom(int s, void *buf, int len, struct sockaddr *from, socklen_t *fromlen, struct in_addr *dstip)
+ {
+     int r;
+     struct iovec iov[1];
++#if defined(linux) && defined(IP_PKTINFO)
+     char cmsg[CMSG_SPACE(sizeof(struct in_pktinfo))];
++#elif defined(IP_RECVDSTADDR)
++    char cmsg[CMSG_SPACE(sizeof(struct in_addr))];
++#endif
+     struct cmsghdr *cmsgptr;
+     struct msghdr msg;
+ 
+@@ -157,6 +163,7 @@
+     }
+     
+     DEBUGMSGTL(("netsnmp_udp", "got source addr: %s\n", inet_ntoa(((struct sockaddr_in *)from)->sin_addr)));
++#if defined(linux) && defined(IP_PKTINFO)
+     for (cmsgptr = CMSG_FIRSTHDR(&msg); cmsgptr != NULL; cmsgptr = CMSG_NXTHDR(&msg, cmsgptr)) {
+         if (cmsgptr->cmsg_level == SOL_IP && cmsgptr->cmsg_type == IP_PKTINFO) {
+             memcpy((void *) dstip, netsnmp_dstaddr(cmsgptr), sizeof(struct in_addr));
+@@ -164,9 +171,20 @@
+                     inet_ntoa(*dstip)));
+         }
+     }
++#elif defined(IP_RECVDSTADDR)
++    for (cmsgptr = CMSG_FIRSTHDR(&msg); cmsgptr != NULL; cmsgptr = CMSG_NXTHDR(&msg, cmsgptr)) {
++        if (cmsgptr->cmsg_level == IPPROTO_IP && cmsgptr->cmsg_type == IP_RECVDSTADDR) {
++            memcpy((void *) dstip, CMSG_DATA(cmsgptr), sizeof(struct in_addr));
++            DEBUGMSGTL(("netsnmp_udp", "got destination (local) addr %s\n",
++                    inet_ntoa(*dstip)));
++        }
++    }
++#endif
+     return r;
+ }
+ 
++
++#if defined(linux) && defined(IP_PKTINFO)
+ int netsnmp_udp_sendto(int fd, struct in_addr *srcip, struct sockaddr *remote,
+ 			void *data, int len)
+ {
+@@ -194,7 +212,35 @@
+ 
+     return sendmsg(fd, &m, MSG_NOSIGNAL|MSG_DONTWAIT);
+ }
+-#endif /* linux && IP_PKTINFO */
++#elif defined(IP_RECVDSTADDR)
++int netsnmp_udp_sendto(int fd, struct in_addr *srcip, struct sockaddr *remote,
++			void *data, int len)
++{
++    struct iovec iov = { data, len };
++    struct cmsghdr *cm;
++    struct in_addr ip;
++    struct msghdr m;
++    char   cmbuf[CMSG_SPACE(sizeof(struct in_addr))];
++
++    memset(&m, 0, sizeof(struct msghdr));
++    m.msg_name		= remote;
++    m.msg_namelen	= sizeof(struct sockaddr_in);
++    m.msg_iov		= &iov;
++    m.msg_iovlen	= 1;
++    m.msg_control	= cmbuf;
++    m.msg_controllen	= sizeof(cmbuf);
++    m.msg_flags		= 0;
++
++    cm = CMSG_FIRSTHDR(&m);
++    cm->cmsg_len = CMSG_LEN(sizeof(struct in_addr));
++    cm->cmsg_level = IPPROTO_IP;
++    cm->cmsg_type = IP_SENDSRCADDR;
++
++    memcpy((struct in_addr *)CMSG_DATA(cm), srcip, sizeof(struct in_addr));
++
++    return sendmsg(fd, &m, MSG_NOSIGNAL|MSG_DONTWAIT);
++}
++#endif
+ 
+ /*
+  * You can write something into opaque that will subsequently get passed back 
+@@ -223,11 +269,11 @@
+         }
+ 
+ 	while (rc < 0) {
+-#if defined(linux) && defined(IP_PKTINFO)
++#if (defined(linux) && defined(IP_PKTINFO)) || defined(IP_RECVDSTADDR)
+             rc = netsnmp_udp_recvfrom(t->sock, buf, size, from, &fromlen, &(addr_pair->local_addr));
+ #else
+             rc = recvfrom(t->sock, buf, size, NETSNMP_DONTWAIT, from, &fromlen);
+-#endif /* linux && IP_PKTINFO */
++#endif
+ 	    if (rc < 0 && errno != EINTR) {
+ 		break;
+ 	    }
+@@ -276,11 +322,11 @@
+                     size, buf, str, t->sock));
+         free(str);
+ 	while (rc < 0) {
+-#if defined(linux) && defined(IP_PKTINFO)
++#if (defined(linux) && defined(IP_PKTINFO)) || defined(IP_RECVDSTADDR)
+             rc = netsnmp_udp_sendto(t->sock, addr_pair ? &(addr_pair->local_addr) : NULL, to, buf, size);
+ #else
+             rc = sendto(t->sock, buf, size, 0, to, sizeof(struct sockaddr));
+-#endif /* linux && IP_PKTINFO */
++#endif
+ 	    if (rc < 0 && errno != EINTR) {
+                 DEBUGMSGTL(("netsnmp_udp", "sendto error, rc %d (errno %d)\n",
+                             rc, errno));
+@@ -660,6 +706,17 @@
+             }
+             DEBUGMSGTL(("netsnmp_udp", "set IP_PKTINFO\n"));
+         }
++#elif defined(IP_RECVDSTADDR)
++        { 
++            int sockopt = 1;
++            if (setsockopt(t->sock, IPPROTO_IP, IP_RECVDSTADDR, &sockopt, sizeof sockopt) == -1) {
++                DEBUGMSGTL(("netsnmp_udp", "couldn't set IP_RECVDSTADDR: %s\n",
++                    strerror(errno)));
++                netsnmp_transport_free(t);
++                return NULL;
++            }
++            DEBUGMSGTL(("netsnmp_udp", "set IP_RECVDSTADDR\n"));
++        }
+ #endif
+         rc = bind(t->sock, (struct sockaddr *) addr,
+                   sizeof(struct sockaddr));


>Release-Note:
>Audit-Trail:
>Unformatted:



More information about the freebsd-ports-bugs mailing list