| From: Xin Long <lucien.xin@gmail.com> |
| Date: Thu, 12 Apr 2018 14:24:31 +0800 |
| Subject: sctp: do not check port in sctp_inet6_cmp_addr |
| |
| commit 1071ec9d453a38023579714b64a951a2fb982071 upstream. |
| |
| pf->cmp_addr() is called before binding a v6 address to the sock. It |
| should not check ports, like in sctp_inet_cmp_addr. |
| |
| But sctp_inet6_cmp_addr checks the addr by invoking af(6)->cmp_addr, |
| sctp_v6_cmp_addr where it also compares the ports. |
| |
| This would cause that setsockopt(SCTP_SOCKOPT_BINDX_ADD) could bind |
| multiple duplicated IPv6 addresses after Commit 40b4f0fd74e4 ("sctp: |
| lack the check for ports in sctp_v6_cmp_addr"). |
| |
| This patch is to remove af->cmp_addr called in sctp_inet6_cmp_addr, |
| but do the proper check for both v6 addrs and v4mapped addrs. |
| |
| v1->v2: |
| - define __sctp_v6_cmp_addr to do the common address comparison |
| used for both pf and af v6 cmp_addr. |
| |
| Fixes: 40b4f0fd74e4 ("sctp: lack the check for ports in sctp_v6_cmp_addr") |
| Reported-by: Jianwen Ji <jiji@redhat.com> |
| Signed-off-by: Xin Long <lucien.xin@gmail.com> |
| Acked-by: Neil Horman <nhorman@tuxdriver.com> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| Signed-off-by: Ben Hutchings <ben@decadent.org.uk> |
| --- |
| net/sctp/ipv6.c | 60 ++++++++++++++++++++++++------------------------- |
| 1 file changed, 30 insertions(+), 30 deletions(-) |
| |
| --- a/net/sctp/ipv6.c |
| +++ b/net/sctp/ipv6.c |
| @@ -496,46 +496,49 @@ static void sctp_v6_to_addr(union sctp_a |
| addr->v6.sin6_scope_id = 0; |
| } |
| |
| -/* Compare addresses exactly. |
| - * v4-mapped-v6 is also in consideration. |
| - */ |
| -static int sctp_v6_cmp_addr(const union sctp_addr *addr1, |
| - const union sctp_addr *addr2) |
| +static int __sctp_v6_cmp_addr(const union sctp_addr *addr1, |
| + const union sctp_addr *addr2) |
| { |
| if (addr1->sa.sa_family != addr2->sa.sa_family) { |
| if (addr1->sa.sa_family == AF_INET && |
| addr2->sa.sa_family == AF_INET6 && |
| - ipv6_addr_v4mapped(&addr2->v6.sin6_addr)) { |
| - if (addr2->v6.sin6_port == addr1->v4.sin_port && |
| - addr2->v6.sin6_addr.s6_addr32[3] == |
| - addr1->v4.sin_addr.s_addr) |
| - return 1; |
| - } |
| + ipv6_addr_v4mapped(&addr2->v6.sin6_addr) && |
| + addr2->v6.sin6_addr.s6_addr32[3] == |
| + addr1->v4.sin_addr.s_addr) |
| + return 1; |
| + |
| if (addr2->sa.sa_family == AF_INET && |
| addr1->sa.sa_family == AF_INET6 && |
| - ipv6_addr_v4mapped(&addr1->v6.sin6_addr)) { |
| - if (addr1->v6.sin6_port == addr2->v4.sin_port && |
| - addr1->v6.sin6_addr.s6_addr32[3] == |
| - addr2->v4.sin_addr.s_addr) |
| - return 1; |
| - } |
| + ipv6_addr_v4mapped(&addr1->v6.sin6_addr) && |
| + addr1->v6.sin6_addr.s6_addr32[3] == |
| + addr2->v4.sin_addr.s_addr) |
| + return 1; |
| + |
| 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. */ |
| - if (ipv6_addr_type(&addr1->v6.sin6_addr) & IPV6_ADDR_LINKLOCAL) { |
| - if (addr1->v6.sin6_scope_id && addr2->v6.sin6_scope_id && |
| - (addr1->v6.sin6_scope_id != addr2->v6.sin6_scope_id)) { |
| - return 0; |
| - } |
| - } |
| + if ((ipv6_addr_type(&addr1->v6.sin6_addr) & IPV6_ADDR_LINKLOCAL) && |
| + addr1->v6.sin6_scope_id && addr2->v6.sin6_scope_id && |
| + addr1->v6.sin6_scope_id != addr2->v6.sin6_scope_id) |
| + return 0; |
| |
| return 1; |
| } |
| |
| +/* Compare addresses exactly. |
| + * v4-mapped-v6 is also in consideration. |
| + */ |
| +static int sctp_v6_cmp_addr(const union sctp_addr *addr1, |
| + const union sctp_addr *addr2) |
| +{ |
| + return __sctp_v6_cmp_addr(addr1, addr2) && |
| + addr1->v6.sin6_port == addr2->v6.sin6_port; |
| +} |
| + |
| /* Initialize addr struct to INADDR_ANY. */ |
| static void sctp_v6_inaddr_any(union sctp_addr *addr, __be16 port) |
| { |
| @@ -820,8 +823,8 @@ static int sctp_inet6_cmp_addr(const uni |
| const union sctp_addr *addr2, |
| struct sctp_sock *opt) |
| { |
| - struct sctp_af *af1, *af2; |
| struct sock *sk = sctp_opt2sk(opt); |
| + struct sctp_af *af1, *af2; |
| |
| af1 = sctp_get_af_specific(addr1->sa.sa_family); |
| af2 = sctp_get_af_specific(addr2->sa.sa_family); |
| @@ -837,10 +840,7 @@ static int sctp_inet6_cmp_addr(const uni |
| if (sctp_is_any(sk, addr1) || sctp_is_any(sk, addr2)) |
| return 1; |
| |
| - if (addr1->sa.sa_family != addr2->sa.sa_family) |
| - return 0; |
| - |
| - return af1->cmp_addr(addr1, addr2); |
| + return __sctp_v6_cmp_addr(addr1, addr2); |
| } |
| |
| /* Verify that the provided sockaddr looks bindable. Common verification, |