GNU Linux-libre 4.19.286-gnu1
[releases.git] / net / vmw_vsock / af_vsock_tap.c
1 /*
2  * Tap functions for AF_VSOCK sockets.
3  *
4  * Code based on net/netlink/af_netlink.c tap functions.
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version
9  * 2 of the License, or (at your option) any later version.
10  */
11
12 #include <linux/module.h>
13 #include <net/sock.h>
14 #include <net/af_vsock.h>
15 #include <linux/if_arp.h>
16
17 static DEFINE_SPINLOCK(vsock_tap_lock);
18 static struct list_head vsock_tap_all __read_mostly =
19                                 LIST_HEAD_INIT(vsock_tap_all);
20
21 int vsock_add_tap(struct vsock_tap *vt)
22 {
23         if (unlikely(vt->dev->type != ARPHRD_VSOCKMON))
24                 return -EINVAL;
25
26         __module_get(vt->module);
27
28         spin_lock(&vsock_tap_lock);
29         list_add_rcu(&vt->list, &vsock_tap_all);
30         spin_unlock(&vsock_tap_lock);
31
32         return 0;
33 }
34 EXPORT_SYMBOL_GPL(vsock_add_tap);
35
36 int vsock_remove_tap(struct vsock_tap *vt)
37 {
38         struct vsock_tap *tmp;
39         bool found = false;
40
41         spin_lock(&vsock_tap_lock);
42
43         list_for_each_entry(tmp, &vsock_tap_all, list) {
44                 if (vt == tmp) {
45                         list_del_rcu(&vt->list);
46                         found = true;
47                         goto out;
48                 }
49         }
50
51         pr_warn("vsock_remove_tap: %p not found\n", vt);
52 out:
53         spin_unlock(&vsock_tap_lock);
54
55         synchronize_net();
56
57         if (found)
58                 module_put(vt->module);
59
60         return found ? 0 : -ENODEV;
61 }
62 EXPORT_SYMBOL_GPL(vsock_remove_tap);
63
64 static int __vsock_deliver_tap_skb(struct sk_buff *skb,
65                                    struct net_device *dev)
66 {
67         int ret = 0;
68         struct sk_buff *nskb = skb_clone(skb, GFP_ATOMIC);
69
70         if (nskb) {
71                 dev_hold(dev);
72
73                 nskb->dev = dev;
74                 ret = dev_queue_xmit(nskb);
75                 if (unlikely(ret > 0))
76                         ret = net_xmit_errno(ret);
77
78                 dev_put(dev);
79         }
80
81         return ret;
82 }
83
84 static void __vsock_deliver_tap(struct sk_buff *skb)
85 {
86         int ret;
87         struct vsock_tap *tmp;
88
89         list_for_each_entry_rcu(tmp, &vsock_tap_all, list) {
90                 ret = __vsock_deliver_tap_skb(skb, tmp->dev);
91                 if (unlikely(ret))
92                         break;
93         }
94 }
95
96 void vsock_deliver_tap(struct sk_buff *build_skb(void *opaque), void *opaque)
97 {
98         struct sk_buff *skb;
99
100         rcu_read_lock();
101
102         if (likely(list_empty(&vsock_tap_all)))
103                 goto out;
104
105         skb = build_skb(opaque);
106         if (skb) {
107                 __vsock_deliver_tap(skb);
108                 consume_skb(skb);
109         }
110
111 out:
112         rcu_read_unlock();
113 }
114 EXPORT_SYMBOL_GPL(vsock_deliver_tap);