patch-2.4.26 linux-2.4.26/net/sctp/protocol.c

Next file: linux-2.4.26/net/sctp/sm_make_chunk.c
Previous file: linux-2.4.26/net/sctp/proc.c
Back to the patch index
Back to the overall index

diff -urN linux-2.4.25/net/sctp/protocol.c linux-2.4.26/net/sctp/protocol.c
@@ -1,7 +1,7 @@
 /* SCTP kernel reference Implementation
+ * (C) Copyright IBM Corp. 2001, 2004
  * Copyright (c) 1999-2000 Cisco, Inc.
  * Copyright (c) 1999-2001 Motorola, Inc.
- * Copyright (c) 2001-2002 International Business Machines, Corp.
  * Copyright (c) 2001 Intel Corp.
  * Copyright (c) 2001 Nokia, Inc.
  * Copyright (c) 2001 La Monte H.P. Yarroll
@@ -50,15 +50,17 @@
 #include <linux/init.h>
 #include <linux/netdevice.h>
 #include <linux/inetdevice.h>
+#include <linux/seq_file.h>
 #include <net/protocol.h>
 #include <net/ip.h>
 #include <net/ipv6.h>
 #include <net/sctp/sctp.h>
 #include <net/addrconf.h>
 #include <net/inet_common.h>
+#include <net/inet_ecn.h>
 
 /* Global data structures. */
-struct sctp_protocol sctp_proto;
+struct sctp_globals sctp_globals;
 struct proc_dir_entry	*proc_net_sctp;
 DEFINE_SNMP_STAT(struct sctp_mib, sctp_statistics);
 
@@ -73,10 +75,16 @@
 static struct sctp_af *sctp_af_v4_specific;
 static struct sctp_af *sctp_af_v6_specific;
 
+kmem_cache_t *sctp_chunk_cachep;
+
 extern struct net_proto_family inet_family_ops;
 
 extern int sctp_snmp_proc_init(void);
 extern int sctp_snmp_proc_exit(void);
+extern int sctp_eps_proc_init(void);
+extern int sctp_eps_proc_exit(void);
+extern int sctp_assocs_proc_init(void);
+extern int sctp_assocs_proc_exit(void);
 
 /* Return the address of the control sock. */
 struct sock *sctp_get_ctl_sock(void)
@@ -87,8 +95,6 @@
 /* Set up the proc fs entry for the SCTP protocol. */
 __init int sctp_proc_init(void)
 {
-	int rc = 0;
-
 	if (!proc_net_sctp) {
 		struct proc_dir_entry *ent;
 		ent = proc_mkdir("net/sctp", 0);
@@ -96,20 +102,31 @@
 			ent->owner = THIS_MODULE;
 			proc_net_sctp = ent;
 		} else
-			rc = -ENOMEM;
+			goto out_nomem;
 	}
 
 	if (sctp_snmp_proc_init())
-		rc = -ENOMEM;
+		goto out_nomem;	
+	if (sctp_eps_proc_init())
+		goto out_nomem;	
+	if (sctp_assocs_proc_init())
+		goto out_nomem;	
+
+	return 0;
 
-	return rc;
+out_nomem:
+	return -ENOMEM;
 }
 
-/* Clean up the proc fs entry for the SCTP protocol. */
+/* Clean up the proc fs entry for the SCTP protocol. 
+ * Note: Do not make this __exit as it is used in the init error
+ * path.
+ */
 void sctp_proc_exit(void)
 {
-
 	sctp_snmp_proc_exit();
+	sctp_eps_proc_exit();
+	sctp_assocs_proc_exit();
 
 	if (proc_net_sctp) {
 		proc_net_sctp = NULL;
@@ -125,7 +142,7 @@
 {
 	struct in_device *in_dev;
 	struct in_ifaddr *ifa;
-	struct sockaddr_storage_list *addr;
+	struct sctp_sockaddr_entry *addr;
 
 	read_lock(&inetdev_lock);
 	if ((in_dev = __in_dev_get(dev)) == NULL) {
@@ -136,7 +153,7 @@
 	read_lock(&in_dev->lock);
 	for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) {
 		/* Add the address to the local list.  */
-		addr = t_new(struct sockaddr_storage_list, GFP_ATOMIC);
+		addr = t_new(struct sctp_sockaddr_entry, GFP_ATOMIC);
 		if (addr) {
 			addr->a.v4.sin_family = AF_INET;
 			addr->a.v4.sin_port = 0;
@@ -152,7 +169,7 @@
 /* Extract our IP addresses from the system and stash them in the
  * protocol structure.
  */
-static void __sctp_get_local_addr_list(struct sctp_protocol *proto)
+static void __sctp_get_local_addr_list(void)
 {
 	struct net_device *dev;
 	struct list_head *pos;
@@ -160,59 +177,58 @@
 
 	read_lock(&dev_base_lock);
 	for (dev = dev_base; dev; dev = dev->next) {
-		list_for_each(pos, &proto->address_families) {
+		list_for_each(pos, &sctp_address_families) {
 			af = list_entry(pos, struct sctp_af, list);
-			af->copy_addrlist(&proto->local_addr_list, dev);
+			af->copy_addrlist(&sctp_local_addr_list, dev);
 		}
 	}
 	read_unlock(&dev_base_lock);
 }
 
-static void sctp_get_local_addr_list(struct sctp_protocol *proto)
+static void sctp_get_local_addr_list(void)
 {
 	unsigned long flags;
 
-	sctp_spin_lock_irqsave(&sctp_proto.local_addr_lock, flags);
-	__sctp_get_local_addr_list(&sctp_proto);
-	sctp_spin_unlock_irqrestore(&sctp_proto.local_addr_lock, flags);
+	sctp_spin_lock_irqsave(&sctp_local_addr_lock, flags);
+	__sctp_get_local_addr_list();
+	sctp_spin_unlock_irqrestore(&sctp_local_addr_lock, flags);
 }
 
 /* Free the existing local addresses.  */
-static void __sctp_free_local_addr_list(struct sctp_protocol *proto)
+static void __sctp_free_local_addr_list(void)
 {
-	struct sockaddr_storage_list *addr;
+	struct sctp_sockaddr_entry *addr;
 	struct list_head *pos, *temp;
 
-	list_for_each_safe(pos, temp, &proto->local_addr_list) {
-		addr = list_entry(pos, struct sockaddr_storage_list, list);
+	list_for_each_safe(pos, temp, &sctp_local_addr_list) {
+		addr = list_entry(pos, struct sctp_sockaddr_entry, list);
 		list_del(pos);
 		kfree(addr);
 	}
 }
 
 /* Free the existing local addresses.  */
-static void sctp_free_local_addr_list(struct sctp_protocol *proto)
+static void sctp_free_local_addr_list(void)
 {
 	unsigned long flags;
 
-	sctp_spin_lock_irqsave(&proto->local_addr_lock, flags);
-	__sctp_free_local_addr_list(proto);
-	sctp_spin_unlock_irqrestore(&proto->local_addr_lock, flags);
+	sctp_spin_lock_irqsave(&sctp_local_addr_lock, flags);
+	__sctp_free_local_addr_list();
+	sctp_spin_unlock_irqrestore(&sctp_local_addr_lock, flags);
 }
 
 /* Copy the local addresses which are valid for 'scope' into 'bp'.  */
-int sctp_copy_local_addr_list(struct sctp_protocol *proto,
-			      struct sctp_bind_addr *bp, sctp_scope_t scope,
+int sctp_copy_local_addr_list(struct sctp_bind_addr *bp, sctp_scope_t scope,
 			      int gfp, int copy_flags)
 {
-	struct sockaddr_storage_list *addr;
+	struct sctp_sockaddr_entry *addr;
 	int error = 0;
 	struct list_head *pos;
 	unsigned long flags;
 
-	sctp_spin_lock_irqsave(&proto->local_addr_lock, flags);
-	list_for_each(pos, &proto->local_addr_list) {
-		addr = list_entry(pos, struct sockaddr_storage_list, list);
+	sctp_spin_lock_irqsave(&sctp_local_addr_lock, flags);
+	list_for_each(pos, &sctp_local_addr_list) {
+		addr = list_entry(pos, struct sctp_sockaddr_entry, list);
 		if (sctp_in_scope(&addr->a, scope)) {
 			/* Now that the address is in scope, check to see if
 			 * the address type is really supported by the local
@@ -232,7 +248,7 @@
 	}
 
 end_copy:
-	sctp_spin_unlock_irqrestore(&proto->local_addr_lock, flags);
+	sctp_spin_unlock_irqrestore(&sctp_local_addr_lock, flags);
 	return error;
 }
 
@@ -262,20 +278,45 @@
 static void sctp_v4_from_sk(union sctp_addr *addr, struct sock *sk)
 {
 	addr->v4.sin_family = AF_INET;
-	addr->v4.sin_port = (sk)->num;
-	addr->v4.sin_addr.s_addr = (sk)->rcv_saddr;
+	addr->v4.sin_port = sk->num;
+	addr->v4.sin_addr.s_addr = sk->rcv_saddr;
 }
 
-/* Initialize sk->rcv_saddr from sctp_addr. */
+/* Initialize sk->sk_rcv_saddr from sctp_addr. */
 static void sctp_v4_to_sk_saddr(union sctp_addr *addr, struct sock *sk)
 {
-	(sk)->rcv_saddr = addr->v4.sin_addr.s_addr;
+	sk->rcv_saddr = addr->v4.sin_addr.s_addr;
 }
 
-/* Initialize sk->daddr from sctp_addr. */
+/* Initialize sk->sk_daddr from sctp_addr. */
 static void sctp_v4_to_sk_daddr(union sctp_addr *addr, struct sock *sk)
 {
-	(sk)->daddr = addr->v4.sin_addr.s_addr;
+	sk->daddr = addr->v4.sin_addr.s_addr;
+}
+
+/* Initialize a sctp_addr from an address parameter. */
+static void sctp_v4_from_addr_param(union sctp_addr *addr,
+				    union sctp_addr_param *param,
+				    __u16 port, int iif)
+{
+	addr->v4.sin_family = AF_INET;
+	addr->v4.sin_port = port;
+	addr->v4.sin_addr.s_addr = param->v4.addr.s_addr;
+}
+
+/* Initialize an address parameter from a sctp_addr and return the length
+ * of the address parameter.
+ */
+static int sctp_v4_to_addr_param(const union sctp_addr *addr,
+				 union sctp_addr_param *param)
+{
+	int length = sizeof(sctp_ipv4addr_param_t);
+
+	param->v4.param_hdr.type = SCTP_PARAM_IPV4_ADDRESS;
+	param->v4.param_hdr.length = ntohs(length);
+	param->v4.addr.s_addr = addr->v4.sin_addr.s_addr;	
+
+	return length;
 }
 
 /* Initialize a sctp_addr from a dst_entry. */
@@ -323,7 +364,7 @@
  * Return 0 - If the address is a non-unicast or an illegal address.
  * Return 1 - If the address is a unicast.
  */
-static int sctp_v4_addr_valid(union sctp_addr *addr)
+static int sctp_v4_addr_valid(union sctp_addr *addr, struct sctp_opt *sp)
 {
 	/* Is this a non-unicast address or a unusable SCTP address? */
 	if (IS_IPV4_UNUSABLE_ADDRESS(&addr->v4.sin_addr.s_addr))
@@ -333,7 +374,7 @@
 }
 
 /* Should this be available for binding?   */
-static int sctp_v4_available(const union sctp_addr *addr)
+static int sctp_v4_available(union sctp_addr *addr, struct sctp_opt *sp)
 {
 	int ret = inet_addr_type(addr->v4.sin_addr.s_addr);
 
@@ -393,16 +434,20 @@
 {
 	struct rtable *rt;
 	struct rt_key key;
-	sctp_bind_addr_t *bp;
+	struct sctp_bind_addr *bp;
 	rwlock_t *addr_lock;
-	struct sockaddr_storage_list *laddr;
+	struct sctp_sockaddr_entry *laddr;
 	struct list_head *pos;
 	struct dst_entry *dst = NULL;
 	union sctp_addr dst_saddr;
-	
+
 	memset(&key, 0x0, sizeof(struct rt_key));
 	key.dst = daddr->v4.sin_addr.s_addr;
-	
+
+	if (asoc) {
+		key.tos = RT_CONN_FLAGS(asoc->base.sk);
+		key.oif = asoc->base.sk->bound_dev_if;
+	}
 	if (saddr)
 		key.src = saddr->v4.sin_addr.s_addr;
 
@@ -429,7 +474,7 @@
 		 */
 		sctp_read_lock(addr_lock);
 		list_for_each(pos, &bp->address_list) {
-			laddr = list_entry(pos, struct sockaddr_storage_list,
+			laddr = list_entry(pos, struct sctp_sockaddr_entry,
 					   list);
 			sctp_v4_dst_saddr(&dst_saddr, dst, bp->port);
 			if (sctp_v4_cmp_addr(&dst_saddr, &laddr->a))
@@ -449,7 +494,7 @@
 	 */
 	sctp_read_lock(addr_lock);
 	list_for_each(pos, &bp->address_list) {
-		laddr = list_entry(pos, struct sockaddr_storage_list, list);
+		laddr = list_entry(pos, struct sctp_sockaddr_entry, list);
 
 		if (AF_INET == laddr->a.sa.sa_family) {
 			key.src = laddr->a.v4.sin_addr.s_addr;
@@ -490,11 +535,17 @@
 }
 
 /* What interface did this skb arrive on? */
-int sctp_v4_skb_iif(const struct sk_buff *skb)
+static int sctp_v4_skb_iif(const struct sk_buff *skb)
 {
      	return ((struct rtable *)skb->dst)->rt_iif;
 }
 
+/* Was this packet marked by Explicit Congestion Notification? */
+static int sctp_v4_is_ce(const struct sk_buff *skb)
+{
+	return INET_ECN_is_ce(skb->nh.iph->tos);
+}
+
 /* Create and initialize a new sk for the socket returned by accept(). */
 struct sock *sctp_v4_create_accept_sk(struct sock *sk,
 				      struct sctp_association *asoc)
@@ -510,18 +561,18 @@
 	sock_init_data(NULL, newsk);
 	sk_set_owner(newsk, THIS_MODULE);
 
-	newsk->type = SOCK_STREAM;
+	newsk->sk_type = SOCK_STREAM;
 
-	newsk->prot = sk->prot;
-	newsk->no_check = sk->no_check;
-	newsk->reuse = sk->reuse;
-	newsk->shutdown = sk->shutdown;
-
-	newsk->destruct = inet_sock_destruct;
-	newsk->zapped = 0;
-	newsk->family = PF_INET;
-	newsk->protocol = IPPROTO_SCTP;
-	newsk->backlog_rcv = sk->prot->backlog_rcv;
+	newsk->sk_prot = sk->sk_prot;
+	newsk->sk_no_check = sk->sk_no_check;
+	newsk->sk_reuse = sk->sk_reuse;
+	newsk->sk_shutdown = sk->sk_shutdown;
+
+	newsk->sk_destruct = inet_sock_destruct;
+	newsk->sk_zapped = 0;
+	newsk->sk_family = PF_INET;
+	newsk->sk_protocol = IPPROTO_SCTP;
+	newsk->sk_backlog_rcv = sk->sk_prot->backlog_rcv;
 
 	newinet = inet_sk(newsk);
 
@@ -546,7 +597,7 @@
 	atomic_inc(&inet_sock_nr);
 #endif
 
-	if (0 != newsk->prot->init(newsk)) {
+	if (newsk->sk_prot->init(newsk)) {
 		inet_sock_release(newsk);
 		newsk = NULL;
 	}
@@ -555,6 +606,18 @@
 	return newsk;
 }
 
+/* Map address, empty for v4 family */
+static void sctp_v4_addr_v4map(struct sctp_opt *sp, union sctp_addr *addr)
+{
+	/* Empty */
+}
+
+/* Dump the v4 addr to the seq file. */
+static void sctp_v4_seq_dump_addr(struct seq_file *seq, union sctp_addr *addr)
+{
+	seq_printf(seq, "%d.%d.%d.%d ", NIPQUAD(addr->v4.sin_addr));
+}
+
 /* Event handler for inet address addition/deletion events.
  * Basically, whenever there is an event, we re-build our local address list.
  */
@@ -563,10 +626,10 @@
 {
 	unsigned long flags;
 
-	sctp_spin_lock_irqsave(&sctp_proto.local_addr_lock, flags);
-	__sctp_free_local_addr_list(&sctp_proto);
-	__sctp_get_local_addr_list(&sctp_proto);
-	sctp_spin_unlock_irqrestore(&sctp_proto.local_addr_lock, flags);
+	sctp_spin_lock_irqsave(&sctp_local_addr_lock, flags);
+	__sctp_free_local_addr_list();
+	__sctp_get_local_addr_list();
+	sctp_spin_unlock_irqrestore(&sctp_local_addr_lock, flags);
 
 	return NOTIFY_DONE;
 }
@@ -592,7 +655,7 @@
 		       "SCTP: Failed to create the SCTP control socket.\n");
 		return err;
 	}
-	sctp_ctl_socket->sk->allocation = GFP_ATOMIC;
+	sctp_ctl_socket->sk->sk_allocation = GFP_ATOMIC;
 	inet_sk(sctp_ctl_socket->sk)->ttl = MAXTTL;
 
 	return 0;
@@ -617,7 +680,7 @@
 	}
 
 	INIT_LIST_HEAD(&af->list);
-	list_add_tail(&af->list, &sctp_proto.address_families);
+	list_add_tail(&af->list, &sctp_address_families);
 	return 1;
 }
 
@@ -654,10 +717,13 @@
 	struct sockaddr_in *sin, *sinfrom;
 
 	if (msgname) {
+		struct sctp_association *asoc;
+
+		asoc = event->sndrcvinfo.sinfo_assoc_id;
 		sctp_inet_msgname(msgname, addr_len);
 		sin = (struct sockaddr_in *)msgname;
-		sinfrom = &event->event_asoc->peer.primary_addr.v4;
-		sin->sin_port = htons(event->event_asoc->peer.port);
+		sinfrom = &asoc->peer.primary_addr.v4;
+		sin->sin_port = htons(asoc->peer.port);
 		sin->sin_addr.s_addr = sinfrom->sin_addr.s_addr;
 	}
 }
@@ -678,7 +744,7 @@
 }
 
 /* Do we support this AF? */
-static int sctp_inet_af_supported(sa_family_t family)
+static int sctp_inet_af_supported(sa_family_t family, struct sctp_opt *sp)
 {
 	/* PF_INET only supports AF_INET addresses. */
 	return (AF_INET == family);
@@ -706,7 +772,7 @@
  */
 static int sctp_inet_bind_verify(struct sctp_opt *opt, union sctp_addr *addr)
 {
-	return sctp_v4_available(addr);
+	return sctp_v4_available(addr, opt);
 }
 
 /* Verify that sockaddr looks sendable.  Common verification has already
@@ -752,6 +818,7 @@
 	.send_verify   = sctp_inet_send_verify,
 	.supported_addrs = sctp_inet_supported_addrs,
 	.create_accept_sk = sctp_v4_create_accept_sk,
+	.addr_v4map	= sctp_v4_addr_v4map,
 	.af            = &sctp_ipv4_specific,
 };
 
@@ -821,6 +888,8 @@
 	.from_sk        = sctp_v4_from_sk,
 	.to_sk_saddr    = sctp_v4_to_sk_saddr,
 	.to_sk_daddr    = sctp_v4_to_sk_daddr,
+	.from_addr_param= sctp_v4_from_addr_param,
+	.to_addr_param  = sctp_v4_to_addr_param,	
 	.dst_saddr      = sctp_v4_dst_saddr,
 	.cmp_addr       = sctp_v4_cmp_addr,
 	.addr_valid     = sctp_v4_addr_valid,
@@ -829,6 +898,8 @@
 	.available      = sctp_v4_available,
 	.scope          = sctp_v4_scope,
 	.skb_iif        = sctp_v4_skb_iif,
+	.is_ce          = sctp_v4_is_ce,
+	.seq_dump_addr  = sctp_v4_seq_dump_addr,
 	.net_header_len = sizeof(struct iphdr),
 	.sockaddr_len   = sizeof(struct sockaddr_in),
 	.sa_family      = AF_INET,
@@ -873,6 +944,7 @@
 
 static void cleanup_sctp_mibs(void)
 {
+	return;
 }
 
 /* Initialize the universe into something sensible.  */
@@ -880,6 +952,8 @@
 {
 	int i;
 	int status = 0;
+	unsigned long goal;
+	int order;
 
 	/* SCTP_DEBUG sanity check. */
 	if (!sctp_sanity_check())
@@ -892,6 +966,14 @@
 	inet_register_protosw(&sctp_seqpacket_protosw);
 	inet_register_protosw(&sctp_stream_protosw);
 
+	/* Allocate cache pools. */
+	sctp_chunk_cachep = kmem_cache_create("sctp_chunk",
+					       sizeof(struct sctp_chunk),
+					       0, SLAB_HWCACHE_ALIGN,
+					       NULL, NULL);
+	if (!sctp_chunk_cachep)
+		goto err_chunk_cachep;
+
 	/* Allocate and initialise sctp mibs.  */
 	status = init_sctp_mibs();
 	if (status)
@@ -910,91 +992,116 @@
 	 */
 	/* The following protocol parameters are RECOMMENDED:  */
 	/* RTO.Initial              - 3  seconds */
-	sctp_proto.rto_initial		= SCTP_RTO_INITIAL;
+	sctp_rto_initial		= SCTP_RTO_INITIAL;
 	/* RTO.Min                  - 1  second */
-	sctp_proto.rto_min	 	= SCTP_RTO_MIN;
+	sctp_rto_min	 		= SCTP_RTO_MIN;
 	/* RTO.Max                 -  60 seconds */
-	sctp_proto.rto_max 		= SCTP_RTO_MAX;
+	sctp_rto_max 			= SCTP_RTO_MAX;
 	/* RTO.Alpha                - 1/8 */
-	sctp_proto.rto_alpha	        = SCTP_RTO_ALPHA;
+	sctp_rto_alpha	        	= SCTP_RTO_ALPHA;
 	/* RTO.Beta                 - 1/4 */
-	sctp_proto.rto_beta		= SCTP_RTO_BETA;
-
+	sctp_rto_beta			= SCTP_RTO_BETA;
 	/* Valid.Cookie.Life        - 60  seconds */
-	sctp_proto.valid_cookie_life	= 60 * HZ;
+	sctp_valid_cookie_life		= 60 * HZ;
 
 	/* Whether Cookie Preservative is enabled(1) or not(0) */
-	sctp_proto.cookie_preserve_enable = 1;
+	sctp_cookie_preserve_enable 	= 1;
 
 	/* Max.Burst		    - 4 */
-	sctp_proto.max_burst = SCTP_MAX_BURST;
+	sctp_max_burst 			= SCTP_MAX_BURST;
 
 	/* Association.Max.Retrans  - 10 attempts
 	 * Path.Max.Retrans         - 5  attempts (per destination address)
 	 * Max.Init.Retransmits     - 8  attempts
 	 */
-	sctp_proto.max_retrans_association = 10;
-	sctp_proto.max_retrans_path	= 5;
-	sctp_proto.max_retrans_init	= 8;
+	sctp_max_retrans_association 	= 10;
+	sctp_max_retrans_path		= 5;
+	sctp_max_retrans_init		= 8;
 
 	/* HB.interval              - 30 seconds */
-	sctp_proto.hb_interval		= 30 * HZ;
+	sctp_hb_interval		= 30 * HZ;
 
 	/* Implementation specific variables. */
 
 	/* Initialize default stream count setup information. */
-	sctp_proto.max_instreams    = SCTP_DEFAULT_INSTREAMS;
-	sctp_proto.max_outstreams   = SCTP_DEFAULT_OUTSTREAMS;
+	sctp_max_instreams    		= SCTP_DEFAULT_INSTREAMS;
+	sctp_max_outstreams   		= SCTP_DEFAULT_OUTSTREAMS;
 
-	/* Allocate and initialize the association hash table.  */
-	sctp_proto.assoc_hashsize = 4096;
-	sctp_proto.assoc_hashbucket = (sctp_hashbucket_t *)
-		kmalloc(4096 * sizeof(sctp_hashbucket_t), GFP_KERNEL);
-	if (!sctp_proto.assoc_hashbucket) {
+	/* Size and allocate the association hash table.
+	 * The methodology is similar to that of the tcp hash tables.
+	 */
+	if (num_physpages >= (128 * 1024))
+		goal = num_physpages >> (22 - PAGE_SHIFT);
+	else
+		goal = num_physpages >> (24 - PAGE_SHIFT);
+
+	for (order = 0; (1UL << order) < goal; order++)
+		;
+
+	do {
+		sctp_assoc_hashsize = (1UL << order) * PAGE_SIZE /
+					sizeof(struct sctp_hashbucket);
+		if ((sctp_assoc_hashsize > (64 * 1024)) && order > 0)
+			continue;
+		sctp_assoc_hashtable = (struct sctp_hashbucket *)
+					__get_free_pages(GFP_ATOMIC, order);
+	} while (!sctp_assoc_hashtable && --order > 0);
+	if (!sctp_assoc_hashtable) {
 		printk(KERN_ERR "SCTP: Failed association hash alloc.\n");
 		status = -ENOMEM;
 		goto err_ahash_alloc;
 	}
-	for (i = 0; i < sctp_proto.assoc_hashsize; i++) {
-		sctp_proto.assoc_hashbucket[i].lock = RW_LOCK_UNLOCKED;
-		sctp_proto.assoc_hashbucket[i].chain = NULL;
+	for (i = 0; i < sctp_assoc_hashsize; i++) {
+		sctp_assoc_hashtable[i].lock = RW_LOCK_UNLOCKED;
+		sctp_assoc_hashtable[i].chain = NULL;
 	}
 
 	/* Allocate and initialize the endpoint hash table.  */
-	sctp_proto.ep_hashsize = 64;
-	sctp_proto.ep_hashbucket = (sctp_hashbucket_t *)
-		kmalloc(64 * sizeof(sctp_hashbucket_t), GFP_KERNEL);
-	if (!sctp_proto.ep_hashbucket) {
+	sctp_ep_hashsize = 64;
+	sctp_ep_hashtable = (struct sctp_hashbucket *)
+		kmalloc(64 * sizeof(struct sctp_hashbucket), GFP_KERNEL);
+	if (!sctp_ep_hashtable) {
 		printk(KERN_ERR "SCTP: Failed endpoint_hash alloc.\n");
 		status = -ENOMEM;
 		goto err_ehash_alloc;
 	}
-
-	for (i = 0; i < sctp_proto.ep_hashsize; i++) {
-		sctp_proto.ep_hashbucket[i].lock = RW_LOCK_UNLOCKED;
-		sctp_proto.ep_hashbucket[i].chain = NULL;
+	for (i = 0; i < sctp_ep_hashsize; i++) {
+		sctp_ep_hashtable[i].lock = RW_LOCK_UNLOCKED;
+		sctp_ep_hashtable[i].chain = NULL;
 	}
 
 	/* Allocate and initialize the SCTP port hash table.  */
-	sctp_proto.port_hashsize = 4096;
-	sctp_proto.port_hashtable = (sctp_bind_hashbucket_t *)
-		kmalloc(4096 * sizeof(sctp_bind_hashbucket_t), GFP_KERNEL);
-	if (!sctp_proto.port_hashtable) {
+	do {
+		sctp_port_hashsize = (1UL << order) * PAGE_SIZE /
+					sizeof(struct sctp_bind_hashbucket);
+		if ((sctp_port_hashsize > (64 * 1024)) && order > 0)
+			continue;
+		sctp_port_hashtable = (struct sctp_bind_hashbucket *)
+					__get_free_pages(GFP_ATOMIC, order);
+	} while (!sctp_port_hashtable && --order > 0);
+	if (!sctp_port_hashtable) {
 		printk(KERN_ERR "SCTP: Failed bind hash alloc.");
 		status = -ENOMEM;
 		goto err_bhash_alloc;
 	}
-
-	sctp_proto.port_alloc_lock = SPIN_LOCK_UNLOCKED;
-	sctp_proto.port_rover = sysctl_local_port_range[0] - 1;
-	for (i = 0; i < sctp_proto.port_hashsize; i++) {
-		sctp_proto.port_hashtable[i].lock = SPIN_LOCK_UNLOCKED;
-		sctp_proto.port_hashtable[i].chain = NULL;
+	for (i = 0; i < sctp_port_hashsize; i++) {
+		sctp_port_hashtable[i].lock = SPIN_LOCK_UNLOCKED;
+		sctp_port_hashtable[i].chain = NULL;
 	}
 
+	sctp_port_alloc_lock = SPIN_LOCK_UNLOCKED;
+	sctp_port_rover = sysctl_local_port_range[0] - 1;
+
+	printk(KERN_INFO "SCTP: Hash tables configured "
+			 "(established %d bind %d)\n",
+		sctp_assoc_hashsize, sctp_port_hashsize);
+
+	/* Disable ADDIP by default. */
+	sctp_addip_enable = 0;
+
 	sctp_sysctl_register();
 
-	INIT_LIST_HEAD(&sctp_proto.address_families);
+	INIT_LIST_HEAD(&sctp_address_families);
 	sctp_register_af(&sctp_ipv4_specific);
 
 	status = sctp_v6_init();
@@ -1009,13 +1116,13 @@
 	}
 
 	/* Initialize the local address list. */
-	INIT_LIST_HEAD(&sctp_proto.local_addr_list);
-	sctp_proto.local_addr_lock = SPIN_LOCK_UNLOCKED;
+	INIT_LIST_HEAD(&sctp_local_addr_list);
+	sctp_local_addr_lock = SPIN_LOCK_UNLOCKED;
 
 	/* Register notifier for inet address additions/deletions. */
 	register_inetaddr_notifier(&sctp_inetaddr_notifier);
 
-	sctp_get_local_addr_list(&sctp_proto);
+	sctp_get_local_addr_list();
 
 	__unsafe(THIS_MODULE);
 	return 0;
@@ -1025,16 +1132,22 @@
 err_v6_init:
 	sctp_sysctl_unregister();
 	list_del(&sctp_ipv4_specific.list);
-	kfree(sctp_proto.port_hashtable);
+	free_pages((unsigned long)sctp_port_hashtable,
+		   get_order(sctp_port_hashsize *
+			     sizeof(struct sctp_bind_hashbucket)));
 err_bhash_alloc:
-	kfree(sctp_proto.ep_hashbucket);
+	kfree(sctp_ep_hashtable);
 err_ehash_alloc:
-	kfree(sctp_proto.assoc_hashbucket);
+	free_pages((unsigned long)sctp_assoc_hashtable,
+		   get_order(sctp_assoc_hashsize *
+			     sizeof(struct sctp_hashbucket)));
 err_ahash_alloc:
 	sctp_dbg_objcnt_exit();
 	sctp_proc_exit();
 	cleanup_sctp_mibs();
 err_init_mibs:
+	kmem_cache_destroy(sctp_chunk_cachep);
+err_chunk_cachep:
 	inet_del_protocol(&sctp_protocol);
 	inet_unregister_protosw(&sctp_seqpacket_protosw);
 	inet_unregister_protosw(&sctp_stream_protosw);
@@ -1052,7 +1165,7 @@
 	unregister_inetaddr_notifier(&sctp_inetaddr_notifier);
 
 	/* Free the local address list.  */
-	sctp_free_local_addr_list(&sctp_proto);
+	sctp_free_local_addr_list();
 
 	/* Free the control endpoint.  */
 	sock_release(sctp_ctl_socket);
@@ -1061,9 +1174,15 @@
 	sctp_sysctl_unregister();
 	list_del(&sctp_ipv4_specific.list);
 
-	kfree(sctp_proto.assoc_hashbucket);
-	kfree(sctp_proto.ep_hashbucket);
-	kfree(sctp_proto.port_hashtable);
+	free_pages((unsigned long)sctp_assoc_hashtable,
+		   get_order(sctp_assoc_hashsize *
+			     sizeof(struct sctp_hashbucket)));
+	kfree(sctp_ep_hashtable);
+	free_pages((unsigned long)sctp_port_hashtable,
+		   get_order(sctp_port_hashsize *
+			     sizeof(struct sctp_bind_hashbucket)));
+
+	kmem_cache_destroy(sctp_chunk_cachep);
 
 	sctp_dbg_objcnt_exit();
 	sctp_proc_exit();

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)