diff options
author | Xin Long <lucien.xin@gmail.com> | 2018-11-12 05:27:16 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2018-11-12 12:09:51 -0500 |
commit | 76c6d988aeb3c15d57ea0c245a3b5f27802c1fbe (patch) | |
tree | 5508ea6085cda96f528f98229a2317536b776bad | |
parent | 532ae2f10e6eab2ec66ecad805d57d3d70cea020 (diff) |
sctp: add sock_reuseport for the sock in __sctp_hash_endpoint
This is a part of sk_reuseport support for sctp. It defines a helper
sctp_bind_addrs_check() to check if the bind_addrs in two socks are
matched. It will add sock_reuseport if they are completely matched,
and return err if they are partly matched, and alloc sock_reuseport
if all socks are not matched at all.
It will work until sk_reuseport support is added in
sctp_get_port_local() in the next patch.
v1->v2:
- use 'laddr->valid && laddr2->valid' check instead as Marcelo
pointed in sctp_bind_addrs_check().
Acked-by: Neil Horman <nhorman@tuxdriver.com>
Signed-off-by: Xin Long <lucien.xin@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | include/net/sctp/sctp.h | 2 | ||||
-rw-r--r-- | include/net/sctp/structs.h | 2 | ||||
-rw-r--r-- | net/core/sock_reuseport.c | 1 | ||||
-rw-r--r-- | net/sctp/bind_addr.c | 28 | ||||
-rw-r--r-- | net/sctp/input.c | 60 | ||||
-rw-r--r-- | net/sctp/socket.c | 3 |
6 files changed, 85 insertions, 11 deletions
diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h index 9a3b48a35e90..cdf2e80abc44 100644 --- a/include/net/sctp/sctp.h +++ b/include/net/sctp/sctp.h | |||
@@ -152,7 +152,7 @@ int sctp_primitive_RECONF(struct net *net, struct sctp_association *asoc, | |||
152 | */ | 152 | */ |
153 | int sctp_rcv(struct sk_buff *skb); | 153 | int sctp_rcv(struct sk_buff *skb); |
154 | int sctp_v4_err(struct sk_buff *skb, u32 info); | 154 | int sctp_v4_err(struct sk_buff *skb, u32 info); |
155 | void sctp_hash_endpoint(struct sctp_endpoint *); | 155 | int sctp_hash_endpoint(struct sctp_endpoint *ep); |
156 | void sctp_unhash_endpoint(struct sctp_endpoint *); | 156 | void sctp_unhash_endpoint(struct sctp_endpoint *); |
157 | struct sock *sctp_err_lookup(struct net *net, int family, struct sk_buff *, | 157 | struct sock *sctp_err_lookup(struct net *net, int family, struct sk_buff *, |
158 | struct sctphdr *, struct sctp_association **, | 158 | struct sctphdr *, struct sctp_association **, |
diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index a11f93790476..15d017f33a46 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h | |||
@@ -1190,6 +1190,8 @@ int sctp_bind_addr_conflict(struct sctp_bind_addr *, const union sctp_addr *, | |||
1190 | struct sctp_sock *, struct sctp_sock *); | 1190 | struct sctp_sock *, struct sctp_sock *); |
1191 | int sctp_bind_addr_state(const struct sctp_bind_addr *bp, | 1191 | int sctp_bind_addr_state(const struct sctp_bind_addr *bp, |
1192 | const union sctp_addr *addr); | 1192 | const union sctp_addr *addr); |
1193 | int sctp_bind_addrs_check(struct sctp_sock *sp, | ||
1194 | struct sctp_sock *sp2, int cnt2); | ||
1193 | union sctp_addr *sctp_find_unmatch_addr(struct sctp_bind_addr *bp, | 1195 | union sctp_addr *sctp_find_unmatch_addr(struct sctp_bind_addr *bp, |
1194 | const union sctp_addr *addrs, | 1196 | const union sctp_addr *addrs, |
1195 | int addrcnt, | 1197 | int addrcnt, |
diff --git a/net/core/sock_reuseport.c b/net/core/sock_reuseport.c index ba5cba56f574..d8fe3e549373 100644 --- a/net/core/sock_reuseport.c +++ b/net/core/sock_reuseport.c | |||
@@ -187,6 +187,7 @@ int reuseport_add_sock(struct sock *sk, struct sock *sk2, bool bind_inany) | |||
187 | call_rcu(&old_reuse->rcu, reuseport_free_rcu); | 187 | call_rcu(&old_reuse->rcu, reuseport_free_rcu); |
188 | return 0; | 188 | return 0; |
189 | } | 189 | } |
190 | EXPORT_SYMBOL(reuseport_add_sock); | ||
190 | 191 | ||
191 | void reuseport_detach_sock(struct sock *sk) | 192 | void reuseport_detach_sock(struct sock *sk) |
192 | { | 193 | { |
diff --git a/net/sctp/bind_addr.c b/net/sctp/bind_addr.c index 7df3704982f5..ebf28adba789 100644 --- a/net/sctp/bind_addr.c +++ b/net/sctp/bind_addr.c | |||
@@ -337,6 +337,34 @@ int sctp_bind_addr_match(struct sctp_bind_addr *bp, | |||
337 | return match; | 337 | return match; |
338 | } | 338 | } |
339 | 339 | ||
340 | int sctp_bind_addrs_check(struct sctp_sock *sp, | ||
341 | struct sctp_sock *sp2, int cnt2) | ||
342 | { | ||
343 | struct sctp_bind_addr *bp2 = &sp2->ep->base.bind_addr; | ||
344 | struct sctp_bind_addr *bp = &sp->ep->base.bind_addr; | ||
345 | struct sctp_sockaddr_entry *laddr, *laddr2; | ||
346 | bool exist = false; | ||
347 | int cnt = 0; | ||
348 | |||
349 | rcu_read_lock(); | ||
350 | list_for_each_entry_rcu(laddr, &bp->address_list, list) { | ||
351 | list_for_each_entry_rcu(laddr2, &bp2->address_list, list) { | ||
352 | if (sp->pf->af->cmp_addr(&laddr->a, &laddr2->a) && | ||
353 | laddr->valid && laddr2->valid) { | ||
354 | exist = true; | ||
355 | goto next; | ||
356 | } | ||
357 | } | ||
358 | cnt = 0; | ||
359 | break; | ||
360 | next: | ||
361 | cnt++; | ||
362 | } | ||
363 | rcu_read_unlock(); | ||
364 | |||
365 | return (cnt == cnt2) ? 0 : (exist ? -EEXIST : 1); | ||
366 | } | ||
367 | |||
340 | /* Does the address 'addr' conflict with any addresses in | 368 | /* Does the address 'addr' conflict with any addresses in |
341 | * the bp. | 369 | * the bp. |
342 | */ | 370 | */ |
diff --git a/net/sctp/input.c b/net/sctp/input.c index 00f995e37795..d7a649d240e5 100644 --- a/net/sctp/input.c +++ b/net/sctp/input.c | |||
@@ -724,43 +724,87 @@ discard: | |||
724 | } | 724 | } |
725 | 725 | ||
726 | /* Insert endpoint into the hash table. */ | 726 | /* Insert endpoint into the hash table. */ |
727 | static void __sctp_hash_endpoint(struct sctp_endpoint *ep) | 727 | static int __sctp_hash_endpoint(struct sctp_endpoint *ep) |
728 | { | 728 | { |
729 | struct net *net = sock_net(ep->base.sk); | 729 | struct sock *sk = ep->base.sk; |
730 | struct sctp_ep_common *epb; | 730 | struct net *net = sock_net(sk); |
731 | struct sctp_hashbucket *head; | 731 | struct sctp_hashbucket *head; |
732 | struct sctp_ep_common *epb; | ||
732 | 733 | ||
733 | epb = &ep->base; | 734 | epb = &ep->base; |
734 | |||
735 | epb->hashent = sctp_ep_hashfn(net, epb->bind_addr.port); | 735 | epb->hashent = sctp_ep_hashfn(net, epb->bind_addr.port); |
736 | head = &sctp_ep_hashtable[epb->hashent]; | 736 | head = &sctp_ep_hashtable[epb->hashent]; |
737 | 737 | ||
738 | if (sk->sk_reuseport) { | ||
739 | bool any = sctp_is_ep_boundall(sk); | ||
740 | struct sctp_ep_common *epb2; | ||
741 | struct list_head *list; | ||
742 | int cnt = 0, err = 1; | ||
743 | |||
744 | list_for_each(list, &ep->base.bind_addr.address_list) | ||
745 | cnt++; | ||
746 | |||
747 | sctp_for_each_hentry(epb2, &head->chain) { | ||
748 | struct sock *sk2 = epb2->sk; | ||
749 | |||
750 | if (!net_eq(sock_net(sk2), net) || sk2 == sk || | ||
751 | !uid_eq(sock_i_uid(sk2), sock_i_uid(sk)) || | ||
752 | !sk2->sk_reuseport) | ||
753 | continue; | ||
754 | |||
755 | err = sctp_bind_addrs_check(sctp_sk(sk2), | ||
756 | sctp_sk(sk), cnt); | ||
757 | if (!err) { | ||
758 | err = reuseport_add_sock(sk, sk2, any); | ||
759 | if (err) | ||
760 | return err; | ||
761 | break; | ||
762 | } else if (err < 0) { | ||
763 | return err; | ||
764 | } | ||
765 | } | ||
766 | |||
767 | if (err) { | ||
768 | err = reuseport_alloc(sk, any); | ||
769 | if (err) | ||
770 | return err; | ||
771 | } | ||
772 | } | ||
773 | |||
738 | write_lock(&head->lock); | 774 | write_lock(&head->lock); |
739 | hlist_add_head(&epb->node, &head->chain); | 775 | hlist_add_head(&epb->node, &head->chain); |
740 | write_unlock(&head->lock); | 776 | write_unlock(&head->lock); |
777 | return 0; | ||
741 | } | 778 | } |
742 | 779 | ||
743 | /* Add an endpoint to the hash. Local BH-safe. */ | 780 | /* Add an endpoint to the hash. Local BH-safe. */ |
744 | void sctp_hash_endpoint(struct sctp_endpoint *ep) | 781 | int sctp_hash_endpoint(struct sctp_endpoint *ep) |
745 | { | 782 | { |
783 | int err; | ||
784 | |||
746 | local_bh_disable(); | 785 | local_bh_disable(); |
747 | __sctp_hash_endpoint(ep); | 786 | err = __sctp_hash_endpoint(ep); |
748 | local_bh_enable(); | 787 | local_bh_enable(); |
788 | |||
789 | return err; | ||
749 | } | 790 | } |
750 | 791 | ||
751 | /* Remove endpoint from the hash table. */ | 792 | /* Remove endpoint from the hash table. */ |
752 | static void __sctp_unhash_endpoint(struct sctp_endpoint *ep) | 793 | static void __sctp_unhash_endpoint(struct sctp_endpoint *ep) |
753 | { | 794 | { |
754 | struct net *net = sock_net(ep->base.sk); | 795 | struct sock *sk = ep->base.sk; |
755 | struct sctp_hashbucket *head; | 796 | struct sctp_hashbucket *head; |
756 | struct sctp_ep_common *epb; | 797 | struct sctp_ep_common *epb; |
757 | 798 | ||
758 | epb = &ep->base; | 799 | epb = &ep->base; |
759 | 800 | ||
760 | epb->hashent = sctp_ep_hashfn(net, epb->bind_addr.port); | 801 | epb->hashent = sctp_ep_hashfn(sock_net(sk), epb->bind_addr.port); |
761 | 802 | ||
762 | head = &sctp_ep_hashtable[epb->hashent]; | 803 | head = &sctp_ep_hashtable[epb->hashent]; |
763 | 804 | ||
805 | if (rcu_access_pointer(sk->sk_reuseport_cb)) | ||
806 | reuseport_detach_sock(sk); | ||
807 | |||
764 | write_lock(&head->lock); | 808 | write_lock(&head->lock); |
765 | hlist_del_init(&epb->node); | 809 | hlist_del_init(&epb->node); |
766 | write_unlock(&head->lock); | 810 | write_unlock(&head->lock); |
diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 739f3e50120d..2e955f1dbe3f 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c | |||
@@ -7852,8 +7852,7 @@ static int sctp_listen_start(struct sock *sk, int backlog) | |||
7852 | } | 7852 | } |
7853 | 7853 | ||
7854 | sk->sk_max_ack_backlog = backlog; | 7854 | sk->sk_max_ack_backlog = backlog; |
7855 | sctp_hash_endpoint(ep); | 7855 | return sctp_hash_endpoint(ep); |
7856 | return 0; | ||
7857 | } | 7856 | } |
7858 | 7857 | ||
7859 | /* | 7858 | /* |