1c319b4d7SVasiliy Kulikov /* 2c319b4d7SVasiliy Kulikov * INET An implementation of the TCP/IP protocol suite for the LINUX 3c319b4d7SVasiliy Kulikov * operating system. INET is implemented using the BSD Socket 4c319b4d7SVasiliy Kulikov * interface as the means of communication with the user level. 5c319b4d7SVasiliy Kulikov * 6c319b4d7SVasiliy Kulikov * "Ping" sockets 7c319b4d7SVasiliy Kulikov * 8c319b4d7SVasiliy Kulikov * This program is free software; you can redistribute it and/or 9c319b4d7SVasiliy Kulikov * modify it under the terms of the GNU General Public License 10c319b4d7SVasiliy Kulikov * as published by the Free Software Foundation; either version 11c319b4d7SVasiliy Kulikov * 2 of the License, or (at your option) any later version. 12c319b4d7SVasiliy Kulikov * 13c319b4d7SVasiliy Kulikov * Based on ipv4/udp.c code. 14c319b4d7SVasiliy Kulikov * 15c319b4d7SVasiliy Kulikov * Authors: Vasiliy Kulikov / Openwall (for Linux 2.6), 16c319b4d7SVasiliy Kulikov * Pavel Kankovsky (for Linux 2.4.32) 17c319b4d7SVasiliy Kulikov * 18c319b4d7SVasiliy Kulikov * Pavel gave all rights to bugs to Vasiliy, 19c319b4d7SVasiliy Kulikov * none of the bugs are Pavel's now. 20c319b4d7SVasiliy Kulikov * 21c319b4d7SVasiliy Kulikov */ 22c319b4d7SVasiliy Kulikov 23c319b4d7SVasiliy Kulikov #include <linux/uaccess.h> 24c319b4d7SVasiliy Kulikov #include <linux/types.h> 25c319b4d7SVasiliy Kulikov #include <linux/fcntl.h> 26c319b4d7SVasiliy Kulikov #include <linux/socket.h> 27c319b4d7SVasiliy Kulikov #include <linux/sockios.h> 28c319b4d7SVasiliy Kulikov #include <linux/in.h> 29c319b4d7SVasiliy Kulikov #include <linux/errno.h> 30c319b4d7SVasiliy Kulikov #include <linux/timer.h> 31c319b4d7SVasiliy Kulikov #include <linux/mm.h> 32c319b4d7SVasiliy Kulikov #include <linux/inet.h> 33c319b4d7SVasiliy Kulikov #include <linux/netdevice.h> 34c319b4d7SVasiliy Kulikov #include <net/snmp.h> 35c319b4d7SVasiliy Kulikov #include <net/ip.h> 36c319b4d7SVasiliy Kulikov #include <net/ipv6.h> 37c319b4d7SVasiliy Kulikov #include <net/icmp.h> 38c319b4d7SVasiliy Kulikov #include <net/protocol.h> 39c319b4d7SVasiliy Kulikov #include <linux/skbuff.h> 40c319b4d7SVasiliy Kulikov #include <linux/proc_fs.h> 41bc3b2d7fSPaul Gortmaker #include <linux/export.h> 42c319b4d7SVasiliy Kulikov #include <net/sock.h> 43c319b4d7SVasiliy Kulikov #include <net/ping.h> 44c319b4d7SVasiliy Kulikov #include <net/udp.h> 45c319b4d7SVasiliy Kulikov #include <net/route.h> 46c319b4d7SVasiliy Kulikov #include <net/inet_common.h> 47c319b4d7SVasiliy Kulikov #include <net/checksum.h> 48c319b4d7SVasiliy Kulikov 49c319b4d7SVasiliy Kulikov 501b1cb1f7SEric Dumazet static struct ping_table ping_table; 51c319b4d7SVasiliy Kulikov 521b1cb1f7SEric Dumazet static u16 ping_port_rover; 53c319b4d7SVasiliy Kulikov 5495c96174SEric Dumazet static inline int ping_hashfn(struct net *net, unsigned int num, unsigned int mask) 55c319b4d7SVasiliy Kulikov { 56c319b4d7SVasiliy Kulikov int res = (num + net_hash_mix(net)) & mask; 5795c96174SEric Dumazet 58c319b4d7SVasiliy Kulikov pr_debug("hash(%d) = %d\n", num, res); 59c319b4d7SVasiliy Kulikov return res; 60c319b4d7SVasiliy Kulikov } 61c319b4d7SVasiliy Kulikov 62c319b4d7SVasiliy Kulikov static inline struct hlist_nulls_head *ping_hashslot(struct ping_table *table, 6395c96174SEric Dumazet struct net *net, unsigned int num) 64c319b4d7SVasiliy Kulikov { 65c319b4d7SVasiliy Kulikov return &table->hash[ping_hashfn(net, num, PING_HTABLE_MASK)]; 66c319b4d7SVasiliy Kulikov } 67c319b4d7SVasiliy Kulikov 68c319b4d7SVasiliy Kulikov static int ping_v4_get_port(struct sock *sk, unsigned short ident) 69c319b4d7SVasiliy Kulikov { 70c319b4d7SVasiliy Kulikov struct hlist_nulls_node *node; 71c319b4d7SVasiliy Kulikov struct hlist_nulls_head *hlist; 72c319b4d7SVasiliy Kulikov struct inet_sock *isk, *isk2; 73c319b4d7SVasiliy Kulikov struct sock *sk2 = NULL; 74c319b4d7SVasiliy Kulikov 75c319b4d7SVasiliy Kulikov isk = inet_sk(sk); 76c319b4d7SVasiliy Kulikov write_lock_bh(&ping_table.lock); 77c319b4d7SVasiliy Kulikov if (ident == 0) { 78c319b4d7SVasiliy Kulikov u32 i; 79c319b4d7SVasiliy Kulikov u16 result = ping_port_rover + 1; 80c319b4d7SVasiliy Kulikov 81c319b4d7SVasiliy Kulikov for (i = 0; i < (1L << 16); i++, result++) { 82c319b4d7SVasiliy Kulikov if (!result) 83c319b4d7SVasiliy Kulikov result++; /* avoid zero */ 84c319b4d7SVasiliy Kulikov hlist = ping_hashslot(&ping_table, sock_net(sk), 85c319b4d7SVasiliy Kulikov result); 86c319b4d7SVasiliy Kulikov ping_portaddr_for_each_entry(sk2, node, hlist) { 87c319b4d7SVasiliy Kulikov isk2 = inet_sk(sk2); 88c319b4d7SVasiliy Kulikov 89c319b4d7SVasiliy Kulikov if (isk2->inet_num == result) 90c319b4d7SVasiliy Kulikov goto next_port; 91c319b4d7SVasiliy Kulikov } 92c319b4d7SVasiliy Kulikov 93c319b4d7SVasiliy Kulikov /* found */ 94c319b4d7SVasiliy Kulikov ping_port_rover = ident = result; 95c319b4d7SVasiliy Kulikov break; 96c319b4d7SVasiliy Kulikov next_port: 97c319b4d7SVasiliy Kulikov ; 98c319b4d7SVasiliy Kulikov } 99c319b4d7SVasiliy Kulikov if (i >= (1L << 16)) 100c319b4d7SVasiliy Kulikov goto fail; 101c319b4d7SVasiliy Kulikov } else { 102c319b4d7SVasiliy Kulikov hlist = ping_hashslot(&ping_table, sock_net(sk), ident); 103c319b4d7SVasiliy Kulikov ping_portaddr_for_each_entry(sk2, node, hlist) { 104c319b4d7SVasiliy Kulikov isk2 = inet_sk(sk2); 105c319b4d7SVasiliy Kulikov 106c319b4d7SVasiliy Kulikov if ((isk2->inet_num == ident) && 107c319b4d7SVasiliy Kulikov (sk2 != sk) && 108c319b4d7SVasiliy Kulikov (!sk2->sk_reuse || !sk->sk_reuse)) 109c319b4d7SVasiliy Kulikov goto fail; 110c319b4d7SVasiliy Kulikov } 111c319b4d7SVasiliy Kulikov } 112c319b4d7SVasiliy Kulikov 113c319b4d7SVasiliy Kulikov pr_debug("found port/ident = %d\n", ident); 114c319b4d7SVasiliy Kulikov isk->inet_num = ident; 115c319b4d7SVasiliy Kulikov if (sk_unhashed(sk)) { 116c319b4d7SVasiliy Kulikov pr_debug("was not hashed\n"); 117c319b4d7SVasiliy Kulikov sock_hold(sk); 118c319b4d7SVasiliy Kulikov hlist_nulls_add_head(&sk->sk_nulls_node, hlist); 119c319b4d7SVasiliy Kulikov sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1); 120c319b4d7SVasiliy Kulikov } 121c319b4d7SVasiliy Kulikov write_unlock_bh(&ping_table.lock); 122c319b4d7SVasiliy Kulikov return 0; 123c319b4d7SVasiliy Kulikov 124c319b4d7SVasiliy Kulikov fail: 125c319b4d7SVasiliy Kulikov write_unlock_bh(&ping_table.lock); 126c319b4d7SVasiliy Kulikov return 1; 127c319b4d7SVasiliy Kulikov } 128c319b4d7SVasiliy Kulikov 129c319b4d7SVasiliy Kulikov static void ping_v4_hash(struct sock *sk) 130c319b4d7SVasiliy Kulikov { 131c319b4d7SVasiliy Kulikov pr_debug("ping_v4_hash(sk->port=%u)\n", inet_sk(sk)->inet_num); 132c319b4d7SVasiliy Kulikov BUG(); /* "Please do not press this button again." */ 133c319b4d7SVasiliy Kulikov } 134c319b4d7SVasiliy Kulikov 135c319b4d7SVasiliy Kulikov static void ping_v4_unhash(struct sock *sk) 136c319b4d7SVasiliy Kulikov { 137c319b4d7SVasiliy Kulikov struct inet_sock *isk = inet_sk(sk); 138c319b4d7SVasiliy Kulikov pr_debug("ping_v4_unhash(isk=%p,isk->num=%u)\n", isk, isk->inet_num); 139c319b4d7SVasiliy Kulikov if (sk_hashed(sk)) { 140c319b4d7SVasiliy Kulikov write_lock_bh(&ping_table.lock); 141c319b4d7SVasiliy Kulikov hlist_nulls_del(&sk->sk_nulls_node); 142c319b4d7SVasiliy Kulikov sock_put(sk); 143747465efSEric Dumazet isk->inet_num = 0; 144747465efSEric Dumazet isk->inet_sport = 0; 145c319b4d7SVasiliy Kulikov sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1); 146c319b4d7SVasiliy Kulikov write_unlock_bh(&ping_table.lock); 147c319b4d7SVasiliy Kulikov } 148c319b4d7SVasiliy Kulikov } 149c319b4d7SVasiliy Kulikov 150747465efSEric Dumazet static struct sock *ping_v4_lookup(struct net *net, __be32 saddr, __be32 daddr, 151c319b4d7SVasiliy Kulikov u16 ident, int dif) 152c319b4d7SVasiliy Kulikov { 153c319b4d7SVasiliy Kulikov struct hlist_nulls_head *hslot = ping_hashslot(&ping_table, net, ident); 154c319b4d7SVasiliy Kulikov struct sock *sk = NULL; 155c319b4d7SVasiliy Kulikov struct inet_sock *isk; 156c319b4d7SVasiliy Kulikov struct hlist_nulls_node *hnode; 157c319b4d7SVasiliy Kulikov 158747465efSEric Dumazet pr_debug("try to find: num = %d, daddr = %pI4, dif = %d\n", 159747465efSEric Dumazet (int)ident, &daddr, dif); 160c319b4d7SVasiliy Kulikov read_lock_bh(&ping_table.lock); 161c319b4d7SVasiliy Kulikov 162c319b4d7SVasiliy Kulikov ping_portaddr_for_each_entry(sk, hnode, hslot) { 163c319b4d7SVasiliy Kulikov isk = inet_sk(sk); 164c319b4d7SVasiliy Kulikov 165747465efSEric Dumazet pr_debug("found: %p: num = %d, daddr = %pI4, dif = %d\n", sk, 166747465efSEric Dumazet (int)isk->inet_num, &isk->inet_rcv_saddr, 167c319b4d7SVasiliy Kulikov sk->sk_bound_dev_if); 168c319b4d7SVasiliy Kulikov 169c319b4d7SVasiliy Kulikov pr_debug("iterate\n"); 170c319b4d7SVasiliy Kulikov if (isk->inet_num != ident) 171c319b4d7SVasiliy Kulikov continue; 172c319b4d7SVasiliy Kulikov if (isk->inet_rcv_saddr && isk->inet_rcv_saddr != daddr) 173c319b4d7SVasiliy Kulikov continue; 174c319b4d7SVasiliy Kulikov if (sk->sk_bound_dev_if && sk->sk_bound_dev_if != dif) 175c319b4d7SVasiliy Kulikov continue; 176c319b4d7SVasiliy Kulikov 177c319b4d7SVasiliy Kulikov sock_hold(sk); 178c319b4d7SVasiliy Kulikov goto exit; 179c319b4d7SVasiliy Kulikov } 180c319b4d7SVasiliy Kulikov 181c319b4d7SVasiliy Kulikov sk = NULL; 182c319b4d7SVasiliy Kulikov exit: 183c319b4d7SVasiliy Kulikov read_unlock_bh(&ping_table.lock); 184c319b4d7SVasiliy Kulikov 185c319b4d7SVasiliy Kulikov return sk; 186c319b4d7SVasiliy Kulikov } 187c319b4d7SVasiliy Kulikov 18875e308c8SChangli Gao static void inet_get_ping_group_range_net(struct net *net, gid_t *low, 18975e308c8SChangli Gao gid_t *high) 190f56e03e8SVasiliy Kulikov { 191f56e03e8SVasiliy Kulikov gid_t *data = net->ipv4.sysctl_ping_group_range; 19295c96174SEric Dumazet unsigned int seq; 19395c96174SEric Dumazet 194f56e03e8SVasiliy Kulikov do { 195f56e03e8SVasiliy Kulikov seq = read_seqbegin(&sysctl_local_ports.lock); 196f56e03e8SVasiliy Kulikov 197f56e03e8SVasiliy Kulikov *low = data[0]; 198f56e03e8SVasiliy Kulikov *high = data[1]; 199f56e03e8SVasiliy Kulikov } while (read_seqretry(&sysctl_local_ports.lock, seq)); 200f56e03e8SVasiliy Kulikov } 201f56e03e8SVasiliy Kulikov 202f56e03e8SVasiliy Kulikov 203c319b4d7SVasiliy Kulikov static int ping_init_sock(struct sock *sk) 204c319b4d7SVasiliy Kulikov { 205c319b4d7SVasiliy Kulikov struct net *net = sock_net(sk); 206c319b4d7SVasiliy Kulikov gid_t group = current_egid(); 207c319b4d7SVasiliy Kulikov gid_t range[2]; 208c319b4d7SVasiliy Kulikov struct group_info *group_info = get_current_groups(); 209c319b4d7SVasiliy Kulikov int i, j, count = group_info->ngroups; 210ae2975bcSEric W. Biederman kgid_t low, high; 211c319b4d7SVasiliy Kulikov 212c319b4d7SVasiliy Kulikov inet_get_ping_group_range_net(net, range, range+1); 213ae2975bcSEric W. Biederman low = make_kgid(&init_user_ns, range[0]); 214ae2975bcSEric W. Biederman high = make_kgid(&init_user_ns, range[1]); 215ae2975bcSEric W. Biederman if (!gid_valid(low) || !gid_valid(high) || gid_lt(high, low)) 216ae2975bcSEric W. Biederman return -EACCES; 217ae2975bcSEric W. Biederman 218c319b4d7SVasiliy Kulikov if (range[0] <= group && group <= range[1]) 219c319b4d7SVasiliy Kulikov return 0; 220c319b4d7SVasiliy Kulikov 221c319b4d7SVasiliy Kulikov for (i = 0; i < group_info->nblocks; i++) { 222c319b4d7SVasiliy Kulikov int cp_count = min_t(int, NGROUPS_PER_BLOCK, count); 223c319b4d7SVasiliy Kulikov for (j = 0; j < cp_count; j++) { 224ae2975bcSEric W. Biederman kgid_t gid = group_info->blocks[i][j]; 225ae2975bcSEric W. Biederman if (gid_lte(low, gid) && gid_lte(gid, high)) 226c319b4d7SVasiliy Kulikov return 0; 227c319b4d7SVasiliy Kulikov } 228c319b4d7SVasiliy Kulikov 229c319b4d7SVasiliy Kulikov count -= cp_count; 230c319b4d7SVasiliy Kulikov } 231c319b4d7SVasiliy Kulikov 232c319b4d7SVasiliy Kulikov return -EACCES; 233c319b4d7SVasiliy Kulikov } 234c319b4d7SVasiliy Kulikov 235c319b4d7SVasiliy Kulikov static void ping_close(struct sock *sk, long timeout) 236c319b4d7SVasiliy Kulikov { 237c319b4d7SVasiliy Kulikov pr_debug("ping_close(sk=%p,sk->num=%u)\n", 238c319b4d7SVasiliy Kulikov inet_sk(sk), inet_sk(sk)->inet_num); 239c319b4d7SVasiliy Kulikov pr_debug("isk->refcnt = %d\n", sk->sk_refcnt.counter); 240c319b4d7SVasiliy Kulikov 241c319b4d7SVasiliy Kulikov sk_common_release(sk); 242c319b4d7SVasiliy Kulikov } 243c319b4d7SVasiliy Kulikov 244c319b4d7SVasiliy Kulikov /* 245c319b4d7SVasiliy Kulikov * We need our own bind because there are no privileged id's == local ports. 246c319b4d7SVasiliy Kulikov * Moreover, we don't allow binding to multi- and broadcast addresses. 247c319b4d7SVasiliy Kulikov */ 248c319b4d7SVasiliy Kulikov 249c319b4d7SVasiliy Kulikov static int ping_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len) 250c319b4d7SVasiliy Kulikov { 251c319b4d7SVasiliy Kulikov struct sockaddr_in *addr = (struct sockaddr_in *)uaddr; 252c319b4d7SVasiliy Kulikov struct inet_sock *isk = inet_sk(sk); 253c319b4d7SVasiliy Kulikov unsigned short snum; 254c319b4d7SVasiliy Kulikov int chk_addr_ret; 255c319b4d7SVasiliy Kulikov int err; 256c319b4d7SVasiliy Kulikov 257c319b4d7SVasiliy Kulikov if (addr_len < sizeof(struct sockaddr_in)) 258c319b4d7SVasiliy Kulikov return -EINVAL; 259c319b4d7SVasiliy Kulikov 260c319b4d7SVasiliy Kulikov pr_debug("ping_v4_bind(sk=%p,sa_addr=%08x,sa_port=%d)\n", 261c319b4d7SVasiliy Kulikov sk, addr->sin_addr.s_addr, ntohs(addr->sin_port)); 262c319b4d7SVasiliy Kulikov 263c319b4d7SVasiliy Kulikov chk_addr_ret = inet_addr_type(sock_net(sk), addr->sin_addr.s_addr); 264747465efSEric Dumazet if (addr->sin_addr.s_addr == htonl(INADDR_ANY)) 265c319b4d7SVasiliy Kulikov chk_addr_ret = RTN_LOCAL; 266c319b4d7SVasiliy Kulikov 267c319b4d7SVasiliy Kulikov if ((sysctl_ip_nonlocal_bind == 0 && 268c319b4d7SVasiliy Kulikov isk->freebind == 0 && isk->transparent == 0 && 269c319b4d7SVasiliy Kulikov chk_addr_ret != RTN_LOCAL) || 270c319b4d7SVasiliy Kulikov chk_addr_ret == RTN_MULTICAST || 271c319b4d7SVasiliy Kulikov chk_addr_ret == RTN_BROADCAST) 272c319b4d7SVasiliy Kulikov return -EADDRNOTAVAIL; 273c319b4d7SVasiliy Kulikov 274c319b4d7SVasiliy Kulikov lock_sock(sk); 275c319b4d7SVasiliy Kulikov 276c319b4d7SVasiliy Kulikov err = -EINVAL; 277c319b4d7SVasiliy Kulikov if (isk->inet_num != 0) 278c319b4d7SVasiliy Kulikov goto out; 279c319b4d7SVasiliy Kulikov 280c319b4d7SVasiliy Kulikov err = -EADDRINUSE; 281c319b4d7SVasiliy Kulikov isk->inet_rcv_saddr = isk->inet_saddr = addr->sin_addr.s_addr; 282c319b4d7SVasiliy Kulikov snum = ntohs(addr->sin_port); 283c319b4d7SVasiliy Kulikov if (ping_v4_get_port(sk, snum) != 0) { 284c319b4d7SVasiliy Kulikov isk->inet_saddr = isk->inet_rcv_saddr = 0; 285c319b4d7SVasiliy Kulikov goto out; 286c319b4d7SVasiliy Kulikov } 287c319b4d7SVasiliy Kulikov 288747465efSEric Dumazet pr_debug("after bind(): num = %d, daddr = %pI4, dif = %d\n", 289c319b4d7SVasiliy Kulikov (int)isk->inet_num, 290747465efSEric Dumazet &isk->inet_rcv_saddr, 291c319b4d7SVasiliy Kulikov (int)sk->sk_bound_dev_if); 292c319b4d7SVasiliy Kulikov 293c319b4d7SVasiliy Kulikov err = 0; 294c319b4d7SVasiliy Kulikov if (isk->inet_rcv_saddr) 295c319b4d7SVasiliy Kulikov sk->sk_userlocks |= SOCK_BINDADDR_LOCK; 296c319b4d7SVasiliy Kulikov if (snum) 297c319b4d7SVasiliy Kulikov sk->sk_userlocks |= SOCK_BINDPORT_LOCK; 298c319b4d7SVasiliy Kulikov isk->inet_sport = htons(isk->inet_num); 299c319b4d7SVasiliy Kulikov isk->inet_daddr = 0; 300c319b4d7SVasiliy Kulikov isk->inet_dport = 0; 301c319b4d7SVasiliy Kulikov sk_dst_reset(sk); 302c319b4d7SVasiliy Kulikov out: 303c319b4d7SVasiliy Kulikov release_sock(sk); 304c319b4d7SVasiliy Kulikov pr_debug("ping_v4_bind -> %d\n", err); 305c319b4d7SVasiliy Kulikov return err; 306c319b4d7SVasiliy Kulikov } 307c319b4d7SVasiliy Kulikov 308c319b4d7SVasiliy Kulikov /* 309c319b4d7SVasiliy Kulikov * Is this a supported type of ICMP message? 310c319b4d7SVasiliy Kulikov */ 311c319b4d7SVasiliy Kulikov 312c319b4d7SVasiliy Kulikov static inline int ping_supported(int type, int code) 313c319b4d7SVasiliy Kulikov { 314c319b4d7SVasiliy Kulikov if (type == ICMP_ECHO && code == 0) 315c319b4d7SVasiliy Kulikov return 1; 316c319b4d7SVasiliy Kulikov return 0; 317c319b4d7SVasiliy Kulikov } 318c319b4d7SVasiliy Kulikov 319c319b4d7SVasiliy Kulikov /* 320c319b4d7SVasiliy Kulikov * This routine is called by the ICMP module when it gets some 321c319b4d7SVasiliy Kulikov * sort of error condition. 322c319b4d7SVasiliy Kulikov */ 323c319b4d7SVasiliy Kulikov 324c319b4d7SVasiliy Kulikov static int ping_queue_rcv_skb(struct sock *sk, struct sk_buff *skb); 325c319b4d7SVasiliy Kulikov 326c319b4d7SVasiliy Kulikov void ping_err(struct sk_buff *skb, u32 info) 327c319b4d7SVasiliy Kulikov { 328c319b4d7SVasiliy Kulikov struct iphdr *iph = (struct iphdr *)skb->data; 329c319b4d7SVasiliy Kulikov struct icmphdr *icmph = (struct icmphdr *)(skb->data+(iph->ihl<<2)); 330c319b4d7SVasiliy Kulikov struct inet_sock *inet_sock; 331c319b4d7SVasiliy Kulikov int type = icmph->type; 332c319b4d7SVasiliy Kulikov int code = icmph->code; 333c319b4d7SVasiliy Kulikov struct net *net = dev_net(skb->dev); 334c319b4d7SVasiliy Kulikov struct sock *sk; 335c319b4d7SVasiliy Kulikov int harderr; 336c319b4d7SVasiliy Kulikov int err; 337c319b4d7SVasiliy Kulikov 338c319b4d7SVasiliy Kulikov /* We assume the packet has already been checked by icmp_unreach */ 339c319b4d7SVasiliy Kulikov 340c319b4d7SVasiliy Kulikov if (!ping_supported(icmph->type, icmph->code)) 341c319b4d7SVasiliy Kulikov return; 342c319b4d7SVasiliy Kulikov 343c319b4d7SVasiliy Kulikov pr_debug("ping_err(type=%04x,code=%04x,id=%04x,seq=%04x)\n", type, 344c319b4d7SVasiliy Kulikov code, ntohs(icmph->un.echo.id), ntohs(icmph->un.echo.sequence)); 345c319b4d7SVasiliy Kulikov 346c319b4d7SVasiliy Kulikov sk = ping_v4_lookup(net, iph->daddr, iph->saddr, 347c319b4d7SVasiliy Kulikov ntohs(icmph->un.echo.id), skb->dev->ifindex); 348c319b4d7SVasiliy Kulikov if (sk == NULL) { 349c319b4d7SVasiliy Kulikov pr_debug("no socket, dropping\n"); 350c319b4d7SVasiliy Kulikov return; /* No socket for error */ 351c319b4d7SVasiliy Kulikov } 352c319b4d7SVasiliy Kulikov pr_debug("err on socket %p\n", sk); 353c319b4d7SVasiliy Kulikov 354c319b4d7SVasiliy Kulikov err = 0; 355c319b4d7SVasiliy Kulikov harderr = 0; 356c319b4d7SVasiliy Kulikov inet_sock = inet_sk(sk); 357c319b4d7SVasiliy Kulikov 358c319b4d7SVasiliy Kulikov switch (type) { 359c319b4d7SVasiliy Kulikov default: 360c319b4d7SVasiliy Kulikov case ICMP_TIME_EXCEEDED: 361c319b4d7SVasiliy Kulikov err = EHOSTUNREACH; 362c319b4d7SVasiliy Kulikov break; 363c319b4d7SVasiliy Kulikov case ICMP_SOURCE_QUENCH: 364c319b4d7SVasiliy Kulikov /* This is not a real error but ping wants to see it. 365c319b4d7SVasiliy Kulikov * Report it with some fake errno. */ 366c319b4d7SVasiliy Kulikov err = EREMOTEIO; 367c319b4d7SVasiliy Kulikov break; 368c319b4d7SVasiliy Kulikov case ICMP_PARAMETERPROB: 369c319b4d7SVasiliy Kulikov err = EPROTO; 370c319b4d7SVasiliy Kulikov harderr = 1; 371c319b4d7SVasiliy Kulikov break; 372c319b4d7SVasiliy Kulikov case ICMP_DEST_UNREACH: 373c319b4d7SVasiliy Kulikov if (code == ICMP_FRAG_NEEDED) { /* Path MTU discovery */ 374*36393395SDavid S. Miller ipv4_sk_update_pmtu(skb, sk, info); 375c319b4d7SVasiliy Kulikov if (inet_sock->pmtudisc != IP_PMTUDISC_DONT) { 376c319b4d7SVasiliy Kulikov err = EMSGSIZE; 377c319b4d7SVasiliy Kulikov harderr = 1; 378c319b4d7SVasiliy Kulikov break; 379c319b4d7SVasiliy Kulikov } 380c319b4d7SVasiliy Kulikov goto out; 381c319b4d7SVasiliy Kulikov } 382c319b4d7SVasiliy Kulikov err = EHOSTUNREACH; 383c319b4d7SVasiliy Kulikov if (code <= NR_ICMP_UNREACH) { 384c319b4d7SVasiliy Kulikov harderr = icmp_err_convert[code].fatal; 385c319b4d7SVasiliy Kulikov err = icmp_err_convert[code].errno; 386c319b4d7SVasiliy Kulikov } 387c319b4d7SVasiliy Kulikov break; 388c319b4d7SVasiliy Kulikov case ICMP_REDIRECT: 389c319b4d7SVasiliy Kulikov /* See ICMP_SOURCE_QUENCH */ 390c319b4d7SVasiliy Kulikov err = EREMOTEIO; 391c319b4d7SVasiliy Kulikov break; 392c319b4d7SVasiliy Kulikov } 393c319b4d7SVasiliy Kulikov 394c319b4d7SVasiliy Kulikov /* 395c319b4d7SVasiliy Kulikov * RFC1122: OK. Passes ICMP errors back to application, as per 396c319b4d7SVasiliy Kulikov * 4.1.3.3. 397c319b4d7SVasiliy Kulikov */ 398c319b4d7SVasiliy Kulikov if (!inet_sock->recverr) { 399c319b4d7SVasiliy Kulikov if (!harderr || sk->sk_state != TCP_ESTABLISHED) 400c319b4d7SVasiliy Kulikov goto out; 401c319b4d7SVasiliy Kulikov } else { 402c319b4d7SVasiliy Kulikov ip_icmp_error(sk, skb, err, 0 /* no remote port */, 403c319b4d7SVasiliy Kulikov info, (u8 *)icmph); 404c319b4d7SVasiliy Kulikov } 405c319b4d7SVasiliy Kulikov sk->sk_err = err; 406c319b4d7SVasiliy Kulikov sk->sk_error_report(sk); 407c319b4d7SVasiliy Kulikov out: 408c319b4d7SVasiliy Kulikov sock_put(sk); 409c319b4d7SVasiliy Kulikov } 410c319b4d7SVasiliy Kulikov 411c319b4d7SVasiliy Kulikov /* 412c319b4d7SVasiliy Kulikov * Copy and checksum an ICMP Echo packet from user space into a buffer. 413c319b4d7SVasiliy Kulikov */ 414c319b4d7SVasiliy Kulikov 415c319b4d7SVasiliy Kulikov struct pingfakehdr { 416c319b4d7SVasiliy Kulikov struct icmphdr icmph; 417c319b4d7SVasiliy Kulikov struct iovec *iov; 418747465efSEric Dumazet __wsum wcheck; 419c319b4d7SVasiliy Kulikov }; 420c319b4d7SVasiliy Kulikov 421c319b4d7SVasiliy Kulikov static int ping_getfrag(void *from, char *to, 422c319b4d7SVasiliy Kulikov int offset, int fraglen, int odd, struct sk_buff *skb) 423c319b4d7SVasiliy Kulikov { 424c319b4d7SVasiliy Kulikov struct pingfakehdr *pfh = (struct pingfakehdr *)from; 425c319b4d7SVasiliy Kulikov 426c319b4d7SVasiliy Kulikov if (offset == 0) { 427c319b4d7SVasiliy Kulikov if (fraglen < sizeof(struct icmphdr)) 428c319b4d7SVasiliy Kulikov BUG(); 429c319b4d7SVasiliy Kulikov if (csum_partial_copy_fromiovecend(to + sizeof(struct icmphdr), 430c319b4d7SVasiliy Kulikov pfh->iov, 0, fraglen - sizeof(struct icmphdr), 431c319b4d7SVasiliy Kulikov &pfh->wcheck)) 432c319b4d7SVasiliy Kulikov return -EFAULT; 433c319b4d7SVasiliy Kulikov 434c319b4d7SVasiliy Kulikov return 0; 435c319b4d7SVasiliy Kulikov } 436c319b4d7SVasiliy Kulikov if (offset < sizeof(struct icmphdr)) 437c319b4d7SVasiliy Kulikov BUG(); 438c319b4d7SVasiliy Kulikov if (csum_partial_copy_fromiovecend 439c319b4d7SVasiliy Kulikov (to, pfh->iov, offset - sizeof(struct icmphdr), 440c319b4d7SVasiliy Kulikov fraglen, &pfh->wcheck)) 441c319b4d7SVasiliy Kulikov return -EFAULT; 442c319b4d7SVasiliy Kulikov return 0; 443c319b4d7SVasiliy Kulikov } 444c319b4d7SVasiliy Kulikov 44575e308c8SChangli Gao static int ping_push_pending_frames(struct sock *sk, struct pingfakehdr *pfh, 44675e308c8SChangli Gao struct flowi4 *fl4) 447c319b4d7SVasiliy Kulikov { 448c319b4d7SVasiliy Kulikov struct sk_buff *skb = skb_peek(&sk->sk_write_queue); 449c319b4d7SVasiliy Kulikov 450c319b4d7SVasiliy Kulikov pfh->wcheck = csum_partial((char *)&pfh->icmph, 451c319b4d7SVasiliy Kulikov sizeof(struct icmphdr), pfh->wcheck); 452c319b4d7SVasiliy Kulikov pfh->icmph.checksum = csum_fold(pfh->wcheck); 453c319b4d7SVasiliy Kulikov memcpy(icmp_hdr(skb), &pfh->icmph, sizeof(struct icmphdr)); 454c319b4d7SVasiliy Kulikov skb->ip_summed = CHECKSUM_NONE; 455c319b4d7SVasiliy Kulikov return ip_push_pending_frames(sk, fl4); 456c319b4d7SVasiliy Kulikov } 457c319b4d7SVasiliy Kulikov 458bb0cd2fbSChangli Gao static int ping_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, 459c319b4d7SVasiliy Kulikov size_t len) 460c319b4d7SVasiliy Kulikov { 461c319b4d7SVasiliy Kulikov struct net *net = sock_net(sk); 462c319b4d7SVasiliy Kulikov struct flowi4 fl4; 463c319b4d7SVasiliy Kulikov struct inet_sock *inet = inet_sk(sk); 464c319b4d7SVasiliy Kulikov struct ipcm_cookie ipc; 465c319b4d7SVasiliy Kulikov struct icmphdr user_icmph; 466c319b4d7SVasiliy Kulikov struct pingfakehdr pfh; 467c319b4d7SVasiliy Kulikov struct rtable *rt = NULL; 468c319b4d7SVasiliy Kulikov struct ip_options_data opt_copy; 469c319b4d7SVasiliy Kulikov int free = 0; 470747465efSEric Dumazet __be32 saddr, daddr, faddr; 471c319b4d7SVasiliy Kulikov u8 tos; 472c319b4d7SVasiliy Kulikov int err; 473c319b4d7SVasiliy Kulikov 474c319b4d7SVasiliy Kulikov pr_debug("ping_sendmsg(sk=%p,sk->num=%u)\n", inet, inet->inet_num); 475c319b4d7SVasiliy Kulikov 476c319b4d7SVasiliy Kulikov 477c319b4d7SVasiliy Kulikov if (len > 0xFFFF) 478c319b4d7SVasiliy Kulikov return -EMSGSIZE; 479c319b4d7SVasiliy Kulikov 480c319b4d7SVasiliy Kulikov /* 481c319b4d7SVasiliy Kulikov * Check the flags. 482c319b4d7SVasiliy Kulikov */ 483c319b4d7SVasiliy Kulikov 484c319b4d7SVasiliy Kulikov /* Mirror BSD error message compatibility */ 485c319b4d7SVasiliy Kulikov if (msg->msg_flags & MSG_OOB) 486c319b4d7SVasiliy Kulikov return -EOPNOTSUPP; 487c319b4d7SVasiliy Kulikov 488c319b4d7SVasiliy Kulikov /* 489c319b4d7SVasiliy Kulikov * Fetch the ICMP header provided by the userland. 490c319b4d7SVasiliy Kulikov * iovec is modified! 491c319b4d7SVasiliy Kulikov */ 492c319b4d7SVasiliy Kulikov 493c319b4d7SVasiliy Kulikov if (memcpy_fromiovec((u8 *)&user_icmph, msg->msg_iov, 494c319b4d7SVasiliy Kulikov sizeof(struct icmphdr))) 495c319b4d7SVasiliy Kulikov return -EFAULT; 496c319b4d7SVasiliy Kulikov if (!ping_supported(user_icmph.type, user_icmph.code)) 497c319b4d7SVasiliy Kulikov return -EINVAL; 498c319b4d7SVasiliy Kulikov 499c319b4d7SVasiliy Kulikov /* 500c319b4d7SVasiliy Kulikov * Get and verify the address. 501c319b4d7SVasiliy Kulikov */ 502c319b4d7SVasiliy Kulikov 503c319b4d7SVasiliy Kulikov if (msg->msg_name) { 504c319b4d7SVasiliy Kulikov struct sockaddr_in *usin = (struct sockaddr_in *)msg->msg_name; 505c319b4d7SVasiliy Kulikov if (msg->msg_namelen < sizeof(*usin)) 506c319b4d7SVasiliy Kulikov return -EINVAL; 507c319b4d7SVasiliy Kulikov if (usin->sin_family != AF_INET) 508c319b4d7SVasiliy Kulikov return -EINVAL; 509c319b4d7SVasiliy Kulikov daddr = usin->sin_addr.s_addr; 510c319b4d7SVasiliy Kulikov /* no remote port */ 511c319b4d7SVasiliy Kulikov } else { 512c319b4d7SVasiliy Kulikov if (sk->sk_state != TCP_ESTABLISHED) 513c319b4d7SVasiliy Kulikov return -EDESTADDRREQ; 514c319b4d7SVasiliy Kulikov daddr = inet->inet_daddr; 515c319b4d7SVasiliy Kulikov /* no remote port */ 516c319b4d7SVasiliy Kulikov } 517c319b4d7SVasiliy Kulikov 518c319b4d7SVasiliy Kulikov ipc.addr = inet->inet_saddr; 519c319b4d7SVasiliy Kulikov ipc.opt = NULL; 520c319b4d7SVasiliy Kulikov ipc.oif = sk->sk_bound_dev_if; 521c319b4d7SVasiliy Kulikov ipc.tx_flags = 0; 522c319b4d7SVasiliy Kulikov err = sock_tx_timestamp(sk, &ipc.tx_flags); 523c319b4d7SVasiliy Kulikov if (err) 524c319b4d7SVasiliy Kulikov return err; 525c319b4d7SVasiliy Kulikov 526c319b4d7SVasiliy Kulikov if (msg->msg_controllen) { 527c319b4d7SVasiliy Kulikov err = ip_cmsg_send(sock_net(sk), msg, &ipc); 528c319b4d7SVasiliy Kulikov if (err) 529c319b4d7SVasiliy Kulikov return err; 530c319b4d7SVasiliy Kulikov if (ipc.opt) 531c319b4d7SVasiliy Kulikov free = 1; 532c319b4d7SVasiliy Kulikov } 533c319b4d7SVasiliy Kulikov if (!ipc.opt) { 534c319b4d7SVasiliy Kulikov struct ip_options_rcu *inet_opt; 535c319b4d7SVasiliy Kulikov 536c319b4d7SVasiliy Kulikov rcu_read_lock(); 537c319b4d7SVasiliy Kulikov inet_opt = rcu_dereference(inet->inet_opt); 538c319b4d7SVasiliy Kulikov if (inet_opt) { 539c319b4d7SVasiliy Kulikov memcpy(&opt_copy, inet_opt, 540c319b4d7SVasiliy Kulikov sizeof(*inet_opt) + inet_opt->opt.optlen); 541c319b4d7SVasiliy Kulikov ipc.opt = &opt_copy.opt; 542c319b4d7SVasiliy Kulikov } 543c319b4d7SVasiliy Kulikov rcu_read_unlock(); 544c319b4d7SVasiliy Kulikov } 545c319b4d7SVasiliy Kulikov 546c319b4d7SVasiliy Kulikov saddr = ipc.addr; 547c319b4d7SVasiliy Kulikov ipc.addr = faddr = daddr; 548c319b4d7SVasiliy Kulikov 549c319b4d7SVasiliy Kulikov if (ipc.opt && ipc.opt->opt.srr) { 550c319b4d7SVasiliy Kulikov if (!daddr) 551c319b4d7SVasiliy Kulikov return -EINVAL; 552c319b4d7SVasiliy Kulikov faddr = ipc.opt->opt.faddr; 553c319b4d7SVasiliy Kulikov } 554c319b4d7SVasiliy Kulikov tos = RT_TOS(inet->tos); 555c319b4d7SVasiliy Kulikov if (sock_flag(sk, SOCK_LOCALROUTE) || 556c319b4d7SVasiliy Kulikov (msg->msg_flags & MSG_DONTROUTE) || 557c319b4d7SVasiliy Kulikov (ipc.opt && ipc.opt->opt.is_strictroute)) { 558c319b4d7SVasiliy Kulikov tos |= RTO_ONLINK; 559c319b4d7SVasiliy Kulikov } 560c319b4d7SVasiliy Kulikov 561c319b4d7SVasiliy Kulikov if (ipv4_is_multicast(daddr)) { 562c319b4d7SVasiliy Kulikov if (!ipc.oif) 563c319b4d7SVasiliy Kulikov ipc.oif = inet->mc_index; 564c319b4d7SVasiliy Kulikov if (!saddr) 565c319b4d7SVasiliy Kulikov saddr = inet->mc_addr; 56676e21053SErich E. Hoover } else if (!ipc.oif) 56776e21053SErich E. Hoover ipc.oif = inet->uc_index; 568c319b4d7SVasiliy Kulikov 569c319b4d7SVasiliy Kulikov flowi4_init_output(&fl4, ipc.oif, sk->sk_mark, tos, 570c319b4d7SVasiliy Kulikov RT_SCOPE_UNIVERSE, sk->sk_protocol, 571c319b4d7SVasiliy Kulikov inet_sk_flowi_flags(sk), faddr, saddr, 0, 0); 572c319b4d7SVasiliy Kulikov 573c319b4d7SVasiliy Kulikov security_sk_classify_flow(sk, flowi4_to_flowi(&fl4)); 574c319b4d7SVasiliy Kulikov rt = ip_route_output_flow(net, &fl4, sk); 575c319b4d7SVasiliy Kulikov if (IS_ERR(rt)) { 576c319b4d7SVasiliy Kulikov err = PTR_ERR(rt); 577c319b4d7SVasiliy Kulikov rt = NULL; 578c319b4d7SVasiliy Kulikov if (err == -ENETUNREACH) 579c319b4d7SVasiliy Kulikov IP_INC_STATS_BH(net, IPSTATS_MIB_OUTNOROUTES); 580c319b4d7SVasiliy Kulikov goto out; 581c319b4d7SVasiliy Kulikov } 582c319b4d7SVasiliy Kulikov 583c319b4d7SVasiliy Kulikov err = -EACCES; 584c319b4d7SVasiliy Kulikov if ((rt->rt_flags & RTCF_BROADCAST) && 585c319b4d7SVasiliy Kulikov !sock_flag(sk, SOCK_BROADCAST)) 586c319b4d7SVasiliy Kulikov goto out; 587c319b4d7SVasiliy Kulikov 588c319b4d7SVasiliy Kulikov if (msg->msg_flags & MSG_CONFIRM) 589c319b4d7SVasiliy Kulikov goto do_confirm; 590c319b4d7SVasiliy Kulikov back_from_confirm: 591c319b4d7SVasiliy Kulikov 592c319b4d7SVasiliy Kulikov if (!ipc.addr) 593c319b4d7SVasiliy Kulikov ipc.addr = fl4.daddr; 594c319b4d7SVasiliy Kulikov 595c319b4d7SVasiliy Kulikov lock_sock(sk); 596c319b4d7SVasiliy Kulikov 597c319b4d7SVasiliy Kulikov pfh.icmph.type = user_icmph.type; /* already checked */ 598c319b4d7SVasiliy Kulikov pfh.icmph.code = user_icmph.code; /* ditto */ 599c319b4d7SVasiliy Kulikov pfh.icmph.checksum = 0; 600c319b4d7SVasiliy Kulikov pfh.icmph.un.echo.id = inet->inet_sport; 601c319b4d7SVasiliy Kulikov pfh.icmph.un.echo.sequence = user_icmph.un.echo.sequence; 602c319b4d7SVasiliy Kulikov pfh.iov = msg->msg_iov; 603c319b4d7SVasiliy Kulikov pfh.wcheck = 0; 604c319b4d7SVasiliy Kulikov 605c319b4d7SVasiliy Kulikov err = ip_append_data(sk, &fl4, ping_getfrag, &pfh, len, 606c319b4d7SVasiliy Kulikov 0, &ipc, &rt, msg->msg_flags); 607c319b4d7SVasiliy Kulikov if (err) 608c319b4d7SVasiliy Kulikov ip_flush_pending_frames(sk); 609c319b4d7SVasiliy Kulikov else 610c319b4d7SVasiliy Kulikov err = ping_push_pending_frames(sk, &pfh, &fl4); 611c319b4d7SVasiliy Kulikov release_sock(sk); 612c319b4d7SVasiliy Kulikov 613c319b4d7SVasiliy Kulikov out: 614c319b4d7SVasiliy Kulikov ip_rt_put(rt); 615c319b4d7SVasiliy Kulikov if (free) 616c319b4d7SVasiliy Kulikov kfree(ipc.opt); 617c319b4d7SVasiliy Kulikov if (!err) { 618c319b4d7SVasiliy Kulikov icmp_out_count(sock_net(sk), user_icmph.type); 619c319b4d7SVasiliy Kulikov return len; 620c319b4d7SVasiliy Kulikov } 621c319b4d7SVasiliy Kulikov return err; 622c319b4d7SVasiliy Kulikov 623c319b4d7SVasiliy Kulikov do_confirm: 624c319b4d7SVasiliy Kulikov dst_confirm(&rt->dst); 625c319b4d7SVasiliy Kulikov if (!(msg->msg_flags & MSG_PROBE) || len) 626c319b4d7SVasiliy Kulikov goto back_from_confirm; 627c319b4d7SVasiliy Kulikov err = 0; 628c319b4d7SVasiliy Kulikov goto out; 629c319b4d7SVasiliy Kulikov } 630c319b4d7SVasiliy Kulikov 631bb0cd2fbSChangli Gao static int ping_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, 632c319b4d7SVasiliy Kulikov size_t len, int noblock, int flags, int *addr_len) 633c319b4d7SVasiliy Kulikov { 634c319b4d7SVasiliy Kulikov struct inet_sock *isk = inet_sk(sk); 635c319b4d7SVasiliy Kulikov struct sockaddr_in *sin = (struct sockaddr_in *)msg->msg_name; 636c319b4d7SVasiliy Kulikov struct sk_buff *skb; 637c319b4d7SVasiliy Kulikov int copied, err; 638c319b4d7SVasiliy Kulikov 639c319b4d7SVasiliy Kulikov pr_debug("ping_recvmsg(sk=%p,sk->num=%u)\n", isk, isk->inet_num); 640c319b4d7SVasiliy Kulikov 641a5e7424dSDavid S. Miller err = -EOPNOTSUPP; 642c319b4d7SVasiliy Kulikov if (flags & MSG_OOB) 643c319b4d7SVasiliy Kulikov goto out; 644c319b4d7SVasiliy Kulikov 645c319b4d7SVasiliy Kulikov if (addr_len) 646c319b4d7SVasiliy Kulikov *addr_len = sizeof(*sin); 647c319b4d7SVasiliy Kulikov 648c319b4d7SVasiliy Kulikov if (flags & MSG_ERRQUEUE) 649c319b4d7SVasiliy Kulikov return ip_recv_error(sk, msg, len); 650c319b4d7SVasiliy Kulikov 651c319b4d7SVasiliy Kulikov skb = skb_recv_datagram(sk, flags, noblock, &err); 652c319b4d7SVasiliy Kulikov if (!skb) 653c319b4d7SVasiliy Kulikov goto out; 654c319b4d7SVasiliy Kulikov 655c319b4d7SVasiliy Kulikov copied = skb->len; 656c319b4d7SVasiliy Kulikov if (copied > len) { 657c319b4d7SVasiliy Kulikov msg->msg_flags |= MSG_TRUNC; 658c319b4d7SVasiliy Kulikov copied = len; 659c319b4d7SVasiliy Kulikov } 660c319b4d7SVasiliy Kulikov 661c319b4d7SVasiliy Kulikov /* Don't bother checking the checksum */ 662c319b4d7SVasiliy Kulikov err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); 663c319b4d7SVasiliy Kulikov if (err) 664c319b4d7SVasiliy Kulikov goto done; 665c319b4d7SVasiliy Kulikov 666c319b4d7SVasiliy Kulikov sock_recv_timestamp(msg, sk, skb); 667c319b4d7SVasiliy Kulikov 668c319b4d7SVasiliy Kulikov /* Copy the address. */ 669c319b4d7SVasiliy Kulikov if (sin) { 670c319b4d7SVasiliy Kulikov sin->sin_family = AF_INET; 671c319b4d7SVasiliy Kulikov sin->sin_port = 0 /* skb->h.uh->source */; 672c319b4d7SVasiliy Kulikov sin->sin_addr.s_addr = ip_hdr(skb)->saddr; 673c319b4d7SVasiliy Kulikov memset(sin->sin_zero, 0, sizeof(sin->sin_zero)); 674c319b4d7SVasiliy Kulikov } 675c319b4d7SVasiliy Kulikov if (isk->cmsg_flags) 676c319b4d7SVasiliy Kulikov ip_cmsg_recv(msg, skb); 677c319b4d7SVasiliy Kulikov err = copied; 678c319b4d7SVasiliy Kulikov 679c319b4d7SVasiliy Kulikov done: 680c319b4d7SVasiliy Kulikov skb_free_datagram(sk, skb); 681c319b4d7SVasiliy Kulikov out: 682c319b4d7SVasiliy Kulikov pr_debug("ping_recvmsg -> %d\n", err); 683c319b4d7SVasiliy Kulikov return err; 684c319b4d7SVasiliy Kulikov } 685c319b4d7SVasiliy Kulikov 686c319b4d7SVasiliy Kulikov static int ping_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) 687c319b4d7SVasiliy Kulikov { 688c319b4d7SVasiliy Kulikov pr_debug("ping_queue_rcv_skb(sk=%p,sk->num=%d,skb=%p)\n", 689c319b4d7SVasiliy Kulikov inet_sk(sk), inet_sk(sk)->inet_num, skb); 690c319b4d7SVasiliy Kulikov if (sock_queue_rcv_skb(sk, skb) < 0) { 691c319b4d7SVasiliy Kulikov kfree_skb(skb); 692c319b4d7SVasiliy Kulikov pr_debug("ping_queue_rcv_skb -> failed\n"); 693c319b4d7SVasiliy Kulikov return -1; 694c319b4d7SVasiliy Kulikov } 695c319b4d7SVasiliy Kulikov return 0; 696c319b4d7SVasiliy Kulikov } 697c319b4d7SVasiliy Kulikov 698c319b4d7SVasiliy Kulikov 699c319b4d7SVasiliy Kulikov /* 700c319b4d7SVasiliy Kulikov * All we need to do is get the socket. 701c319b4d7SVasiliy Kulikov */ 702c319b4d7SVasiliy Kulikov 703c319b4d7SVasiliy Kulikov void ping_rcv(struct sk_buff *skb) 704c319b4d7SVasiliy Kulikov { 705c319b4d7SVasiliy Kulikov struct sock *sk; 706c319b4d7SVasiliy Kulikov struct net *net = dev_net(skb->dev); 707c319b4d7SVasiliy Kulikov struct iphdr *iph = ip_hdr(skb); 708c319b4d7SVasiliy Kulikov struct icmphdr *icmph = icmp_hdr(skb); 709747465efSEric Dumazet __be32 saddr = iph->saddr; 710747465efSEric Dumazet __be32 daddr = iph->daddr; 711c319b4d7SVasiliy Kulikov 712c319b4d7SVasiliy Kulikov /* We assume the packet has already been checked by icmp_rcv */ 713c319b4d7SVasiliy Kulikov 714c319b4d7SVasiliy Kulikov pr_debug("ping_rcv(skb=%p,id=%04x,seq=%04x)\n", 715c319b4d7SVasiliy Kulikov skb, ntohs(icmph->un.echo.id), ntohs(icmph->un.echo.sequence)); 716c319b4d7SVasiliy Kulikov 717c319b4d7SVasiliy Kulikov /* Push ICMP header back */ 718c319b4d7SVasiliy Kulikov skb_push(skb, skb->data - (u8 *)icmph); 719c319b4d7SVasiliy Kulikov 720c319b4d7SVasiliy Kulikov sk = ping_v4_lookup(net, saddr, daddr, ntohs(icmph->un.echo.id), 721c319b4d7SVasiliy Kulikov skb->dev->ifindex); 722c319b4d7SVasiliy Kulikov if (sk != NULL) { 723c319b4d7SVasiliy Kulikov pr_debug("rcv on socket %p\n", sk); 724c319b4d7SVasiliy Kulikov ping_queue_rcv_skb(sk, skb_get(skb)); 725c319b4d7SVasiliy Kulikov sock_put(sk); 726c319b4d7SVasiliy Kulikov return; 727c319b4d7SVasiliy Kulikov } 728c319b4d7SVasiliy Kulikov pr_debug("no socket, dropping\n"); 729c319b4d7SVasiliy Kulikov 730c319b4d7SVasiliy Kulikov /* We're called from icmp_rcv(). kfree_skb() is done there. */ 731c319b4d7SVasiliy Kulikov } 732c319b4d7SVasiliy Kulikov 733c319b4d7SVasiliy Kulikov struct proto ping_prot = { 734c319b4d7SVasiliy Kulikov .name = "PING", 735c319b4d7SVasiliy Kulikov .owner = THIS_MODULE, 736c319b4d7SVasiliy Kulikov .init = ping_init_sock, 737c319b4d7SVasiliy Kulikov .close = ping_close, 738c319b4d7SVasiliy Kulikov .connect = ip4_datagram_connect, 739c319b4d7SVasiliy Kulikov .disconnect = udp_disconnect, 740c319b4d7SVasiliy Kulikov .setsockopt = ip_setsockopt, 741c319b4d7SVasiliy Kulikov .getsockopt = ip_getsockopt, 742c319b4d7SVasiliy Kulikov .sendmsg = ping_sendmsg, 743c319b4d7SVasiliy Kulikov .recvmsg = ping_recvmsg, 744c319b4d7SVasiliy Kulikov .bind = ping_bind, 745c319b4d7SVasiliy Kulikov .backlog_rcv = ping_queue_rcv_skb, 746c319b4d7SVasiliy Kulikov .hash = ping_v4_hash, 747c319b4d7SVasiliy Kulikov .unhash = ping_v4_unhash, 748c319b4d7SVasiliy Kulikov .get_port = ping_v4_get_port, 749c319b4d7SVasiliy Kulikov .obj_size = sizeof(struct inet_sock), 750c319b4d7SVasiliy Kulikov }; 751c319b4d7SVasiliy Kulikov EXPORT_SYMBOL(ping_prot); 752c319b4d7SVasiliy Kulikov 753c319b4d7SVasiliy Kulikov #ifdef CONFIG_PROC_FS 754c319b4d7SVasiliy Kulikov 755c319b4d7SVasiliy Kulikov static struct sock *ping_get_first(struct seq_file *seq, int start) 756c319b4d7SVasiliy Kulikov { 757c319b4d7SVasiliy Kulikov struct sock *sk; 758c319b4d7SVasiliy Kulikov struct ping_iter_state *state = seq->private; 759c319b4d7SVasiliy Kulikov struct net *net = seq_file_net(seq); 760c319b4d7SVasiliy Kulikov 761c319b4d7SVasiliy Kulikov for (state->bucket = start; state->bucket < PING_HTABLE_SIZE; 762c319b4d7SVasiliy Kulikov ++state->bucket) { 763c319b4d7SVasiliy Kulikov struct hlist_nulls_node *node; 76475e308c8SChangli Gao struct hlist_nulls_head *hslot; 76575e308c8SChangli Gao 76675e308c8SChangli Gao hslot = &ping_table.hash[state->bucket]; 767c319b4d7SVasiliy Kulikov 768c319b4d7SVasiliy Kulikov if (hlist_nulls_empty(hslot)) 769c319b4d7SVasiliy Kulikov continue; 770c319b4d7SVasiliy Kulikov 771c319b4d7SVasiliy Kulikov sk_nulls_for_each(sk, node, hslot) { 772c319b4d7SVasiliy Kulikov if (net_eq(sock_net(sk), net)) 773c319b4d7SVasiliy Kulikov goto found; 774c319b4d7SVasiliy Kulikov } 775c319b4d7SVasiliy Kulikov } 776c319b4d7SVasiliy Kulikov sk = NULL; 777c319b4d7SVasiliy Kulikov found: 778c319b4d7SVasiliy Kulikov return sk; 779c319b4d7SVasiliy Kulikov } 780c319b4d7SVasiliy Kulikov 781c319b4d7SVasiliy Kulikov static struct sock *ping_get_next(struct seq_file *seq, struct sock *sk) 782c319b4d7SVasiliy Kulikov { 783c319b4d7SVasiliy Kulikov struct ping_iter_state *state = seq->private; 784c319b4d7SVasiliy Kulikov struct net *net = seq_file_net(seq); 785c319b4d7SVasiliy Kulikov 786c319b4d7SVasiliy Kulikov do { 787c319b4d7SVasiliy Kulikov sk = sk_nulls_next(sk); 788c319b4d7SVasiliy Kulikov } while (sk && (!net_eq(sock_net(sk), net))); 789c319b4d7SVasiliy Kulikov 790c319b4d7SVasiliy Kulikov if (!sk) 791c319b4d7SVasiliy Kulikov return ping_get_first(seq, state->bucket + 1); 792c319b4d7SVasiliy Kulikov return sk; 793c319b4d7SVasiliy Kulikov } 794c319b4d7SVasiliy Kulikov 795c319b4d7SVasiliy Kulikov static struct sock *ping_get_idx(struct seq_file *seq, loff_t pos) 796c319b4d7SVasiliy Kulikov { 797c319b4d7SVasiliy Kulikov struct sock *sk = ping_get_first(seq, 0); 798c319b4d7SVasiliy Kulikov 799c319b4d7SVasiliy Kulikov if (sk) 800c319b4d7SVasiliy Kulikov while (pos && (sk = ping_get_next(seq, sk)) != NULL) 801c319b4d7SVasiliy Kulikov --pos; 802c319b4d7SVasiliy Kulikov return pos ? NULL : sk; 803c319b4d7SVasiliy Kulikov } 804c319b4d7SVasiliy Kulikov 805c319b4d7SVasiliy Kulikov static void *ping_seq_start(struct seq_file *seq, loff_t *pos) 806c319b4d7SVasiliy Kulikov { 807c319b4d7SVasiliy Kulikov struct ping_iter_state *state = seq->private; 808c319b4d7SVasiliy Kulikov state->bucket = 0; 809c319b4d7SVasiliy Kulikov 810c319b4d7SVasiliy Kulikov read_lock_bh(&ping_table.lock); 811c319b4d7SVasiliy Kulikov 812c319b4d7SVasiliy Kulikov return *pos ? ping_get_idx(seq, *pos-1) : SEQ_START_TOKEN; 813c319b4d7SVasiliy Kulikov } 814c319b4d7SVasiliy Kulikov 815c319b4d7SVasiliy Kulikov static void *ping_seq_next(struct seq_file *seq, void *v, loff_t *pos) 816c319b4d7SVasiliy Kulikov { 817c319b4d7SVasiliy Kulikov struct sock *sk; 818c319b4d7SVasiliy Kulikov 819c319b4d7SVasiliy Kulikov if (v == SEQ_START_TOKEN) 820c319b4d7SVasiliy Kulikov sk = ping_get_idx(seq, 0); 821c319b4d7SVasiliy Kulikov else 822c319b4d7SVasiliy Kulikov sk = ping_get_next(seq, v); 823c319b4d7SVasiliy Kulikov 824c319b4d7SVasiliy Kulikov ++*pos; 825c319b4d7SVasiliy Kulikov return sk; 826c319b4d7SVasiliy Kulikov } 827c319b4d7SVasiliy Kulikov 828c319b4d7SVasiliy Kulikov static void ping_seq_stop(struct seq_file *seq, void *v) 829c319b4d7SVasiliy Kulikov { 830c319b4d7SVasiliy Kulikov read_unlock_bh(&ping_table.lock); 831c319b4d7SVasiliy Kulikov } 832c319b4d7SVasiliy Kulikov 833c319b4d7SVasiliy Kulikov static void ping_format_sock(struct sock *sp, struct seq_file *f, 834c319b4d7SVasiliy Kulikov int bucket, int *len) 835c319b4d7SVasiliy Kulikov { 836c319b4d7SVasiliy Kulikov struct inet_sock *inet = inet_sk(sp); 837c319b4d7SVasiliy Kulikov __be32 dest = inet->inet_daddr; 838c319b4d7SVasiliy Kulikov __be32 src = inet->inet_rcv_saddr; 839c319b4d7SVasiliy Kulikov __u16 destp = ntohs(inet->inet_dport); 840c319b4d7SVasiliy Kulikov __u16 srcp = ntohs(inet->inet_sport); 841c319b4d7SVasiliy Kulikov 842c319b4d7SVasiliy Kulikov seq_printf(f, "%5d: %08X:%04X %08X:%04X" 843c319b4d7SVasiliy Kulikov " %02X %08X:%08X %02X:%08lX %08X %5d %8d %lu %d %pK %d%n", 844c319b4d7SVasiliy Kulikov bucket, src, srcp, dest, destp, sp->sk_state, 845c319b4d7SVasiliy Kulikov sk_wmem_alloc_get(sp), 846c319b4d7SVasiliy Kulikov sk_rmem_alloc_get(sp), 847c319b4d7SVasiliy Kulikov 0, 0L, 0, sock_i_uid(sp), 0, sock_i_ino(sp), 848c319b4d7SVasiliy Kulikov atomic_read(&sp->sk_refcnt), sp, 849c319b4d7SVasiliy Kulikov atomic_read(&sp->sk_drops), len); 850c319b4d7SVasiliy Kulikov } 851c319b4d7SVasiliy Kulikov 852c319b4d7SVasiliy Kulikov static int ping_seq_show(struct seq_file *seq, void *v) 853c319b4d7SVasiliy Kulikov { 854c319b4d7SVasiliy Kulikov if (v == SEQ_START_TOKEN) 855c319b4d7SVasiliy Kulikov seq_printf(seq, "%-127s\n", 856c319b4d7SVasiliy Kulikov " sl local_address rem_address st tx_queue " 857c319b4d7SVasiliy Kulikov "rx_queue tr tm->when retrnsmt uid timeout " 858c319b4d7SVasiliy Kulikov "inode ref pointer drops"); 859c319b4d7SVasiliy Kulikov else { 860c319b4d7SVasiliy Kulikov struct ping_iter_state *state = seq->private; 861c319b4d7SVasiliy Kulikov int len; 862c319b4d7SVasiliy Kulikov 863c319b4d7SVasiliy Kulikov ping_format_sock(v, seq, state->bucket, &len); 864c319b4d7SVasiliy Kulikov seq_printf(seq, "%*s\n", 127 - len, ""); 865c319b4d7SVasiliy Kulikov } 866c319b4d7SVasiliy Kulikov return 0; 867c319b4d7SVasiliy Kulikov } 868c319b4d7SVasiliy Kulikov 869c319b4d7SVasiliy Kulikov static const struct seq_operations ping_seq_ops = { 870c319b4d7SVasiliy Kulikov .show = ping_seq_show, 871c319b4d7SVasiliy Kulikov .start = ping_seq_start, 872c319b4d7SVasiliy Kulikov .next = ping_seq_next, 873c319b4d7SVasiliy Kulikov .stop = ping_seq_stop, 874c319b4d7SVasiliy Kulikov }; 875c319b4d7SVasiliy Kulikov 876c319b4d7SVasiliy Kulikov static int ping_seq_open(struct inode *inode, struct file *file) 877c319b4d7SVasiliy Kulikov { 878c319b4d7SVasiliy Kulikov return seq_open_net(inode, file, &ping_seq_ops, 879c319b4d7SVasiliy Kulikov sizeof(struct ping_iter_state)); 880c319b4d7SVasiliy Kulikov } 881c319b4d7SVasiliy Kulikov 882c319b4d7SVasiliy Kulikov static const struct file_operations ping_seq_fops = { 883c319b4d7SVasiliy Kulikov .open = ping_seq_open, 884c319b4d7SVasiliy Kulikov .read = seq_read, 885c319b4d7SVasiliy Kulikov .llseek = seq_lseek, 886c319b4d7SVasiliy Kulikov .release = seq_release_net, 887c319b4d7SVasiliy Kulikov }; 888c319b4d7SVasiliy Kulikov 889c319b4d7SVasiliy Kulikov static int ping_proc_register(struct net *net) 890c319b4d7SVasiliy Kulikov { 891c319b4d7SVasiliy Kulikov struct proc_dir_entry *p; 892c319b4d7SVasiliy Kulikov int rc = 0; 893c319b4d7SVasiliy Kulikov 894c319b4d7SVasiliy Kulikov p = proc_net_fops_create(net, "icmp", S_IRUGO, &ping_seq_fops); 895c319b4d7SVasiliy Kulikov if (!p) 896c319b4d7SVasiliy Kulikov rc = -ENOMEM; 897c319b4d7SVasiliy Kulikov return rc; 898c319b4d7SVasiliy Kulikov } 899c319b4d7SVasiliy Kulikov 900c319b4d7SVasiliy Kulikov static void ping_proc_unregister(struct net *net) 901c319b4d7SVasiliy Kulikov { 902c319b4d7SVasiliy Kulikov proc_net_remove(net, "icmp"); 903c319b4d7SVasiliy Kulikov } 904c319b4d7SVasiliy Kulikov 905c319b4d7SVasiliy Kulikov 906c319b4d7SVasiliy Kulikov static int __net_init ping_proc_init_net(struct net *net) 907c319b4d7SVasiliy Kulikov { 908c319b4d7SVasiliy Kulikov return ping_proc_register(net); 909c319b4d7SVasiliy Kulikov } 910c319b4d7SVasiliy Kulikov 911c319b4d7SVasiliy Kulikov static void __net_exit ping_proc_exit_net(struct net *net) 912c319b4d7SVasiliy Kulikov { 913c319b4d7SVasiliy Kulikov ping_proc_unregister(net); 914c319b4d7SVasiliy Kulikov } 915c319b4d7SVasiliy Kulikov 916c319b4d7SVasiliy Kulikov static struct pernet_operations ping_net_ops = { 917c319b4d7SVasiliy Kulikov .init = ping_proc_init_net, 918c319b4d7SVasiliy Kulikov .exit = ping_proc_exit_net, 919c319b4d7SVasiliy Kulikov }; 920c319b4d7SVasiliy Kulikov 921c319b4d7SVasiliy Kulikov int __init ping_proc_init(void) 922c319b4d7SVasiliy Kulikov { 923c319b4d7SVasiliy Kulikov return register_pernet_subsys(&ping_net_ops); 924c319b4d7SVasiliy Kulikov } 925c319b4d7SVasiliy Kulikov 926c319b4d7SVasiliy Kulikov void ping_proc_exit(void) 927c319b4d7SVasiliy Kulikov { 928c319b4d7SVasiliy Kulikov unregister_pernet_subsys(&ping_net_ops); 929c319b4d7SVasiliy Kulikov } 930c319b4d7SVasiliy Kulikov 931c319b4d7SVasiliy Kulikov #endif 932c319b4d7SVasiliy Kulikov 933c319b4d7SVasiliy Kulikov void __init ping_init(void) 934c319b4d7SVasiliy Kulikov { 935c319b4d7SVasiliy Kulikov int i; 936c319b4d7SVasiliy Kulikov 937c319b4d7SVasiliy Kulikov for (i = 0; i < PING_HTABLE_SIZE; i++) 938c319b4d7SVasiliy Kulikov INIT_HLIST_NULLS_HEAD(&ping_table.hash[i], i); 939c319b4d7SVasiliy Kulikov rwlock_init(&ping_table.lock); 940c319b4d7SVasiliy Kulikov } 941