Skip to content

Commit 33c162a

Browse files
iamkafaidavem330
authored andcommitted
ipv6: datagram: Update dst cache of a connected datagram sk during pmtu update
There is a case in connected UDP socket such that getsockopt(IPV6_MTU) will return a stale MTU value. The reproducible sequence could be the following: 1. Create a connected UDP socket 2. Send some datagrams out 3. Receive a ICMPV6_PKT_TOOBIG 4. No new outgoing datagrams to trigger the sk_dst_check() logic to update the sk->sk_dst_cache. 5. getsockopt(IPV6_MTU) returns the mtu from the invalid sk->sk_dst_cache instead of the newly created RTF_CACHE clone. This patch updates the sk->sk_dst_cache for a connected datagram sk during pmtu-update code path. Note that the sk->sk_v6_daddr is used to do the route lookup instead of skb->data (i.e. iph). It is because a UDP socket can become connected after sending out some datagrams in un-connected state. or It can be connected multiple times to different destinations. Hence, iph may not be related to where sk is currently connected to. It is done under '!sock_owned_by_user(sk)' condition because the user may make another ip6_datagram_connect() (i.e changing the sk->sk_v6_daddr) while dst lookup is happening in the pmtu-update code path. For the sock_owned_by_user(sk) == true case, the next patch will introduce a release_cb() which will update the sk->sk_dst_cache. Test: Server (Connected UDP Socket): ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Route Details: [root@arch-fb-vm1 ~]# ip -6 r show | egrep '2fac' 2fac::/64 dev eth0 proto kernel metric 256 pref medium 2fac:face::/64 via 2fac::face dev eth0 metric 1024 pref medium A simple python code to create a connected UDP socket: import socket import errno HOST = '2fac::1' PORT = 8080 s = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) s.bind((HOST, PORT)) s.connect(('2fac:face::face', 53)) print("connected") while True: try: data = s.recv(1024) except socket.error as se: if se.errno == errno.EMSGSIZE: pmtu = s.getsockopt(41, 24) print("PMTU:%d" % pmtu) break s.close() Python program output after getting a ICMPV6_PKT_TOOBIG: [root@arch-fb-vm1 ~]# python2 ~/devshare/kernel/tasks/fib6/udp-connect-53-8080.py connected PMTU:1300 Cache routes after recieving TOOBIG: [root@arch-fb-vm1 ~]# ip -6 r show table cache 2fac:face::face via 2fac::face dev eth0 metric 0 cache expires 463sec mtu 1300 pref medium Client (Send the ICMPV6_PKT_TOOBIG): ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ scapy is used to generate the TOOBIG message. Here is the scapy script I have used: >>> p=Ether(src='da:75:4d:36:ac:32', dst='52:54:00:12:34:66', type=0x86dd)/IPv6(src='2fac::face', dst='2fac::1')/ICMPv6PacketTooBig(mtu=1300)/IPv6(src='2fac:: 1',dst='2fac:face::face', nh='UDP')/UDP(sport=8080,dport=53) >>> sendp(p, iface='qemubr0') Fixes: 45e4fd2 ("ipv6: Only create RTF_CACHE routes after encountering pmtu exception") Signed-off-by: Martin KaFai Lau <[email protected]> Reported-by: Wei Wang <[email protected]> Cc: Cong Wang <[email protected]> Cc: Eric Dumazet <[email protected]> Cc: Wei Wang <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 7e2040d commit 33c162a

File tree

3 files changed

+24
-9
lines changed

3 files changed

+24
-9
lines changed

include/net/ipv6.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -959,6 +959,7 @@ int compat_ipv6_getsockopt(struct sock *sk, int level, int optname,
959959
int ip6_datagram_connect(struct sock *sk, struct sockaddr *addr, int addr_len);
960960
int ip6_datagram_connect_v6_only(struct sock *sk, struct sockaddr *addr,
961961
int addr_len);
962+
int ip6_datagram_dst_update(struct sock *sk, bool fix_sk_saddr);
962963

963964
int ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len,
964965
int *addr_len);

net/ipv6/datagram.c

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ static void ip6_datagram_flow_key_init(struct flowi6 *fl6, struct sock *sk)
6464
security_sk_classify_flow(sk, flowi6_to_flowi(fl6));
6565
}
6666

67-
static int ip6_datagram_dst_update(struct sock *sk)
67+
int ip6_datagram_dst_update(struct sock *sk, bool fix_sk_saddr)
6868
{
6969
struct ip6_flowlabel *flowlabel = NULL;
7070
struct in6_addr *final_p, final;
@@ -93,14 +93,16 @@ static int ip6_datagram_dst_update(struct sock *sk)
9393
goto out;
9494
}
9595

96-
if (ipv6_addr_any(&np->saddr))
97-
np->saddr = fl6.saddr;
96+
if (fix_sk_saddr) {
97+
if (ipv6_addr_any(&np->saddr))
98+
np->saddr = fl6.saddr;
9899

99-
if (ipv6_addr_any(&sk->sk_v6_rcv_saddr)) {
100-
sk->sk_v6_rcv_saddr = fl6.saddr;
101-
inet->inet_rcv_saddr = LOOPBACK4_IPV6;
102-
if (sk->sk_prot->rehash)
103-
sk->sk_prot->rehash(sk);
100+
if (ipv6_addr_any(&sk->sk_v6_rcv_saddr)) {
101+
sk->sk_v6_rcv_saddr = fl6.saddr;
102+
inet->inet_rcv_saddr = LOOPBACK4_IPV6;
103+
if (sk->sk_prot->rehash)
104+
sk->sk_prot->rehash(sk);
105+
}
104106
}
105107

106108
ip6_dst_store(sk, dst,
@@ -221,7 +223,7 @@ static int __ip6_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int a
221223
* destination cache for it.
222224
*/
223225

224-
err = ip6_datagram_dst_update(sk);
226+
err = ip6_datagram_dst_update(sk, true);
225227
if (err)
226228
goto out;
227229

net/ipv6/route.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1418,8 +1418,20 @@ EXPORT_SYMBOL_GPL(ip6_update_pmtu);
14181418

14191419
void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu)
14201420
{
1421+
struct dst_entry *dst;
1422+
14211423
ip6_update_pmtu(skb, sock_net(sk), mtu,
14221424
sk->sk_bound_dev_if, sk->sk_mark);
1425+
1426+
dst = __sk_dst_get(sk);
1427+
if (!dst || !dst->obsolete ||
1428+
dst->ops->check(dst, inet6_sk(sk)->dst_cookie))
1429+
return;
1430+
1431+
bh_lock_sock(sk);
1432+
if (!sock_owned_by_user(sk) && !ipv6_addr_v4mapped(&sk->sk_v6_daddr))
1433+
ip6_datagram_dst_update(sk, false);
1434+
bh_unlock_sock(sk);
14231435
}
14241436
EXPORT_SYMBOL_GPL(ip6_sk_update_pmtu);
14251437

0 commit comments

Comments
 (0)