162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * INET An implementation of the TCP/IP protocol suite for the LINUX 462306a36Sopenharmony_ci * operating system. INET is implemented using the BSD Socket 562306a36Sopenharmony_ci * interface as the means of communication with the user level. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * "Ping" sockets 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Based on ipv4/udp.c code. 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * Authors: Vasiliy Kulikov / Openwall (for Linux 2.6), 1262306a36Sopenharmony_ci * Pavel Kankovsky (for Linux 2.4.32) 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * Pavel gave all rights to bugs to Vasiliy, 1562306a36Sopenharmony_ci * none of the bugs are Pavel's now. 1662306a36Sopenharmony_ci */ 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include <linux/uaccess.h> 1962306a36Sopenharmony_ci#include <linux/types.h> 2062306a36Sopenharmony_ci#include <linux/fcntl.h> 2162306a36Sopenharmony_ci#include <linux/socket.h> 2262306a36Sopenharmony_ci#include <linux/sockios.h> 2362306a36Sopenharmony_ci#include <linux/in.h> 2462306a36Sopenharmony_ci#include <linux/errno.h> 2562306a36Sopenharmony_ci#include <linux/timer.h> 2662306a36Sopenharmony_ci#include <linux/mm.h> 2762306a36Sopenharmony_ci#include <linux/inet.h> 2862306a36Sopenharmony_ci#include <linux/netdevice.h> 2962306a36Sopenharmony_ci#include <net/snmp.h> 3062306a36Sopenharmony_ci#include <net/ip.h> 3162306a36Sopenharmony_ci#include <net/icmp.h> 3262306a36Sopenharmony_ci#include <net/protocol.h> 3362306a36Sopenharmony_ci#include <linux/skbuff.h> 3462306a36Sopenharmony_ci#include <linux/proc_fs.h> 3562306a36Sopenharmony_ci#include <linux/export.h> 3662306a36Sopenharmony_ci#include <linux/bpf-cgroup.h> 3762306a36Sopenharmony_ci#include <net/sock.h> 3862306a36Sopenharmony_ci#include <net/ping.h> 3962306a36Sopenharmony_ci#include <net/udp.h> 4062306a36Sopenharmony_ci#include <net/route.h> 4162306a36Sopenharmony_ci#include <net/inet_common.h> 4262306a36Sopenharmony_ci#include <net/checksum.h> 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6) 4562306a36Sopenharmony_ci#include <linux/in6.h> 4662306a36Sopenharmony_ci#include <linux/icmpv6.h> 4762306a36Sopenharmony_ci#include <net/addrconf.h> 4862306a36Sopenharmony_ci#include <net/ipv6.h> 4962306a36Sopenharmony_ci#include <net/transp_v6.h> 5062306a36Sopenharmony_ci#endif 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistruct ping_table { 5362306a36Sopenharmony_ci struct hlist_head hash[PING_HTABLE_SIZE]; 5462306a36Sopenharmony_ci spinlock_t lock; 5562306a36Sopenharmony_ci}; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic struct ping_table ping_table; 5862306a36Sopenharmony_cistruct pingv6_ops pingv6_ops; 5962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pingv6_ops); 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistatic u16 ping_port_rover; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic inline u32 ping_hashfn(const struct net *net, u32 num, u32 mask) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci u32 res = (num + net_hash_mix(net)) & mask; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci pr_debug("hash(%u) = %u\n", num, res); 6862306a36Sopenharmony_ci return res; 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ping_hash); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistatic inline struct hlist_head *ping_hashslot(struct ping_table *table, 7362306a36Sopenharmony_ci struct net *net, unsigned int num) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci return &table->hash[ping_hashfn(net, num, PING_HTABLE_MASK)]; 7662306a36Sopenharmony_ci} 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ciint ping_get_port(struct sock *sk, unsigned short ident) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci struct inet_sock *isk, *isk2; 8162306a36Sopenharmony_ci struct hlist_head *hlist; 8262306a36Sopenharmony_ci struct sock *sk2 = NULL; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci isk = inet_sk(sk); 8562306a36Sopenharmony_ci spin_lock(&ping_table.lock); 8662306a36Sopenharmony_ci if (ident == 0) { 8762306a36Sopenharmony_ci u32 i; 8862306a36Sopenharmony_ci u16 result = ping_port_rover + 1; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci for (i = 0; i < (1L << 16); i++, result++) { 9162306a36Sopenharmony_ci if (!result) 9262306a36Sopenharmony_ci result++; /* avoid zero */ 9362306a36Sopenharmony_ci hlist = ping_hashslot(&ping_table, sock_net(sk), 9462306a36Sopenharmony_ci result); 9562306a36Sopenharmony_ci sk_for_each(sk2, hlist) { 9662306a36Sopenharmony_ci isk2 = inet_sk(sk2); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci if (isk2->inet_num == result) 9962306a36Sopenharmony_ci goto next_port; 10062306a36Sopenharmony_ci } 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci /* found */ 10362306a36Sopenharmony_ci ping_port_rover = ident = result; 10462306a36Sopenharmony_ci break; 10562306a36Sopenharmony_cinext_port: 10662306a36Sopenharmony_ci ; 10762306a36Sopenharmony_ci } 10862306a36Sopenharmony_ci if (i >= (1L << 16)) 10962306a36Sopenharmony_ci goto fail; 11062306a36Sopenharmony_ci } else { 11162306a36Sopenharmony_ci hlist = ping_hashslot(&ping_table, sock_net(sk), ident); 11262306a36Sopenharmony_ci sk_for_each(sk2, hlist) { 11362306a36Sopenharmony_ci isk2 = inet_sk(sk2); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci /* BUG? Why is this reuse and not reuseaddr? ping.c 11662306a36Sopenharmony_ci * doesn't turn off SO_REUSEADDR, and it doesn't expect 11762306a36Sopenharmony_ci * that other ping processes can steal its packets. 11862306a36Sopenharmony_ci */ 11962306a36Sopenharmony_ci if ((isk2->inet_num == ident) && 12062306a36Sopenharmony_ci (sk2 != sk) && 12162306a36Sopenharmony_ci (!sk2->sk_reuse || !sk->sk_reuse)) 12262306a36Sopenharmony_ci goto fail; 12362306a36Sopenharmony_ci } 12462306a36Sopenharmony_ci } 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci pr_debug("found port/ident = %d\n", ident); 12762306a36Sopenharmony_ci isk->inet_num = ident; 12862306a36Sopenharmony_ci if (sk_unhashed(sk)) { 12962306a36Sopenharmony_ci pr_debug("was not hashed\n"); 13062306a36Sopenharmony_ci sk_add_node_rcu(sk, hlist); 13162306a36Sopenharmony_ci sock_set_flag(sk, SOCK_RCU_FREE); 13262306a36Sopenharmony_ci sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1); 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci spin_unlock(&ping_table.lock); 13562306a36Sopenharmony_ci return 0; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_cifail: 13862306a36Sopenharmony_ci spin_unlock(&ping_table.lock); 13962306a36Sopenharmony_ci return -EADDRINUSE; 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ping_get_port); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ciint ping_hash(struct sock *sk) 14462306a36Sopenharmony_ci{ 14562306a36Sopenharmony_ci pr_debug("ping_hash(sk->port=%u)\n", inet_sk(sk)->inet_num); 14662306a36Sopenharmony_ci BUG(); /* "Please do not press this button again." */ 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci return 0; 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_civoid ping_unhash(struct sock *sk) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci struct inet_sock *isk = inet_sk(sk); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci pr_debug("ping_unhash(isk=%p,isk->num=%u)\n", isk, isk->inet_num); 15662306a36Sopenharmony_ci spin_lock(&ping_table.lock); 15762306a36Sopenharmony_ci if (sk_del_node_init_rcu(sk)) { 15862306a36Sopenharmony_ci isk->inet_num = 0; 15962306a36Sopenharmony_ci isk->inet_sport = 0; 16062306a36Sopenharmony_ci sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1); 16162306a36Sopenharmony_ci } 16262306a36Sopenharmony_ci spin_unlock(&ping_table.lock); 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ping_unhash); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci/* Called under rcu_read_lock() */ 16762306a36Sopenharmony_cistatic struct sock *ping_lookup(struct net *net, struct sk_buff *skb, u16 ident) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci struct hlist_head *hslot = ping_hashslot(&ping_table, net, ident); 17062306a36Sopenharmony_ci struct sock *sk = NULL; 17162306a36Sopenharmony_ci struct inet_sock *isk; 17262306a36Sopenharmony_ci int dif, sdif; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci if (skb->protocol == htons(ETH_P_IP)) { 17562306a36Sopenharmony_ci dif = inet_iif(skb); 17662306a36Sopenharmony_ci sdif = inet_sdif(skb); 17762306a36Sopenharmony_ci pr_debug("try to find: num = %d, daddr = %pI4, dif = %d\n", 17862306a36Sopenharmony_ci (int)ident, &ip_hdr(skb)->daddr, dif); 17962306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6) 18062306a36Sopenharmony_ci } else if (skb->protocol == htons(ETH_P_IPV6)) { 18162306a36Sopenharmony_ci dif = inet6_iif(skb); 18262306a36Sopenharmony_ci sdif = inet6_sdif(skb); 18362306a36Sopenharmony_ci pr_debug("try to find: num = %d, daddr = %pI6c, dif = %d\n", 18462306a36Sopenharmony_ci (int)ident, &ipv6_hdr(skb)->daddr, dif); 18562306a36Sopenharmony_ci#endif 18662306a36Sopenharmony_ci } else { 18762306a36Sopenharmony_ci return NULL; 18862306a36Sopenharmony_ci } 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci sk_for_each_rcu(sk, hslot) { 19162306a36Sopenharmony_ci isk = inet_sk(sk); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci pr_debug("iterate\n"); 19462306a36Sopenharmony_ci if (isk->inet_num != ident) 19562306a36Sopenharmony_ci continue; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci if (skb->protocol == htons(ETH_P_IP) && 19862306a36Sopenharmony_ci sk->sk_family == AF_INET) { 19962306a36Sopenharmony_ci pr_debug("found: %p: num=%d, daddr=%pI4, dif=%d\n", sk, 20062306a36Sopenharmony_ci (int) isk->inet_num, &isk->inet_rcv_saddr, 20162306a36Sopenharmony_ci sk->sk_bound_dev_if); 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci if (isk->inet_rcv_saddr && 20462306a36Sopenharmony_ci isk->inet_rcv_saddr != ip_hdr(skb)->daddr) 20562306a36Sopenharmony_ci continue; 20662306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6) 20762306a36Sopenharmony_ci } else if (skb->protocol == htons(ETH_P_IPV6) && 20862306a36Sopenharmony_ci sk->sk_family == AF_INET6) { 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci pr_debug("found: %p: num=%d, daddr=%pI6c, dif=%d\n", sk, 21162306a36Sopenharmony_ci (int) isk->inet_num, 21262306a36Sopenharmony_ci &sk->sk_v6_rcv_saddr, 21362306a36Sopenharmony_ci sk->sk_bound_dev_if); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci if (!ipv6_addr_any(&sk->sk_v6_rcv_saddr) && 21662306a36Sopenharmony_ci !ipv6_addr_equal(&sk->sk_v6_rcv_saddr, 21762306a36Sopenharmony_ci &ipv6_hdr(skb)->daddr)) 21862306a36Sopenharmony_ci continue; 21962306a36Sopenharmony_ci#endif 22062306a36Sopenharmony_ci } else { 22162306a36Sopenharmony_ci continue; 22262306a36Sopenharmony_ci } 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci if (sk->sk_bound_dev_if && sk->sk_bound_dev_if != dif && 22562306a36Sopenharmony_ci sk->sk_bound_dev_if != sdif) 22662306a36Sopenharmony_ci continue; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci goto exit; 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci sk = NULL; 23262306a36Sopenharmony_ciexit: 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci return sk; 23562306a36Sopenharmony_ci} 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_cistatic void inet_get_ping_group_range_net(struct net *net, kgid_t *low, 23862306a36Sopenharmony_ci kgid_t *high) 23962306a36Sopenharmony_ci{ 24062306a36Sopenharmony_ci kgid_t *data = net->ipv4.ping_group_range.range; 24162306a36Sopenharmony_ci unsigned int seq; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci do { 24462306a36Sopenharmony_ci seq = read_seqbegin(&net->ipv4.ping_group_range.lock); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci *low = data[0]; 24762306a36Sopenharmony_ci *high = data[1]; 24862306a36Sopenharmony_ci } while (read_seqretry(&net->ipv4.ping_group_range.lock, seq)); 24962306a36Sopenharmony_ci} 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ciint ping_init_sock(struct sock *sk) 25362306a36Sopenharmony_ci{ 25462306a36Sopenharmony_ci struct net *net = sock_net(sk); 25562306a36Sopenharmony_ci kgid_t group = current_egid(); 25662306a36Sopenharmony_ci struct group_info *group_info; 25762306a36Sopenharmony_ci int i; 25862306a36Sopenharmony_ci kgid_t low, high; 25962306a36Sopenharmony_ci int ret = 0; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci if (sk->sk_family == AF_INET6) 26262306a36Sopenharmony_ci sk->sk_ipv6only = 1; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci inet_get_ping_group_range_net(net, &low, &high); 26562306a36Sopenharmony_ci if (gid_lte(low, group) && gid_lte(group, high)) 26662306a36Sopenharmony_ci return 0; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci group_info = get_current_groups(); 26962306a36Sopenharmony_ci for (i = 0; i < group_info->ngroups; i++) { 27062306a36Sopenharmony_ci kgid_t gid = group_info->gid[i]; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci if (gid_lte(low, gid) && gid_lte(gid, high)) 27362306a36Sopenharmony_ci goto out_release_group; 27462306a36Sopenharmony_ci } 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci ret = -EACCES; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ciout_release_group: 27962306a36Sopenharmony_ci put_group_info(group_info); 28062306a36Sopenharmony_ci return ret; 28162306a36Sopenharmony_ci} 28262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ping_init_sock); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_civoid ping_close(struct sock *sk, long timeout) 28562306a36Sopenharmony_ci{ 28662306a36Sopenharmony_ci pr_debug("ping_close(sk=%p,sk->num=%u)\n", 28762306a36Sopenharmony_ci inet_sk(sk), inet_sk(sk)->inet_num); 28862306a36Sopenharmony_ci pr_debug("isk->refcnt = %d\n", refcount_read(&sk->sk_refcnt)); 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci sk_common_release(sk); 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ping_close); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_cistatic int ping_pre_connect(struct sock *sk, struct sockaddr *uaddr, 29562306a36Sopenharmony_ci int addr_len) 29662306a36Sopenharmony_ci{ 29762306a36Sopenharmony_ci /* This check is replicated from __ip4_datagram_connect() and 29862306a36Sopenharmony_ci * intended to prevent BPF program called below from accessing bytes 29962306a36Sopenharmony_ci * that are out of the bound specified by user in addr_len. 30062306a36Sopenharmony_ci */ 30162306a36Sopenharmony_ci if (addr_len < sizeof(struct sockaddr_in)) 30262306a36Sopenharmony_ci return -EINVAL; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci return BPF_CGROUP_RUN_PROG_INET4_CONNECT_LOCK(sk, uaddr, &addr_len); 30562306a36Sopenharmony_ci} 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci/* Checks the bind address and possibly modifies sk->sk_bound_dev_if. */ 30862306a36Sopenharmony_cistatic int ping_check_bind_addr(struct sock *sk, struct inet_sock *isk, 30962306a36Sopenharmony_ci struct sockaddr *uaddr, int addr_len) 31062306a36Sopenharmony_ci{ 31162306a36Sopenharmony_ci struct net *net = sock_net(sk); 31262306a36Sopenharmony_ci if (sk->sk_family == AF_INET) { 31362306a36Sopenharmony_ci struct sockaddr_in *addr = (struct sockaddr_in *) uaddr; 31462306a36Sopenharmony_ci u32 tb_id = RT_TABLE_LOCAL; 31562306a36Sopenharmony_ci int chk_addr_ret; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci if (addr_len < sizeof(*addr)) 31862306a36Sopenharmony_ci return -EINVAL; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci if (addr->sin_family != AF_INET && 32162306a36Sopenharmony_ci !(addr->sin_family == AF_UNSPEC && 32262306a36Sopenharmony_ci addr->sin_addr.s_addr == htonl(INADDR_ANY))) 32362306a36Sopenharmony_ci return -EAFNOSUPPORT; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci pr_debug("ping_check_bind_addr(sk=%p,addr=%pI4,port=%d)\n", 32662306a36Sopenharmony_ci sk, &addr->sin_addr.s_addr, ntohs(addr->sin_port)); 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci if (addr->sin_addr.s_addr == htonl(INADDR_ANY)) 32962306a36Sopenharmony_ci return 0; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci tb_id = l3mdev_fib_table_by_index(net, sk->sk_bound_dev_if) ? : tb_id; 33262306a36Sopenharmony_ci chk_addr_ret = inet_addr_type_table(net, addr->sin_addr.s_addr, tb_id); 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci if (chk_addr_ret == RTN_MULTICAST || 33562306a36Sopenharmony_ci chk_addr_ret == RTN_BROADCAST || 33662306a36Sopenharmony_ci (chk_addr_ret != RTN_LOCAL && 33762306a36Sopenharmony_ci !inet_can_nonlocal_bind(net, isk))) 33862306a36Sopenharmony_ci return -EADDRNOTAVAIL; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6) 34162306a36Sopenharmony_ci } else if (sk->sk_family == AF_INET6) { 34262306a36Sopenharmony_ci struct sockaddr_in6 *addr = (struct sockaddr_in6 *) uaddr; 34362306a36Sopenharmony_ci int addr_type, scoped, has_addr; 34462306a36Sopenharmony_ci struct net_device *dev = NULL; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci if (addr_len < sizeof(*addr)) 34762306a36Sopenharmony_ci return -EINVAL; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci if (addr->sin6_family != AF_INET6) 35062306a36Sopenharmony_ci return -EAFNOSUPPORT; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci pr_debug("ping_check_bind_addr(sk=%p,addr=%pI6c,port=%d)\n", 35362306a36Sopenharmony_ci sk, addr->sin6_addr.s6_addr, ntohs(addr->sin6_port)); 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci addr_type = ipv6_addr_type(&addr->sin6_addr); 35662306a36Sopenharmony_ci scoped = __ipv6_addr_needs_scope_id(addr_type); 35762306a36Sopenharmony_ci if ((addr_type != IPV6_ADDR_ANY && 35862306a36Sopenharmony_ci !(addr_type & IPV6_ADDR_UNICAST)) || 35962306a36Sopenharmony_ci (scoped && !addr->sin6_scope_id)) 36062306a36Sopenharmony_ci return -EINVAL; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci rcu_read_lock(); 36362306a36Sopenharmony_ci if (addr->sin6_scope_id) { 36462306a36Sopenharmony_ci dev = dev_get_by_index_rcu(net, addr->sin6_scope_id); 36562306a36Sopenharmony_ci if (!dev) { 36662306a36Sopenharmony_ci rcu_read_unlock(); 36762306a36Sopenharmony_ci return -ENODEV; 36862306a36Sopenharmony_ci } 36962306a36Sopenharmony_ci } 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci if (!dev && sk->sk_bound_dev_if) { 37262306a36Sopenharmony_ci dev = dev_get_by_index_rcu(net, sk->sk_bound_dev_if); 37362306a36Sopenharmony_ci if (!dev) { 37462306a36Sopenharmony_ci rcu_read_unlock(); 37562306a36Sopenharmony_ci return -ENODEV; 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci } 37862306a36Sopenharmony_ci has_addr = pingv6_ops.ipv6_chk_addr(net, &addr->sin6_addr, dev, 37962306a36Sopenharmony_ci scoped); 38062306a36Sopenharmony_ci rcu_read_unlock(); 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci if (!(ipv6_can_nonlocal_bind(net, isk) || has_addr || 38362306a36Sopenharmony_ci addr_type == IPV6_ADDR_ANY)) 38462306a36Sopenharmony_ci return -EADDRNOTAVAIL; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci if (scoped) 38762306a36Sopenharmony_ci sk->sk_bound_dev_if = addr->sin6_scope_id; 38862306a36Sopenharmony_ci#endif 38962306a36Sopenharmony_ci } else { 39062306a36Sopenharmony_ci return -EAFNOSUPPORT; 39162306a36Sopenharmony_ci } 39262306a36Sopenharmony_ci return 0; 39362306a36Sopenharmony_ci} 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_cistatic void ping_set_saddr(struct sock *sk, struct sockaddr *saddr) 39662306a36Sopenharmony_ci{ 39762306a36Sopenharmony_ci if (saddr->sa_family == AF_INET) { 39862306a36Sopenharmony_ci struct inet_sock *isk = inet_sk(sk); 39962306a36Sopenharmony_ci struct sockaddr_in *addr = (struct sockaddr_in *) saddr; 40062306a36Sopenharmony_ci isk->inet_rcv_saddr = isk->inet_saddr = addr->sin_addr.s_addr; 40162306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6) 40262306a36Sopenharmony_ci } else if (saddr->sa_family == AF_INET6) { 40362306a36Sopenharmony_ci struct sockaddr_in6 *addr = (struct sockaddr_in6 *) saddr; 40462306a36Sopenharmony_ci struct ipv6_pinfo *np = inet6_sk(sk); 40562306a36Sopenharmony_ci sk->sk_v6_rcv_saddr = np->saddr = addr->sin6_addr; 40662306a36Sopenharmony_ci#endif 40762306a36Sopenharmony_ci } 40862306a36Sopenharmony_ci} 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci/* 41162306a36Sopenharmony_ci * We need our own bind because there are no privileged id's == local ports. 41262306a36Sopenharmony_ci * Moreover, we don't allow binding to multi- and broadcast addresses. 41362306a36Sopenharmony_ci */ 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ciint ping_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len) 41662306a36Sopenharmony_ci{ 41762306a36Sopenharmony_ci struct inet_sock *isk = inet_sk(sk); 41862306a36Sopenharmony_ci unsigned short snum; 41962306a36Sopenharmony_ci int err; 42062306a36Sopenharmony_ci int dif = sk->sk_bound_dev_if; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci err = ping_check_bind_addr(sk, isk, uaddr, addr_len); 42362306a36Sopenharmony_ci if (err) 42462306a36Sopenharmony_ci return err; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci lock_sock(sk); 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci err = -EINVAL; 42962306a36Sopenharmony_ci if (isk->inet_num != 0) 43062306a36Sopenharmony_ci goto out; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci err = -EADDRINUSE; 43362306a36Sopenharmony_ci snum = ntohs(((struct sockaddr_in *)uaddr)->sin_port); 43462306a36Sopenharmony_ci if (ping_get_port(sk, snum) != 0) { 43562306a36Sopenharmony_ci /* Restore possibly modified sk->sk_bound_dev_if by ping_check_bind_addr(). */ 43662306a36Sopenharmony_ci sk->sk_bound_dev_if = dif; 43762306a36Sopenharmony_ci goto out; 43862306a36Sopenharmony_ci } 43962306a36Sopenharmony_ci ping_set_saddr(sk, uaddr); 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci pr_debug("after bind(): num = %hu, dif = %d\n", 44262306a36Sopenharmony_ci isk->inet_num, 44362306a36Sopenharmony_ci sk->sk_bound_dev_if); 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci err = 0; 44662306a36Sopenharmony_ci if (sk->sk_family == AF_INET && isk->inet_rcv_saddr) 44762306a36Sopenharmony_ci sk->sk_userlocks |= SOCK_BINDADDR_LOCK; 44862306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6) 44962306a36Sopenharmony_ci if (sk->sk_family == AF_INET6 && !ipv6_addr_any(&sk->sk_v6_rcv_saddr)) 45062306a36Sopenharmony_ci sk->sk_userlocks |= SOCK_BINDADDR_LOCK; 45162306a36Sopenharmony_ci#endif 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci if (snum) 45462306a36Sopenharmony_ci sk->sk_userlocks |= SOCK_BINDPORT_LOCK; 45562306a36Sopenharmony_ci isk->inet_sport = htons(isk->inet_num); 45662306a36Sopenharmony_ci isk->inet_daddr = 0; 45762306a36Sopenharmony_ci isk->inet_dport = 0; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6) 46062306a36Sopenharmony_ci if (sk->sk_family == AF_INET6) 46162306a36Sopenharmony_ci memset(&sk->sk_v6_daddr, 0, sizeof(sk->sk_v6_daddr)); 46262306a36Sopenharmony_ci#endif 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci sk_dst_reset(sk); 46562306a36Sopenharmony_ciout: 46662306a36Sopenharmony_ci release_sock(sk); 46762306a36Sopenharmony_ci pr_debug("ping_v4_bind -> %d\n", err); 46862306a36Sopenharmony_ci return err; 46962306a36Sopenharmony_ci} 47062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ping_bind); 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci/* 47362306a36Sopenharmony_ci * Is this a supported type of ICMP message? 47462306a36Sopenharmony_ci */ 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_cistatic inline int ping_supported(int family, int type, int code) 47762306a36Sopenharmony_ci{ 47862306a36Sopenharmony_ci return (family == AF_INET && type == ICMP_ECHO && code == 0) || 47962306a36Sopenharmony_ci (family == AF_INET && type == ICMP_EXT_ECHO && code == 0) || 48062306a36Sopenharmony_ci (family == AF_INET6 && type == ICMPV6_ECHO_REQUEST && code == 0) || 48162306a36Sopenharmony_ci (family == AF_INET6 && type == ICMPV6_EXT_ECHO_REQUEST && code == 0); 48262306a36Sopenharmony_ci} 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci/* 48562306a36Sopenharmony_ci * This routine is called by the ICMP module when it gets some 48662306a36Sopenharmony_ci * sort of error condition. 48762306a36Sopenharmony_ci */ 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_civoid ping_err(struct sk_buff *skb, int offset, u32 info) 49062306a36Sopenharmony_ci{ 49162306a36Sopenharmony_ci int family; 49262306a36Sopenharmony_ci struct icmphdr *icmph; 49362306a36Sopenharmony_ci struct inet_sock *inet_sock; 49462306a36Sopenharmony_ci int type; 49562306a36Sopenharmony_ci int code; 49662306a36Sopenharmony_ci struct net *net = dev_net(skb->dev); 49762306a36Sopenharmony_ci struct sock *sk; 49862306a36Sopenharmony_ci int harderr; 49962306a36Sopenharmony_ci int err; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci if (skb->protocol == htons(ETH_P_IP)) { 50262306a36Sopenharmony_ci family = AF_INET; 50362306a36Sopenharmony_ci type = icmp_hdr(skb)->type; 50462306a36Sopenharmony_ci code = icmp_hdr(skb)->code; 50562306a36Sopenharmony_ci icmph = (struct icmphdr *)(skb->data + offset); 50662306a36Sopenharmony_ci } else if (skb->protocol == htons(ETH_P_IPV6)) { 50762306a36Sopenharmony_ci family = AF_INET6; 50862306a36Sopenharmony_ci type = icmp6_hdr(skb)->icmp6_type; 50962306a36Sopenharmony_ci code = icmp6_hdr(skb)->icmp6_code; 51062306a36Sopenharmony_ci icmph = (struct icmphdr *) (skb->data + offset); 51162306a36Sopenharmony_ci } else { 51262306a36Sopenharmony_ci BUG(); 51362306a36Sopenharmony_ci } 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci /* We assume the packet has already been checked by icmp_unreach */ 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci if (!ping_supported(family, icmph->type, icmph->code)) 51862306a36Sopenharmony_ci return; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci pr_debug("ping_err(proto=0x%x,type=%d,code=%d,id=%04x,seq=%04x)\n", 52162306a36Sopenharmony_ci skb->protocol, type, code, ntohs(icmph->un.echo.id), 52262306a36Sopenharmony_ci ntohs(icmph->un.echo.sequence)); 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci sk = ping_lookup(net, skb, ntohs(icmph->un.echo.id)); 52562306a36Sopenharmony_ci if (!sk) { 52662306a36Sopenharmony_ci pr_debug("no socket, dropping\n"); 52762306a36Sopenharmony_ci return; /* No socket for error */ 52862306a36Sopenharmony_ci } 52962306a36Sopenharmony_ci pr_debug("err on socket %p\n", sk); 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci err = 0; 53262306a36Sopenharmony_ci harderr = 0; 53362306a36Sopenharmony_ci inet_sock = inet_sk(sk); 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci if (skb->protocol == htons(ETH_P_IP)) { 53662306a36Sopenharmony_ci switch (type) { 53762306a36Sopenharmony_ci default: 53862306a36Sopenharmony_ci case ICMP_TIME_EXCEEDED: 53962306a36Sopenharmony_ci err = EHOSTUNREACH; 54062306a36Sopenharmony_ci break; 54162306a36Sopenharmony_ci case ICMP_SOURCE_QUENCH: 54262306a36Sopenharmony_ci /* This is not a real error but ping wants to see it. 54362306a36Sopenharmony_ci * Report it with some fake errno. 54462306a36Sopenharmony_ci */ 54562306a36Sopenharmony_ci err = EREMOTEIO; 54662306a36Sopenharmony_ci break; 54762306a36Sopenharmony_ci case ICMP_PARAMETERPROB: 54862306a36Sopenharmony_ci err = EPROTO; 54962306a36Sopenharmony_ci harderr = 1; 55062306a36Sopenharmony_ci break; 55162306a36Sopenharmony_ci case ICMP_DEST_UNREACH: 55262306a36Sopenharmony_ci if (code == ICMP_FRAG_NEEDED) { /* Path MTU discovery */ 55362306a36Sopenharmony_ci ipv4_sk_update_pmtu(skb, sk, info); 55462306a36Sopenharmony_ci if (inet_sock->pmtudisc != IP_PMTUDISC_DONT) { 55562306a36Sopenharmony_ci err = EMSGSIZE; 55662306a36Sopenharmony_ci harderr = 1; 55762306a36Sopenharmony_ci break; 55862306a36Sopenharmony_ci } 55962306a36Sopenharmony_ci goto out; 56062306a36Sopenharmony_ci } 56162306a36Sopenharmony_ci err = EHOSTUNREACH; 56262306a36Sopenharmony_ci if (code <= NR_ICMP_UNREACH) { 56362306a36Sopenharmony_ci harderr = icmp_err_convert[code].fatal; 56462306a36Sopenharmony_ci err = icmp_err_convert[code].errno; 56562306a36Sopenharmony_ci } 56662306a36Sopenharmony_ci break; 56762306a36Sopenharmony_ci case ICMP_REDIRECT: 56862306a36Sopenharmony_ci /* See ICMP_SOURCE_QUENCH */ 56962306a36Sopenharmony_ci ipv4_sk_redirect(skb, sk); 57062306a36Sopenharmony_ci err = EREMOTEIO; 57162306a36Sopenharmony_ci break; 57262306a36Sopenharmony_ci } 57362306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6) 57462306a36Sopenharmony_ci } else if (skb->protocol == htons(ETH_P_IPV6)) { 57562306a36Sopenharmony_ci harderr = pingv6_ops.icmpv6_err_convert(type, code, &err); 57662306a36Sopenharmony_ci#endif 57762306a36Sopenharmony_ci } 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci /* 58062306a36Sopenharmony_ci * RFC1122: OK. Passes ICMP errors back to application, as per 58162306a36Sopenharmony_ci * 4.1.3.3. 58262306a36Sopenharmony_ci */ 58362306a36Sopenharmony_ci if ((family == AF_INET && !inet_test_bit(RECVERR, sk)) || 58462306a36Sopenharmony_ci (family == AF_INET6 && !inet6_sk(sk)->recverr)) { 58562306a36Sopenharmony_ci if (!harderr || sk->sk_state != TCP_ESTABLISHED) 58662306a36Sopenharmony_ci goto out; 58762306a36Sopenharmony_ci } else { 58862306a36Sopenharmony_ci if (family == AF_INET) { 58962306a36Sopenharmony_ci ip_icmp_error(sk, skb, err, 0 /* no remote port */, 59062306a36Sopenharmony_ci info, (u8 *)icmph); 59162306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6) 59262306a36Sopenharmony_ci } else if (family == AF_INET6) { 59362306a36Sopenharmony_ci pingv6_ops.ipv6_icmp_error(sk, skb, err, 0, 59462306a36Sopenharmony_ci info, (u8 *)icmph); 59562306a36Sopenharmony_ci#endif 59662306a36Sopenharmony_ci } 59762306a36Sopenharmony_ci } 59862306a36Sopenharmony_ci sk->sk_err = err; 59962306a36Sopenharmony_ci sk_error_report(sk); 60062306a36Sopenharmony_ciout: 60162306a36Sopenharmony_ci return; 60262306a36Sopenharmony_ci} 60362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ping_err); 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci/* 60662306a36Sopenharmony_ci * Copy and checksum an ICMP Echo packet from user space into a buffer 60762306a36Sopenharmony_ci * starting from the payload. 60862306a36Sopenharmony_ci */ 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ciint ping_getfrag(void *from, char *to, 61162306a36Sopenharmony_ci int offset, int fraglen, int odd, struct sk_buff *skb) 61262306a36Sopenharmony_ci{ 61362306a36Sopenharmony_ci struct pingfakehdr *pfh = from; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci if (!csum_and_copy_from_iter_full(to, fraglen, &pfh->wcheck, 61662306a36Sopenharmony_ci &pfh->msg->msg_iter)) 61762306a36Sopenharmony_ci return -EFAULT; 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6) 62062306a36Sopenharmony_ci /* For IPv6, checksum each skb as we go along, as expected by 62162306a36Sopenharmony_ci * icmpv6_push_pending_frames. For IPv4, accumulate the checksum in 62262306a36Sopenharmony_ci * wcheck, it will be finalized in ping_v4_push_pending_frames. 62362306a36Sopenharmony_ci */ 62462306a36Sopenharmony_ci if (pfh->family == AF_INET6) { 62562306a36Sopenharmony_ci skb->csum = csum_block_add(skb->csum, pfh->wcheck, odd); 62662306a36Sopenharmony_ci skb->ip_summed = CHECKSUM_NONE; 62762306a36Sopenharmony_ci pfh->wcheck = 0; 62862306a36Sopenharmony_ci } 62962306a36Sopenharmony_ci#endif 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci return 0; 63262306a36Sopenharmony_ci} 63362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ping_getfrag); 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_cistatic int ping_v4_push_pending_frames(struct sock *sk, struct pingfakehdr *pfh, 63662306a36Sopenharmony_ci struct flowi4 *fl4) 63762306a36Sopenharmony_ci{ 63862306a36Sopenharmony_ci struct sk_buff *skb = skb_peek(&sk->sk_write_queue); 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci if (!skb) 64162306a36Sopenharmony_ci return 0; 64262306a36Sopenharmony_ci pfh->wcheck = csum_partial((char *)&pfh->icmph, 64362306a36Sopenharmony_ci sizeof(struct icmphdr), pfh->wcheck); 64462306a36Sopenharmony_ci pfh->icmph.checksum = csum_fold(pfh->wcheck); 64562306a36Sopenharmony_ci memcpy(icmp_hdr(skb), &pfh->icmph, sizeof(struct icmphdr)); 64662306a36Sopenharmony_ci skb->ip_summed = CHECKSUM_NONE; 64762306a36Sopenharmony_ci return ip_push_pending_frames(sk, fl4); 64862306a36Sopenharmony_ci} 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ciint ping_common_sendmsg(int family, struct msghdr *msg, size_t len, 65162306a36Sopenharmony_ci void *user_icmph, size_t icmph_len) 65262306a36Sopenharmony_ci{ 65362306a36Sopenharmony_ci u8 type, code; 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci if (len > 0xFFFF) 65662306a36Sopenharmony_ci return -EMSGSIZE; 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci /* Must have at least a full ICMP header. */ 65962306a36Sopenharmony_ci if (len < icmph_len) 66062306a36Sopenharmony_ci return -EINVAL; 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci /* 66362306a36Sopenharmony_ci * Check the flags. 66462306a36Sopenharmony_ci */ 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci /* Mirror BSD error message compatibility */ 66762306a36Sopenharmony_ci if (msg->msg_flags & MSG_OOB) 66862306a36Sopenharmony_ci return -EOPNOTSUPP; 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci /* 67162306a36Sopenharmony_ci * Fetch the ICMP header provided by the userland. 67262306a36Sopenharmony_ci * iovec is modified! The ICMP header is consumed. 67362306a36Sopenharmony_ci */ 67462306a36Sopenharmony_ci if (memcpy_from_msg(user_icmph, msg, icmph_len)) 67562306a36Sopenharmony_ci return -EFAULT; 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci if (family == AF_INET) { 67862306a36Sopenharmony_ci type = ((struct icmphdr *) user_icmph)->type; 67962306a36Sopenharmony_ci code = ((struct icmphdr *) user_icmph)->code; 68062306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6) 68162306a36Sopenharmony_ci } else if (family == AF_INET6) { 68262306a36Sopenharmony_ci type = ((struct icmp6hdr *) user_icmph)->icmp6_type; 68362306a36Sopenharmony_ci code = ((struct icmp6hdr *) user_icmph)->icmp6_code; 68462306a36Sopenharmony_ci#endif 68562306a36Sopenharmony_ci } else { 68662306a36Sopenharmony_ci BUG(); 68762306a36Sopenharmony_ci } 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci if (!ping_supported(family, type, code)) 69062306a36Sopenharmony_ci return -EINVAL; 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci return 0; 69362306a36Sopenharmony_ci} 69462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ping_common_sendmsg); 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_cistatic int ping_v4_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) 69762306a36Sopenharmony_ci{ 69862306a36Sopenharmony_ci struct net *net = sock_net(sk); 69962306a36Sopenharmony_ci struct flowi4 fl4; 70062306a36Sopenharmony_ci struct inet_sock *inet = inet_sk(sk); 70162306a36Sopenharmony_ci struct ipcm_cookie ipc; 70262306a36Sopenharmony_ci struct icmphdr user_icmph; 70362306a36Sopenharmony_ci struct pingfakehdr pfh; 70462306a36Sopenharmony_ci struct rtable *rt = NULL; 70562306a36Sopenharmony_ci struct ip_options_data opt_copy; 70662306a36Sopenharmony_ci int free = 0; 70762306a36Sopenharmony_ci __be32 saddr, daddr, faddr; 70862306a36Sopenharmony_ci u8 tos, scope; 70962306a36Sopenharmony_ci int err; 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci pr_debug("ping_v4_sendmsg(sk=%p,sk->num=%u)\n", inet, inet->inet_num); 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci err = ping_common_sendmsg(AF_INET, msg, len, &user_icmph, 71462306a36Sopenharmony_ci sizeof(user_icmph)); 71562306a36Sopenharmony_ci if (err) 71662306a36Sopenharmony_ci return err; 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci /* 71962306a36Sopenharmony_ci * Get and verify the address. 72062306a36Sopenharmony_ci */ 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci if (msg->msg_name) { 72362306a36Sopenharmony_ci DECLARE_SOCKADDR(struct sockaddr_in *, usin, msg->msg_name); 72462306a36Sopenharmony_ci if (msg->msg_namelen < sizeof(*usin)) 72562306a36Sopenharmony_ci return -EINVAL; 72662306a36Sopenharmony_ci if (usin->sin_family != AF_INET) 72762306a36Sopenharmony_ci return -EAFNOSUPPORT; 72862306a36Sopenharmony_ci daddr = usin->sin_addr.s_addr; 72962306a36Sopenharmony_ci /* no remote port */ 73062306a36Sopenharmony_ci } else { 73162306a36Sopenharmony_ci if (sk->sk_state != TCP_ESTABLISHED) 73262306a36Sopenharmony_ci return -EDESTADDRREQ; 73362306a36Sopenharmony_ci daddr = inet->inet_daddr; 73462306a36Sopenharmony_ci /* no remote port */ 73562306a36Sopenharmony_ci } 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci ipcm_init_sk(&ipc, inet); 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci if (msg->msg_controllen) { 74062306a36Sopenharmony_ci err = ip_cmsg_send(sk, msg, &ipc, false); 74162306a36Sopenharmony_ci if (unlikely(err)) { 74262306a36Sopenharmony_ci kfree(ipc.opt); 74362306a36Sopenharmony_ci return err; 74462306a36Sopenharmony_ci } 74562306a36Sopenharmony_ci if (ipc.opt) 74662306a36Sopenharmony_ci free = 1; 74762306a36Sopenharmony_ci } 74862306a36Sopenharmony_ci if (!ipc.opt) { 74962306a36Sopenharmony_ci struct ip_options_rcu *inet_opt; 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci rcu_read_lock(); 75262306a36Sopenharmony_ci inet_opt = rcu_dereference(inet->inet_opt); 75362306a36Sopenharmony_ci if (inet_opt) { 75462306a36Sopenharmony_ci memcpy(&opt_copy, inet_opt, 75562306a36Sopenharmony_ci sizeof(*inet_opt) + inet_opt->opt.optlen); 75662306a36Sopenharmony_ci ipc.opt = &opt_copy.opt; 75762306a36Sopenharmony_ci } 75862306a36Sopenharmony_ci rcu_read_unlock(); 75962306a36Sopenharmony_ci } 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci saddr = ipc.addr; 76262306a36Sopenharmony_ci ipc.addr = faddr = daddr; 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci if (ipc.opt && ipc.opt->opt.srr) { 76562306a36Sopenharmony_ci if (!daddr) { 76662306a36Sopenharmony_ci err = -EINVAL; 76762306a36Sopenharmony_ci goto out_free; 76862306a36Sopenharmony_ci } 76962306a36Sopenharmony_ci faddr = ipc.opt->opt.faddr; 77062306a36Sopenharmony_ci } 77162306a36Sopenharmony_ci tos = get_rttos(&ipc, inet); 77262306a36Sopenharmony_ci scope = ip_sendmsg_scope(inet, &ipc, msg); 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci if (ipv4_is_multicast(daddr)) { 77562306a36Sopenharmony_ci if (!ipc.oif || netif_index_is_l3_master(sock_net(sk), ipc.oif)) 77662306a36Sopenharmony_ci ipc.oif = inet->mc_index; 77762306a36Sopenharmony_ci if (!saddr) 77862306a36Sopenharmony_ci saddr = inet->mc_addr; 77962306a36Sopenharmony_ci } else if (!ipc.oif) 78062306a36Sopenharmony_ci ipc.oif = inet->uc_index; 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci flowi4_init_output(&fl4, ipc.oif, ipc.sockc.mark, tos, scope, 78362306a36Sopenharmony_ci sk->sk_protocol, inet_sk_flowi_flags(sk), faddr, 78462306a36Sopenharmony_ci saddr, 0, 0, sk->sk_uid); 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci fl4.fl4_icmp_type = user_icmph.type; 78762306a36Sopenharmony_ci fl4.fl4_icmp_code = user_icmph.code; 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci security_sk_classify_flow(sk, flowi4_to_flowi_common(&fl4)); 79062306a36Sopenharmony_ci rt = ip_route_output_flow(net, &fl4, sk); 79162306a36Sopenharmony_ci if (IS_ERR(rt)) { 79262306a36Sopenharmony_ci err = PTR_ERR(rt); 79362306a36Sopenharmony_ci rt = NULL; 79462306a36Sopenharmony_ci if (err == -ENETUNREACH) 79562306a36Sopenharmony_ci IP_INC_STATS(net, IPSTATS_MIB_OUTNOROUTES); 79662306a36Sopenharmony_ci goto out; 79762306a36Sopenharmony_ci } 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci err = -EACCES; 80062306a36Sopenharmony_ci if ((rt->rt_flags & RTCF_BROADCAST) && 80162306a36Sopenharmony_ci !sock_flag(sk, SOCK_BROADCAST)) 80262306a36Sopenharmony_ci goto out; 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci if (msg->msg_flags & MSG_CONFIRM) 80562306a36Sopenharmony_ci goto do_confirm; 80662306a36Sopenharmony_ciback_from_confirm: 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci if (!ipc.addr) 80962306a36Sopenharmony_ci ipc.addr = fl4.daddr; 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci lock_sock(sk); 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci pfh.icmph.type = user_icmph.type; /* already checked */ 81462306a36Sopenharmony_ci pfh.icmph.code = user_icmph.code; /* ditto */ 81562306a36Sopenharmony_ci pfh.icmph.checksum = 0; 81662306a36Sopenharmony_ci pfh.icmph.un.echo.id = inet->inet_sport; 81762306a36Sopenharmony_ci pfh.icmph.un.echo.sequence = user_icmph.un.echo.sequence; 81862306a36Sopenharmony_ci pfh.msg = msg; 81962306a36Sopenharmony_ci pfh.wcheck = 0; 82062306a36Sopenharmony_ci pfh.family = AF_INET; 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci err = ip_append_data(sk, &fl4, ping_getfrag, &pfh, len, 82362306a36Sopenharmony_ci sizeof(struct icmphdr), &ipc, &rt, 82462306a36Sopenharmony_ci msg->msg_flags); 82562306a36Sopenharmony_ci if (err) 82662306a36Sopenharmony_ci ip_flush_pending_frames(sk); 82762306a36Sopenharmony_ci else 82862306a36Sopenharmony_ci err = ping_v4_push_pending_frames(sk, &pfh, &fl4); 82962306a36Sopenharmony_ci release_sock(sk); 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ciout: 83262306a36Sopenharmony_ci ip_rt_put(rt); 83362306a36Sopenharmony_ciout_free: 83462306a36Sopenharmony_ci if (free) 83562306a36Sopenharmony_ci kfree(ipc.opt); 83662306a36Sopenharmony_ci if (!err) { 83762306a36Sopenharmony_ci icmp_out_count(sock_net(sk), user_icmph.type); 83862306a36Sopenharmony_ci return len; 83962306a36Sopenharmony_ci } 84062306a36Sopenharmony_ci return err; 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_cido_confirm: 84362306a36Sopenharmony_ci if (msg->msg_flags & MSG_PROBE) 84462306a36Sopenharmony_ci dst_confirm_neigh(&rt->dst, &fl4.daddr); 84562306a36Sopenharmony_ci if (!(msg->msg_flags & MSG_PROBE) || len) 84662306a36Sopenharmony_ci goto back_from_confirm; 84762306a36Sopenharmony_ci err = 0; 84862306a36Sopenharmony_ci goto out; 84962306a36Sopenharmony_ci} 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ciint ping_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int flags, 85262306a36Sopenharmony_ci int *addr_len) 85362306a36Sopenharmony_ci{ 85462306a36Sopenharmony_ci struct inet_sock *isk = inet_sk(sk); 85562306a36Sopenharmony_ci int family = sk->sk_family; 85662306a36Sopenharmony_ci struct sk_buff *skb; 85762306a36Sopenharmony_ci int copied, err; 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci pr_debug("ping_recvmsg(sk=%p,sk->num=%u)\n", isk, isk->inet_num); 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci err = -EOPNOTSUPP; 86262306a36Sopenharmony_ci if (flags & MSG_OOB) 86362306a36Sopenharmony_ci goto out; 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci if (flags & MSG_ERRQUEUE) 86662306a36Sopenharmony_ci return inet_recv_error(sk, msg, len, addr_len); 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci skb = skb_recv_datagram(sk, flags, &err); 86962306a36Sopenharmony_ci if (!skb) 87062306a36Sopenharmony_ci goto out; 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci copied = skb->len; 87362306a36Sopenharmony_ci if (copied > len) { 87462306a36Sopenharmony_ci msg->msg_flags |= MSG_TRUNC; 87562306a36Sopenharmony_ci copied = len; 87662306a36Sopenharmony_ci } 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci /* Don't bother checking the checksum */ 87962306a36Sopenharmony_ci err = skb_copy_datagram_msg(skb, 0, msg, copied); 88062306a36Sopenharmony_ci if (err) 88162306a36Sopenharmony_ci goto done; 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci sock_recv_timestamp(msg, sk, skb); 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci /* Copy the address and add cmsg data. */ 88662306a36Sopenharmony_ci if (family == AF_INET) { 88762306a36Sopenharmony_ci DECLARE_SOCKADDR(struct sockaddr_in *, sin, msg->msg_name); 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci if (sin) { 89062306a36Sopenharmony_ci sin->sin_family = AF_INET; 89162306a36Sopenharmony_ci sin->sin_port = 0 /* skb->h.uh->source */; 89262306a36Sopenharmony_ci sin->sin_addr.s_addr = ip_hdr(skb)->saddr; 89362306a36Sopenharmony_ci memset(sin->sin_zero, 0, sizeof(sin->sin_zero)); 89462306a36Sopenharmony_ci *addr_len = sizeof(*sin); 89562306a36Sopenharmony_ci } 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci if (inet_cmsg_flags(isk)) 89862306a36Sopenharmony_ci ip_cmsg_recv(msg, skb); 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6) 90162306a36Sopenharmony_ci } else if (family == AF_INET6) { 90262306a36Sopenharmony_ci struct ipv6_pinfo *np = inet6_sk(sk); 90362306a36Sopenharmony_ci struct ipv6hdr *ip6 = ipv6_hdr(skb); 90462306a36Sopenharmony_ci DECLARE_SOCKADDR(struct sockaddr_in6 *, sin6, msg->msg_name); 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_ci if (sin6) { 90762306a36Sopenharmony_ci sin6->sin6_family = AF_INET6; 90862306a36Sopenharmony_ci sin6->sin6_port = 0; 90962306a36Sopenharmony_ci sin6->sin6_addr = ip6->saddr; 91062306a36Sopenharmony_ci sin6->sin6_flowinfo = 0; 91162306a36Sopenharmony_ci if (np->sndflow) 91262306a36Sopenharmony_ci sin6->sin6_flowinfo = ip6_flowinfo(ip6); 91362306a36Sopenharmony_ci sin6->sin6_scope_id = 91462306a36Sopenharmony_ci ipv6_iface_scope_id(&sin6->sin6_addr, 91562306a36Sopenharmony_ci inet6_iif(skb)); 91662306a36Sopenharmony_ci *addr_len = sizeof(*sin6); 91762306a36Sopenharmony_ci } 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci if (inet6_sk(sk)->rxopt.all) 92062306a36Sopenharmony_ci pingv6_ops.ip6_datagram_recv_common_ctl(sk, msg, skb); 92162306a36Sopenharmony_ci if (skb->protocol == htons(ETH_P_IPV6) && 92262306a36Sopenharmony_ci inet6_sk(sk)->rxopt.all) 92362306a36Sopenharmony_ci pingv6_ops.ip6_datagram_recv_specific_ctl(sk, msg, skb); 92462306a36Sopenharmony_ci else if (skb->protocol == htons(ETH_P_IP) && 92562306a36Sopenharmony_ci inet_cmsg_flags(isk)) 92662306a36Sopenharmony_ci ip_cmsg_recv(msg, skb); 92762306a36Sopenharmony_ci#endif 92862306a36Sopenharmony_ci } else { 92962306a36Sopenharmony_ci BUG(); 93062306a36Sopenharmony_ci } 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci err = copied; 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_cidone: 93562306a36Sopenharmony_ci skb_free_datagram(sk, skb); 93662306a36Sopenharmony_ciout: 93762306a36Sopenharmony_ci pr_debug("ping_recvmsg -> %d\n", err); 93862306a36Sopenharmony_ci return err; 93962306a36Sopenharmony_ci} 94062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ping_recvmsg); 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_cistatic enum skb_drop_reason __ping_queue_rcv_skb(struct sock *sk, 94362306a36Sopenharmony_ci struct sk_buff *skb) 94462306a36Sopenharmony_ci{ 94562306a36Sopenharmony_ci enum skb_drop_reason reason; 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci pr_debug("ping_queue_rcv_skb(sk=%p,sk->num=%d,skb=%p)\n", 94862306a36Sopenharmony_ci inet_sk(sk), inet_sk(sk)->inet_num, skb); 94962306a36Sopenharmony_ci if (sock_queue_rcv_skb_reason(sk, skb, &reason) < 0) { 95062306a36Sopenharmony_ci kfree_skb_reason(skb, reason); 95162306a36Sopenharmony_ci pr_debug("ping_queue_rcv_skb -> failed\n"); 95262306a36Sopenharmony_ci return reason; 95362306a36Sopenharmony_ci } 95462306a36Sopenharmony_ci return SKB_NOT_DROPPED_YET; 95562306a36Sopenharmony_ci} 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ciint ping_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) 95862306a36Sopenharmony_ci{ 95962306a36Sopenharmony_ci return __ping_queue_rcv_skb(sk, skb) ? -1 : 0; 96062306a36Sopenharmony_ci} 96162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ping_queue_rcv_skb); 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci/* 96562306a36Sopenharmony_ci * All we need to do is get the socket. 96662306a36Sopenharmony_ci */ 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_cienum skb_drop_reason ping_rcv(struct sk_buff *skb) 96962306a36Sopenharmony_ci{ 97062306a36Sopenharmony_ci enum skb_drop_reason reason = SKB_DROP_REASON_NO_SOCKET; 97162306a36Sopenharmony_ci struct sock *sk; 97262306a36Sopenharmony_ci struct net *net = dev_net(skb->dev); 97362306a36Sopenharmony_ci struct icmphdr *icmph = icmp_hdr(skb); 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci /* We assume the packet has already been checked by icmp_rcv */ 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci pr_debug("ping_rcv(skb=%p,id=%04x,seq=%04x)\n", 97862306a36Sopenharmony_ci skb, ntohs(icmph->un.echo.id), ntohs(icmph->un.echo.sequence)); 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci /* Push ICMP header back */ 98162306a36Sopenharmony_ci skb_push(skb, skb->data - (u8 *)icmph); 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci sk = ping_lookup(net, skb, ntohs(icmph->un.echo.id)); 98462306a36Sopenharmony_ci if (sk) { 98562306a36Sopenharmony_ci struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC); 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci pr_debug("rcv on socket %p\n", sk); 98862306a36Sopenharmony_ci if (skb2) 98962306a36Sopenharmony_ci reason = __ping_queue_rcv_skb(sk, skb2); 99062306a36Sopenharmony_ci else 99162306a36Sopenharmony_ci reason = SKB_DROP_REASON_NOMEM; 99262306a36Sopenharmony_ci } 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci if (reason) 99562306a36Sopenharmony_ci pr_debug("no socket, dropping\n"); 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci return reason; 99862306a36Sopenharmony_ci} 99962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ping_rcv); 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_cistruct proto ping_prot = { 100262306a36Sopenharmony_ci .name = "PING", 100362306a36Sopenharmony_ci .owner = THIS_MODULE, 100462306a36Sopenharmony_ci .init = ping_init_sock, 100562306a36Sopenharmony_ci .close = ping_close, 100662306a36Sopenharmony_ci .pre_connect = ping_pre_connect, 100762306a36Sopenharmony_ci .connect = ip4_datagram_connect, 100862306a36Sopenharmony_ci .disconnect = __udp_disconnect, 100962306a36Sopenharmony_ci .setsockopt = ip_setsockopt, 101062306a36Sopenharmony_ci .getsockopt = ip_getsockopt, 101162306a36Sopenharmony_ci .sendmsg = ping_v4_sendmsg, 101262306a36Sopenharmony_ci .recvmsg = ping_recvmsg, 101362306a36Sopenharmony_ci .bind = ping_bind, 101462306a36Sopenharmony_ci .backlog_rcv = ping_queue_rcv_skb, 101562306a36Sopenharmony_ci .release_cb = ip4_datagram_release_cb, 101662306a36Sopenharmony_ci .hash = ping_hash, 101762306a36Sopenharmony_ci .unhash = ping_unhash, 101862306a36Sopenharmony_ci .get_port = ping_get_port, 101962306a36Sopenharmony_ci .put_port = ping_unhash, 102062306a36Sopenharmony_ci .obj_size = sizeof(struct inet_sock), 102162306a36Sopenharmony_ci}; 102262306a36Sopenharmony_ciEXPORT_SYMBOL(ping_prot); 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_ci#ifdef CONFIG_PROC_FS 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_cistatic struct sock *ping_get_first(struct seq_file *seq, int start) 102762306a36Sopenharmony_ci{ 102862306a36Sopenharmony_ci struct sock *sk; 102962306a36Sopenharmony_ci struct ping_iter_state *state = seq->private; 103062306a36Sopenharmony_ci struct net *net = seq_file_net(seq); 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_ci for (state->bucket = start; state->bucket < PING_HTABLE_SIZE; 103362306a36Sopenharmony_ci ++state->bucket) { 103462306a36Sopenharmony_ci struct hlist_head *hslot; 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci hslot = &ping_table.hash[state->bucket]; 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci if (hlist_empty(hslot)) 103962306a36Sopenharmony_ci continue; 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_ci sk_for_each(sk, hslot) { 104262306a36Sopenharmony_ci if (net_eq(sock_net(sk), net) && 104362306a36Sopenharmony_ci sk->sk_family == state->family) 104462306a36Sopenharmony_ci goto found; 104562306a36Sopenharmony_ci } 104662306a36Sopenharmony_ci } 104762306a36Sopenharmony_ci sk = NULL; 104862306a36Sopenharmony_cifound: 104962306a36Sopenharmony_ci return sk; 105062306a36Sopenharmony_ci} 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_cistatic struct sock *ping_get_next(struct seq_file *seq, struct sock *sk) 105362306a36Sopenharmony_ci{ 105462306a36Sopenharmony_ci struct ping_iter_state *state = seq->private; 105562306a36Sopenharmony_ci struct net *net = seq_file_net(seq); 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_ci do { 105862306a36Sopenharmony_ci sk = sk_next(sk); 105962306a36Sopenharmony_ci } while (sk && (!net_eq(sock_net(sk), net))); 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci if (!sk) 106262306a36Sopenharmony_ci return ping_get_first(seq, state->bucket + 1); 106362306a36Sopenharmony_ci return sk; 106462306a36Sopenharmony_ci} 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_cistatic struct sock *ping_get_idx(struct seq_file *seq, loff_t pos) 106762306a36Sopenharmony_ci{ 106862306a36Sopenharmony_ci struct sock *sk = ping_get_first(seq, 0); 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_ci if (sk) 107162306a36Sopenharmony_ci while (pos && (sk = ping_get_next(seq, sk)) != NULL) 107262306a36Sopenharmony_ci --pos; 107362306a36Sopenharmony_ci return pos ? NULL : sk; 107462306a36Sopenharmony_ci} 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_civoid *ping_seq_start(struct seq_file *seq, loff_t *pos, sa_family_t family) 107762306a36Sopenharmony_ci __acquires(ping_table.lock) 107862306a36Sopenharmony_ci{ 107962306a36Sopenharmony_ci struct ping_iter_state *state = seq->private; 108062306a36Sopenharmony_ci state->bucket = 0; 108162306a36Sopenharmony_ci state->family = family; 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_ci spin_lock(&ping_table.lock); 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci return *pos ? ping_get_idx(seq, *pos-1) : SEQ_START_TOKEN; 108662306a36Sopenharmony_ci} 108762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ping_seq_start); 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_cistatic void *ping_v4_seq_start(struct seq_file *seq, loff_t *pos) 109062306a36Sopenharmony_ci{ 109162306a36Sopenharmony_ci return ping_seq_start(seq, pos, AF_INET); 109262306a36Sopenharmony_ci} 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_civoid *ping_seq_next(struct seq_file *seq, void *v, loff_t *pos) 109562306a36Sopenharmony_ci{ 109662306a36Sopenharmony_ci struct sock *sk; 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci if (v == SEQ_START_TOKEN) 109962306a36Sopenharmony_ci sk = ping_get_idx(seq, 0); 110062306a36Sopenharmony_ci else 110162306a36Sopenharmony_ci sk = ping_get_next(seq, v); 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_ci ++*pos; 110462306a36Sopenharmony_ci return sk; 110562306a36Sopenharmony_ci} 110662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ping_seq_next); 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_civoid ping_seq_stop(struct seq_file *seq, void *v) 110962306a36Sopenharmony_ci __releases(ping_table.lock) 111062306a36Sopenharmony_ci{ 111162306a36Sopenharmony_ci spin_unlock(&ping_table.lock); 111262306a36Sopenharmony_ci} 111362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ping_seq_stop); 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_cistatic void ping_v4_format_sock(struct sock *sp, struct seq_file *f, 111662306a36Sopenharmony_ci int bucket) 111762306a36Sopenharmony_ci{ 111862306a36Sopenharmony_ci struct inet_sock *inet = inet_sk(sp); 111962306a36Sopenharmony_ci __be32 dest = inet->inet_daddr; 112062306a36Sopenharmony_ci __be32 src = inet->inet_rcv_saddr; 112162306a36Sopenharmony_ci __u16 destp = ntohs(inet->inet_dport); 112262306a36Sopenharmony_ci __u16 srcp = ntohs(inet->inet_sport); 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_ci seq_printf(f, "%5d: %08X:%04X %08X:%04X" 112562306a36Sopenharmony_ci " %02X %08X:%08X %02X:%08lX %08X %5u %8d %lu %d %pK %u", 112662306a36Sopenharmony_ci bucket, src, srcp, dest, destp, sp->sk_state, 112762306a36Sopenharmony_ci sk_wmem_alloc_get(sp), 112862306a36Sopenharmony_ci sk_rmem_alloc_get(sp), 112962306a36Sopenharmony_ci 0, 0L, 0, 113062306a36Sopenharmony_ci from_kuid_munged(seq_user_ns(f), sock_i_uid(sp)), 113162306a36Sopenharmony_ci 0, sock_i_ino(sp), 113262306a36Sopenharmony_ci refcount_read(&sp->sk_refcnt), sp, 113362306a36Sopenharmony_ci atomic_read(&sp->sk_drops)); 113462306a36Sopenharmony_ci} 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_cistatic int ping_v4_seq_show(struct seq_file *seq, void *v) 113762306a36Sopenharmony_ci{ 113862306a36Sopenharmony_ci seq_setwidth(seq, 127); 113962306a36Sopenharmony_ci if (v == SEQ_START_TOKEN) 114062306a36Sopenharmony_ci seq_puts(seq, " sl local_address rem_address st tx_queue " 114162306a36Sopenharmony_ci "rx_queue tr tm->when retrnsmt uid timeout " 114262306a36Sopenharmony_ci "inode ref pointer drops"); 114362306a36Sopenharmony_ci else { 114462306a36Sopenharmony_ci struct ping_iter_state *state = seq->private; 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_ci ping_v4_format_sock(v, seq, state->bucket); 114762306a36Sopenharmony_ci } 114862306a36Sopenharmony_ci seq_pad(seq, '\n'); 114962306a36Sopenharmony_ci return 0; 115062306a36Sopenharmony_ci} 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_cistatic const struct seq_operations ping_v4_seq_ops = { 115362306a36Sopenharmony_ci .start = ping_v4_seq_start, 115462306a36Sopenharmony_ci .show = ping_v4_seq_show, 115562306a36Sopenharmony_ci .next = ping_seq_next, 115662306a36Sopenharmony_ci .stop = ping_seq_stop, 115762306a36Sopenharmony_ci}; 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_cistatic int __net_init ping_v4_proc_init_net(struct net *net) 116062306a36Sopenharmony_ci{ 116162306a36Sopenharmony_ci if (!proc_create_net("icmp", 0444, net->proc_net, &ping_v4_seq_ops, 116262306a36Sopenharmony_ci sizeof(struct ping_iter_state))) 116362306a36Sopenharmony_ci return -ENOMEM; 116462306a36Sopenharmony_ci return 0; 116562306a36Sopenharmony_ci} 116662306a36Sopenharmony_ci 116762306a36Sopenharmony_cistatic void __net_exit ping_v4_proc_exit_net(struct net *net) 116862306a36Sopenharmony_ci{ 116962306a36Sopenharmony_ci remove_proc_entry("icmp", net->proc_net); 117062306a36Sopenharmony_ci} 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_cistatic struct pernet_operations ping_v4_net_ops = { 117362306a36Sopenharmony_ci .init = ping_v4_proc_init_net, 117462306a36Sopenharmony_ci .exit = ping_v4_proc_exit_net, 117562306a36Sopenharmony_ci}; 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ciint __init ping_proc_init(void) 117862306a36Sopenharmony_ci{ 117962306a36Sopenharmony_ci return register_pernet_subsys(&ping_v4_net_ops); 118062306a36Sopenharmony_ci} 118162306a36Sopenharmony_ci 118262306a36Sopenharmony_civoid ping_proc_exit(void) 118362306a36Sopenharmony_ci{ 118462306a36Sopenharmony_ci unregister_pernet_subsys(&ping_v4_net_ops); 118562306a36Sopenharmony_ci} 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_ci#endif 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_civoid __init ping_init(void) 119062306a36Sopenharmony_ci{ 119162306a36Sopenharmony_ci int i; 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_ci for (i = 0; i < PING_HTABLE_SIZE; i++) 119462306a36Sopenharmony_ci INIT_HLIST_HEAD(&ping_table.hash[i]); 119562306a36Sopenharmony_ci spin_lock_init(&ping_table.lock); 119662306a36Sopenharmony_ci} 1197