aboutsummaryrefslogtreecommitdiffstats
path: root/net/sctp/input.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/sctp/input.c')
-rw-r--r--net/sctp/input.c187
1 files changed, 119 insertions, 68 deletions
diff --git a/net/sctp/input.c b/net/sctp/input.c
index b6493b3f11a9..d9a6e66c5c8a 100644
--- a/net/sctp/input.c
+++ b/net/sctp/input.c
@@ -782,65 +782,135 @@ hit:
782 return ep; 782 return ep;
783} 783}
784 784
785/* Insert association into the hash table. */ 785/* rhashtable for transport */
786static void __sctp_hash_established(struct sctp_association *asoc) 786struct sctp_hash_cmp_arg {
787 const union sctp_addr *laddr;
788 const union sctp_addr *paddr;
789 const struct net *net;
790};
791
792static inline int sctp_hash_cmp(struct rhashtable_compare_arg *arg,
793 const void *ptr)
787{ 794{
788 struct net *net = sock_net(asoc->base.sk); 795 const struct sctp_hash_cmp_arg *x = arg->key;
789 struct sctp_ep_common *epb; 796 const struct sctp_transport *t = ptr;
790 struct sctp_hashbucket *head; 797 struct sctp_association *asoc = t->asoc;
798 const struct net *net = x->net;
791 799
792 epb = &asoc->base; 800 if (x->laddr->v4.sin_port != htons(asoc->base.bind_addr.port))
801 return 1;
802 if (!sctp_cmp_addr_exact(&t->ipaddr, x->paddr))
803 return 1;
804 if (!net_eq(sock_net(asoc->base.sk), net))
805 return 1;
806 if (!sctp_bind_addr_match(&asoc->base.bind_addr,
807 x->laddr, sctp_sk(asoc->base.sk)))
808 return 1;
793 809
794 /* Calculate which chain this entry will belong to. */ 810 return 0;
795 epb->hashent = sctp_assoc_hashfn(net, epb->bind_addr.port, 811}
796 asoc->peer.port);
797 812
798 head = &sctp_assoc_hashtable[epb->hashent]; 813static inline u32 sctp_hash_obj(const void *data, u32 len, u32 seed)
814{
815 const struct sctp_transport *t = data;
816 const union sctp_addr *paddr = &t->ipaddr;
817 const struct net *net = sock_net(t->asoc->base.sk);
818 u16 lport = htons(t->asoc->base.bind_addr.port);
819 u32 addr;
820
821 if (paddr->sa.sa_family == AF_INET6)
822 addr = jhash(&paddr->v6.sin6_addr, 16, seed);
823 else
824 addr = paddr->v4.sin_addr.s_addr;
799 825
800 write_lock(&head->lock); 826 return jhash_3words(addr, ((__u32)paddr->v4.sin_port) << 16 |
801 hlist_add_head(&epb->node, &head->chain); 827 (__force __u32)lport, net_hash_mix(net), seed);
802 write_unlock(&head->lock);
803} 828}
804 829
805/* Add an association to the hash. Local BH-safe. */ 830static inline u32 sctp_hash_key(const void *data, u32 len, u32 seed)
806void sctp_hash_established(struct sctp_association *asoc)
807{ 831{
808 if (asoc->temp) 832 const struct sctp_hash_cmp_arg *x = data;
809 return; 833 const union sctp_addr *paddr = x->paddr;
834 const struct net *net = x->net;
835 u16 lport = x->laddr->v4.sin_port;
836 u32 addr;
837
838 if (paddr->sa.sa_family == AF_INET6)
839 addr = jhash(&paddr->v6.sin6_addr, 16, seed);
840 else
841 addr = paddr->v4.sin_addr.s_addr;
810 842
811 local_bh_disable(); 843 return jhash_3words(addr, ((__u32)paddr->v4.sin_port) << 16 |
812 __sctp_hash_established(asoc); 844 (__force __u32)lport, net_hash_mix(net), seed);
813 local_bh_enable();
814} 845}
815 846
816/* Remove association from the hash table. */ 847static const struct rhashtable_params sctp_hash_params = {
817static void __sctp_unhash_established(struct sctp_association *asoc) 848 .head_offset = offsetof(struct sctp_transport, node),
849 .hashfn = sctp_hash_key,
850 .obj_hashfn = sctp_hash_obj,
851 .obj_cmpfn = sctp_hash_cmp,
852 .automatic_shrinking = true,
853};
854
855int sctp_transport_hashtable_init(void)
818{ 856{
819 struct net *net = sock_net(asoc->base.sk); 857 return rhashtable_init(&sctp_transport_hashtable, &sctp_hash_params);
820 struct sctp_hashbucket *head; 858}
821 struct sctp_ep_common *epb;
822 859
823 epb = &asoc->base; 860void sctp_transport_hashtable_destroy(void)
861{
862 rhashtable_destroy(&sctp_transport_hashtable);
863}
824 864
825 epb->hashent = sctp_assoc_hashfn(net, epb->bind_addr.port, 865void sctp_hash_transport(struct sctp_transport *t)
826 asoc->peer.port); 866{
867 struct sctp_sockaddr_entry *addr;
868 struct sctp_hash_cmp_arg arg;
869
870 addr = list_entry(t->asoc->base.bind_addr.address_list.next,
871 struct sctp_sockaddr_entry, list);
872 arg.laddr = &addr->a;
873 arg.paddr = &t->ipaddr;
874 arg.net = sock_net(t->asoc->base.sk);
875
876reinsert:
877 if (rhashtable_lookup_insert_key(&sctp_transport_hashtable, &arg,
878 &t->node, sctp_hash_params) == -EBUSY)
879 goto reinsert;
880}
827 881
828 head = &sctp_assoc_hashtable[epb->hashent]; 882void sctp_unhash_transport(struct sctp_transport *t)
883{
884 rhashtable_remove_fast(&sctp_transport_hashtable, &t->node,
885 sctp_hash_params);
886}
829 887
830 write_lock(&head->lock); 888struct sctp_transport *sctp_addrs_lookup_transport(
831 hlist_del_init(&epb->node); 889 struct net *net,
832 write_unlock(&head->lock); 890 const union sctp_addr *laddr,
891 const union sctp_addr *paddr)
892{
893 struct sctp_hash_cmp_arg arg = {
894 .laddr = laddr,
895 .paddr = paddr,
896 .net = net,
897 };
898
899 return rhashtable_lookup_fast(&sctp_transport_hashtable, &arg,
900 sctp_hash_params);
833} 901}
834 902
835/* Remove association from the hash table. Local BH-safe. */ 903struct sctp_transport *sctp_epaddr_lookup_transport(
836void sctp_unhash_established(struct sctp_association *asoc) 904 const struct sctp_endpoint *ep,
905 const union sctp_addr *paddr)
837{ 906{
838 if (asoc->temp) 907 struct sctp_sockaddr_entry *addr;
839 return; 908 struct net *net = sock_net(ep->base.sk);
840 909
841 local_bh_disable(); 910 addr = list_entry(ep->base.bind_addr.address_list.next,
842 __sctp_unhash_established(asoc); 911 struct sctp_sockaddr_entry, list);
843 local_bh_enable(); 912
913 return sctp_addrs_lookup_transport(net, &addr->a, paddr);
844} 914}
845 915
846/* Look up an association. */ 916/* Look up an association. */
@@ -850,38 +920,19 @@ static struct sctp_association *__sctp_lookup_association(
850 const union sctp_addr *peer, 920 const union sctp_addr *peer,
851 struct sctp_transport **pt) 921 struct sctp_transport **pt)
852{ 922{
853 struct sctp_hashbucket *head; 923 struct sctp_transport *t;
854 struct sctp_ep_common *epb;
855 struct sctp_association *asoc;
856 struct sctp_transport *transport;
857 int hash;
858 924
859 /* Optimize here for direct hit, only listening connections can 925 t = sctp_addrs_lookup_transport(net, local, peer);
860 * have wildcards anyways. 926 if (!t || t->dead || t->asoc->temp)
861 */ 927 return NULL;
862 hash = sctp_assoc_hashfn(net, ntohs(local->v4.sin_port),
863 ntohs(peer->v4.sin_port));
864 head = &sctp_assoc_hashtable[hash];
865 read_lock(&head->lock);
866 sctp_for_each_hentry(epb, &head->chain) {
867 asoc = sctp_assoc(epb);
868 transport = sctp_assoc_is_match(asoc, net, local, peer);
869 if (transport)
870 goto hit;
871 }
872
873 read_unlock(&head->lock);
874 928
875 return NULL; 929 sctp_association_hold(t->asoc);
930 *pt = t;
876 931
877hit: 932 return t->asoc;
878 *pt = transport;
879 sctp_association_hold(asoc);
880 read_unlock(&head->lock);
881 return asoc;
882} 933}
883 934
884/* Look up an association. BH-safe. */ 935/* Look up an association. protected by RCU read lock */
885static 936static
886struct sctp_association *sctp_lookup_association(struct net *net, 937struct sctp_association *sctp_lookup_association(struct net *net,
887 const union sctp_addr *laddr, 938 const union sctp_addr *laddr,
@@ -890,9 +941,9 @@ struct sctp_association *sctp_lookup_association(struct net *net,
890{ 941{
891 struct sctp_association *asoc; 942 struct sctp_association *asoc;
892 943
893 local_bh_disable(); 944 rcu_read_lock();
894 asoc = __sctp_lookup_association(net, laddr, paddr, transportp); 945 asoc = __sctp_lookup_association(net, laddr, paddr, transportp);
895 local_bh_enable(); 946 rcu_read_unlock();
896 947
897 return asoc; 948 return asoc;
898} 949}