summaryrefslogtreecommitdiff
path: root/net/sctp
diff options
context:
space:
mode:
Diffstat (limited to 'net/sctp')
-rw-r--r--net/sctp/associola.c5
-rw-r--r--net/sctp/endpointola.c52
-rw-r--r--net/sctp/input.c200
-rw-r--r--net/sctp/ipv6.c2
-rw-r--r--net/sctp/output.c2
-rw-r--r--net/sctp/proc.c326
-rw-r--r--net/sctp/protocol.c42
-rw-r--r--net/sctp/sm_sideeffect.c30
-rw-r--r--net/sctp/sm_statefuns.c17
-rw-r--r--net/sctp/socket.c8
-rw-r--r--net/sctp/sysctl.c7
-rw-r--r--net/sctp/transport.c8
12 files changed, 371 insertions, 328 deletions
diff --git a/net/sctp/associola.c b/net/sctp/associola.c
index 559afd0ee..2bf8ec92d 100644
--- a/net/sctp/associola.c
+++ b/net/sctp/associola.c
@@ -383,6 +383,7 @@ void sctp_association_free(struct sctp_association *asoc)
list_for_each_safe(pos, temp, &asoc->peer.transport_addr_list) {
transport = list_entry(pos, struct sctp_transport, transports);
list_del_rcu(pos);
+ sctp_unhash_transport(transport);
sctp_transport_free(transport);
}
@@ -500,6 +501,8 @@ void sctp_assoc_rm_peer(struct sctp_association *asoc,
/* Remove this peer from the list. */
list_del_rcu(&peer->transports);
+ /* Remove this peer from the transport hashtable */
+ sctp_unhash_transport(peer);
/* Get the first transport of asoc. */
pos = asoc->peer.transport_addr_list.next;
@@ -699,6 +702,8 @@ struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc,
/* Attach the remote transport to our asoc. */
list_add_tail_rcu(&peer->transports, &asoc->peer.transport_addr_list);
asoc->peer.transport_count++;
+ /* Add this peer into the transport hashtable */
+ sctp_hash_transport(peer);
/* If we do not yet have a primary path, set one. */
if (!asoc->peer.primary_path) {
diff --git a/net/sctp/endpointola.c b/net/sctp/endpointola.c
index 9da76ba4d..2522a6175 100644
--- a/net/sctp/endpointola.c
+++ b/net/sctp/endpointola.c
@@ -314,21 +314,16 @@ struct sctp_endpoint *sctp_endpoint_is_match(struct sctp_endpoint *ep,
}
/* Find the association that goes with this chunk.
- * We do a linear search of the associations for this endpoint.
- * We return the matching transport address too.
+ * We lookup the transport from hashtable at first, then get association
+ * through t->assoc.
*/
-static struct sctp_association *__sctp_endpoint_lookup_assoc(
+struct sctp_association *sctp_endpoint_lookup_assoc(
const struct sctp_endpoint *ep,
const union sctp_addr *paddr,
struct sctp_transport **transport)
{
struct sctp_association *asoc = NULL;
- struct sctp_association *tmp;
- struct sctp_transport *t = NULL;
- struct sctp_hashbucket *head;
- struct sctp_ep_common *epb;
- int hash;
- int rport;
+ struct sctp_transport *t;
*transport = NULL;
@@ -337,45 +332,16 @@ static struct sctp_association *__sctp_endpoint_lookup_assoc(
*/
if (!ep->base.bind_addr.port)
goto out;
+ t = sctp_epaddr_lookup_transport(ep, paddr);
+ if (!t)
+ goto out;
- rport = ntohs(paddr->v4.sin_port);
-
- hash = sctp_assoc_hashfn(sock_net(ep->base.sk), ep->base.bind_addr.port,
- rport);
- head = &sctp_assoc_hashtable[hash];
- read_lock(&head->lock);
- sctp_for_each_hentry(epb, &head->chain) {
- tmp = sctp_assoc(epb);
- if (tmp->ep != ep || rport != tmp->peer.port)
- continue;
-
- t = sctp_assoc_lookup_paddr(tmp, paddr);
- if (t) {
- asoc = tmp;
- *transport = t;
- break;
- }
- }
- read_unlock(&head->lock);
+ *transport = t;
+ asoc = t->asoc;
out:
return asoc;
}
-/* Lookup association on an endpoint based on a peer address. BH-safe. */
-struct sctp_association *sctp_endpoint_lookup_assoc(
- const struct sctp_endpoint *ep,
- const union sctp_addr *paddr,
- struct sctp_transport **transport)
-{
- struct sctp_association *asoc;
-
- local_bh_disable();
- asoc = __sctp_endpoint_lookup_assoc(ep, paddr, transport);
- local_bh_enable();
-
- return asoc;
-}
-
/* Look for any peeled off association from the endpoint that matches the
* given peer address.
*/
diff --git a/net/sctp/input.c b/net/sctp/input.c
index b6493b3f1..49d2cc751 100644
--- a/net/sctp/input.c
+++ b/net/sctp/input.c
@@ -782,65 +782,149 @@ hit:
return ep;
}
-/* Insert association into the hash table. */
-static void __sctp_hash_established(struct sctp_association *asoc)
+/* rhashtable for transport */
+struct sctp_hash_cmp_arg {
+ const struct sctp_endpoint *ep;
+ const union sctp_addr *laddr;
+ const union sctp_addr *paddr;
+ const struct net *net;
+};
+
+static inline int sctp_hash_cmp(struct rhashtable_compare_arg *arg,
+ const void *ptr)
{
- struct net *net = sock_net(asoc->base.sk);
- struct sctp_ep_common *epb;
- struct sctp_hashbucket *head;
+ const struct sctp_hash_cmp_arg *x = arg->key;
+ const struct sctp_transport *t = ptr;
+ struct sctp_association *asoc = t->asoc;
+ const struct net *net = x->net;
- epb = &asoc->base;
+ if (!sctp_cmp_addr_exact(&t->ipaddr, x->paddr))
+ return 1;
+ if (!net_eq(sock_net(asoc->base.sk), net))
+ return 1;
+ if (x->ep) {
+ if (x->ep != asoc->ep)
+ return 1;
+ } else {
+ if (x->laddr->v4.sin_port != htons(asoc->base.bind_addr.port))
+ return 1;
+ if (!sctp_bind_addr_match(&asoc->base.bind_addr,
+ x->laddr, sctp_sk(asoc->base.sk)))
+ return 1;
+ }
- /* Calculate which chain this entry will belong to. */
- epb->hashent = sctp_assoc_hashfn(net, epb->bind_addr.port,
- asoc->peer.port);
+ return 0;
+}
- head = &sctp_assoc_hashtable[epb->hashent];
+static inline u32 sctp_hash_obj(const void *data, u32 len, u32 seed)
+{
+ const struct sctp_transport *t = data;
+ const union sctp_addr *paddr = &t->ipaddr;
+ const struct net *net = sock_net(t->asoc->base.sk);
+ u16 lport = htons(t->asoc->base.bind_addr.port);
+ u32 addr;
+
+ if (paddr->sa.sa_family == AF_INET6)
+ addr = jhash(&paddr->v6.sin6_addr, 16, seed);
+ else
+ addr = paddr->v4.sin_addr.s_addr;
- write_lock(&head->lock);
- hlist_add_head(&epb->node, &head->chain);
- write_unlock(&head->lock);
+ return jhash_3words(addr, ((__u32)paddr->v4.sin_port) << 16 |
+ (__force __u32)lport, net_hash_mix(net), seed);
}
-/* Add an association to the hash. Local BH-safe. */
-void sctp_hash_established(struct sctp_association *asoc)
+static inline u32 sctp_hash_key(const void *data, u32 len, u32 seed)
{
- if (asoc->temp)
- return;
+ const struct sctp_hash_cmp_arg *x = data;
+ const union sctp_addr *paddr = x->paddr;
+ const struct net *net = x->net;
+ u16 lport;
+ u32 addr;
+
+ lport = x->ep ? htons(x->ep->base.bind_addr.port) :
+ x->laddr->v4.sin_port;
+ if (paddr->sa.sa_family == AF_INET6)
+ addr = jhash(&paddr->v6.sin6_addr, 16, seed);
+ else
+ addr = paddr->v4.sin_addr.s_addr;
- local_bh_disable();
- __sctp_hash_established(asoc);
- local_bh_enable();
+ return jhash_3words(addr, ((__u32)paddr->v4.sin_port) << 16 |
+ (__force __u32)lport, net_hash_mix(net), seed);
}
-/* Remove association from the hash table. */
-static void __sctp_unhash_established(struct sctp_association *asoc)
+static const struct rhashtable_params sctp_hash_params = {
+ .head_offset = offsetof(struct sctp_transport, node),
+ .hashfn = sctp_hash_key,
+ .obj_hashfn = sctp_hash_obj,
+ .obj_cmpfn = sctp_hash_cmp,
+ .automatic_shrinking = true,
+};
+
+int sctp_transport_hashtable_init(void)
{
- struct net *net = sock_net(asoc->base.sk);
- struct sctp_hashbucket *head;
- struct sctp_ep_common *epb;
+ return rhashtable_init(&sctp_transport_hashtable, &sctp_hash_params);
+}
- epb = &asoc->base;
+void sctp_transport_hashtable_destroy(void)
+{
+ rhashtable_destroy(&sctp_transport_hashtable);
+}
- epb->hashent = sctp_assoc_hashfn(net, epb->bind_addr.port,
- asoc->peer.port);
+void sctp_hash_transport(struct sctp_transport *t)
+{
+ struct sctp_hash_cmp_arg arg;
- head = &sctp_assoc_hashtable[epb->hashent];
+ if (t->asoc->temp)
+ return;
- write_lock(&head->lock);
- hlist_del_init(&epb->node);
- write_unlock(&head->lock);
+ arg.ep = t->asoc->ep;
+ arg.paddr = &t->ipaddr;
+ arg.net = sock_net(t->asoc->base.sk);
+
+reinsert:
+ if (rhashtable_lookup_insert_key(&sctp_transport_hashtable, &arg,
+ &t->node, sctp_hash_params) == -EBUSY)
+ goto reinsert;
}
-/* Remove association from the hash table. Local BH-safe. */
-void sctp_unhash_established(struct sctp_association *asoc)
+void sctp_unhash_transport(struct sctp_transport *t)
{
- if (asoc->temp)
+ if (t->asoc->temp)
return;
- local_bh_disable();
- __sctp_unhash_established(asoc);
- local_bh_enable();
+ rhashtable_remove_fast(&sctp_transport_hashtable, &t->node,
+ sctp_hash_params);
+}
+
+struct sctp_transport *sctp_addrs_lookup_transport(
+ struct net *net,
+ const union sctp_addr *laddr,
+ const union sctp_addr *paddr)
+{
+ struct sctp_hash_cmp_arg arg = {
+ .ep = NULL,
+ .laddr = laddr,
+ .paddr = paddr,
+ .net = net,
+ };
+
+ return rhashtable_lookup_fast(&sctp_transport_hashtable, &arg,
+ sctp_hash_params);
+}
+
+struct sctp_transport *sctp_epaddr_lookup_transport(
+ const struct sctp_endpoint *ep,
+ const union sctp_addr *paddr)
+{
+ struct net *net = sock_net(ep->base.sk);
+ struct sctp_hash_cmp_arg arg = {
+ .ep = ep,
+ .paddr = paddr,
+ .net = net,
+ };
+
+ return rhashtable_lookup_fast(&sctp_transport_hashtable, &arg,
+ sctp_hash_params);
}
/* Look up an association. */
@@ -850,38 +934,26 @@ static struct sctp_association *__sctp_lookup_association(
const union sctp_addr *peer,
struct sctp_transport **pt)
{
- struct sctp_hashbucket *head;
- struct sctp_ep_common *epb;
- struct sctp_association *asoc;
- struct sctp_transport *transport;
- int hash;
+ struct sctp_transport *t;
+ struct sctp_association *asoc = NULL;
- /* Optimize here for direct hit, only listening connections can
- * have wildcards anyways.
- */
- hash = sctp_assoc_hashfn(net, ntohs(local->v4.sin_port),
- ntohs(peer->v4.sin_port));
- head = &sctp_assoc_hashtable[hash];
- read_lock(&head->lock);
- sctp_for_each_hentry(epb, &head->chain) {
- asoc = sctp_assoc(epb);
- transport = sctp_assoc_is_match(asoc, net, local, peer);
- if (transport)
- goto hit;
- }
+ rcu_read_lock();
+ t = sctp_addrs_lookup_transport(net, local, peer);
+ if (!t || !sctp_transport_hold(t))
+ goto out;
- read_unlock(&head->lock);
+ asoc = t->asoc;
+ sctp_association_hold(asoc);
+ *pt = t;
- return NULL;
+ sctp_transport_put(t);
-hit:
- *pt = transport;
- sctp_association_hold(asoc);
- read_unlock(&head->lock);
+out:
+ rcu_read_unlock();
return asoc;
}
-/* Look up an association. BH-safe. */
+/* Look up an association. protected by RCU read lock */
static
struct sctp_association *sctp_lookup_association(struct net *net,
const union sctp_addr *laddr,
@@ -890,9 +962,7 @@ struct sctp_association *sctp_lookup_association(struct net *net,
{
struct sctp_association *asoc;
- local_bh_disable();
asoc = __sctp_lookup_association(net, laddr, paddr, transportp);
- local_bh_enable();
return asoc;
}
diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c
index ec529121f..ce46f1c7f 100644
--- a/net/sctp/ipv6.c
+++ b/net/sctp/ipv6.c
@@ -526,6 +526,8 @@ static int sctp_v6_cmp_addr(const union sctp_addr *addr1,
}
return 0;
}
+ if (addr1->v6.sin6_port != addr2->v6.sin6_port)
+ return 0;
if (!ipv6_addr_equal(&addr1->v6.sin6_addr, &addr2->v6.sin6_addr))
return 0;
/* If this is a linklocal address, compare the scope_id. */
diff --git a/net/sctp/output.c b/net/sctp/output.c
index abe7c2db2..9d610eddd 100644
--- a/net/sctp/output.c
+++ b/net/sctp/output.c
@@ -534,7 +534,7 @@ int sctp_packet_transmit(struct sctp_packet *packet)
* by CRC32-C as described in <draft-ietf-tsvwg-sctpcsum-02.txt>.
*/
if (!sctp_checksum_disable) {
- if (!(dst->dev->features & NETIF_F_SCTP_CSUM) ||
+ if (!(dst->dev->features & NETIF_F_SCTP_CRC) ||
(dst_xfrm(dst) != NULL) || packet->ipfragok) {
sh->checksum = sctp_compute_cksum(nskb, 0);
} else {
diff --git a/net/sctp/proc.c b/net/sctp/proc.c
index 0697eda5a..963dffcc2 100644
--- a/net/sctp/proc.c
+++ b/net/sctp/proc.c
@@ -165,8 +165,6 @@ static void sctp_seq_dump_remote_addrs(struct seq_file *seq, struct sctp_associa
list_for_each_entry_rcu(transport, &assoc->peer.transport_addr_list,
transports) {
addr = &transport->ipaddr;
- if (transport->dead)
- continue;
af = sctp_get_af_specific(addr->sa.sa_family);
if (af->cmp_addr(addr, primary)) {
@@ -281,88 +279,140 @@ void sctp_eps_proc_exit(struct net *net)
remove_proc_entry("eps", net->sctp.proc_net_sctp);
}
+struct sctp_ht_iter {
+ struct seq_net_private p;
+ struct rhashtable_iter hti;
+};
-static void *sctp_assocs_seq_start(struct seq_file *seq, loff_t *pos)
+static struct sctp_transport *sctp_transport_get_next(struct seq_file *seq)
{
- if (*pos >= sctp_assoc_hashsize)
- return NULL;
+ struct sctp_ht_iter *iter = seq->private;
+ struct sctp_transport *t;
- if (*pos < 0)
- *pos = 0;
+ t = rhashtable_walk_next(&iter->hti);
+ for (; t; t = rhashtable_walk_next(&iter->hti)) {
+ if (IS_ERR(t)) {
+ if (PTR_ERR(t) == -EAGAIN)
+ continue;
+ break;
+ }
- if (*pos == 0)
- seq_printf(seq, " ASSOC SOCK STY SST ST HBKT "
- "ASSOC-ID TX_QUEUE RX_QUEUE UID INODE LPORT "
- "RPORT LADDRS <-> RADDRS "
- "HBINT INS OUTS MAXRT T1X T2X RTXC "
- "wmema wmemq sndbuf rcvbuf\n");
+ if (net_eq(sock_net(t->asoc->base.sk), seq_file_net(seq)) &&
+ t->asoc->peer.primary_path == t)
+ break;
+ }
- return (void *)pos;
+ return t;
}
-static void sctp_assocs_seq_stop(struct seq_file *seq, void *v)
+static struct sctp_transport *sctp_transport_get_idx(struct seq_file *seq,
+ loff_t pos)
{
+ void *obj = SEQ_START_TOKEN;
+
+ while (pos && (obj = sctp_transport_get_next(seq)) && !IS_ERR(obj))
+ pos--;
+
+ return obj;
}
+static int sctp_transport_walk_start(struct seq_file *seq)
+{
+ struct sctp_ht_iter *iter = seq->private;
+ int err;
+
+ err = rhashtable_walk_init(&sctp_transport_hashtable, &iter->hti);
+ if (err)
+ return err;
+
+ err = rhashtable_walk_start(&iter->hti);
+
+ return err == -EAGAIN ? 0 : err;
+}
+
+static void sctp_transport_walk_stop(struct seq_file *seq)
+{
+ struct sctp_ht_iter *iter = seq->private;
+
+ rhashtable_walk_stop(&iter->hti);
+ rhashtable_walk_exit(&iter->hti);
+}
+
+static void *sctp_assocs_seq_start(struct seq_file *seq, loff_t *pos)
+{
+ int err = sctp_transport_walk_start(seq);
+
+ if (err)
+ return ERR_PTR(err);
+
+ return sctp_transport_get_idx(seq, *pos);
+}
+
+static void sctp_assocs_seq_stop(struct seq_file *seq, void *v)
+{
+ sctp_transport_walk_stop(seq);
+}
static void *sctp_assocs_seq_next(struct seq_file *seq, void *v, loff_t *pos)
{
- if (++*pos >= sctp_assoc_hashsize)
- return NULL;
+ ++*pos;
- return pos;
+ return sctp_transport_get_next(seq);
}
/* Display sctp associations (/proc/net/sctp/assocs). */
static int sctp_assocs_seq_show(struct seq_file *seq, void *v)
{
- struct sctp_hashbucket *head;
- struct sctp_ep_common *epb;
+ struct sctp_transport *transport;
struct sctp_association *assoc;
+ struct sctp_ep_common *epb;
struct sock *sk;
- int hash = *(loff_t *)v;
-
- if (hash >= sctp_assoc_hashsize)
- return -ENOMEM;
- head = &sctp_assoc_hashtable[hash];
- local_bh_disable();
- read_lock(&head->lock);
- sctp_for_each_hentry(epb, &head->chain) {
- assoc = sctp_assoc(epb);
- sk = epb->sk;
- if (!net_eq(sock_net(sk), seq_file_net(seq)))
- continue;
- seq_printf(seq,
- "%8pK %8pK %-3d %-3d %-2d %-4d "
- "%4d %8d %8d %7u %5lu %-5d %5d ",
- assoc, sk, sctp_sk(sk)->type, sk->sk_state,
- assoc->state, hash,
- assoc->assoc_id,
- assoc->sndbuf_used,
- atomic_read(&assoc->rmem_alloc),
- from_kuid_munged(seq_user_ns(seq), sock_i_uid(sk)),
- sock_i_ino(sk),
- epb->bind_addr.port,
- assoc->peer.port);
- seq_printf(seq, " ");
- sctp_seq_dump_local_addrs(seq, epb);
- seq_printf(seq, "<-> ");
- sctp_seq_dump_remote_addrs(seq, assoc);
- seq_printf(seq, "\t%8lu %5d %5d %4d %4d %4d %8d "
- "%8d %8d %8d %8d",
- assoc->hbinterval, assoc->c.sinit_max_instreams,
- assoc->c.sinit_num_ostreams, assoc->max_retrans,
- assoc->init_retries, assoc->shutdown_retries,
- assoc->rtx_data_chunks,
- atomic_read(&sk->sk_wmem_alloc),
- sk->sk_wmem_queued,
- sk->sk_sndbuf,
- sk->sk_rcvbuf);
- seq_printf(seq, "\n");
+ if (v == SEQ_START_TOKEN) {
+ seq_printf(seq, " ASSOC SOCK STY SST ST HBKT "
+ "ASSOC-ID TX_QUEUE RX_QUEUE UID INODE LPORT "
+ "RPORT LADDRS <-> RADDRS "
+ "HBINT INS OUTS MAXRT T1X T2X RTXC "
+ "wmema wmemq sndbuf rcvbuf\n");
+ return 0;
}
- read_unlock(&head->lock);
- local_bh_enable();
+
+ transport = (struct sctp_transport *)v;
+ if (!sctp_transport_hold(transport))
+ return 0;
+ assoc = transport->asoc;
+ epb = &assoc->base;
+ sk = epb->sk;
+
+ seq_printf(seq,
+ "%8pK %8pK %-3d %-3d %-2d %-4d "
+ "%4d %8d %8d %7u %5lu %-5d %5d ",
+ assoc, sk, sctp_sk(sk)->type, sk->sk_state,
+ assoc->state, 0,
+ assoc->assoc_id,
+ assoc->sndbuf_used,
+ atomic_read(&assoc->rmem_alloc),
+ from_kuid_munged(seq_user_ns(seq), sock_i_uid(sk)),
+ sock_i_ino(sk),
+ epb->bind_addr.port,
+ assoc->peer.port);
+ seq_printf(seq, " ");
+ sctp_seq_dump_local_addrs(seq, epb);
+ seq_printf(seq, "<-> ");
+ sctp_seq_dump_remote_addrs(seq, assoc);
+ seq_printf(seq, "\t%8lu %5d %5d %4d %4d %4d %8d "
+ "%8d %8d %8d %8d",
+ assoc->hbinterval, assoc->c.sinit_max_instreams,
+ assoc->c.sinit_num_ostreams, assoc->max_retrans,
+ assoc->init_retries, assoc->shutdown_retries,
+ assoc->rtx_data_chunks,
+ atomic_read(&sk->sk_wmem_alloc),
+ sk->sk_wmem_queued,
+ sk->sk_sndbuf,
+ sk->sk_rcvbuf);
+ seq_printf(seq, "\n");
+
+ sctp_transport_put(transport);
return 0;
}
@@ -378,7 +428,7 @@ static const struct seq_operations sctp_assoc_ops = {
static int sctp_assocs_seq_open(struct inode *inode, struct file *file)
{
return seq_open_net(inode, file, &sctp_assoc_ops,
- sizeof(struct seq_net_private));
+ sizeof(struct sctp_ht_iter));
}
static const struct file_operations sctp_assocs_seq_fops = {
@@ -409,112 +459,96 @@ void sctp_assocs_proc_exit(struct net *net)
static void *sctp_remaddr_seq_start(struct seq_file *seq, loff_t *pos)
{
- if (*pos >= sctp_assoc_hashsize)
- return NULL;
+ int err = sctp_transport_walk_start(seq);
- if (*pos < 0)
- *pos = 0;
-
- if (*pos == 0)
- seq_printf(seq, "ADDR ASSOC_ID HB_ACT RTO MAX_PATH_RTX "
- "REM_ADDR_RTX START STATE\n");
+ if (err)
+ return ERR_PTR(err);
- return (void *)pos;
+ return sctp_transport_get_idx(seq, *pos);
}
static void *sctp_remaddr_seq_next(struct seq_file *seq, void *v, loff_t *pos)
{
- if (++*pos >= sctp_assoc_hashsize)
- return NULL;
+ ++*pos;
- return pos;
+ return sctp_transport_get_next(seq);
}
static void sctp_remaddr_seq_stop(struct seq_file *seq, void *v)
{
+ sctp_transport_walk_stop(seq);
}
static int sctp_remaddr_seq_show(struct seq_file *seq, void *v)
{
- struct sctp_hashbucket *head;
- struct sctp_ep_common *epb;
struct sctp_association *assoc;
- struct sctp_transport *tsp;
- int hash = *(loff_t *)v;
+ struct sctp_transport *transport, *tsp;
- if (hash >= sctp_assoc_hashsize)
- return -ENOMEM;
+ if (v == SEQ_START_TOKEN) {
+ seq_printf(seq, "ADDR ASSOC_ID HB_ACT RTO MAX_PATH_RTX "
+ "REM_ADDR_RTX START STATE\n");
+ return 0;
+ }
- head = &sctp_assoc_hashtable[hash];
- local_bh_disable();
- read_lock(&head->lock);
- rcu_read_lock();
- sctp_for_each_hentry(epb, &head->chain) {
- if (!net_eq(sock_net(epb->sk), seq_file_net(seq)))
- continue;
- assoc = sctp_assoc(epb);
- list_for_each_entry_rcu(tsp, &assoc->peer.transport_addr_list,
- transports) {
- if (tsp->dead)
- continue;
+ transport = (struct sctp_transport *)v;
+ if (!sctp_transport_hold(transport))
+ return 0;
+ assoc = transport->asoc;
+
+ list_for_each_entry_rcu(tsp, &assoc->peer.transport_addr_list,
+ transports) {
+ /*
+ * The remote address (ADDR)
+ */
+ tsp->af_specific->seq_dump_addr(seq, &tsp->ipaddr);
+ seq_printf(seq, " ");
+ /*
+ * The association ID (ASSOC_ID)
+ */
+ seq_printf(seq, "%d ", tsp->asoc->assoc_id);
+
+ /*
+ * If the Heartbeat is active (HB_ACT)
+ * Note: 1 = Active, 0 = Inactive
+ */
+ seq_printf(seq, "%d ", timer_pending(&tsp->hb_timer));
+
+ /*
+ * Retransmit time out (RTO)
+ */
+ seq_printf(seq, "%lu ", tsp->rto);
+
+ /*
+ * Maximum path retransmit count (PATH_MAX_RTX)
+ */
+ seq_printf(seq, "%d ", tsp->pathmaxrxt);
+
+ /*
+ * remote address retransmit count (REM_ADDR_RTX)
+ * Note: We don't have a way to tally this at the moment
+ * so lets just leave it as zero for the moment
+ */
+ seq_puts(seq, "0 ");
+
+ /*
+ * remote address start time (START). This is also not
+ * currently implemented, but we can record it with a
+ * jiffies marker in a subsequent patch
+ */
+ seq_puts(seq, "0 ");
+
+ /*
+ * The current state of this destination. I.e.
+ * SCTP_ACTIVE, SCTP_INACTIVE, ...
+ */
+ seq_printf(seq, "%d", tsp->state);
- /*
- * The remote address (ADDR)
- */
- tsp->af_specific->seq_dump_addr(seq, &tsp->ipaddr);
- seq_printf(seq, " ");
-
- /*
- * The association ID (ASSOC_ID)
- */
- seq_printf(seq, "%d ", tsp->asoc->assoc_id);
-
- /*
- * If the Heartbeat is active (HB_ACT)
- * Note: 1 = Active, 0 = Inactive
- */
- seq_printf(seq, "%d ", timer_pending(&tsp->hb_timer));
-
- /*
- * Retransmit time out (RTO)
- */
- seq_printf(seq, "%lu ", tsp->rto);
-
- /*
- * Maximum path retransmit count (PATH_MAX_RTX)
- */
- seq_printf(seq, "%d ", tsp->pathmaxrxt);
-
- /*
- * remote address retransmit count (REM_ADDR_RTX)
- * Note: We don't have a way to tally this at the moment
- * so lets just leave it as zero for the moment
- */
- seq_puts(seq, "0 ");
-
- /*
- * remote address start time (START). This is also not
- * currently implemented, but we can record it with a
- * jiffies marker in a subsequent patch
- */
- seq_puts(seq, "0 ");
-
- /*
- * The current state of this destination. I.e.
- * SCTP_ACTIVE, SCTP_INACTIVE, ...
- */
- seq_printf(seq, "%d", tsp->state);
-
- seq_printf(seq, "\n");
- }
+ seq_printf(seq, "\n");
}
- rcu_read_unlock();
- read_unlock(&head->lock);
- local_bh_enable();
+ sctp_transport_put(transport);
return 0;
-
}
static const struct seq_operations sctp_remaddr_ops = {
@@ -533,7 +567,7 @@ void sctp_remaddr_proc_exit(struct net *net)
static int sctp_remaddr_seq_open(struct inode *inode, struct file *file)
{
return seq_open_net(inode, file, &sctp_remaddr_ops,
- sizeof(struct seq_net_private));
+ sizeof(struct sctp_ht_iter));
}
static const struct file_operations sctp_remaddr_seq_fops = {
diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c
index 8b4ff3156..1099e99a5 100644
--- a/net/sctp/protocol.c
+++ b/net/sctp/protocol.c
@@ -1225,6 +1225,9 @@ static int __net_init sctp_defaults_init(struct net *net)
/* Max.Burst - 4 */
net->sctp.max_burst = SCTP_DEFAULT_MAX_BURST;
+ /* Enable pf state by default */
+ net->sctp.pf_enable = 1;
+
/* Association.Max.Retrans - 10 attempts
* Path.Max.Retrans - 5 attempts (per destination address)
* Max.Init.Retransmits - 8 attempts
@@ -1427,24 +1430,6 @@ static __init int sctp_init(void)
/* Limit the page order by that maximum hash table size */
order = min(order, max_entry_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|__GFP_NOWARN, order);
- } while (!sctp_assoc_hashtable && --order > 0);
- if (!sctp_assoc_hashtable) {
- pr_err("Failed association hash alloc\n");
- status = -ENOMEM;
- goto err_ahash_alloc;
- }
- for (i = 0; i < sctp_assoc_hashsize; i++) {
- rwlock_init(&sctp_assoc_hashtable[i].lock);
- INIT_HLIST_HEAD(&sctp_assoc_hashtable[i].chain);
- }
-
/* Allocate and initialize the endpoint hash table. */
sctp_ep_hashsize = 64;
sctp_ep_hashtable =
@@ -1466,7 +1451,7 @@ static __init int sctp_init(void)
*/
do {
sctp_port_hashtable = (struct sctp_bind_hashbucket *)
- __get_free_pages(GFP_ATOMIC|__GFP_NOWARN, order);
+ __get_free_pages(GFP_KERNEL | __GFP_NOWARN, order);
} while (!sctp_port_hashtable && --order > 0);
if (!sctp_port_hashtable) {
@@ -1493,8 +1478,11 @@ static __init int sctp_init(void)
INIT_HLIST_HEAD(&sctp_port_hashtable[i].chain);
}
- pr_info("Hash tables configured (established %d bind %d)\n",
- sctp_assoc_hashsize, sctp_port_hashsize);
+ if (sctp_transport_hashtable_init())
+ goto err_thash_alloc;
+
+ pr_info("Hash tables configured (bind %d/%d)\n", sctp_port_hashsize,
+ num_entries);
sctp_sysctl_register();
@@ -1547,12 +1535,10 @@ err_register_defaults:
get_order(sctp_port_hashsize *
sizeof(struct sctp_bind_hashbucket)));
err_bhash_alloc:
+ sctp_transport_hashtable_destroy();
+err_thash_alloc:
kfree(sctp_ep_hashtable);
err_ehash_alloc:
- free_pages((unsigned long)sctp_assoc_hashtable,
- get_order(sctp_assoc_hashsize *
- sizeof(struct sctp_hashbucket)));
-err_ahash_alloc:
percpu_counter_destroy(&sctp_sockets_allocated);
err_percpu_counter_init:
kmem_cache_destroy(sctp_chunk_cachep);
@@ -1586,13 +1572,11 @@ static __exit void sctp_exit(void)
sctp_sysctl_unregister();
- 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)));
+ kfree(sctp_ep_hashtable);
+ sctp_transport_hashtable_destroy();
percpu_counter_destroy(&sctp_sockets_allocated);
diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c
index 6098d4c42..b5327bb77 100644
--- a/net/sctp/sm_sideeffect.c
+++ b/net/sctp/sm_sideeffect.c
@@ -63,7 +63,7 @@ static int sctp_cmd_interpreter(sctp_event_t event_type,
static int sctp_side_effects(sctp_event_t event_type, sctp_subtype_t subtype,
sctp_state_t state,
struct sctp_endpoint *ep,
- struct sctp_association *asoc,
+ struct sctp_association **asoc,
void *event_arg,
sctp_disposition_t status,
sctp_cmd_seq_t *commands,
@@ -259,12 +259,6 @@ void sctp_generate_t3_rtx_event(unsigned long peer)
goto out_unlock;
}
- /* Is this transport really dead and just waiting around for
- * the timer to let go of the reference?
- */
- if (transport->dead)
- goto out_unlock;
-
/* Run through the state machine. */
error = sctp_do_sm(net, SCTP_EVENT_T_TIMEOUT,
SCTP_ST_TIMEOUT(SCTP_EVENT_TIMEOUT_T3_RTX),
@@ -380,12 +374,6 @@ void sctp_generate_heartbeat_event(unsigned long data)
goto out_unlock;
}
- /* Is this structure just waiting around for us to actually
- * get destroyed?
- */
- if (transport->dead)
- goto out_unlock;
-
error = sctp_do_sm(net, SCTP_EVENT_T_TIMEOUT,
SCTP_ST_TIMEOUT(SCTP_EVENT_TIMEOUT_HEARTBEAT),
asoc->state, asoc->ep, asoc,
@@ -477,6 +465,8 @@ static void sctp_do_8_2_transport_strike(sctp_cmd_seq_t *commands,
struct sctp_transport *transport,
int is_hb)
{
+ struct net *net = sock_net(asoc->base.sk);
+
/* The check for association's overall error counter exceeding the
* threshold is done in the state function.
*/
@@ -503,7 +493,8 @@ static void sctp_do_8_2_transport_strike(sctp_cmd_seq_t *commands,
* is SCTP_ACTIVE, then mark this transport as Partially Failed,
* see SCTP Quick Failover Draft, section 5.1
*/
- if ((transport->state == SCTP_ACTIVE) &&
+ if (net->sctp.pf_enable &&
+ (transport->state == SCTP_ACTIVE) &&
(asoc->pf_retrans < transport->pathmaxrxt) &&
(transport->error_count > asoc->pf_retrans)) {
@@ -863,7 +854,6 @@ static void sctp_cmd_delete_tcb(sctp_cmd_seq_t *cmds,
(!asoc->temp) && (sk->sk_shutdown != SHUTDOWN_MASK))
return;
- sctp_unhash_established(asoc);
sctp_association_free(asoc);
}
@@ -1123,7 +1113,7 @@ int sctp_do_sm(struct net *net, sctp_event_t event_type, sctp_subtype_t subtype,
debug_post_sfn();
error = sctp_side_effects(event_type, subtype, state,
- ep, asoc, event_arg, status,
+ ep, &asoc, event_arg, status,
&commands, gfp);
debug_post_sfx();
@@ -1136,7 +1126,7 @@ int sctp_do_sm(struct net *net, sctp_event_t event_type, sctp_subtype_t subtype,
static int sctp_side_effects(sctp_event_t event_type, sctp_subtype_t subtype,
sctp_state_t state,
struct sctp_endpoint *ep,
- struct sctp_association *asoc,
+ struct sctp_association **asoc,
void *event_arg,
sctp_disposition_t status,
sctp_cmd_seq_t *commands,
@@ -1151,7 +1141,7 @@ static int sctp_side_effects(sctp_event_t event_type, sctp_subtype_t subtype,
* disposition SCTP_DISPOSITION_CONSUME.
*/
if (0 != (error = sctp_cmd_interpreter(event_type, subtype, state,
- ep, asoc,
+ ep, *asoc,
event_arg, status,
commands, gfp)))
goto bail;
@@ -1174,11 +1164,12 @@ static int sctp_side_effects(sctp_event_t event_type, sctp_subtype_t subtype,
break;
case SCTP_DISPOSITION_DELETE_TCB:
+ case SCTP_DISPOSITION_ABORT:
/* This should now be a command. */
+ *asoc = NULL;
break;
case SCTP_DISPOSITION_CONSUME:
- case SCTP_DISPOSITION_ABORT:
/*
* We should no longer have much work to do here as the
* real work has been done as explicit commands above.
@@ -1266,7 +1257,6 @@ static int sctp_cmd_interpreter(sctp_event_t event_type,
asoc = cmd->obj.asoc;
BUG_ON(asoc->peer.primary_path == NULL);
sctp_endpoint_add_asoc(ep, asoc);
- sctp_hash_established(asoc);
break;
case SCTP_CMD_UPDATE_ASSOC:
diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c
index 22c2bf367..f1f08c8f2 100644
--- a/net/sctp/sm_statefuns.c
+++ b/net/sctp/sm_statefuns.c
@@ -2976,7 +2976,7 @@ sctp_disposition_t sctp_sf_eat_data_6_2(struct net *net,
SCTP_INC_STATS(net, SCTP_MIB_IN_DATA_CHUNK_DISCARDS);
goto discard_force;
case SCTP_IERROR_NO_DATA:
- goto consume;
+ return SCTP_DISPOSITION_ABORT;
case SCTP_IERROR_PROTO_VIOLATION:
return sctp_sf_abort_violation(net, ep, asoc, chunk, commands,
(u8 *)chunk->subh.data_hdr, sizeof(sctp_datahdr_t));
@@ -3043,9 +3043,6 @@ discard_noforce:
sctp_add_cmd_sf(commands, SCTP_CMD_GEN_SACK, force);
return SCTP_DISPOSITION_DISCARD;
-consume:
- return SCTP_DISPOSITION_CONSUME;
-
}
/*
@@ -3093,7 +3090,7 @@ sctp_disposition_t sctp_sf_eat_data_fast_4_4(struct net *net,
case SCTP_IERROR_BAD_STREAM:
break;
case SCTP_IERROR_NO_DATA:
- goto consume;
+ return SCTP_DISPOSITION_ABORT;
case SCTP_IERROR_PROTO_VIOLATION:
return sctp_sf_abort_violation(net, ep, asoc, chunk, commands,
(u8 *)chunk->subh.data_hdr, sizeof(sctp_datahdr_t));
@@ -3119,7 +3116,6 @@ sctp_disposition_t sctp_sf_eat_data_fast_4_4(struct net *net,
SCTP_TO(SCTP_EVENT_TIMEOUT_T2_SHUTDOWN));
}
-consume:
return SCTP_DISPOSITION_CONSUME;
}
@@ -4825,9 +4821,6 @@ sctp_disposition_t sctp_sf_do_9_1_prm_abort(
* if necessary to fill gaps.
*/
struct sctp_chunk *abort = arg;
- sctp_disposition_t retval;
-
- retval = SCTP_DISPOSITION_CONSUME;
if (abort)
sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(abort));
@@ -4845,7 +4838,7 @@ sctp_disposition_t sctp_sf_do_9_1_prm_abort(
SCTP_INC_STATS(net, SCTP_MIB_ABORTEDS);
SCTP_DEC_STATS(net, SCTP_MIB_CURRESTAB);
- return retval;
+ return SCTP_DISPOSITION_ABORT;
}
/* We tried an illegal operation on an association which is closed. */
@@ -4960,12 +4953,10 @@ sctp_disposition_t sctp_sf_cookie_wait_prm_abort(
sctp_cmd_seq_t *commands)
{
struct sctp_chunk *abort = arg;
- sctp_disposition_t retval;
/* Stop T1-init timer */
sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP,
SCTP_TO(SCTP_EVENT_TIMEOUT_T1_INIT));
- retval = SCTP_DISPOSITION_CONSUME;
if (abort)
sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(abort));
@@ -4985,7 +4976,7 @@ sctp_disposition_t sctp_sf_cookie_wait_prm_abort(
sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED,
SCTP_PERR(SCTP_ERROR_USER_ABORT));
- return retval;
+ return SCTP_DISPOSITION_ABORT;
}
/*
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index be1489fc3..e878da094 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -1228,7 +1228,6 @@ out_free:
* To the hash table, try to unhash it, just in case, its a noop
* if it wasn't hashed so we're safe
*/
- sctp_unhash_established(asoc);
sctp_association_free(asoc);
}
return err;
@@ -1504,7 +1503,6 @@ static void sctp_close(struct sock *sk, long timeout)
* ABORT or SHUTDOWN based on the linger options.
*/
if (sctp_state(asoc, CLOSED)) {
- sctp_unhash_established(asoc);
sctp_association_free(asoc);
continue;
}
@@ -1986,10 +1984,8 @@ static int sctp_sendmsg(struct sock *sk, struct msghdr *msg, size_t msg_len)
goto out_unlock;
out_free:
- if (new_asoc) {
- sctp_unhash_established(asoc);
+ if (new_asoc)
sctp_association_free(asoc);
- }
out_unlock:
release_sock(sk);
@@ -6987,7 +6983,7 @@ void sctp_data_ready(struct sock *sk)
rcu_read_lock();
wq = rcu_dereference(sk->sk_wq);
- if (wq_has_sleeper(wq))
+ if (skwq_has_sleeper(wq))
wake_up_interruptible_sync_poll(&wq->wait, POLLIN |
POLLRDNORM | POLLRDBAND);
sk_wake_async(sk, SOCK_WAKE_WAITD, POLL_IN);
diff --git a/net/sctp/sysctl.c b/net/sctp/sysctl.c
index 3e0fc5127..daf8554fd 100644
--- a/net/sctp/sysctl.c
+++ b/net/sctp/sysctl.c
@@ -308,6 +308,13 @@ static struct ctl_table sctp_net_table[] = {
.extra1 = &max_autoclose_min,
.extra2 = &max_autoclose_max,
},
+ {
+ .procname = "pf_enable",
+ .data = &init_net.sctp.pf_enable,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ },
{ /* sentinel */ }
};
diff --git a/net/sctp/transport.c b/net/sctp/transport.c
index aab9e3f29..a431c1404 100644
--- a/net/sctp/transport.c
+++ b/net/sctp/transport.c
@@ -132,8 +132,6 @@ fail:
*/
void sctp_transport_free(struct sctp_transport *transport)
{
- transport->dead = 1;
-
/* Try to delete the heartbeat timer. */
if (del_timer(&transport->hb_timer))
sctp_transport_put(transport);
@@ -169,7 +167,7 @@ static void sctp_transport_destroy_rcu(struct rcu_head *head)
*/
static void sctp_transport_destroy(struct sctp_transport *transport)
{
- if (unlikely(!transport->dead)) {
+ if (unlikely(atomic_read(&transport->refcnt))) {
WARN(1, "Attempt to destroy undead transport %p!\n", transport);
return;
}
@@ -296,9 +294,9 @@ void sctp_transport_route(struct sctp_transport *transport,
}
/* Hold a reference to a transport. */
-void sctp_transport_hold(struct sctp_transport *transport)
+int sctp_transport_hold(struct sctp_transport *transport)
{
- atomic_inc(&transport->refcnt);
+ return atomic_add_unless(&transport->refcnt, 1, 0);
}
/* Release a reference to a transport and clean up