📄 af_netlink.c
字号:
} 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 + -