net/ipv4/ping.c

Source file repositories/reference/linux-study-clean/net/ipv4/ping.c

File Facts

System
Linux kernel
Corpus path
net/ipv4/ping.c
Extension
.c
Size
28278 bytes
Lines
1172
Domain
Networking Core
Bucket
Sockets, Protocols, Packet Path, And Network Policy
Inferred role
Networking Core: implementation source
Status
source implementation candidate

Why This File Exists

Networking stack implementation surface: socket APIs, protocol dispatch, packet flow, routing, filtering, and network namespaces.

Dependency Surface

Detected Declarations

Annotated Snippet

struct ping_table {
	struct hlist_head	hash[PING_HTABLE_SIZE];
	spinlock_t		lock;
};

static struct ping_table ping_table;
struct pingv6_ops pingv6_ops;

static inline u32 ping_hashfn(const struct net *net, u32 num, u32 mask)
{
	u32 res = (num + net_hash_mix(net)) & mask;

	pr_debug("hash(%u) = %u\n", num, res);
	return res;
}

static inline struct hlist_head *ping_hashslot(struct ping_table *table,
					       struct net *net, unsigned int num)
{
	return &table->hash[ping_hashfn(net, num, PING_HTABLE_MASK)];
}

int ping_get_port(struct sock *sk, unsigned short ident)
{
	struct net *net = sock_net(sk);
	struct inet_sock *isk, *isk2;
	struct hlist_head *hlist;
	struct sock *sk2 = NULL;

	isk = inet_sk(sk);
	spin_lock(&ping_table.lock);
	if (ident == 0) {
		u16 result = net->ipv4.ping_port_rover + 1;
		u32 i;

		for (i = 0; i < (1L << 16); i++, result++) {
			if (!result)
				continue; /* 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 */
			net->ipv4.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;
		}
	}

	pr_debug("found port/ident = %d\n", ident);
	isk->inet_num = ident;
	if (sk_unhashed(sk)) {
		pr_debug("was not hashed\n");
		sk_add_node_rcu(sk, hlist);
		sock_set_flag(sk, SOCK_RCU_FREE);
		sock_prot_inuse_add(net, sk->sk_prot, 1);
	}
	spin_unlock(&ping_table.lock);
	return 0;

fail:
	spin_unlock(&ping_table.lock);
	return -EADDRINUSE;
}

Annotation

Implementation Notes