当前位置:网站首页>The role of connect in the network

The role of connect in the network

2022-06-22 11:15:00 To maintain world peace_

Catalog

connect effect

Function call relationship

Key function analysis  

tcp_connect

tcp_connect The role of

summary


connect effect

The client is executing connect when ,

  1. The local socket The state is set to TCP_SYN_SENT
  2. Select an available port , issue SYN Handshake request and reset timer

Function call relationship

connect -> __sys_connect-> __sys_connect_file
    
int __sys_connect_file(struct file *file, struct sockaddr_storage *address,
		       int addrlen, int file_flags)
{
    // according to file lookup sock object 
	sock = sock_from_file(file, &err);
	if (!sock)
		goto out;

    //connect
	err = sock->ops->connect(sock, (struct sockaddr *)address, addrlen,
				 sock->file->f_flags | file_flags);
}

inet_stream_connect

int inet_stream_connect(struct socket *sock, struct sockaddr *uaddr,
			int addr_len, int flags)
{
	err = __inet_stream_connect(sock, uaddr, addr_len, flags, 0);
}


int __inet_stream_connect(struct socket *sock, struct sockaddr *uaddr,
			  int addr_len, int flags, int is_sendmsg)
{
    // Just created socket state SS_UNCONNECTED
	case SS_UNCONNECTED:
		if (sk->sk_state != TCP_CLOSE)
			goto out;

		err = sk->sk_prot->connect(sk, uaddr, addr_len);
		sock->state = SS_CONNECTING;

		break;
	}
}

tcp_v4_connect

int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
{
    // Set up socket state TCP_SYN_SENT
	tcp_set_state(sk, TCP_SYN_SENT);

	// Dynamically select a port 
	err = inet_hash_connect(tcp_death_row, sk);
    
	// according to sk Information in , Construct a SYNC Message of , And send it out 
	err = tcp_connect(sk);
}

inet_hash_connect

int __inet_hash_connect(struct inet_timewait_death_row *death_row,
		struct sock *sk, u32 port_offset,
		int (*check_established)(struct inet_timewait_death_row *,
			struct sock *, __u16, struct inet_timewait_sock **))
{

	int port = inet_sk(sk)->inet_num;// Get port 

	// Whether to bind ports 
	if (port) {
		head = &hinfo->bhash[inet_bhashfn(net, port,
						  hinfo->bhash_size)];
		tb = inet_csk(sk)->icsk_bind_hash;
		spin_lock_bh(&head->lock);
		if (sk_head(&tb->owners) == sk && !sk->sk_bind_node.next) {
			inet_ehash_nolisten(sk, NULL);
			spin_unlock_bh(&head->lock);
			return 0;
		}
		spin_unlock(&head->lock);
		ret = check_established(death_row, sk, port, NULL);
		local_bh_enable();
		return ret;
	}

	l3mdev = inet_sk_bound_l3mdev(sk);

	// Get local port configuration 
	inet_get_local_port_range(net, &low, &high);
	high++; /* [32768, 60999] -> [32768, 61000[ */
	remaining = high - low;
	if (likely(remaining > 1))
		remaining &= ~1U;

	offset = (hint + port_offset) % remaining;
	offset &= ~1U;

other_parity_scan:
	port = low + offset;// How to determine the port availability 
	for (i = 0; i < remaining; i += 2, port += 2) {
		if (unlikely(port >= high))
			port -= remaining;
		// Whether it is reserved port , net.ipv4.ip_local_reversed_ports
		if (inet_is_local_reserved_port(net, port))
			continue;
		// Find the port that has been used hash surface 
		head = &hinfo->bhash[inet_bhashfn(net, port,
						  hinfo->bhash_size)];
		spin_lock_bh(&head->lock);

		inet_bind_bucket_for_each(tb, &head->chain) {
			// If the port is used 
			if (net_eq(ib_net(tb), net) && tb->l3mdev == l3mdev &&
			    tb->port == port) {
				if (tb->fastreuse >= 0 ||
				    tb->fastreuseport >= 0)
					goto next_port;
				WARN_ON(hlist_empty(&tb->owners));
				// Check whether it is available to continue using , Check whether the quads are the same 
				if (!check_established(death_row, sk,
						       port, &tw))
					goto ok;
				goto next_port;
			}
		}
		// Not used 
		tb = inet_bind_bucket_create(hinfo->bind_bucket_cachep,
					     net, head, port, l3mdev);
		if (!tb) {
			spin_unlock_bh(&head->lock);
			return -ENOMEM;
		}
		tb->fastreuse = -1;
		tb->fastreuseport = -1;
		goto ok;
next_port:
		spin_unlock_bh(&head->lock);
		cond_resched();
	}

	return -EADDRNOTAVAIL;//cannot assign requested address
}

Key function analysis  

  • First judgement inet_sk(sk)->inet_num, If called bind, This function will select the port and set it in inet_num On . without bind,port by 0;
  • inet_get_local_port_range This function gets net.ipv4.ip_local_port_range Kernel parameters ;
  • inet_is_local_reserved_port Determine whether the port to be selected is in net.ipv4.ip_local_reserved_local_port in ;
  • stay hash Find the port in the table , If not found, you can use ;inet_bind_bucket_for_each Apply for one inet_bind_bucket To record port usage , And use hash Form management ;
  • If it has been used , adopt check_established Continue to test for availability , If you return 0 Is still available ;
  • If not used , be inet_bind_bucket_create Create a
  • If the port is not found , Then return to EADDRNOTAVAIL, Application layer tips Cannot assign requested address
// Check whether it is consistent with the existing ESTABLISH State connection conflict 
static int __inet_check_established(struct inet_timewait_death_row *death_row,
				    struct sock *sk, __u16 lport,
				    struct inet_timewait_sock **twp)
{

	// lookup hash
	struct inet_ehash_bucket *head = inet_ehash_bucket(hinfo, hash);


	// Find out if the quadruple has the same 
	sk_nulls_for_each(sk2, node, &head->chain) {
		if (sk2->sk_hash != hash)
			continue;
		// Yes saddr,daddr,port Made a comparison 
		if (likely(INET_MATCH(sk2, net, acookie,
					 saddr, daddr, ports, dif, sdif))) {
			if (sk2->sk_state == TCP_TIME_WAIT) {
				tw = inet_twsk(sk2);
				if (twsk_unique(sk, sk2, twp))
					break;
			}
			goto not_unique;
		}
	}

	return 0;// Available 

not_unique:
	return -EADDRNOTAVAIL;// There is nothing available 
}

INET_MATCH Source code is as follows , take __saddr,__daddr,__ports All of them are compared . If it matches, the port cannot be used , If it doesn't match , Ports available .

#define INET_MATCH(__sk, __net, __cookie, __saddr, __daddr, __ports, __dif, __sdif) \
	(((__sk)->sk_portpair == (__ports))		&&		\
	 ((__sk)->sk_daddr	== (__saddr))		&&		\
	 ((__sk)->sk_rcv_saddr	== (__daddr))		&&		\
	 (((__sk)->sk_bound_dev_if == (__dif))		||		\
	  ((__sk)->sk_bound_dev_if == (__sdif)))	&&		\
	 net_eq(sock_net(__sk), (__net)))
#endif /* 64-bit arch */

tcp_connect

/* Build a SYN and send it off. */
int tcp_connect(struct sock *sk)
{

	// apply skb
	buff = sk_stream_alloc_skb(sk, 0, sk->sk_allocation, true);

	// Add to send queue sk_write_queue
	tcp_connect_queue_skb(sk, buff);
	tcp_ecn_send_syn(sk, buff);
	tcp_rbtree_insert(&sk->tcp_rtx_queue, buff);

	// send out syn  tcp_transmit_skb
	err = tp->fastopen_req ? tcp_send_syn_data(sk, buff) :
	      tcp_transmit_skb(sk, buff, 1, sk->sk_allocation);


	// Restart timer 
	/* Timer for repeating the SYN until an answer. */
	inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS,
				  inet_csk(sk)->icsk_rto, TCP_RTO_MAX);
	return 0;
}

// Timeout time 1s
#define TCP_TIMEOUT_INIT ((unsigned)(1*HZ))	/* RFC6298 2.1 initial RTO value	*/

// On initialization   Set the time 
void tcp_init_sock(struct sock *sk)
{
    ..

	icsk->icsk_rto = TCP_TIMEOUT_INIT;
}

tcp_connect The role of

  1. apply skb, And set it to SKB package
  2. Add to send queue
  3. call tcp_transmit_skb Send the packet
  4. Restart timer , It will be resend after timeout

summary

  1. Cannot assign requested address An error that is issued when the port is not found available ; Can be adjusted by net.ipv4.ip_local_port_range Parameters , Release some ports ;
  2. If some ports do not want to be used , Kernel parameters ip_local_reserved_ports Set up .

  Reference resources
https://course.0voice.com/v1/course/intro?courseId=2&agentId=0


原网站

版权声明
本文为[To maintain world peace_]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/173/202206220955545433.html