@@ -65,7 +65,7 @@ int __ip4_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len
inet->inet_saddr = fl4->saddr; /* Update source address */
if (!inet->inet_rcv_saddr) {
inet->inet_rcv_saddr = fl4->saddr;
- if (sk->sk_prot->rehash)
+ if (sk->sk_prot->rehash && sk->sk_family == AF_INET)
sk->sk_prot->rehash(sk);
}
inet->inet_daddr = fl4->daddr;
@@ -211,7 +211,7 @@ int __ip6_datagram_connect(struct sock *sk, struct sockaddr *uaddr,
ipv6_mapped_addr_any(&sk->sk_v6_rcv_saddr)) {
ipv6_addr_set_v4mapped(inet->inet_rcv_saddr,
&sk->sk_v6_rcv_saddr);
- if (sk->sk_prot->rehash)
+ if (sk->sk_prot->rehash && sk->sk_family == AF_INET6)
sk->sk_prot->rehash(sk);
}
It makes no sense to rehash an IPv4 socket when we change sk_v6_rcv_saddr, or to rehash an IPv6 socket as inet_rcv_saddr is set: the secondary hash (including the local address) won't change, because ipv4_portaddr_hash() and ipv6_portaddr_hash() only take the address matching the socket family. Currently, dual-stack AF_INET6 sockets (without IPV6_V6ONLY) are redundantly rehashed if connect() is issued on them with an IPv4 address: __ip6_datagram_connect() calls __ip4_datagram_connect(). This case can be reproduced with: socat UDP6-LISTEN:1337,null-eof STDOUT & { sleep 1; : | socat STDIN UDP4:0:1337,shut-null; } and, in general, the assumptions __ip4_datagram_connect() <-> AF_INET __ip6_datagram_connect() <-> AF_INET6 don't necessarily hold. This is a mere optimisation at the moment, but, in the next patch, we'll change the rehash operation into an operation that also sets the receive address, and we want this new operation to be called only with addresses corresponding to the socket family, for simplicity. v1: - explain in which case sk_family isn't AF_INET, in __ip4_datagram_connect() (Willem de Bruijn) - rebase onto net-next Signed-off-by: Stefano Brivio <sbrivio@redhat.com> --- net/ipv4/datagram.c | 2 +- net/ipv6/datagram.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-)