From nobody@FreeBSD.org  Mon Apr  3 18:37:22 2006
Return-Path: <nobody@FreeBSD.org>
Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125])
	by hub.freebsd.org (Postfix) with ESMTP id B7C6316A400
	for <freebsd-gnats-submit@FreeBSD.org>; Mon,  3 Apr 2006 18:37:22 +0000 (UTC)
	(envelope-from nobody@FreeBSD.org)
Received: from www.freebsd.org (www.freebsd.org [216.136.204.117])
	by mx1.FreeBSD.org (Postfix) with ESMTP id 85F9043D46
	for <freebsd-gnats-submit@FreeBSD.org>; Mon,  3 Apr 2006 18:37:22 +0000 (GMT)
	(envelope-from nobody@FreeBSD.org)
Received: from www.freebsd.org (localhost [127.0.0.1])
	by www.freebsd.org (8.13.1/8.13.1) with ESMTP id k33IbMoD044848
	for <freebsd-gnats-submit@FreeBSD.org>; Mon, 3 Apr 2006 18:37:22 GMT
	(envelope-from nobody@www.freebsd.org)
Received: (from nobody@localhost)
	by www.freebsd.org (8.13.1/8.13.1/Submit) id k33IbMhv044847;
	Mon, 3 Apr 2006 18:37:22 GMT
	(envelope-from nobody)
Message-Id: <200604031837.k33IbMhv044847@www.freebsd.org>
Date: Mon, 3 Apr 2006 18:37:22 GMT
From: Qiao Yang <qyang@stbernard.com>
To: freebsd-gnats-submit@FreeBSD.org
Subject: IP Encapsulation mask_match() returns wrong results
X-Send-Pr-Version: www-2.3

>Number:         95277
>Category:       kern
>Synopsis:       [netinet] [patch] IP Encapsulation mask_match() returns wrong results
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    freebsd-net
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Mon Apr 03 18:40:16 GMT 2006
>Closed-Date:    
>Last-Modified:  Sun Mar 02 02:44:34 UTC 2008
>Originator:     Qiao Yang
>Release:        5.4
>Organization:
St Bernard Software
>Environment:
>Description:
As documented in the code,
/*
                 * We prioritize the matches by using bit length of the
                 * matches.  mask_match() and user-supplied matching function
                 * should return the bit length of the matches (for example,
                 * if both src/dst are matched for IPv4, 64 should be returned).
                 * 0 or negative return value means "it did not match".
*/

But in mask_match(), it use "struct sockaddr" to do byte-array comparison
when applying the network mask. The problem is that this mask is applied to
the whole 'struct sockaddr' data structure. Because 'struct sockaddr' includes
both sa_len and sa_family, the result match_len will not be 0 even when the
network mask is 0.0.0.0. And, if both src/dst are matched for IPv4, 88 instead
of 64 is returned.

This causes problem for protocols which want to set 0.0.0.0 netmask on the
tunnel end.

>How-To-Repeat:
Just read the code.
>Fix:
When doing byte-array comparison, use sockaddr.sa_data instead of sockaddr.
>Release-Note:
>Audit-Trail:

From: Bruce M Simpson <bms@incunabulum.net>
To: freebsd-gnats-submit@FreeBSD.org
Cc:  
Subject: Re: kern/95277: [netinet] IP Encapsulation mask_match() returns wrong
 results
Date: Thu, 28 Sep 2006 18:07:34 +0100

 This is a multi-part message in MIME format.
 --------------050007060704060608050700
 Content-Type: text/plain; charset=ISO-8859-1; format=flowed
 Content-Transfer-Encoding: 7bit
 
 I don't quite get this. Test case attached.
 
 --------------050007060704060608050700
 Content-Type: text/x-csrc;
  name="maskmatch.c"
 Content-Transfer-Encoding: 7bit
 Content-Disposition: inline;
  filename="maskmatch.c"
 
 /*
  * test case for mask_match() bug
  */
 
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <net/route.h>
 #include <netinet/in.h>
 #include <netinet/ip_mroute.h>
 
 #include <stddef.h>
 #include <stdarg.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
 #include <err.h>
 #include <errno.h>
 
 /* XXX */
 struct encaptab {
 int af;
 int proto;
 struct sockaddr_storage src;
 struct sockaddr_storage srcmask;
 struct sockaddr_storage dst;
 struct sockaddr_storage dstmask;
 };
 
 int
 mask_match(ep, sp, dp)
 	const struct encaptab *ep;
 	const struct sockaddr *sp;
 	const struct sockaddr *dp;
 {
 	struct sockaddr_storage s;
 	struct sockaddr_storage d;
 	int i;
 	const u_int8_t *p, *q;
 	u_int8_t *r;
 	int matchlen;
 
 	if (sp->sa_len > sizeof(s) || dp->sa_len > sizeof(d)) {
 		fprintf(stderr, "lengths too big\n");
 		return 0;
 	}
 	if (sp->sa_family != ep->af || dp->sa_family != ep->af) {
 		fprintf(stderr, "af dont match \n");
 		return 0;
 	}
 	if (sp->sa_len != ep->src.ss_len || dp->sa_len != ep->dst.ss_len) {
 		fprintf(stderr, "lengths dont match \n");
 		return 0;
 	}
 
 	matchlen = 0;
 
 	p = (const u_int8_t *)sp;
 	q = (const u_int8_t *)&ep->srcmask;
 	r = (u_int8_t *)&s;
 	for (i = 0 ; i < sp->sa_len; i++) {
 		r[i] = p[i] & q[i];
 		/* XXX estimate */
 		matchlen += (q[i] ? 8 : 0);
 	}
 
 	p = (const u_int8_t *)dp;
 	q = (const u_int8_t *)&ep->dstmask;
 	r = (u_int8_t *)&d;
 	for (i = 0 ; i < dp->sa_len; i++) {
 		r[i] = p[i] & q[i];
 		/* XXX rough estimate */
 		matchlen += (q[i] ? 8 : 0);
 	}
 
 	/* need to overwrite len/family portion as we don't compare them */
 	s.ss_len = sp->sa_len;
 	s.ss_family = sp->sa_family;
 	d.ss_len = dp->sa_len;
 	d.ss_family = dp->sa_family;
 
 	if (bcmp(&s, &ep->src, ep->src.ss_len) == 0 &&
 	    bcmp(&d, &ep->dst, ep->dst.ss_len) == 0) {
 		fprintf(stderr, "match\n");
 		return matchlen;
 	} else {
 		fprintf(stderr, "no match\n");
 		return 0;
 	}
 }
 
 int
 main(int argc, char *argv[])
 {
 	struct encaptab e;
 	struct sockaddr_storage ss1;
 	struct sockaddr_storage ss2;
 	struct sockaddr_in *psin_1;
 	struct sockaddr_in *psin_2;
 	int result;
 
 	bzero(&e, sizeof(e));
 	e.af = AF_INET;
 	e.proto = -1;
 
 	psin_1 = (struct sockaddr_in *)&e.src;
 	psin_1->sin_family = AF_INET;
 	psin_1->sin_len = sizeof(struct sockaddr_in);
 	psin_1->sin_addr.s_addr = inet_addr("1.2.3.4");
 
 	psin_1 = (struct sockaddr_in *)&e.srcmask;
 	psin_1->sin_family = AF_INET;
 	psin_1->sin_len = sizeof(struct sockaddr_in);
 	psin_1->sin_addr.s_addr = inet_addr("255.255.255.0");
 
 	psin_1 = (struct sockaddr_in *)&e.dst;
 	psin_1->sin_family = AF_INET;
 	psin_1->sin_len = sizeof(struct sockaddr_in);
 	psin_1->sin_addr.s_addr = inet_addr("4.3.2.1");
 
 	psin_1 = (struct sockaddr_in *)&e.dstmask;
 	psin_1->sin_family = AF_INET;
 	psin_1->sin_len = sizeof(struct sockaddr_in);
 	psin_1->sin_addr.s_addr = inet_addr("0.0.0.0");
 
 	bzero(&ss1, sizeof(ss1));
 	bzero(&ss2, sizeof(ss2));
 	psin_1 = (struct sockaddr_in *)&ss1;
 	psin_2 = (struct sockaddr_in *)&ss2;
 	psin_1->sin_len = sizeof(struct sockaddr_in);
 	psin_1->sin_family = AF_INET;
 	psin_1->sin_addr.s_addr = inet_addr("192.168.0.1");
 	psin_2->sin_len = sizeof(struct sockaddr_in);
 	psin_2->sin_family = AF_INET;
 	psin_2->sin_addr.s_addr = inet_addr("4.3.2.2");
 
 	result = mask_match(&e, &ss1, &ss2);
 	printf("result is %d\n", result);
 
 	exit(0);
 }
 
 --------------050007060704060608050700--
State-Changed-From-To: open->feedback 
State-Changed-By: bms 
State-Changed-When: Thu Sep 28 17:25:33 UTC 2006 
State-Changed-Why:  
Can you confirm the issue still exists in RELENG_6? 
I see no code changes but I can't seem to reproduce it with the 
test case I wrote, and more information would be very welcome! 

http://www.freebsd.org/cgi/query-pr.cgi?pr=95277 
Responsible-Changed-From-To: freebsd-bugs->net 
Responsible-Changed-By: bms 
Responsible-Changed-When: Thu Sep 28 17:30:09 UTC 2006 
Responsible-Changed-Why:  
over to net for more discussion 

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

From: Bruce M Simpson <bms@incunabulum.net>
To: freebsd-gnats-submit@FreeBSD.org
Cc:  
Subject: Re: kern/95277: [netinet] IP Encapsulation mask_match() returns wrong
 results
Date: Thu, 28 Sep 2006 18:22:46 +0100

 This is a multi-part message in MIME format.
 --------------080009040700000209070407
 Content-Type: text/plain; charset=ISO-8859-1; format=flowed
 Content-Transfer-Encoding: 7bit
 
 I guess a patch for the desired behaviour would look something like this?
 More detail needed...
 
 --------------080009040700000209070407
 Content-Type: text/x-patch;
  name="maskmatch.diff"
 Content-Transfer-Encoding: 7bit
 Content-Disposition: inline;
  filename="maskmatch.diff"
 
 ==== //depot/user/bms/nethead/sys/netinet/ip_encap.c#1 - /home/bms/fp4/nethead/sys/netinet/ip_encap.c ====
 --- /tmp/tmp.41786.0	Thu Sep 28 18:21:51 2006
 +++ /home/bms/fp4/nethead/sys/netinet/ip_encap.c	Thu Sep 28 18:21:09 2006
 @@ -403,6 +403,7 @@
  	const struct sockaddr *sp;
  	const struct sockaddr *dp;
  {
 +	const int hdrlen = offsetof(struct sockaddr, sa_data);
  	struct sockaddr_storage s;
  	struct sockaddr_storage d;
  	int i;
 @@ -419,32 +420,28 @@
  
  	matchlen = 0;
  
 -	p = (const u_int8_t *)sp;
 -	q = (const u_int8_t *)&ep->srcmask;
 -	r = (u_int8_t *)&s;
 -	for (i = 0 ; i < sp->sa_len; i++) {
 +	p = (const u_int8_t *)&sp->sa_data;
 +	q = (const u_int8_t *)&ep->srcmask + hdrlen;
 +	r = (u_int8_t *)&s + hdrlen;
 +	for (i = 0 ; i < sp->sa_len - hdrlen; i++) {
  		r[i] = p[i] & q[i];
  		/* XXX estimate */
  		matchlen += (q[i] ? 8 : 0);
  	}
  
 -	p = (const u_int8_t *)dp;
 -	q = (const u_int8_t *)&ep->dstmask;
 -	r = (u_int8_t *)&d;
 -	for (i = 0 ; i < dp->sa_len; i++) {
 +	p = (const u_int8_t *)&dp->sa_data;
 +	q = (const u_int8_t *)&ep->dstmask + hdrlen;
 +	r = (u_int8_t *)&d + hdrlen;
 +	for (i = 0 ; i < dp->sa_len - hdrlen; i++) {
  		r[i] = p[i] & q[i];
  		/* XXX rough estimate */
  		matchlen += (q[i] ? 8 : 0);
  	}
  
 -	/* need to overwrite len/family portion as we don't compare them */
 -	s.ss_len = sp->sa_len;
 -	s.ss_family = sp->sa_family;
 -	d.ss_len = dp->sa_len;
 -	d.ss_family = dp->sa_family;
 -
 -	if (bcmp(&s, &ep->src, ep->src.ss_len) == 0 &&
 -	    bcmp(&d, &ep->dst, ep->dst.ss_len) == 0) {
 +	if (bcmp((u_int8_t *)&s + hdrlen, (const u_int8_t *)&ep->src + hdrlen,
 +	    ep->src.ss_len - hdrlen) == 0 &&
 +	    bcmp((u_int8_t *)&d + hdrlen, (const u_int8_t *)&ep->dst + hdrlen,
 +	    ep->dst.ss_len - hdrlen) == 0) {
  		return matchlen;
  	} else
  		return 0;
 
 --------------080009040700000209070407--
Responsible-Changed-From-To: net->freebsd-net 
Responsible-Changed-By: jmg 
Responsible-Changed-When: Mon Oct 2 23:14:49 UTC 2006 
Responsible-Changed-Why:  
move w/ the others... 

http://www.freebsd.org/cgi/query-pr.cgi?pr=95277 
State-Changed-From-To: feedback->open 
State-Changed-By: linimon 
State-Changed-When: Sun Mar 2 02:44:10 UTC 2008 
State-Changed-Why:  
Note that feedback was received some time ago. 

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