Subject: kern/31368: Linux compatibility raw inet protocol handling changes
To: None <kern-bug-people@netbsd.org, gnats-admin@netbsd.org,>
From: Cliff Wright <cliff@vixen.snipe444.org>
List: netbsd-bugs
Date: 09/21/2005 23:17:00
>Number:         31368
>Category:       kern
>Synopsis:       Linux compatibility raw inet protocol handling changes
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    kern-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Wed Sep 21 23:17:00 +0000 2005
>Originator:     Cliff Wright
>Release:        NetBSD 2.0_BETA
>Organization:
	
>Environment:
	
	
System: NetBSD vixen 2.0_BETA NetBSD 2.0_BETA (vixen) #2: Tue Jun 7 16:15:17 PDT 2005 cliff@vixen:/usr/src/sys/arch/i386/compile/vixen i386
Architecture: i386
Machine: i386
>Description:
	
	When running a vendor network analysis program in Linux
	compatibility mode, some missing features, and problems
	were observed. A patch is included that fixes these.

	1. The rarely used get interface mtu (SIOCGIFMTU) was
	   not implemented.
	2. The raw inet protocol option header include (IP_HDRINCL)
	    was not implemented.
	3. When using bind on a raw inet socket, Linux ignores some
	   bytes that BSD does not, so needed to adjust these bytes
	   for the BSD call.
	4. When using the header include option, BSD has 2 fields of
	   the packet in host byte order, whereas Linux does not,
	   so may need to adjust these fields.
>How-To-Repeat:
	
>Fix:
	
--- src/sys/compat/linux/common/linux_socket.h.orig	2003-07-27 12:30:03.000000000 -0700
+++ src/sys/compat/linux/common/linux_socket.h	2005-09-20 14:08:13.000000000 -0700
@@ -110,6 +110,7 @@
 
 #define LINUX_IP_TOS		1
 #define LINUX_IP_TTL		2
+#define LINUX_IP_HDRINCL	3
 #define	LINUX_IP_MULTICAST_IF	32
 #define	LINUX_IP_MULTICAST_TTL	33
 #define	LINUX_IP_MULTICAST_LOOP	34
--- src/sys/compat/linux/common/linux_sockio.h.orig	2000-12-22 03:24:43.000000000 -0800
+++ src/sys/compat/linux/common/linux_sockio.h	2005-09-19 14:41:11.000000000 -0700
@@ -47,6 +47,7 @@
 #define	LINUX_SIOCGIFDSTADDR	_LINUX_IO(0x89, 0x17)
 #define	LINUX_SIOCGIFBRDADDR	_LINUX_IO(0x89, 0x19)
 #define	LINUX_SIOCGIFNETMASK	_LINUX_IO(0x89, 0x1b)
+#define LINUX_SIOCGIFMTU	_LINUX_IO(0x89, 0x21)
 #define LINUX_SIOCADDMULTI	_LINUX_IO(0x89, 0x31)
 #define LINUX_SIOCDELMULTI	_LINUX_IO(0x89, 0x32)
 #define LINUX_SIOCGIFHWADDR	_LINUX_IO(0x89, 0x27)
--- src/sys/compat/linux/common/linux_socket.c.orig	2003-10-25 11:38:42.000000000 -0700
+++ src/sys/compat/linux/common/linux_socket.c	2005-09-21 14:56:05.000000000 -0700
@@ -64,6 +64,10 @@
 #include <net/if_dl.h>
 #include <net/if_types.h>
 #include <netinet/in.h>
+#if BYTE_ORDER == LITTLE_ENDIAN
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#endif
 #include <netinet/tcp.h>
 #include <sys/mount.h>
 #include <sys/proc.h>
@@ -385,10 +389,41 @@
 		struct sockaddr *sa;
 		int error;
 		caddr_t sg = stackgap_init(p, 0);
+#if BYTE_ORDER == LITTLE_ENDIAN
+		struct file	*fp;
+#endif
 
 		if ((error = linux_sa_get(p, &sg, &sa, SCARG(uap, to), &tolen)))
 			return (error);
 
+#if BYTE_ORDER == LITTLE_ENDIAN
+		/* For raw inet header included data, BSD has 2 fields
+		 * in host byte order that Linux does not, so need to
+		 * adjust for this. cliff
+		 */
+
+		if( sa->sa_family == AF_INET &&
+		    getsock(p->p_fd, SCARG(uap, s), &fp) == 0) {
+			if(((struct socket *)fp->f_data)->so_type == SOCK_RAW) {
+				struct mbuf	*m;
+
+				error = sogetopt((struct socket *)fp->f_data,
+				IPPROTO_IP, IP_HDRINCL, &m);
+				if(!error && m != NULL) {
+					if(*mtod(m, int *)) {
+						struct ip *ip_head;
+
+						ip_head = (struct ip *)
+						  SCARG(uap, msg);
+						NTOHS(ip_head->ip_len);
+						NTOHS(ip_head->ip_off);
+					}
+					m_free(m);
+				}
+			}
+			FILE_UNUSE(fp, p);
+		}
+#endif
 		SCARG(&bsa, to) = sa;
 	} else
 		SCARG(&bsa, to) = NULL;
@@ -917,6 +952,8 @@
 		return IP_TOS;
 	case LINUX_IP_TTL:
 		return IP_TTL;
+	case LINUX_IP_HDRINCL:
+		return IP_HDRINCL;
 	case LINUX_IP_MULTICAST_TTL:
 		return IP_MULTICAST_TTL;
 	case LINUX_IP_MULTICAST_LOOP:
@@ -1275,6 +1312,9 @@
 	case LINUX_SIOCGIFNETMASK:
 		SCARG(&ia, com) = OSIOCGIFNETMASK;
 		break;
+	case LINUX_SIOCGIFMTU:
+		SCARG(&ia, com) = SIOCGIFMTU;
+		break;
 	case LINUX_SIOCADDMULTI:
 		SCARG(&ia, com) = SIOCADDMULTI;
 		break;
@@ -1374,11 +1414,12 @@
 	struct proc *p = l->l_proc;
 	int		error, namlen;
 	struct sys_bind_args bsa;
+	struct sockaddr *sa;
+	struct file	*fp;
 
 	namlen = SCARG(uap, namelen);
 	SCARG(&bsa, s) = SCARG(uap, s);
 	if (SCARG(uap, name)) {
-		struct sockaddr *sa;
 		caddr_t sg = stackgap_init(p, 0);
 
 		error = linux_sa_get(p, &sg, &sa, SCARG(uap, name), &namlen);
@@ -1390,6 +1431,20 @@
 		SCARG(&bsa, name) = NULL;
 	SCARG(&bsa, namelen) = namlen;
 
+	/* For raw inet sockets Linux only looks at 4 byte inet address,
+	 * BSD looks at full address, so have to clean up the address
+	 * for BSD. cliff
+	 */
+	if( sa->sa_family == AF_INET &&
+	    getsock(p->p_fd, SCARG(uap, s), &fp) == 0) {
+		if(((struct socket *)fp->f_data)->so_type == SOCK_RAW) {
+			int i;
+			sa->sa_data[0] = sa->sa_data[1] = 0;
+			for (i = 0; i < 8; i++) sa->sa_data[i + 6] = 0;
+		}
+		FILE_UNUSE(fp, p);
+	}
+
 	return (sys_bind(l, &bsa, retval));
 }
 

>Unformatted: