// SPDX-License-Identifier: GPL-2.0-or-later /* * INET An implementation of the TCP/IP protocol suite for the LINUX * operating system. INET is implemented using the BSD Socket * interface as the means of communication with the user level. * * "Ping" sockets * * Based on ipv4/udp.c code. * * Authors: Vasiliy Kulikov / Openwall (for Linux 2.6), * Pavel Kankovsky (for Linux 2.4.32) * * Pavel gave all rights to bugs to Vasiliy, * none of the bugs are Pavel's now.
*/
isk = inet_sk(sk);
spin_lock(&ping_table.lock); if (ident == 0) {
u32 i;
u16 result = ping_port_rover + 1;
for (i = 0; i < (1L << 16); i++, result++) { if (!result)
result++; /* avoid zero */
hlist = ping_hashslot(&ping_table, net, result);
sk_for_each(sk2, hlist) { if (!net_eq(sock_net(sk2), net)) continue;
isk2 = inet_sk(sk2);
if (isk2->inet_num == result) goto next_port;
}
/* found */
ping_port_rover = ident = result; break;
next_port:
;
} if (i >= (1L << 16)) goto fail;
} else {
hlist = ping_hashslot(&ping_table, net, ident);
sk_for_each(sk2, hlist) { if (!net_eq(sock_net(sk2), net)) continue;
isk2 = inet_sk(sk2);
/* BUG? Why is this reuse and not reuseaddr? ping.c * doesn't turn off SO_REUSEADDR, and it doesn't expect * that other ping processes can steal its packets.
*/ if ((isk2->inet_num == ident) &&
(sk2 != sk) &&
(!sk2->sk_reuse || !sk->sk_reuse)) goto fail;
}
}
int ping_init_sock(struct sock *sk)
{ struct net *net = sock_net(sk);
kgid_t group = current_egid(); struct group_info *group_info; int i;
kgid_t low, high; int ret = 0;
if (sk->sk_family == AF_INET6)
sk->sk_ipv6only = 1;
inet_get_ping_group_range_net(net, &low, &high); if (gid_lte(low, group) && gid_lte(group, high)) return0;
group_info = get_current_groups(); for (i = 0; i < group_info->ngroups; i++) {
kgid_t gid = group_info->gid[i];
if (gid_lte(low, gid) && gid_lte(gid, high)) goto out_release_group;
}
staticint ping_pre_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
{ /* This check is replicated from __ip4_datagram_connect() and * intended to prevent BPF program called below from accessing bytes * that are out of the bound specified by user in addr_len.
*/ if (addr_len < sizeof(struct sockaddr_in)) return -EINVAL;
/* * We need our own bind because there are no privileged id's == local ports. * Moreover, we don't allow binding to multi- and broadcast addresses.
*/
int ping_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
{ struct inet_sock *isk = inet_sk(sk); unsignedshort snum; int err; int dif = sk->sk_bound_dev_if;
err = ping_check_bind_addr(sk, isk, uaddr, addr_len); if (err) return err;
staticinlineint ping_supported(int family, int type, int code)
{ return (family == AF_INET && type == ICMP_ECHO && code == 0) ||
(family == AF_INET && type == ICMP_EXT_ECHO && code == 0) ||
(family == AF_INET6 && type == ICMPV6_ECHO_REQUEST && code == 0) ||
(family == AF_INET6 && type == ICMPV6_EXT_ECHO_REQUEST && code == 0);
}
/* * This routine is called by the ICMP module when it gets some * sort of error condition.
*/
void ping_err(struct sk_buff *skb, int offset, u32 info)
{ int family; struct icmphdr *icmph; struct inet_sock *inet_sock; int type; int code; struct net *net = dev_net(skb->dev); struct sock *sk; int harderr; int err;
if (skb->protocol == htons(ETH_P_IP)) {
family = AF_INET;
type = icmp_hdr(skb)->type;
code = icmp_hdr(skb)->code;
icmph = (struct icmphdr *)(skb->data + offset);
} elseif (skb->protocol == htons(ETH_P_IPV6)) {
family = AF_INET6;
type = icmp6_hdr(skb)->icmp6_type;
code = icmp6_hdr(skb)->icmp6_code;
icmph = (struct icmphdr *) (skb->data + offset);
} else {
BUG();
}
/* We assume the packet has already been checked by icmp_unreach */
if (!ping_supported(family, icmph->type, icmph->code)) return;
sk = ping_lookup(net, skb, ntohs(icmph->un.echo.id)); if (!sk) {
pr_debug("no socket, dropping\n"); return; /* No socket for error */
}
pr_debug("err on socket %p\n", sk);
err = 0;
harderr = 0;
inet_sock = inet_sk(sk);
if (skb->protocol == htons(ETH_P_IP)) { switch (type) { default: case ICMP_TIME_EXCEEDED:
err = EHOSTUNREACH; break; case ICMP_SOURCE_QUENCH: /* This is not a real error but ping wants to see it. * Report it with some fake errno.
*/
err = EREMOTEIO; break; case ICMP_PARAMETERPROB:
err = EPROTO;
harderr = 1; break; case ICMP_DEST_UNREACH: if (code == ICMP_FRAG_NEEDED) { /* Path MTU discovery */
ipv4_sk_update_pmtu(skb, sk, info); if (READ_ONCE(inet_sock->pmtudisc) != IP_PMTUDISC_DONT) {
err = EMSGSIZE;
harderr = 1; break;
} goto out;
}
err = EHOSTUNREACH; if (code <= NR_ICMP_UNREACH) {
harderr = icmp_err_convert[code].fatal;
err = icmp_err_convert[code].errno;
} break; case ICMP_REDIRECT: /* See ICMP_SOURCE_QUENCH */
ipv4_sk_redirect(skb, sk);
err = EREMOTEIO; break;
} #if IS_ENABLED(CONFIG_IPV6)
} elseif (skb->protocol == htons(ETH_P_IPV6)) {
harderr = pingv6_ops.icmpv6_err_convert(type, code, &err); #endif
}
/* * RFC1122: OK. Passes ICMP errors back to application, as per * 4.1.3.3.
*/ if ((family == AF_INET && !inet_test_bit(RECVERR, sk)) ||
(family == AF_INET6 && !inet6_test_bit(RECVERR6, sk))) { if (!harderr || sk->sk_state != TCP_ESTABLISHED) goto out;
} else { if (family == AF_INET) {
ip_icmp_error(sk, skb, err, 0/* no remote port */,
info, (u8 *)icmph); #if IS_ENABLED(CONFIG_IPV6)
} elseif (family == AF_INET6) {
pingv6_ops.ipv6_icmp_error(sk, skb, err, 0,
info, (u8 *)icmph); #endif
}
}
sk->sk_err = err;
sk_error_report(sk);
out: return;
}
EXPORT_SYMBOL_GPL(ping_err);
/* * Copy and checksum an ICMP Echo packet from user space into a buffer * starting from the payload.
*/
int ping_getfrag(void *from, char *to, int offset, int fraglen, int odd, struct sk_buff *skb)
{ struct pingfakehdr *pfh = from;
if (!csum_and_copy_from_iter_full(to, fraglen, &pfh->wcheck,
&pfh->msg->msg_iter)) return -EFAULT;
#if IS_ENABLED(CONFIG_IPV6) /* For IPv6, checksum each skb as we go along, as expected by * icmpv6_push_pending_frames. For IPv4, accumulate the checksum in * wcheck, it will be finalized in ping_v4_push_pending_frames.
*/ if (pfh->family == AF_INET6) {
skb->csum = csum_block_add(skb->csum, pfh->wcheck, odd);
skb->ip_summed = CHECKSUM_NONE;
pfh->wcheck = 0;
} #endif
/* * Fetch the ICMP header provided by the userland. * iovec is modified! The ICMP header is consumed.
*/ if (memcpy_from_msg(user_icmph, msg, icmph_len)) return -EFAULT;
int ping_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int flags, int *addr_len)
{ struct inet_sock *isk = inet_sk(sk); int family = sk->sk_family; struct sk_buff *skb; int copied, err;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.