cregit-Linux how code gets into the kernel

Release 4.15 net/netlink/af_netlink.c

Directory: net/netlink
/*
 * NETLINK      Kernel-user communication protocol.
 *
 *              Authors:        Alan Cox <alan@lxorguk.ukuu.org.uk>
 *                              Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>
 *                              Patrick McHardy <kaber@trash.net>
 *
 *              This program is free software; you can redistribute it and/or
 *              modify it under the terms of the GNU General Public License
 *              as published by the Free Software Foundation; either version
 *              2 of the License, or (at your option) any later version.
 *
 * Tue Jun 26 14:36:48 MEST 2001 Herbert "herp" Rosmanith
 *                               added netlink_proto_exit
 * Tue Jan 22 18:32:44 BRST 2002 Arnaldo C. de Melo <acme@conectiva.com.br>
 *                               use nlk_sk, as sk->protinfo is on a diet 8)
 * Fri Jul 22 19:51:12 MEST 2005 Harald Welte <laforge@gnumonks.org>
 *                               - inc module use count of module that owns
 *                                 the kernel socket in case userspace opens
 *                                 socket of same protocol
 *                               - remove all module support, since netlink is
 *                                 mandatory if CONFIG_NET=y these days
 */

#include <linux/module.h>

#include <linux/capability.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/stat.h>
#include <linux/socket.h>
#include <linux/un.h>
#include <linux/fcntl.h>
#include <linux/termios.h>
#include <linux/sockios.h>
#include <linux/net.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
#include <linux/rtnetlink.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/notifier.h>
#include <linux/security.h>
#include <linux/jhash.h>
#include <linux/jiffies.h>
#include <linux/random.h>
#include <linux/bitops.h>
#include <linux/mm.h>
#include <linux/types.h>
#include <linux/audit.h>
#include <linux/mutex.h>
#include <linux/vmalloc.h>
#include <linux/if_arp.h>
#include <linux/rhashtable.h>
#include <asm/cacheflush.h>
#include <linux/hash.h>
#include <linux/genetlink.h>
#include <linux/net_namespace.h>

#include <net/net_namespace.h>
#include <net/sock.h>
#include <net/scm.h>
#include <net/netlink.h>

#include "af_netlink.h"


struct listeners {
	
struct rcu_head		rcu;
	
unsigned long		masks[0];
};

/* state bits */

#define NETLINK_S_CONGESTED		0x0


static inline int netlink_is_kernel(struct sock *sk) { return nlk_sk(sk)->flags & NETLINK_F_KERNEL_SOCKET; }

Contributors

PersonTokensPropCommitsCommitProp
Denis V. Lunev2195.45%150.00%
Nicolas Dichtel14.55%150.00%
Total22100.00%2100.00%

struct netlink_table *nl_table __read_mostly; EXPORT_SYMBOL_GPL(nl_table); static DECLARE_WAIT_QUEUE_HEAD(nl_table_wait); static struct lock_class_key nlk_cb_mutex_keys[MAX_LINKS]; static const char *const nlk_cb_mutex_key_strings[MAX_LINKS + 1] = { "nlk_cb_mutex-ROUTE", "nlk_cb_mutex-1", "nlk_cb_mutex-USERSOCK", "nlk_cb_mutex-FIREWALL", "nlk_cb_mutex-SOCK_DIAG", "nlk_cb_mutex-NFLOG", "nlk_cb_mutex-XFRM", "nlk_cb_mutex-SELINUX", "nlk_cb_mutex-ISCSI", "nlk_cb_mutex-AUDIT", "nlk_cb_mutex-FIB_LOOKUP", "nlk_cb_mutex-CONNECTOR", "nlk_cb_mutex-NETFILTER", "nlk_cb_mutex-IP6_FW", "nlk_cb_mutex-DNRTMSG", "nlk_cb_mutex-KOBJECT_UEVENT", "nlk_cb_mutex-GENERIC", "nlk_cb_mutex-17", "nlk_cb_mutex-SCSITRANSPORT", "nlk_cb_mutex-ECRYPTFS", "nlk_cb_mutex-RDMA", "nlk_cb_mutex-CRYPTO", "nlk_cb_mutex-SMC", "nlk_cb_mutex-23", "nlk_cb_mutex-24", "nlk_cb_mutex-25", "nlk_cb_mutex-26", "nlk_cb_mutex-27", "nlk_cb_mutex-28", "nlk_cb_mutex-29", "nlk_cb_mutex-30", "nlk_cb_mutex-31", "nlk_cb_mutex-MAX_LINKS" }; static int netlink_dump(struct sock *sk); /* nl_table locking explained: * Lookup and traversal are protected with an RCU read-side lock. Insertion * and removal are protected with per bucket lock while using RCU list * modification primitives and may run in parallel to RCU protected lookups. * Destruction of the Netlink socket may only occur *after* nl_table_lock has * been acquired * either during or after the socket has been removed from * the list and after an RCU grace period. */ DEFINE_RWLOCK(nl_table_lock); EXPORT_SYMBOL_GPL(nl_table_lock); static atomic_t nl_table_users = ATOMIC_INIT(0); #define nl_deref_protected(X) rcu_dereference_protected(X, lockdep_is_held(&nl_table_lock)); static BLOCKING_NOTIFIER_HEAD(netlink_chain); static DEFINE_SPINLOCK(netlink_tap_lock); static struct list_head netlink_tap_all __read_mostly; static const struct rhashtable_params netlink_rhashtable_params;
static inline u32 netlink_group_mask(u32 group) { return group ? 1 << (group - 1) : 0; }

Contributors

PersonTokensPropCommitsCommitProp
Patrick McHardy2295.65%150.00%
Stephen Hemminger14.35%150.00%
Total23100.00%2100.00%


static struct sk_buff *netlink_to_full_skb(const struct sk_buff *skb, gfp_t gfp_mask) { unsigned int len = skb_end_offset(skb); struct sk_buff *new; new = alloc_skb(len, gfp_mask); if (new == NULL) return NULL; NETLINK_CB(new).portid = NETLINK_CB(skb).portid; NETLINK_CB(new).dst_group = NETLINK_CB(skb).dst_group; NETLINK_CB(new).creds = NETLINK_CB(skb).creds; skb_put_data(new, skb->data, len); return new; }

Contributors

PersonTokensPropCommitsCommitProp
Daniel Borkmann10499.05%150.00%
Johannes Berg10.95%150.00%
Total105100.00%2100.00%


int netlink_add_tap(struct netlink_tap *nt) { if (unlikely(nt->dev->type != ARPHRD_NETLINK)) return -EINVAL; spin_lock(&netlink_tap_lock); list_add_rcu(&nt->list, &netlink_tap_all); spin_unlock(&netlink_tap_lock); __module_get(nt->module); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Daniel Borkmann60100.00%1100.00%
Total60100.00%1100.00%

EXPORT_SYMBOL_GPL(netlink_add_tap);
static int __netlink_remove_tap(struct netlink_tap *nt) { bool found = false; struct netlink_tap *tmp; spin_lock(&netlink_tap_lock); list_for_each_entry(tmp, &netlink_tap_all, list) { if (nt == tmp) { list_del_rcu(&nt->list); found = true; goto out; } } pr_warn("__netlink_remove_tap: %p not found\n", nt); out: spin_unlock(&netlink_tap_lock); if (found) module_put(nt->module); return found ? 0 : -ENODEV; }

Contributors

PersonTokensPropCommitsCommitProp
Daniel Borkmann9398.94%150.00%
Stephen Hemminger11.06%150.00%
Total94100.00%2100.00%


int netlink_remove_tap(struct netlink_tap *nt) { int ret; ret = __netlink_remove_tap(nt); synchronize_net(); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Daniel Borkmann26100.00%1100.00%
Total26100.00%1100.00%

EXPORT_SYMBOL_GPL(netlink_remove_tap);
static bool netlink_filter_tap(const struct sk_buff *skb) { struct sock *sk = skb->sk; /* We take the more conservative approach and * whitelist socket protocols that may pass. */ switch (sk->sk_protocol) { case NETLINK_ROUTE: case NETLINK_USERSOCK: case NETLINK_SOCK_DIAG: case NETLINK_NFLOG: case NETLINK_XFRM: case NETLINK_FIB_LOOKUP: case NETLINK_NETFILTER: case NETLINK_GENERIC: return true; } return false; }

Contributors

PersonTokensPropCommitsCommitProp
Daniel Borkmann5795.00%150.00%
Varka Bhadram35.00%150.00%
Total60100.00%2100.00%


static int __netlink_deliver_tap_skb(struct sk_buff *skb, struct net_device *dev) { struct sk_buff *nskb; struct sock *sk = skb->sk; int ret = -ENOMEM; if (!net_eq(dev_net(dev), sock_net(sk))) return 0; dev_hold(dev); if (is_vmalloc_addr(skb->head)) nskb = netlink_to_full_skb(skb, GFP_ATOMIC); else nskb = skb_clone(skb, GFP_ATOMIC); if (nskb) { nskb->dev = dev; nskb->protocol = htons((u16) sk->sk_protocol); nskb->pkt_type = netlink_is_kernel(sk) ? PACKET_KERNEL : PACKET_USER; skb_reset_network_header(nskb); ret = dev_queue_xmit(nskb); if (unlikely(ret > 0)) ret = net_xmit_errno(ret); } dev_put(dev); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Daniel Borkmann14488.34%583.33%
Kevin Cernekee1911.66%116.67%
Total163100.00%6100.00%


static void __netlink_deliver_tap(struct sk_buff *skb) { int ret; struct netlink_tap *tmp; if (!netlink_filter_tap(skb)) return; list_for_each_entry_rcu(tmp, &netlink_tap_all, list) { ret = __netlink_deliver_tap_skb(skb, tmp->dev); if (unlikely(ret)) break; } }

Contributors

PersonTokensPropCommitsCommitProp
Daniel Borkmann57100.00%2100.00%
Total57100.00%2100.00%


static void netlink_deliver_tap(struct sk_buff *skb) { rcu_read_lock(); if (unlikely(!list_empty(&netlink_tap_all))) __netlink_deliver_tap(skb); rcu_read_unlock(); }

Contributors

PersonTokensPropCommitsCommitProp
Daniel Borkmann34100.00%1100.00%
Total34100.00%1100.00%


static void netlink_deliver_tap_kernel(struct sock *dst, struct sock *src, struct sk_buff *skb) { if (!(netlink_is_kernel(dst) && netlink_is_kernel(src))) netlink_deliver_tap(skb); }

Contributors

PersonTokensPropCommitsCommitProp
Daniel Borkmann41100.00%1100.00%
Total41100.00%1100.00%


static void netlink_overrun(struct sock *sk) { struct netlink_sock *nlk = nlk_sk(sk); if (!(nlk->flags & NETLINK_F_RECV_NO_ENOBUFS)) { if (!test_and_set_bit(NETLINK_S_CONGESTED, &nlk_sk(sk)->state)) { sk->sk_err = ENOBUFS; sk->sk_error_report(sk); } } atomic_inc(&sk->sk_drops); }

Contributors

PersonTokensPropCommitsCommitProp
Patrick McHardy7197.26%150.00%
Nicolas Dichtel22.74%150.00%
Total73100.00%2100.00%


static void netlink_rcv_wake(struct sock *sk) { struct netlink_sock *nlk = nlk_sk(sk); if (skb_queue_empty(&sk->sk_receive_queue)) clear_bit(NETLINK_S_CONGESTED, &nlk->state); if (!test_bit(NETLINK_S_CONGESTED, &nlk->state)) wake_up_interruptible(&nlk->wait); }

Contributors

PersonTokensPropCommitsCommitProp
Patrick McHardy6096.77%150.00%
Nicolas Dichtel23.23%150.00%
Total62100.00%2100.00%


static void netlink_skb_destructor(struct sk_buff *skb) { if (is_vmalloc_addr(skb->head)) { if (!skb->cloned || !atomic_dec_return(&(skb_shinfo(skb)->dataref))) vfree(skb->head); skb->head = NULL; } if (skb->sk != NULL) sock_rfree(skb); }

Contributors

PersonTokensPropCommitsCommitProp
Florian Westphal5782.61%150.00%
Patrick McHardy1217.39%150.00%
Total69100.00%2100.00%


static void netlink_skb_set_owner_r(struct sk_buff *skb, struct sock *sk) { WARN_ON(skb->sk != NULL); skb->sk = sk; skb->destructor = netlink_skb_destructor; atomic_add(skb->truesize, &sk->sk_rmem_alloc); sk_mem_charge(sk, skb->truesize); }

Contributors

PersonTokensPropCommitsCommitProp
Florian Westphal4068.97%133.33%
Patrick McHardy1831.03%266.67%
Total58100.00%3100.00%


static void netlink_sock_destruct(struct sock *sk) { struct netlink_sock *nlk = nlk_sk(sk); if (nlk->cb_running) { if (nlk->cb.done) nlk->cb.done(&nlk->cb); module_put(nlk->cb.module); kfree_skb(nlk->cb.skb); } skb_queue_purge(&sk->sk_receive_queue); if (!sock_flag(sk, SOCK_DEAD)) { printk(KERN_ERR "Freeing alive netlink socket %p\n", sk); return; } WARN_ON(atomic_read(&sk->sk_rmem_alloc)); WARN_ON(refcount_read(&sk->sk_wmem_alloc)); WARN_ON(nlk_sk(sk)->groups); }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)3628.12%215.38%
Florian Westphal3225.00%17.69%
Patrick McHardy2519.53%323.08%
Herbert Xu2116.41%17.69%
Arnaldo Carvalho de Melo53.91%215.38%
Ilpo Järvinen32.34%17.69%
David S. Miller32.34%17.69%
James Morris21.56%17.69%
Elena Reshetova10.78%17.69%
Total128100.00%13100.00%


static void netlink_sock_destruct_work(struct work_struct *work) { struct netlink_sock *nlk = container_of(work, struct netlink_sock, work); sk_free(&nlk->sk); }

Contributors

PersonTokensPropCommitsCommitProp
Herbert Xu34100.00%2100.00%
Total34100.00%2100.00%

/* This lock without WQ_FLAG_EXCLUSIVE is good on UP and it is _very_ bad on * SMP. Look, when several writers sleep and reader wakes them up, all but one * immediately hit write lock and grab all the cpus. Exclusive sleep solves * this, _but_ remember, it adds useless work on UP machines. */
void netlink_table_grab(void) __acquires(nl_table_lock) { might_sleep(); write_lock_irq(&nl_table_lock); if (atomic_read(&nl_table_users)) { DECLARE_WAITQUEUE(wait, current); add_wait_queue_exclusive(&nl_table_wait, &wait); for (;;) { set_current_state(TASK_UNINTERRUPTIBLE); if (atomic_read(&nl_table_users) == 0) break; write_unlock_irq(&nl_table_lock); schedule(); write_lock_irq(&nl_table_lock); } __set_current_state(TASK_RUNNING); remove_wait_queue(&nl_table_wait, &wait); } }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)8889.80%457.14%
Eric Dumazet44.08%114.29%
Johannes Berg33.06%114.29%
Arjan van de Ven33.06%114.29%
Total98100.00%7100.00%


void netlink_table_ungrab(void) __releases(nl_table_lock) { write_unlock_irq(&nl_table_lock); wake_up(&nl_table_wait); }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)1878.26%360.00%
Eric Dumazet417.39%120.00%
Arjan van de Ven14.35%120.00%
Total23100.00%5100.00%


static inline void netlink_lock_table(void) { /* read_lock() synchronizes us to netlink_table_grab */ read_lock(&nl_table_lock); atomic_inc(&nl_table_users); read_unlock(&nl_table_lock); }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)2796.43%266.67%
Patrick McHardy13.57%133.33%
Total28100.00%3100.00%


static inline void netlink_unlock_table(void) { if (atomic_dec_and_test(&nl_table_users)) wake_up(&nl_table_wait); }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)2295.65%266.67%
Patrick McHardy14.35%133.33%
Total23100.00%3100.00%

struct netlink_compare_arg { possible_net_t pnet; u32 portid; }; /* Doing sizeof directly may yield 4 extra bytes on 64-bit. */ #define netlink_compare_arg_len \ (offsetof(struct netlink_compare_arg, portid) + sizeof(u32))
static inline int netlink_compare(struct rhashtable_compare_arg *arg, const void *ptr) { const struct netlink_compare_arg *x = arg->key; const struct netlink_sock *nlk = ptr; return nlk->portid != x->portid || !net_eq(sock_net(&nlk->sk), read_pnet(&x->pnet)); }

Contributors

PersonTokensPropCommitsCommitProp
Herbert Xu2640.62%250.00%
Thomas Graf2335.94%125.00%
Gao Feng1523.44%125.00%
Total64100.00%4100.00%


static void netlink_compare_arg_init(struct netlink_compare_arg *arg, struct net *net, u32 portid) { memset(arg, 0, sizeof(*arg)); write_pnet(&arg->pnet, net); arg->portid = portid; }

Contributors

PersonTokensPropCommitsCommitProp
Herbert Xu48100.00%1100.00%
Total48100.00%1100.00%


static struct sock *__netlink_lookup(struct netlink_table *table, u32 portid, struct net *net) { struct netlink_compare_arg arg; netlink_compare_arg_init(&arg, net, portid); return rhashtable_lookup_fast(&table->hash, &arg, netlink_rhashtable_params); }

Contributors

PersonTokensPropCommitsCommitProp
Thomas Graf3367.35%250.00%
Herbert Xu1224.49%125.00%
Gao Feng48.16%125.00%
Total49100.00%4100.00%


static int __netlink_insert(struct netlink_table *table, struct sock *sk) { struct netlink_compare_arg arg; netlink_compare_arg_init(&arg, sock_net(sk), nlk_sk(sk)->portid); return rhashtable_lookup_insert_key(&table->hash, &arg, &nlk_sk(sk)->node, netlink_rhashtable_params); }

Contributors

PersonTokensPropCommitsCommitProp
Ying Xue4066.67%125.00%
Herbert Xu2033.33%375.00%
Total60100.00%4100.00%


static struct sock *netlink_lookup(struct net *net, int protocol, u32 portid) { struct netlink_table *table = &nl_table[protocol]; struct sock *sk; rcu_read_lock(); sk = __netlink_lookup(table, portid, net); if (sk) sock_hold(sk); rcu_read_unlock(); return sk; }

Contributors

PersonTokensPropCommitsCommitProp
Patrick McHardy2132.81%112.50%
Linus Torvalds (pre-git)1726.56%225.00%
Thomas Graf914.06%112.50%
Herbert Xu812.50%112.50%
Gao Feng46.25%112.50%
Arnaldo Carvalho de Melo34.69%112.50%
Eric W. Biedermann23.12%112.50%
Total64100.00%8100.00%

static const struct proto_ops netlink_ops;
static void netlink_update_listeners(struct sock *sk) { struct netlink_table *tbl = &nl_table[sk->sk_protocol]; unsigned long mask; unsigned int i; struct listeners *listeners; listeners = nl_deref_protected(tbl->listeners); if (!listeners) return; for (i = 0; i < NLGRPLONGS(tbl->groups); i++) { mask = 0; sk_for_each_bound(sk, &tbl->mc_list) { if (i < NLGRPLONGS(nlk_sk(sk)->ngroups)) mask |= nlk_sk(sk)->groups[i]; } listeners->masks[i] = mask; } /* this function is only called with the netlink table "grabbed", which * makes sure updates are visible before bind or setsockopt return. */ }

Contributors

PersonTokensPropCommitsCommitProp
Patrick McHardy8167.50%125.00%
Eric Dumazet2218.33%250.00%
Johannes Berg1714.17%125.00%
Total120100.00%4100.00%


static int netlink_insert(struct sock *sk, u32 portid) { struct netlink_table *table = &nl_table[sk->sk_protocol]; int err; lock_sock(sk); err = nlk_sk(sk)->portid == portid ? 0 : -EBUSY; if (nlk_sk(sk)->bound) goto err; err = -ENOMEM; if (BITS_PER_LONG > 32 && unlikely(atomic_read(&table->hash.nelems) >= UINT_MAX)) goto err; nlk_sk(sk)->portid = portid; sock_hold(sk); err = __netlink_insert(table, sk); if (err) { /* In case the hashtable backend returns with -EBUSY * from here, it must not escape to the caller. */ if (unlikely(err == -EBUSY)) err = -EOVERFLOW; if (err == -EEXIST) err = -EADDRINUSE; sock_put(sk); goto err; } /* We need to ensure that the socket is hashed and visible. */ smp_wmb(); nlk_sk(sk)->bound = portid; err: release_sock(sk); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Herbert Xu10960.56%640.00%
Thomas Graf2413.33%213.33%
Daniel Borkmann168.89%16.67%
Linus Torvalds (pre-git)137.22%213.33%
Ying Xue105.56%16.67%
Gao Feng31.67%16.67%
David S. Miller31.67%16.67%
Eric W. Biedermann21.11%16.67%
Total180100.00%15100.00%


static void netlink_remove(struct sock *sk) { struct netlink_table *table; table = &nl_table[sk->sk_protocol]; if (!rhashtable_remove_fast(&table->hash, &nlk_sk(sk)->node, netlink_rhashtable_params)) { WARN_ON(refcount_read(&sk->sk_refcnt) == 1); __sock_put(sk); } netlink_table_grab(); if (nlk_sk(sk)->subscriptions) { __sk_del_bind_node(sk); netlink_update_listeners(sk); } if (sk->sk_protocol == NETLINK_GENERIC) atomic_inc(&genl_sk_destructing_cnt); netlink_table_ungrab(); }

Contributors

PersonTokensPropCommitsCommitProp
Thomas Graf4642.59%111.11%
Herbert Xu2523.15%222.22%
Johannes Berg2119.44%222.22%
Linus Torvalds (pre-git)1412.96%222.22%
Elena Reshetova10.93%111.11%
Patrick McHardy10.93%111.11%
Total108100.00%9100.00%

static struct proto netlink_proto = { .name = "NETLINK", .owner = THIS_MODULE, .obj_size = sizeof(struct netlink_sock), };
static int __netlink_create(struct net *net, struct socket *sock, struct mutex *cb_mutex, int protocol, int kern) { struct sock *sk; struct netlink_sock *nlk; sock->ops = &netlink_ops; sk = sk_alloc(net, PF_NETLINK, GFP_KERNEL, &netlink_proto, kern); if (!sk) return -ENOMEM; sock_init_data(sock, sk); nlk = nlk_sk(sk); if (cb_mutex) { nlk->cb_mutex = cb_mutex; } else { nlk->cb_mutex = &nlk->cb_def_mutex; mutex_init(nlk->cb_mutex); lockdep_set_class_and_name(nlk->cb_mutex, nlk_cb_mutex_keys + protocol, nlk_cb_mutex_key_strings[protocol]); } init_waitqueue_head(&nlk->wait); sk->sk_destruct = netlink_sock_destruct; sk->sk_protocol = protocol; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Patrick McHardy10166.01%330.00%
Linus Torvalds (pre-git)1711.11%110.00%
Herbert Xu1610.46%110.00%
Eric W. Biedermann127.84%220.00%
David S. Miller42.61%110.00%
Eric Dumazet21.31%110.00%
Arnaldo Carvalho de Melo10.65%110.00%
Total153100.00%10100.00%


static int netlink_create(struct net *net, struct socket *sock, int protocol, int kern) { struct module *module = NULL; struct mutex *cb_mutex; struct netlink_sock *nlk; int (*bind)(struct net *net, int group); void (*unbind)(struct net *net, int group); int err = 0; sock->state = SS_UNCONNECTED; if (sock->type != SOCK_RAW && sock->type != SOCK_DGRAM) return -ESOCKTNOSUPPORT; if (protocol < 0 || protocol >= MAX_LINKS) return -EPROTONOSUPPORT; netlink_lock_table(); #ifdef CONFIG_MODULES if (!nl_table[protocol].registered) { netlink_unlock_table(); request_module("net-pf-%d-proto-%d", PF_NETLINK, protocol); netlink_lock_table(); } #endif if (nl_table[protocol].registered && try_module_get(nl_table[protocol].module)) module = nl_table[protocol].module; else err = -EPROTONOSUPPORT; cb_mutex = nl_table[protocol].cb_mutex; bind = nl_table[protocol].bind; unbind = nl_table[protocol].unbind; netlink_unlock_table(); if (err < 0) goto out; err = __netlink_create(net, sock, cb_mutex, protocol, kern); if (err < 0) goto out_module; local_bh_disable(); sock_prot_inuse_add(net, &netlink_proto, 1); local_bh_enable(); nlk = nlk_sk(sock->sk); nlk->module = module; nlk->netlink_bind = bind; nlk->netlink_unbind = unbind; out: return err; out_module: module_put(module); goto out; }

Contributors

PersonTokensPropCommitsCommitProp
Patrick McHardy11538.08%526.32%
Linus Torvalds (pre-git)5016.56%210.53%
Harald Welte3210.60%15.26%
Richard Guy Briggs268.61%15.26%
Pablo Neira Ayuso247.95%15.26%
Alexey Dobriyan154.97%15.26%
Johannes Berg113.64%210.53%
Eric Dumazet103.31%15.26%
Eric W. Biedermann92.98%210.53%
David S. Miller61.99%15.26%
Eric Paris30.99%15.26%
Arnaldo Carvalho de Melo10.33%15.26%
Total302100.00%19100.00%


static void deferred_put_nlk_sk(struct rcu_head *head) { struct netlink_sock *nlk = container_of(head, struct netlink_sock, rcu); struct sock *sk = &nlk->sk; kfree(nlk->groups); nlk->groups = NULL; if (!refcount_dec_and_test(&sk->sk_refcnt)) return; if (nlk->cb_running && nlk->cb.done) { INIT_WORK(&nlk->work, netlink_sock_destruct_work); schedule_work(&nlk->work); return; } sk_free(sk); }

Contributors

PersonTokensPropCommitsCommitProp
Herbert Xu5252.53%125.00%
Thomas Graf3333.33%125.00%
Xin Long1313.13%125.00%
Elena Reshetova11.01%125.00%
Total99100.00%4100.00%


static int netlink_release(struct socket *sock) { struct sock *sk = sock->sk; struct netlink_sock *nlk; if (!sk) return 0; netlink_remove(sk); sock_orphan(sk); nlk = nlk_sk(sk); /* * OK. Socket is unlinked, any packets that arrive now * will be purged. */ /* must not acquire netlink_table_lock in any way again before unbind * and notifying genetlink is done as otherwise it might deadlock */ if (nlk->netlink_unbind) { int i; for (i = 0; i < nlk->ngroups; i++) if (test_bit(i, nlk->groups)) nlk->netlink_unbind(sock_net(sk), i + 1); } if (sk->sk_protocol == NETLINK_GENERIC && atomic_dec_return(&genl_sk_destructing_cnt) == 0) wake_up(&genl_sk_destructing_waitq); sock->sk = NULL; wake_up_interruptible_all(&nlk->wait); skb_queue_purge(&sk->sk_write_queue); if (nlk->portid && nlk->bound) { struct netlink_notify n = { .net = sock_net(sk), .protocol = sk->sk_protocol, .portid = nlk->portid, }; blocking_notifier_call_chain(&netlink_chain, NETLINK_URELEASE, &n); } module_put(nlk->module); if (netlink_is_kernel(sk)) { netlink_table_grab(); BUG_ON(nl_table[sk->sk_protocol].registered == 0); if (--nl_table[sk->sk_protocol].registered == 0) { struct listeners *old; old = nl_deref_protected(nl_table[sk->sk_protocol].listeners); RCU_INIT_POINTER(nl_table[sk->sk_protocol].listeners, NULL); kfree_rcu(old, rcu); nl_table[sk->sk_protocol].module = NULL; nl_table[sk->sk_protocol].bind = NULL; nl_table[sk->sk_protocol].unbind = NULL; nl_table[sk->sk_protocol].flags = 0; nl_table[sk->sk_protocol].registered = 0; } netlink_table_ungrab(); } local_bh_disable(); sock_prot_inuse_add(sock_net(sk), &netlink_proto, -1); local_bh_enable(); call_rcu(&nlk->rcu, deferred_put_nlk_sk); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Johannes Berg7820.63%26.67%
Linus Torvalds (pre-git)5915.61%310.00%
Eric Dumazet4912.96%310.00%
Denis V. Lunev3810.05%310.00%
Patrick McHardy307.94%310.00%
James Morris277.14%13.33%
Pablo Neira Ayuso225.82%13.33%
Harald Welte184.76%13.33%
David S. Miller143.70%26.67%
Richard Guy Briggs112.91%13.33%
Eric W. Biedermann82.12%26.67%
Thomas Graf71.85%13.33%
Dmitry Ivanov41.06%13.33%
Arnaldo Carvalho de Melo41.06%26.67%
Art Haas41.06%13.33%
Hideaki Yoshifuji / 吉藤英明30.79%13.33%
Herbert Xu10.26%13.33%
Américo Wang10.26%13.33%
Total378100.00%30100.00%


static int netlink_autobind(struct socket *sock) { struct sock *sk = sock->sk; struct net *net = sock_net(sk); struct netlink_table *table = &nl_table[sk->sk_protocol]; s32 portid = task_tgid_vnr(current); int err; s32 rover = -4096; bool ok; retry: cond_resched(); rcu_read_lock(); ok = !__netlink_lookup(table, portid, net); rcu_read_unlock(); if (!ok) { /* Bind collision, search negative portid values. */ if (rover == -4096) /* rover will be in range [S32_MIN, -4097] */ rover = S32_MIN + prandom_u32_max(-4096 - S32_MIN); else if (rover >= -4096) rover = -4097; portid = rover--; goto retry; } err = netlink_insert(sk, portid); if (err == -EADDRINUSE) goto retry; /* If 2 threads race to autobind, that is fine. */ if (err == -EBUSY) err = 0; return err; }

Contributors

PersonTokensPropCommitsCommitProp
Herbert Xu6940.83%321.43%
Linus Torvalds (pre-git)6136.09%214.29%
David S. Miller148.28%214.29%
Eric W. Biedermann127.10%214.29%
Thomas Graf31.78%17.14%
Thomas Goff31.78%17.14%
Hideaki Yoshifuji / 吉藤英明31.78%17.14%
Gao Feng31.78%17.14%
Arnaldo Carvalho de Melo10.59%17.14%
Total169100.00%14100.00%

/** * __netlink_ns_capable - General netlink message capability test * @nsp: NETLINK_CB of the socket buffer holding a netlink command from userspace. * @user_ns: The user namespace of the capability to use * @cap: The capability to use * * Test to see if the opener of the socket we received the message * from had when the netlink socket was created and the sender of the * message has has the capability @cap in the user namespace @user_ns. */
bool __netlink_ns_capable(const struct netlink_skb_parms *nsp, struct user_namespace *user_ns, int cap) { return ((nsp->flags & NETLINK_SKB_DST) || file_ns_capable(nsp->sk->sk_socket->file, user_ns, cap)) && ns_capable(user_ns, cap); }

Contributors

PersonTokensPropCommitsCommitProp
Eric W. Biedermann52100.00%2100.00%
Total52100.00%2100.00%

EXPORT_SYMBOL(__netlink_ns_capable); /** * netlink_ns_capable - General netlink message capability test * @skb: socket buffer holding a netlink command from userspace * @user_ns: The user namespace of the capability to use * @cap: The capability to use * * Test to see if the opener of the socket we received the message * from had when the netlink socket was created and the sender of the * message has has the capability @cap in the user namespace @user_ns. */
bool netlink_ns_capable(const struct sk_buff *skb, struct user_namespace *user_ns, int cap) { return __netlink_ns_capable(&NETLINK_CB(skb), user_ns, cap); }

Contributors

PersonTokensPropCommitsCommitProp
Eric W. Biedermann33100.00%1100.00%
Total33100.00%1100.00%

EXPORT_SYMBOL(netlink_ns_capable); /** * netlink_capable - Netlink global message capability test * @skb: socket buffer holding a netlink command from userspace * @cap: The capability to use * * Test to see if the opener of the socket we received the message * from had when the netlink socket was created and the sender of the * message has has the capability @cap in all user namespaces. */
bool netlink_capable(const struct sk_buff *skb, int cap) { return netlink_ns_capable(skb, &init_user_ns, cap); }

Contributors

PersonTokensPropCommitsCommitProp
Eric W. Biedermann25100.00%1100.00%
Total25100.00%1100.00%

EXPORT_SYMBOL(netlink_capable); /** * netlink_net_capable - Netlink network namespace message capability test * @skb: socket buffer holding a netlink command from userspace * @cap: The capability to use * * Test to see if the opener of the socket we received the message * from had when the netlink socket was created and the sender of the * message has has the capability @cap over the network namespace of * the socket we received the message from. */
bool netlink_net_capable(const struct sk_buff *skb, int cap) { return netlink_ns_capable(skb, sock_net(skb->sk)->user_ns, cap); }

Contributors

PersonTokensPropCommitsCommitProp
Eric W. Biedermann31100.00%1100.00%
Total31100.00%1100.00%

EXPORT_SYMBOL(netlink_net_capable);
static inline int netlink_allowed(const struct socket *sock, unsigned int flag) { return (nl_table[sock->sk->sk_protocol].flags & flag) || ns_capable(sock_net(sock->sk)->user_ns, CAP_NET_ADMIN); }

Contributors

PersonTokensPropCommitsCommitProp
James Morris3063.83%112.50%
Eric W. Biedermann1123.40%225.00%
Pablo Neira Ayuso36.38%225.00%
Stephen Hemminger12.13%112.50%
Herbert Xu12.13%112.50%
Arnaldo Carvalho de Melo12.13%112.50%
Total47100.00%8100.00%


static void netlink_update_subscriptions(struct sock *sk, unsigned int subscriptions) { struct netlink_sock *nlk = nlk_sk(sk); if (nlk->subscriptions && !subscriptions) __sk_del_bind_node(sk); else if (!nlk->subscriptions && subscriptions) sk_add_bind_node(sk, &nl_table[sk->sk_protocol].mc_list); nlk->subscriptions = subscriptions; }

Contributors

PersonTokensPropCommitsCommitProp
Patrick McHardy70100.00%1100.00%
Total70100.00%1100.00%


static int netlink_realloc_groups(struct sock *sk) { struct netlink_sock *nlk = nlk_sk(sk); unsigned int groups; unsigned long *new_groups; int err = 0; netlink_table_grab(); groups = nl_table[sk->sk_protocol].groups; if (!nl_table[sk->sk_protocol].registered) { err = -ENOENT; goto out_unlock; } if (nlk->ngroups >= groups) goto out_unlock; new_groups = krealloc(nlk->groups, NLGRPSZ(groups), GFP_ATOMIC); if (new_groups == NULL) { err = -ENOMEM; goto out_unlock; } memset((char *)new_groups + NLGRPSZ(nlk->ngroups), 0, NLGRPSZ(groups) - NLGRPSZ(nlk->ngroups)); nlk->groups = new_groups; nlk->ngroups = groups; out_unlock: netlink_table_ungrab(); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Patrick McHardy8853.66%150.00%
Johannes Berg7646.34%150.00%
Total164100.00%2100.00%


static void netlink_undo_bind(int group, long unsigned int groups, struct sock *sk) { struct netlink_sock *nlk = nlk_sk(sk); int undo; if (!nlk->netlink_unbind) return; for (undo = 0; undo < group; undo++) if (test_bit(undo, &groups)) nlk->netlink_unbind(sock_net(sk), undo + 1); }

Contributors

PersonTokensPropCommitsCommitProp
Richard Guy Briggs5672.73%120.00%
Johannes Berg1823.38%240.00%
Pablo Neira Ayuso22.60%120.00%
Hiroaki SHIMODA11.30%120.00%
Total77100.00%5100.00%


static int netlink_bind(struct socket *sock, struct sockaddr *addr, int addr_len) { struct sock *sk = sock->sk; struct net *net = sock_net(sk); struct netlink_sock *nlk = nlk_sk(sk); struct sockaddr_nl *nladdr = (struct sockaddr_nl *)addr; int err = 0; long unsigned int groups = nladdr->nl_groups; bool bound; if (addr_len < sizeof(struct sockaddr_nl)) return -EINVAL; if (nladdr->nl_family != AF_NETLINK) return -EINVAL; /* Only superuser is allowed to listen multicasts */ if (groups) { if (!netlink_allowed(sock, NL_CFG_F_NONROOT_RECV)) return -EPERM; err = netlink_realloc_groups(sk); if (err) return err; } bound = nlk->bound; if (bound) { /* Ensure nlk->portid is up-to-date. */ smp_rmb(); if (nladdr->nl_pid != nlk->portid) return -EINVAL; } netlink_lock_table(); if (nlk->netlink_bind && groups) { int group; for (group = 0; group < nlk->ngroups; group++) { if (!test_bit(group, &groups)) continue; err = nlk->netlink_bind(net, group + 1); if (!err) continue; netlink_undo_bind(group, groups, sk); goto unlock; } } /* No need for barriers here as we return to user-space without * using any of the bound attributes. */ if (!bound) { err = nladdr->nl_pid ? netlink_insert(sk, nladdr->nl_pid) : netlink_autobind(sock); if (err) { netlink_undo_bind(nlk->ngroups, groups, sk); goto unlock; } } if (!groups && (nlk->groups == NULL || !(u32)nlk->groups[0])) goto unlock; netlink_unlock_table(); netlink_table_grab(); netlink_update_subscriptions(sk, nlk->subscriptions + hweight32(groups) - hweight32(nlk->groups[0])); nlk->groups[0] = (nlk->groups[0] & ~0xffffffffUL) | groups; netlink_update_listeners(sk); netlink_table_ungrab(); return 0; unlock: netlink_unlock_table(); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)9624.68%312.50%
Richard Guy Briggs9123.39%14.17%
Patrick McHardy6717.22%312.50%
Herbert Xu5915.17%312.50%
Xin Long256.43%14.17%
Hannes Frederic Sowa143.60%14.17%
David S. Miller123.08%14.17%
Eric W. Biedermann92.31%312.50%
Johannes Berg71.80%312.50%
Pablo Neira Ayuso30.77%28.33%
Hideaki Yoshifuji / 吉藤英明30.77%14.17%
James Morris20.51%14.17%
Arnaldo Carvalho de Melo10.26%14.17%
Total389100.00%24100.00%


static int netlink_connect(struct socket *sock, struct sockaddr *addr, int alen, int flags) { int err = 0; struct sock *sk = sock->sk; struct netlink_sock *nlk = nlk_sk(sk); struct sockaddr_nl *nladdr = (struct sockaddr_nl *)addr; if (alen < sizeof(addr->sa_family)) return -EINVAL; if (addr->sa_family == AF_UNSPEC) { sk->sk_state = NETLINK_UNCONNECTED; nlk->dst_portid = 0; nlk->dst_group = 0; return 0; } if (addr->sa_family != AF_NETLINK) return -EINVAL; if ((nladdr->nl_groups || nladdr->nl_pid) && !netlink_allowed(sock, NL_CFG_F_NONROOT_SEND)) return -EPERM; /* No need for barriers here as we return to user-space without * using any of the bound attributes. */ if (!nlk->bound) err = netlink_autobind(sock); if (err == 0) { sk->sk_state = NETLINK_CONNECTED; nlk->dst_portid = nladdr->nl_pid; nlk->dst_group = ffs(nladdr->nl_groups); } return err; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)12967.54%425.00%
Changli Gao157.85%16.25%
Herbert Xu157.85%318.75%
David S. Miller147.33%16.25%
Mike Pecovnik63.14%16.25%
Patrick McHardy52.62%16.25%
Eric W. Biedermann31.57%212.50%
James Morris21.05%16.25%
Pablo Neira Ayuso10.52%16.25%
Arnaldo Carvalho de Melo10.52%16.25%
Total191100.00%16100.00%


static int netlink_getname(struct socket *sock, struct sockaddr *addr, int *addr_len, int peer) { struct sock *sk = sock->sk; struct netlink_sock *nlk = nlk_sk(sk); DECLARE_SOCKADDR(struct sockaddr_nl *, nladdr, addr); nladdr->nl_family = AF_NETLINK; nladdr->nl_pad = 0; *addr_len = sizeof(*nladdr); if (peer) { nladdr->nl_pid = nlk->dst_portid; nladdr->nl_groups = netlink_group_mask(nlk->dst_group); } else { nladdr->nl_pid = nlk->portid; netlink_lock_table(); nladdr->nl_groups = nlk->groups ? nlk->groups[0] : 0; netlink_unlock_table(); } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)8965.44%218.18%
David S. Miller1813.24%218.18%
Patrick McHardy139.56%327.27%
Cyrill V. Gorcunov75.15%19.09%
Xin Long64.41%19.09%
Eric W. Biedermann21.47%19.09%
Arnaldo Carvalho de Melo10.74%19.09%
Total136100.00%11100.00%


static int netlink_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) { /* try to hand this ioctl down to the NIC drivers. */ return -ENOIOCTLCMD; }

Contributors

PersonTokensPropCommitsCommitProp
David Decotigny24100.00%1100.00%
Total24100.00%1100.00%


static struct sock *netlink_getsockbyportid(struct sock *ssk, u32 portid) { struct sock *sock; struct netlink_sock *nlk; sock = netlink_lookup(sock_net(ssk), ssk->sk_protocol, portid); if (!sock) return ERR_PTR(-ECONNREFUSED); /* Don't bother queuing skb if kernel socket has no input function */ nlk = nlk_sk(sock); if (sock->sk_state == NETLINK_CONNECTED && nlk->dst_portid != nlk_sk(ssk)->portid) { sock_put(sock); return ERR_PTR(-ECONNREFUSED); } return sock; }

Contributors

PersonTokensPropCommitsCommitProp
Andrew Morton3434.69%17.69%
Linus Torvalds (pre-git)2121.43%323.08%
Herbert Xu1414.29%17.69%
David S. Miller1010.20%17.69%
Eric W. Biedermann77.14%215.38%
Denis V. Lunev44.08%17.69%
Hideaki Yoshifuji / 吉藤英明33.06%17.69%
James Morris33.06%17.69%
Adrian Bunk11.02%17.69%
Arnaldo Carvalho de Melo11.02%17.69%
Total98100.00%13100.00%


struct sock *netlink_getsockbyfilp(struct file *filp) { struct inode *inode = file_inode(filp); struct sock *sock; if (!S_ISSOCK(inode->i_mode)) return ERR_PTR(-ENOTSOCK); sock = SOCKET_I(inode)->sk; if (sock->sk_family != AF_NETLINK) return ERR_PTR(-EINVAL); sock_hold(sock); return sock; }

Contributors

PersonTokensPropCommitsCommitProp
Andrew Morton5673.68%116.67%
Linus Torvalds (pre-git)1013.16%350.00%
Matthew Wilcox79.21%116.67%
Al Viro33.95%116.67%
Total76100.00%6100.00%


static struct sk_buff *netlink_alloc_large_skb(unsigned int size, int broadcast) { struct sk_buff *skb; void *data; if (size <= NLMSG_GOODSIZE || broadcast) return alloc_skb(size, GFP_KERNEL); size = SKB_DATA_ALIGN(size) + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); data = vmalloc(size); if (data == NULL) return NULL; skb = __build_skb(data, size); if (skb == NULL) vfree(data); else skb->destructor = netlink_skb_destructor; return skb; }

Contributors

PersonTokensPropCommitsCommitProp
Pablo Neira Ayuso10199.02%266.67%
Eric Dumazet10.98%133.33%
Total102100.00%3100.00%

/* * Attach a skb to a netlink socket. * The caller must hold a reference to the destination socket. On error, the * reference is dropped. The skb is not send to the destination, just all * all error checks are performed and memory in the queue is reserved. * Return values: * < 0: error. skb freed, reference to sock dropped. * 0: continue * 1: repeat lookup - reference dropped while waiting for socket memory. */
int netlink_attachskb(struct sock *sk, struct sk_buff *skb, long *timeo, struct sock *ssk) { struct netlink_sock *nlk; nlk = nlk_sk(sk); if ((atomic_read(&sk->sk_rmem_alloc) > sk->sk_rcvbuf || test_bit(NETLINK_S_CONGESTED, &nlk->state))) { DECLARE_WAITQUEUE(wait, current); if (!*timeo) { if (!ssk || netlink_is_kernel(ssk)) netlink_overrun(sk); sock_put(sk); kfree_skb(skb); return -EAGAIN; } __set_current_state(TASK_INTERRUPTIBLE); add_wait_queue(&nlk->wait, &wait); if ((atomic_read(&sk->sk_rmem_alloc) > sk->sk_rcvbuf || test_bit(NETLINK_S_CONGESTED, &nlk->state)) && !sock_flag(sk, SOCK_DEAD)) *timeo = schedule_timeout(*timeo); __set_current_state(TASK_RUNNING); remove_wait_queue(&nlk->wait, &wait); sock_put(sk); if (signal_pending(current)) { kfree_skb(skb); return sock_intr_errno(*timeo); } return 1; } netlink_skb_set_owner_r(skb, sk); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)14866.67%736.84%
Andrew Morton3817.12%15.26%
Alexey Kuznetsov104.50%15.26%
Patrick McHardy83.60%315.79%
Arnaldo Carvalho de Melo83.60%315.79%
David S. Miller52.25%15.26%
James Morris20.90%15.26%
Nicolas Dichtel20.90%15.26%
Denis V. Lunev10.45%15.26%
Total222100.00%19100.00%


static int __netlink_sendskb(struct sock *sk, struct sk_buff *skb) { int len = skb->len; netlink_deliver_tap(skb); skb_queue_tail(&sk->sk_receive_queue, skb); sk->sk_data_ready(sk); return len; }

Contributors

PersonTokensPropCommitsCommitProp
Andrew Morton2041.67%120.00%
Linus Torvalds (pre-git)1531.25%120.00%
Eric Dumazet612.50%120.00%
Daniel Borkmann510.42%120.00%
Arnaldo Carvalho de Melo24.17%120.00%
Total48100.00%5100.00%


int netlink_sendskb(struct sock *sk, struct sk_buff *skb) { int len = __netlink_sendskb(sk, skb); sock_put(sk); return len; }

Contributors

PersonTokensPropCommitsCommitProp
Eric Dumazet2472.73%125.00%
Linus Torvalds (pre-git)824.24%250.00%
Andrew Morton13.03%125.00%
Total33100.00%4100.00%


void netlink_detachskb(struct sock *sk, struct sk_buff *skb) { kfree_skb(skb); sock_put(sk); }

Contributors

PersonTokensPropCommitsCommitProp
Andrew Morton25100.00%1100.00%
Total25100.00%1100.00%


static struct sk_buff *netlink_trim(struct sk_buff *skb, gfp_t allocation) { int delta; WARN_ON(skb->sk != NULL); delta = skb->end - skb->tail; if (is_vmalloc_addr(skb->head) || delta * 2 < skb->truesize) return skb; if (skb_shared(skb)) { struct sk_buff *nskb = skb_clone(skb, allocation); if (!nskb) return skb; consume_skb(skb); skb = nskb; } pskb_expand_head(skb, 0, -delta, (allocation & ~__GFP_DIRECT_RECLAIM) | __GFP_NOWARN | __GFP_NORETRY); return skb; }

Contributors

PersonTokensPropCommitsCommitProp
Herbert Xu9478.33%330.00%
Eric Dumazet108.33%220.00%
Pablo Neira Ayuso75.83%110.00%
Patrick McHardy54.17%110.00%
Arnaldo Carvalho de Melo21.67%110.00%
Al Viro10.83%110.00%
David S. Miller10.83%110.00%
Total120100.00%10100.00%


static int netlink_unicast_kernel(struct sock *sk, struct sk_buff *skb, struct sock *ssk) { int ret; struct netlink_sock *nlk = nlk_sk(sk); ret = -ECONNREFUSED; if (nlk->netlink_rcv != NULL) { ret = skb->len; netlink_skb_set_owner_r(skb, sk); NETLINK_CB(skb).sk = ssk; netlink_deliver_tap_kernel(sk, ssk, skb); nlk->netlink_rcv(skb); consume_skb(skb); } else { kfree_skb(skb); } sock_put(sk); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Patrick McHardy4440.74%116.67%
Denis V. Lunev3431.48%116.67%
Florian Westphal1614.81%116.67%
Eric Dumazet54.63%116.67%
Eric W. Biedermann54.63%116.67%
Daniel Borkmann43.70%116.67%
Total108100.00%6100.00%


int netlink_unicast(struct sock *ssk, struct sk_buff *skb, u32 portid, int nonblock) { struct sock *sk; int err; long timeo; skb = netlink_trim(skb, gfp_any()); timeo = sock_sndtimeo(ssk, nonblock); retry: sk = netlink_getsockbyportid(ssk, portid); if (IS_ERR(sk)) { kfree_skb(skb); return PTR_ERR(sk); } if (netlink_is_kernel(sk)) return netlink_unicast_kernel(sk, skb, ssk); if (sk_filter(sk, skb)) { err = skb->len; kfree_skb(skb); sock_put(sk); return err; } err = netlink_attachskb(sk, skb, &timeo, ssk); if (err == 1) goto retry; if (err) return err; return netlink_sendskb(sk, skb); }

Contributors

PersonTokensPropCommitsCommitProp
Florian Westphal9556.89%111.11%
Patrick McHardy2615.57%222.22%
Andrew Morton2514.97%111.11%
Stephen Hemminger137.78%111.11%
Denis V. Lunev31.80%111.11%
Herbert Xu31.80%222.22%
Daniel Borkmann21.20%111.11%
Total167100.00%9100.00%

EXPORT_SYMBOL(netlink_unicast);
int netlink_has_listeners(struct sock *sk, unsigned int group) { int res = 0; struct listeners *listeners; BUG_ON(!netlink_is_kernel(sk)); rcu_read_lock(); listeners = rcu_dereference(nl_table[sk->sk_protocol].listeners); if (listeners && group - 1 < nl_table[sk->sk_protocol].groups) res = test_bit(group - 1, listeners->masks); rcu_read_unlock(); return res; }

Contributors

PersonTokensPropCommitsCommitProp
Patrick McHardy5665.12%120.00%
Johannes Berg2326.74%120.00%
Eric Dumazet66.98%240.00%
Denis V. Lunev11.16%120.00%
Total86100.00%5100.00%

EXPORT_SYMBOL_GPL(netlink_has_listeners);
static int netlink_broadcast_deliver(struct sock *sk, struct sk_buff *skb) { struct netlink_sock *nlk = nlk_sk(sk); if (atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf && !test_bit(NETLINK_S_CONGESTED, &nlk->state)) { netlink_skb_set_owner_r(skb, sk); __netlink_sendskb(sk, skb); return atomic_read(&sk->sk_rmem_alloc) > (sk->sk_rcvbuf >> 1); } return -1; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)5764.77%220.00%
Herbert Xu1112.50%110.00%
David S. Miller1011.36%110.00%
Stephen Hemminger44.55%110.00%
Arnaldo Carvalho de Melo33.41%220.00%
Nicolas Dichtel11.14%110.00%
Patrick McHardy11.14%110.00%
Eric Dumazet11.14%110.00%
Total88100.00%10100.00%

struct netlink_broadcast_data { struct sock *exclude_sk; struct net *net; u32 portid; u32 group; int failure; int delivery_failure; int congested; int delivered; gfp_t allocation; struct sk_buff *skb, *skb2; int (*tx_filter)(struct sock *dsk, struct sk_buff *skb, void *data); void *tx_data; };
static void do_one_broadcast(struct sock *sk, struct netlink_broadcast_data *p) { struct netlink_sock *nlk = nlk_sk(sk); int val; if (p->exclude_sk == sk) return; if (nlk->portid == p->portid || p->group - 1 >= nlk->ngroups || !test_bit(p->group - 1, nlk->groups)) return; if (!net_eq(sock_net(sk), p->net)) { if (!(nlk->flags & NETLINK_F_LISTEN_ALL_NSID)) return; if (!peernet_has_id(sock_net(sk), p->net)) return; if (!file_ns_capable(sk->sk_socket->file, p->net->user_ns, CAP_NET_BROADCAST)) return; } if (p->failure) { netlink_overrun(sk); return; } sock_hold(sk); if (p->skb2 == NULL) { if (skb_shared(p->skb)) { p->skb2 = skb_clone(p->skb, p->allocation); } else { p->skb2 = skb_get(p->skb); /* * skb ownership may have been set when * delivered to a previous socket. */ skb_orphan(p->skb2); } } if (p->skb2 == NULL) { netlink_overrun(sk); /* Clone failed. Notify ALL listeners. */ p->failure = 1; if (nlk->flags & NETLINK_F_BROADCAST_SEND_ERROR) p->delivery_failure = 1; goto out; } if (p->tx_filter && p->tx_filter(sk, p->skb2, p->tx_data)) { kfree_skb(p->skb2); p->skb2 = NULL; goto out; } if (sk_filter(sk, p->skb2)) { kfree_skb(p->skb2); p->skb2 = NULL; goto out; } NETLINK_CB(p->skb2).nsid = peernet2id(sock_net(sk), p->net); if (NETLINK_CB(p->skb2).nsid != NETNSA_NSID_NOT_ASSIGNED) NETLINK_CB(p->skb2).nsid_is_set = true; val = netlink_broadcast_deliver(sk, p->skb2); if (val < 0) { netlink_overrun(sk); if (nlk->flags & NETLINK_F_BROADCAST_SEND_ERROR) p->delivery_failure = 1; } else { p->congested |= val; p->delivered = 1; p->skb2 = NULL; } out: sock_put(sk); }

Contributors

PersonTokensPropCommitsCommitProp
Nicolas Dichtel11225.81%313.04%
Linus Torvalds (pre-git)10524.19%28.70%
Herbert Xu6114.06%28.70%
Eric W. Biedermann4510.37%313.04%
Pablo Neira Ayuso265.99%28.70%
Stephen Hemminger245.53%14.35%
David S. Miller225.07%313.04%
Patrick McHardy184.15%28.70%
Hideaki Yoshifuji / 吉藤英明81.84%28.70%
Tommy S. Christensen71.61%14.35%
Rami Rosen51.15%14.35%
Arnaldo Carvalho de Melo10.23%14.35%
Total434100.00%23100.00%


int netlink_broadcast_filtered(struct sock *ssk, struct sk_buff *skb, u32 portid, u32 group, gfp_t allocation, int (*filter)(struct sock *dsk, struct sk_buff *skb, void *data), void *filter_data) { struct net *net = sock_net(ssk); struct netlink_broadcast_data info; struct sock *sk; skb = netlink_trim(skb, allocation); info.exclude_sk = ssk; info.net = net; info.portid = portid; info.group = group; info.failure = 0; info.delivery_failure = 0; info.congested = 0; info.delivered = 0; info.allocation = allocation; info.skb = skb; info.skb2 = NULL; info.tx_filter = filter; info.tx_data = filter_data; /* While we sleep in clone, do not allow to change socket list */ netlink_lock_table(); sk_for_each_bound(sk, &nl_table[ssk->sk_protocol].mc_list) do_one_broadcast(sk, &info); consume_skb(skb); netlink_unlock_table(); if (info.delivery_failure) { kfree_skb(info.skb2); return -ENOBUFS; } consume_skb(info.skb2); if (info.delivered) { if (info.congested && gfpflags_allow_blocking(allocation)) yield(); return 0; } return -ESRCH; }

Contributors

PersonTokensPropCommitsCommitProp
Herbert Xu12753.81%320.00%
Eric W. Biedermann5422.88%320.00%
Neil Horman166.78%16.67%
Pablo Neira Ayuso145.93%16.67%
Linus Torvalds (pre-git)83.39%213.33%
David S. Miller62.54%16.67%
Tommy S. Christensen41.69%16.67%
Hideaki Yoshifuji / 吉藤英明31.27%16.67%
Mel Gorman31.27%16.67%
Al Viro10.42%16.67%
Total236100.00%15100.00%

EXPORT_SYMBOL(netlink_broadcast_filtered);
int netlink_broadcast(struct sock *ssk, struct sk_buff *skb, u32 portid, u32 group, gfp_t allocation) { return netlink_broadcast_filtered(ssk, skb, portid, group, allocation, NULL, NULL); }

Contributors

PersonTokensPropCommitsCommitProp
Eric W. Biedermann42100.00%2100.00%
Total42100.00%2100.00%

EXPORT_SYMBOL(netlink_broadcast); struct netlink_set_err_data { struct sock *exclude_sk; u32 portid; u32 group; int code; };
static int do_one_set_err(struct sock *sk, struct netlink_set_err_data *p) { struct netlink_sock *nlk = nlk_sk(sk); int ret = 0; if (sk == p->exclude_sk) goto out; if (!net_eq(sock_net(sk), sock_net(p->exclude_sk))) goto out; if (nlk->portid == p->portid || p->group - 1 >= nlk->ngroups || !test_bit(p->group - 1, nlk->groups)) goto out; if (p->code == ENOBUFS && nlk->flags & NETLINK_F_RECV_NO_ENOBUFS) { ret = 1; goto out; } sk->sk_err = p->code; sk->sk_error_report(sk); out: return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Herbert Xu7048.95%18.33%
Pablo Neira Ayuso2819.58%18.33%
Patrick McHardy1812.59%216.67%
Eric W. Biedermann128.39%216.67%
Hideaki Yoshifuji / 吉藤英明64.20%18.33%
Octavian Purdila53.50%18.33%
Nicolas Dichtel10.70%18.33%
David S. Miller10.70%18.33%
Arnaldo Carvalho de Melo10.70%18.33%
Linus Torvalds (pre-git)10.70%18.33%
Total143100.00%12100.00%

/** * netlink_set_err - report error to broadcast listeners * @ssk: the kernel netlink socket, as returned by netlink_kernel_create() * @portid: the PORTID of a process that we want to skip (if any) * @group: the broadcast group that will notice the error * @code: error code, must be negative (as usual in kernelspace) * * This function returns the number of broadcast listeners that have set the * NETLINK_NO_ENOBUFS socket option. */
int netlink_set_err(struct sock *ssk, u32 portid, u32 group, int code) { struct netlink_set_err_data info; struct sock *sk; int ret = 0; info.exclude_sk = ssk; info.portid = portid; info.group = group; /* sk->sk_err wants a positive error value */ info.code = -code; read_lock(&nl_table_lock); sk_for_each_bound(sk, &nl_table[ssk->sk_protocol].mc_list) ret += do_one_set_err(sk, &info); read_unlock(&nl_table_lock); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)3538.89%333.33%
Herbert Xu3235.56%111.11%
Pablo Neira Ayuso1314.44%222.22%
Arnaldo Carvalho de Melo55.56%111.11%
Eric W. Biedermann33.33%111.11%
David S. Miller22.22%111.11%
Total90100.00%9100.00%

EXPORT_SYMBOL(netlink_set_err); /* must be called with netlink table grabbed */
static void netlink_update_socket_mc(struct netlink_sock *nlk, unsigned int group, int is_new) { int old, new = !!is_new, subscriptions; old = test_bit(group - 1, nlk->groups); subscriptions = nlk->subscriptions - old + new; if (new) __set_bit(group - 1, nlk->groups); else __clear_bit(group - 1, nlk->groups); netlink_update_subscriptions(&nlk->sk, subscriptions); netlink_update_listeners(&nlk->sk); }

Contributors

PersonTokensPropCommitsCommitProp
Johannes Berg97100.00%1100.00%
Total97100.00%1100.00%


static int netlink_setsockopt(struct socket *sock, int level, int optname, char __user *optval, unsigned int optlen) { struct sock *sk = sock->sk; struct netlink_sock *nlk = nlk_sk(sk); unsigned int val = 0; int err; if (level != SOL_NETLINK) return -ENOPROTOOPT; if (optlen >= sizeof(int) && get_user(val, (unsigned int __user *)optval)) return -EFAULT; switch (optname) { case NETLINK_PKTINFO: if (val) nlk->flags |= NETLINK_F_RECV_PKTINFO; else nlk->flags &= ~NETLINK_F_RECV_PKTINFO; err = 0; break; case NETLINK_ADD_MEMBERSHIP: case NETLINK_DROP_MEMBERSHIP: { if (!netlink_allowed(sock, NL_CFG_F_NONROOT_RECV)) return -EPERM; err = netlink_realloc_groups(sk); if (err) return err; if (!val || val - 1 >= nlk->ngroups) return -EINVAL; if (optname == NETLINK_ADD_MEMBERSHIP && nlk->netlink_bind) { err = nlk->netlink_bind(sock_net(sk), val); if (err) return err; } netlink_table_grab(); netlink_update_socket_mc(nlk, val, optname == NETLINK_ADD_MEMBERSHIP); netlink_table_ungrab(); if (optname == NETLINK_DROP_MEMBERSHIP && nlk->netlink_unbind) nlk->netlink_unbind(sock_net(sk), val); err = 0; break; } case NETLINK_BROADCAST_ERROR: if (val) nlk->flags |= NETLINK_F_BROADCAST_SEND_ERROR; else nlk->flags &= ~NETLINK_F_BROADCAST_SEND_ERROR; err = 0; break; case NETLINK_NO_ENOBUFS: if (val) { nlk->flags |= NETLINK_F_RECV_NO_ENOBUFS; clear_bit(NETLINK_S_CONGESTED, &nlk->state); wake_up_interruptible(&nlk->wait); } else { nlk->flags &= ~NETLINK_F_RECV_NO_ENOBUFS; } err = 0; break; case NETLINK_LISTEN_ALL_NSID: if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_BROADCAST)) return -EPERM; if (val) nlk->flags |= NETLINK_F_LISTEN_ALL_NSID; else nlk->flags &= ~NETLINK_F_LISTEN_ALL_NSID; err = 0; break; case NETLINK_CAP_ACK: if (val) nlk->flags |= NETLINK_F_CAP_ACK; else nlk->flags &= ~NETLINK_F_CAP_ACK; err = 0; break; case NETLINK_EXT_ACK: if (val) nlk->flags |= NETLINK_F_EXT_ACK; else nlk->flags &= ~NETLINK_F_EXT_ACK; err = 0; break; default: err = -ENOPROTOOPT; } return err; }

Contributors

PersonTokensPropCommitsCommitProp
Patrick McHardy19244.44%315.79%
Pablo Neira Ayuso6815.74%315.79%
Nicolas Dichtel5212.04%210.53%
Johannes Berg4510.42%526.32%
Richard Guy Briggs4510.42%210.53%
Christophe Ricard266.02%15.26%
Eric Dumazet20.46%15.26%
David S. Miller10.23%15.26%
Eric W. Biedermann10.23%15.26%
Total432100.00%19100.00%


static int netlink_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen) { struct sock *sk = sock->sk; struct netlink_sock *nlk = nlk_sk(sk); int len, val, err; if (level != SOL_NETLINK) return -ENOPROTOOPT; if (get_user(len, optlen)) return -EFAULT; if (len < 0) return -EINVAL; switch (optname) { case NETLINK_PKTINFO: if (len < sizeof(int)) return -EINVAL; len = sizeof(int); val = nlk->flags & NETLINK_F_RECV_PKTINFO ? 1 : 0; if (put_user(len, optlen) || put_user(val, optval)) return -EFAULT; err = 0; break; case NETLINK_BROADCAST_ERROR: if (len < sizeof(int)) return -EINVAL; len = sizeof(int); val = nlk->flags & NETLINK_F_BROADCAST_SEND_ERROR ? 1 : 0; if (put_user(len, optlen) || put_user(val, optval)) return -EFAULT; err = 0; break; case NETLINK_NO_ENOBUFS: if (len < sizeof(int)) return -EINVAL; len = sizeof(int); val = nlk->flags & NETLINK_F_RECV_NO_ENOBUFS ? 1 : 0; if (put_user(len, optlen) || put_user(val, optval)) return -EFAULT; err = 0; break; case NETLINK_LIST_MEMBERSHIPS: { int pos, idx, shift; err = 0; netlink_lock_table(); for (pos = 0; pos * 8 < nlk->ngroups; pos += sizeof(u32)) { if (len - pos < sizeof(u32)) break; idx = pos / sizeof(unsigned long); shift = (pos % sizeof(unsigned long)) * 8; if (put_user((u32)(nlk->groups[idx] >> shift), (u32 __user *)(optval + pos))) { err = -EFAULT; break; } } if (put_user(ALIGN(nlk->ngroups / 8, sizeof(u32)), optlen)) err = -EFAULT; netlink_unlock_table(); break; } case NETLINK_CAP_ACK: if (len < sizeof(int)) return -EINVAL; len = sizeof(int); val = nlk->flags & NETLINK_F_CAP_ACK ? 1 : 0; if (put_user(len, optlen) || put_user(val, optval)) return -EFAULT; err = 0; break; case NETLINK_EXT_ACK: if (len < sizeof(int)) return -EINVAL; len = sizeof(int); val = nlk->flags & NETLINK_F_EXT_ACK ? 1 : 0; if (put_user(len, optlen) || put_user(val, optval)) return -EFAULT; err = 0; break; default: err = -ENOPROTOOPT; } return err; }

Contributors

PersonTokensPropCommitsCommitProp
Patrick McHardy15227.79%111.11%
David Herrmann14626.69%222.22%
Pablo Neira Ayuso11821.57%222.22%
Christophe Ricard6010.97%111.11%
Johannes Berg6010.97%111.11%
Heiko Carstens81.46%111.11%
Nicolas Dichtel30.55%111.11%
Total547100.00%9100.00%


static void netlink_cmsg_recv_pktinfo(struct msghdr *msg, struct sk_buff *skb) { struct nl_pktinfo info; info.group = NETLINK_CB(skb).dst_group; put_cmsg(msg, SOL_NETLINK, NETLINK_PKTINFO, sizeof(info), &info); }

Contributors

PersonTokensPropCommitsCommitProp
Patrick McHardy48100.00%1100.00%
Total48100.00%1100.00%


static void netlink_cmsg_listen_all_nsid(struct sock *sk, struct msghdr *msg, struct sk_buff *skb) { if (!NETLINK_CB(skb).nsid_is_set) return; put_cmsg(msg, SOL_NETLINK, NETLINK_LISTEN_ALL_NSID, sizeof(int), &NETLINK_CB(skb).nsid); }

Contributors

PersonTokensPropCommitsCommitProp
Nicolas Dichtel54100.00%1100.00%
Total54100.00%1100.00%


static int netlink_sendmsg(struct socket *sock, struct msghdr *msg, size_t len) { struct sock *sk = sock->sk; struct netlink_sock *nlk = nlk_sk(sk); DECLARE_SOCKADDR(struct sockaddr_nl *, addr, msg->msg_name); u32 dst_portid; u32 dst_group; struct sk_buff *skb; int err; struct scm_cookie scm; u32 netlink_skb_flags = 0; if (msg->msg_flags&MSG_OOB) return -EOPNOTSUPP; err = scm_send(sock, msg, &scm, true); if (err < 0) return err; if (msg->msg_namelen) { err = -EINVAL; if (addr->nl_family != AF_NETLINK) goto out; dst_portid = addr->nl_pid; dst_group = ffs(addr->nl_groups); err = -EPERM; if ((dst_group || dst_portid) && !netlink_allowed(sock, NL_CFG_F_NONROOT_SEND)) goto out; netlink_skb_flags |= NETLINK_SKB_DST; } else { dst_portid = nlk->dst_portid; dst_group = nlk->dst_group; } if (!nlk->bound) { err = netlink_autobind(sock); if (err) goto out; } else { /* Ensure nlk is hashed and visible. */ smp_rmb(); } err = -EMSGSIZE; if (len > sk->sk_sndbuf - 32) goto out; err = -ENOBUFS; skb = netlink_alloc_large_skb(len, dst_group); if (skb == NULL) goto out; NETLINK_CB(skb).portid = nlk->portid; NETLINK_CB(skb).dst_group = dst_group; NETLINK_CB(skb).creds = scm.creds; NETLINK_CB(skb).flags = netlink_skb_flags; err = -EFAULT; if (memcpy_from_msg(skb_put(skb, len), msg, len)) { kfree_skb(skb); goto out; } err = security_netlink_send(sk, skb); if (err) { kfree_skb(skb); goto out; } if (dst_group) { refcount_inc(&skb->users); netlink_broadcast(sk, skb, dst_portid, dst_group, GFP_KERNEL); } err = netlink_unicast(sk, skb, dst_portid, msg->msg_flags&MSG_DONTWAIT); out: scm_destroy(&scm); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)24860.34%413.79%
Eric W. Biedermann5312.90%517.24%
James Morris276.57%413.79%
Benjamin LaHaise266.33%13.45%
David S. Miller122.92%13.45%
Patrick McHardy122.92%13.45%
Steffen Hurrle81.95%13.45%
Herbert Xu81.95%13.45%
Pablo Neira Ayuso71.70%413.79%
Christoph Hellwig30.73%13.45%
Arnaldo Carvalho de Melo20.49%26.90%
Eric Dumazet20.49%13.45%
Stephen Hemminger10.24%13.45%
Al Viro10.24%13.45%
Elena Reshetova10.24%13.45%
Total411100.00%29100.00%


static int netlink_recvmsg(struct socket *sock, struct msghdr *msg, size_t len, int flags) { struct scm_cookie scm; struct sock *sk = sock->sk; struct netlink_sock *nlk = nlk_sk(sk); int noblock = flags&MSG_DONTWAIT; size_t copied; struct sk_buff *skb, *data_skb; int err, ret; if (flags&MSG_OOB) return -EOPNOTSUPP; copied = 0; skb = skb_recv_datagram(sk, flags, noblock, &err); if (skb == NULL) goto out; data_skb = skb; #ifdef CONFIG_COMPAT_NETLINK_MESSAGES if (unlikely(skb_shinfo(skb)->frag_list)) { /* * If this skb has a frag_list, then here that means that we * will have to use the frag_list skb's data for compat tasks * and the regular skb's data for normal (non-compat) tasks. * * If we need to send the compat skb, assign it to the * 'data_skb' variable so that it will be used below for data * copying. We keep 'skb' for everything else, including * freeing both later. */ if (flags & MSG_CMSG_COMPAT) data_skb = skb_shinfo(skb)->frag_list; } #endif /* Record the max length of recvmsg() calls for future allocations */ nlk->max_recvmsg_len = max(nlk->max_recvmsg_len, len); nlk->max_recvmsg_len = min_t(size_t, nlk->max_recvmsg_len, SKB_WITH_OVERHEAD(32768)); copied = data_skb->len; if (len < copied) { msg->msg_flags |= MSG_TRUNC; copied = len; } skb_reset_transport_header(data_skb); err = skb_copy_datagram_msg(data_skb, 0, msg, copied); if (msg->msg_name) { DECLARE_SOCKADDR(struct sockaddr_nl *, addr, msg->msg_name); addr->nl_family = AF_NETLINK; addr->nl_pad = 0; addr->nl_pid = NETLINK_CB(skb).portid; addr->nl_groups = netlink_group_mask(NETLINK_CB(skb).dst_group); msg->msg_namelen = sizeof(*addr); } if (nlk->flags & NETLINK_F_RECV_PKTINFO) netlink_cmsg_recv_pktinfo(msg, skb); if (nlk->flags & NETLINK_F_LISTEN_ALL_NSID) netlink_cmsg_listen_all_nsid(sk, msg, skb); memset(&scm, 0, sizeof(scm)); scm.creds = *NETLINK_CREDS(skb); if (flags & MSG_TRUNC) copied = data_skb->len; skb_free_datagram(sk, skb); if (nlk->cb_running && atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf / 2) { ret = netlink_dump(sk); if (ret) { sk->sk_err = -ret; sk->sk_error_report(sk); } } scm_recv(sock, msg, &scm, flags); out: netlink_rcv_wake(sk); return err ? : copied; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)21750.00%27.41%
Johannes Berg4410.14%27.41%
Eric Dumazet327.37%27.41%
Benjamin LaHaise317.14%13.70%
Patrick McHardy276.22%311.11%
Andrey Vagin255.76%13.70%
David S. Miller194.38%414.81%
Nicolas Dichtel184.15%27.41%
Steffen Hurrle71.61%13.70%
Arnaldo Carvalho de Melo61.38%311.11%
Christoph Hellwig20.46%13.70%
Stephen Hemminger20.46%13.70%
Ben Pfaff10.23%13.70%
James Morris10.23%13.70%
Pravin B Shelar10.23%13.70%
Eric W. Biedermann10.23%13.70%
Total434100.00%27100.00%


static void netlink_data_ready(struct sock *sk) { BUG(); }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)1178.57%250.00%
Denis V. Lunev214.29%125.00%
Stephen Hemminger17.14%125.00%
Total14100.00%4100.00%

/* * 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, struct module *module, struct netlink_kernel_cfg *cfg) { struct socket *sock; struct sock *sk; struct netlink_sock *nlk; struct listeners *listeners = NULL; struct mutex *cb_mutex = cfg ? cfg->cb_mutex : NULL; unsigned int groups; 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, 1) < 0) goto out_sock_release_nosk; sk = sock->sk; if (!cfg || cfg->groups < 32) groups = 32; else groups = cfg->groups; listeners = kzalloc(sizeof(*listeners) + NLGRPSZ(groups), GFP_KERNEL); if (!listeners) goto out_sock_release; sk->sk_data_ready = netlink_data_ready; if (cfg && cfg->input) nlk_sk(sk)->netlink_rcv = cfg->input; if (netlink_insert(sk, 0)) goto out_sock_release; nlk = nlk_sk(sk); nlk->flags |= NETLINK_F_KERNEL_SOCKET; netlink_table_grab(); if (!nl_table[unit].registered) { nl_table[unit].groups = groups; rcu_assign_pointer(nl_table[unit].listeners, listeners); nl_table[unit].cb_mutex = cb_mutex; nl_table[unit].module = module; if (cfg) { nl_table[unit].bind = cfg->bind; nl_table[unit].unbind = cfg->unbind; nl_table[unit].flags = cfg->flags; if (cfg->compare) nl_table[unit].compare = cfg->compare; } nl_table[unit].registered = 1; } else { kfree(listeners); nl_table[unit].registered++; } netlink_table_ungrab(); return sk; out_sock_release: kfree(listeners); netlink_kernel_release(sk); return NULL; out_sock_release_nosk: sock_release(sock); return NULL; }

Contributors

PersonTokensPropCommitsCommitProp
Patrick McHardy10226.15%618.18%
Linus Torvalds (pre-git)7218.46%26.06%
Pablo Neira Ayuso6817.44%412.12%
Eric W. Biedermann215.38%412.12%
Harald Welte215.38%13.03%
Gao Feng174.36%13.03%
Pavel Emelyanov164.10%13.03%
Eric Dumazet112.82%13.03%
Herbert Xu112.82%26.06%
Hiroaki SHIMODA112.82%13.03%
Denis V. Lunev102.56%39.09%
James Morris102.56%13.03%
Jesper Juhl82.05%13.03%
Akinobu Mita41.03%13.03%
Johannes Berg30.77%13.03%
David S. Miller30.77%13.03%
Arnaldo Carvalho de Melo10.26%13.03%
Nicolas Dichtel10.26%13.03%
Total390100.00%33100.00%

EXPORT_SYMBOL(__netlink_kernel_create);
void netlink_kernel_release(struct sock *sk) { if (sk == NULL || sk->sk_socket == NULL) return; sock_release(sk->sk_socket); }

Contributors

PersonTokensPropCommitsCommitProp
Eric W. Biedermann1653.33%133.33%
Denis V. Lunev1446.67%266.67%
Total30100.00%3100.00%

EXPORT_SYMBOL(netlink_kernel_release);
int __netlink_change_ngroups(struct sock *sk, unsigned int groups) { struct listeners *new, *old; struct netlink_table *tbl = &nl_table[sk->sk_protocol]; if (groups < 32) groups = 32; if (NLGRPSZ(tbl->groups) < NLGRPSZ(groups)) { new = kzalloc(sizeof(*new) + NLGRPSZ(groups), GFP_ATOMIC); if (!new) return -ENOMEM; old = nl_deref_protected(tbl->listeners); memcpy(new->masks, old->masks, NLGRPSZ(tbl->groups)); rcu_assign_pointer(tbl->listeners, new); kfree_rcu(old, rcu); } tbl->groups = groups; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Johannes Berg11884.29%350.00%
Eric Dumazet2014.29%233.33%
Lai Jiangshan21.43%116.67%
Total140100.00%6100.00%

/** * 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) { int err; netlink_table_grab(); err = __netlink_change_ngroups(sk, groups); netlink_table_ungrab(); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Johannes Berg35100.00%2100.00%
Total35100.00%2100.00%


void __netlink_clear_multicast_users(struct sock *ksk, unsigned int group) { struct sock *sk; struct netlink_table *tbl = &nl_table[ksk->sk_protocol]; sk_for_each_bound(sk, &tbl->mc_list) netlink_update_socket_mc(nlk_sk(sk), group, 0); }

Contributors

PersonTokensPropCommitsCommitProp
Johannes Berg50100.00%2100.00%
Total50100.00%2100.00%


struct nlmsghdr * __nlmsg_put(struct sk_buff *skb, u32 portid, u32 seq, int type, int len, int flags) { struct nlmsghdr *nlh; int size = nlmsg_msg_size(len); nlh = skb_put(skb, NLMSG_ALIGN(size)); nlh->nlmsg_type = type; nlh->nlmsg_len = size; nlh->nlmsg_flags = flags; nlh->nlmsg_pid = portid; nlh->nlmsg_seq = seq; if (!__builtin_constant_p(size) || NLMSG_ALIGN(size) - size != 0) memset(nlmsg_data(nlh) + len, 0, NLMSG_ALIGN(size) - size); return nlh; }

Contributors

PersonTokensPropCommitsCommitProp
Denys Vlasenko11796.69%133.33%
Hong Zhi Guo21.65%133.33%
Eric W. Biedermann21.65%133.33%
Total121100.00%3100.00%

EXPORT_SYMBOL(__nlmsg_put); /* * 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 = NULL; struct nlmsghdr *nlh; struct module *module; int err = -ENOBUFS; int alloc_min_size; int alloc_size; mutex_lock(nlk->cb_mutex); if (!nlk->cb_running) { err = -EINVAL; goto errout_skb; } if (atomic_read(&sk->sk_rmem_alloc) >= sk->sk_rcvbuf) goto errout_skb; /* NLMSG_GOODSIZE is small to avoid high order allocations being * required, but it makes sense to _attempt_ a 16K bytes allocation * to reduce number of system calls on dump operations, if user * ever provided a big enough buffer. */ cb = &nlk->cb; alloc_min_size = max_t(int, cb->min_dump_alloc, NLMSG_GOODSIZE); if (alloc_min_size < nlk->max_recvmsg_len) { alloc_size = nlk->max_recvmsg_len; skb = alloc_skb(alloc_size, (GFP_KERNEL & ~__GFP_DIRECT_RECLAIM) | __GFP_NOWARN | __GFP_NORETRY); } if (!skb) { alloc_size = alloc_min_size; skb = alloc_skb(alloc_size, GFP_KERNEL); } if (!skb) goto errout_skb; /* Trim skb to allocated size. User is expected to provide buffer as * large as max(min_dump_alloc, 16KiB (mac_recvmsg_len capped at * netlink_recvmsg())). dump will pack as many smaller messages as * could fit within the allocated skb. skb is typically allocated * with larger space than required (could be as much as near 2x the * requested size with align to next power of 2 approach). Allowing * dump to use the excess space makes it difficult for a user to have a * reasonable static buffer based on the expected largest dump of a * single netdev. The outcome is MSG_TRUNC error. */ skb_reserve(skb, skb_tailroom(skb) - alloc_size); netlink_skb_set_owner_r(skb, sk); if (nlk->dump_done_errno > 0) nlk->dump_done_errno = cb->dump(skb, cb); if (nlk->dump_done_errno > 0 || skb_tailroom(skb) < nlmsg_total_size(sizeof(nlk->dump_done_errno))) { mutex_unlock(nlk->cb_mutex); if (sk_filter(sk, skb)) kfree_skb(skb); else __netlink_sendskb(sk, skb); return 0; } nlh = nlmsg_put_answer(skb, cb, NLMSG_DONE, sizeof(nlk->dump_done_errno), NLM_F_MULTI); if (WARN_ON(!nlh)) goto errout_skb; nl_dump_check_consistent(cb, nlh); memcpy(nlmsg_data(nlh), &nlk->dump_done_errno, sizeof(nlk->dump_done_errno)); if (sk_filter(sk, skb)) kfree_skb(skb); else __netlink_sendskb(sk, skb); if (cb->done) cb->done(cb); nlk->cb_running = false; module = cb->module; skb = cb->skb; mutex_unlock(nlk->cb_mutex); module_put(module); consume_skb(skb); return 0; errout_skb: mutex_unlock(nlk->cb_mutex); kfree_skb(skb); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)12228.71%29.09%
Arad, Ronen5011.76%14.55%
Thomas Graf4310.12%313.64%
Jason A. Donenfeld419.65%14.55%
Eric Dumazet337.76%313.64%
Patrick McHardy327.53%29.09%
Stephen Hemminger307.06%14.55%
Greg Rose204.71%14.55%
Herbert Xu174.00%14.55%
David S. Miller133.06%14.55%
Pravin B Shelar81.88%14.55%
Johannes Berg71.65%14.55%
Gao Feng51.18%14.55%
Florian Westphal20.47%14.55%
Dan Carpenter10.24%14.55%
Arnaldo Carvalho de Melo10.24%14.55%
Total425100.00%22100.00%


int __netlink_dump_start(struct sock *ssk, struct sk_buff *skb, const struct nlmsghdr *nlh, struct netlink_dump_control *control) { struct netlink_callback *cb; struct sock *sk; struct netlink_sock *nlk; int ret; refcount_inc(&skb->users); sk = netlink_lookup(sock_net(ssk), ssk->sk_protocol, NETLINK_CB(skb).portid); if (sk == NULL) { ret = -ECONNREFUSED; goto error_free; } nlk = nlk_sk(sk); mutex_lock(nlk->cb_mutex); /* A dump is in progress... */ if (nlk->cb_running) { ret = -EBUSY; goto error_unlock; } /* add reference of module which cb->dump belongs to */ if (!try_module_get(control->module)) { ret = -EPROTONOSUPPORT; goto error_unlock; } cb = &nlk->cb; memset(cb, 0, sizeof(*cb)); cb->start = control->start; cb->dump = control->dump; cb->done = control->done; cb->nlh = nlh; cb->data = control->data; cb->module = control->module; cb->min_dump_alloc = control->min_dump_alloc; cb->skb = skb; if (cb->start) { ret = cb->start(cb); if (ret) goto error_unlock; } nlk->cb_running = true; nlk->dump_done_errno = INT_MAX; mutex_unlock(nlk->cb_mutex); ret = netlink_dump(sk); sock_put(sk); if (ret) return ret; /* We successfully started a dump, by returning -EINTR we * signal not to send ACK even if it was requested. */ return -EINTR; error_unlock: sock_put(sk); mutex_unlock(nlk->cb_mutex); error_free: kfree_skb(skb); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Pravin B Shelar10132.37%14.55%
Linus Torvalds (pre-git)7825.00%29.09%
Jason A. Donenfeld309.62%313.64%
Gao Feng268.33%14.55%
Tom Herbert216.73%14.55%
David S. Miller144.49%14.55%
Andrey Vagin123.85%14.55%
Patrick McHardy103.21%313.64%
Herbert Xu51.60%14.55%
Eric W. Biedermann41.28%29.09%
Denis V. Lunev30.96%14.55%
Hideaki Yoshifuji / 吉藤英明30.96%14.55%
Pablo Neira Ayuso20.64%14.55%
Arnaldo Carvalho de Melo20.64%29.09%
Elena Reshetova10.32%14.55%
Total312100.00%22100.00%

EXPORT_SYMBOL(__netlink_dump_start);
void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err, const struct netlink_ext_ack *extack) { struct sk_buff *skb; struct nlmsghdr *rep; struct nlmsgerr *errmsg; size_t payload = sizeof(*errmsg); size_t tlvlen = 0; struct netlink_sock *nlk = nlk_sk(NETLINK_CB(in_skb).sk); unsigned int flags = 0; bool nlk_has_extack = nlk->flags & NETLINK_F_EXT_ACK; /* Error messages get the original request appened, unless the user * requests to cap the error message, and get extra error data if * requested. */ if (nlk_has_extack && extack && extack->_msg) tlvlen += nla_total_size(strlen(extack->_msg) + 1); if (err) { if (!(nlk->flags & NETLINK_F_CAP_ACK)) payload += nlmsg_len(nlh); else flags |= NLM_F_CAPPED; if (nlk_has_extack && extack && extack->bad_attr) tlvlen += nla_total_size(sizeof(u32)); } else { flags |= NLM_F_CAPPED; if (nlk_has_extack && extack && extack->cookie_len) tlvlen += nla_total_size(extack->cookie_len); } if (tlvlen) flags |= NLM_F_ACK_TLVS; skb = nlmsg_new(payload + tlvlen, GFP_KERNEL); if (!skb) { NETLINK_CB(in_skb).sk->sk_err = ENOBUFS; NETLINK_CB(in_skb).sk->sk_error_report(NETLINK_CB(in_skb).sk); return; } rep = __nlmsg_put(skb, NETLINK_CB(in_skb).portid, nlh->nlmsg_seq, NLMSG_ERROR, payload, flags); errmsg = nlmsg_data(rep); errmsg->error = err; memcpy(&errmsg->msg, nlh, payload > sizeof(*errmsg) ? nlh->nlmsg_len : sizeof(*nlh)); if (nlk_has_extack && extack) { if (extack->_msg) { WARN_ON(nla_put_string(skb, NLMSGERR_ATTR_MSG, extack->_msg)); } if (err) { if (extack->bad_attr && !WARN_ON((u8 *)extack->bad_attr < in_skb->data || (u8 *)extack->bad_attr >= in_skb->data + in_skb->len)) WARN_ON(nla_put_u32(skb, NLMSGERR_ATTR_OFFS, (u8 *)extack->bad_attr - in_skb->data)); } else { if (extack->cookie_len) WARN_ON(nla_put(skb, NLMSGERR_ATTR_COOKIE, extack->cookie_len, extack->cookie)); } } nlmsg_end(skb, rep); netlink_unicast(in_skb->sk, skb, NETLINK_CB(in_skb).portid, MSG_DONTWAIT); }

Contributors

PersonTokensPropCommitsCommitProp
Johannes Berg22650.11%426.67%
Linus Torvalds (pre-git)11826.16%213.33%
David Ahern327.10%16.67%
Christophe Ricard306.65%16.67%
David S. Miller214.66%16.67%
Thomas Graf204.43%320.00%
Eric W. Biedermann20.44%16.67%
Florian Westphal10.22%16.67%
John Fastabend10.22%16.67%
Total451100.00%15100.00%

EXPORT_SYMBOL(netlink_ack);
int netlink_rcv_skb(struct sk_buff *skb, int (*cb)(struct sk_buff *, struct nlmsghdr *, struct netlink_ext_ack *)) { struct netlink_ext_ack extack; struct nlmsghdr *nlh; int err; while (skb->len >= nlmsg_total_size(0)) { int msglen; memset(&extack, 0, sizeof(extack)); 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, &extack); if (err == -EINTR) goto skip; ack: if (nlh->nlmsg_flags & NLM_F_ACK || err) netlink_ack(skb, nlh, err, &extack); skip: msglen = NLMSG_ALIGN(nlh->nlmsg_len); if (msglen > skb->len) msglen = skb->len; skb_pull(skb, msglen); } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Thomas Graf14971.63%440.00%
Denis V. Lunev2311.06%220.00%
Johannes Berg146.73%110.00%
Xin Long136.25%110.00%
Martin Murray62.88%110.00%
Arnaldo Carvalho de Melo31.44%110.00%
Total208100.00%10100.00%

EXPORT_SYMBOL(netlink_rcv_skb); /** * nlmsg_notify - send a notification netlink message * @sk: netlink socket to use * @skb: notification message * @portid: destination netlink portid 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 portid, unsigned int group, int report, gfp_t flags) { int err = 0; if (group) { int exclude_portid = 0; if (report) { refcount_inc(&skb->users); exclude_portid = portid; } /* errors reported via destination sk->sk_err, but propagate * delivery errors if NETLINK_BROADCAST_ERROR flag is set */ err = nlmsg_multicast(sk, skb, exclude_portid, group, flags); } if (report) { int err2; err2 = nlmsg_unicast(sk, skb, portid); if (!err || err == -ESRCH) err = err2; } return err; }

Contributors

PersonTokensPropCommitsCommitProp
Thomas Graf8573.91%125.00%
Pablo Neira Ayuso2320.00%125.00%
Eric W. Biedermann65.22%125.00%
Elena Reshetova10.87%125.00%
Total115100.00%4100.00%

EXPORT_SYMBOL(nlmsg_notify); #ifdef CONFIG_PROC_FS struct nl_seq_iter { struct seq_net_private p; struct rhashtable_iter hti; int link; };
static int netlink_walk_start(struct nl_seq_iter *iter) { int err; err = rhashtable_walk_init(&nl_table[iter->link].hash, &iter->hti, GFP_KERNEL); if (err) { iter->link = MAX_LINKS; return err; } err = rhashtable_walk_start(&iter->hti); return err == -EAGAIN ? 0 : err; }

Contributors

PersonTokensPropCommitsCommitProp
Herbert Xu4867.61%233.33%
Stephen Hemminger1723.94%116.67%
Bob Copeland22.82%116.67%
Linus Torvalds (pre-git)22.82%116.67%
Thomas Graf22.82%116.67%
Total71100.00%6100.00%


static void netlink_walk_stop(struct nl_seq_iter *iter) { rhashtable_walk_stop(&iter->hti); rhashtable_walk_exit(&iter->hti); }

Contributors

PersonTokensPropCommitsCommitProp
Herbert Xu1659.26%120.00%
Stephen Hemminger829.63%120.00%
Linus Torvalds (pre-git)13.70%120.00%
Thomas Graf13.70%120.00%
Arnaldo Carvalho de Melo13.70%120.00%
Total27100.00%5100.00%


static void *__netlink_seq_next(struct seq_file *seq) { struct nl_seq_iter *iter = seq->private; struct netlink_sock *nlk; do { for (;;) { int err; nlk = rhashtable_walk_next(&iter->hti); if (IS_ERR(nlk)) { if (PTR_ERR(nlk) == -EAGAIN) continue; return nlk; } if (nlk) break; netlink_walk_stop(iter); if (++iter->link >= MAX_LINKS) return NULL; err = netlink_walk_start(iter); if (err) return ERR_PTR(err); } } while (sock_net(&nlk->sk) != seq_file_net(seq)); return nlk; }

Contributors

PersonTokensPropCommitsCommitProp
Herbert Xu9069.23%222.22%
Stephen Hemminger1612.31%111.11%
Thomas Graf1410.77%333.33%
Gao Feng53.85%111.11%
Eric W. Biedermann32.31%111.11%
Hideaki Yoshifuji / 吉藤英明21.54%111.11%
Total130100.00%9100.00%


static void *netlink_seq_start(struct seq_file *seq, loff_t *posp) { struct nl_seq_iter *iter = seq->private; void *obj = SEQ_START_TOKEN; loff_t pos; int err; iter->link = 0; err = netlink_walk_start(iter); if (err) return ERR_PTR(err); for (pos = *posp; pos && obj && !IS_ERR(obj); pos--) obj = __netlink_seq_next(seq); return obj; }

Contributors

PersonTokensPropCommitsCommitProp
Herbert Xu8291.11%233.33%
Stephen Hemminger33.33%116.67%
Eric W. Biedermann33.33%116.67%
Thomas Graf11.11%116.67%
Arnaldo Carvalho de Melo11.11%116.67%
Total90100.00%6100.00%


static void *netlink_seq_next(struct seq_file *seq, void *v, loff_t *pos) { ++*pos; return __netlink_seq_next(seq); }

Contributors

PersonTokensPropCommitsCommitProp
Herbert Xu2790.00%266.67%
Stephen Hemminger310.00%133.33%
Total30100.00%3100.00%


static void netlink_seq_stop(struct seq_file *seq, void *v) { struct nl_seq_iter *iter = seq->private; if (iter->link >= MAX_LINKS) return; netlink_walk_stop(iter); }

Contributors

PersonTokensPropCommitsCommitProp
Herbert Xu2257.89%150.00%
Stephen Hemminger1642.11%150.00%
Total38100.00%2100.00%


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 Drops Inode\n"); } else { struct sock *s = v; struct netlink_sock *nlk = nlk_sk(s); seq_printf(seq, "%pK %-3d %-6u %08x %-8d %-8d %d %-8d %-8d %-8lu\n", s, s->sk_protocol, nlk->portid, nlk->groups ? (u32)nlk->groups[0] : 0, sk_rmem_alloc_get(s), sk_wmem_alloc_get(s), nlk->cb_running, refcount_read(&s->sk_refcnt), atomic_read(&s->sk_drops), sock_i_ino(s) ); } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Stephen Hemminger4234.43%16.25%
Linus Torvalds (pre-git)3024.59%212.50%
David S. Miller129.84%16.25%
Patrick McHardy129.84%212.50%
Pablo Neira Ayuso86.56%16.25%
Masatake YAMATO64.92%16.25%
Eric Dumazet43.28%212.50%
Arnaldo Carvalho de Melo32.46%212.50%
Pravin B Shelar21.64%16.25%
John Levon10.82%16.25%
Eric W. Biedermann10.82%16.25%
Elena Reshetova10.82%16.25%
Total122100.00%16100.00%

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) { return seq_open_net(inode, file, &netlink_seq_ops, sizeof(struct nl_seq_iter)); }

Contributors

PersonTokensPropCommitsCommitProp
Stephen Hemminger1442.42%116.67%
Eric W. Biedermann515.15%116.67%
Pavel Emelyanov515.15%116.67%
Denis V. Lunev515.15%116.67%
Herbert Xu39.09%116.67%
Linus Torvalds (pre-git)13.03%116.67%
Total33100.00%6100.00%

static const struct file_operations netlink_seq_fops = { .owner = THIS_MODULE, .open = netlink_seq_open, .read = seq_read, .llseek = seq_lseek, .release = seq_release_net, }; #endif
int netlink_register_notifier(struct notifier_block *nb) { return blocking_notifier_chain_register(&netlink_chain, nb); }

Contributors

PersonTokensPropCommitsCommitProp
James Morris1894.74%150.00%
Américo Wang15.26%150.00%
Total19100.00%2100.00%

EXPORT_SYMBOL(netlink_register_notifier);
int netlink_unregister_notifier(struct notifier_block *nb) { return blocking_notifier_chain_unregister(&netlink_chain, nb); }

Contributors

PersonTokensPropCommitsCommitProp
James Morris1894.74%150.00%
Américo Wang15.26%150.00%
Total19100.00%2100.00%

EXPORT_SYMBOL(netlink_unregister_notifier); 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 = netlink_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 const 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_create("netlink", 0, net->proc_net, &netlink_seq_fops)) return -ENOMEM; #endif return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Eric W. Biedermann3585.37%133.33%
Gao Feng512.20%133.33%
Pavel Emelyanov12.44%133.33%
Total41100.00%3100.00%


static void __net_exit netlink_net_exit(struct net *net) { #ifdef CONFIG_PROC_FS remove_proc_entry("netlink", net->proc_net); #endif }

Contributors

PersonTokensPropCommitsCommitProp
Eric W. Biedermann2076.92%133.33%
Gao Feng519.23%133.33%
Pavel Emelyanov13.85%133.33%
Total26100.00%3100.00%


static void __init netlink_add_usersock_entry(void) { struct listeners *listeners; int groups = 32; listeners = kzalloc(sizeof(*listeners) + NLGRPSZ(groups), GFP_KERNEL); if (!listeners) panic("netlink_add_usersock_entry: Cannot allocate listeners\n"); netlink_table_grab(); nl_table[NETLINK_USERSOCK].groups = groups; rcu_assign_pointer(nl_table[NETLINK_USERSOCK].listeners, listeners); nl_table[NETLINK_USERSOCK].module = THIS_MODULE; nl_table[NETLINK_USERSOCK].registered = 1; nl_table[NETLINK_USERSOCK].flags = NL_CFG_F_NONROOT_SEND; netlink_table_ungrab(); }

Contributors

PersonTokensPropCommitsCommitProp
David S. Miller8079.21%125.00%
Eric Dumazet1211.88%125.00%
Pablo Neira Ayuso98.91%250.00%
Total101100.00%4100.00%

static struct pernet_operations __net_initdata netlink_net_ops = { .init = netlink_net_init, .exit = netlink_net_exit, };
static inline u32 netlink_hash(const void *data, u32 len, u32 seed) { const struct netlink_sock *nlk = data; struct netlink_compare_arg arg; netlink_compare_arg_init(&arg, sock_net(&nlk->sk), nlk->portid); return jhash2((u32 *)&arg, netlink_compare_arg_len / sizeof(u32), seed); }

Contributors

PersonTokensPropCommitsCommitProp
Herbert Xu5580.88%342.86%
Linus Torvalds (pre-git)57.35%228.57%
Arnaldo Carvalho de Melo57.35%114.29%
Patrick McHardy34.41%114.29%
Total68100.00%7100.00%

static const struct rhashtable_params netlink_rhashtable_params = { .head_offset = offsetof(struct netlink_sock, node), .key_len = netlink_compare_arg_len, .obj_hashfn = netlink_hash, .obj_cmpfn = netlink_compare, .automatic_shrinking = true, };
static int __init netlink_proto_init(void) { int i; int err = proto_register(&netlink_proto, 0); if (err != 0) goto out; BUILD_BUG_ON(sizeof(struct netlink_skb_parms) > FIELD_SIZEOF(struct sk_buff, cb)); nl_table = kcalloc(MAX_LINKS, sizeof(*nl_table), GFP_KERNEL); if (!nl_table) goto panic; for (i = 0; i < MAX_LINKS; i++) { if (rhashtable_init(&nl_table[i].hash, &netlink_rhashtable_params) < 0) { while (--i > 0) rhashtable_destroy(&nl_table[i].hash); kfree(nl_table); goto panic; } } INIT_LIST_HEAD(&netlink_tap_all); netlink_add_usersock_entry(); 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"); }

Contributors

PersonTokensPropCommitsCommitProp
Herbert Xu8851.76%212.50%
Linus Torvalds (pre-git)2112.35%318.75%
Arnaldo Carvalho de Melo169.41%212.50%
Thomas Graf127.06%16.25%
Akinobu Mita116.47%16.25%
Hideaki Yoshifuji / 吉藤英明74.12%212.50%
Daniel Borkmann63.53%16.25%
Eric W. Biedermann31.76%212.50%
Panagiotis Issaris31.76%16.25%
David S. Miller31.76%16.25%
Total170100.00%16100.00%

core_initcall(netlink_proto_init);

Overall Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)225117.91%185.81%
Patrick McHardy163012.97%247.74%
Herbert Xu159712.70%268.39%
Johannes Berg9677.69%196.13%
Daniel Borkmann6745.36%82.58%
Eric W. Biedermann6395.08%144.52%
Pablo Neira Ayuso5564.42%175.48%
Thomas Graf5464.34%175.48%
David S. Miller3252.59%134.19%
Eric Dumazet2782.21%175.48%
Nicolas Dichtel2542.02%30.97%
Stephen Hemminger2532.01%82.58%
Florian Westphal2471.96%20.65%
Richard Guy Briggs2291.82%20.65%
Andrew Morton2041.62%20.65%
Denis V. Lunev1681.34%103.23%
James Morris1501.19%92.90%
David Herrmann1461.16%20.65%
Arnaldo Carvalho de Melo1230.98%113.55%
Denys Vlasenko1220.97%10.32%
Christophe Ricard1160.92%10.32%
Pravin B Shelar1120.89%10.32%
Gao Feng930.74%41.29%
Harald Welte710.56%10.32%
Jason A. Donenfeld710.56%30.97%
Xin Long570.45%30.97%
Benjamin LaHaise570.45%10.32%
Ying Xue510.41%10.32%
Andrey Vagin500.40%20.65%
Arad, Ronen500.40%10.32%
Hideaki Yoshifuji / 吉藤英明420.33%61.94%
Rusty Russell380.30%10.32%
David Ahern320.25%10.32%
David Decotigny250.20%10.32%
Pavel Emelyanov230.18%30.97%
Tom Herbert210.17%10.32%
Greg Rose200.16%10.32%
Kevin Cernekee190.15%10.32%
Neil Horman160.13%10.32%
Alexey Dobriyan150.12%10.32%
Akinobu Mita150.12%10.32%
Changli Gao150.12%10.32%
Steffen Hurrle150.12%10.32%
Hannes Frederic Sowa140.11%10.32%
Hiroaki SHIMODA120.10%10.32%
Tommy S. Christensen110.09%20.65%
Alexey Kuznetsov100.08%10.32%
Jesper Juhl80.06%10.32%
Heiko Carstens80.06%10.32%
Matthew Wilcox70.06%10.32%
Elena Reshetova70.06%30.97%
Cyrill V. Gorcunov70.06%10.32%
Al Viro70.06%41.29%
Mike Pecovnik60.05%10.32%
Linus Torvalds60.05%41.29%
Martin Murray60.05%10.32%
Masatake YAMATO60.05%10.32%
Rami Rosen50.04%10.32%
Arjan van de Ven50.04%20.65%
Octavian Purdila50.04%10.32%
Christoph Hellwig50.04%10.32%
Art Haas40.03%10.32%
Américo Wang40.03%10.32%
Dmitry Ivanov40.03%10.32%
Thomas Gleixner40.03%10.32%
Mel Gorman30.02%10.32%
Panagiotis Issaris30.02%10.32%
Varka Bhadram30.02%10.32%
Thomas Goff30.02%10.32%
Eric Paris30.02%10.32%
Randy Dunlap30.02%10.32%
Alan Stern30.02%10.32%
Ilpo Järvinen30.02%10.32%
Bob Copeland20.02%10.32%
Hong Zhi Guo20.02%10.32%
Lai Jiangshan20.02%10.32%
John Fastabend10.01%10.32%
John Levon10.01%10.32%
Ben Pfaff10.01%10.32%
Dan Carpenter10.01%10.32%
Adrian Bunk10.01%10.32%
Philippe De Muyter10.01%10.32%
Total12570100.00%310100.00%
Directory: net/netlink
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.