int sctp_del_bind_addr(struct sctp_bind_addr *, union sctp_addr *);
int sctp_bind_addr_match(struct sctp_bind_addr *, const union sctp_addr *,
struct sctp_sock *);
+int sctp_bind_addr_conflict(struct sctp_bind_addr *, const union sctp_addr *,
+ struct sctp_sock *, struct sctp_sock *);
int sctp_bind_addr_state(const struct sctp_bind_addr *bp,
const union sctp_addr *addr);
union sctp_addr *sctp_find_unmatch_addr(struct sctp_bind_addr *bp,
return match;
}
+/* Does the address 'addr' conflict with any addresses in
+ * the bp.
+ */
+int sctp_bind_addr_conflict(struct sctp_bind_addr *bp,
+ const union sctp_addr *addr,
+ struct sctp_sock *bp_sp,
+ struct sctp_sock *addr_sp)
+{
+ struct sctp_sockaddr_entry *laddr;
+ int conflict = 0;
+ struct sctp_sock *sp;
+
+ /* Pick the IPv6 socket as the basis of comparison
+ * since it's usually a superset of the IPv4.
+ * If there is no IPv6 socket, then default to bind_addr.
+ */
+ if (sctp_opt2sk(bp_sp)->sk_family == AF_INET6)
+ sp = bp_sp;
+ else if (sctp_opt2sk(addr_sp)->sk_family == AF_INET6)
+ sp = addr_sp;
+ else
+ sp = bp_sp;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(laddr, &bp->address_list, list) {
+ if (!laddr->valid)
+ continue;
+
+ conflict = sp->pf->cmp_addr(&laddr->a, addr, sp);
+ if (conflict)
+ break;
+ }
+ rcu_read_unlock();
+
+ return conflict;
+}
+
/* Get the state of the entry in the bind_addr_list */
int sctp_bind_addr_state(const struct sctp_bind_addr *bp,
const union sctp_addr *addr)
return 1;
/* v4-mapped-v6 addresses */
case AF_INET:
- if (!__ipv6_only_sock(sctp_opt2sk(sp)) && sp->v4mapped)
+ if (!__ipv6_only_sock(sctp_opt2sk(sp)))
return 1;
default:
return 0;
if (!af1 || !af2)
return 0;
+
+ /* If the socket is IPv6 only, v4 addrs will not match */
+ if (__ipv6_only_sock(sctp_opt2sk(opt)) && af1 != af2)
+ return 0;
+
/* Today, wildcard AF_INET/AF_INET6. */
if (sctp_is_any(addr1) || sctp_is_any(addr2))
return 1;
return 0;
}
dev_put(dev);
+ } else if (type == IPV6_ADDR_MAPPED) {
+ if (!opt->v4mapped)
+ return 0;
}
+
af = opt->pf->af;
}
return af->available(addr, opt);
static int sctp_inet6_supported_addrs(const struct sctp_sock *opt,
__be16 *types)
{
- types[0] = SCTP_PARAM_IPV4_ADDRESS;
- types[1] = SCTP_PARAM_IPV6_ADDRESS;
- return 2;
+ types[0] = SCTP_PARAM_IPV6_ADDRESS;
+ if (!opt || !ipv6_only_sock(sctp_opt2sk(opt))) {
+ types[1] = SCTP_PARAM_IPV4_ADDRESS;
+ return 2;
+ }
+ return 1;
}
static const struct proto_ops inet6_seqpacket_ops = {
struct sctp_sock *sp,
const struct sk_buff *skb)
{
+ /* IPv4 addresses not allowed */
+ if (sp && ipv6_only_sock(sctp_opt2sk(sp)))
+ return 0;
+
/* Is this a non-unicast address or a unusable SCTP address? */
if (IS_IPV4_UNUSABLE_ADDRESS(addr->v4.sin_addr.s_addr))
return 0;
!sysctl_ip_nonlocal_bind)
return 0;
+ if (ipv6_only_sock(sctp_opt2sk(sp)))
+ return 0;
+
return 1;
}
case SCTP_PARAM_IPV6_ADDRESS:
if (PF_INET6 != asoc->base.sk->sk_family)
break;
- /* Fall through. */
+ goto do_addr_param;
+
case SCTP_PARAM_IPV4_ADDRESS:
+ /* v4 addresses are not allowed on v6-only socket */
+ if (ipv6_only_sock(asoc->base.sk))
+ break;
+do_addr_param:
af = sctp_get_af_specific(param_type2af(param.p->type));
af->from_addr_param(&addr, param.addr, htons(asoc->peer.port), 0);
scope = sctp_scope(peer_addr);
if (len < sizeof (struct sockaddr))
return NULL;
- /* Does this PF support this AF? */
- if (!opt->pf->af_supported(addr->sa.sa_family, opt))
- return NULL;
+ /* V4 mapped address are really of AF_INET family */
+ if (addr->sa.sa_family == AF_INET6 &&
+ ipv6_addr_v4mapped(&addr->v6.sin6_addr)) {
+ if (!opt->pf->af_supported(AF_INET, opt))
+ return NULL;
+ } else {
+ /* Does this PF support this AF? */
+ if (!opt->pf->af_supported(addr->sa.sa_family, opt))
+ return NULL;
+ }
/* If we get this far, af is valid. */
af = sctp_get_af_specific(addr->sa.sa_family);
(AF_INET6 == addr->a.sa.sa_family))
continue;
+ if ((PF_INET6 == sk->sk_family) &&
+ inet_v6_ipv6only(sk) &&
+ (AF_INET == addr->a.sa.sa_family))
+ continue;
+
cnt++;
}
rcu_read_unlock();
if ((PF_INET == sk->sk_family) &&
(AF_INET6 == addr->a.sa.sa_family))
continue;
+ if ((PF_INET6 == sk->sk_family) &&
+ inet_v6_ipv6only(sk) &&
+ (AF_INET == addr->a.sa.sa_family))
+ continue;
memcpy(&temp, &addr->a, sizeof(temp));
if (!temp.v4.sin_port)
temp.v4.sin_port = htons(port);
if ((PF_INET == sk->sk_family) &&
(AF_INET6 == addr->a.sa.sa_family))
continue;
+ if ((PF_INET6 == sk->sk_family) &&
+ inet_v6_ipv6only(sk) &&
+ (AF_INET == addr->a.sa.sa_family))
+ continue;
memcpy(&temp, &addr->a, sizeof(temp));
if (!temp.v4.sin_port)
temp.v4.sin_port = htons(port);
sk2->sk_state != SCTP_SS_LISTENING)
continue;
- if (sctp_bind_addr_match(&ep2->base.bind_addr, addr,
- sctp_sk(sk))) {
+ if (sctp_bind_addr_conflict(&ep2->base.bind_addr, addr,
+ sctp_sk(sk2), sctp_sk(sk))) {
ret = (long)sk2;
goto fail_unlock;
}