[LWN Logo]
[LWN.net]
From:	 "La Monte H.P. Yarroll" <piggy@em.cig.mot.com>
To:	 davem@redhat.com
Subject: [PATCH] sockreg2.4.5-05 inet[6]_create() register/unregister table
Date:	 Tue, 5 Jun 2001 11:59:24 -0500
Cc:	 linux-kernel@vger.kernel.org, sctp-developers-list@cig.mot.com

Here is the register/unregister inet[6]_create() table patch revised
for 2.4.5.  We thank Dave Miller for his helpful feedback on earlier
versions of this patch.

DESCRIPTION
This patch adds a mechanism for registering new IP transport protocols
for the socket() system call.  It replaces the hard-coded switch
tables in inet_create() and inet6_create() with explicit data
structures.

The new calls are:
void    inet_register_protosw(struct inet_protosw *p);
void    inet_unregister_protosw(struct inet_protosw *p);
void    inet6_register_protosw(struct inet_protosw *p);
void    inet6_unregister_protosw(struct inet_protosw *p);

This is the first of a series of proposed changes to support IP
transport modules.

MOTIVATION
As part of the effort to create the Linux Kernel implementation of
SCTP <www.sourceforge.net/projects/lksctp>, we seek to make it
possible to load a new IP transport protocol as a kernel module.

It is already possible to register new address families.  It is even
possible to register new transport protocols with IP.  However, in
order to be able to open a socket with a new transport protocol, you
must replace the whole AF_INET address family.

In addition to SCTP, there are other protocols which could find it
useful to be in a kernel module. For example, TCP extensions like TCP
framing and TCP over satellite, multicast protocols, and RTP/ROHC
(robust header compression). In general, support for IP transport
modules makes transport layer experimentation easier.

CHANGES SINCE sockreg2.4.3-04
We noticed that inet6_protocol_base went away in 2.4.5, so we changed
our v6 initialization to parallel the inet6_protocol initialization.
We now call inet6_register_protosw() from *v6_init() instead of having
a static array of struct protosw's.  Since other protocols depend on
raw sockets (e.g. ICMP, IGMP, NDISC) we still register rawv6_protosw
in inet6_init().

piggy (La Monte H.P. Yarroll)
Karl O. Knutson

PATCH FOLLOWS
diff -u -r linux-2.4.5/include/asm-alpha/socket.h linux/include/asm-alpha/socket.h
--- linux-2.4.5/include/asm-alpha/socket.h	Sat Feb  3 13:26:44 2001
+++ linux/include/asm-alpha/socket.h	Mon Jun  4 11:11:30 2001
@@ -66,6 +66,7 @@
 					/* level.  For writing rarp and	*/
 					/* other similar things on the	*/
 					/* user level.			*/
+#define	SOCK_MAX	(SOCK_PACKET+1)
 #endif
 
 #endif /* _ASM_SOCKET_H */
diff -u -r linux-2.4.5/include/asm-arm/socket.h linux/include/asm-arm/socket.h
--- linux-2.4.5/include/asm-arm/socket.h	Sat Feb  3 13:26:44 2001
+++ linux/include/asm-arm/socket.h	Mon Jun  4 11:11:30 2001
@@ -58,6 +58,7 @@
 					/* level.  For writing rarp and	*/
 					/* other similar things on the	*/
 					/* user level.			*/
+#define	SOCK_MAX	(SOCK_PACKET+1)
 #endif
 
 #endif /* _ASM_SOCKET_H */
diff -u -r linux-2.4.5/include/asm-cris/socket.h linux/include/asm-cris/socket.h
--- linux-2.4.5/include/asm-cris/socket.h	Fri Apr  6 12:51:19 2001
+++ linux/include/asm-cris/socket.h	Mon Jun  4 11:11:30 2001
@@ -59,6 +59,7 @@
                                         /* level.  For writing rarp and */
                                         /* other similar things on the  */
                                         /* user level.                  */
+#define	SOCK_MAX	(SOCK_PACKET+1)
 #endif
 
 #endif /* _ASM_SOCKET_H */
diff -u -r linux-2.4.5/include/asm-i386/socket.h linux/include/asm-i386/socket.h
--- linux-2.4.5/include/asm-i386/socket.h	Sat Feb  3 13:26:44 2001
+++ linux/include/asm-i386/socket.h	Mon Jun  4 11:11:30 2001
@@ -58,6 +58,7 @@
 					/* level.  For writing rarp and	*/
 					/* other similar things on the	*/
 					/* user level.			*/
+#define	SOCK_MAX	(SOCK_PACKET+1)
 #endif
 
 #endif /* _ASM_SOCKET_H */
diff -u -r linux-2.4.5/include/asm-ia64/socket.h linux/include/asm-ia64/socket.h
--- linux-2.4.5/include/asm-ia64/socket.h	Sat Feb  3 13:26:44 2001
+++ linux/include/asm-ia64/socket.h	Mon Jun  4 11:11:30 2001
@@ -65,6 +65,7 @@
 					/* level.  For writing rarp and	*/
 					/* other similar things on the	*/
 					/* user level.			*/
+#define	SOCK_MAX	(SOCK_PACKET+1)
 #endif
 
 #endif /* _ASM_IA64_SOCKET_H */
diff -u -r linux-2.4.5/include/asm-m68k/socket.h linux/include/asm-m68k/socket.h
--- linux-2.4.5/include/asm-m68k/socket.h	Sat Feb  3 13:26:44 2001
+++ linux/include/asm-m68k/socket.h	Mon Jun  4 11:11:30 2001
@@ -58,6 +58,7 @@
 					/* level.  For writing rarp and	*/
 					/* other similar things on the	*/
 					/* user level.			*/
+#define	SOCK_MAX	(SOCK_PACKET+1)
 #endif
 
 #endif /* _ASM_SOCKET_H */
diff -u -r linux-2.4.5/include/asm-mips/socket.h linux/include/asm-mips/socket.h
--- linux-2.4.5/include/asm-mips/socket.h	Sat Feb  3 13:26:44 2001
+++ linux/include/asm-mips/socket.h	Mon Jun  4 11:11:30 2001
@@ -71,6 +71,7 @@
 					/* level.  For writing rarp and	*/
 					/* other similar things on the	*/
 					/* user level.			*/
+#define	SOCK_MAX	(SOCK_PACKET+1)
 #endif
 
 #endif /* _ASM_SOCKET_H */
diff -u -r linux-2.4.5/include/asm-mips64/socket.h linux/include/asm-mips64/socket.h
--- linux-2.4.5/include/asm-mips64/socket.h	Sat Feb  3 13:26:44 2001
+++ linux/include/asm-mips64/socket.h	Mon Jun  4 11:11:30 2001
@@ -79,6 +79,7 @@
 					/* level.  For writing rarp and	*/
 					/* other similar things on the	*/
 					/* user level.			*/
+#define	SOCK_MAX	(SOCK_PACKET+1)
 #endif
 
 #endif /* _ASM_SOCKET_H */
diff -u -r linux-2.4.5/include/asm-parisc/socket.h linux/include/asm-parisc/socket.h
--- linux-2.4.5/include/asm-parisc/socket.h	Sat Feb  3 13:26:44 2001
+++ linux/include/asm-parisc/socket.h	Mon Jun  4 11:11:30 2001
@@ -56,6 +56,7 @@
 				/* level.  For writing rarp and	*/
 				/* other similar things on the	*/
 				/* user level.			*/
+#define	SOCK_MAX	(SOCK_PACKET+1)
 #endif
 
 #endif /* _ASM_SOCKET_H */
diff -u -r linux-2.4.5/include/asm-ppc/socket.h linux/include/asm-ppc/socket.h
--- linux-2.4.5/include/asm-ppc/socket.h	Mon May 21 17:02:06 2001
+++ linux/include/asm-ppc/socket.h	Mon Jun  4 11:11:30 2001
@@ -67,6 +67,7 @@
 					/* level.  For writing rarp and	*/
 					/* other similar things on the	*/
 					/* user level.			*/
+#define	SOCK_MAX	(SOCK_PACKET+1)
 #endif
 
 #endif /* _ASM_SOCKET_H */
diff -u -r linux-2.4.5/include/asm-s390/socket.h linux/include/asm-s390/socket.h
--- linux-2.4.5/include/asm-s390/socket.h	Sat Feb  3 13:26:44 2001
+++ linux/include/asm-s390/socket.h	Mon Jun  4 11:11:30 2001
@@ -66,6 +66,7 @@
 					/* level.  For writing rarp and	*/
 					/* other similar things on the	*/
 					/* user level.			*/
+#define	SOCK_MAX	(SOCK_PACKET+1)
 #endif
 
 #endif /* _ASM_SOCKET_H */
diff -u -r linux-2.4.5/include/asm-s390x/socket.h linux/include/asm-s390x/socket.h
--- linux-2.4.5/include/asm-s390x/socket.h	Fri Mar  2 13:12:06 2001
+++ linux/include/asm-s390x/socket.h	Mon Jun  4 11:11:30 2001
@@ -65,6 +65,7 @@
 					/* level.  For writing rarp and	*/
 					/* other similar things on the	*/
 					/* user level.			*/
+#define	SOCK_MAX	(SOCK_PACKET+1)
 #endif
 
 #endif /* _ASM_SOCKET_H */
diff -u -r linux-2.4.5/include/asm-sh/socket.h linux/include/asm-sh/socket.h
--- linux-2.4.5/include/asm-sh/socket.h	Sat Feb  3 13:26:44 2001
+++ linux/include/asm-sh/socket.h	Mon Jun  4 11:11:30 2001
@@ -58,6 +58,7 @@
 					/* level.  For writing rarp and	*/
 					/* other similar things on the	*/
 					/* user level.			*/
+#define	SOCK_MAX	(SOCK_PACKET+1)
 #endif
 
 #endif /* __ASM_SH_SOCKET_H */
diff -u -r linux-2.4.5/include/asm-sparc/socket.h linux/include/asm-sparc/socket.h
--- linux-2.4.5/include/asm-sparc/socket.h	Sat Feb  3 13:26:44 2001
+++ linux/include/asm-sparc/socket.h	Mon Jun  4 11:11:30 2001
@@ -63,6 +63,7 @@
 					/* level.  For writing rarp and	*/
 					/* other similar things on the	*/
 					/* user level.			*/
+#define	SOCK_MAX	(SOCK_PACKET+1)
 #endif
 
 #endif /* _ASM_SOCKET_H */
diff -u -r linux-2.4.5/include/asm-sparc64/socket.h linux/include/asm-sparc64/socket.h
--- linux-2.4.5/include/asm-sparc64/socket.h	Sat Feb  3 13:26:44 2001
+++ linux/include/asm-sparc64/socket.h	Mon Jun  4 11:11:30 2001
@@ -63,6 +63,7 @@
 					/* level.  For writing rarp and	*/
 					/* other similar things on the	*/
 					/* user level.			*/
+#define	SOCK_MAX	(SOCK_PACKET+1)
 #endif
 
 #endif /* _ASM_SOCKET_H */
diff -u -r linux-2.4.5/include/net/protocol.h linux/include/net/protocol.h
--- linux-2.4.5/include/net/protocol.h	Fri May 25 20:01:27 2001
+++ linux/include/net/protocol.h	Mon Jun  4 18:28:00 2001
@@ -63,19 +63,45 @@
 
 #endif
 
+/* This is used to register socket interfaces for IP protocols.  */
+struct inet_protosw {
+	struct list_head list;
+
+        /* These two fields form the lookup key.  */
+	unsigned short	 type;	   /* This is the 2nd argument to socket(2). */
+	int		 protocol; /* This is the L4 protocol number.  */
+
+	struct proto	 *prot;
+	struct proto_ops *ops;
+  
+	char             no_check;   /* checksum on rcv/xmit/none? */
+	unsigned char	 reuse;	     /* Are ports automatically reusable? */
+	int              capability; /* Which (if any) capability do
+				      * we need to use this socket
+				      * interface?
+				      */
+};
+
+
 extern struct inet_protocol *inet_protocol_base;
 extern struct inet_protocol *inet_protos[MAX_INET_PROTOS];
+extern struct list_head inetsw[SOCK_MAX];
 
 #if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
 extern struct inet6_protocol *inet6_protos[MAX_INET_PROTOS];
+extern struct list_head inetsw6[SOCK_MAX];
 #endif
 
 extern void	inet_add_protocol(struct inet_protocol *prot);
 extern int	inet_del_protocol(struct inet_protocol *prot);
+extern void	inet_register_protosw(struct inet_protosw *p);
+extern void	inet_unregister_protosw(struct inet_protosw *p);
 
 #if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
 extern void	inet6_add_protocol(struct inet6_protocol *prot);
 extern int	inet6_del_protocol(struct inet6_protocol *prot);
+extern void	inet6_register_protosw(struct inet_protosw *p);
+extern void	inet6_unregister_protosw(struct inet_protosw *p);
 #endif
 
 #endif	/* _PROTOCOL_H */
diff -u -r linux-2.4.5/net/ipv4/af_inet.c linux/net/ipv4/af_inet.c
--- linux-2.4.5/net/ipv4/af_inet.c	Tue May  1 22:59:24 2001
+++ linux/net/ipv4/af_inet.c	Mon Jun  4 11:16:27 2001
@@ -14,6 +14,8 @@
  *
  * Changes (see also sock.c)
  *
+ *		piggy,
+ *		Karl Knutson	:	Socket protocol table
  *		A.N.Kuznetsov	:	Socket death error in accept().
  *		John Richardson :	Fix non blocking error in connect()
  *					so sockets that fail to connect
@@ -88,6 +90,7 @@
 #include <linux/smp_lock.h>
 #include <linux/inet.h>
 #include <linux/netdevice.h>
+#include <linux/brlock.h>
 #include <net/ip.h>
 #include <net/protocol.h>
 #include <net/arp.h>
@@ -142,6 +145,11 @@
 int (*br_ioctl_hook)(unsigned long);
 #endif
 
+/* The inetsw table contains everything that inet_create needs to
+ * build a new socket.
+ */
+struct list_head inetsw[SOCK_MAX];
+
 /* New destruction routine */
 
 void inet_sock_destruct(struct sock *sk)
@@ -309,46 +317,56 @@
 static int inet_create(struct socket *sock, int protocol)
 {
 	struct sock *sk;
-	struct proto *prot;
+        struct list_head *p;
+        struct inet_protosw *answer;
 
 	sock->state = SS_UNCONNECTED;
 	sk = sk_alloc(PF_INET, GFP_KERNEL, 1);
 	if (sk == NULL) 
 		goto do_oom;
-
-	switch (sock->type) {
-	case SOCK_STREAM:
-		if (protocol && protocol != IPPROTO_TCP)
-			goto free_and_noproto;
-		protocol = IPPROTO_TCP;
-		prot = &tcp_prot;
-		sock->ops = &inet_stream_ops;
-		break;
-	case SOCK_SEQPACKET:
-		goto free_and_badtype;
-	case SOCK_DGRAM:
-		if (protocol && protocol != IPPROTO_UDP)
-			goto free_and_noproto;
-		protocol = IPPROTO_UDP;
-		sk->no_check = UDP_CSUM_DEFAULT;
-		prot=&udp_prot;
-		sock->ops = &inet_dgram_ops;
-		break;
-	case SOCK_RAW:
-		if (!capable(CAP_NET_RAW))
-			goto free_and_badperm;
-		if (!protocol)
-			goto free_and_noproto;
-		prot = &raw_prot;
-		sk->reuse = 1;
-		sk->num = protocol;
-		sock->ops = &inet_dgram_ops;
-		if (protocol == IPPROTO_RAW)
-			sk->protinfo.af_inet.hdrincl = 1;
-		break;
-	default:
-		goto free_and_badtype;
-	}
+  
+        /* Look for the requested type/protocol pair.  */
+        answer = NULL;
+  	br_read_lock_bh(BR_NETPROTO_LOCK);
+        list_for_each(p, &inetsw[sock->type]) {
+                answer = list_entry(p, struct inet_protosw, list);
+                /* Check the non-wild match.  */
+                if (protocol == answer->protocol) {
+                        if (protocol != IPPROTO_IP) {
+                                break;
+                        }
+                } else {
+                        /* Check for the two wild cases. */
+                        if (IPPROTO_IP == protocol) {
+                                protocol = answer->protocol;
+                                break;
+                        }
+                        if (IPPROTO_IP == answer->protocol) {
+                                break;
+                        }
+                }
+                answer = NULL;
+        }
+  	br_read_unlock_bh(BR_NETPROTO_LOCK);
+
+        if (!answer)
+                goto free_and_badtype;
+        if (answer->capability > 0 && !capable(answer->capability))
+                goto free_and_badperm;
+        if (!protocol)
+                goto free_and_noproto;
+
+        sock->ops = answer->ops;
+        sk->prot = answer->prot;
+        sk->no_check = answer->no_check;
+        if (answer->reuse)
+                sk->reuse = answer->reuse;
+
+        if (SOCK_RAW == sock->type) {
+                sk->num = protocol;
+                if (IPPROTO_RAW == protocol)
+                        sk->protinfo.af_inet.hdrincl = 1;
+        }
 
 	if (ipv4_config.no_pmtu_disc)
 		sk->protinfo.af_inet.pmtudisc = IP_PMTUDISC_DONT;
@@ -365,8 +383,7 @@
 	sk->family	= PF_INET;
 	sk->protocol	= protocol;
 
-	sk->prot	= prot;
-	sk->backlog_rcv = prot->backlog_rcv;
+	sk->backlog_rcv = sk->prot->backlog_rcv;
 
 	sk->protinfo.af_inet.ttl	= sysctl_ip_default_ttl;
 
@@ -969,6 +986,70 @@
 extern void tcp_init(void);
 extern void tcp_v4_init(struct net_proto_family *);
 
+/* Upon startup we insert all the elements in inetsw_array[] into
+ * the linked list inetsw.
+ */
+static struct inet_protosw inetsw_array[] =
+{
+        {
+                type:        SOCK_STREAM,
+                protocol:    IPPROTO_TCP,
+                prot:        &tcp_prot,
+                ops:         &inet_stream_ops,
+                no_check:     0,
+                reuse:        0,
+                capability:  -1,
+        },
+
+        {
+                type:        SOCK_DGRAM,
+                protocol:    IPPROTO_UDP,
+                prot:        &udp_prot,
+                ops:         &inet_dgram_ops,
+                no_check:    UDP_CSUM_DEFAULT,
+                reuse:       0,
+                capability:  -1,
+       },
+        
+
+       {
+               type:        SOCK_RAW,
+               protocol:    IPPROTO_IP,	/* wild card */
+               prot:        &raw_prot,
+               ops:         &inet_dgram_ops,
+               no_check:    UDP_CSUM_DEFAULT,
+               reuse:       1,
+               capability:  CAP_NET_RAW,
+       }
+}; /* struct inet_protosw inetsw_array[] */
+
+#define INETSW_ARRAY_LEN (sizeof(inetsw_array) / sizeof(struct inet_protosw))
+
+void
+inet_register_protosw(struct inet_protosw *p)
+{
+	/* Add to the BEGINNING so that we override any existing
+	 * entry.  This means that when we remove this entry, the
+	 * system automatically returns to the old behavior.
+	 */
+        if (p->type < SOCK_MAX) {
+                br_write_lock_bh(BR_NETPROTO_LOCK);
+                list_add(&p->list, &inetsw[p->type]);
+                br_write_unlock_bh(BR_NETPROTO_LOCK);
+        } else {
+                printk(KERN_DEBUG "Ignoring attempt to register illegal socket type %d.\n",
+                       p->type);
+        }
+} /* inet_protosw_register() */
+
+void
+inet_unregister_protosw(struct inet_protosw *p)
+{
+        br_write_lock_bh(BR_NETPROTO_LOCK);
+	list_del(&p->list);
+  	br_write_unlock_bh(BR_NETPROTO_LOCK);
+} /* inet_protosw_unregister() */
+
 
 /*
  *	Called by socket.c on kernel startup.  
@@ -978,6 +1059,8 @@
 {
 	struct sk_buff *dummy_skb;
 	struct inet_protocol *p;
+	struct inet_protosw *q;
+        struct list_head *r;
 
 	printk(KERN_INFO "NET4: Linux TCP/IP 1.0 for NET4.0\n");
 
@@ -1003,6 +1086,14 @@
 		printk("%s%s",p->name,tmp?", ":"\n");
 		p = tmp;
 	}
+
+        /* Register the socket-side information for inet_create.  */
+        for(r = &inetsw[0]; r < &inetsw[SOCK_MAX]; ++r) {
+                INIT_LIST_HEAD(r);
+        }
+        for(q = inetsw_array; q < &inetsw_array[INETSW_ARRAY_LEN]; ++q) {
+                inet_register_protosw(q);
+        }
 
 	/*
 	 *	Set the ARP module up
diff -u -r linux-2.4.5/net/ipv6/af_inet6.c linux/net/ipv6/af_inet6.c
--- linux-2.4.5/net/ipv6/af_inet6.c	Thu Apr 12 14:11:39 2001
+++ linux/net/ipv6/af_inet6.c	Mon Jun  4 18:39:53 2001
@@ -10,6 +10,7 @@
  *	$Id: af_inet6.c,v 1.63 2001/03/02 03:13:05 davem Exp $
  *
  * 	Fixes:
+ *	piggy, Karl Knutson	:	Socket protocol table
  * 	Hideaki YOSHIFUJI	:	sin6_scope_id support
  * 	Arnaldo Melo		: 	check proc_net_create return, cleanups
  *
@@ -44,6 +45,7 @@
 #include <linux/inet.h>
 #include <linux/netdevice.h>
 #include <linux/icmpv6.h>
+#include <linux/brlock.h>
 #include <linux/smp_lock.h>
 
 #include <net/ip.h>
@@ -71,9 +73,6 @@
 MODULE_PARM(unloadable, "i");
 #endif
 
-extern struct proto_ops inet6_stream_ops;
-extern struct proto_ops inet6_dgram_ops;
-
 /* IPv6 procfs goodies... */
 
 #ifdef CONFIG_PROC_FS
@@ -93,6 +92,11 @@
 atomic_t inet6_sock_nr;
 #endif
 
+/* The inetsw table contains everything that inet_create needs to
+ * build a new socket.
+ */
+struct list_head inetsw6[SOCK_MAX];
+
 static void inet6_sock_destruct(struct sock *sk)
 {
 	inet_sock_destruct(sk);
@@ -106,47 +110,64 @@
 static int inet6_create(struct socket *sock, int protocol)
 {
 	struct sock *sk;
-	struct proto *prot;
+        struct list_head *p;
+        struct inet_protosw *answer;
 
 	sk = sk_alloc(PF_INET6, GFP_KERNEL, 1);
 	if (sk == NULL) 
 		goto do_oom;
 
-	if(sock->type == SOCK_STREAM || sock->type == SOCK_SEQPACKET) {
-		if (protocol && protocol != IPPROTO_TCP) 
-			goto free_and_noproto;
-		protocol = IPPROTO_TCP;
-		prot = &tcpv6_prot;
-		sock->ops = &inet6_stream_ops;
-	} else if(sock->type == SOCK_DGRAM) {
-		if (protocol && protocol != IPPROTO_UDP) 
-			goto free_and_noproto;
-		protocol = IPPROTO_UDP;
-		sk->no_check = UDP_CSUM_DEFAULT;
-		prot=&udpv6_prot;
-		sock->ops = &inet6_dgram_ops;
-	} else if(sock->type == SOCK_RAW) {
-		if (!capable(CAP_NET_RAW))
-			goto free_and_badperm;
-		if (!protocol) 
-			goto free_and_noproto;
-		prot = &rawv6_prot;
-		sock->ops = &inet6_dgram_ops;
-		sk->reuse = 1;
-		sk->num = protocol;
-	} else {
-		goto free_and_badtype;
-	}
-	
+        /* Look for the requested type/protocol pair.  */
+        answer = NULL;
+	br_read_lock_bh(BR_NETPROTO_LOCK);
+        list_for_each(p, &inetsw6[sock->type]) {
+                answer = list_entry(p, struct inet_protosw, list);
+                /* Check the non-wild match.  */
+                if (protocol == answer->protocol) {
+                        if (protocol != IPPROTO_IP) {
+                                break;
+                        }
+                } else {
+                        /* Check for the two wild cases. */
+                        if (IPPROTO_IP == protocol) {
+                                protocol = answer->protocol;
+                                break;
+                        }
+                        if (IPPROTO_IP == answer->protocol) {
+                                break;
+                        }
+                }
+                answer = NULL;
+        }
+	br_read_unlock_bh(BR_NETPROTO_LOCK);
+
+        if (!answer)
+                goto free_and_badtype;
+        if (answer->capability > 0 && !capable(answer->capability))
+                goto free_and_badperm;
+        if (!protocol)
+                goto free_and_noproto;
+
+        sock->ops = answer->ops;
 	sock_init_data(sock, sk);
 
+	sk->prot		= answer->prot;
+        sk->no_check = answer->no_check;
+        if (answer->reuse)
+                sk->reuse = answer->reuse;
+
+        if (SOCK_RAW == sock->type) {
+                sk->num = protocol;
+                if (IPPROTO_RAW == protocol)
+                        sk->protinfo.af_inet.hdrincl = 1;
+        }
+
 	sk->destruct            = inet6_sock_destruct;
 	sk->zapped		= 0;
 	sk->family		= PF_INET6;
 	sk->protocol		= protocol;
 
-	sk->prot		= prot;
-	sk->backlog_rcv		= prot->backlog_rcv;
+	sk->backlog_rcv		= answer->prot->backlog_rcv;
 
 	sk->net_pinfo.af_inet6.hop_limit  = -1;
 	sk->net_pinfo.af_inet6.mcast_hops = -1;
@@ -175,9 +196,6 @@
 #endif
 	MOD_INC_USE_COUNT;
 
-	if (sk->type==SOCK_RAW && protocol==IPPROTO_RAW)
-		sk->protinfo.af_inet.hdrincl=1;
-
 	if (sk->num) {
 		/* It assumes that any protocol which allows
 		 * the user to assign a number at socket
@@ -186,7 +204,6 @@
 		sk->sport = ntohs(sk->num);
 		sk->prot->hash(sk);
 	}
-
 	if (sk->prot->init) {
 		int err = sk->prot->init(sk);
 		if (err != 0) {
@@ -504,9 +521,47 @@
 extern void ipv6_sysctl_unregister(void);
 #endif
 
+static struct inet_protosw rawv6_protosw = {
+        type:        SOCK_RAW,
+        protocol:    IPPROTO_IP,	/* wild card */
+        prot:        &rawv6_prot,
+        ops:         &inet6_dgram_ops,
+        no_check:    UDP_CSUM_DEFAULT,
+        reuse:       1,
+        capability:  CAP_NET_RAW,
+};
+
+#define INETSW6_ARRAY_LEN (sizeof(inetsw6_array) / sizeof(struct inet_protosw))
+
+void
+inet6_register_protosw(struct inet_protosw *p)
+{
+	/* Add to the BEGINNING so that we override any existing
+	 * entry.  This means that when we remove this entry, the
+	 * system automatically returns to the old behavior.
+	 */
+        if (p->type < SOCK_MAX) {
+                br_write_lock_bh(BR_NETPROTO_LOCK);
+                list_add(&p->list, &inetsw6[p->type]);
+                br_write_unlock_bh(BR_NETPROTO_LOCK);
+        } else {
+                printk(KERN_DEBUG "Ignoring attempt to register illegal socket type %d.\n",
+                       p->type);
+        }
+} /* inet_protosw_register() */
+
+void
+inet6_unregister_protosw(struct inet_protosw *p)
+{
+        br_write_lock_bh(BR_NETPROTO_LOCK);
+	list_del(&p->list);
+        br_write_unlock_bh(BR_NETPROTO_LOCK);
+} /* inet_protosw_unregister() */
+
 static int __init inet6_init(void)
 {
 	struct sk_buff *dummy_skb;
+        struct list_head *r;
 	int err;
 
 #ifdef MODULE
@@ -523,6 +578,16 @@
 		printk(KERN_CRIT "inet6_proto_init: size fault\n");
 		return -EINVAL;
 	}
+
+        /* Register the socket-side information for inet6_create.  */
+        for(r = &inetsw6[0]; r < &inetsw6[SOCK_MAX]; ++r) {
+                INIT_LIST_HEAD(r);
+        }
+
+        /* We MUST register RAW sockets before we create the ICMP6,
+         * IGMP6, or NDISC control sockets.
+         */
+        inet6_register_protosw(&rawv6_protosw);
 
 	/*
 	 *	ipngwg API draft makes clear that the correct semantics
diff -u -r linux-2.4.5/net/ipv6/tcp_ipv6.c linux/net/ipv6/tcp_ipv6.c
--- linux-2.4.5/net/ipv6/tcp_ipv6.c	Wed Apr 25 16:57:39 2001
+++ linux/net/ipv6/tcp_ipv6.c	Mon Jun  4 18:35:31 2001
@@ -2125,8 +2125,21 @@
 	"TCPv6"			/* name			*/
 };
 
+extern struct proto_ops inet6_stream_ops;
+
+static struct inet_protosw tcpv6_protosw = {
+        type:        SOCK_STREAM,
+        protocol:    IPPROTO_TCP,
+        prot:        &tcpv6_prot,
+        ops:         &inet6_stream_ops,
+        no_check:     0,
+        reuse:        0,
+        capability:  -1,
+};
+
 void __init tcpv6_init(void)
 {
 	/* register inet6 protocol */
 	inet6_add_protocol(&tcpv6_protocol);
+        inet6_register_protosw(&tcpv6_protosw);
 }
diff -u -r linux-2.4.5/net/ipv6/udp.c linux/net/ipv6/udp.c
--- linux-2.4.5/net/ipv6/udp.c	Thu Apr 12 14:11:39 2001
+++ linux/net/ipv6/udp.c	Mon Jun  4 18:36:03 2001
@@ -992,7 +992,21 @@
 	get_port:	udp_v6_get_port,
 };
 
+extern struct proto_ops inet6_dgram_ops;
+
+static struct inet_protosw udpv6_protosw = {
+        type:        SOCK_DGRAM,
+        protocol:    IPPROTO_UDP,
+        prot:        &udpv6_prot,
+        ops:         &inet6_dgram_ops,
+        no_check:    UDP_CSUM_DEFAULT,
+        reuse:        0,
+        capability:  -1,
+};
+
+
 void __init udpv6_init(void)
 {
 	inet6_add_protocol(&udpv6_protocol);
+        inet6_register_protosw(&udpv6_protosw);
 }
diff -u -r linux-2.4.5/net/netsyms.c linux/net/netsyms.c
--- linux-2.4.5/net/netsyms.c	Fri Apr 27 16:15:01 2001
+++ linux/net/netsyms.c	Mon Jun  4 11:11:30 2001
@@ -234,6 +234,8 @@
 EXPORT_SYMBOL(inetdev_lock);
 EXPORT_SYMBOL(inet_add_protocol);
 EXPORT_SYMBOL(inet_del_protocol);
+EXPORT_SYMBOL(inet_register_protosw);
+EXPORT_SYMBOL(inet_unregister_protosw);
 EXPORT_SYMBOL(ip_route_output_key);
 EXPORT_SYMBOL(ip_route_input);
 EXPORT_SYMBOL(icmp_send);
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/