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

📄 af_llc.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
 *	@sock: Socket which connections arrive on. *	@newsock: Socket to move incoming connection to. *	@flags: User specified operational flags. * *	Accept a new incoming connection. *	Returns 0 upon success, negative otherwise. */static int llc_ui_accept(struct socket *sock, struct socket *newsock, int flags){	struct sock *sk = sock->sk, *newsk;	struct llc_sock *llc, *newllc;	struct sk_buff *skb;	int rc = -EOPNOTSUPP;	dprintk("%s: accepting on %02X\n", __FUNCTION__,		llc_sk(sk)->laddr.lsap);	lock_sock(sk);	if (unlikely(sk->sk_type != SOCK_STREAM))		goto out;	rc = -EINVAL;	if (unlikely(sock->state != SS_UNCONNECTED ||		     sk->sk_state != TCP_LISTEN))		goto out;	/* wait for a connection to arrive. */	if (skb_queue_empty(&sk->sk_receive_queue)) {		rc = llc_wait_data(sk, sk->sk_rcvtimeo);		if (rc)			goto out;	}	dprintk("%s: got a new connection on %02X\n", __FUNCTION__,		llc_sk(sk)->laddr.lsap);	skb = skb_dequeue(&sk->sk_receive_queue);	rc = -EINVAL;	if (!skb->sk)		goto frees;	rc = 0;	newsk = skb->sk;	/* attach connection to a new socket. */	llc_ui_sk_init(newsock, newsk);	sock_reset_flag(newsk, SOCK_ZAPPED);	newsk->sk_state		= TCP_ESTABLISHED;	newsock->state		= SS_CONNECTED;	llc			= llc_sk(sk);	newllc			= llc_sk(newsk);	memcpy(&newllc->addr, &llc->addr, sizeof(newllc->addr));	newllc->link = llc_ui_next_link_no(newllc->laddr.lsap);	/* put original socket back into a clean listen state. */	sk->sk_state = TCP_LISTEN;	sk->sk_ack_backlog--;	dprintk("%s: ok success on %02X, client on %02X\n", __FUNCTION__,		llc_sk(sk)->addr.sllc_sap, newllc->daddr.lsap);frees:	kfree_skb(skb);out:	release_sock(sk);	return rc;}/** *	llc_ui_recvmsg - copy received data to the socket user. *	@sock: Socket to copy data from. *	@msg: Various user space related information. *	@len: Size of user buffer. *	@flags: User specified flags. * *	Copy received data to the socket user. *	Returns non-negative upon success, negative otherwise. */static int llc_ui_recvmsg(struct kiocb *iocb, struct socket *sock,			  struct msghdr *msg, size_t len, int flags){	struct sockaddr_llc *uaddr = (struct sockaddr_llc *)msg->msg_name;	const int nonblock = flags & MSG_DONTWAIT;	struct sk_buff *skb = NULL;	struct sock *sk = sock->sk;	struct llc_sock *llc = llc_sk(sk);	size_t copied = 0;	u32 peek_seq = 0;	u32 *seq;	unsigned long used;	int target;	/* Read at least this many bytes */	long timeo;	lock_sock(sk);	copied = -ENOTCONN;	if (unlikely(sk->sk_type == SOCK_STREAM && sk->sk_state == TCP_LISTEN))		goto out;	timeo = sock_rcvtimeo(sk, nonblock);	seq = &llc->copied_seq;	if (flags & MSG_PEEK) {		peek_seq = llc->copied_seq;		seq = &peek_seq;	}	target = sock_rcvlowat(sk, flags & MSG_WAITALL, len);	copied = 0;	do {		u32 offset;		/*		 * We need to check signals first, to get correct SIGURG		 * handling. FIXME: Need to check this doesn't impact 1003.1g		 * and move it down to the bottom of the loop		 */		if (signal_pending(current)) {			if (copied)				break;			copied = timeo ? sock_intr_errno(timeo) : -EAGAIN;			break;		}		/* Next get a buffer. */		skb = skb_peek(&sk->sk_receive_queue);		if (skb) {			offset = *seq;			goto found_ok_skb;		}		/* Well, if we have backlog, try to process it now yet. */		if (copied >= target && !sk->sk_backlog.tail)			break;		if (copied) {			if (sk->sk_err ||			    sk->sk_state == TCP_CLOSE ||			    (sk->sk_shutdown & RCV_SHUTDOWN) ||			    !timeo ||			    (flags & MSG_PEEK))				break;		} else {			if (sock_flag(sk, SOCK_DONE))				break;			if (sk->sk_err) {				copied = sock_error(sk);				break;			}			if (sk->sk_shutdown & RCV_SHUTDOWN)				break;			if (sk->sk_type == SOCK_STREAM && sk->sk_state == TCP_CLOSE) {				if (!sock_flag(sk, SOCK_DONE)) {					/*					 * This occurs when user tries to read					 * from never connected socket.					 */					copied = -ENOTCONN;					break;				}				break;			}			if (!timeo) {				copied = -EAGAIN;				break;			}		}		if (copied >= target) { /* Do not sleep, just process backlog. */			release_sock(sk);			lock_sock(sk);		} else			sk_wait_data(sk, &timeo);		if ((flags & MSG_PEEK) && peek_seq != llc->copied_seq) {			if (net_ratelimit())				printk(KERN_DEBUG "LLC(%s:%d): Application "						  "bug, race in MSG_PEEK.\n",				       current->comm, task_pid_nr(current));			peek_seq = llc->copied_seq;		}		continue;	found_ok_skb:		/* Ok so how much can we use? */		used = skb->len - offset;		if (len < used)			used = len;		if (!(flags & MSG_TRUNC)) {			int rc = skb_copy_datagram_iovec(skb, offset,							 msg->msg_iov, used);			if (rc) {				/* Exception. Bailout! */				if (!copied)					copied = -EFAULT;				break;			}		}		*seq += used;		copied += used;		len -= used;		if (!(flags & MSG_PEEK)) {			sk_eat_skb(sk, skb, 0);			*seq = 0;		}		/* For non stream protcols we get one packet per recvmsg call */		if (sk->sk_type != SOCK_STREAM)			goto copy_uaddr;		/* Partial read */		if (used + offset < skb->len)			continue;	} while (len > 0);out:	release_sock(sk);	return copied;copy_uaddr:	if (uaddr != NULL && skb != NULL) {		memcpy(uaddr, llc_ui_skb_cb(skb), sizeof(*uaddr));		msg->msg_namelen = sizeof(*uaddr);	}	goto out;}/** *	llc_ui_sendmsg - Transmit data provided by the socket user. *	@sock: Socket to transmit data from. *	@msg: Various user related information. *	@len: Length of data to transmit. * *	Transmit data provided by the socket user. *	Returns non-negative upon success, negative otherwise. */static int llc_ui_sendmsg(struct kiocb *iocb, struct socket *sock,			  struct msghdr *msg, size_t len){	struct sock *sk = sock->sk;	struct llc_sock *llc = llc_sk(sk);	struct sockaddr_llc *addr = (struct sockaddr_llc *)msg->msg_name;	int flags = msg->msg_flags;	int noblock = flags & MSG_DONTWAIT;	struct sk_buff *skb;	size_t size = 0;	int rc = -EINVAL, copied = 0, hdrlen;	dprintk("%s: sending from %02X to %02X\n", __FUNCTION__,		llc->laddr.lsap, llc->daddr.lsap);	lock_sock(sk);	if (addr) {		if (msg->msg_namelen < sizeof(*addr))			goto release;	} else {		if (llc_ui_addr_null(&llc->addr))			goto release;		addr = &llc->addr;	}	/* must bind connection to sap if user hasn't done it. */	if (sock_flag(sk, SOCK_ZAPPED)) {		/* bind to sap with null dev, exclusive. */		rc = llc_ui_autobind(sock, addr);		if (rc)			goto release;	}	hdrlen = llc->dev->hard_header_len + llc_ui_header_len(sk, addr);	size = hdrlen + len;	if (size > llc->dev->mtu)		size = llc->dev->mtu;	copied = size - hdrlen;	release_sock(sk);	skb = sock_alloc_send_skb(sk, size, noblock, &rc);	lock_sock(sk);	if (!skb)		goto release;	skb->dev      = llc->dev;	skb->protocol = llc_proto_type(addr->sllc_arphrd);	skb_reserve(skb, hdrlen);	rc = memcpy_fromiovec(skb_put(skb, copied), msg->msg_iov, copied);	if (rc)		goto out;	if (sk->sk_type == SOCK_DGRAM || addr->sllc_ua) {		llc_build_and_send_ui_pkt(llc->sap, skb, addr->sllc_mac,					  addr->sllc_sap);		goto out;	}	if (addr->sllc_test) {		llc_build_and_send_test_pkt(llc->sap, skb, addr->sllc_mac,					    addr->sllc_sap);		goto out;	}	if (addr->sllc_xid) {		llc_build_and_send_xid_pkt(llc->sap, skb, addr->sllc_mac,					   addr->sllc_sap);		goto out;	}	rc = -ENOPROTOOPT;	if (!(sk->sk_type == SOCK_STREAM && !addr->sllc_ua))		goto out;	rc = llc_ui_send_data(sk, skb, noblock);out:	if (rc) {		kfree_skb(skb);release:		dprintk("%s: failed sending from %02X to %02X: %d\n",			__FUNCTION__, llc->laddr.lsap, llc->daddr.lsap, rc);	}	release_sock(sk);	return rc ? : copied;}/** *	llc_ui_getname - return the address info of a socket *	@sock: Socket to get address of. *	@uaddr: Address structure to return information. *	@uaddrlen: Length of address structure. *	@peer: Does user want local or remote address information. * *	Return the address information of a socket. */static int llc_ui_getname(struct socket *sock, struct sockaddr *uaddr,			  int *uaddrlen, int peer){	struct sockaddr_llc sllc;	struct sock *sk = sock->sk;	struct llc_sock *llc = llc_sk(sk);	int rc = 0;	lock_sock(sk);	if (sock_flag(sk, SOCK_ZAPPED))		goto out;	*uaddrlen = sizeof(sllc);	memset(uaddr, 0, *uaddrlen);	if (peer) {		rc = -ENOTCONN;		if (sk->sk_state != TCP_ESTABLISHED)			goto out;		if(llc->dev)			sllc.sllc_arphrd = llc->dev->type;		sllc.sllc_sap = llc->daddr.lsap;		memcpy(&sllc.sllc_mac, &llc->daddr.mac, IFHWADDRLEN);	} else {		rc = -EINVAL;		if (!llc->sap)			goto out;		sllc.sllc_sap = llc->sap->laddr.lsap;		if (llc->dev) {			sllc.sllc_arphrd = llc->dev->type;			memcpy(&sllc.sllc_mac, &llc->dev->dev_addr,			       IFHWADDRLEN);		}	}	rc = 0;	sllc.sllc_family = AF_LLC;	memcpy(uaddr, &sllc, sizeof(sllc));out:	release_sock(sk);	return rc;}/** *	llc_ui_ioctl - io controls for PF_LLC *	@sock: Socket to get/set info *	@cmd: command *	@arg: optional argument for cmd * *	get/set info on llc sockets */static int llc_ui_ioctl(struct socket *sock, unsigned int cmd,			unsigned long arg){	return -ENOIOCTLCMD;}/** *	llc_ui_setsockopt - set various connection specific parameters. *	@sock: Socket to set options on. *	@level: Socket level user is requesting operations on. *	@optname: Operation name. *	@optval User provided operation data. *	@optlen: Length of optval. * *	Set various connection specific parameters. */static int llc_ui_setsockopt(struct socket *sock, int level, int optname,			     char __user *optval, int optlen){	struct sock *sk = sock->sk;	struct llc_sock *llc = llc_sk(sk);	int rc = -EINVAL, opt;	lock_sock(sk);	if (unlikely(level != SOL_LLC || optlen != sizeof(int)))		goto out;	rc = get_user(opt, (int __user *)optval);	if (rc)		goto out;	rc = -EINVAL;	switch (optname) {	case LLC_OPT_RETRY:		if (opt > LLC_OPT_MAX_RETRY)			goto out;		llc->n2 = opt;		break;	case LLC_OPT_SIZE:		if (opt > LLC_OPT_MAX_SIZE)			goto out;		llc->n1 = opt;		break;	case LLC_OPT_ACK_TMR_EXP:		if (opt > LLC_OPT_MAX_ACK_TMR_EXP)			goto out;		llc->ack_timer.expire = opt * HZ;		break;	case LLC_OPT_P_TMR_EXP:		if (opt > LLC_OPT_MAX_P_TMR_EXP)			goto out;		llc->pf_cycle_timer.expire = opt * HZ;		break;	case LLC_OPT_REJ_TMR_EXP:		if (opt > LLC_OPT_MAX_REJ_TMR_EXP)			goto out;		llc->rej_sent_timer.expire = opt * HZ;		break;	case LLC_OPT_BUSY_TMR_EXP:		if (opt > LLC_OPT_MAX_BUSY_TMR_EXP)			goto out;		llc->busy_state_timer.expire = opt * HZ;		break;	case LLC_OPT_TX_WIN:		if (opt > LLC_OPT_MAX_WIN)			goto out;		llc->k = opt;		break;	case LLC_OPT_RX_WIN:		if (opt > LLC_OPT_MAX_WIN)			goto out;		llc->rw = opt;		break;	default:		rc = -ENOPROTOOPT;		goto out;	}	rc = 0;out:	release_sock(sk);	return rc;}/** *	llc_ui_getsockopt - get connection specific socket info *	@sock: Socket to get information from. *	@level: Socket level user is requesting operations on. *	@optname: Operation name. *	@optval: Variable to return operation data in. *	@optlen: Length of optval. * *	Get connection specific socket information. */static int llc_ui_getsockopt(struct socket *sock, int level, int optname,			     char __user *optval, int __user *optlen){	struct sock *sk = sock->sk;	struct llc_sock *llc = llc_sk(sk);	int val = 0, len = 0, rc = -EINVAL;	lock_sock(sk);	if (unlikely(level != SOL_LLC))		goto out;	rc = get_user(len, optlen);	if (rc)		goto out;	rc = -EINVAL;	if (len != sizeof(int))		goto out;	switch (optname) {	case LLC_OPT_RETRY:		val = llc->n2;					break;	case LLC_OPT_SIZE:		val = llc->n1;					break;	case LLC_OPT_ACK_TMR_EXP:		val = llc->ack_timer.expire / HZ;		break;	case LLC_OPT_P_TMR_EXP:		val = llc->pf_cycle_timer.expire / HZ;		break;	case LLC_OPT_REJ_TMR_EXP:		val = llc->rej_sent_timer.expire / HZ;		break;	case LLC_OPT_BUSY_TMR_EXP:		val = llc->busy_state_timer.expire / HZ;	break;	case LLC_OPT_TX_WIN:		val = llc->k;				break;	case LLC_OPT_RX_WIN:		val = llc->rw;				break;	default:		rc = -ENOPROTOOPT;		goto out;	}	rc = 0;	if (put_user(len, optlen) || copy_to_user(optval, &val, len))		rc = -EFAULT;out:	release_sock(sk);	return rc;}static struct net_proto_family llc_ui_family_ops = {	.family = PF_LLC,	.create = llc_ui_create,	.owner	= THIS_MODULE,};static const struct proto_ops llc_ui_ops = {	.family	     = PF_LLC,	.owner       = THIS_MODULE,	.release     = llc_ui_release,	.bind	     = llc_ui_bind,	.connect     = llc_ui_connect,	.socketpair  = sock_no_socketpair,	.accept      = llc_ui_accept,	.getname     = llc_ui_getname,	.poll	     = datagram_poll,	.ioctl       = llc_ui_ioctl,	.listen      = llc_ui_listen,	.shutdown    = llc_ui_shutdown,	.setsockopt  = llc_ui_setsockopt,	.getsockopt  = llc_ui_getsockopt,	.sendmsg     = llc_ui_sendmsg,	.recvmsg     = llc_ui_recvmsg,	.mmap	     = sock_no_mmap,	.sendpage    = sock_no_sendpage,};static char llc_proc_err_msg[] __initdata =	KERN_CRIT "LLC: Unable to register the proc_fs entries\n";static char llc_sysctl_err_msg[] __initdata =	KERN_CRIT "LLC: Unable to register the sysctl entries\n";static char llc_sock_err_msg[] __initdata =	KERN_CRIT "LLC: Unable to register the network family\n";static int __init llc2_init(void){	int rc = proto_register(&llc_proto, 0);	if (rc != 0)		goto out;	llc_build_offset_table();	llc_station_init();	llc_ui_sap_last_autoport = LLC_SAP_DYN_START;	rc = llc_proc_init();	if (rc != 0) {		printk(llc_proc_err_msg);		goto out_unregister_llc_proto;	}	rc = llc_sysctl_init();	if (rc) {		printk(llc_sysctl_err_msg);		goto out_proc;	}	rc = sock_register(&llc_ui_family_ops);	if (rc) {		printk(llc_sock_err_msg);		goto out_sysctl;	}	llc_add_pack(LLC_DEST_SAP, llc_sap_handler);	llc_add_pack(LLC_DEST_CONN, llc_conn_handler);out:	return rc;out_sysctl:	llc_sysctl_exit();out_proc:	llc_proc_exit();out_unregister_llc_proto:	proto_unregister(&llc_proto);	goto out;}static void __exit llc2_exit(void){	llc_station_exit();	llc_remove_pack(LLC_DEST_SAP);	llc_remove_pack(LLC_DEST_CONN);	sock_unregister(PF_LLC);	llc_proc_exit();	llc_sysctl_exit();	proto_unregister(&llc_proto);}module_init(llc2_init);module_exit(llc2_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("Procom 1997, Jay Schullist 2001, Arnaldo C. Melo 2001-2003");MODULE_DESCRIPTION("IEEE 802.2 PF_LLC support");MODULE_ALIAS_NETPROTO(PF_LLC);

⌨️ 快捷键说明

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