From nobody@FreeBSD.org  Mon Apr  4 14:53:29 2011
Return-Path: <nobody@FreeBSD.org>
Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34])
	by hub.freebsd.org (Postfix) with ESMTP id A9B9D106566C
	for <freebsd-gnats-submit@FreeBSD.org>; Mon,  4 Apr 2011 14:53:29 +0000 (UTC)
	(envelope-from nobody@FreeBSD.org)
Received: from red.freebsd.org (red.freebsd.org [IPv6:2001:4f8:fff6::22])
	by mx1.freebsd.org (Postfix) with ESMTP id 9871A8FC22
	for <freebsd-gnats-submit@FreeBSD.org>; Mon,  4 Apr 2011 14:53:29 +0000 (UTC)
Received: from red.freebsd.org (localhost [127.0.0.1])
	by red.freebsd.org (8.14.4/8.14.4) with ESMTP id p34ErTfg092825
	for <freebsd-gnats-submit@FreeBSD.org>; Mon, 4 Apr 2011 14:53:29 GMT
	(envelope-from nobody@red.freebsd.org)
Received: (from nobody@localhost)
	by red.freebsd.org (8.14.4/8.14.4/Submit) id p34ErTYI092821;
	Mon, 4 Apr 2011 14:53:29 GMT
	(envelope-from nobody)
Message-Id: <201104041453.p34ErTYI092821@red.freebsd.org>
Date: Mon, 4 Apr 2011 14:53:29 GMT
From: Nikolay Denev <ndenev@gmail.com>
To: freebsd-gnats-submit@FreeBSD.org
Subject: Fix for net-mgmt/net-snmp in multihomed environments
X-Send-Pr-Version: www-3.1
X-GNATS-Notify:

>Number:         156178
>Category:       ports
>Synopsis:       Fix for net-mgmt/net-snmp in multihomed environments
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    sylvio
>State:          closed
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          change-request
>Submitter-Id:   current-users
>Arrival-Date:   Mon Apr 04 15:00:21 UTC 2011
>Closed-Date:    Wed May 18 12:26:29 UTC 2011
>Last-Modified:  Wed May 18 12:26:29 UTC 2011
>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@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:
Responsible-Changed-From-To: freebsd-ports-bugs->sylvio 
Responsible-Changed-By: edwin 
Responsible-Changed-When: Mon Apr 4 15:00:43 UTC 2011 
Responsible-Changed-Why:  
Over to maintainer (via the GNATS Auto Assign Tool) 

http://www.freebsd.org/cgi/query-pr.cgi?pr=156178 

From: Nikolay Denev <ndenev@gmail.com>
To: bug-followup@FreeBSD.org,
 ndenev@gmail.com
Cc:  
Subject: Re: ports/156178: Fix for net-mgmt/net-snmp in multihomed environments
Date: Mon, 18 Apr 2011 06:32:19 +0300

 The patch has been included upstream, and will appear in the next =
 net-snmp release, so this
 PR probably can be closed.=
State-Changed-From-To: open->closed 
State-Changed-By: sylvio 
State-Changed-When: Wed May 18 12:26:28 UTC 2011 
State-Changed-Why:  
This patch has been included upstream, and will appear in the nex 
net-snmp release. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=156178 
>Unformatted:
