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