Branch data Line data Source code
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/ping.c code. 10 : : * 11 : : * Authors: Lorenzo Colitti (IPv6 support) 12 : : * Vasiliy Kulikov / Openwall (IPv4 implementation, for Linux 2.6), 13 : : * Pavel Kankovsky (IPv4 implementation, for Linux 2.4.32) 14 : : */ 15 : : 16 : : #include <net/addrconf.h> 17 : : #include <net/ipv6.h> 18 : : #include <net/ip6_route.h> 19 : : #include <net/protocol.h> 20 : : #include <net/udp.h> 21 : : #include <net/transp_v6.h> 22 : : #include <linux/proc_fs.h> 23 : : #include <net/ping.h> 24 : : 25 : : /* Compatibility glue so we can support IPv6 when it's compiled as a module */ 26 : 0 : static int dummy_ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len, 27 : : int *addr_len) 28 : : { 29 : 0 : return -EAFNOSUPPORT; 30 : : } 31 : 0 : static void dummy_ip6_datagram_recv_ctl(struct sock *sk, struct msghdr *msg, 32 : : struct sk_buff *skb) 33 : : { 34 : 0 : } 35 : 0 : static int dummy_icmpv6_err_convert(u8 type, u8 code, int *err) 36 : : { 37 : 0 : return -EAFNOSUPPORT; 38 : : } 39 : 0 : static void dummy_ipv6_icmp_error(struct sock *sk, struct sk_buff *skb, int err, 40 : 0 : __be16 port, u32 info, u8 *payload) {} 41 : 0 : static int dummy_ipv6_chk_addr(struct net *net, const struct in6_addr *addr, 42 : : const struct net_device *dev, int strict) 43 : : { 44 : 0 : return 0; 45 : : } 46 : : 47 : 0 : static int ping_v6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) 48 : : { 49 : : struct inet_sock *inet = inet_sk(sk); 50 : : struct ipv6_pinfo *np = inet6_sk(sk); 51 : : struct icmp6hdr user_icmph; 52 : : int addr_type; 53 : : struct in6_addr *daddr; 54 : : int oif = 0; 55 : : struct flowi6 fl6; 56 : : int err; 57 : : struct dst_entry *dst; 58 : : struct rt6_info *rt; 59 : : struct pingfakehdr pfh; 60 : : struct ipcm6_cookie ipc6; 61 : : 62 : : pr_debug("ping_v6_sendmsg(sk=%p,sk->num=%u)\n", inet, inet->inet_num); 63 : : 64 : 0 : err = ping_common_sendmsg(AF_INET6, msg, len, &user_icmph, 65 : : sizeof(user_icmph)); 66 : 0 : if (err) 67 : : return err; 68 : : 69 : 0 : if (msg->msg_name) { 70 : : DECLARE_SOCKADDR(struct sockaddr_in6 *, u, msg->msg_name); 71 : 0 : if (msg->msg_namelen < sizeof(*u)) 72 : : return -EINVAL; 73 : 0 : if (u->sin6_family != AF_INET6) { 74 : : return -EAFNOSUPPORT; 75 : : } 76 : 0 : daddr = &(u->sin6_addr); 77 : 0 : if (__ipv6_addr_needs_scope_id(ipv6_addr_type(daddr))) 78 : 0 : oif = u->sin6_scope_id; 79 : : } else { 80 : 0 : if (sk->sk_state != TCP_ESTABLISHED) 81 : : return -EDESTADDRREQ; 82 : 0 : daddr = &sk->sk_v6_daddr; 83 : : } 84 : : 85 : 0 : if (!oif) 86 : 0 : oif = sk->sk_bound_dev_if; 87 : : 88 : 0 : if (!oif) 89 : 0 : oif = np->sticky_pktinfo.ipi6_ifindex; 90 : : 91 : 0 : if (!oif && ipv6_addr_is_multicast(daddr)) 92 : 0 : oif = np->mcast_oif; 93 : 0 : else if (!oif) 94 : 0 : oif = np->ucast_oif; 95 : : 96 : : addr_type = ipv6_addr_type(daddr); 97 : 0 : if ((__ipv6_addr_needs_scope_id(addr_type) && !oif) || 98 : 0 : (addr_type & IPV6_ADDR_MAPPED) || 99 : 0 : (oif && sk->sk_bound_dev_if && oif != sk->sk_bound_dev_if)) 100 : : return -EINVAL; 101 : : 102 : : /* TODO: use ip6_datagram_send_ctl to get options from cmsg */ 103 : : 104 : 0 : memset(&fl6, 0, sizeof(fl6)); 105 : : 106 : 0 : fl6.flowi6_proto = IPPROTO_ICMPV6; 107 : 0 : fl6.saddr = np->saddr; 108 : 0 : fl6.daddr = *daddr; 109 : 0 : fl6.flowi6_oif = oif; 110 : 0 : fl6.flowi6_mark = sk->sk_mark; 111 : 0 : fl6.flowi6_uid = sk->sk_uid; 112 : 0 : fl6.fl6_icmp_type = user_icmph.icmp6_type; 113 : 0 : fl6.fl6_icmp_code = user_icmph.icmp6_code; 114 : 0 : security_sk_classify_flow(sk, flowi6_to_flowi(&fl6)); 115 : : 116 : : ipcm6_init_sk(&ipc6, np); 117 : 0 : fl6.flowlabel = ip6_make_flowinfo(ipc6.tclass, fl6.flowlabel); 118 : : 119 : 0 : dst = ip6_sk_dst_lookup_flow(sk, &fl6, daddr, false); 120 : 0 : if (IS_ERR(dst)) 121 : 0 : return PTR_ERR(dst); 122 : : rt = (struct rt6_info *) dst; 123 : : 124 : 0 : if (!fl6.flowi6_oif && ipv6_addr_is_multicast(&fl6.daddr)) 125 : 0 : fl6.flowi6_oif = np->mcast_oif; 126 : 0 : else if (!fl6.flowi6_oif) 127 : 0 : fl6.flowi6_oif = np->ucast_oif; 128 : : 129 : 0 : pfh.icmph.type = user_icmph.icmp6_type; 130 : 0 : pfh.icmph.code = user_icmph.icmp6_code; 131 : 0 : pfh.icmph.checksum = 0; 132 : 0 : pfh.icmph.un.echo.id = inet->inet_sport; 133 : 0 : pfh.icmph.un.echo.sequence = user_icmph.icmp6_sequence; 134 : 0 : pfh.msg = msg; 135 : 0 : pfh.wcheck = 0; 136 : 0 : pfh.family = AF_INET6; 137 : : 138 : 0 : ipc6.hlimit = ip6_sk_dst_hoplimit(np, &fl6, dst); 139 : : 140 : : lock_sock(sk); 141 : 0 : err = ip6_append_data(sk, ping_getfrag, &pfh, len, 142 : : 0, &ipc6, &fl6, rt, 143 : : MSG_DONTWAIT); 144 : : 145 : 0 : if (err) { 146 : 0 : ICMP6_INC_STATS(sock_net(sk), rt->rt6i_idev, 147 : : ICMP6_MIB_OUTERRORS); 148 : 0 : ip6_flush_pending_frames(sk); 149 : : } else { 150 : 0 : icmpv6_push_pending_frames(sk, &fl6, 151 : : (struct icmp6hdr *)&pfh.icmph, len); 152 : : } 153 : 0 : release_sock(sk); 154 : : 155 : 0 : dst_release(dst); 156 : : 157 : 0 : if (err) 158 : : return err; 159 : : 160 : 0 : return len; 161 : : } 162 : : 163 : : struct proto pingv6_prot = { 164 : : .name = "PINGv6", 165 : : .owner = THIS_MODULE, 166 : : .init = ping_init_sock, 167 : : .close = ping_close, 168 : : .connect = ip6_datagram_connect_v6_only, 169 : : .disconnect = __udp_disconnect, 170 : : .setsockopt = ipv6_setsockopt, 171 : : .getsockopt = ipv6_getsockopt, 172 : : .sendmsg = ping_v6_sendmsg, 173 : : .recvmsg = ping_recvmsg, 174 : : .bind = ping_bind, 175 : : .backlog_rcv = ping_queue_rcv_skb, 176 : : .hash = ping_hash, 177 : : .unhash = ping_unhash, 178 : : .get_port = ping_get_port, 179 : : .obj_size = sizeof(struct raw6_sock), 180 : : }; 181 : : EXPORT_SYMBOL_GPL(pingv6_prot); 182 : : 183 : : static struct inet_protosw pingv6_protosw = { 184 : : .type = SOCK_DGRAM, 185 : : .protocol = IPPROTO_ICMPV6, 186 : : .prot = &pingv6_prot, 187 : : .ops = &inet6_sockraw_ops, 188 : : .flags = INET_PROTOSW_REUSE, 189 : : }; 190 : : 191 : : #ifdef CONFIG_PROC_FS 192 : 0 : static void *ping_v6_seq_start(struct seq_file *seq, loff_t *pos) 193 : : { 194 : 0 : return ping_seq_start(seq, pos, AF_INET6); 195 : : } 196 : : 197 : 0 : static int ping_v6_seq_show(struct seq_file *seq, void *v) 198 : : { 199 : 0 : if (v == SEQ_START_TOKEN) { 200 : 0 : seq_puts(seq, IPV6_SEQ_DGRAM_HEADER); 201 : : } else { 202 : 0 : int bucket = ((struct ping_iter_state *) seq->private)->bucket; 203 : : struct inet_sock *inet = inet_sk(v); 204 : 0 : __u16 srcp = ntohs(inet->inet_sport); 205 : 0 : __u16 destp = ntohs(inet->inet_dport); 206 : : ip6_dgram_sock_seq_show(seq, v, srcp, destp, bucket); 207 : : } 208 : 0 : return 0; 209 : : } 210 : : 211 : : static const struct seq_operations ping_v6_seq_ops = { 212 : : .start = ping_v6_seq_start, 213 : : .show = ping_v6_seq_show, 214 : : .next = ping_seq_next, 215 : : .stop = ping_seq_stop, 216 : : }; 217 : : 218 : 3 : static int __net_init ping_v6_proc_init_net(struct net *net) 219 : : { 220 : 3 : if (!proc_create_net("icmp6", 0444, net->proc_net, &ping_v6_seq_ops, 221 : : sizeof(struct ping_iter_state))) 222 : : return -ENOMEM; 223 : 3 : return 0; 224 : : } 225 : : 226 : 1 : static void __net_exit ping_v6_proc_exit_net(struct net *net) 227 : : { 228 : 1 : remove_proc_entry("icmp6", net->proc_net); 229 : 1 : } 230 : : 231 : : static struct pernet_operations ping_v6_net_ops = { 232 : : .init = ping_v6_proc_init_net, 233 : : .exit = ping_v6_proc_exit_net, 234 : : }; 235 : : #endif 236 : : 237 : 3 : int __init pingv6_init(void) 238 : : { 239 : : #ifdef CONFIG_PROC_FS 240 : 3 : int ret = register_pernet_subsys(&ping_v6_net_ops); 241 : 3 : if (ret) 242 : : return ret; 243 : : #endif 244 : 3 : pingv6_ops.ipv6_recv_error = ipv6_recv_error; 245 : 3 : pingv6_ops.ip6_datagram_recv_common_ctl = ip6_datagram_recv_common_ctl; 246 : 3 : pingv6_ops.ip6_datagram_recv_specific_ctl = 247 : : ip6_datagram_recv_specific_ctl; 248 : 3 : pingv6_ops.icmpv6_err_convert = icmpv6_err_convert; 249 : 3 : pingv6_ops.ipv6_icmp_error = ipv6_icmp_error; 250 : 3 : pingv6_ops.ipv6_chk_addr = ipv6_chk_addr; 251 : 3 : return inet6_register_protosw(&pingv6_protosw); 252 : : } 253 : : 254 : : /* This never gets called because it's not possible to unload the ipv6 module, 255 : : * but just in case. 256 : : */ 257 : 0 : void pingv6_exit(void) 258 : : { 259 : 0 : pingv6_ops.ipv6_recv_error = dummy_ipv6_recv_error; 260 : 0 : pingv6_ops.ip6_datagram_recv_common_ctl = dummy_ip6_datagram_recv_ctl; 261 : 0 : pingv6_ops.ip6_datagram_recv_specific_ctl = dummy_ip6_datagram_recv_ctl; 262 : 0 : pingv6_ops.icmpv6_err_convert = dummy_icmpv6_err_convert; 263 : 0 : pingv6_ops.ipv6_icmp_error = dummy_ipv6_icmp_error; 264 : 0 : pingv6_ops.ipv6_chk_addr = dummy_ipv6_chk_addr; 265 : : #ifdef CONFIG_PROC_FS 266 : 0 : unregister_pernet_subsys(&ping_v6_net_ops); 267 : : #endif 268 : 0 : inet6_unregister_protosw(&pingv6_protosw); 269 : 0 : }