|
@@ -4288,6 +4288,131 @@ int sctp_get_sctp_info(struct sock *sk, struct sctp_association *asoc,
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(sctp_get_sctp_info);
|
|
|
|
|
|
+/* use callback to avoid exporting the core structure */
|
|
|
+int sctp_transport_walk_start(struct rhashtable_iter *iter)
|
|
|
+{
|
|
|
+ int err;
|
|
|
+
|
|
|
+ err = rhashtable_walk_init(&sctp_transport_hashtable, iter,
|
|
|
+ GFP_KERNEL);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+
|
|
|
+ err = rhashtable_walk_start(iter);
|
|
|
+
|
|
|
+ return err == -EAGAIN ? 0 : err;
|
|
|
+}
|
|
|
+
|
|
|
+void sctp_transport_walk_stop(struct rhashtable_iter *iter)
|
|
|
+{
|
|
|
+ rhashtable_walk_stop(iter);
|
|
|
+ rhashtable_walk_exit(iter);
|
|
|
+}
|
|
|
+
|
|
|
+struct sctp_transport *sctp_transport_get_next(struct net *net,
|
|
|
+ struct rhashtable_iter *iter)
|
|
|
+{
|
|
|
+ struct sctp_transport *t;
|
|
|
+
|
|
|
+ t = rhashtable_walk_next(iter);
|
|
|
+ for (; t; t = rhashtable_walk_next(iter)) {
|
|
|
+ if (IS_ERR(t)) {
|
|
|
+ if (PTR_ERR(t) == -EAGAIN)
|
|
|
+ continue;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (net_eq(sock_net(t->asoc->base.sk), net) &&
|
|
|
+ t->asoc->peer.primary_path == t)
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ return t;
|
|
|
+}
|
|
|
+
|
|
|
+struct sctp_transport *sctp_transport_get_idx(struct net *net,
|
|
|
+ struct rhashtable_iter *iter,
|
|
|
+ int pos)
|
|
|
+{
|
|
|
+ void *obj = SEQ_START_TOKEN;
|
|
|
+
|
|
|
+ while (pos && (obj = sctp_transport_get_next(net, iter)) &&
|
|
|
+ !IS_ERR(obj))
|
|
|
+ pos--;
|
|
|
+
|
|
|
+ return obj;
|
|
|
+}
|
|
|
+
|
|
|
+int sctp_for_each_endpoint(int (*cb)(struct sctp_endpoint *, void *),
|
|
|
+ void *p) {
|
|
|
+ int err = 0;
|
|
|
+ int hash = 0;
|
|
|
+ struct sctp_ep_common *epb;
|
|
|
+ struct sctp_hashbucket *head;
|
|
|
+
|
|
|
+ for (head = sctp_ep_hashtable; hash < sctp_ep_hashsize;
|
|
|
+ hash++, head++) {
|
|
|
+ read_lock(&head->lock);
|
|
|
+ sctp_for_each_hentry(epb, &head->chain) {
|
|
|
+ err = cb(sctp_ep(epb), p);
|
|
|
+ if (err)
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ read_unlock(&head->lock);
|
|
|
+ }
|
|
|
+
|
|
|
+ return err;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(sctp_for_each_endpoint);
|
|
|
+
|
|
|
+int sctp_transport_lookup_process(int (*cb)(struct sctp_transport *, void *),
|
|
|
+ struct net *net,
|
|
|
+ const union sctp_addr *laddr,
|
|
|
+ const union sctp_addr *paddr, void *p)
|
|
|
+{
|
|
|
+ struct sctp_transport *transport;
|
|
|
+ int err = 0;
|
|
|
+
|
|
|
+ rcu_read_lock();
|
|
|
+ transport = sctp_addrs_lookup_transport(net, laddr, paddr);
|
|
|
+ if (!transport || !sctp_transport_hold(transport))
|
|
|
+ goto out;
|
|
|
+ err = cb(transport, p);
|
|
|
+ sctp_transport_put(transport);
|
|
|
+
|
|
|
+out:
|
|
|
+ rcu_read_unlock();
|
|
|
+ return err;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(sctp_transport_lookup_process);
|
|
|
+
|
|
|
+int sctp_for_each_transport(int (*cb)(struct sctp_transport *, void *),
|
|
|
+ struct net *net, int pos, void *p) {
|
|
|
+ struct rhashtable_iter hti;
|
|
|
+ int err = 0;
|
|
|
+ void *obj;
|
|
|
+
|
|
|
+ if (sctp_transport_walk_start(&hti))
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ sctp_transport_get_idx(net, &hti, pos);
|
|
|
+ obj = sctp_transport_get_next(net, &hti);
|
|
|
+ for (; obj && !IS_ERR(obj); obj = sctp_transport_get_next(net, &hti)) {
|
|
|
+ struct sctp_transport *transport = obj;
|
|
|
+
|
|
|
+ if (!sctp_transport_hold(transport))
|
|
|
+ continue;
|
|
|
+ err = cb(transport, p);
|
|
|
+ sctp_transport_put(transport);
|
|
|
+ if (err)
|
|
|
+ break;
|
|
|
+ }
|
|
|
+out:
|
|
|
+ sctp_transport_walk_stop(&hti);
|
|
|
+ return err;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(sctp_for_each_transport);
|
|
|
+
|
|
|
/* 7.2.1 Association Status (SCTP_STATUS)
|
|
|
|
|
|
* Applications can retrieve current status information about an
|