⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 proto.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* *  net/dccp/proto.c * *  An implementation of the DCCP protocol *  Arnaldo Carvalho de Melo <acme@conectiva.com.br> * *	This program is free software; you can redistribute it and/or modify it *	under the terms of the GNU General Public License version 2 as *	published by the Free Software Foundation. */#include <linux/dccp.h>#include <linux/module.h>#include <linux/types.h>#include <linux/sched.h>#include <linux/kernel.h>#include <linux/skbuff.h>#include <linux/netdevice.h>#include <linux/in.h>#include <linux/if_arp.h>#include <linux/init.h>#include <linux/random.h>#include <net/checksum.h>#include <net/inet_sock.h>#include <net/sock.h>#include <net/xfrm.h>#include <asm/ioctls.h>#include <asm/semaphore.h>#include <linux/spinlock.h>#include <linux/timer.h>#include <linux/delay.h>#include <linux/poll.h>#include "ccid.h"#include "dccp.h"#include "feat.h"DEFINE_SNMP_STAT(struct dccp_mib, dccp_statistics) __read_mostly;EXPORT_SYMBOL_GPL(dccp_statistics);atomic_t dccp_orphan_count = ATOMIC_INIT(0);EXPORT_SYMBOL_GPL(dccp_orphan_count);struct inet_hashinfo __cacheline_aligned dccp_hashinfo = {	.lhash_lock	= RW_LOCK_UNLOCKED,	.lhash_users	= ATOMIC_INIT(0),	.lhash_wait = __WAIT_QUEUE_HEAD_INITIALIZER(dccp_hashinfo.lhash_wait),};EXPORT_SYMBOL_GPL(dccp_hashinfo);/* the maximum queue length for tx in packets. 0 is no limit */int sysctl_dccp_tx_qlen __read_mostly = 5;void dccp_set_state(struct sock *sk, const int state){	const int oldstate = sk->sk_state;	dccp_pr_debug("%s(%p) %-10.10s -> %s\n",		      dccp_role(sk), sk,		      dccp_state_name(oldstate), dccp_state_name(state));	WARN_ON(state == oldstate);	switch (state) {	case DCCP_OPEN:		if (oldstate != DCCP_OPEN)			DCCP_INC_STATS(DCCP_MIB_CURRESTAB);		break;	case DCCP_CLOSED:		if (oldstate == DCCP_CLOSING || oldstate == DCCP_OPEN)			DCCP_INC_STATS(DCCP_MIB_ESTABRESETS);		sk->sk_prot->unhash(sk);		if (inet_csk(sk)->icsk_bind_hash != NULL &&		    !(sk->sk_userlocks & SOCK_BINDPORT_LOCK))			inet_put_port(&dccp_hashinfo, sk);		/* fall through */	default:		if (oldstate == DCCP_OPEN)			DCCP_DEC_STATS(DCCP_MIB_CURRESTAB);	}	/* Change state AFTER socket is unhashed to avoid closed	 * socket sitting in hash tables.	 */	sk->sk_state = state;}EXPORT_SYMBOL_GPL(dccp_set_state);void dccp_done(struct sock *sk){	dccp_set_state(sk, DCCP_CLOSED);	dccp_clear_xmit_timers(sk);	sk->sk_shutdown = SHUTDOWN_MASK;	if (!sock_flag(sk, SOCK_DEAD))		sk->sk_state_change(sk);	else		inet_csk_destroy_sock(sk);}EXPORT_SYMBOL_GPL(dccp_done);const char *dccp_packet_name(const int type){	static const char *dccp_packet_names[] = {		[DCCP_PKT_REQUEST]  = "REQUEST",		[DCCP_PKT_RESPONSE] = "RESPONSE",		[DCCP_PKT_DATA]	    = "DATA",		[DCCP_PKT_ACK]	    = "ACK",		[DCCP_PKT_DATAACK]  = "DATAACK",		[DCCP_PKT_CLOSEREQ] = "CLOSEREQ",		[DCCP_PKT_CLOSE]    = "CLOSE",		[DCCP_PKT_RESET]    = "RESET",		[DCCP_PKT_SYNC]	    = "SYNC",		[DCCP_PKT_SYNCACK]  = "SYNCACK",	};	if (type >= DCCP_NR_PKT_TYPES)		return "INVALID";	else		return dccp_packet_names[type];}EXPORT_SYMBOL_GPL(dccp_packet_name);const char *dccp_state_name(const int state){	static char *dccp_state_names[] = {	[DCCP_OPEN]	  = "OPEN",	[DCCP_REQUESTING] = "REQUESTING",	[DCCP_PARTOPEN]	  = "PARTOPEN",	[DCCP_LISTEN]	  = "LISTEN",	[DCCP_RESPOND]	  = "RESPOND",	[DCCP_CLOSING]	  = "CLOSING",	[DCCP_TIME_WAIT]  = "TIME_WAIT",	[DCCP_CLOSED]	  = "CLOSED",	};	if (state >= DCCP_MAX_STATES)		return "INVALID STATE!";	else		return dccp_state_names[state];}EXPORT_SYMBOL_GPL(dccp_state_name);void dccp_hash(struct sock *sk){	inet_hash(&dccp_hashinfo, sk);}EXPORT_SYMBOL_GPL(dccp_hash);void dccp_unhash(struct sock *sk){	inet_unhash(&dccp_hashinfo, sk);}EXPORT_SYMBOL_GPL(dccp_unhash);int dccp_init_sock(struct sock *sk, const __u8 ctl_sock_initialized){	struct dccp_sock *dp = dccp_sk(sk);	struct dccp_minisock *dmsk = dccp_msk(sk);	struct inet_connection_sock *icsk = inet_csk(sk);	dccp_minisock_init(&dp->dccps_minisock);	/*	 * FIXME: We're hardcoding the CCID, and doing this at this point makes	 * the listening (master) sock get CCID control blocks, which is not	 * necessary, but for now, to not mess with the test userspace apps,	 * lets leave it here, later the real solution is to do this in a	 * setsockopt(CCIDs-I-want/accept). -acme	 */	if (likely(ctl_sock_initialized)) {		int rc = dccp_feat_init(dmsk);		if (rc)			return rc;		if (dmsk->dccpms_send_ack_vector) {			dp->dccps_hc_rx_ackvec = dccp_ackvec_alloc(GFP_KERNEL);			if (dp->dccps_hc_rx_ackvec == NULL)				return -ENOMEM;		}		dp->dccps_hc_rx_ccid = ccid_hc_rx_new(dmsk->dccpms_rx_ccid,						      sk, GFP_KERNEL);		dp->dccps_hc_tx_ccid = ccid_hc_tx_new(dmsk->dccpms_tx_ccid,						      sk, GFP_KERNEL);		if (unlikely(dp->dccps_hc_rx_ccid == NULL ||			     dp->dccps_hc_tx_ccid == NULL)) {			ccid_hc_rx_delete(dp->dccps_hc_rx_ccid, sk);			ccid_hc_tx_delete(dp->dccps_hc_tx_ccid, sk);			if (dmsk->dccpms_send_ack_vector) {				dccp_ackvec_free(dp->dccps_hc_rx_ackvec);				dp->dccps_hc_rx_ackvec = NULL;			}			dp->dccps_hc_rx_ccid = dp->dccps_hc_tx_ccid = NULL;			return -ENOMEM;		}	} else {		/* control socket doesn't need feat nego */		INIT_LIST_HEAD(&dmsk->dccpms_pending);		INIT_LIST_HEAD(&dmsk->dccpms_conf);	}	dccp_init_xmit_timers(sk);	icsk->icsk_rto		= DCCP_TIMEOUT_INIT;	icsk->icsk_syn_retries	= sysctl_dccp_request_retries;	sk->sk_state		= DCCP_CLOSED;	sk->sk_write_space	= dccp_write_space;	icsk->icsk_sync_mss	= dccp_sync_mss;	dp->dccps_mss_cache	= 536;	dp->dccps_rate_last	= jiffies;	dp->dccps_role		= DCCP_ROLE_UNDEFINED;	dp->dccps_service	= DCCP_SERVICE_CODE_IS_ABSENT;	dp->dccps_l_ack_ratio	= dp->dccps_r_ack_ratio = 1;	return 0;}EXPORT_SYMBOL_GPL(dccp_init_sock);int dccp_destroy_sock(struct sock *sk){	struct dccp_sock *dp = dccp_sk(sk);	struct dccp_minisock *dmsk = dccp_msk(sk);	/*	 * DCCP doesn't use sk_write_queue, just sk_send_head	 * for retransmissions	 */	if (sk->sk_send_head != NULL) {		kfree_skb(sk->sk_send_head);		sk->sk_send_head = NULL;	}	/* Clean up a referenced DCCP bind bucket. */	if (inet_csk(sk)->icsk_bind_hash != NULL)		inet_put_port(&dccp_hashinfo, sk);	kfree(dp->dccps_service_list);	dp->dccps_service_list = NULL;	if (dmsk->dccpms_send_ack_vector) {		dccp_ackvec_free(dp->dccps_hc_rx_ackvec);		dp->dccps_hc_rx_ackvec = NULL;	}	ccid_hc_rx_delete(dp->dccps_hc_rx_ccid, sk);	ccid_hc_tx_delete(dp->dccps_hc_tx_ccid, sk);	dp->dccps_hc_rx_ccid = dp->dccps_hc_tx_ccid = NULL;	/* clean up feature negotiation state */	dccp_feat_clean(dmsk);	return 0;}EXPORT_SYMBOL_GPL(dccp_destroy_sock);static inline int dccp_listen_start(struct sock *sk, int backlog){	struct dccp_sock *dp = dccp_sk(sk);	dp->dccps_role = DCCP_ROLE_LISTEN;	return inet_csk_listen_start(sk, backlog);}int dccp_disconnect(struct sock *sk, int flags){	struct inet_connection_sock *icsk = inet_csk(sk);	struct inet_sock *inet = inet_sk(sk);	int err = 0;	const int old_state = sk->sk_state;	if (old_state != DCCP_CLOSED)		dccp_set_state(sk, DCCP_CLOSED);	/* ABORT function of RFC793 */	if (old_state == DCCP_LISTEN) {		inet_csk_listen_stop(sk);	/* FIXME: do the active reset thing */	} else if (old_state == DCCP_REQUESTING)		sk->sk_err = ECONNRESET;	dccp_clear_xmit_timers(sk);	__skb_queue_purge(&sk->sk_receive_queue);	if (sk->sk_send_head != NULL) {		__kfree_skb(sk->sk_send_head);		sk->sk_send_head = NULL;	}	inet->dport = 0;	if (!(sk->sk_userlocks & SOCK_BINDADDR_LOCK))		inet_reset_saddr(sk);	sk->sk_shutdown = 0;	sock_reset_flag(sk, SOCK_DONE);	icsk->icsk_backoff = 0;	inet_csk_delack_init(sk);	__sk_dst_reset(sk);	BUG_TRAP(!inet->num || icsk->icsk_bind_hash);	sk->sk_error_report(sk);	return err;}EXPORT_SYMBOL_GPL(dccp_disconnect);/* *	Wait for a DCCP event. * *	Note that we don't need to lock the socket, as the upper poll layers *	take care of normal races (between the test and the event) and we don't *	go look at any of the socket buffers directly. */unsigned int dccp_poll(struct file *file, struct socket *sock,		       poll_table *wait){	unsigned int mask;	struct sock *sk = sock->sk;	poll_wait(file, sk->sk_sleep, wait);	if (sk->sk_state == DCCP_LISTEN)		return inet_csk_listen_poll(sk);	/* Socket is not locked. We are protected from async events	   by poll logic and correct handling of state changes	   made by another threads is impossible in any case.	 */	mask = 0;	if (sk->sk_err)		mask = POLLERR;	if (sk->sk_shutdown == SHUTDOWN_MASK || sk->sk_state == DCCP_CLOSED)		mask |= POLLHUP;	if (sk->sk_shutdown & RCV_SHUTDOWN)		mask |= POLLIN | POLLRDNORM | POLLRDHUP;	/* Connected? */	if ((1 << sk->sk_state) & ~(DCCPF_REQUESTING | DCCPF_RESPOND)) {		if (atomic_read(&sk->sk_rmem_alloc) > 0)			mask |= POLLIN | POLLRDNORM;		if (!(sk->sk_shutdown & SEND_SHUTDOWN)) {			if (sk_stream_wspace(sk) >= sk_stream_min_wspace(sk)) {				mask |= POLLOUT | POLLWRNORM;			} else {  /* send SIGIO later */				set_bit(SOCK_ASYNC_NOSPACE,					&sk->sk_socket->flags);				set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);				/* Race breaker. If space is freed after				 * wspace test but before the flags are set,				 * IO signal will be lost.				 */				if (sk_stream_wspace(sk) >= sk_stream_min_wspace(sk))					mask |= POLLOUT | POLLWRNORM;			}		}	}	return mask;}EXPORT_SYMBOL_GPL(dccp_poll);int dccp_ioctl(struct sock *sk, int cmd, unsigned long arg){	int rc = -ENOTCONN;	lock_sock(sk);	if (sk->sk_state == DCCP_LISTEN)		goto out;	switch (cmd) {	case SIOCINQ: {		struct sk_buff *skb;		unsigned long amount = 0;		skb = skb_peek(&sk->sk_receive_queue);		if (skb != NULL) {			/*			 * We will only return the amount of this packet since			 * that is all that will be read.			 */			amount = skb->len;		}		rc = put_user(amount, (int __user *)arg);	}		break;	default:		rc = -ENOIOCTLCMD;		break;	}out:	release_sock(sk);	return rc;}EXPORT_SYMBOL_GPL(dccp_ioctl);static int dccp_setsockopt_service(struct sock *sk, const __be32 service,				   char __user *optval, int optlen){	struct dccp_sock *dp = dccp_sk(sk);	struct dccp_service_list *sl = NULL;	if (service == DCCP_SERVICE_INVALID_VALUE ||	    optlen > DCCP_SERVICE_LIST_MAX_LEN * sizeof(u32))		return -EINVAL;	if (optlen > sizeof(service)) {		sl = kmalloc(optlen, GFP_KERNEL);		if (sl == NULL)			return -ENOMEM;		sl->dccpsl_nr = optlen / sizeof(u32) - 1;		if (copy_from_user(sl->dccpsl_list,				   optval + sizeof(service),				   optlen - sizeof(service)) ||		    dccp_list_has_service(sl, DCCP_SERVICE_INVALID_VALUE)) {			kfree(sl);			return -EFAULT;		}	}	lock_sock(sk);	dp->dccps_service = service;	kfree(dp->dccps_service_list);	dp->dccps_service_list = sl;	release_sock(sk);	return 0;}/* byte 1 is feature.  the rest is the preference list */static int dccp_setsockopt_change(struct sock *sk, int type,				  struct dccp_so_feat __user *optval){	struct dccp_so_feat opt;	u8 *val;	int rc;	if (copy_from_user(&opt, optval, sizeof(opt)))		return -EFAULT;	val = kmalloc(opt.dccpsf_len, GFP_KERNEL);	if (!val)		return -ENOMEM;	if (copy_from_user(val, opt.dccpsf_val, opt.dccpsf_len)) {		rc = -EFAULT;		goto out_free_val;	}	rc = dccp_feat_change(dccp_msk(sk), type, opt.dccpsf_feat,			      val, opt.dccpsf_len, GFP_KERNEL);	if (rc)		goto out_free_val;out:	return rc;out_free_val:	kfree(val);	goto out;}static int do_dccp_setsockopt(struct sock *sk, int level, int optname,		char __user *optval, int optlen){	struct dccp_sock *dp = dccp_sk(sk);	int val, err = 0;	if (optlen < sizeof(int))		return -EINVAL;	if (get_user(val, (int __user *)optval))		return -EFAULT;	if (optname == DCCP_SOCKOPT_SERVICE)		return dccp_setsockopt_service(sk, val, optval, optlen);	lock_sock(sk);	switch (optname) {	case DCCP_SOCKOPT_PACKET_SIZE:		DCCP_WARN("sockopt(PACKET_SIZE) is deprecated: fix your app\n");		err = 0;		break;	case DCCP_SOCKOPT_CHANGE_L:		if (optlen != sizeof(struct dccp_so_feat))			err = -EINVAL;		else			err = dccp_setsockopt_change(sk, DCCPO_CHANGE_L,						     (struct dccp_so_feat __user *)						     optval);		break;	case DCCP_SOCKOPT_CHANGE_R:		if (optlen != sizeof(struct dccp_so_feat))			err = -EINVAL;		else			err = dccp_setsockopt_change(sk, DCCPO_CHANGE_R,						     (struct dccp_so_feat __user *)						     optval);		break;	case DCCP_SOCKOPT_SEND_CSCOV:	/* sender side, RFC 4340, sec. 9.2 */		if (val < 0 || val > 15)			err = -EINVAL;		else			dp->dccps_pcslen = val;		break;	case DCCP_SOCKOPT_RECV_CSCOV:	/* receiver side, RFC 4340 sec. 9.2.1 */		if (val < 0 || val > 15)			err = -EINVAL;		else {			dp->dccps_pcrlen = val;			/* FIXME: add feature negotiation,			 * ChangeL(MinimumChecksumCoverage, val) */		}		break;	default:		err = -ENOPROTOOPT;		break;	}	release_sock(sk);	return err;}int dccp_setsockopt(struct sock *sk, int level, int optname,		    char __user *optval, int optlen){	if (level != SOL_DCCP)		return inet_csk(sk)->icsk_af_ops->setsockopt(sk, level,							     optname, optval,							     optlen);	return do_dccp_setsockopt(sk, level, optname, optval, optlen);}EXPORT_SYMBOL_GPL(dccp_setsockopt);#ifdef CONFIG_COMPATint compat_dccp_setsockopt(struct sock *sk, int level, int optname,			   char __user *optval, int optlen){	if (level != SOL_DCCP)		return inet_csk_compat_setsockopt(sk, level, optname,						  optval, optlen);	return do_dccp_setsockopt(sk, level, optname, optval, optlen);}EXPORT_SYMBOL_GPL(compat_dccp_setsockopt);#endifstatic int dccp_getsockopt_service(struct sock *sk, int len,				   __be32 __user *optval,				   int __user *optlen){	const struct dccp_sock *dp = dccp_sk(sk);	const struct dccp_service_list *sl;	int err = -ENOENT, slen = 0, total_len = sizeof(u32);	lock_sock(sk);	if ((sl = dp->dccps_service_list) != NULL) {

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -