diff options
Diffstat (limited to 'net/sctp/input.c')
-rw-r--r-- | net/sctp/input.c | 187 |
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 */ |
786 | static void __sctp_hash_established(struct sctp_association *asoc) | 786 | struct sctp_hash_cmp_arg { |
787 | const union sctp_addr *laddr; | ||
788 | const union sctp_addr *paddr; | ||
789 | const struct net *net; | ||
790 | }; | ||
791 | |||
792 | static 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]; | 813 | static 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. */ | 830 | static inline u32 sctp_hash_key(const void *data, u32 len, u32 seed) |
806 | void 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. */ | 847 | static const struct rhashtable_params sctp_hash_params = { |
817 | static 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 | |||
855 | int 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; | 860 | void 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, | 865 | void 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 | |||
876 | reinsert: | ||
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]; | 882 | void 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); | 888 | struct 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. */ | 903 | struct sctp_transport *sctp_epaddr_lookup_transport( |
836 | void 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 | ||
877 | hit: | 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 */ |
885 | static | 936 | static |
886 | struct sctp_association *sctp_lookup_association(struct net *net, | 937 | struct 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 | } |