-/* SCTP kernel reference Implementation
+/* SCTP kernel implementation
* (C) Copyright IBM Corp. 2001, 2004
* Copyright (c) 1999-2000 Cisco, Inc.
* Copyright (c) 1999-2001 Motorola, Inc.
* Copyright (c) 2001 Nokia, Inc.
* Copyright (c) 2001 La Monte H.P. Yarroll
*
- * This file is part of the SCTP kernel reference Implementation
+ * This file is part of the SCTP kernel implementation
*
* Initialization/cleanup for SCTP protocol support.
*
- * The SCTP reference implementation is free software;
+ * This SCTP implementation is free software;
* you can redistribute it and/or modify it under the terms of
* the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
- * The SCTP reference implementation is distributed in the hope that it
+ * This SCTP implementation is distributed in the hope that it
* will be useful, but WITHOUT ANY WARRANTY; without even the implied
* ************************
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#include <linux/netdevice.h>
#include <linux/inetdevice.h>
#include <linux/seq_file.h>
+#include <linux/bootmem.h>
+#include <net/net_namespace.h>
#include <net/protocol.h>
#include <net/ip.h>
#include <net/ipv6.h>
struct kmem_cache *sctp_chunk_cachep __read_mostly;
struct kmem_cache *sctp_bucket_cachep __read_mostly;
+int sysctl_sctp_mem[3];
+int sysctl_sctp_rmem[3];
+int sysctl_sctp_wmem[3];
+
/* Return the address of the control sock. */
struct sock *sctp_get_ctl_sock(void)
{
{
if (!proc_net_sctp) {
struct proc_dir_entry *ent;
- ent = proc_mkdir("net/sctp", NULL);
+ ent = proc_mkdir("sctp", init_net.proc_net);
if (ent) {
ent->owner = THIS_MODULE;
proc_net_sctp = ent;
if (proc_net_sctp) {
proc_net_sctp = NULL;
- remove_proc_entry("net/sctp", NULL);
+ remove_proc_entry("sctp", init_net.proc_net);
}
}
struct sctp_af *af;
read_lock(&dev_base_lock);
- for_each_netdev(dev) {
+ for_each_netdev(&init_net, dev) {
__list_for_each(pos, &sctp_address_families) {
af = list_entry(pos, struct sctp_af, list);
af->copy_addrlist(&sctp_local_addr_list, dev);
(((AF_INET6 == addr->a.sa.sa_family) &&
(copy_flags & SCTP_ADDR6_ALLOWED) &&
(copy_flags & SCTP_ADDR6_PEERSUPP)))) {
- error = sctp_add_bind_addr(bp, &addr->a, 1,
- GFP_ATOMIC);
+ error = sctp_add_bind_addr(bp, &addr->a,
+ SCTP_ADDR_SRC, GFP_ATOMIC);
if (error)
goto end_copy;
}
static void sctp_v4_inaddr_any(union sctp_addr *addr, __be16 port)
{
addr->v4.sin_family = AF_INET;
- addr->v4.sin_addr.s_addr = INADDR_ANY;
+ addr->v4.sin_addr.s_addr = htonl(INADDR_ANY);
addr->v4.sin_port = port;
}
/* Is this a wildcard address? */
static int sctp_v4_is_any(const union sctp_addr *addr)
{
- return INADDR_ANY == addr->v4.sin_addr.s_addr;
+ return htonl(INADDR_ANY) == addr->v4.sin_addr.s_addr;
}
/* This function checks if the address is a valid address to be used for
const struct sk_buff *skb)
{
/* Is this a non-unicast address or a unusable SCTP address? */
- if (IS_IPV4_UNUSABLE_ADDRESS(&addr->v4.sin_addr.s_addr))
+ if (IS_IPV4_UNUSABLE_ADDRESS(addr->v4.sin_addr.s_addr))
return 0;
/* Is this a broadcast address? */
- if (skb && ((struct rtable *)skb->dst)->rt_flags & RTCF_BROADCAST)
+ if (skb && skb->rtable->rt_flags & RTCF_BROADCAST)
return 0;
return 1;
/* Should this be available for binding? */
static int sctp_v4_available(union sctp_addr *addr, struct sctp_sock *sp)
{
- int ret = inet_addr_type(addr->v4.sin_addr.s_addr);
+ int ret = inet_addr_type(&init_net, addr->v4.sin_addr.s_addr);
- if (addr->v4.sin_addr.s_addr != INADDR_ANY &&
+ if (addr->v4.sin_addr.s_addr != htonl(INADDR_ANY) &&
ret != RTN_LOCAL &&
!sp->inet.freebind &&
!sysctl_ip_nonlocal_bind)
*/
/* Check for unusable SCTP addresses. */
- if (IS_IPV4_UNUSABLE_ADDRESS(&addr->v4.sin_addr.s_addr)) {
+ if (IS_IPV4_UNUSABLE_ADDRESS(addr->v4.sin_addr.s_addr)) {
retval = SCTP_SCOPE_UNUSABLE;
- } else if (LOOPBACK(addr->v4.sin_addr.s_addr)) {
+ } else if (ipv4_is_loopback(addr->v4.sin_addr.s_addr)) {
retval = SCTP_SCOPE_LOOPBACK;
- } else if (IS_IPV4_LINK_ADDRESS(&addr->v4.sin_addr.s_addr)) {
+ } else if (ipv4_is_linklocal_169(addr->v4.sin_addr.s_addr)) {
retval = SCTP_SCOPE_LINK;
- } else if (IS_IPV4_PRIVATE_ADDRESS(&addr->v4.sin_addr.s_addr)) {
+ } else if (ipv4_is_private_10(addr->v4.sin_addr.s_addr) ||
+ ipv4_is_private_172(addr->v4.sin_addr.s_addr) ||
+ ipv4_is_private_192(addr->v4.sin_addr.s_addr)) {
retval = SCTP_SCOPE_PRIVATE;
} else {
retval = SCTP_SCOPE_GLOBAL;
struct rtable *rt;
struct flowi fl;
struct sctp_bind_addr *bp;
- rwlock_t *addr_lock;
struct sctp_sockaddr_entry *laddr;
- struct list_head *pos;
struct dst_entry *dst = NULL;
union sctp_addr dst_saddr;
fl.fl4_src = saddr->v4.sin_addr.s_addr;
SCTP_DEBUG_PRINTK("%s: DST:%u.%u.%u.%u, SRC:%u.%u.%u.%u - ",
- __FUNCTION__, NIPQUAD(fl.fl4_dst),
+ __func__, NIPQUAD(fl.fl4_dst),
NIPQUAD(fl.fl4_src));
- if (!ip_route_output_key(&rt, &fl)) {
+ if (!ip_route_output_key(&init_net, &rt, &fl)) {
dst = &rt->u.dst;
}
goto out;
bp = &asoc->base.bind_addr;
- addr_lock = &asoc->base.addr_lock;
if (dst) {
/* Walk through the bind address list and look for a bind
* address that matches the source address of the returned dst.
*/
- sctp_read_lock(addr_lock);
- list_for_each(pos, &bp->address_list) {
- laddr = list_entry(pos, struct sctp_sockaddr_entry,
- list);
- if (!laddr->use_as_src)
+ rcu_read_lock();
+ list_for_each_entry_rcu(laddr, &bp->address_list, list) {
+ if (!laddr->valid || (laddr->state != SCTP_ADDR_SRC))
continue;
sctp_v4_dst_saddr(&dst_saddr, dst, htons(bp->port));
if (sctp_v4_cmp_addr(&dst_saddr, &laddr->a))
goto out_unlock;
}
- sctp_read_unlock(addr_lock);
+ rcu_read_unlock();
/* None of the bound addresses match the source address of the
* dst. So release it.
/* Walk through the bind address list and try to get a dst that
* matches a bind address as the source address.
*/
- sctp_read_lock(addr_lock);
- list_for_each(pos, &bp->address_list) {
- laddr = list_entry(pos, struct sctp_sockaddr_entry, list);
-
- if ((laddr->use_as_src) &&
+ rcu_read_lock();
+ list_for_each_entry_rcu(laddr, &bp->address_list, list) {
+ if (!laddr->valid)
+ continue;
+ if ((laddr->state == SCTP_ADDR_SRC) &&
(AF_INET == laddr->a.sa.sa_family)) {
fl.fl4_src = laddr->a.v4.sin_addr.s_addr;
- if (!ip_route_output_key(&rt, &fl)) {
+ if (!ip_route_output_key(&init_net, &rt, &fl)) {
dst = &rt->u.dst;
goto out_unlock;
}
}
out_unlock:
- sctp_read_unlock(addr_lock);
+ rcu_read_unlock();
out:
if (dst)
SCTP_DEBUG_PRINTK("rt_dst:%u.%u.%u.%u, rt_src:%u.%u.%u.%u\n",
/* What interface did this skb arrive on? */
static int sctp_v4_skb_iif(const struct sk_buff *skb)
{
- return ((struct rtable *)skb->dst)->rt_iif;
+ return skb->rtable->rt_iif;
}
/* Was this packet marked by Explicit Congestion Notification? */
{
struct inet_sock *inet = inet_sk(sk);
struct inet_sock *newinet;
- struct sock *newsk = sk_alloc(PF_INET, GFP_KERNEL, sk->sk_prot, 1);
+ struct sock *newsk = sk_alloc(sk->sk_net, PF_INET, GFP_KERNEL,
+ sk->sk_prot);
if (!newsk)
goto out;
struct in_ifaddr *ifa = (struct in_ifaddr *)ptr;
struct sctp_sockaddr_entry *addr = NULL;
struct sctp_sockaddr_entry *temp;
+ int found = 0;
+
+ if (ifa->ifa_dev->dev->nd_net != &init_net)
+ return NOTIFY_DONE;
switch (ev) {
case NETDEV_UP:
list_for_each_entry_safe(addr, temp,
&sctp_local_addr_list, list) {
if (addr->a.v4.sin_addr.s_addr == ifa->ifa_local) {
+ found = 1;
addr->valid = 0;
list_del_rcu(&addr->list);
break;
}
}
spin_unlock_bh(&sctp_local_addr_lock);
- if (addr && !addr->valid)
+ if (found)
call_rcu(&addr->rcu, sctp_local_addr_free);
break;
}
/* PF_INET only supports AF_INET addresses. */
if (addr1->sa.sa_family != addr2->sa.sa_family)
return 0;
- if (INADDR_ANY == addr1->v4.sin_addr.s_addr ||
- INADDR_ANY == addr2->v4.sin_addr.s_addr)
+ if (htonl(INADDR_ANY) == addr1->v4.sin_addr.s_addr ||
+ htonl(INADDR_ANY) == addr2->v4.sin_addr.s_addr)
return 1;
if (addr1->v4.sin_addr.s_addr == addr2->v4.sin_addr.s_addr)
return 1;
{
SCTP_DEBUG_PRINTK("%s: skb:%p, len:%d, "
"src:%u.%u.%u.%u, dst:%u.%u.%u.%u\n",
- __FUNCTION__, skb, skb->len,
- NIPQUAD(((struct rtable *)skb->dst)->rt_src),
- NIPQUAD(((struct rtable *)skb->dst)->rt_dst));
+ __func__, skb, skb->len,
+ NIPQUAD(skb->rtable->rt_src),
+ NIPQUAD(skb->rtable->rt_dst));
SCTP_INC_STATS(SCTP_MIB_OUTSCTPPACKS);
return ip_queue_xmit(skb, ipfragok);
}
-static struct sctp_af sctp_ipv4_specific;
+static struct sctp_af sctp_af_inet;
static struct sctp_pf sctp_pf_inet = {
.event_msgname = sctp_inet_event_msgname,
.supported_addrs = sctp_inet_supported_addrs,
.create_accept_sk = sctp_v4_create_accept_sk,
.addr_v4map = sctp_v4_addr_v4map,
- .af = &sctp_ipv4_specific,
+ .af = &sctp_af_inet
};
/* Notifier for inetaddr addition/deletion events. */
};
/* IPv4 address related functions. */
-static struct sctp_af sctp_ipv4_specific = {
+static struct sctp_af sctp_af_inet = {
.sa_family = AF_INET,
.sctp_xmit = sctp_v4_xmit,
.setsockopt = ip_setsockopt,
int i;
int status = -EINVAL;
unsigned long goal;
+ unsigned long limit;
+ int max_share;
int order;
/* SCTP_DEBUG sanity check. */
/* Initialize handle used for association ids. */
idr_init(&sctp_assocs_id);
+ /* Set the pressure threshold to be a fraction of global memory that
+ * is up to 1/2 at 256 MB, decreasing toward zero with the amount of
+ * memory, with a floor of 128 pages.
+ * Note this initalizes the data in sctpv6_prot too
+ * Unabashedly stolen from tcp_init
+ */
+ limit = min(num_physpages, 1UL<<(28-PAGE_SHIFT)) >> (20-PAGE_SHIFT);
+ limit = (limit * (num_physpages >> (20-PAGE_SHIFT))) >> (PAGE_SHIFT-11);
+ limit = max(limit, 128UL);
+ sysctl_sctp_mem[0] = limit / 4 * 3;
+ sysctl_sctp_mem[1] = limit;
+ sysctl_sctp_mem[2] = sysctl_sctp_mem[0] * 2;
+
+ /* Set per-socket limits to no more than 1/128 the pressure threshold*/
+ limit = (sysctl_sctp_mem[1]) << (PAGE_SHIFT - 7);
+ max_share = min(4UL*1024*1024, limit);
+
+ sysctl_sctp_rmem[0] = PAGE_SIZE; /* give each asoc 1 page min */
+ sysctl_sctp_rmem[1] = (1500 *(sizeof(struct sk_buff) + 1));
+ sysctl_sctp_rmem[2] = max(sysctl_sctp_rmem[1], max_share);
+
+ sysctl_sctp_wmem[0] = SK_MEM_QUANTUM;
+ sysctl_sctp_wmem[1] = 16*1024;
+ sysctl_sctp_wmem[2] = max(64*1024, max_share);
+
/* Size and allocate the association hash table.
* The methodology is similar to that of the tcp hash tables.
*/
}
for (i = 0; i < sctp_assoc_hashsize; i++) {
rwlock_init(&sctp_assoc_hashtable[i].lock);
- sctp_assoc_hashtable[i].chain = NULL;
+ INIT_HLIST_HEAD(&sctp_assoc_hashtable[i].chain);
}
/* Allocate and initialize the endpoint hash table. */
}
for (i = 0; i < sctp_ep_hashsize; i++) {
rwlock_init(&sctp_ep_hashtable[i].lock);
- sctp_ep_hashtable[i].chain = NULL;
+ INIT_HLIST_HEAD(&sctp_ep_hashtable[i].chain);
}
/* Allocate and initialize the SCTP port hash table. */
}
for (i = 0; i < sctp_port_hashsize; i++) {
spin_lock_init(&sctp_port_hashtable[i].lock);
- sctp_port_hashtable[i].chain = NULL;
+ INIT_HLIST_HEAD(&sctp_port_hashtable[i].chain);
}
- spin_lock_init(&sctp_port_alloc_lock);
- 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_addip_noauth = 0;
/* Enable PR-SCTP by default. */
sctp_prsctp_enable = 1;
+ /* Disable AUTH by default. */
+ sctp_auth_enable = 0;
+
sctp_sysctl_register();
INIT_LIST_HEAD(&sctp_address_families);
- sctp_register_af(&sctp_ipv4_specific);
+ sctp_register_af(&sctp_af_inet);
status = proto_register(&sctp_prot, 1);
if (status)
if (status)
goto err_v6_add_protocol;
- __unsafe(THIS_MODULE);
status = 0;
out:
return status;
proto_unregister(&sctp_prot);
err_proto_register:
sctp_sysctl_unregister();
- list_del(&sctp_ipv4_specific.list);
+ list_del(&sctp_af_inet.list);
free_pages((unsigned long)sctp_port_hashtable,
get_order(sctp_port_hashsize *
sizeof(struct sctp_bind_hashbucket)));
inet_unregister_protosw(&sctp_seqpacket_protosw);
sctp_sysctl_unregister();
- list_del(&sctp_ipv4_specific.list);
+ list_del(&sctp_af_inet.list);
free_pages((unsigned long)sctp_assoc_hashtable,
get_order(sctp_assoc_hashsize *