18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * UDP over IPv6 48c2ecf20Sopenharmony_ci * Linux INET6 implementation 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Authors: 78c2ecf20Sopenharmony_ci * Pedro Roque <roque@di.fc.ul.pt> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Based on linux/ipv4/udp.c 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * Fixes: 128c2ecf20Sopenharmony_ci * Hideaki YOSHIFUJI : sin6_scope_id support 138c2ecf20Sopenharmony_ci * YOSHIFUJI Hideaki @USAGI and: Support IPV6_V6ONLY socket option, which 148c2ecf20Sopenharmony_ci * Alexey Kuznetsov allow both IPv4 and IPv6 sockets to bind 158c2ecf20Sopenharmony_ci * a single port at the same time. 168c2ecf20Sopenharmony_ci * Kazunori MIYAZAWA @USAGI: change process style to use ip6_append_data 178c2ecf20Sopenharmony_ci * YOSHIFUJI Hideaki @USAGI: convert /proc/net/udp6 to seq_file. 188c2ecf20Sopenharmony_ci */ 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include <linux/errno.h> 218c2ecf20Sopenharmony_ci#include <linux/types.h> 228c2ecf20Sopenharmony_ci#include <linux/socket.h> 238c2ecf20Sopenharmony_ci#include <linux/sockios.h> 248c2ecf20Sopenharmony_ci#include <linux/net.h> 258c2ecf20Sopenharmony_ci#include <linux/in6.h> 268c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 278c2ecf20Sopenharmony_ci#include <linux/if_arp.h> 288c2ecf20Sopenharmony_ci#include <linux/ipv6.h> 298c2ecf20Sopenharmony_ci#include <linux/icmpv6.h> 308c2ecf20Sopenharmony_ci#include <linux/init.h> 318c2ecf20Sopenharmony_ci#include <linux/module.h> 328c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 338c2ecf20Sopenharmony_ci#include <linux/slab.h> 348c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 358c2ecf20Sopenharmony_ci#include <linux/indirect_call_wrapper.h> 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#include <net/addrconf.h> 388c2ecf20Sopenharmony_ci#include <net/ndisc.h> 398c2ecf20Sopenharmony_ci#include <net/protocol.h> 408c2ecf20Sopenharmony_ci#include <net/transp_v6.h> 418c2ecf20Sopenharmony_ci#include <net/ip6_route.h> 428c2ecf20Sopenharmony_ci#include <net/raw.h> 438c2ecf20Sopenharmony_ci#include <net/tcp_states.h> 448c2ecf20Sopenharmony_ci#include <net/ip6_checksum.h> 458c2ecf20Sopenharmony_ci#include <net/ip6_tunnel.h> 468c2ecf20Sopenharmony_ci#include <net/xfrm.h> 478c2ecf20Sopenharmony_ci#include <net/inet_hashtables.h> 488c2ecf20Sopenharmony_ci#include <net/inet6_hashtables.h> 498c2ecf20Sopenharmony_ci#include <net/busy_poll.h> 508c2ecf20Sopenharmony_ci#include <net/sock_reuseport.h> 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci#include <linux/proc_fs.h> 538c2ecf20Sopenharmony_ci#include <linux/seq_file.h> 548c2ecf20Sopenharmony_ci#include <trace/events/skb.h> 558c2ecf20Sopenharmony_ci#include "udp_impl.h" 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic void udpv6_destruct_sock(struct sock *sk) 588c2ecf20Sopenharmony_ci{ 598c2ecf20Sopenharmony_ci udp_destruct_common(sk); 608c2ecf20Sopenharmony_ci inet6_sock_destruct(sk); 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ciint udpv6_init_sock(struct sock *sk) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci skb_queue_head_init(&udp_sk(sk)->reader_queue); 668c2ecf20Sopenharmony_ci sk->sk_destruct = udpv6_destruct_sock; 678c2ecf20Sopenharmony_ci return 0; 688c2ecf20Sopenharmony_ci} 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic u32 udp6_ehashfn(const struct net *net, 718c2ecf20Sopenharmony_ci const struct in6_addr *laddr, 728c2ecf20Sopenharmony_ci const u16 lport, 738c2ecf20Sopenharmony_ci const struct in6_addr *faddr, 748c2ecf20Sopenharmony_ci const __be16 fport) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci static u32 udp6_ehash_secret __read_mostly; 778c2ecf20Sopenharmony_ci static u32 udp_ipv6_hash_secret __read_mostly; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci u32 lhash, fhash; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci net_get_random_once(&udp6_ehash_secret, 828c2ecf20Sopenharmony_ci sizeof(udp6_ehash_secret)); 838c2ecf20Sopenharmony_ci net_get_random_once(&udp_ipv6_hash_secret, 848c2ecf20Sopenharmony_ci sizeof(udp_ipv6_hash_secret)); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci lhash = (__force u32)laddr->s6_addr32[3]; 878c2ecf20Sopenharmony_ci fhash = __ipv6_addr_jhash(faddr, udp_ipv6_hash_secret); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci return __inet6_ehashfn(lhash, lport, fhash, fport, 908c2ecf20Sopenharmony_ci udp6_ehash_secret + net_hash_mix(net)); 918c2ecf20Sopenharmony_ci} 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ciint udp_v6_get_port(struct sock *sk, unsigned short snum) 948c2ecf20Sopenharmony_ci{ 958c2ecf20Sopenharmony_ci unsigned int hash2_nulladdr = 968c2ecf20Sopenharmony_ci ipv6_portaddr_hash(sock_net(sk), &in6addr_any, snum); 978c2ecf20Sopenharmony_ci unsigned int hash2_partial = 988c2ecf20Sopenharmony_ci ipv6_portaddr_hash(sock_net(sk), &sk->sk_v6_rcv_saddr, 0); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci /* precompute partial secondary hash */ 1018c2ecf20Sopenharmony_ci udp_sk(sk)->udp_portaddr_hash = hash2_partial; 1028c2ecf20Sopenharmony_ci return udp_lib_get_port(sk, snum, hash2_nulladdr); 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_civoid udp_v6_rehash(struct sock *sk) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci u16 new_hash = ipv6_portaddr_hash(sock_net(sk), 1088c2ecf20Sopenharmony_ci &sk->sk_v6_rcv_saddr, 1098c2ecf20Sopenharmony_ci inet_sk(sk)->inet_num); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci udp_lib_rehash(sk, new_hash); 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistatic int compute_score(struct sock *sk, struct net *net, 1158c2ecf20Sopenharmony_ci const struct in6_addr *saddr, __be16 sport, 1168c2ecf20Sopenharmony_ci const struct in6_addr *daddr, unsigned short hnum, 1178c2ecf20Sopenharmony_ci int dif, int sdif) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci int score; 1208c2ecf20Sopenharmony_ci struct inet_sock *inet; 1218c2ecf20Sopenharmony_ci bool dev_match; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci if (!net_eq(sock_net(sk), net) || 1248c2ecf20Sopenharmony_ci udp_sk(sk)->udp_port_hash != hnum || 1258c2ecf20Sopenharmony_ci sk->sk_family != PF_INET6) 1268c2ecf20Sopenharmony_ci return -1; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci if (!ipv6_addr_equal(&sk->sk_v6_rcv_saddr, daddr)) 1298c2ecf20Sopenharmony_ci return -1; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci score = 0; 1328c2ecf20Sopenharmony_ci inet = inet_sk(sk); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci if (inet->inet_dport) { 1358c2ecf20Sopenharmony_ci if (inet->inet_dport != sport) 1368c2ecf20Sopenharmony_ci return -1; 1378c2ecf20Sopenharmony_ci score++; 1388c2ecf20Sopenharmony_ci } 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci if (!ipv6_addr_any(&sk->sk_v6_daddr)) { 1418c2ecf20Sopenharmony_ci if (!ipv6_addr_equal(&sk->sk_v6_daddr, saddr)) 1428c2ecf20Sopenharmony_ci return -1; 1438c2ecf20Sopenharmony_ci score++; 1448c2ecf20Sopenharmony_ci } 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci dev_match = udp_sk_bound_dev_eq(net, sk->sk_bound_dev_if, dif, sdif); 1478c2ecf20Sopenharmony_ci if (!dev_match) 1488c2ecf20Sopenharmony_ci return -1; 1498c2ecf20Sopenharmony_ci if (sk->sk_bound_dev_if) 1508c2ecf20Sopenharmony_ci score++; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci if (READ_ONCE(sk->sk_incoming_cpu) == raw_smp_processor_id()) 1538c2ecf20Sopenharmony_ci score++; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci return score; 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic struct sock *lookup_reuseport(struct net *net, struct sock *sk, 1598c2ecf20Sopenharmony_ci struct sk_buff *skb, 1608c2ecf20Sopenharmony_ci const struct in6_addr *saddr, 1618c2ecf20Sopenharmony_ci __be16 sport, 1628c2ecf20Sopenharmony_ci const struct in6_addr *daddr, 1638c2ecf20Sopenharmony_ci unsigned int hnum) 1648c2ecf20Sopenharmony_ci{ 1658c2ecf20Sopenharmony_ci struct sock *reuse_sk = NULL; 1668c2ecf20Sopenharmony_ci u32 hash; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci if (sk->sk_reuseport && sk->sk_state != TCP_ESTABLISHED) { 1698c2ecf20Sopenharmony_ci hash = udp6_ehashfn(net, daddr, hnum, saddr, sport); 1708c2ecf20Sopenharmony_ci reuse_sk = reuseport_select_sock(sk, hash, skb, 1718c2ecf20Sopenharmony_ci sizeof(struct udphdr)); 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci return reuse_sk; 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci/* called with rcu_read_lock() */ 1778c2ecf20Sopenharmony_cistatic struct sock *udp6_lib_lookup2(struct net *net, 1788c2ecf20Sopenharmony_ci const struct in6_addr *saddr, __be16 sport, 1798c2ecf20Sopenharmony_ci const struct in6_addr *daddr, unsigned int hnum, 1808c2ecf20Sopenharmony_ci int dif, int sdif, struct udp_hslot *hslot2, 1818c2ecf20Sopenharmony_ci struct sk_buff *skb) 1828c2ecf20Sopenharmony_ci{ 1838c2ecf20Sopenharmony_ci struct sock *sk, *result; 1848c2ecf20Sopenharmony_ci int score, badness; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci result = NULL; 1878c2ecf20Sopenharmony_ci badness = -1; 1888c2ecf20Sopenharmony_ci udp_portaddr_for_each_entry_rcu(sk, &hslot2->head) { 1898c2ecf20Sopenharmony_ci score = compute_score(sk, net, saddr, sport, 1908c2ecf20Sopenharmony_ci daddr, hnum, dif, sdif); 1918c2ecf20Sopenharmony_ci if (score > badness) { 1928c2ecf20Sopenharmony_ci badness = score; 1938c2ecf20Sopenharmony_ci result = lookup_reuseport(net, sk, skb, saddr, sport, daddr, hnum); 1948c2ecf20Sopenharmony_ci if (!result) { 1958c2ecf20Sopenharmony_ci result = sk; 1968c2ecf20Sopenharmony_ci continue; 1978c2ecf20Sopenharmony_ci } 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci /* Fall back to scoring if group has connections */ 2008c2ecf20Sopenharmony_ci if (!reuseport_has_conns(sk)) 2018c2ecf20Sopenharmony_ci return result; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci /* Reuseport logic returned an error, keep original score. */ 2048c2ecf20Sopenharmony_ci if (IS_ERR(result)) 2058c2ecf20Sopenharmony_ci continue; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci badness = compute_score(sk, net, saddr, sport, 2088c2ecf20Sopenharmony_ci daddr, hnum, dif, sdif); 2098c2ecf20Sopenharmony_ci } 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci return result; 2128c2ecf20Sopenharmony_ci} 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_cistatic inline struct sock *udp6_lookup_run_bpf(struct net *net, 2158c2ecf20Sopenharmony_ci struct udp_table *udptable, 2168c2ecf20Sopenharmony_ci struct sk_buff *skb, 2178c2ecf20Sopenharmony_ci const struct in6_addr *saddr, 2188c2ecf20Sopenharmony_ci __be16 sport, 2198c2ecf20Sopenharmony_ci const struct in6_addr *daddr, 2208c2ecf20Sopenharmony_ci u16 hnum) 2218c2ecf20Sopenharmony_ci{ 2228c2ecf20Sopenharmony_ci struct sock *sk, *reuse_sk; 2238c2ecf20Sopenharmony_ci bool no_reuseport; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci if (udptable != &udp_table) 2268c2ecf20Sopenharmony_ci return NULL; /* only UDP is supported */ 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci no_reuseport = bpf_sk_lookup_run_v6(net, IPPROTO_UDP, 2298c2ecf20Sopenharmony_ci saddr, sport, daddr, hnum, &sk); 2308c2ecf20Sopenharmony_ci if (no_reuseport || IS_ERR_OR_NULL(sk)) 2318c2ecf20Sopenharmony_ci return sk; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci reuse_sk = lookup_reuseport(net, sk, skb, saddr, sport, daddr, hnum); 2348c2ecf20Sopenharmony_ci if (reuse_sk) 2358c2ecf20Sopenharmony_ci sk = reuse_sk; 2368c2ecf20Sopenharmony_ci return sk; 2378c2ecf20Sopenharmony_ci} 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci/* rcu_read_lock() must be held */ 2408c2ecf20Sopenharmony_cistruct sock *__udp6_lib_lookup(struct net *net, 2418c2ecf20Sopenharmony_ci const struct in6_addr *saddr, __be16 sport, 2428c2ecf20Sopenharmony_ci const struct in6_addr *daddr, __be16 dport, 2438c2ecf20Sopenharmony_ci int dif, int sdif, struct udp_table *udptable, 2448c2ecf20Sopenharmony_ci struct sk_buff *skb) 2458c2ecf20Sopenharmony_ci{ 2468c2ecf20Sopenharmony_ci unsigned short hnum = ntohs(dport); 2478c2ecf20Sopenharmony_ci unsigned int hash2, slot2; 2488c2ecf20Sopenharmony_ci struct udp_hslot *hslot2; 2498c2ecf20Sopenharmony_ci struct sock *result, *sk; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci hash2 = ipv6_portaddr_hash(net, daddr, hnum); 2528c2ecf20Sopenharmony_ci slot2 = hash2 & udptable->mask; 2538c2ecf20Sopenharmony_ci hslot2 = &udptable->hash2[slot2]; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci /* Lookup connected or non-wildcard sockets */ 2568c2ecf20Sopenharmony_ci result = udp6_lib_lookup2(net, saddr, sport, 2578c2ecf20Sopenharmony_ci daddr, hnum, dif, sdif, 2588c2ecf20Sopenharmony_ci hslot2, skb); 2598c2ecf20Sopenharmony_ci if (!IS_ERR_OR_NULL(result) && result->sk_state == TCP_ESTABLISHED) 2608c2ecf20Sopenharmony_ci goto done; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci /* Lookup redirect from BPF */ 2638c2ecf20Sopenharmony_ci if (static_branch_unlikely(&bpf_sk_lookup_enabled)) { 2648c2ecf20Sopenharmony_ci sk = udp6_lookup_run_bpf(net, udptable, skb, 2658c2ecf20Sopenharmony_ci saddr, sport, daddr, hnum); 2668c2ecf20Sopenharmony_ci if (sk) { 2678c2ecf20Sopenharmony_ci result = sk; 2688c2ecf20Sopenharmony_ci goto done; 2698c2ecf20Sopenharmony_ci } 2708c2ecf20Sopenharmony_ci } 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci /* Got non-wildcard socket or error on first lookup */ 2738c2ecf20Sopenharmony_ci if (result) 2748c2ecf20Sopenharmony_ci goto done; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci /* Lookup wildcard sockets */ 2778c2ecf20Sopenharmony_ci hash2 = ipv6_portaddr_hash(net, &in6addr_any, hnum); 2788c2ecf20Sopenharmony_ci slot2 = hash2 & udptable->mask; 2798c2ecf20Sopenharmony_ci hslot2 = &udptable->hash2[slot2]; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci result = udp6_lib_lookup2(net, saddr, sport, 2828c2ecf20Sopenharmony_ci &in6addr_any, hnum, dif, sdif, 2838c2ecf20Sopenharmony_ci hslot2, skb); 2848c2ecf20Sopenharmony_cidone: 2858c2ecf20Sopenharmony_ci if (IS_ERR(result)) 2868c2ecf20Sopenharmony_ci return NULL; 2878c2ecf20Sopenharmony_ci return result; 2888c2ecf20Sopenharmony_ci} 2898c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(__udp6_lib_lookup); 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_cistatic struct sock *__udp6_lib_lookup_skb(struct sk_buff *skb, 2928c2ecf20Sopenharmony_ci __be16 sport, __be16 dport, 2938c2ecf20Sopenharmony_ci struct udp_table *udptable) 2948c2ecf20Sopenharmony_ci{ 2958c2ecf20Sopenharmony_ci const struct ipv6hdr *iph = ipv6_hdr(skb); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci return __udp6_lib_lookup(dev_net(skb->dev), &iph->saddr, sport, 2988c2ecf20Sopenharmony_ci &iph->daddr, dport, inet6_iif(skb), 2998c2ecf20Sopenharmony_ci inet6_sdif(skb), udptable, skb); 3008c2ecf20Sopenharmony_ci} 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_cistruct sock *udp6_lib_lookup_skb(struct sk_buff *skb, 3038c2ecf20Sopenharmony_ci __be16 sport, __be16 dport) 3048c2ecf20Sopenharmony_ci{ 3058c2ecf20Sopenharmony_ci const struct ipv6hdr *iph = ipv6_hdr(skb); 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci return __udp6_lib_lookup(dev_net(skb->dev), &iph->saddr, sport, 3088c2ecf20Sopenharmony_ci &iph->daddr, dport, inet6_iif(skb), 3098c2ecf20Sopenharmony_ci inet6_sdif(skb), &udp_table, NULL); 3108c2ecf20Sopenharmony_ci} 3118c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(udp6_lib_lookup_skb); 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci/* Must be called under rcu_read_lock(). 3148c2ecf20Sopenharmony_ci * Does increment socket refcount. 3158c2ecf20Sopenharmony_ci */ 3168c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_NF_TPROXY_IPV6) || IS_ENABLED(CONFIG_NF_SOCKET_IPV6) 3178c2ecf20Sopenharmony_cistruct sock *udp6_lib_lookup(struct net *net, const struct in6_addr *saddr, __be16 sport, 3188c2ecf20Sopenharmony_ci const struct in6_addr *daddr, __be16 dport, int dif) 3198c2ecf20Sopenharmony_ci{ 3208c2ecf20Sopenharmony_ci struct sock *sk; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci sk = __udp6_lib_lookup(net, saddr, sport, daddr, dport, 3238c2ecf20Sopenharmony_ci dif, 0, &udp_table, NULL); 3248c2ecf20Sopenharmony_ci if (sk && !refcount_inc_not_zero(&sk->sk_refcnt)) 3258c2ecf20Sopenharmony_ci sk = NULL; 3268c2ecf20Sopenharmony_ci return sk; 3278c2ecf20Sopenharmony_ci} 3288c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(udp6_lib_lookup); 3298c2ecf20Sopenharmony_ci#endif 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci/* do not use the scratch area len for jumbogram: their length execeeds the 3328c2ecf20Sopenharmony_ci * scratch area space; note that the IP6CB flags is still in the first 3338c2ecf20Sopenharmony_ci * cacheline, so checking for jumbograms is cheap 3348c2ecf20Sopenharmony_ci */ 3358c2ecf20Sopenharmony_cistatic int udp6_skb_len(struct sk_buff *skb) 3368c2ecf20Sopenharmony_ci{ 3378c2ecf20Sopenharmony_ci return unlikely(inet6_is_jumbogram(skb)) ? skb->len : udp_skb_len(skb); 3388c2ecf20Sopenharmony_ci} 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci/* 3418c2ecf20Sopenharmony_ci * This should be easy, if there is something there we 3428c2ecf20Sopenharmony_ci * return it, otherwise we block. 3438c2ecf20Sopenharmony_ci */ 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ciint udpv6_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, 3468c2ecf20Sopenharmony_ci int noblock, int flags, int *addr_len) 3478c2ecf20Sopenharmony_ci{ 3488c2ecf20Sopenharmony_ci struct ipv6_pinfo *np = inet6_sk(sk); 3498c2ecf20Sopenharmony_ci struct inet_sock *inet = inet_sk(sk); 3508c2ecf20Sopenharmony_ci struct sk_buff *skb; 3518c2ecf20Sopenharmony_ci unsigned int ulen, copied; 3528c2ecf20Sopenharmony_ci int off, err, peeking = flags & MSG_PEEK; 3538c2ecf20Sopenharmony_ci int is_udplite = IS_UDPLITE(sk); 3548c2ecf20Sopenharmony_ci struct udp_mib __percpu *mib; 3558c2ecf20Sopenharmony_ci bool checksum_valid = false; 3568c2ecf20Sopenharmony_ci int is_udp4; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci if (flags & MSG_ERRQUEUE) 3598c2ecf20Sopenharmony_ci return ipv6_recv_error(sk, msg, len, addr_len); 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci if (np->rxpmtu && np->rxopt.bits.rxpmtu) 3628c2ecf20Sopenharmony_ci return ipv6_recv_rxpmtu(sk, msg, len, addr_len); 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_citry_again: 3658c2ecf20Sopenharmony_ci off = sk_peek_offset(sk, flags); 3668c2ecf20Sopenharmony_ci skb = __skb_recv_udp(sk, flags, noblock, &off, &err); 3678c2ecf20Sopenharmony_ci if (!skb) 3688c2ecf20Sopenharmony_ci return err; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci ulen = udp6_skb_len(skb); 3718c2ecf20Sopenharmony_ci copied = len; 3728c2ecf20Sopenharmony_ci if (copied > ulen - off) 3738c2ecf20Sopenharmony_ci copied = ulen - off; 3748c2ecf20Sopenharmony_ci else if (copied < ulen) 3758c2ecf20Sopenharmony_ci msg->msg_flags |= MSG_TRUNC; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci is_udp4 = (skb->protocol == htons(ETH_P_IP)); 3788c2ecf20Sopenharmony_ci mib = __UDPX_MIB(sk, is_udp4); 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci /* 3818c2ecf20Sopenharmony_ci * If checksum is needed at all, try to do it while copying the 3828c2ecf20Sopenharmony_ci * data. If the data is truncated, or if we only want a partial 3838c2ecf20Sopenharmony_ci * coverage checksum (UDP-Lite), do it before the copy. 3848c2ecf20Sopenharmony_ci */ 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci if (copied < ulen || peeking || 3878c2ecf20Sopenharmony_ci (is_udplite && UDP_SKB_CB(skb)->partial_cov)) { 3888c2ecf20Sopenharmony_ci checksum_valid = udp_skb_csum_unnecessary(skb) || 3898c2ecf20Sopenharmony_ci !__udp_lib_checksum_complete(skb); 3908c2ecf20Sopenharmony_ci if (!checksum_valid) 3918c2ecf20Sopenharmony_ci goto csum_copy_err; 3928c2ecf20Sopenharmony_ci } 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci if (checksum_valid || udp_skb_csum_unnecessary(skb)) { 3958c2ecf20Sopenharmony_ci if (udp_skb_is_linear(skb)) 3968c2ecf20Sopenharmony_ci err = copy_linear_skb(skb, copied, off, &msg->msg_iter); 3978c2ecf20Sopenharmony_ci else 3988c2ecf20Sopenharmony_ci err = skb_copy_datagram_msg(skb, off, msg, copied); 3998c2ecf20Sopenharmony_ci } else { 4008c2ecf20Sopenharmony_ci err = skb_copy_and_csum_datagram_msg(skb, off, msg); 4018c2ecf20Sopenharmony_ci if (err == -EINVAL) 4028c2ecf20Sopenharmony_ci goto csum_copy_err; 4038c2ecf20Sopenharmony_ci } 4048c2ecf20Sopenharmony_ci if (unlikely(err)) { 4058c2ecf20Sopenharmony_ci if (!peeking) { 4068c2ecf20Sopenharmony_ci atomic_inc(&sk->sk_drops); 4078c2ecf20Sopenharmony_ci SNMP_INC_STATS(mib, UDP_MIB_INERRORS); 4088c2ecf20Sopenharmony_ci } 4098c2ecf20Sopenharmony_ci kfree_skb(skb); 4108c2ecf20Sopenharmony_ci return err; 4118c2ecf20Sopenharmony_ci } 4128c2ecf20Sopenharmony_ci if (!peeking) 4138c2ecf20Sopenharmony_ci SNMP_INC_STATS(mib, UDP_MIB_INDATAGRAMS); 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci sock_recv_ts_and_drops(msg, sk, skb); 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci /* Copy the address. */ 4188c2ecf20Sopenharmony_ci if (msg->msg_name) { 4198c2ecf20Sopenharmony_ci DECLARE_SOCKADDR(struct sockaddr_in6 *, sin6, msg->msg_name); 4208c2ecf20Sopenharmony_ci sin6->sin6_family = AF_INET6; 4218c2ecf20Sopenharmony_ci sin6->sin6_port = udp_hdr(skb)->source; 4228c2ecf20Sopenharmony_ci sin6->sin6_flowinfo = 0; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci if (is_udp4) { 4258c2ecf20Sopenharmony_ci ipv6_addr_set_v4mapped(ip_hdr(skb)->saddr, 4268c2ecf20Sopenharmony_ci &sin6->sin6_addr); 4278c2ecf20Sopenharmony_ci sin6->sin6_scope_id = 0; 4288c2ecf20Sopenharmony_ci } else { 4298c2ecf20Sopenharmony_ci sin6->sin6_addr = ipv6_hdr(skb)->saddr; 4308c2ecf20Sopenharmony_ci sin6->sin6_scope_id = 4318c2ecf20Sopenharmony_ci ipv6_iface_scope_id(&sin6->sin6_addr, 4328c2ecf20Sopenharmony_ci inet6_iif(skb)); 4338c2ecf20Sopenharmony_ci } 4348c2ecf20Sopenharmony_ci *addr_len = sizeof(*sin6); 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci if (cgroup_bpf_enabled) 4378c2ecf20Sopenharmony_ci BPF_CGROUP_RUN_PROG_UDP6_RECVMSG_LOCK(sk, 4388c2ecf20Sopenharmony_ci (struct sockaddr *)sin6); 4398c2ecf20Sopenharmony_ci } 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci if (udp_sk(sk)->gro_enabled) 4428c2ecf20Sopenharmony_ci udp_cmsg_recv(msg, sk, skb); 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci if (np->rxopt.all) 4458c2ecf20Sopenharmony_ci ip6_datagram_recv_common_ctl(sk, msg, skb); 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci if (is_udp4) { 4488c2ecf20Sopenharmony_ci if (inet->cmsg_flags) 4498c2ecf20Sopenharmony_ci ip_cmsg_recv_offset(msg, sk, skb, 4508c2ecf20Sopenharmony_ci sizeof(struct udphdr), off); 4518c2ecf20Sopenharmony_ci } else { 4528c2ecf20Sopenharmony_ci if (np->rxopt.all) 4538c2ecf20Sopenharmony_ci ip6_datagram_recv_specific_ctl(sk, msg, skb); 4548c2ecf20Sopenharmony_ci } 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci err = copied; 4578c2ecf20Sopenharmony_ci if (flags & MSG_TRUNC) 4588c2ecf20Sopenharmony_ci err = ulen; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci skb_consume_udp(sk, skb, peeking ? -err : err); 4618c2ecf20Sopenharmony_ci return err; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_cicsum_copy_err: 4648c2ecf20Sopenharmony_ci if (!__sk_queue_drop_skb(sk, &udp_sk(sk)->reader_queue, skb, flags, 4658c2ecf20Sopenharmony_ci udp_skb_destructor)) { 4668c2ecf20Sopenharmony_ci SNMP_INC_STATS(mib, UDP_MIB_CSUMERRORS); 4678c2ecf20Sopenharmony_ci SNMP_INC_STATS(mib, UDP_MIB_INERRORS); 4688c2ecf20Sopenharmony_ci } 4698c2ecf20Sopenharmony_ci kfree_skb(skb); 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci /* starting over for a new packet, but check if we need to yield */ 4728c2ecf20Sopenharmony_ci cond_resched(); 4738c2ecf20Sopenharmony_ci msg->msg_flags &= ~MSG_TRUNC; 4748c2ecf20Sopenharmony_ci goto try_again; 4758c2ecf20Sopenharmony_ci} 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ciDECLARE_STATIC_KEY_FALSE(udpv6_encap_needed_key); 4788c2ecf20Sopenharmony_civoid udpv6_encap_enable(void) 4798c2ecf20Sopenharmony_ci{ 4808c2ecf20Sopenharmony_ci static_branch_inc(&udpv6_encap_needed_key); 4818c2ecf20Sopenharmony_ci} 4828c2ecf20Sopenharmony_ciEXPORT_SYMBOL(udpv6_encap_enable); 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci/* Handler for tunnels with arbitrary destination ports: no socket lookup, go 4858c2ecf20Sopenharmony_ci * through error handlers in encapsulations looking for a match. 4868c2ecf20Sopenharmony_ci */ 4878c2ecf20Sopenharmony_cistatic int __udp6_lib_err_encap_no_sk(struct sk_buff *skb, 4888c2ecf20Sopenharmony_ci struct inet6_skb_parm *opt, 4898c2ecf20Sopenharmony_ci u8 type, u8 code, int offset, __be32 info) 4908c2ecf20Sopenharmony_ci{ 4918c2ecf20Sopenharmony_ci int i; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci for (i = 0; i < MAX_IPTUN_ENCAP_OPS; i++) { 4948c2ecf20Sopenharmony_ci int (*handler)(struct sk_buff *skb, struct inet6_skb_parm *opt, 4958c2ecf20Sopenharmony_ci u8 type, u8 code, int offset, __be32 info); 4968c2ecf20Sopenharmony_ci const struct ip6_tnl_encap_ops *encap; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci encap = rcu_dereference(ip6tun_encaps[i]); 4998c2ecf20Sopenharmony_ci if (!encap) 5008c2ecf20Sopenharmony_ci continue; 5018c2ecf20Sopenharmony_ci handler = encap->err_handler; 5028c2ecf20Sopenharmony_ci if (handler && !handler(skb, opt, type, code, offset, info)) 5038c2ecf20Sopenharmony_ci return 0; 5048c2ecf20Sopenharmony_ci } 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci return -ENOENT; 5078c2ecf20Sopenharmony_ci} 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci/* Try to match ICMP errors to UDP tunnels by looking up a socket without 5108c2ecf20Sopenharmony_ci * reversing source and destination port: this will match tunnels that force the 5118c2ecf20Sopenharmony_ci * same destination port on both endpoints (e.g. VXLAN, GENEVE). Note that 5128c2ecf20Sopenharmony_ci * lwtunnels might actually break this assumption by being configured with 5138c2ecf20Sopenharmony_ci * different destination ports on endpoints, in this case we won't be able to 5148c2ecf20Sopenharmony_ci * trace ICMP messages back to them. 5158c2ecf20Sopenharmony_ci * 5168c2ecf20Sopenharmony_ci * If this doesn't match any socket, probe tunnels with arbitrary destination 5178c2ecf20Sopenharmony_ci * ports (e.g. FoU, GUE): there, the receiving socket is useless, as the port 5188c2ecf20Sopenharmony_ci * we've sent packets to won't necessarily match the local destination port. 5198c2ecf20Sopenharmony_ci * 5208c2ecf20Sopenharmony_ci * Then ask the tunnel implementation to match the error against a valid 5218c2ecf20Sopenharmony_ci * association. 5228c2ecf20Sopenharmony_ci * 5238c2ecf20Sopenharmony_ci * Return an error if we can't find a match, the socket if we need further 5248c2ecf20Sopenharmony_ci * processing, zero otherwise. 5258c2ecf20Sopenharmony_ci */ 5268c2ecf20Sopenharmony_cistatic struct sock *__udp6_lib_err_encap(struct net *net, 5278c2ecf20Sopenharmony_ci const struct ipv6hdr *hdr, int offset, 5288c2ecf20Sopenharmony_ci struct udphdr *uh, 5298c2ecf20Sopenharmony_ci struct udp_table *udptable, 5308c2ecf20Sopenharmony_ci struct sk_buff *skb, 5318c2ecf20Sopenharmony_ci struct inet6_skb_parm *opt, 5328c2ecf20Sopenharmony_ci u8 type, u8 code, __be32 info) 5338c2ecf20Sopenharmony_ci{ 5348c2ecf20Sopenharmony_ci int network_offset, transport_offset; 5358c2ecf20Sopenharmony_ci struct sock *sk; 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci network_offset = skb_network_offset(skb); 5388c2ecf20Sopenharmony_ci transport_offset = skb_transport_offset(skb); 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci /* Network header needs to point to the outer IPv6 header inside ICMP */ 5418c2ecf20Sopenharmony_ci skb_reset_network_header(skb); 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci /* Transport header needs to point to the UDP header */ 5448c2ecf20Sopenharmony_ci skb_set_transport_header(skb, offset); 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci sk = __udp6_lib_lookup(net, &hdr->daddr, uh->source, 5478c2ecf20Sopenharmony_ci &hdr->saddr, uh->dest, 5488c2ecf20Sopenharmony_ci inet6_iif(skb), 0, udptable, skb); 5498c2ecf20Sopenharmony_ci if (sk) { 5508c2ecf20Sopenharmony_ci int (*lookup)(struct sock *sk, struct sk_buff *skb); 5518c2ecf20Sopenharmony_ci struct udp_sock *up = udp_sk(sk); 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci lookup = READ_ONCE(up->encap_err_lookup); 5548c2ecf20Sopenharmony_ci if (!lookup || lookup(sk, skb)) 5558c2ecf20Sopenharmony_ci sk = NULL; 5568c2ecf20Sopenharmony_ci } 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci if (!sk) { 5598c2ecf20Sopenharmony_ci sk = ERR_PTR(__udp6_lib_err_encap_no_sk(skb, opt, type, code, 5608c2ecf20Sopenharmony_ci offset, info)); 5618c2ecf20Sopenharmony_ci } 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci skb_set_transport_header(skb, transport_offset); 5648c2ecf20Sopenharmony_ci skb_set_network_header(skb, network_offset); 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci return sk; 5678c2ecf20Sopenharmony_ci} 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ciint __udp6_lib_err(struct sk_buff *skb, struct inet6_skb_parm *opt, 5708c2ecf20Sopenharmony_ci u8 type, u8 code, int offset, __be32 info, 5718c2ecf20Sopenharmony_ci struct udp_table *udptable) 5728c2ecf20Sopenharmony_ci{ 5738c2ecf20Sopenharmony_ci struct ipv6_pinfo *np; 5748c2ecf20Sopenharmony_ci const struct ipv6hdr *hdr = (const struct ipv6hdr *)skb->data; 5758c2ecf20Sopenharmony_ci const struct in6_addr *saddr = &hdr->saddr; 5768c2ecf20Sopenharmony_ci const struct in6_addr *daddr = &hdr->daddr; 5778c2ecf20Sopenharmony_ci struct udphdr *uh = (struct udphdr *)(skb->data+offset); 5788c2ecf20Sopenharmony_ci bool tunnel = false; 5798c2ecf20Sopenharmony_ci struct sock *sk; 5808c2ecf20Sopenharmony_ci int harderr; 5818c2ecf20Sopenharmony_ci int err; 5828c2ecf20Sopenharmony_ci struct net *net = dev_net(skb->dev); 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci sk = __udp6_lib_lookup(net, daddr, uh->dest, saddr, uh->source, 5858c2ecf20Sopenharmony_ci inet6_iif(skb), inet6_sdif(skb), udptable, NULL); 5868c2ecf20Sopenharmony_ci if (!sk) { 5878c2ecf20Sopenharmony_ci /* No socket for error: try tunnels before discarding */ 5888c2ecf20Sopenharmony_ci sk = ERR_PTR(-ENOENT); 5898c2ecf20Sopenharmony_ci if (static_branch_unlikely(&udpv6_encap_needed_key)) { 5908c2ecf20Sopenharmony_ci sk = __udp6_lib_err_encap(net, hdr, offset, uh, 5918c2ecf20Sopenharmony_ci udptable, skb, 5928c2ecf20Sopenharmony_ci opt, type, code, info); 5938c2ecf20Sopenharmony_ci if (!sk) 5948c2ecf20Sopenharmony_ci return 0; 5958c2ecf20Sopenharmony_ci } 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci if (IS_ERR(sk)) { 5988c2ecf20Sopenharmony_ci __ICMP6_INC_STATS(net, __in6_dev_get(skb->dev), 5998c2ecf20Sopenharmony_ci ICMP6_MIB_INERRORS); 6008c2ecf20Sopenharmony_ci return PTR_ERR(sk); 6018c2ecf20Sopenharmony_ci } 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci tunnel = true; 6048c2ecf20Sopenharmony_ci } 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci harderr = icmpv6_err_convert(type, code, &err); 6078c2ecf20Sopenharmony_ci np = inet6_sk(sk); 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci if (type == ICMPV6_PKT_TOOBIG) { 6108c2ecf20Sopenharmony_ci if (!ip6_sk_accept_pmtu(sk)) 6118c2ecf20Sopenharmony_ci goto out; 6128c2ecf20Sopenharmony_ci ip6_sk_update_pmtu(skb, sk, info); 6138c2ecf20Sopenharmony_ci if (np->pmtudisc != IPV6_PMTUDISC_DONT) 6148c2ecf20Sopenharmony_ci harderr = 1; 6158c2ecf20Sopenharmony_ci } 6168c2ecf20Sopenharmony_ci if (type == NDISC_REDIRECT) { 6178c2ecf20Sopenharmony_ci if (tunnel) { 6188c2ecf20Sopenharmony_ci ip6_redirect(skb, sock_net(sk), inet6_iif(skb), 6198c2ecf20Sopenharmony_ci sk->sk_mark, sk->sk_uid); 6208c2ecf20Sopenharmony_ci } else { 6218c2ecf20Sopenharmony_ci ip6_sk_redirect(skb, sk); 6228c2ecf20Sopenharmony_ci } 6238c2ecf20Sopenharmony_ci goto out; 6248c2ecf20Sopenharmony_ci } 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci /* Tunnels don't have an application socket: don't pass errors back */ 6278c2ecf20Sopenharmony_ci if (tunnel) 6288c2ecf20Sopenharmony_ci goto out; 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci if (!np->recverr) { 6318c2ecf20Sopenharmony_ci if (!harderr || sk->sk_state != TCP_ESTABLISHED) 6328c2ecf20Sopenharmony_ci goto out; 6338c2ecf20Sopenharmony_ci } else { 6348c2ecf20Sopenharmony_ci ipv6_icmp_error(sk, skb, err, uh->dest, ntohl(info), (u8 *)(uh+1)); 6358c2ecf20Sopenharmony_ci } 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci sk->sk_err = err; 6388c2ecf20Sopenharmony_ci sk->sk_error_report(sk); 6398c2ecf20Sopenharmony_ciout: 6408c2ecf20Sopenharmony_ci return 0; 6418c2ecf20Sopenharmony_ci} 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_cistatic int __udpv6_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) 6448c2ecf20Sopenharmony_ci{ 6458c2ecf20Sopenharmony_ci int rc; 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci if (!ipv6_addr_any(&sk->sk_v6_daddr)) { 6488c2ecf20Sopenharmony_ci sock_rps_save_rxhash(sk, skb); 6498c2ecf20Sopenharmony_ci sk_mark_napi_id(sk, skb); 6508c2ecf20Sopenharmony_ci sk_incoming_cpu_update(sk); 6518c2ecf20Sopenharmony_ci } else { 6528c2ecf20Sopenharmony_ci sk_mark_napi_id_once(sk, skb); 6538c2ecf20Sopenharmony_ci } 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci rc = __udp_enqueue_schedule_skb(sk, skb); 6568c2ecf20Sopenharmony_ci if (rc < 0) { 6578c2ecf20Sopenharmony_ci int is_udplite = IS_UDPLITE(sk); 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci /* Note that an ENOMEM error is charged twice */ 6608c2ecf20Sopenharmony_ci if (rc == -ENOMEM) 6618c2ecf20Sopenharmony_ci UDP6_INC_STATS(sock_net(sk), 6628c2ecf20Sopenharmony_ci UDP_MIB_RCVBUFERRORS, is_udplite); 6638c2ecf20Sopenharmony_ci UDP6_INC_STATS(sock_net(sk), UDP_MIB_INERRORS, is_udplite); 6648c2ecf20Sopenharmony_ci kfree_skb(skb); 6658c2ecf20Sopenharmony_ci return -1; 6668c2ecf20Sopenharmony_ci } 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci return 0; 6698c2ecf20Sopenharmony_ci} 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_cistatic __inline__ int udpv6_err(struct sk_buff *skb, 6728c2ecf20Sopenharmony_ci struct inet6_skb_parm *opt, u8 type, 6738c2ecf20Sopenharmony_ci u8 code, int offset, __be32 info) 6748c2ecf20Sopenharmony_ci{ 6758c2ecf20Sopenharmony_ci return __udp6_lib_err(skb, opt, type, code, offset, info, &udp_table); 6768c2ecf20Sopenharmony_ci} 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_cistatic int udpv6_queue_rcv_one_skb(struct sock *sk, struct sk_buff *skb) 6798c2ecf20Sopenharmony_ci{ 6808c2ecf20Sopenharmony_ci struct udp_sock *up = udp_sk(sk); 6818c2ecf20Sopenharmony_ci int is_udplite = IS_UDPLITE(sk); 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci if (!xfrm6_policy_check(sk, XFRM_POLICY_IN, skb)) 6848c2ecf20Sopenharmony_ci goto drop; 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci if (static_branch_unlikely(&udpv6_encap_needed_key) && up->encap_type) { 6878c2ecf20Sopenharmony_ci int (*encap_rcv)(struct sock *sk, struct sk_buff *skb); 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci /* 6908c2ecf20Sopenharmony_ci * This is an encapsulation socket so pass the skb to 6918c2ecf20Sopenharmony_ci * the socket's udp_encap_rcv() hook. Otherwise, just 6928c2ecf20Sopenharmony_ci * fall through and pass this up the UDP socket. 6938c2ecf20Sopenharmony_ci * up->encap_rcv() returns the following value: 6948c2ecf20Sopenharmony_ci * =0 if skb was successfully passed to the encap 6958c2ecf20Sopenharmony_ci * handler or was discarded by it. 6968c2ecf20Sopenharmony_ci * >0 if skb should be passed on to UDP. 6978c2ecf20Sopenharmony_ci * <0 if skb should be resubmitted as proto -N 6988c2ecf20Sopenharmony_ci */ 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci /* if we're overly short, let UDP handle it */ 7018c2ecf20Sopenharmony_ci encap_rcv = READ_ONCE(up->encap_rcv); 7028c2ecf20Sopenharmony_ci if (encap_rcv) { 7038c2ecf20Sopenharmony_ci int ret; 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci /* Verify checksum before giving to encap */ 7068c2ecf20Sopenharmony_ci if (udp_lib_checksum_complete(skb)) 7078c2ecf20Sopenharmony_ci goto csum_error; 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci ret = encap_rcv(sk, skb); 7108c2ecf20Sopenharmony_ci if (ret <= 0) { 7118c2ecf20Sopenharmony_ci __UDP_INC_STATS(sock_net(sk), 7128c2ecf20Sopenharmony_ci UDP_MIB_INDATAGRAMS, 7138c2ecf20Sopenharmony_ci is_udplite); 7148c2ecf20Sopenharmony_ci return -ret; 7158c2ecf20Sopenharmony_ci } 7168c2ecf20Sopenharmony_ci } 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci /* FALLTHROUGH -- it's a UDP Packet */ 7198c2ecf20Sopenharmony_ci } 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci /* 7228c2ecf20Sopenharmony_ci * UDP-Lite specific tests, ignored on UDP sockets (see net/ipv4/udp.c). 7238c2ecf20Sopenharmony_ci */ 7248c2ecf20Sopenharmony_ci if ((up->pcflag & UDPLITE_RECV_CC) && UDP_SKB_CB(skb)->partial_cov) { 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci if (up->pcrlen == 0) { /* full coverage was set */ 7278c2ecf20Sopenharmony_ci net_dbg_ratelimited("UDPLITE6: partial coverage %d while full coverage %d requested\n", 7288c2ecf20Sopenharmony_ci UDP_SKB_CB(skb)->cscov, skb->len); 7298c2ecf20Sopenharmony_ci goto drop; 7308c2ecf20Sopenharmony_ci } 7318c2ecf20Sopenharmony_ci if (UDP_SKB_CB(skb)->cscov < up->pcrlen) { 7328c2ecf20Sopenharmony_ci net_dbg_ratelimited("UDPLITE6: coverage %d too small, need min %d\n", 7338c2ecf20Sopenharmony_ci UDP_SKB_CB(skb)->cscov, up->pcrlen); 7348c2ecf20Sopenharmony_ci goto drop; 7358c2ecf20Sopenharmony_ci } 7368c2ecf20Sopenharmony_ci } 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci prefetch(&sk->sk_rmem_alloc); 7398c2ecf20Sopenharmony_ci if (rcu_access_pointer(sk->sk_filter) && 7408c2ecf20Sopenharmony_ci udp_lib_checksum_complete(skb)) 7418c2ecf20Sopenharmony_ci goto csum_error; 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci if (sk_filter_trim_cap(sk, skb, sizeof(struct udphdr))) 7448c2ecf20Sopenharmony_ci goto drop; 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci udp_csum_pull_header(skb); 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci skb_dst_drop(skb); 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci return __udpv6_queue_rcv_skb(sk, skb); 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_cicsum_error: 7538c2ecf20Sopenharmony_ci __UDP6_INC_STATS(sock_net(sk), UDP_MIB_CSUMERRORS, is_udplite); 7548c2ecf20Sopenharmony_cidrop: 7558c2ecf20Sopenharmony_ci __UDP6_INC_STATS(sock_net(sk), UDP_MIB_INERRORS, is_udplite); 7568c2ecf20Sopenharmony_ci atomic_inc(&sk->sk_drops); 7578c2ecf20Sopenharmony_ci kfree_skb(skb); 7588c2ecf20Sopenharmony_ci return -1; 7598c2ecf20Sopenharmony_ci} 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_cistatic int udpv6_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) 7628c2ecf20Sopenharmony_ci{ 7638c2ecf20Sopenharmony_ci struct sk_buff *next, *segs; 7648c2ecf20Sopenharmony_ci int ret; 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci if (likely(!udp_unexpected_gso(sk, skb))) 7678c2ecf20Sopenharmony_ci return udpv6_queue_rcv_one_skb(sk, skb); 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci __skb_push(skb, -skb_mac_offset(skb)); 7708c2ecf20Sopenharmony_ci segs = udp_rcv_segment(sk, skb, false); 7718c2ecf20Sopenharmony_ci skb_list_walk_safe(segs, skb, next) { 7728c2ecf20Sopenharmony_ci __skb_pull(skb, skb_transport_offset(skb)); 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci ret = udpv6_queue_rcv_one_skb(sk, skb); 7758c2ecf20Sopenharmony_ci if (ret > 0) 7768c2ecf20Sopenharmony_ci ip6_protocol_deliver_rcu(dev_net(skb->dev), skb, ret, 7778c2ecf20Sopenharmony_ci true); 7788c2ecf20Sopenharmony_ci } 7798c2ecf20Sopenharmony_ci return 0; 7808c2ecf20Sopenharmony_ci} 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_cistatic bool __udp_v6_is_mcast_sock(struct net *net, struct sock *sk, 7838c2ecf20Sopenharmony_ci __be16 loc_port, const struct in6_addr *loc_addr, 7848c2ecf20Sopenharmony_ci __be16 rmt_port, const struct in6_addr *rmt_addr, 7858c2ecf20Sopenharmony_ci int dif, int sdif, unsigned short hnum) 7868c2ecf20Sopenharmony_ci{ 7878c2ecf20Sopenharmony_ci struct inet_sock *inet = inet_sk(sk); 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci if (!net_eq(sock_net(sk), net)) 7908c2ecf20Sopenharmony_ci return false; 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci if (udp_sk(sk)->udp_port_hash != hnum || 7938c2ecf20Sopenharmony_ci sk->sk_family != PF_INET6 || 7948c2ecf20Sopenharmony_ci (inet->inet_dport && inet->inet_dport != rmt_port) || 7958c2ecf20Sopenharmony_ci (!ipv6_addr_any(&sk->sk_v6_daddr) && 7968c2ecf20Sopenharmony_ci !ipv6_addr_equal(&sk->sk_v6_daddr, rmt_addr)) || 7978c2ecf20Sopenharmony_ci !udp_sk_bound_dev_eq(net, sk->sk_bound_dev_if, dif, sdif) || 7988c2ecf20Sopenharmony_ci (!ipv6_addr_any(&sk->sk_v6_rcv_saddr) && 7998c2ecf20Sopenharmony_ci !ipv6_addr_equal(&sk->sk_v6_rcv_saddr, loc_addr))) 8008c2ecf20Sopenharmony_ci return false; 8018c2ecf20Sopenharmony_ci if (!inet6_mc_check(sk, loc_addr, rmt_addr)) 8028c2ecf20Sopenharmony_ci return false; 8038c2ecf20Sopenharmony_ci return true; 8048c2ecf20Sopenharmony_ci} 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_cistatic void udp6_csum_zero_error(struct sk_buff *skb) 8078c2ecf20Sopenharmony_ci{ 8088c2ecf20Sopenharmony_ci /* RFC 2460 section 8.1 says that we SHOULD log 8098c2ecf20Sopenharmony_ci * this error. Well, it is reasonable. 8108c2ecf20Sopenharmony_ci */ 8118c2ecf20Sopenharmony_ci net_dbg_ratelimited("IPv6: udp checksum is 0 for [%pI6c]:%u->[%pI6c]:%u\n", 8128c2ecf20Sopenharmony_ci &ipv6_hdr(skb)->saddr, ntohs(udp_hdr(skb)->source), 8138c2ecf20Sopenharmony_ci &ipv6_hdr(skb)->daddr, ntohs(udp_hdr(skb)->dest)); 8148c2ecf20Sopenharmony_ci} 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci/* 8178c2ecf20Sopenharmony_ci * Note: called only from the BH handler context, 8188c2ecf20Sopenharmony_ci * so we don't need to lock the hashes. 8198c2ecf20Sopenharmony_ci */ 8208c2ecf20Sopenharmony_cistatic int __udp6_lib_mcast_deliver(struct net *net, struct sk_buff *skb, 8218c2ecf20Sopenharmony_ci const struct in6_addr *saddr, const struct in6_addr *daddr, 8228c2ecf20Sopenharmony_ci struct udp_table *udptable, int proto) 8238c2ecf20Sopenharmony_ci{ 8248c2ecf20Sopenharmony_ci struct sock *sk, *first = NULL; 8258c2ecf20Sopenharmony_ci const struct udphdr *uh = udp_hdr(skb); 8268c2ecf20Sopenharmony_ci unsigned short hnum = ntohs(uh->dest); 8278c2ecf20Sopenharmony_ci struct udp_hslot *hslot = udp_hashslot(udptable, net, hnum); 8288c2ecf20Sopenharmony_ci unsigned int offset = offsetof(typeof(*sk), sk_node); 8298c2ecf20Sopenharmony_ci unsigned int hash2 = 0, hash2_any = 0, use_hash2 = (hslot->count > 10); 8308c2ecf20Sopenharmony_ci int dif = inet6_iif(skb); 8318c2ecf20Sopenharmony_ci int sdif = inet6_sdif(skb); 8328c2ecf20Sopenharmony_ci struct hlist_node *node; 8338c2ecf20Sopenharmony_ci struct sk_buff *nskb; 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci if (use_hash2) { 8368c2ecf20Sopenharmony_ci hash2_any = ipv6_portaddr_hash(net, &in6addr_any, hnum) & 8378c2ecf20Sopenharmony_ci udptable->mask; 8388c2ecf20Sopenharmony_ci hash2 = ipv6_portaddr_hash(net, daddr, hnum) & udptable->mask; 8398c2ecf20Sopenharmony_cistart_lookup: 8408c2ecf20Sopenharmony_ci hslot = &udptable->hash2[hash2]; 8418c2ecf20Sopenharmony_ci offset = offsetof(typeof(*sk), __sk_common.skc_portaddr_node); 8428c2ecf20Sopenharmony_ci } 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci sk_for_each_entry_offset_rcu(sk, node, &hslot->head, offset) { 8458c2ecf20Sopenharmony_ci if (!__udp_v6_is_mcast_sock(net, sk, uh->dest, daddr, 8468c2ecf20Sopenharmony_ci uh->source, saddr, dif, sdif, 8478c2ecf20Sopenharmony_ci hnum)) 8488c2ecf20Sopenharmony_ci continue; 8498c2ecf20Sopenharmony_ci /* If zero checksum and no_check is not on for 8508c2ecf20Sopenharmony_ci * the socket then skip it. 8518c2ecf20Sopenharmony_ci */ 8528c2ecf20Sopenharmony_ci if (!uh->check && !udp_sk(sk)->no_check6_rx) 8538c2ecf20Sopenharmony_ci continue; 8548c2ecf20Sopenharmony_ci if (!first) { 8558c2ecf20Sopenharmony_ci first = sk; 8568c2ecf20Sopenharmony_ci continue; 8578c2ecf20Sopenharmony_ci } 8588c2ecf20Sopenharmony_ci nskb = skb_clone(skb, GFP_ATOMIC); 8598c2ecf20Sopenharmony_ci if (unlikely(!nskb)) { 8608c2ecf20Sopenharmony_ci atomic_inc(&sk->sk_drops); 8618c2ecf20Sopenharmony_ci __UDP6_INC_STATS(net, UDP_MIB_RCVBUFERRORS, 8628c2ecf20Sopenharmony_ci IS_UDPLITE(sk)); 8638c2ecf20Sopenharmony_ci __UDP6_INC_STATS(net, UDP_MIB_INERRORS, 8648c2ecf20Sopenharmony_ci IS_UDPLITE(sk)); 8658c2ecf20Sopenharmony_ci continue; 8668c2ecf20Sopenharmony_ci } 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci if (udpv6_queue_rcv_skb(sk, nskb) > 0) 8698c2ecf20Sopenharmony_ci consume_skb(nskb); 8708c2ecf20Sopenharmony_ci } 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci /* Also lookup *:port if we are using hash2 and haven't done so yet. */ 8738c2ecf20Sopenharmony_ci if (use_hash2 && hash2 != hash2_any) { 8748c2ecf20Sopenharmony_ci hash2 = hash2_any; 8758c2ecf20Sopenharmony_ci goto start_lookup; 8768c2ecf20Sopenharmony_ci } 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_ci if (first) { 8798c2ecf20Sopenharmony_ci if (udpv6_queue_rcv_skb(first, skb) > 0) 8808c2ecf20Sopenharmony_ci consume_skb(skb); 8818c2ecf20Sopenharmony_ci } else { 8828c2ecf20Sopenharmony_ci kfree_skb(skb); 8838c2ecf20Sopenharmony_ci __UDP6_INC_STATS(net, UDP_MIB_IGNOREDMULTI, 8848c2ecf20Sopenharmony_ci proto == IPPROTO_UDPLITE); 8858c2ecf20Sopenharmony_ci } 8868c2ecf20Sopenharmony_ci return 0; 8878c2ecf20Sopenharmony_ci} 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_cistatic void udp6_sk_rx_dst_set(struct sock *sk, struct dst_entry *dst) 8908c2ecf20Sopenharmony_ci{ 8918c2ecf20Sopenharmony_ci if (udp_sk_rx_dst_set(sk, dst)) { 8928c2ecf20Sopenharmony_ci const struct rt6_info *rt = (const struct rt6_info *)dst; 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci inet6_sk(sk)->rx_dst_cookie = rt6_get_cookie(rt); 8958c2ecf20Sopenharmony_ci } 8968c2ecf20Sopenharmony_ci} 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_ci/* wrapper for udp_queue_rcv_skb tacking care of csum conversion and 8998c2ecf20Sopenharmony_ci * return code conversion for ip layer consumption 9008c2ecf20Sopenharmony_ci */ 9018c2ecf20Sopenharmony_cistatic int udp6_unicast_rcv_skb(struct sock *sk, struct sk_buff *skb, 9028c2ecf20Sopenharmony_ci struct udphdr *uh) 9038c2ecf20Sopenharmony_ci{ 9048c2ecf20Sopenharmony_ci int ret; 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ci if (inet_get_convert_csum(sk) && uh->check && !IS_UDPLITE(sk)) 9078c2ecf20Sopenharmony_ci skb_checksum_try_convert(skb, IPPROTO_UDP, ip6_compute_pseudo); 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci ret = udpv6_queue_rcv_skb(sk, skb); 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ci /* a return value > 0 means to resubmit the input */ 9128c2ecf20Sopenharmony_ci if (ret > 0) 9138c2ecf20Sopenharmony_ci return ret; 9148c2ecf20Sopenharmony_ci return 0; 9158c2ecf20Sopenharmony_ci} 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ciint __udp6_lib_rcv(struct sk_buff *skb, struct udp_table *udptable, 9188c2ecf20Sopenharmony_ci int proto) 9198c2ecf20Sopenharmony_ci{ 9208c2ecf20Sopenharmony_ci const struct in6_addr *saddr, *daddr; 9218c2ecf20Sopenharmony_ci struct net *net = dev_net(skb->dev); 9228c2ecf20Sopenharmony_ci struct udphdr *uh; 9238c2ecf20Sopenharmony_ci struct sock *sk; 9248c2ecf20Sopenharmony_ci bool refcounted; 9258c2ecf20Sopenharmony_ci u32 ulen = 0; 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci if (!pskb_may_pull(skb, sizeof(struct udphdr))) 9288c2ecf20Sopenharmony_ci goto discard; 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_ci saddr = &ipv6_hdr(skb)->saddr; 9318c2ecf20Sopenharmony_ci daddr = &ipv6_hdr(skb)->daddr; 9328c2ecf20Sopenharmony_ci uh = udp_hdr(skb); 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ci ulen = ntohs(uh->len); 9358c2ecf20Sopenharmony_ci if (ulen > skb->len) 9368c2ecf20Sopenharmony_ci goto short_packet; 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_ci if (proto == IPPROTO_UDP) { 9398c2ecf20Sopenharmony_ci /* UDP validates ulen. */ 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ci /* Check for jumbo payload */ 9428c2ecf20Sopenharmony_ci if (ulen == 0) 9438c2ecf20Sopenharmony_ci ulen = skb->len; 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_ci if (ulen < sizeof(*uh)) 9468c2ecf20Sopenharmony_ci goto short_packet; 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci if (ulen < skb->len) { 9498c2ecf20Sopenharmony_ci if (pskb_trim_rcsum(skb, ulen)) 9508c2ecf20Sopenharmony_ci goto short_packet; 9518c2ecf20Sopenharmony_ci saddr = &ipv6_hdr(skb)->saddr; 9528c2ecf20Sopenharmony_ci daddr = &ipv6_hdr(skb)->daddr; 9538c2ecf20Sopenharmony_ci uh = udp_hdr(skb); 9548c2ecf20Sopenharmony_ci } 9558c2ecf20Sopenharmony_ci } 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci if (udp6_csum_init(skb, uh, proto)) 9588c2ecf20Sopenharmony_ci goto csum_error; 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci /* Check if the socket is already available, e.g. due to early demux */ 9618c2ecf20Sopenharmony_ci sk = skb_steal_sock(skb, &refcounted); 9628c2ecf20Sopenharmony_ci if (sk) { 9638c2ecf20Sopenharmony_ci struct dst_entry *dst = skb_dst(skb); 9648c2ecf20Sopenharmony_ci int ret; 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_ci if (unlikely(rcu_dereference(sk->sk_rx_dst) != dst)) 9678c2ecf20Sopenharmony_ci udp6_sk_rx_dst_set(sk, dst); 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_ci if (!uh->check && !udp_sk(sk)->no_check6_rx) { 9708c2ecf20Sopenharmony_ci if (refcounted) 9718c2ecf20Sopenharmony_ci sock_put(sk); 9728c2ecf20Sopenharmony_ci goto report_csum_error; 9738c2ecf20Sopenharmony_ci } 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci ret = udp6_unicast_rcv_skb(sk, skb, uh); 9768c2ecf20Sopenharmony_ci if (refcounted) 9778c2ecf20Sopenharmony_ci sock_put(sk); 9788c2ecf20Sopenharmony_ci return ret; 9798c2ecf20Sopenharmony_ci } 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ci /* 9828c2ecf20Sopenharmony_ci * Multicast receive code 9838c2ecf20Sopenharmony_ci */ 9848c2ecf20Sopenharmony_ci if (ipv6_addr_is_multicast(daddr)) 9858c2ecf20Sopenharmony_ci return __udp6_lib_mcast_deliver(net, skb, 9868c2ecf20Sopenharmony_ci saddr, daddr, udptable, proto); 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_ci /* Unicast */ 9898c2ecf20Sopenharmony_ci sk = __udp6_lib_lookup_skb(skb, uh->source, uh->dest, udptable); 9908c2ecf20Sopenharmony_ci if (sk) { 9918c2ecf20Sopenharmony_ci if (!uh->check && !udp_sk(sk)->no_check6_rx) 9928c2ecf20Sopenharmony_ci goto report_csum_error; 9938c2ecf20Sopenharmony_ci return udp6_unicast_rcv_skb(sk, skb, uh); 9948c2ecf20Sopenharmony_ci } 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_ci if (!uh->check) 9978c2ecf20Sopenharmony_ci goto report_csum_error; 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci if (!xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb)) 10008c2ecf20Sopenharmony_ci goto discard; 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_ci if (udp_lib_checksum_complete(skb)) 10038c2ecf20Sopenharmony_ci goto csum_error; 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ci __UDP6_INC_STATS(net, UDP_MIB_NOPORTS, proto == IPPROTO_UDPLITE); 10068c2ecf20Sopenharmony_ci icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0); 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_ci kfree_skb(skb); 10098c2ecf20Sopenharmony_ci return 0; 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_cishort_packet: 10128c2ecf20Sopenharmony_ci net_dbg_ratelimited("UDP%sv6: short packet: From [%pI6c]:%u %d/%d to [%pI6c]:%u\n", 10138c2ecf20Sopenharmony_ci proto == IPPROTO_UDPLITE ? "-Lite" : "", 10148c2ecf20Sopenharmony_ci saddr, ntohs(uh->source), 10158c2ecf20Sopenharmony_ci ulen, skb->len, 10168c2ecf20Sopenharmony_ci daddr, ntohs(uh->dest)); 10178c2ecf20Sopenharmony_ci goto discard; 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_cireport_csum_error: 10208c2ecf20Sopenharmony_ci udp6_csum_zero_error(skb); 10218c2ecf20Sopenharmony_cicsum_error: 10228c2ecf20Sopenharmony_ci __UDP6_INC_STATS(net, UDP_MIB_CSUMERRORS, proto == IPPROTO_UDPLITE); 10238c2ecf20Sopenharmony_cidiscard: 10248c2ecf20Sopenharmony_ci __UDP6_INC_STATS(net, UDP_MIB_INERRORS, proto == IPPROTO_UDPLITE); 10258c2ecf20Sopenharmony_ci kfree_skb(skb); 10268c2ecf20Sopenharmony_ci return 0; 10278c2ecf20Sopenharmony_ci} 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_cistatic struct sock *__udp6_lib_demux_lookup(struct net *net, 10318c2ecf20Sopenharmony_ci __be16 loc_port, const struct in6_addr *loc_addr, 10328c2ecf20Sopenharmony_ci __be16 rmt_port, const struct in6_addr *rmt_addr, 10338c2ecf20Sopenharmony_ci int dif, int sdif) 10348c2ecf20Sopenharmony_ci{ 10358c2ecf20Sopenharmony_ci unsigned short hnum = ntohs(loc_port); 10368c2ecf20Sopenharmony_ci unsigned int hash2 = ipv6_portaddr_hash(net, loc_addr, hnum); 10378c2ecf20Sopenharmony_ci unsigned int slot2 = hash2 & udp_table.mask; 10388c2ecf20Sopenharmony_ci struct udp_hslot *hslot2 = &udp_table.hash2[slot2]; 10398c2ecf20Sopenharmony_ci const __portpair ports = INET_COMBINED_PORTS(rmt_port, hnum); 10408c2ecf20Sopenharmony_ci struct sock *sk; 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_ci udp_portaddr_for_each_entry_rcu(sk, &hslot2->head) { 10438c2ecf20Sopenharmony_ci if (sk->sk_state == TCP_ESTABLISHED && 10448c2ecf20Sopenharmony_ci inet6_match(net, sk, rmt_addr, loc_addr, ports, dif, sdif)) 10458c2ecf20Sopenharmony_ci return sk; 10468c2ecf20Sopenharmony_ci /* Only check first socket in chain */ 10478c2ecf20Sopenharmony_ci break; 10488c2ecf20Sopenharmony_ci } 10498c2ecf20Sopenharmony_ci return NULL; 10508c2ecf20Sopenharmony_ci} 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_civoid udp_v6_early_demux(struct sk_buff *skb) 10538c2ecf20Sopenharmony_ci{ 10548c2ecf20Sopenharmony_ci struct net *net = dev_net(skb->dev); 10558c2ecf20Sopenharmony_ci const struct udphdr *uh; 10568c2ecf20Sopenharmony_ci struct sock *sk; 10578c2ecf20Sopenharmony_ci struct dst_entry *dst; 10588c2ecf20Sopenharmony_ci int dif = skb->dev->ifindex; 10598c2ecf20Sopenharmony_ci int sdif = inet6_sdif(skb); 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_ci if (!pskb_may_pull(skb, skb_transport_offset(skb) + 10628c2ecf20Sopenharmony_ci sizeof(struct udphdr))) 10638c2ecf20Sopenharmony_ci return; 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_ci uh = udp_hdr(skb); 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci if (skb->pkt_type == PACKET_HOST) 10688c2ecf20Sopenharmony_ci sk = __udp6_lib_demux_lookup(net, uh->dest, 10698c2ecf20Sopenharmony_ci &ipv6_hdr(skb)->daddr, 10708c2ecf20Sopenharmony_ci uh->source, &ipv6_hdr(skb)->saddr, 10718c2ecf20Sopenharmony_ci dif, sdif); 10728c2ecf20Sopenharmony_ci else 10738c2ecf20Sopenharmony_ci return; 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_ci if (!sk || !refcount_inc_not_zero(&sk->sk_refcnt)) 10768c2ecf20Sopenharmony_ci return; 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_ci skb->sk = sk; 10798c2ecf20Sopenharmony_ci skb->destructor = sock_efree; 10808c2ecf20Sopenharmony_ci dst = rcu_dereference(sk->sk_rx_dst); 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_ci if (dst) 10838c2ecf20Sopenharmony_ci dst = dst_check(dst, inet6_sk(sk)->rx_dst_cookie); 10848c2ecf20Sopenharmony_ci if (dst) { 10858c2ecf20Sopenharmony_ci /* set noref for now. 10868c2ecf20Sopenharmony_ci * any place which wants to hold dst has to call 10878c2ecf20Sopenharmony_ci * dst_hold_safe() 10888c2ecf20Sopenharmony_ci */ 10898c2ecf20Sopenharmony_ci skb_dst_set_noref(skb, dst); 10908c2ecf20Sopenharmony_ci } 10918c2ecf20Sopenharmony_ci} 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_ciINDIRECT_CALLABLE_SCOPE int udpv6_rcv(struct sk_buff *skb) 10948c2ecf20Sopenharmony_ci{ 10958c2ecf20Sopenharmony_ci return __udp6_lib_rcv(skb, &udp_table, IPPROTO_UDP); 10968c2ecf20Sopenharmony_ci} 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_ci/* 10998c2ecf20Sopenharmony_ci * Throw away all pending data and cancel the corking. Socket is locked. 11008c2ecf20Sopenharmony_ci */ 11018c2ecf20Sopenharmony_cistatic void udp_v6_flush_pending_frames(struct sock *sk) 11028c2ecf20Sopenharmony_ci{ 11038c2ecf20Sopenharmony_ci struct udp_sock *up = udp_sk(sk); 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci if (up->pending == AF_INET) 11068c2ecf20Sopenharmony_ci udp_flush_pending_frames(sk); 11078c2ecf20Sopenharmony_ci else if (up->pending) { 11088c2ecf20Sopenharmony_ci up->len = 0; 11098c2ecf20Sopenharmony_ci up->pending = 0; 11108c2ecf20Sopenharmony_ci ip6_flush_pending_frames(sk); 11118c2ecf20Sopenharmony_ci } 11128c2ecf20Sopenharmony_ci} 11138c2ecf20Sopenharmony_ci 11148c2ecf20Sopenharmony_cistatic int udpv6_pre_connect(struct sock *sk, struct sockaddr *uaddr, 11158c2ecf20Sopenharmony_ci int addr_len) 11168c2ecf20Sopenharmony_ci{ 11178c2ecf20Sopenharmony_ci if (addr_len < offsetofend(struct sockaddr, sa_family)) 11188c2ecf20Sopenharmony_ci return -EINVAL; 11198c2ecf20Sopenharmony_ci /* The following checks are replicated from __ip6_datagram_connect() 11208c2ecf20Sopenharmony_ci * and intended to prevent BPF program called below from accessing 11218c2ecf20Sopenharmony_ci * bytes that are out of the bound specified by user in addr_len. 11228c2ecf20Sopenharmony_ci */ 11238c2ecf20Sopenharmony_ci if (uaddr->sa_family == AF_INET) { 11248c2ecf20Sopenharmony_ci if (__ipv6_only_sock(sk)) 11258c2ecf20Sopenharmony_ci return -EAFNOSUPPORT; 11268c2ecf20Sopenharmony_ci return udp_pre_connect(sk, uaddr, addr_len); 11278c2ecf20Sopenharmony_ci } 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_ci if (addr_len < SIN6_LEN_RFC2133) 11308c2ecf20Sopenharmony_ci return -EINVAL; 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_ci return BPF_CGROUP_RUN_PROG_INET6_CONNECT_LOCK(sk, uaddr); 11338c2ecf20Sopenharmony_ci} 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ci/** 11368c2ecf20Sopenharmony_ci * udp6_hwcsum_outgoing - handle outgoing HW checksumming 11378c2ecf20Sopenharmony_ci * @sk: socket we are sending on 11388c2ecf20Sopenharmony_ci * @skb: sk_buff containing the filled-in UDP header 11398c2ecf20Sopenharmony_ci * (checksum field must be zeroed out) 11408c2ecf20Sopenharmony_ci * @saddr: source address 11418c2ecf20Sopenharmony_ci * @daddr: destination address 11428c2ecf20Sopenharmony_ci * @len: length of packet 11438c2ecf20Sopenharmony_ci */ 11448c2ecf20Sopenharmony_cistatic void udp6_hwcsum_outgoing(struct sock *sk, struct sk_buff *skb, 11458c2ecf20Sopenharmony_ci const struct in6_addr *saddr, 11468c2ecf20Sopenharmony_ci const struct in6_addr *daddr, int len) 11478c2ecf20Sopenharmony_ci{ 11488c2ecf20Sopenharmony_ci unsigned int offset; 11498c2ecf20Sopenharmony_ci struct udphdr *uh = udp_hdr(skb); 11508c2ecf20Sopenharmony_ci struct sk_buff *frags = skb_shinfo(skb)->frag_list; 11518c2ecf20Sopenharmony_ci __wsum csum = 0; 11528c2ecf20Sopenharmony_ci 11538c2ecf20Sopenharmony_ci if (!frags) { 11548c2ecf20Sopenharmony_ci /* Only one fragment on the socket. */ 11558c2ecf20Sopenharmony_ci skb->csum_start = skb_transport_header(skb) - skb->head; 11568c2ecf20Sopenharmony_ci skb->csum_offset = offsetof(struct udphdr, check); 11578c2ecf20Sopenharmony_ci uh->check = ~csum_ipv6_magic(saddr, daddr, len, IPPROTO_UDP, 0); 11588c2ecf20Sopenharmony_ci } else { 11598c2ecf20Sopenharmony_ci /* 11608c2ecf20Sopenharmony_ci * HW-checksum won't work as there are two or more 11618c2ecf20Sopenharmony_ci * fragments on the socket so that all csums of sk_buffs 11628c2ecf20Sopenharmony_ci * should be together 11638c2ecf20Sopenharmony_ci */ 11648c2ecf20Sopenharmony_ci offset = skb_transport_offset(skb); 11658c2ecf20Sopenharmony_ci skb->csum = skb_checksum(skb, offset, skb->len - offset, 0); 11668c2ecf20Sopenharmony_ci csum = skb->csum; 11678c2ecf20Sopenharmony_ci 11688c2ecf20Sopenharmony_ci skb->ip_summed = CHECKSUM_NONE; 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_ci do { 11718c2ecf20Sopenharmony_ci csum = csum_add(csum, frags->csum); 11728c2ecf20Sopenharmony_ci } while ((frags = frags->next)); 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci uh->check = csum_ipv6_magic(saddr, daddr, len, IPPROTO_UDP, 11758c2ecf20Sopenharmony_ci csum); 11768c2ecf20Sopenharmony_ci if (uh->check == 0) 11778c2ecf20Sopenharmony_ci uh->check = CSUM_MANGLED_0; 11788c2ecf20Sopenharmony_ci } 11798c2ecf20Sopenharmony_ci} 11808c2ecf20Sopenharmony_ci 11818c2ecf20Sopenharmony_ci/* 11828c2ecf20Sopenharmony_ci * Sending 11838c2ecf20Sopenharmony_ci */ 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_cistatic int udp_v6_send_skb(struct sk_buff *skb, struct flowi6 *fl6, 11868c2ecf20Sopenharmony_ci struct inet_cork *cork) 11878c2ecf20Sopenharmony_ci{ 11888c2ecf20Sopenharmony_ci struct sock *sk = skb->sk; 11898c2ecf20Sopenharmony_ci struct udphdr *uh; 11908c2ecf20Sopenharmony_ci int err = 0; 11918c2ecf20Sopenharmony_ci int is_udplite = IS_UDPLITE(sk); 11928c2ecf20Sopenharmony_ci __wsum csum = 0; 11938c2ecf20Sopenharmony_ci int offset = skb_transport_offset(skb); 11948c2ecf20Sopenharmony_ci int len = skb->len - offset; 11958c2ecf20Sopenharmony_ci int datalen = len - sizeof(*uh); 11968c2ecf20Sopenharmony_ci 11978c2ecf20Sopenharmony_ci /* 11988c2ecf20Sopenharmony_ci * Create a UDP header 11998c2ecf20Sopenharmony_ci */ 12008c2ecf20Sopenharmony_ci uh = udp_hdr(skb); 12018c2ecf20Sopenharmony_ci uh->source = fl6->fl6_sport; 12028c2ecf20Sopenharmony_ci uh->dest = fl6->fl6_dport; 12038c2ecf20Sopenharmony_ci uh->len = htons(len); 12048c2ecf20Sopenharmony_ci uh->check = 0; 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_ci if (cork->gso_size) { 12078c2ecf20Sopenharmony_ci const int hlen = skb_network_header_len(skb) + 12088c2ecf20Sopenharmony_ci sizeof(struct udphdr); 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_ci if (hlen + cork->gso_size > cork->fragsize) { 12118c2ecf20Sopenharmony_ci kfree_skb(skb); 12128c2ecf20Sopenharmony_ci return -EINVAL; 12138c2ecf20Sopenharmony_ci } 12148c2ecf20Sopenharmony_ci if (datalen > cork->gso_size * UDP_MAX_SEGMENTS) { 12158c2ecf20Sopenharmony_ci kfree_skb(skb); 12168c2ecf20Sopenharmony_ci return -EINVAL; 12178c2ecf20Sopenharmony_ci } 12188c2ecf20Sopenharmony_ci if (udp_sk(sk)->no_check6_tx) { 12198c2ecf20Sopenharmony_ci kfree_skb(skb); 12208c2ecf20Sopenharmony_ci return -EINVAL; 12218c2ecf20Sopenharmony_ci } 12228c2ecf20Sopenharmony_ci if (skb->ip_summed != CHECKSUM_PARTIAL || is_udplite || 12238c2ecf20Sopenharmony_ci dst_xfrm(skb_dst(skb))) { 12248c2ecf20Sopenharmony_ci kfree_skb(skb); 12258c2ecf20Sopenharmony_ci return -EIO; 12268c2ecf20Sopenharmony_ci } 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_ci if (datalen > cork->gso_size) { 12298c2ecf20Sopenharmony_ci skb_shinfo(skb)->gso_size = cork->gso_size; 12308c2ecf20Sopenharmony_ci skb_shinfo(skb)->gso_type = SKB_GSO_UDP_L4; 12318c2ecf20Sopenharmony_ci skb_shinfo(skb)->gso_segs = DIV_ROUND_UP(datalen, 12328c2ecf20Sopenharmony_ci cork->gso_size); 12338c2ecf20Sopenharmony_ci } 12348c2ecf20Sopenharmony_ci goto csum_partial; 12358c2ecf20Sopenharmony_ci } 12368c2ecf20Sopenharmony_ci 12378c2ecf20Sopenharmony_ci if (is_udplite) 12388c2ecf20Sopenharmony_ci csum = udplite_csum(skb); 12398c2ecf20Sopenharmony_ci else if (udp_sk(sk)->no_check6_tx) { /* UDP csum disabled */ 12408c2ecf20Sopenharmony_ci skb->ip_summed = CHECKSUM_NONE; 12418c2ecf20Sopenharmony_ci goto send; 12428c2ecf20Sopenharmony_ci } else if (skb->ip_summed == CHECKSUM_PARTIAL) { /* UDP hardware csum */ 12438c2ecf20Sopenharmony_cicsum_partial: 12448c2ecf20Sopenharmony_ci udp6_hwcsum_outgoing(sk, skb, &fl6->saddr, &fl6->daddr, len); 12458c2ecf20Sopenharmony_ci goto send; 12468c2ecf20Sopenharmony_ci } else 12478c2ecf20Sopenharmony_ci csum = udp_csum(skb); 12488c2ecf20Sopenharmony_ci 12498c2ecf20Sopenharmony_ci /* add protocol-dependent pseudo-header */ 12508c2ecf20Sopenharmony_ci uh->check = csum_ipv6_magic(&fl6->saddr, &fl6->daddr, 12518c2ecf20Sopenharmony_ci len, fl6->flowi6_proto, csum); 12528c2ecf20Sopenharmony_ci if (uh->check == 0) 12538c2ecf20Sopenharmony_ci uh->check = CSUM_MANGLED_0; 12548c2ecf20Sopenharmony_ci 12558c2ecf20Sopenharmony_cisend: 12568c2ecf20Sopenharmony_ci err = ip6_send_skb(skb); 12578c2ecf20Sopenharmony_ci if (err) { 12588c2ecf20Sopenharmony_ci if (err == -ENOBUFS && !inet6_sk(sk)->recverr) { 12598c2ecf20Sopenharmony_ci UDP6_INC_STATS(sock_net(sk), 12608c2ecf20Sopenharmony_ci UDP_MIB_SNDBUFERRORS, is_udplite); 12618c2ecf20Sopenharmony_ci err = 0; 12628c2ecf20Sopenharmony_ci } 12638c2ecf20Sopenharmony_ci } else { 12648c2ecf20Sopenharmony_ci UDP6_INC_STATS(sock_net(sk), 12658c2ecf20Sopenharmony_ci UDP_MIB_OUTDATAGRAMS, is_udplite); 12668c2ecf20Sopenharmony_ci } 12678c2ecf20Sopenharmony_ci return err; 12688c2ecf20Sopenharmony_ci} 12698c2ecf20Sopenharmony_ci 12708c2ecf20Sopenharmony_cistatic int udp_v6_push_pending_frames(struct sock *sk) 12718c2ecf20Sopenharmony_ci{ 12728c2ecf20Sopenharmony_ci struct sk_buff *skb; 12738c2ecf20Sopenharmony_ci struct udp_sock *up = udp_sk(sk); 12748c2ecf20Sopenharmony_ci struct flowi6 fl6; 12758c2ecf20Sopenharmony_ci int err = 0; 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_ci if (up->pending == AF_INET) 12788c2ecf20Sopenharmony_ci return udp_push_pending_frames(sk); 12798c2ecf20Sopenharmony_ci 12808c2ecf20Sopenharmony_ci /* ip6_finish_skb will release the cork, so make a copy of 12818c2ecf20Sopenharmony_ci * fl6 here. 12828c2ecf20Sopenharmony_ci */ 12838c2ecf20Sopenharmony_ci fl6 = inet_sk(sk)->cork.fl.u.ip6; 12848c2ecf20Sopenharmony_ci 12858c2ecf20Sopenharmony_ci skb = ip6_finish_skb(sk); 12868c2ecf20Sopenharmony_ci if (!skb) 12878c2ecf20Sopenharmony_ci goto out; 12888c2ecf20Sopenharmony_ci 12898c2ecf20Sopenharmony_ci err = udp_v6_send_skb(skb, &fl6, &inet_sk(sk)->cork.base); 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_ciout: 12928c2ecf20Sopenharmony_ci up->len = 0; 12938c2ecf20Sopenharmony_ci up->pending = 0; 12948c2ecf20Sopenharmony_ci return err; 12958c2ecf20Sopenharmony_ci} 12968c2ecf20Sopenharmony_ci 12978c2ecf20Sopenharmony_ciint udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) 12988c2ecf20Sopenharmony_ci{ 12998c2ecf20Sopenharmony_ci struct ipv6_txoptions opt_space; 13008c2ecf20Sopenharmony_ci struct udp_sock *up = udp_sk(sk); 13018c2ecf20Sopenharmony_ci struct inet_sock *inet = inet_sk(sk); 13028c2ecf20Sopenharmony_ci struct ipv6_pinfo *np = inet6_sk(sk); 13038c2ecf20Sopenharmony_ci DECLARE_SOCKADDR(struct sockaddr_in6 *, sin6, msg->msg_name); 13048c2ecf20Sopenharmony_ci struct in6_addr *daddr, *final_p, final; 13058c2ecf20Sopenharmony_ci struct ipv6_txoptions *opt = NULL; 13068c2ecf20Sopenharmony_ci struct ipv6_txoptions *opt_to_free = NULL; 13078c2ecf20Sopenharmony_ci struct ip6_flowlabel *flowlabel = NULL; 13088c2ecf20Sopenharmony_ci struct flowi6 fl6; 13098c2ecf20Sopenharmony_ci struct dst_entry *dst; 13108c2ecf20Sopenharmony_ci struct ipcm6_cookie ipc6; 13118c2ecf20Sopenharmony_ci int addr_len = msg->msg_namelen; 13128c2ecf20Sopenharmony_ci bool connected = false; 13138c2ecf20Sopenharmony_ci int ulen = len; 13148c2ecf20Sopenharmony_ci int corkreq = READ_ONCE(up->corkflag) || msg->msg_flags&MSG_MORE; 13158c2ecf20Sopenharmony_ci int err; 13168c2ecf20Sopenharmony_ci int is_udplite = IS_UDPLITE(sk); 13178c2ecf20Sopenharmony_ci int (*getfrag)(void *, char *, int, int, int, struct sk_buff *); 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_ci ipcm6_init(&ipc6); 13208c2ecf20Sopenharmony_ci ipc6.gso_size = READ_ONCE(up->gso_size); 13218c2ecf20Sopenharmony_ci ipc6.sockc.tsflags = sk->sk_tsflags; 13228c2ecf20Sopenharmony_ci ipc6.sockc.mark = sk->sk_mark; 13238c2ecf20Sopenharmony_ci 13248c2ecf20Sopenharmony_ci /* destination address check */ 13258c2ecf20Sopenharmony_ci if (sin6) { 13268c2ecf20Sopenharmony_ci if (addr_len < offsetof(struct sockaddr, sa_data)) 13278c2ecf20Sopenharmony_ci return -EINVAL; 13288c2ecf20Sopenharmony_ci 13298c2ecf20Sopenharmony_ci switch (sin6->sin6_family) { 13308c2ecf20Sopenharmony_ci case AF_INET6: 13318c2ecf20Sopenharmony_ci if (addr_len < SIN6_LEN_RFC2133) 13328c2ecf20Sopenharmony_ci return -EINVAL; 13338c2ecf20Sopenharmony_ci daddr = &sin6->sin6_addr; 13348c2ecf20Sopenharmony_ci if (ipv6_addr_any(daddr) && 13358c2ecf20Sopenharmony_ci ipv6_addr_v4mapped(&np->saddr)) 13368c2ecf20Sopenharmony_ci ipv6_addr_set_v4mapped(htonl(INADDR_LOOPBACK), 13378c2ecf20Sopenharmony_ci daddr); 13388c2ecf20Sopenharmony_ci break; 13398c2ecf20Sopenharmony_ci case AF_INET: 13408c2ecf20Sopenharmony_ci goto do_udp_sendmsg; 13418c2ecf20Sopenharmony_ci case AF_UNSPEC: 13428c2ecf20Sopenharmony_ci msg->msg_name = sin6 = NULL; 13438c2ecf20Sopenharmony_ci msg->msg_namelen = addr_len = 0; 13448c2ecf20Sopenharmony_ci daddr = NULL; 13458c2ecf20Sopenharmony_ci break; 13468c2ecf20Sopenharmony_ci default: 13478c2ecf20Sopenharmony_ci return -EINVAL; 13488c2ecf20Sopenharmony_ci } 13498c2ecf20Sopenharmony_ci } else if (!up->pending) { 13508c2ecf20Sopenharmony_ci if (sk->sk_state != TCP_ESTABLISHED) 13518c2ecf20Sopenharmony_ci return -EDESTADDRREQ; 13528c2ecf20Sopenharmony_ci daddr = &sk->sk_v6_daddr; 13538c2ecf20Sopenharmony_ci } else 13548c2ecf20Sopenharmony_ci daddr = NULL; 13558c2ecf20Sopenharmony_ci 13568c2ecf20Sopenharmony_ci if (daddr) { 13578c2ecf20Sopenharmony_ci if (ipv6_addr_v4mapped(daddr)) { 13588c2ecf20Sopenharmony_ci struct sockaddr_in sin; 13598c2ecf20Sopenharmony_ci sin.sin_family = AF_INET; 13608c2ecf20Sopenharmony_ci sin.sin_port = sin6 ? sin6->sin6_port : inet->inet_dport; 13618c2ecf20Sopenharmony_ci sin.sin_addr.s_addr = daddr->s6_addr32[3]; 13628c2ecf20Sopenharmony_ci msg->msg_name = &sin; 13638c2ecf20Sopenharmony_ci msg->msg_namelen = sizeof(sin); 13648c2ecf20Sopenharmony_cido_udp_sendmsg: 13658c2ecf20Sopenharmony_ci err = __ipv6_only_sock(sk) ? 13668c2ecf20Sopenharmony_ci -ENETUNREACH : udp_sendmsg(sk, msg, len); 13678c2ecf20Sopenharmony_ci msg->msg_name = sin6; 13688c2ecf20Sopenharmony_ci msg->msg_namelen = addr_len; 13698c2ecf20Sopenharmony_ci return err; 13708c2ecf20Sopenharmony_ci } 13718c2ecf20Sopenharmony_ci } 13728c2ecf20Sopenharmony_ci 13738c2ecf20Sopenharmony_ci if (up->pending == AF_INET) 13748c2ecf20Sopenharmony_ci return udp_sendmsg(sk, msg, len); 13758c2ecf20Sopenharmony_ci 13768c2ecf20Sopenharmony_ci /* Rough check on arithmetic overflow, 13778c2ecf20Sopenharmony_ci better check is made in ip6_append_data(). 13788c2ecf20Sopenharmony_ci */ 13798c2ecf20Sopenharmony_ci if (len > INT_MAX - sizeof(struct udphdr)) 13808c2ecf20Sopenharmony_ci return -EMSGSIZE; 13818c2ecf20Sopenharmony_ci 13828c2ecf20Sopenharmony_ci getfrag = is_udplite ? udplite_getfrag : ip_generic_getfrag; 13838c2ecf20Sopenharmony_ci if (up->pending) { 13848c2ecf20Sopenharmony_ci /* 13858c2ecf20Sopenharmony_ci * There are pending frames. 13868c2ecf20Sopenharmony_ci * The socket lock must be held while it's corked. 13878c2ecf20Sopenharmony_ci */ 13888c2ecf20Sopenharmony_ci lock_sock(sk); 13898c2ecf20Sopenharmony_ci if (likely(up->pending)) { 13908c2ecf20Sopenharmony_ci if (unlikely(up->pending != AF_INET6)) { 13918c2ecf20Sopenharmony_ci release_sock(sk); 13928c2ecf20Sopenharmony_ci return -EAFNOSUPPORT; 13938c2ecf20Sopenharmony_ci } 13948c2ecf20Sopenharmony_ci dst = NULL; 13958c2ecf20Sopenharmony_ci goto do_append_data; 13968c2ecf20Sopenharmony_ci } 13978c2ecf20Sopenharmony_ci release_sock(sk); 13988c2ecf20Sopenharmony_ci } 13998c2ecf20Sopenharmony_ci ulen += sizeof(struct udphdr); 14008c2ecf20Sopenharmony_ci 14018c2ecf20Sopenharmony_ci memset(&fl6, 0, sizeof(fl6)); 14028c2ecf20Sopenharmony_ci 14038c2ecf20Sopenharmony_ci if (sin6) { 14048c2ecf20Sopenharmony_ci if (sin6->sin6_port == 0) 14058c2ecf20Sopenharmony_ci return -EINVAL; 14068c2ecf20Sopenharmony_ci 14078c2ecf20Sopenharmony_ci fl6.fl6_dport = sin6->sin6_port; 14088c2ecf20Sopenharmony_ci daddr = &sin6->sin6_addr; 14098c2ecf20Sopenharmony_ci 14108c2ecf20Sopenharmony_ci if (np->sndflow) { 14118c2ecf20Sopenharmony_ci fl6.flowlabel = sin6->sin6_flowinfo&IPV6_FLOWINFO_MASK; 14128c2ecf20Sopenharmony_ci if (fl6.flowlabel&IPV6_FLOWLABEL_MASK) { 14138c2ecf20Sopenharmony_ci flowlabel = fl6_sock_lookup(sk, fl6.flowlabel); 14148c2ecf20Sopenharmony_ci if (IS_ERR(flowlabel)) 14158c2ecf20Sopenharmony_ci return -EINVAL; 14168c2ecf20Sopenharmony_ci } 14178c2ecf20Sopenharmony_ci } 14188c2ecf20Sopenharmony_ci 14198c2ecf20Sopenharmony_ci /* 14208c2ecf20Sopenharmony_ci * Otherwise it will be difficult to maintain 14218c2ecf20Sopenharmony_ci * sk->sk_dst_cache. 14228c2ecf20Sopenharmony_ci */ 14238c2ecf20Sopenharmony_ci if (sk->sk_state == TCP_ESTABLISHED && 14248c2ecf20Sopenharmony_ci ipv6_addr_equal(daddr, &sk->sk_v6_daddr)) 14258c2ecf20Sopenharmony_ci daddr = &sk->sk_v6_daddr; 14268c2ecf20Sopenharmony_ci 14278c2ecf20Sopenharmony_ci if (addr_len >= sizeof(struct sockaddr_in6) && 14288c2ecf20Sopenharmony_ci sin6->sin6_scope_id && 14298c2ecf20Sopenharmony_ci __ipv6_addr_needs_scope_id(__ipv6_addr_type(daddr))) 14308c2ecf20Sopenharmony_ci fl6.flowi6_oif = sin6->sin6_scope_id; 14318c2ecf20Sopenharmony_ci } else { 14328c2ecf20Sopenharmony_ci if (sk->sk_state != TCP_ESTABLISHED) 14338c2ecf20Sopenharmony_ci return -EDESTADDRREQ; 14348c2ecf20Sopenharmony_ci 14358c2ecf20Sopenharmony_ci fl6.fl6_dport = inet->inet_dport; 14368c2ecf20Sopenharmony_ci daddr = &sk->sk_v6_daddr; 14378c2ecf20Sopenharmony_ci fl6.flowlabel = np->flow_label; 14388c2ecf20Sopenharmony_ci connected = true; 14398c2ecf20Sopenharmony_ci } 14408c2ecf20Sopenharmony_ci 14418c2ecf20Sopenharmony_ci if (!fl6.flowi6_oif) 14428c2ecf20Sopenharmony_ci fl6.flowi6_oif = sk->sk_bound_dev_if; 14438c2ecf20Sopenharmony_ci 14448c2ecf20Sopenharmony_ci if (!fl6.flowi6_oif) 14458c2ecf20Sopenharmony_ci fl6.flowi6_oif = np->sticky_pktinfo.ipi6_ifindex; 14468c2ecf20Sopenharmony_ci 14478c2ecf20Sopenharmony_ci fl6.flowi6_uid = sk->sk_uid; 14488c2ecf20Sopenharmony_ci 14498c2ecf20Sopenharmony_ci if (msg->msg_controllen) { 14508c2ecf20Sopenharmony_ci opt = &opt_space; 14518c2ecf20Sopenharmony_ci memset(opt, 0, sizeof(struct ipv6_txoptions)); 14528c2ecf20Sopenharmony_ci opt->tot_len = sizeof(*opt); 14538c2ecf20Sopenharmony_ci ipc6.opt = opt; 14548c2ecf20Sopenharmony_ci 14558c2ecf20Sopenharmony_ci err = udp_cmsg_send(sk, msg, &ipc6.gso_size); 14568c2ecf20Sopenharmony_ci if (err > 0) 14578c2ecf20Sopenharmony_ci err = ip6_datagram_send_ctl(sock_net(sk), sk, msg, &fl6, 14588c2ecf20Sopenharmony_ci &ipc6); 14598c2ecf20Sopenharmony_ci if (err < 0) { 14608c2ecf20Sopenharmony_ci fl6_sock_release(flowlabel); 14618c2ecf20Sopenharmony_ci return err; 14628c2ecf20Sopenharmony_ci } 14638c2ecf20Sopenharmony_ci if ((fl6.flowlabel&IPV6_FLOWLABEL_MASK) && !flowlabel) { 14648c2ecf20Sopenharmony_ci flowlabel = fl6_sock_lookup(sk, fl6.flowlabel); 14658c2ecf20Sopenharmony_ci if (IS_ERR(flowlabel)) 14668c2ecf20Sopenharmony_ci return -EINVAL; 14678c2ecf20Sopenharmony_ci } 14688c2ecf20Sopenharmony_ci if (!(opt->opt_nflen|opt->opt_flen)) 14698c2ecf20Sopenharmony_ci opt = NULL; 14708c2ecf20Sopenharmony_ci connected = false; 14718c2ecf20Sopenharmony_ci } 14728c2ecf20Sopenharmony_ci if (!opt) { 14738c2ecf20Sopenharmony_ci opt = txopt_get(np); 14748c2ecf20Sopenharmony_ci opt_to_free = opt; 14758c2ecf20Sopenharmony_ci } 14768c2ecf20Sopenharmony_ci if (flowlabel) 14778c2ecf20Sopenharmony_ci opt = fl6_merge_options(&opt_space, flowlabel, opt); 14788c2ecf20Sopenharmony_ci opt = ipv6_fixup_options(&opt_space, opt); 14798c2ecf20Sopenharmony_ci ipc6.opt = opt; 14808c2ecf20Sopenharmony_ci 14818c2ecf20Sopenharmony_ci fl6.flowi6_proto = sk->sk_protocol; 14828c2ecf20Sopenharmony_ci fl6.flowi6_mark = ipc6.sockc.mark; 14838c2ecf20Sopenharmony_ci fl6.daddr = *daddr; 14848c2ecf20Sopenharmony_ci if (ipv6_addr_any(&fl6.saddr) && !ipv6_addr_any(&np->saddr)) 14858c2ecf20Sopenharmony_ci fl6.saddr = np->saddr; 14868c2ecf20Sopenharmony_ci fl6.fl6_sport = inet->inet_sport; 14878c2ecf20Sopenharmony_ci 14888c2ecf20Sopenharmony_ci if (cgroup_bpf_enabled && !connected) { 14898c2ecf20Sopenharmony_ci err = BPF_CGROUP_RUN_PROG_UDP6_SENDMSG_LOCK(sk, 14908c2ecf20Sopenharmony_ci (struct sockaddr *)sin6, &fl6.saddr); 14918c2ecf20Sopenharmony_ci if (err) 14928c2ecf20Sopenharmony_ci goto out_no_dst; 14938c2ecf20Sopenharmony_ci if (sin6) { 14948c2ecf20Sopenharmony_ci if (ipv6_addr_v4mapped(&sin6->sin6_addr)) { 14958c2ecf20Sopenharmony_ci /* BPF program rewrote IPv6-only by IPv4-mapped 14968c2ecf20Sopenharmony_ci * IPv6. It's currently unsupported. 14978c2ecf20Sopenharmony_ci */ 14988c2ecf20Sopenharmony_ci err = -ENOTSUPP; 14998c2ecf20Sopenharmony_ci goto out_no_dst; 15008c2ecf20Sopenharmony_ci } 15018c2ecf20Sopenharmony_ci if (sin6->sin6_port == 0) { 15028c2ecf20Sopenharmony_ci /* BPF program set invalid port. Reject it. */ 15038c2ecf20Sopenharmony_ci err = -EINVAL; 15048c2ecf20Sopenharmony_ci goto out_no_dst; 15058c2ecf20Sopenharmony_ci } 15068c2ecf20Sopenharmony_ci fl6.fl6_dport = sin6->sin6_port; 15078c2ecf20Sopenharmony_ci fl6.daddr = sin6->sin6_addr; 15088c2ecf20Sopenharmony_ci } 15098c2ecf20Sopenharmony_ci } 15108c2ecf20Sopenharmony_ci 15118c2ecf20Sopenharmony_ci if (ipv6_addr_any(&fl6.daddr)) 15128c2ecf20Sopenharmony_ci fl6.daddr.s6_addr[15] = 0x1; /* :: means loopback (BSD'ism) */ 15138c2ecf20Sopenharmony_ci 15148c2ecf20Sopenharmony_ci final_p = fl6_update_dst(&fl6, opt, &final); 15158c2ecf20Sopenharmony_ci if (final_p) 15168c2ecf20Sopenharmony_ci connected = false; 15178c2ecf20Sopenharmony_ci 15188c2ecf20Sopenharmony_ci if (!fl6.flowi6_oif && ipv6_addr_is_multicast(&fl6.daddr)) { 15198c2ecf20Sopenharmony_ci fl6.flowi6_oif = np->mcast_oif; 15208c2ecf20Sopenharmony_ci connected = false; 15218c2ecf20Sopenharmony_ci } else if (!fl6.flowi6_oif) 15228c2ecf20Sopenharmony_ci fl6.flowi6_oif = np->ucast_oif; 15238c2ecf20Sopenharmony_ci 15248c2ecf20Sopenharmony_ci security_sk_classify_flow(sk, flowi6_to_flowi_common(&fl6)); 15258c2ecf20Sopenharmony_ci 15268c2ecf20Sopenharmony_ci if (ipc6.tclass < 0) 15278c2ecf20Sopenharmony_ci ipc6.tclass = np->tclass; 15288c2ecf20Sopenharmony_ci 15298c2ecf20Sopenharmony_ci fl6.flowlabel = ip6_make_flowinfo(ipc6.tclass, fl6.flowlabel); 15308c2ecf20Sopenharmony_ci 15318c2ecf20Sopenharmony_ci dst = ip6_sk_dst_lookup_flow(sk, &fl6, final_p, connected); 15328c2ecf20Sopenharmony_ci if (IS_ERR(dst)) { 15338c2ecf20Sopenharmony_ci err = PTR_ERR(dst); 15348c2ecf20Sopenharmony_ci dst = NULL; 15358c2ecf20Sopenharmony_ci goto out; 15368c2ecf20Sopenharmony_ci } 15378c2ecf20Sopenharmony_ci 15388c2ecf20Sopenharmony_ci if (ipc6.hlimit < 0) 15398c2ecf20Sopenharmony_ci ipc6.hlimit = ip6_sk_dst_hoplimit(np, &fl6, dst); 15408c2ecf20Sopenharmony_ci 15418c2ecf20Sopenharmony_ci if (msg->msg_flags&MSG_CONFIRM) 15428c2ecf20Sopenharmony_ci goto do_confirm; 15438c2ecf20Sopenharmony_ciback_from_confirm: 15448c2ecf20Sopenharmony_ci 15458c2ecf20Sopenharmony_ci /* Lockless fast path for the non-corking case */ 15468c2ecf20Sopenharmony_ci if (!corkreq) { 15478c2ecf20Sopenharmony_ci struct inet_cork_full cork; 15488c2ecf20Sopenharmony_ci struct sk_buff *skb; 15498c2ecf20Sopenharmony_ci 15508c2ecf20Sopenharmony_ci skb = ip6_make_skb(sk, getfrag, msg, ulen, 15518c2ecf20Sopenharmony_ci sizeof(struct udphdr), &ipc6, 15528c2ecf20Sopenharmony_ci &fl6, (struct rt6_info *)dst, 15538c2ecf20Sopenharmony_ci msg->msg_flags, &cork); 15548c2ecf20Sopenharmony_ci err = PTR_ERR(skb); 15558c2ecf20Sopenharmony_ci if (!IS_ERR_OR_NULL(skb)) 15568c2ecf20Sopenharmony_ci err = udp_v6_send_skb(skb, &fl6, &cork.base); 15578c2ecf20Sopenharmony_ci goto out; 15588c2ecf20Sopenharmony_ci } 15598c2ecf20Sopenharmony_ci 15608c2ecf20Sopenharmony_ci lock_sock(sk); 15618c2ecf20Sopenharmony_ci if (unlikely(up->pending)) { 15628c2ecf20Sopenharmony_ci /* The socket is already corked while preparing it. */ 15638c2ecf20Sopenharmony_ci /* ... which is an evident application bug. --ANK */ 15648c2ecf20Sopenharmony_ci release_sock(sk); 15658c2ecf20Sopenharmony_ci 15668c2ecf20Sopenharmony_ci net_dbg_ratelimited("udp cork app bug 2\n"); 15678c2ecf20Sopenharmony_ci err = -EINVAL; 15688c2ecf20Sopenharmony_ci goto out; 15698c2ecf20Sopenharmony_ci } 15708c2ecf20Sopenharmony_ci 15718c2ecf20Sopenharmony_ci up->pending = AF_INET6; 15728c2ecf20Sopenharmony_ci 15738c2ecf20Sopenharmony_cido_append_data: 15748c2ecf20Sopenharmony_ci if (ipc6.dontfrag < 0) 15758c2ecf20Sopenharmony_ci ipc6.dontfrag = np->dontfrag; 15768c2ecf20Sopenharmony_ci up->len += ulen; 15778c2ecf20Sopenharmony_ci err = ip6_append_data(sk, getfrag, msg, ulen, sizeof(struct udphdr), 15788c2ecf20Sopenharmony_ci &ipc6, &fl6, (struct rt6_info *)dst, 15798c2ecf20Sopenharmony_ci corkreq ? msg->msg_flags|MSG_MORE : msg->msg_flags); 15808c2ecf20Sopenharmony_ci if (err) 15818c2ecf20Sopenharmony_ci udp_v6_flush_pending_frames(sk); 15828c2ecf20Sopenharmony_ci else if (!corkreq) 15838c2ecf20Sopenharmony_ci err = udp_v6_push_pending_frames(sk); 15848c2ecf20Sopenharmony_ci else if (unlikely(skb_queue_empty(&sk->sk_write_queue))) 15858c2ecf20Sopenharmony_ci up->pending = 0; 15868c2ecf20Sopenharmony_ci 15878c2ecf20Sopenharmony_ci if (err > 0) 15888c2ecf20Sopenharmony_ci err = np->recverr ? net_xmit_errno(err) : 0; 15898c2ecf20Sopenharmony_ci release_sock(sk); 15908c2ecf20Sopenharmony_ci 15918c2ecf20Sopenharmony_ciout: 15928c2ecf20Sopenharmony_ci dst_release(dst); 15938c2ecf20Sopenharmony_ciout_no_dst: 15948c2ecf20Sopenharmony_ci fl6_sock_release(flowlabel); 15958c2ecf20Sopenharmony_ci txopt_put(opt_to_free); 15968c2ecf20Sopenharmony_ci if (!err) 15978c2ecf20Sopenharmony_ci return len; 15988c2ecf20Sopenharmony_ci /* 15998c2ecf20Sopenharmony_ci * ENOBUFS = no kernel mem, SOCK_NOSPACE = no sndbuf space. Reporting 16008c2ecf20Sopenharmony_ci * ENOBUFS might not be good (it's not tunable per se), but otherwise 16018c2ecf20Sopenharmony_ci * we don't have a good statistic (IpOutDiscards but it can be too many 16028c2ecf20Sopenharmony_ci * things). We could add another new stat but at least for now that 16038c2ecf20Sopenharmony_ci * seems like overkill. 16048c2ecf20Sopenharmony_ci */ 16058c2ecf20Sopenharmony_ci if (err == -ENOBUFS || test_bit(SOCK_NOSPACE, &sk->sk_socket->flags)) { 16068c2ecf20Sopenharmony_ci UDP6_INC_STATS(sock_net(sk), 16078c2ecf20Sopenharmony_ci UDP_MIB_SNDBUFERRORS, is_udplite); 16088c2ecf20Sopenharmony_ci } 16098c2ecf20Sopenharmony_ci return err; 16108c2ecf20Sopenharmony_ci 16118c2ecf20Sopenharmony_cido_confirm: 16128c2ecf20Sopenharmony_ci if (msg->msg_flags & MSG_PROBE) 16138c2ecf20Sopenharmony_ci dst_confirm_neigh(dst, &fl6.daddr); 16148c2ecf20Sopenharmony_ci if (!(msg->msg_flags&MSG_PROBE) || len) 16158c2ecf20Sopenharmony_ci goto back_from_confirm; 16168c2ecf20Sopenharmony_ci err = 0; 16178c2ecf20Sopenharmony_ci goto out; 16188c2ecf20Sopenharmony_ci} 16198c2ecf20Sopenharmony_ci 16208c2ecf20Sopenharmony_civoid udpv6_destroy_sock(struct sock *sk) 16218c2ecf20Sopenharmony_ci{ 16228c2ecf20Sopenharmony_ci struct udp_sock *up = udp_sk(sk); 16238c2ecf20Sopenharmony_ci lock_sock(sk); 16248c2ecf20Sopenharmony_ci 16258c2ecf20Sopenharmony_ci /* protects from races with udp_abort() */ 16268c2ecf20Sopenharmony_ci sock_set_flag(sk, SOCK_DEAD); 16278c2ecf20Sopenharmony_ci udp_v6_flush_pending_frames(sk); 16288c2ecf20Sopenharmony_ci release_sock(sk); 16298c2ecf20Sopenharmony_ci 16308c2ecf20Sopenharmony_ci if (static_branch_unlikely(&udpv6_encap_needed_key)) { 16318c2ecf20Sopenharmony_ci if (up->encap_type) { 16328c2ecf20Sopenharmony_ci void (*encap_destroy)(struct sock *sk); 16338c2ecf20Sopenharmony_ci encap_destroy = READ_ONCE(up->encap_destroy); 16348c2ecf20Sopenharmony_ci if (encap_destroy) 16358c2ecf20Sopenharmony_ci encap_destroy(sk); 16368c2ecf20Sopenharmony_ci } 16378c2ecf20Sopenharmony_ci if (up->encap_enabled) { 16388c2ecf20Sopenharmony_ci static_branch_dec(&udpv6_encap_needed_key); 16398c2ecf20Sopenharmony_ci udp_encap_disable(); 16408c2ecf20Sopenharmony_ci } 16418c2ecf20Sopenharmony_ci } 16428c2ecf20Sopenharmony_ci} 16438c2ecf20Sopenharmony_ci 16448c2ecf20Sopenharmony_ci/* 16458c2ecf20Sopenharmony_ci * Socket option code for UDP 16468c2ecf20Sopenharmony_ci */ 16478c2ecf20Sopenharmony_ciint udpv6_setsockopt(struct sock *sk, int level, int optname, sockptr_t optval, 16488c2ecf20Sopenharmony_ci unsigned int optlen) 16498c2ecf20Sopenharmony_ci{ 16508c2ecf20Sopenharmony_ci if (level == SOL_UDP || level == SOL_UDPLITE) 16518c2ecf20Sopenharmony_ci return udp_lib_setsockopt(sk, level, optname, 16528c2ecf20Sopenharmony_ci optval, optlen, 16538c2ecf20Sopenharmony_ci udp_v6_push_pending_frames); 16548c2ecf20Sopenharmony_ci return ipv6_setsockopt(sk, level, optname, optval, optlen); 16558c2ecf20Sopenharmony_ci} 16568c2ecf20Sopenharmony_ci 16578c2ecf20Sopenharmony_ciint udpv6_getsockopt(struct sock *sk, int level, int optname, 16588c2ecf20Sopenharmony_ci char __user *optval, int __user *optlen) 16598c2ecf20Sopenharmony_ci{ 16608c2ecf20Sopenharmony_ci if (level == SOL_UDP || level == SOL_UDPLITE) 16618c2ecf20Sopenharmony_ci return udp_lib_getsockopt(sk, level, optname, optval, optlen); 16628c2ecf20Sopenharmony_ci return ipv6_getsockopt(sk, level, optname, optval, optlen); 16638c2ecf20Sopenharmony_ci} 16648c2ecf20Sopenharmony_ci 16658c2ecf20Sopenharmony_cistatic const struct inet6_protocol udpv6_protocol = { 16668c2ecf20Sopenharmony_ci .handler = udpv6_rcv, 16678c2ecf20Sopenharmony_ci .err_handler = udpv6_err, 16688c2ecf20Sopenharmony_ci .flags = INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL, 16698c2ecf20Sopenharmony_ci}; 16708c2ecf20Sopenharmony_ci 16718c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------ */ 16728c2ecf20Sopenharmony_ci#ifdef CONFIG_PROC_FS 16738c2ecf20Sopenharmony_ciint udp6_seq_show(struct seq_file *seq, void *v) 16748c2ecf20Sopenharmony_ci{ 16758c2ecf20Sopenharmony_ci if (v == SEQ_START_TOKEN) { 16768c2ecf20Sopenharmony_ci seq_puts(seq, IPV6_SEQ_DGRAM_HEADER); 16778c2ecf20Sopenharmony_ci } else { 16788c2ecf20Sopenharmony_ci int bucket = ((struct udp_iter_state *)seq->private)->bucket; 16798c2ecf20Sopenharmony_ci struct inet_sock *inet = inet_sk(v); 16808c2ecf20Sopenharmony_ci __u16 srcp = ntohs(inet->inet_sport); 16818c2ecf20Sopenharmony_ci __u16 destp = ntohs(inet->inet_dport); 16828c2ecf20Sopenharmony_ci __ip6_dgram_sock_seq_show(seq, v, srcp, destp, 16838c2ecf20Sopenharmony_ci udp_rqueue_get(v), bucket); 16848c2ecf20Sopenharmony_ci } 16858c2ecf20Sopenharmony_ci return 0; 16868c2ecf20Sopenharmony_ci} 16878c2ecf20Sopenharmony_ci 16888c2ecf20Sopenharmony_ciconst struct seq_operations udp6_seq_ops = { 16898c2ecf20Sopenharmony_ci .start = udp_seq_start, 16908c2ecf20Sopenharmony_ci .next = udp_seq_next, 16918c2ecf20Sopenharmony_ci .stop = udp_seq_stop, 16928c2ecf20Sopenharmony_ci .show = udp6_seq_show, 16938c2ecf20Sopenharmony_ci}; 16948c2ecf20Sopenharmony_ciEXPORT_SYMBOL(udp6_seq_ops); 16958c2ecf20Sopenharmony_ci 16968c2ecf20Sopenharmony_cistatic struct udp_seq_afinfo udp6_seq_afinfo = { 16978c2ecf20Sopenharmony_ci .family = AF_INET6, 16988c2ecf20Sopenharmony_ci .udp_table = &udp_table, 16998c2ecf20Sopenharmony_ci}; 17008c2ecf20Sopenharmony_ci 17018c2ecf20Sopenharmony_ciint __net_init udp6_proc_init(struct net *net) 17028c2ecf20Sopenharmony_ci{ 17038c2ecf20Sopenharmony_ci if (!proc_create_net_data("udp6", 0444, net->proc_net, &udp6_seq_ops, 17048c2ecf20Sopenharmony_ci sizeof(struct udp_iter_state), &udp6_seq_afinfo)) 17058c2ecf20Sopenharmony_ci return -ENOMEM; 17068c2ecf20Sopenharmony_ci return 0; 17078c2ecf20Sopenharmony_ci} 17088c2ecf20Sopenharmony_ci 17098c2ecf20Sopenharmony_civoid udp6_proc_exit(struct net *net) 17108c2ecf20Sopenharmony_ci{ 17118c2ecf20Sopenharmony_ci remove_proc_entry("udp6", net->proc_net); 17128c2ecf20Sopenharmony_ci} 17138c2ecf20Sopenharmony_ci#endif /* CONFIG_PROC_FS */ 17148c2ecf20Sopenharmony_ci 17158c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------ */ 17168c2ecf20Sopenharmony_ci 17178c2ecf20Sopenharmony_cistruct proto udpv6_prot = { 17188c2ecf20Sopenharmony_ci .name = "UDPv6", 17198c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 17208c2ecf20Sopenharmony_ci .close = udp_lib_close, 17218c2ecf20Sopenharmony_ci .pre_connect = udpv6_pre_connect, 17228c2ecf20Sopenharmony_ci .connect = ip6_datagram_connect, 17238c2ecf20Sopenharmony_ci .disconnect = udp_disconnect, 17248c2ecf20Sopenharmony_ci .ioctl = udp_ioctl, 17258c2ecf20Sopenharmony_ci .init = udpv6_init_sock, 17268c2ecf20Sopenharmony_ci .destroy = udpv6_destroy_sock, 17278c2ecf20Sopenharmony_ci .setsockopt = udpv6_setsockopt, 17288c2ecf20Sopenharmony_ci .getsockopt = udpv6_getsockopt, 17298c2ecf20Sopenharmony_ci .sendmsg = udpv6_sendmsg, 17308c2ecf20Sopenharmony_ci .recvmsg = udpv6_recvmsg, 17318c2ecf20Sopenharmony_ci .release_cb = ip6_datagram_release_cb, 17328c2ecf20Sopenharmony_ci .hash = udp_lib_hash, 17338c2ecf20Sopenharmony_ci .unhash = udp_lib_unhash, 17348c2ecf20Sopenharmony_ci .rehash = udp_v6_rehash, 17358c2ecf20Sopenharmony_ci .get_port = udp_v6_get_port, 17368c2ecf20Sopenharmony_ci .memory_allocated = &udp_memory_allocated, 17378c2ecf20Sopenharmony_ci .sysctl_mem = sysctl_udp_mem, 17388c2ecf20Sopenharmony_ci .sysctl_wmem_offset = offsetof(struct net, ipv4.sysctl_udp_wmem_min), 17398c2ecf20Sopenharmony_ci .sysctl_rmem_offset = offsetof(struct net, ipv4.sysctl_udp_rmem_min), 17408c2ecf20Sopenharmony_ci .obj_size = sizeof(struct udp6_sock), 17418c2ecf20Sopenharmony_ci .h.udp_table = &udp_table, 17428c2ecf20Sopenharmony_ci .diag_destroy = udp_abort, 17438c2ecf20Sopenharmony_ci}; 17448c2ecf20Sopenharmony_ci 17458c2ecf20Sopenharmony_cistatic struct inet_protosw udpv6_protosw = { 17468c2ecf20Sopenharmony_ci .type = SOCK_DGRAM, 17478c2ecf20Sopenharmony_ci .protocol = IPPROTO_UDP, 17488c2ecf20Sopenharmony_ci .prot = &udpv6_prot, 17498c2ecf20Sopenharmony_ci .ops = &inet6_dgram_ops, 17508c2ecf20Sopenharmony_ci .flags = INET_PROTOSW_PERMANENT, 17518c2ecf20Sopenharmony_ci}; 17528c2ecf20Sopenharmony_ci 17538c2ecf20Sopenharmony_ciint __init udpv6_init(void) 17548c2ecf20Sopenharmony_ci{ 17558c2ecf20Sopenharmony_ci int ret; 17568c2ecf20Sopenharmony_ci 17578c2ecf20Sopenharmony_ci ret = inet6_add_protocol(&udpv6_protocol, IPPROTO_UDP); 17588c2ecf20Sopenharmony_ci if (ret) 17598c2ecf20Sopenharmony_ci goto out; 17608c2ecf20Sopenharmony_ci 17618c2ecf20Sopenharmony_ci ret = inet6_register_protosw(&udpv6_protosw); 17628c2ecf20Sopenharmony_ci if (ret) 17638c2ecf20Sopenharmony_ci goto out_udpv6_protocol; 17648c2ecf20Sopenharmony_ciout: 17658c2ecf20Sopenharmony_ci return ret; 17668c2ecf20Sopenharmony_ci 17678c2ecf20Sopenharmony_ciout_udpv6_protocol: 17688c2ecf20Sopenharmony_ci inet6_del_protocol(&udpv6_protocol, IPPROTO_UDP); 17698c2ecf20Sopenharmony_ci goto out; 17708c2ecf20Sopenharmony_ci} 17718c2ecf20Sopenharmony_ci 17728c2ecf20Sopenharmony_civoid udpv6_exit(void) 17738c2ecf20Sopenharmony_ci{ 17748c2ecf20Sopenharmony_ci inet6_unregister_protosw(&udpv6_protosw); 17758c2ecf20Sopenharmony_ci inet6_del_protocol(&udpv6_protocol, IPPROTO_UDP); 17768c2ecf20Sopenharmony_ci} 1777