From 7d6850f7c618d8f13d7945dd0dcee98223be6459 Mon Sep 17 00:00:00 2001 From: Alexey Kodanev Date: Tue, 3 Apr 2018 15:00:07 +0300 Subject: ipv6: add a wrapper for ip6_dst_store() with flowi6 checks Move commonly used pattern of ip6_dst_store() usage to a separate function - ip6_sk_dst_store_flow(), which will check the addresses for equality using the flow information, before saving them. There is no functional changes in this patch. In addition, it will be used in the next patch, in ip6_sk_dst_lookup_flow(). Signed-off-by: Alexey Kodanev Signed-off-by: David S. Miller --- include/net/ip6_route.h | 3 +++ net/ipv6/datagram.c | 9 +-------- net/ipv6/route.c | 17 +++++++++++++++++ 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/include/net/ip6_route.h b/include/net/ip6_route.h index 0084013d6bed..08b132381984 100644 --- a/include/net/ip6_route.h +++ b/include/net/ip6_route.h @@ -214,6 +214,9 @@ static inline void ip6_dst_store(struct sock *sk, struct dst_entry *dst, #endif } +void ip6_sk_dst_store_flow(struct sock *sk, struct dst_entry *dst, + const struct flowi6 *fl6); + static inline bool ipv6_unicast_destination(const struct sk_buff *skb) { struct rt6_info *rt = (struct rt6_info *) skb_dst(skb); diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c index 88bc2ef7c7a8..a02ad100f0d7 100644 --- a/net/ipv6/datagram.c +++ b/net/ipv6/datagram.c @@ -106,14 +106,7 @@ int ip6_datagram_dst_update(struct sock *sk, bool fix_sk_saddr) } } - ip6_dst_store(sk, dst, - ipv6_addr_equal(&fl6.daddr, &sk->sk_v6_daddr) ? - &sk->sk_v6_daddr : NULL, -#ifdef CONFIG_IPV6_SUBTREES - ipv6_addr_equal(&fl6.saddr, &np->saddr) ? - &np->saddr : -#endif - NULL); + ip6_sk_dst_store_flow(sk, dst, &fl6); out: fl6_sock_release(flowlabel); diff --git a/net/ipv6/route.c b/net/ipv6/route.c index f239f91d2efb..49b954d6d0fa 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -2229,6 +2229,23 @@ void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu) } EXPORT_SYMBOL_GPL(ip6_sk_update_pmtu); +void ip6_sk_dst_store_flow(struct sock *sk, struct dst_entry *dst, + const struct flowi6 *fl6) +{ +#ifdef CONFIG_IPV6_SUBTREES + struct ipv6_pinfo *np = inet6_sk(sk); +#endif + + ip6_dst_store(sk, dst, + ipv6_addr_equal(&fl6->daddr, &sk->sk_v6_daddr) ? + &sk->sk_v6_daddr : NULL, +#ifdef CONFIG_IPV6_SUBTREES + ipv6_addr_equal(&fl6->saddr, &np->saddr) ? + &np->saddr : +#endif + NULL); +} + /* Handle redirects */ struct ip6rd_flowi { struct flowi6 fl6; -- cgit v1.2.3 From 96818159c3c08911330e84f86b3becf71aeeaac8 Mon Sep 17 00:00:00 2001 From: Alexey Kodanev Date: Tue, 3 Apr 2018 15:00:08 +0300 Subject: ipv6: allow to cache dst for a connected sk in ip6_sk_dst_lookup_flow() Add 'connected' parameter to ip6_sk_dst_lookup_flow() and update the cache only if ip6_sk_dst_check() returns NULL and a socket is connected. The function is used as before, the new behavior for UDP sockets in udpv6_sendmsg() will be enabled in the next patch. Signed-off-by: Alexey Kodanev Signed-off-by: David S. Miller --- include/net/ipv6.h | 3 ++- net/ipv6/ip6_output.c | 15 ++++++++++++--- net/ipv6/ping.c | 2 +- net/ipv6/udp.c | 2 +- 4 files changed, 16 insertions(+), 6 deletions(-) diff --git a/include/net/ipv6.h b/include/net/ipv6.h index 9b6e7f51b1d4..836f31af1369 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -965,7 +965,8 @@ int ip6_dst_lookup(struct net *net, struct sock *sk, struct dst_entry **dst, struct dst_entry *ip6_dst_lookup_flow(const struct sock *sk, struct flowi6 *fl6, const struct in6_addr *final_dst); struct dst_entry *ip6_sk_dst_lookup_flow(struct sock *sk, struct flowi6 *fl6, - const struct in6_addr *final_dst); + const struct in6_addr *final_dst, + bool connected); struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *orig_dst); diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index e6eaa4dd9f60..66a768b7b8fb 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -1105,23 +1105,32 @@ EXPORT_SYMBOL_GPL(ip6_dst_lookup_flow); * @sk: socket which provides the dst cache and route info * @fl6: flow to lookup * @final_dst: final destination address for ipsec lookup + * @connected: whether @sk is connected or not * * This function performs a route lookup on the given flow with the * possibility of using the cached route in the socket if it is valid. * It will take the socket dst lock when operating on the dst cache. * As a result, this function can only be used in process context. * + * In addition, for a connected socket, cache the dst in the socket + * if the current cache is not valid. + * * It returns a valid dst pointer on success, or a pointer encoded * error code. */ struct dst_entry *ip6_sk_dst_lookup_flow(struct sock *sk, struct flowi6 *fl6, - const struct in6_addr *final_dst) + const struct in6_addr *final_dst, + bool connected) { struct dst_entry *dst = sk_dst_check(sk, inet6_sk(sk)->dst_cookie); dst = ip6_sk_dst_check(sk, dst, fl6); - if (!dst) - dst = ip6_dst_lookup_flow(sk, fl6, final_dst); + if (dst) + return dst; + + dst = ip6_dst_lookup_flow(sk, fl6, final_dst); + if (connected && !IS_ERR(dst)) + ip6_sk_dst_store_flow(sk, dst_clone(dst), fl6); return dst; } diff --git a/net/ipv6/ping.c b/net/ipv6/ping.c index d12c55dad7d1..746eeae7f581 100644 --- a/net/ipv6/ping.c +++ b/net/ipv6/ping.c @@ -121,7 +121,7 @@ static int ping_v6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) ipc6.tclass = np->tclass; fl6.flowlabel = ip6_make_flowinfo(ipc6.tclass, fl6.flowlabel); - dst = ip6_sk_dst_lookup_flow(sk, &fl6, daddr); + dst = ip6_sk_dst_lookup_flow(sk, &fl6, daddr, false); if (IS_ERR(dst)) return PTR_ERR(dst); rt = (struct rt6_info *) dst; diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 6861ed479469..fc13a1e2d789 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -1308,7 +1308,7 @@ do_udp_sendmsg: fl6.flowlabel = ip6_make_flowinfo(ipc6.tclass, fl6.flowlabel); - dst = ip6_sk_dst_lookup_flow(sk, &fl6, final_p); + dst = ip6_sk_dst_lookup_flow(sk, &fl6, final_p, false); if (IS_ERR(dst)) { err = PTR_ERR(dst); dst = NULL; -- cgit v1.2.3 From 9f542f616c24f7d85a4bde42ec621e8d87478fd8 Mon Sep 17 00:00:00 2001 From: Alexey Kodanev Date: Tue, 3 Apr 2018 15:00:09 +0300 Subject: ipv6: udp: convert 'connected' to bool type in udpv6_sendmsg() This should make it consistent with ip6_sk_dst_lookup_flow() that is accepting the new 'connected' parameter of type bool. Signed-off-by: Alexey Kodanev Signed-off-by: David S. Miller --- net/ipv6/udp.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index fc13a1e2d789..94861abbea60 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -1116,10 +1116,10 @@ int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) struct dst_entry *dst; struct ipcm6_cookie ipc6; int addr_len = msg->msg_namelen; + bool connected = false; int ulen = len; int corkreq = up->corkflag || msg->msg_flags&MSG_MORE; int err; - int connected = 0; int is_udplite = IS_UDPLITE(sk); int (*getfrag)(void *, char *, int, int, int, struct sk_buff *); struct sockcm_cookie sockc; @@ -1241,7 +1241,7 @@ do_udp_sendmsg: fl6.fl6_dport = inet->inet_dport; daddr = &sk->sk_v6_daddr; fl6.flowlabel = np->flow_label; - connected = 1; + connected = true; } if (!fl6.flowi6_oif) @@ -1271,7 +1271,7 @@ do_udp_sendmsg: } if (!(opt->opt_nflen|opt->opt_flen)) opt = NULL; - connected = 0; + connected = false; } if (!opt) { opt = txopt_get(np); @@ -1293,11 +1293,11 @@ do_udp_sendmsg: final_p = fl6_update_dst(&fl6, opt, &final); if (final_p) - connected = 0; + connected = false; if (!fl6.flowi6_oif && ipv6_addr_is_multicast(&fl6.daddr)) { fl6.flowi6_oif = np->mcast_oif; - connected = 0; + connected = false; } else if (!fl6.flowi6_oif) fl6.flowi6_oif = np->ucast_oif; -- cgit v1.2.3 From 4f858c56bdac801728b8983d7b542ae0e263e7e6 Mon Sep 17 00:00:00 2001 From: Alexey Kodanev Date: Tue, 3 Apr 2018 15:00:10 +0300 Subject: ipv6: udp: set dst cache for a connected sk if current not valid A new RTF_CACHE route can be created between ip6_sk_dst_lookup_flow() and ip6_dst_store() calls in udpv6_sendmsg(), when datagram sending results to ICMPV6_PKT_TOOBIG error: udp_v6_send_skb(), for example with vti6 tunnel: vti6_xmit(), get ICMPV6_PKT_TOOBIG error skb_dst_update_pmtu(), can create a RTF_CACHE clone icmpv6_send() ... udpv6_err() ip6_sk_update_pmtu() ip6_update_pmtu(), can create a RTF_CACHE clone ... ip6_datagram_dst_update() ip6_dst_store() And after commit 33c162a980fe ("ipv6: datagram: Update dst cache of a connected datagram sk during pmtu update"), the UDPv6 error handler can update socket's dst cache, but it can happen before the update in the end of udpv6_sendmsg(), preventing getting the new dst cache on the next udpv6_sendmsg() calls. In order to fix it, save dst in a connected socket only if the current socket's dst cache is invalid. The previous patch prepared ip6_sk_dst_lookup_flow() to do that with the new argument, and this patch enables it in udpv6_sendmsg(). Fixes: 33c162a980fe ("ipv6: datagram: Update dst cache of a connected datagram sk during pmtu update") Fixes: 45e4fd26683c ("ipv6: Only create RTF_CACHE routes after encountering pmtu exception") Signed-off-by: Alexey Kodanev Signed-off-by: David S. Miller --- net/ipv6/udp.c | 21 ++------------------- 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 94861abbea60..4ec76a87aeb8 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -1308,7 +1308,7 @@ do_udp_sendmsg: fl6.flowlabel = ip6_make_flowinfo(ipc6.tclass, fl6.flowlabel); - dst = ip6_sk_dst_lookup_flow(sk, &fl6, final_p, false); + dst = ip6_sk_dst_lookup_flow(sk, &fl6, final_p, connected); if (IS_ERR(dst)) { err = PTR_ERR(dst); dst = NULL; @@ -1333,7 +1333,7 @@ back_from_confirm: err = PTR_ERR(skb); if (!IS_ERR_OR_NULL(skb)) err = udp_v6_send_skb(skb, &fl6); - goto release_dst; + goto out; } lock_sock(sk); @@ -1367,23 +1367,6 @@ do_append_data: err = np->recverr ? net_xmit_errno(err) : 0; release_sock(sk); -release_dst: - if (dst) { - if (connected) { - ip6_dst_store(sk, dst, - ipv6_addr_equal(&fl6.daddr, &sk->sk_v6_daddr) ? - &sk->sk_v6_daddr : NULL, -#ifdef CONFIG_IPV6_SUBTREES - ipv6_addr_equal(&fl6.saddr, &np->saddr) ? - &np->saddr : -#endif - NULL); - } else { - dst_release(dst); - } - dst = NULL; - } - out: dst_release(dst); fl6_sock_release(flowlabel); -- cgit v1.2.3