Commit | Line | Data |
---|---|---|
a8e04698 SG |
1 | #include <linux/etherdevice.h> |
2 | #include <linux/if_macvlan.h> | |
635b8c8e | 3 | #include <linux/if_tap.h> |
a8e04698 SG |
4 | #include <linux/if_vlan.h> |
5 | #include <linux/interrupt.h> | |
6 | #include <linux/nsproxy.h> | |
7 | #include <linux/compat.h> | |
8 | #include <linux/if_tun.h> | |
9 | #include <linux/module.h> | |
10 | #include <linux/skbuff.h> | |
11 | #include <linux/cache.h> | |
12 | #include <linux/sched.h> | |
13 | #include <linux/types.h> | |
14 | #include <linux/slab.h> | |
15 | #include <linux/wait.h> | |
16 | #include <linux/cdev.h> | |
17 | #include <linux/idr.h> | |
18 | #include <linux/fs.h> | |
19 | #include <linux/uio.h> | |
20 | ||
21 | #include <net/net_namespace.h> | |
22 | #include <net/rtnetlink.h> | |
23 | #include <net/sock.h> | |
24 | #include <linux/virtio_net.h> | |
25 | #include <linux/skb_array.h> | |
26 | ||
27 | /* | |
28 | * Variables for dealing with macvtaps device numbers. | |
29 | */ | |
30 | static dev_t macvtap_major; | |
31 | #define MACVTAP_NUM_DEVS (1U << MINORBITS) | |
32 | ||
33 | static const void *macvtap_net_namespace(struct device *d) | |
34 | { | |
35 | struct net_device *dev = to_net_dev(d->parent); | |
36 | return dev_net(dev); | |
37 | } | |
38 | ||
39 | static struct class macvtap_class = { | |
40 | .name = "macvtap", | |
41 | .owner = THIS_MODULE, | |
42 | .ns_type = &net_ns_type_operations, | |
43 | .namespace = macvtap_net_namespace, | |
44 | }; | |
45 | static struct cdev macvtap_cdev; | |
46 | ||
47 | #define TUN_OFFLOADS (NETIF_F_HW_CSUM | NETIF_F_TSO_ECN | NETIF_F_TSO | \ | |
48 | NETIF_F_TSO6 | NETIF_F_UFO) | |
49 | ||
50 | static int macvtap_newlink(struct net *src_net, | |
51 | struct net_device *dev, | |
52 | struct nlattr *tb[], | |
53 | struct nlattr *data[]) | |
54 | { | |
55 | struct macvlan_dev *vlan = netdev_priv(dev); | |
56 | int err; | |
57 | ||
58 | INIT_LIST_HEAD(&vlan->queue_list); | |
59 | ||
60 | /* Since macvlan supports all offloads by default, make | |
61 | * tap support all offloads also. | |
62 | */ | |
63 | vlan->tap_features = TUN_OFFLOADS; | |
64 | ||
635b8c8e | 65 | err = netdev_rx_handler_register(dev, tap_handle_frame, vlan); |
a8e04698 SG |
66 | if (err) |
67 | return err; | |
68 | ||
69 | /* Don't put anything that may fail after macvlan_common_newlink | |
70 | * because we can't undo what it does. | |
71 | */ | |
72 | err = macvlan_common_newlink(src_net, dev, tb, data); | |
73 | if (err) { | |
74 | netdev_rx_handler_unregister(dev); | |
75 | return err; | |
76 | } | |
77 | ||
78 | return 0; | |
79 | } | |
80 | ||
81 | static void macvtap_dellink(struct net_device *dev, | |
82 | struct list_head *head) | |
83 | { | |
84 | netdev_rx_handler_unregister(dev); | |
635b8c8e | 85 | tap_del_queues(dev); |
a8e04698 SG |
86 | macvlan_dellink(dev, head); |
87 | } | |
88 | ||
89 | static void macvtap_setup(struct net_device *dev) | |
90 | { | |
91 | macvlan_common_setup(dev); | |
92 | dev->tx_queue_len = TUN_READQ_SIZE; | |
93 | } | |
94 | ||
95 | static struct rtnl_link_ops macvtap_link_ops __read_mostly = { | |
96 | .kind = "macvtap", | |
97 | .setup = macvtap_setup, | |
98 | .newlink = macvtap_newlink, | |
99 | .dellink = macvtap_dellink, | |
100 | }; | |
101 | ||
102 | static int macvtap_device_event(struct notifier_block *unused, | |
103 | unsigned long event, void *ptr) | |
104 | { | |
105 | struct net_device *dev = netdev_notifier_info_to_dev(ptr); | |
106 | struct macvlan_dev *vlan; | |
107 | struct device *classdev; | |
108 | dev_t devt; | |
109 | int err; | |
110 | char tap_name[IFNAMSIZ]; | |
111 | ||
112 | if (dev->rtnl_link_ops != &macvtap_link_ops) | |
113 | return NOTIFY_DONE; | |
114 | ||
115 | snprintf(tap_name, IFNAMSIZ, "tap%d", dev->ifindex); | |
116 | vlan = netdev_priv(dev); | |
117 | ||
118 | switch (event) { | |
119 | case NETDEV_REGISTER: | |
120 | /* Create the device node here after the network device has | |
121 | * been registered but before register_netdevice has | |
122 | * finished running. | |
123 | */ | |
635b8c8e | 124 | err = tap_get_minor(vlan); |
a8e04698 SG |
125 | if (err) |
126 | return notifier_from_errno(err); | |
127 | ||
128 | devt = MKDEV(MAJOR(macvtap_major), vlan->minor); | |
129 | classdev = device_create(&macvtap_class, &dev->dev, devt, | |
130 | dev, tap_name); | |
131 | if (IS_ERR(classdev)) { | |
635b8c8e | 132 | tap_free_minor(vlan); |
a8e04698 SG |
133 | return notifier_from_errno(PTR_ERR(classdev)); |
134 | } | |
135 | err = sysfs_create_link(&dev->dev.kobj, &classdev->kobj, | |
136 | tap_name); | |
137 | if (err) | |
138 | return notifier_from_errno(err); | |
139 | break; | |
140 | case NETDEV_UNREGISTER: | |
141 | /* vlan->minor == 0 if NETDEV_REGISTER above failed */ | |
142 | if (vlan->minor == 0) | |
143 | break; | |
144 | sysfs_remove_link(&dev->dev.kobj, tap_name); | |
145 | devt = MKDEV(MAJOR(macvtap_major), vlan->minor); | |
146 | device_destroy(&macvtap_class, devt); | |
635b8c8e | 147 | tap_free_minor(vlan); |
a8e04698 SG |
148 | break; |
149 | case NETDEV_CHANGE_TX_QUEUE_LEN: | |
635b8c8e | 150 | if (tap_queue_resize(vlan)) |
a8e04698 SG |
151 | return NOTIFY_BAD; |
152 | break; | |
153 | } | |
154 | ||
155 | return NOTIFY_DONE; | |
156 | } | |
157 | ||
158 | static struct notifier_block macvtap_notifier_block __read_mostly = { | |
159 | .notifier_call = macvtap_device_event, | |
160 | }; | |
161 | ||
635b8c8e | 162 | extern struct file_operations tap_fops; |
a8e04698 SG |
163 | static int macvtap_init(void) |
164 | { | |
165 | int err; | |
166 | ||
167 | err = alloc_chrdev_region(&macvtap_major, 0, | |
168 | MACVTAP_NUM_DEVS, "macvtap"); | |
169 | if (err) | |
170 | goto out1; | |
171 | ||
635b8c8e | 172 | cdev_init(&macvtap_cdev, &tap_fops); |
a8e04698 SG |
173 | err = cdev_add(&macvtap_cdev, macvtap_major, MACVTAP_NUM_DEVS); |
174 | if (err) | |
175 | goto out2; | |
176 | ||
177 | err = class_register(&macvtap_class); | |
178 | if (err) | |
179 | goto out3; | |
180 | ||
181 | err = register_netdevice_notifier(&macvtap_notifier_block); | |
182 | if (err) | |
183 | goto out4; | |
184 | ||
185 | err = macvlan_link_register(&macvtap_link_ops); | |
186 | if (err) | |
187 | goto out5; | |
188 | ||
189 | return 0; | |
190 | ||
191 | out5: | |
192 | unregister_netdevice_notifier(&macvtap_notifier_block); | |
193 | out4: | |
194 | class_unregister(&macvtap_class); | |
195 | out3: | |
196 | cdev_del(&macvtap_cdev); | |
197 | out2: | |
198 | unregister_chrdev_region(macvtap_major, MACVTAP_NUM_DEVS); | |
199 | out1: | |
200 | return err; | |
201 | } | |
202 | module_init(macvtap_init); | |
203 | ||
204 | extern struct idr minor_idr; | |
205 | static void macvtap_exit(void) | |
206 | { | |
207 | rtnl_link_unregister(&macvtap_link_ops); | |
208 | unregister_netdevice_notifier(&macvtap_notifier_block); | |
209 | class_unregister(&macvtap_class); | |
210 | cdev_del(&macvtap_cdev); | |
211 | unregister_chrdev_region(macvtap_major, MACVTAP_NUM_DEVS); | |
212 | idr_destroy(&minor_idr); | |
213 | } | |
214 | module_exit(macvtap_exit); | |
215 | ||
216 | MODULE_ALIAS_RTNL_LINK("macvtap"); | |
217 | MODULE_AUTHOR("Arnd Bergmann <arnd@arndb.de>"); | |
218 | MODULE_LICENSE("GPL"); |