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/icmp.h> 37c319b4d7SVasiliy Kulikov #include <net/protocol.h> 38c319b4d7SVasiliy Kulikov #include <linux/skbuff.h> 39c319b4d7SVasiliy Kulikov #include <linux/proc_fs.h> 40bc3b2d7fSPaul Gortmaker #include <linux/export.h> 41c319b4d7SVasiliy Kulikov #include <net/sock.h> 42c319b4d7SVasiliy Kulikov #include <net/ping.h> 43c319b4d7SVasiliy Kulikov #include <net/udp.h> 44c319b4d7SVasiliy Kulikov #include <net/route.h> 45c319b4d7SVasiliy Kulikov #include <net/inet_common.h> 46c319b4d7SVasiliy Kulikov #include <net/checksum.h> 47c319b4d7SVasiliy Kulikov 48*6d0bfe22SLorenzo Colitti #if IS_ENABLED(CONFIG_IPV6) 49*6d0bfe22SLorenzo Colitti #include <linux/in6.h> 50*6d0bfe22SLorenzo Colitti #include <linux/icmpv6.h> 51*6d0bfe22SLorenzo Colitti #include <net/addrconf.h> 52*6d0bfe22SLorenzo Colitti #include <net/ipv6.h> 53*6d0bfe22SLorenzo Colitti #include <net/transp_v6.h> 54*6d0bfe22SLorenzo Colitti #endif 55c319b4d7SVasiliy Kulikov 56*6d0bfe22SLorenzo Colitti 57*6d0bfe22SLorenzo Colitti struct ping_table ping_table; 58*6d0bfe22SLorenzo Colitti struct pingv6_ops pingv6_ops; 59*6d0bfe22SLorenzo Colitti EXPORT_SYMBOL_GPL(pingv6_ops); 60c319b4d7SVasiliy Kulikov 611b1cb1f7SEric Dumazet static u16 ping_port_rover; 62c319b4d7SVasiliy Kulikov 6395c96174SEric Dumazet static inline int ping_hashfn(struct net *net, unsigned int num, unsigned int mask) 64c319b4d7SVasiliy Kulikov { 65c319b4d7SVasiliy Kulikov int res = (num + net_hash_mix(net)) & mask; 6695c96174SEric Dumazet 67c319b4d7SVasiliy Kulikov pr_debug("hash(%d) = %d\n", num, res); 68c319b4d7SVasiliy Kulikov return res; 69c319b4d7SVasiliy Kulikov } 70*6d0bfe22SLorenzo Colitti EXPORT_SYMBOL_GPL(ping_hash); 71c319b4d7SVasiliy Kulikov 72c319b4d7SVasiliy Kulikov static inline struct hlist_nulls_head *ping_hashslot(struct ping_table *table, 7395c96174SEric Dumazet struct net *net, unsigned int num) 74c319b4d7SVasiliy Kulikov { 75c319b4d7SVasiliy Kulikov return &table->hash[ping_hashfn(net, num, PING_HTABLE_MASK)]; 76c319b4d7SVasiliy Kulikov } 77c319b4d7SVasiliy Kulikov 78*6d0bfe22SLorenzo Colitti int ping_get_port(struct sock *sk, unsigned short ident) 79c319b4d7SVasiliy Kulikov { 80c319b4d7SVasiliy Kulikov struct hlist_nulls_node *node; 81c319b4d7SVasiliy Kulikov struct hlist_nulls_head *hlist; 82c319b4d7SVasiliy Kulikov struct inet_sock *isk, *isk2; 83c319b4d7SVasiliy Kulikov struct sock *sk2 = NULL; 84c319b4d7SVasiliy Kulikov 85c319b4d7SVasiliy Kulikov isk = inet_sk(sk); 86c319b4d7SVasiliy Kulikov write_lock_bh(&ping_table.lock); 87c319b4d7SVasiliy Kulikov if (ident == 0) { 88c319b4d7SVasiliy Kulikov u32 i; 89c319b4d7SVasiliy Kulikov u16 result = ping_port_rover + 1; 90c319b4d7SVasiliy Kulikov 91c319b4d7SVasiliy Kulikov for (i = 0; i < (1L << 16); i++, result++) { 92c319b4d7SVasiliy Kulikov if (!result) 93c319b4d7SVasiliy Kulikov result++; /* avoid zero */ 94c319b4d7SVasiliy Kulikov hlist = ping_hashslot(&ping_table, sock_net(sk), 95c319b4d7SVasiliy Kulikov result); 96c319b4d7SVasiliy Kulikov ping_portaddr_for_each_entry(sk2, node, hlist) { 97c319b4d7SVasiliy Kulikov isk2 = inet_sk(sk2); 98c319b4d7SVasiliy Kulikov 99c319b4d7SVasiliy Kulikov if (isk2->inet_num == result) 100c319b4d7SVasiliy Kulikov goto next_port; 101c319b4d7SVasiliy Kulikov } 102c319b4d7SVasiliy Kulikov 103c319b4d7SVasiliy Kulikov /* found */ 104c319b4d7SVasiliy Kulikov ping_port_rover = ident = result; 105c319b4d7SVasiliy Kulikov break; 106c319b4d7SVasiliy Kulikov next_port: 107c319b4d7SVasiliy Kulikov ; 108c319b4d7SVasiliy Kulikov } 109c319b4d7SVasiliy Kulikov if (i >= (1L << 16)) 110c319b4d7SVasiliy Kulikov goto fail; 111c319b4d7SVasiliy Kulikov } else { 112c319b4d7SVasiliy Kulikov hlist = ping_hashslot(&ping_table, sock_net(sk), ident); 113c319b4d7SVasiliy Kulikov ping_portaddr_for_each_entry(sk2, node, hlist) { 114c319b4d7SVasiliy Kulikov isk2 = inet_sk(sk2); 115c319b4d7SVasiliy Kulikov 116*6d0bfe22SLorenzo Colitti /* BUG? Why is this reuse and not reuseaddr? ping.c 117*6d0bfe22SLorenzo Colitti * doesn't turn off SO_REUSEADDR, and it doesn't expect 118*6d0bfe22SLorenzo Colitti * that other ping processes can steal its packets. 119*6d0bfe22SLorenzo Colitti */ 120c319b4d7SVasiliy Kulikov if ((isk2->inet_num == ident) && 121c319b4d7SVasiliy Kulikov (sk2 != sk) && 122c319b4d7SVasiliy Kulikov (!sk2->sk_reuse || !sk->sk_reuse)) 123c319b4d7SVasiliy Kulikov goto fail; 124c319b4d7SVasiliy Kulikov } 125c319b4d7SVasiliy Kulikov } 126c319b4d7SVasiliy Kulikov 127c319b4d7SVasiliy Kulikov pr_debug("found port/ident = %d\n", ident); 128c319b4d7SVasiliy Kulikov isk->inet_num = ident; 129c319b4d7SVasiliy Kulikov if (sk_unhashed(sk)) { 130c319b4d7SVasiliy Kulikov pr_debug("was not hashed\n"); 131c319b4d7SVasiliy Kulikov sock_hold(sk); 132c319b4d7SVasiliy Kulikov hlist_nulls_add_head(&sk->sk_nulls_node, hlist); 133c319b4d7SVasiliy Kulikov sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1); 134c319b4d7SVasiliy Kulikov } 135c319b4d7SVasiliy Kulikov write_unlock_bh(&ping_table.lock); 136c319b4d7SVasiliy Kulikov return 0; 137c319b4d7SVasiliy Kulikov 138c319b4d7SVasiliy Kulikov fail: 139c319b4d7SVasiliy Kulikov write_unlock_bh(&ping_table.lock); 140c319b4d7SVasiliy Kulikov return 1; 141c319b4d7SVasiliy Kulikov } 142*6d0bfe22SLorenzo Colitti EXPORT_SYMBOL_GPL(ping_get_port); 143c319b4d7SVasiliy Kulikov 144*6d0bfe22SLorenzo Colitti void ping_hash(struct sock *sk) 145c319b4d7SVasiliy Kulikov { 146*6d0bfe22SLorenzo Colitti pr_debug("ping_hash(sk->port=%u)\n", inet_sk(sk)->inet_num); 147c319b4d7SVasiliy Kulikov BUG(); /* "Please do not press this button again." */ 148c319b4d7SVasiliy Kulikov } 149c319b4d7SVasiliy Kulikov 150*6d0bfe22SLorenzo Colitti void ping_unhash(struct sock *sk) 151c319b4d7SVasiliy Kulikov { 152c319b4d7SVasiliy Kulikov struct inet_sock *isk = inet_sk(sk); 153*6d0bfe22SLorenzo Colitti pr_debug("ping_unhash(isk=%p,isk->num=%u)\n", isk, isk->inet_num); 154c319b4d7SVasiliy Kulikov if (sk_hashed(sk)) { 155c319b4d7SVasiliy Kulikov write_lock_bh(&ping_table.lock); 156c319b4d7SVasiliy Kulikov hlist_nulls_del(&sk->sk_nulls_node); 157c319b4d7SVasiliy Kulikov sock_put(sk); 158747465efSEric Dumazet isk->inet_num = 0; 159747465efSEric Dumazet isk->inet_sport = 0; 160c319b4d7SVasiliy Kulikov sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1); 161c319b4d7SVasiliy Kulikov write_unlock_bh(&ping_table.lock); 162c319b4d7SVasiliy Kulikov } 163c319b4d7SVasiliy Kulikov } 164*6d0bfe22SLorenzo Colitti EXPORT_SYMBOL_GPL(ping_unhash); 165c319b4d7SVasiliy Kulikov 166*6d0bfe22SLorenzo Colitti static struct sock *ping_lookup(struct net *net, struct sk_buff *skb, u16 ident) 167c319b4d7SVasiliy Kulikov { 168c319b4d7SVasiliy Kulikov struct hlist_nulls_head *hslot = ping_hashslot(&ping_table, net, ident); 169c319b4d7SVasiliy Kulikov struct sock *sk = NULL; 170c319b4d7SVasiliy Kulikov struct inet_sock *isk; 171c319b4d7SVasiliy Kulikov struct hlist_nulls_node *hnode; 172*6d0bfe22SLorenzo Colitti int dif = skb->dev->ifindex; 173c319b4d7SVasiliy Kulikov 174*6d0bfe22SLorenzo Colitti if (skb->protocol == htons(ETH_P_IP)) { 175747465efSEric Dumazet pr_debug("try to find: num = %d, daddr = %pI4, dif = %d\n", 176*6d0bfe22SLorenzo Colitti (int)ident, &ip_hdr(skb)->daddr, dif); 177*6d0bfe22SLorenzo Colitti #if IS_ENABLED(CONFIG_IPV6) 178*6d0bfe22SLorenzo Colitti } else if (skb->protocol == htons(ETH_P_IPV6)) { 179*6d0bfe22SLorenzo Colitti pr_debug("try to find: num = %d, daddr = %pI6c, dif = %d\n", 180*6d0bfe22SLorenzo Colitti (int)ident, &ipv6_hdr(skb)->daddr, dif); 181*6d0bfe22SLorenzo Colitti #endif 182*6d0bfe22SLorenzo Colitti } 183*6d0bfe22SLorenzo Colitti 184c319b4d7SVasiliy Kulikov read_lock_bh(&ping_table.lock); 185c319b4d7SVasiliy Kulikov 186c319b4d7SVasiliy Kulikov ping_portaddr_for_each_entry(sk, hnode, hslot) { 187c319b4d7SVasiliy Kulikov isk = inet_sk(sk); 188c319b4d7SVasiliy Kulikov 189*6d0bfe22SLorenzo Colitti pr_debug("iterate\n"); 190*6d0bfe22SLorenzo Colitti if (isk->inet_num != ident) 191*6d0bfe22SLorenzo Colitti continue; 192*6d0bfe22SLorenzo Colitti 193*6d0bfe22SLorenzo Colitti if (skb->protocol == htons(ETH_P_IP) && 194*6d0bfe22SLorenzo Colitti sk->sk_family == AF_INET) { 195747465efSEric Dumazet pr_debug("found: %p: num=%d, daddr=%pI4, dif=%d\n", sk, 196747465efSEric Dumazet (int) isk->inet_num, &isk->inet_rcv_saddr, 197c319b4d7SVasiliy Kulikov sk->sk_bound_dev_if); 198c319b4d7SVasiliy Kulikov 199*6d0bfe22SLorenzo Colitti if (isk->inet_rcv_saddr && 200*6d0bfe22SLorenzo Colitti isk->inet_rcv_saddr != ip_hdr(skb)->daddr) 201c319b4d7SVasiliy Kulikov continue; 202*6d0bfe22SLorenzo Colitti #if IS_ENABLED(CONFIG_IPV6) 203*6d0bfe22SLorenzo Colitti } else if (skb->protocol == htons(ETH_P_IPV6) && 204*6d0bfe22SLorenzo Colitti sk->sk_family == AF_INET6) { 205*6d0bfe22SLorenzo Colitti struct ipv6_pinfo *np = inet6_sk(sk); 206*6d0bfe22SLorenzo Colitti 207*6d0bfe22SLorenzo Colitti pr_debug("found: %p: num=%d, daddr=%pI6c, dif=%d\n", sk, 208*6d0bfe22SLorenzo Colitti (int) isk->inet_num, 209*6d0bfe22SLorenzo Colitti &inet6_sk(sk)->rcv_saddr, 210*6d0bfe22SLorenzo Colitti sk->sk_bound_dev_if); 211*6d0bfe22SLorenzo Colitti 212*6d0bfe22SLorenzo Colitti if (!ipv6_addr_any(&np->rcv_saddr) && 213*6d0bfe22SLorenzo Colitti !ipv6_addr_equal(&np->rcv_saddr, 214*6d0bfe22SLorenzo Colitti &ipv6_hdr(skb)->daddr)) 215c319b4d7SVasiliy Kulikov continue; 216*6d0bfe22SLorenzo Colitti #endif 217*6d0bfe22SLorenzo Colitti } 218*6d0bfe22SLorenzo Colitti 219c319b4d7SVasiliy Kulikov if (sk->sk_bound_dev_if && sk->sk_bound_dev_if != dif) 220c319b4d7SVasiliy Kulikov continue; 221c319b4d7SVasiliy Kulikov 222c319b4d7SVasiliy Kulikov sock_hold(sk); 223c319b4d7SVasiliy Kulikov goto exit; 224c319b4d7SVasiliy Kulikov } 225c319b4d7SVasiliy Kulikov 226c319b4d7SVasiliy Kulikov sk = NULL; 227c319b4d7SVasiliy Kulikov exit: 228c319b4d7SVasiliy Kulikov read_unlock_bh(&ping_table.lock); 229c319b4d7SVasiliy Kulikov 230c319b4d7SVasiliy Kulikov return sk; 231c319b4d7SVasiliy Kulikov } 232c319b4d7SVasiliy Kulikov 2337064d16eSEric W. Biederman static void inet_get_ping_group_range_net(struct net *net, kgid_t *low, 2347064d16eSEric W. Biederman kgid_t *high) 235f56e03e8SVasiliy Kulikov { 2367064d16eSEric W. Biederman kgid_t *data = net->ipv4.sysctl_ping_group_range; 23795c96174SEric Dumazet unsigned int seq; 23895c96174SEric Dumazet 239f56e03e8SVasiliy Kulikov do { 240f56e03e8SVasiliy Kulikov seq = read_seqbegin(&sysctl_local_ports.lock); 241f56e03e8SVasiliy Kulikov 242f56e03e8SVasiliy Kulikov *low = data[0]; 243f56e03e8SVasiliy Kulikov *high = data[1]; 244f56e03e8SVasiliy Kulikov } while (read_seqretry(&sysctl_local_ports.lock, seq)); 245f56e03e8SVasiliy Kulikov } 246f56e03e8SVasiliy Kulikov 247f56e03e8SVasiliy Kulikov 248*6d0bfe22SLorenzo Colitti int ping_init_sock(struct sock *sk) 249c319b4d7SVasiliy Kulikov { 250c319b4d7SVasiliy Kulikov struct net *net = sock_net(sk); 2517064d16eSEric W. Biederman kgid_t group = current_egid(); 252c319b4d7SVasiliy Kulikov struct group_info *group_info = get_current_groups(); 253c319b4d7SVasiliy Kulikov int i, j, count = group_info->ngroups; 254ae2975bcSEric W. Biederman kgid_t low, high; 255c319b4d7SVasiliy Kulikov 2567064d16eSEric W. Biederman inet_get_ping_group_range_net(net, &low, &high); 2577064d16eSEric W. Biederman if (gid_lte(low, group) && gid_lte(group, high)) 258c319b4d7SVasiliy Kulikov return 0; 259c319b4d7SVasiliy Kulikov 260c319b4d7SVasiliy Kulikov for (i = 0; i < group_info->nblocks; i++) { 261c319b4d7SVasiliy Kulikov int cp_count = min_t(int, NGROUPS_PER_BLOCK, count); 262c319b4d7SVasiliy Kulikov for (j = 0; j < cp_count; j++) { 263ae2975bcSEric W. Biederman kgid_t gid = group_info->blocks[i][j]; 264ae2975bcSEric W. Biederman if (gid_lte(low, gid) && gid_lte(gid, high)) 265c319b4d7SVasiliy Kulikov return 0; 266c319b4d7SVasiliy Kulikov } 267c319b4d7SVasiliy Kulikov 268c319b4d7SVasiliy Kulikov count -= cp_count; 269c319b4d7SVasiliy Kulikov } 270c319b4d7SVasiliy Kulikov 271c319b4d7SVasiliy Kulikov return -EACCES; 272c319b4d7SVasiliy Kulikov } 273*6d0bfe22SLorenzo Colitti EXPORT_SYMBOL_GPL(ping_init_sock); 274c319b4d7SVasiliy Kulikov 275*6d0bfe22SLorenzo Colitti void ping_close(struct sock *sk, long timeout) 276c319b4d7SVasiliy Kulikov { 277c319b4d7SVasiliy Kulikov pr_debug("ping_close(sk=%p,sk->num=%u)\n", 278c319b4d7SVasiliy Kulikov inet_sk(sk), inet_sk(sk)->inet_num); 279c319b4d7SVasiliy Kulikov pr_debug("isk->refcnt = %d\n", sk->sk_refcnt.counter); 280c319b4d7SVasiliy Kulikov 281c319b4d7SVasiliy Kulikov sk_common_release(sk); 282c319b4d7SVasiliy Kulikov } 283*6d0bfe22SLorenzo Colitti EXPORT_SYMBOL_GPL(ping_close); 284c319b4d7SVasiliy Kulikov 285*6d0bfe22SLorenzo Colitti /* Checks the bind address and possibly modifies sk->sk_bound_dev_if. */ 286*6d0bfe22SLorenzo Colitti int ping_check_bind_addr(struct sock *sk, struct inet_sock *isk, 287*6d0bfe22SLorenzo Colitti struct sockaddr *uaddr, int addr_len) { 288*6d0bfe22SLorenzo Colitti struct net *net = sock_net(sk); 289*6d0bfe22SLorenzo Colitti if (sk->sk_family == AF_INET) { 290c319b4d7SVasiliy Kulikov struct sockaddr_in *addr = (struct sockaddr_in *) uaddr; 291c319b4d7SVasiliy Kulikov int chk_addr_ret; 292c319b4d7SVasiliy Kulikov 293*6d0bfe22SLorenzo Colitti if (addr_len < sizeof(*addr)) 294c319b4d7SVasiliy Kulikov return -EINVAL; 295c319b4d7SVasiliy Kulikov 296*6d0bfe22SLorenzo Colitti pr_debug("ping_check_bind_addr(sk=%p,addr=%pI4,port=%d)\n", 297*6d0bfe22SLorenzo Colitti sk, &addr->sin_addr.s_addr, ntohs(addr->sin_port)); 298c319b4d7SVasiliy Kulikov 299*6d0bfe22SLorenzo Colitti chk_addr_ret = inet_addr_type(net, addr->sin_addr.s_addr); 300*6d0bfe22SLorenzo Colitti 301747465efSEric Dumazet if (addr->sin_addr.s_addr == htonl(INADDR_ANY)) 302c319b4d7SVasiliy Kulikov chk_addr_ret = RTN_LOCAL; 303c319b4d7SVasiliy Kulikov 304c319b4d7SVasiliy Kulikov if ((sysctl_ip_nonlocal_bind == 0 && 305c319b4d7SVasiliy Kulikov isk->freebind == 0 && isk->transparent == 0 && 306c319b4d7SVasiliy Kulikov chk_addr_ret != RTN_LOCAL) || 307c319b4d7SVasiliy Kulikov chk_addr_ret == RTN_MULTICAST || 308c319b4d7SVasiliy Kulikov chk_addr_ret == RTN_BROADCAST) 309c319b4d7SVasiliy Kulikov return -EADDRNOTAVAIL; 310c319b4d7SVasiliy Kulikov 311*6d0bfe22SLorenzo Colitti #if IS_ENABLED(CONFIG_IPV6) 312*6d0bfe22SLorenzo Colitti } else if (sk->sk_family == AF_INET6) { 313*6d0bfe22SLorenzo Colitti struct sockaddr_in6 *addr = (struct sockaddr_in6 *) uaddr; 314*6d0bfe22SLorenzo Colitti int addr_type, scoped, has_addr; 315*6d0bfe22SLorenzo Colitti struct net_device *dev = NULL; 316*6d0bfe22SLorenzo Colitti 317*6d0bfe22SLorenzo Colitti if (addr_len < sizeof(*addr)) 318*6d0bfe22SLorenzo Colitti return -EINVAL; 319*6d0bfe22SLorenzo Colitti 320*6d0bfe22SLorenzo Colitti pr_debug("ping_check_bind_addr(sk=%p,addr=%pI6c,port=%d)\n", 321*6d0bfe22SLorenzo Colitti sk, addr->sin6_addr.s6_addr, ntohs(addr->sin6_port)); 322*6d0bfe22SLorenzo Colitti 323*6d0bfe22SLorenzo Colitti addr_type = ipv6_addr_type(&addr->sin6_addr); 324*6d0bfe22SLorenzo Colitti scoped = __ipv6_addr_needs_scope_id(addr_type); 325*6d0bfe22SLorenzo Colitti if ((addr_type != IPV6_ADDR_ANY && 326*6d0bfe22SLorenzo Colitti !(addr_type & IPV6_ADDR_UNICAST)) || 327*6d0bfe22SLorenzo Colitti (scoped && !addr->sin6_scope_id)) 328*6d0bfe22SLorenzo Colitti return -EINVAL; 329*6d0bfe22SLorenzo Colitti 330*6d0bfe22SLorenzo Colitti rcu_read_lock(); 331*6d0bfe22SLorenzo Colitti if (addr->sin6_scope_id) { 332*6d0bfe22SLorenzo Colitti dev = dev_get_by_index_rcu(net, addr->sin6_scope_id); 333*6d0bfe22SLorenzo Colitti if (!dev) { 334*6d0bfe22SLorenzo Colitti rcu_read_unlock(); 335*6d0bfe22SLorenzo Colitti return -ENODEV; 336*6d0bfe22SLorenzo Colitti } 337*6d0bfe22SLorenzo Colitti } 338*6d0bfe22SLorenzo Colitti has_addr = pingv6_ops.ipv6_chk_addr(net, &addr->sin6_addr, dev, 339*6d0bfe22SLorenzo Colitti scoped); 340*6d0bfe22SLorenzo Colitti rcu_read_unlock(); 341*6d0bfe22SLorenzo Colitti 342*6d0bfe22SLorenzo Colitti if (!(isk->freebind || isk->transparent || has_addr || 343*6d0bfe22SLorenzo Colitti addr_type == IPV6_ADDR_ANY)) 344*6d0bfe22SLorenzo Colitti return -EADDRNOTAVAIL; 345*6d0bfe22SLorenzo Colitti 346*6d0bfe22SLorenzo Colitti if (scoped) 347*6d0bfe22SLorenzo Colitti sk->sk_bound_dev_if = addr->sin6_scope_id; 348*6d0bfe22SLorenzo Colitti #endif 349*6d0bfe22SLorenzo Colitti } else { 350*6d0bfe22SLorenzo Colitti return -EAFNOSUPPORT; 351*6d0bfe22SLorenzo Colitti } 352*6d0bfe22SLorenzo Colitti return 0; 353*6d0bfe22SLorenzo Colitti } 354*6d0bfe22SLorenzo Colitti 355*6d0bfe22SLorenzo Colitti void ping_set_saddr(struct sock *sk, struct sockaddr *saddr) 356*6d0bfe22SLorenzo Colitti { 357*6d0bfe22SLorenzo Colitti if (saddr->sa_family == AF_INET) { 358*6d0bfe22SLorenzo Colitti struct inet_sock *isk = inet_sk(sk); 359*6d0bfe22SLorenzo Colitti struct sockaddr_in *addr = (struct sockaddr_in *) saddr; 360*6d0bfe22SLorenzo Colitti isk->inet_rcv_saddr = isk->inet_saddr = addr->sin_addr.s_addr; 361*6d0bfe22SLorenzo Colitti #if IS_ENABLED(CONFIG_IPV6) 362*6d0bfe22SLorenzo Colitti } else if (saddr->sa_family == AF_INET6) { 363*6d0bfe22SLorenzo Colitti struct sockaddr_in6 *addr = (struct sockaddr_in6 *) saddr; 364*6d0bfe22SLorenzo Colitti struct ipv6_pinfo *np = inet6_sk(sk); 365*6d0bfe22SLorenzo Colitti np->rcv_saddr = np->saddr = addr->sin6_addr; 366*6d0bfe22SLorenzo Colitti #endif 367*6d0bfe22SLorenzo Colitti } 368*6d0bfe22SLorenzo Colitti } 369*6d0bfe22SLorenzo Colitti 370*6d0bfe22SLorenzo Colitti void ping_clear_saddr(struct sock *sk, int dif) 371*6d0bfe22SLorenzo Colitti { 372*6d0bfe22SLorenzo Colitti sk->sk_bound_dev_if = dif; 373*6d0bfe22SLorenzo Colitti if (sk->sk_family == AF_INET) { 374*6d0bfe22SLorenzo Colitti struct inet_sock *isk = inet_sk(sk); 375*6d0bfe22SLorenzo Colitti isk->inet_rcv_saddr = isk->inet_saddr = 0; 376*6d0bfe22SLorenzo Colitti #if IS_ENABLED(CONFIG_IPV6) 377*6d0bfe22SLorenzo Colitti } else if (sk->sk_family == AF_INET6) { 378*6d0bfe22SLorenzo Colitti struct ipv6_pinfo *np = inet6_sk(sk); 379*6d0bfe22SLorenzo Colitti memset(&np->rcv_saddr, 0, sizeof(np->rcv_saddr)); 380*6d0bfe22SLorenzo Colitti memset(&np->saddr, 0, sizeof(np->saddr)); 381*6d0bfe22SLorenzo Colitti #endif 382*6d0bfe22SLorenzo Colitti } 383*6d0bfe22SLorenzo Colitti } 384*6d0bfe22SLorenzo Colitti /* 385*6d0bfe22SLorenzo Colitti * We need our own bind because there are no privileged id's == local ports. 386*6d0bfe22SLorenzo Colitti * Moreover, we don't allow binding to multi- and broadcast addresses. 387*6d0bfe22SLorenzo Colitti */ 388*6d0bfe22SLorenzo Colitti 389*6d0bfe22SLorenzo Colitti int ping_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len) 390*6d0bfe22SLorenzo Colitti { 391*6d0bfe22SLorenzo Colitti struct inet_sock *isk = inet_sk(sk); 392*6d0bfe22SLorenzo Colitti unsigned short snum; 393*6d0bfe22SLorenzo Colitti int err; 394*6d0bfe22SLorenzo Colitti int dif = sk->sk_bound_dev_if; 395*6d0bfe22SLorenzo Colitti 396*6d0bfe22SLorenzo Colitti err = ping_check_bind_addr(sk, isk, uaddr, addr_len); 397*6d0bfe22SLorenzo Colitti if (err) 398*6d0bfe22SLorenzo Colitti return err; 399*6d0bfe22SLorenzo Colitti 400c319b4d7SVasiliy Kulikov lock_sock(sk); 401c319b4d7SVasiliy Kulikov 402c319b4d7SVasiliy Kulikov err = -EINVAL; 403c319b4d7SVasiliy Kulikov if (isk->inet_num != 0) 404c319b4d7SVasiliy Kulikov goto out; 405c319b4d7SVasiliy Kulikov 406c319b4d7SVasiliy Kulikov err = -EADDRINUSE; 407*6d0bfe22SLorenzo Colitti ping_set_saddr(sk, uaddr); 408*6d0bfe22SLorenzo Colitti snum = ntohs(((struct sockaddr_in *)uaddr)->sin_port); 409*6d0bfe22SLorenzo Colitti if (ping_get_port(sk, snum) != 0) { 410*6d0bfe22SLorenzo Colitti ping_clear_saddr(sk, dif); 411c319b4d7SVasiliy Kulikov goto out; 412c319b4d7SVasiliy Kulikov } 413c319b4d7SVasiliy Kulikov 414*6d0bfe22SLorenzo Colitti pr_debug("after bind(): num = %d, dif = %d\n", 415c319b4d7SVasiliy Kulikov (int)isk->inet_num, 416c319b4d7SVasiliy Kulikov (int)sk->sk_bound_dev_if); 417c319b4d7SVasiliy Kulikov 418c319b4d7SVasiliy Kulikov err = 0; 419*6d0bfe22SLorenzo Colitti if ((sk->sk_family == AF_INET && isk->inet_rcv_saddr) || 420*6d0bfe22SLorenzo Colitti (sk->sk_family == AF_INET6 && 421*6d0bfe22SLorenzo Colitti !ipv6_addr_any(&inet6_sk(sk)->rcv_saddr))) 422c319b4d7SVasiliy Kulikov sk->sk_userlocks |= SOCK_BINDADDR_LOCK; 423*6d0bfe22SLorenzo Colitti 424c319b4d7SVasiliy Kulikov if (snum) 425c319b4d7SVasiliy Kulikov sk->sk_userlocks |= SOCK_BINDPORT_LOCK; 426c319b4d7SVasiliy Kulikov isk->inet_sport = htons(isk->inet_num); 427c319b4d7SVasiliy Kulikov isk->inet_daddr = 0; 428c319b4d7SVasiliy Kulikov isk->inet_dport = 0; 429*6d0bfe22SLorenzo Colitti 430*6d0bfe22SLorenzo Colitti #if IS_ENABLED(CONFIG_IPV6) 431*6d0bfe22SLorenzo Colitti if (sk->sk_family == AF_INET6) 432*6d0bfe22SLorenzo Colitti memset(&inet6_sk(sk)->daddr, 0, sizeof(inet6_sk(sk)->daddr)); 433*6d0bfe22SLorenzo Colitti #endif 434*6d0bfe22SLorenzo Colitti 435c319b4d7SVasiliy Kulikov sk_dst_reset(sk); 436c319b4d7SVasiliy Kulikov out: 437c319b4d7SVasiliy Kulikov release_sock(sk); 438c319b4d7SVasiliy Kulikov pr_debug("ping_v4_bind -> %d\n", err); 439c319b4d7SVasiliy Kulikov return err; 440c319b4d7SVasiliy Kulikov } 441*6d0bfe22SLorenzo Colitti EXPORT_SYMBOL_GPL(ping_bind); 442c319b4d7SVasiliy Kulikov 443c319b4d7SVasiliy Kulikov /* 444c319b4d7SVasiliy Kulikov * Is this a supported type of ICMP message? 445c319b4d7SVasiliy Kulikov */ 446c319b4d7SVasiliy Kulikov 447*6d0bfe22SLorenzo Colitti static inline int ping_supported(int family, int type, int code) 448c319b4d7SVasiliy Kulikov { 449*6d0bfe22SLorenzo Colitti return (family == AF_INET && type == ICMP_ECHO && code == 0) || 450*6d0bfe22SLorenzo Colitti (family == AF_INET6 && type == ICMPV6_ECHO_REQUEST && code == 0); 451c319b4d7SVasiliy Kulikov } 452c319b4d7SVasiliy Kulikov 453c319b4d7SVasiliy Kulikov /* 454c319b4d7SVasiliy Kulikov * This routine is called by the ICMP module when it gets some 455c319b4d7SVasiliy Kulikov * sort of error condition. 456c319b4d7SVasiliy Kulikov */ 457c319b4d7SVasiliy Kulikov 458*6d0bfe22SLorenzo Colitti void ping_err(struct sk_buff *skb, int offset, u32 info) 459c319b4d7SVasiliy Kulikov { 460*6d0bfe22SLorenzo Colitti int family; 461*6d0bfe22SLorenzo Colitti struct icmphdr *icmph; 462c319b4d7SVasiliy Kulikov struct inet_sock *inet_sock; 463*6d0bfe22SLorenzo Colitti int type; 464*6d0bfe22SLorenzo Colitti int code; 465c319b4d7SVasiliy Kulikov struct net *net = dev_net(skb->dev); 466c319b4d7SVasiliy Kulikov struct sock *sk; 467c319b4d7SVasiliy Kulikov int harderr; 468c319b4d7SVasiliy Kulikov int err; 469c319b4d7SVasiliy Kulikov 470*6d0bfe22SLorenzo Colitti if (skb->protocol == htons(ETH_P_IP)) { 471*6d0bfe22SLorenzo Colitti family = AF_INET; 472*6d0bfe22SLorenzo Colitti type = icmp_hdr(skb)->type; 473*6d0bfe22SLorenzo Colitti code = icmp_hdr(skb)->code; 474*6d0bfe22SLorenzo Colitti icmph = (struct icmphdr *)(skb->data + offset); 475*6d0bfe22SLorenzo Colitti } else if (skb->protocol == htons(ETH_P_IPV6)) { 476*6d0bfe22SLorenzo Colitti family = AF_INET6; 477*6d0bfe22SLorenzo Colitti type = icmp6_hdr(skb)->icmp6_type; 478*6d0bfe22SLorenzo Colitti code = icmp6_hdr(skb)->icmp6_code; 479*6d0bfe22SLorenzo Colitti icmph = (struct icmphdr *) (skb->data + offset); 480*6d0bfe22SLorenzo Colitti } else { 481*6d0bfe22SLorenzo Colitti BUG(); 482*6d0bfe22SLorenzo Colitti } 483*6d0bfe22SLorenzo Colitti 484c319b4d7SVasiliy Kulikov /* We assume the packet has already been checked by icmp_unreach */ 485c319b4d7SVasiliy Kulikov 486*6d0bfe22SLorenzo Colitti if (!ping_supported(family, icmph->type, icmph->code)) 487c319b4d7SVasiliy Kulikov return; 488c319b4d7SVasiliy Kulikov 489*6d0bfe22SLorenzo Colitti pr_debug("ping_err(proto=0x%x,type=%d,code=%d,id=%04x,seq=%04x)\n", 490*6d0bfe22SLorenzo Colitti skb->protocol, type, code, ntohs(icmph->un.echo.id), 491*6d0bfe22SLorenzo Colitti ntohs(icmph->un.echo.sequence)); 492c319b4d7SVasiliy Kulikov 493*6d0bfe22SLorenzo Colitti sk = ping_lookup(net, skb, ntohs(icmph->un.echo.id)); 494c319b4d7SVasiliy Kulikov if (sk == NULL) { 495c319b4d7SVasiliy Kulikov pr_debug("no socket, dropping\n"); 496c319b4d7SVasiliy Kulikov return; /* No socket for error */ 497c319b4d7SVasiliy Kulikov } 498c319b4d7SVasiliy Kulikov pr_debug("err on socket %p\n", sk); 499c319b4d7SVasiliy Kulikov 500c319b4d7SVasiliy Kulikov err = 0; 501c319b4d7SVasiliy Kulikov harderr = 0; 502c319b4d7SVasiliy Kulikov inet_sock = inet_sk(sk); 503c319b4d7SVasiliy Kulikov 504*6d0bfe22SLorenzo Colitti if (skb->protocol == htons(ETH_P_IP)) { 505c319b4d7SVasiliy Kulikov switch (type) { 506c319b4d7SVasiliy Kulikov default: 507c319b4d7SVasiliy Kulikov case ICMP_TIME_EXCEEDED: 508c319b4d7SVasiliy Kulikov err = EHOSTUNREACH; 509c319b4d7SVasiliy Kulikov break; 510c319b4d7SVasiliy Kulikov case ICMP_SOURCE_QUENCH: 511c319b4d7SVasiliy Kulikov /* This is not a real error but ping wants to see it. 512*6d0bfe22SLorenzo Colitti * Report it with some fake errno. 513*6d0bfe22SLorenzo Colitti */ 514c319b4d7SVasiliy Kulikov err = EREMOTEIO; 515c319b4d7SVasiliy Kulikov break; 516c319b4d7SVasiliy Kulikov case ICMP_PARAMETERPROB: 517c319b4d7SVasiliy Kulikov err = EPROTO; 518c319b4d7SVasiliy Kulikov harderr = 1; 519c319b4d7SVasiliy Kulikov break; 520c319b4d7SVasiliy Kulikov case ICMP_DEST_UNREACH: 521c319b4d7SVasiliy Kulikov if (code == ICMP_FRAG_NEEDED) { /* Path MTU discovery */ 52236393395SDavid S. Miller ipv4_sk_update_pmtu(skb, sk, info); 523c319b4d7SVasiliy Kulikov if (inet_sock->pmtudisc != IP_PMTUDISC_DONT) { 524c319b4d7SVasiliy Kulikov err = EMSGSIZE; 525c319b4d7SVasiliy Kulikov harderr = 1; 526c319b4d7SVasiliy Kulikov break; 527c319b4d7SVasiliy Kulikov } 528c319b4d7SVasiliy Kulikov goto out; 529c319b4d7SVasiliy Kulikov } 530c319b4d7SVasiliy Kulikov err = EHOSTUNREACH; 531c319b4d7SVasiliy Kulikov if (code <= NR_ICMP_UNREACH) { 532c319b4d7SVasiliy Kulikov harderr = icmp_err_convert[code].fatal; 533c319b4d7SVasiliy Kulikov err = icmp_err_convert[code].errno; 534c319b4d7SVasiliy Kulikov } 535c319b4d7SVasiliy Kulikov break; 536c319b4d7SVasiliy Kulikov case ICMP_REDIRECT: 537c319b4d7SVasiliy Kulikov /* See ICMP_SOURCE_QUENCH */ 53855be7a9cSDavid S. Miller ipv4_sk_redirect(skb, sk); 539c319b4d7SVasiliy Kulikov err = EREMOTEIO; 540c319b4d7SVasiliy Kulikov break; 541c319b4d7SVasiliy Kulikov } 542*6d0bfe22SLorenzo Colitti #if IS_ENABLED(CONFIG_IPV6) 543*6d0bfe22SLorenzo Colitti } else if (skb->protocol == htons(ETH_P_IPV6)) { 544*6d0bfe22SLorenzo Colitti harderr = pingv6_ops.icmpv6_err_convert(type, code, &err); 545*6d0bfe22SLorenzo Colitti #endif 546*6d0bfe22SLorenzo Colitti } 547c319b4d7SVasiliy Kulikov 548c319b4d7SVasiliy Kulikov /* 549c319b4d7SVasiliy Kulikov * RFC1122: OK. Passes ICMP errors back to application, as per 550c319b4d7SVasiliy Kulikov * 4.1.3.3. 551c319b4d7SVasiliy Kulikov */ 552*6d0bfe22SLorenzo Colitti if ((family == AF_INET && !inet_sock->recverr) || 553*6d0bfe22SLorenzo Colitti (family == AF_INET6 && !inet6_sk(sk)->recverr)) { 554c319b4d7SVasiliy Kulikov if (!harderr || sk->sk_state != TCP_ESTABLISHED) 555c319b4d7SVasiliy Kulikov goto out; 556c319b4d7SVasiliy Kulikov } else { 557*6d0bfe22SLorenzo Colitti if (family == AF_INET) { 558c319b4d7SVasiliy Kulikov ip_icmp_error(sk, skb, err, 0 /* no remote port */, 559c319b4d7SVasiliy Kulikov info, (u8 *)icmph); 560*6d0bfe22SLorenzo Colitti #if IS_ENABLED(CONFIG_IPV6) 561*6d0bfe22SLorenzo Colitti } else if (family == AF_INET6) { 562*6d0bfe22SLorenzo Colitti pingv6_ops.ipv6_icmp_error(sk, skb, err, 0, 563*6d0bfe22SLorenzo Colitti info, (u8 *)icmph); 564*6d0bfe22SLorenzo Colitti #endif 565*6d0bfe22SLorenzo Colitti } 566c319b4d7SVasiliy Kulikov } 567c319b4d7SVasiliy Kulikov sk->sk_err = err; 568c319b4d7SVasiliy Kulikov sk->sk_error_report(sk); 569c319b4d7SVasiliy Kulikov out: 570c319b4d7SVasiliy Kulikov sock_put(sk); 571c319b4d7SVasiliy Kulikov } 572*6d0bfe22SLorenzo Colitti EXPORT_SYMBOL_GPL(ping_err); 573c319b4d7SVasiliy Kulikov 574c319b4d7SVasiliy Kulikov /* 575*6d0bfe22SLorenzo Colitti * Copy and checksum an ICMP Echo packet from user space into a buffer 576*6d0bfe22SLorenzo Colitti * starting from the payload. 577c319b4d7SVasiliy Kulikov */ 578c319b4d7SVasiliy Kulikov 579*6d0bfe22SLorenzo Colitti int ping_getfrag(void *from, char *to, 580c319b4d7SVasiliy Kulikov int offset, int fraglen, int odd, struct sk_buff *skb) 581c319b4d7SVasiliy Kulikov { 582c319b4d7SVasiliy Kulikov struct pingfakehdr *pfh = (struct pingfakehdr *)from; 583c319b4d7SVasiliy Kulikov 584c319b4d7SVasiliy Kulikov if (offset == 0) { 585c319b4d7SVasiliy Kulikov if (fraglen < sizeof(struct icmphdr)) 586c319b4d7SVasiliy Kulikov BUG(); 587c319b4d7SVasiliy Kulikov if (csum_partial_copy_fromiovecend(to + sizeof(struct icmphdr), 588c319b4d7SVasiliy Kulikov pfh->iov, 0, fraglen - sizeof(struct icmphdr), 589c319b4d7SVasiliy Kulikov &pfh->wcheck)) 590c319b4d7SVasiliy Kulikov return -EFAULT; 591*6d0bfe22SLorenzo Colitti } else if (offset < sizeof(struct icmphdr)) { 592c319b4d7SVasiliy Kulikov BUG(); 593*6d0bfe22SLorenzo Colitti } else { 594c319b4d7SVasiliy Kulikov if (csum_partial_copy_fromiovecend 595c319b4d7SVasiliy Kulikov (to, pfh->iov, offset - sizeof(struct icmphdr), 596c319b4d7SVasiliy Kulikov fraglen, &pfh->wcheck)) 597c319b4d7SVasiliy Kulikov return -EFAULT; 598c319b4d7SVasiliy Kulikov } 599c319b4d7SVasiliy Kulikov 600*6d0bfe22SLorenzo Colitti #if IS_ENABLED(CONFIG_IPV6) 601*6d0bfe22SLorenzo Colitti /* For IPv6, checksum each skb as we go along, as expected by 602*6d0bfe22SLorenzo Colitti * icmpv6_push_pending_frames. For IPv4, accumulate the checksum in 603*6d0bfe22SLorenzo Colitti * wcheck, it will be finalized in ping_v4_push_pending_frames. 604*6d0bfe22SLorenzo Colitti */ 605*6d0bfe22SLorenzo Colitti if (pfh->family == AF_INET6) { 606*6d0bfe22SLorenzo Colitti skb->csum = pfh->wcheck; 607*6d0bfe22SLorenzo Colitti skb->ip_summed = CHECKSUM_NONE; 608*6d0bfe22SLorenzo Colitti pfh->wcheck = 0; 609*6d0bfe22SLorenzo Colitti } 610*6d0bfe22SLorenzo Colitti #endif 611*6d0bfe22SLorenzo Colitti 612*6d0bfe22SLorenzo Colitti return 0; 613*6d0bfe22SLorenzo Colitti } 614*6d0bfe22SLorenzo Colitti EXPORT_SYMBOL_GPL(ping_getfrag); 615*6d0bfe22SLorenzo Colitti 616*6d0bfe22SLorenzo Colitti static int ping_v4_push_pending_frames(struct sock *sk, struct pingfakehdr *pfh, 61775e308c8SChangli Gao struct flowi4 *fl4) 618c319b4d7SVasiliy Kulikov { 619c319b4d7SVasiliy Kulikov struct sk_buff *skb = skb_peek(&sk->sk_write_queue); 620c319b4d7SVasiliy Kulikov 621c319b4d7SVasiliy Kulikov pfh->wcheck = csum_partial((char *)&pfh->icmph, 622c319b4d7SVasiliy Kulikov sizeof(struct icmphdr), pfh->wcheck); 623c319b4d7SVasiliy Kulikov pfh->icmph.checksum = csum_fold(pfh->wcheck); 624c319b4d7SVasiliy Kulikov memcpy(icmp_hdr(skb), &pfh->icmph, sizeof(struct icmphdr)); 625c319b4d7SVasiliy Kulikov skb->ip_summed = CHECKSUM_NONE; 626c319b4d7SVasiliy Kulikov return ip_push_pending_frames(sk, fl4); 627c319b4d7SVasiliy Kulikov } 628c319b4d7SVasiliy Kulikov 629*6d0bfe22SLorenzo Colitti int ping_common_sendmsg(int family, struct msghdr *msg, size_t len, 630*6d0bfe22SLorenzo Colitti void *user_icmph, size_t icmph_len) { 631*6d0bfe22SLorenzo Colitti u8 type, code; 632*6d0bfe22SLorenzo Colitti 633*6d0bfe22SLorenzo Colitti if (len > 0xFFFF) 634*6d0bfe22SLorenzo Colitti return -EMSGSIZE; 635*6d0bfe22SLorenzo Colitti 636*6d0bfe22SLorenzo Colitti /* 637*6d0bfe22SLorenzo Colitti * Check the flags. 638*6d0bfe22SLorenzo Colitti */ 639*6d0bfe22SLorenzo Colitti 640*6d0bfe22SLorenzo Colitti /* Mirror BSD error message compatibility */ 641*6d0bfe22SLorenzo Colitti if (msg->msg_flags & MSG_OOB) 642*6d0bfe22SLorenzo Colitti return -EOPNOTSUPP; 643*6d0bfe22SLorenzo Colitti 644*6d0bfe22SLorenzo Colitti /* 645*6d0bfe22SLorenzo Colitti * Fetch the ICMP header provided by the userland. 646*6d0bfe22SLorenzo Colitti * iovec is modified! The ICMP header is consumed. 647*6d0bfe22SLorenzo Colitti */ 648*6d0bfe22SLorenzo Colitti if (memcpy_fromiovec(user_icmph, msg->msg_iov, icmph_len)) 649*6d0bfe22SLorenzo Colitti return -EFAULT; 650*6d0bfe22SLorenzo Colitti 651*6d0bfe22SLorenzo Colitti if (family == AF_INET) { 652*6d0bfe22SLorenzo Colitti type = ((struct icmphdr *) user_icmph)->type; 653*6d0bfe22SLorenzo Colitti code = ((struct icmphdr *) user_icmph)->code; 654*6d0bfe22SLorenzo Colitti #if IS_ENABLED(CONFIG_IPV6) 655*6d0bfe22SLorenzo Colitti } else if (family == AF_INET6) { 656*6d0bfe22SLorenzo Colitti type = ((struct icmp6hdr *) user_icmph)->icmp6_type; 657*6d0bfe22SLorenzo Colitti code = ((struct icmp6hdr *) user_icmph)->icmp6_code; 658*6d0bfe22SLorenzo Colitti #endif 659*6d0bfe22SLorenzo Colitti } else { 660*6d0bfe22SLorenzo Colitti BUG(); 661*6d0bfe22SLorenzo Colitti } 662*6d0bfe22SLorenzo Colitti 663*6d0bfe22SLorenzo Colitti if (!ping_supported(family, type, code)) 664*6d0bfe22SLorenzo Colitti return -EINVAL; 665*6d0bfe22SLorenzo Colitti 666*6d0bfe22SLorenzo Colitti return 0; 667*6d0bfe22SLorenzo Colitti } 668*6d0bfe22SLorenzo Colitti EXPORT_SYMBOL_GPL(ping_common_sendmsg); 669*6d0bfe22SLorenzo Colitti 670*6d0bfe22SLorenzo Colitti int ping_v4_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, 671c319b4d7SVasiliy Kulikov size_t len) 672c319b4d7SVasiliy Kulikov { 673c319b4d7SVasiliy Kulikov struct net *net = sock_net(sk); 674c319b4d7SVasiliy Kulikov struct flowi4 fl4; 675c319b4d7SVasiliy Kulikov struct inet_sock *inet = inet_sk(sk); 676c319b4d7SVasiliy Kulikov struct ipcm_cookie ipc; 677c319b4d7SVasiliy Kulikov struct icmphdr user_icmph; 678c319b4d7SVasiliy Kulikov struct pingfakehdr pfh; 679c319b4d7SVasiliy Kulikov struct rtable *rt = NULL; 680c319b4d7SVasiliy Kulikov struct ip_options_data opt_copy; 681c319b4d7SVasiliy Kulikov int free = 0; 682747465efSEric Dumazet __be32 saddr, daddr, faddr; 683c319b4d7SVasiliy Kulikov u8 tos; 684c319b4d7SVasiliy Kulikov int err; 685c319b4d7SVasiliy Kulikov 686*6d0bfe22SLorenzo Colitti pr_debug("ping_v4_sendmsg(sk=%p,sk->num=%u)\n", inet, inet->inet_num); 687c319b4d7SVasiliy Kulikov 688*6d0bfe22SLorenzo Colitti err = ping_common_sendmsg(AF_INET, msg, len, &user_icmph, 689*6d0bfe22SLorenzo Colitti sizeof(user_icmph)); 690*6d0bfe22SLorenzo Colitti if (err) 691*6d0bfe22SLorenzo Colitti return err; 692c319b4d7SVasiliy Kulikov 693c319b4d7SVasiliy Kulikov /* 694c319b4d7SVasiliy Kulikov * Get and verify the address. 695c319b4d7SVasiliy Kulikov */ 696c319b4d7SVasiliy Kulikov 697c319b4d7SVasiliy Kulikov if (msg->msg_name) { 698c319b4d7SVasiliy Kulikov struct sockaddr_in *usin = (struct sockaddr_in *)msg->msg_name; 699c319b4d7SVasiliy Kulikov if (msg->msg_namelen < sizeof(*usin)) 700c319b4d7SVasiliy Kulikov return -EINVAL; 701c319b4d7SVasiliy Kulikov if (usin->sin_family != AF_INET) 702c319b4d7SVasiliy Kulikov return -EINVAL; 703c319b4d7SVasiliy Kulikov daddr = usin->sin_addr.s_addr; 704c319b4d7SVasiliy Kulikov /* no remote port */ 705c319b4d7SVasiliy Kulikov } else { 706c319b4d7SVasiliy Kulikov if (sk->sk_state != TCP_ESTABLISHED) 707c319b4d7SVasiliy Kulikov return -EDESTADDRREQ; 708c319b4d7SVasiliy Kulikov daddr = inet->inet_daddr; 709c319b4d7SVasiliy Kulikov /* no remote port */ 710c319b4d7SVasiliy Kulikov } 711c319b4d7SVasiliy Kulikov 712c319b4d7SVasiliy Kulikov ipc.addr = inet->inet_saddr; 713c319b4d7SVasiliy Kulikov ipc.opt = NULL; 714c319b4d7SVasiliy Kulikov ipc.oif = sk->sk_bound_dev_if; 715c319b4d7SVasiliy Kulikov ipc.tx_flags = 0; 716bf84a010SDaniel Borkmann 717bf84a010SDaniel Borkmann sock_tx_timestamp(sk, &ipc.tx_flags); 718c319b4d7SVasiliy Kulikov 719c319b4d7SVasiliy Kulikov if (msg->msg_controllen) { 720c319b4d7SVasiliy Kulikov err = ip_cmsg_send(sock_net(sk), msg, &ipc); 721c319b4d7SVasiliy Kulikov if (err) 722c319b4d7SVasiliy Kulikov return err; 723c319b4d7SVasiliy Kulikov if (ipc.opt) 724c319b4d7SVasiliy Kulikov free = 1; 725c319b4d7SVasiliy Kulikov } 726c319b4d7SVasiliy Kulikov if (!ipc.opt) { 727c319b4d7SVasiliy Kulikov struct ip_options_rcu *inet_opt; 728c319b4d7SVasiliy Kulikov 729c319b4d7SVasiliy Kulikov rcu_read_lock(); 730c319b4d7SVasiliy Kulikov inet_opt = rcu_dereference(inet->inet_opt); 731c319b4d7SVasiliy Kulikov if (inet_opt) { 732c319b4d7SVasiliy Kulikov memcpy(&opt_copy, inet_opt, 733c319b4d7SVasiliy Kulikov sizeof(*inet_opt) + inet_opt->opt.optlen); 734c319b4d7SVasiliy Kulikov ipc.opt = &opt_copy.opt; 735c319b4d7SVasiliy Kulikov } 736c319b4d7SVasiliy Kulikov rcu_read_unlock(); 737c319b4d7SVasiliy Kulikov } 738c319b4d7SVasiliy Kulikov 739c319b4d7SVasiliy Kulikov saddr = ipc.addr; 740c319b4d7SVasiliy Kulikov ipc.addr = faddr = daddr; 741c319b4d7SVasiliy Kulikov 742c319b4d7SVasiliy Kulikov if (ipc.opt && ipc.opt->opt.srr) { 743c319b4d7SVasiliy Kulikov if (!daddr) 744c319b4d7SVasiliy Kulikov return -EINVAL; 745c319b4d7SVasiliy Kulikov faddr = ipc.opt->opt.faddr; 746c319b4d7SVasiliy Kulikov } 747c319b4d7SVasiliy Kulikov tos = RT_TOS(inet->tos); 748c319b4d7SVasiliy Kulikov if (sock_flag(sk, SOCK_LOCALROUTE) || 749c319b4d7SVasiliy Kulikov (msg->msg_flags & MSG_DONTROUTE) || 750c319b4d7SVasiliy Kulikov (ipc.opt && ipc.opt->opt.is_strictroute)) { 751c319b4d7SVasiliy Kulikov tos |= RTO_ONLINK; 752c319b4d7SVasiliy Kulikov } 753c319b4d7SVasiliy Kulikov 754c319b4d7SVasiliy Kulikov if (ipv4_is_multicast(daddr)) { 755c319b4d7SVasiliy Kulikov if (!ipc.oif) 756c319b4d7SVasiliy Kulikov ipc.oif = inet->mc_index; 757c319b4d7SVasiliy Kulikov if (!saddr) 758c319b4d7SVasiliy Kulikov saddr = inet->mc_addr; 75976e21053SErich E. Hoover } else if (!ipc.oif) 76076e21053SErich E. Hoover ipc.oif = inet->uc_index; 761c319b4d7SVasiliy Kulikov 762c319b4d7SVasiliy Kulikov flowi4_init_output(&fl4, ipc.oif, sk->sk_mark, tos, 763c319b4d7SVasiliy Kulikov RT_SCOPE_UNIVERSE, sk->sk_protocol, 764c319b4d7SVasiliy Kulikov inet_sk_flowi_flags(sk), faddr, saddr, 0, 0); 765c319b4d7SVasiliy Kulikov 766c319b4d7SVasiliy Kulikov security_sk_classify_flow(sk, flowi4_to_flowi(&fl4)); 767c319b4d7SVasiliy Kulikov rt = ip_route_output_flow(net, &fl4, sk); 768c319b4d7SVasiliy Kulikov if (IS_ERR(rt)) { 769c319b4d7SVasiliy Kulikov err = PTR_ERR(rt); 770c319b4d7SVasiliy Kulikov rt = NULL; 771c319b4d7SVasiliy Kulikov if (err == -ENETUNREACH) 772c319b4d7SVasiliy Kulikov IP_INC_STATS_BH(net, IPSTATS_MIB_OUTNOROUTES); 773c319b4d7SVasiliy Kulikov goto out; 774c319b4d7SVasiliy Kulikov } 775c319b4d7SVasiliy Kulikov 776c319b4d7SVasiliy Kulikov err = -EACCES; 777c319b4d7SVasiliy Kulikov if ((rt->rt_flags & RTCF_BROADCAST) && 778c319b4d7SVasiliy Kulikov !sock_flag(sk, SOCK_BROADCAST)) 779c319b4d7SVasiliy Kulikov goto out; 780c319b4d7SVasiliy Kulikov 781c319b4d7SVasiliy Kulikov if (msg->msg_flags & MSG_CONFIRM) 782c319b4d7SVasiliy Kulikov goto do_confirm; 783c319b4d7SVasiliy Kulikov back_from_confirm: 784c319b4d7SVasiliy Kulikov 785c319b4d7SVasiliy Kulikov if (!ipc.addr) 786c319b4d7SVasiliy Kulikov ipc.addr = fl4.daddr; 787c319b4d7SVasiliy Kulikov 788c319b4d7SVasiliy Kulikov lock_sock(sk); 789c319b4d7SVasiliy Kulikov 790c319b4d7SVasiliy Kulikov pfh.icmph.type = user_icmph.type; /* already checked */ 791c319b4d7SVasiliy Kulikov pfh.icmph.code = user_icmph.code; /* ditto */ 792c319b4d7SVasiliy Kulikov pfh.icmph.checksum = 0; 793c319b4d7SVasiliy Kulikov pfh.icmph.un.echo.id = inet->inet_sport; 794c319b4d7SVasiliy Kulikov pfh.icmph.un.echo.sequence = user_icmph.un.echo.sequence; 795c319b4d7SVasiliy Kulikov pfh.iov = msg->msg_iov; 796c319b4d7SVasiliy Kulikov pfh.wcheck = 0; 797*6d0bfe22SLorenzo Colitti pfh.family = AF_INET; 798c319b4d7SVasiliy Kulikov 799c319b4d7SVasiliy Kulikov err = ip_append_data(sk, &fl4, ping_getfrag, &pfh, len, 800c319b4d7SVasiliy Kulikov 0, &ipc, &rt, msg->msg_flags); 801c319b4d7SVasiliy Kulikov if (err) 802c319b4d7SVasiliy Kulikov ip_flush_pending_frames(sk); 803c319b4d7SVasiliy Kulikov else 804*6d0bfe22SLorenzo Colitti err = ping_v4_push_pending_frames(sk, &pfh, &fl4); 805c319b4d7SVasiliy Kulikov release_sock(sk); 806c319b4d7SVasiliy Kulikov 807c319b4d7SVasiliy Kulikov out: 808c319b4d7SVasiliy Kulikov ip_rt_put(rt); 809c319b4d7SVasiliy Kulikov if (free) 810c319b4d7SVasiliy Kulikov kfree(ipc.opt); 811c319b4d7SVasiliy Kulikov if (!err) { 812c319b4d7SVasiliy Kulikov icmp_out_count(sock_net(sk), user_icmph.type); 813c319b4d7SVasiliy Kulikov return len; 814c319b4d7SVasiliy Kulikov } 815c319b4d7SVasiliy Kulikov return err; 816c319b4d7SVasiliy Kulikov 817c319b4d7SVasiliy Kulikov do_confirm: 818c319b4d7SVasiliy Kulikov dst_confirm(&rt->dst); 819c319b4d7SVasiliy Kulikov if (!(msg->msg_flags & MSG_PROBE) || len) 820c319b4d7SVasiliy Kulikov goto back_from_confirm; 821c319b4d7SVasiliy Kulikov err = 0; 822c319b4d7SVasiliy Kulikov goto out; 823c319b4d7SVasiliy Kulikov } 824c319b4d7SVasiliy Kulikov 825*6d0bfe22SLorenzo Colitti int ping_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, 826c319b4d7SVasiliy Kulikov size_t len, int noblock, int flags, int *addr_len) 827c319b4d7SVasiliy Kulikov { 828c319b4d7SVasiliy Kulikov struct inet_sock *isk = inet_sk(sk); 829*6d0bfe22SLorenzo Colitti int family = sk->sk_family; 830*6d0bfe22SLorenzo Colitti struct sockaddr_in *sin; 831*6d0bfe22SLorenzo Colitti struct sockaddr_in6 *sin6; 832c319b4d7SVasiliy Kulikov struct sk_buff *skb; 833c319b4d7SVasiliy Kulikov int copied, err; 834c319b4d7SVasiliy Kulikov 835c319b4d7SVasiliy Kulikov pr_debug("ping_recvmsg(sk=%p,sk->num=%u)\n", isk, isk->inet_num); 836c319b4d7SVasiliy Kulikov 837a5e7424dSDavid S. Miller err = -EOPNOTSUPP; 838c319b4d7SVasiliy Kulikov if (flags & MSG_OOB) 839c319b4d7SVasiliy Kulikov goto out; 840c319b4d7SVasiliy Kulikov 841*6d0bfe22SLorenzo Colitti if (addr_len) { 842*6d0bfe22SLorenzo Colitti if (family == AF_INET) 843c319b4d7SVasiliy Kulikov *addr_len = sizeof(*sin); 844*6d0bfe22SLorenzo Colitti else if (family == AF_INET6 && addr_len) 845*6d0bfe22SLorenzo Colitti *addr_len = sizeof(*sin6); 846*6d0bfe22SLorenzo Colitti } 847c319b4d7SVasiliy Kulikov 848*6d0bfe22SLorenzo Colitti if (flags & MSG_ERRQUEUE) { 849*6d0bfe22SLorenzo Colitti if (family == AF_INET) { 850c319b4d7SVasiliy Kulikov return ip_recv_error(sk, msg, len); 851*6d0bfe22SLorenzo Colitti #if IS_ENABLED(CONFIG_IPV6) 852*6d0bfe22SLorenzo Colitti } else if (family == AF_INET6) { 853*6d0bfe22SLorenzo Colitti return pingv6_ops.ipv6_recv_error(sk, msg, len); 854*6d0bfe22SLorenzo Colitti #endif 855*6d0bfe22SLorenzo Colitti } 856*6d0bfe22SLorenzo Colitti } 857c319b4d7SVasiliy Kulikov 858c319b4d7SVasiliy Kulikov skb = skb_recv_datagram(sk, flags, noblock, &err); 859c319b4d7SVasiliy Kulikov if (!skb) 860c319b4d7SVasiliy Kulikov goto out; 861c319b4d7SVasiliy Kulikov 862c319b4d7SVasiliy Kulikov copied = skb->len; 863c319b4d7SVasiliy Kulikov if (copied > len) { 864c319b4d7SVasiliy Kulikov msg->msg_flags |= MSG_TRUNC; 865c319b4d7SVasiliy Kulikov copied = len; 866c319b4d7SVasiliy Kulikov } 867c319b4d7SVasiliy Kulikov 868c319b4d7SVasiliy Kulikov /* Don't bother checking the checksum */ 869c319b4d7SVasiliy Kulikov err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); 870c319b4d7SVasiliy Kulikov if (err) 871c319b4d7SVasiliy Kulikov goto done; 872c319b4d7SVasiliy Kulikov 873c319b4d7SVasiliy Kulikov sock_recv_timestamp(msg, sk, skb); 874c319b4d7SVasiliy Kulikov 875*6d0bfe22SLorenzo Colitti /* Copy the address and add cmsg data. */ 876*6d0bfe22SLorenzo Colitti if (family == AF_INET) { 877*6d0bfe22SLorenzo Colitti sin = (struct sockaddr_in *) msg->msg_name; 878c319b4d7SVasiliy Kulikov sin->sin_family = AF_INET; 879c319b4d7SVasiliy Kulikov sin->sin_port = 0 /* skb->h.uh->source */; 880c319b4d7SVasiliy Kulikov sin->sin_addr.s_addr = ip_hdr(skb)->saddr; 881c319b4d7SVasiliy Kulikov memset(sin->sin_zero, 0, sizeof(sin->sin_zero)); 882*6d0bfe22SLorenzo Colitti 883c319b4d7SVasiliy Kulikov if (isk->cmsg_flags) 884c319b4d7SVasiliy Kulikov ip_cmsg_recv(msg, skb); 885*6d0bfe22SLorenzo Colitti 886*6d0bfe22SLorenzo Colitti #if IS_ENABLED(CONFIG_IPV6) 887*6d0bfe22SLorenzo Colitti } else if (family == AF_INET6) { 888*6d0bfe22SLorenzo Colitti struct ipv6_pinfo *np = inet6_sk(sk); 889*6d0bfe22SLorenzo Colitti struct ipv6hdr *ip6 = ipv6_hdr(skb); 890*6d0bfe22SLorenzo Colitti sin6 = (struct sockaddr_in6 *) msg->msg_name; 891*6d0bfe22SLorenzo Colitti sin6->sin6_family = AF_INET6; 892*6d0bfe22SLorenzo Colitti sin6->sin6_port = 0; 893*6d0bfe22SLorenzo Colitti sin6->sin6_addr = ip6->saddr; 894*6d0bfe22SLorenzo Colitti 895*6d0bfe22SLorenzo Colitti if (np->sndflow) 896*6d0bfe22SLorenzo Colitti sin6->sin6_flowinfo = ip6_flowinfo(ip6); 897*6d0bfe22SLorenzo Colitti 898*6d0bfe22SLorenzo Colitti if (__ipv6_addr_needs_scope_id( 899*6d0bfe22SLorenzo Colitti ipv6_addr_type(&sin6->sin6_addr))) 900*6d0bfe22SLorenzo Colitti sin6->sin6_scope_id = IP6CB(skb)->iif; 901*6d0bfe22SLorenzo Colitti 902*6d0bfe22SLorenzo Colitti if (inet6_sk(sk)->rxopt.all) 903*6d0bfe22SLorenzo Colitti pingv6_ops.ip6_datagram_recv_ctl(sk, msg, skb); 904*6d0bfe22SLorenzo Colitti #endif 905*6d0bfe22SLorenzo Colitti } else { 906*6d0bfe22SLorenzo Colitti BUG(); 907*6d0bfe22SLorenzo Colitti } 908*6d0bfe22SLorenzo Colitti 909c319b4d7SVasiliy Kulikov err = copied; 910c319b4d7SVasiliy Kulikov 911c319b4d7SVasiliy Kulikov done: 912c319b4d7SVasiliy Kulikov skb_free_datagram(sk, skb); 913c319b4d7SVasiliy Kulikov out: 914c319b4d7SVasiliy Kulikov pr_debug("ping_recvmsg -> %d\n", err); 915c319b4d7SVasiliy Kulikov return err; 916c319b4d7SVasiliy Kulikov } 917*6d0bfe22SLorenzo Colitti EXPORT_SYMBOL_GPL(ping_recvmsg); 918c319b4d7SVasiliy Kulikov 919*6d0bfe22SLorenzo Colitti int ping_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) 920c319b4d7SVasiliy Kulikov { 921c319b4d7SVasiliy Kulikov pr_debug("ping_queue_rcv_skb(sk=%p,sk->num=%d,skb=%p)\n", 922c319b4d7SVasiliy Kulikov inet_sk(sk), inet_sk(sk)->inet_num, skb); 923c319b4d7SVasiliy Kulikov if (sock_queue_rcv_skb(sk, skb) < 0) { 924c319b4d7SVasiliy Kulikov kfree_skb(skb); 925c319b4d7SVasiliy Kulikov pr_debug("ping_queue_rcv_skb -> failed\n"); 926c319b4d7SVasiliy Kulikov return -1; 927c319b4d7SVasiliy Kulikov } 928c319b4d7SVasiliy Kulikov return 0; 929c319b4d7SVasiliy Kulikov } 930*6d0bfe22SLorenzo Colitti EXPORT_SYMBOL_GPL(ping_queue_rcv_skb); 931c319b4d7SVasiliy Kulikov 932c319b4d7SVasiliy Kulikov 933c319b4d7SVasiliy Kulikov /* 934c319b4d7SVasiliy Kulikov * All we need to do is get the socket. 935c319b4d7SVasiliy Kulikov */ 936c319b4d7SVasiliy Kulikov 937c319b4d7SVasiliy Kulikov void ping_rcv(struct sk_buff *skb) 938c319b4d7SVasiliy Kulikov { 939c319b4d7SVasiliy Kulikov struct sock *sk; 940c319b4d7SVasiliy Kulikov struct net *net = dev_net(skb->dev); 941c319b4d7SVasiliy Kulikov struct icmphdr *icmph = icmp_hdr(skb); 942c319b4d7SVasiliy Kulikov 943c319b4d7SVasiliy Kulikov /* We assume the packet has already been checked by icmp_rcv */ 944c319b4d7SVasiliy Kulikov 945c319b4d7SVasiliy Kulikov pr_debug("ping_rcv(skb=%p,id=%04x,seq=%04x)\n", 946c319b4d7SVasiliy Kulikov skb, ntohs(icmph->un.echo.id), ntohs(icmph->un.echo.sequence)); 947c319b4d7SVasiliy Kulikov 948c319b4d7SVasiliy Kulikov /* Push ICMP header back */ 949c319b4d7SVasiliy Kulikov skb_push(skb, skb->data - (u8 *)icmph); 950c319b4d7SVasiliy Kulikov 951*6d0bfe22SLorenzo Colitti sk = ping_lookup(net, skb, ntohs(icmph->un.echo.id)); 952c319b4d7SVasiliy Kulikov if (sk != NULL) { 953c319b4d7SVasiliy Kulikov pr_debug("rcv on socket %p\n", sk); 954c319b4d7SVasiliy Kulikov ping_queue_rcv_skb(sk, skb_get(skb)); 955c319b4d7SVasiliy Kulikov sock_put(sk); 956c319b4d7SVasiliy Kulikov return; 957c319b4d7SVasiliy Kulikov } 958c319b4d7SVasiliy Kulikov pr_debug("no socket, dropping\n"); 959c319b4d7SVasiliy Kulikov 960c319b4d7SVasiliy Kulikov /* We're called from icmp_rcv(). kfree_skb() is done there. */ 961c319b4d7SVasiliy Kulikov } 962*6d0bfe22SLorenzo Colitti EXPORT_SYMBOL_GPL(ping_rcv); 963c319b4d7SVasiliy Kulikov 964c319b4d7SVasiliy Kulikov struct proto ping_prot = { 965c319b4d7SVasiliy Kulikov .name = "PING", 966c319b4d7SVasiliy Kulikov .owner = THIS_MODULE, 967c319b4d7SVasiliy Kulikov .init = ping_init_sock, 968c319b4d7SVasiliy Kulikov .close = ping_close, 969c319b4d7SVasiliy Kulikov .connect = ip4_datagram_connect, 970c319b4d7SVasiliy Kulikov .disconnect = udp_disconnect, 971c319b4d7SVasiliy Kulikov .setsockopt = ip_setsockopt, 972c319b4d7SVasiliy Kulikov .getsockopt = ip_getsockopt, 973*6d0bfe22SLorenzo Colitti .sendmsg = ping_v4_sendmsg, 974c319b4d7SVasiliy Kulikov .recvmsg = ping_recvmsg, 975c319b4d7SVasiliy Kulikov .bind = ping_bind, 976c319b4d7SVasiliy Kulikov .backlog_rcv = ping_queue_rcv_skb, 9778141ed9fSSteffen Klassert .release_cb = ip4_datagram_release_cb, 978*6d0bfe22SLorenzo Colitti .hash = ping_hash, 979*6d0bfe22SLorenzo Colitti .unhash = ping_unhash, 980*6d0bfe22SLorenzo Colitti .get_port = ping_get_port, 981c319b4d7SVasiliy Kulikov .obj_size = sizeof(struct inet_sock), 982c319b4d7SVasiliy Kulikov }; 983c319b4d7SVasiliy Kulikov EXPORT_SYMBOL(ping_prot); 984c319b4d7SVasiliy Kulikov 985c319b4d7SVasiliy Kulikov #ifdef CONFIG_PROC_FS 986c319b4d7SVasiliy Kulikov 987c319b4d7SVasiliy Kulikov static struct sock *ping_get_first(struct seq_file *seq, int start) 988c319b4d7SVasiliy Kulikov { 989c319b4d7SVasiliy Kulikov struct sock *sk; 990c319b4d7SVasiliy Kulikov struct ping_iter_state *state = seq->private; 991c319b4d7SVasiliy Kulikov struct net *net = seq_file_net(seq); 992c319b4d7SVasiliy Kulikov 993c319b4d7SVasiliy Kulikov for (state->bucket = start; state->bucket < PING_HTABLE_SIZE; 994c319b4d7SVasiliy Kulikov ++state->bucket) { 995c319b4d7SVasiliy Kulikov struct hlist_nulls_node *node; 99675e308c8SChangli Gao struct hlist_nulls_head *hslot; 99775e308c8SChangli Gao 99875e308c8SChangli Gao hslot = &ping_table.hash[state->bucket]; 999c319b4d7SVasiliy Kulikov 1000c319b4d7SVasiliy Kulikov if (hlist_nulls_empty(hslot)) 1001c319b4d7SVasiliy Kulikov continue; 1002c319b4d7SVasiliy Kulikov 1003c319b4d7SVasiliy Kulikov sk_nulls_for_each(sk, node, hslot) { 1004c319b4d7SVasiliy Kulikov if (net_eq(sock_net(sk), net)) 1005c319b4d7SVasiliy Kulikov goto found; 1006c319b4d7SVasiliy Kulikov } 1007c319b4d7SVasiliy Kulikov } 1008c319b4d7SVasiliy Kulikov sk = NULL; 1009c319b4d7SVasiliy Kulikov found: 1010c319b4d7SVasiliy Kulikov return sk; 1011c319b4d7SVasiliy Kulikov } 1012c319b4d7SVasiliy Kulikov 1013c319b4d7SVasiliy Kulikov static struct sock *ping_get_next(struct seq_file *seq, struct sock *sk) 1014c319b4d7SVasiliy Kulikov { 1015c319b4d7SVasiliy Kulikov struct ping_iter_state *state = seq->private; 1016c319b4d7SVasiliy Kulikov struct net *net = seq_file_net(seq); 1017c319b4d7SVasiliy Kulikov 1018c319b4d7SVasiliy Kulikov do { 1019c319b4d7SVasiliy Kulikov sk = sk_nulls_next(sk); 1020c319b4d7SVasiliy Kulikov } while (sk && (!net_eq(sock_net(sk), net))); 1021c319b4d7SVasiliy Kulikov 1022c319b4d7SVasiliy Kulikov if (!sk) 1023c319b4d7SVasiliy Kulikov return ping_get_first(seq, state->bucket + 1); 1024c319b4d7SVasiliy Kulikov return sk; 1025c319b4d7SVasiliy Kulikov } 1026c319b4d7SVasiliy Kulikov 1027c319b4d7SVasiliy Kulikov static struct sock *ping_get_idx(struct seq_file *seq, loff_t pos) 1028c319b4d7SVasiliy Kulikov { 1029c319b4d7SVasiliy Kulikov struct sock *sk = ping_get_first(seq, 0); 1030c319b4d7SVasiliy Kulikov 1031c319b4d7SVasiliy Kulikov if (sk) 1032c319b4d7SVasiliy Kulikov while (pos && (sk = ping_get_next(seq, sk)) != NULL) 1033c319b4d7SVasiliy Kulikov --pos; 1034c319b4d7SVasiliy Kulikov return pos ? NULL : sk; 1035c319b4d7SVasiliy Kulikov } 1036c319b4d7SVasiliy Kulikov 1037c319b4d7SVasiliy Kulikov static void *ping_seq_start(struct seq_file *seq, loff_t *pos) 1038c319b4d7SVasiliy Kulikov { 1039c319b4d7SVasiliy Kulikov struct ping_iter_state *state = seq->private; 1040c319b4d7SVasiliy Kulikov state->bucket = 0; 1041c319b4d7SVasiliy Kulikov 1042c319b4d7SVasiliy Kulikov read_lock_bh(&ping_table.lock); 1043c319b4d7SVasiliy Kulikov 1044c319b4d7SVasiliy Kulikov return *pos ? ping_get_idx(seq, *pos-1) : SEQ_START_TOKEN; 1045c319b4d7SVasiliy Kulikov } 1046c319b4d7SVasiliy Kulikov 1047c319b4d7SVasiliy Kulikov static void *ping_seq_next(struct seq_file *seq, void *v, loff_t *pos) 1048c319b4d7SVasiliy Kulikov { 1049c319b4d7SVasiliy Kulikov struct sock *sk; 1050c319b4d7SVasiliy Kulikov 1051c319b4d7SVasiliy Kulikov if (v == SEQ_START_TOKEN) 1052c319b4d7SVasiliy Kulikov sk = ping_get_idx(seq, 0); 1053c319b4d7SVasiliy Kulikov else 1054c319b4d7SVasiliy Kulikov sk = ping_get_next(seq, v); 1055c319b4d7SVasiliy Kulikov 1056c319b4d7SVasiliy Kulikov ++*pos; 1057c319b4d7SVasiliy Kulikov return sk; 1058c319b4d7SVasiliy Kulikov } 1059c319b4d7SVasiliy Kulikov 1060c319b4d7SVasiliy Kulikov static void ping_seq_stop(struct seq_file *seq, void *v) 1061c319b4d7SVasiliy Kulikov { 1062c319b4d7SVasiliy Kulikov read_unlock_bh(&ping_table.lock); 1063c319b4d7SVasiliy Kulikov } 1064c319b4d7SVasiliy Kulikov 1065c319b4d7SVasiliy Kulikov static void ping_format_sock(struct sock *sp, struct seq_file *f, 1066c319b4d7SVasiliy Kulikov int bucket, int *len) 1067c319b4d7SVasiliy Kulikov { 1068c319b4d7SVasiliy Kulikov struct inet_sock *inet = inet_sk(sp); 1069c319b4d7SVasiliy Kulikov __be32 dest = inet->inet_daddr; 1070c319b4d7SVasiliy Kulikov __be32 src = inet->inet_rcv_saddr; 1071c319b4d7SVasiliy Kulikov __u16 destp = ntohs(inet->inet_dport); 1072c319b4d7SVasiliy Kulikov __u16 srcp = ntohs(inet->inet_sport); 1073c319b4d7SVasiliy Kulikov 1074c319b4d7SVasiliy Kulikov seq_printf(f, "%5d: %08X:%04X %08X:%04X" 1075c319b4d7SVasiliy Kulikov " %02X %08X:%08X %02X:%08lX %08X %5d %8d %lu %d %pK %d%n", 1076c319b4d7SVasiliy Kulikov bucket, src, srcp, dest, destp, sp->sk_state, 1077c319b4d7SVasiliy Kulikov sk_wmem_alloc_get(sp), 1078c319b4d7SVasiliy Kulikov sk_rmem_alloc_get(sp), 1079a7cb5a49SEric W. Biederman 0, 0L, 0, 1080a7cb5a49SEric W. Biederman from_kuid_munged(seq_user_ns(f), sock_i_uid(sp)), 1081a7cb5a49SEric W. Biederman 0, sock_i_ino(sp), 1082c319b4d7SVasiliy Kulikov atomic_read(&sp->sk_refcnt), sp, 1083c319b4d7SVasiliy Kulikov atomic_read(&sp->sk_drops), len); 1084c319b4d7SVasiliy Kulikov } 1085c319b4d7SVasiliy Kulikov 1086c319b4d7SVasiliy Kulikov static int ping_seq_show(struct seq_file *seq, void *v) 1087c319b4d7SVasiliy Kulikov { 1088c319b4d7SVasiliy Kulikov if (v == SEQ_START_TOKEN) 1089c319b4d7SVasiliy Kulikov seq_printf(seq, "%-127s\n", 1090c319b4d7SVasiliy Kulikov " sl local_address rem_address st tx_queue " 1091c319b4d7SVasiliy Kulikov "rx_queue tr tm->when retrnsmt uid timeout " 1092c319b4d7SVasiliy Kulikov "inode ref pointer drops"); 1093c319b4d7SVasiliy Kulikov else { 1094c319b4d7SVasiliy Kulikov struct ping_iter_state *state = seq->private; 1095c319b4d7SVasiliy Kulikov int len; 1096c319b4d7SVasiliy Kulikov 1097c319b4d7SVasiliy Kulikov ping_format_sock(v, seq, state->bucket, &len); 1098c319b4d7SVasiliy Kulikov seq_printf(seq, "%*s\n", 127 - len, ""); 1099c319b4d7SVasiliy Kulikov } 1100c319b4d7SVasiliy Kulikov return 0; 1101c319b4d7SVasiliy Kulikov } 1102c319b4d7SVasiliy Kulikov 1103c319b4d7SVasiliy Kulikov static const struct seq_operations ping_seq_ops = { 1104c319b4d7SVasiliy Kulikov .show = ping_seq_show, 1105c319b4d7SVasiliy Kulikov .start = ping_seq_start, 1106c319b4d7SVasiliy Kulikov .next = ping_seq_next, 1107c319b4d7SVasiliy Kulikov .stop = ping_seq_stop, 1108c319b4d7SVasiliy Kulikov }; 1109c319b4d7SVasiliy Kulikov 1110c319b4d7SVasiliy Kulikov static int ping_seq_open(struct inode *inode, struct file *file) 1111c319b4d7SVasiliy Kulikov { 1112c319b4d7SVasiliy Kulikov return seq_open_net(inode, file, &ping_seq_ops, 1113c319b4d7SVasiliy Kulikov sizeof(struct ping_iter_state)); 1114c319b4d7SVasiliy Kulikov } 1115c319b4d7SVasiliy Kulikov 1116c319b4d7SVasiliy Kulikov static const struct file_operations ping_seq_fops = { 1117c319b4d7SVasiliy Kulikov .open = ping_seq_open, 1118c319b4d7SVasiliy Kulikov .read = seq_read, 1119c319b4d7SVasiliy Kulikov .llseek = seq_lseek, 1120c319b4d7SVasiliy Kulikov .release = seq_release_net, 1121c319b4d7SVasiliy Kulikov }; 1122c319b4d7SVasiliy Kulikov 1123c319b4d7SVasiliy Kulikov static int ping_proc_register(struct net *net) 1124c319b4d7SVasiliy Kulikov { 1125c319b4d7SVasiliy Kulikov struct proc_dir_entry *p; 1126c319b4d7SVasiliy Kulikov int rc = 0; 1127c319b4d7SVasiliy Kulikov 1128d4beaa66SGao feng p = proc_create("icmp", S_IRUGO, net->proc_net, &ping_seq_fops); 1129c319b4d7SVasiliy Kulikov if (!p) 1130c319b4d7SVasiliy Kulikov rc = -ENOMEM; 1131c319b4d7SVasiliy Kulikov return rc; 1132c319b4d7SVasiliy Kulikov } 1133c319b4d7SVasiliy Kulikov 1134c319b4d7SVasiliy Kulikov static void ping_proc_unregister(struct net *net) 1135c319b4d7SVasiliy Kulikov { 1136ece31ffdSGao feng remove_proc_entry("icmp", net->proc_net); 1137c319b4d7SVasiliy Kulikov } 1138c319b4d7SVasiliy Kulikov 1139c319b4d7SVasiliy Kulikov 1140c319b4d7SVasiliy Kulikov static int __net_init ping_proc_init_net(struct net *net) 1141c319b4d7SVasiliy Kulikov { 1142c319b4d7SVasiliy Kulikov return ping_proc_register(net); 1143c319b4d7SVasiliy Kulikov } 1144c319b4d7SVasiliy Kulikov 1145c319b4d7SVasiliy Kulikov static void __net_exit ping_proc_exit_net(struct net *net) 1146c319b4d7SVasiliy Kulikov { 1147c319b4d7SVasiliy Kulikov ping_proc_unregister(net); 1148c319b4d7SVasiliy Kulikov } 1149c319b4d7SVasiliy Kulikov 1150c319b4d7SVasiliy Kulikov static struct pernet_operations ping_net_ops = { 1151c319b4d7SVasiliy Kulikov .init = ping_proc_init_net, 1152c319b4d7SVasiliy Kulikov .exit = ping_proc_exit_net, 1153c319b4d7SVasiliy Kulikov }; 1154c319b4d7SVasiliy Kulikov 1155c319b4d7SVasiliy Kulikov int __init ping_proc_init(void) 1156c319b4d7SVasiliy Kulikov { 1157c319b4d7SVasiliy Kulikov return register_pernet_subsys(&ping_net_ops); 1158c319b4d7SVasiliy Kulikov } 1159c319b4d7SVasiliy Kulikov 1160c319b4d7SVasiliy Kulikov void ping_proc_exit(void) 1161c319b4d7SVasiliy Kulikov { 1162c319b4d7SVasiliy Kulikov unregister_pernet_subsys(&ping_net_ops); 1163c319b4d7SVasiliy Kulikov } 1164c319b4d7SVasiliy Kulikov 1165c319b4d7SVasiliy Kulikov #endif 1166c319b4d7SVasiliy Kulikov 1167c319b4d7SVasiliy Kulikov void __init ping_init(void) 1168c319b4d7SVasiliy Kulikov { 1169c319b4d7SVasiliy Kulikov int i; 1170c319b4d7SVasiliy Kulikov 1171c319b4d7SVasiliy Kulikov for (i = 0; i < PING_HTABLE_SIZE; i++) 1172c319b4d7SVasiliy Kulikov INIT_HLIST_NULLS_HEAD(&ping_table.hash[i], i); 1173c319b4d7SVasiliy Kulikov rwlock_init(&ping_table.lock); 1174c319b4d7SVasiliy Kulikov } 1175