From dark@vpn1-m1.rambler.ru  Mon Sep  3 14:49:37 2012
Return-Path: <dark@vpn1-m1.rambler.ru>
Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34])
	by hub.freebsd.org (Postfix) with ESMTP id A5BF91065675
	for <FreeBSD-gnats-submit@freebsd.org>; Mon,  3 Sep 2012 14:49:37 +0000 (UTC)
	(envelope-from dark@vpn1-m1.rambler.ru)
Received: from vpn1-m1.rambler.ru (vpn1-m1.rambler.ru [81.19.94.147])
	by mx1.freebsd.org (Postfix) with ESMTP id 2515F8FC15
	for <FreeBSD-gnats-submit@freebsd.org>; Mon,  3 Sep 2012 14:49:36 +0000 (UTC)
Received: from vpn1-m1.rambler.ru (localhost [127.0.0.1])
	by vpn1-m1.rambler.ru (8.14.5/8.14.5) with ESMTP id q7VC79d9042801;
	Fri, 31 Aug 2012 16:07:09 +0400 (MSK)
	(envelope-from dark@vpn1-m1.rambler.ru)
Received: (from dark@localhost)
	by vpn1-m1.rambler.ru (8.14.5/8.14.5/Submit) id q7VC790M042800;
	Fri, 31 Aug 2012 16:07:09 +0400 (MSK)
	(envelope-from dark)
Message-Id: <201208311207.q7VC790M042800@vpn1-m1.rambler.ru>
Date: Fri, 31 Aug 2012 16:07:09 +0400 (MSK)
From: Konstantin Kukushkin <dark@rambler-co.ru>
Reply-To: Konstantin Kukushkin <dark@rambler-co.ru>
To: FreeBSD-gnats-submit@freebsd.org
Subject: bsnmpd can reply from other address
X-Send-Pr-Version: 3.113
X-GNATS-Notify:

>Number:         171279
>Category:       bin
>Synopsis:       bsnmpd can reply from other address
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    glebius
>State:          closed
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Mon Sep 03 14:50:04 UTC 2012
>Closed-Date:    Wed Oct 24 11:36:51 UTC 2012
>Last-Modified:  Wed Oct 24 11:36:51 UTC 2012
>Originator:     Konstantin Kukushkin
>Release:        FreeBSD 9.0-STABLE amd64
>Organization:
Rambler Internet Holding, LLC
>Environment:
System: FreeBSD vpn1-m1.rambler.ru 9.0-STABLE FreeBSD 9.0-STABLE #2 r231584M: Mon Feb 13 18:24:25 MSK 2012 glebius@vpn1-m1.rambler.ru:/usr/obj/usr/home/glebius/9/sys/VPN amd64

>Description:
	bsnmpd by default listen INADDR_ANY, and on multihomed system daemon can receive queries to some addresses.
When replying to query bsdnmp simply use sendto(), so OS build response datagram with source ip nearest to sender, which can be not equal to destination ip on source query.
This is ok for net-snmp utils like snmpget & snmpwalk, but this can't work with statefull firewalls like ipfw(4) or pf(4).

Please fix it.

>How-To-Repeat:
I used multihomed host vpn1-m1:
[pts/2] dark@vpn1-m1:~> ( ifconfig bge0 inet ; ifconfig lo0 inet )|grep inet
        inet 81.19.94.147 netmask 0xfffffff8 broadcast 81.19.94.151
        inet 127.0.0.1 netmask 0xff000000 
        inet 81.19.64.133 netmask 0xffffffff 
        inet 81.19.79.1 netmask 0xffffffff 
with ``onestarted`` bsnmpd:
[pts/2] dark@vpn1-m1:~> sudo /etc/rc.d/bsnmpd onestart
Starting bsnmpd.
[pts/2] dark@vpn1-m1:~> sockstat | grep 'bsnmpd.*161'
root     bsnmpd     38365 6  udp4   *:161                 *:*

and other host for query to address, routed to vpn1-m1:
[pts/53] dark@dark:~> ifconfig re0 inet|grep inet
        inet 81.19.64.109 netmask 0xffffffe0 broadcast 81.19.64.127

[pts/53] dark@dark:~> snmpget -v 2c -c public 81.19.64.133 sysDescr.0
Timeout: No Response from 81.19.64.133.

tcpdump on multihomed host shows that bsnmpd reply from source other that query destination:
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on bge0, link-type EN10MB (Ethernet), capture size 65535 bytes
15:17:16.007788 IP 81.19.64.109.60689 > 81.19.64.133.161:  GetRequest(28)  .1.3.6.1.2.1.1.1.0
15:17:16.008005 IP 81.19.94.147.161 > 81.19.64.109.60689:  GetResponse(76)  .1.3.6.1.2.1.1.1.0="vpn1-m1.rambler.ru 4212937669 FreeBSD 9.0-STABLE"
>Fix:

Other udp servers like named try to create listen socket bind()'ed on adresses from getifaddrs() output, not INADDR_ANY. While daemon receiving query on bind()'ed socket it knows on which address query was sent, and can reply right.
Unfortunately I don't know any other mechanism getting datagram destination address in FreeBSD, in Linux there is 'IP_PKTINFO' socket option for this.
>Release-Note:
>Audit-Trail:
Responsible-Changed-From-To: freebsd-bugs->glebius 
Responsible-Changed-By: glebius 
Responsible-Changed-When: Thu Sep 6 10:37:25 UTC 2012 
Responsible-Changed-Why:  
Take this one. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=171279 
State-Changed-From-To: open->patched 
State-Changed-By: glebius 
State-Changed-When: Sun Sep 9 09:48:46 UTC 2012 
State-Changed-Why:  
Fixed in head/ 

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

From: dfilter@FreeBSD.ORG (dfilter service)
To: bug-followup@FreeBSD.org
Cc:  
Subject: Re: bin/171279: commit references a PR
Date: Sun,  9 Sep 2012 09:47:03 +0000 (UTC)

 Author: glebius
 Date: Sun Sep  9 09:46:48 2012
 New Revision: 240271
 URL: http://svn.freebsd.org/changeset/base/240271
 
 Log:
   For UDP transport set IP_RECVDSTADDR sockopt on the socket, and provide
   IP_SENDSRCADDR control with datagram message we reply with. This makes
   bsnmpd reply from exactly same address that request was sent to, thus
   successfully bypassing stateful firewalls or other kinds of strict checking.
   
   PR:		bin/171279
 
 Modified:
   head/contrib/bsnmp/snmpd/main.c
   head/contrib/bsnmp/snmpd/trans_udp.c
 
 Modified: head/contrib/bsnmp/snmpd/main.c
 ==============================================================================
 --- head/contrib/bsnmp/snmpd/main.c	Sun Sep  9 08:40:44 2012	(r240270)
 +++ head/contrib/bsnmp/snmpd/main.c	Sun Sep  9 09:46:48 2012	(r240271)
 @@ -1106,10 +1106,11 @@ recv_stream(struct port_input *pi)
   * Each receive should return one datagram.
   */
  static int
 -recv_dgram(struct port_input *pi)
 +recv_dgram(struct port_input *pi, struct in_addr *laddr)
  {
  	u_char embuf[1000];
 -	char cbuf[CMSG_SPACE(SOCKCREDSIZE(CMGROUP_MAX))];
 +	char cbuf[CMSG_SPACE(SOCKCREDSIZE(CMGROUP_MAX)) +
 +	    CMSG_SPACE(sizeof(struct in_addr))];
  	struct msghdr msg;
  	struct iovec iov[1];
  	ssize_t len;
 @@ -1159,6 +1160,9 @@ recv_dgram(struct port_input *pi)
  
  	for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
  	    cmsg = CMSG_NXTHDR(&msg, cmsg)) {
 +		if (cmsg->cmsg_level == IPPROTO_IP &&
 +		    cmsg->cmsg_type == IP_RECVDSTADDR)
 +			memcpy(laddr, CMSG_DATA(cmsg), sizeof(struct in_addr));
  		if (cmsg->cmsg_level == SOL_SOCKET &&
  		    cmsg->cmsg_type == SCM_CREDS)
  			cred = (struct sockcred *)CMSG_DATA(cmsg);
 @@ -1187,12 +1191,27 @@ snmpd_input(struct port_input *pi, struc
  #ifdef USE_TCPWRAPPERS
  	char client[16];
  #endif
 +	struct msghdr msg;
 +	struct iovec iov[1];
 +	char cbuf[CMSG_SPACE(sizeof(struct in_addr))];
 +	struct cmsghdr *cmsgp;
  
  	/* get input depending on the transport */
  	if (pi->stream) {
 +		msg.msg_control = NULL;
 +		msg.msg_controllen = 0;
 +
  		ret = recv_stream(pi);
  	} else {
 -		ret = recv_dgram(pi);
 +		memset(cbuf, 0, CMSG_SPACE(sizeof(struct in_addr)));
 +		msg.msg_control = cbuf;
 +		msg.msg_controllen = CMSG_SPACE(sizeof(struct in_addr));
 +		cmsgp = CMSG_FIRSTHDR(&msg);
 +		cmsgp->cmsg_len = CMSG_LEN(sizeof(struct in_addr));
 +		cmsgp->cmsg_level = IPPROTO_IP;
 +		cmsgp->cmsg_type = IP_SENDSRCADDR;
 +		
 +		ret = recv_dgram(pi, (struct in_addr *)CMSG_DATA(cmsgp));
  	}
  
  	if (ret == -1)
 @@ -1337,11 +1356,19 @@ snmpd_input(struct port_input *pi, struc
  	    sndbuf, &sndlen, "SNMP", ierr, vi, NULL);
  
  	if (ferr == SNMPD_INPUT_OK) {
 -		slen = sendto(pi->fd, sndbuf, sndlen, 0, pi->peer, pi->peerlen);
 +		msg.msg_name = pi->peer;
 +		msg.msg_namelen = pi->peerlen;
 +		msg.msg_iov = iov;
 +		msg.msg_iovlen = 1;
 +		msg.msg_flags = 0;
 +		iov[0].iov_base = sndbuf;
 +		iov[0].iov_len = sndlen;
 +
 +		slen = sendmsg(pi->fd, &msg, 0);
  		if (slen == -1)
 -			syslog(LOG_ERR, "sendto: %m");
 +			syslog(LOG_ERR, "sendmsg: %m");
  		else if ((size_t)slen != sndlen)
 -			syslog(LOG_ERR, "sendto: short write %zu/%zu",
 +			syslog(LOG_ERR, "sendmsg: short write %zu/%zu",
  			    sndlen, (size_t)slen);
  	}
  	snmp_pdu_free(&pdu);
 
 Modified: head/contrib/bsnmp/snmpd/trans_udp.c
 ==============================================================================
 --- head/contrib/bsnmp/snmpd/trans_udp.c	Sun Sep  9 08:40:44 2012	(r240270)
 +++ head/contrib/bsnmp/snmpd/trans_udp.c	Sun Sep  9 09:46:48 2012	(r240271)
 @@ -103,11 +103,19 @@ udp_init_port(struct tport *tp)
  	struct udp_port *p = (struct udp_port *)tp;
  	struct sockaddr_in addr;
  	u_int32_t ip;
 +	const int on = 1;
  
  	if ((p->input.fd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
  		syslog(LOG_ERR, "creating UDP socket: %m");
  		return (SNMP_ERR_RES_UNAVAIL);
  	}
 +	if (setsockopt(p->input.fd, IPPROTO_IP, IP_RECVDSTADDR, &on,
 +	    sizeof(on)) == -1) {
 +		syslog(LOG_ERR, "setsockopt(IP_RECVDSTADDR): %m");
 +		close(p->input.fd);
 +		p->input.fd = -1;
 +		return (SNMP_ERR_GENERR);
 +	}
  	ip = (p->addr[0] << 24) | (p->addr[1] << 16) | (p->addr[2] << 8) |
  	    p->addr[3];
  	memset(&addr, 0, sizeof(addr));
 _______________________________________________
 svn-src-all@freebsd.org mailing list
 http://lists.freebsd.org/mailman/listinfo/svn-src-all
 To unsubscribe, send any mail to "svn-src-all-unsubscribe@freebsd.org"
 

From: dfilter@FreeBSD.ORG (dfilter service)
To: bug-followup@FreeBSD.org
Cc:  
Subject: Re: bin/171279: commit references a PR
Date: Thu, 20 Sep 2012 05:41:36 +0000 (UTC)

 Author: glebius
 Date: Thu Sep 20 05:41:20 2012
 New Revision: 240734
 URL: http://svn.freebsd.org/changeset/base/240734
 
 Log:
   Re-do r240271:
   - Set IP_RECVDSTADDR sockopt on the socket only in case if
     it is INADDR_ANY bound.
   - Supply IP_SENDSRCADDR control message only if we did receive
     IP_RECVDSTADDR control message.
   
   This fixes operation of snmpd bound to a specific local IP address.
   
   PR:		bin/171279
 
 Modified:
   head/contrib/bsnmp/snmpd/main.c
   head/contrib/bsnmp/snmpd/trans_udp.c
 
 Modified: head/contrib/bsnmp/snmpd/main.c
 ==============================================================================
 --- head/contrib/bsnmp/snmpd/main.c	Thu Sep 20 04:53:12 2012	(r240733)
 +++ head/contrib/bsnmp/snmpd/main.c	Thu Sep 20 05:41:20 2012	(r240734)
 @@ -1203,6 +1203,8 @@ snmpd_input(struct port_input *pi, struc
  
  		ret = recv_stream(pi);
  	} else {
 +		struct in_addr *laddr;
 +
  		memset(cbuf, 0, CMSG_SPACE(sizeof(struct in_addr)));
  		msg.msg_control = cbuf;
  		msg.msg_controllen = CMSG_SPACE(sizeof(struct in_addr));
 @@ -1210,8 +1212,14 @@ snmpd_input(struct port_input *pi, struc
  		cmsgp->cmsg_len = CMSG_LEN(sizeof(struct in_addr));
  		cmsgp->cmsg_level = IPPROTO_IP;
  		cmsgp->cmsg_type = IP_SENDSRCADDR;
 +		laddr = (struct in_addr *)CMSG_DATA(cmsgp);
  		
 -		ret = recv_dgram(pi, (struct in_addr *)CMSG_DATA(cmsgp));
 +		ret = recv_dgram(pi, laddr);
 +
 +		if (laddr->s_addr == 0) {
 +			msg.msg_control = NULL;
 +			msg.msg_controllen = 0;
 +		}
  	}
  
  	if (ret == -1)
 
 Modified: head/contrib/bsnmp/snmpd/trans_udp.c
 ==============================================================================
 --- head/contrib/bsnmp/snmpd/trans_udp.c	Thu Sep 20 04:53:12 2012	(r240733)
 +++ head/contrib/bsnmp/snmpd/trans_udp.c	Thu Sep 20 05:41:20 2012	(r240734)
 @@ -109,13 +109,6 @@ udp_init_port(struct tport *tp)
  		syslog(LOG_ERR, "creating UDP socket: %m");
  		return (SNMP_ERR_RES_UNAVAIL);
  	}
 -	if (setsockopt(p->input.fd, IPPROTO_IP, IP_RECVDSTADDR, &on,
 -	    sizeof(on)) == -1) {
 -		syslog(LOG_ERR, "setsockopt(IP_RECVDSTADDR): %m");
 -		close(p->input.fd);
 -		p->input.fd = -1;
 -		return (SNMP_ERR_GENERR);
 -	}
  	ip = (p->addr[0] << 24) | (p->addr[1] << 16) | (p->addr[2] << 8) |
  	    p->addr[3];
  	memset(&addr, 0, sizeof(addr));
 @@ -123,6 +116,14 @@ udp_init_port(struct tport *tp)
  	addr.sin_port = htons(p->port);
  	addr.sin_family = AF_INET;
  	addr.sin_len = sizeof(addr);
 +	if (addr.sin_addr.s_addr == INADDR_ANY &&
 +	    setsockopt(p->input.fd, IPPROTO_IP, IP_RECVDSTADDR, &on,
 +	    sizeof(on)) == -1) {
 +		syslog(LOG_ERR, "setsockopt(IP_RECVDSTADDR): %m");
 +		close(p->input.fd);
 +		p->input.fd = -1;
 +		return (SNMP_ERR_GENERR);
 +	}
  	if (bind(p->input.fd, (struct sockaddr *)&addr, sizeof(addr))) {
  		if (errno == EADDRNOTAVAIL) {
  			close(p->input.fd);
 _______________________________________________
 svn-src-all@freebsd.org mailing list
 http://lists.freebsd.org/mailman/listinfo/svn-src-all
 To unsubscribe, send any mail to "svn-src-all-unsubscribe@freebsd.org"
 
State-Changed-From-To: patched->closed 
State-Changed-By: glebius 
State-Changed-When: Wed Oct 24 11:36:37 UTC 2012 
State-Changed-Why:  
Fixed in stable/9. 

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