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

📄 af_netlink.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
	}	siocb->scm->creds = *NETLINK_CREDS(skb);	if (flags & MSG_TRUNC)		copied = skb->len;	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){	BUG();}/* *	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(struct net *net, int unit, unsigned int groups,		      void (*input)(struct sk_buff *skb),		      struct mutex *cb_mutex, struct module *module){	struct socket *sock;	struct sock *sk;	struct netlink_sock *nlk;	unsigned long *listeners = NULL;	BUG_ON(!nl_table);	if (unit<0 || unit>=MAX_LINKS)		return NULL;	if (sock_create_lite(PF_NETLINK, SOCK_DGRAM, unit, &sock))		return NULL;	if (__netlink_create(net, sock, cb_mutex, unit) < 0)		goto out_sock_release;	if (groups < 32)		groups = 32;	listeners = kzalloc(NLGRPSZ(groups), GFP_KERNEL);	if (!listeners)		goto out_sock_release;	sk = sock->sk;	sk->sk_data_ready = netlink_data_ready;	if (input)		nlk_sk(sk)->netlink_rcv = input;	if (netlink_insert(sk, net, 0))		goto out_sock_release;	nlk = nlk_sk(sk);	nlk->flags |= NETLINK_KERNEL_SOCKET;	netlink_table_grab();	if (!nl_table[unit].registered) {		nl_table[unit].groups = groups;		nl_table[unit].listeners = listeners;		nl_table[unit].cb_mutex = cb_mutex;		nl_table[unit].module = module;		nl_table[unit].registered = 1;	} else {		kfree(listeners);	}	netlink_table_ungrab();	return sk;out_sock_release:	kfree(listeners);	sock_release(sock);	return NULL;}/** * netlink_change_ngroups - change number of multicast groups * * This changes the number of multicast groups that are available * on a certain netlink family. Note that it is not possible to * change the number of groups to below 32. Also note that it does * not implicitly call netlink_clear_multicast_users() when the * number of groups is reduced. * * @sk: The kernel netlink socket, as returned by netlink_kernel_create(). * @groups: The new number of groups. */int netlink_change_ngroups(struct sock *sk, unsigned int groups){	unsigned long *listeners, *old = NULL;	struct netlink_table *tbl = &nl_table[sk->sk_protocol];	int err = 0;	if (groups < 32)		groups = 32;	netlink_table_grab();	if (NLGRPSZ(tbl->groups) < NLGRPSZ(groups)) {		listeners = kzalloc(NLGRPSZ(groups), GFP_ATOMIC);		if (!listeners) {			err = -ENOMEM;			goto out_ungrab;		}		old = tbl->listeners;		memcpy(listeners, old, NLGRPSZ(tbl->groups));		rcu_assign_pointer(tbl->listeners, listeners);	}	tbl->groups = groups; out_ungrab:	netlink_table_ungrab();	synchronize_rcu();	kfree(old);	return err;}EXPORT_SYMBOL(netlink_change_ngroups);/** * netlink_clear_multicast_users - kick off multicast listeners * * This function removes all listeners from the given group. * @ksk: The kernel netlink socket, as returned by *	netlink_kernel_create(). * @group: The multicast group to clear. */void netlink_clear_multicast_users(struct sock *ksk, unsigned int group){	struct sock *sk;	struct hlist_node *node;	struct netlink_table *tbl = &nl_table[ksk->sk_protocol];	netlink_table_grab();	sk_for_each_bound(sk, node, &tbl->mc_list)		netlink_update_socket_mc(nlk_sk(sk), group, 0);	netlink_table_ungrab();}EXPORT_SYMBOL(netlink_clear_multicast_users);void netlink_set_nonroot(int protocol, unsigned int flags){	if ((unsigned int)protocol < MAX_LINKS)		nl_table[protocol].nl_nonroot = 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_sock *nlk = nlk_sk(sk);	struct netlink_callback *cb;	struct sk_buff *skb;	struct nlmsghdr *nlh;	int len, err = -ENOBUFS;	skb = sock_rmalloc(sk, NLMSG_GOODSIZE, 0, GFP_KERNEL);	if (!skb)		goto errout;	mutex_lock(nlk->cb_mutex);	cb = nlk->cb;	if (cb == NULL) {		err = -EINVAL;		goto errout_skb;	}	len = cb->dump(skb, cb);	if (len > 0) {		mutex_unlock(nlk->cb_mutex);		skb_queue_tail(&sk->sk_receive_queue, skb);		sk->sk_data_ready(sk, len);		return 0;	}	nlh = nlmsg_put_answer(skb, cb, NLMSG_DONE, sizeof(len), NLM_F_MULTI);	if (!nlh)		goto errout_skb;	memcpy(nlmsg_data(nlh), &len, sizeof(len));	skb_queue_tail(&sk->sk_receive_queue, skb);	sk->sk_data_ready(sk, skb->len);	if (cb->done)		cb->done(cb);	nlk->cb = NULL;	mutex_unlock(nlk->cb_mutex);	netlink_destroy_callback(cb);	return 0;errout_skb:	mutex_unlock(nlk->cb_mutex);	kfree_skb(skb);errout:	return err;}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_sock *nlk;	cb = kzalloc(sizeof(*cb), GFP_KERNEL);	if (cb == NULL)		return -ENOBUFS;	cb->dump = dump;	cb->done = done;	cb->nlh = nlh;	atomic_inc(&skb->users);	cb->skb = skb;	sk = netlink_lookup(ssk->sk_net, 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... */	mutex_lock(nlk->cb_mutex);	if (nlk->cb) {		mutex_unlock(nlk->cb_mutex);		netlink_destroy_callback(cb);		sock_put(sk);		return -EBUSY;	}	nlk->cb = cb;	mutex_unlock(nlk->cb_mutex);	netlink_dump(sk);	sock_put(sk);	/* We successfully started a dump, by returning -EINTR we	 * signal not to send ACK even if it was requested.	 */	return -EINTR;}void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err){	struct sk_buff *skb;	struct nlmsghdr *rep;	struct nlmsgerr *errmsg;	size_t payload = sizeof(*errmsg);	/* error messages get the original request appened */	if (err)		payload += nlmsg_len(nlh);	skb = nlmsg_new(payload, GFP_KERNEL);	if (!skb) {		struct sock *sk;		sk = netlink_lookup(in_skb->sk->sk_net,				    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), 0);	errmsg = nlmsg_data(rep);	errmsg->error = err;	memcpy(&errmsg->msg, nlh, err ? nlh->nlmsg_len : sizeof(*nlh));	netlink_unicast(in_skb->sk, skb, NETLINK_CB(in_skb).pid, MSG_DONTWAIT);}int netlink_rcv_skb(struct sk_buff *skb, int (*cb)(struct sk_buff *,						     struct nlmsghdr *)){	struct nlmsghdr *nlh;	int err;	while (skb->len >= nlmsg_total_size(0)) {		int msglen;		nlh = nlmsg_hdr(skb);		err = 0;		if (nlh->nlmsg_len < NLMSG_HDRLEN || skb->len < nlh->nlmsg_len)			return 0;		/* Only requests are handled by the kernel */		if (!(nlh->nlmsg_flags & NLM_F_REQUEST))			goto ack;		/* Skip control messages */		if (nlh->nlmsg_type < NLMSG_MIN_TYPE)			goto ack;		err = cb(skb, nlh);		if (err == -EINTR)			goto skip;ack:		if (nlh->nlmsg_flags & NLM_F_ACK || err)			netlink_ack(skb, nlh, err);skip:	        msglen = NLMSG_ALIGN(nlh->nlmsg_len);		if (msglen > skb->len)			msglen = skb->len;		skb_pull(skb, msglen);	}	return 0;}/** * nlmsg_notify - send a notification netlink message * @sk: netlink socket to use * @skb: notification message * @pid: destination netlink pid for reports or 0 * @group: destination multicast group or 0 * @report: 1 to report back, 0 to disable * @flags: allocation flags */int nlmsg_notify(struct sock *sk, struct sk_buff *skb, u32 pid,		 unsigned int group, int report, gfp_t flags){	int err = 0;	if (group) {		int exclude_pid = 0;		if (report) {			atomic_inc(&skb->users);			exclude_pid = pid;		}		/* errors reported via destination sk->sk_err */		nlmsg_multicast(sk, skb, exclude_pid, group, flags);	}	if (report)		err = nlmsg_unicast(sk, skb, pid);	return err;}#ifdef CONFIG_PROC_FSstruct nl_seq_iter {	struct net *net;	int link;	int hash_idx;};static struct sock *netlink_seq_socket_idx(struct seq_file *seq, loff_t pos){	struct nl_seq_iter *iter = seq->private;	int i, j;	struct sock *s;	struct hlist_node *node;	loff_t off = 0;	for (i=0; i<MAX_LINKS; i++) {		struct nl_pid_hash *hash = &nl_table[i].hash;		for (j = 0; j <= hash->mask; j++) {			sk_for_each(s, node, &hash->table[j]) {				if (iter->net != s->sk_net)					continue;				if (off == pos) {					iter->link = i;					iter->hash_idx = j;					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;	struct nl_seq_iter *iter;	int i, j;	++*pos;	if (v == SEQ_START_TOKEN)		return netlink_seq_socket_idx(seq, 0);	iter = seq->private;	s = v;	do {		s = sk_next(s);	} while (s && (iter->net != s->sk_net));	if (s)		return s;	i = iter->link;	j = iter->hash_idx + 1;	do {		struct nl_pid_hash *hash = &nl_table[i].hash;		for (; j <= hash->mask; j++) {			s = sk_head(&hash->table[j]);			while (s && (iter->net != s->sk_net))				s = sk_next(s);			if (s) {				iter->link = i;				iter->hash_idx = j;				return s;			}		}		j = 0;	} while (++i < MAX_LINKS);	return NULL;}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_sock *nlk = nlk_sk(s);		seq_printf(seq, "%p %-3d %-6d %08x %-8d %-8d %p %d\n",			   s,			   s->sk_protocol,			   nlk->pid,			   nlk->groups ? (u32)nlk->groups[0] : 0,			   atomic_read(&s->sk_rmem_alloc),			   atomic_read(&s->sk_wmem_alloc),			   nlk->cb,			   atomic_read(&s->sk_refcnt)			);	}	return 0;}static const 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){	struct nl_seq_iter *iter;	iter = __seq_open_private(file, &netlink_seq_ops, sizeof(*iter));	if (!iter)		return -ENOMEM;	iter->net = get_proc_net(inode);	if (!iter->net) {		seq_release_private(inode, file);		return -ENXIO;	}	return 0;}static int netlink_seq_release(struct inode *inode, struct file *file){	struct seq_file *seq = file->private_data;	struct nl_seq_iter *iter = seq->private;	put_net(iter->net);	return seq_release_private(inode, file);}static const struct file_operations netlink_seq_fops = {	.owner		= THIS_MODULE,	.open		= netlink_seq_open,	.read		= seq_read,	.llseek		= seq_lseek,	.release	= netlink_seq_release,};#endifint netlink_register_notifier(struct notifier_block *nb){	return atomic_notifier_chain_register(&netlink_chain, nb);}int netlink_unregister_notifier(struct notifier_block *nb){	return atomic_notifier_chain_unregister(&netlink_chain, nb);}static const 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 =	netlink_setsockopt,	.getsockopt =	netlink_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 __net_init netlink_net_init(struct net *net){#ifdef CONFIG_PROC_FS	if (!proc_net_fops_create(net, "netlink", 0, &netlink_seq_fops))		return -ENOMEM;#endif	return 0;}static void __net_exit netlink_net_exit(struct net *net){#ifdef CONFIG_PROC_FS	proc_net_remove(net, "netlink");#endif}static struct pernet_operations __net_initdata netlink_net_ops = {	.init = netlink_net_init,	.exit = netlink_net_exit,};static int __init netlink_proto_init(void){	struct sk_buff *dummy_skb;	int i;	unsigned long limit;	unsigned int order;	int err = proto_register(&netlink_proto, 0);	if (err != 0)		goto out;	BUILD_BUG_ON(sizeof(struct netlink_skb_parms) > sizeof(dummy_skb->cb));	nl_table = kcalloc(MAX_LINKS, sizeof(*nl_table), GFP_KERNEL);	if (!nl_table)		goto panic;	if (num_physpages >= (128 * 1024))		limit = num_physpages >> (21 - PAGE_SHIFT);	else		limit = num_physpages >> (23 - PAGE_SHIFT);	order = get_bitmask_order(limit) - 1 + PAGE_SHIFT;	limit = (1UL << order) / sizeof(struct hlist_head);	order = get_bitmask_order(min(limit, (unsigned long)UINT_MAX)) - 1;	for (i = 0; i < MAX_LINKS; i++) {		struct nl_pid_hash *hash = &nl_table[i].hash;		hash->table = nl_pid_hash_alloc(1 * sizeof(*hash->table));		if (!hash->table) {			while (i-- > 0)				nl_pid_hash_free(nl_table[i].hash.table,						 1 * sizeof(*hash->table));			kfree(nl_table);			goto panic;		}		memset(hash->table, 0, 1 * sizeof(*hash->table));		hash->max_shift = order;		hash->shift = 0;		hash->mask = 0;		hash->rehash_time = jiffies;	}	sock_register(&netlink_family_ops);	register_pernet_subsys(&netlink_net_ops);	/* The netlink device handler may be needed early. */	rtnetlink_init();out:	return err;panic:	panic("netlink_init: Cannot allocate nl_table\n");}core_initcall(netlink_proto_init);EXPORT_SYMBOL(netlink_ack);EXPORT_SYMBOL(netlink_rcv_skb);EXPORT_SYMBOL(netlink_broadcast);EXPORT_SYMBOL(netlink_dump_start);EXPORT_SYMBOL(netlink_kernel_create);EXPORT_SYMBOL(netlink_register_notifier);EXPORT_SYMBOL(netlink_set_nonroot);EXPORT_SYMBOL(netlink_unicast);EXPORT_SYMBOL(netlink_unregister_notifier);EXPORT_SYMBOL(nlmsg_notify);

⌨️ 快捷键说明

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