0
点赞
收藏
分享

微信扫一扫

UDP socket--udp_table

软件共享软件 2022-03-12 阅读 40

udp_table用于存放udp socket,其定义如下:

struct udp_table {
	struct udp_hslot	*hash; //hash table, sockets are hashed on (local port)
	struct udp_hslot	*hash2;//hash table, sockets are hashed on (local port, local address)
	unsigned int		mask;//number of slots in hash tables, minus 1
	unsigned int		log;//log2(number of slots in hash table)
};
extern struct udp_table udp_table;

udp_table的初始化:

inet_init
	udp_init
		udp_table_init(&udp_table, "UDP");
		{
			unsigned int i;
			table->hash = alloc_large_system_hash(name,
							      2 * sizeof(struct udp_hslot),
							      uhash_entries,
							      21, /* one slot per 2 MB */
							      0,
							      &table->log,
							      &table->mask,
							      UDP_HTABLE_SIZE_MIN,
							      64 * 1024);
			table->hash2 = table->hash + (table->mask + 1);
			for (i = 0; i <= table->mask; i++) {
				INIT_HLIST_HEAD(&table->hash[i].head);
				table->hash[i].count = 0;
				spin_lock_init(&table->hash[i].lock);
			}
			for (i = 0; i <= table->mask; i++) {
				INIT_HLIST_HEAD(&table->hash2[i].head);
				table->hash2[i].count = 0;
				spin_lock_init(&table->hash2[i].lock);
			}
		}

调用bind()及sendto()时,UDP socket会被插入到udp_table中,而这是在udp_v4_get_port()中进行的。

int udp_v4_get_port(struct sock *sk, unsigned short snum)
{
	unsigned int hash2_nulladdr =
		ipv4_portaddr_hash(sock_net(sk), htonl(INADDR_ANY), snum);
	unsigned int hash2_partial =
		ipv4_portaddr_hash(sock_net(sk), inet_sk(sk)->inet_rcv_saddr, 0);

	/* precompute partial secondary hash */
	udp_sk(sk)->udp_portaddr_hash = hash2_partial;
	return udp_lib_get_port(sk, snum, hash2_nulladdr);
}

这个函数先计算两个特殊的hash值,然后调用udp_lib_get_port处理:

udp_lib_get_port(sk, snum, hash2_nulladdr)
{
	struct udp_hslot *hslot, *hslot2;
	struct udp_table *udptable = sk->sk_prot->h.udp_table;
	int    error = 1;
	struct net *net = sock_net(sk);
	
	if (!snum) { 
		int low, high, remaining;
		unsigned int rand;
		unsigned short first, last;
		DECLARE_BITMAP(bitmap, PORTS_PER_CHAIN);
		inet_get_local_port_range(net, &low, &high);
		remaining = (high - low) + 1;
		rand = prandom_u32();
		first = reciprocal_scale(rand, remaining) + low;
		/*
		 * force rand to be an odd multiple of UDP_HTABLE_SIZE
		 */
		rand = (rand | 1) * (udptable->mask + 1);
		last = first + udptable->mask + 1;
		do {
			hslot = udp_hashslot(udptable, net, first);
			bitmap_zero(bitmap, PORTS_PER_CHAIN);
			spin_lock_bh(&hslot->lock);
			udp_lib_lport_inuse(net, snum, hslot, bitmap, sk,
					    udptable->log);

			snum = first;
			/*
			 * Iterate on all possible values of snum for this hash.
			 * Using steps of an odd multiple of UDP_HTABLE_SIZE
			 * give us randomization and full range coverage.
			 */
			do {
				if (low <= snum && snum <= high &&
				    !test_bit(snum >> udptable->log, bitmap) &&
				    !inet_is_local_reserved_port(net, snum))
					goto found;
				snum += rand;
			} while (snum != first);
			spin_unlock_bh(&hslot->lock);
			cond_resched();
		} while (++first != last);
		goto fail;
	} else {
		hslot = udp_hashslot(udptable, net, snum);
		spin_lock_bh(&hslot->lock);
		if (hslot->count > 10) {
			int exist;
			unsigned int slot2 = udp_sk(sk)->udp_portaddr_hash ^ snum;

			slot2          &= udptable->mask;
			hash2_nulladdr &= udptable->mask;

			hslot2 = udp_hashslot2(udptable, slot2);
			if (hslot->count < hslot2->count)
				goto scan_primary_hash;

			exist = udp_lib_lport_inuse2(net, snum, hslot2, sk);
			if (!exist && (hash2_nulladdr != slot2)) {
				hslot2 = udp_hashslot2(udptable, hash2_nulladdr);
				exist = udp_lib_lport_inuse2(net, snum, hslot2,
							     sk);
			}
			if (exist)
				goto fail_unlock;
			else
				goto found;
		}
scan_primary_hash:
		if (udp_lib_lport_inuse(net, snum, hslot, NULL, sk, 0))
			goto fail_unlock;
	}
found:
	inet_sk(sk)->inet_num = snum;
	udp_sk(sk)->udp_port_hash = snum;
	udp_sk(sk)->udp_portaddr_hash ^= snum;
	if (sk_unhashed(sk)) {
		if (sk->sk_reuseport &&
		    udp_reuseport_add_sock(sk, hslot)) {
			inet_sk(sk)->inet_num = 0;
			udp_sk(sk)->udp_port_hash = 0;
			udp_sk(sk)->udp_portaddr_hash ^= snum;
			goto fail_unlock;
		}

		sk_add_node_rcu(sk, &hslot->head);
		hslot->count++;
		sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1);

		hslot2 = udp_hashslot2(udptable, udp_sk(sk)->udp_portaddr_hash);
		spin_lock(&hslot2->lock);
		if (IS_ENABLED(CONFIG_IPV6) && sk->sk_reuseport &&
		    sk->sk_family == AF_INET6)
			hlist_add_tail_rcu(&udp_sk(sk)->udp_portaddr_node,
					   &hslot2->head);
		else
			hlist_add_head_rcu(&udp_sk(sk)->udp_portaddr_node,
					   &hslot2->head);
		hslot2->count++;
		spin_unlock(&hslot2->lock);
	}
	sock_set_flag(sk, SOCK_RCU_FREE);
	error = 0;
fail_unlock:
	spin_unlock_bh(&hslot->lock);
fail:
	return error;
}

根椐参数snum的值分为两种情况:

1.如果snum为0,则为socket分配一个没有在使用的端口号,然后将socket插入到udp_table,更新socket部分成员。

2.如果snum不为0,则snum就是要bind的端口,先查udp_table,看这个端口号是否已在用,如果没有在用,那么当前socket就使用这个端口号,并将socket插入udp_table,更新socket成员
举报

相关推荐

0 条评论