12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 2c319b4d7SVasiliy Kulikov /* 3c319b4d7SVasiliy Kulikov * INET An implementation of the TCP/IP protocol suite for the LINUX 4c319b4d7SVasiliy Kulikov * operating system. INET is implemented using the BSD Socket 5c319b4d7SVasiliy Kulikov * interface as the means of communication with the user level. 6c319b4d7SVasiliy Kulikov * 7c319b4d7SVasiliy Kulikov * "Ping" sockets 8c319b4d7SVasiliy Kulikov * 9c319b4d7SVasiliy Kulikov * Based on ipv4/udp.c code. 10c319b4d7SVasiliy Kulikov * 11c319b4d7SVasiliy Kulikov * Authors: Vasiliy Kulikov / Openwall (for Linux 2.6), 12c319b4d7SVasiliy Kulikov * Pavel Kankovsky (for Linux 2.4.32) 13c319b4d7SVasiliy Kulikov * 14c319b4d7SVasiliy Kulikov * Pavel gave all rights to bugs to Vasiliy, 15c319b4d7SVasiliy Kulikov * none of the bugs are Pavel's now. 16c319b4d7SVasiliy Kulikov */ 17c319b4d7SVasiliy Kulikov 18c319b4d7SVasiliy Kulikov #include <linux/uaccess.h> 19c319b4d7SVasiliy Kulikov #include <linux/types.h> 20c319b4d7SVasiliy Kulikov #include <linux/fcntl.h> 21c319b4d7SVasiliy Kulikov #include <linux/socket.h> 22c319b4d7SVasiliy Kulikov #include <linux/sockios.h> 23c319b4d7SVasiliy Kulikov #include <linux/in.h> 24c319b4d7SVasiliy Kulikov #include <linux/errno.h> 25c319b4d7SVasiliy Kulikov #include <linux/timer.h> 26c319b4d7SVasiliy Kulikov #include <linux/mm.h> 27c319b4d7SVasiliy Kulikov #include <linux/inet.h> 28c319b4d7SVasiliy Kulikov #include <linux/netdevice.h> 29c319b4d7SVasiliy Kulikov #include <net/snmp.h> 30c319b4d7SVasiliy Kulikov #include <net/ip.h> 31c319b4d7SVasiliy Kulikov #include <net/icmp.h> 32c319b4d7SVasiliy Kulikov #include <net/protocol.h> 33c319b4d7SVasiliy Kulikov #include <linux/skbuff.h> 34c319b4d7SVasiliy Kulikov #include <linux/proc_fs.h> 35bc3b2d7fSPaul Gortmaker #include <linux/export.h> 360ffe2412SYiFei Zhu #include <linux/bpf-cgroup.h> 37c319b4d7SVasiliy Kulikov #include <net/sock.h> 38c319b4d7SVasiliy Kulikov #include <net/ping.h> 39c319b4d7SVasiliy Kulikov #include <net/udp.h> 40c319b4d7SVasiliy Kulikov #include <net/route.h> 41c319b4d7SVasiliy Kulikov #include <net/inet_common.h> 42c319b4d7SVasiliy Kulikov #include <net/checksum.h> 43c319b4d7SVasiliy Kulikov 446d0bfe22SLorenzo Colitti #if IS_ENABLED(CONFIG_IPV6) 456d0bfe22SLorenzo Colitti #include <linux/in6.h> 466d0bfe22SLorenzo Colitti #include <linux/icmpv6.h> 476d0bfe22SLorenzo Colitti #include <net/addrconf.h> 486d0bfe22SLorenzo Colitti #include <net/ipv6.h> 496d0bfe22SLorenzo Colitti #include <net/transp_v6.h> 506d0bfe22SLorenzo Colitti #endif 51c319b4d7SVasiliy Kulikov 52ea074b34SStephen Hemminger struct ping_table { 53f1b5dfe6SKuniyuki Iwashima struct hlist_head hash[PING_HTABLE_SIZE]; 54dbca1596SEric Dumazet spinlock_t lock; 55ea074b34SStephen Hemminger }; 566d0bfe22SLorenzo Colitti 57ea074b34SStephen Hemminger static struct ping_table ping_table; 586d0bfe22SLorenzo Colitti struct pingv6_ops pingv6_ops; 596d0bfe22SLorenzo Colitti EXPORT_SYMBOL_GPL(pingv6_ops); 60c319b4d7SVasiliy Kulikov 611b1cb1f7SEric Dumazet static u16 ping_port_rover; 62c319b4d7SVasiliy Kulikov 636eada011SEric Dumazet static inline u32 ping_hashfn(const struct net *net, u32 num, u32 mask) 64c319b4d7SVasiliy Kulikov { 656eada011SEric Dumazet u32 res = (num + net_hash_mix(net)) & mask; 6695c96174SEric Dumazet 676eada011SEric Dumazet pr_debug("hash(%u) = %u\n", num, res); 68c319b4d7SVasiliy Kulikov return res; 69c319b4d7SVasiliy Kulikov } 706d0bfe22SLorenzo Colitti EXPORT_SYMBOL_GPL(ping_hash); 71c319b4d7SVasiliy Kulikov 72f1b5dfe6SKuniyuki Iwashima static inline struct hlist_head *ping_hashslot(struct ping_table *table, 7395c96174SEric Dumazet struct net *net, unsigned int num) 74c319b4d7SVasiliy Kulikov { 75c319b4d7SVasiliy Kulikov return &table->hash[ping_hashfn(net, num, PING_HTABLE_MASK)]; 76c319b4d7SVasiliy Kulikov } 77c319b4d7SVasiliy Kulikov 786d0bfe22SLorenzo Colitti int ping_get_port(struct sock *sk, unsigned short ident) 79c319b4d7SVasiliy Kulikov { 80c319b4d7SVasiliy Kulikov struct inet_sock *isk, *isk2; 81f1b5dfe6SKuniyuki Iwashima struct hlist_head *hlist; 82c319b4d7SVasiliy Kulikov struct sock *sk2 = NULL; 83c319b4d7SVasiliy Kulikov 84c319b4d7SVasiliy Kulikov isk = inet_sk(sk); 85dbca1596SEric Dumazet spin_lock(&ping_table.lock); 86c319b4d7SVasiliy Kulikov if (ident == 0) { 87c319b4d7SVasiliy Kulikov u32 i; 88c319b4d7SVasiliy Kulikov u16 result = ping_port_rover + 1; 89c319b4d7SVasiliy Kulikov 90c319b4d7SVasiliy Kulikov for (i = 0; i < (1L << 16); i++, result++) { 91c319b4d7SVasiliy Kulikov if (!result) 92c319b4d7SVasiliy Kulikov result++; /* avoid zero */ 93c319b4d7SVasiliy Kulikov hlist = ping_hashslot(&ping_table, sock_net(sk), 94c319b4d7SVasiliy Kulikov result); 95f1b5dfe6SKuniyuki Iwashima sk_for_each(sk2, hlist) { 96c319b4d7SVasiliy Kulikov isk2 = inet_sk(sk2); 97c319b4d7SVasiliy Kulikov 98c319b4d7SVasiliy Kulikov if (isk2->inet_num == result) 99c319b4d7SVasiliy Kulikov goto next_port; 100c319b4d7SVasiliy Kulikov } 101c319b4d7SVasiliy Kulikov 102c319b4d7SVasiliy Kulikov /* found */ 103c319b4d7SVasiliy Kulikov ping_port_rover = ident = result; 104c319b4d7SVasiliy Kulikov break; 105c319b4d7SVasiliy Kulikov next_port: 106c319b4d7SVasiliy Kulikov ; 107c319b4d7SVasiliy Kulikov } 108c319b4d7SVasiliy Kulikov if (i >= (1L << 16)) 109c319b4d7SVasiliy Kulikov goto fail; 110c319b4d7SVasiliy Kulikov } else { 111c319b4d7SVasiliy Kulikov hlist = ping_hashslot(&ping_table, sock_net(sk), ident); 112f1b5dfe6SKuniyuki Iwashima sk_for_each(sk2, hlist) { 113c319b4d7SVasiliy Kulikov isk2 = inet_sk(sk2); 114c319b4d7SVasiliy Kulikov 1156d0bfe22SLorenzo Colitti /* BUG? Why is this reuse and not reuseaddr? ping.c 1166d0bfe22SLorenzo Colitti * doesn't turn off SO_REUSEADDR, and it doesn't expect 1176d0bfe22SLorenzo Colitti * that other ping processes can steal its packets. 1186d0bfe22SLorenzo Colitti */ 119c319b4d7SVasiliy Kulikov if ((isk2->inet_num == ident) && 120c319b4d7SVasiliy Kulikov (sk2 != sk) && 121c319b4d7SVasiliy Kulikov (!sk2->sk_reuse || !sk->sk_reuse)) 122c319b4d7SVasiliy Kulikov goto fail; 123c319b4d7SVasiliy Kulikov } 124c319b4d7SVasiliy Kulikov } 125c319b4d7SVasiliy Kulikov 126c319b4d7SVasiliy Kulikov pr_debug("found port/ident = %d\n", ident); 127c319b4d7SVasiliy Kulikov isk->inet_num = ident; 128c319b4d7SVasiliy Kulikov if (sk_unhashed(sk)) { 129c319b4d7SVasiliy Kulikov pr_debug("was not hashed\n"); 130f1b5dfe6SKuniyuki Iwashima sk_add_node_rcu(sk, hlist); 131dbca1596SEric Dumazet sock_set_flag(sk, SOCK_RCU_FREE); 132c319b4d7SVasiliy Kulikov sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1); 133c319b4d7SVasiliy Kulikov } 134dbca1596SEric Dumazet spin_unlock(&ping_table.lock); 135c319b4d7SVasiliy Kulikov return 0; 136c319b4d7SVasiliy Kulikov 137c319b4d7SVasiliy Kulikov fail: 138dbca1596SEric Dumazet spin_unlock(&ping_table.lock); 1397a7160edSKuniyuki Iwashima return -EADDRINUSE; 140c319b4d7SVasiliy Kulikov } 1416d0bfe22SLorenzo Colitti EXPORT_SYMBOL_GPL(ping_get_port); 142c319b4d7SVasiliy Kulikov 143086c653fSCraig Gallek int ping_hash(struct sock *sk) 144c319b4d7SVasiliy Kulikov { 1456d0bfe22SLorenzo Colitti pr_debug("ping_hash(sk->port=%u)\n", inet_sk(sk)->inet_num); 146c319b4d7SVasiliy Kulikov BUG(); /* "Please do not press this button again." */ 147086c653fSCraig Gallek 148086c653fSCraig Gallek return 0; 149c319b4d7SVasiliy Kulikov } 150c319b4d7SVasiliy Kulikov 1516d0bfe22SLorenzo Colitti void ping_unhash(struct sock *sk) 152c319b4d7SVasiliy Kulikov { 153c319b4d7SVasiliy Kulikov struct inet_sock *isk = inet_sk(sk); 15443a66845SEric Dumazet 1556d0bfe22SLorenzo Colitti pr_debug("ping_unhash(isk=%p,isk->num=%u)\n", isk, isk->inet_num); 156dbca1596SEric Dumazet spin_lock(&ping_table.lock); 157f1b5dfe6SKuniyuki Iwashima if (sk_del_node_init_rcu(sk)) { 158747465efSEric Dumazet isk->inet_num = 0; 159747465efSEric Dumazet isk->inet_sport = 0; 160c319b4d7SVasiliy Kulikov sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1); 161c319b4d7SVasiliy Kulikov } 162dbca1596SEric Dumazet spin_unlock(&ping_table.lock); 163c319b4d7SVasiliy Kulikov } 1646d0bfe22SLorenzo Colitti EXPORT_SYMBOL_GPL(ping_unhash); 165c319b4d7SVasiliy Kulikov 166dbca1596SEric Dumazet /* Called under rcu_read_lock() */ 1676d0bfe22SLorenzo Colitti static struct sock *ping_lookup(struct net *net, struct sk_buff *skb, u16 ident) 168c319b4d7SVasiliy Kulikov { 169f1b5dfe6SKuniyuki Iwashima struct hlist_head *hslot = ping_hashslot(&ping_table, net, ident); 170c319b4d7SVasiliy Kulikov struct sock *sk = NULL; 171c319b4d7SVasiliy Kulikov struct inet_sock *isk; 17235a79e64SXin Long int dif, sdif; 173c319b4d7SVasiliy Kulikov 1746d0bfe22SLorenzo Colitti if (skb->protocol == htons(ETH_P_IP)) { 17535a79e64SXin Long dif = inet_iif(skb); 17635a79e64SXin Long sdif = inet_sdif(skb); 177747465efSEric Dumazet pr_debug("try to find: num = %d, daddr = %pI4, dif = %d\n", 1786d0bfe22SLorenzo Colitti (int)ident, &ip_hdr(skb)->daddr, dif); 1796d0bfe22SLorenzo Colitti #if IS_ENABLED(CONFIG_IPV6) 1806d0bfe22SLorenzo Colitti } else if (skb->protocol == htons(ETH_P_IPV6)) { 18135a79e64SXin Long dif = inet6_iif(skb); 18235a79e64SXin Long sdif = inet6_sdif(skb); 1836d0bfe22SLorenzo Colitti pr_debug("try to find: num = %d, daddr = %pI6c, dif = %d\n", 1846d0bfe22SLorenzo Colitti (int)ident, &ipv6_hdr(skb)->daddr, dif); 1856d0bfe22SLorenzo Colitti #endif 18635a79e64SXin Long } else { 18735a79e64SXin Long return NULL; 1886d0bfe22SLorenzo Colitti } 1896d0bfe22SLorenzo Colitti 190f1b5dfe6SKuniyuki Iwashima sk_for_each_rcu(sk, hslot) { 191c319b4d7SVasiliy Kulikov isk = inet_sk(sk); 192c319b4d7SVasiliy Kulikov 1936d0bfe22SLorenzo Colitti pr_debug("iterate\n"); 1946d0bfe22SLorenzo Colitti if (isk->inet_num != ident) 1956d0bfe22SLorenzo Colitti continue; 1966d0bfe22SLorenzo Colitti 1976d0bfe22SLorenzo Colitti if (skb->protocol == htons(ETH_P_IP) && 1986d0bfe22SLorenzo Colitti sk->sk_family == AF_INET) { 199747465efSEric Dumazet pr_debug("found: %p: num=%d, daddr=%pI4, dif=%d\n", sk, 200747465efSEric Dumazet (int) isk->inet_num, &isk->inet_rcv_saddr, 201c319b4d7SVasiliy Kulikov sk->sk_bound_dev_if); 202c319b4d7SVasiliy Kulikov 2036d0bfe22SLorenzo Colitti if (isk->inet_rcv_saddr && 2046d0bfe22SLorenzo Colitti isk->inet_rcv_saddr != ip_hdr(skb)->daddr) 205c319b4d7SVasiliy Kulikov continue; 2066d0bfe22SLorenzo Colitti #if IS_ENABLED(CONFIG_IPV6) 2076d0bfe22SLorenzo Colitti } else if (skb->protocol == htons(ETH_P_IPV6) && 2086d0bfe22SLorenzo Colitti sk->sk_family == AF_INET6) { 2096d0bfe22SLorenzo Colitti 2106d0bfe22SLorenzo Colitti pr_debug("found: %p: num=%d, daddr=%pI6c, dif=%d\n", sk, 2116d0bfe22SLorenzo Colitti (int) isk->inet_num, 212efe4208fSEric Dumazet &sk->sk_v6_rcv_saddr, 2136d0bfe22SLorenzo Colitti sk->sk_bound_dev_if); 2146d0bfe22SLorenzo Colitti 215efe4208fSEric Dumazet if (!ipv6_addr_any(&sk->sk_v6_rcv_saddr) && 216efe4208fSEric Dumazet !ipv6_addr_equal(&sk->sk_v6_rcv_saddr, 2176d0bfe22SLorenzo Colitti &ipv6_hdr(skb)->daddr)) 218c319b4d7SVasiliy Kulikov continue; 2196d0bfe22SLorenzo Colitti #endif 22091a0b603SJane Zhou } else { 22191a0b603SJane Zhou continue; 2226d0bfe22SLorenzo Colitti } 2236d0bfe22SLorenzo Colitti 2242afc3b5aSXin Long if (sk->sk_bound_dev_if && sk->sk_bound_dev_if != dif && 22535a79e64SXin Long sk->sk_bound_dev_if != sdif) 226c319b4d7SVasiliy Kulikov continue; 227c319b4d7SVasiliy Kulikov 228c319b4d7SVasiliy Kulikov goto exit; 229c319b4d7SVasiliy Kulikov } 230c319b4d7SVasiliy Kulikov 231c319b4d7SVasiliy Kulikov sk = NULL; 232c319b4d7SVasiliy Kulikov exit: 233c319b4d7SVasiliy Kulikov 234c319b4d7SVasiliy Kulikov return sk; 235c319b4d7SVasiliy Kulikov } 236c319b4d7SVasiliy Kulikov 2377064d16eSEric W. Biederman static void inet_get_ping_group_range_net(struct net *net, kgid_t *low, 2387064d16eSEric W. Biederman kgid_t *high) 239f56e03e8SVasiliy Kulikov { 240ba6b918aSCong Wang kgid_t *data = net->ipv4.ping_group_range.range; 24195c96174SEric Dumazet unsigned int seq; 24295c96174SEric Dumazet 243f56e03e8SVasiliy Kulikov do { 244ba6b918aSCong Wang seq = read_seqbegin(&net->ipv4.ping_group_range.lock); 245f56e03e8SVasiliy Kulikov 246f56e03e8SVasiliy Kulikov *low = data[0]; 247f56e03e8SVasiliy Kulikov *high = data[1]; 248ba6b918aSCong Wang } while (read_seqretry(&net->ipv4.ping_group_range.lock, seq)); 249f56e03e8SVasiliy Kulikov } 250f56e03e8SVasiliy Kulikov 251f56e03e8SVasiliy Kulikov 2526d0bfe22SLorenzo Colitti int ping_init_sock(struct sock *sk) 253c319b4d7SVasiliy Kulikov { 254c319b4d7SVasiliy Kulikov struct net *net = sock_net(sk); 2557064d16eSEric W. Biederman kgid_t group = current_egid(); 256b04c4619SWang, Xiaoming struct group_info *group_info; 25781243eacSAlexey Dobriyan int i; 258ae2975bcSEric W. Biederman kgid_t low, high; 259b04c4619SWang, Xiaoming int ret = 0; 260c319b4d7SVasiliy Kulikov 2619145736dSLorenzo Colitti if (sk->sk_family == AF_INET6) 2629145736dSLorenzo Colitti sk->sk_ipv6only = 1; 2639145736dSLorenzo Colitti 2647064d16eSEric W. Biederman inet_get_ping_group_range_net(net, &low, &high); 2657064d16eSEric W. Biederman if (gid_lte(low, group) && gid_lte(group, high)) 266c319b4d7SVasiliy Kulikov return 0; 267c319b4d7SVasiliy Kulikov 268b04c4619SWang, Xiaoming group_info = get_current_groups(); 26981243eacSAlexey Dobriyan for (i = 0; i < group_info->ngroups; i++) { 27081243eacSAlexey Dobriyan kgid_t gid = group_info->gid[i]; 27181243eacSAlexey Dobriyan 272ae2975bcSEric W. Biederman if (gid_lte(low, gid) && gid_lte(gid, high)) 273b04c4619SWang, Xiaoming goto out_release_group; 274c319b4d7SVasiliy Kulikov } 275c319b4d7SVasiliy Kulikov 276b04c4619SWang, Xiaoming ret = -EACCES; 277b04c4619SWang, Xiaoming 278b04c4619SWang, Xiaoming out_release_group: 279b04c4619SWang, Xiaoming put_group_info(group_info); 280b04c4619SWang, Xiaoming return ret; 281c319b4d7SVasiliy Kulikov } 2826d0bfe22SLorenzo Colitti EXPORT_SYMBOL_GPL(ping_init_sock); 283c319b4d7SVasiliy Kulikov 2846d0bfe22SLorenzo Colitti void ping_close(struct sock *sk, long timeout) 285c319b4d7SVasiliy Kulikov { 286c319b4d7SVasiliy Kulikov pr_debug("ping_close(sk=%p,sk->num=%u)\n", 287c319b4d7SVasiliy Kulikov inet_sk(sk), inet_sk(sk)->inet_num); 28841c6d650SReshetova, Elena pr_debug("isk->refcnt = %d\n", refcount_read(&sk->sk_refcnt)); 289c319b4d7SVasiliy Kulikov 290c319b4d7SVasiliy Kulikov sk_common_release(sk); 291c319b4d7SVasiliy Kulikov } 2926d0bfe22SLorenzo Colitti EXPORT_SYMBOL_GPL(ping_close); 293c319b4d7SVasiliy Kulikov 2940ffe2412SYiFei Zhu static int ping_pre_connect(struct sock *sk, struct sockaddr *uaddr, 2950ffe2412SYiFei Zhu int addr_len) 2960ffe2412SYiFei Zhu { 2970ffe2412SYiFei Zhu /* This check is replicated from __ip4_datagram_connect() and 2980ffe2412SYiFei Zhu * intended to prevent BPF program called below from accessing bytes 2990ffe2412SYiFei Zhu * that are out of the bound specified by user in addr_len. 3000ffe2412SYiFei Zhu */ 3010ffe2412SYiFei Zhu if (addr_len < sizeof(struct sockaddr_in)) 3020ffe2412SYiFei Zhu return -EINVAL; 3030ffe2412SYiFei Zhu 3040ffe2412SYiFei Zhu return BPF_CGROUP_RUN_PROG_INET4_CONNECT_LOCK(sk, uaddr); 3050ffe2412SYiFei Zhu } 3060ffe2412SYiFei Zhu 3076d0bfe22SLorenzo Colitti /* Checks the bind address and possibly modifies sk->sk_bound_dev_if. */ 308a06a2d37SWu Fengguang static int ping_check_bind_addr(struct sock *sk, struct inet_sock *isk, 3095af68891SMiaohe Lin struct sockaddr *uaddr, int addr_len) 3105af68891SMiaohe Lin { 3116d0bfe22SLorenzo Colitti struct net *net = sock_net(sk); 3126d0bfe22SLorenzo Colitti if (sk->sk_family == AF_INET) { 313c319b4d7SVasiliy Kulikov struct sockaddr_in *addr = (struct sockaddr_in *) uaddr; 314e1a7ac6fSNicolas Dichtel u32 tb_id = RT_TABLE_LOCAL; 315c319b4d7SVasiliy Kulikov int chk_addr_ret; 316c319b4d7SVasiliy Kulikov 3176d0bfe22SLorenzo Colitti if (addr_len < sizeof(*addr)) 318c319b4d7SVasiliy Kulikov return -EINVAL; 319c319b4d7SVasiliy Kulikov 3209145736dSLorenzo Colitti if (addr->sin_family != AF_INET && 3219145736dSLorenzo Colitti !(addr->sin_family == AF_UNSPEC && 3229145736dSLorenzo Colitti addr->sin_addr.s_addr == htonl(INADDR_ANY))) 3239145736dSLorenzo Colitti return -EAFNOSUPPORT; 3249145736dSLorenzo Colitti 3256d0bfe22SLorenzo Colitti pr_debug("ping_check_bind_addr(sk=%p,addr=%pI4,port=%d)\n", 3266d0bfe22SLorenzo Colitti sk, &addr->sin_addr.s_addr, ntohs(addr->sin_port)); 327c319b4d7SVasiliy Kulikov 328b4a028c4SRiccardo Paolo Bestetti if (addr->sin_addr.s_addr == htonl(INADDR_ANY)) 329b4a028c4SRiccardo Paolo Bestetti return 0; 330b4a028c4SRiccardo Paolo Bestetti 331e1a7ac6fSNicolas Dichtel tb_id = l3mdev_fib_table_by_index(net, sk->sk_bound_dev_if) ? : tb_id; 332e1a7ac6fSNicolas Dichtel chk_addr_ret = inet_addr_type_table(net, addr->sin_addr.s_addr, tb_id); 333c319b4d7SVasiliy Kulikov 334b4a028c4SRiccardo Paolo Bestetti if (chk_addr_ret == RTN_MULTICAST || 335b4a028c4SRiccardo Paolo Bestetti chk_addr_ret == RTN_BROADCAST || 336b4a028c4SRiccardo Paolo Bestetti (chk_addr_ret != RTN_LOCAL && 337b4a028c4SRiccardo Paolo Bestetti !inet_can_nonlocal_bind(net, isk))) 338c319b4d7SVasiliy Kulikov return -EADDRNOTAVAIL; 339c319b4d7SVasiliy Kulikov 3406d0bfe22SLorenzo Colitti #if IS_ENABLED(CONFIG_IPV6) 3416d0bfe22SLorenzo Colitti } else if (sk->sk_family == AF_INET6) { 3426d0bfe22SLorenzo Colitti struct sockaddr_in6 *addr = (struct sockaddr_in6 *) uaddr; 3436d0bfe22SLorenzo Colitti int addr_type, scoped, has_addr; 3446d0bfe22SLorenzo Colitti struct net_device *dev = NULL; 3456d0bfe22SLorenzo Colitti 3466d0bfe22SLorenzo Colitti if (addr_len < sizeof(*addr)) 3476d0bfe22SLorenzo Colitti return -EINVAL; 3486d0bfe22SLorenzo Colitti 34982b276cdSHannes Frederic Sowa if (addr->sin6_family != AF_INET6) 3509145736dSLorenzo Colitti return -EAFNOSUPPORT; 35182b276cdSHannes Frederic Sowa 3526d0bfe22SLorenzo Colitti pr_debug("ping_check_bind_addr(sk=%p,addr=%pI6c,port=%d)\n", 3536d0bfe22SLorenzo Colitti sk, addr->sin6_addr.s6_addr, ntohs(addr->sin6_port)); 3546d0bfe22SLorenzo Colitti 3556d0bfe22SLorenzo Colitti addr_type = ipv6_addr_type(&addr->sin6_addr); 3566d0bfe22SLorenzo Colitti scoped = __ipv6_addr_needs_scope_id(addr_type); 3576d0bfe22SLorenzo Colitti if ((addr_type != IPV6_ADDR_ANY && 3586d0bfe22SLorenzo Colitti !(addr_type & IPV6_ADDR_UNICAST)) || 3596d0bfe22SLorenzo Colitti (scoped && !addr->sin6_scope_id)) 3606d0bfe22SLorenzo Colitti return -EINVAL; 3616d0bfe22SLorenzo Colitti 3626d0bfe22SLorenzo Colitti rcu_read_lock(); 3636d0bfe22SLorenzo Colitti if (addr->sin6_scope_id) { 3646d0bfe22SLorenzo Colitti dev = dev_get_by_index_rcu(net, addr->sin6_scope_id); 3656d0bfe22SLorenzo Colitti if (!dev) { 3666d0bfe22SLorenzo Colitti rcu_read_unlock(); 3676d0bfe22SLorenzo Colitti return -ENODEV; 3686d0bfe22SLorenzo Colitti } 3696d0bfe22SLorenzo Colitti } 370e1a7ac6fSNicolas Dichtel 371e1a7ac6fSNicolas Dichtel if (!dev && sk->sk_bound_dev_if) { 372e1a7ac6fSNicolas Dichtel dev = dev_get_by_index_rcu(net, sk->sk_bound_dev_if); 373e1a7ac6fSNicolas Dichtel if (!dev) { 374e1a7ac6fSNicolas Dichtel rcu_read_unlock(); 375e1a7ac6fSNicolas Dichtel return -ENODEV; 376e1a7ac6fSNicolas Dichtel } 377e1a7ac6fSNicolas Dichtel } 3786d0bfe22SLorenzo Colitti has_addr = pingv6_ops.ipv6_chk_addr(net, &addr->sin6_addr, dev, 3796d0bfe22SLorenzo Colitti scoped); 3806d0bfe22SLorenzo Colitti rcu_read_unlock(); 3816d0bfe22SLorenzo Colitti 38283ba4645SVincent Bernat if (!(ipv6_can_nonlocal_bind(net, isk) || has_addr || 3836d0bfe22SLorenzo Colitti addr_type == IPV6_ADDR_ANY)) 3846d0bfe22SLorenzo Colitti return -EADDRNOTAVAIL; 3856d0bfe22SLorenzo Colitti 3866d0bfe22SLorenzo Colitti if (scoped) 3876d0bfe22SLorenzo Colitti sk->sk_bound_dev_if = addr->sin6_scope_id; 3886d0bfe22SLorenzo Colitti #endif 3896d0bfe22SLorenzo Colitti } else { 3906d0bfe22SLorenzo Colitti return -EAFNOSUPPORT; 3916d0bfe22SLorenzo Colitti } 3926d0bfe22SLorenzo Colitti return 0; 3936d0bfe22SLorenzo Colitti } 3946d0bfe22SLorenzo Colitti 395a06a2d37SWu Fengguang static void ping_set_saddr(struct sock *sk, struct sockaddr *saddr) 3966d0bfe22SLorenzo Colitti { 3976d0bfe22SLorenzo Colitti if (saddr->sa_family == AF_INET) { 3986d0bfe22SLorenzo Colitti struct inet_sock *isk = inet_sk(sk); 3996d0bfe22SLorenzo Colitti struct sockaddr_in *addr = (struct sockaddr_in *) saddr; 4006d0bfe22SLorenzo Colitti isk->inet_rcv_saddr = isk->inet_saddr = addr->sin_addr.s_addr; 4016d0bfe22SLorenzo Colitti #if IS_ENABLED(CONFIG_IPV6) 4026d0bfe22SLorenzo Colitti } else if (saddr->sa_family == AF_INET6) { 4036d0bfe22SLorenzo Colitti struct sockaddr_in6 *addr = (struct sockaddr_in6 *) saddr; 4046d0bfe22SLorenzo Colitti struct ipv6_pinfo *np = inet6_sk(sk); 405efe4208fSEric Dumazet sk->sk_v6_rcv_saddr = np->saddr = addr->sin6_addr; 4066d0bfe22SLorenzo Colitti #endif 4076d0bfe22SLorenzo Colitti } 4086d0bfe22SLorenzo Colitti } 4096d0bfe22SLorenzo Colitti 4106d0bfe22SLorenzo Colitti /* 4116d0bfe22SLorenzo Colitti * We need our own bind because there are no privileged id's == local ports. 4126d0bfe22SLorenzo Colitti * Moreover, we don't allow binding to multi- and broadcast addresses. 4136d0bfe22SLorenzo Colitti */ 4146d0bfe22SLorenzo Colitti 4156d0bfe22SLorenzo Colitti int ping_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len) 4166d0bfe22SLorenzo Colitti { 4176d0bfe22SLorenzo Colitti struct inet_sock *isk = inet_sk(sk); 4186d0bfe22SLorenzo Colitti unsigned short snum; 4196d0bfe22SLorenzo Colitti int err; 4206d0bfe22SLorenzo Colitti int dif = sk->sk_bound_dev_if; 4216d0bfe22SLorenzo Colitti 4226d0bfe22SLorenzo Colitti err = ping_check_bind_addr(sk, isk, uaddr, addr_len); 4236d0bfe22SLorenzo Colitti if (err) 4246d0bfe22SLorenzo Colitti return err; 4256d0bfe22SLorenzo Colitti 426c319b4d7SVasiliy Kulikov lock_sock(sk); 427c319b4d7SVasiliy Kulikov 428c319b4d7SVasiliy Kulikov err = -EINVAL; 429c319b4d7SVasiliy Kulikov if (isk->inet_num != 0) 430c319b4d7SVasiliy Kulikov goto out; 431c319b4d7SVasiliy Kulikov 432c319b4d7SVasiliy Kulikov err = -EADDRINUSE; 4336d0bfe22SLorenzo Colitti snum = ntohs(((struct sockaddr_in *)uaddr)->sin_port); 4346d0bfe22SLorenzo Colitti if (ping_get_port(sk, snum) != 0) { 4350316a211SMiaohe Lin /* Restore possibly modified sk->sk_bound_dev_if by ping_check_bind_addr(). */ 4360316a211SMiaohe Lin sk->sk_bound_dev_if = dif; 437c319b4d7SVasiliy Kulikov goto out; 438c319b4d7SVasiliy Kulikov } 4390316a211SMiaohe Lin ping_set_saddr(sk, uaddr); 440c319b4d7SVasiliy Kulikov 441a7ef6715SGao Feng pr_debug("after bind(): num = %hu, dif = %d\n", 442a7ef6715SGao Feng isk->inet_num, 443a7ef6715SGao Feng sk->sk_bound_dev_if); 444c319b4d7SVasiliy Kulikov 445c319b4d7SVasiliy Kulikov err = 0; 446c2bb06dbSEric Dumazet if (sk->sk_family == AF_INET && isk->inet_rcv_saddr) 447c319b4d7SVasiliy Kulikov sk->sk_userlocks |= SOCK_BINDADDR_LOCK; 448c2bb06dbSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 449c2bb06dbSEric Dumazet if (sk->sk_family == AF_INET6 && !ipv6_addr_any(&sk->sk_v6_rcv_saddr)) 450c2bb06dbSEric Dumazet sk->sk_userlocks |= SOCK_BINDADDR_LOCK; 451c2bb06dbSEric Dumazet #endif 4526d0bfe22SLorenzo Colitti 453c319b4d7SVasiliy Kulikov if (snum) 454c319b4d7SVasiliy Kulikov sk->sk_userlocks |= SOCK_BINDPORT_LOCK; 455c319b4d7SVasiliy Kulikov isk->inet_sport = htons(isk->inet_num); 456c319b4d7SVasiliy Kulikov isk->inet_daddr = 0; 457c319b4d7SVasiliy Kulikov isk->inet_dport = 0; 4586d0bfe22SLorenzo Colitti 4596d0bfe22SLorenzo Colitti #if IS_ENABLED(CONFIG_IPV6) 4606d0bfe22SLorenzo Colitti if (sk->sk_family == AF_INET6) 461efe4208fSEric Dumazet memset(&sk->sk_v6_daddr, 0, sizeof(sk->sk_v6_daddr)); 4626d0bfe22SLorenzo Colitti #endif 4636d0bfe22SLorenzo Colitti 464c319b4d7SVasiliy Kulikov sk_dst_reset(sk); 465c319b4d7SVasiliy Kulikov out: 466c319b4d7SVasiliy Kulikov release_sock(sk); 467c319b4d7SVasiliy Kulikov pr_debug("ping_v4_bind -> %d\n", err); 468c319b4d7SVasiliy Kulikov return err; 469c319b4d7SVasiliy Kulikov } 4706d0bfe22SLorenzo Colitti EXPORT_SYMBOL_GPL(ping_bind); 471c319b4d7SVasiliy Kulikov 472c319b4d7SVasiliy Kulikov /* 473c319b4d7SVasiliy Kulikov * Is this a supported type of ICMP message? 474c319b4d7SVasiliy Kulikov */ 475c319b4d7SVasiliy Kulikov 4766d0bfe22SLorenzo Colitti static inline int ping_supported(int family, int type, int code) 477c319b4d7SVasiliy Kulikov { 4786d0bfe22SLorenzo Colitti return (family == AF_INET && type == ICMP_ECHO && code == 0) || 47908baf54fSAndreas Roeseler (family == AF_INET && type == ICMP_EXT_ECHO && code == 0) || 48008baf54fSAndreas Roeseler (family == AF_INET6 && type == ICMPV6_ECHO_REQUEST && code == 0) || 48108baf54fSAndreas Roeseler (family == AF_INET6 && type == ICMPV6_EXT_ECHO_REQUEST && code == 0); 482c319b4d7SVasiliy Kulikov } 483c319b4d7SVasiliy Kulikov 484c319b4d7SVasiliy Kulikov /* 485c319b4d7SVasiliy Kulikov * This routine is called by the ICMP module when it gets some 486c319b4d7SVasiliy Kulikov * sort of error condition. 487c319b4d7SVasiliy Kulikov */ 488c319b4d7SVasiliy Kulikov 4896d0bfe22SLorenzo Colitti void ping_err(struct sk_buff *skb, int offset, u32 info) 490c319b4d7SVasiliy Kulikov { 4916d0bfe22SLorenzo Colitti int family; 4926d0bfe22SLorenzo Colitti struct icmphdr *icmph; 493c319b4d7SVasiliy Kulikov struct inet_sock *inet_sock; 4946d0bfe22SLorenzo Colitti int type; 4956d0bfe22SLorenzo Colitti int code; 496c319b4d7SVasiliy Kulikov struct net *net = dev_net(skb->dev); 497c319b4d7SVasiliy Kulikov struct sock *sk; 498c319b4d7SVasiliy Kulikov int harderr; 499c319b4d7SVasiliy Kulikov int err; 500c319b4d7SVasiliy Kulikov 5016d0bfe22SLorenzo Colitti if (skb->protocol == htons(ETH_P_IP)) { 5026d0bfe22SLorenzo Colitti family = AF_INET; 5036d0bfe22SLorenzo Colitti type = icmp_hdr(skb)->type; 5046d0bfe22SLorenzo Colitti code = icmp_hdr(skb)->code; 5056d0bfe22SLorenzo Colitti icmph = (struct icmphdr *)(skb->data + offset); 5066d0bfe22SLorenzo Colitti } else if (skb->protocol == htons(ETH_P_IPV6)) { 5076d0bfe22SLorenzo Colitti family = AF_INET6; 5086d0bfe22SLorenzo Colitti type = icmp6_hdr(skb)->icmp6_type; 5096d0bfe22SLorenzo Colitti code = icmp6_hdr(skb)->icmp6_code; 5106d0bfe22SLorenzo Colitti icmph = (struct icmphdr *) (skb->data + offset); 5116d0bfe22SLorenzo Colitti } else { 5126d0bfe22SLorenzo Colitti BUG(); 5136d0bfe22SLorenzo Colitti } 5146d0bfe22SLorenzo Colitti 515c319b4d7SVasiliy Kulikov /* We assume the packet has already been checked by icmp_unreach */ 516c319b4d7SVasiliy Kulikov 5176d0bfe22SLorenzo Colitti if (!ping_supported(family, icmph->type, icmph->code)) 518c319b4d7SVasiliy Kulikov return; 519c319b4d7SVasiliy Kulikov 5206d0bfe22SLorenzo Colitti pr_debug("ping_err(proto=0x%x,type=%d,code=%d,id=%04x,seq=%04x)\n", 5216d0bfe22SLorenzo Colitti skb->protocol, type, code, ntohs(icmph->un.echo.id), 5226d0bfe22SLorenzo Colitti ntohs(icmph->un.echo.sequence)); 523c319b4d7SVasiliy Kulikov 5246d0bfe22SLorenzo Colitti sk = ping_lookup(net, skb, ntohs(icmph->un.echo.id)); 52551456b29SIan Morris if (!sk) { 526c319b4d7SVasiliy Kulikov pr_debug("no socket, dropping\n"); 527c319b4d7SVasiliy Kulikov return; /* No socket for error */ 528c319b4d7SVasiliy Kulikov } 529c319b4d7SVasiliy Kulikov pr_debug("err on socket %p\n", sk); 530c319b4d7SVasiliy Kulikov 531c319b4d7SVasiliy Kulikov err = 0; 532c319b4d7SVasiliy Kulikov harderr = 0; 533c319b4d7SVasiliy Kulikov inet_sock = inet_sk(sk); 534c319b4d7SVasiliy Kulikov 5356d0bfe22SLorenzo Colitti if (skb->protocol == htons(ETH_P_IP)) { 536c319b4d7SVasiliy Kulikov switch (type) { 537c319b4d7SVasiliy Kulikov default: 538c319b4d7SVasiliy Kulikov case ICMP_TIME_EXCEEDED: 539c319b4d7SVasiliy Kulikov err = EHOSTUNREACH; 540c319b4d7SVasiliy Kulikov break; 541c319b4d7SVasiliy Kulikov case ICMP_SOURCE_QUENCH: 542c319b4d7SVasiliy Kulikov /* This is not a real error but ping wants to see it. 5436d0bfe22SLorenzo Colitti * Report it with some fake errno. 5446d0bfe22SLorenzo Colitti */ 545c319b4d7SVasiliy Kulikov err = EREMOTEIO; 546c319b4d7SVasiliy Kulikov break; 547c319b4d7SVasiliy Kulikov case ICMP_PARAMETERPROB: 548c319b4d7SVasiliy Kulikov err = EPROTO; 549c319b4d7SVasiliy Kulikov harderr = 1; 550c319b4d7SVasiliy Kulikov break; 551c319b4d7SVasiliy Kulikov case ICMP_DEST_UNREACH: 552c319b4d7SVasiliy Kulikov if (code == ICMP_FRAG_NEEDED) { /* Path MTU discovery */ 55336393395SDavid S. Miller ipv4_sk_update_pmtu(skb, sk, info); 554c319b4d7SVasiliy Kulikov if (inet_sock->pmtudisc != IP_PMTUDISC_DONT) { 555c319b4d7SVasiliy Kulikov err = EMSGSIZE; 556c319b4d7SVasiliy Kulikov harderr = 1; 557c319b4d7SVasiliy Kulikov break; 558c319b4d7SVasiliy Kulikov } 559c319b4d7SVasiliy Kulikov goto out; 560c319b4d7SVasiliy Kulikov } 561c319b4d7SVasiliy Kulikov err = EHOSTUNREACH; 562c319b4d7SVasiliy Kulikov if (code <= NR_ICMP_UNREACH) { 563c319b4d7SVasiliy Kulikov harderr = icmp_err_convert[code].fatal; 564c319b4d7SVasiliy Kulikov err = icmp_err_convert[code].errno; 565c319b4d7SVasiliy Kulikov } 566c319b4d7SVasiliy Kulikov break; 567c319b4d7SVasiliy Kulikov case ICMP_REDIRECT: 568c319b4d7SVasiliy Kulikov /* See ICMP_SOURCE_QUENCH */ 56955be7a9cSDavid S. Miller ipv4_sk_redirect(skb, sk); 570c319b4d7SVasiliy Kulikov err = EREMOTEIO; 571c319b4d7SVasiliy Kulikov break; 572c319b4d7SVasiliy Kulikov } 5736d0bfe22SLorenzo Colitti #if IS_ENABLED(CONFIG_IPV6) 5746d0bfe22SLorenzo Colitti } else if (skb->protocol == htons(ETH_P_IPV6)) { 5756d0bfe22SLorenzo Colitti harderr = pingv6_ops.icmpv6_err_convert(type, code, &err); 5766d0bfe22SLorenzo Colitti #endif 5776d0bfe22SLorenzo Colitti } 578c319b4d7SVasiliy Kulikov 579c319b4d7SVasiliy Kulikov /* 580c319b4d7SVasiliy Kulikov * RFC1122: OK. Passes ICMP errors back to application, as per 581c319b4d7SVasiliy Kulikov * 4.1.3.3. 582c319b4d7SVasiliy Kulikov */ 583*6b5f43eaSEric Dumazet if ((family == AF_INET && !inet_test_bit(RECVERR, sk)) || 5846d0bfe22SLorenzo Colitti (family == AF_INET6 && !inet6_sk(sk)->recverr)) { 585c319b4d7SVasiliy Kulikov if (!harderr || sk->sk_state != TCP_ESTABLISHED) 586c319b4d7SVasiliy Kulikov goto out; 587c319b4d7SVasiliy Kulikov } else { 5886d0bfe22SLorenzo Colitti if (family == AF_INET) { 589c319b4d7SVasiliy Kulikov ip_icmp_error(sk, skb, err, 0 /* no remote port */, 590c319b4d7SVasiliy Kulikov info, (u8 *)icmph); 5916d0bfe22SLorenzo Colitti #if IS_ENABLED(CONFIG_IPV6) 5926d0bfe22SLorenzo Colitti } else if (family == AF_INET6) { 5936d0bfe22SLorenzo Colitti pingv6_ops.ipv6_icmp_error(sk, skb, err, 0, 5946d0bfe22SLorenzo Colitti info, (u8 *)icmph); 5956d0bfe22SLorenzo Colitti #endif 5966d0bfe22SLorenzo Colitti } 597c319b4d7SVasiliy Kulikov } 598c319b4d7SVasiliy Kulikov sk->sk_err = err; 599e3ae2365SAlexander Aring sk_error_report(sk); 600c319b4d7SVasiliy Kulikov out: 601dbca1596SEric Dumazet return; 602c319b4d7SVasiliy Kulikov } 6036d0bfe22SLorenzo Colitti EXPORT_SYMBOL_GPL(ping_err); 604c319b4d7SVasiliy Kulikov 605c319b4d7SVasiliy Kulikov /* 6066d0bfe22SLorenzo Colitti * Copy and checksum an ICMP Echo packet from user space into a buffer 6076d0bfe22SLorenzo Colitti * starting from the payload. 608c319b4d7SVasiliy Kulikov */ 609c319b4d7SVasiliy Kulikov 6106d0bfe22SLorenzo Colitti int ping_getfrag(void *from, char *to, 611c319b4d7SVasiliy Kulikov int offset, int fraglen, int odd, struct sk_buff *skb) 612c319b4d7SVasiliy Kulikov { 6132e47eeceSYu Zhe struct pingfakehdr *pfh = from; 614c319b4d7SVasiliy Kulikov 6150b62fca2SAl Viro if (!csum_and_copy_from_iter_full(to, fraglen, &pfh->wcheck, 6160b62fca2SAl Viro &pfh->msg->msg_iter)) 617c319b4d7SVasiliy Kulikov return -EFAULT; 618c319b4d7SVasiliy Kulikov 6196d0bfe22SLorenzo Colitti #if IS_ENABLED(CONFIG_IPV6) 6206d0bfe22SLorenzo Colitti /* For IPv6, checksum each skb as we go along, as expected by 6216d0bfe22SLorenzo Colitti * icmpv6_push_pending_frames. For IPv4, accumulate the checksum in 6226d0bfe22SLorenzo Colitti * wcheck, it will be finalized in ping_v4_push_pending_frames. 6236d0bfe22SLorenzo Colitti */ 6246d0bfe22SLorenzo Colitti if (pfh->family == AF_INET6) { 62587445f36SEric Dumazet skb->csum = csum_block_add(skb->csum, pfh->wcheck, odd); 6266d0bfe22SLorenzo Colitti skb->ip_summed = CHECKSUM_NONE; 6276d0bfe22SLorenzo Colitti pfh->wcheck = 0; 6286d0bfe22SLorenzo Colitti } 6296d0bfe22SLorenzo Colitti #endif 6306d0bfe22SLorenzo Colitti 6316d0bfe22SLorenzo Colitti return 0; 6326d0bfe22SLorenzo Colitti } 6336d0bfe22SLorenzo Colitti EXPORT_SYMBOL_GPL(ping_getfrag); 6346d0bfe22SLorenzo Colitti 6356d0bfe22SLorenzo Colitti static int ping_v4_push_pending_frames(struct sock *sk, struct pingfakehdr *pfh, 63675e308c8SChangli Gao struct flowi4 *fl4) 637c319b4d7SVasiliy Kulikov { 638c319b4d7SVasiliy Kulikov struct sk_buff *skb = skb_peek(&sk->sk_write_queue); 639c319b4d7SVasiliy Kulikov 64073d2c667SWANG Cong if (!skb) 64173d2c667SWANG Cong return 0; 642c319b4d7SVasiliy Kulikov pfh->wcheck = csum_partial((char *)&pfh->icmph, 643c319b4d7SVasiliy Kulikov sizeof(struct icmphdr), pfh->wcheck); 644c319b4d7SVasiliy Kulikov pfh->icmph.checksum = csum_fold(pfh->wcheck); 645c319b4d7SVasiliy Kulikov memcpy(icmp_hdr(skb), &pfh->icmph, sizeof(struct icmphdr)); 646c319b4d7SVasiliy Kulikov skb->ip_summed = CHECKSUM_NONE; 647c319b4d7SVasiliy Kulikov return ip_push_pending_frames(sk, fl4); 648c319b4d7SVasiliy Kulikov } 649c319b4d7SVasiliy Kulikov 6506d0bfe22SLorenzo Colitti int ping_common_sendmsg(int family, struct msghdr *msg, size_t len, 6515af68891SMiaohe Lin void *user_icmph, size_t icmph_len) 6525af68891SMiaohe Lin { 6536d0bfe22SLorenzo Colitti u8 type, code; 6546d0bfe22SLorenzo Colitti 6556d0bfe22SLorenzo Colitti if (len > 0xFFFF) 6566d0bfe22SLorenzo Colitti return -EMSGSIZE; 6576d0bfe22SLorenzo Colitti 6580eab121eSKees Cook /* Must have at least a full ICMP header. */ 6590eab121eSKees Cook if (len < icmph_len) 6600eab121eSKees Cook return -EINVAL; 6610eab121eSKees Cook 6626d0bfe22SLorenzo Colitti /* 6636d0bfe22SLorenzo Colitti * Check the flags. 6646d0bfe22SLorenzo Colitti */ 6656d0bfe22SLorenzo Colitti 6666d0bfe22SLorenzo Colitti /* Mirror BSD error message compatibility */ 6676d0bfe22SLorenzo Colitti if (msg->msg_flags & MSG_OOB) 6686d0bfe22SLorenzo Colitti return -EOPNOTSUPP; 6696d0bfe22SLorenzo Colitti 6706d0bfe22SLorenzo Colitti /* 6716d0bfe22SLorenzo Colitti * Fetch the ICMP header provided by the userland. 6726d0bfe22SLorenzo Colitti * iovec is modified! The ICMP header is consumed. 6736d0bfe22SLorenzo Colitti */ 6746ce8e9ceSAl Viro if (memcpy_from_msg(user_icmph, msg, icmph_len)) 6756d0bfe22SLorenzo Colitti return -EFAULT; 6766d0bfe22SLorenzo Colitti 6776d0bfe22SLorenzo Colitti if (family == AF_INET) { 6786d0bfe22SLorenzo Colitti type = ((struct icmphdr *) user_icmph)->type; 6796d0bfe22SLorenzo Colitti code = ((struct icmphdr *) user_icmph)->code; 6806d0bfe22SLorenzo Colitti #if IS_ENABLED(CONFIG_IPV6) 6816d0bfe22SLorenzo Colitti } else if (family == AF_INET6) { 6826d0bfe22SLorenzo Colitti type = ((struct icmp6hdr *) user_icmph)->icmp6_type; 6836d0bfe22SLorenzo Colitti code = ((struct icmp6hdr *) user_icmph)->icmp6_code; 6846d0bfe22SLorenzo Colitti #endif 6856d0bfe22SLorenzo Colitti } else { 6866d0bfe22SLorenzo Colitti BUG(); 6876d0bfe22SLorenzo Colitti } 6886d0bfe22SLorenzo Colitti 6896d0bfe22SLorenzo Colitti if (!ping_supported(family, type, code)) 6906d0bfe22SLorenzo Colitti return -EINVAL; 6916d0bfe22SLorenzo Colitti 6926d0bfe22SLorenzo Colitti return 0; 6936d0bfe22SLorenzo Colitti } 6946d0bfe22SLorenzo Colitti EXPORT_SYMBOL_GPL(ping_common_sendmsg); 6956d0bfe22SLorenzo Colitti 6961b784140SYing Xue static int ping_v4_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) 697c319b4d7SVasiliy Kulikov { 698c319b4d7SVasiliy Kulikov struct net *net = sock_net(sk); 699c319b4d7SVasiliy Kulikov struct flowi4 fl4; 700c319b4d7SVasiliy Kulikov struct inet_sock *inet = inet_sk(sk); 701c319b4d7SVasiliy Kulikov struct ipcm_cookie ipc; 702c319b4d7SVasiliy Kulikov struct icmphdr user_icmph; 703c319b4d7SVasiliy Kulikov struct pingfakehdr pfh; 704c319b4d7SVasiliy Kulikov struct rtable *rt = NULL; 705c319b4d7SVasiliy Kulikov struct ip_options_data opt_copy; 706c319b4d7SVasiliy Kulikov int free = 0; 707747465efSEric Dumazet __be32 saddr, daddr, faddr; 708726de790SGuillaume Nault u8 tos, scope; 709c319b4d7SVasiliy Kulikov int err; 710c319b4d7SVasiliy Kulikov 7116d0bfe22SLorenzo Colitti pr_debug("ping_v4_sendmsg(sk=%p,sk->num=%u)\n", inet, inet->inet_num); 712c319b4d7SVasiliy Kulikov 7136d0bfe22SLorenzo Colitti err = ping_common_sendmsg(AF_INET, msg, len, &user_icmph, 7146d0bfe22SLorenzo Colitti sizeof(user_icmph)); 7156d0bfe22SLorenzo Colitti if (err) 7166d0bfe22SLorenzo Colitti return err; 717c319b4d7SVasiliy Kulikov 718c319b4d7SVasiliy Kulikov /* 719c319b4d7SVasiliy Kulikov * Get and verify the address. 720c319b4d7SVasiliy Kulikov */ 721c319b4d7SVasiliy Kulikov 722c319b4d7SVasiliy Kulikov if (msg->msg_name) { 723342dfc30SSteffen Hurrle DECLARE_SOCKADDR(struct sockaddr_in *, usin, msg->msg_name); 724c319b4d7SVasiliy Kulikov if (msg->msg_namelen < sizeof(*usin)) 725c319b4d7SVasiliy Kulikov return -EINVAL; 726c319b4d7SVasiliy Kulikov if (usin->sin_family != AF_INET) 7279145736dSLorenzo Colitti return -EAFNOSUPPORT; 728c319b4d7SVasiliy Kulikov daddr = usin->sin_addr.s_addr; 729c319b4d7SVasiliy Kulikov /* no remote port */ 730c319b4d7SVasiliy Kulikov } else { 731c319b4d7SVasiliy Kulikov if (sk->sk_state != TCP_ESTABLISHED) 732c319b4d7SVasiliy Kulikov return -EDESTADDRREQ; 733c319b4d7SVasiliy Kulikov daddr = inet->inet_daddr; 734c319b4d7SVasiliy Kulikov /* no remote port */ 735c319b4d7SVasiliy Kulikov } 736c319b4d7SVasiliy Kulikov 73735178206SWillem de Bruijn ipcm_init_sk(&ipc, inet); 738bf84a010SDaniel Borkmann 739c319b4d7SVasiliy Kulikov if (msg->msg_controllen) { 74024025c46SSoheil Hassas Yeganeh err = ip_cmsg_send(sk, msg, &ipc, false); 74191948309SEric Dumazet if (unlikely(err)) { 74291948309SEric Dumazet kfree(ipc.opt); 743c319b4d7SVasiliy Kulikov return err; 74491948309SEric Dumazet } 745c319b4d7SVasiliy Kulikov if (ipc.opt) 746c319b4d7SVasiliy Kulikov free = 1; 747c319b4d7SVasiliy Kulikov } 748c319b4d7SVasiliy Kulikov if (!ipc.opt) { 749c319b4d7SVasiliy Kulikov struct ip_options_rcu *inet_opt; 750c319b4d7SVasiliy Kulikov 751c319b4d7SVasiliy Kulikov rcu_read_lock(); 752c319b4d7SVasiliy Kulikov inet_opt = rcu_dereference(inet->inet_opt); 753c319b4d7SVasiliy Kulikov if (inet_opt) { 754c319b4d7SVasiliy Kulikov memcpy(&opt_copy, inet_opt, 755c319b4d7SVasiliy Kulikov sizeof(*inet_opt) + inet_opt->opt.optlen); 756c319b4d7SVasiliy Kulikov ipc.opt = &opt_copy.opt; 757c319b4d7SVasiliy Kulikov } 758c319b4d7SVasiliy Kulikov rcu_read_unlock(); 759c319b4d7SVasiliy Kulikov } 760c319b4d7SVasiliy Kulikov 761c319b4d7SVasiliy Kulikov saddr = ipc.addr; 762c319b4d7SVasiliy Kulikov ipc.addr = faddr = daddr; 763c319b4d7SVasiliy Kulikov 764c319b4d7SVasiliy Kulikov if (ipc.opt && ipc.opt->opt.srr) { 7651b97013bSAndrey Ignatov if (!daddr) { 7661b97013bSAndrey Ignatov err = -EINVAL; 7671b97013bSAndrey Ignatov goto out_free; 7681b97013bSAndrey Ignatov } 769c319b4d7SVasiliy Kulikov faddr = ipc.opt->opt.faddr; 770c319b4d7SVasiliy Kulikov } 771aa661581SFrancesco Fusco tos = get_rttos(&ipc, inet); 772726de790SGuillaume Nault scope = ip_sendmsg_scope(inet, &ipc, msg); 773c319b4d7SVasiliy Kulikov 774c319b4d7SVasiliy Kulikov if (ipv4_is_multicast(daddr)) { 775854da991SRobert Shearman if (!ipc.oif || netif_index_is_l3_master(sock_net(sk), ipc.oif)) 776c319b4d7SVasiliy Kulikov ipc.oif = inet->mc_index; 777c319b4d7SVasiliy Kulikov if (!saddr) 778c319b4d7SVasiliy Kulikov saddr = inet->mc_addr; 77976e21053SErich E. Hoover } else if (!ipc.oif) 78076e21053SErich E. Hoover ipc.oif = inet->uc_index; 781c319b4d7SVasiliy Kulikov 782726de790SGuillaume Nault flowi4_init_output(&fl4, ipc.oif, ipc.sockc.mark, tos, scope, 783726de790SGuillaume Nault sk->sk_protocol, inet_sk_flowi_flags(sk), faddr, 784726de790SGuillaume Nault saddr, 0, 0, sk->sk_uid); 785c319b4d7SVasiliy Kulikov 7865eff0690SSabrina Dubroca fl4.fl4_icmp_type = user_icmph.type; 7875eff0690SSabrina Dubroca fl4.fl4_icmp_code = user_icmph.code; 7885eff0690SSabrina Dubroca 7893df98d79SPaul Moore security_sk_classify_flow(sk, flowi4_to_flowi_common(&fl4)); 790c319b4d7SVasiliy Kulikov rt = ip_route_output_flow(net, &fl4, sk); 791c319b4d7SVasiliy Kulikov if (IS_ERR(rt)) { 792c319b4d7SVasiliy Kulikov err = PTR_ERR(rt); 793c319b4d7SVasiliy Kulikov rt = NULL; 794c319b4d7SVasiliy Kulikov if (err == -ENETUNREACH) 795f1d8cba6SEric Dumazet IP_INC_STATS(net, IPSTATS_MIB_OUTNOROUTES); 796c319b4d7SVasiliy Kulikov goto out; 797c319b4d7SVasiliy Kulikov } 798c319b4d7SVasiliy Kulikov 799c319b4d7SVasiliy Kulikov err = -EACCES; 800c319b4d7SVasiliy Kulikov if ((rt->rt_flags & RTCF_BROADCAST) && 801c319b4d7SVasiliy Kulikov !sock_flag(sk, SOCK_BROADCAST)) 802c319b4d7SVasiliy Kulikov goto out; 803c319b4d7SVasiliy Kulikov 804c319b4d7SVasiliy Kulikov if (msg->msg_flags & MSG_CONFIRM) 805c319b4d7SVasiliy Kulikov goto do_confirm; 806c319b4d7SVasiliy Kulikov back_from_confirm: 807c319b4d7SVasiliy Kulikov 808c319b4d7SVasiliy Kulikov if (!ipc.addr) 809c319b4d7SVasiliy Kulikov ipc.addr = fl4.daddr; 810c319b4d7SVasiliy Kulikov 811c319b4d7SVasiliy Kulikov lock_sock(sk); 812c319b4d7SVasiliy Kulikov 813c319b4d7SVasiliy Kulikov pfh.icmph.type = user_icmph.type; /* already checked */ 814c319b4d7SVasiliy Kulikov pfh.icmph.code = user_icmph.code; /* ditto */ 815c319b4d7SVasiliy Kulikov pfh.icmph.checksum = 0; 816c319b4d7SVasiliy Kulikov pfh.icmph.un.echo.id = inet->inet_sport; 817c319b4d7SVasiliy Kulikov pfh.icmph.un.echo.sequence = user_icmph.un.echo.sequence; 818cacdc7d2SAl Viro pfh.msg = msg; 819c319b4d7SVasiliy Kulikov pfh.wcheck = 0; 8206d0bfe22SLorenzo Colitti pfh.family = AF_INET; 821c319b4d7SVasiliy Kulikov 822c319b4d7SVasiliy Kulikov err = ip_append_data(sk, &fl4, ping_getfrag, &pfh, len, 8230d24148bSEric Dumazet sizeof(struct icmphdr), &ipc, &rt, 8240d24148bSEric Dumazet msg->msg_flags); 825c319b4d7SVasiliy Kulikov if (err) 826c319b4d7SVasiliy Kulikov ip_flush_pending_frames(sk); 827c319b4d7SVasiliy Kulikov else 8286d0bfe22SLorenzo Colitti err = ping_v4_push_pending_frames(sk, &pfh, &fl4); 829c319b4d7SVasiliy Kulikov release_sock(sk); 830c319b4d7SVasiliy Kulikov 831c319b4d7SVasiliy Kulikov out: 832c319b4d7SVasiliy Kulikov ip_rt_put(rt); 8331b97013bSAndrey Ignatov out_free: 834c319b4d7SVasiliy Kulikov if (free) 835c319b4d7SVasiliy Kulikov kfree(ipc.opt); 836c319b4d7SVasiliy Kulikov if (!err) { 837c319b4d7SVasiliy Kulikov icmp_out_count(sock_net(sk), user_icmph.type); 838c319b4d7SVasiliy Kulikov return len; 839c319b4d7SVasiliy Kulikov } 840c319b4d7SVasiliy Kulikov return err; 841c319b4d7SVasiliy Kulikov 842c319b4d7SVasiliy Kulikov do_confirm: 8430dec879fSJulian Anastasov if (msg->msg_flags & MSG_PROBE) 8440dec879fSJulian Anastasov dst_confirm_neigh(&rt->dst, &fl4.daddr); 845c319b4d7SVasiliy Kulikov if (!(msg->msg_flags & MSG_PROBE) || len) 846c319b4d7SVasiliy Kulikov goto back_from_confirm; 847c319b4d7SVasiliy Kulikov err = 0; 848c319b4d7SVasiliy Kulikov goto out; 849c319b4d7SVasiliy Kulikov } 850c319b4d7SVasiliy Kulikov 851ec095263SOliver Hartkopp int ping_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int flags, 852ec095263SOliver Hartkopp int *addr_len) 853c319b4d7SVasiliy Kulikov { 854c319b4d7SVasiliy Kulikov struct inet_sock *isk = inet_sk(sk); 8556d0bfe22SLorenzo Colitti int family = sk->sk_family; 856c319b4d7SVasiliy Kulikov struct sk_buff *skb; 857c319b4d7SVasiliy Kulikov int copied, err; 858c319b4d7SVasiliy Kulikov 859c319b4d7SVasiliy Kulikov pr_debug("ping_recvmsg(sk=%p,sk->num=%u)\n", isk, isk->inet_num); 860c319b4d7SVasiliy Kulikov 861a5e7424dSDavid S. Miller err = -EOPNOTSUPP; 862c319b4d7SVasiliy Kulikov if (flags & MSG_OOB) 863c319b4d7SVasiliy Kulikov goto out; 864c319b4d7SVasiliy Kulikov 865f4713a3dSWillem de Bruijn if (flags & MSG_ERRQUEUE) 866f4713a3dSWillem de Bruijn return inet_recv_error(sk, msg, len, addr_len); 867c319b4d7SVasiliy Kulikov 868f4b41f06SOliver Hartkopp skb = skb_recv_datagram(sk, flags, &err); 869c319b4d7SVasiliy Kulikov if (!skb) 870c319b4d7SVasiliy Kulikov goto out; 871c319b4d7SVasiliy Kulikov 872c319b4d7SVasiliy Kulikov copied = skb->len; 873c319b4d7SVasiliy Kulikov if (copied > len) { 874c319b4d7SVasiliy Kulikov msg->msg_flags |= MSG_TRUNC; 875c319b4d7SVasiliy Kulikov copied = len; 876c319b4d7SVasiliy Kulikov } 877c319b4d7SVasiliy Kulikov 878c319b4d7SVasiliy Kulikov /* Don't bother checking the checksum */ 87951f3d02bSDavid S. Miller err = skb_copy_datagram_msg(skb, 0, msg, copied); 880c319b4d7SVasiliy Kulikov if (err) 881c319b4d7SVasiliy Kulikov goto done; 882c319b4d7SVasiliy Kulikov 883c319b4d7SVasiliy Kulikov sock_recv_timestamp(msg, sk, skb); 884c319b4d7SVasiliy Kulikov 8856d0bfe22SLorenzo Colitti /* Copy the address and add cmsg data. */ 8866d0bfe22SLorenzo Colitti if (family == AF_INET) { 887342dfc30SSteffen Hurrle DECLARE_SOCKADDR(struct sockaddr_in *, sin, msg->msg_name); 888bceaa902SHannes Frederic Sowa 889cf970c00SHannes Frederic Sowa if (sin) { 890c319b4d7SVasiliy Kulikov sin->sin_family = AF_INET; 891c319b4d7SVasiliy Kulikov sin->sin_port = 0 /* skb->h.uh->source */; 892c319b4d7SVasiliy Kulikov sin->sin_addr.s_addr = ip_hdr(skb)->saddr; 893c319b4d7SVasiliy Kulikov memset(sin->sin_zero, 0, sizeof(sin->sin_zero)); 894bceaa902SHannes Frederic Sowa *addr_len = sizeof(*sin); 895cf970c00SHannes Frederic Sowa } 8966d0bfe22SLorenzo Colitti 897c274af22SEric Dumazet if (inet_cmsg_flags(isk)) 898c319b4d7SVasiliy Kulikov ip_cmsg_recv(msg, skb); 8996d0bfe22SLorenzo Colitti 9006d0bfe22SLorenzo Colitti #if IS_ENABLED(CONFIG_IPV6) 9016d0bfe22SLorenzo Colitti } else if (family == AF_INET6) { 9026d0bfe22SLorenzo Colitti struct ipv6_pinfo *np = inet6_sk(sk); 9036d0bfe22SLorenzo Colitti struct ipv6hdr *ip6 = ipv6_hdr(skb); 904342dfc30SSteffen Hurrle DECLARE_SOCKADDR(struct sockaddr_in6 *, sin6, msg->msg_name); 905bceaa902SHannes Frederic Sowa 906cf970c00SHannes Frederic Sowa if (sin6) { 9076d0bfe22SLorenzo Colitti sin6->sin6_family = AF_INET6; 9086d0bfe22SLorenzo Colitti sin6->sin6_port = 0; 9096d0bfe22SLorenzo Colitti sin6->sin6_addr = ip6->saddr; 910c26d6b46SCong Wang sin6->sin6_flowinfo = 0; 9116d0bfe22SLorenzo Colitti if (np->sndflow) 9126d0bfe22SLorenzo Colitti sin6->sin6_flowinfo = ip6_flowinfo(ip6); 913cf970c00SHannes Frederic Sowa sin6->sin6_scope_id = 914cf970c00SHannes Frederic Sowa ipv6_iface_scope_id(&sin6->sin6_addr, 9154330487aSDuan Jiong inet6_iif(skb)); 916bceaa902SHannes Frederic Sowa *addr_len = sizeof(*sin6); 917cf970c00SHannes Frederic Sowa } 9186d0bfe22SLorenzo Colitti 9196d0bfe22SLorenzo Colitti if (inet6_sk(sk)->rxopt.all) 9204b261c75SHannes Frederic Sowa pingv6_ops.ip6_datagram_recv_common_ctl(sk, msg, skb); 9214b261c75SHannes Frederic Sowa if (skb->protocol == htons(ETH_P_IPV6) && 9224b261c75SHannes Frederic Sowa inet6_sk(sk)->rxopt.all) 9234b261c75SHannes Frederic Sowa pingv6_ops.ip6_datagram_recv_specific_ctl(sk, msg, skb); 924c274af22SEric Dumazet else if (skb->protocol == htons(ETH_P_IP) && 925c274af22SEric Dumazet inet_cmsg_flags(isk)) 9264b261c75SHannes Frederic Sowa ip_cmsg_recv(msg, skb); 9276d0bfe22SLorenzo Colitti #endif 9286d0bfe22SLorenzo Colitti } else { 9296d0bfe22SLorenzo Colitti BUG(); 9306d0bfe22SLorenzo Colitti } 9316d0bfe22SLorenzo Colitti 932c319b4d7SVasiliy Kulikov err = copied; 933c319b4d7SVasiliy Kulikov 934c319b4d7SVasiliy Kulikov done: 935c319b4d7SVasiliy Kulikov skb_free_datagram(sk, skb); 936c319b4d7SVasiliy Kulikov out: 937c319b4d7SVasiliy Kulikov pr_debug("ping_recvmsg -> %d\n", err); 938c319b4d7SVasiliy Kulikov return err; 939c319b4d7SVasiliy Kulikov } 9406d0bfe22SLorenzo Colitti EXPORT_SYMBOL_GPL(ping_recvmsg); 941c319b4d7SVasiliy Kulikov 94241a95a00SMenglong Dong static enum skb_drop_reason __ping_queue_rcv_skb(struct sock *sk, 94341a95a00SMenglong Dong struct sk_buff *skb) 944c319b4d7SVasiliy Kulikov { 94541a95a00SMenglong Dong enum skb_drop_reason reason; 94641a95a00SMenglong Dong 947c319b4d7SVasiliy Kulikov pr_debug("ping_queue_rcv_skb(sk=%p,sk->num=%d,skb=%p)\n", 948c319b4d7SVasiliy Kulikov inet_sk(sk), inet_sk(sk)->inet_num, skb); 94941a95a00SMenglong Dong if (sock_queue_rcv_skb_reason(sk, skb, &reason) < 0) { 95041a95a00SMenglong Dong kfree_skb_reason(skb, reason); 951c319b4d7SVasiliy Kulikov pr_debug("ping_queue_rcv_skb -> failed\n"); 95241a95a00SMenglong Dong return reason; 953c319b4d7SVasiliy Kulikov } 95441a95a00SMenglong Dong return SKB_NOT_DROPPED_YET; 95541a95a00SMenglong Dong } 95641a95a00SMenglong Dong 95741a95a00SMenglong Dong int ping_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) 95841a95a00SMenglong Dong { 95941a95a00SMenglong Dong return __ping_queue_rcv_skb(sk, skb) ? -1 : 0; 960c319b4d7SVasiliy Kulikov } 9616d0bfe22SLorenzo Colitti EXPORT_SYMBOL_GPL(ping_queue_rcv_skb); 962c319b4d7SVasiliy Kulikov 963c319b4d7SVasiliy Kulikov 964c319b4d7SVasiliy Kulikov /* 965c319b4d7SVasiliy Kulikov * All we need to do is get the socket. 966c319b4d7SVasiliy Kulikov */ 967c319b4d7SVasiliy Kulikov 968b384c95aSMenglong Dong enum skb_drop_reason ping_rcv(struct sk_buff *skb) 969c319b4d7SVasiliy Kulikov { 970b384c95aSMenglong Dong enum skb_drop_reason reason = SKB_DROP_REASON_NO_SOCKET; 971c319b4d7SVasiliy Kulikov struct sock *sk; 972c319b4d7SVasiliy Kulikov struct net *net = dev_net(skb->dev); 973c319b4d7SVasiliy Kulikov struct icmphdr *icmph = icmp_hdr(skb); 974c319b4d7SVasiliy Kulikov 975c319b4d7SVasiliy Kulikov /* We assume the packet has already been checked by icmp_rcv */ 976c319b4d7SVasiliy Kulikov 977c319b4d7SVasiliy Kulikov pr_debug("ping_rcv(skb=%p,id=%04x,seq=%04x)\n", 978c319b4d7SVasiliy Kulikov skb, ntohs(icmph->un.echo.id), ntohs(icmph->un.echo.sequence)); 979c319b4d7SVasiliy Kulikov 980c319b4d7SVasiliy Kulikov /* Push ICMP header back */ 981c319b4d7SVasiliy Kulikov skb_push(skb, skb->data - (u8 *)icmph); 982c319b4d7SVasiliy Kulikov 9836d0bfe22SLorenzo Colitti sk = ping_lookup(net, skb, ntohs(icmph->un.echo.id)); 98400db4124SIan Morris if (sk) { 985fc752f1fSsubashab@codeaurora.org struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC); 986fc752f1fSsubashab@codeaurora.org 987c319b4d7SVasiliy Kulikov pr_debug("rcv on socket %p\n", sk); 988b384c95aSMenglong Dong if (skb2) 989b384c95aSMenglong Dong reason = __ping_queue_rcv_skb(sk, skb2); 990b384c95aSMenglong Dong else 991b384c95aSMenglong Dong reason = SKB_DROP_REASON_NOMEM; 992c319b4d7SVasiliy Kulikov } 9939d44fa3eSZheng Yongjun 994b384c95aSMenglong Dong if (reason) 995c319b4d7SVasiliy Kulikov pr_debug("no socket, dropping\n"); 996c319b4d7SVasiliy Kulikov 997b384c95aSMenglong Dong return reason; 998c319b4d7SVasiliy Kulikov } 9996d0bfe22SLorenzo Colitti EXPORT_SYMBOL_GPL(ping_rcv); 1000c319b4d7SVasiliy Kulikov 1001c319b4d7SVasiliy Kulikov struct proto ping_prot = { 1002c319b4d7SVasiliy Kulikov .name = "PING", 1003c319b4d7SVasiliy Kulikov .owner = THIS_MODULE, 1004c319b4d7SVasiliy Kulikov .init = ping_init_sock, 1005c319b4d7SVasiliy Kulikov .close = ping_close, 10060ffe2412SYiFei Zhu .pre_connect = ping_pre_connect, 1007c319b4d7SVasiliy Kulikov .connect = ip4_datagram_connect, 1008286c72deSEric Dumazet .disconnect = __udp_disconnect, 1009c319b4d7SVasiliy Kulikov .setsockopt = ip_setsockopt, 1010c319b4d7SVasiliy Kulikov .getsockopt = ip_getsockopt, 10116d0bfe22SLorenzo Colitti .sendmsg = ping_v4_sendmsg, 1012c319b4d7SVasiliy Kulikov .recvmsg = ping_recvmsg, 1013c319b4d7SVasiliy Kulikov .bind = ping_bind, 1014c319b4d7SVasiliy Kulikov .backlog_rcv = ping_queue_rcv_skb, 10158141ed9fSSteffen Klassert .release_cb = ip4_datagram_release_cb, 10166d0bfe22SLorenzo Colitti .hash = ping_hash, 10176d0bfe22SLorenzo Colitti .unhash = ping_unhash, 10186d0bfe22SLorenzo Colitti .get_port = ping_get_port, 101991a760b2SMenglong Dong .put_port = ping_unhash, 1020c319b4d7SVasiliy Kulikov .obj_size = sizeof(struct inet_sock), 1021c319b4d7SVasiliy Kulikov }; 1022c319b4d7SVasiliy Kulikov EXPORT_SYMBOL(ping_prot); 1023c319b4d7SVasiliy Kulikov 1024c319b4d7SVasiliy Kulikov #ifdef CONFIG_PROC_FS 1025c319b4d7SVasiliy Kulikov 1026c319b4d7SVasiliy Kulikov static struct sock *ping_get_first(struct seq_file *seq, int start) 1027c319b4d7SVasiliy Kulikov { 1028c319b4d7SVasiliy Kulikov struct sock *sk; 1029c319b4d7SVasiliy Kulikov struct ping_iter_state *state = seq->private; 1030c319b4d7SVasiliy Kulikov struct net *net = seq_file_net(seq); 1031c319b4d7SVasiliy Kulikov 1032c319b4d7SVasiliy Kulikov for (state->bucket = start; state->bucket < PING_HTABLE_SIZE; 1033c319b4d7SVasiliy Kulikov ++state->bucket) { 1034f1b5dfe6SKuniyuki Iwashima struct hlist_head *hslot; 103575e308c8SChangli Gao 103675e308c8SChangli Gao hslot = &ping_table.hash[state->bucket]; 1037c319b4d7SVasiliy Kulikov 1038f1b5dfe6SKuniyuki Iwashima if (hlist_empty(hslot)) 1039c319b4d7SVasiliy Kulikov continue; 1040c319b4d7SVasiliy Kulikov 1041f1b5dfe6SKuniyuki Iwashima sk_for_each(sk, hslot) { 10428cc785f6SLorenzo Colitti if (net_eq(sock_net(sk), net) && 10438cc785f6SLorenzo Colitti sk->sk_family == state->family) 1044c319b4d7SVasiliy Kulikov goto found; 1045c319b4d7SVasiliy Kulikov } 1046c319b4d7SVasiliy Kulikov } 1047c319b4d7SVasiliy Kulikov sk = NULL; 1048c319b4d7SVasiliy Kulikov found: 1049c319b4d7SVasiliy Kulikov return sk; 1050c319b4d7SVasiliy Kulikov } 1051c319b4d7SVasiliy Kulikov 1052c319b4d7SVasiliy Kulikov static struct sock *ping_get_next(struct seq_file *seq, struct sock *sk) 1053c319b4d7SVasiliy Kulikov { 1054c319b4d7SVasiliy Kulikov struct ping_iter_state *state = seq->private; 1055c319b4d7SVasiliy Kulikov struct net *net = seq_file_net(seq); 1056c319b4d7SVasiliy Kulikov 1057c319b4d7SVasiliy Kulikov do { 1058f1b5dfe6SKuniyuki Iwashima sk = sk_next(sk); 1059c319b4d7SVasiliy Kulikov } while (sk && (!net_eq(sock_net(sk), net))); 1060c319b4d7SVasiliy Kulikov 1061c319b4d7SVasiliy Kulikov if (!sk) 1062c319b4d7SVasiliy Kulikov return ping_get_first(seq, state->bucket + 1); 1063c319b4d7SVasiliy Kulikov return sk; 1064c319b4d7SVasiliy Kulikov } 1065c319b4d7SVasiliy Kulikov 1066c319b4d7SVasiliy Kulikov static struct sock *ping_get_idx(struct seq_file *seq, loff_t pos) 1067c319b4d7SVasiliy Kulikov { 1068c319b4d7SVasiliy Kulikov struct sock *sk = ping_get_first(seq, 0); 1069c319b4d7SVasiliy Kulikov 1070c319b4d7SVasiliy Kulikov if (sk) 1071c319b4d7SVasiliy Kulikov while (pos && (sk = ping_get_next(seq, sk)) != NULL) 1072c319b4d7SVasiliy Kulikov --pos; 1073c319b4d7SVasiliy Kulikov return pos ? NULL : sk; 1074c319b4d7SVasiliy Kulikov } 1075c319b4d7SVasiliy Kulikov 1076d862e546SLorenzo Colitti void *ping_seq_start(struct seq_file *seq, loff_t *pos, sa_family_t family) 1077ab5fb73fSKuniyuki Iwashima __acquires(ping_table.lock) 1078c319b4d7SVasiliy Kulikov { 1079c319b4d7SVasiliy Kulikov struct ping_iter_state *state = seq->private; 1080c319b4d7SVasiliy Kulikov state->bucket = 0; 10818cc785f6SLorenzo Colitti state->family = family; 1082c319b4d7SVasiliy Kulikov 1083ab5fb73fSKuniyuki Iwashima spin_lock(&ping_table.lock); 1084c319b4d7SVasiliy Kulikov 1085c319b4d7SVasiliy Kulikov return *pos ? ping_get_idx(seq, *pos-1) : SEQ_START_TOKEN; 1086c319b4d7SVasiliy Kulikov } 1087d862e546SLorenzo Colitti EXPORT_SYMBOL_GPL(ping_seq_start); 1088c319b4d7SVasiliy Kulikov 10898cc785f6SLorenzo Colitti static void *ping_v4_seq_start(struct seq_file *seq, loff_t *pos) 10908cc785f6SLorenzo Colitti { 10918cc785f6SLorenzo Colitti return ping_seq_start(seq, pos, AF_INET); 10928cc785f6SLorenzo Colitti } 10938cc785f6SLorenzo Colitti 1094d862e546SLorenzo Colitti void *ping_seq_next(struct seq_file *seq, void *v, loff_t *pos) 1095c319b4d7SVasiliy Kulikov { 1096c319b4d7SVasiliy Kulikov struct sock *sk; 1097c319b4d7SVasiliy Kulikov 1098c319b4d7SVasiliy Kulikov if (v == SEQ_START_TOKEN) 1099c319b4d7SVasiliy Kulikov sk = ping_get_idx(seq, 0); 1100c319b4d7SVasiliy Kulikov else 1101c319b4d7SVasiliy Kulikov sk = ping_get_next(seq, v); 1102c319b4d7SVasiliy Kulikov 1103c319b4d7SVasiliy Kulikov ++*pos; 1104c319b4d7SVasiliy Kulikov return sk; 1105c319b4d7SVasiliy Kulikov } 1106d862e546SLorenzo Colitti EXPORT_SYMBOL_GPL(ping_seq_next); 1107c319b4d7SVasiliy Kulikov 1108d862e546SLorenzo Colitti void ping_seq_stop(struct seq_file *seq, void *v) 1109ab5fb73fSKuniyuki Iwashima __releases(ping_table.lock) 1110c319b4d7SVasiliy Kulikov { 1111ab5fb73fSKuniyuki Iwashima spin_unlock(&ping_table.lock); 1112c319b4d7SVasiliy Kulikov } 1113d862e546SLorenzo Colitti EXPORT_SYMBOL_GPL(ping_seq_stop); 1114c319b4d7SVasiliy Kulikov 11158cc785f6SLorenzo Colitti static void ping_v4_format_sock(struct sock *sp, struct seq_file *f, 1116652586dfSTetsuo Handa int bucket) 1117c319b4d7SVasiliy Kulikov { 1118c319b4d7SVasiliy Kulikov struct inet_sock *inet = inet_sk(sp); 1119c319b4d7SVasiliy Kulikov __be32 dest = inet->inet_daddr; 1120c319b4d7SVasiliy Kulikov __be32 src = inet->inet_rcv_saddr; 1121c319b4d7SVasiliy Kulikov __u16 destp = ntohs(inet->inet_dport); 1122c319b4d7SVasiliy Kulikov __u16 srcp = ntohs(inet->inet_sport); 1123c319b4d7SVasiliy Kulikov 1124c319b4d7SVasiliy Kulikov seq_printf(f, "%5d: %08X:%04X %08X:%04X" 1125ea9a0379SPatrick Talbert " %02X %08X:%08X %02X:%08lX %08X %5u %8d %lu %d %pK %u", 1126c319b4d7SVasiliy Kulikov bucket, src, srcp, dest, destp, sp->sk_state, 1127c319b4d7SVasiliy Kulikov sk_wmem_alloc_get(sp), 1128c319b4d7SVasiliy Kulikov sk_rmem_alloc_get(sp), 1129a7cb5a49SEric W. Biederman 0, 0L, 0, 1130a7cb5a49SEric W. Biederman from_kuid_munged(seq_user_ns(f), sock_i_uid(sp)), 1131a7cb5a49SEric W. Biederman 0, sock_i_ino(sp), 113241c6d650SReshetova, Elena refcount_read(&sp->sk_refcnt), sp, 1133652586dfSTetsuo Handa atomic_read(&sp->sk_drops)); 1134c319b4d7SVasiliy Kulikov } 1135c319b4d7SVasiliy Kulikov 11368cc785f6SLorenzo Colitti static int ping_v4_seq_show(struct seq_file *seq, void *v) 1137c319b4d7SVasiliy Kulikov { 1138652586dfSTetsuo Handa seq_setwidth(seq, 127); 1139c319b4d7SVasiliy Kulikov if (v == SEQ_START_TOKEN) 1140652586dfSTetsuo Handa seq_puts(seq, " sl local_address rem_address st tx_queue " 1141c319b4d7SVasiliy Kulikov "rx_queue tr tm->when retrnsmt uid timeout " 1142c319b4d7SVasiliy Kulikov "inode ref pointer drops"); 1143c319b4d7SVasiliy Kulikov else { 1144c319b4d7SVasiliy Kulikov struct ping_iter_state *state = seq->private; 1145c319b4d7SVasiliy Kulikov 1146652586dfSTetsuo Handa ping_v4_format_sock(v, seq, state->bucket); 1147c319b4d7SVasiliy Kulikov } 1148652586dfSTetsuo Handa seq_pad(seq, '\n'); 1149c319b4d7SVasiliy Kulikov return 0; 1150c319b4d7SVasiliy Kulikov } 1151c319b4d7SVasiliy Kulikov 1152f4550221SChristoph Hellwig static const struct seq_operations ping_v4_seq_ops = { 11538cc785f6SLorenzo Colitti .start = ping_v4_seq_start, 11548cc785f6SLorenzo Colitti .show = ping_v4_seq_show, 11558cc785f6SLorenzo Colitti .next = ping_seq_next, 11568cc785f6SLorenzo Colitti .stop = ping_seq_stop, 11578cc785f6SLorenzo Colitti }; 11588cc785f6SLorenzo Colitti 11598cc785f6SLorenzo Colitti static int __net_init ping_v4_proc_init_net(struct net *net) 1160c319b4d7SVasiliy Kulikov { 1161c3506372SChristoph Hellwig if (!proc_create_net("icmp", 0444, net->proc_net, &ping_v4_seq_ops, 1162c3506372SChristoph Hellwig sizeof(struct ping_iter_state))) 1163f4550221SChristoph Hellwig return -ENOMEM; 1164f4550221SChristoph Hellwig return 0; 1165c319b4d7SVasiliy Kulikov } 1166c319b4d7SVasiliy Kulikov 11678cc785f6SLorenzo Colitti static void __net_exit ping_v4_proc_exit_net(struct net *net) 1168c319b4d7SVasiliy Kulikov { 1169f4550221SChristoph Hellwig remove_proc_entry("icmp", net->proc_net); 1170c319b4d7SVasiliy Kulikov } 1171c319b4d7SVasiliy Kulikov 11728cc785f6SLorenzo Colitti static struct pernet_operations ping_v4_net_ops = { 11738cc785f6SLorenzo Colitti .init = ping_v4_proc_init_net, 11748cc785f6SLorenzo Colitti .exit = ping_v4_proc_exit_net, 1175c319b4d7SVasiliy Kulikov }; 1176c319b4d7SVasiliy Kulikov 1177c319b4d7SVasiliy Kulikov int __init ping_proc_init(void) 1178c319b4d7SVasiliy Kulikov { 11798cc785f6SLorenzo Colitti return register_pernet_subsys(&ping_v4_net_ops); 1180c319b4d7SVasiliy Kulikov } 1181c319b4d7SVasiliy Kulikov 1182c319b4d7SVasiliy Kulikov void ping_proc_exit(void) 1183c319b4d7SVasiliy Kulikov { 11848cc785f6SLorenzo Colitti unregister_pernet_subsys(&ping_v4_net_ops); 1185c319b4d7SVasiliy Kulikov } 1186c319b4d7SVasiliy Kulikov 1187c319b4d7SVasiliy Kulikov #endif 1188c319b4d7SVasiliy Kulikov 1189c319b4d7SVasiliy Kulikov void __init ping_init(void) 1190c319b4d7SVasiliy Kulikov { 1191c319b4d7SVasiliy Kulikov int i; 1192c319b4d7SVasiliy Kulikov 1193c319b4d7SVasiliy Kulikov for (i = 0; i < PING_HTABLE_SIZE; i++) 1194f1b5dfe6SKuniyuki Iwashima INIT_HLIST_HEAD(&ping_table.hash[i]); 1195dbca1596SEric Dumazet spin_lock_init(&ping_table.lock); 1196c319b4d7SVasiliy Kulikov } 1197