af_netlink.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,257 行 · 第 1/2 页

C
1,257
字号
		sock_hold(sk);		if (skb2 == NULL) {			if (atomic_read(&skb->users) != 1) {				skb2 = skb_clone(skb, allocation);			} else {				skb2 = skb;				atomic_inc(&skb->users);			}		}		if (skb2 == NULL) {			netlink_overrun(sk);			/* Clone failed. Notify ALL listeners. */			failure = 1;		} else if (netlink_broadcast_deliver(sk, skb2)) {			netlink_overrun(sk);		} else {			delivered = 1;			skb2 = NULL;		}		sock_put(sk);	}	netlink_unlock_table();	if (skb2)		kfree_skb(skb2);	kfree_skb(skb);	if (delivered)		return 0;	if (failure)		return -ENOBUFS;	return -ESRCH;}void netlink_set_err(struct sock *ssk, u32 pid, u32 group, int code){	struct sock *sk;	struct hlist_node *node;	int protocol = ssk->sk_protocol;	read_lock(&nl_table_lock);	sk_for_each(sk, node, &nl_table[protocol]) {		struct netlink_opt *nlk = nlk_sk(sk);		if (ssk == sk)			continue;		if (nlk->pid == pid || !(nlk->groups & group))			continue;		sk->sk_err = code;		sk->sk_error_report(sk);	}	read_unlock(&nl_table_lock);}static inline void netlink_rcv_wake(struct sock *sk){	struct netlink_opt *nlk = nlk_sk(sk);	if (!skb_queue_len(&sk->sk_receive_queue))		clear_bit(0, &nlk->state);	if (!test_bit(0, &nlk->state))		wake_up_interruptible(&nlk->wait);}static int netlink_sendmsg(struct kiocb *kiocb, struct socket *sock,			   struct msghdr *msg, size_t len){	struct sock_iocb *siocb = kiocb_to_siocb(kiocb);	struct sock *sk = sock->sk;	struct netlink_opt *nlk = nlk_sk(sk);	struct sockaddr_nl *addr=msg->msg_name;	u32 dst_pid;	u32 dst_groups;	struct sk_buff *skb;	int err;	struct scm_cookie scm;	if (msg->msg_flags&MSG_OOB)		return -EOPNOTSUPP;	if (NULL == siocb->scm)		siocb->scm = &scm;	err = scm_send(sock, msg, siocb->scm);	if (err < 0)		return err;	if (msg->msg_namelen) {		if (addr->nl_family != AF_NETLINK)			return -EINVAL;		dst_pid = addr->nl_pid;		dst_groups = addr->nl_groups;		if (dst_groups && !netlink_capable(sock, NL_NONROOT_SEND))			return -EPERM;	} else {		dst_pid = nlk->dst_pid;		dst_groups = nlk->dst_groups;	}	if (!nlk->pid) {		err = netlink_autobind(sock);		if (err)			goto out;	}	err = -EMSGSIZE;	if (len > sk->sk_sndbuf - 32)		goto out;	err = -ENOBUFS;	skb = alloc_skb(len, GFP_KERNEL);	if (skb==NULL)		goto out;	NETLINK_CB(skb).pid	= nlk->pid;	NETLINK_CB(skb).groups	= nlk->groups;	NETLINK_CB(skb).dst_pid = dst_pid;	NETLINK_CB(skb).dst_groups = dst_groups;	memcpy(NETLINK_CREDS(skb), &siocb->scm->creds, sizeof(struct ucred));	/* What can I do? Netlink is asynchronous, so that	   we will have to save current capabilities to	   check them, when this message will be delivered	   to corresponding kernel module.   --ANK (980802)	 */	err = -EFAULT;	if (memcpy_fromiovec(skb_put(skb,len), msg->msg_iov, len)) {		kfree_skb(skb);		goto out;	}	err = security_netlink_send(sk, skb);	if (err) {		kfree_skb(skb);		goto out;	}	if (dst_groups) {		atomic_inc(&skb->users);		netlink_broadcast(sk, skb, dst_pid, dst_groups, GFP_KERNEL);	}	err = netlink_unicast(sk, skb, dst_pid, msg->msg_flags&MSG_DONTWAIT);out:	return err;}static int netlink_recvmsg(struct kiocb *kiocb, struct socket *sock,			   struct msghdr *msg, size_t len,			   int flags){	struct sock_iocb *siocb = kiocb_to_siocb(kiocb);	struct scm_cookie scm;	struct sock *sk = sock->sk;	struct netlink_opt *nlk = nlk_sk(sk);	int noblock = flags&MSG_DONTWAIT;	size_t copied;	struct sk_buff *skb;	int err;	if (flags&MSG_OOB)		return -EOPNOTSUPP;	copied = 0;	skb = skb_recv_datagram(sk,flags,noblock,&err);	if (skb==NULL)		goto out;	msg->msg_namelen = 0;	copied = skb->len;	if (len < copied) {		msg->msg_flags |= MSG_TRUNC;		copied = len;	}	skb->h.raw = skb->data;	err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);	if (msg->msg_name) {		struct sockaddr_nl *addr = (struct sockaddr_nl*)msg->msg_name;		addr->nl_family = AF_NETLINK;		addr->nl_pad    = 0;		addr->nl_pid	= NETLINK_CB(skb).pid;		addr->nl_groups	= NETLINK_CB(skb).dst_groups;		msg->msg_namelen = sizeof(*addr);	}	if (NULL == siocb->scm) {		memset(&scm, 0, sizeof(scm));		siocb->scm = &scm;	}	siocb->scm->creds = *NETLINK_CREDS(skb);	skb_free_datagram(sk, skb);	if (nlk->cb && atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf / 2)		netlink_dump(sk);	scm_recv(sock, msg, siocb->scm, flags);out:	netlink_rcv_wake(sk);	return err ? : copied;}static void netlink_data_ready(struct sock *sk, int len){	struct netlink_opt *nlk = nlk_sk(sk);	if (nlk->data_ready)		nlk->data_ready(sk, len);	netlink_rcv_wake(sk);}/* *	We export these functions to other modules. They provide a  *	complete set of kernel non-blocking support for message *	queueing. */struct sock *netlink_kernel_create(int unit, void (*input)(struct sock *sk, int len)){	struct socket *sock;	struct sock *sk;	if (unit<0 || unit>=MAX_LINKS)		return NULL;	if (sock_create_lite(PF_NETLINK, SOCK_DGRAM, unit, &sock))		return NULL;	if (netlink_create(sock, unit) < 0) {		sock_release(sock);		return NULL;	}	sk = sock->sk;	sk->sk_data_ready = netlink_data_ready;	if (input)		nlk_sk(sk)->data_ready = input;	netlink_insert(sk, 0);	return sk;}void netlink_set_nonroot(int protocol, unsigned flags){ 	if ((unsigned)protocol < MAX_LINKS) 		nl_nonroot[protocol] = flags;} static void netlink_destroy_callback(struct netlink_callback *cb){	if (cb->skb)		kfree_skb(cb->skb);	kfree(cb);}/* * It looks a bit ugly. * It would be better to create kernel thread. */static int netlink_dump(struct sock *sk){	struct netlink_opt *nlk = nlk_sk(sk);	struct netlink_callback *cb;	struct sk_buff *skb;	struct nlmsghdr *nlh;	int len;		skb = sock_rmalloc(sk, NLMSG_GOODSIZE, 0, GFP_KERNEL);	if (!skb)		return -ENOBUFS;	spin_lock(&nlk->cb_lock);	cb = nlk->cb;	if (cb == NULL) {		spin_unlock(&nlk->cb_lock);		kfree_skb(skb);		return -EINVAL;	}	len = cb->dump(skb, cb);	if (len > 0) {		spin_unlock(&nlk->cb_lock);		skb_queue_tail(&sk->sk_receive_queue, skb);		sk->sk_data_ready(sk, len);		return 0;	}	nlh = __nlmsg_put(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq, NLMSG_DONE, sizeof(int));	nlh->nlmsg_flags |= NLM_F_MULTI;	memcpy(NLMSG_DATA(nlh), &len, sizeof(len));	skb_queue_tail(&sk->sk_receive_queue, skb);	sk->sk_data_ready(sk, skb->len);	cb->done(cb);	nlk->cb = NULL;	spin_unlock(&nlk->cb_lock);	netlink_destroy_callback(cb);	sock_put(sk);	return 0;}int netlink_dump_start(struct sock *ssk, struct sk_buff *skb,		       struct nlmsghdr *nlh,		       int (*dump)(struct sk_buff *skb, struct netlink_callback*),		       int (*done)(struct netlink_callback*)){	struct netlink_callback *cb;	struct sock *sk;	struct netlink_opt *nlk;	cb = kmalloc(sizeof(*cb), GFP_KERNEL);	if (cb == NULL)		return -ENOBUFS;	memset(cb, 0, sizeof(*cb));	cb->dump = dump;	cb->done = done;	cb->nlh = nlh;	atomic_inc(&skb->users);	cb->skb = skb;	sk = netlink_lookup(ssk->sk_protocol, NETLINK_CB(skb).pid);	if (sk == NULL) {		netlink_destroy_callback(cb);		return -ECONNREFUSED;	}	nlk = nlk_sk(sk);	/* A dump is in progress... */	spin_lock(&nlk->cb_lock);	if (nlk->cb) {		spin_unlock(&nlk->cb_lock);		netlink_destroy_callback(cb);		sock_put(sk);		return -EBUSY;	}	nlk->cb = cb;	spin_unlock(&nlk->cb_lock);	netlink_dump(sk);	return 0;}void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err){	struct sk_buff *skb;	struct nlmsghdr *rep;	struct nlmsgerr *errmsg;	int size;	if (err == 0)		size = NLMSG_SPACE(sizeof(struct nlmsgerr));	else		size = NLMSG_SPACE(4 + NLMSG_ALIGN(nlh->nlmsg_len));	skb = alloc_skb(size, GFP_KERNEL);	if (!skb) {		struct sock *sk;		sk = netlink_lookup(in_skb->sk->sk_protocol,				    NETLINK_CB(in_skb).pid);		if (sk) {			sk->sk_err = ENOBUFS;			sk->sk_error_report(sk);			sock_put(sk);		}		return;	}	rep = __nlmsg_put(skb, NETLINK_CB(in_skb).pid, nlh->nlmsg_seq,			  NLMSG_ERROR, sizeof(struct nlmsgerr));	errmsg = NLMSG_DATA(rep);	errmsg->error = err;	memcpy(&errmsg->msg, nlh, err ? nlh->nlmsg_len : sizeof(struct nlmsghdr));	netlink_unicast(in_skb->sk, skb, NETLINK_CB(in_skb).pid, MSG_DONTWAIT);}#ifdef NL_EMULATE_DEVstatic rwlock_t nl_emu_lock = RW_LOCK_UNLOCKED;/* *	Backward compatibility. */	 int netlink_attach(int unit, int (*function)(int, struct sk_buff *skb)){	struct sock *sk = netlink_kernel_create(unit, NULL);	if (sk == NULL)		return -ENOBUFS;	nlk_sk(sk)->handler = function;	write_lock_bh(&nl_emu_lock);	netlink_kernel[unit] = sk->sk_socket;	write_unlock_bh(&nl_emu_lock);	return 0;}void netlink_detach(int unit){	struct socket *sock;	write_lock_bh(&nl_emu_lock);	sock = netlink_kernel[unit];	netlink_kernel[unit] = NULL;	write_unlock_bh(&nl_emu_lock);	sock_release(sock);}int netlink_post(int unit, struct sk_buff *skb){	struct socket *sock;	read_lock(&nl_emu_lock);	sock = netlink_kernel[unit];	if (sock) {		struct sock *sk = sock->sk;		memset(skb->cb, 0, sizeof(skb->cb));		sock_hold(sk);		read_unlock(&nl_emu_lock);		netlink_broadcast(sk, skb, 0, ~0, GFP_ATOMIC);		sock_put(sk);		return 0;	}	read_unlock(&nl_emu_lock);	return -EUNATCH;}#endif#ifdef CONFIG_PROC_FSstatic struct sock *netlink_seq_socket_idx(struct seq_file *seq, loff_t pos){	long i;	struct sock *s;	struct hlist_node *node;	loff_t off = 0;	for (i=0; i<MAX_LINKS; i++) {		sk_for_each(s, node, &nl_table[i]) {			if (off == pos) {				seq->private = (void *) i;				return s;			}			++off;		}	}	return NULL;}static void *netlink_seq_start(struct seq_file *seq, loff_t *pos){	read_lock(&nl_table_lock);	return *pos ? netlink_seq_socket_idx(seq, *pos - 1) : SEQ_START_TOKEN;}static void *netlink_seq_next(struct seq_file *seq, void *v, loff_t *pos){	struct sock *s;	++*pos;	if (v == SEQ_START_TOKEN)		return netlink_seq_socket_idx(seq, 0);			s = sk_next(v);	if (!s) {		long i = (long)seq->private;		while (++i < MAX_LINKS) {			s = sk_head(&nl_table[i]);			if (s) {				seq->private = (void *) i;				break;			}		}	}	return s;}static void netlink_seq_stop(struct seq_file *seq, void *v){	read_unlock(&nl_table_lock);}static int netlink_seq_show(struct seq_file *seq, void *v){	if (v == SEQ_START_TOKEN)		seq_puts(seq,			 "sk       Eth Pid    Groups   "			 "Rmem     Wmem     Dump     Locks\n");	else {		struct sock *s = v;		struct netlink_opt *nlk = nlk_sk(s);		seq_printf(seq, "%p %-3d %-6d %08x %-8d %-8d %p %d\n",			   s,			   s->sk_protocol,			   nlk->pid,			   nlk->groups,			   atomic_read(&s->sk_rmem_alloc),			   atomic_read(&s->sk_wmem_alloc),			   nlk->cb,			   atomic_read(&s->sk_refcnt)			);	}	return 0;}static struct seq_operations netlink_seq_ops = {	.start  = netlink_seq_start,	.next   = netlink_seq_next,	.stop   = netlink_seq_stop,	.show   = netlink_seq_show,};static int netlink_seq_open(struct inode *inode, struct file *file){	return seq_open(file, &netlink_seq_ops);}static struct file_operations netlink_seq_fops = {	.owner		= THIS_MODULE,	.open		= netlink_seq_open,	.read		= seq_read,	.llseek		= seq_lseek,	.release	= seq_release,};#endifint netlink_register_notifier(struct notifier_block *nb){	return notifier_chain_register(&netlink_chain, nb);}int netlink_unregister_notifier(struct notifier_block *nb){	return notifier_chain_unregister(&netlink_chain, nb);}                static struct proto_ops netlink_ops = {	.family =	PF_NETLINK,	.owner =	THIS_MODULE,	.release =	netlink_release,	.bind =		netlink_bind,	.connect =	netlink_connect,	.socketpair =	sock_no_socketpair,	.accept =	sock_no_accept,	.getname =	netlink_getname,	.poll =		datagram_poll,	.ioctl =	sock_no_ioctl,	.listen =	sock_no_listen,	.shutdown =	sock_no_shutdown,	.setsockopt =	sock_no_setsockopt,	.getsockopt =	sock_no_getsockopt,	.sendmsg =	netlink_sendmsg,	.recvmsg =	netlink_recvmsg,	.mmap =		sock_no_mmap,	.sendpage =	sock_no_sendpage,};static struct net_proto_family netlink_family_ops = {	.family = PF_NETLINK,	.create = netlink_create,	.owner	= THIS_MODULE,	/* for consistency 8) */};static int __init netlink_proto_init(void){	struct sk_buff *dummy_skb;	if (sizeof(struct netlink_skb_parms) > sizeof(dummy_skb->cb)) {		printk(KERN_CRIT "netlink_init: panic\n");		return -1;	}	sock_register(&netlink_family_ops);#ifdef CONFIG_PROC_FS	proc_net_fops_create("netlink", 0, &netlink_seq_fops);#endif	/* The netlink device handler may be needed early. */ 	rtnetlink_init();	return 0;}static void __exit netlink_proto_exit(void){       sock_unregister(PF_NETLINK);       proc_net_remove("netlink");}core_initcall(netlink_proto_init);module_exit(netlink_proto_exit);MODULE_LICENSE("GPL");MODULE_ALIAS_NETPROTO(PF_NETLINK);EXPORT_SYMBOL(netlink_ack);EXPORT_SYMBOL(netlink_broadcast);EXPORT_SYMBOL(netlink_dump_start);EXPORT_SYMBOL(netlink_kernel_create);EXPORT_SYMBOL(netlink_register_notifier);EXPORT_SYMBOL(netlink_set_err);EXPORT_SYMBOL(netlink_set_nonroot);EXPORT_SYMBOL(netlink_unicast);EXPORT_SYMBOL(netlink_unregister_notifier);#if defined(CONFIG_NETLINK_DEV) || defined(CONFIG_NETLINK_DEV_MODULE)EXPORT_SYMBOL(netlink_attach);EXPORT_SYMBOL(netlink_detach);EXPORT_SYMBOL(netlink_post);#endif

⌨️ 快捷键说明

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