Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net: netlink: virtual tap device management

Similarly to the networking receive path with ptype_all taps, we add
the possibility to register netdevices that are for ARPHRD_NETLINK to
the netlink subsystem, so that those can be used for netlink analyzers
resp. debuggers. We do not offer a direct callback function as out-of-tree
modules could do crap with it. Instead, a netdevice must be registered
properly and only receives a clone, managed by the netlink layer. Symbols
are exported as GPL-only.

Signed-off-by: Daniel Borkmann <dborkman@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Daniel Borkmann and committed by
David S. Miller
bcbde0d4 77e2af03

+117
+10
include/linux/netlink.h
··· 145 145 return __netlink_dump_start(ssk, skb, nlh, control); 146 146 } 147 147 148 + struct netlink_tap { 149 + struct net_device *dev; 150 + struct module *module; 151 + struct list_head list; 152 + }; 153 + 154 + extern int netlink_add_tap(struct netlink_tap *nt); 155 + extern int __netlink_remove_tap(struct netlink_tap *nt); 156 + extern int netlink_remove_tap(struct netlink_tap *nt); 157 + 148 158 #endif /* __LINUX_NETLINK_H */
+107
net/netlink/af_netlink.c
··· 57 57 #include <linux/audit.h> 58 58 #include <linux/mutex.h> 59 59 #include <linux/vmalloc.h> 60 + #include <linux/if_arp.h> 60 61 #include <asm/cacheflush.h> 61 62 62 63 #include <net/net_namespace.h> ··· 102 101 103 102 static ATOMIC_NOTIFIER_HEAD(netlink_chain); 104 103 104 + static DEFINE_SPINLOCK(netlink_tap_lock); 105 + static struct list_head netlink_tap_all __read_mostly; 106 + 105 107 static inline u32 netlink_group_mask(u32 group) 106 108 { 107 109 return group ? 1 << (group - 1) : 0; ··· 113 109 static inline struct hlist_head *nl_portid_hashfn(struct nl_portid_hash *hash, u32 portid) 114 110 { 115 111 return &hash->table[jhash_1word(portid, hash->rnd) & hash->mask]; 112 + } 113 + 114 + int netlink_add_tap(struct netlink_tap *nt) 115 + { 116 + if (unlikely(nt->dev->type != ARPHRD_NETLINK)) 117 + return -EINVAL; 118 + 119 + spin_lock(&netlink_tap_lock); 120 + list_add_rcu(&nt->list, &netlink_tap_all); 121 + spin_unlock(&netlink_tap_lock); 122 + 123 + if (nt->module) 124 + __module_get(nt->module); 125 + 126 + return 0; 127 + } 128 + EXPORT_SYMBOL_GPL(netlink_add_tap); 129 + 130 + int __netlink_remove_tap(struct netlink_tap *nt) 131 + { 132 + bool found = false; 133 + struct netlink_tap *tmp; 134 + 135 + spin_lock(&netlink_tap_lock); 136 + 137 + list_for_each_entry(tmp, &netlink_tap_all, list) { 138 + if (nt == tmp) { 139 + list_del_rcu(&nt->list); 140 + found = true; 141 + goto out; 142 + } 143 + } 144 + 145 + pr_warn("__netlink_remove_tap: %p not found\n", nt); 146 + out: 147 + spin_unlock(&netlink_tap_lock); 148 + 149 + if (found && nt->module) 150 + module_put(nt->module); 151 + 152 + return found ? 0 : -ENODEV; 153 + } 154 + EXPORT_SYMBOL_GPL(__netlink_remove_tap); 155 + 156 + int netlink_remove_tap(struct netlink_tap *nt) 157 + { 158 + int ret; 159 + 160 + ret = __netlink_remove_tap(nt); 161 + synchronize_net(); 162 + 163 + return ret; 164 + } 165 + EXPORT_SYMBOL_GPL(netlink_remove_tap); 166 + 167 + static int __netlink_deliver_tap_skb(struct sk_buff *skb, 168 + struct net_device *dev) 169 + { 170 + struct sk_buff *nskb; 171 + int ret = -ENOMEM; 172 + 173 + dev_hold(dev); 174 + nskb = skb_clone(skb, GFP_ATOMIC); 175 + if (nskb) { 176 + nskb->dev = dev; 177 + ret = dev_queue_xmit(nskb); 178 + if (unlikely(ret > 0)) 179 + ret = net_xmit_errno(ret); 180 + } 181 + 182 + dev_put(dev); 183 + return ret; 184 + } 185 + 186 + static void __netlink_deliver_tap(struct sk_buff *skb) 187 + { 188 + int ret; 189 + struct netlink_tap *tmp; 190 + 191 + list_for_each_entry_rcu(tmp, &netlink_tap_all, list) { 192 + ret = __netlink_deliver_tap_skb(skb, tmp->dev); 193 + if (unlikely(ret)) 194 + break; 195 + } 196 + } 197 + 198 + static void netlink_deliver_tap(struct sk_buff *skb) 199 + { 200 + rcu_read_lock(); 201 + 202 + if (unlikely(!list_empty(&netlink_tap_all))) 203 + __netlink_deliver_tap(skb); 204 + 205 + rcu_read_unlock(); 116 206 } 117 207 118 208 static void netlink_overrun(struct sock *sk) ··· 1616 1518 { 1617 1519 int len = skb->len; 1618 1520 1521 + netlink_deliver_tap(skb); 1522 + 1619 1523 #ifdef CONFIG_NETLINK_MMAP 1620 1524 if (netlink_skb_is_mmaped(skb)) 1621 1525 netlink_queue_mmaped_skb(sk, skb); ··· 1678 1578 1679 1579 ret = -ECONNREFUSED; 1680 1580 if (nlk->netlink_rcv != NULL) { 1581 + /* We could do a netlink_deliver_tap(skb) here as well 1582 + * but since this is intended for the kernel only, we 1583 + * should rather let it stay under the hood. 1584 + */ 1585 + 1681 1586 ret = skb->len; 1682 1587 netlink_skb_set_owner_r(skb, sk); 1683 1588 NETLINK_CB(skb).sk = ssk; ··· 3079 2974 3080 2975 nl_table[i].compare = netlink_compare; 3081 2976 } 2977 + 2978 + INIT_LIST_HEAD(&netlink_tap_all); 3082 2979 3083 2980 netlink_add_usersock_entry(); 3084 2981