xref: /openbmc/linux/net/vmw_vsock/af_vsock_tap.c (revision 9d64fc08)
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);
115