| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * INET An implementation of the TCP/IP protocol suite for the LINUX |
|---|
| 3 | 4 | * operating system. INET is implemented using the BSD Socket |
|---|
| 4 | 5 | * interface as the means of communication with the user level. |
|---|
| 5 | 6 | * |
|---|
| 6 | 7 | * "Ping" sockets |
|---|
| 7 | | - * |
|---|
| 8 | | - * This program is free software; you can redistribute it and/or |
|---|
| 9 | | - * modify it under the terms of the GNU General Public License |
|---|
| 10 | | - * as published by the Free Software Foundation; either version |
|---|
| 11 | | - * 2 of the License, or (at your option) any later version. |
|---|
| 12 | 8 | * |
|---|
| 13 | 9 | * Based on ipv4/udp.c code. |
|---|
| 14 | 10 | * |
|---|
| .. | .. |
|---|
| 17 | 13 | * |
|---|
| 18 | 14 | * Pavel gave all rights to bugs to Vasiliy, |
|---|
| 19 | 15 | * none of the bugs are Pavel's now. |
|---|
| 20 | | - * |
|---|
| 21 | 16 | */ |
|---|
| 22 | 17 | |
|---|
| 23 | 18 | #include <linux/uaccess.h> |
|---|
| .. | .. |
|---|
| 305 | 300 | |
|---|
| 306 | 301 | /* Checks the bind address and possibly modifies sk->sk_bound_dev_if. */ |
|---|
| 307 | 302 | static int ping_check_bind_addr(struct sock *sk, struct inet_sock *isk, |
|---|
| 308 | | - struct sockaddr *uaddr, int addr_len) { |
|---|
| 303 | + struct sockaddr *uaddr, int addr_len) |
|---|
| 304 | +{ |
|---|
| 309 | 305 | struct net *net = sock_net(sk); |
|---|
| 310 | 306 | if (sk->sk_family == AF_INET) { |
|---|
| 311 | 307 | struct sockaddr_in *addr = (struct sockaddr_in *) uaddr; |
|---|
| 308 | + u32 tb_id = RT_TABLE_LOCAL; |
|---|
| 312 | 309 | int chk_addr_ret; |
|---|
| 313 | 310 | |
|---|
| 314 | 311 | if (addr_len < sizeof(*addr)) |
|---|
| .. | .. |
|---|
| 322 | 319 | pr_debug("ping_check_bind_addr(sk=%p,addr=%pI4,port=%d)\n", |
|---|
| 323 | 320 | sk, &addr->sin_addr.s_addr, ntohs(addr->sin_port)); |
|---|
| 324 | 321 | |
|---|
| 325 | | - chk_addr_ret = inet_addr_type(net, addr->sin_addr.s_addr); |
|---|
| 326 | | - |
|---|
| 327 | 322 | if (addr->sin_addr.s_addr == htonl(INADDR_ANY)) |
|---|
| 328 | 323 | chk_addr_ret = RTN_LOCAL; |
|---|
| 324 | + else { |
|---|
| 325 | + tb_id = l3mdev_fib_table_by_index(net, sk->sk_bound_dev_if) ? : tb_id; |
|---|
| 326 | + chk_addr_ret = inet_addr_type_table(net, addr->sin_addr.s_addr, tb_id); |
|---|
| 327 | + } |
|---|
| 329 | 328 | |
|---|
| 330 | 329 | if ((!inet_can_nonlocal_bind(net, isk) && |
|---|
| 331 | 330 | chk_addr_ret != RTN_LOCAL) || |
|---|
| .. | .. |
|---|
| 363 | 362 | return -ENODEV; |
|---|
| 364 | 363 | } |
|---|
| 365 | 364 | } |
|---|
| 365 | + |
|---|
| 366 | + if (!dev && sk->sk_bound_dev_if) { |
|---|
| 367 | + dev = dev_get_by_index_rcu(net, sk->sk_bound_dev_if); |
|---|
| 368 | + if (!dev) { |
|---|
| 369 | + rcu_read_unlock(); |
|---|
| 370 | + return -ENODEV; |
|---|
| 371 | + } |
|---|
| 372 | + } |
|---|
| 366 | 373 | has_addr = pingv6_ops.ipv6_chk_addr(net, &addr->sin6_addr, dev, |
|---|
| 367 | 374 | scoped); |
|---|
| 368 | 375 | rcu_read_unlock(); |
|---|
| .. | .. |
|---|
| 395 | 402 | } |
|---|
| 396 | 403 | } |
|---|
| 397 | 404 | |
|---|
| 398 | | -static void ping_clear_saddr(struct sock *sk, int dif) |
|---|
| 399 | | -{ |
|---|
| 400 | | - sk->sk_bound_dev_if = dif; |
|---|
| 401 | | - if (sk->sk_family == AF_INET) { |
|---|
| 402 | | - struct inet_sock *isk = inet_sk(sk); |
|---|
| 403 | | - isk->inet_rcv_saddr = isk->inet_saddr = 0; |
|---|
| 404 | | -#if IS_ENABLED(CONFIG_IPV6) |
|---|
| 405 | | - } else if (sk->sk_family == AF_INET6) { |
|---|
| 406 | | - struct ipv6_pinfo *np = inet6_sk(sk); |
|---|
| 407 | | - memset(&sk->sk_v6_rcv_saddr, 0, sizeof(sk->sk_v6_rcv_saddr)); |
|---|
| 408 | | - memset(&np->saddr, 0, sizeof(np->saddr)); |
|---|
| 409 | | -#endif |
|---|
| 410 | | - } |
|---|
| 411 | | -} |
|---|
| 412 | 405 | /* |
|---|
| 413 | 406 | * We need our own bind because there are no privileged id's == local ports. |
|---|
| 414 | 407 | * Moreover, we don't allow binding to multi- and broadcast addresses. |
|---|
| .. | .. |
|---|
| 432 | 425 | goto out; |
|---|
| 433 | 426 | |
|---|
| 434 | 427 | err = -EADDRINUSE; |
|---|
| 435 | | - ping_set_saddr(sk, uaddr); |
|---|
| 436 | 428 | snum = ntohs(((struct sockaddr_in *)uaddr)->sin_port); |
|---|
| 437 | 429 | if (ping_get_port(sk, snum) != 0) { |
|---|
| 438 | | - ping_clear_saddr(sk, dif); |
|---|
| 430 | + /* Restore possibly modified sk->sk_bound_dev_if by ping_check_bind_addr(). */ |
|---|
| 431 | + sk->sk_bound_dev_if = dif; |
|---|
| 439 | 432 | goto out; |
|---|
| 440 | 433 | } |
|---|
| 434 | + ping_set_saddr(sk, uaddr); |
|---|
| 441 | 435 | |
|---|
| 442 | 436 | pr_debug("after bind(): num = %hu, dif = %d\n", |
|---|
| 443 | 437 | isk->inet_num, |
|---|
| .. | .. |
|---|
| 659 | 653 | } |
|---|
| 660 | 654 | |
|---|
| 661 | 655 | int ping_common_sendmsg(int family, struct msghdr *msg, size_t len, |
|---|
| 662 | | - void *user_icmph, size_t icmph_len) { |
|---|
| 656 | + void *user_icmph, size_t icmph_len) |
|---|
| 657 | +{ |
|---|
| 663 | 658 | u8 type, code; |
|---|
| 664 | 659 | |
|---|
| 665 | 660 | if (len > 0xFFFF) |
|---|
| .. | .. |
|---|
| 786 | 781 | } |
|---|
| 787 | 782 | |
|---|
| 788 | 783 | if (ipv4_is_multicast(daddr)) { |
|---|
| 789 | | - if (!ipc.oif) |
|---|
| 784 | + if (!ipc.oif || netif_index_is_l3_master(sock_net(sk), ipc.oif)) |
|---|
| 790 | 785 | ipc.oif = inet->mc_index; |
|---|
| 791 | 786 | if (!saddr) |
|---|
| 792 | 787 | saddr = inet->mc_addr; |
|---|
| 793 | 788 | } else if (!ipc.oif) |
|---|
| 794 | 789 | ipc.oif = inet->uc_index; |
|---|
| 795 | 790 | |
|---|
| 796 | | - flowi4_init_output(&fl4, ipc.oif, sk->sk_mark, tos, |
|---|
| 791 | + flowi4_init_output(&fl4, ipc.oif, ipc.sockc.mark, tos, |
|---|
| 797 | 792 | RT_SCOPE_UNIVERSE, sk->sk_protocol, |
|---|
| 798 | 793 | inet_sk_flowi_flags(sk), faddr, saddr, 0, 0, |
|---|
| 799 | 794 | sk->sk_uid); |
|---|
| .. | .. |
|---|
| 801 | 796 | fl4.fl4_icmp_type = user_icmph.type; |
|---|
| 802 | 797 | fl4.fl4_icmp_code = user_icmph.code; |
|---|
| 803 | 798 | |
|---|
| 804 | | - security_sk_classify_flow(sk, flowi4_to_flowi(&fl4)); |
|---|
| 799 | + security_sk_classify_flow(sk, flowi4_to_flowi_common(&fl4)); |
|---|
| 805 | 800 | rt = ip_route_output_flow(net, &fl4, sk); |
|---|
| 806 | 801 | if (IS_ERR(rt)) { |
|---|
| 807 | 802 | err = PTR_ERR(rt); |
|---|
| .. | .. |
|---|
| 1125 | 1120 | __u16 srcp = ntohs(inet->inet_sport); |
|---|
| 1126 | 1121 | |
|---|
| 1127 | 1122 | seq_printf(f, "%5d: %08X:%04X %08X:%04X" |
|---|
| 1128 | | - " %02X %08X:%08X %02X:%08lX %08X %5u %8d %lu %d %pK %d", |
|---|
| 1123 | + " %02X %08X:%08X %02X:%08lX %08X %5u %8d %lu %d %pK %u", |
|---|
| 1129 | 1124 | bucket, src, srcp, dest, destp, sp->sk_state, |
|---|
| 1130 | 1125 | sk_wmem_alloc_get(sp), |
|---|
| 1131 | 1126 | sk_rmem_alloc_get(sp), |
|---|