| 1 | // SPDX-License-Identifier: GPL-2.0-or-later | 
|---|
| 2 | /* | 
|---|
| 3 | * INET		An implementation of the TCP/IP protocol suite for the LINUX | 
|---|
| 4 | *		operating system.  INET is implemented using the  BSD Socket | 
|---|
| 5 | *		interface as the means of communication with the user level. | 
|---|
| 6 | * | 
|---|
| 7 | *		"Ping" sockets | 
|---|
| 8 | * | 
|---|
| 9 | * Based on ipv4/udp.c code. | 
|---|
| 10 | * | 
|---|
| 11 | * Authors:	Vasiliy Kulikov / Openwall (for Linux 2.6), | 
|---|
| 12 | *		Pavel Kankovsky (for Linux 2.4.32) | 
|---|
| 13 | * | 
|---|
| 14 | * Pavel gave all rights to bugs to Vasiliy, | 
|---|
| 15 | * none of the bugs are Pavel's now. | 
|---|
| 16 | */ | 
|---|
| 17 |  | 
|---|
| 18 | #include <linux/uaccess.h> | 
|---|
| 19 | #include <linux/types.h> | 
|---|
| 20 | #include <linux/fcntl.h> | 
|---|
| 21 | #include <linux/socket.h> | 
|---|
| 22 | #include <linux/sockios.h> | 
|---|
| 23 | #include <linux/in.h> | 
|---|
| 24 | #include <linux/errno.h> | 
|---|
| 25 | #include <linux/timer.h> | 
|---|
| 26 | #include <linux/mm.h> | 
|---|
| 27 | #include <linux/inet.h> | 
|---|
| 28 | #include <linux/netdevice.h> | 
|---|
| 29 | #include <net/snmp.h> | 
|---|
| 30 | #include <net/ip.h> | 
|---|
| 31 | #include <net/icmp.h> | 
|---|
| 32 | #include <net/protocol.h> | 
|---|
| 33 | #include <linux/skbuff.h> | 
|---|
| 34 | #include <linux/proc_fs.h> | 
|---|
| 35 | #include <linux/export.h> | 
|---|
| 36 | #include <linux/bpf-cgroup.h> | 
|---|
| 37 | #include <net/sock.h> | 
|---|
| 38 | #include <net/ping.h> | 
|---|
| 39 | #include <net/udp.h> | 
|---|
| 40 | #include <net/route.h> | 
|---|
| 41 | #include <net/inet_common.h> | 
|---|
| 42 | #include <net/checksum.h> | 
|---|
| 43 |  | 
|---|
| 44 | #if IS_ENABLED(CONFIG_IPV6) | 
|---|
| 45 | #include <linux/in6.h> | 
|---|
| 46 | #include <linux/icmpv6.h> | 
|---|
| 47 | #include <net/addrconf.h> | 
|---|
| 48 | #include <net/ipv6.h> | 
|---|
| 49 | #include <net/transp_v6.h> | 
|---|
| 50 | #endif | 
|---|
| 51 |  | 
|---|
| 52 | struct ping_table { | 
|---|
| 53 | struct hlist_head	hash[PING_HTABLE_SIZE]; | 
|---|
| 54 | spinlock_t		lock; | 
|---|
| 55 | }; | 
|---|
| 56 |  | 
|---|
| 57 | static struct ping_table ping_table; | 
|---|
| 58 | struct pingv6_ops pingv6_ops; | 
|---|
| 59 | EXPORT_IPV6_MOD_GPL(pingv6_ops); | 
|---|
| 60 |  | 
|---|
| 61 | static inline u32 ping_hashfn(const struct net *net, u32 num, u32 mask) | 
|---|
| 62 | { | 
|---|
| 63 | u32 res = (num + net_hash_mix(net)) & mask; | 
|---|
| 64 |  | 
|---|
| 65 | pr_debug( "hash(%u) = %u\n", num, res); | 
|---|
| 66 | return res; | 
|---|
| 67 | } | 
|---|
| 68 |  | 
|---|
| 69 | static inline struct hlist_head *ping_hashslot(struct ping_table *table, | 
|---|
| 70 | struct net *net, unsigned int num) | 
|---|
| 71 | { | 
|---|
| 72 | return &table->hash[ping_hashfn(net, num, PING_HTABLE_MASK)]; | 
|---|
| 73 | } | 
|---|
| 74 |  | 
|---|
| 75 | int ping_get_port(struct sock *sk, unsigned short ident) | 
|---|
| 76 | { | 
|---|
| 77 | struct net *net = sock_net(sk); | 
|---|
| 78 | struct inet_sock *isk, *isk2; | 
|---|
| 79 | struct hlist_head *hlist; | 
|---|
| 80 | struct sock *sk2 = NULL; | 
|---|
| 81 |  | 
|---|
| 82 | isk = inet_sk(sk); | 
|---|
| 83 | spin_lock(lock: &ping_table.lock); | 
|---|
| 84 | if (ident == 0) { | 
|---|
| 85 | u16 result = net->ipv4.ping_port_rover + 1; | 
|---|
| 86 | u32 i; | 
|---|
| 87 |  | 
|---|
| 88 | for (i = 0; i < (1L << 16); i++, result++) { | 
|---|
| 89 | if (!result) | 
|---|
| 90 | continue; /* avoid zero */ | 
|---|
| 91 | hlist = ping_hashslot(table: &ping_table, net, num: result); | 
|---|
| 92 | sk_for_each(sk2, hlist) { | 
|---|
| 93 | if (!net_eq(net1: sock_net(sk: sk2), net2: net)) | 
|---|
| 94 | continue; | 
|---|
| 95 | isk2 = inet_sk(sk2); | 
|---|
| 96 |  | 
|---|
| 97 | if (isk2->inet_num == result) | 
|---|
| 98 | goto next_port; | 
|---|
| 99 | } | 
|---|
| 100 |  | 
|---|
| 101 | /* found */ | 
|---|
| 102 | net->ipv4.ping_port_rover = ident = result; | 
|---|
| 103 | break; | 
|---|
| 104 | next_port: | 
|---|
| 105 | ; | 
|---|
| 106 | } | 
|---|
| 107 | if (i >= (1L << 16)) | 
|---|
| 108 | goto fail; | 
|---|
| 109 | } else { | 
|---|
| 110 | hlist = ping_hashslot(table: &ping_table, net, num: ident); | 
|---|
| 111 | sk_for_each(sk2, hlist) { | 
|---|
| 112 | if (!net_eq(net1: sock_net(sk: sk2), net2: net)) | 
|---|
| 113 | continue; | 
|---|
| 114 | isk2 = inet_sk(sk2); | 
|---|
| 115 |  | 
|---|
| 116 | /* BUG? Why is this reuse and not reuseaddr? ping.c | 
|---|
| 117 | * doesn't turn off SO_REUSEADDR, and it doesn't expect | 
|---|
| 118 | * that other ping processes can steal its packets. | 
|---|
| 119 | */ | 
|---|
| 120 | if ((isk2->inet_num == ident) && | 
|---|
| 121 | (sk2 != sk) && | 
|---|
| 122 | (!sk2->sk_reuse || !sk->sk_reuse)) | 
|---|
| 123 | goto fail; | 
|---|
| 124 | } | 
|---|
| 125 | } | 
|---|
| 126 |  | 
|---|
| 127 | pr_debug( "found port/ident = %d\n", ident); | 
|---|
| 128 | isk->inet_num = ident; | 
|---|
| 129 | if (sk_unhashed(sk)) { | 
|---|
| 130 | pr_debug( "was not hashed\n"); | 
|---|
| 131 | sk_add_node_rcu(sk, list: hlist); | 
|---|
| 132 | sock_set_flag(sk, flag: SOCK_RCU_FREE); | 
|---|
| 133 | sock_prot_inuse_add(net, prot: sk->sk_prot, val: 1); | 
|---|
| 134 | } | 
|---|
| 135 | spin_unlock(lock: &ping_table.lock); | 
|---|
| 136 | return 0; | 
|---|
| 137 |  | 
|---|
| 138 | fail: | 
|---|
| 139 | spin_unlock(lock: &ping_table.lock); | 
|---|
| 140 | return -EADDRINUSE; | 
|---|
| 141 | } | 
|---|
| 142 | EXPORT_IPV6_MOD_GPL(ping_get_port); | 
|---|
| 143 |  | 
|---|
| 144 | void ping_unhash(struct sock *sk) | 
|---|
| 145 | { | 
|---|
| 146 | struct inet_sock *isk = inet_sk(sk); | 
|---|
| 147 |  | 
|---|
| 148 | pr_debug( "ping_unhash(isk=%p,isk->num=%u)\n", isk, isk->inet_num); | 
|---|
| 149 | spin_lock(lock: &ping_table.lock); | 
|---|
| 150 | if (sk_del_node_init_rcu(sk)) { | 
|---|
| 151 | isk->inet_num = 0; | 
|---|
| 152 | isk->inet_sport = 0; | 
|---|
| 153 | sock_prot_inuse_add(net: sock_net(sk), prot: sk->sk_prot, val: -1); | 
|---|
| 154 | } | 
|---|
| 155 | spin_unlock(lock: &ping_table.lock); | 
|---|
| 156 | } | 
|---|
| 157 | EXPORT_IPV6_MOD_GPL(ping_unhash); | 
|---|
| 158 |  | 
|---|
| 159 | /* Called under rcu_read_lock() */ | 
|---|
| 160 | static struct sock *ping_lookup(struct net *net, struct sk_buff *skb, u16 ident) | 
|---|
| 161 | { | 
|---|
| 162 | struct hlist_head *hslot = ping_hashslot(table: &ping_table, net, num: ident); | 
|---|
| 163 | struct sock *sk = NULL; | 
|---|
| 164 | struct inet_sock *isk; | 
|---|
| 165 | int dif, sdif; | 
|---|
| 166 |  | 
|---|
| 167 | if (skb->protocol == htons(ETH_P_IP)) { | 
|---|
| 168 | dif = inet_iif(skb); | 
|---|
| 169 | sdif = inet_sdif(skb); | 
|---|
| 170 | pr_debug( "try to find: num = %d, daddr = %pI4, dif = %d\n", | 
|---|
| 171 | (int)ident, &ip_hdr(skb)->daddr, dif); | 
|---|
| 172 | #if IS_ENABLED(CONFIG_IPV6) | 
|---|
| 173 | } else if (skb->protocol == htons(ETH_P_IPV6)) { | 
|---|
| 174 | dif = inet6_iif(skb); | 
|---|
| 175 | sdif = inet6_sdif(skb); | 
|---|
| 176 | pr_debug( "try to find: num = %d, daddr = %pI6c, dif = %d\n", | 
|---|
| 177 | (int)ident, &ipv6_hdr(skb)->daddr, dif); | 
|---|
| 178 | #endif | 
|---|
| 179 | } else { | 
|---|
| 180 | return NULL; | 
|---|
| 181 | } | 
|---|
| 182 |  | 
|---|
| 183 | sk_for_each_rcu(sk, hslot) { | 
|---|
| 184 | if (!net_eq(net1: sock_net(sk), net2: net)) | 
|---|
| 185 | continue; | 
|---|
| 186 | isk = inet_sk(sk); | 
|---|
| 187 |  | 
|---|
| 188 | pr_debug( "iterate\n"); | 
|---|
| 189 | if (isk->inet_num != ident) | 
|---|
| 190 | continue; | 
|---|
| 191 |  | 
|---|
| 192 | if (skb->protocol == htons(ETH_P_IP) && | 
|---|
| 193 | sk->sk_family == AF_INET) { | 
|---|
| 194 | pr_debug( "found: %p: num=%d, daddr=%pI4, dif=%d\n", sk, | 
|---|
| 195 | (int) isk->inet_num, &isk->inet_rcv_saddr, | 
|---|
| 196 | sk->sk_bound_dev_if); | 
|---|
| 197 |  | 
|---|
| 198 | if (isk->inet_rcv_saddr && | 
|---|
| 199 | isk->inet_rcv_saddr != ip_hdr(skb)->daddr) | 
|---|
| 200 | continue; | 
|---|
| 201 | #if IS_ENABLED(CONFIG_IPV6) | 
|---|
| 202 | } else if (skb->protocol == htons(ETH_P_IPV6) && | 
|---|
| 203 | sk->sk_family == AF_INET6) { | 
|---|
| 204 |  | 
|---|
| 205 | pr_debug( "found: %p: num=%d, daddr=%pI6c, dif=%d\n", sk, | 
|---|
| 206 | (int) isk->inet_num, | 
|---|
| 207 | &sk->sk_v6_rcv_saddr, | 
|---|
| 208 | sk->sk_bound_dev_if); | 
|---|
| 209 |  | 
|---|
| 210 | if (!ipv6_addr_any(a: &sk->sk_v6_rcv_saddr) && | 
|---|
| 211 | !ipv6_addr_equal(a1: &sk->sk_v6_rcv_saddr, | 
|---|
| 212 | a2: &ipv6_hdr(skb)->daddr)) | 
|---|
| 213 | continue; | 
|---|
| 214 | #endif | 
|---|
| 215 | } else { | 
|---|
| 216 | continue; | 
|---|
| 217 | } | 
|---|
| 218 |  | 
|---|
| 219 | if (sk->sk_bound_dev_if && sk->sk_bound_dev_if != dif && | 
|---|
| 220 | sk->sk_bound_dev_if != sdif) | 
|---|
| 221 | continue; | 
|---|
| 222 |  | 
|---|
| 223 | goto exit; | 
|---|
| 224 | } | 
|---|
| 225 |  | 
|---|
| 226 | sk = NULL; | 
|---|
| 227 | exit: | 
|---|
| 228 |  | 
|---|
| 229 | return sk; | 
|---|
| 230 | } | 
|---|
| 231 |  | 
|---|
| 232 | static void inet_get_ping_group_range_net(struct net *net, kgid_t *low, | 
|---|
| 233 | kgid_t *high) | 
|---|
| 234 | { | 
|---|
| 235 | kgid_t *data = net->ipv4.ping_group_range.range; | 
|---|
| 236 | unsigned int seq; | 
|---|
| 237 |  | 
|---|
| 238 | do { | 
|---|
| 239 | seq = read_seqbegin(sl: &net->ipv4.ping_group_range.lock); | 
|---|
| 240 |  | 
|---|
| 241 | *low = data[0]; | 
|---|
| 242 | *high = data[1]; | 
|---|
| 243 | } while (read_seqretry(sl: &net->ipv4.ping_group_range.lock, start: seq)); | 
|---|
| 244 | } | 
|---|
| 245 |  | 
|---|
| 246 |  | 
|---|
| 247 | int ping_init_sock(struct sock *sk) | 
|---|
| 248 | { | 
|---|
| 249 | struct net *net = sock_net(sk); | 
|---|
| 250 | kgid_t group = current_egid(); | 
|---|
| 251 | struct group_info *group_info; | 
|---|
| 252 | int i; | 
|---|
| 253 | kgid_t low, high; | 
|---|
| 254 | int ret = 0; | 
|---|
| 255 |  | 
|---|
| 256 | if (sk->sk_family == AF_INET6) | 
|---|
| 257 | sk->sk_ipv6only = 1; | 
|---|
| 258 |  | 
|---|
| 259 | inet_get_ping_group_range_net(net, low: &low, high: &high); | 
|---|
| 260 | if (gid_lte(left: low, right: group) && gid_lte(left: group, right: high)) | 
|---|
| 261 | return 0; | 
|---|
| 262 |  | 
|---|
| 263 | group_info = get_current_groups(); | 
|---|
| 264 | for (i = 0; i < group_info->ngroups; i++) { | 
|---|
| 265 | kgid_t gid = group_info->gid[i]; | 
|---|
| 266 |  | 
|---|
| 267 | if (gid_lte(left: low, right: gid) && gid_lte(left: gid, right: high)) | 
|---|
| 268 | goto out_release_group; | 
|---|
| 269 | } | 
|---|
| 270 |  | 
|---|
| 271 | ret = -EACCES; | 
|---|
| 272 |  | 
|---|
| 273 | out_release_group: | 
|---|
| 274 | put_group_info(group_info); | 
|---|
| 275 | return ret; | 
|---|
| 276 | } | 
|---|
| 277 | EXPORT_IPV6_MOD_GPL(ping_init_sock); | 
|---|
| 278 |  | 
|---|
| 279 | void ping_close(struct sock *sk, long timeout) | 
|---|
| 280 | { | 
|---|
| 281 | pr_debug( "ping_close(sk=%p,sk->num=%u)\n", | 
|---|
| 282 | inet_sk(sk), inet_sk(sk)->inet_num); | 
|---|
| 283 | pr_debug( "isk->refcnt = %d\n", refcount_read(&sk->sk_refcnt)); | 
|---|
| 284 |  | 
|---|
| 285 | sk_common_release(sk); | 
|---|
| 286 | } | 
|---|
| 287 | EXPORT_IPV6_MOD_GPL(ping_close); | 
|---|
| 288 |  | 
|---|
| 289 | static int ping_pre_connect(struct sock *sk, struct sockaddr *uaddr, | 
|---|
| 290 | int addr_len) | 
|---|
| 291 | { | 
|---|
| 292 | /* This check is replicated from __ip4_datagram_connect() and | 
|---|
| 293 | * intended to prevent BPF program called below from accessing bytes | 
|---|
| 294 | * that are out of the bound specified by user in addr_len. | 
|---|
| 295 | */ | 
|---|
| 296 | if (addr_len < sizeof(struct sockaddr_in)) | 
|---|
| 297 | return -EINVAL; | 
|---|
| 298 |  | 
|---|
| 299 | return BPF_CGROUP_RUN_PROG_INET4_CONNECT_LOCK(sk, uaddr, &addr_len); | 
|---|
| 300 | } | 
|---|
| 301 |  | 
|---|
| 302 | /* Checks the bind address and possibly modifies sk->sk_bound_dev_if. */ | 
|---|
| 303 | static int ping_check_bind_addr(struct sock *sk, struct inet_sock *isk, | 
|---|
| 304 | struct sockaddr *uaddr, int addr_len) | 
|---|
| 305 | { | 
|---|
| 306 | struct net *net = sock_net(sk); | 
|---|
| 307 | if (sk->sk_family == AF_INET) { | 
|---|
| 308 | struct sockaddr_in *addr = (struct sockaddr_in *) uaddr; | 
|---|
| 309 | u32 tb_id = RT_TABLE_LOCAL; | 
|---|
| 310 | int chk_addr_ret; | 
|---|
| 311 |  | 
|---|
| 312 | if (addr_len < sizeof(*addr)) | 
|---|
| 313 | return -EINVAL; | 
|---|
| 314 |  | 
|---|
| 315 | if (addr->sin_family != AF_INET && | 
|---|
| 316 | !(addr->sin_family == AF_UNSPEC && | 
|---|
| 317 | addr->sin_addr.s_addr == htonl(INADDR_ANY))) | 
|---|
| 318 | return -EAFNOSUPPORT; | 
|---|
| 319 |  | 
|---|
| 320 | pr_debug( "ping_check_bind_addr(sk=%p,addr=%pI4,port=%d)\n", | 
|---|
| 321 | sk, &addr->sin_addr.s_addr, ntohs(addr->sin_port)); | 
|---|
| 322 |  | 
|---|
| 323 | if (addr->sin_addr.s_addr == htonl(INADDR_ANY)) | 
|---|
| 324 | return 0; | 
|---|
| 325 |  | 
|---|
| 326 | tb_id = l3mdev_fib_table_by_index(net, ifindex: sk->sk_bound_dev_if) ? : tb_id; | 
|---|
| 327 | chk_addr_ret = inet_addr_type_table(net, addr: addr->sin_addr.s_addr, tb_id); | 
|---|
| 328 |  | 
|---|
| 329 | if (chk_addr_ret == RTN_MULTICAST || | 
|---|
| 330 | chk_addr_ret == RTN_BROADCAST || | 
|---|
| 331 | (chk_addr_ret != RTN_LOCAL && | 
|---|
| 332 | !inet_can_nonlocal_bind(net, inet: isk))) | 
|---|
| 333 | return -EADDRNOTAVAIL; | 
|---|
| 334 |  | 
|---|
| 335 | #if IS_ENABLED(CONFIG_IPV6) | 
|---|
| 336 | } else if (sk->sk_family == AF_INET6) { | 
|---|
| 337 | struct sockaddr_in6 *addr = (struct sockaddr_in6 *) uaddr; | 
|---|
| 338 | int addr_type, scoped, has_addr; | 
|---|
| 339 | struct net_device *dev = NULL; | 
|---|
| 340 |  | 
|---|
| 341 | if (addr_len < sizeof(*addr)) | 
|---|
| 342 | return -EINVAL; | 
|---|
| 343 |  | 
|---|
| 344 | if (addr->sin6_family != AF_INET6) | 
|---|
| 345 | return -EAFNOSUPPORT; | 
|---|
| 346 |  | 
|---|
| 347 | pr_debug( "ping_check_bind_addr(sk=%p,addr=%pI6c,port=%d)\n", | 
|---|
| 348 | sk, addr->sin6_addr.s6_addr, ntohs(addr->sin6_port)); | 
|---|
| 349 |  | 
|---|
| 350 | addr_type = ipv6_addr_type(addr: &addr->sin6_addr); | 
|---|
| 351 | scoped = __ipv6_addr_needs_scope_id(type: addr_type); | 
|---|
| 352 | if ((addr_type != IPV6_ADDR_ANY && | 
|---|
| 353 | !(addr_type & IPV6_ADDR_UNICAST)) || | 
|---|
| 354 | (scoped && !addr->sin6_scope_id)) | 
|---|
| 355 | return -EINVAL; | 
|---|
| 356 |  | 
|---|
| 357 | rcu_read_lock(); | 
|---|
| 358 | if (addr->sin6_scope_id) { | 
|---|
| 359 | dev = dev_get_by_index_rcu(net, ifindex: addr->sin6_scope_id); | 
|---|
| 360 | if (!dev) { | 
|---|
| 361 | rcu_read_unlock(); | 
|---|
| 362 | return -ENODEV; | 
|---|
| 363 | } | 
|---|
| 364 | } | 
|---|
| 365 |  | 
|---|
| 366 | if (!dev && sk->sk_bound_dev_if) { | 
|---|
| 367 | dev = dev_get_by_index_rcu(net, ifindex: sk->sk_bound_dev_if); | 
|---|
| 368 | if (!dev) { | 
|---|
| 369 | rcu_read_unlock(); | 
|---|
| 370 | return -ENODEV; | 
|---|
| 371 | } | 
|---|
| 372 | } | 
|---|
| 373 | has_addr = pingv6_ops.ipv6_chk_addr(net, &addr->sin6_addr, dev, | 
|---|
| 374 | scoped); | 
|---|
| 375 | rcu_read_unlock(); | 
|---|
| 376 |  | 
|---|
| 377 | if (!(ipv6_can_nonlocal_bind(net, inet: isk) || has_addr || | 
|---|
| 378 | addr_type == IPV6_ADDR_ANY)) | 
|---|
| 379 | return -EADDRNOTAVAIL; | 
|---|
| 380 |  | 
|---|
| 381 | if (scoped) | 
|---|
| 382 | sk->sk_bound_dev_if = addr->sin6_scope_id; | 
|---|
| 383 | #endif | 
|---|
| 384 | } else { | 
|---|
| 385 | return -EAFNOSUPPORT; | 
|---|
| 386 | } | 
|---|
| 387 | return 0; | 
|---|
| 388 | } | 
|---|
| 389 |  | 
|---|
| 390 | static void ping_set_saddr(struct sock *sk, struct sockaddr *saddr) | 
|---|
| 391 | { | 
|---|
| 392 | if (saddr->sa_family == AF_INET) { | 
|---|
| 393 | struct inet_sock *isk = inet_sk(sk); | 
|---|
| 394 | struct sockaddr_in *addr = (struct sockaddr_in *) saddr; | 
|---|
| 395 | isk->inet_rcv_saddr = isk->inet_saddr = addr->sin_addr.s_addr; | 
|---|
| 396 | #if IS_ENABLED(CONFIG_IPV6) | 
|---|
| 397 | } else if (saddr->sa_family == AF_INET6) { | 
|---|
| 398 | struct sockaddr_in6 *addr = (struct sockaddr_in6 *) saddr; | 
|---|
| 399 | struct ipv6_pinfo *np = inet6_sk(sk: sk); | 
|---|
| 400 | sk->sk_v6_rcv_saddr = np->saddr = addr->sin6_addr; | 
|---|
| 401 | #endif | 
|---|
| 402 | } | 
|---|
| 403 | } | 
|---|
| 404 |  | 
|---|
| 405 | /* | 
|---|
| 406 | * We need our own bind because there are no privileged id's == local ports. | 
|---|
| 407 | * Moreover, we don't allow binding to multi- and broadcast addresses. | 
|---|
| 408 | */ | 
|---|
| 409 |  | 
|---|
| 410 | int ping_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len) | 
|---|
| 411 | { | 
|---|
| 412 | struct inet_sock *isk = inet_sk(sk); | 
|---|
| 413 | unsigned short snum; | 
|---|
| 414 | int err; | 
|---|
| 415 | int dif = sk->sk_bound_dev_if; | 
|---|
| 416 |  | 
|---|
| 417 | err = ping_check_bind_addr(sk, isk, uaddr, addr_len); | 
|---|
| 418 | if (err) | 
|---|
| 419 | return err; | 
|---|
| 420 |  | 
|---|
| 421 | lock_sock(sk); | 
|---|
| 422 |  | 
|---|
| 423 | err = -EINVAL; | 
|---|
| 424 | if (isk->inet_num != 0) | 
|---|
| 425 | goto out; | 
|---|
| 426 |  | 
|---|
| 427 | err = -EADDRINUSE; | 
|---|
| 428 | snum = ntohs(((struct sockaddr_in *)uaddr)->sin_port); | 
|---|
| 429 | if (ping_get_port(sk, ident: snum) != 0) { | 
|---|
| 430 | /* Restore possibly modified sk->sk_bound_dev_if by ping_check_bind_addr(). */ | 
|---|
| 431 | sk->sk_bound_dev_if = dif; | 
|---|
| 432 | goto out; | 
|---|
| 433 | } | 
|---|
| 434 | ping_set_saddr(sk, saddr: uaddr); | 
|---|
| 435 |  | 
|---|
| 436 | pr_debug( "after bind(): num = %hu, dif = %d\n", | 
|---|
| 437 | isk->inet_num, | 
|---|
| 438 | sk->sk_bound_dev_if); | 
|---|
| 439 |  | 
|---|
| 440 | err = 0; | 
|---|
| 441 | if (sk->sk_family == AF_INET && isk->inet_rcv_saddr) | 
|---|
| 442 | sk->sk_userlocks |= SOCK_BINDADDR_LOCK; | 
|---|
| 443 | #if IS_ENABLED(CONFIG_IPV6) | 
|---|
| 444 | if (sk->sk_family == AF_INET6 && !ipv6_addr_any(a: &sk->sk_v6_rcv_saddr)) | 
|---|
| 445 | sk->sk_userlocks |= SOCK_BINDADDR_LOCK; | 
|---|
| 446 | #endif | 
|---|
| 447 |  | 
|---|
| 448 | if (snum) | 
|---|
| 449 | sk->sk_userlocks |= SOCK_BINDPORT_LOCK; | 
|---|
| 450 | isk->inet_sport = htons(isk->inet_num); | 
|---|
| 451 | isk->inet_daddr = 0; | 
|---|
| 452 | isk->inet_dport = 0; | 
|---|
| 453 |  | 
|---|
| 454 | #if IS_ENABLED(CONFIG_IPV6) | 
|---|
| 455 | if (sk->sk_family == AF_INET6) | 
|---|
| 456 | memset(s: &sk->sk_v6_daddr, c: 0, n: sizeof(sk->sk_v6_daddr)); | 
|---|
| 457 | #endif | 
|---|
| 458 |  | 
|---|
| 459 | sk_dst_reset(sk); | 
|---|
| 460 | out: | 
|---|
| 461 | release_sock(sk); | 
|---|
| 462 | pr_debug( "ping_v4_bind -> %d\n", err); | 
|---|
| 463 | return err; | 
|---|
| 464 | } | 
|---|
| 465 | EXPORT_IPV6_MOD_GPL(ping_bind); | 
|---|
| 466 |  | 
|---|
| 467 | /* | 
|---|
| 468 | * Is this a supported type of ICMP message? | 
|---|
| 469 | */ | 
|---|
| 470 |  | 
|---|
| 471 | static inline int ping_supported(int family, int type, int code) | 
|---|
| 472 | { | 
|---|
| 473 | return (family == AF_INET && type == ICMP_ECHO && code == 0) || | 
|---|
| 474 | (family == AF_INET && type == ICMP_EXT_ECHO && code == 0) || | 
|---|
| 475 | (family == AF_INET6 && type == ICMPV6_ECHO_REQUEST && code == 0) || | 
|---|
| 476 | (family == AF_INET6 && type == ICMPV6_EXT_ECHO_REQUEST && code == 0); | 
|---|
| 477 | } | 
|---|
| 478 |  | 
|---|
| 479 | /* | 
|---|
| 480 | * This routine is called by the ICMP module when it gets some | 
|---|
| 481 | * sort of error condition. | 
|---|
| 482 | */ | 
|---|
| 483 |  | 
|---|
| 484 | void ping_err(struct sk_buff *skb, int offset, u32 info) | 
|---|
| 485 | { | 
|---|
| 486 | int family; | 
|---|
| 487 | struct icmphdr *icmph; | 
|---|
| 488 | struct inet_sock *inet_sock; | 
|---|
| 489 | int type; | 
|---|
| 490 | int code; | 
|---|
| 491 | struct net *net = dev_net(dev: skb->dev); | 
|---|
| 492 | struct sock *sk; | 
|---|
| 493 | int harderr; | 
|---|
| 494 | int err; | 
|---|
| 495 |  | 
|---|
| 496 | if (skb->protocol == htons(ETH_P_IP)) { | 
|---|
| 497 | family = AF_INET; | 
|---|
| 498 | type = icmp_hdr(skb)->type; | 
|---|
| 499 | code = icmp_hdr(skb)->code; | 
|---|
| 500 | icmph = (struct icmphdr *)(skb->data + offset); | 
|---|
| 501 | } else if (skb->protocol == htons(ETH_P_IPV6)) { | 
|---|
| 502 | family = AF_INET6; | 
|---|
| 503 | type = icmp6_hdr(skb)->icmp6_type; | 
|---|
| 504 | code = icmp6_hdr(skb)->icmp6_code; | 
|---|
| 505 | icmph = (struct icmphdr *) (skb->data + offset); | 
|---|
| 506 | } else { | 
|---|
| 507 | BUG(); | 
|---|
| 508 | } | 
|---|
| 509 |  | 
|---|
| 510 | /* We assume the packet has already been checked by icmp_unreach */ | 
|---|
| 511 |  | 
|---|
| 512 | if (!ping_supported(family, type: icmph->type, code: icmph->code)) | 
|---|
| 513 | return; | 
|---|
| 514 |  | 
|---|
| 515 | pr_debug( "ping_err(proto=0x%x,type=%d,code=%d,id=%04x,seq=%04x)\n", | 
|---|
| 516 | skb->protocol, type, code, ntohs(icmph->un.echo.id), | 
|---|
| 517 | ntohs(icmph->un.echo.sequence)); | 
|---|
| 518 |  | 
|---|
| 519 | sk = ping_lookup(net, skb, ntohs(icmph->un.echo.id)); | 
|---|
| 520 | if (!sk) { | 
|---|
| 521 | pr_debug( "no socket, dropping\n"); | 
|---|
| 522 | return;	/* No socket for error */ | 
|---|
| 523 | } | 
|---|
| 524 | pr_debug( "err on socket %p\n", sk); | 
|---|
| 525 |  | 
|---|
| 526 | err = 0; | 
|---|
| 527 | harderr = 0; | 
|---|
| 528 | inet_sock = inet_sk(sk); | 
|---|
| 529 |  | 
|---|
| 530 | if (skb->protocol == htons(ETH_P_IP)) { | 
|---|
| 531 | switch (type) { | 
|---|
| 532 | default: | 
|---|
| 533 | case ICMP_TIME_EXCEEDED: | 
|---|
| 534 | err = EHOSTUNREACH; | 
|---|
| 535 | break; | 
|---|
| 536 | case ICMP_SOURCE_QUENCH: | 
|---|
| 537 | /* This is not a real error but ping wants to see it. | 
|---|
| 538 | * Report it with some fake errno. | 
|---|
| 539 | */ | 
|---|
| 540 | err = EREMOTEIO; | 
|---|
| 541 | break; | 
|---|
| 542 | case ICMP_PARAMETERPROB: | 
|---|
| 543 | err = EPROTO; | 
|---|
| 544 | harderr = 1; | 
|---|
| 545 | break; | 
|---|
| 546 | case ICMP_DEST_UNREACH: | 
|---|
| 547 | if (code == ICMP_FRAG_NEEDED) { /* Path MTU discovery */ | 
|---|
| 548 | ipv4_sk_update_pmtu(skb, sk, mtu: info); | 
|---|
| 549 | if (READ_ONCE(inet_sock->pmtudisc) != IP_PMTUDISC_DONT) { | 
|---|
| 550 | err = EMSGSIZE; | 
|---|
| 551 | harderr = 1; | 
|---|
| 552 | break; | 
|---|
| 553 | } | 
|---|
| 554 | goto out; | 
|---|
| 555 | } | 
|---|
| 556 | err = EHOSTUNREACH; | 
|---|
| 557 | if (code <= NR_ICMP_UNREACH) { | 
|---|
| 558 | harderr = icmp_err_convert[code].fatal; | 
|---|
| 559 | err = icmp_err_convert[code].errno; | 
|---|
| 560 | } | 
|---|
| 561 | break; | 
|---|
| 562 | case ICMP_REDIRECT: | 
|---|
| 563 | /* See ICMP_SOURCE_QUENCH */ | 
|---|
| 564 | ipv4_sk_redirect(skb, sk); | 
|---|
| 565 | err = EREMOTEIO; | 
|---|
| 566 | break; | 
|---|
| 567 | } | 
|---|
| 568 | #if IS_ENABLED(CONFIG_IPV6) | 
|---|
| 569 | } else if (skb->protocol == htons(ETH_P_IPV6)) { | 
|---|
| 570 | harderr = pingv6_ops.icmpv6_err_convert(type, code, &err); | 
|---|
| 571 | #endif | 
|---|
| 572 | } | 
|---|
| 573 |  | 
|---|
| 574 | /* | 
|---|
| 575 | *      RFC1122: OK.  Passes ICMP errors back to application, as per | 
|---|
| 576 | *	4.1.3.3. | 
|---|
| 577 | */ | 
|---|
| 578 | if ((family == AF_INET && !inet_test_bit(RECVERR, sk)) || | 
|---|
| 579 | (family == AF_INET6 && !inet6_test_bit(RECVERR6, sk))) { | 
|---|
| 580 | if (!harderr || sk->sk_state != TCP_ESTABLISHED) | 
|---|
| 581 | goto out; | 
|---|
| 582 | } else { | 
|---|
| 583 | if (family == AF_INET) { | 
|---|
| 584 | ip_icmp_error(sk, skb, err, port: 0 /* no remote port */, | 
|---|
| 585 | info, payload: (u8 *)icmph); | 
|---|
| 586 | #if IS_ENABLED(CONFIG_IPV6) | 
|---|
| 587 | } else if (family == AF_INET6) { | 
|---|
| 588 | pingv6_ops.ipv6_icmp_error(sk, skb, err, 0, | 
|---|
| 589 | info, (u8 *)icmph); | 
|---|
| 590 | #endif | 
|---|
| 591 | } | 
|---|
| 592 | } | 
|---|
| 593 | sk->sk_err = err; | 
|---|
| 594 | sk_error_report(sk); | 
|---|
| 595 | out: | 
|---|
| 596 | return; | 
|---|
| 597 | } | 
|---|
| 598 | EXPORT_IPV6_MOD_GPL(ping_err); | 
|---|
| 599 |  | 
|---|
| 600 | /* | 
|---|
| 601 | *	Copy and checksum an ICMP Echo packet from user space into a buffer | 
|---|
| 602 | *	starting from the payload. | 
|---|
| 603 | */ | 
|---|
| 604 |  | 
|---|
| 605 | int ping_getfrag(void *from, char *to, | 
|---|
| 606 | int offset, int fraglen, int odd, struct sk_buff *skb) | 
|---|
| 607 | { | 
|---|
| 608 | struct pingfakehdr *pfh = from; | 
|---|
| 609 |  | 
|---|
| 610 | if (!csum_and_copy_from_iter_full(addr: to, bytes: fraglen, csum: &pfh->wcheck, | 
|---|
| 611 | i: &pfh->msg->msg_iter)) | 
|---|
| 612 | return -EFAULT; | 
|---|
| 613 |  | 
|---|
| 614 | #if IS_ENABLED(CONFIG_IPV6) | 
|---|
| 615 | /* For IPv6, checksum each skb as we go along, as expected by | 
|---|
| 616 | * icmpv6_push_pending_frames. For IPv4, accumulate the checksum in | 
|---|
| 617 | * wcheck, it will be finalized in ping_v4_push_pending_frames. | 
|---|
| 618 | */ | 
|---|
| 619 | if (pfh->family == AF_INET6) { | 
|---|
| 620 | skb->csum = csum_block_add(csum: skb->csum, csum2: pfh->wcheck, offset: odd); | 
|---|
| 621 | skb->ip_summed = CHECKSUM_NONE; | 
|---|
| 622 | pfh->wcheck = 0; | 
|---|
| 623 | } | 
|---|
| 624 | #endif | 
|---|
| 625 |  | 
|---|
| 626 | return 0; | 
|---|
| 627 | } | 
|---|
| 628 | EXPORT_IPV6_MOD_GPL(ping_getfrag); | 
|---|
| 629 |  | 
|---|
| 630 | static int ping_v4_push_pending_frames(struct sock *sk, struct pingfakehdr *pfh, | 
|---|
| 631 | struct flowi4 *fl4) | 
|---|
| 632 | { | 
|---|
| 633 | struct sk_buff *skb = skb_peek(list_: &sk->sk_write_queue); | 
|---|
| 634 |  | 
|---|
| 635 | if (!skb) | 
|---|
| 636 | return 0; | 
|---|
| 637 | pfh->wcheck = csum_partial(buff: (char *)&pfh->icmph, | 
|---|
| 638 | len: sizeof(struct icmphdr), sum: pfh->wcheck); | 
|---|
| 639 | pfh->icmph.checksum = csum_fold(sum: pfh->wcheck); | 
|---|
| 640 | memcpy(to: icmp_hdr(skb), from: &pfh->icmph, len: sizeof(struct icmphdr)); | 
|---|
| 641 | skb->ip_summed = CHECKSUM_NONE; | 
|---|
| 642 | return ip_push_pending_frames(sk, fl4); | 
|---|
| 643 | } | 
|---|
| 644 |  | 
|---|
| 645 | int ping_common_sendmsg(int family, struct msghdr *msg, size_t len, | 
|---|
| 646 | void *user_icmph, size_t icmph_len) | 
|---|
| 647 | { | 
|---|
| 648 | u8 type, code; | 
|---|
| 649 |  | 
|---|
| 650 | if (len > 0xFFFF) | 
|---|
| 651 | return -EMSGSIZE; | 
|---|
| 652 |  | 
|---|
| 653 | /* Must have at least a full ICMP header. */ | 
|---|
| 654 | if (len < icmph_len) | 
|---|
| 655 | return -EINVAL; | 
|---|
| 656 |  | 
|---|
| 657 | /* | 
|---|
| 658 | *	Check the flags. | 
|---|
| 659 | */ | 
|---|
| 660 |  | 
|---|
| 661 | /* Mirror BSD error message compatibility */ | 
|---|
| 662 | if (msg->msg_flags & MSG_OOB) | 
|---|
| 663 | return -EOPNOTSUPP; | 
|---|
| 664 |  | 
|---|
| 665 | /* | 
|---|
| 666 | *	Fetch the ICMP header provided by the userland. | 
|---|
| 667 | *	iovec is modified! The ICMP header is consumed. | 
|---|
| 668 | */ | 
|---|
| 669 | if (memcpy_from_msg(data: user_icmph, msg, len: icmph_len)) | 
|---|
| 670 | return -EFAULT; | 
|---|
| 671 |  | 
|---|
| 672 | if (family == AF_INET) { | 
|---|
| 673 | type = ((struct icmphdr *) user_icmph)->type; | 
|---|
| 674 | code = ((struct icmphdr *) user_icmph)->code; | 
|---|
| 675 | #if IS_ENABLED(CONFIG_IPV6) | 
|---|
| 676 | } else if (family == AF_INET6) { | 
|---|
| 677 | type = ((struct icmp6hdr *) user_icmph)->icmp6_type; | 
|---|
| 678 | code = ((struct icmp6hdr *) user_icmph)->icmp6_code; | 
|---|
| 679 | #endif | 
|---|
| 680 | } else { | 
|---|
| 681 | BUG(); | 
|---|
| 682 | } | 
|---|
| 683 |  | 
|---|
| 684 | if (!ping_supported(family, type, code)) | 
|---|
| 685 | return -EINVAL; | 
|---|
| 686 |  | 
|---|
| 687 | return 0; | 
|---|
| 688 | } | 
|---|
| 689 | EXPORT_IPV6_MOD_GPL(ping_common_sendmsg); | 
|---|
| 690 |  | 
|---|
| 691 | static int ping_v4_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) | 
|---|
| 692 | { | 
|---|
| 693 | struct net *net = sock_net(sk); | 
|---|
| 694 | struct flowi4 fl4; | 
|---|
| 695 | struct inet_sock *inet = inet_sk(sk); | 
|---|
| 696 | struct ipcm_cookie ipc; | 
|---|
| 697 | struct icmphdr user_icmph; | 
|---|
| 698 | struct pingfakehdr pfh; | 
|---|
| 699 | struct rtable *rt = NULL; | 
|---|
| 700 | struct ip_options_data opt_copy; | 
|---|
| 701 | int free = 0; | 
|---|
| 702 | __be32 saddr, daddr, faddr; | 
|---|
| 703 | u8 scope; | 
|---|
| 704 | int err; | 
|---|
| 705 |  | 
|---|
| 706 | pr_debug( "ping_v4_sendmsg(sk=%p,sk->num=%u)\n", inet, inet->inet_num); | 
|---|
| 707 |  | 
|---|
| 708 | err = ping_common_sendmsg(AF_INET, msg, len, user_icmph: &user_icmph, | 
|---|
| 709 | icmph_len: sizeof(user_icmph)); | 
|---|
| 710 | if (err) | 
|---|
| 711 | return err; | 
|---|
| 712 |  | 
|---|
| 713 | /* | 
|---|
| 714 | *	Get and verify the address. | 
|---|
| 715 | */ | 
|---|
| 716 |  | 
|---|
| 717 | if (msg->msg_name) { | 
|---|
| 718 | DECLARE_SOCKADDR(struct sockaddr_in *, usin, msg->msg_name); | 
|---|
| 719 | if (msg->msg_namelen < sizeof(*usin)) | 
|---|
| 720 | return -EINVAL; | 
|---|
| 721 | if (usin->sin_family != AF_INET) | 
|---|
| 722 | return -EAFNOSUPPORT; | 
|---|
| 723 | daddr = usin->sin_addr.s_addr; | 
|---|
| 724 | /* no remote port */ | 
|---|
| 725 | } else { | 
|---|
| 726 | if (sk->sk_state != TCP_ESTABLISHED) | 
|---|
| 727 | return -EDESTADDRREQ; | 
|---|
| 728 | daddr = inet->inet_daddr; | 
|---|
| 729 | /* no remote port */ | 
|---|
| 730 | } | 
|---|
| 731 |  | 
|---|
| 732 | ipcm_init_sk(ipcm: &ipc, inet); | 
|---|
| 733 |  | 
|---|
| 734 | if (msg->msg_controllen) { | 
|---|
| 735 | err = ip_cmsg_send(sk, msg, ipc: &ipc, allow_ipv6: false); | 
|---|
| 736 | if (unlikely(err)) { | 
|---|
| 737 | kfree(objp: ipc.opt); | 
|---|
| 738 | return err; | 
|---|
| 739 | } | 
|---|
| 740 | if (ipc.opt) | 
|---|
| 741 | free = 1; | 
|---|
| 742 | } | 
|---|
| 743 | if (!ipc.opt) { | 
|---|
| 744 | struct ip_options_rcu *inet_opt; | 
|---|
| 745 |  | 
|---|
| 746 | rcu_read_lock(); | 
|---|
| 747 | inet_opt = rcu_dereference(inet->inet_opt); | 
|---|
| 748 | if (inet_opt) { | 
|---|
| 749 | memcpy(to: &opt_copy, from: inet_opt, | 
|---|
| 750 | len: sizeof(*inet_opt) + inet_opt->opt.optlen); | 
|---|
| 751 | ipc.opt = &opt_copy.opt; | 
|---|
| 752 | } | 
|---|
| 753 | rcu_read_unlock(); | 
|---|
| 754 | } | 
|---|
| 755 |  | 
|---|
| 756 | saddr = ipc.addr; | 
|---|
| 757 | ipc.addr = faddr = daddr; | 
|---|
| 758 |  | 
|---|
| 759 | if (ipc.opt && ipc.opt->opt.srr) { | 
|---|
| 760 | if (!daddr) { | 
|---|
| 761 | err = -EINVAL; | 
|---|
| 762 | goto out_free; | 
|---|
| 763 | } | 
|---|
| 764 | faddr = ipc.opt->opt.faddr; | 
|---|
| 765 | } | 
|---|
| 766 | scope = ip_sendmsg_scope(inet, ipc: &ipc, msg); | 
|---|
| 767 |  | 
|---|
| 768 | if (ipv4_is_multicast(addr: daddr)) { | 
|---|
| 769 | if (!ipc.oif || netif_index_is_l3_master(net: sock_net(sk), ifindex: ipc.oif)) | 
|---|
| 770 | ipc.oif = READ_ONCE(inet->mc_index); | 
|---|
| 771 | if (!saddr) | 
|---|
| 772 | saddr = READ_ONCE(inet->mc_addr); | 
|---|
| 773 | } else if (!ipc.oif) | 
|---|
| 774 | ipc.oif = READ_ONCE(inet->uc_index); | 
|---|
| 775 |  | 
|---|
| 776 | flowi4_init_output(fl4: &fl4, oif: ipc.oif, mark: ipc.sockc.mark, | 
|---|
| 777 | tos: ipc.tos & INET_DSCP_MASK, scope, | 
|---|
| 778 | proto: sk->sk_protocol, flags: inet_sk_flowi_flags(sk), daddr: faddr, | 
|---|
| 779 | saddr, dport: 0, sport: 0, uid: sk_uid(sk)); | 
|---|
| 780 |  | 
|---|
| 781 | fl4.fl4_icmp_type = user_icmph.type; | 
|---|
| 782 | fl4.fl4_icmp_code = user_icmph.code; | 
|---|
| 783 |  | 
|---|
| 784 | security_sk_classify_flow(sk, flic: flowi4_to_flowi_common(fl4: &fl4)); | 
|---|
| 785 | rt = ip_route_output_flow(net, flp: &fl4, sk); | 
|---|
| 786 | if (IS_ERR(ptr: rt)) { | 
|---|
| 787 | err = PTR_ERR(ptr: rt); | 
|---|
| 788 | rt = NULL; | 
|---|
| 789 | if (err == -ENETUNREACH) | 
|---|
| 790 | IP_INC_STATS(net, IPSTATS_MIB_OUTNOROUTES); | 
|---|
| 791 | goto out; | 
|---|
| 792 | } | 
|---|
| 793 |  | 
|---|
| 794 | err = -EACCES; | 
|---|
| 795 | if ((rt->rt_flags & RTCF_BROADCAST) && | 
|---|
| 796 | !sock_flag(sk, flag: SOCK_BROADCAST)) | 
|---|
| 797 | goto out; | 
|---|
| 798 |  | 
|---|
| 799 | if (msg->msg_flags & MSG_CONFIRM) | 
|---|
| 800 | goto do_confirm; | 
|---|
| 801 | back_from_confirm: | 
|---|
| 802 |  | 
|---|
| 803 | if (!ipc.addr) | 
|---|
| 804 | ipc.addr = fl4.daddr; | 
|---|
| 805 |  | 
|---|
| 806 | lock_sock(sk); | 
|---|
| 807 |  | 
|---|
| 808 | pfh.icmph.type = user_icmph.type; /* already checked */ | 
|---|
| 809 | pfh.icmph.code = user_icmph.code; /* ditto */ | 
|---|
| 810 | pfh.icmph.checksum = 0; | 
|---|
| 811 | pfh.icmph.un.echo.id = inet->inet_sport; | 
|---|
| 812 | pfh.icmph.un.echo.sequence = user_icmph.un.echo.sequence; | 
|---|
| 813 | pfh.msg = msg; | 
|---|
| 814 | pfh.wcheck = 0; | 
|---|
| 815 | pfh.family = AF_INET; | 
|---|
| 816 |  | 
|---|
| 817 | err = ip_append_data(sk, fl4: &fl4, getfrag: ping_getfrag, from: &pfh, len, | 
|---|
| 818 | protolen: sizeof(struct icmphdr), ipc: &ipc, rt: &rt, | 
|---|
| 819 | flags: msg->msg_flags); | 
|---|
| 820 | if (err) | 
|---|
| 821 | ip_flush_pending_frames(sk); | 
|---|
| 822 | else | 
|---|
| 823 | err = ping_v4_push_pending_frames(sk, pfh: &pfh, fl4: &fl4); | 
|---|
| 824 | release_sock(sk); | 
|---|
| 825 |  | 
|---|
| 826 | out: | 
|---|
| 827 | ip_rt_put(rt); | 
|---|
| 828 | out_free: | 
|---|
| 829 | if (free) | 
|---|
| 830 | kfree(objp: ipc.opt); | 
|---|
| 831 | if (!err) { | 
|---|
| 832 | icmp_out_count(net: sock_net(sk), type: user_icmph.type); | 
|---|
| 833 | return len; | 
|---|
| 834 | } | 
|---|
| 835 | return err; | 
|---|
| 836 |  | 
|---|
| 837 | do_confirm: | 
|---|
| 838 | if (msg->msg_flags & MSG_PROBE) | 
|---|
| 839 | dst_confirm_neigh(dst: &rt->dst, daddr: &fl4.daddr); | 
|---|
| 840 | if (!(msg->msg_flags & MSG_PROBE) || len) | 
|---|
| 841 | goto back_from_confirm; | 
|---|
| 842 | err = 0; | 
|---|
| 843 | goto out; | 
|---|
| 844 | } | 
|---|
| 845 |  | 
|---|
| 846 | int ping_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int flags, | 
|---|
| 847 | int *addr_len) | 
|---|
| 848 | { | 
|---|
| 849 | struct inet_sock *isk = inet_sk(sk); | 
|---|
| 850 | int family = sk->sk_family; | 
|---|
| 851 | struct sk_buff *skb; | 
|---|
| 852 | int copied, err; | 
|---|
| 853 |  | 
|---|
| 854 | pr_debug( "ping_recvmsg(sk=%p,sk->num=%u)\n", isk, isk->inet_num); | 
|---|
| 855 |  | 
|---|
| 856 | err = -EOPNOTSUPP; | 
|---|
| 857 | if (flags & MSG_OOB) | 
|---|
| 858 | goto out; | 
|---|
| 859 |  | 
|---|
| 860 | if (flags & MSG_ERRQUEUE) | 
|---|
| 861 | return inet_recv_error(sk, msg, len, addr_len); | 
|---|
| 862 |  | 
|---|
| 863 | skb = skb_recv_datagram(sk, flags, err: &err); | 
|---|
| 864 | if (!skb) | 
|---|
| 865 | goto out; | 
|---|
| 866 |  | 
|---|
| 867 | copied = skb->len; | 
|---|
| 868 | if (copied > len) { | 
|---|
| 869 | msg->msg_flags |= MSG_TRUNC; | 
|---|
| 870 | copied = len; | 
|---|
| 871 | } | 
|---|
| 872 |  | 
|---|
| 873 | /* Don't bother checking the checksum */ | 
|---|
| 874 | err = skb_copy_datagram_msg(from: skb, offset: 0, msg, size: copied); | 
|---|
| 875 | if (err) | 
|---|
| 876 | goto done; | 
|---|
| 877 |  | 
|---|
| 878 | sock_recv_timestamp(msg, sk, skb); | 
|---|
| 879 |  | 
|---|
| 880 | /* Copy the address and add cmsg data. */ | 
|---|
| 881 | if (family == AF_INET) { | 
|---|
| 882 | DECLARE_SOCKADDR(struct sockaddr_in *, sin, msg->msg_name); | 
|---|
| 883 |  | 
|---|
| 884 | if (sin) { | 
|---|
| 885 | sin->sin_family = AF_INET; | 
|---|
| 886 | sin->sin_port = 0 /* skb->h.uh->source */; | 
|---|
| 887 | sin->sin_addr.s_addr = ip_hdr(skb)->saddr; | 
|---|
| 888 | memset(s: sin->sin_zero, c: 0, n: sizeof(sin->sin_zero)); | 
|---|
| 889 | *addr_len = sizeof(*sin); | 
|---|
| 890 | } | 
|---|
| 891 |  | 
|---|
| 892 | if (inet_cmsg_flags(inet: isk)) | 
|---|
| 893 | ip_cmsg_recv(msg, skb); | 
|---|
| 894 |  | 
|---|
| 895 | #if IS_ENABLED(CONFIG_IPV6) | 
|---|
| 896 | } else if (family == AF_INET6) { | 
|---|
| 897 | struct ipv6hdr *ip6 = ipv6_hdr(skb); | 
|---|
| 898 | DECLARE_SOCKADDR(struct sockaddr_in6 *, sin6, msg->msg_name); | 
|---|
| 899 |  | 
|---|
| 900 | if (sin6) { | 
|---|
| 901 | sin6->sin6_family = AF_INET6; | 
|---|
| 902 | sin6->sin6_port = 0; | 
|---|
| 903 | sin6->sin6_addr = ip6->saddr; | 
|---|
| 904 | sin6->sin6_flowinfo = 0; | 
|---|
| 905 | if (inet6_test_bit(SNDFLOW, sk)) | 
|---|
| 906 | sin6->sin6_flowinfo = ip6_flowinfo(hdr: ip6); | 
|---|
| 907 | sin6->sin6_scope_id = | 
|---|
| 908 | ipv6_iface_scope_id(addr: &sin6->sin6_addr, | 
|---|
| 909 | iface: inet6_iif(skb)); | 
|---|
| 910 | *addr_len = sizeof(*sin6); | 
|---|
| 911 | } | 
|---|
| 912 |  | 
|---|
| 913 | if (inet6_sk(sk: sk)->rxopt.all) | 
|---|
| 914 | pingv6_ops.ip6_datagram_recv_common_ctl(sk, msg, skb); | 
|---|
| 915 | if (skb->protocol == htons(ETH_P_IPV6) && | 
|---|
| 916 | inet6_sk(sk: sk)->rxopt.all) | 
|---|
| 917 | pingv6_ops.ip6_datagram_recv_specific_ctl(sk, msg, skb); | 
|---|
| 918 | else if (skb->protocol == htons(ETH_P_IP) && | 
|---|
| 919 | inet_cmsg_flags(inet: isk)) | 
|---|
| 920 | ip_cmsg_recv(msg, skb); | 
|---|
| 921 | #endif | 
|---|
| 922 | } else { | 
|---|
| 923 | BUG(); | 
|---|
| 924 | } | 
|---|
| 925 |  | 
|---|
| 926 | err = copied; | 
|---|
| 927 |  | 
|---|
| 928 | done: | 
|---|
| 929 | skb_free_datagram(sk, skb); | 
|---|
| 930 | out: | 
|---|
| 931 | pr_debug( "ping_recvmsg -> %d\n", err); | 
|---|
| 932 | return err; | 
|---|
| 933 | } | 
|---|
| 934 | EXPORT_IPV6_MOD_GPL(ping_recvmsg); | 
|---|
| 935 |  | 
|---|
| 936 | static enum skb_drop_reason __ping_queue_rcv_skb(struct sock *sk, | 
|---|
| 937 | struct sk_buff *skb) | 
|---|
| 938 | { | 
|---|
| 939 | enum skb_drop_reason reason; | 
|---|
| 940 |  | 
|---|
| 941 | pr_debug( "ping_queue_rcv_skb(sk=%p,sk->num=%d,skb=%p)\n", | 
|---|
| 942 | inet_sk(sk), inet_sk(sk)->inet_num, skb); | 
|---|
| 943 | if (sock_queue_rcv_skb_reason(sk, skb, reason: &reason) < 0) { | 
|---|
| 944 | sk_skb_reason_drop(sk, skb, reason); | 
|---|
| 945 | pr_debug( "ping_queue_rcv_skb -> failed\n"); | 
|---|
| 946 | return reason; | 
|---|
| 947 | } | 
|---|
| 948 | return SKB_NOT_DROPPED_YET; | 
|---|
| 949 | } | 
|---|
| 950 |  | 
|---|
| 951 | int ping_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) | 
|---|
| 952 | { | 
|---|
| 953 | return __ping_queue_rcv_skb(sk, skb) ? -1 : 0; | 
|---|
| 954 | } | 
|---|
| 955 | EXPORT_IPV6_MOD_GPL(ping_queue_rcv_skb); | 
|---|
| 956 |  | 
|---|
| 957 |  | 
|---|
| 958 | /* | 
|---|
| 959 | *	All we need to do is get the socket. | 
|---|
| 960 | */ | 
|---|
| 961 |  | 
|---|
| 962 | enum skb_drop_reason ping_rcv(struct sk_buff *skb) | 
|---|
| 963 | { | 
|---|
| 964 | struct net *net = dev_net(dev: skb->dev); | 
|---|
| 965 | struct icmphdr *icmph = icmp_hdr(skb); | 
|---|
| 966 | struct sock *sk; | 
|---|
| 967 |  | 
|---|
| 968 | /* We assume the packet has already been checked by icmp_rcv */ | 
|---|
| 969 |  | 
|---|
| 970 | pr_debug( "ping_rcv(skb=%p,id=%04x,seq=%04x)\n", | 
|---|
| 971 | skb, ntohs(icmph->un.echo.id), ntohs(icmph->un.echo.sequence)); | 
|---|
| 972 |  | 
|---|
| 973 | /* Push ICMP header back */ | 
|---|
| 974 | skb_push(skb, len: skb->data - (u8 *)icmph); | 
|---|
| 975 |  | 
|---|
| 976 | sk = ping_lookup(net, skb, ntohs(icmph->un.echo.id)); | 
|---|
| 977 | if (sk) | 
|---|
| 978 | return __ping_queue_rcv_skb(sk, skb); | 
|---|
| 979 |  | 
|---|
| 980 | kfree_skb_reason(skb, reason: SKB_DROP_REASON_NO_SOCKET); | 
|---|
| 981 | return SKB_DROP_REASON_NO_SOCKET; | 
|---|
| 982 | } | 
|---|
| 983 | EXPORT_IPV6_MOD_GPL(ping_rcv); | 
|---|
| 984 |  | 
|---|
| 985 | struct proto ping_prot = { | 
|---|
| 986 | .name = "PING", | 
|---|
| 987 | .owner =	THIS_MODULE, | 
|---|
| 988 | .init =		ping_init_sock, | 
|---|
| 989 | .close =	ping_close, | 
|---|
| 990 | .pre_connect =	ping_pre_connect, | 
|---|
| 991 | .connect =	ip4_datagram_connect, | 
|---|
| 992 | .disconnect =	__udp_disconnect, | 
|---|
| 993 | .setsockopt =	ip_setsockopt, | 
|---|
| 994 | .getsockopt =	ip_getsockopt, | 
|---|
| 995 | .sendmsg =	ping_v4_sendmsg, | 
|---|
| 996 | .recvmsg =	ping_recvmsg, | 
|---|
| 997 | .bind =		ping_bind, | 
|---|
| 998 | .backlog_rcv =	ping_queue_rcv_skb, | 
|---|
| 999 | .release_cb =	ip4_datagram_release_cb, | 
|---|
| 1000 | .unhash =	ping_unhash, | 
|---|
| 1001 | .get_port =	ping_get_port, | 
|---|
| 1002 | .put_port =	ping_unhash, | 
|---|
| 1003 | .obj_size =	sizeof(struct inet_sock), | 
|---|
| 1004 | }; | 
|---|
| 1005 | EXPORT_IPV6_MOD(ping_prot); | 
|---|
| 1006 |  | 
|---|
| 1007 | #ifdef CONFIG_PROC_FS | 
|---|
| 1008 |  | 
|---|
| 1009 | static struct sock *ping_get_first(struct seq_file *seq, int start) | 
|---|
| 1010 | { | 
|---|
| 1011 | struct sock *sk; | 
|---|
| 1012 | struct ping_iter_state *state = seq->private; | 
|---|
| 1013 | struct net *net = seq_file_net(seq); | 
|---|
| 1014 |  | 
|---|
| 1015 | for (state->bucket = start; state->bucket < PING_HTABLE_SIZE; | 
|---|
| 1016 | ++state->bucket) { | 
|---|
| 1017 | struct hlist_head *hslot; | 
|---|
| 1018 |  | 
|---|
| 1019 | hslot = &ping_table.hash[state->bucket]; | 
|---|
| 1020 |  | 
|---|
| 1021 | if (hlist_empty(h: hslot)) | 
|---|
| 1022 | continue; | 
|---|
| 1023 |  | 
|---|
| 1024 | sk_for_each(sk, hslot) { | 
|---|
| 1025 | if (net_eq(net1: sock_net(sk), net2: net) && | 
|---|
| 1026 | sk->sk_family == state->family) | 
|---|
| 1027 | goto found; | 
|---|
| 1028 | } | 
|---|
| 1029 | } | 
|---|
| 1030 | sk = NULL; | 
|---|
| 1031 | found: | 
|---|
| 1032 | return sk; | 
|---|
| 1033 | } | 
|---|
| 1034 |  | 
|---|
| 1035 | static struct sock *ping_get_next(struct seq_file *seq, struct sock *sk) | 
|---|
| 1036 | { | 
|---|
| 1037 | struct ping_iter_state *state = seq->private; | 
|---|
| 1038 | struct net *net = seq_file_net(seq); | 
|---|
| 1039 |  | 
|---|
| 1040 | do { | 
|---|
| 1041 | sk = sk_next(sk); | 
|---|
| 1042 | } while (sk && (!net_eq(net1: sock_net(sk), net2: net))); | 
|---|
| 1043 |  | 
|---|
| 1044 | if (!sk) | 
|---|
| 1045 | return ping_get_first(seq, start: state->bucket + 1); | 
|---|
| 1046 | return sk; | 
|---|
| 1047 | } | 
|---|
| 1048 |  | 
|---|
| 1049 | static struct sock *ping_get_idx(struct seq_file *seq, loff_t pos) | 
|---|
| 1050 | { | 
|---|
| 1051 | struct sock *sk = ping_get_first(seq, start: 0); | 
|---|
| 1052 |  | 
|---|
| 1053 | if (sk) | 
|---|
| 1054 | while (pos && (sk = ping_get_next(seq, sk)) != NULL) | 
|---|
| 1055 | --pos; | 
|---|
| 1056 | return pos ? NULL : sk; | 
|---|
| 1057 | } | 
|---|
| 1058 |  | 
|---|
| 1059 | void *ping_seq_start(struct seq_file *seq, loff_t *pos, sa_family_t family) | 
|---|
| 1060 | __acquires(ping_table.lock) | 
|---|
| 1061 | { | 
|---|
| 1062 | struct ping_iter_state *state = seq->private; | 
|---|
| 1063 | state->bucket = 0; | 
|---|
| 1064 | state->family = family; | 
|---|
| 1065 |  | 
|---|
| 1066 | spin_lock(lock: &ping_table.lock); | 
|---|
| 1067 |  | 
|---|
| 1068 | return *pos ? ping_get_idx(seq, pos: *pos-1) : SEQ_START_TOKEN; | 
|---|
| 1069 | } | 
|---|
| 1070 | EXPORT_IPV6_MOD_GPL(ping_seq_start); | 
|---|
| 1071 |  | 
|---|
| 1072 | static void *ping_v4_seq_start(struct seq_file *seq, loff_t *pos) | 
|---|
| 1073 | { | 
|---|
| 1074 | return ping_seq_start(seq, pos, AF_INET); | 
|---|
| 1075 | } | 
|---|
| 1076 |  | 
|---|
| 1077 | void *ping_seq_next(struct seq_file *seq, void *v, loff_t *pos) | 
|---|
| 1078 | { | 
|---|
| 1079 | struct sock *sk; | 
|---|
| 1080 |  | 
|---|
| 1081 | if (v == SEQ_START_TOKEN) | 
|---|
| 1082 | sk = ping_get_idx(seq, pos: 0); | 
|---|
| 1083 | else | 
|---|
| 1084 | sk = ping_get_next(seq, sk: v); | 
|---|
| 1085 |  | 
|---|
| 1086 | ++*pos; | 
|---|
| 1087 | return sk; | 
|---|
| 1088 | } | 
|---|
| 1089 | EXPORT_IPV6_MOD_GPL(ping_seq_next); | 
|---|
| 1090 |  | 
|---|
| 1091 | void ping_seq_stop(struct seq_file *seq, void *v) | 
|---|
| 1092 | __releases(ping_table.lock) | 
|---|
| 1093 | { | 
|---|
| 1094 | spin_unlock(lock: &ping_table.lock); | 
|---|
| 1095 | } | 
|---|
| 1096 | EXPORT_IPV6_MOD_GPL(ping_seq_stop); | 
|---|
| 1097 |  | 
|---|
| 1098 | static void ping_v4_format_sock(struct sock *sp, struct seq_file *f, | 
|---|
| 1099 | int bucket) | 
|---|
| 1100 | { | 
|---|
| 1101 | struct inet_sock *inet = inet_sk(sp); | 
|---|
| 1102 | __be32 dest = inet->inet_daddr; | 
|---|
| 1103 | __be32 src = inet->inet_rcv_saddr; | 
|---|
| 1104 | __u16 destp = ntohs(inet->inet_dport); | 
|---|
| 1105 | __u16 srcp = ntohs(inet->inet_sport); | 
|---|
| 1106 |  | 
|---|
| 1107 | seq_printf(m: f, fmt: "%5d: %08X:%04X %08X:%04X" | 
|---|
| 1108 | " %02X %08X:%08X %02X:%08lX %08X %5u %8d %lu %d %pK %u", | 
|---|
| 1109 | bucket, src, srcp, dest, destp, sp->sk_state, | 
|---|
| 1110 | sk_wmem_alloc_get(sk: sp), | 
|---|
| 1111 | sk_rmem_alloc_get(sk: sp), | 
|---|
| 1112 | 0, 0L, 0, | 
|---|
| 1113 | from_kuid_munged(to: seq_user_ns(seq: f), kuid: sk_uid(sk: sp)), | 
|---|
| 1114 | 0, sock_i_ino(sk: sp), | 
|---|
| 1115 | refcount_read(r: &sp->sk_refcnt), sp, | 
|---|
| 1116 | sk_drops_read(sk: sp)); | 
|---|
| 1117 | } | 
|---|
| 1118 |  | 
|---|
| 1119 | static int ping_v4_seq_show(struct seq_file *seq, void *v) | 
|---|
| 1120 | { | 
|---|
| 1121 | seq_setwidth(m: seq, size: 127); | 
|---|
| 1122 | if (v == SEQ_START_TOKEN) | 
|---|
| 1123 | seq_puts(m: seq, s: "  sl  local_address rem_address   st tx_queue " | 
|---|
| 1124 | "rx_queue tr tm->when retrnsmt   uid  timeout " | 
|---|
| 1125 | "inode ref pointer drops"); | 
|---|
| 1126 | else { | 
|---|
| 1127 | struct ping_iter_state *state = seq->private; | 
|---|
| 1128 |  | 
|---|
| 1129 | ping_v4_format_sock(sp: v, f: seq, bucket: state->bucket); | 
|---|
| 1130 | } | 
|---|
| 1131 | seq_pad(m: seq, c: '\n'); | 
|---|
| 1132 | return 0; | 
|---|
| 1133 | } | 
|---|
| 1134 |  | 
|---|
| 1135 | static const struct seq_operations ping_v4_seq_ops = { | 
|---|
| 1136 | .start		= ping_v4_seq_start, | 
|---|
| 1137 | .show		= ping_v4_seq_show, | 
|---|
| 1138 | .next		= ping_seq_next, | 
|---|
| 1139 | .stop		= ping_seq_stop, | 
|---|
| 1140 | }; | 
|---|
| 1141 |  | 
|---|
| 1142 | static int __net_init ping_v4_proc_init_net(struct net *net) | 
|---|
| 1143 | { | 
|---|
| 1144 | if (!proc_create_net( "icmp", 0444, net->proc_net, &ping_v4_seq_ops, | 
|---|
| 1145 | sizeof(struct ping_iter_state))) | 
|---|
| 1146 | return -ENOMEM; | 
|---|
| 1147 |  | 
|---|
| 1148 | net->ipv4.ping_port_rover = get_random_u16(); | 
|---|
| 1149 | return 0; | 
|---|
| 1150 | } | 
|---|
| 1151 |  | 
|---|
| 1152 | static void __net_exit ping_v4_proc_exit_net(struct net *net) | 
|---|
| 1153 | { | 
|---|
| 1154 | remove_proc_entry( "icmp", net->proc_net); | 
|---|
| 1155 | } | 
|---|
| 1156 |  | 
|---|
| 1157 | static struct pernet_operations ping_v4_net_ops = { | 
|---|
| 1158 | .init = ping_v4_proc_init_net, | 
|---|
| 1159 | .exit = ping_v4_proc_exit_net, | 
|---|
| 1160 | }; | 
|---|
| 1161 |  | 
|---|
| 1162 | int __init ping_proc_init(void) | 
|---|
| 1163 | { | 
|---|
| 1164 | return register_pernet_subsys(&ping_v4_net_ops); | 
|---|
| 1165 | } | 
|---|
| 1166 |  | 
|---|
| 1167 | void ping_proc_exit(void) | 
|---|
| 1168 | { | 
|---|
| 1169 | unregister_pernet_subsys(&ping_v4_net_ops); | 
|---|
| 1170 | } | 
|---|
| 1171 |  | 
|---|
| 1172 | #endif | 
|---|
| 1173 |  | 
|---|
| 1174 | void __init ping_init(void) | 
|---|
| 1175 | { | 
|---|
| 1176 | int i; | 
|---|
| 1177 |  | 
|---|
| 1178 | for (i = 0; i < PING_HTABLE_SIZE; i++) | 
|---|
| 1179 | INIT_HLIST_HEAD(&ping_table.hash[i]); | 
|---|
| 1180 | spin_lock_init(&ping_table.lock); | 
|---|
| 1181 | } | 
|---|
| 1182 |  | 
|---|